diff --git a/.clang-format b/.clang-format index a65a29f8c9..47d681e0e2 100644 --- a/.clang-format +++ b/.clang-format @@ -25,6 +25,11 @@ CommentPragmas: '\$(FRR|clippy)' ContinuationIndentWidth: 8 ForEachMacros: # lib + - frr_each + - frr_each_safe + - frr_each_from + - frr_with_mutex + - frr_with_privs - LIST_FOREACH - LIST_FOREACH_SAFE - SLIST_FOREACH @@ -61,5 +66,6 @@ ForEachMacros: - SUBGRP_FOREACH_ADJ_SAFE - AF_FOREACH - FOREACH_AFI_SAFI + - FOREACH_SAFI # ospfd - LSDB_LOOP diff --git a/.dir-locals.el b/.dir-locals.el index 21392ecf28..e47f245db7 100644 --- a/.dir-locals.el +++ b/.dir-locals.el @@ -2,7 +2,7 @@ ;;; For more information see (info "(emacs) Directory Variables") ;;; Match project coding conventions -((c-mode - (indent-tabs-mode . t) - (show-trailing-whitespace . t) - (c-basic-offset . 8))) +((c-mode . ((indent-tabs-mode . t) + (show-trailing-whitespace . t) + (c-basic-offset . 8) + ))) diff --git a/.dockerignore b/.dockerignore index d613e18dfc..e6e1310d24 100644 --- a/.dockerignore +++ b/.dockerignore @@ -6,3 +6,4 @@ **/*.so **/.libs docker/alpine/pkgs +docker/centos/pkgs diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 0000000000..e8a364050a --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1 @@ +d62a17aedeb0eebdba98238874bb13d62c48dbf9 diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 1209f52e14..98c7c3d54d 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -37,7 +37,7 @@ If applicable, add screenshots to help explain your problem. **Versions** - OS Kernel: [e.g. Linux, OpenBSD, etc] [version] - - FRR Version [version] + - FRR Version: [version] **Additional context** Add any other context about the problem here. diff --git a/.gitignore b/.gitignore index 5003c97572..fbbb04b60c 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,10 @@ /Makefile /Makefile.in +/symalyzer_report.html +/jquery-3.4.1.min.js +/jquery-3.4.1.min.js.tmp + ### autoconf/automake subdir stuff .deps @@ -49,7 +53,18 @@ *.pb.h *.pb-c.h *.pb-c.c +*.pb.cc *_clippy.c +*.bc +*.cg.json +*.cg.dot +*.cg.svg + +### gcov outputs + +*.gcno +*.gcov +*.gcda ### dist @@ -87,5 +102,9 @@ GSYMS GRTAGS GPATH compile_commands.json +.ccls-cache .dirstamp refix +.vscode +.kitchen +.emacs.desktop* diff --git a/Makefile.am b/Makefile.am index 9c6c8663ee..a959fd9e5a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -12,7 +12,9 @@ AM_CFLAGS = \ # end AM_CPPFLAGS = \ -I$(top_srcdir) -I$(top_srcdir)/include -I$(top_srcdir)/lib \ - -I$(top_builddir) -I$(top_builddir)/include -I$(top_builddir)/lib + -I$(top_builddir) -I$(top_builddir)/include -I$(top_builddir)/lib \ + $(LUA_INCLUDE) \ + # end AM_LDFLAGS = \ -export-dynamic \ $(AC_LDFLAGS) \ @@ -38,6 +40,12 @@ shvar-%: var-%: @echo "$($*)" >&$(VARFD) +if ONLY_CLIPPY +.DEFAULT_GOAL := clippy-only +endif +clippy-only: Makefile lib/clippy config.h +.PHONY: clippy-only + # overwriting these vars breaks cross-compilation. let's be helpful and warn. # # note: "#AUTODERP# " will be removed from Makefile by configure. These are @@ -81,9 +89,11 @@ var-%: #AUTODERP# endif EXTRA_DIST = +EXTRA_PROGRAMS = BUILT_SOURCES = CLEANFILES = DISTCLEANFILES = +SUFFIXES = examplesdir = $(exampledir) @@ -96,13 +106,13 @@ noinst_LIBRARIES = nodist_noinst_DATA = lib_LTLIBRARIES = module_LTLIBRARIES = -libyang_plugins_LTLIBRARIES = pkginclude_HEADERS = nodist_pkginclude_HEADERS = dist_examples_DATA = dist_yangmodels_DATA = man_MANS = vtysh_scan = +clippy_scan = ## libtool, the self-made GNU scourge ## ... this should fix relinking @@ -111,7 +121,6 @@ vtysh_scan = $(AUTOMAKE_DUMMY)install-moduleLTLIBRARIES: install-libLTLIBRARIES $(AUTOMAKE_DUMMY)install-binPROGRAMS: install-libLTLIBRARIES $(AUTOMAKE_DUMMY)install-sbinPROGRAMS: install-libLTLIBRARIES -$(AUTOMAKE_DUMMY)install-libyang_pluginsLTLIBRARIES: install-libLTLIBRARIES include doc/subdir.am include doc/user/subdir.am @@ -119,10 +128,12 @@ include doc/manpages/subdir.am include doc/developer/subdir.am include include/subdir.am include lib/subdir.am +include mlag/subdir.am include zebra/subdir.am include watchfrr/subdir.am include qpb/subdir.am include fpm/subdir.am +include grpc/subdir.am include tools/subdir.am include solaris/subdir.am @@ -146,6 +157,7 @@ include staticd/subdir.am include bfdd/subdir.am include yang/subdir.am include yang/libyang_plugins/subdir.am +include vrrpd/subdir.am include vtysh/subdir.am include tests/subdir.am @@ -174,6 +186,8 @@ EXTRA_DIST += \ \ python/clidef.py \ python/clippy/__init__.py \ + python/makevars.py \ + python/makefile.py \ \ redhat/frr.logrotate \ redhat/frr.pam \ @@ -187,7 +201,6 @@ EXTRA_DIST += \ snapcraft/defaults \ snapcraft/helpers \ snapcraft/snap \ - \ babeld/Makefile \ bgpd/Makefile \ bgpd/rfp-example/librfp/Makefile \ @@ -198,6 +211,7 @@ EXTRA_DIST += \ doc/user/Makefile \ eigrpd/Makefile \ fpm/Makefile \ + grpc/Makefile \ isisd/Makefile \ ldpd/Makefile \ lib/Makefile \ @@ -216,16 +230,51 @@ EXTRA_DIST += \ vtysh/Makefile \ watchfrr/Makefile \ zebra/Makefile \ + vrrpd/Makefile \ # end -noinst_HEADERS += defaults.h +AM_V_LLVM_BC = $(am__v_LLVM_BC_$(V)) +am__v_LLVM_BC_ = $(am__v_LLVM_BC_$(AM_DEFAULT_VERBOSITY)) +am__v_LLVM_BC_0 = @echo " LLVM.BC " $@; +am__v_LLVM_BC_1 = + +AM_V_LLVM_LD = $(am__v_LLVM_LD_$(V)) +am__v_LLVM_LD_ = $(am__v_LLVM_LD_$(AM_DEFAULT_VERBOSITY)) +am__v_LLVM_LD_0 = @echo " LLVM.LD " $@; +am__v_LLVM_LD_1 = -clean-local: clean-python -.PHONY: clean-python +SUFFIXES += .lo.bc .o.bc + +.o.o.bc: + $(AM_V_LLVM_BC)$(COMPILE) -emit-llvm -c -o $@ $(patsubst %.o,%.c,$<) +.lo.lo.bc: + $(AM_V_LLVM_BC)$(COMPILE) -emit-llvm -c -o $@ $(patsubst %.lo,%.c,$<) + +%.cg.json: %.bc tools/frr-llvm-cg + tools/frr-llvm-cg -o $@ $< +%.cg.dot: %.cg.json + $(PYTHON) $(top_srcdir)/python/callgraph-dot.py $< $@ +%.cg.svg: %.cg.dot + @echo if the following command fails, you need to install graphviz. + @echo also, the output is nondeterministic. run it multiple times and use the nicest output. + @echo tuning parameters may yield nicer looking graphs as well. + fdp -GK=0.7 -Gstart=42231337 -Gmaxiter=2000 -Elen=2 -Gnodesep=1.5 -Tsvg -o$@ $< +# don't delete intermediaries +.PRECIOUS: %.cg.json %.cg.dot + +# .la.bc, .a.bc and .bc targets are generated by +# python/makefile.py +LLVM_LINK = llvm-link-$(llvm_version) + +clean-local: clean-python clean-llvm-bitcode +.PHONY: clean-python clean-llvm-bitcode clean-python: find . -name __pycache__ -o -name .pytest_cache | xargs rm -rf find . -name "*.pyc" -o -name "*_clippy.c" | xargs rm -f +clean-llvm-bitcode: + find . -name "*.bc" -o -name "*.cg.json" -o -name "*.cg.dot" -o -name "*.cg.svg" | xargs rm -f + redistclean: $(MAKE) distclean CONFIG_CLEAN_FILES="$(filter-out $(EXTRA_DIST), $(CONFIG_CLEAN_FILES))" diff --git a/README.md b/README.md index a7725d5e4e..937728c9e6 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,7 @@ +

+Icon +

+ FRRouting ========= @@ -20,20 +24,22 @@ FRR currently supports the following protocols: * Babel * PBR * OpenFabric +* VRRP * EIGRP (alpha) * NHRP (alpha) Installation & Use ------------------ -Packages are available for various distributions on our +For source tarballs, see the [releases page](https://github.com/FRRouting/frr/releases). -Snaps are also available [here](https://snapcraft.io/frr). +For Debian and its derivatives, use the APT repository at +[https://deb.frrouting.org/](https://deb.frrouting.org/). Instructions on building and installing from source for supported platforms may -be found -[here](http://docs.frrouting.org/projects/dev-guide/en/latest/building.html). +be found in the +[developer docs](http://docs.frrouting.org/projects/dev-guide/en/latest/building.html). Once installed, please refer to the [user guide](http://docs.frrouting.org/) for instructions on use. diff --git a/alpine/APKBUILD.in b/alpine/APKBUILD.in index 0f56a427dd..d6c6c5f0af 100644 --- a/alpine/APKBUILD.in +++ b/alpine/APKBUILD.in @@ -1,25 +1,24 @@ # Maintainer: Arthur Jones pkgname=frr +arch="all" pkgver=@VERSION@ pkgrel=0 -pkgdesc="Free Range Routing is a fork of quagga" +pkgdesc="FRRouting is a fork of quagga" url="https://frrouting.org/" -arch="x86_64" license="GPL-2.0" -depends="json-c c-ares ipsec-tools iproute2 python py-ipaddr bash" +depends="json-c c-ares iproute2 python3 bash" makedepends="ncurses-dev net-snmp-dev gawk texinfo perl - acct autoconf automake bash - binutils bison bsd-compat-headers build-base - c-ares c-ares-dev ca-certificates cryptsetup-libs curl - device-mapper-libs expat fakeroot flex fortify-headers gdbm - git gmp isl json-c-dev kmod lddtree libacl libatomic libattr - libblkid libburn libbz2 libc-dev libcap libcurl libedit libffi libgcc - libgomp libisoburn libisofs libltdl libressl libssh2 - libstdc++ libtool libuuid libyang-dev linux-headers lzip lzo m4 make mkinitfs mpc1 - mpfr3 mtools musl-dev ncurses-libs ncurses-terminfo ncurses-terminfo-base - patch pax-utils pcre perl pkgconf python2 python2-dev readline - readline-dev sqlite-libs squashfs-tools sudo tar texinfo xorriso xz-libs - py-sphinx" + acct autoconf automake bash binutils bison bsd-compat-headers build-base + c-ares c-ares-dev ca-certificates cryptsetup-libs curl device-mapper-libs + expat fakeroot flex fortify-headers gdbm git gmp isl json-c-dev kmod + lddtree libacl libatomic libattr libblkid libburn libbz2 libc-dev + libcap-dev libcurl libedit libffi libgcc libgomp libisoburn libisofs + libltdl libressl libssh2 libstdc++ libtool libuuid libyang-dev + linux-headers lzip lzo m4 make mkinitfs mpc1 mpfr4 mtools musl-dev + ncurses-libs ncurses-terminfo ncurses-terminfo-base patch pax-utils pcre + perl pkgconf python3 python3-dev readline readline-dev sqlite-libs + squashfs-tools sudo tar texinfo xorriso xz-libs py-pip rtrlib rtrlib-dev + py3-sphinx" checkdepends="pytest py-setuptools" install="$pkgname.pre-install $pkgname.pre-deinstall $pkgname.post-deinstall" subpackages="$pkgname-dev $pkgname-doc $pkgname-dbg" @@ -35,6 +34,7 @@ _user=frr build() { cd "$builddir" + ./configure \ --prefix=/usr \ --sbindir=$_sbindir \ @@ -42,6 +42,7 @@ build() { --libdir=$_libdir \ --localstatedir=$_localstatedir \ --enable-systemd=no \ + --enable-rpki \ --enable-vtysh \ --enable-multipath=64 \ --enable-vty-group=frrvty \ diff --git a/babeld/babel_interface.c b/babeld/babel_interface.c index 0ff89abc49..7d9ccdb6d7 100644 --- a/babeld/babel_interface.c +++ b/babeld/babel_interface.c @@ -28,6 +28,7 @@ THE SOFTWARE. #include "vector.h" #include "distribute.h" #include "lib_errors.h" +#include "network.h" #include "babel_main.h" #include "util.h" @@ -39,9 +40,10 @@ THE SOFTWARE. #include "neighbour.h" #include "route.h" #include "xroute.h" -#include "babel_memory.h" #include "babel_errors.h" +DEFINE_MTYPE_STATIC(BABELD, BABEL_IF, "Babel Interface") + #define IS_ENABLE(ifp) (babel_enable_if_lookup(ifp->name) >= 0) static int babel_enable_if_lookup (const char *ifname); @@ -57,16 +59,18 @@ static void babel_interface_free (babel_interface_nfo *bi); static vector babel_enable_if; /* enable interfaces (by cmd). */ -static struct cmd_node babel_interface_node = /* babeld's interface node. */ -{ - INTERFACE_NODE, - "%s(config-if)# ", - 1 /* VTYSH */ +static int interface_config_write(struct vty *vty); +static struct cmd_node babel_interface_node = { + .name = "interface", + .node = INTERFACE_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-if)# ", + .config_write = interface_config_write, }; int -babel_interface_up (int cmd, struct zclient *client, zebra_size_t length, vrf_id_t vrf) +babel_interface_up (ZAPI_CALLBACK_ARGS) { struct stream *s = NULL; struct interface *ifp = NULL; @@ -74,7 +78,7 @@ babel_interface_up (int cmd, struct zclient *client, zebra_size_t length, vrf_id debugf(BABEL_DEBUG_IF, "receive a 'interface up'"); s = zclient->ibuf; - ifp = zebra_interface_state_read(s, vrf); /* it updates iflist */ + ifp = zebra_interface_state_read(s, vrf_id); /* it updates iflist */ if (ifp == NULL) { return 0; @@ -85,16 +89,10 @@ babel_interface_up (int cmd, struct zclient *client, zebra_size_t length, vrf_id } int -babel_interface_down (int cmd, struct zclient *client, zebra_size_t length, vrf_id_t vrf) +babel_ifp_down(struct interface *ifp) { - struct stream *s = NULL; - struct interface *ifp = NULL; - debugf(BABEL_DEBUG_IF, "receive a 'interface down'"); - s = zclient->ibuf; - ifp = zebra_interface_state_read(s, vrf); /* it updates iflist */ - if (ifp == NULL) { return 0; } @@ -103,51 +101,28 @@ babel_interface_down (int cmd, struct zclient *client, zebra_size_t length, vrf_ return 0; } -int -babel_interface_add (int cmd, struct zclient *client, zebra_size_t length, vrf_id_t vrf) +int babel_ifp_create (struct interface *ifp) { - struct interface *ifp = NULL; - debugf(BABEL_DEBUG_IF, "receive a 'interface add'"); - /* read and add the interface in the iflist. */ - ifp = zebra_interface_add_read (zclient->ibuf, vrf); - - if (ifp == NULL) { - return 0; - } - interface_recalculate(ifp); - return 0; -} + + return 0; + } int -babel_interface_delete (int cmd, struct zclient *client, zebra_size_t length, vrf_id_t vrf) +babel_ifp_destroy(struct interface *ifp) { - struct interface *ifp; - struct stream *s; - debugf(BABEL_DEBUG_IF, "receive a 'interface delete'"); - s = zclient->ibuf; - ifp = zebra_interface_state_read(s, vrf); /* it updates iflist */ - - if (ifp == NULL) - return 0; - if (IS_ENABLE(ifp)) interface_reset(ifp); - /* To support pseudo interface do not free interface structure. */ - /* if_delete(ifp); */ - if_set_index(ifp, IFINDEX_INTERNAL); - return 0; } int -babel_interface_address_add (int cmd, struct zclient *client, - zebra_size_t length, vrf_id_t vrf) +babel_interface_address_add (ZAPI_CALLBACK_ARGS) { babel_interface_nfo *babel_ifp; struct connected *ifc; @@ -156,7 +131,7 @@ babel_interface_address_add (int cmd, struct zclient *client, debugf(BABEL_DEBUG_IF, "receive a 'interface address add'"); ifc = zebra_interface_address_read (ZEBRA_INTERFACE_ADDRESS_ADD, - zclient->ibuf, vrf); + zclient->ibuf, vrf_id); if (ifc == NULL) return 0; @@ -183,8 +158,7 @@ babel_interface_address_add (int cmd, struct zclient *client, } int -babel_interface_address_delete (int cmd, struct zclient *client, - zebra_size_t length, vrf_id_t vrf) +babel_interface_address_delete (ZAPI_CALLBACK_ARGS) { babel_interface_nfo *babel_ifp; struct connected *ifc; @@ -193,7 +167,7 @@ babel_interface_address_delete (int cmd, struct zclient *client, debugf(BABEL_DEBUG_IF, "receive a 'interface address delete'"); ifc = zebra_interface_address_read (ZEBRA_INTERFACE_ADDRESS_DELETE, - zclient->ibuf, vrf); + zclient->ibuf, vrf_id); if (ifc == NULL) return 0; @@ -213,6 +187,7 @@ babel_interface_address_delete (int cmd, struct zclient *client, send_request(ifc->ifp, NULL, 0); send_update(ifc->ifp, 0, NULL, 0); + connected_free(&ifc); return 0; } @@ -718,8 +693,7 @@ interface_recalculate(struct interface *ifp) rc = resize_receive_buffer(mtu); if(rc < 0) - zlog_warn("couldn't resize " - "receive buffer for interface %s (%d) (%d bytes).\n", + zlog_warn("couldn't resize receive buffer for interface %s (%d) (%d bytes).\n", ifp->name, ifp->ifindex, mtu); memset(&mreq, 0, sizeof(mreq)); @@ -922,8 +896,7 @@ static void show_babel_neighbour_sub (struct vty *vty, struct neighbour *neigh) { vty_out (vty, - "Neighbour %s dev %s reach %04x rxcost %d txcost %d " - "rtt %s rttcost %d%s.\n", + "Neighbour %s dev %s reach %04x rxcost %d txcost %d rtt %s rttcost %d%s.\n", format_address(neigh->address), neigh->ifp->name, neigh->reach, @@ -998,7 +971,7 @@ show_babel_routes_sub(struct babel_route *route, struct vty *vty, channels[0] = '\0'; else { int k, j = 0; - snprintf(channels, 100, " chan ("); + snprintf(channels, sizeof(channels), " chan ("); j = strlen(channels); for(k = 0; k < DIVERSITY_HOPS; k++) { if(route->channels[k] == 0) @@ -1014,8 +987,7 @@ show_babel_routes_sub(struct babel_route *route, struct vty *vty, } vty_out (vty, - "%s metric %d refmetric %d id %s seqno %d%s age %d " - "via %s neigh %s%s%s%s\n", + "%s metric %d refmetric %d id %s seqno %d%s age %d via %s neigh %s%s%s%s\n", format_prefix(route->src->prefix, route->src->plen), route_metric(route), route->refmetric, format_eui64(route->src->id), @@ -1096,7 +1068,7 @@ DEFUN (show_babel_route_prefix, vty_out (vty, "%% Malformed address\n"); return CMD_WARNING; } - + routes = route_stream(0); if(routes) { while(1) { @@ -1261,6 +1233,11 @@ DEFUN (show_babel_parameters, return CMD_SUCCESS; } +int babel_ifp_up(struct interface *ifp) +{ + return 0; +} + void babel_if_init(void) { @@ -1271,7 +1248,7 @@ babel_if_init(void) babel_enable_if = vector_init (1); /* install interface node and commands */ - install_node (&babel_interface_node, interface_config_write); + install_node(&babel_interface_node); if_cmd_init(); install_element(BABEL_NODE, &babel_network_cmd); @@ -1418,7 +1395,7 @@ babel_interface_allocate (void) /* All flags are unset */ babel_ifp->bucket_time = babel_now.tv_sec; babel_ifp->bucket = BUCKET_TOKENS_MAX; - babel_ifp->hello_seqno = (random() & 0xFFFF); + babel_ifp->hello_seqno = (frr_weak_random() & 0xFFFF); babel_ifp->rtt_min = 10000; babel_ifp->rtt_max = 120000; babel_ifp->max_rtt_penalty = 150; diff --git a/babeld/babel_interface.h b/babeld/babel_interface.h index d9e2745827..9833827927 100644 --- a/babeld/babel_interface.h +++ b/babeld/babel_interface.h @@ -121,6 +121,11 @@ int babel_interface_delete (int, struct zclient *, zebra_size_t, vrf_id_t); int babel_interface_address_add (int, struct zclient *, zebra_size_t, vrf_id_t); int babel_interface_address_delete (int, struct zclient *, zebra_size_t, vrf_id_t); +int babel_ifp_create(struct interface *ifp); +int babel_ifp_up(struct interface *ifp); +int babel_ifp_down(struct interface *ifp); +int babel_ifp_destroy(struct interface *ifp); + unsigned jitter(babel_interface_nfo *, int); unsigned update_jitter(babel_interface_nfo *babel_ifp, int urgent); /* return "true" if "address" is one of our ipv6 addresses */ diff --git a/babeld/babel_main.c b/babeld/babel_main.c index eaff97a495..14e583a35c 100644 --- a/babeld/babel_main.c +++ b/babeld/babel_main.c @@ -68,13 +68,13 @@ const unsigned char ones[16] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; -static const char *state_file = DAEMON_VTY_DIR "/babel-state"; +static char state_file[1024]; unsigned char protocol_group[16]; /* babel's link-local multicast address */ int protocol_port; /* babel's port */ int protocol_socket = -1; /* socket: communicate with others babeld */ -static char babel_config_default[] = SYSCONFDIR BABEL_DEFAULT_CONFIG; +static const char babel_config_default[] = SYSCONFDIR BABEL_DEFAULT_CONFIG; static char *babel_vty_addr = NULL; static int babel_vty_port = BABEL_VTY_PORT; @@ -136,10 +136,11 @@ struct option longopts[] = { 0 } }; -static const struct frr_yang_module_info *babeld_yang_modules[] = - { - &frr_interface_info, - }; +static const struct frr_yang_module_info *const babeld_yang_modules[] = { + &frr_filter_info, + &frr_interface_info, + &frr_vrf_info, +}; FRR_DAEMON_INFO(babeld, BABELD, .vty_port = BABEL_VTY_PORT, @@ -187,6 +188,9 @@ main(int argc, char **argv) } } + snprintf(state_file, sizeof(state_file), "%s/%s", + frr_vtydir, "babel-state"); + /* create the threads handler */ master = frr_init (); @@ -199,6 +203,8 @@ main(int argc, char **argv) babel_replace_by_null(STDIN_FILENO); /* init some quagga's dependencies, and babeld's commands */ + if_zapi_callbacks(babel_ifp_create, babel_ifp_up, + babel_ifp_down, babel_ifp_destroy); babeld_quagga_init(); /* init zebra client's structure and it's commands */ /* this replace kernel_setup && kernel_setup_socket */ diff --git a/babeld/babel_memory.c b/babeld/babel_memory.c deleted file mode 100644 index a10b7791e7..0000000000 --- a/babeld/babel_memory.c +++ /dev/null @@ -1,30 +0,0 @@ -/* babeld memory type definitions - * - * Copyright (C) 2017 Donald Sharp - * - * This file is part of FRR - * - * FRR is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2, or (at your option) any - * later version. - * - * FRR is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with FRR; see the file COPYING. If not, write to the Free - * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "babel_memory.h" - -DEFINE_MGROUP(BABELD, "babeld") -DEFINE_MTYPE(BABELD, BABEL, "Babel Structure") -DEFINE_MTYPE(BABELD, BABEL_IF, "Babel Interface") diff --git a/babeld/babel_zebra.c b/babeld/babel_zebra.c index e909f8ea7a..d10d418572 100644 --- a/babeld/babel_zebra.c +++ b/babeld/babel_zebra.c @@ -39,7 +39,7 @@ void babelz_zebra_init(void); struct zclient *zclient; /* Debug types */ -static struct { +static const struct { int type; int str_min_len; const char *str; @@ -56,8 +56,7 @@ static struct { /* Zebra route add and delete treatment. */ static int -babel_zebra_read_route (int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf) +babel_zebra_read_route (ZAPI_CALLBACK_ARGS) { struct zapi_route api; @@ -68,7 +67,7 @@ babel_zebra_read_route (int command, struct zclient *zclient, if (CHECK_FLAG(api.message, ZAPI_MESSAGE_SRCPFX)) return 0; - if (command == ZEBRA_REDISTRIBUTE_ROUTE_ADD) { + if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD) { babel_route_add(&api); } else { babel_route_delete(&api); @@ -241,10 +240,6 @@ void babelz_zebra_init(void) zclient_init(zclient, ZEBRA_ROUTE_BABEL, 0, &babeld_privs); zclient->zebra_connected = babel_zebra_connected; - zclient->interface_add = babel_interface_add; - zclient->interface_delete = babel_interface_delete; - zclient->interface_up = babel_interface_up; - zclient->interface_down = babel_interface_down; zclient->interface_address_add = babel_interface_address_add; zclient->interface_address_delete = babel_interface_address_delete; zclient->redistribute_route_add = babel_zebra_read_route; @@ -256,7 +251,7 @@ void babelz_zebra_init(void) install_element(CONFIG_NODE, &debug_babel_cmd); install_element(CONFIG_NODE, &no_debug_babel_cmd); - install_element(VIEW_NODE, &show_debugging_babel_cmd); + install_element(ENABLE_NODE, &show_debugging_babel_cmd); } void diff --git a/babeld/babeld.c b/babeld/babeld.c index 39451b435a..09955cfbef 100644 --- a/babeld/babeld.c +++ b/babeld/babeld.c @@ -30,6 +30,7 @@ THE SOFTWARE. #include "filter.h" #include "plist.h" #include "lib_errors.h" +#include "network.h" #include "babel_main.h" #include "babeld.h" @@ -43,9 +44,11 @@ THE SOFTWARE. #include "resend.h" #include "babel_filter.h" #include "babel_zebra.h" -#include "babel_memory.h" #include "babel_errors.h" +DEFINE_MGROUP(BABELD, "babeld") +DEFINE_MTYPE_STATIC(BABELD, BABEL, "Babel Structure") + static int babel_init_routing_process(struct thread *thread); static void babel_get_myid(void); static void babel_initial_noise(void); @@ -67,11 +70,14 @@ static time_t expiry_time; static time_t source_expiry_time; /* Babel node structure. */ +static int babel_config_write (struct vty *vty); static struct cmd_node cmd_babel_node = { + .name = "babel", .node = BABEL_NODE, + .parent_node = CONFIG_NODE, .prompt = "%s(config-router)# ", - .vtysh = 1, + .config_write = babel_config_write, }; /* print current babel configuration on vty */ @@ -136,7 +142,7 @@ babel_create_routing_process (void) assert (babel_routing_process == NULL); /* Allocaste Babel instance. */ - babel_routing_process = XCALLOC (MTYPE_BABEL, sizeof (struct babel)); + babel_routing_process = XCALLOC(MTYPE_BABEL, sizeof(struct babel)); /* Initialize timeouts */ gettime(&babel_now); @@ -163,7 +169,6 @@ babel_create_routing_process (void) return 0; fail: XFREE(MTYPE_BABEL, babel_routing_process); - babel_routing_process = NULL; return -1; } @@ -209,7 +214,7 @@ babel_read_protocol (struct thread *thread) static int babel_init_routing_process(struct thread *thread) { - myseqno = (random() & 0xFFFF); + myseqno = (frr_weak_random() & 0xFFFF); babel_get_myid(); babel_load_state_file(); debugf(BABEL_DEBUG_COMMON, "My ID is : %s.", format_eui64(myid)); @@ -322,7 +327,6 @@ babel_clean_routing_process(void) distribute_list_delete(&babel_routing_process->distribute_ctx); XFREE(MTYPE_BABEL, babel_routing_process); - babel_routing_process = NULL; } /* Function used with timeout. */ @@ -409,6 +413,7 @@ babel_main_loop(struct thread *thread) } assert(0); /* this line should never be reach */ + return 0; } static void @@ -718,7 +723,7 @@ void babeld_quagga_init(void) { - install_node(&cmd_babel_node, &babel_config_write); + install_node(&cmd_babel_node); install_element(CONFIG_NODE, &router_babel_cmd); install_element(CONFIG_NODE, &no_router_babel_cmd); diff --git a/babeld/kernel.c b/babeld/kernel.c index d4c962af3b..e3c76bdd92 100644 --- a/babeld/kernel.c +++ b/babeld/kernel.c @@ -103,10 +103,8 @@ kernel_route(enum babel_kernel_routes operation, const unsigned char *pref, switch (operation) { case ROUTE_ADD: return zebra_route(1, family, pref, plen, gate, ifindex, metric); - break; case ROUTE_FLUSH: return zebra_route(0, family, pref, plen, gate, ifindex, metric); - break; case ROUTE_MODIFY: if(newmetric == metric && memcmp(newgate, gate, 16) == 0 && newifindex == ifindex) @@ -119,7 +117,6 @@ kernel_route(enum babel_kernel_routes operation, const unsigned char *pref, rc = zebra_route(1, family, pref, plen, newgate, newifindex, newmetric); return rc; - break; } return 0; diff --git a/babeld/message.c b/babeld/message.c index 794b6e9976..c9a10cb1c0 100644 --- a/babeld/message.c +++ b/babeld/message.c @@ -710,8 +710,7 @@ fill_rtt_message(struct interface *ifp) DO_HTONL(babel_ifp->sendbuf + babel_ifp->buffered_hello + 10, time); return 1; } else { - flog_err(EC_BABEL_PACKET, "No space left for timestamp sub-TLV " - "(this shouldn't happen)"); + flog_err(EC_BABEL_PACKET, "No space left for timestamp sub-TLV (this shouldn't happen)"); return -1; } } @@ -1115,7 +1114,9 @@ really_send_update(struct interface *ifp, if(channels_len >= 0) { accumulate_byte(ifp, 2); accumulate_byte(ifp, channels_len); - accumulate_bytes(ifp, channels, channels_len); + + if (channels && channels_len > 0) + accumulate_bytes(ifp, channels, channels_len); } end_message(ifp, MESSAGE_UPDATE, 10 + (real_plen + 7) / 8 - omit + channels_size); diff --git a/babeld/net.c b/babeld/net.c index d1f6a44142..40716a701d 100644 --- a/babeld/net.c +++ b/babeld/net.c @@ -144,7 +144,7 @@ babel_send(int s, iovec[1].iov_base = buf2; iovec[1].iov_len = buflen2; memset(&msg, 0, sizeof(msg)); - msg.msg_name = (struct sockaddr*)sin; + msg.msg_name = sin; msg.msg_namelen = slen; msg.msg_iov = iovec; msg.msg_iovlen = 2; diff --git a/babeld/route.c b/babeld/route.c index ab104aa2b1..0f6f6486f2 100644 --- a/babeld/route.c +++ b/babeld/route.c @@ -399,16 +399,14 @@ install_route(struct babel_route *route) return; if(!route_feasible(route)) - flog_err(EC_BABEL_ROUTE, "WARNING: installing unfeasible route " - "(this shouldn't happen)."); + flog_err(EC_BABEL_ROUTE, "WARNING: installing unfeasible route (this shouldn't happen)."); i = find_route_slot(route->src->prefix, route->src->plen, NULL); assert(i >= 0 && i < route_slots); if(routes[i] != route && routes[i]->installed) { flog_err(EC_BABEL_ROUTE, - "WARNING: attempting to install duplicate route " - "(this shouldn't happen)."); + "WARNING: attempting to install duplicate route (this shouldn't happen)."); return; } @@ -465,8 +463,7 @@ switch_routes(struct babel_route *old, struct babel_route *new) return; if(!route_feasible(new)) - flog_err(EC_BABEL_ROUTE, "WARNING: switching to unfeasible route " - "(this shouldn't happen)."); + flog_err(EC_BABEL_ROUTE, "WARNING: switching to unfeasible route (this shouldn't happen)."); rc = kernel_route(ROUTE_MODIFY, old->src->prefix, old->src->plen, old->nexthop, old->neigh->ifp->ifindex, @@ -835,8 +832,7 @@ update_route(const unsigned char *router_id, in a timely manner. If the source remains the same, we ignore the update. */ if(!feasible && route->installed) { - debugf(BABEL_DEBUG_COMMON,"Unfeasible update for installed route to %s " - "(%s %d %d -> %s %d %d).", + debugf(BABEL_DEBUG_COMMON,"Unfeasible update for installed route to %s (%s %d %d -> %s %d %d).", format_prefix(src->prefix, src->plen), format_eui64(route->src->id), route->seqno, route->refmetric, diff --git a/babeld/subdir.am b/babeld/subdir.am index 7081c730aa..7827e7dc58 100644 --- a/babeld/subdir.am +++ b/babeld/subdir.am @@ -7,9 +7,9 @@ noinst_LIBRARIES += babeld/libbabel.a sbin_PROGRAMS += babeld/babeld dist_examples_DATA += babeld/babeld.conf.sample vtysh_scan += \ - $(top_srcdir)/babeld/babel_interface.c \ - $(top_srcdir)/babeld/babel_zebra.c \ - $(top_srcdir)/babeld/babeld.c \ + babeld/babel_interface.c \ + babeld/babel_zebra.c \ + babeld/babeld.c \ # end endif @@ -17,7 +17,6 @@ babeld_libbabel_a_SOURCES = \ babeld/babel_errors.c \ babeld/babel_filter.c \ babeld/babel_interface.c \ - babeld/babel_memory.c \ babeld/babel_zebra.c \ babeld/babeld.c \ babeld/kernel.c \ @@ -36,7 +35,6 @@ noinst_HEADERS += \ babeld/babel_filter.h \ babeld/babel_interface.h \ babeld/babel_main.h \ - babeld/babel_memory.h \ babeld/babel_zebra.h \ babeld/babeld.h \ babeld/kernel.h \ diff --git a/babeld/util.c b/babeld/util.c index c6606e4f0e..e99bd861dc 100644 --- a/babeld/util.c +++ b/babeld/util.c @@ -39,6 +39,8 @@ THE SOFTWARE. #include #include +#include "lib/network.h" + #include "babel_main.h" #include "babeld.h" #include "util.h" @@ -51,7 +53,7 @@ roughly(int value) else if(value <= 1) return value; else - return value * 3 / 4 + random() % (value / 2); + return value * 3 / 4 + frr_weak_random() % (value / 2); } /* d = s1 - s2 */ @@ -145,7 +147,7 @@ timeval_min_sec(struct timeval *d, time_t secs) { if(d->tv_sec == 0 || d->tv_sec > secs) { d->tv_sec = secs; - d->tv_usec = random() % 1000000; + d->tv_usec = frr_weak_random() % 1000000; } } diff --git a/babeld/util.h b/babeld/util.h index 7b836c2e4c..9310040571 100644 --- a/babeld/util.h +++ b/babeld/util.h @@ -27,6 +27,9 @@ THE SOFTWARE. #include "babeld.h" #include "babel_main.h" #include "log.h" +#include "memory.h" + +DECLARE_MGROUP(BABELD) #if defined(i386) || defined(__mc68020__) || defined(__x86_64__) #define DO_NTOHS(_d, _s) do{ _d = ntohs(*(const unsigned short*)(_s)); }while(0) @@ -119,7 +122,7 @@ void uchar_to_inaddr(struct in_addr *dest, const unsigned char *src); void in6addr_to_uchar(unsigned char *dest, const struct in6_addr *src); void uchar_to_in6addr(struct in6_addr *dest, const unsigned char *src); int daemonise(void); -const unsigned char v4prefix[16]; +extern const unsigned char v4prefix[16]; /* If debugging is disabled, we want to avoid calling format_address for every omitted debugging message. So debug is a macro. But diff --git a/bfdd/bfd.c b/bfdd/bfd.c index e9645824f2..cf292a8363 100644 --- a/bfdd/bfd.c +++ b/bfdd/bfd.c @@ -28,22 +28,20 @@ #include #include "lib/jhash.h" +#include "lib/network.h" #include "bfd.h" -DEFINE_QOBJ_TYPE(bfd_session); +DEFINE_MTYPE_STATIC(BFDD, BFDD_CONFIG, "long-lived configuration memory") +DEFINE_MTYPE_STATIC(BFDD, BFDD_PROFILE, "long-lived profile memory") +DEFINE_MTYPE_STATIC(BFDD, BFDD_SESSION_OBSERVER, "Session observer") +DEFINE_MTYPE_STATIC(BFDD, BFDD_VRF, "BFD VRF") /* * Prototypes */ -void gen_bfd_key(struct bfd_key *key, struct sockaddr_any *peer, - struct sockaddr_any *local, bool mhop, const char *ifname, - const char *vrfname); - static uint32_t ptm_bfd_gen_ID(void); static void ptm_bfd_echo_xmt_TO(struct bfd_session *bfd); -static void bfd_session_free(struct bfd_session *bs); -static struct bfd_session *bfd_session_new(void); static struct bfd_session *bfd_find_disc(struct sockaddr_any *sa, uint32_t ldisc); static int bfd_session_update(struct bfd_session *bs, struct bfd_peer_cfg *bpc); @@ -53,13 +51,182 @@ static void bs_admin_down_handler(struct bfd_session *bs, int nstate); static void bs_down_handler(struct bfd_session *bs, int nstate); static void bs_init_handler(struct bfd_session *bs, int nstate); static void bs_up_handler(struct bfd_session *bs, int nstate); +static void bs_neighbour_admin_down_handler(struct bfd_session *bfd, + uint8_t diag); + +/** + * Remove BFD profile from all BFD sessions so we don't leave dangling + * pointers. + */ +static void bfd_profile_detach(struct bfd_profile *bp); /* Zeroed array with the size of an IPv6 address. */ struct in6_addr zero_addr; +/** BFD profiles list. */ +struct bfdproflist bplist; + /* * Functions */ +struct bfd_profile *bfd_profile_lookup(const char *name) +{ + struct bfd_profile *bp; + + TAILQ_FOREACH (bp, &bplist, entry) { + if (strcmp(name, bp->name)) + continue; + + return bp; + } + + return NULL; +} + +static void bfd_profile_set_default(struct bfd_profile *bp) +{ + bp->admin_shutdown = false; + bp->detection_multiplier = BFD_DEFDETECTMULT; + bp->echo_mode = false; + bp->passive = false; + bp->minimum_ttl = BFD_DEF_MHOP_TTL; + bp->min_echo_rx = BFD_DEF_REQ_MIN_ECHO; + bp->min_rx = BFD_DEFREQUIREDMINRX; + bp->min_tx = BFD_DEFDESIREDMINTX; +} + +struct bfd_profile *bfd_profile_new(const char *name) +{ + struct bfd_profile *bp; + + /* Search for duplicates. */ + if (bfd_profile_lookup(name) != NULL) + return NULL; + + /* Allocate, name it and put into list. */ + bp = XCALLOC(MTYPE_BFDD_PROFILE, sizeof(*bp)); + strlcpy(bp->name, name, sizeof(bp->name)); + TAILQ_INSERT_TAIL(&bplist, bp, entry); + + /* Set default values. */ + bfd_profile_set_default(bp); + + return bp; +} + +void bfd_profile_free(struct bfd_profile *bp) +{ + /* Detach from any session. */ + if (bglobal.bg_shutdown == false) + bfd_profile_detach(bp); + + /* Remove from global list. */ + TAILQ_REMOVE(&bplist, bp, entry); + + XFREE(MTYPE_BFDD_PROFILE, bp); +} + +void bfd_profile_apply(const char *profname, struct bfd_session *bs) +{ + struct bfd_profile *bp; + + /* Remove previous profile if any. */ + if (bs->profile_name) { + /* We are changing profiles. */ + if (strcmp(bs->profile_name, profname)) { + XFREE(MTYPE_BFDD_PROFILE, bs->profile_name); + bs->profile_name = + XSTRDUP(MTYPE_BFDD_PROFILE, profname); + } + } else /* Save the current profile name (in case it doesn't exist). */ + bs->profile_name = XSTRDUP(MTYPE_BFDD_PROFILE, profname); + + /* Look up new profile to apply. */ + bp = bfd_profile_lookup(profname); + + /* Point to profile if it exists. */ + bs->profile = bp; + + /* Apply configuration. */ + bfd_session_apply(bs); +} + +void bfd_session_apply(struct bfd_session *bs) +{ + struct bfd_profile *bp; + uint32_t min_tx = bs->timers.desired_min_tx; + uint32_t min_rx = bs->timers.required_min_rx; + + /* Pick the source of configuration. */ + bp = bs->profile ? bs->profile : &bs->peer_profile; + + /* Set multiplier if not the default. */ + if (bs->peer_profile.detection_multiplier == BFD_DEFDETECTMULT) + bs->detect_mult = bp->detection_multiplier; + else + bs->detect_mult = bs->peer_profile.detection_multiplier; + + /* Set timers if not the default. */ + if (bs->peer_profile.min_tx == BFD_DEFDESIREDMINTX) + bs->timers.desired_min_tx = bp->min_tx; + else + bs->timers.desired_min_tx = bs->peer_profile.min_tx; + + if (bs->peer_profile.min_rx == BFD_DEFREQUIREDMINRX) + bs->timers.required_min_rx = bp->min_rx; + else + bs->timers.required_min_rx = bs->peer_profile.min_rx; + + /* We can only apply echo options on single hop sessions. */ + if (!CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) { + /* Configure remote echo if it was default. */ + if (bs->peer_profile.min_echo_rx == BFD_DEF_REQ_MIN_ECHO) + bs->timers.required_min_echo = bp->min_echo_rx; + else + bs->timers.required_min_echo = + bs->peer_profile.min_echo_rx; + + /* Toggle echo if default value. */ + if (bs->peer_profile.echo_mode == false) + bfd_set_echo(bs, bp->echo_mode); + else + bfd_set_echo(bs, bs->peer_profile.echo_mode); + } else { + /* Configure the TTL packet filter. */ + if (bs->peer_profile.minimum_ttl == BFD_DEF_MHOP_TTL) + bs->mh_ttl = bp->minimum_ttl; + else + bs->mh_ttl = bs->peer_profile.minimum_ttl; + } + + /* Toggle 'passive-mode' if default value. */ + if (bs->peer_profile.passive == false) + bfd_set_passive_mode(bs, bp->passive); + else + bfd_set_passive_mode(bs, bs->peer_profile.passive); + + /* Toggle 'no shutdown' if default value. */ + if (bs->peer_profile.admin_shutdown == false) + bfd_set_shutdown(bs, bp->admin_shutdown); + else + bfd_set_shutdown(bs, bs->peer_profile.admin_shutdown); + + /* If session interval changed negotiate new timers. */ + if (bs->ses_state == PTM_BFD_UP + && (bs->timers.desired_min_tx != min_tx + || bs->timers.required_min_rx != min_rx)) + bfd_set_polling(bs); +} + +void bfd_profile_remove(struct bfd_session *bs) +{ + /* Remove any previous set profile name. */ + XFREE(MTYPE_BFDD_PROFILE, bs->profile_name); + bs->profile = NULL; + + bfd_session_apply(bs); +} + void gen_bfd_key(struct bfd_key *key, struct sockaddr_any *peer, struct sockaddr_any *local, bool mhop, const char *ifname, const char *vrfname) @@ -88,6 +255,8 @@ void gen_bfd_key(struct bfd_key *key, struct sockaddr_any *peer, strlcpy(key->ifname, ifname, sizeof(key->ifname)); if (vrfname && vrfname[0]) strlcpy(key->vrfname, vrfname, sizeof(key->vrfname)); + else + strlcpy(key->vrfname, VRF_DEFAULT_NAME, sizeof(key->vrfname)); } struct bfd_session *bs_peer_find(struct bfd_peer_cfg *bpc) @@ -128,43 +297,57 @@ int bfd_session_enable(struct bfd_session *bs) * If the interface or VRF doesn't exist, then we must register * the session but delay its start. */ - if (bs->key.ifname[0]) { - ifp = if_lookup_by_name_all_vrf(bs->key.ifname); - if (ifp == NULL) { - log_error( - "session-enable: specified interface doesn't exists."); - return 0; - } - - vrf = vrf_lookup_by_id(ifp->vrf_id); + if (bs->key.vrfname[0]) { + vrf = vrf_lookup_by_name(bs->key.vrfname); if (vrf == NULL) { - log_error( + zlog_err( "session-enable: specified VRF doesn't exists."); return 0; } } - if (bs->key.vrfname[0]) { - vrf = vrf_lookup_by_name(bs->key.vrfname); - if (vrf == NULL) { - log_error( - "session-enable: specified VRF doesn't exists."); + if (!vrf_is_backend_netns() && vrf && vrf->vrf_id != VRF_DEFAULT + && !if_lookup_by_name(vrf->name, vrf->vrf_id)) { + zlog_err("session-enable: vrf interface %s not available yet", + vrf->name); + return 0; + } + + if (bs->key.ifname[0]) { + if (vrf) + ifp = if_lookup_by_name(bs->key.ifname, vrf->vrf_id); + else + ifp = if_lookup_by_name_all_vrf(bs->key.ifname); + if (ifp == NULL) { + zlog_err( + "session-enable: specified interface doesn't exists."); return 0; } + if (bs->key.ifname[0] && !vrf) { + vrf = vrf_lookup_by_id(ifp->vrf_id); + if (vrf == NULL) { + zlog_err( + "session-enable: specified VRF doesn't exists."); + return 0; + } + } } /* Assign interface/VRF pointers. */ bs->vrf = vrf; if (bs->vrf == NULL) bs->vrf = vrf_lookup_by_id(VRF_DEFAULT); + assert(bs->vrf); if (bs->key.ifname[0] - && BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH) == 0) + && CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH) == 0) bs->ifp = ifp; /* Sanity check: don't leak open sockets. */ if (bs->sock != -1) { - zlog_debug("session-enable: previous socket open"); + if (bglobal.debug_peer_event) + zlog_debug("session-enable: previous socket open"); + close(bs->sock); bs->sock = -1; } @@ -174,7 +357,7 @@ int bfd_session_enable(struct bfd_session *bs) * could use the destination port (3784) for the source * port we wouldn't need a socket per session. */ - if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_IPV6) == 0) { + if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_IPV6) == 0) { psock = bp_peer_socket(bs); if (psock == -1) return 0; @@ -189,8 +372,12 @@ int bfd_session_enable(struct bfd_session *bs) * protocol. */ bs->sock = psock; - bfd_recvtimer_update(bs); - ptm_bfd_start_xmt_timer(bs, false); + + /* Only start timers if we are using active mode. */ + if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_PASSIVE) == 0) { + bfd_recvtimer_update(bs); + ptm_bfd_start_xmt_timer(bs, false); + } return 0; } @@ -211,9 +398,13 @@ void bfd_session_disable(struct bfd_session *bs) /* Disable all timers. */ bfd_recvtimer_delete(bs); - bfd_echo_recvtimer_delete(bs); bfd_xmttimer_delete(bs); - bfd_echo_xmttimer_delete(bs); + ptm_bfd_echo_stop(bs); + bs->vrf = NULL; + bs->ifp = NULL; + + /* Set session down so it doesn't report UP and disabled. */ + ptm_bfd_sess_dn(bs, BD_PATH_DOWN); } static uint32_t ptm_bfd_gen_ID(void) @@ -225,8 +416,8 @@ static uint32_t ptm_bfd_gen_ID(void) * random session identification numbers. */ do { - session_id = ((random() << 16) & 0xFFFF0000) - | (random() & 0x0000FFFF); + session_id = ((frr_weak_random() << 16) & 0xFFFF0000) + | (frr_weak_random() & 0x0000FFFF); } while (session_id == 0 || bfd_id_lookup(session_id) != NULL); return session_id; @@ -247,7 +438,7 @@ void ptm_bfd_start_xmt_timer(struct bfd_session *bfd, bool is_echo) * between 75% and 90%. */ maxpercent = (bfd->detect_mult == 1) ? 16 : 26; - jitter = (xmt_TO * (75 + (random() % maxpercent))) / 100; + jitter = (xmt_TO * (75 + (frr_weak_random() % maxpercent))) / 100; /* XXX remove that division above */ if (is_echo) @@ -278,7 +469,7 @@ void ptm_bfd_echo_stop(struct bfd_session *bfd) { bfd->echo_xmt_TO = 0; bfd->echo_detect_TO = 0; - BFD_UNSET_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE); + UNSET_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE); bfd_echo_xmttimer_delete(bfd); bfd_echo_recvtimer_delete(bfd); @@ -287,11 +478,13 @@ void ptm_bfd_echo_stop(struct bfd_session *bfd) void ptm_bfd_echo_start(struct bfd_session *bfd) { bfd->echo_detect_TO = (bfd->remote_detect_mult * bfd->echo_xmt_TO); - if (bfd->echo_detect_TO > 0) + if (bfd->echo_detect_TO > 0) { + bfd_echo_recvtimer_update(bfd); ptm_bfd_echo_xmt_TO(bfd); + } } -void ptm_bfd_ses_up(struct bfd_session *bfd) +void ptm_bfd_sess_up(struct bfd_session *bfd) { int old_state = bfd->ses_state; @@ -305,17 +498,18 @@ void ptm_bfd_ses_up(struct bfd_session *bfd) /* Start sending control packets with poll bit immediately. */ ptm_bfd_snd(bfd, 0); - control_notify(bfd); + control_notify(bfd, bfd->ses_state); if (old_state != bfd->ses_state) { bfd->stats.session_up++; - log_info("state-change: [%s] %s -> %s", bs_to_string(bfd), - state_list[old_state].str, - state_list[bfd->ses_state].str); + if (bglobal.debug_peer_event) + zlog_debug("state-change: [%s] %s -> %s", + bs_to_string(bfd), state_list[old_state].str, + state_list[bfd->ses_state].str); } } -void ptm_bfd_ses_dn(struct bfd_session *bfd, uint8_t diag) +void ptm_bfd_sess_dn(struct bfd_session *bfd, uint8_t diag) { int old_state = bfd->ses_state; @@ -326,25 +520,39 @@ void ptm_bfd_ses_dn(struct bfd_session *bfd, uint8_t diag) bfd->demand_mode = 0; monotime(&bfd->downtime); - ptm_bfd_snd(bfd, 0); + /* + * Only attempt to send if we have a valid socket: + * this function might be called by session disablers and in + * this case we won't have a valid socket (i.e. interface was + * removed or VRF doesn't exist anymore). + */ + if (bfd->sock != -1) + ptm_bfd_snd(bfd, 0); /* Slow down the control packets, the connection is down. */ bs_set_slow_timers(bfd); /* only signal clients when going from up->down state */ if (old_state == PTM_BFD_UP) - control_notify(bfd); + control_notify(bfd, PTM_BFD_DOWN); /* Stop echo packet transmission if they are active */ - if (BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE)) + if (CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE)) ptm_bfd_echo_stop(bfd); + /* Stop attempting to transmit or expect control packets if passive. */ + if (CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_PASSIVE)) { + bfd_recvtimer_delete(bfd); + bfd_xmttimer_delete(bfd); + } + if (old_state != bfd->ses_state) { bfd->stats.session_down++; - log_info("state-change: [%s] %s -> %s reason:%s", - bs_to_string(bfd), state_list[old_state].str, - state_list[bfd->ses_state].str, - get_diag_str(bfd->local_diag)); + if (bglobal.debug_peer_event) + zlog_debug("state-change: [%s] %s -> %s reason:%s", + bs_to_string(bfd), state_list[old_state].str, + state_list[bfd->ses_state].str, + get_diag_str(bfd->local_diag)); } } @@ -387,19 +595,24 @@ struct bfd_session *ptm_bfd_sess_find(struct bfd_pkt *cp, if (cp->discrs.remote_discr) return bfd_find_disc(peer, ntohl(cp->discrs.remote_discr)); - /* Search for session without using discriminator. */ - ifp = if_lookup_by_index(ifindex, vrfid); - if (vrfid == VRF_DEFAULT) { - /* - * Don't use the default vrf, otherwise we won't find - * sessions that doesn't specify it. - */ - vrf = NULL; + /* + * Search for session without using discriminator. + * + * XXX: we can't trust `vrfid` because the VRF handling is not + * properly implemented. Meanwhile we should use the interface + * VRF to find out which one it belongs. + */ + ifp = if_lookup_by_index_all_vrf(ifindex); + if (ifp == NULL) { + if (vrfid != VRF_DEFAULT) + vrf = vrf_lookup_by_id(vrfid); + else + vrf = NULL; } else - vrf = vrf_lookup_by_id(vrfid); + vrf = vrf_lookup_by_id(ifp->vrf_id); gen_bfd_key(&key, peer, local, is_mhop, ifp ? ifp->name : NULL, - vrf ? vrf->name : NULL); + vrf ? vrf->name : VRF_DEFAULT_NAME); /* XXX maybe remoteDiscr should be checked for remoteHeard cases. */ return bfd_key_lookup(key); @@ -432,7 +645,7 @@ int bfd_recvtimer_cb(struct thread *t) switch (bs->ses_state) { case PTM_BFD_INIT: case PTM_BFD_UP: - ptm_bfd_ses_dn(bs, BD_CONTROL_EXPIRED); + ptm_bfd_sess_dn(bs, BD_CONTROL_EXPIRED); bfd_recvtimer_update(bs); break; @@ -455,20 +668,21 @@ int bfd_echo_recvtimer_cb(struct thread *t) switch (bs->ses_state) { case PTM_BFD_INIT: case PTM_BFD_UP: - ptm_bfd_ses_dn(bs, BD_ECHO_FAILED); + ptm_bfd_sess_dn(bs, BD_ECHO_FAILED); break; } return 0; } -static struct bfd_session *bfd_session_new(void) +struct bfd_session *bfd_session_new(void) { struct bfd_session *bs; bs = XCALLOC(MTYPE_BFDD_CONFIG, sizeof(*bs)); - QOBJ_REG(bs, bfd_session); + /* Set peer session defaults. */ + bfd_profile_set_default(&bs->peer_profile); bs->timers.desired_min_tx = BFD_DEFDESIREDMINTX; bs->timers.required_min_rx = BFD_DEFREQUIREDMINRX; @@ -503,8 +717,7 @@ int bfd_session_update_label(struct bfd_session *bs, const char *nlabel) return -1; } - if (pl_new(nlabel, bs) == NULL) - return -1; + pl_new(nlabel, bs); return 0; } @@ -527,75 +740,58 @@ int bfd_session_update_label(struct bfd_session *bs, const char *nlabel) static void _bfd_session_update(struct bfd_session *bs, struct bfd_peer_cfg *bpc) { - if (bpc->bpc_echo) { - /* Check if echo mode is already active. */ - if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO)) - goto skip_echo; - - BFD_SET_FLAG(bs->flags, BFD_SESS_FLAG_ECHO); - - /* Activate/update echo receive timeout timer. */ - bs_echo_timer_handler(bs); - } else { - /* Check if echo mode is already disabled. */ - if (!BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO)) - goto skip_echo; - - BFD_UNSET_FLAG(bs->flags, BFD_SESS_FLAG_ECHO); - ptm_bfd_echo_stop(bs); - } - -skip_echo: - if (bpc->bpc_has_txinterval) + if (bpc->bpc_has_txinterval) { bs->timers.desired_min_tx = bpc->bpc_txinterval * 1000; + bs->peer_profile.min_tx = bs->timers.desired_min_tx; + } - if (bpc->bpc_has_recvinterval) + if (bpc->bpc_has_recvinterval) { bs->timers.required_min_rx = bpc->bpc_recvinterval * 1000; + bs->peer_profile.min_rx = bs->timers.required_min_rx; + } - if (bpc->bpc_has_detectmultiplier) + if (bpc->bpc_has_detectmultiplier) { bs->detect_mult = bpc->bpc_detectmultiplier; + bs->peer_profile.detection_multiplier = bs->detect_mult; + } - if (bpc->bpc_has_echointerval) + if (bpc->bpc_has_echointerval) { bs->timers.required_min_echo = bpc->bpc_echointerval * 1000; + bs->peer_profile.min_echo_rx = bs->timers.required_min_echo; + } if (bpc->bpc_has_label) bfd_session_update_label(bs, bpc->bpc_label); - if (bpc->bpc_shutdown) { - /* Check if already shutdown. */ - if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN)) - return; - - BFD_SET_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN); - - /* Disable all events. */ - bfd_recvtimer_delete(bs); - bfd_echo_recvtimer_delete(bs); - bfd_xmttimer_delete(bs); - bfd_echo_xmttimer_delete(bs); - - /* Change and notify state change. */ - bs->ses_state = PTM_BFD_ADM_DOWN; - control_notify(bs); + if (bpc->bpc_cbit) + SET_FLAG(bs->flags, BFD_SESS_FLAG_CBIT); + else + UNSET_FLAG(bs->flags, BFD_SESS_FLAG_CBIT); - /* Don't try to send packets with a disabled session. */ - if (bs->sock != -1) - ptm_bfd_snd(bs, 0); - } else { - /* Check if already working. */ - if (!BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN)) - return; + if (bpc->bpc_has_minimum_ttl) { + bs->mh_ttl = bpc->bpc_minimum_ttl; + bs->peer_profile.minimum_ttl = bpc->bpc_minimum_ttl; + } - BFD_UNSET_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN); + bs->peer_profile.echo_mode = bpc->bpc_echo; + bfd_set_echo(bs, bpc->bpc_echo); - /* Change and notify state change. */ - bs->ses_state = PTM_BFD_DOWN; - control_notify(bs); + /* + * Shutdown needs to be the last in order to avoid timers enable when + * the session is disabled. + */ + bs->peer_profile.admin_shutdown = bpc->bpc_shutdown; + bfd_set_passive_mode(bs, bpc->bpc_passive); + bfd_set_shutdown(bs, bpc->bpc_shutdown); - /* Enable all timers. */ - bfd_recvtimer_update(bs); - bfd_xmttimer_update(bs, bs->xmt_TO); - } + /* + * Apply profile last: it also calls `bfd_set_shutdown`. + * + * There is no problem calling `shutdown` twice if the value doesn't + * change or if it is overriden by peer specific configuration. + */ + if (bpc->bpc_has_profile) + bfd_profile_apply(bpc->bpc_profile, bs); } static int bfd_session_update(struct bfd_session *bs, struct bfd_peer_cfg *bpc) @@ -611,7 +807,7 @@ static int bfd_session_update(struct bfd_session *bs, struct bfd_peer_cfg *bpc) return 0; } -static void bfd_session_free(struct bfd_session *bs) +void bfd_session_free(struct bfd_session *bs) { struct bfd_session_observer *bso; @@ -632,7 +828,7 @@ static void bfd_session_free(struct bfd_session *bs) pl_free(bs->pl); - QOBJ_UNREG(bs); + XFREE(MTYPE_BFDD_PROFILE, bs->profile_name); XFREE(MTYPE_BFDD_CONFIG, bs); } @@ -652,10 +848,6 @@ struct bfd_session *ptm_bfd_sess_new(struct bfd_peer_cfg *bpc) /* Get BFD session storage with its defaults. */ bfd = bfd_session_new(); - if (bfd == NULL) { - log_error("session-new: allocation failed"); - return NULL; - } /* * Store interface/VRF name in case we need to delay session @@ -668,10 +860,13 @@ struct bfd_session *ptm_bfd_sess_new(struct bfd_peer_cfg *bpc) if (bpc->bpc_has_vrfname) strlcpy(bfd->key.vrfname, bpc->bpc_vrfname, sizeof(bfd->key.vrfname)); + else + strlcpy(bfd->key.vrfname, VRF_DEFAULT_NAME, + sizeof(bfd->key.vrfname)); /* Copy remaining data. */ if (bpc->bpc_ipv4 == false) - BFD_SET_FLAG(bfd->flags, BFD_SESS_FLAG_IPV6); + SET_FLAG(bfd->flags, BFD_SESS_FLAG_IPV6); bfd->key.family = (bpc->bpc_ipv4) ? AF_INET : AF_INET6; switch (bfd->key.family) { @@ -695,10 +890,21 @@ struct bfd_session *ptm_bfd_sess_new(struct bfd_peer_cfg *bpc) } if (bpc->bpc_mhop) - BFD_SET_FLAG(bfd->flags, BFD_SESS_FLAG_MH); + SET_FLAG(bfd->flags, BFD_SESS_FLAG_MH); bfd->key.mhop = bpc->bpc_mhop; + if (bs_registrate(bfd) == NULL) + return NULL; + + /* Apply other configurations. */ + _bfd_session_update(bfd, bpc); + + return bfd; +} + +struct bfd_session *bs_registrate(struct bfd_session *bfd) +{ /* Registrate session into data structures. */ bfd_key_insert(bfd); bfd->discrs.my_discr = ptm_bfd_gen_ID(); @@ -715,17 +921,15 @@ struct bfd_session *ptm_bfd_sess_new(struct bfd_peer_cfg *bpc) if (bfd->key.ifname[0] || bfd->key.vrfname[0] || bfd->sock == -1) bs_observer_add(bfd); - /* Apply other configurations. */ - _bfd_session_update(bfd, bpc); - - log_info("session-new: %s", bs_to_string(bfd)); + if (bglobal.debug_peer_event) + zlog_debug("session-new: %s", bs_to_string(bfd)); control_notify_config(BCM_NOTIFY_CONFIG_ADD, bfd); return bfd; } -int ptm_bfd_ses_del(struct bfd_peer_cfg *bpc) +int ptm_bfd_sess_del(struct bfd_peer_cfg *bpc) { struct bfd_session *bs; @@ -736,13 +940,13 @@ int ptm_bfd_ses_del(struct bfd_peer_cfg *bpc) /* This pointer is being referenced, don't let it be deleted. */ if (bs->refcount > 0) { - log_error("session-delete: refcount failure: %" PRIu64 - " references", - bs->refcount); + zlog_err("session-delete: refcount failure: %" PRIu64" references", + bs->refcount); return -1; } - log_info("session-delete: %s", bs_to_string(bs)); + if (bglobal.debug_peer_event) + zlog_debug("session-delete: %s", bs_to_string(bs)); control_notify_config(BCM_NOTIFY_CONFIG_DELETE, bs); @@ -798,6 +1002,10 @@ static void bs_down_handler(struct bfd_session *bs, int nstate) * bring it up. */ bs->ses_state = PTM_BFD_INIT; + + /* Answer peer with INIT immediately in passive mode. */ + if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_PASSIVE)) + ptm_bfd_snd(bs, 0); break; case PTM_BFD_INIT: @@ -805,11 +1013,13 @@ static void bs_down_handler(struct bfd_session *bs, int nstate) * Remote peer told us his path is up, lets turn * activate the session. */ - ptm_bfd_ses_up(bs); + ptm_bfd_sess_up(bs); break; default: - log_debug("state-change: unhandled neighbor state: %d", nstate); + if (bglobal.debug_peer_event) + zlog_debug("state-change: unhandled neighbor state: %d", + nstate); break; } } @@ -832,22 +1042,60 @@ static void bs_init_handler(struct bfd_session *bs, int nstate) case PTM_BFD_INIT: case PTM_BFD_UP: /* We agreed on the settings and the path is up. */ - ptm_bfd_ses_up(bs); + ptm_bfd_sess_up(bs); break; default: - log_debug("state-change: unhandled neighbor state: %d", nstate); + if (bglobal.debug_peer_event) + zlog_debug("state-change: unhandled neighbor state: %d", + nstate); break; } } +static void bs_neighbour_admin_down_handler(struct bfd_session *bfd, + uint8_t diag) +{ + int old_state = bfd->ses_state; + + bfd->local_diag = diag; + bfd->discrs.remote_discr = 0; + bfd->ses_state = PTM_BFD_DOWN; + bfd->polling = 0; + bfd->demand_mode = 0; + monotime(&bfd->downtime); + + /* Slow down the control packets, the connection is down. */ + bs_set_slow_timers(bfd); + + /* only signal clients when going from up->down state */ + if (old_state == PTM_BFD_UP) + control_notify(bfd, PTM_BFD_ADM_DOWN); + + /* Stop echo packet transmission if they are active */ + if (CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE)) + ptm_bfd_echo_stop(bfd); + + if (old_state != bfd->ses_state) { + bfd->stats.session_down++; + if (bglobal.debug_peer_event) + zlog_debug("state-change: [%s] %s -> %s reason:%s", + bs_to_string(bfd), state_list[old_state].str, + state_list[bfd->ses_state].str, + get_diag_str(bfd->local_diag)); + } +} + static void bs_up_handler(struct bfd_session *bs, int nstate) { switch (nstate) { case PTM_BFD_ADM_DOWN: + bs_neighbour_admin_down_handler(bs, BD_ADMIN_DOWN); + break; + case PTM_BFD_DOWN: /* Peer lost or asked to shutdown connection. */ - ptm_bfd_ses_dn(bs, BD_NEIGHBOR_DOWN); + ptm_bfd_sess_dn(bs, BD_NEIGHBOR_DOWN); break; case PTM_BFD_INIT: @@ -856,7 +1104,9 @@ static void bs_up_handler(struct bfd_session *bs, int nstate) break; default: - log_debug("state-change: unhandled neighbor state: %d", nstate); + if (bglobal.debug_peer_event) + zlog_debug("state-change: unhandled neighbor state: %d", + nstate); break; } } @@ -878,8 +1128,9 @@ void bs_state_handler(struct bfd_session *bs, int nstate) break; default: - log_debug("state-change: [%s] is in invalid state: %d", - bs_to_string(bs), nstate); + if (bglobal.debug_peer_event) + zlog_debug("state-change: [%s] is in invalid state: %d", + bs_to_string(bs), nstate); break; } } @@ -900,14 +1151,14 @@ void bs_echo_timer_handler(struct bfd_session *bs) * Section 3). * - Check that we are already at the up state. */ - if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO) == 0 - || BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH) + if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO) == 0 + || CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH) || bs->ses_state != PTM_BFD_UP) return; /* Remote peer asked to stop echo. */ if (bs->remote_timers.required_min_echo == 0) { - if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO_ACTIVE)) + if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO_ACTIVE)) ptm_bfd_echo_stop(bs); return; @@ -926,7 +1177,7 @@ void bs_echo_timer_handler(struct bfd_session *bs) else bs->echo_xmt_TO = bs->timers.required_min_echo; - if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO_ACTIVE) == 0 + if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO_ACTIVE) == 0 || old_timer != bs->echo_xmt_TO) ptm_bfd_echo_start(bs); } @@ -956,20 +1207,19 @@ void bs_final_handler(struct bfd_session *bs) } /* - * Calculate detection time based on new timers. + * Calculate transmission time based on new timers. * * Transmission calculation: - * We must respect the RequiredMinRxInterval from the remote - * system: if our desired transmission timer is more than the - * minimum receive rate, then we must lower it to at least the - * minimum receive interval. + * Unless specified by exceptions at the end of Section 6.8.7, the + * transmission time will be determined by the system with the + * slowest rate. * - * RFC 5880, Section 6.8.3. + * RFC 5880, Section 6.8.7. */ if (bs->timers.desired_min_tx > bs->remote_timers.required_min_rx) - bs->xmt_TO = bs->remote_timers.required_min_rx; - else bs->xmt_TO = bs->timers.desired_min_tx; + else + bs->xmt_TO = bs->remote_timers.required_min_rx; /* Apply new transmission timer immediately. */ ptm_bfd_start_xmt_timer(bs, false); @@ -985,12 +1235,12 @@ void bs_final_handler(struct bfd_session *bs) * TODO: support sending/counting more packets inside detection * timeout. */ - if (bs->remote_timers.required_min_rx > bs->timers.desired_min_tx) + if (bs->timers.required_min_rx > bs->remote_timers.desired_min_tx) bs->detect_TO = bs->remote_detect_mult - * bs->remote_timers.required_min_rx; + * bs->timers.required_min_rx; else bs->detect_TO = bs->remote_detect_mult - * bs->timers.desired_min_tx; + * bs->remote_timers.desired_min_tx; /* Apply new receive timer immediately. */ bfd_recvtimer_update(bs); @@ -1016,6 +1266,108 @@ void bs_set_slow_timers(struct bfd_session *bs) bs->xmt_TO = BFD_DEF_SLOWTX; } +void bfd_set_echo(struct bfd_session *bs, bool echo) +{ + if (echo) { + /* Check if echo mode is already active. */ + if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO)) + return; + + SET_FLAG(bs->flags, BFD_SESS_FLAG_ECHO); + + /* Activate/update echo receive timeout timer. */ + bs_echo_timer_handler(bs); + } else { + /* Check if echo mode is already disabled. */ + if (!CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO)) + return; + + UNSET_FLAG(bs->flags, BFD_SESS_FLAG_ECHO); + ptm_bfd_echo_stop(bs); + } +} + +void bfd_set_shutdown(struct bfd_session *bs, bool shutdown) +{ + bool is_shutdown; + + /* + * Special case: we are batching changes and the previous state was + * not shutdown. Instead of potentially disconnect a running peer, + * we'll get the current status to validate we were really down. + */ + if (bs->ses_state == PTM_BFD_UP) + is_shutdown = false; + else + is_shutdown = CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN); + + if (shutdown) { + /* Already shutdown. */ + if (is_shutdown) + return; + + SET_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN); + + /* Disable all events. */ + bfd_recvtimer_delete(bs); + bfd_echo_recvtimer_delete(bs); + bfd_xmttimer_delete(bs); + bfd_echo_xmttimer_delete(bs); + + /* Change and notify state change. */ + bs->ses_state = PTM_BFD_ADM_DOWN; + control_notify(bs, bs->ses_state); + + /* Don't try to send packets with a disabled session. */ + if (bs->sock != -1) + ptm_bfd_snd(bs, 0); + } else { + /* Already working. */ + if (!is_shutdown) + return; + + UNSET_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN); + + /* Change and notify state change. */ + bs->ses_state = PTM_BFD_DOWN; + control_notify(bs, bs->ses_state); + + /* Enable timers if non passive, otherwise stop them. */ + if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_PASSIVE)) { + bfd_recvtimer_delete(bs); + bfd_xmttimer_delete(bs); + } else { + bfd_recvtimer_update(bs); + bfd_xmttimer_update(bs, bs->xmt_TO); + } + } +} + +void bfd_set_passive_mode(struct bfd_session *bs, bool passive) +{ + if (passive) { + SET_FLAG(bs->flags, BFD_SESS_FLAG_PASSIVE); + + /* Session is already up and running, nothing to do now. */ + if (bs->ses_state != PTM_BFD_DOWN) + return; + + /* Lets disable the timers since we are now passive. */ + bfd_recvtimer_delete(bs); + bfd_xmttimer_delete(bs); + } else { + UNSET_FLAG(bs->flags, BFD_SESS_FLAG_PASSIVE); + + /* Session is already up and running, nothing to do now. */ + if (bs->ses_state != PTM_BFD_DOWN) + return; + + /* Session is down, let it attempt to start the connection. */ + bfd_xmttimer_update(bs, bs->xmt_TO); + bfd_recvtimer_update(bs); + } +} + /* * Helper functions. */ @@ -1028,13 +1380,13 @@ static const char *get_diag_str(int diag) return "N/A"; } -const char *satostr(struct sockaddr_any *sa) +const char *satostr(const struct sockaddr_any *sa) { #define INETSTR_BUFCOUNT 8 static char buf[INETSTR_BUFCOUNT][INET6_ADDRSTRLEN]; static int bufidx; - struct sockaddr_in *sin = &sa->sa_sin; - struct sockaddr_in6 *sin6 = &sa->sa_sin6; + const struct sockaddr_in *sin = &sa->sa_sin; + const struct sockaddr_in6 *sin6 = &sa->sa_sin6; bufidx += (bufidx + 1) % INETSTR_BUFCOUNT; buf[bufidx][0] = 0; @@ -1165,7 +1517,7 @@ const char *bs_to_string(const struct bfd_session *bs) static char buf[256]; char addr_buf[INET6_ADDRSTRLEN]; int pos; - bool is_mhop = BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH); + bool is_mhop = CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH); pos = snprintf(buf, sizeof(buf), "mhop:%s", is_mhop ? "yes" : "no"); pos += snprintf(buf + pos, sizeof(buf) - pos, " peer:%s", @@ -1191,23 +1543,10 @@ int bs_observer_add(struct bfd_session *bs) struct bfd_session_observer *bso; bso = XCALLOC(MTYPE_BFDD_SESSION_OBSERVER, sizeof(*bso)); - bso->bso_isaddress = false; bso->bso_bs = bs; - bso->bso_isinterface = !BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH); - if (bso->bso_isinterface) - strlcpy(bso->bso_entryname, bs->key.ifname, - sizeof(bso->bso_entryname)); - else - strlcpy(bso->bso_entryname, bs->key.vrfname, - sizeof(bso->bso_entryname)); - - /* Handle socket binding failures caused by missing local addresses. */ - if (bs->sock == -1) { - bso->bso_isaddress = true; - bso->bso_addr.family = bs->key.family; - memcpy(&bso->bso_addr.u.prefix, &bs->key.local, - sizeof(bs->key.local)); - } + bso->bso_addr.family = bs->key.family; + memcpy(&bso->bso_addr.u.prefix, &bs->key.local, + sizeof(bs->key.local)); TAILQ_INSERT_TAIL(&bglobal.bg_obslist, bso, bso_entry); @@ -1271,16 +1610,16 @@ void bs_to_bpc(struct bfd_session *bs, struct bfd_peer_cfg *bpc) static struct hash *bfd_id_hash; static struct hash *bfd_key_hash; -static unsigned int bfd_id_hash_do(void *p); -static unsigned int bfd_key_hash_do(void *p); +static unsigned int bfd_id_hash_do(const void *p); +static unsigned int bfd_key_hash_do(const void *p); static void _bfd_free(struct hash_bucket *hb, void *arg __attribute__((__unused__))); /* BFD hash for our discriminator. */ -static unsigned int bfd_id_hash_do(void *p) +static unsigned int bfd_id_hash_do(const void *p) { - struct bfd_session *bs = p; + const struct bfd_session *bs = p; return jhash_1word(bs->discrs.my_discr, 0); } @@ -1293,18 +1632,57 @@ static bool bfd_id_hash_cmp(const void *n1, const void *n2) } /* BFD hash for single hop. */ -static unsigned int bfd_key_hash_do(void *p) +static unsigned int bfd_key_hash_do(const void *p) { - struct bfd_session *bs = p; + const struct bfd_session *bs = p; + struct bfd_key key = bs->key; - return jhash(&bs->key, sizeof(bs->key), 0); + /* + * Local address and interface name are optional and + * can be filled any time after session creation. + * Hash key should not depend on these fields. + */ + memset(&key.local, 0, sizeof(key.local)); + memset(key.ifname, 0, sizeof(key.ifname)); + + return jhash(&key, sizeof(key), 0); } static bool bfd_key_hash_cmp(const void *n1, const void *n2) { const struct bfd_session *bs1 = n1, *bs2 = n2; - return memcmp(&bs1->key, &bs2->key, sizeof(bs1->key)) == 0; + if (bs1->key.family != bs2->key.family) + return false; + if (bs1->key.mhop != bs2->key.mhop) + return false; + if (memcmp(&bs1->key.peer, &bs2->key.peer, sizeof(bs1->key.peer))) + return false; + if (memcmp(bs1->key.vrfname, bs2->key.vrfname, + sizeof(bs1->key.vrfname))) + return false; + + /* + * Local address is optional and can be empty. + * If both addresses are not empty and different, + * then the keys are different. + */ + if (memcmp(&bs1->key.local, &zero_addr, sizeof(bs1->key.local)) + && memcmp(&bs2->key.local, &zero_addr, sizeof(bs2->key.local)) + && memcmp(&bs1->key.local, &bs2->key.local, sizeof(bs1->key.local))) + return false; + + /* + * Interface name is optional and can be empty. + * If both names are not empty and different, + * then the keys are different. + */ + if (bs1->key.ifname[0] && bs2->key.ifname[0] + && memcmp(bs1->key.ifname, bs2->key.ifname, + sizeof(bs1->key.ifname))) + return false; + + return true; } @@ -1324,31 +1702,11 @@ struct bfd_session *bfd_id_lookup(uint32_t id) struct bfd_session *bfd_key_lookup(struct bfd_key key) { - struct bfd_session bs, *bsp; + struct bfd_session bs; bs.key = key; - bsp = hash_lookup(bfd_key_hash, &bs); - /* Handle cases where local-address is optional. */ - if (bsp == NULL && bs.key.family == AF_INET) { - memset(&bs.key.local, 0, sizeof(bs.key.local)); - bsp = hash_lookup(bfd_key_hash, &bs); - } - - /* Handle cases where ifname is optional. */ - bs.key = key; - if (bsp == NULL && bs.key.ifname[0]) { - memset(bs.key.ifname, 0, sizeof(bs.key.ifname)); - bsp = hash_lookup(bfd_key_hash, &bs); - - /* Handle cases where local-address and ifname are optional. */ - if (bsp == NULL && bs.key.family == AF_INET) { - memset(&bs.key.local, 0, sizeof(bs.key.local)); - bsp = hash_lookup(bfd_key_hash, &bs); - } - } - - return bsp; + return hash_lookup(bfd_key_hash, &bs); } /* @@ -1372,16 +1730,11 @@ struct bfd_session *bfd_id_delete(uint32_t id) struct bfd_session *bfd_key_delete(struct bfd_key key) { - struct bfd_session bs, *bsp; + struct bfd_session bs; bs.key = key; - bsp = hash_lookup(bfd_key_hash, &bs); - if (bsp == NULL && key.ifname[0]) { - memset(bs.key.ifname, 0, sizeof(bs.key.ifname)); - bsp = hash_lookup(bfd_key_hash, &bs); - } - return hash_release(bfd_key_hash, bsp); + return hash_release(bfd_key_hash, &bs); } /* Iteration functions. */ @@ -1417,6 +1770,7 @@ void bfd_initialize(void) "BFD session discriminator hash"); bfd_key_hash = hash_create(bfd_key_hash_do, bfd_key_hash_cmp, "BFD session hash"); + TAILQ_INIT(&bplist); } static void _bfd_free(struct hash_bucket *hb, @@ -1429,6 +1783,8 @@ static void _bfd_free(struct hash_bucket *hb, void bfd_shutdown(void) { + struct bfd_profile *bp; + /* * Close and free all BFD sessions. * @@ -1442,4 +1798,360 @@ void bfd_shutdown(void) /* Now free the hashes themselves. */ hash_free(bfd_id_hash); hash_free(bfd_key_hash); + + /* Free all profile allocations. */ + while ((bp = TAILQ_FIRST(&bplist)) != NULL) + bfd_profile_free(bp); +} + +struct bfd_session_iterator { + int bsi_stop; + bool bsi_mhop; + const struct bfd_session *bsi_bs; +}; + +static int _bfd_session_next(struct hash_bucket *hb, void *arg) +{ + struct bfd_session_iterator *bsi = arg; + struct bfd_session *bs = hb->data; + + /* Previous entry signaled stop. */ + if (bsi->bsi_stop == 1) { + /* Match the single/multi hop sessions. */ + if (bs->key.mhop != bsi->bsi_mhop) + return HASHWALK_CONTINUE; + + bsi->bsi_bs = bs; + return HASHWALK_ABORT; + } + + /* We found the current item, stop in the next one. */ + if (bsi->bsi_bs == hb->data) { + bsi->bsi_stop = 1; + /* Set entry to NULL to signal end of list. */ + bsi->bsi_bs = NULL; + } else if (bsi->bsi_bs == NULL && bsi->bsi_mhop == bs->key.mhop) { + /* We want the first list item. */ + bsi->bsi_stop = 1; + bsi->bsi_bs = hb->data; + return HASHWALK_ABORT; + } + + return HASHWALK_CONTINUE; +} + +/* + * bfd_session_next: uses the current session to find the next. + * + * `bs` might point to NULL to get the first item of the data structure. + */ +const struct bfd_session *bfd_session_next(const struct bfd_session *bs, + bool mhop) +{ + struct bfd_session_iterator bsi; + + bsi.bsi_stop = 0; + bsi.bsi_bs = bs; + bsi.bsi_mhop = mhop; + hash_walk(bfd_key_hash, _bfd_session_next, &bsi); + if (bsi.bsi_stop == 0) + return NULL; + + return bsi.bsi_bs; +} + +static void _bfd_session_remove_manual(struct hash_bucket *hb, + void *arg __attribute__((__unused__))) +{ + struct bfd_session *bs = hb->data; + + /* Delete only manually configured sessions. */ + if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG) == 0) + return; + + bs->refcount--; + UNSET_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG); + + /* Don't delete sessions still in use. */ + if (bs->refcount != 0) + return; + + bfd_session_free(bs); +} + +/* + * bfd_sessions_remove_manual: remove all manually configured sessions. + * + * NOTE: this function doesn't remove automatically created sessions. + */ +void bfd_sessions_remove_manual(void) +{ + hash_iterate(bfd_key_hash, _bfd_session_remove_manual, NULL); +} + +void bfd_profiles_remove(void) +{ + struct bfd_profile *bp; + + while ((bp = TAILQ_FIRST(&bplist)) != NULL) + bfd_profile_free(bp); +} + +/* + * Profile related hash functions. + */ +static void _bfd_profile_update(struct hash_bucket *hb, void *arg) +{ + struct bfd_profile *bp = arg; + struct bfd_session *bs = hb->data; + + /* This session is not using the profile. */ + if (bs->profile_name == NULL || strcmp(bs->profile_name, bp->name) != 0) + return; + + bfd_profile_apply(bp->name, bs); +} + +void bfd_profile_update(struct bfd_profile *bp) +{ + hash_iterate(bfd_key_hash, _bfd_profile_update, bp); +} + +static void _bfd_profile_detach(struct hash_bucket *hb, void *arg) +{ + struct bfd_profile *bp = arg; + struct bfd_session *bs = hb->data; + + /* This session is not using the profile. */ + if (bs->profile_name == NULL || strcmp(bs->profile_name, bp->name) != 0) + return; + + bfd_profile_remove(bs); +} + +static void bfd_profile_detach(struct bfd_profile *bp) +{ + hash_iterate(bfd_key_hash, _bfd_profile_detach, bp); +} + +/* + * VRF related functions. + */ +static int bfd_vrf_new(struct vrf *vrf) +{ + if (bglobal.debug_zebra) + zlog_debug("VRF Created: %s(%u)", vrf->name, vrf->vrf_id); + + return 0; +} + +static int bfd_vrf_delete(struct vrf *vrf) +{ + if (bglobal.debug_zebra) + zlog_debug("VRF Deletion: %s(%u)", vrf->name, vrf->vrf_id); + + return 0; +} + +static int bfd_vrf_update(struct vrf *vrf) +{ + if (!vrf_is_enabled(vrf)) + return 0; + + if (bglobal.debug_zebra) + zlog_debug("VRF update: %s(%u)", vrf->name, vrf->vrf_id); + + /* a different name is given; update bfd list */ + bfdd_sessions_enable_vrf(vrf); + return 0; +} + +static int bfd_vrf_enable(struct vrf *vrf) +{ + struct bfd_vrf_global *bvrf; + + /* a different name */ + if (!vrf->info) { + bvrf = XCALLOC(MTYPE_BFDD_VRF, sizeof(struct bfd_vrf_global)); + bvrf->vrf = vrf; + vrf->info = (void *)bvrf; + } else + bvrf = vrf->info; + + if (bglobal.debug_zebra) + zlog_debug("VRF enable add %s id %u", vrf->name, vrf->vrf_id); + + if (vrf->vrf_id == VRF_DEFAULT || + vrf_get_backend() == VRF_BACKEND_NETNS) { + if (!bvrf->bg_shop) + bvrf->bg_shop = bp_udp_shop(vrf); + if (!bvrf->bg_mhop) + bvrf->bg_mhop = bp_udp_mhop(vrf); + if (!bvrf->bg_shop6) + bvrf->bg_shop6 = bp_udp6_shop(vrf); + if (!bvrf->bg_mhop6) + bvrf->bg_mhop6 = bp_udp6_mhop(vrf); + if (!bvrf->bg_echo) + bvrf->bg_echo = bp_echo_socket(vrf); + if (!bvrf->bg_echov6) + bvrf->bg_echov6 = bp_echov6_socket(vrf); + + /* Add descriptors to the event loop. */ + if (!bvrf->bg_ev[0]) + thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_shop, + &bvrf->bg_ev[0]); + if (!bvrf->bg_ev[1]) + thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_mhop, + &bvrf->bg_ev[1]); + if (!bvrf->bg_ev[2] && bvrf->bg_shop6 != -1) + thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_shop6, + &bvrf->bg_ev[2]); + if (!bvrf->bg_ev[3] && bvrf->bg_mhop6 != -1) + thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_mhop6, + &bvrf->bg_ev[3]); + if (!bvrf->bg_ev[4]) + thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_echo, + &bvrf->bg_ev[4]); + if (!bvrf->bg_ev[5] && bvrf->bg_echov6 != -1) + thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_echov6, + &bvrf->bg_ev[5]); + } + if (vrf->vrf_id != VRF_DEFAULT) { + bfdd_zclient_register(vrf->vrf_id); + bfdd_sessions_enable_vrf(vrf); + } + return 0; +} + +static int bfd_vrf_disable(struct vrf *vrf) +{ + struct bfd_vrf_global *bvrf; + + if (!vrf->info) + return 0; + bvrf = vrf->info; + + if (vrf->vrf_id != VRF_DEFAULT) { + bfdd_sessions_disable_vrf(vrf); + bfdd_zclient_unregister(vrf->vrf_id); + } + + if (bglobal.debug_zebra) + zlog_debug("VRF disable %s id %d", vrf->name, vrf->vrf_id); + + /* Disable read/write poll triggering. */ + THREAD_OFF(bvrf->bg_ev[0]); + THREAD_OFF(bvrf->bg_ev[1]); + THREAD_OFF(bvrf->bg_ev[2]); + THREAD_OFF(bvrf->bg_ev[3]); + THREAD_OFF(bvrf->bg_ev[4]); + THREAD_OFF(bvrf->bg_ev[5]); + + /* Close all descriptors. */ + socket_close(&bvrf->bg_echo); + socket_close(&bvrf->bg_shop); + socket_close(&bvrf->bg_mhop); + if (bvrf->bg_shop6 != -1) + socket_close(&bvrf->bg_shop6); + if (bvrf->bg_mhop6 != -1) + socket_close(&bvrf->bg_mhop6); + socket_close(&bvrf->bg_echo); + if (bvrf->bg_echov6 != -1) + socket_close(&bvrf->bg_echov6); + + /* free context */ + XFREE(MTYPE_BFDD_VRF, bvrf); + vrf->info = NULL; + + return 0; +} + +void bfd_vrf_init(void) +{ + vrf_init(bfd_vrf_new, bfd_vrf_enable, bfd_vrf_disable, + bfd_vrf_delete, bfd_vrf_update); +} + +void bfd_vrf_terminate(void) +{ + vrf_terminate(); +} + +struct bfd_vrf_global *bfd_vrf_look_by_session(struct bfd_session *bfd) +{ + struct vrf *vrf; + + if (!vrf_is_backend_netns()) { + vrf = vrf_lookup_by_id(VRF_DEFAULT); + if (vrf) + return (struct bfd_vrf_global *)vrf->info; + return NULL; + } + if (!bfd) + return NULL; + if (!bfd->vrf) + return NULL; + return bfd->vrf->info; +} + +void bfd_session_update_vrf_name(struct bfd_session *bs, struct vrf *vrf) +{ + if (!vrf || !bs) + return; + /* update key */ + hash_release(bfd_key_hash, bs); + /* + * HACK: Change the BFD VRF in the running configuration directly, + * bypassing the northbound layer. This is necessary to avoid deleting + * the BFD and readding it in the new VRF, which would have + * several implications. + */ + 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; + + /* build xpath */ + if (bs->key.mhop) { + inet_ntop(bs->key.family, &bs->key.local, addr_buf, sizeof(addr_buf)); + snprintf(xpath_srcaddr, sizeof(xpath_srcaddr), "[source-addr='%s']", + addr_buf); + } else + xpath_srcaddr[0] = 0; + inet_ntop(bs->key.family, &bs->key.peer, addr_buf, sizeof(addr_buf)); + slen = snprintf(xpath, sizeof(xpath), + "/frr-bfdd:bfdd/bfd/sessions/%s%s[dest-addr='%s']", + bs->key.mhop ? "multi-hop" : "single-hop", xpath_srcaddr, + addr_buf); + if (bs->key.ifname[0]) + slen += snprintf(xpath + slen, sizeof(xpath) - slen, + "[interface='%s']", bs->key.ifname); + else + slen += snprintf(xpath + slen, sizeof(xpath) - slen, + "[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++; + } + } + memset(bs->key.vrfname, 0, sizeof(bs->key.vrfname)); + strlcpy(bs->key.vrfname, vrf->name, sizeof(bs->key.vrfname)); + hash_get(bfd_key_hash, bs, hash_alloc_intern); +} + +unsigned long bfd_get_session_count(void) +{ + return bfd_key_hash->count; } diff --git a/bfdd/bfd.h b/bfdd/bfd.h index e08a1ff724..9ee1da7287 100644 --- a/bfdd/bfd.h +++ b/bfdd/bfd.h @@ -41,13 +41,9 @@ #define BFDD_JSON_CONV_OPTIONS (0) #endif -DECLARE_MGROUP(BFDD); -DECLARE_MTYPE(BFDD_TMP); -DECLARE_MTYPE(BFDD_CONFIG); -DECLARE_MTYPE(BFDD_LABEL); -DECLARE_MTYPE(BFDD_CONTROL); -DECLARE_MTYPE(BFDD_SESSION_OBSERVER); -DECLARE_MTYPE(BFDD_NOTIFICATION); +DECLARE_MGROUP(BFDD) +DECLARE_MTYPE(BFDD_CONTROL) +DECLARE_MTYPE(BFDD_NOTIFICATION) struct bfd_timers { uint32_t desired_min_tx; @@ -128,6 +124,12 @@ struct bfd_echo_pkt { flags |= (val & 0x3) << 6; \ } #define BFD_GETSTATE(flags) ((flags >> 6) & 0x3) +#define BFD_SETCBIT(flags, val) \ + { \ + if ((val)) \ + flags |= val; \ + } +#define BFD_GETCBIT(flags) (flags & BFD_FBIT) #define BFD_ECHO_VERSION 1 #define BFD_ECHO_PKT_LEN sizeof(struct bfd_echo_pkt) @@ -167,12 +169,10 @@ enum bfd_session_flags { */ BFD_SESS_FLAG_SHUTDOWN = 1 << 7, /* disable BGP peer function */ BFD_SESS_FLAG_CONFIG = 1 << 8, /* Session configured with bfd NB API */ + BFD_SESS_FLAG_CBIT = 1 << 9, /* CBIT is set */ + BFD_SESS_FLAG_PASSIVE = 1 << 10, /* Passive mode */ }; -#define BFD_SET_FLAG(field, flag) (field |= flag) -#define BFD_UNSET_FLAG(field, flag) (field &= ~flag) -#define BFD_CHECK_FLAG(field, flag) (field & flag) - /* BFD session hash keys */ struct bfd_key { uint16_t family; @@ -193,6 +193,38 @@ struct bfd_session_stats { uint64_t znotification; }; +/** + * BFD session profile to override default configurations. + */ +struct bfd_profile { + /** Profile name. */ + char name[64]; + + /** Session detection multiplier. */ + uint8_t detection_multiplier; + /** Desired transmission interval (in microseconds). */ + uint32_t min_tx; + /** Minimum required receive interval (in microseconds). */ + uint32_t min_rx; + /** Administrative state. */ + bool admin_shutdown; + /** Passive mode. */ + bool passive; + /** Minimum expected TTL value. */ + uint8_t minimum_ttl; + + /** Echo mode (only applies to single hop). */ + bool echo_mode; + /** Minimum required echo receive interval (in microseconds). */ + uint32_t min_echo_rx; + + /** Profile list entry. */ + TAILQ_ENTRY(bfd_profile) entry; +}; + +/** Profile list type. */ +TAILQ_HEAD(bfdproflist, bfd_profile); + /* bfd_session shortcut label forwarding. */ struct peer_label; @@ -209,6 +241,14 @@ struct bfd_session { uint8_t detect_mult; uint8_t remote_detect_mult; uint8_t mh_ttl; + uint8_t remote_cbit; + + /** BFD profile name. */ + char *profile_name; + /** BFD pre configured profile. */ + struct bfd_profile *profile; + /** BFD peer configuration (without profile). */ + struct bfd_profile peer_profile; /* Timers */ struct bfd_timers timers; @@ -248,11 +288,7 @@ struct bfd_session { struct bfd_timers remote_timers; uint64_t refcount; /* number of pointers referencing this. */ - - /* VTY context data. */ - QOBJ_FIELDS; }; -DECLARE_QOBJ_TYPE(bfd_session); struct peer_label { TAILQ_ENTRY(peer_label) pl_entry; @@ -274,12 +310,8 @@ struct bfd_state_str_list { struct bfd_session_observer { struct bfd_session *bso_bs; - bool bso_isinterface; - bool bso_isaddress; - union { - char bso_entryname[MAXNAMELEN]; - struct prefix bso_addr; - }; + char bso_entryname[MAXNAMELEN]; + struct prefix bso_addr; TAILQ_ENTRY(bfd_session_observer) bso_entry; }; @@ -301,7 +333,8 @@ TAILQ_HEAD(obslist, bfd_session_observer); #define BFD_DEFREQUIREDMINRX (300 * 1000) /* microseconds. */ #define BFD_DEF_REQ_MIN_ECHO (50 * 1000) /* microseconds. */ #define BFD_DEF_SLOWTX (1000 * 1000) /* microseconds. */ -#define BFD_DEF_MHOP_TTL 5 +/** Minimum multi hop TTL. */ +#define BFD_DEF_MHOP_TTL 254 #define BFD_PKT_LEN 24 /* Length of control packet */ #define BFD_TTL_VAL 255 #define BFD_RCV_TTL_VAL 1 @@ -369,7 +402,7 @@ TAILQ_HEAD(bcslist, bfd_control_socket); int control_init(const char *path); void control_shutdown(void); -int control_notify(struct bfd_session *bs); +int control_notify(struct bfd_session *bs, uint8_t notify_state); int control_notify_config(const char *op, struct bfd_session *bs); int control_accept(struct thread *t); @@ -379,15 +412,19 @@ int control_accept(struct thread *t); * * Daemon specific code. */ -struct bfd_global { +struct bfd_vrf_global { int bg_shop; int bg_mhop; int bg_shop6; int bg_mhop6; int bg_echo; int bg_echov6; + struct vrf *vrf; + struct thread *bg_ev[6]; +}; +struct bfd_global { int bg_csock; struct thread *bg_csockev; struct bcslist bg_bcslist; @@ -395,10 +432,37 @@ struct bfd_global { struct pllist bg_pllist; struct obslist bg_obslist; + + struct zebra_privs_t bfdd_privs; + + /** + * Daemon is exit()ing? Use this to avoid actions that expect a + * running system or to avoid unnecessary operations when quitting. + */ + bool bg_shutdown; + + /* Debug options. */ + /* Show all peer state changes events. */ + bool debug_peer_event; + /* + * Show zebra message exchanges: + * - Interface add/delete. + * - Local address add/delete. + * - VRF add/delete. + */ + bool debug_zebra; + /* + * Show network level debug information: + * - Echo packets without session. + * - Unavailable peer sessions. + * - Network system call failures. + */ + bool debug_network; }; + extern struct bfd_global bglobal; -extern struct bfd_diag_str_list diag_list[]; -extern struct bfd_state_str_list state_list[]; +extern const struct bfd_diag_str_list diag_list[]; +extern const struct bfd_state_str_list state_list[]; void socket_close(int *s); @@ -425,27 +489,14 @@ void pl_free(struct peer_label *pl); /* - * log.c - * - * Contains code that does the logging procedures. Might implement multiple - * backends (e.g. zebra log, syslog or other logging lib). + * logging - alias to zebra log */ -enum blog_level { - /* level vs syslog equivalent */ - BLOG_DEBUG = 0, /* LOG_DEBUG */ - BLOG_INFO = 1, /* LOG_INFO */ - BLOG_WARNING = 2, /* LOG_WARNING */ - BLOG_ERROR = 3, /* LOG_ERR */ - BLOG_FATAL = 4, /* LOG_CRIT */ -}; - -void log_init(int foreground, enum blog_level level, - struct frr_daemon_info *fdi); -void log_info(const char *fmt, ...); -void log_debug(const char *fmt, ...); -void log_warning(const char *fmt, ...); -void log_error(const char *fmt, ...); -void log_fatal(const char *fmt, ...); +#define zlog_fatal(msg, ...) \ + do { \ + zlog_err(msg, ##__VA_ARGS__); \ + assert(!msg); \ + abort(); \ + } while (0) /* @@ -459,14 +510,14 @@ int bp_set_tosv6(int sd, uint8_t value); int bp_set_tos(int sd, uint8_t value); int bp_bind_dev(int sd, const char *dev); -int bp_udp_shop(void); -int bp_udp_mhop(void); -int bp_udp6_shop(void); -int bp_udp6_mhop(void); +int bp_udp_shop(const struct vrf *vrf); +int bp_udp_mhop(const struct vrf *vrf); +int bp_udp6_shop(const struct vrf *vrf); +int bp_udp6_mhop(const struct vrf *vrf); int bp_peer_socket(const struct bfd_session *bs); int bp_peer_socketv6(const struct bfd_session *bs); -int bp_echo_socket(void); -int bp_echov6_socket(void); +int bp_echo_socket(const struct vrf *vrf); +int bp_echov6_socket(const struct vrf *vrf); void ptm_bfd_snd(struct bfd_session *bfd, int fbit); void ptm_bfd_echo_snd(struct bfd_session *bfd); @@ -505,9 +556,9 @@ void bfd_echo_xmttimer_assign(struct bfd_session *bs, bfd_ev_cb cb); int bfd_session_enable(struct bfd_session *bs); void bfd_session_disable(struct bfd_session *bs); struct bfd_session *ptm_bfd_sess_new(struct bfd_peer_cfg *bpc); -int ptm_bfd_ses_del(struct bfd_peer_cfg *bpc); -void ptm_bfd_ses_dn(struct bfd_session *bfd, uint8_t diag); -void ptm_bfd_ses_up(struct bfd_session *bfd); +int ptm_bfd_sess_del(struct bfd_peer_cfg *bpc); +void ptm_bfd_sess_dn(struct bfd_session *bfd, uint8_t diag); +void ptm_bfd_sess_up(struct bfd_session *bfd); void ptm_bfd_echo_stop(struct bfd_session *bfd); void ptm_bfd_echo_start(struct bfd_session *bfd); void ptm_bfd_xmt_TO(struct bfd_session *bfd, int fbit); @@ -525,7 +576,7 @@ void bs_state_handler(struct bfd_session *bs, int nstate); void bs_echo_timer_handler(struct bfd_session *bs); void bs_final_handler(struct bfd_session *bs); void bs_set_slow_timers(struct bfd_session *bs); -const char *satostr(struct sockaddr_any *sa); +const char *satostr(const struct sockaddr_any *sa); const char *diag2str(uint8_t diag); int strtosa(const char *addr, struct sockaddr_any *sa); void integer2timestr(uint64_t time, char *buf, size_t buflen); @@ -536,9 +587,56 @@ void bs_observer_del(struct bfd_session_observer *bso); void bs_to_bpc(struct bfd_session *bs, struct bfd_peer_cfg *bpc); +void gen_bfd_key(struct bfd_key *key, struct sockaddr_any *peer, + struct sockaddr_any *local, bool mhop, const char *ifname, + const char *vrfname); +struct bfd_session *bfd_session_new(void); +struct bfd_session *bs_registrate(struct bfd_session *bs); +void bfd_session_free(struct bfd_session *bs); +const struct bfd_session *bfd_session_next(const struct bfd_session *bs, + bool mhop); +void bfd_sessions_remove_manual(void); +void bfd_profiles_remove(void); + +/** + * Set the BFD session echo state. + * + * \param bs the BFD session. + * \param echo the echo operational state. + */ +void bfd_set_echo(struct bfd_session *bs, bool echo); + +/** + * Set the BFD session functional state. + * + * \param bs the BFD session. + * \param shutdown the operational value. + */ +void bfd_set_shutdown(struct bfd_session *bs, bool shutdown); + +/** + * Set the BFD session passive mode. + * + * \param bs the BFD session. + * \param passive the passive mode. + */ +void bfd_set_passive_mode(struct bfd_session *bs, bool passive); + +/** + * Picks the BFD session configuration from the appropriated source: + * if using the default peer configuration prefer profile (if it exists), + * otherwise use session. + * + * \param bs the BFD session. + */ +void bfd_session_apply(struct bfd_session *bs); + /* BFD hash data structures interface */ void bfd_initialize(void); void bfd_shutdown(void); +void bfd_vrf_init(void); +void bfd_vrf_terminate(void); +struct bfd_vrf_global *bfd_vrf_look_by_session(struct bfd_session *bfd); struct bfd_session *bfd_id_lookup(uint32_t id); struct bfd_session *bfd_key_lookup(struct bfd_key key); @@ -552,6 +650,8 @@ typedef void (*hash_iter_func)(struct hash_bucket *hb, void *arg); void bfd_id_iterate(hash_iter_func hif, void *arg); void bfd_key_iterate(hash_iter_func hif, void *arg); +unsigned long bfd_get_session_count(void); + /* Export callback functions for `event.c`. */ extern struct thread_master *master; @@ -562,6 +662,57 @@ int bfd_echo_xmt_cb(struct thread *t); extern struct in6_addr zero_addr; +/** + * Creates a new profile entry and insert into the global list. + * + * \param name the BFD profile name. + * + * \returns `NULL` if it already exists otherwise the new entry. + */ +struct bfd_profile *bfd_profile_new(const char *name); + +/** + * Search for configured BFD profiles (profile name is case insensitive). + * + * \param name the BFD profile name. + * + * \returns `NULL` if it doesn't exist otherwise the entry. + */ +struct bfd_profile *bfd_profile_lookup(const char *name); + +/** + * Removes profile from list and free memory. + * + * \param bp the BFD profile. + */ +void bfd_profile_free(struct bfd_profile *bp); + +/** + * Apply a profile configuration to an existing BFD session. The non default + * values will not be overriden. + * + * NOTE: if the profile doesn't exist yet, then the profile will be applied + * once it begins to exist. + * + * \param profile_name the BFD profile name. + * \param bs the BFD session. + */ +void bfd_profile_apply(const char *profname, struct bfd_session *bs); + +/** + * Remove any applied profile from session and revert the session + * configuration. + * + * \param bs the BFD session. + */ +void bfd_profile_remove(struct bfd_session *bs); + +/** + * Apply new profile values to sessions using it. + * + * \param[in] bp the BFD profile that got updated. + */ +void bfd_profile_update(struct bfd_profile *bp); /* * bfdd_vty.c @@ -571,12 +722,25 @@ extern struct in6_addr zero_addr; void bfdd_vty_init(void); +/* + * bfdd_cli.c + * + * BFD daemon CLI implementation. + */ +void bfdd_cli_init(void); + + /* * ptm_adapter.c */ void bfdd_zclient_init(struct zebra_privs_t *bfdd_priv); void bfdd_zclient_stop(void); +void bfdd_zclient_unregister(vrf_id_t vrf_id); +void bfdd_zclient_register(vrf_id_t vrf_id); +void bfdd_sessions_enable_vrf(struct vrf *vrf); +void bfdd_sessions_disable_vrf(struct vrf *vrf); +void bfd_session_update_vrf_name(struct bfd_session *bs, struct vrf *vrf); -int ptm_bfd_notify(struct bfd_session *bs); +int ptm_bfd_notify(struct bfd_session *bs, uint8_t notify_state); #endif /* _BFD_H_ */ diff --git a/bfdd/bfd_packet.c b/bfdd/bfd_packet.c index 93677ec85a..12bb52cf67 100644 --- a/bfdd/bfd_packet.c +++ b/bfdd/bfd_packet.c @@ -37,15 +37,14 @@ #include "bfd.h" - /* * Prototypes */ -static int ptm_bfd_process_echo_pkt(int s); +static int ptm_bfd_process_echo_pkt(struct bfd_vrf_global *bvrf, int s); int _ptm_bfd_send(struct bfd_session *bs, uint16_t *port, const void *data, size_t datalen); -static void bfd_sd_reschedule(int sd); +static void bfd_sd_reschedule(struct bfd_vrf_global *bvrf, int sd); ssize_t bfd_recv_ipv4(int sd, uint8_t *msgbuf, size_t msgbuflen, uint8_t *ttl, ifindex_t *ifindex, struct sockaddr_any *local, struct sockaddr_any *peer); @@ -54,7 +53,8 @@ ssize_t bfd_recv_ipv6(int sd, uint8_t *msgbuf, size_t msgbuflen, uint8_t *ttl, struct sockaddr_any *peer); int bp_udp_send(int sd, uint8_t ttl, uint8_t *data, size_t datalen, struct sockaddr *to, socklen_t tolen); -int bp_bfd_echo_in(int sd, uint8_t *ttl, uint32_t *my_discr); +int bp_bfd_echo_in(struct bfd_vrf_global *bvrf, int sd, + uint8_t *ttl, uint32_t *my_discr); /* socket related prototypes */ static void bp_set_ipopts(int sd); @@ -76,16 +76,16 @@ int _ptm_bfd_send(struct bfd_session *bs, uint16_t *port, const void *data, ssize_t rv; int sd = -1; - if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_IPV6)) { + if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_IPV6)) { memset(&sin6, 0, sizeof(sin6)); sin6.sin6_family = AF_INET6; memcpy(&sin6.sin6_addr, &bs->key.peer, sizeof(sin6.sin6_addr)); - if (IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr)) + if (bs->ifp && IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr)) sin6.sin6_scope_id = bs->ifp->ifindex; sin6.sin6_port = (port) ? *port - : (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) + : (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) ? htons(BFD_DEF_MHOP_DEST_PORT) : htons(BFD_DEFDESTPORT); @@ -98,7 +98,7 @@ int _ptm_bfd_send(struct bfd_session *bs, uint16_t *port, const void *data, memcpy(&sin.sin_addr, &bs->key.peer, sizeof(sin.sin_addr)); sin.sin_port = (port) ? *port - : (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) + : (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) ? htons(BFD_DEF_MHOP_DEST_PORT) : htons(BFD_DEFDESTPORT); @@ -112,11 +112,16 @@ int _ptm_bfd_send(struct bfd_session *bs, uint16_t *port, const void *data, #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */ rv = sendto(sd, data, datalen, 0, sa, slen); if (rv <= 0) { - log_debug("packet-send: send failure: %s", strerror(errno)); + if (bglobal.debug_network) + zlog_debug("packet-send: send failure: %s", + strerror(errno)); return -1; } - if (rv < (ssize_t)datalen) - log_debug("packet-send: send partial", strerror(errno)); + if (rv < (ssize_t)datalen) { + if (bglobal.debug_network) + zlog_debug("packet-send: send partial: %s", + strerror(errno)); + } return 0; } @@ -129,17 +134,22 @@ void ptm_bfd_echo_snd(struct bfd_session *bfd) struct bfd_echo_pkt bep; struct sockaddr_in sin; struct sockaddr_in6 sin6; + struct bfd_vrf_global *bvrf = bfd_vrf_look_by_session(bfd); - if (!BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE)) - BFD_SET_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE); + if (!bvrf) + return; + if (!CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE)) + SET_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE); memset(&bep, 0, sizeof(bep)); bep.ver = BFD_ECHO_VERSION; bep.len = BFD_ECHO_PKT_LEN; bep.my_discr = htonl(bfd->discrs.my_discr); - if (BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_IPV6)) { - sd = bglobal.bg_echov6; + if (CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_IPV6)) { + if (bvrf->bg_echov6 == -1) + return; + sd = bvrf->bg_echov6; memset(&sin6, 0, sizeof(sin6)); sin6.sin6_family = AF_INET6; memcpy(&sin6.sin6_addr, &bfd->key.peer, sizeof(sin6.sin6_addr)); @@ -154,8 +164,8 @@ void ptm_bfd_echo_snd(struct bfd_session *bfd) sa = (struct sockaddr *)&sin6; salen = sizeof(sin6); } else { - sd = bglobal.bg_echo; - memset(&sin6, 0, sizeof(sin6)); + sd = bvrf->bg_echo; + memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; memcpy(&sin.sin_addr, &bfd->key.peer, sizeof(sin.sin_addr)); sin.sin_port = htons(BFD_DEF_ECHO_PORT); @@ -174,26 +184,29 @@ void ptm_bfd_echo_snd(struct bfd_session *bfd) bfd->stats.tx_echo_pkt++; } -static int ptm_bfd_process_echo_pkt(int s) +static int ptm_bfd_process_echo_pkt(struct bfd_vrf_global *bvrf, int s) { struct bfd_session *bfd; uint32_t my_discr = 0; uint8_t ttl = 0; /* Receive and parse echo packet. */ - if (bp_bfd_echo_in(s, &ttl, &my_discr) == -1) + if (bp_bfd_echo_in(bvrf, s, &ttl, &my_discr) == -1) return 0; /* Your discriminator not zero - use it to find session */ bfd = bfd_id_lookup(my_discr); if (bfd == NULL) { - log_debug("echo-packet: no matching session (id:%u)", my_discr); + if (bglobal.debug_network) + zlog_debug("echo-packet: no matching session (id:%u)", + my_discr); return -1; } - if (!BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE)) { - log_debug("echo-packet: echo disabled [%s] (id:%u)", - bs_to_string(bfd), my_discr); + if (!CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE)) { + if (bglobal.debug_network) + zlog_debug("echo-packet: echo disabled [%s] (id:%u)", + bs_to_string(bfd), my_discr); return -1; } @@ -211,13 +224,17 @@ static int ptm_bfd_process_echo_pkt(int s) void ptm_bfd_snd(struct bfd_session *bfd, int fbit) { - struct bfd_pkt cp; + struct bfd_pkt cp = {}; /* Set fields according to section 6.5.7 */ cp.diag = bfd->local_diag; BFD_SETVER(cp.diag, BFD_VERSION); cp.flags = 0; BFD_SETSTATE(cp.flags, bfd->ses_state); + + if (CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_CBIT)) + BFD_SETCBIT(cp.flags, BFD_CBIT); + BFD_SETDEMANDBIT(cp.flags, BFD_DEF_DEMAND); /* @@ -284,8 +301,7 @@ ssize_t bfd_recv_ipv4(int sd, uint8_t *msgbuf, size_t msgbuflen, uint8_t *ttl, mlen = recvmsg(sd, &msghdr, MSG_DONTWAIT); if (mlen == -1) { if (errno != EAGAIN) - log_error("ipv4-recv: recv failed: %s", - strerror(errno)); + zlog_err("ipv4-recv: recv failed: %s", strerror(errno)); return -1; } @@ -306,7 +322,9 @@ ssize_t bfd_recv_ipv4(int sd, uint8_t *msgbuf, size_t msgbuflen, uint8_t *ttl, memcpy(&ttlval, CMSG_DATA(cm), sizeof(ttlval)); if (ttlval > 255) { - log_debug("ipv4-recv: invalid TTL: %u", ttlval); + if (bglobal.debug_network) + zlog_debug("ipv4-recv: invalid TTL: %u", + ttlval); return -1; } *ttl = ttlval; @@ -395,8 +413,7 @@ ssize_t bfd_recv_ipv6(int sd, uint8_t *msgbuf, size_t msgbuflen, uint8_t *ttl, mlen = recvmsg(sd, &msghdr6, MSG_DONTWAIT); if (mlen == -1) { if (errno != EAGAIN) - log_error("ipv6-recv: recv failed: %s", - strerror(errno)); + zlog_err("ipv6-recv: recv failed: %s", strerror(errno)); return -1; } @@ -413,7 +430,9 @@ ssize_t bfd_recv_ipv6(int sd, uint8_t *msgbuf, size_t msgbuflen, uint8_t *ttl, if (cm->cmsg_type == IPV6_HOPLIMIT) { memcpy(&ttlval, CMSG_DATA(cm), sizeof(ttlval)); if (ttlval > 255) { - log_debug("ipv6-recv: invalid TTL: %u", ttlval); + if (bglobal.debug_network) + zlog_debug("ipv6-recv: invalid TTL: %u", + ttlval); return -1; } @@ -428,45 +447,47 @@ ssize_t bfd_recv_ipv6(int sd, uint8_t *msgbuf, size_t msgbuflen, uint8_t *ttl, #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */ *ifindex = pi6->ipi6_ifindex; + + /* Set scope ID for link local addresses. */ + if (IN6_IS_ADDR_LINKLOCAL( + &peer->sa_sin6.sin6_addr)) + peer->sa_sin6.sin6_scope_id = *ifindex; + if (IN6_IS_ADDR_LINKLOCAL( + &local->sa_sin6.sin6_addr)) + local->sa_sin6.sin6_scope_id = *ifindex; } } } - /* Set scope ID for link local addresses. */ - if (IN6_IS_ADDR_LINKLOCAL(&peer->sa_sin6.sin6_addr)) - peer->sa_sin6.sin6_scope_id = *ifindex; - if (IN6_IS_ADDR_LINKLOCAL(&local->sa_sin6.sin6_addr)) - local->sa_sin6.sin6_scope_id = *ifindex; - return mlen; } -static void bfd_sd_reschedule(int sd) +static void bfd_sd_reschedule(struct bfd_vrf_global *bvrf, int sd) { - if (sd == bglobal.bg_shop) { - THREAD_OFF(bglobal.bg_ev[0]); - thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_shop, - &bglobal.bg_ev[0]); - } else if (sd == bglobal.bg_mhop) { - THREAD_OFF(bglobal.bg_ev[1]); - thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_mhop, - &bglobal.bg_ev[1]); - } else if (sd == bglobal.bg_shop6) { - THREAD_OFF(bglobal.bg_ev[2]); - thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_shop6, - &bglobal.bg_ev[2]); - } else if (sd == bglobal.bg_mhop6) { - THREAD_OFF(bglobal.bg_ev[3]); - thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_mhop6, - &bglobal.bg_ev[3]); - } else if (sd == bglobal.bg_echo) { - THREAD_OFF(bglobal.bg_ev[4]); - thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_echo, - &bglobal.bg_ev[4]); - } else if (sd == bglobal.bg_echov6) { - THREAD_OFF(bglobal.bg_ev[5]); - thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_echov6, - &bglobal.bg_ev[5]); + if (sd == bvrf->bg_shop) { + THREAD_OFF(bvrf->bg_ev[0]); + thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_shop, + &bvrf->bg_ev[0]); + } else if (sd == bvrf->bg_mhop) { + THREAD_OFF(bvrf->bg_ev[1]); + thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_mhop, + &bvrf->bg_ev[1]); + } else if (sd == bvrf->bg_shop6) { + THREAD_OFF(bvrf->bg_ev[2]); + thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_shop6, + &bvrf->bg_ev[2]); + } else if (sd == bvrf->bg_mhop6) { + THREAD_OFF(bvrf->bg_ev[3]); + thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_mhop6, + &bvrf->bg_ev[3]); + } else if (sd == bvrf->bg_echo) { + THREAD_OFF(bvrf->bg_ev[4]); + thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_echo, + &bvrf->bg_ev[4]); + } else if (sd == bvrf->bg_echov6) { + THREAD_OFF(bvrf->bg_ev[5]); + thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_echov6, + &bvrf->bg_ev[5]); } } @@ -477,6 +498,10 @@ static void cp_debug(bool mhop, struct sockaddr_any *peer, char buf[512], peerstr[128], localstr[128], portstr[64], vrfstr[64]; va_list vl; + /* Don't to any processing if debug is disabled. */ + if (bglobal.debug_network == false) + return; + if (peer->sa_sin.sin_family) snprintf(peerstr, sizeof(peerstr), " peer:%s", satostr(peer)); else @@ -502,8 +527,8 @@ static void cp_debug(bool mhop, struct sockaddr_any *peer, vsnprintf(buf, sizeof(buf), fmt, vl); va_end(vl); - log_debug("control-packet: %s [mhop:%s%s%s%s%s]", buf, - mhop ? "yes" : "no", peerstr, localstr, portstr, vrfstr); + zlog_debug("control-packet: %s [mhop:%s%s%s%s%s]", buf, + mhop ? "yes" : "no", peerstr, localstr, portstr, vrfstr); } int bfd_recv_cb(struct thread *t) @@ -514,17 +539,21 @@ int bfd_recv_cb(struct thread *t) bool is_mhop; ssize_t mlen = 0; uint8_t ttl = 0; - vrf_id_t vrfid = VRF_DEFAULT; + vrf_id_t vrfid; ifindex_t ifindex = IFINDEX_INTERNAL; struct sockaddr_any local, peer; uint8_t msgbuf[1516]; + struct interface *ifp = NULL; + struct bfd_vrf_global *bvrf = THREAD_ARG(t); + + vrfid = bvrf->vrf->vrf_id; /* Schedule next read. */ - bfd_sd_reschedule(sd); + bfd_sd_reschedule(bvrf, sd); /* Handle echo packets. */ - if (sd == bglobal.bg_echo || sd == bglobal.bg_echov6) { - ptm_bfd_process_echo_pkt(sd); + if (sd == bvrf->bg_echo || sd == bvrf->bg_echov6) { + ptm_bfd_process_echo_pkt(bvrf, sd); return 0; } @@ -534,16 +563,25 @@ int bfd_recv_cb(struct thread *t) /* Handle control packets. */ is_mhop = false; - if (sd == bglobal.bg_shop || sd == bglobal.bg_mhop) { - is_mhop = sd == bglobal.bg_mhop; + if (sd == bvrf->bg_shop || sd == bvrf->bg_mhop) { + is_mhop = sd == bvrf->bg_mhop; mlen = bfd_recv_ipv4(sd, msgbuf, sizeof(msgbuf), &ttl, &ifindex, &local, &peer); - } else if (sd == bglobal.bg_shop6 || sd == bglobal.bg_mhop6) { - is_mhop = sd == bglobal.bg_mhop6; + } else if (sd == bvrf->bg_shop6 || sd == bvrf->bg_mhop6) { + is_mhop = sd == bvrf->bg_mhop6; mlen = bfd_recv_ipv6(sd, msgbuf, sizeof(msgbuf), &ttl, &ifindex, &local, &peer); } + /* update vrf-id because when in vrf-lite mode, + * the socket is on default namespace + */ + if (ifindex) { + ifp = if_lookup_by_index(ifindex, vrfid); + if (ifp) + vrfid = ifp->vrf_id; + } + /* Implement RFC 5880 6.8.6 */ if (mlen < BFD_PKT_LEN) { cp_debug(is_mhop, &peer, &local, ifindex, vrfid, @@ -551,7 +589,7 @@ int bfd_recv_cb(struct thread *t) return 0; } - /* Validate packet TTL. */ + /* Validate single hop packet TTL. */ if ((!is_mhop) && (ttl != BFD_TTL_VAL)) { cp_debug(is_mhop, &peer, &local, ifindex, vrfid, "invalid TTL: %d expected %d", ttl, BFD_TTL_VAL); @@ -604,10 +642,10 @@ int bfd_recv_cb(struct thread *t) * Single hop: set local address that received the packet. */ if (is_mhop) { - if ((BFD_TTL_VAL - bfd->mh_ttl) > BFD_TTL_VAL) { + if (ttl < bfd->mh_ttl) { cp_debug(is_mhop, &peer, &local, ifindex, vrfid, "exceeded max hop count (expected %d, got %d)", - bfd->mh_ttl, BFD_TTL_VAL); + bfd->mh_ttl, ttl); return 0; } } else if (bfd->local_address.sa_sin.sin_family == AF_UNSPEC) { @@ -640,6 +678,11 @@ int bfd_recv_cb(struct thread *t) ntohl(cp->timers.required_min_echo); bfd->remote_detect_mult = cp->detect_mult; + if (BFD_GETCBIT(cp->flags)) + bfd->remote_cbit = 1; + else + bfd->remote_cbit = 0; + /* State switch from section 6.2. */ bs_state_handler(bfd, BFD_GETSTATE(cp->flags)); @@ -682,7 +725,8 @@ int bfd_recv_cb(struct thread *t) * * Returns -1 on error or loopback or 0 on success. */ -int bp_bfd_echo_in(int sd, uint8_t *ttl, uint32_t *my_discr) +int bp_bfd_echo_in(struct bfd_vrf_global *bvrf, int sd, + uint8_t *ttl, uint32_t *my_discr) { struct bfd_echo_pkt *bep; ssize_t rlen; @@ -691,7 +735,7 @@ int bp_bfd_echo_in(int sd, uint8_t *ttl, uint32_t *my_discr) vrf_id_t vrfid = VRF_DEFAULT; uint8_t msgbuf[1516]; - if (sd == bglobal.bg_echo) + if (sd == bvrf->bg_echo) rlen = bfd_recv_ipv4(sd, msgbuf, sizeof(msgbuf), ttl, &ifindex, &local, &peer); else @@ -709,7 +753,7 @@ int bp_bfd_echo_in(int sd, uint8_t *ttl, uint32_t *my_discr) if (*ttl == BFD_TTL_VAL) { bp_udp_send(sd, *ttl - 1, msgbuf, rlen, (struct sockaddr *)&peer, - (sd == bglobal.bg_echo) ? sizeof(peer.sa_sin) + (sd == bvrf->bg_echo) ? sizeof(peer.sa_sin) : sizeof(peer.sa_sin6)); return -1; } @@ -761,7 +805,7 @@ int bp_udp_send(int sd, uint8_t ttl, uint8_t *data, size_t datalen, cmsg->cmsg_level = IPPROTO_IPV6; cmsg->cmsg_type = IPV6_HOPLIMIT; } else { -#if BFD_LINUX +#ifdef BFD_LINUX cmsg->cmsg_level = IPPROTO_IP; cmsg->cmsg_type = IP_TTL; #else @@ -778,11 +822,14 @@ int bp_udp_send(int sd, uint8_t ttl, uint8_t *data, size_t datalen, /* Send echo back. */ wlen = sendmsg(sd, &msg, 0); if (wlen <= 0) { - log_debug("udp-send: loopback failure: (%d) %s", errno, strerror(errno)); + if (bglobal.debug_network) + zlog_debug("udp-send: loopback failure: (%d) %s", errno, + strerror(errno)); return -1; } else if (wlen < (ssize_t)datalen) { - log_debug("udp-send: partial send: %ld expected %ld", wlen, - datalen); + if (bglobal.debug_network) + zlog_debug("udp-send: partial send: %zd expected %zu", + wlen, datalen); return -1; } @@ -803,8 +850,8 @@ int bp_set_ttl(int sd, uint8_t value) int ttl = value; if (setsockopt(sd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)) == -1) { - log_warning("set-ttl: setsockopt(IP_TTL, %d): %s", value, - strerror(errno)); + zlog_warn("set-ttl: setsockopt(IP_TTL, %d): %s", value, + strerror(errno)); return -1; } @@ -816,8 +863,8 @@ int bp_set_tos(int sd, uint8_t value) int tos = value; if (setsockopt(sd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) == -1) { - log_warning("set-tos: setsockopt(IP_TOS, %d): %s", value, - strerror(errno)); + zlog_warn("set-tos: setsockopt(IP_TOS, %d): %s", value, + strerror(errno)); return -1; } @@ -829,12 +876,12 @@ static void bp_set_ipopts(int sd) int rcvttl = BFD_RCV_TTL_VAL; if (bp_set_ttl(sd, BFD_TTL_VAL) != 0) - log_fatal("set-ipopts: TTL configuration failed"); + zlog_fatal("set-ipopts: TTL configuration failed"); if (setsockopt(sd, IPPROTO_IP, IP_RECVTTL, &rcvttl, sizeof(rcvttl)) == -1) - log_fatal("set-ipopts: setsockopt(IP_RECVTTL, %d): %s", rcvttl, - strerror(errno)); + zlog_fatal("set-ipopts: setsockopt(IP_RECVTTL, %d): %s", rcvttl, + strerror(errno)); #ifdef BFD_LINUX int pktinfo = BFD_PKT_INFO_VAL; @@ -842,21 +889,21 @@ static void bp_set_ipopts(int sd) /* Figure out address and interface to do the peer matching. */ if (setsockopt(sd, IPPROTO_IP, IP_PKTINFO, &pktinfo, sizeof(pktinfo)) == -1) - log_fatal("set-ipopts: setsockopt(IP_PKTINFO, %d): %s", pktinfo, - strerror(errno)); + zlog_fatal("set-ipopts: setsockopt(IP_PKTINFO, %d): %s", + pktinfo, strerror(errno)); #endif /* BFD_LINUX */ #ifdef BFD_BSD int yes = 1; /* Find out our address for peer matching. */ if (setsockopt(sd, IPPROTO_IP, IP_RECVDSTADDR, &yes, sizeof(yes)) == -1) - log_fatal("set-ipopts: setsockopt(IP_RECVDSTADDR, %d): %s", yes, - strerror(errno)); + zlog_fatal("set-ipopts: setsockopt(IP_RECVDSTADDR, %d): %s", + yes, strerror(errno)); /* Find out interface where the packet came in. */ if (setsockopt_ifindex(AF_INET, sd, yes) == -1) - log_fatal("set-ipopts: setsockopt_ipv4_ifindex(%d): %s", yes, - strerror(errno)); + zlog_fatal("set-ipopts: setsockopt_ipv4_ifindex(%d): %s", yes, + strerror(errno)); #endif /* BFD_BSD */ } @@ -869,30 +916,35 @@ static void bp_bind_ip(int sd, uint16_t port) sin.sin_addr.s_addr = htonl(INADDR_ANY); sin.sin_port = htons(port); if (bind(sd, (struct sockaddr *)&sin, sizeof(sin)) == -1) - log_fatal("bind-ip: bind: %s", strerror(errno)); + zlog_fatal("bind-ip: bind: %s", strerror(errno)); } -int bp_udp_shop(void) +int bp_udp_shop(const struct vrf *vrf) { int sd; - sd = socket(AF_INET, SOCK_DGRAM, PF_UNSPEC); + frr_with_privs(&bglobal.bfdd_privs) { + sd = vrf_socket(AF_INET, SOCK_DGRAM, PF_UNSPEC, vrf->vrf_id, + vrf->name); + } if (sd == -1) - log_fatal("udp-shop: socket: %s", strerror(errno)); + zlog_fatal("udp-shop: socket: %s", strerror(errno)); bp_set_ipopts(sd); bp_bind_ip(sd, BFD_DEFDESTPORT); - return sd; } -int bp_udp_mhop(void) +int bp_udp_mhop(const struct vrf *vrf) { int sd; - sd = socket(AF_INET, SOCK_DGRAM, PF_UNSPEC); + frr_with_privs(&bglobal.bfdd_privs) { + sd = vrf_socket(AF_INET, SOCK_DGRAM, PF_UNSPEC, vrf->vrf_id, + vrf->name); + } if (sd == -1) - log_fatal("udp-mhop: socket: %s", strerror(errno)); + zlog_fatal("udp-mhop: socket: %s", strerror(errno)); bp_set_ipopts(sd); bp_bind_ip(sd, BFD_DEF_MHOP_DEST_PORT); @@ -905,11 +957,22 @@ int bp_peer_socket(const struct bfd_session *bs) int sd, pcount; struct sockaddr_in sin; static int srcPort = BFD_SRCPORTINIT; - - sd = socket(AF_INET, SOCK_DGRAM, PF_UNSPEC); + const char *device_to_bind = NULL; + + if (bs->key.ifname[0]) + device_to_bind = (const char *)bs->key.ifname; + else if ((!vrf_is_backend_netns() && bs->vrf->vrf_id != VRF_DEFAULT) + || ((CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH) + && bs->key.vrfname[0]))) + device_to_bind = (const char *)bs->key.vrfname; + + frr_with_privs(&bglobal.bfdd_privs) { + sd = vrf_socket(AF_INET, SOCK_DGRAM, PF_UNSPEC, + bs->vrf->vrf_id, device_to_bind); + } if (sd == -1) { - log_error("ipv4-new: failed to create socket: %s", - strerror(errno)); + zlog_err("ipv4-new: failed to create socket: %s", + strerror(errno)); return -1; } @@ -925,19 +988,6 @@ int bp_peer_socket(const struct bfd_session *bs) return -1; } - if (bs->key.ifname[0]) { - if (bp_bind_dev(sd, bs->key.ifname) != 0) { - close(sd); - return -1; - } - } else if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH) - && bs->key.vrfname[0]) { - if (bp_bind_dev(sd, bs->key.vrfname) != 0) { - close(sd); - return -1; - } - } - /* Find an available source port in the proper range */ memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; @@ -945,15 +995,15 @@ int bp_peer_socket(const struct bfd_session *bs) sin.sin_len = sizeof(sin); #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */ memcpy(&sin.sin_addr, &bs->key.local, sizeof(sin.sin_addr)); - if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH) == 0) + if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH) == 0) sin.sin_addr.s_addr = INADDR_ANY; pcount = 0; do { if ((++pcount) > (BFD_SRCPORTMAX - BFD_SRCPORTINIT)) { /* Searched all ports, none available */ - log_error("ipv4-new: failed to bind port: %s", - strerror(errno)); + zlog_err("ipv4-new: failed to bind port: %s", + strerror(errno)); close(sd); return -1; } @@ -975,11 +1025,22 @@ int bp_peer_socketv6(const struct bfd_session *bs) int sd, pcount; struct sockaddr_in6 sin6; static int srcPort = BFD_SRCPORTINIT; - - sd = socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC); + const char *device_to_bind = NULL; + + if (bs->key.ifname[0]) + device_to_bind = (const char *)bs->key.ifname; + else if ((!vrf_is_backend_netns() && bs->vrf->vrf_id != VRF_DEFAULT) + || ((CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH) + && bs->key.vrfname[0]))) + device_to_bind = (const char *)bs->key.vrfname; + + frr_with_privs(&bglobal.bfdd_privs) { + sd = vrf_socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC, + bs->vrf->vrf_id, device_to_bind); + } if (sd == -1) { - log_error("ipv6-new: failed to create socket: %s", - strerror(errno)); + zlog_err("ipv6-new: failed to create socket: %s", + strerror(errno)); return -1; } @@ -1002,28 +1063,15 @@ int bp_peer_socketv6(const struct bfd_session *bs) sin6.sin6_len = sizeof(sin6); #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */ memcpy(&sin6.sin6_addr, &bs->key.local, sizeof(sin6.sin6_addr)); - if (IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr)) + if (bs->ifp && IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr)) sin6.sin6_scope_id = bs->ifp->ifindex; - if (bs->key.ifname[0]) { - if (bp_bind_dev(sd, bs->key.ifname) != 0) { - close(sd); - return -1; - } - } else if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH) - && bs->key.vrfname[0]) { - if (bp_bind_dev(sd, bs->key.vrfname) != 0) { - close(sd); - return -1; - } - } - pcount = 0; do { if ((++pcount) > (BFD_SRCPORTMAX - BFD_SRCPORTINIT)) { /* Searched all ports, none available */ - log_error("ipv6-new: failed to bind port: %s", - strerror(errno)); + zlog_err("ipv6-new: failed to bind port: %s", + strerror(errno)); close(sd); return -1; } @@ -1041,8 +1089,8 @@ int bp_set_ttlv6(int sd, uint8_t value) if (setsockopt(sd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl)) == -1) { - log_warning("set-ttlv6: setsockopt(IPV6_UNICAST_HOPS, %d): %s", - value, strerror(errno)); + zlog_warn("set-ttlv6: setsockopt(IPV6_UNICAST_HOPS, %d): %s", + value, strerror(errno)); return -1; } @@ -1055,8 +1103,8 @@ int bp_set_tosv6(int sd, uint8_t value) if (setsockopt(sd, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos)) == -1) { - log_warning("set-tosv6: setsockopt(IPV6_TCLASS, %d): %s", value, - strerror(errno)); + zlog_warn("set-tosv6: setsockopt(IPV6_TCLASS, %d): %s", value, + strerror(errno)); return -1; } @@ -1069,22 +1117,23 @@ static void bp_set_ipv6opts(int sd) int ipv6_only = BFD_IPV6_ONLY_VAL; if (bp_set_ttlv6(sd, BFD_TTL_VAL) == -1) - log_fatal("set-ipv6opts: setsockopt(IPV6_UNICAST_HOPS, %d): %s", - BFD_TTL_VAL, strerror(errno)); + zlog_fatal( + "set-ipv6opts: setsockopt(IPV6_UNICAST_HOPS, %d): %s", + BFD_TTL_VAL, strerror(errno)); if (setsockopt_ipv6_hoplimit(sd, BFD_RCV_TTL_VAL) == -1) - log_fatal("set-ipv6opts: setsockopt(IPV6_HOPLIMIT, %d): %s", - BFD_RCV_TTL_VAL, strerror(errno)); + zlog_fatal("set-ipv6opts: setsockopt(IPV6_HOPLIMIT, %d): %s", + BFD_RCV_TTL_VAL, strerror(errno)); if (setsockopt_ipv6_pktinfo(sd, ipv6_pktinfo) == -1) - log_fatal("set-ipv6opts: setsockopt(IPV6_PKTINFO, %d): %s", - ipv6_pktinfo, strerror(errno)); + zlog_fatal("set-ipv6opts: setsockopt(IPV6_PKTINFO, %d): %s", + ipv6_pktinfo, strerror(errno)); if (setsockopt(sd, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6_only, sizeof(ipv6_only)) == -1) - log_fatal("set-ipv6opts: setsockopt(IPV6_V6ONLY, %d): %s", - ipv6_only, strerror(errno)); + zlog_fatal("set-ipv6opts: setsockopt(IPV6_V6ONLY, %d): %s", + ipv6_only, strerror(errno)); } static void bp_bind_ipv6(int sd, uint16_t port) @@ -1099,16 +1148,25 @@ static void bp_bind_ipv6(int sd, uint16_t port) sin6.sin6_len = sizeof(sin6); #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */ if (bind(sd, (struct sockaddr *)&sin6, sizeof(sin6)) == -1) - log_fatal("bind-ipv6: bind: %s", strerror(errno)); + zlog_fatal("bind-ipv6: bind: %s", strerror(errno)); } -int bp_udp6_shop(void) +int bp_udp6_shop(const struct vrf *vrf) { int sd; - sd = socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC); - if (sd == -1) - log_fatal("udp6-shop: socket: %s", strerror(errno)); + frr_with_privs(&bglobal.bfdd_privs) { + sd = vrf_socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC, vrf->vrf_id, + vrf->name); + } + if (sd == -1) { + if (errno != EAFNOSUPPORT) + zlog_fatal("udp6-shop: socket: %s", strerror(errno)); + else + zlog_warn("udp6-shop: V6 is not supported, continuing"); + + return -1; + } bp_set_ipv6opts(sd); bp_bind_ipv6(sd, BFD_DEFDESTPORT); @@ -1116,13 +1174,22 @@ int bp_udp6_shop(void) return sd; } -int bp_udp6_mhop(void) +int bp_udp6_mhop(const struct vrf *vrf) { int sd; - sd = socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC); - if (sd == -1) - log_fatal("udp6-mhop: socket: %s", strerror(errno)); + frr_with_privs(&bglobal.bfdd_privs) { + sd = vrf_socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC, vrf->vrf_id, + vrf->name); + } + if (sd == -1) { + if (errno != EAFNOSUPPORT) + zlog_fatal("udp6-mhop: socket: %s", strerror(errno)); + else + zlog_warn("udp6-mhop: V6 is not supported, continuing"); + + return -1; + } bp_set_ipv6opts(sd); bp_bind_ipv6(sd, BFD_DEF_MHOP_DEST_PORT); @@ -1130,13 +1197,15 @@ int bp_udp6_mhop(void) return sd; } -int bp_echo_socket(void) +int bp_echo_socket(const struct vrf *vrf) { int s; - s = socket(AF_INET, SOCK_DGRAM, 0); + frr_with_privs(&bglobal.bfdd_privs) { + s = vrf_socket(AF_INET, SOCK_DGRAM, 0, vrf->vrf_id, vrf->name); + } if (s == -1) - log_fatal("echo-socket: socket: %s", strerror(errno)); + zlog_fatal("echo-socket: socket: %s", strerror(errno)); bp_set_ipopts(s); bp_bind_ip(s, BFD_DEF_ECHO_PORT); @@ -1144,13 +1213,22 @@ int bp_echo_socket(void) return s; } -int bp_echov6_socket(void) +int bp_echov6_socket(const struct vrf *vrf) { int s; - s = socket(AF_INET6, SOCK_DGRAM, 0); - if (s == -1) - log_fatal("echov6-socket: socket: %s", strerror(errno)); + frr_with_privs(&bglobal.bfdd_privs) { + s = vrf_socket(AF_INET6, SOCK_DGRAM, 0, vrf->vrf_id, vrf->name); + } + if (s == -1) { + if (errno != EAFNOSUPPORT) + zlog_fatal("echov6-socket: socket: %s", + strerror(errno)); + else + zlog_warn("echov6-socket: V6 is not supported, continuing"); + + return -1; + } bp_set_ipv6opts(s); bp_bind_ipv6(s, BFD_DEF_ECHO_PORT); diff --git a/bfdd/bfdctl.h b/bfdd/bfdctl.h index 0da1ca8df6..e1cff9a31c 100644 --- a/bfdd/bfdctl.h +++ b/bfdd/bfdctl.h @@ -84,10 +84,19 @@ struct bfd_peer_cfg { bool bpc_has_echointerval; uint64_t bpc_echointerval; + bool bpc_has_minimum_ttl; + uint8_t bpc_minimum_ttl; + bool bpc_echo; bool bpc_createonly; bool bpc_shutdown; + bool bpc_cbit; + bool bpc_passive; + + bool bpc_has_profile; + char bpc_profile[64]; + /* Status information */ enum bfd_peer_status bpc_bps; uint32_t bpc_id; diff --git a/bfdd/bfdd.c b/bfdd/bfdd.c index 6023b5e4f0..6b8f2c5d46 100644 --- a/bfdd/bfdd.c +++ b/bfdd/bfdd.c @@ -20,39 +20,29 @@ #include +#include "filter.h" + #include "bfd.h" +#include "bfdd_nb.h" #include "lib/version.h" +#include "lib/command.h" /* * FRR related code. */ -DEFINE_MGROUP(BFDD, "Bidirectional Forwarding Detection Daemon"); -DEFINE_MTYPE(BFDD, BFDD_TMP, "short-lived temporary memory"); -DEFINE_MTYPE(BFDD, BFDD_CONFIG, "long-lived configuration memory"); -DEFINE_MTYPE(BFDD, BFDD_LABEL, "long-lived label memory"); -DEFINE_MTYPE(BFDD, BFDD_CONTROL, "long-lived control socket memory"); -DEFINE_MTYPE(BFDD, BFDD_SESSION_OBSERVER, "Session observer"); -DEFINE_MTYPE(BFDD, BFDD_NOTIFICATION, "short-lived control notification data"); +DEFINE_MGROUP(BFDD, "Bidirectional Forwarding Detection Daemon") +DEFINE_MTYPE(BFDD, BFDD_CONTROL, "long-lived control socket memory") +DEFINE_MTYPE(BFDD, BFDD_NOTIFICATION, "short-lived control notification data") /* Master of threads. */ struct thread_master *master; /* BFDd privileges */ -static zebra_capabilities_t _caps_p[] = {ZCAP_BIND}; +static zebra_capabilities_t _caps_p[] = {ZCAP_BIND, ZCAP_SYS_ADMIN, ZCAP_NET_RAW}; -struct zebra_privs_t bfdd_privs = { -#if defined(FRR_USER) && defined(FRR_GROUP) - .user = FRR_USER, - .group = FRR_GROUP, -#endif -#if defined(VTY_GROUP) - .vty_group = VTY_GROUP, -#endif - .caps_p = _caps_p, - .cap_num_p = array_size(_caps_p), - .cap_num_i = 0, -}; +/* BFD daemon information. */ +static struct frr_daemon_info bfdd_di; void socket_close(int *s) { @@ -60,8 +50,8 @@ void socket_close(int *s) return; if (close(*s) != 0) - log_error("%s: close(%d): (%d) %s", __func__, *s, errno, - strerror(errno)); + zlog_err("%s: close(%d): (%d) %s", __func__, *s, errno, + strerror(errno)); *s = -1; } @@ -73,6 +63,8 @@ static void sigusr1_handler(void) static void sigterm_handler(void) { + bglobal.bg_shutdown = true; + /* Signalize shutdown. */ frr_early_fini(); @@ -85,12 +77,7 @@ static void sigterm_handler(void) /* Shutdown and free all protocol related memory. */ bfd_shutdown(); - /* Close all descriptors. */ - socket_close(&bglobal.bg_echo); - socket_close(&bglobal.bg_shop); - socket_close(&bglobal.bg_mhop); - socket_close(&bglobal.bg_shop6); - socket_close(&bglobal.bg_mhop6); + bfd_vrf_terminate(); /* Terminate and free() FRR related memory. */ frr_fini(); @@ -98,6 +85,14 @@ static void sigterm_handler(void) exit(0); } +static void sighup_handler(void) +{ + zlog_info("SIGHUP received"); + + /* Reload config file. */ + vty_read_config(NULL, bfdd_di.config_file, config_default); +} + static struct quagga_signal_t bfd_signals[] = { { .signal = SIGUSR1, @@ -111,15 +106,28 @@ static struct quagga_signal_t bfd_signals[] = { .signal = SIGINT, .handler = &sigterm_handler, }, + { + .signal = SIGHUP, + .handler = &sighup_handler, + }, +}; + +static const struct frr_yang_module_info *const bfdd_yang_modules[] = { + &frr_filter_info, + &frr_interface_info, + &frr_bfdd_info, + &frr_vrf_info, }; FRR_DAEMON_INFO(bfdd, BFD, .vty_port = 2617, .proghelp = "Implementation of the BFD protocol.", .signals = bfd_signals, .n_signals = array_size(bfd_signals), - .privs = &bfdd_privs) + .privs = &bglobal.bfdd_privs, + .yang_modules = bfdd_yang_modules, + .n_yang_modules = array_size(bfdd_yang_modules)) #define OPTION_CTLSOCK 1001 -static struct option longopts[] = { +static const struct option longopts[] = { {"bfdctl", required_argument, NULL, OPTION_CTLSOCK}, {0} }; @@ -130,7 +138,7 @@ static struct option longopts[] = { */ struct bfd_global bglobal; -struct bfd_diag_str_list diag_list[] = { +const struct bfd_diag_str_list diag_list[] = { {.str = "control-expired", .type = BD_CONTROL_EXPIRED}, {.str = "echo-failed", .type = BD_ECHO_FAILED}, {.str = "neighbor-down", .type = BD_NEIGHBOR_DOWN}, @@ -142,7 +150,7 @@ struct bfd_diag_str_list diag_list[] = { {.str = NULL}, }; -struct bfd_state_str_list state_list[] = { +const struct bfd_state_str_list state_list[] = { {.str = "admin-down", .type = PTM_BFD_ADM_DOWN}, {.str = "down", .type = PTM_BFD_DOWN}, {.str = "init", .type = PTM_BFD_INIT}, @@ -153,26 +161,41 @@ struct bfd_state_str_list state_list[] = { static void bg_init(void) { + struct zebra_privs_t bfdd_privs = { +#if defined(FRR_USER) && defined(FRR_GROUP) + .user = FRR_USER, + .group = FRR_GROUP, +#endif +#if defined(VTY_GROUP) + .vty_group = VTY_GROUP, +#endif + .caps_p = _caps_p, + .cap_num_p = array_size(_caps_p), + .cap_num_i = 0, + }; + TAILQ_INIT(&bglobal.bg_bcslist); TAILQ_INIT(&bglobal.bg_obslist); - bglobal.bg_shop = bp_udp_shop(); - bglobal.bg_mhop = bp_udp_mhop(); - bglobal.bg_shop6 = bp_udp6_shop(); - bglobal.bg_mhop6 = bp_udp6_mhop(); - bglobal.bg_echo = bp_echo_socket(); - bglobal.bg_echov6 = bp_echov6_socket(); + memcpy(&bglobal.bfdd_privs, &bfdd_privs, + sizeof(bfdd_privs)); } int main(int argc, char *argv[]) { - const char *ctl_path = BFDD_CONTROL_SOCKET; + char ctl_path[512]; + bool ctlsockused = false; int opt; + /* Initialize system sockets. */ + bg_init(); + frr_preinit(&bfdd_di, argc, argv); frr_opt_add("", longopts, " --bfdctl Specify bfdd control socket\n"); + snprintf(ctl_path, sizeof(ctl_path), BFDD_CONTROL_SOCKET, + "", ""); while (true) { opt = frr_getopt(argc, argv, NULL); if (opt == EOF) @@ -180,7 +203,8 @@ int main(int argc, char *argv[]) switch (opt) { case OPTION_CTLSOCK: - ctl_path = optarg; + strlcpy(ctl_path, optarg, sizeof(ctl_path)); + ctlsockused = true; break; default: @@ -189,41 +213,30 @@ int main(int argc, char *argv[]) } } + if (bfdd_di.pathspace && !ctlsockused) + snprintf(ctl_path, sizeof(ctl_path), BFDD_CONTROL_SOCKET, + "/", bfdd_di.pathspace); + #if 0 /* TODO add support for JSON configuration files. */ parse_config(conf); #endif - /* Initialize logging API. */ - log_init(1, BLOG_DEBUG, &bfdd_di); - - /* Initialize system sockets. */ - bg_init(); + /* Initialize FRR infrastructure. */ + master = frr_init(); /* Initialize control socket. */ control_init(ctl_path); - /* Initialize FRR infrastructure. */ - master = frr_init(); - /* Initialize BFD data structures. */ bfd_initialize(); + bfd_vrf_init(); + + access_list_init(); + /* Initialize zebra connection. */ - bfdd_zclient_init(&bfdd_privs); - - /* Add descriptors to the event loop. */ - thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_shop, - &bglobal.bg_ev[0]); - thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_mhop, - &bglobal.bg_ev[1]); - thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_shop6, - &bglobal.bg_ev[2]); - thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_mhop6, - &bglobal.bg_ev[3]); - thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_echo, - &bglobal.bg_ev[4]); - thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_echov6, - &bglobal.bg_ev[5]); + bfdd_zclient_init(&bglobal.bfdd_privs); + thread_add_read(master, control_accept, NULL, bglobal.bg_csock, &bglobal.bg_csockev); diff --git a/bfdd/bfdd_cli.c b/bfdd/bfdd_cli.c new file mode 100644 index 0000000000..5a844e56e1 --- /dev/null +++ b/bfdd/bfdd_cli.c @@ -0,0 +1,658 @@ +/* + * BFD daemon CLI implementation. + * + * Copyright (C) 2019 Network Device Education Foundation, Inc. ("NetDEF") + * Rafael Zalamena + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#include + +#include "lib/command.h" +#include "lib/log.h" +#include "lib/northbound_cli.h" + +#ifndef VTYSH_EXTRACT_PL +#include "bfdd/bfdd_cli_clippy.c" +#endif /* VTYSH_EXTRACT_PL */ + +#include "bfd.h" +#include "bfdd_nb.h" + +/* + * Definitions. + */ +#define PEER_STR "Configure peer\n" +#define INTERFACE_NAME_STR "Configure interface name to use\n" +#define PEER_IPV4_STR "IPv4 peer address\n" +#define PEER_IPV6_STR "IPv6 peer address\n" +#define MHOP_STR "Configure multihop\n" +#define LOCAL_STR "Configure local address\n" +#define LOCAL_IPV4_STR "IPv4 local address\n" +#define LOCAL_IPV6_STR "IPv6 local address\n" +#define LOCAL_INTF_STR "Configure local interface name to use\n" +#define VRF_STR "Configure VRF\n" +#define VRF_NAME_STR "Configure VRF name\n" + +/* + * Prototypes. + */ +static bool +bfd_cli_is_single_hop(struct vty *vty) +{ + return strstr(VTY_CURR_XPATH, "/single-hop") != NULL; +} + +static bool +bfd_cli_is_profile(struct vty *vty) +{ + return strstr(VTY_CURR_XPATH, "/bfd/profile") != NULL; +} + +/* + * Functions. + */ +DEFPY_YANG_NOSH( + bfd_enter, bfd_enter_cmd, + "bfd", + "Configure BFD peers\n") +{ + int ret; + + nb_cli_enqueue_change(vty, "/frr-bfdd:bfdd/bfd", NB_OP_CREATE, NULL); + ret = nb_cli_apply_changes(vty, NULL); + if (ret == CMD_SUCCESS) + VTY_PUSH_XPATH(BFD_NODE, "/frr-bfdd:bfdd/bfd"); + + return ret; +} + +DEFUN_YANG( + bfd_config_reset, bfd_config_reset_cmd, + "no bfd", + NO_STR + "Configure BFD peers\n") +{ + nb_cli_enqueue_change(vty, "/frr-bfdd:bfdd/bfd", NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +void bfd_cli_show_header(struct vty *vty, + struct lyd_node *dnode __attribute__((__unused__)), + bool show_defaults __attribute__((__unused__))) +{ + vty_out(vty, "!\nbfd\n"); +} + +void bfd_cli_show_header_end(struct vty *vty, + struct lyd_node *dnode __attribute__((__unused__))) +{ + vty_out(vty, "!\n"); +} + +DEFPY_YANG_NOSH( + bfd_peer_enter, bfd_peer_enter_cmd, + "peer [{multihop$multihop|local-address |interface IFNAME$ifname|vrf NAME}]", + PEER_STR + PEER_IPV4_STR + PEER_IPV6_STR + MHOP_STR + LOCAL_STR + LOCAL_IPV4_STR + LOCAL_IPV6_STR + INTERFACE_STR + LOCAL_INTF_STR + VRF_STR + VRF_NAME_STR) +{ + int ret, slen; + char source_str[INET6_ADDRSTRLEN + 32]; + char xpath[XPATH_MAXLEN], xpath_srcaddr[XPATH_MAXLEN + 32]; + + if (multihop) { + if (!local_address_str) { + vty_out(vty, "%% local-address is required when using multihop\n"); + return CMD_WARNING_CONFIG_FAILED; + } + snprintf(source_str, sizeof(source_str), "[source-addr='%s']", + local_address_str); + } else + source_str[0] = 0; + + slen = snprintf(xpath, sizeof(xpath), + "/frr-bfdd:bfdd/bfd/sessions/%s%s[dest-addr='%s']", + multihop ? "multi-hop" : "single-hop", source_str, + peer_str); + if (ifname) + slen += snprintf(xpath + slen, sizeof(xpath) - slen, + "[interface='%s']", ifname); + else + slen += snprintf(xpath + slen, sizeof(xpath) - slen, + "[interface='']"); + if (vrf) + snprintf(xpath + slen, sizeof(xpath) - slen, "[vrf='%s']", vrf); + else + snprintf(xpath + slen, sizeof(xpath) - slen, "[vrf='%s']", + VRF_DEFAULT_NAME); + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + if (multihop == NULL && local_address_str != NULL) { + snprintf(xpath_srcaddr, sizeof(xpath_srcaddr), + "%s/source-addr", xpath); + nb_cli_enqueue_change(vty, xpath_srcaddr, NB_OP_MODIFY, + local_address_str); + } + + /* Apply settings immediately. */ + ret = nb_cli_apply_changes(vty, NULL); + if (ret == CMD_SUCCESS) + VTY_PUSH_XPATH(BFD_PEER_NODE, xpath); + + return ret; +} + +DEFPY_YANG( + bfd_no_peer, bfd_no_peer_cmd, + "no peer [{multihop$multihop|local-address |interface IFNAME$ifname|vrf NAME}]", + NO_STR + PEER_STR + PEER_IPV4_STR + PEER_IPV6_STR + MHOP_STR + LOCAL_STR + LOCAL_IPV4_STR + LOCAL_IPV6_STR + INTERFACE_STR + LOCAL_INTF_STR + VRF_STR + VRF_NAME_STR) +{ + int slen; + char xpath[XPATH_MAXLEN]; + char source_str[INET6_ADDRSTRLEN + 32]; + + if (multihop) + snprintf(source_str, sizeof(source_str), "[source-addr='%s']", + local_address_str); + else + source_str[0] = 0; + + slen = snprintf(xpath, sizeof(xpath), + "/frr-bfdd:bfdd/bfd/sessions/%s%s[dest-addr='%s']", + multihop ? "multi-hop" : "single-hop", source_str, + peer_str); + if (ifname) + slen += snprintf(xpath + slen, sizeof(xpath) - slen, + "[interface='%s']", ifname); + else + slen += snprintf(xpath + slen, sizeof(xpath) - slen, + "[interface='']"); + if (vrf) + snprintf(xpath + slen, sizeof(xpath) - slen, "[vrf='%s']", vrf); + else + snprintf(xpath + slen, sizeof(xpath) - slen, "[vrf='%s']", + VRF_DEFAULT_NAME); + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + /* Apply settings immediatly. */ + return nb_cli_apply_changes(vty, NULL); +} + +static void _bfd_cli_show_peer(struct vty *vty, struct lyd_node *dnode, + bool show_defaults __attribute__((__unused__)), + bool mhop) +{ + const char *vrf = yang_dnode_get_string(dnode, "./vrf"); + const char *ifname = yang_dnode_get_string(dnode, "./interface"); + + vty_out(vty, " peer %s", + yang_dnode_get_string(dnode, "./dest-addr")); + + if (mhop) + vty_out(vty, " multihop"); + + if (yang_dnode_exists(dnode, "./source-addr")) + vty_out(vty, " local-address %s", + yang_dnode_get_string(dnode, "./source-addr")); + + if (strcmp(vrf, VRF_DEFAULT_NAME)) + vty_out(vty, " vrf %s", vrf); + + if (ifname[0]) + vty_out(vty, " interface %s", ifname); + + vty_out(vty, "\n"); +} + +void bfd_cli_show_single_hop_peer(struct vty *vty, + struct lyd_node *dnode, + bool show_defaults) +{ + _bfd_cli_show_peer(vty, dnode, show_defaults, false); +} + +void bfd_cli_show_multi_hop_peer(struct vty *vty, + struct lyd_node *dnode, + bool show_defaults) +{ + _bfd_cli_show_peer(vty, dnode, show_defaults, true); +} + +void bfd_cli_show_peer_end(struct vty *vty, + struct lyd_node *dnode __attribute__((__unused__))) +{ + vty_out(vty, " !\n"); +} + +DEFPY_YANG( + bfd_peer_shutdown, bfd_peer_shutdown_cmd, + "[no] shutdown", + NO_STR + "Disable BFD peer\n") +{ + nb_cli_enqueue_change(vty, "./administrative-down", NB_OP_MODIFY, + no ? "false" : "true"); + return nb_cli_apply_changes(vty, NULL); +} + +void bfd_cli_show_shutdown(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + if (show_defaults) + vty_out(vty, " no shutdown\n"); + else + vty_out(vty, " %sshutdown\n", + yang_dnode_get_bool(dnode, NULL) ? "" : "no "); +} + +DEFPY_YANG( + bfd_peer_passive, bfd_peer_passive_cmd, + "[no] passive-mode", + NO_STR + "Don't attempt to start sessions\n") +{ + nb_cli_enqueue_change(vty, "./passive-mode", NB_OP_MODIFY, + no ? "false" : "true"); + return nb_cli_apply_changes(vty, NULL); +} + +void bfd_cli_show_passive(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + if (show_defaults) + vty_out(vty, " no passive-mode\n"); + else + vty_out(vty, " %spassive-mode\n", + yang_dnode_get_bool(dnode, NULL) ? "" : "no "); +} + +DEFPY_YANG( + bfd_peer_minimum_ttl, bfd_peer_minimum_ttl_cmd, + "[no] minimum-ttl (1-254)$ttl", + NO_STR + "Expect packets with at least this TTL\n" + "Minimum TTL expected\n") +{ + if (bfd_cli_is_single_hop(vty)) { + vty_out(vty, "%% Minimum TTL is only available for multi hop sessions.\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (no) + nb_cli_enqueue_change(vty, "./minimum-ttl", NB_OP_DESTROY, + NULL); + else + nb_cli_enqueue_change(vty, "./minimum-ttl", NB_OP_MODIFY, + ttl_str); + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + no_bfd_peer_minimum_ttl, no_bfd_peer_minimum_ttl_cmd, + "no minimum-ttl", + NO_STR + "Expect packets with at least this TTL\n") +{ + nb_cli_enqueue_change(vty, "./minimum-ttl", NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +void bfd_cli_show_minimum_ttl(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + if (show_defaults) + vty_out(vty, " minimum-ttl 254\n"); + else + vty_out(vty, " minimum-ttl %s\n", + yang_dnode_get_string(dnode, NULL)); +} + +DEFPY_YANG( + bfd_peer_mult, bfd_peer_mult_cmd, + "detect-multiplier (2-255)$multiplier", + "Configure peer detection multiplier\n" + "Configure peer detection multiplier value\n") +{ + nb_cli_enqueue_change(vty, "./detection-multiplier", NB_OP_MODIFY, + multiplier_str); + return nb_cli_apply_changes(vty, NULL); +} + +void bfd_cli_show_mult(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + if (show_defaults) + vty_out(vty, " detect-multiplier %d\n", + BFD_DEFDETECTMULT); + else + vty_out(vty, " detect-multiplier %s\n", + yang_dnode_get_string(dnode, NULL)); +} + +DEFPY_YANG( + bfd_peer_rx, bfd_peer_rx_cmd, + "receive-interval (10-60000)$interval", + "Configure peer receive interval\n" + "Configure peer receive interval value in milliseconds\n") +{ + char value[32]; + + snprintf(value, sizeof(value), "%ld", interval * 1000); + nb_cli_enqueue_change(vty, "./required-receive-interval", NB_OP_MODIFY, + value); + + return nb_cli_apply_changes(vty, NULL); +} + +void bfd_cli_show_rx(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + uint32_t value; + + if (show_defaults) + vty_out(vty, " receive-interval %d\n", + BFD_DEFREQUIREDMINRX); + else { + value = yang_dnode_get_uint32(dnode, NULL); + vty_out(vty, " receive-interval %u\n", value / 1000); + } +} + +DEFPY_YANG( + bfd_peer_tx, bfd_peer_tx_cmd, + "transmit-interval (10-60000)$interval", + "Configure peer transmit interval\n" + "Configure peer transmit interval value in milliseconds\n") +{ + char value[32]; + + snprintf(value, sizeof(value), "%ld", interval * 1000); + nb_cli_enqueue_change(vty, "./desired-transmission-interval", + NB_OP_MODIFY, value); + + return nb_cli_apply_changes(vty, NULL); +} + +void bfd_cli_show_tx(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + uint32_t value; + + if (show_defaults) + vty_out(vty, " transmit-interval %d\n", + BFD_DEFDESIREDMINTX); + else { + value = yang_dnode_get_uint32(dnode, NULL); + vty_out(vty, " transmit-interval %u\n", value / 1000); + } +} + +DEFPY_YANG( + bfd_peer_echo, bfd_peer_echo_cmd, + "[no] echo-mode", + NO_STR + "Configure echo mode\n") +{ + if (!bfd_cli_is_profile(vty) && !bfd_cli_is_single_hop(vty)) { + vty_out(vty, "%% Echo mode is only available for single hop sessions.\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + nb_cli_enqueue_change(vty, "./echo-mode", NB_OP_MODIFY, + no ? "false" : "true"); + return nb_cli_apply_changes(vty, NULL); +} + +void bfd_cli_show_echo(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + if (show_defaults) + vty_out(vty, " no echo-mode\n"); + else + vty_out(vty, " %secho-mode\n", + yang_dnode_get_bool(dnode, NULL) ? "" : "no "); +} + +DEFPY_YANG( + bfd_peer_echo_interval, bfd_peer_echo_interval_cmd, + "echo-interval (10-60000)$interval", + "Configure peer echo interval\n" + "Configure peer echo interval value in milliseconds\n") +{ + char value[32]; + + if (!bfd_cli_is_profile(vty) && !bfd_cli_is_single_hop(vty)) { + vty_out(vty, "%% Echo mode is only available for single hop sessions.\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + snprintf(value, sizeof(value), "%ld", interval * 1000); + nb_cli_enqueue_change(vty, "./desired-echo-transmission-interval", + NB_OP_MODIFY, value); + + return nb_cli_apply_changes(vty, NULL); +} + +void bfd_cli_show_echo_interval(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + uint32_t value; + + if (show_defaults) + vty_out(vty, " echo-interval %d\n", + BFD_DEF_REQ_MIN_ECHO); + else { + value = yang_dnode_get_uint32(dnode, NULL); + vty_out(vty, " echo-interval %u\n", value / 1000); + } +} + +/* + * Profile commands. + */ +DEFPY_YANG_NOSH(bfd_profile, bfd_profile_cmd, + "profile BFDPROF$name", + BFD_PROFILE_STR + BFD_PROFILE_NAME_STR) +{ + char xpath[XPATH_MAXLEN]; + int rv; + + snprintf(xpath, sizeof(xpath), "/frr-bfdd:bfdd/bfd/profile[name='%s']", + name); + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + /* Apply settings immediately. */ + rv = nb_cli_apply_changes(vty, NULL); + if (rv == CMD_SUCCESS) + VTY_PUSH_XPATH(BFD_PROFILE_NODE, xpath); + + return CMD_SUCCESS; +} + +DEFPY_YANG(no_bfd_profile, no_bfd_profile_cmd, + "no profile BFDPROF$name", + NO_STR + BFD_PROFILE_STR + BFD_PROFILE_NAME_STR) +{ + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), "/frr-bfdd:bfdd/bfd/profile[name='%s']", + name); + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + /* Apply settings immediately. */ + return nb_cli_apply_changes(vty, NULL); +} + +void bfd_cli_show_profile(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + vty_out(vty, " profile %s\n", yang_dnode_get_string(dnode, "./name")); +} + +ALIAS_YANG(bfd_peer_mult, bfd_profile_mult_cmd, + "detect-multiplier (2-255)$multiplier", + "Configure peer detection multiplier\n" + "Configure peer detection multiplier value\n") + +ALIAS_YANG(bfd_peer_tx, bfd_profile_tx_cmd, + "transmit-interval (10-60000)$interval", + "Configure peer transmit interval\n" + "Configure peer transmit interval value in milliseconds\n") + +ALIAS_YANG(bfd_peer_rx, bfd_profile_rx_cmd, + "receive-interval (10-60000)$interval", + "Configure peer receive interval\n" + "Configure peer receive interval value in milliseconds\n") + +ALIAS_YANG(bfd_peer_shutdown, bfd_profile_shutdown_cmd, + "[no] shutdown", + NO_STR + "Disable BFD peer\n") + +ALIAS_YANG(bfd_peer_passive, bfd_profile_passive_cmd, + "[no] passive-mode", + NO_STR + "Don't attempt to start sessions\n") + +ALIAS_YANG(bfd_peer_minimum_ttl, bfd_profile_minimum_ttl_cmd, + "[no] minimum-ttl (1-254)$ttl", + NO_STR + "Expect packets with at least this TTL\n" + "Minimum TTL expected\n") + +ALIAS_YANG(no_bfd_peer_minimum_ttl, no_bfd_profile_minimum_ttl_cmd, + "no minimum-ttl", + NO_STR + "Expect packets with at least this TTL\n") + +ALIAS_YANG(bfd_peer_echo, bfd_profile_echo_cmd, + "[no] echo-mode", + NO_STR + "Configure echo mode\n") + +ALIAS_YANG(bfd_peer_echo_interval, bfd_profile_echo_interval_cmd, + "echo-interval (10-60000)$interval", + "Configure peer echo interval\n" + "Configure peer echo interval value in milliseconds\n") + +DEFPY_YANG(bfd_peer_profile, bfd_peer_profile_cmd, + "[no] profile BFDPROF$pname", + NO_STR + "Use BFD profile settings\n" + BFD_PROFILE_NAME_STR) +{ + if (no) + nb_cli_enqueue_change(vty, "./profile", NB_OP_DESTROY, NULL); + else + nb_cli_enqueue_change(vty, "./profile", NB_OP_MODIFY, pname); + + return nb_cli_apply_changes(vty, NULL); +} + +void bfd_cli_peer_profile_show(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + vty_out(vty, " profile %s\n", yang_dnode_get_string(dnode, NULL)); +} + +struct cmd_node bfd_profile_node = { + .name = "bfd profile", + .node = BFD_PROFILE_NODE, + .parent_node = BFD_NODE, + .prompt = "%s(config-bfd-profile)# ", +}; + +static void bfd_profile_var(vector comps, struct cmd_token *token) +{ + extern struct bfdproflist bplist; + struct bfd_profile *bp; + + TAILQ_FOREACH (bp, &bplist, entry) { + vector_set(comps, XSTRDUP(MTYPE_COMPLETION, bp->name)); + } +} + +static const struct cmd_variable_handler bfd_vars[] = { + {.tokenname = "BFDPROF", .completions = bfd_profile_var}, + {.completions = NULL} +}; + +void +bfdd_cli_init(void) +{ + install_element(CONFIG_NODE, &bfd_enter_cmd); + install_element(CONFIG_NODE, &bfd_config_reset_cmd); + + install_element(BFD_NODE, &bfd_peer_enter_cmd); + install_element(BFD_NODE, &bfd_no_peer_cmd); + + install_element(BFD_PEER_NODE, &bfd_peer_shutdown_cmd); + install_element(BFD_PEER_NODE, &bfd_peer_mult_cmd); + install_element(BFD_PEER_NODE, &bfd_peer_rx_cmd); + install_element(BFD_PEER_NODE, &bfd_peer_tx_cmd); + install_element(BFD_PEER_NODE, &bfd_peer_echo_cmd); + install_element(BFD_PEER_NODE, &bfd_peer_echo_interval_cmd); + install_element(BFD_PEER_NODE, &bfd_peer_profile_cmd); + install_element(BFD_PEER_NODE, &bfd_peer_passive_cmd); + install_element(BFD_PEER_NODE, &bfd_peer_minimum_ttl_cmd); + install_element(BFD_PEER_NODE, &no_bfd_peer_minimum_ttl_cmd); + + /* Profile commands. */ + cmd_variable_handler_register(bfd_vars); + + install_node(&bfd_profile_node); + install_default(BFD_PROFILE_NODE); + + install_element(BFD_NODE, &bfd_profile_cmd); + install_element(BFD_NODE, &no_bfd_profile_cmd); + + install_element(BFD_PROFILE_NODE, &bfd_profile_mult_cmd); + install_element(BFD_PROFILE_NODE, &bfd_profile_tx_cmd); + install_element(BFD_PROFILE_NODE, &bfd_profile_rx_cmd); + install_element(BFD_PROFILE_NODE, &bfd_profile_shutdown_cmd); + install_element(BFD_PROFILE_NODE, &bfd_profile_echo_cmd); + install_element(BFD_PROFILE_NODE, &bfd_profile_echo_interval_cmd); + install_element(BFD_PROFILE_NODE, &bfd_profile_passive_cmd); + install_element(BFD_PROFILE_NODE, &bfd_profile_minimum_ttl_cmd); + install_element(BFD_PROFILE_NODE, &no_bfd_profile_minimum_ttl_cmd); +} diff --git a/bfdd/bfdd_nb.c b/bfdd/bfdd_nb.c new file mode 100644 index 0000000000..64ba3cf811 --- /dev/null +++ b/bfdd/bfdd_nb.c @@ -0,0 +1,492 @@ +/* + * BFD daemon northbound implementation. + * + * Copyright (C) 2019 Network Device Education Foundation, Inc. ("NetDEF") + * Rafael Zalamena + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#include + +#include "lib/log.h" +#include "lib/northbound.h" + +#include "bfdd_nb.h" + +/* clang-format off */ +const struct frr_yang_module_info frr_bfdd_info = { + .name = "frr-bfdd", + .nodes = { + { + .xpath = "/frr-bfdd:bfdd/bfd", + .cbs = { + .create = bfdd_bfd_create, + .destroy = bfdd_bfd_destroy, + .cli_show = bfd_cli_show_header, + .cli_show_end = bfd_cli_show_header_end, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/profile", + .cbs = { + .create = bfdd_bfd_profile_create, + .destroy = bfdd_bfd_profile_destroy, + .cli_show = bfd_cli_show_profile, + .cli_show_end = bfd_cli_show_peer_end, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/profile/detection-multiplier", + .cbs = { + .modify = bfdd_bfd_profile_detection_multiplier_modify, + .cli_show = bfd_cli_show_mult, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/profile/desired-transmission-interval", + .cbs = { + .modify = bfdd_bfd_profile_desired_transmission_interval_modify, + .cli_show = bfd_cli_show_tx, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/profile/required-receive-interval", + .cbs = { + .modify = bfdd_bfd_profile_required_receive_interval_modify, + .cli_show = bfd_cli_show_rx, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/profile/administrative-down", + .cbs = { + .modify = bfdd_bfd_profile_administrative_down_modify, + .cli_show = bfd_cli_show_shutdown, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/profile/passive-mode", + .cbs = { + .modify = bfdd_bfd_profile_passive_mode_modify, + .cli_show = bfd_cli_show_passive, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/profile/minimum-ttl", + .cbs = { + .modify = bfdd_bfd_profile_minimum_ttl_modify, + .destroy = bfdd_bfd_profile_minimum_ttl_destroy, + .cli_show = bfd_cli_show_minimum_ttl, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/profile/echo-mode", + .cbs = { + .modify = bfdd_bfd_profile_echo_mode_modify, + .cli_show = bfd_cli_show_echo, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/profile/desired-echo-transmission-interval", + .cbs = { + .modify = bfdd_bfd_profile_desired_echo_transmission_interval_modify, + .cli_show = bfd_cli_show_echo_interval, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop", + .cbs = { + .create = bfdd_bfd_sessions_single_hop_create, + .destroy = bfdd_bfd_sessions_single_hop_destroy, + .get_next = bfdd_bfd_sessions_single_hop_get_next, + .get_keys = bfdd_bfd_sessions_single_hop_get_keys, + .lookup_entry = bfdd_bfd_sessions_single_hop_lookup_entry, + .cli_show = bfd_cli_show_single_hop_peer, + .cli_show_end = bfd_cli_show_peer_end, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/source-addr", + .cbs = { + .modify = bfdd_bfd_sessions_single_hop_source_addr_modify, + .destroy = bfdd_bfd_sessions_single_hop_source_addr_destroy, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/profile", + .cbs = { + .modify = bfdd_bfd_sessions_single_hop_profile_modify, + .destroy = bfdd_bfd_sessions_single_hop_profile_destroy, + .cli_show = bfd_cli_peer_profile_show, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/detection-multiplier", + .cbs = { + .modify = bfdd_bfd_sessions_single_hop_detection_multiplier_modify, + .cli_show = bfd_cli_show_mult, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/desired-transmission-interval", + .cbs = { + .modify = bfdd_bfd_sessions_single_hop_desired_transmission_interval_modify, + .cli_show = bfd_cli_show_tx, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/required-receive-interval", + .cbs = { + .modify = bfdd_bfd_sessions_single_hop_required_receive_interval_modify, + .cli_show = bfd_cli_show_rx, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/administrative-down", + .cbs = { + .modify = bfdd_bfd_sessions_single_hop_administrative_down_modify, + .cli_show = bfd_cli_show_shutdown, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/passive-mode", + .cbs = { + .modify = bfdd_bfd_sessions_single_hop_passive_mode_modify, + .cli_show = bfd_cli_show_passive, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/echo-mode", + .cbs = { + .modify = bfdd_bfd_sessions_single_hop_echo_mode_modify, + .cli_show = bfd_cli_show_echo, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/desired-echo-transmission-interval", + .cbs = { + .modify = bfdd_bfd_sessions_single_hop_desired_echo_transmission_interval_modify, + .cli_show = bfd_cli_show_echo_interval, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/local-discriminator", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_local_discriminator_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/local-state", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_local_state_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/local-diagnostic", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_local_diagnostic_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/local-multiplier", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_local_multiplier_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/remote-discriminator", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_remote_discriminator_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/remote-state", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_remote_state_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/remote-diagnostic", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_remote_diagnostic_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/remote-multiplier", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_remote_multiplier_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/negotiated-transmission-interval", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_negotiated_transmission_interval_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/negotiated-receive-interval", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_negotiated_receive_interval_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/detection-mode", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_detection_mode_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/last-down-time", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_last_down_time_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/last-up-time", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_last_up_time_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/session-down-count", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_session_down_count_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/session-up-count", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_session_up_count_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/control-packet-input-count", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_control_packet_input_count_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/control-packet-output-count", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_control_packet_output_count_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/negotiated-echo-transmission-interval", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_negotiated_echo_transmission_interval_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/echo-packet-input-count", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_echo_packet_input_count_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/stats/echo-packet-output-count", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_echo_packet_output_count_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop", + .cbs = { + .create = bfdd_bfd_sessions_multi_hop_create, + .destroy = bfdd_bfd_sessions_multi_hop_destroy, + .get_next = bfdd_bfd_sessions_multi_hop_get_next, + .get_keys = bfdd_bfd_sessions_multi_hop_get_keys, + .lookup_entry = bfdd_bfd_sessions_multi_hop_lookup_entry, + .cli_show = bfd_cli_show_multi_hop_peer, + .cli_show_end = bfd_cli_show_peer_end, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/profile", + .cbs = { + .modify = bfdd_bfd_sessions_single_hop_profile_modify, + .destroy = bfdd_bfd_sessions_single_hop_profile_destroy, + .cli_show = bfd_cli_peer_profile_show, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/detection-multiplier", + .cbs = { + .modify = bfdd_bfd_sessions_single_hop_detection_multiplier_modify, + .cli_show = bfd_cli_show_mult, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/desired-transmission-interval", + .cbs = { + .modify = bfdd_bfd_sessions_single_hop_desired_transmission_interval_modify, + .cli_show = bfd_cli_show_tx, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/required-receive-interval", + .cbs = { + .modify = bfdd_bfd_sessions_single_hop_required_receive_interval_modify, + .cli_show = bfd_cli_show_rx, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/administrative-down", + .cbs = { + .modify = bfdd_bfd_sessions_single_hop_administrative_down_modify, + .cli_show = bfd_cli_show_shutdown, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/passive-mode", + .cbs = { + .modify = bfdd_bfd_sessions_single_hop_passive_mode_modify, + .cli_show = bfd_cli_show_passive, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/minimum-ttl", + .cbs = { + .modify = bfdd_bfd_sessions_multi_hop_minimum_ttl_modify, + .destroy = bfdd_bfd_sessions_multi_hop_minimum_ttl_destroy, + .cli_show = bfd_cli_show_minimum_ttl, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/local-discriminator", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_local_discriminator_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/local-state", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_local_state_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/local-diagnostic", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_local_diagnostic_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/local-multiplier", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_local_multiplier_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/remote-discriminator", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_remote_discriminator_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/remote-state", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_remote_state_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/remote-diagnostic", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_remote_diagnostic_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/remote-multiplier", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_remote_multiplier_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/negotiated-transmission-interval", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_negotiated_transmission_interval_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/negotiated-receive-interval", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_negotiated_receive_interval_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/detection-mode", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_detection_mode_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/last-down-time", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_last_down_time_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/last-up-time", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_last_up_time_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/session-down-count", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_session_down_count_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/session-up-count", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_session_up_count_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/control-packet-input-count", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_control_packet_input_count_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/control-packet-output-count", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_control_packet_output_count_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/negotiated-echo-transmission-interval", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_negotiated_echo_transmission_interval_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/echo-packet-input-count", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_echo_packet_input_count_get_elem, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/echo-packet-output-count", + .cbs = { + .get_elem = bfdd_bfd_sessions_single_hop_stats_echo_packet_output_count_get_elem, + } + }, + { + .xpath = NULL, + }, + } +}; diff --git a/bfdd/bfdd_nb.h b/bfdd/bfdd_nb.h new file mode 100644 index 0000000000..fbd557b6b1 --- /dev/null +++ b/bfdd/bfdd_nb.h @@ -0,0 +1,223 @@ +/* + * BFD daemon northbound implementation. + * + * Copyright (C) 2019 Network Device Education Foundation, Inc. ("NetDEF") + * Rafael Zalamena + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#ifndef _FRR_BFDD_NB_H_ +#define _FRR_BFDD_NB_H_ + +extern const struct frr_yang_module_info frr_bfdd_info; + +/* Mandatory callbacks. */ +int bfdd_bfd_create(struct nb_cb_create_args *args); +int bfdd_bfd_destroy(struct nb_cb_destroy_args *args); +int bfdd_bfd_profile_create(struct nb_cb_create_args *args); +int bfdd_bfd_profile_destroy(struct nb_cb_destroy_args *args); +int bfdd_bfd_profile_detection_multiplier_modify( + struct nb_cb_modify_args *args); +int bfdd_bfd_profile_desired_transmission_interval_modify( + struct nb_cb_modify_args *args); +int bfdd_bfd_profile_required_receive_interval_modify( + struct nb_cb_modify_args *args); +int bfdd_bfd_profile_administrative_down_modify(struct nb_cb_modify_args *args); +int bfdd_bfd_profile_passive_mode_modify(struct nb_cb_modify_args *args); +int bfdd_bfd_profile_minimum_ttl_modify(struct nb_cb_modify_args *args); +int bfdd_bfd_profile_minimum_ttl_destroy(struct nb_cb_destroy_args *args); +int bfdd_bfd_profile_echo_mode_modify(struct nb_cb_modify_args *args); +int bfdd_bfd_profile_desired_echo_transmission_interval_modify( + struct nb_cb_modify_args *args); +int bfdd_bfd_sessions_single_hop_create(struct nb_cb_create_args *args); +int bfdd_bfd_sessions_single_hop_destroy(struct nb_cb_destroy_args *args); +const void * +bfdd_bfd_sessions_single_hop_get_next(struct nb_cb_get_next_args *args); +int bfdd_bfd_sessions_single_hop_get_keys(struct nb_cb_get_keys_args *args); +const void * +bfdd_bfd_sessions_single_hop_lookup_entry(struct nb_cb_lookup_entry_args *args); +int bfdd_bfd_sessions_single_hop_source_addr_modify( + struct nb_cb_modify_args *args); +int bfdd_bfd_sessions_single_hop_source_addr_destroy( + struct nb_cb_destroy_args *args); +int bfdd_bfd_sessions_single_hop_profile_modify(struct nb_cb_modify_args *args); +int bfdd_bfd_sessions_single_hop_profile_destroy( + struct nb_cb_destroy_args *args); +int bfdd_bfd_sessions_single_hop_detection_multiplier_modify( + struct nb_cb_modify_args *args); +int bfdd_bfd_sessions_single_hop_desired_transmission_interval_modify( + struct nb_cb_modify_args *args); +int bfdd_bfd_sessions_single_hop_required_receive_interval_modify( + struct nb_cb_modify_args *args); +int bfdd_bfd_sessions_single_hop_administrative_down_modify( + struct nb_cb_modify_args *args); +int bfdd_bfd_sessions_single_hop_passive_mode_modify( + struct nb_cb_modify_args *args); +int bfdd_bfd_sessions_single_hop_echo_mode_modify( + struct nb_cb_modify_args *args); +int bfdd_bfd_sessions_single_hop_desired_echo_transmission_interval_modify( + struct nb_cb_modify_args *args); +struct yang_data * +bfdd_bfd_sessions_single_hop_stats_local_discriminator_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data *bfdd_bfd_sessions_single_hop_stats_local_state_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data *bfdd_bfd_sessions_single_hop_stats_local_diagnostic_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data *bfdd_bfd_sessions_single_hop_stats_local_multiplier_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +bfdd_bfd_sessions_single_hop_stats_remote_discriminator_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data *bfdd_bfd_sessions_single_hop_stats_remote_state_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data *bfdd_bfd_sessions_single_hop_stats_remote_diagnostic_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data *bfdd_bfd_sessions_single_hop_stats_remote_multiplier_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +bfdd_bfd_sessions_single_hop_stats_negotiated_transmission_interval_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +bfdd_bfd_sessions_single_hop_stats_negotiated_receive_interval_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data *bfdd_bfd_sessions_single_hop_stats_detection_mode_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data *bfdd_bfd_sessions_single_hop_stats_last_down_time_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data *bfdd_bfd_sessions_single_hop_stats_last_up_time_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +bfdd_bfd_sessions_single_hop_stats_session_down_count_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data *bfdd_bfd_sessions_single_hop_stats_session_up_count_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +bfdd_bfd_sessions_single_hop_stats_control_packet_input_count_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +bfdd_bfd_sessions_single_hop_stats_control_packet_output_count_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +bfdd_bfd_sessions_single_hop_stats_negotiated_echo_transmission_interval_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +bfdd_bfd_sessions_single_hop_stats_echo_packet_input_count_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +bfdd_bfd_sessions_single_hop_stats_echo_packet_output_count_get_elem( + struct nb_cb_get_elem_args *args); +int bfdd_bfd_sessions_multi_hop_create(struct nb_cb_create_args *args); +int bfdd_bfd_sessions_multi_hop_destroy(struct nb_cb_destroy_args *args); +const void * +bfdd_bfd_sessions_multi_hop_get_next(struct nb_cb_get_next_args *args); +int bfdd_bfd_sessions_multi_hop_get_keys(struct nb_cb_get_keys_args *args); +const void * +bfdd_bfd_sessions_multi_hop_lookup_entry(struct nb_cb_lookup_entry_args *args); +int bfdd_bfd_sessions_multi_hop_detection_multiplier_modify( + struct nb_cb_modify_args *args); +int bfdd_bfd_sessions_multi_hop_desired_transmission_interval_modify( + struct nb_cb_modify_args *args); +int bfdd_bfd_sessions_multi_hop_required_receive_interval_modify( + struct nb_cb_modify_args *args); +int bfdd_bfd_sessions_multi_hop_administrative_down_modify( + struct nb_cb_modify_args *args); +int bfdd_bfd_sessions_multi_hop_minimum_ttl_modify( + struct nb_cb_modify_args *args); +int bfdd_bfd_sessions_multi_hop_minimum_ttl_destroy( + struct nb_cb_destroy_args *args); +struct yang_data * +bfdd_bfd_sessions_multi_hop_stats_local_discriminator_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data *bfdd_bfd_sessions_multi_hop_stats_local_state_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data *bfdd_bfd_sessions_multi_hop_stats_local_diagnostic_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data *bfdd_bfd_sessions_multi_hop_stats_local_multiplier_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +bfdd_bfd_sessions_multi_hop_stats_remote_discriminator_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data *bfdd_bfd_sessions_multi_hop_stats_remote_state_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data *bfdd_bfd_sessions_multi_hop_stats_remote_diagnostic_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data *bfdd_bfd_sessions_multi_hop_stats_remote_multiplier_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +bfdd_bfd_sessions_multi_hop_stats_negotiated_transmission_interval_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +bfdd_bfd_sessions_multi_hop_stats_negotiated_receive_interval_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data *bfdd_bfd_sessions_multi_hop_stats_detection_mode_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data *bfdd_bfd_sessions_multi_hop_stats_last_down_time_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data *bfdd_bfd_sessions_multi_hop_stats_last_up_time_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data *bfdd_bfd_sessions_multi_hop_stats_session_down_count_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data *bfdd_bfd_sessions_multi_hop_stats_session_up_count_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +bfdd_bfd_sessions_multi_hop_stats_control_packet_input_count_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +bfdd_bfd_sessions_multi_hop_stats_control_packet_output_count_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +bfdd_bfd_sessions_multi_hop_stats_negotiated_echo_transmission_interval_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +bfdd_bfd_sessions_multi_hop_stats_echo_packet_input_count_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +bfdd_bfd_sessions_multi_hop_stats_echo_packet_output_count_get_elem( + struct nb_cb_get_elem_args *args); + +/* Optional 'cli_show' callbacks. */ +void bfd_cli_show_header(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void bfd_cli_show_header_end(struct vty *vty, struct lyd_node *dnode); +void bfd_cli_show_single_hop_peer(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void bfd_cli_show_multi_hop_peer(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void bfd_cli_show_peer_end(struct vty *vty, struct lyd_node *dnode); +void bfd_cli_show_mult(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void bfd_cli_show_tx(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void bfd_cli_show_rx(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void bfd_cli_show_shutdown(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void bfd_cli_show_echo(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void bfd_cli_show_echo_interval(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void bfd_cli_show_profile(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void bfd_cli_peer_profile_show(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void bfd_cli_show_passive(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void bfd_cli_show_minimum_ttl(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); + +#endif /* _FRR_BFDD_NB_H_ */ diff --git a/bfdd/bfdd_nb_config.c b/bfdd/bfdd_nb_config.c new file mode 100644 index 0000000000..4030e2eefa --- /dev/null +++ b/bfdd/bfdd_nb_config.c @@ -0,0 +1,843 @@ +/* + * BFD daemon northbound implementation. + * + * Copyright (C) 2019 Network Device Education Foundation, Inc. ("NetDEF") + * Rafael Zalamena + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#include + +#include "lib/log.h" +#include "lib/northbound.h" + +#include "bfd.h" +#include "bfdd_nb.h" + +/* + * Helpers. + */ +static void bfd_session_get_key(bool mhop, const struct lyd_node *dnode, + struct bfd_key *bk) +{ + const char *ifname = NULL, *vrfname = NULL; + struct sockaddr_any psa, lsa; + + /* Required destination parameter. */ + strtosa(yang_dnode_get_string(dnode, "./dest-addr"), &psa); + + /* Get optional source address. */ + memset(&lsa, 0, sizeof(lsa)); + 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"); + + /* Generate the corresponding key. */ + gen_bfd_key(bk, &psa, &lsa, mhop, ifname, vrfname); +} + +static int bfd_session_create(enum nb_event event, const struct lyd_node *dnode, + union nb_resource *resource, bool mhop) +{ + struct bfd_session *bs; + const char *ifname; + struct bfd_key bk; + struct prefix p; + + switch (event) { + case NB_EV_VALIDATE: + /* + * When `dest-addr` is IPv6 and link-local we must + * require interface name, otherwise we can't figure + * which interface to use to send the packets. + */ + 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 = ""; + + if (p.family == AF_INET6 && IN6_IS_ADDR_LINKLOCAL(&p.u.prefix6) + && strlen(ifname) == 0) { + zlog_warn( + "%s: when using link-local you must specify an interface.", + __func__); + return NB_ERR_VALIDATION; + } + break; + + case NB_EV_PREPARE: + bfd_session_get_key(mhop, dnode, &bk); + bs = bfd_key_lookup(bk); + + /* This session was already configured by another daemon. */ + if (bs != NULL) { + /* Now it is configured also by CLI. */ + SET_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG); + bs->refcount++; + + resource->ptr = bs; + break; + } + + bs = bfd_session_new(); + + /* Fill the session key. */ + bfd_session_get_key(mhop, dnode, &bs->key); + + /* Set configuration flags. */ + bs->refcount = 1; + SET_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG); + if (mhop) + SET_FLAG(bs->flags, BFD_SESS_FLAG_MH); + if (bs->key.family == AF_INET6) + SET_FLAG(bs->flags, BFD_SESS_FLAG_IPV6); + + resource->ptr = bs; + break; + + case NB_EV_APPLY: + bs = resource->ptr; + + /* Only attempt to registrate if freshly allocated. */ + if (bs->discrs.my_discr == 0 && bs_registrate(bs) == NULL) + return NB_ERR_RESOURCE; + + nb_running_set_entry(dnode, bs); + break; + + case NB_EV_ABORT: + bs = resource->ptr; + if (bs->refcount <= 1) + bfd_session_free(resource->ptr); + break; + } + + return NB_OK; +} + +static int bfd_session_destroy(enum nb_event event, + const struct lyd_node *dnode, bool mhop) +{ + struct bfd_session *bs; + struct bfd_key bk; + + switch (event) { + case NB_EV_VALIDATE: + bfd_session_get_key(mhop, dnode, &bk); + if (bfd_key_lookup(bk) == NULL) + return NB_ERR_INCONSISTENCY; + break; + + case NB_EV_PREPARE: + /* NOTHING */ + break; + + case NB_EV_APPLY: + bs = nb_running_unset_entry(dnode); + /* CLI is not using this session anymore. */ + if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG) == 0) + break; + + UNSET_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG); + bs->refcount--; + /* There are still daemons using it. */ + if (bs->refcount > 0) + break; + + bfd_session_free(bs); + break; + + case NB_EV_ABORT: + /* NOTHING */ + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-bfdd:bfdd/bfd + */ +int bfdd_bfd_create(struct nb_cb_create_args *args) +{ + if (args->event != NB_EV_APPLY) + return NB_OK; + + /* + * Set any non-NULL value to be able to call + * nb_running_unset_entry in bfdd_bfd_destroy. + */ + nb_running_set_entry(args->dnode, (void *)0x1); + + return NB_OK; +} + +int bfdd_bfd_destroy(struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + /* NOTHING */ + return NB_OK; + + case NB_EV_PREPARE: + /* NOTHING */ + return NB_OK; + + case NB_EV_APPLY: + /* + * We need to call this to unset pointers from + * the child nodes - sessions and profiles. + */ + nb_running_unset_entry(args->dnode); + + bfd_sessions_remove_manual(); + bfd_profiles_remove(); + break; + + case NB_EV_ABORT: + /* NOTHING */ + return NB_OK; + } + + return NB_OK; +} + +/* + * XPath: /frr-bfdd:bfdd/bfd/profile + */ +int bfdd_bfd_profile_create(struct nb_cb_create_args *args) +{ + struct bfd_profile *bp; + const char *name; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + name = yang_dnode_get_string(args->dnode, "./name"); + bp = bfd_profile_new(name); + nb_running_set_entry(args->dnode, bp); + + return NB_OK; +} + +int bfdd_bfd_profile_destroy(struct nb_cb_destroy_args *args) +{ + struct bfd_profile *bp; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + bp = nb_running_unset_entry(args->dnode); + bfd_profile_free(bp); + + return NB_OK; +} + +/* + * XPath: /frr-bfdd:bfdd/bfd/profile/detection-multiplier + */ +int bfdd_bfd_profile_detection_multiplier_modify(struct nb_cb_modify_args *args) +{ + struct bfd_profile *bp; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + bp = nb_running_get_entry(args->dnode, NULL, true); + bp->detection_multiplier = yang_dnode_get_uint8(args->dnode, NULL); + bfd_profile_update(bp); + + return NB_OK; +} + +/* + * XPath: /frr-bfdd:bfdd/bfd/profile/desired-transmission-interval + */ +int bfdd_bfd_profile_desired_transmission_interval_modify( + struct nb_cb_modify_args *args) +{ + struct bfd_profile *bp; + uint32_t min_tx; + + switch (args->event) { + case NB_EV_VALIDATE: + min_tx = yang_dnode_get_uint32(args->dnode, NULL); + if (min_tx < 10000 || min_tx > 60000000) + return NB_ERR_VALIDATION; + break; + + case NB_EV_PREPARE: + /* NOTHING */ + break; + + case NB_EV_APPLY: + min_tx = yang_dnode_get_uint32(args->dnode, NULL); + bp = nb_running_get_entry(args->dnode, NULL, true); + if (bp->min_tx == min_tx) + return NB_OK; + + bp->min_tx = min_tx; + bfd_profile_update(bp); + break; + + case NB_EV_ABORT: + /* NOTHING */ + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-bfdd:bfdd/bfd/profile/required-receive-interval + */ +int bfdd_bfd_profile_required_receive_interval_modify( + struct nb_cb_modify_args *args) +{ + struct bfd_profile *bp; + uint32_t min_rx; + + switch (args->event) { + case NB_EV_VALIDATE: + min_rx = yang_dnode_get_uint32(args->dnode, NULL); + if (min_rx < 10000 || min_rx > 60000000) + return NB_ERR_VALIDATION; + break; + + case NB_EV_PREPARE: + /* NOTHING */ + break; + + case NB_EV_APPLY: + min_rx = yang_dnode_get_uint32(args->dnode, NULL); + bp = nb_running_get_entry(args->dnode, NULL, true); + if (bp->min_rx == min_rx) + return NB_OK; + + bp->min_rx = min_rx; + bfd_profile_update(bp); + break; + + case NB_EV_ABORT: + /* NOTHING */ + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-bfdd:bfdd/bfd/profile/administrative-down + */ +int bfdd_bfd_profile_administrative_down_modify(struct nb_cb_modify_args *args) +{ + struct bfd_profile *bp; + bool shutdown; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + shutdown = yang_dnode_get_bool(args->dnode, NULL); + bp = nb_running_get_entry(args->dnode, NULL, true); + if (bp->admin_shutdown == shutdown) + return NB_OK; + + bp->admin_shutdown = shutdown; + bfd_profile_update(bp); + + return NB_OK; +} + +/* + * XPath: /frr-bfdd:bfdd/bfd/profile/passive-mode + */ +int bfdd_bfd_profile_passive_mode_modify(struct nb_cb_modify_args *args) +{ + struct bfd_profile *bp; + bool passive; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + passive = yang_dnode_get_bool(args->dnode, NULL); + bp = nb_running_get_entry(args->dnode, NULL, true); + if (bp->passive == passive) + return NB_OK; + + bp->passive = passive; + bfd_profile_update(bp); + + return NB_OK; +} + +/* + * XPath: /frr-bfdd:bfdd/bfd/profile/minimum-ttl + */ +int bfdd_bfd_profile_minimum_ttl_modify(struct nb_cb_modify_args *args) +{ + struct bfd_profile *bp; + uint8_t minimum_ttl; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + minimum_ttl = yang_dnode_get_uint8(args->dnode, NULL); + bp = nb_running_get_entry(args->dnode, NULL, true); + if (bp->minimum_ttl == minimum_ttl) + return NB_OK; + + bp->minimum_ttl = minimum_ttl; + bfd_profile_update(bp); + + return NB_OK; +} + +int bfdd_bfd_profile_minimum_ttl_destroy(struct nb_cb_destroy_args *args) +{ + struct bfd_profile *bp; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + bp = nb_running_get_entry(args->dnode, NULL, true); + bp->minimum_ttl = BFD_DEF_MHOP_TTL; + bfd_profile_update(bp); + + return NB_OK; +} + +/* + * XPath: /frr-bfdd:bfdd/bfd/profile/echo-mode + */ +int bfdd_bfd_profile_echo_mode_modify(struct nb_cb_modify_args *args) +{ + struct bfd_profile *bp; + bool echo; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + echo = yang_dnode_get_bool(args->dnode, NULL); + bp = nb_running_get_entry(args->dnode, NULL, true); + if (bp->echo_mode == echo) + return NB_OK; + + bp->echo_mode = echo; + bfd_profile_update(bp); + + return NB_OK; +} + +/* + * XPath: /frr-bfdd:bfdd/bfd/profile/desired-echo-echo-transmission-interval + */ +int bfdd_bfd_profile_desired_echo_transmission_interval_modify( + struct nb_cb_modify_args *args) +{ + struct bfd_profile *bp; + uint32_t min_rx; + + switch (args->event) { + case NB_EV_VALIDATE: + min_rx = yang_dnode_get_uint32(args->dnode, NULL); + if (min_rx < 10000 || min_rx > 60000000) + return NB_ERR_VALIDATION; + break; + + case NB_EV_PREPARE: + /* NOTHING */ + break; + + case NB_EV_APPLY: + min_rx = yang_dnode_get_uint32(args->dnode, NULL); + bp = nb_running_get_entry(args->dnode, NULL, true); + if (bp->min_echo_rx == min_rx) + return NB_OK; + + bp->min_echo_rx = min_rx; + bfd_profile_update(bp); + break; + + case NB_EV_ABORT: + /* NOTHING */ + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop + */ +int bfdd_bfd_sessions_single_hop_create(struct nb_cb_create_args *args) +{ + return bfd_session_create(args->event, args->dnode, args->resource, + false); +} + +int bfdd_bfd_sessions_single_hop_destroy(struct nb_cb_destroy_args *args) +{ + return bfd_session_destroy(args->event, args->dnode, false); +} + +/* + * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/source-addr + */ +int bfdd_bfd_sessions_single_hop_source_addr_modify( + struct nb_cb_modify_args *args) +{ + return NB_OK; +} + +int bfdd_bfd_sessions_single_hop_source_addr_destroy( + struct nb_cb_destroy_args *args) +{ + return NB_OK; +} + +/* + * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/profile + */ +int bfdd_bfd_sessions_single_hop_profile_modify(struct nb_cb_modify_args *args) +{ + struct bfd_session *bs; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + bs = nb_running_get_entry(args->dnode, NULL, true); + bfd_profile_apply(yang_dnode_get_string(args->dnode, NULL), bs); + + return NB_OK; +} + +int bfdd_bfd_sessions_single_hop_profile_destroy( + struct nb_cb_destroy_args *args) +{ + struct bfd_session *bs; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + bs = nb_running_get_entry(args->dnode, NULL, true); + bfd_profile_remove(bs); + + return NB_OK; +} + +/* + * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/detection-multiplier + */ +int bfdd_bfd_sessions_single_hop_detection_multiplier_modify( + struct nb_cb_modify_args *args) +{ + uint8_t detection_multiplier = yang_dnode_get_uint8(args->dnode, NULL); + struct bfd_session *bs; + + switch (args->event) { + case NB_EV_VALIDATE: + break; + + case NB_EV_PREPARE: + /* NOTHING */ + break; + + case NB_EV_APPLY: + bs = nb_running_get_entry(args->dnode, NULL, true); + bs->peer_profile.detection_multiplier = detection_multiplier; + bfd_session_apply(bs); + break; + + case NB_EV_ABORT: + /* NOTHING */ + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/desired-transmission-interval + */ +int bfdd_bfd_sessions_single_hop_desired_transmission_interval_modify( + struct nb_cb_modify_args *args) +{ + uint32_t tx_interval = yang_dnode_get_uint32(args->dnode, NULL); + struct bfd_session *bs; + + switch (args->event) { + case NB_EV_VALIDATE: + if (tx_interval < 10000 || tx_interval > 60000000) + return NB_ERR_VALIDATION; + break; + + case NB_EV_PREPARE: + /* NOTHING */ + break; + + case NB_EV_APPLY: + bs = nb_running_get_entry(args->dnode, NULL, true); + if (tx_interval == bs->timers.desired_min_tx) + return NB_OK; + + bs->peer_profile.min_tx = tx_interval; + bfd_session_apply(bs); + break; + + case NB_EV_ABORT: + /* NOTHING */ + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/required-receive-interval + */ +int bfdd_bfd_sessions_single_hop_required_receive_interval_modify( + struct nb_cb_modify_args *args) +{ + uint32_t rx_interval = yang_dnode_get_uint32(args->dnode, NULL); + struct bfd_session *bs; + + switch (args->event) { + case NB_EV_VALIDATE: + if (rx_interval < 10000 || rx_interval > 60000000) + return NB_ERR_VALIDATION; + break; + + case NB_EV_PREPARE: + /* NOTHING */ + break; + + case NB_EV_APPLY: + bs = nb_running_get_entry(args->dnode, NULL, true); + if (rx_interval == bs->timers.required_min_rx) + return NB_OK; + + bs->peer_profile.min_rx = rx_interval; + bfd_session_apply(bs); + break; + + case NB_EV_ABORT: + /* NOTHING */ + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/administrative-down + */ +int bfdd_bfd_sessions_single_hop_administrative_down_modify( + struct nb_cb_modify_args *args) +{ + bool shutdown = yang_dnode_get_bool(args->dnode, NULL); + struct bfd_session *bs; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + return NB_OK; + + case NB_EV_APPLY: + break; + + case NB_EV_ABORT: + return NB_OK; + } + + bs = nb_running_get_entry(args->dnode, NULL, true); + bs->peer_profile.admin_shutdown = shutdown; + bfd_session_apply(bs); + + return NB_OK; +} + +/* + * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/passive-mode + */ +int bfdd_bfd_sessions_single_hop_passive_mode_modify( + struct nb_cb_modify_args *args) +{ + struct bfd_session *bs; + bool passive; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + return NB_OK; + + case NB_EV_APPLY: + break; + + case NB_EV_ABORT: + return NB_OK; + } + + passive = yang_dnode_get_bool(args->dnode, NULL); + + bs = nb_running_get_entry(args->dnode, NULL, true); + bs->peer_profile.passive = passive; + bfd_session_apply(bs); + + return NB_OK; +} + +/* + * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/echo-mode + */ +int bfdd_bfd_sessions_single_hop_echo_mode_modify( + struct nb_cb_modify_args *args) +{ + bool echo = yang_dnode_get_bool(args->dnode, NULL); + struct bfd_session *bs; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + return NB_OK; + + case NB_EV_APPLY: + break; + + case NB_EV_ABORT: + return NB_OK; + } + + bs = nb_running_get_entry(args->dnode, NULL, true); + bs->peer_profile.echo_mode = echo; + bfd_session_apply(bs); + + return NB_OK; +} + +/* + * XPath: + * /frr-bfdd:bfdd/bfd/sessions/single-hop/desired-echo-transmission-interval + */ +int bfdd_bfd_sessions_single_hop_desired_echo_transmission_interval_modify( + struct nb_cb_modify_args *args) +{ + uint32_t echo_interval = yang_dnode_get_uint32(args->dnode, NULL); + struct bfd_session *bs; + + switch (args->event) { + case NB_EV_VALIDATE: + if (echo_interval < 10000 || echo_interval > 60000000) + return NB_ERR_VALIDATION; + break; + + case NB_EV_PREPARE: + /* NOTHING */ + break; + + case NB_EV_APPLY: + bs = nb_running_get_entry(args->dnode, NULL, true); + if (echo_interval == bs->timers.required_min_echo) + return NB_OK; + + bs->peer_profile.min_echo_rx = echo_interval; + bfd_session_apply(bs); + break; + + case NB_EV_ABORT: + /* NOTHING */ + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-bfdd:bfdd/bfd/sessions/multi-hop + */ +int bfdd_bfd_sessions_multi_hop_create(struct nb_cb_create_args *args) +{ + return bfd_session_create(args->event, args->dnode, args->resource, + true); +} + +int bfdd_bfd_sessions_multi_hop_destroy(struct nb_cb_destroy_args *args) +{ + return bfd_session_destroy(args->event, args->dnode, true); +} + +/* + * XPath: /frr-bfdd:bfdd/bfd/sessions/multi-hop/minimum-ttl + */ +int bfdd_bfd_sessions_multi_hop_minimum_ttl_modify( + struct nb_cb_modify_args *args) +{ + struct bfd_session *bs; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + return NB_OK; + + case NB_EV_APPLY: + break; + + case NB_EV_ABORT: + return NB_OK; + } + + bs = nb_running_get_entry(args->dnode, NULL, true); + bs->peer_profile.minimum_ttl = yang_dnode_get_uint8(args->dnode, NULL); + bfd_session_apply(bs); + + return NB_OK; +} + +int bfdd_bfd_sessions_multi_hop_minimum_ttl_destroy( + struct nb_cb_destroy_args *args) +{ + struct bfd_session *bs; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + return NB_OK; + + case NB_EV_APPLY: + break; + + case NB_EV_ABORT: + return NB_OK; + } + + bs = nb_running_get_entry(args->dnode, NULL, true); + bs->peer_profile.minimum_ttl = BFD_DEF_MHOP_TTL; + bfd_session_apply(bs); + + return NB_OK; +} diff --git a/bfdd/bfdd_nb_state.c b/bfdd/bfdd_nb_state.c new file mode 100644 index 0000000000..043f850afa --- /dev/null +++ b/bfdd/bfdd_nb_state.c @@ -0,0 +1,376 @@ +/* + * BFD daemon northbound implementation. + * + * Copyright (C) 2019 Network Device Education Foundation, Inc. ("NetDEF") + * Rafael Zalamena + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#include + +#include "lib/log.h" +#include "lib/northbound.h" + +#include "bfd.h" +#include "bfdd_nb.h" + +/* + * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop + */ +const void * +bfdd_bfd_sessions_single_hop_get_next(struct nb_cb_get_next_args *args) +{ + return bfd_session_next(args->list_entry, false); +} + +int bfdd_bfd_sessions_single_hop_get_keys(struct nb_cb_get_keys_args *args) +{ + const struct bfd_session *bs = args->list_entry; + char dstbuf[INET6_ADDRSTRLEN]; + + inet_ntop(bs->key.family, &bs->key.peer, dstbuf, sizeof(dstbuf)); + + args->keys->num = 3; + strlcpy(args->keys->key[0], dstbuf, sizeof(args->keys->key[0])); + strlcpy(args->keys->key[1], bs->key.ifname, sizeof(args->keys->key[1])); + strlcpy(args->keys->key[2], bs->key.vrfname, + sizeof(args->keys->key[2])); + + return NB_OK; +} + +const void * +bfdd_bfd_sessions_single_hop_lookup_entry(struct nb_cb_lookup_entry_args *args) +{ + const char *dest_addr = args->keys->key[0]; + const char *ifname = args->keys->key[1]; + const char *vrf = args->keys->key[2]; + struct sockaddr_any psa, lsa; + struct bfd_key bk; + + strtosa(dest_addr, &psa); + memset(&lsa, 0, sizeof(lsa)); + gen_bfd_key(&bk, &psa, &lsa, false, ifname, vrf); + + return bfd_key_lookup(bk); +} + +/* + * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/local-discriminator + */ +struct yang_data * +bfdd_bfd_sessions_single_hop_stats_local_discriminator_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct bfd_session *bs = args->list_entry; + + return yang_data_new_uint32(args->xpath, bs->discrs.my_discr); +} + +/* + * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/local-state + */ +struct yang_data *bfdd_bfd_sessions_single_hop_stats_local_state_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct bfd_session *bs = args->list_entry; + + return yang_data_new_enum(args->xpath, bs->ses_state); +} + +/* + * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/local-diagnostic + */ +struct yang_data *bfdd_bfd_sessions_single_hop_stats_local_diagnostic_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct bfd_session *bs = args->list_entry; + + return yang_data_new_enum(args->xpath, bs->local_diag); +} + +/* + * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/local-multiplier + */ +struct yang_data *bfdd_bfd_sessions_single_hop_stats_local_multiplier_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct bfd_session *bs = args->list_entry; + + return yang_data_new_int8(args->xpath, bs->detect_mult); +} + +/* + * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/remote-discriminator + */ +struct yang_data * +bfdd_bfd_sessions_single_hop_stats_remote_discriminator_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct bfd_session *bs = args->list_entry; + + if (bs->discrs.remote_discr == 0) + return NULL; + + return yang_data_new_uint32(args->xpath, bs->discrs.remote_discr); +} + +/* + * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/remote-state + */ +struct yang_data *bfdd_bfd_sessions_single_hop_stats_remote_state_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct bfd_session *bs = args->list_entry; + + return yang_data_new_enum(args->xpath, bs->ses_state); +} + +/* + * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/remote-diagnostic + */ +struct yang_data *bfdd_bfd_sessions_single_hop_stats_remote_diagnostic_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct bfd_session *bs = args->list_entry; + + return yang_data_new_enum(args->xpath, bs->remote_diag); +} + +/* + * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/remote-multiplier + */ +struct yang_data *bfdd_bfd_sessions_single_hop_stats_remote_multiplier_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct bfd_session *bs = args->list_entry; + + return yang_data_new_int8(args->xpath, bs->remote_detect_mult); +} + +/* + * XPath: + * /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/negotiated-transmission-interval + */ +struct yang_data * +bfdd_bfd_sessions_single_hop_stats_negotiated_transmission_interval_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct bfd_session *bs = args->list_entry; + + return yang_data_new_uint32(args->xpath, + bs->remote_timers.desired_min_tx); +} + +/* + * XPath: + * /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/negotiated-receive-interval + */ +struct yang_data * +bfdd_bfd_sessions_single_hop_stats_negotiated_receive_interval_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct bfd_session *bs = args->list_entry; + + return yang_data_new_uint32(args->xpath, + bs->remote_timers.required_min_rx); +} + +/* + * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/detection-mode + */ +struct yang_data *bfdd_bfd_sessions_single_hop_stats_detection_mode_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct bfd_session *bs = args->list_entry; + int detection_mode; + + /* + * Detection mode: + * 1. Async with echo + * 2. Async without echo + * 3. Demand with echo + * 4. Demand without echo + * + * TODO: support demand mode. + */ + if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO)) + detection_mode = 1; + else + detection_mode = 2; + + return yang_data_new_enum(args->xpath, detection_mode); +} + +/* + * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/last-down-time + */ +struct yang_data *bfdd_bfd_sessions_single_hop_stats_last_down_time_get_elem( + struct nb_cb_get_elem_args *args) +{ + /* + * TODO: implement me. + * + * No yang support for time elements yet. + */ + return NULL; +} + +/* + * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/last-up-time + */ +struct yang_data *bfdd_bfd_sessions_single_hop_stats_last_up_time_get_elem( + struct nb_cb_get_elem_args *args) +{ + /* + * TODO: implement me. + * + * No yang support for time elements yet. + */ + return NULL; +} + +/* + * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/session-down-count + */ +struct yang_data * +bfdd_bfd_sessions_single_hop_stats_session_down_count_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct bfd_session *bs = args->list_entry; + + return yang_data_new_uint64(args->xpath, bs->stats.session_down); +} + +/* + * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/session-up-count + */ +struct yang_data *bfdd_bfd_sessions_single_hop_stats_session_up_count_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct bfd_session *bs = args->list_entry; + + return yang_data_new_uint64(args->xpath, bs->stats.session_up); +} + +/* + * XPath: + * /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/control-packet-input-count + */ +struct yang_data * +bfdd_bfd_sessions_single_hop_stats_control_packet_input_count_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct bfd_session *bs = args->list_entry; + + return yang_data_new_uint64(args->xpath, bs->stats.rx_ctrl_pkt); +} + +/* + * XPath: + * /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/control-packet-output-count + */ +struct yang_data * +bfdd_bfd_sessions_single_hop_stats_control_packet_output_count_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct bfd_session *bs = args->list_entry; + + return yang_data_new_uint64(args->xpath, bs->stats.tx_ctrl_pkt); +} + +/* + * XPath: + * /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/negotiated-echo-transmission-interval + */ +struct yang_data * +bfdd_bfd_sessions_single_hop_stats_negotiated_echo_transmission_interval_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct bfd_session *bs = args->list_entry; + + return yang_data_new_uint32(args->xpath, + bs->remote_timers.required_min_echo); +} + +/* + * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/echo-packet-input-count + */ +struct yang_data * +bfdd_bfd_sessions_single_hop_stats_echo_packet_input_count_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct bfd_session *bs = args->list_entry; + + return yang_data_new_uint64(args->xpath, bs->stats.rx_echo_pkt); +} + +/* + * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/stats/echo-packet-output-count + */ +struct yang_data * +bfdd_bfd_sessions_single_hop_stats_echo_packet_output_count_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct bfd_session *bs = args->list_entry; + + return yang_data_new_uint64(args->xpath, bs->stats.tx_echo_pkt); +} + +/* + * XPath: /frr-bfdd:bfdd/bfd/sessions/multi-hop + */ +const void * +bfdd_bfd_sessions_multi_hop_get_next(struct nb_cb_get_next_args *args) +{ + return bfd_session_next(args->list_entry, true); +} + +int bfdd_bfd_sessions_multi_hop_get_keys(struct nb_cb_get_keys_args *args) +{ + const struct bfd_session *bs = args->list_entry; + char dstbuf[INET6_ADDRSTRLEN], srcbuf[INET6_ADDRSTRLEN]; + + inet_ntop(bs->key.family, &bs->key.peer, dstbuf, sizeof(dstbuf)); + inet_ntop(bs->key.family, &bs->key.local, srcbuf, sizeof(srcbuf)); + + args->keys->num = 4; + strlcpy(args->keys->key[0], srcbuf, sizeof(args->keys->key[0])); + strlcpy(args->keys->key[1], dstbuf, sizeof(args->keys->key[1])); + strlcpy(args->keys->key[2], bs->key.ifname, sizeof(args->keys->key[2])); + strlcpy(args->keys->key[3], bs->key.vrfname, + sizeof(args->keys->key[3])); + + return NB_OK; +} + +const void * +bfdd_bfd_sessions_multi_hop_lookup_entry(struct nb_cb_lookup_entry_args *args) +{ + const char *source_addr = args->keys->key[0]; + const char *dest_addr = args->keys->key[1]; + const char *ifname = args->keys->key[2]; + const char *vrf = args->keys->key[3]; + struct sockaddr_any psa, lsa; + struct bfd_key bk; + + strtosa(dest_addr, &psa); + strtosa(source_addr, &lsa); + gen_bfd_key(&bk, &psa, &lsa, true, ifname, vrf); + + return bfd_key_lookup(bk); +} diff --git a/bfdd/bfdd_vty.c b/bfdd/bfdd_vty.c index 4efdd817c1..eea493a501 100644 --- a/bfdd/bfdd_vty.c +++ b/bfdd/bfdd_vty.c @@ -23,6 +23,7 @@ #include "lib/command.h" #include "lib/json.h" #include "lib/log.h" +#include "lib/northbound_cli.h" #include "lib/vty.h" #include "bfd.h" @@ -34,8 +35,6 @@ /* * Commands help string definitions. */ -#define PEER_STR "Configure peer\n" -#define INTERFACE_NAME_STR "Configure interface name to use\n" #define PEER_IPV4_STR "IPv4 peer address\n" #define PEER_IPV6_STR "IPv6 peer address\n" #define MHOP_STR "Configure multihop\n" @@ -43,16 +42,10 @@ #define LOCAL_IPV4_STR "IPv4 local address\n" #define LOCAL_IPV6_STR "IPv6 local address\n" #define LOCAL_INTF_STR "Configure local interface name to use\n" -#define VRF_STR "Configure VRF\n" -#define VRF_NAME_STR "Configure VRF name\n" /* * Prototypes */ -static int bfdd_write_config(struct vty *vty); -static int bfdd_peer_write_config(struct vty *vty); -static void _bfdd_peer_write_config(struct vty *vty, struct bfd_session *bs); -static void _bfdd_peer_write_config_iter(struct hash_bucket *hb, void *arg); static int bfd_configure_peer(struct bfd_peer_cfg *bpc, bool mhop, const struct sockaddr_any *peer, const struct sockaddr_any *local, @@ -64,7 +57,7 @@ static struct json_object *__display_peer_json(struct bfd_session *bs); static struct json_object *_peer_json_header(struct bfd_session *bs); static void _display_peer_json(struct vty *vty, struct bfd_session *bs); static void _display_peer(struct vty *vty, struct bfd_session *bs); -static void _display_all_peers(struct vty *vty, bool use_json); +static void _display_all_peers(struct vty *vty, char *vrfname, bool use_json); static void _display_peer_iter(struct hash_bucket *hb, void *arg); static void _display_peer_json_iter(struct hash_bucket *hb, void *arg); static void _display_peer_counter(struct vty *vty, struct bfd_session *bs); @@ -72,302 +65,13 @@ static struct json_object *__display_peer_counters_json(struct bfd_session *bs); static void _display_peer_counters_json(struct vty *vty, struct bfd_session *bs); static void _display_peer_counter_iter(struct hash_bucket *hb, void *arg); static void _display_peer_counter_json_iter(struct hash_bucket *hb, void *arg); -static void _display_peers_counter(struct vty *vty, bool use_json); +static void _display_peers_counter(struct vty *vty, char *vrfname, bool use_json); static struct bfd_session * _find_peer_or_error(struct vty *vty, int argc, struct cmd_token **argv, const char *label, const char *peer_str, const char *local_str, const char *ifname, const char *vrfname); -/* - * Commands definition. - */ -DEFUN_NOSH(bfd_enter, bfd_enter_cmd, "bfd", "Configure BFD peers\n") -{ - vty->node = BFD_NODE; - return CMD_SUCCESS; -} - -DEFUN_NOSH( - bfd_peer_enter, bfd_peer_enter_cmd, - "peer [{[multihop] local-address |interface IFNAME|vrf NAME}]", - PEER_STR PEER_IPV4_STR PEER_IPV6_STR - MHOP_STR - LOCAL_STR LOCAL_IPV4_STR LOCAL_IPV6_STR - INTERFACE_STR - LOCAL_INTF_STR - VRF_STR VRF_NAME_STR) -{ - bool mhop; - int idx; - struct bfd_session *bs; - const char *peer, *ifname, *local, *vrfname; - struct bfd_peer_cfg bpc; - struct sockaddr_any psa, lsa, *lsap; - char errormsg[128]; - - vrfname = peer = ifname = local = NULL; - - /* Gather all provided information. */ - peer = argv[1]->arg; - - idx = 0; - mhop = argv_find(argv, argc, "multihop", &idx); - - idx = 0; - if (argv_find(argv, argc, "interface", &idx)) - ifname = argv[idx + 1]->arg; - - idx = 0; - if (argv_find(argv, argc, "local-address", &idx)) - local = argv[idx + 1]->arg; - - idx = 0; - if (argv_find(argv, argc, "vrf", &idx)) - vrfname = argv[idx + 1]->arg; - - if (vrfname && ifname) { - vty_out(vty, "%% VRF is not mixable with interface\n"); - return CMD_WARNING_CONFIG_FAILED; - } - if (vrfname && !mhop) { - vty_out(vty, "%% VRF only applies with multihop.\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - strtosa(peer, &psa); - if (local) { - strtosa(local, &lsa); - lsap = &lsa; - } else - lsap = NULL; - - if (bfd_configure_peer(&bpc, mhop, &psa, lsap, ifname, vrfname, - errormsg, sizeof(errormsg)) - != 0) { - vty_out(vty, "%% Invalid peer configuration: %s\n", errormsg); - return CMD_WARNING_CONFIG_FAILED; - } - - bs = bs_peer_find(&bpc); - if (bs == NULL) { - bs = ptm_bfd_sess_new(&bpc); - if (bs == NULL) { - vty_out(vty, "%% Failed to add peer.\n"); - return CMD_WARNING_CONFIG_FAILED; - } - } - - if (!BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG)) { - if (bs->refcount) - vty_out(vty, "%% session peer is now configurable via bfd daemon.\n"); - BFD_SET_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG); - } - - VTY_PUSH_CONTEXT(BFD_PEER_NODE, bs); - - return CMD_SUCCESS; -} - -DEFPY(bfd_peer_detectmultiplier, bfd_peer_detectmultiplier_cmd, - "detect-multiplier (2-255)$multiplier", - "Configure peer detection multiplier\n" - "Configure peer detection multiplier value\n") -{ - struct bfd_session *bs; - - bs = VTY_GET_CONTEXT(bfd_session); - if (bs->detect_mult == multiplier) - return CMD_SUCCESS; - - bs->detect_mult = multiplier; - - return CMD_SUCCESS; -} - -DEFPY(bfd_peer_recvinterval, bfd_peer_recvinterval_cmd, - "receive-interval (10-60000)$interval", - "Configure peer receive interval\n" - "Configure peer receive interval value in milliseconds\n") -{ - struct bfd_session *bs; - - bs = VTY_GET_CONTEXT(bfd_session); - if (bs->timers.required_min_rx == (uint32_t)(interval * 1000)) - return CMD_SUCCESS; - - bs->timers.required_min_rx = interval * 1000; - bfd_set_polling(bs); - - return CMD_SUCCESS; -} - -DEFPY(bfd_peer_txinterval, bfd_peer_txinterval_cmd, - "transmit-interval (10-60000)$interval", - "Configure peer transmit interval\n" - "Configure peer transmit interval value in milliseconds\n") -{ - struct bfd_session *bs; - - bs = VTY_GET_CONTEXT(bfd_session); - if (bs->timers.desired_min_tx == (uint32_t)(interval * 1000)) - return CMD_SUCCESS; - - bs->timers.desired_min_tx = interval * 1000; - bfd_set_polling(bs); - - return CMD_SUCCESS; -} - -DEFPY(bfd_peer_echointerval, bfd_peer_echointerval_cmd, - "echo-interval (10-60000)$interval", - "Configure peer echo interval\n" - "Configure peer echo interval value in milliseconds\n") -{ - struct bfd_session *bs; - - bs = VTY_GET_CONTEXT(bfd_session); - if (bs->timers.required_min_echo == (uint32_t)(interval * 1000)) - return CMD_SUCCESS; - - bs->timers.required_min_echo = interval * 1000; - - return CMD_SUCCESS; -} - -DEFPY(bfd_peer_shutdown, bfd_peer_shutdown_cmd, "[no] shutdown", - NO_STR "Disable BFD peer") -{ - struct bfd_session *bs; - - bs = VTY_GET_CONTEXT(bfd_session); - if (no) { - if (!BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN)) - return CMD_SUCCESS; - - BFD_UNSET_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN); - - /* Change and notify state change. */ - bs->ses_state = PTM_BFD_DOWN; - control_notify(bs); - - /* Enable all timers. */ - bfd_recvtimer_update(bs); - bfd_xmttimer_update(bs, bs->xmt_TO); - if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO)) { - bfd_echo_recvtimer_update(bs); - bfd_echo_xmttimer_update(bs, bs->echo_xmt_TO); - } - } else { - if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN)) - return CMD_SUCCESS; - - BFD_SET_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN); - - /* Disable all events. */ - bfd_recvtimer_delete(bs); - bfd_echo_recvtimer_delete(bs); - bfd_xmttimer_delete(bs); - bfd_echo_xmttimer_delete(bs); - - /* Change and notify state change. */ - bs->ses_state = PTM_BFD_ADM_DOWN; - control_notify(bs); - - ptm_bfd_snd(bs, 0); - } - - return CMD_SUCCESS; -} - -DEFPY(bfd_peer_echo, bfd_peer_echo_cmd, "[no] echo-mode", - NO_STR "Configure echo mode\n") -{ - struct bfd_session *bs; - - bs = VTY_GET_CONTEXT(bfd_session); - if (no) { - if (!BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO)) - return CMD_SUCCESS; - - BFD_UNSET_FLAG(bs->flags, BFD_SESS_FLAG_ECHO); - ptm_bfd_echo_stop(bs); - } else { - if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO)) - return CMD_SUCCESS; - - BFD_SET_FLAG(bs->flags, BFD_SESS_FLAG_ECHO); - /* Apply setting immediately. */ - if (!BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN)) - bs_echo_timer_handler(bs); - } - - return CMD_SUCCESS; -} - -DEFPY(bfd_peer_label, bfd_peer_label_cmd, "label WORD$label", - "Register peer label\n" - "Register peer label identification\n") -{ - struct bfd_session *bs; - - /* Validate label length. */ - if (strlen(label) >= MAXNAMELEN) { - vty_out(vty, "%% Label name is too long\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - bs = VTY_GET_CONTEXT(bfd_session); - if (bfd_session_update_label(bs, label) == -1) { - vty_out(vty, "%% Failed to update peer label.\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - return CMD_SUCCESS; -} - -DEFPY(bfd_no_peer, bfd_no_peer_cmd, - "no peer $peer [{multihop|local-address $local|interface IFNAME$ifname|vrf NAME$vrfname}]", - NO_STR - PEER_STR PEER_IPV4_STR PEER_IPV6_STR - MHOP_STR - LOCAL_STR LOCAL_IPV4_STR LOCAL_IPV6_STR - INTERFACE_STR - LOCAL_INTF_STR - VRF_STR VRF_NAME_STR) -{ - int idx; - bool mhop; - struct bfd_peer_cfg bpc; - struct sockaddr_any psa, lsa, *lsap; - char errormsg[128]; - - strtosa(peer_str, &psa); - if (local) { - strtosa(local_str, &lsa); - lsap = &lsa; - } else { - lsap = NULL; - } - - idx = 0; - mhop = argv_find(argv, argc, "multihop", &idx); - - if (bfd_configure_peer(&bpc, mhop, &psa, lsap, ifname, vrfname, - errormsg, sizeof(errormsg)) - != 0) { - vty_out(vty, "%% Invalid peer configuration: %s\n", errormsg); - return CMD_WARNING_CONFIG_FAILED; - } - - if (ptm_bfd_ses_del(&bpc) != 0) { - vty_out(vty, "%% Failed to remove peer.\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - return CMD_SUCCESS; -} - /* * Show commands helper functions @@ -380,7 +84,7 @@ static void _display_peer_header(struct vty *vty, struct bfd_session *bs) inet_ntop(bs->key.family, &bs->key.peer, addr_buf, sizeof(addr_buf))); - if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) + if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) vty_out(vty, " multihop"); if (memcmp(&bs->key.local, &zero_addr, sizeof(bs->key.local))) @@ -407,6 +111,12 @@ static void _display_peer(struct vty *vty, struct bfd_session *bs) vty_out(vty, "\t\tID: %u\n", bs->discrs.my_discr); vty_out(vty, "\t\tRemote ID: %u\n", bs->discrs.remote_discr); + if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_PASSIVE)) + vty_out(vty, "\t\tPassive mode\n"); + else + vty_out(vty, "\t\tActive mode\n"); + if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) + vty_out(vty, "\t\tMinimum TTL: %d\n", bs->mh_ttl); vty_out(vty, "\t\tStatus: "); switch (bs->ses_state) { @@ -438,21 +148,27 @@ static void _display_peer(struct vty *vty, struct bfd_session *bs) vty_out(vty, "\t\tDiagnostics: %s\n", diag2str(bs->local_diag)); vty_out(vty, "\t\tRemote diagnostics: %s\n", diag2str(bs->remote_diag)); + vty_out(vty, "\t\tPeer Type: %s\n", + CHECK_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG) ? "configured" : "dynamic"); vty_out(vty, "\t\tLocal timers:\n"); - vty_out(vty, "\t\t\tReceive interval: %" PRIu32 "ms\n", + vty_out(vty, "\t\t\tDetect-multiplier: %u\n", + bs->detect_mult); + vty_out(vty, "\t\t\tReceive interval: %ums\n", bs->timers.required_min_rx / 1000); - vty_out(vty, "\t\t\tTransmission interval: %" PRIu32 "ms\n", + vty_out(vty, "\t\t\tTransmission interval: %ums\n", bs->timers.desired_min_tx / 1000); - vty_out(vty, "\t\t\tEcho transmission interval: %" PRIu32 "ms\n", + vty_out(vty, "\t\t\tEcho transmission interval: %ums\n", bs->timers.required_min_echo / 1000); vty_out(vty, "\t\tRemote timers:\n"); - vty_out(vty, "\t\t\tReceive interval: %" PRIu32 "ms\n", + vty_out(vty, "\t\t\tDetect-multiplier: %u\n", + bs->remote_detect_mult); + vty_out(vty, "\t\t\tReceive interval: %ums\n", bs->remote_timers.required_min_rx / 1000); - vty_out(vty, "\t\t\tTransmission interval: %" PRIu32 "ms\n", + vty_out(vty, "\t\t\tTransmission interval: %ums\n", bs->remote_timers.desired_min_tx / 1000); - vty_out(vty, "\t\t\tEcho transmission interval: %" PRIu32 "ms\n", + vty_out(vty, "\t\t\tEcho transmission interval: %ums\n", bs->remote_timers.required_min_echo / 1000); vty_out(vty, "\n"); @@ -493,6 +209,10 @@ static struct json_object *__display_peer_json(struct bfd_session *bs) json_object_int_add(jo, "id", bs->discrs.my_discr); json_object_int_add(jo, "remote-id", bs->discrs.remote_discr); + json_object_boolean_add(jo, "passive-mode", + CHECK_FLAG(bs->flags, BFD_SESS_FLAG_PASSIVE)); + if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) + json_object_int_add(jo, "minimum-ttl", bs->mh_ttl); switch (bs->ses_state) { case PTM_BFD_ADM_DOWN: @@ -525,18 +245,22 @@ static struct json_object *__display_peer_json(struct bfd_session *bs) bs->timers.required_min_rx / 1000); json_object_int_add(jo, "transmit-interval", bs->timers.desired_min_tx / 1000); - if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO)) + if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO)) json_object_int_add(jo, "echo-interval", bs->timers.required_min_echo / 1000); else json_object_int_add(jo, "echo-interval", 0); + json_object_int_add(jo, "detect-multiplier", bs->detect_mult); + json_object_int_add(jo, "remote-receive-interval", bs->remote_timers.required_min_rx / 1000); json_object_int_add(jo, "remote-transmit-interval", bs->remote_timers.desired_min_tx / 1000); json_object_int_add(jo, "remote-echo-interval", bs->remote_timers.required_min_echo / 1000); + json_object_int_add(jo, "remote-detect-multiplier", + bs->remote_detect_mult); return jo; } @@ -549,40 +273,72 @@ static void _display_peer_json(struct vty *vty, struct bfd_session *bs) json_object_free(jo); } +struct bfd_vrf_tuple { + const char *vrfname; + struct vty *vty; + struct json_object *jo; +}; + static void _display_peer_iter(struct hash_bucket *hb, void *arg) { - struct vty *vty = arg; + struct bfd_vrf_tuple *bvt = (struct bfd_vrf_tuple *)arg; + struct vty *vty; struct bfd_session *bs = hb->data; + if (!bvt) + return; + vty = bvt->vty; + + if (bvt->vrfname) { + if (!bs->key.vrfname[0] || + !strmatch(bs->key.vrfname, bvt->vrfname)) + return; + } _display_peer(vty, bs); } static void _display_peer_json_iter(struct hash_bucket *hb, void *arg) { - struct json_object *jo = arg, *jon = NULL; + struct bfd_vrf_tuple *bvt = (struct bfd_vrf_tuple *)arg; + struct json_object *jo, *jon = NULL; struct bfd_session *bs = hb->data; + if (!bvt) + return; + jo = bvt->jo; + + if (bvt->vrfname) { + if (!bs->key.vrfname[0] || + !strmatch(bs->key.vrfname, bvt->vrfname)) + return; + } + jon = __display_peer_json(bs); if (jon == NULL) { - log_warning("%s: not enough memory", __func__); + zlog_warn("%s: not enough memory", __func__); return; } json_object_array_add(jo, jon); } -static void _display_all_peers(struct vty *vty, bool use_json) +static void _display_all_peers(struct vty *vty, char *vrfname, bool use_json) { struct json_object *jo; + struct bfd_vrf_tuple bvt = {0}; + + bvt.vrfname = vrfname; if (!use_json) { + bvt.vty = vty; vty_out(vty, "BFD Peers:\n"); - bfd_id_iterate(_display_peer_iter, vty); + bfd_id_iterate(_display_peer_iter, &bvt); return; } jo = json_object_new_array(); - bfd_id_iterate(_display_peer_json_iter, jo); + bvt.jo = jo; + bfd_id_iterate(_display_peer_json_iter, &bvt); vty_out(vty, "%s\n", json_object_to_json_string_ext(jo, 0)); json_object_free(jo); @@ -634,38 +390,148 @@ static void _display_peer_counters_json(struct vty *vty, struct bfd_session *bs) static void _display_peer_counter_iter(struct hash_bucket *hb, void *arg) { - struct vty *vty = arg; + struct bfd_vrf_tuple *bvt = arg; + struct vty *vty; struct bfd_session *bs = hb->data; + if (!bvt) + return; + vty = bvt->vty; + + if (bvt->vrfname) { + if (!bs->key.vrfname[0] || + !strmatch(bs->key.vrfname, bvt->vrfname)) + return; + } + _display_peer_counter(vty, bs); } static void _display_peer_counter_json_iter(struct hash_bucket *hb, void *arg) { - struct json_object *jo = arg, *jon = NULL; + struct json_object *jo, *jon = NULL; struct bfd_session *bs = hb->data; + struct bfd_vrf_tuple *bvt = arg; + + if (!bvt) + return; + jo = bvt->jo; + + if (bvt->vrfname) { + if (!bs->key.vrfname[0] || + !strmatch(bs->key.vrfname, bvt->vrfname)) + return; + } jon = __display_peer_counters_json(bs); if (jon == NULL) { - log_warning("%s: not enough memory", __func__); + zlog_warn("%s: not enough memory", __func__); return; } json_object_array_add(jo, jon); } -static void _display_peers_counter(struct vty *vty, bool use_json) +static void _display_peers_counter(struct vty *vty, char *vrfname, bool use_json) { struct json_object *jo; + struct bfd_vrf_tuple bvt = {0}; + bvt.vrfname = vrfname; if (!use_json) { + bvt.vty = vty; vty_out(vty, "BFD Peers:\n"); - bfd_id_iterate(_display_peer_counter_iter, vty); + bfd_id_iterate(_display_peer_counter_iter, &bvt); + return; + } + + jo = json_object_new_array(); + bvt.jo = jo; + bfd_id_iterate(_display_peer_counter_json_iter, &bvt); + + vty_out(vty, "%s\n", json_object_to_json_string_ext(jo, 0)); + json_object_free(jo); +} + +static void _clear_peer_counter(struct bfd_session *bs) +{ + /* Clear only pkt stats, intention is not to loose system + events counters */ + bs->stats.rx_ctrl_pkt = 0; + bs->stats.tx_ctrl_pkt = 0; + bs->stats.rx_echo_pkt = 0; + bs->stats.tx_echo_pkt = 0; +} + +static void _display_peer_brief(struct vty *vty, struct bfd_session *bs) +{ + char addr_buf[INET6_ADDRSTRLEN]; + + if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) { + vty_out(vty, "%-10u", bs->discrs.my_discr); + inet_ntop(bs->key.family, &bs->key.local, addr_buf, sizeof(addr_buf)); + vty_out(vty, " %-40s", addr_buf); + inet_ntop(bs->key.family, &bs->key.peer, addr_buf, sizeof(addr_buf)); + vty_out(vty, " %-40s", addr_buf); + vty_out(vty, "%-15s\n", state_list[bs->ses_state].str); + } else { + vty_out(vty, "%-10u", bs->discrs.my_discr); + vty_out(vty, " %-40s", satostr(&bs->local_address)); + inet_ntop(bs->key.family, &bs->key.peer, addr_buf, sizeof(addr_buf)); + vty_out(vty, " %-40s", addr_buf); + + vty_out(vty, "%-15s\n", state_list[bs->ses_state].str); + } +} + +static void _display_peer_brief_iter(struct hash_bucket *hb, void *arg) +{ + struct bfd_vrf_tuple *bvt = arg; + struct vty *vty; + struct bfd_session *bs = hb->data; + + if (!bvt) + return; + vty = bvt->vty; + + if (bvt->vrfname) { + if (!bs->key.vrfname[0] || + !strmatch(bs->key.vrfname, bvt->vrfname)) + return; + } + + _display_peer_brief(vty, bs); +} + +static void _display_peers_brief(struct vty *vty, const char *vrfname, bool use_json) +{ + struct json_object *jo; + struct bfd_vrf_tuple bvt = {0}; + + bvt.vrfname = vrfname; + + if (!use_json) { + bvt.vty = vty; + + vty_out(vty, "Session count: %lu\n", bfd_get_session_count()); + vty_out(vty, "%-10s", "SessionId"); + vty_out(vty, " %-40s", "LocalAddress"); + vty_out(vty, " %-40s", "PeerAddress"); + vty_out(vty, "%-15s\n", "Status"); + + vty_out(vty, "%-10s", "========="); + vty_out(vty, " %-40s", "============"); + vty_out(vty, " %-40s", "==========="); + vty_out(vty, "%-15s\n", "======"); + + bfd_id_iterate(_display_peer_brief_iter, &bvt); return; } jo = json_object_new_array(); - bfd_id_iterate(_display_peer_counter_json_iter, jo); + bvt.jo = jo; + + bfd_id_iterate(_display_peer_json_iter, &bvt); vty_out(vty, "%s\n", json_object_to_json_string_ext(jo, 0)); json_object_free(jo); @@ -734,30 +600,37 @@ _find_peer_or_error(struct vty *vty, int argc, struct cmd_token **argv, /* * Show commands. */ -DEFPY(bfd_show_peers, bfd_show_peers_cmd, "show bfd peers [json]", +DEFPY(bfd_show_peers, bfd_show_peers_cmd, "show bfd [vrf NAME] peers [json]", SHOW_STR "Bidirection Forwarding Detection\n" + VRF_CMD_HELP_STR "BFD peers status\n" JSON_STR) { - _display_all_peers(vty, use_json(argc, argv)); + char *vrf_name = NULL; + int idx_vrf = 0; + + if (argv_find(argv, argc, "vrf", &idx_vrf)) + vrf_name = argv[idx_vrf + 1]->arg; + + _display_all_peers(vty, vrf_name, use_json(argc, argv)); return CMD_SUCCESS; } DEFPY(bfd_show_peer, bfd_show_peer_cmd, - "show bfd peer $peer [{multihop|local-address $local|interface IFNAME$ifname|vrf NAME$vrfname}]> [json]", + "show bfd [vrf NAME$vrf_name] peer $peer [{multihop|local-address $local|interface IFNAME$ifname}]> [json]", SHOW_STR "Bidirection Forwarding Detection\n" + VRF_CMD_HELP_STR "BFD peers status\n" "Peer label\n" PEER_IPV4_STR PEER_IPV6_STR MHOP_STR LOCAL_STR - LOCAL_IPV4_STR LOCAL_IPV6_STR INTERFACE_STR LOCAL_INTF_STR VRF_STR - VRF_NAME_STR JSON_STR) + LOCAL_IPV4_STR LOCAL_IPV6_STR INTERFACE_STR LOCAL_INTF_STR JSON_STR) { struct bfd_session *bs; /* Look up the BFD peer. */ bs = _find_peer_or_error(vty, argc, argv, label, peer_str, local_str, - ifname, vrfname); + ifname, vrf_name); if (bs == NULL) return CMD_WARNING_CONFIG_FAILED; @@ -772,9 +645,10 @@ DEFPY(bfd_show_peer, bfd_show_peer_cmd, } DEFPY(bfd_show_peer_counters, bfd_show_peer_counters_cmd, - "show bfd peer $peer [{multihop|local-address $local|interface IFNAME$ifname|vrf NAME$vrfname}]> counters [json]", + "show bfd [vrf NAME$vrf_name] peer $peer [{multihop|local-address $local|interface IFNAME$ifname}]> counters [json]", SHOW_STR "Bidirection Forwarding Detection\n" + VRF_CMD_HELP_STR "BFD peers status\n" "Peer label\n" PEER_IPV4_STR @@ -785,8 +659,6 @@ DEFPY(bfd_show_peer_counters, bfd_show_peer_counters_cmd, LOCAL_IPV6_STR INTERFACE_STR LOCAL_INTF_STR - VRF_STR - VRF_NAME_STR "Show BFD peer counters information\n" JSON_STR) { @@ -794,7 +666,7 @@ DEFPY(bfd_show_peer_counters, bfd_show_peer_counters_cmd, /* Look up the BFD peer. */ bs = _find_peer_or_error(vty, argc, argv, label, peer_str, local_str, - ifname, vrfname); + ifname, vrf_name); if (bs == NULL) return CMD_WARNING_CONFIG_FAILED; @@ -807,18 +679,110 @@ DEFPY(bfd_show_peer_counters, bfd_show_peer_counters_cmd, } DEFPY(bfd_show_peers_counters, bfd_show_peers_counters_cmd, - "show bfd peers counters [json]", + "show bfd [vrf NAME] peers counters [json]", SHOW_STR "Bidirection Forwarding Detection\n" + VRF_CMD_HELP_STR "BFD peers status\n" "Show BFD peer counters information\n" JSON_STR) { - _display_peers_counter(vty, use_json(argc, argv)); + char *vrf_name = NULL; + int idx_vrf = 0; + + if (argv_find(argv, argc, "vrf", &idx_vrf)) + vrf_name = argv[idx_vrf + 1]->arg; + + _display_peers_counter(vty, vrf_name, use_json(argc, argv)); + + return CMD_SUCCESS; +} + +DEFPY(bfd_clear_peer_counters, bfd_clear_peer_counters_cmd, + "clear bfd [vrf ] peer $peer [{multihop|local-address $local|interface IFNAME$ifname}]> counters", + SHOW_STR + "Bidirection Forwarding Detection\n" + VRF_CMD_HELP_STR + "BFD peers status\n" + "Peer label\n" + PEER_IPV4_STR + PEER_IPV6_STR + MHOP_STR + LOCAL_STR + LOCAL_IPV4_STR + LOCAL_IPV6_STR + INTERFACE_STR + LOCAL_INTF_STR + "clear BFD peer counters information\n") +{ + struct bfd_session *bs; + + /* Look up the BFD peer. */ + bs = _find_peer_or_error(vty, argc, argv, label, peer_str, local_str, + ifname, vrfname); + if (bs == NULL) + return CMD_WARNING_CONFIG_FAILED; + + _clear_peer_counter(bs); + + return CMD_SUCCESS; +} + +DEFPY(bfd_show_peers_brief, bfd_show_peers_brief_cmd, + "show bfd [vrf ] peers brief [json]", + SHOW_STR + "Bidirection Forwarding Detection\n" + VRF_CMD_HELP_STR + "BFD peers status\n" + "Show BFD peer information in tabular form\n" + JSON_STR) +{ + char *vrf_name = NULL; + int idx_vrf = 0; + + if (argv_find(argv, argc, "vrf", &idx_vrf)) + vrf_name = argv[idx_vrf + 1]->arg; + + _display_peers_brief(vty, vrf_name, use_json(argc, argv)); return CMD_SUCCESS; } +DEFPY( + bfd_debug_peer, bfd_debug_peer_cmd, + "[no] debug bfd peer", + NO_STR + DEBUG_STR + "Bidirection Forwarding Detection\n" + "Peer events debugging\n") +{ + bglobal.debug_peer_event = !no; + return CMD_SUCCESS; +} + +DEFPY( + bfd_debug_zebra, bfd_debug_zebra_cmd, + "[no] debug bfd zebra", + NO_STR + DEBUG_STR + "Bidirection Forwarding Detection\n" + "Zebra events debugging\n") +{ + bglobal.debug_zebra = !no; + return CMD_SUCCESS; +} + +DEFPY( + bfd_debug_network, bfd_debug_network_cmd, + "[no] debug bfd network", + NO_STR + DEBUG_STR + "Bidirection Forwarding Detection\n" + "Network layer debugging\n") +{ + bglobal.debug_network = !no; + return CMD_SUCCESS; +} /* * Function definitions. @@ -844,7 +808,7 @@ static int bfd_configure_peer(struct bfd_peer_cfg *bpc, bool mhop, memset(bpc, 0, sizeof(*bpc)); /* Defaults */ - bpc->bpc_shutdown = true; + bpc->bpc_shutdown = false; bpc->bpc_detectmultiplier = BPC_DEF_DETECTMULTIPLIER; bpc->bpc_recvinterval = BPC_DEF_RECEIVEINTERVAL; bpc->bpc_txinterval = BPC_DEF_TRANSMITINTERVAL; @@ -892,12 +856,6 @@ static int bfd_configure_peer(struct bfd_peer_cfg *bpc, bool mhop, /* Handle interface specification configuration. */ if (ifname) { - if (bpc->bpc_mhop) { - snprintf(ebuf, ebuflen, - "multihop doesn't accept interface names"); - return -1; - } - bpc->bpc_has_localif = true; if (strlcpy(bpc->bpc_localif, ifname, sizeof(bpc->bpc_localif)) > sizeof(bpc->bpc_localif)) { @@ -914,64 +872,13 @@ static int bfd_configure_peer(struct bfd_peer_cfg *bpc, bool mhop, snprintf(ebuf, ebuflen, "vrf name too long"); return -1; } + } else { + bpc->bpc_has_vrfname = true; + strlcpy(bpc->bpc_vrfname, VRF_DEFAULT_NAME, sizeof(bpc->bpc_vrfname)); } return 0; } -static int bfdd_write_config(struct vty *vty) -{ - vty_out(vty, "bfd\n"); - vty_out(vty, "!\n"); - return 0; -} - -static void _bfdd_peer_write_config(struct vty *vty, struct bfd_session *bs) -{ - char addr_buf[INET6_ADDRSTRLEN]; - - vty_out(vty, " peer %s", - inet_ntop(bs->key.family, &bs->key.peer, addr_buf, - sizeof(addr_buf))); - - if (bs->key.mhop) - vty_out(vty, " multihop"); - - if (memcmp(&bs->key.local, &zero_addr, sizeof(bs->key.local))) - vty_out(vty, " local-address %s", - inet_ntop(bs->key.family, &bs->key.local, addr_buf, - sizeof(addr_buf))); - - if (bs->key.vrfname[0]) - vty_out(vty, " vrf %s", bs->key.vrfname); - if (bs->key.ifname[0]) - vty_out(vty, " interface %s", bs->key.ifname); - vty_out(vty, "\n"); - - if (bs->sock == -1) - vty_out(vty, - " ! vrf, interface or local-address doesn't exist\n"); - - if (bs->detect_mult != BPC_DEF_DETECTMULTIPLIER) - vty_out(vty, " detect-multiplier %d\n", bs->detect_mult); - if (bs->timers.required_min_rx != (BPC_DEF_RECEIVEINTERVAL * 1000)) - vty_out(vty, " receive-interval %" PRIu32 "\n", - bs->timers.required_min_rx / 1000); - if (bs->timers.desired_min_tx != (BPC_DEF_TRANSMITINTERVAL * 1000)) - vty_out(vty, " transmit-interval %" PRIu32 "\n", - bs->timers.desired_min_tx / 1000); - if (bs->timers.required_min_echo != (BPC_DEF_ECHOINTERVAL * 1000)) - vty_out(vty, " echo-interval %" PRIu32 "\n", - bs->timers.required_min_echo / 1000); - if (bs->pl) - vty_out(vty, " label %s\n", bs->pl->pl_label); - if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO)) - vty_out(vty, " echo-mode\n"); - - vty_out(vty, " %sshutdown\n", - BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN) ? "" : "no "); - - vty_out(vty, " !\n"); -} DEFUN_NOSH(show_debugging_bfd, show_debugging_bfd_cmd, @@ -981,63 +888,86 @@ DEFUN_NOSH(show_debugging_bfd, "BFD daemon\n") { vty_out(vty, "BFD debugging status:\n"); + if (bglobal.debug_peer_event) + vty_out(vty, " Peer events debugging is on.\n"); + if (bglobal.debug_zebra) + vty_out(vty, " Zebra events debugging is on.\n"); + if (bglobal.debug_network) + vty_out(vty, " Network layer debugging is on.\n"); return CMD_SUCCESS; } -static void _bfdd_peer_write_config_iter(struct hash_bucket *hb, void *arg) -{ - struct vty *vty = arg; - struct bfd_session *bs = hb->data; - - if (!BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG)) - return; +static int bfdd_write_config(struct vty *vty); +struct cmd_node bfd_node = { + .name = "bfd", + .node = BFD_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-bfd)# ", + .config_write = bfdd_write_config, +}; - _bfdd_peer_write_config(vty, bs); -} +struct cmd_node bfd_peer_node = { + .name = "bfd peer", + .node = BFD_PEER_NODE, + .parent_node = BFD_NODE, + .prompt = "%s(config-bfd-peer)# ", +}; -static int bfdd_peer_write_config(struct vty *vty) +static int bfdd_write_config(struct vty *vty) { - bfd_id_iterate(_bfdd_peer_write_config_iter, vty); + struct lyd_node *dnode; + int written = 0; - return 1; -} + if (bglobal.debug_peer_event) { + vty_out(vty, "debug bfd peer\n"); + written = 1; + } -struct cmd_node bfd_node = { - BFD_NODE, - "%s(config-bfd)# ", - 1, -}; + if (bglobal.debug_zebra) { + vty_out(vty, "debug bfd zebra\n"); + written = 1; + } -struct cmd_node bfd_peer_node = { - BFD_PEER_NODE, - "%s(config-bfd-peer)# ", - 1, -}; + if (bglobal.debug_network) { + vty_out(vty, "debug bfd network\n"); + written = 1; + } + + dnode = yang_dnode_get(running_config->dnode, "/frr-bfdd:bfdd"); + if (dnode) { + nb_cli_show_dnode_cmds(vty, dnode, false); + written = 1; + } + + return written; +} void bfdd_vty_init(void) { install_element(ENABLE_NODE, &bfd_show_peers_counters_cmd); install_element(ENABLE_NODE, &bfd_show_peer_counters_cmd); + install_element(ENABLE_NODE, &bfd_clear_peer_counters_cmd); install_element(ENABLE_NODE, &bfd_show_peers_cmd); install_element(ENABLE_NODE, &bfd_show_peer_cmd); - install_element(CONFIG_NODE, &bfd_enter_cmd); + install_element(ENABLE_NODE, &bfd_show_peers_brief_cmd); install_element(ENABLE_NODE, &show_debugging_bfd_cmd); + install_element(ENABLE_NODE, &bfd_debug_peer_cmd); + install_element(ENABLE_NODE, &bfd_debug_zebra_cmd); + install_element(ENABLE_NODE, &bfd_debug_network_cmd); + + install_element(CONFIG_NODE, &bfd_debug_peer_cmd); + install_element(CONFIG_NODE, &bfd_debug_zebra_cmd); + install_element(CONFIG_NODE, &bfd_debug_network_cmd); + /* Install BFD node and commands. */ - install_node(&bfd_node, bfdd_write_config); + install_node(&bfd_node); install_default(BFD_NODE); - install_element(BFD_NODE, &bfd_peer_enter_cmd); - install_element(BFD_NODE, &bfd_no_peer_cmd); /* Install BFD peer node. */ - install_node(&bfd_peer_node, bfdd_peer_write_config); + install_node(&bfd_peer_node); install_default(BFD_PEER_NODE); - install_element(BFD_PEER_NODE, &bfd_peer_detectmultiplier_cmd); - install_element(BFD_PEER_NODE, &bfd_peer_recvinterval_cmd); - install_element(BFD_PEER_NODE, &bfd_peer_txinterval_cmd); - install_element(BFD_PEER_NODE, &bfd_peer_echointerval_cmd); - install_element(BFD_PEER_NODE, &bfd_peer_shutdown_cmd); - install_element(BFD_PEER_NODE, &bfd_peer_echo_cmd); - install_element(BFD_PEER_NODE, &bfd_peer_label_cmd); + + bfdd_cli_init(); } diff --git a/bfdd/bsd.c b/bfdd/bsd.c deleted file mode 100644 index 923fbd909e..0000000000 --- a/bfdd/bsd.c +++ /dev/null @@ -1,48 +0,0 @@ -/* - * *BSD specific code - * - * Copyright (C) 2018 Network Device Education Foundation, Inc. ("NetDEF") - * - * FRR is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2, or (at your option) any - * later version. - * - * FRR is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with FRR; see the file COPYING. If not, write to the Free - * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - */ - -#include - -#ifdef BFD_BSD - -#include -#include -#include -#include - -#include - -#include "bfd.h" - -/* - * Definitions. - */ -int bp_bind_dev(int sd, const char *dev) -{ - /* - * *BSDs don't support `SO_BINDTODEVICE`, instead you must - * manually specify the main address of the interface or use - * BPF on the socket descriptor. - */ - return 0; -} - -#endif /* BFD_BSD */ diff --git a/bfdd/config.c b/bfdd/config.c index cd57ea9fe3..b71670f012 100644 --- a/bfdd/config.c +++ b/bfdd/config.c @@ -30,6 +30,8 @@ #include "bfd.h" +DEFINE_MTYPE_STATIC(BFDD, BFDD_LABEL, "long-lived label memory") + /* * Definitions */ @@ -68,7 +70,7 @@ static int config_add(struct bfd_peer_cfg *bpc, static int config_del(struct bfd_peer_cfg *bpc, void *arg __attribute__((unused))) { - return ptm_bfd_ses_del(bpc) != 0; + return ptm_bfd_sess_del(bpc) != 0; } static int parse_config_json(struct json_object *jo, bpc_handle h, void *arg) @@ -90,8 +92,8 @@ static int parse_config_json(struct json_object *jo, bpc_handle h, void *arg) error += parse_list(jo_val, PLT_LABEL, h, arg); } else { sval = json_object_get_string(jo_val); - log_warning("%s:%d invalid configuration: %s", __func__, - __LINE__, sval); + zlog_warn("%s:%d invalid configuration: %s", __func__, + __LINE__, sval); error++; } } @@ -137,15 +139,15 @@ static int parse_list(struct json_object *jo, enum peer_list_type plt, switch (plt) { case PLT_IPV4: - log_debug("ipv4 peers %d:", allen); + zlog_debug("ipv4 peers %d:", allen); bpc.bpc_ipv4 = true; break; case PLT_IPV6: - log_debug("ipv6 peers %d:", allen); + zlog_debug("ipv6 peers %d:", allen); bpc.bpc_ipv4 = false; break; case PLT_LABEL: - log_debug("label peers %d:", allen); + zlog_debug("label peers %d:", allen); if (parse_peer_label_config(jo_val, &bpc) != 0) { error++; continue; @@ -154,8 +156,8 @@ static int parse_list(struct json_object *jo, enum peer_list_type plt, default: error++; - log_error("%s:%d: unsupported peer type", __func__, - __LINE__); + zlog_err("%s:%d: unsupported peer type", __func__, + __LINE__); break; } @@ -176,7 +178,7 @@ static int parse_peer_config(struct json_object *jo, struct bfd_peer_cfg *bpc) int family_type = (bpc->bpc_ipv4) ? AF_INET : AF_INET6; int error = 0; - log_debug("\tpeer: %s", bpc->bpc_ipv4 ? "ipv4" : "ipv6"); + zlog_debug(" peer: %s", bpc->bpc_ipv4 ? "ipv4" : "ipv6"); JSON_FOREACH (jo, joi, join) { key = json_object_iter_peek_name(&joi); @@ -184,39 +186,41 @@ static int parse_peer_config(struct json_object *jo, struct bfd_peer_cfg *bpc) if (strcmp(key, "multihop") == 0) { bpc->bpc_mhop = json_object_get_boolean(jo_val); - log_debug("\tmultihop: %s", - bpc->bpc_mhop ? "true" : "false"); + zlog_debug(" multihop: %s", + bpc->bpc_mhop ? "true" : "false"); } else if (strcmp(key, "peer-address") == 0) { sval = json_object_get_string(jo_val); if (strtosa(sval, &bpc->bpc_peer) != 0 || bpc->bpc_peer.sa_sin.sin_family != family_type) { - log_info( + zlog_debug( "%s:%d failed to parse peer-address '%s'", __func__, __LINE__, sval); error++; } - log_debug("\tpeer-address: %s", sval); + zlog_debug(" peer-address: %s", sval); } else if (strcmp(key, "local-address") == 0) { sval = json_object_get_string(jo_val); if (strtosa(sval, &bpc->bpc_local) != 0 || bpc->bpc_local.sa_sin.sin_family != family_type) { - log_info( + zlog_debug( "%s:%d failed to parse local-address '%s'", __func__, __LINE__, sval); error++; } - log_debug("\tlocal-address: %s", sval); + zlog_debug(" local-address: %s", sval); } else if (strcmp(key, "local-interface") == 0) { bpc->bpc_has_localif = true; sval = json_object_get_string(jo_val); if (strlcpy(bpc->bpc_localif, sval, sizeof(bpc->bpc_localif)) > sizeof(bpc->bpc_localif)) { - log_debug("\tlocal-interface: %s (truncated)"); + zlog_debug( + " local-interface: %s (truncated)", + sval); error++; } else { - log_debug("\tlocal-interface: %s", sval); + zlog_debug(" local-interface: %s", sval); } } else if (strcmp(key, "vrf-name") == 0) { bpc->bpc_has_vrfname = true; @@ -224,65 +228,68 @@ static int parse_peer_config(struct json_object *jo, struct bfd_peer_cfg *bpc) if (strlcpy(bpc->bpc_vrfname, sval, sizeof(bpc->bpc_vrfname)) > sizeof(bpc->bpc_vrfname)) { - log_debug("\tvrf-name: %s (truncated)", sval); + zlog_debug(" vrf-name: %s (truncated)", + sval); error++; } else { - log_debug("\tvrf-name: %s", sval); + zlog_debug(" vrf-name: %s", sval); } } else if (strcmp(key, "detect-multiplier") == 0) { bpc->bpc_detectmultiplier = json_object_get_int64(jo_val); bpc->bpc_has_detectmultiplier = true; - log_debug("\tdetect-multiplier: %llu", - bpc->bpc_detectmultiplier); + zlog_debug(" detect-multiplier: %u", + bpc->bpc_detectmultiplier); } else if (strcmp(key, "receive-interval") == 0) { bpc->bpc_recvinterval = json_object_get_int64(jo_val); bpc->bpc_has_recvinterval = true; - log_debug("\treceive-interval: %llu", - bpc->bpc_recvinterval); + zlog_debug(" receive-interval: %" PRIu64, + bpc->bpc_recvinterval); } else if (strcmp(key, "transmit-interval") == 0) { bpc->bpc_txinterval = json_object_get_int64(jo_val); bpc->bpc_has_txinterval = true; - log_debug("\ttransmit-interval: %llu", - bpc->bpc_txinterval); + zlog_debug(" transmit-interval: %" PRIu64, + bpc->bpc_txinterval); } else if (strcmp(key, "echo-interval") == 0) { bpc->bpc_echointerval = json_object_get_int64(jo_val); bpc->bpc_has_echointerval = true; - log_debug("\techo-interval: %llu", - bpc->bpc_echointerval); + zlog_debug(" echo-interval: %" PRIu64, + bpc->bpc_echointerval); } else if (strcmp(key, "create-only") == 0) { bpc->bpc_createonly = json_object_get_boolean(jo_val); - log_debug("\tcreate-only: %s", - bpc->bpc_createonly ? "true" : "false"); + zlog_debug(" create-only: %s", + bpc->bpc_createonly ? "true" : "false"); } else if (strcmp(key, "shutdown") == 0) { bpc->bpc_shutdown = json_object_get_boolean(jo_val); - log_debug("\tshutdown: %s", - bpc->bpc_shutdown ? "true" : "false"); + zlog_debug(" shutdown: %s", + bpc->bpc_shutdown ? "true" : "false"); } else if (strcmp(key, "echo-mode") == 0) { bpc->bpc_echo = json_object_get_boolean(jo_val); - log_debug("\techo-mode: %s", - bpc->bpc_echo ? "true" : "false"); + zlog_debug(" echo-mode: %s", + bpc->bpc_echo ? "true" : "false"); } else if (strcmp(key, "label") == 0) { bpc->bpc_has_label = true; sval = json_object_get_string(jo_val); if (strlcpy(bpc->bpc_label, sval, sizeof(bpc->bpc_label)) > sizeof(bpc->bpc_label)) { - log_debug("\tlabel: %s (truncated)", sval); + zlog_debug(" label: %s (truncated)", + sval); error++; } else { - log_debug("\tlabel: %s", sval); + zlog_debug(" label: %s", sval); } } else { sval = json_object_get_string(jo_val); - log_warning("%s:%d invalid configuration: '%s: %s'", - __func__, __LINE__, key, sval); + zlog_warn("%s:%d invalid configuration: '%s: %s'", + __func__, __LINE__, key, sval); error++; } } if (bpc->bpc_peer.sa_sin.sin_family == 0) { - log_debug("%s:%d no peer address provided", __func__, __LINE__); + zlog_debug("%s:%d no peer address provided", __func__, + __LINE__); error++; } @@ -306,7 +313,7 @@ static int parse_peer_label_config(struct json_object *jo, if (pl == NULL) return 1; - log_debug("\tpeer-label: %s", sval); + zlog_debug(" peer-label: %s", sval); /* Translate the label into BFD address keys. */ bs_to_bpc(pl->pl_bs, bpc); @@ -468,12 +475,12 @@ char *config_notify_config(const char *op, struct bfd_session *bs) json_object_int_add(resp, "remote-echo-interval", bs->remote_timers.required_min_echo / 1000); - if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO)) + if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO)) json_object_boolean_true_add(resp, "echo-mode"); else json_object_boolean_false_add(resp, "echo-mode"); - if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN)) + if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN)) json_object_boolean_true_add(resp, "shutdown"); else json_object_boolean_false_add(resp, "shutdown"); @@ -505,12 +512,12 @@ static int json_object_add_peer(struct json_object *jo, struct bfd_session *bs) char addr_buf[INET6_ADDRSTRLEN]; /* Add peer 'key' information. */ - if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_IPV6)) + if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_IPV6)) json_object_boolean_true_add(jo, "ipv6"); else json_object_boolean_false_add(jo, "ipv6"); - if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) { + if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) { json_object_boolean_true_add(jo, "multihop"); json_object_string_add(jo, "peer-address", inet_ntop(bs->key.family, &bs->key.peer, @@ -567,7 +574,7 @@ struct peer_label *pl_new(const char *label, struct bfd_session *bs) if (strlcpy(pl->pl_label, label, sizeof(pl->pl_label)) > sizeof(pl->pl_label)) - log_warning("%s:%d: label was truncated", __func__, __LINE__); + zlog_warn("%s:%d: label was truncated", __func__, __LINE__); pl->pl_bs = bs; bs->pl = pl; diff --git a/bfdd/control.c b/bfdd/control.c index c308d647d8..3b954c64f8 100644 --- a/bfdd/control.c +++ b/bfdd/control.c @@ -86,13 +86,13 @@ static int sock_set_nonblock(int fd) flags = fcntl(fd, F_GETFL, 0); if (flags == -1) { - log_warning("%s: fcntl F_GETFL: %s", __func__, strerror(errno)); + zlog_warn("%s: fcntl F_GETFL: %s", __func__, strerror(errno)); return -1; } flags |= O_NONBLOCK; if (fcntl(fd, F_SETFL, flags) == -1) { - log_warning("%s: fcntl F_SETFL: %s", __func__, strerror(errno)); + zlog_warn("%s: fcntl F_SETFL: %s", __func__, strerror(errno)); return -1; } @@ -116,20 +116,20 @@ int control_init(const char *path) sd = socket(AF_UNIX, SOCK_STREAM, PF_UNSPEC); if (sd == -1) { - log_error("%s: socket: %s", __func__, strerror(errno)); + zlog_err("%s: socket: %s", __func__, strerror(errno)); return -1; } umval = umask(0); if (bind(sd, (struct sockaddr *)&sun_, sizeof(sun_)) == -1) { - log_error("%s: bind: %s", __func__, strerror(errno)); + zlog_err("%s: bind: %s", __func__, strerror(errno)); close(sd); return -1; } umask(umval); if (listen(sd, SOMAXCONN) == -1) { - log_error("%s: listen: %s", __func__, strerror(errno)); + zlog_err("%s: listen: %s", __func__, strerror(errno)); close(sd); return -1; } @@ -164,12 +164,11 @@ int control_accept(struct thread *t) csock = accept(sd, NULL, 0); if (csock == -1) { - log_warning("%s: accept: %s", __func__, strerror(errno)); + zlog_warn("%s: accept: %s", __func__, strerror(errno)); return 0; } - if (control_new(csock) == NULL) - close(csock); + control_new(csock); bglobal.bg_csockev = NULL; thread_add_read(master, control_accept, NULL, sd, &bglobal.bg_csockev); @@ -334,8 +333,6 @@ static int control_queue_enqueue(struct bfd_control_socket *bcs, struct bfd_control_buffer *bcb; bcq = control_queue_new(bcs); - if (bcq == NULL) - return -1; bcb = &bcq->bcq_bcb; bcb->bcb_left = sizeof(struct bfd_control_msg) + ntohl(bcm->bcm_length); @@ -407,7 +404,6 @@ static void control_reset_buf(struct bfd_control_buffer *bcb) { /* Get ride of old data. */ XFREE(MTYPE_BFDD_NOTIFICATION, bcb->bcb_buf); - bcb->bcb_buf = NULL; bcb->bcb_pos = 0; bcb->bcb_left = 0; } @@ -441,7 +437,7 @@ static int control_read(struct thread *t) if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) goto schedule_next_read; - log_warning("%s: read: %s", __func__, strerror(errno)); + zlog_warn("%s: read: %s", __func__, strerror(errno)); control_free(bcs); return 0; } @@ -449,15 +445,15 @@ static int control_read(struct thread *t) /* Validate header fields. */ plen = ntohl(bcm.bcm_length); if (plen < 2) { - log_debug("%s: client closed due small message length: %d", - __func__, bcm.bcm_length); + zlog_debug("%s: client closed due small message length: %d", + __func__, bcm.bcm_length); control_free(bcs); return 0; } if (bcm.bcm_ver != BMV_VERSION_1) { - log_debug("%s: client closed due bad version: %d", __func__, - bcm.bcm_ver); + zlog_debug("%s: client closed due bad version: %d", __func__, + bcm.bcm_ver); control_free(bcs); return 0; } @@ -471,8 +467,8 @@ static int control_read(struct thread *t) bcb->bcb_buf = XMALLOC(MTYPE_BFDD_NOTIFICATION, sizeof(bcm) + bcb->bcb_left + 1); if (bcb->bcb_buf == NULL) { - log_warning("%s: not enough memory for message size: %u", - __func__, bcb->bcb_left); + zlog_warn("%s: not enough memory for message size: %zu", + __func__, bcb->bcb_left); control_free(bcs); return 0; } @@ -493,7 +489,7 @@ static int control_read(struct thread *t) if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) goto schedule_next_read; - log_warning("%s: read: %s", __func__, strerror(errno)); + zlog_warn("%s: read: %s", __func__, strerror(errno)); control_free(bcs); return 0; } @@ -522,8 +518,8 @@ static int control_read(struct thread *t) break; default: - log_debug("%s: unhandled message type: %d", __func__, - bcb->bcb_bcm->bcm_type); + zlog_debug("%s: unhandled message type: %d", __func__, + bcb->bcb_bcm->bcm_type); control_response(bcs, bcb->bcb_bcm->bcm_id, BCM_RESPONSE_ERROR, "invalid message type"); break; @@ -560,7 +556,7 @@ static int control_write(struct thread *t) return 0; } - log_warning("%s: write: %s", __func__, strerror(errno)); + zlog_warn("%s: write: %s", __func__, strerror(errno)); control_free(bcs); return 0; } @@ -657,8 +653,7 @@ static int notify_add_cb(struct bfd_peer_cfg *bpc, void *arg) if (bs == NULL) return -1; - if (control_notifypeer_new(bcs, bs) == NULL) - return -1; + control_notifypeer_new(bcs, bs); /* Notify peer status. */ _control_notify(bcs, bs); @@ -724,8 +719,8 @@ static void control_response(struct bfd_control_socket *bcs, uint16_t id, /* Generate JSON response. */ jsonstr = config_response(status, error); if (jsonstr == NULL) { - log_warning("%s: config_response: failed to get JSON str", - __func__); + zlog_warn("%s: config_response: failed to get JSON str", + __func__); return; } @@ -754,8 +749,8 @@ static void _control_notify(struct bfd_control_socket *bcs, /* Generate JSON response. */ jsonstr = config_notify(bs); if (jsonstr == NULL) { - log_warning("%s: config_notify: failed to get JSON str", - __func__); + zlog_warn("%s: config_notify: failed to get JSON str", + __func__); return; } @@ -774,13 +769,13 @@ static void _control_notify(struct bfd_control_socket *bcs, control_queue_enqueue(bcs, bcm); } -int control_notify(struct bfd_session *bs) +int control_notify(struct bfd_session *bs, uint8_t notify_state) { struct bfd_control_socket *bcs; struct bfd_notify_peer *bnp; /* Notify zebra listeners as well. */ - ptm_bfd_notify(bs); + ptm_bfd_notify(bs, notify_state); /* * PERFORMANCE: reuse the bfd_control_msg allocated data for @@ -817,8 +812,8 @@ static void _control_notify_config(struct bfd_control_socket *bcs, /* Generate JSON response. */ jsonstr = config_notify_config(op, bs); if (jsonstr == NULL) { - log_warning("%s: config_notify_config: failed to get JSON str", - __func__); + zlog_warn("%s: config_notify_config: failed to get JSON str", + __func__); return; } diff --git a/bfdd/event.c b/bfdd/event.c index 5ba54c2b0b..654928b9b3 100644 --- a/bfdd/event.c +++ b/bfdd/event.c @@ -43,14 +43,11 @@ void bfd_recvtimer_update(struct bfd_session *bs) bfd_recvtimer_delete(bs); /* Don't add event if peer is deactivated. */ - if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN) || + if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN) || bs->sock == -1) return; tv_normalize(&tv); -#ifdef BFD_EVENT_DEBUG - log_debug("%s: sec = %ld, usec = %ld", __func__, tv.tv_sec, tv.tv_usec); -#endif /* BFD_EVENT_DEBUG */ thread_add_timer_tv(master, bfd_recvtimer_cb, bs, &tv, &bs->recvtimer_ev); @@ -64,14 +61,11 @@ void bfd_echo_recvtimer_update(struct bfd_session *bs) bfd_echo_recvtimer_delete(bs); /* Don't add event if peer is deactivated. */ - if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN) || + if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN) || bs->sock == -1) return; tv_normalize(&tv); -#ifdef BFD_EVENT_DEBUG - log_debug("%s: sec = %ld, usec = %ld", __func__, tv.tv_sec, tv.tv_usec); -#endif /* BFD_EVENT_DEBUG */ thread_add_timer_tv(master, bfd_echo_recvtimer_cb, bs, &tv, &bs->echo_recvtimer_ev); @@ -85,14 +79,11 @@ void bfd_xmttimer_update(struct bfd_session *bs, uint64_t jitter) bfd_xmttimer_delete(bs); /* Don't add event if peer is deactivated. */ - if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN) || + if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN) || bs->sock == -1) return; tv_normalize(&tv); -#ifdef BFD_EVENT_DEBUG - log_debug("%s: sec = %ld, usec = %ld", __func__, tv.tv_sec, tv.tv_usec); -#endif /* BFD_EVENT_DEBUG */ thread_add_timer_tv(master, bfd_xmt_cb, bs, &tv, &bs->xmttimer_ev); } @@ -105,14 +96,11 @@ void bfd_echo_xmttimer_update(struct bfd_session *bs, uint64_t jitter) bfd_echo_xmttimer_delete(bs); /* Don't add event if peer is deactivated. */ - if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN) || + if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN) || bs->sock == -1) return; tv_normalize(&tv); -#ifdef BFD_EVENT_DEBUG - log_debug("%s: sec = %ld, usec = %ld", __func__, tv.tv_sec, tv.tv_usec); -#endif /* BFD_EVENT_DEBUG */ thread_add_timer_tv(master, bfd_echo_xmt_cb, bs, &tv, &bs->echo_xmttimer_ev); diff --git a/bfdd/linux.c b/bfdd/linux.c deleted file mode 100644 index 3a76b459d7..0000000000 --- a/bfdd/linux.c +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Linux specific code - * - * Copyright (C) 2018 Network Device Education Foundation, Inc. ("NetDEF") - * - * FRR is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2, or (at your option) any - * later version. - * - * FRR is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with FRR; see the file COPYING. If not, write to the Free - * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - */ - -#include - -#ifdef BFD_LINUX - -#include "bfd.h" - - -/* - * Definitions. - */ -int bp_bind_dev(int sd __attribute__((__unused__)), - const char *dev __attribute__((__unused__))) -{ - /* - * TODO: implement this differently. It is not possible to - * SO_BINDTODEVICE after the daemon has dropped its privileges. - */ -#if 0 - size_t devlen = strlen(dev) + 1; - - if (setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, dev, devlen) == -1) { - log_warning("%s: setsockopt(SO_BINDTODEVICE, \"%s\"): %s", - __func__, dev, strerror(errno)); - return -1; - } -#endif - - return 0; -} - -#endif /* BFD_LINUX */ diff --git a/bfdd/log.c b/bfdd/log.c deleted file mode 100644 index d81d7cd9b3..0000000000 --- a/bfdd/log.c +++ /dev/null @@ -1,125 +0,0 @@ -/********************************************************************* - * Copyright 2017-2018 Network Device Education Foundation, Inc. ("NetDEF") - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; see the file COPYING; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * log.c: implements an abstraction between loggers interface. Implement all - * log backends in this file. - * - * Authors - * ------- - * Rafael Zalamena - */ - -#include - -#include "bfd.h" - -#include "lib/log_int.h" - -void log_msg(int level, const char *fmt, va_list vl); - - -static int log_fg; -static int log_level = BLOG_DEBUG; - -void log_init(int foreground, enum blog_level level, - struct frr_daemon_info *fdi) -{ - log_fg = foreground; - log_level = level; - - openzlog(fdi->progname, fdi->logname, 0, - LOG_CONS | LOG_NDELAY | LOG_PID, LOG_DAEMON); -} - -void log_msg(int level, const char *fmt, va_list vl) -{ - if (level < log_level) - return; - - switch (level) { - case BLOG_DEBUG: - vzlog(LOG_DEBUG, fmt, vl); - break; - - case BLOG_INFO: - vzlog(LOG_INFO, fmt, vl); - break; - - case BLOG_WARNING: - vzlog(LOG_WARNING, fmt, vl); - break; - - case BLOG_ERROR: - vzlog(LOG_ERR, fmt, vl); - break; - - case BLOG_FATAL: - vzlog(LOG_EMERG, fmt, vl); - break; - - default: - vfprintf(stderr, fmt, vl); - break; - } -} - -void log_info(const char *fmt, ...) -{ - va_list vl; - - va_start(vl, fmt); - log_msg(BLOG_INFO, fmt, vl); - va_end(vl); -} - -void log_debug(const char *fmt, ...) -{ - va_list vl; - - va_start(vl, fmt); - log_msg(BLOG_DEBUG, fmt, vl); - va_end(vl); -} - -void log_error(const char *fmt, ...) -{ - va_list vl; - - va_start(vl, fmt); - log_msg(BLOG_ERROR, fmt, vl); - va_end(vl); -} - -void log_warning(const char *fmt, ...) -{ - va_list vl; - - va_start(vl, fmt); - log_msg(BLOG_WARNING, fmt, vl); - va_end(vl); -} - -void log_fatal(const char *fmt, ...) -{ - va_list vl; - - va_start(vl, fmt); - log_msg(BLOG_FATAL, fmt, vl); - va_end(vl); - - exit(1); -} diff --git a/bfdd/ptm_adapter.c b/bfdd/ptm_adapter.c index e92500cd80..cc294b9b9a 100644 --- a/bfdd/ptm_adapter.c +++ b/bfdd/ptm_adapter.c @@ -24,6 +24,7 @@ #include "lib/queue.h" #include "lib/stream.h" #include "lib/zclient.h" +#include "lib/printfrr.h" #include "lib/bfd.h" @@ -58,7 +59,7 @@ static struct zclient *zclient; static int _ptm_msg_address(struct stream *msg, int family, const void *addr); static void _ptm_msg_read_address(struct stream *msg, struct sockaddr_any *sa); -static int _ptm_msg_read(struct stream *msg, int command, +static int _ptm_msg_read(struct stream *msg, int command, vrf_id_t vrf_id, struct bfd_peer_cfg *bpc, struct ptm_client **pc); static struct ptm_client *pc_lookup(uint32_t pid); @@ -72,26 +73,26 @@ static struct ptm_client_notification *pcn_lookup(struct ptm_client *pc, static void pcn_free(struct ptm_client_notification *pcn); -static void bfdd_dest_register(struct stream *msg); -static void bfdd_dest_deregister(struct stream *msg); +static void bfdd_dest_register(struct stream *msg, vrf_id_t vrf_id); +static void bfdd_dest_deregister(struct stream *msg, vrf_id_t vrf_id); static void bfdd_client_register(struct stream *msg); static void bfdd_client_deregister(struct stream *msg); /* * Functions */ -#ifdef BFD_DEBUG -static void debug_printbpc(const char *func, unsigned int line, - struct bfd_peer_cfg *bpc); - -static void debug_printbpc(const char *func, unsigned int line, - struct bfd_peer_cfg *bpc) +static void debug_printbpc(const struct bfd_peer_cfg *bpc, const char *fmt, ...) { - char addr[3][128]; - char timers[3][128]; - - addr[0][0] = addr[1][0] = addr[2][0] = timers[0][0] = timers[1][0] = - timers[2][0] = 0; + char timers[3][128] = {}; + char addr[3][128] = {}; + char profile[128] = {}; + char cbit_str[32]; + char msgbuf[256]; + va_list vl; + + /* Avoid debug calculations if it's disabled. */ + if (bglobal.debug_zebra == false) + return; snprintf(addr[0], sizeof(addr[0]), "peer:%s", satostr(&bpc->bpc_peer)); if (bpc->bpc_local.sa_sin.sin_family) @@ -106,26 +107,59 @@ static void debug_printbpc(const char *func, unsigned int line, snprintf(addr[2], sizeof(addr[2]), " vrf:%s", bpc->bpc_vrfname); if (bpc->bpc_has_recvinterval) - snprintf(timers[0], sizeof(timers[0]), " rx:%lu", - bpc->bpc_recvinterval); + snprintfrr(timers[0], sizeof(timers[0]), " rx:%" PRIu64, + bpc->bpc_recvinterval); if (bpc->bpc_has_txinterval) - snprintf(timers[1], sizeof(timers[1]), " tx:%lu", - bpc->bpc_recvinterval); + snprintfrr(timers[1], sizeof(timers[1]), " tx:%" PRIu64, + bpc->bpc_recvinterval); if (bpc->bpc_has_detectmultiplier) snprintf(timers[2], sizeof(timers[2]), " detect-multiplier:%d", bpc->bpc_detectmultiplier); - log_debug("%s:%d: %s %s%s%s%s%s%s", func, line, - bpc->bpc_mhop ? "multi-hop" : "single-hop", addr[0], addr[1], - addr[2], timers[0], timers[1], timers[2]); + snprintf(cbit_str, sizeof(cbit_str), " cbit:0x%02x", bpc->bpc_cbit); + + if (bpc->bpc_has_profile) + snprintf(profile, sizeof(profile), " profile:%s", + bpc->bpc_profile); + + va_start(vl, fmt); + vsnprintf(msgbuf, sizeof(msgbuf), fmt, vl); + va_end(vl); + + zlog_debug("%s [mhop:%s %s%s%s%s%s%s%s%s]", msgbuf, + bpc->bpc_mhop ? "yes" : "no", addr[0], addr[1], addr[2], + timers[0], timers[1], timers[2], cbit_str, profile); } -#define DEBUG_PRINTBPC(bpc) debug_printbpc(__FILE__, __LINE__, (bpc)) -#else -#define DEBUG_PRINTBPC(bpc) -#endif /* BFD_DEBUG */ +static void _ptm_bfd_session_del(struct bfd_session *bs, uint8_t diag) +{ + if (bglobal.debug_peer_event) + zlog_debug("session-delete: %s", bs_to_string(bs)); + + /* Change state and notify peer. */ + bs->ses_state = PTM_BFD_DOWN; + bs->local_diag = diag; + ptm_bfd_snd(bs, 0); + + /* Session reached refcount == 0, lets delete it. */ + if (bs->refcount == 0) { + /* + * Sanity check: if there is a refcount bug, we can't delete + * the session a user configured manually. Lets leave a + * message here so we can catch the bug if it exists. + */ + if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG)) { + zlog_err( + "ptm-del-session: [%s] session refcount is zero but it was configured by CLI", + bs_to_string(bs)); + } else { + control_notify_config(BCM_NOTIFY_CONFIG_DELETE, bs); + bfd_session_free(bs); + } + } +} static int _ptm_msg_address(struct stream *msg, int family, const void *addr) { @@ -150,7 +184,7 @@ static int _ptm_msg_address(struct stream *msg, int family, const void *addr) return 0; } -int ptm_bfd_notify(struct bfd_session *bs) +int ptm_bfd_notify(struct bfd_session *bs, uint8_t notify_state) { struct stream *msg; @@ -173,6 +207,7 @@ int ptm_bfd_notify(struct bfd_session *bs) * - AF_INET6: * - 16 bytes: ipv6 * - c: prefix length + * - c: cbit * * Commands: ZEBRA_BFD_DEST_REPLAY * @@ -182,7 +217,10 @@ int ptm_bfd_notify(struct bfd_session *bs) stream_reset(msg); /* TODO: VRF handling */ - zclient_create_header(msg, ZEBRA_BFD_DEST_REPLAY, VRF_DEFAULT); + if (bs->vrf) + zclient_create_header(msg, ZEBRA_BFD_DEST_REPLAY, bs->vrf->vrf_id); + else + zclient_create_header(msg, ZEBRA_BFD_DEST_REPLAY, VRF_DEFAULT); /* This header will be handled by `zebra_ptm.c`. */ stream_putl(msg, ZEBRA_INTERFACE_BFD_DEST_UPDATE); @@ -197,12 +235,15 @@ int ptm_bfd_notify(struct bfd_session *bs) _ptm_msg_address(msg, bs->key.family, &bs->key.peer); /* BFD status */ - switch (bs->ses_state) { + switch (notify_state) { case PTM_BFD_UP: stream_putl(msg, BFD_STATUS_UP); break; case PTM_BFD_ADM_DOWN: + stream_putl(msg, BFD_STATUS_ADMIN_DOWN); + break; + case PTM_BFD_DOWN: case PTM_BFD_INIT: stream_putl(msg, BFD_STATUS_DOWN); @@ -216,6 +257,8 @@ int ptm_bfd_notify(struct bfd_session *bs) /* BFD source prefix information. */ _ptm_msg_address(msg, bs->key.family, &bs->key.local); + stream_putc(msg, bs->remote_cbit); + /* Write packet size. */ stream_putw_at(msg, 0, stream_get_endp(msg)); @@ -248,7 +291,7 @@ static void _ptm_msg_read_address(struct stream *msg, struct sockaddr_any *sa) return; default: - log_warning("ptm-read-address: invalid family: %d", family); + zlog_warn("ptm-read-address: invalid family: %d", family); break; } @@ -256,11 +299,10 @@ static void _ptm_msg_read_address(struct stream *msg, struct sockaddr_any *sa) memset(sa, 0, sizeof(*sa)); } -static int _ptm_msg_read(struct stream *msg, int command, +static int _ptm_msg_read(struct stream *msg, int command, vrf_id_t vrf_id, struct bfd_peer_cfg *bpc, struct ptm_client **pc) { uint32_t pid; - uint8_t ttl __attribute__((unused)); size_t ifnamelen; /* @@ -290,6 +332,9 @@ static int _ptm_msg_read(struct stream *msg, int command, * - 16 bytes: ipv6 address * - c: ifname length * - X bytes: interface name + * - c: bfd_cbit + * - c: profile name length. + * - X bytes: profile name. * * q(64), l(32), w(16), c(8) */ @@ -302,10 +347,6 @@ static int _ptm_msg_read(struct stream *msg, int command, STREAM_GETL(msg, pid); *pc = pc_new(pid); - if (*pc == NULL) { - log_debug("ptm-read: failed to allocate memory"); - return -1; - } /* Register/update peer information. */ _ptm_msg_read_address(msg, &bpc->bpc_peer); @@ -333,7 +374,18 @@ static int _ptm_msg_read(struct stream *msg, int command, if (bpc->bpc_mhop) { /* Read multihop source address and TTL. */ _ptm_msg_read_address(msg, &bpc->bpc_local); - STREAM_GETC(msg, ttl); + STREAM_GETC(msg, bpc->bpc_minimum_ttl); + if (bpc->bpc_minimum_ttl >= BFD_TTL_VAL + || bpc->bpc_minimum_ttl == 0) { + zlog_warn("%s: received invalid TTL configuration %d", + __func__, bpc->bpc_has_minimum_ttl); + bpc->bpc_minimum_ttl = BFD_DEF_MHOP_TTL; + bpc->bpc_has_minimum_ttl = false; + } else { + bpc->bpc_minimum_ttl = + (BFD_TTL_VAL + 1) - bpc->bpc_minimum_ttl; + bpc->bpc_has_minimum_ttl = true; + } } else { /* If target is IPv6, then we must obtain local address. */ if (bpc->bpc_ipv4 == false) @@ -345,7 +397,7 @@ static int _ptm_msg_read(struct stream *msg, int command, */ STREAM_GETC(msg, ifnamelen); if (ifnamelen >= sizeof(bpc->bpc_localif)) { - log_error("ptm-read: interface name is too big"); + zlog_err("ptm-read: interface name is too big"); return -1; } @@ -355,12 +407,38 @@ static int _ptm_msg_read(struct stream *msg, int command, bpc->bpc_localif[ifnamelen] = 0; } } + if (vrf_id != VRF_DEFAULT) { + struct vrf *vrf; + + vrf = vrf_lookup_by_id(vrf_id); + if (vrf) { + bpc->bpc_has_vrfname = true; + strlcpy(bpc->bpc_vrfname, vrf->name, sizeof(bpc->bpc_vrfname)); + } else { + zlog_err("ptm-read: vrf id %u could not be identified", + vrf_id); + return -1; + } + } else { + bpc->bpc_has_vrfname = true; + strlcpy(bpc->bpc_vrfname, VRF_DEFAULT_NAME, sizeof(bpc->bpc_vrfname)); + } + + STREAM_GETC(msg, bpc->bpc_cbit); + + /* Handle profile names. */ + STREAM_GETC(msg, ifnamelen); + bpc->bpc_has_profile = ifnamelen > 0; + if (bpc->bpc_has_profile) { + STREAM_GET(bpc->bpc_profile, msg, ifnamelen); + bpc->bpc_profile[ifnamelen] = 0; + } /* Sanity check: peer and local address must match IP types. */ if (bpc->bpc_local.sa_sin.sin_family != 0 && (bpc->bpc_local.sa_sin.sin_family != bpc->bpc_peer.sa_sin.sin_family)) { - log_warning("ptm-read: peer family doesn't match local type"); + zlog_warn("ptm-read: peer family doesn't match local type"); return -1; } @@ -370,45 +448,50 @@ static int _ptm_msg_read(struct stream *msg, int command, return -1; } -static void bfdd_dest_register(struct stream *msg) +static void bfdd_dest_register(struct stream *msg, vrf_id_t vrf_id) { struct ptm_client *pc; - struct ptm_client_notification *pcn; struct bfd_session *bs; struct bfd_peer_cfg bpc; /* Read the client context and peer data. */ - if (_ptm_msg_read(msg, ZEBRA_BFD_DEST_REGISTER, &bpc, &pc) == -1) + if (_ptm_msg_read(msg, ZEBRA_BFD_DEST_REGISTER, vrf_id, &bpc, &pc) == -1) return; - DEBUG_PRINTBPC(&bpc); + debug_printbpc(&bpc, "ptm-add-dest: register peer"); /* Find or start new BFD session. */ bs = bs_peer_find(&bpc); if (bs == NULL) { bs = ptm_bfd_sess_new(&bpc); if (bs == NULL) { - log_debug("ptm-add-dest: failed to create BFD session"); + if (bglobal.debug_zebra) + zlog_debug( + "ptm-add-dest: failed to create BFD session"); return; } } else { - /* Don't try to change echo/shutdown state. */ - bpc.bpc_echo = BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO); - bpc.bpc_shutdown = - BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN); + /* + * BFD session was already created, we are just updating the + * current peer. + * + * `ptm-bfd` (or `HAVE_BFDD == 0`) is the only implementation + * that allow users to set peer specific timers via protocol. + * BFD daemon (this code) on the other hand only supports + * changing peer configuration manually (through `peer` node) + * or via profiles. + */ + if (bpc.bpc_has_profile) + bfd_profile_apply(bpc.bpc_profile, bs); } /* Create client peer notification register. */ - pcn = pcn_new(pc, bs); - if (pcn == NULL) { - log_error("ptm-add-dest: failed to registrate notifications"); - return; - } + pcn_new(pc, bs); - ptm_bfd_notify(bs); + ptm_bfd_notify(bs, bs->ses_state); } -static void bfdd_dest_deregister(struct stream *msg) +static void bfdd_dest_deregister(struct stream *msg, vrf_id_t vrf_id) { struct ptm_client *pc; struct ptm_client_notification *pcn; @@ -416,25 +499,35 @@ static void bfdd_dest_deregister(struct stream *msg) struct bfd_peer_cfg bpc; /* Read the client context and peer data. */ - if (_ptm_msg_read(msg, ZEBRA_BFD_DEST_DEREGISTER, &bpc, &pc) == -1) + if (_ptm_msg_read(msg, ZEBRA_BFD_DEST_DEREGISTER, vrf_id, &bpc, &pc) == -1) return; - DEBUG_PRINTBPC(&bpc); + debug_printbpc(&bpc, "ptm-del-dest: deregister peer"); /* Find or start new BFD session. */ bs = bs_peer_find(&bpc); if (bs == NULL) { - log_debug("ptm-del-dest: failed to find BFD session"); + if (bglobal.debug_zebra) + zlog_debug("ptm-del-dest: failed to find BFD session"); return; } /* Unregister client peer notification. */ pcn = pcn_lookup(pc, bs); - pcn_free(pcn); - if (bs->refcount || - BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG)) + if (pcn != NULL) { + pcn_free(pcn); return; - ptm_bfd_ses_del(&bpc); + } + + if (bglobal.debug_zebra) + zlog_debug("ptm-del-dest: failed to find BFD session"); + + /* + * XXX: We either got a double deregistration or the daemon who + * created this is no longer around. Lets try to delete it anyway + * and the worst case is the refcount will detain us. + */ + _ptm_bfd_session_del(bs, BD_NEIGHBOR_DOWN); } /* @@ -443,22 +536,17 @@ static void bfdd_dest_deregister(struct stream *msg) */ static void bfdd_client_register(struct stream *msg) { - struct ptm_client *pc; uint32_t pid; /* Find or allocate process context data. */ STREAM_GETL(msg, pid); - pc = pc_new(pid); - if (pc == NULL) { - log_error("ptm-add-client: failed to register client: %u", pid); - return; - } + pc_new(pid); return; stream_failure: - log_error("ptm-add-client: failed to register client"); + zlog_err("ptm-add-client: failed to register client"); } /* @@ -475,21 +563,26 @@ static void bfdd_client_deregister(struct stream *msg) pc = pc_lookup(pid); if (pc == NULL) { - log_debug("ptm-del-client: failed to find client: %u", pid); + if (bglobal.debug_zebra) + zlog_debug("ptm-del-client: failed to find client: %u", + pid); return; } + if (bglobal.debug_zebra) + zlog_debug("ptm-del-client: client pid %u", pid); + pc_free(pc); return; stream_failure: - log_error("ptm-del-client: failed to deregister client"); + zlog_err("ptm-del-client: failed to deregister client"); } -static int bfdd_replay(int cmd, struct zclient *zc, uint16_t len, vrf_id_t vid) +static int bfdd_replay(ZAPI_CALLBACK_ARGS) { - struct stream *msg = zc->ibuf; + struct stream *msg = zclient->ibuf; uint32_t rcmd; STREAM_GETL(msg, rcmd); @@ -497,10 +590,10 @@ static int bfdd_replay(int cmd, struct zclient *zc, uint16_t len, vrf_id_t vid) switch (rcmd) { case ZEBRA_BFD_DEST_REGISTER: case ZEBRA_BFD_DEST_UPDATE: - bfdd_dest_register(msg); + bfdd_dest_register(msg, vrf_id); break; case ZEBRA_BFD_DEST_DEREGISTER: - bfdd_dest_deregister(msg); + bfdd_dest_deregister(msg, vrf_id); break; case ZEBRA_BFD_CLIENT_REGISTER: bfdd_client_register(msg); @@ -510,14 +603,15 @@ static int bfdd_replay(int cmd, struct zclient *zc, uint16_t len, vrf_id_t vid) break; default: - log_debug("ptm-replay: invalid message type %u", rcmd); + if (bglobal.debug_zebra) + zlog_debug("ptm-replay: invalid message type %u", rcmd); return -1; } return 0; stream_failure: - log_error("ptm-replay: failed to find command"); + zlog_err("ptm-replay: failed to find command"); return -1; } @@ -548,15 +642,26 @@ static void bfdd_sessions_enable_interface(struct interface *ifp) { struct bfd_session_observer *bso; struct bfd_session *bs; + struct vrf *vrf; - TAILQ_FOREACH(bso, &bglobal.bg_obslist, bso_entry) { - if (bso->bso_isinterface == false) - continue; + vrf = vrf_lookup_by_id(ifp->vrf_id); + if (!vrf) + return; - /* Interface name mismatch. */ + TAILQ_FOREACH(bso, &bglobal.bg_obslist, bso_entry) { bs = bso->bso_bs; - if (strcmp(ifp->name, bs->key.ifname)) + /* check vrf name */ + if (bs->key.vrfname[0] && + strcmp(vrf->name, bs->key.vrfname)) continue; + + /* If Interface matches vrfname, then bypass iface check */ + if (vrf_is_backend_netns() || strcmp(ifp->name, vrf->name)) { + /* Interface name mismatch. */ + if (strcmp(ifp->name, bs->key.ifname)) + continue; + } + /* Skip enabled sessions. */ if (bs->sock != -1) continue; @@ -572,66 +677,86 @@ static void bfdd_sessions_disable_interface(struct interface *ifp) struct bfd_session *bs; TAILQ_FOREACH(bso, &bglobal.bg_obslist, bso_entry) { - if (bso->bso_isinterface == false) - continue; - - /* Interface name mismatch. */ bs = bso->bso_bs; - if (strcmp(ifp->name, bs->key.ifname)) + + if (bs->ifp != ifp) continue; + /* Skip disabled sessions. */ - if (bs->sock == -1) + if (bs->sock == -1) { + bs->ifp = NULL; continue; + } - /* Try to enable it. */ bfd_session_disable(bs); - TAILQ_INSERT_HEAD(&bglobal.bg_obslist, bso, bso_entry); } } -static int bfdd_interface_update(int cmd, struct zclient *zc, - uint16_t len __attribute__((__unused__)), - vrf_id_t vrfid) +void bfdd_sessions_enable_vrf(struct vrf *vrf) { - struct interface *ifp; + struct bfd_session_observer *bso; + struct bfd_session *bs; - /* - * `zebra_interface_add_read` will handle the interface creation - * on `lib/if.c`. We'll use that data structure instead of - * rolling our own. - */ - if (cmd == ZEBRA_INTERFACE_ADD) { - ifp = zebra_interface_add_read(zc->ibuf, vrfid); - if (ifp == NULL) - return 0; + /* it may affect configs without interfaces */ + TAILQ_FOREACH(bso, &bglobal.bg_obslist, bso_entry) { + bs = bso->bso_bs; + /* update name */ + if (bs->vrf && bs->vrf == vrf) { + if (!strmatch(bs->key.vrfname, vrf->name)) + bfd_session_update_vrf_name(bs, vrf); + } + if (bs->vrf) + continue; + if (bs->key.vrfname[0] && + strcmp(vrf->name, bs->key.vrfname)) + continue; + /* need to update the vrf information on + * bs so that callbacks are handled + */ + bs->vrf = vrf; + /* Skip enabled sessions. */ + if (bs->sock != -1) + continue; + /* Try to enable it. */ + bfd_session_enable(bs); + } +} - bfdd_sessions_enable_interface(ifp); - return 0; +void bfdd_sessions_disable_vrf(struct vrf *vrf) +{ + struct bfd_session_observer *bso; + struct bfd_session *bs; + + TAILQ_FOREACH(bso, &bglobal.bg_obslist, bso_entry) { + bs = bso->bso_bs; + if (bs->key.vrfname[0] && + strcmp(vrf->name, bs->key.vrfname)) + continue; + /* Skip disabled sessions. */ + if (bs->sock == -1) + continue; + + bfd_session_disable(bs); } +} - /* Update interface information. */ - ifp = zebra_interface_state_read(zc->ibuf, vrfid); - if (ifp == NULL) - return 0; +static int bfd_ifp_destroy(struct interface *ifp) +{ + if (bglobal.debug_zebra) + zlog_debug("zclient: delete interface %s", ifp->name); bfdd_sessions_disable_interface(ifp); - if_set_index(ifp, IFINDEX_INTERNAL); - return 0; } -static int bfdd_interface_vrf_update(int command __attribute__((__unused__)), - struct zclient *zclient, - zebra_size_t length - __attribute__((__unused__)), - vrf_id_t vrfid) +static int bfdd_interface_vrf_update(ZAPI_CALLBACK_ARGS) { struct interface *ifp; vrf_id_t nvrfid; - ifp = zebra_interface_vrf_update_read(zclient->ibuf, vrfid, &nvrfid); + ifp = zebra_interface_vrf_update_read(zclient->ibuf, vrf_id, &nvrfid); if (ifp == NULL) return 0; @@ -647,9 +772,6 @@ static void bfdd_sessions_enable_address(struct connected *ifc) struct prefix prefix; TAILQ_FOREACH(bso, &bglobal.bg_obslist, bso_entry) { - if (bso->bso_isaddress == false) - continue; - /* Skip enabled sessions. */ bs = bso->bso_bs; if (bs->sock != -1) @@ -666,24 +788,42 @@ static void bfdd_sessions_enable_address(struct connected *ifc) } } -static int bfdd_interface_address_update(int cmd, struct zclient *zc, - zebra_size_t len - __attribute__((__unused__)), - vrf_id_t vrfid) +static int bfdd_interface_address_update(ZAPI_CALLBACK_ARGS) { struct connected *ifc; + char buf[64]; - ifc = zebra_interface_address_read(cmd, zc->ibuf, vrfid); + ifc = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); if (ifc == NULL) return 0; - bfdd_sessions_enable_address(ifc); + if (bglobal.debug_zebra) + zlog_debug("zclient: %s local address %s", + cmd == ZEBRA_INTERFACE_ADDRESS_ADD ? "add" + : "delete", + prefix2str(ifc->address, buf, sizeof(buf))); + + if (cmd == ZEBRA_INTERFACE_ADDRESS_ADD) + bfdd_sessions_enable_address(ifc); + else + connected_free(&ifc); + + return 0; +} + +static int bfd_ifp_create(struct interface *ifp) +{ + if (bglobal.debug_zebra) + zlog_debug("zclient: add interface %s", ifp->name); + + bfdd_sessions_enable_interface(ifp); return 0; } void bfdd_zclient_init(struct zebra_privs_t *bfdd_priv) { + if_zapi_callbacks(bfd_ifp_create, NULL, NULL, bfd_ifp_destroy); zclient = zclient_new(master, &zclient_options_default); assert(zclient != NULL); zclient_init(zclient, ZEBRA_ROUTE_BFD, 0, bfdd_priv); @@ -698,10 +838,6 @@ void bfdd_zclient_init(struct zebra_privs_t *bfdd_priv) /* Send replay request on zebra connect. */ zclient->zebra_connected = bfdd_zebra_connected; - /* Learn interfaces from zebra instead of the OS. */ - zclient->interface_add = bfdd_interface_update; - zclient->interface_delete = bfdd_interface_update; - /* Learn about interface VRF. */ zclient->interface_vrf_update = bfdd_interface_vrf_update; @@ -710,6 +846,20 @@ void bfdd_zclient_init(struct zebra_privs_t *bfdd_priv) zclient->interface_address_delete = bfdd_interface_address_update; } +void bfdd_zclient_register(vrf_id_t vrf_id) +{ + if (!zclient || zclient->sock < 0) + return; + zclient_send_reg_requests(zclient, vrf_id); +} + +void bfdd_zclient_unregister(vrf_id_t vrf_id) +{ + if (!zclient || zclient->sock < 0) + return; + zclient_send_dereg_requests(zclient, vrf_id); +} + void bfdd_zclient_stop(void) { zclient_stop(zclient); @@ -757,9 +907,6 @@ static void pc_free(struct ptm_client *pc) { struct ptm_client_notification *pcn; - if (pc == NULL) - return; - TAILQ_REMOVE(&pcqueue, pc, pc_entry); while (!TAILQ_EMPTY(&pc->pc_pcnqueue)) { @@ -821,14 +968,19 @@ static void pcn_free(struct ptm_client_notification *pcn) struct ptm_client *pc; struct bfd_session *bs; - if (pcn == NULL) - return; - /* Handle session de-registration. */ bs = pcn->pcn_bs; pcn->pcn_bs = NULL; bs->refcount--; + /* Log modification to users. */ + if (bglobal.debug_zebra) + zlog_debug("ptm-del-session: [%s] refcount=%" PRIu64, + bs_to_string(bs), bs->refcount); + + /* Set session down. */ + _ptm_bfd_session_del(bs, BD_NEIGHBOR_DOWN); + /* Handle ptm_client deregistration. */ pc = pcn->pcn_pc; pcn->pcn_pc = NULL; diff --git a/bfdd/subdir.am b/bfdd/subdir.am index 334e974b04..a79620c45c 100644 --- a/bfdd/subdir.am +++ b/bfdd/subdir.am @@ -6,30 +6,39 @@ if BFDD noinst_LIBRARIES += bfdd/libbfd.a sbin_PROGRAMS += bfdd/bfdd dist_examples_DATA += bfdd/bfdd.conf.sample -vtysh_scan += $(top_srcdir)/bfdd/bfdd_vty.c -man8 += $(MANBUILD)/bfdd.8 +vtysh_scan += bfdd/bfdd_vty.c +vtysh_scan += bfdd/bfdd_cli.c +man8 += $(MANBUILD)/frr-bfdd.8 endif bfdd_libbfd_a_SOURCES = \ bfdd/bfd.c \ + bfdd/bfdd_nb.c \ + bfdd/bfdd_nb_config.c \ + bfdd/bfdd_nb_state.c \ bfdd/bfdd_vty.c \ + bfdd/bfdd_cli.c \ bfdd/bfd_packet.c \ - bfdd/bsd.c \ bfdd/config.c \ bfdd/control.c \ bfdd/event.c \ - bfdd/linux.c \ - bfdd/log.c \ bfdd/ptm_adapter.c \ # end -bfdd/bfdd_vty_clippy.c: $(CLIPPY_DEPS) -bfdd/bfdd_vty.$(OBJEXT): bfdd/bfdd_vty_clippy.c +clippy_scan += \ + bfdd/bfdd_cli.c \ + bfdd/bfdd_vty.c \ + # end noinst_HEADERS += \ bfdd/bfdctl.h \ + bfdd/bfdd_nb.h \ bfdd/bfd.h \ # end +nodist_bfdd_bfdd_SOURCES = \ + yang/frr-bfdd.yang.c \ + # end + bfdd_bfdd_SOURCES = bfdd/bfdd.c bfdd_bfdd_LDADD = bfdd/libbfd.a lib/libfrr.la diff --git a/bgpd/bgp_addpath.c b/bgpd/bgp_addpath.c index 63373cb9a7..d07d41bef8 100644 --- a/bgpd/bgp_addpath.c +++ b/bgpd/bgp_addpath.c @@ -17,10 +17,14 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include "bgp_addpath.h" #include "bgp_route.h" -static struct bgp_addpath_strategy_names strat_names[BGP_ADDPATH_MAX] = { +static const struct bgp_addpath_strategy_names strat_names[BGP_ADDPATH_MAX] = { { .config_name = "addpath-tx-all-paths", .human_name = "All", @@ -37,7 +41,7 @@ static struct bgp_addpath_strategy_names strat_names[BGP_ADDPATH_MAX] = { } }; -static struct bgp_addpath_strategy_names unknown_names = { +static const struct bgp_addpath_strategy_names unknown_names = { .config_name = "addpath-tx-unknown", .human_name = "Unknown-Addpath-Strategy", .human_description = "Unknown Addpath Strategy", @@ -49,7 +53,7 @@ static struct bgp_addpath_strategy_names unknown_names = { * Returns a structure full of strings associated with an addpath type. Will * never return null. */ -struct bgp_addpath_strategy_names * +const struct bgp_addpath_strategy_names * bgp_addpath_names(enum bgp_addpath_strat strat) { if (strat < BGP_ADDPATH_MAX) @@ -61,8 +65,8 @@ bgp_addpath_names(enum bgp_addpath_strat strat) /* * Returns if any peer is transmitting addpaths for a given afi/safi. */ -int bgp_addpath_is_addpath_used(struct bgp_addpath_bgp_data *d, afi_t afi, - safi_t safi) +bool bgp_addpath_is_addpath_used(struct bgp_addpath_bgp_data *d, afi_t afi, + safi_t safi) { return d->total_peercount[afi][safi] > 0; } @@ -119,15 +123,15 @@ uint32_t bgp_addpath_id_for_peer(struct peer *peer, afi_t afi, safi_t safi, * Returns true if the path has an assigned addpath ID for any of the addpath * strategies. */ -int bgp_addpath_info_has_ids(struct bgp_addpath_info_data *d) +bool bgp_addpath_info_has_ids(struct bgp_addpath_info_data *d) { int i; for (i = 0; i < BGP_ADDPATH_MAX; i++) if (d->addpath_tx_id[i] != 0) - return 1; + return true; - return 0; + return false; } /* @@ -148,7 +152,7 @@ void bgp_addpath_free_node_data(struct bgp_addpath_bgp_data *bd, /* * Check to see if the addpath strategy requires DMED to be configured to work. */ -int bgp_addpath_dmed_required(int strategy) +bool bgp_addpath_dmed_required(int strategy) { return strategy == BGP_ADDPATH_BEST_PER_AS; } @@ -157,34 +161,33 @@ int bgp_addpath_dmed_required(int strategy) * Return true if this is a path we should advertise due to a * configured addpath-tx knob */ -int bgp_addpath_tx_path(enum bgp_addpath_strat strat, - struct bgp_path_info *pi) +bool bgp_addpath_tx_path(enum bgp_addpath_strat strat, struct bgp_path_info *pi) { switch (strat) { case BGP_ADDPATH_NONE: - return 0; + return false; case BGP_ADDPATH_ALL: - return 1; + return true; case BGP_ADDPATH_BEST_PER_AS: if (CHECK_FLAG(pi->flags, BGP_PATH_DMED_SELECTED)) - return 1; + return true; else - return 0; + return false; default: - return 0; + return false; } } static void bgp_addpath_flush_type_rn(struct bgp *bgp, afi_t afi, safi_t safi, enum bgp_addpath_strat addpath_type, - struct bgp_node *rn) + struct bgp_dest *dest) { struct bgp_path_info *pi; idalloc_drain_pool( bgp->tx_addpath.id_allocators[afi][safi][addpath_type], - &(rn->tx_addpath.free_ids[addpath_type])); - for (pi = bgp_node_get_bgp_path_info(rn); pi; pi = pi->next) { + &(dest->tx_addpath.free_ids[addpath_type])); + for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) { if (pi->tx_addpath.addpath_tx_id[addpath_type] != IDALLOC_INVALID) { idalloc_free( @@ -207,24 +210,24 @@ static void bgp_addpath_flush_type_rn(struct bgp *bgp, afi_t afi, safi_t safi, static void bgp_addpath_flush_type(struct bgp *bgp, afi_t afi, safi_t safi, enum bgp_addpath_strat addpath_type) { - struct bgp_node *rn, *nrn; + struct bgp_dest *dest, *ndest; - for (rn = bgp_table_top(bgp->rib[afi][safi]); rn; - rn = bgp_route_next(rn)) { + for (dest = bgp_table_top(bgp->rib[afi][safi]); dest; + dest = bgp_route_next(dest)) { if (safi == SAFI_MPLS_VPN) { struct bgp_table *table; - table = bgp_node_get_bgp_table_info(rn); + table = bgp_dest_get_bgp_table_info(dest); if (!table) continue; - for (nrn = bgp_table_top(table); nrn; - nrn = bgp_route_next(nrn)) + for (ndest = bgp_table_top(table); ndest; + ndest = bgp_route_next(ndest)) bgp_addpath_flush_type_rn(bgp, afi, safi, - addpath_type, nrn); + addpath_type, ndest); } else { bgp_addpath_flush_type_rn(bgp, afi, safi, addpath_type, - rn); + dest); } } @@ -254,7 +257,7 @@ static void bgp_addpath_populate_path(struct id_alloc *allocator, static void bgp_addpath_populate_type(struct bgp *bgp, afi_t afi, safi_t safi, enum bgp_addpath_strat addpath_type) { - struct bgp_node *rn, *nrn; + struct bgp_dest *dest, *ndest; char buf[200]; struct id_alloc *allocator; @@ -273,25 +276,25 @@ static void bgp_addpath_populate_type(struct bgp *bgp, afi_t afi, safi_t safi, allocator = bgp->tx_addpath.id_allocators[afi][safi][addpath_type]; - for (rn = bgp_table_top(bgp->rib[afi][safi]); rn; - rn = bgp_route_next(rn)) { + for (dest = bgp_table_top(bgp->rib[afi][safi]); dest; + dest = bgp_route_next(dest)) { struct bgp_path_info *bi; if (safi == SAFI_MPLS_VPN) { struct bgp_table *table; - table = bgp_node_get_bgp_table_info(rn); + table = bgp_dest_get_bgp_table_info(dest); if (!table) continue; - for (nrn = bgp_table_top(table); nrn; - nrn = bgp_route_next(nrn)) - for (bi = bgp_node_get_bgp_path_info(nrn); bi; + for (ndest = bgp_table_top(table); ndest; + ndest = bgp_route_next(ndest)) + for (bi = bgp_dest_get_bgp_path_info(ndest); bi; bi = bi->next) bgp_addpath_populate_path(allocator, bi, addpath_type); } else { - for (bi = bgp_node_get_bgp_path_info(rn); bi; + for (bi = bgp_dest_get_bgp_path_info(dest); bi; bi = bi->next) bgp_addpath_populate_path(allocator, bi, addpath_type); @@ -317,6 +320,7 @@ void bgp_addpath_type_changed(struct bgp *bgp) for (type=0; typetx_addpath.total_peercount[afi][safi] = 0; } for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { @@ -324,6 +328,7 @@ void bgp_addpath_type_changed(struct bgp *bgp) type = peer->addpath_type[afi][safi]; if (type != BGP_ADDPATH_NONE) { peer_count[afi][safi][type] += 1; + bgp->tx_addpath.total_peercount[afi][safi] += 1; } } } @@ -374,11 +379,13 @@ void bgp_addpath_set_peer_type(struct peer *peer, afi_t afi, safi_t safi, if (addpath_type != BGP_ADDPATH_NONE) { if (bgp_addpath_dmed_required(addpath_type)) { - if (!bgp_flag_check(bgp, BGP_FLAG_DETERMINISTIC_MED)) { + if (!CHECK_FLAG(bgp->flags, + BGP_FLAG_DETERMINISTIC_MED)) { zlog_warn( "%s: enabling bgp deterministic-med, this is required for addpath-tx-bestpath-per-AS", peer->host); - bgp_flag_set(bgp, BGP_FLAG_DETERMINISTIC_MED); + SET_FLAG(bgp->flags, + BGP_FLAG_DETERMINISTIC_MED); bgp_recalculate_all_bestpaths(bgp); } } @@ -419,8 +426,8 @@ void bgp_addpath_set_peer_type(struct peer *peer, afi_t afi, safi_t safi, * best-per-as updates from needing to do a separate withdraw and update just to * swap out which path is sent. */ -void bgp_addpath_update_ids(struct bgp *bgp, struct bgp_node *bn, afi_t afi, - safi_t safi) +void bgp_addpath_update_ids(struct bgp *bgp, struct bgp_dest *bn, afi_t afi, + safi_t safi) { int i; struct bgp_path_info *pi; @@ -435,7 +442,7 @@ void bgp_addpath_update_ids(struct bgp *bgp, struct bgp_node *bn, afi_t afi, continue; /* Free Unused IDs back to the pool.*/ - for (pi = bgp_node_get_bgp_path_info(bn); pi; pi = pi->next) { + for (pi = bgp_dest_get_bgp_path_info(bn); pi; pi = pi->next) { if (pi->tx_addpath.addpath_tx_id[i] != IDALLOC_INVALID && !bgp_addpath_tx_path(i, pi)) { idalloc_free_to_pool(pool_ptr, @@ -446,7 +453,7 @@ void bgp_addpath_update_ids(struct bgp *bgp, struct bgp_node *bn, afi_t afi, } /* Give IDs to paths that need them (pulling from the pool) */ - for (pi = bgp_node_get_bgp_path_info(bn); pi; pi = pi->next) { + for (pi = bgp_dest_get_bgp_path_info(bn); pi; pi = pi->next) { if (pi->tx_addpath.addpath_tx_id[i] == IDALLOC_INVALID && bgp_addpath_tx_path(i, pi)) { pi->tx_addpath.addpath_tx_id[i] = diff --git a/bgpd/bgp_addpath.h b/bgpd/bgp_addpath.h index c0c182791b..0350ee8a76 100644 --- a/bgpd/bgp_addpath.h +++ b/bgpd/bgp_addpath.h @@ -32,8 +32,8 @@ void bgp_addpath_init_bgp_data(struct bgp_addpath_bgp_data *d); -int bgp_addpath_is_addpath_used(struct bgp_addpath_bgp_data *d, afi_t afi, - safi_t safi); +bool bgp_addpath_is_addpath_used(struct bgp_addpath_bgp_data *d, afi_t afi, + safi_t safi); void bgp_addpath_free_node_data(struct bgp_addpath_bgp_data *bd, struct bgp_addpath_node_data *nd, @@ -43,29 +43,29 @@ void bgp_addpath_free_info_data(struct bgp_addpath_info_data *d, struct bgp_addpath_node_data *nd); -int bgp_addpath_info_has_ids(struct bgp_addpath_info_data *d); +bool bgp_addpath_info_has_ids(struct bgp_addpath_info_data *d); uint32_t bgp_addpath_id_for_peer(struct peer *peer, afi_t afi, safi_t safi, struct bgp_addpath_info_data *d); -struct bgp_addpath_strategy_names * +const struct bgp_addpath_strategy_names * bgp_addpath_names(enum bgp_addpath_strat strat); -int bgp_addpath_dmed_required(int strategy); +bool bgp_addpath_dmed_required(int strategy); /* * Return true if this is a path we should advertise due to a configured * addpath-tx knob */ -int bgp_addpath_tx_path(enum bgp_addpath_strat strat, - struct bgp_path_info *pi); +bool bgp_addpath_tx_path(enum bgp_addpath_strat strat, + struct bgp_path_info *pi); /* * Change the type of addpath used for a peer. */ void bgp_addpath_set_peer_type(struct peer *peer, afi_t afi, safi_t safi, enum bgp_addpath_strat addpath_type); -void bgp_addpath_update_ids(struct bgp *bgp, struct bgp_node *bn, afi_t afi, +void bgp_addpath_update_ids(struct bgp *bgp, struct bgp_dest *dest, afi_t afi, safi_t safi); void bgp_addpath_type_changed(struct bgp *bgp); diff --git a/bgpd/bgp_advertise.c b/bgpd/bgp_advertise.c index c9f68d037b..1ebe4e5b53 100644 --- a/bgpd/bgp_advertise.c +++ b/bgpd/bgp_advertise.c @@ -64,9 +64,9 @@ static void *baa_hash_alloc(void *p) return baa; } -unsigned int baa_hash_key(void *p) +unsigned int baa_hash_key(const void *p) { - struct bgp_advertise_attr *baa = (struct bgp_advertise_attr *)p; + const struct bgp_advertise_attr *baa = p; return attrhash_key_make(baa->attr); } @@ -144,8 +144,8 @@ void bgp_advertise_unintern(struct hash *hash, struct bgp_advertise_attr *baa) } } -int bgp_adj_out_lookup(struct peer *peer, struct bgp_node *rn, - uint32_t addpath_tx_id) +bool bgp_adj_out_lookup(struct peer *peer, struct bgp_dest *dest, + uint32_t addpath_tx_id) { struct bgp_adj_out *adj; struct peer_af *paf; @@ -153,7 +153,7 @@ int bgp_adj_out_lookup(struct peer *peer, struct bgp_node *rn, safi_t safi; int addpath_capable; - RB_FOREACH (adj, bgp_adj_out_rb, &rn->adj_out) + RB_FOREACH (adj, bgp_adj_out_rb, &dest->adj_out) SUBGRP_FOREACH_PEER (adj->subgroup, paf) if (paf->peer == peer) { afi = SUBGRP_AFI(adj->subgroup); @@ -169,20 +169,21 @@ int bgp_adj_out_lookup(struct peer *peer, struct bgp_node *rn, && adj->addpath_tx_id != addpath_tx_id) continue; - return (adj->adv ? (adj->adv->baa ? 1 : 0) - : (adj->attr ? 1 : 0)); + return (adj->adv + ? (adj->adv->baa ? true : false) + : (adj->attr ? true : false)); } - return 0; + return false; } -void bgp_adj_in_set(struct bgp_node *rn, struct peer *peer, struct attr *attr, +void bgp_adj_in_set(struct bgp_dest *dest, struct peer *peer, struct attr *attr, uint32_t addpath_id) { struct bgp_adj_in *adj; - for (adj = rn->adj_in; adj; adj = adj->next) { + for (adj = dest->adj_in; adj; adj = adj->next) { if (adj->peer == peer && adj->addpath_rx_id == addpath_id) { if (adj->attr != attr) { bgp_attr_unintern(&adj->attr); @@ -194,42 +195,43 @@ void bgp_adj_in_set(struct bgp_node *rn, struct peer *peer, struct attr *attr, adj = XCALLOC(MTYPE_BGP_ADJ_IN, sizeof(struct bgp_adj_in)); adj->peer = peer_lock(peer); /* adj_in peer reference */ adj->attr = bgp_attr_intern(attr); + adj->uptime = bgp_clock(); adj->addpath_rx_id = addpath_id; - BGP_ADJ_IN_ADD(rn, adj); - bgp_lock_node(rn); + BGP_ADJ_IN_ADD(dest, adj); + bgp_dest_lock_node(dest); } -void bgp_adj_in_remove(struct bgp_node *rn, struct bgp_adj_in *bai) +void bgp_adj_in_remove(struct bgp_dest *dest, struct bgp_adj_in *bai) { bgp_attr_unintern(&bai->attr); - BGP_ADJ_IN_DEL(rn, bai); + BGP_ADJ_IN_DEL(dest, bai); peer_unlock(bai->peer); /* adj_in peer reference */ XFREE(MTYPE_BGP_ADJ_IN, bai); } -int bgp_adj_in_unset(struct bgp_node *rn, struct peer *peer, - uint32_t addpath_id) +bool bgp_adj_in_unset(struct bgp_dest *dest, struct peer *peer, + uint32_t addpath_id) { struct bgp_adj_in *adj; struct bgp_adj_in *adj_next; - adj = rn->adj_in; + adj = dest->adj_in; if (!adj) - return 0; + return false; while (adj) { adj_next = adj->next; if (adj->peer == peer && adj->addpath_rx_id == addpath_id) { - bgp_adj_in_remove(rn, adj); - bgp_unlock_node(rn); + bgp_adj_in_remove(dest, adj); + bgp_dest_unlock_node(dest); } adj = adj_next; } - return 1; + return true; } void bgp_sync_init(struct peer *peer) @@ -255,6 +257,5 @@ void bgp_sync_delete(struct peer *peer) FOREACH_AFI_SAFI (afi, safi) { XFREE(MTYPE_BGP_SYNCHRONISE, peer->sync[afi][safi]); - peer->sync[afi][safi] = NULL; } } diff --git a/bgpd/bgp_advertise.h b/bgpd/bgp_advertise.h index cc845b93e7..f2cf78efde 100644 --- a/bgpd/bgp_advertise.h +++ b/bgpd/bgp_advertise.h @@ -23,7 +23,7 @@ #include "lib/typesafe.h" -PREDECL_LIST(bgp_adv_fifo) +PREDECL_DLIST(bgp_adv_fifo) struct update_subgroup; @@ -48,7 +48,7 @@ struct bgp_advertise { struct bgp_advertise *prev; /* Prefix information. */ - struct bgp_node *rn; + struct bgp_dest *dest; /* Reference pointer. */ struct bgp_adj_out *adj; @@ -60,7 +60,7 @@ struct bgp_advertise { struct bgp_path_info *pathi; }; -DECLARE_LIST(bgp_adv_fifo, struct bgp_advertise, fifo) +DECLARE_DLIST(bgp_adv_fifo, struct bgp_advertise, fifo) /* BGP adjacency out. */ struct bgp_adj_out { @@ -74,7 +74,7 @@ struct bgp_adj_out { TAILQ_ENTRY(bgp_adj_out) subgrp_adj_train; /* Prefix information. */ - struct bgp_node *rn; + struct bgp_dest *dest; uint32_t addpath_tx_id; @@ -101,6 +101,9 @@ struct bgp_adj_in { /* Received attribute. */ struct attr *attr; + /* timestamp (monotime) */ + time_t uptime; + /* Addpath identifier */ uint32_t addpath_rx_id; }; @@ -136,15 +139,15 @@ struct bgp_synchronize { #define BGP_ADJ_IN_DEL(N, A) BGP_PATH_INFO_DEL(N, A, adj_in) /* Prototypes. */ -extern int bgp_adj_out_lookup(struct peer *, struct bgp_node *, uint32_t); -extern void bgp_adj_in_set(struct bgp_node *, struct peer *, struct attr *, +extern bool bgp_adj_out_lookup(struct peer *, struct bgp_dest *, uint32_t); +extern void bgp_adj_in_set(struct bgp_dest *, struct peer *, struct attr *, uint32_t); -extern int bgp_adj_in_unset(struct bgp_node *, struct peer *, uint32_t); -extern void bgp_adj_in_remove(struct bgp_node *, struct bgp_adj_in *); +extern bool bgp_adj_in_unset(struct bgp_dest *, struct peer *, uint32_t); +extern void bgp_adj_in_remove(struct bgp_dest *, struct bgp_adj_in *); extern void bgp_sync_init(struct peer *); extern void bgp_sync_delete(struct peer *); -extern unsigned int baa_hash_key(void *p); +extern unsigned int baa_hash_key(const void *p); extern bool baa_hash_cmp(const void *p1, const void *p2); extern void bgp_advertise_add(struct bgp_advertise_attr *baa, struct bgp_advertise *adv); diff --git a/bgpd/bgp_aspath.c b/bgpd/bgp_aspath.c index 92c37fabd2..5cf3c60fa2 100644 --- a/bgpd/bgp_aspath.c +++ b/bgpd/bgp_aspath.c @@ -41,9 +41,9 @@ #define AS_HEADER_SIZE 2 /* Now FOUR octets are used for AS value. */ -#define AS_VALUE_SIZE sizeof (as_t) +#define AS_VALUE_SIZE sizeof(as_t) /* This is the old one */ -#define AS16_VALUE_SIZE sizeof (as16_t) +#define AS16_VALUE_SIZE sizeof(as16_t) /* Maximum protocol segment length value */ #define AS_SEGMENT_MAX 255 @@ -102,8 +102,10 @@ static void assegment_data_free(as_t *asdata) XFREE(MTYPE_AS_SEG_DATA, asdata); } -const char *aspath_segment_type_str[] = {"as-invalid", "as-set", "as-sequence", - "as-confed-sequence", "as-confed-set"}; +const char *const aspath_segment_type_str[] = { + "as-invalid", "as-set", "as-sequence", "as-confed-sequence", + "as-confed-set" +}; /* Get a new segment. Note that 0 is an allowed length, * and will result in a segment with no allocated data segment. @@ -130,8 +132,7 @@ static void assegment_free(struct assegment *seg) if (!seg) return; - if (seg->as) - assegment_data_free(seg->as); + assegment_data_free(seg->as); memset(seg, 0xfe, sizeof(struct assegment)); XFREE(MTYPE_AS_SEG, seg); @@ -414,6 +415,35 @@ unsigned int aspath_count_hops(const struct aspath *aspath) return count; } +/* Check if aspath has AS_SET or AS_CONFED_SET */ +bool aspath_check_as_sets(struct aspath *aspath) +{ + struct assegment *seg = aspath->segments; + + while (seg) { + if (seg->type == AS_SET || seg->type == AS_CONFED_SET) + return true; + seg = seg->next; + } + return false; +} + +/* Check if aspath has BGP_AS_ZERO */ +bool aspath_check_as_zero(struct aspath *aspath) +{ + struct assegment *seg = aspath->segments; + unsigned int i; + + while (seg) { + for (i = 0; i < seg->length; i++) + if (seg->as[i] == BGP_AS_ZERO) + return true; + seg = seg->next; + } + + return false; +} + /* Estimate size aspath /might/ take if encoded into an * ASPATH attribute. * @@ -462,7 +492,7 @@ as_t aspath_leftmost(struct aspath *aspath) } /* Return 1 if there are any 4-byte ASes in the path */ -unsigned int aspath_has_as4(struct aspath *aspath) +bool aspath_has_as4(struct aspath *aspath) { struct assegment *seg = aspath->segments; unsigned int i; @@ -470,10 +500,10 @@ unsigned int aspath_has_as4(struct aspath *aspath) while (seg) { for (i = 0; i < seg->length; i++) if (seg->as[i] > BGP_AS_MAX) - return 1; + return true; seg = seg->next; } - return 0; + return false; } /* Convert aspath structure to string expression. */ @@ -576,7 +606,7 @@ static void aspath_make_str_count(struct aspath *as, bool make_json) if (make_json) json_object_array_add( jseg_list, - json_object_new_int(seg->as[i])); + json_object_new_int64(seg->as[i])); len += snprintf(str_buf + len, str_size - len, "%u", seg->as[i]); @@ -753,7 +783,7 @@ static int assegments_parse(struct stream *s, size_t length, * on more, than 8 bits (otherwise it's a warning, bug * #564). */ - || ((sizeof segh.length > 1) + || ((sizeof(segh.length) > 1) && (0x10 + segh.length > 0x10 + AS_SEGMENT_MAX))) { if (head) assegment_free_all(head); @@ -778,7 +808,7 @@ static int assegments_parse(struct stream *s, size_t length, if (head) prev->next = seg; else /* it's the first segment */ - head = prev = seg; + head = seg; for (i = 0; i < segh.length; i++) seg->as[i] = @@ -1099,16 +1129,16 @@ struct aspath *aspath_aggregate(struct aspath *as1, struct aspath *as2) /* When a BGP router receives an UPDATE with an MP_REACH_NLRI attribute, check the leftmost AS number in the AS_PATH attribute is or not the peer's AS number. */ -int aspath_firstas_check(struct aspath *aspath, as_t asno) +bool aspath_firstas_check(struct aspath *aspath, as_t asno) { if ((aspath == NULL) || (aspath->segments == NULL)) - return 0; + return false; if (aspath->segments && (aspath->segments->type == AS_SEQUENCE) && (aspath->segments->as[0] == asno)) - return 1; + return true; - return 0; + return false; } unsigned int aspath_get_first_as(struct aspath *aspath) @@ -1164,12 +1194,12 @@ int aspath_loop_check(struct aspath *aspath, as_t asno) } /* When all of AS path is private AS return 1. */ -int aspath_private_as_check(struct aspath *aspath) +bool aspath_private_as_check(struct aspath *aspath) { struct assegment *seg; if (!(aspath && aspath->segments)) - return 0; + return false; seg = aspath->segments; @@ -1178,20 +1208,20 @@ int aspath_private_as_check(struct aspath *aspath) for (i = 0; i < seg->length; i++) { if (!BGP_AS_IS_PRIVATE(seg->as[i])) - return 0; + return false; } seg = seg->next; } - return 1; + return true; } /* Return True if the entire ASPATH consist of the specified ASN */ -int aspath_single_asn_check(struct aspath *aspath, as_t asn) +bool aspath_single_asn_check(struct aspath *aspath, as_t asn) { struct assegment *seg; if (!(aspath && aspath->segments)) - return 0; + return false; seg = aspath->segments; @@ -1200,11 +1230,11 @@ int aspath_single_asn_check(struct aspath *aspath, as_t asn) for (i = 0; i < seg->length; i++) { if (seg->as[i] != asn) - return 0; + return false; } seg = seg->next; } - return 1; + return true; } /* Replace all instances of the target ASN with our own ASN */ @@ -1232,7 +1262,8 @@ struct aspath *aspath_replace_specific_asn(struct aspath *aspath, } /* Replace all private ASNs with our own ASN */ -struct aspath *aspath_replace_private_asns(struct aspath *aspath, as_t asn) +struct aspath *aspath_replace_private_asns(struct aspath *aspath, as_t asn, + as_t peer_asn) { struct aspath *new; struct assegment *seg; @@ -1244,7 +1275,9 @@ struct aspath *aspath_replace_private_asns(struct aspath *aspath, as_t asn) int i; for (i = 0; i < seg->length; i++) { - if (BGP_AS_IS_PRIVATE(seg->as[i])) + /* Don't replace if public ASN or peer's ASN */ + if (BGP_AS_IS_PRIVATE(seg->as[i]) + && (seg->as[i] != peer_asn)) seg->as[i] = asn; } seg = seg->next; @@ -1255,7 +1288,7 @@ struct aspath *aspath_replace_private_asns(struct aspath *aspath, as_t asn) } /* Remove all private ASNs */ -struct aspath *aspath_remove_private_asns(struct aspath *aspath) +struct aspath *aspath_remove_private_asns(struct aspath *aspath, as_t peer_asn) { struct aspath *new; struct assegment *seg; @@ -1282,16 +1315,9 @@ struct aspath *aspath_remove_private_asns(struct aspath *aspath) } } - // The entire segment is private so skip it - if (!public) { - seg = seg->next; - continue; - } - // The entire segment is public so copy it - else if (public == seg->length) { + if (public == seg->length) new_seg = assegment_dup(seg); - } // The segment is a mix of public and private ASNs. Copy as many // spots as @@ -1301,8 +1327,9 @@ struct aspath *aspath_remove_private_asns(struct aspath *aspath) new_seg = assegment_new(seg->type, public); j = 0; for (i = 0; i < seg->length; i++) { - // ASN is public - if (!BGP_AS_IS_PRIVATE(seg->as[i])) { + // keep ASN if public or matches peer's ASN + if (!BGP_AS_IS_PRIVATE(seg->as[i]) + || (seg->as[i] == peer_asn)) { new_seg->as[j] = seg->as[i]; j++; } @@ -1326,37 +1353,37 @@ struct aspath *aspath_remove_private_asns(struct aspath *aspath) /* AS path confed check. If aspath contains confed set or sequence then return * 1. */ -int aspath_confed_check(struct aspath *aspath) +bool aspath_confed_check(struct aspath *aspath) { struct assegment *seg; if (!(aspath && aspath->segments)) - return 0; + return false; seg = aspath->segments; while (seg) { if (seg->type == AS_CONFED_SET || seg->type == AS_CONFED_SEQUENCE) - return 1; + return true; seg = seg->next; } - return 0; + return false; } /* Leftmost AS path segment confed check. If leftmost AS segment is of type AS_CONFED_SEQUENCE or AS_CONFED_SET then return 1. */ -int aspath_left_confed_check(struct aspath *aspath) +bool aspath_left_confed_check(struct aspath *aspath) { if (!(aspath && aspath->segments)) - return 0; + return false; if ((aspath->segments->type == AS_CONFED_SEQUENCE) || (aspath->segments->type == AS_CONFED_SET)) - return 1; + return true; - return 0; + return false; } /* Merge as1 to as2. as2 should be uninterned aspath. */ @@ -1469,7 +1496,7 @@ struct aspath *aspath_prepend(struct aspath *as1, struct aspath *as2) /* Not reached */ } -/* Iterate over AS_PATH segments and wipe all occurences of the +/* Iterate over AS_PATH segments and wipe all occurrences of the * listed AS numbers. Hence some segments may lose some or even * all data on the way, the operation is implemented as a smarter * version of aspath_dup(), which allocates memory to hold the new @@ -1593,13 +1620,13 @@ struct aspath *aspath_add_seq(struct aspath *aspath, as_t asno) /* Compare leftmost AS value for MED check. If as1's leftmost AS and as2's leftmost AS is same return 1. */ -int aspath_cmp_left(const struct aspath *aspath1, const struct aspath *aspath2) +bool aspath_cmp_left(const struct aspath *aspath1, const struct aspath *aspath2) { const struct assegment *seg1; const struct assegment *seg2; if (!(aspath1 && aspath2)) - return 0; + return false; seg1 = aspath1->segments; seg2 = aspath2->segments; @@ -1607,7 +1634,7 @@ int aspath_cmp_left(const struct aspath *aspath1, const struct aspath *aspath2) /* If both paths are originated in this AS then we do want to compare * MED */ if (!seg1 && !seg2) - return 1; + return true; /* find first non-confed segments for each */ while (seg1 && ((seg1->type == AS_CONFED_SEQUENCE) @@ -1621,12 +1648,12 @@ int aspath_cmp_left(const struct aspath *aspath1, const struct aspath *aspath2) /* Check as1's */ if (!(seg1 && seg2 && (seg1->type == AS_SEQUENCE) && (seg2->type == AS_SEQUENCE))) - return 0; + return false; if (seg1->as[0] == seg2->as[0]) - return 1; + return true; - return 0; + return false; } /* Truncate an aspath after a number of hops, and put the hops remaining @@ -1696,8 +1723,7 @@ struct aspath *aspath_reconcile_as4(struct aspath *aspath, if (hops < seg->length) { if (BGP_DEBUG(as4, AS4)) zlog_debug( - "[AS4] AS4PATHmangle: AS_CONFED_SEQUENCE falls" - " across 2/4 ASN boundary somewhere, broken.."); + "[AS4] AS4PATHmangle: AS_CONFED_SEQUENCE falls across 2/4 ASN boundary somewhere, broken.."); hops = seg->length; } /* fallthru */ @@ -1888,7 +1914,7 @@ static const char *aspath_gettoken(const char *buf, enum as_token *token, const char *p = buf; /* Skip seperators (space for sequences, ',' for sets). */ - while (isspace((int)*p) || *p == ',') + while (isspace((unsigned char)*p) || *p == ',') p++; /* Check the end of the string and type specify characters @@ -1923,14 +1949,14 @@ static const char *aspath_gettoken(const char *buf, enum as_token *token, } /* Check actual AS value. */ - if (isdigit((int)*p)) { + if (isdigit((unsigned char)*p)) { as_t asval; *token = as_token_asval; asval = (*p - '0'); p++; - while (isdigit((int)*p)) { + while (isdigit((unsigned char)*p)) { asval *= 10; asval += (*p - '0'); p++; @@ -2008,13 +2034,13 @@ struct aspath *aspath_str2aspath(const char *str) } /* Make hash value by raw aspath data. */ -unsigned int aspath_key_make(void *p) +unsigned int aspath_key_make(const void *p) { - struct aspath *aspath = (struct aspath *)p; + const struct aspath *aspath = p; unsigned int key = 0; if (!aspath->str) - aspath_str_update(aspath, false); + aspath_str_update((struct aspath *)aspath, false); key = jhash(aspath->str, aspath->str_len, 2334325); @@ -2115,17 +2141,14 @@ static void *bgp_aggr_aspath_hash_alloc(void *p) return aspath; } -static void bgp_aggr_aspath_prepare(struct hash_backet *hb, void *arg) +static void bgp_aggr_aspath_prepare(struct hash_bucket *hb, void *arg) { - struct aspath *asmerge = NULL; struct aspath *hb_aspath = hb->data; struct aspath **aggr_aspath = arg; - if (*aggr_aspath) { - asmerge = aspath_aggregate(*aggr_aspath, hb_aspath); - aspath_free(*aggr_aspath); - *aggr_aspath = asmerge; - } else + if (*aggr_aspath) + *aggr_aspath = aspath_aggregate(*aggr_aspath, hb_aspath); + else *aggr_aspath = aspath_dup(hb_aspath); } @@ -2138,6 +2161,15 @@ void bgp_aggr_aspath_remove(void *arg) void bgp_compute_aggregate_aspath(struct bgp_aggregate *aggregate, struct aspath *aspath) +{ + bgp_compute_aggregate_aspath_hash(aggregate, aspath); + + bgp_compute_aggregate_aspath_val(aggregate); + +} + +void bgp_compute_aggregate_aspath_hash(struct bgp_aggregate *aggregate, + struct aspath *aspath) { struct aspath *aggr_aspath = NULL; @@ -2157,17 +2189,29 @@ void bgp_compute_aggregate_aspath(struct bgp_aggregate *aggregate, */ aggr_aspath = hash_get(aggregate->aspath_hash, aspath, bgp_aggr_aspath_hash_alloc); + } - /* Compute aggregate's as-path. - */ + /* Increment reference counter. + */ + aggr_aspath->refcnt++; +} + +void bgp_compute_aggregate_aspath_val(struct bgp_aggregate *aggregate) +{ + if (aggregate == NULL) + return; + /* Re-compute aggregate's as-path. + */ + if (aggregate->aspath) { + aspath_free(aggregate->aspath); + aggregate->aspath = NULL; + } + if (aggregate->aspath_hash + && aggregate->aspath_hash->count) { hash_iterate(aggregate->aspath_hash, bgp_aggr_aspath_prepare, &aggregate->aspath); } - - /* Increment refernce counter. - */ - aggr_aspath->refcnt++; } void bgp_remove_aspath_from_aggregate(struct bgp_aggregate *aggregate, @@ -2176,10 +2220,9 @@ void bgp_remove_aspath_from_aggregate(struct bgp_aggregate *aggregate, struct aspath *aggr_aspath = NULL; struct aspath *ret_aspath = NULL; - if ((aggregate == NULL) || (aspath == NULL)) - return; - - if (aggregate->aspath_hash == NULL) + if ((!aggregate) + || (!aggregate->aspath_hash) + || (!aspath)) return; /* Look-up the aspath in the hash. @@ -2192,17 +2235,41 @@ void bgp_remove_aspath_from_aggregate(struct bgp_aggregate *aggregate, ret_aspath = hash_release(aggregate->aspath_hash, aggr_aspath); aspath_free(ret_aspath); + ret_aspath = NULL; /* Remove aggregate's old as-path. */ aspath_free(aggregate->aspath); aggregate->aspath = NULL; - /* Compute aggregate's as-path. - */ - hash_iterate(aggregate->aspath_hash, - bgp_aggr_aspath_prepare, - &aggregate->aspath); + bgp_compute_aggregate_aspath_val(aggregate); + } + } +} + +void bgp_remove_aspath_from_aggregate_hash(struct bgp_aggregate *aggregate, + struct aspath *aspath) +{ + struct aspath *aggr_aspath = NULL; + struct aspath *ret_aspath = NULL; + + if ((!aggregate) + || (!aggregate->aspath_hash) + || (!aspath)) + return; + + /* Look-up the aspath in the hash. + */ + aggr_aspath = bgp_aggr_aspath_lookup(aggregate, aspath); + if (aggr_aspath) { + aggr_aspath->refcnt--; + + if (aggr_aspath->refcnt == 0) { + ret_aspath = hash_release(aggregate->aspath_hash, + aggr_aspath); + aspath_free(ret_aspath); + ret_aspath = NULL; } } } + diff --git a/bgpd/bgp_aspath.h b/bgpd/bgp_aspath.h index be5725c1ae..9df352fcd6 100644 --- a/bgpd/bgp_aspath.h +++ b/bgpd/bgp_aspath.h @@ -39,6 +39,7 @@ #define BGP_PRIVATE_AS4_MAX 4294967294U /* we leave BGP_AS_MAX as the 16bit AS MAX number. */ +#define BGP_AS_ZERO 0 #define BGP_AS_MAX 65535U #define BGP_AS4_MAX 4294967295U /* Transition 16Bit AS as defined by IANA */ @@ -87,7 +88,7 @@ extern struct aspath *aspath_add_seq_n(struct aspath *, as_t, unsigned); extern struct aspath *aspath_add_seq(struct aspath *, as_t); extern struct aspath *aspath_add_confed_seq(struct aspath *, as_t); extern bool aspath_cmp(const void *as1, const void *as2); -extern int aspath_cmp_left(const struct aspath *, const struct aspath *); +extern bool aspath_cmp_left(const struct aspath *, const struct aspath *); extern bool aspath_cmp_left_confed(const struct aspath *as1, const struct aspath *as2xs); extern struct aspath *aspath_delete_confed_seq(struct aspath *); @@ -102,23 +103,26 @@ extern const char *aspath_print(struct aspath *); extern void aspath_print_vty(struct vty *, const char *, struct aspath *, const char *); extern void aspath_print_all_vty(struct vty *); -extern unsigned int aspath_key_make(void *); +extern unsigned int aspath_key_make(const void *); extern unsigned int aspath_get_first_as(struct aspath *); extern unsigned int aspath_get_last_as(struct aspath *); extern int aspath_loop_check(struct aspath *, as_t); -extern int aspath_private_as_check(struct aspath *); -extern int aspath_single_asn_check(struct aspath *, as_t asn); +extern bool aspath_private_as_check(struct aspath *); +extern bool aspath_single_asn_check(struct aspath *, as_t asn); extern struct aspath *aspath_replace_specific_asn(struct aspath *aspath, as_t target_asn, as_t our_asn); extern struct aspath *aspath_replace_private_asns(struct aspath *aspath, - as_t asn); -extern struct aspath *aspath_remove_private_asns(struct aspath *aspath); -extern int aspath_firstas_check(struct aspath *, as_t); -extern int aspath_confed_check(struct aspath *); -extern int aspath_left_confed_check(struct aspath *); + as_t asn, as_t peer_asn); +extern struct aspath *aspath_remove_private_asns(struct aspath *aspath, + as_t peer_asn); +extern bool aspath_firstas_check(struct aspath *, as_t); +extern bool aspath_confed_check(struct aspath *); +extern bool aspath_left_confed_check(struct aspath *); extern unsigned long aspath_count(void); extern unsigned int aspath_count_hops(const struct aspath *); +extern bool aspath_check_as_sets(struct aspath *aspath); +extern bool aspath_check_as_zero(struct aspath *aspath); extern unsigned int aspath_count_confeds(struct aspath *); extern unsigned int aspath_size(struct aspath *); extern as_t aspath_highest(struct aspath *); @@ -126,15 +130,23 @@ extern as_t aspath_leftmost(struct aspath *); extern size_t aspath_put(struct stream *, struct aspath *, int); extern struct aspath *aspath_reconcile_as4(struct aspath *, struct aspath *); -extern unsigned int aspath_has_as4(struct aspath *); +extern bool aspath_has_as4(struct aspath *); /* For SNMP BGP4PATHATTRASPATHSEGMENT, might be useful for debug */ extern uint8_t *aspath_snmp_pathseg(struct aspath *, size_t *); extern void bgp_compute_aggregate_aspath(struct bgp_aggregate *aggregate, struct aspath *aspath); + +extern void bgp_compute_aggregate_aspath_hash(struct bgp_aggregate *aggregate, + struct aspath *aspath); +extern void bgp_compute_aggregate_aspath_val(struct bgp_aggregate *aggregate); extern void bgp_remove_aspath_from_aggregate(struct bgp_aggregate *aggregate, struct aspath *aspath); +extern void bgp_remove_aspath_from_aggregate_hash( + struct bgp_aggregate *aggregate, + struct aspath *aspath); + extern void bgp_aggr_aspath_remove(void *arg); #endif /* _QUAGGA_BGP_ASPATH_H */ diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index 167ad89a59..f5e54267d4 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -32,6 +32,7 @@ #include "table.h" #include "filter.h" #include "command.h" +#include "srv6.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_attr.h" @@ -46,12 +47,11 @@ #include "bgpd/bgp_lcommunity.h" #include "bgpd/bgp_updgrp.h" #include "bgpd/bgp_encap_types.h" -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC #include "bgpd/rfapi/bgp_rfapi_cfg.h" #include "bgp_encap_types.h" #include "bgp_vnc_types.h" #endif -#include "bgp_encap_types.h" #include "bgp_evpn.h" #include "bgp_flowspec_private.h" #include "bgp_mac.h" @@ -79,11 +79,12 @@ static const struct message attr_str[] = { {BGP_ATTR_AS_PATHLIMIT, "AS_PATHLIMIT"}, {BGP_ATTR_PMSI_TUNNEL, "PMSI_TUNNEL_ATTRIBUTE"}, {BGP_ATTR_ENCAP, "ENCAP"}, -#if ENABLE_BGP_VNC_ATTR +#ifdef ENABLE_BGP_VNC_ATTR {BGP_ATTR_VNC, "VNC"}, #endif {BGP_ATTR_LARGE_COMMUNITIES, "LARGE_COMMUNITY"}, {BGP_ATTR_PREFIX_SID, "PREFIX_SID"}, + {BGP_ATTR_IPV6_EXT_COMMUNITIES, "IPV6_EXT_COMMUNITIES"}, {0}}; static const struct message attr_flag_str[] = { @@ -119,28 +120,28 @@ static void *cluster_hash_alloc(void *p) /* Cluster list related functions. */ static struct cluster_list *cluster_parse(struct in_addr *pnt, int length) { - struct cluster_list tmp; + struct cluster_list tmp = {}; struct cluster_list *cluster; tmp.length = length; - tmp.list = pnt; + tmp.list = length == 0 ? NULL : pnt; cluster = hash_get(cluster_hash, &tmp, cluster_hash_alloc); cluster->refcnt++; return cluster; } -int cluster_loop_check(struct cluster_list *cluster, struct in_addr originator) +bool cluster_loop_check(struct cluster_list *cluster, struct in_addr originator) { int i; for (i = 0; i < cluster->length / 4; i++) if (cluster->list[i].s_addr == originator.s_addr) - return 1; - return 0; + return true; + return false; } -static unsigned int cluster_hash_key_make(void *p) +static unsigned int cluster_hash_key_make(const void *p) { const struct cluster_list *cluster = p; @@ -152,9 +153,16 @@ static bool cluster_hash_cmp(const void *p1, const void *p2) const struct cluster_list *cluster1 = p1; const struct cluster_list *cluster2 = p2; - return (cluster1->length == cluster2->length - && memcmp(cluster1->list, cluster2->list, cluster1->length) - == 0); + if (cluster1->list == cluster2->list) + return true; + + if (!cluster1->list || !cluster2->list) + return false; + + if (cluster1->length != cluster2->length) + return false; + + return (memcmp(cluster1->list, cluster2->list, cluster1->length) == 0); } static void cluster_free(struct cluster_list *cluster) @@ -173,14 +181,16 @@ static struct cluster_list *cluster_intern(struct cluster_list *cluster) return find; } -void cluster_unintern(struct cluster_list *cluster) +static void cluster_unintern(struct cluster_list **cluster) { - if (cluster->refcnt) - cluster->refcnt--; - - if (cluster->refcnt == 0) { - hash_release(cluster_hash, cluster); - cluster_free(cluster); + if ((*cluster)->refcnt) + (*cluster)->refcnt--; + + if ((*cluster)->refcnt == 0) { + void *p = hash_release(cluster_hash, *cluster); + assert(p == *cluster); + cluster_free(*cluster); + *cluster = NULL; } } @@ -198,9 +208,11 @@ static void cluster_finish(void) } static struct hash *encap_hash = NULL; -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC static struct hash *vnc_hash = NULL; #endif +static struct hash *srv6_l3vpn_hash; +static struct hash *srv6_vpn_hash; struct bgp_attr_encap_subtlv *encap_tlv_dup(struct bgp_attr_encap_subtlv *orig) { @@ -244,7 +256,7 @@ void bgp_attr_flush_encap(struct attr *attr) encap_free(attr->encap_subtlvs); attr->encap_subtlvs = NULL; } -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC if (attr->vnc_subtlvs) { encap_free(attr->vnc_subtlvs); attr->vnc_subtlvs = NULL; @@ -260,16 +272,16 @@ void bgp_attr_flush_encap(struct attr *attr) * * This algorithm could be made faster if needed */ -static int encap_same(const struct bgp_attr_encap_subtlv *h1, - const struct bgp_attr_encap_subtlv *h2) +static bool encap_same(const struct bgp_attr_encap_subtlv *h1, + const struct bgp_attr_encap_subtlv *h2) { const struct bgp_attr_encap_subtlv *p; const struct bgp_attr_encap_subtlv *q; if (h1 == h2) - return 1; + return true; if (h1 == NULL || h2 == NULL) - return 0; + return false; for (p = h1; p; p = p->next) { for (q = h2; q; q = q->next) { @@ -280,7 +292,7 @@ static int encap_same(const struct bgp_attr_encap_subtlv *h1, } } if (!q) - return 0; + return false; } for (p = h2; p; p = p->next) { @@ -292,10 +304,10 @@ static int encap_same(const struct bgp_attr_encap_subtlv *h1, } } if (!q) - return 0; + return false; } - return 1; + return true; } static void *encap_hash_alloc(void *p) @@ -306,7 +318,7 @@ static void *encap_hash_alloc(void *p) typedef enum { ENCAP_SUBTLV_TYPE, -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC VNC_SUBTLV_TYPE #endif } encap_subtlv_type; @@ -316,7 +328,7 @@ encap_intern(struct bgp_attr_encap_subtlv *encap, encap_subtlv_type type) { struct bgp_attr_encap_subtlv *find; struct hash *hash = encap_hash; -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC if (type == VNC_SUBTLV_TYPE) hash = vnc_hash; #endif @@ -338,7 +350,7 @@ static void encap_unintern(struct bgp_attr_encap_subtlv **encapp, if (encap->refcnt == 0) { struct hash *hash = encap_hash; -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC if (type == VNC_SUBTLV_TYPE) hash = vnc_hash; #endif @@ -348,7 +360,7 @@ static void encap_unintern(struct bgp_attr_encap_subtlv **encapp, } } -static unsigned int encap_hash_key_make(void *p) +static unsigned int encap_hash_key_make(const void *p) { const struct bgp_attr_encap_subtlv *encap = p; @@ -365,7 +377,7 @@ static void encap_init(void) { encap_hash = hash_create(encap_hash_key_make, encap_hash_cmp, "BGP Encap Hash"); -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC vnc_hash = hash_create(encap_hash_key_make, encap_hash_cmp, "BGP VNC Hash"); #endif @@ -376,7 +388,7 @@ static void encap_finish(void) hash_clean(encap_hash, (void (*)(void *))encap_free); hash_free(encap_hash); encap_hash = NULL; -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC hash_clean(vnc_hash, (void (*)(void *))encap_free); hash_free(vnc_hash); vnc_hash = NULL; @@ -422,18 +434,171 @@ static struct transit *transit_intern(struct transit *transit) return find; } -void transit_unintern(struct transit *transit) +static void transit_unintern(struct transit **transit) { - if (transit->refcnt) - transit->refcnt--; + if ((*transit)->refcnt) + (*transit)->refcnt--; - if (transit->refcnt == 0) { - hash_release(transit_hash, transit); - transit_free(transit); + if ((*transit)->refcnt == 0) { + hash_release(transit_hash, *transit); + transit_free(*transit); + *transit = NULL; + } +} + +static void *srv6_l3vpn_hash_alloc(void *p) +{ + return p; +} + +static void srv6_l3vpn_free(struct bgp_attr_srv6_l3vpn *l3vpn) +{ + XFREE(MTYPE_BGP_SRV6_L3VPN, l3vpn); +} + +static struct bgp_attr_srv6_l3vpn * +srv6_l3vpn_intern(struct bgp_attr_srv6_l3vpn *l3vpn) +{ + struct bgp_attr_srv6_l3vpn *find; + + find = hash_get(srv6_l3vpn_hash, l3vpn, srv6_l3vpn_hash_alloc); + if (find != l3vpn) + srv6_l3vpn_free(l3vpn); + find->refcnt++; + return find; +} + +static void srv6_l3vpn_unintern(struct bgp_attr_srv6_l3vpn **l3vpnp) +{ + struct bgp_attr_srv6_l3vpn *l3vpn = *l3vpnp; + + if (l3vpn->refcnt) + l3vpn->refcnt--; + + if (l3vpn->refcnt == 0) { + hash_release(srv6_l3vpn_hash, l3vpn); + srv6_l3vpn_free(l3vpn); + *l3vpnp = NULL; + } +} + +static void *srv6_vpn_hash_alloc(void *p) +{ + return p; +} + +static void srv6_vpn_free(struct bgp_attr_srv6_vpn *vpn) +{ + XFREE(MTYPE_BGP_SRV6_VPN, vpn); +} + +static struct bgp_attr_srv6_vpn *srv6_vpn_intern(struct bgp_attr_srv6_vpn *vpn) +{ + struct bgp_attr_srv6_vpn *find; + + find = hash_get(srv6_vpn_hash, vpn, srv6_vpn_hash_alloc); + if (find != vpn) + srv6_vpn_free(vpn); + find->refcnt++; + return find; +} + +static void srv6_vpn_unintern(struct bgp_attr_srv6_vpn **vpnp) +{ + struct bgp_attr_srv6_vpn *vpn = *vpnp; + + if (vpn->refcnt) + vpn->refcnt--; + + if (vpn->refcnt == 0) { + hash_release(srv6_vpn_hash, vpn); + srv6_vpn_free(vpn); + *vpnp = NULL; } } -static unsigned int transit_hash_key_make(void *p) +static uint32_t srv6_l3vpn_hash_key_make(const void *p) +{ + const struct bgp_attr_srv6_l3vpn *l3vpn = p; + uint32_t key = 0; + + key = jhash(&l3vpn->sid, 16, key); + key = jhash_1word(l3vpn->sid_flags, key); + key = jhash_1word(l3vpn->endpoint_behavior, key); + return key; +} + +static bool srv6_l3vpn_hash_cmp(const void *p1, const void *p2) +{ + const struct bgp_attr_srv6_l3vpn *l3vpn1 = p1; + const struct bgp_attr_srv6_l3vpn *l3vpn2 = p2; + + return sid_same(&l3vpn1->sid, &l3vpn2->sid) + && l3vpn1->sid_flags == l3vpn2->sid_flags + && l3vpn1->endpoint_behavior == l3vpn2->endpoint_behavior; +} + +static bool srv6_l3vpn_same(const struct bgp_attr_srv6_l3vpn *h1, + const struct bgp_attr_srv6_l3vpn *h2) +{ + if (h1 == h2) + return true; + else if (h1 == NULL || h2 == NULL) + return false; + else + return srv6_l3vpn_hash_cmp((const void *)h1, (const void *)h2); +} + +static unsigned int srv6_vpn_hash_key_make(const void *p) +{ + const struct bgp_attr_srv6_vpn *vpn = p; + uint32_t key = 0; + + key = jhash(&vpn->sid, 16, key); + key = jhash_1word(vpn->sid_flags, key); + return key; +} + +static bool srv6_vpn_hash_cmp(const void *p1, const void *p2) +{ + const struct bgp_attr_srv6_vpn *vpn1 = p1; + const struct bgp_attr_srv6_vpn *vpn2 = p2; + + return sid_same(&vpn1->sid, &vpn2->sid) + && vpn1->sid_flags == vpn2->sid_flags; +} + +static bool srv6_vpn_same(const struct bgp_attr_srv6_vpn *h1, + const struct bgp_attr_srv6_vpn *h2) +{ + if (h1 == h2) + return true; + else if (h1 == NULL || h2 == NULL) + return false; + else + return srv6_vpn_hash_cmp((const void *)h1, (const void *)h2); +} + +static void srv6_init(void) +{ + srv6_l3vpn_hash = + hash_create(srv6_l3vpn_hash_key_make, srv6_l3vpn_hash_cmp, + "BGP Prefix-SID SRv6-L3VPN-Service-TLV"); + srv6_vpn_hash = hash_create(srv6_vpn_hash_key_make, srv6_vpn_hash_cmp, + "BGP Prefix-SID SRv6-VPN-Service-TLV"); +} + +static void srv6_finish(void) +{ + hash_clean(srv6_l3vpn_hash, (void (*)(void *))srv6_l3vpn_free); + hash_free(srv6_l3vpn_hash); + srv6_l3vpn_hash = NULL; + hash_clean(srv6_vpn_hash, (void (*)(void *))srv6_vpn_free); + hash_free(srv6_vpn_hash); + srv6_vpn_hash = NULL; +} + +static unsigned int transit_hash_key_make(const void *p) { const struct transit *transit = p; @@ -465,15 +630,6 @@ static void transit_finish(void) /* Attribute hash routines. */ static struct hash *attrhash; -/* Shallow copy of an attribute - * Though, not so shallow that it doesn't copy the contents - * of the attr_extra pointed to by 'extra' - */ -void bgp_attr_dup(struct attr *new, struct attr *orig) -{ - *new = *orig; -} - unsigned long int attr_count(void) { return attrhash->count; @@ -484,7 +640,7 @@ unsigned long int attr_unknown_count(void) return transit_hash->count; } -unsigned int attrhash_key_make(void *p) +unsigned int attrhash_key_make(const void *p) { const struct attr *attr = (struct attr *)p; uint32_t key = 0; @@ -507,21 +663,23 @@ unsigned int attrhash_key_make(void *p) MIX(lcommunity_hash_make(attr->lcommunity)); if (attr->ecommunity) MIX(ecommunity_hash_make(attr->ecommunity)); + if (attr->ipv6_ecommunity) + MIX(ecommunity_hash_make(attr->ipv6_ecommunity)); if (attr->cluster) MIX(cluster_hash_key_make(attr->cluster)); if (attr->transit) MIX(transit_hash_key_make(attr->transit)); if (attr->encap_subtlvs) MIX(encap_hash_key_make(attr->encap_subtlvs)); -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC if (attr->vnc_subtlvs) MIX(encap_hash_key_make(attr->vnc_subtlvs)); #endif MIX(attr->mp_nexthop_len); key = jhash(attr->mp_nexthop_global.s6_addr, IPV6_MAX_BYTELEN, key); key = jhash(attr->mp_nexthop_local.s6_addr, IPV6_MAX_BYTELEN, key); - MIX(attr->nh_ifindex); - MIX(attr->nh_lla_ifindex); + MIX3(attr->nh_ifindex, attr->nh_lla_ifindex, attr->distance); + MIX(attr->rmap_table_id); return key; } @@ -545,12 +703,14 @@ bool attrhash_cmp(const void *p1, const void *p2) && attr1->label_index == attr2->label_index && attr1->mp_nexthop_len == attr2->mp_nexthop_len && attr1->ecommunity == attr2->ecommunity + && attr1->ipv6_ecommunity == attr2->ipv6_ecommunity && attr1->lcommunity == attr2->lcommunity && attr1->cluster == attr2->cluster && attr1->transit == attr2->transit + && attr1->rmap_table_id == attr2->rmap_table_id && (attr1->encap_tunneltype == attr2->encap_tunneltype) && encap_same(attr1->encap_subtlvs, attr2->encap_subtlvs) -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC && encap_same(attr1->vnc_subtlvs, attr2->vnc_subtlvs) #endif && IPV6_ADDR_SAME(&attr1->mp_nexthop_global, @@ -562,8 +722,15 @@ bool attrhash_cmp(const void *p1, const void *p2) && IPV4_ADDR_SAME(&attr1->originator_id, &attr2->originator_id) && overlay_index_same(attr1, attr2) + && !memcmp(&attr1->esi, &attr2->esi, sizeof(esi_t)) + && attr1->es_flags == attr2->es_flags + && attr1->mm_sync_seqnum == attr2->mm_sync_seqnum && attr1->nh_ifindex == attr2->nh_ifindex - && attr1->nh_lla_ifindex == attr2->nh_lla_ifindex) + && attr1->nh_lla_ifindex == attr2->nh_lla_ifindex + && attr1->distance == attr2->distance + && srv6_l3vpn_same(attr1->srv6_l3vpn, attr2->srv6_l3vpn) + && srv6_vpn_same(attr1->srv6_vpn, attr2->srv6_vpn) + && attr1->srte_color == attr2->srte_color) return true; } @@ -594,12 +761,21 @@ static void attrhash_finish(void) static void attr_show_all_iterator(struct hash_bucket *bucket, struct vty *vty) { struct attr *attr = bucket->data; + char sid_str[BUFSIZ]; vty_out(vty, "attr[%ld] nexthop %s\n", attr->refcnt, inet_ntoa(attr->nexthop)); - vty_out(vty, "\tflags: %" PRIu64 " med: %u local_pref: %u origin: %u weight: %u label: %u\n", + + sid_str[0] = '\0'; + if (attr->srv6_l3vpn) + inet_ntop(AF_INET6, &attr->srv6_l3vpn->sid, sid_str, BUFSIZ); + else if (attr->srv6_vpn) + inet_ntop(AF_INET6, &attr->srv6_vpn->sid, sid_str, BUFSIZ); + + vty_out(vty, + "\tflags: %" PRIu64" med: %u local_pref: %u origin: %u weight: %u label: %u sid: %s\n", attr->flag, attr->med, attr->local_pref, attr->origin, - attr->weight, attr->label); + attr->weight, attr->label, sid_str); } void attr_show_all(struct vty *vty) @@ -619,11 +795,16 @@ static void *bgp_attr_hash_alloc(void *p) if (val->encap_subtlvs) { val->encap_subtlvs = NULL; } -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC if (val->vnc_subtlvs) { val->vnc_subtlvs = NULL; } #endif + if (val->srv6_l3vpn) + val->srv6_l3vpn = NULL; + if (val->srv6_vpn) + val->srv6_vpn = NULL; + attr->refcnt = 0; return attr; } @@ -653,6 +834,15 @@ struct attr *bgp_attr_intern(struct attr *attr) else attr->ecommunity->refcnt++; } + + if (attr->ipv6_ecommunity) { + if (!attr->ipv6_ecommunity->refcnt) + attr->ipv6_ecommunity = + ecommunity_intern(attr->ipv6_ecommunity); + else + attr->ipv6_ecommunity->refcnt++; + } + if (attr->lcommunity) { if (!attr->lcommunity->refcnt) attr->lcommunity = lcommunity_intern(attr->lcommunity); @@ -678,7 +868,19 @@ struct attr *bgp_attr_intern(struct attr *attr) else attr->encap_subtlvs->refcnt++; } -#if ENABLE_BGP_VNC + if (attr->srv6_l3vpn) { + if (!attr->srv6_l3vpn->refcnt) + attr->srv6_l3vpn = srv6_l3vpn_intern(attr->srv6_l3vpn); + else + attr->srv6_l3vpn->refcnt++; + } + if (attr->srv6_vpn) { + if (!attr->srv6_vpn->refcnt) + attr->srv6_vpn = srv6_vpn_intern(attr->srv6_vpn); + else + attr->srv6_vpn->refcnt++; + } +#ifdef ENABLE_BGP_VNC if (attr->vnc_subtlvs) { if (!attr->vnc_subtlvs->refcnt) attr->vnc_subtlvs = encap_intern(attr->vnc_subtlvs, @@ -720,15 +922,15 @@ struct attr *bgp_attr_default_set(struct attr *attr, uint8_t origin) } /* Create the attributes for an aggregate */ -struct attr *bgp_attr_aggregate_intern(struct bgp *bgp, uint8_t origin, - struct aspath *aspath, - struct community *community, - struct ecommunity *ecommunity, - struct lcommunity *lcommunity, - int as_set, uint8_t atomic_aggregate) +struct attr *bgp_attr_aggregate_intern( + struct bgp *bgp, uint8_t origin, struct aspath *aspath, + struct community *community, struct ecommunity *ecommunity, + struct lcommunity *lcommunity, struct bgp_aggregate *aggregate, + uint8_t atomic_aggregate, const struct prefix *p) { struct attr attr; struct attr *new; + int ret; memset(&attr, 0, sizeof(struct attr)); @@ -752,7 +954,7 @@ struct attr *bgp_attr_aggregate_intern(struct bgp *bgp, uint8_t origin, /* If we are not shutting down ourselves and we are * aggregating a route that contains the GSHUT community we * need to remove that community when creating the aggregate */ - if (!bgp_flag_check(bgp, BGP_FLAG_GRACEFUL_SHUTDOWN) + if (!CHECK_FLAG(bgp->flags, BGP_FLAG_GRACEFUL_SHUTDOWN) && community_include(community, gshut)) { community_del_val(community, &gshut); } @@ -771,15 +973,14 @@ struct attr *bgp_attr_aggregate_intern(struct bgp *bgp, uint8_t origin, attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_LARGE_COMMUNITIES); } - if (bgp_flag_check(bgp, BGP_FLAG_GRACEFUL_SHUTDOWN)) { + if (CHECK_FLAG(bgp->flags, BGP_FLAG_GRACEFUL_SHUTDOWN)) bgp_attr_add_gshut_community(&attr); - } attr.label_index = BGP_INVALID_LABEL_INDEX; attr.label = MPLS_INVALID_LABEL; attr.weight = BGP_ATTR_DEFAULT_WEIGHT; attr.mp_nexthop_len = IPV6_MAX_BYTELEN; - if (!as_set || atomic_aggregate) + if (!aggregate->as_set || atomic_aggregate) attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_ATOMIC_AGGREGATE); attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_AGGREGATOR); if (CHECK_FLAG(bgp->config, BGP_CONFIG_CONFEDERATION)) @@ -790,7 +991,42 @@ struct attr *bgp_attr_aggregate_intern(struct bgp *bgp, uint8_t origin, attr.label_index = BGP_INVALID_LABEL_INDEX; attr.label = MPLS_INVALID_LABEL; - new = bgp_attr_intern(&attr); + /* Apply route-map */ + if (aggregate->rmap.name) { + struct attr attr_tmp = attr; + struct bgp_path_info rmap_path; + + memset(&rmap_path, 0, sizeof(struct bgp_path_info)); + rmap_path.peer = bgp->peer_self; + rmap_path.attr = &attr_tmp; + + SET_FLAG(bgp->peer_self->rmap_type, PEER_RMAP_TYPE_AGGREGATE); + + ret = route_map_apply(aggregate->rmap.map, p, RMAP_BGP, + &rmap_path); + + bgp->peer_self->rmap_type = 0; + + if (ret == RMAP_DENYMATCH) { + /* Free uninterned attribute. */ + bgp_attr_flush(&attr_tmp); + + /* Unintern original. */ + aspath_unintern(&attr.aspath); + return NULL; + } + + if (CHECK_FLAG(bgp->flags, BGP_FLAG_GRACEFUL_SHUTDOWN)) + bgp_attr_add_gshut_community(&attr_tmp); + + new = bgp_attr_intern(&attr_tmp); + } else { + + if (CHECK_FLAG(bgp->flags, BGP_FLAG_GRACEFUL_SHUTDOWN)) + bgp_attr_add_gshut_community(&attr); + + new = bgp_attr_intern(&attr); + } aspath_unintern(&new->aspath); return new; @@ -812,24 +1048,34 @@ void bgp_attr_unintern_sub(struct attr *attr) ecommunity_unintern(&attr->ecommunity); UNSET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES)); + if (attr->ipv6_ecommunity) + ecommunity_unintern(&attr->ipv6_ecommunity); + UNSET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_IPV6_EXT_COMMUNITIES)); + if (attr->lcommunity) lcommunity_unintern(&attr->lcommunity); UNSET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_LARGE_COMMUNITIES)); if (attr->cluster) - cluster_unintern(attr->cluster); + cluster_unintern(&attr->cluster); UNSET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_CLUSTER_LIST)); if (attr->transit) - transit_unintern(attr->transit); + transit_unintern(&attr->transit); if (attr->encap_subtlvs) encap_unintern(&attr->encap_subtlvs, ENCAP_SUBTLV_TYPE); -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC if (attr->vnc_subtlvs) encap_unintern(&attr->vnc_subtlvs, VNC_SUBTLV_TYPE); #endif + + if (attr->srv6_l3vpn) + srv6_l3vpn_unintern(&attr->srv6_l3vpn); + + if (attr->srv6_vpn) + srv6_vpn_unintern(&attr->srv6_vpn); } /* @@ -890,6 +1136,8 @@ void bgp_attr_flush(struct attr *attr) community_free(&attr->community); if (attr->ecommunity && !attr->ecommunity->refcnt) ecommunity_free(&attr->ecommunity); + if (attr->ipv6_ecommunity && !attr->ipv6_ecommunity->refcnt) + ecommunity_free(&attr->ipv6_ecommunity); if (attr->lcommunity && !attr->lcommunity->refcnt) lcommunity_free(&attr->lcommunity); if (attr->cluster && !attr->cluster->refcnt) { @@ -904,7 +1152,7 @@ void bgp_attr_flush(struct attr *attr) encap_free(attr->encap_subtlvs); attr->encap_subtlvs = NULL; } -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC if (attr->vnc_subtlvs && !attr->vnc_subtlvs->refcnt) { encap_free(attr->vnc_subtlvs); attr->vnc_subtlvs = NULL; @@ -922,6 +1170,7 @@ bgp_attr_malformed(struct bgp_attr_parser_args *args, uint8_t subcode, bgp_size_t length) { struct peer *const peer = args->peer; + struct attr *const attr = args->attr; const uint8_t flags = args->flags; /* startp and length must be special-cased, as whether or not to * send the attribute data with the NOTIFY depends on the error, @@ -929,6 +1178,14 @@ bgp_attr_malformed(struct bgp_attr_parser_args *args, uint8_t subcode, */ uint8_t *notify_datap = (length > 0 ? args->startp : NULL); + if (bgp_debug_update(peer, NULL, NULL, 1)) { + char attr_str[BUFSIZ] = {0}; + + bgp_dump_attr(attr, attr_str, sizeof(attr_str)); + + zlog_debug("%s: attributes: %s", __func__, attr_str); + } + /* Only relax error handling for eBGP peers */ if (peer->sort != BGP_PEER_EBGP) { bgp_notify_send_with_data(peer, BGP_NOTIFY_UPDATE_ERR, subcode, @@ -955,7 +1212,7 @@ bgp_attr_malformed(struct bgp_attr_parser_args *args, uint8_t subcode, return BGP_ATTR_PARSE_PROCEED; /* Core attributes, particularly ones which may influence route - * selection, should always cause session resets + * selection, should be treat-as-withdraw. */ case BGP_ATTR_ORIGIN: case BGP_ATTR_AS_PATH: @@ -963,11 +1220,14 @@ bgp_attr_malformed(struct bgp_attr_parser_args *args, uint8_t subcode, case BGP_ATTR_MULTI_EXIT_DISC: case BGP_ATTR_LOCAL_PREF: case BGP_ATTR_COMMUNITIES: + case BGP_ATTR_EXT_COMMUNITIES: + case BGP_ATTR_IPV6_EXT_COMMUNITIES: + case BGP_ATTR_LARGE_COMMUNITIES: case BGP_ATTR_ORIGINATOR_ID: case BGP_ATTR_CLUSTER_LIST: + return BGP_ATTR_PARSE_WITHDRAW; case BGP_ATTR_MP_REACH_NLRI: case BGP_ATTR_MP_UNREACH_NLRI: - case BGP_ATTR_EXT_COMMUNITIES: bgp_notify_send_with_data(peer, BGP_NOTIFY_UPDATE_ERR, subcode, notify_datap, length); return BGP_ATTR_PARSE_ERROR; @@ -1016,8 +1276,7 @@ bgp_attr_flags_diagnose(struct bgp_attr_parser_args *args, } if (!seen) { zlog_debug( - "Strange, %s called for attr %s, but no problem found with flags" - " (real flags 0x%x, desired 0x%x)", + "Strange, %s called for attr %s, but no problem found with flags (real flags 0x%x, desired 0x%x)", __func__, lookup_msg(attr_str, attr_code, NULL), real_flags, desired_flags); } @@ -1048,10 +1307,12 @@ const uint8_t attr_flags_values[] = { [BGP_ATTR_LARGE_COMMUNITIES] = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS, [BGP_ATTR_PREFIX_SID] = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS, + [BGP_ATTR_IPV6_EXT_COMMUNITIES] = + BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS, }; static const size_t attr_flags_values_max = array_size(attr_flags_values) - 1; -static int bgp_attr_flag_invalid(struct bgp_attr_parser_args *args) +static bool bgp_attr_flag_invalid(struct bgp_attr_parser_args *args) { uint8_t mask = BGP_ATTR_FLAG_EXTLEN; const uint8_t flags = args->flags; @@ -1059,9 +1320,9 @@ static int bgp_attr_flag_invalid(struct bgp_attr_parser_args *args) /* there may be attributes we don't know about */ if (attr_code > attr_flags_values_max) - return 0; + return false; if (attr_flags_values[attr_code] == 0) - return 0; + return false; /* RFC4271, "For well-known attributes, the Transitive bit MUST be set * to @@ -1073,7 +1334,7 @@ static int bgp_attr_flag_invalid(struct bgp_attr_parser_args *args) EC_BGP_ATTR_FLAG, "%s well-known attributes must have transitive flag set (%x)", lookup_msg(attr_str, attr_code, NULL), flags); - return 1; + return true; } /* "For well-known attributes and for optional non-transitive @@ -1083,18 +1344,16 @@ static int bgp_attr_flag_invalid(struct bgp_attr_parser_args *args) if (CHECK_FLAG(flags, BGP_ATTR_FLAG_PARTIAL)) { if (!CHECK_FLAG(flags, BGP_ATTR_FLAG_OPTIONAL)) { flog_err(EC_BGP_ATTR_FLAG, - "%s well-known attribute " - "must NOT have the partial flag set (%x)", + "%s well-known attribute must NOT have the partial flag set (%x)", lookup_msg(attr_str, attr_code, NULL), flags); - return 1; + return true; } if (CHECK_FLAG(flags, BGP_ATTR_FLAG_OPTIONAL) && !CHECK_FLAG(flags, BGP_ATTR_FLAG_TRANS)) { flog_err(EC_BGP_ATTR_FLAG, - "%s optional + transitive attribute " - "must NOT have the partial flag set (%x)", + "%s optional + transitive attribute must NOT have the partial flag set (%x)", lookup_msg(attr_str, attr_code, NULL), flags); - return 1; + return true; } } @@ -1106,10 +1365,10 @@ static int bgp_attr_flag_invalid(struct bgp_attr_parser_args *args) SET_FLAG(mask, BGP_ATTR_FLAG_PARTIAL); if ((flags & ~mask) == attr_flags_values[attr_code]) - return 0; + return false; bgp_attr_flags_diagnose(args, attr_flags_values[attr_code]); - return 1; + return true; } /* Get origin attribute of the update message. */ @@ -1175,6 +1434,16 @@ static int bgp_attr_aspath(struct bgp_attr_parser_args *args) 0); } + /* Codification of AS 0 Processing */ + if (aspath_check_as_zero(attr->aspath)) { + flog_err( + EC_BGP_ATTR_MAL_AS_PATH, + "Malformed AS path, AS number is 0 in the path from %s", + peer->host); + return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_MAL_AS_PATH, + 0); + } + /* Set aspath attribute flag. */ attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_AS_PATH); @@ -1200,9 +1469,7 @@ static bgp_attr_parse_ret_t bgp_attr_aspath_check(struct peer *const peer, && aspath_confed_check(attr->aspath))) { flog_err(EC_BGP_ATTR_MAL_AS_PATH, "Malformed AS path from %s", peer->host); - bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_MAL_AS_PATH); - return BGP_ATTR_PARSE_ERROR; + return BGP_ATTR_PARSE_WITHDRAW; } /* First AS check for EBGP. */ @@ -1212,9 +1479,7 @@ static bgp_attr_parse_ret_t bgp_attr_aspath_check(struct peer *const peer, flog_err(EC_BGP_ATTR_FIRST_AS, "%s incorrect first AS (must be %u)", peer->host, peer->as); - bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_MAL_AS_PATH); - return BGP_ATTR_PARSE_ERROR; + return BGP_ATTR_PARSE_WITHDRAW; } } @@ -1250,12 +1515,54 @@ static int bgp_attr_as4_path(struct bgp_attr_parser_args *args, 0); } + /* Codification of AS 0 Processing */ + if (aspath_check_as_zero(*as4_path)) { + flog_err( + EC_BGP_ATTR_MAL_AS_PATH, + "Malformed AS path, AS number is 0 in the path from %s", + peer->host); + return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_MAL_AS_PATH, + 0); + } + /* Set aspath attribute flag. */ attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_AS4_PATH); return BGP_ATTR_PARSE_PROCEED; } +/* + * Check that the nexthop attribute is valid. + */ +bgp_attr_parse_ret_t +bgp_attr_nexthop_valid(struct peer *peer, struct attr *attr) +{ + in_addr_t nexthop_h; + + nexthop_h = ntohl(attr->nexthop.s_addr); + if ((IPV4_NET0(nexthop_h) || IPV4_NET127(nexthop_h) + || IPV4_CLASS_DE(nexthop_h)) + && !BGP_DEBUG(allow_martians, ALLOW_MARTIANS)) { + uint8_t data[7]; /* type(2) + length(1) + nhop(4) */ + char buf[INET_ADDRSTRLEN]; + + inet_ntop(AF_INET, &attr->nexthop.s_addr, buf, + INET_ADDRSTRLEN); + flog_err(EC_BGP_ATTR_MARTIAN_NH, "Martian nexthop %s", + buf); + data[0] = BGP_ATTR_FLAG_TRANS; + data[1] = BGP_ATTR_NEXT_HOP; + data[2] = BGP_ATTR_NHLEN_IPV4; + memcpy(&data[3], &attr->nexthop.s_addr, BGP_ATTR_NHLEN_IPV4); + bgp_notify_send_with_data(peer, BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_INVAL_NEXT_HOP, + data, 7); + return BGP_ATTR_PARSE_ERROR; + } + + return BGP_ATTR_PARSE_PROCEED; +} + /* Nexthop attribute. */ static bgp_attr_parse_ret_t bgp_attr_nexthop(struct bgp_attr_parser_args *args) { @@ -1263,8 +1570,6 @@ static bgp_attr_parse_ret_t bgp_attr_nexthop(struct bgp_attr_parser_args *args) struct attr *const attr = args->attr; const bgp_size_t length = args->length; - in_addr_t nexthop_h, nexthop_n; - /* Check nexthop attribute length. */ if (length != 4) { flog_err(EC_BGP_ATTR_LEN, @@ -1274,30 +1579,7 @@ static bgp_attr_parse_ret_t bgp_attr_nexthop(struct bgp_attr_parser_args *args) args->total); } - /* According to section 6.3 of RFC4271, syntactically incorrect NEXT_HOP - attribute must result in a NOTIFICATION message (this is implemented - below). - At the same time, semantically incorrect NEXT_HOP is more likely to - be just - logged locally (this is implemented somewhere else). The UPDATE - message - gets ignored in any of these cases. */ - nexthop_n = stream_get_ipv4(peer->curr); - nexthop_h = ntohl(nexthop_n); - if ((IPV4_NET0(nexthop_h) || IPV4_NET127(nexthop_h) - || IPV4_CLASS_DE(nexthop_h)) - && !BGP_DEBUG( - allow_martians, - ALLOW_MARTIANS)) /* loopbacks may be used in testing */ - { - char buf[INET_ADDRSTRLEN]; - inet_ntop(AF_INET, &nexthop_n, buf, INET_ADDRSTRLEN); - flog_err(EC_BGP_ATTR_MARTIAN_NH, "Martian nexthop %s", buf); - return bgp_attr_malformed( - args, BGP_NOTIFY_UPDATE_INVAL_NEXT_HOP, args->total); - } - - attr->nexthop.s_addr = nexthop_n; + attr->nexthop.s_addr = stream_get_ipv4(peer->curr); attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP); return BGP_ATTR_PARSE_PROCEED; @@ -1334,8 +1616,12 @@ bgp_attr_local_pref(struct bgp_attr_parser_args *args) struct attr *const attr = args->attr; const bgp_size_t length = args->length; - /* Length check. */ - if (length != 4) { + /* if received from an internal neighbor, it SHALL be considered + * malformed if its length is not equal to 4. If malformed, the + * UPDATE message SHALL be handled using the approach of "treat-as- + * withdraw". + */ + if (peer->sort == BGP_PEER_IBGP && length != 4) { flog_err(EC_BGP_ATTR_LEN, "LOCAL_PREF attribute length isn't 4 [%u]", length); return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, @@ -1346,16 +1632,20 @@ bgp_attr_local_pref(struct bgp_attr_parser_args *args) external peer, then this attribute MUST be ignored by the receiving speaker. */ if (peer->sort == BGP_PEER_EBGP) { - stream_forward_getp(peer->curr, length); + STREAM_FORWARD_GETP(peer->curr, length); return BGP_ATTR_PARSE_PROCEED; } - attr->local_pref = stream_getl(peer->curr); + STREAM_GETL(peer->curr, attr->local_pref); /* Set the local-pref flag. */ attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF); return BGP_ATTR_PARSE_PROCEED; + +stream_failure: + return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, + args->total); } /* Atomic aggregate. */ @@ -1385,11 +1675,13 @@ static int bgp_attr_aggregator(struct bgp_attr_parser_args *args) struct peer *const peer = args->peer; struct attr *const attr = args->attr; const bgp_size_t length = args->length; + as_t aggregator_as; int wantedlen = 6; /* peer with AS4 will send 4 Byte AS, peer without will send 2 Byte */ - if (CHECK_FLAG(peer->cap, PEER_CAP_AS4_RCV)) + if (CHECK_FLAG(peer->cap, PEER_CAP_AS4_RCV) + && CHECK_FLAG(peer->cap, PEER_CAP_AS4_ADV)) wantedlen = 8; if (length != wantedlen) { @@ -1401,13 +1693,20 @@ static int bgp_attr_aggregator(struct bgp_attr_parser_args *args) } if (CHECK_FLAG(peer->cap, PEER_CAP_AS4_RCV)) - attr->aggregator_as = stream_getl(peer->curr); + aggregator_as = stream_getl(peer->curr); else - attr->aggregator_as = stream_getw(peer->curr); + aggregator_as = stream_getw(peer->curr); + + attr->aggregator_as = aggregator_as; attr->aggregator_addr.s_addr = stream_get_ipv4(peer->curr); - /* Set atomic aggregate flag. */ - attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_AGGREGATOR); + /* Codification of AS 0 Processing */ + if (aggregator_as == BGP_AS_ZERO) + flog_err(EC_BGP_ATTR_LEN, + "%s: AGGREGATOR AS number is 0 for aspath: %s", + peer->host, aspath_print(attr->aspath)); + else + attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_AGGREGATOR); return BGP_ATTR_PARSE_PROCEED; } @@ -1421,6 +1720,7 @@ bgp_attr_as4_aggregator(struct bgp_attr_parser_args *args, struct peer *const peer = args->peer; struct attr *const attr = args->attr; const bgp_size_t length = args->length; + as_t aggregator_as; if (length != 8) { flog_err(EC_BGP_ATTR_LEN, "New Aggregator length is not 8 [%d]", @@ -1429,10 +1729,18 @@ bgp_attr_as4_aggregator(struct bgp_attr_parser_args *args, 0); } - *as4_aggregator_as = stream_getl(peer->curr); + aggregator_as = stream_getl(peer->curr); + + *as4_aggregator_as = aggregator_as; as4_aggregator_addr->s_addr = stream_get_ipv4(peer->curr); - attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_AS4_AGGREGATOR); + /* Codification of AS 0 Processing */ + if (aggregator_as == BGP_AS_ZERO) + flog_err(EC_BGP_ATTR_LEN, + "%s: AS4_AGGREGATOR AS number is 0 for aspath: %s", + peer->host, aspath_print(attr->aspath)); + else + attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_AS4_AGGREGATOR); return BGP_ATTR_PARSE_PROCEED; } @@ -1500,10 +1808,7 @@ bgp_attr_munge_as4_attrs(struct peer *const peer, struct attr *const attr, /* ignore */ if (BGP_DEBUG(as4, AS4)) zlog_debug( - "[AS4] %s BGP not AS4 capable peer" - " send AGGREGATOR != AS_TRANS and" - " AS4_AGGREGATOR, so ignore" - " AS4_AGGREGATOR and AS4_PATH", + "[AS4] %s BGP not AS4 capable peer send AGGREGATOR != AS_TRANS and AS4_AGGREGATOR, so ignore AS4_AGGREGATOR and AS4_PATH", peer->host); ignore_as4_path = 1; } else { @@ -1521,9 +1826,7 @@ bgp_attr_munge_as4_attrs(struct peer *const peer, struct attr *const attr, */ if (BGP_DEBUG(as4, AS4)) zlog_debug( - "[AS4] %s BGP not AS4 capable peer send" - " AS4_AGGREGATOR but no AGGREGATOR, will take" - " it as if AGGREGATOR with AS_TRANS had been there", + "[AS4] %s BGP not AS4 capable peer send AS4_AGGREGATOR but no AGGREGATOR, will take it as if AGGREGATOR with AS_TRANS had been there", peer->host); attr->aggregator_as = as4_aggregator; /* sweep it under the carpet and simulate a "good" @@ -1555,7 +1858,8 @@ bgp_attr_community(struct bgp_attr_parser_args *args) if (length == 0) { attr->community = NULL; - return BGP_ATTR_PARSE_PROCEED; + return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_OPT_ATTR_ERR, + args->total); } attr->community = @@ -1564,6 +1868,9 @@ bgp_attr_community(struct bgp_attr_parser_args *args) /* XXX: fix community_parse to use stream API and remove this */ stream_forward_getp(peer->curr, length); + /* The Community attribute SHALL be considered malformed if its + * length is not a non-zero multiple of 4. + */ if (!attr->community) return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_OPT_ATTR_ERR, args->total); @@ -1581,7 +1888,11 @@ bgp_attr_originator_id(struct bgp_attr_parser_args *args) struct attr *const attr = args->attr; const bgp_size_t length = args->length; - /* Length check. */ + /* if received from an internal neighbor, it SHALL be considered + * malformed if its length is not equal to 4. If malformed, the + * UPDATE message SHALL be handled using the approach of "treat-as- + * withdraw". + */ if (length != 4) { flog_err(EC_BGP_ATTR_LEN, "Bad originator ID length %d", length); @@ -1605,8 +1916,12 @@ bgp_attr_cluster_list(struct bgp_attr_parser_args *args) struct attr *const attr = args->attr; const bgp_size_t length = args->length; - /* Check length. */ - if (length % 4) { + /* if received from an internal neighbor, it SHALL be considered + * malformed if its length is not a non-zero multiple of 4. If + * malformed, the UPDATE message SHALL be handled using the approach + * of "treat-as-withdraw". + */ + if (length == 0 || length % 4) { flog_err(EC_BGP_ATTR_LEN, "Bad cluster list length %d", length); return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, @@ -1647,8 +1962,8 @@ int bgp_mp_reach_parse(struct bgp_attr_parser_args *args, #define BGP_MP_REACH_MIN_SIZE 5 #define LEN_LEFT (length - (stream_get_getp(s) - start)) if ((length > STREAM_READABLE(s)) || (length < BGP_MP_REACH_MIN_SIZE)) { - zlog_info("%s: %s sent invalid length, %lu", __func__, - peer->host, (unsigned long)length); + zlog_info("%s: %s sent invalid length, %lu, of MP_REACH_NLRI", + __func__, peer->host, (unsigned long)length); return BGP_ATTR_PARSE_ERROR_NOTIFYPLS; } @@ -1664,8 +1979,9 @@ int bgp_mp_reach_parse(struct bgp_attr_parser_args *args, */ if (bgp_debug_update(peer, NULL, NULL, 0)) zlog_debug( - "%s: MP_REACH received AFI %u or SAFI %u is unrecognized", - peer->host, pkt_afi, pkt_safi); + "%s sent unrecognizable AFI, %s or, SAFI, %s, of MP_REACH_NLRI", + peer->host, iana_afi2str(pkt_afi), + iana_safi2str(pkt_safi)); return BGP_ATTR_PARSE_ERROR; } @@ -1674,7 +1990,7 @@ int bgp_mp_reach_parse(struct bgp_attr_parser_args *args, if (LEN_LEFT < attr->mp_nexthop_len) { zlog_info( - "%s: %s, MP nexthop length, %u, goes past end of attribute", + "%s: %s sent next-hop length, %u, in MP_REACH_NLRI which goes past the end of attribute", __func__, peer->host, attr->mp_nexthop_len); return BGP_ATTR_PARSE_ERROR_NOTIFYPLS; } @@ -1683,7 +1999,7 @@ int bgp_mp_reach_parse(struct bgp_attr_parser_args *args, switch (attr->mp_nexthop_len) { case 0: if (safi != SAFI_FLOWSPEC) { - zlog_info("%s: (%s) Wrong multiprotocol next hop length: %d", + zlog_info("%s: %s sent wrong next-hop length, %d, in MP_REACH_NLRI", __func__, peer->host, attr->mp_nexthop_len); return BGP_ATTR_PARSE_ERROR_NOTIFYPLS; } @@ -1702,7 +2018,7 @@ int bgp_mp_reach_parse(struct bgp_attr_parser_args *args, case BGP_ATTR_NHLEN_IPV4: stream_get(&attr->mp_nexthop_global_in, s, IPV4_MAX_BYTELEN); /* Probably needed for RFC 2283 */ - if (attr->nexthop.s_addr == 0) + if (attr->nexthop.s_addr == INADDR_ANY) memcpy(&attr->nexthop.s_addr, &attr->mp_nexthop_global_in, IPV4_MAX_BYTELEN); break; @@ -1715,7 +2031,7 @@ int bgp_mp_reach_parse(struct bgp_attr_parser_args *args, stream_get(&attr->mp_nexthop_global, s, IPV6_MAX_BYTELEN); if (IN6_IS_ADDR_LINKLOCAL(&attr->mp_nexthop_global)) { if (!peer->nexthop.ifp) { - zlog_warn("%s: Received a V6/VPNV6 Global attribute but address is a V6 LL and we have no peer interface information, withdrawing", + zlog_warn("%s sent a v6 global attribute but address is a V6 LL and there's no peer interface information. Hence, withdrawing", peer->host); return BGP_ATTR_PARSE_WITHDRAW; } @@ -1732,7 +2048,7 @@ int bgp_mp_reach_parse(struct bgp_attr_parser_args *args, stream_get(&attr->mp_nexthop_global, s, IPV6_MAX_BYTELEN); if (IN6_IS_ADDR_LINKLOCAL(&attr->mp_nexthop_global)) { if (!peer->nexthop.ifp) { - zlog_warn("%s: Received V6/VPNV6 Global and LL attribute but global address is a V6 LL and we have no peer interface information, withdrawing", + zlog_warn("%s sent a v6 global and LL attribute but global address is a V6 LL and there's no peer interface information. Hence, withdrawing", peer->host); return BGP_ATTR_PARSE_WITHDRAW; } @@ -1750,7 +2066,7 @@ int bgp_mp_reach_parse(struct bgp_attr_parser_args *args, if (bgp_debug_update(peer, NULL, NULL, 1)) zlog_debug( - "%s rcvd nexthops %s, %s -- ignoring non-LL value", + "%s sent next-hops %s and %s. Ignoring non-LL value", peer->host, inet_ntop(AF_INET6, &attr->mp_nexthop_global, @@ -1762,21 +2078,21 @@ int bgp_mp_reach_parse(struct bgp_attr_parser_args *args, attr->mp_nexthop_len = IPV6_MAX_BYTELEN; } if (!peer->nexthop.ifp) { - zlog_warn("%s: Received a V6 LL nexthop and we have no peer interface information, withdrawing", + zlog_warn("%s sent a v6 LL next-hop and there's no peer interface information. Hence, withdrawing", peer->host); return BGP_ATTR_PARSE_WITHDRAW; } attr->nh_lla_ifindex = peer->nexthop.ifp->ifindex; break; default: - zlog_info("%s: (%s) Wrong multiprotocol next hop length: %d", + zlog_info("%s: %s sent wrong next-hop length, %d, in MP_REACH_NLRI", __func__, peer->host, attr->mp_nexthop_len); return BGP_ATTR_PARSE_ERROR_NOTIFYPLS; } if (!LEN_LEFT) { - zlog_info("%s: (%s) Failed to read SNPA and NLRI(s)", __func__, - peer->host); + zlog_info("%s: %s sent SNPA which couldn't be read", + __func__, peer->host); return BGP_ATTR_PARSE_ERROR_NOTIFYPLS; } @@ -1792,12 +2108,13 @@ int bgp_mp_reach_parse(struct bgp_attr_parser_args *args, /* must have nrli_len, what is left of the attribute */ nlri_len = LEN_LEFT; if (nlri_len > STREAM_READABLE(s)) { - zlog_info("%s: (%s) Failed to read NLRI", __func__, peer->host); + zlog_info("%s: %s sent MP_REACH_NLRI which couldn't be read", + __func__, peer->host); return BGP_ATTR_PARSE_ERROR_NOTIFYPLS; } if (!nlri_len) { - zlog_info("%s: (%s) No Reachability, Treating as a EOR marker", + zlog_info("%s: %s sent a zero-length NLRI. Hence, treating as a EOR marker", __func__, peer->host); mp_update->afi = afi; @@ -1849,8 +2166,9 @@ int bgp_mp_unreach_parse(struct bgp_attr_parser_args *args, */ if (bgp_debug_update(peer, NULL, NULL, 0)) zlog_debug( - "%s: MP_UNREACH received AFI %u or SAFI %u is unrecognized", - peer->host, pkt_afi, pkt_safi); + "%s: MP_UNREACH received AFI %s or SAFI %s is unrecognized", + peer->host, iana_afi2str(pkt_afi), + iana_safi2str(pkt_safi)); return BGP_ATTR_PARSE_ERROR; } @@ -1882,11 +2200,11 @@ bgp_attr_large_community(struct bgp_attr_parser_args *args) if (length == 0) { attr->lcommunity = NULL; /* Empty extcomm doesn't seem to be invalid per se */ - return BGP_ATTR_PARSE_PROCEED; + return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_OPT_ATTR_ERR, + args->total); } - attr->lcommunity = - lcommunity_parse((uint8_t *)stream_pnt(peer->curr), length); + attr->lcommunity = lcommunity_parse(stream_pnt(peer->curr), length); /* XXX: fix ecommunity_parse to use stream API */ stream_forward_getp(peer->curr, length); @@ -1907,18 +2225,23 @@ bgp_attr_ext_communities(struct bgp_attr_parser_args *args) struct attr *const attr = args->attr; const bgp_size_t length = args->length; uint8_t sticky = 0; + bool proxy = false; if (length == 0) { attr->ecommunity = NULL; /* Empty extcomm doesn't seem to be invalid per se */ - return BGP_ATTR_PARSE_PROCEED; + return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_OPT_ATTR_ERR, + args->total); } attr->ecommunity = - ecommunity_parse((uint8_t *)stream_pnt(peer->curr), length); + ecommunity_parse(stream_pnt(peer->curr), length); /* XXX: fix ecommunity_parse to use stream API */ stream_forward_getp(peer->curr, length); + /* The Extended Community attribute SHALL be considered malformed if + * its length is not a non-zero multiple of 8. + */ if (!attr->ecommunity) return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_OPT_ATTR_ERR, args->total); @@ -1940,7 +2263,9 @@ bgp_attr_ext_communities(struct bgp_attr_parser_args *args) attr->router_flag = 1; /* Check EVPN Neighbor advertisement flags, R-bit */ - bgp_attr_evpn_na_flag(attr, &attr->router_flag); + bgp_attr_evpn_na_flag(attr, &attr->router_flag, &proxy); + if (proxy) + attr->es_flags |= ATTR_ES_PROXY_ADVERT; /* Extract the Rmac, if any */ if (bgp_attr_rmac(attr, &attr->rmac)) { @@ -1956,6 +2281,41 @@ bgp_attr_ext_communities(struct bgp_attr_parser_args *args) } + /* Get the tunnel type from encap extended community */ + bgp_attr_extcom_tunnel_type(attr, + (bgp_encap_types *)&attr->encap_tunneltype); + + /* Extract link bandwidth, if any. */ + (void)ecommunity_linkbw_present(attr->ecommunity, &attr->link_bw); + + return BGP_ATTR_PARSE_PROCEED; +} + +/* IPv6 Extended Community attribute. */ +static bgp_attr_parse_ret_t +bgp_attr_ipv6_ext_communities(struct bgp_attr_parser_args *args) +{ + struct peer *const peer = args->peer; + struct attr *const attr = args->attr; + const bgp_size_t length = args->length; + + if (length == 0) { + attr->ipv6_ecommunity = NULL; + return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_OPT_ATTR_ERR, + args->total); + } + + attr->ipv6_ecommunity = + ecommunity_parse_ipv6(stream_pnt(peer->curr), length); + /* XXX: fix ecommunity_parse to use stream API */ + stream_forward_getp(peer->curr, length); + + if (!attr->ipv6_ecommunity) + return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_OPT_ATTR_ERR, + args->total); + + attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_IPV6_EXT_COMMUNITIES); + return BGP_ATTR_PARSE_PROCEED; } @@ -1999,8 +2359,8 @@ static int bgp_attr_encap(uint8_t type, struct peer *peer, /* IN */ length -= 4; if (tlv_length != length) { - zlog_info("%s: tlv_length(%d) != length(%d)", __func__, - tlv_length, length); + zlog_info("%s: tlv_length(%d) != length(%d)", + __func__, tlv_length, length); } } @@ -2013,7 +2373,7 @@ static int bgp_attr_encap(uint8_t type, struct peer *peer, /* IN */ subtype = stream_getc(BGP_INPUT(peer)); sublength = stream_getc(BGP_INPUT(peer)); length -= 2; -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC } else { subtype = stream_getw(BGP_INPUT(peer)); sublength = stream_getw(BGP_INPUT(peer)); @@ -2052,7 +2412,7 @@ static int bgp_attr_encap(uint8_t type, struct peer *peer, /* IN */ } else { attr->encap_subtlvs = tlv; } -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC } else { struct bgp_attr_encap_subtlv *stlv_last; for (stlv_last = attr->vnc_subtlvs; @@ -2090,10 +2450,8 @@ static int bgp_attr_encap(uint8_t type, struct peer *peer, /* IN */ * Read an individual SID value returning how much data we have read * Returns 0 if there was an error that needs to be passed up the stack */ -static bgp_attr_parse_ret_t bgp_attr_psid_sub(int32_t type, - int32_t length, - struct bgp_attr_parser_args *args, - struct bgp_nlri *mp_update) +static bgp_attr_parse_ret_t bgp_attr_psid_sub(uint8_t type, uint16_t length, + struct bgp_attr_parser_args *args) { struct peer *const peer = args->peer; struct attr *const attr = args->attr; @@ -2102,13 +2460,16 @@ static bgp_attr_parse_ret_t bgp_attr_psid_sub(int32_t type, uint32_t srgb_base; uint32_t srgb_range; int srgb_count; + uint8_t sid_type, sid_flags; + uint16_t endpoint_behavior; + char buf[BUFSIZ]; if (type == BGP_PREFIX_SID_LABEL_INDEX) { - if (length != BGP_PREFIX_SID_LABEL_INDEX_LENGTH) { - flog_err( - EC_BGP_ATTR_LEN, - "Prefix SID label index length is %d instead of %d", - length, BGP_PREFIX_SID_LABEL_INDEX_LENGTH); + if (STREAM_READABLE(peer->curr) < length + || length != BGP_PREFIX_SID_LABEL_INDEX_LENGTH) { + flog_err(EC_BGP_ATTR_LEN, + "Prefix SID label index length is %hu instead of %u", + length, BGP_PREFIX_SID_LABEL_INDEX_LENGTH); return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, args->total); @@ -2127,22 +2488,14 @@ static bgp_attr_parse_ret_t bgp_attr_psid_sub(int32_t type, /* Store label index; subsequently, we'll check on * address-family */ attr->label_index = label_index; - - /* - * Ignore the Label index attribute unless received for - * labeled-unicast - * SAFI. - */ - if (!mp_update->length - || mp_update->safi != SAFI_LABELED_UNICAST) - attr->label_index = BGP_INVALID_LABEL_INDEX; } /* Placeholder code for the IPv6 SID type */ else if (type == BGP_PREFIX_SID_IPV6) { - if (length != BGP_PREFIX_SID_IPV6_LENGTH) { + if (STREAM_READABLE(peer->curr) < length + || length != BGP_PREFIX_SID_IPV6_LENGTH) { flog_err(EC_BGP_ATTR_LEN, - "Prefix SID IPv6 length is %d instead of %d", + "Prefix SID IPv6 length is %hu instead of %u", length, BGP_PREFIX_SID_IPV6_LENGTH); return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, @@ -2158,15 +2511,54 @@ static bgp_attr_parse_ret_t bgp_attr_psid_sub(int32_t type, /* Placeholder code for the Originator SRGB type */ else if (type == BGP_PREFIX_SID_ORIGINATOR_SRGB) { - /* Ignore flags */ - stream_getw(peer->curr); + /* + * ietf-idr-bgp-prefix-sid-05: + * Length is the total length of the value portion of the + * TLV: 2 + multiple of 6. + * + * peer->curr stream readp should be at the beginning of the 16 + * bit flag field at this point in the code. + */ - length -= 2; + /* + * Check that the TLV length field is sane: at least 2 bytes of + * flag, and at least 1 SRGB (these are 6 bytes each) + */ + if (length < (2 + BGP_PREFIX_SID_ORIGINATOR_SRGB_LENGTH)) { + flog_err( + EC_BGP_ATTR_LEN, + "Prefix SID Originator SRGB length field claims length of %hu bytes, but the minimum for this TLV type is %u", + length, + 2 + BGP_PREFIX_SID_ORIGINATOR_SRGB_LENGTH); + return bgp_attr_malformed( + args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, + args->total); + } + /* + * Check that we actually have at least as much data as + * specified by the length field + */ + if (STREAM_READABLE(peer->curr) < length) { + flog_err(EC_BGP_ATTR_LEN, + "Prefix SID Originator SRGB specifies length %hu, but only %zu bytes remain", + length, STREAM_READABLE(peer->curr)); + return bgp_attr_malformed( + args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, + args->total); + } + + /* + * Check that the portion of the TLV containing the sequence of + * SRGBs corresponds to a multiple of the SRGB size; to get + * that length, we skip the 16 bit flags field + */ + stream_getw(peer->curr); + length -= 2; if (length % BGP_PREFIX_SID_ORIGINATOR_SRGB_LENGTH) { flog_err( EC_BGP_ATTR_LEN, - "Prefix SID Originator SRGB length is %d, it must be a multiple of %d ", + "Prefix SID Originator SRGB length field claims attribute SRGB sequence section is %hubytes, but it must be a multiple of %u", length, BGP_PREFIX_SID_ORIGINATOR_SRGB_LENGTH); return bgp_attr_malformed( args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, @@ -2181,15 +2573,116 @@ static bgp_attr_parse_ret_t bgp_attr_psid_sub(int32_t type, } } + /* Placeholder code for the VPN-SID Service type */ + else if (type == BGP_PREFIX_SID_VPN_SID) { + if (STREAM_READABLE(peer->curr) < length + || length != BGP_PREFIX_SID_VPN_SID_LENGTH) { + flog_err(EC_BGP_ATTR_LEN, + "Prefix SID VPN SID length is %hu instead of %u", + length, BGP_PREFIX_SID_VPN_SID_LENGTH); + return bgp_attr_malformed(args, + BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, + args->total); + } + + /* Parse VPN-SID Sub-TLV */ + stream_getc(peer->curr); /* reserved */ + sid_type = stream_getc(peer->curr); /* sid_type */ + sid_flags = stream_getc(peer->curr); /* sid_flags */ + stream_get(&ipv6_sid, peer->curr, + sizeof(ipv6_sid)); /* sid_value */ + + /* Log VPN-SID Sub-TLV */ + if (BGP_DEBUG(vpn, VPN_LEAK_LABEL)) { + inet_ntop(AF_INET6, &ipv6_sid, buf, sizeof(buf)); + zlog_debug( + "%s: vpn-sid: sid %s, sid-type 0x%02x sid-flags 0x%02x", + __func__, buf, sid_type, sid_flags); + } + + /* Configure from Info */ + if (attr->srv6_vpn) { + flog_err(EC_BGP_ATTRIBUTE_REPEATED, + "Prefix SID SRv6 VPN field repeated"); + return bgp_attr_malformed( + args, BGP_NOTIFY_UPDATE_MAL_ATTR, args->total); + } + attr->srv6_vpn = XCALLOC(MTYPE_BGP_SRV6_VPN, + sizeof(struct bgp_attr_srv6_vpn)); + attr->srv6_vpn->sid_flags = sid_flags; + sid_copy(&attr->srv6_vpn->sid, &ipv6_sid); + } + + /* Placeholder code for the SRv6 L3 Service type */ + else if (type == BGP_PREFIX_SID_SRV6_L3_SERVICE) { + if (STREAM_READABLE(peer->curr) < length + || length != BGP_PREFIX_SID_SRV6_L3_SERVICE_LENGTH) { + flog_err(EC_BGP_ATTR_LEN, + "Prefix SID SRv6 L3-Service length is %hu instead of %u", + length, BGP_PREFIX_SID_SRV6_L3_SERVICE_LENGTH); + return bgp_attr_malformed(args, + BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, + args->total); + } + + /* Parse L3-SERVICE Sub-TLV */ + stream_getc(peer->curr); /* reserved */ + stream_get(&ipv6_sid, peer->curr, + sizeof(ipv6_sid)); /* sid_value */ + sid_flags = stream_getc(peer->curr); /* sid_flags */ + endpoint_behavior = stream_getw(peer->curr); /* endpoint */ + stream_getc(peer->curr); /* reserved */ + + /* Log L3-SERVICE Sub-TLV */ + if (BGP_DEBUG(vpn, VPN_LEAK_LABEL)) { + inet_ntop(AF_INET6, &ipv6_sid, buf, sizeof(buf)); + zlog_debug( + "%s: srv6-l3-srv sid %s, sid-flags 0x%02x, end-behaviour 0x%04x", + __func__, buf, sid_flags, endpoint_behavior); + } + + /* Configure from Info */ + if (attr->srv6_l3vpn) { + flog_err(EC_BGP_ATTRIBUTE_REPEATED, + "Prefix SID SRv6 L3VPN field repeated"); + return bgp_attr_malformed( + args, BGP_NOTIFY_UPDATE_MAL_ATTR, args->total); + } + attr->srv6_l3vpn = XCALLOC(MTYPE_BGP_SRV6_L3VPN, + sizeof(struct bgp_attr_srv6_l3vpn)); + attr->srv6_l3vpn->sid_flags = sid_flags; + attr->srv6_l3vpn->endpoint_behavior = endpoint_behavior; + sid_copy(&attr->srv6_l3vpn->sid, &ipv6_sid); + } + + /* Placeholder code for Unsupported TLV */ + else { + + if (STREAM_READABLE(peer->curr) < length) { + flog_err( + EC_BGP_ATTR_LEN, + "Prefix SID SRv6 length is %hu - too long, only %zu remaining in this UPDATE", + length, STREAM_READABLE(peer->curr)); + return bgp_attr_malformed( + args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, + args->total); + } + + if (bgp_debug_update(peer, NULL, NULL, 1)) + zlog_debug( + "%s attr Prefix-SID sub-type=%u is not supported, skipped", + peer->host, type); + + stream_forward_getp(peer->curr, length); + } + return BGP_ATTR_PARSE_PROCEED; } /* Prefix SID attribute * draft-ietf-idr-bgp-prefix-sid-05 */ -bgp_attr_parse_ret_t -bgp_attr_prefix_sid(int32_t tlength, struct bgp_attr_parser_args *args, - struct bgp_nlri *mp_update) +bgp_attr_parse_ret_t bgp_attr_prefix_sid(struct bgp_attr_parser_args *args) { struct peer *const peer = args->peer; struct attr *const attr = args->attr; @@ -2197,30 +2690,52 @@ bgp_attr_prefix_sid(int32_t tlength, struct bgp_attr_parser_args *args, attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_PREFIX_SID); - while (tlength) { - int32_t type, length; + uint8_t type; + uint16_t length; + size_t headersz = sizeof(type) + sizeof(length); + size_t psid_parsed_length = 0; + + while (STREAM_READABLE(peer->curr) > 0 + && psid_parsed_length < args->length) { + + if (STREAM_READABLE(peer->curr) < headersz) { + flog_err( + EC_BGP_ATTR_LEN, + "Malformed Prefix SID attribute - insufficent data (need %zu for attribute header, have %zu remaining in UPDATE)", + headersz, STREAM_READABLE(peer->curr)); + return bgp_attr_malformed( + args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, + args->total); + } type = stream_getc(peer->curr); length = stream_getw(peer->curr); - ret = bgp_attr_psid_sub(type, length, args, mp_update); + if (STREAM_READABLE(peer->curr) < length) { + flog_err( + EC_BGP_ATTR_LEN, + "Malformed Prefix SID attribute - insufficient data (need %hu for attribute body, have %zu remaining in UPDATE)", + length, STREAM_READABLE(peer->curr)); + return bgp_attr_malformed(args, + BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, + args->total); + } + + ret = bgp_attr_psid_sub(type, length, args); if (ret != BGP_ATTR_PARSE_PROCEED) return ret; - /* - * Subtract length + the T and the L - * since length is the Vector portion - */ - tlength -= length + 3; - if (tlength < 0) { + psid_parsed_length += length + headersz; + + if (psid_parsed_length > args->length) { flog_err( EC_BGP_ATTR_LEN, - "Prefix SID internal length %d causes us to read beyond the total Prefix SID length", - length); - return bgp_attr_malformed(args, - BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, - args->total); + "Malformed Prefix SID attribute - TLV overflow by attribute (need %zu for TLV length, have %zu overflowed in UPDATE)", + length + headersz, psid_parsed_length - (length + headersz)); + return bgp_attr_malformed( + args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, + args->total); } } @@ -2372,14 +2887,14 @@ static int bgp_attr_check(struct peer *peer, struct attr *attr) && !CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))) type = BGP_ATTR_LOCAL_PREF; + /* If any of the well-known mandatory attributes are not present + * in an UPDATE message, then "treat-as-withdraw" MUST be used. + */ if (type) { flog_warn(EC_BGP_MISSING_ATTRIBUTE, "%s Missing well-known attribute %s.", peer->host, lookup_msg(attr_str, type, NULL)); - bgp_notify_send_with_data(peer, BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_MISS_ATTR, &type, - 1); - return BGP_ATTR_PARSE_ERROR; + return BGP_ATTR_PARSE_WITHDRAW; } return BGP_ATTR_PARSE_PROCEED; } @@ -2424,7 +2939,8 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr, bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); - return BGP_ATTR_PARSE_ERROR; + ret = BGP_ATTR_PARSE_ERROR; + goto done; } /* Fetch attribute flag and type. */ @@ -2447,7 +2963,8 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr, bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); - return BGP_ATTR_PARSE_ERROR; + ret = BGP_ATTR_PARSE_ERROR; + goto done; } /* Check extended attribue length bit. */ @@ -2468,7 +2985,8 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr, bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR, BGP_NOTIFY_UPDATE_MAL_ATTR); - return BGP_ATTR_PARSE_ERROR; + ret = BGP_ATTR_PARSE_ERROR; + goto done; } /* Set type to bitmap to check duplicate attribute. `type' is @@ -2510,7 +3028,7 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr, size_t lfl = CHECK_FLAG(flag, BGP_ATTR_FLAG_EXTLEN) ? 2 : 1; /* Rewind to end of flag field */ - stream_forward_getp(BGP_INPUT(peer), -(1 + lfl)); + stream_rewind_getp(BGP_INPUT(peer), (1 + lfl)); /* Type */ stream_get(&ndata[0], BGP_INPUT(peer), 1); /* Length */ @@ -2525,7 +3043,8 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, ndata, ndl + lfl + 1); - return BGP_ATTR_PARSE_ERROR; + ret = BGP_ATTR_PARSE_ERROR; + goto done; } struct bgp_attr_parser_args attr_args = { @@ -2550,7 +3069,7 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr, attr_args.total); if (ret == BGP_ATTR_PARSE_PROCEED) continue; - return ret; + goto done; } /* OK check attribute and store it's value. */ @@ -2605,7 +3124,7 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr, case BGP_ATTR_EXT_COMMUNITIES: ret = bgp_attr_ext_communities(&attr_args); break; -#if ENABLE_BGP_VNC_ATTR +#ifdef ENABLE_BGP_VNC_ATTR case BGP_ATTR_VNC: #endif case BGP_ATTR_ENCAP: @@ -2613,12 +3132,14 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr, startp); break; case BGP_ATTR_PREFIX_SID: - ret = bgp_attr_prefix_sid(length, - &attr_args, mp_update); + ret = bgp_attr_prefix_sid(&attr_args); break; case BGP_ATTR_PMSI_TUNNEL: ret = bgp_attr_pmsi_tunnel(&attr_args); break; + case BGP_ATTR_IPV6_EXT_COMMUNITIES: + ret = bgp_attr_ipv6_ext_communities(&attr_args); + break; default: ret = bgp_attr_unknown(&attr_args); break; @@ -2628,32 +3149,25 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr, bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR, BGP_NOTIFY_UPDATE_MAL_ATTR); ret = BGP_ATTR_PARSE_ERROR; + goto done; } if (ret == BGP_ATTR_PARSE_EOR) { - if (as4_path) - aspath_unintern(&as4_path); - return ret; + goto done; } - /* If hard error occurred immediately return to the caller. */ if (ret == BGP_ATTR_PARSE_ERROR) { flog_warn(EC_BGP_ATTRIBUTE_PARSE_ERROR, "%s: Attribute %s, parse error", peer->host, lookup_msg(attr_str, type, NULL)); - if (as4_path) - aspath_unintern(&as4_path); - return ret; + goto done; } if (ret == BGP_ATTR_PARSE_WITHDRAW) { - flog_warn( EC_BGP_ATTRIBUTE_PARSE_WITHDRAW, "%s: Attribute %s, parse error - treating as withdrawal", peer->host, lookup_msg(attr_str, type, NULL)); - if (as4_path) - aspath_unintern(&as4_path); - return ret; + goto done; } /* Check the fetched length. */ @@ -2663,12 +3177,22 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr, peer->host, lookup_msg(attr_str, type, NULL)); bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); - if (as4_path) - aspath_unintern(&as4_path); - return BGP_ATTR_PARSE_ERROR; + ret = BGP_ATTR_PARSE_ERROR; + goto done; } } + /* + * draft-ietf-idr-bgp-prefix-sid-27#section-3: + * About Prefix-SID path attribute, + * Label-Index TLV(type1) and The Originator SRGB TLV(type-3) + * may only appear in a BGP Prefix-SID attribute attached to + * IPv4/IPv6 Labeled Unicast prefixes ([RFC8277]). + * It MUST be ignored when received for other BGP AFI/SAFI combinations. + */ + if (!attr->mp_nexthop_len || mp_update->safi != SAFI_LABELED_UNICAST) + attr->label_index = BGP_INVALID_LABEL_INDEX; + /* Check final read pointer is same as end pointer. */ if (BGP_INPUT_PNT(peer) != endp) { flog_warn(EC_BGP_ATTRIBUTES_MISMATCH, @@ -2676,18 +3200,36 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr, lookup_msg(attr_str, type, NULL)); bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); - if (as4_path) - aspath_unintern(&as4_path); - return BGP_ATTR_PARSE_ERROR; + + ret = BGP_ATTR_PARSE_ERROR; + goto done; } - /* Check all mandatory well-known attributes are present */ - if ((ret = bgp_attr_check(peer, attr)) < 0) { - if (as4_path) - aspath_unintern(&as4_path); - return ret; + /* + * RFC4271: If the NEXT_HOP attribute field is syntactically incorrect, + * then the Error Subcode MUST be set to Invalid NEXT_HOP Attribute. + * This is implemented below and will result in a NOTIFICATION. If the + * NEXT_HOP attribute is semantically incorrect, the error SHOULD be + * logged, and the route SHOULD be ignored. In this case, a NOTIFICATION + * message SHOULD NOT be sent. This is implemented elsewhere. + * + * RFC4760: An UPDATE message that carries no NLRI, other than the one + * encoded in the MP_REACH_NLRI attribute, SHOULD NOT carry the NEXT_HOP + * attribute. If such a message contains the NEXT_HOP attribute, the BGP + * speaker that receives the message SHOULD ignore this attribute. + */ + if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP)) + && !CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_MP_REACH_NLRI))) { + if (bgp_attr_nexthop_valid(peer, attr) < 0) { + ret = BGP_ATTR_PARSE_ERROR; + goto done; + } } + /* Check all mandatory well-known attributes are present */ + if ((ret = bgp_attr_check(peer, attr)) < 0) + goto done; + /* * At this place we can see whether we got AS4_PATH and/or * AS4_AGGREGATOR from a 16Bit peer and act accordingly. @@ -2709,28 +3251,10 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr, &as4_aggregator_addr)) { bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR, BGP_NOTIFY_UPDATE_MAL_ATTR); - if (as4_path) - aspath_unintern(&as4_path); - return BGP_ATTR_PARSE_ERROR; + ret = BGP_ATTR_PARSE_ERROR; + goto done; } - /* At this stage, we have done all fiddling with as4, and the - * resulting info is in attr->aggregator resp. attr->aspath - * so we can chuck as4_aggregator and as4_path alltogether in - * order to save memory - */ - if (as4_path) { - aspath_unintern(&as4_path); /* unintern - it is in the hash */ - /* The flag that we got this is still there, but that does not - * do any trouble - */ - } - /* - * The "rest" of the code does nothing with as4_aggregator. - * there is no memory attached specifically which is not part - * of the attr. - * so ignoring just means do nothing. - */ /* * Finally do the checks on the aspath we did not do yet * because we waited for a potentially synthesized aspath. @@ -2738,21 +3262,91 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr, if (attr->flag & (ATTR_FLAG_BIT(BGP_ATTR_AS_PATH))) { ret = bgp_attr_aspath_check(peer, attr); if (ret != BGP_ATTR_PARSE_PROCEED) - return ret; + goto done; } - /* Finally intern unknown attribute. */ + + ret = BGP_ATTR_PARSE_PROCEED; +done: + + /* + * At this stage, we have done all fiddling with as4, and the + * resulting info is in attr->aggregator resp. attr->aspath so + * we can chuck as4_aggregator and as4_path alltogether in order + * to save memory + */ + if (as4_path) { + /* + * unintern - it is in the hash + * The flag that we got this is still there, but that + * does not do any trouble + */ + aspath_unintern(&as4_path); + } + + if (ret != BGP_ATTR_PARSE_ERROR) { + /* Finally intern unknown attribute. */ + if (attr->transit) + attr->transit = transit_intern(attr->transit); + if (attr->encap_subtlvs) + attr->encap_subtlvs = encap_intern(attr->encap_subtlvs, + ENCAP_SUBTLV_TYPE); +#ifdef ENABLE_BGP_VNC + if (attr->vnc_subtlvs) + attr->vnc_subtlvs = encap_intern(attr->vnc_subtlvs, + VNC_SUBTLV_TYPE); +#endif + } else { + if (attr->transit) { + transit_free(attr->transit); + attr->transit = NULL; + } + + bgp_attr_flush_encap(attr); + }; + + /* Sanity checks */ if (attr->transit) - attr->transit = transit_intern(attr->transit); + assert(attr->transit->refcnt > 0); if (attr->encap_subtlvs) - attr->encap_subtlvs = - encap_intern(attr->encap_subtlvs, ENCAP_SUBTLV_TYPE); -#if ENABLE_BGP_VNC + assert(attr->encap_subtlvs->refcnt > 0); +#ifdef ENABLE_BGP_VNC if (attr->vnc_subtlvs) - attr->vnc_subtlvs = - encap_intern(attr->vnc_subtlvs, VNC_SUBTLV_TYPE); + assert(attr->vnc_subtlvs->refcnt > 0); #endif - return BGP_ATTR_PARSE_PROCEED; + return ret; +} + +/* + * Extract the tunnel type from extended community + */ +void bgp_attr_extcom_tunnel_type(struct attr *attr, + bgp_encap_types *tunnel_type) +{ + struct ecommunity *ecom; + int i; + if (!attr) + return; + + ecom = attr->ecommunity; + if (!ecom || !ecom->size) + return; + + for (i = 0; i < ecom->size; i++) { + uint8_t *pnt; + uint8_t type, sub_type; + + pnt = (ecom->val + (i * ECOMMUNITY_SIZE)); + type = pnt[0]; + sub_type = pnt[1]; + if (!(type == ECOMMUNITY_ENCODE_OPAQUE && + sub_type == ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP)) + continue; + *tunnel_type = ((pnt[6] << 8) | pnt[7]); + return; + } + + return; } size_t bgp_packet_mpattr_start(struct stream *s, struct peer *peer, afi_t afi, @@ -2779,8 +3373,11 @@ size_t bgp_packet_mpattr_start(struct stream *s, struct peer *peer, afi_t afi, /* Nexthop AFI */ if (afi == AFI_IP - && (safi == SAFI_UNICAST || safi == SAFI_LABELED_UNICAST)) + && (safi == SAFI_UNICAST || safi == SAFI_LABELED_UNICAST + || safi == SAFI_MPLS_VPN || safi == SAFI_MULTICAST)) nh_afi = peer_cap_enhe(peer, afi, safi) ? AFI_IP6 : AFI_IP; + else if (safi == SAFI_FLOWSPEC) + nh_afi = afi; else nh_afi = BGP_NEXTHOP_AFI_FROM_NHLEN(attr->mp_nexthop_len); @@ -2807,7 +3404,12 @@ size_t bgp_packet_mpattr_start(struct stream *s, struct peer *peer, afi_t afi, stream_put(s, &attr->mp_nexthop_global_in, 4); break; case SAFI_FLOWSPEC: - stream_putc(s, 0); /* no nexthop for flowspec */ + if (attr->mp_nexthop_len == 0) + stream_putc(s, 0); /* no nexthop for flowspec */ + else { + stream_putc(s, attr->mp_nexthop_len); + stream_put_ipv4(s, attr->nexthop.s_addr); + } default: break; } @@ -2879,10 +3481,10 @@ size_t bgp_packet_mpattr_start(struct stream *s, struct peer *peer, afi_t afi, } void bgp_packet_mpattr_prefix(struct stream *s, afi_t afi, safi_t safi, - struct prefix *p, struct prefix_rd *prd, - mpls_label_t *label, uint32_t num_labels, - int addpath_encode, uint32_t addpath_tx_id, - struct attr *attr) + const struct prefix *p, + const struct prefix_rd *prd, mpls_label_t *label, + uint32_t num_labels, int addpath_encode, + uint32_t addpath_tx_id, struct attr *attr) { if (safi == SAFI_MPLS_VPN) { if (addpath_encode) @@ -2898,24 +3500,24 @@ void bgp_packet_mpattr_prefix(struct stream *s, afi_t afi, safi_t safi, addpath_encode, addpath_tx_id); } else if (safi == SAFI_LABELED_UNICAST) { /* Prefix write with label. */ - stream_put_labeled_prefix(s, p, label); + stream_put_labeled_prefix(s, p, label, addpath_encode, + addpath_tx_id); } else if (safi == SAFI_FLOWSPEC) { - if (PSIZE (p->prefixlen)+2 < FLOWSPEC_NLRI_SIZELIMIT) - stream_putc(s, PSIZE (p->prefixlen)+2); - else - stream_putw(s, (PSIZE (p->prefixlen)+2)|(0xf<<12)); - stream_putc(s, 2);/* Filter type */ - stream_putc(s, p->prefixlen);/* Prefix length */ - stream_put(s, &p->u.prefix, PSIZE (p->prefixlen)); + stream_putc(s, p->u.prefix_flowspec.prefixlen); + stream_put(s, (const void *)p->u.prefix_flowspec.ptr, + p->u.prefix_flowspec.prefixlen); } else stream_put_prefix_addpath(s, p, addpath_encode, addpath_tx_id); } -size_t bgp_packet_mpattr_prefix_size(afi_t afi, safi_t safi, struct prefix *p) +size_t bgp_packet_mpattr_prefix_size(afi_t afi, safi_t safi, + const struct prefix *p) { int size = PSIZE(p->prefixlen); if (safi == SAFI_MPLS_VPN) size += 88; + else if (safi == SAFI_LABELED_UNICAST) + size += BGP_LABEL_BYTES; else if (afi == AFI_L2VPN && safi == SAFI_EVPN) size += 232; // TODO: Maximum possible for type-2, type-3 and // type-5 @@ -2958,7 +3560,7 @@ static void bgp_packet_mpattr_tea(struct bgp *bgp, struct peer *peer, attrhdrlen = 1 + 1; /* subTLV T + L */ break; -#if ENABLE_BGP_VNC_ATTR +#ifdef ENABLE_BGP_VNC_ATTR case BGP_ATTR_VNC: attrname = "VNC"; subtlvs = attr->vnc_subtlvs; @@ -3009,7 +3611,7 @@ static void bgp_packet_mpattr_tea(struct bgp *bgp, struct peer *peer, if (attrtype == BGP_ATTR_ENCAP) { stream_putc(s, st->type); stream_putc(s, st->length); -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC } else { stream_putw(s, st->type); stream_putw(s, st->length); @@ -3026,7 +3628,7 @@ void bgp_packet_mpattr_end(struct stream *s, size_t sizep) stream_putw_at(s, sizep, (stream_get_endp(s) - sizep) - 2); } -static int bgp_append_local_as(struct peer *peer, afi_t afi, safi_t safi) +static bool bgp_append_local_as(struct peer *peer, afi_t afi, safi_t safi) { if (!BGP_AS_IS_PRIVATE(peer->local_as) || (BGP_AS_IS_PRIVATE(peer->local_as) @@ -3038,8 +3640,8 @@ static int bgp_append_local_as(struct peer *peer, afi_t afi, safi_t safi) PEER_FLAG_REMOVE_PRIVATE_AS_REPLACE) && !CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_REMOVE_PRIVATE_AS_ALL_REPLACE))) - return 1; - return 0; + return true; + return false; } /* Make attribute packet. */ @@ -3155,6 +3757,8 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer, /* Nexthop attribute. */ if (afi == AFI_IP && safi == SAFI_UNICAST && !peer_cap_enhe(peer, afi, safi)) { + afi_t nh_afi = BGP_NEXTHOP_AFI_FROM_NHLEN(attr->mp_nexthop_len); + if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP)) { stream_putc(s, BGP_ATTR_FLAG_TRANS); stream_putc(s, BGP_ATTR_NEXT_HOP); @@ -3162,17 +3766,18 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer, attr); stream_putc(s, 4); stream_put_ipv4(s, attr->nexthop.s_addr); - } else if (peer_cap_enhe(from, afi, safi)) { + } else if (peer_cap_enhe(from, afi, safi) + || (nh_afi == AFI_IP6)) { /* * Likely this is the case when an IPv4 prefix was - * received with - * Extended Next-hop capability and now being advertised - * to - * non-ENHE peers. + * received with Extended Next-hop capability in this + * or another vrf and is now being advertised to + * non-ENHE peers. Since peer_cap_enhe only checks + * peers in this vrf, also check the nh_afi to catch + * the case where the originator was in another vrf. * Setting the mandatory (ipv4) next-hop attribute here - * to enable - * implicit next-hop self with correct (ipv4 address - * family). + * to enable implicit next-hop self with correct A-F + * (ipv4 address family). */ stream_putc(s, BGP_ATTR_FLAG_TRANS); stream_putc(s, BGP_ATTR_NEXT_HOP); @@ -3415,6 +4020,36 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer, } } + /* SRv6 Service Information Attribute. */ + if (afi == AFI_IP && safi == SAFI_MPLS_VPN) { + if (attr->srv6_l3vpn) { + stream_putc(s, BGP_ATTR_FLAG_OPTIONAL + | BGP_ATTR_FLAG_TRANS); + stream_putc(s, BGP_ATTR_PREFIX_SID); + stream_putc(s, 24); /* tlv len */ + stream_putc(s, BGP_PREFIX_SID_SRV6_L3_SERVICE); + stream_putw(s, 21); /* sub-tlv len */ + stream_putc(s, 0); /* reserved */ + stream_put(s, &attr->srv6_l3vpn->sid, + sizeof(attr->srv6_l3vpn->sid)); /* sid */ + stream_putc(s, 0); /* sid_flags */ + stream_putw(s, 0xffff); /* endpoint */ + stream_putc(s, 0); /* reserved */ + } else if (attr->srv6_vpn) { + stream_putc(s, BGP_ATTR_FLAG_OPTIONAL + | BGP_ATTR_FLAG_TRANS); + stream_putc(s, BGP_ATTR_PREFIX_SID); + stream_putc(s, 22); /* tlv len */ + stream_putc(s, BGP_PREFIX_SID_VPN_SID); + stream_putw(s, 0x13); /* tlv len */ + stream_putc(s, 0x00); /* reserved */ + stream_putc(s, 0x01); /* sid_type */ + stream_putc(s, 0x00); /* sif_flags */ + stream_put(s, &attr->srv6_vpn->sid, + sizeof(attr->srv6_vpn->sid)); /* sid */ + } + } + if (send_as4_path) { /* If the peer is NOT As4 capable, AND */ /* there are ASnums > 65535 in path THEN @@ -3461,7 +4096,7 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer, /* Tunnel Encap attribute */ bgp_packet_mpattr_tea(bgp, peer, s, attr, BGP_ATTR_ENCAP); -#if ENABLE_BGP_VNC_ATTR +#ifdef ENABLE_BGP_VNC_ATTR /* VNC attribute */ bgp_packet_mpattr_tea(bgp, peer, s, attr, BGP_ATTR_VNC); #endif @@ -3510,8 +4145,9 @@ size_t bgp_packet_mpunreach_start(struct stream *s, afi_t afi, safi_t safi) return attrlen_pnt; } -void bgp_packet_mpunreach_prefix(struct stream *s, struct prefix *p, afi_t afi, - safi_t safi, struct prefix_rd *prd, +void bgp_packet_mpunreach_prefix(struct stream *s, const struct prefix *p, + afi_t afi, safi_t safi, + const struct prefix_rd *prd, mpls_label_t *label, uint32_t num_labels, int addpath_encode, uint32_t addpath_tx_id, struct attr *attr) @@ -3543,6 +4179,7 @@ void bgp_attr_init(void) cluster_init(); transit_init(); encap_init(); + srv6_init(); } void bgp_attr_finish(void) @@ -3555,11 +4192,12 @@ void bgp_attr_finish(void) cluster_finish(); transit_finish(); encap_finish(); + srv6_finish(); } /* Make attribute packet. */ void bgp_dump_routes_attr(struct stream *s, struct attr *attr, - struct prefix *prefix) + const struct prefix *prefix) { unsigned long cp; unsigned long len; diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h index 6d5c647b21..e6e953364b 100644 --- a/bgpd/bgp_attr.h +++ b/bgpd/bgp_attr.h @@ -23,6 +23,8 @@ #include "mpls.h" #include "bgp_attr_evpn.h" +#include "bgpd/bgp_encap_types.h" +#include "srte.h" /* Simple bit mapping. */ #define BITMAP_NBBY 8 @@ -61,10 +63,19 @@ #define BGP_PREFIX_SID_LABEL_INDEX 1 #define BGP_PREFIX_SID_IPV6 2 #define BGP_PREFIX_SID_ORIGINATOR_SRGB 3 +#define BGP_PREFIX_SID_VPN_SID 4 +#define BGP_PREFIX_SID_SRV6_L3_SERVICE 5 +#define BGP_PREFIX_SID_SRV6_L2_SERVICE 6 #define BGP_PREFIX_SID_LABEL_INDEX_LENGTH 7 #define BGP_PREFIX_SID_IPV6_LENGTH 19 #define BGP_PREFIX_SID_ORIGINATOR_SRGB_LENGTH 6 +#define BGP_PREFIX_SID_VPN_SID_LENGTH 19 +#define BGP_PREFIX_SID_SRV6_L3_SERVICE_LENGTH 21 + +#define BGP_ATTR_NH_AFI(afi, attr) \ + ((afi != AFI_L2VPN) ? afi : \ + ((attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV4) ? AFI_IP : AFI_IP6)) /* PMSI tunnel types (RFC 6514) */ @@ -77,7 +88,7 @@ struct bgp_attr_encap_subtlv { uint8_t value[0]; /* will be extended */ }; -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC /* * old rfp<->rfapi representation */ @@ -104,6 +115,29 @@ enum pta_type { PMSI_TNLTYPE_MAX = PMSI_TNLTYPE_MLDP_MP2MP }; +/* + * Prefix-SID type-4 + * SRv6-VPN-SID-TLV + * draft-dawra-idr-srv6-vpn-04 + */ +struct bgp_attr_srv6_vpn { + unsigned long refcnt; + uint8_t sid_flags; + struct in6_addr sid; +}; + +/* + * Prefix-SID type-5 + * SRv6-L3VPN-Service-TLV + * draft-dawra-idr-srv6-vpn-05 + */ +struct bgp_attr_srv6_l3vpn { + unsigned long refcnt; + uint8_t sid_flags; + uint16_t endpoint_behavior; + struct in6_addr sid; +}; + /* BGP core attribute structure. */ struct attr { /* AS Path structure */ @@ -144,6 +178,9 @@ struct attr { /* Extended Communities attribute. */ struct ecommunity *ecommunity; + /* Extended Communities attribute. */ + struct ecommunity *ipv6_ecommunity; + /* Large Communities attribute. */ struct lcommunity *lcommunity; @@ -182,6 +219,30 @@ struct attr { /* NA router flag (R-bit) support in EVPN */ uint8_t router_flag; + /* ES info */ + uint8_t es_flags; + /* Path is not "locally-active" on the advertising VTEP. This is + * translated into an ARP-ND ECOM. + */ +#define ATTR_ES_PROXY_ADVERT (1 << 0) + /* Destination ES is present locally. This flag is set on local + * paths and sync paths + */ +#define ATTR_ES_IS_LOCAL (1 << 1) + /* There are one or more non-best paths from ES peers. Note that + * this flag is only set on the local MAC-IP paths in the VNI + * route table (not set in the global routing table). And only + * non-proxy advertisements from an ES peer can result in this + * flag being set. + */ +#define ATTR_ES_PEER_ACTIVE (1 << 2) + /* There are one or more non-best proxy paths from ES peers */ +#define ATTR_ES_PEER_PROXY (1 << 3) + /* An ES peer has router bit set - only applicable if + * ATTR_ES_PEER_ACTIVE is set + */ +#define ATTR_ES_PEER_ROUTER (1 << 4) + /* route tag */ route_tag_t tag; @@ -191,10 +252,16 @@ struct attr { /* MPLS label */ mpls_label_t label; + /* SRv6 VPN SID */ + struct bgp_attr_srv6_vpn *srv6_vpn; + + /* SRv6 L3VPN SID */ + struct bgp_attr_srv6_l3vpn *srv6_l3vpn; + uint16_t encap_tunneltype; /* grr */ struct bgp_attr_encap_subtlv *encap_subtlvs; /* rfc5512 */ -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC struct bgp_attr_encap_subtlv *vnc_subtlvs; /* VNC-specific */ #endif /* EVPN */ @@ -202,9 +269,31 @@ struct attr { /* EVPN MAC Mobility sequence number, if any. */ uint32_t mm_seqnum; + /* highest MM sequence number rxed in a MAC-IP route from an + * ES peer (this includes both proxy and non-proxy MAC-IP + * advertisements from ES peers). + * This is only applicable to local paths in the VNI routing + * table and derived from other imported/non-best paths. + */ + uint32_t mm_sync_seqnum; /* EVPN local router-mac */ struct ethaddr rmac; + + /* Distance as applied by Route map */ + uint8_t distance; + + /* rmap set table */ + uint32_t rmap_table_id; + + /* Link bandwidth value, if any. */ + uint32_t link_bw; + + /* EVPN ES */ + esi_t esi; + + /* SR-TE Color */ + uint32_t srte_color; }; /* rmap_change_flags definition */ @@ -215,6 +304,7 @@ struct attr { #define BATTR_RMAP_IPV6_GLOBAL_NHOP_CHANGED (1 << 4) #define BATTR_RMAP_IPV6_LL_NHOP_CHANGED (1 << 5) #define BATTR_RMAP_IPV6_PREFER_GLOBAL_CHANGED (1 << 6) +#define BATTR_RMAP_LINK_BW_SET (1 << 7) /* Router Reflector related structure. */ struct cluster_list { @@ -259,40 +349,33 @@ extern void bgp_attr_finish(void); extern bgp_attr_parse_ret_t bgp_attr_parse(struct peer *, struct attr *, bgp_size_t, struct bgp_nlri *, struct bgp_nlri *); -extern void bgp_attr_dup(struct attr *, struct attr *); extern void bgp_attr_undup(struct attr *new, struct attr *old); extern struct attr *bgp_attr_intern(struct attr *attr); extern void bgp_attr_unintern_sub(struct attr *); extern void bgp_attr_unintern(struct attr **); extern void bgp_attr_flush(struct attr *); extern struct attr *bgp_attr_default_set(struct attr *attr, uint8_t); -extern struct attr *bgp_attr_aggregate_intern(struct bgp *bgp, uint8_t origin, - struct aspath *aspath, - struct community *community, - struct ecommunity *ecommunity, - struct lcommunity *lcommunity, - int as_set, - uint8_t atomic_aggregate); +extern struct attr *bgp_attr_aggregate_intern( + struct bgp *bgp, uint8_t origin, struct aspath *aspath, + struct community *community, struct ecommunity *ecommunity, + struct lcommunity *lcommunity, struct bgp_aggregate *aggregate, + uint8_t atomic_aggregate, const struct prefix *p); extern bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *, struct stream *, struct attr *, struct bpacket_attr_vec_arr *vecarr, struct prefix *, afi_t, safi_t, struct peer *, struct prefix_rd *, mpls_label_t *, uint32_t, int, uint32_t); -extern void bgp_dump_routes_attr(struct stream *, struct attr *, - struct prefix *); +extern void bgp_dump_routes_attr(struct stream *s, struct attr *attr, + const struct prefix *p); extern bool attrhash_cmp(const void *arg1, const void *arg2); -extern unsigned int attrhash_key_make(void *); +extern unsigned int attrhash_key_make(const void *); extern void attr_show_all(struct vty *); extern unsigned long int attr_count(void); extern unsigned long int attr_unknown_count(void); /* Cluster list prototypes. */ -extern int cluster_loop_check(struct cluster_list *, struct in_addr); -extern void cluster_unintern(struct cluster_list *); - -/* Transit attribute prototypes. */ -void transit_unintern(struct transit *); +extern bool cluster_loop_check(struct cluster_list *, struct in_addr); /* Below exported for unit-test purposes only */ struct bgp_attr_parser_args { @@ -309,14 +392,16 @@ extern int bgp_mp_reach_parse(struct bgp_attr_parser_args *args, extern int bgp_mp_unreach_parse(struct bgp_attr_parser_args *args, struct bgp_nlri *); extern bgp_attr_parse_ret_t -bgp_attr_prefix_sid(int32_t tlength, struct bgp_attr_parser_args *args, - struct bgp_nlri *mp_update); +bgp_attr_prefix_sid(struct bgp_attr_parser_args *args); extern struct bgp_attr_encap_subtlv * encap_tlv_dup(struct bgp_attr_encap_subtlv *orig); extern void bgp_attr_flush_encap(struct attr *attr); +extern void bgp_attr_extcom_tunnel_type(struct attr *attr, + bgp_encap_types *tunnel_type); + /** * Set of functions to encode MP_REACH_NLRI and MP_UNREACH_NLRI attributes. * Typical call sequence is to call _start(), followed by multiple _prefix(), @@ -328,22 +413,26 @@ extern size_t bgp_packet_mpattr_start(struct stream *s, struct peer *peer, struct bpacket_attr_vec_arr *vecarr, struct attr *attr); extern void bgp_packet_mpattr_prefix(struct stream *s, afi_t afi, safi_t safi, - struct prefix *p, struct prefix_rd *prd, + const struct prefix *p, + const struct prefix_rd *prd, mpls_label_t *label, uint32_t num_labels, int addpath_encode, uint32_t addpath_tx_id, struct attr *); extern size_t bgp_packet_mpattr_prefix_size(afi_t afi, safi_t safi, - struct prefix *p); + const struct prefix *p); extern void bgp_packet_mpattr_end(struct stream *s, size_t sizep); extern size_t bgp_packet_mpunreach_start(struct stream *s, afi_t afi, safi_t safi); -extern void bgp_packet_mpunreach_prefix(struct stream *s, struct prefix *p, - afi_t afi, safi_t safi, - struct prefix_rd *prd, mpls_label_t *, - uint32_t, int, uint32_t, struct attr *); +extern void bgp_packet_mpunreach_prefix( + struct stream *s, const struct prefix *p, afi_t afi, safi_t safi, + const struct prefix_rd *prd, mpls_label_t *label, uint32_t num_labels, + int addpath_encode, uint32_t addpath_tx_id, struct attr *attr); extern void bgp_packet_mpunreach_end(struct stream *s, size_t attrlen_pnt); +extern bgp_attr_parse_ret_t bgp_attr_nexthop_valid(struct peer *peer, + struct attr *attr); + static inline int bgp_rmap_nhop_changed(uint32_t out_rmap_flags, uint32_t in_rmap_flags) { @@ -364,5 +453,4 @@ static inline uint32_t mac_mobility_seqnum(struct attr *attr) { return (attr) ? attr->mm_seqnum : 0; } - #endif /* _QUAGGA_BGP_ATTR_H */ diff --git a/bgpd/bgp_attr_evpn.c b/bgpd/bgp_attr_evpn.c index 15fa322159..aa0c59f3a7 100644 --- a/bgpd/bgp_attr_evpn.c +++ b/bgpd/bgp_attr_evpn.c @@ -45,7 +45,7 @@ void bgp_add_routermac_ecom(struct attr *attr, struct ethaddr *routermac) memcpy(&routermac_ecom.val[2], routermac->octet, ETH_ALEN); if (!attr->ecommunity) attr->ecommunity = ecommunity_new(); - ecommunity_add_val(attr->ecommunity, &routermac_ecom); + ecommunity_add_val(attr->ecommunity, &routermac_ecom, false, false); ecommunity_str(attr->ecommunity); } @@ -54,45 +54,25 @@ void bgp_add_routermac_ecom(struct attr *attr, struct ethaddr *routermac) * format accepted: AA:BB:CC:DD:EE:FF:GG:HH:II:JJ * if id is null, check only is done */ -int str2esi(const char *str, struct eth_segment_id *id) +bool str2esi(const char *str, esi_t *id) { - unsigned int a[ESI_LEN]; + unsigned int a[ESI_BYTES]; int i; if (!str) - return 0; + return false; if (sscanf(str, "%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x", a + 0, a + 1, a + 2, a + 3, a + 4, a + 5, a + 6, a + 7, a + 8, a + 9) - != ESI_LEN) { + != ESI_BYTES) { /* error in incoming str length */ - return 0; + return false; } /* valid mac address */ if (!id) - return 1; - for (i = 0; i < ESI_LEN; ++i) + return true; + for (i = 0; i < ESI_BYTES; ++i) id->val[i] = a[i] & 0xff; - return 1; -} - -char *esi2str(struct eth_segment_id *id) -{ - char *ptr; - uint8_t *val; - - if (!id) - return NULL; - - val = id->val; - ptr = XMALLOC(MTYPE_TMP, - (ESI_LEN * 2 + ESI_LEN - 1 + 1) * sizeof(char)); - - snprintf(ptr, (ESI_LEN * 2 + ESI_LEN - 1 + 1), - "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", val[0], - val[1], val[2], val[3], val[4], val[5], val[6], val[7], val[8], - val[9]); - - return ptr; + return true; } char *ecom_mac2str(char *ecom_mac) @@ -186,7 +166,7 @@ uint32_t bgp_attr_mac_mobility_seqnum(struct attr *attr, uint8_t *sticky) * one. */ for (i = 0; i < ecom->size; i++) { - uint8_t *pnt; + const uint8_t *pnt; uint8_t type, sub_type; uint32_t seq_num; @@ -215,7 +195,8 @@ uint32_t bgp_attr_mac_mobility_seqnum(struct attr *attr, uint8_t *sticky) /* * return true if attr contains router flag extended community */ -void bgp_attr_evpn_na_flag(struct attr *attr, uint8_t *router_flag) +void bgp_attr_evpn_na_flag(struct attr *attr, + uint8_t *router_flag, bool *proxy) { struct ecommunity *ecom; int i; @@ -237,10 +218,14 @@ void bgp_attr_evpn_na_flag(struct attr *attr, uint8_t *router_flag) if (type == ECOMMUNITY_ENCODE_EVPN && sub_type == ECOMMUNITY_EVPN_SUBTYPE_ND) { val = *pnt++; - if (val & ECOMMUNITY_EVPN_SUBTYPE_ND_ROUTER_FLAG) { + + if (val & ECOMMUNITY_EVPN_SUBTYPE_ND_ROUTER_FLAG) *router_flag = 1; - break; - } + + if (val & ECOMMUNITY_EVPN_SUBTYPE_PROXY_FLAG) + *proxy = true; + + break; } } } @@ -281,3 +266,14 @@ extern int bgp_build_evpn_prefix(int evpn_type, uint32_t eth_tag, return -1; return 0; } + +extern bool is_zero_gw_ip(const union gw_addr *gw_ip, const afi_t afi) +{ + if (afi == AF_INET6 && IN6_IS_ADDR_UNSPECIFIED(&gw_ip->ipv6)) + return true; + + if (afi == AF_INET && gw_ip->ipv4.s_addr == INADDR_ANY) + return true; + + return false; +} diff --git a/bgpd/bgp_attr_evpn.h b/bgpd/bgp_attr_evpn.h index 5b0ce1da28..19c028a826 100644 --- a/bgpd/bgp_attr_evpn.h +++ b/bgpd/bgp_attr_evpn.h @@ -21,39 +21,20 @@ #ifndef _QUAGGA_BGP_ATTR_EVPN_H #define _QUAGGA_BGP_ATTR_EVPN_H -/* value of first byte of ESI */ -#define ESI_TYPE_ARBITRARY 0 /* */ -#define ESI_TYPE_LACP 1 /* <> */ -#define ESI_TYPE_BRIDGE 2 /* ::00 */ -#define ESI_TYPE_MAC 3 /* : */ -#define ESI_TYPE_ROUTER 4 /* : */ -#define ESI_TYPE_AS 5 /* : */ - -#define MAX_ESI {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff} -#define ESI_LEN 10 - #define MAX_ET 0xffffffff -unsigned long eth_tag_id; struct attr; -/* EVPN ESI */ -struct eth_segment_id { - uint8_t val[ESI_LEN]; -}; - union gw_addr { struct in_addr ipv4; struct in6_addr ipv6; }; struct bgp_route_evpn { - struct eth_segment_id eth_s_id; union gw_addr gw_ip; }; -extern int str2esi(const char *str, struct eth_segment_id *id); -extern char *esi2str(struct eth_segment_id *id); +extern bool str2esi(const char *str, esi_t *id); extern char *ecom_mac2str(char *ecom_mac); extern void bgp_add_routermac_ecom(struct attr *attr, @@ -65,6 +46,9 @@ extern uint32_t bgp_attr_mac_mobility_seqnum(struct attr *attr, uint8_t *sticky); extern uint8_t bgp_attr_default_gw(struct attr *attr); -extern void bgp_attr_evpn_na_flag(struct attr *attr, uint8_t *router_flag); +extern void bgp_attr_evpn_na_flag(struct attr *attr, uint8_t *router_flag, + bool *proxy); + +extern bool is_zero_gw_ip(const union gw_addr *gw_ip, afi_t afi); #endif /* _QUAGGA_BGP_ATTR_EVPN_H */ diff --git a/bgpd/bgp_bfd.c b/bgpd/bgp_bfd.c index dadf124eec..b566b0ea06 100644 --- a/bgpd/bgp_bfd.c +++ b/bgpd/bgp_bfd.c @@ -72,21 +72,21 @@ void bgp_bfd_peer_group2peer_copy(struct peer *conf, struct peer *peer) * bgp_bfd_is_peer_multihop - returns whether BFD peer is multi-hop or single * hop. */ -int bgp_bfd_is_peer_multihop(struct peer *peer) +bool bgp_bfd_is_peer_multihop(struct peer *peer) { struct bfd_info *bfd_info; bfd_info = (struct bfd_info *)peer->bfd_info; if (!bfd_info) - return 0; + return false; if ((bfd_info->type == BFD_TYPE_MULTIHOP) || ((peer->sort == BGP_PEER_IBGP) && !peer->shared_network) || is_ebgp_multihop_configured(peer)) - return 1; + return true; else - return 0; + return false; } /* @@ -95,14 +95,28 @@ int bgp_bfd_is_peer_multihop(struct peer *peer) */ static void bgp_bfd_peer_sendmsg(struct peer *peer, int command) { + struct bfd_session_arg arg = {}; struct bfd_info *bfd_info; - vrf_id_t vrf_id = VRF_DEFAULT; int multihop; + vrf_id_t vrf_id; + size_t addrlen; + + /* + * XXX: some pointers are dangling during shutdown, so instead of + * trying to send a message during signal handlers lets just wait BGP + * to terminate zebra's connection and BFD will automatically find + * out that we are no longer expecting notifications. + * + * The pointer that is causing a crash here is `peer->nexthop.ifp`. + * That happens because at this point of the shutdown all interfaces are + * already `free()`d. + */ + if (bm->terminating) + return; bfd_info = (struct bfd_info *)peer->bfd_info; - if (peer->bgp->inst_type == BGP_INSTANCE_TYPE_VRF) - vrf_id = peer->bgp->vrf_id; + vrf_id = peer->bgp->vrf_id; if (command == ZEBRA_BFD_DEST_DEREGISTER) { multihop = @@ -113,20 +127,58 @@ static void bgp_bfd_peer_sendmsg(struct peer *peer, int command) if ((command == ZEBRA_BFD_DEST_REGISTER) && multihop) SET_FLAG(bfd_info->flags, BFD_FLAG_BFD_TYPE_MULTIHOP); } + /* while graceful restart with fwd path preserved + * and bfd controlplane check not configured is not kept + * keep bfd independent controlplane bit set to 1 + */ + if (!CHECK_FLAG(peer->bgp->flags, BGP_FLAG_GRACEFUL_RESTART) + && !CHECK_FLAG(peer->bgp->flags, BGP_FLAG_GR_PRESERVE_FWD) + && !CHECK_FLAG(bfd_info->flags, BFD_FLAG_BFD_CHECK_CONTROLPLANE)) + SET_FLAG(bfd_info->flags, BFD_FLAG_BFD_CBIT_ON); + + /* Set all message arguments. */ + arg.family = peer->su.sa.sa_family; + addrlen = arg.family == AF_INET ? sizeof(struct in_addr) + : sizeof(struct in6_addr); + + if (arg.family == AF_INET) + memcpy(&arg.dst, &peer->su.sin.sin_addr, addrlen); + else + memcpy(&arg.dst, &peer->su.sin6.sin6_addr, addrlen); + + if (peer->su_local) { + if (arg.family == AF_INET) + memcpy(&arg.src, &peer->su_local->sin.sin_addr, + addrlen); + else + memcpy(&arg.src, &peer->su_local->sin6.sin6_addr, + addrlen); + } + + if (peer->nexthop.ifp) { + arg.ifnamelen = strlen(peer->nexthop.ifp->name); + strlcpy(arg.ifname, peer->nexthop.ifp->name, + sizeof(arg.ifname)); + } - if (peer->su.sa.sa_family == AF_INET) - bfd_peer_sendmsg( - zclient, bfd_info, AF_INET, &peer->su.sin.sin_addr, - (peer->su_local) ? &peer->su_local->sin.sin_addr : NULL, - (peer->nexthop.ifp) ? peer->nexthop.ifp->name : NULL, - peer->ttl, multihop, command, 1, vrf_id); - else if (peer->su.sa.sa_family == AF_INET6) - bfd_peer_sendmsg( - zclient, bfd_info, AF_INET6, &peer->su.sin6.sin6_addr, - (peer->su_local) ? &peer->su_local->sin6.sin6_addr - : NULL, - (peer->nexthop.ifp) ? peer->nexthop.ifp->name : NULL, - peer->ttl, multihop, command, 1, vrf_id); + if (bfd_info->profile[0]) { + arg.profilelen = strlen(bfd_info->profile); + strlcpy(arg.profile, bfd_info->profile, sizeof(arg.profile)); + } + + arg.set_flag = 1; + arg.mhop = multihop; + arg.ttl = peer->ttl; + arg.vrf_id = vrf_id; + arg.command = command; + arg.bfd_info = bfd_info; + arg.min_tx = bfd_info->desired_min_tx; + arg.min_rx = bfd_info->required_min_rx; + arg.detection_multiplier = bfd_info->detect_mult; + arg.cbit = CHECK_FLAG(bfd_info->flags, BFD_FLAG_BFD_CBIT_ON); + + /* Send message. */ + zclient_bfd_command(zclient, &arg); } /* @@ -191,6 +243,19 @@ static void bgp_bfd_update_peer(struct peer *peer) bgp_bfd_peer_sendmsg(peer, ZEBRA_BFD_DEST_UPDATE); } +/** + * bgp_bfd_reset_peer - reinitialise bfd + * ensures that bfd state machine is restarted + * to be synced with remote bfd + */ +void bgp_bfd_reset_peer(struct peer *peer) +{ + if (!peer->bfd_info) + return; + + bgp_bfd_peer_sendmsg(peer, ZEBRA_BFD_DEST_REGISTER); +} + /* * bgp_bfd_update_type - update session type with BFD through zebra. */ @@ -234,8 +299,7 @@ static void bgp_bfd_update_type(struct peer *peer) * bgp_bfd_dest_replay - Replay all the peers that have BFD enabled * to zebra */ -static int bgp_bfd_dest_replay(int command, struct zclient *client, - zebra_size_t length, vrf_id_t vrf_id) +static int bgp_bfd_dest_replay(ZAPI_CALLBACK_ARGS) { struct listnode *mnode, *node, *nnode; struct bgp *bgp; @@ -245,7 +309,7 @@ static int bgp_bfd_dest_replay(int command, struct zclient *client, zlog_debug("Zebra: BFD Dest replay request"); /* Send the client registration */ - bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER); + bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, vrf_id); /* Replay the peer, if BFD is enabled in BGP */ @@ -262,7 +326,8 @@ static int bgp_bfd_dest_replay(int command, struct zclient *client, * down the peer if the BFD session went down from * * up. */ -static void bgp_bfd_peer_status_update(struct peer *peer, int status) +static void bgp_bfd_peer_status_update(struct peer *peer, int status, + int remote_cbit) { struct bfd_info *bfd_info; int old_status; @@ -273,10 +338,23 @@ static void bgp_bfd_peer_status_update(struct peer *peer, int status) return; old_status = bfd_info->status; - bfd_info->status = status; + BFD_SET_CLIENT_STATUS(bfd_info->status, status); + bfd_info->last_update = bgp_clock(); + if (status != old_status) { + if (BGP_DEBUG(neighbor_events, NEIGHBOR_EVENTS)) + zlog_debug("[%s]: BFD %s", peer->host, + bfd_get_status_str(status)); + } if ((status == BFD_STATUS_DOWN) && (old_status == BFD_STATUS_UP)) { + if (CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_MODE) && + CHECK_FLAG(bfd_info->flags, BFD_FLAG_BFD_CHECK_CONTROLPLANE) && + !remote_cbit) { + zlog_info("%s BFD DOWN message ignored in the process of graceful restart when C bit is cleared", + peer->host); + return; + } peer->last_reset = PEER_DOWN_BFD_DOWN; BGP_EVENT_ADD(peer, BGP_Stop); } @@ -294,30 +372,36 @@ static void bgp_bfd_peer_status_update(struct peer *peer, int status) * has changed and bring down the peer * connectivity if the BFD session went down. */ -static int bgp_bfd_dest_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int bgp_bfd_dest_update(ZAPI_CALLBACK_ARGS) { struct interface *ifp; struct prefix dp; struct prefix sp; int status; + int remote_cbit; - ifp = bfd_get_peer_info(zclient->ibuf, &dp, &sp, &status, vrf_id); + ifp = bfd_get_peer_info(zclient->ibuf, &dp, &sp, &status, + &remote_cbit, vrf_id); if (BGP_DEBUG(zebra, ZEBRA)) { + struct vrf *vrf; char buf[2][PREFIX2STR_BUFFER]; + + vrf = vrf_lookup_by_id(vrf_id); prefix2str(&dp, buf[0], sizeof(buf[0])); if (ifp) { zlog_debug( - "Zebra: vrf %u interface %s bfd destination %s %s", - vrf_id, ifp->name, buf[0], - bfd_get_status_str(status)); + "Zebra: vrf %s(%u) interface %s bfd destination %s %s %s", + VRF_LOGNAME(vrf), vrf_id, ifp->name, + buf[0], bfd_get_status_str(status), + remote_cbit ? "(cbit on)" : ""); } else { prefix2str(&sp, buf[1], sizeof(buf[1])); zlog_debug( - "Zebra: vrf %u source %s bfd destination %s %s", - vrf_id, buf[1], buf[0], - bfd_get_status_str(status)); + "Zebra: vrf %s(%u) source %s bfd destination %s %s %s", + VRF_LOGNAME(vrf), vrf_id, buf[1], buf[0], + bfd_get_status_str(status), + remote_cbit ? "(cbit on)" : ""); } } @@ -349,7 +433,8 @@ static int bgp_bfd_dest_update(int command, struct zclient *zclient, if (ifp && (ifp == peer->nexthop.ifp)) { bgp_bfd_peer_status_update(peer, - status); + status, + remote_cbit); } else { if (!peer->su_local) continue; @@ -379,7 +464,8 @@ static int bgp_bfd_dest_update(int command, struct zclient *zclient, continue; bgp_bfd_peer_status_update(peer, - status); + status, + remote_cbit); } } } @@ -394,20 +480,32 @@ static int bgp_bfd_peer_param_set(struct peer *peer, uint32_t min_rx, uint32_t min_tx, uint8_t detect_mult, int defaults) { + struct bfd_info *bi; struct peer_group *group; struct listnode *node, *nnode; int command = 0; bfd_set_param((struct bfd_info **)&(peer->bfd_info), min_rx, min_tx, - detect_mult, defaults, &command); + detect_mult, NULL, defaults, &command); + + /* This command overrides profile if it was previously applied. */ + bi = peer->bfd_info; + bi->profile[0] = 0; if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { group = peer->group; for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) { command = 0; bfd_set_param((struct bfd_info **)&(peer->bfd_info), - min_rx, min_tx, detect_mult, defaults, - &command); + min_rx, min_tx, detect_mult, NULL, + defaults, &command); + + /* + * This command overrides profile if it was previously + * applied. + */ + bi = peer->bfd_info; + bi->profile[0] = 0; if ((peer->status == Established) && (command == ZEBRA_BFD_DEST_REGISTER)) @@ -466,7 +564,7 @@ static int bgp_bfd_peer_param_type_set(struct peer *peer, if (!peer->bfd_info) bfd_set_param((struct bfd_info **)&(peer->bfd_info), BFD_DEF_MIN_RX, BFD_DEF_MIN_TX, - BFD_DEF_DETECT_MULT, 1, &command); + BFD_DEF_DETECT_MULT, NULL, 1, &command); bfd_info = (struct bfd_info *)peer->bfd_info; bfd_info->type = type; @@ -479,7 +577,7 @@ static int bgp_bfd_peer_param_type_set(struct peer *peer, bfd_set_param( (struct bfd_info **)&(peer->bfd_info), BFD_DEF_MIN_RX, BFD_DEF_MIN_TX, - BFD_DEF_DETECT_MULT, 1, &command); + BFD_DEF_DETECT_MULT, NULL, 1, &command); bfd_info = (struct bfd_info *)peer->bfd_info; bfd_info->type = type; @@ -503,6 +601,63 @@ static int bgp_bfd_peer_param_type_set(struct peer *peer, return 0; } +#if HAVE_BFDD > 0 +/** + * Set peer BFD profile configuration. + */ +static int bgp_bfd_peer_set_profile(struct peer *peer, const char *profile) +{ + struct peer_group *group; + struct listnode *node, *nnode; + int command = 0; + struct bfd_info *bfd_info; + + bfd_set_param((struct bfd_info **)&(peer->bfd_info), BFD_DEF_MIN_RX, + BFD_DEF_MIN_TX, BFD_DEF_DETECT_MULT, NULL, 1, &command); + + bfd_info = (struct bfd_info *)peer->bfd_info; + + /* If profile was specified, then copy string. */ + if (profile) + strlcpy(bfd_info->profile, profile, sizeof(bfd_info->profile)); + else /* Otherwise just initialize it empty. */ + bfd_info->profile[0] = 0; + + if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { + group = peer->group; + for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) { + command = 0; + bfd_set_param((struct bfd_info **)&(peer->bfd_info), + BFD_DEF_MIN_RX, BFD_DEF_MIN_TX, + BFD_DEF_DETECT_MULT, NULL, 1, &command); + + bfd_info = (struct bfd_info *)peer->bfd_info; + + /* If profile was specified, then copy string. */ + if (profile) + strlcpy(bfd_info->profile, profile, + sizeof(bfd_info->profile)); + else /* Otherwise just initialize it empty. */ + bfd_info->profile[0] = 0; + + if (peer->status == Established + && command == ZEBRA_BFD_DEST_REGISTER) + bgp_bfd_register_peer(peer); + else if (command == ZEBRA_BFD_DEST_UPDATE) + bgp_bfd_update_peer(peer); + } + } else { + if (peer->status == Established + && command == ZEBRA_BFD_DEST_REGISTER) + bgp_bfd_register_peer(peer); + else if (command == ZEBRA_BFD_DEST_UPDATE) + bgp_bfd_update_peer(peer); + } + + return 0; +} +#endif + /* * bgp_bfd_peer_config_write - Write the peer BFD configuration. */ @@ -530,8 +685,15 @@ void bgp_bfd_peer_config_write(struct vty *vty, struct peer *peer, char *addr) : "singlehop"); if (!CHECK_FLAG(bfd_info->flags, BFD_FLAG_PARAM_CFG) - && (bfd_info->type == BFD_TYPE_NOT_CONFIGURED)) - vty_out(vty, " neighbor %s bfd\n", addr); + && (bfd_info->type == BFD_TYPE_NOT_CONFIGURED)) { + vty_out(vty, " neighbor %s bfd", addr); + if (bfd_info->profile[0]) + vty_out(vty, " profile %s", bfd_info->profile); + vty_out(vty, "\n"); + } + + if (CHECK_FLAG(bfd_info->flags, BFD_FLAG_BFD_CHECK_CONTROLPLANE)) + vty_out(vty, " neighbor %s bfd check-control-plane-failure\n", addr); } /* @@ -642,6 +804,73 @@ DEFUN_HIDDEN (neighbor_bfd_type, return CMD_SUCCESS; } +static int bgp_bfd_set_check_controlplane_failure_peer(struct vty *vty, struct peer *peer, + const char *no) +{ + struct bfd_info *bfd_info; + + if (!peer->bfd_info) { + if (no) + return CMD_SUCCESS; + vty_out(vty, "%% Specify bfd command first\n"); + return CMD_WARNING_CONFIG_FAILED; + } + bfd_info = (struct bfd_info *)peer->bfd_info; + if (!no) { + if (!CHECK_FLAG(bfd_info->flags, BFD_FLAG_BFD_CHECK_CONTROLPLANE)) { + SET_FLAG(bfd_info->flags, BFD_FLAG_BFD_CHECK_CONTROLPLANE); + bgp_bfd_update_peer(peer); + } + } else { + if (CHECK_FLAG(bfd_info->flags, BFD_FLAG_BFD_CHECK_CONTROLPLANE)) { + UNSET_FLAG(bfd_info->flags, BFD_FLAG_BFD_CHECK_CONTROLPLANE); + bgp_bfd_update_peer(peer); + } + } + return CMD_SUCCESS; +} + + +DEFUN (neighbor_bfd_check_controlplane_failure, + neighbor_bfd_check_controlplane_failure_cmd, + "[no] neighbor bfd check-control-plane-failure", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "BFD support\n" + "Link dataplane status with BGP controlplane\n") +{ + const char *no = strmatch(argv[0]->text, "no") ? "no" : NULL; + int idx_peer = 0; + struct peer *peer; + struct peer_group *group; + struct listnode *node, *nnode; + int ret = CMD_SUCCESS; + + if (no) + idx_peer = 2; + else + idx_peer = 1; + peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg); + if (!peer) { + vty_out(vty, "%% Specify remote-as or peer-group commands first\n"); + return CMD_WARNING_CONFIG_FAILED; + } + if (!peer->bfd_info) { + if (no) + return CMD_SUCCESS; + vty_out(vty, "%% Specify bfd command first\n"); + return CMD_WARNING_CONFIG_FAILED; + } + if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { + group = peer->group; + for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) + ret = bgp_bfd_set_check_controlplane_failure_peer(vty, peer, no); + } else + ret = bgp_bfd_set_check_controlplane_failure_peer(vty, peer, no); + return ret; + } + DEFUN (no_neighbor_bfd, no_neighbor_bfd_cmd, #if HAVE_BFDD > 0 @@ -704,6 +933,58 @@ DEFUN_HIDDEN (no_neighbor_bfd_type, return CMD_SUCCESS; } +#if HAVE_BFDD > 0 +DEFUN(neighbor_bfd_profile, neighbor_bfd_profile_cmd, + "neighbor bfd profile BFDPROF", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "BFD integration\n" + BFD_PROFILE_STR + BFD_PROFILE_NAME_STR) +{ + int idx_peer = 1, idx_prof = 4; + struct peer *peer; + int ret; + + peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg); + if (!peer) + return CMD_WARNING_CONFIG_FAILED; + + ret = bgp_bfd_peer_set_profile(peer, argv[idx_prof]->arg); + if (ret != 0) + return bgp_vty_return(vty, ret); + + return CMD_SUCCESS; +} + +DEFUN(no_neighbor_bfd_profile, no_neighbor_bfd_profile_cmd, + "no neighbor bfd profile [BFDPROF]", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "BFD integration\n" + BFD_PROFILE_STR + BFD_PROFILE_NAME_STR) +{ + int idx_peer = 2; + struct peer *peer; + int ret; + + peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg); + if (!peer) + return CMD_WARNING_CONFIG_FAILED; + + if (!peer->bfd_info) + return 0; + + ret = bgp_bfd_peer_set_profile(peer, NULL); + if (ret != 0) + return bgp_vty_return(vty, ret); + + return CMD_SUCCESS; +} +#endif /* HAVE_BFDD */ + void bgp_bfd_init(void) { bfd_gbl_init(); @@ -716,6 +997,12 @@ void bgp_bfd_init(void) install_element(BGP_NODE, &neighbor_bfd_cmd); install_element(BGP_NODE, &neighbor_bfd_param_cmd); install_element(BGP_NODE, &neighbor_bfd_type_cmd); + install_element(BGP_NODE, &neighbor_bfd_check_controlplane_failure_cmd); install_element(BGP_NODE, &no_neighbor_bfd_cmd); install_element(BGP_NODE, &no_neighbor_bfd_type_cmd); + +#if HAVE_BFDD > 0 + install_element(BGP_NODE, &neighbor_bfd_profile_cmd); + install_element(BGP_NODE, &no_neighbor_bfd_profile_cmd); +#endif /* HAVE_BFDD */ } diff --git a/bgpd/bgp_bfd.h b/bgpd/bgp_bfd.h index caa5651e3a..f2fa959b45 100644 --- a/bgpd/bgp_bfd.h +++ b/bgpd/bgp_bfd.h @@ -31,12 +31,14 @@ extern void bgp_bfd_register_peer(struct peer *peer); extern void bgp_bfd_deregister_peer(struct peer *peer); +extern void bgp_bfd_reset_peer(struct peer *peer); + extern void bgp_bfd_peer_config_write(struct vty *vty, struct peer *peer, char *addr); extern void bgp_bfd_show_info(struct vty *vty, struct peer *peer, bool use_json, json_object *json_neigh); -extern int bgp_bfd_is_peer_multihop(struct peer *peer); +extern bool bgp_bfd_is_peer_multihop(struct peer *peer); #endif /* _QUAGGA_BGP_BFD_H */ diff --git a/bgpd/bgp_bmp.c b/bgpd/bgp_bmp.c new file mode 100644 index 0000000000..2ce5e1c6d6 --- /dev/null +++ b/bgpd/bgp_bmp.c @@ -0,0 +1,2395 @@ +/* BMP support. + * Copyright (C) 2018 Yasuhiro Ohara + * Copyright (C) 2019 David Lamparter for NetDEF, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "log.h" +#include "stream.h" +#include "sockunion.h" +#include "command.h" +#include "prefix.h" +#include "thread.h" +#include "linklist.h" +#include "queue.h" +#include "pullwr.h" +#include "memory.h" +#include "network.h" +#include "filter.h" +#include "lib_errors.h" +#include "stream.h" +#include "libfrr.h" +#include "version.h" +#include "jhash.h" +#include "termtable.h" + +#include "bgpd/bgp_table.h" +#include "bgpd/bgpd.h" +#include "bgpd/bgp_route.h" +#include "bgpd/bgp_attr.h" +#include "bgpd/bgp_dump.h" +#include "bgpd/bgp_errors.h" +#include "bgpd/bgp_packet.h" +#include "bgpd/bgp_bmp.h" +#include "bgpd/bgp_fsm.h" +#include "bgpd/bgp_updgrp.h" +#include "bgpd/bgp_vty.h" + +static void bmp_close(struct bmp *bmp); +static struct bmp_bgp *bmp_bgp_find(struct bgp *bgp); +static void bmp_targets_put(struct bmp_targets *bt); +static struct bmp_bgp_peer *bmp_bgp_peer_find(uint64_t peerid); +static struct bmp_bgp_peer *bmp_bgp_peer_get(struct peer *peer); +static void bmp_active_disconnected(struct bmp_active *ba); +static void bmp_active_put(struct bmp_active *ba); + +DEFINE_MGROUP(BMP, "BMP (BGP Monitoring Protocol)") + +DEFINE_MTYPE_STATIC(BMP, BMP_CONN, "BMP connection state") +DEFINE_MTYPE_STATIC(BMP, BMP_TARGETS, "BMP targets") +DEFINE_MTYPE_STATIC(BMP, BMP_TARGETSNAME, "BMP targets name") +DEFINE_MTYPE_STATIC(BMP, BMP_LISTENER, "BMP listener") +DEFINE_MTYPE_STATIC(BMP, BMP_ACTIVE, "BMP active connection config") +DEFINE_MTYPE_STATIC(BMP, BMP_ACLNAME, "BMP access-list name") +DEFINE_MTYPE_STATIC(BMP, BMP_QUEUE, "BMP update queue item") +DEFINE_MTYPE_STATIC(BMP, BMP, "BMP instance state") +DEFINE_MTYPE_STATIC(BMP, BMP_MIRRORQ, "BMP route mirroring buffer") +DEFINE_MTYPE_STATIC(BMP, BMP_PEER, "BMP per BGP peer data") +DEFINE_MTYPE_STATIC(BMP, BMP_OPEN, "BMP stored BGP OPEN message") + +DEFINE_QOBJ_TYPE(bmp_targets) + +static int bmp_bgp_cmp(const struct bmp_bgp *a, const struct bmp_bgp *b) +{ + if (a->bgp < b->bgp) + return -1; + if (a->bgp > b->bgp) + return 1; + return 0; +} + +static uint32_t bmp_bgp_hash(const struct bmp_bgp *e) +{ + return jhash(&e->bgp, sizeof(e->bgp), 0x55aa5a5a); +} + +DECLARE_HASH(bmp_bgph, struct bmp_bgp, bbi, bmp_bgp_cmp, bmp_bgp_hash) + +struct bmp_bgph_head bmp_bgph; + +static int bmp_bgp_peer_cmp(const struct bmp_bgp_peer *a, + const struct bmp_bgp_peer *b) +{ + if (a->peerid < b->peerid) + return -1; + if (a->peerid > b->peerid) + return 1; + return 0; +} + +static uint32_t bmp_bgp_peer_hash(const struct bmp_bgp_peer *e) +{ + return e->peerid; +} + +DECLARE_HASH(bmp_peerh, struct bmp_bgp_peer, bpi, + bmp_bgp_peer_cmp, bmp_bgp_peer_hash) + +struct bmp_peerh_head bmp_peerh; + +DECLARE_LIST(bmp_mirrorq, struct bmp_mirrorq, bmi) + +/* listener management */ + +static int bmp_listener_cmp(const struct bmp_listener *a, + const struct bmp_listener *b) +{ + int c; + + c = sockunion_cmp(&a->addr, &b->addr); + if (c) + return c; + if (a->port < b->port) + return -1; + if (a->port > b->port) + return 1; + return 0; +} + +DECLARE_SORTLIST_UNIQ(bmp_listeners, struct bmp_listener, bli, bmp_listener_cmp) + +static int bmp_targets_cmp(const struct bmp_targets *a, + const struct bmp_targets *b) +{ + return strcmp(a->name, b->name); +} + +DECLARE_SORTLIST_UNIQ(bmp_targets, struct bmp_targets, bti, bmp_targets_cmp) + +DECLARE_LIST(bmp_session, struct bmp, bsi) + +DECLARE_DLIST(bmp_qlist, struct bmp_queue_entry, bli) + +static int bmp_qhash_cmp(const struct bmp_queue_entry *a, + const struct bmp_queue_entry *b) +{ + int ret; + if (a->afi == AFI_L2VPN && a->safi == SAFI_EVPN && b->afi == AFI_L2VPN + && b->safi == SAFI_EVPN) { + ret = prefix_cmp(&a->rd, &b->rd); + if (ret) + return ret; + } else if (a->afi == AFI_L2VPN && a->safi == SAFI_EVPN) + return 1; + else if (b->afi == AFI_L2VPN && b->safi == SAFI_EVPN) + return -1; + + ret = prefix_cmp(&a->p, &b->p); + if (ret) + return ret; + ret = memcmp(&a->peerid, &b->peerid, + offsetof(struct bmp_queue_entry, refcount) - + offsetof(struct bmp_queue_entry, peerid)); + return ret; +} + +static uint32_t bmp_qhash_hkey(const struct bmp_queue_entry *e) +{ + uint32_t key; + + key = prefix_hash_key((void *)&e->p); + key = jhash(&e->peerid, + offsetof(struct bmp_queue_entry, refcount) + - offsetof(struct bmp_queue_entry, peerid), + key); + if (e->afi == AFI_L2VPN && e->safi == SAFI_EVPN) + key = jhash(&e->rd, + offsetof(struct bmp_queue_entry, rd) + - offsetof(struct bmp_queue_entry, refcount) + + PSIZE(e->rd.prefixlen), + key); + + return key; +} + +DECLARE_HASH(bmp_qhash, struct bmp_queue_entry, bhi, + bmp_qhash_cmp, bmp_qhash_hkey) + +static int bmp_active_cmp(const struct bmp_active *a, + const struct bmp_active *b) +{ + int c; + + c = strcmp(a->hostname, b->hostname); + if (c) + return c; + if (a->port < b->port) + return -1; + if (a->port > b->port) + return 1; + return 0; +} + +DECLARE_SORTLIST_UNIQ(bmp_actives, struct bmp_active, bai, bmp_active_cmp) + +static struct bmp *bmp_new(struct bmp_targets *bt, int bmp_sock) +{ + struct bmp *new = XCALLOC(MTYPE_BMP_CONN, sizeof(struct bmp)); + afi_t afi; + safi_t safi; + + monotime(&new->t_up); + new->targets = bt; + new->socket = bmp_sock; + new->syncafi = AFI_MAX; + + FOREACH_AFI_SAFI (afi, safi) { + new->afistate[afi][safi] = bt->afimon[afi][safi] + ? BMP_AFI_NEEDSYNC : BMP_AFI_INACTIVE; + } + + bmp_session_add_tail(&bt->sessions, new); + return new; +} + +static void bmp_free(struct bmp *bmp) +{ + bmp_session_del(&bmp->targets->sessions, bmp); + XFREE(MTYPE_BMP_CONN, bmp); +} + +static void bmp_common_hdr(struct stream *s, uint8_t ver, uint8_t type) +{ + stream_putc(s, ver); + stream_putl(s, 0); //dummy message length. will be set later. + stream_putc(s, type); +} + +static void bmp_per_peer_hdr(struct stream *s, struct peer *peer, + uint8_t flags, const struct timeval *tv) +{ + char peer_distinguisher[8]; + +#define BMP_PEER_TYPE_GLOBAL_INSTANCE 0 +#define BMP_PEER_TYPE_RD_INSTANCE 1 +#define BMP_PEER_TYPE_LOCAL_INSTANCE 2 + +#define BMP_PEER_FLAG_V (1 << 7) +#define BMP_PEER_FLAG_L (1 << 6) +#define BMP_PEER_FLAG_A (1 << 5) + + /* Peer Type */ + stream_putc(s, BMP_PEER_TYPE_GLOBAL_INSTANCE); + + /* Peer Flags */ + if (peer->su.sa.sa_family == AF_INET6) + SET_FLAG(flags, BMP_PEER_FLAG_V); + else + UNSET_FLAG(flags, BMP_PEER_FLAG_V); + stream_putc(s, flags); + + /* Peer Distinguisher */ + memset (&peer_distinguisher[0], 0, 8); + stream_put(s, &peer_distinguisher[0], 8); + + /* Peer Address */ + if (peer->su.sa.sa_family == AF_INET6) + stream_put(s, &peer->su.sin6.sin6_addr, 16); + else if (peer->su.sa.sa_family == AF_INET) { + stream_putl(s, 0); + stream_putl(s, 0); + stream_putl(s, 0); + stream_put_in_addr(s, &peer->su.sin.sin_addr); + } else { + stream_putl(s, 0); + stream_putl(s, 0); + stream_putl(s, 0); + stream_putl(s, 0); + } + + /* Peer AS */ + stream_putl(s, peer->as); + + /* Peer BGP ID */ + stream_put_in_addr(s, &peer->remote_id); + + /* Timestamp */ + if (tv) { + stream_putl(s, tv->tv_sec); + stream_putl(s, tv->tv_usec); + } else { + stream_putl(s, 0); + stream_putl(s, 0); + } +} + +static void bmp_put_info_tlv(struct stream *s, uint16_t type, + const char *string) +{ + int len = strlen (string); + stream_putw(s, type); + stream_putw(s, len); + stream_put(s, string, len); +} + +static int bmp_send_initiation(struct bmp *bmp) +{ + int len; + struct stream *s; + s = stream_new(BGP_MAX_PACKET_SIZE); + bmp_common_hdr(s, BMP_VERSION_3, BMP_TYPE_INITIATION); + +#define BMP_INFO_TYPE_SYSDESCR 1 +#define BMP_INFO_TYPE_SYSNAME 2 + bmp_put_info_tlv(s, BMP_INFO_TYPE_SYSDESCR, + FRR_FULL_NAME " " FRR_VER_SHORT); + bmp_put_info_tlv(s, BMP_INFO_TYPE_SYSNAME, cmd_hostname_get()); + + len = stream_get_endp(s); + stream_putl_at(s, BMP_LENGTH_POS, len); //message length is set. + + pullwr_write_stream(bmp->pullwr, s); + stream_free(s); + return 0; +} + +static void bmp_notify_put(struct stream *s, struct bgp_notify *nfy) +{ + size_t len_pos; + uint8_t marker[16] = { + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + }; + + stream_put(s, marker, sizeof(marker)); + len_pos = stream_get_endp(s); + stream_putw(s, 0); + stream_putc(s, BGP_MSG_NOTIFY); + stream_putc(s, nfy->code); + stream_putc(s, nfy->subcode); + stream_put(s, nfy->data, nfy->length); + + stream_putw_at(s, len_pos, stream_get_endp(s) - len_pos + + sizeof(marker)); +} + +static struct stream *bmp_peerstate(struct peer *peer, bool down) +{ + struct stream *s; + size_t len; + struct timeval uptime, uptime_real; + + uptime.tv_sec = peer->uptime; + uptime.tv_usec = 0; + monotime_to_realtime(&uptime, &uptime_real); + +#define BGP_BMP_MAX_PACKET_SIZE 1024 + s = stream_new(BGP_MAX_PACKET_SIZE); + + if (peer->status == Established && !down) { + struct bmp_bgp_peer *bbpeer; + + bmp_common_hdr(s, BMP_VERSION_3, + BMP_TYPE_PEER_UP_NOTIFICATION); + bmp_per_peer_hdr(s, peer, 0, &uptime_real); + + /* Local Address (16 bytes) */ + if (peer->su_local->sa.sa_family == AF_INET6) + stream_put(s, &peer->su_local->sin6.sin6_addr, 16); + else if (peer->su_local->sa.sa_family == AF_INET) { + stream_putl(s, 0); + stream_putl(s, 0); + stream_putl(s, 0); + stream_put_in_addr(s, &peer->su_local->sin.sin_addr); + } + + /* Local Port, Remote Port */ + if (peer->su_local->sa.sa_family == AF_INET6) + stream_putw(s, peer->su_local->sin6.sin6_port); + else if (peer->su_local->sa.sa_family == AF_INET) + stream_putw(s, peer->su_local->sin.sin_port); + if (peer->su_remote->sa.sa_family == AF_INET6) + stream_putw(s, peer->su_remote->sin6.sin6_port); + else if (peer->su_remote->sa.sa_family == AF_INET) + stream_putw(s, peer->su_remote->sin.sin_port); + + static const uint8_t dummy_open[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x13, 0x01, + }; + + bbpeer = bmp_bgp_peer_find(peer->qobj_node.nid); + + if (bbpeer && bbpeer->open_tx) + stream_put(s, bbpeer->open_tx, bbpeer->open_tx_len); + else { + stream_put(s, dummy_open, sizeof(dummy_open)); + zlog_warn("bmp: missing TX OPEN message for peer %s", + peer->host); + } + if (bbpeer && bbpeer->open_rx) + stream_put(s, bbpeer->open_rx, bbpeer->open_rx_len); + else { + stream_put(s, dummy_open, sizeof(dummy_open)); + zlog_warn("bmp: missing RX OPEN message for peer %s", + peer->host); + } + + if (peer->desc) + bmp_put_info_tlv(s, 0, peer->desc); + } else { + uint8_t type; + size_t type_pos; + + bmp_common_hdr(s, BMP_VERSION_3, + BMP_TYPE_PEER_DOWN_NOTIFICATION); + bmp_per_peer_hdr(s, peer, 0, &uptime_real); + + type_pos = stream_get_endp(s); + stream_putc(s, 0); /* placeholder for down reason */ + + switch (peer->last_reset) { + case PEER_DOWN_NOTIFY_RECEIVED: + type = BMP_PEERDOWN_REMOTE_NOTIFY; + bmp_notify_put(s, &peer->notify); + break; + case PEER_DOWN_CLOSE_SESSION: + type = BMP_PEERDOWN_REMOTE_CLOSE; + break; + case PEER_DOWN_WAITING_NHT: + type = BMP_PEERDOWN_LOCAL_FSM; + stream_putw(s, BGP_FSM_TcpConnectionFails); + break; + /* + * TODO: Map remaining PEER_DOWN_* reasons to RFC event codes. + * TODO: Implement BMP_PEERDOWN_LOCAL_NOTIFY. + * + * See RFC7854 ss. 4.9 + */ + default: + type = BMP_PEERDOWN_LOCAL_FSM; + stream_putw(s, BMP_PEER_DOWN_NO_RELEVANT_EVENT_CODE); + break; + } + stream_putc_at(s, type_pos, type); + } + + len = stream_get_endp(s); + stream_putl_at(s, BMP_LENGTH_POS, len); //message length is set. + return s; +} + + +static int bmp_send_peerup(struct bmp *bmp) +{ + struct peer *peer; + struct listnode *node; + struct stream *s; + + /* Walk down all peers */ + for (ALL_LIST_ELEMENTS_RO(bmp->targets->bgp->peer, node, peer)) { + s = bmp_peerstate(peer, false); + pullwr_write_stream(bmp->pullwr, s); + stream_free(s); + } + + return 0; +} + +/* XXX: kludge - filling the pullwr's buffer */ +static void bmp_send_all(struct bmp_bgp *bmpbgp, struct stream *s) +{ + struct bmp_targets *bt; + struct bmp *bmp; + + frr_each(bmp_targets, &bmpbgp->targets, bt) + frr_each(bmp_session, &bt->sessions, bmp) + pullwr_write_stream(bmp->pullwr, s); + stream_free(s); +} + +/* + * Route Mirroring + */ + +#define BMP_MIRROR_TLV_TYPE_BGP_MESSAGE 0 +#define BMP_MIRROR_TLV_TYPE_INFO 1 + +#define BMP_MIRROR_INFO_CODE_ERRORPDU 0 +#define BMP_MIRROR_INFO_CODE_LOSTMSGS 1 + +static struct bmp_mirrorq *bmp_pull_mirror(struct bmp *bmp) +{ + struct bmp_mirrorq *bmq; + + bmq = bmp->mirrorpos; + if (!bmq) + return NULL; + + bmp->mirrorpos = bmp_mirrorq_next(&bmp->targets->bmpbgp->mirrorq, bmq); + + bmq->refcount--; + if (!bmq->refcount) { + bmp->targets->bmpbgp->mirror_qsize -= sizeof(*bmq) + bmq->len; + bmp_mirrorq_del(&bmp->targets->bmpbgp->mirrorq, bmq); + } + return bmq; +} + +static void bmp_mirror_cull(struct bmp_bgp *bmpbgp) +{ + while (bmpbgp->mirror_qsize > bmpbgp->mirror_qsizelimit) { + struct bmp_mirrorq *bmq, *inner; + struct bmp_targets *bt; + struct bmp *bmp; + + bmq = bmp_mirrorq_first(&bmpbgp->mirrorq); + + frr_each(bmp_targets, &bmpbgp->targets, bt) { + if (!bt->mirror) + continue; + frr_each(bmp_session, &bt->sessions, bmp) { + if (bmp->mirrorpos != bmq) + continue; + + while ((inner = bmp_pull_mirror(bmp))) { + if (!inner->refcount) + XFREE(MTYPE_BMP_MIRRORQ, + inner); + } + + zlog_warn("bmp[%s] lost mirror messages due to buffer size limit", + bmp->remote); + bmp->mirror_lost = true; + pullwr_bump(bmp->pullwr); + } + } + } +} + +static int bmp_mirror_packet(struct peer *peer, uint8_t type, bgp_size_t size, + struct stream *packet) +{ + struct bmp_bgp *bmpbgp = bmp_bgp_find(peer->bgp); + struct timeval tv; + struct bmp_mirrorq *qitem; + struct bmp_targets *bt; + struct bmp *bmp; + + gettimeofday(&tv, NULL); + + if (type == BGP_MSG_OPEN) { + struct bmp_bgp_peer *bbpeer = bmp_bgp_peer_get(peer); + + XFREE(MTYPE_BMP_OPEN, bbpeer->open_rx); + + bbpeer->open_rx_len = size; + bbpeer->open_rx = XMALLOC(MTYPE_BMP_OPEN, size); + memcpy(bbpeer->open_rx, packet->data, size); + } + + if (!bmpbgp) + return 0; + + qitem = XCALLOC(MTYPE_BMP_MIRRORQ, sizeof(*qitem) + size); + qitem->peerid = peer->qobj_node.nid; + qitem->tv = tv; + qitem->len = size; + memcpy(qitem->data, packet->data, size); + + frr_each(bmp_targets, &bmpbgp->targets, bt) { + if (!bt->mirror) + continue; + frr_each(bmp_session, &bt->sessions, bmp) { + qitem->refcount++; + if (!bmp->mirrorpos) + bmp->mirrorpos = qitem; + pullwr_bump(bmp->pullwr); + } + } + if (qitem->refcount == 0) + XFREE(MTYPE_BMP_MIRRORQ, qitem); + else { + bmpbgp->mirror_qsize += sizeof(*qitem) + size; + bmp_mirrorq_add_tail(&bmpbgp->mirrorq, qitem); + + bmp_mirror_cull(bmpbgp); + + bmpbgp->mirror_qsizemax = MAX(bmpbgp->mirror_qsizemax, + bmpbgp->mirror_qsize); + } + return 0; +} + +static void bmp_wrmirror_lost(struct bmp *bmp, struct pullwr *pullwr) +{ + struct stream *s; + struct timeval tv; + + gettimeofday(&tv, NULL); + + s = stream_new(BGP_MAX_PACKET_SIZE); + + bmp_common_hdr(s, BMP_VERSION_3, BMP_TYPE_ROUTE_MIRRORING); + bmp_per_peer_hdr(s, bmp->targets->bgp->peer_self, 0, &tv); + + stream_putw(s, BMP_MIRROR_TLV_TYPE_INFO); + stream_putw(s, 2); + stream_putw(s, BMP_MIRROR_INFO_CODE_LOSTMSGS); + stream_putl_at(s, BMP_LENGTH_POS, stream_get_endp(s)); + + bmp->cnt_mirror_overruns++; + pullwr_write_stream(bmp->pullwr, s); + stream_free(s); +} + +static bool bmp_wrmirror(struct bmp *bmp, struct pullwr *pullwr) +{ + struct bmp_mirrorq *bmq; + struct peer *peer; + bool written = false; + + if (bmp->mirror_lost) { + bmp_wrmirror_lost(bmp, pullwr); + bmp->mirror_lost = false; + return true; + } + + bmq = bmp_pull_mirror(bmp); + if (!bmq) + return false; + + peer = QOBJ_GET_TYPESAFE(bmq->peerid, peer); + if (!peer) { + zlog_info("bmp: skipping mirror message for deleted peer"); + goto out; + } + + struct stream *s; + s = stream_new(BGP_MAX_PACKET_SIZE); + + bmp_common_hdr(s, BMP_VERSION_3, BMP_TYPE_ROUTE_MIRRORING); + bmp_per_peer_hdr(s, peer, 0, &bmq->tv); + + /* BMP Mirror TLV. */ + stream_putw(s, BMP_MIRROR_TLV_TYPE_BGP_MESSAGE); + stream_putw(s, bmq->len); + stream_putl_at(s, BMP_LENGTH_POS, stream_get_endp(s) + bmq->len); + + bmp->cnt_mirror++; + pullwr_write_stream(bmp->pullwr, s); + pullwr_write(bmp->pullwr, bmq->data, bmq->len); + + stream_free(s); + written = true; + +out: + if (!bmq->refcount) + XFREE(MTYPE_BMP_MIRRORQ, bmq); + return written; +} + +static int bmp_outgoing_packet(struct peer *peer, uint8_t type, bgp_size_t size, + struct stream *packet) +{ + if (type == BGP_MSG_OPEN) { + struct bmp_bgp_peer *bbpeer = bmp_bgp_peer_get(peer); + + XFREE(MTYPE_BMP_OPEN, bbpeer->open_tx); + + bbpeer->open_tx_len = size; + bbpeer->open_tx = XMALLOC(MTYPE_BMP_OPEN, size); + memcpy(bbpeer->open_tx, packet->data, size); + } + return 0; +} + +static int bmp_peer_established(struct peer *peer) +{ + struct bmp_bgp *bmpbgp = bmp_bgp_find(peer->bgp); + + if (!bmpbgp) + return 0; + + /* Check if this peer just went to Established */ + if ((peer->ostatus != OpenConfirm) || !(peer_established(peer))) + return 0; + + if (peer->doppelganger && (peer->doppelganger->status != Deleted)) { + struct bmp_bgp_peer *bbpeer, *bbdopp; + + bbpeer = bmp_bgp_peer_get(peer); + bbdopp = bmp_bgp_peer_find(peer->doppelganger->qobj_node.nid); + if (bbdopp) { + XFREE(MTYPE_BMP_OPEN, bbpeer->open_tx); + XFREE(MTYPE_BMP_OPEN, bbpeer->open_rx); + + bbpeer->open_tx = bbdopp->open_tx; + bbpeer->open_tx_len = bbdopp->open_tx_len; + bbpeer->open_rx = bbdopp->open_rx; + bbpeer->open_rx_len = bbdopp->open_rx_len; + + bmp_peerh_del(&bmp_peerh, bbdopp); + XFREE(MTYPE_BMP_PEER, bbdopp); + } + } + + bmp_send_all(bmpbgp, bmp_peerstate(peer, false)); + return 0; +} + +static int bmp_peer_backward(struct peer *peer) +{ + struct bmp_bgp *bmpbgp = bmp_bgp_find(peer->bgp); + struct bmp_bgp_peer *bbpeer; + + if (!bmpbgp) + return 0; + + bbpeer = bmp_bgp_peer_find(peer->qobj_node.nid); + if (bbpeer) { + XFREE(MTYPE_BMP_OPEN, bbpeer->open_tx); + bbpeer->open_tx_len = 0; + XFREE(MTYPE_BMP_OPEN, bbpeer->open_rx); + bbpeer->open_rx_len = 0; + } + + bmp_send_all(bmpbgp, bmp_peerstate(peer, true)); + return 0; +} + +static void bmp_eor(struct bmp *bmp, afi_t afi, safi_t safi, uint8_t flags) +{ + struct peer *peer; + struct listnode *node; + struct stream *s, *s2; + iana_afi_t pkt_afi; + iana_safi_t pkt_safi; + + s = stream_new(BGP_MAX_PACKET_SIZE); + + /* Make BGP update packet. */ + bgp_packet_set_marker(s, BGP_MSG_UPDATE); + + /* Unfeasible Routes Length */ + stream_putw(s, 0); + + if (afi == AFI_IP && safi == SAFI_UNICAST) { + /* Total Path Attribute Length */ + stream_putw(s, 0); + } else { + /* Convert AFI, SAFI to values for packet. */ + bgp_map_afi_safi_int2iana(afi, safi, &pkt_afi, &pkt_safi); + + /* Total Path Attribute Length */ + stream_putw(s, 6); + stream_putc(s, BGP_ATTR_FLAG_OPTIONAL); + stream_putc(s, BGP_ATTR_MP_UNREACH_NLRI); + stream_putc(s, 3); + stream_putw(s, pkt_afi); + stream_putc(s, pkt_safi); + } + + bgp_packet_set_size(s); + + for (ALL_LIST_ELEMENTS_RO(bmp->targets->bgp->peer, node, peer)) { + if (!peer->afc_nego[afi][safi]) + continue; + + s2 = stream_new(BGP_MAX_PACKET_SIZE); + + bmp_common_hdr(s2, BMP_VERSION_3, + BMP_TYPE_ROUTE_MONITORING); + bmp_per_peer_hdr(s2, peer, flags, NULL); + + stream_putl_at(s2, BMP_LENGTH_POS, + stream_get_endp(s) + stream_get_endp(s2)); + + bmp->cnt_update++; + pullwr_write_stream(bmp->pullwr, s2); + pullwr_write_stream(bmp->pullwr, s); + stream_free(s2); + } + stream_free(s); +} + +static struct stream *bmp_update(const struct prefix *p, struct prefix_rd *prd, + struct peer *peer, struct attr *attr, + afi_t afi, safi_t safi) +{ + struct bpacket_attr_vec_arr vecarr; + struct stream *s; + size_t attrlen_pos = 0, mpattrlen_pos = 0; + bgp_size_t total_attr_len = 0; + + bpacket_attr_vec_arr_reset(&vecarr); + + s = stream_new(BGP_MAX_PACKET_SIZE); + bgp_packet_set_marker(s, BGP_MSG_UPDATE); + + /* 2: withdrawn routes length */ + stream_putw(s, 0); + + /* 3: total attributes length - attrlen_pos stores the position */ + attrlen_pos = stream_get_endp(s); + stream_putw(s, 0); + + /* 5: Encode all the attributes, except MP_REACH_NLRI attr. */ + total_attr_len = bgp_packet_attribute(NULL, peer, s, attr, + &vecarr, NULL, afi, safi, peer, NULL, NULL, 0, 0, 0); + + /* space check? */ + + /* peer_cap_enhe & add-path removed */ + if (afi == AFI_IP && safi == SAFI_UNICAST) + stream_put_prefix(s, p); + else { + size_t p1 = stream_get_endp(s); + + /* MPLS removed for now */ + + mpattrlen_pos = bgp_packet_mpattr_start(s, peer, afi, safi, + &vecarr, attr); + bgp_packet_mpattr_prefix(s, afi, safi, p, prd, NULL, 0, 0, 0, + attr); + bgp_packet_mpattr_end(s, mpattrlen_pos); + total_attr_len += stream_get_endp(s) - p1; + } + + /* set the total attribute length correctly */ + stream_putw_at(s, attrlen_pos, total_attr_len); + bgp_packet_set_size(s); + return s; +} + +static struct stream *bmp_withdraw(const struct prefix *p, + struct prefix_rd *prd, afi_t afi, + safi_t safi) +{ + struct stream *s; + size_t attrlen_pos = 0, mp_start, mplen_pos; + bgp_size_t total_attr_len = 0; + bgp_size_t unfeasible_len; + + s = stream_new(BGP_MAX_PACKET_SIZE); + + bgp_packet_set_marker(s, BGP_MSG_UPDATE); + stream_putw(s, 0); + + if (afi == AFI_IP && safi == SAFI_UNICAST) { + stream_put_prefix(s, p); + unfeasible_len = stream_get_endp(s) - BGP_HEADER_SIZE + - BGP_UNFEASIBLE_LEN; + stream_putw_at(s, BGP_HEADER_SIZE, unfeasible_len); + stream_putw(s, 0); + } else { + attrlen_pos = stream_get_endp(s); + /* total attr length = 0 for now. reevaluate later */ + stream_putw(s, 0); + mp_start = stream_get_endp(s); + mplen_pos = bgp_packet_mpunreach_start(s, afi, safi); + + bgp_packet_mpunreach_prefix(s, p, afi, safi, prd, NULL, 0, 0, 0, + NULL); + /* Set the mp_unreach attr's length */ + bgp_packet_mpunreach_end(s, mplen_pos); + + /* Set total path attribute length. */ + total_attr_len = stream_get_endp(s) - mp_start; + stream_putw_at(s, attrlen_pos, total_attr_len); + } + + bgp_packet_set_size(s); + return s; +} + +static void bmp_monitor(struct bmp *bmp, struct peer *peer, uint8_t flags, + const struct prefix *p, struct prefix_rd *prd, + struct attr *attr, afi_t afi, safi_t safi, + time_t uptime) +{ + struct stream *hdr, *msg; + struct timeval tv = { .tv_sec = uptime, .tv_usec = 0 }; + struct timeval uptime_real; + + monotime_to_realtime(&tv, &uptime_real); + if (attr) + msg = bmp_update(p, prd, peer, attr, afi, safi); + else + msg = bmp_withdraw(p, prd, afi, safi); + + hdr = stream_new(BGP_MAX_PACKET_SIZE); + bmp_common_hdr(hdr, BMP_VERSION_3, BMP_TYPE_ROUTE_MONITORING); + bmp_per_peer_hdr(hdr, peer, flags, &uptime_real); + + stream_putl_at(hdr, BMP_LENGTH_POS, + stream_get_endp(hdr) + stream_get_endp(msg)); + + bmp->cnt_update++; + pullwr_write_stream(bmp->pullwr, hdr); + pullwr_write_stream(bmp->pullwr, msg); + stream_free(hdr); + stream_free(msg); +} + +static bool bmp_wrsync(struct bmp *bmp, struct pullwr *pullwr) +{ + afi_t afi; + safi_t safi; + + if (bmp->syncafi == AFI_MAX) { + FOREACH_AFI_SAFI (afi, safi) { + if (bmp->afistate[afi][safi] != BMP_AFI_NEEDSYNC) + continue; + + bmp->afistate[afi][safi] = BMP_AFI_SYNC; + + bmp->syncafi = afi; + bmp->syncsafi = safi; + bmp->syncpeerid = 0; + memset(&bmp->syncpos, 0, sizeof(bmp->syncpos)); + bmp->syncpos.family = afi2family(afi); + bmp->syncrdpos = NULL; + zlog_info("bmp[%s] %s %s sending table", + bmp->remote, + afi2str(bmp->syncafi), + safi2str(bmp->syncsafi)); + /* break does not work here, 2 loops... */ + goto afibreak; + } + if (bmp->syncafi == AFI_MAX) + return false; + } + +afibreak: + afi = bmp->syncafi; + safi = bmp->syncsafi; + + if (!bmp->targets->afimon[afi][safi]) { + /* shouldn't happen */ + bmp->afistate[afi][safi] = BMP_AFI_INACTIVE; + bmp->syncafi = AFI_MAX; + bmp->syncsafi = SAFI_MAX; + return true; + } + + struct bgp_table *table = bmp->targets->bgp->rib[afi][safi]; + struct bgp_dest *bn; + struct bgp_path_info *bpi = NULL, *bpiter; + struct bgp_adj_in *adjin = NULL, *adjiter; + + if (afi == AFI_L2VPN && safi == SAFI_EVPN) { + /* initialize syncrdpos to the first + * mid-layer table entry + */ + if (!bmp->syncrdpos) { + bmp->syncrdpos = bgp_table_top(table); + if (!bmp->syncrdpos) + goto eor; + } + + /* look for a valid mid-layer table */ + do { + table = bgp_dest_get_bgp_table_info(bmp->syncrdpos); + if (table) { + break; + } + bmp->syncrdpos = bgp_route_next(bmp->syncrdpos); + } while (bmp->syncrdpos); + + /* mid-layer table completed */ + if (!bmp->syncrdpos) + goto eor; + } + + bn = bgp_node_lookup(table, &bmp->syncpos); + do { + if (!bn) { + bn = bgp_table_get_next(table, &bmp->syncpos); + if (!bn) { + if (afi == AFI_L2VPN && safi == SAFI_EVPN) { + /* reset bottom-layer pointer */ + memset(&bmp->syncpos, 0, + sizeof(bmp->syncpos)); + bmp->syncpos.family = afi2family(afi); + /* check whethere there is a valid + * next mid-layer table, otherwise + * declare table completed (eor) + */ + for (bmp->syncrdpos = bgp_route_next( + bmp->syncrdpos); + bmp->syncrdpos; + bmp->syncrdpos = bgp_route_next( + bmp->syncrdpos)) + if (bgp_dest_get_bgp_table_info( + bmp->syncrdpos)) + return true; + } + eor: + zlog_info("bmp[%s] %s %s table completed (EoR)", + bmp->remote, afi2str(afi), + safi2str(safi)); + bmp_eor(bmp, afi, safi, BMP_PEER_FLAG_L); + bmp_eor(bmp, afi, safi, 0); + + bmp->afistate[afi][safi] = BMP_AFI_LIVE; + bmp->syncafi = AFI_MAX; + bmp->syncsafi = SAFI_MAX; + return true; + } + bmp->syncpeerid = 0; + prefix_copy(&bmp->syncpos, bgp_dest_get_prefix(bn)); + } + + if (bmp->targets->afimon[afi][safi] & BMP_MON_POSTPOLICY) { + for (bpiter = bgp_dest_get_bgp_path_info(bn); bpiter; + bpiter = bpiter->next) { + if (!CHECK_FLAG(bpiter->flags, BGP_PATH_VALID)) + continue; + if (bpiter->peer->qobj_node.nid + <= bmp->syncpeerid) + continue; + if (bpi && bpiter->peer->qobj_node.nid + > bpi->peer->qobj_node.nid) + continue; + bpi = bpiter; + } + } + if (bmp->targets->afimon[afi][safi] & BMP_MON_PREPOLICY) { + for (adjiter = bn->adj_in; adjiter; + adjiter = adjiter->next) { + if (adjiter->peer->qobj_node.nid + <= bmp->syncpeerid) + continue; + if (adjin && adjiter->peer->qobj_node.nid + > adjin->peer->qobj_node.nid) + continue; + adjin = adjiter; + } + } + if (bpi || adjin) + break; + + bn = NULL; + } while (1); + + if (adjin && bpi + && adjin->peer->qobj_node.nid < bpi->peer->qobj_node.nid) { + bpi = NULL; + bmp->syncpeerid = adjin->peer->qobj_node.nid; + } else if (adjin && bpi + && adjin->peer->qobj_node.nid > bpi->peer->qobj_node.nid) { + adjin = NULL; + bmp->syncpeerid = bpi->peer->qobj_node.nid; + } else if (bpi) { + bmp->syncpeerid = bpi->peer->qobj_node.nid; + } else if (adjin) { + bmp->syncpeerid = adjin->peer->qobj_node.nid; + } + + const struct prefix *bn_p = bgp_dest_get_prefix(bn); + struct prefix_rd *prd = NULL; + if (afi == AFI_L2VPN && safi == SAFI_EVPN) + prd = (struct prefix_rd *)bgp_dest_get_prefix(bmp->syncrdpos); + + if (bpi) + bmp_monitor(bmp, bpi->peer, BMP_PEER_FLAG_L, bn_p, prd, + bpi->attr, afi, safi, bpi->uptime); + if (adjin) + bmp_monitor(bmp, adjin->peer, 0, bn_p, prd, adjin->attr, afi, + safi, adjin->uptime); + + return true; +} + +static struct bmp_queue_entry *bmp_pull(struct bmp *bmp) +{ + struct bmp_queue_entry *bqe; + + bqe = bmp->queuepos; + if (!bqe) + return NULL; + + bmp->queuepos = bmp_qlist_next(&bmp->targets->updlist, bqe); + + bqe->refcount--; + if (!bqe->refcount) { + bmp_qhash_del(&bmp->targets->updhash, bqe); + bmp_qlist_del(&bmp->targets->updlist, bqe); + } + return bqe; +} + +static bool bmp_wrqueue(struct bmp *bmp, struct pullwr *pullwr) +{ + struct bmp_queue_entry *bqe; + struct peer *peer; + struct bgp_dest *bn; + bool written = false; + + bqe = bmp_pull(bmp); + if (!bqe) + return false; + + afi_t afi = bqe->afi; + safi_t safi = bqe->safi; + + switch (bmp->afistate[afi][safi]) { + case BMP_AFI_INACTIVE: + case BMP_AFI_NEEDSYNC: + goto out; + case BMP_AFI_SYNC: + if (prefix_cmp(&bqe->p, &bmp->syncpos) <= 0) + /* currently syncing but have already passed this + * prefix => send it. */ + break; + + /* currently syncing & haven't reached this prefix yet + * => it'll be sent as part of the table sync, no need here */ + goto out; + case BMP_AFI_LIVE: + break; + } + + peer = QOBJ_GET_TYPESAFE(bqe->peerid, peer); + if (!peer) { + zlog_info("bmp: skipping queued item for deleted peer"); + goto out; + } + if (peer->status != Established) + goto out; + + bn = bgp_node_lookup(bmp->targets->bgp->rib[afi][safi], &bqe->p); + struct prefix_rd *prd = NULL; + if (bqe->afi == AFI_L2VPN && bqe->safi == SAFI_EVPN) + prd = &bqe->rd; + + if (bmp->targets->afimon[afi][safi] & BMP_MON_POSTPOLICY) { + struct bgp_path_info *bpi; + + for (bpi = bn ? bgp_dest_get_bgp_path_info(bn) : NULL; bpi; + bpi = bpi->next) { + if (!CHECK_FLAG(bpi->flags, BGP_PATH_VALID)) + continue; + if (bpi->peer == peer) + break; + } + + bmp_monitor(bmp, peer, BMP_PEER_FLAG_L, &bqe->p, prd, + bpi ? bpi->attr : NULL, afi, safi, + bpi ? bpi->uptime : monotime(NULL)); + written = true; + } + + if (bmp->targets->afimon[afi][safi] & BMP_MON_PREPOLICY) { + struct bgp_adj_in *adjin; + + for (adjin = bn ? bn->adj_in : NULL; adjin; + adjin = adjin->next) { + if (adjin->peer == peer) + break; + } + bmp_monitor(bmp, peer, BMP_PEER_FLAG_L, &bqe->p, prd, + adjin ? adjin->attr : NULL, afi, safi, + adjin ? adjin->uptime : monotime(NULL)); + written = true; + } + +out: + if (!bqe->refcount) + XFREE(MTYPE_BMP_QUEUE, bqe); + return written; +} + +static void bmp_wrfill(struct bmp *bmp, struct pullwr *pullwr) +{ + switch(bmp->state) { + case BMP_PeerUp: + bmp_send_peerup(bmp); + bmp->state = BMP_Run; + break; + + case BMP_Run: + if (bmp_wrmirror(bmp, pullwr)) + break; + if (bmp_wrqueue(bmp, pullwr)) + break; + if (bmp_wrsync(bmp, pullwr)) + break; + break; + } +} + +static void bmp_wrerr(struct bmp *bmp, struct pullwr *pullwr, bool eof) +{ + if (eof) + zlog_info("bmp[%s] disconnected", bmp->remote); + else + flog_warn(EC_LIB_SYSTEM_CALL, "bmp[%s] connection error: %s", + bmp->remote, strerror(errno)); + + bmp_close(bmp); + bmp_free(bmp); +} + +static void bmp_process_one(struct bmp_targets *bt, struct bgp *bgp, afi_t afi, + safi_t safi, struct bgp_dest *bn, struct peer *peer) +{ + struct bmp *bmp; + struct bmp_queue_entry *bqe, bqeref; + size_t refcount; + + refcount = bmp_session_count(&bt->sessions); + if (refcount == 0) + return; + + memset(&bqeref, 0, sizeof(bqeref)); + prefix_copy(&bqeref.p, bgp_dest_get_prefix(bn)); + bqeref.peerid = peer->qobj_node.nid; + bqeref.afi = afi; + bqeref.safi = safi; + + if (afi == AFI_L2VPN && safi == SAFI_EVPN && bn->pdest) + prefix_copy(&bqeref.rd, + (struct prefix_rd *)bgp_dest_get_prefix(bn->pdest)); + + bqe = bmp_qhash_find(&bt->updhash, &bqeref); + if (bqe) { + if (bqe->refcount >= refcount) + /* nothing to do here */ + return; + + bmp_qlist_del(&bt->updlist, bqe); + } else { + bqe = XMALLOC(MTYPE_BMP_QUEUE, sizeof(*bqe)); + memcpy(bqe, &bqeref, sizeof(*bqe)); + + bmp_qhash_add(&bt->updhash, bqe); + } + + bqe->refcount = refcount; + bmp_qlist_add_tail(&bt->updlist, bqe); + + frr_each (bmp_session, &bt->sessions, bmp) + if (!bmp->queuepos) + bmp->queuepos = bqe; +} + +static int bmp_process(struct bgp *bgp, afi_t afi, safi_t safi, + struct bgp_dest *bn, struct peer *peer, bool withdraw) +{ + struct bmp_bgp *bmpbgp = bmp_bgp_find(peer->bgp); + struct bmp_targets *bt; + struct bmp *bmp; + + if (!bmpbgp) + return 0; + + frr_each(bmp_targets, &bmpbgp->targets, bt) { + if (!bt->afimon[afi][safi]) + continue; + + bmp_process_one(bt, bgp, afi, safi, bn, peer); + + frr_each(bmp_session, &bt->sessions, bmp) { + pullwr_bump(bmp->pullwr); + } + } + return 0; +} + +static void bmp_stat_put_u32(struct stream *s, size_t *cnt, uint16_t type, + uint32_t value) +{ + stream_putw(s, type); + stream_putw(s, 4); + stream_putl(s, value); + (*cnt)++; +} + +static int bmp_stats(struct thread *thread) +{ + struct bmp_targets *bt = THREAD_ARG(thread); + struct stream *s; + struct peer *peer; + struct listnode *node; + struct timeval tv; + + if (bt->stat_msec) + thread_add_timer_msec(bm->master, bmp_stats, bt, bt->stat_msec, + &bt->t_stats); + + gettimeofday(&tv, NULL); + + /* Walk down all peers */ + for (ALL_LIST_ELEMENTS_RO(bt->bgp->peer, node, peer)) { + size_t count = 0, count_pos, len; + + if (peer->status != Established) + continue; + + s = stream_new(BGP_MAX_PACKET_SIZE); + bmp_common_hdr(s, BMP_VERSION_3, BMP_TYPE_STATISTICS_REPORT); + bmp_per_peer_hdr(s, peer, 0, &tv); + + count_pos = stream_get_endp(s); + stream_putl(s, 0); + + bmp_stat_put_u32(s, &count, BMP_STATS_PFX_REJECTED, + peer->stat_pfx_filter); + bmp_stat_put_u32(s, &count, BMP_STATS_UPD_LOOP_ASPATH, + peer->stat_pfx_aspath_loop); + bmp_stat_put_u32(s, &count, BMP_STATS_UPD_LOOP_ORIGINATOR, + peer->stat_pfx_originator_loop); + bmp_stat_put_u32(s, &count, BMP_STATS_UPD_LOOP_CLUSTER, + peer->stat_pfx_cluster_loop); + bmp_stat_put_u32(s, &count, BMP_STATS_PFX_DUP_WITHDRAW, + peer->stat_pfx_dup_withdraw); + bmp_stat_put_u32(s, &count, BMP_STATS_UPD_7606_WITHDRAW, + peer->stat_upd_7606); + bmp_stat_put_u32(s, &count, BMP_STATS_FRR_NH_INVALID, + peer->stat_pfx_nh_invalid); + + stream_putl_at(s, count_pos, count); + + len = stream_get_endp(s); + stream_putl_at(s, BMP_LENGTH_POS, len); + + bmp_send_all(bt->bmpbgp, s); + } + return 0; +} + +static struct bmp *bmp_open(struct bmp_targets *bt, int bmp_sock) +{ + union sockunion su, *sumem; + struct prefix p; + int on = 1; + struct access_list *acl = NULL; + enum filter_type ret; + char buf[SU_ADDRSTRLEN]; + struct bmp *bmp; + + sumem = sockunion_getpeername(bmp_sock); + if (!sumem) { + close(bmp_sock); + return NULL; + } + memcpy(&su, sumem, sizeof(su)); + sockunion_free(sumem); + + set_nonblocking(bmp_sock); + set_cloexec(bmp_sock); + shutdown(bmp_sock, SHUT_RD); + + sockunion2hostprefix(&su, &p); + + acl = NULL; + switch (p.family) { + case AF_INET: + acl = access_list_lookup(AFI_IP, bt->acl_name); + break; + case AF_INET6: + acl = access_list_lookup(AFI_IP6, bt->acl6_name); + break; + default: + break; + } + + ret = FILTER_PERMIT; + if (acl) { + ret = access_list_apply(acl, &p); + } + + sockunion2str(&su, buf, SU_ADDRSTRLEN); + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ":%u", + su.sa.sa_family == AF_INET + ? ntohs(su.sin.sin_port) + : ntohs(su.sin6.sin6_port)); + + if (ret == FILTER_DENY) { + bt->cnt_aclrefused++; + zlog_info("bmp[%s] connection refused by access-list", buf); + close(bmp_sock); + return NULL; + } + bt->cnt_accept++; + + if (setsockopt(bmp_sock, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)) < 0) + flog_err(EC_LIB_SOCKET, "bmp: %d can't setsockopt SO_KEEPALIVE: %s(%d)", + bmp_sock, safe_strerror(errno), errno); + if (setsockopt(bmp_sock, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) < 0) + flog_err(EC_LIB_SOCKET, "bmp: %d can't setsockopt TCP_NODELAY: %s(%d)", + bmp_sock, safe_strerror(errno), errno); + + zlog_info("bmp[%s] connection established", buf); + + /* Allocate new BMP structure and set up default values. */ + bmp = bmp_new(bt, bmp_sock); + strlcpy(bmp->remote, buf, sizeof(bmp->remote)); + + bmp->state = BMP_PeerUp; + bmp->pullwr = pullwr_new(bm->master, bmp_sock, bmp, bmp_wrfill, + bmp_wrerr); + bmp_send_initiation(bmp); + + return bmp; +} + +/* Accept BMP connection. */ +static int bmp_accept(struct thread *thread) +{ + union sockunion su; + struct bmp_listener *bl = THREAD_ARG(thread); + int bmp_sock; + + /* We continue hearing BMP socket. */ + thread_add_read(bm->master, bmp_accept, bl, bl->sock, &bl->t_accept); + + memset(&su, 0, sizeof(union sockunion)); + + /* We can handle IPv4 or IPv6 socket. */ + bmp_sock = sockunion_accept(bl->sock, &su); + if (bmp_sock < 0) { + zlog_info("bmp: accept_sock failed: %s", safe_strerror(errno)); + return -1; + } + bmp_open(bl->targets, bmp_sock); + return 0; +} + +static void bmp_close(struct bmp *bmp) +{ + struct bmp_queue_entry *bqe; + struct bmp_mirrorq *bmq; + + if (bmp->active) + bmp_active_disconnected(bmp->active); + + while ((bmq = bmp_pull_mirror(bmp))) + if (!bmq->refcount) + XFREE(MTYPE_BMP_MIRRORQ, bmq); + while ((bqe = bmp_pull(bmp))) + if (!bqe->refcount) + XFREE(MTYPE_BMP_QUEUE, bqe); + + THREAD_OFF(bmp->t_read); + pullwr_del(bmp->pullwr); + close(bmp->socket); +} + +static struct bmp_bgp *bmp_bgp_find(struct bgp *bgp) +{ + struct bmp_bgp dummy = { .bgp = bgp }; + return bmp_bgph_find(&bmp_bgph, &dummy); +} + +static struct bmp_bgp *bmp_bgp_get(struct bgp *bgp) +{ + struct bmp_bgp *bmpbgp; + + bmpbgp = bmp_bgp_find(bgp); + if (bmpbgp) + return bmpbgp; + + bmpbgp = XCALLOC(MTYPE_BMP, sizeof(*bmpbgp)); + bmpbgp->bgp = bgp; + bmpbgp->mirror_qsizelimit = ~0UL; + bmp_mirrorq_init(&bmpbgp->mirrorq); + bmp_bgph_add(&bmp_bgph, bmpbgp); + + return bmpbgp; +} + +static void bmp_bgp_put(struct bmp_bgp *bmpbgp) +{ + struct bmp_targets *bt; + + bmp_bgph_del(&bmp_bgph, bmpbgp); + + frr_each_safe(bmp_targets, &bmpbgp->targets, bt) + bmp_targets_put(bt); + + bmp_mirrorq_fini(&bmpbgp->mirrorq); + XFREE(MTYPE_BMP, bmpbgp); +} + +static int bmp_bgp_del(struct bgp *bgp) +{ + struct bmp_bgp *bmpbgp = bmp_bgp_find(bgp); + + if (bmpbgp) + bmp_bgp_put(bmpbgp); + return 0; +} + +static struct bmp_bgp_peer *bmp_bgp_peer_find(uint64_t peerid) +{ + struct bmp_bgp_peer dummy = { .peerid = peerid }; + return bmp_peerh_find(&bmp_peerh, &dummy); +} + +static struct bmp_bgp_peer *bmp_bgp_peer_get(struct peer *peer) +{ + struct bmp_bgp_peer *bbpeer; + + bbpeer = bmp_bgp_peer_find(peer->qobj_node.nid); + if (bbpeer) + return bbpeer; + + bbpeer = XCALLOC(MTYPE_BMP_PEER, sizeof(*bbpeer)); + bbpeer->peerid = peer->qobj_node.nid; + bmp_peerh_add(&bmp_peerh, bbpeer); + + return bbpeer; +} + +static struct bmp_targets *bmp_targets_find1(struct bgp *bgp, const char *name) +{ + struct bmp_bgp *bmpbgp = bmp_bgp_find(bgp); + struct bmp_targets dummy; + + if (!bmpbgp) + return NULL; + dummy.name = (char *)name; + return bmp_targets_find(&bmpbgp->targets, &dummy); +} + +static struct bmp_targets *bmp_targets_get(struct bgp *bgp, const char *name) +{ + struct bmp_targets *bt; + + bt = bmp_targets_find1(bgp, name); + if (bt) + return bt; + + bt = XCALLOC(MTYPE_BMP_TARGETS, sizeof(*bt)); + bt->name = XSTRDUP(MTYPE_BMP_TARGETSNAME, name); + bt->bgp = bgp; + bt->bmpbgp = bmp_bgp_get(bgp); + bmp_session_init(&bt->sessions); + bmp_qhash_init(&bt->updhash); + bmp_qlist_init(&bt->updlist); + bmp_actives_init(&bt->actives); + bmp_listeners_init(&bt->listeners); + + QOBJ_REG(bt, bmp_targets); + bmp_targets_add(&bt->bmpbgp->targets, bt); + return bt; +} + +static void bmp_targets_put(struct bmp_targets *bt) +{ + struct bmp *bmp; + struct bmp_active *ba; + + frr_each_safe (bmp_actives, &bt->actives, ba) + bmp_active_put(ba); + + frr_each_safe(bmp_session, &bt->sessions, bmp) { + bmp_close(bmp); + bmp_free(bmp); + } + + bmp_targets_del(&bt->bmpbgp->targets, bt); + QOBJ_UNREG(bt); + + bmp_listeners_fini(&bt->listeners); + bmp_actives_fini(&bt->actives); + bmp_qhash_fini(&bt->updhash); + bmp_qlist_fini(&bt->updlist); + + XFREE(MTYPE_BMP_ACLNAME, bt->acl_name); + XFREE(MTYPE_BMP_ACLNAME, bt->acl6_name); + bmp_session_fini(&bt->sessions); + + XFREE(MTYPE_BMP_TARGETSNAME, bt->name); + XFREE(MTYPE_BMP_TARGETS, bt); +} + +static struct bmp_listener *bmp_listener_find(struct bmp_targets *bt, + const union sockunion *su, + int port) +{ + struct bmp_listener dummy; + dummy.addr = *su; + dummy.port = port; + return bmp_listeners_find(&bt->listeners, &dummy); +} + +static struct bmp_listener *bmp_listener_get(struct bmp_targets *bt, + const union sockunion *su, + int port) +{ + struct bmp_listener *bl = bmp_listener_find(bt, su, port); + + if (bl) + return bl; + + bl = XCALLOC(MTYPE_BMP_LISTENER, sizeof(*bl)); + bl->targets = bt; + bl->addr = *su; + bl->port = port; + bl->sock = -1; + + bmp_listeners_add(&bt->listeners, bl); + return bl; +} + +static void bmp_listener_put(struct bmp_listener *bl) +{ + bmp_listeners_del(&bl->targets->listeners, bl); + XFREE(MTYPE_BMP_LISTENER, bl); +} + +static void bmp_listener_start(struct bmp_listener *bl) +{ + int sock, ret; + + sock = socket(bl->addr.sa.sa_family, SOCK_STREAM, 0); + if (sock < 0) + return; + + sockopt_reuseaddr(sock); + sockopt_reuseport(sock); + sockopt_v6only(bl->addr.sa.sa_family, sock); + set_cloexec(sock); + + ret = sockunion_bind(sock, &bl->addr, bl->port, &bl->addr); + if (ret < 0) + goto out_sock; + + ret = listen(sock, 3); + if (ret < 0) + goto out_sock; + + bl->sock = sock; + thread_add_read(bm->master, bmp_accept, bl, sock, &bl->t_accept); + return; +out_sock: + close(sock); +} + +static void bmp_listener_stop(struct bmp_listener *bl) +{ + THREAD_OFF(bl->t_accept); + + if (bl->sock != -1) + close(bl->sock); + bl->sock = -1; +} + +static struct bmp_active *bmp_active_find(struct bmp_targets *bt, + const char *hostname, int port) +{ + struct bmp_active dummy; + dummy.hostname = (char *)hostname; + dummy.port = port; + return bmp_actives_find(&bt->actives, &dummy); +} + +static struct bmp_active *bmp_active_get(struct bmp_targets *bt, + const char *hostname, int port) +{ + struct bmp_active *ba; + + ba = bmp_active_find(bt, hostname, port); + if (ba) + return ba; + + ba = XCALLOC(MTYPE_BMP_ACTIVE, sizeof(*ba)); + ba->targets = bt; + ba->hostname = XSTRDUP(MTYPE_TMP, hostname); + ba->port = port; + ba->minretry = BMP_DFLT_MINRETRY; + ba->maxretry = BMP_DFLT_MAXRETRY; + ba->socket = -1; + + bmp_actives_add(&bt->actives, ba); + return ba; +} + +static void bmp_active_put(struct bmp_active *ba) +{ + THREAD_OFF(ba->t_timer); + THREAD_OFF(ba->t_read); + THREAD_OFF(ba->t_write); + + bmp_actives_del(&ba->targets->actives, ba); + + if (ba->bmp) { + ba->bmp->active = NULL; + bmp_close(ba->bmp); + bmp_free(ba->bmp); + } + if (ba->socket != -1) + close(ba->socket); + + XFREE(MTYPE_TMP, ba->hostname); + XFREE(MTYPE_BMP_ACTIVE, ba); +} + +static void bmp_active_setup(struct bmp_active *ba); + +static void bmp_active_connect(struct bmp_active *ba) +{ + enum connect_result res; + char buf[SU_ADDRSTRLEN]; + + for (; ba->addrpos < ba->addrtotal; ba->addrpos++) { + ba->socket = sockunion_socket(&ba->addrs[ba->addrpos]); + if (ba->socket < 0) { + zlog_warn("bmp[%s]: failed to create socket", + ba->hostname); + continue; + } + + set_nonblocking(ba->socket); + res = sockunion_connect(ba->socket, &ba->addrs[ba->addrpos], + htons(ba->port), 0); + switch (res) { + case connect_error: + sockunion2str(&ba->addrs[ba->addrpos], buf, + sizeof(buf)); + zlog_warn("bmp[%s]: failed to connect to %s:%d", + ba->hostname, buf, ba->port); + close(ba->socket); + ba->socket = -1; + continue; + case connect_success: + break; + case connect_in_progress: + bmp_active_setup(ba); + return; + } + } + + /* exhausted all addresses */ + ba->curretry += ba->curretry / 2; + bmp_active_setup(ba); +} + +static void bmp_active_resolved(struct resolver_query *resq, const char *errstr, + int numaddrs, union sockunion *addr) +{ + struct bmp_active *ba = container_of(resq, struct bmp_active, resq); + unsigned i; + + if (numaddrs <= 0) { + zlog_warn("bmp[%s]: hostname resolution failed: %s", + ba->hostname, errstr); + ba->last_err = errstr; + ba->curretry += ba->curretry / 2; + ba->addrpos = 0; + ba->addrtotal = 0; + bmp_active_setup(ba); + return; + } + + if (numaddrs > (int)array_size(ba->addrs)) + numaddrs = array_size(ba->addrs); + + ba->addrpos = 0; + ba->addrtotal = numaddrs; + for (i = 0; i < ba->addrtotal; i++) + memcpy(&ba->addrs[i], &addr[i], sizeof(ba->addrs[0])); + + bmp_active_connect(ba); +} + +static int bmp_active_thread(struct thread *t) +{ + struct bmp_active *ba = THREAD_ARG(t); + socklen_t slen; + int status, ret; + char buf[SU_ADDRSTRLEN]; + + /* all 3 end up here, though only timer or read+write are active + * at a time */ + THREAD_OFF(ba->t_timer); + THREAD_OFF(ba->t_read); + THREAD_OFF(ba->t_write); + + ba->last_err = NULL; + + if (ba->socket == -1) { + resolver_resolve(&ba->resq, AF_UNSPEC, ba->hostname, + bmp_active_resolved); + return 0; + } + + slen = sizeof(status); + ret = getsockopt(ba->socket, SOL_SOCKET, SO_ERROR, (void *)&status, + &slen); + + sockunion2str(&ba->addrs[ba->addrpos], buf, sizeof(buf)); + if (ret < 0 || status != 0) { + ba->last_err = strerror(status); + zlog_warn("bmp[%s]: failed to connect to %s:%d: %s", + ba->hostname, buf, ba->port, ba->last_err); + goto out_next; + } + + zlog_warn("bmp[%s]: outbound connection to %s:%d", + ba->hostname, buf, ba->port); + + ba->bmp = bmp_open(ba->targets, ba->socket); + if (!ba->bmp) + goto out_next; + + ba->bmp->active = ba; + ba->socket = -1; + ba->curretry = ba->minretry; + return 0; + +out_next: + close(ba->socket); + ba->socket = -1; + ba->addrpos++; + bmp_active_connect(ba); + return 0; +} + +static void bmp_active_disconnected(struct bmp_active *ba) +{ + ba->bmp = NULL; + bmp_active_setup(ba); +} + +static void bmp_active_setup(struct bmp_active *ba) +{ + THREAD_OFF(ba->t_timer); + THREAD_OFF(ba->t_read); + THREAD_OFF(ba->t_write); + + if (ba->bmp) + return; + if (ba->resq.callback) + return; + + if (ba->curretry > ba->maxretry) + ba->curretry = ba->maxretry; + + if (ba->socket == -1) + thread_add_timer_msec(bm->master, bmp_active_thread, ba, + ba->curretry, &ba->t_timer); + else { + thread_add_read(bm->master, bmp_active_thread, ba, ba->socket, + &ba->t_read); + thread_add_write(bm->master, bmp_active_thread, ba, ba->socket, + &ba->t_write); + } +} + +static struct cmd_node bmp_node = { + .name = "bmp", + .node = BMP_NODE, + .parent_node = BGP_NODE, + .prompt = "%s(config-bgp-bmp)# " +}; + +#define BMP_STR "BGP Monitoring Protocol\n" + +#ifndef VTYSH_EXTRACT_PL +#include "bgpd/bgp_bmp_clippy.c" +#endif + +DEFPY_NOSH(bmp_targets_main, + bmp_targets_cmd, + "bmp targets BMPTARGETS", + BMP_STR + "Create BMP target group\n" + "Name of the BMP target group\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + struct bmp_targets *bt; + + bt = bmp_targets_get(bgp, bmptargets); + + VTY_PUSH_CONTEXT_SUB(BMP_NODE, bt); + return CMD_SUCCESS; +} + +DEFPY(no_bmp_targets_main, + no_bmp_targets_cmd, + "no bmp targets BMPTARGETS", + NO_STR + BMP_STR + "Delete BMP target group\n" + "Name of the BMP target group\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + struct bmp_targets *bt; + + bt = bmp_targets_find1(bgp, bmptargets); + if (!bt) { + vty_out(vty, "%% BMP target group not found\n"); + return CMD_WARNING; + } + bmp_targets_put(bt); + return CMD_SUCCESS; +} + +DEFPY(bmp_listener_main, + bmp_listener_cmd, + "bmp listener port (1-65535)", + BMP_STR + "Listen for inbound BMP connections\n" + "IPv6 address to listen on\n" + "IPv4 address to listen on\n" + "TCP Port number\n" + "TCP Port number\n") +{ + VTY_DECLVAR_CONTEXT_SUB(bmp_targets, bt); + struct bmp_listener *bl; + + bl = bmp_listener_get(bt, listener, port); + if (bl->sock == -1) + bmp_listener_start(bl); + + return CMD_SUCCESS; +} + +DEFPY(no_bmp_listener_main, + no_bmp_listener_cmd, + "no bmp listener port (1-65535)", + NO_STR + BMP_STR + "Create BMP listener\n" + "IPv6 address to listen on\n" + "IPv4 address to listen on\n" + "TCP Port number\n" + "TCP Port number\n") +{ + VTY_DECLVAR_CONTEXT_SUB(bmp_targets, bt); + struct bmp_listener *bl; + + bl = bmp_listener_find(bt, listener, port); + if (!bl) { + vty_out(vty, "%% BMP listener not found\n"); + return CMD_WARNING; + } + bmp_listener_stop(bl); + bmp_listener_put(bl); + return CMD_SUCCESS; +} + +DEFPY(bmp_connect, + bmp_connect_cmd, + "[no] bmp connect HOSTNAME port (1-65535) {min-retry (100-86400000)|max-retry (100-86400000)}", + NO_STR + BMP_STR + "Actively establish connection to monitoring station\n" + "Monitoring station hostname or address\n" + "TCP port\n" + "TCP port\n" + "Minimum connection retry interval\n" + "Minimum connection retry interval (milliseconds)\n" + "Maximum connection retry interval\n" + "Maximum connection retry interval (milliseconds)\n") +{ + VTY_DECLVAR_CONTEXT_SUB(bmp_targets, bt); + struct bmp_active *ba; + + if (no) { + ba = bmp_active_find(bt, hostname, port); + if (!ba) { + vty_out(vty, "%% No such active connection found\n"); + return CMD_WARNING; + } + bmp_active_put(ba); + return CMD_SUCCESS; + } + + ba = bmp_active_get(bt, hostname, port); + if (min_retry_str) + ba->minretry = min_retry; + if (max_retry_str) + ba->maxretry = max_retry; + ba->curretry = ba->minretry; + bmp_active_setup(ba); + + return CMD_SUCCESS; +} + +DEFPY(bmp_acl, + bmp_acl_cmd, + "[no] $af access-list WORD", + NO_STR + IP_STR + IPV6_STR + "Access list to restrict BMP sessions\n" + "Access list name\n") +{ + VTY_DECLVAR_CONTEXT_SUB(bmp_targets, bt); + char **what; + + if (no) + access_list = NULL; + if (!strcmp(af, "ipv6")) + what = &bt->acl6_name; + else + what = &bt->acl_name; + + XFREE(MTYPE_BMP_ACLNAME, *what); + if (access_list) + *what = XSTRDUP(MTYPE_BMP_ACLNAME, access_list); + + return CMD_SUCCESS; +} + +DEFPY(bmp_stats_cfg, + bmp_stats_cmd, + "[no] bmp stats [interval (100-86400000)]", + NO_STR + BMP_STR + "Send BMP statistics messages\n" + "Specify BMP stats interval\n" + "Interval (milliseconds) to send BMP Stats in\n") +{ + VTY_DECLVAR_CONTEXT_SUB(bmp_targets, bt); + + THREAD_OFF(bt->t_stats); + if (no) + bt->stat_msec = 0; + else if (interval_str) + bt->stat_msec = interval; + else + bt->stat_msec = BMP_STAT_DEFAULT_TIMER; + + if (bt->stat_msec) + thread_add_timer_msec(bm->master, bmp_stats, bt, bt->stat_msec, + &bt->t_stats); + return CMD_SUCCESS; +} + +DEFPY(bmp_monitor_cfg, + bmp_monitor_cmd, + "[no] bmp monitor $policy", + NO_STR + BMP_STR + "Send BMP route monitoring messages\n" + "Address Family\nAddress Family\nAddress Family\n" + "Address Family\nAddress Family\nAddress Family\n" + "Send state before policy and filter processing\n" + "Send state with policy and filters applied\n") +{ + int index = 0; + uint8_t flag, prev; + afi_t afi; + safi_t safi; + + VTY_DECLVAR_CONTEXT_SUB(bmp_targets, bt); + struct bmp *bmp; + + argv_find_and_parse_afi(argv, argc, &index, &afi); + argv_find_and_parse_safi(argv, argc, &index, &safi); + + if (policy[1] == 'r') + flag = BMP_MON_PREPOLICY; + else + flag = BMP_MON_POSTPOLICY; + + prev = bt->afimon[afi][safi]; + if (no) + bt->afimon[afi][safi] &= ~flag; + else + bt->afimon[afi][safi] |= flag; + + if (prev == bt->afimon[afi][safi]) + return CMD_SUCCESS; + + frr_each (bmp_session, &bt->sessions, bmp) { + if (bmp->syncafi == afi && bmp->syncsafi == safi) { + bmp->syncafi = AFI_MAX; + bmp->syncsafi = SAFI_MAX; + } + + if (!bt->afimon[afi][safi]) { + bmp->afistate[afi][safi] = BMP_AFI_INACTIVE; + continue; + } + + bmp->afistate[afi][safi] = BMP_AFI_NEEDSYNC; + } + + return CMD_SUCCESS; +} + +DEFPY(bmp_mirror_cfg, + bmp_mirror_cmd, + "[no] bmp mirror", + NO_STR + BMP_STR + "Send BMP route mirroring messages\n") +{ + VTY_DECLVAR_CONTEXT_SUB(bmp_targets, bt); + struct bmp *bmp; + + if (bt->mirror == !no) + return CMD_SUCCESS; + + bt->mirror = !no; + if (bt->mirror) + return CMD_SUCCESS; + + frr_each (bmp_session, &bt->sessions, bmp) { + struct bmp_mirrorq *bmq; + + while ((bmq = bmp_pull_mirror(bmp))) + if (!bmq->refcount) + XFREE(MTYPE_BMP_MIRRORQ, bmq); + } + return CMD_SUCCESS; +} + +DEFPY(bmp_mirror_limit_cfg, + bmp_mirror_limit_cmd, + "bmp mirror buffer-limit (0-4294967294)", + BMP_STR + "Route Mirroring settings\n" + "Configure maximum memory used for buffered mirroring messages\n" + "Limit in bytes\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + struct bmp_bgp *bmpbgp; + + bmpbgp = bmp_bgp_get(bgp); + bmpbgp->mirror_qsizelimit = buffer_limit; + + return CMD_SUCCESS; +} + +DEFPY(no_bmp_mirror_limit_cfg, + no_bmp_mirror_limit_cmd, + "no bmp mirror buffer-limit [(0-4294967294)]", + NO_STR + BMP_STR + "Route Mirroring settings\n" + "Configure maximum memory used for buffered mirroring messages\n" + "Limit in bytes\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + struct bmp_bgp *bmpbgp; + + bmpbgp = bmp_bgp_get(bgp); + bmpbgp->mirror_qsizelimit = ~0UL; + + return CMD_SUCCESS; +} + + +DEFPY(show_bmp, + show_bmp_cmd, + "show bmp", + SHOW_STR + BMP_STR) +{ + struct bmp_bgp *bmpbgp; + struct bmp_targets *bt; + struct bmp_listener *bl; + struct bmp_active *ba; + struct bmp *bmp; + struct ttable *tt; + char buf[SU_ADDRSTRLEN]; + char uptime[BGP_UPTIME_LEN]; + char *out; + + frr_each(bmp_bgph, &bmp_bgph, bmpbgp) { + vty_out(vty, "BMP state for BGP %s:\n\n", + bmpbgp->bgp->name_pretty); + vty_out(vty, " Route Mirroring %9zu bytes (%zu messages) pending\n", + bmpbgp->mirror_qsize, + bmp_mirrorq_count(&bmpbgp->mirrorq)); + vty_out(vty, " %9zu bytes maximum buffer used\n", + bmpbgp->mirror_qsizemax); + if (bmpbgp->mirror_qsizelimit != ~0UL) + vty_out(vty, " %9zu bytes buffer size limit\n", + bmpbgp->mirror_qsizelimit); + vty_out(vty, "\n"); + + frr_each(bmp_targets, &bmpbgp->targets, bt) { + vty_out(vty, " Targets \"%s\":\n", bt->name); + vty_out(vty, " Route Mirroring %sabled\n", + bt->mirror ? "en" : "dis"); + + afi_t afi; + safi_t safi; + + FOREACH_AFI_SAFI (afi, safi) { + const char *str = NULL; + + switch (bt->afimon[afi][safi]) { + case BMP_MON_PREPOLICY: + str = "pre-policy"; + break; + case BMP_MON_POSTPOLICY: + str = "post-policy"; + break; + case BMP_MON_PREPOLICY | BMP_MON_POSTPOLICY: + str = "pre-policy and post-policy"; + break; + } + if (!str) + continue; + vty_out(vty, " Route Monitoring %s %s %s\n", + afi2str(afi), safi2str(safi), str); + } + + vty_out(vty, " Listeners:\n"); + frr_each (bmp_listeners, &bt->listeners, bl) + vty_out(vty, " %s:%d\n", + sockunion2str(&bl->addr, buf, + SU_ADDRSTRLEN), bl->port); + + vty_out(vty, "\n Outbound connections:\n"); + tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]); + ttable_add_row(tt, "remote|state||timer"); + ttable_rowseps(tt, 0, BOTTOM, true, '-'); + frr_each (bmp_actives, &bt->actives, ba) { + const char *state_str = "?"; + + if (ba->bmp) { + peer_uptime(ba->bmp->t_up.tv_sec, + uptime, sizeof(uptime), + false, NULL); + ttable_add_row(tt, "%s:%d|Up|%s|%s", + ba->hostname, ba->port, + ba->bmp->remote, uptime); + continue; + } + + uptime[0] = '\0'; + + if (ba->t_timer) { + long trem = thread_timer_remain_second( + ba->t_timer); + + peer_uptime(monotime(NULL) - trem, + uptime, sizeof(uptime), + false, NULL); + state_str = "RetryWait"; + } else if (ba->t_read) { + state_str = "Connecting"; + } else if (ba->resq.callback) { + state_str = "Resolving"; + } + + ttable_add_row(tt, "%s:%d|%s|%s|%s", + ba->hostname, ba->port, + state_str, + ba->last_err ? ba->last_err : "", + uptime); + continue; + } + out = ttable_dump(tt, "\n"); + vty_out(vty, "%s", out); + XFREE(MTYPE_TMP, out); + ttable_del(tt); + + vty_out(vty, "\n %zu connected clients:\n", + bmp_session_count(&bt->sessions)); + tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]); + ttable_add_row(tt, "remote|uptime|MonSent|MirrSent|MirrLost|ByteSent|ByteQ|ByteQKernel"); + ttable_rowseps(tt, 0, BOTTOM, true, '-'); + + frr_each (bmp_session, &bt->sessions, bmp) { + uint64_t total; + size_t q, kq; + + pullwr_stats(bmp->pullwr, &total, &q, &kq); + + peer_uptime(bmp->t_up.tv_sec, uptime, + sizeof(uptime), false, NULL); + + ttable_add_row(tt, "%s|%s|%Lu|%Lu|%Lu|%Lu|%zu|%zu", + bmp->remote, uptime, + bmp->cnt_update, + bmp->cnt_mirror, + bmp->cnt_mirror_overruns, + total, q, kq); + } + out = ttable_dump(tt, "\n"); + vty_out(vty, "%s", out); + XFREE(MTYPE_TMP, out); + ttable_del(tt); + vty_out(vty, "\n"); + } + } + + return CMD_SUCCESS; +} + +static int bmp_config_write(struct bgp *bgp, struct vty *vty) +{ + struct bmp_bgp *bmpbgp = bmp_bgp_find(bgp); + struct bmp_targets *bt; + struct bmp_listener *bl; + struct bmp_active *ba; + char buf[SU_ADDRSTRLEN]; + afi_t afi; + safi_t safi; + + if (!bmpbgp) + return 0; + + if (bmpbgp->mirror_qsizelimit != ~0UL) + vty_out(vty, " !\n bmp mirror buffer-limit %zu\n", + bmpbgp->mirror_qsizelimit); + + frr_each(bmp_targets, &bmpbgp->targets, bt) { + vty_out(vty, " !\n bmp targets %s\n", bt->name); + + if (bt->acl6_name) + vty_out(vty, " ipv6 access-list %s\n", bt->acl6_name); + if (bt->acl_name) + vty_out(vty, " ip access-list %s\n", bt->acl_name); + + if (bt->stat_msec) + vty_out(vty, " bmp stats interval %d\n", + bt->stat_msec); + + if (bt->mirror) + vty_out(vty, " bmp mirror\n"); + + FOREACH_AFI_SAFI (afi, safi) { + const char *afi_str = (afi == AFI_IP) ? "ipv4" : "ipv6"; + + if (bt->afimon[afi][safi] & BMP_MON_PREPOLICY) + vty_out(vty, " bmp monitor %s %s pre-policy\n", + afi_str, safi2str(safi)); + if (bt->afimon[afi][safi] & BMP_MON_POSTPOLICY) + vty_out(vty, " bmp monitor %s %s post-policy\n", + afi_str, safi2str(safi)); + } + frr_each (bmp_listeners, &bt->listeners, bl) + vty_out(vty, " \n bmp listener %s port %d\n", + sockunion2str(&bl->addr, buf, SU_ADDRSTRLEN), + bl->port); + + frr_each (bmp_actives, &bt->actives, ba) + vty_out(vty, " bmp connect %s port %u min-retry %u max-retry %u\n", + ba->hostname, ba->port, ba->minretry, ba->maxretry); + } + + return 0; +} + +static int bgp_bmp_init(struct thread_master *tm) +{ + install_node(&bmp_node); + install_default(BMP_NODE); + install_element(BGP_NODE, &bmp_targets_cmd); + install_element(BGP_NODE, &no_bmp_targets_cmd); + + install_element(BMP_NODE, &bmp_listener_cmd); + install_element(BMP_NODE, &no_bmp_listener_cmd); + install_element(BMP_NODE, &bmp_connect_cmd); + install_element(BMP_NODE, &bmp_acl_cmd); + install_element(BMP_NODE, &bmp_stats_cmd); + install_element(BMP_NODE, &bmp_monitor_cmd); + install_element(BMP_NODE, &bmp_mirror_cmd); + + install_element(BGP_NODE, &bmp_mirror_limit_cmd); + install_element(BGP_NODE, &no_bmp_mirror_limit_cmd); + + install_element(VIEW_NODE, &show_bmp_cmd); + + resolver_init(tm); + return 0; +} + +static int bgp_bmp_module_init(void) +{ + hook_register(bgp_packet_dump, bmp_mirror_packet); + hook_register(bgp_packet_send, bmp_outgoing_packet); + hook_register(peer_status_changed, bmp_peer_established); + hook_register(peer_backward_transition, bmp_peer_backward); + hook_register(bgp_process, bmp_process); + hook_register(bgp_inst_config_write, bmp_config_write); + hook_register(bgp_inst_delete, bmp_bgp_del); + hook_register(frr_late_init, bgp_bmp_init); + return 0; +} + +FRR_MODULE_SETUP(.name = "bgpd_bmp", .version = FRR_VERSION, + .description = "bgpd BMP module", + .init = bgp_bmp_module_init) diff --git a/bgpd/bgp_bmp.h b/bgpd/bgp_bmp.h new file mode 100644 index 0000000000..9cbb27e805 --- /dev/null +++ b/bgpd/bgp_bmp.h @@ -0,0 +1,314 @@ +/* BMP support. + * Copyright (C) 2018 Yasuhiro Ohara + * Copyright (C) 2019 David Lamparter for NetDEF, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _BGP_BMP_H_ +#define _BGP_BMP_H_ + +#include "zebra.h" +#include "typesafe.h" +#include "pullwr.h" +#include "qobj.h" +#include "resolver.h" + +#define BMP_VERSION_3 3 + +#define BMP_LENGTH_POS 1 + +/* BMP message types */ +#define BMP_TYPE_ROUTE_MONITORING 0 +#define BMP_TYPE_STATISTICS_REPORT 1 +#define BMP_TYPE_PEER_DOWN_NOTIFICATION 2 +#define BMP_TYPE_PEER_UP_NOTIFICATION 3 +#define BMP_TYPE_INITIATION 4 +#define BMP_TYPE_TERMINATION 5 +#define BMP_TYPE_ROUTE_MIRRORING 6 + +#define BMP_READ_BUFSIZ 1024 + +/* bmp->state */ +#define BMP_None 0 +#define BMP_PeerUp 2 +#define BMP_Run 3 + +/* This one is for BMP Route Monitoring messages, i.e. delivering updates + * in somewhat processed (as opposed to fully raw, see mirroring below) form. + * RFC explicitly says that we can skip old updates if we haven't sent them out + * yet and another newer update for the same prefix arrives. + * + * So, at most one of these can exist for each (bgp, afi, safi, prefix, peerid) + * tuple; if some prefix is "re-added" to the queue, the existing entry is + * instead moved to the end of the queue. This ensures that the queue size is + * bounded by the BGP table size. + * + * bmp_qlist is the queue itself while bmp_qhash is used to efficiently check + * whether a tuple is already on the list. The queue is maintained per + * bmp_target. + * + * refcount = number of "struct bmp *" whose queue position is before this + * entry, i.e. number of BMP sessions where we still want to send this out. + * Decremented on send so we know when we're done with an entry (i.e. this + * always happens from the front of the queue.) + */ + +PREDECL_DLIST(bmp_qlist) +PREDECL_HASH(bmp_qhash) + +struct bmp_queue_entry { + struct bmp_qlist_item bli; + struct bmp_qhash_item bhi; + + struct prefix p; + uint64_t peerid; + afi_t afi; + safi_t safi; + + size_t refcount; + + /* initialized only for L2VPN/EVPN (S)AFIs */ + struct prefix_rd rd; +}; + +/* This is for BMP Route Mirroring, which feeds fully raw BGP PDUs out to BMP + * receivers. So, this goes directly off packet RX/TX handling instead of + * grabbing bits from tables. + * + * There is *one* queue for each "struct bgp *" where we throw everything on, + * with a size limit. Refcount works the same as for monitoring above. + */ + +PREDECL_LIST(bmp_mirrorq) + +struct bmp_mirrorq { + struct bmp_mirrorq_item bmi; + + size_t refcount; + uint64_t peerid; + struct timeval tv; + + size_t len; + uint8_t data[0]; +}; + +enum { + BMP_AFI_INACTIVE = 0, + BMP_AFI_NEEDSYNC, + BMP_AFI_SYNC, + BMP_AFI_LIVE, +}; + +PREDECL_LIST(bmp_session) + +struct bmp_active; +struct bmp_targets; + +/* an established BMP session to a peer */ +struct bmp { + struct bmp_session_item bsi; + struct bmp_targets *targets; + struct bmp_active *active; + + int socket; + char remote[SU_ADDRSTRLEN + 6]; + struct thread *t_read; + + struct pullwr *pullwr; + + int state; + + /* queue positions must remain synced with refcounts in the items. + * Whenever appending a queue item, we need to know the correct number + * of "struct bmp *" that want it, and when moving these positions + * ahead we need to make sure that refcount is decremented. Also, on + * disconnects we need to walk the queue and drop our reference. + */ + struct bmp_queue_entry *queuepos; + struct bmp_mirrorq *mirrorpos; + bool mirror_lost; + + /* enum BMP_AFI_* */ + uint8_t afistate[AFI_MAX][SAFI_MAX]; + + /* counters for the various BMP packet types */ + uint64_t cnt_update, cnt_mirror; + /* number of times this peer wasn't fast enough in consuming the + * mirror queue + */ + uint64_t cnt_mirror_overruns; + struct timeval t_up; + + /* synchronization / startup works by repeatedly finding the next + * table entry, the sync* fields note down what we sent last + */ + struct prefix syncpos; + struct bgp_node *syncrdpos; + uint64_t syncpeerid; + afi_t syncafi; + safi_t syncsafi; +}; + +/* config & state for an active outbound connection. When the connection + * succeeds, "bmp" is set up. + */ + +PREDECL_SORTLIST_UNIQ(bmp_actives) + +#define BMP_DFLT_MINRETRY 30000 +#define BMP_DFLT_MAXRETRY 720000 + +struct bmp_active { + struct bmp_actives_item bai; + struct bmp_targets *targets; + struct bmp *bmp; + + char *hostname; + int port; + unsigned minretry, maxretry; + + struct resolver_query resq; + + unsigned curretry; + unsigned addrpos, addrtotal; + union sockunion addrs[8]; + int socket; + const char *last_err; + struct thread *t_timer, *t_read, *t_write; +}; + +/* config & state for passive / listening sockets */ +PREDECL_SORTLIST_UNIQ(bmp_listeners) + +struct bmp_listener { + struct bmp_listeners_item bli; + + struct bmp_targets *targets; + + union sockunion addr; + int port; + + struct thread *t_accept; + int sock; +}; + +/* bmp_targets - plural since it may contain multiple bmp_listener & + * bmp_active items. If they have the same config, BMP session should be + * put in the same targets since that's a bit more effective. + */ +PREDECL_SORTLIST_UNIQ(bmp_targets) + +struct bmp_targets { + struct bmp_targets_item bti; + + struct bmp_bgp *bmpbgp; + struct bgp *bgp; + char *name; + + struct bmp_listeners_head listeners; + + char *acl_name; + char *acl6_name; +#define BMP_STAT_DEFAULT_TIMER 60000 + int stat_msec; + + /* only supporting: + * - IPv4 / unicast & multicast + * - IPv6 / unicast & multicast + * - L2VPN / EVPN + */ +#define BMP_MON_PREPOLICY (1 << 0) +#define BMP_MON_POSTPOLICY (1 << 1) + uint8_t afimon[AFI_MAX][SAFI_MAX]; + bool mirror; + + struct bmp_actives_head actives; + + struct thread *t_stats; + struct bmp_session_head sessions; + + struct bmp_qhash_head updhash; + struct bmp_qlist_head updlist; + + uint64_t cnt_accept, cnt_aclrefused; + + QOBJ_FIELDS +}; +DECLARE_QOBJ_TYPE(bmp_targets) + +/* per struct peer * data. Lookup by peer->qobj_node.nid, created on demand, + * deleted in peer_backward hook. */ +PREDECL_HASH(bmp_peerh) + +struct bmp_bgp_peer { + struct bmp_peerh_item bpi; + + uint64_t peerid; + /* struct peer *peer; */ + + uint8_t *open_rx; + size_t open_rx_len; + + uint8_t *open_tx; + size_t open_tx_len; +}; + +/* per struct bgp * data */ +PREDECL_HASH(bmp_bgph) + +#define BMP_PEER_DOWN_NO_RELEVANT_EVENT_CODE 0x00 + +struct bmp_bgp { + struct bmp_bgph_item bbi; + + struct bgp *bgp; + struct bmp_targets_head targets; + + struct bmp_mirrorq_head mirrorq; + size_t mirror_qsize, mirror_qsizemax; + + size_t mirror_qsizelimit; +}; + +enum { + BMP_PEERDOWN_LOCAL_NOTIFY = 1, + BMP_PEERDOWN_LOCAL_FSM = 2, + BMP_PEERDOWN_REMOTE_NOTIFY = 3, + BMP_PEERDOWN_REMOTE_CLOSE = 4, + BMP_PEERDOWN_ENDMONITOR = 5, +}; + +enum { + BMP_STATS_PFX_REJECTED = 0, + BMP_STATS_PFX_DUP_ADV = 1, + BMP_STATS_PFX_DUP_WITHDRAW = 2, + BMP_STATS_UPD_LOOP_CLUSTER = 3, + BMP_STATS_UPD_LOOP_ASPATH = 4, + BMP_STATS_UPD_LOOP_ORIGINATOR = 5, + BMP_STATS_UPD_LOOP_CONFED = 6, + BMP_STATS_SIZE_ADJ_RIB_IN = 7, + BMP_STATS_SIZE_LOC_RIB = 8, + BMP_STATS_SIZE_ADJ_RIB_IN_SAFI = 9, + BMP_STATS_SIZE_LOC_RIB_IN_SAFI = 10, + BMP_STATS_UPD_7606_WITHDRAW = 11, + BMP_STATS_PFX_7606_WITHDRAW = 12, + BMP_STATS_UPD_DUP = 13, + BMP_STATS_FRR_NH_INVALID = 65531, +}; + +DECLARE_MGROUP(BMP) + +#endif /*_BGP_BMP_H_*/ diff --git a/bgpd/bgp_btoa.c b/bgpd/bgp_btoa.c index cc37e352ef..cbe18e23cb 100644 --- a/bgpd/bgp_btoa.c +++ b/bgpd/bgp_btoa.c @@ -68,7 +68,7 @@ enum MRT_MSG_TYPES { MSG_TABLE_DUMP /* routing table dump */ }; -static int attr_parse(struct stream *s, uint16_t len) +static void attr_parse(struct stream *s, uint16_t len) { unsigned int flag; unsigned int type; @@ -115,8 +115,6 @@ static int attr_parse(struct stream *s, uint16_t len) break; } } - - return 0; } int main(int argc, char **argv) diff --git a/bgpd/bgp_clist.c b/bgpd/bgp_clist.c index 7b64f349d2..6ac6cf56dd 100644 --- a/bgpd/bgp_clist.c +++ b/bgpd/bgp_clist.c @@ -27,6 +27,7 @@ #include "filter.h" #include "stream.h" #include "jhash.h" +#include "frrstr.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_community.h" @@ -36,9 +37,40 @@ #include "bgpd/bgp_regex.h" #include "bgpd/bgp_clist.h" -static uint32_t bgp_clist_hash_key_community_list(void *data) +/* Calculate new sequential number. */ +static int64_t bgp_clist_new_seq_get(struct community_list *list) { - struct community_list *cl = data; + int64_t maxseq; + int64_t newseq; + struct community_entry *entry; + + maxseq = 0; + + for (entry = list->head; entry; entry = entry->next) { + if (maxseq < entry->seq) + maxseq = entry->seq; + } + + newseq = ((maxseq / 5) * 5) + 5; + + return (newseq > UINT_MAX) ? UINT_MAX : newseq; +} + +/* Return community-list entry which has same seq number. */ +static struct community_entry *bgp_clist_seq_check(struct community_list *list, + int64_t seq) +{ + struct community_entry *entry; + + for (entry = list->head; entry; entry = entry->next) + if (entry->seq == seq) + return entry; + return NULL; +} + +static uint32_t bgp_clist_hash_key_community_list(const void *data) +{ + struct community_list *cl = (struct community_list *) data; if (cl->name_hash) return cl->name_hash; @@ -156,7 +188,7 @@ community_list_insert(struct community_list_handler *ch, const char *name, /* If name is made by all digit character. We treat it as number. */ for (number = 0, i = 0; i < strlen(name); i++) { - if (isdigit((int)name[i])) + if (isdigit((unsigned char)name[i])) number = (number * 10) + (name[i] - '0'); else break; @@ -279,23 +311,9 @@ static void community_list_delete(struct community_list_master *cm, community_list_free(list); } -static int community_list_empty_p(struct community_list *list) +static bool community_list_empty_p(struct community_list *list) { - return (list->head == NULL && list->tail == NULL) ? 1 : 0; -} - -/* Add community-list entry to the list. */ -static void community_list_entry_add(struct community_list *list, - struct community_entry *entry) -{ - entry->next = NULL; - entry->prev = list->tail; - - if (list->tail) - list->tail->next = entry; - else - list->head = entry; - list->tail = entry; + return list->head == NULL && list->tail == NULL; } /* Delete community-list entry from the list. */ @@ -319,6 +337,83 @@ static void community_list_entry_delete(struct community_list_master *cm, community_list_delete(cm, list); } +/* + * Replace community-list entry in the list. Note that entry is the new one + * and replace is one one being replaced. + */ +static void community_list_entry_replace(struct community_list *list, + struct community_entry *replace, + struct community_entry *entry) +{ + if (replace->next) { + entry->next = replace->next; + replace->next->prev = entry; + } else { + entry->next = NULL; + list->tail = entry; + } + + if (replace->prev) { + entry->prev = replace->prev; + replace->prev->next = entry; + } else { + entry->prev = NULL; + list->head = entry; + } + + community_entry_free(replace); +} + +/* Add community-list entry to the list. */ +static void community_list_entry_add(struct community_list *list, + struct community_entry *entry, + struct community_list_handler *ch, + int master) +{ + struct community_entry *replace; + struct community_entry *point; + + /* Automatic assignment of seq no. */ + if (entry->seq == COMMUNITY_SEQ_NUMBER_AUTO) + entry->seq = bgp_clist_new_seq_get(list); + + if (list->tail && entry->seq > list->tail->seq) + point = NULL; + else { + replace = bgp_clist_seq_check(list, entry->seq); + if (replace) { + community_list_entry_replace(list, replace, entry); + return; + } + + /* Check insert point. */ + for (point = list->head; point; point = point->next) + if (point->seq >= entry->seq) + break; + } + + /* In case of this is the first element of the list. */ + entry->next = point; + + if (point) { + if (point->prev) + point->prev->next = entry; + else + list->head = entry; + + entry->prev = point->prev; + point->prev = entry; + } else { + if (list->tail) + list->tail->next = entry; + else + list->head = entry; + + entry->prev = list->tail; + list->tail = entry; + } +} + /* Lookup community-list entry from the list. */ static struct community_entry * community_list_entry_lookup(struct community_list *list, const void *arg, @@ -428,7 +523,7 @@ static char *community_str_get(struct community *com, int i) /* Internal function to perform regular expression match for * a single community. */ -static int community_regexp_include(regex_t *reg, struct community *com, int i) +static bool community_regexp_include(regex_t *reg, struct community *com, int i) { char *str; int rv; @@ -445,16 +540,12 @@ static int community_regexp_include(regex_t *reg, struct community *com, int i) XFREE(MTYPE_COMMUNITY_STR, str); - if (rv == 0) - return 1; - - /* No match. */ - return 0; + return rv == 0; } /* Internal function to perform regular expression match for community attribute. */ -static int community_regexp_match(struct community *com, regex_t *reg) +static bool community_regexp_match(struct community *com, regex_t *reg) { const char *str; @@ -467,10 +558,10 @@ static int community_regexp_match(struct community *com, regex_t *reg) /* Regular expression match. */ if (regexec(reg, str, 0, NULL, 0) == 0) - return 1; + return true; /* No match. */ - return 0; + return false; } static char *lcommunity_str_get(struct lcommunity *lcom, int i) @@ -480,33 +571,29 @@ static char *lcommunity_str_get(struct lcommunity *lcom, int i) uint32_t localdata1; uint32_t localdata2; char *str; - uint8_t *ptr; - char *pnt; + const uint8_t *ptr; ptr = lcom->val + (i * LCOMMUNITY_SIZE); memcpy(&lcomval, ptr, LCOMMUNITY_SIZE); /* Allocate memory. 48 bytes taken off bgp_lcommunity.c */ - str = pnt = XMALLOC(MTYPE_LCOMMUNITY_STR, 48); - ptr = (uint8_t *)lcomval.val; ptr = ptr_get_be32(ptr, &globaladmin); ptr = ptr_get_be32(ptr, &localdata1); ptr = ptr_get_be32(ptr, &localdata2); (void)ptr; /* consume value */ - sprintf(pnt, "%u:%u:%u", globaladmin, localdata1, localdata2); - pnt += strlen(pnt); - *pnt = '\0'; + str = XMALLOC(MTYPE_LCOMMUNITY_STR, 48); + snprintf(str, 48, "%u:%u:%u", globaladmin, localdata1, localdata2); return str; } /* Internal function to perform regular expression match for * a single community. */ -static int lcommunity_regexp_include(regex_t *reg, struct lcommunity *lcom, - int i) +static bool lcommunity_regexp_include(regex_t *reg, struct lcommunity *lcom, + int i) { char *str; @@ -520,15 +607,15 @@ static int lcommunity_regexp_include(regex_t *reg, struct lcommunity *lcom, /* Regular expression match. */ if (regexec(reg, str, 0, NULL, 0) == 0) { XFREE(MTYPE_LCOMMUNITY_STR, str); - return 1; + return true; } XFREE(MTYPE_LCOMMUNITY_STR, str); /* No match. */ - return 0; + return false; } -static int lcommunity_regexp_match(struct lcommunity *com, regex_t *reg) +static bool lcommunity_regexp_match(struct lcommunity *com, regex_t *reg) { const char *str; @@ -541,14 +628,14 @@ static int lcommunity_regexp_match(struct lcommunity *com, regex_t *reg) /* Regular expression match. */ if (regexec(reg, str, 0, NULL, 0) == 0) - return 1; + return true; /* No match. */ - return 0; + return false; } -static int ecommunity_regexp_match(struct ecommunity *ecom, regex_t *reg) +static bool ecommunity_regexp_match(struct ecommunity *ecom, regex_t *reg) { const char *str; @@ -561,10 +648,10 @@ static int ecommunity_regexp_match(struct ecommunity *ecom, regex_t *reg) /* Regular expression match. */ if (regexec(reg, str, 0, NULL, 0) == 0) - return 1; + return true; /* No match. */ - return 0; + return false; } #if 0 @@ -585,7 +672,7 @@ community_regexp_delete (struct community *com, regex_t * reg) i = 0; while (i < com->size) { - memcpy (&comval, com_nthval (com, i), sizeof (uint32_t)); + memcpy (&comval, com_nthval (com, i), sizeof(uint32_t)); comval = ntohl (comval); switch (comval) { @@ -649,99 +736,113 @@ community_regexp_delete (struct community *com, regex_t * reg) /* When given community attribute matches to the community-list return 1 else return 0. */ -int community_list_match(struct community *com, struct community_list *list) +bool community_list_match(struct community *com, struct community_list *list) { struct community_entry *entry; for (entry = list->head; entry; entry = entry->next) { if (entry->any) - return entry->direct == COMMUNITY_PERMIT ? 1 : 0; + return entry->direct == COMMUNITY_PERMIT; if (entry->style == COMMUNITY_LIST_STANDARD) { if (community_include(entry->u.com, COMMUNITY_INTERNET)) - return entry->direct == COMMUNITY_PERMIT ? 1 - : 0; + return entry->direct == COMMUNITY_PERMIT; if (community_match(com, entry->u.com)) - return entry->direct == COMMUNITY_PERMIT ? 1 - : 0; + return entry->direct == COMMUNITY_PERMIT; } else if (entry->style == COMMUNITY_LIST_EXPANDED) { if (community_regexp_match(com, entry->reg)) - return entry->direct == COMMUNITY_PERMIT ? 1 - : 0; + return entry->direct == COMMUNITY_PERMIT; } } - return 0; + return false; } -int lcommunity_list_match(struct lcommunity *lcom, struct community_list *list) +bool lcommunity_list_match(struct lcommunity *lcom, struct community_list *list) { struct community_entry *entry; for (entry = list->head; entry; entry = entry->next) { if (entry->any) - return entry->direct == COMMUNITY_PERMIT ? 1 : 0; + return entry->direct == COMMUNITY_PERMIT; if (entry->style == LARGE_COMMUNITY_LIST_STANDARD) { if (lcommunity_match(lcom, entry->u.lcom)) - return entry->direct == COMMUNITY_PERMIT ? 1 - : 0; + return entry->direct == COMMUNITY_PERMIT; } else if (entry->style == LARGE_COMMUNITY_LIST_EXPANDED) { if (lcommunity_regexp_match(lcom, entry->reg)) - return entry->direct == COMMUNITY_PERMIT ? 1 - : 0; + return entry->direct == COMMUNITY_PERMIT; } } - return 0; + return false; +} + + +/* Perform exact matching. In case of expanded large-community-list, do + * same thing as lcommunity_list_match(). + */ +bool lcommunity_list_exact_match(struct lcommunity *lcom, + struct community_list *list) +{ + struct community_entry *entry; + + for (entry = list->head; entry; entry = entry->next) { + if (entry->any) + return entry->direct == COMMUNITY_PERMIT; + + if (entry->style == LARGE_COMMUNITY_LIST_STANDARD) { + if (lcommunity_cmp(lcom, entry->u.com)) + return entry->direct == COMMUNITY_PERMIT; + } else if (entry->style == LARGE_COMMUNITY_LIST_EXPANDED) { + if (lcommunity_regexp_match(lcom, entry->reg)) + return entry->direct == COMMUNITY_PERMIT; + } + } + return false; } -int ecommunity_list_match(struct ecommunity *ecom, struct community_list *list) +bool ecommunity_list_match(struct ecommunity *ecom, struct community_list *list) { struct community_entry *entry; for (entry = list->head; entry; entry = entry->next) { if (entry->any) - return entry->direct == COMMUNITY_PERMIT ? 1 : 0; + return entry->direct == COMMUNITY_PERMIT; if (entry->style == EXTCOMMUNITY_LIST_STANDARD) { if (ecommunity_match(ecom, entry->u.ecom)) - return entry->direct == COMMUNITY_PERMIT ? 1 - : 0; + return entry->direct == COMMUNITY_PERMIT; } else if (entry->style == EXTCOMMUNITY_LIST_EXPANDED) { if (ecommunity_regexp_match(ecom, entry->reg)) - return entry->direct == COMMUNITY_PERMIT ? 1 - : 0; + return entry->direct == COMMUNITY_PERMIT; } } - return 0; + return false; } /* Perform exact matching. In case of expanded community-list, do same thing as community_list_match(). */ -int community_list_exact_match(struct community *com, - struct community_list *list) +bool community_list_exact_match(struct community *com, + struct community_list *list) { struct community_entry *entry; for (entry = list->head; entry; entry = entry->next) { if (entry->any) - return entry->direct == COMMUNITY_PERMIT ? 1 : 0; + return entry->direct == COMMUNITY_PERMIT; if (entry->style == COMMUNITY_LIST_STANDARD) { if (community_include(entry->u.com, COMMUNITY_INTERNET)) - return entry->direct == COMMUNITY_PERMIT ? 1 - : 0; + return entry->direct == COMMUNITY_PERMIT; if (community_cmp(com, entry->u.com)) - return entry->direct == COMMUNITY_PERMIT ? 1 - : 0; + return entry->direct == COMMUNITY_PERMIT; } else if (entry->style == COMMUNITY_LIST_EXPANDED) { if (community_regexp_match(com, entry->reg)) - return entry->direct == COMMUNITY_PERMIT ? 1 - : 0; + return entry->direct == COMMUNITY_PERMIT; } } - return 0; + return false; } /* Delete all permitted communities in the list from com. */ @@ -796,6 +897,7 @@ struct community *community_list_match_delete(struct community *com, /* Delete all of the communities we flagged for deletion */ for (i = delete_index - 1; i >= 0; i--) { val = community_val_get(com, com_index_to_delete[i]); + val = htonl(val); community_del_val(com, &val); } @@ -804,8 +906,8 @@ struct community *community_list_match_delete(struct community *com, /* To avoid duplicated entry in the community-list, this function compares specified entry to existing entry. */ -static int community_list_dup_check(struct community_list *list, - struct community_entry *new) +static bool community_list_dup_check(struct community_list *list, + struct community_entry *new) { struct community_entry *entry; @@ -820,42 +922,46 @@ static int community_list_dup_check(struct community_list *list, continue; if (entry->any) - return 1; + return true; switch (entry->style) { case COMMUNITY_LIST_STANDARD: if (community_cmp(entry->u.com, new->u.com)) - return 1; + return true; break; case LARGE_COMMUNITY_LIST_STANDARD: if (lcommunity_cmp(entry->u.lcom, new->u.lcom)) - return 1; + return true; break; case EXTCOMMUNITY_LIST_STANDARD: if (ecommunity_cmp(entry->u.ecom, new->u.ecom)) - return 1; + return true; break; case COMMUNITY_LIST_EXPANDED: case EXTCOMMUNITY_LIST_EXPANDED: case LARGE_COMMUNITY_LIST_EXPANDED: if (strcmp(entry->config, new->config) == 0) - return 1; + return true; break; default: break; } } - return 0; + return false; } /* Set community-list. */ int community_list_set(struct community_list_handler *ch, const char *name, - const char *str, int direct, int style) + const char *str, const char *seq, int direct, int style) { struct community_entry *entry = NULL; struct community_list *list; struct community *com = NULL; regex_t *regex = NULL; + int64_t seqnum = COMMUNITY_SEQ_NUMBER_AUTO; + + if (seq) + seqnum = (int64_t)atol(seq); /* Get community list. */ list = community_list_get(ch, name, COMMUNITY_LIST_MASTER); @@ -888,9 +994,10 @@ int community_list_set(struct community_list_handler *ch, const char *name, entry = community_entry_new(); entry->direct = direct; entry->style = style; - entry->any = (str ? 0 : 1); + entry->any = (str ? false : true); entry->u.com = com; entry->reg = regex; + entry->seq = seqnum; entry->config = (regex ? XSTRDUP(MTYPE_COMMUNITY_LIST_CONFIG, str) : NULL); @@ -898,7 +1005,8 @@ int community_list_set(struct community_list_handler *ch, const char *name, if (community_list_dup_check(list, entry)) community_entry_free(entry); else { - community_list_entry_add(list, entry); + community_list_entry_add(list, entry, ch, + COMMUNITY_LIST_MASTER); route_map_notify_dependencies(name, RMAP_EVENT_CLIST_ADDED); } @@ -907,7 +1015,8 @@ int community_list_set(struct community_list_handler *ch, const char *name, /* Unset community-list */ int community_list_unset(struct community_list_handler *ch, const char *name, - const char *str, int direct, int style) + const char *str, const char *seq, int direct, + int style) { struct community_list_master *cm = NULL; struct community_entry *entry = NULL; @@ -1000,14 +1109,74 @@ struct lcommunity *lcommunity_list_match_delete(struct lcommunity *lcom, return lcom; } +/* Helper to check if every octet do not exceed UINT_MAX */ +bool lcommunity_list_valid(const char *community, int style) +{ + int octets; + char **splits, **communities; + char *endptr; + int num, num_communities; + regex_t *regres; + int invalid = 0; + + frrstr_split(community, " ", &communities, &num_communities); + + for (int j = 0; j < num_communities; j++) { + octets = 0; + frrstr_split(communities[j], ":", &splits, &num); + + for (int i = 0; i < num; i++) { + if (strlen(splits[i]) == 0) + /* There is no digit to check */ + invalid++; + + if (style == LARGE_COMMUNITY_LIST_STANDARD) { + if (*splits[i] == '-') + /* Must not be negative */ + invalid++; + else if (strtoul(splits[i], &endptr, 10) + > UINT_MAX) + /* Larger than 4 octets */ + invalid++; + else if (*endptr) + /* Not all characters were digits */ + invalid++; + } else { + regres = bgp_regcomp(communities[j]); + if (!regres) + /* malformed regex */ + invalid++; + else + bgp_regex_free(regres); + } + + octets++; + XFREE(MTYPE_TMP, splits[i]); + } + XFREE(MTYPE_TMP, splits); + + if (octets != 3) + invalid++; + + XFREE(MTYPE_TMP, communities[j]); + } + XFREE(MTYPE_TMP, communities); + + return (invalid > 0) ? false : true; +} + /* Set lcommunity-list. */ int lcommunity_list_set(struct community_list_handler *ch, const char *name, - const char *str, int direct, int style) + const char *str, const char *seq, int direct, int style) { struct community_entry *entry = NULL; struct community_list *list; struct lcommunity *lcom = NULL; regex_t *regex = NULL; + int64_t seqnum = COMMUNITY_SEQ_NUMBER_AUTO; + + if (seq) + seqnum = (int64_t)atol(seq); /* Get community list. */ list = community_list_get(ch, name, LARGE_COMMUNITY_LIST_MASTER); @@ -1028,6 +1197,9 @@ int lcommunity_list_set(struct community_list_handler *ch, const char *name, } if (str) { + if (!lcommunity_list_valid(str, style)) + return COMMUNITY_LIST_ERR_MALFORMED_VAL; + if (style == LARGE_COMMUNITY_LIST_STANDARD) lcom = lcommunity_str2com(str); else @@ -1040,17 +1212,21 @@ int lcommunity_list_set(struct community_list_handler *ch, const char *name, entry = community_entry_new(); entry->direct = direct; entry->style = style; - entry->any = (str ? 0 : 1); + entry->any = (str ? false : true); entry->u.lcom = lcom; entry->reg = regex; + entry->seq = seqnum; entry->config = (regex ? XSTRDUP(MTYPE_COMMUNITY_LIST_CONFIG, str) : NULL); /* Do not put duplicated community entry. */ if (community_list_dup_check(list, entry)) community_entry_free(entry); - else - community_list_entry_add(list, entry); + else { + community_list_entry_add(list, entry, ch, + LARGE_COMMUNITY_LIST_MASTER); + route_map_notify_dependencies(name, RMAP_EVENT_LLIST_ADDED); + } return 0; } @@ -1058,7 +1234,8 @@ int lcommunity_list_set(struct community_list_handler *ch, const char *name, /* Unset community-list. When str is NULL, delete all of community-list entry belongs to the specified name. */ int lcommunity_list_unset(struct community_list_handler *ch, const char *name, - const char *str, int direct, int style) + const char *str, const char *seq, int direct, + int style) { struct community_list_master *cm = NULL; struct community_entry *entry = NULL; @@ -1075,6 +1252,7 @@ int lcommunity_list_unset(struct community_list_handler *ch, const char *name, /* Delete all of entry belongs to this community-list. */ if (!str) { community_list_delete(cm, list); + route_map_notify_dependencies(name, RMAP_EVENT_LLIST_DELETED); return 0; } @@ -1100,18 +1278,24 @@ int lcommunity_list_unset(struct community_list_handler *ch, const char *name, return COMMUNITY_LIST_ERR_CANT_FIND_LIST; community_list_entry_delete(cm, list, entry); + route_map_notify_dependencies(name, RMAP_EVENT_LLIST_DELETED); return 0; } /* Set extcommunity-list. */ int extcommunity_list_set(struct community_list_handler *ch, const char *name, - const char *str, int direct, int style) + const char *str, const char *seq, int direct, + int style) { struct community_entry *entry = NULL; struct community_list *list; struct ecommunity *ecom = NULL; regex_t *regex = NULL; + int64_t seqnum = COMMUNITY_SEQ_NUMBER_AUTO; + + if (seq) + seqnum = (int64_t)atol(seq); if (str == NULL) return COMMUNITY_LIST_ERR_MALFORMED_VAL; @@ -1149,7 +1333,7 @@ int extcommunity_list_set(struct community_list_handler *ch, const char *name, entry = community_entry_new(); entry->direct = direct; entry->style = style; - entry->any = 0; + entry->any = false; if (ecom) entry->config = ecommunity_ecom2str( ecom, ECOMMUNITY_FORMAT_COMMUNITY_LIST, 0); @@ -1158,12 +1342,14 @@ int extcommunity_list_set(struct community_list_handler *ch, const char *name, entry->u.ecom = ecom; entry->reg = regex; + entry->seq = seqnum; /* Do not put duplicated community entry. */ if (community_list_dup_check(list, entry)) community_entry_free(entry); else { - community_list_entry_add(list, entry); + community_list_entry_add(list, entry, ch, + EXTCOMMUNITY_LIST_MASTER); route_map_notify_dependencies(name, RMAP_EVENT_ECLIST_ADDED); } @@ -1176,7 +1362,8 @@ int extcommunity_list_set(struct community_list_handler *ch, const char *name, * specified name. */ int extcommunity_list_unset(struct community_list_handler *ch, const char *name, - const char *str, int direct, int style) + const char *str, const char *seq, int direct, + int style) { struct community_list_master *cm = NULL; struct community_entry *entry = NULL; diff --git a/bgpd/bgp_clist.h b/bgpd/bgp_clist.h index 75a31611ba..632e1730cc 100644 --- a/bgpd/bgp_clist.h +++ b/bgpd/bgp_clist.h @@ -36,6 +36,8 @@ #define COMMUNITY_LIST_STRING 0 #define COMMUNITY_LIST_NUMBER 1 +#define COMMUNITY_SEQ_NUMBER_AUTO -1 + /* Community-list entry types. */ #define COMMUNITY_LIST_STANDARD 0 /* Standard community-list. */ #define COMMUNITY_LIST_EXPANDED 1 /* Expanded community-list. */ @@ -79,7 +81,10 @@ struct community_entry { uint8_t style; /* Any match. */ - uint8_t any; + bool any; + + /* Sequence number. */ + int64_t seq; /* Community structure. */ union { @@ -135,23 +140,24 @@ extern struct community_list_handler *community_list_init(void); extern void community_list_terminate(struct community_list_handler *); extern int community_list_set(struct community_list_handler *ch, - const char *name, const char *str, int direct, - int style); + const char *name, const char *str, + const char *seq, int direct, int style); extern int community_list_unset(struct community_list_handler *ch, - const char *name, const char *str, int direct, - int style); + const char *name, const char *str, + const char *seq, int direct, int style); extern int extcommunity_list_set(struct community_list_handler *ch, - const char *name, const char *str, int direct, - int style); + const char *name, const char *str, + const char *seq, int direct, int style); extern int extcommunity_list_unset(struct community_list_handler *ch, const char *name, const char *str, - int direct, int style); + const char *seq, int direct, int style); extern int lcommunity_list_set(struct community_list_handler *ch, - const char *name, const char *str, int direct, - int style); + const char *name, const char *str, + const char *seq, int direct, int style); +extern bool lcommunity_list_valid(const char *community, int style); extern int lcommunity_list_unset(struct community_list_handler *ch, - const char *name, const char *str, int direct, - int style); + const char *name, const char *str, + const char *seq, int direct, int style); extern struct community_list_master * community_list_master_lookup(struct community_list_handler *, int); @@ -160,11 +166,13 @@ extern struct community_list * community_list_lookup(struct community_list_handler *c, const char *name, uint32_t name_hash, int master); -extern int community_list_match(struct community *, struct community_list *); -extern int ecommunity_list_match(struct ecommunity *, struct community_list *); -extern int lcommunity_list_match(struct lcommunity *, struct community_list *); -extern int community_list_exact_match(struct community *, - struct community_list *); +extern bool community_list_match(struct community *, struct community_list *); +extern bool ecommunity_list_match(struct ecommunity *, struct community_list *); +extern bool lcommunity_list_match(struct lcommunity *, struct community_list *); +extern bool community_list_exact_match(struct community *, + struct community_list *); +extern bool lcommunity_list_exact_match(struct lcommunity *lcom, + struct community_list *list); extern struct community *community_list_match_delete(struct community *, struct community_list *); extern struct lcommunity * diff --git a/bgpd/bgp_community.c b/bgpd/bgp_community.c index 67cd2be214..f722a8dbc7 100644 --- a/bgpd/bgp_community.c +++ b/bgpd/bgp_community.c @@ -24,6 +24,7 @@ #include "hash.h" #include "memory.h" #include "jhash.h" +#include "frrstr.h" #include "bgpd/bgp_memory.h" #include "bgpd/bgp_community.h" @@ -40,6 +41,9 @@ static struct community *community_new(void) /* Free communities value. */ void community_free(struct community **com) { + if (!(*com)) + return; + XFREE(MTYPE_COMMUNITY_VAL, (*com)->val); XFREE(MTYPE_COMMUNITY_STR, (*com)->str); @@ -89,7 +93,6 @@ void community_del_val(struct community *com, uint32_t *val) com->val, com_length(com)); else { XFREE(MTYPE_COMMUNITY_VAL, com->val); - com->val = NULL; } return; } @@ -129,7 +132,7 @@ static int community_compare(const void *a1, const void *a2) return 0; } -int community_include(struct community *com, uint32_t val) +bool community_include(struct community *com, uint32_t val) { int i; @@ -137,9 +140,8 @@ int community_include(struct community *com, uint32_t val) for (i = 0; i < com->size; i++) if (memcmp(&val, com_nthval(com, i), sizeof(uint32_t)) == 0) - return 1; - - return 0; + return true; + return false; } uint32_t community_val_get(struct community *com, int i) @@ -148,7 +150,7 @@ uint32_t community_val_get(struct community *com, int i) uint32_t val; p = (uint8_t *)com->val; - p += (i * 4); + p += (i * COMMUNITY_SIZE); memcpy(&val, p, sizeof(uint32_t)); @@ -205,7 +207,6 @@ static void set_community_string(struct community *com, bool make_json) { int i; char *str; - char *pnt; int len; int first; uint32_t comval; @@ -297,7 +298,7 @@ static void set_community_string(struct community *com, bool make_json) } /* Allocate memory. */ - str = pnt = XMALLOC(MTYPE_COMMUNITY_STR, len); + str = XCALLOC(MTYPE_COMMUNITY_STR, len); first = 1; /* Fill in string. */ @@ -308,12 +309,11 @@ static void set_community_string(struct community *com, bool make_json) if (first) first = 0; else - *pnt++ = ' '; + strlcat(str, " ", len); switch (comval) { case COMMUNITY_INTERNET: - strcpy(pnt, "internet"); - pnt += strlen("internet"); + strlcat(str, "internet", len); if (make_json) { json_string = json_object_new_string("internet"); @@ -322,8 +322,7 @@ static void set_community_string(struct community *com, bool make_json) } break; case COMMUNITY_GSHUT: - strcpy(pnt, "graceful-shutdown"); - pnt += strlen("graceful-shutdown"); + strlcat(str, "graceful-shutdown", len); if (make_json) { json_string = json_object_new_string( "gracefulShutdown"); @@ -332,8 +331,7 @@ static void set_community_string(struct community *com, bool make_json) } break; case COMMUNITY_ACCEPT_OWN: - strcpy(pnt, "accept-own"); - pnt += strlen("accept-own"); + strlcat(str, "accept-own", len); if (make_json) { json_string = json_object_new_string( "acceptown"); @@ -342,8 +340,7 @@ static void set_community_string(struct community *com, bool make_json) } break; case COMMUNITY_ROUTE_FILTER_TRANSLATED_v4: - strcpy(pnt, "route-filter-translated-v4"); - pnt += strlen("route-filter-translated-v4"); + strlcat(str, "route-filter-translated-v4", len); if (make_json) { json_string = json_object_new_string( "routeFilterTranslatedV4"); @@ -352,8 +349,7 @@ static void set_community_string(struct community *com, bool make_json) } break; case COMMUNITY_ROUTE_FILTER_v4: - strcpy(pnt, "route-filter-v4"); - pnt += strlen("route-filter-v4"); + strlcat(str, "route-filter-v4", len); if (make_json) { json_string = json_object_new_string( "routeFilterV4"); @@ -362,8 +358,7 @@ static void set_community_string(struct community *com, bool make_json) } break; case COMMUNITY_ROUTE_FILTER_TRANSLATED_v6: - strcpy(pnt, "route-filter-translated-v6"); - pnt += strlen("route-filter-translated-v6"); + strlcat(str, "route-filter-translated-v6", len); if (make_json) { json_string = json_object_new_string( "routeFilterTranslatedV6"); @@ -372,8 +367,7 @@ static void set_community_string(struct community *com, bool make_json) } break; case COMMUNITY_ROUTE_FILTER_v6: - strcpy(pnt, "route-filter-v6"); - pnt += strlen("route-filter-v6"); + strlcat(str, "route-filter-v6", len); if (make_json) { json_string = json_object_new_string( "routeFilterV6"); @@ -382,8 +376,7 @@ static void set_community_string(struct community *com, bool make_json) } break; case COMMUNITY_LLGR_STALE: - strcpy(pnt, "llgr-stale"); - pnt += strlen("llgr-stale"); + strlcat(str, "llgr-stale", len); if (make_json) { json_string = json_object_new_string( "llgrStale"); @@ -392,8 +385,7 @@ static void set_community_string(struct community *com, bool make_json) } break; case COMMUNITY_NO_LLGR: - strcpy(pnt, "no-llgr"); - pnt += strlen("no-llgr"); + strlcat(str, "no-llgr", len); if (make_json) { json_string = json_object_new_string( "noLlgr"); @@ -402,8 +394,7 @@ static void set_community_string(struct community *com, bool make_json) } break; case COMMUNITY_ACCEPT_OWN_NEXTHOP: - strcpy(pnt, "accept-own-nexthop"); - pnt += strlen("accept-own-nexthop"); + strlcat(str, "accept-own-nexthop", len); if (make_json) { json_string = json_object_new_string( "acceptownnexthop"); @@ -412,8 +403,7 @@ static void set_community_string(struct community *com, bool make_json) } break; case COMMUNITY_BLACKHOLE: - strcpy(pnt, "blackhole"); - pnt += strlen("blackhole"); + strlcat(str, "blackhole", len); if (make_json) { json_string = json_object_new_string( "blackhole"); @@ -422,8 +412,7 @@ static void set_community_string(struct community *com, bool make_json) } break; case COMMUNITY_NO_EXPORT: - strcpy(pnt, "no-export"); - pnt += strlen("no-export"); + strlcat(str, "no-export", len); if (make_json) { json_string = json_object_new_string("noExport"); @@ -432,8 +421,7 @@ static void set_community_string(struct community *com, bool make_json) } break; case COMMUNITY_NO_ADVERTISE: - strcpy(pnt, "no-advertise"); - pnt += strlen("no-advertise"); + strlcat(str, "no-advertise", len); if (make_json) { json_string = json_object_new_string("noAdvertise"); @@ -442,8 +430,7 @@ static void set_community_string(struct community *com, bool make_json) } break; case COMMUNITY_LOCAL_AS: - strcpy(pnt, "local-AS"); - pnt += strlen("local-AS"); + strlcat(str, "local-AS", len); if (make_json) { json_string = json_object_new_string("localAs"); json_object_array_add(json_community_list, @@ -451,8 +438,7 @@ static void set_community_string(struct community *com, bool make_json) } break; case COMMUNITY_NO_PEER: - strcpy(pnt, "no-peer"); - pnt += strlen("no-peer"); + strlcat(str, "no-peer", len); if (make_json) { json_string = json_object_new_string("noPeer"); json_object_array_add(json_community_list, @@ -462,17 +448,17 @@ static void set_community_string(struct community *com, bool make_json) default: as = (comval >> 16) & 0xFFFF; val = comval & 0xFFFF; - sprintf(pnt, "%u:%d", as, val); + char buf[32]; + snprintf(buf, sizeof(buf), "%u:%d", as, val); + strlcat(str, buf, len); if (make_json) { - json_string = json_object_new_string(pnt); + json_string = json_object_new_string(buf); json_object_array_add(json_community_list, json_string); } - pnt += strlen(pnt); break; } } - *pnt = '\0'; if (make_json) { json_object_string_add(com->json, "string", str); @@ -532,11 +518,11 @@ struct community *community_parse(uint32_t *pnt, unsigned short length) struct community *new; /* If length is malformed return NULL. */ - if (length % 4) + if (length % COMMUNITY_SIZE) return NULL; /* Make temporary community for hash look up. */ - tmp.size = length / 4; + tmp.size = length / COMMUNITY_SIZE; tmp.val = pnt; new = community_uniq_sort(&tmp); @@ -551,8 +537,9 @@ struct community *community_dup(struct community *com) new = XCALLOC(MTYPE_COMMUNITY, sizeof(struct community)); new->size = com->size; if (new->size) { - new->val = XMALLOC(MTYPE_COMMUNITY_VAL, com->size * 4); - memcpy(new->val, com->val, com->size * 4); + new->val = XMALLOC(MTYPE_COMMUNITY_VAL, + com->size * COMMUNITY_SIZE); + memcpy(new->val, com->val, com->size * COMMUNITY_SIZE); } else new->val = NULL; return new; @@ -574,26 +561,26 @@ char *community_str(struct community *com, bool make_json) /* Make hash value of community attribute. This function is used by hash package.*/ -unsigned int community_hash_make(struct community *com) +unsigned int community_hash_make(const struct community *com) { - uint32_t *pnt = (uint32_t *)com->val; + uint32_t *pnt = com->val; return jhash2(pnt, com->size, 0x43ea96c1); } -int community_match(const struct community *com1, const struct community *com2) +bool community_match(const struct community *com1, const struct community *com2) { int i = 0; int j = 0; if (com1 == NULL && com2 == NULL) - return 1; + return true; if (com1 == NULL || com2 == NULL) - return 0; + return false; if (com1->size < com2->size) - return 0; + return false; /* Every community on com2 needs to be on com1 for this to match */ while (i < com1->size && j < com2->size) { @@ -603,9 +590,9 @@ int community_match(const struct community *com1, const struct community *com2) } if (j == com2->size) - return 1; + return true; else - return 0; + return false; } /* If two aspath have same value then return 1 else return 0. This @@ -618,7 +605,8 @@ bool community_cmp(const struct community *com1, const struct community *com2) return false; if (com1->size == com2->size) - if (memcmp(com1->val, com2->val, com1->size * 4) == 0) + if (memcmp(com1->val, com2->val, com1->size * COMMUNITY_SIZE) + == 0) return true; return false; } @@ -628,13 +616,14 @@ struct community *community_merge(struct community *com1, struct community *com2) { if (com1->val) - com1->val = XREALLOC(MTYPE_COMMUNITY_VAL, com1->val, - (com1->size + com2->size) * 4); + com1->val = + XREALLOC(MTYPE_COMMUNITY_VAL, com1->val, + (com1->size + com2->size) * COMMUNITY_SIZE); else com1->val = XMALLOC(MTYPE_COMMUNITY_VAL, - (com1->size + com2->size) * 4); + (com1->size + com2->size) * COMMUNITY_SIZE); - memcpy(com1->val + com1->size, com2->val, com2->size * 4); + memcpy(com1->val + com1->size, com2->val, com2->size * COMMUNITY_SIZE); com1->size += com2->size; return com1; @@ -660,6 +649,31 @@ enum community_token { community_token_unknown }; +/* Helper to check if a given community is valid */ +static bool community_valid(const char *community) +{ + int octets = 0; + char **splits; + int num; + int invalid = 0; + + frrstr_split(community, ":", &splits, &num); + + for (int i = 0; i < num; i++) { + if (strtoul(splits[i], NULL, 10) > UINT16_MAX) + invalid++; + + if (strlen(splits[i]) == 0) + invalid++; + + octets++; + XFREE(MTYPE_TMP, splits[i]); + } + XFREE(MTYPE_TMP, splits); + + return (octets < 2 || invalid) ? false : true; +} + /* Get next community token from string. */ static const char * community_gettoken(const char *buf, enum community_token *token, uint32_t *val) @@ -667,7 +681,7 @@ community_gettoken(const char *buf, enum community_token *token, uint32_t *val) const char *p = buf; /* Skip white space. */ - while (isspace((int)*p)) + while (isspace((unsigned char)*p)) p++; /* Check the end of the line. */ @@ -675,7 +689,7 @@ community_gettoken(const char *buf, enum community_token *token, uint32_t *val) return NULL; /* Well known community string check. */ - if (isalpha((int)*p)) { + if (isalpha((unsigned char)*p)) { if (strncmp(p, "internet", strlen("internet")) == 0) { *val = COMMUNITY_INTERNET; *token = community_token_no_export; @@ -689,6 +703,14 @@ community_gettoken(const char *buf, enum community_token *token, uint32_t *val) p += strlen("graceful-shutdown"); return p; } + if (strncmp(p, "accept-own-nexthop", + strlen("accept-own-nexthop")) + == 0) { + *val = COMMUNITY_ACCEPT_OWN_NEXTHOP; + *token = community_token_accept_own_nexthop; + p += strlen("accept-own-nexthop"); + return p; + } if (strncmp(p, "accept-own", strlen("accept-own")) == 0) { *val = COMMUNITY_ACCEPT_OWN; @@ -740,14 +762,6 @@ community_gettoken(const char *buf, enum community_token *token, uint32_t *val) p += strlen("no-llgr"); return p; } - if (strncmp(p, "accept-own-nexthop", - strlen("accept-own-nexthop")) - == 0) { - *val = COMMUNITY_ACCEPT_OWN_NEXTHOP; - *token = community_token_accept_own_nexthop; - p += strlen("accept-own-nexthop"); - return p; - } if (strncmp(p, "blackhole", strlen("blackhole")) == 0) { *val = COMMUNITY_BLACKHOLE; @@ -786,13 +800,18 @@ community_gettoken(const char *buf, enum community_token *token, uint32_t *val) } /* Community value. */ - if (isdigit((int)*p)) { + if (isdigit((unsigned char)*p)) { int separator = 0; int digit = 0; uint32_t community_low = 0; uint32_t community_high = 0; - while (isdigit((int)*p) || *p == ':') { + if (!community_valid(p)) { + *token = community_token_unknown; + return NULL; + } + + while (isdigit((unsigned char)*p) || *p == ':') { if (*p == ':') { if (separator) { *token = community_token_unknown; @@ -822,11 +841,6 @@ community_gettoken(const char *buf, enum community_token *token, uint32_t *val) return NULL; } - if (community_low > UINT16_MAX) { - *token = community_token_unknown; - return NULL; - } - *val = community_high + community_low; *token = community_token_val; return p; @@ -897,7 +911,7 @@ struct hash *community_hash(void) void community_init(void) { comhash = - hash_create((unsigned int (*)(void *))community_hash_make, + hash_create((unsigned int (*)(const void *))community_hash_make, (bool (*)(const void *, const void *))community_cmp, "BGP Community Hash"); } @@ -924,17 +938,15 @@ static void *bgp_aggr_communty_hash_alloc(void *p) return community; } -static void bgp_aggr_community_prepare(struct hash_backet *hb, void *arg) +static void bgp_aggr_community_prepare(struct hash_bucket *hb, void *arg) { - struct community *commerge = NULL; struct community *hb_community = hb->data; struct community **aggr_community = arg; - if (*aggr_community) { - commerge = community_merge(*aggr_community, hb_community); - *aggr_community = community_uniq_sort(commerge); - community_free(&commerge); - } else + if (*aggr_community) + *aggr_community = community_merge(*aggr_community, + hb_community); + else *aggr_community = community_dup(hb_community); } @@ -947,6 +959,14 @@ void bgp_aggr_community_remove(void *arg) void bgp_compute_aggregate_community(struct bgp_aggregate *aggregate, struct community *community) +{ + bgp_compute_aggregate_community_hash(aggregate, community); + bgp_compute_aggregate_community_val(aggregate); +} + + +void bgp_compute_aggregate_community_hash(struct bgp_aggregate *aggregate, + struct community *community) { struct community *aggr_community = NULL; @@ -957,7 +977,7 @@ void bgp_compute_aggregate_community(struct bgp_aggregate *aggregate, */ if (aggregate->community_hash == NULL) aggregate->community_hash = hash_create( - (unsigned int (*)(void *))community_hash_make, + (unsigned int (*)(const void *))community_hash_make, (bool (*)(const void *, const void *))community_cmp, "BGP Aggregator community hash"); @@ -967,32 +987,47 @@ void bgp_compute_aggregate_community(struct bgp_aggregate *aggregate, */ aggr_community = hash_get(aggregate->community_hash, community, bgp_aggr_communty_hash_alloc); + } - /* Re-compute aggregate's community. - */ - if (aggregate->community) - community_free(&aggregate->community); + /* Increment reference counter. + */ + aggr_community->refcnt++; +} +void bgp_compute_aggregate_community_val(struct bgp_aggregate *aggregate) +{ + struct community *commerge = NULL; + + if (aggregate == NULL) + return; + + /* Re-compute aggregate's community. + */ + if (aggregate->community) + community_free(&aggregate->community); + if (aggregate->community_hash && + aggregate->community_hash->count) { hash_iterate(aggregate->community_hash, bgp_aggr_community_prepare, &aggregate->community); + commerge = aggregate->community; + aggregate->community = community_uniq_sort(commerge); + if (commerge) + community_free(&commerge); } - - /* Increment refernce counter. - */ - aggr_community->refcnt++; } + + void bgp_remove_community_from_aggregate(struct bgp_aggregate *aggregate, struct community *community) { struct community *aggr_community = NULL; struct community *ret_comm = NULL; - if ((aggregate == NULL) || (community == NULL)) - return; - - if (aggregate->community_hash == NULL) + if ((!aggregate) + || (!aggregate->community_hash) + || (!community)) return; /* Look-up the community in the hash. @@ -1006,13 +1041,33 @@ void bgp_remove_community_from_aggregate(struct bgp_aggregate *aggregate, aggr_community); community_free(&ret_comm); - community_free(&aggregate->community); + bgp_compute_aggregate_community_val(aggregate); + } + } +} + +void bgp_remove_comm_from_aggregate_hash(struct bgp_aggregate *aggregate, + struct community *community) +{ + + struct community *aggr_community = NULL; + struct community *ret_comm = NULL; + + if ((!aggregate) + || (!aggregate->community_hash) + || (!community)) + return; - /* Compute aggregate's community. - */ - hash_iterate(aggregate->community_hash, - bgp_aggr_community_prepare, - &aggregate->community); + /* Look-up the community in the hash. + */ + aggr_community = bgp_aggr_community_lookup(aggregate, community); + if (aggr_community) { + aggr_community->refcnt--; + + if (aggr_community->refcnt == 0) { + ret_comm = hash_release(aggregate->community_hash, + aggr_community); + community_free(&ret_comm); } } } diff --git a/bgpd/bgp_community.h b/bgpd/bgp_community.h index 4ff4d214a5..b99f38ab64 100644 --- a/bgpd/bgp_community.h +++ b/bgpd/bgp_community.h @@ -61,8 +61,10 @@ struct community { #define COMMUNITY_LOCAL_AS 0xFFFFFF03 #define COMMUNITY_NO_PEER 0xFFFFFF04 +#define COMMUNITY_SIZE 4 + /* Macros of community attribute. */ -#define com_length(X) ((X)->size * 4) +#define com_length(X) ((X)->size * COMMUNITY_SIZE) #define com_lastval(X) ((X)->val + (X)->size - 1) #define com_nthval(X,n) ((X)->val + (n)) @@ -75,9 +77,9 @@ extern struct community *community_parse(uint32_t *, unsigned short); extern struct community *community_intern(struct community *); extern void community_unintern(struct community **); extern char *community_str(struct community *, bool make_json); -extern unsigned int community_hash_make(struct community *); +extern unsigned int community_hash_make(const struct community *); extern struct community *community_str2com(const char *); -extern int community_match(const struct community *, const struct community *); +extern bool community_match(const struct community *, const struct community *); extern bool community_cmp(const struct community *c1, const struct community *c2); extern struct community *community_merge(struct community *, @@ -85,15 +87,23 @@ extern struct community *community_merge(struct community *, extern struct community *community_delete(struct community *, struct community *); extern struct community *community_dup(struct community *); -extern int community_include(struct community *, uint32_t); +extern bool community_include(struct community *, uint32_t); extern void community_del_val(struct community *, uint32_t *); extern unsigned long community_count(void); extern struct hash *community_hash(void); extern uint32_t community_val_get(struct community *com, int i); extern void bgp_compute_aggregate_community(struct bgp_aggregate *aggregate, struct community *community); + +extern void bgp_compute_aggregate_community_val( + struct bgp_aggregate *aggregate); +extern void bgp_compute_aggregate_community_hash( + struct bgp_aggregate *aggregate, + struct community *community); extern void bgp_remove_community_from_aggregate(struct bgp_aggregate *aggregate, struct community *community); +extern void bgp_remove_comm_from_aggregate_hash(struct bgp_aggregate *aggregate, + struct community *community); extern void bgp_aggr_community_remove(void *arg); #endif /* _QUAGGA_BGP_COMMUNITY_H */ diff --git a/bgpd/bgp_damp.c b/bgpd/bgp_damp.c index cf085e46fb..565d0b8e19 100644 --- a/bgpd/bgp_damp.c +++ b/bgpd/bgp_damp.c @@ -37,8 +37,7 @@ #include "bgpd/bgp_advertise.h" /* Global variable to access damping configuration */ -struct bgp_damp_config bgp_damp_cfg; -static struct bgp_damp_config *damp = &bgp_damp_cfg; +static struct bgp_damp_config damp[AFI_MAX][SAFI_MAX]; /* Utility macro to add and delete BGP dampening information to no used list. */ @@ -46,49 +45,57 @@ static struct bgp_damp_config *damp = &bgp_damp_cfg; #define BGP_DAMP_LIST_DEL(N, A) BGP_PATH_INFO_DEL(N, A, no_reuse_list) /* Calculate reuse list index by penalty value. */ -static int bgp_reuse_index(int penalty) +static int bgp_reuse_index(int penalty, struct bgp_damp_config *bdc) { unsigned int i; int index; - i = (int)(((double)penalty / damp->reuse_limit - 1.0) - * damp->scale_factor); + /* + * reuse_limit can't be zero, this is for Coverity + * to bypass division by zero test. + */ + assert(bdc->reuse_limit); - if (i >= damp->reuse_index_size) - i = damp->reuse_index_size - 1; + i = (int)(((double)penalty / bdc->reuse_limit - 1.0) + * bdc->scale_factor); - index = damp->reuse_index[i] - damp->reuse_index[0]; + if (i >= bdc->reuse_index_size) + i = bdc->reuse_index_size - 1; - return (damp->reuse_offset + index) % damp->reuse_list_size; + index = bdc->reuse_index[i] - bdc->reuse_index[0]; + + return (bdc->reuse_offset + index) % bdc->reuse_list_size; } /* Add BGP dampening information to reuse list. */ -static void bgp_reuse_list_add(struct bgp_damp_info *bdi) +static void bgp_reuse_list_add(struct bgp_damp_info *bdi, + struct bgp_damp_config *bdc) { int index; - index = bdi->index = bgp_reuse_index(bdi->penalty); + index = bdi->index = bgp_reuse_index(bdi->penalty, bdc); bdi->prev = NULL; - bdi->next = damp->reuse_list[index]; - if (damp->reuse_list[index]) - damp->reuse_list[index]->prev = bdi; - damp->reuse_list[index] = bdi; + bdi->next = bdc->reuse_list[index]; + if (bdc->reuse_list[index]) + bdc->reuse_list[index]->prev = bdi; + bdc->reuse_list[index] = bdi; } /* Delete BGP dampening information from reuse list. */ -static void bgp_reuse_list_delete(struct bgp_damp_info *bdi) +static void bgp_reuse_list_delete(struct bgp_damp_info *bdi, + struct bgp_damp_config *bdc) { if (bdi->next) bdi->next->prev = bdi->prev; if (bdi->prev) bdi->prev->next = bdi->next; else - damp->reuse_list[bdi->index] = bdi->next; + bdc->reuse_list[bdi->index] = bdi->next; } /* Return decayed penalty value. */ -int bgp_damp_decay(time_t tdiff, int penalty) +int bgp_damp_decay(time_t tdiff, int penalty, struct bgp_damp_config *bdc) { unsigned int i; @@ -97,10 +104,10 @@ int bgp_damp_decay(time_t tdiff, int penalty) if (i == 0) return penalty; - if (i >= damp->decay_array_size) + if (i >= bdc->decay_array_size) return 0; - return (int)(penalty * damp->decay_array[i]); + return (int)(penalty * bdc->decay_array[i]); } /* Handler of reuse timer event. Each route in the current reuse-list @@ -111,20 +118,22 @@ static int bgp_reuse_timer(struct thread *t) struct bgp_damp_info *next; time_t t_now, t_diff; - damp->t_reuse = NULL; - thread_add_timer(bm->master, bgp_reuse_timer, NULL, DELTA_REUSE, - &damp->t_reuse); + struct bgp_damp_config *bdc = THREAD_ARG(t); + + bdc->t_reuse = NULL; + thread_add_timer(bm->master, bgp_reuse_timer, bdc, DELTA_REUSE, + &bdc->t_reuse); t_now = bgp_clock(); /* 1. save a pointer to the current zeroth queue head and zero the list head entry. */ - bdi = damp->reuse_list[damp->reuse_offset]; - damp->reuse_list[damp->reuse_offset] = NULL; + bdi = bdc->reuse_list[bdc->reuse_offset]; + bdc->reuse_list[bdc->reuse_offset] = NULL; /* 2. set offset = modulo reuse-list-size ( offset + 1 ), thereby rotating the circular queue of list-heads. */ - damp->reuse_offset = (damp->reuse_offset + 1) % damp->reuse_list_size; + bdc->reuse_offset = (bdc->reuse_offset + 1) % bdc->reuse_list_size; /* 3. if ( the saved list head pointer is non-empty ) */ for (; bdi; bdi = next) { @@ -137,47 +146,49 @@ static int bgp_reuse_timer(struct thread *t) /* Set figure-of-merit = figure-of-merit * decay-array-ok * [t-diff] */ - bdi->penalty = bgp_damp_decay(t_diff, bdi->penalty); + bdi->penalty = bgp_damp_decay(t_diff, bdi->penalty, bdc); /* Set t-updated = t-now. */ bdi->t_updated = t_now; /* if (figure-of-merit < reuse). */ - if (bdi->penalty < damp->reuse_limit) { + if (bdi->penalty < bdc->reuse_limit) { /* Reuse the route. */ - bgp_path_info_unset_flag(bdi->rn, bdi->path, + bgp_path_info_unset_flag(bdi->dest, bdi->path, BGP_PATH_DAMPED); bdi->suppress_time = 0; if (bdi->lastrecord == BGP_RECORD_UPDATE) { - bgp_path_info_unset_flag(bdi->rn, bdi->path, + bgp_path_info_unset_flag(bdi->dest, bdi->path, BGP_PATH_HISTORY); - bgp_aggregate_increment(bgp, &bdi->rn->p, - bdi->path, bdi->afi, - bdi->safi); - bgp_process(bgp, bdi->rn, bdi->afi, bdi->safi); + bgp_aggregate_increment( + bgp, bgp_dest_get_prefix(bdi->dest), + bdi->path, bdi->afi, bdi->safi); + bgp_process(bgp, bdi->dest, bdi->afi, + bdi->safi); } - if (bdi->penalty <= damp->reuse_limit / 2.0) - bgp_damp_info_free(bdi, 1); + if (bdi->penalty <= bdc->reuse_limit / 2.0) + bgp_damp_info_free(bdi, 1, bdc->afi, bdc->safi); else - BGP_DAMP_LIST_ADD(damp, bdi); + BGP_DAMP_LIST_ADD(bdc, bdi); } else /* Re-insert into another list (See RFC2439 Section * 4.8.6). */ - bgp_reuse_list_add(bdi); + bgp_reuse_list_add(bdi, bdc); } return 0; } /* A route becomes unreachable (RFC2439 Section 4.8.2). */ -int bgp_damp_withdraw(struct bgp_path_info *path, struct bgp_node *rn, +int bgp_damp_withdraw(struct bgp_path_info *path, struct bgp_dest *dest, afi_t afi, safi_t safi, int attr_change) { time_t t_now; struct bgp_damp_info *bdi = NULL; unsigned int last_penalty = 0; + struct bgp_damp_config *bdc = &damp[afi][safi]; t_now = bgp_clock(); @@ -196,7 +207,7 @@ int bgp_damp_withdraw(struct bgp_path_info *path, struct bgp_node *rn, bdi = XCALLOC(MTYPE_BGP_DAMP_INFO, sizeof(struct bgp_damp_info)); bdi->path = path; - bdi->rn = rn; + bdi->dest = dest; bdi->penalty = (attr_change ? DEFAULT_PENALTY / 2 : DEFAULT_PENALTY); bdi->flap = 1; @@ -206,136 +217,96 @@ int bgp_damp_withdraw(struct bgp_path_info *path, struct bgp_node *rn, bdi->afi = afi; bdi->safi = safi; (bgp_path_info_extra_get(path))->damp_info = bdi; - BGP_DAMP_LIST_ADD(damp, bdi); + BGP_DAMP_LIST_ADD(bdc, bdi); } else { last_penalty = bdi->penalty; /* 1. Set t-diff = t-now - t-updated. */ - bdi->penalty = - (bgp_damp_decay(t_now - bdi->t_updated, bdi->penalty) - + (attr_change ? DEFAULT_PENALTY / 2 - : DEFAULT_PENALTY)); + bdi->penalty = (bgp_damp_decay(t_now - bdi->t_updated, + bdi->penalty, bdc) + + (attr_change ? DEFAULT_PENALTY / 2 + : DEFAULT_PENALTY)); - if (bdi->penalty > damp->ceiling) - bdi->penalty = damp->ceiling; + if (bdi->penalty > bdc->ceiling) + bdi->penalty = bdc->ceiling; bdi->flap++; } - assert((rn == bdi->rn) && (path == bdi->path)); + assert((dest == bdi->dest) && (path == bdi->path)); bdi->lastrecord = BGP_RECORD_WITHDRAW; bdi->t_updated = t_now; /* Make this route as historical status. */ - bgp_path_info_set_flag(rn, path, BGP_PATH_HISTORY); + bgp_path_info_set_flag(dest, path, BGP_PATH_HISTORY); /* Remove the route from a reuse list if it is on one. */ if (CHECK_FLAG(bdi->path->flags, BGP_PATH_DAMPED)) { /* If decay rate isn't equal to 0, reinsert brn. */ if (bdi->penalty != last_penalty && bdi->index >= 0) { - bgp_reuse_list_delete(bdi); - bgp_reuse_list_add(bdi); + bgp_reuse_list_delete(bdi, bdc); + bgp_reuse_list_add(bdi, bdc); } return BGP_DAMP_SUPPRESSED; } /* If not suppressed before, do annonunce this withdraw and insert into reuse_list. */ - if (bdi->penalty >= damp->suppress_value) { - bgp_path_info_set_flag(rn, path, BGP_PATH_DAMPED); + if (bdi->penalty >= bdc->suppress_value) { + bgp_path_info_set_flag(dest, path, BGP_PATH_DAMPED); bdi->suppress_time = t_now; - BGP_DAMP_LIST_DEL(damp, bdi); - bgp_reuse_list_add(bdi); + BGP_DAMP_LIST_DEL(bdc, bdi); + bgp_reuse_list_add(bdi, bdc); } return BGP_DAMP_USED; } -int bgp_damp_update(struct bgp_path_info *path, struct bgp_node *rn, afi_t afi, - safi_t safi) +int bgp_damp_update(struct bgp_path_info *path, struct bgp_dest *dest, + afi_t afi, safi_t safi) { time_t t_now; struct bgp_damp_info *bdi; int status; + struct bgp_damp_config *bdc = &damp[afi][safi]; if (!path->extra || !((bdi = path->extra->damp_info))) return BGP_DAMP_USED; t_now = bgp_clock(); - bgp_path_info_unset_flag(rn, path, BGP_PATH_HISTORY); + bgp_path_info_unset_flag(dest, path, BGP_PATH_HISTORY); bdi->lastrecord = BGP_RECORD_UPDATE; - bdi->penalty = bgp_damp_decay(t_now - bdi->t_updated, bdi->penalty); + bdi->penalty = + bgp_damp_decay(t_now - bdi->t_updated, bdi->penalty, bdc); if (!CHECK_FLAG(bdi->path->flags, BGP_PATH_DAMPED) - && (bdi->penalty < damp->suppress_value)) + && (bdi->penalty < bdc->suppress_value)) status = BGP_DAMP_USED; else if (CHECK_FLAG(bdi->path->flags, BGP_PATH_DAMPED) - && (bdi->penalty < damp->reuse_limit)) { - bgp_path_info_unset_flag(rn, path, BGP_PATH_DAMPED); - bgp_reuse_list_delete(bdi); - BGP_DAMP_LIST_ADD(damp, bdi); + && (bdi->penalty < bdc->reuse_limit)) { + bgp_path_info_unset_flag(dest, path, BGP_PATH_DAMPED); + bgp_reuse_list_delete(bdi, bdc); + BGP_DAMP_LIST_ADD(bdc, bdi); bdi->suppress_time = 0; status = BGP_DAMP_USED; } else status = BGP_DAMP_SUPPRESSED; - if (bdi->penalty > damp->reuse_limit / 2.0) + if (bdi->penalty > bdc->reuse_limit / 2.0) bdi->t_updated = t_now; else - bgp_damp_info_free(bdi, 0); + bgp_damp_info_free(bdi, 0, afi, safi); return status; } -/* Remove dampening information and history route. */ -int bgp_damp_scan(struct bgp_path_info *path, afi_t afi, safi_t safi) -{ - time_t t_now, t_diff; - struct bgp_damp_info *bdi; - - assert(path->extra && path->extra->damp_info); - - t_now = bgp_clock(); - bdi = path->extra->damp_info; - - if (CHECK_FLAG(path->flags, BGP_PATH_DAMPED)) { - t_diff = t_now - bdi->suppress_time; - - if (t_diff >= damp->max_suppress_time) { - bgp_path_info_unset_flag(bdi->rn, path, - BGP_PATH_DAMPED); - bgp_reuse_list_delete(bdi); - BGP_DAMP_LIST_ADD(damp, bdi); - bdi->penalty = damp->reuse_limit; - bdi->suppress_time = 0; - bdi->t_updated = t_now; - - /* Need to announce UPDATE once this path is usable - * again. */ - if (bdi->lastrecord == BGP_RECORD_UPDATE) - return 1; - else - return 0; - } - } else { - t_diff = t_now - bdi->t_updated; - bdi->penalty = bgp_damp_decay(t_diff, bdi->penalty); - - if (bdi->penalty <= damp->reuse_limit / 2.0) { - /* release the bdi, bdi->path. */ - bgp_damp_info_free(bdi, 1); - return 0; - } else - bdi->t_updated = t_now; - } - return 0; -} - -void bgp_damp_info_free(struct bgp_damp_info *bdi, int withdraw) +void bgp_damp_info_free(struct bgp_damp_info *bdi, int withdraw, afi_t afi, + safi_t safi) { struct bgp_path_info *path; + struct bgp_damp_config *bdc = &damp[afi][safi]; if (!bdi) return; @@ -344,81 +315,80 @@ void bgp_damp_info_free(struct bgp_damp_info *bdi, int withdraw) path->extra->damp_info = NULL; if (CHECK_FLAG(path->flags, BGP_PATH_DAMPED)) - bgp_reuse_list_delete(bdi); + bgp_reuse_list_delete(bdi, bdc); else - BGP_DAMP_LIST_DEL(damp, bdi); + BGP_DAMP_LIST_DEL(bdc, bdi); - bgp_path_info_unset_flag(bdi->rn, path, + bgp_path_info_unset_flag(bdi->dest, path, BGP_PATH_HISTORY | BGP_PATH_DAMPED); if (bdi->lastrecord == BGP_RECORD_WITHDRAW && withdraw) - bgp_path_info_delete(bdi->rn, path); + bgp_path_info_delete(bdi->dest, path); XFREE(MTYPE_BGP_DAMP_INFO, bdi); } -static void bgp_damp_parameter_set(int hlife, int reuse, int sup, int maxsup) +static void bgp_damp_parameter_set(int hlife, int reuse, int sup, int maxsup, + struct bgp_damp_config *bdc) { double reuse_max_ratio; unsigned int i; double j; - damp->suppress_value = sup; - damp->half_life = hlife; - damp->reuse_limit = reuse; - damp->max_suppress_time = maxsup; + bdc->suppress_value = sup; + bdc->half_life = hlife; + bdc->reuse_limit = reuse; + bdc->max_suppress_time = maxsup; /* Initialize params per bgp_damp_config. */ - damp->reuse_index_size = REUSE_ARRAY_SIZE; + bdc->reuse_index_size = REUSE_ARRAY_SIZE; - damp->ceiling = - (int)(damp->reuse_limit * (pow(2, - (double)damp->max_suppress_time - / damp->half_life))); + bdc->ceiling = (int)(bdc->reuse_limit + * (pow(2, (double)bdc->max_suppress_time + / bdc->half_life))); /* Decay-array computations */ - damp->decay_array_size = - ceil((double)damp->max_suppress_time / DELTA_T); - damp->decay_array = XMALLOC(MTYPE_BGP_DAMP_ARRAY, - sizeof(double) * (damp->decay_array_size)); - damp->decay_array[0] = 1.0; - damp->decay_array[1] = - exp((1.0 / ((double)damp->half_life / DELTA_T)) * log(0.5)); + bdc->decay_array_size = ceil((double)bdc->max_suppress_time / DELTA_T); + bdc->decay_array = XMALLOC(MTYPE_BGP_DAMP_ARRAY, + sizeof(double) * (bdc->decay_array_size)); + bdc->decay_array[0] = 1.0; + bdc->decay_array[1] = + exp((1.0 / ((double)bdc->half_life / DELTA_T)) * log(0.5)); /* Calculate decay values for all possible times */ - for (i = 2; i < damp->decay_array_size; i++) - damp->decay_array[i] = - damp->decay_array[i - 1] * damp->decay_array[1]; + for (i = 2; i < bdc->decay_array_size; i++) + bdc->decay_array[i] = + bdc->decay_array[i - 1] * bdc->decay_array[1]; /* Reuse-list computations */ - i = ceil((double)damp->max_suppress_time / DELTA_REUSE) + 1; + i = ceil((double)bdc->max_suppress_time / DELTA_REUSE) + 1; if (i > REUSE_LIST_SIZE || i == 0) i = REUSE_LIST_SIZE; - damp->reuse_list_size = i; + bdc->reuse_list_size = i; - damp->reuse_list = XCALLOC(MTYPE_BGP_DAMP_ARRAY, - damp->reuse_list_size - * sizeof(struct bgp_reuse_node *)); + bdc->reuse_list = + XCALLOC(MTYPE_BGP_DAMP_ARRAY, + bdc->reuse_list_size * sizeof(struct bgp_reuse_node *)); /* Reuse-array computations */ - damp->reuse_index = XCALLOC(MTYPE_BGP_DAMP_ARRAY, - sizeof(int) * damp->reuse_index_size); + bdc->reuse_index = XCALLOC(MTYPE_BGP_DAMP_ARRAY, + sizeof(int) * bdc->reuse_index_size); - reuse_max_ratio = (double)damp->ceiling / damp->reuse_limit; - j = (exp((double)damp->max_suppress_time / damp->half_life) - * log10(2.0)); + reuse_max_ratio = (double)bdc->ceiling / bdc->reuse_limit; + j = (exp((double)bdc->max_suppress_time / bdc->half_life) * log10(2.0)); if (reuse_max_ratio > j && j != 0) reuse_max_ratio = j; - damp->scale_factor = - (double)damp->reuse_index_size / (reuse_max_ratio - 1); + bdc->scale_factor = + (double)bdc->reuse_index_size / (reuse_max_ratio - 1); - for (i = 0; i < damp->reuse_index_size; i++) { - damp->reuse_index[i] = - (int)(((double)damp->half_life / DELTA_REUSE) - * log10(1.0 / (damp->reuse_limit - * (1.0 + ((double)i - / damp->scale_factor)))) + for (i = 0; i < bdc->reuse_index_size; i++) { + bdc->reuse_index[i] = + (int)(((double)bdc->half_life / DELTA_REUSE) + * log10(1.0 + / (bdc->reuse_limit + * (1.0 + + ((double)i / bdc->scale_factor)))) / log10(0.5)); } } @@ -426,124 +396,131 @@ static void bgp_damp_parameter_set(int hlife, int reuse, int sup, int maxsup) int bgp_damp_enable(struct bgp *bgp, afi_t afi, safi_t safi, time_t half, unsigned int reuse, unsigned int suppress, time_t max) { + struct bgp_damp_config *bdc = &damp[afi][safi]; + if (CHECK_FLAG(bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING)) { - if (damp->half_life == half && damp->reuse_limit == reuse - && damp->suppress_value == suppress - && damp->max_suppress_time == max) + if (bdc->half_life == half && bdc->reuse_limit == reuse + && bdc->suppress_value == suppress + && bdc->max_suppress_time == max) return 0; bgp_damp_disable(bgp, afi, safi); } SET_FLAG(bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING); - bgp_damp_parameter_set(half, reuse, suppress, max); + bgp_damp_parameter_set(half, reuse, suppress, max, bdc); /* Register reuse timer. */ - thread_add_timer(bm->master, bgp_reuse_timer, NULL, DELTA_REUSE, - &damp->t_reuse); + thread_add_timer(bm->master, bgp_reuse_timer, bdc, DELTA_REUSE, + &bdc->t_reuse); return 0; } -static void bgp_damp_config_clean(struct bgp_damp_config *damp) +static void bgp_damp_config_clean(struct bgp_damp_config *bdc) { /* Free decay array */ - XFREE(MTYPE_BGP_DAMP_ARRAY, damp->decay_array); - damp->decay_array_size = 0; + XFREE(MTYPE_BGP_DAMP_ARRAY, bdc->decay_array); + bdc->decay_array_size = 0; /* Free reuse index array */ - XFREE(MTYPE_BGP_DAMP_ARRAY, damp->reuse_index); - damp->reuse_index_size = 0; + XFREE(MTYPE_BGP_DAMP_ARRAY, bdc->reuse_index); + bdc->reuse_index_size = 0; /* Free reuse list array. */ - XFREE(MTYPE_BGP_DAMP_ARRAY, damp->reuse_list); - damp->reuse_list_size = 0; + XFREE(MTYPE_BGP_DAMP_ARRAY, bdc->reuse_list); + bdc->reuse_list_size = 0; } /* Clean all the bgp_damp_info stored in reuse_list. */ -void bgp_damp_info_clean(void) +void bgp_damp_info_clean(afi_t afi, safi_t safi) { unsigned int i; struct bgp_damp_info *bdi, *next; + struct bgp_damp_config *bdc = &damp[afi][safi]; - damp->reuse_offset = 0; + bdc->reuse_offset = 0; - for (i = 0; i < damp->reuse_list_size; i++) { - if (!damp->reuse_list[i]) + for (i = 0; i < bdc->reuse_list_size; i++) { + if (!bdc->reuse_list[i]) continue; - for (bdi = damp->reuse_list[i]; bdi; bdi = next) { + for (bdi = bdc->reuse_list[i]; bdi; bdi = next) { next = bdi->next; - bgp_damp_info_free(bdi, 1); + bgp_damp_info_free(bdi, 1, afi, safi); } - damp->reuse_list[i] = NULL; + bdc->reuse_list[i] = NULL; } - for (bdi = damp->no_reuse_list; bdi; bdi = next) { + for (bdi = bdc->no_reuse_list; bdi; bdi = next) { next = bdi->next; - bgp_damp_info_free(bdi, 1); + bgp_damp_info_free(bdi, 1, afi, safi); } - damp->no_reuse_list = NULL; + bdc->no_reuse_list = NULL; } int bgp_damp_disable(struct bgp *bgp, afi_t afi, safi_t safi) { + struct bgp_damp_config *bdc = &damp[afi][safi]; /* If it wasn't enabled, there's nothing to do. */ if (!CHECK_FLAG(bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING)) return 0; /* Cancel reuse thread. */ - if (damp->t_reuse) - thread_cancel(damp->t_reuse); - damp->t_reuse = NULL; + if (bdc->t_reuse) + thread_cancel(bdc->t_reuse); + bdc->t_reuse = NULL; /* Clean BGP dampening information. */ - bgp_damp_info_clean(); + bgp_damp_info_clean(afi, safi); /* Clear configuration */ - bgp_damp_config_clean(&bgp_damp_cfg); + bgp_damp_config_clean(bdc); UNSET_FLAG(bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING); return 0; } -void bgp_config_write_damp(struct vty *vty) +void bgp_config_write_damp(struct vty *vty, afi_t afi, safi_t safi) { - if (bgp_damp_cfg.half_life == DEFAULT_HALF_LIFE * 60 - && bgp_damp_cfg.reuse_limit == DEFAULT_REUSE - && bgp_damp_cfg.suppress_value == DEFAULT_SUPPRESS - && bgp_damp_cfg.max_suppress_time == bgp_damp_cfg.half_life * 4) - vty_out(vty, " bgp dampening\n"); - else if (bgp_damp_cfg.half_life != DEFAULT_HALF_LIFE * 60 - && bgp_damp_cfg.reuse_limit == DEFAULT_REUSE - && bgp_damp_cfg.suppress_value == DEFAULT_SUPPRESS - && bgp_damp_cfg.max_suppress_time - == bgp_damp_cfg.half_life * 4) - vty_out(vty, " bgp dampening %lld\n", - bgp_damp_cfg.half_life / 60LL); + if (damp[afi][safi].half_life == DEFAULT_HALF_LIFE * 60 + && damp[afi][safi].reuse_limit == DEFAULT_REUSE + && damp[afi][safi].suppress_value == DEFAULT_SUPPRESS + && damp[afi][safi].max_suppress_time + == damp[afi][safi].half_life * 4) + vty_out(vty, " bgp dampening\n"); + else if (damp[afi][safi].half_life != DEFAULT_HALF_LIFE * 60 + && damp[afi][safi].reuse_limit == DEFAULT_REUSE + && damp[afi][safi].suppress_value == DEFAULT_SUPPRESS + && damp[afi][safi].max_suppress_time + == damp[afi][safi].half_life * 4) + vty_out(vty, " bgp dampening %lld\n", + damp[afi][safi].half_life / 60LL); else - vty_out(vty, " bgp dampening %lld %d %d %lld\n", - bgp_damp_cfg.half_life / 60LL, bgp_damp_cfg.reuse_limit, - bgp_damp_cfg.suppress_value, - bgp_damp_cfg.max_suppress_time / 60LL); + vty_out(vty, " bgp dampening %lld %d %d %lld\n", + damp[afi][safi].half_life / 60LL, + damp[afi][safi].reuse_limit, + damp[afi][safi].suppress_value, + damp[afi][safi].max_suppress_time / 60LL); } static const char *bgp_get_reuse_time(unsigned int penalty, char *buf, - size_t len, bool use_json, - json_object *json) + size_t len, afi_t afi, safi_t safi, + bool use_json, json_object *json) { time_t reuse_time = 0; - struct tm *tm = NULL; + struct tm tm; int time_store = 0; - if (penalty > damp->reuse_limit) { + if (penalty > damp[afi][safi].reuse_limit) { reuse_time = (int)(DELTA_T - * ((log((double)damp->reuse_limit / penalty)) - / (log(damp->decay_array[1])))); + * ((log((double)damp[afi][safi].reuse_limit + / penalty)) + / (log(damp[afi][safi].decay_array[1])))); - if (reuse_time > damp->max_suppress_time) - reuse_time = damp->max_suppress_time; + if (reuse_time > damp[afi][safi].max_suppress_time) + reuse_time = damp[afi][safi].max_suppress_time; - tm = gmtime(&reuse_time); + gmtime_r(&reuse_time, &tm); } else reuse_time = 0; @@ -555,51 +532,52 @@ static const char *bgp_get_reuse_time(unsigned int penalty, char *buf, snprintf(buf, len, "00:00:00"); } else if (reuse_time < ONE_DAY_SECOND) { if (use_json) { - time_store = (3600000 * tm->tm_hour) - + (60000 * tm->tm_min) - + (1000 * tm->tm_sec); + time_store = (3600000 * tm.tm_hour) + + (60000 * tm.tm_min) + + (1000 * tm.tm_sec); json_object_int_add(json, "reuseTimerMsecs", time_store); } else - snprintf(buf, len, "%02d:%02d:%02d", tm->tm_hour, - tm->tm_min, tm->tm_sec); + snprintf(buf, len, "%02d:%02d:%02d", tm.tm_hour, + tm.tm_min, tm.tm_sec); } else if (reuse_time < ONE_WEEK_SECOND) { if (use_json) { - time_store = (86400000 * tm->tm_yday) - + (3600000 * tm->tm_hour) - + (60000 * tm->tm_min) - + (1000 * tm->tm_sec); + time_store = (86400000 * tm.tm_yday) + + (3600000 * tm.tm_hour) + + (60000 * tm.tm_min) + + (1000 * tm.tm_sec); json_object_int_add(json, "reuseTimerMsecs", time_store); } else - snprintf(buf, len, "%dd%02dh%02dm", tm->tm_yday, - tm->tm_hour, tm->tm_min); + snprintf(buf, len, "%dd%02dh%02dm", tm.tm_yday, + tm.tm_hour, tm.tm_min); } else { if (use_json) { time_store = - (604800000 * tm->tm_yday / 7) + (604800000 * tm.tm_yday / 7) + (86400000 - * (tm->tm_yday - ((tm->tm_yday / 7) * 7))) - + (3600000 * tm->tm_hour) + (60000 * tm->tm_min) - + (1000 * tm->tm_sec); + * (tm.tm_yday - ((tm.tm_yday / 7) * 7))) + + (3600000 * tm.tm_hour) + (60000 * tm.tm_min) + + (1000 * tm.tm_sec); json_object_int_add(json, "reuseTimerMsecs", time_store); } else - snprintf(buf, len, "%02dw%dd%02dh", tm->tm_yday / 7, - tm->tm_yday - ((tm->tm_yday / 7) * 7), - tm->tm_hour); + snprintf(buf, len, "%02dw%dd%02dh", tm.tm_yday / 7, + tm.tm_yday - ((tm.tm_yday / 7) * 7), + tm.tm_hour); } return buf; } -void bgp_damp_info_vty(struct vty *vty, struct bgp_path_info *path, - json_object *json_path) +void bgp_damp_info_vty(struct vty *vty, struct bgp_path_info *path, afi_t afi, + safi_t safi, json_object *json_path) { struct bgp_damp_info *bdi; time_t t_now, t_diff; char timebuf[BGP_UPTIME_LEN]; int penalty; + struct bgp_damp_config *bdc = &damp[afi][safi]; if (!path->extra) return; @@ -609,13 +587,13 @@ void bgp_damp_info_vty(struct vty *vty, struct bgp_path_info *path, /* If dampening is not enabled or there is no dampening information, return immediately. */ - if (!damp || !bdi) + if (!bdc || !bdi) return; /* Calculate new penalty. */ t_now = bgp_clock(); t_diff = t_now - bdi->t_updated; - penalty = bgp_damp_decay(t_diff, bdi->penalty); + penalty = bgp_damp_decay(t_diff, bdi->penalty, bdc); if (json_path) { json_object_int_add(json_path, "dampeningPenalty", penalty); @@ -625,8 +603,8 @@ void bgp_damp_info_vty(struct vty *vty, struct bgp_path_info *path, if (CHECK_FLAG(path->flags, BGP_PATH_DAMPED) && !CHECK_FLAG(path->flags, BGP_PATH_HISTORY)) - bgp_get_reuse_time(penalty, timebuf, BGP_UPTIME_LEN, 1, - json_path); + bgp_get_reuse_time(penalty, timebuf, BGP_UPTIME_LEN, + afi, safi, 1, json_path); } else { vty_out(vty, " Dampinfo: penalty %d, flapped %d times in %s", @@ -638,7 +616,7 @@ void bgp_damp_info_vty(struct vty *vty, struct bgp_path_info *path, && !CHECK_FLAG(path->flags, BGP_PATH_HISTORY)) vty_out(vty, ", reuse in %s", bgp_get_reuse_time(penalty, timebuf, - BGP_UPTIME_LEN, 0, + BGP_UPTIME_LEN, afi, safi, 0, json_path)); vty_out(vty, "\n"); @@ -646,12 +624,14 @@ void bgp_damp_info_vty(struct vty *vty, struct bgp_path_info *path, } const char *bgp_damp_reuse_time_vty(struct vty *vty, struct bgp_path_info *path, - char *timebuf, size_t len, bool use_json, + char *timebuf, size_t len, afi_t afi, + safi_t safi, bool use_json, json_object *json) { struct bgp_damp_info *bdi; time_t t_now, t_diff; int penalty; + struct bgp_damp_config *bdc = &damp[afi][safi]; if (!path->extra) return NULL; @@ -661,15 +641,16 @@ const char *bgp_damp_reuse_time_vty(struct vty *vty, struct bgp_path_info *path, /* If dampening is not enabled or there is no dampening information, return immediately. */ - if (!damp || !bdi) + if (!bdc || !bdi) return NULL; /* Calculate new penalty. */ t_now = bgp_clock(); t_diff = t_now - bdi->t_updated; - penalty = bgp_damp_decay(t_diff, bdi->penalty); + penalty = bgp_damp_decay(t_diff, bdi->penalty, bdc); - return bgp_get_reuse_time(penalty, timebuf, len, use_json, json); + return bgp_get_reuse_time(penalty, timebuf, len, afi, safi, use_json, + json); } int bgp_show_dampening_parameters(struct vty *vty, afi_t afi, safi_t safi) @@ -684,12 +665,15 @@ int bgp_show_dampening_parameters(struct vty *vty, afi_t afi, safi_t safi) if (CHECK_FLAG(bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING)) { vty_out(vty, "Half-life time: %lld min\n", - (long long)damp->half_life / 60); - vty_out(vty, "Reuse penalty: %d\n", damp->reuse_limit); - vty_out(vty, "Suppress penalty: %d\n", damp->suppress_value); + (long long)damp[afi][safi].half_life / 60); + vty_out(vty, "Reuse penalty: %d\n", + damp[afi][safi].reuse_limit); + vty_out(vty, "Suppress penalty: %d\n", + damp[afi][safi].suppress_value); vty_out(vty, "Max suppress time: %lld min\n", - (long long)damp->max_suppress_time / 60); - vty_out(vty, "Max suppress penalty: %u\n", damp->ceiling); + (long long)damp[afi][safi].max_suppress_time / 60); + vty_out(vty, "Max suppress penalty: %u\n", + damp[afi][safi].ceiling); vty_out(vty, "\n"); } else vty_out(vty, "dampening not enabled for %s\n", diff --git a/bgpd/bgp_damp.h b/bgpd/bgp_damp.h index 18bf561c47..4ab38326e2 100644 --- a/bgpd/bgp_damp.h +++ b/bgpd/bgp_damp.h @@ -21,6 +21,8 @@ #ifndef _QUAGGA_BGP_DAMP_H #define _QUAGGA_BGP_DAMP_H +#include "bgpd/bgp_table.h" + /* Structure maintained on a per-route basis. */ struct bgp_damp_info { /* Doubly linked list. This information must be linked to @@ -47,7 +49,7 @@ struct bgp_damp_info { struct bgp_path_info *path; /* Back reference to bgp_node. */ - struct bgp_node *rn; + struct bgp_dest *dest; /* Current index in the reuse_list. */ int index; @@ -106,6 +108,9 @@ struct bgp_damp_config { /* Reuse timer thread per-set base. */ struct thread *t_reuse; + + afi_t afi; + safi_t safi; }; #define BGP_DAMP_NONE 0 @@ -130,21 +135,22 @@ struct bgp_damp_config { extern int bgp_damp_enable(struct bgp *, afi_t, safi_t, time_t, unsigned int, unsigned int, time_t); extern int bgp_damp_disable(struct bgp *, afi_t, safi_t); -extern int bgp_damp_withdraw(struct bgp_path_info *path, struct bgp_node *rn, +extern int bgp_damp_withdraw(struct bgp_path_info *path, struct bgp_dest *dest, afi_t afi, safi_t safi, int attr_change); -extern int bgp_damp_update(struct bgp_path_info *path, struct bgp_node *rn, +extern int bgp_damp_update(struct bgp_path_info *path, struct bgp_dest *dest, afi_t afi, safi_t saff); -extern int bgp_damp_scan(struct bgp_path_info *path, afi_t afi, safi_t safi); -extern void bgp_damp_info_free(struct bgp_damp_info *path, int withdraw); -extern void bgp_damp_info_clean(void); -extern int bgp_damp_decay(time_t, int); -extern void bgp_config_write_damp(struct vty *); +extern void bgp_damp_info_free(struct bgp_damp_info *path, int withdraw, + afi_t afi, safi_t safi); +extern void bgp_damp_info_clean(afi_t afi, safi_t safi); +extern int bgp_damp_decay(time_t, int, struct bgp_damp_config *damp); +extern void bgp_config_write_damp(struct vty *, afi_t afi, safi_t safi); extern void bgp_damp_info_vty(struct vty *vty, struct bgp_path_info *path, - json_object *json_path); + afi_t afi, safi_t safi, json_object *json_path); extern const char *bgp_damp_reuse_time_vty(struct vty *vty, struct bgp_path_info *path, - char *timebuf, size_t len, - bool use_json, json_object *json); + char *timebuf, size_t len, afi_t afi, + safi_t safi, bool use_json, + json_object *json); extern int bgp_show_dampening_parameters(struct vty *vty, afi_t, safi_t); #endif /* _QUAGGA_BGP_DAMP_H */ diff --git a/bgpd/bgp_debug.c b/bgpd/bgp_debug.c index f476b16188..96c311f6f0 100644 --- a/bgpd/bgp_debug.c +++ b/bgpd/bgp_debug.c @@ -63,6 +63,8 @@ unsigned long conf_bgp_debug_vpn; unsigned long conf_bgp_debug_flowspec; unsigned long conf_bgp_debug_labelpool; unsigned long conf_bgp_debug_pbr; +unsigned long conf_bgp_debug_graceful_restart; +unsigned long conf_bgp_debug_evpn_mh; unsigned long term_bgp_debug_as4; unsigned long term_bgp_debug_neighbor_events; @@ -80,6 +82,8 @@ unsigned long term_bgp_debug_vpn; unsigned long term_bgp_debug_flowspec; unsigned long term_bgp_debug_labelpool; unsigned long term_bgp_debug_pbr; +unsigned long term_bgp_debug_graceful_restart; +unsigned long term_bgp_debug_evpn_mh; struct list *bgp_debug_neighbor_events_peers = NULL; struct list *bgp_debug_keepalive_peers = NULL; @@ -101,7 +105,7 @@ const struct message bgp_status_msg[] = {{Idle, "Idle"}, {0}}; /* BGP message type string. */ -const char *bgp_type_str[] = {NULL, "OPEN", "UPDATE", +const char *const bgp_type_str[] = {NULL, "OPEN", "UPDATE", "NOTIFICATION", "KEEPALIVE", "ROUTE-REFRESH", "CAPABILITY"}; @@ -168,12 +172,22 @@ static const struct message bgp_notify_capability_msg[] = { {BGP_NOTIFY_CAPABILITY_MALFORMED_CODE, "/Malformed Capability Value"}, {0}}; +static const struct message bgp_notify_fsm_msg[] = { + {BGP_NOTIFY_FSM_ERR_SUBCODE_UNSPECIFIC, "/Unspecific"}, + {BGP_NOTIFY_FSM_ERR_SUBCODE_OPENSENT, + "/Receive Unexpected Message in OpenSent State"}, + {BGP_NOTIFY_FSM_ERR_SUBCODE_OPENCONFIRM, + "/Receive Unexpected Message in OpenConfirm State"}, + {BGP_NOTIFY_FSM_ERR_SUBCODE_ESTABLISHED, + "/Receive Unexpected Message in Established State"}, + {0}}; + /* Origin strings. */ -const char *bgp_origin_str[] = {"i", "e", "?"}; -const char *bgp_origin_long_str[] = {"IGP", "EGP", "incomplete"}; +const char *const bgp_origin_str[] = {"i", "e", "?"}; +const char *const bgp_origin_long_str[] = {"IGP", "EGP", "incomplete"}; -static int bgp_debug_print_evpn_prefix(struct vty *vty, const char *desc, - struct prefix *p); +static void bgp_debug_print_evpn_prefix(struct vty *vty, const char *desc, + struct prefix *p); /* Given a string return a pointer the corresponding peer structure */ static struct peer *bgp_find_peer(struct vty *vty, const char *peer_str) { @@ -207,13 +221,8 @@ static void bgp_debug_list_free(struct list *list) if (list) for (ALL_LIST_ELEMENTS(list, node, nnode, filter)) { listnode_delete(list, filter); - - if (filter->p) - prefix_free(filter->p); - - if (filter->host) - XFREE(MTYPE_BGP_DEBUG_STR, filter->host); - + prefix_free(&filter->p); + XFREE(MTYPE_BGP_DEBUG_STR, filter->host); XFREE(MTYPE_BGP_DEBUG_FILTER, filter); } } @@ -308,8 +317,8 @@ static void bgp_debug_list_add_entry(struct list *list, const char *host, listnode_add(list, filter); } -static int bgp_debug_list_remove_entry(struct list *list, const char *host, - struct prefix *p) +static bool bgp_debug_list_remove_entry(struct list *list, const char *host, + struct prefix *p) { struct bgp_debug_filter *filter; struct listnode *node, *nnode; @@ -319,21 +328,21 @@ static int bgp_debug_list_remove_entry(struct list *list, const char *host, listnode_delete(list, filter); XFREE(MTYPE_BGP_DEBUG_STR, filter->host); XFREE(MTYPE_BGP_DEBUG_FILTER, filter); - return 1; + return true; } else if (p && filter->p->prefixlen == p->prefixlen && prefix_match(filter->p, p)) { listnode_delete(list, filter); - prefix_free(filter->p); + prefix_free(&filter->p); XFREE(MTYPE_BGP_DEBUG_FILTER, filter); - return 1; + return true; } } - return 0; + return false; } -static int bgp_debug_list_has_entry(struct list *list, const char *host, - const struct prefix *p) +static bool bgp_debug_list_has_entry(struct list *list, const char *host, + const struct prefix *p) { struct bgp_debug_filter *filter; struct listnode *node, *nnode; @@ -341,32 +350,32 @@ static int bgp_debug_list_has_entry(struct list *list, const char *host, for (ALL_LIST_ELEMENTS(list, node, nnode, filter)) { if (host) { if (strcmp(filter->host, host) == 0) { - return 1; + return true; } } else if (p) { if (filter->p->prefixlen == p->prefixlen && prefix_match(filter->p, p)) { - return 1; + return true; } } } - return 0; + return false; } -int bgp_debug_peer_updout_enabled(char *host) +bool bgp_debug_peer_updout_enabled(char *host) { return (bgp_debug_list_has_entry(bgp_debug_update_out_peers, host, NULL)); } /* Dump attribute. */ -int bgp_dump_attr(struct attr *attr, char *buf, size_t size) +bool bgp_dump_attr(struct attr *attr, char *buf, size_t size) { char addrbuf[BUFSIZ]; if (!attr) - return 0; + return false; buf[0] = '\0'; @@ -448,9 +457,9 @@ int bgp_dump_attr(struct attr *attr, char *buf, size_t size) } if (strlen(buf) > 1) - return 1; + return true; else - return 0; + return false; } const char *bgp_notify_code_str(char code) @@ -474,7 +483,8 @@ const char *bgp_notify_subcode_str(char code, char subcode) case BGP_NOTIFY_HOLD_ERR: break; case BGP_NOTIFY_FSM_ERR: - break; + return lookup_msg(bgp_notify_fsm_msg, subcode, + "Unrecognized Error Subcode"); case BGP_NOTIFY_CEASE: return lookup_msg(bgp_notify_cease_msg, subcode, "Unrecognized Error Subcode"); @@ -509,7 +519,7 @@ void bgp_notify_print(struct peer *peer, struct bgp_notify *bgp_notify, char msg_buf[1024]; if (BGP_DEBUG(neighbor_events, NEIGHBOR_EVENTS) - || bgp_flag_check(peer->bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES)) { + || CHECK_FLAG(peer->bgp->flags, BGP_FLAG_LOG_NEIGHBOR_CHANGES)) { code_str = bgp_notify_code_str(bgp_notify->code); subcode_str = bgp_notify_subcode_str(bgp_notify->code, bgp_notify->subcode); @@ -553,8 +563,8 @@ static void bgp_debug_clear_updgrp_update_dbg(struct bgp *bgp) update_group_walk(bgp, update_group_clear_update_dbg, NULL); } -static int bgp_debug_print_evpn_prefix(struct vty *vty, const char *desc, - struct prefix *p) +static void bgp_debug_print_evpn_prefix(struct vty *vty, const char *desc, + struct prefix *p) { char evpn_desc[PREFIX2STR_BUFFER + INET_ADDRSTRLEN]; char buf[PREFIX2STR_BUFFER]; @@ -562,39 +572,42 @@ static int bgp_debug_print_evpn_prefix(struct vty *vty, const char *desc, if (p->u.prefix_evpn.route_type == BGP_EVPN_MAC_IP_ROUTE) { if (is_evpn_prefix_ipaddr_none((struct prefix_evpn *)p)) { - sprintf(evpn_desc, "l2vpn evpn type macip mac %s", - prefix_mac2str( - &p->u.prefix_evpn.macip_addr.mac, - buf2, sizeof(buf2))); + snprintf( + evpn_desc, sizeof(evpn_desc), + "l2vpn evpn type macip mac %s", + prefix_mac2str(&p->u.prefix_evpn.macip_addr.mac, + buf2, sizeof(buf2))); } else { uint8_t family = is_evpn_prefix_ipaddr_v4( (struct prefix_evpn *)p) ? AF_INET : AF_INET6; - sprintf(evpn_desc, "l2vpn evpn type macip mac %s ip %s", - prefix_mac2str( - &p->u.prefix_evpn.macip_addr.mac, - buf2, sizeof(buf2)), - inet_ntop(family, + snprintf( + evpn_desc, sizeof(evpn_desc), + "l2vpn evpn type macip mac %s ip %s", + prefix_mac2str(&p->u.prefix_evpn.macip_addr.mac, + buf2, sizeof(buf2)), + inet_ntop( + family, &p->u.prefix_evpn.macip_addr.ip.ip.addr, buf, PREFIX2STR_BUFFER)); } } else if (p->u.prefix_evpn.route_type == BGP_EVPN_IMET_ROUTE) { - sprintf(evpn_desc, "l2vpn evpn type multicast ip %s", - inet_ntoa(p->u.prefix_evpn.imet_addr.ip.ipaddr_v4)); + snprintf(evpn_desc, sizeof(evpn_desc), + "l2vpn evpn type multicast ip %s", + inet_ntoa(p->u.prefix_evpn.imet_addr.ip.ipaddr_v4)); } else if (p->u.prefix_evpn.route_type == BGP_EVPN_IP_PREFIX_ROUTE) { uint8_t family = is_evpn_prefix_ipaddr_v4( (struct prefix_evpn *)p) ? AF_INET : AF_INET6; - sprintf(evpn_desc, "l2vpn evpn type prefix ip %s/%d", - inet_ntop(family, - &p->u.prefix_evpn.prefix_addr.ip.ip.addr, buf, - PREFIX2STR_BUFFER), - p->u.prefix_evpn.prefix_addr.ip_prefix_length); + snprintf(evpn_desc, sizeof(evpn_desc), + "l2vpn evpn type prefix ip %s/%d", + inet_ntop(family, + &p->u.prefix_evpn.prefix_addr.ip.ip.addr, + buf, PREFIX2STR_BUFFER), + p->u.prefix_evpn.prefix_addr.ip_prefix_length); } vty_out(vty, "%s %s\n", desc, evpn_desc); - - return 0; } static int bgp_debug_parse_evpn_prefix(struct vty *vty, struct cmd_token **argv, @@ -902,8 +915,8 @@ DEFUN (debug_bgp_keepalive_peer, "debug bgp keepalives ", DEBUG_STR BGP_STR - "BGP Neighbor Events\n" - "BGP neighbor IP address to debug\n" + "BGP keepalives\n" + "BGP IPv4 neighbor to debug\n" "BGP IPv6 neighbor to debug\n" "BGP neighbor on interface to debug\n") { @@ -1412,7 +1425,7 @@ DEFPY (debug_bgp_update_prefix_afi_safi, ret = bgp_debug_parse_evpn_prefix(vty, argv, argc, &argv_p); if (ret != CMD_SUCCESS) { - prefix_free(argv_p); + prefix_free(&argv_p); return ret; } @@ -1425,7 +1438,7 @@ DEFPY (debug_bgp_update_prefix_afi_safi, vty_out(vty, "BGP updates debugging is already enabled for %s\n", buf); - prefix_free(argv_p); + prefix_free(&argv_p); return CMD_SUCCESS; } @@ -1438,7 +1451,7 @@ DEFPY (debug_bgp_update_prefix_afi_safi, vty_out(vty, "BGP updates debugging is on for %s\n", buf); } - prefix_free(argv_p); + prefix_free(&argv_p); return CMD_SUCCESS; } @@ -1477,7 +1490,7 @@ DEFPY (no_debug_bgp_update_prefix_afi_safi, ret = bgp_debug_parse_evpn_prefix(vty, argv, argc, &argv_p); if (ret != CMD_SUCCESS) { - prefix_free(argv_p); + prefix_free(&argv_p); return ret; } @@ -1505,7 +1518,7 @@ DEFPY (no_debug_bgp_update_prefix_afi_safi, vty_out(vty, "BGP updates debugging was not enabled for %s\n", buf); - prefix_free(argv_p); + prefix_free(&argv_p); return ret; } @@ -1644,6 +1657,23 @@ DEFUN (debug_bgp_zebra, return CMD_SUCCESS; } +DEFUN (debug_bgp_graceful_restart, + debug_bgp_graceful_restart_cmd, + "debug bgp graceful-restart", + DEBUG_STR + BGP_STR + GR_DEBUG) +{ + if (vty->node == CONFIG_NODE) { + DEBUG_ON(graceful_restart, GRACEFUL_RESTART); + } else { + TERM_DEBUG_ON(graceful_restart, GRACEFUL_RESTART); + vty_out(vty, "BGP Graceful Restart debugging is on\n"); + } + return CMD_SUCCESS; +} + + DEFUN (debug_bgp_zebra_prefix, debug_bgp_zebra_prefix_cmd, "debug bgp zebra prefix ", @@ -1702,6 +1732,23 @@ DEFUN (no_debug_bgp_zebra, return CMD_SUCCESS; } +DEFUN (no_debug_bgp_graceful_restart, + no_debug_bgp_graceful_restart_cmd, + "no debug bgp graceful-restart", + DEBUG_STR + BGP_STR + GR_DEBUG + NO_STR) +{ + if (vty->node == CONFIG_NODE) { + DEBUG_OFF(graceful_restart, GRACEFUL_RESTART); + } else { + TERM_DEBUG_OFF(graceful_restart, GRACEFUL_RESTART); + vty_out(vty, "BGP Graceful Restart debugging is off\n"); + } + return CMD_SUCCESS; +} + DEFUN (no_debug_bgp_zebra_prefix, no_debug_bgp_zebra_prefix_cmd, "no debug bgp zebra prefix ", @@ -1961,6 +2008,57 @@ DEFUN (no_debug_bgp_pbr, return CMD_SUCCESS; } +DEFPY (debug_bgp_evpn_mh, + debug_bgp_evpn_mh_cmd, + "[no$no] debug bgp evpn mh ", + NO_STR + DEBUG_STR + BGP_STR + "EVPN\n" + "Multihoming\n" + "Ethernet Segment debugging\n" + "Route debugging\n") +{ + if (es) { + if (vty->node == CONFIG_NODE) { + if (no) + DEBUG_OFF(evpn_mh, EVPN_MH_ES); + else + DEBUG_ON(evpn_mh, EVPN_MH_ES); + } else { + if (no) { + TERM_DEBUG_OFF(evpn_mh, EVPN_MH_ES); + vty_out(vty, + "BGP EVPN-MH ES debugging is off\n"); + } else { + TERM_DEBUG_ON(evpn_mh, EVPN_MH_ES); + vty_out(vty, + "BGP EVPN-MH ES debugging is on\n"); + } + } + } + if (rt) { + if (vty->node == CONFIG_NODE) { + if (no) + DEBUG_OFF(evpn_mh, EVPN_MH_RT); + else + DEBUG_ON(evpn_mh, EVPN_MH_RT); + } else { + if (no) { + TERM_DEBUG_OFF(evpn_mh, EVPN_MH_RT); + vty_out(vty, + "BGP EVPN-MH route debugging is off\n"); + } else { + TERM_DEBUG_ON(evpn_mh, EVPN_MH_RT); + vty_out(vty, + "BGP EVPN-MH route debugging is on\n"); + } + } + } + + return CMD_SUCCESS; +} + DEFUN (debug_bgp_labelpool, debug_bgp_labelpool_cmd, "debug bgp labelpool", @@ -2039,6 +2137,10 @@ DEFUN (no_debug_bgp, TERM_DEBUG_OFF(labelpool, LABELPOOL); TERM_DEBUG_OFF(pbr, PBR); TERM_DEBUG_OFF(pbr, PBR_ERROR); + TERM_DEBUG_OFF(graceful_restart, GRACEFUL_RESTART); + TERM_DEBUG_OFF(evpn_mh, EVPN_MH_ES); + TERM_DEBUG_OFF(evpn_mh, EVPN_MH_RT); + vty_out(vty, "All possible debugging has been turned off\n"); return CMD_SUCCESS; @@ -2096,6 +2198,9 @@ DEFUN_NOSH (show_debugging_bgp, bgp_debug_list_print(vty, " BGP zebra debugging is on", bgp_debug_zebra_prefixes); + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + vty_out(vty, " BGP graceful-restart debugging is on\n"); + if (BGP_DEBUG(allow_martians, ALLOW_MARTIANS)) vty_out(vty, " BGP allow martian next hop debugging is on\n"); @@ -2119,6 +2224,11 @@ DEFUN_NOSH (show_debugging_bgp, if (BGP_DEBUG(pbr, PBR_ERROR)) vty_out(vty, " BGP policy based routing error debugging is on\n"); + if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) + vty_out(vty, " BGP EVPN-MH ES debugging is on\n"); + if (BGP_DEBUG(evpn_mh, EVPN_MH_RT)) + vty_out(vty, " BGP EVPN-MH route debugging is on\n"); + vty_out(vty, "\n"); return CMD_SUCCESS; } @@ -2229,14 +2339,35 @@ static int bgp_config_write_debug(struct vty *vty) vty_out(vty, "debug bgp pbr error\n"); write++; } + + if (CONF_BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) { + vty_out(vty, "debug bgp graceful-restart\n"); + write++; + } + + if (CONF_BGP_DEBUG(evpn_mh, EVPN_MH_ES)) { + vty_out(vty, "debug bgp evpn mh es\n"); + write++; + } + if (CONF_BGP_DEBUG(evpn_mh, EVPN_MH_RT)) { + vty_out(vty, "debug bgp evpn mh route\n"); + write++; + } + return write; } -static struct cmd_node debug_node = {DEBUG_NODE, "", 1}; +static int bgp_config_write_debug(struct vty *vty); +static struct cmd_node debug_node = { + .name = "debug", + .node = DEBUG_NODE, + .prompt = "", + .config_write = bgp_config_write_debug, +}; void bgp_debug_init(void) { - install_node(&debug_node, bgp_config_write_debug); + install_node(&debug_node); install_element(ENABLE_NODE, &show_debugging_bgp_cmd); @@ -2262,6 +2393,9 @@ void bgp_debug_init(void) install_element(ENABLE_NODE, &debug_bgp_bestpath_prefix_cmd); install_element(CONFIG_NODE, &debug_bgp_bestpath_prefix_cmd); + install_element(ENABLE_NODE, &debug_bgp_graceful_restart_cmd); + install_element(CONFIG_NODE, &debug_bgp_graceful_restart_cmd); + /* debug bgp updates (in|out) */ install_element(ENABLE_NODE, &debug_bgp_update_direct_cmd); install_element(CONFIG_NODE, &debug_bgp_update_direct_cmd); @@ -2327,6 +2461,9 @@ void bgp_debug_init(void) install_element(ENABLE_NODE, &no_debug_bgp_bestpath_prefix_cmd); install_element(CONFIG_NODE, &no_debug_bgp_bestpath_prefix_cmd); + install_element(ENABLE_NODE, &no_debug_bgp_graceful_restart_cmd); + install_element(CONFIG_NODE, &no_debug_bgp_graceful_restart_cmd); + install_element(ENABLE_NODE, &debug_bgp_vpn_cmd); install_element(CONFIG_NODE, &debug_bgp_vpn_cmd); install_element(ENABLE_NODE, &no_debug_bgp_vpn_cmd); @@ -2343,12 +2480,14 @@ void bgp_debug_init(void) install_element(ENABLE_NODE, &no_debug_bgp_pbr_cmd); install_element(CONFIG_NODE, &no_debug_bgp_pbr_cmd); + install_element(ENABLE_NODE, &debug_bgp_evpn_mh_cmd); + install_element(CONFIG_NODE, &debug_bgp_evpn_mh_cmd); } /* Return true if this prefix is on the per_prefix_list of prefixes to debug * for BGP_DEBUG_TYPE */ -static int bgp_debug_per_prefix(struct prefix *p, +static int bgp_debug_per_prefix(const struct prefix *p, unsigned long term_bgp_debug_type, unsigned int BGP_DEBUG_TYPE, struct list *per_prefix_list) @@ -2433,8 +2572,8 @@ int bgp_debug_keepalive(struct peer *peer) bgp_debug_keepalive_peers); } -int bgp_debug_update(struct peer *peer, struct prefix *p, - struct update_group *updgrp, unsigned int inbound) +bool bgp_debug_update(struct peer *peer, const struct prefix *p, + struct update_group *updgrp, unsigned int inbound) { char *host = NULL; @@ -2445,7 +2584,7 @@ int bgp_debug_update(struct peer *peer, struct prefix *p, if (bgp_debug_per_peer(host, term_bgp_debug_update, BGP_DEBUG_UPDATE_IN, bgp_debug_update_in_peers)) - return 1; + return true; } /* outbound */ @@ -2453,12 +2592,12 @@ int bgp_debug_update(struct peer *peer, struct prefix *p, if (bgp_debug_per_peer(host, term_bgp_debug_update, BGP_DEBUG_UPDATE_OUT, bgp_debug_update_out_peers)) - return 1; + return true; /* Check if update debugging implicitly enabled for the group. */ if (updgrp && UPDGRP_DBG_ON(updgrp)) - return 1; + return true; } @@ -2466,38 +2605,38 @@ int bgp_debug_update(struct peer *peer, struct prefix *p, if (bgp_debug_per_prefix(p, term_bgp_debug_update, BGP_DEBUG_UPDATE_PREFIX, bgp_debug_update_prefixes)) - return 1; + return true; } - return 0; + return false; } -int bgp_debug_bestpath(struct prefix *p) +bool bgp_debug_bestpath(struct bgp_dest *dest) { if (BGP_DEBUG(bestpath, BESTPATH)) { - if (bgp_debug_per_prefix(p, term_bgp_debug_bestpath, - BGP_DEBUG_BESTPATH, - bgp_debug_bestpath_prefixes)) - return 1; + if (bgp_debug_per_prefix( + bgp_dest_get_prefix(dest), term_bgp_debug_bestpath, + BGP_DEBUG_BESTPATH, bgp_debug_bestpath_prefixes)) + return true; } - return 0; + return false; } -int bgp_debug_zebra(struct prefix *p) +bool bgp_debug_zebra(const struct prefix *p) { if (BGP_DEBUG(zebra, ZEBRA)) { if (bgp_debug_per_prefix(p, term_bgp_debug_zebra, BGP_DEBUG_ZEBRA, bgp_debug_zebra_prefixes)) - return 1; + return true; } - return 0; + return false; } const char *bgp_debug_rdpfxpath2str(afi_t afi, safi_t safi, - struct prefix_rd *prd, + const struct prefix_rd *prd, union prefixconstptr pu, mpls_label_t *label, uint32_t num_labels, int addpath_valid, uint32_t addpath_id, @@ -2530,12 +2669,14 @@ const char *bgp_debug_rdpfxpath2str(afi_t afi, safi_t safi, char tag_buf2[20]; bgp_evpn_label2str(label, num_labels, tag_buf2, 20); - sprintf(tag_buf, " label %s", tag_buf2); + snprintf(tag_buf, sizeof(tag_buf), " label %s", + tag_buf2); } else { uint32_t label_value; label_value = decode_label(label); - sprintf(tag_buf, " label %u", label_value); + snprintf(tag_buf, sizeof(tag_buf), " label %u", + label_value); } } @@ -2551,7 +2692,8 @@ const char *bgp_debug_rdpfxpath2str(afi_t afi, safi_t safi, bgp_fs_nlri_get_string((unsigned char *)fs->prefix.ptr, fs->prefix.prefixlen, return_string, - NLRI_STRING_FORMAT_DEBUG, NULL); + NLRI_STRING_FORMAT_DEBUG, NULL, + family2afi(fs->prefix.family)); snprintf(str, size, "FS %s Match{%s}", afi2str(afi), return_string); } else diff --git a/bgpd/bgp_debug.h b/bgpd/bgp_debug.h index e05da37647..f16cfee4f2 100644 --- a/bgpd/bgp_debug.h +++ b/bgpd/bgp_debug.h @@ -76,6 +76,8 @@ extern unsigned long conf_bgp_debug_vpn; extern unsigned long conf_bgp_debug_flowspec; extern unsigned long conf_bgp_debug_labelpool; extern unsigned long conf_bgp_debug_pbr; +extern unsigned long conf_bgp_debug_graceful_restart; +extern unsigned long conf_bgp_debug_evpn_mh; extern unsigned long term_bgp_debug_as4; extern unsigned long term_bgp_debug_neighbor_events; @@ -91,6 +93,8 @@ extern unsigned long term_bgp_debug_vpn; extern unsigned long term_bgp_debug_flowspec; extern unsigned long term_bgp_debug_labelpool; extern unsigned long term_bgp_debug_pbr; +extern unsigned long term_bgp_debug_graceful_restart; +extern unsigned long term_bgp_debug_evpn_mh; extern struct list *bgp_debug_neighbor_events_peers; extern struct list *bgp_debug_keepalive_peers; @@ -127,10 +131,14 @@ struct bgp_debug_filter { #define BGP_DEBUG_LABELPOOL 0x01 #define BGP_DEBUG_PBR 0x01 #define BGP_DEBUG_PBR_ERROR 0x02 +#define BGP_DEBUG_EVPN_MH_ES 0x01 +#define BGP_DEBUG_EVPN_MH_RT 0x02 #define BGP_DEBUG_PACKET_SEND 0x01 #define BGP_DEBUG_PACKET_SEND_DETAIL 0x02 +#define BGP_DEBUG_GRACEFUL_RESTART 0x01 + #define CONF_DEBUG_ON(a, b) (conf_bgp_debug_ ## a |= (BGP_DEBUG_ ## b)) #define CONF_DEBUG_OFF(a, b) (conf_bgp_debug_ ## a &= ~(BGP_DEBUG_ ## b)) @@ -151,11 +159,10 @@ struct bgp_debug_filter { #define BGP_DEBUG(a, b) (term_bgp_debug_ ## a & BGP_DEBUG_ ## b) #define CONF_BGP_DEBUG(a, b) (conf_bgp_debug_ ## a & BGP_DEBUG_ ## b) -extern const char *bgp_type_str[]; -extern const char *pmsi_tnltype_str[]; +extern const char *const bgp_type_str[]; -extern int bgp_dump_attr(struct attr *, char *, size_t); -extern int bgp_debug_peer_updout_enabled(char *host); +extern bool bgp_dump_attr(struct attr *, char *, size_t); +extern bool bgp_debug_peer_updout_enabled(char *host); extern const char *bgp_notify_code_str(char); extern const char *bgp_notify_subcode_str(char, char); extern void bgp_notify_print(struct peer *, struct bgp_notify *, const char *); @@ -163,15 +170,16 @@ extern void bgp_notify_print(struct peer *, struct bgp_notify *, const char *); extern const struct message bgp_status_msg[]; extern int bgp_debug_neighbor_events(struct peer *peer); extern int bgp_debug_keepalive(struct peer *peer); -extern int bgp_debug_update(struct peer *peer, struct prefix *p, - struct update_group *updgrp, unsigned int inbound); -extern int bgp_debug_bestpath(struct prefix *p); -extern int bgp_debug_zebra(struct prefix *p); - -extern const char *bgp_debug_rdpfxpath2str(afi_t, safi_t, struct prefix_rd *, - union prefixconstptr, mpls_label_t *, - uint32_t, int, uint32_t, char *, - int); +extern bool bgp_debug_update(struct peer *peer, const struct prefix *p, + struct update_group *updgrp, unsigned int inbound); +extern bool bgp_debug_bestpath(struct bgp_dest *dest); +extern bool bgp_debug_zebra(const struct prefix *p); + +extern const char * +bgp_debug_rdpfxpath2str(afi_t afi, safi_t safi, const struct prefix_rd *prd, + union prefixconstptr pu, mpls_label_t *label, + uint32_t num_labels, int addpath_valid, + uint32_t addpath_id, char *str, int size); const char *bgp_notify_admin_message(char *buf, size_t bufsz, uint8_t *data, size_t datalen); diff --git a/bgpd/bgp_dump.c b/bgpd/bgp_dump.c index 751140850a..abd349a188 100644 --- a/bgpd/bgp_dump.c +++ b/bgpd/bgp_dump.c @@ -37,6 +37,7 @@ #include "bgpd/bgp_attr.h" #include "bgpd/bgp_dump.h" #include "bgpd/bgp_errors.h" +#include "bgpd/bgp_packet.h" enum bgp_dump_type { BGP_DUMP_ALL, @@ -105,19 +106,20 @@ static FILE *bgp_dump_open_file(struct bgp_dump *bgp_dump) { int ret; time_t clock; - struct tm *tm; + struct tm tm; char fullpath[MAXPATHLEN]; char realpath[MAXPATHLEN]; mode_t oldumask; time(&clock); - tm = localtime(&clock); + localtime_r(&clock, &tm); if (bgp_dump->filename[0] != DIRECTORY_SEP) { - sprintf(fullpath, "%s/%s", vty_get_cwd(), bgp_dump->filename); - ret = strftime(realpath, MAXPATHLEN, fullpath, tm); + snprintf(fullpath, sizeof(fullpath), "%s/%s", vty_get_cwd(), + bgp_dump->filename); + ret = strftime(realpath, MAXPATHLEN, fullpath, &tm); } else - ret = strftime(realpath, MAXPATHLEN, bgp_dump->filename, tm); + ret = strftime(realpath, MAXPATHLEN, bgp_dump->filename, &tm); if (ret == 0) { flog_warn(EC_BGP_DUMP, "bgp_dump_open_file: strftime error"); @@ -146,7 +148,7 @@ static int bgp_dump_interval_add(struct bgp_dump *bgp_dump, int interval) { int secs_into_day; time_t t; - struct tm *tm; + struct tm tm; if (interval > 0) { /* Periodic dump every interval seconds */ @@ -157,9 +159,9 @@ static int bgp_dump_interval_add(struct bgp_dump *bgp_dump, int interval) * midnight */ (void)time(&t); - tm = localtime(&t); - secs_into_day = tm->tm_sec + 60 * tm->tm_min - + 60 * 60 * tm->tm_hour; + localtime_r(&t, &tm); + secs_into_day = tm.tm_sec + 60 * tm.tm_min + + 60 * 60 * tm.tm_hour; interval = interval - secs_into_day % interval; /* always > 0 */ } @@ -233,9 +235,9 @@ static void bgp_dump_routes_index_table(struct bgp *bgp) stream_put_in_addr(obuf, &bgp->router_id); /* View name */ - if (bgp->name) { - stream_putw(obuf, strlen(bgp->name)); - stream_put(obuf, bgp->name, strlen(bgp->name)); + if (bgp->name_pretty) { + stream_putw(obuf, strlen(bgp->name_pretty)); + stream_put(obuf, bgp->name_pretty, strlen(bgp->name_pretty)); } else { stream_putw(obuf, 0); } @@ -300,12 +302,13 @@ static void bgp_dump_routes_index_table(struct bgp *bgp) static struct bgp_path_info * -bgp_dump_route_node_record(int afi, struct bgp_node *rn, +bgp_dump_route_node_record(int afi, struct bgp_dest *dest, struct bgp_path_info *path, unsigned int seq) { struct stream *obuf; size_t sizep; size_t endp; + const struct prefix *p = bgp_dest_get_prefix(dest); obuf = bgp_dump_obuf; stream_reset(obuf); @@ -324,19 +327,19 @@ bgp_dump_route_node_record(int afi, struct bgp_node *rn, stream_putl(obuf, seq); /* Prefix length */ - stream_putc(obuf, rn->p.prefixlen); + stream_putc(obuf, p->prefixlen); /* Prefix */ if (afi == AFI_IP) { /* We'll dump only the useful bits (those not 0), but have to * align on 8 bits */ - stream_write(obuf, (uint8_t *)&rn->p.u.prefix4, - (rn->p.prefixlen + 7) / 8); + stream_write(obuf, (uint8_t *)&p->u.prefix4, + (p->prefixlen + 7) / 8); } else if (afi == AFI_IP6) { /* We'll dump only the useful bits (those not 0), but have to * align on 8 bits */ - stream_write(obuf, (uint8_t *)&rn->p.u.prefix6, - (rn->p.prefixlen + 7) / 8); + stream_write(obuf, (uint8_t *)&p->u.prefix6, + (p->prefixlen + 7) / 8); } /* Save where we are now, so we can overwride the entry count later */ @@ -360,7 +363,7 @@ bgp_dump_route_node_record(int afi, struct bgp_node *rn, /* Dump attribute. */ /* Skip prefix & AFI/SAFI for MP_NLRI */ - bgp_dump_routes_attr(obuf, path->attr, &rn->p); + bgp_dump_routes_attr(obuf, path->attr, p); cur_endp = stream_get_endp(obuf); if (cur_endp > BGP_MAX_PACKET_SIZE + BGP_DUMP_MSG_HEADER @@ -388,7 +391,7 @@ static unsigned int bgp_dump_routes_func(int afi, int first_run, unsigned int seq) { struct bgp_path_info *path; - struct bgp_node *rn; + struct bgp_dest *dest; struct bgp *bgp; struct bgp_table *table; @@ -409,10 +412,10 @@ static unsigned int bgp_dump_routes_func(int afi, int first_run, /* Walk down each BGP route. */ table = bgp->rib[afi][SAFI_UNICAST]; - for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) { - path = bgp_node_get_bgp_path_info(rn); + for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) { + path = bgp_dest_get_bgp_path_info(dest); while (path) { - path = bgp_dump_route_node_record(afi, rn, path, seq); + path = bgp_dump_route_node_record(afi, dest, path, seq); seq++; } } @@ -493,13 +496,13 @@ static void bgp_dump_common(struct stream *obuf, struct peer *peer, } /* Dump BGP status change. */ -void bgp_dump_state(struct peer *peer, int status_old, int status_new) +int bgp_dump_state(struct peer *peer) { struct stream *obuf; /* If dump file pointer is disabled return immediately. */ if (bgp_dump_all.fp == NULL) - return; + return 0; /* Make dump stream. */ obuf = bgp_dump_obuf; @@ -509,8 +512,8 @@ void bgp_dump_state(struct peer *peer, int status_old, int status_new) bgp_dump_all.type); bgp_dump_common(obuf, peer, 1); /* force this in as4speak*/ - stream_putw(obuf, status_old); - stream_putw(obuf, status_new); + stream_putw(obuf, peer->ostatus); + stream_putw(obuf, peer->status); /* Set length. */ bgp_dump_set_size(obuf, MSG_PROTOCOL_BGP4MP); @@ -518,6 +521,7 @@ void bgp_dump_state(struct peer *peer, int status_old, int status_new) /* Write to the stream. */ fwrite(STREAM_DATA(obuf), stream_get_endp(obuf), 1, bgp_dump_all.fp); fflush(bgp_dump_all.fp); + return 0; } static void bgp_dump_packet_func(struct bgp_dump *bgp_dump, struct peer *peer, @@ -555,7 +559,8 @@ static void bgp_dump_packet_func(struct bgp_dump *bgp_dump, struct peer *peer, } /* Called from bgp_packet.c when BGP packet is received. */ -void bgp_dump_packet(struct peer *peer, int type, struct stream *packet) +static int bgp_dump_packet(struct peer *peer, uint8_t type, bgp_size_t size, + struct stream *packet) { /* bgp_dump_all. */ bgp_dump_packet_func(&bgp_dump_all, peer, packet); @@ -563,6 +568,7 @@ void bgp_dump_packet(struct peer *peer, int type, struct stream *packet) /* bgp_dump_updates. */ if (type == BGP_MSG_UPDATE) bgp_dump_packet_func(&bgp_dump_updates, peer, packet); + return 0; } static unsigned int bgp_dump_parse_time(const char *str) @@ -581,7 +587,7 @@ static unsigned int bgp_dump_parse_time(const char *str) len = strlen(str); for (i = 0; i < len; i++) { - if (isdigit((int)str[i])) { + if (isdigit((unsigned char)str[i])) { time *= 10; time += str[i] - '0'; } else if (str[i] == 'H' || str[i] == 'h') { @@ -663,10 +669,7 @@ static int bgp_dump_set(struct vty *vty, struct bgp_dump *bgp_dump, static int bgp_dump_unset(struct bgp_dump *bgp_dump) { /* Removing file name. */ - if (bgp_dump->filename) { - XFREE(MTYPE_BGP_DUMP_STR, bgp_dump->filename); - bgp_dump->filename = NULL; - } + XFREE(MTYPE_BGP_DUMP_STR, bgp_dump->filename); /* Closing file. */ if (bgp_dump->fp) { @@ -683,10 +686,7 @@ static int bgp_dump_unset(struct bgp_dump *bgp_dump) bgp_dump->interval = 0; /* Removing interval string. */ - if (bgp_dump->interval_str) { - XFREE(MTYPE_BGP_DUMP_STR, bgp_dump->interval_str); - bgp_dump->interval_str = NULL; - } + XFREE(MTYPE_BGP_DUMP_STR, bgp_dump->interval_str); return CMD_SUCCESS; } @@ -778,34 +778,14 @@ DEFUN (no_dump_bgp_all, return bgp_dump_unset(bgp_dump_struct); } +static int config_write_bgp_dump(struct vty *vty); /* BGP node structure. */ -static struct cmd_node bgp_dump_node = {DUMP_NODE, "", 1}; - -#if 0 -char * -config_time2str (unsigned int interval) -{ - static char buf[BUFSIZ]; - - buf[0] = '\0'; - - if (interval / 3600) - { - sprintf (buf, "%dh", interval / 3600); - interval %= 3600; - } - if (interval / 60) - { - sprintf (buf + strlen (buf), "%dm", interval /60); - interval %= 60; - } - if (interval) - { - sprintf (buf + strlen (buf), "%d", interval); - } - return buf; -} -#endif +static struct cmd_node bgp_dump_node = { + .name = "dump", + .node = DUMP_NODE, + .prompt = "", + .config_write = config_write_bgp_dump, +}; static int config_write_bgp_dump(struct vty *vty) { @@ -858,10 +838,13 @@ void bgp_dump_init(void) stream_new((BGP_MAX_PACKET_SIZE << 1) + BGP_DUMP_MSG_HEADER + BGP_DUMP_HEADER_SIZE); - install_node(&bgp_dump_node, config_write_bgp_dump); + install_node(&bgp_dump_node); install_element(CONFIG_NODE, &dump_bgp_all_cmd); install_element(CONFIG_NODE, &no_dump_bgp_all_cmd); + + hook_register(bgp_packet_dump, bgp_dump_packet); + hook_register(peer_status_changed, bgp_dump_state); } void bgp_dump_finish(void) @@ -872,4 +855,6 @@ void bgp_dump_finish(void) stream_free(bgp_dump_obuf); bgp_dump_obuf = NULL; + hook_unregister(bgp_packet_dump, bgp_dump_packet); + hook_unregister(peer_status_changed, bgp_dump_state); } diff --git a/bgpd/bgp_dump.h b/bgpd/bgp_dump.h index f73081b2e2..86c80d481c 100644 --- a/bgpd/bgp_dump.h +++ b/bgpd/bgp_dump.h @@ -51,7 +51,6 @@ extern void bgp_dump_init(void); extern void bgp_dump_finish(void); -extern void bgp_dump_state(struct peer *, int, int); -extern void bgp_dump_packet(struct peer *, int, struct stream *); +extern int bgp_dump_state(struct peer *); #endif /* _QUAGGA_BGP_DUMP_H */ diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c index 8ef398952d..ea99f5f4ec 100644 --- a/bgpd/bgp_ecommunity.c +++ b/bgpd/bgp_ecommunity.c @@ -48,7 +48,12 @@ static struct hash *ecomhash; /* Allocate a new ecommunities. */ struct ecommunity *ecommunity_new(void) { - return XCALLOC(MTYPE_ECOMMUNITY, sizeof(struct ecommunity)); + struct ecommunity *ecom; + + ecom = (struct ecommunity *)XCALLOC(MTYPE_ECOMMUNITY, + sizeof(struct ecommunity)); + ecom->unit_size = ECOMMUNITY_SIZE; + return ecom; } void ecommunity_strfree(char **s) @@ -59,6 +64,9 @@ void ecommunity_strfree(char **s) /* Allocate ecommunities. */ void ecommunity_free(struct ecommunity **ecom) { + if (!(*ecom)) + return; + XFREE(MTYPE_ECOMMUNITY_VAL, (*ecom)->val); XFREE(MTYPE_ECOMMUNITY_STR, (*ecom)->str); XFREE(MTYPE_ECOMMUNITY, *ecom); @@ -74,88 +82,178 @@ static void ecommunity_hash_free(struct ecommunity *ecom) Attribute structure. When the value is already exists in the structure, we don't add the value. Newly added value is sorted by numerical order. When the value is added to the structure return 1 - else return 0. */ -int ecommunity_add_val(struct ecommunity *ecom, struct ecommunity_val *eval) + else return 0. + The additional parameters 'unique' and 'overwrite' ensure a particular + extended community (based on type and sub-type) is present only + once and whether the new value should replace what is existing or + not. +*/ +static bool ecommunity_add_val_internal(struct ecommunity *ecom, + const void *eval, + bool unique, bool overwrite, + uint8_t ecom_size) { - uint8_t *p; - int ret; - int c; + int c, ins_idx; + const struct ecommunity_val *eval4 = (struct ecommunity_val *)eval; + const struct ecommunity_val_ipv6 *eval6 = + (struct ecommunity_val_ipv6 *)eval; - /* When this is fist value, just add it. */ + /* When this is fist value, just add it. */ if (ecom->val == NULL) { - ecom->size++; - ecom->val = XMALLOC(MTYPE_ECOMMUNITY_VAL, ecom_length(ecom)); - memcpy(ecom->val, eval->val, ECOMMUNITY_SIZE); - return 1; + ecom->size = 1; + ecom->val = XMALLOC(MTYPE_ECOMMUNITY_VAL, + ecom_length_size(ecom, ecom_size)); + memcpy(ecom->val, eval, ecom_size); + return true; } /* If the value already exists in the structure return 0. */ + /* check also if the extended community itself exists. */ c = 0; - for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++) { - ret = memcmp(p, eval->val, ECOMMUNITY_SIZE); + + ins_idx = -1; + for (uint8_t *p = ecom->val; c < ecom->size; + p += ecom_size, c++) { + if (unique) { + if (ecom_size == ECOMMUNITY_SIZE) { + if (p[0] == eval4->val[0] && + p[1] == eval4->val[1]) { + if (overwrite) { + memcpy(p, eval4->val, + ecom_size); + return true; + } + return false; + } + } else { + if (p[0] == eval6->val[0] && + p[1] == eval6->val[1]) { + if (overwrite) { + memcpy(p, eval6->val, + ecom_size); + return true; + } + return false; + } + } + } + int ret = memcmp(p, eval, ecom_size); if (ret == 0) - return 0; - if (ret > 0) - break; + return false; + if (ret > 0) { + if (!unique) + break; + if (ins_idx == -1) + ins_idx = c; + } } + if (ins_idx == -1) + ins_idx = c; + /* Add the value to the structure with numerical sorting. */ ecom->size++; - ecom->val = - XREALLOC(MTYPE_ECOMMUNITY_VAL, ecom->val, ecom_length(ecom)); + ecom->val = XREALLOC(MTYPE_ECOMMUNITY_VAL, ecom->val, + ecom_length_size(ecom, ecom_size)); - memmove(ecom->val + (c + 1) * ECOMMUNITY_SIZE, - ecom->val + c * ECOMMUNITY_SIZE, - (ecom->size - 1 - c) * ECOMMUNITY_SIZE); - memcpy(ecom->val + c * ECOMMUNITY_SIZE, eval->val, ECOMMUNITY_SIZE); - return 1; + memmove(ecom->val + ((ins_idx + 1) * ecom_size), + ecom->val + (ins_idx * ecom_size), + (ecom->size - 1 - ins_idx) * ecom_size); + memcpy(ecom->val + (ins_idx * ecom_size), + eval, ecom_size); + + return true; } -/* This function takes pointer to Extended Communites strucutre then - create a new Extended Communities structure by uniq and sort each - Extended Communities value. */ -struct ecommunity *ecommunity_uniq_sort(struct ecommunity *ecom) +/* Add a new Extended Communities value to Extended Communities + * Attribute structure. When the value is already exists in the + * structure, we don't add the value. Newly added value is sorted by + * numerical order. When the value is added to the structure return 1 + * else return 0. + */ +bool ecommunity_add_val(struct ecommunity *ecom, struct ecommunity_val *eval, + bool unique, bool overwrite) +{ + return ecommunity_add_val_internal(ecom, (const void *)eval, unique, + overwrite, ECOMMUNITY_SIZE); +} + +bool ecommunity_add_val_ipv6(struct ecommunity *ecom, + struct ecommunity_val_ipv6 *eval, + bool unique, bool overwrite) +{ + return ecommunity_add_val_internal(ecom, (const void *)eval, unique, + overwrite, IPV6_ECOMMUNITY_SIZE); +} + +static struct ecommunity * +ecommunity_uniq_sort_internal(struct ecommunity *ecom, + unsigned short ecom_size) { int i; struct ecommunity *new; - struct ecommunity_val *eval; + const void *eval; if (!ecom) return NULL; new = ecommunity_new(); + new->unit_size = ecom_size; for (i = 0; i < ecom->size; i++) { - eval = (struct ecommunity_val *)(ecom->val - + (i * ECOMMUNITY_SIZE)); - ecommunity_add_val(new, eval); + eval = (void *)(ecom->val + (i * ecom_size)); + ecommunity_add_val_internal(new, eval, false, false, ecom_size); } return new; } +/* This function takes pointer to Extended Communites strucutre then + * create a new Extended Communities structure by uniq and sort each + * Extended Communities value. + */ +struct ecommunity *ecommunity_uniq_sort(struct ecommunity *ecom) +{ + return ecommunity_uniq_sort_internal(ecom, ECOMMUNITY_SIZE); +} + /* Parse Extended Communites Attribute in BGP packet. */ -struct ecommunity *ecommunity_parse(uint8_t *pnt, unsigned short length) +static struct ecommunity *ecommunity_parse_internal(uint8_t *pnt, + unsigned short length, + unsigned short size_ecom) { struct ecommunity tmp; struct ecommunity *new; /* Length check. */ - if (length % ECOMMUNITY_SIZE) + if (length % size_ecom) return NULL; /* Prepare tmporary structure for making a new Extended Communities Attribute. */ - tmp.size = length / ECOMMUNITY_SIZE; + tmp.size = length / size_ecom; tmp.val = pnt; /* Create a new Extended Communities Attribute by uniq and sort each Extended Communities value */ - new = ecommunity_uniq_sort(&tmp); + new = ecommunity_uniq_sort_internal(&tmp, size_ecom); return ecommunity_intern(new); } +struct ecommunity *ecommunity_parse(uint8_t *pnt, + unsigned short length) +{ + return ecommunity_parse_internal(pnt, length, ECOMMUNITY_SIZE); +} + +struct ecommunity *ecommunity_parse_ipv6(uint8_t *pnt, + unsigned short length) +{ + return ecommunity_parse_internal(pnt, length, + IPV6_ECOMMUNITY_SIZE); +} + /* Duplicate the Extended Communities Attribute structure. */ struct ecommunity *ecommunity_dup(struct ecommunity *ecom) { @@ -163,10 +261,12 @@ struct ecommunity *ecommunity_dup(struct ecommunity *ecom) new = XCALLOC(MTYPE_ECOMMUNITY, sizeof(struct ecommunity)); new->size = ecom->size; + new->unit_size = ecom->unit_size; if (new->size) { new->val = XMALLOC(MTYPE_ECOMMUNITY_VAL, - ecom->size * ECOMMUNITY_SIZE); - memcpy(new->val, ecom->val, ecom->size * ECOMMUNITY_SIZE); + ecom->size * ecom->unit_size); + memcpy(new->val, ecom->val, + (size_t)ecom->size * (size_t)ecom->unit_size); } else new->val = NULL; return new; @@ -186,16 +286,16 @@ struct ecommunity *ecommunity_merge(struct ecommunity *ecom1, struct ecommunity *ecom2) { if (ecom1->val) - ecom1->val = - XREALLOC(MTYPE_ECOMMUNITY_VAL, ecom1->val, - (ecom1->size + ecom2->size) * ECOMMUNITY_SIZE); + ecom1->val = XREALLOC(MTYPE_ECOMMUNITY_VAL, ecom1->val, + (size_t)(ecom1->size + ecom2->size) + * (size_t)ecom1->unit_size); else - ecom1->val = - XMALLOC(MTYPE_ECOMMUNITY_VAL, - (ecom1->size + ecom2->size) * ECOMMUNITY_SIZE); + ecom1->val = XMALLOC(MTYPE_ECOMMUNITY_VAL, + (size_t)(ecom1->size + ecom2->size) + * (size_t)ecom1->unit_size); - memcpy(ecom1->val + (ecom1->size * ECOMMUNITY_SIZE), ecom2->val, - ecom2->size * ECOMMUNITY_SIZE); + memcpy(ecom1->val + (ecom1->size * ecom1->unit_size), ecom2->val, + (size_t)ecom2->size * (size_t)ecom1->unit_size); ecom1->size += ecom2->size; return ecom1; @@ -207,9 +307,7 @@ struct ecommunity *ecommunity_intern(struct ecommunity *ecom) struct ecommunity *find; assert(ecom->refcnt == 0); - find = (struct ecommunity *)hash_get(ecomhash, ecom, hash_alloc_intern); - if (find != ecom) ecommunity_free(&ecom); @@ -241,10 +339,10 @@ void ecommunity_unintern(struct ecommunity **ecom) } /* Utinity function to make hash key. */ -unsigned int ecommunity_hash_make(void *arg) +unsigned int ecommunity_hash_make(const void *arg) { const struct ecommunity *ecom = arg; - int size = ecom->size * ECOMMUNITY_SIZE; + int size = ecom->size * ecom->unit_size; return jhash(ecom->val, size, 0x564321ab); } @@ -261,9 +359,12 @@ bool ecommunity_cmp(const void *arg1, const void *arg2) if (ecom1 == NULL || ecom2 == NULL) return false; + if (ecom1->unit_size != ecom2->unit_size) + return false; + return (ecom1->size == ecom2->size - && memcmp(ecom1->val, ecom2->val, ecom1->size * ECOMMUNITY_SIZE) - == 0); + && memcmp(ecom1->val, ecom2->val, ecom1->size * + ecom1->unit_size) == 0); } /* Initialize Extended Comminities related hash. */ @@ -286,16 +387,21 @@ enum ecommunity_token { ecommunity_token_rt, ecommunity_token_soo, ecommunity_token_val, + ecommunity_token_rt6, + ecommunity_token_val6, }; -/* - * Encode BGP extended community from passed values. Supports types - * defined in RFC 4360 and well-known sub-types. - */ -static int ecommunity_encode(uint8_t type, uint8_t sub_type, int trans, as_t as, - struct in_addr ip, uint32_t val, - struct ecommunity_val *eval) +static int ecommunity_encode_internal(uint8_t type, uint8_t sub_type, + int trans, as_t as, + struct in_addr *ip, + struct in6_addr *ip6, + uint32_t val, + void *eval_ptr) { + struct ecommunity_val *eval = (struct ecommunity_val *)eval_ptr; + struct ecommunity_val_ipv6 *eval6 = + (struct ecommunity_val_ipv6 *)eval_ptr; + assert(eval); if (type == ECOMMUNITY_ENCODE_AS) { if (as > BGP_AS_MAX) @@ -304,6 +410,10 @@ static int ecommunity_encode(uint8_t type, uint8_t sub_type, int trans, as_t as, || type == ECOMMUNITY_ENCODE_AS4) { if (val > UINT16_MAX) return -1; + } else if (type == ECOMMUNITY_ENCODE_TRANS_EXP && + sub_type == ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6 && + (!ip6 || val > UINT16_MAX)) { + return -1; } /* Fill in the values. */ @@ -319,9 +429,14 @@ static int ecommunity_encode(uint8_t type, uint8_t sub_type, int trans, as_t as, eval->val[6] = (val >> 8) & 0xff; eval->val[7] = val & 0xff; } else if (type == ECOMMUNITY_ENCODE_IP) { - memcpy(&eval->val[2], &ip, sizeof(struct in_addr)); + memcpy(&eval->val[2], ip, sizeof(struct in_addr)); eval->val[6] = (val >> 8) & 0xff; eval->val[7] = val & 0xff; + } else if (type == ECOMMUNITY_ENCODE_TRANS_EXP && + sub_type == ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6) { + memcpy(&eval6->val[2], ip6, sizeof(struct in6_addr)); + eval6->val[18] = (val >> 8) & 0xff; + eval6->val[19] = val & 0xff; } else { eval->val[2] = (as >> 24) & 0xff; eval->val[3] = (as >> 16) & 0xff; @@ -334,9 +449,21 @@ static int ecommunity_encode(uint8_t type, uint8_t sub_type, int trans, as_t as, return 0; } +/* + * Encode BGP extended community from passed values. Supports types + * defined in RFC 4360 and well-known sub-types. + */ +static int ecommunity_encode(uint8_t type, uint8_t sub_type, int trans, as_t as, + struct in_addr ip, uint32_t val, + struct ecommunity_val *eval) +{ + return ecommunity_encode_internal(type, sub_type, trans, as, + &ip, NULL, val, (void *)eval); +} + /* Get next Extended Communities token from the string. */ static const char *ecommunity_gettoken(const char *str, - struct ecommunity_val *eval, + void *eval_ptr, enum ecommunity_token *token) { int ret; @@ -346,13 +473,14 @@ static const char *ecommunity_gettoken(const char *str, const char *p = str; char *endptr; struct in_addr ip; + struct in6_addr ip6; as_t as = 0; uint32_t val = 0; uint8_t ecomm_type; char buf[INET_ADDRSTRLEN + 1]; - + struct ecommunity_val *eval = (struct ecommunity_val *)eval_ptr; /* Skip white space. */ - while (isspace((int)*p)) { + while (isspace((unsigned char)*p)) { p++; str++; } @@ -362,38 +490,41 @@ static const char *ecommunity_gettoken(const char *str, return NULL; /* "rt" and "soo" keyword parse. */ - if (!isdigit((int)*p)) { + if (!isdigit((unsigned char)*p)) { /* "rt" match check. */ - if (tolower((int)*p) == 'r') { + if (tolower((unsigned char)*p) == 'r') { p++; - if (tolower((int)*p) == 't') { + if (tolower((unsigned char)*p) == 't') { p++; - *token = ecommunity_token_rt; + if (*p != '\0' && tolower((int)*p) == '6') + *token = ecommunity_token_rt6; + else + *token = ecommunity_token_rt; return p; } - if (isspace((int)*p) || *p == '\0') { + if (isspace((unsigned char)*p) || *p == '\0') { *token = ecommunity_token_rt; return p; } goto error; } /* "soo" match check. */ - else if (tolower((int)*p) == 's') { + else if (tolower((unsigned char)*p) == 's') { p++; - if (tolower((int)*p) == 'o') { + if (tolower((unsigned char)*p) == 'o') { p++; - if (tolower((int)*p) == 'o') { + if (tolower((unsigned char)*p) == 'o') { p++; *token = ecommunity_token_soo; return p; } - if (isspace((int)*p) || *p == '\0') { + if (isspace((unsigned char)*p) || *p == '\0') { *token = ecommunity_token_soo; return p; } goto error; } - if (isspace((int)*p) || *p == '\0') { + if (isspace((unsigned char)*p) || *p == '\0') { *token = ecommunity_token_soo; return p; } @@ -407,6 +538,7 @@ static const char *ecommunity_gettoken(const char *str, * a) A.B.C.D:MN * b) EF:OPQR * c) GHJK:MN + * d) :MN (only with rt6) * * A.B.C.D: Four Byte IP * EF: Two byte ASN @@ -415,7 +547,39 @@ static const char *ecommunity_gettoken(const char *str, * OPQR: Four byte value * */ - while (isdigit((int)*p) || *p == ':' || *p == '.') { + /* IPv6 case : look for last ':' */ + if (*token == ecommunity_token_rt6 || + *token == ecommunity_token_val6) { + char *limit; + + limit = endptr = strrchr(p, ':'); + if (!endptr) + goto error; + + endptr++; + as = strtoul(endptr, &endptr, 10); + if (*endptr != '\0' || as == BGP_AS4_MAX) + goto error; + + memcpy(buf, p, (limit - p)); + buf[limit - p] = '\0'; + ret = inet_pton(AF_INET6, buf, &ip6); + if (ret == 0) + goto error; + + ecomm_type = ECOMMUNITY_ENCODE_TRANS_EXP; + if (ecommunity_encode_internal(ecomm_type, + ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6, + 1, 0, NULL, &ip6, as, eval_ptr)) + goto error; + + *token = ecommunity_token_val6; + while (isdigit((int)*p) || *p == ':' || *p == '.') { + p++; + } + return p; + } + while (isdigit((unsigned char)*p) || *p == ':' || *p == '.') { if (*p == ':') { if (separator) goto error; @@ -480,41 +644,18 @@ static const char *ecommunity_gettoken(const char *str, return p; } -/* Convert string to extended community attribute. - - When type is already known, please specify both str and type. str - should not include keyword such as "rt" and "soo". Type is - ECOMMUNITY_ROUTE_TARGET or ECOMMUNITY_SITE_ORIGIN. - keyword_included should be zero. - - For example route-map's "set extcommunity" command case: - - "rt 100:1 100:2 100:3" -> str = "100:1 100:2 100:3" - type = ECOMMUNITY_ROUTE_TARGET - keyword_included = 0 - - "soo 100:1" -> str = "100:1" - type = ECOMMUNITY_SITE_ORIGIN - keyword_included = 0 - - When string includes keyword for each extended community value. - Please specify keyword_included as non-zero value. - - For example standard extcommunity-list case: - - "rt 100:1 rt 100:2 soo 100:1" -> str = "rt 100:1 rt 100:2 soo 100:1" - type = 0 - keyword_include = 1 -*/ -struct ecommunity *ecommunity_str2com(const char *str, int type, - int keyword_included) +static struct ecommunity *ecommunity_str2com_internal(const char *str, int type, + int keyword_included, + bool is_ipv6_extcomm) { struct ecommunity *ecom = NULL; enum ecommunity_token token = ecommunity_token_unknown; - struct ecommunity_val eval; + struct ecommunity_val_ipv6 eval; int keyword = 0; - while ((str = ecommunity_gettoken(str, &eval, &token))) { + if (is_ipv6_extcomm) + token = ecommunity_token_rt6; + while ((str = ecommunity_gettoken(str, (void *)&eval, &token))) { switch (token) { case ecommunity_token_rt: case ecommunity_token_soo: @@ -525,7 +666,8 @@ struct ecommunity *ecommunity_str2com(const char *str, int type, } keyword = 1; - if (token == ecommunity_token_rt) { + if (token == ecommunity_token_rt || + token == ecommunity_token_rt6) { type = ECOMMUNITY_ROUTE_TARGET; } if (token == ecommunity_token_soo) { @@ -535,8 +677,7 @@ struct ecommunity *ecommunity_str2com(const char *str, int type, case ecommunity_token_val: if (keyword_included) { if (!keyword) { - if (ecom) - ecommunity_free(&ecom); + ecommunity_free(&ecom); return NULL; } keyword = 0; @@ -544,7 +685,24 @@ struct ecommunity *ecommunity_str2com(const char *str, int type, if (ecom == NULL) ecom = ecommunity_new(); eval.val[1] = type; - ecommunity_add_val(ecom, &eval); + ecommunity_add_val_internal(ecom, (void *)&eval, + false, false, + ecom->unit_size); + break; + case ecommunity_token_val6: + if (keyword_included) { + if (!keyword) { + ecommunity_free(&ecom); + return NULL; + } + keyword = 0; + } + if (ecom == NULL) + ecom = ecommunity_new(); + ecom->unit_size = IPV6_ECOMMUNITY_SIZE; + eval.val[1] = type; + ecommunity_add_val_internal(ecom, (void *)&eval, false, false, + ecom->unit_size); break; case ecommunity_token_unknown: default: @@ -556,16 +714,59 @@ struct ecommunity *ecommunity_str2com(const char *str, int type, return ecom; } -static int ecommunity_rt_soo_str(char *buf, uint8_t *pnt, int type, - int sub_type, int format) +/* Convert string to extended community attribute. + * + * When type is already known, please specify both str and type. str + * should not include keyword such as "rt" and "soo". Type is + * ECOMMUNITY_ROUTE_TARGET or ECOMMUNITY_SITE_ORIGIN. + * keyword_included should be zero. + * + * For example route-map's "set extcommunity" command case: + * + * "rt 100:1 100:2 100:3" -> str = "100:1 100:2 100:3" + * type = ECOMMUNITY_ROUTE_TARGET + * keyword_included = 0 + * + * "soo 100:1" -> str = "100:1" + * type = ECOMMUNITY_SITE_ORIGIN + * keyword_included = 0 + * + * When string includes keyword for each extended community value. + * Please specify keyword_included as non-zero value. + * + * For example standard extcommunity-list case: + * + * "rt 100:1 rt 100:2 soo 100:1" -> str = "rt 100:1 rt 100:2 soo 100:1" + * type = 0 + * keyword_include = 1 + */ +struct ecommunity *ecommunity_str2com(const char *str, int type, + int keyword_included) +{ + return ecommunity_str2com_internal(str, type, + keyword_included, false); +} + +struct ecommunity *ecommunity_str2com_ipv6(const char *str, int type, + int keyword_included) +{ + return ecommunity_str2com_internal(str, type, + keyword_included, true); +} + +static int ecommunity_rt_soo_str_internal(char *buf, size_t bufsz, + const uint8_t *pnt, int type, + int sub_type, int format, + unsigned short ecom_size) { int len = 0; const char *prefix; + char buf_local[INET6_ADDRSTRLEN]; /* For parse Extended Community attribute tupple. */ struct ecommunity_as eas; struct ecommunity_ip eip; - + struct ecommunity_ip6 eip6; /* Determine prefix for string, if any. */ switch (format) { @@ -589,40 +790,93 @@ static int ecommunity_rt_soo_str(char *buf, uint8_t *pnt, int type, eas.val = (*pnt++ << 8); eas.val |= (*pnt++); - len = sprintf(buf, "%s%u:%u", prefix, eas.as, eas.val); + len = snprintf(buf, bufsz, "%s%u:%u", prefix, eas.as, eas.val); } else if (type == ECOMMUNITY_ENCODE_AS) { - eas.as = (*pnt++ << 8); - eas.as |= (*pnt++); - pnt = ptr_get_be32(pnt, &eas.val); + if (ecom_size == ECOMMUNITY_SIZE) { + eas.as = (*pnt++ << 8); + eas.as |= (*pnt++); + pnt = ptr_get_be32(pnt, &eas.val); - len = sprintf(buf, "%s%u:%u", prefix, eas.as, eas.val); + len = snprintf(buf, bufsz, "%s%u:%u", prefix, eas.as, + eas.val); + } else { + /* this is an IPv6 ext community + * first 16 bytes stands for IPv6 addres + */ + memcpy(&eip6.ip, pnt, 16); + pnt += 16; + eip6.val = (*pnt++ << 8); + eip6.val |= (*pnt++); + + inet_ntop(AF_INET6, &eip6.ip, buf_local, + sizeof(buf_local)); + len = snprintf(buf, bufsz, "%s%s:%u", prefix, + buf_local, eip6.val); + } } else if (type == ECOMMUNITY_ENCODE_IP) { memcpy(&eip.ip, pnt, 4); pnt += 4; eip.val = (*pnt++ << 8); eip.val |= (*pnt++); - len = sprintf(buf, "%s%s:%u", prefix, inet_ntoa(eip.ip), - eip.val); + len = snprintf(buf, bufsz, "%s%s:%u", prefix, inet_ntoa(eip.ip), + eip.val); } - (void)pnt; /* consume value */ + + /* consume value */ + (void)pnt; return len; } +static int ecommunity_rt_soo_str(char *buf, size_t bufsz, const uint8_t *pnt, + int type, int sub_type, int format) +{ + return ecommunity_rt_soo_str_internal(buf, bufsz, pnt, type, + sub_type, format, + ECOMMUNITY_SIZE); +} + +static int ecommunity_lb_str(char *buf, size_t bufsz, const uint8_t *pnt) +{ + int len = 0; + as_t as; + uint32_t bw; + char bps_buf[20] = {0}; + +#define ONE_GBPS_BYTES (1000 * 1000 * 1000 / 8) +#define ONE_MBPS_BYTES (1000 * 1000 / 8) +#define ONE_KBPS_BYTES (1000 / 8) + + as = (*pnt++ << 8); + as |= (*pnt++); + (void)ptr_get_be32(pnt, &bw); + if (bw >= ONE_GBPS_BYTES) + snprintf(bps_buf, sizeof(bps_buf), "%.3f Gbps", + (float)(bw / ONE_GBPS_BYTES)); + else if (bw >= ONE_MBPS_BYTES) + snprintf(bps_buf, sizeof(bps_buf), "%.3f Mbps", + (float)(bw / ONE_MBPS_BYTES)); + else if (bw >= ONE_KBPS_BYTES) + snprintf(bps_buf, sizeof(bps_buf), "%.3f Kbps", + (float)(bw / ONE_KBPS_BYTES)); + else + snprintf(bps_buf, sizeof(bps_buf), "%u bps", bw * 8); + + len = snprintf(buf, bufsz, "LB:%u:%u (%s)", as, bw, bps_buf); + return len; +} + /* Convert extended community attribute to string. Due to historical reason of industry standard implementation, there are three types of format. route-map set extcommunity format - "rt 100:1 100:2" - "soo 100:3" + "rt 100:1 100:2soo 100:3" extcommunity-list - "rt 100:1 rt 100:2 soo 100:3" - - "show [ip] bgp" and extcommunity-list regular expression matching + "rt 100:1 rt 100:2 soo 100:3show [ip] bgp" and extcommunity-list regular expression matching "RT:100:1 RT:100:2 SoO:100:3" For each formath please use below definition for format: @@ -640,44 +894,31 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter) uint8_t *pnt; uint8_t type = 0; uint8_t sub_type = 0; -#define ECOMMUNITY_STR_DEFAULT_LEN 64 +#define ECOMMUNITY_STRLEN 64 int str_size; - int str_pnt; char *str_buf; - int len = 0; - int first = 1; - if (ecom->size == 0) { - str_buf = XMALLOC(MTYPE_ECOMMUNITY_STR, 1); - str_buf[0] = '\0'; - return str_buf; - } + if (!ecom || ecom->size == 0) + return XCALLOC(MTYPE_ECOMMUNITY_STR, 1); + + /* ecom strlen + space + null term */ + str_size = (ecom->size * (ECOMMUNITY_STRLEN + 1)) + 1; + str_buf = XCALLOC(MTYPE_ECOMMUNITY_STR, str_size); - /* Prepare buffer. */ - str_buf = XMALLOC(MTYPE_ECOMMUNITY_STR, ECOMMUNITY_STR_DEFAULT_LEN + 1); - str_size = ECOMMUNITY_STR_DEFAULT_LEN + 1; - str_buf[0] = '\0'; - str_pnt = 0; + char encbuf[128]; for (i = 0; i < ecom->size; i++) { int unk_ecom = 0; - - /* Make it sure size is enough. */ - while (str_pnt + ECOMMUNITY_STR_DEFAULT_LEN >= str_size) { - str_size *= 2; - str_buf = XREALLOC(MTYPE_ECOMMUNITY_STR, str_buf, - str_size); - } + memset(encbuf, 0x00, sizeof(encbuf)); /* Space between each value. */ - if (!first) { - str_buf[str_pnt++] = ' '; - len++; - } + if (i > 0) + strlcat(str_buf, " ", str_size); + /* Retrieve value field */ pnt = ecom->val + (i * 8); - /* High-order octet of type. */ + /* High-order octet is the type */ type = *pnt++; if (type == ECOMMUNITY_ENCODE_AS || type == ECOMMUNITY_ENCODE_IP @@ -696,15 +937,20 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter) inet_ntop(AF_INET, ipv4, ipv4str, INET_ADDRSTRLEN); - len = sprintf(str_buf + str_pnt, - "NH:%s:%d", - ipv4str, pnt[5]); + snprintf(encbuf, sizeof(encbuf), + "NH:%s:%d", ipv4str, pnt[5]); + } else if (sub_type == + ECOMMUNITY_LINK_BANDWIDTH && + type == ECOMMUNITY_ENCODE_AS) { + ecommunity_lb_str(encbuf, + sizeof(encbuf), pnt); } else unk_ecom = 1; - } else - len = ecommunity_rt_soo_str(str_buf + str_pnt, - pnt, type, sub_type, - format); + } else { + ecommunity_rt_soo_str(encbuf, sizeof(encbuf), + pnt, type, sub_type, + format); + } } else if (type == ECOMMUNITY_ENCODE_OPAQUE) { if (filter == ECOMMUNITY_ROUTE_TARGET) continue; @@ -712,13 +958,15 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter) uint16_t tunneltype; memcpy(&tunneltype, pnt + 5, 2); tunneltype = ntohs(tunneltype); - len = sprintf(str_buf + str_pnt, "ET:%d", - tunneltype); + + snprintf(encbuf, sizeof(encbuf), "ET:%d", + tunneltype); } else if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_DEF_GW) { - len = sprintf(str_buf + str_pnt, - "Default Gateway"); - } else + strlcpy(encbuf, "Default Gateway", + sizeof(encbuf)); + } else { unk_ecom = 1; + } } else if (type == ECOMMUNITY_ENCODE_EVPN) { if (filter == ECOMMUNITY_ROUTE_TARGET) continue; @@ -726,15 +974,15 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter) struct ethaddr rmac; pnt++; memcpy(&rmac, pnt, ETH_ALEN); - len = sprintf( - str_buf + str_pnt, - "Rmac:%02x:%02x:%02x:%02x:%02x:%02x", - (uint8_t)rmac.octet[0], - (uint8_t)rmac.octet[1], - (uint8_t)rmac.octet[2], - (uint8_t)rmac.octet[3], - (uint8_t)rmac.octet[4], - (uint8_t)rmac.octet[5]); + + snprintf(encbuf, sizeof(encbuf), + "Rmac:%02x:%02x:%02x:%02x:%02x:%02x", + (uint8_t)rmac.octet[0], + (uint8_t)rmac.octet[1], + (uint8_t)rmac.octet[2], + (uint8_t)rmac.octet[3], + (uint8_t)rmac.octet[4], + (uint8_t)rmac.octet[5]); } else if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY) { uint32_t seqnum; @@ -742,68 +990,127 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter) memcpy(&seqnum, pnt + 2, 4); seqnum = ntohl(seqnum); - if (flags - & ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY_FLAG_STICKY) - len = sprintf(str_buf + str_pnt, - "MM:%u, sticky MAC", - seqnum); - else - len = sprintf(str_buf + str_pnt, - "MM:%u", seqnum); + + snprintf(encbuf, sizeof(encbuf), "MM:%u", + seqnum); + + if (CHECK_FLAG( + flags, + ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY_FLAG_STICKY)) + strlcat(encbuf, ", sticky MAC", + sizeof(encbuf)); } else if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_ND) { uint8_t flags = *++pnt; - if (flags - & ECOMMUNITY_EVPN_SUBTYPE_ND_ROUTER_FLAG) - len = sprintf(str_buf + str_pnt, - "ND:Router Flag"); + if (CHECK_FLAG( + flags, + ECOMMUNITY_EVPN_SUBTYPE_ND_ROUTER_FLAG)) + strlcpy(encbuf, "ND:Router Flag", + sizeof(encbuf)); + if (CHECK_FLAG( + flags, + ECOMMUNITY_EVPN_SUBTYPE_PROXY_FLAG)) + strlcpy(encbuf, "ND:Proxy", + sizeof(encbuf)); + } else if (*pnt + == ECOMMUNITY_EVPN_SUBTYPE_ES_IMPORT_RT) { + struct ethaddr mac; + + pnt++; + memcpy(&mac, pnt, ETH_ALEN); + snprintf(encbuf, + sizeof(encbuf), + "ES-Import-Rt:%02x:%02x:%02x:%02x:%02x:%02x", + (uint8_t)mac.octet[0], + (uint8_t)mac.octet[1], + (uint8_t)mac.octet[2], + (uint8_t)mac.octet[3], + (uint8_t)mac.octet[4], + (uint8_t)mac.octet[5]); + } else if (*pnt + == ECOMMUNITY_EVPN_SUBTYPE_ESI_LABEL) { + uint8_t flags = *++pnt; + + snprintf(encbuf, + sizeof(encbuf), "ESI-label-Rt:%s", + (flags & + ECOMMUNITY_EVPN_SUBTYPE_ESI_SA_FLAG) ? + "SA":"AA"); } else unk_ecom = 1; } else if (type == ECOMMUNITY_ENCODE_REDIRECT_IP_NH) { sub_type = *pnt++; if (sub_type == ECOMMUNITY_REDIRECT_IP_NH) { - len = sprintf( - str_buf + str_pnt, - "FS:redirect IP 0x%x", *(pnt+5)); + snprintf(encbuf, sizeof(encbuf), + "FS:redirect IP 0x%x", *(pnt + 5)); } else unk_ecom = 1; } else if (type == ECOMMUNITY_ENCODE_TRANS_EXP || type == ECOMMUNITY_EXTENDED_COMMUNITY_PART_2 || type == ECOMMUNITY_EXTENDED_COMMUNITY_PART_3) { sub_type = *pnt++; - if (sub_type == ECOMMUNITY_REDIRECT_VRF) { + + if (sub_type == ECOMMUNITY_ROUTE_TARGET) { + char buf[ECOMMUNITY_STRLEN]; + + memset(buf, 0, sizeof(buf)); + ecommunity_rt_soo_str_internal(buf, sizeof(buf), + (const uint8_t *)pnt, + type & + ~ECOMMUNITY_ENCODE_TRANS_EXP, + ECOMMUNITY_ROUTE_TARGET, + format, + ecom->unit_size); + snprintf(encbuf, sizeof(encbuf), "%s", buf); + } else if (sub_type == + ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6) { + char buf[64]; + + memset(buf, 0, sizeof(buf)); + ecommunity_rt_soo_str_internal(buf, sizeof(buf), + (const uint8_t *)pnt, + type & + ~ECOMMUNITY_ENCODE_TRANS_EXP, + ECOMMUNITY_ROUTE_TARGET, + ECOMMUNITY_FORMAT_DISPLAY, + ecom->unit_size); + snprintf(encbuf, sizeof(encbuf), + "FS:redirect VRF %s", buf); + } else if (sub_type == ECOMMUNITY_REDIRECT_VRF) { char buf[16]; memset(buf, 0, sizeof(buf)); - ecommunity_rt_soo_str(buf, (uint8_t *)pnt, - type & - ~ECOMMUNITY_ENCODE_TRANS_EXP, - ECOMMUNITY_ROUTE_TARGET, - ECOMMUNITY_FORMAT_DISPLAY); - len = snprintf(str_buf + str_pnt, - str_size - len, - "FS:redirect VRF %s", buf); + ecommunity_rt_soo_str(buf, sizeof(buf), + (const uint8_t *)pnt, + type & + ~ECOMMUNITY_ENCODE_TRANS_EXP, + ECOMMUNITY_ROUTE_TARGET, + ECOMMUNITY_FORMAT_DISPLAY); + snprintf(encbuf, sizeof(encbuf), + "FS:redirect VRF %s", buf); + snprintf(encbuf, sizeof(encbuf), + "FS:redirect VRF %s", buf); } else if (type != ECOMMUNITY_ENCODE_TRANS_EXP) unk_ecom = 1; else if (sub_type == ECOMMUNITY_TRAFFIC_ACTION) { char action[64]; - char *ptr = action; if (*(pnt+3) == 1 << FLOWSPEC_TRAFFIC_ACTION_TERMINAL) - ptr += snprintf(ptr, sizeof(action), - "terminate (apply)"); + strlcpy(action, "terminate (apply)", + sizeof(action)); else - ptr += snprintf(ptr, sizeof(action), - "eval stops"); + strlcpy(action, "eval stops", + sizeof(action)); + if (*(pnt+3) == 1 << FLOWSPEC_TRAFFIC_ACTION_SAMPLE) - snprintf(ptr, sizeof(action) - - (size_t)(ptr-action), - ", sample"); - len = snprintf(str_buf + str_pnt, - str_size - len, - "FS:action %s", action); + strlcat(action, ", sample", + sizeof(action)); + + + snprintf(encbuf, sizeof(encbuf), "FS:action %s", + action); } else if (sub_type == ECOMMUNITY_TRAFFIC_RATE) { union traffic_rate data; @@ -811,74 +1118,64 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter) data.rate_byte[2] = *(pnt+3); data.rate_byte[1] = *(pnt+4); data.rate_byte[0] = *(pnt+5); - len = sprintf( - str_buf + str_pnt, - "FS:rate %f", data.rate_float); + snprintf(encbuf, sizeof(encbuf), "FS:rate %f", + data.rate_float); } else if (sub_type == ECOMMUNITY_TRAFFIC_MARKING) { - len = sprintf( - str_buf + str_pnt, - "FS:marking %u", *(pnt+5)); - } else if (*pnt - == ECOMMUNITY_EVPN_SUBTYPE_ES_IMPORT_RT) { - struct ethaddr mac; - - pnt++; - memcpy(&mac, pnt, ETH_ALEN); - len = sprintf( - str_buf + str_pnt, - "ES-Import-Rt:%02x:%02x:%02x:%02x:%02x:%02x", - (uint8_t)mac.octet[0], - (uint8_t)mac.octet[1], - (uint8_t)mac.octet[2], - (uint8_t)mac.octet[3], - (uint8_t)mac.octet[4], - (uint8_t)mac.octet[5]); + snprintf(encbuf, sizeof(encbuf), + "FS:marking %u", *(pnt + 5)); } else unk_ecom = 1; + } else if (type == ECOMMUNITY_ENCODE_AS_NON_TRANS) { + sub_type = *pnt++; + if (sub_type == ECOMMUNITY_LINK_BANDWIDTH) + ecommunity_lb_str(encbuf, sizeof(encbuf), pnt); + else + unk_ecom = 1; } else { sub_type = *pnt++; unk_ecom = 1; } if (unk_ecom) - len = sprintf(str_buf + str_pnt, "UNK:%d, %d", - type, sub_type); + snprintf(encbuf, sizeof(encbuf), "UNK:%d, %d", type, + sub_type); - str_pnt += len; - first = 0; + int r = strlcat(str_buf, encbuf, str_size); + assert(r < str_size); } return str_buf; } -int ecommunity_match(const struct ecommunity *ecom1, - const struct ecommunity *ecom2) +bool ecommunity_match(const struct ecommunity *ecom1, + const struct ecommunity *ecom2) { int i = 0; int j = 0; if (ecom1 == NULL && ecom2 == NULL) - return 1; + return true; if (ecom1 == NULL || ecom2 == NULL) - return 0; + return false; if (ecom1->size < ecom2->size) - return 0; + return false; /* Every community on com2 needs to be on com1 for this to match */ while (i < ecom1->size && j < ecom2->size) { - if (memcmp(ecom1->val + i * ECOMMUNITY_SIZE, - ecom2->val + j * ECOMMUNITY_SIZE, ECOMMUNITY_SIZE) + if (memcmp(ecom1->val + i * ecom1->unit_size, + ecom2->val + j * ecom2->unit_size, + ecom2->unit_size) == 0) j++; i++; } if (j == ecom2->size) - return 1; + return true; else - return 0; + return false; } /* return first occurence of type */ @@ -890,7 +1187,7 @@ extern struct ecommunity_val *ecommunity_lookup(const struct ecommunity *ecom, /* If the value already exists in the structure return 0. */ c = 0; - for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++) { + for (p = ecom->val; c < ecom->size; p += ecom->unit_size, c++) { if (p == NULL) { continue; } @@ -903,80 +1200,90 @@ extern struct ecommunity_val *ecommunity_lookup(const struct ecommunity *ecom, /* remove ext. community matching type and subtype * return 1 on success ( removed ), 0 otherwise (not present) */ -extern int ecommunity_strip(struct ecommunity *ecom, uint8_t type, - uint8_t subtype) +bool ecommunity_strip(struct ecommunity *ecom, uint8_t type, + uint8_t subtype) { - uint8_t *p; + uint8_t *p, *q, *new; int c, found = 0; /* When this is fist value, just add it. */ - if (ecom == NULL || ecom->val == NULL) { - return 0; - } + if (ecom == NULL || ecom->val == NULL) + return false; - /* If the value already exists in the structure return 0. */ + /* Check if any existing ext community matches. */ + /* Certain extended communities like the Route Target can be present + * multiple times, handle that. + */ c = 0; - for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++) { - if (p[0] == type && p[1] == subtype) { - found = 1; - break; - } + for (p = ecom->val; c < ecom->size; p += ecom->unit_size, c++) { + if (p[0] == type && p[1] == subtype) + found++; } + /* If no matching ext community exists, return. */ if (found == 0) - return 0; - /* Strip The selected value */ - ecom->size--; - /* size is reduced. no memmove to do */ - p = XMALLOC(MTYPE_ECOMMUNITY_VAL, ecom->size * ECOMMUNITY_SIZE); - if (c != 0) - memcpy(p, ecom->val, c * ECOMMUNITY_SIZE); - if ((ecom->size - c) != 0) - memcpy(p + (c)*ECOMMUNITY_SIZE, - ecom->val + (c + 1) * ECOMMUNITY_SIZE, - (ecom->size - c) * ECOMMUNITY_SIZE); - /* shift last ecommunities */ - XFREE(MTYPE_ECOMMUNITY, ecom->val); - ecom->val = p; - return 1; + return false; + + /* Handle the case where everything needs to be stripped. */ + if (found == ecom->size) { + XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val); + ecom->size = 0; + return true; + } + + /* Strip matching ext community(ies). */ + new = XMALLOC(MTYPE_ECOMMUNITY_VAL, + (ecom->size - found) * ecom->unit_size); + q = new; + for (c = 0, p = ecom->val; c < ecom->size; c++, p += ecom->unit_size) { + if (!(p[0] == type && p[1] == subtype)) { + memcpy(q, p, ecom->unit_size); + q += ecom->unit_size; + } + } + XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val); + ecom->val = new; + ecom->size -= found; + return true; } /* * Remove specified extended community value from extended community. * Returns 1 if value was present (and hence, removed), 0 otherwise. */ -int ecommunity_del_val(struct ecommunity *ecom, struct ecommunity_val *eval) +bool ecommunity_del_val(struct ecommunity *ecom, struct ecommunity_val *eval) { uint8_t *p; int c, found = 0; /* Make sure specified value exists. */ if (ecom == NULL || ecom->val == NULL) - return 0; + return false; c = 0; - for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++) { - if (!memcmp(p, eval->val, ECOMMUNITY_SIZE)) { + for (p = ecom->val; c < ecom->size; p += ecom->unit_size, c++) { + if (!memcmp(p, eval->val, ecom->unit_size)) { found = 1; break; } } if (found == 0) - return 0; + return false; /* Delete the selected value */ ecom->size--; - p = XMALLOC(MTYPE_ECOMMUNITY_VAL, ecom->size * ECOMMUNITY_SIZE); + p = XMALLOC(MTYPE_ECOMMUNITY_VAL, ecom->size * ecom->unit_size); if (c != 0) - memcpy(p, ecom->val, c * ECOMMUNITY_SIZE); + memcpy(p, ecom->val, c * ecom->unit_size); if ((ecom->size - c) != 0) - memcpy(p + (c)*ECOMMUNITY_SIZE, - ecom->val + (c + 1) * ECOMMUNITY_SIZE, - (ecom->size - c) * ECOMMUNITY_SIZE); + memcpy(p + (c)*ecom->unit_size, + ecom->val + (c + 1) * ecom->unit_size, + (ecom->size - c) * ecom->unit_size); XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val); ecom->val = p; - return 1; + return true; } int ecommunity_fill_pbr_action(struct ecommunity_val *ecom_eval, - struct bgp_pbr_entry_action *api) + struct bgp_pbr_entry_action *api, + afi_t afi) { if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_RATE) { api->action = ACTION_TRAFFICRATE; @@ -1000,7 +1307,8 @@ int ecommunity_fill_pbr_action(struct ecommunity_val *ecom_eval, } else if (ecom_eval->val[1] == ECOMMUNITY_REDIRECT_VRF) { /* must use external function */ return 0; - } else if (ecom_eval->val[1] == ECOMMUNITY_REDIRECT_IP_NH) { + } else if (ecom_eval->val[1] == ECOMMUNITY_REDIRECT_IP_NH && + afi == AFI_IP) { /* see draft-ietf-idr-flowspec-redirect-ip-02 * Q1: how come a ext. community can host ipv6 address * Q2 : from cisco documentation: @@ -1040,17 +1348,15 @@ static void *bgp_aggr_ecommunty_hash_alloc(void *p) return ecommunity; } -static void bgp_aggr_ecommunity_prepare(struct hash_backet *hb, void *arg) +static void bgp_aggr_ecommunity_prepare(struct hash_bucket *hb, void *arg) { - struct ecommunity *ecommerge = NULL; struct ecommunity *hb_ecommunity = hb->data; struct ecommunity **aggr_ecommunity = arg; - if (*aggr_ecommunity) { - ecommerge = ecommunity_merge(*aggr_ecommunity, hb_ecommunity); - *aggr_ecommunity = ecommunity_uniq_sort(ecommerge); - ecommunity_free(&ecommerge); - } else + if (*aggr_ecommunity) + *aggr_ecommunity = ecommunity_merge(*aggr_ecommunity, + hb_ecommunity); + else *aggr_ecommunity = ecommunity_dup(hb_ecommunity); } @@ -1063,6 +1369,14 @@ void bgp_aggr_ecommunity_remove(void *arg) void bgp_compute_aggregate_ecommunity(struct bgp_aggregate *aggregate, struct ecommunity *ecommunity) +{ + bgp_compute_aggregate_ecommunity_hash(aggregate, ecommunity); + bgp_compute_aggregate_ecommunity_val(aggregate); +} + + +void bgp_compute_aggregate_ecommunity_hash(struct bgp_aggregate *aggregate, + struct ecommunity *ecommunity) { struct ecommunity *aggr_ecommunity = NULL; @@ -1083,20 +1397,34 @@ void bgp_compute_aggregate_ecommunity(struct bgp_aggregate *aggregate, aggr_ecommunity = hash_get(aggregate->ecommunity_hash, ecommunity, bgp_aggr_ecommunty_hash_alloc); + } - /* Re-compute aggregate's ecommunity. - */ - if (aggregate->ecommunity) - ecommunity_free(&aggregate->ecommunity); + /* Increment reference counter. + */ + aggr_ecommunity->refcnt++; +} +void bgp_compute_aggregate_ecommunity_val(struct bgp_aggregate *aggregate) +{ + struct ecommunity *ecommerge = NULL; + + if (aggregate == NULL) + return; + + /* Re-compute aggregate's ecommunity. + */ + if (aggregate->ecommunity) + ecommunity_free(&aggregate->ecommunity); + if (aggregate->ecommunity_hash + && aggregate->ecommunity_hash->count) { hash_iterate(aggregate->ecommunity_hash, bgp_aggr_ecommunity_prepare, &aggregate->ecommunity); + ecommerge = aggregate->ecommunity; + aggregate->ecommunity = ecommunity_uniq_sort(ecommerge); + if (ecommerge) + ecommunity_free(&ecommerge); } - - /* Increment refernce counter. - */ - aggr_ecommunity->refcnt++; } void bgp_remove_ecommunity_from_aggregate(struct bgp_aggregate *aggregate, @@ -1105,10 +1433,36 @@ void bgp_remove_ecommunity_from_aggregate(struct bgp_aggregate *aggregate, struct ecommunity *aggr_ecommunity = NULL; struct ecommunity *ret_ecomm = NULL; - if ((aggregate == NULL) || (ecommunity == NULL)) + if ((!aggregate) + || (!aggregate->ecommunity_hash) + || (!ecommunity)) return; - if (aggregate->ecommunity_hash == NULL) + /* Look-up the ecommunity in the hash. + */ + aggr_ecommunity = bgp_aggr_ecommunity_lookup(aggregate, ecommunity); + if (aggr_ecommunity) { + aggr_ecommunity->refcnt--; + + if (aggr_ecommunity->refcnt == 0) { + ret_ecomm = hash_release(aggregate->ecommunity_hash, + aggr_ecommunity); + ecommunity_free(&ret_ecomm); + bgp_compute_aggregate_ecommunity_val(aggregate); + } + } +} + +void bgp_remove_ecomm_from_aggregate_hash(struct bgp_aggregate *aggregate, + struct ecommunity *ecommunity) +{ + + struct ecommunity *aggr_ecommunity = NULL; + struct ecommunity *ret_ecomm = NULL; + + if ((!aggregate) + || (!aggregate->ecommunity_hash) + || (!ecommunity)) return; /* Look-up the ecommunity in the hash. @@ -1121,14 +1475,86 @@ void bgp_remove_ecommunity_from_aggregate(struct bgp_aggregate *aggregate, ret_ecomm = hash_release(aggregate->ecommunity_hash, aggr_ecommunity); ecommunity_free(&ret_ecomm); + } + } +} - ecommunity_free(&aggregate->ecommunity); +/* + * return the BGP link bandwidth extended community, if present; + * the actual bandwidth is returned via param + */ +const uint8_t *ecommunity_linkbw_present(struct ecommunity *ecom, uint32_t *bw) +{ + const uint8_t *eval; + int i; - /* Compute aggregate's ecommunity. - */ - hash_iterate(aggregate->ecommunity_hash, - bgp_aggr_ecommunity_prepare, - &aggregate->ecommunity); + if (bw) + *bw = 0; + + if (!ecom || !ecom->size) + return NULL; + + for (i = 0; i < ecom->size; i++) { + const uint8_t *pnt; + uint8_t type, sub_type; + uint32_t bwval; + + eval = pnt = (ecom->val + (i * ECOMMUNITY_SIZE)); + type = *pnt++; + sub_type = *pnt++; + + if ((type == ECOMMUNITY_ENCODE_AS || + type == ECOMMUNITY_ENCODE_AS_NON_TRANS) && + sub_type == ECOMMUNITY_LINK_BANDWIDTH) { + pnt += 2; /* bandwidth is encoded as AS:val */ + pnt = ptr_get_be32(pnt, &bwval); + (void)pnt; /* consume value */ + if (bw) + *bw = bwval; + return eval; } } + + return NULL; +} + + +struct ecommunity *ecommunity_replace_linkbw(as_t as, + struct ecommunity *ecom, + uint64_t cum_bw) +{ + struct ecommunity *new; + struct ecommunity_val lb_eval; + const uint8_t *eval; + uint8_t type; + uint32_t cur_bw; + + /* Nothing to replace if link-bandwidth doesn't exist or + * is non-transitive - just return existing extcommunity. + */ + new = ecom; + if (!ecom || !ecom->size) + return new; + + eval = ecommunity_linkbw_present(ecom, &cur_bw); + if (!eval) + return new; + + type = *eval; + if (type & ECOMMUNITY_FLAG_NON_TRANSITIVE) + return new; + + /* Transitive link-bandwidth exists, replace with the passed + * (cumulative) bandwidth value. We need to create a new + * extcommunity for this - refer to AS-Path replace function + * for reference. + */ + if (cum_bw > 0xFFFFFFFF) + cum_bw = 0xFFFFFFFF; + encode_lb_extcomm(as > BGP_AS_MAX ? BGP_AS_TRANS : as, cum_bw, + false, &lb_eval); + new = ecommunity_dup(ecom); + ecommunity_add_val(new, &lb_eval, true, true); + + return new; } diff --git a/bgpd/bgp_ecommunity.h b/bgpd/bgp_ecommunity.h index 62b2137753..fe90efe1f7 100644 --- a/bgpd/bgp_ecommunity.h +++ b/bgpd/bgp_ecommunity.h @@ -22,22 +22,38 @@ #define _QUAGGA_BGP_ECOMMUNITY_H #include "bgpd/bgp_route.h" +#include "bgpd/bgpd.h" +/* Refer to rfc7153 for the IANA registry definitions. These are + * updated by other standards like rfc7674. + */ /* High-order octet of the Extended Communities type field. */ #define ECOMMUNITY_ENCODE_AS 0x00 #define ECOMMUNITY_ENCODE_IP 0x01 #define ECOMMUNITY_ENCODE_AS4 0x02 #define ECOMMUNITY_ENCODE_OPAQUE 0x03 #define ECOMMUNITY_ENCODE_EVPN 0x06 -#define ECOMMUNITY_ENCODE_TRANS_EXP 0x80 /* Flow Spec */ #define ECOMMUNITY_ENCODE_REDIRECT_IP_NH 0x08 /* Flow Spec */ +/* Generic Transitive Experimental */ +#define ECOMMUNITY_ENCODE_TRANS_EXP 0x80 + /* RFC7674 */ #define ECOMMUNITY_EXTENDED_COMMUNITY_PART_2 0x81 #define ECOMMUNITY_EXTENDED_COMMUNITY_PART_3 0x82 +/* Non-transitive extended community types. */ +#define ECOMMUNITY_ENCODE_AS_NON_TRANS 0x40 +#define ECOMMUNITY_ENCODE_IP_NON_TRANS 0x41 +#define ECOMMUNITY_ENCODE_AS4_NON_TRANS 0x42 +#define ECOMMUNITY_ENCODE_OPAQUE_NON_TRANS 0x43 + /* Low-order octet of the Extended Communities type field. */ +/* Note: This really depends on the high-order octet. This means that + * multiple definitions for the same value are possible. + */ #define ECOMMUNITY_ROUTE_TARGET 0x02 #define ECOMMUNITY_SITE_ORIGIN 0x03 +#define ECOMMUNITY_LINK_BANDWIDTH 0x04 #define ECOMMUNITY_TRAFFIC_RATE 0x06 /* Flow Spec */ #define ECOMMUNITY_TRAFFIC_ACTION 0x07 #define ECOMMUNITY_REDIRECT_VRF 0x08 @@ -47,6 +63,10 @@ * 0x0c Flow-spec Redirect to IPv4 - draft-ietf-idr-flowspec-redirect */ #define ECOMMUNITY_FLOWSPEC_REDIRECT_IPV4 0x0c +/* from draft-ietf-idr-flow-spec-v6-09 + * 0x0b Flow-spec Redirect to IPv6 + */ +#define ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6 0x0b /* Low-order octet of the Extended Communities type field for EVPN types */ #define ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY 0x00 @@ -57,8 +77,12 @@ #define ECOMMUNITY_EVPN_SUBTYPE_ND 0x08 #define ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY_FLAG_STICKY 0x01 -#define ECOMMUNITY_EVPN_SUBTYPE_ND_ROUTER_FLAG 0x01 -#define ECOMMUNITY_EVPN_SUBTYPE_ND_OVERRIDE_FLAG 0x02 + +#define ECOMMUNITY_EVPN_SUBTYPE_ND_ROUTER_FLAG 0x01 +#define ECOMMUNITY_EVPN_SUBTYPE_ND_OVERRIDE_FLAG 0x02 +#define ECOMMUNITY_EVPN_SUBTYPE_PROXY_FLAG 0x04 + +#define ECOMMUNITY_EVPN_SUBTYPE_ESI_SA_FLAG (1 << 0) /* single-active */ /* Low-order octet of the Extended Communities type field for OPAQUE types */ #define ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP 0x0c @@ -70,6 +94,7 @@ /* Extended Communities value is eight octet long. */ #define ECOMMUNITY_SIZE 8 +#define IPV6_ECOMMUNITY_SIZE 20 /* Extended Communities type flag. */ #define ECOMMUNITY_FLAG_NON_TRANSITIVE 0x40 @@ -79,6 +104,11 @@ struct ecommunity { /* Reference counter. */ unsigned long refcnt; + /* Size of Each Unit of Extended Communities attribute. + * to differentiate between IPv6 ext comm and ext comm + */ + uint8_t unit_size; + /* Size of Extended Communities attribute. */ int size; @@ -99,12 +129,22 @@ struct ecommunity_ip { uint16_t val; }; +struct ecommunity_ip6 { + struct in6_addr ip; + uint16_t val; +}; + /* Extended community value is eight octet. */ struct ecommunity_val { char val[ECOMMUNITY_SIZE]; }; -#define ecom_length(X) ((X)->size * ECOMMUNITY_SIZE) +/* IPv6 Extended community value is eight octet. */ +struct ecommunity_val_ipv6 { + char val[IPV6_ECOMMUNITY_SIZE]; +}; + +#define ecom_length_size(X, Y) ((X)->size * (Y)) /* * Encode BGP Route Target AS:nn. @@ -151,10 +191,32 @@ static inline void encode_route_target_as4(as_t as, uint16_t val, eval->val[7] = val & 0xff; } +/* + * Encode BGP Link Bandwidth extended community + * bandwidth (bw) is in bytes-per-sec + */ +static inline void encode_lb_extcomm(as_t as, uint32_t bw, bool non_trans, + struct ecommunity_val *eval) +{ + memset(eval, 0, sizeof(*eval)); + eval->val[0] = ECOMMUNITY_ENCODE_AS; + if (non_trans) + eval->val[0] |= ECOMMUNITY_FLAG_NON_TRANSITIVE; + eval->val[1] = ECOMMUNITY_LINK_BANDWIDTH; + eval->val[2] = (as >> 8) & 0xff; + eval->val[3] = as & 0xff; + eval->val[4] = (bw >> 24) & 0xff; + eval->val[5] = (bw >> 16) & 0xff; + eval->val[6] = (bw >> 8) & 0xff; + eval->val[7] = bw & 0xff; +} + extern void ecommunity_init(void); extern void ecommunity_finish(void); extern void ecommunity_free(struct ecommunity **); extern struct ecommunity *ecommunity_parse(uint8_t *, unsigned short); +extern struct ecommunity *ecommunity_parse_ipv6(uint8_t *pnt, + unsigned short length); extern struct ecommunity *ecommunity_dup(struct ecommunity *); extern struct ecommunity *ecommunity_merge(struct ecommunity *, struct ecommunity *); @@ -162,36 +224,64 @@ extern struct ecommunity *ecommunity_uniq_sort(struct ecommunity *); extern struct ecommunity *ecommunity_intern(struct ecommunity *); extern bool ecommunity_cmp(const void *arg1, const void *arg2); extern void ecommunity_unintern(struct ecommunity **); -extern unsigned int ecommunity_hash_make(void *); +extern unsigned int ecommunity_hash_make(const void *); extern struct ecommunity *ecommunity_str2com(const char *, int, int); +extern struct ecommunity *ecommunity_str2com_ipv6(const char *str, int type, + int keyword_included); extern char *ecommunity_ecom2str(struct ecommunity *, int, int); extern void ecommunity_strfree(char **s); -extern int ecommunity_match(const struct ecommunity *, - const struct ecommunity *); +extern bool ecommunity_match(const struct ecommunity *, + const struct ecommunity *); extern char *ecommunity_str(struct ecommunity *); extern struct ecommunity_val *ecommunity_lookup(const struct ecommunity *, uint8_t, uint8_t); -extern int ecommunity_add_val(struct ecommunity *ecom, - struct ecommunity_val *eval); + +extern bool ecommunity_add_val(struct ecommunity *ecom, + struct ecommunity_val *eval, + bool unique, bool overwrite); +extern bool ecommunity_add_val_ipv6(struct ecommunity *ecom, + struct ecommunity_val_ipv6 *eval, + bool unique, bool overwrite); /* for vpn */ extern struct ecommunity *ecommunity_new(void); -extern int ecommunity_add_val(struct ecommunity *, struct ecommunity_val *); -extern int ecommunity_strip(struct ecommunity *ecom, uint8_t type, - uint8_t subtype); +extern bool ecommunity_strip(struct ecommunity *ecom, uint8_t type, + uint8_t subtype); extern struct ecommunity *ecommunity_new(void); -extern int ecommunity_del_val(struct ecommunity *ecom, - struct ecommunity_val *eval); +extern bool ecommunity_del_val(struct ecommunity *ecom, + struct ecommunity_val *eval); struct bgp_pbr_entry_action; extern int ecommunity_fill_pbr_action(struct ecommunity_val *ecom_eval, - struct bgp_pbr_entry_action *api); + struct bgp_pbr_entry_action *api, + afi_t afi); extern void bgp_compute_aggregate_ecommunity( struct bgp_aggregate *aggregate, struct ecommunity *ecommunity); + +extern void bgp_compute_aggregate_ecommunity_hash( + struct bgp_aggregate *aggregate, + struct ecommunity *ecommunity); +extern void bgp_compute_aggregate_ecommunity_val( + struct bgp_aggregate *aggregate); extern void bgp_remove_ecommunity_from_aggregate( struct bgp_aggregate *aggregate, struct ecommunity *ecommunity); +extern void bgp_remove_ecomm_from_aggregate_hash( + struct bgp_aggregate *aggregate, + struct ecommunity *ecommunity); extern void bgp_aggr_ecommunity_remove(void *arg); +extern const uint8_t *ecommunity_linkbw_present(struct ecommunity *ecom, + uint32_t *bw); +extern struct ecommunity *ecommunity_replace_linkbw(as_t as, + struct ecommunity *ecom, uint64_t cum_bw); +static inline void ecommunity_strip_rts(struct ecommunity *ecom) +{ + uint8_t subtype = ECOMMUNITY_ROUTE_TARGET; + + ecommunity_strip(ecom, ECOMMUNITY_ENCODE_AS, subtype); + ecommunity_strip(ecom, ECOMMUNITY_ENCODE_IP, subtype); + ecommunity_strip(ecom, ECOMMUNITY_ENCODE_AS4, subtype); +} #endif /* _QUAGGA_BGP_ECOMMUNITY_H */ diff --git a/bgpd/bgp_errors.c b/bgpd/bgp_errors.c index 753ee6baf1..8a33ce6789 100644 --- a/bgpd/bgp_errors.c +++ b/bgpd/bgp_errors.c @@ -121,12 +121,6 @@ static struct log_ref ferr_bgp_warn[] = { .description = "BGP attempted to setup TCP MD5 configuration on the socket as per configuration but was unable to", .suggestion = "Please collect log files and open Issue", }, - { - .code = EC_BGP_NO_SOCKOPT_MARK, - .title = "Unable to set socket MARK option", - .description = "BGP attempted to set the SO_MARK option for a socket and was unable to do so", - .suggestion = "Please collect log files and open Issue", - }, { .code = EC_BGP_EVPN_PMSI_PRESENT, .title = "BGP Received a EVPN NLRI with PMSI included", @@ -432,12 +426,6 @@ static struct log_ref ferr_bgp_err[] = { .description = "BGP attempted to create an EVPN ES entry and failed", .suggestion = "Most likely a bug. If the problem persists, report the problem for troubleshooting" }, - { - .code = EC_BGP_MULTI_INSTANCE, - .title = "BGP config multi-instance issue", - .description = "BGP configuration attempting multiple instances without enabling the feature", - .suggestion = "Correct the configuration so that bgp multiple-instance is enabled if desired" - }, { .code = EC_BGP_EVPN_AS_MISMATCH, .title = "BGP AS configuration issue", @@ -468,6 +456,12 @@ static struct log_ref ferr_bgp_err[] = { .description = "As part of BGP startup, the peer and ourselves can start connections to each other at the same time. During this process BGP received additional configuration, but it was only applied to one of the two nascent connections. Depending on the result of collision detection and resolution this configuration might be lost. To remedy this, after performing collision detection and resolution the peer session has been reset in order to apply the new configuration.", .suggestion = "Gather data and open a Issue so that this developmental escape can be fixed, the peer should have been reset", }, + { + .code = EC_BGP_ROUTER_ID_SAME, + .title = "BGP has detected a duplicate router id during collision resolution", + .description = "As part of normal collision detection for opening a connection to a peer, BGP has detected that the remote peer's router-id is the same as ours", + .suggestion = "Change one of the two router-id's", + }, { .code = END_FERR, } diff --git a/bgpd/bgp_errors.h b/bgpd/bgp_errors.h index 13bd318e27..49c58ae6b0 100644 --- a/bgpd/bgp_errors.h +++ b/bgpd/bgp_errors.h @@ -67,7 +67,6 @@ enum bgp_log_refs { EC_BGP_EVPN_ROUTE_INVALID, EC_BGP_EVPN_ROUTE_CREATE, EC_BGP_ES_CREATE, - EC_BGP_MULTI_INSTANCE, EC_BGP_EVPN_AS_MISMATCH, EC_BGP_EVPN_INSTANCE_MISMATCH, EC_BGP_FLOWSPEC_PACKET, @@ -88,7 +87,6 @@ enum bgp_log_refs { EC_BGP_UPDATE_PACKET_LONG, EC_BGP_UNRECOGNIZED_CAPABILITY, EC_BGP_NO_TCP_MD5, - EC_BGP_NO_SOCKOPT_MARK, EC_BGP_EVPN_PMSI_PRESENT, EC_BGP_EVPN_VPN_VNI, EC_BGP_EVPN_ESI, @@ -100,6 +98,7 @@ enum bgp_log_refs { EC_BGP_CAPABILITY_UNKNOWN, EC_BGP_INVALID_NEXTHOP_LENGTH, EC_BGP_DOPPELGANGER_CONFIG, + EC_BGP_ROUTER_ID_SAME, }; extern void bgp_error_init(void); diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index 52aa923959..fa4145cf7e 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -40,6 +40,7 @@ #include "bgpd/bgp_label.h" #include "bgpd/bgp_evpn.h" #include "bgpd/bgp_evpn_private.h" +#include "bgpd/bgp_evpn_mh.h" #include "bgpd/bgp_ecommunity.h" #include "bgpd/bgp_encap_types.h" #include "bgpd/bgp_debug.h" @@ -49,71 +50,35 @@ #include "bgpd/bgp_nexthop.h" #include "bgpd/bgp_addpath.h" #include "bgpd/bgp_mac.h" +#include "bgpd/bgp_vty.h" /* * Definitions and external declarations. */ -extern struct zclient *zclient; - DEFINE_QOBJ_TYPE(bgpevpn) -DEFINE_QOBJ_TYPE(evpnes) +DEFINE_QOBJ_TYPE(bgp_evpn_es) /* * Static function declarations */ -static void delete_evpn_route_entry(struct bgp *bgp, afi_t afi, safi_t safi, - struct bgp_node *rn, - struct bgp_path_info **pi); static int delete_all_vni_routes(struct bgp *bgp, struct bgpevpn *vpn); +static void bgp_evpn_update_type2_route_entry(struct bgp *bgp, + struct bgpevpn *vpn, + struct bgp_node *rn, struct bgp_path_info *local_pi, + const char *caller); +static struct in_addr zero_vtep_ip; /* * Private functions. */ -/* compare two IPV4 VTEP IPs */ -static int evpn_vtep_ip_cmp(void *p1, void *p2) -{ - const struct in_addr *ip1 = p1; - const struct in_addr *ip2 = p2; - - return ip1->s_addr - ip2->s_addr; -} - -/* - * Make hash key for ESI. - */ -static unsigned int esi_hash_keymake(void *p) -{ - struct evpnes *pes = p; - const void *pnt = (void *)pes->esi.val; - - return jhash(pnt, ESI_BYTES, 0xa5a5a55a); -} - -/* - * Compare two ESIs. - */ -static bool esi_cmp(const void *p1, const void *p2) -{ - const struct evpnes *pes1 = p1; - const struct evpnes *pes2 = p2; - - if (pes1 == NULL && pes2 == NULL) - return true; - - if (pes1 == NULL || pes2 == NULL) - return false; - - return (memcmp(pes1->esi.val, pes2->esi.val, ESI_BYTES) == 0); -} - /* * Make vni hash key. */ -static unsigned int vni_hash_key_make(void *p) +static unsigned int vni_hash_key_make(const void *p) { - struct bgpevpn *vpn = p; + const struct bgpevpn *vpn = p; return (jhash_1word(vpn->vni, 0)); } @@ -132,7 +97,7 @@ static bool vni_hash_cmp(const void *p1, const void *p2) return (vpn1->vni == vpn2->vni); } -static int vni_list_cmp(void *p1, void *p2) +int vni_list_cmp(void *p1, void *p2) { const struct bgpevpn *vpn1 = p1; const struct bgpevpn *vpn2 = p2; @@ -143,10 +108,10 @@ static int vni_list_cmp(void *p1, void *p2) /* * Make vrf import route target hash key. */ -static unsigned int vrf_import_rt_hash_key_make(void *p) +static unsigned int vrf_import_rt_hash_key_make(const void *p) { - struct vrf_irt_node *irt = p; - char *pnt = irt->rt.val; + const struct vrf_irt_node *irt = p; + const char *pnt = irt->rt.val; return jhash(pnt, 8, 0x5abc1234); } @@ -259,10 +224,10 @@ static int is_vrf_present_in_irt_vrfs(struct list *vrfs, struct bgp *bgp_vrf) /* * Make import route target hash key. */ -static unsigned int import_rt_hash_key_make(void *p) +static unsigned int import_rt_hash_key_make(const void *p) { - struct irt_node *irt = p; - char *pnt = irt->rt.val; + const struct irt_node *irt = p; + const char *pnt = irt->rt.val; return jhash(pnt, 8, 0xdeadbeef); } @@ -494,6 +459,39 @@ static void unmap_vni_from_rt(struct bgp *bgp, struct bgpevpn *vpn, } } +static void bgp_evpn_get_rmac_nexthop(struct bgpevpn *vpn, + const struct prefix_evpn *p, + struct attr *attr, uint8_t flags) +{ + struct bgp *bgp_vrf = vpn->bgp_vrf; + + memset(&attr->rmac, 0, sizeof(struct ethaddr)); + if (!bgp_vrf) + return; + + if (p->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE) + return; + + /* Copy sys (pip) RMAC and PIP IP as nexthop + * in case of route is self MAC-IP, + * advertise-pip and advertise-svi-ip features + * are enabled. + * Otherwise, for all host MAC-IP route's + * copy anycast RMAC. + */ + if (CHECK_FLAG(flags, BGP_EVPN_MACIP_TYPE_SVI_IP) + && bgp_vrf->evpn_info->advertise_pip && + bgp_vrf->evpn_info->is_anycast_mac) { + /* copy sys rmac */ + memcpy(&attr->rmac, &bgp_vrf->evpn_info->pip_rmac, + ETH_ALEN); + attr->nexthop = bgp_vrf->evpn_info->pip_ip; + attr->mp_nexthop_global_in = + bgp_vrf->evpn_info->pip_ip; + } else + memcpy(&attr->rmac, &bgp_vrf->rmac, ETH_ALEN); +} + /* * Create RT extended community automatically from passed information: * of the form AS:VNI. @@ -505,15 +503,24 @@ static void unmap_vni_from_rt(struct bgp *bgp, struct bgpevpn *vpn, static void form_auto_rt(struct bgp *bgp, vni_t vni, struct list *rtl) { struct ecommunity_val eval; - struct ecommunity *ecomadd; + struct ecommunity *ecomadd, *ecom; + bool ecom_found = false; + struct listnode *node; if (bgp->advertise_autort_rfc8365) vni |= EVPN_AUTORT_VXLAN; encode_route_target_as((bgp->as & 0xFFFF), vni, &eval); ecomadd = ecommunity_new(); - ecommunity_add_val(ecomadd, &eval); - listnode_add_sort(rtl, ecomadd); + ecommunity_add_val(ecomadd, &eval, false, false); + for (ALL_LIST_ELEMENTS_RO(rtl, node, ecom)) + if (ecommunity_cmp(ecomadd, ecom)) + ecom_found = true; + + if (!ecom_found) + listnode_add_sort(rtl, ecomadd); + else + ecommunity_free(&ecomadd); } /* @@ -538,19 +545,54 @@ static void evpn_convert_nexthop_to_ipv6(struct attr *attr) attr->mp_nexthop_len = IPV6_MAX_BYTELEN; } +struct bgp_node *bgp_global_evpn_node_get( + struct bgp_table *table, afi_t afi, + safi_t safi, const struct prefix_evpn *evp, + struct prefix_rd *prd) +{ + struct prefix_evpn global_p; + + if (evp->prefix.route_type == BGP_EVPN_AD_ROUTE) { + /* prefix in the global table doesn't include the VTEP-IP so + * we need to create a different copy of the prefix + */ + evpn_type1_prefix_global_copy(&global_p, evp); + evp = &global_p; + } + return bgp_afi_node_get(table, afi, safi, (struct prefix *)evp, prd); +} + +struct bgp_node *bgp_global_evpn_node_lookup( + struct bgp_table *table, afi_t afi, + safi_t safi, const struct prefix_evpn *evp, + struct prefix_rd *prd) +{ + struct prefix_evpn global_p; + + if (evp->prefix.route_type == BGP_EVPN_AD_ROUTE) { + /* prefix in the global table doesn't include the VTEP-IP so + * we need to create a different copy of the prefix + */ + evpn_type1_prefix_global_copy(&global_p, evp); + evp = &global_p; + } + return bgp_afi_node_lookup(table, afi, safi, (struct prefix *)evp, prd); +} + /* * Add (update) or delete MACIP from zebra. */ static int bgp_zebra_send_remote_macip(struct bgp *bgp, struct bgpevpn *vpn, - struct prefix_evpn *p, + const struct prefix_evpn *p, struct in_addr remote_vtep_ip, int add, - uint8_t flags, uint32_t seq) + uint8_t flags, uint32_t seq, esi_t *esi) { struct stream *s; int ipa_len; char buf1[ETHER_ADDR_STRLEN]; char buf2[INET6_ADDRSTRLEN]; char buf3[INET6_ADDRSTRLEN]; + static struct in_addr zero_remote_vtep_ip; /* Check socket. */ if (!zclient || zclient->sock < 0) @@ -559,10 +601,14 @@ static int bgp_zebra_send_remote_macip(struct bgp *bgp, struct bgpevpn *vpn, /* Don't try to register if Zebra doesn't know of this instance. */ if (!IS_BGP_INST_KNOWN_TO_ZEBRA(bgp)) { if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug("%s: No zebra instance to talk to, not installing remote macip", - __PRETTY_FUNCTION__); + zlog_debug( + "%s: No zebra instance to talk to, not installing remote macip", + __func__); return 0; } + + if (!esi) + esi = zero_esi; s = zclient->obuf; stream_reset(s); @@ -580,13 +626,20 @@ static int bgp_zebra_send_remote_macip(struct bgp *bgp, struct bgpevpn *vpn, stream_putl(s, ipa_len); stream_put(s, &p->prefix.macip_addr.ip.ip.addr, ipa_len); } - stream_put_in_addr(s, &remote_vtep_ip); + /* If the ESI is valid that becomes the nexthop; tape out the + * VTEP-IP for that case + */ + if (bgp_evpn_is_esi_valid(esi)) + stream_put_in_addr(s, &zero_remote_vtep_ip); + else + stream_put_in_addr(s, &remote_vtep_ip); /* TX flags - MAC sticky status and/or gateway mac */ /* Also TX the sequence number of the best route. */ if (add) { stream_putc(s, flags); stream_putl(s, seq); + stream_put(s, esi, sizeof(esi_t)); } stream_putw_at(s, 0, stream_get_endp(s)); @@ -609,8 +662,8 @@ static int bgp_zebra_send_remote_macip(struct bgp *bgp, struct bgpevpn *vpn, * Add (update) or delete remote VTEP from zebra. */ static int bgp_zebra_send_remote_vtep(struct bgp *bgp, struct bgpevpn *vpn, - struct prefix_evpn *p, - int flood_control, int add) + const struct prefix_evpn *p, + int flood_control, int add) { struct stream *s; @@ -621,8 +674,9 @@ static int bgp_zebra_send_remote_vtep(struct bgp *bgp, struct bgpevpn *vpn, /* Don't try to register if Zebra doesn't know of this instance. */ if (!IS_BGP_INST_KNOWN_TO_ZEBRA(bgp)) { if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug("%s: No zebra instance to talk to, not installing remote vtep", - __PRETTY_FUNCTION__); + zlog_debug( + "%s: No zebra instance to talk to, not installing remote vtep", + __func__); return 0; } @@ -654,40 +708,6 @@ static int bgp_zebra_send_remote_vtep(struct bgp *bgp, struct bgpevpn *vpn, return zclient_send_message(zclient); } -/* - * Build extended community for EVPN ES (type-4) route - */ -static void build_evpn_type4_route_extcomm(struct evpnes *es, - struct attr *attr) -{ - struct ecommunity ecom_encap; - struct ecommunity ecom_es_rt; - struct ecommunity_val eval; - struct ecommunity_val eval_es_rt; - bgp_encap_types tnl_type; - struct ethaddr mac; - - /* Encap */ - tnl_type = BGP_ENCAP_TYPE_VXLAN; - memset(&ecom_encap, 0, sizeof(ecom_encap)); - encode_encap_extcomm(tnl_type, &eval); - ecom_encap.size = 1; - ecom_encap.val = (uint8_t *)eval.val; - attr->ecommunity = ecommunity_dup(&ecom_encap); - - /* ES import RT */ - memset(&mac, 0, sizeof(struct ethaddr)); - memset(&ecom_es_rt, 0, sizeof(ecom_es_rt)); - es_get_system_mac(&es->esi, &mac); - encode_es_rt_extcomm(&eval_es_rt, &mac); - ecom_es_rt.size = 1; - ecom_es_rt.val = (uint8_t *)eval_es_rt.val; - attr->ecommunity = - ecommunity_merge(attr->ecommunity, &ecom_es_rt); - - attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES); -} - /* * Build extended communities for EVPN prefix route. */ @@ -695,12 +715,12 @@ static void build_evpn_type5_route_extcomm(struct bgp *bgp_vrf, struct attr *attr) { struct ecommunity ecom_encap; - struct ecommunity ecom_rmac; struct ecommunity_val eval; struct ecommunity_val eval_rmac; bgp_encap_types tnl_type; struct listnode *node, *nnode; struct ecommunity *ecom; + struct ecommunity *old_ecom; struct list *vrf_export_rtl = NULL; /* Encap */ @@ -708,10 +728,19 @@ static void build_evpn_type5_route_extcomm(struct bgp *bgp_vrf, memset(&ecom_encap, 0, sizeof(ecom_encap)); encode_encap_extcomm(tnl_type, &eval); ecom_encap.size = 1; + ecom_encap.unit_size = ECOMMUNITY_SIZE; ecom_encap.val = (uint8_t *)eval.val; /* Add Encap */ - attr->ecommunity = ecommunity_dup(&ecom_encap); + if (attr->ecommunity) { + old_ecom = attr->ecommunity; + ecom = ecommunity_merge(ecommunity_dup(old_ecom), &ecom_encap); + if (!old_ecom->refcnt) + ecommunity_free(&old_ecom); + } else + ecom = ecommunity_dup(&ecom_encap); + attr->ecommunity = ecom; + attr->encap_tunneltype = tnl_type; /* Add the export RTs for L3VNI/VRF */ vrf_export_rtl = bgp_vrf->vrf_export_rtl; @@ -721,12 +750,8 @@ static void build_evpn_type5_route_extcomm(struct bgp *bgp_vrf, /* add the router mac extended community */ if (!is_zero_mac(&attr->rmac)) { - memset(&ecom_rmac, 0, sizeof(ecom_rmac)); encode_rmac_extcomm(&eval_rmac, &attr->rmac); - ecom_rmac.size = 1; - ecom_rmac.val = (uint8_t *)eval_rmac.val; - attr->ecommunity = - ecommunity_merge(attr->ecommunity, &ecom_rmac); + ecommunity_add_val(attr->ecommunity, &eval_rmac, true, true); } attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES); @@ -748,13 +773,13 @@ static void build_evpn_route_extcomm(struct bgpevpn *vpn, struct attr *attr, struct ecommunity ecom_encap; struct ecommunity ecom_sticky; struct ecommunity ecom_default_gw; - struct ecommunity ecom_rmac; struct ecommunity ecom_na; struct ecommunity_val eval; struct ecommunity_val eval_sticky; struct ecommunity_val eval_default_gw; struct ecommunity_val eval_rmac; struct ecommunity_val eval_na; + bool proxy; bgp_encap_types tnl_type; struct listnode *node, *nnode; @@ -767,10 +792,12 @@ static void build_evpn_route_extcomm(struct bgpevpn *vpn, struct attr *attr, memset(&ecom_encap, 0, sizeof(ecom_encap)); encode_encap_extcomm(tnl_type, &eval); ecom_encap.size = 1; + ecom_encap.unit_size = ECOMMUNITY_SIZE; ecom_encap.val = (uint8_t *)eval.val; /* Add Encap */ attr->ecommunity = ecommunity_dup(&ecom_encap); + attr->encap_tunneltype = tnl_type; /* Add the export RTs for L2VNI */ for (ALL_LIST_ELEMENTS(vpn->export_rtl, node, nnode, ecom)) @@ -795,6 +822,7 @@ static void build_evpn_route_extcomm(struct bgpevpn *vpn, struct attr *attr, memset(&ecom_sticky, 0, sizeof(ecom_sticky)); encode_mac_mobility_extcomm(1, seqnum, &eval_sticky); ecom_sticky.size = 1; + ecom_sticky.unit_size = ECOMMUNITY_SIZE; ecom_sticky.val = (uint8_t *)eval_sticky.val; attr->ecommunity = ecommunity_merge(attr->ecommunity, &ecom_sticky); @@ -802,12 +830,8 @@ static void build_evpn_route_extcomm(struct bgpevpn *vpn, struct attr *attr, /* Add RMAC, if told to. */ if (add_l3_ecomm) { - memset(&ecom_rmac, 0, sizeof(ecom_rmac)); encode_rmac_extcomm(&eval_rmac, &attr->rmac); - ecom_rmac.size = 1; - ecom_rmac.val = (uint8_t *)eval_rmac.val; - attr->ecommunity = - ecommunity_merge(attr->ecommunity, &ecom_rmac); + ecommunity_add_val(attr->ecommunity, &eval_rmac, true, true); } /* Add default gateway, if needed. */ @@ -815,15 +839,18 @@ static void build_evpn_route_extcomm(struct bgpevpn *vpn, struct attr *attr, memset(&ecom_default_gw, 0, sizeof(ecom_default_gw)); encode_default_gw_extcomm(&eval_default_gw); ecom_default_gw.size = 1; + ecom_default_gw.unit_size = ECOMMUNITY_SIZE; ecom_default_gw.val = (uint8_t *)eval_default_gw.val; attr->ecommunity = ecommunity_merge(attr->ecommunity, &ecom_default_gw); } - if (attr->router_flag) { + proxy = !!(attr->es_flags & ATTR_ES_PROXY_ADVERT); + if (attr->router_flag || proxy) { memset(&ecom_na, 0, sizeof(ecom_na)); - encode_na_flag_extcomm(&eval_na, attr->router_flag); + encode_na_flag_extcomm(&eval_na, attr->router_flag, proxy); ecom_na.size = 1; + ecom_na.unit_size = ECOMMUNITY_SIZE; ecom_na.val = (uint8_t *)eval_na.val; attr->ecommunity = ecommunity_merge(attr->ecommunity, &ecom_na); @@ -853,15 +880,17 @@ static void add_mac_mobility_to_attr(uint32_t seq_num, struct attr *attr) if (attr->ecommunity) { for (i = 0; i < attr->ecommunity->size; i++) { - pnt = attr->ecommunity->val + (i * 8); + pnt = attr->ecommunity->val + + (i * attr->ecommunity->unit_size); type = *pnt++; sub_type = *pnt++; if (type == ECOMMUNITY_ENCODE_EVPN && sub_type == ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY) { - ecom_val_ptr = (uint8_t *)(attr->ecommunity->val - + (i * 8)); + ecom_val_ptr = + (attr->ecommunity->val + + (i * attr->ecommunity->unit_size)); break; } } @@ -869,12 +898,14 @@ static void add_mac_mobility_to_attr(uint32_t seq_num, struct attr *attr) /* Update the existing MM ecommunity */ if (ecom_val_ptr) { - memcpy(ecom_val_ptr, eval.val, sizeof(char) * ECOMMUNITY_SIZE); + memcpy(ecom_val_ptr, eval.val, sizeof(char) + * attr->ecommunity->unit_size); } /* Add MM to existing */ else { memset(&ecom_tmp, 0, sizeof(ecom_tmp)); ecom_tmp.size = 1; + ecom_tmp.unit_size = ECOMMUNITY_SIZE; ecom_tmp.val = (uint8_t *)eval.val; if (attr->ecommunity) @@ -887,24 +918,66 @@ static void add_mac_mobility_to_attr(uint32_t seq_num, struct attr *attr) /* Install EVPN route into zebra. */ static int evpn_zebra_install(struct bgp *bgp, struct bgpevpn *vpn, - struct prefix_evpn *p, struct bgp_path_info *pi) + const struct prefix_evpn *p, + struct bgp_path_info *pi) { int ret; uint8_t flags; int flood_control; + uint32_t seq; if (p->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) { flags = 0; - if (pi->attr->sticky) - SET_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY); - if (pi->attr->default_gw) - SET_FLAG(flags, ZEBRA_MACIP_TYPE_GW); - if (is_evpn_prefix_ipaddr_v6(p) && - pi->attr->router_flag) - SET_FLAG(flags, ZEBRA_MACIP_TYPE_ROUTER_FLAG); + + if (pi->sub_type == BGP_ROUTE_IMPORTED) { + if (pi->attr->sticky) + SET_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY); + if (pi->attr->default_gw) + SET_FLAG(flags, ZEBRA_MACIP_TYPE_GW); + if (is_evpn_prefix_ipaddr_v6(p) && + pi->attr->router_flag) + SET_FLAG(flags, ZEBRA_MACIP_TYPE_ROUTER_FLAG); + + seq = mac_mobility_seqnum(pi->attr); + /* if local ES notify zebra that this is a sync path */ + if (bgp_evpn_attr_is_local_es(pi->attr)) { + SET_FLAG(flags, ZEBRA_MACIP_TYPE_SYNC_PATH); + if (bgp_evpn_attr_is_proxy(pi->attr)) + SET_FLAG(flags, + ZEBRA_MACIP_TYPE_PROXY_ADVERT); + } + } else { + if (!bgp_evpn_attr_is_sync(pi->attr)) + return 0; + + /* if a local path is being turned around and sent + * to zebra it is because it is a sync path on + * a local ES + */ + SET_FLAG(flags, ZEBRA_MACIP_TYPE_SYNC_PATH); + /* supply the highest peer seq number to zebra + * for MM seq syncing + */ + seq = bgp_evpn_attr_get_sync_seq(pi->attr); + /* if any of the paths from the peer have the ROUTER + * flag set install the local entry as a router entry + */ + if (is_evpn_prefix_ipaddr_v6(p) && + (pi->attr->es_flags & + ATTR_ES_PEER_ROUTER)) + SET_FLAG(flags, + ZEBRA_MACIP_TYPE_ROUTER_FLAG); + + if (!(pi->attr->es_flags & ATTR_ES_PEER_ACTIVE)) + SET_FLAG(flags, + ZEBRA_MACIP_TYPE_PROXY_ADVERT); + } + ret = bgp_zebra_send_remote_macip( - bgp, vpn, p, pi->attr->nexthop, 1, flags, - mac_mobility_seqnum(pi->attr)); + bgp, vpn, p, pi->attr->nexthop, 1, flags, + seq, bgp_evpn_attr_get_esi(pi->attr)); + } else if (p->prefix.route_type == BGP_EVPN_AD_ROUTE) { + ret = bgp_evpn_remote_es_evi_add(bgp, vpn, p); } else { switch (pi->attr->pmsi_tnl_type) { case PMSI_TNLTYPE_INGR_REPL: @@ -927,14 +1000,16 @@ static int evpn_zebra_install(struct bgp *bgp, struct bgpevpn *vpn, /* Uninstall EVPN route from zebra. */ static int evpn_zebra_uninstall(struct bgp *bgp, struct bgpevpn *vpn, - struct prefix_evpn *p, + const struct prefix_evpn *p, struct in_addr remote_vtep_ip) { int ret; if (p->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) ret = bgp_zebra_send_remote_macip(bgp, vpn, p, remote_vtep_ip, - 0, 0, 0); + 0, 0, 0, NULL); + else if (p->prefix.route_type == BGP_EVPN_AD_ROUTE) + ret = bgp_evpn_remote_es_evi_del(bgp, vpn, p); else ret = bgp_zebra_send_remote_vtep(bgp, vpn, p, VXLAN_FLOOD_DISABLED, 0); @@ -948,198 +1023,60 @@ static int evpn_zebra_uninstall(struct bgp *bgp, struct bgpevpn *vpn, * from peers. */ static void evpn_delete_old_local_route(struct bgp *bgp, struct bgpevpn *vpn, - struct bgp_node *rn, - struct bgp_path_info *old_local) + struct bgp_dest *dest, + struct bgp_path_info *old_local, + struct bgp_path_info *new_select) { - struct bgp_node *global_rn; + struct bgp_dest *global_dest; struct bgp_path_info *pi; afi_t afi = AFI_L2VPN; safi_t safi = SAFI_EVPN; + if (BGP_DEBUG(evpn_mh, EVPN_MH_RT)) { + char prefix_buf[PREFIX_STRLEN]; + char esi_buf[ESI_STR_LEN]; + char esi_buf2[ESI_STR_LEN]; + struct prefix_evpn *evp = (struct prefix_evpn *)&dest->p; + + zlog_debug("local path deleted %s es %s; new-path-es %s", + prefix2str(evp, + prefix_buf, sizeof(prefix_buf)), + esi_to_str(&old_local->attr->esi, + esi_buf, sizeof(esi_buf)), + new_select ? esi_to_str(&new_select->attr->esi, + esi_buf2, sizeof(esi_buf2)) : ""); + } + /* Locate route node in the global EVPN routing table. Note that * this table is a 2-level tree (RD-level + Prefix-level) similar to * L3VPN routes. */ - global_rn = bgp_afi_node_lookup(bgp->rib[afi][safi], afi, safi, - (struct prefix *)&rn->p, &vpn->prd); - if (global_rn) { + global_dest = bgp_global_evpn_node_lookup(bgp->rib[afi][safi], afi, safi, + (const struct prefix_evpn *)bgp_dest_get_prefix(dest), + &vpn->prd); + if (global_dest) { /* Delete route entry in the global EVPN table. */ - delete_evpn_route_entry(bgp, afi, safi, global_rn, &pi); + delete_evpn_route_entry(bgp, afi, safi, global_dest, &pi); /* Schedule for processing - withdraws to peers happen from * this table. */ if (pi) - bgp_process(bgp, global_rn, afi, safi); - bgp_unlock_node(global_rn); + bgp_process(bgp, global_dest, afi, safi); + bgp_dest_unlock_node(global_dest); } /* Delete route entry in the VNI route table, caller to remove. */ - bgp_path_info_delete(rn, old_local); -} - -static struct in_addr *es_vtep_new(struct in_addr vtep) -{ - struct in_addr *ip; - - ip = XCALLOC(MTYPE_BGP_EVPN_ES_VTEP, sizeof(struct in_addr)); - - ip->s_addr = vtep.s_addr; - return ip; -} - -static void es_vtep_free(struct in_addr *ip) -{ - XFREE(MTYPE_BGP_EVPN_ES_VTEP, ip); -} - -/* check if VTEP is already part of the list */ -static int is_vtep_present_in_list(struct list *list, - struct in_addr vtep) -{ - struct listnode *node = NULL; - struct in_addr *tmp; - - for (ALL_LIST_ELEMENTS_RO(list, node, tmp)) { - if (tmp->s_addr == vtep.s_addr) - return 1; - } - return 0; -} - -/* - * Best path for ES route was changed, - * update the list of VTEPs for this ES - */ -static int evpn_es_install_vtep(struct bgp *bgp, - struct evpnes *es, - struct prefix_evpn *p, - struct in_addr rvtep) -{ - struct in_addr *vtep_ip; - - if (is_vtep_present_in_list(es->vtep_list, rvtep)) - return 0; - - - vtep_ip = es_vtep_new(rvtep); - if (vtep_ip) - listnode_add_sort(es->vtep_list, vtep_ip); - return 0; -} - -/* - * Best path for ES route was changed, - * update the list of VTEPs for this ES - */ -static int evpn_es_uninstall_vtep(struct bgp *bgp, - struct evpnes *es, - struct prefix_evpn *p, - struct in_addr rvtep) -{ - struct listnode *node, *nnode, *node_to_del = NULL; - struct in_addr *tmp; - - for (ALL_LIST_ELEMENTS(es->vtep_list, node, nnode, tmp)) { - if (tmp->s_addr == rvtep.s_addr) { - es_vtep_free(tmp); - node_to_del = node; - } - } - - if (node_to_del) - list_delete_node(es->vtep_list, node_to_del); - - return 0; -} - -/* - * Calculate the best path for a ES(type-4) route. - */ -static int evpn_es_route_select_install(struct bgp *bgp, - struct evpnes *es, - struct bgp_node *rn) -{ - int ret = 0; - afi_t afi = AFI_L2VPN; - safi_t safi = SAFI_EVPN; - struct bgp_path_info *old_select; /* old best */ - struct bgp_path_info *new_select; /* new best */ - struct bgp_path_info_pair old_and_new; - - /* Compute the best path. */ - bgp_best_selection(bgp, rn, &bgp->maxpaths[afi][safi], - &old_and_new, afi, safi); - old_select = old_and_new.old; - new_select = old_and_new.new; - - /* - * If the best path hasn't changed - see if something needs to be - * updated - */ - if (old_select && old_select == new_select - && old_select->type == ZEBRA_ROUTE_BGP - && old_select->sub_type == BGP_ROUTE_IMPORTED - && !CHECK_FLAG(rn->flags, BGP_NODE_USER_CLEAR) - && !CHECK_FLAG(old_select->flags, BGP_PATH_ATTR_CHANGED) - && !bgp_addpath_is_addpath_used(&bgp->tx_addpath, afi, safi)) { - if (bgp_zebra_has_route_changed(rn, old_select)) { - ret = evpn_es_install_vtep(bgp, es, - (struct prefix_evpn *)&rn->p, - old_select->attr->nexthop); - } - UNSET_FLAG(old_select->flags, BGP_PATH_MULTIPATH_CHG); - bgp_zebra_clear_route_change_flags(rn); - return ret; - } - - /* If the user did a "clear" this flag will be set */ - UNSET_FLAG(rn->flags, BGP_NODE_USER_CLEAR); - - /* - * bestpath has changed; update relevant fields and install or uninstall - * into the zebra RIB. - */ - if (old_select || new_select) - bgp_bump_version(rn); - - if (old_select) - bgp_path_info_unset_flag(rn, old_select, BGP_PATH_SELECTED); - if (new_select) { - bgp_path_info_set_flag(rn, new_select, BGP_PATH_SELECTED); - bgp_path_info_unset_flag(rn, new_select, BGP_PATH_ATTR_CHANGED); - UNSET_FLAG(new_select->flags, BGP_PATH_MULTIPATH_CHG); - } - - if (new_select && new_select->type == ZEBRA_ROUTE_BGP - && new_select->sub_type == BGP_ROUTE_IMPORTED) { - ret = evpn_es_install_vtep(bgp, es, - (struct prefix_evpn *)&rn->p, - new_select->attr->nexthop); - } else { - if (old_select && old_select->type == ZEBRA_ROUTE_BGP - && old_select->sub_type == BGP_ROUTE_IMPORTED) - ret = evpn_es_uninstall_vtep( - bgp, es, (struct prefix_evpn *)&rn->p, - old_select->attr->nexthop); - } - - /* Clear any route change flags. */ - bgp_zebra_clear_route_change_flags(rn); - - /* Reap old select bgp_path_info, if it has been removed */ - if (old_select && CHECK_FLAG(old_select->flags, BGP_PATH_REMOVED)) - bgp_path_info_reap(rn, old_select); - - return ret; + bgp_path_info_delete(dest, old_local); } /* * Calculate the best path for an EVPN route. Install/update best path in zebra, * if appropriate. + * Note: vpn is NULL for local EAD-ES routes. */ -static int evpn_route_select_install(struct bgp *bgp, struct bgpevpn *vpn, - struct bgp_node *rn) +int evpn_route_select_install(struct bgp *bgp, struct bgpevpn *vpn, + struct bgp_dest *dest) { struct bgp_path_info *old_select, *new_select; struct bgp_path_info_pair old_and_new; @@ -1148,51 +1085,65 @@ static int evpn_route_select_install(struct bgp *bgp, struct bgpevpn *vpn, int ret = 0; /* Compute the best path. */ - bgp_best_selection(bgp, rn, &bgp->maxpaths[afi][safi], &old_and_new, + bgp_best_selection(bgp, dest, &bgp->maxpaths[afi][safi], &old_and_new, afi, safi); old_select = old_and_new.old; new_select = old_and_new.new; /* If the best path hasn't changed - see if there is still something to - * update - * to zebra RIB. + * update to zebra RIB. + * Remote routes and SYNC route (i.e. local routes with + * SYNCED_FROM_PEER flag) need to updated to zebra on any attr + * change. */ if (old_select && old_select == new_select && old_select->type == ZEBRA_ROUTE_BGP - && old_select->sub_type == BGP_ROUTE_IMPORTED - && !CHECK_FLAG(rn->flags, BGP_NODE_USER_CLEAR) + && (old_select->sub_type == BGP_ROUTE_IMPORTED || + bgp_evpn_attr_is_sync(old_select->attr)) + && !CHECK_FLAG(dest->flags, BGP_NODE_USER_CLEAR) && !CHECK_FLAG(old_select->flags, BGP_PATH_ATTR_CHANGED) && !bgp_addpath_is_addpath_used(&bgp->tx_addpath, afi, safi)) { - if (bgp_zebra_has_route_changed(rn, old_select)) + if (bgp_zebra_has_route_changed(old_select)) ret = evpn_zebra_install( - bgp, vpn, (struct prefix_evpn *)&rn->p, + bgp, vpn, + (const struct prefix_evpn *)bgp_dest_get_prefix( + dest), old_select); UNSET_FLAG(old_select->flags, BGP_PATH_MULTIPATH_CHG); - bgp_zebra_clear_route_change_flags(rn); + UNSET_FLAG(old_select->flags, BGP_PATH_LINK_BW_CHG); + bgp_zebra_clear_route_change_flags(dest); return ret; } /* If the user did a "clear" this flag will be set */ - UNSET_FLAG(rn->flags, BGP_NODE_USER_CLEAR); + UNSET_FLAG(dest->flags, BGP_NODE_USER_CLEAR); /* bestpath has changed; update relevant fields and install or uninstall * into the zebra RIB. */ if (old_select || new_select) - bgp_bump_version(rn); + bgp_bump_version(dest); if (old_select) - bgp_path_info_unset_flag(rn, old_select, BGP_PATH_SELECTED); + bgp_path_info_unset_flag(dest, old_select, BGP_PATH_SELECTED); if (new_select) { - bgp_path_info_set_flag(rn, new_select, BGP_PATH_SELECTED); - bgp_path_info_unset_flag(rn, new_select, BGP_PATH_ATTR_CHANGED); + bgp_path_info_set_flag(dest, new_select, BGP_PATH_SELECTED); + bgp_path_info_unset_flag(dest, new_select, + BGP_PATH_ATTR_CHANGED); UNSET_FLAG(new_select->flags, BGP_PATH_MULTIPATH_CHG); + UNSET_FLAG(new_select->flags, BGP_PATH_LINK_BW_CHG); } + /* a local entry with the SYNC flag also results in a MAC-IP update + * to zebra + */ if (new_select && new_select->type == ZEBRA_ROUTE_BGP - && new_select->sub_type == BGP_ROUTE_IMPORTED) { - ret = evpn_zebra_install(bgp, vpn, (struct prefix_evpn *)&rn->p, - new_select); + && (new_select->sub_type == BGP_ROUTE_IMPORTED || + bgp_evpn_attr_is_sync(new_select->attr))) { + ret = evpn_zebra_install( + bgp, vpn, + (struct prefix_evpn *)bgp_dest_get_prefix(dest), + new_select); /* If an old best existed and it was a "local" route, the only * reason @@ -1201,247 +1152,53 @@ static int evpn_route_select_install(struct bgp *bgp, struct bgpevpn *vpn, * need to do an implicit delete and withdraw that route from * peers. */ - if (old_select && old_select->peer == bgp->peer_self - && old_select->type == ZEBRA_ROUTE_BGP - && old_select->sub_type == BGP_ROUTE_STATIC) - evpn_delete_old_local_route(bgp, vpn, rn, old_select); + if (new_select->sub_type == BGP_ROUTE_IMPORTED && + old_select && old_select->peer == bgp->peer_self + && old_select->type == ZEBRA_ROUTE_BGP + && old_select->sub_type == BGP_ROUTE_STATIC + && vpn) + evpn_delete_old_local_route(bgp, vpn, dest, + old_select, new_select); } else { if (old_select && old_select->type == ZEBRA_ROUTE_BGP && old_select->sub_type == BGP_ROUTE_IMPORTED) - ret = evpn_zebra_uninstall(bgp, vpn, - (struct prefix_evpn *)&rn->p, - old_select->attr->nexthop); + ret = evpn_zebra_uninstall( + bgp, vpn, + (const struct prefix_evpn *)bgp_dest_get_prefix( + dest), + old_select->attr->nexthop); } /* Clear any route change flags. */ - bgp_zebra_clear_route_change_flags(rn); + bgp_zebra_clear_route_change_flags(dest); /* Reap old select bgp_path_info, if it has been removed */ if (old_select && CHECK_FLAG(old_select->flags, BGP_PATH_REMOVED)) - bgp_path_info_reap(rn, old_select); + bgp_path_info_reap(dest, old_select); return ret; } -/* - * Return true if the local ri for this rn is of type gateway mac - */ -static int evpn_route_is_def_gw(struct bgp *bgp, struct bgp_node *rn) -{ - struct bgp_path_info *tmp_pi = NULL; - struct bgp_path_info *local_pi = NULL; - - local_pi = NULL; - for (tmp_pi = bgp_node_get_bgp_path_info(rn); tmp_pi; - tmp_pi = tmp_pi->next) { - if (tmp_pi->peer == bgp->peer_self - && tmp_pi->type == ZEBRA_ROUTE_BGP - && tmp_pi->sub_type == BGP_ROUTE_STATIC) - local_pi = tmp_pi; - } - - if (!local_pi) - return 0; - - return local_pi->attr->default_gw; -} - - -/* - * Return true if the local ri for this rn has sticky set - */ -static int evpn_route_is_sticky(struct bgp *bgp, struct bgp_node *rn) +static struct bgp_path_info *bgp_evpn_route_get_local_path( + struct bgp *bgp, struct bgp_dest *dest) { struct bgp_path_info *tmp_pi; - struct bgp_path_info *local_pi; - - local_pi = NULL; - for (tmp_pi = bgp_node_get_bgp_path_info(rn); tmp_pi; - tmp_pi = tmp_pi->next) { - if (tmp_pi->peer == bgp->peer_self - && tmp_pi->type == ZEBRA_ROUTE_BGP - && tmp_pi->sub_type == BGP_ROUTE_STATIC) - local_pi = tmp_pi; - } - - if (!local_pi) - return 0; - - return local_pi->attr->sticky; -} - -/* - * create or update EVPN type4 route entry. - * This could be in the ES table or the global table. - * TODO: handle remote ES (type4) routes as well - */ -static int update_evpn_type4_route_entry(struct bgp *bgp, struct evpnes *es, - afi_t afi, safi_t safi, - struct bgp_node *rn, struct attr *attr, - int add, struct bgp_path_info **ri, - int *route_changed) -{ - char buf[ESI_STR_LEN]; - char buf1[INET6_ADDRSTRLEN]; - struct bgp_path_info *tmp_pi = NULL; - struct bgp_path_info *local_pi = NULL; /* local route entry if any */ - struct bgp_path_info *remote_pi = NULL; /* remote route entry if any */ - struct attr *attr_new = NULL; - struct prefix_evpn *evp = NULL; - - *ri = NULL; - *route_changed = 1; - evp = (struct prefix_evpn *)&rn->p; + struct bgp_path_info *local_pi = NULL; - /* locate the local and remote entries if any */ - for (tmp_pi = bgp_node_get_bgp_path_info(rn); tmp_pi; - tmp_pi = tmp_pi->next) { - if (tmp_pi->peer == bgp->peer_self - && tmp_pi->type == ZEBRA_ROUTE_BGP - && tmp_pi->sub_type == BGP_ROUTE_STATIC) + for (tmp_pi = bgp_dest_get_bgp_path_info(dest); tmp_pi; + tmp_pi = tmp_pi->next) { + if (bgp_evpn_is_path_local(bgp, tmp_pi)) { local_pi = tmp_pi; - if (tmp_pi->type == ZEBRA_ROUTE_BGP - && tmp_pi->sub_type == BGP_ROUTE_IMPORTED - && CHECK_FLAG(tmp_pi->flags, BGP_PATH_VALID)) - remote_pi = tmp_pi; - } - - /* we don't expect to see a remote_ri at this point. - * An ES route has esi + vtep_ip as the key, - * We shouldn't see the same route from any other vtep. - */ - if (remote_pi) { - flog_err( - EC_BGP_ES_INVALID, - "%u ERROR: local es route for ESI: %s Vtep %s also learnt from remote", - bgp->vrf_id, - esi_to_str(&evp->prefix.es_addr.esi, buf, sizeof(buf)), - ipaddr2str(&es->originator_ip, buf1, sizeof(buf1))); - return -1; - } - - if (!local_pi && !add) - return 0; - - /* create or update the entry */ - if (!local_pi) { - - /* Add or update attribute to hash */ - attr_new = bgp_attr_intern(attr); - - /* Create new route with its attribute. */ - tmp_pi = info_make(ZEBRA_ROUTE_BGP, BGP_ROUTE_STATIC, 0, - bgp->peer_self, attr_new, rn); - SET_FLAG(tmp_pi->flags, BGP_PATH_VALID); - - /* add the newly created path to the route-node */ - bgp_path_info_add(rn, tmp_pi); - } else { - tmp_pi = local_pi; - if (attrhash_cmp(tmp_pi->attr, attr) - && !CHECK_FLAG(tmp_pi->flags, BGP_PATH_REMOVED)) - *route_changed = 0; - else { - /* The attribute has changed. - * Add (or update) attribute to hash. */ - attr_new = bgp_attr_intern(attr); - bgp_path_info_set_flag(rn, tmp_pi, - BGP_PATH_ATTR_CHANGED); - - /* Restore route, if needed. */ - if (CHECK_FLAG(tmp_pi->flags, BGP_PATH_REMOVED)) - bgp_path_info_restore(rn, tmp_pi); - - /* Unintern existing, set to new. */ - bgp_attr_unintern(&tmp_pi->attr); - tmp_pi->attr = attr_new; - tmp_pi->uptime = bgp_clock(); + break; } } - /* Return back the route entry. */ - *ri = tmp_pi; - return 0; -} - -/* update evpn es (type-4) route */ -static int update_evpn_type4_route(struct bgp *bgp, - struct evpnes *es, - struct prefix_evpn *p) -{ - int ret = 0; - int route_changed = 0; - char buf[ESI_STR_LEN]; - char buf1[INET6_ADDRSTRLEN]; - afi_t afi = AFI_L2VPN; - safi_t safi = SAFI_EVPN; - struct attr attr; - struct attr *attr_new = NULL; - struct bgp_node *rn = NULL; - struct bgp_path_info *pi = NULL; - - memset(&attr, 0, sizeof(struct attr)); - - /* Build path-attribute for this route. */ - bgp_attr_default_set(&attr, BGP_ORIGIN_IGP); - attr.nexthop = es->originator_ip.ipaddr_v4; - attr.mp_nexthop_global_in = es->originator_ip.ipaddr_v4; - attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4; - - /* Set up extended community. */ - build_evpn_type4_route_extcomm(es, &attr); - - /* First, create (or fetch) route node within the ESI. */ - /* NOTE: There is no RD here. */ - rn = bgp_node_get(es->route_table, (struct prefix *)p); - - /* Create or update route entry. */ - ret = update_evpn_type4_route_entry(bgp, es, afi, safi, rn, &attr, 1, - &pi, &route_changed); - if (ret != 0) { - flog_err(EC_BGP_ES_INVALID, - "%u ERROR: Failed to updated ES route ESI: %s VTEP %s", - bgp->vrf_id, - esi_to_str(&p->prefix.es_addr.esi, buf, sizeof(buf)), - ipaddr2str(&es->originator_ip, buf1, sizeof(buf1))); - } - - assert(pi); - attr_new = pi->attr; - - /* Perform route selection; - * this is just to set the flags correctly - * as local route in the ES always wins. - */ - evpn_es_route_select_install(bgp, es, rn); - bgp_unlock_node(rn); - - /* If this is a new route or some attribute has changed, export the - * route to the global table. The route will be advertised to peers - * from there. Note that this table is a 2-level tree (RD-level + - * Prefix-level) similar to L3VPN routes. - */ - if (route_changed) { - struct bgp_path_info *global_pi; - - rn = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi, - (struct prefix *)p, &es->prd); - update_evpn_type4_route_entry(bgp, es, afi, safi, rn, attr_new, - 1, &global_pi, &route_changed); - - /* Schedule for processing and unlock node. */ - bgp_process(bgp, rn, afi, safi); - bgp_unlock_node(rn); - } - - /* Unintern temporary. */ - aspath_unintern(&attr.aspath); - return 0; + return local_pi; } static int update_evpn_type5_route_entry(struct bgp *bgp_evpn, struct bgp *bgp_vrf, afi_t afi, - safi_t safi, struct bgp_node *rn, + safi_t safi, struct bgp_dest *dest, struct attr *attr, int *route_changed) { struct attr *attr_new = NULL; @@ -1452,7 +1209,7 @@ static int update_evpn_type5_route_entry(struct bgp *bgp_evpn, *route_changed = 0; /* locate the local route entry if any */ - for (tmp_pi = bgp_node_get_bgp_path_info(rn); tmp_pi; + for (tmp_pi = bgp_dest_get_bgp_path_info(dest); tmp_pi; tmp_pi = tmp_pi->next) { if (tmp_pi->peer == bgp_evpn->peer_self && tmp_pi->type == ZEBRA_ROUTE_BGP @@ -1474,7 +1231,7 @@ static int update_evpn_type5_route_entry(struct bgp *bgp_evpn, /* create the route info from attribute */ pi = info_make(ZEBRA_ROUTE_BGP, BGP_ROUTE_STATIC, 0, - bgp_evpn->peer_self, attr_new, rn); + bgp_evpn->peer_self, attr_new, dest); SET_FLAG(pi->flags, BGP_PATH_VALID); /* Type-5 routes advertise the L3-VNI */ @@ -1484,7 +1241,7 @@ static int update_evpn_type5_route_entry(struct bgp *bgp_evpn, pi->extra->num_labels = 1; /* add the route entry to route node*/ - bgp_path_info_add(rn, pi); + bgp_path_info_add(dest, pi); } else { tmp_pi = local_pi; @@ -1496,12 +1253,12 @@ static int update_evpn_type5_route_entry(struct bgp *bgp_evpn, /* The attribute has changed. */ /* Add (or update) attribute to hash. */ attr_new = bgp_attr_intern(attr); - bgp_path_info_set_flag(rn, tmp_pi, + bgp_path_info_set_flag(dest, tmp_pi, BGP_PATH_ATTR_CHANGED); /* Restore route, if needed. */ if (CHECK_FLAG(tmp_pi->flags, BGP_PATH_REMOVED)) - bgp_path_info_restore(rn, tmp_pi); + bgp_path_info_restore(dest, tmp_pi); /* Unintern existing, set to new. */ bgp_attr_unintern(&tmp_pi->attr); @@ -1519,7 +1276,7 @@ static int update_evpn_type5_route(struct bgp *bgp_vrf, struct prefix_evpn *evp, afi_t afi = AFI_L2VPN; safi_t safi = SAFI_EVPN; struct attr attr; - struct bgp_node *rn = NULL; + struct bgp_dest *dest = NULL; struct bgp *bgp_evpn = NULL; int route_changed = 0; @@ -1531,33 +1288,70 @@ static int update_evpn_type5_route(struct bgp *bgp_vrf, struct prefix_evpn *evp, * present, else treat as locally originated. */ if (src_attr) - bgp_attr_dup(&attr, src_attr); + attr = *src_attr; else { memset(&attr, 0, sizeof(struct attr)); bgp_attr_default_set(&attr, BGP_ORIGIN_IGP); } - /* Set nexthop to ourselves and fill in the Router MAC. */ - attr.nexthop = bgp_vrf->originator_ip; - attr.mp_nexthop_global_in = bgp_vrf->originator_ip; + + /* Advertise Primary IP (PIP) is enabled, send individual + * IP (default instance router-id) as nexthop. + * PIP is disabled or vrr interface is not present + * use anycast-IP as nexthop and anycast RMAC. + */ + if (!bgp_vrf->evpn_info->advertise_pip || + (!bgp_vrf->evpn_info->is_anycast_mac)) { + attr.nexthop = bgp_vrf->originator_ip; + attr.mp_nexthop_global_in = bgp_vrf->originator_ip; + memcpy(&attr.rmac, &bgp_vrf->rmac, ETH_ALEN); + } else { + /* copy sys rmac */ + memcpy(&attr.rmac, &bgp_vrf->evpn_info->pip_rmac, ETH_ALEN); + if (bgp_vrf->evpn_info->pip_ip.s_addr != INADDR_ANY) { + attr.nexthop = bgp_vrf->evpn_info->pip_ip; + attr.mp_nexthop_global_in = bgp_vrf->evpn_info->pip_ip; + } else if (bgp_vrf->evpn_info->pip_ip.s_addr == INADDR_ANY) + if (bgp_debug_zebra(NULL)) { + char buf1[PREFIX_STRLEN]; + + zlog_debug("VRF %s evp %s advertise-pip primary ip is not configured", + vrf_id_to_name(bgp_vrf->vrf_id), + prefix2str(evp, buf1, sizeof(buf1))); + } + } + + if (bgp_debug_zebra(NULL)) { + char buf[ETHER_ADDR_STRLEN]; + char buf1[PREFIX_STRLEN]; + char buf2[INET6_ADDRSTRLEN]; + + zlog_debug("VRF %s type-5 route evp %s RMAC %s nexthop %s", + vrf_id_to_name(bgp_vrf->vrf_id), + prefix2str(evp, buf1, sizeof(buf1)), + prefix_mac2str(&attr.rmac, buf, sizeof(buf)), + inet_ntop(AF_INET, &attr.nexthop, buf2, + INET_ADDRSTRLEN)); + } + attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4; - memcpy(&attr.rmac, &bgp_vrf->rmac, sizeof(struct ethaddr)); /* Setup RT and encap extended community */ build_evpn_type5_route_extcomm(bgp_vrf, &attr); /* get the route node in global table */ - rn = bgp_afi_node_get(bgp_evpn->rib[afi][safi], afi, safi, - (struct prefix *)evp, &bgp_vrf->vrf_prd); - assert(rn); + dest = bgp_global_evpn_node_get(bgp_evpn->rib[afi][safi], afi, safi, + (const struct prefix_evpn *)evp, + &bgp_vrf->vrf_prd); + assert(dest); /* create or update the route entry within the route node */ - update_evpn_type5_route_entry(bgp_evpn, bgp_vrf, afi, safi, rn, &attr, + update_evpn_type5_route_entry(bgp_evpn, bgp_vrf, afi, safi, dest, &attr, &route_changed); /* schedule for processing and unlock node */ if (route_changed) { - bgp_process(bgp_evpn, rn, afi, safi); - bgp_unlock_node(rn); + bgp_process(bgp_evpn, dest, afi, safi); + bgp_dest_unlock_node(dest); } /* uninten temporary */ @@ -1566,15 +1360,137 @@ static int update_evpn_type5_route(struct bgp *bgp_vrf, struct prefix_evpn *evp, return 0; } +static void bgp_evpn_get_sync_info(struct bgp *bgp, esi_t *esi, + struct bgp_node *rn, uint32_t loc_seq, uint32_t *max_sync_seq, + bool *active_on_peer, bool *peer_router, + bool *proxy_from_peer) +{ + struct bgp_path_info *tmp_pi; + struct bgp_path_info *second_best_path = NULL; + uint32_t tmp_mm_seq = 0; + esi_t *tmp_esi; + int paths_eq; + + /* find the best non-local path. a local path can only be present + * as best path + */ + for (tmp_pi = bgp_dest_get_bgp_path_info(rn); tmp_pi; + tmp_pi = tmp_pi->next) { + if (tmp_pi->sub_type != BGP_ROUTE_IMPORTED || + !CHECK_FLAG(tmp_pi->flags, BGP_PATH_VALID)) + continue; + + if (bgp_evpn_path_info_cmp(bgp, tmp_pi, + second_best_path, &paths_eq)) + second_best_path = tmp_pi; + } + + if (!second_best_path) + return; + + tmp_esi = bgp_evpn_attr_get_esi(second_best_path->attr); + /* if this has the same ES desination as the local path + * it is a sync path + */ + if (!memcmp(esi, tmp_esi, sizeof(esi_t))) { + tmp_mm_seq = mac_mobility_seqnum(second_best_path->attr); + if (tmp_mm_seq < loc_seq) + return; + + /* we have a non-proxy path from the ES peer. */ + if (second_best_path->attr->es_flags & + ATTR_ES_PROXY_ADVERT) { + *proxy_from_peer = true; + } else { + *active_on_peer = true; + } + + if (second_best_path->attr->router_flag) + *peer_router = true; + + /* we use both proxy and non-proxy imports to + * determine the max sync sequence + */ + if (tmp_mm_seq > *max_sync_seq) + *max_sync_seq = tmp_mm_seq; + } +} + +/* Bubble up sync-info from all paths (non-best) to the local-path. + * This is need for MM sequence number syncing and proxy advertisement. + * Note: The local path can only exist as a best path in the + * VPN route table. It will take precedence over all sync paths. + */ +static void update_evpn_route_entry_sync_info(struct bgp *bgp, + struct bgp_node *rn, struct attr *attr, uint32_t loc_seq, + bool setup_sync) +{ + esi_t *esi; + struct prefix_evpn *evp = (struct prefix_evpn *)&rn->p; + + if (evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE) + return; + + esi = bgp_evpn_attr_get_esi(attr); + if (bgp_evpn_is_esi_valid(esi)) { + if (setup_sync) { + uint32_t max_sync_seq = 0; + bool active_on_peer = false; + bool peer_router = false; + bool proxy_from_peer = false; + + bgp_evpn_get_sync_info(bgp, esi, rn, loc_seq, + &max_sync_seq, &active_on_peer, + &peer_router, &proxy_from_peer); + attr->mm_sync_seqnum = max_sync_seq; + if (active_on_peer) + attr->es_flags |= ATTR_ES_PEER_ACTIVE; + else + attr->es_flags &= ~ATTR_ES_PEER_ACTIVE; + if (proxy_from_peer) + attr->es_flags |= ATTR_ES_PEER_PROXY; + else + attr->es_flags &= ~ATTR_ES_PEER_PROXY; + if (peer_router) + attr->es_flags |= ATTR_ES_PEER_ROUTER; + else + attr->es_flags &= ~ATTR_ES_PEER_ROUTER; + + if (BGP_DEBUG(evpn_mh, EVPN_MH_RT)) { + char prefix_buf[PREFIX_STRLEN]; + char esi_buf[ESI_STR_LEN]; + + zlog_debug("setup sync info for %s es %s max_seq %d %s%s%s", + prefix2str(evp, prefix_buf, + sizeof(prefix_buf)), + esi_to_str(esi, esi_buf, + sizeof(esi_buf)), + max_sync_seq, + (attr->es_flags & ATTR_ES_PEER_ACTIVE) ? + "peer-active " : "", + (attr->es_flags & ATTR_ES_PEER_PROXY) ? + "peer-proxy " : "", + (attr->es_flags & ATTR_ES_PEER_ROUTER) ? + "peer-router " : ""); + } + } + } else { + attr->mm_sync_seqnum = 0; + attr->es_flags &= ~ATTR_ES_PEER_ACTIVE; + attr->es_flags &= ~ATTR_ES_PEER_PROXY; + } +} + /* * Create or update EVPN route entry. This could be in the VNI route table * or the global route table. */ static int update_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, - afi_t afi, safi_t safi, struct bgp_node *rn, + afi_t afi, safi_t safi, struct bgp_dest *dest, struct attr *attr, int add, struct bgp_path_info **pi, uint8_t flags, - uint32_t seq) + uint32_t seq, bool setup_sync, + bool *old_is_sync) { struct bgp_path_info *tmp_pi; struct bgp_path_info *local_pi; @@ -1583,21 +1499,14 @@ static int update_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, uint32_t num_labels = 1; int route_change = 1; uint8_t sticky = 0; - struct prefix_evpn *evp; + const struct prefix_evpn *evp; *pi = NULL; - evp = (struct prefix_evpn *)&rn->p; + evp = (const struct prefix_evpn *)bgp_dest_get_prefix(dest); memset(&label, 0, sizeof(label)); /* See if this is an update of an existing route, or a new add. */ - local_pi = NULL; - for (tmp_pi = bgp_node_get_bgp_path_info(rn); tmp_pi; - tmp_pi = tmp_pi->next) { - if (tmp_pi->peer == bgp->peer_self - && tmp_pi->type == ZEBRA_ROUTE_BGP - && tmp_pi->sub_type == BGP_ROUTE_STATIC) - local_pi = tmp_pi; - } + local_pi = bgp_evpn_route_get_local_path(bgp, dest); /* If route doesn't exist already, create a new one, if told to. * Otherwise act based on whether the attributes of the route have @@ -1606,6 +1515,14 @@ static int update_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, if (!local_pi && !add) return 0; + if (old_is_sync && local_pi) + *old_is_sync = bgp_evpn_attr_is_sync(local_pi->attr); + + /* if a local path is being added with a non-zero esi look + * for SYNC paths from ES peers and bubble up the sync-info + */ + update_evpn_route_entry_sync_info(bgp, dest, attr, seq, setup_sync); + /* For non-GW MACs, update MAC mobility seq number, if needed. */ if (seq && !CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_GW)) add_mac_mobility_to_attr(seq, attr); @@ -1621,7 +1538,7 @@ static int update_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, /* Create new route with its attribute. */ tmp_pi = info_make(ZEBRA_ROUTE_BGP, BGP_ROUTE_STATIC, 0, - bgp->peer_self, attr_new, rn); + bgp->peer_self, attr_new, dest); SET_FLAG(tmp_pi->flags, BGP_PATH_VALID); bgp_path_info_extra_get(tmp_pi); @@ -1645,7 +1562,10 @@ static int update_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, memcpy(&tmp_pi->extra->label, label, sizeof(label)); tmp_pi->extra->num_labels = num_labels; - bgp_path_info_add(rn, tmp_pi); + /* Mark route as self type-2 route */ + if (flags && CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_SVI_IP)) + tmp_pi->extra->af_flags = BGP_EVPN_MACIP_TYPE_SVI_IP; + bgp_path_info_add(dest, tmp_pi); } else { tmp_pi = local_pi; if (attrhash_cmp(tmp_pi->attr, attr) @@ -1674,7 +1594,7 @@ static int update_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, /* The attribute has changed. */ /* Add (or update) attribute to hash. */ attr_new = bgp_attr_intern(attr); - bgp_path_info_set_flag(rn, tmp_pi, + bgp_path_info_set_flag(dest, tmp_pi, BGP_PATH_ATTR_CHANGED); /* Extract MAC mobility sequence number, if any. */ @@ -1684,7 +1604,7 @@ static int update_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, /* Restore route, if needed. */ if (CHECK_FLAG(tmp_pi->flags, BGP_PATH_REMOVED)) - bgp_path_info_restore(rn, tmp_pi); + bgp_path_info_restore(dest, tmp_pi); /* Unintern existing, set to new. */ bgp_attr_unintern(&tmp_pi->attr); @@ -1699,13 +1619,14 @@ static int update_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, } static void evpn_zebra_reinstall_best_route(struct bgp *bgp, - struct bgpevpn *vpn, struct bgp_node *rn) + struct bgpevpn *vpn, + struct bgp_dest *dest) { struct bgp_path_info *tmp_ri; struct bgp_path_info *curr_select = NULL; - for (tmp_ri = bgp_node_get_bgp_path_info(rn); - tmp_ri; tmp_ri = tmp_ri->next) { + for (tmp_ri = bgp_dest_get_bgp_path_info(dest); tmp_ri; + tmp_ri = tmp_ri->next) { if (CHECK_FLAG(tmp_ri->flags, BGP_PATH_SELECTED)) { curr_select = tmp_ri; break; @@ -1713,10 +1634,11 @@ static void evpn_zebra_reinstall_best_route(struct bgp *bgp, } if (curr_select && curr_select->type == ZEBRA_ROUTE_BGP - && curr_select->sub_type == BGP_ROUTE_IMPORTED) + && (curr_select->sub_type == BGP_ROUTE_IMPORTED || + bgp_evpn_attr_is_sync(curr_select->attr))) evpn_zebra_install(bgp, vpn, - (struct prefix_evpn *)&rn->p, - curr_select); + (const struct prefix_evpn *)bgp_dest_get_prefix(dest), + curr_select); } /* @@ -1735,21 +1657,19 @@ static void evpn_zebra_reinstall_best_route(struct bgp *bgp, */ static void evpn_cleanup_local_non_best_route(struct bgp *bgp, struct bgpevpn *vpn, - struct bgp_node *rn, + struct bgp_dest *dest, struct bgp_path_info *local_pi) { - char buf[PREFIX_STRLEN]; - /* local path was not picked as the winner; kick it out */ - if (bgp_debug_zebra(NULL)) { - zlog_debug("evicting local evpn prefix %s as remote won", - prefix2str(&rn->p, buf, sizeof(buf))); - } - evpn_delete_old_local_route(bgp, vpn, rn, local_pi); - bgp_path_info_reap(rn, local_pi); + if (bgp_debug_zebra(NULL)) + zlog_debug("evicting local evpn prefix %pRN as remote won", + dest); + + evpn_delete_old_local_route(bgp, vpn, dest, local_pi, NULL); + bgp_path_info_reap(dest, local_pi); /* tell zebra to re-add the best remote path */ - evpn_zebra_reinstall_best_route(bgp, vpn, rn); + evpn_zebra_reinstall_best_route(bgp, vpn, dest); } /* @@ -1758,9 +1678,9 @@ static void evpn_cleanup_local_non_best_route(struct bgp *bgp, */ static int update_evpn_route(struct bgp *bgp, struct bgpevpn *vpn, struct prefix_evpn *p, uint8_t flags, - uint32_t seq) + uint32_t seq, esi_t *esi) { - struct bgp_node *rn; + struct bgp_dest *dest; struct attr attr; struct attr *attr_new; int add_l3_ecomm = 0; @@ -1768,6 +1688,7 @@ static int update_evpn_route(struct bgp *bgp, struct bgpevpn *vpn, afi_t afi = AFI_L2VPN; safi_t safi = SAFI_EVPN; int route_change; + bool old_is_sync = false; memset(&attr, 0, sizeof(struct attr)); @@ -1780,6 +1701,13 @@ static int update_evpn_route(struct bgp *bgp, struct bgpevpn *vpn, attr.default_gw = CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_GW) ? 1 : 0; attr.router_flag = CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_ROUTER_FLAG) ? 1 : 0; + if (CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_PROXY_ADVERT)) + attr.es_flags |= ATTR_ES_PROXY_ADVERT; + + if (esi && bgp_evpn_is_esi_valid(esi)) { + memcpy(&attr.esi, esi, sizeof(esi_t)); + attr.es_flags |= ATTR_ES_IS_LOCAL; + } /* PMSI is only needed for type-3 routes */ if (p->prefix.route_type == BGP_EVPN_IMET_ROUTE) { @@ -1787,9 +1715,31 @@ static int update_evpn_route(struct bgp *bgp, struct bgpevpn *vpn, attr.pmsi_tnl_type = PMSI_TNLTYPE_INGR_REPL; } + if (bgp_debug_zebra(NULL)) { + char buf[ETHER_ADDR_STRLEN]; + char buf1[PREFIX_STRLEN]; + char buf3[ESI_STR_LEN]; + + zlog_debug("VRF %s vni %u type-2 route evp %s RMAC %s nexthop %s esi %s", + vpn->bgp_vrf ? + vrf_id_to_name(vpn->bgp_vrf->vrf_id) : " ", + vpn->vni, + prefix2str(p, buf1, sizeof(buf1)), + prefix_mac2str(&attr.rmac, buf, + sizeof(buf)), + inet_ntoa(attr.mp_nexthop_global_in), + esi_to_str(esi, buf3, sizeof(buf3))); + } /* router mac is only needed for type-2 routes here. */ - if (p->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) - bgpevpn_get_rmac(vpn, &attr.rmac); + if (p->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) { + uint8_t af_flags = 0; + + if (CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_SVI_IP)) + SET_FLAG(af_flags, BGP_EVPN_MACIP_TYPE_SVI_IP); + + bgp_evpn_get_rmac_nexthop(vpn, p, &attr, af_flags); + } + vni2label(vpn->vni, &(attr.label)); /* Include L3 VNI related RTs and RMAC for type-2 routes, if they're @@ -1808,32 +1758,55 @@ static int update_evpn_route(struct bgp *bgp, struct bgpevpn *vpn, /* First, create (or fetch) route node within the VNI. */ /* NOTE: There is no RD here. */ - rn = bgp_node_get(vpn->route_table, (struct prefix *)p); + dest = bgp_node_get(vpn->route_table, (struct prefix *)p); /* Create or update route entry. */ - route_change = update_evpn_route_entry(bgp, vpn, afi, safi, rn, &attr, - 1, &pi, flags, seq); + route_change = update_evpn_route_entry(bgp, vpn, afi, safi, dest, &attr, + 1, &pi, flags, seq, + true /* setup_sync */, &old_is_sync); assert(pi); attr_new = pi->attr; /* lock ri to prevent freeing in evpn_route_select_install */ bgp_path_info_lock(pi); - /* Perform route selection; this is just to set the flags correctly - * as local route in the VNI always wins. - */ - evpn_route_select_install(bgp, vpn, rn); + + /* Perform route selection. Normally, the local route in the + * VNI is expected to win and be the best route. However, if + * there is a race condition where a host moved from local to + * remote and the remote route was received in BGP just prior + * to the local MACIP notification from zebra, the remote + * route would win, and we should evict the defunct local route + * and (re)install the remote route into zebra. + */ + evpn_route_select_install(bgp, vpn, dest); /* * If the new local route was not selected evict it and tell zebra * to re-add the best remote dest. BGP doesn't retain non-best local * routes. */ - if (!CHECK_FLAG(pi->flags, BGP_PATH_SELECTED)) { + if (CHECK_FLAG(pi->flags, BGP_PATH_REMOVED)) { route_change = 0; - evpn_cleanup_local_non_best_route(bgp, vpn, rn, pi); + } else { + if (!CHECK_FLAG(pi->flags, BGP_PATH_SELECTED)) { + route_change = 0; + evpn_cleanup_local_non_best_route(bgp, vpn, dest, pi); + } else { + bool new_is_sync; + + /* If the local path already existed and is still the + * best path we need to also check if it transitioned + * from being a sync path to a non-sync path. If it + * it did we need to notify zebra that the sync-path + * has been removed. + */ + new_is_sync = bgp_evpn_attr_is_sync(pi->attr); + if (!new_is_sync && old_is_sync) + evpn_zebra_uninstall(bgp, vpn, p, zero_vtep_ip); + } } bgp_path_info_unlock(pi); - bgp_unlock_node(rn); + bgp_dest_unlock_node(dest); /* If this is a new route or some attribute has changed, export the * route to the global table. The route will be advertised to peers @@ -1843,14 +1816,16 @@ static int update_evpn_route(struct bgp *bgp, struct bgpevpn *vpn, if (route_change) { struct bgp_path_info *global_pi; - rn = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi, - (struct prefix *)p, &vpn->prd); - update_evpn_route_entry(bgp, vpn, afi, safi, rn, attr_new, 1, - &global_pi, flags, seq); + dest = bgp_global_evpn_node_get(bgp->rib[afi][safi], afi, safi, + (const struct prefix_evpn *)p, + &vpn->prd); + update_evpn_route_entry(bgp, vpn, afi, safi, dest, attr_new, 1, + &global_pi, flags, seq, + false /* setup_sync */, NULL /* old_is_sync */); /* Schedule for processing and unlock node. */ - bgp_process(bgp, rn, afi, safi); - bgp_unlock_node(rn); + bgp_process(bgp, dest, afi, safi); + bgp_dest_unlock_node(dest); } /* Unintern temporary. */ @@ -1863,8 +1838,8 @@ static int update_evpn_route(struct bgp *bgp, struct bgpevpn *vpn, * Delete EVPN route entry. * The entry can be in ESI/VNI table or the global table. */ -static void delete_evpn_route_entry(struct bgp *bgp, afi_t afi, safi_t safi, - struct bgp_node *rn, +void delete_evpn_route_entry(struct bgp *bgp, afi_t afi, safi_t safi, + struct bgp_dest *dest, struct bgp_path_info **pi) { struct bgp_path_info *tmp_pi; @@ -1872,7 +1847,7 @@ static void delete_evpn_route_entry(struct bgp *bgp, afi_t afi, safi_t safi, *pi = NULL; /* Now, find matching route. */ - for (tmp_pi = bgp_node_get_bgp_path_info(rn); tmp_pi; + for (tmp_pi = bgp_dest_get_bgp_path_info(dest); tmp_pi; tmp_pi = tmp_pi->next) if (tmp_pi->peer == bgp->peer_self && tmp_pi->type == ZEBRA_ROUTE_BGP @@ -1883,57 +1858,7 @@ static void delete_evpn_route_entry(struct bgp *bgp, afi_t afi, safi_t safi, /* Mark route for delete. */ if (tmp_pi) - bgp_path_info_delete(rn, tmp_pi); -} - - - -/* Delete EVPN ES (type-4) route */ -static int delete_evpn_type4_route(struct bgp *bgp, - struct evpnes *es, - struct prefix_evpn *p) -{ - afi_t afi = AFI_L2VPN; - safi_t safi = SAFI_EVPN; - struct bgp_path_info *pi; - struct bgp_node *rn = NULL; /* rn in esi table */ - struct bgp_node *global_rn = NULL; /* rn in global table */ - - /* First, locate the route node within the ESI. - * If it doesn't exist, ther is nothing to do. - * Note: there is no RD here. - */ - rn = bgp_node_lookup(es->route_table, (struct prefix *)p); - if (!rn) - return 0; - - /* Next, locate route node in the global EVPN routing table. - * Note that this table is a 2-level tree (RD-level + Prefix-level) - */ - global_rn = bgp_afi_node_lookup(bgp->rib[afi][safi], afi, safi, - (struct prefix *)p, &es->prd); - if (global_rn) { - - /* Delete route entry in the global EVPN table. */ - delete_evpn_route_entry(bgp, afi, safi, global_rn, &pi); - - /* Schedule for processing - withdraws to peers happen from - * this table. - */ - if (pi) - bgp_process(bgp, global_rn, afi, safi); - bgp_unlock_node(global_rn); - } - - /* - * Delete route entry in the ESI route table. - * This can just be removed. - */ - delete_evpn_route_entry(bgp, afi, safi, rn, &pi); - if (pi) - bgp_path_info_reap(rn, pi); - bgp_unlock_node(rn); - return 0; + bgp_path_info_delete(dest, tmp_pi); } /* Delete EVPN type5 route */ @@ -1941,7 +1866,7 @@ static int delete_evpn_type5_route(struct bgp *bgp_vrf, struct prefix_evpn *evp) { afi_t afi = AFI_L2VPN; safi_t safi = SAFI_EVPN; - struct bgp_node *rn = NULL; + struct bgp_dest *dest = NULL; struct bgp_path_info *pi = NULL; struct bgp *bgp_evpn = NULL; /* evpn bgp instance */ @@ -1950,15 +1875,15 @@ static int delete_evpn_type5_route(struct bgp *bgp_vrf, struct prefix_evpn *evp) return 0; /* locate the global route entry for this type-5 prefix */ - rn = bgp_afi_node_lookup(bgp_evpn->rib[afi][safi], afi, safi, - (struct prefix *)evp, &bgp_vrf->vrf_prd); - if (!rn) + dest = bgp_global_evpn_node_lookup(bgp_evpn->rib[afi][safi], afi, safi, + (const struct prefix_evpn *)evp, &bgp_vrf->vrf_prd); + if (!dest) return 0; - delete_evpn_route_entry(bgp_evpn, afi, safi, rn, &pi); + delete_evpn_route_entry(bgp_evpn, afi, safi, dest, &pi); if (pi) - bgp_process(bgp_evpn, rn, afi, safi); - bgp_unlock_node(rn); + bgp_process(bgp_evpn, dest, afi, safi); + bgp_dest_unlock_node(dest); return 0; } @@ -1969,7 +1894,7 @@ static int delete_evpn_type5_route(struct bgp *bgp_vrf, struct prefix_evpn *evp) static int delete_evpn_route(struct bgp *bgp, struct bgpevpn *vpn, struct prefix_evpn *p) { - struct bgp_node *rn, *global_rn; + struct bgp_dest *dest, *global_dest; struct bgp_path_info *pi; afi_t afi = AFI_L2VPN; safi_t safi = SAFI_EVPN; @@ -1979,38 +1904,175 @@ static int delete_evpn_route(struct bgp *bgp, struct bgpevpn *vpn, * is nothing further to do. */ /* NOTE: There is no RD here. */ - rn = bgp_node_lookup(vpn->route_table, (struct prefix *)p); - if (!rn) + dest = bgp_node_lookup(vpn->route_table, (struct prefix *)p); + if (!dest) return 0; /* Next, locate route node in the global EVPN routing table. Note that * this table is a 2-level tree (RD-level + Prefix-level) similar to * L3VPN routes. */ - global_rn = bgp_afi_node_lookup(bgp->rib[afi][safi], afi, safi, - (struct prefix *)p, &vpn->prd); - if (global_rn) { + global_dest = bgp_global_evpn_node_lookup(bgp->rib[afi][safi], afi, safi, + (const struct prefix_evpn *)p, &vpn->prd); + if (global_dest) { /* Delete route entry in the global EVPN table. */ - delete_evpn_route_entry(bgp, afi, safi, global_rn, &pi); + delete_evpn_route_entry(bgp, afi, safi, global_dest, &pi); + + /* Schedule for processing - withdraws to peers happen from + * this table. + */ + if (pi) + bgp_process(bgp, global_dest, afi, safi); + bgp_dest_unlock_node(global_dest); + } + + /* Delete route entry in the VNI route table. This can just be removed. + */ + delete_evpn_route_entry(bgp, afi, safi, dest, &pi); + if (pi) { + bgp_path_info_reap(dest, pi); + evpn_route_select_install(bgp, vpn, dest); + } + bgp_dest_unlock_node(dest); + + return 0; +} + +static void bgp_evpn_update_type2_route_entry(struct bgp *bgp, + struct bgpevpn *vpn, struct bgp_node *rn, + struct bgp_path_info *local_pi, const char *caller) +{ + afi_t afi = AFI_L2VPN; + safi_t safi = SAFI_EVPN; + struct bgp_path_info *pi; + struct attr attr; + struct attr *attr_new; + uint32_t seq; + int add_l3_ecomm = 0; + struct bgp_node *global_rn; + struct bgp_path_info *global_pi; + struct prefix_evpn *evp = (struct prefix_evpn *)&rn->p; + int route_change; + bool old_is_sync = false; + + if (CHECK_FLAG(local_pi->flags, BGP_PATH_REMOVED)) + return; + + /* + * Build attribute per local route as the MAC mobility and + * some other values could differ for different routes. The + * attributes will be shared in the hash table. + */ + bgp_attr_default_set(&attr, BGP_ORIGIN_IGP); + attr.nexthop = vpn->originator_ip; + attr.mp_nexthop_global_in = vpn->originator_ip; + attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4; + attr.sticky = (local_pi->attr->sticky) ? 1 : 0; + attr.router_flag = (local_pi->attr->router_flag) ? 1 : 0; + attr.es_flags = local_pi->attr->es_flags; + if (local_pi->attr->default_gw) { + attr.default_gw = 1; + if (is_evpn_prefix_ipaddr_v6(evp)) + attr.router_flag = 1; + } + memcpy(&attr.esi, &local_pi->attr->esi, sizeof(esi_t)); + bgp_evpn_get_rmac_nexthop(vpn, evp, &attr, + local_pi->extra->af_flags); + vni2label(vpn->vni, &(attr.label)); + /* Add L3 VNI RTs and RMAC for non IPv6 link-local if + * using L3 VNI for type-2 routes also. + */ + if ((is_evpn_prefix_ipaddr_v4(evp) || + !IN6_IS_ADDR_LINKLOCAL( + &evp->prefix.macip_addr.ip.ipaddr_v6)) && + CHECK_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS) && + bgpevpn_get_l3vni(vpn)) + add_l3_ecomm = 1; + + /* Set up extended community. */ + build_evpn_route_extcomm(vpn, &attr, add_l3_ecomm); + seq = mac_mobility_seqnum(local_pi->attr); + + if (bgp_debug_zebra(NULL)) { + char buf[ETHER_ADDR_STRLEN]; + char buf1[PREFIX_STRLEN]; + char buf3[ESI_STR_LEN]; + + zlog_debug("VRF %s vni %u evp %s RMAC %s nexthop %s esi %s esf 0x%x from %s", + vpn->bgp_vrf ? + vrf_id_to_name(vpn->bgp_vrf->vrf_id) : " ", + vpn->vni, + prefix2str(evp, buf1, sizeof(buf1)), + prefix_mac2str(&attr.rmac, buf, sizeof(buf)), + inet_ntoa(attr.mp_nexthop_global_in), + esi_to_str(&attr.esi, buf3, sizeof(buf3)), + attr.es_flags, caller); + } + + /* Update the route entry. */ + route_change = update_evpn_route_entry(bgp, vpn, afi, safi, + rn, &attr, 0, &pi, 0, seq, + true /* setup_sync */, &old_is_sync); + + assert(pi); + attr_new = pi->attr; + /* lock ri to prevent freeing in evpn_route_select_install */ + bgp_path_info_lock(pi); + + /* Perform route selection. Normally, the local route in the + * VNI is expected to win and be the best route. However, + * under peculiar situations (e.g., tunnel (next hop) IP change + * that causes best selection to be based on next hop), a + * remote route could win. If the local route is the best, + * ensure it is updated in the global EVPN route table and + * advertised to peers; otherwise, ensure it is evicted and + * (re)install the remote route into zebra. + */ + evpn_route_select_install(bgp, vpn, rn); - /* Schedule for processing - withdraws to peers happen from - * this table. - */ - if (pi) - bgp_process(bgp, global_rn, afi, safi); - bgp_unlock_node(global_rn); + if (CHECK_FLAG(pi->flags, BGP_PATH_REMOVED)) { + route_change = 0; + } else { + if (!CHECK_FLAG(pi->flags, BGP_PATH_SELECTED)) { + route_change = 0; + evpn_cleanup_local_non_best_route(bgp, vpn, rn, pi); + } else { + bool new_is_sync; + + /* If the local path already existed and is still the + * best path we need to also check if it transitioned + * from being a sync path to a non-sync path. If it + * it did we need to notify zebra that the sync-path + * has been removed. + */ + new_is_sync = bgp_evpn_attr_is_sync(pi->attr); + if (!new_is_sync && old_is_sync) + evpn_zebra_uninstall(bgp, vpn, + evp, zero_vtep_ip); + } } - /* Delete route entry in the VNI route table. This can just be removed. - */ - delete_evpn_route_entry(bgp, afi, safi, rn, &pi); - if (pi) { - bgp_path_info_reap(rn, pi); - evpn_route_select_install(bgp, vpn, rn); + + /* unlock pi */ + bgp_path_info_unlock(pi); + + if (route_change) { + /* Update route in global routing table. */ + global_rn = bgp_global_evpn_node_get(bgp->rib[afi][safi], + afi, safi, evp, &vpn->prd); + assert(global_rn); + update_evpn_route_entry(bgp, vpn, afi, safi, global_rn, + attr_new, 0, &global_pi, 0, + mac_mobility_seqnum(attr_new), + false /* setup_sync */, NULL /* old_is_sync */); + + /* Schedule for processing and unlock node. */ + bgp_process(bgp, global_rn, afi, safi); + bgp_dest_unlock_node(global_rn); } - bgp_unlock_node(rn); - return 0; + /* Unintern temporary. */ + aspath_unintern(&attr.aspath); } /* @@ -2019,102 +2081,34 @@ static int delete_evpn_route(struct bgp *bgp, struct bgpevpn *vpn, */ static int update_all_type2_routes(struct bgp *bgp, struct bgpevpn *vpn) { - afi_t afi; - safi_t safi; - struct bgp_node *rn; - struct bgp_path_info *pi, *tmp_pi; - struct attr attr; - struct attr *attr_new; - uint32_t seq; - int add_l3_ecomm = 0; - - afi = AFI_L2VPN; - safi = SAFI_EVPN; + struct bgp_dest *dest; + struct bgp_path_info *tmp_pi; /* Walk this VNI's route table and update local type-2 routes. For any * routes updated, update corresponding entry in the global table too. */ - for (rn = bgp_table_top(vpn->route_table); rn; - rn = bgp_route_next(rn)) { - struct prefix_evpn *evp = (struct prefix_evpn *)&rn->p; - struct bgp_node *rd_rn; - struct bgp_path_info *global_pi; + for (dest = bgp_table_top(vpn->route_table); dest; + dest = bgp_route_next(dest)) { + const struct prefix_evpn *evp = + (const struct prefix_evpn *)bgp_dest_get_prefix(dest); if (evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE) continue; /* Identify local route. */ - for (tmp_pi = bgp_node_get_bgp_path_info(rn); tmp_pi; - tmp_pi = tmp_pi->next) { + for (tmp_pi = bgp_dest_get_bgp_path_info(dest); tmp_pi; + tmp_pi = tmp_pi->next) { if (tmp_pi->peer == bgp->peer_self - && tmp_pi->type == ZEBRA_ROUTE_BGP - && tmp_pi->sub_type == BGP_ROUTE_STATIC) + && tmp_pi->type == ZEBRA_ROUTE_BGP + && tmp_pi->sub_type == BGP_ROUTE_STATIC) break; } if (!tmp_pi) continue; - /* - * Build attribute per local route as the MAC mobility and - * some other values could differ for different routes. The - * attributes will be shared in the hash table. - */ - bgp_attr_default_set(&attr, BGP_ORIGIN_IGP); - attr.nexthop = vpn->originator_ip; - attr.mp_nexthop_global_in = vpn->originator_ip; - attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4; - bgpevpn_get_rmac(vpn, &attr.rmac); - - if (evpn_route_is_sticky(bgp, rn)) - attr.sticky = 1; - else if (evpn_route_is_def_gw(bgp, rn)) { - attr.default_gw = 1; - if (is_evpn_prefix_ipaddr_v6(evp)) - attr.router_flag = 1; - } - - /* Add L3 VNI RTs and RMAC for non IPv6 link-local if - * using L3 VNI for type-2 routes also. - */ - if ((is_evpn_prefix_ipaddr_v4(evp) || - !IN6_IS_ADDR_LINKLOCAL( - &evp->prefix.macip_addr.ip.ipaddr_v6)) && - CHECK_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS) && - bgpevpn_get_l3vni(vpn)) - add_l3_ecomm = 1; - - /* Set up extended community. */ - build_evpn_route_extcomm(vpn, &attr, add_l3_ecomm); - - seq = mac_mobility_seqnum(tmp_pi->attr); - - /* Update the route entry. */ - update_evpn_route_entry(bgp, vpn, afi, safi, rn, &attr, 0, &pi, - 0, seq); - - /* Perform route selection; this is just to set the flags - * correctly as local route in the VNI always wins. - */ - evpn_route_select_install(bgp, vpn, rn); - - attr_new = pi->attr; - - /* Update route in global routing table. */ - rd_rn = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi, - (struct prefix *)evp, &vpn->prd); - assert(rd_rn); - update_evpn_route_entry(bgp, vpn, afi, safi, rd_rn, attr_new, 0, - &global_pi, 0, - mac_mobility_seqnum(attr_new)); - - /* Schedule for processing and unlock node. */ - bgp_process(bgp, rd_rn, afi, safi); - bgp_unlock_node(rd_rn); - - /* Unintern temporary. */ - aspath_unintern(&attr.aspath); - + bgp_evpn_update_type2_route_entry(bgp, vpn, dest, tmp_pi, + __func__); } return 0; @@ -2128,31 +2122,35 @@ static int delete_global_type2_routes(struct bgp *bgp, struct bgpevpn *vpn) { afi_t afi; safi_t safi; - struct bgp_node *rdrn, *rn; + struct bgp_dest *rddest, *dest; struct bgp_table *table; struct bgp_path_info *pi; afi = AFI_L2VPN; safi = SAFI_EVPN; - rdrn = bgp_node_lookup(bgp->rib[afi][safi], (struct prefix *)&vpn->prd); - if (rdrn && bgp_node_has_bgp_path_info_data(rdrn)) { - table = bgp_node_get_bgp_table_info(rdrn); - for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) { - struct prefix_evpn *evp = (struct prefix_evpn *)&rn->p; + rddest = bgp_node_lookup(bgp->rib[afi][safi], + (struct prefix *)&vpn->prd); + if (rddest && bgp_dest_has_bgp_path_info_data(rddest)) { + table = bgp_dest_get_bgp_table_info(rddest); + for (dest = bgp_table_top(table); dest; + dest = bgp_route_next(dest)) { + const struct prefix_evpn *evp = + (const struct prefix_evpn *)bgp_dest_get_prefix( + dest); if (evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE) continue; - delete_evpn_route_entry(bgp, afi, safi, rn, &pi); + delete_evpn_route_entry(bgp, afi, safi, dest, &pi); if (pi) - bgp_process(bgp, rn, afi, safi); + bgp_process(bgp, dest, afi, safi); } } /* Unlock RD node. */ - if (rdrn) - bgp_unlock_node(rdrn); + if (rddest) + bgp_dest_unlock_node(rddest); return 0; } @@ -2165,7 +2163,7 @@ static int delete_all_type2_routes(struct bgp *bgp, struct bgpevpn *vpn) { afi_t afi; safi_t safi; - struct bgp_node *rn; + struct bgp_dest *dest; struct bgp_path_info *pi; afi = AFI_L2VPN; @@ -2178,39 +2176,19 @@ static int delete_all_type2_routes(struct bgp *bgp, struct bgpevpn *vpn) delete_global_type2_routes(bgp, vpn); /* Next, walk this VNI's route table and delete local type-2 routes. */ - for (rn = bgp_table_top(vpn->route_table); rn; - rn = bgp_route_next(rn)) { - struct prefix_evpn *evp = (struct prefix_evpn *)&rn->p; + for (dest = bgp_table_top(vpn->route_table); dest; + dest = bgp_route_next(dest)) { + const struct prefix_evpn *evp = + (const struct prefix_evpn *)bgp_dest_get_prefix(dest); if (evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE) continue; - delete_evpn_route_entry(bgp, afi, safi, rn, &pi); + delete_evpn_route_entry(bgp, afi, safi, dest, &pi); /* Route entry in local table gets deleted immediately. */ if (pi) - bgp_path_info_reap(rn, pi); - } - - return 0; -} - -/* - * Delete all routes in per ES route-table - */ -static int delete_all_es_routes(struct bgp *bgp, struct evpnes *es) -{ - struct bgp_node *rn; - struct bgp_path_info *pi, *nextpi; - - /* Walk this ES's route table and delete all routes. */ - for (rn = bgp_table_top(es->route_table); rn; - rn = bgp_route_next(rn)) { - for (pi = bgp_node_get_bgp_path_info(rn); - (pi != NULL) && (nextpi = pi->next, 1); pi = nextpi) { - bgp_path_info_delete(rn, pi); - bgp_path_info_reap(rn, pi); - } + bgp_path_info_reap(dest, pi); } return 0; @@ -2221,16 +2199,16 @@ static int delete_all_es_routes(struct bgp *bgp, struct evpnes *es) */ static int delete_all_vni_routes(struct bgp *bgp, struct bgpevpn *vpn) { - struct bgp_node *rn; + struct bgp_dest *dest; struct bgp_path_info *pi, *nextpi; /* Walk this VNI's route table and delete all routes. */ - for (rn = bgp_table_top(vpn->route_table); rn; - rn = bgp_route_next(rn)) { - for (pi = bgp_node_get_bgp_path_info(rn); + for (dest = bgp_table_top(vpn->route_table); dest; + dest = bgp_route_next(dest)) { + for (pi = bgp_dest_get_bgp_path_info(dest); (pi != NULL) && (nextpi = pi->next, 1); pi = nextpi) { - bgp_path_info_delete(rn, pi); - bgp_path_info_reap(rn, pi); + bgp_path_info_delete(dest, pi); + bgp_path_info_reap(dest, pi); } } @@ -2261,7 +2239,7 @@ static int bgp_evpn_vni_flood_mode_get(struct bgp *bgp, * situations need the route in the per-VNI table as well as the global * table to be updated (as attributes change). */ -static int update_routes_for_vni(struct bgp *bgp, struct bgpevpn *vpn) +int update_routes_for_vni(struct bgp *bgp, struct bgpevpn *vpn) { int ret; struct prefix_evpn p; @@ -2274,7 +2252,7 @@ static int update_routes_for_vni(struct bgp *bgp, struct bgpevpn *vpn) if (bgp_evpn_vni_flood_mode_get(bgp, vpn) == VXLAN_FLOOD_HEAD_END_REPL) { build_evpn_type3_prefix(&p, vpn->originator_ip); - ret = update_evpn_route(bgp, vpn, &p, 0, 0); + ret = update_evpn_route(bgp, vpn, &p, 0, 0, NULL); if (ret) return ret; } @@ -2282,29 +2260,6 @@ static int update_routes_for_vni(struct bgp *bgp, struct bgpevpn *vpn) return update_all_type2_routes(bgp, vpn); } -/* Delete (and withdraw) local routes for specified ES from global and ES table. - * Also remove all other routes from the per ES table. - * Invoked when ES is deleted. - */ -static int delete_routes_for_es(struct bgp *bgp, struct evpnes *es) -{ - int ret; - char buf[ESI_STR_LEN]; - struct prefix_evpn p; - - /* Delete and withdraw locally learnt ES route */ - build_evpn_type4_prefix(&p, &es->esi, es->originator_ip.ipaddr_v4); - ret = delete_evpn_type4_route(bgp, es, &p); - if (ret) { - flog_err(EC_BGP_EVPN_ROUTE_DELETE, - "%u failed to delete type-4 route for ESI %s", - bgp->vrf_id, esi_to_str(&es->esi, buf, sizeof(buf))); - } - - /* Delete all routes from per ES table */ - return delete_all_es_routes(bgp, es); -} - /* * Delete (and withdraw) local routes for specified VNI from the global * table and per-VNI table. After this, remove all other routes from @@ -2387,75 +2342,41 @@ static int handle_tunnel_ip_change(struct bgp *bgp, struct bgpevpn *vpn, return 0; } -/* Install EVPN route entry in ES */ -static int install_evpn_route_entry_in_es(struct bgp *bgp, struct evpnes *es, - struct prefix_evpn *p, - struct bgp_path_info *parent_pi) +static struct bgp_path_info * +bgp_create_evpn_bgp_path_info(struct bgp_path_info *parent_pi, + struct bgp_dest *dest, struct attr *attr) { - int ret = 0; - struct bgp_node *rn = NULL; - struct bgp_path_info *pi = NULL; - struct attr *attr_new = NULL; - - /* Create (or fetch) route within the VNI. - * NOTE: There is no RD here. - */ - rn = bgp_node_get(es->route_table, (struct prefix *)p); - - /* Check if route entry is already present. */ - for (pi = bgp_node_get_bgp_path_info(rn); pi; pi = pi->next) - if (pi->extra - && (struct bgp_path_info *)pi->extra->parent == parent_pi) - break; - - if (!pi) { - /* Add (or update) attribute to hash. */ - attr_new = bgp_attr_intern(parent_pi->attr); - - /* Create new route with its attribute. */ - pi = info_make(parent_pi->type, BGP_ROUTE_IMPORTED, 0, - parent_pi->peer, attr_new, rn); - SET_FLAG(pi->flags, BGP_PATH_VALID); - bgp_path_info_extra_get(pi); - pi->extra->parent = parent_pi; - bgp_path_info_add(rn, pi); - } else { - if (attrhash_cmp(pi->attr, parent_pi->attr) - && !CHECK_FLAG(pi->flags, BGP_PATH_REMOVED)) { - bgp_unlock_node(rn); - return 0; - } - /* The attribute has changed. */ - /* Add (or update) attribute to hash. */ - attr_new = bgp_attr_intern(parent_pi->attr); - - /* Restore route, if needed. */ - if (CHECK_FLAG(pi->flags, BGP_PATH_REMOVED)) - bgp_path_info_restore(rn, pi); + struct attr *attr_new; + struct bgp_path_info *pi; - /* Mark if nexthop has changed. */ - if (!IPV4_ADDR_SAME(&pi->attr->nexthop, &attr_new->nexthop)) - SET_FLAG(pi->flags, BGP_PATH_IGP_CHANGED); + /* Add (or update) attribute to hash. */ + attr_new = bgp_attr_intern(attr); - /* Unintern existing, set to new. */ - bgp_attr_unintern(&pi->attr); - pi->attr = attr_new; - pi->uptime = bgp_clock(); + /* Create new route with its attribute. */ + pi = info_make(parent_pi->type, BGP_ROUTE_IMPORTED, 0, parent_pi->peer, + attr_new, dest); + SET_FLAG(pi->flags, BGP_PATH_VALID); + bgp_path_info_extra_get(pi); + pi->extra->parent = bgp_path_info_lock(parent_pi); + bgp_dest_lock_node((struct bgp_dest *)parent_pi->net); + if (parent_pi->extra) { + memcpy(&pi->extra->label, &parent_pi->extra->label, + sizeof(pi->extra->label)); + pi->extra->num_labels = parent_pi->extra->num_labels; } + bgp_path_info_add(dest, pi); - /* Perform route selection and update zebra, if required. */ - ret = evpn_es_route_select_install(bgp, es, rn); - return ret; + return pi; } /* * Install route entry into the VRF routing table and invoke route selection. */ static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, - struct prefix_evpn *evp, + const struct prefix_evpn *evp, struct bgp_path_info *parent_pi) { - struct bgp_node *rn; + struct bgp_dest *dest; struct bgp_path_info *pi; struct attr attr; struct attr *attr_new; @@ -2465,17 +2386,17 @@ static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, afi_t afi = 0; safi_t safi = 0; char buf[PREFIX_STRLEN]; - char buf1[PREFIX_STRLEN]; + bool new_pi = false; memset(pp, 0, sizeof(struct prefix)); ip_prefix_from_evpn_prefix(evp, pp); if (bgp_debug_zebra(NULL)) { zlog_debug( - "installing evpn prefix %s as ip prefix %s in vrf %s", + "vrf %s: import evpn prefix %s parent %p flags 0x%x", + vrf_id_to_name(bgp_vrf->vrf_id), prefix2str(evp, buf, sizeof(buf)), - prefix2str(pp, buf1, sizeof(buf)), - vrf_id_to_name(bgp_vrf->vrf_id)); + parent_pi, parent_pi->flags); } /* Create (or fetch) route within the VRF. */ @@ -2483,11 +2404,11 @@ static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, if (is_evpn_prefix_ipaddr_v4(evp)) { afi = AFI_IP; safi = SAFI_UNICAST; - rn = bgp_node_get(bgp_vrf->rib[afi][safi], pp); + dest = bgp_node_get(bgp_vrf->rib[afi][safi], pp); } else if (is_evpn_prefix_ipaddr_v6(evp)) { afi = AFI_IP6; safi = SAFI_UNICAST; - rn = bgp_node_get(bgp_vrf->rib[afi][safi], pp); + dest = bgp_node_get(bgp_vrf->rib[afi][safi], pp); } else return 0; @@ -2497,39 +2418,25 @@ static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, * address for the rest of the code to flow through. In the case of IPv4, * make sure to set the flag for next hop attribute. */ - bgp_attr_dup(&attr, parent_pi->attr); + attr = *parent_pi->attr; if (afi == AFI_IP6) evpn_convert_nexthop_to_ipv6(&attr); else attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP); /* Check if route entry is already present. */ - for (pi = bgp_node_get_bgp_path_info(rn); pi; pi = pi->next) + for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) if (pi->extra && (struct bgp_path_info *)pi->extra->parent == parent_pi) break; if (!pi) { - /* Add (or update) attribute to hash. */ - attr_new = bgp_attr_intern(&attr); - - /* Create new route with its attribute. */ - pi = info_make(parent_pi->type, BGP_ROUTE_IMPORTED, 0, - parent_pi->peer, attr_new, rn); - SET_FLAG(pi->flags, BGP_PATH_VALID); - bgp_path_info_extra_get(pi); - pi->extra->parent = bgp_path_info_lock(parent_pi); - bgp_lock_node((struct bgp_node *)parent_pi->net); - if (parent_pi->extra) { - memcpy(&pi->extra->label, &parent_pi->extra->label, - sizeof(pi->extra->label)); - pi->extra->num_labels = parent_pi->extra->num_labels; - } - bgp_path_info_add(rn, pi); + pi = bgp_create_evpn_bgp_path_info(parent_pi, dest, &attr); + new_pi = true; } else { if (attrhash_cmp(pi->attr, &attr) && !CHECK_FLAG(pi->flags, BGP_PATH_REMOVED)) { - bgp_unlock_node(rn); + bgp_dest_unlock_node(dest); return 0; } /* The attribute has changed. */ @@ -2538,7 +2445,7 @@ static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, /* Restore route, if needed. */ if (CHECK_FLAG(pi->flags, BGP_PATH_REMOVED)) - bgp_path_info_restore(rn, pi); + bgp_path_info_restore(dest, pi); /* Mark if nexthop has changed. */ if ((afi == AFI_IP @@ -2548,21 +2455,33 @@ static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, &attr_new->mp_nexthop_global))) SET_FLAG(pi->flags, BGP_PATH_IGP_CHANGED); - bgp_path_info_set_flag(rn, pi, BGP_PATH_ATTR_CHANGED); + bgp_path_info_set_flag(dest, pi, BGP_PATH_ATTR_CHANGED); /* Unintern existing, set to new. */ bgp_attr_unintern(&pi->attr); pi->attr = attr_new; pi->uptime = bgp_clock(); } + /* as it is an importation, change nexthop */ + bgp_path_info_set_flag(dest, pi, BGP_PATH_ANNC_NH_SELF); - bgp_aggregate_increment(bgp_vrf, &rn->p, pi, afi, safi); + bgp_aggregate_increment(bgp_vrf, bgp_dest_get_prefix(dest), pi, afi, + safi); /* Perform route selection and update zebra, if required. */ - bgp_process(bgp_vrf, rn, afi, safi); + bgp_process(bgp_vrf, dest, afi, safi); /* Process for route leaking. */ vpn_leak_from_vrf_update(bgp_get_default(), bgp_vrf, pi); + bgp_dest_unlock_node(dest); + + if (bgp_debug_zebra(NULL)) + zlog_debug( + "... %s pi dest %p (l %d) pi %p (l %d, f 0x%x)", + new_pi ? "new" : "update", + dest, bgp_dest_to_rnode(dest)->lock, + pi, pi->lock, pi->flags); + return ret; } @@ -2570,45 +2489,41 @@ static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, * Install route entry into the VNI routing table and invoke route selection. */ static int install_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, - struct prefix_evpn *p, + const struct prefix_evpn *p, struct bgp_path_info *parent_pi) { - struct bgp_node *rn; + struct bgp_dest *dest; struct bgp_path_info *pi; + struct bgp_path_info *local_pi; struct attr *attr_new; int ret; + struct prefix_evpn ad_evp; + + /* EAD prefix in the global table doesn't include the VTEP-IP so + * we need to create a different copy for the VNI + */ + if (p->prefix.route_type == BGP_EVPN_AD_ROUTE) + p = evpn_type1_prefix_vni_copy(&ad_evp, p, + parent_pi->attr->nexthop); /* Create (or fetch) route within the VNI. */ /* NOTE: There is no RD here. */ - rn = bgp_node_get(vpn->route_table, (struct prefix *)p); + dest = bgp_node_get(vpn->route_table, (struct prefix *)p); /* Check if route entry is already present. */ - for (pi = bgp_node_get_bgp_path_info(rn); pi; pi = pi->next) + for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) if (pi->extra && (struct bgp_path_info *)pi->extra->parent == parent_pi) break; if (!pi) { - /* Add (or update) attribute to hash. */ - attr_new = bgp_attr_intern(parent_pi->attr); - - /* Create new route with its attribute. */ - pi = info_make(parent_pi->type, BGP_ROUTE_IMPORTED, 0, - parent_pi->peer, attr_new, rn); - SET_FLAG(pi->flags, BGP_PATH_VALID); - bgp_path_info_extra_get(pi); - pi->extra->parent = bgp_path_info_lock(parent_pi); - bgp_lock_node((struct bgp_node *)parent_pi->net); - if (parent_pi->extra) { - memcpy(&pi->extra->label, &parent_pi->extra->label, - sizeof(pi->extra->label)); - pi->extra->num_labels = parent_pi->extra->num_labels; - } - bgp_path_info_add(rn, pi); + /* Create an info */ + (void)bgp_create_evpn_bgp_path_info(parent_pi, dest, + parent_pi->attr); } else { if (attrhash_cmp(pi->attr, parent_pi->attr) && !CHECK_FLAG(pi->flags, BGP_PATH_REMOVED)) { - bgp_unlock_node(rn); + bgp_dest_unlock_node(dest); return 0; } /* The attribute has changed. */ @@ -2617,7 +2532,7 @@ static int install_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, /* Restore route, if needed. */ if (CHECK_FLAG(pi->flags, BGP_PATH_REMOVED)) - bgp_path_info_restore(rn, pi); + bgp_path_info_restore(dest, pi); /* Mark if nexthop has changed. */ if (!IPV4_ADDR_SAME(&pi->attr->nexthop, &attr_new->nexthop)) @@ -2630,47 +2545,19 @@ static int install_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, } /* Perform route selection and update zebra, if required. */ - ret = evpn_route_select_install(bgp, vpn, rn); - - return ret; -} + ret = evpn_route_select_install(bgp, vpn, dest); -/* Uninstall EVPN route entry from ES route table */ -static int uninstall_evpn_route_entry_in_es(struct bgp *bgp, struct evpnes *es, - struct prefix_evpn *p, - struct bgp_path_info *parent_pi) -{ - int ret; - struct bgp_node *rn; - struct bgp_path_info *pi; - - if (!es->route_table) - return 0; - - /* Locate route within the ESI. - * NOTE: There is no RD here. + /* if the best path is a local path with a non-zero ES + * sync info against the local path may need to be updated + * when a remote path is added/updated (including changes + * from sync-path to remote-path) */ - rn = bgp_node_lookup(es->route_table, (struct prefix *)p); - if (!rn) - return 0; - - /* Find matching route entry. */ - for (pi = bgp_node_get_bgp_path_info(rn); pi; pi = pi->next) - if (pi->extra - && (struct bgp_path_info *)pi->extra->parent == parent_pi) - break; - - if (!pi) - return 0; - - /* Mark entry for deletion */ - bgp_path_info_delete(rn, pi); - - /* Perform route selection and update zebra, if required. */ - ret = evpn_es_route_select_install(bgp, es, rn); + local_pi = bgp_evpn_route_get_local_path(bgp, dest); + if (local_pi && bgp_evpn_attr_is_local_es(local_pi->attr)) + bgp_evpn_update_type2_route_entry(bgp, vpn, dest, local_pi, + __func__); - /* Unlock route node. */ - bgp_unlock_node(rn); + bgp_dest_unlock_node(dest); return ret; } @@ -2680,10 +2567,10 @@ static int uninstall_evpn_route_entry_in_es(struct bgp *bgp, struct evpnes *es, * to zebra, if appropriate. */ static int uninstall_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, - struct prefix_evpn *evp, + const struct prefix_evpn *evp, struct bgp_path_info *parent_pi) { - struct bgp_node *rn; + struct bgp_dest *dest; struct bgp_path_info *pi; int ret = 0; struct prefix p; @@ -2691,17 +2578,16 @@ static int uninstall_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, afi_t afi = 0; safi_t safi = 0; char buf[PREFIX_STRLEN]; - char buf1[PREFIX_STRLEN]; memset(pp, 0, sizeof(struct prefix)); ip_prefix_from_evpn_prefix(evp, pp); if (bgp_debug_zebra(NULL)) { zlog_debug( - "uninstalling evpn prefix %s as ip prefix %s in vrf %s", + "vrf %s: unimport evpn prefix %s parent %p flags 0x%x", + vrf_id_to_name(bgp_vrf->vrf_id), prefix2str(evp, buf, sizeof(buf)), - prefix2str(pp, buf1, sizeof(buf)), - vrf_id_to_name(bgp_vrf->vrf_id)); + parent_pi, parent_pi->flags); } /* Locate route within the VRF. */ @@ -2709,18 +2595,18 @@ static int uninstall_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, if (is_evpn_prefix_ipaddr_v4(evp)) { afi = AFI_IP; safi = SAFI_UNICAST; - rn = bgp_node_lookup(bgp_vrf->rib[afi][safi], pp); + dest = bgp_node_lookup(bgp_vrf->rib[afi][safi], pp); } else { afi = AFI_IP6; safi = SAFI_UNICAST; - rn = bgp_node_lookup(bgp_vrf->rib[afi][safi], pp); + dest = bgp_node_lookup(bgp_vrf->rib[afi][safi], pp); } - if (!rn) + if (!dest) return 0; /* Find matching route entry. */ - for (pi = bgp_node_get_bgp_path_info(rn); pi; pi = pi->next) + for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) if (pi->extra && (struct bgp_path_info *)pi->extra->parent == parent_pi) break; @@ -2728,19 +2614,26 @@ static int uninstall_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, if (!pi) return 0; + if (bgp_debug_zebra(NULL)) + zlog_debug( + "... delete dest %p (l %d) pi %p (l %d, f 0x%x)", + dest, bgp_dest_to_rnode(dest)->lock, + pi, pi->lock, pi->flags); + /* Process for route leaking. */ vpn_leak_from_vrf_withdraw(bgp_get_default(), bgp_vrf, pi); - bgp_aggregate_decrement(bgp_vrf, &rn->p, pi, afi, safi); + bgp_aggregate_decrement(bgp_vrf, bgp_dest_get_prefix(dest), pi, afi, + safi); /* Mark entry for deletion */ - bgp_path_info_delete(rn, pi); + bgp_path_info_delete(dest, pi); /* Perform route selection and update zebra, if required. */ - bgp_process(bgp_vrf, rn, afi, safi); + bgp_process(bgp_vrf, dest, afi, safi); /* Unlock route node. */ - bgp_unlock_node(rn); + bgp_dest_unlock_node(dest); return ret; } @@ -2750,21 +2643,30 @@ static int uninstall_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, * to zebra, if appropriate. */ static int uninstall_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, - struct prefix_evpn *p, + const struct prefix_evpn *p, struct bgp_path_info *parent_pi) { - struct bgp_node *rn; + struct bgp_dest *dest; struct bgp_path_info *pi; + struct bgp_path_info *local_pi; int ret; + struct prefix_evpn ad_evp; + + /* EAD prefix in the global table doesn't include the VTEP-IP so + * we need to create a different copy for the VNI + */ + if (p->prefix.route_type == BGP_EVPN_AD_ROUTE) + p = evpn_type1_prefix_vni_copy(&ad_evp, p, + parent_pi->attr->nexthop); /* Locate route within the VNI. */ /* NOTE: There is no RD here. */ - rn = bgp_node_lookup(vpn->route_table, (struct prefix *)p); - if (!rn) + dest = bgp_node_lookup(vpn->route_table, (struct prefix *)p); + if (!dest) return 0; /* Find matching route entry. */ - for (pi = bgp_node_get_bgp_path_info(rn); pi; pi = pi->next) + for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) if (pi->extra && (struct bgp_path_info *)pi->extra->parent == parent_pi) break; @@ -2773,33 +2675,26 @@ static int uninstall_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, return 0; /* Mark entry for deletion */ - bgp_path_info_delete(rn, pi); + bgp_path_info_delete(dest, pi); /* Perform route selection and update zebra, if required. */ - ret = evpn_route_select_install(bgp, vpn, rn); + ret = evpn_route_select_install(bgp, vpn, dest); + + /* if the best path is a local path with a non-zero ES + * sync info against the local path may need to be updated + * when a remote path is deleted + */ + local_pi = bgp_evpn_route_get_local_path(bgp, dest); + if (local_pi && bgp_evpn_attr_is_local_es(local_pi->attr)) + bgp_evpn_update_type2_route_entry(bgp, vpn, dest, local_pi, + __func__); /* Unlock route node. */ - bgp_unlock_node(rn); + bgp_dest_unlock_node(dest); return ret; } -/* - * Given a prefix, see if it belongs to ES. - */ -static int is_prefix_matching_for_es(struct prefix_evpn *p, - struct evpnes *es) -{ - /* if not an ES route return false */ - if (p->prefix.route_type != BGP_EVPN_ES_ROUTE) - return 0; - - if (memcmp(&p->prefix.es_addr.esi, &es->esi, sizeof(esi_t)) == 0) - return 1; - - return 0; -} - /* * Given a route entry and a VRF, see if this route entry should be * imported into the VRF i.e., RTs match. @@ -2831,9 +2726,9 @@ static int is_route_matching_for_vrf(struct bgp *bgp_vrf, struct vrf_irt_node *irt; /* Only deal with RTs */ - pnt = (ecom->val + (i * ECOMMUNITY_SIZE)); + pnt = (ecom->val + (i * ecom->unit_size)); eval = (struct ecommunity_val *)(ecom->val - + (i * ECOMMUNITY_SIZE)); + + (i * ecom->unit_size)); type = *pnt++; sub_type = *pnt++; if (sub_type != ECOMMUNITY_ROUTE_TARGET) @@ -2855,7 +2750,7 @@ static int is_route_matching_for_vrf(struct bgp *bgp_vrf, if (type == ECOMMUNITY_ENCODE_AS || type == ECOMMUNITY_ENCODE_AS4 || type == ECOMMUNITY_ENCODE_IP) { - memcpy(&eval_tmp, eval, ECOMMUNITY_SIZE); + memcpy(&eval_tmp, eval, ecom->unit_size); mask_ecom_global_admin(&eval_tmp, eval); irt = lookup_vrf_import_rt(&eval_tmp); } @@ -2898,9 +2793,9 @@ static int is_route_matching_for_vni(struct bgp *bgp, struct bgpevpn *vpn, struct irt_node *irt; /* Only deal with RTs */ - pnt = (ecom->val + (i * ECOMMUNITY_SIZE)); + pnt = (ecom->val + (i * ecom->unit_size)); eval = (struct ecommunity_val *)(ecom->val - + (i * ECOMMUNITY_SIZE)); + + (i * ecom->unit_size)); type = *pnt++; sub_type = *pnt++; if (sub_type != ECOMMUNITY_ROUTE_TARGET) @@ -2922,7 +2817,7 @@ static int is_route_matching_for_vni(struct bgp *bgp, struct bgpevpn *vpn, if (type == ECOMMUNITY_ENCODE_AS || type == ECOMMUNITY_ENCODE_AS4 || type == ECOMMUNITY_ENCODE_IP) { - memcpy(&eval_tmp, eval, ECOMMUNITY_SIZE); + memcpy(&eval_tmp, eval, ecom->unit_size); mask_ecom_global_admin(&eval_tmp, eval); irt = lookup_import_rt(bgp, &eval_tmp); } @@ -2934,81 +2829,12 @@ static int is_route_matching_for_vni(struct bgp *bgp, struct bgpevpn *vpn, return 0; } -static int install_uninstall_routes_for_es(struct bgp *bgp, - struct evpnes *es, - int install) -{ - int ret; - afi_t afi; - safi_t safi; - char buf[PREFIX_STRLEN]; - char buf1[ESI_STR_LEN]; - struct bgp_node *rd_rn, *rn; - struct bgp_table *table; - struct bgp_path_info *pi; - - afi = AFI_L2VPN; - safi = SAFI_EVPN; - - /* - * Walk entire global routing table and evaluate routes which could be - * imported into this VRF. Note that we need to loop through all global - * routes to determine which route matches the import rt on vrf - */ - for (rd_rn = bgp_table_top(bgp->rib[afi][safi]); rd_rn; - rd_rn = bgp_route_next(rd_rn)) { - table = bgp_node_get_bgp_table_info(rd_rn); - if (!table) - continue; - - for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) { - struct prefix_evpn *evp = (struct prefix_evpn *)&rn->p; - - for (pi = bgp_node_get_bgp_path_info(rn); pi; - pi = pi->next) { - /* - * Consider "valid" remote routes applicable for - * this ES. - */ - if (!(CHECK_FLAG(pi->flags, BGP_PATH_VALID) - && pi->type == ZEBRA_ROUTE_BGP - && pi->sub_type == BGP_ROUTE_NORMAL)) - continue; - - if (!is_prefix_matching_for_es(evp, es)) - continue; - - if (install) - ret = install_evpn_route_entry_in_es( - bgp, es, evp, pi); - else - ret = uninstall_evpn_route_entry_in_es( - bgp, es, evp, pi); - - if (ret) { - flog_err( - EC_BGP_EVPN_FAIL, - "Failed to %s EVPN %s route in ESI %s", - install ? "install" - : "uninstall", - prefix2str(evp, buf, - sizeof(buf)), - esi_to_str(&es->esi, buf1, - sizeof(buf1))); - return ret; - } - } - } - } - return 0; -} - /* This API will scan evpn routes for checking attribute's rmac * macthes with bgp instance router mac. It avoid installing * route into bgp vrf table and remote rmac in bridge table. */ static int bgp_evpn_route_rmac_self_check(struct bgp *bgp_vrf, - struct prefix_evpn *evp, + const struct prefix_evpn *evp, struct bgp_path_info *pi) { /* evpn route could have learnt prior to L3vni has come up, @@ -3018,13 +2844,12 @@ static int bgp_evpn_route_rmac_self_check(struct bgp *bgp_vrf, * SVI comes up with MAC and stored in hash, triggers * bgp_mac_rescan_all_evpn_tables. */ - if (pi->attr && - memcmp(&bgp_vrf->rmac, &pi->attr->rmac, ETH_ALEN) == 0) { + if (memcmp(&bgp_vrf->rmac, &pi->attr->rmac, ETH_ALEN) == 0) { if (bgp_debug_update(pi->peer, NULL, NULL, 1)) { char buf1[PREFIX_STRLEN]; char attr_str[BUFSIZ] = {0}; - bgp_dump_attr(pi->attr, attr_str, BUFSIZ); + bgp_dump_attr(pi->attr, attr_str, sizeof(attr_str)); zlog_debug("%s: bgp %u prefix %s with attr %s - DENIED due to self mac", __func__, bgp_vrf->vrf_id, @@ -3046,7 +2871,7 @@ static int install_uninstall_routes_for_vrf(struct bgp *bgp_vrf, int install) { afi_t afi; safi_t safi; - struct bgp_node *rd_rn, *rn; + struct bgp_dest *rd_dest, *dest; struct bgp_table *table; struct bgp_path_info *pi; int ret; @@ -3063,14 +2888,17 @@ static int install_uninstall_routes_for_vrf(struct bgp *bgp_vrf, int install) * imported into this VRF. Note that we need to loop through all global * routes to determine which route matches the import rt on vrf */ - for (rd_rn = bgp_table_top(bgp_evpn->rib[afi][safi]); rd_rn; - rd_rn = bgp_route_next(rd_rn)) { - table = bgp_node_get_bgp_table_info(rd_rn); + for (rd_dest = bgp_table_top(bgp_evpn->rib[afi][safi]); rd_dest; + rd_dest = bgp_route_next(rd_dest)) { + table = bgp_dest_get_bgp_table_info(rd_dest); if (!table) continue; - for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) { - struct prefix_evpn *evp = (struct prefix_evpn *)&rn->p; + for (dest = bgp_table_top(table); dest; + dest = bgp_route_next(dest)) { + const struct prefix_evpn *evp = + (const struct prefix_evpn *)bgp_dest_get_prefix( + dest); /* if not mac-ip route skip this route */ if (!(evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE @@ -3083,7 +2911,7 @@ static int install_uninstall_routes_for_vrf(struct bgp *bgp_vrf, int install) || is_evpn_prefix_ipaddr_v6(evp))) continue; - for (pi = bgp_node_get_bgp_path_info(rn); pi; + for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) { /* Consider "valid" remote routes applicable for * this VRF. @@ -3115,6 +2943,8 @@ static int install_uninstall_routes_for_vrf(struct bgp *bgp_vrf, int install) sizeof(buf)), vrf_id_to_name( bgp_vrf->vrf_id)); + bgp_dest_unlock_node(rd_dest); + bgp_dest_unlock_node(dest); return ret; } } @@ -3136,7 +2966,7 @@ static int install_uninstall_routes_for_vni(struct bgp *bgp, { afi_t afi; safi_t safi; - struct bgp_node *rd_rn, *rn; + struct bgp_dest *rd_dest, *dest; struct bgp_table *table; struct bgp_path_info *pi; int ret; @@ -3151,19 +2981,22 @@ static int install_uninstall_routes_for_vni(struct bgp *bgp, * RD. */ /* EVPN routes are a 2-level table. */ - for (rd_rn = bgp_table_top(bgp->rib[afi][safi]); rd_rn; - rd_rn = bgp_route_next(rd_rn)) { - table = bgp_node_get_bgp_table_info(rd_rn); + for (rd_dest = bgp_table_top(bgp->rib[afi][safi]); rd_dest; + rd_dest = bgp_route_next(rd_dest)) { + table = bgp_dest_get_bgp_table_info(rd_dest); if (!table) continue; - for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) { - struct prefix_evpn *evp = (struct prefix_evpn *)&rn->p; + for (dest = bgp_table_top(table); dest; + dest = bgp_route_next(dest)) { + const struct prefix_evpn *evp = + (const struct prefix_evpn *)bgp_dest_get_prefix( + dest); if (evp->prefix.route_type != rtype) continue; - for (pi = bgp_node_get_bgp_path_info(rn); pi; + for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) { /* Consider "valid" remote routes applicable for * this VNI. */ @@ -3191,6 +3024,9 @@ static int install_uninstall_routes_for_vni(struct bgp *bgp, ? "MACIP" : "IMET", vpn->vni); + + bgp_dest_unlock_node(rd_dest); + bgp_dest_unlock_node(dest); return ret; } } @@ -3201,15 +3037,6 @@ static int install_uninstall_routes_for_vni(struct bgp *bgp, return 0; } -/* Install any existing remote ES routes applicable for this ES into its routing - * table. This is invoked when ES comes up. - */ -static int install_routes_for_es(struct bgp *bgp, struct evpnes *es) -{ - return install_uninstall_routes_for_es(bgp, es, 1); -} - - /* Install any existing remote routes applicable for this VRF into VRF RIB. This * is invoked upon l3vni-add or l3vni import rt change */ @@ -3236,6 +3063,11 @@ static int install_routes_for_vni(struct bgp *bgp, struct bgpevpn *vpn) if (ret) return ret; + ret = install_uninstall_routes_for_vni(bgp, vpn, BGP_EVPN_AD_ROUTE, + 1); + if (ret) + return ret; + return install_uninstall_routes_for_vni(bgp, vpn, BGP_EVPN_MAC_IP_ROUTE, 1); } @@ -3264,33 +3096,14 @@ static int uninstall_routes_for_vni(struct bgp *bgp, struct bgpevpn *vpn) if (ret) return ret; - return install_uninstall_routes_for_vni(bgp, vpn, BGP_EVPN_IMET_ROUTE, - 0); -} - -/* Install or unistall route in ES */ -static int install_uninstall_route_in_es(struct bgp *bgp, struct evpnes *es, - afi_t afi, safi_t safi, - struct prefix_evpn *evp, - struct bgp_path_info *pi, int install) -{ - int ret = 0; - char buf[ESI_STR_LEN]; + ret = install_uninstall_routes_for_vni(bgp, vpn, BGP_EVPN_AD_ROUTE, + 1); + if (ret) + return ret; - if (install) - ret = install_evpn_route_entry_in_es(bgp, es, evp, pi); - else - ret = uninstall_evpn_route_entry_in_es(bgp, es, evp, pi); - if (ret) { - flog_err( - EC_BGP_EVPN_FAIL, - "%u: Failed to %s EVPN %s route in ESI %s", bgp->vrf_id, - install ? "install" : "uninstall", "ES", - esi_to_str(&evp->prefix.es_addr.esi, buf, sizeof(buf))); - return ret; - } - return 0; + return install_uninstall_routes_for_vni(bgp, vpn, BGP_EVPN_IMET_ROUTE, + 0); } /* @@ -3380,13 +3193,14 @@ static int install_uninstall_route_in_vnis(struct bgp *bgp, afi_t afi, * Install or uninstall route for appropriate VNIs/ESIs. */ static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi, - struct prefix *p, + const struct prefix *p, struct bgp_path_info *pi, int import) { struct prefix_evpn *evp = (struct prefix_evpn *)p; struct attr *attr = pi->attr; struct ecommunity *ecom; int i; + struct prefix_evpn ad_evp; assert(attr); @@ -3394,6 +3208,7 @@ static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi, if (!(evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE || evp->prefix.route_type == BGP_EVPN_IMET_ROUTE || evp->prefix.route_type == BGP_EVPN_ES_ROUTE + || evp->prefix.route_type == BGP_EVPN_AD_ROUTE || evp->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE)) return 0; @@ -3401,6 +3216,12 @@ static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi, if (!(attr->flag & ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES))) return 0; + /* EAD prefix in the global table doesn't include the VTEP-IP so + * we need to create a different copy for the VNI + */ + if (evp->prefix.route_type == BGP_EVPN_AD_ROUTE) + evp = evpn_type1_prefix_vni_copy(&ad_evp, evp, attr->nexthop); + ecom = attr->ecommunity; if (!ecom || !ecom->size) return -1; @@ -3414,12 +3235,12 @@ static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi, struct ecommunity_val eval_tmp; struct irt_node *irt; /* import rt for l2vni */ struct vrf_irt_node *vrf_irt; /* import rt for l3vni */ - struct evpnes *es; + struct bgp_evpn_es *es; /* Only deal with RTs */ - pnt = (ecom->val + (i * ECOMMUNITY_SIZE)); + pnt = (ecom->val + (i * ecom->unit_size)); eval = (struct ecommunity_val *)(ecom->val - + (i * ECOMMUNITY_SIZE)); + + (i * ecom->unit_size)); type = *pnt++; sub_type = *pnt++; if (sub_type != ECOMMUNITY_ROUTE_TARGET) @@ -3432,6 +3253,7 @@ static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi, */ if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE || evp->prefix.route_type == BGP_EVPN_IMET_ROUTE || + evp->prefix.route_type == BGP_EVPN_AD_ROUTE || evp->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE) { irt = lookup_import_rt(bgp, eval); @@ -3457,7 +3279,7 @@ static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi, if (type == ECOMMUNITY_ENCODE_AS || type == ECOMMUNITY_ENCODE_AS4 || type == ECOMMUNITY_ENCODE_IP) { - memcpy(&eval_tmp, eval, ECOMMUNITY_SIZE); + memcpy(&eval_tmp, eval, ecom->unit_size); mask_ecom_global_admin(&eval_tmp, eval); irt = lookup_import_rt(bgp, &eval_tmp); vrf_irt = lookup_vrf_import_rt(&eval_tmp); @@ -3479,9 +3301,9 @@ static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi, /* we will match based on the entire esi to avoid * imoort of an es route for esi2 into esi1 */ - es = bgp_evpn_lookup_es(bgp, &evp->prefix.es_addr.esi); - if (es && is_es_local(es)) - install_uninstall_route_in_es( + es = bgp_evpn_es_find(&evp->prefix.es_addr.esi); + if (es && bgp_evpn_is_es_local(es)) + bgp_evpn_es_route_install_uninstall( bgp, es, afi, safi, evp, pi, import); } } @@ -3518,8 +3340,14 @@ static void delete_withdraw_vrf_routes(struct bgp *bgp_vrf) * update and advertise all ipv4 and ipv6 routes in thr vrf table as type-5 * routes */ -static void update_advertise_vrf_routes(struct bgp *bgp_vrf) +void update_advertise_vrf_routes(struct bgp *bgp_vrf) { + struct bgp *bgp_evpn = NULL; /* EVPN bgp instance */ + + bgp_evpn = bgp_get_evpn(); + if (!bgp_evpn) + return; + /* update all ipv4 routes */ if (advertise_type5_routes(bgp_vrf, AFI_IP)) bgp_evpn_advertise_type5_routes(bgp_vrf, AFI_IP, SAFI_UNICAST); @@ -3582,7 +3410,7 @@ static void withdraw_router_id_vrf(struct bgp *bgp_vrf) static int update_advertise_vni_routes(struct bgp *bgp, struct bgpevpn *vpn) { struct prefix_evpn p; - struct bgp_node *rn, *global_rn; + struct bgp_dest *dest, *global_dest; struct bgp_path_info *pi, *global_pi; struct attr *attr; afi_t afi = AFI_L2VPN; @@ -3597,10 +3425,10 @@ static int update_advertise_vni_routes(struct bgp *bgp, struct bgpevpn *vpn) if (bgp_evpn_vni_flood_mode_get(bgp, vpn) == VXLAN_FLOOD_HEAD_END_REPL) { build_evpn_type3_prefix(&p, vpn->originator_ip); - rn = bgp_node_lookup(vpn->route_table, (struct prefix *)&p); - if (!rn) /* unexpected */ + dest = bgp_node_lookup(vpn->route_table, (struct prefix *)&p); + if (!dest) /* unexpected */ return 0; - for (pi = bgp_node_get_bgp_path_info(rn); pi; pi = pi->next) + for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) if (pi->peer == bgp->peer_self && pi->type == ZEBRA_ROUTE_BGP && pi->sub_type == BGP_ROUTE_STATIC) @@ -3609,28 +3437,30 @@ static int update_advertise_vni_routes(struct bgp *bgp, struct bgpevpn *vpn) return 0; attr = pi->attr; - global_rn = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi, - (struct prefix *)&p, &vpn->prd); - update_evpn_route_entry(bgp, vpn, afi, safi, global_rn, attr, - 1, &pi, 0, mac_mobility_seqnum(attr)); + global_dest = bgp_global_evpn_node_get(bgp->rib[afi][safi], + afi, safi, &p, &vpn->prd); + update_evpn_route_entry(bgp, vpn, afi, safi, global_dest, attr, + 1, &pi, 0, mac_mobility_seqnum(attr), + false /* setup_sync */, NULL /* old_is_sync */); /* Schedule for processing and unlock node. */ - bgp_process(bgp, global_rn, afi, safi); - bgp_unlock_node(global_rn); + bgp_process(bgp, global_dest, afi, safi); + bgp_dest_unlock_node(global_dest); } /* Now, walk this VNI's route table and use the route and its attribute * to create and schedule route in global table. */ - for (rn = bgp_table_top(vpn->route_table); rn; - rn = bgp_route_next(rn)) { - struct prefix_evpn *evp = (struct prefix_evpn *)&rn->p; + for (dest = bgp_table_top(vpn->route_table); dest; + dest = bgp_route_next(dest)) { + const struct prefix_evpn *evp = + (const struct prefix_evpn *)bgp_dest_get_prefix(dest); /* Identify MAC-IP local routes. */ if (evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE) continue; - for (pi = bgp_node_get_bgp_path_info(rn); pi; pi = pi->next) + for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) if (pi->peer == bgp->peer_self && pi->type == ZEBRA_ROUTE_BGP && pi->sub_type == BGP_ROUTE_STATIC) @@ -3642,16 +3472,17 @@ static int update_advertise_vni_routes(struct bgp *bgp, struct bgpevpn *vpn) * attribute. */ attr = pi->attr; - global_rn = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi, - (struct prefix *)evp, &vpn->prd); - assert(global_rn); - update_evpn_route_entry(bgp, vpn, afi, safi, global_rn, attr, 1, - &global_pi, 0, - mac_mobility_seqnum(attr)); + global_dest = bgp_global_evpn_node_get(bgp->rib[afi][safi], afi, safi, + evp, &vpn->prd); + assert(global_dest); + update_evpn_route_entry(bgp, vpn, afi, safi, global_dest, attr, 1, + &global_pi, 0, + mac_mobility_seqnum(attr), + false /* setup_sync */, NULL /* old_is_sync */); /* Schedule for processing and unlock node. */ - bgp_process(bgp, global_rn, afi, safi); - bgp_unlock_node(global_rn); + bgp_process(bgp, global_dest, afi, safi); + bgp_dest_unlock_node(global_dest); } return 0; @@ -3665,7 +3496,7 @@ static int delete_withdraw_vni_routes(struct bgp *bgp, struct bgpevpn *vpn) { int ret; struct prefix_evpn p; - struct bgp_node *global_rn; + struct bgp_dest *global_dest; struct bgp_path_info *pi; afi_t afi = AFI_L2VPN; safi_t safi = SAFI_EVPN; @@ -3679,18 +3510,18 @@ static int delete_withdraw_vni_routes(struct bgp *bgp, struct bgpevpn *vpn) /* Remove type-3 route for this VNI from global table. */ build_evpn_type3_prefix(&p, vpn->originator_ip); - global_rn = bgp_afi_node_lookup(bgp->rib[afi][safi], afi, safi, - (struct prefix *)&p, &vpn->prd); - if (global_rn) { + global_dest = bgp_global_evpn_node_lookup(bgp->rib[afi][safi], afi, safi, + (const struct prefix_evpn *)&p, &vpn->prd); + if (global_dest) { /* Delete route entry in the global EVPN table. */ - delete_evpn_route_entry(bgp, afi, safi, global_rn, &pi); + delete_evpn_route_entry(bgp, afi, safi, global_dest, &pi); /* Schedule for processing - withdraws to peers happen from * this table. */ if (pi) - bgp_process(bgp, global_rn, afi, safi); - bgp_unlock_node(global_rn); + bgp_process(bgp, global_dest, afi, safi); + bgp_dest_unlock_node(global_dest); } return 0; @@ -3747,7 +3578,7 @@ static void create_advertise_type3(struct hash_bucket *bucket, void *data) return; build_evpn_type3_prefix(&p, vpn->originator_ip); - if (update_evpn_route(bgp, vpn, &p, 0, 0)) + if (update_evpn_route(bgp, vpn, &p, 0, 0, NULL)) flog_err(EC_BGP_EVPN_ROUTE_CREATE, "Type3 route creation failure for VNI %u", vpn->vni); } @@ -3777,11 +3608,12 @@ static int process_type2_route(struct peer *peer, afi_t afi, safi_t safi, uint32_t addpath_id) { struct prefix_rd prd; - struct prefix_evpn p; - struct bgp_route_evpn evpn; + struct prefix_evpn p = {}; + struct bgp_route_evpn evpn = {}; uint8_t ipaddr_len; uint8_t macaddr_len; - mpls_label_t label[BGP_MAX_LABELS]; /* holds the VNI(s) as in packet */ + /* holds the VNI(s) as in packet */ + mpls_label_t label[BGP_MAX_LABELS] = {}; uint32_t num_labels = 0; uint32_t eth_tag; int ret; @@ -3800,54 +3632,61 @@ static int process_type2_route(struct peer *peer, afi_t afi, safi_t safi, return -1; } - memset(&evpn, 0, sizeof(evpn)); + struct stream *pkt = stream_new(psize); + stream_put(pkt, pfx, psize); /* Make prefix_rd */ prd.family = AF_UNSPEC; prd.prefixlen = 64; - memcpy(&prd.val, pfx, 8); - pfx += 8; + + STREAM_GET(&prd.val, pkt, 8); /* Make EVPN prefix. */ - memset(&p, 0, sizeof(struct prefix_evpn)); p.family = AF_EVPN; p.prefixlen = EVPN_ROUTE_PREFIXLEN; p.prefix.route_type = BGP_EVPN_MAC_IP_ROUTE; /* Copy Ethernet Seg Identifier */ - memcpy(&evpn.eth_s_id.val, pfx, ESI_LEN); - pfx += ESI_LEN; + if (attr) { + STREAM_GET(&attr->esi, pkt, sizeof(esi_t)); + + if (bgp_evpn_is_esi_local(&attr->esi)) + attr->es_flags |= ATTR_ES_IS_LOCAL; + else + attr->es_flags &= ~ATTR_ES_IS_LOCAL; + } else { + STREAM_FORWARD_GETP(pkt, sizeof(esi_t)); + } /* Copy Ethernet Tag */ - memcpy(ð_tag, pfx, 4); + STREAM_GET(ð_tag, pkt, 4); p.prefix.macip_addr.eth_tag = ntohl(eth_tag); - pfx += 4; /* Get the MAC Addr len */ - macaddr_len = *pfx++; + STREAM_GETC(pkt, macaddr_len); /* Get the MAC Addr */ if (macaddr_len == (ETH_ALEN * 8)) { - memcpy(&p.prefix.macip_addr.mac.octet, pfx, ETH_ALEN); - pfx += ETH_ALEN; + STREAM_GET(&p.prefix.macip_addr.mac.octet, pkt, ETH_ALEN); } else { flog_err( EC_BGP_EVPN_ROUTE_INVALID, "%u:%s - Rx EVPN Type-2 NLRI with unsupported MAC address length %d", peer->bgp->vrf_id, peer->host, macaddr_len); - return -1; + goto fail; } /* Get the IP. */ - ipaddr_len = *pfx++; + STREAM_GETC(pkt, ipaddr_len); + if (ipaddr_len != 0 && ipaddr_len != IPV4_MAX_BITLEN && ipaddr_len != IPV6_MAX_BITLEN) { flog_err( EC_BGP_EVPN_ROUTE_INVALID, "%u:%s - Rx EVPN Type-2 NLRI with unsupported IP address length %d", peer->bgp->vrf_id, peer->host, ipaddr_len); - return -1; + goto fail; } if (ipaddr_len) { @@ -3855,25 +3694,17 @@ static int process_type2_route(struct peer *peer, afi_t afi, safi_t safi, p.prefix.macip_addr.ip.ipa_type = (ipaddr_len == IPV4_MAX_BYTELEN) ? IPADDR_V4 : IPADDR_V6; - memcpy(&p.prefix.macip_addr.ip.ip.addr, pfx, ipaddr_len); + STREAM_GET(&p.prefix.macip_addr.ip.ip.addr, pkt, ipaddr_len); } - pfx += ipaddr_len; /* Get the VNI(s). Stored as bytes here. */ + STREAM_GET(&label[0], pkt, BGP_LABEL_BYTES); num_labels++; - memset(label, 0, sizeof(label)); - memcpy(&label[0], pfx, BGP_LABEL_BYTES); - pfx += BGP_LABEL_BYTES; - psize -= (33 + ipaddr_len); + /* Do we have a second VNI? */ - if (psize) { + if (STREAM_READABLE(pkt)) { num_labels++; - memcpy(&label[1], pfx, BGP_LABEL_BYTES); - /* - * If in future, we are required to access additional fields, - * we MUST increment pfx by BGP_LABEL_BYTES in before reading - * the next field - */ + STREAM_GET(&label[1], pkt, BGP_LABEL_BYTES); } /* Process the route. */ @@ -3885,6 +3716,16 @@ static int process_type2_route(struct peer *peer, afi_t afi, safi_t safi, ret = bgp_withdraw(peer, (struct prefix *)&p, addpath_id, attr, afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, &label[0], num_labels, &evpn); + goto done; + +fail: +stream_failure: + flog_err(EC_BGP_EVPN_ROUTE_INVALID, + "%u:%s - Rx EVPN Type-2 NLRI - corrupt, discarding", + peer->bgp->vrf_id, peer->host); + ret = -1; +done: + stream_free(pkt); return ret; } @@ -3968,68 +3809,6 @@ static int process_type3_route(struct peer *peer, afi_t afi, safi_t safi, return ret; } -/* - * Process received EVPN type-4 route (advertise or withdraw). - */ -static int process_type4_route(struct peer *peer, afi_t afi, safi_t safi, - struct attr *attr, uint8_t *pfx, int psize, - uint32_t addpath_id) -{ - int ret; - esi_t esi; - uint8_t ipaddr_len; - struct in_addr vtep_ip; - struct prefix_rd prd; - struct prefix_evpn p; - - /* Type-4 route should be either 23 or 35 bytes - * RD (8), ESI (10), ip-len (1), ip (4 or 16) - */ - if (psize != 23 && psize != 35) { - flog_err(EC_BGP_EVPN_ROUTE_INVALID, - "%u:%s - Rx EVPN Type-4 NLRI with invalid length %d", - peer->bgp->vrf_id, peer->host, psize); - return -1; - } - - /* Make prefix_rd */ - prd.family = AF_UNSPEC; - prd.prefixlen = 64; - memcpy(&prd.val, pfx, 8); - pfx += 8; - - /* get the ESI */ - memcpy(&esi, pfx, ESI_BYTES); - pfx += ESI_BYTES; - - - /* Get the IP. */ - ipaddr_len = *pfx++; - if (ipaddr_len == IPV4_MAX_BITLEN) { - memcpy(&vtep_ip, pfx, IPV4_MAX_BYTELEN); - } else { - flog_err( - EC_BGP_EVPN_ROUTE_INVALID, - "%u:%s - Rx EVPN Type-4 NLRI with unsupported IP address length %d", - peer->bgp->vrf_id, peer->host, ipaddr_len); - return -1; - } - - build_evpn_type4_prefix(&p, &esi, vtep_ip); - /* Process the route. */ - if (attr) { - ret = bgp_update(peer, (struct prefix *)&p, addpath_id, attr, - afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, - &prd, NULL, 0, 0, NULL); - } else { - ret = bgp_withdraw(peer, (struct prefix *)&p, addpath_id, attr, - afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, - &prd, NULL, 0, NULL); - } - return ret; -} - - /* * Process received EVPN type-5 route (advertise or withdraw). */ @@ -4044,6 +3823,8 @@ static int process_type5_route(struct peer *peer, afi_t afi, safi_t safi, uint32_t eth_tag; mpls_label_t label; /* holds the VNI as in the packet */ int ret; + afi_t gw_afi; + bool is_valid_update = false; /* Type-5 route should be 34 or 58 bytes: * RD (8), ESI (10), Eth Tag (4), IP len (1), IP (4 or 16), @@ -4073,8 +3854,9 @@ static int process_type5_route(struct peer *peer, afi_t afi, safi_t safi, memset(&evpn, 0, sizeof(evpn)); /* Fetch ESI */ - memcpy(&evpn.eth_s_id.val, pfx, 10); - pfx += 10; + if (attr) + memcpy(&attr->esi, pfx, sizeof(esi_t)); + pfx += ESI_BYTES; /* Fetch Ethernet Tag. */ memcpy(ð_tag, pfx, 4); @@ -4102,12 +3884,14 @@ static int process_type5_route(struct peer *peer, afi_t afi, safi_t safi, pfx += 4; memcpy(&evpn.gw_ip.ipv4, pfx, 4); pfx += 4; + gw_afi = AF_INET; } else { SET_IPADDR_V6(&p.prefix.prefix_addr.ip); memcpy(&p.prefix.prefix_addr.ip.ipaddr_v6, pfx, 16); pfx += 16; memcpy(&evpn.gw_ip.ipv6, pfx, 16); pfx += 16; + gw_afi = AF_INET6; } /* Get the VNI (in MPLS label field). Stored as bytes here. */ @@ -4120,8 +3904,18 @@ static int process_type5_route(struct peer *peer, afi_t afi, safi_t safi, * field */ + if (attr) { + is_valid_update = true; + if (is_zero_mac(&attr->rmac) && + is_zero_gw_ip(&evpn.gw_ip, gw_afi)) + is_valid_update = false; + + if (is_mcast_mac(&attr->rmac) || is_bcast_mac(&attr->rmac)) + is_valid_update = false; + } + /* Process the route. */ - if (attr) + if (is_valid_update) ret = bgp_update(peer, (struct prefix *)&p, addpath_id, attr, afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, &label, 1, 0, &evpn); @@ -4133,13 +3927,14 @@ static int process_type5_route(struct peer *peer, afi_t afi, safi_t safi, return ret; } -static void evpn_mpattr_encode_type5(struct stream *s, struct prefix *p, - struct prefix_rd *prd, mpls_label_t *label, - uint32_t num_labels, struct attr *attr) +static void evpn_mpattr_encode_type5(struct stream *s, const struct prefix *p, + const struct prefix_rd *prd, + mpls_label_t *label, uint32_t num_labels, + struct attr *attr) { int len; char temp[16]; - struct evpn_addr *p_evpn_p; + const struct evpn_addr *p_evpn_p; memset(&temp, 0, 16); if (p->family != AF_EVPN) @@ -4157,9 +3952,9 @@ static void evpn_mpattr_encode_type5(struct stream *s, struct prefix *p, stream_putc(s, 8 + 10 + 4 + 1 + len + 3); stream_put(s, prd->val, 8); if (attr) - stream_put(s, &(attr->evpn_overlay.eth_s_id), 10); + stream_put(s, &attr->esi, sizeof(esi_t)); else - stream_put(s, &temp, 10); + stream_put(s, 0, sizeof(esi_t)); stream_putl(s, p_evpn_p->prefix_addr.eth_tag); stream_putc(s, p_evpn_p->prefix_addr.ip_prefix_length); if (IS_IPADDR_V4(&p_evpn_p->prefix_addr.ip)) @@ -4301,7 +4096,7 @@ static void update_autort_vni(struct hash_bucket *bucket, struct bgp *bgp) */ /* withdraw type-5 route corresponding to ip prefix */ -void bgp_evpn_withdraw_type5_route(struct bgp *bgp_vrf, struct prefix *p, +void bgp_evpn_withdraw_type5_route(struct bgp *bgp_vrf, const struct prefix *p, afi_t afi, safi_t safi) { int ret = 0; @@ -4323,20 +4118,21 @@ void bgp_evpn_withdraw_type5_route(struct bgp *bgp_vrf, struct prefix *p, void bgp_evpn_withdraw_type5_routes(struct bgp *bgp_vrf, afi_t afi, safi_t safi) { struct bgp_table *table = NULL; - struct bgp_node *rn = NULL; + struct bgp_dest *dest = NULL; struct bgp_path_info *pi; table = bgp_vrf->rib[afi][safi]; - for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) { + for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) { /* Only care about "selected" routes. Also ensure that * these are routes that are injectable into EVPN. */ /* TODO: Support for AddPath for EVPN. */ - for (pi = bgp_node_get_bgp_path_info(rn); pi; pi = pi->next) { + for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) { if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED) && is_route_injectable_into_evpn(pi)) { - bgp_evpn_withdraw_type5_route(bgp_vrf, &rn->p, - afi, safi); + bgp_evpn_withdraw_type5_route( + bgp_vrf, bgp_dest_get_prefix(dest), afi, + safi); break; } } @@ -4371,7 +4167,7 @@ void bgp_evpn_install_uninstall_default_route(struct bgp *bgp_vrf, afi_t afi, * path in the case of the attr. In the case of a local prefix (when we * are advertising local subnets), the src_attr will be NULL. */ -void bgp_evpn_advertise_type5_route(struct bgp *bgp_vrf, struct prefix *p, +void bgp_evpn_advertise_type5_route(struct bgp *bgp_vrf, const struct prefix *p, struct attr *src_attr, afi_t afi, safi_t safi) { @@ -4395,33 +4191,54 @@ void bgp_evpn_advertise_type5_routes(struct bgp *bgp_vrf, afi_t afi, safi_t safi) { struct bgp_table *table = NULL; - struct bgp_node *rn = NULL; + struct bgp_dest *dest = NULL; struct bgp_path_info *pi; table = bgp_vrf->rib[afi][safi]; - for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) { + for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) { /* Need to identify the "selected" route entry to use its * attribute. Also, ensure that the route is injectable * into EVPN. * TODO: Support for AddPath for EVPN. */ - for (pi = bgp_node_get_bgp_path_info(rn); pi; pi = pi->next) { + for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) { if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED) && is_route_injectable_into_evpn(pi)) { /* apply the route-map */ if (bgp_vrf->adv_cmd_rmap[afi][safi].map) { - int ret = 0; + route_map_result_t ret; + struct bgp_path_info tmp_pi; + struct bgp_path_info_extra tmp_pie; + struct attr tmp_attr; + + tmp_attr = *pi->attr; + + /* Fill temp path_info */ + prep_for_rmap_apply(&tmp_pi, &tmp_pie, + dest, pi, pi->peer, + &tmp_attr); + + RESET_FLAG(tmp_attr.rmap_change_flags); ret = route_map_apply( bgp_vrf->adv_cmd_rmap[afi][safi] .map, - &rn->p, RMAP_BGP, pi); - if (ret == RMAP_DENYMATCH) + bgp_dest_get_prefix(dest), + RMAP_BGP, &tmp_pi); + if (ret == RMAP_DENYMATCH) { + bgp_attr_flush(&tmp_attr); continue; - } - bgp_evpn_advertise_type5_route( - bgp_vrf, &rn->p, pi->attr, afi, safi); + } + bgp_evpn_advertise_type5_route( + bgp_vrf, + bgp_dest_get_prefix(dest), + &tmp_attr, afi, safi); + } else + bgp_evpn_advertise_type5_route( + bgp_vrf, + bgp_dest_get_prefix(dest), + pi->attr, afi, safi); break; } } @@ -4439,7 +4256,7 @@ void evpn_rt_delete_auto(struct bgp *bgp, vni_t vni, struct list *rtl) encode_route_target_as((bgp->as & 0xFFFF), vni, &eval); ecom_auto = ecommunity_new(); - ecommunity_add_val(ecom_auto, &eval); + ecommunity_add_val(ecom_auto, &eval, false, false); node_to_del = NULL; for (ALL_LIST_ELEMENTS(rtl, node, nnode, ecom)) { @@ -4459,7 +4276,8 @@ void bgp_evpn_configure_import_rt_for_vrf(struct bgp *bgp_vrf, struct ecommunity *ecomadd) { /* uninstall routes from vrf */ - uninstall_routes_for_vrf(bgp_vrf); + if (is_l3vni_live(bgp_vrf)) + uninstall_routes_for_vrf(bgp_vrf); /* Cleanup the RT to VRF mapping */ bgp_evpn_unmap_vrf_from_its_rts(bgp_vrf); @@ -4471,11 +4289,11 @@ void bgp_evpn_configure_import_rt_for_vrf(struct bgp *bgp_vrf, listnode_add_sort(bgp_vrf->vrf_import_rtl, ecomadd); SET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD); - /* map VRF to its RTs */ - bgp_evpn_map_vrf_to_its_rts(bgp_vrf); - - /* install routes matching the new VRF */ - install_routes_for_vrf(bgp_vrf); + /* map VRF to its RTs and install routes matching the new RTs */ + if (is_l3vni_live(bgp_vrf)) { + bgp_evpn_map_vrf_to_its_rts(bgp_vrf); + install_routes_for_vrf(bgp_vrf); + } } void bgp_evpn_unconfigure_import_rt_for_vrf(struct bgp *bgp_vrf, @@ -4485,7 +4303,8 @@ void bgp_evpn_unconfigure_import_rt_for_vrf(struct bgp *bgp_vrf, struct ecommunity *ecom = NULL; /* uninstall routes from vrf */ - uninstall_routes_for_vrf(bgp_vrf); + if (is_l3vni_live(bgp_vrf)) + uninstall_routes_for_vrf(bgp_vrf); /* Cleanup the RT to VRF mapping */ bgp_evpn_unmap_vrf_from_its_rts(bgp_vrf); @@ -4506,14 +4325,15 @@ void bgp_evpn_unconfigure_import_rt_for_vrf(struct bgp *bgp_vrf, /* fallback to auto import rt, if this was the last RT */ if (bgp_vrf->vrf_import_rtl && list_isempty(bgp_vrf->vrf_import_rtl)) { UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD); - evpn_auto_rt_import_add_for_vrf(bgp_vrf); + if (is_l3vni_live(bgp_vrf)) + evpn_auto_rt_import_add_for_vrf(bgp_vrf); } - /* map VRFs to its RTs */ - bgp_evpn_map_vrf_to_its_rts(bgp_vrf); - - /* install routes matching this new RT */ - install_routes_for_vrf(bgp_vrf); + /* map VRFs to its RTs and install routes matching this new RT */ + if (is_l3vni_live(bgp_vrf)) { + bgp_evpn_map_vrf_to_its_rts(bgp_vrf); + install_routes_for_vrf(bgp_vrf); + } } void bgp_evpn_configure_export_rt_for_vrf(struct bgp *bgp_vrf, @@ -4526,7 +4346,8 @@ void bgp_evpn_configure_export_rt_for_vrf(struct bgp *bgp_vrf, listnode_add_sort(bgp_vrf->vrf_export_rtl, ecomadd); SET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_RT_CFGD); - bgp_evpn_handle_export_rt_change_for_vrf(bgp_vrf); + if (is_l3vni_live(bgp_vrf)) + bgp_evpn_handle_export_rt_change_for_vrf(bgp_vrf); } void bgp_evpn_unconfigure_export_rt_for_vrf(struct bgp *bgp_vrf, @@ -4560,10 +4381,12 @@ void bgp_evpn_unconfigure_export_rt_for_vrf(struct bgp *bgp_vrf, /* fall back to auto-generated RT if this was the last RT */ if (list_isempty(bgp_vrf->vrf_export_rtl)) { UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_RT_CFGD); - evpn_auto_rt_export_add_for_vrf(bgp_vrf); + if (is_l3vni_live(bgp_vrf)) + evpn_auto_rt_export_add_for_vrf(bgp_vrf); } - bgp_evpn_handle_export_rt_change_for_vrf(bgp_vrf); + if (is_l3vni_live(bgp_vrf)) + bgp_evpn_handle_export_rt_change_for_vrf(bgp_vrf); } /* @@ -4575,6 +4398,9 @@ void bgp_evpn_unconfigure_export_rt_for_vrf(struct bgp *bgp_vrf, */ void bgp_evpn_handle_router_id_update(struct bgp *bgp, int withdraw) { + struct listnode *node; + struct bgp *bgp_vrf; + if (withdraw) { /* delete and withdraw all the type-5 routes @@ -4589,8 +4415,34 @@ void bgp_evpn_handle_router_id_update(struct bgp *bgp, int withdraw) (void (*)(struct hash_bucket *, void *))withdraw_router_id_vni, bgp); + + if (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT) { + for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp_vrf)) { + if (bgp_vrf->evpn_info->advertise_pip && + (bgp_vrf->evpn_info->pip_ip_static.s_addr + == INADDR_ANY)) + bgp_vrf->evpn_info->pip_ip.s_addr + = INADDR_ANY; + } + } } else { + /* Assign new default instance router-id */ + if (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT) { + for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp_vrf)) { + if (bgp_vrf->evpn_info->advertise_pip && + (bgp_vrf->evpn_info->pip_ip_static.s_addr + == INADDR_ANY)) { + bgp_vrf->evpn_info->pip_ip = + bgp->router_id; + /* advertise type-5 routes with + * new nexthop + */ + update_advertise_vrf_routes(bgp_vrf); + } + } + } + /* advertise all routes in the vrf as type-5 routes with the new * RD */ @@ -4687,63 +4539,64 @@ char *bgp_evpn_label2str(mpls_label_t *label, uint32_t num_labels, char *buf, * Function to convert evpn route to json format. * NOTE: We don't use prefix2str as the output here is a bit different. */ -void bgp_evpn_route2json(struct prefix_evpn *p, json_object *json) +void bgp_evpn_route2json(const struct prefix_evpn *p, json_object *json) { char buf1[ETHER_ADDR_STRLEN]; char buf2[PREFIX2STR_BUFFER]; + uint8_t family; + uint8_t prefixlen; if (!json) return; - if (p->prefix.route_type == BGP_EVPN_IMET_ROUTE) { - json_object_int_add(json, "routeType", p->prefix.route_type); + json_object_int_add(json, "routeType", p->prefix.route_type); + + switch (p->prefix.route_type) { + case BGP_EVPN_MAC_IP_ROUTE: json_object_int_add(json, "ethTag", - p->prefix.imet_addr.eth_tag); - json_object_int_add(json, "ipLen", - is_evpn_prefix_ipaddr_v4(p) - ? IPV4_MAX_BITLEN - : IPV6_MAX_BITLEN); - json_object_string_add(json, "ip", - inet_ntoa(p->prefix.imet_addr.ip.ipaddr_v4)); - } else if (p->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) { - if (is_evpn_prefix_ipaddr_none(p)) { - json_object_int_add(json, "routeType", - p->prefix.route_type); - json_object_int_add(json, "ethTag", - p->prefix.macip_addr.eth_tag); - json_object_int_add(json, "macLen", 8 * ETH_ALEN); - json_object_string_add(json, "mac", - prefix_mac2str(&p->prefix.macip_addr.mac, - buf1, - sizeof(buf1))); - } else { - uint8_t family; + p->prefix.macip_addr.eth_tag); + json_object_int_add(json, "macLen", 8 * ETH_ALEN); + json_object_string_add(json, "mac", + prefix_mac2str(&p->prefix.macip_addr.mac, buf1, + sizeof(buf1))); + + if (!is_evpn_prefix_ipaddr_none(p)) { + family = is_evpn_prefix_ipaddr_v4(p) ? AF_INET : + AF_INET6; + prefixlen = (family == AF_INET) ? + IPV4_MAX_BITLEN : IPV6_MAX_BITLEN; + inet_ntop(family, &p->prefix.macip_addr.ip.ip.addr, + buf2, PREFIX2STR_BUFFER); + json_object_int_add(json, "ipLen", prefixlen); + json_object_string_add(json, "ip", buf2); + } + break; - family = is_evpn_prefix_ipaddr_v4(p) ? AF_INET - : AF_INET6; + case BGP_EVPN_IMET_ROUTE: + json_object_int_add(json, "ethTag", + p->prefix.imet_addr.eth_tag); + family = is_evpn_prefix_ipaddr_v4(p) ? AF_INET : AF_INET6; + prefixlen = (family == AF_INET) ? IPV4_MAX_BITLEN : + IPV6_MAX_BITLEN; + inet_ntop(family, &p->prefix.imet_addr.ip.ip.addr, buf2, + PREFIX2STR_BUFFER); + json_object_int_add(json, "ipLen", prefixlen); + json_object_string_add(json, "ip", buf2); + break; - json_object_int_add(json, "routeType", - p->prefix.route_type); - json_object_int_add(json, "ethTag", - p->prefix.macip_addr.eth_tag); - json_object_int_add(json, "macLen", 8 * ETH_ALEN); - json_object_string_add(json, "mac", - prefix_mac2str(&p->prefix.macip_addr.mac, - buf1, - sizeof(buf1))); - json_object_int_add(json, "ipLen", - is_evpn_prefix_ipaddr_v4(p) - ? IPV4_MAX_BITLEN - : IPV6_MAX_BITLEN); - json_object_string_add( - json, "ip", - inet_ntop(family, - &p->prefix.macip_addr.ip.ip.addr, - buf2, - PREFIX2STR_BUFFER)); - } - } else { - /* Currently, this is to cater to other AF_ETHERNET code. */ + case BGP_EVPN_IP_PREFIX_ROUTE: + json_object_int_add(json, "ethTag", + p->prefix.prefix_addr.eth_tag); + family = is_evpn_prefix_ipaddr_v4(p) ? AF_INET : AF_INET6; + inet_ntop(family, &p->prefix.prefix_addr.ip.ip.addr, + buf2, sizeof(buf2)); + json_object_int_add(json, "ipLen", + p->prefix.prefix_addr.ip_prefix_length); + json_object_string_add(json, "ip", buf2); + break; + + default: + break; } } @@ -4751,7 +4604,7 @@ void bgp_evpn_route2json(struct prefix_evpn *p, json_object *json) * Function to convert evpn route to string. * NOTE: We don't use prefix2str as the output here is a bit different. */ -char *bgp_evpn_route2str(struct prefix_evpn *p, char *buf, int len) +char *bgp_evpn_route2str(const struct prefix_evpn *p, char *buf, int len) { char buf1[ETHER_ADDR_STRLEN]; char buf2[PREFIX2STR_BUFFER]; @@ -4804,6 +4657,15 @@ char *bgp_evpn_route2str(struct prefix_evpn *p, char *buf, int len) is_evpn_prefix_ipaddr_v4(p) ? IPV4_MAX_BITLEN : IPV6_MAX_BITLEN, inet_ntoa(p->prefix.es_addr.ip.ipaddr_v4)); + } else if (p->prefix.route_type == BGP_EVPN_AD_ROUTE) { + snprintf(buf, len, "[%d]:[%u]:[%s]:[%d]:[%s]", + p->prefix.route_type, + p->prefix.ead_addr.eth_tag, + esi_to_str(&p->prefix.ead_addr.esi, + buf3, sizeof(buf3)), + is_evpn_prefix_ipaddr_v4(p) ? IPV4_MAX_BITLEN + : IPV6_MAX_BITLEN, + inet_ntoa(p->prefix.ead_addr.ip.ipaddr_v4)); } else { /* For EVPN route types not supported yet. */ snprintf(buf, len, "(unsupported route type %d)", @@ -4816,8 +4678,8 @@ char *bgp_evpn_route2str(struct prefix_evpn *p, char *buf, int len) /* * Encode EVPN prefix in Update (MP_REACH) */ -void bgp_evpn_encode_prefix(struct stream *s, struct prefix *p, - struct prefix_rd *prd, mpls_label_t *label, +void bgp_evpn_encode_prefix(struct stream *s, const struct prefix *p, + const struct prefix_rd *prd, mpls_label_t *label, uint32_t num_labels, struct attr *attr, int addpath_encode, uint32_t addpath_tx_id) { @@ -4843,7 +4705,7 @@ void bgp_evpn_encode_prefix(struct stream *s, struct prefix *p, stream_putc(s, len); stream_put(s, prd->val, 8); /* RD */ if (attr) - stream_put(s, &attr->evpn_overlay.eth_s_id, ESI_LEN); + stream_put(s, &attr->esi, ESI_BYTES); else stream_put(s, 0, 10); stream_putl(s, evp->prefix.macip_addr.eth_tag); /* Ethernet Tag ID */ @@ -4878,6 +4740,16 @@ void bgp_evpn_encode_prefix(struct stream *s, struct prefix *p, stream_put_in_addr(s, &evp->prefix.es_addr.ip.ipaddr_v4); break; + case BGP_EVPN_AD_ROUTE: + /* RD, ESI, EthTag, 1 VNI */ + len = RD_BYTES + ESI_BYTES + EVPN_ETH_TAG_BYTES + BGP_LABEL_BYTES; + stream_putc(s, len); + stream_put(s, prd->val, RD_BYTES); /* RD */ + stream_put(s, evp->prefix.ead_addr.esi.val, ESI_BYTES); /* ESI */ + stream_putl(s, evp->prefix.ead_addr.eth_tag); /* Ethernet Tag */ + stream_put(s, label, BGP_LABEL_BYTES); + break; + case BGP_EVPN_IP_PREFIX_ROUTE: /* TODO: AddPath support. */ evpn_mpattr_encode_type5(s, p, prd, label, num_labels, attr); @@ -4923,7 +4795,8 @@ int bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr, if (pnt + BGP_ADDPATH_ID_LEN > lim) return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW; - addpath_id = ntohl(*((uint32_t *)pnt)); + memcpy(&addpath_id, pnt, BGP_ADDPATH_ID_LEN); + addpath_id = ntohl(addpath_id); pnt += BGP_ADDPATH_ID_LEN; } @@ -4964,7 +4837,7 @@ int bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr, break; case BGP_EVPN_ES_ROUTE: - if (process_type4_route(peer, afi, safi, + if (bgp_evpn_type4_route_process(peer, afi, safi, withdraw ? NULL : attr, pnt, psize, addpath_id)) { flog_err( @@ -4975,6 +4848,18 @@ int bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr, } break; + case BGP_EVPN_AD_ROUTE: + if (bgp_evpn_type1_route_process(peer, afi, safi, + withdraw ? NULL : attr, pnt, + psize, addpath_id)) { + flog_err( + EC_BGP_PKT_PROCESS, + "%u:%s - Error in processing EVPN type-1 NLRI size %d", + peer->bgp->vrf_id, peer->host, psize); + return BGP_NLRI_PARSE_ERROR_EVPN_TYPE1_SIZE; + } + break; + case BGP_EVPN_IP_PREFIX_ROUTE: if (process_type5_route(peer, afi, safi, withdraw ? NULL : attr, pnt, @@ -5153,11 +5038,12 @@ void bgp_evpn_derive_auto_rd_for_vrf(struct bgp *bgp) */ void bgp_evpn_derive_auto_rd(struct bgp *bgp, struct bgpevpn *vpn) { - char buf[100]; + char buf[BGP_EVPN_PREFIX_RD_LEN]; vpn->prd.family = AF_UNSPEC; vpn->prd.prefixlen = 64; - sprintf(buf, "%s:%hu", inet_ntoa(bgp->router_id), vpn->rd_id); + snprintf(buf, sizeof(buf), "%s:%hu", inet_ntoa(bgp->router_id), + vpn->rd_id); (void)str2prefix_rd(buf, &vpn->prd); UNSET_FLAG(vpn->flags, VNI_FLAG_RD_CFGD); } @@ -5236,6 +5122,8 @@ struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni, /* add to l2vni list on corresponding vrf */ bgpevpn_link_to_l3vni(vpn); + bgp_evpn_vni_es_init(vpn); + QOBJ_REG(vpn, bgpevpn); return vpn; } @@ -5248,6 +5136,7 @@ struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni, */ void bgp_evpn_free(struct bgp *bgp, struct bgpevpn *vpn) { + bgp_evpn_vni_es_cleanup(vpn); bgpevpn_unlink_from_l3vni(vpn); bgp_table_unlock(vpn->route_table); bgp_evpn_unmap_vni_from_its_rts(bgp, vpn); @@ -5259,83 +5148,11 @@ void bgp_evpn_free(struct bgp *bgp, struct bgpevpn *vpn) XFREE(MTYPE_BGP_EVPN, vpn); } -/* - * Lookup local ES. - */ -struct evpnes *bgp_evpn_lookup_es(struct bgp *bgp, esi_t *esi) -{ - struct evpnes *es; - struct evpnes tmp; - - memset(&tmp, 0, sizeof(struct evpnes)); - memcpy(&tmp.esi, esi, sizeof(esi_t)); - es = hash_lookup(bgp->esihash, &tmp); - return es; -} - -/* - * Create a new local es - invoked upon zebra notification. - */ -struct evpnes *bgp_evpn_es_new(struct bgp *bgp, - esi_t *esi, - struct ipaddr *originator_ip) -{ - char buf[100]; - struct evpnes *es; - - if (!bgp) - return NULL; - - es = XCALLOC(MTYPE_BGP_EVPN_ES, sizeof(struct evpnes)); - - /* set the ESI and originator_ip */ - memcpy(&es->esi, esi, sizeof(esi_t)); - memcpy(&es->originator_ip, originator_ip, sizeof(struct ipaddr)); - - /* Initialise the VTEP list */ - es->vtep_list = list_new(); - es->vtep_list->cmp = evpn_vtep_ip_cmp; - - /* auto derive RD for this es */ - bf_assign_index(bm->rd_idspace, es->rd_id); - es->prd.family = AF_UNSPEC; - es->prd.prefixlen = 64; - sprintf(buf, "%s:%hu", inet_ntoa(bgp->router_id), es->rd_id); - (void)str2prefix_rd(buf, &es->prd); - - /* Initialize the ES route table */ - es->route_table = bgp_table_init(bgp, AFI_L2VPN, SAFI_EVPN); - - /* Add to hash */ - if (!hash_get(bgp->esihash, es, hash_alloc_intern)) { - XFREE(MTYPE_BGP_EVPN_ES, es); - return NULL; - } - - QOBJ_REG(es, evpnes); - return es; -} - -/* - * Free a given ES - - * This just frees appropriate memory, caller should have taken other - * needed actions. - */ -void bgp_evpn_es_free(struct bgp *bgp, struct evpnes *es) -{ - list_delete(&es->vtep_list); - bgp_table_unlock(es->route_table); - bf_release_index(bm->rd_idspace, es->rd_id); - hash_release(bgp->esihash, es); - QOBJ_UNREG(es); - XFREE(MTYPE_BGP_EVPN_ES, es); -} - /* * Import evpn route from global table to VNI/VRF/ESI. */ int bgp_evpn_import_route(struct bgp *bgp, afi_t afi, safi_t safi, - struct prefix *p, struct bgp_path_info *pi) + const struct prefix *p, struct bgp_path_info *pi) { return install_uninstall_evpn_route(bgp, afi, safi, p, pi, 1); } @@ -5344,7 +5161,7 @@ int bgp_evpn_import_route(struct bgp *bgp, afi_t afi, safi_t safi, * Unimport evpn route from VNI/VRF/ESI. */ int bgp_evpn_unimport_route(struct bgp *bgp, afi_t afi, safi_t safi, - struct prefix *p, struct bgp_path_info *pi) + const struct prefix *p, struct bgp_path_info *pi) { return install_uninstall_evpn_route(bgp, afi, safi, p, pi, 0); } @@ -5354,7 +5171,7 @@ int bgp_filter_evpn_routes_upon_martian_nh_change(struct bgp *bgp) { afi_t afi; safi_t safi; - struct bgp_node *rd_rn, *rn; + struct bgp_dest *rd_dest, *dest; struct bgp_table *table; struct bgp_path_info *pi; @@ -5367,15 +5184,16 @@ int bgp_filter_evpn_routes_upon_martian_nh_change(struct bgp *bgp) * remote routes applicable for this VNI could have any RD. */ /* EVPN routes are a 2-level table. */ - for (rd_rn = bgp_table_top(bgp->rib[afi][safi]); rd_rn; - rd_rn = bgp_route_next(rd_rn)) { - table = bgp_node_get_bgp_table_info(rd_rn); + for (rd_dest = bgp_table_top(bgp->rib[afi][safi]); rd_dest; + rd_dest = bgp_route_next(rd_dest)) { + table = bgp_dest_get_bgp_table_info(rd_dest); if (!table) continue; - for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) { + for (dest = bgp_table_top(table); dest; + dest = bgp_route_next(dest)) { - for (pi = bgp_node_get_bgp_path_info(rn); pi; + for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) { /* Consider "valid" remote routes applicable for @@ -5383,29 +5201,29 @@ int bgp_filter_evpn_routes_upon_martian_nh_change(struct bgp *bgp) if (!(pi->type == ZEBRA_ROUTE_BGP && pi->sub_type == BGP_ROUTE_NORMAL)) continue; + if (bgp_nexthop_self(bgp, afi, pi->type, + pi->sub_type, pi->attr, + dest)) { + const struct prefix *p = + bgp_dest_get_prefix(dest); - if (bgp_nexthop_self(bgp, pi->attr->nexthop)) { + if (bgp_debug_update(pi->peer, p, NULL, + 1)) { + char attr_str[BUFSIZ] = {0}; - char attr_str[BUFSIZ] = {0}; - char pbuf[PREFIX_STRLEN]; + bgp_dump_attr(pi->attr, + attr_str, + sizeof(attr_str)); - bgp_dump_attr(pi->attr, attr_str, - BUFSIZ); - - if (bgp_debug_update(pi->peer, &rn->p, - NULL, 1)) zlog_debug( - "%u: prefix %s with attr %s - DENIED due to martian or self nexthop", - bgp->vrf_id, - prefix2str( - &rn->p, pbuf, - sizeof(pbuf)), + "%u: prefix %pRN with attr %s - DENIED due to martian or self nexthop", + bgp->vrf_id, dest, attr_str); - + } bgp_evpn_unimport_route(bgp, afi, safi, - &rn->p, pi); + p, pi); - bgp_rib_remove(rn, pi, pi->peer, afi, + bgp_rib_remove(dest, pi, pi->peer, afi, safi); } } @@ -5423,7 +5241,7 @@ int bgp_evpn_local_macip_del(struct bgp *bgp, vni_t vni, struct ethaddr *mac, { struct bgpevpn *vpn; struct prefix_evpn p; - struct bgp_node *rn; + struct bgp_dest *dest; /* Lookup VNI hash - should exist. */ vpn = bgp_evpn_lookup_vni(bgp, vni); @@ -5440,9 +5258,9 @@ int bgp_evpn_local_macip_del(struct bgp *bgp, vni_t vni, struct ethaddr *mac, delete_evpn_route(bgp, vpn, &p); } else { /* Re-instate the current remote best path if any */ - rn = bgp_node_lookup(vpn->route_table, (struct prefix *)&p); - if (rn) - evpn_zebra_reinstall_best_route(bgp, vpn, rn); + dest = bgp_node_lookup(vpn->route_table, (struct prefix *)&p); + if (dest) + evpn_zebra_reinstall_best_route(bgp, vpn, dest); } return 0; @@ -5452,7 +5270,7 @@ int bgp_evpn_local_macip_del(struct bgp *bgp, vni_t vni, struct ethaddr *mac, * Handle add of a local MACIP. */ int bgp_evpn_local_macip_add(struct bgp *bgp, vni_t vni, struct ethaddr *mac, - struct ipaddr *ip, uint8_t flags, uint32_t seq) + struct ipaddr *ip, uint8_t flags, uint32_t seq, esi_t *esi) { struct bgpevpn *vpn; struct prefix_evpn p; @@ -5468,7 +5286,7 @@ int bgp_evpn_local_macip_add(struct bgp *bgp, vni_t vni, struct ethaddr *mac, /* Create EVPN type-2 route and schedule for processing. */ build_evpn_type2_prefix(&p, mac, ip); - if (update_evpn_route(bgp, vpn, &p, flags, seq)) { + if (update_evpn_route(bgp, vpn, &p, flags, seq, esi)) { char buf[ETHER_ADDR_STRLEN]; char buf2[INET6_ADDRSTRLEN]; @@ -5500,9 +5318,12 @@ static void link_l2vni_hash_to_l3vni(struct hash_bucket *bucket, bgpevpn_link_to_l3vni(vpn); } -int bgp_evpn_local_l3vni_add(vni_t l3vni, vrf_id_t vrf_id, struct ethaddr *rmac, +int bgp_evpn_local_l3vni_add(vni_t l3vni, vrf_id_t vrf_id, + struct ethaddr *svi_rmac, + struct ethaddr *vrr_rmac, struct in_addr originator_ip, int filter, - ifindex_t svi_ifindex) + ifindex_t svi_ifindex, + bool is_anycast_mac) { struct bgp *bgp_vrf = NULL; /* bgp VRF instance */ struct bgp *bgp_evpn = NULL; /* EVPN bgp instance */ @@ -5529,14 +5350,11 @@ int bgp_evpn_local_l3vni_add(vni_t l3vni, vrf_id_t vrf_id, struct ethaddr *rmac, int ret = 0; - ret = bgp_get(&bgp_vrf, &as, vrf_id_to_name(vrf_id), - vrf_id == VRF_DEFAULT ? BGP_INSTANCE_TYPE_DEFAULT - : BGP_INSTANCE_TYPE_VRF); + ret = bgp_get_vty(&bgp_vrf, &as, vrf_id_to_name(vrf_id), + vrf_id == VRF_DEFAULT + ? BGP_INSTANCE_TYPE_DEFAULT + : BGP_INSTANCE_TYPE_VRF); switch (ret) { - case BGP_ERR_MULTIPLE_INSTANCE_NOT_SET: - flog_err(EC_BGP_MULTI_INSTANCE, - "'bgp multiple-instance' not present\n"); - return -1; case BGP_ERR_AS_MISMATCH: flog_err(EC_BGP_EVPN_AS_MISMATCH, "BGP is already running; AS is %u\n", as); @@ -5553,14 +5371,61 @@ int bgp_evpn_local_l3vni_add(vni_t l3vni, vrf_id_t vrf_id, struct ethaddr *rmac, /* associate the vrf with l3vni and related parameters */ bgp_vrf->l3vni = l3vni; - memcpy(&bgp_vrf->rmac, rmac, sizeof(struct ethaddr)); bgp_vrf->originator_ip = originator_ip; bgp_vrf->l3vni_svi_ifindex = svi_ifindex; + bgp_vrf->evpn_info->is_anycast_mac = is_anycast_mac; + + /* copy anycast MAC from VRR MAC */ + memcpy(&bgp_vrf->rmac, vrr_rmac, ETH_ALEN); + /* copy sys RMAC from SVI MAC */ + memcpy(&bgp_vrf->evpn_info->pip_rmac_zebra, svi_rmac, ETH_ALEN); + /* PIP user configured mac is not present use svi mac as sys mac */ + if (is_zero_mac(&bgp_vrf->evpn_info->pip_rmac_static)) + memcpy(&bgp_vrf->evpn_info->pip_rmac, svi_rmac, ETH_ALEN); + if (bgp_debug_zebra(NULL)) { + char buf[ETHER_ADDR_STRLEN]; + char buf1[ETHER_ADDR_STRLEN]; + char buf2[ETHER_ADDR_STRLEN]; + + zlog_debug("VRF %s vni %u pip %s RMAC %s sys RMAC %s static RMAC %s is_anycast_mac %s", + vrf_id_to_name(bgp_vrf->vrf_id), + bgp_vrf->l3vni, + bgp_vrf->evpn_info->advertise_pip ? "enable" + : "disable", + prefix_mac2str(&bgp_vrf->rmac, buf, sizeof(buf)), + prefix_mac2str(&bgp_vrf->evpn_info->pip_rmac, + buf1, sizeof(buf1)), + prefix_mac2str(&bgp_vrf->evpn_info->pip_rmac_static, + buf2, sizeof(buf2)), + is_anycast_mac ? "Enable" : "Disable"); + } /* set the right filter - are we using l3vni only for prefix routes? */ - if (filter) + if (filter) { SET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY); + /* + * VNI_FLAG_USE_TWO_LABELS flag for linked L2VNIs should not be + * set before linking vrf to L3VNI. Thus, no need to clear + * that explicitly. + */ + } else { + UNSET_FLAG(bgp_vrf->vrf_flags, + BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY); + + for (ALL_LIST_ELEMENTS_RO(bgp_vrf->l2vnis, node, vpn)) { + if (!CHECK_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS)) { + + /* + * If we are flapping VNI_FLAG_USE_TWO_LABELS + * flag, update all MACIP routes in this VNI + */ + SET_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS); + update_all_type2_routes(bgp_evpn, vpn); + } + } + } + /* Map auto derive or configured RTs */ if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD)) evpn_auto_rt_import_add_for_vrf(bgp_vrf); @@ -5637,6 +5502,10 @@ int bgp_evpn_local_l3vni_del(vni_t l3vni, vrf_id_t vrf_id) /* remove the Rmac from the BGP vrf */ memset(&bgp_vrf->rmac, 0, sizeof(struct ethaddr)); + memset(&bgp_vrf->evpn_info->pip_rmac_zebra, 0, ETH_ALEN); + if (is_zero_mac(&bgp_vrf->evpn_info->pip_rmac_static) && + !is_zero_mac(&bgp_vrf->evpn_info->pip_rmac)) + memset(&bgp_vrf->evpn_info->pip_rmac, 0, ETH_ALEN); /* remove default import RT or Unmap non-default import RT */ if (!list_isempty(bgp_vrf->vrf_import_rtl)) { @@ -5663,6 +5532,8 @@ int bgp_evpn_local_l3vni_del(vni_t l3vni, vrf_id_t vrf_id) for (ALL_LIST_ELEMENTS(bgp_vrf->l2vnis, node, next, vpn)) bgpevpn_unlink_from_l3vni(vpn); + UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY); + /* Delete the instance if it was autocreated */ if (CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_AUTO)) bgp_delete(bgp_vrf); @@ -5787,7 +5658,7 @@ int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni, if (bgp_evpn_vni_flood_mode_get(bgp, vpn) == VXLAN_FLOOD_HEAD_END_REPL) { build_evpn_type3_prefix(&p, vpn->originator_ip); - if (update_evpn_route(bgp, vpn, &p, 0, 0)) { + if (update_evpn_route(bgp, vpn, &p, 0, 0, NULL)) { flog_err(EC_BGP_EVPN_ROUTE_CREATE, "%u: Type3 route creation failure for VNI %u", bgp->vrf_id, vni); @@ -5805,87 +5676,8 @@ int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni, It needs to be conveyed again to zebra */ bgp_zebra_advertise_gw_macip(bgp, vpn->advertise_gw_macip, vpn->vni); - return 0; -} - -/* - * bgp_evpn_local_es_del - */ -int bgp_evpn_local_es_del(struct bgp *bgp, - esi_t *esi, - struct ipaddr *originator_ip) -{ - char buf[ESI_STR_LEN]; - struct evpnes *es = NULL; - - if (!bgp->esihash) { - flog_err(EC_BGP_ES_CREATE, "%u: ESI hash not yet created", - bgp->vrf_id); - return -1; - } - - /* Lookup ESI hash - should exist. */ - es = bgp_evpn_lookup_es(bgp, esi); - if (!es) { - flog_warn(EC_BGP_EVPN_ESI, - "%u: ESI hash entry for ESI %s at Local ES DEL", - bgp->vrf_id, esi_to_str(esi, buf, sizeof(buf))); - return -1; - } - - /* Delete all local EVPN ES routes from ESI table - * and schedule for processing (to withdraw from peers)) - */ - delete_routes_for_es(bgp, es); - - /* free the hash entry */ - bgp_evpn_es_free(bgp, es); - - return 0; -} - -/* - * bgp_evpn_local_es_add - */ -int bgp_evpn_local_es_add(struct bgp *bgp, - esi_t *esi, - struct ipaddr *originator_ip) -{ - char buf[ESI_STR_LEN]; - struct evpnes *es = NULL; - struct prefix_evpn p; - - if (!bgp->esihash) { - flog_err(EC_BGP_ES_CREATE, "%u: ESI hash not yet created", - bgp->vrf_id); - return -1; - } - - /* create the new es */ - es = bgp_evpn_lookup_es(bgp, esi); - if (!es) { - es = bgp_evpn_es_new(bgp, esi, originator_ip); - if (!es) { - flog_err( - EC_BGP_ES_CREATE, - "%u: Failed to allocate ES entry for ESI %s - at Local ES Add", - bgp->vrf_id, esi_to_str(esi, buf, sizeof(buf))); - return -1; - } - } - UNSET_FLAG(es->flags, EVPNES_REMOTE); - SET_FLAG(es->flags, EVPNES_LOCAL); - - build_evpn_type4_prefix(&p, esi, originator_ip->ipaddr_v4); - if (update_evpn_type4_route(bgp, es, &p)) { - flog_err(EC_BGP_EVPN_ROUTE_CREATE, - "%u: Type4 route creation failure for ESI %s", - bgp->vrf_id, esi_to_str(esi, buf, sizeof(buf))); - return -1; - } - - /* import all remote ES routes in th ES table */ - install_routes_for_es(bgp, es); + /* advertise svi mac-ip knob to zebra */ + bgp_zebra_advertise_svi_macip(bgp, vpn->advertise_svi_macip, vpn->vni); return 0; } @@ -5939,9 +5731,6 @@ void bgp_evpn_cleanup(struct bgp *bgp) hash_free(bgp->vnihash); bgp->vnihash = NULL; - if (bgp->esihash) - hash_free(bgp->esihash); - bgp->esihash = NULL; list_delete(&bgp->vrf_import_rtl); list_delete(&bgp->vrf_export_rtl); @@ -5958,9 +5747,6 @@ void bgp_evpn_init(struct bgp *bgp) { bgp->vnihash = hash_create(vni_hash_key_make, vni_hash_cmp, "BGP VNI Hash"); - bgp->esihash = - hash_create(esi_hash_keymake, esi_cmp, - "BGP EVPN Local ESI Hash"); bgp->import_rt_hash = hash_create(import_rt_hash_key_make, import_rt_hash_cmp, "BGP Import RT Hash"); @@ -5991,6 +5777,15 @@ void bgp_evpn_init(struct bgp *bgp) bgp->evpn_info->dad_freeze_time = 0; /* Initialize zebra vxlan */ bgp_zebra_dup_addr_detection(bgp); + /* Enable PIP feature by default for bgp vrf instance */ + if (bgp->inst_type == BGP_INSTANCE_TYPE_VRF) { + struct bgp *bgp_default; + + bgp->evpn_info->advertise_pip = true; + bgp_default = bgp_get_default(); + if (bgp_default) + bgp->evpn_info->pip_ip = bgp_default->router_id; + } } /* Default BUM handling is to do head-end replication. */ @@ -6001,3 +5796,43 @@ void bgp_evpn_vrf_delete(struct bgp *bgp_vrf) { bgp_evpn_unmap_vrf_from_its_rts(bgp_vrf); } + +/* + * Get the prefixlen of the ip prefix carried within the type5 evpn route. + */ +int bgp_evpn_get_type5_prefixlen(const struct prefix *pfx) +{ + struct prefix_evpn *evp = (struct prefix_evpn *)pfx; + + if (!pfx || pfx->family != AF_EVPN) + return 0; + + if (evp->prefix.route_type != BGP_EVPN_IP_PREFIX_ROUTE) + return 0; + + return evp->prefix.prefix_addr.ip_prefix_length; +} + +/* + * Should we register nexthop for this EVPN prefix for nexthop tracking? + */ +bool bgp_evpn_is_prefix_nht_supported(const struct prefix *pfx) +{ + struct prefix_evpn *evp = (struct prefix_evpn *)pfx; + + /* + * EVPN routes should be marked as valid only if the nexthop is + * reachable. Only if this happens, the route should be imported + * (into VNI or VRF routing tables) and/or advertised. + * Note: This is currently applied for EVPN type-2, type-3 and + * type-5 routes. It may be tweaked later on for other routes, or + * even removed completely when all routes are handled. + */ + if (pfx && pfx->family == AF_EVPN && + (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE || + evp->prefix.route_type == BGP_EVPN_IMET_ROUTE || + evp->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE)) + return true; + + return false; +} diff --git a/bgpd/bgp_evpn.h b/bgpd/bgp_evpn.h index 4ad8e95bee..8535f1fa31 100644 --- a/bgpd/bgp_evpn.h +++ b/bgpd/bgp_evpn.h @@ -81,7 +81,7 @@ static inline int is_route_parent_evpn(struct bgp_path_info *ri) { struct bgp_path_info *parent_ri; struct bgp_table *table; - struct bgp_node *rn; + struct bgp_dest *dest; /* If not imported (or doesn't have a parent), bail. */ if (ri->sub_type != BGP_ROUTE_IMPORTED || @@ -96,10 +96,10 @@ static inline int is_route_parent_evpn(struct bgp_path_info *ri) ; /* See if of family L2VPN/EVPN */ - rn = parent_ri->net; - if (!rn) + dest = parent_ri->net; + if (!dest) return 0; - table = bgp_node_table(rn); + table = bgp_dest_table(dest); if (table && table->afi == AFI_L2VPN && table->safi == SAFI_EVPN) @@ -120,7 +120,7 @@ static inline bool is_route_injectable_into_evpn(struct bgp_path_info *pi) { struct bgp_path_info *parent_pi; struct bgp_table *table; - struct bgp_node *rn; + struct bgp_dest *dest; if (pi->sub_type != BGP_ROUTE_IMPORTED || !pi->extra || @@ -128,10 +128,10 @@ static inline bool is_route_injectable_into_evpn(struct bgp_path_info *pi) return true; parent_pi = (struct bgp_path_info *)pi->extra->parent; - rn = parent_pi->net; - if (!rn) + dest = parent_pi->net; + if (!dest) return true; - table = bgp_node_table(rn); + table = bgp_dest_table(dest); if (table && table->afi == AFI_L2VPN && table->safi == SAFI_EVPN) @@ -140,11 +140,12 @@ static inline bool is_route_injectable_into_evpn(struct bgp_path_info *pi) } extern void bgp_evpn_advertise_type5_route(struct bgp *bgp_vrf, - struct prefix *p, + const struct prefix *p, struct attr *src_attr, afi_t afi, safi_t safi); -extern void bgp_evpn_withdraw_type5_route(struct bgp *bgp_vrf, struct prefix *p, - afi_t afi, safi_t safi); +extern void bgp_evpn_withdraw_type5_route(struct bgp *bgp_vrf, + const struct prefix *p, afi_t afi, + safi_t safi); extern void bgp_evpn_withdraw_type5_routes(struct bgp *bgp_vrf, afi_t afi, safi_t safi); extern void bgp_evpn_advertise_type5_routes(struct bgp *bgp_vrf, afi_t afi, @@ -153,42 +154,46 @@ extern void bgp_evpn_vrf_delete(struct bgp *bgp_vrf); extern void bgp_evpn_handle_router_id_update(struct bgp *bgp, int withdraw); extern char *bgp_evpn_label2str(mpls_label_t *label, uint32_t num_labels, char *buf, int len); -extern char *bgp_evpn_route2str(struct prefix_evpn *p, char *buf, int len); -extern void bgp_evpn_route2json(struct prefix_evpn *p, json_object *json); -extern void bgp_evpn_encode_prefix(struct stream *s, struct prefix *p, - struct prefix_rd *prd, mpls_label_t *label, - uint32_t num_labels, struct attr *attr, - int addpath_encode, uint32_t addpath_tx_id); +extern char *bgp_evpn_route2str(const struct prefix_evpn *p, char *buf, + int len); +extern void bgp_evpn_route2json(const struct prefix_evpn *p, json_object *json); +extern void bgp_evpn_encode_prefix(struct stream *s, const struct prefix *p, + const struct prefix_rd *prd, + mpls_label_t *label, uint32_t num_labels, + struct attr *attr, int addpath_encode, + uint32_t addpath_tx_id); extern int bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr, struct bgp_nlri *packet, int withdraw); extern int bgp_evpn_import_route(struct bgp *bgp, afi_t afi, safi_t safi, - struct prefix *p, struct bgp_path_info *ri); + const struct prefix *p, + struct bgp_path_info *ri); extern int bgp_evpn_unimport_route(struct bgp *bgp, afi_t afi, safi_t safi, - struct prefix *p, struct bgp_path_info *ri); + const struct prefix *p, + struct bgp_path_info *ri); extern int bgp_filter_evpn_routes_upon_martian_nh_change(struct bgp *bgp); extern int bgp_evpn_local_macip_del(struct bgp *bgp, vni_t vni, struct ethaddr *mac, struct ipaddr *ip, int state); extern int bgp_evpn_local_macip_add(struct bgp *bgp, vni_t vni, struct ethaddr *mac, struct ipaddr *ip, - uint8_t flags, uint32_t seq); + uint8_t flags, uint32_t seq, esi_t *esi); extern int bgp_evpn_local_l3vni_add(vni_t vni, vrf_id_t vrf_id, struct ethaddr *rmac, + struct ethaddr *vrr_rmac, struct in_addr originator_ip, int filter, - ifindex_t svi_ifindex); + ifindex_t svi_ifindex, bool is_anycast_mac); extern int bgp_evpn_local_l3vni_del(vni_t vni, vrf_id_t vrf_id); extern int bgp_evpn_local_vni_del(struct bgp *bgp, vni_t vni); extern int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni, struct in_addr originator_ip, vrf_id_t tenant_vrf_id, struct in_addr mcast_grp); -extern int bgp_evpn_local_es_add(struct bgp *bgp, esi_t *esi, - struct ipaddr *originator_ip); -extern int bgp_evpn_local_es_del(struct bgp *bgp, esi_t *esi, - struct ipaddr *originator_ip); extern void bgp_evpn_flood_control_change(struct bgp *bgp); extern void bgp_evpn_cleanup_on_disable(struct bgp *bgp); extern void bgp_evpn_cleanup(struct bgp *bgp); extern void bgp_evpn_init(struct bgp *bgp); +extern int bgp_evpn_get_type5_prefixlen(const struct prefix *pfx); +extern bool bgp_evpn_is_prefix_nht_supported(const struct prefix *pfx); +extern void update_advertise_vrf_routes(struct bgp *bgp_vrf); #endif /* _QUAGGA_BGP_EVPN_H */ diff --git a/bgpd/bgp_evpn_mh.c b/bgpd/bgp_evpn_mh.c new file mode 100644 index 0000000000..69996e3a1b --- /dev/null +++ b/bgpd/bgp_evpn_mh.c @@ -0,0 +1,2918 @@ +/* EVPN Multihoming procedures + * + * Copyright (C) 2019 Cumulus Networks, Inc. + * Anuradha Karuppiah + * + * This file is part of FRR. + * + * FRRouting is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRRouting is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + */ + +#include + +#include "command.h" +#include "filter.h" +#include "prefix.h" +#include "log.h" +#include "memory.h" +#include "stream.h" +#include "hash.h" +#include "jhash.h" +#include "zclient.h" + +#include "bgpd/bgp_attr_evpn.h" +#include "bgpd/bgpd.h" +#include "bgpd/bgp_table.h" +#include "bgpd/bgp_route.h" +#include "bgpd/bgp_attr.h" +#include "bgpd/bgp_mplsvpn.h" +#include "bgpd/bgp_evpn.h" +#include "bgpd/bgp_evpn_private.h" +#include "bgpd/bgp_evpn_mh.h" +#include "bgpd/bgp_ecommunity.h" +#include "bgpd/bgp_encap_types.h" +#include "bgpd/bgp_debug.h" +#include "bgpd/bgp_errors.h" +#include "bgpd/bgp_aspath.h" +#include "bgpd/bgp_zebra.h" +#include "bgpd/bgp_addpath.h" +#include "bgpd/bgp_label.h" + +static void bgp_evpn_local_es_down(struct bgp *bgp, + struct bgp_evpn_es *es); +static void bgp_evpn_local_type1_evi_route_del(struct bgp *bgp, + struct bgp_evpn_es *es); +static struct bgp_evpn_es_vtep *bgp_evpn_es_vtep_add(struct bgp *bgp, + struct bgp_evpn_es *es, struct in_addr vtep_ip, bool esr); +static void bgp_evpn_es_vtep_del(struct bgp *bgp, + struct bgp_evpn_es *es, struct in_addr vtep_ip, bool esr); +static void bgp_evpn_es_cons_checks_pend_add(struct bgp_evpn_es *es); +static void bgp_evpn_es_cons_checks_pend_del(struct bgp_evpn_es *es); +static void bgp_evpn_local_es_evi_do_del(struct bgp_evpn_es_evi *es_evi); + +esi_t zero_esi_buf, *zero_esi = &zero_esi_buf; + +/****************************************************************************** + * per-ES (Ethernet Segment) routing table + * + * Following routes are added to the ES's routing table - + * 1. Local and remote ESR (Type-4) + * 2. Local EAD-per-ES (Type-1). + * + * Key for these routes is {ESI, VTEP-IP} so the path selection is practically + * a no-op i.e. all paths lead to same VTEP-IP (i.e. result in the same VTEP + * being added to same ES). + * + * Note the following routes go into the VNI routing table (instead of the + * ES routing table) - + * 1. Remote EAD-per-ES + * 2. Local and remote EAD-per-EVI + */ + +/* Calculate the best path for a multi-homing (Type-1 or Type-4) route + * installed in the ES's routing table. + */ +static int bgp_evpn_es_route_select_install(struct bgp *bgp, + struct bgp_evpn_es *es, + struct bgp_node *rn) +{ + int ret = 0; + afi_t afi = AFI_L2VPN; + safi_t safi = SAFI_EVPN; + struct bgp_path_info *old_select; /* old best */ + struct bgp_path_info *new_select; /* new best */ + struct bgp_path_info_pair old_and_new; + + /* Compute the best path. */ + bgp_best_selection(bgp, rn, &bgp->maxpaths[afi][safi], + &old_and_new, afi, safi); + old_select = old_and_new.old; + new_select = old_and_new.new; + + /* + * If the best path hasn't changed - see if something needs to be + * updated + */ + if (old_select && old_select == new_select + && old_select->type == ZEBRA_ROUTE_BGP + && old_select->sub_type == BGP_ROUTE_IMPORTED + && !CHECK_FLAG(rn->flags, BGP_NODE_USER_CLEAR) + && !CHECK_FLAG(old_select->flags, BGP_PATH_ATTR_CHANGED) + && !bgp_addpath_is_addpath_used(&bgp->tx_addpath, afi, safi)) { + if (bgp_zebra_has_route_changed(old_select)) { + bgp_evpn_es_vtep_add(bgp, es, + old_select->attr->nexthop, + true /*esr*/); + } + UNSET_FLAG(old_select->flags, BGP_PATH_MULTIPATH_CHG); + bgp_zebra_clear_route_change_flags(rn); + return ret; + } + + /* If the user did a "clear" this flag will be set */ + UNSET_FLAG(rn->flags, BGP_NODE_USER_CLEAR); + + /* bestpath has changed; update relevant fields and install or uninstall + * into the zebra RIB. + */ + if (old_select || new_select) + bgp_bump_version(rn); + + if (old_select) + bgp_path_info_unset_flag(rn, old_select, BGP_PATH_SELECTED); + if (new_select) { + bgp_path_info_set_flag(rn, new_select, BGP_PATH_SELECTED); + bgp_path_info_unset_flag(rn, new_select, BGP_PATH_ATTR_CHANGED); + UNSET_FLAG(new_select->flags, BGP_PATH_MULTIPATH_CHG); + } + + if (new_select && new_select->type == ZEBRA_ROUTE_BGP + && new_select->sub_type == BGP_ROUTE_IMPORTED) { + bgp_evpn_es_vtep_add(bgp, es, + new_select->attr->nexthop, true /*esr */); + } else { + if (old_select && old_select->type == ZEBRA_ROUTE_BGP + && old_select->sub_type == BGP_ROUTE_IMPORTED) + bgp_evpn_es_vtep_del( + bgp, es, old_select->attr->nexthop, + true /*esr*/); + } + + /* Clear any route change flags. */ + bgp_zebra_clear_route_change_flags(rn); + + /* Reap old select bgp_path_info, if it has been removed */ + if (old_select && CHECK_FLAG(old_select->flags, BGP_PATH_REMOVED)) + bgp_path_info_reap(rn, old_select); + + return ret; +} + +/* Install Type-1/Type-4 route entry in the per-ES routing table */ +static int bgp_evpn_es_route_install(struct bgp *bgp, + struct bgp_evpn_es *es, struct prefix_evpn *p, + struct bgp_path_info *parent_pi) +{ + int ret = 0; + struct bgp_node *rn = NULL; + struct bgp_path_info *pi = NULL; + struct attr *attr_new = NULL; + + /* Create (or fetch) route within the VNI. + * NOTE: There is no RD here. + */ + rn = bgp_node_get(es->route_table, (struct prefix *)p); + + /* Check if route entry is already present. */ + for (pi = bgp_dest_get_bgp_path_info(rn); pi; pi = pi->next) + if (pi->extra + && (struct bgp_path_info *)pi->extra->parent == + parent_pi) + break; + + if (!pi) { + /* Add (or update) attribute to hash. */ + attr_new = bgp_attr_intern(parent_pi->attr); + + /* Create new route with its attribute. */ + pi = info_make(parent_pi->type, BGP_ROUTE_IMPORTED, 0, + parent_pi->peer, attr_new, rn); + SET_FLAG(pi->flags, BGP_PATH_VALID); + bgp_path_info_extra_get(pi); + pi->extra->parent = bgp_path_info_lock(parent_pi); + bgp_dest_lock_node((struct bgp_node *)parent_pi->net); + bgp_path_info_add(rn, pi); + } else { + if (attrhash_cmp(pi->attr, parent_pi->attr) + && !CHECK_FLAG(pi->flags, BGP_PATH_REMOVED)) { + bgp_dest_unlock_node(rn); + return 0; + } + /* The attribute has changed. */ + /* Add (or update) attribute to hash. */ + attr_new = bgp_attr_intern(parent_pi->attr); + + /* Restore route, if needed. */ + if (CHECK_FLAG(pi->flags, BGP_PATH_REMOVED)) + bgp_path_info_restore(rn, pi); + + /* Mark if nexthop has changed. */ + if (!IPV4_ADDR_SAME(&pi->attr->nexthop, &attr_new->nexthop)) + SET_FLAG(pi->flags, BGP_PATH_IGP_CHANGED); + + /* Unintern existing, set to new. */ + bgp_attr_unintern(&pi->attr); + pi->attr = attr_new; + pi->uptime = bgp_clock(); + } + + /* Perform route selection and update zebra, if required. */ + ret = bgp_evpn_es_route_select_install(bgp, es, rn); + + bgp_dest_unlock_node(rn); + + return ret; +} + +/* Uninstall Type-1/Type-4 route entry from the ES routing table */ +static int bgp_evpn_es_route_uninstall(struct bgp *bgp, struct bgp_evpn_es *es, + struct prefix_evpn *p, struct bgp_path_info *parent_pi) +{ + int ret; + struct bgp_node *rn; + struct bgp_path_info *pi; + + if (!es->route_table) + return 0; + + /* Locate route within the ESI. + * NOTE: There is no RD here. + */ + rn = bgp_node_lookup(es->route_table, (struct prefix *)p); + if (!rn) + return 0; + + /* Find matching route entry. */ + for (pi = bgp_dest_get_bgp_path_info(rn); pi; pi = pi->next) + if (pi->extra + && (struct bgp_path_info *)pi->extra->parent == + parent_pi) + break; + + if (!pi) + return 0; + + /* Mark entry for deletion */ + bgp_path_info_delete(rn, pi); + + /* Perform route selection and update zebra, if required. */ + ret = bgp_evpn_es_route_select_install(bgp, es, rn); + + /* Unlock route node. */ + bgp_dest_unlock_node(rn); + + return ret; +} + +/* Install or unistall a Tyoe-4 route in the per-ES routing table */ +int bgp_evpn_es_route_install_uninstall(struct bgp *bgp, struct bgp_evpn_es *es, + afi_t afi, safi_t safi, struct prefix_evpn *evp, + struct bgp_path_info *pi, int install) +{ + int ret = 0; + + if (install) + ret = bgp_evpn_es_route_install(bgp, es, evp, pi); + else + ret = bgp_evpn_es_route_uninstall(bgp, es, evp, pi); + + if (ret) { + flog_err( + EC_BGP_EVPN_FAIL, + "%u: Failed to %s EVPN %s route in ESI %s", + bgp->vrf_id, + install ? "install" : "uninstall", + "ES", es->esi_str); + return ret; + } + return 0; +} + +/* Delete (and withdraw) local routes for specified ES from global and ES table. + * Also remove all remote routes from the per ES table. Invoked when ES + * is deleted. + */ +static void bgp_evpn_es_route_del_all(struct bgp *bgp, struct bgp_evpn_es *es) +{ + struct bgp_node *rn; + struct bgp_path_info *pi, *nextpi; + + /* de-activate the ES */ + bgp_evpn_local_es_down(bgp, es); + bgp_evpn_local_type1_evi_route_del(bgp, es); + + /* Walk this ES's routing table and delete all routes. */ + for (rn = bgp_table_top(es->route_table); rn; + rn = bgp_route_next(rn)) { + for (pi = bgp_dest_get_bgp_path_info(rn); + (pi != NULL) && (nextpi = pi->next, 1); + pi = nextpi) { + bgp_path_info_delete(rn, pi); + bgp_path_info_reap(rn, pi); + } + } +} + +/***************************************************************************** + * Base APIs for creating MH routes (Type-1 or Type-4) on local ethernet + * segment updates. + */ + +/* create or update local EVPN type1/type4 route entry. + * + * This could be in - + * the ES table if ESR/EAD-ES (or) + * the VNI table if EAD-EVI (or) + * the global table if ESR/EAD-ES/EAD-EVI + * + * Note: vpn is applicable only to EAD-EVI routes (NULL for EAD-ES and + * ESR). + */ +static int bgp_evpn_mh_route_update(struct bgp *bgp, + struct bgp_evpn_es *es, struct bgpevpn *vpn, afi_t afi, + safi_t safi, struct bgp_node *rn, struct attr *attr, + int add, struct bgp_path_info **ri, int *route_changed) +{ + struct bgp_path_info *tmp_pi = NULL; + struct bgp_path_info *local_pi = NULL; /* local route entry if any */ + struct bgp_path_info *remote_pi = NULL; /* remote route entry if any */ + struct attr *attr_new = NULL; + struct prefix_evpn *evp; + + *ri = NULL; + evp = (struct prefix_evpn *)&rn->p; + *route_changed = 1; + + /* locate the local and remote entries if any */ + for (tmp_pi = bgp_dest_get_bgp_path_info(rn); tmp_pi; + tmp_pi = tmp_pi->next) { + if (tmp_pi->peer == bgp->peer_self + && tmp_pi->type == ZEBRA_ROUTE_BGP + && tmp_pi->sub_type == BGP_ROUTE_STATIC) + local_pi = tmp_pi; + if (tmp_pi->type == ZEBRA_ROUTE_BGP + && tmp_pi->sub_type == BGP_ROUTE_IMPORTED + && CHECK_FLAG(tmp_pi->flags, BGP_PATH_VALID)) + remote_pi = tmp_pi; + } + + /* we don't expect to see a remote_ri at this point as + * an ES route has {esi, vtep_ip} as the key in the ES-rt-table + * in the VNI-rt-table. + */ + if (remote_pi) { + flog_err( + EC_BGP_ES_INVALID, + "%u ERROR: local es route for ESI: %s Vtep %s also learnt from remote", + bgp->vrf_id, es->esi_str, + inet_ntoa(es->originator_ip)); + return -1; + } + + if (!local_pi && !add) + return 0; + + /* create or update the entry */ + if (!local_pi) { + + /* Add or update attribute to hash */ + attr_new = bgp_attr_intern(attr); + + /* Create new route with its attribute. */ + tmp_pi = info_make(ZEBRA_ROUTE_BGP, BGP_ROUTE_STATIC, 0, + bgp->peer_self, attr_new, rn); + SET_FLAG(tmp_pi->flags, BGP_PATH_VALID); + + if (evp->prefix.route_type == BGP_EVPN_AD_ROUTE) { + bgp_path_info_extra_get(tmp_pi); + tmp_pi->extra->num_labels = 1; + if (vpn) + vni2label(vpn->vni, &tmp_pi->extra->label[0]); + else + tmp_pi->extra->label[0] = 0; + } + + /* add the newly created path to the route-node */ + bgp_path_info_add(rn, tmp_pi); + } else { + tmp_pi = local_pi; + if (attrhash_cmp(tmp_pi->attr, attr) + && !CHECK_FLAG(tmp_pi->flags, BGP_PATH_REMOVED)) + *route_changed = 0; + else { + /* The attribute has changed. + * Add (or update) attribute to hash. + */ + attr_new = bgp_attr_intern(attr); + bgp_path_info_set_flag(rn, tmp_pi, + BGP_PATH_ATTR_CHANGED); + + /* Restore route, if needed. */ + if (CHECK_FLAG(tmp_pi->flags, BGP_PATH_REMOVED)) + bgp_path_info_restore(rn, tmp_pi); + + /* Unintern existing, set to new. */ + bgp_attr_unintern(&tmp_pi->attr); + tmp_pi->attr = attr_new; + tmp_pi->uptime = bgp_clock(); + } + } + + if (*route_changed) { + if (BGP_DEBUG(evpn_mh, EVPN_MH_RT)) + zlog_debug("local ES %s vni %u route-type %s nexthop %s updated", + es->esi_str, + vpn ? vpn->vni : 0, + evp->prefix.route_type == + BGP_EVPN_ES_ROUTE ? "esr" : + (vpn ? "ead-evi" : "ead-es"), + inet_ntoa(attr->mp_nexthop_global_in)); + } + + /* Return back the route entry. */ + *ri = tmp_pi; + return 0; +} + +/* Delete local EVPN ESR (type-4) and EAD (type-1) route + * + * Note: vpn is applicable only to EAD-EVI routes (NULL for EAD-ES and + * ESR). + */ +static int bgp_evpn_mh_route_delete(struct bgp *bgp, struct bgp_evpn_es *es, + struct bgpevpn *vpn, struct prefix_evpn *p) +{ + afi_t afi = AFI_L2VPN; + safi_t safi = SAFI_EVPN; + struct bgp_path_info *pi; + struct bgp_node *rn = NULL; /* rn in esi table */ + struct bgp_node *global_rn = NULL; /* rn in global table */ + struct bgp_table *rt_table; + struct prefix_rd *prd; + + if (vpn) { + rt_table = vpn->route_table; + prd = &vpn->prd; + } else { + rt_table = es->route_table; + prd = &es->prd; + } + + /* First, locate the route node within the ESI or VNI. + * If it doesn't exist, ther is nothing to do. + * Note: there is no RD here. + */ + rn = bgp_node_lookup(rt_table, (struct prefix *)p); + if (!rn) + return 0; + + if (BGP_DEBUG(evpn_mh, EVPN_MH_RT)) + zlog_debug("local ES %s vni %u route-type %s nexthop %s delete", + es->esi_str, + vpn ? vpn->vni : 0, + p->prefix.route_type == BGP_EVPN_ES_ROUTE ? + "esr" : (vpn ? "ead-evi" : "ead-es"), + inet_ntoa(es->originator_ip)); + + /* Next, locate route node in the global EVPN routing table. + * Note that this table is a 2-level tree (RD-level + Prefix-level) + */ + global_rn = bgp_global_evpn_node_lookup(bgp->rib[afi][safi], afi, safi, + (const struct prefix_evpn *)p, prd); + if (global_rn) { + + /* Delete route entry in the global EVPN table. */ + delete_evpn_route_entry(bgp, afi, safi, global_rn, &pi); + + /* Schedule for processing - withdraws to peers happen from + * this table. + */ + if (pi) + bgp_process(bgp, global_rn, afi, safi); + bgp_dest_unlock_node(global_rn); + } + + /* + * Delete route entry in the ESI or VNI routing table. + * This can just be removed. + */ + delete_evpn_route_entry(bgp, afi, safi, rn, &pi); + if (pi) + bgp_path_info_reap(rn, pi); + bgp_dest_unlock_node(rn); + return 0; +} + +/***************************************************************************** + * Ethernet Segment (Type-4) Routes + * ESRs are used for BUM handling. XXX - BUM support is planned for phase-2 i.e. + * this code is just a place holder for now + */ +/* Build extended community for EVPN ES (type-4) route */ +static void bgp_evpn_type4_route_extcomm_build(struct bgp_evpn_es *es, + struct attr *attr) +{ + struct ecommunity ecom_encap; + struct ecommunity ecom_es_rt; + struct ecommunity_val eval; + struct ecommunity_val eval_es_rt; + bgp_encap_types tnl_type; + struct ethaddr mac; + + /* Encap */ + tnl_type = BGP_ENCAP_TYPE_VXLAN; + memset(&ecom_encap, 0, sizeof(ecom_encap)); + encode_encap_extcomm(tnl_type, &eval); + ecom_encap.size = 1; + ecom_encap.unit_size = ECOMMUNITY_SIZE; + ecom_encap.val = (uint8_t *)eval.val; + attr->ecommunity = ecommunity_dup(&ecom_encap); + + /* ES import RT */ + memset(&mac, 0, sizeof(struct ethaddr)); + memset(&ecom_es_rt, 0, sizeof(ecom_es_rt)); + es_get_system_mac(&es->esi, &mac); + encode_es_rt_extcomm(&eval_es_rt, &mac); + ecom_es_rt.size = 1; + ecom_es_rt.unit_size = ECOMMUNITY_SIZE; + ecom_es_rt.val = (uint8_t *)eval_es_rt.val; + attr->ecommunity = + ecommunity_merge(attr->ecommunity, &ecom_es_rt); + + attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES); +} + +/* Create or update local type-4 route */ +static int bgp_evpn_type4_route_update(struct bgp *bgp, + struct bgp_evpn_es *es, struct prefix_evpn *p) +{ + int ret = 0; + int route_changed = 0; + afi_t afi = AFI_L2VPN; + safi_t safi = SAFI_EVPN; + struct attr attr; + struct attr *attr_new = NULL; + struct bgp_node *rn = NULL; + struct bgp_path_info *pi = NULL; + + memset(&attr, 0, sizeof(struct attr)); + + /* Build path-attribute for this route. */ + bgp_attr_default_set(&attr, BGP_ORIGIN_IGP); + attr.nexthop = es->originator_ip; + attr.mp_nexthop_global_in = es->originator_ip; + attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4; + + /* Set up extended community. */ + bgp_evpn_type4_route_extcomm_build(es, &attr); + + /* First, create (or fetch) route node within the ESI. */ + /* NOTE: There is no RD here. */ + rn = bgp_node_get(es->route_table, (struct prefix *)p); + + /* Create or update route entry. */ + ret = bgp_evpn_mh_route_update(bgp, es, NULL, afi, safi, + rn, &attr, 1, &pi, &route_changed); + if (ret != 0) { + flog_err(EC_BGP_ES_INVALID, + "%u ERROR: Failed to updated ES route ESI: %s VTEP %s", + bgp->vrf_id, es->esi_str, + inet_ntoa(es->originator_ip)); + } + + assert(pi); + attr_new = pi->attr; + + /* Perform route selection; + * this is just to set the flags correctly + * as local route in the ES always wins. + */ + bgp_evpn_es_route_select_install(bgp, es, rn); + bgp_dest_unlock_node(rn); + + /* If this is a new route or some attribute has changed, export the + * route to the global table. The route will be advertised to peers + * from there. Note that this table is a 2-level tree (RD-level + + * Prefix-level) similar to L3VPN routes. + */ + if (route_changed) { + struct bgp_path_info *global_pi; + + rn = bgp_global_evpn_node_get(bgp->rib[afi][safi], afi, safi, + p, &es->prd); + bgp_evpn_mh_route_update(bgp, es, NULL, afi, safi, + rn, attr_new, 1, &global_pi, &route_changed); + + /* Schedule for processing and unlock node. */ + bgp_process(bgp, rn, afi, safi); + bgp_dest_unlock_node(rn); + } + + /* Unintern temporary. */ + aspath_unintern(&attr.aspath); + return 0; +} + +/* Delete local type-4 route */ +static int bgp_evpn_type4_route_delete(struct bgp *bgp, + struct bgp_evpn_es *es, struct prefix_evpn *p) +{ + return bgp_evpn_mh_route_delete(bgp, es, NULL /* l2vni */, p); +} + +/* Process remote/received EVPN type-4 route (advertise or withdraw) */ +int bgp_evpn_type4_route_process(struct peer *peer, afi_t afi, safi_t safi, + struct attr *attr, uint8_t *pfx, int psize, + uint32_t addpath_id) +{ + int ret; + esi_t esi; + uint8_t ipaddr_len; + struct in_addr vtep_ip; + struct prefix_rd prd; + struct prefix_evpn p; + + /* Type-4 route should be either 23 or 35 bytes + * RD (8), ESI (10), ip-len (1), ip (4 or 16) + */ + if (psize != BGP_EVPN_TYPE4_V4_PSIZE && + psize != BGP_EVPN_TYPE4_V6_PSIZE) { + flog_err(EC_BGP_EVPN_ROUTE_INVALID, + "%u:%s - Rx EVPN Type-4 NLRI with invalid length %d", + peer->bgp->vrf_id, peer->host, psize); + return -1; + } + + /* Make prefix_rd */ + prd.family = AF_UNSPEC; + prd.prefixlen = 64; + memcpy(&prd.val, pfx, RD_BYTES); + pfx += RD_BYTES; + + /* get the ESI */ + memcpy(&esi, pfx, ESI_BYTES); + pfx += ESI_BYTES; + + + /* Get the IP. */ + ipaddr_len = *pfx++; + if (ipaddr_len == IPV4_MAX_BITLEN) { + memcpy(&vtep_ip, pfx, IPV4_MAX_BYTELEN); + } else { + flog_err( + EC_BGP_EVPN_ROUTE_INVALID, + "%u:%s - Rx EVPN Type-4 NLRI with unsupported IP address length %d", + peer->bgp->vrf_id, peer->host, ipaddr_len); + return -1; + } + + build_evpn_type4_prefix(&p, &esi, vtep_ip); + /* Process the route. */ + if (attr) { + ret = bgp_update(peer, (struct prefix *)&p, addpath_id, attr, + afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, + &prd, NULL, 0, 0, NULL); + } else { + ret = bgp_withdraw(peer, (struct prefix *)&p, addpath_id, attr, + afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, + &prd, NULL, 0, NULL); + } + return ret; +} + +/* Check if a prefix belongs to the local ES */ +static bool bgp_evpn_type4_prefix_match(struct prefix_evpn *p, + struct bgp_evpn_es *es) +{ + return (p->prefix.route_type == BGP_EVPN_ES_ROUTE) && + !memcmp(&p->prefix.es_addr.esi, &es->esi, sizeof(esi_t)); +} + +/* Import remote ESRs on local ethernet segment add */ +static int bgp_evpn_type4_remote_routes_import(struct bgp *bgp, + struct bgp_evpn_es *es, bool install) +{ + int ret; + afi_t afi; + safi_t safi; + char buf[PREFIX_STRLEN]; + struct bgp_node *rd_rn, *rn; + struct bgp_table *table; + struct bgp_path_info *pi; + + afi = AFI_L2VPN; + safi = SAFI_EVPN; + + /* Walk entire global routing table and evaluate routes which could be + * imported into this Ethernet Segment. + */ + for (rd_rn = bgp_table_top(bgp->rib[afi][safi]); rd_rn; + rd_rn = bgp_route_next(rd_rn)) { + table = bgp_dest_get_bgp_table_info(rd_rn); + if (!table) + continue; + + for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) { + struct prefix_evpn *evp = (struct prefix_evpn *)&rn->p; + + for (pi = bgp_dest_get_bgp_path_info(rn); pi; + pi = pi->next) { + /* + * Consider "valid" remote routes applicable for + * this ES. + */ + if (!(CHECK_FLAG(pi->flags, BGP_PATH_VALID) + && pi->type == ZEBRA_ROUTE_BGP + && pi->sub_type == BGP_ROUTE_NORMAL)) + continue; + + if (!bgp_evpn_type4_prefix_match(evp, es)) + continue; + + if (install) + ret = bgp_evpn_es_route_install( + bgp, es, evp, pi); + else + ret = bgp_evpn_es_route_uninstall( + bgp, es, evp, pi); + + if (ret) { + flog_err( + EC_BGP_EVPN_FAIL, + "Failed to %s EVPN %s route in ESI %s", + install ? "install" + : "uninstall", + prefix2str(evp, buf, + sizeof(buf)), + es->esi_str); + + bgp_dest_unlock_node(rd_rn); + bgp_dest_unlock_node(rn); + return ret; + } + } + } + } + return 0; +} + +/***************************************************************************** + * Ethernet Auto Discovery (EAD/Type-1) route handling + * There are two types of EAD routes - + * 1. EAD-per-ES - Key: {ESI, ET=0xffffffff} + * 2. EAD-per-EVI - Key: {ESI, ET=0} + */ + +/* Extended communities associated with EAD-per-ES */ +static void bgp_evpn_type1_es_route_extcomm_build(struct bgp_evpn_es *es, + struct attr *attr) +{ + struct ecommunity ecom_encap; + struct ecommunity ecom_esi_label; + struct ecommunity_val eval; + struct ecommunity_val eval_esi_label; + bgp_encap_types tnl_type; + struct listnode *evi_node, *rt_node; + struct ecommunity *ecom; + struct bgp_evpn_es_evi *es_evi; + + /* Encap */ + tnl_type = BGP_ENCAP_TYPE_VXLAN; + memset(&ecom_encap, 0, sizeof(ecom_encap)); + encode_encap_extcomm(tnl_type, &eval); + ecom_encap.size = 1; + ecom_encap.unit_size = ECOMMUNITY_SIZE; + ecom_encap.val = (uint8_t *)eval.val; + attr->ecommunity = ecommunity_dup(&ecom_encap); + + /* ESI label */ + encode_esi_label_extcomm(&eval_esi_label, + false /*single_active*/); + ecom_esi_label.size = 1; + ecom_esi_label.unit_size = ECOMMUNITY_SIZE; + ecom_esi_label.val = (uint8_t *)eval_esi_label.val; + attr->ecommunity = + ecommunity_merge(attr->ecommunity, &ecom_esi_label); + + /* Add export RTs for all L2-VNIs associated with this ES */ + /* XXX - suppress EAD-ES advertisment if there are no EVIs associated + * with it. + */ + for (ALL_LIST_ELEMENTS_RO(es->es_evi_list, + evi_node, es_evi)) { + if (!CHECK_FLAG(es_evi->flags, BGP_EVPNES_EVI_LOCAL)) + continue; + + /* Help SA understand the following loop */ + assert(es_evi->vpn != NULL); + + for (ALL_LIST_ELEMENTS_RO(es_evi->vpn->export_rtl, + rt_node, ecom)) + attr->ecommunity = ecommunity_merge(attr->ecommunity, + ecom); + } + + attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES); +} + +/* Extended communities associated with EAD-per-EVI */ +static void bgp_evpn_type1_evi_route_extcomm_build(struct bgp_evpn_es *es, + struct bgpevpn *vpn, struct attr *attr) +{ + struct ecommunity ecom_encap; + struct ecommunity_val eval; + bgp_encap_types tnl_type; + struct listnode *rt_node; + struct ecommunity *ecom; + + /* Encap */ + tnl_type = BGP_ENCAP_TYPE_VXLAN; + memset(&ecom_encap, 0, sizeof(ecom_encap)); + encode_encap_extcomm(tnl_type, &eval); + ecom_encap.size = 1; + ecom_encap.unit_size = ECOMMUNITY_SIZE; + ecom_encap.val = (uint8_t *)eval.val; + attr->ecommunity = ecommunity_dup(&ecom_encap); + + /* Add export RTs for the L2-VNI */ + for (ALL_LIST_ELEMENTS_RO(vpn->export_rtl, rt_node, ecom)) + attr->ecommunity = ecommunity_merge(attr->ecommunity, ecom); + + attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES); +} + +/* Update EVPN EAD (type-1) route - + * vpn - valid for EAD-EVI routes and NULL for EAD-ES routes + */ +static int bgp_evpn_type1_route_update(struct bgp *bgp, + struct bgp_evpn_es *es, struct bgpevpn *vpn, + struct prefix_evpn *p) +{ + int ret = 0; + afi_t afi = AFI_L2VPN; + safi_t safi = SAFI_EVPN; + struct attr attr; + struct attr *attr_new = NULL; + struct bgp_node *rn = NULL; + struct bgp_path_info *pi = NULL; + int route_changed = 0; + struct prefix_rd *global_rd; + + memset(&attr, 0, sizeof(struct attr)); + + /* Build path-attribute for this route. */ + bgp_attr_default_set(&attr, BGP_ORIGIN_IGP); + attr.nexthop = es->originator_ip; + attr.mp_nexthop_global_in = es->originator_ip; + attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4; + + if (vpn) { + /* EAD-EVI route update */ + /* MPLS label */ + vni2label(vpn->vni, &(attr.label)); + + /* Set up extended community */ + bgp_evpn_type1_evi_route_extcomm_build(es, vpn, &attr); + + /* First, create (or fetch) route node within the VNI. */ + rn = bgp_node_get(vpn->route_table, (struct prefix *)p); + + /* Create or update route entry. */ + ret = bgp_evpn_mh_route_update(bgp, es, vpn, afi, safi, + rn, &attr, 1, &pi, &route_changed); + if (ret != 0) { + flog_err(EC_BGP_ES_INVALID, + "%u Failed to update EAD-EVI route ESI: %s VNI %u VTEP %s", + bgp->vrf_id, es->esi_str, vpn->vni, + inet_ntoa(es->originator_ip)); + } + global_rd = &vpn->prd; + } else { + /* EAD-ES route update */ + /* MPLS label is 0 for EAD-ES route */ + + /* Set up extended community */ + bgp_evpn_type1_es_route_extcomm_build(es, &attr); + + /* First, create (or fetch) route node within the ES. */ + /* NOTE: There is no RD here. */ + /* XXX: fragment ID must be included as a part of the prefix. */ + rn = bgp_node_get(es->route_table, (struct prefix *)p); + + /* Create or update route entry. */ + ret = bgp_evpn_mh_route_update(bgp, es, vpn, afi, safi, + rn, &attr, 1, &pi, &route_changed); + if (ret != 0) { + flog_err(EC_BGP_ES_INVALID, + "%u ERROR: Failed to updated EAD-EVI route ESI: %s VTEP %s", + bgp->vrf_id, es->esi_str, + inet_ntoa(es->originator_ip)); + } + global_rd = &es->prd; + } + + + assert(pi); + attr_new = pi->attr; + + /* Perform route selection; + * this is just to set the flags correctly as local route in + * the ES always wins. + */ + evpn_route_select_install(bgp, vpn, rn); + bgp_dest_unlock_node(rn); + + /* If this is a new route or some attribute has changed, export the + * route to the global table. The route will be advertised to peers + * from there. Note that this table is a 2-level tree (RD-level + + * Prefix-level) similar to L3VPN routes. + */ + if (route_changed) { + struct bgp_path_info *global_pi; + + rn = bgp_global_evpn_node_get(bgp->rib[afi][safi], afi, safi, + p, global_rd); + bgp_evpn_mh_route_update(bgp, es, vpn, afi, safi, + rn, attr_new, 1, &global_pi, &route_changed); + + /* Schedule for processing and unlock node. */ + bgp_process(bgp, rn, afi, safi); + bgp_dest_unlock_node(rn); + } + + /* Unintern temporary. */ + aspath_unintern(&attr.aspath); + return 0; +} + +/* Delete local Type-1 route */ +static int bgp_evpn_type1_es_route_delete(struct bgp *bgp, + struct bgp_evpn_es *es, struct prefix_evpn *p) +{ + return bgp_evpn_mh_route_delete(bgp, es, NULL /* l2vni */, p); +} + +static int bgp_evpn_type1_evi_route_delete(struct bgp *bgp, + struct bgp_evpn_es *es, struct bgpevpn *vpn, + struct prefix_evpn *p) +{ + return bgp_evpn_mh_route_delete(bgp, es, vpn, p); +} + +/* Generate EAD-EVI for all VNIs */ +static void bgp_evpn_local_type1_evi_route_add(struct bgp *bgp, + struct bgp_evpn_es *es) +{ + struct listnode *evi_node; + struct prefix_evpn p; + struct bgp_evpn_es_evi *es_evi; + + if (CHECK_FLAG(es->flags, BGP_EVPNES_ADV_EVI)) + /* EAD-EVI route add for this ES is already done */ + return; + + SET_FLAG(es->flags, BGP_EVPNES_ADV_EVI); + build_evpn_type1_prefix(&p, BGP_EVPN_AD_EVI_ETH_TAG, + &es->esi, es->originator_ip); + + for (ALL_LIST_ELEMENTS_RO(es->es_evi_list, evi_node, es_evi)) { + if (!CHECK_FLAG(es_evi->flags, BGP_EVPNES_EVI_LOCAL)) + continue; + if (bgp_evpn_type1_route_update(bgp, es, es_evi->vpn, &p)) + flog_err(EC_BGP_EVPN_ROUTE_CREATE, + "%u: Type4 route creation failure for ESI %s", + bgp->vrf_id, es->esi_str); + } +} + +/* + * Withdraw EAD-EVI for all VNIs + */ +static void bgp_evpn_local_type1_evi_route_del(struct bgp *bgp, + struct bgp_evpn_es *es) +{ + struct listnode *evi_node; + struct prefix_evpn p; + struct bgp_evpn_es_evi *es_evi; + + /* Delete and withdraw locally learnt EAD-EVI route */ + if (!CHECK_FLAG(es->flags, BGP_EVPNES_ADV_EVI)) + /* EAD-EVI route has not been advertised for this ES */ + return; + + UNSET_FLAG(es->flags, BGP_EVPNES_ADV_EVI); + build_evpn_type1_prefix(&p, BGP_EVPN_AD_EVI_ETH_TAG, + &es->esi, es->originator_ip); + for (ALL_LIST_ELEMENTS_RO(es->es_evi_list, evi_node, es_evi)) { + if (!CHECK_FLAG(es_evi->flags, BGP_EVPNES_EVI_LOCAL)) + continue; + if (bgp_evpn_mh_route_delete(bgp, es, es_evi->vpn, &p)) + flog_err(EC_BGP_EVPN_ROUTE_CREATE, + "%u: Type4 route creation failure for ESI %s", + bgp->vrf_id, es->esi_str); + } +} + +/* + * Process received EVPN type-1 route (advertise or withdraw). + */ +int bgp_evpn_type1_route_process(struct peer *peer, afi_t afi, safi_t safi, + struct attr *attr, uint8_t *pfx, int psize, + uint32_t addpath_id) +{ + int ret; + struct prefix_rd prd; + esi_t esi; + uint32_t eth_tag; + mpls_label_t label; + struct in_addr vtep_ip; + struct prefix_evpn p; + + if (psize != BGP_EVPN_TYPE1_PSIZE) { + flog_err(EC_BGP_EVPN_ROUTE_INVALID, + "%u:%s - Rx EVPN Type-1 NLRI with invalid length %d", + peer->bgp->vrf_id, peer->host, psize); + return -1; + } + + /* Make prefix_rd */ + prd.family = AF_UNSPEC; + prd.prefixlen = 64; + memcpy(&prd.val, pfx, RD_BYTES); + pfx += RD_BYTES; + + /* get the ESI */ + memcpy(&esi, pfx, ESI_BYTES); + pfx += ESI_BYTES; + + /* Copy Ethernet Tag */ + memcpy(ð_tag, pfx, EVPN_ETH_TAG_BYTES); + eth_tag = ntohl(eth_tag); + pfx += EVPN_ETH_TAG_BYTES; + + memcpy(&label, pfx, BGP_LABEL_BYTES); + + /* EAD route prefix doesn't include the nexthop in the global + * table + */ + vtep_ip.s_addr = 0; + build_evpn_type1_prefix(&p, eth_tag, &esi, vtep_ip); + /* Process the route. */ + if (attr) { + ret = bgp_update(peer, (struct prefix *)&p, addpath_id, attr, + afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, + &prd, NULL, 0, 0, NULL); + } else { + ret = bgp_withdraw(peer, (struct prefix *)&p, addpath_id, attr, + afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, + &prd, NULL, 0, NULL); + } + return ret; +} + +/*****************************************************************************/ +/* Ethernet Segment Management + * 1. Ethernet Segment is a collection of links attached to the same + * server (MHD) or switch (MHN) + * 2. An Ethernet Segment can span multiple PEs and is identified by the + * 10-byte ES-ID. + * 3. Local ESs are configured in zebra and sent to BGP + * 4. Remote ESs are created by BGP when one or more ES-EVIs reference it i.e. + * created on first reference and release on last de-reference + * 5. An ES can be both local and remote. Infact most local ESs are expected + * to have an ES peer. + */ + +/* A list of remote VTEPs is maintained for each ES. This list includes - + * 1. VTEPs for which we have imported the ESR i.e. ES-peers + * 2. VTEPs that have an "active" ES-EVI VTEP i.e. EAD-per-ES and EAD-per-EVI + * have been imported into one or more VNIs + */ +static int bgp_evpn_es_vtep_cmp(void *p1, void *p2) +{ + const struct bgp_evpn_es_vtep *es_vtep1 = p1; + const struct bgp_evpn_es_vtep *es_vtep2 = p2; + + return es_vtep1->vtep_ip.s_addr - es_vtep2->vtep_ip.s_addr; +} + +static struct bgp_evpn_es_vtep *bgp_evpn_es_vtep_new(struct bgp_evpn_es *es, + struct in_addr vtep_ip) +{ + struct bgp_evpn_es_vtep *es_vtep; + + es_vtep = XCALLOC(MTYPE_BGP_EVPN_ES_VTEP, sizeof(*es_vtep)); + + es_vtep->es = es; + es_vtep->vtep_ip.s_addr = vtep_ip.s_addr; + listnode_init(&es_vtep->es_listnode, es_vtep); + listnode_add_sort(es->es_vtep_list, &es_vtep->es_listnode); + + return es_vtep; +} + +static void bgp_evpn_es_vtep_free(struct bgp_evpn_es_vtep *es_vtep) +{ + struct bgp_evpn_es *es = es_vtep->es; + + if (CHECK_FLAG(es_vtep->flags, BGP_EVPNES_VTEP_ESR) || + es_vtep->evi_cnt) + /* as long as there is some reference we can't free it */ + return; + + list_delete_node(es->es_vtep_list, &es_vtep->es_listnode); + XFREE(MTYPE_BGP_EVPN_ES_VTEP, es_vtep); +} + +/* check if VTEP is already part of the list */ +static struct bgp_evpn_es_vtep *bgp_evpn_es_vtep_find(struct bgp_evpn_es *es, + struct in_addr vtep_ip) +{ + struct listnode *node = NULL; + struct bgp_evpn_es_vtep *es_vtep; + + for (ALL_LIST_ELEMENTS_RO(es->es_vtep_list, node, es_vtep)) { + if (es_vtep->vtep_ip.s_addr == vtep_ip.s_addr) + return es_vtep; + } + return NULL; +} + +/* Send the remote ES to zebra for NHG programming */ +static int bgp_zebra_send_remote_es_vtep(struct bgp *bgp, + struct bgp_evpn_es_vtep *es_vtep, bool add) +{ + struct bgp_evpn_es *es = es_vtep->es; + struct stream *s; + + /* Check socket. */ + if (!zclient || zclient->sock < 0) + return 0; + + /* Don't try to register if Zebra doesn't know of this instance. */ + if (!IS_BGP_INST_KNOWN_TO_ZEBRA(bgp)) { + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("No zebra instance, not installing remote es %s", + es->esi_str); + return 0; + } + + s = zclient->obuf; + stream_reset(s); + + zclient_create_header(s, + add ? ZEBRA_REMOTE_ES_VTEP_ADD : ZEBRA_REMOTE_ES_VTEP_DEL, + bgp->vrf_id); + stream_put(s, &es->esi, sizeof(esi_t)); + stream_put_ipv4(s, es_vtep->vtep_ip.s_addr); + + stream_putw_at(s, 0, stream_get_endp(s)); + + if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) + zlog_debug("Tx %s Remote ESI %s VTEP %s", + add ? "ADD" : "DEL", es->esi_str, + inet_ntoa(es_vtep->vtep_ip)); + + return zclient_send_message(zclient); +} + +static void bgp_evpn_es_vtep_re_eval_active(struct bgp *bgp, + struct bgp_evpn_es_vtep *es_vtep) +{ + bool old_active; + bool new_active; + + old_active = !!CHECK_FLAG(es_vtep->flags, BGP_EVPNES_VTEP_ACTIVE); + /* currently we need an active EVI reference to use the VTEP as + * a nexthop. this may change... + */ + if (es_vtep->evi_cnt) + SET_FLAG(es_vtep->flags, BGP_EVPNES_VTEP_ACTIVE); + else + UNSET_FLAG(es_vtep->flags, BGP_EVPNES_VTEP_ACTIVE); + + new_active = !!CHECK_FLAG(es_vtep->flags, BGP_EVPNES_VTEP_ACTIVE); + + if (old_active == new_active) + return; + + if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) + zlog_debug("es %s vtep %s %s", + es_vtep->es->esi_str, + inet_ntoa(es_vtep->vtep_ip), + new_active ? "active" : "inactive"); + + /* send remote ES to zebra */ + bgp_zebra_send_remote_es_vtep(bgp, es_vtep, new_active); + + /* queue up the es for background consistency checks */ + bgp_evpn_es_cons_checks_pend_add(es_vtep->es); +} + +static struct bgp_evpn_es_vtep *bgp_evpn_es_vtep_add(struct bgp *bgp, + struct bgp_evpn_es *es, struct in_addr vtep_ip, bool esr) +{ + struct bgp_evpn_es_vtep *es_vtep; + + es_vtep = bgp_evpn_es_vtep_find(es, vtep_ip); + + if (!es_vtep) + es_vtep = bgp_evpn_es_vtep_new(es, vtep_ip); + + if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) + zlog_debug("es %s vtep %s add %s", + es_vtep->es->esi_str, + inet_ntoa(es_vtep->vtep_ip), + esr ? "esr" : "ead"); + + if (esr) + SET_FLAG(es_vtep->flags, BGP_EVPNES_VTEP_ESR); + else + ++es_vtep->evi_cnt; + + bgp_evpn_es_vtep_re_eval_active(bgp, es_vtep); + + return es_vtep; +} + +static void bgp_evpn_es_vtep_do_del(struct bgp *bgp, + struct bgp_evpn_es_vtep *es_vtep, bool esr) +{ + if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) + zlog_debug("es %s vtep %s del %s", + es_vtep->es->esi_str, + inet_ntoa(es_vtep->vtep_ip), + esr ? "esr" : "ead"); + if (esr) { + UNSET_FLAG(es_vtep->flags, BGP_EVPNES_VTEP_ESR); + } else { + if (es_vtep->evi_cnt) + --es_vtep->evi_cnt; + } + + bgp_evpn_es_vtep_re_eval_active(bgp, es_vtep); + bgp_evpn_es_vtep_free(es_vtep); +} + +static void bgp_evpn_es_vtep_del(struct bgp *bgp, + struct bgp_evpn_es *es, struct in_addr vtep_ip, bool esr) +{ + struct bgp_evpn_es_vtep *es_vtep; + + es_vtep = bgp_evpn_es_vtep_find(es, vtep_ip); + if (es_vtep) + bgp_evpn_es_vtep_do_del(bgp, es_vtep, esr); +} + +/* compare ES-IDs for the global ES RB tree */ +static int bgp_es_rb_cmp(const struct bgp_evpn_es *es1, + const struct bgp_evpn_es *es2) +{ + return memcmp(&es1->esi, &es2->esi, ESI_BYTES); +} +RB_GENERATE(bgp_es_rb_head, bgp_evpn_es, rb_node, bgp_es_rb_cmp); + +struct bgp_evpn_es *bgp_evpn_es_find(const esi_t *esi) +{ + struct bgp_evpn_es tmp; + + memcpy(&tmp.esi, esi, sizeof(esi_t)); + return RB_FIND(bgp_es_rb_head, &bgp_mh_info->es_rb_tree, &tmp); +} + +static struct bgp_evpn_es *bgp_evpn_es_new(struct bgp *bgp, const esi_t *esi) +{ + struct bgp_evpn_es *es; + + if (!bgp) + return NULL; + + es = XCALLOC(MTYPE_BGP_EVPN_ES, sizeof(struct bgp_evpn_es)); + + /* set the ESI */ + memcpy(&es->esi, esi, sizeof(esi_t)); + + /* Initialise the VTEP list */ + es->es_vtep_list = list_new(); + listset_app_node_mem(es->es_vtep_list); + es->es_vtep_list->cmp = bgp_evpn_es_vtep_cmp; + + esi_to_str(&es->esi, es->esi_str, sizeof(es->esi_str)); + + /* Initialize the ES routing table */ + es->route_table = bgp_table_init(bgp, AFI_L2VPN, SAFI_EVPN); + + /* Add to rb_tree */ + if (RB_INSERT(bgp_es_rb_head, &bgp_mh_info->es_rb_tree, es)) { + XFREE(MTYPE_BGP_EVPN_ES, es); + return NULL; + } + + /* Initialise the ES-EVI list */ + es->es_evi_list = list_new(); + listset_app_node_mem(es->es_evi_list); + + QOBJ_REG(es, bgp_evpn_es); + + return es; +} + +/* Free a given ES - + * This just frees appropriate memory, caller should have taken other + * needed actions. + */ +static void bgp_evpn_es_free(struct bgp_evpn_es *es, const char *caller) +{ + if (es->flags & (BGP_EVPNES_LOCAL | BGP_EVPNES_REMOTE)) + return; + + if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) + zlog_debug("%s: es %s free", caller, es->esi_str); + + /* cleanup resources maintained against the ES */ + list_delete(&es->es_evi_list); + list_delete(&es->es_vtep_list); + bgp_table_unlock(es->route_table); + + /* remove the entry from various databases */ + RB_REMOVE(bgp_es_rb_head, &bgp_mh_info->es_rb_tree, es); + bgp_evpn_es_cons_checks_pend_del(es); + + QOBJ_UNREG(es); + XFREE(MTYPE_BGP_EVPN_ES, es); +} + +/* init local info associated with the ES */ +static void bgp_evpn_es_local_info_set(struct bgp *bgp, struct bgp_evpn_es *es) +{ + char buf[BGP_EVPN_PREFIX_RD_LEN]; + + if (CHECK_FLAG(es->flags, BGP_EVPNES_LOCAL)) + return; + + SET_FLAG(es->flags, BGP_EVPNES_LOCAL); + listnode_init(&es->es_listnode, es); + listnode_add(bgp_mh_info->local_es_list, &es->es_listnode); + + /* auto derive RD for this es */ + bf_assign_index(bm->rd_idspace, es->rd_id); + es->prd.family = AF_UNSPEC; + es->prd.prefixlen = 64; + snprintf(buf, sizeof(buf), "%s:%hu", inet_ntoa(bgp->router_id), + es->rd_id); + (void)str2prefix_rd(buf, &es->prd); +} + +/* clear any local info associated with the ES */ +static void bgp_evpn_es_local_info_clear(struct bgp_evpn_es *es) +{ + if (!CHECK_FLAG(es->flags, BGP_EVPNES_LOCAL)) + return; + + UNSET_FLAG(es->flags, BGP_EVPNES_LOCAL); + + /* remove from the ES local list */ + list_delete_node(bgp_mh_info->local_es_list, &es->es_listnode); + + bf_release_index(bm->rd_idspace, es->rd_id); + + bgp_evpn_es_free(es, __func__); +} + +/* eval remote info associated with the ES */ +static void bgp_evpn_es_remote_info_re_eval(struct bgp_evpn_es *es) +{ + if (es->remote_es_evi_cnt) { + SET_FLAG(es->flags, BGP_EVPNES_REMOTE); + } else { + if (CHECK_FLAG(es->flags, BGP_EVPNES_REMOTE)) { + UNSET_FLAG(es->flags, BGP_EVPNES_REMOTE); + bgp_evpn_es_free(es, __func__); + } + } +} + +/* Process ES link oper-down by withdrawing ES-EAD and ESR */ +static void bgp_evpn_local_es_down(struct bgp *bgp, + struct bgp_evpn_es *es) +{ + struct prefix_evpn p; + int ret; + + if (!CHECK_FLAG(es->flags, BGP_EVPNES_OPER_UP)) + return; + + UNSET_FLAG(es->flags, BGP_EVPNES_OPER_UP); + + if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) + zlog_debug("local es %s down", es->esi_str); + + /* withdraw ESR */ + /* Delete and withdraw locally learnt ES route */ + build_evpn_type4_prefix(&p, &es->esi, es->originator_ip); + ret = bgp_evpn_type4_route_delete(bgp, es, &p); + if (ret) { + flog_err(EC_BGP_EVPN_ROUTE_DELETE, + "%u failed to delete type-4 route for ESI %s", + bgp->vrf_id, es->esi_str); + } + + /* withdraw EAD-EVI */ + if (!bgp_mh_info->ead_evi_adv_for_down_links) + bgp_evpn_local_type1_evi_route_del(bgp, es); + + /* withdraw EAD-ES */ + build_evpn_type1_prefix(&p, BGP_EVPN_AD_ES_ETH_TAG, + &es->esi, es->originator_ip); + ret = bgp_evpn_type1_es_route_delete(bgp, es, &p); + if (ret) { + flog_err(EC_BGP_EVPN_ROUTE_DELETE, + "%u failed to delete type-1 route for ESI %s", + bgp->vrf_id, es->esi_str); + } +} + +/* Process ES link oper-up by generating ES-EAD and ESR */ +static void bgp_evpn_local_es_up(struct bgp *bgp, struct bgp_evpn_es *es) +{ + struct prefix_evpn p; + + if (CHECK_FLAG(es->flags, BGP_EVPNES_OPER_UP)) + return; + + SET_FLAG(es->flags, BGP_EVPNES_OPER_UP); + + if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) + zlog_debug("local es %s up", es->esi_str); + + /* generate ESR */ + build_evpn_type4_prefix(&p, &es->esi, es->originator_ip); + if (bgp_evpn_type4_route_update(bgp, es, &p)) + flog_err(EC_BGP_EVPN_ROUTE_CREATE, + "%u: Type4 route creation failure for ESI %s", + bgp->vrf_id, es->esi_str); + + /* generate EAD-EVI */ + bgp_evpn_local_type1_evi_route_add(bgp, es); + + /* generate EAD-ES */ + build_evpn_type1_prefix(&p, BGP_EVPN_AD_ES_ETH_TAG, + &es->esi, es->originator_ip); + bgp_evpn_type1_route_update(bgp, es, NULL, &p); +} + +static void bgp_evpn_local_es_do_del(struct bgp *bgp, struct bgp_evpn_es *es) +{ + struct bgp_evpn_es_evi *es_evi; + struct listnode *evi_node, *evi_next_node; + + if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) + zlog_debug("del local es %s", es->esi_str); + + /* Delete all local EVPN ES routes from ESI table + * and schedule for processing (to withdraw from peers)) + */ + bgp_evpn_es_route_del_all(bgp, es); + + /* release all local ES EVIs associated with the ES */ + for (ALL_LIST_ELEMENTS(es->es_evi_list, evi_node, + evi_next_node, es_evi)) { + bgp_evpn_local_es_evi_do_del(es_evi); + } + + /* Clear local info associated with the ES and free it up if there is + * no remote reference + */ + bgp_evpn_es_local_info_clear(es); +} + +bool bgp_evpn_is_esi_local(esi_t *esi) +{ + struct bgp_evpn_es *es = NULL; + + /* Lookup ESI hash - should exist. */ + es = bgp_evpn_es_find(esi); + return es ? !!(es->flags & BGP_EVPNES_LOCAL) : false; +} + +int bgp_evpn_local_es_del(struct bgp *bgp, esi_t *esi) +{ + struct bgp_evpn_es *es = NULL; + + /* Lookup ESI hash - should exist. */ + es = bgp_evpn_es_find(esi); + if (!es) { + flog_warn(EC_BGP_EVPN_ESI, + "%u: ES %s missing at local ES DEL", + bgp->vrf_id, es->esi_str); + return -1; + } + + bgp_evpn_local_es_do_del(bgp, es); + return 0; +} + +/* Handle device to ES id association. Results in the creation of a local + * ES. + */ +int bgp_evpn_local_es_add(struct bgp *bgp, esi_t *esi, + struct in_addr originator_ip, bool oper_up) +{ + char buf[ESI_STR_LEN]; + struct bgp_evpn_es *es; + bool new_es = true; + + /* create the new es */ + es = bgp_evpn_es_find(esi); + if (es) { + if (CHECK_FLAG(es->flags, BGP_EVPNES_LOCAL)) + new_es = false; + } else { + es = bgp_evpn_es_new(bgp, esi); + if (!es) { + flog_err(EC_BGP_ES_CREATE, + "%u: Failed to allocate ES entry for ESI %s - at Local ES Add", + bgp->vrf_id, esi_to_str(esi, buf, sizeof(buf))); + return -1; + } + } + + if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) + zlog_debug("add local es %s orig-ip %s", + es->esi_str, + inet_ntoa(originator_ip)); + + es->originator_ip = originator_ip; + bgp_evpn_es_local_info_set(bgp, es); + + /* import all remote Type-4 routes in the ES table */ + if (new_es) + bgp_evpn_type4_remote_routes_import(bgp, es, + true /* install */); + + /* create and advertise EAD-EVI routes for the ES - + * XXX - till an ES-EVI reference is created there is really nothing to + * advertise + */ + if (bgp_mh_info->ead_evi_adv_for_down_links) + bgp_evpn_local_type1_evi_route_add(bgp, es); + + /* If the ES link is operationally up generate EAD-ES. EAD-EVI + * can be generated even if the link is inactive. + */ + if (oper_up) + bgp_evpn_local_es_up(bgp, es); + else + bgp_evpn_local_es_down(bgp, es); + + return 0; +} + +static char *bgp_evpn_es_vteps_str(char *vtep_str, struct bgp_evpn_es *es, + uint8_t vtep_str_size) +{ + char vtep_flag_str[BGP_EVPN_FLAG_STR_SZ]; + struct listnode *node; + struct bgp_evpn_es_vtep *es_vtep; + bool first = true; + + vtep_str[0] = '\0'; + for (ALL_LIST_ELEMENTS_RO(es->es_vtep_list, node, es_vtep)) { + vtep_flag_str[0] = '\0'; + if (es_vtep->flags & BGP_EVPNES_VTEP_ESR) + strlcat(vtep_flag_str, "E", sizeof(vtep_flag_str)); + if (es_vtep->flags & BGP_EVPNES_VTEP_ACTIVE) + strlcat(vtep_flag_str, "A", sizeof(vtep_flag_str)); + + if (!strlen(vtep_flag_str)) + strlcat(vtep_flag_str, "-", sizeof(vtep_flag_str)); + if (first) + first = false; + else + strlcat(vtep_str, ",", vtep_str_size); + strlcat(vtep_str, inet_ntoa(es_vtep->vtep_ip), vtep_str_size); + strlcat(vtep_str, "(", vtep_str_size); + strlcat(vtep_str, vtep_flag_str, vtep_str_size); + strlcat(vtep_str, ")", vtep_str_size); + } + + return vtep_str; +} + +static inline void json_array_string_add(json_object *json, const char *str) +{ + json_object_array_add(json, json_object_new_string(str)); +} + +static void bgp_evpn_es_json_vtep_fill(json_object *json_vteps, + struct bgp_evpn_es_vtep *es_vtep) +{ + json_object *json_vtep_entry; + json_object *json_flags; + + json_vtep_entry = json_object_new_object(); + + json_object_string_add(json_vtep_entry, "vtep_ip", + inet_ntoa(es_vtep->vtep_ip)); + if (es_vtep->flags & (BGP_EVPNES_VTEP_ESR | + BGP_EVPNES_VTEP_ACTIVE)) { + json_flags = json_object_new_array(); + if (es_vtep->flags & BGP_EVPNES_VTEP_ESR) + json_array_string_add(json_flags, "esr"); + if (es_vtep->flags & BGP_EVPNES_VTEP_ACTIVE) + json_array_string_add(json_flags, "active"); + json_object_object_add(json_vtep_entry, "flags", json_flags); + } + + json_object_array_add(json_vteps, + json_vtep_entry); +} + +static void bgp_evpn_es_show_entry(struct vty *vty, + struct bgp_evpn_es *es, json_object *json) +{ + char buf1[RD_ADDRSTRLEN]; + struct listnode *node; + struct bgp_evpn_es_vtep *es_vtep; + + if (json) { + json_object *json_vteps; + json_object *json_types; + + json_object_string_add(json, "esi", es->esi_str); + json_object_string_add(json, "rd", + prefix_rd2str(&es->prd, buf1, + sizeof(buf1))); + + if (es->flags & (BGP_EVPNES_LOCAL | BGP_EVPNES_REMOTE)) { + json_types = json_object_new_array(); + if (es->flags & BGP_EVPNES_LOCAL) + json_array_string_add(json_types, "local"); + if (es->flags & BGP_EVPNES_REMOTE) + json_array_string_add(json_types, "remote"); + json_object_object_add(json, "type", json_types); + } + + if (listcount(es->es_vtep_list)) { + json_vteps = json_object_new_array(); + for (ALL_LIST_ELEMENTS_RO(es->es_vtep_list, + node, es_vtep)) { + bgp_evpn_es_json_vtep_fill(json_vteps, es_vtep); + } + json_object_object_add(json, "vteps", json_vteps); + } + json_object_int_add(json, "vniCount", + listcount(es->es_evi_list)); + } else { + char type_str[4]; + char vtep_str[ES_VTEP_LIST_STR_SZ + BGP_EVPN_VTEPS_FLAG_STR_SZ]; + + type_str[0] = '\0'; + if (es->flags & BGP_EVPNES_LOCAL) + strlcat(type_str, "L", sizeof(type_str)); + if (es->flags & BGP_EVPNES_REMOTE) + strlcat(type_str, "R", sizeof(type_str)); + if (es->inconsistencies) + strlcat(type_str, "I", sizeof(type_str)); + + bgp_evpn_es_vteps_str(vtep_str, es, sizeof(vtep_str)); + + if (es->flags & BGP_EVPNES_LOCAL) + prefix_rd2str(&es->prd, buf1, sizeof(buf1)); + else + strlcpy(buf1, "-", sizeof(buf1)); + + vty_out(vty, "%-30s %-5s %-21s %-8d %s\n", + es->esi_str, type_str, buf1, + listcount(es->es_evi_list), vtep_str); + } +} + +static void bgp_evpn_es_show_entry_detail(struct vty *vty, + struct bgp_evpn_es *es, json_object *json) +{ + if (json) { + json_object *json_flags; + json_object *json_incons; + + /* Add the "brief" info first */ + bgp_evpn_es_show_entry(vty, es, json); + if (es->flags & (BGP_EVPNES_OPER_UP | BGP_EVPNES_ADV_EVI)) { + json_flags = json_object_new_array(); + if (es->flags & BGP_EVPNES_OPER_UP) + json_array_string_add(json_flags, "up"); + if (es->flags & BGP_EVPNES_ADV_EVI) + json_array_string_add(json_flags, + "advertiseEVI"); + json_object_object_add(json, "flags", json_flags); + } + json_object_string_add(json, "originator_ip", + inet_ntoa(es->originator_ip)); + json_object_int_add(json, "remoteVniCount", + es->remote_es_evi_cnt); + json_object_int_add(json, "inconsistentVniVtepCount", + es->incons_evi_vtep_cnt); + if (es->inconsistencies) { + json_incons = json_object_new_array(); + if (es->inconsistencies & BGP_EVPNES_INCONS_VTEP_LIST) + json_array_string_add(json_incons, + "vni-vtep-mismatch"); + json_object_object_add(json, "inconsistencies", + json_incons); + } + } else { + char incons_str[BGP_EVPNES_INCONS_STR_SZ]; + char type_str[4]; + char vtep_str[ES_VTEP_LIST_STR_SZ + BGP_EVPN_VTEPS_FLAG_STR_SZ]; + char buf1[RD_ADDRSTRLEN]; + + type_str[0] = '\0'; + if (es->flags & BGP_EVPNES_LOCAL) + strlcat(type_str, "L", sizeof(type_str)); + if (es->flags & BGP_EVPNES_REMOTE) + strlcat(type_str, "R", sizeof(type_str)); + + bgp_evpn_es_vteps_str(vtep_str, es, sizeof(vtep_str)); + if (!strlen(vtep_str)) + strlcpy(buf1, "-", sizeof(buf1)); + + if (es->flags & BGP_EVPNES_LOCAL) + prefix_rd2str(&es->prd, buf1, sizeof(buf1)); + else + strlcpy(buf1, "-", sizeof(buf1)); + + vty_out(vty, "ESI: %s\n", es->esi_str); + vty_out(vty, " Type: %s\n", type_str); + vty_out(vty, " RD: %s\n", buf1); + vty_out(vty, " Originator-IP: %s\n", + inet_ntoa(es->originator_ip)); + vty_out(vty, " VNI Count: %d\n", listcount(es->es_evi_list)); + vty_out(vty, " Remote VNI Count: %d\n", + es->remote_es_evi_cnt); + vty_out(vty, " Inconsistent VNI VTEP Count: %d\n", + es->incons_evi_vtep_cnt); + if (es->inconsistencies) { + incons_str[0] = '\0'; + if (es->inconsistencies & BGP_EVPNES_INCONS_VTEP_LIST) + strlcat(incons_str, "vni-vtep-mismatch", + sizeof(incons_str)); + } else { + strlcpy(incons_str, "-", sizeof(incons_str)); + } + vty_out(vty, " Inconsistencies: %s\n", + incons_str); + vty_out(vty, " VTEPs: %s\n", vtep_str); + vty_out(vty, "\n"); + } +} + +/* Display all ESs */ +void bgp_evpn_es_show(struct vty *vty, bool uj, bool detail) +{ + struct bgp_evpn_es *es; + json_object *json_array = NULL; + json_object *json = NULL; + + if (uj) { + /* create an array of ESs */ + json_array = json_object_new_array(); + } else { + if (!detail) { + vty_out(vty, + "ES Flags: L local, R remote, I inconsistent\n"); + vty_out(vty, + "VTEP Flags: E ESR/Type-4, A active nexthop\n"); + vty_out(vty, + "%-30s %-5s %-21s %-8s %s\n", + "ESI", "Flags", "RD", "#VNIs", "VTEPs"); + } + } + + RB_FOREACH(es, bgp_es_rb_head, &bgp_mh_info->es_rb_tree) { + if (uj) + /* create a separate json object for each ES */ + json = json_object_new_object(); + if (detail) + bgp_evpn_es_show_entry_detail(vty, es, json); + else + bgp_evpn_es_show_entry(vty, es, json); + /* add ES to the json array */ + if (uj) + json_object_array_add(json_array, json); + } + + /* print the array of json-ESs */ + if (uj) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json_array, JSON_C_TO_STRING_PRETTY)); + json_object_free(json_array); + } +} + +/* Display specific ES */ +void bgp_evpn_es_show_esi(struct vty *vty, esi_t *esi, bool uj) +{ + struct bgp_evpn_es *es; + json_object *json = NULL; + + if (uj) + json = json_object_new_object(); + + es = bgp_evpn_es_find(esi); + if (es) { + bgp_evpn_es_show_entry_detail(vty, es, json); + } else { + if (!uj) + vty_out(vty, "ESI not found\n"); + } + + if (uj) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } +} + +/*****************************************************************************/ +/* Ethernet Segment to EVI association - + * 1. The ES-EVI entry is maintained as a RB tree per L2-VNI + * (bgpevpn->es_evi_rb_tree). + * 2. Each local ES-EVI entry is rxed from zebra and then used by BGP to + * advertises an EAD-EVI (Type-1 EVPN) route + * 3. The remote ES-EVI is created when a bgp_evpn_es_evi_vtep references + * it. + */ + +/* A list of remote VTEPs is maintained for each ES-EVI. This list includes - + * 1. VTEPs for which we have imported the EAD-per-ES Type1 route + * 2. VTEPs for which we have imported the EAD-per-EVI Type1 route + * VTEPs for which both routes have been rxed are activated. Activation + * creates a NHG in the parent ES. + */ +static int bgp_evpn_es_evi_vtep_cmp(void *p1, void *p2) +{ + const struct bgp_evpn_es_evi_vtep *evi_vtep1 = p1; + const struct bgp_evpn_es_evi_vtep *evi_vtep2 = p2; + + return evi_vtep1->vtep_ip.s_addr - evi_vtep2->vtep_ip.s_addr; +} + +static struct bgp_evpn_es_evi_vtep *bgp_evpn_es_evi_vtep_new( + struct bgp_evpn_es_evi *es_evi, struct in_addr vtep_ip) +{ + struct bgp_evpn_es_evi_vtep *evi_vtep; + + evi_vtep = XCALLOC(MTYPE_BGP_EVPN_ES_EVI_VTEP, sizeof(*evi_vtep)); + + evi_vtep->es_evi = es_evi; + evi_vtep->vtep_ip.s_addr = vtep_ip.s_addr; + listnode_init(&evi_vtep->es_evi_listnode, evi_vtep); + listnode_add_sort(es_evi->es_evi_vtep_list, &evi_vtep->es_evi_listnode); + + return evi_vtep; +} + +static void bgp_evpn_es_evi_vtep_free(struct bgp_evpn_es_evi_vtep *evi_vtep) +{ + struct bgp_evpn_es_evi *es_evi = evi_vtep->es_evi; + + if (evi_vtep->flags & (BGP_EVPN_EVI_VTEP_EAD)) + /* as long as there is some reference we can't free it */ + return; + + list_delete_node(es_evi->es_evi_vtep_list, &evi_vtep->es_evi_listnode); + XFREE(MTYPE_BGP_EVPN_ES_EVI_VTEP, evi_vtep); +} + +/* check if VTEP is already part of the list */ +static struct bgp_evpn_es_evi_vtep *bgp_evpn_es_evi_vtep_find( + struct bgp_evpn_es_evi *es_evi, struct in_addr vtep_ip) +{ + struct listnode *node = NULL; + struct bgp_evpn_es_evi_vtep *evi_vtep; + + for (ALL_LIST_ELEMENTS_RO(es_evi->es_evi_vtep_list, node, evi_vtep)) { + if (evi_vtep->vtep_ip.s_addr == vtep_ip.s_addr) + return evi_vtep; + } + return NULL; +} + +/* A VTEP can be added as "active" attach to an ES if EAD-per-ES and + * EAD-per-EVI routes are rxed from it. + */ +static void bgp_evpn_es_evi_vtep_re_eval_active(struct bgp *bgp, + struct bgp_evpn_es_evi_vtep *evi_vtep) +{ + bool old_active; + bool new_active; + + old_active = !!CHECK_FLAG(evi_vtep->flags, BGP_EVPN_EVI_VTEP_ACTIVE); + + /* Both EAD-per-ES and EAD-per-EVI routes must be rxed from a PE + * before it can be activated. + */ + if ((evi_vtep->flags & BGP_EVPN_EVI_VTEP_EAD) == + BGP_EVPN_EVI_VTEP_EAD) + SET_FLAG(evi_vtep->flags, BGP_EVPN_EVI_VTEP_ACTIVE); + else + UNSET_FLAG(evi_vtep->flags, BGP_EVPN_EVI_VTEP_ACTIVE); + + new_active = !!CHECK_FLAG(evi_vtep->flags, BGP_EVPN_EVI_VTEP_ACTIVE); + + if (old_active == new_active) + return; + + if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) + zlog_debug("es %s evi %u vtep %s %s", + evi_vtep->es_evi->es->esi_str, + evi_vtep->es_evi->vpn->vni, + inet_ntoa(evi_vtep->vtep_ip), + new_active ? "active" : "inactive"); + + /* add VTEP to parent es */ + if (new_active) { + struct bgp_evpn_es_vtep *es_vtep; + + es_vtep = bgp_evpn_es_vtep_add(bgp, evi_vtep->es_evi->es, + evi_vtep->vtep_ip, false /*esr*/); + evi_vtep->es_vtep = es_vtep; + } else { + if (evi_vtep->es_vtep) { + bgp_evpn_es_vtep_do_del(bgp, evi_vtep->es_vtep, + false /*esr*/); + evi_vtep->es_vtep = NULL; + } + } + /* queue up the parent es for background consistency checks */ + bgp_evpn_es_cons_checks_pend_add(evi_vtep->es_evi->es); +} + +static void bgp_evpn_es_evi_vtep_add(struct bgp *bgp, + struct bgp_evpn_es_evi *es_evi, struct in_addr vtep_ip, + bool ead_es) +{ + struct bgp_evpn_es_evi_vtep *evi_vtep; + + evi_vtep = bgp_evpn_es_evi_vtep_find(es_evi, vtep_ip); + + if (!evi_vtep) + evi_vtep = bgp_evpn_es_evi_vtep_new(es_evi, vtep_ip); + + if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) + zlog_debug("add es %s evi %u vtep %s %s", + evi_vtep->es_evi->es->esi_str, + evi_vtep->es_evi->vpn->vni, + inet_ntoa(evi_vtep->vtep_ip), + ead_es ? "ead_es" : "ead_evi"); + + if (ead_es) + SET_FLAG(evi_vtep->flags, BGP_EVPN_EVI_VTEP_EAD_PER_ES); + else + SET_FLAG(evi_vtep->flags, BGP_EVPN_EVI_VTEP_EAD_PER_EVI); + + bgp_evpn_es_evi_vtep_re_eval_active(bgp, evi_vtep); +} + +static void bgp_evpn_es_evi_vtep_del(struct bgp *bgp, + struct bgp_evpn_es_evi *es_evi, struct in_addr vtep_ip, + bool ead_es) +{ + struct bgp_evpn_es_evi_vtep *evi_vtep; + + evi_vtep = bgp_evpn_es_evi_vtep_find(es_evi, vtep_ip); + if (!evi_vtep) + return; + + if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) + zlog_debug("del es %s evi %u vtep %s %s", + evi_vtep->es_evi->es->esi_str, + evi_vtep->es_evi->vpn->vni, + inet_ntoa(evi_vtep->vtep_ip), + ead_es ? "ead_es" : "ead_evi"); + + if (ead_es) + UNSET_FLAG(evi_vtep->flags, BGP_EVPN_EVI_VTEP_EAD_PER_ES); + else + UNSET_FLAG(evi_vtep->flags, BGP_EVPN_EVI_VTEP_EAD_PER_EVI); + + bgp_evpn_es_evi_vtep_re_eval_active(bgp, evi_vtep); + bgp_evpn_es_evi_vtep_free(evi_vtep); +} + +/* compare ES-IDs for the ES-EVI RB tree maintained per-VNI */ +static int bgp_es_evi_rb_cmp(const struct bgp_evpn_es_evi *es_evi1, + const struct bgp_evpn_es_evi *es_evi2) +{ + return memcmp(&es_evi1->es->esi, &es_evi2->es->esi, ESI_BYTES); +} +RB_GENERATE(bgp_es_evi_rb_head, bgp_evpn_es_evi, rb_node, bgp_es_evi_rb_cmp); + +/* find the ES-EVI in the per-L2-VNI RB tree */ +static struct bgp_evpn_es_evi *bgp_evpn_es_evi_find(struct bgp_evpn_es *es, + struct bgpevpn *vpn) +{ + struct bgp_evpn_es_evi es_evi; + + es_evi.es = es; + + return RB_FIND(bgp_es_evi_rb_head, &vpn->es_evi_rb_tree, &es_evi); +} + +/* allocate a new ES-EVI and insert it into the per-L2-VNI and per-ES + * tables. + */ +static struct bgp_evpn_es_evi *bgp_evpn_es_evi_new(struct bgp_evpn_es *es, + struct bgpevpn *vpn) +{ + struct bgp_evpn_es_evi *es_evi; + + es_evi = XCALLOC(MTYPE_BGP_EVPN_ES_EVI, sizeof(*es_evi)); + + es_evi->es = es; + es_evi->vpn = vpn; + + /* Initialise the VTEP list */ + es_evi->es_evi_vtep_list = list_new(); + listset_app_node_mem(es_evi->es_evi_vtep_list); + es_evi->es_evi_vtep_list->cmp = bgp_evpn_es_evi_vtep_cmp; + + /* insert into the VNI-ESI rb tree */ + if (RB_INSERT(bgp_es_evi_rb_head, &vpn->es_evi_rb_tree, es_evi)) { + XFREE(MTYPE_BGP_EVPN_ES_EVI, es_evi); + return NULL; + } + + /* add to the ES's VNI list */ + listnode_init(&es_evi->es_listnode, es_evi); + listnode_add(es->es_evi_list, &es_evi->es_listnode); + + return es_evi; +} + +/* remove the ES-EVI from the per-L2-VNI and per-ES tables and free + * up the memory. + */ +static void bgp_evpn_es_evi_free(struct bgp_evpn_es_evi *es_evi) +{ + struct bgp_evpn_es *es = es_evi->es; + struct bgpevpn *vpn = es_evi->vpn; + + /* cannot free the element as long as there is a local or remote + * reference + */ + if (es_evi->flags & (BGP_EVPNES_EVI_LOCAL | BGP_EVPNES_EVI_REMOTE)) + return; + + /* remove from the ES's VNI list */ + list_delete_node(es->es_evi_list, &es_evi->es_listnode); + + /* remove from the VNI-ESI rb tree */ + RB_REMOVE(bgp_es_evi_rb_head, &vpn->es_evi_rb_tree, es_evi); + + /* free the VTEP list */ + list_delete(&es_evi->es_evi_vtep_list); + + /* remove from the VNI-ESI rb tree */ + XFREE(MTYPE_BGP_EVPN_ES_EVI, es_evi); +} + +/* init local info associated with the ES-EVI */ +static void bgp_evpn_es_evi_local_info_set(struct bgp_evpn_es_evi *es_evi) +{ + struct bgpevpn *vpn = es_evi->vpn; + + if (CHECK_FLAG(es_evi->flags, BGP_EVPNES_EVI_LOCAL)) + return; + + SET_FLAG(es_evi->flags, BGP_EVPNES_EVI_LOCAL); + listnode_init(&es_evi->l2vni_listnode, es_evi); + listnode_add(vpn->local_es_evi_list, &es_evi->l2vni_listnode); +} + +/* clear any local info associated with the ES-EVI */ +static void bgp_evpn_es_evi_local_info_clear(struct bgp_evpn_es_evi *es_evi) +{ + struct bgpevpn *vpn = es_evi->vpn; + + if (!CHECK_FLAG(es_evi->flags, BGP_EVPNES_EVI_LOCAL)) + return; + + UNSET_FLAG(es_evi->flags, BGP_EVPNES_EVI_LOCAL); + list_delete_node(vpn->local_es_evi_list, &es_evi->l2vni_listnode); + + bgp_evpn_es_evi_free(es_evi); +} + +/* eval remote info associated with the ES */ +static void bgp_evpn_es_evi_remote_info_re_eval(struct bgp_evpn_es_evi *es_evi) +{ + struct bgp_evpn_es *es = es_evi->es; + + /* if there are remote VTEPs the ES-EVI is classified as "remote" */ + if (listcount(es_evi->es_evi_vtep_list)) { + if (!CHECK_FLAG(es_evi->flags, BGP_EVPNES_EVI_REMOTE)) { + SET_FLAG(es_evi->flags, BGP_EVPNES_EVI_REMOTE); + ++es->remote_es_evi_cnt; + /* set remote on the parent es */ + bgp_evpn_es_remote_info_re_eval(es); + } + } else { + if (CHECK_FLAG(es_evi->flags, BGP_EVPNES_EVI_REMOTE)) { + UNSET_FLAG(es_evi->flags, BGP_EVPNES_EVI_REMOTE); + if (es->remote_es_evi_cnt) + --es->remote_es_evi_cnt; + bgp_evpn_es_evi_free(es_evi); + /* check if "remote" can be cleared from the + * parent es. + */ + bgp_evpn_es_remote_info_re_eval(es); + } + } +} + +static void bgp_evpn_local_es_evi_do_del(struct bgp_evpn_es_evi *es_evi) +{ + struct prefix_evpn p; + struct bgp_evpn_es *es = es_evi->es; + struct bgp *bgp; + + if (!CHECK_FLAG(es_evi->flags, BGP_EVPNES_EVI_LOCAL)) + return; + + if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) + zlog_debug("del local es %s evi %u", + es_evi->es->esi_str, + es_evi->vpn->vni); + + bgp = bgp_get_evpn(); + + if (bgp) { + /* update EAD-ES with new list of VNIs */ + if (CHECK_FLAG(es->flags, BGP_EVPNES_OPER_UP)) { + build_evpn_type1_prefix(&p, BGP_EVPN_AD_ES_ETH_TAG, + &es->esi, es->originator_ip); + if (bgp_evpn_type1_route_update(bgp, es, NULL, &p)) + flog_err(EC_BGP_EVPN_ROUTE_CREATE, + "%u: EAD-ES route update failure for ESI %s VNI %u", + bgp->vrf_id, es->esi_str, + es_evi->vpn->vni); + } + + /* withdraw and delete EAD-EVI */ + if (CHECK_FLAG(es->flags, BGP_EVPNES_ADV_EVI)) { + build_evpn_type1_prefix(&p, BGP_EVPN_AD_EVI_ETH_TAG, + &es->esi, es->originator_ip); + if (bgp_evpn_type1_evi_route_delete(bgp, + es, es_evi->vpn, &p)) + flog_err(EC_BGP_EVPN_ROUTE_DELETE, + "%u: EAD-EVI route deletion failure for ESI %s VNI %u", + bgp->vrf_id, es->esi_str, + es_evi->vpn->vni); + } + } + + bgp_evpn_es_evi_local_info_clear(es_evi); + +} + +int bgp_evpn_local_es_evi_del(struct bgp *bgp, esi_t *esi, vni_t vni) +{ + struct bgpevpn *vpn; + struct bgp_evpn_es *es; + struct bgp_evpn_es_evi *es_evi; + char buf[ESI_STR_LEN]; + + es = bgp_evpn_es_find(esi); + if (!es) { + flog_err( + EC_BGP_ES_CREATE, + "%u: Failed to deref VNI %d from ESI %s; ES not present", + bgp->vrf_id, vni, + esi_to_str(esi, buf, sizeof(buf))); + return -1; + } + + vpn = bgp_evpn_lookup_vni(bgp, vni); + if (!vpn) { + flog_err( + EC_BGP_ES_CREATE, + "%u: Failed to deref VNI %d from ESI %s; VNI not present", + bgp->vrf_id, vni, es->esi_str); + return -1; + } + + es_evi = bgp_evpn_es_evi_find(es, vpn); + if (!es_evi) { + flog_err( + EC_BGP_ES_CREATE, + "%u: Failed to deref VNI %d from ESI %s; ES-VNI not present", + bgp->vrf_id, vni, es->esi_str); + return -1; + } + + bgp_evpn_local_es_evi_do_del(es_evi); + return 0; +} + +/* Create ES-EVI and advertise the corresponding EAD routes */ +int bgp_evpn_local_es_evi_add(struct bgp *bgp, esi_t *esi, vni_t vni) +{ + struct bgpevpn *vpn; + struct prefix_evpn p; + struct bgp_evpn_es *es; + struct bgp_evpn_es_evi *es_evi; + char buf[ESI_STR_LEN]; + + es = bgp_evpn_es_find(esi); + if (!es) { + flog_err( + EC_BGP_ES_CREATE, + "%u: Failed to associate VNI %d with ESI %s; ES not present", + bgp->vrf_id, vni, + esi_to_str(esi, buf, sizeof(buf))); + return -1; + } + + vpn = bgp_evpn_lookup_vni(bgp, vni); + if (!vpn) { + flog_err( + EC_BGP_ES_CREATE, + "%u: Failed to associate VNI %d with ESI %s; VNI not present", + bgp->vrf_id, vni, es->esi_str); + return -1; + } + + if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) + zlog_debug("add local es %s evi %u", + es->esi_str, vni); + + es_evi = bgp_evpn_es_evi_find(es, vpn); + + if (es_evi) { + if (CHECK_FLAG(es_evi->flags, BGP_EVPNES_EVI_LOCAL)) + /* dup */ + return 0; + } else { + es_evi = bgp_evpn_es_evi_new(es, vpn); + if (!es_evi) + return -1; + } + + bgp_evpn_es_evi_local_info_set(es_evi); + + /* generate an EAD-EVI for this new VNI */ + build_evpn_type1_prefix(&p, BGP_EVPN_AD_EVI_ETH_TAG, + &es->esi, es->originator_ip); + if (CHECK_FLAG(es->flags, BGP_EVPNES_ADV_EVI)) { + if (bgp_evpn_type1_route_update(bgp, es, vpn, &p)) + flog_err(EC_BGP_EVPN_ROUTE_CREATE, + "%u: EAD-EVI route creation failure for ESI %s VNI %u", + bgp->vrf_id, es->esi_str, vni); + } + + /* update EAD-ES */ + build_evpn_type1_prefix(&p, BGP_EVPN_AD_ES_ETH_TAG, + &es->esi, es->originator_ip); + if (CHECK_FLAG(es->flags, BGP_EVPNES_OPER_UP)) { + if (bgp_evpn_type1_route_update(bgp, es, NULL, &p)) + flog_err(EC_BGP_EVPN_ROUTE_CREATE, + "%u: EAD-ES route creation failure for ESI %s VNI %u", + bgp->vrf_id, es->esi_str, vni); + } + + return 0; +} + +/* Add remote ES-EVI entry. This is actually the remote VTEP add and the + * ES-EVI is implicity created on first VTEP's reference. + */ +int bgp_evpn_remote_es_evi_add(struct bgp *bgp, struct bgpevpn *vpn, + const struct prefix_evpn *p) +{ + char buf[ESI_STR_LEN]; + struct bgp_evpn_es *es; + struct bgp_evpn_es_evi *es_evi; + bool ead_es; + const esi_t *esi = &p->prefix.ead_addr.esi; + + if (!vpn) + /* local EAD-ES need not be sent back to zebra */ + return 0; + + if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) + zlog_debug("add remote %s es %s evi %u vtep %s", + p->prefix.ead_addr.eth_tag ? + "ead-es" : "ead-evi", + esi_to_str(esi, buf, + sizeof(buf)), + vpn->vni, + inet_ntoa(p->prefix.ead_addr.ip.ipaddr_v4)); + + es = bgp_evpn_es_find(esi); + if (!es) { + es = bgp_evpn_es_new(bgp, esi); + if (!es) { + flog_err(EC_BGP_ES_CREATE, + "%u: Failed to allocate ES entry for ESI %s - at remote ES Add", + bgp->vrf_id, esi_to_str(esi, buf, sizeof(buf))); + return -1; + } + } + + es_evi = bgp_evpn_es_evi_find(es, vpn); + if (!es_evi) { + es_evi = bgp_evpn_es_evi_new(es, vpn); + if (!es_evi) { + bgp_evpn_es_free(es, __func__); + return -1; + } + } + + ead_es = !!p->prefix.ead_addr.eth_tag; + bgp_evpn_es_evi_vtep_add(bgp, es_evi, p->prefix.ead_addr.ip.ipaddr_v4, + ead_es); + + bgp_evpn_es_evi_remote_info_re_eval(es_evi); + return 0; +} + +/* A remote VTEP has withdrawn. The es-evi-vtep will be deleted and the + * parent es-evi freed up implicitly in last VTEP's deref. + */ +int bgp_evpn_remote_es_evi_del(struct bgp *bgp, struct bgpevpn *vpn, + const struct prefix_evpn *p) +{ + char buf[ESI_STR_LEN]; + struct bgp_evpn_es *es; + struct bgp_evpn_es_evi *es_evi; + bool ead_es; + + if (!vpn) + /* local EAD-ES need not be sent back to zebra */ + return 0; + + if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) + zlog_debug("del remote %s es %s evi %u vtep %s", + p->prefix.ead_addr.eth_tag ? + "ead-es" : "ead-evi", + esi_to_str(&p->prefix.ead_addr.esi, buf, + sizeof(buf)), + vpn->vni, + inet_ntoa(p->prefix.ead_addr.ip.ipaddr_v4)); + + es = bgp_evpn_es_find(&p->prefix.ead_addr.esi); + if (!es) + /* XXX - error logs */ + return 0; + es_evi = bgp_evpn_es_evi_find(es, vpn); + if (!es_evi) + /* XXX - error logs */ + return 0; + + ead_es = !!p->prefix.ead_addr.eth_tag; + bgp_evpn_es_evi_vtep_del(bgp, es_evi, p->prefix.ead_addr.ip.ipaddr_v4, + ead_es); + bgp_evpn_es_evi_remote_info_re_eval(es_evi); + return 0; +} + +/* Initialize the ES tables maintained per-L2_VNI */ +void bgp_evpn_vni_es_init(struct bgpevpn *vpn) +{ + /* Initialize the ES-EVI RB tree */ + RB_INIT(bgp_es_evi_rb_head, &vpn->es_evi_rb_tree); + + /* Initialize the local list maintained for quick walks by type */ + vpn->local_es_evi_list = list_new(); + listset_app_node_mem(vpn->local_es_evi_list); +} + +/* Cleanup the ES info maintained per-L2_VNI */ +void bgp_evpn_vni_es_cleanup(struct bgpevpn *vpn) +{ + struct bgp_evpn_es_evi *es_evi; + struct bgp_evpn_es_evi *es_evi_next; + + RB_FOREACH_SAFE(es_evi, bgp_es_evi_rb_head, + &vpn->es_evi_rb_tree, es_evi_next) { + bgp_evpn_local_es_evi_do_del(es_evi); + } + + list_delete(&vpn->local_es_evi_list); +} + +static char *bgp_evpn_es_evi_vteps_str(char *vtep_str, + struct bgp_evpn_es_evi *es_evi, + uint8_t vtep_str_size) +{ + char vtep_flag_str[BGP_EVPN_FLAG_STR_SZ]; + struct listnode *node; + struct bgp_evpn_es_evi_vtep *evi_vtep; + bool first = true; + + vtep_str[0] = '\0'; + for (ALL_LIST_ELEMENTS_RO(es_evi->es_evi_vtep_list, node, evi_vtep)) { + vtep_flag_str[0] = '\0'; + if (evi_vtep->flags & BGP_EVPN_EVI_VTEP_EAD_PER_ES) + strlcat(vtep_flag_str, "E", sizeof(vtep_flag_str)); + if (evi_vtep->flags & BGP_EVPN_EVI_VTEP_EAD_PER_EVI) + strlcat(vtep_flag_str, "V", sizeof(vtep_flag_str)); + + if (!strnlen(vtep_flag_str, sizeof(vtep_flag_str))) + strlcpy(vtep_flag_str, "-", sizeof(vtep_flag_str)); + if (first) + first = false; + else + strlcat(vtep_str, ",", vtep_str_size); + strlcat(vtep_str, inet_ntoa(evi_vtep->vtep_ip), vtep_str_size); + strlcat(vtep_str, "(", vtep_str_size); + strlcat(vtep_str, vtep_flag_str, vtep_str_size); + strlcat(vtep_str, ")", vtep_str_size); + } + + return vtep_str; +} + +static void bgp_evpn_es_evi_json_vtep_fill(json_object *json_vteps, + struct bgp_evpn_es_evi_vtep *evi_vtep) +{ + json_object *json_vtep_entry; + json_object *json_flags; + + json_vtep_entry = json_object_new_object(); + + json_object_string_add(json_vtep_entry, + "vtep_ip", + inet_ntoa(evi_vtep->vtep_ip)); + if (evi_vtep->flags & (BGP_EVPN_EVI_VTEP_EAD_PER_ES | + BGP_EVPN_EVI_VTEP_EAD_PER_EVI)) { + json_flags = json_object_new_array(); + if (evi_vtep->flags & BGP_EVPN_EVI_VTEP_EAD_PER_ES) + json_array_string_add(json_flags, "ead-per-es"); + if (evi_vtep->flags & BGP_EVPN_EVI_VTEP_EAD_PER_EVI) + json_array_string_add(json_flags, "ed-per-evi"); + json_object_object_add(json_vtep_entry, + "flags", json_flags); + } + + json_object_array_add(json_vteps, + json_vtep_entry); +} + +static void bgp_evpn_es_evi_show_entry(struct vty *vty, + struct bgp_evpn_es_evi *es_evi, json_object *json) +{ + struct listnode *node; + struct bgp_evpn_es_evi_vtep *evi_vtep; + + if (json) { + json_object *json_vteps; + json_object *json_types; + + json_object_string_add(json, "esi", es_evi->es->esi_str); + json_object_int_add(json, "vni", es_evi->vpn->vni); + + if (es_evi->flags & (BGP_EVPNES_EVI_LOCAL | + BGP_EVPNES_EVI_REMOTE)) { + json_types = json_object_new_array(); + if (es_evi->flags & BGP_EVPNES_EVI_LOCAL) + json_array_string_add(json_types, "local"); + if (es_evi->flags & BGP_EVPNES_EVI_REMOTE) + json_array_string_add(json_types, "remote"); + json_object_object_add(json, "type", json_types); + } + + if (listcount(es_evi->es_evi_vtep_list)) { + json_vteps = json_object_new_array(); + for (ALL_LIST_ELEMENTS_RO(es_evi->es_evi_vtep_list, + node, evi_vtep)) { + bgp_evpn_es_evi_json_vtep_fill(json_vteps, + evi_vtep); + } + json_object_object_add(json, "vteps", json_vteps); + } + } else { + char type_str[4]; + char vtep_str[ES_VTEP_LIST_STR_SZ + BGP_EVPN_VTEPS_FLAG_STR_SZ]; + + type_str[0] = '\0'; + if (es_evi->flags & BGP_EVPNES_EVI_LOCAL) + strlcat(type_str, "L", sizeof(type_str)); + if (es_evi->flags & BGP_EVPNES_EVI_REMOTE) + strlcat(type_str, "R", sizeof(type_str)); + if (es_evi->flags & BGP_EVPNES_EVI_INCONS_VTEP_LIST) + strlcat(type_str, "I", sizeof(type_str)); + + bgp_evpn_es_evi_vteps_str(vtep_str, es_evi, sizeof(vtep_str)); + + vty_out(vty, "%-8d %-30s %-5s %s\n", + es_evi->vpn->vni, es_evi->es->esi_str, + type_str, vtep_str); + } +} + +static void bgp_evpn_es_evi_show_entry_detail(struct vty *vty, + struct bgp_evpn_es_evi *es_evi, json_object *json) +{ + if (json) { + json_object *json_flags; + + /* Add the "brief" info first */ + bgp_evpn_es_evi_show_entry(vty, es_evi, json); + if (es_evi->flags & BGP_EVPNES_EVI_INCONS_VTEP_LIST) { + json_flags = json_object_new_array(); + json_array_string_add(json_flags, "es-vtep-mismatch"); + json_object_object_add(json, "flags", json_flags); + } + } else { + char vtep_str[ES_VTEP_LIST_STR_SZ + BGP_EVPN_VTEPS_FLAG_STR_SZ]; + char type_str[4]; + + type_str[0] = '\0'; + if (es_evi->flags & BGP_EVPNES_EVI_LOCAL) + strlcat(type_str, "L", sizeof(type_str)); + if (es_evi->flags & BGP_EVPNES_EVI_REMOTE) + strlcat(type_str, "R", sizeof(type_str)); + + bgp_evpn_es_evi_vteps_str(vtep_str, es_evi, sizeof(vtep_str)); + if (!strlen(vtep_str)) + strlcpy(vtep_str, "-", sizeof(type_str)); + + vty_out(vty, "VNI: %d ESI: %s\n", + es_evi->vpn->vni, es_evi->es->esi_str); + vty_out(vty, " Type: %s\n", type_str); + vty_out(vty, " Inconsistencies: %s\n", + (es_evi->flags & BGP_EVPNES_EVI_INCONS_VTEP_LIST) ? + "es-vtep-mismatch":"-"); + vty_out(vty, " VTEPs: %s\n", vtep_str); + vty_out(vty, "\n"); + } +} + +static void bgp_evpn_es_evi_show_one_vni(struct bgpevpn *vpn, struct vty *vty, + json_object *json_array, bool detail) +{ + struct bgp_evpn_es_evi *es_evi; + json_object *json = NULL; + + RB_FOREACH(es_evi, bgp_es_evi_rb_head, &vpn->es_evi_rb_tree) { + if (json_array) + /* create a separate json object for each ES */ + json = json_object_new_object(); + if (detail) + bgp_evpn_es_evi_show_entry_detail(vty, es_evi, json); + else + bgp_evpn_es_evi_show_entry(vty, es_evi, json); + /* add ES to the json array */ + if (json_array) + json_object_array_add(json_array, json); + } +} + +struct es_evi_show_ctx { + struct vty *vty; + json_object *json; + int detail; +}; + +static void bgp_evpn_es_evi_show_one_vni_hash_cb(struct hash_bucket *bucket, + void *ctxt) +{ + struct bgpevpn *vpn = (struct bgpevpn *)bucket->data; + struct es_evi_show_ctx *wctx = (struct es_evi_show_ctx *)ctxt; + + bgp_evpn_es_evi_show_one_vni(vpn, wctx->vty, wctx->json, wctx->detail); +} + +/* Display all ES EVIs */ +void bgp_evpn_es_evi_show(struct vty *vty, bool uj, bool detail) +{ + json_object *json_array = NULL; + struct es_evi_show_ctx wctx; + struct bgp *bgp; + + if (uj) { + /* create an array of ES-EVIs */ + json_array = json_object_new_array(); + } + + wctx.vty = vty; + wctx.json = json_array; + wctx.detail = detail; + + bgp = bgp_get_evpn(); + + if (!json_array && !detail) { + vty_out(vty, "Flags: L local, R remote, I inconsistent\n"); + vty_out(vty, "VTEP-Flags: E EAD-per-ES, V EAD-per-EVI\n"); + vty_out(vty, "%-8s %-30s %-5s %s\n", + "VNI", "ESI", "Flags", "VTEPs"); + } + + if (bgp) + hash_iterate(bgp->vnihash, + (void (*)(struct hash_bucket *, + void *))bgp_evpn_es_evi_show_one_vni_hash_cb, + &wctx); + if (uj) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json_array, JSON_C_TO_STRING_PRETTY)); + json_object_free(json_array); + } +} + +/* Display specific ES EVI */ +void bgp_evpn_es_evi_show_vni(struct vty *vty, vni_t vni, + bool uj, bool detail) +{ + struct bgpevpn *vpn = NULL; + json_object *json_array = NULL; + struct bgp *bgp; + + if (uj) { + /* create an array of ES-EVIs */ + json_array = json_object_new_array(); + } + + bgp = bgp_get_evpn(); + if (bgp) + vpn = bgp_evpn_lookup_vni(bgp, vni); + + if (vpn) { + if (!json_array && !detail) { + vty_out(vty, "Flags: L local, R remote, I inconsistent\n"); + vty_out(vty, "VTEP-Flags: E EAD-per-ES, V EAD-per-EVI\n"); + vty_out(vty, "%-8s %-30s %-5s %s\n", + "VNI", "ESI", "Flags", "VTEPs"); + } + + bgp_evpn_es_evi_show_one_vni(vpn, vty, json_array, detail); + } else { + if (!uj) + vty_out(vty, "VNI not found\n"); + } + + if (uj) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json_array, JSON_C_TO_STRING_PRETTY)); + json_object_free(json_array); + } +} + +/***************************************************************************** + * Ethernet Segment Consistency checks + * Consistency checking is done to detect misconfig or mis-cabling. When + * an inconsistency is detected it is simply logged (and displayed via + * show commands) at this point. A more drastic action can be executed (based + * on user config) in the future. + */ +/* queue up the es for background consistency checks */ +static void bgp_evpn_es_cons_checks_pend_add(struct bgp_evpn_es *es) +{ + if (!bgp_mh_info->consistency_checking) + /* consistency checking is not enabled */ + return; + + if (CHECK_FLAG(es->flags, BGP_EVPNES_CONS_CHECK_PEND)) + /* already queued for consistency checking */ + return; + + SET_FLAG(es->flags, BGP_EVPNES_CONS_CHECK_PEND); + listnode_init(&es->pend_es_listnode, es); + listnode_add_after(bgp_mh_info->pend_es_list, + listtail_unchecked(bgp_mh_info->pend_es_list), + &es->pend_es_listnode); +} + +/* pull the ES from the consistency check list */ +static void bgp_evpn_es_cons_checks_pend_del(struct bgp_evpn_es *es) +{ + if (!CHECK_FLAG(es->flags, BGP_EVPNES_CONS_CHECK_PEND)) + return; + + UNSET_FLAG(es->flags, BGP_EVPNES_CONS_CHECK_PEND); + list_delete_node(bgp_mh_info->pend_es_list, + &es->pend_es_listnode); +} + +/* Number of active VTEPs associated with the ES-per-EVI */ +static uint32_t bgp_evpn_es_evi_get_active_vtep_cnt( + struct bgp_evpn_es_evi *es_evi) +{ + struct bgp_evpn_es_evi_vtep *evi_vtep; + struct listnode *node; + uint32_t vtep_cnt = 0; + + for (ALL_LIST_ELEMENTS_RO(es_evi->es_evi_vtep_list, node, evi_vtep)) { + if (CHECK_FLAG(evi_vtep->flags, BGP_EVPN_EVI_VTEP_ACTIVE)) + ++vtep_cnt; + } + + return vtep_cnt; +} + +/* Number of active VTEPs associated with the ES */ +static uint32_t bgp_evpn_es_get_active_vtep_cnt(struct bgp_evpn_es *es) +{ + struct listnode *node; + uint32_t vtep_cnt = 0; + struct bgp_evpn_es_vtep *es_vtep; + + for (ALL_LIST_ELEMENTS_RO(es->es_vtep_list, node, es_vtep)) { + if (CHECK_FLAG(es_vtep->flags, BGP_EVPNES_VTEP_ACTIVE)) + ++vtep_cnt; + } + + return vtep_cnt; +} + +static struct bgp_evpn_es_vtep *bgp_evpn_es_get_next_active_vtep( + struct bgp_evpn_es *es, struct bgp_evpn_es_vtep *es_vtep) +{ + struct listnode *node; + struct bgp_evpn_es_vtep *next_es_vtep; + + if (es_vtep) + node = listnextnode_unchecked(&es_vtep->es_listnode); + else + node = listhead(es->es_vtep_list); + + for (; node; node = listnextnode_unchecked(node)) { + next_es_vtep = listgetdata(node); + if (CHECK_FLAG(next_es_vtep->flags, BGP_EVPNES_VTEP_ACTIVE)) + return next_es_vtep; + } + + return NULL; +} + +static struct bgp_evpn_es_evi_vtep *bgp_evpn_es_evi_get_next_active_vtep( + struct bgp_evpn_es_evi *es_evi, + struct bgp_evpn_es_evi_vtep *evi_vtep) +{ + struct listnode *node; + struct bgp_evpn_es_evi_vtep *next_evi_vtep; + + if (evi_vtep) + node = listnextnode_unchecked(&evi_vtep->es_evi_listnode); + else + node = listhead(es_evi->es_evi_vtep_list); + + for (; node; node = listnextnode_unchecked(node)) { + next_evi_vtep = listgetdata(node); + if (CHECK_FLAG(next_evi_vtep->flags, BGP_EVPN_EVI_VTEP_ACTIVE)) + return next_evi_vtep; + } + + return NULL; +} + +static void bgp_evpn_es_evi_set_inconsistent(struct bgp_evpn_es_evi *es_evi) +{ + if (!CHECK_FLAG(es_evi->flags, BGP_EVPNES_EVI_INCONS_VTEP_LIST)) { + if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) + zlog_debug("inconsistency detected - es %s evi %u vtep list mismatch", + es_evi->es->esi_str, + es_evi->vpn->vni); + SET_FLAG(es_evi->flags, BGP_EVPNES_EVI_INCONS_VTEP_LIST); + + /* update parent ES with the incosistency setting */ + if (!es_evi->es->incons_evi_vtep_cnt && + BGP_DEBUG(evpn_mh, EVPN_MH_ES)) + zlog_debug("inconsistency detected - es %s vtep list mismatch", + es_evi->es->esi_str); + ++es_evi->es->incons_evi_vtep_cnt; + SET_FLAG(es_evi->es->inconsistencies, + BGP_EVPNES_INCONS_VTEP_LIST); + } +} + +static uint32_t bgp_evpn_es_run_consistency_checks(struct bgp_evpn_es *es) +{ + int proc_cnt = 0; + int es_active_vtep_cnt; + int evi_active_vtep_cnt; + struct bgp_evpn_es_evi *es_evi; + struct listnode *evi_node; + struct bgp_evpn_es_vtep *es_vtep; + struct bgp_evpn_es_evi_vtep *evi_vtep; + + /* reset the inconsistencies and re-evaluate */ + es->incons_evi_vtep_cnt = 0; + es->inconsistencies = 0; + + es_active_vtep_cnt = bgp_evpn_es_get_active_vtep_cnt(es); + for (ALL_LIST_ELEMENTS_RO(es->es_evi_list, + evi_node, es_evi)) { + ++proc_cnt; + + /* reset the inconsistencies on the EVI and re-evaluate*/ + UNSET_FLAG(es_evi->flags, BGP_EVPNES_EVI_INCONS_VTEP_LIST); + + evi_active_vtep_cnt = + bgp_evpn_es_evi_get_active_vtep_cnt(es_evi); + if (es_active_vtep_cnt != evi_active_vtep_cnt) { + bgp_evpn_es_evi_set_inconsistent(es_evi); + continue; + } + + if (!es_active_vtep_cnt) + continue; + + es_vtep = NULL; + evi_vtep = NULL; + while ((es_vtep = bgp_evpn_es_get_next_active_vtep( + es, es_vtep))) { + evi_vtep = bgp_evpn_es_evi_get_next_active_vtep(es_evi, + evi_vtep); + if (!evi_vtep) { + bgp_evpn_es_evi_set_inconsistent(es_evi); + break; + } + if (es_vtep->vtep_ip.s_addr != + evi_vtep->vtep_ip.s_addr) { + /* inconsistency detected; set it and move + * to the next evi + */ + bgp_evpn_es_evi_set_inconsistent(es_evi); + break; + } + } + } + + return proc_cnt; +} + +static int bgp_evpn_run_consistency_checks(struct thread *t) +{ + int proc_cnt = 0; + int es_cnt = 0; + struct listnode *node; + struct listnode *nextnode; + struct bgp_evpn_es *es; + + for (ALL_LIST_ELEMENTS(bgp_mh_info->pend_es_list, + node, nextnode, es)) { + ++es_cnt; + ++proc_cnt; + /* run consistency checks on the ES and remove it from the + * pending list + */ + proc_cnt += bgp_evpn_es_run_consistency_checks(es); + bgp_evpn_es_cons_checks_pend_del(es); + if (proc_cnt > 500) + break; + } + + /* restart the timer */ + thread_add_timer(bm->master, bgp_evpn_run_consistency_checks, NULL, + BGP_EVPN_CONS_CHECK_INTERVAL, + &bgp_mh_info->t_cons_check); + + return 0; +} + +/*****************************************************************************/ +void bgp_evpn_mh_init(void) +{ + bm->mh_info = XCALLOC(MTYPE_BGP_EVPN_MH_INFO, sizeof(*bm->mh_info)); + + /* setup ES tables */ + RB_INIT(bgp_es_rb_head, &bgp_mh_info->es_rb_tree); + /* local ES list */ + bgp_mh_info->local_es_list = list_new(); + listset_app_node_mem(bgp_mh_info->local_es_list); + /* list of ESs with pending processing */ + bgp_mh_info->pend_es_list = list_new(); + listset_app_node_mem(bgp_mh_info->pend_es_list); + + /* config knobs - XXX add cli to control it */ + bgp_mh_info->ead_evi_adv_for_down_links = true; + bgp_mh_info->consistency_checking = true; + + if (bgp_mh_info->consistency_checking) + thread_add_timer(bm->master, bgp_evpn_run_consistency_checks, + NULL, BGP_EVPN_CONS_CHECK_INTERVAL, + &bgp_mh_info->t_cons_check); + + memset(&zero_esi_buf, 0, sizeof(esi_t)); +} + +void bgp_evpn_mh_finish(void) +{ + struct bgp_evpn_es *es; + struct bgp_evpn_es *es_next; + + if (BGP_DEBUG(evpn_mh, EVPN_MH_RT)) + zlog_debug("evpn mh finish"); + + RB_FOREACH_SAFE (es, bgp_es_rb_head, &bgp_mh_info->es_rb_tree, + es_next) { + bgp_evpn_es_local_info_clear(es); + } + thread_cancel(bgp_mh_info->t_cons_check); + list_delete(&bgp_mh_info->local_es_list); + list_delete(&bgp_mh_info->pend_es_list); + + XFREE(MTYPE_BGP_EVPN_MH_INFO, bgp_mh_info); +} diff --git a/bgpd/bgp_evpn_mh.h b/bgpd/bgp_evpn_mh.h new file mode 100644 index 0000000000..93355d495a --- /dev/null +++ b/bgpd/bgp_evpn_mh.h @@ -0,0 +1,299 @@ +/* EVPN header for multihoming procedures + * + * Copyright (C) 2019 Cumulus Networks + * Anuradha Karuppiah + * + * This file is part of FRRouting. + * + * FRRouting is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRRouting is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + */ + +#ifndef _FRR_BGP_EVPN_MH_H +#define _FRR_BGP_EVPN_MH_H + +#include "vxlan.h" +#include "bgpd.h" +#include "bgp_evpn.h" +#include "bgp_evpn_private.h" + +#define BGP_EVPN_AD_ES_ETH_TAG 0xffffffff +#define BGP_EVPN_AD_EVI_ETH_TAG 0 + +#define BGP_EVPNES_INCONS_STR_SZ 80 +#define BGP_EVPN_FLAG_STR_SZ 5 +#define BGP_EVPN_VTEPS_FLAG_STR_SZ (BGP_EVPN_FLAG_STR_SZ * ES_VTEP_MAX_CNT) + +#define BGP_EVPN_CONS_CHECK_INTERVAL 60 + + +/* Ethernet Segment entry - + * - Local and remote ESs are maintained in a global RB tree, + * bgp_mh_info->es_rb_tree using ESI as key + * - Local ESs are received from zebra (BGP_EVPNES_LOCAL) + * - Remotes ESs are implicitly created (by reference) by a remote ES-EVI + * (BGP_EVPNES_REMOTE) + * - An ES can be simulatenously LOCAL and REMOTE; infact all LOCAL ESs are + * expected to have REMOTE ES peers. + */ +struct bgp_evpn_es { + /* Ethernet Segment Identifier */ + esi_t esi; + char esi_str[ESI_STR_LEN]; + + /* es flags */ + uint32_t flags; + /* created via zebra config */ +#define BGP_EVPNES_LOCAL (1 << 0) + /* created implicitly by a remote ES-EVI reference */ +#define BGP_EVPNES_REMOTE (1 << 1) + /* local ES link is oper-up */ +#define BGP_EVPNES_OPER_UP (1 << 2) + /* enable generation of EAD-EVI routes */ +#define BGP_EVPNES_ADV_EVI (1 << 3) + /* consistency checks pending */ +#define BGP_EVPNES_CONS_CHECK_PEND (1 << 4) + + /* memory used for adding the es to bgp->es_rb_tree */ + RB_ENTRY(bgp_evpn_es) rb_node; + + /* [EVPNES_LOCAL] memory used for linking the es to + * bgp_mh_info->local_es_list + */ + struct listnode es_listnode; + + /* memory used for linking the es to "processing" pending list + * bgp_mh_info->pend_es_list + */ + struct listnode pend_es_listnode; + + /* [EVPNES_LOCAL] Id for deriving the RD automatically for this ESI */ + uint16_t rd_id; + + /* [EVPNES_LOCAL] RD for this ES */ + struct prefix_rd prd; + + /* [EVPNES_LOCAL] originator ip address */ + struct in_addr originator_ip; + + /* [EVPNES_LOCAL] Route table for EVPN routes for this ESI- + * - Type-4 local and remote routes + * - Type-1 local routes + */ + struct bgp_table *route_table; + + /* list of PEs (bgp_evpn_es_vtep) attached to the ES */ + struct list *es_vtep_list; + + /* List of ES-EVIs associated with this ES */ + struct list *es_evi_list; + + /* Number of remote VNIs referencing this ES */ + uint32_t remote_es_evi_cnt; + + uint32_t inconsistencies; + /* there are one or more EVIs whose VTEP list doesn't match + * with the ES's VTEP list + */ +#define BGP_EVPNES_INCONS_VTEP_LIST (1 << 0) + + /* number of es-evi entries whose VTEP list doesn't match + * with the ES's + */ + uint32_t incons_evi_vtep_cnt; + + QOBJ_FIELDS +}; +DECLARE_QOBJ_TYPE(bgp_evpn_es) +RB_HEAD(bgp_es_rb_head, bgp_evpn_es); +RB_PROTOTYPE(bgp_es_rb_head, bgp_evpn_es, rb_node, bgp_es_rb_cmp); + +/* PE attached to an ES */ +struct bgp_evpn_es_vtep { + struct bgp_evpn_es *es; /* parent ES */ + struct in_addr vtep_ip; + + uint32_t flags; + /* Rxed a Type4 route from this PE */ +#define BGP_EVPNES_VTEP_ESR (1 << 0) + /* Active (rxed EAD-ES and EAD-EVI) and can be included as + * a nexthop + */ +#define BGP_EVPNES_VTEP_ACTIVE (1 << 1) + + uint32_t evi_cnt; /* es_evis referencing this vtep as an active path */ + + /* memory used for adding the entry to es->es_vtep_list */ + struct listnode es_listnode; +}; + +/* ES per-EVI info + * - ES-EVIs are maintained per-L2-VNI (vpn->es_evi_rb_tree) + * - ES-EVIs are also linked to the parent ES (es->es_evi_list) + * - Local ES-EVIs are created by zebra (via config). They are linked to a + * per-VNI list (vpn->local_es_evi_list) for quick access + * - Remote ES-EVIs are created implicitly when a bgp_evpn_es_evi_vtep + * references it. + */ +struct bgp_evpn_es_evi { + struct bgp_evpn_es *es; + struct bgpevpn *vpn; + + /* ES-EVI flags */ + uint32_t flags; +/* local ES-EVI, created by zebra */ +#define BGP_EVPNES_EVI_LOCAL (1 << 0) +/* created via a remote VTEP imported by BGP */ +#define BGP_EVPNES_EVI_REMOTE (1 << 1) +#define BGP_EVPNES_EVI_INCONS_VTEP_LIST (1 << 2) + + /* memory used for adding the es_evi to es_evi->vpn->es_evi_rb_tree */ + RB_ENTRY(bgp_evpn_es_evi) rb_node; + /* memory used for linking the es_evi to + * es_evi->vpn->local_es_evi_list + */ + struct listnode l2vni_listnode; + /* memory used for linking the es_evi to + * es_evi->es->es_evi_list + */ + struct listnode es_listnode; + + /* list of PEs (bgp_evpn_es_evi_vtep) attached to the ES for this VNI */ + struct list *es_evi_vtep_list; +}; + +/* PE attached to an ES for a VNI. This entry is created when an EAD-per-ES + * or EAD-per-EVI Type1 route is imported into the VNI. + */ +struct bgp_evpn_es_evi_vtep { + struct bgp_evpn_es_evi *es_evi; /* parent ES-EVI */ + struct in_addr vtep_ip; + + uint32_t flags; + /* Rxed an EAD-per-ES route from the PE */ +#define BGP_EVPN_EVI_VTEP_EAD_PER_ES (1 << 0) /* rxed EAD-per-ES */ + /* Rxed an EAD-per-EVI route from the PE */ +#define BGP_EVPN_EVI_VTEP_EAD_PER_EVI (1 << 1) /* rxed EAD-per-EVI */ + /* VTEP is active i.e. will result in the creation of an es-vtep */ +#define BGP_EVPN_EVI_VTEP_ACTIVE (1 << 2) +#define BGP_EVPN_EVI_VTEP_EAD (BGP_EVPN_EVI_VTEP_EAD_PER_ES |\ + BGP_EVPN_EVI_VTEP_EAD_PER_EVI) + + /* memory used for adding the entry to es_evi->es_evi_vtep_list */ + struct listnode es_evi_listnode; + struct bgp_evpn_es_vtep *es_vtep; +}; + +/* multihoming information stored in bgp_master */ +#define bgp_mh_info (bm->mh_info) +struct bgp_evpn_mh_info { + /* RB tree of Ethernet segments (used for EVPN-MH) */ + struct bgp_es_rb_head es_rb_tree; + /* List of local ESs */ + struct list *local_es_list; + /* List of ESs with pending/periodic processing */ + struct list *pend_es_list; + /* periodic timer for running background consistency checks */ + struct thread *t_cons_check; + + /* config knobs for optimizing or interop */ + /* Generate EAD-EVI routes even if the ES is oper-down. This can be + * enabled as an optimization to avoid a storm of updates when an ES + * link flaps. + */ + bool ead_evi_adv_for_down_links; + /* Enable ES consistency checking */ + bool consistency_checking; +}; + +/****************************************************************************/ +static inline int bgp_evpn_is_es_local(struct bgp_evpn_es *es) +{ + return CHECK_FLAG(es->flags, BGP_EVPNES_LOCAL) ? 1 : 0; +} + +extern esi_t *zero_esi; +static inline bool bgp_evpn_is_esi_valid(esi_t *esi) +{ + return !!memcmp(esi, zero_esi, sizeof(esi_t)); +} + +static inline esi_t *bgp_evpn_attr_get_esi(struct attr *attr) +{ + return attr ? &attr->esi : zero_esi; +} + +static inline bool bgp_evpn_attr_is_sync(struct attr *attr) +{ + return attr ? !!(attr->es_flags & + (ATTR_ES_PEER_PROXY | ATTR_ES_PEER_ACTIVE)) : false; +} + +static inline uint32_t bgp_evpn_attr_get_sync_seq(struct attr *attr) +{ + return attr ? attr->mm_sync_seqnum : 0; +} + +static inline bool bgp_evpn_attr_is_active_on_peer(struct attr *attr) +{ + return attr ? + !!(attr->es_flags & ATTR_ES_PEER_ACTIVE) : false; +} + +static inline bool bgp_evpn_attr_is_router_on_peer(struct attr *attr) +{ + return attr ? + !!(attr->es_flags & ATTR_ES_PEER_ROUTER) : false; +} + +static inline bool bgp_evpn_attr_is_proxy(struct attr *attr) +{ + return attr ? !!(attr->es_flags & ATTR_ES_PROXY_ADVERT) : false; +} + +static inline bool bgp_evpn_attr_is_local_es(struct attr *attr) +{ + return attr ? !!(attr->es_flags & ATTR_ES_IS_LOCAL) : false; +} + +/****************************************************************************/ +extern int bgp_evpn_es_route_install_uninstall(struct bgp *bgp, + struct bgp_evpn_es *es, afi_t afi, safi_t safi, + struct prefix_evpn *evp, struct bgp_path_info *pi, + int install); +int bgp_evpn_type1_route_process(struct peer *peer, afi_t afi, safi_t safi, + struct attr *attr, uint8_t *pfx, int psize, + uint32_t addpath_id); +int bgp_evpn_type4_route_process(struct peer *peer, afi_t afi, safi_t safi, + struct attr *attr, uint8_t *pfx, int psize, + uint32_t addpath_id); +extern int bgp_evpn_local_es_add(struct bgp *bgp, esi_t *esi, + struct in_addr originator_ip, bool oper_up); +extern int bgp_evpn_local_es_del(struct bgp *bgp, esi_t *esi); +extern int bgp_evpn_local_es_evi_add(struct bgp *bgp, esi_t *esi, vni_t vni); +extern int bgp_evpn_local_es_evi_del(struct bgp *bgp, esi_t *esi, vni_t vni); +extern int bgp_evpn_remote_es_evi_add(struct bgp *bgp, struct bgpevpn *vpn, + const struct prefix_evpn *p); +extern int bgp_evpn_remote_es_evi_del(struct bgp *bgp, struct bgpevpn *vpn, + const struct prefix_evpn *p); +extern void bgp_evpn_mh_init(void); +extern void bgp_evpn_mh_finish(void); +void bgp_evpn_vni_es_init(struct bgpevpn *vpn); +void bgp_evpn_vni_es_cleanup(struct bgpevpn *vpn); +void bgp_evpn_es_show_esi(struct vty *vty, esi_t *esi, bool uj); +void bgp_evpn_es_show(struct vty *vty, bool uj, bool detail); +void bgp_evpn_es_evi_show_vni(struct vty *vty, vni_t vni, + bool uj, bool detail); +void bgp_evpn_es_evi_show(struct vty *vty, bool uj, bool detail); +struct bgp_evpn_es *bgp_evpn_es_find(const esi_t *esi); +extern bool bgp_evpn_is_esi_local(esi_t *esi); + +#endif /* _FRR_BGP_EVPN_MH_H */ diff --git a/bgpd/bgp_evpn_private.h b/bgpd/bgp_evpn_private.h index a5a091242f..ca45b198a7 100644 --- a/bgpd/bgp_evpn_private.h +++ b/bgpd/bgp_evpn_private.h @@ -34,15 +34,23 @@ * in bits */ #define EVPN_ROUTE_PREFIXLEN (sizeof(struct evpn_addr) * 8) -/* EVPN route types. */ -typedef enum { - BGP_EVPN_AD_ROUTE = 1, /* Ethernet Auto-Discovery (A-D) route */ - BGP_EVPN_MAC_IP_ROUTE, /* MAC/IP Advertisement route */ - BGP_EVPN_IMET_ROUTE, /* Inclusive Multicast Ethernet Tag route */ - BGP_EVPN_ES_ROUTE, /* Ethernet Segment route */ - BGP_EVPN_IP_PREFIX_ROUTE, /* IP Prefix route */ -} bgp_evpn_route_type; +/* EVPN route RD buffer length */ +#define BGP_EVPN_PREFIX_RD_LEN 100 +/* packet sizes for EVPN routes */ +/* Type-1 route should be 25 bytes + * RD (8), ESI (10), eth-tag (4), vni (3) + */ +#define BGP_EVPN_TYPE1_PSIZE 25 +/* Type-4 route should be either 23 or 35 bytes + * RD (8), ESI (10), ip-len (1), ip (4 or 16) + */ +#define BGP_EVPN_TYPE4_V4_PSIZE 23 +#define BGP_EVPN_TYPE4_V6_PSIZE 34 + +RB_HEAD(bgp_es_evi_rb_head, bgp_evpn_es_evi); +RB_PROTOTYPE(bgp_es_evi_rb_head, bgp_evpn_es_evi, rb_node, + bgp_es_evi_rb_cmp); /* * Hash table of EVIs. Right now, the only type of EVI supported is with * VxLAN encapsulation, hence each EVI corresponds to a L2 VNI. @@ -98,46 +106,16 @@ struct bgpevpn { * this VNI. */ struct bgp_table *route_table; - QOBJ_FIELDS -}; - -DECLARE_QOBJ_TYPE(bgpevpn) - -struct evpnes { - - /* Ethernet Segment Identifier */ - esi_t esi; + /* RB tree of ES-EVIs */ + struct bgp_es_evi_rb_head es_evi_rb_tree; - /* es flags */ - uint16_t flags; -#define EVPNES_LOCAL 0x01 -#define EVPNES_REMOTE 0x02 - - /* - * Id for deriving the RD - * automatically for this ESI - */ - uint16_t rd_id; - - /* RD for this VNI. */ - struct prefix_rd prd; - - /* originator ip address */ - struct ipaddr originator_ip; - - /* list of VTEPs in the same site */ - struct list *vtep_list; - - /* - * Route table for EVPN routes for - * this ESI. - type4 routes - */ - struct bgp_table *route_table; + /* List of local ESs */ + struct list *local_es_evi_list; QOBJ_FIELDS }; -DECLARE_QOBJ_TYPE(evpnes) +DECLARE_QOBJ_TYPE(bgpevpn) /* Mapping of Import RT to VNIs. * The Import RTs of all VNIs are maintained in a hash table with each @@ -188,6 +166,16 @@ struct bgp_evpn_info { /* EVPN enable - advertise svi macip routes */ int advertise_svi_macip; + /* PIP feature knob */ + bool advertise_pip; + /* PIP IP (sys ip) */ + struct in_addr pip_ip; + struct in_addr pip_ip_static; + /* PIP MAC (sys MAC) */ + struct ethaddr pip_rmac; + struct ethaddr pip_rmac_static; + struct ethaddr pip_rmac_zebra; + bool is_anycast_mac; }; static inline int is_vrf_rd_configured(struct bgp *bgp_vrf) @@ -260,8 +248,12 @@ static inline void bgpevpn_link_to_l3vni(struct bgpevpn *vpn) vpn->bgp_vrf = bgp_lock(bgp_vrf); listnode_add_sort(bgp_vrf->l2vnis, vpn); - /* check if we are advertising two labels for this vpn */ - if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY)) + /* + * If L3VNI is configured, + * check if we are advertising two labels for this vpn + */ + if (bgp_vrf->l3vni && + !CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY)) SET_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS); } @@ -275,6 +267,11 @@ static inline int is_vni_live(struct bgpevpn *vpn) return (CHECK_FLAG(vpn->flags, VNI_FLAG_LIVE)); } +static inline int is_l3vni_live(struct bgp *bgp_vrf) +{ + return (bgp_vrf->l3vni && bgp_vrf->l3vni_svi_ifindex); +} + static inline int is_rd_configured(struct bgpevpn *vpn) { return (CHECK_FLAG(vpn->flags, VNI_FLAG_RD_CFGD)); @@ -311,6 +308,16 @@ static inline void encode_es_rt_extcomm(struct ecommunity_val *eval, memcpy(&eval->val[2], mac, ETH_ALEN); } +static inline void encode_esi_label_extcomm(struct ecommunity_val *eval, + bool single_active) +{ + memset(eval, 0, sizeof(struct ecommunity_val)); + eval->val[0] = ECOMMUNITY_ENCODE_EVPN; + eval->val[1] = ECOMMUNITY_EVPN_SUBTYPE_ESI_LABEL; + if (single_active) + eval->val[2] |= (1 << 0); +} + static inline void encode_rmac_extcomm(struct ecommunity_val *eval, struct ethaddr *rmac) { @@ -342,16 +349,18 @@ static inline void encode_mac_mobility_extcomm(int static_mac, uint32_t seq, } static inline void encode_na_flag_extcomm(struct ecommunity_val *eval, - uint8_t na_flag) + uint8_t na_flag, bool proxy) { memset(eval, 0, sizeof(*eval)); eval->val[0] = ECOMMUNITY_ENCODE_EVPN; eval->val[1] = ECOMMUNITY_EVPN_SUBTYPE_ND; if (na_flag) eval->val[2] |= ECOMMUNITY_EVPN_SUBTYPE_ND_ROUTER_FLAG; + if (proxy) + eval->val[2] |= ECOMMUNITY_EVPN_SUBTYPE_PROXY_FLAG; } -static inline void ip_prefix_from_type5_prefix(struct prefix_evpn *evp, +static inline void ip_prefix_from_type5_prefix(const struct prefix_evpn *evp, struct prefix *ip) { memset(ip, 0, sizeof(struct prefix)); @@ -377,7 +386,7 @@ static inline int is_evpn_prefix_default(const struct prefix *evp) 1 : 0); } -static inline void ip_prefix_from_type2_prefix(struct prefix_evpn *evp, +static inline void ip_prefix_from_type2_prefix(const struct prefix_evpn *evp, struct prefix *ip) { memset(ip, 0, sizeof(struct prefix)); @@ -394,7 +403,7 @@ static inline void ip_prefix_from_type2_prefix(struct prefix_evpn *evp, } } -static inline void ip_prefix_from_evpn_prefix(struct prefix_evpn *evp, +static inline void ip_prefix_from_evpn_prefix(const struct prefix_evpn *evp, struct prefix *ip) { if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) @@ -417,8 +426,9 @@ static inline void build_evpn_type2_prefix(struct prefix_evpn *p, memcpy(&p->prefix.macip_addr.ip, ip, sizeof(*ip)); } -static inline void build_type5_prefix_from_ip_prefix(struct prefix_evpn *evp, - struct prefix *ip_prefix) +static inline void +build_type5_prefix_from_ip_prefix(struct prefix_evpn *evp, + const struct prefix *ip_prefix) { struct ipaddr ip; @@ -467,6 +477,44 @@ static inline void build_evpn_type4_prefix(struct prefix_evpn *p, memcpy(&p->prefix.es_addr.esi, esi, sizeof(esi_t)); } +static inline void build_evpn_type1_prefix(struct prefix_evpn *p, + uint32_t eth_tag, + esi_t *esi, + struct in_addr originator_ip) +{ + memset(p, 0, sizeof(struct prefix_evpn)); + p->family = AF_EVPN; + p->prefixlen = EVPN_ROUTE_PREFIXLEN; + p->prefix.route_type = BGP_EVPN_AD_ROUTE; + p->prefix.ead_addr.eth_tag = eth_tag; + p->prefix.ead_addr.ip.ipa_type = IPADDR_V4; + p->prefix.ead_addr.ip.ipaddr_v4 = originator_ip; + memcpy(&p->prefix.ead_addr.esi, esi, sizeof(esi_t)); +} + +static inline void evpn_type1_prefix_global_copy(struct prefix_evpn *global_p, + const struct prefix_evpn *vni_p) +{ + memcpy(global_p, vni_p, sizeof(*global_p)); + global_p->prefix.ead_addr.ip.ipa_type = 0; + global_p->prefix.ead_addr.ip.ipaddr_v4.s_addr = 0; +} + +/* EAD prefix in the global table doesn't include the VTEP-IP so + * we need to create a different copy for the VNI + */ +static inline struct prefix_evpn *evpn_type1_prefix_vni_copy( + struct prefix_evpn *vni_p, + const struct prefix_evpn *global_p, + struct in_addr originator_ip) +{ + memcpy(vni_p, global_p, sizeof(*vni_p)); + vni_p->prefix.ead_addr.ip.ipa_type = IPADDR_V4; + vni_p->prefix.ead_addr.ip.ipaddr_v4 = originator_ip; + + return vni_p; +} + static inline int evpn_default_originate_set(struct bgp *bgp, afi_t afi, safi_t safi) { @@ -491,11 +539,26 @@ static inline void es_get_system_mac(esi_t *esi, memcpy(mac, &esi->val[1], ETH_ALEN); } -static inline int is_es_local(struct evpnes *es) +static inline bool bgp_evpn_is_svi_macip_enabled(struct bgpevpn *vpn) { - return CHECK_FLAG(es->flags, EVPNES_LOCAL) ? 1 : 0; + struct bgp *bgp_evpn = NULL; + + bgp_evpn = bgp_get_evpn(); + + return (bgp_evpn->evpn_info->advertise_svi_macip || + vpn->advertise_svi_macip); +} + +static inline bool bgp_evpn_is_path_local(struct bgp *bgp, + struct bgp_path_info *pi) +{ + return (pi->peer == bgp->peer_self + && pi->type == ZEBRA_ROUTE_BGP + && pi->sub_type == BGP_ROUTE_STATIC); } +extern struct zclient *zclient; + extern void bgp_evpn_install_uninstall_default_route(struct bgp *bgp_vrf, afi_t afi, safi_t safi, bool add); @@ -533,9 +596,18 @@ extern struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni, vrf_id_t tenant_vrf_id, struct in_addr mcast_grp); extern void bgp_evpn_free(struct bgp *bgp, struct bgpevpn *vpn); -extern struct evpnes *bgp_evpn_lookup_es(struct bgp *bgp, esi_t *esi); -extern struct evpnes *bgp_evpn_es_new(struct bgp *bgp, esi_t *esi, - struct ipaddr *originator_ip); -extern void bgp_evpn_es_free(struct bgp *bgp, struct evpnes *es); extern bool bgp_evpn_lookup_l3vni_l2vni_table(vni_t vni); +extern int update_routes_for_vni(struct bgp *bgp, struct bgpevpn *vpn); +extern void delete_evpn_route_entry(struct bgp *bgp, afi_t afi, safi_t safi, + struct bgp_dest *dest, + struct bgp_path_info **pi); +int vni_list_cmp(void *p1, void *p2); +extern int evpn_route_select_install(struct bgp *bgp, struct bgpevpn *vpn, + struct bgp_node *rn); +extern struct bgp_node *bgp_global_evpn_node_get( + struct bgp_table *table, afi_t afi, safi_t safi, + const struct prefix_evpn *evp, struct prefix_rd *prd); +extern struct bgp_node *bgp_global_evpn_node_lookup( + struct bgp_table *table, afi_t afi, safi_t safi, + const struct prefix_evpn *evp, struct prefix_rd *prd); #endif /* _BGP_EVPN_PRIVATE_H */ diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index 4dccc89f52..2f207f8ab8 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -33,10 +33,13 @@ #include "bgpd/bgp_evpn_vty.h" #include "bgpd/bgp_evpn.h" #include "bgpd/bgp_evpn_private.h" +#include "bgpd/bgp_evpn_mh.h" #include "bgpd/bgp_zebra.h" #include "bgpd/bgp_vty.h" #include "bgpd/bgp_errors.h" #include "bgpd/bgp_ecommunity.h" +#include "bgpd/bgp_lcommunity.h" +#include "bgpd/bgp_community.h" #define SHOW_DISPLAY_STANDARD 0 #define SHOW_DISPLAY_TAGS 1 @@ -57,7 +60,7 @@ struct vni_walk_ctx { static void display_vrf_import_rt(struct vty *vty, struct vrf_irt_node *irt, json_object *json) { - uint8_t *pnt; + const uint8_t *pnt; uint8_t type, sub_type; struct ecommunity_as eas; struct ecommunity_ip eip; @@ -85,7 +88,7 @@ static void display_vrf_import_rt(struct vty *vty, struct vrf_irt_node *irt, eas.as |= (*pnt++); ptr_get_be32(pnt, &eas.val); - snprintf(rt_buf, RT_ADDRSTRLEN, "%u:%u", eas.as, eas.val); + snprintf(rt_buf, sizeof(rt_buf), "%u:%u", eas.as, eas.val); if (json) json_object_string_add(json_rt, "rt", rt_buf); @@ -100,7 +103,7 @@ static void display_vrf_import_rt(struct vty *vty, struct vrf_irt_node *irt, eip.val = (*pnt++ << 8); eip.val |= (*pnt++); - snprintf(rt_buf, RT_ADDRSTRLEN, "%s:%u", inet_ntoa(eip.ip), + snprintf(rt_buf, sizeof(rt_buf), "%s:%u", inet_ntoa(eip.ip), eip.val); if (json) @@ -115,7 +118,7 @@ static void display_vrf_import_rt(struct vty *vty, struct vrf_irt_node *irt, eas.val = (*pnt++ << 8); eas.val |= (*pnt++); - snprintf(rt_buf, RT_ADDRSTRLEN, "%u:%u", eas.as, eas.val); + snprintf(rt_buf, sizeof(rt_buf), "%u:%u", eas.as, eas.val); if (json) json_object_string_add(json_rt, "rt", rt_buf); @@ -165,7 +168,7 @@ static void show_vrf_import_rt_entry(struct hash_bucket *bucket, void *args[]) static void display_import_rt(struct vty *vty, struct irt_node *irt, json_object *json) { - uint8_t *pnt; + const uint8_t *pnt; uint8_t type, sub_type; struct ecommunity_as eas; struct ecommunity_ip eip; @@ -195,7 +198,7 @@ static void display_import_rt(struct vty *vty, struct irt_node *irt, eas.as |= (*pnt++); ptr_get_be32(pnt, &eas.val); - snprintf(rt_buf, RT_ADDRSTRLEN, "%u:%u", eas.as, eas.val); + snprintf(rt_buf, sizeof(rt_buf), "%u:%u", eas.as, eas.val); if (json) json_object_string_add(json_rt, "rt", rt_buf); @@ -210,7 +213,7 @@ static void display_import_rt(struct vty *vty, struct irt_node *irt, eip.val = (*pnt++ << 8); eip.val |= (*pnt++); - snprintf(rt_buf, RT_ADDRSTRLEN, "%s:%u", inet_ntoa(eip.ip), + snprintf(rt_buf, sizeof(rt_buf), "%s:%u", inet_ntoa(eip.ip), eip.val); if (json) @@ -225,7 +228,7 @@ static void display_import_rt(struct vty *vty, struct irt_node *irt, eas.val = (*pnt++ << 8); eas.val |= (*pnt++); - snprintf(rt_buf, RT_ADDRSTRLEN, "%u:%u", eas.as, eas.val); + snprintf(rt_buf, sizeof(rt_buf), "%u:%u", eas.as, eas.val); if (json) json_object_string_add(json_rt, "rt", rt_buf); @@ -272,43 +275,63 @@ static void show_import_rt_entry(struct hash_bucket *bucket, void *args[]) } static void bgp_evpn_show_route_rd_header(struct vty *vty, - struct bgp_node *rd_rn, - json_object *json) + struct bgp_dest *rd_dest, + json_object *json, char *rd_str, + int len) { uint16_t type; struct rd_as rd_as; struct rd_ip rd_ip; - uint8_t *pnt; - char rd_str[RD_ADDRSTRLEN]; + const uint8_t *pnt; + const struct prefix *p = bgp_dest_get_prefix(rd_dest); - pnt = rd_rn->p.u.val; + pnt = p->u.val; /* Decode RD type. */ type = decode_rd_type(pnt); - if (json) - return; - - vty_out(vty, "Route Distinguisher: "); + if (!json) + vty_out(vty, "Route Distinguisher: "); switch (type) { case RD_TYPE_AS: decode_rd_as(pnt + 2, &rd_as); - snprintf(rd_str, RD_ADDRSTRLEN, "%u:%d", rd_as.as, rd_as.val); + snprintf(rd_str, len, "%u:%d", rd_as.as, rd_as.val); + if (json) + json_object_string_add(json, "rd", rd_str); + else + vty_out(vty, "%s\n", rd_str); + break; + + case RD_TYPE_AS4: + decode_rd_as4(pnt + 2, &rd_as); + snprintf(rd_str, len, "%u:%d", rd_as.as, rd_as.val); + if (json) + json_object_string_add(json, "rd", rd_str); + else + vty_out(vty, "%s\n", rd_str); break; case RD_TYPE_IP: decode_rd_ip(pnt + 2, &rd_ip); - snprintf(rd_str, RD_ADDRSTRLEN, "%s:%d", inet_ntoa(rd_ip.ip), + snprintf(rd_str, len, "%s:%d", inet_ntoa(rd_ip.ip), rd_ip.val); + if (json) + json_object_string_add(json, "rd", rd_str); + else + vty_out(vty, "%s\n", rd_str); break; default: - snprintf(rd_str, RD_ADDRSTRLEN, "Unknown RD type"); + if (json) { + snprintf(rd_str, len, "Unknown"); + json_object_string_add(json, "rd", rd_str); + } else { + snprintf(rd_str, len, "Unknown RD type"); + vty_out(vty, "%s\n", rd_str); + } break; } - - vty_out(vty, "%s\n", rd_str); } static void bgp_evpn_show_route_header(struct vty *vty, struct bgp *bgp, @@ -323,9 +346,10 @@ static void bgp_evpn_show_route_header(struct vty *vty, struct bgp *bgp, vty_out(vty, "BGP table version is %" PRIu64 ", local router ID is %s\n", tbl_ver, inet_ntoa(bgp->router_id)); vty_out(vty, - "Status codes: s suppressed, d damped, h history, " - "* valid, > best, i - internal\n"); + "Status codes: s suppressed, d damped, h history, * valid, > best, i - internal\n"); vty_out(vty, "Origin codes: i - IGP, e - EGP, ? - incomplete\n"); + vty_out(vty, + "EVPN type-1 prefix: [1]:[ESI]:[EthTag]:[IPlen]:[VTEP-IP]\n"); vty_out(vty, "EVPN type-2 prefix: [2]:[EthTag]:[MAClen]:[MAC]:[IPlen]:[IP]\n"); vty_out(vty, "EVPN type-3 prefix: [3]:[EthTag]:[IPlen]:[OrigIP]\n"); @@ -343,6 +367,7 @@ static void display_l3vni(struct vty *vty, struct bgp *bgp_vrf, struct ecommunity *ecom; json_object *json_import_rtl = NULL; json_object *json_export_rtl = NULL; + char buf2[ETHER_ADDR_STRLEN]; json_import_rtl = json_export_rtl = 0; @@ -351,13 +376,29 @@ static void display_l3vni(struct vty *vty, struct bgp *bgp_vrf, json_export_rtl = json_object_new_array(); json_object_int_add(json, "vni", bgp_vrf->l3vni); json_object_string_add(json, "type", "L3"); - json_object_string_add(json, "kernelFlag", "Yes"); + json_object_string_add(json, "inKernel", "True"); json_object_string_add( json, "rd", prefix_rd2str(&bgp_vrf->vrf_prd, buf1, RD_ADDRSTRLEN)); json_object_string_add(json, "originatorIp", inet_ntoa(bgp_vrf->originator_ip)); json_object_string_add(json, "advertiseGatewayMacip", "n/a"); + json_object_string_add(json, "advertiseSviMacIp", "n/a"); + json_object_to_json_string_ext(json, + JSON_C_TO_STRING_NOSLASHESCAPE); + json_object_string_add(json, "advertisePip", + bgp_vrf->evpn_info->advertise_pip ? + "Enabled" : "Disabled"); + json_object_string_add(json, "sysIP", + inet_ntop(AF_INET, + &bgp_vrf->evpn_info->pip_ip, + buf1, INET_ADDRSTRLEN)); + json_object_string_add(json, "sysMac", + prefix_mac2str(&bgp_vrf->evpn_info->pip_rmac, + buf2, sizeof(buf2))); + json_object_string_add(json, "rmac", + prefix_mac2str(&bgp_vrf->rmac, + buf2, sizeof(buf2))); } else { vty_out(vty, "VNI: %d", bgp_vrf->l3vni); vty_out(vty, " (known to the kernel)"); @@ -371,6 +412,18 @@ static void display_l3vni(struct vty *vty, struct bgp *bgp_vrf, vty_out(vty, " Originator IP: %s\n", inet_ntoa(bgp_vrf->originator_ip)); vty_out(vty, " Advertise-gw-macip : %s\n", "n/a"); + vty_out(vty, " Advertise-svi-macip : %s\n", "n/a"); + vty_out(vty, " Advertise-pip: %s\n", + bgp_vrf->evpn_info->advertise_pip ? "Yes" : "No"); + vty_out(vty, " System-IP: %s\n", + inet_ntop(AF_INET, &bgp_vrf->evpn_info->pip_ip, + buf1, INET_ADDRSTRLEN)); + vty_out(vty, " System-MAC: %s\n", + prefix_mac2str(&bgp_vrf->evpn_info->pip_rmac, + buf2, sizeof(buf2))); + vty_out(vty, " Router-MAC: %s\n", + prefix_mac2str(&bgp_vrf->rmac, + buf2, sizeof(buf2))); } if (!json) @@ -411,47 +464,6 @@ static void display_l3vni(struct vty *vty, struct bgp *bgp_vrf, json_object_object_add(json, "exportRts", json_export_rtl); } -static void display_es(struct vty *vty, struct evpnes *es, json_object *json) -{ - struct in_addr *vtep; - char buf[ESI_STR_LEN]; - char buf1[RD_ADDRSTRLEN]; - char buf2[INET6_ADDRSTRLEN]; - struct listnode *node = NULL; - json_object *json_vteps = NULL; - - if (json) { - json_vteps = json_object_new_array(); - json_object_string_add(json, "esi", - esi_to_str(&es->esi, buf, sizeof(buf))); - json_object_string_add(json, "rd", - prefix_rd2str(&es->prd, buf1, - sizeof(buf1))); - json_object_string_add( - json, "originatorIp", - ipaddr2str(&es->originator_ip, buf2, sizeof(buf2))); - if (es->vtep_list) { - for (ALL_LIST_ELEMENTS_RO(es->vtep_list, node, vtep)) - json_object_array_add( - json_vteps, json_object_new_string( - inet_ntoa(*vtep))); - } - json_object_object_add(json, "vteps", json_vteps); - } else { - vty_out(vty, "ESI: %s\n", - esi_to_str(&es->esi, buf, sizeof(buf))); - vty_out(vty, " RD: %s\n", prefix_rd2str(&es->prd, buf1, - sizeof(buf1))); - vty_out(vty, " Originator-IP: %s\n", - ipaddr2str(&es->originator_ip, buf2, sizeof(buf2))); - if (es->vtep_list) { - vty_out(vty, " VTEP List:\n"); - for (ALL_LIST_ELEMENTS_RO(es->vtep_list, node, vtep)) - vty_out(vty, " %s\n", inet_ntoa(*vtep)); - } - } -} - static void display_vni(struct vty *vty, struct bgpevpn *vpn, json_object *json) { char buf1[RD_ADDRSTRLEN]; @@ -460,14 +472,17 @@ static void display_vni(struct vty *vty, struct bgpevpn *vpn, json_object *json) struct ecommunity *ecom; json_object *json_import_rtl = NULL; json_object *json_export_rtl = NULL; + struct bgp *bgp_evpn; + + bgp_evpn = bgp_get_evpn(); if (json) { json_import_rtl = json_object_new_array(); json_export_rtl = json_object_new_array(); json_object_int_add(json, "vni", vpn->vni); json_object_string_add(json, "type", "L2"); - json_object_string_add(json, "kernelFlag", - is_vni_live(vpn) ? "Yes" : "No"); + json_object_string_add(json, "inKernel", + is_vni_live(vpn) ? "True" : "False"); json_object_string_add( json, "rd", prefix_rd2str(&vpn->prd, buf1, sizeof(buf1))); @@ -475,8 +490,30 @@ static void display_vni(struct vty *vty, struct bgpevpn *vpn, json_object *json) inet_ntoa(vpn->originator_ip)); json_object_string_add(json, "mcastGroup", inet_ntoa(vpn->mcast_grp)); - json_object_string_add(json, "advertiseGatewayMacip", - vpn->advertise_gw_macip ? "Yes" : "No"); + /* per vni knob is enabled -- Enabled + * Global knob is enabled -- Active + * default -- Disabled + */ + if (!vpn->advertise_gw_macip && + bgp_evpn && bgp_evpn->advertise_gw_macip) + json_object_string_add(json, "advertiseGatewayMacip", + "Active"); + else if (vpn->advertise_gw_macip) + json_object_string_add(json, "advertiseGatewayMacip", + "Enabled"); + else + json_object_string_add(json, "advertiseGatewayMacip", + "Disabled"); + if (!vpn->advertise_svi_macip && bgp_evpn && + bgp_evpn->evpn_info->advertise_svi_macip) + json_object_string_add(json, "advertiseSviMacIp", + "Active"); + else if (vpn->advertise_svi_macip) + json_object_string_add(json, "advertiseSviMacIp", + "Enabled"); + else + json_object_string_add(json, "advertiseSviMacIp", + "Disabled"); } else { vty_out(vty, "VNI: %d", vpn->vni); if (is_vni_live(vpn)) @@ -492,10 +529,26 @@ static void display_vni(struct vty *vty, struct bgpevpn *vpn, json_object *json) inet_ntoa(vpn->originator_ip)); vty_out(vty, " Mcast group: %s\n", inet_ntoa(vpn->mcast_grp)); - vty_out(vty, " Advertise-gw-macip : %s\n", - vpn->advertise_gw_macip ? "Yes" : "No"); - vty_out(vty, " Advertise-svi-macip : %s\n", - vpn->advertise_svi_macip ? "Yes" : "No"); + if (!vpn->advertise_gw_macip && + bgp_evpn && bgp_evpn->advertise_gw_macip) + vty_out(vty, " Advertise-gw-macip : %s\n", + "Active"); + else if (vpn->advertise_gw_macip) + vty_out(vty, " Advertise-gw-macip : %s\n", + "Enabled"); + else + vty_out(vty, " Advertise-gw-macip : %s\n", + "Disabled"); + if (!vpn->advertise_svi_macip && bgp_evpn && + bgp_evpn->evpn_info->advertise_svi_macip) + vty_out(vty, " Advertise-svi-macip : %s\n", + "Active"); + else if (vpn->advertise_svi_macip) + vty_out(vty, " Advertise-svi-macip : %s\n", + "Enabled"); + else + vty_out(vty, " Advertise-svi-macip : %s\n", + "Disabled"); } if (!json) @@ -537,12 +590,12 @@ static void display_vni(struct vty *vty, struct bgpevpn *vpn, json_object *json) } static void show_esi_routes(struct bgp *bgp, - struct evpnes *es, + struct bgp_evpn_es *es, struct vty *vty, json_object *json) { int header = 1; - struct bgp_node *rn; + struct bgp_dest *dest; struct bgp_path_info *pi; uint32_t prefix_cnt, path_cnt; uint64_t tbl_ver; @@ -550,20 +603,21 @@ static void show_esi_routes(struct bgp *bgp, prefix_cnt = path_cnt = 0; tbl_ver = es->route_table->version; - for (rn = bgp_table_top(es->route_table); rn; - rn = bgp_route_next(rn)) { + for (dest = bgp_table_top(es->route_table); dest; + dest = bgp_route_next(dest)) { int add_prefix_to_json = 0; char prefix_str[BUFSIZ]; json_object *json_paths = NULL; json_object *json_prefix = NULL; + const struct prefix *p = bgp_dest_get_prefix(dest); - bgp_evpn_route2str((struct prefix_evpn *)&rn->p, prefix_str, + bgp_evpn_route2str((struct prefix_evpn *)p, prefix_str, sizeof(prefix_str)); if (json) json_prefix = json_object_new_object(); - pi = bgp_node_get_bgp_path_info(rn); + pi = bgp_dest_get_bgp_path_info(dest); if (pi) { /* Overall header/legend displayed once. */ if (header) { @@ -587,7 +641,8 @@ static void show_esi_routes(struct bgp *bgp, if (json) json_path = json_object_new_array(); - route_vty_out(vty, &rn->p, pi, 0, SAFI_EVPN, json_path); + route_vty_out(vty, p, pi, 0, SAFI_EVPN, json_path, + false); if (json) json_object_array_add(json_paths, json_path); @@ -596,14 +651,22 @@ static void show_esi_routes(struct bgp *bgp, add_prefix_to_json = 1; } - if (json && add_prefix_to_json) { - json_object_string_add(json_prefix, "prefix", - prefix_str); - json_object_int_add(json_prefix, "prefixLen", - rn->p.prefixlen); - json_object_object_add(json_prefix, "paths", - json_paths); - json_object_object_add(json, prefix_str, json_prefix); + if (json) { + if (add_prefix_to_json) { + json_object_string_add(json_prefix, "prefix", + prefix_str); + json_object_int_add(json_prefix, "prefixLen", + p->prefixlen); + json_object_object_add(json_prefix, "paths", + json_paths); + json_object_object_add(json, prefix_str, + json_prefix); + } else { + json_object_free(json_paths); + json_object_free(json_prefix); + json_paths = NULL; + json_prefix = NULL; + } } } @@ -623,7 +686,7 @@ static void show_vni_routes(struct bgp *bgp, struct bgpevpn *vpn, int type, struct vty *vty, struct in_addr vtep_ip, json_object *json, int detail) { - struct bgp_node *rn; + struct bgp_dest *dest; struct bgp_path_info *pi; struct bgp_table *table; int header = detail ? 0 : 1; @@ -634,16 +697,18 @@ static void show_vni_routes(struct bgp *bgp, struct bgpevpn *vpn, int type, table = vpn->route_table; tbl_ver = table->version; - for (rn = bgp_table_top(table); rn; - rn = bgp_route_next(rn)) { - struct prefix_evpn *evp = (struct prefix_evpn *)&rn->p; + for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) { + const struct prefix_evpn *evp = + (const struct prefix_evpn *)bgp_dest_get_prefix(dest); int add_prefix_to_json = 0; char prefix_str[BUFSIZ]; json_object *json_paths = NULL; json_object *json_prefix = NULL; + const struct prefix *p = bgp_dest_get_prefix(dest); - bgp_evpn_route2str((struct prefix_evpn *)&rn->p, prefix_str, - sizeof(prefix_str)); + bgp_evpn_route2str( + (struct prefix_evpn *)bgp_dest_get_prefix(dest), + prefix_str, sizeof(prefix_str)); if (type && evp->prefix.route_type != type) continue; @@ -651,7 +716,7 @@ static void show_vni_routes(struct bgp *bgp, struct bgpevpn *vpn, int type, if (json) json_prefix = json_object_new_object(); - pi = bgp_node_get_bgp_path_info(rn); + pi = bgp_dest_get_bgp_path_info(dest); if (pi) { /* Overall header/legend displayed once. */ if (header) { @@ -672,7 +737,7 @@ static void show_vni_routes(struct bgp *bgp, struct bgpevpn *vpn, int type, for (; pi; pi = pi->next) { json_object *json_path = NULL; - if (vtep_ip.s_addr + if (vtep_ip.s_addr != INADDR_ANY && !IPV4_ADDR_SAME(&(vtep_ip), &(pi->attr->nexthop))) continue; @@ -681,12 +746,12 @@ static void show_vni_routes(struct bgp *bgp, struct bgpevpn *vpn, int type, json_path = json_object_new_array(); if (detail) - route_vty_out_detail(vty, bgp, &rn->p, pi, + route_vty_out_detail(vty, bgp, dest, pi, AFI_L2VPN, SAFI_EVPN, json_path); else - route_vty_out(vty, &rn->p, pi, 0, SAFI_EVPN, - json_path); + route_vty_out(vty, p, pi, 0, SAFI_EVPN, + json_path, false); if (json) json_object_array_add(json_paths, json_path); @@ -695,14 +760,22 @@ static void show_vni_routes(struct bgp *bgp, struct bgpevpn *vpn, int type, add_prefix_to_json = 1; } - if (json && add_prefix_to_json) { - json_object_string_add(json_prefix, "prefix", - prefix_str); - json_object_int_add(json_prefix, "prefixLen", - rn->p.prefixlen); - json_object_object_add(json_prefix, "paths", - json_paths); - json_object_object_add(json, prefix_str, json_prefix); + if (json) { + if (add_prefix_to_json) { + json_object_string_add(json_prefix, "prefix", + prefix_str); + json_object_int_add(json_prefix, "prefixLen", + p->prefixlen); + json_object_object_add(json_prefix, "paths", + json_paths); + json_object_object_add(json, prefix_str, + json_prefix); + } else { + json_object_free(json_paths); + json_object_free(json_prefix); + json_paths = NULL; + json_prefix = NULL; + } } } @@ -730,7 +803,7 @@ static void show_vni_routes_hash(struct hash_bucket *bucket, void *arg) json_object *json_vni = NULL; char vni_str[VNI_STR_LEN]; - snprintf(vni_str, VNI_STR_LEN, "%d", vpn->vni); + snprintf(vni_str, sizeof(vni_str), "%d", vpn->vni); if (json) { json_vni = json_object_new_object(); json_object_int_add(json_vni, "vni", vpn->vni); @@ -769,7 +842,7 @@ static void show_l3vni_entry(struct vty *vty, struct bgp *bgp, /* if an l3vni is present in bgp it is live */ buf1[0] = '\0'; - sprintf(buf1, "*"); + snprintf(buf1, sizeof(buf1), "*"); if (json) { json_object_int_add(json_vni, "vni", bgp->l3vni); @@ -780,6 +853,22 @@ static void show_l3vni_entry(struct vty *vty, struct bgp *bgp, json_object_string_add( json_vni, "rd", prefix_rd2str(&bgp->vrf_prd, buf2, RD_ADDRSTRLEN)); + json_object_string_add(json_vni, "advertiseGatewayMacip", + "n/a"); + json_object_string_add(json_vni, "advertiseSviMacIp", "n/a"); + json_object_to_json_string_ext(json_vni, + JSON_C_TO_STRING_NOSLASHESCAPE); + json_object_string_add( + json_vni, "advertisePip", + bgp->evpn_info->advertise_pip ? "Enabled" : "Disabled"); + json_object_string_add(json_vni, "sysIP", + inet_ntoa(bgp->evpn_info->pip_ip)); + json_object_string_add(json_vni, "sysMAC", + prefix_mac2str(&bgp->evpn_info->pip_rmac, + buf2, sizeof(buf2))); + json_object_string_add( + json_vni, "rmac", + prefix_mac2str(&bgp->rmac, buf2, sizeof(buf2))); } else { vty_out(vty, "%-1s %-10u %-4s %-21s", buf1, bgp->l3vni, "L3", prefix_rd2str(&bgp->vrf_prd, buf2, RD_ADDRSTRLEN)); @@ -794,9 +883,11 @@ static void show_l3vni_entry(struct vty *vty, struct bgp *bgp, json_object_new_string(ecom_str)); } else { if (listcount(bgp->vrf_import_rtl) > 1) - sprintf(rt_buf, "%s, ...", ecom_str); + snprintf(rt_buf, sizeof(rt_buf), "%s, ...", + ecom_str); else - sprintf(rt_buf, "%s", ecom_str); + snprintf(rt_buf, sizeof(rt_buf), "%s", + ecom_str); vty_out(vty, " %-25s", rt_buf); } @@ -820,9 +911,11 @@ static void show_l3vni_entry(struct vty *vty, struct bgp *bgp, json_object_new_string(ecom_str)); } else { if (listcount(bgp->vrf_export_rtl) > 1) - sprintf(rt_buf, "%s, ...", ecom_str); + snprintf(rt_buf, sizeof(rt_buf), "%s, ...", + ecom_str); else - sprintf(rt_buf, "%s", ecom_str); + snprintf(rt_buf, sizeof(rt_buf), "%s", + ecom_str); vty_out(vty, " %-25s", rt_buf); } @@ -841,55 +934,13 @@ static void show_l3vni_entry(struct vty *vty, struct bgp *bgp, char vni_str[VNI_STR_LEN]; json_object_object_add(json_vni, "exportRTs", json_export_rtl); - snprintf(vni_str, VNI_STR_LEN, "%u", bgp->l3vni); + snprintf(vni_str, sizeof(vni_str), "%u", bgp->l3vni); json_object_object_add(json, vni_str, json_vni); } else { vty_out(vty, "\n"); } } -static void show_es_entry(struct hash_bucket *bucket, void *args[]) -{ - char buf[ESI_STR_LEN]; - char buf1[RD_ADDRSTRLEN]; - char buf2[INET6_ADDRSTRLEN]; - struct in_addr *vtep = NULL; - struct vty *vty = args[0]; - json_object *json = args[1]; - json_object *json_vteps = NULL; - struct listnode *node = NULL; - struct evpnes *es = (struct evpnes *)bucket->data; - - if (json) { - json_vteps = json_object_new_array(); - json_object_string_add(json, "esi", - esi_to_str(&es->esi, buf, sizeof(buf))); - json_object_string_add(json, "type", - is_es_local(es) ? "Local" : "Remote"); - json_object_string_add(json, "rd", - prefix_rd2str(&es->prd, buf1, - sizeof(buf1))); - json_object_string_add( - json, "originatorIp", - ipaddr2str(&es->originator_ip, buf2, sizeof(buf2))); - if (es->vtep_list) { - for (ALL_LIST_ELEMENTS_RO(es->vtep_list, node, vtep)) - json_object_array_add(json_vteps, - json_object_new_string( - inet_ntoa(*vtep))); - } - json_object_object_add(json, "vteps", json_vteps); - } else { - vty_out(vty, "%-30s %-6s %-21s %-15s %-6d\n", - esi_to_str(&es->esi, buf, sizeof(buf)), - is_es_local(es) ? "Local" : "Remote", - prefix_rd2str(&es->prd, buf1, sizeof(buf1)), - ipaddr2str(&es->originator_ip, buf2, - sizeof(buf2)), - es->vtep_list ? listcount(es->vtep_list) : 0); - } -} - static void show_vni_entry(struct hash_bucket *bucket, void *args[]) { struct vty *vty; @@ -904,10 +955,13 @@ static void show_vni_entry(struct hash_bucket *bucket, void *args[]) char *ecom_str; struct listnode *node, *nnode; struct ecommunity *ecom; + struct bgp *bgp_evpn; vty = args[0]; json = args[1]; + bgp_evpn = bgp_get_evpn(); + if (json) { json_vni = json_object_new_object(); json_import_rtl = json_object_new_array(); @@ -916,20 +970,44 @@ static void show_vni_entry(struct hash_bucket *bucket, void *args[]) buf1[0] = '\0'; if (is_vni_live(vpn)) - sprintf(buf1, "*"); + snprintf(buf1, sizeof(buf1), "*"); if (json) { json_object_int_add(json_vni, "vni", vpn->vni); json_object_string_add(json_vni, "type", "L2"); json_object_string_add(json_vni, "inKernel", is_vni_live(vpn) ? "True" : "False"); - json_object_string_add(json_vni, "originatorIp", - inet_ntoa(vpn->originator_ip)); - json_object_string_add(json_vni, "originatorIp", - inet_ntoa(vpn->originator_ip)); json_object_string_add( json_vni, "rd", prefix_rd2str(&vpn->prd, buf2, sizeof(buf2))); + json_object_string_add(json_vni, "originatorIp", + inet_ntoa(vpn->originator_ip)); + json_object_string_add(json_vni, "mcastGroup", + inet_ntoa(vpn->mcast_grp)); + /* per vni knob is enabled -- Enabled + * Global knob is enabled -- Active + * default -- Disabled + */ + if (!vpn->advertise_gw_macip && bgp_evpn + && bgp_evpn->advertise_gw_macip) + json_object_string_add( + json_vni, "advertiseGatewayMacip", "Active"); + else if (vpn->advertise_gw_macip) + json_object_string_add( + json_vni, "advertiseGatewayMacip", "Enabled"); + else + json_object_string_add( + json_vni, "advertiseGatewayMacip", "Disabled"); + if (!vpn->advertise_svi_macip && bgp_evpn + && bgp_evpn->evpn_info->advertise_svi_macip) + json_object_string_add(json_vni, "advertiseSviMacIp", + "Active"); + else if (vpn->advertise_svi_macip) + json_object_string_add(json_vni, "advertiseSviMacIp", + "Enabled"); + else + json_object_string_add(json_vni, "advertiseSviMacIp", + "Disabled"); } else { vty_out(vty, "%-1s %-10u %-4s %-21s", buf1, vpn->vni, "L2", prefix_rd2str(&vpn->prd, buf2, RD_ADDRSTRLEN)); @@ -944,9 +1022,11 @@ static void show_vni_entry(struct hash_bucket *bucket, void *args[]) json_object_new_string(ecom_str)); } else { if (listcount(vpn->import_rtl) > 1) - sprintf(rt_buf, "%s, ...", ecom_str); + snprintf(rt_buf, sizeof(rt_buf), "%s, ...", + ecom_str); else - sprintf(rt_buf, "%s", ecom_str); + snprintf(rt_buf, sizeof(rt_buf), "%s", + ecom_str); vty_out(vty, " %-25s", rt_buf); } @@ -970,9 +1050,11 @@ static void show_vni_entry(struct hash_bucket *bucket, void *args[]) json_object_new_string(ecom_str)); } else { if (listcount(vpn->export_rtl) > 1) - sprintf(rt_buf, "%s, ...", ecom_str); + snprintf(rt_buf, sizeof(rt_buf), "%s, ...", + ecom_str); else - sprintf(rt_buf, "%s", ecom_str); + snprintf(rt_buf, sizeof(rt_buf), "%s", + ecom_str); vty_out(vty, " %-25s", rt_buf); } @@ -991,7 +1073,7 @@ static void show_vni_entry(struct hash_bucket *bucket, void *args[]) char vni_str[VNI_STR_LEN]; json_object_object_add(json_vni, "exportRTs", json_export_rtl); - snprintf(vni_str, VNI_STR_LEN, "%u", vpn->vni); + snprintf(vni_str, sizeof(vni_str), "%u", vpn->vni); json_object_object_add(json, vni_str, json_vni); } else { vty_out(vty, "\n"); @@ -1005,19 +1087,22 @@ static int bgp_show_ethernet_vpn(struct vty *vty, struct prefix_rd *prd, afi_t afi = AFI_L2VPN; struct bgp *bgp; struct bgp_table *table; - struct bgp_node *rn; - struct bgp_node *rm; + struct bgp_dest *dest; + struct bgp_dest *rm; struct bgp_path_info *pi; int rd_header; int header = 1; + char rd_str[RD_ADDRSTRLEN]; + char buf[BUFSIZ]; + int no_display; unsigned long output_count = 0; unsigned long total_count = 0; json_object *json = NULL; - json_object *json_nroute = NULL; json_object *json_array = NULL; - json_object *json_scode = NULL; - json_object *json_ocode = NULL; + json_object *json_prefix_info = NULL; + + memset(rd_str, 0, RD_ADDRSTRLEN); bgp = bgp_get_evpn(); if (bgp == NULL) { @@ -1028,75 +1113,88 @@ static int bgp_show_ethernet_vpn(struct vty *vty, struct prefix_rd *prd, return CMD_WARNING; } - if (use_json) { - json_scode = json_object_new_object(); - json_ocode = json_object_new_object(); + if (use_json) json = json_object_new_object(); - json_nroute = json_object_new_object(); - - json_object_string_add(json_scode, "suppressed", "s"); - json_object_string_add(json_scode, "damped", "d"); - json_object_string_add(json_scode, "history", "h"); - json_object_string_add(json_scode, "valid", "*"); - json_object_string_add(json_scode, "best", ">"); - json_object_string_add(json_scode, "internal", "i"); - json_object_string_add(json_ocode, "igp", "i"); - json_object_string_add(json_ocode, "egp", "e"); - json_object_string_add(json_ocode, "incomplete", "?"); - } - - for (rn = bgp_table_top(bgp->rib[afi][SAFI_EVPN]); rn; - rn = bgp_route_next(rn)) { + for (dest = bgp_table_top(bgp->rib[afi][SAFI_EVPN]); dest; + dest = bgp_route_next(dest)) { uint64_t tbl_ver; + json_object *json_nroute = NULL; + const struct prefix *p = bgp_dest_get_prefix(dest); - if (use_json) - continue; /* XXX json TODO */ - - if (prd && memcmp(rn->p.u.val, prd->val, 8) != 0) + if (prd && memcmp(p->u.val, prd->val, 8) != 0) continue; - table = bgp_node_get_bgp_table_info(rn); + table = bgp_dest_get_bgp_table_info(dest); if (!table) continue; rd_header = 1; tbl_ver = table->version; - for (rm = bgp_table_top(table); rm; rm = bgp_route_next(rm)) - for (pi = bgp_node_get_bgp_path_info(rm); pi; - pi = pi->next) { + for (rm = bgp_table_top(table); rm; rm = bgp_route_next(rm)) { + pi = bgp_dest_get_bgp_path_info(rm); + if (pi == NULL) + continue; + + no_display = 0; + for (; pi; pi = pi->next) { total_count++; if (type == bgp_show_type_neighbor) { - union sockunion *su = output_arg; + struct peer *peer = output_arg; - if (pi->peer->su_remote == NULL - || !sockunion_same( - pi->peer->su_remote, su)) + if (peer_cmp(peer, pi->peer) != 0) continue; } - if (header == 0) { + if (type == bgp_show_type_lcommunity_exact) { + struct lcommunity *lcom = output_arg; + + if (!pi->attr->lcommunity || + !lcommunity_cmp( + pi->attr->lcommunity, lcom)) + continue; + } + if (type == bgp_show_type_lcommunity) { + struct lcommunity *lcom = output_arg; + + if (!pi->attr->lcommunity || + !lcommunity_match( + pi->attr->lcommunity, lcom)) + continue; + } + if (type == bgp_show_type_community) { + struct community *com = output_arg; + + if (!pi->attr->community || + !community_match( + pi->attr->community, com)) + continue; + } + if (type == bgp_show_type_community_exact) { + struct community *com = output_arg; + + if (!pi->attr->community || + !community_cmp( + pi->attr->community, com)) + continue; + } + if (header) { if (use_json) { - if (option - == SHOW_DISPLAY_TAGS) { - json_object_int_add( - json, - "bgpTableVersion", - tbl_ver); - json_object_string_add( - json, - "bgpLocalRouterId", - inet_ntoa( - bgp->router_id)); - json_object_object_add( - json, - "bgpStatusCodes", - json_scode); - json_object_object_add( - json, - "bgpOriginCodes", - json_ocode); - } + json_object_int_add( + json, "bgpTableVersion", + tbl_ver); + json_object_string_add( + json, + "bgpLocalRouterId", + inet_ntoa( + bgp->router_id)); + json_object_int_add( + json, + "defaultLocPrf", + bgp->default_local_pref); + json_object_int_add( + json, "localAS", + bgp->as); } else { if (option == SHOW_DISPLAY_TAGS) vty_out(vty, @@ -1107,98 +1205,85 @@ static int bgp_show_ethernet_vpn(struct vty *vty, struct prefix_rd *prd, vty_out(vty, V4_HEADER_OVERLAY); else { - vty_out(vty, - "BGP table version is %" PRIu64 ", local router ID is %s\n", - tbl_ver, - inet_ntoa( - bgp->router_id)); - vty_out(vty, - "Status codes: s suppressed, d damped, h history, * valid, > best, i - internal\n"); - vty_out(vty, - "Origin codes: i - IGP, e - EGP, ? - incomplete\n\n"); - vty_out(vty, V4_HEADER); + bgp_evpn_show_route_header(vty, bgp, tbl_ver, NULL); } } header = 0; } if (rd_header) { - uint16_t type; - struct rd_as rd_as; - struct rd_ip rd_ip; - uint8_t *pnt; - - pnt = rn->p.u.val; - - /* Decode RD type. */ - type = decode_rd_type(pnt); - /* Decode RD value. */ - if (type == RD_TYPE_AS) - decode_rd_as(pnt + 2, &rd_as); - else if (type == RD_TYPE_AS4) - decode_rd_as4(pnt + 2, &rd_as); - else if (type == RD_TYPE_IP) - decode_rd_ip(pnt + 2, &rd_ip); - if (use_json) { - char buffer[BUFSIZ]; - if (type == RD_TYPE_AS - || type == RD_TYPE_AS4) - sprintf(buffer, "%u:%d", - rd_as.as, - rd_as.val); - else if (type == RD_TYPE_IP) - sprintf(buffer, "%s:%d", - inet_ntoa( - rd_ip.ip), - rd_ip.val); - json_object_string_add( - json_nroute, - "routeDistinguisher", - buffer); - } else { - vty_out(vty, - "Route Distinguisher: "); - if (type == RD_TYPE_AS) - vty_out(vty, - "as2 %u:%d", - rd_as.as, - rd_as.val); - else if (type == RD_TYPE_AS4) - vty_out(vty, - "as4 %u:%d", - rd_as.as, - rd_as.val); - else if (type == RD_TYPE_IP) - vty_out(vty, "ip %s:%d", - inet_ntoa( - rd_ip.ip), - rd_ip.val); - vty_out(vty, "\n\n"); - } + if (use_json) + json_nroute = + json_object_new_object(); + bgp_evpn_show_route_rd_header( + vty, dest, json_nroute, rd_str, + RD_ADDRSTRLEN); rd_header = 0; } - if (use_json) + if (use_json && !json_array) json_array = json_object_new_array(); - else - json_array = NULL; + if (option == SHOW_DISPLAY_TAGS) - route_vty_out_tag(vty, &rm->p, pi, 0, - SAFI_EVPN, - json_array); + route_vty_out_tag( + vty, bgp_dest_get_prefix(rm), + pi, no_display, SAFI_EVPN, + json_array); else if (option == SHOW_DISPLAY_OVERLAY) - route_vty_out_overlay(vty, &rm->p, pi, - 0, json_array); + route_vty_out_overlay( + vty, bgp_dest_get_prefix(rm), + pi, no_display, json_array); else - route_vty_out(vty, &rm->p, pi, 0, - SAFI_EVPN, json_array); + route_vty_out(vty, + bgp_dest_get_prefix(rm), + pi, no_display, SAFI_EVPN, + json_array, false); + no_display = 1; + } + + if (no_display) output_count++; + + if (use_json && json_array) { + const struct prefix *p = + bgp_dest_get_prefix(rm); + + json_prefix_info = json_object_new_object(); + + json_object_string_add( + json_prefix_info, "prefix", + bgp_evpn_route2str( + (struct prefix_evpn *)p, buf, + BUFSIZ)); + + json_object_int_add(json_prefix_info, + "prefixLen", p->prefixlen); + + json_object_object_add(json_prefix_info, + "paths", json_array); + json_object_object_add(json_nroute, buf, + json_prefix_info); + json_array = NULL; } - /* XXX json */ + } + + if (use_json && json_nroute) + json_object_object_add(json, rd_str, json_nroute); + } + + if (use_json) { + json_object_int_add(json, "numPrefix", output_count); + json_object_int_add(json, "totalPrefix", total_count); + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } else { + if (output_count == 0) + vty_out(vty, "No prefixes displayed, %ld exist\n", + total_count); + else + vty_out(vty, + "\nDisplayed %ld out of %ld total prefixes\n", + output_count, total_count); } - if (output_count == 0) - vty_out(vty, "No prefixes displayed, %ld exist\n", total_count); - else - vty_out(vty, "\nDisplayed %ld out of %ld total prefixes\n", - output_count, total_count); return CMD_SUCCESS; } @@ -1278,29 +1363,41 @@ DEFUN(show_ip_bgp_l2vpn_evpn_rd_tags, 0); } -DEFUN(show_ip_bgp_l2vpn_evpn_all_neighbor_routes, - show_ip_bgp_l2vpn_evpn_all_neighbor_routes_cmd, - "show [ip] bgp l2vpn evpn all neighbors A.B.C.D routes [json]", +DEFUN(show_ip_bgp_l2vpn_evpn_neighbor_routes, + show_ip_bgp_l2vpn_evpn_neighbor_routes_cmd, + "show [ip] bgp l2vpn evpn neighbors routes [json]", SHOW_STR IP_STR BGP_STR L2VPN_HELP_STR EVPN_HELP_STR - "Display information about all EVPN NLRIs\n" "Detailed information on TCP and BGP neighbor connections\n" - "Neighbor to display information about\n" + "IPv4 Neighbor to display information about\n" + "IPv6 Neighbor to display information about\n" + "Neighbor on BGP configured interface\n" "Display routes learned from neighbor\n" JSON_STR) { - int idx_ipv4 = 0; - union sockunion su; + int idx = 0; struct peer *peer; - int ret; + char *peerstr = NULL; bool uj = use_json(argc, argv); + afi_t afi = AFI_L2VPN; + safi_t safi = SAFI_EVPN; + struct bgp *bgp = NULL; + + bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi, + &bgp, uj); + if (!idx) { + vty_out(vty, "No index\n"); + return CMD_WARNING; + } - argv_find(argv, argc, "A.B.C.D", &idx_ipv4); + /* neighbors */ + argv_find(argv, argc, "neighbors", &idx); + peerstr = argv[++idx]->arg; - ret = str2sockunion(argv[idx_ipv4]->arg, &su); - if (ret < 0) { + peer = peer_lookup_in_view(vty, bgp, peerstr, uj); + if (!peer) { if (uj) { json_object *json_no = NULL; json_no = json_object_new_object(); @@ -1311,11 +1408,9 @@ DEFUN(show_ip_bgp_l2vpn_evpn_all_neighbor_routes, json_object_free(json_no); } else vty_out(vty, "Malformed address: %s\n", - argv[idx_ipv4]->arg); + argv[idx]->arg); return CMD_WARNING; } - - peer = peer_lookup(NULL, &su); if (!peer || !peer->afc[AFI_L2VPN][SAFI_EVPN]) { if (uj) { json_object *json_no = NULL; @@ -1331,13 +1426,13 @@ DEFUN(show_ip_bgp_l2vpn_evpn_all_neighbor_routes, return CMD_WARNING; } - return bgp_show_ethernet_vpn(vty, NULL, bgp_show_type_neighbor, &su, 0, + return bgp_show_ethernet_vpn(vty, NULL, bgp_show_type_neighbor, peer, 0, uj); } DEFUN(show_ip_bgp_l2vpn_evpn_rd_neighbor_routes, show_ip_bgp_l2vpn_evpn_rd_neighbor_routes_cmd, - "show [ip] bgp l2vpn evpn rd ASN:NN_OR_IP-ADDRESS:NN neighbors A.B.C.D routes [json]", + "show [ip] bgp l2vpn evpn rd ASN:NN_OR_IP-ADDRESS:NN neighbors routes [json]", SHOW_STR IP_STR BGP_STR @@ -1346,20 +1441,30 @@ DEFUN(show_ip_bgp_l2vpn_evpn_rd_neighbor_routes, "Display information for a route distinguisher\n" "VPN Route Distinguisher\n" "Detailed information on TCP and BGP neighbor connections\n" - "Neighbor to display information about\n" + "IPv4 Neighbor to display information about\n" + "IPv6 Neighbor to display information about\n" + "Neighbor on BGP configured interface\n" "Display routes learned from neighbor\n" JSON_STR) { int idx_ext_community = 0; - int idx_ipv4 = 0; + int idx = 0; int ret; - union sockunion su; struct peer *peer; + char *peerstr = NULL; struct prefix_rd prd; bool uj = use_json(argc, argv); + afi_t afi = AFI_L2VPN; + safi_t safi = SAFI_EVPN; + struct bgp *bgp = NULL; - argv_find(argv, argc, "ASN:NN_OR_IP-ADDRESS:NN", &idx_ext_community); - argv_find(argv, argc, "A.B.C.D", &idx_ipv4); + bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi, + &bgp, uj); + if (!idx) { + vty_out(vty, "No index\n"); + return CMD_WARNING; + } + argv_find(argv, argc, "ASN:NN_OR_IP-ADDRESS:NN", &idx_ext_community); ret = str2prefix_rd(argv[idx_ext_community]->arg, &prd); if (!ret) { if (uj) { @@ -1375,8 +1480,12 @@ DEFUN(show_ip_bgp_l2vpn_evpn_rd_neighbor_routes, return CMD_WARNING; } - ret = str2sockunion(argv[idx_ipv4]->arg, &su); - if (ret < 0) { + /* neighbors */ + argv_find(argv, argc, "neighbors", &idx); + peerstr = argv[++idx]->arg; + + peer = peer_lookup_in_view(vty, bgp, peerstr, uj); + if (!peer) { if (uj) { json_object *json_no = NULL; json_no = json_object_new_object(); @@ -1387,11 +1496,9 @@ DEFUN(show_ip_bgp_l2vpn_evpn_rd_neighbor_routes, json_object_free(json_no); } else vty_out(vty, "Malformed address: %s\n", - argv[idx_ext_community]->arg); + argv[idx]->arg); return CMD_WARNING; } - - peer = peer_lookup(NULL, &su); if (!peer || !peer->afc[AFI_L2VPN][SAFI_EVPN]) { if (uj) { json_object *json_no = NULL; @@ -1407,33 +1514,48 @@ DEFUN(show_ip_bgp_l2vpn_evpn_rd_neighbor_routes, return CMD_WARNING; } - return bgp_show_ethernet_vpn(vty, &prd, bgp_show_type_neighbor, &su, 0, + return bgp_show_ethernet_vpn(vty, &prd, bgp_show_type_neighbor, peer, 0, uj); } -DEFUN(show_ip_bgp_l2vpn_evpn_all_neighbor_advertised_routes, - show_ip_bgp_l2vpn_evpn_all_neighbor_advertised_routes_cmd, - "show [ip] bgp l2vpn evpn all neighbors A.B.C.D advertised-routes [json]", +DEFUN(show_ip_bgp_l2vpn_evpn_neighbor_advertised_routes, + show_ip_bgp_l2vpn_evpn_neighbor_advertised_routes_cmd, + "show [ip] bgp l2vpn evpn neighbors advertised-routes [json]", SHOW_STR IP_STR BGP_STR L2VPN_HELP_STR EVPN_HELP_STR - "Display information about all EVPN NLRIs\n" "Detailed information on TCP and BGP neighbor connections\n" - "Neighbor to display information about\n" + "IPv4 Neighbor to display information about\n" + "IPv6 Neighbor to display information about\n" + "Neighbor on BGP configured interface\n" "Display the routes advertised to a BGP neighbor\n" JSON_STR) { - int idx_ipv4 = 0; - int ret; + int idx = 0; struct peer *peer; - union sockunion su; bool uj = use_json(argc, argv); + struct bgp *bgp = NULL; + afi_t afi = AFI_L2VPN; + safi_t safi = SAFI_EVPN; + char *peerstr = NULL; + + if (uj) + argc--; + + bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi, + &bgp, uj); + if (!idx) { + vty_out(vty, "No index\n"); + return CMD_WARNING; + } - argv_find(argv, argc, "A.B.C.D", &idx_ipv4); + /* neighbors */ + argv_find(argv, argc, "neighbors", &idx); + peerstr = argv[++idx]->arg; - ret = str2sockunion(argv[idx_ipv4]->arg, &su); - if (ret < 0) { + peer = peer_lookup_in_view(vty, bgp, peerstr, uj); + if (!peer) { if (uj) { json_object *json_no = NULL; json_no = json_object_new_object(); @@ -1444,10 +1566,9 @@ DEFUN(show_ip_bgp_l2vpn_evpn_all_neighbor_advertised_routes, json_object_free(json_no); } else vty_out(vty, "Malformed address: %s\n", - argv[idx_ipv4]->arg); + argv[idx]->arg); return CMD_WARNING; } - peer = peer_lookup(NULL, &su); if (!peer || !peer->afc[AFI_L2VPN][SAFI_EVPN]) { if (uj) { json_object *json_no = NULL; @@ -1468,7 +1589,7 @@ DEFUN(show_ip_bgp_l2vpn_evpn_all_neighbor_advertised_routes, DEFUN(show_ip_bgp_l2vpn_evpn_rd_neighbor_advertised_routes, show_ip_bgp_l2vpn_evpn_rd_neighbor_advertised_routes_cmd, - "show [ip] bgp l2vpn evpn rd ASN:NN_OR_IP-ADDRESS:NN neighbors A.B.C.D advertised-routes [json]", + "show [ip] bgp l2vpn evpn rd ASN:NN_OR_IP-ADDRESS:NN neighbors advertised-routes [json]", SHOW_STR IP_STR BGP_STR @@ -1477,22 +1598,43 @@ DEFUN(show_ip_bgp_l2vpn_evpn_rd_neighbor_advertised_routes, "Display information for a route distinguisher\n" "VPN Route Distinguisher\n" "Detailed information on TCP and BGP neighbor connections\n" - "Neighbor to display information about\n" + "IPv4 Neighbor to display information about\n" + "IPv6 Neighbor to display information about\n" + "Neighbor on BGP configured interface\n" "Display the routes advertised to a BGP neighbor\n" JSON_STR) { int idx_ext_community = 0; - int idx_ipv4 = 0; + int idx = 0; int ret; struct peer *peer; struct prefix_rd prd; - union sockunion su; + struct bgp *bgp = NULL; bool uj = use_json(argc, argv); + char *peerstr = NULL; + afi_t afi = AFI_L2VPN; + safi_t safi = SAFI_EVPN; + + if (uj) + argc--; + + if (uj) + argc--; + + bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi, + &bgp, uj); + if (!idx) { + vty_out(vty, "No index\n"); + return CMD_WARNING; + } argv_find(argv, argc, "ASN:NN_OR_IP-ADDRESS:NN", &idx_ext_community); - argv_find(argv, argc, "A.B.C.D", &idx_ipv4); - ret = str2sockunion(argv[idx_ipv4]->arg, &su); - if (ret < 0) { + /* neighbors */ + argv_find(argv, argc, "neighbors", &idx); + peerstr = argv[++idx]->arg; + + peer = peer_lookup_in_view(vty, bgp, peerstr, uj); + if (!peer) { if (uj) { json_object *json_no = NULL; json_no = json_object_new_object(); @@ -1503,10 +1645,9 @@ DEFUN(show_ip_bgp_l2vpn_evpn_rd_neighbor_advertised_routes, json_object_free(json_no); } else vty_out(vty, "Malformed address: %s\n", - argv[idx_ext_community]->arg); + argv[idx]->arg); return CMD_WARNING; } - peer = peer_lookup(NULL, &su); if (!peer || !peer->afc[AFI_L2VPN][SAFI_EVPN]) { if (uj) { json_object *json_no = NULL; @@ -1542,14 +1683,15 @@ DEFUN(show_ip_bgp_l2vpn_evpn_rd_neighbor_advertised_routes, DEFUN(show_ip_bgp_l2vpn_evpn_all_overlay, show_ip_bgp_l2vpn_evpn_all_overlay_cmd, - "show [ip] bgp l2vpn evpn all overlay", + "show [ip] bgp l2vpn evpn all overlay [json]", SHOW_STR IP_STR BGP_STR L2VPN_HELP_STR EVPN_HELP_STR "Display information about all EVPN NLRIs\n" - "Display BGP Overlay Information for prefixes\n") + "Display BGP Overlay Information for prefixes\n" + JSON_STR) { return bgp_show_ethernet_vpn(vty, NULL, bgp_show_type_normal, NULL, SHOW_DISPLAY_OVERLAY, @@ -1584,7 +1726,72 @@ DEFUN(show_ip_bgp_evpn_rd_overlay, use_json(argc, argv)); } -/* For testing purpose, static route of MPLS-VPN. */ +DEFUN(show_bgp_l2vpn_evpn_com, + show_bgp_l2vpn_evpn_com_cmd, + "show bgp l2vpn evpn \ + \ + [exact-match] [json]", + SHOW_STR + BGP_STR + L2VPN_HELP_STR + EVPN_HELP_STR + "Display routes matching the community\n" + "Community number where AA and NN are (0-65535)\n" + "Display routes matching the large-community\n" + "List of large-community numbers\n" + "Exact match of the communities\n" + JSON_STR) +{ + int idx = 0; + int ret = 0; + const char *clist_number_or_name; + int show_type = bgp_show_type_normal; + struct community *com; + struct lcommunity *lcom; + + if (argv_find(argv, argc, "large-community", &idx)) { + clist_number_or_name = argv[++idx]->arg; + show_type = bgp_show_type_lcommunity; + + if (++idx < argc && strmatch(argv[idx]->text, "exact-match")) + show_type = bgp_show_type_lcommunity_exact; + + lcom = lcommunity_str2com(clist_number_or_name); + if (!lcom) { + vty_out(vty, "%% Large-community malformed\n"); + return CMD_WARNING; + } + + ret = bgp_show_ethernet_vpn(vty, NULL, show_type, lcom, + SHOW_DISPLAY_STANDARD, + use_json(argc, argv)); + + lcommunity_free(&lcom); + } else if (argv_find(argv, argc, "community", &idx)) { + clist_number_or_name = argv[++idx]->arg; + show_type = bgp_show_type_community; + + if (++idx < argc && strmatch(argv[idx]->text, "exact-match")) + show_type = bgp_show_type_community_exact; + + com = community_str2com(clist_number_or_name); + + if (!com) { + vty_out(vty, "%% Community malformed: %s\n", + clist_number_or_name); + return CMD_WARNING; + } + + ret = bgp_show_ethernet_vpn(vty, NULL, show_type, com, + SHOW_DISPLAY_STANDARD, + use_json(argc, argv)); + community_free(&com); + } + + return ret; +} + +/* For testing purpose, static route of EVPN RT-5. */ DEFUN(evpnrt5_network, evpnrt5_network_cmd, "network rd ASN:NN_OR_IP-ADDRESS:NN ethtag WORD label WORD esi WORD gwip routermac WORD [route-map WORD]", @@ -1623,7 +1830,7 @@ DEFUN(evpnrt5_network, argv[idx_routermac]->arg); } -/* For testing purpose, static route of MPLS-VPN. */ +/* For testing purpose, static route of EVPN RT-5. */ DEFUN(no_evpnrt5_network, no_evpnrt5_network_cmd, "no network rd ASN:NN_OR_IP-ADDRESS:NN ethtag WORD label WORD esi WORD gwip ", @@ -1942,13 +2149,13 @@ static struct bgpevpn *evpn_create_update_vni(struct bgp *bgp, vni_t vni) * appropriate action) and the VNI marked as unconfigured; the * VNI will continue to exist, purely as a "learnt" entity. */ -static int evpn_delete_vni(struct bgp *bgp, struct bgpevpn *vpn) +static void evpn_delete_vni(struct bgp *bgp, struct bgpevpn *vpn) { assert(bgp->vnihash); if (!is_vni_live(vpn)) { bgp_evpn_free(bgp, vpn); - return 0; + return; } /* We need to take the unconfigure action for each parameter of this VNI @@ -1966,8 +2173,6 @@ static int evpn_delete_vni(struct bgp *bgp, struct bgpevpn *vpn) /* Next, deal with the import side. */ if (is_import_rt_configured(vpn)) evpn_unconfigure_import_rt(bgp, vpn, NULL); - - return 0; } /* @@ -2038,7 +2243,7 @@ static void evpn_show_route_vni_multicast(struct vty *vty, struct bgp *bgp, { struct bgpevpn *vpn; struct prefix_evpn p; - struct bgp_node *rn; + struct bgp_dest *dest; struct bgp_path_info *pi; uint32_t path_cnt = 0; afi_t afi; @@ -2057,8 +2262,8 @@ static void evpn_show_route_vni_multicast(struct vty *vty, struct bgp *bgp, /* See if route exists. */ build_evpn_type3_prefix(&p, orig_ip); - rn = bgp_node_lookup(vpn->route_table, (struct prefix *)&p); - if (!rn || !bgp_node_has_bgp_path_info_data(rn)) { + dest = bgp_node_lookup(vpn->route_table, (struct prefix *)&p); + if (!dest || !bgp_dest_has_bgp_path_info_data(dest)) { if (!json) vty_out(vty, "%% Network not in table\n"); return; @@ -2068,17 +2273,16 @@ static void evpn_show_route_vni_multicast(struct vty *vty, struct bgp *bgp, json_paths = json_object_new_array(); /* Prefix and num paths displayed once per prefix. */ - route_vty_out_detail_header(vty, bgp, rn, NULL, afi, safi, json); + route_vty_out_detail_header(vty, bgp, dest, NULL, afi, safi, json); /* Display each path for this prefix. */ - for (pi = bgp_node_get_bgp_path_info(rn); pi; pi = pi->next) { + for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) { json_object *json_path = NULL; if (json) json_path = json_object_new_array(); - route_vty_out_detail(vty, bgp, &rn->p, pi, afi, safi, - json_path); + route_vty_out_detail(vty, bgp, dest, pi, afi, safi, json_path); if (json) json_object_array_add(json_paths, json_path); @@ -2107,7 +2311,7 @@ static void evpn_show_route_vni_macip(struct vty *vty, struct bgp *bgp, { struct bgpevpn *vpn; struct prefix_evpn p; - struct bgp_node *rn; + struct bgp_dest *dest; struct bgp_path_info *pi; uint32_t path_cnt = 0; afi_t afi; @@ -2127,8 +2331,8 @@ static void evpn_show_route_vni_macip(struct vty *vty, struct bgp *bgp, /* See if route exists. Look for both non-sticky and sticky. */ build_evpn_type2_prefix(&p, mac, ip); - rn = bgp_node_lookup(vpn->route_table, (struct prefix *)&p); - if (!rn || !bgp_node_has_bgp_path_info_data(rn)) { + dest = bgp_node_lookup(vpn->route_table, (struct prefix *)&p); + if (!dest || !bgp_dest_has_bgp_path_info_data(dest)) { if (!json) vty_out(vty, "%% Network not in table\n"); return; @@ -2138,17 +2342,16 @@ static void evpn_show_route_vni_macip(struct vty *vty, struct bgp *bgp, json_paths = json_object_new_array(); /* Prefix and num paths displayed once per prefix. */ - route_vty_out_detail_header(vty, bgp, rn, NULL, afi, safi, json); + route_vty_out_detail_header(vty, bgp, dest, NULL, afi, safi, json); /* Display each path for this prefix. */ - for (pi = bgp_node_get_bgp_path_info(rn); pi; pi = pi->next) { + for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) { json_object *json_path = NULL; if (json) json_path = json_object_new_array(); - route_vty_out_detail(vty, bgp, &rn->p, pi, afi, safi, - json_path); + route_vty_out_detail(vty, bgp, dest, pi, afi, safi, json_path); if (json) json_object_array_add(json_paths, json_path); @@ -2171,10 +2374,10 @@ static void evpn_show_route_vni_macip(struct vty *vty, struct bgp *bgp, static void evpn_show_routes_esi(struct vty *vty, struct bgp *bgp, esi_t *esi, json_object *json) { - struct evpnes *es = NULL; + struct bgp_evpn_es *es = NULL; /* locate the ES */ - es = bgp_evpn_lookup_es(bgp, esi); + es = bgp_evpn_es_find(esi); if (!es) { if (!json) vty_out(vty, "ESI not found\n"); @@ -2217,7 +2420,7 @@ static void evpn_show_route_rd_macip(struct vty *vty, struct bgp *bgp, struct ipaddr *ip, json_object *json) { struct prefix_evpn p; - struct bgp_node *rn; + struct bgp_dest *dest; struct bgp_path_info *pi; afi_t afi; safi_t safi; @@ -2230,32 +2433,30 @@ static void evpn_show_route_rd_macip(struct vty *vty, struct bgp *bgp, /* See if route exists. Look for both non-sticky and sticky. */ build_evpn_type2_prefix(&p, mac, ip); - rn = bgp_afi_node_lookup(bgp->rib[afi][safi], afi, safi, - (struct prefix *)&p, prd); - if (!rn || !bgp_node_has_bgp_path_info_data(rn)) { + dest = bgp_afi_node_lookup(bgp->rib[afi][safi], afi, safi, + (struct prefix *)&p, prd); + if (!dest || !bgp_dest_has_bgp_path_info_data(dest)) { if (!json) vty_out(vty, "%% Network not in table\n"); return; } - bgp_evpn_route2str((struct prefix_evpn *)&p, prefix_str, - sizeof(prefix_str)); + bgp_evpn_route2str(&p, prefix_str, sizeof(prefix_str)); /* Prefix and num paths displayed once per prefix. */ - route_vty_out_detail_header(vty, bgp, rn, prd, afi, safi, json); + route_vty_out_detail_header(vty, bgp, dest, prd, afi, safi, json); if (json) json_paths = json_object_new_array(); /* Display each path for this prefix. */ - for (pi = bgp_node_get_bgp_path_info(rn); pi; pi = pi->next) { + for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) { json_object *json_path = NULL; if (json) json_path = json_object_new_array(); - route_vty_out_detail(vty, bgp, &rn->p, pi, afi, safi, - json_path); + route_vty_out_detail(vty, bgp, dest, pi, afi, safi, json_path); if (json) json_object_array_add(json_paths, json_path); @@ -2281,9 +2482,9 @@ static void evpn_show_route_rd(struct vty *vty, struct bgp *bgp, struct prefix_rd *prd, int type, json_object *json) { - struct bgp_node *rd_rn; + struct bgp_dest *rd_dest; struct bgp_table *table; - struct bgp_node *rn; + struct bgp_dest *dest; struct bgp_path_info *pi; int rd_header = 1; afi_t afi; @@ -2297,13 +2498,13 @@ static void evpn_show_route_rd(struct vty *vty, struct bgp *bgp, safi = SAFI_EVPN; prefix_cnt = path_cnt = 0; - prefix_rd2str((struct prefix_rd *)prd, rd_str, sizeof(rd_str)); + prefix_rd2str(prd, rd_str, sizeof(rd_str)); - rd_rn = bgp_node_lookup(bgp->rib[afi][safi], (struct prefix *)prd); - if (!rd_rn) + rd_dest = bgp_node_lookup(bgp->rib[afi][safi], (struct prefix *)prd); + if (!rd_dest) return; - table = bgp_node_get_bgp_table_info(rd_rn); + table = bgp_dest_get_bgp_table_info(rd_dest); if (table == NULL) return; @@ -2313,14 +2514,15 @@ static void evpn_show_route_rd(struct vty *vty, struct bgp *bgp, } /* Display all prefixes with this RD. */ - for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) { - struct prefix_evpn *evp = (struct prefix_evpn *)&rn->p; + for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) { + const struct prefix_evpn *evp = + (const struct prefix_evpn *)bgp_dest_get_prefix(dest); json_object *json_prefix = NULL; json_object *json_paths = NULL; char prefix_str[BUFSIZ]; int add_prefix_to_json = 0; - bgp_evpn_route2str((struct prefix_evpn *)&rn->p, prefix_str, + bgp_evpn_route2str((struct prefix_evpn *)evp, prefix_str, sizeof(prefix_str)); if (type && evp->prefix.route_type != type) @@ -2329,7 +2531,7 @@ static void evpn_show_route_rd(struct vty *vty, struct bgp *bgp, if (json) json_prefix = json_object_new_object(); - pi = bgp_node_get_bgp_path_info(rn); + pi = bgp_dest_get_bgp_path_info(dest); if (pi) { /* RD header and legend - once overall. */ if (rd_header && !json) { @@ -2343,7 +2545,7 @@ static void evpn_show_route_rd(struct vty *vty, struct bgp *bgp, } /* Prefix and num paths displayed once per prefix. */ - route_vty_out_detail_header(vty, bgp, rn, prd, afi, + route_vty_out_detail_header(vty, bgp, dest, prd, afi, safi, json_prefix); prefix_cnt++; @@ -2359,7 +2561,7 @@ static void evpn_show_route_rd(struct vty *vty, struct bgp *bgp, if (json) json_path = json_object_new_array(); - route_vty_out_detail(vty, bgp, &rn->p, pi, afi, safi, + route_vty_out_detail(vty, bgp, dest, pi, afi, safi, json_path); if (json) @@ -2370,18 +2572,29 @@ static void evpn_show_route_rd(struct vty *vty, struct bgp *bgp, add_rd_to_json = 1; } - if (json && add_prefix_to_json) { - json_object_object_add(json_prefix, "paths", - json_paths); - json_object_object_add(json_rd, prefix_str, - json_prefix); + if (json) { + if (add_prefix_to_json) { + json_object_object_add(json_prefix, "paths", + json_paths); + json_object_object_add(json_rd, prefix_str, + json_prefix); + } else { + json_object_free(json_paths); + json_object_free(json_prefix); + json_paths = NULL; + json_prefix = NULL; + } } } - if (json && add_rd_to_json) - json_object_object_add(json, rd_str, json_rd); - if (json) { + if (add_rd_to_json) + json_object_object_add(json, rd_str, json_rd); + else { + json_object_free(json_rd); + json_rd = NULL; + } + json_object_int_add(json, "numPrefix", prefix_cnt); json_object_int_add(json, "numPaths", path_cnt); } else { @@ -2403,9 +2616,9 @@ static void evpn_show_route_rd(struct vty *vty, struct bgp *bgp, static void evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type, json_object *json, int detail) { - struct bgp_node *rd_rn; + struct bgp_dest *rd_dest; struct bgp_table *table; - struct bgp_node *rn; + struct bgp_dest *dest; struct bgp_path_info *pi; int header = detail ? 0 : 1; int rd_header; @@ -2420,58 +2633,66 @@ static void evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type, /* EVPN routing table is a 2-level table with the first level being * the RD. */ - for (rd_rn = bgp_table_top(bgp->rib[afi][safi]); rd_rn; - rd_rn = bgp_route_next(rd_rn)) { + for (rd_dest = bgp_table_top(bgp->rib[afi][safi]); rd_dest; + rd_dest = bgp_route_next(rd_dest)) { char rd_str[RD_ADDRSTRLEN]; json_object *json_rd = NULL; /* contains routes for an RD */ int add_rd_to_json = 0; uint64_t tbl_ver; + const struct prefix *rd_destp = bgp_dest_get_prefix(rd_dest); - table = bgp_node_get_bgp_table_info(rd_rn); + table = bgp_dest_get_bgp_table_info(rd_dest); if (table == NULL) continue; tbl_ver = table->version; - prefix_rd2str((struct prefix_rd *)&rd_rn->p, rd_str, + prefix_rd2str((struct prefix_rd *)rd_destp, rd_str, sizeof(rd_str)); - if (json) { + if (json) json_rd = json_object_new_object(); - json_object_string_add(json_rd, "rd", rd_str); - } rd_header = 1; /* Display all prefixes for an RD */ - for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) { + for (dest = bgp_table_top(table); dest; + dest = bgp_route_next(dest)) { json_object *json_prefix = NULL; /* contains prefix under a RD */ json_object *json_paths = NULL; /* array of paths under a prefix*/ - struct prefix_evpn *evp = (struct prefix_evpn *)&rn->p; + const struct prefix_evpn *evp = + (const struct prefix_evpn *)bgp_dest_get_prefix( + dest); char prefix_str[BUFSIZ]; int add_prefix_to_json = 0; + const struct prefix *p = bgp_dest_get_prefix(dest); - bgp_evpn_route2str((struct prefix_evpn *)&rn->p, - prefix_str, sizeof(prefix_str)); + bgp_evpn_route2str((struct prefix_evpn *)p, prefix_str, + sizeof(prefix_str)); if (type && evp->prefix.route_type != type) continue; - pi = bgp_node_get_bgp_path_info(rn); + pi = bgp_dest_get_bgp_path_info(dest); if (pi) { /* Overall header/legend displayed once. */ if (header) { bgp_evpn_show_route_header(vty, bgp, tbl_ver, json); + if (!json) + vty_out(vty, + "%19s Extended Community\n" + , " "); header = 0; } /* RD header - per RD. */ if (rd_header) { bgp_evpn_show_route_rd_header( - vty, rd_rn, json); + vty, rd_dest, json_rd, rd_str, + RD_ADDRSTRLEN); rd_header = 0; } @@ -2484,15 +2705,15 @@ static void evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type, json_object_string_add(json_prefix, "prefix", prefix_str); json_object_int_add(json_prefix, "prefixLen", - rn->p.prefixlen); + p->prefixlen); } /* Prefix and num paths displayed once per prefix. */ if (detail) route_vty_out_detail_header( - vty, bgp, rn, - (struct prefix_rd *)&rd_rn->p, - AFI_L2VPN, SAFI_EVPN, json_prefix); + vty, bgp, dest, + (struct prefix_rd *)rd_destp, AFI_L2VPN, + SAFI_EVPN, json_prefix); /* For EVPN, the prefix is displayed for each path (to * fit in @@ -2509,27 +2730,42 @@ static void evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type, if (detail) { route_vty_out_detail( - vty, bgp, &rn->p, pi, AFI_L2VPN, + vty, bgp, dest, pi, AFI_L2VPN, SAFI_EVPN, json_path); } else - route_vty_out(vty, &rn->p, pi, 0, - SAFI_EVPN, json_path); + route_vty_out(vty, p, pi, 0, SAFI_EVPN, + json_path, false); if (json) json_object_array_add(json_paths, json_path); } - if (json && add_prefix_to_json) { - json_object_object_add(json_prefix, "paths", - json_paths); - json_object_object_add(json_rd, prefix_str, - json_prefix); + if (json) { + if (add_prefix_to_json) { + json_object_object_add(json_prefix, + "paths", + json_paths); + json_object_object_add(json_rd, + prefix_str, + json_prefix); + } else { + json_object_free(json_prefix); + json_object_free(json_paths); + json_prefix = NULL; + json_paths = NULL; + } } } - if (json && add_rd_to_json) - json_object_object_add(json, rd_str, json_rd); + if (json) { + if (add_rd_to_json) + json_object_object_add(json, rd_str, json_rd); + else { + json_object_free(json_rd); + json_rd = NULL; + } + } } if (json) { @@ -2547,43 +2783,6 @@ static void evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type, } } -/* Display specific ES */ -static void evpn_show_es(struct vty *vty, struct bgp *bgp, esi_t *esi, - json_object *json) -{ - struct evpnes *es = NULL; - - es = bgp_evpn_lookup_es(bgp, esi); - if (es) { - display_es(vty, es, json); - } else { - if (json) { - vty_out(vty, "{}\n"); - } else { - vty_out(vty, "ESI not found\n"); - return; - } - } -} - -/* Display all ESs */ -static void evpn_show_all_es(struct vty *vty, struct bgp *bgp, - json_object *json) -{ - void *args[2]; - - if (!json) - vty_out(vty, "%-30s %-6s %-21s %-15s %-6s\n", - "ESI", "Type", "RD", "Originator-IP", "#VTEPs"); - - /* print all ESs */ - args[0] = vty; - args[1] = json; - hash_iterate(bgp->esihash, - (void (*)(struct hash_bucket *, void *))show_es_entry, - args); -} - /* * Display specified VNI (vty handler) */ @@ -3259,9 +3458,6 @@ DEFPY(bgp_evpn_advertise_svi_ip_vni, if (!bgp) return CMD_WARNING; - if (!vpn) - return CMD_WARNING; - if (no) evpn_set_advertise_svi_macip(bgp, vpn, 0); else @@ -3466,6 +3662,145 @@ DEFUN (no_bgp_evpn_advertise_type5, return CMD_SUCCESS; } +DEFPY (bgp_evpn_advertise_pip_ip_mac, + bgp_evpn_advertise_pip_ip_mac_cmd, + "[no$no] advertise-pip [ip [mac ]]", + NO_STR + "evpn system primary IP\n" + IP_STR + "ip address\n" + MAC_STR MAC_STR MAC_STR) +{ + struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp); /* bgp vrf instance */ + struct bgp *bgp_evpn = NULL; + + if (EVPN_ENABLED(bgp_vrf)) { + vty_out(vty, + "This command is supported under L3VNI BGP EVPN VRF\n"); + return CMD_WARNING_CONFIG_FAILED; + } + bgp_evpn = bgp_get_evpn(); + + if (!no) { + /* pip is already enabled */ + if (argc == 1 && bgp_vrf->evpn_info->advertise_pip) + return CMD_SUCCESS; + + bgp_vrf->evpn_info->advertise_pip = true; + if (ip.s_addr != INADDR_ANY) { + /* Already configured with same IP */ + if (IPV4_ADDR_SAME(&ip, + &bgp_vrf->evpn_info->pip_ip_static)) + return CMD_SUCCESS; + + bgp_vrf->evpn_info->pip_ip_static = ip; + bgp_vrf->evpn_info->pip_ip = ip; + } else { + bgp_vrf->evpn_info->pip_ip_static.s_addr + = INADDR_ANY; + /* default instance router-id assignemt */ + if (bgp_evpn) + bgp_vrf->evpn_info->pip_ip = + bgp_evpn->router_id; + } + /* parse sys mac */ + if (!is_zero_mac(&mac->eth_addr)) { + /* Already configured with same MAC */ + if (memcmp(&bgp_vrf->evpn_info->pip_rmac_static, + &mac->eth_addr, ETH_ALEN) == 0) + return CMD_SUCCESS; + + memcpy(&bgp_vrf->evpn_info->pip_rmac_static, + &mac->eth_addr, ETH_ALEN); + memcpy(&bgp_vrf->evpn_info->pip_rmac, + &bgp_vrf->evpn_info->pip_rmac_static, + ETH_ALEN); + } else { + /* Copy zebra sys mac */ + if (!is_zero_mac(&bgp_vrf->evpn_info->pip_rmac_zebra)) + memcpy(&bgp_vrf->evpn_info->pip_rmac, + &bgp_vrf->evpn_info->pip_rmac_zebra, + ETH_ALEN); + } + } else { + if (argc == 2) { + if (!bgp_vrf->evpn_info->advertise_pip) + return CMD_SUCCESS; + /* Disable PIP feature */ + bgp_vrf->evpn_info->advertise_pip = false; + /* copy anycast mac */ + memcpy(&bgp_vrf->evpn_info->pip_rmac, + &bgp_vrf->rmac, ETH_ALEN); + } else { + /* remove MAC-IP option retain PIP knob. */ + if ((ip.s_addr != INADDR_ANY) && + !IPV4_ADDR_SAME(&ip, + &bgp_vrf->evpn_info->pip_ip_static)) { + vty_out(vty, + "%% BGP EVPN PIP IP does not match\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (!is_zero_mac(&mac->eth_addr) && + memcmp(&bgp_vrf->evpn_info->pip_rmac_static, + &mac->eth_addr, ETH_ALEN) != 0) { + vty_out(vty, + "%% BGP EVPN PIP MAC does not match\n"); + return CMD_WARNING_CONFIG_FAILED; + } + /* pip_rmac can carry vrr_rmac reset only if it matches + * with static value. + */ + if (memcmp(&bgp_vrf->evpn_info->pip_rmac, + &bgp_vrf->evpn_info->pip_rmac_static, + ETH_ALEN) == 0) { + /* Copy zebra sys mac */ + if (!is_zero_mac( + &bgp_vrf->evpn_info->pip_rmac_zebra)) + memcpy(&bgp_vrf->evpn_info->pip_rmac, + &bgp_vrf->evpn_info->pip_rmac_zebra, + ETH_ALEN); + else { + /* copy anycast mac */ + memcpy(&bgp_vrf->evpn_info->pip_rmac, + &bgp_vrf->rmac, ETH_ALEN); + } + } + } + /* reset user configured sys MAC */ + memset(&bgp_vrf->evpn_info->pip_rmac_static, 0, ETH_ALEN); + /* reset user configured sys IP */ + bgp_vrf->evpn_info->pip_ip_static.s_addr = INADDR_ANY; + /* Assign default PIP IP (bgp instance router-id) */ + if (bgp_evpn) + bgp_vrf->evpn_info->pip_ip = bgp_evpn->router_id; + else + bgp_vrf->evpn_info->pip_ip.s_addr = INADDR_ANY; + } + + if (is_evpn_enabled()) { + struct listnode *node = NULL; + struct bgpevpn *vpn = NULL; + + /* + * At this point if bgp_evpn is NULL and evpn is enabled + * something stupid has gone wrong + */ + assert(bgp_evpn); + + update_advertise_vrf_routes(bgp_vrf); + + /* Update (svi) type-2 routes */ + for (ALL_LIST_ELEMENTS_RO(bgp_vrf->l2vnis, node, vpn)) { + if (!bgp_evpn_is_svi_macip_enabled(vpn)) + continue; + update_routes_for_vni(bgp_evpn, vpn); + } + } + + return CMD_SUCCESS; +} + /* * Display VNI information - for all or a specific VNI */ @@ -3517,6 +3852,9 @@ DEFUN(show_bgp_l2vpn_evpn_vni, bgp_evpn->advertise_gw_macip ? "Enabled" : "Disabled"); + json_object_string_add(json, "advertiseSviMacIp", + bgp_evpn->evpn_info->advertise_svi_macip + ? "Enabled" : "Disabled"); json_object_string_add(json, "advertiseAllVnis", is_evpn_enabled() ? "Enabled" : "Disabled"); @@ -3567,55 +3905,50 @@ DEFUN(show_bgp_l2vpn_evpn_vni, return CMD_SUCCESS; } -/* Disaply ES */ -DEFUN(show_bgp_l2vpn_evpn_es, +DEFPY(show_bgp_l2vpn_evpn_es_evi, + show_bgp_l2vpn_evpn_es_evi_cmd, + "show bgp l2vpn evpn es-evi [vni (1-16777215)$vni] [json$uj] [detail$detail]", + SHOW_STR + BGP_STR + L2VPN_HELP_STR + EVPN_HELP_STR + "ES per EVI\n" + "VxLAN Network Identifier\n" + "VNI\n" + JSON_STR + "Detailed information\n") +{ + if (vni) + bgp_evpn_es_evi_show_vni(vty, vni, !!uj, !!detail); + else + bgp_evpn_es_evi_show(vty, !!uj, !!detail); + + return CMD_SUCCESS; +} + +DEFPY(show_bgp_l2vpn_evpn_es, show_bgp_l2vpn_evpn_es_cmd, - "show bgp l2vpn evpn es [ESI] [json]", + "show bgp l2vpn evpn es [NAME$esi_str|detail$detail] [json$uj]", SHOW_STR BGP_STR L2VPN_HELP_STR EVPN_HELP_STR - "ethernet-Segment\n" - "Ethernet-Segment Identifier\n" + "Ethernet Segment\n" + "ES ID\n" + "Detailed information\n" JSON_STR) { - int idx = 0; - bool uj = false; esi_t esi; - json_object *json = NULL; - struct bgp *bgp = NULL; - - memset(&esi, 0, sizeof(esi)); - uj = use_json(argc, argv); - - bgp = bgp_get_evpn(); - if (!bgp) - return CMD_WARNING; - - if (!argv_find(argv, argc, "evpn", &idx)) - return CMD_WARNING; - - if ((uj && argc == ((idx + 1) + 2)) || - (!uj && argc == (idx + 1) + 1)) { - - /* show all ESs */ - evpn_show_all_es(vty, bgp, json); - } else { - /* show a specific ES */ - - /* get the ESI - ESI-ID is at argv[5] */ - if (!str_to_esi(argv[idx + 2]->arg, &esi)) { - vty_out(vty, "%% Malformed ESI\n"); + if (esi_str) { + if (!str_to_esi(esi_str, &esi)) { + vty_out(vty, "%%Malformed ESI\n"); return CMD_WARNING; } - evpn_show_es(vty, bgp, &esi, json); - } + bgp_evpn_es_show_esi(vty, &esi, uj); + } else { - if (uj) { - vty_out(vty, "%s\n", json_object_to_json_string_ext( - json, JSON_C_TO_STRING_PRETTY)); - json_object_free(json); + bgp_evpn_es_show(vty, uj, !!detail); } return CMD_SUCCESS; @@ -3626,7 +3959,7 @@ DEFUN(show_bgp_l2vpn_evpn_es, */ DEFUN(show_bgp_l2vpn_evpn_summary, show_bgp_l2vpn_evpn_summary_cmd, - "show bgp [vrf VRFNAME] l2vpn evpn summary [json]", + "show bgp [vrf VRFNAME] l2vpn evpn summary [established|failed] [json]", SHOW_STR BGP_STR "bgp vrf\n" @@ -3634,15 +3967,25 @@ DEFUN(show_bgp_l2vpn_evpn_summary, L2VPN_HELP_STR EVPN_HELP_STR "Summary of BGP neighbor status\n" + "Show only sessions in Established state\n" + "Show only sessions not in Established state\n" JSON_STR) { int idx_vrf = 0; bool uj = use_json(argc, argv); char *vrf = NULL; + bool show_failed = false; + bool show_established = false; if (argv_find(argv, argc, "vrf", &idx_vrf)) vrf = argv[++idx_vrf]->arg; - return bgp_show_summary_vty(vty, vrf, AFI_L2VPN, SAFI_EVPN, uj); + if (argv_find(argv, argc, "failed", &idx_vrf)) + show_failed = true; + if (argv_find(argv, argc, "established", &idx_vrf)) + show_established = true; + + return bgp_show_summary_vty(vty, vrf, AFI_L2VPN, SAFI_EVPN, show_failed, + show_established, uj); } /* @@ -3650,7 +3993,7 @@ DEFUN(show_bgp_l2vpn_evpn_summary, */ DEFUN(show_bgp_l2vpn_evpn_route, show_bgp_l2vpn_evpn_route_cmd, - "show bgp l2vpn evpn route [detail] [type ] [json]", + "show bgp l2vpn evpn route [detail] [type ] [json]", SHOW_STR BGP_STR L2VPN_HELP_STR @@ -3658,10 +4001,16 @@ DEFUN(show_bgp_l2vpn_evpn_route, "EVPN route information\n" "Display Detailed Information\n" "Specify Route type\n" + "EAD (Type-1) route\n" + "EAD (Type-1) route\n" + "MAC-IP (Type-2) route\n" "MAC-IP (Type-2) route\n" "Multicast (Type-3) route\n" - "Ethernet Segment (type-4) route \n" - "Prefix (type-5 )route\n" + "Multicast (Type-3) route\n" + "Ethernet Segment (Type-4) route\n" + "Ethernet Segment (Type-4) route\n" + "Prefix (Type-5) route\n" + "Prefix (Type-5) route\n" JSON_STR) { struct bgp *bgp; @@ -3683,13 +4032,20 @@ DEFUN(show_bgp_l2vpn_evpn_route, /* get the type */ if (argv_find(argv, argc, "type", &type_idx)) { /* Specific type is requested */ - if (strncmp(argv[type_idx + 1]->arg, "ma", 2) == 0) + if ((strncmp(argv[type_idx + 1]->arg, "ma", 2) == 0) + || (strmatch(argv[type_idx + 1]->arg, "2"))) type = BGP_EVPN_MAC_IP_ROUTE; - else if (strncmp(argv[type_idx + 1]->arg, "mu", 2) == 0) + else if ((strncmp(argv[type_idx + 1]->arg, "mu", 2) == 0) + || (strmatch(argv[type_idx + 1]->arg, "3"))) type = BGP_EVPN_IMET_ROUTE; - else if (strncmp(argv[type_idx + 1]->arg, "es", 2) == 0) + else if ((strncmp(argv[type_idx + 1]->arg, "es", 2) == 0) + || (strmatch(argv[type_idx + 1]->arg, "4"))) type = BGP_EVPN_ES_ROUTE; - else if (strncmp(argv[type_idx + 1]->arg, "pr", 2) == 0) + else if ((strncmp(argv[type_idx + 1]->arg, "ea", 2) == 0) + || (strmatch(argv[type_idx + 1]->arg, "1"))) + type = BGP_EVPN_AD_ROUTE; + else if ((strncmp(argv[type_idx + 1]->arg, "p", 1) == 0) + || (strmatch(argv[type_idx + 1]->arg, "5"))) type = BGP_EVPN_IP_PREFIX_ROUTE; else return CMD_WARNING; @@ -3713,7 +4069,7 @@ DEFUN(show_bgp_l2vpn_evpn_route, */ DEFUN(show_bgp_l2vpn_evpn_route_rd, show_bgp_l2vpn_evpn_route_rd_cmd, - "show bgp l2vpn evpn route rd ASN:NN_OR_IP-ADDRESS:NN [type ] [json]", + "show bgp l2vpn evpn route rd ASN:NN_OR_IP-ADDRESS:NN [type ] [json]", SHOW_STR BGP_STR L2VPN_HELP_STR @@ -3722,6 +4078,7 @@ DEFUN(show_bgp_l2vpn_evpn_route_rd, "Route Distinguisher\n" "ASN:XX or A.B.C.D:XX\n" "Specify Route type\n" + "EAD (Type-1) route\n" "MAC-IP (Type-2) route\n" "Multicast (Type-3) route\n" "Ethernet Segment route\n" @@ -3763,6 +4120,10 @@ DEFUN(show_bgp_l2vpn_evpn_route_rd, type = BGP_EVPN_MAC_IP_ROUTE; else if (strncmp(argv[type_idx + 1]->arg, "mu", 2) == 0) type = BGP_EVPN_IMET_ROUTE; + else if (strncmp(argv[type_idx + 1]->arg, "es", 2) == 0) + type = BGP_EVPN_ES_ROUTE; + else if (strncmp(argv[type_idx + 1]->arg, "ea", 2) == 0) + type = BGP_EVPN_AD_ROUTE; else if (strncmp(argv[type_idx + 1]->arg, "pr", 2) == 0) type = BGP_EVPN_IP_PREFIX_ROUTE; else @@ -3907,7 +4268,7 @@ DEFUN(show_bgp_l2vpn_evpn_route_esi, * Display per-VNI EVPN routing table. */ DEFUN(show_bgp_l2vpn_evpn_route_vni, show_bgp_l2vpn_evpn_route_vni_cmd, - "show bgp l2vpn evpn route vni " CMD_VNI_RANGE " [ | vtep A.B.C.D>] [json]", + "show bgp l2vpn evpn route vni " CMD_VNI_RANGE " [ | vtep A.B.C.D>] [json]", SHOW_STR BGP_STR L2VPN_HELP_STR @@ -3916,6 +4277,7 @@ DEFUN(show_bgp_l2vpn_evpn_route_vni, show_bgp_l2vpn_evpn_route_vni_cmd, "VXLAN Network Identifier\n" "VNI number\n" "Specify Route type\n" + "EAD (Type-1) route\n" "MAC-IP (Type-2) route\n" "Multicast (Type-3) route\n" "Remote VTEP\n" @@ -3953,6 +4315,8 @@ DEFUN(show_bgp_l2vpn_evpn_route_vni, show_bgp_l2vpn_evpn_route_vni_cmd, type = BGP_EVPN_MAC_IP_ROUTE; else if (strncmp(argv[idx + 5]->arg, "mu", 2) == 0) type = BGP_EVPN_IMET_ROUTE; + else if (strncmp(argv[idx + 5]->arg, "ea", 2) == 0) + type = BGP_EVPN_AD_ROUTE; else return CMD_WARNING; } else if (strncmp(argv[idx + 4]->arg, "vtep", 4) == 0) { @@ -4238,17 +4602,22 @@ DEFUN(show_bgp_l2vpn_evpn_import_rt, return CMD_SUCCESS; } -DEFUN(test_adv_evpn_type4_route, - test_adv_evpn_type4_route_cmd, - "advertise es ESI", - "Advertise EVPN ES route\n" +DEFPY_HIDDEN(test_es_add, + test_es_add_cmd, + "[no$no] test es NAME$esi_str [state NAME$state_str]", + NO_STR + "Test\n" "Ethernet-segment\n" - "Ethernet-Segment Identifier\n") + "Ethernet-Segment Identifier\n" + "ES link state\n" + "up|down\n" +) { int ret = 0; esi_t esi; struct bgp *bgp; - struct ipaddr vtep_ip; + struct in_addr vtep_ip; + bool oper_up; bgp = bgp_get_evpn(); if (!bgp) { @@ -4256,33 +4625,47 @@ DEFUN(test_adv_evpn_type4_route, return CMD_WARNING; } - if (!str_to_esi(argv[2]->arg, &esi)) { + if (!str_to_esi(esi_str, &esi)) { vty_out(vty, "%%Malformed ESI\n"); return CMD_WARNING; } - vtep_ip.ipa_type = IPADDR_V4; - vtep_ip.ipaddr_v4 = bgp->router_id; + if (no) { + ret = bgp_evpn_local_es_del(bgp, &esi); + if (ret == -1) { + vty_out(vty, "%%Failed to delete ES\n"); + return CMD_WARNING; + } + } else { + if (state_str && !strcmp(state_str, "up")) + oper_up = true; + else + oper_up = false; + vtep_ip = bgp->router_id; - ret = bgp_evpn_local_es_add(bgp, &esi, &vtep_ip); - if (ret == -1) { - vty_out(vty, "%%Failed to EVPN advertise type-4 route\n"); - return CMD_WARNING; + ret = bgp_evpn_local_es_add(bgp, &esi, vtep_ip, oper_up); + if (ret == -1) { + vty_out(vty, "%%Failed to add ES\n"); + return CMD_WARNING; + } } return CMD_SUCCESS; } -DEFUN(test_withdraw_evpn_type4_route, - test_withdraw_evpn_type4_route_cmd, - "withdraw es ESI", - "Advertise EVPN ES route\n" +DEFPY_HIDDEN(test_es_vni_add, + test_es_vni_add_cmd, + "[no$no] test es NAME$esi_str vni (1-16777215)$vni", + NO_STR + "Test\n" "Ethernet-segment\n" - "Ethernet-Segment Identifier\n") + "Ethernet-Segment Identifier\n" + "VNI\n" + "1-16777215\n" +) { int ret = 0; esi_t esi; struct bgp *bgp; - struct ipaddr vtep_ip; bgp = bgp_get_evpn(); if (!bgp) { @@ -4290,22 +4673,23 @@ DEFUN(test_withdraw_evpn_type4_route, return CMD_WARNING; } - if (!bgp->peer_self) { - vty_out(vty, "%%BGP instance doesn't have self peer\n"); - return CMD_WARNING; - } - - if (!str_to_esi(argv[2]->arg, &esi)) { + if (!str_to_esi(esi_str, &esi)) { vty_out(vty, "%%Malformed ESI\n"); return CMD_WARNING; } - vtep_ip.ipa_type = IPADDR_V4; - vtep_ip.ipaddr_v4 = bgp->router_id; - ret = bgp_evpn_local_es_del(bgp, &esi, &vtep_ip); - if (ret == -1) { - vty_out(vty, "%%Failed to withdraw EVPN type-4 route\n"); - return CMD_WARNING; + if (no) { + ret = bgp_evpn_local_es_evi_del(bgp, &esi, vni); + if (ret == -1) { + vty_out(vty, "%%Failed to deref ES VNI\n"); + return CMD_WARNING; + } + } else { + ret = bgp_evpn_local_es_evi_add(bgp, &esi, vni); + if (ret == -1) { + vty_out(vty, "%%Failed to ref ES VNI\n"); + return CMD_WARNING; + } } return CMD_SUCCESS; } @@ -4916,6 +5300,7 @@ DEFUN (no_bgp_evpn_vrf_rt, if (rt_type == RT_TYPE_IMPORT) { if (!bgp_evpn_rt_matches_existing(bgp->vrf_import_rtl, ecomdel)) { + ecommunity_free(&ecomdel); vty_out(vty, "%% RT specified does not match configuration for this VRF\n"); return CMD_WARNING; @@ -4924,6 +5309,7 @@ DEFUN (no_bgp_evpn_vrf_rt, } else if (rt_type == RT_TYPE_EXPORT) { if (!bgp_evpn_rt_matches_existing(bgp->vrf_export_rtl, ecomdel)) { + ecommunity_free(&ecomdel); vty_out(vty, "%% RT specified does not match configuration for this VRF\n"); return CMD_WARNING; @@ -4945,12 +5331,14 @@ DEFUN (no_bgp_evpn_vrf_rt, } if (!found_ecomdel) { + ecommunity_free(&ecomdel); vty_out(vty, "%% RT specified does not match configuration for this VRF\n"); return CMD_WARNING; } } + ecommunity_free(&ecomdel); return CMD_SUCCESS; } @@ -5191,6 +5579,10 @@ void bgp_config_write_evpn_info(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi) { char buf1[RD_ADDRSTRLEN]; + char buf2[INET6_ADDRSTRLEN]; + + if (bgp->advertise_all_vni) + vty_out(vty, " advertise-all-vni\n"); if (bgp->vnihash) { struct list *vnilist = hash_to_list(bgp->vnihash); @@ -5204,9 +5596,6 @@ void bgp_config_write_evpn_info(struct vty *vty, struct bgp *bgp, afi_t afi, list_delete(&vnilist); } - if (bgp->advertise_all_vni) - vty_out(vty, " advertise-all-vni\n"); - if (bgp->advertise_autort_rfc8365) vty_out(vty, " autort rfc8365-compatible\n"); @@ -5265,6 +5654,30 @@ void bgp_config_write_evpn_info(struct vty *vty, struct bgp *bgp, afi_t afi, BGP_L2VPN_EVPN_DEFAULT_ORIGINATE_IPV6)) vty_out(vty, " default-originate ipv6\n"); + if (bgp->inst_type == BGP_INSTANCE_TYPE_VRF) { + if (!bgp->evpn_info->advertise_pip) + vty_out(vty, " no advertise-pip\n"); + if (bgp->evpn_info->advertise_pip) { + if (bgp->evpn_info->pip_ip_static.s_addr + != INADDR_ANY) { + vty_out(vty, " advertise-pip ip %s", + inet_ntop(AF_INET, + &bgp->evpn_info->pip_ip_static, + buf2, INET_ADDRSTRLEN)); + if (!is_zero_mac(&( + bgp->evpn_info->pip_rmac_static))) { + char buf[ETHER_ADDR_STRLEN]; + + vty_out(vty, " mac %s", + prefix_mac2str( + &bgp->evpn_info + ->pip_rmac, + buf, sizeof(buf))); + } + vty_out(vty, "\n"); + } + } + } if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_RD_CFGD)) vty_out(vty, " rd %s\n", prefix_rd2str(&bgp->vrf_prd, buf1, sizeof(buf1))); @@ -5307,12 +5720,12 @@ void bgp_ethernetvpn_init(void) install_element(VIEW_NODE, &show_ip_bgp_l2vpn_evpn_all_tags_cmd); install_element(VIEW_NODE, &show_ip_bgp_l2vpn_evpn_rd_tags_cmd); install_element(VIEW_NODE, - &show_ip_bgp_l2vpn_evpn_all_neighbor_routes_cmd); + &show_ip_bgp_l2vpn_evpn_neighbor_routes_cmd); install_element(VIEW_NODE, &show_ip_bgp_l2vpn_evpn_rd_neighbor_routes_cmd); install_element( VIEW_NODE, - &show_ip_bgp_l2vpn_evpn_all_neighbor_advertised_routes_cmd); + &show_ip_bgp_l2vpn_evpn_neighbor_advertised_routes_cmd); install_element( VIEW_NODE, &show_ip_bgp_l2vpn_evpn_rd_neighbor_advertised_routes_cmd); @@ -5335,13 +5748,15 @@ void bgp_ethernetvpn_init(void) install_element(BGP_EVPN_NODE, &dup_addr_detection_auto_recovery_cmd); install_element(BGP_EVPN_NODE, &no_dup_addr_detection_cmd); install_element(BGP_EVPN_NODE, &bgp_evpn_flood_control_cmd); + install_element(BGP_EVPN_NODE, &bgp_evpn_advertise_pip_ip_mac_cmd); /* test commands */ - install_element(BGP_EVPN_NODE, &test_adv_evpn_type4_route_cmd); - install_element(BGP_EVPN_NODE, &test_withdraw_evpn_type4_route_cmd); + install_element(BGP_EVPN_NODE, &test_es_add_cmd); + install_element(BGP_EVPN_NODE, &test_es_vni_add_cmd); /* "show bgp l2vpn evpn" commands. */ install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_es_cmd); + install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_es_evi_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_vni_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_summary_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_cmd); @@ -5368,6 +5783,7 @@ void bgp_ethernetvpn_init(void) install_element(VIEW_NODE, &show_bgp_evpn_route_vni_all_cmd); install_element(VIEW_NODE, &show_bgp_evpn_import_rt_cmd); install_element(VIEW_NODE, &show_bgp_vrf_l3vni_info_cmd); + install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_com_cmd); install_element(BGP_EVPN_NODE, &bgp_evpn_vni_cmd); install_element(BGP_EVPN_NODE, &no_bgp_evpn_vni_cmd); diff --git a/bgpd/bgp_filter.c b/bgpd/bgp_filter.c index d4f608d40f..3162579688 100644 --- a/bgpd/bgp_filter.c +++ b/bgpd/bgp_filter.c @@ -168,10 +168,7 @@ static struct as_list *as_list_new(void) static void as_list_free(struct as_list *aslist) { - if (aslist->name) { - XFREE(MTYPE_AS_STR, aslist->name); - aslist->name = NULL; - } + XFREE(MTYPE_AS_STR, aslist->name); XFREE(MTYPE_AS_LIST, aslist); } @@ -193,7 +190,7 @@ static struct as_list *as_list_insert(const char *name) /* If name is made by all digit character. We treat it as number. */ for (number = 0, i = 0; i < strlen(name); i++) { - if (isdigit((int)name[i])) + if (isdigit((unsigned char)name[i])) number = (number * 10) + (name[i] - '0'); else break; @@ -305,12 +302,9 @@ static void as_list_delete(struct as_list *aslist) as_list_free(aslist); } -static int as_list_empty(struct as_list *aslist) +static bool as_list_empty(struct as_list *aslist) { - if (aslist->head == NULL && aslist->tail == NULL) - return 1; - else - return 0; + return aslist->head == NULL && aslist->tail == NULL; } static void as_list_filter_delete(struct as_list *aslist, @@ -340,11 +334,9 @@ static void as_list_filter_delete(struct as_list *aslist, XFREE(MTYPE_AS_STR, name); } -static int as_filter_match(struct as_filter *asfilter, struct aspath *aspath) +static bool as_filter_match(struct as_filter *asfilter, struct aspath *aspath) { - if (bgp_regexec(asfilter->reg, aspath) != REG_NOMATCH) - return 1; - return 0; + return bgp_regexec(asfilter->reg, aspath) != REG_NOMATCH; } /* Apply AS path filter to AS. */ @@ -377,26 +369,25 @@ void as_list_delete_hook(void (*func)(const char *)) as_list_master.delete_hook = func; } -static int as_list_dup_check(struct as_list *aslist, struct as_filter *new) +static bool as_list_dup_check(struct as_list *aslist, struct as_filter *new) { struct as_filter *asfilter; for (asfilter = aslist->head; asfilter; asfilter = asfilter->next) { if (asfilter->type == new->type && strcmp(asfilter->reg_str, new->reg_str) == 0) - return 1; + return true; } - return 0; + return false; } -int config_bgp_aspath_validate(const char *regstr) +bool config_bgp_aspath_validate(const char *regstr) { char valid_chars[] = "1234567890_^|[,{}() ]$*+.?-\\"; if (strspn(regstr, valid_chars) == strlen(regstr)) - return 1; - - return 0; + return true; + return false; } DEFUN(as_path, bgp_as_path_cmd, @@ -416,13 +407,6 @@ DEFUN(as_path, bgp_as_path_cmd, regex_t *regex; char *regstr; - if (argv_find(argv, argc, "ip", &idx)) { - vty_out(vty, "This config option is deprecated and is scheduled for removal.\n"); - vty_out(vty, "if you are using this please migrate to the below command\n"); - vty_out(vty, "'bgp as-path access-list WORD LINE'\n"); - zlog_warn("Deprecated option: 'ip as-path access-list WORD LINE' being used"); - } - /* Retrieve access list name */ argv_find(argv, argc, "WORD", &idx); char *alname = argv[idx]->arg; @@ -464,19 +448,6 @@ DEFUN(as_path, bgp_as_path_cmd, return CMD_SUCCESS; } -#if CONFDATE > 20191005 -CPP_NOTICE("bgpd: remove deprecated 'ip as-path access-list WORD LINE' command") -#endif -ALIAS(as_path, ip_as_path_cmd, - "ip as-path access-list WORD LINE...", - IP_STR - "BGP autonomous system path filter\n" - "Specify an access list name\n" - "Regular expression access list name\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "A regular-expression (1234567890_^|[,{}() ]$*+.?-\\) to match the BGP AS paths\n") - DEFUN(no_as_path, no_bgp_as_path_cmd, "no bgp as-path access-list WORD LINE...", NO_STR @@ -495,12 +466,6 @@ DEFUN(no_as_path, no_bgp_as_path_cmd, char *regstr; regex_t *regex; - if (argv_find(argv, argc, "ip", &idx)) { - vty_out(vty, "This config option is deprecated, and is scheduled for removal.\n"); - vty_out(vty, "if you are using this please migrate to the below command\n"); - vty_out(vty, "'no bgp as-path access-list WORD LINE'\n"); - zlog_warn("Deprecated option: 'no ip as-path access-list WORD LINE' being used"); - } char *aslistname = argv_find(argv, argc, "WORD", &idx) ? argv[idx]->arg : NULL; @@ -542,29 +507,21 @@ DEFUN(no_as_path, no_bgp_as_path_cmd, /* Lookup asfilter. */ asfilter = as_filter_lookup(aslist, regstr, type); - XFREE(MTYPE_TMP, regstr); bgp_regex_free(regex); if (asfilter == NULL) { - vty_out(vty, "\n"); + vty_out(vty, "Regex entered %s does not exist\n", regstr); + XFREE(MTYPE_TMP, regstr); return CMD_WARNING_CONFIG_FAILED; } + XFREE(MTYPE_TMP, regstr); + as_list_filter_delete(aslist, asfilter); return CMD_SUCCESS; } -ALIAS(no_as_path, no_ip_as_path_cmd, - "no ip as-path access-list WORD LINE...", - NO_STR IP_STR - "BGP autonomous system path filter\n" - "Specify an access list name\n" - "Regular expression access list name\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "A regular-expression (1234567890_^|[,{}() ]$*+.?-\\) to match the BGP AS paths\n") - DEFUN (no_as_path_all, no_bgp_as_path_all_cmd, "no bgp as-path access-list WORD", @@ -576,14 +533,6 @@ DEFUN (no_as_path_all, { int idx_word = 4; struct as_list *aslist; - int idx = 0; - - if (argv_find(argv, argc, "ip", &idx)) { - vty_out(vty, "This config option is deprecated, and is scheduled for removal.\n"); - vty_out(vty, "if you are using this please migrate to the below command\n"); - vty_out(vty, "'no bgp as-path access-list WORD'\n"); - zlog_warn("Deprecated option: `no ip as-path access-list WORD` being used"); - } aslist = as_list_lookup(argv[idx_word]->arg); if (aslist == NULL) { @@ -601,15 +550,6 @@ DEFUN (no_as_path_all, return CMD_SUCCESS; } -ALIAS (no_as_path_all, - no_ip_as_path_all_cmd, - "no ip as-path access-list WORD", - NO_STR - IP_STR - "BGP autonomous system path filter\n" - "Specify an access list name\n" - "Regular expression access list name\n") - static void as_list_show(struct vty *vty, struct as_list *aslist) { struct as_filter *asfilter; @@ -660,14 +600,7 @@ DEFUN (show_as_path_access_list, { int idx_word = 3; struct as_list *aslist; - int idx = 0; - if (argv_find(argv, argc, "ip", &idx)) { - vty_out(vty, "This config option is deprecated, and is scheduled for removal.\n"); - vty_out(vty, "if you are using this please migrate to the below command\n"); - vty_out(vty, "'show bgp as-path-access-list WORD'\n"); - zlog_warn("Deprecated option: 'show ip as-path-access-list WORD' being used"); - } aslist = as_list_lookup(argv[idx_word]->arg); if (aslist) as_list_show(vty, aslist); @@ -690,14 +623,6 @@ DEFUN (show_as_path_access_list_all, BGP_STR "List AS path access lists\n") { - int idx = 0; - - if (argv_find(argv, argc, "ip", &idx)) { - vty_out(vty, "This config option is deprecated, and is scheduled for removal.\n"); - vty_out(vty, "if you are using this please migrate to the below command\n"); - vty_out(vty, "'show bgp as-path-access-list'\n"); - zlog_warn("Deprecated option: 'show ip as-path-access-list' being used"); - } as_list_show_all(vty); return CMD_SUCCESS; } @@ -735,19 +660,22 @@ static int config_write_as_list(struct vty *vty) return write; } -static struct cmd_node as_list_node = {AS_LIST_NODE, "", 1}; +static int config_write_as_list(struct vty *vty); +static struct cmd_node as_list_node = { + .name = "as list", + .node = AS_LIST_NODE, + .prompt = "", + .config_write = config_write_as_list, +}; /* Register functions. */ void bgp_filter_init(void) { - install_node(&as_list_node, config_write_as_list); + install_node(&as_list_node); install_element(CONFIG_NODE, &bgp_as_path_cmd); - install_element(CONFIG_NODE, &ip_as_path_cmd); install_element(CONFIG_NODE, &no_bgp_as_path_cmd); - install_element(CONFIG_NODE, &no_ip_as_path_cmd); install_element(CONFIG_NODE, &no_bgp_as_path_all_cmd); - install_element(CONFIG_NODE, &no_ip_as_path_all_cmd); install_element(VIEW_NODE, &show_bgp_as_path_access_list_cmd); install_element(VIEW_NODE, &show_ip_as_path_access_list_cmd); diff --git a/bgpd/bgp_filter.h b/bgpd/bgp_filter.h index 3c49e357ff..9357a2d382 100644 --- a/bgpd/bgp_filter.h +++ b/bgpd/bgp_filter.h @@ -31,6 +31,6 @@ extern enum as_filter_type as_list_apply(struct as_list *, void *); extern struct as_list *as_list_lookup(const char *); extern void as_list_add_hook(void (*func)(char *)); extern void as_list_delete_hook(void (*func)(const char *)); -extern int config_bgp_aspath_validate(const char *regstr); +extern bool config_bgp_aspath_validate(const char *regstr); #endif /* _QUAGGA_BGP_FILTER_H */ diff --git a/bgpd/bgp_flowspec.c b/bgpd/bgp_flowspec.c index 9554638735..341cfe9d07 100644 --- a/bgpd/bgp_flowspec.c +++ b/bgpd/bgp_flowspec.c @@ -33,7 +33,8 @@ #include "bgpd/bgp_debug.h" #include "bgpd/bgp_errors.h" -static int bgp_fs_nlri_validate(uint8_t *nlri_content, uint32_t len) +static int bgp_fs_nlri_validate(uint8_t *nlri_content, uint32_t len, + afi_t afi) { uint32_t offset = 0; int type; @@ -48,7 +49,15 @@ static int bgp_fs_nlri_validate(uint8_t *nlri_content, uint32_t len) ret = bgp_flowspec_ip_address( BGP_FLOWSPEC_VALIDATE_ONLY, nlri_content + offset, - len - offset, NULL, &error); + len - offset, NULL, &error, + afi, NULL); + break; + case FLOWSPEC_FLOW_LABEL: + if (afi == AFI_IP) + return -1; + ret = bgp_flowspec_op_decode(BGP_FLOWSPEC_VALIDATE_ONLY, + nlri_content + offset, + len - offset, NULL, &error); break; case FLOWSPEC_IP_PROTOCOL: case FLOWSPEC_PORT: @@ -103,12 +112,7 @@ int bgp_nlri_parse_flowspec(struct peer *peer, struct attr *attr, afi = packet->afi; safi = packet->safi; - if (afi == AFI_IP6) { - flog_err(EC_LIB_DEVELOPMENT, "BGP flowspec IPv6 not supported"); - return BGP_NLRI_PARSE_ERROR_FLOWSPEC_IPV6_NOT_SUPPORTED; - } - - if (packet->length >= FLOWSPEC_NLRI_SIZELIMIT) { + if (packet->length >= FLOWSPEC_NLRI_SIZELIMIT_EXTENDED) { flog_err(EC_BGP_FLOWSPEC_PACKET, "BGP flowspec nlri length maximum reached (%u)", packet->length); @@ -124,7 +128,11 @@ int bgp_nlri_parse_flowspec(struct peer *peer, struct attr *attr, return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW; psize = *pnt++; - + if (psize >= FLOWSPEC_NLRI_SIZELIMIT) { + psize &= 0x0f; + psize = psize << 8; + psize |= *pnt++; + } /* When packet overflow occur return immediately. */ if (pnt + psize > lim) { flog_err( @@ -133,7 +141,7 @@ int bgp_nlri_parse_flowspec(struct peer *peer, struct attr *attr, psize); return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW; } - if (bgp_fs_nlri_validate(pnt, psize) < 0) { + if (bgp_fs_nlri_validate(pnt, psize, afi) < 0) { flog_err( EC_BGP_FLOWSPEC_PACKET, "Bad flowspec format or NLRI options not supported"); @@ -143,6 +151,7 @@ int bgp_nlri_parse_flowspec(struct peer *peer, struct attr *attr, p.prefixlen = 0; /* Flowspec encoding is in bytes */ p.u.prefix_flowspec.prefixlen = psize; + p.u.prefix_flowspec.family = afi2family(afi); temp = XCALLOC(MTYPE_TMP, psize); memcpy(temp, pnt, psize); p.u.prefix_flowspec.ptr = (uintptr_t) temp; @@ -157,7 +166,8 @@ int bgp_nlri_parse_flowspec(struct peer *peer, struct attr *attr, p.u.prefix_flowspec.ptr, p.u.prefix_flowspec.prefixlen, return_string, - NLRI_STRING_FORMAT_MIN, NULL); + NLRI_STRING_FORMAT_MIN, NULL, + afi); snprintf(ec_string, sizeof(ec_string), "EC{none}"); if (attr && attr->ecommunity) { diff --git a/bgpd/bgp_flowspec.h b/bgpd/bgp_flowspec.h index bc201b739f..d1a59a672d 100644 --- a/bgpd/bgp_flowspec.h +++ b/bgpd/bgp_flowspec.h @@ -41,9 +41,10 @@ extern int bgp_show_table_flowspec(struct vty *vty, struct bgp *bgp, afi_t afi, extern void bgp_fs_nlri_get_string(unsigned char *nlri_content, size_t len, char *return_string, int format, - json_object *json_path); + json_object *json_path, + afi_t afi); -extern void route_vty_out_flowspec(struct vty *vty, struct prefix *p, +extern void route_vty_out_flowspec(struct vty *vty, const struct prefix *p, struct bgp_path_info *path, int display, json_object *json_paths); extern int bgp_fs_config_write_pbr(struct vty *vty, struct bgp *bgp, diff --git a/bgpd/bgp_flowspec_private.h b/bgpd/bgp_flowspec_private.h index dede4e03d3..757a8ae2e9 100644 --- a/bgpd/bgp_flowspec_private.h +++ b/bgpd/bgp_flowspec_private.h @@ -20,6 +20,7 @@ #define _FRR_BGP_FLOWSPEC_PRIVATE_H #define FLOWSPEC_NLRI_SIZELIMIT 240 +#define FLOWSPEC_NLRI_SIZELIMIT_EXTENDED 4095 /* Flowspec raffic action bit*/ #define FLOWSPEC_TRAFFIC_ACTION_TERMINAL 1 @@ -40,5 +41,6 @@ #define FLOWSPEC_PKT_LEN 10 #define FLOWSPEC_DSCP 11 #define FLOWSPEC_FRAGMENT 12 +#define FLOWSPEC_FLOW_LABEL 13 /* For IPv6 only */ #endif /* _FRR_BGP_FLOWSPEC_PRIVATE_H */ diff --git a/bgpd/bgp_flowspec_util.c b/bgpd/bgp_flowspec_util.c index b9a0d81cc5..90e9236385 100644 --- a/bgpd/bgp_flowspec_util.c +++ b/bgpd/bgp_flowspec_util.c @@ -76,15 +76,16 @@ static int bgp_flowspec_call_non_opaque_decode(uint8_t *nlri_content, int len, return ret; } -bool bgp_flowspec_contains_prefix(struct prefix *pfs, - struct prefix *input, - int prefix_check) + +bool bgp_flowspec_contains_prefix(const struct prefix *pfs, + struct prefix *input, int prefix_check) { uint32_t offset = 0; int type; int ret = 0, error = 0; uint8_t *nlri_content = (uint8_t *)pfs->u.prefix_flowspec.ptr; size_t len = pfs->u.prefix_flowspec.prefixlen; + afi_t afi = family2afi(pfs->u.prefix_flowspec.family); struct prefix compare; error = 0; @@ -99,7 +100,8 @@ bool bgp_flowspec_contains_prefix(struct prefix *pfs, BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE, nlri_content+offset, len - offset, - &compare, &error); + &compare, &error, + afi, NULL); if (ret <= 0) break; if (prefix_check && @@ -116,6 +118,16 @@ bool bgp_flowspec_contains_prefix(struct prefix *pfs, &compare.u.prefix6.s6_addr)) return true; break; + case FLOWSPEC_FLOW_LABEL: + if (afi == AFI_IP) { + error = -1; + continue; + } + ret = bgp_flowspec_op_decode(BGP_FLOWSPEC_VALIDATE_ONLY, + nlri_content+offset, + len - offset, + NULL, &error); + break; case FLOWSPEC_IP_PROTOCOL: case FLOWSPEC_PORT: case FLOWSPEC_DEST_PORT: @@ -159,13 +171,16 @@ bool bgp_flowspec_contains_prefix(struct prefix *pfs, int bgp_flowspec_ip_address(enum bgp_flowspec_util_nlri_t type, uint8_t *nlri_ptr, uint32_t max_len, - void *result, int *error) + void *result, int *error, + afi_t afi, + uint8_t *ipv6_offset) { char *display = (char *)result; /* for return_string */ struct prefix *prefix = (struct prefix *)result; uint32_t offset = 0; struct prefix prefix_local; int psize; + uint8_t prefix_offset = 0; *error = 0; memset(&prefix_local, 0, sizeof(struct prefix)); @@ -173,8 +188,13 @@ int bgp_flowspec_ip_address(enum bgp_flowspec_util_nlri_t type, prefix_local.prefixlen = nlri_ptr[offset]; psize = PSIZE(prefix_local.prefixlen); offset++; - /* TODO Flowspec IPv6 Support */ - prefix_local.family = AF_INET; + prefix_local.family = afi2family(afi); + if (prefix_local.family == AF_INET6) { + prefix_offset = nlri_ptr[offset]; + if (ipv6_offset) + *ipv6_offset = prefix_offset; + offset++; + } /* Prefix length check. */ if (prefix_local.prefixlen > prefix_blen(&prefix_local) * 8) *error = -1; @@ -190,11 +210,30 @@ int bgp_flowspec_ip_address(enum bgp_flowspec_util_nlri_t type, offset += psize; switch (type) { case BGP_FLOWSPEC_RETURN_STRING: - prefix2str(&prefix_local, display, - BGP_FLOWSPEC_STRING_DISPLAY_MAX); + if (prefix_local.family == AF_INET6) { + char str[BGP_FLOWSPEC_STRING_DISPLAY_MAX]; + int ret; + + prefix2str(&prefix_local, str, + BGP_FLOWSPEC_STRING_DISPLAY_MAX); + ret = snprintf(display, BGP_FLOWSPEC_STRING_DISPLAY_MAX, + "%s/off %u", + str, prefix_offset); + if (ret < 0) { + *error = -1; + break; + } + } else + prefix2str(&prefix_local, display, + BGP_FLOWSPEC_STRING_DISPLAY_MAX); break; case BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE: - PREFIX_COPY_IPV4(prefix, &prefix_local) + if (prefix) { + if (prefix_local.family == AF_INET) + PREFIX_COPY_IPV4(prefix, &prefix_local) + else + PREFIX_COPY_IPV6(prefix, &prefix_local) + } break; case BGP_FLOWSPEC_VALIDATE_ONLY: default: @@ -418,7 +457,8 @@ int bgp_flowspec_bitmask_decode(enum bgp_flowspec_util_nlri_t type, } int bgp_flowspec_match_rules_fill(uint8_t *nlri_content, int len, - struct bgp_pbr_entry_main *bpem) + struct bgp_pbr_entry_main *bpem, + afi_t afi) { int offset = 0, error = 0; struct prefix *prefix; @@ -426,6 +466,7 @@ int bgp_flowspec_match_rules_fill(uint8_t *nlri_content, int len, uint8_t *match_num; uint8_t bitmask = 0; int ret = 0, type; + uint8_t *prefix_offset; while (offset < len - 1 && error >= 0) { type = nlri_content[offset]; @@ -437,15 +478,18 @@ int bgp_flowspec_match_rules_fill(uint8_t *nlri_content, int len, if (type == FLOWSPEC_DEST_PREFIX) { bitmask |= PREFIX_DST_PRESENT; prefix = &bpem->dst_prefix; + prefix_offset = &bpem->dst_prefix_offset; } else { bitmask |= PREFIX_SRC_PRESENT; prefix = &bpem->src_prefix; + prefix_offset = &bpem->src_prefix_offset; } ret = bgp_flowspec_ip_address( BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE, nlri_content + offset, len - offset, - prefix, &error); + prefix, &error, + afi, prefix_offset); if (error < 0) flog_err(EC_BGP_FLOWSPEC_PACKET, "%s: flowspec_ip_address error %d", @@ -455,13 +499,32 @@ int bgp_flowspec_match_rules_fill(uint8_t *nlri_content, int len, * ignore that rule */ if (prefix->family == AF_INET - && prefix->u.prefix4.s_addr == 0) + && prefix->u.prefix4.s_addr == INADDR_ANY) + bpem->match_bitmask_iprule |= bitmask; + else if (prefix->family == AF_INET6 + && !memcmp(&prefix->u.prefix6, + &in6addr_any, + sizeof(struct in6_addr))) bpem->match_bitmask_iprule |= bitmask; else bpem->match_bitmask |= bitmask; } offset += ret; break; + case FLOWSPEC_FLOW_LABEL: + if (afi == AFI_IP) { + error = -1; + continue; + } + match_num = &(bpem->match_flowlabel_num); + mval = (struct bgp_pbr_match_val *) + &(bpem->flow_label); + offset += bgp_flowspec_call_non_opaque_decode( + nlri_content + offset, + len - offset, + mval, match_num, + &error); + break; case FLOWSPEC_IP_PROTOCOL: match_num = &(bpem->match_protocol_num); mval = (struct bgp_pbr_match_val *) @@ -584,7 +647,8 @@ int bgp_flowspec_match_rules_fill(uint8_t *nlri_content, int len, bpem->match_packet_length_num || bpem->match_icmp_code_num || bpem->match_icmp_type_num || bpem->match_port_num || bpem->match_src_port_num || bpem->match_dst_port_num || - bpem->match_protocol_num || bpem->match_bitmask) + bpem->match_protocol_num || bpem->match_bitmask || + bpem->match_flowlabel_num) bpem->type = BGP_PBR_IPSET; else if ((bpem->match_bitmask_iprule & PREFIX_SRC_PRESENT) || (bpem->match_bitmask_iprule & PREFIX_DST_PRESENT)) @@ -599,25 +663,33 @@ int bgp_flowspec_match_rules_fill(uint8_t *nlri_content, int len, } /* return 1 if FS entry invalid or no NH IP */ -int bgp_flowspec_get_first_nh(struct bgp *bgp, struct bgp_path_info *pi, - struct prefix *p) +bool bgp_flowspec_get_first_nh(struct bgp *bgp, struct bgp_path_info *pi, + struct prefix *p, afi_t afi) { struct bgp_pbr_entry_main api; int i; - struct bgp_node *rn = pi->net; + struct bgp_dest *dest = pi->net; struct bgp_pbr_entry_action *api_action; memset(&api, 0, sizeof(struct bgp_pbr_entry_main)); - if (bgp_pbr_build_and_validate_entry(&rn->p, pi, &api) < 0) - return 1; + if (bgp_pbr_build_and_validate_entry(bgp_dest_get_prefix(dest), pi, + &api) + < 0) + return true; for (i = 0; i < api.action_num; i++) { api_action = &api.actions[i]; if (api_action->action != ACTION_REDIRECT_IP) continue; - p->family = AF_INET; - p->prefixlen = IPV4_MAX_BITLEN; - p->u.prefix4 = api_action->u.zr.redirect_ip_v4; - return 0; + p->family = afi2family(afi); + if (afi == AFI_IP) { + p->prefixlen = IPV4_MAX_BITLEN; + p->u.prefix4 = api_action->u.zr.redirect_ip_v4; + } else { + p->prefixlen = IPV6_MAX_BITLEN; + memcpy(&p->u.prefix6, &api_action->u.zr.redirect_ip_v6, + sizeof(struct in6_addr)); + } + return false; } - return 1; + return true; } diff --git a/bgpd/bgp_flowspec_util.h b/bgpd/bgp_flowspec_util.h index 2ce911da4e..6cc4854d7f 100644 --- a/bgpd/bgp_flowspec_util.h +++ b/bgpd/bgp_flowspec_util.h @@ -39,7 +39,8 @@ extern int bgp_flowspec_op_decode(enum bgp_flowspec_util_nlri_t type, extern int bgp_flowspec_ip_address(enum bgp_flowspec_util_nlri_t type, uint8_t *nlri_ptr, uint32_t max_len, - void *result, int *error); + void *result, int *error, + afi_t afi, uint8_t *ipv6_offset); extern int bgp_flowspec_bitmask_decode(enum bgp_flowspec_util_nlri_t type, uint8_t *nlri_ptr, @@ -48,14 +49,14 @@ extern int bgp_flowspec_bitmask_decode(enum bgp_flowspec_util_nlri_t type, struct bgp_pbr_entry_main; extern int bgp_flowspec_match_rules_fill(uint8_t *nlri_content, int len, - struct bgp_pbr_entry_main *bpem); + struct bgp_pbr_entry_main *bpem, + afi_t afi); -extern bool bgp_flowspec_contains_prefix(struct prefix *pfs, +extern bool bgp_flowspec_contains_prefix(const struct prefix *pfs, struct prefix *input, int prefix_check); -extern int bgp_flowspec_get_first_nh(struct bgp *bgp, - struct bgp_path_info *pi, - struct prefix *nh); +extern bool bgp_flowspec_get_first_nh(struct bgp *bgp, struct bgp_path_info *pi, + struct prefix *nh, afi_t afi); #endif /* _FRR_BGP_FLOWSPEC_UTIL_H */ diff --git a/bgpd/bgp_flowspec_vty.c b/bgpd/bgp_flowspec_vty.c index 2d6523ed31..3b7fb649a9 100644 --- a/bgpd/bgp_flowspec_vty.c +++ b/bgpd/bgp_flowspec_vty.c @@ -50,6 +50,7 @@ static const struct message bgp_flowspec_display_large[] = { {FLOWSPEC_PKT_LEN, "Packet Length"}, {FLOWSPEC_DSCP, "DSCP field"}, {FLOWSPEC_FRAGMENT, "Packet Fragment"}, + {FLOWSPEC_FLOW_LABEL, "Packet Flow Label"}, {0} }; @@ -66,6 +67,7 @@ static const struct message bgp_flowspec_display_min[] = { {FLOWSPEC_PKT_LEN, "pktlen"}, {FLOWSPEC_DSCP, "dscp"}, {FLOWSPEC_FRAGMENT, "pktfrag"}, + {FLOWSPEC_FLOW_LABEL, "flwlbl"}, {0} }; @@ -93,7 +95,8 @@ static const struct message bgp_flowspec_display_min[] = { */ void bgp_fs_nlri_get_string(unsigned char *nlri_content, size_t len, char *return_string, int format, - json_object *json_path) + json_object *json_path, + afi_t afi) { uint32_t offset = 0; int type; @@ -127,7 +130,8 @@ void bgp_fs_nlri_get_string(unsigned char *nlri_content, size_t len, type_util, nlri_content+offset, len - offset, - local_string, &error); + local_string, &error, + afi, NULL); if (ret <= 0) break; if (json_path) { @@ -145,6 +149,7 @@ void bgp_fs_nlri_get_string(unsigned char *nlri_content, size_t len, len_string -= len_written; ptr += len_written; break; + case FLOWSPEC_FLOW_LABEL: case FLOWSPEC_IP_PROTOCOL: case FLOWSPEC_PORT: case FLOWSPEC_DEST_PORT: @@ -252,72 +257,100 @@ void bgp_fs_nlri_get_string(unsigned char *nlri_content, size_t len, } } -void route_vty_out_flowspec(struct vty *vty, struct prefix *p, +void route_vty_out_flowspec(struct vty *vty, const struct prefix *p, struct bgp_path_info *path, int display, json_object *json_paths) { struct attr *attr; char return_string[BGP_FLOWSPEC_STRING_DISPLAY_MAX]; - char *s; + char *s1 = NULL, *s2 = NULL; json_object *json_nlri_path = NULL; json_object *json_ecom_path = NULL; json_object *json_time_path = NULL; char timebuf[BGP_UPTIME_LEN]; - /* Print prefix */ - if (p != NULL) { - if (p->family != AF_FLOWSPEC) - return; - if (json_paths) { - if (display == NLRI_STRING_FORMAT_JSON) - json_nlri_path = json_object_new_object(); - else - json_nlri_path = json_paths; - } - if (display == NLRI_STRING_FORMAT_LARGE && path) - vty_out(vty, "BGP flowspec entry: (flags 0x%x)\n", - path->flags); - bgp_fs_nlri_get_string((unsigned char *) - p->u.prefix_flowspec.ptr, - p->u.prefix_flowspec.prefixlen, - return_string, - display, - json_nlri_path); - if (display == NLRI_STRING_FORMAT_LARGE) - vty_out(vty, "%s", return_string); - else if (display == NLRI_STRING_FORMAT_DEBUG) - vty_out(vty, "%s", return_string); - else if (display == NLRI_STRING_FORMAT_MIN) - vty_out(vty, " %-30s", return_string); - else if (json_paths && display == NLRI_STRING_FORMAT_JSON) - json_object_array_add(json_paths, json_nlri_path); + if (p == NULL || p->family != AF_FLOWSPEC) + return; + if (json_paths) { + if (display == NLRI_STRING_FORMAT_JSON) + json_nlri_path = json_object_new_object(); + else + json_nlri_path = json_paths; } + if (display == NLRI_STRING_FORMAT_LARGE && path) + vty_out(vty, "BGP flowspec entry: (flags 0x%x)\n", + path->flags); + bgp_fs_nlri_get_string((unsigned char *) + p->u.prefix_flowspec.ptr, + p->u.prefix_flowspec.prefixlen, + return_string, + display, + json_nlri_path, + family2afi(p->u.prefix_flowspec + .family)); + if (display == NLRI_STRING_FORMAT_LARGE) + vty_out(vty, "%s", return_string); + else if (display == NLRI_STRING_FORMAT_DEBUG) + vty_out(vty, "%s", return_string); + else if (display == NLRI_STRING_FORMAT_MIN) + vty_out(vty, " %-30s", return_string); + else if (json_paths && display == NLRI_STRING_FORMAT_JSON) + json_object_array_add(json_paths, json_nlri_path); if (!path) return; - if (path->attr && path->attr->ecommunity) { + if (path->attr && + (path->attr->ecommunity || path->attr->ipv6_ecommunity)) { /* Print attribute */ attr = path->attr; - s = ecommunity_ecom2str(attr->ecommunity, - ECOMMUNITY_FORMAT_ROUTE_MAP, 0); - if (!s) + if (attr->ecommunity) + s1 = ecommunity_ecom2str(attr->ecommunity, + ECOMMUNITY_FORMAT_ROUTE_MAP, 0); + if (attr->ipv6_ecommunity) + s2 = ecommunity_ecom2str(attr->ipv6_ecommunity, + ECOMMUNITY_FORMAT_ROUTE_MAP, 0); + if (!s1 && !s2) return; if (display == NLRI_STRING_FORMAT_LARGE) - vty_out(vty, "\t%s\n", s); + vty_out(vty, "\t%s%s%s\n", s1 ? s1 : "", + s2 && s1 ? " " : "", s2 ? s2 : ""); else if (display == NLRI_STRING_FORMAT_MIN) - vty_out(vty, "%s", s); + vty_out(vty, "%s%s", s1 ? s1 : "", s2 ? s2 : ""); else if (json_paths) { json_ecom_path = json_object_new_object(); - json_object_string_add(json_ecom_path, - "ecomlist", s); + if (s1) + json_object_string_add(json_ecom_path, + "ecomlist", s1); + if (s2) + json_object_string_add(json_ecom_path, + "ecom6list", s2); if (display == NLRI_STRING_FORMAT_JSON) json_object_array_add(json_paths, json_ecom_path); } - if (attr->nexthop.s_addr != 0 && - display == NLRI_STRING_FORMAT_LARGE) - vty_out(vty, "\tNLRI NH %-16s\n", - inet_ntoa(attr->nexthop)); - XFREE(MTYPE_ECOMMUNITY_STR, s); + if (display == NLRI_STRING_FORMAT_LARGE) { + char local_buff[INET6_ADDRSTRLEN]; + + local_buff[0] = '\0'; + if (p->u.prefix_flowspec.family == AF_INET && + attr->nexthop.s_addr != 0) + inet_ntop(AF_INET, + &attr->nexthop.s_addr, + local_buff, + INET6_ADDRSTRLEN); + else if (p->u.prefix_flowspec.family == AF_INET6 && + attr->mp_nexthop_len != 0 && + attr->mp_nexthop_len != BGP_ATTR_NHLEN_IPV4 && + attr->mp_nexthop_len != BGP_ATTR_NHLEN_VPNV4) + inet_ntop(AF_INET6, + &attr->mp_nexthop_global, + local_buff, + INET6_ADDRSTRLEN); + if (local_buff[0] != '\0') + vty_out(vty, "\tNLRI NH %s\n", + local_buff); + } + XFREE(MTYPE_ECOMMUNITY_STR, s1); + XFREE(MTYPE_ECOMMUNITY_STR, s2); } peer_uptime(path->uptime, timebuf, BGP_UPTIME_LEN, 0, NULL); if (display == NLRI_STRING_FORMAT_LARGE) { @@ -376,11 +409,10 @@ void route_vty_out_flowspec(struct vty *vty, struct prefix *p, bpr->priority, bpr->action->table_id); } - if (list_began) - vty_out(vty, ")"); - vty_out(vty, "\n"); } - if (!list_began) + if (list_began) + vty_out(vty, ")\n"); + else vty_out(vty, "\tnot installed in PBR\n"); } } @@ -391,7 +423,7 @@ int bgp_show_table_flowspec(struct vty *vty, struct bgp *bgp, afi_t afi, unsigned long *output_cum, unsigned long *total_cum) { struct bgp_path_info *pi; - struct bgp_node *rn; + struct bgp_dest *dest; unsigned long total_count = 0; json_object *json_paths = NULL; int display = NLRI_STRING_FORMAT_LARGE; @@ -399,8 +431,8 @@ int bgp_show_table_flowspec(struct vty *vty, struct bgp *bgp, afi_t afi, if (type != bgp_show_type_detail) return CMD_SUCCESS; - for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) { - pi = bgp_node_get_bgp_path_info(rn); + for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) { + pi = bgp_dest_get_bgp_path_info(dest); if (pi == NULL) continue; if (use_json) { @@ -409,8 +441,8 @@ int bgp_show_table_flowspec(struct vty *vty, struct bgp *bgp, afi_t afi, } for (; pi; pi = pi->next) { total_count++; - route_vty_out_flowspec(vty, &rn->p, pi, display, - json_paths); + route_vty_out_flowspec(vty, bgp_dest_get_prefix(dest), + pi, display, json_paths); } if (use_json) { vty_out(vty, "%s\n", @@ -470,10 +502,17 @@ int bgp_fs_config_write_pbr(struct vty *vty, struct bgp *bgp, struct bgp_pbr_interface_head *head; bool bgp_pbr_interface_any; - if (!bgp_pbr_cfg || safi != SAFI_FLOWSPEC || afi != AFI_IP) + if (!bgp_pbr_cfg || safi != SAFI_FLOWSPEC) + return 0; + if (afi == AFI_IP) { + head = &(bgp_pbr_cfg->ifaces_by_name_ipv4); + bgp_pbr_interface_any = bgp_pbr_cfg->pbr_interface_any_ipv4; + } else if (afi == AFI_IP6) { + head = &(bgp_pbr_cfg->ifaces_by_name_ipv6); + bgp_pbr_interface_any = bgp_pbr_cfg->pbr_interface_any_ipv6; + } else { return 0; - head = &(bgp_pbr_cfg->ifaces_by_name_ipv4); - bgp_pbr_interface_any = bgp_pbr_cfg->pbr_interface_any_ipv4; + } if (!RB_EMPTY(bgp_pbr_interface_head, head) || !bgp_pbr_interface_any) declare_node = true; @@ -484,7 +523,8 @@ int bgp_fs_config_write_pbr(struct vty *vty, struct bgp *bgp, } static int bgp_fs_local_install_interface(struct bgp *bgp, - const char *no, const char *ifname) + const char *no, const char *ifname, + afi_t afi) { struct bgp_pbr_interface *pbr_if; struct bgp_pbr_config *bgp_pbr_cfg = bgp->bgp_pbr_cfg; @@ -493,14 +533,19 @@ static int bgp_fs_local_install_interface(struct bgp *bgp, if (!bgp_pbr_cfg) return CMD_SUCCESS; - head = &(bgp_pbr_cfg->ifaces_by_name_ipv4); - bgp_pbr_interface_any = &(bgp_pbr_cfg->pbr_interface_any_ipv4); + if (afi == AFI_IP) { + head = &(bgp_pbr_cfg->ifaces_by_name_ipv4); + bgp_pbr_interface_any = &(bgp_pbr_cfg->pbr_interface_any_ipv4); + } else { + head = &(bgp_pbr_cfg->ifaces_by_name_ipv6); + bgp_pbr_interface_any = &(bgp_pbr_cfg->pbr_interface_any_ipv6); + } if (no) { if (!ifname) { if (*bgp_pbr_interface_any) { *bgp_pbr_interface_any = false; /* remove all other interface list */ - bgp_pbr_reset(bgp, AFI_IP); + bgp_pbr_reset(bgp, afi); } return CMD_SUCCESS; } @@ -524,7 +569,7 @@ static int bgp_fs_local_install_interface(struct bgp *bgp, if (!*bgp_pbr_interface_any) { /* remove all other interface list */ - bgp_pbr_reset(bgp, AFI_IP); + bgp_pbr_reset(bgp, afi); *bgp_pbr_interface_any = true; } } @@ -544,7 +589,8 @@ DEFUN (bgp_fs_local_install_ifname, char *ifname = argv_find(argv, argc, "INTERFACE", &idx) ? argv[idx]->arg : NULL; - return bgp_fs_local_install_interface(bgp, no, ifname); + return bgp_fs_local_install_interface(bgp, no, ifname, + bgp_node_afi(vty)); } extern int bgp_flowspec_display_match_per_ip(afi_t afi, struct bgp_table *rib, @@ -553,19 +599,19 @@ extern int bgp_flowspec_display_match_per_ip(afi_t afi, struct bgp_table *rib, bool use_json, json_object *json_paths) { - struct bgp_node *rn; - struct prefix *prefix; + struct bgp_dest *dest; + const struct prefix *prefix; int display = 0; - for (rn = bgp_table_top(rib); rn; rn = bgp_route_next(rn)) { - prefix = &rn->p; + for (dest = bgp_table_top(rib); dest; dest = bgp_route_next(dest)) { + prefix = bgp_dest_get_prefix(dest); if (prefix->family != AF_FLOWSPEC) continue; if (bgp_flowspec_contains_prefix(prefix, match, prefix_check)) { route_vty_out_flowspec( - vty, &rn->p, bgp_node_get_bgp_path_info(rn), + vty, prefix, bgp_dest_get_bgp_path_info(dest), use_json ? NLRI_STRING_FORMAT_JSON : NLRI_STRING_FORMAT_LARGE, json_paths); @@ -582,4 +628,5 @@ void bgp_flowspec_vty_init(void) install_element(ENABLE_NODE, &no_debug_bgp_flowspec_cmd); install_element(CONFIG_NODE, &no_debug_bgp_flowspec_cmd); install_element(BGP_FLOWSPECV4_NODE, &bgp_fs_local_install_ifname_cmd); + install_element(BGP_FLOWSPECV6_NODE, &bgp_fs_local_install_ifname_cmd); } diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c index 9e37a60188..14dcf2b593 100644 --- a/bgpd/bgp_fsm.c +++ b/bgpd/bgp_fsm.c @@ -35,7 +35,7 @@ #include "filter.h" #include "command.h" #include "lib_errors.h" - +#include "zclient.h" #include "lib/json.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_attr.h" @@ -57,12 +57,12 @@ #include "bgpd/bgp_zebra.h" DEFINE_HOOK(peer_backward_transition, (struct peer * peer), (peer)) -DEFINE_HOOK(peer_established, (struct peer * peer), (peer)) - +DEFINE_HOOK(peer_status_changed, (struct peer * peer), (peer)) +extern const char *get_afi_safi_str(afi_t afi, safi_t safi, bool for_json); /* Definition of display strings corresponding to FSM events. This should be * kept consistent with the events defined in bgpd.h */ -static const char *bgp_event_str[] = { +static const char *const bgp_event_str[] = { NULL, "BGP_Start", "BGP_Stop", @@ -96,6 +96,21 @@ static int bgp_holdtime_timer(struct thread *); /* BGP FSM functions. */ static int bgp_start(struct peer *); +/* Register peer with NHT */ +static int bgp_peer_reg_with_nht(struct peer *peer) +{ + int connected = 0; + + if (peer->sort == BGP_PEER_EBGP && peer->ttl == BGP_DEFAULT_TTL + && !CHECK_FLAG(peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK) + && !CHECK_FLAG(peer->bgp->flags, BGP_FLAG_DISABLE_NH_CONNECTED_CHK)) + connected = 1; + + return bgp_find_or_add_nexthop( + peer->bgp, peer->bgp, family2afi(peer->su.sa.sa_family), + NULL, peer, connected); +} + static void peer_xfer_stats(struct peer *peer_dst, struct peer *peer_src) { /* Copy stats over. These are only the pre-established state stats */ @@ -115,8 +130,8 @@ static struct peer *peer_xfer_conn(struct peer *from_peer) afi_t afi; safi_t safi; int fd; - int status, pstatus; - unsigned char last_evt, last_maj_evt; + enum bgp_fsm_status status, pstatus; + enum bgp_fsm_events last_evt, last_maj_evt; assert(from_peer != NULL); @@ -149,6 +164,14 @@ static struct peer *peer_xfer_conn(struct peer *from_peer) bgp_writes_off(from_peer); bgp_reads_off(from_peer); + /* + * Before exchanging FD remove doppelganger from + * keepalive peer hash. It could be possible conf peer + * fd is set to -1. If blocked on lock then keepalive + * thread can access peer pointer with fd -1. + */ + bgp_keepalives_off(from_peer); + BGP_TIMER_OFF(peer->t_routeadv); BGP_TIMER_OFF(peer->t_connect); BGP_TIMER_OFF(peer->t_connect_check_r); @@ -164,9 +187,7 @@ static struct peer *peer_xfer_conn(struct peer *from_peer) * on various buffers. Those need to be transferred or dropped, * otherwise we'll get spurious failures during session establishment. */ - pthread_mutex_lock(&peer->io_mtx); - pthread_mutex_lock(&from_peer->io_mtx); - { + frr_with_mutex(&peer->io_mtx, &from_peer->io_mtx) { fd = peer->fd; peer->fd = from_peer->fd; from_peer->fd = fd; @@ -184,9 +205,11 @@ static struct peer *peer_xfer_conn(struct peer *from_peer) EC_BGP_PKT_PROCESS, "[%s] Dropping pending packet on connection transfer:", peer->host); - uint16_t type = stream_getc_from(peer->curr, - BGP_MARKER_SIZE + 2); - bgp_dump_packet(peer, type, peer->curr); + /* there used to be a bgp_packet_dump call here, but + * that's extremely confusing since there's no way to + * identify the packet in MRT dumps or BMP as dropped + * due to connection transfer. + */ stream_free(peer->curr); peer->curr = NULL; } @@ -205,8 +228,6 @@ static struct peer *peer_xfer_conn(struct peer *from_peer) ringbuf_copy(peer->ibuf_work, from_peer->ibuf_work, ringbuf_remain(from_peer->ibuf_work)); } - pthread_mutex_unlock(&from_peer->io_mtx); - pthread_mutex_unlock(&peer->io_mtx); peer->as = from_peer->as; peer->v_holdtime = from_peer->v_holdtime; @@ -227,6 +248,23 @@ static struct peer *peer_xfer_conn(struct peer *from_peer) from_peer->last_event = last_evt; from_peer->last_major_event = last_maj_evt; peer->remote_id = from_peer->remote_id; + peer->last_reset = from_peer->last_reset; + + peer->peer_gr_present_state = from_peer->peer_gr_present_state; + peer->peer_gr_new_status_flag = from_peer->peer_gr_new_status_flag; + bgp_peer_gr_flags_update(peer); + + BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(peer->bgp, + peer->bgp->peer); + + if (bgp_peer_gr_mode_get(peer) == PEER_DISABLE) { + + UNSET_FLAG(peer->sflags, PEER_STATUS_NSF_MODE); + + if (CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT)) { + peer_nsf_stop(peer); + } + } if (from_peer->hostname != NULL) { if (peer->hostname) { @@ -266,8 +304,8 @@ static struct peer *peer_xfer_conn(struct peer *from_peer) ? "accept" : ""), peer->host, peer->fd, from_peer->fd); - bgp_stop(peer); - bgp_stop(from_peer); + BGP_EVENT_ADD(peer, BGP_Stop); + BGP_EVENT_ADD(from_peer, BGP_Stop); return NULL; } if (from_peer->status > Active) { @@ -291,6 +329,11 @@ static struct peer *peer_xfer_conn(struct peer *from_peer) if (from_peer) peer_xfer_stats(peer, from_peer); + /* Register peer for NHT. This is to allow RAs to be enabled when + * needed, even on a passive connection. + */ + bgp_peer_reg_with_nht(peer); + bgp_reads_on(peer); bgp_writes_on(peer); thread_add_timer_msec(bm->master, bgp_process_packet, peer, 0, @@ -413,6 +456,10 @@ void bgp_timer_set(struct peer *peer) bgp_keepalives_off(peer); BGP_TIMER_OFF(peer->t_routeadv); break; + case BGP_STATUS_MAX: + flog_err(EC_LIB_DEVELOPMENT, + "BGP_STATUS_MAX while a legal state is not valid state for the FSM"); + break; } } @@ -423,7 +470,6 @@ static int bgp_start_timer(struct thread *thread) struct peer *peer; peer = THREAD_ARG(thread); - peer->t_start = NULL; if (bgp_debug_neighbor_events(peer)) zlog_debug("%s [FSM] Timer (start timer expire).", peer->host); @@ -445,8 +491,6 @@ static int bgp_connect_timer(struct thread *thread) assert(!peer->t_write); assert(!peer->t_read); - peer->t_connect = NULL; - if (bgp_debug_neighbor_events(peer)) zlog_debug("%s [FSM] Timer (connect timer expire)", peer->host); @@ -465,15 +509,34 @@ static int bgp_connect_timer(struct thread *thread) /* BGP holdtime timer. */ static int bgp_holdtime_timer(struct thread *thread) { + atomic_size_t inq_count; struct peer *peer; peer = THREAD_ARG(thread); - peer->t_holdtime = NULL; if (bgp_debug_neighbor_events(peer)) zlog_debug("%s [FSM] Timer (holdtime timer expire)", peer->host); + /* + * Given that we do not have any expectation of ordering + * for handling packets from a peer -vs- handling + * the hold timer for a peer as that they are both + * events on the peer. If we have incoming + * data on the peers inq, let's give the system a chance + * to handle that data. This can be especially true + * for systems where we are heavily loaded for one + * reason or another. + */ + inq_count = atomic_load_explicit(&peer->ibuf->count, + memory_order_relaxed); + if (inq_count) { + BGP_TIMER_ON(peer->t_holdtime, bgp_holdtime_timer, + peer->v_holdtime); + + return 0; + } + THREAD_VAL(thread) = Hold_Timer_expired; bgp_event(thread); /* bgp_event unlocks peer */ @@ -485,7 +548,6 @@ int bgp_routeadv_timer(struct thread *thread) struct peer *peer; peer = THREAD_ARG(thread); - peer->t_routeadv = NULL; if (bgp_debug_neighbor_events(peer)) zlog_debug("%s [FSM] Timer (routeadv timer expire)", @@ -503,7 +565,7 @@ int bgp_routeadv_timer(struct thread *thread) } /* BGP Peer Down Cause */ -const char *peer_down_str[] = {"", +const char *const peer_down_str[] = {"", "Router ID changed", "Remote AS changed", "Local AS change", @@ -529,7 +591,14 @@ const char *peer_down_str[] = {"", "Intf peering v6only config change", "BFD down received", "Interface down", - "Neighbor address lost"}; + "Neighbor address lost", + "Waiting for NHT", + "Waiting for Peer IPv6 LLA", + "Waiting for VRF to be initialized", + "No AFI/SAFI activated for peer", + "AS Set config change", + "Waiting for peer OPEN", + "Reached received prefix count"}; static int bgp_graceful_restart_timer_expire(struct thread *thread) { @@ -538,7 +607,6 @@ static int bgp_graceful_restart_timer_expire(struct thread *thread) safi_t safi; peer = THREAD_ARG(thread); - peer->t_gr_restart = NULL; /* NSF delete stale route */ for (afi = AFI_IP; afi < AFI_MAX; afi++) @@ -567,7 +635,6 @@ static int bgp_graceful_stale_timer_expire(struct thread *thread) safi_t safi; peer = THREAD_ARG(thread); - peer->t_gr_stale = NULL; if (bgp_debug_neighbor_events(peer)) zlog_debug("%s graceful restart stalepath timer expired", @@ -582,32 +649,55 @@ static int bgp_graceful_stale_timer_expire(struct thread *thread) return 0; } -static int bgp_update_delay_applicable(struct bgp *bgp) +/* Selection deferral timer processing function */ +static int bgp_graceful_deferral_timer_expire(struct thread *thread) +{ + struct afi_safi_info *info; + afi_t afi; + safi_t safi; + struct bgp *bgp; + + info = THREAD_ARG(thread); + afi = info->afi; + safi = info->safi; + bgp = info->bgp; + + if (BGP_DEBUG(update, UPDATE_OUT)) + zlog_debug( + "afi %d, safi %d : graceful restart deferral timer expired", + afi, safi); + + bgp->gr_info[afi][safi].eor_required = 0; + bgp->gr_info[afi][safi].eor_received = 0; + XFREE(MTYPE_TMP, info); + + /* Best path selection */ + return bgp_best_path_select_defer(bgp, afi, safi); +} + +static bool bgp_update_delay_applicable(struct bgp *bgp) { /* update_delay_over flag should be reset (set to 0) for any new applicability of the update-delay during BGP process lifetime. And it should be set after an occurence of the update-delay is over)*/ if (!bgp->update_delay_over) - return 1; - - return 0; + return true; + return false; } -int bgp_update_delay_active(struct bgp *bgp) +bool bgp_update_delay_active(struct bgp *bgp) { if (bgp->t_update_delay) - return 1; - - return 0; + return true; + return false; } -int bgp_update_delay_configured(struct bgp *bgp) +bool bgp_update_delay_configured(struct bgp *bgp) { if (bgp->v_update_delay) - return 1; - - return 0; + return true; + return false; } /* Do the post-processing needed when bgp comes out of the read-only mode @@ -758,28 +848,25 @@ void bgp_adjust_routeadv(struct peer *peer) } } -static int bgp_maxmed_onstartup_applicable(struct bgp *bgp) +static bool bgp_maxmed_onstartup_applicable(struct bgp *bgp) { if (!bgp->maxmed_onstartup_over) - return 1; - - return 0; + return true; + return false; } -int bgp_maxmed_onstartup_configured(struct bgp *bgp) +bool bgp_maxmed_onstartup_configured(struct bgp *bgp) { if (bgp->v_maxmed_onstartup != BGP_MAXMED_ONSTARTUP_UNCONFIGURED) - return 1; - - return 0; + return true; + return false; } -int bgp_maxmed_onstartup_active(struct bgp *bgp) +bool bgp_maxmed_onstartup_active(struct bgp *bgp) { if (bgp->t_maxmed_onstartup) - return 1; - - return 0; + return true; + return false; } void bgp_maxmed_update(struct bgp *bgp) @@ -807,6 +894,27 @@ void bgp_maxmed_update(struct bgp *bgp) } } +int bgp_fsm_error_subcode(int status) +{ + int fsm_err_subcode = BGP_NOTIFY_FSM_ERR_SUBCODE_UNSPECIFIC; + + switch (status) { + case OpenSent: + fsm_err_subcode = BGP_NOTIFY_FSM_ERR_SUBCODE_OPENSENT; + break; + case OpenConfirm: + fsm_err_subcode = BGP_NOTIFY_FSM_ERR_SUBCODE_OPENCONFIRM; + break; + case Established: + fsm_err_subcode = BGP_NOTIFY_FSM_ERR_SUBCODE_ESTABLISHED; + break; + default: + break; + } + + return fsm_err_subcode; +} + /* The maxmed onstartup timer expiry callback. */ static int bgp_maxmed_onstartup_timer(struct thread *thread) { @@ -939,8 +1047,6 @@ void bgp_fsm_change_status(struct peer *peer, int status) struct bgp *bgp; uint32_t peer_count; - bgp_dump_state(peer, peer->status, status); - bgp = peer->bgp; peer_count = bgp->established_peers; @@ -949,9 +1055,15 @@ void bgp_fsm_change_status(struct peer *peer, int status) else if ((peer->status == Established) && (status != Established)) bgp->established_peers--; - if (BGP_DEBUG(neighbor_events, NEIGHBOR_EVENTS)) - zlog_debug("%s : vrf %u, established_peers %u", __func__, - bgp->vrf_id, bgp->established_peers); + if (bgp_debug_neighbor_events(peer)) { + struct vrf *vrf = vrf_lookup_by_id(bgp->vrf_id); + + zlog_debug("%s : vrf %s(%u), Status: %s established_peers %u", __func__, + vrf ? vrf->name : "Unknown", bgp->vrf_id, + lookup_msg(bgp_status_msg, status, NULL), + bgp->established_peers); + } + /* Set to router ID to the value provided by RIB if there are no peers * in the established state and peer count did not change */ @@ -993,9 +1105,19 @@ void bgp_fsm_change_status(struct peer *peer, int status) peer->ostatus = peer->status; peer->status = status; + /* Reset received keepalives counter on every FSM change */ + peer->rtt_keepalive_rcv = 0; + + /* Fire backward transition hook if that's the case */ + if (peer->ostatus > peer->status) + hook_call(peer_backward_transition, peer); + /* Save event that caused status change. */ peer->last_major_event = peer->cur_event; + /* Operations after status change */ + hook_call(peer_status_changed, peer); + if (status == Established) UNSET_FLAG(peer->sflags, PEER_STATUS_ACCEPT_PEER); @@ -1038,6 +1160,14 @@ int bgp_stop(struct peer *peer) safi_t safi; char orf_name[BUFSIZ]; int ret = 0; + struct bgp *bgp = peer->bgp; + struct graceful_restart_info *gr_info = NULL; + + peer->nsf_af_count = 0; + + /* deregister peer */ + if (peer->last_reset == PEER_DOWN_UPDATE_SOURCE_CHANGE) + bgp_bfd_deregister_peer(peer); if (peer_dynamic_neighbor(peer) && !(CHECK_FLAG(peer->flags, PEER_FLAG_DELETE))) { @@ -1058,8 +1188,10 @@ int bgp_stop(struct peer *peer) peer->dropped++; /* bgp log-neighbor-changes of neighbor Down */ - if (bgp_flag_check(peer->bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES)) { + if (CHECK_FLAG(peer->bgp->flags, + BGP_FLAG_LOG_NEIGHBOR_CHANGES)) { struct vrf *vrf = vrf_lookup_by_id(peer->bgp->vrf_id); + zlog_info( "%%ADJCHANGE: neighbor %s(%s) in vrf %s Down %s", peer->host, @@ -1103,6 +1235,38 @@ int bgp_stop(struct peer *peer) peer->nsf[afi][safi] = 0; } + /* If peer reset before receiving EOR, decrement EOR count and + * cancel the selection deferral timer if there are no + * pending EOR messages to be received + */ + if (BGP_PEER_GRACEFUL_RESTART_CAPABLE(peer)) { + FOREACH_AFI_SAFI (afi, safi) { + if (!peer->afc_nego[afi][safi] + || CHECK_FLAG(peer->af_sflags[afi][safi], + PEER_STATUS_EOR_RECEIVED)) + continue; + + gr_info = &bgp->gr_info[afi][safi]; + if (!gr_info) + continue; + + if (gr_info->eor_required) + gr_info->eor_required--; + + if (BGP_DEBUG(update, UPDATE_OUT)) + zlog_debug("peer %s, EOR_required %d", + peer->host, + gr_info->eor_required); + + /* There is no pending EOR message */ + if (gr_info->eor_required == 0) { + BGP_TIMER_OFF( + gr_info->t_select_deferral); + gr_info->eor_received = 0; + } + } + } + /* set last reset time */ peer->resettime = peer->uptime = bgp_clock(); @@ -1111,12 +1275,8 @@ int bgp_stop(struct peer *peer) peer->host); update_group_remove_peer_afs(peer); - hook_call(peer_backward_transition, peer); - /* Reset peer synctime */ peer->synctime = 0; - - bgp_bfd_deregister_peer(peer); } /* stop keepalives */ @@ -1136,8 +1296,7 @@ int bgp_stop(struct peer *peer) BGP_TIMER_OFF(peer->t_routeadv); /* Clear input and output buffer. */ - pthread_mutex_lock(&peer->io_mtx); - { + frr_with_mutex(&peer->io_mtx) { if (peer->ibuf) stream_fifo_clean(peer->ibuf); if (peer->obuf) @@ -1153,7 +1312,6 @@ int bgp_stop(struct peer *peer) peer->curr = NULL; } } - pthread_mutex_unlock(&peer->io_mtx); /* Close of file descriptor. */ if (peer->fd >= 0) { @@ -1179,7 +1337,8 @@ int bgp_stop(struct peer *peer) if ((peer->status == OpenConfirm) || (peer->status == Established)) { /* ORF received prefix-filter pnt */ - sprintf(orf_name, "%s.%d.%d", peer->host, afi, safi); + snprintf(orf_name, sizeof(orf_name), "%s.%d.%d", + peer->host, afi, safi); prefix_bgp_orf_remove_all(afi, orf_name); } } @@ -1215,7 +1374,6 @@ int bgp_stop(struct peer *peer) } else { bgp_peer_conf_if_to_su_update(peer); } - return ret; } @@ -1298,7 +1456,7 @@ static int bgp_connect_check(struct thread *thread) /* If getsockopt is fail, this is fatal error. */ if (ret < 0) { - zlog_info("can't get sockopt for nonblocking connect: %d(%s)", + zlog_err("can't get sockopt for nonblocking connect: %d(%s)", errno, safe_strerror(errno)); BGP_EVENT_ADD(peer, TCP_fatal_error); return -1; @@ -1332,9 +1490,9 @@ static int bgp_connect_success(struct peer *peer) if (bgp_getsockname(peer) < 0) { flog_err_sys(EC_LIB_SOCKET, "%s: bgp_getsockname(): failed for peer %s, fd %d", - __FUNCTION__, peer->host, peer->fd); + __func__, peer->host, peer->fd); bgp_notify_send(peer, BGP_NOTIFY_FSM_ERR, - 0); /* internal error */ + bgp_fsm_error_subcode(peer->status)); bgp_writes_on(peer); return -1; } @@ -1376,7 +1534,6 @@ static int bgp_connect_fail(struct peer *peer) int bgp_start(struct peer *peer) { int status; - int connected = 0; bgp_peer_conf_if_to_su_update(peer); @@ -1385,15 +1542,21 @@ int bgp_start(struct peer *peer) zlog_debug( "%s [FSM] Unable to get neighbor's IP address, waiting...", peer->host); + peer->last_reset = PEER_DOWN_NBR_ADDR; return -1; } if (BGP_PEER_START_SUPPRESSED(peer)) { if (bgp_debug_neighbor_events(peer)) flog_err(EC_BGP_FSM, - "%s [FSM] Trying to start suppressed peer" - " - this is never supposed to happen!", + "%s [FSM] Trying to start suppressed peer - this is never supposed to happen!", peer->host); + if (CHECK_FLAG(peer->flags, PEER_FLAG_SHUTDOWN)) + peer->last_reset = PEER_DOWN_USER_SHUTDOWN; + else if (CHECK_FLAG(peer->bgp->flags, BGP_FLAG_SHUTDOWN)) + peer->last_reset = PEER_DOWN_USER_SHUTDOWN; + else if (CHECK_FLAG(peer->sflags, PEER_STATUS_PREFIX_OVERFLOW)) + peer->last_reset = PEER_DOWN_PFX_COUNT; return -1; } @@ -1412,7 +1575,7 @@ int bgp_start(struct peer *peer) } /* Clear remote router-id. */ - peer->remote_id.s_addr = 0; + peer->remote_id.s_addr = INADDR_ANY; /* Clear peer capability flag. */ peer->cap = 0; @@ -1430,25 +1593,19 @@ int bgp_start(struct peer *peer) EC_BGP_FSM, "%s [FSM] In a VRF that is not initialised yet", peer->host); + peer->last_reset = PEER_DOWN_VRF_UNINIT; return -1; } - /* Register to be notified on peer up */ - if (peer->sort == BGP_PEER_EBGP && peer->ttl == 1 - && !CHECK_FLAG(peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK) - && !bgp_flag_check(peer->bgp, BGP_FLAG_DISABLE_NH_CONNECTED_CHK)) - connected = 1; - else - connected = 0; - - if (!bgp_find_or_add_nexthop(peer->bgp, peer->bgp, - family2afi(peer->su.sa.sa_family), NULL, - peer, connected)) { + /* Register peer for NHT. If next hop is already resolved, proceed + * with connection setup, else wait. + */ + if (!bgp_peer_reg_with_nht(peer)) { if (bgp_zebra_num_connects()) { if (bgp_debug_neighbor_events(peer)) zlog_debug("%s [FSM] Waiting for NHT", peer->host); - + peer->last_reset = PEER_DOWN_WAITING_NHT; BGP_EVENT_ADD(peer, TCP_connection_open_failed); return 0; } @@ -1510,6 +1667,10 @@ static int bgp_reconnect(struct peer *peer) if (bgp_stop(peer) < 0) return -1; + /* Send graceful restart capabilty */ + BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(peer->bgp, + peer->bgp->peer); + bgp_start(peer); return 0; } @@ -1519,9 +1680,6 @@ static int bgp_fsm_open(struct peer *peer) /* Send keepalive and make keepalive timer */ bgp_keepalive_send(peer); - /* Reset holdtimer value. */ - BGP_TIMER_OFF(peer->t_holdtime); - return 0; } @@ -1532,7 +1690,8 @@ static int bgp_fsm_event_error(struct peer *peer) flog_err(EC_BGP_FSM, "%s [FSM] unexpected packet received in state %s", peer->host, lookup_msg(bgp_status_msg, peer->status, NULL)); - return bgp_stop_with_notify(peer, BGP_NOTIFY_FSM_ERR, 0); + return bgp_stop_with_notify(peer, BGP_NOTIFY_FSM_ERR, + bgp_fsm_error_subcode(peer->status)); } /* Hold timer expire. This is error of BGP connection. So cut the @@ -1545,6 +1704,78 @@ static int bgp_fsm_holdtime_expire(struct peer *peer) return bgp_stop_with_notify(peer, BGP_NOTIFY_HOLD_ERR, 0); } +/* Start the selection deferral timer thread for the specified AFI, SAFI */ +static int bgp_start_deferral_timer(struct bgp *bgp, afi_t afi, safi_t safi, + struct graceful_restart_info *gr_info) +{ + struct afi_safi_info *thread_info; + + /* If the deferral timer is active, then increment eor count */ + if (gr_info->t_select_deferral) { + gr_info->eor_required++; + return 0; + } + + /* Start the deferral timer when the first peer enabled for the graceful + * restart is established + */ + if (gr_info->eor_required == 0) { + thread_info = XMALLOC(MTYPE_TMP, sizeof(struct afi_safi_info)); + + thread_info->afi = afi; + thread_info->safi = safi; + thread_info->bgp = bgp; + + thread_add_timer(bm->master, bgp_graceful_deferral_timer_expire, + thread_info, bgp->select_defer_time, + &gr_info->t_select_deferral); + } + gr_info->eor_required++; + /* Send message to RIB indicating route update pending */ + if (gr_info->af_enabled[afi][safi] == false) { + gr_info->af_enabled[afi][safi] = true; + /* Send message to RIB */ + bgp_zebra_update(afi, safi, bgp->vrf_id, + ZEBRA_CLIENT_ROUTE_UPDATE_PENDING); + } + if (BGP_DEBUG(update, UPDATE_OUT)) + zlog_debug("Started the deferral timer for %s eor_required %d", + get_afi_safi_str(afi, safi, false), + gr_info->eor_required); + return 0; +} + +/* Update the graceful restart information for the specified AFI, SAFI */ +static int bgp_update_gr_info(struct peer *peer, afi_t afi, safi_t safi) +{ + struct graceful_restart_info *gr_info; + struct bgp *bgp = peer->bgp; + int ret = 0; + + if ((afi < AFI_IP) || (afi >= AFI_MAX)) { + if (BGP_DEBUG(update, UPDATE_OUT)) + zlog_debug("%s : invalid afi %d", __func__, afi); + return -1; + } + + if ((safi < SAFI_UNICAST) || (safi > SAFI_MPLS_VPN)) { + if (BGP_DEBUG(update, UPDATE_OUT)) + zlog_debug("%s : invalid safi %d", __func__, safi); + return -1; + } + + /* Restarting router */ + if (BGP_PEER_GRACEFUL_RESTART_CAPABLE(peer) + && BGP_PEER_RESTARTING_MODE(peer)) { + /* Check if the forwarding state is preserved */ + if (CHECK_FLAG(bgp->flags, BGP_FLAG_GR_PRESERVE_FWD)) { + gr_info = &(bgp->gr_info[afi][safi]); + ret = bgp_start_deferral_timer(bgp, afi, safi, gr_info); + } + } + return ret; +} + /** * Transition to Established state. * @@ -1558,6 +1789,7 @@ static int bgp_establish(struct peer *peer) int nsf_af_count = 0; int ret = 0; struct peer *other; + int status; other = peer->doppelganger; peer = peer_xfer_conn(peer); @@ -1582,21 +1814,26 @@ static int bgp_establish(struct peer *peer) bgp_fsm_change_status(peer, Established); /* bgp log-neighbor-changes of neighbor Up */ - if (bgp_flag_check(peer->bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES)) { + if (CHECK_FLAG(peer->bgp->flags, BGP_FLAG_LOG_NEIGHBOR_CHANGES)) { struct vrf *vrf = vrf_lookup_by_id(peer->bgp->vrf_id); - zlog_info("%%ADJCHANGE: neighbor %s(%s) in vrf %s Up", - peer->host, - (peer->hostname) ? peer->hostname : "Unknown", - vrf ? ((vrf->vrf_id != VRF_DEFAULT) - ? vrf->name - : VRF_DEFAULT_NAME) - : ""); + zlog_info( + "%%ADJCHANGE: neighbor %s(%s) in vrf %s Up", peer->host, + (peer->hostname) ? peer->hostname : "Unknown", + vrf ? ((vrf->vrf_id != VRF_DEFAULT) ? vrf->name + : VRF_DEFAULT_NAME) + : ""); } /* assign update-group/subgroup */ update_group_adjust_peer_afs(peer); /* graceful restart */ UNSET_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT); + if (bgp_debug_neighbor_events(peer)) { + if (BGP_PEER_RESTARTING_MODE(peer)) + zlog_debug("peer %s BGP_RESTARTING_MODE", peer->host); + else if (BGP_PEER_HELPER_MODE(peer)) + zlog_debug("peer %s BGP_HELPER_MODE", peer->host); + } for (afi = AFI_IP; afi < AFI_MAX; afi++) for (safi = SAFI_UNICAST; safi <= SAFI_MPLS_VPN; safi++) { if (peer->afc_nego[afi][safi] @@ -1616,7 +1853,55 @@ static int bgp_establish(struct peer *peer) bgp_clear_stale_route(peer, afi, safi); peer->nsf[afi][safi] = 0; } + /* Update the graceful restart information */ + if (peer->afc_nego[afi][safi]) { + if (!BGP_SELECT_DEFER_DISABLE(peer->bgp)) { + status = bgp_update_gr_info(peer, afi, + safi); + if (status < 0) + zlog_err( + "Error in updating graceful restart for %s", + get_afi_safi_str( + afi, safi, + false)); + } else { + if (BGP_PEER_GRACEFUL_RESTART_CAPABLE( + peer) + && BGP_PEER_RESTARTING_MODE(peer) + && CHECK_FLAG( + peer->bgp->flags, + BGP_FLAG_GR_PRESERVE_FWD)) + peer->bgp->gr_info[afi][safi] + .eor_required++; + } + } + } + + if (!CHECK_FLAG(peer->cap, PEER_CAP_RESTART_RCV)) { + if ((bgp_peer_gr_mode_get(peer) == PEER_GR) + || ((bgp_peer_gr_mode_get(peer) == PEER_GLOBAL_INHERIT) + && (bgp_global_gr_mode_get(peer->bgp) == GLOBAL_GR))) { + FOREACH_AFI_SAFI (afi, safi) + /* Send route processing complete + message to RIB */ + bgp_zebra_update( + afi, safi, peer->bgp->vrf_id, + ZEBRA_CLIENT_ROUTE_UPDATE_COMPLETE); + } + } else { + /* Peer sends R-bit. In this case, we need to send + * ZEBRA_CLIENT_ROUTE_UPDATE_COMPLETE to Zebra. */ + if (CHECK_FLAG(peer->cap, PEER_CAP_RESTART_BIT_RCV)) { + FOREACH_AFI_SAFI (afi, safi) + /* Send route processing complete + message to RIB */ + bgp_zebra_update( + afi, safi, peer->bgp->vrf_id, + ZEBRA_CLIENT_ROUTE_UPDATE_COMPLETE); } + } + + peer->nsf_af_count = nsf_af_count; if (nsf_af_count) SET_FLAG(peer->sflags, PEER_STATUS_NSF_MODE); @@ -1638,8 +1923,6 @@ static int bgp_establish(struct peer *peer) peer->host); } - hook_call(peer_established, peer); - /* Reset uptime, turn on keepalives, send current table. */ if (!peer->v_holdtime) bgp_keepalives_on(peer); @@ -1710,7 +1993,7 @@ static int bgp_establish(struct peer *peer) hash_release(peer->bgp->peerhash, peer); hash_get(peer->bgp->peerhash, peer, hash_alloc_intern); - bgp_bfd_register_peer(peer); + bgp_bfd_reset_peer(peer); return ret; } @@ -1779,7 +2062,7 @@ void bgp_fsm_event_update(struct peer *peer, int valid) case OpenSent: case OpenConfirm: case Established: - if (!valid && (peer->gtsm_hops == 1)) + if (!valid && (peer->gtsm_hops == BGP_GTSM_HOPS_CONNECTED)) BGP_EVENT_ADD(peer, TCP_fatal_error); case Clearing: case Deleted: @@ -1791,7 +2074,7 @@ void bgp_fsm_event_update(struct peer *peer, int valid) /* Finite State Machine structure */ static const struct { int (*func)(struct peer *); - int next_state; + enum bgp_fsm_status next_state; } FSM[BGP_STATUS_MAX - 1][BGP_EVENTS_MAX - 1] = { { /* Idle state: In Idle state, all events other than BGP_Start is @@ -1860,7 +2143,7 @@ static const struct { {bgp_fsm_open, OpenConfirm}, /* Receive_OPEN_message */ {bgp_fsm_event_error, Idle}, /* Receive_KEEPALIVE_message */ {bgp_fsm_event_error, Idle}, /* Receive_UPDATE_message */ - {bgp_stop_with_error, Idle}, /* Receive_NOTIFICATION_message */ + {bgp_fsm_event_error, Idle}, /* Receive_NOTIFICATION_message */ {bgp_fsm_exeption, Idle}, /* Clearing_Completed */ }, { @@ -1938,7 +2221,7 @@ static const struct { /* Execute event process. */ int bgp_event(struct thread *thread) { - int event; + enum bgp_fsm_events event; struct peer *peer; int ret; @@ -1950,9 +2233,9 @@ int bgp_event(struct thread *thread) return (ret); } -int bgp_event_update(struct peer *peer, int event) +int bgp_event_update(struct peer *peer, enum bgp_fsm_events event) { - int next; + enum bgp_fsm_status next; int ret = 0; struct peer *other; int passive_conn = 0; @@ -2020,8 +2303,7 @@ int bgp_event_update(struct peer *peer, int event) if (!dyn_nbr && !passive_conn && peer->bgp) { flog_err( EC_BGP_FSM, - "%s [FSM] Failure handling event %s in state %s, " - "prior events %s, %s, fd %d", + "%s [FSM] Failure handling event %s in state %s, prior events %s, %s, fd %d", peer->host, bgp_event_str[peer->cur_event], lookup_msg(bgp_status_msg, peer->status, NULL), bgp_event_str[peer->last_event], @@ -2036,3 +2318,479 @@ int bgp_event_update(struct peer *peer, int event) return ret; } +/* BGP GR Code */ + +int bgp_gr_lookup_n_update_all_peer(struct bgp *bgp, + enum global_mode global_new_state, + enum global_mode global_old_state) +{ + struct peer *peer = {0}; + struct listnode *node = {0}; + struct listnode *nnode = {0}; + enum peer_mode peer_old_state = PEER_INVALID; + + for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug("%s [BGP_GR] Peer: (%s) :", __func__, + peer->host); + + peer_old_state = bgp_peer_gr_mode_get(peer); + + if (peer_old_state == PEER_GLOBAL_INHERIT) { + + /* + *Reset only these peers and send a + *new open message with the change capabilities. + *Considering the mode to be "global_new_state" and + *do all operation accordingly + */ + + switch (global_new_state) { + case GLOBAL_HELPER: + BGP_PEER_GR_HELPER_ENABLE(peer); + break; + case GLOBAL_GR: + BGP_PEER_GR_ENABLE(peer); + break; + case GLOBAL_DISABLE: + BGP_PEER_GR_DISABLE(peer); + break; + case GLOBAL_INVALID: + zlog_debug("%s [BGP_GR] GLOBAL_INVALID", + __func__); + return BGP_ERR_GR_OPERATION_FAILED; + } + } + } + + bgp->global_gr_present_state = global_new_state; + + return BGP_GR_SUCCESS; +} + +int bgp_gr_update_all(struct bgp *bgp, int global_gr_cmd) +{ + enum global_mode global_new_state = GLOBAL_INVALID; + enum global_mode global_old_state = GLOBAL_INVALID; + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug("%s [BGP_GR]START: global_gr_cmd :%s:", __func__, + print_global_gr_cmd(global_gr_cmd)); + + global_old_state = bgp_global_gr_mode_get(bgp); + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug("[BGP_GR] global_old_gr_state :%s:", + print_global_gr_mode(global_old_state)); + + if (global_old_state != GLOBAL_INVALID) { + global_new_state = + bgp->GLOBAL_GR_FSM[global_old_state][global_gr_cmd]; + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug("[BGP_GR] global_new_gr_state :%s:", + print_global_gr_mode(global_new_state)); + } else { + zlog_err("%s [BGP_GR] global_old_state == GLOBAL_INVALID", + __func__); + return BGP_ERR_GR_OPERATION_FAILED; + } + + if (global_new_state == GLOBAL_INVALID) { + zlog_err("%s [BGP_GR] global_new_state == GLOBAL_INVALID", + __func__); + return BGP_ERR_GR_INVALID_CMD; + } + if (global_new_state == global_old_state) { + /* Trace msg */ + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "%s [BGP_GR] global_new_state == global_old_state :%s", + __func__, + print_global_gr_mode(global_new_state)); + return BGP_GR_NO_OPERATION; + } + + return bgp_gr_lookup_n_update_all_peer(bgp, global_new_state, + global_old_state); +} + +const char *print_peer_gr_mode(enum peer_mode pr_mode) +{ + const char *peer_gr_mode = NULL; + + switch (pr_mode) { + case PEER_HELPER: + peer_gr_mode = "PEER_HELPER"; + break; + case PEER_GR: + peer_gr_mode = "PEER_GR"; + break; + case PEER_DISABLE: + peer_gr_mode = "PEER_DISABLE"; + break; + case PEER_INVALID: + peer_gr_mode = "PEER_INVALID"; + break; + case PEER_GLOBAL_INHERIT: + peer_gr_mode = "PEER_GLOBAL_INHERIT"; + break; + } + + return peer_gr_mode; +} + +const char *print_peer_gr_cmd(enum peer_gr_command pr_gr_cmd) +{ + const char *peer_gr_cmd = NULL; + + switch (pr_gr_cmd) { + case PEER_GR_CMD: + peer_gr_cmd = "PEER_GR_CMD"; + break; + case NO_PEER_GR_CMD: + peer_gr_cmd = "NO_PEER_GR_CMD"; + break; + case PEER_DISABLE_CMD: + peer_gr_cmd = "PEER_GR_CMD"; + break; + case NO_PEER_DISABLE_CMD: + peer_gr_cmd = "NO_PEER_GR_CMD"; + break; + case PEER_HELPER_CMD: + peer_gr_cmd = "PEER_HELPER_CMD"; + break; + case NO_PEER_HELPER_CMD: + peer_gr_cmd = "NO_PEER_HELPER_CMD"; + break; + } + + return peer_gr_cmd; +} + +const char *print_global_gr_mode(enum global_mode gl_mode) +{ + const char *global_gr_mode = NULL; + + switch (gl_mode) { + case GLOBAL_HELPER: + global_gr_mode = "GLOBAL_HELPER"; + break; + case GLOBAL_GR: + global_gr_mode = "GLOBAL_GR"; + break; + case GLOBAL_DISABLE: + global_gr_mode = "GLOBAL_DISABLE"; + break; + case GLOBAL_INVALID: + global_gr_mode = "GLOBAL_INVALID"; + break; + } + + return global_gr_mode; +} + +const char *print_global_gr_cmd(enum global_gr_command gl_gr_cmd) +{ + const char *global_gr_cmd = NULL; + + switch (gl_gr_cmd) { + case GLOBAL_GR_CMD: + global_gr_cmd = "GLOBAL_GR_CMD"; + break; + case NO_GLOBAL_GR_CMD: + global_gr_cmd = "NO_GLOBAL_GR_CMD"; + break; + case GLOBAL_DISABLE_CMD: + global_gr_cmd = "GLOBAL_DISABLE_CMD"; + break; + case NO_GLOBAL_DISABLE_CMD: + global_gr_cmd = "NO_GLOBAL_DISABLE_CMD"; + break; + } + + return global_gr_cmd; +} + +enum global_mode bgp_global_gr_mode_get(struct bgp *bgp) +{ + return bgp->global_gr_present_state; +} + +enum peer_mode bgp_peer_gr_mode_get(struct peer *peer) +{ + return peer->peer_gr_present_state; +} + +int bgp_neighbor_graceful_restart(struct peer *peer, int peer_gr_cmd) +{ + enum peer_mode peer_new_state = PEER_INVALID; + enum peer_mode peer_old_state = PEER_INVALID; + struct bgp_peer_gr peer_state; + int result = BGP_GR_FAILURE; + + /* + * fetch peer_old_state from peer structure also + * fetch global_old_state from bgp structure, + * peer had a back pointer to bgpo struct ; + */ + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug("%s [BGP_GR] START:Peer: (%s) : peer_gr_cmd :%s:", + __func__, peer->host, + print_peer_gr_cmd(peer_gr_cmd)); + + peer_old_state = bgp_peer_gr_mode_get(peer); + + if (peer_old_state == PEER_INVALID) { + zlog_debug("[BGP_GR] peer_old_state == Invalid state !"); + return BGP_ERR_GR_OPERATION_FAILED; + } + + peer_state = peer->PEER_GR_FSM[peer_old_state][peer_gr_cmd]; + peer_new_state = peer_state.next_state; + + if (peer_new_state == PEER_INVALID) { + zlog_debug( + "[BGP_GR] Invalid bgp graceful restart command used !"); + return BGP_ERR_GR_INVALID_CMD; + } + + if (peer_new_state != peer_old_state) { + result = peer_state.action_fun(peer, peer_old_state, + peer_new_state); + } else { + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] peer_old_state == peer_new_state !"); + return BGP_GR_NO_OPERATION; + } + + if (result == BGP_GR_SUCCESS) { + + /* Update the mode i.e peer_new_state into the peer structure */ + peer->peer_gr_present_state = peer_new_state; + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] Successfully change the state of the peer to : %s : !", + print_peer_gr_mode(peer_new_state)); + + return BGP_GR_SUCCESS; + } + + return result; +} + +unsigned int bgp_peer_gr_action(struct peer *peer, int old_peer_state, + int new_peer_state) +{ + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "%s [BGP_GR] Move peer from old_peer_state :%s: to new_peer_state :%s: !!!!", + __func__, print_peer_gr_mode(old_peer_state), + print_peer_gr_mode(new_peer_state)); + + int bgp_gr_global_mode = GLOBAL_INVALID; + unsigned int ret = BGP_GR_FAILURE; + + if (old_peer_state == new_peer_state) { + /* Nothing to do over here as the present and old state is the + * same */ + return BGP_GR_NO_OPERATION; + } + if ((old_peer_state == PEER_INVALID) + || (new_peer_state == PEER_INVALID)) { + /* something bad happend , print error message */ + return BGP_ERR_GR_INVALID_CMD; + } + + bgp_gr_global_mode = bgp_global_gr_mode_get(peer->bgp); + + if ((old_peer_state == PEER_GLOBAL_INHERIT) + && (new_peer_state != PEER_GLOBAL_INHERIT)) { + + /* fetch the Mode running in the Global state machine + *from the bgp structure into a variable called + *bgp_gr_global_mode + */ + + /* Here we are checking if the + *1. peer_new_state == global_mode == helper_mode + *2. peer_new_state == global_mode == GR_mode + *3. peer_new_state == global_mode == disabled_mode + */ + + BGP_PEER_GR_GLOBAL_INHERIT_UNSET(peer); + + if (new_peer_state == bgp_gr_global_mode) { + /*This is incremental updates i.e no tear down + *of the existing session + *as the peer is already working in the same mode. + */ + ret = BGP_GR_SUCCESS; + } else { + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] Peer state changed from :%s ", + print_peer_gr_mode(old_peer_state)); + + bgp_peer_move_to_gr_mode(peer, new_peer_state); + + ret = BGP_GR_SUCCESS; + } + } + /* In the case below peer is going into Global inherit mode i.e. + * the peer would work as the mode configured at the global level + */ + else if ((new_peer_state == PEER_GLOBAL_INHERIT) + && (old_peer_state != PEER_GLOBAL_INHERIT)) { + /* Here in this case it would be destructive + * in all the cases except one case when, + * Global GR is configured Disabled + * and present_peer_state is not disable + */ + + BGP_PEER_GR_GLOBAL_INHERIT_SET(peer); + + if (old_peer_state == bgp_gr_global_mode) { + + /* This is incremental updates + *i.e no tear down of the existing session + *as the peer is already working in the same mode. + */ + ret = BGP_GR_SUCCESS; + } else { + /* Destructive always */ + /* Tear down the old session + * and send the new capability + * as per the bgp_gr_global_mode + */ + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] Peer state changed from :%s", + print_peer_gr_mode(old_peer_state)); + + bgp_peer_move_to_gr_mode(peer, bgp_gr_global_mode); + + ret = BGP_GR_SUCCESS; + } + } else { + /* + *This else case, it include all the cases except --> + *(new_peer_state != Peer_Global) && + *( old_peer_state != Peer_Global ) + */ + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug("[BGP_GR] Peer state changed from :%s", + print_peer_gr_mode(old_peer_state)); + + bgp_peer_move_to_gr_mode(peer, new_peer_state); + + ret = BGP_GR_SUCCESS; + } + + return ret; +} + +inline void bgp_peer_move_to_gr_mode(struct peer *peer, int new_state) + +{ + int bgp_global_gr_mode = bgp_global_gr_mode_get(peer->bgp); + + switch (new_state) { + case PEER_HELPER: + BGP_PEER_GR_HELPER_ENABLE(peer); + break; + case PEER_GR: + BGP_PEER_GR_ENABLE(peer); + break; + case PEER_DISABLE: + BGP_PEER_GR_DISABLE(peer); + break; + case PEER_GLOBAL_INHERIT: + BGP_PEER_GR_GLOBAL_INHERIT_SET(peer); + + if (bgp_global_gr_mode == GLOBAL_HELPER) { + BGP_PEER_GR_HELPER_ENABLE(peer); + } else if (bgp_global_gr_mode == GLOBAL_GR) { + BGP_PEER_GR_ENABLE(peer); + } else if (bgp_global_gr_mode == GLOBAL_DISABLE) { + BGP_PEER_GR_DISABLE(peer); + } else { + zlog_err( + "[BGP_GR] Default switch inherit mode ::: SOMETHING IS WRONG !!!"); + } + break; + default: + zlog_err( + "[BGP_GR] Default switch mode ::: SOMETHING IS WRONG !!!"); + break; + } + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug("[BGP_GR] Peer state changed --to--> : %d : !", + new_state); +} + +void bgp_peer_gr_flags_update(struct peer *peer) +{ + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug("%s [BGP_GR] called !", __func__); + if (CHECK_FLAG(peer->peer_gr_new_status_flag, + PEER_GRACEFUL_RESTART_NEW_STATE_HELPER)) + SET_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART_HELPER); + else + UNSET_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART_HELPER); + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] Peer %s Flag PEER_FLAG_GRACEFUL_RESTART_HELPER : %s : !", + peer->host, + (CHECK_FLAG(peer->flags, + PEER_FLAG_GRACEFUL_RESTART_HELPER) + ? "Set" + : "UnSet")); + if (CHECK_FLAG(peer->peer_gr_new_status_flag, + PEER_GRACEFUL_RESTART_NEW_STATE_RESTART)) + SET_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART); + else + UNSET_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART); + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] Peer %s Flag PEER_FLAG_GRACEFUL_RESTART : %s : !", + peer->host, + (CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART) + ? "Set" + : "UnSet")); + if (CHECK_FLAG(peer->peer_gr_new_status_flag, + PEER_GRACEFUL_RESTART_NEW_STATE_INHERIT)) + SET_FLAG(peer->flags, + PEER_FLAG_GRACEFUL_RESTART_GLOBAL_INHERIT); + else + UNSET_FLAG(peer->flags, + PEER_FLAG_GRACEFUL_RESTART_GLOBAL_INHERIT); + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] Peer %s Flag PEER_FLAG_GRACEFUL_RESTART_GLOBAL_INHERIT : %s : !", + peer->host, + (CHECK_FLAG(peer->flags, + PEER_FLAG_GRACEFUL_RESTART_GLOBAL_INHERIT) + ? "Set" + : "UnSet")); + + if (!CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART) + && !CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART_HELPER)) { + zlog_debug("[BGP_GR] Peer %s UNSET PEER_STATUS_NSF_MODE!", + peer->host); + + UNSET_FLAG(peer->sflags, PEER_STATUS_NSF_MODE); + + if (CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT)) { + + peer_nsf_stop(peer); + zlog_debug( + "[BGP_GR] Peer %s UNSET PEER_STATUS_NSF_WAIT!", + peer->host); + } + } +} diff --git a/bgpd/bgp_fsm.h b/bgpd/bgp_fsm.h index 3476a3c3a9..2fd5f6fc47 100644 --- a/bgpd/bgp_fsm.h +++ b/bgpd/bgp_fsm.h @@ -56,19 +56,72 @@ #define FSM_PEER_TRANSFERRED 2 #define FSM_PEER_TRANSITIONED 3 +#define BGP_PEER_GR_HELPER_ENABLE(peer) \ + do { \ + UNSET_FLAG( \ + peer->peer_gr_new_status_flag, \ + PEER_GRACEFUL_RESTART_NEW_STATE_RESTART); \ + SET_FLAG( \ + peer->peer_gr_new_status_flag, \ + PEER_GRACEFUL_RESTART_NEW_STATE_HELPER);\ + } while (0) + +#define BGP_PEER_GR_ENABLE(peer)\ + do { \ + SET_FLAG( \ + peer->peer_gr_new_status_flag, \ + PEER_GRACEFUL_RESTART_NEW_STATE_RESTART); \ + UNSET_FLAG( \ + peer->peer_gr_new_status_flag, \ + PEER_GRACEFUL_RESTART_NEW_STATE_HELPER);\ + } while (0) + +#define BGP_PEER_GR_DISABLE(peer)\ + do { \ + UNSET_FLAG( \ + peer->peer_gr_new_status_flag, \ + PEER_GRACEFUL_RESTART_NEW_STATE_RESTART);\ + UNSET_FLAG(\ + peer->peer_gr_new_status_flag, \ + PEER_GRACEFUL_RESTART_NEW_STATE_HELPER);\ + } while (0) + +#define BGP_PEER_GR_GLOBAL_INHERIT_SET(peer) \ + SET_FLAG(peer->peer_gr_new_status_flag, \ + PEER_GRACEFUL_RESTART_NEW_STATE_INHERIT) + +#define BGP_PEER_GR_GLOBAL_INHERIT_UNSET(peer) \ + UNSET_FLAG(peer->peer_gr_new_status_flag, \ + PEER_GRACEFUL_RESTART_NEW_STATE_INHERIT) + +#define BGP_PEER_GRACEFUL_RESTART_CAPABLE(peer) \ + (CHECK_FLAG(peer->cap, PEER_CAP_RESTART_ADV) \ + && CHECK_FLAG(peer->cap, PEER_CAP_RESTART_RCV)) + +#define BGP_PEER_RESTARTING_MODE(peer) \ + (CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART) \ + && CHECK_FLAG(peer->cap, PEER_CAP_RESTART_BIT_ADV) \ + && !CHECK_FLAG(peer->cap, PEER_CAP_RESTART_BIT_RCV)) + +#define BGP_PEER_HELPER_MODE(peer) \ + (CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART_HELPER) \ + && CHECK_FLAG(peer->cap, PEER_CAP_RESTART_BIT_RCV) \ + && !CHECK_FLAG(peer->cap, PEER_CAP_RESTART_BIT_ADV)) + /* Prototypes. */ extern void bgp_fsm_event_update(struct peer *peer, int valid); extern int bgp_event(struct thread *); -extern int bgp_event_update(struct peer *, int event); +extern int bgp_event_update(struct peer *, enum bgp_fsm_events event); extern int bgp_stop(struct peer *peer); extern void bgp_timer_set(struct peer *); extern int bgp_routeadv_timer(struct thread *); extern void bgp_fsm_change_status(struct peer *peer, int status); -extern const char *peer_down_str[]; +extern const char *const peer_down_str[]; extern void bgp_update_delay_end(struct bgp *); extern void bgp_maxmed_update(struct bgp *); -extern int bgp_maxmed_onstartup_configured(struct bgp *); -extern int bgp_maxmed_onstartup_active(struct bgp *); +extern bool bgp_maxmed_onstartup_configured(struct bgp *); +extern bool bgp_maxmed_onstartup_active(struct bgp *); +extern int bgp_fsm_error_subcode(int status); /** * Start the route advertisement timer (that honors MRAI) for all the @@ -87,7 +140,28 @@ extern void bgp_start_routeadv(struct bgp *); extern void bgp_adjust_routeadv(struct peer *); #include "hook.h" -DECLARE_HOOK(peer_backward_transition, (struct peer * peer), (peer)) -DECLARE_HOOK(peer_established, (struct peer * peer), (peer)) +DECLARE_HOOK(peer_backward_transition, (struct peer *peer), (peer)) +DECLARE_HOOK(peer_established, (struct peer *peer), (peer)) +int bgp_gr_update_all(struct bgp *bgp, int global_gr_cmd); +int bgp_neighbor_graceful_restart(struct peer *peer, int peer_gr_cmd); +unsigned int bgp_peer_gr_action(struct peer *peer, + int old_peer_state, int new_peer_state); +void bgp_peer_move_to_gr_mode(struct peer *peer, int new_state); +unsigned int bgp_peer_gr_helper_enable(struct peer *peer); +unsigned int bgp_peer_gr_enable(struct peer *peer); +unsigned int bgp_peer_gr_global_inherit(struct peer *peer); +unsigned int bgp_peer_gr_disable(struct peer *peer); +enum peer_mode bgp_peer_gr_mode_get(struct peer *peer); +enum global_mode bgp_global_gr_mode_get(struct bgp *bgp); +enum peer_mode bgp_get_peer_gr_mode_from_flags(struct peer *peer); +unsigned int bgp_peer_gr_global_inherit_unset(struct peer *peer); +int bgp_gr_lookup_n_update_all_peer(struct bgp *bgp, + enum global_mode global_new_state, + enum global_mode global_old_state); +void bgp_peer_gr_flags_update(struct peer *peer); +const char *print_peer_gr_mode(enum peer_mode pr_mode); +const char *print_peer_gr_cmd(enum peer_gr_command pr_gr_cmd); +const char *print_global_gr_mode(enum global_mode gl_mode); +const char *print_global_gr_cmd(enum global_gr_command gl_gr_cmd); #endif /* _QUAGGA_BGP_FSM_H */ diff --git a/bgpd/bgp_io.c b/bgpd/bgp_io.c index f111822e53..4190b18518 100644 --- a/bgpd/bgp_io.c +++ b/bgpd/bgp_io.c @@ -22,6 +22,7 @@ /* clang-format off */ #include #include // for pthread_mutex_unlock, pthread_mutex_lock +#include // for writev #include "frr_pthread.h" #include "linklist.h" // for list_delete, list_delete_all_node, lis... @@ -43,7 +44,7 @@ /* forward declarations */ static uint16_t bgp_write(struct peer *); -static uint16_t bgp_read(struct peer *); +static uint16_t bgp_read(struct peer *peer, int *code_p); static int bgp_process_writes(struct thread *); static int bgp_process_reads(struct thread *); static bool validate_header(struct peer *); @@ -132,12 +133,10 @@ static int bgp_process_writes(struct thread *thread) struct frr_pthread *fpt = bgp_pth_io; - pthread_mutex_lock(&peer->io_mtx); - { + frr_with_mutex(&peer->io_mtx) { status = bgp_write(peer); reschedule = (stream_fifo_head(peer->obuf) != NULL); } - pthread_mutex_unlock(&peer->io_mtx); /* no problem */ if (CHECK_FLAG(status, BGP_IO_TRANS_ERR)) { @@ -176,6 +175,7 @@ static int bgp_process_reads(struct thread *thread) bool fatal = false; // whether fatal error occurred bool added_pkt = false; // whether we pushed onto ->ibuf /* clang-format on */ + int code; peer = THREAD_ARG(thread); @@ -184,11 +184,9 @@ static int bgp_process_reads(struct thread *thread) struct frr_pthread *fpt = bgp_pth_io; - pthread_mutex_lock(&peer->io_mtx); - { - status = bgp_read(peer); + frr_with_mutex(&peer->io_mtx) { + status = bgp_read(peer, &code); } - pthread_mutex_unlock(&peer->io_mtx); /* error checking phase */ if (CHECK_FLAG(status, BGP_IO_TRANS_ERR)) { @@ -200,11 +198,16 @@ static int bgp_process_reads(struct thread *thread) /* problem; tear down session */ more = false; fatal = true; + + /* Handle the error in the main pthread, include the + * specific state change from 'bgp_read'. + */ + thread_add_event(bm->master, bgp_packet_process_error, + peer, code, NULL); } while (more) { /* static buffer for transferring packets */ - static unsigned char pktbuf[BGP_MAX_PACKET_SIZE]; /* shorter alias to peer's input buffer */ struct ringbuf *ibw = peer->ibuf_work; /* packet size as given by header */ @@ -234,27 +237,27 @@ static int bgp_process_reads(struct thread *thread) */ if (ringbuf_remain(ibw) >= pktsize) { struct stream *pkt = stream_new(pktsize); - assert(ringbuf_get(ibw, pktbuf, pktsize) == pktsize); - stream_put(pkt, pktbuf, pktsize); - pthread_mutex_lock(&peer->io_mtx); - { + assert(STREAM_WRITEABLE(pkt) == pktsize); + assert(ringbuf_get(ibw, pkt->data, pktsize) == pktsize); + stream_set_endp(pkt, pktsize); + + frr_with_mutex(&peer->io_mtx) { stream_fifo_push(peer->ibuf, pkt); } - pthread_mutex_unlock(&peer->io_mtx); added_pkt = true; } else break; } - assert(ringbuf_space(peer->ibuf_work) >= BGP_MAX_PACKET_SIZE); - /* handle invalid header */ if (fatal) { /* wipe buffer just in case someone screwed up */ ringbuf_wipe(peer->ibuf_work); } else { + assert(ringbuf_space(peer->ibuf_work) >= BGP_MAX_PACKET_SIZE); + thread_add_read(fpt->master, bgp_process_reads, peer, peer->fd, &peer->t_read); if (added_pkt) @@ -281,35 +284,96 @@ static uint16_t bgp_write(struct peer *peer) { uint8_t type; struct stream *s; - int num; int update_last_write = 0; - unsigned int count = 0; + unsigned int count; uint32_t uo = 0; uint16_t status = 0; uint32_t wpkt_quanta_old; + int writenum = 0; + int num; + unsigned int iovsz; + unsigned int strmsz; + unsigned int total_written; + wpkt_quanta_old = atomic_load_explicit(&peer->bgp->wpkt_quanta, memory_order_relaxed); + struct stream *ostreams[wpkt_quanta_old]; + struct stream **streams = ostreams; + struct iovec iov[wpkt_quanta_old]; + + s = stream_fifo_head(peer->obuf); + + if (!s) + goto done; + + count = iovsz = 0; + while (count < wpkt_quanta_old && iovsz < array_size(iov) && s) { + ostreams[iovsz] = s; + iov[iovsz].iov_base = stream_pnt(s); + iov[iovsz].iov_len = STREAM_READABLE(s); + writenum += STREAM_READABLE(s); + s = s->next; + ++iovsz; + ++count; + } + + strmsz = iovsz; + total_written = 0; + + do { + num = writev(peer->fd, iov, iovsz); + + if (num < 0) { + if (!ERRNO_IO_RETRY(errno)) { + BGP_EVENT_ADD(peer, TCP_fatal_error); + SET_FLAG(status, BGP_IO_FATAL_ERR); + } else { + SET_FLAG(status, BGP_IO_TRANS_ERR); + } + + break; + } else if (num != writenum) { + unsigned int msg_written = 0; + unsigned int ic = iovsz; - while (count < wpkt_quanta_old && (s = stream_fifo_head(peer->obuf))) { - int writenum; - do { - writenum = stream_get_endp(s) - stream_get_getp(s); - num = write(peer->fd, stream_pnt(s), writenum); + for (unsigned int i = 0; i < ic; i++) { + size_t ss = iov[i].iov_len; - if (num < 0) { - if (!ERRNO_IO_RETRY(errno)) { - BGP_EVENT_ADD(peer, TCP_fatal_error); - SET_FLAG(status, BGP_IO_FATAL_ERR); - } else { - SET_FLAG(status, BGP_IO_TRANS_ERR); - } + if (ss > (unsigned int) num) + break; - goto done; - } else if (num != writenum) - stream_forward_getp(s, num); + msg_written++; + iovsz--; + writenum -= ss; + num -= ss; + } - } while (num != writenum); + total_written += msg_written; + + assert(total_written < count); + + memmove(&iov, &iov[msg_written], + sizeof(iov[0]) * iovsz); + streams = &streams[msg_written]; + stream_forward_getp(streams[0], num); + iov[0].iov_base = stream_pnt(streams[0]); + iov[0].iov_len = STREAM_READABLE(streams[0]); + + writenum -= num; + num = 0; + assert(writenum > 0); + } else { + total_written = strmsz; + } + + } while (num != writenum); + + /* Handle statistics */ + for (unsigned int i = 0; i < total_written; i++) { + s = stream_fifo_pop(peer->obuf); + + assert(s == ostreams[i]); /* Retrieve BGP packet type. */ stream_set_getp(s, BGP_MARKER_SIZE + 2); @@ -357,9 +421,8 @@ static uint16_t bgp_write(struct peer *peer) break; } - count++; - - stream_free(stream_fifo_pop(peer->obuf)); + stream_free(s); + ostreams[i] = NULL; update_last_write = 1; } @@ -387,7 +450,7 @@ done : { * * @return status flag (see top-of-file) */ -static uint16_t bgp_read(struct peer *peer) +static uint16_t bgp_read(struct peer *peer, int *code_p) { size_t readsize; // how many bytes we want to read ssize_t nbytes; // how many bytes we actually read @@ -400,37 +463,28 @@ static uint16_t bgp_read(struct peer *peer) /* EAGAIN or EWOULDBLOCK; come back later */ if (nbytes < 0 && ERRNO_IO_RETRY(errno)) { SET_FLAG(status, BGP_IO_TRANS_ERR); - /* Fatal error; tear down session */ } else if (nbytes < 0) { + /* Fatal error; tear down session */ flog_err(EC_BGP_UPDATE_RCV, "%s [Error] bgp_read_packet error: %s", peer->host, safe_strerror(errno)); - if (peer->status == Established) { - if (CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_MODE)) { - peer->last_reset = PEER_DOWN_NSF_CLOSE_SESSION; - SET_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT); - } else - peer->last_reset = PEER_DOWN_CLOSE_SESSION; - } + /* Handle the error in the main pthread. */ + if (code_p) + *code_p = TCP_fatal_error; - BGP_EVENT_ADD(peer, TCP_fatal_error); SET_FLAG(status, BGP_IO_FATAL_ERR); - /* Received EOF / TCP session closed */ + } else if (nbytes == 0) { + /* Received EOF / TCP session closed */ if (bgp_debug_neighbor_events(peer)) zlog_debug("%s [Event] BGP connection closed fd %d", peer->host, peer->fd); - if (peer->status == Established) { - if (CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_MODE)) { - peer->last_reset = PEER_DOWN_NSF_CLOSE_SESSION; - SET_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT); - } else - peer->last_reset = PEER_DOWN_CLOSE_SESSION; - } + /* Handle the error in the main pthread. */ + if (code_p) + *code_p = TCP_connection_closed; - BGP_EVENT_ADD(peer, TCP_connection_closed); SET_FLAG(status, BGP_IO_FATAL_ERR); } else { assert(ringbuf_put(peer->ibuf_work, ibw, nbytes) @@ -453,7 +507,7 @@ static bool validate_header(struct peer *peer) uint8_t type; struct ringbuf *pkt = peer->ibuf_work; - static uint8_t m_correct[BGP_MARKER_SIZE] = { + static const uint8_t m_correct[BGP_MARKER_SIZE] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; uint8_t m_rx[BGP_MARKER_SIZE] = {0x00}; diff --git a/bgpd/bgp_io.h b/bgpd/bgp_io.h index 14a12d3705..75d014f38e 100644 --- a/bgpd/bgp_io.h +++ b/bgpd/bgp_io.h @@ -22,7 +22,7 @@ #ifndef _FRR_BGP_IO_H #define _FRR_BGP_IO_H -#define BGP_WRITE_PACKET_MAX 10U +#define BGP_WRITE_PACKET_MAX 64U #define BGP_READ_PACKET_MAX 10U #include "bgpd/bgpd.h" diff --git a/bgpd/bgp_keepalives.c b/bgpd/bgp_keepalives.c index c2f0baff76..3a5adae874 100644 --- a/bgpd/bgp_keepalives.c +++ b/bgpd/bgp_keepalives.c @@ -95,13 +95,20 @@ static void peer_process(struct hash_bucket *hb, void *arg) static struct timeval ka = {0}; // peer->v_keepalive as a timeval static struct timeval diff; // ka - elapsed - static struct timeval tolerance = {0, 100000}; + static const struct timeval tolerance = {0, 100000}; + + uint32_t v_ka = atomic_load_explicit(&pkat->peer->v_keepalive, + memory_order_relaxed); + + /* 0 keepalive timer means no keepalives */ + if (v_ka == 0) + return; /* calculate elapsed time since last keepalive */ monotime_since(&pkat->last, &elapsed); /* calculate difference between elapsed time and configured time */ - ka.tv_sec = pkat->peer->v_keepalive; + ka.tv_sec = v_ka; timersub(&ka, &elapsed, &diff); int send_keepalive = @@ -131,9 +138,9 @@ static bool peer_hash_cmp(const void *f, const void *s) return p1->peer == p2->peer; } -static unsigned int peer_hash_key(void *arg) +static unsigned int peer_hash_key(const void *arg) { - struct pkat *pkat = arg; + const struct pkat *pkat = arg; return (uintptr_t)pkat->peer; } @@ -245,8 +252,7 @@ void bgp_keepalives_on(struct peer *peer) */ assert(peerhash_mtx); - pthread_mutex_lock(peerhash_mtx); - { + frr_with_mutex(peerhash_mtx) { holder.peer = peer; if (!hash_lookup(peerhash, &holder)) { struct pkat *pkat = pkat_new(peer); @@ -255,7 +261,6 @@ void bgp_keepalives_on(struct peer *peer) } SET_FLAG(peer->thread_flags, PEER_THREAD_KEEPALIVES_ON); } - pthread_mutex_unlock(peerhash_mtx); bgp_keepalives_wake(); } @@ -275,8 +280,7 @@ void bgp_keepalives_off(struct peer *peer) */ assert(peerhash_mtx); - pthread_mutex_lock(peerhash_mtx); - { + frr_with_mutex(peerhash_mtx) { holder.peer = peer; struct pkat *res = hash_release(peerhash, &holder); if (res) { @@ -285,16 +289,13 @@ void bgp_keepalives_off(struct peer *peer) } UNSET_FLAG(peer->thread_flags, PEER_THREAD_KEEPALIVES_ON); } - pthread_mutex_unlock(peerhash_mtx); } void bgp_keepalives_wake(void) { - pthread_mutex_lock(peerhash_mtx); - { + frr_with_mutex(peerhash_mtx) { pthread_cond_signal(peerhash_cond); } - pthread_mutex_unlock(peerhash_mtx); } int bgp_keepalives_stop(struct frr_pthread *fpt, void **result) diff --git a/bgpd/bgp_label.c b/bgpd/bgp_label.c index 9511650842..fba954c432 100644 --- a/bgpd/bgp_label.c +++ b/bgpd/bgp_label.c @@ -45,7 +45,7 @@ extern struct zclient *zclient; int bgp_parse_fec_update(void) { struct stream *s; - struct bgp_node *rn; + struct bgp_dest *dest; struct bgp *bgp; struct bgp_table *table; struct prefix p; @@ -75,33 +75,33 @@ int bgp_parse_fec_update(void) zlog_debug("no %u unicast table", p.family); return -1; } - rn = bgp_node_lookup(table, &p); - if (!rn) { + dest = bgp_node_lookup(table, &p); + if (!dest) { zlog_debug("no node for the prefix"); return -1; } /* treat it as implicit withdraw - the label is invalid */ if (label == MPLS_INVALID_LABEL) - bgp_unset_valid_label(&rn->local_label); + bgp_unset_valid_label(&dest->local_label); else { - label_ntop(label, 1, &rn->local_label); - bgp_set_valid_label(&rn->local_label); + label_ntop(label, 1, &dest->local_label); + bgp_set_valid_label(&dest->local_label); } - SET_FLAG(rn->flags, BGP_NODE_LABEL_CHANGED); - bgp_unlock_node(rn); - bgp_process(bgp, rn, afi, safi); + SET_FLAG(dest->flags, BGP_NODE_LABEL_CHANGED); + bgp_dest_unlock_node(dest); + bgp_process(bgp, dest, afi, safi); return 1; } -mpls_label_t bgp_adv_label(struct bgp_node *rn, struct bgp_path_info *pi, +mpls_label_t bgp_adv_label(struct bgp_dest *dest, struct bgp_path_info *pi, struct peer *to, afi_t afi, safi_t safi) { struct peer *from; mpls_label_t remote_label; int reflect; - if (!rn || !pi || !to) + if (!dest || !pi || !to) return MPLS_INVALID_LABEL; remote_label = pi->extra ? pi->extra->label[0] : MPLS_INVALID_LABEL; @@ -117,7 +117,7 @@ mpls_label_t bgp_adv_label(struct bgp_node *rn, struct bgp_path_info *pi, if (CHECK_FLAG(to->af_flags[afi][safi], PEER_FLAG_NEXTHOP_UNCHANGED)) return remote_label; - return rn->local_label; + return dest->local_label; } /** @@ -130,15 +130,24 @@ mpls_label_t bgp_adv_label(struct bgp_node *rn, struct bgp_path_info *pi, int bgp_reg_for_label_callback(mpls_label_t new_label, void *labelid, bool allocated) { - struct bgp_path_info *pi = (struct bgp_path_info *)labelid; - struct bgp_node *rn = (struct bgp_node *)pi->net; - char addr[PREFIX_STRLEN]; + struct bgp_path_info *pi; + struct bgp_dest *dest; + + pi = labelid; + /* Is this path still valid? */ + if (!bgp_path_info_unlock(pi)) { + if (BGP_DEBUG(labelpool, LABELPOOL)) + zlog_debug( + "%s: bgp_path_info is no longer valid, ignoring", + __func__); + return -1; + } - prefix2str(&rn->p, addr, PREFIX_STRLEN); + dest = pi->net; if (BGP_DEBUG(labelpool, LABELPOOL)) - zlog_debug("%s: FEC %s label=%u, allocated=%d", __func__, addr, - new_label, allocated); + zlog_debug("%s: FEC %pRN label=%u, allocated=%d", __func__, + bgp_dest_to_rnode(dest), new_label, allocated); if (!allocated) { /* @@ -146,11 +155,11 @@ int bgp_reg_for_label_callback(mpls_label_t new_label, void *labelid, */ if (pi->attr->label_index == MPLS_INVALID_LABEL_INDEX && pi->attr->label != MPLS_LABEL_NONE - && CHECK_FLAG(rn->flags, BGP_NODE_REGISTERED_FOR_LABEL)) { - bgp_unregister_for_label(rn); + && CHECK_FLAG(dest->flags, BGP_NODE_REGISTERED_FOR_LABEL)) { + bgp_unregister_for_label(dest); label_ntop(MPLS_LABEL_IMPLICIT_NULL, 1, - &rn->local_label); - bgp_set_valid_label(&rn->local_label); + &dest->local_label); + bgp_set_valid_label(&dest->local_label); } return 0; } @@ -163,8 +172,9 @@ int bgp_reg_for_label_callback(mpls_label_t new_label, void *labelid, if (pi->attr->label_index != MPLS_INVALID_LABEL_INDEX) { flog_err( EC_BGP_LABEL, - "%s: FEC %s Rejecting allocated label %u as Label Index is %u", - __func__, addr, new_label, pi->attr->label_index); + "%s: FEC %pRN Rejecting allocated label %u as Label Index is %u", + __func__, bgp_dest_to_rnode(dest), new_label, + pi->attr->label_index); bgp_register_for_label(pi->net, pi); @@ -178,13 +188,14 @@ int bgp_reg_for_label_callback(mpls_label_t new_label, void *labelid, } /* Shouldn't happen: different label allocation */ flog_err(EC_BGP_LABEL, - "%s: %s had label %u but got new assignment %u", - __func__, addr, pi->attr->label, new_label); + "%s: %pRN had label %u but got new assignment %u", + __func__, bgp_dest_to_rnode(dest), pi->attr->label, + new_label); /* continue means use new one */ } - label_ntop(new_label, 1, &rn->local_label); - bgp_set_valid_label(&rn->local_label); + label_ntop(new_label, 1, &dest->local_label); + bgp_set_valid_label(&dest->local_label); /* * Get back to registering the FEC @@ -194,20 +205,20 @@ int bgp_reg_for_label_callback(mpls_label_t new_label, void *labelid, return 0; } -void bgp_reg_dereg_for_label(struct bgp_node *rn, struct bgp_path_info *pi, +void bgp_reg_dereg_for_label(struct bgp_dest *dest, struct bgp_path_info *pi, bool reg) { bool with_label_index = false; struct stream *s; - struct prefix *p; + const struct prefix *p; mpls_label_t *local_label; int command; uint16_t flags = 0; size_t flags_pos = 0; char addr[PREFIX_STRLEN]; - p = &(rn->p); - local_label = &(rn->local_label); + p = bgp_dest_get_prefix(dest); + local_label = &(dest->local_label); /* this prevents the loop when we're called by * bgp_reg_for_label_callback() */ @@ -271,9 +282,9 @@ void bgp_reg_dereg_for_label(struct bgp_node *rn, struct bgp_path_info *pi, flags |= ZEBRA_FEC_REGISTER_LABEL_INDEX; stream_putl(s, pi->attr->label_index); } - SET_FLAG(rn->flags, BGP_NODE_REGISTERED_FOR_LABEL); + SET_FLAG(dest->flags, BGP_NODE_REGISTERED_FOR_LABEL); } else - UNSET_FLAG(rn->flags, BGP_NODE_REGISTERED_FOR_LABEL); + UNSET_FLAG(dest->flags, BGP_NODE_REGISTERED_FOR_LABEL); /* Set length and flags */ stream_putw_at(s, 0, stream_get_endp(s)); @@ -357,7 +368,8 @@ int bgp_nlri_parse_label(struct peer *peer, struct attr *attr, if (pnt + BGP_ADDPATH_ID_LEN > lim) return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW; - addpath_id = ntohl(*((uint32_t *)pnt)); + memcpy(&addpath_id, pnt, BGP_ADDPATH_ID_LEN); + addpath_id = ntohl(addpath_id); pnt += BGP_ADDPATH_ID_LEN; } @@ -382,8 +394,7 @@ int bgp_nlri_parse_label(struct peer *peer, struct attr *attr, /* There needs to be at least one label */ if (prefixlen < 24) { flog_err(EC_BGP_UPDATE_RCV, - "%s [Error] Update packet error" - " (wrong label length %d)", + "%s [Error] Update packet error (wrong label length %d)", peer->host, prefixlen); bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR, BGP_NOTIFY_UPDATE_INVAL_NETWORK); @@ -461,7 +472,7 @@ int bgp_nlri_parse_label(struct peer *peer, struct attr *attr, if (pnt != lim) { flog_err( EC_BGP_UPDATE_RCV, - "%s [Error] Update packet error / L-U (%zu data remaining after parsing)", + "%s [Error] Update packet error / L-U (%td data remaining after parsing)", peer->host, lim - pnt); return BGP_NLRI_PARSE_ERROR_PACKET_LENGTH; } diff --git a/bgpd/bgp_label.h b/bgpd/bgp_label.h index 89bc9aabb0..d227cf7241 100644 --- a/bgpd/bgp_label.h +++ b/bgpd/bgp_label.h @@ -26,17 +26,18 @@ #define BGP_WITHDRAW_LABEL 0x800000 #define BGP_PREVENT_VRF_2_VRF_LEAK 0xFFFFFFFE -struct bgp_node; +struct bgp_dest; struct bgp_path_info; struct peer; extern int bgp_reg_for_label_callback(mpls_label_t new_label, void *labelid, bool allocated); -extern void bgp_reg_dereg_for_label(struct bgp_node *rn, +extern void bgp_reg_dereg_for_label(struct bgp_dest *dest, struct bgp_path_info *pi, bool reg); extern int bgp_parse_fec_update(void); -extern mpls_label_t bgp_adv_label(struct bgp_node *rn, struct bgp_path_info *pi, - struct peer *to, afi_t afi, safi_t safi); +extern mpls_label_t bgp_adv_label(struct bgp_dest *dest, + struct bgp_path_info *pi, struct peer *to, + afi_t afi, safi_t safi); extern int bgp_nlri_parse_label(struct peer *peer, struct attr *attr, struct bgp_nlri *packet); @@ -58,7 +59,7 @@ static inline int bgp_is_withdraw_label(mpls_label_t *label) /* The check on pkt[2] for 0x00 or 0x02 is in case bgp_set_valid_label() * was called on the withdraw label */ - if ((pkt[0] == 0x80) && (pkt[1] == 0x00) + if (((pkt[0] == 0x80) || (pkt[0] == 0x00)) && (pkt[1] == 0x00) && ((pkt[2] == 0x00) || (pkt[2] == 0x02))) return 1; return 0; @@ -86,15 +87,15 @@ static inline void bgp_unset_valid_label(mpls_label_t *label) t[2] &= ~0x02; } -static inline void bgp_register_for_label(struct bgp_node *rn, +static inline void bgp_register_for_label(struct bgp_dest *dest, struct bgp_path_info *pi) { - bgp_reg_dereg_for_label(rn, pi, true); + bgp_reg_dereg_for_label(dest, pi, true); } -static inline void bgp_unregister_for_label(struct bgp_node *rn) +static inline void bgp_unregister_for_label(struct bgp_dest *dest) { - bgp_reg_dereg_for_label(rn, NULL, false); + bgp_reg_dereg_for_label(dest, NULL, false); } /* Label stream to value */ diff --git a/bgpd/bgp_labelpool.c b/bgpd/bgp_labelpool.c index 71c0c8c7c6..feda0328bd 100644 --- a/bgpd/bgp_labelpool.c +++ b/bgpd/bgp_labelpool.c @@ -29,11 +29,13 @@ #include "skiplist.h" #include "workqueue.h" #include "zclient.h" +#include "mpls.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_labelpool.h" #include "bgpd/bgp_debug.h" #include "bgpd/bgp_errors.h" +#include "bgpd/bgp_route.h" /* * Definitions and external declarations. @@ -180,9 +182,24 @@ void bgp_lp_init(struct thread_master *master, struct labelpool *pool) lp->callback_q->spec.max_retries = 0; } +/* check if a label callback was for a BGP LU path, and if so, unlock it */ +static void check_bgp_lu_cb_unlock(struct lp_lcb *lcb) +{ + if (lcb->type == LP_TYPE_BGP_LU) + bgp_path_info_unlock(lcb->labelid); +} + +/* check if a label callback was for a BGP LU path, and if so, lock it */ +static void check_bgp_lu_cb_lock(struct lp_lcb *lcb) +{ + if (lcb->type == LP_TYPE_BGP_LU) + bgp_path_info_lock(lcb->labelid); +} + void bgp_lp_finish(void) { struct lp_fifo *lf; + struct work_queue_item *item, *titem; if (!lp) return; @@ -195,10 +212,21 @@ void bgp_lp_finish(void) list_delete(&lp->chunks); - while ((lf = lp_fifo_pop(&lp->requests))) + while ((lf = lp_fifo_pop(&lp->requests))) { + check_bgp_lu_cb_unlock(&lf->lcb); XFREE(MTYPE_BGP_LABEL_FIFO, lf); + } lp_fifo_fini(&lp->requests); + /* we must unlock path infos for LU callbacks; but we cannot do that + * in the deletion callback of the workqueue, as that is also called + * to remove an element from the queue after it has been run, resulting + * in a double unlock. Hence we need to iterate over our queues and + * lists and manually perform the unlocking (ugh) + */ + STAILQ_FOREACH_SAFE (item, &lp->callback_q->items, wq, titem) + check_bgp_lu_cb_unlock(item->data); + work_queue_free_and_null(&lp->callback_q); lp = NULL; @@ -328,6 +356,9 @@ void bgp_lp_get( q->labelid = lcb->labelid; q->allocated = true; + /* if this is a LU request, lock path info before queueing */ + check_bgp_lu_cb_lock(lcb); + work_queue_add(lp->callback_q, q); return; @@ -353,13 +384,17 @@ void bgp_lp_get( sizeof(struct lp_fifo)); lf->lcb = *lcb; + /* if this is a LU request, lock path info before queueing */ + check_bgp_lu_cb_lock(lcb); + lp_fifo_add_tail(&lp->requests, lf); if (lp_fifo_count(&lp->requests) > lp->pending_count) { - if (!zclient_send_get_label_chunk(zclient, 0, LP_CHUNK_SIZE)) { - lp->pending_count += LP_CHUNK_SIZE; + if (!zclient || zclient->sock < 0) return; - } + if (!zclient_send_get_label_chunk(zclient, 0, LP_CHUNK_SIZE, + MPLS_LABEL_BASE_ANY)) + lp->pending_count += LP_CHUNK_SIZE; } } @@ -436,6 +471,10 @@ void bgp_lp_event_chunk(uint8_t keep, uint32_t first, uint32_t last) __func__, labelid, lcb->label, lcb->label, lcb); } + /* if this was a BGP_LU request, unlock path info node + */ + check_bgp_lu_cb_unlock(lcb); + goto finishedrequest; } @@ -510,10 +549,13 @@ void bgp_lp_event_zebra_up(void) lm_init_ok = lm_label_manager_connect(zclient, 1) == 0; - if (!lm_init_ok) + if (!lm_init_ok) { zlog_err("%s: label manager connection error", __func__); + return; + } - zclient_send_get_label_chunk(zclient, 0, labels_needed); + zclient_send_get_label_chunk(zclient, 0, labels_needed, + MPLS_LABEL_BASE_ANY); lp->pending_count = labels_needed; /* @@ -544,6 +586,7 @@ void bgp_lp_event_zebra_up(void) q->label = lcb->label; q->labelid = lcb->labelid; q->allocated = false; + check_bgp_lu_cb_lock(lcb); work_queue_add(lp->callback_q, q); lcb->label = MPLS_LABEL_NONE; @@ -556,6 +599,7 @@ void bgp_lp_event_zebra_up(void) sizeof(struct lp_fifo)); lf->lcb = *lcb; + check_bgp_lu_cb_lock(lcb); lp_fifo_add_tail(&lp->requests, lf); } diff --git a/bgpd/bgp_lcommunity.c b/bgpd/bgp_lcommunity.c index 44766c9b6e..88a85c979e 100644 --- a/bgpd/bgp_lcommunity.c +++ b/bgpd/bgp_lcommunity.c @@ -2,7 +2,7 @@ * * Copyright (C) 2016 Keyur Patel * - * This file is part of FreeRangeRouting (FRR). + * This file is part of FRRouting (FRR). * * FRR is free software; you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software @@ -44,8 +44,13 @@ static struct lcommunity *lcommunity_new(void) /* Allocate lcommunities. */ void lcommunity_free(struct lcommunity **lcom) { + if (!(*lcom)) + return; + XFREE(MTYPE_LCOMMUNITY_VAL, (*lcom)->val); XFREE(MTYPE_LCOMMUNITY_STR, (*lcom)->str); + if ((*lcom)->json) + json_object_free((*lcom)->json); XFREE(MTYPE_LCOMMUNITY, *lcom); } @@ -59,8 +64,8 @@ static void lcommunity_hash_free(struct lcommunity *lcom) structure, we don't add the value. Newly added value is sorted by numerical order. When the value is added to the structure return 1 else return 0. */ -static int lcommunity_add_val(struct lcommunity *lcom, - struct lcommunity_val *lval) +static bool lcommunity_add_val(struct lcommunity *lcom, + struct lcommunity_val *lval) { uint8_t *p; int ret; @@ -71,7 +76,7 @@ static int lcommunity_add_val(struct lcommunity *lcom, lcom->size++; lcom->val = XMALLOC(MTYPE_LCOMMUNITY_VAL, lcom_length(lcom)); memcpy(lcom->val, lval->val, LCOMMUNITY_SIZE); - return 1; + return true; } /* If the value already exists in the structure return 0. */ @@ -79,7 +84,7 @@ static int lcommunity_add_val(struct lcommunity *lcom, for (p = lcom->val; c < lcom->size; p += LCOMMUNITY_SIZE, c++) { ret = memcmp(p, lval->val, LCOMMUNITY_SIZE); if (ret == 0) - return 0; + return false; if (ret > 0) break; } @@ -94,7 +99,7 @@ static int lcommunity_add_val(struct lcommunity *lcom, (lcom->size - 1 - c) * LCOMMUNITY_SIZE); memcpy(lcom->val + c * LCOMMUNITY_SIZE, lval->val, LCOMMUNITY_SIZE); - return 1; + return true; } /* This function takes pointer to Large Communites strucutre then @@ -177,15 +182,14 @@ static void set_lcommunity_string(struct lcommunity *lcom, bool make_json) { int i; int len; - bool first = true; char *str_buf; - char *str_pnt; - uint8_t *pnt; + const uint8_t *pnt; uint32_t global, local1, local2; json_object *json_lcommunity_list = NULL; json_object *json_string = NULL; -#define LCOMMUNITY_STR_DEFAULT_LEN 32 + /* 3 32-bit integers, 2 colons, and a space */ +#define LCOMMUNITY_STRLEN (10 * 3 + 2 + 1) if (!lcom) return; @@ -196,8 +200,7 @@ static void set_lcommunity_string(struct lcommunity *lcom, bool make_json) } if (lcom->size == 0) { - str_buf = XMALLOC(MTYPE_LCOMMUNITY_STR, 1); - str_buf[0] = '\0'; + str_buf = XCALLOC(MTYPE_LCOMMUNITY_STR, 1); if (make_json) { json_object_string_add(lcom->json, "string", ""); @@ -209,15 +212,13 @@ static void set_lcommunity_string(struct lcommunity *lcom, bool make_json) return; } - str_buf = str_pnt = - XMALLOC(MTYPE_LCOMMUNITY_STR, - (LCOMMUNITY_STR_DEFAULT_LEN * lcom->size) + 1); + /* 1 space + lcom->size lcom strings + null terminator */ + size_t str_buf_sz = (LCOMMUNITY_STRLEN * lcom->size) + 2; + str_buf = XCALLOC(MTYPE_LCOMMUNITY_STR, str_buf_sz); for (i = 0; i < lcom->size; i++) { - if (first) - first = false; - else - *str_pnt++ = ' '; + if (i > 0) + strlcat(str_buf, " ", str_buf_sz); pnt = lcom->val + (i * LCOMMUNITY_SIZE); pnt = ptr_get_be32(pnt, &global); @@ -225,19 +226,21 @@ static void set_lcommunity_string(struct lcommunity *lcom, bool make_json) pnt = ptr_get_be32(pnt, &local2); (void)pnt; - len = sprintf(str_pnt, "%u:%u:%u", global, local1, local2); + char lcsb[LCOMMUNITY_STRLEN + 1]; + + snprintf(lcsb, sizeof(lcsb), "%u:%u:%u", global, local1, + local2); + + len = strlcat(str_buf, lcsb, str_buf_sz); + assert((unsigned int)len < str_buf_sz); + if (make_json) { - json_string = json_object_new_string(str_pnt); + json_string = json_object_new_string(lcsb); json_object_array_add(json_lcommunity_list, json_string); } - - str_pnt += len; } - str_buf = - XREALLOC(MTYPE_LCOMMUNITY_STR, str_buf, str_pnt - str_buf + 1); - if (make_json) { json_object_string_add(lcom->json, "string", str_buf); json_object_object_add(lcom->json, "list", @@ -301,7 +304,7 @@ char *lcommunity_str(struct lcommunity *lcom, bool make_json) } /* Utility function to make hash key. */ -unsigned int lcommunity_hash_make(void *arg) +unsigned int lcommunity_hash_make(const void *arg) { const struct lcommunity *lcom = arg; int size = lcom_length(lcom); @@ -345,21 +348,17 @@ void lcommunity_finish(void) lcomhash = NULL; } -/* Large Communities token enum. */ -enum lcommunity_token { - lcommunity_token_unknown = 0, - lcommunity_token_val, -}; - -/* Get next Large Communities token from the string. */ +/* Get next Large Communities token from the string. + * Assumes str is space-delimeted and describes 0 or more + * valid large communities + */ static const char *lcommunity_gettoken(const char *str, - struct lcommunity_val *lval, - enum lcommunity_token *token) + struct lcommunity_val *lval) { const char *p = str; /* Skip white space. */ - while (isspace((int)*p)) { + while (isspace((unsigned char)*p)) { p++; str++; } @@ -369,60 +368,55 @@ static const char *lcommunity_gettoken(const char *str, return NULL; /* Community value. */ - if (isdigit((int)*p)) { - int separator = 0; - int digit = 0; - uint32_t globaladmin = 0; - uint32_t localdata1 = 0; - uint32_t localdata2 = 0; - - while (isdigit((int)*p) || *p == ':') { - if (*p == ':') { - if (separator == 2) { - *token = lcommunity_token_unknown; - return NULL; - } else { - separator++; - digit = 0; - if (separator == 1) { - globaladmin = localdata2; - } else { - localdata1 = localdata2; - } - localdata2 = 0; - } + int separator = 0; + int digit = 0; + uint32_t globaladmin = 0; + uint32_t localdata1 = 0; + uint32_t localdata2 = 0; + + while (*p && *p != ' ') { + /* large community valid chars */ + assert(isdigit((unsigned char)*p) || *p == ':'); + + if (*p == ':') { + separator++; + digit = 0; + if (separator == 1) { + globaladmin = localdata2; } else { - digit = 1; - localdata2 *= 10; - localdata2 += (*p - '0'); + localdata1 = localdata2; } - p++; - } - if (!digit) { - *token = lcommunity_token_unknown; - return NULL; + localdata2 = 0; + } else { + digit = 1; + /* left shift the accumulated value and add current + * digit + */ + localdata2 *= 10; + localdata2 += (*p - '0'); } - - /* - * Copy the large comm. - */ - lval->val[0] = (globaladmin >> 24) & 0xff; - lval->val[1] = (globaladmin >> 16) & 0xff; - lval->val[2] = (globaladmin >> 8) & 0xff; - lval->val[3] = globaladmin & 0xff; - lval->val[4] = (localdata1 >> 24) & 0xff; - lval->val[5] = (localdata1 >> 16) & 0xff; - lval->val[6] = (localdata1 >> 8) & 0xff; - lval->val[7] = localdata1 & 0xff; - lval->val[8] = (localdata2 >> 24) & 0xff; - lval->val[9] = (localdata2 >> 16) & 0xff; - lval->val[10] = (localdata2 >> 8) & 0xff; - lval->val[11] = localdata2 & 0xff; - - *token = lcommunity_token_val; - return p; + p++; } - *token = lcommunity_token_unknown; + + /* Assert str was a valid large community */ + assert(separator == 2 && digit == 1); + + /* + * Copy the large comm. + */ + lval->val[0] = (globaladmin >> 24) & 0xff; + lval->val[1] = (globaladmin >> 16) & 0xff; + lval->val[2] = (globaladmin >> 8) & 0xff; + lval->val[3] = globaladmin & 0xff; + lval->val[4] = (localdata1 >> 24) & 0xff; + lval->val[5] = (localdata1 >> 16) & 0xff; + lval->val[6] = (localdata1 >> 8) & 0xff; + lval->val[7] = localdata1 & 0xff; + lval->val[8] = (localdata2 >> 24) & 0xff; + lval->val[9] = (localdata2 >> 16) & 0xff; + lval->val[10] = (localdata2 >> 8) & 0xff; + lval->val[11] = localdata2 & 0xff; + return p; } @@ -436,27 +430,22 @@ static const char *lcommunity_gettoken(const char *str, struct lcommunity *lcommunity_str2com(const char *str) { struct lcommunity *lcom = NULL; - enum lcommunity_token token = lcommunity_token_unknown; struct lcommunity_val lval; - while ((str = lcommunity_gettoken(str, &lval, &token))) { - switch (token) { - case lcommunity_token_val: - if (lcom == NULL) - lcom = lcommunity_new(); - lcommunity_add_val(lcom, &lval); - break; - case lcommunity_token_unknown: - default: - if (lcom) - lcommunity_free(&lcom); - return NULL; - } - } + if (!lcommunity_list_valid(str, LARGE_COMMUNITY_LIST_STANDARD)) + return NULL; + + do { + str = lcommunity_gettoken(str, &lval); + if (lcom == NULL) + lcom = lcommunity_new(); + lcommunity_add_val(lcom, &lval); + } while (str); + return lcom; } -int lcommunity_include(struct lcommunity *lcom, uint8_t *ptr) +bool lcommunity_include(struct lcommunity *lcom, uint8_t *ptr) { int i; uint8_t *lcom_ptr; @@ -464,25 +453,25 @@ int lcommunity_include(struct lcommunity *lcom, uint8_t *ptr) for (i = 0; i < lcom->size; i++) { lcom_ptr = lcom->val + (i * LCOMMUNITY_SIZE); if (memcmp(ptr, lcom_ptr, LCOMMUNITY_SIZE) == 0) - return 1; + return true; } - return 0; + return false; } -int lcommunity_match(const struct lcommunity *lcom1, - const struct lcommunity *lcom2) +bool lcommunity_match(const struct lcommunity *lcom1, + const struct lcommunity *lcom2) { int i = 0; int j = 0; if (lcom1 == NULL && lcom2 == NULL) - return 1; + return true; if (lcom1 == NULL || lcom2 == NULL) - return 0; + return false; if (lcom1->size < lcom2->size) - return 0; + return false; /* Every community on com2 needs to be on com1 for this to match */ while (i < lcom1->size && j < lcom2->size) { @@ -494,9 +483,9 @@ int lcommunity_match(const struct lcommunity *lcom1, } if (j == lcom2->size) - return 1; + return true; else - return 0; + return false; } /* Delete one lcommunity. */ @@ -527,7 +516,6 @@ void lcommunity_del_val(struct lcommunity *lcom, uint8_t *ptr) lcom->val, lcom_length(lcom)); else { XFREE(MTYPE_LCOMMUNITY_VAL, lcom->val); - lcom->val = NULL; } return; } @@ -551,17 +539,15 @@ static void *bgp_aggr_lcommunty_hash_alloc(void *p) return lcommunity; } -static void bgp_aggr_lcommunity_prepare(struct hash_backet *hb, void *arg) +static void bgp_aggr_lcommunity_prepare(struct hash_bucket *hb, void *arg) { - struct lcommunity *lcommerge = NULL; struct lcommunity *hb_lcommunity = hb->data; struct lcommunity **aggr_lcommunity = arg; - if (*aggr_lcommunity) { - lcommerge = lcommunity_merge(*aggr_lcommunity, hb_lcommunity); - *aggr_lcommunity = lcommunity_uniq_sort(lcommerge); - lcommunity_free(&lcommerge); - } else + if (*aggr_lcommunity) + *aggr_lcommunity = lcommunity_merge(*aggr_lcommunity, + hb_lcommunity); + else *aggr_lcommunity = lcommunity_dup(hb_lcommunity); } @@ -575,6 +561,15 @@ void bgp_aggr_lcommunity_remove(void *arg) void bgp_compute_aggregate_lcommunity(struct bgp_aggregate *aggregate, struct lcommunity *lcommunity) { + + bgp_compute_aggregate_lcommunity_hash(aggregate, lcommunity); + bgp_compute_aggregate_lcommunity_val(aggregate); +} + +void bgp_compute_aggregate_lcommunity_hash(struct bgp_aggregate *aggregate, + struct lcommunity *lcommunity) +{ + struct lcommunity *aggr_lcommunity = NULL; if ((aggregate == NULL) || (lcommunity == NULL)) @@ -594,20 +589,34 @@ void bgp_compute_aggregate_lcommunity(struct bgp_aggregate *aggregate, aggr_lcommunity = hash_get(aggregate->lcommunity_hash, lcommunity, bgp_aggr_lcommunty_hash_alloc); + } - /* Re-compute aggregate's lcommunity. - */ - if (aggregate->lcommunity) - lcommunity_free(&aggregate->lcommunity); + /* Increment reference counter. + */ + aggr_lcommunity->refcnt++; +} + +void bgp_compute_aggregate_lcommunity_val(struct bgp_aggregate *aggregate) +{ + struct lcommunity *lcommerge = NULL; + if (aggregate == NULL) + return; + + /* Re-compute aggregate's lcommunity. + */ + if (aggregate->lcommunity) + lcommunity_free(&aggregate->lcommunity); + if (aggregate->lcommunity_hash && + aggregate->lcommunity_hash->count) { hash_iterate(aggregate->lcommunity_hash, bgp_aggr_lcommunity_prepare, &aggregate->lcommunity); + lcommerge = aggregate->lcommunity; + aggregate->lcommunity = lcommunity_uniq_sort(lcommerge); + if (lcommerge) + lcommunity_free(&lcommerge); } - - /* Increment refernce counter. - */ - aggr_lcommunity->refcnt++; } void bgp_remove_lcommunity_from_aggregate(struct bgp_aggregate *aggregate, @@ -616,10 +625,9 @@ void bgp_remove_lcommunity_from_aggregate(struct bgp_aggregate *aggregate, struct lcommunity *aggr_lcommunity = NULL; struct lcommunity *ret_lcomm = NULL; - if ((aggregate == NULL) || (lcommunity == NULL)) - return; - - if (aggregate->lcommunity_hash == NULL) + if ((!aggregate) + || (!aggregate->lcommunity_hash) + || (!lcommunity)) return; /* Look-up the lcommunity in the hash. @@ -633,13 +641,33 @@ void bgp_remove_lcommunity_from_aggregate(struct bgp_aggregate *aggregate, aggr_lcommunity); lcommunity_free(&ret_lcomm); - lcommunity_free(&aggregate->lcommunity); + bgp_compute_aggregate_lcommunity_val(aggregate); - /* Compute aggregate's lcommunity. - */ - hash_iterate(aggregate->lcommunity_hash, - bgp_aggr_lcommunity_prepare, - &aggregate->lcommunity); + } + } +} + +void bgp_remove_lcomm_from_aggregate_hash(struct bgp_aggregate *aggregate, + struct lcommunity *lcommunity) +{ + struct lcommunity *aggr_lcommunity = NULL; + struct lcommunity *ret_lcomm = NULL; + + if ((!aggregate) + || (!aggregate->lcommunity_hash) + || (!lcommunity)) + return; + + /* Look-up the lcommunity in the hash. + */ + aggr_lcommunity = bgp_aggr_lcommunity_lookup(aggregate, lcommunity); + if (aggr_lcommunity) { + aggr_lcommunity->refcnt--; + + if (aggr_lcommunity->refcnt == 0) { + ret_lcomm = hash_release(aggregate->lcommunity_hash, + aggr_lcommunity); + lcommunity_free(&ret_lcomm); } } } diff --git a/bgpd/bgp_lcommunity.h b/bgpd/bgp_lcommunity.h index aa4e8c69fe..6ccb6b7879 100644 --- a/bgpd/bgp_lcommunity.h +++ b/bgpd/bgp_lcommunity.h @@ -2,7 +2,7 @@ * * Copyright (C) 2016 Keyur Patel * - * This file is part of FreeRangeRouting (FRR). + * This file is part of FRRouting (FRR). * * FRR is free software; you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software @@ -23,6 +23,7 @@ #include "lib/json.h" #include "bgpd/bgp_route.h" +#include "bgpd/bgp_clist.h" /* Large Communities value is twelve octets long. */ #define LCOMMUNITY_SIZE 12 @@ -63,21 +64,31 @@ extern struct lcommunity *lcommunity_uniq_sort(struct lcommunity *); extern struct lcommunity *lcommunity_intern(struct lcommunity *); extern bool lcommunity_cmp(const void *arg1, const void *arg2); extern void lcommunity_unintern(struct lcommunity **); -extern unsigned int lcommunity_hash_make(void *); +extern unsigned int lcommunity_hash_make(const void *); extern struct hash *lcommunity_hash(void); extern struct lcommunity *lcommunity_str2com(const char *); -extern int lcommunity_match(const struct lcommunity *, - const struct lcommunity *); +extern bool lcommunity_match(const struct lcommunity *, + const struct lcommunity *); extern char *lcommunity_str(struct lcommunity *, bool make_json); -extern int lcommunity_include(struct lcommunity *lcom, uint8_t *ptr); +extern bool lcommunity_include(struct lcommunity *lcom, uint8_t *ptr); extern void lcommunity_del_val(struct lcommunity *lcom, uint8_t *ptr); extern void bgp_compute_aggregate_lcommunity( struct bgp_aggregate *aggregate, struct lcommunity *lcommunity); + +extern void bgp_compute_aggregate_lcommunity_hash( + struct bgp_aggregate *aggregate, + struct lcommunity *lcommunity); +extern void bgp_compute_aggregate_lcommunity_val( + struct bgp_aggregate *aggregate); + extern void bgp_remove_lcommunity_from_aggregate( struct bgp_aggregate *aggregate, struct lcommunity *lcommunity); +extern void bgp_remove_lcomm_from_aggregate_hash( + struct bgp_aggregate *aggregate, + struct lcommunity *lcommunity); extern void bgp_aggr_lcommunity_remove(void *arg); #endif /* _QUAGGA_BGP_LCOMMUNITY_H */ diff --git a/bgpd/bgp_mac.c b/bgpd/bgp_mac.c index 49b5854020..265b4568e5 100644 --- a/bgpd/bgp_mac.c +++ b/bgpd/bgp_mac.c @@ -29,6 +29,7 @@ #include "bgpd/bgp_memory.h" #include "bgpd/bgp_route.h" #include "bgpd/bgp_packet.h" +#include "bgpd/bgp_rd.h" #include "bgpd/bgp_debug.h" #include "bgpd/bgp_evpn_private.h" @@ -40,9 +41,9 @@ struct bgp_self_mac { struct list *ifp_list; }; -static unsigned int bgp_mac_hash_key_make(void *data) +static unsigned int bgp_mac_hash_key_make(const void *data) { - struct bgp_self_mac *bsm = data; + const struct bgp_self_mac *bsm = data; return jhash(&bsm->macaddr, ETH_ALEN, 0xa5a5dead); } @@ -133,26 +134,40 @@ static struct bgp_self_mac *bgp_mac_find_interface_name(const char *ifname) } static void bgp_process_mac_rescan_table(struct bgp *bgp, struct peer *peer, - struct bgp_table *table) + struct bgp_table *table, + struct ethaddr *macaddr) { - struct bgp_node *prn, *rn; + struct bgp_dest *pdest, *dest; struct bgp_path_info *pi; - uint32_t count = 0; - for (prn = bgp_table_top(table); prn; prn = bgp_route_next(prn)) { - struct bgp_table *sub = prn->info; + for (pdest = bgp_table_top(table); pdest; + pdest = bgp_route_next(pdest)) { + struct bgp_table *sub = pdest->info; + const struct prefix *pdest_p = bgp_dest_get_prefix(pdest); if (!sub) continue; - for (rn = bgp_table_top(sub); rn; rn = bgp_route_next(rn)) { + for (dest = bgp_table_top(sub); dest; + dest = bgp_route_next(dest)) { + bool dest_affected; + const struct prefix *p = bgp_dest_get_prefix(dest); + struct prefix_evpn *pevpn = (struct prefix_evpn *)dest; struct prefix_rd prd; uint32_t num_labels = 0; mpls_label_t *label_pnt = NULL; struct bgp_route_evpn evpn; - count++; - for (pi = rn->info; pi; pi = pi->next) { + if (pevpn->family == AF_EVPN + && pevpn->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE + && memcmp(&p->u.prefix_evpn.macip_addr.mac, macaddr, + ETH_ALEN) + == 0) + dest_affected = true; + else + dest_affected = false; + + for (pi = dest->info; pi; pi = pi->next) { if (pi->peer == peer) break; } @@ -160,6 +175,14 @@ static void bgp_process_mac_rescan_table(struct bgp *bgp, struct peer *peer, if (!pi) continue; + /* + * If the mac address is not the same then + * we don't care and since we are looking + */ + if ((memcmp(&pi->attr->rmac, macaddr, ETH_ALEN) != 0) + && !dest_affected) + continue; + if (pi->extra) num_labels = pi->extra->num_labels; if (num_labels) @@ -167,10 +190,27 @@ static void bgp_process_mac_rescan_table(struct bgp *bgp, struct peer *peer, prd.family = AF_UNSPEC; prd.prefixlen = 64; - memcpy(&prd.val, &prn->p.u.val, 8); + memcpy(&prd.val, pdest_p->u.val, 8); + + if (CHECK_FLAG(pi->flags, BGP_PATH_REMOVED)) { + if (bgp_debug_update(peer, p, NULL, 1)) { + char pfx_buf[BGP_PRD_PATH_STRLEN]; + + bgp_debug_rdpfxpath2str( + AFI_L2VPN, SAFI_EVPN, &prd, + p, label_pnt, num_labels, + pi->addpath_rx_id ? 1 : 0, + pi->addpath_rx_id, pfx_buf, + sizeof(pfx_buf)); + zlog_debug( + "%s skip update of %s marked as removed", + peer->host, pfx_buf); + } + continue; + } memcpy(&evpn, &pi->attr->evpn_overlay, sizeof(evpn)); - int32_t ret = bgp_update(peer, &rn->p, + int32_t ret = bgp_update(peer, p, pi->addpath_rx_id, pi->attr, AFI_L2VPN, SAFI_EVPN, ZEBRA_ROUTE_BGP, @@ -179,12 +219,12 @@ static void bgp_process_mac_rescan_table(struct bgp *bgp, struct peer *peer, 1, &evpn); if (ret < 0) - bgp_unlock_node(rn); + bgp_dest_unlock_node(dest); } } } -static void bgp_mac_rescan_evpn_table(struct bgp *bgp) +static void bgp_mac_rescan_evpn_table(struct bgp *bgp, struct ethaddr *macaddr) { struct listnode *node; struct peer *peer; @@ -214,12 +254,12 @@ static void bgp_mac_rescan_evpn_table(struct bgp *bgp) if (bgp_debug_update(peer, NULL, NULL, 1)) zlog_debug("Processing EVPN MAC interface change on peer %s", peer->host); - bgp_process_mac_rescan_table(bgp, peer, table); + bgp_process_mac_rescan_table(bgp, peer, table, macaddr); } } } -static void bgp_mac_rescan_all_evpn_tables(void) +static void bgp_mac_rescan_all_evpn_tables(struct ethaddr *macaddr) { struct listnode *node; struct bgp *bgp; @@ -228,11 +268,12 @@ static void bgp_mac_rescan_all_evpn_tables(void) struct bgp_table *table = bgp->rib[AFI_L2VPN][SAFI_EVPN]; if (table) - bgp_mac_rescan_evpn_table(bgp); + bgp_mac_rescan_evpn_table(bgp, macaddr); } } -static void bgp_mac_remove_ifp_internal(struct bgp_self_mac *bsm, char *ifname) +static void bgp_mac_remove_ifp_internal(struct bgp_self_mac *bsm, char *ifname, + struct ethaddr *macaddr) { struct listnode *node = NULL; char *name; @@ -248,11 +289,13 @@ static void bgp_mac_remove_ifp_internal(struct bgp_self_mac *bsm, char *ifname) } if (bsm->ifp_list->count == 0) { + struct ethaddr mac = *macaddr; + hash_release(bm->self_mac_hash, bsm); list_delete(&bsm->ifp_list); XFREE(MTYPE_BSM, bsm); - bgp_mac_rescan_all_evpn_tables(); + bgp_mac_rescan_all_evpn_tables(&mac); } } @@ -276,22 +319,26 @@ void bgp_mac_add_mac_entry(struct interface *ifp) listnode_add(bsm->ifp_list, ifname); if (old_bsm) - bgp_mac_remove_ifp_internal(old_bsm, ifname); + bgp_mac_remove_ifp_internal(old_bsm, ifname, + &old_bsm->macaddr); } else { /* * If old mac address is the same as the new, * then there is nothing to do here */ - if (old_bsm == bsm) + if (old_bsm == bsm) { + XFREE(MTYPE_BSM_STRING, ifname); return; + } if (old_bsm) - bgp_mac_remove_ifp_internal(old_bsm, ifp->name); + bgp_mac_remove_ifp_internal(old_bsm, ifp->name, + &old_bsm->macaddr); listnode_add(bsm->ifp_list, ifname); } - bgp_mac_rescan_all_evpn_tables(); + bgp_mac_rescan_all_evpn_tables(&bsm->macaddr); } void bgp_mac_del_mac_entry(struct interface *ifp) @@ -308,7 +355,7 @@ void bgp_mac_del_mac_entry(struct interface *ifp) * Write code to allow old mac address to no-longer * win if we happen to have received it from a peer. */ - bgp_mac_remove_ifp_internal(bsm, ifp->name); + bgp_mac_remove_ifp_internal(bsm, ifp->name, &bsm->macaddr); } /* This API checks MAC address against any of local @@ -316,7 +363,7 @@ void bgp_mac_del_mac_entry(struct interface *ifp) * An example: router-mac attribute in any of evpn update * requires to compare against local mac. */ -bool bgp_mac_exist(struct ethaddr *mac) +bool bgp_mac_exist(const struct ethaddr *mac) { struct bgp_self_mac lookup; struct bgp_self_mac *bsm; @@ -337,9 +384,9 @@ bool bgp_mac_exist(struct ethaddr *mac) * mac against any of local assigned (SVIs) MAC * address. */ -bool bgp_mac_entry_exists(struct prefix *p) +bool bgp_mac_entry_exists(const struct prefix *p) { - struct prefix_evpn *pevpn = (struct prefix_evpn *)p; + const struct prefix_evpn *pevpn = (const struct prefix_evpn *)p; if (pevpn->family != AF_EVPN) return false; diff --git a/bgpd/bgp_mac.h b/bgpd/bgp_mac.h index 68449b574a..4b94d80d1a 100644 --- a/bgpd/bgp_mac.h +++ b/bgpd/bgp_mac.h @@ -36,7 +36,7 @@ void bgp_mac_dump_table(struct vty *vty); /* * Function to lookup the prefix and see if we have a matching mac */ -bool bgp_mac_entry_exists(struct prefix *p); -bool bgp_mac_exist(struct ethaddr *mac); +bool bgp_mac_entry_exists(const struct prefix *p); +bool bgp_mac_exist(const struct ethaddr *mac); #endif diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c index adba73e404..fa67cfd562 100644 --- a/bgpd/bgp_main.c +++ b/bgpd/bgp_main.c @@ -27,7 +27,6 @@ #include "thread.h" #include #include "memory.h" -#include "memory_vty.h" #include "prefix.h" #include "log.h" #include "privs.h" @@ -61,6 +60,7 @@ #include "bgpd/bgp_keepalives.h" #include "bgpd/bgp_network.h" #include "bgpd/bgp_errors.h" +#include "bgpd/bgp_evpn_mh.h" #ifdef ENABLE_BGP_VNC #include "bgpd/rfapi/rfapi_backend.h" @@ -70,15 +70,12 @@ static const struct option longopts[] = { {"bgp_port", required_argument, NULL, 'p'}, {"listenon", required_argument, NULL, 'l'}, -#if CONFDATE > 20190521 - CPP_NOTICE("-r / --retain has reached deprecation EOL, remove") -#endif - {"retain", no_argument, NULL, 'r'}, {"no_kernel", no_argument, NULL, 'n'}, {"skip_runas", no_argument, NULL, 'S'}, {"ecmp", required_argument, NULL, 'e'}, {"int_num", required_argument, NULL, 'I'}, {"no_zebra", no_argument, NULL, 'Z'}, + {"socket_size", required_argument, NULL, 's'}, {0}}; /* signal definitions */ @@ -130,16 +127,27 @@ static struct frr_daemon_info bgpd_di; /* SIGHUP handler. */ void sighup(void) { - zlog_info("SIGHUP received"); - + zlog_info("SIGHUP received, ignoring"); + + return; + + /* + * This is turned off for the moment. There is all + * sorts of config turned off by bgp_terminate + * that is not setup properly again in bgp_reset. + * I see no easy way to do this nor do I see that + * this is a desirable way to reload config + * given the yang work. + */ /* Terminate all thread. */ - bgp_terminate(); - bgp_reset(); - zlog_info("bgpd restarting!"); - - /* Reload config file. */ - vty_read_config(NULL, bgpd_di.config_file, config_default); - + /* + * bgp_terminate(); + * bgp_reset(); + * zlog_info("bgpd restarting!"); + + * Reload config file. + * vty_read_config(NULL, bgpd_di.config_file, config_default); + */ /* Try to return to normal operation. */ } @@ -198,6 +206,8 @@ static __attribute__((__noreturn__)) void bgp_exit(int status) if (bgp_default) bgp_delete(bgp_default); + bgp_evpn_mh_finish(); + /* reverse bgp_dump_init */ bgp_dump_finish(); @@ -232,7 +242,7 @@ static __attribute__((__noreturn__)) void bgp_exit(int status) community_list_terminate(bgp_clist); bgp_vrf_terminate(); -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC vnc_zebra_destroy(); #endif bgp_zebra_destroy(); @@ -273,12 +283,19 @@ static int bgp_vrf_enable(struct vrf *vrf) zlog_debug("VRF enable add %s id %u", vrf->name, vrf->vrf_id); bgp = bgp_lookup_by_name(vrf->name); - if (bgp) { + if (bgp && bgp->vrf_id != vrf->vrf_id) { if (bgp->name && strmatch(vrf->name, VRF_DEFAULT_NAME)) { XFREE(MTYPE_BGP, bgp->name); - bgp->name = NULL; XFREE(MTYPE_BGP, bgp->name_pretty); bgp->name_pretty = XSTRDUP(MTYPE_BGP, "VRF default"); + bgp->inst_type = BGP_INSTANCE_TYPE_DEFAULT; +#ifdef ENABLE_BGP_VNC + if (!bgp->rfapi) { + bgp->rfapi = bgp_rfapi_new(bgp); + assert(bgp->rfapi); + assert(bgp->rfapi_cfg); + } +#endif /* ENABLE_BGP_VNC */ } old_vrf_id = bgp->vrf_id; /* We have instance configured, link to VRF and make it "up". */ @@ -347,7 +364,7 @@ static int bgp_vrf_disable(struct vrf *vrf) static void bgp_vrf_init(void) { vrf_init(bgp_vrf_new, bgp_vrf_enable, bgp_vrf_disable, - bgp_vrf_delete, NULL); + bgp_vrf_delete, bgp_vrf_enable); } static void bgp_vrf_terminate(void) @@ -355,7 +372,11 @@ static void bgp_vrf_terminate(void) vrf_terminate(); } -static const struct frr_yang_module_info *bgpd_yang_modules[] = { +static const struct frr_yang_module_info *const bgpd_yang_modules[] = { + &frr_filter_info, + &frr_interface_info, + &frr_route_map_info, + &frr_vrf_info, }; FRR_DAEMON_INFO(bgpd, BGP, .vty_port = BGP_VTY_PORT, @@ -367,10 +388,7 @@ FRR_DAEMON_INFO(bgpd, BGP, .vty_port = BGP_VTY_PORT, .privs = &bgpd_privs, .yang_modules = bgpd_yang_modules, .n_yang_modules = array_size(bgpd_yang_modules), ) -#if CONFDATE > 20190521 -CPP_NOTICE("-r / --retain has reached deprecation EOL, remove") -#endif -#define DEPRECATED_OPTIONS "r" +#define DEPRECATED_OPTIONS "" /* Main routine of bgpd. Treatment of argument and start bgp finite state machine is handled at here. */ @@ -385,17 +403,19 @@ int main(int argc, char **argv) int no_zebra_flag = 0; int skip_runas = 0; int instance = 0; + int buffer_size = BGP_SOCKET_SNDBUF_SIZE; frr_preinit(&bgpd_di, argc, argv); frr_opt_add( - "p:l:SnZe:I:" DEPRECATED_OPTIONS, longopts, + "p:l:SnZe:I:s:" DEPRECATED_OPTIONS, longopts, " -p, --bgp_port Set BGP listen port number (0 means do not listen).\n" " -l, --listenon Listen on specified address (implies -n)\n" " -n, --no_kernel Do not install route to kernel.\n" " -Z, --no_zebra Do not communicate with Zebra.\n" " -S, --skip_runas Skip capabilities checks, and changing user and group IDs.\n" " -e, --ecmp Specify ECMP to use.\n" - " -I, --int_num Set instance number (label-manager)\n"); + " -I, --int_num Set instance number (label-manager)\n" + " -s, --socket_size Set BGP peer socket send buffer size\n"); /* Command line argument treatment. */ while (1) { @@ -421,17 +441,21 @@ int main(int argc, char **argv) else bgp_port = tmp_port; break; - case 'e': - multipath_num = atoi(optarg); - if (multipath_num > MULTIPATH_NUM - || multipath_num <= 0) { + case 'e': { + unsigned long int parsed_multipath = + strtoul(optarg, NULL, 10); + if (parsed_multipath == 0 + || parsed_multipath > MULTIPATH_NUM + || parsed_multipath > UINT_MAX) { flog_err( EC_BGP_MULTIPATH, - "Multipath Number specified must be less than %d and greater than 0", + "Multipath Number specified must be less than %u and greater than 0", MULTIPATH_NUM); return 1; } + multipath_num = parsed_multipath; break; + } case 'l': bgp_address = optarg; /* listenon implies -n */ @@ -451,6 +475,9 @@ int main(int argc, char **argv) zlog_err("Instance %i out of range (0..%u)", instance, (unsigned short)-1); break; + case 's': + buffer_size = atoi(optarg); + break; default: frr_help_exit(1); break; @@ -460,7 +487,7 @@ int main(int argc, char **argv) memset(&bgpd_privs, 0, sizeof(bgpd_privs)); /* BGP master init. */ - bgp_master_init(frr_init()); + bgp_master_init(frr_init(), buffer_size); bm->port = bgp_port; if (bgp_port == 0) bgp_option_set(BGP_OPT_NO_LISTEN); @@ -481,9 +508,10 @@ int main(int argc, char **argv) frr_config_fork(); /* must be called after fork() */ + bgp_gr_apply_running_config(); bgp_pthreads_run(); frr_run(bm->master); /* Not reached. */ - return (0); + return 0; } diff --git a/bgpd/bgp_memory.c b/bgpd/bgp_memory.c index 3e4dfb11ad..8bdab16680 100644 --- a/bgpd/bgp_memory.c +++ b/bgpd/bgp_memory.c @@ -116,8 +116,11 @@ DEFINE_MTYPE(BGPD, LCOMMUNITY_STR, "Large Community display string") DEFINE_MTYPE(BGPD, LCOMMUNITY_VAL, "Large Community value") DEFINE_MTYPE(BGPD, BGP_EVPN, "BGP EVPN Information") -DEFINE_MTYPE(BGPD, BGP_EVPN_ES_VTEP, "BGP EVPN ES VTEP Ip") +DEFINE_MTYPE(BGPD, BGP_EVPN_MH_INFO, "BGP EVPN Multihoming Information") +DEFINE_MTYPE(BGPD, BGP_EVPN_ES_VTEP, "BGP EVPN ES VTEP") +DEFINE_MTYPE(BGPD, BGP_EVPN_ES_EVI_VTEP, "BGP EVPN ES-EVI VTEP") DEFINE_MTYPE(BGPD, BGP_EVPN_ES, "BGP EVPN ESI Information") +DEFINE_MTYPE(BGPD, BGP_EVPN_ES_EVI, "BGP EVPN ES-per-EVI Information") DEFINE_MTYPE(BGPD, BGP_EVPN_IMPORT_RT, "BGP EVPN Import RT") DEFINE_MTYPE(BGPD, BGP_EVPN_VRF_IMPORT_RT, "BGP EVPN VRF Import RT") DEFINE_MTYPE(BGPD, BGP_EVPN_MACIP, "BGP EVPN MAC IP") @@ -128,3 +131,6 @@ DEFINE_MTYPE(BGPD, BGP_FLOWSPEC_RULE_STR, "BGP flowspec rule str") DEFINE_MTYPE(BGPD, BGP_FLOWSPEC_COMPILED, "BGP flowspec compiled") DEFINE_MTYPE(BGPD, BGP_FLOWSPEC_NAME, "BGP flowspec name") DEFINE_MTYPE(BGPD, BGP_FLOWSPEC_INDEX, "BGP flowspec index") + +DEFINE_MTYPE(BGPD, BGP_SRV6_L3VPN, "BGP prefix-sid srv6 l3vpn servcie") +DEFINE_MTYPE(BGPD, BGP_SRV6_VPN, "BGP prefix-sid srv6 vpn service") diff --git a/bgpd/bgp_memory.h b/bgpd/bgp_memory.h index 03715f5621..d1ae392c65 100644 --- a/bgpd/bgp_memory.h +++ b/bgpd/bgp_memory.h @@ -111,8 +111,11 @@ DECLARE_MTYPE(LCOMMUNITY) DECLARE_MTYPE(LCOMMUNITY_STR) DECLARE_MTYPE(LCOMMUNITY_VAL) +DECLARE_MTYPE(BGP_EVPN_MH_INFO) DECLARE_MTYPE(BGP_EVPN_ES) +DECLARE_MTYPE(BGP_EVPN_ES_EVI) DECLARE_MTYPE(BGP_EVPN_ES_VTEP) +DECLARE_MTYPE(BGP_EVPN_ES_EVI_VTEP) DECLARE_MTYPE(BGP_EVPN) DECLARE_MTYPE(BGP_EVPN_IMPORT_RT) @@ -126,4 +129,7 @@ DECLARE_MTYPE(BGP_FLOWSPEC_COMPILED) DECLARE_MTYPE(BGP_FLOWSPEC_NAME) DECLARE_MTYPE(BGP_FLOWSPEC_INDEX) +DECLARE_MTYPE(BGP_SRV6_L3VPN) +DECLARE_MTYPE(BGP_SRV6_VPN) + #endif /* _QUAGGA_BGP_MEMORY_H */ diff --git a/bgpd/bgp_mpath.c b/bgpd/bgp_mpath.c index d5b3d6b197..ff5cfe05fb 100644 --- a/bgpd/bgp_mpath.c +++ b/bgpd/bgp_mpath.c @@ -280,7 +280,6 @@ void bgp_path_info_mpath_free(struct bgp_path_info_mpath **mpath) if ((*mpath)->mp_attr) bgp_attr_unintern(&(*mpath)->mp_attr); XFREE(MTYPE_BGP_MPATH_INFO, *mpath); - *mpath = NULL; } } @@ -294,6 +293,10 @@ static struct bgp_path_info_mpath * bgp_path_info_mpath_get(struct bgp_path_info *path) { struct bgp_path_info_mpath *mpath; + + if (!path) + return NULL; + if (!path->mpath) { mpath = bgp_path_info_mpath_new(); if (!mpath) @@ -387,7 +390,7 @@ uint32_t bgp_path_info_mpath_count(struct bgp_path_info *path) * Sets the count of multipaths into bestpath's mpath element */ static void bgp_path_info_mpath_count_set(struct bgp_path_info *path, - uint32_t count) + uint16_t count) { struct bgp_path_info_mpath *mpath; if (!count && !path->mpath) @@ -398,6 +401,40 @@ static void bgp_path_info_mpath_count_set(struct bgp_path_info *path, mpath->mp_count = count; } +/* + * bgp_path_info_mpath_lb_update + * + * Update cumulative info related to link-bandwidth + */ +static void bgp_path_info_mpath_lb_update(struct bgp_path_info *path, bool set, + bool all_paths_lb, uint64_t cum_bw) +{ + struct bgp_path_info_mpath *mpath; + + if ((mpath = path->mpath) == NULL) { + if (!set || (cum_bw == 0 && !all_paths_lb)) + return; + + mpath = bgp_path_info_mpath_get(path); + if (!mpath) + return; + } + if (set) { + if (cum_bw) + SET_FLAG(mpath->mp_flags, BGP_MP_LB_PRESENT); + else + UNSET_FLAG(mpath->mp_flags, BGP_MP_LB_PRESENT); + if (all_paths_lb) + SET_FLAG(mpath->mp_flags, BGP_MP_LB_ALL); + else + UNSET_FLAG(mpath->mp_flags, BGP_MP_LB_ALL); + mpath->cum_bw = cum_bw; + } else { + mpath->mp_flags = 0; + mpath->cum_bw = 0; + } +} + /* * bgp_path_info_mpath_attr * @@ -411,6 +448,42 @@ struct attr *bgp_path_info_mpath_attr(struct bgp_path_info *path) return path->mpath->mp_attr; } +/* + * bgp_path_info_chkwtd + * + * Return if we should attempt to do weighted ECMP or not + * The path passed in is the bestpath. + */ +bool bgp_path_info_mpath_chkwtd(struct bgp *bgp, struct bgp_path_info *path) +{ + /* Check if told to ignore weights or not multipath */ + if (bgp->lb_handling == BGP_LINK_BW_IGNORE_BW || !path->mpath) + return false; + + /* All paths in multipath should have associated weight (bandwidth) + * unless told explicitly otherwise. + */ + if (bgp->lb_handling != BGP_LINK_BW_SKIP_MISSING && + bgp->lb_handling != BGP_LINK_BW_DEFWT_4_MISSING) + return (path->mpath->mp_flags & BGP_MP_LB_ALL); + + /* At least one path should have bandwidth. */ + return (path->mpath->mp_flags & BGP_MP_LB_PRESENT); +} + +/* + * bgp_path_info_mpath_attr + * + * Given bestpath bgp_path_info, return cumulative bandwidth + * computed for all multipaths with bandwidth info + */ +uint64_t bgp_path_info_mpath_cumbw(struct bgp_path_info *path) +{ + if (!path->mpath) + return 0; + return path->mpath->cum_bw; +} + /* * bgp_path_info_mpath_attr_set * @@ -434,17 +507,20 @@ static void bgp_path_info_mpath_attr_set(struct bgp_path_info *path, * Compare and sync up the multipath list with the mp_list generated by * bgp_best_selection */ -void bgp_path_info_mpath_update(struct bgp_node *rn, +void bgp_path_info_mpath_update(struct bgp_dest *dest, struct bgp_path_info *new_best, struct bgp_path_info *old_best, struct list *mp_list, struct bgp_maxpaths_cfg *mpath_cfg) { uint16_t maxpaths, mpath_count, old_mpath_count; + uint32_t bwval; + uint64_t cum_bw, old_cum_bw; struct listnode *mp_node, *mp_next_node; struct bgp_path_info *cur_mpath, *new_mpath, *next_mpath, *prev_mpath; int mpath_changed, debug; - char pfx_buf[PREFIX2STR_BUFFER], nh_buf[2][INET6_ADDRSTRLEN]; + char nh_buf[2][INET6_ADDRSTRLEN]; + bool all_paths_lb; char path_buf[PATH_ADDPATH_STR_BUFFER]; mpath_changed = 0; @@ -452,12 +528,10 @@ void bgp_path_info_mpath_update(struct bgp_node *rn, mpath_count = 0; cur_mpath = NULL; old_mpath_count = 0; + old_cum_bw = cum_bw = 0; prev_mpath = new_best; mp_node = listhead(mp_list); - debug = bgp_debug_bestpath(&rn->p); - - if (debug) - prefix2str(&rn->p, pfx_buf, sizeof(pfx_buf)); + debug = bgp_debug_bestpath(dest); if (new_best) { mpath_count++; @@ -471,15 +545,19 @@ void bgp_path_info_mpath_update(struct bgp_node *rn, if (old_best) { cur_mpath = bgp_path_info_mpath_first(old_best); old_mpath_count = bgp_path_info_mpath_count(old_best); + old_cum_bw = bgp_path_info_mpath_cumbw(old_best); bgp_path_info_mpath_count_set(old_best, 0); + bgp_path_info_mpath_lb_update(old_best, false, false, 0); bgp_path_info_mpath_dequeue(old_best); } if (debug) zlog_debug( - "%s: starting mpath update, newbest %s num candidates %d old-mpath-count %d", - pfx_buf, new_best ? new_best->peer->host : "NONE", - mp_list ? listcount(mp_list) : 0, old_mpath_count); + "%pRN: starting mpath update, newbest %s num candidates %d old-mpath-count %d old-cum-bw u%" PRIu64, + bgp_dest_to_rnode(dest), + new_best ? new_best->peer->host : "NONE", + mp_list ? listcount(mp_list) : 0, + old_mpath_count, old_cum_bw); /* * We perform an ordered walk through both lists in parallel. @@ -492,6 +570,7 @@ void bgp_path_info_mpath_update(struct bgp_node *rn, * Note that new_best might be somewhere in the mp_list, so we need * to skip over it */ + all_paths_lb = true; /* We'll reset if any path doesn't have LB. */ while (mp_node || cur_mpath) { struct bgp_path_info *tmp_info; @@ -510,8 +589,8 @@ void bgp_path_info_mpath_update(struct bgp_node *rn, if (debug) zlog_debug( - "%s: comparing candidate %s with existing mpath %s", - pfx_buf, + "%pRN: comparing candidate %s with existing mpath %s", + bgp_dest_to_rnode(dest), tmp_info ? tmp_info->peer->host : "NONE", cur_mpath ? cur_mpath->peer->host : "NONE"); @@ -523,18 +602,25 @@ void bgp_path_info_mpath_update(struct bgp_node *rn, list_delete_node(mp_list, mp_node); bgp_path_info_mpath_dequeue(cur_mpath); if ((mpath_count < maxpaths) + && prev_mpath && bgp_path_info_nexthop_cmp(prev_mpath, cur_mpath)) { bgp_path_info_mpath_enqueue(prev_mpath, cur_mpath); prev_mpath = cur_mpath; mpath_count++; + if (ecommunity_linkbw_present( + cur_mpath->attr->ecommunity, &bwval)) + cum_bw += bwval; + else + all_paths_lb = false; if (debug) { bgp_path_info_path_with_addpath_rx_str( cur_mpath, path_buf); zlog_debug( - "%s: %s is still multipath, cur count %d", - pfx_buf, path_buf, mpath_count); + "%pRN: %s is still multipath, cur count %d", + bgp_dest_to_rnode(dest), + path_buf, mpath_count); } } else { mpath_changed = 1; @@ -542,8 +628,9 @@ void bgp_path_info_mpath_update(struct bgp_node *rn, bgp_path_info_path_with_addpath_rx_str( cur_mpath, path_buf); zlog_debug( - "%s: remove mpath %s nexthop %s, cur count %d", - pfx_buf, path_buf, + "%pRN: remove mpath %s nexthop %s, cur count %d", + bgp_dest_to_rnode(dest), + path_buf, inet_ntop(AF_INET, &cur_mpath->attr ->nexthop, @@ -575,8 +662,8 @@ void bgp_path_info_mpath_update(struct bgp_node *rn, bgp_path_info_path_with_addpath_rx_str( cur_mpath, path_buf); zlog_debug( - "%s: remove mpath %s nexthop %s, cur count %d", - pfx_buf, path_buf, + "%pRN: remove mpath %s nexthop %s, cur count %d", + bgp_dest_to_rnode(dest), path_buf, inet_ntop(AF_INET, &cur_mpath->attr->nexthop, nh_buf[0], sizeof(nh_buf[0])), @@ -609,8 +696,6 @@ void bgp_path_info_mpath_update(struct bgp_node *rn, if ((mpath_count < maxpaths) && (new_mpath != new_best) && bgp_path_info_nexthop_cmp(prev_mpath, new_mpath)) { - if (new_mpath == next_mpath) - bgp_path_info_mpath_next(new_mpath); bgp_path_info_mpath_dequeue(new_mpath); bgp_path_info_mpath_enqueue(prev_mpath, @@ -618,12 +703,18 @@ void bgp_path_info_mpath_update(struct bgp_node *rn, prev_mpath = new_mpath; mpath_changed = 1; mpath_count++; + if (ecommunity_linkbw_present( + new_mpath->attr->ecommunity, &bwval)) + cum_bw += bwval; + else + all_paths_lb = false; if (debug) { bgp_path_info_path_with_addpath_rx_str( new_mpath, path_buf); zlog_debug( - "%s: add mpath %s nexthop %s, cur count %d", - pfx_buf, path_buf, + "%pRN: add mpath %s nexthop %s, cur count %d", + bgp_dest_to_rnode(dest), + path_buf, inet_ntop(AF_INET, &new_mpath->attr ->nexthop, @@ -637,16 +728,29 @@ void bgp_path_info_mpath_update(struct bgp_node *rn, } if (new_best) { + bgp_path_info_mpath_count_set(new_best, mpath_count - 1); + if (mpath_count <= 1 || + !ecommunity_linkbw_present( + new_best->attr->ecommunity, &bwval)) + all_paths_lb = false; + else + cum_bw += bwval; + bgp_path_info_mpath_lb_update(new_best, true, + all_paths_lb, cum_bw); + if (debug) zlog_debug( - "%s: New mpath count (incl newbest) %d mpath-change %s", - pfx_buf, mpath_count, - mpath_changed ? "YES" : "NO"); + "%pRN: New mpath count (incl newbest) %d mpath-change %s all_paths_lb %d cum_bw u%" PRIu64, + bgp_dest_to_rnode(dest), mpath_count, + mpath_changed ? "YES" : "NO", + all_paths_lb, cum_bw); - bgp_path_info_mpath_count_set(new_best, mpath_count - 1); if (mpath_changed || (bgp_path_info_mpath_count(new_best) != old_mpath_count)) SET_FLAG(new_best->flags, BGP_PATH_MULTIPATH_CHG); + if ((mpath_count - 1) != old_mpath_count || + old_cum_bw != cum_bw) + SET_FLAG(new_best->flags, BGP_PATH_LINK_BW_CHG); } } @@ -671,6 +775,7 @@ void bgp_mp_dmed_deselect(struct bgp_path_info *dmed_best) bgp_path_info_mpath_count_set(dmed_best, 0); UNSET_FLAG(dmed_best->flags, BGP_PATH_MULTIPATH_CHG); + UNSET_FLAG(dmed_best->flags, BGP_PATH_LINK_BW_CHG); assert(bgp_path_info_mpath_first(dmed_best) == NULL); } @@ -717,10 +822,11 @@ void bgp_path_info_mpath_aggregate_update(struct bgp_path_info *new_best, return; } - bgp_attr_dup(&attr, new_best->attr); + attr = *new_best->attr; - if (new_best->peer && bgp_flag_check(new_best->peer->bgp, - BGP_FLAG_MULTIPATH_RELAX_AS_SET)) { + if (new_best->peer + && CHECK_FLAG(new_best->peer->bgp->flags, + BGP_FLAG_MULTIPATH_RELAX_AS_SET)) { /* aggregate attribute from multipath constituents */ aspath = aspath_dup(attr.aspath); @@ -795,7 +901,7 @@ void bgp_path_info_mpath_aggregate_update(struct bgp_path_info *new_best, } /* Zap multipath attr nexthop so we set nexthop to self */ - attr.nexthop.s_addr = 0; + attr.nexthop.s_addr = INADDR_ANY; memset(&attr.mp_nexthop_global, 0, sizeof(struct in6_addr)); /* TODO: should we set ATOMIC_AGGREGATE and AGGREGATOR? */ diff --git a/bgpd/bgp_mpath.h b/bgpd/bgp_mpath.h index d15f3c9035..a07e48159e 100644 --- a/bgpd/bgp_mpath.h +++ b/bgpd/bgp_mpath.h @@ -36,10 +36,18 @@ struct bgp_path_info_mpath { struct bgp_path_info *mp_info; /* When attached to best path, the number of selected multipaths */ - uint32_t mp_count; + uint16_t mp_count; + + /* Flags - relevant as noted. */ + uint16_t mp_flags; +#define BGP_MP_LB_PRESENT 0x1 /* Link-bandwidth present for >= 1 path */ +#define BGP_MP_LB_ALL 0x2 /* Link-bandwidth present for all multipaths */ /* Aggregated attribute for advertising multipath route */ struct attr *mp_attr; + + /* Cumulative bandiwdth of all multipaths - attached to best path. */ + uint64_t cum_bw; }; /* Functions to support maximum-paths configuration */ @@ -56,7 +64,7 @@ extern void bgp_mp_list_init(struct list *); extern void bgp_mp_list_clear(struct list *); extern void bgp_mp_list_add(struct list *mp_list, struct bgp_path_info *mpinfo); extern void bgp_mp_dmed_deselect(struct bgp_path_info *dmed_best); -extern void bgp_path_info_mpath_update(struct bgp_node *rn, +extern void bgp_path_info_mpath_update(struct bgp_dest *dest, struct bgp_path_info *new_best, struct bgp_path_info *old_best, struct list *mp_list, @@ -78,5 +86,8 @@ bgp_path_info_mpath_next(struct bgp_path_info *path); /* Accessors for multipath information */ extern uint32_t bgp_path_info_mpath_count(struct bgp_path_info *path); extern struct attr *bgp_path_info_mpath_attr(struct bgp_path_info *path); +extern bool bgp_path_info_mpath_chkwtd(struct bgp *bgp, + struct bgp_path_info *path); +extern uint64_t bgp_path_info_mpath_cumbw(struct bgp_path_info *path); #endif /* _QUAGGA_BGP_MPATH_H */ diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index d7cb84c323..4511573f88 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -48,7 +48,7 @@ #include "bgpd/bgp_nht.h" #include "bgpd/bgp_evpn.h" -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC #include "bgpd/rfapi/rfapi_backend.h" #endif @@ -101,27 +101,26 @@ void encode_label(mpls_label_t label, mpls_label_t *label_pnt) int bgp_nlri_parse_vpn(struct peer *peer, struct attr *attr, struct bgp_nlri *packet) { - uint8_t *pnt; - uint8_t *lim; struct prefix p; - int psize = 0; - int prefixlen; + uint8_t psize = 0; + uint8_t prefixlen; uint16_t type; struct rd_as rd_as; struct rd_ip rd_ip; - struct prefix_rd prd; + struct prefix_rd prd = {0}; mpls_label_t label = {0}; afi_t afi; safi_t safi; int addpath_encoded; uint32_t addpath_id; + int ret = 0; /* Make prefix_rd */ prd.family = AF_UNSPEC; prd.prefixlen = 64; - pnt = packet->nlri; - lim = pnt + packet->length; + struct stream *data = stream_new(packet->length); + stream_put(data, packet->nlri, packet->length); afi = packet->afi; safi = packet->safi; addpath_id = 0; @@ -132,22 +131,26 @@ int bgp_nlri_parse_vpn(struct peer *peer, struct attr *attr, PEER_CAP_ADDPATH_AF_TX_RCV)); #define VPN_PREFIXLEN_MIN_BYTES (3 + 8) /* label + RD */ - for (; pnt < lim; pnt += psize) { + while (STREAM_READABLE(data) > 0) { /* Clear prefix structure. */ memset(&p, 0, sizeof(struct prefix)); if (addpath_encoded) { + STREAM_GET(&addpath_id, data, BGP_ADDPATH_ID_LEN); + addpath_id = ntohl(addpath_id); + } - /* When packet overflow occurs return immediately. */ - if (pnt + BGP_ADDPATH_ID_LEN > lim) - return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW; - - addpath_id = ntohl(*((uint32_t *)pnt)); - pnt += BGP_ADDPATH_ID_LEN; + if (STREAM_READABLE(data) < 1) { + flog_err( + EC_BGP_UPDATE_RCV, + "%s [Error] Update packet error / VPN (truncated NLRI of size %u; no prefix length)", + peer->host, packet->length); + ret = BGP_NLRI_PARSE_ERROR_PACKET_LENGTH; + goto done; } /* Fetch prefix length. */ - prefixlen = *pnt++; + STREAM_GETC(data, prefixlen); p.family = afi2family(packet->afi); psize = PSIZE(prefixlen); @@ -156,16 +159,18 @@ int bgp_nlri_parse_vpn(struct peer *peer, struct attr *attr, EC_BGP_UPDATE_RCV, "%s [Error] Update packet error / VPN (prefix length %d less than VPN min length)", peer->host, prefixlen); - return BGP_NLRI_PARSE_ERROR_PREFIX_LENGTH; + ret = BGP_NLRI_PARSE_ERROR_PREFIX_LENGTH; + goto done; } /* sanity check against packet data */ - if ((pnt + psize) > lim) { + if (STREAM_READABLE(data) < psize) { flog_err( EC_BGP_UPDATE_RCV, "%s [Error] Update packet error / VPN (prefix length %d exceeds packet size %u)", - peer->host, prefixlen, (uint)(lim - pnt)); - return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW; + peer->host, prefixlen, packet->length); + ret = BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW; + goto done; } /* sanity check against storage for the IP address portion */ @@ -176,7 +181,8 @@ int bgp_nlri_parse_vpn(struct peer *peer, struct attr *attr, peer->host, prefixlen - VPN_PREFIXLEN_MIN_BYTES * 8, sizeof(p.u)); - return BGP_NLRI_PARSE_ERROR_PACKET_LENGTH; + ret = BGP_NLRI_PARSE_ERROR_PACKET_LENGTH; + goto done; } /* Sanity check against max bitlen of the address family */ @@ -187,33 +193,51 @@ int bgp_nlri_parse_vpn(struct peer *peer, struct attr *attr, peer->host, prefixlen - VPN_PREFIXLEN_MIN_BYTES * 8, p.family, prefix_blen(&p)); - return BGP_NLRI_PARSE_ERROR_PACKET_LENGTH; + ret = BGP_NLRI_PARSE_ERROR_PACKET_LENGTH; + goto done; } /* Copy label to prefix. */ - memcpy(&label, pnt, BGP_LABEL_BYTES); + if (STREAM_READABLE(data) < BGP_LABEL_BYTES) { + flog_err( + EC_BGP_UPDATE_RCV, + "%s [Error] Update packet error / VPN (truncated NLRI of size %u; no label)", + peer->host, packet->length); + ret = BGP_NLRI_PARSE_ERROR_PACKET_LENGTH; + goto done; + } + + STREAM_GET(&label, data, BGP_LABEL_BYTES); bgp_set_valid_label(&label); /* Copy routing distinguisher to rd. */ - memcpy(&prd.val, pnt + BGP_LABEL_BYTES, 8); + if (STREAM_READABLE(data) < 8) { + flog_err( + EC_BGP_UPDATE_RCV, + "%s [Error] Update packet error / VPN (truncated NLRI of size %u; no RD)", + peer->host, packet->length); + ret = BGP_NLRI_PARSE_ERROR_PACKET_LENGTH; + goto done; + } + STREAM_GET(&prd.val, data, 8); /* Decode RD type. */ - type = decode_rd_type(pnt + BGP_LABEL_BYTES); + type = decode_rd_type(prd.val); switch (type) { case RD_TYPE_AS: - decode_rd_as(pnt + 5, &rd_as); + decode_rd_as(&prd.val[2], &rd_as); break; case RD_TYPE_AS4: - decode_rd_as4(pnt + 5, &rd_as); + decode_rd_as4(&prd.val[2], &rd_as); break; case RD_TYPE_IP: - decode_rd_ip(pnt + 5, &rd_ip); + decode_rd_ip(&prd.val[2], &rd_ip); break; -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC case RD_TYPE_VNC_ETH: break; #endif @@ -223,11 +247,9 @@ int bgp_nlri_parse_vpn(struct peer *peer, struct attr *attr, break; /* just report */ } - p.prefixlen = - prefixlen - - VPN_PREFIXLEN_MIN_BYTES * 8; /* exclude label & RD */ - memcpy(p.u.val, pnt + VPN_PREFIXLEN_MIN_BYTES, - psize - VPN_PREFIXLEN_MIN_BYTES); + /* exclude label & RD */ + p.prefixlen = prefixlen - VPN_PREFIXLEN_MIN_BYTES * 8; + STREAM_GET(p.u.val, data, psize - VPN_PREFIXLEN_MIN_BYTES); if (attr) { bgp_update(peer, &p, addpath_id, attr, packet->afi, @@ -240,15 +262,27 @@ int bgp_nlri_parse_vpn(struct peer *peer, struct attr *attr, } } /* Packet length consistency check. */ - if (pnt != lim) { + if (STREAM_READABLE(data) != 0) { flog_err( EC_BGP_UPDATE_RCV, "%s [Error] Update packet error / VPN (%zu data remaining after parsing)", - peer->host, lim - pnt); + peer->host, STREAM_READABLE(data)); return BGP_NLRI_PARSE_ERROR_PACKET_LENGTH; } - return 0; + goto done; + +stream_failure: + flog_err( + EC_BGP_UPDATE_RCV, + "%s [Error] Update packet error / VPN (NLRI of size %u - length error)", + peer->host, packet->length); + ret = BGP_NLRI_PARSE_ERROR_PACKET_LENGTH; + +done: + stream_free(data); + return ret; + #undef VPN_PREFIXLEN_MIN_BYTES } @@ -273,8 +307,7 @@ void vpn_leak_zebra_vrf_label_update(struct bgp *bgp, afi_t afi) if (bgp->vrf_id == VRF_UNKNOWN) { if (debug) { zlog_debug( - "%s: vrf %s: afi %s: vrf_id not set, " - "can't set zebra vrf label", + "%s: vrf %s: afi %s: vrf_id not set, can't set zebra vrf label", __func__, bgp->name_pretty, afi2str(afi)); } return; @@ -319,9 +352,6 @@ void vpn_leak_zebra_vrf_label_withdraw(struct bgp *bgp, afi_t afi) bgp->name_pretty, bgp->vrf_id); } - if (label == BGP_PREVENT_VRF_2_VRF_LEAK) - label = MPLS_LABEL_NONE; - zclient_send_vrf_label(zclient, bgp->vrf_id, afi, label, ZEBRA_LSP_BGP); bgp->vpn_policy[afi].tovpn_zebra_vrf_label_last_sent = label; } @@ -387,25 +417,24 @@ int vpn_leak_label_callback( return 0; } -static int ecom_intersect(struct ecommunity *e1, struct ecommunity *e2) +static bool ecom_intersect(struct ecommunity *e1, struct ecommunity *e2) { int i; int j; if (!e1 || !e2) - return 0; - + return false; for (i = 0; i < e1->size; ++i) { for (j = 0; j < e2->size; ++j) { - if (!memcmp(e1->val + (i * ECOMMUNITY_SIZE), - e2->val + (j * ECOMMUNITY_SIZE), - ECOMMUNITY_SIZE)) { + if (!memcmp(e1->val + (i * e1->unit_size), + e2->val + (j * e2->unit_size), + e1->unit_size)) { - return 1; + return true; } } } - return 0; + return false; } static bool labels_same(struct bgp_path_info *bpi, mpls_label_t *label, @@ -464,24 +493,22 @@ static void setlabels(struct bgp_path_info *bpi, */ static struct bgp_path_info * leak_update(struct bgp *bgp, /* destination bgp instance */ - struct bgp_node *bn, struct attr *new_attr, /* already interned */ + struct bgp_dest *bn, struct attr *new_attr, /* already interned */ afi_t afi, safi_t safi, struct bgp_path_info *source_bpi, mpls_label_t *label, uint32_t num_labels, void *parent, struct bgp *bgp_orig, struct prefix *nexthop_orig, int nexthop_self_flag, int debug) { - struct prefix *p = &bn->p; + const struct prefix *p = bgp_dest_get_prefix(bn); struct bgp_path_info *bpi; struct bgp_path_info *bpi_ultimate; struct bgp_path_info *new; - char buf_prefix[PREFIX_STRLEN]; - if (debug) { - prefix2str(&bn->p, buf_prefix, sizeof(buf_prefix)); - zlog_debug("%s: entry: leak-to=%s, p=%s, type=%d, sub_type=%d", - __func__, bgp->name_pretty, buf_prefix, - source_bpi->type, source_bpi->sub_type); - } + if (debug) + zlog_debug( + "%s: entry: leak-to=%s, p=%pRN, type=%d, sub_type=%d", + __func__, bgp->name_pretty, bn, source_bpi->type, + source_bpi->sub_type); /* * Routes that are redistributed into BGP from zebra do not get @@ -506,7 +533,7 @@ leak_update(struct bgp *bgp, /* destination bgp instance */ /* * match parent */ - for (bpi = bgp_node_get_bgp_path_info(bn); bpi; bpi = bpi->next) { + for (bpi = bgp_dest_get_bgp_path_info(bn); bpi; bpi = bpi->next) { if (bpi->extra && bpi->extra->parent == parent) break; } @@ -520,9 +547,8 @@ leak_update(struct bgp *bgp, /* destination bgp instance */ bgp_attr_unintern(&new_attr); if (debug) zlog_debug( - "%s: ->%s: %s: Found route, no change", - __func__, bgp->name_pretty, - buf_prefix); + "%s: ->%s: %pRN: Found route, no change", + __func__, bgp->name_pretty, bn); return NULL; } @@ -579,11 +605,11 @@ leak_update(struct bgp *bgp, /* destination bgp instance */ /* Process change. */ bgp_aggregate_increment(bgp, p, bpi, afi, safi); bgp_process(bgp, bn, afi, safi); - bgp_unlock_node(bn); + bgp_dest_unlock_node(bn); if (debug) - zlog_debug("%s: ->%s: %s Found route, changed attr", - __func__, bgp->name_pretty, buf_prefix); + zlog_debug("%s: ->%s: %pRN Found route, changed attr", + __func__, bgp->name_pretty, bn); return bpi; } @@ -600,7 +626,8 @@ leak_update(struct bgp *bgp, /* destination bgp instance */ setlabels(new, label, num_labels); new->extra->parent = bgp_path_info_lock(parent); - bgp_lock_node((struct bgp_node *)((struct bgp_path_info *)parent)->net); + bgp_dest_lock_node( + (struct bgp_dest *)((struct bgp_path_info *)parent)->net); if (bgp_orig) new->extra->bgp_orig = bgp_lock(bgp_orig); if (nexthop_orig) @@ -643,12 +670,12 @@ leak_update(struct bgp *bgp, /* destination bgp instance */ bgp_aggregate_increment(bgp, p, new, afi, safi); bgp_path_info_add(bn, new); - bgp_unlock_node(bn); + bgp_dest_unlock_node(bn); bgp_process(bgp, bn, afi, safi); if (debug) - zlog_debug("%s: ->%s: %s: Added new route", __func__, - bgp->name_pretty, buf_prefix); + zlog_debug("%s: ->%s: %pRN: Added new route", __func__, + bgp->name_pretty, bn); return new; } @@ -659,14 +686,14 @@ void vpn_leak_from_vrf_update(struct bgp *bgp_vpn, /* to */ struct bgp_path_info *path_vrf) /* route */ { int debug = BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF); - struct prefix *p = &path_vrf->net->p; + const struct prefix *p = bgp_dest_get_prefix(path_vrf->net); afi_t afi = family2afi(p->family); struct attr static_attr = {0}; struct attr *new_attr = NULL; safi_t safi = SAFI_MPLS_VPN; mpls_label_t label_val; mpls_label_t label; - struct bgp_node *bn; + struct bgp_dest *bn; const char *debugmsg; int nexthop_self_flag = 0; @@ -702,7 +729,8 @@ void vpn_leak_from_vrf_update(struct bgp *bgp_vpn, /* to */ return; } - bgp_attr_dup(&static_attr, path_vrf->attr); /* shallow copy */ + /* shallow copy */ + static_attr = *path_vrf->attr; /* * route map handling @@ -745,10 +773,15 @@ void vpn_leak_from_vrf_update(struct bgp *bgp_vpn, /* to */ struct ecommunity *old_ecom; struct ecommunity *new_ecom; + /* Export with the 'from' instance's export RTs. */ + /* If doing VRF-to-VRF leaking, strip existing RTs first. */ old_ecom = static_attr.ecommunity; if (old_ecom) { - new_ecom = ecommunity_merge( - ecommunity_dup(old_ecom), + new_ecom = ecommunity_dup(old_ecom); + if (CHECK_FLAG(bgp_vrf->af_flags[afi][SAFI_UNICAST], + BGP_CONFIG_VRF_TO_VRF_EXPORT)) + ecommunity_strip_rts(new_ecom); + new_ecom = ecommunity_merge(new_ecom, bgp_vrf->vpn_policy[afi] .rtlist[BGP_VPN_POLICY_DIR_TOVPN]); if (!old_ecom->refcnt) @@ -784,12 +817,12 @@ void vpn_leak_from_vrf_update(struct bgp *bgp_vpn, /* to */ static_attr.nexthop.s_addr = nexthop->u.prefix4.s_addr; static_attr.mp_nexthop_global_in = nexthop->u.prefix4; - static_attr.mp_nexthop_len = 4; + static_attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4; break; case AF_INET6: static_attr.mp_nexthop_global = nexthop->u.prefix6; - static_attr.mp_nexthop_len = 16; + static_attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV6_GLOBAL; break; default: @@ -805,7 +838,8 @@ void vpn_leak_from_vrf_update(struct bgp *bgp_vpn, /* to */ */ static_attr.mp_nexthop_global_in = static_attr.nexthop; - static_attr.mp_nexthop_len = 4; + static_attr.mp_nexthop_len = + BGP_ATTR_NHLEN_IPV4; /* * XXX Leave static_attr.nexthop * intact for NHT @@ -824,7 +858,8 @@ void vpn_leak_from_vrf_update(struct bgp *bgp_vpn, /* to */ && !BGP_ATTR_NEXTHOP_AFI_IP6(path_vrf->attr)) { static_attr.mp_nexthop_global_in.s_addr = static_attr.nexthop.s_addr; - static_attr.mp_nexthop_len = 4; + static_attr.mp_nexthop_len = + BGP_ATTR_NHLEN_IPV4; static_attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP); } @@ -885,19 +920,17 @@ void vpn_leak_from_vrf_withdraw(struct bgp *bgp_vpn, /* to */ struct bgp_path_info *path_vrf) /* route */ { int debug = BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF); - struct prefix *p = &path_vrf->net->p; + const struct prefix *p = bgp_dest_get_prefix(path_vrf->net); afi_t afi = family2afi(p->family); safi_t safi = SAFI_MPLS_VPN; struct bgp_path_info *bpi; - struct bgp_node *bn; + struct bgp_dest *bn; const char *debugmsg; - char buf_prefix[PREFIX_STRLEN]; if (debug) { - prefix2str(p, buf_prefix, sizeof(buf_prefix)); zlog_debug( - "%s: entry: leak-from=%s, p=%s, type=%d, sub_type=%d", - __func__, bgp_vrf->name_pretty, buf_prefix, + "%s: entry: leak-from=%s, p=%pRN, type=%d, sub_type=%d", + __func__, bgp_vrf->name_pretty, path_vrf->net, path_vrf->type, path_vrf->sub_type); } @@ -932,7 +965,7 @@ void vpn_leak_from_vrf_withdraw(struct bgp *bgp_vpn, /* to */ * vrf -> vpn * match original bpi imported from */ - for (bpi = bgp_node_get_bgp_path_info(bn); bpi; bpi = bpi->next) { + for (bpi = bgp_dest_get_bgp_path_info(bn); bpi; bpi = bpi->next) { if (bpi->extra && bpi->extra->parent == path_vrf) { break; } @@ -946,7 +979,7 @@ void vpn_leak_from_vrf_withdraw(struct bgp *bgp_vpn, /* to */ bgp_path_info_delete(bn, bpi); bgp_process(bgp_vpn, bn, afi, safi); } - bgp_unlock_node(bn); + bgp_dest_unlock_node(bn); } void vpn_leak_from_vrf_withdraw_all(struct bgp *bgp_vpn, /* to */ @@ -954,34 +987,30 @@ void vpn_leak_from_vrf_withdraw_all(struct bgp *bgp_vpn, /* to */ afi_t afi) { int debug = BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF); - struct bgp_node *prn; + struct bgp_dest *pdest; safi_t safi = SAFI_MPLS_VPN; /* * Walk vpn table, delete bpi with bgp_orig == bgp_vrf */ - for (prn = bgp_table_top(bgp_vpn->rib[afi][safi]); prn; - prn = bgp_route_next(prn)) { + for (pdest = bgp_table_top(bgp_vpn->rib[afi][safi]); pdest; + pdest = bgp_route_next(pdest)) { struct bgp_table *table; - struct bgp_node *bn; + struct bgp_dest *bn; struct bgp_path_info *bpi; /* This is the per-RD table of prefixes */ - table = bgp_node_get_bgp_table_info(prn); + table = bgp_dest_get_bgp_table_info(pdest); if (!table) continue; for (bn = bgp_table_top(table); bn; bn = bgp_route_next(bn)) { - - char buf[PREFIX2STR_BUFFER]; - - bpi = bgp_node_get_bgp_path_info(bn); + bpi = bgp_dest_get_bgp_path_info(bn); if (debug && bpi) { - zlog_debug( - "%s: looking at prefix %s", __func__, - prefix2str(&bn->p, buf, sizeof(buf))); + zlog_debug("%s: looking at prefix %pRN", + __func__, bn); } for (; bpi; bpi = bpi->next) { @@ -999,8 +1028,10 @@ void vpn_leak_from_vrf_withdraw_all(struct bgp *bgp_vpn, /* to */ if (debug) zlog_debug("%s: deleting it", __func__); - bgp_aggregate_decrement(bgp_vpn, &bn->p, - bpi, afi, safi); + bgp_aggregate_decrement( + bgp_vpn, + bgp_dest_get_prefix(bn), bpi, + afi, safi); bgp_path_info_delete(bn, bpi); bgp_process(bgp_vpn, bn, afi, safi); } @@ -1013,7 +1044,7 @@ void vpn_leak_from_vrf_update_all(struct bgp *bgp_vpn, /* to */ struct bgp *bgp_vrf, /* from */ afi_t afi) { - struct bgp_node *bn; + struct bgp_dest *bn; struct bgp_path_info *bpi; int debug = BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF); @@ -1027,7 +1058,7 @@ void vpn_leak_from_vrf_update_all(struct bgp *bgp_vpn, /* to */ if (debug) zlog_debug("%s: node=%p", __func__, bn); - for (bpi = bgp_node_get_bgp_path_info(bn); bpi; + for (bpi = bgp_dest_get_bgp_path_info(bn); bpi; bpi = bpi->next) { if (debug) zlog_debug( @@ -1043,12 +1074,12 @@ vpn_leak_to_vrf_update_onevrf(struct bgp *bgp_vrf, /* to */ struct bgp *bgp_vpn, /* from */ struct bgp_path_info *path_vpn) /* route */ { - struct prefix *p = &path_vpn->net->p; + const struct prefix *p = bgp_dest_get_prefix(path_vpn->net); afi_t afi = family2afi(p->family); struct attr static_attr = {0}; struct attr *new_attr = NULL; - struct bgp_node *bn; + struct bgp_dest *bn; safi_t safi = SAFI_UNICAST; const char *debugmsg; struct prefix nexthop_orig; @@ -1083,7 +1114,30 @@ vpn_leak_to_vrf_update_onevrf(struct bgp *bgp_vrf, /* to */ buf_prefix, bgp_vrf->name_pretty); } - bgp_attr_dup(&static_attr, path_vpn->attr); /* shallow copy */ + /* shallow copy */ + static_attr = *path_vpn->attr; + + struct ecommunity *old_ecom; + struct ecommunity *new_ecom; + + /* If doing VRF-to-VRF leaking, strip RTs. */ + old_ecom = static_attr.ecommunity; + if (old_ecom && CHECK_FLAG(bgp_vrf->af_flags[afi][safi], + BGP_CONFIG_VRF_TO_VRF_IMPORT)) { + new_ecom = ecommunity_dup(old_ecom); + ecommunity_strip_rts(new_ecom); + static_attr.ecommunity = new_ecom; + + if (new_ecom->size == 0) { + UNSET_FLAG(static_attr.flag, + ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES)); + ecommunity_free(&new_ecom); + static_attr.ecommunity = NULL; + } + + if (!old_ecom->refcnt) + ecommunity_free(&old_ecom); + } /* * Nexthop: stash and clear @@ -1192,7 +1246,7 @@ vpn_leak_to_vrf_update_onevrf(struct bgp *bgp_vrf, /* to */ if (bpi_ultimate->net) { struct bgp_table *table; - table = bgp_node_table(bpi_ultimate->net); + table = bgp_dest_table(bpi_ultimate->net); if (table && (table->safi == SAFI_UNICAST)) origin_local = 1; } @@ -1207,12 +1261,9 @@ vpn_leak_to_vrf_update_onevrf(struct bgp *bgp_vrf, /* to */ } } - if (debug) { - char buf_prefix[PREFIX_STRLEN]; - prefix2str(p, buf_prefix, sizeof(buf_prefix)); - zlog_debug("%s: pfx %s: num_labels %d", __func__, buf_prefix, - num_labels); - } + if (debug) + zlog_debug("%s: pfx %pRN: num_labels %d", __func__, + path_vpn->net, num_labels); /* * For VRF-2-VRF route-leaking, @@ -1252,29 +1303,26 @@ void vpn_leak_to_vrf_update(struct bgp *bgp_vpn, /* from */ void vpn_leak_to_vrf_withdraw(struct bgp *bgp_vpn, /* from */ struct bgp_path_info *path_vpn) /* route */ { - struct prefix *p; + const struct prefix *p; afi_t afi; safi_t safi = SAFI_UNICAST; struct bgp *bgp; struct listnode *mnode, *mnnode; - struct bgp_node *bn; + struct bgp_dest *bn; struct bgp_path_info *bpi; const char *debugmsg; - char buf_prefix[PREFIX_STRLEN]; int debug = BGP_DEBUG(vpn, VPN_LEAK_TO_VRF); - if (debug) { - prefix2str(&path_vpn->net->p, buf_prefix, sizeof(buf_prefix)); - zlog_debug("%s: entry: p=%s, type=%d, sub_type=%d", __func__, - buf_prefix, path_vpn->type, path_vpn->sub_type); - } + if (debug) + zlog_debug("%s: entry: p=%pRN, type=%d, sub_type=%d", __func__, + path_vpn->net, path_vpn->type, path_vpn->sub_type); if (debug) zlog_debug("%s: start (path_vpn=%p)", __func__, path_vpn); if (!path_vpn->net) { -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC /* BGP_ROUTE_RFP routes do not have path_vpn->net set (yet) */ if (path_vpn->type == ZEBRA_ROUTE_BGP && path_vpn->sub_type == BGP_ROUTE_RFP) { @@ -1289,7 +1337,7 @@ void vpn_leak_to_vrf_withdraw(struct bgp *bgp_vpn, /* from */ return; } - p = &path_vpn->net->p; + p = bgp_dest_get_prefix(path_vpn->net); afi = family2afi(p->family); /* Loop over VRFs */ @@ -1315,7 +1363,7 @@ void vpn_leak_to_vrf_withdraw(struct bgp *bgp_vpn, /* from */ bn = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi, p, NULL); - for (bpi = bgp_node_get_bgp_path_info(bn); bpi; + for (bpi = bgp_dest_get_bgp_path_info(bn); bpi; bpi = bpi->next) { if (bpi->extra && (struct bgp_path_info *)bpi->extra->parent @@ -1332,14 +1380,14 @@ void vpn_leak_to_vrf_withdraw(struct bgp *bgp_vpn, /* from */ bgp_path_info_delete(bn, bpi); bgp_process(bgp, bn, afi, safi); } - bgp_unlock_node(bn); + bgp_dest_unlock_node(bn); } } void vpn_leak_to_vrf_withdraw_all(struct bgp *bgp_vrf, /* to */ afi_t afi) { - struct bgp_node *bn; + struct bgp_dest *bn; struct bgp_path_info *bpi; safi_t safi = SAFI_UNICAST; int debug = BGP_DEBUG(vpn, VPN_LEAK_TO_VRF); @@ -1352,7 +1400,7 @@ void vpn_leak_to_vrf_withdraw_all(struct bgp *bgp_vrf, /* to */ for (bn = bgp_table_top(bgp_vrf->rib[afi][safi]); bn; bn = bgp_route_next(bn)) { - for (bpi = bgp_node_get_bgp_path_info(bn); bpi; + for (bpi = bgp_dest_get_bgp_path_info(bn); bpi; bpi = bpi->next) { if (bpi->extra && bpi->extra->bgp_orig != bgp_vrf @@ -1360,8 +1408,9 @@ void vpn_leak_to_vrf_withdraw_all(struct bgp *bgp_vrf, /* to */ && is_pi_family_vpn(bpi->extra->parent)) { /* delete route */ - bgp_aggregate_decrement(bgp_vrf, &bn->p, bpi, - afi, safi); + bgp_aggregate_decrement(bgp_vrf, + bgp_dest_get_prefix(bn), + bpi, afi, safi); bgp_path_info_delete(bn, bpi); bgp_process(bgp_vrf, bn, afi, safi); } @@ -1374,7 +1423,7 @@ void vpn_leak_to_vrf_update_all(struct bgp *bgp_vrf, /* to */ afi_t afi) { struct prefix_rd prd; - struct bgp_node *prn; + struct bgp_dest *pdest; safi_t safi = SAFI_MPLS_VPN; assert(bgp_vpn); @@ -1382,27 +1431,27 @@ void vpn_leak_to_vrf_update_all(struct bgp *bgp_vrf, /* to */ /* * Walk vpn table */ - for (prn = bgp_table_top(bgp_vpn->rib[afi][safi]); prn; - prn = bgp_route_next(prn)) { - + for (pdest = bgp_table_top(bgp_vpn->rib[afi][safi]); pdest; + pdest = bgp_route_next(pdest)) { + const struct prefix *p = bgp_dest_get_prefix(pdest); struct bgp_table *table; - struct bgp_node *bn; + struct bgp_dest *bn; struct bgp_path_info *bpi; memset(&prd, 0, sizeof(prd)); prd.family = AF_UNSPEC; prd.prefixlen = 64; - memcpy(prd.val, prn->p.u.val, 8); + memcpy(prd.val, &p->u.val, 8); /* This is the per-RD table of prefixes */ - table = bgp_node_get_bgp_table_info(prn); + table = bgp_dest_get_bgp_table_info(pdest); if (!table) continue; for (bn = bgp_table_top(table); bn; bn = bgp_route_next(bn)) { - for (bpi = bgp_node_get_bgp_path_info(bn); bpi; + for (bpi = bgp_dest_get_bgp_path_info(bn); bpi; bpi = bpi->next) { if (bpi->extra @@ -1491,7 +1540,8 @@ static void vpn_policy_routemap_update(struct bgp *bgp, const char *rmap_name) /* This API is used during router-id change, reflect VPNs * auto RD and RT values and readvertise routes to VPN table. */ -void vpn_handle_router_id_update(struct bgp *bgp, bool withdraw) +void vpn_handle_router_id_update(struct bgp *bgp, bool withdraw, + bool is_config) { afi_t afi; int debug; @@ -1529,7 +1579,10 @@ void vpn_handle_router_id_update(struct bgp *bgp, bool withdraw) ecom = bgp->vpn_policy[afi].rtlist[edir]; for (ALL_LIST_ELEMENTS_RO(bgp->vpn_policy[afi]. export_vrf, node, vname)) { - bgp_import = bgp_lookup_by_name(vname); + if (strcmp(vname, VRF_DEFAULT_NAME) == 0) + bgp_import = bgp_get_default(); + else + bgp_import = bgp_lookup_by_name(vname); if (!bgp_import) continue; @@ -1539,6 +1592,20 @@ void vpn_handle_router_id_update(struct bgp *bgp, bool withdraw) } } else { + /* + * Router-id changes that are not explicit config + * changes should not replace configured RD/RT. + */ + if (!is_config) { + if (CHECK_FLAG(bgp->vpn_policy[afi].flags, + BGP_VPN_POLICY_TOVPN_RD_SET)) { + if (debug) + zlog_debug("%s: auto router-id change skipped", + __func__); + goto postchange; + } + } + /* New router-id derive auto RD and RT and export * to VPN */ @@ -1555,7 +1622,10 @@ void vpn_handle_router_id_update(struct bgp *bgp, bool withdraw) ecom = bgp->vpn_policy[afi].rtlist[edir]; for (ALL_LIST_ELEMENTS_RO(bgp->vpn_policy[afi]. export_vrf, node, vname)) { - bgp_import = bgp_lookup_by_name(vname); + if (strcmp(vname, VRF_DEFAULT_NAME) == 0) + bgp_import = bgp_get_default(); + else + bgp_import = bgp_lookup_by_name(vname); if (!bgp_import) continue; if (bgp_import->vpn_policy[afi].rtlist[idir]) @@ -1568,6 +1638,8 @@ void vpn_handle_router_id_update(struct bgp *bgp, bool withdraw) = ecommunity_dup(ecom); } + +postchange: /* Update routes to VPN */ vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, afi, bgp_get_default(), @@ -1600,11 +1672,13 @@ void vrf_import_from_vrf(struct bgp *to_bgp, struct bgp *from_bgp, { const char *export_name; vpn_policy_direction_t idir, edir; - char *vname; - char buf[1000]; + char *vname, *tmp_name; + char buf[RD_ADDRSTRLEN]; struct ecommunity *ecom; bool first_export = false; int debug; + struct listnode *node; + bool is_inst_match = false; export_name = to_bgp->name ? to_bgp->name : VRF_DEFAULT_NAME; idir = BGP_VPN_POLICY_DIR_FROMVPN; @@ -1620,12 +1694,45 @@ void vrf_import_from_vrf(struct bgp *to_bgp, struct bgp *from_bgp, vname = (from_bgp->name ? XSTRDUP(MTYPE_TMP, from_bgp->name) : XSTRDUP(MTYPE_TMP, VRF_DEFAULT_NAME)); - listnode_add(to_bgp->vpn_policy[afi].import_vrf, vname); + /* Check the import_vrf list of destination vrf for the source vrf name, + * insert otherwise. + */ + for (ALL_LIST_ELEMENTS_RO(to_bgp->vpn_policy[afi].import_vrf, + node, tmp_name)) { + if (strcmp(vname, tmp_name) == 0) { + is_inst_match = true; + break; + } + } + if (!is_inst_match) + listnode_add(to_bgp->vpn_policy[afi].import_vrf, + vname); + else + XFREE(MTYPE_TMP, vname); - if (!listcount(from_bgp->vpn_policy[afi].export_vrf)) - first_export = true; + /* Check if the source vrf already exports to any vrf, + * first time export requires to setup auto derived RD/RT values. + * Add the destination vrf name to export vrf list if it is + * not present. + */ + is_inst_match = false; vname = XSTRDUP(MTYPE_TMP, export_name); - listnode_add(from_bgp->vpn_policy[afi].export_vrf, vname); + if (!listcount(from_bgp->vpn_policy[afi].export_vrf)) { + first_export = true; + } else { + for (ALL_LIST_ELEMENTS_RO(from_bgp->vpn_policy[afi].export_vrf, + node, tmp_name)) { + if (strcmp(vname, tmp_name) == 0) { + is_inst_match = true; + break; + } + } + } + if (!is_inst_match) + listnode_add(from_bgp->vpn_policy[afi].export_vrf, + vname); + else + XFREE(MTYPE_TMP, vname); /* Update import RT for current VRF using export RT of the VRF we're * importing from. First though, make sure "import_vrf" has that @@ -1657,19 +1764,26 @@ void vrf_import_from_vrf(struct bgp *to_bgp, struct bgp *from_bgp, if (debug) { const char *from_name; + char *ecom1, *ecom2; from_name = from_bgp->name ? from_bgp->name : VRF_DEFAULT_NAME; - zlog_debug("%s from %s to %s first_export %u import-rt %s export-rt %s", - __func__, from_name, export_name, first_export, - to_bgp->vpn_policy[afi].rtlist[idir] ? - (ecommunity_ecom2str(to_bgp->vpn_policy[afi]. - rtlist[idir], - ECOMMUNITY_FORMAT_ROUTE_MAP, 0)) : " ", - to_bgp->vpn_policy[afi].rtlist[edir] ? - (ecommunity_ecom2str(to_bgp->vpn_policy[afi]. - rtlist[edir], - ECOMMUNITY_FORMAT_ROUTE_MAP, 0)) : " "); + + ecom1 = ecommunity_ecom2str( + to_bgp->vpn_policy[afi].rtlist[idir], + ECOMMUNITY_FORMAT_ROUTE_MAP, 0); + + ecom2 = ecommunity_ecom2str( + to_bgp->vpn_policy[afi].rtlist[edir], + ECOMMUNITY_FORMAT_ROUTE_MAP, 0); + + zlog_debug( + "%s from %s to %s first_export %u import-rt %s export-rt %s", + __func__, from_name, export_name, first_export, ecom1, + ecom2); + + ecommunity_strfree(&ecom1); + ecommunity_strfree(&ecom2); } /* Does "import_vrf" first need to export its routes or that @@ -1688,7 +1802,7 @@ void vrf_unimport_from_vrf(struct bgp *to_bgp, struct bgp *from_bgp, const char *export_name, *tmp_name; vpn_policy_direction_t idir, edir; char *vname; - struct ecommunity *ecom; + struct ecommunity *ecom = NULL; struct listnode *node; int debug; @@ -1731,12 +1845,15 @@ void vrf_unimport_from_vrf(struct bgp *to_bgp, struct bgp *from_bgp, vpn_leak_prechange(idir, afi, bgp_get_default(), to_bgp); if (to_bgp->vpn_policy[afi].import_vrf->count == 0) { - UNSET_FLAG(to_bgp->af_flags[afi][safi], - BGP_CONFIG_VRF_TO_VRF_IMPORT); - ecommunity_free(&to_bgp->vpn_policy[afi].rtlist[idir]); + if (!to_bgp->vpn_policy[afi].rmap[idir]) + UNSET_FLAG(to_bgp->af_flags[afi][safi], + BGP_CONFIG_VRF_TO_VRF_IMPORT); + if (to_bgp->vpn_policy[afi].rtlist[idir]) + ecommunity_free(&to_bgp->vpn_policy[afi].rtlist[idir]); } else { ecom = from_bgp->vpn_policy[afi].rtlist[edir]; - ecommunity_del_val(to_bgp->vpn_policy[afi].rtlist[idir], + if (ecom) + ecommunity_del_val(to_bgp->vpn_policy[afi].rtlist[idir], (struct ecommunity_val *)ecom->val); vpn_leak_postchange(idir, afi, bgp_get_default(), to_bgp); } @@ -1769,8 +1886,11 @@ void vrf_unimport_from_vrf(struct bgp *to_bgp, struct bgp *from_bgp, * * import_vrf and export_vrf must match in having * the in/out names as appropriate. + * export_vrf list could have been cleaned up + * as part of no router bgp source instnace. */ - assert(vname); + if (!vname) + return; listnode_delete(from_bgp->vpn_policy[afi].export_vrf, vname); XFREE(MTYPE_TMP, vname); @@ -2380,6 +2500,10 @@ vrf_id_t get_first_vrf_for_redirect_with_rt(struct ecommunity *eckey) { struct listnode *mnode, *mnnode; struct bgp *bgp; + afi_t afi = AFI_IP; + + if (eckey->unit_size == IPV6_ECOMMUNITY_SIZE) + afi = AFI_IP6; for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp)) { struct ecommunity *ec; @@ -2387,7 +2511,10 @@ vrf_id_t get_first_vrf_for_redirect_with_rt(struct ecommunity *eckey) if (bgp->inst_type != BGP_INSTANCE_TYPE_VRF) continue; - ec = bgp->vpn_policy[AFI_IP].import_redirect_rtlist; + ec = bgp->vpn_policy[afi].import_redirect_rtlist; + + if (ec && eckey->unit_size != ec->unit_size) + continue; if (ecom_intersect(ec, eckey)) return bgp->vrf_id; @@ -2457,3 +2584,166 @@ void vpn_leak_postchange_all(void) bgp); } } + +/* When a bgp vrf instance is unconfigured, remove its routes + * from the VPN table and this vrf could be importing routes from other + * bgp vrf instnaces, unimport them. + * VRF X and VRF Y are exporting routes to each other. + * When VRF X is deleted, unimport its routes from all target vrfs, + * also VRF Y should unimport its routes from VRF X table. + * This will ensure VPN table is cleaned up appropriately. + */ +int bgp_vpn_leak_unimport(struct bgp *from_bgp) +{ + struct bgp *to_bgp; + const char *tmp_name; + char *vname; + struct listnode *node, *next; + safi_t safi = SAFI_UNICAST; + afi_t afi; + bool is_vrf_leak_bind; + int debug; + + if (from_bgp->inst_type != BGP_INSTANCE_TYPE_VRF) + return 0; + + debug = (BGP_DEBUG(vpn, VPN_LEAK_TO_VRF) | + BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF)); + + tmp_name = from_bgp->name ? from_bgp->name : VRF_DEFAULT_NAME; + + for (afi = 0; afi < AFI_MAX; ++afi) { + /* vrf leak is for IPv4 and IPv6 Unicast only */ + if (afi != AFI_IP && afi != AFI_IP6) + continue; + + for (ALL_LIST_ELEMENTS_RO(bm->bgp, next, to_bgp)) { + if (from_bgp == to_bgp) + continue; + + /* Unimport and remove source vrf from the + * other vrfs import list. + */ + struct vpn_policy *to_vpolicy; + + is_vrf_leak_bind = false; + to_vpolicy = &(to_bgp->vpn_policy[afi]); + for (ALL_LIST_ELEMENTS_RO(to_vpolicy->import_vrf, node, + vname)) { + if (strcmp(vname, tmp_name) == 0) { + is_vrf_leak_bind = true; + break; + } + } + /* skip this bgp instance as there is no leak to this + * vrf instance. + */ + if (!is_vrf_leak_bind) + continue; + + if (debug) + zlog_debug("%s: unimport routes from %s to_bgp %s afi %s import vrfs count %u", + __func__, from_bgp->name_pretty, + to_bgp->name_pretty, afi2str(afi), + to_vpolicy->import_vrf->count); + + vrf_unimport_from_vrf(to_bgp, from_bgp, afi, safi); + + /* readd vrf name as unimport removes import vrf name + * from the destination vrf's import list where the + * `import vrf` configuration still exist. + */ + vname = XSTRDUP(MTYPE_TMP, tmp_name); + listnode_add(to_bgp->vpn_policy[afi].import_vrf, + vname); + SET_FLAG(to_bgp->af_flags[afi][safi], + BGP_CONFIG_VRF_TO_VRF_IMPORT); + + /* If to_bgp exports its routes to the bgp vrf + * which is being deleted, un-import the + * to_bgp routes from VPN. + */ + for (ALL_LIST_ELEMENTS_RO(to_bgp->vpn_policy[afi] + .export_vrf, node, + vname)) { + if (strcmp(vname, tmp_name) == 0) { + vrf_unimport_from_vrf(from_bgp, to_bgp, + afi, safi); + break; + } + } + } + } + return 0; +} + +/* When a router bgp is configured, there could be a bgp vrf + * instance importing routes from this newly configured + * bgp vrf instance. Export routes from configured + * bgp vrf to VPN. + * VRF Y has import from bgp vrf x, + * when a bgp vrf x instance is created, export its routes + * to VRF Y instance. + */ +void bgp_vpn_leak_export(struct bgp *from_bgp) +{ + afi_t afi; + const char *export_name; + char *vname; + struct listnode *node, *next; + struct ecommunity *ecom; + vpn_policy_direction_t idir, edir; + safi_t safi = SAFI_UNICAST; + struct bgp *to_bgp; + int debug; + + debug = (BGP_DEBUG(vpn, VPN_LEAK_TO_VRF) | + BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF)); + + idir = BGP_VPN_POLICY_DIR_FROMVPN; + edir = BGP_VPN_POLICY_DIR_TOVPN; + + export_name = from_bgp->name ? from_bgp->name : VRF_DEFAULT_NAME; + + for (afi = 0; afi < AFI_MAX; ++afi) { + /* vrf leak is for IPv4 and IPv6 Unicast only */ + if (afi != AFI_IP && afi != AFI_IP6) + continue; + + for (ALL_LIST_ELEMENTS_RO(bm->bgp, next, to_bgp)) { + if (from_bgp == to_bgp) + continue; + + /* bgp instance has import list, check to see if newly + * configured bgp instance is the list. + */ + struct vpn_policy *to_vpolicy; + + to_vpolicy = &(to_bgp->vpn_policy[afi]); + for (ALL_LIST_ELEMENTS_RO(to_vpolicy->import_vrf, + node, vname)) { + if (strcmp(vname, export_name) != 0) + continue; + + if (debug) + zlog_debug("%s: found from_bgp %s in to_bgp %s import list, import routes.", + __func__, + export_name, to_bgp->name_pretty); + + ecom = from_bgp->vpn_policy[afi].rtlist[edir]; + /* remove import rt, it will be readded + * as part of import from vrf. + */ + if (ecom) + ecommunity_del_val( + to_vpolicy->rtlist[idir], + (struct ecommunity_val *) + ecom->val); + vrf_import_from_vrf(to_bgp, from_bgp, + afi, safi); + break; + + } + } + } +} diff --git a/bgpd/bgp_mplsvpn.h b/bgpd/bgp_mplsvpn.h index 1526a8111e..1150583fc8 100644 --- a/bgpd/bgp_mplsvpn.h +++ b/bgpd/bgp_mplsvpn.h @@ -233,7 +233,7 @@ static inline bool is_route_injectable_into_vpn(struct bgp_path_info *pi) { struct bgp_path_info *parent_pi; struct bgp_table *table; - struct bgp_node *rn; + struct bgp_dest *dest; if (pi->sub_type != BGP_ROUTE_IMPORTED || !pi->extra || @@ -241,10 +241,10 @@ static inline bool is_route_injectable_into_vpn(struct bgp_path_info *pi) return true; parent_pi = (struct bgp_path_info *)pi->extra->parent; - rn = parent_pi->net; - if (!rn) + dest = parent_pi->net; + if (!dest) return true; - table = bgp_node_table(rn); + table = bgp_dest_table(dest); if (table && (table->afi == AFI_IP || table->afi == AFI_IP6) && table->safi == SAFI_MPLS_VPN) @@ -264,6 +264,9 @@ extern void vpn_policy_routemap_event(const char *rmap_name); extern vrf_id_t get_first_vrf_for_redirect_with_rt(struct ecommunity *eckey); extern void vpn_leak_postchange_all(void); -extern void vpn_handle_router_id_update(struct bgp *bgp, bool withdraw); +extern void vpn_handle_router_id_update(struct bgp *bgp, bool withdraw, + bool is_config); +extern int bgp_vpn_leak_unimport(struct bgp *from_bgp); +extern void bgp_vpn_leak_export(struct bgp *from_bgp); #endif /* _QUAGGA_BGP_MPLSVPN_H */ diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c index 6a5c2c4b38..cae11ae7bd 100644 --- a/bgpd/bgp_network.c +++ b/bgpd/bgp_network.c @@ -57,8 +57,26 @@ struct bgp_listener { union sockunion su; struct thread *thread; struct bgp *bgp; + char *name; }; +void bgp_dump_listener_info(struct vty *vty) +{ + struct listnode *node; + struct bgp_listener *listener; + + vty_out(vty, "Name fd Address\n"); + vty_out(vty, "---------------------------\n"); + for (ALL_LIST_ELEMENTS_RO(bm->listen_sockets, node, listener)) { + char buf[SU_ADDRSTRLEN]; + + vty_out(vty, "%-16s %d %s\n", + listener->name ? listener->name : VRF_DEFAULT_NAME, + listener->fd, + sockunion2str(&listener->su, buf, sizeof(buf))); + } +} + /* * Set MD5 key for the socket, for the given IPv4 peer address. * If the password is NULL or zero-length, the option will be disabled. @@ -122,7 +140,7 @@ static int bgp_md5_set_connect(int socket, union sockunion *su, int ret = -1; #if HAVE_DECL_TCP_MD5SIG - frr_elevate_privs(&bgpd_privs) { + frr_with_privs(&bgpd_privs) { ret = bgp_md5_set_socket(socket, su, prefixlen, password); } #endif /* HAVE_TCP_MD5SIG */ @@ -140,15 +158,28 @@ static int bgp_md5_set_password(struct peer *peer, const char *password) * Set or unset the password on the listen socket(s). Outbound * connections are taken care of in bgp_connect() below. */ - frr_elevate_privs(&bgpd_privs) - { + frr_with_privs(&bgpd_privs) { for (ALL_LIST_ELEMENTS_RO(bm->listen_sockets, node, listener)) - if (listener->su.sa.sa_family - == peer->su.sa.sa_family) { + if (listener->su.sa.sa_family == + peer->su.sa.sa_family) { uint16_t prefixlen = peer->su.sa.sa_family == AF_INET - ? IPV4_MAX_PREFIXLEN - : IPV6_MAX_PREFIXLEN; + ? IPV4_MAX_PREFIXLEN + : IPV6_MAX_PREFIXLEN; + + /* + * if we have stored a BGP vrf instance in the + * listener it must match the bgp instance in + * the peer otherwise the peer bgp instance + * must be the default vrf or a view instance + */ + if (!listener->bgp) { + if (peer->bgp->vrf_id != VRF_DEFAULT + && peer->bgp->inst_type + != BGP_INSTANCE_TYPE_VIEW) + continue; + } else if (listener->bgp != peer->bgp) + continue; ret = bgp_md5_set_socket(listener->fd, &peer->su, prefixlen, @@ -159,7 +190,7 @@ static int bgp_md5_set_password(struct peer *peer, const char *password) return ret; } -int bgp_md5_set_prefix(struct prefix *p, const char *password) +int bgp_md5_set_prefix(struct bgp *bgp, struct prefix *p, const char *password) { int ret = 0; union sockunion su; @@ -167,10 +198,11 @@ int bgp_md5_set_prefix(struct prefix *p, const char *password) struct bgp_listener *listener; /* Set or unset the password on the listen socket(s). */ - frr_elevate_privs(&bgpd_privs) - { + frr_with_privs(&bgpd_privs) { for (ALL_LIST_ELEMENTS_RO(bm->listen_sockets, node, listener)) - if (listener->su.sa.sa_family == p->family) { + if (listener->su.sa.sa_family == p->family + && ((bgp->vrf_id == VRF_DEFAULT) + || (listener->bgp == bgp))) { prefix2sockunion(p, &su); ret = bgp_md5_set_socket(listener->fd, &su, p->prefixlen, @@ -182,9 +214,9 @@ int bgp_md5_set_prefix(struct prefix *p, const char *password) return ret; } -int bgp_md5_unset_prefix(struct prefix *p) +int bgp_md5_unset_prefix(struct bgp *bgp, struct prefix *p) { - return bgp_md5_set_prefix(p, NULL); + return bgp_md5_set_prefix(bgp, p, NULL); } int bgp_md5_set(struct peer *peer) @@ -205,7 +237,7 @@ int bgp_set_socket_ttl(struct peer *peer, int bgp_sock) int ret = 0; /* In case of peer is EBGP, we should set TTL for this connection. */ - if (!peer->gtsm_hops && (peer_sort(peer) == BGP_PEER_EBGP)) { + if (!peer->gtsm_hops && (peer_sort_lookup(peer) == BGP_PEER_EBGP)) { ret = sockopt_ttl(peer->su.sa.sa_family, bgp_sock, peer->ttl); if (ret) { flog_err( @@ -322,6 +354,14 @@ static int bgp_get_instance_for_inc_conn(int sock, struct bgp **bgp_inst) #endif } +static void bgp_socket_set_buffer_size(const int fd) +{ + if (getsockopt_so_sendbuf(fd) < (int)bm->socket_buffer) + setsockopt_so_sendbuf(fd, bm->socket_buffer); + if (getsockopt_so_recvbuf(fd) < (int)bm->socket_buffer) + setsockopt_so_recvbuf(fd, bm->socket_buffer); +} + /* Accept bgp connection. */ static int bgp_accept(struct thread *thread) { @@ -336,10 +376,13 @@ static int bgp_accept(struct thread *thread) sockunion_init(&su); + bgp = bgp_lookup_by_name(listener->name); + /* Register accept thread. */ accept_sock = THREAD_FD(thread); if (accept_sock < 0) { - flog_err_sys(EC_LIB_SOCKET, "accept_sock is nevative value %d", + flog_err_sys(EC_LIB_SOCKET, + "[Error] BGP accept socket fd is negative: %d", accept_sock); return -1; } @@ -350,10 +393,37 @@ static int bgp_accept(struct thread *thread) /* Accept client connection. */ bgp_sock = sockunion_accept(accept_sock, &su); + int save_errno = errno; if (bgp_sock < 0) { - flog_err_sys(EC_LIB_SOCKET, - "[Error] BGP socket accept failed (%s)", - safe_strerror(errno)); + if (save_errno == EINVAL) { + struct vrf *vrf = + bgp ? vrf_lookup_by_id(bgp->vrf_id) : NULL; + + /* + * It appears that sometimes, when VRFs are deleted on + * the system, it takes a little while for us to get + * notified about that. In the meantime we endlessly + * loop on accept(), because the socket, having been + * bound to a now-deleted VRF device, is in some weird + * state which causes accept() to fail. + * + * To avoid this, if we see accept() fail with EINVAL, + * we cancel ourselves and trust that when the VRF + * deletion notification comes in the event handler for + * that will take care of cleaning us up. + */ + flog_err_sys( + EC_LIB_SOCKET, + "[Error] accept() failed with error \"%s\" on BGP listener socket %d for BGP instance in VRF \"%s\"; refreshing socket", + safe_strerror(save_errno), accept_sock, + VRF_LOGNAME(vrf)); + THREAD_OFF(listener->thread); + } else { + flog_err_sys( + EC_LIB_SOCKET, + "[Error] BGP socket accept failed (%s); retrying", + safe_strerror(save_errno)); + } return -1; } set_nonblocking(bgp_sock); @@ -373,8 +443,7 @@ static int bgp_accept(struct thread *thread) return -1; } - /* Set socket send buffer size */ - setsockopt_so_sendbuf(bgp_sock, BGP_SOCKET_SNDBUF_SIZE); + bgp_socket_set_buffer_size(bgp_sock); /* Check remote IP address */ peer1 = peer_lookup(bgp, &su); @@ -398,19 +467,21 @@ static int bgp_accept(struct thread *thread) if (!peer1) { if (bgp_debug_neighbor_events(NULL)) { zlog_debug( - "[Event] %s connection rejected - not configured" - " and not valid for dynamic", - inet_sutop(&su, buf)); + "[Event] %s connection rejected(%s:%u:%s) - not configured and not valid for dynamic", + inet_sutop(&su, buf), bgp->name_pretty, bgp->as, + VRF_LOGNAME(vrf_lookup_by_id(bgp->vrf_id))); } close(bgp_sock); return -1; } - if (CHECK_FLAG(peer1->flags, PEER_FLAG_SHUTDOWN)) { + if (CHECK_FLAG(peer1->flags, PEER_FLAG_SHUTDOWN) + || CHECK_FLAG(peer1->bgp->flags, BGP_FLAG_SHUTDOWN)) { if (bgp_debug_neighbor_events(peer1)) zlog_debug( - "[Event] connection from %s rejected due to admin shutdown", - inet_sutop(&su, buf)); + "[Event] connection from %s rejected(%s:%u:%s) due to admin shutdown", + inet_sutop(&su, buf), bgp->name_pretty, bgp->as, + VRF_LOGNAME(vrf_lookup_by_id(bgp->vrf_id))); close(bgp_sock); return -1; } @@ -440,6 +511,19 @@ static int bgp_accept(struct thread *thread) return -1; } + /* Do not try to reconnect if the peer reached maximum + * prefixes, restart timer is still running or the peer + * is shutdown. + */ + if (BGP_PEER_START_SUPPRESSED(peer1)) { + if (bgp_debug_neighbor_events(peer1)) + zlog_debug( + "[Event] Incoming BGP connection rejected from %s due to maximum-prefix or shutdown", + peer1->host); + close(bgp_sock); + return -1; + } + if (bgp_debug_neighbor_events(peer1)) zlog_debug("[Event] BGP connection from host %s fd %d", inet_sutop(&su, buf), bgp_sock); @@ -450,8 +534,7 @@ static int bgp_accept(struct thread *thread) */ if (bgp_debug_neighbor_events(peer1)) zlog_debug( - "[Event] New active connection from peer %s, Killing" - " previous active connection", + "[Event] New active connection from peer %s, Killing previous active connection", peer1->host); peer_delete(peer1->doppelganger); } @@ -468,6 +551,20 @@ static int bgp_accept(struct thread *thread) hash_get(peer->bgp->peerhash, peer, hash_alloc_intern); peer_xfer_config(peer, peer1); + bgp_peer_gr_flags_update(peer); + + BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(peer->bgp, + peer->bgp->peer); + + if (bgp_peer_gr_mode_get(peer) == PEER_DISABLE) { + + UNSET_FLAG(peer->sflags, PEER_STATUS_NSF_MODE); + + if (CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT)) { + peer_nsf_stop(peer); + } + } + UNSET_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE); peer->doppelganger = peer1; @@ -478,7 +575,6 @@ static int bgp_accept(struct thread *thread) BGP_TIMER_OFF(peer->t_start); /* created in peer_create() */ SET_FLAG(peer->sflags, PEER_STATUS_ACCEPT_PEER); - /* Make dummy peer until read Open packet. */ if (peer1->status == Established && CHECK_FLAG(peer1->sflags, PEER_STATUS_NSF_MODE)) { @@ -489,7 +585,12 @@ static int bgp_accept(struct thread *thread) * existing established connection and move state to connect. */ peer1->last_reset = PEER_DOWN_NSF_CLOSE_SESSION; - SET_FLAG(peer1->sflags, PEER_STATUS_NSF_WAIT); + + if (CHECK_FLAG(peer1->flags, PEER_FLAG_GRACEFUL_RESTART) + || CHECK_FLAG(peer1->flags, + PEER_FLAG_GRACEFUL_RESTART_HELPER)) + SET_FLAG(peer1->sflags, PEER_STATUS_NSF_WAIT); + bgp_event_update(peer1, TCP_connection_closed); } @@ -588,8 +689,6 @@ static int bgp_update_source(struct peer *peer) return ret; } -#define DATAPLANE_MARK 254 /* main table ID */ - /* BGP try to connect to the peer. */ int bgp_connect(struct peer *peer) { @@ -601,7 +700,7 @@ int bgp_connect(struct peer *peer) zlog_debug("Peer address not learnt: Returning from connect"); return 0; } - frr_elevate_privs(&bgpd_privs) { + frr_with_privs(&bgpd_privs) { /* Make socket for the peer. */ peer->fd = vrf_sockunion_socket(&peer->su, peer->bgp->vrf_id, bgp_get_bound_name(peer)); @@ -611,21 +710,16 @@ int bgp_connect(struct peer *peer) set_nonblocking(peer->fd); - /* Set socket send buffer size */ - setsockopt_so_sendbuf(peer->fd, BGP_SOCKET_SNDBUF_SIZE); + bgp_socket_set_buffer_size(peer->fd); if (bgp_set_socket_ttl(peer, peer->fd) < 0) return -1; sockopt_reuseaddr(peer->fd); sockopt_reuseport(peer->fd); - if (sockopt_mark_default(peer->fd, DATAPLANE_MARK, &bgpd_privs) < 0) - flog_warn(EC_BGP_NO_SOCKOPT_MARK, - "Unable to set mark on FD for peer %s, err=%s", - peer->host, safe_strerror(errno)); #ifdef IPTOS_PREC_INTERNETCONTROL - frr_elevate_privs(&bgpd_privs) { + frr_with_privs(&bgpd_privs) { if (sockunion_family(&peer->su) == AF_INET) setsockopt_ipv4_tos(peer->fd, IPTOS_PREC_INTERNETCONTROL); @@ -703,7 +797,7 @@ static int bgp_listener(int sock, struct sockaddr *sa, socklen_t salen, sockopt_reuseaddr(sock); sockopt_reuseport(sock); - frr_elevate_privs(&bgpd_privs) { + frr_with_privs(&bgpd_privs) { #ifdef IPTOS_PREC_INTERNETCONTROL if (sa->sa_family == AF_INET) @@ -732,9 +826,11 @@ static int bgp_listener(int sock, struct sockaddr *sa, socklen_t salen, listener = XCALLOC(MTYPE_BGP_LISTENER, sizeof(*listener)); listener->fd = sock; + listener->name = XSTRDUP(MTYPE_BGP_LISTENER, bgp->name); - /* this socket needs a change of ns. record bgp back pointer */ - if (bgp->vrf_id != VRF_DEFAULT && vrf_is_backend_netns()) + /* this socket is in a vrf record bgp back pointer */ + if (bgp->vrf_id != VRF_DEFAULT + && bgp->inst_type != BGP_INSTANCE_TYPE_VIEW) listener->bgp = bgp; memcpy(&listener->su, sa, salen); @@ -762,7 +858,7 @@ int bgp_socket(struct bgp *bgp, unsigned short port, const char *address) snprintf(port_str, sizeof(port_str), "%d", port); port_str[sizeof(port_str) - 1] = '\0'; - frr_elevate_privs(&bgpd_privs) { + frr_with_privs(&bgpd_privs) { ret = vrf_getaddrinfo(address, port_str, &req, &ainfo_save, bgp->vrf_id); } @@ -783,7 +879,7 @@ int bgp_socket(struct bgp *bgp, unsigned short port, const char *address) if (ainfo->ai_family != AF_INET && ainfo->ai_family != AF_INET6) continue; - frr_elevate_privs(&bgpd_privs) { + frr_with_privs(&bgpd_privs) { sock = vrf_socket(ainfo->ai_family, ainfo->ai_socktype, ainfo->ai_protocol, bgp->vrf_id, @@ -838,9 +934,10 @@ void bgp_close_vrf_socket(struct bgp *bgp) for (ALL_LIST_ELEMENTS(bm->listen_sockets, node, next, listener)) { if (listener->bgp == bgp) { - thread_cancel(listener->thread); + THREAD_OFF(listener->thread); close(listener->fd); listnode_delete(bm->listen_sockets, listener); + XFREE(MTYPE_BGP_LISTENER, listener->name); XFREE(MTYPE_BGP_LISTENER, listener); } } @@ -859,9 +956,10 @@ void bgp_close(void) for (ALL_LIST_ELEMENTS(bm->listen_sockets, node, next, listener)) { if (listener->bgp) continue; - thread_cancel(listener->thread); + THREAD_OFF(listener->thread); close(listener->fd); listnode_delete(bm->listen_sockets, listener); + XFREE(MTYPE_BGP_LISTENER, listener->name); XFREE(MTYPE_BGP_LISTENER, listener); } } diff --git a/bgpd/bgp_network.h b/bgpd/bgp_network.h index 59b18f9376..0b5cc17523 100644 --- a/bgpd/bgp_network.h +++ b/bgpd/bgp_network.h @@ -23,6 +23,7 @@ #define BGP_SOCKET_SNDBUF_SIZE 65536 +extern void bgp_dump_listener_info(struct vty *vty); extern int bgp_socket(struct bgp *bgp, unsigned short port, const char *address); extern void bgp_close_vrf_socket(struct bgp *bgp); @@ -30,8 +31,9 @@ extern void bgp_close(void); extern int bgp_connect(struct peer *); extern int bgp_getsockname(struct peer *); -extern int bgp_md5_set_prefix(struct prefix *p, const char *password); -extern int bgp_md5_unset_prefix(struct prefix *p); +extern int bgp_md5_set_prefix(struct bgp *bgp, struct prefix *p, + const char *password); +extern int bgp_md5_unset_prefix(struct bgp *bgp, struct prefix *p); extern int bgp_md5_set(struct peer *); extern int bgp_md5_unset(struct peer *); extern int bgp_set_socket_ttl(struct peer *, int fd); diff --git a/bgpd/bgp_nexthop.c b/bgpd/bgp_nexthop.c index de97b73c72..0d8214e4d6 100644 --- a/bgpd/bgp_nexthop.c +++ b/bgpd/bgp_nexthop.c @@ -35,7 +35,6 @@ #include "filter.h" #include "bgpd/bgpd.h" -#include "bgpd/bgp_table.h" #include "bgpd/bgp_route.h" #include "bgpd/bgp_attr.h" #include "bgpd/bgp_nexthop.h" @@ -44,13 +43,24 @@ #include "bgpd/bgp_damp.h" #include "bgpd/bgp_fsm.h" #include "bgpd/bgp_vty.h" +#include "bgpd/bgp_rd.h" DEFINE_MTYPE_STATIC(BGPD, MARTIAN_STRING, "BGP Martian Address Intf String"); -char *bnc_str(struct bgp_nexthop_cache *bnc, char *buf, int size) +int bgp_nexthop_cache_compare(const struct bgp_nexthop_cache *a, + const struct bgp_nexthop_cache *b) { - prefix2str(&(bnc->node->p), buf, size); - return buf; + if (a->srte_color < b->srte_color) + return -1; + if (a->srte_color > b->srte_color) + return 1; + + return prefix_cmp(&a->prefix, &b->prefix); +} + +const char *bnc_str(struct bgp_nexthop_cache *bnc, char *buf, int size) +{ + return prefix2str(&bnc->prefix, buf, size); } void bnc_nexthop_free(struct bgp_nexthop_cache *bnc) @@ -58,32 +68,62 @@ void bnc_nexthop_free(struct bgp_nexthop_cache *bnc) nexthops_free(bnc->nexthop); } -struct bgp_nexthop_cache *bnc_new(void) +struct bgp_nexthop_cache *bnc_new(struct bgp_nexthop_cache_head *tree, + struct prefix *prefix, uint32_t srte_color) { struct bgp_nexthop_cache *bnc; bnc = XCALLOC(MTYPE_BGP_NEXTHOP_CACHE, sizeof(struct bgp_nexthop_cache)); + bnc->prefix = *prefix; + bnc->srte_color = srte_color; + bnc->tree = tree; LIST_INIT(&(bnc->paths)); + bgp_nexthop_cache_add(tree, bnc); + return bnc; } +bool bnc_existing_for_prefix(struct bgp_nexthop_cache *bnc) +{ + struct bgp_nexthop_cache *bnc_tmp; + + frr_each (bgp_nexthop_cache, bnc->tree, bnc_tmp) { + if (bnc_tmp == bnc) + continue; + if (prefix_cmp(&bnc->prefix, &bnc_tmp->prefix) == 0) + return true; + } + return false; +} + void bnc_free(struct bgp_nexthop_cache *bnc) { bnc_nexthop_free(bnc); + bgp_nexthop_cache_del(bnc->tree, bnc); XFREE(MTYPE_BGP_NEXTHOP_CACHE, bnc); } +struct bgp_nexthop_cache *bnc_find(struct bgp_nexthop_cache_head *tree, + struct prefix *prefix, uint32_t srte_color) +{ + struct bgp_nexthop_cache bnc = {}; + + if (!tree) + return NULL; + + bnc.prefix = *prefix; + bnc.srte_color = srte_color; + return bgp_nexthop_cache_find(tree, &bnc); +} + /* Reset and free all BGP nexthop cache. */ -static void bgp_nexthop_cache_reset(struct bgp_table *table) +static void bgp_nexthop_cache_reset(struct bgp_nexthop_cache_head *tree) { - struct bgp_node *rn; struct bgp_nexthop_cache *bnc; - for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) { - bnc = bgp_node_get_bgp_nexthop_info(rn); - if (!bnc) - continue; + while (bgp_nexthop_cache_count(tree) > 0) { + bnc = bgp_nexthop_cache_first(tree); while (!LIST_EMPTY(&(bnc->paths))) { struct bgp_path_info *path = LIST_FIRST(&(bnc->paths)); @@ -92,8 +132,6 @@ static void bgp_nexthop_cache_reset(struct bgp_table *table) } bnc_free(bnc); - bgp_node_set_bgp_nexthop_info(rn, NULL); - bgp_unlock_node(rn); } } @@ -114,7 +152,7 @@ static void bgp_tip_hash_free(void *addr) XFREE(MTYPE_TIP_ADDR, addr); } -static unsigned int bgp_tip_hash_key_make(void *p) +static unsigned int bgp_tip_hash_key_make(const void *p) { const struct tip_addr *addr = p; @@ -180,7 +218,7 @@ void bgp_tip_del(struct bgp *bgp, struct in_addr *tip) /* BGP own address structure */ struct bgp_addr { - struct in_addr addr; + struct prefix p; struct list *ifp_name_list; }; @@ -190,8 +228,11 @@ static void show_address_entry(struct hash_bucket *bucket, void *args) struct bgp_addr *addr = (struct bgp_addr *)bucket->data; char *name; struct listnode *node; + char str[INET6_ADDRSTRLEN] = {0}; - vty_out(vty, "addr: %s, count: %d : ", inet_ntoa(addr->addr), + vty_out(vty, "addr: %s, count: %d : ", + inet_ntop(addr->p.family, &(addr->p.u.prefix), + str, INET6_ADDRSTRLEN), addr->ifp_name_list->count); for (ALL_LIST_ELEMENTS_RO(addr->ifp_name_list, node, name)) { @@ -217,11 +258,11 @@ static void bgp_address_hash_string_del(void *val) static void *bgp_address_hash_alloc(void *p) { - const struct in_addr *val = (const struct in_addr *)p; - struct bgp_addr *addr; + struct bgp_addr *copy_addr = p; + struct bgp_addr *addr = NULL; addr = XMALLOC(MTYPE_BGP_ADDR, sizeof(struct bgp_addr)); - addr->addr.s_addr = val->s_addr; + prefix_copy(&addr->p, ©_addr->p); addr->ifp_name_list = list_new(); addr->ifp_name_list->del = bgp_address_hash_string_del; @@ -237,11 +278,11 @@ static void bgp_address_hash_free(void *data) XFREE(MTYPE_BGP_ADDR, addr); } -static unsigned int bgp_address_hash_key_make(void *p) +static unsigned int bgp_address_hash_key_make(const void *p) { const struct bgp_addr *addr = p; - return jhash_1word(addr->addr.s_addr, 0); + return prefix_hash_key(&addr->p); } static bool bgp_address_hash_cmp(const void *p1, const void *p2) @@ -249,14 +290,14 @@ static bool bgp_address_hash_cmp(const void *p1, const void *p2) const struct bgp_addr *addr1 = p1; const struct bgp_addr *addr2 = p2; - return addr1->addr.s_addr == addr2->addr.s_addr; + return prefix_same(&addr1->p, &addr2->p); } void bgp_address_init(struct bgp *bgp) { bgp->address_hash = hash_create(bgp_address_hash_key_make, bgp_address_hash_cmp, - "BGP Address Hash"); + "BGP Connected Address Hash"); } void bgp_address_destroy(struct bgp *bgp) @@ -276,7 +317,12 @@ static void bgp_address_add(struct bgp *bgp, struct connected *ifc, struct listnode *node; char *name; - tmp.addr = p->u.prefix4; + tmp.p = *p; + + if (tmp.p.family == AF_INET) + tmp.p.prefixlen = IPV4_MAX_BITLEN; + else if (tmp.p.family == AF_INET6) + tmp.p.prefixlen = IPV6_MAX_BITLEN; addr = hash_get(bgp->address_hash, &tmp, bgp_address_hash_alloc); @@ -298,7 +344,12 @@ static void bgp_address_del(struct bgp *bgp, struct connected *ifc, struct listnode *node; char *name; - tmp.addr = p->u.prefix4; + tmp.p = *p; + + if (tmp.p.family == AF_INET) + tmp.p.prefixlen = IPV4_MAX_BITLEN; + else if (tmp.p.family == AF_INET6) + tmp.p.prefixlen = IPV6_MAX_BITLEN; addr = hash_lookup(bgp->address_hash, &tmp); /* may have been deleted earlier by bgp_interface_down() */ @@ -331,7 +382,7 @@ void bgp_connected_add(struct bgp *bgp, struct connected *ifc) { struct prefix p; struct prefix *addr; - struct bgp_node *rn; + struct bgp_dest *dest; struct bgp_connected_ref *bc; struct listnode *node, *nnode; struct peer *peer; @@ -347,16 +398,16 @@ void bgp_connected_add(struct bgp *bgp, struct connected *ifc) bgp_address_add(bgp, ifc, addr); - rn = bgp_node_get(bgp->connected_table[AFI_IP], - (struct prefix *)&p); - bc = bgp_node_get_bgp_connected_ref_info(rn); + dest = bgp_node_get(bgp->connected_table[AFI_IP], + (struct prefix *)&p); + bc = bgp_dest_get_bgp_connected_ref_info(dest); if (bc) bc->refcnt++; else { bc = XCALLOC(MTYPE_BGP_CONN, sizeof(struct bgp_connected_ref)); bc->refcnt = 1; - bgp_node_set_bgp_connected_ref_info(rn, bc); + bgp_dest_set_bgp_connected_ref_info(dest, bc); } for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { @@ -379,17 +430,19 @@ void bgp_connected_add(struct bgp *bgp, struct connected *ifc) if (IN6_IS_ADDR_LINKLOCAL(&p.u.prefix6)) return; - rn = bgp_node_get(bgp->connected_table[AFI_IP6], - (struct prefix *)&p); + bgp_address_add(bgp, ifc, addr); + + dest = bgp_node_get(bgp->connected_table[AFI_IP6], + (struct prefix *)&p); - bc = bgp_node_get_bgp_connected_ref_info(rn); + bc = bgp_dest_get_bgp_connected_ref_info(dest); if (bc) bc->refcnt++; else { bc = XCALLOC(MTYPE_BGP_CONN, sizeof(struct bgp_connected_ref)); bc->refcnt = 1; - bgp_node_set_bgp_connected_ref_info(rn, bc); + bgp_dest_set_bgp_connected_ref_info(dest, bc); } } } @@ -398,7 +451,7 @@ void bgp_connected_delete(struct bgp *bgp, struct connected *ifc) { struct prefix p; struct prefix *addr; - struct bgp_node *rn = NULL; + struct bgp_dest *dest = NULL; struct bgp_connected_ref *bc; addr = ifc->address; @@ -411,7 +464,7 @@ void bgp_connected_delete(struct bgp *bgp, struct connected *ifc) bgp_address_del(bgp, ifc, addr); - rn = bgp_node_lookup(bgp->connected_table[AFI_IP], &p); + dest = bgp_node_lookup(bgp->connected_table[AFI_IP], &p); } else if (addr->family == AF_INET6) { if (IN6_IS_ADDR_UNSPECIFIED(&p.u.prefix6)) return; @@ -419,63 +472,121 @@ void bgp_connected_delete(struct bgp *bgp, struct connected *ifc) if (IN6_IS_ADDR_LINKLOCAL(&p.u.prefix6)) return; - rn = bgp_node_lookup(bgp->connected_table[AFI_IP6], - (struct prefix *)&p); + bgp_address_del(bgp, ifc, addr); + + dest = bgp_node_lookup(bgp->connected_table[AFI_IP6], &p); } - if (!rn) + if (!dest) return; - bc = bgp_node_get_bgp_connected_ref_info(rn); + bc = bgp_dest_get_bgp_connected_ref_info(dest); bc->refcnt--; if (bc->refcnt == 0) { XFREE(MTYPE_BGP_CONN, bc); - bgp_node_set_bgp_connected_ref_info(rn, NULL); + bgp_dest_set_bgp_connected_ref_info(dest, NULL); } - bgp_unlock_node(rn); - bgp_unlock_node(rn); + bgp_dest_unlock_node(dest); + bgp_dest_unlock_node(dest); } static void bgp_connected_cleanup(struct route_table *table, struct route_node *rn) { struct bgp_connected_ref *bc; - struct bgp_node *bn = bgp_node_from_rnode(rn); + struct bgp_dest *bn = bgp_dest_from_rnode(rn); - bc = bgp_node_get_bgp_connected_ref_info(bn); + bc = bgp_dest_get_bgp_connected_ref_info(bn); if (!bc) return; bc->refcnt--; if (bc->refcnt == 0) { XFREE(MTYPE_BGP_CONN, bc); - bgp_node_set_bgp_connected_ref_info(bn, NULL); + bgp_dest_set_bgp_connected_ref_info(bn, NULL); } } -int bgp_nexthop_self(struct bgp *bgp, struct in_addr nh_addr) -{ - struct bgp_addr tmp, *addr; - struct tip_addr tmp_tip, *tip; - - tmp.addr = nh_addr; +bool bgp_nexthop_self(struct bgp *bgp, afi_t afi, uint8_t type, + uint8_t sub_type, struct attr *attr, + struct bgp_dest *dest) +{ + uint8_t new_afi = afi == AFI_IP ? AF_INET : AF_INET6; + struct bgp_addr tmp_addr = {{0}}, *addr = NULL; + struct tip_addr tmp_tip, *tip = NULL; + const struct prefix *p = bgp_dest_get_prefix(dest); + bool is_bgp_static_route = + ((type == ZEBRA_ROUTE_BGP) && (sub_type == BGP_ROUTE_STATIC)) + ? true + : false; + + if (!is_bgp_static_route) + new_afi = BGP_ATTR_NEXTHOP_AFI_IP6(attr) ? AF_INET6 : AF_INET; + + tmp_addr.p.family = new_afi; + switch (new_afi) { + case AF_INET: + if (is_bgp_static_route) { + tmp_addr.p.u.prefix4 = p->u.prefix4; + tmp_addr.p.prefixlen = p->prefixlen; + } else { + /* Here we need to find out which nexthop to be used*/ + if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP)) { + tmp_addr.p.u.prefix4 = attr->nexthop; + tmp_addr.p.prefixlen = IPV4_MAX_BITLEN; + } else if ((attr->mp_nexthop_len) + && ((attr->mp_nexthop_len + == BGP_ATTR_NHLEN_IPV4) + || (attr->mp_nexthop_len + == BGP_ATTR_NHLEN_VPNV4))) { + tmp_addr.p.u.prefix4 = + attr->mp_nexthop_global_in; + tmp_addr.p.prefixlen = IPV4_MAX_BITLEN; + } else + return false; + } + break; + case AF_INET6: + if (is_bgp_static_route) { + tmp_addr.p.u.prefix6 = p->u.prefix6; + tmp_addr.p.prefixlen = p->prefixlen; + } else { + tmp_addr.p.u.prefix6 = attr->mp_nexthop_global; + tmp_addr.p.prefixlen = IPV6_MAX_BITLEN; + } + break; + default: + break; + } - addr = hash_lookup(bgp->address_hash, &tmp); + addr = hash_lookup(bgp->address_hash, &tmp_addr); if (addr) - return 1; + return true; + + if (new_afi == AF_INET && hashcount(bgp->tip_hash)) { + memset(&tmp_tip, 0, sizeof(struct tip_addr)); + tmp_tip.addr = attr->nexthop; + + if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP)) { + tmp_tip.addr = attr->nexthop; + } else if ((attr->mp_nexthop_len) && + ((attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV4) + || (attr->mp_nexthop_len == BGP_ATTR_NHLEN_VPNV4))) { + tmp_tip.addr = attr->mp_nexthop_global_in; + } - tmp_tip.addr = nh_addr; - tip = hash_lookup(bgp->tip_hash, &tmp_tip); - if (tip) - return 1; + tip = hash_lookup(bgp->tip_hash, &tmp_tip); + if (tip) + return true; + } - return 0; + return false; } -int bgp_multiaccess_check_v4(struct in_addr nexthop, struct peer *peer) +bool bgp_multiaccess_check_v4(struct in_addr nexthop, struct peer *peer) { - struct bgp_node *rn1; - struct bgp_node *rn2; + struct bgp_dest *dest1; + struct bgp_dest *dest2; struct prefix p; int ret; @@ -483,32 +594,108 @@ int bgp_multiaccess_check_v4(struct in_addr nexthop, struct peer *peer) p.prefixlen = IPV4_MAX_BITLEN; p.u.prefix4 = nexthop; - rn1 = bgp_node_match(peer->bgp->connected_table[AFI_IP], &p); - if (!rn1) - return 0; + dest1 = bgp_node_match(peer->bgp->connected_table[AFI_IP], &p); + if (!dest1) + return false; p.family = AF_INET; p.prefixlen = IPV4_MAX_BITLEN; p.u.prefix4 = peer->su.sin.sin_addr; - rn2 = bgp_node_match(peer->bgp->connected_table[AFI_IP], &p); - if (!rn2) { - bgp_unlock_node(rn1); - return 0; + dest2 = bgp_node_match(peer->bgp->connected_table[AFI_IP], &p); + if (!dest2) { + bgp_dest_unlock_node(dest1); + return false; + } + + ret = (dest1 == dest2); + + bgp_dest_unlock_node(dest1); + bgp_dest_unlock_node(dest2); + + return ret; +} + +bool bgp_multiaccess_check_v6(struct in6_addr nexthop, struct peer *peer) +{ + struct bgp_dest *dest1; + struct bgp_dest *dest2; + struct prefix p; + int ret; + + p.family = AF_INET6; + p.prefixlen = IPV6_MAX_BITLEN; + p.u.prefix6 = nexthop; + + dest1 = bgp_node_match(peer->bgp->connected_table[AFI_IP6], &p); + if (!dest1) + return false; + + p.family = AF_INET6; + p.prefixlen = IPV6_MAX_BITLEN; + p.u.prefix6 = peer->su.sin6.sin6_addr; + + dest2 = bgp_node_match(peer->bgp->connected_table[AFI_IP6], &p); + if (!dest2) { + bgp_dest_unlock_node(dest1); + return false; } - ret = (rn1 == rn2) ? 1 : 0; + ret = (dest1 == dest2); + + bgp_dest_unlock_node(dest1); + bgp_dest_unlock_node(dest2); + + return ret; +} + +bool bgp_subgrp_multiaccess_check_v6(struct in6_addr nexthop, + struct update_subgroup *subgrp, + struct peer *exclude) +{ + struct bgp_dest *dest1 = NULL, *dest2 = NULL; + struct peer_af *paf = NULL; + struct prefix p = {0}, np = {0}; + struct bgp *bgp = NULL; - bgp_unlock_node(rn1); - bgp_unlock_node(rn2); + np.family = AF_INET6; + np.prefixlen = IPV6_MAX_BITLEN; + np.u.prefix6 = nexthop; + + p.family = AF_INET; + p.prefixlen = IPV6_MAX_BITLEN; + + bgp = SUBGRP_INST(subgrp); + dest1 = bgp_node_match(bgp->connected_table[AFI_IP6], &np); + if (!dest1) + return false; + + SUBGRP_FOREACH_PEER (subgrp, paf) { + /* Skip peer we're told to exclude - e.g., source of route. */ + if (paf->peer == exclude) + continue; + + p.u.prefix6 = paf->peer->su.sin6.sin6_addr; + dest2 = bgp_node_match(bgp->connected_table[AFI_IP6], &p); + if (dest1 == dest2) { + bgp_dest_unlock_node(dest1); + bgp_dest_unlock_node(dest2); + return true; + } + + if (dest2) + bgp_dest_unlock_node(dest2); + } - return (ret); + bgp_dest_unlock_node(dest1); + return false; } -int bgp_subgrp_multiaccess_check_v4(struct in_addr nexthop, - struct update_subgroup *subgrp) +bool bgp_subgrp_multiaccess_check_v4(struct in_addr nexthop, + struct update_subgroup *subgrp, + struct peer *exclude) { - struct bgp_node *rn1, *rn2; + struct bgp_dest *dest1, *dest2; struct peer_af *paf; struct prefix p, np; struct bgp *bgp; @@ -521,26 +708,61 @@ int bgp_subgrp_multiaccess_check_v4(struct in_addr nexthop, p.prefixlen = IPV4_MAX_BITLEN; bgp = SUBGRP_INST(subgrp); - rn1 = bgp_node_match(bgp->connected_table[AFI_IP], &np); - if (!rn1) - return 0; + dest1 = bgp_node_match(bgp->connected_table[AFI_IP], &np); + if (!dest1) + return false; SUBGRP_FOREACH_PEER (subgrp, paf) { + /* Skip peer we're told to exclude - e.g., source of route. */ + if (paf->peer == exclude) + continue; + p.u.prefix4 = paf->peer->su.sin.sin_addr; - rn2 = bgp_node_match(bgp->connected_table[AFI_IP], &p); - if (rn1 == rn2) { - bgp_unlock_node(rn1); - bgp_unlock_node(rn2); - return 1; + dest2 = bgp_node_match(bgp->connected_table[AFI_IP], &p); + if (dest1 == dest2) { + bgp_dest_unlock_node(dest1); + bgp_dest_unlock_node(dest2); + return true; } - if (rn2) - bgp_unlock_node(rn2); + if (dest2) + bgp_dest_unlock_node(dest2); } - bgp_unlock_node(rn1); - return 0; + bgp_dest_unlock_node(dest1); + return false; +} + +static void bgp_show_nexthop_paths(struct vty *vty, struct bgp *bgp, + struct bgp_nexthop_cache *bnc) +{ + struct bgp_dest *dest; + struct bgp_path_info *path; + int afi; + safi_t safi; + struct bgp_table *table; + struct bgp *bgp_path; + char buf1[BUFSIZ]; + + vty_out(vty, " Paths:\n"); + LIST_FOREACH (path, &(bnc->paths), nh_thread) { + dest = path->net; + assert(dest && bgp_dest_table(dest)); + afi = family2afi(bgp_dest_get_prefix(dest)->family); + table = bgp_dest_table(dest); + safi = table->safi; + bgp_path = table->bgp; + + if (dest->pdest) { + prefix_rd2str((struct prefix_rd *)bgp_dest_get_prefix(dest->pdest), + buf1, sizeof(buf1)); + vty_out(vty, " %d/%d %pRN RD %s %s flags 0x%x\n", + afi, safi, dest, buf1, bgp_path->name_pretty, path->flags); + } else + vty_out(vty, " %d/%d %pRN %s flags 0x%x\n", + afi, safi, dest, bgp_path->name_pretty, path->flags); + } } static void bgp_show_nexthops_detail(struct vty *vty, struct bgp *bgp, @@ -549,7 +771,7 @@ static void bgp_show_nexthops_detail(struct vty *vty, struct bgp *bgp, char buf[PREFIX2STR_BUFFER]; struct nexthop *nexthop; - for (nexthop = bnc->nexthop; nexthop; nexthop = nexthop->next) + for (nexthop = bnc->nexthop; nexthop; nexthop = nexthop->next) { switch (nexthop->type) { case NEXTHOP_TYPE_IPV6: vty_out(vty, " gate %s\n", @@ -584,69 +806,76 @@ static void bgp_show_nexthops_detail(struct vty *vty, struct bgp *bgp, vty_out(vty, " invalid nexthop type %u\n", nexthop->type); } + } } -static void bgp_show_nexthops(struct vty *vty, struct bgp *bgp, int detail, - bool import_table) +static void bgp_show_nexthop(struct vty *vty, struct bgp *bgp, + struct bgp_nexthop_cache *bnc, + bool specific) { - struct bgp_node *rn; - struct bgp_nexthop_cache *bnc; char buf[PREFIX2STR_BUFFER]; time_t tbuf; + struct peer *peer; + + peer = (struct peer *)bnc->nht_info; + + if (bnc->srte_color) + vty_out(vty, " SR-TE color %u -", bnc->srte_color); + if (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_VALID)) { + vty_out(vty, " %s valid [IGP metric %d], #paths %d", + inet_ntop(bnc->prefix.family, &bnc->prefix.u.prefix, + buf, sizeof(buf)), + bnc->metric, bnc->path_count); + if (peer) + vty_out(vty, ", peer %s", peer->host); + vty_out(vty, "\n"); + bgp_show_nexthops_detail(vty, bgp, bnc); + } else { + vty_out(vty, " %s invalid, #paths %d", + inet_ntop(bnc->prefix.family, &bnc->prefix.u.prefix, + buf, sizeof(buf)), + bnc->path_count); + if (peer) + vty_out(vty, ", peer %s", peer->host); + vty_out(vty, "\n"); + if (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_CONNECTED)) + vty_out(vty, " Must be Connected\n"); + if (!CHECK_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED)) + vty_out(vty, " Is not Registered\n"); + } + tbuf = time(NULL) - (bgp_clock() - bnc->last_update); + vty_out(vty, " Last update: %s", ctime(&tbuf)); + vty_out(vty, "\n"); + + /* show paths dependent on nexthop, if needed. */ + if (specific) + bgp_show_nexthop_paths(vty, bgp, bnc); +} + +static void bgp_show_nexthops(struct vty *vty, struct bgp *bgp, + bool import_table) +{ + struct bgp_nexthop_cache *bnc; afi_t afi; - struct bgp_table **table; + struct bgp_nexthop_cache_head(*tree)[AFI_MAX]; if (import_table) vty_out(vty, "Current BGP import check cache:\n"); else vty_out(vty, "Current BGP nexthop cache:\n"); if (import_table) - table = bgp->import_check_table; + tree = &bgp->import_check_table; else - table = bgp->nexthop_cache_table; + tree = &bgp->nexthop_cache_table; for (afi = AFI_IP; afi < AFI_MAX; afi++) { - if (!table || !table[afi]) - continue; - for (rn = bgp_table_top(table[afi]); rn; - rn = bgp_route_next(rn)) { - bnc = bgp_node_get_bgp_nexthop_info(rn); - if (!bnc) - continue; - - if (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_VALID)) { - vty_out(vty, - " %s valid [IGP metric %d], #paths %d\n", - inet_ntop(rn->p.family, - &rn->p.u.prefix, buf, - sizeof(buf)), - bnc->metric, bnc->path_count); - - if (!detail) - continue; - - bgp_show_nexthops_detail(vty, bgp, bnc); - - } else { - vty_out(vty, " %s invalid\n", - inet_ntop(rn->p.family, - &rn->p.u.prefix, buf, - sizeof(buf))); - if (CHECK_FLAG(bnc->flags, - BGP_NEXTHOP_CONNECTED)) - vty_out(vty, " Must be Connected\n"); - if (!CHECK_FLAG(bnc->flags, - BGP_NEXTHOP_REGISTERED)) - vty_out(vty, " Is not Registered\n"); - } - tbuf = time(NULL) - (bgp_clock() - bnc->last_update); - vty_out(vty, " Last update: %s", ctime(&tbuf)); - vty_out(vty, "\n"); - } + frr_each (bgp_nexthop_cache, &(*tree)[afi], bnc) + bgp_show_nexthop(vty, bgp, bnc, false); } } static int show_ip_bgp_nexthop_table(struct vty *vty, const char *name, - int detail, bool import_table) + const char *nhopip_str, + bool import_table) { struct bgp *bgp; @@ -659,7 +888,25 @@ static int show_ip_bgp_nexthop_table(struct vty *vty, const char *name, return CMD_WARNING; } - bgp_show_nexthops(vty, bgp, detail, import_table); + if (nhopip_str) { + struct prefix nhop; + struct bgp_nexthop_cache_head (*tree)[AFI_MAX]; + struct bgp_nexthop_cache *bnc; + + if (!str2prefix(nhopip_str, &nhop)) { + vty_out(vty, "nexthop address is malformed\n"); + return CMD_WARNING; + } + tree = import_table ? &bgp->import_check_table + : &bgp->nexthop_cache_table; + bnc = bnc_find(tree[family2afi(nhop.family)], &nhop, 0); + if (!bnc) { + vty_out(vty, "specified nexthop does not have entry\n"); + return CMD_SUCCESS; + } + bgp_show_nexthop(vty, bgp, bnc, true); + } else + bgp_show_nexthops(vty, bgp, import_table); return CMD_SUCCESS; } @@ -674,29 +921,36 @@ static void bgp_show_all_instances_nexthops_vty(struct vty *vty) (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT) ? VRF_DEFAULT_NAME : bgp->name); - bgp_show_nexthops(vty, bgp, 0, false); + bgp_show_nexthops(vty, bgp, false); } } DEFUN (show_ip_bgp_nexthop, show_ip_bgp_nexthop_cmd, - "show [ip] bgp [ VIEWVRFNAME] nexthop [detail]", + "show [ip] bgp [ VIEWVRFNAME] nexthop [] [detail]", SHOW_STR IP_STR BGP_STR BGP_INSTANCE_HELP_STR "BGP nexthop table\n" + "IPv4 nexthop address\n" + "IPv6 nexthop address\n" "Show detailed information\n") { int idx = 0; + int nh_idx = 0; char *vrf = NULL; + char *nhop_ip = NULL; if (argv_find(argv, argc, "view", &idx) || argv_find(argv, argc, "vrf", &idx)) vrf = argv[++idx]->arg; - int detail = argv_find(argv, argc, "detail", &idx) ? 1 : 0; - return show_ip_bgp_nexthop_table(vty, vrf, detail, false); + if (argv_find(argv, argc, "A.B.C.D", &nh_idx) + || argv_find(argv, argc, "X:X::X:X", &nh_idx)) + nhop_ip = argv[nh_idx]->arg; + + return show_ip_bgp_nexthop_table(vty, vrf, nhop_ip, false); } DEFUN (show_ip_bgp_import_check, @@ -715,8 +969,8 @@ DEFUN (show_ip_bgp_import_check, if (argv_find(argv, argc, "view", &idx) || argv_find(argv, argc, "vrf", &idx)) vrf = argv[++idx]->arg; - int detail = argv_find(argv, argc, "detail", &idx) ? 1 : 0; - return show_ip_bgp_nexthop_table(vty, vrf, detail, true); + + return show_ip_bgp_nexthop_table(vty, vrf, NULL, true); } DEFUN (show_ip_bgp_instance_all_nexthop, @@ -737,12 +991,10 @@ void bgp_scan_init(struct bgp *bgp) afi_t afi; for (afi = AFI_IP; afi < AFI_MAX; afi++) { - bgp->nexthop_cache_table[afi] = - bgp_table_init(bgp, afi, SAFI_UNICAST); + bgp_nexthop_cache_init(&bgp->nexthop_cache_table[afi]); + bgp_nexthop_cache_init(&bgp->import_check_table[afi]); bgp->connected_table[afi] = bgp_table_init(bgp, afi, SAFI_UNICAST); - bgp->import_check_table[afi] = - bgp_table_init(bgp, afi, SAFI_UNICAST); } } @@ -759,16 +1011,12 @@ void bgp_scan_finish(struct bgp *bgp) for (afi = AFI_IP; afi < AFI_MAX; afi++) { /* Only the current one needs to be reset. */ - bgp_nexthop_cache_reset(bgp->nexthop_cache_table[afi]); - bgp_table_unlock(bgp->nexthop_cache_table[afi]); - bgp->nexthop_cache_table[afi] = NULL; + bgp_nexthop_cache_reset(&bgp->nexthop_cache_table[afi]); + bgp_nexthop_cache_reset(&bgp->import_check_table[afi]); bgp->connected_table[afi]->route_table->cleanup = bgp_connected_cleanup; bgp_table_unlock(bgp->connected_table[afi]); bgp->connected_table[afi] = NULL; - - bgp_table_unlock(bgp->import_check_table[afi]); - bgp->import_check_table[afi] = NULL; } } diff --git a/bgpd/bgp_nexthop.h b/bgpd/bgp_nexthop.h index f06fae5706..c4b913faf4 100644 --- a/bgpd/bgp_nexthop.h +++ b/bgpd/bgp_nexthop.h @@ -24,6 +24,7 @@ #include "if.h" #include "queue.h" #include "prefix.h" +#include "bgp_table.h" #define NEXTHOP_FAMILY(nexthop_len) \ (((nexthop_len) == 4 || (nexthop_len) == 12 \ @@ -36,8 +37,13 @@ #define BGP_MP_NEXTHOP_FAMILY NEXTHOP_FAMILY +PREDECL_RBTREE_UNIQ(bgp_nexthop_cache); + /* BGP nexthop cache value structure. */ struct bgp_nexthop_cache { + /* RB-tree entry. */ + struct bgp_nexthop_cache_item entry; + /* IGP route's metric. */ uint32_t metric; @@ -61,30 +67,64 @@ struct bgp_nexthop_cache { #define BGP_NEXTHOP_METRIC_CHANGED (1 << 1) #define BGP_NEXTHOP_CONNECTED_CHANGED (1 << 2) - struct bgp_node *node; + /* Back pointer to the cache tree this entry belongs to. */ + struct bgp_nexthop_cache_head *tree; + + uint32_t srte_color; + struct prefix prefix; void *nht_info; /* In BGP, peer session */ LIST_HEAD(path_list, bgp_path_info) paths; unsigned int path_count; struct bgp *bgp; }; +extern int bgp_nexthop_cache_compare(const struct bgp_nexthop_cache *a, + const struct bgp_nexthop_cache *b); +DECLARE_RBTREE_UNIQ(bgp_nexthop_cache, struct bgp_nexthop_cache, entry, + bgp_nexthop_cache_compare); + /* Own tunnel-ip address structure */ struct tip_addr { struct in_addr addr; int refcnt; }; +struct bgp_addrv6 { + struct in6_addr addrv6; + struct list *ifp_name_list; +}; + +/* Forward declaration(s). */ +struct peer; +struct update_subgroup; +struct bgp_dest; +struct attr; + extern void bgp_connected_add(struct bgp *bgp, struct connected *c); extern void bgp_connected_delete(struct bgp *bgp, struct connected *c); -extern int bgp_subgrp_multiaccess_check_v4(struct in_addr nexthop, - struct update_subgroup *subgrp); -extern int bgp_multiaccess_check_v4(struct in_addr, struct peer *); +extern bool bgp_subgrp_multiaccess_check_v4(struct in_addr nexthop, + struct update_subgroup *subgrp, + struct peer *exclude); +extern bool bgp_subgrp_multiaccess_check_v6(struct in6_addr nexthop, + struct update_subgroup *subgrp, + struct peer *exclude); +extern bool bgp_multiaccess_check_v4(struct in_addr nexthop, struct peer *peer); +extern bool bgp_multiaccess_check_v6(struct in6_addr nexthop, + struct peer *peer); extern int bgp_config_write_scan_time(struct vty *); -extern int bgp_nexthop_self(struct bgp *, struct in_addr); -extern struct bgp_nexthop_cache *bnc_new(void); +extern bool bgp_nexthop_self(struct bgp *bgp, afi_t afi, uint8_t type, + uint8_t sub_type, struct attr *attr, + struct bgp_dest *dest); +extern struct bgp_nexthop_cache *bnc_new(struct bgp_nexthop_cache_head *tree, + struct prefix *prefix, + uint32_t srte_color); +extern bool bnc_existing_for_prefix(struct bgp_nexthop_cache *bnc); extern void bnc_free(struct bgp_nexthop_cache *bnc); +extern struct bgp_nexthop_cache *bnc_find(struct bgp_nexthop_cache_head *tree, + struct prefix *prefix, + uint32_t srte_color); extern void bnc_nexthop_free(struct bgp_nexthop_cache *bnc); -extern char *bnc_str(struct bgp_nexthop_cache *bnc, char *buf, int size); +extern const char *bnc_str(struct bgp_nexthop_cache *bnc, char *buf, int size); extern void bgp_scan_init(struct bgp *bgp); extern void bgp_scan_finish(struct bgp *bgp); extern void bgp_scan_vty_init(void); diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c index 7e721db49d..2c70d01d46 100644 --- a/bgpd/bgp_nht.c +++ b/bgpd/bgp_nht.c @@ -43,6 +43,8 @@ #include "bgpd/bgp_fsm.h" #include "bgpd/bgp_zebra.h" #include "bgpd/bgp_flowspec_util.h" +#include "bgpd/bgp_evpn.h" +#include "bgpd/bgp_rd.h" extern struct zclient *zclient; @@ -65,40 +67,19 @@ static int bgp_isvalid_labeled_nexthop(struct bgp_nexthop_cache *bnc) || (bnc && CHECK_FLAG(bnc->flags, BGP_NEXTHOP_LABELED_VALID))); } -int bgp_find_nexthop(struct bgp_path_info *path, int connected) -{ - struct bgp_nexthop_cache *bnc = path->nexthop; - - if (!bnc) - return 0; - - /* - * We are cheating here. Views have no associated underlying - * ability to detect nexthops. So when we have a view - * just tell everyone the nexthop is valid - */ - if (path->peer && path->peer->bgp->inst_type == BGP_INSTANCE_TYPE_VIEW) - return 1; - - if (connected && !(CHECK_FLAG(bnc->flags, BGP_NEXTHOP_CONNECTED))) - return 0; - - return (bgp_isvalid_nexthop(bnc)); -} - static void bgp_unlink_nexthop_check(struct bgp_nexthop_cache *bnc) { if (LIST_EMPTY(&(bnc->paths)) && !bnc->nht_info) { if (BGP_DEBUG(nht, NHT)) { char buf[PREFIX2STR_BUFFER]; - zlog_debug("bgp_unlink_nexthop: freeing bnc %s", - bnc_str(bnc, buf, PREFIX2STR_BUFFER)); + zlog_debug("bgp_unlink_nexthop: freeing bnc %s(%u)(%s)", + bnc_str(bnc, buf, PREFIX2STR_BUFFER), + bnc->srte_color, bnc->bgp->name_pretty); } - unregister_zebra_rnh(bnc, - CHECK_FLAG(bnc->flags, BGP_STATIC_ROUTE)); - bgp_node_set_bgp_nexthop_info(bnc->node, NULL); - bgp_unlock_node(bnc->node); - bnc->node = NULL; + /* only unregister if this is the last nh for this prefix*/ + if (!bnc_existing_for_prefix(bnc)) + unregister_zebra_rnh( + bnc, CHECK_FLAG(bnc->flags, BGP_STATIC_ROUTE)); bnc_free(bnc); } } @@ -118,16 +99,13 @@ void bgp_unlink_nexthop(struct bgp_path_info *path) void bgp_unlink_nexthop_by_peer(struct peer *peer) { struct prefix p; - struct bgp_node *rn; struct bgp_nexthop_cache *bnc; afi_t afi = family2afi(peer->su.sa.sa_family); if (!sockunion2hostprefix(&peer->su, &p)) return; - rn = bgp_node_get(peer->bgp->nexthop_cache_table[afi], &p); - - bnc = bgp_node_get_bgp_nexthop_info(rn); + bnc = bnc_find(&peer->bgp->nexthop_cache_table[afi], &p, 0); if (!bnc) return; @@ -145,9 +123,10 @@ int bgp_find_or_add_nexthop(struct bgp *bgp_route, struct bgp *bgp_nexthop, afi_t afi, struct bgp_path_info *pi, struct peer *peer, int connected) { - struct bgp_node *rn; + struct bgp_nexthop_cache_head *tree = NULL; struct bgp_nexthop_cache *bnc; struct prefix p; + uint32_t srte_color = 0; int is_bgp_static_route = 0; if (pi) { @@ -163,16 +142,23 @@ int bgp_find_or_add_nexthop(struct bgp *bgp_route, struct bgp *bgp_nexthop, afi = BGP_ATTR_NEXTHOP_AFI_IP6(pi->attr) ? AFI_IP6 : AFI_IP; - /* This will return TRUE if the global IPv6 NH is a link local + /* Validation for the ipv4 mapped ipv6 nexthop. */ + if (IS_MAPPED_IPV6(&pi->attr->mp_nexthop_global)) { + afi = AFI_IP; + } + + /* This will return true if the global IPv6 NH is a link local * addr */ if (make_prefix(afi, pi, &p) < 0) return 1; + + srte_color = pi->attr->srte_color; } else if (peer) { if (!sockunion2hostprefix(&peer->su, &p)) { if (BGP_DEBUG(nht, NHT)) { zlog_debug( "%s: Attempting to register with unknown AFI %d (not %d or %d)", - __FUNCTION__, afi, AFI_IP, AFI_IP6); + __func__, afi, AFI_IP, AFI_IP6); } return 0; } @@ -180,36 +166,35 @@ int bgp_find_or_add_nexthop(struct bgp *bgp_route, struct bgp *bgp_nexthop, return 0; if (is_bgp_static_route) - rn = bgp_node_get(bgp_nexthop->import_check_table[afi], &p); + tree = &bgp_nexthop->import_check_table[afi]; else - rn = bgp_node_get(bgp_nexthop->nexthop_cache_table[afi], &p); + tree = &bgp_nexthop->nexthop_cache_table[afi]; - bnc = bgp_node_get_bgp_nexthop_info(rn); + bnc = bnc_find(tree, &p, srte_color); if (!bnc) { - bnc = bnc_new(); - bgp_node_set_bgp_nexthop_info(rn, bnc); - bnc->node = rn; + bnc = bnc_new(tree, &p, srte_color); bnc->bgp = bgp_nexthop; - bgp_lock_node(rn); if (BGP_DEBUG(nht, NHT)) { char buf[PREFIX2STR_BUFFER]; - zlog_debug("Allocated bnc %s peer %p", - bnc_str(bnc, buf, PREFIX2STR_BUFFER), peer); + zlog_debug("Allocated bnc %s(%u)(%s) peer %p", + bnc_str(bnc, buf, PREFIX2STR_BUFFER), + bnc->srte_color, bnc->bgp->name_pretty, + peer); } } - bgp_unlock_node(rn); if (is_bgp_static_route) { SET_FLAG(bnc->flags, BGP_STATIC_ROUTE); /* If we're toggling the type, re-register */ - if ((bgp_flag_check(bgp_route, BGP_FLAG_IMPORT_CHECK)) + if ((CHECK_FLAG(bgp_route->flags, BGP_FLAG_IMPORT_CHECK)) && !CHECK_FLAG(bnc->flags, BGP_STATIC_ROUTE_EXACT_MATCH)) { SET_FLAG(bnc->flags, BGP_STATIC_ROUTE_EXACT_MATCH); UNSET_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED); UNSET_FLAG(bnc->flags, BGP_NEXTHOP_VALID); - } else if ((!bgp_flag_check(bgp_route, BGP_FLAG_IMPORT_CHECK)) + } else if ((!CHECK_FLAG(bgp_route->flags, + BGP_FLAG_IMPORT_CHECK)) && CHECK_FLAG(bnc->flags, BGP_STATIC_ROUTE_EXACT_MATCH)) { UNSET_FLAG(bnc->flags, BGP_STATIC_ROUTE_EXACT_MATCH); @@ -243,8 +228,8 @@ int bgp_find_or_add_nexthop(struct bgp *bgp_route, struct bgp *bgp_nexthop, if (bgp_route->inst_type == BGP_INSTANCE_TYPE_VIEW) { SET_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED); SET_FLAG(bnc->flags, BGP_NEXTHOP_VALID); - } else if (!CHECK_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED) && - !is_default_host_route(&bnc->node->p)) + } else if (!CHECK_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED) + && !is_default_host_route(&bnc->prefix)) register_zebra_rnh(bnc, is_bgp_static_route); if (pi && pi->nexthop != bnc) { @@ -277,7 +262,6 @@ int bgp_find_or_add_nexthop(struct bgp *bgp_route, struct bgp *bgp_nexthop, void bgp_delete_connected_nexthop(afi_t afi, struct peer *peer) { - struct bgp_node *rn; struct bgp_nexthop_cache *bnc; struct prefix p; @@ -287,30 +271,22 @@ void bgp_delete_connected_nexthop(afi_t afi, struct peer *peer) if (!sockunion2hostprefix(&peer->su, &p)) return; - rn = bgp_node_lookup( - peer->bgp->nexthop_cache_table[family2afi(p.family)], &p); - if (!rn) { - if (BGP_DEBUG(nht, NHT)) - zlog_debug("Cannot find connected NHT node for peer %s", - peer->host); - return; - } - - bnc = bgp_node_get_bgp_nexthop_info(rn); + bnc = bnc_find(&peer->bgp->nexthop_cache_table[family2afi(p.family)], + &p, 0); if (!bnc) { if (BGP_DEBUG(nht, NHT)) - zlog_debug("Cannot find connected NHT node for peer %s on route_node as expected", - peer->host); - bgp_unlock_node(rn); + zlog_debug( + "Cannot find connected NHT node for peer %s(%s)", + peer->host, peer->bgp->name_pretty); return; } - bgp_unlock_node(rn); if (bnc->nht_info != peer) { if (BGP_DEBUG(nht, NHT)) zlog_debug( - "Connected NHT %p node for peer %s points to %p", - bnc, peer->host, bnc->nht_info); + "Connected NHT %p node for peer %s(%s) points to %p", + bnc, peer->host, bnc->bgp->name_pretty, + bnc->nht_info); return; } @@ -318,96 +294,44 @@ void bgp_delete_connected_nexthop(afi_t afi, struct peer *peer) if (LIST_EMPTY(&(bnc->paths))) { if (BGP_DEBUG(nht, NHT)) - zlog_debug("Freeing connected NHT node %p for peer %s", - bnc, peer->host); + zlog_debug( + "Freeing connected NHT node %p for peer %s(%s)", + bnc, peer->host, bnc->bgp->name_pretty); unregister_zebra_rnh(bnc, 0); - bgp_node_set_bgp_nexthop_info(bnc->node, NULL); - bgp_unlock_node(bnc->node); bnc_free(bnc); } } -void bgp_parse_nexthop_update(int command, vrf_id_t vrf_id) +static void bgp_process_nexthop_update(struct bgp_nexthop_cache *bnc, + struct zapi_route *nhr) { - struct bgp_node *rn = NULL; - struct bgp_nexthop_cache *bnc; struct nexthop *nexthop; struct nexthop *oldnh; struct nexthop *nhlist_head = NULL; struct nexthop *nhlist_tail = NULL; int i; - struct bgp *bgp; - struct zapi_route nhr; - - bgp = bgp_lookup_by_vrf_id(vrf_id); - if (!bgp) { - flog_err( - EC_BGP_NH_UPD, - "parse nexthop update: instance not found for vrf_id %u", - vrf_id); - return; - } - - if (!zapi_nexthop_update_decode(zclient->ibuf, &nhr)) { - if (BGP_DEBUG(nht, NHT)) - zlog_debug("%s: Failure to decode nexthop update", - __PRETTY_FUNCTION__); - return; - } - - if (command == ZEBRA_NEXTHOP_UPDATE) - rn = bgp_node_lookup( - bgp->nexthop_cache_table[family2afi(nhr.prefix.family)], - &nhr.prefix); - else if (command == ZEBRA_IMPORT_CHECK_UPDATE) - rn = bgp_node_lookup( - bgp->import_check_table[family2afi(nhr.prefix.family)], - &nhr.prefix); - if (!rn) { - if (BGP_DEBUG(nht, NHT)) { - char buf[PREFIX2STR_BUFFER]; - prefix2str(&nhr.prefix, buf, sizeof(buf)); - zlog_debug("parse nexthop update(%s): rn not found", - buf); - } - return; - } - - bnc = bgp_node_get_bgp_nexthop_info(rn); - if (!bnc) { - if (BGP_DEBUG(nht, NHT)) { - char buf[PREFIX2STR_BUFFER]; - - prefix2str(&nhr.prefix, buf, sizeof(buf)); - zlog_debug("parse nexthop update(%s): bnc node info not found", - buf); - } - bgp_unlock_node(rn); - return; - } - - bgp_unlock_node(rn); bnc->last_update = bgp_clock(); bnc->change_flags = 0; /* debug print the input */ if (BGP_DEBUG(nht, NHT)) { char buf[PREFIX2STR_BUFFER]; - prefix2str(&nhr.prefix, buf, sizeof(buf)); + prefix2str(&nhr->prefix, buf, sizeof(buf)); zlog_debug( - "%u: Rcvd NH update %s - metric %d/%d #nhops %d/%d flags 0x%x", - vrf_id, buf, nhr.metric, bnc->metric, nhr.nexthop_num, - bnc->nexthop_num, bnc->flags); + "%s(%u): Rcvd NH update %s(%u) - metric %d/%d #nhops %d/%d flags 0x%x", + bnc->bgp->name_pretty, bnc->bgp->vrf_id, buf, + bnc->srte_color, nhr->metric, bnc->metric, + nhr->nexthop_num, bnc->nexthop_num, bnc->flags); } - if (nhr.metric != bnc->metric) + if (nhr->metric != bnc->metric) bnc->change_flags |= BGP_NEXTHOP_METRIC_CHANGED; - if (nhr.nexthop_num != bnc->nexthop_num) + if (nhr->nexthop_num != bnc->nexthop_num) bnc->change_flags |= BGP_NEXTHOP_CHANGED; - if (nhr.nexthop_num) { + if (nhr->nexthop_num) { struct peer *peer = bnc->nht_info; /* notify bgp fsm if nbr ip goes from invalid->valid */ @@ -415,15 +339,15 @@ void bgp_parse_nexthop_update(int command, vrf_id_t vrf_id) UNSET_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED); bnc->flags |= BGP_NEXTHOP_VALID; - bnc->metric = nhr.metric; - bnc->nexthop_num = nhr.nexthop_num; + bnc->metric = nhr->metric; + bnc->nexthop_num = nhr->nexthop_num; bnc->flags &= ~BGP_NEXTHOP_LABELED_VALID; /* check below */ - for (i = 0; i < nhr.nexthop_num; i++) { + for (i = 0; i < nhr->nexthop_num; i++) { int num_labels = 0; - nexthop = nexthop_from_zapi_nexthop(&nhr.nexthops[i]); + nexthop = nexthop_from_zapi_nexthop(&nhr->nexthops[i]); /* * Turn on RA for the v6 nexthops @@ -433,14 +357,17 @@ void bgp_parse_nexthop_update(int command, vrf_id_t vrf_id) if (peer && !peer->ifp && CHECK_FLAG(peer->flags, PEER_FLAG_CAPABILITY_ENHE) - && nhr.prefix.family == AF_INET6) { + && nhr->prefix.family == AF_INET6 + && nexthop->type != NEXTHOP_TYPE_BLACKHOLE) { struct interface *ifp; ifp = if_lookup_by_index(nexthop->ifindex, nexthop->vrf_id); - zclient_send_interface_radv_req( - zclient, nexthop->vrf_id, ifp, true, - BGP_UNNUM_DEFAULT_RA_INTERVAL); + if (ifp) + zclient_send_interface_radv_req( + zclient, nexthop->vrf_id, ifp, + true, + BGP_UNNUM_DEFAULT_RA_INTERVAL); } /* There is at least one label-switched path */ if (nexthop->nh_label && @@ -474,8 +401,7 @@ void bgp_parse_nexthop_update(int command, vrf_id_t vrf_id) continue; for (oldnh = bnc->nexthop; oldnh; oldnh = oldnh->next) - if (nexthop_same_no_recurse(oldnh, nexthop) && - nexthop_labels_match(oldnh, nexthop)) + if (nexthop_same(oldnh, nexthop)) break; if (!oldnh) @@ -485,7 +411,7 @@ void bgp_parse_nexthop_update(int command, vrf_id_t vrf_id) bnc->nexthop = nhlist_head; } else { bnc->flags &= ~BGP_NEXTHOP_VALID; - bnc->nexthop_num = nhr.nexthop_num; + bnc->nexthop_num = nhr->nexthop_num; /* notify bgp fsm if nbr ip goes from valid->invalid */ UNSET_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED); @@ -497,26 +423,87 @@ void bgp_parse_nexthop_update(int command, vrf_id_t vrf_id) evaluate_paths(bnc); } +void bgp_parse_nexthop_update(int command, vrf_id_t vrf_id) +{ + struct bgp_nexthop_cache_head *tree = NULL; + struct bgp_nexthop_cache *bnc; + struct bgp *bgp; + struct zapi_route nhr; + afi_t afi; + + bgp = bgp_lookup_by_vrf_id(vrf_id); + if (!bgp) { + flog_err( + EC_BGP_NH_UPD, + "parse nexthop update: instance not found for vrf_id %u", + vrf_id); + return; + } + + if (!zapi_nexthop_update_decode(zclient->ibuf, &nhr)) { + zlog_err("%s[%s]: Failure to decode nexthop update", + __PRETTY_FUNCTION__, bgp->name_pretty); + return; + } + + afi = family2afi(nhr.prefix.family); + if (command == ZEBRA_NEXTHOP_UPDATE) + tree = &bgp->nexthop_cache_table[afi]; + else if (command == ZEBRA_IMPORT_CHECK_UPDATE) + tree = &bgp->import_check_table[afi]; + + bnc = bnc_find(tree, &nhr.prefix, nhr.srte_color); + if (!bnc) { + if (BGP_DEBUG(nht, NHT)) { + char buf[PREFIX2STR_BUFFER]; + + prefix2str(&nhr.prefix, buf, sizeof(buf)); + zlog_debug( + "parse nexthop update(%s(%u)(%s)): bnc info not found", + buf, nhr.srte_color, bgp->name_pretty); + } + return; + } + + bgp_process_nexthop_update(bnc, &nhr); + + /* + * HACK: if any BGP route is dependant on an SR-policy that doesn't + * exist, zebra will never send NH updates relative to that policy. In + * that case, whenever we receive an update about a colorless NH, update + * the corresponding colorful NHs that share the same endpoint but that + * are inactive. This ugly hack should work around the problem at the + * cost of a performance pernalty. Long term, what should be done is to + * make zebra's RNH subsystem aware of SR-TE colors (like bgpd is), + * which should provide a better infrastructure to solve this issue in + * a more efficient and elegant way. + */ + if (nhr.srte_color == 0) { + struct bgp_nexthop_cache *bnc_iter; + + frr_each (bgp_nexthop_cache, &bgp->nexthop_cache_table[afi], + bnc_iter) { + if (!prefix_same(&bnc->prefix, &bnc_iter->prefix) + || bnc_iter->srte_color == 0 + || CHECK_FLAG(bnc_iter->flags, BGP_NEXTHOP_VALID)) + continue; + + bgp_process_nexthop_update(bnc_iter, &nhr); + } + } +} + /* * Cleanup nexthop registration and status information for BGP nexthops * pertaining to this VRF. This is invoked upon VRF deletion. */ void bgp_cleanup_nexthops(struct bgp *bgp) { - afi_t afi; - struct bgp_node *rn; - struct bgp_nexthop_cache *bnc; - - for (afi = AFI_IP; afi < AFI_MAX; afi++) { - if (!bgp->nexthop_cache_table[afi]) - continue; - - for (rn = bgp_table_top(bgp->nexthop_cache_table[afi]); rn; - rn = bgp_route_next(rn)) { - bnc = bgp_node_get_bgp_nexthop_info(rn); - if (!bnc) - continue; + for (afi_t afi = AFI_IP; afi < AFI_MAX; afi++) { + struct bgp_nexthop_cache *bnc; + frr_each (bgp_nexthop_cache, &bgp->nexthop_cache_table[afi], + bnc) { /* Clear relevant flags. */ UNSET_FLAG(bnc->flags, BGP_NEXTHOP_VALID); UNSET_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED); @@ -536,35 +523,54 @@ static int make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p) && (pi->sub_type == BGP_ROUTE_STATIC)) ? 1 : 0; - struct bgp_node *net = pi->net; - struct prefix *p_orig = &net->p; + struct bgp_dest *net = pi->net; + const struct prefix *p_orig = bgp_dest_get_prefix(net); + struct in_addr ipv4; if (p_orig->family == AF_FLOWSPEC) { if (!pi->peer) return -1; return bgp_flowspec_get_first_nh(pi->peer->bgp, - pi, p); + pi, p, afi); } memset(p, 0, sizeof(struct prefix)); switch (afi) { case AFI_IP: p->family = AF_INET; if (is_bgp_static) { - p->u.prefix4 = pi->net->p.u.prefix4; - p->prefixlen = pi->net->p.prefixlen; + p->u.prefix4 = p_orig->u.prefix4; + p->prefixlen = p_orig->prefixlen; } else { - p->u.prefix4 = pi->attr->nexthop; - p->prefixlen = IPV4_MAX_BITLEN; + if (IS_MAPPED_IPV6(&pi->attr->mp_nexthop_global)) { + ipv4_mapped_ipv6_to_ipv4( + &pi->attr->mp_nexthop_global, &ipv4); + p->u.prefix4 = ipv4; + p->prefixlen = IPV4_MAX_BITLEN; + } else { + p->u.prefix4 = pi->attr->nexthop; + p->prefixlen = IPV4_MAX_BITLEN; + } } break; case AFI_IP6: p->family = AF_INET6; if (is_bgp_static) { - p->u.prefix6 = pi->net->p.u.prefix6; - p->prefixlen = pi->net->p.prefixlen; + p->u.prefix6 = p_orig->u.prefix6; + p->prefixlen = p_orig->prefixlen; } else { - p->u.prefix6 = pi->attr->mp_nexthop_global; + /* If we receive MP_REACH nexthop with ::(LL) + * or LL(LL), use LL address as nexthop cache. + */ + if (pi->attr->mp_nexthop_len + == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL + && (IN6_IS_ADDR_UNSPECIFIED( + &pi->attr->mp_nexthop_global) + || IN6_IS_ADDR_LINKLOCAL( + &pi->attr->mp_nexthop_global))) + p->u.prefix6 = pi->attr->mp_nexthop_local; + else + p->u.prefix6 = pi->attr->mp_nexthop_global; p->prefixlen = IPV6_MAX_BITLEN; } break; @@ -572,7 +578,7 @@ static int make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p) if (BGP_DEBUG(nht, NHT)) { zlog_debug( "%s: Attempting to make prefix with unknown AFI %d (not %d or %d)", - __FUNCTION__, afi, AFI_IP, AFI_IP6); + __func__, afi, AFI_IP, AFI_IP6); } break; } @@ -590,7 +596,6 @@ static int make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p) */ static void sendmsg_zebra_rnh(struct bgp_nexthop_cache *bnc, int command) { - struct prefix *p; bool exact_match = false; int ret; @@ -600,33 +605,30 @@ static void sendmsg_zebra_rnh(struct bgp_nexthop_cache *bnc, int command) /* Don't try to register if Zebra doesn't know of this instance. */ if (!IS_BGP_INST_KNOWN_TO_ZEBRA(bnc->bgp)) { if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug("%s: No zebra instance to talk to, not installing NHT entry", - __PRETTY_FUNCTION__); + zlog_debug( + "%s: No zebra instance to talk to, not installing NHT entry", + __func__); return; } if (!bgp_zebra_num_connects()) { if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug("%s: We have not connected yet, cannot send nexthops", - __PRETTY_FUNCTION__); + zlog_debug( + "%s: We have not connected yet, cannot send nexthops", + __func__); } - p = &(bnc->node->p); if ((command == ZEBRA_NEXTHOP_REGISTER || command == ZEBRA_IMPORT_ROUTE_REGISTER) && (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_CONNECTED) || CHECK_FLAG(bnc->flags, BGP_STATIC_ROUTE_EXACT_MATCH))) exact_match = true; - if (BGP_DEBUG(zebra, ZEBRA)) { - char buf[PREFIX2STR_BUFFER]; + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("%s: sending cmd %s for %pFX (vrf %s)", __func__, + zserv_command_string(command), &bnc->prefix, + bnc->bgp->name_pretty); - prefix2str(p, buf, PREFIX2STR_BUFFER); - zlog_debug("%s: sending cmd %s for %s (vrf %s)", - __func__, zserv_command_string(command), buf, - bnc->bgp->name); - } - - ret = zclient_send_rnh(zclient, command, p, exact_match, + ret = zclient_send_rnh(zclient, command, &bnc->prefix, exact_match, bnc->bgp->vrf_id); /* TBD: handle the failure */ if (ret < 0) @@ -691,20 +693,22 @@ static void unregister_zebra_rnh(struct bgp_nexthop_cache *bnc, */ static void evaluate_paths(struct bgp_nexthop_cache *bnc) { - struct bgp_node *rn; + struct bgp_dest *dest; struct bgp_path_info *path; int afi; struct peer *peer = (struct peer *)bnc->nht_info; struct bgp_table *table; safi_t safi; struct bgp *bgp_path; + const struct prefix *p; if (BGP_DEBUG(nht, NHT)) { char buf[PREFIX2STR_BUFFER]; bnc_str(bnc, buf, PREFIX2STR_BUFFER); zlog_debug( - "NH update for %s - flags 0x%x chgflags 0x%x - evaluate paths", - buf, bnc->flags, bnc->change_flags); + "NH update for %s(%u)(%s) - flags 0x%x chgflags 0x%x - evaluate paths", + buf, bnc->srte_color, bnc->bgp->name_pretty, bnc->flags, + bnc->change_flags); } LIST_FOREACH (path, &(bnc->paths), nh_thread) { @@ -714,10 +718,11 @@ static void evaluate_paths(struct bgp_nexthop_cache *bnc) || (path->sub_type == BGP_ROUTE_IMPORTED)))) continue; - rn = path->net; - assert(rn && bgp_node_table(rn)); - afi = family2afi(rn->p.family); - table = bgp_node_table(rn); + dest = path->net; + assert(dest && bgp_dest_table(dest)); + p = bgp_dest_get_prefix(dest); + afi = family2afi(p->family); + table = bgp_dest_table(dest); safi = table->safi; /* @@ -736,7 +741,8 @@ static void evaluate_paths(struct bgp_nexthop_cache *bnc) * nexthops with labels */ - int bnc_is_valid_nexthop = 0; + bool bnc_is_valid_nexthop = false; + bool path_valid = false; if (safi == SAFI_UNICAST && path->sub_type == BGP_ROUTE_IMPORTED && @@ -744,35 +750,41 @@ static void evaluate_paths(struct bgp_nexthop_cache *bnc) path->extra->num_labels) { bnc_is_valid_nexthop = - bgp_isvalid_labeled_nexthop(bnc) ? 1 : 0; + bgp_isvalid_labeled_nexthop(bnc) ? true : false; } else { - bnc_is_valid_nexthop = - bgp_isvalid_nexthop(bnc) ? 1 : 0; + if (bgp_update_martian_nexthop( + bnc->bgp, afi, safi, path->type, + path->sub_type, path->attr, dest)) { + if (BGP_DEBUG(nht, NHT)) + zlog_debug( + "%s: prefix %pRN (vrf %s), ignoring path due to martian or self-next-hop", + __func__, dest, bgp_path->name); + } else + bnc_is_valid_nexthop = + bgp_isvalid_nexthop(bnc) ? true : false; } if (BGP_DEBUG(nht, NHT)) { - char buf[PREFIX_STRLEN]; + char buf1[RD_ADDRSTRLEN]; - prefix2str(&rn->p, buf, PREFIX_STRLEN); - zlog_debug("%s: prefix %s (vrf %s) %svalid", - __func__, buf, bgp_path->name, - (bnc_is_valid_nexthop ? "" : "not ")); + if (dest->pdest) { + prefix_rd2str((struct prefix_rd *)bgp_dest_get_prefix(dest->pdest), + buf1, sizeof(buf1)); + zlog_debug( + "... eval path %d/%d %pRN RD %s %s flags 0x%x", + afi, safi, dest, buf1, + bgp_path->name_pretty, path->flags); + } else + zlog_debug( + "... eval path %d/%d %pRN %s flags 0x%x", + afi, safi, dest, bgp_path->name_pretty, + path->flags); } - if ((CHECK_FLAG(path->flags, BGP_PATH_VALID) ? 1 : 0) - != bnc_is_valid_nexthop) { - if (CHECK_FLAG(path->flags, BGP_PATH_VALID)) { - bgp_aggregate_decrement(bgp_path, &rn->p, - path, afi, safi); - bgp_path_info_unset_flag(rn, path, - BGP_PATH_VALID); - } else { - bgp_path_info_set_flag(rn, path, - BGP_PATH_VALID); - bgp_aggregate_increment(bgp_path, &rn->p, - path, afi, safi); - } - } + /* Skip paths marked for removal or as history. */ + if (CHECK_FLAG(path->flags, BGP_PATH_REMOVED) + || CHECK_FLAG(path->flags, BGP_PATH_HISTORY)) + continue; /* Copy the metric to the path. Will be used for bestpath * computation */ @@ -783,18 +795,67 @@ static void evaluate_paths(struct bgp_nexthop_cache *bnc) path->extra->igpmetric = 0; if (CHECK_FLAG(bnc->change_flags, BGP_NEXTHOP_METRIC_CHANGED) - || CHECK_FLAG(bnc->change_flags, BGP_NEXTHOP_CHANGED)) + || CHECK_FLAG(bnc->change_flags, BGP_NEXTHOP_CHANGED) + || path->attr->srte_color != 0) SET_FLAG(path->flags, BGP_PATH_IGP_CHANGED); - bgp_process(bgp_path, rn, afi, safi); + path_valid = !!CHECK_FLAG(path->flags, BGP_PATH_VALID); + if (path_valid != bnc_is_valid_nexthop) { + if (path_valid) { + /* No longer valid, clear flag; also for EVPN + * routes, unimport from VRFs if needed. + */ + bgp_aggregate_decrement(bgp_path, p, path, afi, + safi); + bgp_path_info_unset_flag(dest, path, + BGP_PATH_VALID); + if (safi == SAFI_EVPN && + bgp_evpn_is_prefix_nht_supported(bgp_dest_get_prefix(dest))) + bgp_evpn_unimport_route(bgp_path, + afi, safi, bgp_dest_get_prefix(dest), path); + } else { + /* Path becomes valid, set flag; also for EVPN + * routes, import from VRFs if needed. + */ + bgp_path_info_set_flag(dest, path, + BGP_PATH_VALID); + bgp_aggregate_increment(bgp_path, p, path, afi, + safi); + if (safi == SAFI_EVPN && + bgp_evpn_is_prefix_nht_supported(bgp_dest_get_prefix(dest))) + bgp_evpn_import_route(bgp_path, + afi, safi, bgp_dest_get_prefix(dest), path); + } + } + + bgp_process(bgp_path, dest, afi, safi); } - if (peer && !CHECK_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED)) { - if (BGP_DEBUG(nht, NHT)) - zlog_debug("%s: Updating peer (%s) status with NHT", - __FUNCTION__, peer->host); - bgp_fsm_event_update(peer, bgp_isvalid_nexthop(bnc)); - SET_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED); + if (peer) { + int valid_nexthops = bgp_isvalid_nexthop(bnc); + + if (valid_nexthops) { + /* + * Peering cannot occur across a blackhole nexthop + */ + if (bnc->nexthop_num == 1 && bnc->nexthop + && bnc->nexthop->type == NEXTHOP_TYPE_BLACKHOLE) { + peer->last_reset = PEER_DOWN_WAITING_NHT; + valid_nexthops = 0; + } else + peer->last_reset = PEER_DOWN_WAITING_OPEN; + } else + peer->last_reset = PEER_DOWN_WAITING_NHT; + + if (!CHECK_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED)) { + if (BGP_DEBUG(nht, NHT)) + zlog_debug( + "%s: Updating peer (%s(%s)) status with NHT", + __func__, peer->host, + peer->bgp->name_pretty); + bgp_fsm_event_update(peer, valid_nexthops); + SET_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED); + } } RESET_FLAG(bnc->change_flags); @@ -830,30 +891,19 @@ void path_nh_map(struct bgp_path_info *path, struct bgp_nexthop_cache *bnc, */ void bgp_nht_register_nexthops(struct bgp *bgp) { - struct bgp_node *rn; - struct bgp_nexthop_cache *bnc; - afi_t afi; - - for (afi = AFI_IP; afi < AFI_MAX; afi++) { - if (!bgp->nexthop_cache_table[afi]) - continue; - - for (rn = bgp_table_top(bgp->nexthop_cache_table[afi]); rn; - rn = bgp_route_next(rn)) { - bnc = bgp_node_get_bgp_nexthop_info(rn); - - if (!bnc) - continue; + for (afi_t afi = AFI_IP; afi < AFI_MAX; afi++) { + struct bgp_nexthop_cache *bnc; + frr_each (bgp_nexthop_cache, &bgp->nexthop_cache_table[afi], + bnc) { register_zebra_rnh(bnc, 0); } } } -void bgp_nht_register_enhe_capability_interfaces(struct peer *peer) +void bgp_nht_reg_enhe_cap_intfs(struct peer *peer) { struct bgp *bgp; - struct bgp_node *rn; struct bgp_nexthop_cache *bnc; struct nexthop *nhop; struct interface *ifp; @@ -863,24 +913,16 @@ void bgp_nht_register_enhe_capability_interfaces(struct peer *peer) return; bgp = peer->bgp; - - if (!bgp->nexthop_cache_table[AFI_IP6]) - return; - if (!sockunion2hostprefix(&peer->su, &p)) { - if (BGP_DEBUG(nht, NHT)) - zlog_debug("%s: Unable to convert prefix to sockunion", - __PRETTY_FUNCTION__); + zlog_warn("%s: Unable to convert sockunion to prefix for %s", + __func__, peer->host); return; } if (p.family != AF_INET6) return; - rn = bgp_node_lookup(bgp->nexthop_cache_table[AFI_IP6], &p); - if (!rn) - return; - bnc = bgp_node_get_bgp_nexthop_info(rn); + bnc = bnc_find(&bgp->nexthop_cache_table[AFI_IP6], &p, 0); if (!bnc) return; @@ -888,11 +930,54 @@ void bgp_nht_register_enhe_capability_interfaces(struct peer *peer) return; for (nhop = bnc->nexthop; nhop; nhop = nhop->next) { - ifp = if_lookup_by_index(nhop->ifindex, - nhop->vrf_id); + ifp = if_lookup_by_index(nhop->ifindex, nhop->vrf_id); + + if (!ifp) + continue; + zclient_send_interface_radv_req(zclient, nhop->vrf_id, ifp, true, BGP_UNNUM_DEFAULT_RA_INTERVAL); } } + +void bgp_nht_dereg_enhe_cap_intfs(struct peer *peer) +{ + struct bgp *bgp; + struct bgp_nexthop_cache *bnc; + struct nexthop *nhop; + struct interface *ifp; + struct prefix p; + + if (peer->ifp) + return; + + bgp = peer->bgp; + + if (!sockunion2hostprefix(&peer->su, &p)) { + zlog_warn("%s: Unable to convert sockunion to prefix for %s", + __func__, peer->host); + return; + } + + if (p.family != AF_INET6) + return; + + bnc = bnc_find(&bgp->nexthop_cache_table[AFI_IP6], &p, 0); + if (!bnc) + return; + + if (peer != bnc->nht_info) + return; + + for (nhop = bnc->nexthop; nhop; nhop = nhop->next) { + ifp = if_lookup_by_index(nhop->ifindex, nhop->vrf_id); + + if (!ifp) + continue; + + zclient_send_interface_radv_req(zclient, nhop->vrf_id, ifp, 0, + 0); + } +} diff --git a/bgpd/bgp_nht.h b/bgpd/bgp_nht.h index 7daae93b25..4e015e4aae 100644 --- a/bgpd/bgp_nht.h +++ b/bgpd/bgp_nht.h @@ -26,14 +26,6 @@ */ extern void bgp_parse_nexthop_update(int command, vrf_id_t vrf_id); -/** - * bgp_find_nexthop() - lookup the nexthop cache table for the bnc object - * ARGUMENTS: - * p - path for which the nexthop object is being looked up - * connected - True if NH MUST be a connected route - */ -extern int bgp_find_nexthop(struct bgp_path_info *p, int connected); - /** * bgp_find_or_add_nexthop() - lookup the nexthop cache table for the bnc * object. If not found, create a new object and register with ZEBRA for @@ -95,6 +87,7 @@ extern void bgp_nht_register_nexthops(struct bgp *bgp); * this code can walk the registered nexthops and * register the important ones with zebra for RA. */ -extern void bgp_nht_register_enhe_capability_interfaces(struct peer *peer); +extern void bgp_nht_reg_enhe_cap_intfs(struct peer *peer); +extern void bgp_nht_dereg_enhe_cap_intfs(struct peer *peer); #endif /* _BGP_NHT_H */ diff --git a/bgpd/bgp_open.c b/bgpd/bgp_open.c index 7af5827d00..6cfcb9cc3d 100644 --- a/bgpd/bgp_open.c +++ b/bgpd/bgp_open.c @@ -264,8 +264,9 @@ static int bgp_capability_mp(struct peer *peer, struct capability_header *hdr) bgp_capability_mp_data(s, &mpc); if (bgp_debug_neighbor_events(peer)) - zlog_debug("%s OPEN has MP_EXT CAP for afi/safi: %u/%u", - peer->host, mpc.afi, mpc.safi); + zlog_debug("%s OPEN has MP_EXT CAP for afi/safi: %s/%s", + peer->host, iana_afi2str(mpc.afi), + iana_safi2str(mpc.safi)); /* Convert AFI, SAFI to internal values, check. */ if (bgp_map_afi_safi_iana2int(mpc.afi, mpc.safi, &afi, &safi)) @@ -325,14 +326,13 @@ static int bgp_capability_orf_entry(struct peer *peer, pkt_safi = mpc.safi; if (bgp_debug_neighbor_events(peer)) - zlog_debug("%s ORF Cap entry for afi/safi: %u/%u", peer->host, - mpc.afi, mpc.safi); + zlog_debug("%s ORF Cap entry for afi/safi: %s/%s", peer->host, + iana_afi2str(mpc.afi), iana_safi2str(mpc.safi)); /* Convert AFI, SAFI to internal values, check. */ if (bgp_map_afi_safi_iana2int(pkt_afi, pkt_safi, &afi, &safi)) { zlog_info( - "%s Addr-family %d/%d not supported." - " Ignoring the ORF capability", + "%s Addr-family %d/%d not supported. Ignoring the ORF capability", peer->host, pkt_afi, pkt_safi); return 0; } @@ -343,8 +343,7 @@ static int bgp_capability_orf_entry(struct peer *peer, /* validate number field */ if (CAPABILITY_CODE_ORF_LEN + (num * 2) > hdr->length) { zlog_info( - "%s ORF Capability entry length error," - " Cap length %u, num %u", + "%s ORF Capability entry length error, Cap length %u, num %u", peer->host, hdr->length, num); bgp_notify_send(peer, BGP_NOTIFY_OPEN_ERR, BGP_NOTIFY_OPEN_MALFORMED_ATTR); @@ -406,12 +405,11 @@ static int bgp_capability_orf_entry(struct peer *peer, if (bgp_debug_neighbor_events(peer)) zlog_debug( - "%s OPEN has %s ORF capability" - " as %s for afi/safi: %d/%d", + "%s OPEN has %s ORF capability as %s for afi/safi: %s/%s", peer->host, lookup_msg(orf_type_str, type, NULL), - lookup_msg(orf_mode_str, mode, NULL), pkt_afi, - pkt_safi); + lookup_msg(orf_mode_str, mode, NULL), + iana_afi2str(pkt_afi), iana_safi2str(pkt_safi)); if (hdr->code == CAPABILITY_CODE_ORF) { sm_cap = PEER_CAP_ORF_PREFIX_SM_RCV; @@ -461,6 +459,8 @@ static int bgp_capability_restart(struct peer *peer, restart_flag_time = stream_getw(s); if (CHECK_FLAG(restart_flag_time, RESTART_R_BIT)) SET_FLAG(peer->cap, PEER_CAP_RESTART_BIT_RCV); + else + UNSET_FLAG(peer->cap, PEER_CAP_RESTART_BIT_RCV); UNSET_FLAG(restart_flag_time, 0xF000); peer->v_gr_restart = restart_flag_time; @@ -487,20 +487,20 @@ static int bgp_capability_restart(struct peer *peer, if (bgp_map_afi_safi_iana2int(pkt_afi, pkt_safi, &afi, &safi)) { if (bgp_debug_neighbor_events(peer)) zlog_debug( - "%s Addr-family %d/%d(afi/safi) not supported." - " Ignore the Graceful Restart capability for this AFI/SAFI", - peer->host, pkt_afi, pkt_safi); + "%s Addr-family %s/%s(afi/safi) not supported. Ignore the Graceful Restart capability for this AFI/SAFI", + peer->host, iana_afi2str(pkt_afi), + iana_safi2str(pkt_safi)); } else if (!peer->afc[afi][safi]) { if (bgp_debug_neighbor_events(peer)) zlog_debug( - "%s Addr-family %d/%d(afi/safi) not enabled." - " Ignore the Graceful Restart capability", - peer->host, pkt_afi, pkt_safi); + "%s Addr-family %s/%s(afi/safi) not enabled. Ignore the Graceful Restart capability", + peer->host, iana_afi2str(pkt_afi), + iana_safi2str(pkt_safi)); } else { if (bgp_debug_neighbor_events(peer)) zlog_debug( "%s Address family %s is%spreserved", - peer->host, afi_safi_print(afi, safi), + peer->host, get_afi_safi_str(afi, safi, false), CHECK_FLAG( peer->af_cap[afi][safi], PEER_CAP_RESTART_AF_PRESERVE_RCV) @@ -564,8 +564,9 @@ static int bgp_capability_addpath(struct peer *peer, if (bgp_debug_neighbor_events(peer)) zlog_debug( - "%s OPEN has AddPath CAP for afi/safi: %u/%u%s%s", - peer->host, pkt_afi, pkt_safi, + "%s OPEN has AddPath CAP for afi/safi: %s/%s%s%s", + peer->host, iana_afi2str(pkt_afi), + iana_safi2str(pkt_safi), (send_receive & BGP_ADDPATH_RX) ? ", receive" : "", (send_receive & BGP_ADDPATH_TX) ? ", transmit" @@ -575,16 +576,16 @@ static int bgp_capability_addpath(struct peer *peer, if (bgp_map_afi_safi_iana2int(pkt_afi, pkt_safi, &afi, &safi)) { if (bgp_debug_neighbor_events(peer)) zlog_debug( - "%s Addr-family %d/%d(afi/safi) not supported." - " Ignore the Addpath Attribute for this AFI/SAFI", - peer->host, pkt_afi, pkt_safi); + "%s Addr-family %s/%s(afi/safi) not supported. Ignore the Addpath Attribute for this AFI/SAFI", + peer->host, iana_afi2str(pkt_afi), + iana_safi2str(pkt_safi)); continue; } else if (!peer->afc[afi][safi]) { if (bgp_debug_neighbor_events(peer)) zlog_debug( - "%s Addr-family %d/%d(afi/safi) not enabled." - " Ignore the AddPath capability for this AFI/SAFI", - peer->host, pkt_afi, pkt_safi); + "%s Addr-family %s/%s(afi/safi) not enabled. Ignore the AddPath capability for this AFI/SAFI", + peer->host, iana_afi2str(pkt_afi), + iana_safi2str(pkt_safi)); continue; } @@ -624,16 +625,17 @@ static int bgp_capability_enhe(struct peer *peer, struct capability_header *hdr) if (bgp_debug_neighbor_events(peer)) zlog_debug( - "%s Received with afi/safi/next-hop afi: %u/%u/%u", - peer->host, pkt_afi, pkt_safi, pkt_nh_afi); + "%s Received with afi/safi/next-hop afi: %s/%s/%u", + peer->host, iana_afi2str(pkt_afi), + iana_safi2str(pkt_safi), pkt_nh_afi); /* Convert AFI, SAFI to internal values, check. */ if (bgp_map_afi_safi_iana2int(pkt_afi, pkt_safi, &afi, &safi)) { if (bgp_debug_neighbor_events(peer)) zlog_debug( - "%s Addr-family %d/%d(afi/safi) not supported." - " Ignore the ENHE Attribute for this AFI/SAFI", - peer->host, pkt_afi, pkt_safi); + "%s Addr-family %s/%s(afi/safi) not supported. Ignore the ENHE Attribute for this AFI/SAFI", + peer->host, iana_afi2str(pkt_afi), + iana_safi2str(pkt_safi)); continue; } @@ -648,13 +650,13 @@ static int bgp_capability_enhe(struct peer *peer, struct capability_header *hdr) nh_afi = afi_iana2int(pkt_nh_afi); if (afi != AFI_IP || nh_afi != AFI_IP6 - || !(safi == SAFI_UNICAST + || !(safi == SAFI_UNICAST || safi == SAFI_MPLS_VPN || safi == SAFI_LABELED_UNICAST)) { flog_warn( EC_BGP_CAPABILITY_INVALID_DATA, - "%s Unexpected afi/safi/next-hop afi: %u/%u/%u " - "in Extended Next-hop capability, ignoring", - peer->host, pkt_afi, pkt_safi, pkt_nh_afi); + "%s Unexpected afi/safi/next-hop afi: %s/%s/%u in Extended Next-hop capability, ignoring", + peer->host, iana_afi2str(pkt_afi), + iana_safi2str(pkt_safi), pkt_nh_afi); continue; } @@ -685,7 +687,7 @@ static int bgp_capability_hostname(struct peer *peer, flog_warn( EC_BGP_CAPABILITY_INVALID_DATA, "%s: Received malformed hostname capability from peer %s", - __FUNCTION__, peer->host); + __func__, peer->host); return -1; } @@ -699,15 +701,8 @@ static int bgp_capability_hostname(struct peer *peer, if (len) { str[len] = '\0'; - if (peer->hostname != NULL) { - XFREE(MTYPE_BGP_PEER_HOST, peer->hostname); - peer->hostname = NULL; - } - - if (peer->domainname != NULL) { - XFREE(MTYPE_BGP_PEER_HOST, peer->domainname); - peer->domainname = NULL; - } + XFREE(MTYPE_BGP_PEER_HOST, peer->hostname); + XFREE(MTYPE_BGP_PEER_HOST, peer->domainname); peer->hostname = XSTRDUP(MTYPE_BGP_PEER_HOST, str); } @@ -716,7 +711,7 @@ static int bgp_capability_hostname(struct peer *peer, flog_warn( EC_BGP_CAPABILITY_INVALID_DATA, "%s: Received invalid domain name len (hostname capability) from peer %s", - __FUNCTION__, peer->host); + __func__, peer->host); return -1; } @@ -725,7 +720,7 @@ static int bgp_capability_hostname(struct peer *peer, flog_warn( EC_BGP_CAPABILITY_INVALID_DATA, "%s: Received runt domain name (hostname capability) from peer %s", - __FUNCTION__, peer->host); + __func__, peer->host); return -1; } @@ -738,6 +733,9 @@ static int bgp_capability_hostname(struct peer *peer, if (len) { str[len] = '\0'; + + XFREE(MTYPE_BGP_PEER_HOST, peer->domainname); + peer->domainname = XSTRDUP(MTYPE_BGP_PEER_HOST, str); } @@ -813,6 +811,7 @@ static int bgp_capability_parse(struct peer *peer, size_t length, int ret; struct stream *s = BGP_INPUT(peer); size_t end = stream_get_getp(s) + length; + uint16_t restart_flag_time = 0; assert(STREAM_READABLE(s) >= length); @@ -867,8 +866,7 @@ static int bgp_capability_parse(struct peer *peer, size_t length, /* Check length. */ if (caphdr.length < cap_minsizes[caphdr.code]) { zlog_info( - "%s %s Capability length error: got %u," - " expected at least %u", + "%s %s Capability length error: got %u, expected at least %u", peer->host, lookup_msg(capcode_str, caphdr.code, NULL), @@ -881,8 +879,7 @@ static int bgp_capability_parse(struct peer *peer, size_t length, if (caphdr.length && caphdr.length % cap_modsizes[caphdr.code] != 0) { zlog_info( - "%s %s Capability length error: got %u," - " expected a multiple of %u", + "%s %s Capability length error: got %u, expected a multiple of %u", peer->host, lookup_msg(capcode_str, caphdr.code, NULL), @@ -989,6 +986,11 @@ static int bgp_capability_parse(struct peer *peer, size_t length, caphdr.length); stream_set_getp(s, start + caphdr.length); } + + if (!CHECK_FLAG(peer->cap, PEER_CAP_RESTART_RCV)) { + UNSET_FLAG(restart_flag_time, 0xF000); + peer->v_gr_restart = restart_flag_time; + } } return 0; } @@ -1000,15 +1002,15 @@ static int bgp_auth_parse(struct peer *peer, size_t length) return -1; } -static int strict_capability_same(struct peer *peer) +static bool strict_capability_same(struct peer *peer) { int i, j; for (i = AFI_IP; i < AFI_MAX; i++) for (j = SAFI_UNICAST; j < SAFI_MAX; j++) if (peer->afc[i][j] != peer->afc_nego[i][j]) - return 0; - return 1; + return false; + return true; } /* peek into option, stores ASN to *as4 if the AS4 capability was found. @@ -1022,9 +1024,8 @@ as_t peek_for_as4_capability(struct peer *peer, uint8_t length) as_t as4 = 0; if (BGP_DEBUG(as4, AS4)) - zlog_info( - "%s [AS4] rcv OPEN w/ OPTION parameter len: %u," - " peeking for as4", + zlog_debug( + "%s [AS4] rcv OPEN w/ OPTION parameter len: %u, peeking for as4", peer->host, length); /* the error cases we DONT handle, we ONLY try to read as4 out of * correctly formatted options. @@ -1066,7 +1067,7 @@ as_t peek_for_as4_capability(struct peer *peer, uint8_t length) if (hdr.code == CAPABILITY_CODE_AS4) { if (BGP_DEBUG(as4, AS4)) - zlog_info( + zlog_debug( "[AS4] found AS4 capability, about to parse"); as4 = bgp_capability_as4(peer, &hdr); @@ -1101,6 +1102,9 @@ int bgp_open_option_parse(struct peer *peer, uint8_t length, int *mp_capability) zlog_debug("%s rcv OPEN w/ OPTION parameter len: %u", peer->host, length); + /* Unset any previously received GR capability. */ + UNSET_FLAG(peer->cap, PEER_CAP_RESTART_RCV); + while (stream_get_getp(s) < end) { uint8_t opt_type; uint8_t opt_length; @@ -1198,8 +1202,7 @@ int bgp_open_option_parse(struct peer *peer, uint8_t length, int *mp_capability) && !peer->afc_nego[AFI_IP6][SAFI_FLOWSPEC] && !peer->afc_nego[AFI_L2VPN][SAFI_EVPN]) { flog_err(EC_BGP_PKT_OPEN, - "%s [Error] Configured AFI/SAFIs do not " - "overlap with received MP capabilities", + "%s [Error] Configured AFI/SAFIs do not overlap with received MP capabilities", peer->host); if (error != error_data) @@ -1284,6 +1287,87 @@ static void bgp_open_capability_orf(struct stream *s, struct peer *peer, stream_putc_at(s, capp, cap_len); } +static void bgp_peer_send_gr_capability(struct stream *s, struct peer *peer, + unsigned long cp) +{ + int len; + iana_afi_t pkt_afi; + afi_t afi; + safi_t safi; + iana_safi_t pkt_safi; + uint32_t restart_time; + unsigned long capp = 0; + unsigned long rcapp = 0; + + if (!CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART) + && !CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART_HELPER)) + return; + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug("[BGP_GR] Sending helper Capability for Peer :%s :", + peer->host); + + SET_FLAG(peer->cap, PEER_CAP_RESTART_ADV); + stream_putc(s, BGP_OPEN_OPT_CAP); + capp = stream_get_endp(s); /* Set Capability Len Pointer */ + stream_putc(s, 0); /* Capability Length */ + stream_putc(s, CAPABILITY_CODE_RESTART); + /* Set Restart Capability Len Pointer */ + rcapp = stream_get_endp(s); + stream_putc(s, 0); + restart_time = peer->bgp->restart_time; + if (peer->bgp->t_startup) { + SET_FLAG(restart_time, RESTART_R_BIT); + SET_FLAG(peer->cap, PEER_CAP_RESTART_BIT_ADV); + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug("[BGP_GR] Sending R-Bit for Peer :%s :", + peer->host); + } + + stream_putw(s, restart_time); + + /* Send address-family specific graceful-restart capability + * only when GR config is present + */ + if (CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART)) { + if (CHECK_FLAG(peer->bgp->flags, BGP_FLAG_GR_PRESERVE_FWD) + && BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug("[BGP_GR] F bit Set"); + + FOREACH_AFI_SAFI (afi, safi) { + if (!peer->afc[afi][safi]) + continue; + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] Sending GR Capability for AFI :%d :, SAFI :%d:", + afi, safi); + + /* Convert AFI, SAFI to values for + * packet. + */ + bgp_map_afi_safi_int2iana(afi, safi, &pkt_afi, + &pkt_safi); + stream_putw(s, pkt_afi); + stream_putc(s, pkt_safi); + if (CHECK_FLAG(peer->bgp->flags, + BGP_FLAG_GR_PRESERVE_FWD)) + stream_putc(s, RESTART_F_BIT); + else + stream_putc(s, 0); + } + } + + /* Total Graceful restart capability Len. */ + len = stream_get_endp(s) - rcapp - 1; + stream_putc_at(s, rcapp, len); + + /* Total Capability Len. */ + len = stream_get_endp(s) - capp - 1; + stream_putc_at(s, capp, len); +} + /* Fill in capability open option to the packet. */ void bgp_open_capability(struct stream *s, struct peer *peer) { @@ -1294,7 +1378,6 @@ void bgp_open_capability(struct stream *s, struct peer *peer) safi_t safi; iana_safi_t pkt_safi; as_t local_as; - uint32_t restart_time; uint8_t afi_safi_count = 0; int adv_addpath_tx = 0; @@ -1332,7 +1415,7 @@ void bgp_open_capability(struct stream *s, struct peer *peer) if (CHECK_FLAG(peer->flags, PEER_FLAG_CAPABILITY_ENHE) && peer->su.sa.sa_family == AF_INET6 && afi == AFI_IP - && (safi == SAFI_UNICAST + && (safi == SAFI_UNICAST || safi == SAFI_MPLS_VPN || safi == SAFI_LABELED_UNICAST)) { /* RFC 5549 Extended Next Hop Encoding */ @@ -1487,50 +1570,7 @@ void bgp_open_capability(struct stream *s, struct peer *peer) cmd_domainname_get()); } - /* Sending base graceful-restart capability irrespective of the config - */ - SET_FLAG(peer->cap, PEER_CAP_RESTART_ADV); - stream_putc(s, BGP_OPEN_OPT_CAP); - capp = stream_get_endp(s); /* Set Capability Len Pointer */ - stream_putc(s, 0); /* Capability Length */ - stream_putc(s, CAPABILITY_CODE_RESTART); - rcapp = stream_get_endp(s); /* Set Restart Capability Len Pointer */ - stream_putc(s, 0); - restart_time = peer->bgp->restart_time; - if (peer->bgp->t_startup) { - SET_FLAG(restart_time, RESTART_R_BIT); - SET_FLAG(peer->cap, PEER_CAP_RESTART_BIT_ADV); - } - stream_putw(s, restart_time); - - /* Send address-family specific graceful-restart capability only when GR - config - is present */ - if (bgp_flag_check(peer->bgp, BGP_FLAG_GRACEFUL_RESTART)) { - FOREACH_AFI_SAFI (afi, safi) { - if (peer->afc[afi][safi]) { - /* Convert AFI, SAFI to values for - * packet. */ - bgp_map_afi_safi_int2iana(afi, safi, &pkt_afi, - &pkt_safi); - stream_putw(s, pkt_afi); - stream_putc(s, pkt_safi); - if (bgp_flag_check(peer->bgp, - BGP_FLAG_GR_PRESERVE_FWD)) - stream_putc(s, RESTART_F_BIT); - else - stream_putc(s, 0); - } - } - } - - /* Total Graceful restart capability Len. */ - len = stream_get_endp(s) - rcapp - 1; - stream_putc_at(s, rcapp, len); - - /* Total Capability Len. */ - len = stream_get_endp(s) - capp - 1; - stream_putc_at(s, capp, len); + bgp_peer_send_gr_capability(s, peer, cp); /* Total Opt Parm Len. */ len = stream_get_endp(s) - cp - 1; diff --git a/bgpd/bgp_open.h b/bgpd/bgp_open.h index 8359f59a41..5250a68581 100644 --- a/bgpd/bgp_open.h +++ b/bgpd/bgp_open.h @@ -68,13 +68,13 @@ struct graceful_restart_af { /* Cooperative Route Filtering Capability. */ /* ORF Type */ -#define ORF_TYPE_PREFIX 64 +#define ORF_TYPE_PREFIX 64 #define ORF_TYPE_PREFIX_OLD 128 /* ORF Mode */ -#define ORF_MODE_RECEIVE 1 -#define ORF_MODE_SEND 2 -#define ORF_MODE_BOTH 3 +#define ORF_MODE_RECEIVE 1 +#define ORF_MODE_SEND 2 +#define ORF_MODE_BOTH 3 /* Capability Message Action. */ #define CAPABILITY_ACTION_SET 0 diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index 130e06a6cf..2c8a375a55 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -41,6 +41,7 @@ #include "bgpd/bgpd.h" #include "bgpd/bgp_table.h" #include "bgpd/bgp_dump.h" +#include "bgpd/bgp_bmp.h" #include "bgpd/bgp_attr.h" #include "bgpd/bgp_debug.h" #include "bgpd/bgp_errors.h" @@ -63,6 +64,16 @@ #include "bgpd/bgp_keepalives.h" #include "bgpd/bgp_flowspec.h" +DEFINE_HOOK(bgp_packet_dump, + (struct peer *peer, uint8_t type, bgp_size_t size, + struct stream *s), + (peer, type, size, s)) + +DEFINE_HOOK(bgp_packet_send, + (struct peer *peer, uint8_t type, bgp_size_t size, + struct stream *s), + (peer, type, size, s)) + /** * Sets marker and type fields for a BGP message. * @@ -113,9 +124,9 @@ int bgp_packet_set_size(struct stream *s) */ static void bgp_packet_add(struct peer *peer, struct stream *s) { - pthread_mutex_lock(&peer->io_mtx); - stream_fifo_push(peer->obuf, s); - pthread_mutex_unlock(&peer->io_mtx); + frr_with_mutex(&peer->io_mtx) { + stream_fifo_push(peer->obuf, s); + } } static struct stream *bgp_update_packet_eor(struct peer *peer, afi_t afi, @@ -130,7 +141,7 @@ static struct stream *bgp_update_packet_eor(struct peer *peer, afi_t afi, if (bgp_debug_neighbor_events(peer)) zlog_debug("send End-of-RIB for %s to %s", - afi_safi_print(afi, safi), peer->host); + get_afi_safi_str(afi, safi, false), peer->host); s = stream_new(BGP_MAX_PACKET_SIZE); @@ -187,6 +198,8 @@ void bgp_check_update_delay(struct bgp *bgp) PEER_FLAG_CONFIG_NODE) && !CHECK_FLAG(peer->flags, PEER_FLAG_SHUTDOWN) + && !CHECK_FLAG(peer->bgp->flags, + BGP_FLAG_SHUTDOWN) && !peer->update_delay_over) { if (bgp_debug_neighbor_events(peer)) zlog_debug( @@ -389,12 +402,16 @@ int bgp_generate_updgrp_packets(struct thread *thread) /* * The code beyond this part deals with update packets, proceed only * if peer is Established and updates are not on hold (as part of - * update-delay post processing). + * update-delay processing). */ if (peer->status != Established) return 0; - if (peer->bgp->main_peers_update_hold) + if ((peer->bgp->main_peers_update_hold) + || bgp_update_delay_active(peer->bgp)) + return 0; + + if (peer->t_routeadv) return 0; do { @@ -433,23 +450,37 @@ int bgp_generate_updgrp_packets(struct thread *thread) && peer->afc_nego[afi][safi] && peer->synctime && !CHECK_FLAG( - peer->af_sflags[afi] - [safi], - PEER_STATUS_EOR_SEND)) { - SET_FLAG(peer->af_sflags[afi] + peer->af_sflags[afi][safi], + PEER_STATUS_EOR_SEND)) { + /* If EOR is disabled, + * the message is not sent + */ + if (BGP_SEND_EOR(peer->bgp, afi, + safi)) { + SET_FLAG( + peer->af_sflags + [afi] [safi], - PEER_STATUS_EOR_SEND); - - if ((s = bgp_update_packet_eor( - peer, afi, - safi))) { - bgp_packet_add(peer, s); + PEER_STATUS_EOR_SEND); + + /* Update EOR + * send time + */ + peer->eor_stime[afi] + [safi] = + monotime(NULL); + + BGP_UPDATE_EOR_PKT( + peer, afi, safi, + s); } } } continue; } + /* Update packet send time */ + peer->pkt_stime[afi][safi] = monotime(NULL); /* Found a packet template to send, overwrite * packet with appropriate attributes from peer @@ -542,6 +573,7 @@ void bgp_open_send(struct peer *peer) /* Dump packet if debug option is set. */ /* bgp_packet_dump (s); */ + hook_call(bgp_packet_send, peer, BGP_MSG_OPEN, stream_get_endp(s), s); /* Add packet to the peer. */ bgp_packet_add(peer, s); @@ -561,7 +593,7 @@ void bgp_open_send(struct peer *peer) * @param peer * @return 0 */ -static int bgp_write_notify(struct peer *peer) +static void bgp_write_notify(struct peer *peer) { int ret, val; uint8_t type; @@ -571,7 +603,7 @@ static int bgp_write_notify(struct peer *peer) s = stream_fifo_pop(peer->obuf); if (!s) - return 0; + return; assert(stream_get_endp(s) >= BGP_HEADER_SIZE); @@ -591,7 +623,7 @@ static int bgp_write_notify(struct peer *peer) if (ret <= 0) { stream_free(s); BGP_EVENT_ADD(peer, TCP_fatal_error); - return 0; + return; } /* Disable Nagle, make NOTIFY packet go out right away */ @@ -623,8 +655,6 @@ static int bgp_write_notify(struct peer *peer) BGP_EVENT_ADD(peer, BGP_Stop); stream_free(s); - - return 0; } /* @@ -653,7 +683,7 @@ void bgp_notify_send_with_data(struct peer *peer, uint8_t code, struct stream *s; /* Lock I/O mutex to prevent other threads from pushing packets */ - pthread_mutex_lock(&peer->io_mtx); + frr_mutex_lock_autounlock(&peer->io_mtx); /* ============================================== */ /* Allocate new stream. */ @@ -681,9 +711,9 @@ void bgp_notify_send_with_data(struct peer *peer, uint8_t code, * in place because we are sometimes called with a doppelganger peer, * who tends to have a plethora of fields nulled out. */ - if (peer->curr && peer->last_reset_cause_size) { + if (peer->curr) { size_t packetsize = stream_get_endp(peer->curr); - assert(packetsize <= peer->last_reset_cause_size); + assert(packetsize <= sizeof(peer->last_reset_cause)); memcpy(peer->last_reset_cause, peer->curr->data, packetsize); peer->last_reset_cause_size = packetsize; } @@ -709,19 +739,24 @@ void bgp_notify_send_with_data(struct peer *peer, uint8_t code, XMALLOC(MTYPE_TMP, bgp_notify.length * 3); for (i = 0; i < bgp_notify.length; i++) if (first) { - sprintf(c, " %02x", data[i]); - strcat(bgp_notify.data, c); + snprintf(c, sizeof(c), " %02x", + data[i]); + + strlcat(bgp_notify.data, c, + bgp_notify.length); + } else { first = 1; - sprintf(c, "%02x", data[i]); - strcpy(bgp_notify.data, c); + snprintf(c, sizeof(c), "%02x", data[i]); + + strlcpy(bgp_notify.data, c, + bgp_notify.length); } } bgp_notify_print(peer, &bgp_notify, "sending"); if (bgp_notify.data) { XFREE(MTYPE_TMP, bgp_notify.data); - bgp_notify.data = NULL; bgp_notify.length = 0; } } @@ -740,10 +775,11 @@ void bgp_notify_send_with_data(struct peer *peer, uint8_t code, /* Add packet to peer's output queue */ stream_fifo_push(peer->obuf, s); - bgp_write_notify(peer); + bgp_peer_gr_flags_update(peer); + BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(peer->bgp, + peer->bgp->peer); - /* ============================================== */ - pthread_mutex_unlock(&peer->io_mtx); + bgp_write_notify(peer); } /* @@ -819,12 +855,13 @@ void bgp_route_refresh_send(struct peer *peer, afi_t afi, safi_t safi, stream_putc(s, ORF_COMMON_PART_REMOVE_ALL); if (bgp_debug_neighbor_events(peer)) zlog_debug( - "%s sending REFRESH_REQ to remove ORF(%d) (%s) for afi/safi: %d/%d", + "%s sending REFRESH_REQ to remove ORF(%d) (%s) for afi/safi: %s/%s", peer->host, orf_type, (when_to_refresh == REFRESH_DEFER ? "defer" : "immediate"), - pkt_afi, pkt_safi); + iana_afi2str(pkt_afi), + iana_safi2str(pkt_safi)); } else { SET_FLAG(peer->af_sflags[afi][safi], PEER_STATUS_ORF_PREFIX_SEND); @@ -835,12 +872,13 @@ void bgp_route_refresh_send(struct peer *peer, afi_t afi, safi_t safi, ORF_COMMON_PART_DENY); if (bgp_debug_neighbor_events(peer)) zlog_debug( - "%s sending REFRESH_REQ with pfxlist ORF(%d) (%s) for afi/safi: %d/%d", + "%s sending REFRESH_REQ with pfxlist ORF(%d) (%s) for afi/safi: %s/%s", peer->host, orf_type, (when_to_refresh == REFRESH_DEFER ? "defer" : "immediate"), - pkt_afi, pkt_safi); + iana_afi2str(pkt_afi), + iana_safi2str(pkt_safi)); } /* Total ORF Entry Len. */ @@ -853,8 +891,9 @@ void bgp_route_refresh_send(struct peer *peer, afi_t afi, safi_t safi, if (bgp_debug_neighbor_events(peer)) { if (!orf_refresh) - zlog_debug("%s sending REFRESH_REQ for afi/safi: %d/%d", - peer->host, pkt_afi, pkt_safi); + zlog_debug("%s sending REFRESH_REQ for afi/safi: %s/%s", + peer->host, iana_afi2str(pkt_afi), + iana_safi2str(pkt_safi)); } /* Add packet to the peer. */ @@ -898,11 +937,11 @@ void bgp_capability_send(struct peer *peer, afi_t afi, safi_t safi, if (bgp_debug_neighbor_events(peer)) zlog_debug( - "%s sending CAPABILITY has %s MP_EXT CAP for afi/safi: %d/%d", + "%s sending CAPABILITY has %s MP_EXT CAP for afi/safi: %s/%s", peer->host, action == CAPABILITY_ACTION_SET ? "Advertising" : "Removing", - pkt_afi, pkt_safi); + iana_afi2str(pkt_afi), iana_safi2str(pkt_safi)); } /* Set packet size. */ @@ -938,17 +977,24 @@ static int bgp_collision_detect(struct peer *new, struct in_addr remote_id) if (peer->status == Established || peer->status == Clearing) { bgp_notify_send(new, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_COLLISION_RESOLUTION); - return (-1); + return -1; } else if ((peer->status == OpenConfirm) || (peer->status == OpenSent)) { - /* 1. The BGP Identifier of the local system is compared - to - the BGP Identifier of the remote system (as specified - in - the OPEN message). */ - + /* 1. The BGP Identifier of the local system is + * compared to the BGP Identifier of the remote + * system (as specified in the OPEN message). + * + * If the BGP Identifiers of the peers + * involved in the connection collision + * are identical, then the connection + * initiated by the BGP speaker with the + * larger AS number is preserved. + */ if (ntohl(peer->local_id.s_addr) - < ntohl(remote_id.s_addr)) + < ntohl(remote_id.s_addr) + || (ntohl(peer->local_id.s_addr) + == ntohl(remote_id.s_addr) + && peer->local_as < peer->as)) if (!CHECK_FLAG(peer->sflags, PEER_STATUS_ACCEPT_PEER)) { /* 2. If the value of the local BGP @@ -972,6 +1018,14 @@ static int bgp_collision_detect(struct peer *new, struct in_addr remote_id) return -1; } else { + if (ntohl(peer->local_id.s_addr) + == ntohl(remote_id.s_addr) + && peer->local_as == peer->as) + flog_err( + EC_BGP_ROUTER_ID_SAME, + "Peer's router-id %s is the same as ours", + inet_ntoa(remote_id)); + /* 3. Otherwise, the local system closes newly created BGP connection (the one associated with the @@ -1042,7 +1096,7 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size) uint16_t holdtime; uint16_t send_holdtime; as_t remote_as; - as_t as4 = 0; + as_t as4 = 0, as4_be; struct in_addr remote_id; int mp_capability; uint8_t notify_data_remote_as[2]; @@ -1062,8 +1116,7 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size) /* Receive OPEN message log */ if (bgp_debug_neighbor_events(peer)) zlog_debug( - "%s rcv OPEN, version %d, remote-as (in open) %u," - " holdtime %d, id %s", + "%s rcv OPEN, version %d, remote-as (in open) %u, holdtime %d, id %s", peer->host, version, remote_as, holdtime, inet_ntoa(remote_id)); @@ -1085,9 +1138,11 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size) * that we do not know which peer is connecting to us now. */ as4 = peek_for_as4_capability(peer, optlen); - memcpy(notify_data_remote_as4, &as4, 4); } + as4_be = htonl(as4); + memcpy(notify_data_remote_as4, &as4_be, 4); + /* Just in case we have a silly peer who sends AS4 capability set to 0 */ if (CHECK_FLAG(peer->cap, PEER_CAP_AS4_RCV) && !as4) { @@ -1100,6 +1155,15 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size) return BGP_Stop; } + /* Codification of AS 0 Processing */ + if (remote_as == BGP_AS_ZERO) { + flog_err(EC_BGP_PKT_OPEN, "%s bad OPEN, got AS set to 0", + peer->host); + bgp_notify_send(peer, BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_BAD_PEER_AS); + return BGP_Stop; + } + if (remote_as == BGP_AS_TRANS) { /* Take the AS4 from the capability. We must have received the * capability now! Otherwise we have a asn16 peer who uses @@ -1118,13 +1182,11 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size) if (!as4 && BGP_DEBUG(as4, AS4)) zlog_debug( - "%s [AS4] OPEN remote_as is AS_TRANS, but no AS4." - " Odd, but proceeding.", + "%s [AS4] OPEN remote_as is AS_TRANS, but no AS4. Odd, but proceeding.", peer->host); else if (as4 < BGP_AS_MAX && BGP_DEBUG(as4, AS4)) zlog_debug( - "%s [AS4] OPEN remote_as is AS_TRANS, but AS4 (%u) fits " - "in 2-bytes, very odd peer.", + "%s [AS4] OPEN remote_as is AS_TRANS, but AS4 (%u) fits in 2-bytes, very odd peer.", peer->host, as4); if (as4) remote_as = as4; @@ -1138,8 +1200,7 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size) /* raise error, log this, close session */ flog_err( EC_BGP_PKT_OPEN, - "%s bad OPEN, got AS4 capability, but remote_as %u" - " mismatch with 16bit 'myasn' %u in open", + "%s bad OPEN, got AS4 capability, but remote_as %u mismatch with 16bit 'myasn' %u in open", peer->host, as4, remote_as); bgp_notify_send_with_data(peer, BGP_NOTIFY_OPEN_ERR, BGP_NOTIFY_OPEN_BAD_PEER_AS, @@ -1148,9 +1209,16 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size) } } - /* remote router-id check. */ - if (remote_id.s_addr == 0 || IPV4_CLASS_DE(ntohl(remote_id.s_addr)) - || ntohl(peer->local_id.s_addr) == ntohl(remote_id.s_addr)) { + /* rfc6286: + * If the BGP Identifier field of the OPEN message + * is zero, or if it is the same as the BGP Identifier + * of the local BGP speaker and the message is from an + * internal peer, then the Error Subcode is set to + * "Bad BGP Identifier". + */ + if (remote_id.s_addr == INADDR_ANY + || (peer->sort == BGP_PEER_IBGP + && ntohl(peer->local_id.s_addr) == ntohl(remote_id.s_addr))) { if (bgp_debug_neighbor_events(peer)) zlog_debug("%s bad OPEN, wrong router identifier %s", peer->host, inet_ntoa(remote_id)); @@ -1252,16 +1320,22 @@ 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) { - if ((ret = bgp_open_option_parse(peer, optlen, &mp_capability)) - < 0) + if (bgp_open_option_parse(peer, optlen, &mp_capability) < 0) return BGP_Stop; } else { if (bgp_debug_neighbor_events(peer)) @@ -1296,17 +1370,18 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size) peer->afc[AFI_IP6][SAFI_FLOWSPEC]; } - /* When collision is detected and this peer is closed. Retrun - immidiately. */ + /* When collision is detected and this peer is closed. + * Return immediately. + */ ret = bgp_collision_detect(peer, remote_id); if (ret < 0) return BGP_Stop; /* Get sockname. */ - if ((ret = bgp_getsockname(peer)) < 0) { + if (bgp_getsockname(peer) < 0) { flog_err_sys(EC_LIB_SOCKET, "%s: bgp_getsockname() failed for peer: %s", - __FUNCTION__, peer->host); + __func__, peer->host); return BGP_Stop; } @@ -1317,7 +1392,7 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size) || peer->afc_nego[AFI_IP][SAFI_MULTICAST] || peer->afc_nego[AFI_IP][SAFI_MPLS_VPN] || peer->afc_nego[AFI_IP][SAFI_ENCAP]) { - if (!peer->nexthop.v4.s_addr) { + if (peer->nexthop.v4.s_addr == INADDR_ANY) { #if defined(HAVE_CUMULUS) flog_err( EC_BGP_SND_FAIL, @@ -1365,6 +1440,27 @@ static int bgp_keepalive_receive(struct peer *peer, bgp_size_t size) bgp_update_implicit_eors(peer); + peer->rtt = sockopt_tcp_rtt(peer->fd); + + /* If the peer's RTT is higher than expected, shutdown + * the peer automatically. + */ + if (CHECK_FLAG(peer->flags, PEER_FLAG_RTT_SHUTDOWN) + && peer->rtt > peer->rtt_expected) { + + peer->rtt_keepalive_rcv++; + + if (peer->rtt_keepalive_rcv > peer->rtt_keepalive_conf) { + zlog_warn( + "%s shutdown due to high round-trip-time (%dms > %dms)", + peer->host, peer->rtt, peer->rtt_expected); + peer_flag_set(peer, PEER_FLAG_SHUTDOWN); + } + } else { + if (peer->rtt_keepalive_rcv) + peer->rtt_keepalive_rcv--; + } + return Receive_KEEPALIVE_message; } @@ -1387,6 +1483,7 @@ static int bgp_update_receive(struct peer *peer, bgp_size_t size) bgp_size_t attribute_len; bgp_size_t update_len; bgp_size_t withdraw_len; + bool restart = false; enum NLRI_TYPES { NLRI_UPDATE, @@ -1403,7 +1500,8 @@ static int bgp_update_receive(struct peer *peer, bgp_size_t size) "%s [FSM] Update packet received under status %s", peer->host, lookup_msg(bgp_status_msg, peer->status, NULL)); - bgp_notify_send(peer, BGP_NOTIFY_FSM_ERR, 0); + bgp_notify_send(peer, BGP_NOTIFY_FSM_ERR, + bgp_fsm_error_subcode(peer->status)); return BGP_Stop; } @@ -1424,8 +1522,7 @@ static int bgp_update_receive(struct peer *peer, bgp_size_t size) Subcode is set to Malformed Attribute List. */ if (stream_pnt(s) + 2 > end) { flog_err(EC_BGP_UPDATE_RCV, - "%s [Error] Update packet error" - " (packet length is short for unfeasible length)", + "%s [Error] Update packet error (packet length is short for unfeasible length)", peer->host); bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR, BGP_NOTIFY_UPDATE_MAL_ATTR); @@ -1438,8 +1535,7 @@ static int bgp_update_receive(struct peer *peer, bgp_size_t size) /* Unfeasible Route Length check. */ if (stream_pnt(s) + withdraw_len > end) { flog_err(EC_BGP_UPDATE_RCV, - "%s [Error] Update packet error" - " (packet unfeasible length overflow %d)", + "%s [Error] Update packet error (packet unfeasible length overflow %d)", peer->host, withdraw_len); bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR, BGP_NOTIFY_UPDATE_MAL_ATTR); @@ -1508,7 +1604,10 @@ static int bgp_update_receive(struct peer *peer, bgp_size_t size) if (attr_parse_ret == BGP_ATTR_PARSE_WITHDRAW || BGP_DEBUG(update, UPDATE_IN) || BGP_DEBUG(update, UPDATE_PREFIX)) { - ret = bgp_dump_attr(&attr, peer->rcvd_attr_str, BUFSIZ); + ret = bgp_dump_attr(&attr, peer->rcvd_attr_str, + sizeof(peer->rcvd_attr_str)); + + peer->stat_upd_7606++; if (attr_parse_ret == BGP_ATTR_PARSE_WITHDRAW) flog_err( @@ -1533,6 +1632,17 @@ static int bgp_update_receive(struct peer *peer, bgp_size_t size) nlris[NLRI_UPDATE].nlri = stream_pnt(s); nlris[NLRI_UPDATE].length = update_len; stream_forward_getp(s, update_len); + + if (CHECK_FLAG(attr.flag, ATTR_FLAG_BIT(BGP_ATTR_MP_REACH_NLRI))) { + /* + * We skipped nexthop attribute validation earlier so + * validate the nexthop now. + */ + if (bgp_attr_nexthop_valid(peer, &attr) < 0) { + bgp_attr_unintern_sub(&attr); + return BGP_Stop; + } + } } if (BGP_DEBUG(update, UPDATE_IN)) @@ -1595,6 +1705,12 @@ static int bgp_update_receive(struct peer *peer, bgp_size_t size) || (attr_parse_ret == BGP_ATTR_PARSE_EOR)) { afi_t afi = 0; safi_t safi; + struct graceful_restart_info *gr_info; + + /* Restarting router */ + if (BGP_PEER_GRACEFUL_RESTART_CAPABLE(peer) + && BGP_PEER_RESTARTING_MODE(peer)) + restart = true; /* Non-MP IPv4/Unicast is a completely emtpy UPDATE - already * checked @@ -1621,16 +1737,44 @@ static int bgp_update_receive(struct peer *peer, bgp_size_t size) SET_FLAG(peer->af_sflags[afi][safi], PEER_STATUS_EOR_RECEIVED); bgp_update_explicit_eors(peer); + /* Update graceful restart information */ + gr_info = &(peer->bgp->gr_info[afi][safi]); + if (restart) + gr_info->eor_received++; + /* If EOR received from all peers and selection + * deferral timer is running, cancel the timer + * and invoke the best path calculation + */ + if (gr_info->eor_required + == gr_info->eor_received) { + if (bgp_debug_neighbor_events(peer)) + zlog_debug( + "%s %d, %s %d", + "EOR REQ", + gr_info->eor_required, + "EOR RCV", + gr_info->eor_received); + BGP_TIMER_OFF( + gr_info->t_select_deferral); + gr_info->eor_required = 0; + gr_info->eor_received = 0; + /* Best path selection */ + if (bgp_best_path_select_defer( + peer->bgp, afi, safi) + < 0) + return BGP_Stop; + } } /* NSF delete stale route */ if (peer->nsf[afi][safi]) bgp_clear_stale_route(peer, afi, safi); - zlog_info("%%NOTIFICATION: rcvd End-of-RIB for %s from %s in vrf %s", - afi_safi_print(afi, safi), peer->host, - vrf ? vrf->name : VRF_DEFAULT_NAME); - } + zlog_info( + "%s: rcvd End-of-RIB for %s from %s in vrf %s", + __func__, get_afi_safi_str(afi, safi, false), + peer->host, vrf ? vrf->name : VRF_DEFAULT_NAME); + } } /* Everything is done. We unintern temporary structures which @@ -1639,10 +1783,6 @@ static int bgp_update_receive(struct peer *peer, bgp_size_t size) peer->update_time = bgp_clock(); - /* Rearm holdtime timer */ - BGP_TIMER_OFF(peer->t_holdtime); - bgp_timer_set(peer); - return Receive_UPDATE_message; } @@ -1659,7 +1799,6 @@ static int bgp_notify_receive(struct peer *peer, bgp_size_t size) if (peer->notify.data) { XFREE(MTYPE_TMP, peer->notify.data); - peer->notify.data = NULL; peer->notify.length = 0; } @@ -1667,6 +1806,7 @@ static int bgp_notify_receive(struct peer *peer, bgp_size_t size) bgp_notify.subcode = stream_getc(peer->curr); bgp_notify.length = size - 2; bgp_notify.data = NULL; + bgp_notify.raw_data = NULL; /* Preserv notify code and sub code. */ peer->notify.code = bgp_notify.code; @@ -1689,14 +1829,19 @@ static int bgp_notify_receive(struct peer *peer, bgp_size_t size) XMALLOC(MTYPE_TMP, bgp_notify.length * 3); for (i = 0; i < bgp_notify.length; i++) if (first) { - sprintf(c, " %02x", + snprintf(c, sizeof(c), " %02x", stream_getc(peer->curr)); - strcat(bgp_notify.data, c); + + strlcat(bgp_notify.data, c, + bgp_notify.length * 3); + } else { first = 1; - sprintf(c, "%02x", - stream_getc(peer->curr)); - strcpy(bgp_notify.data, c); + snprintf(c, sizeof(c), "%02x", + stream_getc(peer->curr)); + + strlcpy(bgp_notify.data, c, + bgp_notify.length * 3); } bgp_notify.raw_data = (uint8_t *)peer->notify.data; } @@ -1704,7 +1849,6 @@ static int bgp_notify_receive(struct peer *peer, bgp_size_t size) bgp_notify_print(peer, &bgp_notify, "received"); if (bgp_notify.data) { XFREE(MTYPE_TMP, bgp_notify.data); - bgp_notify.data = NULL; bgp_notify.length = 0; } } @@ -1722,6 +1866,10 @@ static int bgp_notify_receive(struct peer *peer, bgp_size_t size) && bgp_notify.subcode == BGP_NOTIFY_OPEN_UNSUP_PARAM) UNSET_FLAG(peer->sflags, PEER_STATUS_CAPABILITY_OPEN); + bgp_peer_gr_flags_update(peer); + BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(peer->bgp, + peer->bgp->peer); + return Receive_NOTIFICATION_message; } @@ -1760,7 +1908,8 @@ static int bgp_route_refresh_receive(struct peer *peer, bgp_size_t size) "%s [Error] Route refresh packet received under status %s", peer->host, lookup_msg(bgp_status_msg, peer->status, NULL)); - bgp_notify_send(peer, BGP_NOTIFY_FSM_ERR, 0); + bgp_notify_send(peer, BGP_NOTIFY_FSM_ERR, + bgp_fsm_error_subcode(peer->status)); return BGP_Stop; } @@ -1772,14 +1921,16 @@ static int bgp_route_refresh_receive(struct peer *peer, bgp_size_t size) pkt_safi = stream_getc(s); if (bgp_debug_update(peer, NULL, NULL, 0)) - zlog_debug("%s rcvd REFRESH_REQ for afi/safi: %d/%d", - peer->host, pkt_afi, pkt_safi); + zlog_debug("%s rcvd REFRESH_REQ for afi/safi: %s/%s", + peer->host, iana_afi2str(pkt_afi), + iana_safi2str(pkt_safi)); /* Convert AFI, SAFI to internal values and check. */ if (bgp_map_afi_safi_iana2int(pkt_afi, pkt_safi, &afi, &safi)) { zlog_info( - "%s REFRESH_REQ for unrecognized afi/safi: %d/%d - ignored", - peer->host, pkt_afi, pkt_safi); + "%s REFRESH_REQ for unrecognized afi/safi: %s/%s - ignored", + peer->host, iana_afi2str(pkt_afi), + iana_safi2str(pkt_safi)); return BGP_PACKET_NOOP; } @@ -1793,7 +1944,8 @@ static int bgp_route_refresh_receive(struct peer *peer, bgp_size_t size) < 5) { zlog_info("%s ORF route refresh length error", peer->host); - bgp_notify_send(peer, BGP_NOTIFY_CEASE, 0); + bgp_notify_send(peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_SUBCODE_UNSPECIFIC); return BGP_Stop; } @@ -1833,8 +1985,8 @@ static int bgp_route_refresh_receive(struct peer *peer, bgp_size_t size) break; /* ORF prefix-list name */ - sprintf(name, "%s.%d.%d", peer->host, afi, - safi); + snprintf(name, sizeof(name), "%s.%d.%d", + peer->host, afi, safi); while (p_pnt < p_end) { /* If the ORF entry is malformed, want @@ -1869,38 +2021,29 @@ static int bgp_route_refresh_receive(struct peer *peer, bgp_size_t size) } else p_pnt = p_end; - if ((ok = (p_pnt < p_end))) - orfp.ge = - *p_pnt++; /* value - checked in - prefix_bgp_orf_set() - */ - if ((ok = (p_pnt < p_end))) - orfp.le = - *p_pnt++; /* value - checked in - prefix_bgp_orf_set() - */ + /* val checked in prefix_bgp_orf_set */ + if (p_pnt < p_end) + orfp.ge = *p_pnt++; + + /* val checked in prefix_bgp_orf_set */ + if (p_pnt < p_end) + orfp.le = *p_pnt++; + if ((ok = (p_pnt < p_end))) orfp.p.prefixlen = *p_pnt++; - orfp.p.family = afi2family( - afi); /* afi checked already */ - - psize = PSIZE( - orfp.p.prefixlen); /* 0 if not - ok */ - if (psize - > prefix_blen( - &orfp.p)) /* valid for - family ? */ - { + + /* afi checked already */ + orfp.p.family = afi2family(afi); + + /* 0 if not ok */ + psize = PSIZE(orfp.p.prefixlen); + /* valid for family ? */ + if (psize > prefix_blen(&orfp.p)) { ok = 0; psize = prefix_blen(&orfp.p); } - if (psize - > (p_end - p_pnt)) /* valid for - packet ? */ - { + /* valid for packet ? */ + if (psize > (p_end - p_pnt)) { ok = 0; psize = p_end - p_pnt; } @@ -1945,8 +2088,7 @@ static int bgp_route_refresh_receive(struct peer *peer, bgp_size_t size) if (!ok || (ok && ret != CMD_SUCCESS)) { zlog_info( - "%s Received misformatted prefixlist ORF." - " Remove All pfxlist", + "%s Received misformatted prefixlist ORF. Remove All pfxlist", peer->host); prefix_bgp_orf_remove_all(afi, name); @@ -2027,7 +2169,8 @@ static int bgp_capability_msg_parse(struct peer *peer, uint8_t *pnt, * length. */ if (pnt + 3 > end) { zlog_info("%s Capability length error", peer->host); - bgp_notify_send(peer, BGP_NOTIFY_CEASE, 0); + bgp_notify_send(peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_SUBCODE_UNSPECIFIC); return BGP_Stop; } action = *pnt; @@ -2038,7 +2181,8 @@ static int bgp_capability_msg_parse(struct peer *peer, uint8_t *pnt, && action != CAPABILITY_ACTION_UNSET) { zlog_info("%s Capability Action Value error %d", peer->host, action); - bgp_notify_send(peer, BGP_NOTIFY_CEASE, 0); + bgp_notify_send(peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_SUBCODE_UNSPECIFIC); return BGP_Stop; } @@ -2050,7 +2194,8 @@ static int bgp_capability_msg_parse(struct peer *peer, uint8_t *pnt, /* Capability length check. */ if ((pnt + hdr->length + 3) > end) { zlog_info("%s Capability length error", peer->host); - bgp_notify_send(peer, BGP_NOTIFY_CEASE, 0); + bgp_notify_send(peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_SUBCODE_UNSPECIFIC); return BGP_Stop; } @@ -2073,9 +2218,10 @@ static int bgp_capability_msg_parse(struct peer *peer, uint8_t *pnt, &safi)) { if (bgp_debug_neighbor_events(peer)) zlog_debug( - "%s Dynamic Capability MP_EXT afi/safi invalid " - "(%u/%u)", - peer->host, pkt_afi, pkt_safi); + "%s Dynamic Capability MP_EXT afi/safi invalid (%s/%s)", + peer->host, + iana_afi2str(pkt_afi), + iana_safi2str(pkt_safi)); continue; } @@ -2152,7 +2298,8 @@ int bgp_capability_receive(struct peer *peer, bgp_size_t size) "%s [Error] Dynamic capability packet received under status %s", peer->host, lookup_msg(bgp_status_msg, peer->status, NULL)); - bgp_notify_send(peer, BGP_NOTIFY_FSM_ERR, 0); + bgp_notify_send(peer, BGP_NOTIFY_FSM_ERR, + bgp_fsm_error_subcode(peer->status)); return BGP_Stop; } @@ -2198,11 +2345,9 @@ int bgp_process_packet(struct thread *thread) bgp_size_t size; char notify_data_length[2]; - pthread_mutex_lock(&peer->io_mtx); - { + frr_with_mutex(&peer->io_mtx) { peer->curr = stream_fifo_pop(peer->ibuf); } - pthread_mutex_unlock(&peer->io_mtx); if (peer->curr == NULL) // no packets to process, hmm... return 0; @@ -2215,8 +2360,7 @@ int bgp_process_packet(struct thread *thread) size = stream_getw(peer->curr); type = stream_getc(peer->curr); - /* BGP packet dump function. */ - bgp_dump_packet(peer, type, peer->curr); + hook_call(bgp_packet_dump, peer, type, size, peer->curr); /* adjust size to exclude the marker + length + type */ size -= BGP_HEADER_SIZE; @@ -2232,7 +2376,7 @@ int bgp_process_packet(struct thread *thread) flog_err( EC_BGP_PKT_OPEN, "%s: BGP OPEN receipt failed for peer: %s", - __FUNCTION__, peer->host); + __func__, peer->host); break; case BGP_MSG_UPDATE: atomic_fetch_add_explicit(&peer->update_in, 1, @@ -2243,7 +2387,7 @@ int bgp_process_packet(struct thread *thread) flog_err( EC_BGP_UPDATE_RCV, "%s: BGP UPDATE receipt failed for peer: %s", - __FUNCTION__, peer->host); + __func__, peer->host); break; case BGP_MSG_NOTIFY: atomic_fetch_add_explicit(&peer->notify_in, 1, @@ -2253,7 +2397,7 @@ int bgp_process_packet(struct thread *thread) flog_err( EC_BGP_NOTIFY_RCV, "%s: BGP NOTIFY receipt failed for peer: %s", - __FUNCTION__, peer->host); + __func__, peer->host); break; case BGP_MSG_KEEPALIVE: peer->readtime = monotime(NULL); @@ -2264,7 +2408,7 @@ int bgp_process_packet(struct thread *thread) flog_err( EC_BGP_KEEP_RCV, "%s: BGP KEEPALIVE receipt failed for peer: %s", - __FUNCTION__, peer->host); + __func__, peer->host); break; case BGP_MSG_ROUTE_REFRESH_NEW: case BGP_MSG_ROUTE_REFRESH_OLD: @@ -2275,7 +2419,7 @@ int bgp_process_packet(struct thread *thread) flog_err( EC_BGP_RFSH_RCV, "%s: BGP ROUTEREFRESH receipt failed for peer: %s", - __FUNCTION__, peer->host); + __func__, peer->host); break; case BGP_MSG_CAPABILITY: atomic_fetch_add_explicit(&peer->dynamic_cap_in, 1, @@ -2285,9 +2429,12 @@ int bgp_process_packet(struct thread *thread) flog_err( EC_BGP_CAP_RCV, "%s: BGP CAPABILITY receipt failed for peer: %s", - __FUNCTION__, peer->host); + __func__, peer->host); break; default: + /* Suppress uninitialized variable warning */ + mprc = 0; + (void)mprc; /* * The message type should have been sanitized before * we ever got here. Receipt of a message with an @@ -2319,16 +2466,59 @@ int bgp_process_packet(struct thread *thread) if (fsm_update_result != FSM_PEER_TRANSFERRED && fsm_update_result != FSM_PEER_STOPPED) { - pthread_mutex_lock(&peer->io_mtx); - { + frr_with_mutex(&peer->io_mtx) { // more work to do, come back later if (peer->ibuf->count > 0) thread_add_timer_msec( bm->master, bgp_process_packet, peer, 0, &peer->t_process_packet); } - pthread_mutex_unlock(&peer->io_mtx); } return 0; } + +/* Send EOR when routes are processed by selection deferral timer */ +void bgp_send_delayed_eor(struct bgp *bgp) +{ + struct peer *peer; + struct listnode *node, *nnode; + + /* EOR message sent in bgp_write_proceed_actions */ + for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) + bgp_write_proceed_actions(peer); +} + +/* + * Task callback to handle socket error encountered in the io pthread. We avoid + * having the io pthread try to enqueue fsm events or mess with the peer + * struct. + */ +int bgp_packet_process_error(struct thread *thread) +{ + struct peer *peer; + int code; + + peer = THREAD_ARG(thread); + code = THREAD_VAL(thread); + + if (bgp_debug_neighbor_events(peer)) + zlog_debug("%s [Event] BGP error %d on fd %d", + peer->host, peer->fd, code); + + /* Closed connection or error on the socket */ + if (peer->status == Established) { + if ((CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART) + || CHECK_FLAG(peer->flags, + PEER_FLAG_GRACEFUL_RESTART_HELPER)) + && CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_MODE)) { + peer->last_reset = PEER_DOWN_NSF_CLOSE_SESSION; + SET_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT); + } else + peer->last_reset = PEER_DOWN_CLOSE_SESSION; + } + + bgp_event_update(peer, code); + + return 0; +} diff --git a/bgpd/bgp_packet.h b/bgpd/bgp_packet.h index 06a190585b..48de9d9cbd 100644 --- a/bgpd/bgp_packet.h +++ b/bgpd/bgp_packet.h @@ -21,20 +21,40 @@ #ifndef _QUAGGA_BGP_PACKET_H #define _QUAGGA_BGP_PACKET_H +#include "hook.h" + +DECLARE_HOOK(bgp_packet_dump, + (struct peer *peer, uint8_t type, bgp_size_t size, + struct stream *s), + (peer, type, size, s)) + +DECLARE_HOOK(bgp_packet_send, + (struct peer *peer, uint8_t type, bgp_size_t size, + struct stream *s), + (peer, type, size, s)) + #define BGP_NLRI_LENGTH 1U #define BGP_TOTAL_ATTR_LEN 2U #define BGP_UNFEASIBLE_LEN 2U /* When to refresh */ #define REFRESH_IMMEDIATE 1 -#define REFRESH_DEFER 2 +#define REFRESH_DEFER 2 /* ORF Common part flag */ -#define ORF_COMMON_PART_ADD 0x00 -#define ORF_COMMON_PART_REMOVE 0x80 -#define ORF_COMMON_PART_REMOVE_ALL 0xC0 -#define ORF_COMMON_PART_PERMIT 0x00 -#define ORF_COMMON_PART_DENY 0x20 +#define ORF_COMMON_PART_ADD 0x00 +#define ORF_COMMON_PART_REMOVE 0x80 +#define ORF_COMMON_PART_REMOVE_ALL 0xC0 +#define ORF_COMMON_PART_PERMIT 0x00 +#define ORF_COMMON_PART_DENY 0x20 + +#define BGP_UPDATE_EOR_PKT(_peer, _afi, _safi, _s) \ + do { \ + _s = bgp_update_packet_eor(_peer, _afi, _safi); \ + if (_s) { \ + bgp_packet_add(_peer, _s); \ + } \ + } while (0) /* Packet send and receive function prototypes. */ extern void bgp_keepalive_send(struct peer *); @@ -45,9 +65,6 @@ extern void bgp_notify_send_with_data(struct peer *, uint8_t, uint8_t, extern void bgp_route_refresh_send(struct peer *, afi_t, safi_t, uint8_t, uint8_t, int); extern void bgp_capability_send(struct peer *, afi_t, safi_t, int, int); -extern void bgp_default_update_send(struct peer *, struct attr *, afi_t, safi_t, - struct peer *); -extern void bgp_default_withdraw_send(struct peer *, afi_t, safi_t); extern int bgp_capability_receive(struct peer *, bgp_size_t); @@ -64,4 +81,9 @@ extern int bgp_packet_set_size(struct stream *s); extern int bgp_generate_updgrp_packets(struct thread *); extern int bgp_process_packet(struct thread *); +extern void bgp_send_delayed_eor(struct bgp *bgp); + +/* Task callback to handle socket error encountered in the io pthread */ +int bgp_packet_process_error(struct thread *thread); + #endif /* _QUAGGA_BGP_PACKET_H */ diff --git a/bgpd/bgp_pbr.c b/bgpd/bgp_pbr.c index 0fddfa75a1..f6e5c196ca 100644 --- a/bgpd/bgp_pbr.c +++ b/bgpd/bgp_pbr.c @@ -42,6 +42,10 @@ DEFINE_MTYPE_STATIC(BGPD, PBR_RULE, "PBR rule") DEFINE_MTYPE_STATIC(BGPD, PBR, "BGP PBR Context") DEFINE_MTYPE_STATIC(BGPD, PBR_VALMASK, "BGP PBR Val Mask Value") +/* chain strings too long to fit in one line */ +#define FSPEC_ACTION_EXCEED_LIMIT "flowspec actions exceeds limit" +#define IPV6_FRAGMENT_INVALID "fragment not valid for IPv6 for this implementation" + RB_GENERATE(bgp_pbr_interface_head, bgp_pbr_interface, id_entry, bgp_pbr_interface_compare); struct bgp_pbr_interface_head ifaces_by_name_ipv4 = @@ -168,35 +172,62 @@ static int bgp_pbr_match_walkcb(struct hash_bucket *bucket, void *arg) return HASHWALK_CONTINUE; } -static int sprintf_bgp_pbr_match_val(char *str, struct bgp_pbr_match_val *mval, - const char *prepend) +static int snprintf_bgp_pbr_match_val(char *str, int len, + struct bgp_pbr_match_val *mval, + const char *prepend) { char *ptr = str; + int delta; - if (prepend) - ptr += sprintf(ptr, "%s", prepend); - else { - if (mval->unary_operator & OPERATOR_UNARY_OR) - ptr += sprintf(ptr, ", or "); - if (mval->unary_operator & OPERATOR_UNARY_AND) - ptr += sprintf(ptr, ", and "); - } - if (mval->compare_operator & OPERATOR_COMPARE_LESS_THAN) - ptr += sprintf(ptr, "<"); - if (mval->compare_operator & OPERATOR_COMPARE_GREATER_THAN) - ptr += sprintf(ptr, ">"); - if (mval->compare_operator & OPERATOR_COMPARE_EQUAL_TO) - ptr += sprintf(ptr, "="); - if (mval->compare_operator & OPERATOR_COMPARE_EXACT_MATCH) - ptr += sprintf(ptr, "match"); - ptr += sprintf(ptr, " %u", mval->value); + if (prepend) { + delta = snprintf(ptr, len, "%s", prepend); + ptr += delta; + len -= delta; + } else { + if (mval->unary_operator & OPERATOR_UNARY_OR) { + delta = snprintf(ptr, len, ", or "); + ptr += delta; + len -= delta; + } + if (mval->unary_operator & OPERATOR_UNARY_AND) { + delta = snprintf(ptr, len, ", and "); + ptr += delta; + len -= delta; + } + } + if (mval->compare_operator & OPERATOR_COMPARE_LESS_THAN) { + delta = snprintf(ptr, len, "<"); + ptr += delta; + len -= delta; + } + if (mval->compare_operator & OPERATOR_COMPARE_GREATER_THAN) { + delta = snprintf(ptr, len, ">"); + ptr += delta; + len -= delta; + } + if (mval->compare_operator & OPERATOR_COMPARE_EQUAL_TO) { + delta = snprintf(ptr, len, "="); + ptr += delta; + len -= delta; + } + if (mval->compare_operator & OPERATOR_COMPARE_EXACT_MATCH) { + delta = snprintf(ptr, len, "match"); + ptr += delta; + len -= delta; + } + ptr += snprintf(ptr, len, " %u", mval->value); return (int)(ptr - str); } -#define INCREMENT_DISPLAY(_ptr, _cnt) do { \ - if (_cnt) \ - (_ptr) += sprintf((_ptr), "; "); \ - _cnt++; \ +#define INCREMENT_DISPLAY(_ptr, _cnt, _len) do { \ + int sn_delta; \ + \ + if (_cnt) { \ + sn_delta = snprintf((_ptr), (_len), "; ");\ + (_len) -= sn_delta; \ + (_ptr) += sn_delta; \ + } \ + (_cnt)++; \ } while (0) /* this structure can be used for port range, @@ -222,6 +253,7 @@ struct bgp_pbr_val_mask { struct bgp_pbr_filter { uint8_t type; vrf_id_t vrf_id; + uint8_t family; struct prefix *src; struct prefix *dst; uint8_t bitmask_iprule; @@ -231,6 +263,7 @@ struct bgp_pbr_filter { struct bgp_pbr_range_port *dst_port; struct bgp_pbr_val_mask *tcp_flags; struct bgp_pbr_val_mask *dscp; + struct bgp_pbr_val_mask *flow_label; struct bgp_pbr_val_mask *pkt_len_val; struct bgp_pbr_val_mask *fragment; }; @@ -242,6 +275,7 @@ struct bgp_pbr_filter { struct bgp_pbr_or_filter { struct list *tcpflags; struct list *dscp; + struct list *flowlabel; struct list *pkt_len; struct list *fragment; struct list *icmp_type; @@ -268,6 +302,7 @@ static bool bgp_pbr_extract_enumerate_unary_opposite( TCP_HEADER_ALL_FLAGS & ~(value); } else if (type_entry == FLOWSPEC_DSCP || + type_entry == FLOWSPEC_FLOW_LABEL || type_entry == FLOWSPEC_PKT_LEN || type_entry == FLOWSPEC_FRAGMENT) { and_valmask->val = value; @@ -282,6 +317,7 @@ static bool bgp_pbr_extract_enumerate_unary_opposite( TCP_HEADER_ALL_FLAGS & ~(value); } else if (type_entry == FLOWSPEC_DSCP || + type_entry == FLOWSPEC_FLOW_LABEL || type_entry == FLOWSPEC_FRAGMENT || type_entry == FLOWSPEC_PKT_LEN) { and_valmask->val = value; @@ -296,7 +332,7 @@ static bool bgp_pbr_extract_enumerate_unary_opposite( /* TCP : FIN and SYN -> val = ALL; mask = 3 * TCP : not (FIN and SYN) -> val = ALL; mask = ALL & ~(FIN|RST) - * other variables type: dscp, pkt len, fragment + * other variables type: dscp, pkt len, fragment, flow label * - value is copied in bgp_pbr_val_mask->val value * - if negate form is identifierd, bgp_pbr_val_mask->mask set to 1 */ @@ -351,6 +387,7 @@ static bool bgp_pbr_extract_enumerate_unary(struct bgp_pbr_match_val list[], and_valmask->mask |= TCP_HEADER_ALL_FLAGS & list[i].value; } else if (type_entry == FLOWSPEC_DSCP || + type_entry == FLOWSPEC_FLOW_LABEL || type_entry == FLOWSPEC_ICMP_TYPE || type_entry == FLOWSPEC_ICMP_CODE || type_entry == FLOWSPEC_FRAGMENT || @@ -486,31 +523,35 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api) */ if (api->match_protocol_num > 1) { if (BGP_DEBUG(pbr, PBR)) - zlog_debug("BGP: match protocol operations:" - "multiple protocols ( %d). ignoring.", + zlog_debug("BGP: match protocol operations:multiple protocols ( %d). ignoring.", api->match_protocol_num); return 0; } + if (api->src_prefix_offset > 0 || + api->dst_prefix_offset > 0) { + if (BGP_DEBUG(pbr, PBR)) + zlog_debug("BGP: match prefix offset:" + "implementation does not support it."); + return 0; + } if (api->match_protocol_num == 1 && api->protocol[0].value != PROTOCOL_UDP && api->protocol[0].value != PROTOCOL_ICMP && + api->protocol[0].value != PROTOCOL_ICMPV6 && api->protocol[0].value != PROTOCOL_TCP) { if (BGP_DEBUG(pbr, PBR)) - zlog_debug("BGP: match protocol operations:" - "protocol (%d) not supported. ignoring", + zlog_debug("BGP: match protocol operations:protocol (%d) not supported. ignoring", api->match_protocol_num); return 0; } if (!bgp_pbr_extract(api->src_port, api->match_src_port_num, NULL)) { if (BGP_DEBUG(pbr, PBR)) - zlog_debug("BGP: match src port operations:" - "too complex. ignoring."); + zlog_debug("BGP: match src port operations:too complex. ignoring."); return 0; } if (!bgp_pbr_extract(api->dst_port, api->match_dst_port_num, NULL)) { if (BGP_DEBUG(pbr, PBR)) - zlog_debug("BGP: match dst port operations:" - "too complex. ignoring."); + zlog_debug("BGP: match dst port operations:too complex. ignoring."); return 0; } if (!bgp_pbr_extract_enumerate(api->tcpflags, @@ -519,8 +560,7 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api) OPERATOR_UNARY_OR, NULL, FLOWSPEC_TCP_FLAGS)) { if (BGP_DEBUG(pbr, PBR)) - zlog_debug("BGP: match tcp flags:" - "too complex. ignoring."); + zlog_debug("BGP: match tcp flags:too complex. ignoring."); return 0; } if (!bgp_pbr_extract(api->icmp_type, api->match_icmp_type_num, NULL)) { @@ -529,8 +569,7 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api) OPERATOR_UNARY_OR, NULL, FLOWSPEC_ICMP_TYPE)) { if (BGP_DEBUG(pbr, PBR)) - zlog_debug("BGP: match icmp type operations:" - "too complex. ignoring."); + zlog_debug("BGP: match icmp type operations:too complex. ignoring."); return 0; } enumerate_icmp = true; @@ -541,22 +580,18 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api) OPERATOR_UNARY_OR, NULL, FLOWSPEC_ICMP_CODE)) { if (BGP_DEBUG(pbr, PBR)) - zlog_debug("BGP: match icmp code operations:" - "too complex. ignoring."); + zlog_debug("BGP: match icmp code operations:too complex. ignoring."); return 0; } else if (api->match_icmp_type_num > 1 && !enumerate_icmp) { if (BGP_DEBUG(pbr, PBR)) - zlog_debug("BGP: match icmp code is enumerate" - ", and icmp type is not." - " too complex. ignoring."); + zlog_debug("BGP: match icmp code is enumerate, and icmp type is not. too complex. ignoring."); return 0; } } if (!bgp_pbr_extract(api->port, api->match_port_num, NULL)) { if (BGP_DEBUG(pbr, PBR)) - zlog_debug("BGP: match port operations:" - "too complex. ignoring."); + zlog_debug("BGP: match port operations:too complex. ignoring."); return 0; } if (api->match_packet_length_num) { @@ -572,8 +607,7 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api) NULL, FLOWSPEC_PKT_LEN); if (!ret) { if (BGP_DEBUG(pbr, PBR)) - zlog_debug("BGP: match packet length operations:" - "too complex. ignoring."); + zlog_debug("BGP: match packet length operations:too complex. ignoring."); return 0; } } @@ -582,10 +616,30 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api) OPERATOR_UNARY_OR | OPERATOR_UNARY_AND, NULL, FLOWSPEC_DSCP)) { if (BGP_DEBUG(pbr, PBR)) - zlog_debug("BGP: match DSCP operations:" + zlog_debug("BGP: match DSCP operations:too complex. ignoring."); + return 0; + } + } + if (api->match_flowlabel_num) { + if (api->afi == AFI_IP) { + if (BGP_DEBUG(pbr, PBR)) + zlog_debug("BGP: match Flow Label operations:" + "Not for IPv4."); + return 0; + } + if (!bgp_pbr_extract_enumerate(api->flow_label, + api->match_flowlabel_num, + OPERATOR_UNARY_OR | OPERATOR_UNARY_AND, + NULL, FLOWSPEC_FLOW_LABEL)) { + if (BGP_DEBUG(pbr, PBR)) + zlog_debug("BGP: match FlowLabel operations:" "too complex. ignoring."); return 0; } + if (BGP_DEBUG(pbr, PBR)) + zlog_debug("BGP: match FlowLabel operations " + "not supported. ignoring."); + return 0; } if (api->match_fragment_num) { char fail_str[64]; @@ -605,13 +659,27 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api) api->fragment[i].value != 4 && api->fragment[i].value != 8) { success = false; - sprintf(fail_str, + snprintf( + fail_str, sizeof(fail_str), "Value not valid (%d) for this implementation", api->fragment[i].value); } + if (api->afi == AFI_IP6 && + api->fragment[i].value == 1) { + success = false; + snprintf(fail_str, sizeof(fail_str), + "IPv6 dont fragment match invalid (%d)", + api->fragment[i].value); + } + } + if (api->afi == AFI_IP6) { + success = false; + snprintf(fail_str, sizeof(fail_str), + "%s", IPV6_FRAGMENT_INVALID); } } else - sprintf(fail_str, "too complex. ignoring"); + snprintf(fail_str, sizeof(fail_str), + "too complex. ignoring"); if (!success) { if (BGP_DEBUG(pbr, PBR)) zlog_debug("BGP: match fragment operation (%d) %s", @@ -627,16 +695,14 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api) if (api->match_src_port_num + api->match_dst_port_num + api->match_port_num > 3) { if (BGP_DEBUG(pbr, PBR)) - zlog_debug("BGP: match multiple port operations:" - " too complex. ignoring."); + zlog_debug("BGP: match multiple port operations: too complex. ignoring."); return 0; } if ((api->match_src_port_num || api->match_dst_port_num || api->match_port_num) && (api->match_icmp_type_num || api->match_icmp_code_num)) { if (BGP_DEBUG(pbr, PBR)) - zlog_debug("BGP: match multiple port/imcp operations:" - " too complex. ignoring."); + zlog_debug("BGP: match multiple port/imcp operations: too complex. ignoring."); return 0; } /* iprule only supports redirect IP */ @@ -648,24 +714,21 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api) api->actions[i].u.r.rate == 0) { if (BGP_DEBUG(pbr, PBR)) { bgp_pbr_print_policy_route(api); - zlog_debug("BGP: iprule match actions" - " drop not supported"); + zlog_debug("BGP: iprule match actions drop not supported"); } return 0; } if (api->actions[i].action == ACTION_MARKING) { if (BGP_DEBUG(pbr, PBR)) { bgp_pbr_print_policy_route(api); - zlog_warn("PBR: iprule set DSCP %u" - " not supported", + zlog_warn("PBR: iprule set DSCP/Flow Label %u not supported", api->actions[i].u.marking_dscp); } } if (api->actions[i].action == ACTION_REDIRECT) { if (BGP_DEBUG(pbr, PBR)) { bgp_pbr_print_policy_route(api); - zlog_warn("PBR: iprule redirect VRF %u" - " not supported", + zlog_warn("PBR: iprule redirect VRF %u not supported", api->actions[i].u.redirect_vrf); } } @@ -675,9 +738,7 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api) !(api->match_bitmask & PREFIX_DST_PRESENT)) { if (BGP_DEBUG(pbr, PBR)) { bgp_pbr_print_policy_route(api); - zlog_debug("BGP: match actions without src" - " or dst address can not operate." - " ignoring."); + zlog_debug("BGP: match actions without src or dst address can not operate. ignoring."); } return 0; } @@ -685,9 +746,10 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api) } /* return -1 if build or validation failed */ -int bgp_pbr_build_and_validate_entry(struct prefix *p, - struct bgp_path_info *path, - struct bgp_pbr_entry_main *api) + +int bgp_pbr_build_and_validate_entry(const struct prefix *p, + struct bgp_path_info *path, + struct bgp_pbr_entry_main *api) { int ret; int i, action_count = 0; @@ -696,16 +758,17 @@ int bgp_pbr_build_and_validate_entry(struct prefix *p, struct bgp_pbr_entry_action *api_action; struct prefix *src = NULL, *dst = NULL; int valid_prefix = 0; - afi_t afi = AFI_IP; struct bgp_pbr_entry_action *api_action_redirect_ip = NULL; + bool discard_action_found = false; + afi_t afi = family2afi(p->u.prefix_flowspec.family); /* extract match from flowspec entries */ ret = bgp_flowspec_match_rules_fill((uint8_t *)p->u.prefix_flowspec.ptr, - p->u.prefix_flowspec.prefixlen, api); + p->u.prefix_flowspec.prefixlen, api, afi); if (ret < 0) return -1; /* extract actiosn from flowspec ecom list */ - if (path && path->attr && path->attr->ecommunity) { + if (path && path->attr->ecommunity) { ecom = path->attr->ecommunity; for (i = 0; i < ecom->size; i++) { ecom_eval = (struct ecommunity_val *) @@ -715,8 +778,10 @@ int bgp_pbr_build_and_validate_entry(struct prefix *p, if (BGP_DEBUG(pbr, PBR_ERROR)) flog_err( EC_BGP_FLOWSPEC_PACKET, - "%s: flowspec actions exceeds limit (max %u)", - __func__, action_count); + "%s: %s (max %u)", + __func__, + FSPEC_ACTION_EXCEED_LIMIT, + action_count); break; } api_action = &api->actions[action_count - 1]; @@ -737,7 +802,8 @@ int bgp_pbr_build_and_validate_entry(struct prefix *p, ecom_copy.val[0] &= ~ECOMMUNITY_ENCODE_TRANS_EXP; ecom_copy.val[1] = ECOMMUNITY_ROUTE_TARGET; - ecommunity_add_val(eckey, &ecom_copy); + ecommunity_add_val(eckey, &ecom_copy, + false, false); api_action->action = ACTION_REDIRECT; api_action->u.redirect_vrf = @@ -752,25 +818,59 @@ int bgp_pbr_build_and_validate_entry(struct prefix *p, * do not overwrite * draft-ietf-idr-flowspec-redirect */ - if (api_action_redirect_ip) { + if (api_action_redirect_ip && + p->u.prefix_flowspec.family == AF_INET) { if (api_action_redirect_ip->u - .zr.redirect_ip_v4.s_addr) + .zr.redirect_ip_v4.s_addr + != INADDR_ANY) continue; - if (!path->attr->nexthop.s_addr) + if (path->attr->nexthop.s_addr + == INADDR_ANY) continue; - api_action_redirect_ip->u - .zr.redirect_ip_v4.s_addr = + api_action_redirect_ip->u.zr + .redirect_ip_v4.s_addr = path->attr->nexthop.s_addr; api_action_redirect_ip->u.zr.duplicate = ecom_eval->val[7]; continue; - } else { + } else if (api_action_redirect_ip && + p->u.prefix_flowspec.family == AF_INET6) { + if (memcmp(&api_action_redirect_ip->u + .zr.redirect_ip_v6, + &in6addr_any, + sizeof(struct in6_addr))) + continue; + if (path->attr->mp_nexthop_len == 0 || + path->attr->mp_nexthop_len == + BGP_ATTR_NHLEN_IPV4 || + path->attr->mp_nexthop_len == + BGP_ATTR_NHLEN_VPNV4) + continue; + memcpy(&api_action_redirect_ip->u + .zr.redirect_ip_v6, + &path->attr->mp_nexthop_global, + sizeof(struct in6_addr)); + api_action_redirect_ip->u.zr.duplicate + = ecom_eval->val[7]; + continue; + } else if (p->u.prefix_flowspec.family == + AF_INET) { api_action->action = ACTION_REDIRECT_IP; api_action->u.zr.redirect_ip_v4.s_addr = path->attr->nexthop.s_addr; api_action->u.zr.duplicate = ecom_eval->val[7]; api_action_redirect_ip = api_action; + } else if (p->u.prefix_flowspec.family == + AF_INET6) { + api_action->action = ACTION_REDIRECT_IP; + memcpy(&api_action->u + .zr.redirect_ip_v6, + &path->attr->mp_nexthop_global, + sizeof(struct in6_addr)); + api_action->u.zr.duplicate + = ecom_eval->val[7]; + api_action_redirect_ip = api_action; } } else if ((ecom_eval->val[0] == (char)ECOMMUNITY_ENCODE_IP) && @@ -802,13 +902,65 @@ int bgp_pbr_build_and_validate_entry(struct prefix *p, (char)ECOMMUNITY_ENCODE_TRANS_EXP) continue; ret = ecommunity_fill_pbr_action(ecom_eval, - api_action); + api_action, + afi); if (ret != 0) continue; + if ((api_action->action == ACTION_TRAFFICRATE) + && api->actions[i].u.r.rate == 0) + discard_action_found = true; } api->action_num++; } } + if (path && path->attr && path->attr->ipv6_ecommunity) { + struct ecommunity_val_ipv6 *ipv6_ecom_eval; + + ecom = path->attr->ipv6_ecommunity; + for (i = 0; i < ecom->size; i++) { + ipv6_ecom_eval = (struct ecommunity_val_ipv6 *) + (ecom->val + (i * ecom->unit_size)); + action_count++; + if (action_count > ACTIONS_MAX_NUM) { + if (BGP_DEBUG(pbr, PBR_ERROR)) + flog_err( + EC_BGP_FLOWSPEC_PACKET, + "%s: flowspec actions exceeds limit (max %u)", + __func__, action_count); + break; + } + api_action = &api->actions[action_count - 1]; + if ((ipv6_ecom_eval->val[1] == + (char)ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6) && + (ipv6_ecom_eval->val[0] == + (char)ECOMMUNITY_ENCODE_TRANS_EXP)) { + struct ecommunity *eckey = ecommunity_new(); + struct ecommunity_val_ipv6 ecom_copy; + + eckey->unit_size = IPV6_ECOMMUNITY_SIZE; + memcpy(&ecom_copy, ipv6_ecom_eval, + sizeof(struct ecommunity_val_ipv6)); + ecom_copy.val[1] = ECOMMUNITY_ROUTE_TARGET; + ecommunity_add_val_ipv6(eckey, &ecom_copy, + false, false); + api_action->action = ACTION_REDIRECT; + api_action->u.redirect_vrf = + get_first_vrf_for_redirect_with_rt( + eckey); + ecommunity_free(&eckey); + api->action_num++; + } + } + } + /* if ECOMMUNITY_TRAFFIC_RATE = 0 as action + * then reduce the API action list to that action + */ + if (api->action_num > 1 && discard_action_found) { + api->action_num = 1; + memset(&api->actions[0], 0, + sizeof(struct bgp_pbr_entry_action)); + api->actions[0].action = ACTION_TRAFFICRATE; + } /* validate if incoming matc/action is compatible * with our policy routing engine @@ -827,8 +979,7 @@ int bgp_pbr_build_and_validate_entry(struct prefix *p, if (valid_prefix && afi != family2afi(dst->family)) { if (BGP_DEBUG(pbr, PBR)) { bgp_pbr_print_policy_route(api); - zlog_debug("%s: inconsistency:" - " no match for afi src and dst (%u/%u)", + zlog_debug("%s: inconsistency: no match for afi src and dst (%u/%u)", __func__, afi, family2afi(dst->family)); } return -1; @@ -964,19 +1115,22 @@ static void *bgp_pbr_match_entry_alloc_intern(void *arg) return new; } -uint32_t bgp_pbr_match_hash_key(void *arg) +uint32_t bgp_pbr_match_hash_key(const void *arg) { - struct bgp_pbr_match *pbm = (struct bgp_pbr_match *)arg; + const struct bgp_pbr_match *pbm = arg; uint32_t key; key = jhash_1word(pbm->vrf_id, 0x4312abde); key = jhash_1word(pbm->flags, key); + key = jhash_1word(pbm->family, key); key = jhash(&pbm->pkt_len_min, 2, key); key = jhash(&pbm->pkt_len_max, 2, key); key = jhash(&pbm->tcp_flags, 2, key); key = jhash(&pbm->tcp_mask_flags, 2, key); key = jhash(&pbm->dscp_value, 1, key); + key = jhash(&pbm->flow_label, 2, key); key = jhash(&pbm->fragment, 1, key); + key = jhash(&pbm->protocol, 1, key); return jhash_1word(pbm->type, key); } @@ -990,6 +1144,9 @@ bool bgp_pbr_match_hash_equal(const void *arg1, const void *arg2) if (r1->vrf_id != r2->vrf_id) return false; + if (r1->family != r2->family) + return false; + if (r1->type != r2->type) return false; @@ -1014,14 +1171,20 @@ bool bgp_pbr_match_hash_equal(const void *arg1, const void *arg2) if (r1->dscp_value != r2->dscp_value) return false; + if (r1->flow_label != r2->flow_label) + return false; + if (r1->fragment != r2->fragment) return false; + + if (r1->protocol != r2->protocol) + return false; return true; } -uint32_t bgp_pbr_rule_hash_key(void *arg) +uint32_t bgp_pbr_rule_hash_key(const void *arg) { - struct bgp_pbr_rule *pbr = (struct bgp_pbr_rule *)arg; + const struct bgp_pbr_rule *pbr = arg; uint32_t key; key = prefix_hash_key(&pbr->src); @@ -1057,12 +1220,12 @@ bool bgp_pbr_rule_hash_equal(const void *arg1, const void *arg2) return true; } -uint32_t bgp_pbr_match_entry_hash_key(void *arg) +uint32_t bgp_pbr_match_entry_hash_key(const void *arg) { - struct bgp_pbr_match_entry *pbme; + const struct bgp_pbr_match_entry *pbme; uint32_t key; - pbme = (struct bgp_pbr_match_entry *)arg; + pbme = arg; key = prefix_hash_key(&pbme->src); key = jhash_1word(prefix_hash_key(&pbme->dst), key); key = jhash(&pbme->dst_port_min, 2, key); @@ -1111,14 +1274,15 @@ bool bgp_pbr_match_entry_hash_equal(const void *arg1, const void *arg2) return true; } -uint32_t bgp_pbr_action_hash_key(void *arg) +uint32_t bgp_pbr_action_hash_key(const void *arg) { - struct bgp_pbr_action *pbra; + const struct bgp_pbr_action *pbra; uint32_t key; - pbra = (struct bgp_pbr_action *)arg; + pbra = arg; key = jhash_1word(pbra->table_id, 0x4312abde); key = jhash_1word(pbra->fwmark, key); + key = jhash_1word(pbra->afi, key); return key; } @@ -1136,6 +1300,9 @@ bool bgp_pbr_action_hash_equal(const void *arg1, const void *arg2) if (r1->vrf_id != r2->vrf_id) return false; + if (r1->afi != r2->afi) + return false; + if (memcmp(&r1->nh, &r2->nh, sizeof(struct nexthop))) return false; @@ -1242,8 +1409,8 @@ void bgp_pbr_cleanup(struct bgp *bgp) if (bgp->bgp_pbr_cfg == NULL) return; bgp_pbr_reset(bgp, AFI_IP); + bgp_pbr_reset(bgp, AFI_IP6); XFREE(MTYPE_PBR, bgp->bgp_pbr_cfg); - bgp->bgp_pbr_cfg = NULL; } void bgp_pbr_init(struct bgp *bgp) @@ -1273,127 +1440,227 @@ void bgp_pbr_print_policy_route(struct bgp_pbr_entry_main *api) char *ptr = return_string; char buff[64]; int nb_items = 0; + int delta, len = sizeof(return_string); - ptr += sprintf(ptr, "MATCH : "); + delta = snprintf(ptr, sizeof(return_string), "MATCH : "); + len -= delta; + ptr += delta; if (api->match_bitmask & PREFIX_SRC_PRESENT) { struct prefix *p = &(api->src_prefix); - ptr += sprintf(ptr, "@src %s", prefix2str(p, buff, 64)); - INCREMENT_DISPLAY(ptr, nb_items); + if (api->src_prefix_offset) + delta = snprintf(ptr, len, "@src %s/off%u", + prefix2str(p, buff, 64), + api->src_prefix_offset); + else + delta = snprintf(ptr, len, "@src %s", + prefix2str(p, buff, 64)); + len -= delta; + ptr += delta; + INCREMENT_DISPLAY(ptr, nb_items, len); } if (api->match_bitmask & PREFIX_DST_PRESENT) { struct prefix *p = &(api->dst_prefix); - INCREMENT_DISPLAY(ptr, nb_items); - ptr += sprintf(ptr, "@dst %s", prefix2str(p, buff, 64)); + INCREMENT_DISPLAY(ptr, nb_items, len); + if (api->dst_prefix_offset) + delta = snprintf(ptr, len, "@dst %s/off%u", + prefix2str(p, buff, 64), + api->dst_prefix_offset); + else + delta = snprintf(ptr, len, "@dst %s", + prefix2str(p, buff, 64)); + len -= delta; + ptr += delta; } if (api->match_protocol_num) - INCREMENT_DISPLAY(ptr, nb_items); - for (i = 0; i < api->match_protocol_num; i++) - ptr += sprintf_bgp_pbr_match_val(ptr, &api->protocol[i], - i > 0 ? NULL : "@proto "); + INCREMENT_DISPLAY(ptr, nb_items, len); + for (i = 0; i < api->match_protocol_num; i++) { + delta = snprintf_bgp_pbr_match_val(ptr, len, &api->protocol[i], + i > 0 ? NULL : "@proto "); + len -= delta; + ptr += delta; + } if (api->match_src_port_num) - INCREMENT_DISPLAY(ptr, nb_items); - for (i = 0; i < api->match_src_port_num; i++) - ptr += sprintf_bgp_pbr_match_val(ptr, &api->src_port[i], - i > 0 ? NULL : "@srcport "); + INCREMENT_DISPLAY(ptr, nb_items, len); + for (i = 0; i < api->match_src_port_num; i++) { + delta = snprintf_bgp_pbr_match_val(ptr, len, &api->src_port[i], + i > 0 ? NULL : "@srcport "); + len -= delta; + ptr += delta; + } if (api->match_dst_port_num) - INCREMENT_DISPLAY(ptr, nb_items); - for (i = 0; i < api->match_dst_port_num; i++) - ptr += sprintf_bgp_pbr_match_val(ptr, &api->dst_port[i], - i > 0 ? NULL : "@dstport "); + INCREMENT_DISPLAY(ptr, nb_items, len); + for (i = 0; i < api->match_dst_port_num; i++) { + delta = snprintf_bgp_pbr_match_val(ptr, len, &api->dst_port[i], + i > 0 ? NULL : "@dstport "); + len -= delta; + ptr += delta; + } if (api->match_port_num) - INCREMENT_DISPLAY(ptr, nb_items); - for (i = 0; i < api->match_port_num; i++) - ptr += sprintf_bgp_pbr_match_val(ptr, &api->port[i], - i > 0 ? NULL : "@port "); + INCREMENT_DISPLAY(ptr, nb_items, len); + for (i = 0; i < api->match_port_num; i++) { + delta = snprintf_bgp_pbr_match_val(ptr, len, &api->port[i], + i > 0 ? NULL : "@port "); + len -= delta; + ptr += delta; + } if (api->match_icmp_type_num) - INCREMENT_DISPLAY(ptr, nb_items); - for (i = 0; i < api->match_icmp_type_num; i++) - ptr += sprintf_bgp_pbr_match_val(ptr, &api->icmp_type[i], - i > 0 ? NULL : "@icmptype "); + INCREMENT_DISPLAY(ptr, nb_items, len); + for (i = 0; i < api->match_icmp_type_num; i++) { + delta = snprintf_bgp_pbr_match_val(ptr, len, &api->icmp_type[i], + i > 0 ? NULL : "@icmptype "); + len -= delta; + ptr += delta; + } if (api->match_icmp_code_num) - INCREMENT_DISPLAY(ptr, nb_items); - for (i = 0; i < api->match_icmp_code_num; i++) - ptr += sprintf_bgp_pbr_match_val(ptr, &api->icmp_code[i], - i > 0 ? NULL : "@icmpcode "); + INCREMENT_DISPLAY(ptr, nb_items, len); + for (i = 0; i < api->match_icmp_code_num; i++) { + delta = snprintf_bgp_pbr_match_val(ptr, len, &api->icmp_code[i], + i > 0 ? NULL : "@icmpcode "); + len -= delta; + ptr += delta; + } if (api->match_packet_length_num) - INCREMENT_DISPLAY(ptr, nb_items); - for (i = 0; i < api->match_packet_length_num; i++) - ptr += sprintf_bgp_pbr_match_val(ptr, &api->packet_length[i], - i > 0 ? NULL : "@plen "); + INCREMENT_DISPLAY(ptr, nb_items, len); + for (i = 0; i < api->match_packet_length_num; i++) { + delta = snprintf_bgp_pbr_match_val(ptr, len, + &api->packet_length[i], + i > 0 ? NULL : "@plen "); + len -= delta; + ptr += delta; + } if (api->match_dscp_num) - INCREMENT_DISPLAY(ptr, nb_items); - for (i = 0; i < api->match_dscp_num; i++) - ptr += sprintf_bgp_pbr_match_val(ptr, &api->dscp[i], - i > 0 ? NULL : "@dscp "); + INCREMENT_DISPLAY(ptr, nb_items, len); + for (i = 0; i < api->match_dscp_num; i++) { + delta = snprintf_bgp_pbr_match_val(ptr, len, &api->dscp[i], + i > 0 ? NULL : "@dscp "); + len -= delta; + ptr += delta; + } + + if (api->match_flowlabel_num) + INCREMENT_DISPLAY(ptr, nb_items, len); + for (i = 0; i < api->match_flowlabel_num; i++) { + delta = snprintf_bgp_pbr_match_val(ptr, len, + &api->flow_label[i], + i > 0 ? NULL : "@flowlabel "); + len -= delta; + ptr += delta; + } if (api->match_tcpflags_num) - INCREMENT_DISPLAY(ptr, nb_items); - for (i = 0; i < api->match_tcpflags_num; i++) - ptr += sprintf_bgp_pbr_match_val(ptr, &api->tcpflags[i], - i > 0 ? NULL : "@tcpflags "); + INCREMENT_DISPLAY(ptr, nb_items, len); + for (i = 0; i < api->match_tcpflags_num; i++) { + delta = snprintf_bgp_pbr_match_val(ptr, len, &api->tcpflags[i], + i > 0 ? NULL : "@tcpflags "); + len -= delta; + ptr += delta; + } if (api->match_fragment_num) - INCREMENT_DISPLAY(ptr, nb_items); - for (i = 0; i < api->match_fragment_num; i++) - ptr += sprintf_bgp_pbr_match_val(ptr, &api->fragment[i], - i > 0 ? NULL : "@fragment "); - if (!nb_items) + INCREMENT_DISPLAY(ptr, nb_items, len); + for (i = 0; i < api->match_fragment_num; i++) { + delta = snprintf_bgp_pbr_match_val(ptr, len, &api->fragment[i], + i > 0 ? NULL : "@fragment "); + len -= delta; + ptr += delta; + } + + len = sizeof(return_string); + if (!nb_items) { ptr = return_string; - else - ptr += sprintf(ptr, "; "); - if (api->action_num) - ptr += sprintf(ptr, "SET : "); + } else { + len -= (ptr - return_string); + delta = snprintf(ptr, len, "; "); + len -= delta; + ptr += delta; + } + if (api->action_num) { + delta = snprintf(ptr, len, "SET : "); + len -= delta; + ptr += delta; + } nb_items = 0; for (i = 0; i < api->action_num; i++) { switch (api->actions[i].action) { case ACTION_TRAFFICRATE: - INCREMENT_DISPLAY(ptr, nb_items); - ptr += sprintf(ptr, "@set rate %f", - api->actions[i].u.r.rate); + INCREMENT_DISPLAY(ptr, nb_items, len); + delta = snprintf(ptr, len, "@set rate %f", + api->actions[i].u.r.rate); + len -= delta; + ptr += delta; break; case ACTION_TRAFFIC_ACTION: - INCREMENT_DISPLAY(ptr, nb_items); - ptr += sprintf(ptr, "@action "); + INCREMENT_DISPLAY(ptr, nb_items, len); + delta = snprintf(ptr, len, "@action "); + len -= delta; + ptr += delta; if (api->actions[i].u.za.filter - & TRAFFIC_ACTION_TERMINATE) - ptr += sprintf(ptr, - " terminate (apply filter(s))"); + & TRAFFIC_ACTION_TERMINATE) { + delta = snprintf(ptr, len, + " terminate (apply filter(s))"); + len -= delta; + ptr += delta; + } if (api->actions[i].u.za.filter - & TRAFFIC_ACTION_DISTRIBUTE) - ptr += sprintf(ptr, " distribute"); + & TRAFFIC_ACTION_DISTRIBUTE) { + delta = snprintf(ptr, len, " distribute"); + len -= delta; + ptr += delta; + } if (api->actions[i].u.za.filter - & TRAFFIC_ACTION_SAMPLE) - ptr += sprintf(ptr, " sample"); + & TRAFFIC_ACTION_SAMPLE) { + delta = snprintf(ptr, len, " sample"); + len -= delta; + ptr += delta; + } break; - case ACTION_REDIRECT_IP: - INCREMENT_DISPLAY(ptr, nb_items); - char local_buff[INET_ADDRSTRLEN]; + case ACTION_REDIRECT_IP: { + char local_buff[INET6_ADDRSTRLEN]; + void *ptr_ip; - if (inet_ntop(AF_INET, - &api->actions[i].u.zr.redirect_ip_v4, - local_buff, INET_ADDRSTRLEN) != NULL) - ptr += sprintf(ptr, + INCREMENT_DISPLAY(ptr, nb_items, len); + if (api->afi == AF_INET) + ptr_ip = &api->actions[i].u.zr.redirect_ip_v4; + else + ptr_ip = &api->actions[i].u.zr.redirect_ip_v6; + if (inet_ntop(afi2family(api->afi), + ptr_ip, local_buff, + INET6_ADDRSTRLEN) != NULL) { + delta = snprintf(ptr, len, "@redirect ip nh %s", local_buff); + len -= delta; + ptr += delta; + } break; - case ACTION_REDIRECT: - INCREMENT_DISPLAY(ptr, nb_items); - ptr += sprintf(ptr, "@redirect vrf %u", - api->actions[i].u.redirect_vrf); + } + case ACTION_REDIRECT: { + struct vrf *vrf; + + vrf = vrf_lookup_by_id(api->actions[i].u.redirect_vrf); + INCREMENT_DISPLAY(ptr, nb_items, len); + delta = snprintf(ptr, len, "@redirect vrf %s(%u)", + VRF_LOGNAME(vrf), + api->actions[i].u.redirect_vrf); + len -= delta; + ptr += delta; break; + } case ACTION_MARKING: - INCREMENT_DISPLAY(ptr, nb_items); - ptr += sprintf(ptr, "@set dscp %u", - api->actions[i].u.marking_dscp); + INCREMENT_DISPLAY(ptr, nb_items, len); + delta = snprintf(ptr, len, "@set dscp/flowlabel %u", + api->actions[i].u.marking_dscp); + len -= delta; + ptr += delta; break; default: break; @@ -1488,7 +1755,7 @@ static void bgp_pbr_flush_entry(struct bgp *bgp, struct bgp_pbr_action *bpa, if (bpa->installed && bpa->table_id != 0) { bgp_send_pbr_rule_action(bpa, NULL, false); bgp_zebra_announce_default(bpa->bgp, &(bpa->nh), - AFI_IP, + bpa->afi, bpa->table_id, false); bpa->installed = false; @@ -1567,6 +1834,8 @@ static int bgp_pbr_get_remaining_entry(struct hash_bucket *bucket, void *arg) bpm_temp->pkt_len_min != bpm->pkt_len_min || bpm_temp->pkt_len_max != bpm->pkt_len_max || bpm_temp->dscp_value != bpm->dscp_value || + bpm_temp->flow_label != bpm->flow_label || + bpm_temp->family != bpm->family || bpm_temp->fragment != bpm->fragment) return HASHWALK_CONTINUE; @@ -1638,16 +1907,17 @@ static void bgp_pbr_policyroute_remove_from_zebra_unit( return; } + temp.family = bpf->family; if (bpf->src) { temp.flags |= MATCH_IP_SRC_SET; prefix_copy(&temp2.src, bpf->src); } else - temp2.src.family = AF_INET; + temp2.src.family = bpf->family; if (bpf->dst) { temp.flags |= MATCH_IP_DST_SET; prefix_copy(&temp2.dst, bpf->dst); } else - temp2.dst.family = AF_INET; + temp2.dst.family = bpf->family; if (src_port && (src_port->min_port || bpf->protocol == IPPROTO_ICMP)) { if (bpf->protocol == IPPROTO_ICMP) temp.flags |= MATCH_ICMP_SET; @@ -1690,6 +1960,14 @@ static void bgp_pbr_policyroute_remove_from_zebra_unit( temp.flags |= MATCH_DSCP_SET; temp.dscp_value = bpf->dscp->val; } + if (bpf->flow_label) { + if (bpf->flow_label->mask) + temp.flags |= MATCH_FLOW_LABEL_INVERSE_SET; + else + temp.flags |= MATCH_FLOW_LABEL_SET; + temp.flow_label = bpf->flow_label->val; + } + if (bpf->fragment) { if (bpf->fragment->mask) temp.flags |= MATCH_FRAGMENT_INVERSE_SET; @@ -1708,7 +1986,7 @@ static void bgp_pbr_policyroute_remove_from_zebra_unit( temp.type = IPSET_NET_NET; } if (bpf->vrf_id == VRF_UNKNOWN) /* XXX case BGP destroy */ - temp.vrf_id = 0; + temp.vrf_id = VRF_DEFAULT; else temp.vrf_id = bpf->vrf_id; bpme = &temp2; @@ -1736,6 +2014,8 @@ static uint8_t bgp_pbr_next_type_entry(uint8_t type_entry) if (type_entry == FLOWSPEC_TCP_FLAGS) return FLOWSPEC_DSCP; if (type_entry == FLOWSPEC_DSCP) + return FLOWSPEC_FLOW_LABEL; + if (type_entry == FLOWSPEC_FLOW_LABEL) return FLOWSPEC_PKT_LEN; if (type_entry == FLOWSPEC_PKT_LEN) return FLOWSPEC_FRAGMENT; @@ -1829,6 +2109,9 @@ static void bgp_pbr_policyroute_remove_from_zebra_recursive( } else if (type_entry == FLOWSPEC_DSCP && bpof->dscp) { orig_list = bpof->dscp; target_val = &bpf->dscp; + } else if (type_entry == FLOWSPEC_FLOW_LABEL && bpof->flowlabel) { + orig_list = bpof->flowlabel; + target_val = &bpf->flow_label; } else if (type_entry == FLOWSPEC_PKT_LEN && bpof->pkt_len) { orig_list = bpof->pkt_len; target_val = &bpf->pkt_len_val; @@ -1866,6 +2149,9 @@ static void bgp_pbr_policyroute_remove_from_zebra( else if (bpof->dscp) bgp_pbr_policyroute_remove_from_zebra_recursive( bgp, path, bpf, bpof, FLOWSPEC_DSCP); + else if (bpof->flowlabel) + bgp_pbr_policyroute_remove_from_zebra_recursive( + bgp, path, bpf, bpof, FLOWSPEC_FLOW_LABEL); else if (bpof->pkt_len) bgp_pbr_policyroute_remove_from_zebra_recursive( bgp, path, bpf, bpof, FLOWSPEC_PKT_LEN); @@ -1882,6 +2168,8 @@ static void bgp_pbr_policyroute_remove_from_zebra( list_delete_all_node(bpof->tcpflags); if (bpof->dscp) list_delete_all_node(bpof->dscp); + if (bpof->flowlabel) + list_delete_all_node(bpof->flowlabel); if (bpof->pkt_len) list_delete_all_node(bpof->pkt_len); if (bpof->fragment) @@ -1973,6 +2261,15 @@ static void bgp_pbr_dump_entry(struct bgp_pbr_filter *bpf, bool add) ? "!" : "", bpf->dscp->val); } + if (bpf->flow_label) { + snprintf(buffer + remaining_len, + sizeof(buffer) + - remaining_len, + "%s flow_label %d", + bpf->flow_label->mask + ? "!" : "", + bpf->flow_label->val); + } zlog_debug("BGP: %s FS PBR from %s to %s, %s %s", add ? "adding" : "removing", bpf->src == NULL ? "" : @@ -2021,6 +2318,7 @@ static void bgp_pbr_policyroute_add_to_zebra_unit(struct bgp *bgp, if (nh) memcpy(&temp3.nh, nh, sizeof(struct nexthop)); temp3.vrf_id = bpf->vrf_id; + temp3.afi = family2afi(bpf->family); bpa = hash_get(bgp->pbr_action_hash, &temp3, bgp_pbr_action_alloc_intern); @@ -2071,8 +2369,7 @@ static void bgp_pbr_policyroute_add_to_zebra_unit(struct bgp *bgp, listnode_lookup_nocheck(extra->bgp_fs_iprule, bpr)) { if (BGP_DEBUG(pbr, PBR_ERROR)) - zlog_err("%s: entry %p/%p already " - "installed in bgp pbr iprule", + zlog_err("%s: entry %p/%p already installed in bgp pbr iprule", __func__, path, bpr); return; } @@ -2080,7 +2377,8 @@ static void bgp_pbr_policyroute_add_to_zebra_unit(struct bgp *bgp, if (!bpa->installed && !bpa->install_in_progress) { bgp_send_pbr_rule_action(bpa, NULL, true); bgp_zebra_announce_default(bgp, nh, - AFI_IP, bpa->table_id, true); + bpa->afi, + bpa->table_id, true); } /* ip rule add */ if (bpr && !bpr->installed) @@ -2106,6 +2404,7 @@ static void bgp_pbr_policyroute_add_to_zebra_unit(struct bgp *bgp, /* then look for bpm */ memset(&temp, 0, sizeof(temp)); temp.vrf_id = bpf->vrf_id; + temp.family = bpf->family; if (bpf->src) temp.flags |= MATCH_IP_SRC_SET; if (bpf->dst) @@ -2157,11 +2456,22 @@ static void bgp_pbr_policyroute_add_to_zebra_unit(struct bgp *bgp, temp.flags |= MATCH_DSCP_SET; temp.dscp_value = bpf->dscp->val; } + if (bpf->flow_label) { + if (bpf->flow_label->mask) + temp.flags |= MATCH_FLOW_LABEL_INVERSE_SET; + else + temp.flags |= MATCH_FLOW_LABEL_SET; + temp.flow_label = bpf->flow_label->val; + } if (bpf->fragment) { if (bpf->fragment->mask) temp.flags |= MATCH_FRAGMENT_INVERSE_SET; temp.fragment = bpf->fragment->val; } + if (bpf->protocol) { + temp.protocol = bpf->protocol; + temp.flags |= MATCH_PROTOCOL_SET; + } temp.action = bpa; bpm = hash_get(bgp->pbr_match_hash, &temp, bgp_pbr_match_alloc_intern); @@ -2170,7 +2480,8 @@ static void bgp_pbr_policyroute_add_to_zebra_unit(struct bgp *bgp, if (bpm->unique == 0) { bpm->unique = ++bgp_pbr_match_counter_unique; /* 0 value is forbidden */ - sprintf(bpm->ipset_name, "match%p", bpm); + snprintf(bpm->ipset_name, sizeof(bpm->ipset_name), + "match%p", bpm); bpm->entry_hash = hash_create_size(8, bgp_pbr_match_entry_hash_key, bgp_pbr_match_entry_hash_equal, @@ -2188,11 +2499,11 @@ static void bgp_pbr_policyroute_add_to_zebra_unit(struct bgp *bgp, if (bpf->src) prefix_copy(&temp2.src, bpf->src); else - temp2.src.family = AF_INET; + temp2.src.family = bpf->family; if (bpf->dst) prefix_copy(&temp2.dst, bpf->dst); else - temp2.dst.family = AF_INET; + temp2.dst.family = bpf->family; temp2.src_port_min = src_port ? src_port->min_port : 0; temp2.dst_port_min = dst_port ? dst_port->min_port : 0; temp2.src_port_max = src_port ? src_port->max_port : 0; @@ -2239,7 +2550,7 @@ static void bgp_pbr_policyroute_add_to_zebra_unit(struct bgp *bgp, if (!bpa->installed && !bpa->install_in_progress) { bgp_send_pbr_rule_action(bpa, NULL, true); bgp_zebra_announce_default(bgp, nh, - AFI_IP, bpa->table_id, true); + bpa->afi, bpa->table_id, true); } /* ipset create */ @@ -2391,8 +2702,11 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, struct bgp_path_info *path, bpf.type = api->type; memset(&nh, 0, sizeof(struct nexthop)); nh.vrf_id = VRF_UNKNOWN; - if (api->match_protocol_num) + if (api->match_protocol_num) { proto = (uint8_t)api->protocol[0].value; + if (api->afi == AF_INET6 && proto == IPPROTO_ICMPV6) + proto = IPPROTO_ICMP; + } /* if match_port is selected, then either src or dst port will be parsed * but not both at the same time */ @@ -2501,6 +2815,7 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, struct bgp_path_info *path, bpf.protocol = proto; bpf.src_port = srcp; bpf.dst_port = dstp; + bpf.family = afi2family(api->afi); if (!add) { bgp_pbr_policyroute_remove_from_zebra(bgp, path, &bpf, &bpof); return; @@ -2550,10 +2865,18 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, struct bgp_path_info *path, */ break; case ACTION_REDIRECT_IP: - nh.type = NEXTHOP_TYPE_IPV4; - nh.gate.ipv4.s_addr = - api->actions[i].u.zr.redirect_ip_v4.s_addr; nh.vrf_id = api->vrf_id; + if (api->afi == AFI_IP) { + nh.type = NEXTHOP_TYPE_IPV4; + nh.gate.ipv4.s_addr = + api->actions[i].u.zr. + redirect_ip_v4.s_addr; + } else { + nh.type = NEXTHOP_TYPE_IPV6; + memcpy(&nh.gate.ipv6, + &api->actions[i].u.zr.redirect_ip_v6, + sizeof(struct in6_addr)); + } bgp_pbr_policyroute_add_to_zebra(bgp, path, &bpf, &bpof, &nh, &rate); /* XXX combination with REDIRECT_VRF @@ -2562,8 +2885,11 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, struct bgp_path_info *path, continue_loop = 0; break; case ACTION_REDIRECT: + if (api->afi == AFI_IP) + nh.type = NEXTHOP_TYPE_IPV4; + else + nh.type = NEXTHOP_TYPE_IPV6; nh.vrf_id = api->actions[i].u.redirect_vrf; - nh.type = NEXTHOP_TYPE_IPV4; bgp_pbr_policyroute_add_to_zebra(bgp, path, &bpf, &bpof, &nh, &rate); continue_loop = 0; @@ -2571,7 +2897,7 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, struct bgp_path_info *path, case ACTION_MARKING: if (BGP_DEBUG(pbr, PBR)) { bgp_pbr_print_policy_route(api); - zlog_warn("PBR: Set DSCP %u Ignored", + zlog_warn("PBR: Set DSCP/FlowLabel %u Ignored", api->actions[i].u.marking_dscp); } break; @@ -2583,14 +2909,12 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, struct bgp_path_info *path, } } -void bgp_pbr_update_entry(struct bgp *bgp, struct prefix *p, +void bgp_pbr_update_entry(struct bgp *bgp, const struct prefix *p, struct bgp_path_info *info, afi_t afi, safi_t safi, bool nlri_update) { struct bgp_pbr_entry_main api; - if (afi == AFI_IP6) - return; /* IPv6 not supported */ if (safi != SAFI_FLOWSPEC) return; /* not supported */ /* Make Zebra API structure. */ @@ -2640,10 +2964,12 @@ void bgp_pbr_reset(struct bgp *bgp, afi_t afi) struct bgp_pbr_interface_head *head; struct bgp_pbr_interface *pbr_if; - if (!bgp_pbr_cfg || afi != AFI_IP) + if (!bgp_pbr_cfg) return; - head = &(bgp_pbr_cfg->ifaces_by_name_ipv4); - + if (afi == AFI_IP) + head = &(bgp_pbr_cfg->ifaces_by_name_ipv4); + else + head = &(bgp_pbr_cfg->ifaces_by_name_ipv6); while (!RB_EMPTY(bgp_pbr_interface_head, head)) { pbr_if = RB_ROOT(bgp_pbr_interface_head, head); RB_REMOVE(bgp_pbr_interface_head, head, pbr_if); diff --git a/bgpd/bgp_pbr.h b/bgpd/bgp_pbr.h index f7fddac7fb..379ac40672 100644 --- a/bgpd/bgp_pbr.h +++ b/bgpd/bgp_pbr.h @@ -79,6 +79,7 @@ struct bgp_pbr_entry_action { vrf_id_t redirect_vrf; struct _pbr_redirect_ip { struct in_addr redirect_ip_v4; + struct in6_addr redirect_ip_v6; uint8_t duplicate; } zr; uint8_t marking_dscp; @@ -114,13 +115,17 @@ struct bgp_pbr_entry_main { uint8_t match_dscp_num; uint8_t match_tcpflags_num; uint8_t match_fragment_num; + uint8_t match_flowlabel_num; struct prefix src_prefix; struct prefix dst_prefix; + uint8_t src_prefix_offset; + uint8_t dst_prefix_offset; #define PROTOCOL_UDP 17 #define PROTOCOL_TCP 6 #define PROTOCOL_ICMP 1 +#define PROTOCOL_ICMPV6 58 struct bgp_pbr_match_val protocol[BGP_PBR_MATCH_VAL_MAX]; struct bgp_pbr_match_val src_port[BGP_PBR_MATCH_VAL_MAX]; struct bgp_pbr_match_val dst_port[BGP_PBR_MATCH_VAL_MAX]; @@ -129,6 +134,7 @@ struct bgp_pbr_entry_main { struct bgp_pbr_match_val icmp_code[BGP_PBR_MATCH_VAL_MAX]; struct bgp_pbr_match_val packet_length[BGP_PBR_MATCH_VAL_MAX]; struct bgp_pbr_match_val dscp[BGP_PBR_MATCH_VAL_MAX]; + struct bgp_pbr_match_val flow_label[BGP_PBR_MATCH_VAL_MAX]; struct bgp_pbr_match_val tcpflags[BGP_PBR_MATCH_VAL_MAX]; struct bgp_pbr_match_val fragment[BGP_PBR_MATCH_VAL_MAX]; @@ -154,6 +160,8 @@ extern int bgp_pbr_interface_compare(const struct bgp_pbr_interface *a, struct bgp_pbr_config { struct bgp_pbr_interface_head ifaces_by_name_ipv4; bool pbr_interface_any_ipv4; + struct bgp_pbr_interface_head ifaces_by_name_ipv6; + bool pbr_interface_any_ipv6; }; extern struct bgp_pbr_config *bgp_pbr_cfg; @@ -179,6 +187,7 @@ struct bgp_pbr_match { uint32_t type; uint32_t flags; + uint8_t family; uint16_t pkt_len_min; uint16_t pkt_len_max; @@ -186,6 +195,8 @@ struct bgp_pbr_match { uint16_t tcp_mask_flags; uint8_t dscp_value; uint8_t fragment; + uint8_t protocol; + uint16_t flow_label; vrf_id_t vrf_id; @@ -253,6 +264,7 @@ struct bgp_pbr_action { bool install_in_progress; uint32_t refcnt; struct bgp *bgp; + afi_t afi; }; extern struct bgp_pbr_rule *bgp_pbr_rule_lookup(vrf_id_t vrf_id, @@ -273,24 +285,23 @@ extern struct bgp_pbr_match *bgp_pbr_match_iptable_lookup(vrf_id_t vrf_id, extern void bgp_pbr_cleanup(struct bgp *bgp); extern void bgp_pbr_init(struct bgp *bgp); -extern uint32_t bgp_pbr_rule_hash_key(void *arg); +extern uint32_t bgp_pbr_rule_hash_key(const void *arg); extern bool bgp_pbr_rule_hash_equal(const void *arg1, const void *arg2); -extern uint32_t bgp_pbr_action_hash_key(void *arg); +extern uint32_t bgp_pbr_action_hash_key(const void *arg); extern bool bgp_pbr_action_hash_equal(const void *arg1, const void *arg2); -extern uint32_t bgp_pbr_match_entry_hash_key(void *arg); +extern uint32_t bgp_pbr_match_entry_hash_key(const void *arg); extern bool bgp_pbr_match_entry_hash_equal(const void *arg1, const void *arg2); -extern uint32_t bgp_pbr_match_hash_key(void *arg); +extern uint32_t bgp_pbr_match_hash_key(const void *arg); extern bool bgp_pbr_match_hash_equal(const void *arg1, const void *arg2); void bgp_pbr_print_policy_route(struct bgp_pbr_entry_main *api); -struct bgp_node; struct bgp_path_info; -extern void bgp_pbr_update_entry(struct bgp *bgp, struct prefix *p, +extern void bgp_pbr_update_entry(struct bgp *bgp, const struct prefix *p, struct bgp_path_info *new_select, afi_t afi, safi_t safi, bool nlri_update); @@ -300,7 +311,7 @@ extern void bgp_pbr_reset(struct bgp *bgp, afi_t afi); extern struct bgp_pbr_interface *bgp_pbr_interface_lookup(const char *name, struct bgp_pbr_interface_head *head); -extern int bgp_pbr_build_and_validate_entry(struct prefix *p, +extern int bgp_pbr_build_and_validate_entry(const struct prefix *p, struct bgp_path_info *path, struct bgp_pbr_entry_main *api); #endif /* __BGP_PBR_H__ */ diff --git a/bgpd/bgp_rd.c b/bgpd/bgp_rd.c index 571139a49a..6ba56c7011 100644 --- a/bgpd/bgp_rd.c +++ b/bgpd/bgp_rd.c @@ -33,16 +33,16 @@ #include "bgpd/bgp_rd.h" #include "bgpd/bgp_attr.h" -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC #include "bgpd/rfapi/rfapi_backend.h" #endif -uint16_t decode_rd_type(uint8_t *pnt) +uint16_t decode_rd_type(const uint8_t *pnt) { uint16_t v; v = ((uint16_t)*pnt++ << 8); -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC /* * VNC L2 stores LHI in lower byte, so omit it */ @@ -60,7 +60,7 @@ void encode_rd_type(uint16_t v, uint8_t *pnt) } /* type == RD_TYPE_AS */ -void decode_rd_as(uint8_t *pnt, struct rd_as *rd_as) +void decode_rd_as(const uint8_t *pnt, struct rd_as *rd_as) { rd_as->as = (uint16_t)*pnt++ << 8; rd_as->as |= (uint16_t)*pnt++; @@ -68,7 +68,7 @@ void decode_rd_as(uint8_t *pnt, struct rd_as *rd_as) } /* type == RD_TYPE_AS4 */ -void decode_rd_as4(uint8_t *pnt, struct rd_as *rd_as) +void decode_rd_as4(const uint8_t *pnt, struct rd_as *rd_as) { pnt = ptr_get_be32(pnt, &rd_as->as); rd_as->val = ((uint16_t)*pnt++ << 8); @@ -76,7 +76,7 @@ void decode_rd_as4(uint8_t *pnt, struct rd_as *rd_as) } /* type == RD_TYPE_IP */ -void decode_rd_ip(uint8_t *pnt, struct rd_ip *rd_ip) +void decode_rd_ip(const uint8_t *pnt, struct rd_ip *rd_ip) { memcpy(&rd_ip->ip, pnt, 4); pnt += 4; @@ -85,9 +85,9 @@ void decode_rd_ip(uint8_t *pnt, struct rd_ip *rd_ip) rd_ip->val |= (uint16_t)*pnt; } -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC /* type == RD_TYPE_VNC_ETH */ -void decode_rd_vnc_eth(uint8_t *pnt, struct rd_vnc_eth *rd_vnc_eth) +void decode_rd_vnc_eth(const uint8_t *pnt, struct rd_vnc_eth *rd_vnc_eth) { rd_vnc_eth->type = RD_TYPE_VNC_ETH; rd_vnc_eth->local_nve_id = pnt[1]; @@ -159,9 +159,9 @@ int str2prefix_rd(const char *str, struct prefix_rd *prd) return lret; } -char *prefix_rd2str(struct prefix_rd *prd, char *buf, size_t size) +char *prefix_rd2str(const struct prefix_rd *prd, char *buf, size_t size) { - uint8_t *pnt; + const uint8_t *pnt; uint16_t type; struct rd_as rd_as; struct rd_ip rd_ip; @@ -174,18 +174,19 @@ char *prefix_rd2str(struct prefix_rd *prd, char *buf, size_t size) if (type == RD_TYPE_AS) { decode_rd_as(pnt + 2, &rd_as); - snprintf(buf, size, "%u:%d", rd_as.as, rd_as.val); + snprintf(buf, size, "%u:%u", rd_as.as, rd_as.val); return buf; } else if (type == RD_TYPE_AS4) { decode_rd_as4(pnt + 2, &rd_as); - snprintf(buf, size, "%u:%d", rd_as.as, rd_as.val); + snprintf(buf, size, "%u:%u", rd_as.as, rd_as.val); return buf; } else if (type == RD_TYPE_IP) { decode_rd_ip(pnt + 2, &rd_ip); - snprintf(buf, size, "%s:%d", inet_ntoa(rd_ip.ip), rd_ip.val); + snprintf(buf, size, "%s:%hu", inet_ntoa(rd_ip.ip), + rd_ip.val); return buf; } -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC else if (type == RD_TYPE_VNC_ETH) { snprintf(buf, size, "LHI:%d, %02x:%02x:%02x:%02x:%02x:%02x", *(pnt + 1), /* LHI */ @@ -209,6 +210,6 @@ void form_auto_rd(struct in_addr router_id, prd->family = AF_UNSPEC; prd->prefixlen = 64; - sprintf(buf, "%s:%hu", inet_ntoa(router_id), rd_id); + snprintf(buf, sizeof(buf), "%s:%hu", inet_ntoa(router_id), rd_id); (void)str2prefix_rd(buf, prd); } diff --git a/bgpd/bgp_rd.h b/bgpd/bgp_rd.h index c5ea34103f..2aee44c721 100644 --- a/bgpd/bgp_rd.h +++ b/bgpd/bgp_rd.h @@ -28,11 +28,12 @@ #define RD_TYPE_IP 1 #define RD_TYPE_AS4 2 -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC #define RD_TYPE_VNC_ETH 0xff00 /* VNC L2VPN */ #endif #define RD_ADDRSTRLEN 28 +#define RD_BYTES 8 struct rd_as { uint16_t type; @@ -46,7 +47,7 @@ struct rd_ip { uint16_t val; }; -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC struct rd_vnc_eth { uint16_t type; uint8_t local_nve_id; @@ -54,18 +55,19 @@ struct rd_vnc_eth { }; #endif -extern uint16_t decode_rd_type(uint8_t *pnt); +extern uint16_t decode_rd_type(const uint8_t *pnt); extern void encode_rd_type(uint16_t, uint8_t *); -extern void decode_rd_as(uint8_t *pnt, struct rd_as *rd_as); -extern void decode_rd_as4(uint8_t *pnt, struct rd_as *rd_as); -extern void decode_rd_ip(uint8_t *pnt, struct rd_ip *rd_ip); -#if ENABLE_BGP_VNC -extern void decode_rd_vnc_eth(uint8_t *pnt, struct rd_vnc_eth *rd_vnc_eth); +extern void decode_rd_as(const uint8_t *pnt, struct rd_as *rd_as); +extern void decode_rd_as4(const uint8_t *pnt, struct rd_as *rd_as); +extern void decode_rd_ip(const uint8_t *pnt, struct rd_ip *rd_ip); +#ifdef ENABLE_BGP_VNC +extern void decode_rd_vnc_eth(const uint8_t *pnt, + struct rd_vnc_eth *rd_vnc_eth); #endif extern int str2prefix_rd(const char *, struct prefix_rd *); -extern char *prefix_rd2str(struct prefix_rd *, char *, size_t); +extern char *prefix_rd2str(const struct prefix_rd *, char *, size_t); extern void form_auto_rd(struct in_addr router_id, uint16_t rd_id, struct prefix_rd *prd); diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index fc6798fdfc..d160f4b8e1 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -22,6 +22,7 @@ #include #include +#include "printfrr.h" #include "prefix.h" #include "linklist.h" #include "memory.h" @@ -37,9 +38,10 @@ #include "workqueue.h" #include "queue.h" #include "memory.h" +#include "srv6.h" #include "lib/json.h" #include "lib_errors.h" - +#include "zclient.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_table.h" #include "bgpd/bgp_route.h" @@ -67,8 +69,9 @@ #include "bgpd/bgp_label.h" #include "bgpd/bgp_addpath.h" #include "bgpd/bgp_mac.h" +#include "bgpd/bgp_network.h" -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC #include "bgpd/rfapi/rfapi_backend.h" #include "bgpd/rfapi/vnc_import_bgp.h" #include "bgpd/rfapi/vnc_export_bgp.h" @@ -76,6 +79,7 @@ #include "bgpd/bgp_encap_types.h" #include "bgpd/bgp_encap_tlv.h" #include "bgpd/bgp_evpn.h" +#include "bgpd/bgp_evpn_mh.h" #include "bgpd/bgp_evpn_vty.h" #include "bgpd/bgp_flowspec.h" #include "bgpd/bgp_flowspec_util.h" @@ -88,7 +92,7 @@ /* Extern from bgp_dump.c */ extern const char *bgp_origin_str[]; extern const char *bgp_origin_long_str[]; - +const char *get_afi_safi_str(afi_t afi, safi_t safi, bool for_json); /* PMSI strings. */ #define PMSI_TNLTYPE_STR_NO_INFO "No info" #define PMSI_TNLTYPE_STR_DEFAULT PMSI_TNLTYPE_STR_NO_INFO @@ -106,65 +110,69 @@ static const struct message bgp_pmsi_tnltype_str[] = { #define VRFID_NONE_STR "-" -struct bgp_node *bgp_afi_node_get(struct bgp_table *table, afi_t afi, - safi_t safi, struct prefix *p, +DEFINE_HOOK(bgp_process, + (struct bgp * bgp, afi_t afi, safi_t safi, struct bgp_dest *bn, + struct peer *peer, bool withdraw), + (bgp, afi, safi, bn, peer, withdraw)) + + +struct bgp_dest *bgp_afi_node_get(struct bgp_table *table, afi_t afi, + safi_t safi, const struct prefix *p, struct prefix_rd *prd) { - struct bgp_node *rn; - struct bgp_node *prn = NULL; + struct bgp_dest *dest; + struct bgp_dest *pdest = NULL; assert(table); - if (!table) - return NULL; if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP) || (safi == SAFI_EVPN)) { - prn = bgp_node_get(table, (struct prefix *)prd); + pdest = bgp_node_get(table, (struct prefix *)prd); - if (!bgp_node_has_bgp_path_info_data(prn)) - bgp_node_set_bgp_table_info( - prn, bgp_table_init(table->bgp, afi, safi)); + if (!bgp_dest_has_bgp_path_info_data(pdest)) + bgp_dest_set_bgp_table_info( + pdest, bgp_table_init(table->bgp, afi, safi)); else - bgp_unlock_node(prn); - table = bgp_node_get_bgp_table_info(prn); + bgp_dest_unlock_node(pdest); + table = bgp_dest_get_bgp_table_info(pdest); } - rn = bgp_node_get(table, p); + dest = bgp_node_get(table, p); if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP) || (safi == SAFI_EVPN)) - rn->prn = prn; + dest->pdest = pdest; - return rn; + return dest; } -struct bgp_node *bgp_afi_node_lookup(struct bgp_table *table, afi_t afi, - safi_t safi, struct prefix *p, +struct bgp_dest *bgp_afi_node_lookup(struct bgp_table *table, afi_t afi, + safi_t safi, const struct prefix *p, struct prefix_rd *prd) { - struct bgp_node *rn; - struct bgp_node *prn = NULL; + struct bgp_dest *dest; + struct bgp_dest *pdest = NULL; if (!table) return NULL; if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP) || (safi == SAFI_EVPN)) { - prn = bgp_node_lookup(table, (struct prefix *)prd); - if (!prn) + pdest = bgp_node_lookup(table, (struct prefix *)prd); + if (!pdest) return NULL; - if (!bgp_node_has_bgp_path_info_data(prn)) { - bgp_unlock_node(prn); + if (!bgp_dest_has_bgp_path_info_data(pdest)) { + bgp_dest_unlock_node(pdest); return NULL; } - table = bgp_node_get_bgp_table_info(prn); + table = bgp_dest_get_bgp_table_info(pdest); } - rn = bgp_node_lookup(table, p); + dest = bgp_node_lookup(table, p); - return rn; + return dest; } /* Allocate bgp_path_info_extra */ @@ -189,7 +197,8 @@ void bgp_path_info_extra_free(struct bgp_path_info_extra **extra) e = *extra; if (e->damp_info) - bgp_damp_info_free(e->damp_info, 0); + bgp_damp_info_free(e->damp_info, 0, e->damp_info->afi, + e->damp_info->safi); e->damp_info = NULL; if (e->parent) { @@ -208,7 +217,7 @@ void bgp_path_info_extra_free(struct bgp_path_info_extra **extra) bpi = bgp_path_info_lock(bpi); refcount = bpi->net->lock - 1; - bgp_unlock_node((struct bgp_node *)bpi->net); + bgp_dest_unlock_node((struct bgp_dest *)bpi->net); if (!refcount) bpi->net = NULL; bgp_path_info_unlock(bpi); @@ -225,8 +234,6 @@ void bgp_path_info_extra_free(struct bgp_path_info_extra **extra) if ((*extra)->bgp_fs_pbr) list_delete(&((*extra)->bgp_fs_pbr)); XFREE(MTYPE_BGP_ROUTE_EXTRA, *extra); - - *extra = NULL; } /* Get bgp_path_info extra information for the given bgp_path_info, lazy @@ -242,8 +249,7 @@ struct bgp_path_info_extra *bgp_path_info_extra_get(struct bgp_path_info *pi) /* Free bgp route information. */ static void bgp_path_info_free(struct bgp_path_info *path) { - if (path->attr) - bgp_attr_unintern(&path->attr); + bgp_attr_unintern(&path->attr); bgp_unlink_nexthop(path); bgp_path_info_extra_free(&path->extra); @@ -288,42 +294,122 @@ struct bgp_path_info *bgp_path_info_unlock(struct bgp_path_info *path) return path; } -void bgp_path_info_add(struct bgp_node *rn, struct bgp_path_info *pi) +/* This function sets flag BGP_NODE_SELECT_DEFER based on condition */ +static int bgp_dest_set_defer_flag(struct bgp_dest *dest, bool delete) +{ + struct peer *peer; + struct bgp_path_info *old_pi, *nextpi; + bool set_flag = false; + struct bgp *bgp = NULL; + struct bgp_table *table = NULL; + afi_t afi = 0; + safi_t safi = 0; + + /* If the flag BGP_NODE_SELECT_DEFER is set and new path is added + * then the route selection is deferred + */ + if (CHECK_FLAG(dest->flags, BGP_NODE_SELECT_DEFER) && (!delete)) + return 0; + + if (CHECK_FLAG(dest->flags, BGP_NODE_PROCESS_SCHEDULED)) { + if (BGP_DEBUG(update, UPDATE_OUT)) + zlog_debug( + "Route %pRN is in workqueue and being processed, not deferred.", + bgp_dest_to_rnode(dest)); + + return 0; + } + + table = bgp_dest_table(dest); + if (table) { + bgp = table->bgp; + afi = table->afi; + safi = table->safi; + } + + for (old_pi = bgp_dest_get_bgp_path_info(dest); + (old_pi != NULL) && (nextpi = old_pi->next, 1); old_pi = nextpi) { + if (CHECK_FLAG(old_pi->flags, BGP_PATH_SELECTED)) + continue; + + /* Route selection is deferred if there is a stale path which + * which indicates peer is in restart mode + */ + if (CHECK_FLAG(old_pi->flags, BGP_PATH_STALE) + && (old_pi->sub_type == BGP_ROUTE_NORMAL)) { + set_flag = true; + } else { + /* If the peer is graceful restart capable and peer is + * restarting mode, set the flag BGP_NODE_SELECT_DEFER + */ + peer = old_pi->peer; + if (BGP_PEER_GRACEFUL_RESTART_CAPABLE(peer) + && BGP_PEER_RESTARTING_MODE(peer) + && (old_pi + && old_pi->sub_type == BGP_ROUTE_NORMAL)) { + set_flag = true; + } + } + if (set_flag) + break; + } + + /* Set the flag BGP_NODE_SELECT_DEFER if route selection deferral timer + * is active + */ + if (set_flag && table) { + if (bgp && (bgp->gr_info[afi][safi].t_select_deferral)) { + SET_FLAG(dest->flags, BGP_NODE_SELECT_DEFER); + if (dest->rt_node == NULL) + dest->rt_node = listnode_add( + bgp->gr_info[afi][safi].route_list, + dest); + if (BGP_DEBUG(update, UPDATE_OUT)) + zlog_debug("DEFER route %pRN, dest %p, node %p", + dest, dest, dest->rt_node); + return 0; + } + } + return -1; +} + +void bgp_path_info_add(struct bgp_dest *dest, struct bgp_path_info *pi) { struct bgp_path_info *top; - top = bgp_node_get_bgp_path_info(rn); + top = bgp_dest_get_bgp_path_info(dest); pi->next = top; pi->prev = NULL; if (top) top->prev = pi; - bgp_node_set_bgp_path_info(rn, pi); + bgp_dest_set_bgp_path_info(dest, pi); bgp_path_info_lock(pi); - bgp_lock_node(rn); + bgp_dest_lock_node(dest); peer_lock(pi->peer); /* bgp_path_info peer reference */ + bgp_dest_set_defer_flag(dest, false); } /* Do the actual removal of info from RIB, for use by bgp_process completion callback *only* */ -void bgp_path_info_reap(struct bgp_node *rn, struct bgp_path_info *pi) +void bgp_path_info_reap(struct bgp_dest *dest, struct bgp_path_info *pi) { if (pi->next) pi->next->prev = pi->prev; if (pi->prev) pi->prev->next = pi->next; else - bgp_node_set_bgp_path_info(rn, pi->next); + bgp_dest_set_bgp_path_info(dest, pi->next); bgp_path_info_mpath_dequeue(pi); bgp_path_info_unlock(pi); - bgp_unlock_node(rn); + bgp_dest_unlock_node(dest); } -void bgp_path_info_delete(struct bgp_node *rn, struct bgp_path_info *pi) +void bgp_path_info_delete(struct bgp_dest *dest, struct bgp_path_info *pi) { - bgp_path_info_set_flag(rn, pi, BGP_PATH_REMOVED); + bgp_path_info_set_flag(dest, pi, BGP_PATH_REMOVED); /* set of previous already took care of pcount */ UNSET_FLAG(pi->flags, BGP_PATH_VALID); } @@ -331,22 +417,22 @@ void bgp_path_info_delete(struct bgp_node *rn, struct bgp_path_info *pi) /* undo the effects of a previous call to bgp_path_info_delete; typically called when a route is deleted and then quickly re-added before the deletion has been processed */ -void bgp_path_info_restore(struct bgp_node *rn, struct bgp_path_info *pi) +void bgp_path_info_restore(struct bgp_dest *dest, struct bgp_path_info *pi) { - bgp_path_info_unset_flag(rn, pi, BGP_PATH_REMOVED); + bgp_path_info_unset_flag(dest, pi, BGP_PATH_REMOVED); /* unset of previous already took care of pcount */ SET_FLAG(pi->flags, BGP_PATH_VALID); } /* Adjust pcount as required */ -static void bgp_pcount_adjust(struct bgp_node *rn, struct bgp_path_info *pi) +static void bgp_pcount_adjust(struct bgp_dest *dest, struct bgp_path_info *pi) { struct bgp_table *table; - assert(rn && bgp_node_table(rn)); + assert(dest && bgp_dest_table(dest)); assert(pi && pi->peer && pi->peer->bgp); - table = bgp_node_table(rn); + table = bgp_dest_table(dest); if (pi->peer == pi->peer->bgp->peer_self) return; @@ -378,7 +464,7 @@ static int bgp_label_index_differs(struct bgp_path_info *pi1, /* Set/unset bgp_path_info flags, adjusting any other state as needed. * This is here primarily to keep prefix-count in check. */ -void bgp_path_info_set_flag(struct bgp_node *rn, struct bgp_path_info *pi, +void bgp_path_info_set_flag(struct bgp_dest *dest, struct bgp_path_info *pi, uint32_t flag) { SET_FLAG(pi->flags, flag); @@ -389,10 +475,10 @@ void bgp_path_info_set_flag(struct bgp_node *rn, struct bgp_path_info *pi, BGP_PATH_VALID | BGP_PATH_HISTORY | BGP_PATH_REMOVED)) return; - bgp_pcount_adjust(rn, pi); + bgp_pcount_adjust(dest, pi); } -void bgp_path_info_unset_flag(struct bgp_node *rn, struct bgp_path_info *pi, +void bgp_path_info_unset_flag(struct bgp_dest *dest, struct bgp_path_info *pi, uint32_t flag) { UNSET_FLAG(pi->flags, flag); @@ -403,7 +489,7 @@ void bgp_path_info_unset_flag(struct bgp_node *rn, struct bgp_path_info *pi, BGP_PATH_VALID | BGP_PATH_HISTORY | BGP_PATH_REMOVED)) return; - bgp_pcount_adjust(rn, pi); + bgp_pcount_adjust(dest, pi); } /* Get MED value. If MED value is missing and "bgp bestpath @@ -413,7 +499,7 @@ static uint32_t bgp_med_value(struct attr *attr, struct bgp *bgp) if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC)) return attr->med; else { - if (bgp_flag_check(bgp, BGP_FLAG_MED_MISSING_AS_WORST)) + if (CHECK_FLAG(bgp->flags, BGP_FLAG_MED_MISSING_AS_WORST)) return BGP_MED_MAX; else return 0; @@ -434,7 +520,8 @@ void bgp_path_info_path_with_addpath_rx_str(struct bgp_path_info *pi, char *buf) static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, struct bgp_path_info *exist, int *paths_eq, struct bgp_maxpaths_cfg *mpath_cfg, int debug, - char *pfx_buf, afi_t afi, safi_t safi) + char *pfx_buf, afi_t afi, safi_t safi, + enum bgp_path_selection_reason *reason) { struct attr *newattr, *existattr; bgp_peer_sort_t new_sort; @@ -458,11 +545,17 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, uint32_t new_mm_seq; uint32_t exist_mm_seq; int nh_cmp; + esi_t *exist_esi; + esi_t *new_esi; + bool same_esi; + bool old_proxy; + bool new_proxy; *paths_eq = 0; /* 0. Null check. */ if (new == NULL) { + *reason = bgp_path_selection_none; if (debug) zlog_debug("%s: new is NULL", pfx_buf); return 0; @@ -472,6 +565,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, bgp_path_info_path_with_addpath_rx_str(new, new_buf); if (exist == NULL) { + *reason = bgp_path_selection_first; if (debug) zlog_debug("%s: %s is the initial bestpath", pfx_buf, new_buf); @@ -504,9 +598,9 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, */ if (newattr->sticky != existattr->sticky) { if (!debug) { - prefix2str(&new->net->p, pfx_buf, - sizeof(*pfx_buf) - * PREFIX2STR_BUFFER); + prefix2str( + bgp_dest_get_prefix(new->net), pfx_buf, + sizeof(*pfx_buf) * PREFIX2STR_BUFFER); bgp_path_info_path_with_addpath_rx_str(new, new_buf); bgp_path_info_path_with_addpath_rx_str( @@ -514,6 +608,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, } if (newattr->sticky && !existattr->sticky) { + *reason = bgp_path_selection_evpn_sticky_mac; if (debug) zlog_debug( "%s: %s wins over %s due to sticky MAC flag", @@ -522,6 +617,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, } if (!newattr->sticky && existattr->sticky) { + *reason = bgp_path_selection_evpn_sticky_mac; if (debug) zlog_debug( "%s: %s loses to %s due to sticky MAC flag", @@ -530,10 +626,52 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, } } + new_esi = bgp_evpn_attr_get_esi(newattr); + exist_esi = bgp_evpn_attr_get_esi(existattr); + if (bgp_evpn_is_esi_valid(new_esi) && + !memcmp(new_esi, exist_esi, sizeof(esi_t))) { + same_esi = true; + } else { + same_esi = false; + } + + /* If both paths have the same non-zero ES and + * one path is local it wins. + * PS: Note the local path wins even if the remote + * has the higher MM seq. The local path's + * MM seq will be fixed up to match the highest + * rem seq, subsequently. + */ + if (same_esi) { + char esi_buf[ESI_STR_LEN]; + + if (bgp_evpn_is_path_local(bgp, new)) { + *reason = bgp_path_selection_evpn_local_path; + if (debug) + zlog_debug( + "%s: %s wins over %s as ES %s is same and local", + pfx_buf, new_buf, exist_buf, + esi_to_str(new_esi, esi_buf, + sizeof(esi_buf))); + return 1; + } + if (bgp_evpn_is_path_local(bgp, exist)) { + *reason = bgp_path_selection_evpn_local_path; + if (debug) + zlog_debug( + "%s: %s loses to %s as ES %s is same and local", + pfx_buf, new_buf, exist_buf, + esi_to_str(new_esi, esi_buf, + sizeof(esi_buf))); + return 0; + } + } + new_mm_seq = mac_mobility_seqnum(newattr); exist_mm_seq = mac_mobility_seqnum(existattr); if (new_mm_seq > exist_mm_seq) { + *reason = bgp_path_selection_evpn_seq; if (debug) zlog_debug( "%s: %s wins over %s due to MM seq %u > %u", @@ -543,6 +681,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, } if (new_mm_seq < exist_mm_seq) { + *reason = bgp_path_selection_evpn_seq; if (debug) zlog_debug( "%s: %s loses to %s due to MM seq %u < %u", @@ -551,12 +690,37 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, return 0; } + /* if the sequence numbers and ESI are the same and one path + * is non-proxy it wins (over proxy) + */ + new_proxy = bgp_evpn_attr_is_proxy(newattr); + old_proxy = bgp_evpn_attr_is_proxy(existattr); + if (same_esi && bgp_evpn_attr_is_local_es(newattr) && + old_proxy != new_proxy) { + if (!new_proxy) { + *reason = bgp_path_selection_evpn_non_proxy; + if (debug) + zlog_debug( + "%s: %s wins over %s, same seq/es and non-proxy", + pfx_buf, new_buf, exist_buf); + return 1; + } + + *reason = bgp_path_selection_evpn_non_proxy; + if (debug) + zlog_debug( + "%s: %s loses to %s, same seq/es and non-proxy", + pfx_buf, new_buf, exist_buf); + return 0; + } + /* * if sequence numbers are the same path with the lowest IP * wins */ nh_cmp = bgp_path_info_nexthop_cmp(new, exist); if (nh_cmp < 0) { + *reason = bgp_path_selection_evpn_lower_ip; if (debug) zlog_debug( "%s: %s wins over %s due to same MM seq %u and lower IP %s", @@ -565,6 +729,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, return 1; } if (nh_cmp > 0) { + *reason = bgp_path_selection_evpn_lower_ip; if (debug) zlog_debug( "%s: %s loses to %s due to same MM seq %u and higher IP %s", @@ -579,6 +744,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, exist_weight = existattr->weight; if (new_weight > exist_weight) { + *reason = bgp_path_selection_weight; if (debug) zlog_debug("%s: %s wins over %s due to weight %d > %d", pfx_buf, new_buf, exist_buf, new_weight, @@ -587,6 +753,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, } if (new_weight < exist_weight) { + *reason = bgp_path_selection_weight; if (debug) zlog_debug("%s: %s loses to %s due to weight %d < %d", pfx_buf, new_buf, exist_buf, new_weight, @@ -603,6 +770,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, exist_pref = existattr->local_pref; if (new_pref > exist_pref) { + *reason = bgp_path_selection_local_pref; if (debug) zlog_debug( "%s: %s wins over %s due to localpref %d > %d", @@ -612,6 +780,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, } if (new_pref < exist_pref) { + *reason = bgp_path_selection_local_pref; if (debug) zlog_debug( "%s: %s loses to %s due to localpref %d < %d", @@ -627,6 +796,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, */ if (!(new->sub_type == BGP_ROUTE_NORMAL || new->sub_type == BGP_ROUTE_IMPORTED)) { + *reason = bgp_path_selection_local_route; if (debug) zlog_debug( "%s: %s wins over %s due to preferred BGP_ROUTE type", @@ -636,6 +806,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, if (!(exist->sub_type == BGP_ROUTE_NORMAL || exist->sub_type == BGP_ROUTE_IMPORTED)) { + *reason = bgp_path_selection_local_route; if (debug) zlog_debug( "%s: %s loses to %s due to preferred BGP_ROUTE type", @@ -644,17 +815,18 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, } /* 4. AS path length check. */ - if (!bgp_flag_check(bgp, BGP_FLAG_ASPATH_IGNORE)) { + if (!CHECK_FLAG(bgp->flags, BGP_FLAG_ASPATH_IGNORE)) { int exist_hops = aspath_count_hops(existattr->aspath); int exist_confeds = aspath_count_confeds(existattr->aspath); - if (bgp_flag_check(bgp, BGP_FLAG_ASPATH_CONFED)) { + if (CHECK_FLAG(bgp->flags, BGP_FLAG_ASPATH_CONFED)) { int aspath_hops; aspath_hops = aspath_count_hops(newattr->aspath); aspath_hops += aspath_count_confeds(newattr->aspath); if (aspath_hops < (exist_hops + exist_confeds)) { + *reason = bgp_path_selection_confed_as_path; if (debug) zlog_debug( "%s: %s wins over %s due to aspath (with confeds) hopcount %d < %d", @@ -665,6 +837,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, } if (aspath_hops > (exist_hops + exist_confeds)) { + *reason = bgp_path_selection_confed_as_path; if (debug) zlog_debug( "%s: %s loses to %s due to aspath (with confeds) hopcount %d > %d", @@ -677,6 +850,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, int newhops = aspath_count_hops(newattr->aspath); if (newhops < exist_hops) { + *reason = bgp_path_selection_as_path; if (debug) zlog_debug( "%s: %s wins over %s due to aspath hopcount %d < %d", @@ -686,6 +860,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, } if (newhops > exist_hops) { + *reason = bgp_path_selection_as_path; if (debug) zlog_debug( "%s: %s loses to %s due to aspath hopcount %d > %d", @@ -698,6 +873,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, /* 5. Origin check. */ if (newattr->origin < existattr->origin) { + *reason = bgp_path_selection_origin; if (debug) zlog_debug("%s: %s wins over %s due to ORIGIN %s < %s", pfx_buf, new_buf, exist_buf, @@ -707,6 +883,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, } if (newattr->origin > existattr->origin) { + *reason = bgp_path_selection_origin; if (debug) zlog_debug("%s: %s loses to %s due to ORIGIN %s > %s", pfx_buf, new_buf, exist_buf, @@ -723,8 +900,8 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, && aspath_count_hops(newattr->aspath) == 0 && aspath_count_hops(existattr->aspath) == 0); - if (bgp_flag_check(bgp, BGP_FLAG_ALWAYS_COMPARE_MED) - || (bgp_flag_check(bgp, BGP_FLAG_MED_CONFED) && confed_as_route) + if (CHECK_FLAG(bgp->flags, BGP_FLAG_ALWAYS_COMPARE_MED) + || (CHECK_FLAG(bgp->flags, BGP_FLAG_MED_CONFED) && confed_as_route) || aspath_cmp_left(newattr->aspath, existattr->aspath) || aspath_cmp_left_confed(newattr->aspath, existattr->aspath) || internal_as_route) { @@ -732,6 +909,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, exist_med = bgp_med_value(exist->attr, bgp); if (new_med < exist_med) { + *reason = bgp_path_selection_med; if (debug) zlog_debug( "%s: %s wins over %s due to MED %d < %d", @@ -741,6 +919,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, } if (new_med > exist_med) { + *reason = bgp_path_selection_med; if (debug) zlog_debug( "%s: %s loses to %s due to MED %d > %d", @@ -756,6 +935,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, if (new_sort == BGP_PEER_EBGP && (exist_sort == BGP_PEER_IBGP || exist_sort == BGP_PEER_CONFED)) { + *reason = bgp_path_selection_peer; if (debug) zlog_debug( "%s: %s wins over %s due to eBGP peer > iBGP peer", @@ -765,6 +945,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, if (exist_sort == BGP_PEER_EBGP && (new_sort == BGP_PEER_IBGP || new_sort == BGP_PEER_CONFED)) { + *reason = bgp_path_selection_peer; if (debug) zlog_debug( "%s: %s loses to %s due to iBGP peer < eBGP peer", @@ -801,8 +982,8 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, pair (newm, existm) with the cluster list length. Prefer the path with smaller cluster list length. */ if (newm == existm) { - if (peer_sort(new->peer) == BGP_PEER_IBGP - && peer_sort(exist->peer) == BGP_PEER_IBGP + if (peer_sort_lookup(new->peer) == BGP_PEER_IBGP + && peer_sort_lookup(exist->peer) == BGP_PEER_IBGP && (mpath_cfg == NULL || CHECK_FLAG( mpath_cfg->ibgp_flags, @@ -834,6 +1015,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, if (CHECK_FLAG(bgp->config, BGP_CONFIG_CONFEDERATION)) { if (new_sort == BGP_PEER_CONFED && exist_sort == BGP_PEER_IBGP) { + *reason = bgp_path_selection_confed; if (debug) zlog_debug( "%s: %s wins over %s due to confed-external peer > confed-internal peer", @@ -843,6 +1025,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, if (exist_sort == BGP_PEER_CONFED && new_sort == BGP_PEER_IBGP) { + *reason = bgp_path_selection_confed; if (debug) zlog_debug( "%s: %s loses to %s due to confed-internal peer < confed-external peer", @@ -863,8 +1046,8 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, zlog_debug( "%s: %s and %s cannot be multipath, one has a label while the other does not", pfx_buf, new_buf, exist_buf); - } else if (bgp_flag_check(bgp, - BGP_FLAG_ASPATH_MULTIPATH_RELAX)) { + } else if (CHECK_FLAG(bgp->flags, + BGP_FLAG_ASPATH_MULTIPATH_RELAX)) { /* * For the two paths, all comparison steps till IGP @@ -918,6 +1101,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, "%s: %s loses to %s after IGP metric comparison", pfx_buf, new_buf, exist_buf); } + *reason = bgp_path_selection_igp_metric; return ret; } @@ -925,9 +1109,10 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, first (the oldest one). This step minimizes route-flap, since a newer path won't displace an older one, even if it was the preferred route based on the additional decision criteria below. */ - if (!bgp_flag_check(bgp, BGP_FLAG_COMPARE_ROUTER_ID) + if (!CHECK_FLAG(bgp->flags, BGP_FLAG_COMPARE_ROUTER_ID) && new_sort == BGP_PEER_EBGP && exist_sort == BGP_PEER_EBGP) { if (CHECK_FLAG(new->flags, BGP_PATH_SELECTED)) { + *reason = bgp_path_selection_older; if (debug) zlog_debug( "%s: %s wins over %s due to oldest external", @@ -936,6 +1121,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, } if (CHECK_FLAG(exist->flags, BGP_PATH_SELECTED)) { + *reason = bgp_path_selection_older; if (debug) zlog_debug( "%s: %s loses to %s due to oldest external", @@ -959,6 +1145,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, exist_id.s_addr = exist->peer->remote_id.s_addr; if (ntohl(new_id.s_addr) < ntohl(exist_id.s_addr)) { + *reason = bgp_path_selection_router_id; if (debug) zlog_debug( "%s: %s wins over %s due to Router-ID comparison", @@ -967,6 +1154,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, } if (ntohl(new_id.s_addr) > ntohl(exist_id.s_addr)) { + *reason = bgp_path_selection_router_id; if (debug) zlog_debug( "%s: %s loses to %s due to Router-ID comparison", @@ -979,6 +1167,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, exist_cluster = BGP_CLUSTER_LIST_LENGTH(exist->attr); if (new_cluster < exist_cluster) { + *reason = bgp_path_selection_cluster_length; if (debug) zlog_debug( "%s: %s wins over %s due to CLUSTER_LIST length %d < %d", @@ -988,6 +1177,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, } if (new_cluster > exist_cluster) { + *reason = bgp_path_selection_cluster_length; if (debug) zlog_debug( "%s: %s loses to %s due to CLUSTER_LIST length %d > %d", @@ -1001,6 +1191,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, * valid peer information (as the connection may or may not be up). */ if (CHECK_FLAG(exist->flags, BGP_PATH_STALE)) { + *reason = bgp_path_selection_stale; if (debug) zlog_debug( "%s: %s wins over %s due to latter path being STALE", @@ -1009,6 +1200,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, } if (CHECK_FLAG(new->flags, BGP_PATH_STALE)) { + *reason = bgp_path_selection_stale; if (debug) zlog_debug( "%s: %s loses to %s due to former path being STALE", @@ -1017,14 +1209,19 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, } /* locally configured routes to advertise do not have su_remote */ - if (new->peer->su_remote == NULL) + if (new->peer->su_remote == NULL) { + *reason = bgp_path_selection_local_configured; return 0; - if (exist->peer->su_remote == NULL) + } + if (exist->peer->su_remote == NULL) { + *reason = bgp_path_selection_local_configured; return 1; + } ret = sockunion_cmp(new->peer->su_remote, exist->peer->su_remote); if (ret == 1) { + *reason = bgp_path_selection_neighbor_ip; if (debug) zlog_debug( "%s: %s loses to %s due to Neighor IP comparison", @@ -1033,6 +1230,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, } if (ret == -1) { + *reason = bgp_path_selection_neighbor_ip; if (debug) zlog_debug( "%s: %s wins over %s due to Neighor IP comparison", @@ -1040,6 +1238,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, return 1; } + *reason = bgp_path_selection_default; if (debug) zlog_debug("%s: %s wins over %s due to nothing left to compare", pfx_buf, new_buf, exist_buf); @@ -1047,18 +1246,30 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, return 1; } + +int bgp_evpn_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, + struct bgp_path_info *exist, int *paths_eq) +{ + enum bgp_path_selection_reason reason; + char pfx_buf[PREFIX2STR_BUFFER]; + + return bgp_path_info_cmp(bgp, new, exist, paths_eq, NULL, 0, pfx_buf, + AFI_L2VPN, SAFI_EVPN, &reason); +} + /* Compare two bgp route entity. Return -1 if new is preferred, 1 if exist * is preferred, or 0 if they are the same (usually will only occur if * multipath is enabled * This version is compatible with */ int bgp_path_info_cmp_compatible(struct bgp *bgp, struct bgp_path_info *new, struct bgp_path_info *exist, char *pfx_buf, - afi_t afi, safi_t safi) + afi_t afi, safi_t safi, + enum bgp_path_selection_reason *reason) { int paths_eq; int ret; ret = bgp_path_info_cmp(bgp, new, exist, &paths_eq, NULL, 0, pfx_buf, - afi, safi); + afi, safi, reason); if (paths_eq) ret = 0; @@ -1071,7 +1282,8 @@ int bgp_path_info_cmp_compatible(struct bgp *bgp, struct bgp_path_info *new, return ret; } -static enum filter_type bgp_input_filter(struct peer *peer, struct prefix *p, +static enum filter_type bgp_input_filter(struct peer *peer, + const struct prefix *p, struct attr *attr, afi_t afi, safi_t safi) { @@ -1110,7 +1322,8 @@ static enum filter_type bgp_input_filter(struct peer *peer, struct prefix *p, #undef FILTER_EXIST_WARN } -static enum filter_type bgp_output_filter(struct peer *peer, struct prefix *p, +static enum filter_type bgp_output_filter(struct peer *peer, + const struct prefix *p, struct attr *attr, afi_t afi, safi_t safi) { @@ -1151,30 +1364,30 @@ static enum filter_type bgp_output_filter(struct peer *peer, struct prefix *p, } /* If community attribute includes no_export then return 1. */ -static int bgp_community_filter(struct peer *peer, struct attr *attr) +static bool bgp_community_filter(struct peer *peer, struct attr *attr) { if (attr->community) { /* NO_ADVERTISE check. */ if (community_include(attr->community, COMMUNITY_NO_ADVERTISE)) - return 1; + return true; /* NO_EXPORT check. */ if (peer->sort == BGP_PEER_EBGP && community_include(attr->community, COMMUNITY_NO_EXPORT)) - return 1; + return true; /* NO_EXPORT_SUBCONFED check. */ if (peer->sort == BGP_PEER_EBGP || peer->sort == BGP_PEER_CONFED) if (community_include(attr->community, COMMUNITY_NO_EXPORT_SUBCONFED)) - return 1; + return true; } - return 0; + return false; } /* Route reflection loop check. */ -static int bgp_cluster_filter(struct peer *peer, struct attr *attr) +static bool bgp_cluster_filter(struct peer *peer, struct attr *attr) { struct in_addr cluster_id; @@ -1185,17 +1398,19 @@ static int bgp_cluster_filter(struct peer *peer, struct attr *attr) cluster_id = peer->bgp->router_id; if (cluster_loop_check(attr->cluster, cluster_id)) - return 1; + return true; } - return 0; + return false; } -static int bgp_input_modifier(struct peer *peer, struct prefix *p, +static int bgp_input_modifier(struct peer *peer, const struct prefix *p, struct attr *attr, afi_t afi, safi_t safi, - const char *rmap_name) + const char *rmap_name, mpls_label_t *label, + uint32_t num_labels, struct bgp_dest *dest) { struct bgp_filter *filter; - struct bgp_path_info rmap_path; + struct bgp_path_info rmap_path = { 0 }; + struct bgp_path_info_extra extra = { 0 }; route_map_result_t ret; struct route_map *rmap = NULL; @@ -1219,26 +1434,19 @@ static int bgp_input_modifier(struct peer *peer, struct prefix *p, } } - /* RFC 8212 to prevent route leaks. - * This specification intends to improve this situation by requiring the - * explicit configuration of both BGP Import and Export Policies for any - * External BGP (EBGP) session such as customers, peers, or - * confederation boundaries for all enabled address families. Through - * codification of the aforementioned requirement, operators will - * benefit from consistent behavior across different BGP - * implementations. - */ - if (peer->bgp->ebgp_requires_policy - == DEFAULT_EBGP_POLICY_ENABLED) - if (!bgp_inbound_policy_exists(peer, filter)) - return RMAP_DENY; - /* Route map apply. */ if (rmap) { memset(&rmap_path, 0, sizeof(struct bgp_path_info)); /* Duplicate current value to new strucutre for modification. */ rmap_path.peer = peer; rmap_path.attr = attr; + rmap_path.extra = &extra; + rmap_path.net = dest; + + extra.num_labels = num_labels; + if (label && num_labels && num_labels <= BGP_MAX_LABELS) + memcpy(extra.label, label, + num_labels * sizeof(mpls_label_t)); SET_FLAG(peer->rmap_type, PEER_RMAP_TYPE_IN); @@ -1253,7 +1461,7 @@ static int bgp_input_modifier(struct peer *peer, struct prefix *p, return RMAP_PERMIT; } -static int bgp_output_modifier(struct peer *peer, struct prefix *p, +static int bgp_output_modifier(struct peer *peer, const struct prefix *p, struct attr *attr, afi_t afi, safi_t safi, const char *rmap_name) { @@ -1329,7 +1537,7 @@ static void bgp_peer_remove_private_as(struct bgp *bgp, afi_t afi, safi_t safi, peer, afi, safi, PEER_FLAG_REMOVE_PRIVATE_AS_ALL_REPLACE)) attr->aspath = aspath_replace_private_asns( - attr->aspath, bgp->as); + attr->aspath, bgp->as, peer->as); // The entire aspath consists of private ASNs so create // an empty aspath @@ -1340,7 +1548,7 @@ static void bgp_peer_remove_private_as(struct bgp *bgp, afi_t afi, safi_t safi, // the private ASNs else attr->aspath = aspath_remove_private_asns( - attr->aspath); + attr->aspath, peer->as); } // 'all' was not specified so the entire aspath must be private @@ -1351,7 +1559,7 @@ static void bgp_peer_remove_private_as(struct bgp *bgp, afi_t afi, safi_t safi, peer, afi, safi, PEER_FLAG_REMOVE_PRIVATE_AS_REPLACE)) attr->aspath = aspath_replace_private_asns( - attr->aspath, bgp->as); + attr->aspath, bgp->as, peer->as); else attr->aspath = aspath_empty_get(); } @@ -1405,11 +1613,11 @@ void bgp_attr_add_gshut_community(struct attr *attr) } -static void subgroup_announce_reset_nhop(uint8_t family, struct attr *attr) +void subgroup_announce_reset_nhop(uint8_t family, struct attr *attr) { if (family == AF_INET) { - attr->nexthop.s_addr = 0; - attr->mp_nexthop_global_in.s_addr = 0; + attr->nexthop.s_addr = INADDR_ANY; + attr->mp_nexthop_global_in.s_addr = INADDR_ANY; } if (family == AF_INET6) memset(&attr->mp_nexthop_global, 0, IPV6_MAX_BYTELEN); @@ -1417,9 +1625,9 @@ static void subgroup_announce_reset_nhop(uint8_t family, struct attr *attr) memset(&attr->mp_nexthop_global_in, 0, BGP_ATTR_NHLEN_IPV4); } -int subgroup_announce_check(struct bgp_node *rn, struct bgp_path_info *pi, - struct update_subgroup *subgrp, struct prefix *p, - struct attr *attr) +bool subgroup_announce_check(struct bgp_dest *dest, struct bgp_path_info *pi, + struct update_subgroup *subgrp, + const struct prefix *p, struct attr *attr) { struct bgp_filter *filter; struct peer *from; @@ -1428,15 +1636,17 @@ int subgroup_announce_check(struct bgp_node *rn, struct bgp_path_info *pi, struct bgp *bgp; struct attr *piattr; char buf[PREFIX_STRLEN]; - int ret; + route_map_result_t ret; int transparent; int reflect; afi_t afi; safi_t safi; int samepeer_safe = 0; /* for synthetic mplsvpns routes */ + bool nh_reset = false; + uint64_t cum_bw; if (DISABLE_BGP_ANNOUNCE) - return 0; + return false; afi = SUBGRP_AFI(subgrp); safi = SUBGRP_SAFI(subgrp); @@ -1451,7 +1661,7 @@ int subgroup_announce_check(struct bgp_node *rn, struct bgp_path_info *pi, piattr = bgp_path_info_mpath_count(pi) ? bgp_path_info_mpath_attr(pi) : pi->attr; -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC if (((afi == AFI_IP) || (afi == AFI_IP6)) && (safi == SAFI_MPLS_VPN) && ((pi->type == ZEBRA_ROUTE_BGP_DIRECT) || (pi->type == ZEBRA_ROUTE_BGP_DIRECT_EXT))) { @@ -1483,7 +1693,7 @@ int subgroup_announce_check(struct bgp_node *rn, struct bgp_path_info *pi, if (!CHECK_FLAG(pi->flags, BGP_PATH_VALID) || CHECK_FLAG(pi->flags, BGP_PATH_HISTORY) || CHECK_FLAG(pi->flags, BGP_PATH_REMOVED)) { - return 0; + return false; } /* If this is not the bestpath then check to see if there is an enabled @@ -1491,14 +1701,14 @@ int subgroup_announce_check(struct bgp_node *rn, struct bgp_path_info *pi, * feature that requires us to advertise it */ if (!CHECK_FLAG(pi->flags, BGP_PATH_SELECTED)) { if (!bgp_addpath_tx_path(peer->addpath_type[afi][safi], pi)) { - return 0; + return false; } } /* Aggregate-address suppress check. */ if (pi->extra && pi->extra->suppress) if (!UNSUPPRESS_MAP_NAME(filter)) { - return 0; + return false; } /* @@ -1509,26 +1719,25 @@ int subgroup_announce_check(struct bgp_node *rn, struct bgp_path_info *pi, */ if (safi == SAFI_MPLS_VPN && pi->extra && pi->extra->num_labels && pi->extra->label[0] == BGP_PREVENT_VRF_2_VRF_LEAK) - return 0; + return false; /* If it's labeled safi, make sure the route has a valid label. */ if (safi == SAFI_LABELED_UNICAST) { - mpls_label_t label = bgp_adv_label(rn, pi, peer, afi, safi); + mpls_label_t label = bgp_adv_label(dest, pi, peer, afi, safi); if (!bgp_is_valid_label(&label)) { if (bgp_debug_update(NULL, p, subgrp->update_group, 0)) - zlog_debug("u%" PRIu64 ":s%" PRIu64 - " %s/%d is filtered - no label (%p)", + zlog_debug("u%" PRIu64 ":s%" PRIu64" %s/%d is filtered - no label (%p)", subgrp->update_group->id, subgrp->id, inet_ntop(p->family, &p->u.prefix, buf, SU_ADDRSTRLEN), p->prefixlen, &label); - return 0; + return false; } } /* Do not send back route to sender. */ if (onlypeer && from == onlypeer) { - return 0; + return false; } /* Do not send the default route in the BGP table if the neighbor is @@ -1536,9 +1745,9 @@ int subgroup_announce_check(struct bgp_node *rn, struct bgp_path_info *pi, if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_DEFAULT_ORIGINATE)) { if (p->family == AF_INET && p->u.prefix4.s_addr == INADDR_ANY) - return 0; + return false; else if (p->family == AF_INET6 && p->prefixlen == 0) - return 0; + return false; } /* Transparency check. */ @@ -1553,7 +1762,7 @@ int subgroup_announce_check(struct bgp_node *rn, struct bgp_path_info *pi, if (bgp_debug_update(NULL, p, subgrp->update_group, 0)) zlog_debug( "subgrpannouncecheck: community filter check fail"); - return 0; + return false; } /* If the attribute has originator-id and it is same as remote @@ -1562,11 +1771,10 @@ int subgroup_announce_check(struct bgp_node *rn, struct bgp_path_info *pi, && (IPV4_ADDR_SAME(&onlypeer->remote_id, &piattr->originator_id))) { if (bgp_debug_update(NULL, p, subgrp->update_group, 0)) zlog_debug( - "%s [Update:SEND] %s originator-id is same as " - "remote router-id", + "%s [Update:SEND] %s originator-id is same as remote router-id", onlypeer->host, prefix2str(p, buf, sizeof(buf))); - return 0; + return false; } /* ORF prefix-list filter check */ @@ -1584,7 +1792,7 @@ int subgroup_announce_check(struct bgp_node *rn, struct bgp_path_info *pi, peer->host, prefix2str(p, buf, sizeof(buf))); - return 0; + return false; } } @@ -1593,30 +1801,27 @@ int subgroup_announce_check(struct bgp_node *rn, struct bgp_path_info *pi, if (bgp_debug_update(NULL, p, subgrp->update_group, 0)) zlog_debug("%s [Update:SEND] %s is filtered", peer->host, prefix2str(p, buf, sizeof(buf))); - return 0; + return false; } -#ifdef BGP_SEND_ASPATH_CHECK /* AS path loop check. */ - if (onlypeer && aspath_loop_check(piattr->aspath, onlypeer->as)) { + if (onlypeer && onlypeer->as_path_loop_detection + && aspath_loop_check(piattr->aspath, onlypeer->as)) { if (bgp_debug_update(NULL, p, subgrp->update_group, 0)) zlog_debug( - "%s [Update:SEND] suppress announcement to peer AS %u " - "that is part of AS path.", + "%s [Update:SEND] suppress announcement to peer AS %u that is part of AS path.", onlypeer->host, onlypeer->as); - return 0; + return false; } -#endif /* BGP_SEND_ASPATH_CHECK */ /* If we're a CONFED we need to loop check the CONFED ID too */ if (CHECK_FLAG(bgp->config, BGP_CONFIG_CONFEDERATION)) { if (aspath_loop_check(piattr->aspath, bgp->confed_id)) { if (bgp_debug_update(NULL, p, subgrp->update_group, 0)) zlog_debug( - "%s [Update:SEND] suppress announcement to peer AS %u" - " is AS path.", + "%s [Update:SEND] suppress announcement to peer AS %u is AS path.", peer->host, bgp->confed_id); - return 0; + return false; } } @@ -1636,21 +1841,22 @@ int subgroup_announce_check(struct bgp_node *rn, struct bgp_path_info *pi, check is already done. So there is noting to do. */ /* no bgp client-to-client reflection check. */ - if (bgp_flag_check(bgp, BGP_FLAG_NO_CLIENT_TO_CLIENT)) + if (CHECK_FLAG(bgp->flags, + BGP_FLAG_NO_CLIENT_TO_CLIENT)) if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_REFLECTOR_CLIENT)) - return 0; + return false; } else { /* A route from a Non-client peer. Reflect to all other clients. */ if (!CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_REFLECTOR_CLIENT)) - return 0; + return false; } } /* For modify attribute, copy it to temporary structure. */ - bgp_attr_dup(attr, piattr); + *attr = *piattr; /* If local-preference is not set. */ if ((peer->sort == BGP_PEER_IBGP || peer->sort == BGP_PEER_CONFED) @@ -1708,18 +1914,22 @@ int subgroup_announce_check(struct bgp_node *rn, struct bgp_path_info *pi, * if * the peer (group) is configured to receive link-local nexthop * unchanged - * and it is available in the prefix OR we're not reflecting the route - * and + * and it is available in the prefix OR we're not reflecting the route, + * link-local nexthop address is valid and * the peer (group) to whom we're going to announce is on a shared * network * and this is either a self-originated route or the peer is EBGP. + * By checking if nexthop LL address is valid we are sure that + * we do not announce LL address as `::`. */ if (NEXTHOP_IS_V6) { attr->mp_nexthop_len = BGP_ATTR_NHLEN_IPV6_GLOBAL; if ((CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED) && IN6_IS_ADDR_LINKLOCAL(&attr->mp_nexthop_local)) - || (!reflect && peer->shared_network + || (!reflect + && IN6_IS_ADDR_LINKLOCAL(&peer->nexthop.v6_local) + && peer->shared_network && (from == bgp->peer_self || peer->sort == BGP_PEER_EBGP))) { attr->mp_nexthop_len = @@ -1741,19 +1951,13 @@ int subgroup_announce_check(struct bgp_node *rn, struct bgp_path_info *pi, /* Route map & unsuppress-map apply. */ if (ROUTE_MAP_OUT_NAME(filter) || (pi->extra && pi->extra->suppress)) { - struct bgp_path_info rmap_path; - struct bgp_path_info_extra dummy_rmap_path_extra; - struct attr dummy_attr; - - memset(&rmap_path, 0, sizeof(struct bgp_path_info)); - rmap_path.peer = peer; - rmap_path.attr = attr; + struct bgp_path_info rmap_path = {0}; + struct bgp_path_info_extra dummy_rmap_path_extra = {0}; + struct attr dummy_attr = {0}; - if (pi->extra) { - memcpy(&dummy_rmap_path_extra, pi->extra, - sizeof(struct bgp_path_info_extra)); - rmap_path.extra = &dummy_rmap_path_extra; - } + /* Fill temp path_info */ + prep_for_rmap_apply(&rmap_path, &dummy_rmap_path_extra, dest, + pi, peer, attr); /* don't confuse inbound and outbound setting */ RESET_FLAG(attr->rmap_change_flags); @@ -1763,9 +1967,9 @@ int subgroup_announce_check(struct bgp_node *rn, struct bgp_path_info *pi, * of the reflected IBGP routes unless explicitly allowed. */ if ((from->sort == BGP_PEER_IBGP && peer->sort == BGP_PEER_IBGP) - && !bgp_flag_check(bgp, - BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY)) { - bgp_attr_dup(&dummy_attr, attr); + && !CHECK_FLAG(bgp->flags, + BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY)) { + dummy_attr = *attr; rmap_path.attr = &dummy_attr; } @@ -1781,8 +1985,12 @@ int subgroup_announce_check(struct bgp_node *rn, struct bgp_path_info *pi, peer->rmap_type = 0; if (ret == RMAP_DENYMATCH) { + if (bgp_debug_update(NULL, p, subgrp->update_group, 0)) + zlog_debug("%s [Update:SEND] %s is filtered by route-map", + peer->host, prefix2str(p, buf, sizeof(buf))); + bgp_attr_flush(attr); - return 0; + return false; } } @@ -1795,12 +2003,25 @@ int subgroup_announce_check(struct bgp_node *rn, struct bgp_path_info *pi, * benefit from consistent behavior across different BGP * implementations. */ - if (peer->bgp->ebgp_requires_policy - == DEFAULT_EBGP_POLICY_ENABLED) + if (CHECK_FLAG(bgp->flags, BGP_FLAG_EBGP_REQUIRES_POLICY)) if (!bgp_outbound_policy_exists(peer, filter)) - return 0; + return false; + + /* draft-ietf-idr-deprecate-as-set-confed-set + * Filter routes having AS_SET or AS_CONFED_SET in the path. + * Eventually, This document (if approved) updates RFC 4271 + * and RFC 5065 by eliminating AS_SET and AS_CONFED_SET types, + * and obsoletes RFC 6472. + */ + if (peer->bgp->reject_as_sets) + if (aspath_check_as_sets(attr->aspath)) + return false; + + /* Codification of AS 0 Processing */ + if (aspath_check_as_zero(attr->aspath)) + return false; - if (bgp_flag_check(bgp, BGP_FLAG_GRACEFUL_SHUTDOWN)) { + if (CHECK_FLAG(bgp->flags, BGP_FLAG_GRACEFUL_SHUTDOWN)) { if (peer->sort == BGP_PEER_IBGP || peer->sort == BGP_PEER_CONFED) { attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF); @@ -1841,12 +2062,14 @@ int subgroup_announce_check(struct bgp_node *rn, struct bgp_path_info *pi, PEER_FLAG_FORCE_NEXTHOP_SELF)) { if (!reflect || CHECK_FLAG(peer->af_flags[afi][safi], - PEER_FLAG_FORCE_NEXTHOP_SELF)) + PEER_FLAG_FORCE_NEXTHOP_SELF)) { subgroup_announce_reset_nhop( (peer_cap_enhe(peer, afi, safi) ? AF_INET6 : p->family), attr); + nh_reset = true; + } } else if (peer->sort == BGP_PEER_EBGP) { /* Can also reset the nexthop if announcing to EBGP, but * only if @@ -1854,13 +2077,32 @@ int subgroup_announce_check(struct bgp_node *rn, struct bgp_path_info *pi, * Note: 3rd party nexthop currently implemented for * IPv4 only. */ - if (!bgp_subgrp_multiaccess_check_v4(piattr->nexthop, - subgrp)) + if ((p->family == AF_INET) && + (!bgp_subgrp_multiaccess_check_v4( + piattr->nexthop, + subgrp, from))) { subgroup_announce_reset_nhop( (peer_cap_enhe(peer, afi, safi) ? AF_INET6 : p->family), - attr); + attr); + nh_reset = true; + } + + if ((p->family == AF_INET6) && + (!bgp_subgrp_multiaccess_check_v6( + piattr->mp_nexthop_global, + subgrp, from))) { + subgroup_announce_reset_nhop( + (peer_cap_enhe(peer, afi, safi) + ? AF_INET6 + : p->family), + attr); + nh_reset = true; + } + + + } else if (CHECK_FLAG(pi->flags, BGP_PATH_ANNC_NH_SELF)) { /* * This flag is used for leaked vpn-vrf routes @@ -1875,26 +2117,66 @@ int subgroup_announce_check(struct bgp_node *rn, struct bgp_path_info *pi, "%s: BGP_PATH_ANNC_NH_SELF, family=%s", __func__, family2str(family)); subgroup_announce_reset_nhop(family, attr); + nh_reset = true; } + } - /* If IPv6/MP and nexthop does not have any override and happens - * to - * be a link-local address, reset it so that we don't pass along - * the - * source's link-local IPv6 address to recipients who may not be - * on - * the same interface. - */ - if (p->family == AF_INET6 || peer_cap_enhe(peer, afi, safi)) { - if (IN6_IS_ADDR_LINKLOCAL(&attr->mp_nexthop_global)) - subgroup_announce_reset_nhop(AF_INET6, attr); - } + /* If IPv6/MP and nexthop does not have any override and happens + * to + * be a link-local address, reset it so that we don't pass along + * the + * source's link-local IPv6 address to recipients who may not be + * on + * the same interface. + */ + if (p->family == AF_INET6 || peer_cap_enhe(peer, afi, safi)) { + if (IN6_IS_ADDR_LINKLOCAL(&attr->mp_nexthop_global)) { + subgroup_announce_reset_nhop(AF_INET6, attr); + nh_reset = true; + } } - return 1; + /* + * When the next hop is set to ourselves, if all multipaths have + * link-bandwidth announce the cumulative bandwidth as that makes + * the most sense. However, don't modify if the link-bandwidth has + * been explicitly set by user policy. + */ + if (nh_reset && + bgp_path_info_mpath_chkwtd(bgp, pi) && + (cum_bw = bgp_path_info_mpath_cumbw(pi)) != 0 && + !CHECK_FLAG(attr->rmap_change_flags, BATTR_RMAP_LINK_BW_SET)) + attr->ecommunity = ecommunity_replace_linkbw( + bgp->as, attr->ecommunity, cum_bw); + + return true; +} + +static int bgp_route_select_timer_expire(struct thread *thread) +{ + struct afi_safi_info *info; + afi_t afi; + safi_t safi; + struct bgp *bgp; + + info = THREAD_ARG(thread); + afi = info->afi; + safi = info->safi; + bgp = info->bgp; + + if (BGP_DEBUG(update, UPDATE_OUT)) + zlog_debug("afi %d, safi %d : route select timer expired", afi, + safi); + + bgp->gr_info[afi][safi].t_route_select = NULL; + + XFREE(MTYPE_TMP, info); + + /* Best path selection */ + return bgp_best_path_select_defer(bgp, afi, safi); } -void bgp_best_selection(struct bgp *bgp, struct bgp_node *rn, +void bgp_best_selection(struct bgp *bgp, struct bgp_dest *dest, struct bgp_maxpaths_cfg *mpath_cfg, struct bgp_path_info_pair *result, afi_t afi, safi_t safi) @@ -1914,22 +2196,23 @@ void bgp_best_selection(struct bgp *bgp, struct bgp_node *rn, do_mpath = (mpath_cfg->maxpaths_ebgp > 1 || mpath_cfg->maxpaths_ibgp > 1); - debug = bgp_debug_bestpath(&rn->p); + debug = bgp_debug_bestpath(dest); if (debug) - prefix2str(&rn->p, pfx_buf, sizeof(pfx_buf)); + prefix2str(bgp_dest_get_prefix(dest), pfx_buf, sizeof(pfx_buf)); + dest->reason = bgp_path_selection_none; /* bgp deterministic-med */ new_select = NULL; - if (bgp_flag_check(bgp, BGP_FLAG_DETERMINISTIC_MED)) { + if (CHECK_FLAG(bgp->flags, BGP_FLAG_DETERMINISTIC_MED)) { /* Clear BGP_PATH_DMED_SELECTED for all paths */ - for (pi1 = bgp_node_get_bgp_path_info(rn); pi1; + for (pi1 = bgp_dest_get_bgp_path_info(dest); pi1; pi1 = pi1->next) - bgp_path_info_unset_flag(rn, pi1, + bgp_path_info_unset_flag(dest, pi1, BGP_PATH_DMED_SELECTED); - for (pi1 = bgp_node_get_bgp_path_info(rn); pi1; + for (pi1 = bgp_dest_get_bgp_path_info(dest); pi1; pi1 = pi1->next) { if (CHECK_FLAG(pi1->flags, BGP_PATH_DMED_CHECK)) continue; @@ -1965,20 +2248,21 @@ void bgp_best_selection(struct bgp *bgp, struct bgp_node *rn, if (bgp_path_info_cmp( bgp, pi2, new_select, &paths_eq, mpath_cfg, debug, - pfx_buf, afi, safi)) { + pfx_buf, afi, safi, + &dest->reason)) { bgp_path_info_unset_flag( - rn, new_select, + dest, new_select, BGP_PATH_DMED_SELECTED); new_select = pi2; } bgp_path_info_set_flag( - rn, pi2, BGP_PATH_DMED_CHECK); + dest, pi2, BGP_PATH_DMED_CHECK); } } - bgp_path_info_set_flag(rn, new_select, + bgp_path_info_set_flag(dest, new_select, BGP_PATH_DMED_CHECK); - bgp_path_info_set_flag(rn, new_select, + bgp_path_info_set_flag(dest, new_select, BGP_PATH_DMED_SELECTED); if (debug) { @@ -1995,8 +2279,10 @@ void bgp_best_selection(struct bgp *bgp, struct bgp_node *rn, /* Check old selected route and new selected route. */ old_select = NULL; new_select = NULL; - for (pi = bgp_node_get_bgp_path_info(rn); + for (pi = bgp_dest_get_bgp_path_info(dest); (pi != NULL) && (nextpi = pi->next, 1); pi = nextpi) { + enum bgp_path_selection_reason reason; + if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED)) old_select = pi; @@ -2006,7 +2292,7 @@ void bgp_best_selection(struct bgp *bgp, struct bgp_node *rn, */ if (CHECK_FLAG(pi->flags, BGP_PATH_REMOVED) && (pi != old_select)) - bgp_path_info_reap(rn, pi); + bgp_path_info_reap(dest, pi); if (debug) zlog_debug("%s: pi %p in holddown", __func__, @@ -2027,18 +2313,23 @@ void bgp_best_selection(struct bgp *bgp, struct bgp_node *rn, continue; } - if (bgp_flag_check(bgp, BGP_FLAG_DETERMINISTIC_MED) + if (CHECK_FLAG(bgp->flags, BGP_FLAG_DETERMINISTIC_MED) && (!CHECK_FLAG(pi->flags, BGP_PATH_DMED_SELECTED))) { - bgp_path_info_unset_flag(rn, pi, BGP_PATH_DMED_CHECK); + bgp_path_info_unset_flag(dest, pi, BGP_PATH_DMED_CHECK); if (debug) zlog_debug("%s: pi %p dmed", __func__, pi); continue; } - bgp_path_info_unset_flag(rn, pi, BGP_PATH_DMED_CHECK); + bgp_path_info_unset_flag(dest, pi, BGP_PATH_DMED_CHECK); + reason = dest->reason; if (bgp_path_info_cmp(bgp, pi, new_select, &paths_eq, mpath_cfg, - debug, pfx_buf, afi, safi)) { + debug, pfx_buf, afi, safi, + &dest->reason)) { + if (new_select == NULL && + reason != bgp_path_selection_none) + dest->reason = reason; new_select = pi; } } @@ -2052,7 +2343,7 @@ void bgp_best_selection(struct bgp *bgp, struct bgp_node *rn, bgp_path_info_path_with_addpath_rx_str(new_select, path_buf); else - sprintf(path_buf, "NONE"); + snprintf(path_buf, sizeof(path_buf), "NONE"); zlog_debug( "%s: After path selection, newbest is %s oldbest was %s", pfx_buf, path_buf, @@ -2060,7 +2351,7 @@ void bgp_best_selection(struct bgp *bgp, struct bgp_node *rn, } if (do_mpath && new_select) { - for (pi = bgp_node_get_bgp_path_info(rn); + for (pi = bgp_dest_get_bgp_path_info(dest); (pi != NULL) && (nextpi = pi->next, 1); pi = nextpi) { if (debug) @@ -2094,7 +2385,8 @@ void bgp_best_selection(struct bgp *bgp, struct bgp_node *rn, } bgp_path_info_cmp(bgp, pi, new_select, &paths_eq, - mpath_cfg, debug, pfx_buf, afi, safi); + mpath_cfg, debug, pfx_buf, afi, safi, + &dest->reason); if (paths_eq) { if (debug) @@ -2106,12 +2398,12 @@ void bgp_best_selection(struct bgp *bgp, struct bgp_node *rn, } } - bgp_path_info_mpath_update(rn, new_select, old_select, &mp_list, + bgp_path_info_mpath_update(dest, new_select, old_select, &mp_list, mpath_cfg); bgp_path_info_mpath_aggregate_update(new_select, old_select); bgp_mp_list_clear(&mp_list); - bgp_addpath_update_ids(bgp, rn, afi, safi); + bgp_addpath_update_ids(bgp, dest, afi, safi); result->old = old_select; result->new = new_select; @@ -2123,18 +2415,18 @@ void bgp_best_selection(struct bgp *bgp, struct bgp_node *rn, * A new route/change in bestpath of an existing route. Evaluate the path * for advertisement to the subgroup. */ -int subgroup_process_announce_selected(struct update_subgroup *subgrp, - struct bgp_path_info *selected, - struct bgp_node *rn, - uint32_t addpath_tx_id) +void subgroup_process_announce_selected(struct update_subgroup *subgrp, + struct bgp_path_info *selected, + struct bgp_dest *dest, + uint32_t addpath_tx_id) { - struct prefix *p; + const struct prefix *p; struct peer *onlypeer; struct attr attr; afi_t afi; safi_t safi; - p = &rn->p; + p = bgp_dest_get_prefix(dest); afi = SUBGRP_AFI(subgrp); safi = SUBGRP_SAFI(subgrp); onlypeer = ((SUBGRP_PCOUNT(subgrp) == 1) ? (SUBGRP_PFIRST(subgrp))->peer @@ -2150,7 +2442,7 @@ int subgroup_process_announce_selected(struct update_subgroup *subgrp, /* First update is deferred until ORF or ROUTE-REFRESH is received */ if (onlypeer && CHECK_FLAG(onlypeer->af_sflags[afi][safi], PEER_STATUS_ORF_WAIT_REFRESH)) - return 0; + return; memset(&attr, 0, sizeof(struct attr)); /* It's initialized in bgp_announce_check() */ @@ -2158,30 +2450,28 @@ int subgroup_process_announce_selected(struct update_subgroup *subgrp, /* Announcement to the subgroup. If the route is filtered withdraw it. */ if (selected) { - if (subgroup_announce_check(rn, selected, subgrp, p, &attr)) - bgp_adj_out_set_subgroup(rn, subgrp, &attr, selected); + if (subgroup_announce_check(dest, selected, subgrp, p, &attr)) + bgp_adj_out_set_subgroup(dest, subgrp, &attr, selected); else - bgp_adj_out_unset_subgroup(rn, subgrp, 1, + bgp_adj_out_unset_subgroup(dest, subgrp, 1, addpath_tx_id); } /* If selected is NULL we must withdraw the path using addpath_tx_id */ else { - bgp_adj_out_unset_subgroup(rn, subgrp, 1, addpath_tx_id); + bgp_adj_out_unset_subgroup(dest, subgrp, 1, addpath_tx_id); } - - return 0; } /* * Clear IGP changed flag and attribute changed flag for a route (all paths). * This is called at the end of route processing. */ -void bgp_zebra_clear_route_change_flags(struct bgp_node *rn) +void bgp_zebra_clear_route_change_flags(struct bgp_dest *dest) { struct bgp_path_info *pi; - for (pi = bgp_node_get_bgp_path_info(rn); pi; pi = pi->next) { + for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) { if (BGP_PATH_HOLDDOWN(pi)) continue; UNSET_FLAG(pi->flags, BGP_PATH_IGP_CHANGED); @@ -2194,8 +2484,7 @@ void bgp_zebra_clear_route_change_flags(struct bgp_node *rn) * if the route selection returns the same best route as earlier - to * determine if we need to update zebra or not. */ -int bgp_zebra_has_route_changed(struct bgp_node *rn, - struct bgp_path_info *selected) +bool bgp_zebra_has_route_changed(struct bgp_path_info *selected) { struct bgp_path_info *mpinfo; @@ -2206,8 +2495,9 @@ int bgp_zebra_has_route_changed(struct bgp_node *rn, * when the best path has an attribute change anyway. */ if (CHECK_FLAG(selected->flags, BGP_PATH_IGP_CHANGED) - || CHECK_FLAG(selected->flags, BGP_PATH_MULTIPATH_CHG)) - return 1; + || CHECK_FLAG(selected->flags, BGP_PATH_MULTIPATH_CHG) + || CHECK_FLAG(selected->flags, BGP_PATH_LINK_BW_CHG)) + return true; /* * If this is multipath, check all selected paths for any nexthop change @@ -2216,21 +2506,69 @@ int bgp_zebra_has_route_changed(struct bgp_node *rn, mpinfo = bgp_path_info_mpath_next(mpinfo)) { if (CHECK_FLAG(mpinfo->flags, BGP_PATH_IGP_CHANGED) || CHECK_FLAG(mpinfo->flags, BGP_PATH_ATTR_CHANGED)) - return 1; + return true; } /* Nothing has changed from the RIB's perspective. */ - return 0; + return false; } struct bgp_process_queue { struct bgp *bgp; - STAILQ_HEAD(, bgp_node) pqueue; + STAILQ_HEAD(, bgp_dest) pqueue; #define BGP_PROCESS_QUEUE_EOIU_MARKER (1 << 0) unsigned int flags; unsigned int queued; }; +static void bgp_process_evpn_route_injection(struct bgp *bgp, afi_t afi, + safi_t safi, struct bgp_dest *dest, + struct bgp_path_info *new_select, + struct bgp_path_info *old_select) +{ + const struct prefix *p = bgp_dest_get_prefix(dest); + + if ((afi != AFI_IP && afi != AFI_IP6) || (safi != SAFI_UNICAST)) + return; + + if (advertise_type5_routes(bgp, afi) && new_select + && is_route_injectable_into_evpn(new_select)) { + + /* apply the route-map */ + if (bgp->adv_cmd_rmap[afi][safi].map) { + route_map_result_t ret; + struct bgp_path_info rmap_path; + struct bgp_path_info_extra rmap_path_extra; + struct attr dummy_attr; + + dummy_attr = *new_select->attr; + + /* Fill temp path_info */ + prep_for_rmap_apply(&rmap_path, &rmap_path_extra, dest, + new_select, new_select->peer, + &dummy_attr); + + RESET_FLAG(dummy_attr.rmap_change_flags); + + ret = route_map_apply(bgp->adv_cmd_rmap[afi][safi].map, + p, RMAP_BGP, &rmap_path); + + if (ret == RMAP_DENYMATCH) { + bgp_attr_flush(&dummy_attr); + bgp_evpn_withdraw_type5_route(bgp, p, afi, + safi); + } else + bgp_evpn_advertise_type5_route( + bgp, p, &dummy_attr, afi, safi); + } else { + bgp_evpn_advertise_type5_route(bgp, p, new_select->attr, + afi, safi); + } + } else if (advertise_type5_routes(bgp, afi) && old_select + && is_route_injectable_into_evpn(old_select)) + bgp_evpn_withdraw_type5_route(bgp, p, afi, safi); +} + /* * old_select = The old best path * new_select = the new best path @@ -2250,17 +2588,25 @@ struct bgp_process_queue { * We have no eligible route that we can announce or the rn * is being removed. */ -static void bgp_process_main_one(struct bgp *bgp, struct bgp_node *rn, +static void bgp_process_main_one(struct bgp *bgp, struct bgp_dest *dest, afi_t afi, safi_t safi) { struct bgp_path_info *new_select; struct bgp_path_info *old_select; struct bgp_path_info_pair old_and_new; - char pfx_buf[PREFIX2STR_BUFFER]; int debug = 0; + if (CHECK_FLAG(bgp->flags, BGP_FLAG_DELETE_IN_PROGRESS)) { + if (dest) + debug = bgp_debug_bestpath(dest); + if (debug) + zlog_debug( + "%s: bgp delete in progress, ignoring event, p=%pRN", + __func__, dest); + return; + } /* Is it end of initial update? (after startup) */ - if (!rn) { + if (!dest) { quagga_timestamp(3, bgp->update_delay_zebra_resume_time, sizeof(bgp->update_delay_zebra_resume_time)); @@ -2275,17 +2621,24 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_node *rn, return; } - struct prefix *p = &rn->p; + const struct prefix *p = bgp_dest_get_prefix(dest); - debug = bgp_debug_bestpath(&rn->p); - if (debug) { - prefix2str(&rn->p, pfx_buf, sizeof(pfx_buf)); - zlog_debug("%s: p=%s afi=%s, safi=%s start", __func__, pfx_buf, + debug = bgp_debug_bestpath(dest); + if (debug) + zlog_debug("%s: p=%pRN afi=%s, safi=%s start", __func__, dest, afi2str(afi), safi2str(safi)); + + /* The best path calculation for the route is deferred if + * BGP_NODE_SELECT_DEFER is set + */ + if (CHECK_FLAG(dest->flags, BGP_NODE_SELECT_DEFER)) { + if (BGP_DEBUG(update, UPDATE_OUT)) + zlog_debug("SELECT_DEFER flag set for route %p", dest); + return; } /* Best path selection. */ - bgp_best_selection(bgp, rn, &bgp->maxpaths[afi][safi], &old_and_new, + bgp_best_selection(bgp, dest, &bgp->maxpaths[afi][safi], &old_and_new, afi, safi); old_select = old_and_new.old; new_select = old_and_new.new; @@ -2302,7 +2655,7 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_node *rn, if (!old_select || bgp_label_index_differs(new_select, old_select) || new_select->sub_type != old_select->sub_type - || !bgp_is_valid_label(&rn->local_label)) { + || !bgp_is_valid_label(&dest->local_label)) { /* Enforced penultimate hop popping: * implicit-null for local routes, aggregate * and redistributed routes @@ -2313,40 +2666,39 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_node *rn, || new_select->sub_type == BGP_ROUTE_REDISTRIBUTE) { if (CHECK_FLAG( - rn->flags, + dest->flags, BGP_NODE_REGISTERED_FOR_LABEL)) - bgp_unregister_for_label(rn); + bgp_unregister_for_label(dest); label_ntop(MPLS_LABEL_IMPLICIT_NULL, 1, - &rn->local_label); - bgp_set_valid_label(&rn->local_label); + &dest->local_label); + bgp_set_valid_label(&dest->local_label); } else - bgp_register_for_label(rn, new_select); + bgp_register_for_label(dest, + new_select); } - } else if (CHECK_FLAG(rn->flags, + } else if (CHECK_FLAG(dest->flags, BGP_NODE_REGISTERED_FOR_LABEL)) { - bgp_unregister_for_label(rn); + bgp_unregister_for_label(dest); } - } else if (CHECK_FLAG(rn->flags, BGP_NODE_REGISTERED_FOR_LABEL)) { - bgp_unregister_for_label(rn); + } else if (CHECK_FLAG(dest->flags, BGP_NODE_REGISTERED_FOR_LABEL)) { + bgp_unregister_for_label(dest); } - if (debug) { - prefix2str(&rn->p, pfx_buf, sizeof(pfx_buf)); + if (debug) zlog_debug( - "%s: p=%s afi=%s, safi=%s, old_select=%p, new_select=%p", - __func__, pfx_buf, afi2str(afi), safi2str(safi), + "%s: p=%pRN afi=%s, safi=%s, old_select=%p, new_select=%p", + __func__, dest, afi2str(afi), safi2str(safi), old_select, new_select); - } /* If best route remains the same and this is not due to user-initiated * clear, see exactly what needs to be done. */ if (old_select && old_select == new_select - && !CHECK_FLAG(rn->flags, BGP_NODE_USER_CLEAR) + && !CHECK_FLAG(dest->flags, BGP_NODE_USER_CLEAR) && !CHECK_FLAG(old_select->flags, BGP_PATH_ATTR_CHANGED) && !bgp_addpath_is_addpath_used(&bgp->tx_addpath, afi, safi)) { - if (bgp_zebra_has_route_changed(rn, old_select)) { -#if ENABLE_BGP_VNC + if (bgp_zebra_has_route_changed(old_select)) { +#ifdef ENABLE_BGP_VNC vnc_import_bgp_add_route(bgp, p, old_select); vnc_import_bgp_exterior_add_route(bgp, p, old_select); #endif @@ -2358,41 +2710,49 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_node *rn, || new_select->sub_type == BGP_ROUTE_IMPORTED)) - bgp_zebra_announce(rn, p, old_select, + bgp_zebra_announce(dest, p, old_select, bgp, afi, safi); } } - UNSET_FLAG(old_select->flags, BGP_PATH_MULTIPATH_CHG); - bgp_zebra_clear_route_change_flags(rn); /* If there is a change of interest to peers, reannounce the * route. */ if (CHECK_FLAG(old_select->flags, BGP_PATH_ATTR_CHANGED) - || CHECK_FLAG(rn->flags, BGP_NODE_LABEL_CHANGED)) { - group_announce_route(bgp, afi, safi, rn, new_select); + || CHECK_FLAG(old_select->flags, BGP_PATH_LINK_BW_CHG) + || CHECK_FLAG(dest->flags, BGP_NODE_LABEL_CHANGED)) { + group_announce_route(bgp, afi, safi, dest, new_select); /* unicast routes must also be annouced to * labeled-unicast update-groups */ if (safi == SAFI_UNICAST) group_announce_route(bgp, afi, - SAFI_LABELED_UNICAST, rn, + SAFI_LABELED_UNICAST, dest, new_select); UNSET_FLAG(old_select->flags, BGP_PATH_ATTR_CHANGED); - UNSET_FLAG(rn->flags, BGP_NODE_LABEL_CHANGED); + UNSET_FLAG(dest->flags, BGP_NODE_LABEL_CHANGED); } - UNSET_FLAG(rn->flags, BGP_NODE_PROCESS_SCHEDULED); + /* advertise/withdraw type-5 routes */ + if (CHECK_FLAG(old_select->flags, BGP_PATH_LINK_BW_CHG) + || CHECK_FLAG(old_select->flags, BGP_PATH_MULTIPATH_CHG)) + bgp_process_evpn_route_injection( + bgp, afi, safi, dest, old_select, old_select); + + UNSET_FLAG(old_select->flags, BGP_PATH_MULTIPATH_CHG); + UNSET_FLAG(old_select->flags, BGP_PATH_LINK_BW_CHG); + bgp_zebra_clear_route_change_flags(dest); + UNSET_FLAG(dest->flags, BGP_NODE_PROCESS_SCHEDULED); return; } /* If the user did "clear ip bgp prefix x.x.x.x" this flag will be set */ - UNSET_FLAG(rn->flags, BGP_NODE_USER_CLEAR); + UNSET_FLAG(dest->flags, BGP_NODE_USER_CLEAR); /* bestpath has changed; bump version */ if (old_select || new_select) { - bgp_bump_version(rn); + bgp_bump_version(dest); if (!bgp->t_rmap_def_originate_eval) { bgp_lock(bgp); @@ -2405,16 +2765,18 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_node *rn, } if (old_select) - bgp_path_info_unset_flag(rn, old_select, BGP_PATH_SELECTED); + bgp_path_info_unset_flag(dest, old_select, BGP_PATH_SELECTED); if (new_select) { if (debug) zlog_debug("%s: setting SELECTED flag", __func__); - bgp_path_info_set_flag(rn, new_select, BGP_PATH_SELECTED); - bgp_path_info_unset_flag(rn, new_select, BGP_PATH_ATTR_CHANGED); + bgp_path_info_set_flag(dest, new_select, BGP_PATH_SELECTED); + bgp_path_info_unset_flag(dest, new_select, + BGP_PATH_ATTR_CHANGED); UNSET_FLAG(new_select->flags, BGP_PATH_MULTIPATH_CHG); + UNSET_FLAG(new_select->flags, BGP_PATH_LINK_BW_CHG); } -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC if ((afi == AFI_IP || afi == AFI_IP6) && (safi == SAFI_UNICAST)) { if (old_select != new_select) { if (old_select) { @@ -2431,12 +2793,12 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_node *rn, } #endif - group_announce_route(bgp, afi, safi, rn, new_select); + group_announce_route(bgp, afi, safi, dest, new_select); /* unicast routes must also be annouced to labeled-unicast update-groups */ if (safi == SAFI_UNICAST) - group_announce_route(bgp, afi, SAFI_LABELED_UNICAST, rn, + group_announce_route(bgp, afi, SAFI_LABELED_UNICAST, dest, new_select); /* FIB update. */ @@ -2455,7 +2817,7 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_node *rn, is_route_parent_evpn(old_select)) bgp_zebra_withdraw(p, old_select, bgp, safi); - bgp_zebra_announce(rn, p, new_select, bgp, afi, safi); + bgp_zebra_announce(dest, p, new_select, bgp, afi, safi); } else { /* Withdraw the route from the kernel. */ if (old_select && old_select->type == ZEBRA_ROUTE_BGP @@ -2467,56 +2829,85 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_node *rn, } } - /* advertise/withdraw type-5 routes */ - if ((afi == AFI_IP || afi == AFI_IP6) && (safi == SAFI_UNICAST)) { - if (advertise_type5_routes(bgp, afi) && - new_select && - is_route_injectable_into_evpn(new_select)) { - - /* apply the route-map */ - if (bgp->adv_cmd_rmap[afi][safi].map) { - int ret = 0; - - ret = route_map_apply( - bgp->adv_cmd_rmap[afi][safi].map, - &rn->p, RMAP_BGP, new_select); - if (ret == RMAP_MATCH) - bgp_evpn_advertise_type5_route( - bgp, &rn->p, new_select->attr, - afi, safi); - else - bgp_evpn_withdraw_type5_route( - bgp, &rn->p, afi, safi); - } else { - bgp_evpn_advertise_type5_route(bgp, - &rn->p, - new_select->attr, - afi, safi); - - } - } else if (advertise_type5_routes(bgp, afi) && - old_select && - is_route_injectable_into_evpn(old_select)) - bgp_evpn_withdraw_type5_route(bgp, &rn->p, afi, safi); - } + bgp_process_evpn_route_injection(bgp, afi, safi, dest, new_select, + old_select); /* Clear any route change flags. */ - bgp_zebra_clear_route_change_flags(rn); + bgp_zebra_clear_route_change_flags(dest); /* Reap old select bgp_path_info, if it has been removed */ if (old_select && CHECK_FLAG(old_select->flags, BGP_PATH_REMOVED)) - bgp_path_info_reap(rn, old_select); + bgp_path_info_reap(dest, old_select); - UNSET_FLAG(rn->flags, BGP_NODE_PROCESS_SCHEDULED); + UNSET_FLAG(dest->flags, BGP_NODE_PROCESS_SCHEDULED); return; } +/* Process the routes with the flag BGP_NODE_SELECT_DEFER set */ +int bgp_best_path_select_defer(struct bgp *bgp, afi_t afi, safi_t safi) +{ + struct bgp_dest *dest; + int cnt = 0; + struct afi_safi_info *thread_info; + struct listnode *node = NULL, *nnode = NULL; + + if (bgp->gr_info[afi][safi].t_route_select) + BGP_TIMER_OFF(bgp->gr_info[afi][safi].t_route_select); + + if (BGP_DEBUG(update, UPDATE_OUT)) { + zlog_debug("%s: processing route for %s : cnt %d", __func__, + get_afi_safi_str(afi, safi, false), + listcount(bgp->gr_info[afi][safi].route_list)); + } + + /* Process the route list */ + node = listhead(bgp->gr_info[afi][safi].route_list); + while (node) { + dest = listgetdata(node); + nnode = node->next; + list_delete_node(bgp->gr_info[afi][safi].route_list, node); + dest->rt_node = NULL; + + if (CHECK_FLAG(dest->flags, BGP_NODE_SELECT_DEFER)) { + UNSET_FLAG(dest->flags, BGP_NODE_SELECT_DEFER); + bgp_process_main_one(bgp, dest, afi, safi); + cnt++; + if (cnt >= BGP_MAX_BEST_ROUTE_SELECT) + break; + } + node = nnode; + } + + /* Send EOR message when all routes are processed */ + if (list_isempty(bgp->gr_info[afi][safi].route_list)) { + bgp_send_delayed_eor(bgp); + /* Send route processing complete message to RIB */ + bgp_zebra_update(afi, safi, bgp->vrf_id, + ZEBRA_CLIENT_ROUTE_UPDATE_COMPLETE); + return 0; + } + + thread_info = XMALLOC(MTYPE_TMP, sizeof(struct afi_safi_info)); + + thread_info->afi = afi; + thread_info->safi = safi; + thread_info->bgp = bgp; + + /* If there are more routes to be processed, start the + * selection timer + */ + thread_add_timer(bm->master, bgp_route_select_timer_expire, thread_info, + BGP_ROUTE_SELECT_DELAY, + &bgp->gr_info[afi][safi].t_route_select); + return 0; +} + static wq_item_status bgp_process_wq(struct work_queue *wq, void *data) { struct bgp_process_queue *pqnode = data; struct bgp *bgp = pqnode->bgp; struct bgp_table *table; - struct bgp_node *rn; + struct bgp_dest *dest; /* eoiu marker */ if (CHECK_FLAG(pqnode->flags, BGP_PROCESS_QUEUE_EOIU_MARKER)) { @@ -2527,14 +2918,14 @@ static wq_item_status bgp_process_wq(struct work_queue *wq, void *data) } while (!STAILQ_EMPTY(&pqnode->pqueue)) { - rn = STAILQ_FIRST(&pqnode->pqueue); + dest = STAILQ_FIRST(&pqnode->pqueue); STAILQ_REMOVE_HEAD(&pqnode->pqueue, pq); - STAILQ_NEXT(rn, pq) = NULL; /* complete unlink */ - table = bgp_node_table(rn); - /* note, new RNs may be added as part of processing */ - bgp_process_main_one(bgp, rn, table->afi, table->safi); + STAILQ_NEXT(dest, pq) = NULL; /* complete unlink */ + table = bgp_dest_table(dest); + /* note, new DESTs may be added as part of processing */ + bgp_process_main_one(bgp, dest, table->afi, table->safi); - bgp_unlock_node(rn); + bgp_dest_unlock_node(dest); bgp_table_unlock(table); } @@ -2578,7 +2969,7 @@ static struct bgp_process_queue *bgp_processq_alloc(struct bgp *bgp) return pqnode; } -void bgp_process(struct bgp *bgp, struct bgp_node *rn, afi_t afi, safi_t safi) +void bgp_process(struct bgp *bgp, struct bgp_dest *dest, afi_t afi, safi_t safi) { #define ARBITRARY_PROCESS_QLEN 10000 struct work_queue *wq = bm->process_main_queue; @@ -2586,8 +2977,18 @@ void bgp_process(struct bgp *bgp, struct bgp_node *rn, afi_t afi, safi_t safi) int pqnode_reuse = 0; /* already scheduled for processing? */ - if (CHECK_FLAG(rn->flags, BGP_NODE_PROCESS_SCHEDULED)) + if (CHECK_FLAG(dest->flags, BGP_NODE_PROCESS_SCHEDULED)) + return; + + /* If the flag BGP_NODE_SELECT_DEFER is set, do not add route to + * the workqueue + */ + if (CHECK_FLAG(dest->flags, BGP_NODE_SELECT_DEFER)) { + if (BGP_DEBUG(update, UPDATE_OUT)) + zlog_debug("BGP_NODE_SELECT_DEFER set for route %p", + dest); return; + } if (wq == NULL) return; @@ -2608,14 +3009,14 @@ void bgp_process(struct bgp *bgp, struct bgp_node *rn, afi_t afi, safi_t safi) } else pqnode = bgp_processq_alloc(bgp); /* all unlocked in bgp_process_wq */ - bgp_table_lock(bgp_node_table(rn)); + bgp_table_lock(bgp_dest_table(dest)); - SET_FLAG(rn->flags, BGP_NODE_PROCESS_SCHEDULED); - bgp_lock_node(rn); + SET_FLAG(dest->flags, BGP_NODE_PROCESS_SCHEDULED); + bgp_dest_lock_node(dest); /* can't be enqueued twice */ - assert(STAILQ_NEXT(rn, pq) == NULL); - STAILQ_INSERT_TAIL(&pqnode->pqueue, rn, pq); + assert(STAILQ_NEXT(dest, pq) == NULL); + STAILQ_INSERT_TAIL(&pqnode->pqueue, dest, pq); pqnode->queued++; if (!pqnode_reuse) @@ -2650,37 +3051,77 @@ static int bgp_maximum_prefix_restart_timer(struct thread *thread) peer->host); if ((peer_clear(peer, NULL) < 0) && bgp_debug_neighbor_events(peer)) - zlog_debug("%s: %s peer_clear failed", - __PRETTY_FUNCTION__, peer->host); + zlog_debug("%s: %s peer_clear failed", __func__, peer->host); return 0; } -int bgp_maximum_prefix_overflow(struct peer *peer, afi_t afi, safi_t safi, - int always) +static uint32_t bgp_filtered_routes_count(struct peer *peer, afi_t afi, + safi_t safi) { - iana_afi_t pkt_afi; - iana_safi_t pkt_safi; + uint32_t count = 0; + bool filtered = false; + struct bgp_dest *dest; + struct bgp_adj_in *ain; + struct attr attr = {}; + struct bgp_table *table = peer->bgp->rib[afi][safi]; - if (!CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX)) - return 0; + for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) { + for (ain = dest->adj_in; ain; ain = ain->next) { + const struct prefix *rn_p = bgp_dest_get_prefix(dest); - if (peer->pcount[afi][safi] > peer->pmax[afi][safi]) { - if (CHECK_FLAG(peer->af_sflags[afi][safi], - PEER_STATUS_PREFIX_LIMIT) - && !always) - return 0; + attr = *ain->attr; - zlog_info( - "%%MAXPFXEXCEED: No. of %s prefix received from %s %ld exceed, " - "limit %ld", - afi_safi_print(afi, safi), peer->host, - peer->pcount[afi][safi], peer->pmax[afi][safi]); - SET_FLAG(peer->af_sflags[afi][safi], PEER_STATUS_PREFIX_LIMIT); + if (bgp_input_filter(peer, rn_p, &attr, afi, safi) + == FILTER_DENY) + filtered = true; - if (CHECK_FLAG(peer->af_flags[afi][safi], - PEER_FLAG_MAX_PREFIX_WARNING)) - return 0; + if (bgp_input_modifier( + peer, rn_p, &attr, afi, safi, + ROUTE_MAP_IN_NAME(&peer->filter[afi][safi]), + NULL, 0, NULL) + == RMAP_DENY) + filtered = true; + + if (filtered) + count++; + + bgp_attr_undup(&attr, ain->attr); + } + } + + return count; +} + +bool bgp_maximum_prefix_overflow(struct peer *peer, afi_t afi, safi_t safi, + int always) +{ + iana_afi_t pkt_afi; + iana_safi_t pkt_safi; + uint32_t pcount = (CHECK_FLAG(peer->af_flags[afi][safi], + PEER_FLAG_MAX_PREFIX_FORCE)) + ? bgp_filtered_routes_count(peer, afi, safi) + + peer->pcount[afi][safi] + : peer->pcount[afi][safi]; + + if (!CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX)) + return false; + + if (pcount > peer->pmax[afi][safi]) { + if (CHECK_FLAG(peer->af_sflags[afi][safi], + PEER_STATUS_PREFIX_LIMIT) + && !always) + return false; + + zlog_info( + "%%MAXPFXEXCEED: No. of %s prefix received from %s %u exceed, limit %u", + get_afi_safi_str(afi, safi, false), peer->host, pcount, + peer->pmax[afi][safi]); + SET_FLAG(peer->af_sflags[afi][safi], PEER_STATUS_PREFIX_LIMIT); + + if (CHECK_FLAG(peer->af_flags[afi][safi], + PEER_FLAG_MAX_PREFIX_WARNING)) + return false; /* Convert AFI, SAFI to values for packet. */ pkt_afi = afi_int2iana(afi); @@ -2704,7 +3145,7 @@ int bgp_maximum_prefix_overflow(struct peer *peer, afi_t afi, safi_t safi, /* Dynamic peers will just close their connection. */ if (peer_dynamic_neighbor(peer)) - return 1; + return true; /* restart timer start */ if (peer->pmax_restart[afi][safi]) { @@ -2721,95 +3162,124 @@ int bgp_maximum_prefix_overflow(struct peer *peer, afi_t afi, safi_t safi, peer->v_pmax_restart); } - return 1; + return true; } else UNSET_FLAG(peer->af_sflags[afi][safi], PEER_STATUS_PREFIX_LIMIT); - if (peer->pcount[afi][safi] + if (pcount > (peer->pmax[afi][safi] * peer->pmax_threshold[afi][safi] / 100)) { if (CHECK_FLAG(peer->af_sflags[afi][safi], PEER_STATUS_PREFIX_THRESHOLD) && !always) - return 0; + return false; zlog_info( - "%%MAXPFX: No. of %s prefix received from %s reaches %ld, max %ld", - afi_safi_print(afi, safi), peer->host, - peer->pcount[afi][safi], peer->pmax[afi][safi]); + "%%MAXPFX: No. of %s prefix received from %s reaches %u, max %u", + get_afi_safi_str(afi, safi, false), peer->host, pcount, + peer->pmax[afi][safi]); SET_FLAG(peer->af_sflags[afi][safi], PEER_STATUS_PREFIX_THRESHOLD); } else UNSET_FLAG(peer->af_sflags[afi][safi], PEER_STATUS_PREFIX_THRESHOLD); - return 0; + return false; } /* Unconditionally remove the route from the RIB, without taking * damping into consideration (eg, because the session went down) */ -void bgp_rib_remove(struct bgp_node *rn, struct bgp_path_info *pi, +void bgp_rib_remove(struct bgp_dest *dest, struct bgp_path_info *pi, struct peer *peer, afi_t afi, safi_t safi) { - bgp_aggregate_decrement(peer->bgp, &rn->p, pi, afi, safi); - if (!CHECK_FLAG(pi->flags, BGP_PATH_HISTORY)) - bgp_path_info_delete(rn, pi); /* keep historical info */ + struct bgp *bgp = NULL; + bool delete_route = false; + + bgp_aggregate_decrement(peer->bgp, bgp_dest_get_prefix(dest), pi, afi, + safi); - bgp_process(peer->bgp, rn, afi, safi); + if (!CHECK_FLAG(pi->flags, BGP_PATH_HISTORY)) { + bgp_path_info_delete(dest, pi); /* keep historical info */ + + /* If the selected path is removed, reset BGP_NODE_SELECT_DEFER + * flag + */ + if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED)) + delete_route = true; + else if (bgp_dest_set_defer_flag(dest, true) < 0) + delete_route = true; + if (delete_route) { + if (CHECK_FLAG(dest->flags, BGP_NODE_SELECT_DEFER)) { + UNSET_FLAG(dest->flags, BGP_NODE_SELECT_DEFER); + bgp = pi->peer->bgp; + if ((dest->rt_node) + && (bgp->gr_info[afi][safi].route_list)) { + list_delete_node(bgp->gr_info[afi][safi] + .route_list, + dest->rt_node); + dest->rt_node = NULL; + } + } + } + } + + hook_call(bgp_process, peer->bgp, afi, safi, dest, peer, true); + bgp_process(peer->bgp, dest, afi, safi); } -static void bgp_rib_withdraw(struct bgp_node *rn, struct bgp_path_info *pi, +static void bgp_rib_withdraw(struct bgp_dest *dest, struct bgp_path_info *pi, struct peer *peer, afi_t afi, safi_t safi, struct prefix_rd *prd) { + const struct prefix *p = bgp_dest_get_prefix(dest); + /* apply dampening, if result is suppressed, we'll be retaining * the bgp_path_info in the RIB for historical reference. */ if (CHECK_FLAG(peer->bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING) && peer->sort == BGP_PEER_EBGP) - if ((bgp_damp_withdraw(pi, rn, afi, safi, 0)) + if ((bgp_damp_withdraw(pi, dest, afi, safi, 0)) == BGP_DAMP_SUPPRESSED) { - bgp_aggregate_decrement(peer->bgp, &rn->p, pi, afi, + bgp_aggregate_decrement(peer->bgp, p, pi, afi, safi); return; } -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC if (safi == SAFI_MPLS_VPN) { - struct bgp_node *prn = NULL; + struct bgp_dest *pdest = NULL; struct bgp_table *table = NULL; - prn = bgp_node_get(peer->bgp->rib[afi][safi], - (struct prefix *)prd); - if (bgp_node_has_bgp_path_info_data(prn)) { - table = bgp_node_get_bgp_table_info(prn); + pdest = bgp_node_get(peer->bgp->rib[afi][safi], + (struct prefix *)prd); + if (bgp_dest_has_bgp_path_info_data(pdest)) { + table = bgp_dest_get_bgp_table_info(pdest); vnc_import_bgp_del_vnc_host_route_mode_resolve_nve( - peer->bgp, prd, table, &rn->p, pi); + peer->bgp, prd, table, p, pi); } - bgp_unlock_node(prn); + bgp_dest_unlock_node(pdest); } if ((afi == AFI_IP || afi == AFI_IP6) && (safi == SAFI_UNICAST)) { if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED)) { - vnc_import_bgp_del_route(peer->bgp, &rn->p, pi); - vnc_import_bgp_exterior_del_route(peer->bgp, &rn->p, - pi); + vnc_import_bgp_del_route(peer->bgp, p, pi); + vnc_import_bgp_exterior_del_route(peer->bgp, p, pi); } } #endif /* If this is an EVPN route, process for un-import. */ if (safi == SAFI_EVPN) - bgp_evpn_unimport_route(peer->bgp, afi, safi, &rn->p, pi); + bgp_evpn_unimport_route(peer->bgp, afi, safi, p, pi); - bgp_rib_remove(rn, pi, peer, afi, safi); + bgp_rib_remove(dest, pi, peer, afi, safi); } struct bgp_path_info *info_make(int type, int sub_type, unsigned short instance, struct peer *peer, struct attr *attr, - struct bgp_node *rn) + struct bgp_dest *dest) { struct bgp_path_info *new; @@ -2821,24 +3291,15 @@ struct bgp_path_info *info_make(int type, int sub_type, unsigned short instance, new->peer = peer; new->attr = attr; new->uptime = bgp_clock(); - new->net = rn; + new->net = dest; return new; } static void overlay_index_update(struct attr *attr, - struct eth_segment_id *eth_s_id, union gw_addr *gw_ip) { if (!attr) return; - - if (eth_s_id == NULL) { - memset(&(attr->evpn_overlay.eth_s_id), 0, - sizeof(struct eth_segment_id)); - } else { - memcpy(&(attr->evpn_overlay.eth_s_id), eth_s_id, - sizeof(struct eth_segment_id)); - } if (gw_ip == NULL) { memset(&(attr->evpn_overlay.gw_ip), 0, sizeof(union gw_addr)); } else { @@ -2848,29 +3309,18 @@ static void overlay_index_update(struct attr *attr, } static bool overlay_index_equal(afi_t afi, struct bgp_path_info *path, - struct eth_segment_id *eth_s_id, union gw_addr *gw_ip) { - struct eth_segment_id *path_eth_s_id, *path_eth_s_id_remote; union gw_addr *path_gw_ip, *path_gw_ip_remote; union { - struct eth_segment_id esi; + esi_t esi; union gw_addr ip; } temp; if (afi != AFI_L2VPN) return true; - if (!path->attr) { - memset(&temp, 0, sizeof(temp)); - path_eth_s_id = &temp.esi; - path_gw_ip = &temp.ip; - if (eth_s_id == NULL && gw_ip == NULL) - return true; - } else { - path_eth_s_id = &(path->attr->evpn_overlay.eth_s_id); - path_gw_ip = &(path->attr->evpn_overlay.gw_ip); - } + path_gw_ip = &(path->attr->evpn_overlay.gw_ip); if (gw_ip == NULL) { memset(&temp, 0, sizeof(temp)); @@ -2878,65 +3328,78 @@ static bool overlay_index_equal(afi_t afi, struct bgp_path_info *path, } else path_gw_ip_remote = gw_ip; - if (eth_s_id == NULL) { - memset(&temp, 0, sizeof(temp)); - path_eth_s_id_remote = &temp.esi; - } else - path_eth_s_id_remote = eth_s_id; - - if (!memcmp(path_gw_ip, path_gw_ip_remote, sizeof(union gw_addr))) - return false; - - return !memcmp(path_eth_s_id, path_eth_s_id_remote, - sizeof(struct eth_segment_id)); + return !!memcmp(path_gw_ip, path_gw_ip_remote, sizeof(union gw_addr)); } /* Check if received nexthop is valid or not. */ -static int bgp_update_martian_nexthop(struct bgp *bgp, afi_t afi, safi_t safi, - struct attr *attr) +bool bgp_update_martian_nexthop(struct bgp *bgp, afi_t afi, safi_t safi, + uint8_t type, uint8_t stype, struct attr *attr, + struct bgp_dest *dest) { - int ret = 0; + bool ret = false; + bool is_bgp_static_route = + (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) - return 0; + /* + * 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 == 0 + if (attr->nexthop.s_addr == INADDR_ANY || IPV4_CLASS_DE(ntohl(attr->nexthop.s_addr)) - || bgp_nexthop_self(bgp, attr->nexthop)) - return 1; + || bgp_nexthop_self(bgp, afi, type, stype, attr, dest)) + return true; } /* If MP_NEXTHOP is present, validate it. */ /* Note: For IPv6 nexthops, we only validate the global (1st) nexthop; * there is code in bgp_attr.c to ignore the link-local (2nd) nexthop if * it is not an IPv6 link-local address. + * + * If we receive an UPDATE with nexthop length set to 32 bytes + * we shouldn't discard an UPDATE if it's set to (::). + * The link-local (2st) is validated along the code path later. */ if (attr->mp_nexthop_len) { switch (attr->mp_nexthop_len) { case BGP_ATTR_NHLEN_IPV4: case BGP_ATTR_NHLEN_VPNV4: - ret = (attr->mp_nexthop_global_in.s_addr == 0 - || IPV4_CLASS_DE(ntohl( - attr->mp_nexthop_global_in.s_addr)) - || bgp_nexthop_self(bgp, - attr->mp_nexthop_global_in)); + 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, + dest)); break; case BGP_ATTR_NHLEN_IPV6_GLOBAL: - case BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL: case BGP_ATTR_NHLEN_VPNV6_GLOBAL: - ret = (IN6_IS_ADDR_UNSPECIFIED(&attr->mp_nexthop_global) + ret = (IN6_IS_ADDR_UNSPECIFIED( + &attr->mp_nexthop_global) || IN6_IS_ADDR_LOOPBACK(&attr->mp_nexthop_global) || IN6_IS_ADDR_MULTICAST( - &attr->mp_nexthop_global)); + &attr->mp_nexthop_global) + || bgp_nexthop_self(bgp, afi, type, stype, attr, + dest)); + break; + case BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL: + ret = (IN6_IS_ADDR_LOOPBACK(&attr->mp_nexthop_global) + || IN6_IS_ADDR_MULTICAST( + &attr->mp_nexthop_global) + || bgp_nexthop_self(bgp, afi, type, stype, attr, + dest)); break; default: - ret = 1; + ret = true; break; } } @@ -2944,7 +3407,7 @@ static int bgp_update_martian_nexthop(struct bgp *bgp, afi_t afi, safi_t safi, return ret; } -int bgp_update(struct peer *peer, struct prefix *p, uint32_t addpath_id, +int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, struct attr *attr, afi_t afi, safi_t safi, int type, int sub_type, struct prefix_rd *prd, mpls_label_t *label, uint32_t num_labels, int soft_reconfig, @@ -2952,7 +3415,7 @@ int bgp_update(struct peer *peer, struct prefix *p, uint32_t addpath_id, { int ret; int aspath_loop_count = 0; - struct bgp_node *rn; + struct bgp_dest *dest; struct bgp *bgp; struct attr new_attr; struct attr *attr_new; @@ -2964,7 +3427,11 @@ int bgp_update(struct peer *peer, struct prefix *p, uint32_t addpath_id, int connected = 0; int do_loop_check = 1; int has_valid_label = 0; -#if ENABLE_BGP_VNC + afi_t nh_afi; + uint8_t pi_type = 0; + uint8_t pi_sub_type = 0; + +#ifdef ENABLE_BGP_VNC int vnc_implicit_withdraw = 0; #endif int same_attr = 0; @@ -2974,7 +3441,7 @@ int bgp_update(struct peer *peer, struct prefix *p, uint32_t addpath_id, new_attr.label = MPLS_INVALID_LABEL; bgp = peer->bgp; - rn = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi, p, prd); + dest = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi, p, prd); /* TODO: Check to see if we can get rid of "is_valid_label" */ if (afi == AFI_L2VPN && safi == SAFI_EVPN) has_valid_label = (num_labels > 0) ? 1 : 0; @@ -2986,10 +3453,10 @@ int bgp_update(struct peer *peer, struct prefix *p, uint32_t addpath_id, if (!soft_reconfig && CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_SOFT_RECONFIG) && peer != bgp->peer_self) - bgp_adj_in_set(rn, peer, attr, addpath_id); + bgp_adj_in_set(dest, peer, attr, addpath_id); /* Check previously received route. */ - for (pi = bgp_node_get_bgp_path_info(rn); pi; pi = pi->next) + for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) if (pi->peer == peer && pi->type == type && pi->sub_type == sub_type && pi->addpath_rx_id == addpath_id) @@ -3005,7 +3472,8 @@ int bgp_update(struct peer *peer, struct prefix *p, uint32_t addpath_id, if (aspath_loop_check(attr->aspath, peer->change_local_as) > aspath_loop_count) { - reason = "as-path contains our own AS;"; + peer->stat_pfx_aspath_loop++; + reason = "as-path contains our own AS A;"; goto filtered; } } @@ -3025,6 +3493,7 @@ int bgp_update(struct peer *peer, struct prefix *p, uint32_t addpath_id, || (CHECK_FLAG(bgp->config, BGP_CONFIG_CONFEDERATION) && aspath_loop_check(attr->aspath, bgp->confed_id) > peer->allowas_in[afi][safi])) { + peer->stat_pfx_aspath_loop++; reason = "as-path contains our own AS;"; goto filtered; } @@ -3033,23 +3502,55 @@ int bgp_update(struct peer *peer, struct prefix *p, uint32_t addpath_id, /* Route reflector originator ID check. */ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID) && IPV4_ADDR_SAME(&bgp->router_id, &attr->originator_id)) { + peer->stat_pfx_originator_loop++; reason = "originator is us;"; goto filtered; } /* Route reflector cluster ID check. */ if (bgp_cluster_filter(peer, attr)) { + peer->stat_pfx_cluster_loop++; reason = "reflected from the same cluster;"; goto filtered; } /* Apply incoming filter. */ if (bgp_input_filter(peer, p, attr, afi, safi) == FILTER_DENY) { + peer->stat_pfx_filter++; reason = "filter;"; goto filtered; } - bgp_attr_dup(&new_attr, attr); + /* RFC 8212 to prevent route leaks. + * This specification intends to improve this situation by requiring the + * explicit configuration of both BGP Import and Export Policies for any + * External BGP (EBGP) session such as customers, peers, or + * confederation boundaries for all enabled address families. Through + * codification of the aforementioned requirement, operators will + * benefit from consistent behavior across different BGP + * implementations. + */ + if (CHECK_FLAG(bgp->flags, BGP_FLAG_EBGP_REQUIRES_POLICY)) + if (!bgp_inbound_policy_exists(peer, + &peer->filter[afi][safi])) { + reason = "inbound policy missing"; + goto filtered; + } + + /* draft-ietf-idr-deprecate-as-set-confed-set + * Filter routes having AS_SET or AS_CONFED_SET in the path. + * Eventually, This document (if approved) updates RFC 4271 + * and RFC 5065 by eliminating AS_SET and AS_CONFED_SET types, + * and obsoletes RFC 6472. + */ + if (peer->bgp->reject_as_sets) + if (aspath_check_as_sets(attr->aspath)) { + reason = + "as-path contains AS_SET or AS_CONFED_SET type;"; + goto filtered; + } + + new_attr = *attr; /* Apply incoming route-map. * NB: new_attr may now contain newly allocated values from route-map @@ -3057,13 +3558,21 @@ int bgp_update(struct peer *peer, struct prefix *p, uint32_t addpath_id, * commands, so we need bgp_attr_flush in the error paths, until we * intern * the attr (which takes over the memory references) */ - if (bgp_input_modifier(peer, p, &new_attr, afi, safi, NULL) + if (bgp_input_modifier(peer, p, &new_attr, afi, safi, NULL, label, + num_labels, dest) == RMAP_DENY) { + peer->stat_pfx_filter++; reason = "route-map;"; bgp_attr_flush(&new_attr); goto filtered; } + if (pi && pi->attr->rmap_table_id != new_attr.rmap_table_id) { + if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED)) + /* remove from RIB previous entry */ + bgp_zebra_withdraw(p, pi, bgp, safi); + } + if (peer->sort == BGP_PEER_EBGP) { /* If we receive the graceful-shutdown community from an eBGP @@ -3075,32 +3584,53 @@ int bgp_update(struct peer *peer, struct prefix *p, uint32_t addpath_id, /* If graceful-shutdown is configured then add the GSHUT * community to all paths received from eBGP peers */ - } else if (bgp_flag_check(peer->bgp, - BGP_FLAG_GRACEFUL_SHUTDOWN)) { + } else if (CHECK_FLAG(peer->bgp->flags, + BGP_FLAG_GRACEFUL_SHUTDOWN)) bgp_attr_add_gshut_community(&new_attr); - } + } + + if (pi) { + pi_type = pi->type; + pi_sub_type = pi->sub_type; } /* next hop check. */ if (!CHECK_FLAG(peer->flags, PEER_FLAG_IS_RFAPI_HD) - && bgp_update_martian_nexthop(bgp, afi, safi, &new_attr)) { + && bgp_update_martian_nexthop(bgp, afi, safi, pi_type, pi_sub_type, + &new_attr, dest)) { + peer->stat_pfx_nh_invalid++; reason = "martian or self next-hop;"; bgp_attr_flush(&new_attr); goto filtered; } if (bgp_mac_entry_exists(p) || bgp_mac_exist(&attr->rmac)) { + peer->stat_pfx_nh_invalid++; reason = "self mac;"; goto filtered; } + /* Update Overlay Index */ + if (afi == AFI_L2VPN) { + overlay_index_update(&new_attr, + evpn == NULL ? NULL : &evpn->gw_ip); + } + attr_new = bgp_attr_intern(&new_attr); + /* If maximum prefix count is configured and current prefix + * count exeed it. + */ + if (bgp_maximum_prefix_overflow(peer, afi, safi, 0)) + return -1; + /* If the update is implicit withdraw. */ if (pi) { pi->uptime = bgp_clock(); same_attr = attrhash_cmp(pi->attr, attr_new); + hook_call(bgp_process, bgp, afi, safi, dest, peer, true); + /* Same attribute comes in. */ if (!CHECK_FLAG(pi->flags, BGP_PATH_REMOVED) && attrhash_cmp(pi->attr, attr_new) @@ -3109,7 +3639,7 @@ int bgp_update(struct peer *peer, struct prefix *p, uint32_t addpath_id, num_labels * sizeof(mpls_label_t)) == 0) && (overlay_index_equal( - afi, pi, evpn == NULL ? NULL : &evpn->eth_s_id, + afi, pi, evpn == NULL ? NULL : &evpn->gw_ip))) { if (CHECK_FLAG(bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING) @@ -3125,11 +3655,11 @@ int bgp_update(struct peer *peer, struct prefix *p, uint32_t addpath_id, pfx_buf); } - if (bgp_damp_update(pi, rn, afi, safi) + if (bgp_damp_update(pi, dest, afi, safi) != BGP_DAMP_SUPPRESSED) { bgp_aggregate_increment(bgp, p, pi, afi, safi); - bgp_process(bgp, rn, afi, safi); + bgp_process(bgp, dest, afi, safi); } } else /* Duplicate - odd */ { @@ -3155,12 +3685,13 @@ int bgp_update(struct peer *peer, struct prefix *p, uint32_t addpath_id, /* graceful restart STALE flag unset. */ if (CHECK_FLAG(pi->flags, BGP_PATH_STALE)) { bgp_path_info_unset_flag( - rn, pi, BGP_PATH_STALE); - bgp_process(bgp, rn, afi, safi); + dest, pi, BGP_PATH_STALE); + bgp_dest_set_defer_flag(dest, false); + bgp_process(bgp, dest, afi, safi); } } - bgp_unlock_node(rn); + bgp_dest_unlock_node(dest); bgp_attr_unintern(&attr_new); return 0; @@ -3178,7 +3709,7 @@ int bgp_update(struct peer *peer, struct prefix *p, uint32_t addpath_id, peer->host, pfx_buf); } - bgp_path_info_restore(rn, pi); + bgp_path_info_restore(dest, pi); } /* Received Logging. */ @@ -3191,11 +3722,13 @@ int bgp_update(struct peer *peer, struct prefix *p, uint32_t addpath_id, } /* graceful restart STALE flag unset. */ - if (CHECK_FLAG(pi->flags, BGP_PATH_STALE)) - bgp_path_info_unset_flag(rn, pi, BGP_PATH_STALE); + if (CHECK_FLAG(pi->flags, BGP_PATH_STALE)) { + bgp_path_info_unset_flag(dest, pi, BGP_PATH_STALE); + bgp_dest_set_defer_flag(dest, false); + } /* The attribute is changed. */ - bgp_path_info_set_flag(rn, pi, BGP_PATH_ATTR_CHANGED); + bgp_path_info_set_flag(dest, pi, BGP_PATH_ATTR_CHANGED); /* implicit withdraw, decrement aggregate and pcount here. * only if update is accepted, they'll increment below. @@ -3209,22 +3742,22 @@ int bgp_update(struct peer *peer, struct prefix *p, uint32_t addpath_id, dampening information. */ if (!CHECK_FLAG(pi->flags, BGP_PATH_HISTORY)) - bgp_damp_withdraw(pi, rn, afi, safi, 1); + bgp_damp_withdraw(pi, dest, afi, safi, 1); } -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC if (safi == SAFI_MPLS_VPN) { - struct bgp_node *prn = NULL; + struct bgp_dest *pdest = NULL; struct bgp_table *table = NULL; - prn = bgp_node_get(bgp->rib[afi][safi], - (struct prefix *)prd); - if (bgp_node_has_bgp_path_info_data(prn)) { - table = bgp_node_get_bgp_table_info(prn); + pdest = bgp_node_get(bgp->rib[afi][safi], + (struct prefix *)prd); + if (bgp_dest_has_bgp_path_info_data(pdest)) { + table = bgp_dest_get_bgp_table_info(pdest); vnc_import_bgp_del_vnc_host_route_mode_resolve_nve( bgp, prd, table, p, pi); } - bgp_unlock_node(prn); + bgp_dest_unlock_node(pdest); } if ((afi == AFI_IP || afi == AFI_IP6) && (safi == SAFI_UNICAST)) { @@ -3246,7 +3779,8 @@ int bgp_update(struct peer *peer, struct prefix *p, uint32_t addpath_id, * subsequently processed for import with the new extended * community. */ - if (safi == SAFI_EVPN && !same_attr) { + if (((safi == SAFI_EVPN) || (safi == SAFI_MPLS_VPN)) + && !same_attr) { if ((pi->attr->flag & ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES)) && (attr_new->flag @@ -3263,8 +3797,12 @@ int bgp_update(struct peer *peer, struct prefix *p, uint32_t addpath_id, pi->attr->ecommunity), ecommunity_str( attr_new->ecommunity)); - bgp_evpn_unimport_route(bgp, afi, safi, - p, pi); + if (safi == SAFI_EVPN) + bgp_evpn_unimport_route( + bgp, afi, safi, p, pi); + else /* SAFI_MPLS_VPN */ + vpn_leak_to_vrf_withdraw(bgp, + pi); } } } @@ -3285,7 +3823,23 @@ int bgp_update(struct peer *peer, struct prefix *p, uint32_t addpath_id, bgp_set_valid_label(&extra->label[0]); } -#if ENABLE_BGP_VNC + /* Update SRv6 SID */ + if (attr->srv6_l3vpn) { + extra = bgp_path_info_extra_get(pi); + if (sid_diff(&extra->sid[0], &attr->srv6_l3vpn->sid)) { + sid_copy(&extra->sid[0], + &attr->srv6_l3vpn->sid); + extra->num_sids = 1; + } + } else if (attr->srv6_vpn) { + extra = bgp_path_info_extra_get(pi); + if (sid_diff(&extra->sid[0], &attr->srv6_vpn->sid)) { + sid_copy(&extra->sid[0], &attr->srv6_vpn->sid); + extra->num_sids = 1; + } + } + +#ifdef ENABLE_BGP_VNC if ((afi == AFI_IP || afi == AFI_IP6) && (safi == SAFI_UNICAST)) { if (vnc_implicit_withdraw) { @@ -3307,33 +3861,30 @@ int bgp_update(struct peer *peer, struct prefix *p, uint32_t addpath_id, } } #endif - /* Update Overlay Index */ - if (afi == AFI_L2VPN) { - overlay_index_update( - pi->attr, evpn == NULL ? NULL : &evpn->eth_s_id, - evpn == NULL ? NULL : &evpn->gw_ip); - } /* Update bgp route dampening information. */ if (CHECK_FLAG(bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING) && peer->sort == BGP_PEER_EBGP) { /* Now we do normal update dampening. */ - ret = bgp_damp_update(pi, rn, afi, safi); + ret = bgp_damp_update(pi, dest, afi, safi); if (ret == BGP_DAMP_SUPPRESSED) { - bgp_unlock_node(rn); + bgp_dest_unlock_node(dest); return 0; } } /* Nexthop reachability check - for unicast and * labeled-unicast.. */ - if ((afi == AFI_IP || afi == AFI_IP6) - && (safi == SAFI_UNICAST || safi == SAFI_LABELED_UNICAST)) { - if (peer->sort == BGP_PEER_EBGP && peer->ttl == 1 + if (((afi == AFI_IP || afi == AFI_IP6) + && (safi == SAFI_UNICAST || safi == SAFI_LABELED_UNICAST)) + || (safi == SAFI_EVPN && + bgp_evpn_is_prefix_nht_supported(p))) { + if (safi != SAFI_EVPN && peer->sort == BGP_PEER_EBGP + && peer->ttl == BGP_DEFAULT_TTL && !CHECK_FLAG(peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK) - && !bgp_flag_check( - bgp, BGP_FLAG_DISABLE_NH_CONNECTED_CHK)) + && !CHECK_FLAG(bgp->flags, + BGP_FLAG_DISABLE_NH_CONNECTED_CHK)) connected = 1; else connected = 0; @@ -3343,40 +3894,39 @@ int bgp_update(struct peer *peer, struct prefix *p, uint32_t addpath_id, if (pi->extra && pi->extra->bgp_orig) bgp_nexthop = pi->extra->bgp_orig; - if (bgp_find_or_add_nexthop(bgp, bgp_nexthop, afi, pi, - NULL, connected) + nh_afi = BGP_ATTR_NH_AFI(afi, pi->attr); + + if (bgp_find_or_add_nexthop(bgp, bgp_nexthop, nh_afi, + pi, NULL, connected) || CHECK_FLAG(peer->flags, PEER_FLAG_IS_RFAPI_HD)) - bgp_path_info_set_flag(rn, pi, BGP_PATH_VALID); + bgp_path_info_set_flag(dest, pi, + BGP_PATH_VALID); else { if (BGP_DEBUG(nht, NHT)) { - char buf1[INET6_ADDRSTRLEN]; - inet_ntop(AF_INET, - (const void *)&attr_new - ->nexthop, - buf1, INET6_ADDRSTRLEN); - zlog_debug("%s(%s): NH unresolved", - __FUNCTION__, buf1); + zlog_debug("%s(%pI4): NH unresolved", + __func__, + (in_addr_t *)&attr_new->nexthop); } - bgp_path_info_unset_flag(rn, pi, + bgp_path_info_unset_flag(dest, pi, BGP_PATH_VALID); } } else - bgp_path_info_set_flag(rn, pi, BGP_PATH_VALID); + bgp_path_info_set_flag(dest, pi, BGP_PATH_VALID); -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC if (safi == SAFI_MPLS_VPN) { - struct bgp_node *prn = NULL; + struct bgp_dest *pdest = NULL; struct bgp_table *table = NULL; - prn = bgp_node_get(bgp->rib[afi][safi], - (struct prefix *)prd); - if (bgp_node_has_bgp_path_info_data(prn)) { - table = bgp_node_get_bgp_table_info(prn); + pdest = bgp_node_get(bgp->rib[afi][safi], + (struct prefix *)prd); + if (bgp_dest_has_bgp_path_info_data(pdest)) { + table = bgp_dest_get_bgp_table_info(pdest); vnc_import_bgp_add_vnc_host_route_mode_resolve_nve( bgp, prd, table, p, pi); } - bgp_unlock_node(prn); + bgp_dest_unlock_node(pdest); } #endif @@ -3392,14 +3942,15 @@ int bgp_update(struct peer *peer, struct prefix *p, uint32_t addpath_id, * updating * the attributes for the route in the VNI(s). */ - if (safi == SAFI_EVPN && !same_attr) + if (safi == SAFI_EVPN && !same_attr && + CHECK_FLAG(pi->flags, BGP_PATH_VALID)) bgp_evpn_import_route(bgp, afi, safi, p, pi); /* Process change. */ bgp_aggregate_increment(bgp, p, pi, afi, safi); - bgp_process(bgp, rn, afi, safi); - bgp_unlock_node(rn); + bgp_process(bgp, dest, afi, safi); + bgp_dest_unlock_node(dest); if (SAFI_UNICAST == safi && (bgp->inst_type == BGP_INSTANCE_TYPE_VRF @@ -3413,7 +3964,7 @@ int bgp_update(struct peer *peer, struct prefix *p, uint32_t addpath_id, vpn_leak_to_vrf_update(bgp, pi); } -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC if (SAFI_MPLS_VPN == safi) { mpls_label_t label_decoded = decode_label(label); @@ -3444,7 +3995,7 @@ int bgp_update(struct peer *peer, struct prefix *p, uint32_t addpath_id, } /* Make new BGP info. */ - new = info_make(type, sub_type, 0, peer, attr_new, rn); + new = info_make(type, sub_type, 0, peer, attr_new, dest); /* Update MPLS label */ if (has_valid_label) { @@ -3458,39 +4009,56 @@ int bgp_update(struct peer *peer, struct prefix *p, uint32_t addpath_id, bgp_set_valid_label(&extra->label[0]); } + /* Update SRv6 SID */ + if (safi == SAFI_MPLS_VPN) { + extra = bgp_path_info_extra_get(new); + if (attr->srv6_l3vpn) { + sid_copy(&extra->sid[0], &attr->srv6_l3vpn->sid); + extra->num_sids = 1; + } else if (attr->srv6_vpn) { + sid_copy(&extra->sid[0], &attr->srv6_vpn->sid); + extra->num_sids = 1; + } + } + /* Update Overlay Index */ if (afi == AFI_L2VPN) { overlay_index_update(new->attr, - evpn == NULL ? NULL : &evpn->eth_s_id, evpn == NULL ? NULL : &evpn->gw_ip); } /* Nexthop reachability check. */ - if ((afi == AFI_IP || afi == AFI_IP6) - && (safi == SAFI_UNICAST || safi == SAFI_LABELED_UNICAST)) { - if (peer->sort == BGP_PEER_EBGP && peer->ttl == 1 + if (((afi == AFI_IP || afi == AFI_IP6) + && (safi == SAFI_UNICAST || safi == SAFI_LABELED_UNICAST)) + || (safi == SAFI_EVPN && bgp_evpn_is_prefix_nht_supported(p))) { + if (safi != SAFI_EVPN && peer->sort == BGP_PEER_EBGP + && peer->ttl == BGP_DEFAULT_TTL && !CHECK_FLAG(peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK) - && !bgp_flag_check(bgp, BGP_FLAG_DISABLE_NH_CONNECTED_CHK)) + && !CHECK_FLAG(bgp->flags, + BGP_FLAG_DISABLE_NH_CONNECTED_CHK)) connected = 1; else connected = 0; - if (bgp_find_or_add_nexthop(bgp, bgp, afi, new, NULL, connected) + nh_afi = BGP_ATTR_NH_AFI(afi, new->attr); + + if (bgp_find_or_add_nexthop(bgp, bgp, nh_afi, new, NULL, + connected) || CHECK_FLAG(peer->flags, PEER_FLAG_IS_RFAPI_HD)) - bgp_path_info_set_flag(rn, new, BGP_PATH_VALID); + bgp_path_info_set_flag(dest, new, BGP_PATH_VALID); else { if (BGP_DEBUG(nht, NHT)) { char buf1[INET6_ADDRSTRLEN]; inet_ntop(AF_INET, (const void *)&attr_new->nexthop, buf1, INET6_ADDRSTRLEN); - zlog_debug("%s(%s): NH unresolved", - __FUNCTION__, buf1); + zlog_debug("%s(%s): NH unresolved", __func__, + buf1); } - bgp_path_info_unset_flag(rn, new, BGP_PATH_VALID); + bgp_path_info_unset_flag(dest, new, BGP_PATH_VALID); } } else - bgp_path_info_set_flag(rn, new, BGP_PATH_VALID); + bgp_path_info_set_flag(dest, new, BGP_PATH_VALID); /* Addpath ID */ new->addpath_rx_id = addpath_id; @@ -3499,38 +4067,35 @@ int bgp_update(struct peer *peer, struct prefix *p, uint32_t addpath_id, bgp_aggregate_increment(bgp, p, new, afi, safi); /* Register new BGP information. */ - bgp_path_info_add(rn, new); + bgp_path_info_add(dest, new); /* route_node_get lock */ - bgp_unlock_node(rn); + bgp_dest_unlock_node(dest); -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC if (safi == SAFI_MPLS_VPN) { - struct bgp_node *prn = NULL; + struct bgp_dest *pdest = NULL; struct bgp_table *table = NULL; - prn = bgp_node_get(bgp->rib[afi][safi], (struct prefix *)prd); - if (bgp_node_has_bgp_path_info_data(prn)) { - table = bgp_node_get_bgp_table_info(prn); + pdest = bgp_node_get(bgp->rib[afi][safi], (struct prefix *)prd); + if (bgp_dest_has_bgp_path_info_data(pdest)) { + table = bgp_dest_get_bgp_table_info(pdest); vnc_import_bgp_add_vnc_host_route_mode_resolve_nve( bgp, prd, table, p, new); } - bgp_unlock_node(prn); + bgp_dest_unlock_node(pdest); } #endif - /* If maximum prefix count is configured and current prefix - count exeed it. */ - if (bgp_maximum_prefix_overflow(peer, afi, safi, 0)) - return -1; - /* If this is an EVPN route, process for import. */ - if (safi == SAFI_EVPN) + if (safi == SAFI_EVPN && CHECK_FLAG(new->flags, BGP_PATH_VALID)) bgp_evpn_import_route(bgp, afi, safi, p, new); + hook_call(bgp_process, bgp, afi, safi, dest, peer, false); + /* Process change. */ - bgp_process(bgp, rn, afi, safi); + bgp_process(bgp, dest, afi, safi); if (SAFI_UNICAST == safi && (bgp->inst_type == BGP_INSTANCE_TYPE_VRF @@ -3542,7 +4107,7 @@ int bgp_update(struct peer *peer, struct prefix *p, uint32_t addpath_id, vpn_leak_to_vrf_update(bgp, new); } -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC if (SAFI_MPLS_VPN == safi) { mpls_label_t label_decoded = decode_label(label); @@ -3560,6 +4125,8 @@ int bgp_update(struct peer *peer, struct prefix *p, uint32_t addpath_id, /* This BGP update is filtered. Log the reason then update BGP entry. */ filtered: + hook_call(bgp_process, bgp, afi, safi, dest, peer, true); + if (bgp_debug_update(peer, p, NULL, 1)) { if (!peer->rcvd_attr_printed) { zlog_debug("%s rcvd UPDATE w/ attr: %s", peer->host, @@ -3592,12 +4159,12 @@ int bgp_update(struct peer *peer, struct prefix *p, uint32_t addpath_id, vpn_leak_to_vrf_withdraw(bgp, pi); } - bgp_rib_remove(rn, pi, peer, afi, safi); + bgp_rib_remove(dest, pi, peer, afi, safi); } - bgp_unlock_node(rn); + bgp_dest_unlock_node(dest); -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC /* * Filtered update is treated as an implicit withdrawal (see * bgp_rib_remove() @@ -3612,17 +4179,17 @@ int bgp_update(struct peer *peer, struct prefix *p, uint32_t addpath_id, return 0; } -int bgp_withdraw(struct peer *peer, struct prefix *p, uint32_t addpath_id, +int bgp_withdraw(struct peer *peer, const struct prefix *p, uint32_t addpath_id, struct attr *attr, afi_t afi, safi_t safi, int type, int sub_type, struct prefix_rd *prd, mpls_label_t *label, uint32_t num_labels, struct bgp_route_evpn *evpn) { struct bgp *bgp; char pfx_buf[BGP_PRD_PATH_STRLEN]; - struct bgp_node *rn; + struct bgp_dest *dest; struct bgp_path_info *pi; -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC if ((SAFI_MPLS_VPN == safi) || (SAFI_ENCAP == safi)) { rfapiProcessWithdraw(peer, NULL, p, prd, NULL, afi, safi, type, 0); @@ -3632,7 +4199,7 @@ int bgp_withdraw(struct peer *peer, struct prefix *p, uint32_t addpath_id, bgp = peer->bgp; /* Lookup node. */ - rn = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi, p, prd); + dest = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi, p, prd); /* If peer is soft reconfiguration enabled. Record input packet for * further calculation. @@ -3647,7 +4214,9 @@ int bgp_withdraw(struct peer *peer, struct prefix *p, uint32_t addpath_id, */ if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_SOFT_RECONFIG) && peer != bgp->peer_self) - if (!bgp_adj_in_unset(rn, peer, addpath_id)) { + if (!bgp_adj_in_unset(dest, peer, addpath_id)) { + peer->stat_pfx_dup_withdraw++; + if (bgp_debug_update(peer, p, NULL, 1)) { bgp_debug_rdpfxpath2str( afi, safi, prd, p, label, num_labels, @@ -3657,12 +4226,12 @@ int bgp_withdraw(struct peer *peer, struct prefix *p, uint32_t addpath_id, "%s withdrawing route %s not in adj-in", peer->host, pfx_buf); } - bgp_unlock_node(rn); + bgp_dest_unlock_node(dest); return 0; } /* Lookup withdrawn route. */ - for (pi = bgp_node_get_bgp_path_info(rn); pi; pi = pi->next) + for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) if (pi->peer == peer && pi->type == type && pi->sub_type == sub_type && pi->addpath_rx_id == addpath_id) @@ -3679,7 +4248,7 @@ int bgp_withdraw(struct peer *peer, struct prefix *p, uint32_t addpath_id, /* Withdraw specified route from routing table. */ if (pi && !CHECK_FLAG(pi->flags, BGP_PATH_HISTORY)) { - bgp_rib_withdraw(rn, pi, peer, afi, safi, prd); + bgp_rib_withdraw(dest, pi, peer, afi, safi, prd); if (SAFI_UNICAST == safi && (bgp->inst_type == BGP_INSTANCE_TYPE_VRF || bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) { @@ -3698,7 +4267,7 @@ int bgp_withdraw(struct peer *peer, struct prefix *p, uint32_t addpath_id, } /* Unlock bgp_node_get() lock. */ - bgp_unlock_node(rn); + bgp_dest_unlock_node(dest); return 0; } @@ -3805,23 +4374,27 @@ static void bgp_soft_reconfig_table(struct peer *peer, afi_t afi, safi_t safi, struct prefix_rd *prd) { int ret; - struct bgp_node *rn; + struct bgp_dest *dest; struct bgp_adj_in *ain; if (!table) table = peer->bgp->rib[afi][safi]; - for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) - for (ain = rn->adj_in; ain; ain = ain->next) { + for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) + for (ain = dest->adj_in; ain; ain = ain->next) { if (ain->peer != peer) continue; - struct bgp_path_info *pi = - bgp_node_get_bgp_path_info(rn); + struct bgp_path_info *pi; uint32_t num_labels = 0; mpls_label_t *label_pnt = NULL; struct bgp_route_evpn evpn; + for (pi = bgp_dest_get_bgp_path_info(dest); pi; + pi = pi->next) + if (pi->peer == peer) + break; + if (pi && pi->extra) num_labels = pi->extra->num_labels; if (num_labels) @@ -3832,13 +4405,14 @@ static void bgp_soft_reconfig_table(struct peer *peer, afi_t afi, safi_t safi, else memset(&evpn, 0, sizeof(evpn)); - ret = bgp_update(peer, &rn->p, ain->addpath_rx_id, - ain->attr, afi, safi, ZEBRA_ROUTE_BGP, + ret = bgp_update(peer, bgp_dest_get_prefix(dest), + ain->addpath_rx_id, ain->attr, afi, + safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, prd, label_pnt, num_labels, 1, &evpn); if (ret < 0) { - bgp_unlock_node(rn); + bgp_dest_unlock_node(dest); return; } } @@ -3846,7 +4420,7 @@ static void bgp_soft_reconfig_table(struct peer *peer, afi_t afi, safi_t safi, void bgp_soft_reconfig_in(struct peer *peer, afi_t afi, safi_t safi) { - struct bgp_node *rn; + struct bgp_dest *dest; struct bgp_table *table; if (peer->status != Established) @@ -3856,44 +4430,46 @@ void bgp_soft_reconfig_in(struct peer *peer, afi_t afi, safi_t safi) && (safi != SAFI_EVPN)) bgp_soft_reconfig_table(peer, afi, safi, NULL, NULL); else - for (rn = bgp_table_top(peer->bgp->rib[afi][safi]); rn; - rn = bgp_route_next(rn)) { - table = bgp_node_get_bgp_table_info(rn); - if (table != NULL) { - struct prefix_rd prd; + for (dest = bgp_table_top(peer->bgp->rib[afi][safi]); dest; + dest = bgp_route_next(dest)) { + table = bgp_dest_get_bgp_table_info(dest); - prd.family = AF_UNSPEC; - prd.prefixlen = 64; - memcpy(&prd.val, rn->p.u.val, 8); + if (table == NULL) + continue; - bgp_soft_reconfig_table(peer, afi, safi, table, - &prd); - } + const struct prefix *p = bgp_dest_get_prefix(dest); + struct prefix_rd prd; + + prd.family = AF_UNSPEC; + prd.prefixlen = 64; + memcpy(&prd.val, p->u.val, 8); + + bgp_soft_reconfig_table(peer, afi, safi, table, &prd); } } struct bgp_clear_node_queue { - struct bgp_node *rn; + struct bgp_dest *dest; }; static wq_item_status bgp_clear_route_node(struct work_queue *wq, void *data) { struct bgp_clear_node_queue *cnq = data; - struct bgp_node *rn = cnq->rn; + struct bgp_dest *dest = cnq->dest; struct peer *peer = wq->spec.data; struct bgp_path_info *pi; struct bgp *bgp; - afi_t afi = bgp_node_table(rn)->afi; - safi_t safi = bgp_node_table(rn)->safi; + afi_t afi = bgp_dest_table(dest)->afi; + safi_t safi = bgp_dest_table(dest)->safi; - assert(rn && peer); + assert(dest && peer); bgp = peer->bgp; /* It is possible that we have multiple paths for a prefix from a peer * if that peer is using AddPath. */ - for (pi = bgp_node_get_bgp_path_info(rn); pi; pi = pi->next) { + for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) { if (pi->peer != peer) continue; @@ -3902,13 +4478,14 @@ static wq_item_status bgp_clear_route_node(struct work_queue *wq, void *data) && peer->nsf[afi][safi] && !CHECK_FLAG(pi->flags, BGP_PATH_STALE) && !CHECK_FLAG(pi->flags, BGP_PATH_UNUSEABLE)) - bgp_path_info_set_flag(rn, pi, BGP_PATH_STALE); + bgp_path_info_set_flag(dest, pi, BGP_PATH_STALE); else { /* If this is an EVPN route, process for * un-import. */ if (safi == SAFI_EVPN) - bgp_evpn_unimport_route(bgp, afi, safi, &rn->p, - pi); + bgp_evpn_unimport_route( + bgp, afi, safi, + bgp_dest_get_prefix(dest), pi); /* Handle withdraw for VRF route-leaking and L3VPN */ if (SAFI_UNICAST == safi && (bgp->inst_type == BGP_INSTANCE_TYPE_VRF || @@ -3921,7 +4498,7 @@ static wq_item_status bgp_clear_route_node(struct work_queue *wq, void *data) vpn_leak_to_vrf_withdraw(bgp, pi); } - bgp_rib_remove(rn, pi, peer, afi, safi); + bgp_rib_remove(dest, pi, peer, afi, safi); } } return WQ_SUCCESS; @@ -3930,10 +4507,10 @@ static wq_item_status bgp_clear_route_node(struct work_queue *wq, void *data) static void bgp_clear_node_queue_del(struct work_queue *wq, void *data) { struct bgp_clear_node_queue *cnq = data; - struct bgp_node *rn = cnq->rn; - struct bgp_table *table = bgp_node_table(rn); + struct bgp_dest *dest = cnq->dest; + struct bgp_table *table = bgp_dest_table(dest); - bgp_unlock_node(rn); + bgp_dest_unlock_node(dest); bgp_table_unlock(table); XFREE(MTYPE_BGP_CLEAR_NODE_QUEUE, cnq); } @@ -3970,7 +4547,7 @@ static void bgp_clear_node_queue_init(struct peer *peer) static void bgp_clear_route_table(struct peer *peer, afi_t afi, safi_t safi, struct bgp_table *table) { - struct bgp_node *rn; + struct bgp_dest *dest; int force = bm->process_main_queue ? 0 : 1; if (!table) @@ -3980,7 +4557,7 @@ static void bgp_clear_route_table(struct peer *peer, afi_t afi, safi_t safi, if (!table) return; - for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) { + for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) { struct bgp_path_info *pi, *next; struct bgp_adj_in *ain; struct bgp_adj_in *ain_next; @@ -4020,35 +4597,35 @@ static void bgp_clear_route_table(struct peer *peer, afi_t afi, safi_t safi, * a peer * if that peer is using AddPath. */ - ain = rn->adj_in; + ain = dest->adj_in; while (ain) { ain_next = ain->next; if (ain->peer == peer) { - bgp_adj_in_remove(rn, ain); - bgp_unlock_node(rn); + bgp_adj_in_remove(dest, ain); + bgp_dest_unlock_node(dest); } ain = ain_next; } - for (pi = bgp_node_get_bgp_path_info(rn); pi; pi = next) { + for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = next) { next = pi->next; if (pi->peer != peer) continue; if (force) - bgp_path_info_reap(rn, pi); + bgp_path_info_reap(dest, pi); else { struct bgp_clear_node_queue *cnq; /* both unlocked in bgp_clear_node_queue_del */ - bgp_table_lock(bgp_node_table(rn)); - bgp_lock_node(rn); + bgp_table_lock(bgp_dest_table(dest)); + bgp_dest_lock_node(dest); cnq = XCALLOC( MTYPE_BGP_CLEAR_NODE_QUEUE, sizeof(struct bgp_clear_node_queue)); - cnq->rn = rn; + cnq->dest = dest; work_queue_add(peer->clear_node_queue, cnq); break; } @@ -4059,7 +4636,7 @@ static void bgp_clear_route_table(struct peer *peer, afi_t afi, safi_t safi, void bgp_clear_route(struct peer *peer, afi_t afi, safi_t safi) { - struct bgp_node *rn; + struct bgp_dest *dest; struct bgp_table *table; if (peer->clear_node_queue == NULL) @@ -4088,9 +4665,9 @@ void bgp_clear_route(struct peer *peer, afi_t afi, safi_t safi) if (safi != SAFI_MPLS_VPN && safi != SAFI_ENCAP && safi != SAFI_EVPN) bgp_clear_route_table(peer, afi, safi, NULL); else - for (rn = bgp_table_top(peer->bgp->rib[afi][safi]); rn; - rn = bgp_route_next(rn)) { - table = bgp_node_get_bgp_table_info(rn); + for (dest = bgp_table_top(peer->bgp->rib[afi][safi]); dest; + dest = bgp_route_next(dest)) { + table = bgp_dest_get_bgp_table_info(dest); if (!table) continue; @@ -4110,7 +4687,7 @@ void bgp_clear_route_all(struct peer *peer) FOREACH_AFI_SAFI (afi, safi) bgp_clear_route(peer, afi, safi); -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC rfapiProcessPeerDown(peer); #endif } @@ -4118,7 +4695,7 @@ void bgp_clear_route_all(struct peer *peer) void bgp_clear_adj_in(struct peer *peer, afi_t afi, safi_t safi) { struct bgp_table *table; - struct bgp_node *rn; + struct bgp_dest *dest; struct bgp_adj_in *ain; struct bgp_adj_in *ain_next; @@ -4127,15 +4704,15 @@ void bgp_clear_adj_in(struct peer *peer, afi_t afi, safi_t safi) /* It is possible that we have multiple paths for a prefix from a peer * if that peer is using AddPath. */ - for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) { - ain = rn->adj_in; + for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) { + ain = dest->adj_in; while (ain) { ain_next = ain->next; if (ain->peer == peer) { - bgp_adj_in_remove(rn, ain); - bgp_unlock_node(rn); + bgp_adj_in_remove(dest, ain); + bgp_dest_unlock_node(dest); } ain = ain_next; @@ -4145,23 +4722,23 @@ void bgp_clear_adj_in(struct peer *peer, afi_t afi, safi_t safi) void bgp_clear_stale_route(struct peer *peer, afi_t afi, safi_t safi) { - struct bgp_node *rn; + struct bgp_dest *dest; struct bgp_path_info *pi; struct bgp_table *table; if (safi == SAFI_MPLS_VPN) { - for (rn = bgp_table_top(peer->bgp->rib[afi][safi]); rn; - rn = bgp_route_next(rn)) { - struct bgp_node *rm; + for (dest = bgp_table_top(peer->bgp->rib[afi][safi]); dest; + dest = bgp_route_next(dest)) { + struct bgp_dest *rm; /* look for neighbor in tables */ - table = bgp_node_get_bgp_table_info(rn); + table = bgp_dest_get_bgp_table_info(dest); if (!table) continue; for (rm = bgp_table_top(table); rm; rm = bgp_route_next(rm)) - for (pi = bgp_node_get_bgp_path_info(rm); pi; + for (pi = bgp_dest_get_bgp_path_info(rm); pi; pi = pi->next) { if (pi->peer != peer) continue; @@ -4174,56 +4751,63 @@ void bgp_clear_stale_route(struct peer *peer, afi_t afi, safi_t safi) } } } else { - for (rn = bgp_table_top(peer->bgp->rib[afi][safi]); rn; - rn = bgp_route_next(rn)) - for (pi = bgp_node_get_bgp_path_info(rn); pi; + for (dest = bgp_table_top(peer->bgp->rib[afi][safi]); dest; + dest = bgp_route_next(dest)) + for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) { if (pi->peer != peer) continue; if (!CHECK_FLAG(pi->flags, BGP_PATH_STALE)) break; - bgp_rib_remove(rn, pi, peer, afi, safi); + bgp_rib_remove(dest, pi, peer, afi, safi); break; } } } -int bgp_outbound_policy_exists(struct peer *peer, struct bgp_filter *filter) +bool bgp_outbound_policy_exists(struct peer *peer, struct bgp_filter *filter) { + if (peer->sort == BGP_PEER_IBGP) + return true; + if (peer->sort == BGP_PEER_EBGP && (ROUTE_MAP_OUT_NAME(filter) || PREFIX_LIST_OUT_NAME(filter) || FILTER_LIST_OUT_NAME(filter) || DISTRIBUTE_OUT_NAME(filter))) - return 1; - return 0; + return true; + return false; } -int bgp_inbound_policy_exists(struct peer *peer, struct bgp_filter *filter) +bool bgp_inbound_policy_exists(struct peer *peer, struct bgp_filter *filter) { + if (peer->sort == BGP_PEER_IBGP) + return true; + if (peer->sort == BGP_PEER_EBGP && (ROUTE_MAP_IN_NAME(filter) || PREFIX_LIST_IN_NAME(filter) || FILTER_LIST_IN_NAME(filter) || DISTRIBUTE_IN_NAME(filter))) - return 1; - return 0; + return true; + return false; } static void bgp_cleanup_table(struct bgp *bgp, struct bgp_table *table, safi_t safi) { - struct bgp_node *rn; + struct bgp_dest *dest; struct bgp_path_info *pi; struct bgp_path_info *next; - for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) - for (pi = bgp_node_get_bgp_path_info(rn); pi; pi = next) { + for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) + for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = next) { + const struct prefix *p = bgp_dest_get_prefix(dest); + next = pi->next; /* Unimport EVPN routes from VRFs */ if (safi == SAFI_EVPN) bgp_evpn_unimport_route(bgp, AFI_L2VPN, - SAFI_EVPN, - &rn->p, pi); + SAFI_EVPN, p, pi); if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED) && pi->type == ZEBRA_ROUTE_BGP @@ -4232,10 +4816,10 @@ static void bgp_cleanup_table(struct bgp *bgp, struct bgp_table *table, || pi->sub_type == BGP_ROUTE_IMPORTED)) { if (bgp_fibupd_safi(safi)) - bgp_zebra_withdraw(&rn->p, pi, bgp, - safi); - bgp_path_info_reap(rn, pi); + bgp_zebra_withdraw(p, pi, bgp, safi); } + + bgp_path_info_reap(dest, pi); } } @@ -4243,7 +4827,7 @@ static void bgp_cleanup_table(struct bgp *bgp, struct bgp_table *table, void bgp_cleanup_routes(struct bgp *bgp) { afi_t afi; - struct bgp_node *rn; + struct bgp_dest *dest; struct bgp_table *table; for (afi = AFI_IP; afi < AFI_MAX; ++afi) { @@ -4257,37 +4841,37 @@ void bgp_cleanup_routes(struct bgp *bgp) if (afi != AFI_L2VPN) { safi_t safi; safi = SAFI_MPLS_VPN; - for (rn = bgp_table_top(bgp->rib[afi][safi]); rn; - rn = bgp_route_next(rn)) { - table = bgp_node_get_bgp_table_info(rn); + for (dest = bgp_table_top(bgp->rib[afi][safi]); dest; + dest = bgp_route_next(dest)) { + table = bgp_dest_get_bgp_table_info(dest); if (table != NULL) { bgp_cleanup_table(bgp, table, safi); bgp_table_finish(&table); - bgp_node_set_bgp_table_info(rn, NULL); - bgp_unlock_node(rn); + bgp_dest_set_bgp_table_info(dest, NULL); + bgp_dest_unlock_node(dest); } } safi = SAFI_ENCAP; - for (rn = bgp_table_top(bgp->rib[afi][safi]); rn; - rn = bgp_route_next(rn)) { - table = bgp_node_get_bgp_table_info(rn); + for (dest = bgp_table_top(bgp->rib[afi][safi]); dest; + dest = bgp_route_next(dest)) { + table = bgp_dest_get_bgp_table_info(dest); if (table != NULL) { bgp_cleanup_table(bgp, table, safi); bgp_table_finish(&table); - bgp_node_set_bgp_table_info(rn, NULL); - bgp_unlock_node(rn); + bgp_dest_set_bgp_table_info(dest, NULL); + bgp_dest_unlock_node(dest); } } } } - for (rn = bgp_table_top(bgp->rib[AFI_L2VPN][SAFI_EVPN]); rn; - rn = bgp_route_next(rn)) { - table = bgp_node_get_bgp_table_info(rn); + for (dest = bgp_table_top(bgp->rib[AFI_L2VPN][SAFI_EVPN]); dest; + dest = bgp_route_next(dest)) { + table = bgp_dest_get_bgp_table_info(dest); if (table != NULL) { bgp_cleanup_table(bgp, table, SAFI_EVPN); bgp_table_finish(&table); - bgp_node_set_bgp_table_info(rn, NULL); - bgp_unlock_node(rn); + bgp_dest_set_bgp_table_info(dest, NULL); + bgp_dest_unlock_node(dest); } } } @@ -4339,10 +4923,11 @@ int bgp_nlri_parse_ip(struct peer *peer, struct attr *attr, if (addpath_encoded) { /* When packet overflow occurs return immediately. */ - if (pnt + BGP_ADDPATH_ID_LEN > lim) + if (pnt + BGP_ADDPATH_ID_LEN >= lim) return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW; - addpath_id = ntohl(*((uint32_t *)pnt)); + memcpy(&addpath_id, pnt, BGP_ADDPATH_ID_LEN); + addpath_id = ntohl(addpath_id); pnt += BGP_ADDPATH_ID_LEN; } @@ -4481,25 +5066,23 @@ static void bgp_static_free(struct bgp_static *bgp_static) XFREE(MTYPE_BGP_STATIC, bgp_static); } -void bgp_static_update(struct bgp *bgp, struct prefix *p, +void bgp_static_update(struct bgp *bgp, const struct prefix *p, struct bgp_static *bgp_static, afi_t afi, safi_t safi) { - struct bgp_node *rn; + struct bgp_dest *dest; struct bgp_path_info *pi; struct bgp_path_info *new; struct bgp_path_info rmap_path; struct attr attr; struct attr *attr_new; - int ret; -#if ENABLE_BGP_VNC + route_map_result_t ret; +#ifdef ENABLE_BGP_VNC int vnc_implicit_withdraw = 0; #endif assert(bgp_static); - if (!bgp_static) - return; - rn = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi, p, NULL); + dest = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi, p, NULL); bgp_attr_default_set(&attr, BGP_ORIGIN_IGP); @@ -4541,19 +5124,19 @@ void bgp_static_update(struct bgp *bgp, struct prefix *p, return; } - if (bgp_flag_check(bgp, BGP_FLAG_GRACEFUL_SHUTDOWN)) + if (CHECK_FLAG(bgp->flags, BGP_FLAG_GRACEFUL_SHUTDOWN)) bgp_attr_add_gshut_community(&attr_tmp); attr_new = bgp_attr_intern(&attr_tmp); } else { - if (bgp_flag_check(bgp, BGP_FLAG_GRACEFUL_SHUTDOWN)) + if (CHECK_FLAG(bgp->flags, BGP_FLAG_GRACEFUL_SHUTDOWN)) bgp_attr_add_gshut_community(&attr); attr_new = bgp_attr_intern(&attr); } - for (pi = bgp_node_get_bgp_path_info(rn); pi; pi = pi->next) + for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) if (pi->peer == bgp->peer_self && pi->type == ZEBRA_ROUTE_BGP && pi->sub_type == BGP_ROUTE_STATIC) break; @@ -4561,21 +5144,21 @@ void bgp_static_update(struct bgp *bgp, struct prefix *p, if (pi) { if (attrhash_cmp(pi->attr, attr_new) && !CHECK_FLAG(pi->flags, BGP_PATH_REMOVED) - && !bgp_flag_check(bgp, BGP_FLAG_FORCE_STATIC_PROCESS)) { - bgp_unlock_node(rn); + && !CHECK_FLAG(bgp->flags, BGP_FLAG_FORCE_STATIC_PROCESS)) { + bgp_dest_unlock_node(dest); bgp_attr_unintern(&attr_new); aspath_unintern(&attr.aspath); return; } else { /* The attribute is changed. */ - bgp_path_info_set_flag(rn, pi, BGP_PATH_ATTR_CHANGED); + bgp_path_info_set_flag(dest, pi, BGP_PATH_ATTR_CHANGED); /* Rewrite BGP route information. */ if (CHECK_FLAG(pi->flags, BGP_PATH_REMOVED)) - bgp_path_info_restore(rn, pi); + bgp_path_info_restore(dest, pi); else bgp_aggregate_decrement(bgp, p, pi, afi, safi); -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC if ((afi == AFI_IP || afi == AFI_IP6) && (safi == SAFI_UNICAST)) { if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED)) { @@ -4594,7 +5177,7 @@ void bgp_static_update(struct bgp *bgp, struct prefix *p, bgp_attr_unintern(&pi->attr); pi->attr = attr_new; pi->uptime = bgp_clock(); -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC if ((afi == AFI_IP || afi == AFI_IP6) && (safi == SAFI_UNICAST)) { if (vnc_implicit_withdraw) { @@ -4606,7 +5189,7 @@ void bgp_static_update(struct bgp *bgp, struct prefix *p, #endif /* Nexthop reachability check. */ - if (bgp_flag_check(bgp, BGP_FLAG_IMPORT_CHECK) + if (CHECK_FLAG(bgp->flags, BGP_FLAG_IMPORT_CHECK) && (safi == SAFI_UNICAST || safi == SAFI_LABELED_UNICAST)) { @@ -4617,7 +5200,7 @@ void bgp_static_update(struct bgp *bgp, struct prefix *p, if (bgp_find_or_add_nexthop(bgp, bgp_nexthop, afi, pi, NULL, 0)) - bgp_path_info_set_flag(rn, pi, + bgp_path_info_set_flag(dest, pi, BGP_PATH_VALID); else { if (BGP_DEBUG(nht, NHT)) { @@ -4627,10 +5210,10 @@ void bgp_static_update(struct bgp *bgp, struct prefix *p, INET6_ADDRSTRLEN); zlog_debug( "%s(%s): Route not in table, not advertising", - __FUNCTION__, buf1); + __func__, buf1); } bgp_path_info_unset_flag( - rn, pi, BGP_PATH_VALID); + dest, pi, BGP_PATH_VALID); } } else { /* Delete the NHT structure if any, if we're @@ -4641,11 +5224,12 @@ void bgp_static_update(struct bgp *bgp, struct prefix *p, * process interaction */ bgp_unlink_nexthop(pi); - bgp_path_info_set_flag(rn, pi, BGP_PATH_VALID); + bgp_path_info_set_flag(dest, pi, + BGP_PATH_VALID); } /* Process change. */ bgp_aggregate_increment(bgp, p, pi, afi, safi); - bgp_process(bgp, rn, afi, safi); + bgp_process(bgp, dest, afi, safi); if (SAFI_UNICAST == safi && (bgp->inst_type == BGP_INSTANCE_TYPE_VRF @@ -4655,7 +5239,7 @@ void bgp_static_update(struct bgp *bgp, struct prefix *p, pi); } - bgp_unlock_node(rn); + bgp_dest_unlock_node(dest); aspath_unintern(&attr.aspath); return; } @@ -4663,12 +5247,12 @@ void bgp_static_update(struct bgp *bgp, struct prefix *p, /* Make new BGP info. */ new = info_make(ZEBRA_ROUTE_BGP, BGP_ROUTE_STATIC, 0, bgp->peer_self, - attr_new, rn); + attr_new, dest); /* Nexthop reachability check. */ - if (bgp_flag_check(bgp, BGP_FLAG_IMPORT_CHECK) + if (CHECK_FLAG(bgp->flags, BGP_FLAG_IMPORT_CHECK) && (safi == SAFI_UNICAST || safi == SAFI_LABELED_UNICAST)) { if (bgp_find_or_add_nexthop(bgp, bgp, afi, new, NULL, 0)) - bgp_path_info_set_flag(rn, new, BGP_PATH_VALID); + bgp_path_info_set_flag(dest, new, BGP_PATH_VALID); else { if (BGP_DEBUG(nht, NHT)) { char buf1[INET6_ADDRSTRLEN]; @@ -4676,9 +5260,9 @@ void bgp_static_update(struct bgp *bgp, struct prefix *p, INET6_ADDRSTRLEN); zlog_debug( "%s(%s): Route not in table, not advertising", - __FUNCTION__, buf1); + __func__, buf1); } - bgp_path_info_unset_flag(rn, new, BGP_PATH_VALID); + bgp_path_info_unset_flag(dest, new, BGP_PATH_VALID); } } else { /* Delete the NHT structure if any, if we're toggling between @@ -4687,20 +5271,20 @@ void bgp_static_update(struct bgp *bgp, struct prefix *p, */ bgp_unlink_nexthop(new); - bgp_path_info_set_flag(rn, new, BGP_PATH_VALID); + bgp_path_info_set_flag(dest, new, BGP_PATH_VALID); } /* Aggregate address increment. */ bgp_aggregate_increment(bgp, p, new, afi, safi); /* Register new BGP information. */ - bgp_path_info_add(rn, new); + bgp_path_info_add(dest, new); /* route_node_get lock */ - bgp_unlock_node(rn); + bgp_dest_unlock_node(dest); /* Process change. */ - bgp_process(bgp, rn, afi, safi); + bgp_process(bgp, dest, afi, safi); if (SAFI_UNICAST == safi && (bgp->inst_type == BGP_INSTANCE_TYPE_VRF @@ -4712,16 +5296,16 @@ void bgp_static_update(struct bgp *bgp, struct prefix *p, aspath_unintern(&attr.aspath); } -void bgp_static_withdraw(struct bgp *bgp, struct prefix *p, afi_t afi, +void bgp_static_withdraw(struct bgp *bgp, const struct prefix *p, afi_t afi, safi_t safi) { - struct bgp_node *rn; + struct bgp_dest *dest; struct bgp_path_info *pi; - rn = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi, p, NULL); + dest = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi, p, NULL); /* Check selected route and self inserted route. */ - for (pi = bgp_node_get_bgp_path_info(rn); pi; pi = pi->next) + for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) if (pi->peer == bgp->peer_self && pi->type == ZEBRA_ROUTE_BGP && pi->sub_type == BGP_ROUTE_STATIC) break; @@ -4735,35 +5319,35 @@ void bgp_static_withdraw(struct bgp *bgp, struct prefix *p, afi_t afi, } bgp_aggregate_decrement(bgp, p, pi, afi, safi); bgp_unlink_nexthop(pi); - bgp_path_info_delete(rn, pi); - bgp_process(bgp, rn, afi, safi); + bgp_path_info_delete(dest, pi); + bgp_process(bgp, dest, afi, safi); } /* Unlock bgp_node_lookup. */ - bgp_unlock_node(rn); + bgp_dest_unlock_node(dest); } /* * Used for SAFI_MPLS_VPN and SAFI_ENCAP */ -static void bgp_static_withdraw_safi(struct bgp *bgp, struct prefix *p, +static void bgp_static_withdraw_safi(struct bgp *bgp, const struct prefix *p, afi_t afi, safi_t safi, struct prefix_rd *prd) { - struct bgp_node *rn; + struct bgp_dest *dest; struct bgp_path_info *pi; - rn = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi, p, prd); + dest = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi, p, prd); /* Check selected route and self inserted route. */ - for (pi = bgp_node_get_bgp_path_info(rn); pi; pi = pi->next) + for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) if (pi->peer == bgp->peer_self && pi->type == ZEBRA_ROUTE_BGP && pi->sub_type == BGP_ROUTE_STATIC) break; /* Withdraw static BGP route from routing table. */ if (pi) { -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC rfapiProcessWithdraw( pi->peer, NULL, p, prd, pi->attr, afi, safi, pi->type, 1); /* Kill, since it is an administrative change */ @@ -4773,24 +5357,24 @@ static void bgp_static_withdraw_safi(struct bgp *bgp, struct prefix *p, vpn_leak_to_vrf_withdraw(bgp, pi); } bgp_aggregate_decrement(bgp, p, pi, afi, safi); - bgp_path_info_delete(rn, pi); - bgp_process(bgp, rn, afi, safi); + bgp_path_info_delete(dest, pi); + bgp_process(bgp, dest, afi, safi); } /* Unlock bgp_node_lookup. */ - bgp_unlock_node(rn); + bgp_dest_unlock_node(dest); } -static void bgp_static_update_safi(struct bgp *bgp, struct prefix *p, +static void bgp_static_update_safi(struct bgp *bgp, const struct prefix *p, struct bgp_static *bgp_static, afi_t afi, safi_t safi) { - struct bgp_node *rn; + struct bgp_dest *dest; struct bgp_path_info *new; struct attr *attr_new; struct attr attr = {0}; struct bgp_path_info *pi; -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC mpls_label_t label = 0; #endif uint32_t num_labels = 0; @@ -4800,8 +5384,8 @@ static void bgp_static_update_safi(struct bgp *bgp, struct prefix *p, if (bgp_static->label != MPLS_INVALID_LABEL) num_labels = 1; - rn = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi, p, - &bgp_static->prd); + dest = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi, p, + &bgp_static->prd); bgp_attr_default_set(&attr, BGP_ORIGIN_IGP); @@ -4823,7 +5407,7 @@ static void bgp_static_update_safi(struct bgp *bgp, struct prefix *p, else if (bgp_static->gatewayIp.family == AF_INET6) memcpy(&(add.ipv6), &(bgp_static->gatewayIp.u.prefix6), sizeof(struct in6_addr)); - overlay_index_update(&attr, bgp_static->eth_s_id, &add); + memcpy(&attr.esi, bgp_static->eth_s_id, sizeof(esi_t)); if (bgp_static->encap_tunneltype == BGP_ENCAP_TYPE_VXLAN) { struct bgp_encap_type_vxlan bet; memset(&bet, 0, sizeof(struct bgp_encap_type_vxlan)); @@ -4838,7 +5422,7 @@ static void bgp_static_update_safi(struct bgp *bgp, struct prefix *p, if (bgp_static->rmap.name) { struct attr attr_tmp = attr; struct bgp_path_info rmap_path; - int ret; + route_map_result_t ret; rmap_path.peer = bgp->peer_self; rmap_path.attr = &attr_tmp; @@ -4866,7 +5450,7 @@ static void bgp_static_update_safi(struct bgp *bgp, struct prefix *p, attr_new = bgp_attr_intern(&attr); } - for (pi = bgp_node_get_bgp_path_info(rn); pi; pi = pi->next) + for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) if (pi->peer == bgp->peer_self && pi->type == ZEBRA_ROUTE_BGP && pi->sub_type == BGP_ROUTE_STATIC) break; @@ -4874,43 +5458,43 @@ static void bgp_static_update_safi(struct bgp *bgp, struct prefix *p, if (pi) { memset(&add, 0, sizeof(union gw_addr)); if (attrhash_cmp(pi->attr, attr_new) - && overlay_index_equal(afi, pi, bgp_static->eth_s_id, &add) + && overlay_index_equal(afi, pi, &add) && !CHECK_FLAG(pi->flags, BGP_PATH_REMOVED)) { - bgp_unlock_node(rn); + bgp_dest_unlock_node(dest); bgp_attr_unintern(&attr_new); aspath_unintern(&attr.aspath); return; } else { /* The attribute is changed. */ - bgp_path_info_set_flag(rn, pi, BGP_PATH_ATTR_CHANGED); + bgp_path_info_set_flag(dest, pi, BGP_PATH_ATTR_CHANGED); /* Rewrite BGP route information. */ if (CHECK_FLAG(pi->flags, BGP_PATH_REMOVED)) - bgp_path_info_restore(rn, pi); + bgp_path_info_restore(dest, pi); else bgp_aggregate_decrement(bgp, p, pi, afi, safi); bgp_attr_unintern(&pi->attr); pi->attr = attr_new; pi->uptime = bgp_clock(); -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC if (pi->extra) label = decode_label(&pi->extra->label[0]); #endif /* Process change. */ bgp_aggregate_increment(bgp, p, pi, afi, safi); - bgp_process(bgp, rn, afi, safi); + bgp_process(bgp, dest, afi, safi); if (SAFI_MPLS_VPN == safi && bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT) { vpn_leak_to_vrf_update(bgp, pi); } -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC rfapiProcessUpdate(pi->peer, NULL, p, &bgp_static->prd, pi->attr, afi, safi, pi->type, pi->sub_type, &label); #endif - bgp_unlock_node(rn); + bgp_dest_unlock_node(dest); aspath_unintern(&attr.aspath); return; } @@ -4919,14 +5503,14 @@ static void bgp_static_update_safi(struct bgp *bgp, struct prefix *p, /* Make new BGP info. */ new = info_make(ZEBRA_ROUTE_BGP, BGP_ROUTE_STATIC, 0, bgp->peer_self, - attr_new, rn); + attr_new, dest); SET_FLAG(new->flags, BGP_PATH_VALID); new->extra = bgp_path_info_extra_new(); if (num_labels) { new->extra->label[0] = bgp_static->label; new->extra->num_labels = num_labels; } -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC label = decode_label(&bgp_static->label); #endif @@ -4934,18 +5518,18 @@ static void bgp_static_update_safi(struct bgp *bgp, struct prefix *p, bgp_aggregate_increment(bgp, p, new, afi, safi); /* Register new BGP information. */ - bgp_path_info_add(rn, new); + bgp_path_info_add(dest, new); /* route_node_get lock */ - bgp_unlock_node(rn); + bgp_dest_unlock_node(dest); /* Process change. */ - bgp_process(bgp, rn, afi, safi); + bgp_process(bgp, dest, afi, safi); if (SAFI_MPLS_VPN == safi && bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT) { vpn_leak_to_vrf_update(bgp, new); } -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC rfapiProcessUpdate(new->peer, NULL, p, &bgp_static->prd, new->attr, afi, safi, new->type, new->sub_type, &label); #endif @@ -4964,7 +5548,7 @@ static int bgp_static_set(struct vty *vty, const char *negate, int ret; struct prefix p; struct bgp_static *bgp_static; - struct bgp_node *rn; + struct bgp_dest *dest; uint8_t need_update = 0; /* Convert IP prefix string to struct prefix. */ @@ -4983,14 +5567,14 @@ static int bgp_static_set(struct vty *vty, const char *negate, if (negate) { /* Set BGP static route configuration. */ - rn = bgp_node_lookup(bgp->route[afi][safi], &p); + dest = bgp_node_lookup(bgp->route[afi][safi], &p); - if (!rn) { + if (!dest) { vty_out(vty, "%% Can't find static route specified\n"); return CMD_WARNING_CONFIG_FAILED; } - bgp_static = bgp_node_get_bgp_static_info(rn); + bgp_static = bgp_dest_get_bgp_static_info(dest); if ((label_index != BGP_INVALID_LABEL_INDEX) && (label_index != bgp_static->label_index)) { @@ -5012,15 +5596,14 @@ static int bgp_static_set(struct vty *vty, const char *negate, /* Clear configuration. */ bgp_static_free(bgp_static); - bgp_node_set_bgp_static_info(rn, NULL); - bgp_unlock_node(rn); - bgp_unlock_node(rn); + bgp_dest_set_bgp_static_info(dest, NULL); + bgp_dest_unlock_node(dest); + bgp_dest_unlock_node(dest); } else { /* Set BGP static route configuration. */ - rn = bgp_node_get(bgp->route[afi][safi], &p); - - bgp_static = bgp_node_get_bgp_static_info(rn); + dest = bgp_node_get(bgp->route[afi][safi], &p); + bgp_static = bgp_dest_get_bgp_static_info(dest); if (bgp_static) { /* Configuration change. */ /* Label index cannot be changed. */ @@ -5052,18 +5635,17 @@ static int bgp_static_set(struct vty *vty, const char *negate, bgp_static->rmap.name); route_map_counter_decrement( bgp_static->rmap.map); - bgp_static->rmap.name = NULL; bgp_static->rmap.map = NULL; bgp_static->valid = 0; } - bgp_unlock_node(rn); + bgp_dest_unlock_node(dest); } else { /* New configuration. */ bgp_static = bgp_static_new(); bgp_static->backdoor = backdoor; bgp_static->valid = 0; bgp_static->igpmetric = 0; - bgp_static->igpnexthop.s_addr = 0; + bgp_static->igpnexthop.s_addr = INADDR_ANY; bgp_static->label_index = label_index; if (rmap) { @@ -5078,7 +5660,7 @@ static int bgp_static_set(struct vty *vty, const char *negate, route_map_counter_increment( bgp_static->rmap.map); } - bgp_node_set_bgp_static_info(rn, bgp_static); + bgp_dest_set_bgp_static_info(dest, bgp_static); } bgp_static->valid = 1; @@ -5096,34 +5678,34 @@ void bgp_static_add(struct bgp *bgp) { afi_t afi; safi_t safi; - struct bgp_node *rn; - struct bgp_node *rm; + struct bgp_dest *dest; + struct bgp_dest *rm; struct bgp_table *table; struct bgp_static *bgp_static; FOREACH_AFI_SAFI (afi, safi) - for (rn = bgp_table_top(bgp->route[afi][safi]); rn; - rn = bgp_route_next(rn)) { - if (!bgp_node_has_bgp_path_info_data(rn)) + for (dest = bgp_table_top(bgp->route[afi][safi]); dest; + dest = bgp_route_next(dest)) { + if (!bgp_dest_has_bgp_path_info_data(dest)) continue; if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP) || (safi == SAFI_EVPN)) { - table = bgp_node_get_bgp_table_info(rn); + table = bgp_dest_get_bgp_table_info(dest); for (rm = bgp_table_top(table); rm; rm = bgp_route_next(rm)) { bgp_static = - bgp_node_get_bgp_static_info( + bgp_dest_get_bgp_static_info( rm); - bgp_static_update_safi(bgp, &rm->p, - bgp_static, afi, - safi); + bgp_static_update_safi( + bgp, bgp_dest_get_prefix(rm), + bgp_static, afi, safi); } } else { bgp_static_update( - bgp, &rn->p, - bgp_node_get_bgp_static_info(rn), afi, + bgp, bgp_dest_get_prefix(dest), + bgp_dest_get_bgp_static_info(dest), afi, safi); } } @@ -5135,42 +5717,48 @@ void bgp_static_delete(struct bgp *bgp) { afi_t afi; safi_t safi; - struct bgp_node *rn; - struct bgp_node *rm; + struct bgp_dest *dest; + struct bgp_dest *rm; struct bgp_table *table; struct bgp_static *bgp_static; FOREACH_AFI_SAFI (afi, safi) - for (rn = bgp_table_top(bgp->route[afi][safi]); rn; - rn = bgp_route_next(rn)) { - if (!bgp_node_has_bgp_path_info_data(rn)) + for (dest = bgp_table_top(bgp->route[afi][safi]); dest; + dest = bgp_route_next(dest)) { + if (!bgp_dest_has_bgp_path_info_data(dest)) continue; if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP) || (safi == SAFI_EVPN)) { - table = bgp_node_get_bgp_table_info(rn); + table = bgp_dest_get_bgp_table_info(dest); for (rm = bgp_table_top(table); rm; rm = bgp_route_next(rm)) { bgp_static = - bgp_node_get_bgp_static_info( + bgp_dest_get_bgp_static_info( rm); if (!bgp_static) continue; bgp_static_withdraw_safi( - bgp, &rm->p, AFI_IP, safi, - (struct prefix_rd *)&rn->p); + bgp, bgp_dest_get_prefix(rm), + AFI_IP, safi, + (struct prefix_rd *) + bgp_dest_get_prefix( + dest)); bgp_static_free(bgp_static); - bgp_node_set_bgp_static_info(rn, NULL); - bgp_unlock_node(rn); + bgp_dest_set_bgp_static_info(rm, + NULL); + bgp_dest_unlock_node(rm); } } else { - bgp_static = bgp_node_get_bgp_static_info(rn); - bgp_static_withdraw(bgp, &rn->p, afi, safi); + bgp_static = bgp_dest_get_bgp_static_info(dest); + bgp_static_withdraw(bgp, + bgp_dest_get_prefix(dest), + afi, safi); bgp_static_free(bgp_static); - bgp_node_set_bgp_static_info(rn, NULL); - bgp_unlock_node(rn); + bgp_dest_set_bgp_static_info(dest, NULL); + bgp_dest_unlock_node(dest); } } } @@ -5179,63 +5767,72 @@ void bgp_static_redo_import_check(struct bgp *bgp) { afi_t afi; safi_t safi; - struct bgp_node *rn; - struct bgp_node *rm; + struct bgp_dest *dest; + struct bgp_dest *rm; struct bgp_table *table; struct bgp_static *bgp_static; /* Use this flag to force reprocessing of the route */ - bgp_flag_set(bgp, BGP_FLAG_FORCE_STATIC_PROCESS); + SET_FLAG(bgp->flags, BGP_FLAG_FORCE_STATIC_PROCESS); FOREACH_AFI_SAFI (afi, safi) { - for (rn = bgp_table_top(bgp->route[afi][safi]); rn; - rn = bgp_route_next(rn)) { - if (!bgp_node_has_bgp_path_info_data(rn)) + for (dest = bgp_table_top(bgp->route[afi][safi]); dest; + dest = bgp_route_next(dest)) { + if (!bgp_dest_has_bgp_path_info_data(dest)) continue; if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP) || (safi == SAFI_EVPN)) { - table = bgp_node_get_bgp_table_info(rn); + table = bgp_dest_get_bgp_table_info(dest); for (rm = bgp_table_top(table); rm; rm = bgp_route_next(rm)) { bgp_static = - bgp_node_get_bgp_static_info( + bgp_dest_get_bgp_static_info( rm); - bgp_static_update_safi(bgp, &rm->p, - bgp_static, afi, - safi); + bgp_static_update_safi( + bgp, bgp_dest_get_prefix(rm), + bgp_static, afi, safi); } } else { - bgp_static = bgp_node_get_bgp_static_info(rn); - bgp_static_update(bgp, &rn->p, bgp_static, afi, - safi); + bgp_static = bgp_dest_get_bgp_static_info(dest); + bgp_static_update(bgp, + bgp_dest_get_prefix(dest), + bgp_static, afi, safi); } } } - bgp_flag_unset(bgp, BGP_FLAG_FORCE_STATIC_PROCESS); + UNSET_FLAG(bgp->flags, BGP_FLAG_FORCE_STATIC_PROCESS); } static void bgp_purge_af_static_redist_routes(struct bgp *bgp, afi_t afi, safi_t safi) { struct bgp_table *table; - struct bgp_node *rn; + struct bgp_dest *dest; struct bgp_path_info *pi; + /* Do not install the aggregate route if BGP is in the + * process of termination. + */ + if (CHECK_FLAG(bgp->flags, BGP_FLAG_DELETE_IN_PROGRESS) + || (bgp->peer_self == NULL)) + return; + table = bgp->rib[afi][safi]; - for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) { - for (pi = bgp_node_get_bgp_path_info(rn); pi; pi = pi->next) { + for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) { + for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) { if (pi->peer == bgp->peer_self && ((pi->type == ZEBRA_ROUTE_BGP && pi->sub_type == BGP_ROUTE_STATIC) || (pi->type != ZEBRA_ROUTE_BGP && pi->sub_type == BGP_ROUTE_REDISTRIBUTE))) { - bgp_aggregate_decrement(bgp, &rn->p, pi, afi, - safi); + bgp_aggregate_decrement( + bgp, bgp_dest_get_prefix(dest), pi, afi, + safi); bgp_unlink_nexthop(pi); - bgp_path_info_delete(rn, pi); - bgp_process(bgp, rn, afi, safi); + bgp_path_info_delete(dest, pi); + bgp_process(bgp, dest, afi, safi); } } } @@ -5269,8 +5866,8 @@ int bgp_static_set_safi(afi_t afi, safi_t safi, struct vty *vty, int ret; struct prefix p; struct prefix_rd prd; - struct bgp_node *prn; - struct bgp_node *rn; + struct bgp_dest *pdest; + struct bgp_dest *dest; struct bgp_table *table; struct bgp_static *bgp_static; mpls_label_t label = MPLS_INVALID_LABEL; @@ -5330,24 +5927,24 @@ int bgp_static_set_safi(afi_t afi, safi_t safi, struct vty *vty, } } } - prn = bgp_node_get(bgp->route[afi][safi], (struct prefix *)&prd); - if (!bgp_node_has_bgp_path_info_data(prn)) - bgp_node_set_bgp_table_info(prn, + pdest = bgp_node_get(bgp->route[afi][safi], (struct prefix *)&prd); + if (!bgp_dest_has_bgp_path_info_data(pdest)) + bgp_dest_set_bgp_table_info(pdest, bgp_table_init(bgp, afi, safi)); - table = bgp_node_get_bgp_table_info(prn); + table = bgp_dest_get_bgp_table_info(pdest); - rn = bgp_node_get(table, &p); + dest = bgp_node_get(table, &p); - if (bgp_node_has_bgp_path_info_data(rn)) { + if (bgp_dest_has_bgp_path_info_data(dest)) { vty_out(vty, "%% Same network configuration exists\n"); - bgp_unlock_node(rn); + bgp_dest_unlock_node(dest); } else { /* New configuration. */ bgp_static = bgp_static_new(); bgp_static->backdoor = 0; bgp_static->valid = 0; bgp_static->igpmetric = 0; - bgp_static->igpnexthop.s_addr = 0; + bgp_static->igpnexthop.s_addr = INADDR_ANY; bgp_static->label = label; bgp_static->prd = prd; @@ -5365,7 +5962,7 @@ int bgp_static_set_safi(afi_t afi, safi_t safi, struct vty *vty, if (esi) { bgp_static->eth_s_id = XCALLOC(MTYPE_ATTR, - sizeof(struct eth_segment_id)); + sizeof(esi_t)); str2esi(esi, bgp_static->eth_s_id); } if (routermac) { @@ -5377,7 +5974,7 @@ int bgp_static_set_safi(afi_t afi, safi_t safi, struct vty *vty, if (gwip) prefix_copy(&bgp_static->gatewayIp, &gw_ip); } - bgp_node_set_bgp_static_info(rn, bgp_static); + bgp_dest_set_bgp_static_info(dest, bgp_static); bgp_static->valid = 1; bgp_static_update_safi(bgp, &p, bgp_static, afi, safi); @@ -5396,8 +5993,8 @@ int bgp_static_unset_safi(afi_t afi, safi_t safi, struct vty *vty, int ret; struct prefix p; struct prefix_rd prd; - struct bgp_node *prn; - struct bgp_node *rn; + struct bgp_dest *pdest; + struct bgp_dest *dest; struct bgp_table *table; struct bgp_static *bgp_static; mpls_label_t label = MPLS_INVALID_LABEL; @@ -5427,24 +6024,24 @@ int bgp_static_unset_safi(afi_t afi, safi_t safi, struct vty *vty, encode_label(label_val, &label); } - prn = bgp_node_get(bgp->route[afi][safi], (struct prefix *)&prd); - if (!bgp_node_has_bgp_path_info_data(prn)) - bgp_node_set_bgp_table_info(prn, + pdest = bgp_node_get(bgp->route[afi][safi], (struct prefix *)&prd); + if (!bgp_dest_has_bgp_path_info_data(pdest)) + bgp_dest_set_bgp_table_info(pdest, bgp_table_init(bgp, afi, safi)); else - bgp_unlock_node(prn); - table = bgp_node_get_bgp_table_info(prn); + bgp_dest_unlock_node(pdest); + table = bgp_dest_get_bgp_table_info(pdest); - rn = bgp_node_lookup(table, &p); + dest = bgp_node_lookup(table, &p); - if (rn) { + if (dest) { bgp_static_withdraw_safi(bgp, &p, afi, safi, &prd); - bgp_static = bgp_node_get_bgp_static_info(rn); + bgp_static = bgp_dest_get_bgp_static_info(dest); bgp_static_free(bgp_static); - bgp_node_set_bgp_static_info(rn, NULL); - bgp_unlock_node(rn); - bgp_unlock_node(rn); + bgp_dest_set_bgp_static_info(dest, NULL); + bgp_dest_unlock_node(dest); + bgp_dest_unlock_node(dest); } else vty_out(vty, "%% Can't find the route\n"); @@ -5467,7 +6064,6 @@ static int bgp_table_map_set(struct vty *vty, afi_t afi, safi_t safi, } else { XFREE(MTYPE_ROUTE_MAP_NAME, rmap->name); route_map_counter_decrement(rmap->map); - rmap->name = NULL; rmap->map = NULL; } @@ -5486,7 +6082,6 @@ static int bgp_table_map_unset(struct vty *vty, afi_t afi, safi_t safi, rmap = &bgp->table_map[afi][safi]; XFREE(MTYPE_ROUTE_MAP_NAME, rmap->name); route_map_counter_decrement(rmap->map); - rmap->name = NULL; rmap->map = NULL; if (bgp_fibupd_safi(safi)) @@ -5587,14 +6182,16 @@ static struct bgp_aggregate *bgp_aggregate_new(void) static void bgp_aggregate_free(struct bgp_aggregate *aggregate) { + XFREE(MTYPE_ROUTE_MAP_NAME, aggregate->rmap.name); + route_map_counter_decrement(aggregate->rmap.map); XFREE(MTYPE_BGP_AGGREGATE, aggregate); } -static int bgp_aggregate_info_same(struct bgp_path_info *pi, uint8_t origin, - struct aspath *aspath, - struct community *comm, - struct ecommunity *ecomm, - struct lcommunity *lcomm) +static bool bgp_aggregate_info_same(struct bgp_path_info *pi, uint8_t origin, + struct aspath *aspath, + struct community *comm, + struct ecommunity *ecomm, + struct lcommunity *lcomm) { static struct aspath *ae = NULL; @@ -5602,47 +6199,45 @@ static int bgp_aggregate_info_same(struct bgp_path_info *pi, uint8_t origin, ae = aspath_empty(); if (!pi) - return 0; + return false; if (origin != pi->attr->origin) - return 0; + return false; if (!aspath_cmp(pi->attr->aspath, (aspath) ? aspath : ae)) - return 0; + return false; if (!community_cmp(pi->attr->community, comm)) - return 0; + return false; if (!ecommunity_cmp(pi->attr->ecommunity, ecomm)) - return 0; + return false; if (!lcommunity_cmp(pi->attr->lcommunity, lcomm)) - return 0; + return false; if (!CHECK_FLAG(pi->flags, BGP_PATH_VALID)) - return 0; + return false; - return 1; + return true; } -static void bgp_aggregate_install(struct bgp *bgp, afi_t afi, safi_t safi, - struct prefix *p, uint8_t origin, - struct aspath *aspath, - struct community *community, - struct ecommunity *ecommunity, - struct lcommunity *lcommunity, - uint8_t atomic_aggregate, - struct bgp_aggregate *aggregate) +static void bgp_aggregate_install( + struct bgp *bgp, afi_t afi, safi_t safi, const struct prefix *p, + uint8_t origin, struct aspath *aspath, struct community *community, + struct ecommunity *ecommunity, struct lcommunity *lcommunity, + uint8_t atomic_aggregate, struct bgp_aggregate *aggregate) { - struct bgp_node *rn; + struct bgp_dest *dest; struct bgp_table *table; struct bgp_path_info *pi, *orig, *new; + struct attr *attr; table = bgp->rib[afi][safi]; - rn = bgp_node_get(table, p); + dest = bgp_node_get(table, p); - for (orig = pi = bgp_node_get_bgp_path_info(rn); pi; pi = pi->next) + for (orig = pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) if (pi->peer == bgp->peer_self && pi->type == ZEBRA_ROUTE_BGP && pi->sub_type == BGP_ROUTE_AGGREGATE) break; @@ -5654,7 +6249,7 @@ static void bgp_aggregate_install(struct bgp *bgp, afi_t afi, safi_t safi, */ if (bgp_aggregate_info_same(orig, origin, aspath, community, ecommunity, lcommunity)) { - bgp_unlock_node(rn); + bgp_dest_unlock_node(dest); if (aspath) aspath_free(aspath); @@ -5672,20 +6267,24 @@ static void bgp_aggregate_install(struct bgp *bgp, afi_t afi, safi_t safi, * Mark the old as unusable */ if (pi) - bgp_path_info_delete(rn, pi); + bgp_path_info_delete(dest, pi); + + attr = bgp_attr_aggregate_intern( + bgp, origin, aspath, community, ecommunity, lcommunity, + aggregate, atomic_aggregate, p); + + if (!attr) { + bgp_aggregate_delete(bgp, p, afi, safi, aggregate); + return; + } new = info_make(ZEBRA_ROUTE_BGP, BGP_ROUTE_AGGREGATE, 0, - bgp->peer_self, - bgp_attr_aggregate_intern(bgp, origin, aspath, - community, ecommunity, - lcommunity, - aggregate->as_set, - atomic_aggregate), - rn); + bgp->peer_self, attr, dest); + SET_FLAG(new->flags, BGP_PATH_VALID); - bgp_path_info_add(rn, new); - bgp_process(bgp, rn, afi, safi); + bgp_path_info_add(dest, new); + bgp_process(bgp, dest, afi, safi); } else { for (pi = orig; pi; pi = pi->next) if (pi->peer == bgp->peer_self @@ -5695,22 +6294,21 @@ static void bgp_aggregate_install(struct bgp *bgp, afi_t afi, safi_t safi, /* Withdraw static BGP route from routing table. */ if (pi) { - bgp_path_info_delete(rn, pi); - bgp_process(bgp, rn, afi, safi); + bgp_path_info_delete(dest, pi); + bgp_process(bgp, dest, afi, safi); } } - bgp_unlock_node(rn); + bgp_dest_unlock_node(dest); } /* Update an aggregate as routes are added/removed from the BGP table */ -static void bgp_aggregate_route(struct bgp *bgp, struct prefix *p, - afi_t afi, safi_t safi, - struct bgp_aggregate *aggregate) +void bgp_aggregate_route(struct bgp *bgp, const struct prefix *p, afi_t afi, + safi_t safi, struct bgp_aggregate *aggregate) { struct bgp_table *table; - struct bgp_node *top; - struct bgp_node *rn; + struct bgp_dest *top; + struct bgp_dest *dest; uint8_t origin; struct aspath *aspath = NULL; struct community *community = NULL; @@ -5720,6 +6318,13 @@ static void bgp_aggregate_route(struct bgp *bgp, struct prefix *p, unsigned long match = 0; uint8_t atomic_aggregate = 0; + /* If the bgp instance is being deleted or self peer is deleted + * then do not create aggregate route + */ + if (CHECK_FLAG(bgp->flags, BGP_FLAG_DELETE_IN_PROGRESS) + || (bgp->peer_self == NULL)) + return; + /* ORIGIN attribute: If at least one route among routes that are aggregated has ORIGIN with the value INCOMPLETE, then the aggregated route must have the ORIGIN attribute with the value @@ -5733,14 +6338,16 @@ static void bgp_aggregate_route(struct bgp *bgp, struct prefix *p, table = bgp->rib[afi][safi]; top = bgp_node_get(table, p); - for (rn = bgp_node_get(table, p); rn; - rn = bgp_route_next_until(rn, top)) { - if (rn->p.prefixlen <= p->prefixlen) + for (dest = bgp_node_get(table, p); dest; + dest = bgp_route_next_until(dest, top)) { + const struct prefix *dest_p = bgp_dest_get_prefix(dest); + + if (dest_p->prefixlen <= p->prefixlen) continue; match = 0; - for (pi = bgp_node_get_bgp_path_info(rn); pi; pi = pi->next) { + for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) { if (BGP_PATH_HOLDDOWN(pi)) continue; @@ -5757,7 +6364,7 @@ static void bgp_aggregate_route(struct bgp *bgp, struct prefix *p, */ if (aggregate->summary_only) { (bgp_path_info_extra_get(pi))->suppress++; - bgp_path_info_set_flag(rn, pi, + bgp_path_info_set_flag(dest, pi, BGP_PATH_ATTR_CHANGED); match++; } @@ -5796,34 +6403,42 @@ static void bgp_aggregate_route(struct bgp *bgp, struct prefix *p, */ /* Compute aggregate route's as-path. */ - bgp_compute_aggregate_aspath(aggregate, - pi->attr->aspath); + bgp_compute_aggregate_aspath_hash(aggregate, + pi->attr->aspath); /* Compute aggregate route's community. */ if (pi->attr->community) - bgp_compute_aggregate_community( + bgp_compute_aggregate_community_hash( aggregate, pi->attr->community); /* Compute aggregate route's extended community. */ if (pi->attr->ecommunity) - bgp_compute_aggregate_ecommunity( + bgp_compute_aggregate_ecommunity_hash( aggregate, pi->attr->ecommunity); /* Compute aggregate route's large community. */ if (pi->attr->lcommunity) - bgp_compute_aggregate_lcommunity( + bgp_compute_aggregate_lcommunity_hash( aggregate, pi->attr->lcommunity); } if (match) - bgp_process(bgp, rn, afi, safi); + bgp_process(bgp, dest, afi, safi); } - bgp_unlock_node(top); + if (aggregate->as_set) { + bgp_compute_aggregate_aspath_val(aggregate); + bgp_compute_aggregate_community_val(aggregate); + bgp_compute_aggregate_ecommunity_val(aggregate); + bgp_compute_aggregate_lcommunity_val(aggregate); + } + + + bgp_dest_unlock_node(top); if (aggregate->incomplete_origin_count > 0) @@ -5831,6 +6446,9 @@ static void bgp_aggregate_route(struct bgp *bgp, struct prefix *p, else if (aggregate->egp_origin_count > 0) origin = BGP_ORIGIN_EGP; + if (aggregate->origin != BGP_ORIGIN_UNSPECIFIED) + origin = aggregate->origin; + if (aggregate->as_set) { if (aggregate->aspath) /* Retrieve aggregate route's as-path. @@ -5858,12 +6476,12 @@ static void bgp_aggregate_route(struct bgp *bgp, struct prefix *p, aggregate); } -static void bgp_aggregate_delete(struct bgp *bgp, struct prefix *p, afi_t afi, - safi_t safi, struct bgp_aggregate *aggregate) +void bgp_aggregate_delete(struct bgp *bgp, const struct prefix *p, afi_t afi, + safi_t safi, struct bgp_aggregate *aggregate) { struct bgp_table *table; - struct bgp_node *top; - struct bgp_node *rn; + struct bgp_dest *top; + struct bgp_dest *dest; struct bgp_path_info *pi; unsigned long match; @@ -5871,13 +6489,15 @@ static void bgp_aggregate_delete(struct bgp *bgp, struct prefix *p, afi_t afi, /* If routes exists below this node, generate aggregate routes. */ top = bgp_node_get(table, p); - for (rn = bgp_node_get(table, p); rn; - rn = bgp_route_next_until(rn, top)) { - if (rn->p.prefixlen <= p->prefixlen) + for (dest = bgp_node_get(table, p); dest; + dest = bgp_route_next_until(dest, top)) { + const struct prefix *dest_p = bgp_dest_get_prefix(dest); + + if (dest_p->prefixlen <= p->prefixlen) continue; match = 0; - for (pi = bgp_node_get_bgp_path_info(rn); pi; pi = pi->next) { + for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) { if (BGP_PATH_HOLDDOWN(pi)) continue; @@ -5889,7 +6509,8 @@ static void bgp_aggregate_delete(struct bgp *bgp, struct prefix *p, afi_t afi, if (pi->extra->suppress == 0) { bgp_path_info_set_flag( - rn, pi, BGP_PATH_ATTR_CHANGED); + dest, pi, + BGP_PATH_ATTR_CHANGED); match++; } } @@ -5903,42 +6524,53 @@ static void bgp_aggregate_delete(struct bgp *bgp, struct prefix *p, afi_t afi, if (aggregate->as_set) { /* Remove as-path from aggregate. */ - bgp_remove_aspath_from_aggregate( + bgp_remove_aspath_from_aggregate_hash( aggregate, pi->attr->aspath); if (pi->attr->community) /* Remove community from aggregate. */ - bgp_remove_community_from_aggregate( + bgp_remove_comm_from_aggregate_hash( aggregate, pi->attr->community); if (pi->attr->ecommunity) /* Remove ecommunity from aggregate. */ - bgp_remove_ecommunity_from_aggregate( + bgp_remove_ecomm_from_aggregate_hash( aggregate, pi->attr->ecommunity); if (pi->attr->lcommunity) /* Remove lcommunity from aggregate. */ - bgp_remove_lcommunity_from_aggregate( + bgp_remove_lcomm_from_aggregate_hash( aggregate, pi->attr->lcommunity); } - } /* If this node was suppressed, process the change. */ if (match) - bgp_process(bgp, rn, afi, safi); + bgp_process(bgp, dest, afi, safi); + } + if (aggregate->as_set) { + aspath_free(aggregate->aspath); + aggregate->aspath = NULL; + if (aggregate->community) + community_free(&aggregate->community); + if (aggregate->ecommunity) + ecommunity_free(&aggregate->ecommunity); + if (aggregate->lcommunity) + lcommunity_free(&aggregate->lcommunity); } - bgp_unlock_node(top); + + bgp_dest_unlock_node(top); } -static void bgp_add_route_to_aggregate(struct bgp *bgp, struct prefix *aggr_p, +static void bgp_add_route_to_aggregate(struct bgp *bgp, + const struct prefix *aggr_p, struct bgp_path_info *pinew, afi_t afi, safi_t safi, struct bgp_aggregate *aggregate) @@ -5984,6 +6616,9 @@ static void bgp_add_route_to_aggregate(struct bgp *bgp, struct prefix *aggr_p, else if (aggregate->egp_origin_count > 0) origin = BGP_ORIGIN_EGP; + if (aggregate->origin != BGP_ORIGIN_UNSPECIFIED) + origin = aggregate->origin; + if (aggregate->as_set) { /* Compute aggregate route's as-path. */ @@ -6041,7 +6676,7 @@ static void bgp_remove_route_from_aggregate(struct bgp *bgp, afi_t afi, safi_t safi, struct bgp_path_info *pi, struct bgp_aggregate *aggregate, - struct prefix *aggr_p) + const struct prefix *aggr_p) { uint8_t origin; struct aspath *aspath = NULL; @@ -6115,6 +6750,9 @@ static void bgp_remove_route_from_aggregate(struct bgp *bgp, afi_t afi, else if (aggregate->egp_origin_count > 0) origin = BGP_ORIGIN_EGP; + if (aggregate->origin != BGP_ORIGIN_UNSPECIFIED) + origin = aggregate->origin; + if (aggregate->as_set) { /* Retrieve aggregate route's as-path. */ @@ -6142,11 +6780,11 @@ static void bgp_remove_route_from_aggregate(struct bgp *bgp, afi_t afi, lcommunity, atomic_aggregate, aggregate); } -void bgp_aggregate_increment(struct bgp *bgp, struct prefix *p, +void bgp_aggregate_increment(struct bgp *bgp, const struct prefix *p, struct bgp_path_info *pi, afi_t afi, safi_t safi) { - struct bgp_node *child; - struct bgp_node *rn; + struct bgp_dest *child; + struct bgp_dest *dest; struct bgp_aggregate *aggregate; struct bgp_table *table; @@ -6165,21 +6803,23 @@ void bgp_aggregate_increment(struct bgp *bgp, struct prefix *p, child = bgp_node_get(table, p); /* Aggregate address configuration check. */ - for (rn = child; rn; rn = bgp_node_parent_nolock(rn)) { - aggregate = bgp_node_get_bgp_aggregate_info(rn); - if (aggregate != NULL && rn->p.prefixlen < p->prefixlen) { - bgp_add_route_to_aggregate(bgp, &rn->p, pi, afi, - safi, aggregate); + for (dest = child; dest; dest = bgp_dest_parent_nolock(dest)) { + const struct prefix *dest_p = bgp_dest_get_prefix(dest); + + aggregate = bgp_dest_get_bgp_aggregate_info(dest); + if (aggregate != NULL && dest_p->prefixlen < p->prefixlen) { + bgp_add_route_to_aggregate(bgp, dest_p, pi, afi, safi, + aggregate); } } - bgp_unlock_node(child); + bgp_dest_unlock_node(child); } -void bgp_aggregate_decrement(struct bgp *bgp, struct prefix *p, +void bgp_aggregate_decrement(struct bgp *bgp, const struct prefix *p, struct bgp_path_info *del, afi_t afi, safi_t safi) { - struct bgp_node *child; - struct bgp_node *rn; + struct bgp_dest *child; + struct bgp_dest *dest; struct bgp_aggregate *aggregate; struct bgp_table *table; @@ -6195,19 +6835,35 @@ void bgp_aggregate_decrement(struct bgp *bgp, struct prefix *p, child = bgp_node_get(table, p); /* Aggregate address configuration check. */ - for (rn = child; rn; rn = bgp_node_parent_nolock(rn)) { - aggregate = bgp_node_get_bgp_aggregate_info(rn); - if (aggregate != NULL && rn->p.prefixlen < p->prefixlen) { - bgp_remove_route_from_aggregate(bgp, afi, safi, - del, aggregate, &rn->p); + for (dest = child; dest; dest = bgp_dest_parent_nolock(dest)) { + const struct prefix *dest_p = bgp_dest_get_prefix(dest); + + aggregate = bgp_dest_get_bgp_aggregate_info(dest); + if (aggregate != NULL && dest_p->prefixlen < p->prefixlen) { + bgp_remove_route_from_aggregate(bgp, afi, safi, del, + aggregate, dest_p); } } - bgp_unlock_node(child); + bgp_dest_unlock_node(child); } /* Aggregate route attribute. */ #define AGGREGATE_SUMMARY_ONLY 1 #define AGGREGATE_AS_SET 1 +#define AGGREGATE_AS_UNSET 0 + +static const char *bgp_origin2str(uint8_t origin) +{ + switch (origin) { + case BGP_ORIGIN_IGP: + return "igp"; + case BGP_ORIGIN_EGP: + return "egp"; + case BGP_ORIGIN_INCOMPLETE: + return "incomplete"; + } + return "n/a"; +} static int bgp_aggregate_unset(struct vty *vty, const char *prefix_str, afi_t afi, safi_t safi) @@ -6215,7 +6871,7 @@ static int bgp_aggregate_unset(struct vty *vty, const char *prefix_str, VTY_DECLVAR_CONTEXT(bgp, bgp); int ret; struct prefix p; - struct bgp_node *rn; + struct bgp_dest *dest; struct bgp_aggregate *aggregate; /* Convert string to prefix structure. */ @@ -6227,20 +6883,20 @@ static int bgp_aggregate_unset(struct vty *vty, const char *prefix_str, apply_mask(&p); /* Old configuration check. */ - rn = bgp_node_lookup(bgp->aggregate[afi][safi], &p); - if (!rn) { + dest = bgp_node_lookup(bgp->aggregate[afi][safi], &p); + if (!dest) { vty_out(vty, "%% There is no aggregate-address configuration.\n"); return CMD_WARNING_CONFIG_FAILED; } - aggregate = bgp_node_get_bgp_aggregate_info(rn); + aggregate = bgp_dest_get_bgp_aggregate_info(dest); bgp_aggregate_delete(bgp, &p, afi, safi, aggregate); bgp_aggregate_install(bgp, afi, safi, &p, 0, NULL, NULL, NULL, NULL, 0, aggregate); /* Unlock aggregate address configuration. */ - bgp_node_set_bgp_aggregate_info(rn, NULL); + bgp_dest_set_bgp_aggregate_info(dest, NULL); if (aggregate->community) community_free(&aggregate->community); @@ -6295,20 +6951,23 @@ static int bgp_aggregate_unset(struct vty *vty, const char *prefix_str, } bgp_aggregate_free(aggregate); - bgp_unlock_node(rn); - bgp_unlock_node(rn); + bgp_dest_unlock_node(dest); + bgp_dest_unlock_node(dest); return CMD_SUCCESS; } static int bgp_aggregate_set(struct vty *vty, const char *prefix_str, afi_t afi, - safi_t safi, uint8_t summary_only, uint8_t as_set) + safi_t safi, const char *rmap, + uint8_t summary_only, uint8_t as_set, + uint8_t origin) { VTY_DECLVAR_CONTEXT(bgp, bgp); int ret; struct prefix p; - struct bgp_node *rn; + struct bgp_dest *dest; struct bgp_aggregate *aggregate; + uint8_t as_set_new = as_set; /* Convert string to prefix structure. */ ret = str2prefix(prefix_str, &p); @@ -6326,15 +6985,16 @@ static int bgp_aggregate_set(struct vty *vty, const char *prefix_str, afi_t afi, } /* Old configuration check. */ - rn = bgp_node_get(bgp->aggregate[afi][safi], &p); + dest = bgp_node_get(bgp->aggregate[afi][safi], &p); + aggregate = bgp_dest_get_bgp_aggregate_info(dest); - if (bgp_node_has_bgp_path_info_data(rn)) { + if (aggregate) { vty_out(vty, "There is already same aggregate network.\n"); /* try to remove the old entry */ ret = bgp_aggregate_unset(vty, prefix_str, afi, safi); if (ret) { vty_out(vty, "Error deleting aggregate.\n"); - bgp_unlock_node(rn); + bgp_dest_unlock_node(dest); return CMD_WARNING_CONFIG_FAILED; } } @@ -6342,9 +7002,44 @@ static int bgp_aggregate_set(struct vty *vty, const char *prefix_str, afi_t afi, /* Make aggregate address structure. */ aggregate = bgp_aggregate_new(); aggregate->summary_only = summary_only; - aggregate->as_set = as_set; + + /* Network operators MUST NOT locally generate any new + * announcements containing AS_SET or AS_CONFED_SET. If they have + * announced routes with AS_SET or AS_CONFED_SET in them, then they + * SHOULD withdraw those routes and re-announce routes for the + * aggregate or component prefixes (i.e., the more-specific routes + * subsumed by the previously aggregated route) without AS_SET + * or AS_CONFED_SET in the updates. + */ + if (bgp->reject_as_sets) { + if (as_set == AGGREGATE_AS_SET) { + as_set_new = AGGREGATE_AS_UNSET; + zlog_warn( + "%s: Ignoring as-set because `bgp reject-as-sets` is enabled.", + __func__); + vty_out(vty, + "Ignoring as-set because `bgp reject-as-sets` is enabled.\n"); + } + } + + aggregate->as_set = as_set_new; aggregate->safi = safi; - bgp_node_set_bgp_aggregate_info(rn, aggregate); + /* Override ORIGIN attribute if defined. + * E.g.: Cisco and Juniper set ORIGIN for aggregated address + * to IGP which is not what rfc4271 says. + * This enables the same behavior, optionally. + */ + aggregate->origin = origin; + + if (rmap) { + XFREE(MTYPE_ROUTE_MAP_NAME, aggregate->rmap.name); + route_map_counter_decrement(aggregate->rmap.map); + aggregate->rmap.name = + XSTRDUP(MTYPE_ROUTE_MAP_NAME, rmap); + aggregate->rmap.map = route_map_lookup_by_name(rmap); + route_map_counter_increment(aggregate->rmap.map); + } + bgp_dest_set_bgp_aggregate_info(dest, aggregate); /* Aggregate address insert into BGP routing table. */ bgp_aggregate_route(bgp, &p, afi, safi, aggregate); @@ -6354,50 +7049,86 @@ static int bgp_aggregate_set(struct vty *vty, const char *prefix_str, afi_t afi, DEFUN (aggregate_address, aggregate_address_cmd, - "aggregate-address A.B.C.D/M []", + "aggregate-address A.B.C.D/M [] [route-map WORD] [origin ]", "Configure BGP aggregate entries\n" "Aggregate prefix\n" "Generate AS set path information\n" "Filter more specific routes from updates\n" "Filter more specific routes from updates\n" - "Generate AS set path information\n") + "Generate AS set path information\n" + "Apply route map to aggregate network\n" + "Name of route map\n" + "BGP origin code\n" + "Remote EGP\n" + "Local IGP\n" + "Unknown heritage\n") { int idx = 0; argv_find(argv, argc, "A.B.C.D/M", &idx); char *prefix = argv[idx]->arg; - int as_set = - argv_find(argv, argc, "as-set", &idx) ? AGGREGATE_AS_SET : 0; + char *rmap = NULL; + uint8_t origin = BGP_ORIGIN_UNSPECIFIED; + int as_set = argv_find(argv, argc, "as-set", &idx) ? AGGREGATE_AS_SET + : AGGREGATE_AS_UNSET; idx = 0; int summary_only = argv_find(argv, argc, "summary-only", &idx) ? AGGREGATE_SUMMARY_ONLY : 0; - return bgp_aggregate_set(vty, prefix, AFI_IP, bgp_node_safi(vty), - summary_only, as_set); + idx = 0; + argv_find(argv, argc, "WORD", &idx); + if (idx) + rmap = argv[idx]->arg; + + idx = 0; + if (argv_find(argv, argc, "origin", &idx)) { + if (strncmp(argv[idx + 1]->arg, "igp", 2) == 0) + origin = BGP_ORIGIN_IGP; + if (strncmp(argv[idx + 1]->arg, "egp", 1) == 0) + origin = BGP_ORIGIN_EGP; + if (strncmp(argv[idx + 1]->arg, "incomplete", 2) == 0) + origin = BGP_ORIGIN_INCOMPLETE; + } + + return bgp_aggregate_set(vty, prefix, AFI_IP, bgp_node_safi(vty), rmap, + summary_only, as_set, origin); } DEFUN (aggregate_address_mask, aggregate_address_mask_cmd, - "aggregate-address A.B.C.D A.B.C.D []", + "aggregate-address A.B.C.D A.B.C.D [] [route-map WORD] [origin ]", "Configure BGP aggregate entries\n" "Aggregate address\n" "Aggregate mask\n" "Generate AS set path information\n" "Filter more specific routes from updates\n" "Filter more specific routes from updates\n" - "Generate AS set path information\n") + "Generate AS set path information\n" + "Apply route map to aggregate network\n" + "Name of route map\n" + "BGP origin code\n" + "Remote EGP\n" + "Local IGP\n" + "Unknown heritage\n") { int idx = 0; argv_find(argv, argc, "A.B.C.D", &idx); char *prefix = argv[idx]->arg; char *mask = argv[idx + 1]->arg; - int as_set = - argv_find(argv, argc, "as-set", &idx) ? AGGREGATE_AS_SET : 0; + bool rmap_found; + char *rmap = NULL; + uint8_t origin = BGP_ORIGIN_UNSPECIFIED; + int as_set = argv_find(argv, argc, "as-set", &idx) ? AGGREGATE_AS_SET + : AGGREGATE_AS_UNSET; idx = 0; int summary_only = argv_find(argv, argc, "summary-only", &idx) ? AGGREGATE_SUMMARY_ONLY : 0; + rmap_found = argv_find(argv, argc, "WORD", &idx); + if (rmap_found) + rmap = argv[idx]->arg; + char prefix_str[BUFSIZ]; int ret = netmask_str2prefix_str(prefix, mask, prefix_str); @@ -6406,20 +7137,36 @@ DEFUN (aggregate_address_mask, return CMD_WARNING_CONFIG_FAILED; } + idx = 0; + if (argv_find(argv, argc, "origin", &idx)) { + if (strncmp(argv[idx + 1]->arg, "igp", 2) == 0) + origin = BGP_ORIGIN_IGP; + if (strncmp(argv[idx + 1]->arg, "egp", 1) == 0) + origin = BGP_ORIGIN_EGP; + if (strncmp(argv[idx + 1]->arg, "incomplete", 2) == 0) + origin = BGP_ORIGIN_INCOMPLETE; + } + return bgp_aggregate_set(vty, prefix_str, AFI_IP, bgp_node_safi(vty), - summary_only, as_set); + rmap, summary_only, as_set, origin); } DEFUN (no_aggregate_address, no_aggregate_address_cmd, - "no aggregate-address A.B.C.D/M []", + "no aggregate-address A.B.C.D/M [] [route-map WORD] [origin ]", NO_STR "Configure BGP aggregate entries\n" "Aggregate prefix\n" "Generate AS set path information\n" "Filter more specific routes from updates\n" "Filter more specific routes from updates\n" - "Generate AS set path information\n") + "Generate AS set path information\n" + "Apply route map to aggregate network\n" + "Name of route map\n" + "BGP origin code\n" + "Remote EGP\n" + "Local IGP\n" + "Unknown heritage\n") { int idx = 0; argv_find(argv, argc, "A.B.C.D/M", &idx); @@ -6429,7 +7176,7 @@ DEFUN (no_aggregate_address, DEFUN (no_aggregate_address_mask, no_aggregate_address_mask_cmd, - "no aggregate-address A.B.C.D A.B.C.D []", + "no aggregate-address A.B.C.D A.B.C.D [] [route-map WORD] [origin ]", NO_STR "Configure BGP aggregate entries\n" "Aggregate address\n" @@ -6437,7 +7184,13 @@ DEFUN (no_aggregate_address_mask, "Generate AS set path information\n" "Filter more specific routes from updates\n" "Filter more specific routes from updates\n" - "Generate AS set path information\n") + "Generate AS set path information\n" + "Apply route map to aggregate network\n" + "Name of route map\n" + "BGP origin code\n" + "Remote EGP\n" + "Local IGP\n" + "Unknown heritage\n") { int idx = 0; argv_find(argv, argc, "A.B.C.D", &idx); @@ -6457,28 +7210,68 @@ DEFUN (no_aggregate_address_mask, DEFUN (ipv6_aggregate_address, ipv6_aggregate_address_cmd, - "aggregate-address X:X::X:X/M [summary-only]", + "aggregate-address X:X::X:X/M [] [route-map WORD] [origin ]", "Configure BGP aggregate entries\n" "Aggregate prefix\n" - "Filter more specific routes from updates\n") + "Generate AS set path information\n" + "Filter more specific routes from updates\n" + "Filter more specific routes from updates\n" + "Generate AS set path information\n" + "Apply route map to aggregate network\n" + "Name of route map\n" + "BGP origin code\n" + "Remote EGP\n" + "Local IGP\n" + "Unknown heritage\n") { int idx = 0; argv_find(argv, argc, "X:X::X:X/M", &idx); char *prefix = argv[idx]->arg; + char *rmap = NULL; + bool rmap_found; + uint8_t origin = BGP_ORIGIN_UNSPECIFIED; + int as_set = argv_find(argv, argc, "as-set", &idx) ? AGGREGATE_AS_SET + : AGGREGATE_AS_UNSET; + + idx = 0; int sum_only = argv_find(argv, argc, "summary-only", &idx) ? AGGREGATE_SUMMARY_ONLY : 0; - return bgp_aggregate_set(vty, prefix, AFI_IP6, SAFI_UNICAST, sum_only, - 0); + + rmap_found = argv_find(argv, argc, "WORD", &idx); + if (rmap_found) + rmap = argv[idx]->arg; + + idx = 0; + if (argv_find(argv, argc, "origin", &idx)) { + if (strncmp(argv[idx + 1]->arg, "igp", 2) == 0) + origin = BGP_ORIGIN_IGP; + if (strncmp(argv[idx + 1]->arg, "egp", 1) == 0) + origin = BGP_ORIGIN_EGP; + if (strncmp(argv[idx + 1]->arg, "incomplete", 2) == 0) + origin = BGP_ORIGIN_INCOMPLETE; + } + + return bgp_aggregate_set(vty, prefix, AFI_IP6, SAFI_UNICAST, rmap, + sum_only, as_set, origin); } DEFUN (no_ipv6_aggregate_address, no_ipv6_aggregate_address_cmd, - "no aggregate-address X:X::X:X/M [summary-only]", + "no aggregate-address X:X::X:X/M [] [route-map WORD] [origin ]", NO_STR "Configure BGP aggregate entries\n" "Aggregate prefix\n" - "Filter more specific routes from updates\n") + "Generate AS set path information\n" + "Filter more specific routes from updates\n" + "Filter more specific routes from updates\n" + "Generate AS set path information\n" + "Apply route map to aggregate network\n" + "Name of route map\n" + "BGP origin code\n" + "Remote EGP\n" + "Local IGP\n" + "Unknown heritage\n") { int idx = 0; argv_find(argv, argc, "X:X::X:X/M", &idx); @@ -6496,15 +7289,19 @@ void bgp_redistribute_add(struct bgp *bgp, struct prefix *p, struct bgp_path_info *new; struct bgp_path_info *bpi; struct bgp_path_info rmap_path; - struct bgp_node *bn; + struct bgp_dest *bn; struct attr attr; struct attr *new_attr; afi_t afi; - int ret; + route_map_result_t ret; struct bgp_redist *red; /* Make default attribute. */ bgp_attr_default_set(&attr, BGP_ORIGIN_INCOMPLETE); + /* + * This must not be NULL to satisfy Coverity SA + */ + assert(attr.aspath); switch (nhtype) { case NEXTHOP_TYPE_IFINDEX: @@ -6544,7 +7341,7 @@ void bgp_redistribute_add(struct bgp *bgp, struct prefix *p, struct attr attr_new; /* Copy attribute for modification. */ - bgp_attr_dup(&attr_new, &attr); + attr_new = attr; if (red->redist_metric_flag) attr_new.med = red->redist_metric; @@ -6574,7 +7371,7 @@ void bgp_redistribute_add(struct bgp *bgp, struct prefix *p, } } - if (bgp_flag_check(bgp, BGP_FLAG_GRACEFUL_SHUTDOWN)) + if (CHECK_FLAG(bgp->flags, BGP_FLAG_GRACEFUL_SHUTDOWN)) bgp_attr_add_gshut_community(&attr_new); bn = bgp_afi_node_get(bgp->rib[afi][SAFI_UNICAST], afi, @@ -6582,8 +7379,7 @@ void bgp_redistribute_add(struct bgp *bgp, struct prefix *p, new_attr = bgp_attr_intern(&attr_new); - for (bpi = bgp_node_get_bgp_path_info(bn); bpi; - bpi = bpi->next) + for (bpi = bgp_dest_get_bgp_path_info(bn); bpi; bpi = bpi->next) if (bpi->peer == bgp->peer_self && bpi->sub_type == BGP_ROUTE_REDISTRIBUTE) break; @@ -6595,7 +7391,7 @@ void bgp_redistribute_add(struct bgp *bgp, struct prefix *p, && !CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) { bgp_attr_unintern(&new_attr); aspath_unintern(&attr.aspath); - bgp_unlock_node(bn); + bgp_dest_unlock_node(bn); return; } else { /* The attribute is changed. */ @@ -6616,7 +7412,7 @@ void bgp_redistribute_add(struct bgp *bgp, struct prefix *p, bgp_aggregate_increment(bgp, p, bpi, afi, SAFI_UNICAST); bgp_process(bgp, bn, afi, SAFI_UNICAST); - bgp_unlock_node(bn); + bgp_dest_unlock_node(bn); aspath_unintern(&attr.aspath); if ((bgp->inst_type == BGP_INSTANCE_TYPE_VRF) @@ -6636,7 +7432,7 @@ void bgp_redistribute_add(struct bgp *bgp, struct prefix *p, bgp_aggregate_increment(bgp, p, new, afi, SAFI_UNICAST); bgp_path_info_add(bn, new); - bgp_unlock_node(bn); + bgp_dest_unlock_node(bn); bgp_process(bgp, bn, afi, SAFI_UNICAST); if ((bgp->inst_type == BGP_INSTANCE_TYPE_VRF) @@ -6654,7 +7450,7 @@ void bgp_redistribute_delete(struct bgp *bgp, struct prefix *p, uint8_t type, unsigned short instance) { afi_t afi; - struct bgp_node *rn; + struct bgp_dest *dest; struct bgp_path_info *pi; struct bgp_redist *red; @@ -6662,10 +7458,10 @@ void bgp_redistribute_delete(struct bgp *bgp, struct prefix *p, uint8_t type, red = bgp_redist_lookup(bgp, afi, type, instance); if (red) { - rn = bgp_afi_node_get(bgp->rib[afi][SAFI_UNICAST], afi, - SAFI_UNICAST, p, NULL); + dest = bgp_afi_node_get(bgp->rib[afi][SAFI_UNICAST], afi, + SAFI_UNICAST, p, NULL); - for (pi = bgp_node_get_bgp_path_info(rn); pi; pi = pi->next) + for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) if (pi->peer == bgp->peer_self && pi->type == type) break; @@ -6677,10 +7473,10 @@ void bgp_redistribute_delete(struct bgp *bgp, struct prefix *p, uint8_t type, bgp, pi); } bgp_aggregate_decrement(bgp, p, pi, afi, SAFI_UNICAST); - bgp_path_info_delete(rn, pi); - bgp_process(bgp, rn, afi, SAFI_UNICAST); + bgp_path_info_delete(dest, pi); + bgp_process(bgp, dest, afi, SAFI_UNICAST); } - bgp_unlock_node(rn); + bgp_dest_unlock_node(dest); } } @@ -6688,14 +7484,14 @@ void bgp_redistribute_delete(struct bgp *bgp, struct prefix *p, uint8_t type, void bgp_redistribute_withdraw(struct bgp *bgp, afi_t afi, int type, unsigned short instance) { - struct bgp_node *rn; + struct bgp_dest *dest; struct bgp_path_info *pi; struct bgp_table *table; table = bgp->rib[afi][SAFI_UNICAST]; - for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) { - for (pi = bgp_node_get_bgp_path_info(rn); pi; pi = pi->next) + for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) { + for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) if (pi->peer == bgp->peer_self && pi->type == type && pi->instance == instance) break; @@ -6707,17 +7503,17 @@ void bgp_redistribute_withdraw(struct bgp *bgp, afi_t afi, int type, vpn_leak_from_vrf_withdraw(bgp_get_default(), bgp, pi); } - bgp_aggregate_decrement(bgp, &rn->p, pi, afi, - SAFI_UNICAST); - bgp_path_info_delete(rn, pi); - bgp_process(bgp, rn, afi, SAFI_UNICAST); + bgp_aggregate_decrement(bgp, bgp_dest_get_prefix(dest), + pi, afi, SAFI_UNICAST); + bgp_path_info_delete(dest, pi); + bgp_process(bgp, dest, afi, SAFI_UNICAST); } } } /* Static function to display route. */ -static void route_vty_out_route(struct prefix *p, struct vty *vty, - json_object *json) +static void route_vty_out_route(const struct prefix *p, struct vty *vty, + json_object *json, bool wide) { int len = 0; char buf[BUFSIZ]; @@ -6772,7 +7568,7 @@ static void route_vty_out_route(struct prefix *p, struct vty *vty, } if (!json) { - len = 17 - len; + len = wide ? (45 - len) : (17 - len); if (len < 1) vty_out(vty, "\n%*s", 20, " "); else @@ -6863,22 +7659,36 @@ static void route_vty_short_status_out(struct vty *vty, vty_out(vty, " "); } -/* called from terminal list command */ -void route_vty_out(struct vty *vty, struct prefix *p, +static char *bgp_nexthop_hostname(struct peer *peer, + struct bgp_nexthop_cache *bnc) +{ + if (peer->hostname + && CHECK_FLAG(peer->bgp->flags, BGP_FLAG_SHOW_NEXTHOP_HOSTNAME)) + return peer->hostname; + return NULL; +} + +/* called from terminal list command */ +void route_vty_out(struct vty *vty, const struct prefix *p, struct bgp_path_info *path, int display, safi_t safi, - json_object *json_paths) + json_object *json_paths, bool wide) { - struct attr *attr; + int len; + struct attr *attr = path->attr; json_object *json_path = NULL; json_object *json_nexthops = NULL; json_object *json_nexthop_global = NULL; json_object *json_nexthop_ll = NULL; + json_object *json_ext_community = NULL; char vrf_id_str[VRF_NAMSIZ] = {0}; bool nexthop_self = CHECK_FLAG(path->flags, BGP_PATH_ANNC_NH_SELF) ? true : false; bool nexthop_othervrf = false; vrf_id_t nexthop_vrfid = VRF_DEFAULT; const char *nexthop_vrfname = VRF_DEFAULT_NAME; + char *nexthop_hostname = + bgp_nexthop_hostname(path->peer, path->nexthop); + char esi_buf[ESI_STR_LEN]; if (json_paths) json_path = json_object_new_object(); @@ -6889,22 +7699,11 @@ void route_vty_out(struct vty *vty, struct prefix *p, if (!json_paths) { /* print prefix and mask */ if (!display) - route_vty_out_route(p, vty, json_path); + route_vty_out_route(p, vty, json_path, wide); else - vty_out(vty, "%*s", 17, " "); + vty_out(vty, "%*s", (wide ? 45 : 17), " "); } else { - route_vty_out_route(p, vty, json_path); - } - - /* Print attribute */ - attr = path->attr; - if (!attr) { - if (json_paths) - json_object_array_add(json_paths, json_path); - else - vty_out(vty, "\n"); - - return; + route_vty_out_route(p, vty, json_path, wide); } /* @@ -6957,90 +7756,150 @@ void route_vty_out(struct vty *vty, struct prefix *p, switch (af) { case AF_INET: - sprintf(nexthop, "%s", - inet_ntop(af, &attr->mp_nexthop_global_in, buf, - BUFSIZ)); + snprintf(nexthop, sizeof(nexthop), "%s", + inet_ntop(af, &attr->mp_nexthop_global_in, buf, + BUFSIZ)); break; case AF_INET6: - sprintf(nexthop, "%s", - inet_ntop(af, &attr->mp_nexthop_global, buf, - BUFSIZ)); + snprintf(nexthop, sizeof(nexthop), "%s", + inet_ntop(af, &attr->mp_nexthop_global, buf, + BUFSIZ)); break; default: - sprintf(nexthop, "?"); + snprintf(nexthop, sizeof(nexthop), "?"); break; } if (json_paths) { json_nexthop_global = json_object_new_object(); - json_object_string_add(json_nexthop_global, "afi", - (af == AF_INET) ? "ip" : "ipv6"); - json_object_string_add(json_nexthop_global, - (af == AF_INET) ? "ip" : "ipv6", + json_object_string_add(json_nexthop_global, "ip", nexthop); + + if (path->peer->hostname) + json_object_string_add(json_nexthop_global, + "hostname", + path->peer->hostname); + + json_object_string_add(json_nexthop_global, "afi", + (af == AF_INET) ? "ipv4" + : "ipv6"); json_object_boolean_true_add(json_nexthop_global, "used"); - } else - vty_out(vty, "%s%s", nexthop, vrf_id_str); + } else { + if (nexthop_hostname) + len = vty_out(vty, "%s(%s)%s", nexthop, + nexthop_hostname, vrf_id_str); + else + len = vty_out(vty, "%s%s", nexthop, vrf_id_str); + + len = wide ? (41 - len) : (16 - len); + if (len < 1) + vty_out(vty, "\n%*s", 36, " "); + else + vty_out(vty, "%*s", len, " "); + } } else if (safi == SAFI_EVPN) { if (json_paths) { json_nexthop_global = json_object_new_object(); json_object_string_add(json_nexthop_global, "ip", inet_ntoa(attr->nexthop)); + + if (path->peer->hostname) + json_object_string_add(json_nexthop_global, + "hostname", + path->peer->hostname); + json_object_string_add(json_nexthop_global, "afi", "ipv4"); json_object_boolean_true_add(json_nexthop_global, "used"); - } else - vty_out(vty, "%-16s%s", inet_ntoa(attr->nexthop), - vrf_id_str); + } else { + if (nexthop_hostname) + len = vty_out(vty, "%pI4(%s)%s", &attr->nexthop, + nexthop_hostname, vrf_id_str); + else + len = vty_out(vty, "%pI4%s", &attr->nexthop, + vrf_id_str); + + len = wide ? (41 - len) : (16 - len); + if (len < 1) + vty_out(vty, "\n%*s", 36, " "); + else + vty_out(vty, "%*s", len, " "); + } } else if (safi == SAFI_FLOWSPEC) { - if (attr->nexthop.s_addr != 0) { + if (attr->nexthop.s_addr != INADDR_ANY) { if (json_paths) { json_nexthop_global = json_object_new_object(); - json_object_string_add( - json_nexthop_global, "ip", - inet_ntoa(attr->nexthop)); + json_object_string_add(json_nexthop_global, "afi", "ipv4"); + json_object_string_add( + json_nexthop_global, "ip", + inet_ntoa(attr->nexthop)); + + if (path->peer->hostname) + json_object_string_add( + json_nexthop_global, "hostname", + path->peer->hostname); + json_object_boolean_true_add( json_nexthop_global, "used"); } else { - vty_out(vty, "%-16s", inet_ntoa(attr->nexthop)); + if (nexthop_hostname) + len = vty_out(vty, "%pI4(%s)%s", + &attr->nexthop, + nexthop_hostname, + vrf_id_str); + else + len = vty_out(vty, "%pI4%s", + &attr->nexthop, + vrf_id_str); + + len = wide ? (41 - len) : (16 - len); + if (len < 1) + vty_out(vty, "\n%*s", 36, " "); + else + vty_out(vty, "%*s", len, " "); } } } else if (p->family == AF_INET && !BGP_ATTR_NEXTHOP_AFI_IP6(attr)) { if (json_paths) { json_nexthop_global = json_object_new_object(); - if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_EVPN)) - json_object_string_add( - json_nexthop_global, "ip", - inet_ntoa(attr->mp_nexthop_global_in)); - else - json_object_string_add( - json_nexthop_global, "ip", - inet_ntoa(attr->nexthop)); + json_object_string_add(json_nexthop_global, "ip", + inet_ntoa(attr->nexthop)); + + if (path->peer->hostname) + json_object_string_add(json_nexthop_global, + "hostname", + path->peer->hostname); json_object_string_add(json_nexthop_global, "afi", "ipv4"); json_object_boolean_true_add(json_nexthop_global, "used"); } else { - char buf[BUFSIZ]; + if (nexthop_hostname) + len = vty_out(vty, "%pI4(%s)%s", &attr->nexthop, + nexthop_hostname, vrf_id_str); + else + len = vty_out(vty, "%pI4%s", &attr->nexthop, + vrf_id_str); - snprintf(buf, sizeof(buf), "%s%s", - inet_ntoa(attr->nexthop), vrf_id_str); - vty_out(vty, "%-16s", buf); + len = wide ? (41 - len) : (16 - len); + if (len < 1) + vty_out(vty, "\n%*s", 36, " "); + else + vty_out(vty, "%*s", len, " "); } } /* IPv6 Next Hop */ else if (p->family == AF_INET6 || BGP_ATTR_NEXTHOP_AFI_IP6(attr)) { - int len; char buf[BUFSIZ]; if (json_paths) { @@ -7049,6 +7908,12 @@ void route_vty_out(struct vty *vty, struct prefix *p, json_nexthop_global, "ip", inet_ntop(AF_INET6, &attr->mp_nexthop_global, buf, BUFSIZ)); + + if (path->peer->hostname) + json_object_string_add(json_nexthop_global, + "hostname", + path->peer->hostname); + json_object_string_add(json_nexthop_global, "afi", "ipv6"); json_object_string_add(json_nexthop_global, "scope", @@ -7056,7 +7921,8 @@ void route_vty_out(struct vty *vty, struct prefix *p, /* We display both LL & GL if both have been * received */ - if ((attr->mp_nexthop_len == 32) + if ((attr->mp_nexthop_len + == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL) || (path->peer->conf_if)) { json_nexthop_ll = json_object_new_object(); json_object_string_add( @@ -7064,6 +7930,12 @@ void route_vty_out(struct vty *vty, struct prefix *p, inet_ntop(AF_INET6, &attr->mp_nexthop_local, buf, BUFSIZ)); + + if (path->peer->hostname) + json_object_string_add( + json_nexthop_ll, "hostname", + path->peer->hostname); + json_object_string_add(json_nexthop_ll, "afi", "ipv6"); json_object_string_add(json_nexthop_ll, "scope", @@ -7084,30 +7956,35 @@ void route_vty_out(struct vty *vty, struct prefix *p, } else { /* Display LL if LL/Global both in table unless * prefer-global is set */ - if (((attr->mp_nexthop_len == 32) + if (((attr->mp_nexthop_len + == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL) && !attr->mp_nexthop_prefer_global) || (path->peer->conf_if)) { if (path->peer->conf_if) { len = vty_out(vty, "%s", path->peer->conf_if); - len = 16 - len; /* len of IPv6 - addr + max - len of def - ifname */ + /* len of IPv6 addr + max len of def + * ifname */ + len = wide ? (41 - len) : (16 - len); if (len < 1) vty_out(vty, "\n%*s", 36, " "); else vty_out(vty, "%*s", len, " "); } else { - len = vty_out( - vty, "%s%s", - inet_ntop( - AF_INET6, + if (nexthop_hostname) + len = vty_out( + vty, "%pI6(%s)%s", + &attr->mp_nexthop_local, + nexthop_hostname, + vrf_id_str); + else + len = vty_out( + vty, "%pI6%s", &attr->mp_nexthop_local, - buf, BUFSIZ), - vrf_id_str); - len = 16 - len; + vrf_id_str); + + len = wide ? (41 - len) : (16 - len); if (len < 1) vty_out(vty, "\n%*s", 36, " "); @@ -7115,13 +7992,17 @@ void route_vty_out(struct vty *vty, struct prefix *p, vty_out(vty, "%*s", len, " "); } } else { - len = vty_out( - vty, "%s%s", - inet_ntop(AF_INET6, - &attr->mp_nexthop_global, buf, - BUFSIZ), - vrf_id_str); - len = 16 - len; + if (nexthop_hostname) + len = vty_out(vty, "%pI6(%s)%s", + &attr->mp_nexthop_global, + nexthop_hostname, + vrf_id_str); + else + len = vty_out(vty, "%pI6%s", + &attr->mp_nexthop_global, + vrf_id_str); + + len = wide ? (41 - len) : (16 - len); if (len < 1) vty_out(vty, "\n%*s", 36, " "); @@ -7133,32 +8014,25 @@ void route_vty_out(struct vty *vty, struct prefix *p, /* MED/Metric */ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC)) - if (json_paths) { - - /* - * Adding "metric" field to match with corresponding - * CLI. "med" will be deprecated in future. - */ - json_object_int_add(json_path, "med", attr->med); + if (json_paths) json_object_int_add(json_path, "metric", attr->med); - } else + else if (wide) + vty_out(vty, "%7u", attr->med); + else vty_out(vty, "%10u", attr->med); - else if (!json_paths) - vty_out(vty, " "); + else if (!json_paths) { + if (wide) + vty_out(vty, "%*s", 7, " "); + else + vty_out(vty, "%*s", 10, " "); + } /* Local Pref */ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF)) - if (json_paths) { - - /* - * Adding "locPrf" field to match with corresponding - * CLI. "localPref" will be deprecated in future. - */ - json_object_int_add(json_path, "localpref", - attr->local_pref); + if (json_paths) json_object_int_add(json_path, "locPrf", - attr->local_pref); - } else + attr->local_pref); + else vty_out(vty, "%7u", attr->local_pref); else if (!json_paths) vty_out(vty, " "); @@ -7177,17 +8051,10 @@ void route_vty_out(struct vty *vty, struct prefix *p, /* Print aspath */ if (attr->aspath) { - if (json_paths) { - - /* - * Adding "path" field to match with corresponding - * CLI. "aspath" will be deprecated in future. - */ - json_object_string_add(json_path, "aspath", - attr->aspath->str); + if (json_paths) json_object_string_add(json_path, "path", - attr->aspath->str); - } else + attr->aspath->str); + else aspath_print_vty(vty, "%s", attr->aspath, " "); } @@ -7199,6 +8066,22 @@ void route_vty_out(struct vty *vty, struct prefix *p, vty_out(vty, "%s", bgp_origin_str[attr->origin]); if (json_paths) { + if (bgp_evpn_is_esi_valid(&attr->esi)) { + json_object_string_add(json_path, "esi", + esi_to_str(&attr->esi, + esi_buf, sizeof(esi_buf))); + } + if (safi == SAFI_EVPN && + attr->flag & ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES)) { + json_ext_community = json_object_new_object(); + json_object_string_add(json_ext_community, + "string", + attr->ecommunity->str); + json_object_object_add(json_path, + "extendedCommunity", + json_ext_community); + } + if (nexthop_self) json_object_boolean_true_add(json_path, "announceNexthopSelf"); @@ -7232,7 +8115,22 @@ void route_vty_out(struct vty *vty, struct prefix *p, json_object_array_add(json_paths, json_path); } else { vty_out(vty, "\n"); -#if ENABLE_BGP_VNC + + if (safi == SAFI_EVPN) { + if (bgp_evpn_is_esi_valid(&attr->esi)) { + vty_out(vty, "%*s", 20, " "); + vty_out(vty, "ESI:%s\n", + esi_to_str(&attr->esi, + esi_buf, sizeof(esi_buf))); + } + if (attr->flag & + ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES)) { + vty_out(vty, "%*s", 20, " "); + vty_out(vty, "%s\n", attr->ecommunity->str); + } + } + +#ifdef ENABLE_BGP_VNC /* prints an additional line, indented, with VNC info, if * present */ if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP)) @@ -7242,13 +8140,15 @@ void route_vty_out(struct vty *vty, struct prefix *p, } /* called from terminal list command */ -void route_vty_out_tmp(struct vty *vty, struct prefix *p, struct attr *attr, - safi_t safi, bool use_json, json_object *json_ar) +void route_vty_out_tmp(struct vty *vty, const struct prefix *p, + struct attr *attr, safi_t safi, bool use_json, + json_object *json_ar, bool wide) { json_object *json_status = NULL; json_object *json_net = NULL; + int len; char buff[BUFSIZ]; - char buf2[BUFSIZ]; + /* Route status display. */ if (use_json) { json_status = json_object_new_object(); @@ -7261,24 +8161,28 @@ void route_vty_out_tmp(struct vty *vty, struct prefix *p, struct attr *attr, /* print prefix and mask */ if (use_json) { - json_object_string_add( - json_net, "addrPrefix", - inet_ntop(p->family, &p->u.prefix, buff, BUFSIZ)); - json_object_int_add(json_net, "prefixLen", p->prefixlen); - prefix2str(p, buf2, PREFIX_STRLEN); - json_object_string_add(json_net, "network", buf2); + if (safi == SAFI_EVPN) + bgp_evpn_route2json((struct prefix_evpn *)p, json_net); + else if (p->family == AF_INET || p->family == AF_INET6) { + json_object_string_add( + json_net, "addrPrefix", + inet_ntop(p->family, &p->u.prefix, buff, + BUFSIZ)); + json_object_int_add(json_net, "prefixLen", + p->prefixlen); + prefix2str(p, buff, PREFIX_STRLEN); + json_object_string_add(json_net, "network", buff); + } } else - route_vty_out_route(p, vty, NULL); + route_vty_out_route(p, vty, NULL, wide); /* Print attribute */ if (attr) { if (use_json) { if (p->family == AF_INET && (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP - || safi == SAFI_EVPN || !BGP_ATTR_NEXTHOP_AFI_IP6(attr))) { - if (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP - || safi == SAFI_EVPN) + if (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP) json_object_string_add( json_net, "nextHop", inet_ntoa( @@ -7296,41 +8200,27 @@ void route_vty_out_tmp(struct vty *vty, struct prefix *p, struct attr *attr, inet_ntop(AF_INET6, &attr->mp_nexthop_global, buf, BUFSIZ)); - } + } else if (p->family == AF_EVPN && + !BGP_ATTR_NEXTHOP_AFI_IP6(attr)) + json_object_string_add(json_net, + "nextHop", inet_ntoa( + attr->mp_nexthop_global_in)); if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC)) json_object_int_add(json_net, "metric", attr->med); - if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF)) { - - /* - * Adding "locPrf" field to match with - * corresponding CLI. "localPref" will be - * deprecated in future. - */ - json_object_int_add(json_net, "localPref", - attr->local_pref); + if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF)) json_object_int_add(json_net, "locPrf", - attr->local_pref); - } + attr->local_pref); json_object_int_add(json_net, "weight", attr->weight); /* Print aspath */ - if (attr->aspath) { - - /* - * Adding "path" field to match with - * corresponding CLI. "localPref" will be - * deprecated in future. - */ - json_object_string_add(json_net, "asPath", - attr->aspath->str); + if (attr->aspath) json_object_string_add(json_net, "path", - attr->aspath->str); - } + attr->aspath->str); /* Print origin */ json_object_string_add(json_net, "bgpOriginCode", @@ -7345,12 +8235,14 @@ void route_vty_out_tmp(struct vty *vty, struct prefix *p, struct attr *attr, vty_out(vty, "%-16s", inet_ntoa( attr->mp_nexthop_global_in)); + else if (wide) + vty_out(vty, "%-41s", + inet_ntoa(attr->nexthop)); else vty_out(vty, "%-16s", inet_ntoa(attr->nexthop)); } else if (p->family == AF_INET6 || BGP_ATTR_NEXTHOP_AFI_IP6(attr)) { - int len; char buf[BUFSIZ]; len = vty_out( @@ -7358,7 +8250,7 @@ void route_vty_out_tmp(struct vty *vty, struct prefix *p, struct attr *attr, inet_ntop(AF_INET6, &attr->mp_nexthop_global, buf, BUFSIZ)); - len = 16 - len; + len = wide ? (41 - len) : (16 - len); if (len < 1) vty_out(vty, "\n%*s", 36, " "); else @@ -7366,7 +8258,12 @@ void route_vty_out_tmp(struct vty *vty, struct prefix *p, struct attr *attr, } if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC)) - vty_out(vty, "%10u", attr->med); + if (wide) + vty_out(vty, "%7u", attr->med); + else + vty_out(vty, "%10u", attr->med); + else if (wide) + vty_out(vty, " "); else vty_out(vty, " "); @@ -7390,16 +8287,14 @@ void route_vty_out_tmp(struct vty *vty, struct prefix *p, struct attr *attr, json_object_boolean_true_add(json_status, ">"); json_object_object_add(json_net, "appliedStatusSymbols", json_status); - char buf_cut[BUFSIZ]; - json_object_object_add( - json_ar, - inet_ntop(p->family, &p->u.prefix, buf_cut, BUFSIZ), - json_net); + + prefix2str(p, buff, PREFIX_STRLEN); + json_object_object_add(json_ar, buff, json_net); } else vty_out(vty, "\n"); } -void route_vty_out_tag(struct vty *vty, struct prefix *p, +void route_vty_out_tag(struct vty *vty, const struct prefix *p, struct bgp_path_info *path, int display, safi_t safi, json_object *json) { @@ -7419,85 +8314,63 @@ void route_vty_out_tag(struct vty *vty, struct prefix *p, /* print prefix and mask */ if (json == NULL) { if (!display) - route_vty_out_route(p, vty, NULL); + route_vty_out_route(p, vty, NULL, false); else vty_out(vty, "%*s", 17, " "); } /* Print attribute */ attr = path->attr; - if (attr) { - if (((p->family == AF_INET) - && ((safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP))) - || (safi == SAFI_EVPN && !BGP_ATTR_NEXTHOP_AFI_IP6(attr)) - || (!BGP_ATTR_NEXTHOP_AFI_IP6(attr))) { - if (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP - || safi == SAFI_EVPN) { - if (json) - json_object_string_add( - json_out, "mpNexthopGlobalIn", - inet_ntoa( - attr->mp_nexthop_global_in)); - else - vty_out(vty, "%-16s", - inet_ntoa( - attr->mp_nexthop_global_in)); - } else { - if (json) - json_object_string_add( - json_out, "nexthop", - inet_ntoa(attr->nexthop)); - else - vty_out(vty, "%-16s", - inet_ntoa(attr->nexthop)); - } - } else if (((p->family == AF_INET6) - && ((safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP))) - || (safi == SAFI_EVPN - && BGP_ATTR_NEXTHOP_AFI_IP6(attr)) - || (BGP_ATTR_NEXTHOP_AFI_IP6(attr))) { - char buf_a[512]; - char buf_b[512]; - char buf_c[BUFSIZ]; - if (attr->mp_nexthop_len - == BGP_ATTR_NHLEN_IPV6_GLOBAL) { - if (json) - json_object_string_add( - json_out, "mpNexthopGlobalIn", - inet_ntop( - AF_INET6, - &attr->mp_nexthop_global, - buf_a, sizeof(buf_a))); - else - vty_out(vty, "%s", - inet_ntop( - AF_INET6, - &attr->mp_nexthop_global, - buf_a, sizeof(buf_a))); - } else if (attr->mp_nexthop_len - == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL) { - if (json) { + if (((p->family == AF_INET) + && ((safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP))) + || (safi == SAFI_EVPN && !BGP_ATTR_NEXTHOP_AFI_IP6(attr)) + || (!BGP_ATTR_NEXTHOP_AFI_IP6(attr))) { + if (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP + || safi == SAFI_EVPN) { + if (json) + json_object_string_add( + json_out, "mpNexthopGlobalIn", + inet_ntoa(attr->mp_nexthop_global_in)); + else + vty_out(vty, "%-16s", + inet_ntoa(attr->mp_nexthop_global_in)); + } else { + if (json) + json_object_string_add( + json_out, "nexthop", + inet_ntoa(attr->nexthop)); + else + vty_out(vty, "%-16s", inet_ntoa(attr->nexthop)); + } + } else if (((p->family == AF_INET6) + && ((safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP))) + || (safi == SAFI_EVPN && BGP_ATTR_NEXTHOP_AFI_IP6(attr)) + || (BGP_ATTR_NEXTHOP_AFI_IP6(attr))) { + char buf_a[512]; + + if (attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL) { + if (json) + json_object_string_add( + json_out, "mpNexthopGlobalIn", inet_ntop(AF_INET6, &attr->mp_nexthop_global, - buf_a, sizeof(buf_a)); + buf_a, sizeof(buf_a))); + else + vty_out(vty, "%s", inet_ntop(AF_INET6, - &attr->mp_nexthop_local, - buf_b, sizeof(buf_b)); - sprintf(buf_c, "%s(%s)", buf_a, buf_b); - json_object_string_add( - json_out, - "mpNexthopGlobalLocal", buf_c); - } else - vty_out(vty, "%s(%s)", - inet_ntop( - AF_INET6, - &attr->mp_nexthop_global, - buf_a, sizeof(buf_a)), - inet_ntop( - AF_INET6, - &attr->mp_nexthop_local, - buf_b, sizeof(buf_b))); - } + &attr->mp_nexthop_global, + buf_a, sizeof(buf_a))); + } else if (attr->mp_nexthop_len + == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL) { + snprintfrr(buf_a, sizeof(buf_a), "%pI6(%pI6)", + &attr->mp_nexthop_global, + &attr->mp_nexthop_local); + if (json) + json_object_string_add(json_out, + "mpNexthopGlobalLocal", + buf_a); + else + vty_out(vty, "%s", buf_a); } } @@ -7514,87 +8387,125 @@ void route_vty_out_tag(struct vty *vty, struct prefix *p, } } -void route_vty_out_overlay(struct vty *vty, struct prefix *p, +void route_vty_out_overlay(struct vty *vty, const struct prefix *p, struct bgp_path_info *path, int display, json_object *json_paths) { struct attr *attr; - char buf[BUFSIZ]; + char buf[BUFSIZ] = {0}; json_object *json_path = NULL; - - if (json_paths) - json_path = json_object_new_object(); + json_object *json_nexthop = NULL; + json_object *json_overlay = NULL; if (!path->extra) return; + if (json_paths) { + json_path = json_object_new_object(); + json_overlay = json_object_new_object(); + json_nexthop = json_object_new_object(); + } + /* short status lead text */ route_vty_short_status_out(vty, path, json_path); /* print prefix and mask */ if (!display) - route_vty_out_route(p, vty, NULL); + route_vty_out_route(p, vty, json_path, false); else vty_out(vty, "%*s", 17, " "); /* Print attribute */ attr = path->attr; - if (attr) { - char buf1[BUFSIZ]; - int af = NEXTHOP_FAMILY(attr->mp_nexthop_len); + char buf1[BUFSIZ]; + int af = NEXTHOP_FAMILY(attr->mp_nexthop_len); - switch (af) { - case AF_INET: - vty_out(vty, "%-16s", - inet_ntop(af, &attr->mp_nexthop_global_in, buf, - BUFSIZ)); - break; - case AF_INET6: - vty_out(vty, "%s(%s)", - inet_ntop(af, &attr->mp_nexthop_global, buf, - BUFSIZ), - inet_ntop(af, &attr->mp_nexthop_local, buf1, - BUFSIZ)); - break; - default: - vty_out(vty, "?"); + switch (af) { + case AF_INET: + inet_ntop(af, &attr->mp_nexthop_global_in, buf, BUFSIZ); + if (!json_path) { + vty_out(vty, "%-16s", buf); + } else { + json_object_string_add(json_nexthop, "ip", buf); + + json_object_string_add(json_nexthop, "afi", "ipv4"); + + json_object_object_add(json_path, "nexthop", + json_nexthop); } + break; + case AF_INET6: + inet_ntop(af, &attr->mp_nexthop_global, buf, BUFSIZ); + inet_ntop(af, &attr->mp_nexthop_local, buf1, BUFSIZ); + if (!json_path) { + vty_out(vty, "%s(%s)", buf, buf1); + } else { + json_object_string_add(json_nexthop, "ipv6Global", buf); - char *str = esi2str(&(attr->evpn_overlay.eth_s_id)); + json_object_string_add(json_nexthop, "ipv6LinkLocal", + buf1); - vty_out(vty, "%s", str); - XFREE(MTYPE_TMP, str); + json_object_string_add(json_nexthop, "afi", "ipv6"); - if (is_evpn_prefix_ipaddr_v4((struct prefix_evpn *)p)) { - vty_out(vty, "/%s", - inet_ntoa(attr->evpn_overlay.gw_ip.ipv4)); - } else if (is_evpn_prefix_ipaddr_v6((struct prefix_evpn *)p)) { - vty_out(vty, "/%s", - inet_ntop(AF_INET6, - &(attr->evpn_overlay.gw_ip.ipv6), buf, - BUFSIZ)); + json_object_object_add(json_path, "nexthop", + json_nexthop); + } + break; + default: + if (!json_path) { + vty_out(vty, "?"); + } else { + json_object_string_add(json_nexthop, "Error", + "Unsupported address-family"); } - if (attr->ecommunity) { - char *mac = NULL; - struct ecommunity_val *routermac = ecommunity_lookup( - attr->ecommunity, ECOMMUNITY_ENCODE_EVPN, - ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC); - if (routermac) - mac = ecom_mac2str((char *)routermac->val); - if (mac) { - vty_out(vty, "/%s", (char *)mac); - XFREE(MTYPE_TMP, mac); + } + + if (is_evpn_prefix_ipaddr_v4((struct prefix_evpn *)p)) { + inet_ntop(AF_INET, &(attr->evpn_overlay.gw_ip.ipv4), buf, + BUFSIZ); + } else if (is_evpn_prefix_ipaddr_v6((struct prefix_evpn *)p)) { + inet_ntop(AF_INET6, &(attr->evpn_overlay.gw_ip.ipv6), buf, + BUFSIZ); + } + + if (!json_path) + vty_out(vty, "/%s", buf); + else + json_object_string_add(json_overlay, "gw", buf); + + if (attr->ecommunity) { + char *mac = NULL; + struct ecommunity_val *routermac = ecommunity_lookup( + attr->ecommunity, ECOMMUNITY_ENCODE_EVPN, + ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC); + + if (routermac) + mac = ecom_mac2str((char *)routermac->val); + if (mac) { + if (!json_path) { + vty_out(vty, "/%s", mac); + } else { + json_object_string_add(json_overlay, "rmac", + mac); } + XFREE(MTYPE_TMP, mac); } - vty_out(vty, "\n"); } + if (!json_path) { + vty_out(vty, "\n"); + } else { + json_object_object_add(json_path, "overlay", json_overlay); + + json_object_array_add(json_paths, json_path); + } } /* dampening route */ -static void damp_route_vty_out(struct vty *vty, struct prefix *p, +static void damp_route_vty_out(struct vty *vty, const struct prefix *p, struct bgp_path_info *path, int display, - safi_t safi, bool use_json, json_object *json) + afi_t afi, safi_t safi, bool use_json, + json_object *json) { struct attr *attr; int len; @@ -7606,7 +8517,7 @@ static void damp_route_vty_out(struct vty *vty, struct prefix *p, /* print prefix and mask */ if (!use_json) { if (!display) - route_vty_out_route(p, vty, NULL); + route_vty_out_route(p, vty, NULL, false); else vty_out(vty, "%*s", 17, " "); } @@ -7624,41 +8535,42 @@ static void damp_route_vty_out(struct vty *vty, struct prefix *p, } if (use_json) - bgp_damp_reuse_time_vty(vty, path, timebuf, BGP_UPTIME_LEN, - use_json, json); + bgp_damp_reuse_time_vty(vty, path, timebuf, BGP_UPTIME_LEN, afi, + safi, use_json, json); else vty_out(vty, "%s ", bgp_damp_reuse_time_vty(vty, path, timebuf, - BGP_UPTIME_LEN, use_json, - json)); + BGP_UPTIME_LEN, afi, safi, + use_json, json)); /* Print attribute */ attr = path->attr; - if (attr) { - /* Print aspath */ - if (attr->aspath) { - if (use_json) - json_object_string_add(json, "asPath", - attr->aspath->str); - else - aspath_print_vty(vty, "%s", attr->aspath, " "); - } - /* Print origin */ + /* Print aspath */ + if (attr->aspath) { if (use_json) - json_object_string_add(json, "origin", - bgp_origin_str[attr->origin]); + json_object_string_add(json, "asPath", + attr->aspath->str); else - vty_out(vty, "%s", bgp_origin_str[attr->origin]); + aspath_print_vty(vty, "%s", attr->aspath, " "); } + + /* Print origin */ + if (use_json) + json_object_string_add(json, "origin", + bgp_origin_str[attr->origin]); + else + vty_out(vty, "%s", bgp_origin_str[attr->origin]); + if (!use_json) vty_out(vty, "\n"); } /* flap route */ -static void flap_route_vty_out(struct vty *vty, struct prefix *p, +static void flap_route_vty_out(struct vty *vty, const struct prefix *p, struct bgp_path_info *path, int display, - safi_t safi, bool use_json, json_object *json) + afi_t afi, safi_t safi, bool use_json, + json_object *json) { struct attr *attr; struct bgp_damp_info *bdi; @@ -7676,7 +8588,7 @@ static void flap_route_vty_out(struct vty *vty, struct prefix *p, /* print prefix and mask */ if (!use_json) { if (!display) - route_vty_out_route(p, vty, NULL); + route_vty_out_route(p, vty, NULL, false); else vty_out(vty, "%*s", 17, " "); } @@ -7716,12 +8628,13 @@ static void flap_route_vty_out(struct vty *vty, struct prefix *p, && !CHECK_FLAG(path->flags, BGP_PATH_HISTORY)) { if (use_json) bgp_damp_reuse_time_vty(vty, path, timebuf, - BGP_UPTIME_LEN, use_json, json); + BGP_UPTIME_LEN, afi, safi, + use_json, json); else vty_out(vty, "%s ", bgp_damp_reuse_time_vty(vty, path, timebuf, - BGP_UPTIME_LEN, - use_json, json)); + BGP_UPTIME_LEN, afi, + safi, use_json, json)); } else { if (!use_json) vty_out(vty, "%*s ", 8, " "); @@ -7729,23 +8642,23 @@ static void flap_route_vty_out(struct vty *vty, struct prefix *p, /* Print attribute */ attr = path->attr; - if (attr) { - /* Print aspath */ - if (attr->aspath) { - if (use_json) - json_object_string_add(json, "asPath", - attr->aspath->str); - else - aspath_print_vty(vty, "%s", attr->aspath, " "); - } - /* Print origin */ + /* Print aspath */ + if (attr->aspath) { if (use_json) - json_object_string_add(json, "origin", - bgp_origin_str[attr->origin]); + json_object_string_add(json, "asPath", + attr->aspath->str); else - vty_out(vty, "%s", bgp_origin_str[attr->origin]); + aspath_print_vty(vty, "%s", attr->aspath, " "); } + + /* Print origin */ + if (use_json) + json_object_string_add(json, "origin", + bgp_origin_str[attr->origin]); + else + vty_out(vty, "%s", bgp_origin_str[attr->origin]); + if (!use_json) vty_out(vty, "\n"); } @@ -7785,7 +8698,7 @@ static void route_vty_out_advertised_to(struct vty *vty, struct peer *peer, } if (peer->hostname - && bgp_flag_check(peer->bgp, BGP_FLAG_SHOW_HOSTNAME)) { + && CHECK_FLAG(peer->bgp->flags, BGP_FLAG_SHOW_HOSTNAME)) { if (peer->conf_if) vty_out(vty, " %s(%s)", peer->hostname, peer->conf_if); @@ -7816,14 +8729,128 @@ static void route_vty_out_tx_ids(struct vty *vty, } } -void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct prefix *p, - struct bgp_path_info *path, afi_t afi, safi_t safi, - json_object *json_paths) +static const char *bgp_path_selection_reason2str( + enum bgp_path_selection_reason reason) +{ + switch (reason) { + case bgp_path_selection_none: + return "Nothing to Select"; + case bgp_path_selection_first: + return "First path received"; + case bgp_path_selection_evpn_sticky_mac: + return "EVPN Sticky Mac"; + case bgp_path_selection_evpn_seq: + return "EVPN sequence number"; + case bgp_path_selection_evpn_lower_ip: + return "EVPN lower IP"; + case bgp_path_selection_evpn_local_path: + return "EVPN local ES path"; + case bgp_path_selection_evpn_non_proxy: + return "EVPN non proxy"; + case bgp_path_selection_weight: + return "Weight"; + case bgp_path_selection_local_pref: + return "Local Pref"; + case bgp_path_selection_local_route: + return "Local Route"; + case bgp_path_selection_confed_as_path: + return "Confederation based AS Path"; + case bgp_path_selection_as_path: + return "AS Path"; + case bgp_path_selection_origin: + return "Origin"; + case bgp_path_selection_med: + return "MED"; + case bgp_path_selection_peer: + return "Peer Type"; + case bgp_path_selection_confed: + return "Confed Peer Type"; + case bgp_path_selection_igp_metric: + return "IGP Metric"; + case bgp_path_selection_older: + return "Older Path"; + case bgp_path_selection_router_id: + return "Router ID"; + case bgp_path_selection_cluster_length: + return "Cluser length"; + case bgp_path_selection_stale: + return "Path Staleness"; + case bgp_path_selection_local_configured: + return "Locally configured route"; + case bgp_path_selection_neighbor_ip: + return "Neighbor IP"; + case bgp_path_selection_default: + return "Nothing left to compare"; + } + return "Invalid (internal error)"; +} + +static void route_vty_out_detail_es_info(struct vty *vty, + struct attr *attr, json_object *json_path) +{ + char esi_buf[ESI_STR_LEN]; + bool es_local = !!CHECK_FLAG(attr->es_flags, ATTR_ES_IS_LOCAL); + bool peer_router = !!CHECK_FLAG(attr->es_flags, + ATTR_ES_PEER_ROUTER); + bool peer_active = !!CHECK_FLAG(attr->es_flags, + ATTR_ES_PEER_ACTIVE); + bool peer_proxy = !!CHECK_FLAG(attr->es_flags, + ATTR_ES_PEER_PROXY); + + esi_to_str(&attr->esi, esi_buf, sizeof(esi_buf)); + if (json_path) { + json_object *json_es_info = NULL; + + json_object_string_add( + json_path, "esi", + esi_buf); + if (es_local || bgp_evpn_attr_is_sync(attr)) { + json_es_info = json_object_new_object(); + if (es_local) + json_object_boolean_true_add( + json_es_info, "localEs"); + if (peer_active) + json_object_boolean_true_add( + json_es_info, "peerActive"); + if (peer_proxy) + json_object_boolean_true_add( + json_es_info, "peerProxy"); + if (peer_router) + json_object_boolean_true_add( + json_es_info, "peerRouter"); + if (attr->mm_sync_seqnum) + json_object_int_add( + json_es_info, "peerSeq", + attr->mm_sync_seqnum); + json_object_object_add( + json_path, "es_info", + json_es_info); + } + } else { + if (bgp_evpn_attr_is_sync(attr)) + vty_out(vty, + " ESI %s %s peer-info: (%s%s%sMM: %d)\n", + esi_buf, + es_local ? "local-es":"", + peer_proxy ? "proxy " : "", + peer_active ? "active ":"", + peer_router ? "router ":"", + attr->mm_sync_seqnum); + else + vty_out(vty, " ESI %s %s\n", + esi_buf, + es_local ? "local-es":""); + } +} + +void route_vty_out_detail(struct vty *vty, struct bgp *bgp, + struct bgp_dest *bn, struct bgp_path_info *path, + afi_t afi, safi_t safi, json_object *json_paths) { char buf[INET6_ADDRSTRLEN]; char buf1[BUFSIZ]; char buf2[EVPN_ROUTE_STRLEN]; - struct attr *attr; + struct attr *attr = path->attr; int sockunion_vty_out(struct vty *, union sockunion *); time_t tbuf; json_object *json_bestpath = NULL; @@ -7848,6 +8875,8 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct prefix *p, bool nexthop_self = CHECK_FLAG(path->flags, BGP_PATH_ANNC_NH_SELF) ? true : false; int i; + char *nexthop_hostname = + bgp_nexthop_hostname(path->peer, path->nexthop); if (json_paths) { json_path = json_object_new_object(); @@ -7855,819 +8884,834 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct prefix *p, json_nexthop_global = json_object_new_object(); } - if (!json_paths && safi == SAFI_EVPN) { + if (path->extra) { char tag_buf[30]; - bgp_evpn_route2str((struct prefix_evpn *)p, buf2, sizeof(buf2)); - vty_out(vty, " Route %s", buf2); + buf2[0] = '\0'; tag_buf[0] = '\0'; if (path->extra && path->extra->num_labels) { bgp_evpn_label2str(path->extra->label, path->extra->num_labels, tag_buf, sizeof(tag_buf)); - vty_out(vty, " VNI %s", tag_buf); } - vty_out(vty, "\n"); - if (path->extra && path->extra->parent) { - struct bgp_path_info *parent_ri; - struct bgp_node *rn, *prn; - - parent_ri = (struct bgp_path_info *)path->extra->parent; - rn = parent_ri->net; - if (rn && rn->prn) { - prn = rn->prn; - vty_out(vty, " Imported from %s:%s\n", - prefix_rd2str( - (struct prefix_rd *)&prn->p, - buf1, sizeof(buf1)), - buf2); + if (safi == SAFI_EVPN) { + if (!json_paths) { + bgp_evpn_route2str( + (struct prefix_evpn *) + bgp_dest_get_prefix(bn), + buf2, sizeof(buf2)); + vty_out(vty, " Route %s", buf2); + if (tag_buf[0] != '\0') + vty_out(vty, " VNI %s", tag_buf); + vty_out(vty, "\n"); + } else { + if (tag_buf[0]) + json_object_string_add(json_path, "VNI", + tag_buf); } } - } - attr = path->attr; + if (path->extra && path->extra->parent && !json_paths) { + struct bgp_path_info *parent_ri; + struct bgp_dest *dest, *pdest; - if (attr) { - /* Line1 display AS-path, Aggregator */ - if (attr->aspath) { - if (json_paths) { - if (!attr->aspath->json) - aspath_str_update(attr->aspath, true); - json_object_lock(attr->aspath->json); - json_object_object_add(json_path, "aspath", - attr->aspath->json); - } else { - if (attr->aspath->segments) - aspath_print_vty(vty, " %s", - attr->aspath, ""); - else - vty_out(vty, " Local"); + parent_ri = (struct bgp_path_info *)path->extra->parent; + dest = parent_ri->net; + if (dest && dest->pdest) { + pdest = dest->pdest; + prefix_rd2str( + (struct prefix_rd *)bgp_dest_get_prefix( + pdest), + buf1, sizeof(buf1)); + if (is_pi_family_evpn(parent_ri)) { + bgp_evpn_route2str( + (struct prefix_evpn *) + bgp_dest_get_prefix( + dest), + buf2, sizeof(buf2)); + vty_out(vty, " Imported from %s:%s, VNI %s\n", buf1, buf2, tag_buf); + } else + vty_out(vty, " Imported from %s:%s\n", buf1, buf2); } } + } - if (CHECK_FLAG(path->flags, BGP_PATH_REMOVED)) { - if (json_paths) - json_object_boolean_true_add(json_path, - "removed"); + /* Line1 display AS-path, Aggregator */ + if (attr->aspath) { + if (json_paths) { + if (!attr->aspath->json) + aspath_str_update(attr->aspath, true); + json_object_lock(attr->aspath->json); + json_object_object_add(json_path, "aspath", + attr->aspath->json); + } else { + if (attr->aspath->segments) + aspath_print_vty(vty, " %s", attr->aspath, ""); else - vty_out(vty, ", (removed)"); + vty_out(vty, " Local"); } + } - if (CHECK_FLAG(path->flags, BGP_PATH_STALE)) { - if (json_paths) - json_object_boolean_true_add(json_path, - "stale"); - else - vty_out(vty, ", (stale)"); - } + if (CHECK_FLAG(path->flags, BGP_PATH_REMOVED)) { + if (json_paths) + json_object_boolean_true_add(json_path, "removed"); + else + vty_out(vty, ", (removed)"); + } - if (CHECK_FLAG(attr->flag, - ATTR_FLAG_BIT(BGP_ATTR_AGGREGATOR))) { - if (json_paths) { - json_object_int_add(json_path, "aggregatorAs", - attr->aggregator_as); - json_object_string_add( - json_path, "aggregatorId", + if (CHECK_FLAG(path->flags, BGP_PATH_STALE)) { + if (json_paths) + json_object_boolean_true_add(json_path, "stale"); + else + vty_out(vty, ", (stale)"); + } + + if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_AGGREGATOR))) { + if (json_paths) { + json_object_int_add(json_path, "aggregatorAs", + attr->aggregator_as); + json_object_string_add( + json_path, "aggregatorId", + inet_ntoa(attr->aggregator_addr)); + if (attr->aggregator_as == BGP_AS_ZERO) + json_object_boolean_true_add( + json_path, "aggregatorAsMalformed"); + else + json_object_boolean_false_add( + json_path, "aggregatorAsMalformed"); + } else { + if (attr->aggregator_as == BGP_AS_ZERO) + vty_out(vty, + ", (aggregated by %u(malformed) %s)", + attr->aggregator_as, inet_ntoa(attr->aggregator_addr)); - } else { + else vty_out(vty, ", (aggregated by %u %s)", attr->aggregator_as, inet_ntoa(attr->aggregator_addr)); - } } + } - if (CHECK_FLAG(path->peer->af_flags[afi][safi], - PEER_FLAG_REFLECTOR_CLIENT)) { - if (json_paths) - json_object_boolean_true_add( - json_path, "rxedFromRrClient"); - else - vty_out(vty, ", (Received from a RR-client)"); - } + if (CHECK_FLAG(path->peer->af_flags[afi][safi], + PEER_FLAG_REFLECTOR_CLIENT)) { + if (json_paths) + json_object_boolean_true_add(json_path, + "rxedFromRrClient"); + else + vty_out(vty, ", (Received from a RR-client)"); + } - if (CHECK_FLAG(path->peer->af_flags[afi][safi], - PEER_FLAG_RSERVER_CLIENT)) { - if (json_paths) - json_object_boolean_true_add( - json_path, "rxedFromRsClient"); - else - vty_out(vty, ", (Received from a RS-client)"); - } + if (CHECK_FLAG(path->peer->af_flags[afi][safi], + PEER_FLAG_RSERVER_CLIENT)) { + if (json_paths) + json_object_boolean_true_add(json_path, + "rxedFromRsClient"); + else + vty_out(vty, ", (Received from a RS-client)"); + } - if (CHECK_FLAG(path->flags, BGP_PATH_HISTORY)) { - if (json_paths) - json_object_boolean_true_add( - json_path, "dampeningHistoryEntry"); - else - vty_out(vty, ", (history entry)"); - } else if (CHECK_FLAG(path->flags, BGP_PATH_DAMPED)) { - if (json_paths) - json_object_boolean_true_add( - json_path, "dampeningSuppressed"); - else - vty_out(vty, ", (suppressed due to dampening)"); - } + if (CHECK_FLAG(path->flags, BGP_PATH_HISTORY)) { + if (json_paths) + json_object_boolean_true_add(json_path, + "dampeningHistoryEntry"); + else + vty_out(vty, ", (history entry)"); + } else if (CHECK_FLAG(path->flags, BGP_PATH_DAMPED)) { + if (json_paths) + json_object_boolean_true_add(json_path, + "dampeningSuppressed"); + else + vty_out(vty, ", (suppressed due to dampening)"); + } - if (!json_paths) - vty_out(vty, "\n"); + if (!json_paths) + vty_out(vty, "\n"); - /* Line2 display Next-hop, Neighbor, Router-id */ - /* Display the nexthop */ - if ((p->family == AF_INET || p->family == AF_ETHERNET - || p->family == AF_EVPN) - && (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP - || safi == SAFI_EVPN - || !BGP_ATTR_NEXTHOP_AFI_IP6(attr))) { - if (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP - || safi == SAFI_EVPN) { - if (json_paths) + /* Line2 display Next-hop, Neighbor, Router-id */ + /* Display the nexthop */ + const struct prefix *bn_p = bgp_dest_get_prefix(bn); + + if ((bn_p->family == AF_INET || bn_p->family == AF_ETHERNET + || bn_p->family == AF_EVPN) + && (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP || safi == SAFI_EVPN + || !BGP_ATTR_NEXTHOP_AFI_IP6(attr))) { + if (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP + || safi == SAFI_EVPN) { + if (json_paths) { + json_object_string_add( + json_nexthop_global, "ip", + inet_ntoa(attr->mp_nexthop_global_in)); + + if (path->peer->hostname) json_object_string_add( - json_nexthop_global, "ip", - inet_ntoa( - attr->mp_nexthop_global_in)); - else - vty_out(vty, " %s", - inet_ntoa( - attr->mp_nexthop_global_in)); + json_nexthop_global, "hostname", + path->peer->hostname); } else { - if (json_paths) - json_object_string_add( - json_nexthop_global, "ip", - inet_ntoa(attr->nexthop)); + if (nexthop_hostname) + vty_out(vty, " %pI4(%s)", + &attr->mp_nexthop_global_in, + nexthop_hostname); else - vty_out(vty, " %s", - inet_ntoa(attr->nexthop)); + vty_out(vty, " %pI4", + &attr->mp_nexthop_global_in); } - - if (json_paths) - json_object_string_add(json_nexthop_global, - "afi", "ipv4"); } else { if (json_paths) { json_object_string_add( json_nexthop_global, "ip", - inet_ntop(AF_INET6, - &attr->mp_nexthop_global, buf, - INET6_ADDRSTRLEN)); - json_object_string_add(json_nexthop_global, - "afi", "ipv6"); - json_object_string_add(json_nexthop_global, - "scope", "global"); + inet_ntoa(attr->nexthop)); + + if (path->peer->hostname) + json_object_string_add( + json_nexthop_global, "hostname", + path->peer->hostname); } else { - vty_out(vty, " %s", - inet_ntop(AF_INET6, - &attr->mp_nexthop_global, buf, - INET6_ADDRSTRLEN)); + if (nexthop_hostname) + vty_out(vty, " %pI4(%s)", + &attr->nexthop, + nexthop_hostname); + else + vty_out(vty, " %pI4", + &attr->nexthop); } } - /* Display the IGP cost or 'inaccessible' */ - if (!CHECK_FLAG(path->flags, BGP_PATH_VALID)) { + if (json_paths) + json_object_string_add(json_nexthop_global, "afi", + "ipv4"); + } else { + if (json_paths) { + json_object_string_add( + json_nexthop_global, "ip", + inet_ntop(AF_INET6, &attr->mp_nexthop_global, + buf, INET6_ADDRSTRLEN)); + + if (path->peer->hostname) + json_object_string_add(json_nexthop_global, + "hostname", + path->peer->hostname); + + json_object_string_add(json_nexthop_global, "afi", + "ipv6"); + json_object_string_add(json_nexthop_global, "scope", + "global"); + } else { + if (nexthop_hostname) + vty_out(vty, " %pI6(%s)", + &attr->mp_nexthop_global, + nexthop_hostname); + else + vty_out(vty, " %pI6", + &attr->mp_nexthop_global); + } + } + + /* Display the IGP cost or 'inaccessible' */ + if (!CHECK_FLAG(path->flags, BGP_PATH_VALID)) { + if (json_paths) + json_object_boolean_false_add(json_nexthop_global, + "accessible"); + else + vty_out(vty, " (inaccessible)"); + } else { + if (path->extra && path->extra->igpmetric) { if (json_paths) - json_object_boolean_false_add( - json_nexthop_global, "accessible"); + json_object_int_add(json_nexthop_global, + "metric", + path->extra->igpmetric); else - vty_out(vty, " (inaccessible)"); - } else { - if (path->extra && path->extra->igpmetric) { - if (json_paths) - json_object_int_add( - json_nexthop_global, "metric", - path->extra->igpmetric); - else - vty_out(vty, " (metric %u)", - path->extra->igpmetric); - } + vty_out(vty, " (metric %u)", + path->extra->igpmetric); + } - /* IGP cost is 0, display this only for json */ - else { - if (json_paths) - json_object_int_add(json_nexthop_global, - "metric", 0); - } + /* IGP cost is 0, display this only for json */ + else { + if (json_paths) + json_object_int_add(json_nexthop_global, + "metric", 0); + } + if (json_paths) + json_object_boolean_true_add(json_nexthop_global, + "accessible"); + } + + /* Display peer "from" output */ + /* This path was originated locally */ + if (path->peer == bgp->peer_self) { + + if (safi == SAFI_EVPN + || (bn_p->family == AF_INET + && !BGP_ATTR_NEXTHOP_AFI_IP6(attr))) { if (json_paths) - json_object_boolean_true_add( - json_nexthop_global, "accessible"); + json_object_string_add(json_peer, "peerId", + "0.0.0.0"); + else + vty_out(vty, " from 0.0.0.0 "); + } else { + if (json_paths) + json_object_string_add(json_peer, "peerId", + "::"); + else + vty_out(vty, " from :: "); } - /* Display peer "from" output */ - /* This path was originated locally */ - if (path->peer == bgp->peer_self) { + if (json_paths) + json_object_string_add(json_peer, "routerId", + inet_ntoa(bgp->router_id)); + else + vty_out(vty, "(%s)", inet_ntoa(bgp->router_id)); + } - if (safi == SAFI_EVPN - || (p->family == AF_INET - && !BGP_ATTR_NEXTHOP_AFI_IP6(attr))) { - if (json_paths) - json_object_string_add( - json_peer, "peerId", "0.0.0.0"); + /* We RXed this path from one of our peers */ + else { + + if (json_paths) { + json_object_string_add(json_peer, "peerId", + sockunion2str(&path->peer->su, + buf, + SU_ADDRSTRLEN)); + json_object_string_add(json_peer, "routerId", + inet_ntop(AF_INET, + &path->peer->remote_id, + buf1, sizeof(buf1))); + + if (path->peer->hostname) + json_object_string_add(json_peer, "hostname", + path->peer->hostname); + + if (path->peer->domainname) + json_object_string_add(json_peer, "domainname", + path->peer->domainname); + + if (path->peer->conf_if) + json_object_string_add(json_peer, "interface", + path->peer->conf_if); + } else { + if (path->peer->conf_if) { + if (path->peer->hostname + && CHECK_FLAG(path->peer->bgp->flags, + BGP_FLAG_SHOW_HOSTNAME)) + vty_out(vty, " from %s(%s)", + path->peer->hostname, + path->peer->conf_if); else - vty_out(vty, " from 0.0.0.0 "); + vty_out(vty, " from %s", + path->peer->conf_if); } else { - if (json_paths) - json_object_string_add(json_peer, - "peerId", "::"); + if (path->peer->hostname + && CHECK_FLAG(path->peer->bgp->flags, + BGP_FLAG_SHOW_HOSTNAME)) + vty_out(vty, " from %s(%s)", + path->peer->hostname, + path->peer->host); else - vty_out(vty, " from :: "); + vty_out(vty, " from %s", + sockunion2str(&path->peer->su, + buf, + SU_ADDRSTRLEN)); } - if (json_paths) - json_object_string_add( - json_peer, "routerId", - inet_ntoa(bgp->router_id)); + if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID)) + vty_out(vty, " (%s)", + inet_ntoa(attr->originator_id)); else - vty_out(vty, "(%s)", inet_ntoa(bgp->router_id)); - } - - /* We RXed this path from one of our peers */ - else { - - if (json_paths) { - json_object_string_add( - json_peer, "peerId", - sockunion2str(&path->peer->su, buf, - SU_ADDRSTRLEN)); - json_object_string_add( - json_peer, "routerId", + vty_out(vty, " (%s)", inet_ntop(AF_INET, &path->peer->remote_id, buf1, sizeof(buf1))); + } + } - if (path->peer->hostname) - json_object_string_add( - json_peer, "hostname", - path->peer->hostname); + /* + * Note when vrfid of nexthop is different from that of prefix + */ + if (path->extra && path->extra->bgp_orig) { + vrf_id_t nexthop_vrfid = path->extra->bgp_orig->vrf_id; - if (path->peer->domainname) - json_object_string_add( - json_peer, "domainname", - path->peer->domainname); + if (json_paths) { + const char *vn; - if (path->peer->conf_if) - json_object_string_add( - json_peer, "interface", - path->peer->conf_if); + if (path->extra->bgp_orig->inst_type + == BGP_INSTANCE_TYPE_DEFAULT) + vn = VRF_DEFAULT_NAME; + else + vn = path->extra->bgp_orig->name; + + json_object_string_add(json_path, "nhVrfName", vn); + + if (nexthop_vrfid == VRF_UNKNOWN) { + json_object_int_add(json_path, "nhVrfId", -1); } else { - if (path->peer->conf_if) { - if (path->peer->hostname - && bgp_flag_check( - path->peer->bgp, - BGP_FLAG_SHOW_HOSTNAME)) - vty_out(vty, " from %s(%s)", - path->peer->hostname, - path->peer->conf_if); - else - vty_out(vty, " from %s", - path->peer->conf_if); - } else { - if (path->peer->hostname - && bgp_flag_check( - path->peer->bgp, - BGP_FLAG_SHOW_HOSTNAME)) - vty_out(vty, " from %s(%s)", - path->peer->hostname, - path->peer->host); - else - vty_out(vty, " from %s", - sockunion2str( - &path->peer->su, - buf, - SU_ADDRSTRLEN)); - } + json_object_int_add(json_path, "nhVrfId", + (int)nexthop_vrfid); + } + } else { + if (nexthop_vrfid == VRF_UNKNOWN) + vty_out(vty, " vrf ?"); + else { + struct vrf *vrf; - if (attr->flag - & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID)) - vty_out(vty, " (%s)", - inet_ntoa(attr->originator_id)); - else - vty_out(vty, " (%s)", - inet_ntop( - AF_INET, - &path->peer->remote_id, - buf1, sizeof(buf1))); + vrf = vrf_lookup_by_id(nexthop_vrfid); + vty_out(vty, " vrf %s(%u)", + VRF_LOGNAME(vrf), nexthop_vrfid); } } + } + + if (nexthop_self) { + if (json_paths) { + json_object_boolean_true_add(json_path, + "announceNexthopSelf"); + } else { + vty_out(vty, " announce-nh-self"); + } + } + + if (!json_paths) + vty_out(vty, "\n"); + + /* display the link-local nexthop */ + if (attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL) { + if (json_paths) { + json_nexthop_ll = json_object_new_object(); + json_object_string_add( + json_nexthop_ll, "ip", + inet_ntop(AF_INET6, &attr->mp_nexthop_local, + buf, INET6_ADDRSTRLEN)); + + if (path->peer->hostname) + json_object_string_add(json_nexthop_ll, + "hostname", + path->peer->hostname); + + json_object_string_add(json_nexthop_ll, "afi", "ipv6"); + json_object_string_add(json_nexthop_ll, "scope", + "link-local"); + + json_object_boolean_true_add(json_nexthop_ll, + "accessible"); + + if (!attr->mp_nexthop_prefer_global) + json_object_boolean_true_add(json_nexthop_ll, + "used"); + else + json_object_boolean_true_add( + json_nexthop_global, "used"); + } else { + vty_out(vty, " (%s) %s\n", + inet_ntop(AF_INET6, &attr->mp_nexthop_local, + buf, INET6_ADDRSTRLEN), + attr->mp_nexthop_prefer_global + ? "(prefer-global)" + : "(used)"); + } + } + /* If we do not have a link-local nexthop then we must flag the + global as "used" */ + else { + if (json_paths) + json_object_boolean_true_add(json_nexthop_global, + "used"); + } + + if (safi == SAFI_EVPN && + bgp_evpn_is_esi_valid(&attr->esi)) { + route_vty_out_detail_es_info(vty, attr, json_path); + } + + /* Line 3 display Origin, Med, Locpref, Weight, Tag, valid, + * Int/Ext/Local, Atomic, best */ + if (json_paths) + json_object_string_add(json_path, "origin", + bgp_origin_long_str[attr->origin]); + else + vty_out(vty, " Origin %s", + bgp_origin_long_str[attr->origin]); - /* - * Note when vrfid of nexthop is different from that of prefix - */ - if (path->extra && path->extra->bgp_orig) { - vrf_id_t nexthop_vrfid = path->extra->bgp_orig->vrf_id; + if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC)) { + if (json_paths) + json_object_int_add(json_path, "metric", attr->med); + else + vty_out(vty, ", metric %u", attr->med); + } - if (json_paths) { - const char *vn; + if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF)) { + if (json_paths) + json_object_int_add(json_path, "locPrf", + attr->local_pref); + else + vty_out(vty, ", localpref %u", attr->local_pref); + } - if (path->extra->bgp_orig->inst_type - == BGP_INSTANCE_TYPE_DEFAULT) + if (attr->weight != 0) { + if (json_paths) + json_object_int_add(json_path, "weight", attr->weight); + else + vty_out(vty, ", weight %u", attr->weight); + } - vn = VRF_DEFAULT_NAME; - else - vn = path->extra->bgp_orig->name; + if (attr->tag != 0) { + if (json_paths) + json_object_int_add(json_path, "tag", attr->tag); + else + vty_out(vty, ", tag %" ROUTE_TAG_PRI, attr->tag); + } - json_object_string_add(json_path, "nhVrfName", - vn); + if (!CHECK_FLAG(path->flags, BGP_PATH_VALID)) { + if (json_paths) + json_object_boolean_false_add(json_path, "valid"); + else + vty_out(vty, ", invalid"); + } else if (!CHECK_FLAG(path->flags, BGP_PATH_HISTORY)) { + if (json_paths) + json_object_boolean_true_add(json_path, "valid"); + else + vty_out(vty, ", valid"); + } - if (nexthop_vrfid == VRF_UNKNOWN) { - json_object_int_add(json_path, - "nhVrfId", -1); - } else { - json_object_int_add(json_path, - "nhVrfId", (int)nexthop_vrfid); - } - } else { - if (nexthop_vrfid == VRF_UNKNOWN) - vty_out(vty, " vrf ?"); + if (path->peer != bgp->peer_self) { + if (path->peer->as == path->peer->local_as) { + if (CHECK_FLAG(bgp->config, BGP_CONFIG_CONFEDERATION)) { + if (json_paths) + json_object_string_add( + json_peer, "type", + "confed-internal"); else - vty_out(vty, " vrf %u", nexthop_vrfid); - } - } - - if (nexthop_self) { - if (json_paths) { - json_object_boolean_true_add(json_path, - "announceNexthopSelf"); + vty_out(vty, ", confed-internal"); } else { - vty_out(vty, " announce-nh-self"); + if (json_paths) + json_object_string_add( + json_peer, "type", "internal"); + else + vty_out(vty, ", internal"); } - } - - if (!json_paths) - vty_out(vty, "\n"); - - /* display the link-local nexthop */ - if (attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL) { - if (json_paths) { - json_nexthop_ll = json_object_new_object(); - json_object_string_add( - json_nexthop_ll, "ip", - inet_ntop(AF_INET6, - &attr->mp_nexthop_local, buf, - INET6_ADDRSTRLEN)); - json_object_string_add(json_nexthop_ll, "afi", - "ipv6"); - json_object_string_add(json_nexthop_ll, "scope", - "link-local"); - - json_object_boolean_true_add(json_nexthop_ll, - "accessible"); - - if (!attr->mp_nexthop_prefer_global) - json_object_boolean_true_add( - json_nexthop_ll, "used"); + } else { + if (bgp_confederation_peers_check(bgp, + path->peer->as)) { + if (json_paths) + json_object_string_add( + json_peer, "type", + "confed-external"); else - json_object_boolean_true_add( - json_nexthop_global, "used"); + vty_out(vty, ", confed-external"); } else { - vty_out(vty, " (%s) %s\n", - inet_ntop(AF_INET6, - &attr->mp_nexthop_local, buf, - INET6_ADDRSTRLEN), - attr->mp_nexthop_prefer_global - ? "(prefer-global)" - : "(used)"); + if (json_paths) + json_object_string_add( + json_peer, "type", "external"); + else + vty_out(vty, ", external"); } } - /* If we do not have a link-local nexthop then we must flag the - global as "used" */ - else { - if (json_paths) - json_object_boolean_true_add( - json_nexthop_global, "used"); + } else if (path->sub_type == BGP_ROUTE_AGGREGATE) { + if (json_paths) { + json_object_boolean_true_add(json_path, "aggregated"); + json_object_boolean_true_add(json_path, "local"); + } else { + vty_out(vty, ", aggregated, local"); + } + } else if (path->type != ZEBRA_ROUTE_BGP) { + if (json_paths) + json_object_boolean_true_add(json_path, "sourced"); + else + vty_out(vty, ", sourced"); + } else { + if (json_paths) { + json_object_boolean_true_add(json_path, "sourced"); + json_object_boolean_true_add(json_path, "local"); + } else { + vty_out(vty, ", sourced, local"); } + } - /* Line 3 display Origin, Med, Locpref, Weight, Tag, valid, - * Int/Ext/Local, Atomic, best */ + if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ATOMIC_AGGREGATE)) { if (json_paths) - json_object_string_add( - json_path, "origin", - bgp_origin_long_str[attr->origin]); + json_object_boolean_true_add(json_path, + "atomicAggregate"); else - vty_out(vty, " Origin %s", - bgp_origin_long_str[attr->origin]); + vty_out(vty, ", atomic-aggregate"); + } - if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC)) { - if (json_paths) { + if (CHECK_FLAG(path->flags, BGP_PATH_MULTIPATH) + || (CHECK_FLAG(path->flags, BGP_PATH_SELECTED) + && bgp_path_info_mpath_count(path))) { + if (json_paths) + json_object_boolean_true_add(json_path, "multipath"); + else + vty_out(vty, ", multipath"); + } - /* - * Adding "metric" field to match with - * corresponding CLI. "med" will be - * deprecated in future. - */ - json_object_int_add(json_path, "med", - attr->med); - json_object_int_add(json_path, "metric", - attr->med); - } else - vty_out(vty, ", metric %u", attr->med); - } + // Mark the bestpath(s) + if (CHECK_FLAG(path->flags, BGP_PATH_DMED_SELECTED)) { + first_as = aspath_get_first_as(attr->aspath); - if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF)) { - if (json_paths) - json_object_int_add(json_path, "localpref", - attr->local_pref); + if (json_paths) { + if (!json_bestpath) + json_bestpath = json_object_new_object(); + json_object_int_add(json_bestpath, "bestpathFromAs", + first_as); + } else { + if (first_as) + vty_out(vty, ", bestpath-from-AS %u", first_as); else - vty_out(vty, ", localpref %u", - attr->local_pref); + vty_out(vty, ", bestpath-from-AS Local"); } + } - if (attr->weight != 0) { - if (json_paths) - json_object_int_add(json_path, "weight", - attr->weight); - else - vty_out(vty, ", weight %u", attr->weight); + if (CHECK_FLAG(path->flags, BGP_PATH_SELECTED)) { + if (json_paths) { + if (!json_bestpath) + json_bestpath = json_object_new_object(); + json_object_boolean_true_add(json_bestpath, "overall"); + json_object_string_add( + json_bestpath, "selectionReason", + bgp_path_selection_reason2str(bn->reason)); + } else { + vty_out(vty, ", best"); + vty_out(vty, " (%s)", + bgp_path_selection_reason2str(bn->reason)); } + } - if (attr->tag != 0) { - if (json_paths) - json_object_int_add(json_path, "tag", - attr->tag); - else - vty_out(vty, ", tag %" ROUTE_TAG_PRI, - attr->tag); - } + if (json_bestpath) + json_object_object_add(json_path, "bestpath", json_bestpath); - if (!CHECK_FLAG(path->flags, BGP_PATH_VALID)) { - if (json_paths) - json_object_boolean_false_add(json_path, - "valid"); - else - vty_out(vty, ", invalid"); - } else if (!CHECK_FLAG(path->flags, BGP_PATH_HISTORY)) { - if (json_paths) - json_object_boolean_true_add(json_path, - "valid"); - else - vty_out(vty, ", valid"); + if (!json_paths) + vty_out(vty, "\n"); + + /* Line 4 display Community */ + if (attr->community) { + if (json_paths) { + if (!attr->community->json) + community_str(attr->community, true); + json_object_lock(attr->community->json); + json_object_object_add(json_path, "community", + attr->community->json); + } else { + vty_out(vty, " Community: %s\n", + attr->community->str); } + } - if (path->peer != bgp->peer_self) { - if (path->peer->as == path->peer->local_as) { - if (CHECK_FLAG(bgp->config, - BGP_CONFIG_CONFEDERATION)) { - if (json_paths) - json_object_string_add( - json_peer, "type", - "confed-internal"); - else - vty_out(vty, - ", confed-internal"); - } else { - if (json_paths) - json_object_string_add( - json_peer, "type", - "internal"); - else - vty_out(vty, ", internal"); - } - } else { - if (bgp_confederation_peers_check( - bgp, path->peer->as)) { - if (json_paths) - json_object_string_add( - json_peer, "type", - "confed-external"); - else - vty_out(vty, - ", confed-external"); - } else { - if (json_paths) - json_object_string_add( - json_peer, "type", - "external"); - else - vty_out(vty, ", external"); - } - } - } else if (path->sub_type == BGP_ROUTE_AGGREGATE) { - if (json_paths) { - json_object_boolean_true_add(json_path, - "aggregated"); - json_object_boolean_true_add(json_path, - "local"); - } else { - vty_out(vty, ", aggregated, local"); - } - } else if (path->type != ZEBRA_ROUTE_BGP) { - if (json_paths) - json_object_boolean_true_add(json_path, - "sourced"); - else - vty_out(vty, ", sourced"); + /* Line 5 display Extended-community */ + if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES)) { + if (json_paths) { + json_ext_community = json_object_new_object(); + json_object_string_add(json_ext_community, "string", + attr->ecommunity->str); + json_object_object_add(json_path, "extendedCommunity", + json_ext_community); } else { - if (json_paths) { - json_object_boolean_true_add(json_path, - "sourced"); - json_object_boolean_true_add(json_path, - "local"); - } else { - vty_out(vty, ", sourced, local"); - } + vty_out(vty, " Extended Community: %s\n", + attr->ecommunity->str); } + } - if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ATOMIC_AGGREGATE)) { - if (json_paths) - json_object_boolean_true_add(json_path, - "atomicAggregate"); - else - vty_out(vty, ", atomic-aggregate"); + /* Line 6 display Large community */ + if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LARGE_COMMUNITIES)) { + if (json_paths) { + if (!attr->lcommunity->json) + lcommunity_str(attr->lcommunity, true); + json_object_lock(attr->lcommunity->json); + json_object_object_add(json_path, "largeCommunity", + attr->lcommunity->json); + } else { + vty_out(vty, " Large Community: %s\n", + attr->lcommunity->str); } + } - if (CHECK_FLAG(path->flags, BGP_PATH_MULTIPATH) - || (CHECK_FLAG(path->flags, BGP_PATH_SELECTED) - && bgp_path_info_mpath_count(path))) { + /* Line 7 display Originator, Cluster-id */ + if ((attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID)) + || (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_CLUSTER_LIST))) { + if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID)) { if (json_paths) - json_object_boolean_true_add(json_path, - "multipath"); + json_object_string_add( + json_path, "originatorId", + inet_ntoa(attr->originator_id)); else - vty_out(vty, ", multipath"); + vty_out(vty, " Originator: %s", + inet_ntoa(attr->originator_id)); } - // Mark the bestpath(s) - if (CHECK_FLAG(path->flags, BGP_PATH_DMED_SELECTED)) { - first_as = aspath_get_first_as(attr->aspath); + if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_CLUSTER_LIST)) { + int i; if (json_paths) { - if (!json_bestpath) - json_bestpath = - json_object_new_object(); - json_object_int_add(json_bestpath, - "bestpathFromAs", first_as); + json_cluster_list = json_object_new_object(); + json_cluster_list_list = + json_object_new_array(); + + for (i = 0; i < attr->cluster->length / 4; + i++) { + json_string = json_object_new_string( + inet_ntoa(attr->cluster + ->list[i])); + json_object_array_add( + json_cluster_list_list, + json_string); + } + + /* + * struct cluster_list does not have + * "str" variable like aspath and community + * do. Add this someday if someone asks + * for it. + * json_object_string_add(json_cluster_list, + * "string", attr->cluster->str); + */ + json_object_object_add(json_cluster_list, + "list", + json_cluster_list_list); + json_object_object_add(json_path, "clusterList", + json_cluster_list); } else { - if (first_as) - vty_out(vty, ", bestpath-from-AS %u", - first_as); - else - vty_out(vty, - ", bestpath-from-AS Local"); - } - } + vty_out(vty, ", Cluster list: "); - if (CHECK_FLAG(path->flags, BGP_PATH_SELECTED)) { - if (json_paths) { - if (!json_bestpath) - json_bestpath = - json_object_new_object(); - json_object_boolean_true_add(json_bestpath, - "overall"); - } else - vty_out(vty, ", best"); + for (i = 0; i < attr->cluster->length / 4; + i++) { + vty_out(vty, "%s ", + inet_ntoa(attr->cluster + ->list[i])); + } + } } - if (json_bestpath) - json_object_object_add(json_path, "bestpath", - json_bestpath); - if (!json_paths) vty_out(vty, "\n"); + } - /* Line 4 display Community */ - if (attr->community) { - if (json_paths) { - if (!attr->community->json) - community_str(attr->community, true); - json_object_lock(attr->community->json); - json_object_object_add(json_path, "community", - attr->community->json); - } else { - vty_out(vty, " Community: %s\n", - attr->community->str); - } - } - - /* Line 5 display Extended-community */ - if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES)) { - if (json_paths) { - json_ext_community = json_object_new_object(); - json_object_string_add(json_ext_community, - "string", - attr->ecommunity->str); - json_object_object_add(json_path, - "extendedCommunity", - json_ext_community); - } else { - vty_out(vty, " Extended Community: %s\n", - attr->ecommunity->str); - } - } + if (path->extra && path->extra->damp_info) + bgp_damp_info_vty(vty, path, afi, safi, json_path); - /* Line 6 display Large community */ - if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LARGE_COMMUNITIES)) { - if (json_paths) { - if (!attr->lcommunity->json) - lcommunity_str(attr->lcommunity, true); - json_object_lock(attr->lcommunity->json); - json_object_object_add(json_path, - "largeCommunity", - attr->lcommunity->json); - } else { - vty_out(vty, " Large Community: %s\n", - attr->lcommunity->str); - } - } + /* Remote Label */ + if (path->extra && bgp_is_valid_label(&path->extra->label[0]) + && (safi != SAFI_EVPN && !is_route_parent_evpn(path))) { + mpls_label_t label = label_pton(&path->extra->label[0]); - /* Line 7 display Originator, Cluster-id */ - if ((attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID)) - || (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_CLUSTER_LIST))) { - if (attr->flag - & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID)) { - if (json_paths) - json_object_string_add( - json_path, "originatorId", - inet_ntoa(attr->originator_id)); - else - vty_out(vty, " Originator: %s", - inet_ntoa(attr->originator_id)); - } - - if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_CLUSTER_LIST)) { - int i; - - if (json_paths) { - json_cluster_list = - json_object_new_object(); - json_cluster_list_list = - json_object_new_array(); - - for (i = 0; - i < attr->cluster->length / 4; - i++) { - json_string = json_object_new_string( - inet_ntoa( - attr->cluster->list - [i])); - json_object_array_add( - json_cluster_list_list, - json_string); - } + if (json_paths) + json_object_int_add(json_path, "remoteLabel", label); + else + vty_out(vty, " Remote label: %d\n", label); + } - /* struct cluster_list does not have - "str" variable like - * aspath and community do. Add this - someday if someone - * asks for it. - json_object_string_add(json_cluster_list, - "string", attr->cluster->str); - */ - json_object_object_add( - json_cluster_list, "list", - json_cluster_list_list); - json_object_object_add( - json_path, "clusterList", - json_cluster_list); - } else { - vty_out(vty, ", Cluster list: "); - - for (i = 0; - i < attr->cluster->length / 4; - i++) { - vty_out(vty, "%s ", - inet_ntoa( - attr->cluster->list - [i])); - } - } - } + /* Remote SID */ + if (path->extra && path->extra->num_sids > 0 && safi != SAFI_EVPN) { + inet_ntop(AF_INET6, &path->extra->sid, buf, sizeof(buf)); + if (json_paths) + json_object_string_add(json_path, "remoteSid", buf); + else + vty_out(vty, " Remote SID: %s\n", buf); + } - if (!json_paths) - vty_out(vty, "\n"); - } + /* Label Index */ + if (attr->label_index != BGP_INVALID_LABEL_INDEX) { + if (json_paths) + json_object_int_add(json_path, "labelIndex", + attr->label_index); + else + vty_out(vty, " Label Index: %d\n", + attr->label_index); + } - if (path->extra && path->extra->damp_info) - bgp_damp_info_vty(vty, path, json_path); + /* Line 8 display Addpath IDs */ + if (path->addpath_rx_id + || bgp_addpath_info_has_ids(&path->tx_addpath)) { + if (json_paths) { + json_object_int_add(json_path, "addpathRxId", + path->addpath_rx_id); - /* Remote Label */ - if (path->extra && bgp_is_valid_label(&path->extra->label[0]) - && safi != SAFI_EVPN) { - mpls_label_t label = label_pton(&path->extra->label[0]); + /* Keep backwards compatibility with the old API + * by putting TX All's ID in the old field + */ + json_object_int_add( + json_path, "addpathTxId", + path->tx_addpath + .addpath_tx_id[BGP_ADDPATH_ALL]); - if (json_paths) - json_object_int_add(json_path, "remoteLabel", - label); - else - vty_out(vty, " Remote label: %d\n", label); - } + /* ... but create a specific field for each + * strategy + */ + for (i = 0; i < BGP_ADDPATH_MAX; i++) { + json_object_int_add( + json_path, + bgp_addpath_names(i)->id_json_name, + path->tx_addpath.addpath_tx_id[i]); + } + } else { + vty_out(vty, " AddPath ID: RX %u, ", + path->addpath_rx_id); - /* Label Index */ - if (attr->label_index != BGP_INVALID_LABEL_INDEX) { - if (json_paths) - json_object_int_add(json_path, "labelIndex", - attr->label_index); - else - vty_out(vty, " Label Index: %d\n", - attr->label_index); + route_vty_out_tx_ids(vty, &path->tx_addpath); } + } - /* Line 8 display Addpath IDs */ - if (path->addpath_rx_id - || bgp_addpath_info_has_ids(&path->tx_addpath)) { - if (json_paths) { - json_object_int_add(json_path, "addpathRxId", - path->addpath_rx_id); - - /* Keep backwards compatibility with the old API - * by putting TX All's ID in the old field - */ - json_object_int_add( - json_path, "addpathTxId", - path->tx_addpath.addpath_tx_id - [BGP_ADDPATH_ALL]); + /* If we used addpath to TX a non-bestpath we need to display + * "Advertised to" on a path-by-path basis + */ + if (bgp_addpath_is_addpath_used(&bgp->tx_addpath, afi, safi)) { + first = 1; - /* ... but create a specific field for each - * strategy - */ - for (i = 0; i < BGP_ADDPATH_MAX; i++) { - json_object_int_add( - json_path, - bgp_addpath_names(i) - ->id_json_name, - path->tx_addpath - .addpath_tx_id[i]); - } - } else { - vty_out(vty, " AddPath ID: RX %u, ", - path->addpath_rx_id); + for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { + addpath_capable = + bgp_addpath_encode_tx(peer, afi, safi); + has_adj = bgp_adj_out_lookup( + peer, path->net, + bgp_addpath_id_for_peer(peer, afi, safi, + &path->tx_addpath)); + + if ((addpath_capable && has_adj) + || (!addpath_capable && has_adj + && CHECK_FLAG(path->flags, + BGP_PATH_SELECTED))) { + if (json_path && !json_adv_to) + json_adv_to = json_object_new_object(); - route_vty_out_tx_ids(vty, &path->tx_addpath); + route_vty_out_advertised_to( + vty, peer, &first, + " Advertised to:", json_adv_to); } } - /* If we used addpath to TX a non-bestpath we need to display - * "Advertised to" on a path-by-path basis - */ - if (bgp_addpath_is_addpath_used(&bgp->tx_addpath, afi, safi)) { - first = 1; - - for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { - addpath_capable = - bgp_addpath_encode_tx(peer, afi, safi); - has_adj = bgp_adj_out_lookup( - peer, path->net, - bgp_addpath_id_for_peer( - peer, afi, safi, - &path->tx_addpath)); - - if ((addpath_capable && has_adj) - || (!addpath_capable && has_adj - && CHECK_FLAG(path->flags, - BGP_PATH_SELECTED))) { - if (json_path && !json_adv_to) - json_adv_to = - json_object_new_object(); - - route_vty_out_advertised_to( - vty, peer, &first, - " Advertised to:", - json_adv_to); - } + if (json_path) { + if (json_adv_to) { + json_object_object_add( + json_path, "advertisedTo", json_adv_to); } - - if (json_path) { - if (json_adv_to) { - json_object_object_add(json_path, - "advertisedTo", - json_adv_to); - } - } else { - if (!first) { - vty_out(vty, "\n"); - } + } else { + if (!first) { + vty_out(vty, "\n"); } } + } - /* Line 9 display Uptime */ - tbuf = time(NULL) - (bgp_clock() - path->uptime); - if (json_paths) { - json_last_update = json_object_new_object(); - json_object_int_add(json_last_update, "epoch", tbuf); - json_object_string_add(json_last_update, "string", - ctime(&tbuf)); - json_object_object_add(json_path, "lastUpdate", - json_last_update); - } else - vty_out(vty, " Last update: %s", ctime(&tbuf)); - - /* Line 10 display PMSI tunnel attribute, if present */ - if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_PMSI_TUNNEL)) { - const char *str = lookup_msg(bgp_pmsi_tnltype_str, - attr->pmsi_tnl_type, - PMSI_TNLTYPE_STR_DEFAULT); + /* Line 9 display Uptime */ + tbuf = time(NULL) - (bgp_clock() - path->uptime); + if (json_paths) { + json_last_update = json_object_new_object(); + json_object_int_add(json_last_update, "epoch", tbuf); + json_object_string_add(json_last_update, "string", + ctime(&tbuf)); + json_object_object_add(json_path, "lastUpdate", + json_last_update); + } else + vty_out(vty, " Last update: %s", ctime(&tbuf)); - if (json_paths) { - json_pmsi = json_object_new_object(); - json_object_string_add(json_pmsi, - "tunnelType", str); - json_object_int_add(json_pmsi, - "label", - label2vni(&attr->label)); - json_object_object_add(json_path, "pmsi", - json_pmsi); - } else - vty_out(vty, - " PMSI Tunnel Type: %s, label: %d\n", - str, label2vni(&attr->label)); - } + /* Line 10 display PMSI tunnel attribute, if present */ + if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_PMSI_TUNNEL)) { + const char *str = + lookup_msg(bgp_pmsi_tnltype_str, attr->pmsi_tnl_type, + PMSI_TNLTYPE_STR_DEFAULT); + if (json_paths) { + json_pmsi = json_object_new_object(); + json_object_string_add(json_pmsi, "tunnelType", str); + json_object_int_add(json_pmsi, "label", + label2vni(&attr->label)); + json_object_object_add(json_path, "pmsi", json_pmsi); + } else + vty_out(vty, " PMSI Tunnel Type: %s, label: %d\n", + str, label2vni(&attr->label)); } /* We've constructed the json object for this path, add it to the json @@ -8691,8 +9735,7 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct prefix *p, json_object_object_add(json_path, "peer", json_peer); json_object_array_add(json_paths, json_path); - } else - vty_out(vty, "\n"); + } } #define BGP_SHOW_HEADER_CSV "Flags, Network, Next Hop, Metric, LocPrf, Weight, Path" @@ -8715,7 +9758,8 @@ static int bgp_show_prefix_longer(struct vty *vty, struct bgp *bgp, const char *prefix, afi_t afi, safi_t safi, enum bgp_show_type type); static int bgp_show_regexp(struct vty *vty, struct bgp *bgp, const char *regstr, - afi_t afi, safi_t safi, enum bgp_show_type type); + afi_t afi, safi_t safi, enum bgp_show_type type, + bool use_json); static int bgp_show_community(struct vty *vty, struct bgp *bgp, const char *comstr, int exact, afi_t afi, safi_t safi, bool use_json); @@ -8726,16 +9770,15 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi, void *output_arg, bool use_json, char *rd, int is_last, unsigned long *output_cum, unsigned long *total_cum, - unsigned long *json_header_depth) + unsigned long *json_header_depth, bool wide) { struct bgp_path_info *pi; - struct bgp_node *rn; + struct bgp_dest *dest; int header = 1; int display; unsigned long output_count = 0; unsigned long total_count = 0; struct prefix *p; - char buf2[BUFSIZ]; json_object *json_paths = NULL; int first = 1; @@ -8744,8 +9787,7 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi, if (use_json && !*json_header_depth) { vty_out(vty, - "{\n \"vrfId\": %d,\n \"vrfName\": \"%s\",\n \"tableVersion\": %" PRId64 - ",\n \"routerId\": \"%s\",\n \"defaultLocPrf\": %u,\n" + "{\n \"vrfId\": %d,\n \"vrfName\": \"%s\",\n \"tableVersion\": %" PRId64",\n \"routerId\": \"%s\",\n \"defaultLocPrf\": %u,\n" " \"localAS\": %u,\n \"routes\": { ", bgp->vrf_id == VRF_UNKNOWN ? -1 : (int)bgp->vrf_id, bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT @@ -8765,8 +9807,10 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi, } /* Start processing of routes. */ - for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) { - pi = bgp_node_get_bgp_path_info(rn); + for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) { + const struct prefix *dest_p = bgp_dest_get_prefix(dest); + + pi = bgp_dest_get_bgp_path_info(dest); if (pi == NULL) continue; @@ -8795,7 +9839,7 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi, if (type == bgp_show_type_prefix_list) { struct prefix_list *plist = output_arg; - if (prefix_list_apply(plist, &rn->p) + if (prefix_list_apply(plist, dest_p) != PREFIX_PERMIT) continue; } @@ -8810,14 +9854,14 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi, struct route_map *rmap = output_arg; struct bgp_path_info path; struct attr dummy_attr; - int ret; + route_map_result_t ret; - bgp_attr_dup(&dummy_attr, pi->attr); + dummy_attr = *pi->attr; path.peer = pi->peer; path.attr = &dummy_attr; - ret = route_map_apply(rmap, &rn->p, RMAP_BGP, + ret = route_map_apply(rmap, dest_p, RMAP_BGP, &path); if (ret == RMAP_DENYMATCH) continue; @@ -8835,20 +9879,20 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi, if (type == bgp_show_type_cidr_only) { uint32_t destination; - destination = ntohl(rn->p.u.prefix4.s_addr); + destination = ntohl(dest_p->u.prefix4.s_addr); if (IN_CLASSC(destination) - && rn->p.prefixlen == 24) + && dest_p->prefixlen == 24) continue; if (IN_CLASSB(destination) - && rn->p.prefixlen == 16) + && dest_p->prefixlen == 16) continue; if (IN_CLASSA(destination) - && rn->p.prefixlen == 8) + && dest_p->prefixlen == 8) continue; } if (type == bgp_show_type_prefix_longer) { p = output_arg; - if (!prefix_match(p, &rn->p)) + if (!prefix_match(p, dest_p)) continue; } if (type == bgp_show_type_community_all) { @@ -8892,11 +9936,28 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi, lcom)) continue; } + + if (type == bgp_show_type_lcommunity_exact) { + struct lcommunity *lcom = output_arg; + + if (!pi->attr->lcommunity + || !lcommunity_cmp(pi->attr->lcommunity, + lcom)) + continue; + } if (type == bgp_show_type_lcommunity_list) { struct community_list *list = output_arg; - if (!lcommunity_list_match(pi->attr->lcommunity, - list)) + if (!lcommunity_list_match(pi->attr->lcommunity, + list)) + continue; + } + if (type + == bgp_show_type_lcommunity_list_exact) { + struct community_list *list = output_arg; + + if (!lcommunity_list_exact_match( + pi->attr->lcommunity, list)) continue; } if (type == bgp_show_type_lcommunity_all) { @@ -8911,8 +9972,7 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi, } if (!use_json && header) { - vty_out(vty, "BGP table version is %" PRIu64 - ", local router ID is %s, vrf id ", + vty_out(vty, "BGP table version is %" PRIu64", local router ID is %s, vrf id ", table->version, inet_ntoa(bgp->router_id)); if (bgp->vrf_id == VRF_UNKNOWN) @@ -8933,7 +9993,8 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi, || type == bgp_show_type_flap_neighbor) vty_out(vty, BGP_SHOW_FLAP_HEADER); else - vty_out(vty, BGP_SHOW_HEADER); + vty_out(vty, (wide ? BGP_SHOW_HEADER_WIDE + : BGP_SHOW_HEADER)); header = 0; } if (rd != NULL && !display && !output_count) { @@ -8944,15 +10005,17 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi, } if (type == bgp_show_type_dampend_paths || type == bgp_show_type_damp_neighbor) - damp_route_vty_out(vty, &rn->p, pi, display, - safi, use_json, json_paths); + damp_route_vty_out(vty, dest_p, pi, display, + AFI_IP, safi, use_json, + json_paths); else if (type == bgp_show_type_flap_statistics || type == bgp_show_type_flap_neighbor) - flap_route_vty_out(vty, &rn->p, pi, display, - safi, use_json, json_paths); + flap_route_vty_out(vty, dest_p, pi, display, + AFI_IP, safi, use_json, + json_paths); else - route_vty_out(vty, &rn->p, pi, display, safi, - json_paths); + route_vty_out(vty, dest_p, pi, display, safi, + json_paths, wide); display++; } @@ -8961,39 +10024,40 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi, if (!use_json) continue; - p = &rn->p; /* encode prefix */ - if (p->family == AF_FLOWSPEC) { + if (dest_p->family == AF_FLOWSPEC) { char retstr[BGP_FLOWSPEC_STRING_DISPLAY_MAX]; - bgp_fs_nlri_get_string((unsigned char *) - p->u.prefix_flowspec.ptr, - p->u.prefix_flowspec - .prefixlen, - retstr, - NLRI_STRING_FORMAT_MIN, - NULL); + + bgp_fs_nlri_get_string( + (unsigned char *) + dest_p->u.prefix_flowspec.ptr, + dest_p->u.prefix_flowspec.prefixlen, + retstr, NLRI_STRING_FORMAT_MIN, NULL, + family2afi(dest_p->u + .prefix_flowspec.family)); if (first) - vty_out(vty, "\"%s/%d\": ", - retstr, - p->u.prefix_flowspec.prefixlen); + vty_out(vty, "\"%s/%d\": ", retstr, + dest_p->u.prefix_flowspec + .prefixlen); else - vty_out(vty, ",\"%s/%d\": ", - retstr, - p->u.prefix_flowspec.prefixlen); + vty_out(vty, ",\"%s/%d\": ", retstr, + dest_p->u.prefix_flowspec + .prefixlen); } else { - prefix2str(p, buf2, sizeof(buf2)); if (first) - vty_out(vty, "\"%s\": ", buf2); + vty_out(vty, "\"%pFX\": ", dest_p); else - vty_out(vty, ",\"%s\": ", buf2); + vty_out(vty, ",\"%pFX\": ", dest_p); } vty_out(vty, "%s", - json_object_to_json_string(json_paths)); + json_object_to_json_string_ext( + json_paths, JSON_C_TO_STRING_PRETTY)); json_object_free(json_paths); json_paths = NULL; first = 0; - } + } else + json_object_free(json_paths); } if (output_cum) { @@ -9036,7 +10100,7 @@ int bgp_show_table_rd(struct vty *vty, struct bgp *bgp, safi_t safi, struct bgp_table *table, struct prefix_rd *prd_match, enum bgp_show_type type, void *output_arg, bool use_json) { - struct bgp_node *rn, *next; + struct bgp_dest *dest, *next; unsigned long output_cum = 0; unsigned long total_cum = 0; unsigned long json_header_depth = 0; @@ -9045,21 +10109,23 @@ int bgp_show_table_rd(struct vty *vty, struct bgp *bgp, safi_t safi, show_msg = (!use_json && type == bgp_show_type_normal); - for (rn = bgp_table_top(table); rn; rn = next) { - next = bgp_route_next(rn); - if (prd_match && memcmp(rn->p.u.val, prd_match->val, 8) != 0) + for (dest = bgp_table_top(table); dest; dest = next) { + const struct prefix *dest_p = bgp_dest_get_prefix(dest); + + next = bgp_route_next(dest); + if (prd_match && memcmp(dest_p->u.val, prd_match->val, 8) != 0) continue; - itable = bgp_node_get_bgp_table_info(rn); + itable = bgp_dest_get_bgp_table_info(dest); if (itable != NULL) { struct prefix_rd prd; char rd[RD_ADDRSTRLEN]; - memcpy(&prd, &(rn->p), sizeof(struct prefix_rd)); + memcpy(&prd, dest_p, sizeof(struct prefix_rd)); prefix_rd2str(&prd, rd, sizeof(rd)); bgp_show_table(vty, bgp, safi, itable, type, output_arg, use_json, rd, next == NULL, &output_cum, - &total_cum, &json_header_depth); + &total_cum, &json_header_depth, false); if (next == NULL) show_msg = false; } @@ -9076,7 +10142,8 @@ int bgp_show_table_rd(struct vty *vty, struct bgp *bgp, safi_t safi, return CMD_SUCCESS; } static int bgp_show(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, - enum bgp_show_type type, void *output_arg, bool use_json) + enum bgp_show_type type, void *output_arg, bool use_json, + bool wide) { struct bgp_table *table; unsigned long json_header_depth = 0; @@ -9110,11 +10177,12 @@ static int bgp_show(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, safi = SAFI_UNICAST; return bgp_show_table(vty, bgp, safi, table, type, output_arg, use_json, - NULL, 1, NULL, NULL, &json_header_depth); + NULL, 1, NULL, NULL, &json_header_depth, wide); } static void bgp_show_all_instances_routes_vty(struct vty *vty, afi_t afi, - safi_t safi, bool use_json) + safi_t safi, bool use_json, + bool wide) { struct listnode *node, *nnode; struct bgp *bgp; @@ -9143,7 +10211,7 @@ static void bgp_show_all_instances_routes_vty(struct vty *vty, afi_t afi, : bgp->name); } bgp_show(vty, bgp, afi, safi, bgp_show_type_normal, NULL, - use_json); + use_json, wide); } if (use_json) @@ -9154,11 +10222,11 @@ static void bgp_show_all_instances_routes_vty(struct vty *vty, afi_t afi, /* Header of detailed BGP route information */ void route_vty_out_detail_header(struct vty *vty, struct bgp *bgp, - struct bgp_node *rn, struct prefix_rd *prd, + struct bgp_dest *dest, struct prefix_rd *prd, afi_t afi, safi_t safi, json_object *json) { struct bgp_path_info *pi; - struct prefix *p; + const struct prefix *p; struct peer *peer; struct listnode *node, *nnode; char buf1[RD_ADDRSTRLEN]; @@ -9186,45 +10254,55 @@ void route_vty_out_detail_header(struct vty *vty, struct bgp *bgp, mpls_label_t label = 0; json_object *json_adv_to = NULL; - p = &rn->p; - has_valid_label = bgp_is_valid_label(&rn->local_label); + p = bgp_dest_get_prefix(dest); + has_valid_label = bgp_is_valid_label(&dest->local_label); if (has_valid_label) - label = label_pton(&rn->local_label); + label = label_pton(&dest->local_label); - if (json) { - if (has_valid_label) - json_object_int_add(json, "localLabel", label); + if (safi == SAFI_EVPN) { - json_object_string_add( - json, "prefix", - prefix2str(p, prefix_str, sizeof(prefix_str))); - } else { - if (safi == SAFI_EVPN) + if (!json) { vty_out(vty, "BGP routing table entry for %s%s%s\n", prd ? prefix_rd2str(prd, buf1, sizeof(buf1)) - : "", - prd ? ":" : "", + : "", prd ? ":" : "", bgp_evpn_route2str((struct prefix_evpn *)p, - buf3, sizeof(buf3))); - else + buf3, sizeof(buf3))); + } else { + json_object_string_add(json, "rd", + prd ? prefix_rd2str(prd, buf1, sizeof(buf1)) : + ""); + bgp_evpn_route2json((struct prefix_evpn *)p, json); + } + } else { + if (!json) { vty_out(vty, "BGP routing table entry for %s%s%s/%d\n", ((safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP) - ? prefix_rd2str(prd, buf1, - sizeof(buf1)) - : ""), + ? prefix_rd2str(prd, buf1, + sizeof(buf1)) + : ""), safi == SAFI_MPLS_VPN ? ":" : "", inet_ntop(p->family, &p->u.prefix, buf2, - INET6_ADDRSTRLEN), + INET6_ADDRSTRLEN), p->prefixlen); - if (has_valid_label) + } else + json_object_string_add(json, "prefix", + prefix2str(p, prefix_str, sizeof(prefix_str))); + } + + if (has_valid_label) { + if (json) + json_object_int_add(json, "localLabel", label); + else vty_out(vty, "Local label: %d\n", label); + } + + if (!json) if (bgp_labeled_safi(safi) && safi != SAFI_EVPN) vty_out(vty, "not allocated\n"); - } - for (pi = bgp_node_get_bgp_path_info(rn); pi; pi = pi->next) { + for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) { count++; if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED)) { best = count; @@ -9270,12 +10348,14 @@ void route_vty_out_detail_header(struct vty *vty, struct bgp *bgp, vty_out(vty, "Paths: (%d available", count); if (best) { vty_out(vty, ", best #%d", best); - if (safi == SAFI_UNICAST) - vty_out(vty, ", table %s", - (bgp->inst_type - == BGP_INSTANCE_TYPE_DEFAULT) - ? VRF_DEFAULT_NAME - : bgp->name); + if (safi == SAFI_UNICAST) { + if (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT) + vty_out(vty, ", table %s", + VRF_DEFAULT_NAME); + else + vty_out(vty, ", vrf %s", + bgp->name); + } } else vty_out(vty, ", no best path"); @@ -9328,7 +10408,7 @@ void route_vty_out_detail_header(struct vty *vty, struct bgp *bgp, * though then we must display Advertised to on a path-by-path basis. */ if (!bgp_addpath_is_addpath_used(&bgp->tx_addpath, afi, safi)) { for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { - if (bgp_adj_out_lookup(peer, rn, 0)) { + if (bgp_adj_out_lookup(peer, dest, 0)) { if (json && !json_adv_to) json_adv_to = json_object_new_object(); @@ -9352,6 +10432,57 @@ void route_vty_out_detail_header(struct vty *vty, struct bgp *bgp, } } +static void bgp_show_path_info(struct prefix_rd *pfx_rd, + struct bgp_dest *bgp_node, struct vty *vty, + struct bgp *bgp, afi_t afi, safi_t safi, + json_object *json, enum bgp_path_type pathtype, + int *display) +{ + struct bgp_path_info *pi; + int header = 1; + char rdbuf[RD_ADDRSTRLEN]; + json_object *json_header = NULL; + json_object *json_paths = NULL; + + for (pi = bgp_dest_get_bgp_path_info(bgp_node); pi; pi = pi->next) { + + if (json && !json_paths) { + /* Instantiate json_paths only if path is valid */ + json_paths = json_object_new_array(); + if (pfx_rd) { + prefix_rd2str(pfx_rd, rdbuf, sizeof(rdbuf)); + json_header = json_object_new_object(); + } else + json_header = json; + } + + if (header) { + route_vty_out_detail_header( + vty, bgp, bgp_node, pfx_rd, + AFI_IP, safi, json_header); + header = 0; + } + (*display)++; + + if (pathtype == BGP_PATH_SHOW_ALL + || (pathtype == BGP_PATH_SHOW_BESTPATH + && CHECK_FLAG(pi->flags, BGP_PATH_SELECTED)) + || (pathtype == BGP_PATH_SHOW_MULTIPATH + && (CHECK_FLAG(pi->flags, BGP_PATH_MULTIPATH) + || CHECK_FLAG(pi->flags, BGP_PATH_SELECTED)))) + route_vty_out_detail(vty, bgp, bgp_node, + pi, AFI_IP, safi, + json_paths); + } + + if (json && json_paths) { + json_object_object_add(json_header, "paths", json_paths); + + if (pfx_rd) + json_object_object_add(json, rdbuf, json_header); + } +} + /* Display specified route of BGP table. */ static int bgp_show_route_in_table(struct vty *vty, struct bgp *bgp, struct bgp_table *rib, const char *ip_str, @@ -9360,12 +10491,10 @@ static int bgp_show_route_in_table(struct vty *vty, struct bgp *bgp, enum bgp_path_type pathtype, bool use_json) { int ret; - int header; int display = 0; struct prefix match; - struct bgp_node *rn; - struct bgp_node *rm; - struct bgp_path_info *pi; + struct bgp_dest *dest; + struct bgp_dest *rm; struct bgp_table *table; json_object *json = NULL; json_object *json_paths = NULL; @@ -9379,109 +10508,129 @@ static int bgp_show_route_in_table(struct vty *vty, struct bgp *bgp, match.family = afi2family(afi); - if (use_json) { + if (use_json) json = json_object_new_object(); - json_paths = json_object_new_array(); - } - if (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP || safi == SAFI_EVPN) { - for (rn = bgp_table_top(rib); rn; rn = bgp_route_next(rn)) { - if (prd && memcmp(rn->p.u.val, prd->val, 8) != 0) + if (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP) { + for (dest = bgp_table_top(rib); dest; + dest = bgp_route_next(dest)) { + const struct prefix *dest_p = bgp_dest_get_prefix(dest); + + if (prd && memcmp(dest_p->u.val, prd->val, 8) != 0) continue; - table = bgp_node_get_bgp_table_info(rn); + table = bgp_dest_get_bgp_table_info(dest); if (!table) continue; - header = 1; - if ((rm = bgp_node_match(table, &match)) == NULL) continue; + const struct prefix *rm_p = bgp_dest_get_prefix(rm); if (prefix_check - && rm->p.prefixlen != match.prefixlen) { - bgp_unlock_node(rm); + && rm_p->prefixlen != match.prefixlen) { + bgp_dest_unlock_node(rm); continue; } - for (pi = bgp_node_get_bgp_path_info(rm); pi; - pi = pi->next) { - if (header) { - route_vty_out_detail_header( - vty, bgp, rm, - (struct prefix_rd *)&rn->p, - AFI_IP, safi, json); - header = 0; - } - display++; + bgp_show_path_info((struct prefix_rd *)dest_p, rm, vty, + bgp, afi, safi, json, pathtype, + &display); + + bgp_dest_unlock_node(rm); + } + } else if (safi == SAFI_EVPN) { + struct bgp_dest *longest_pfx; + bool is_exact_pfxlen_match = false; + + for (dest = bgp_table_top(rib); dest; + dest = bgp_route_next(dest)) { + const struct prefix *dest_p = bgp_dest_get_prefix(dest); + + if (prd && memcmp(&dest_p->u.val, prd->val, 8) != 0) + continue; + table = bgp_dest_get_bgp_table_info(dest); + if (!table) + continue; - if (pathtype == BGP_PATH_SHOW_ALL - || (pathtype == BGP_PATH_SHOW_BESTPATH - && CHECK_FLAG(pi->flags, - BGP_PATH_SELECTED)) - || (pathtype == BGP_PATH_SHOW_MULTIPATH - && (CHECK_FLAG(pi->flags, - BGP_PATH_MULTIPATH) - || CHECK_FLAG(pi->flags, - BGP_PATH_SELECTED)))) - route_vty_out_detail(vty, bgp, &rm->p, - pi, AFI_IP, safi, - json_paths); + longest_pfx = NULL; + is_exact_pfxlen_match = false; + /* + * Search through all the prefixes for a match. The + * pfx's are enumerated in ascending order of pfxlens. + * So, the last pfx match is the longest match. Set + * is_exact_pfxlen_match when we get exact pfxlen match + */ + for (rm = bgp_table_top(table); rm; + rm = bgp_route_next(rm)) { + const struct prefix *rm_p = + bgp_dest_get_prefix(rm); + /* + * Get prefixlen of the ip-prefix within type5 + * evpn route + */ + if (evpn_type5_prefix_match(rm_p, &match) + && rm->info) { + longest_pfx = rm; + int type5_pfxlen = + bgp_evpn_get_type5_prefixlen( + rm_p); + if (type5_pfxlen == match.prefixlen) { + is_exact_pfxlen_match = true; + bgp_dest_unlock_node(rm); + break; + } + } } - bgp_unlock_node(rm); + if (!longest_pfx) + continue; + + if (prefix_check && !is_exact_pfxlen_match) + continue; + + rm = longest_pfx; + bgp_dest_lock_node(rm); + + bgp_show_path_info((struct prefix_rd *)dest_p, rm, vty, + bgp, afi, safi, json, pathtype, + &display); + + bgp_dest_unlock_node(rm); } } else if (safi == SAFI_FLOWSPEC) { + if (use_json) + json_paths = json_object_new_array(); + display = bgp_flowspec_display_match_per_ip(afi, rib, &match, prefix_check, vty, use_json, json_paths); + if (use_json) { + if (display) + json_object_object_add(json, "paths", + json_paths); + else + json_object_free(json_paths); + } } else { - header = 1; - - if ((rn = bgp_node_match(rib, &match)) != NULL) { + if ((dest = bgp_node_match(rib, &match)) != NULL) { + const struct prefix *dest_p = bgp_dest_get_prefix(dest); if (!prefix_check - || rn->p.prefixlen == match.prefixlen) { - for (pi = bgp_node_get_bgp_path_info(rn); pi; - pi = pi->next) { - if (header) { - route_vty_out_detail_header( - vty, bgp, rn, NULL, afi, - safi, json); - header = 0; - } - display++; - - if (pathtype == BGP_PATH_SHOW_ALL - || (pathtype - == BGP_PATH_SHOW_BESTPATH - && CHECK_FLAG( - pi->flags, - BGP_PATH_SELECTED)) - || (pathtype - == BGP_PATH_SHOW_MULTIPATH - && (CHECK_FLAG( - pi->flags, - BGP_PATH_MULTIPATH) - || CHECK_FLAG( - pi->flags, - BGP_PATH_SELECTED)))) - route_vty_out_detail( - vty, bgp, &rn->p, pi, - afi, safi, json_paths); - } + || dest_p->prefixlen == match.prefixlen) { + bgp_show_path_info(NULL, dest, vty, bgp, afi, + safi, json, pathtype, + &display); } - bgp_unlock_node(rn); + bgp_dest_unlock_node(dest); } } if (use_json) { - if (display) - json_object_object_add(json, "paths", json_paths); - vty_out(vty, "%s\n", json_object_to_json_string_ext( - json, JSON_C_TO_STRING_PRETTY)); + json, JSON_C_TO_STRING_PRETTY | + JSON_C_TO_STRING_NOSLASHESCAPE)); json_object_free(json); } else { if (!display) { @@ -9520,14 +10669,15 @@ static int bgp_show_route(struct vty *vty, struct bgp *bgp, const char *ip_str, } static int bgp_show_lcommunity(struct vty *vty, struct bgp *bgp, int argc, - struct cmd_token **argv, afi_t afi, safi_t safi, - bool uj) + struct cmd_token **argv, bool exact, afi_t afi, + safi_t safi, bool uj) { struct lcommunity *lcom; struct buffer *b; int i; char *str; int first = 0; + int ret; b = buffer_new(1024); for (i = 0; i < argc; i++) { @@ -9552,13 +10702,18 @@ static int bgp_show_lcommunity(struct vty *vty, struct bgp *bgp, int argc, return CMD_WARNING; } - return bgp_show(vty, bgp, afi, safi, bgp_show_type_lcommunity, lcom, - uj); + ret = bgp_show(vty, bgp, afi, safi, + (exact ? bgp_show_type_lcommunity_exact + : bgp_show_type_lcommunity), + lcom, uj, false); + + lcommunity_free(&lcom); + return ret; } static int bgp_show_lcommunity_list(struct vty *vty, struct bgp *bgp, - const char *lcom, afi_t afi, safi_t safi, - bool uj) + const char *lcom, bool exact, afi_t afi, + safi_t safi, bool uj) { struct community_list *list; @@ -9570,13 +10725,15 @@ static int bgp_show_lcommunity_list(struct vty *vty, struct bgp *bgp, return CMD_WARNING; } - return bgp_show(vty, bgp, afi, safi, bgp_show_type_lcommunity_list, - list, uj); + return bgp_show(vty, bgp, afi, safi, + (exact ? bgp_show_type_lcommunity_list_exact + : bgp_show_type_lcommunity_list), + list, uj, false); } DEFUN (show_ip_bgp_large_community_list, show_ip_bgp_large_community_list_cmd, - "show [ip] bgp [ VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] large-community-list <(1-500)|WORD> [json]", + "show [ip] bgp [ VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] large-community-list <(1-500)|WORD> [exact-match] [json]", SHOW_STR IP_STR BGP_STR @@ -9586,41 +10743,37 @@ DEFUN (show_ip_bgp_large_community_list, "Display routes matching the large-community-list\n" "large-community-list number\n" "large-community-list name\n" + "Exact match of the large-communities\n" JSON_STR) { - char *vrf = NULL; afi_t afi = AFI_IP6; safi_t safi = SAFI_UNICAST; int idx = 0; - - if (argv_find(argv, argc, "ip", &idx)) - afi = AFI_IP; - if (argv_find(argv, argc, "view", &idx) - || argv_find(argv, argc, "vrf", &idx)) - vrf = argv[++idx]->arg; - if (argv_find(argv, argc, "ipv4", &idx) - || argv_find(argv, argc, "ipv6", &idx)) { - afi = strmatch(argv[idx]->text, "ipv6") ? AFI_IP6 : AFI_IP; - if (argv_find(argv, argc, "unicast", &idx) - || argv_find(argv, argc, "multicast", &idx)) - safi = bgp_vty_safi_from_str(argv[idx]->text); - } - + bool exact_match = 0; + struct bgp *bgp = NULL; bool uj = use_json(argc, argv); - struct bgp *bgp = bgp_lookup_by_name(vrf); - if (bgp == NULL) { - vty_out(vty, "Can't find BGP instance %s\n", vrf); - return CMD_WARNING; - } + if (uj) + argc--; + + bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi, + &bgp, uj); + if (!idx) + return CMD_WARNING; argv_find(argv, argc, "large-community-list", &idx); - return bgp_show_lcommunity_list(vty, bgp, argv[idx + 1]->arg, afi, safi, - uj); + + const char *clist_number_or_name = argv[++idx]->arg; + + if (++idx < argc && strmatch(argv[idx]->text, "exact-match")) + exact_match = 1; + + return bgp_show_lcommunity_list(vty, bgp, clist_number_or_name, + exact_match, afi, safi, uj); } DEFUN (show_ip_bgp_large_community, show_ip_bgp_large_community_cmd, - "show [ip] bgp [ VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] large-community [AA:BB:CC] [json]", + "show [ip] bgp [ VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] large-community [ [exact-match]] [json]", SHOW_STR IP_STR BGP_STR @@ -9629,81 +10782,208 @@ DEFUN (show_ip_bgp_large_community, BGP_SAFI_WITH_LABEL_HELP_STR "Display routes matching the large-communities\n" "List of large-community numbers\n" + "Exact match of the large-communities\n" JSON_STR) { - char *vrf = NULL; afi_t afi = AFI_IP6; safi_t safi = SAFI_UNICAST; int idx = 0; + bool exact_match = 0; + struct bgp *bgp = NULL; + bool uj = use_json(argc, argv); - if (argv_find(argv, argc, "ip", &idx)) - afi = AFI_IP; - if (argv_find(argv, argc, "view", &idx) - || argv_find(argv, argc, "vrf", &idx)) - vrf = argv[++idx]->arg; - if (argv_find(argv, argc, "ipv4", &idx) - || argv_find(argv, argc, "ipv6", &idx)) { - afi = strmatch(argv[idx]->text, "ipv6") ? AFI_IP6 : AFI_IP; - if (argv_find(argv, argc, "unicast", &idx) - || argv_find(argv, argc, "multicast", &idx)) - safi = bgp_vty_safi_from_str(argv[idx]->text); - } + if (uj) + argc--; + + bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi, + &bgp, uj); + if (!idx) + return CMD_WARNING; + + if (argv_find(argv, argc, "AA:BB:CC", &idx)) { + if (argv_find(argv, argc, "exact-match", &idx)) + exact_match = 1; + return bgp_show_lcommunity(vty, bgp, argc, argv, + exact_match, afi, safi, uj); + } else + return bgp_show(vty, bgp, afi, safi, + bgp_show_type_lcommunity_all, NULL, uj, false); +} + +static int bgp_table_stats_single(struct vty *vty, struct bgp *bgp, afi_t afi, + safi_t safi, struct json_object *json_array); +static int bgp_table_stats(struct vty *vty, struct bgp *bgp, afi_t afi, + safi_t safi, struct json_object *json); + +DEFUN(show_ip_bgp_statistics_all, show_ip_bgp_statistics_all_cmd, + "show [ip] bgp [ VIEWVRFNAME] statistics-all [json]", + SHOW_STR IP_STR BGP_STR BGP_INSTANCE_HELP_STR + "Display number of prefixes for all afi/safi\n" JSON_STR) +{ bool uj = use_json(argc, argv); + struct bgp *bgp = NULL; + safi_t safi = SAFI_UNICAST; + afi_t afi = AFI_IP6; + int idx = 0; + struct json_object *json_all = NULL; + struct json_object *json_afi_safi = NULL; - struct bgp *bgp = bgp_lookup_by_name(vrf); - if (bgp == NULL) { - vty_out(vty, "Can't find BGP instance %s\n", vrf); + bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi, + &bgp, false); + if (!idx) return CMD_WARNING; + + if (uj) + json_all = json_object_new_object(); + + FOREACH_AFI_SAFI (afi, safi) { + /* + * So limit output to those afi/safi pairs that + * actually have something interesting in them + */ + if (strmatch(get_afi_safi_str(afi, safi, true), + "Unknown")) { + continue; + } + if (uj) { + json_afi_safi = json_object_new_array(); + json_object_object_add( + json_all, + get_afi_safi_str(afi, safi, true), + json_afi_safi); + } else { + json_afi_safi = NULL; + } + + bgp_table_stats(vty, bgp, afi, safi, json_afi_safi); + } + + if (uj) { + vty_out(vty, "%s", + json_object_to_json_string_ext( + json_all, JSON_C_TO_STRING_PRETTY)); + json_object_free(json_all); } - if (argv_find(argv, argc, "AA:BB:CC", &idx)) - return bgp_show_lcommunity(vty, bgp, argc, argv, afi, safi, uj); + return CMD_SUCCESS; +} + +/* BGP route print out function without JSON */ +DEFUN (show_ip_bgp_l2vpn_evpn_statistics, + show_ip_bgp_l2vpn_evpn_statistics_cmd, + "show [ip] bgp [ VIEWVRFNAME] l2vpn evpn statistics [json]", + SHOW_STR + IP_STR + BGP_STR + BGP_INSTANCE_HELP_STR + L2VPN_HELP_STR + EVPN_HELP_STR + "BGP RIB advertisement statistics\n" + JSON_STR) +{ + afi_t afi = AFI_IP6; + safi_t safi = SAFI_UNICAST; + struct bgp *bgp = NULL; + int idx = 0, ret; + bool uj = use_json(argc, argv); + struct json_object *json_afi_safi = NULL, *json = NULL; + + bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi, + &bgp, false); + if (!idx) + return CMD_WARNING; + + if (uj) + json_afi_safi = json_object_new_array(); else - return bgp_show(vty, bgp, afi, safi, - bgp_show_type_lcommunity_all, NULL, uj); + json_afi_safi = NULL; + + ret = bgp_table_stats(vty, bgp, afi, safi, json_afi_safi); + + if (uj) { + json = json_object_new_object(); + json_object_object_add(json, get_afi_safi_str(afi, safi, true), + json_afi_safi); + vty_out(vty, "%s", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } + return ret; } -static int bgp_table_stats(struct vty *vty, struct bgp *bgp, afi_t afi, - safi_t safi); +/* BGP route print out function without JSON */ +DEFUN(show_ip_bgp_afi_safi_statistics, show_ip_bgp_afi_safi_statistics_cmd, + "show [ip] bgp [ VIEWVRFNAME] [" BGP_AFI_CMD_STR + " [" BGP_SAFI_WITH_LABEL_CMD_STR + "]]\ + statistics [json]", + SHOW_STR IP_STR BGP_STR BGP_INSTANCE_HELP_STR BGP_AFI_HELP_STR + BGP_SAFI_WITH_LABEL_HELP_STR + "BGP RIB advertisement statistics\n" JSON_STR) +{ + afi_t afi = AFI_IP6; + safi_t safi = SAFI_UNICAST; + struct bgp *bgp = NULL; + int idx = 0, ret; + bool uj = use_json(argc, argv); + struct json_object *json_afi_safi = NULL, *json = NULL; + + bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi, + &bgp, false); + if (!idx) + return CMD_WARNING; + + if (uj) + json_afi_safi = json_object_new_array(); + else + json_afi_safi = NULL; + ret = bgp_table_stats(vty, bgp, afi, safi, json_afi_safi); + + if (uj) { + json = json_object_new_object(); + json_object_object_add(json, get_afi_safi_str(afi, safi, true), + json_afi_safi); + vty_out(vty, "%s", + json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } + return ret; +} /* BGP route print out function without JSON */ -DEFUN (show_ip_bgp, - show_ip_bgp_cmd, - "show [ip] bgp [ VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]]\ +DEFUN(show_ip_bgp, show_ip_bgp_cmd, + "show [ip] bgp [ VIEWVRFNAME] [" BGP_AFI_CMD_STR + " [" BGP_SAFI_WITH_LABEL_CMD_STR + "]]\ \ |route-map WORD\ |prefix-list WORD\ |filter-list WORD\ - |statistics\ |community-list <(1-500)|WORD> [exact-match]\ |A.B.C.D/M longer-prefixes\ |X:X::X:X/M longer-prefixes\ - >", - SHOW_STR - IP_STR - BGP_STR - BGP_INSTANCE_HELP_STR - BGP_AFI_HELP_STR - BGP_SAFI_WITH_LABEL_HELP_STR - "Display detailed information about dampening\n" - "Display detail of configured dampening parameters\n" - "Display routes matching the route-map\n" - "A route-map to match on\n" - "Display routes conforming to the prefix-list\n" - "Prefix-list name\n" - "Display routes conforming to the filter-list\n" - "Regular expression access list name\n" - "BGP RIB advertisement statistics\n" - "Display routes matching the community-list\n" - "community-list number\n" - "community-list name\n" - "Exact match of the communities\n" - "IPv4 prefix\n" - "Display route and more specific routes\n" - "IPv6 prefix\n" - "Display route and more specific routes\n") + >", + SHOW_STR IP_STR BGP_STR BGP_INSTANCE_HELP_STR BGP_AFI_HELP_STR + BGP_SAFI_WITH_LABEL_HELP_STR + "Display detailed information about dampening\n" + "Display detail of configured dampening parameters\n" + "Display routes matching the route-map\n" + "A route-map to match on\n" + "Display routes conforming to the prefix-list\n" + "Prefix-list name\n" + "Display routes conforming to the filter-list\n" + "Regular expression access list name\n" + "Display routes matching the community-list\n" + "community-list number\n" + "community-list name\n" + "Exact match of the communities\n" + "IPv4 prefix\n" + "Display route and more specific routes\n" + "IPv6 prefix\n" + "Display route and more specific routes\n") { afi_t afi = AFI_IP6; safi_t safi = SAFI_UNICAST; @@ -9729,9 +11009,6 @@ DEFUN (show_ip_bgp, return bgp_show_filter_list(vty, bgp, argv[idx + 1]->arg, afi, safi, bgp_show_type_filter_list); - if (argv_find(argv, argc, "statistics", &idx)) - return bgp_table_stats(vty, bgp, afi, safi); - if (argv_find(argv, argc, "route-map", &idx)) return bgp_show_route_map(vty, bgp, argv[idx + 1]->arg, afi, safi, bgp_show_type_route_map); @@ -9754,7 +11031,7 @@ DEFUN (show_ip_bgp, } /* BGP route print out function with JSON */ -DEFUN (show_ip_bgp_json, +DEFPY (show_ip_bgp_json, show_ip_bgp_json_cmd, "show [ip] bgp [ VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]]\ [cidr-only\ @@ -9764,7 +11041,7 @@ DEFUN (show_ip_bgp_json, |accept-own|accept-own-nexthop|route-filter-v6\ |route-filter-v4|route-filter-translated-v6\ |route-filter-translated-v4] [exact-match]\ - ] [json]", + ] [json$uj | wide$wide]", SHOW_STR IP_STR BGP_STR @@ -9792,7 +11069,8 @@ DEFUN (show_ip_bgp_json, "RT translated VPNv6 route filtering (well-known community)\n" "RT translated VPNv4 route filtering (well-known community)\n" "Exact match of the communities\n" - JSON_STR) + JSON_STR + "Increase table width for longer prefixes\n") { afi_t afi = AFI_IP6; safi_t safi = SAFI_UNICAST; @@ -9800,7 +11078,6 @@ DEFUN (show_ip_bgp_json, struct bgp *bgp = NULL; int idx = 0; int exact_match = 0; - bool uj = use_json(argc, argv); if (uj) argc--; @@ -9812,16 +11089,17 @@ DEFUN (show_ip_bgp_json, if (argv_find(argv, argc, "cidr-only", &idx)) return bgp_show(vty, bgp, afi, safi, bgp_show_type_cidr_only, - NULL, uj); + NULL, uj, wide); if (argv_find(argv, argc, "dampening", &idx)) { if (argv_find(argv, argc, "dampened-paths", &idx)) return bgp_show(vty, bgp, afi, safi, - bgp_show_type_dampend_paths, NULL, uj); + bgp_show_type_dampend_paths, NULL, uj, + wide); else if (argv_find(argv, argc, "flap-statistics", &idx)) return bgp_show(vty, bgp, afi, safi, - bgp_show_type_flap_statistics, NULL, - uj); + bgp_show_type_flap_statistics, NULL, uj, + wide); } if (argv_find(argv, argc, "community", &idx)) { @@ -9847,17 +11125,16 @@ DEFUN (show_ip_bgp_json, exact_match, afi, safi, uj); else return (bgp_show(vty, bgp, afi, safi, - bgp_show_type_community_all, NULL, - uj)); + bgp_show_type_community_all, NULL, uj, + wide)); } - return bgp_show(vty, bgp, afi, safi, sh_type, NULL, uj); + return bgp_show(vty, bgp, afi, safi, sh_type, NULL, uj, wide); } DEFUN (show_ip_bgp_route, show_ip_bgp_route_cmd, - "show [ip] bgp [ VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]]" - " [] [json]", + "show [ip] bgp [ VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] [] [json]", SHOW_STR IP_STR BGP_STR @@ -9931,7 +11208,7 @@ DEFUN (show_ip_bgp_route, DEFUN (show_ip_bgp_regexp, show_ip_bgp_regexp_cmd, - "show [ip] bgp [ VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] regexp REGEX...", + "show [ip] bgp [ VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] regexp REGEX [json]", SHOW_STR IP_STR BGP_STR @@ -9939,11 +11216,14 @@ DEFUN (show_ip_bgp_regexp, BGP_AFI_HELP_STR BGP_SAFI_WITH_LABEL_HELP_STR "Display routes matching the AS path regular expression\n" - "A regular-expression (1234567890_^|[,{}() ]$*+.?-\\) to match the BGP AS paths\n") + "A regular-expression (1234567890_^|[,{}() ]$*+.?-\\) to match the BGP AS paths\n" + JSON_STR) { afi_t afi = AFI_IP6; safi_t safi = SAFI_UNICAST; struct bgp *bgp = NULL; + bool uj = use_json(argc, argv); + char *regstr = NULL; int idx = 0; bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi, @@ -9952,32 +11232,30 @@ DEFUN (show_ip_bgp_regexp, return CMD_WARNING; // get index of regex - argv_find(argv, argc, "regexp", &idx); - idx++; + if (argv_find(argv, argc, "REGEX", &idx)) + regstr = argv[idx]->arg; - char *regstr = argv_concat(argv, argc, idx); - int rc = bgp_show_regexp(vty, bgp, (const char *)regstr, afi, safi, - bgp_show_type_regexp); - XFREE(MTYPE_TMP, regstr); - return rc; + assert(regstr); + return bgp_show_regexp(vty, bgp, (const char *)regstr, afi, safi, + bgp_show_type_regexp, uj); } -DEFUN (show_ip_bgp_instance_all, +DEFPY (show_ip_bgp_instance_all, show_ip_bgp_instance_all_cmd, - "show [ip] bgp all ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] [json]", + "show [ip] bgp all ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] [json$uj | wide$wide]", SHOW_STR IP_STR BGP_STR BGP_INSTANCE_ALL_HELP_STR BGP_AFI_HELP_STR BGP_SAFI_WITH_LABEL_HELP_STR - JSON_STR) + JSON_STR + "Increase table width for longer prefixes\n") { afi_t afi = AFI_IP; safi_t safi = SAFI_UNICAST; struct bgp *bgp = NULL; int idx = 0; - bool uj = use_json(argc, argv); if (uj) argc--; @@ -9987,18 +11265,19 @@ DEFUN (show_ip_bgp_instance_all, if (!idx) return CMD_WARNING; - bgp_show_all_instances_routes_vty(vty, afi, safi, uj); + bgp_show_all_instances_routes_vty(vty, afi, safi, uj, wide); return CMD_SUCCESS; } static int bgp_show_regexp(struct vty *vty, struct bgp *bgp, const char *regstr, - afi_t afi, safi_t safi, enum bgp_show_type type) + afi_t afi, safi_t safi, enum bgp_show_type type, + bool use_json) { regex_t *regex; int rc; if (!config_bgp_aspath_validate(regstr)) { - vty_out(vty, "Invalid character in as-path access-list %s\n", + vty_out(vty, "Invalid character in REGEX %s\n", regstr); return CMD_WARNING_CONFIG_FAILED; } @@ -10009,7 +11288,7 @@ static int bgp_show_regexp(struct vty *vty, struct bgp *bgp, const char *regstr, return CMD_WARNING; } - rc = bgp_show(vty, bgp, afi, safi, type, regex, 0); + rc = bgp_show(vty, bgp, afi, safi, type, regex, use_json, false); bgp_regex_free(regex); return rc; } @@ -10027,7 +11306,7 @@ static int bgp_show_prefix_list(struct vty *vty, struct bgp *bgp, return CMD_WARNING; } - return bgp_show(vty, bgp, afi, safi, type, plist, 0); + return bgp_show(vty, bgp, afi, safi, type, plist, 0, false); } static int bgp_show_filter_list(struct vty *vty, struct bgp *bgp, @@ -10043,7 +11322,7 @@ static int bgp_show_filter_list(struct vty *vty, struct bgp *bgp, return CMD_WARNING; } - return bgp_show(vty, bgp, afi, safi, type, as_list, 0); + return bgp_show(vty, bgp, afi, safi, type, as_list, 0, false); } static int bgp_show_route_map(struct vty *vty, struct bgp *bgp, @@ -10058,7 +11337,7 @@ static int bgp_show_route_map(struct vty *vty, struct bgp *bgp, return CMD_WARNING; } - return bgp_show(vty, bgp, afi, safi, type, rmap, 0); + return bgp_show(vty, bgp, afi, safi, type, rmap, 0, false); } static int bgp_show_community(struct vty *vty, struct bgp *bgp, @@ -10077,7 +11356,7 @@ static int bgp_show_community(struct vty *vty, struct bgp *bgp, ret = bgp_show(vty, bgp, afi, safi, (exact ? bgp_show_type_community_exact : bgp_show_type_community), - com, use_json); + com, use_json, false); community_free(&com); return ret; @@ -10098,7 +11377,7 @@ static int bgp_show_community_list(struct vty *vty, struct bgp *bgp, return bgp_show(vty, bgp, afi, safi, (exact ? bgp_show_type_community_list_exact : bgp_show_type_community_list), - list, 0); + list, 0, false); } static int bgp_show_prefix_longer(struct vty *vty, struct bgp *bgp, @@ -10110,72 +11389,15 @@ static int bgp_show_prefix_longer(struct vty *vty, struct bgp *bgp, p = prefix_new(); - ret = str2prefix(prefix, p); - if (!ret) { - vty_out(vty, "%% Malformed Prefix\n"); - return CMD_WARNING; - } - - ret = bgp_show(vty, bgp, afi, safi, type, p, 0); - prefix_free(p); - return ret; -} - -static struct peer *peer_lookup_in_view(struct vty *vty, struct bgp *bgp, - const char *ip_str, bool use_json) -{ - int ret; - struct peer *peer; - union sockunion su; - - /* Get peer sockunion. */ - ret = str2sockunion(ip_str, &su); - if (ret < 0) { - peer = peer_lookup_by_conf_if(bgp, ip_str); - if (!peer) { - peer = peer_lookup_by_hostname(bgp, ip_str); - - if (!peer) { - if (use_json) { - json_object *json_no = NULL; - json_no = json_object_new_object(); - json_object_string_add( - json_no, - "malformedAddressOrName", - ip_str); - vty_out(vty, "%s\n", - json_object_to_json_string_ext( - json_no, - JSON_C_TO_STRING_PRETTY)); - json_object_free(json_no); - } else - vty_out(vty, - "%% Malformed address or name: %s\n", - ip_str); - return NULL; - } - } - return peer; - } - - /* Peer structure lookup. */ - peer = peer_lookup(bgp, &su); - if (!peer) { - if (use_json) { - json_object *json_no = NULL; - json_no = json_object_new_object(); - json_object_string_add(json_no, "warning", - "No such neighbor in this view/vrf"); - vty_out(vty, "%s\n", - json_object_to_json_string_ext( - json_no, JSON_C_TO_STRING_PRETTY)); - json_object_free(json_no); - } else - vty_out(vty, "No such neighbor in this view/vrf\n"); - return NULL; + ret = str2prefix(prefix, p); + if (!ret) { + vty_out(vty, "%% Malformed Prefix\n"); + return CMD_WARNING; } - return peer; + ret = bgp_show(vty, bgp, afi, safi, type, p, 0, false); + prefix_free(&p); + return ret; } enum bgp_stats { @@ -10196,22 +11418,33 @@ enum bgp_stats { BGP_STATS_MAX, }; -static const char *table_stats_strs[] = { - [BGP_STATS_PREFIXES] = "Total Prefixes", - [BGP_STATS_TOTPLEN] = "Average prefix length", - [BGP_STATS_RIB] = "Total Advertisements", - [BGP_STATS_UNAGGREGATEABLE] = "Unaggregateable prefixes", - [BGP_STATS_MAX_AGGREGATEABLE] = - "Maximum aggregateable prefixes", - [BGP_STATS_AGGREGATES] = "BGP Aggregate advertisements", - [BGP_STATS_SPACE] = "Address space advertised", - [BGP_STATS_ASPATH_COUNT] = "Advertisements with paths", - [BGP_STATS_ASPATH_MAXHOPS] = "Longest AS-Path (hops)", - [BGP_STATS_ASPATH_MAXSIZE] = "Largest AS-Path (bytes)", - [BGP_STATS_ASPATH_TOTHOPS] = "Average AS-Path length (hops)", - [BGP_STATS_ASPATH_TOTSIZE] = "Average AS-Path size (bytes)", - [BGP_STATS_ASN_HIGHEST] = "Highest public ASN", - [BGP_STATS_MAX] = NULL, +#define TABLE_STATS_IDX_VTY 0 +#define TABLE_STATS_IDX_JSON 1 + +static const char *table_stats_strs[][2] = { + [BGP_STATS_PREFIXES] = {"Total Prefixes", "totalPrefixes"}, + [BGP_STATS_TOTPLEN] = {"Average prefix length", "averagePrefixLength"}, + [BGP_STATS_RIB] = {"Total Advertisements", "totalAdvertisements"}, + [BGP_STATS_UNAGGREGATEABLE] = {"Unaggregateable prefixes", + "unaggregateablePrefixes"}, + [BGP_STATS_MAX_AGGREGATEABLE] = {"Maximum aggregateable prefixes", + "maximumAggregateablePrefixes"}, + [BGP_STATS_AGGREGATES] = {"BGP Aggregate advertisements", + "bgpAggregateAdvertisements"}, + [BGP_STATS_SPACE] = {"Address space advertised", + "addressSpaceAdvertised"}, + [BGP_STATS_ASPATH_COUNT] = {"Advertisements with paths", + "advertisementsWithPaths"}, + [BGP_STATS_ASPATH_MAXHOPS] = {"Longest AS-Path (hops)", + "longestAsPath"}, + [BGP_STATS_ASPATH_MAXSIZE] = {"Largest AS-Path (bytes)", + "largestAsPath"}, + [BGP_STATS_ASPATH_TOTHOPS] = {"Average AS-Path length (hops)", + "averageAsPathLengthHops"}, + [BGP_STATS_ASPATH_TOTSIZE] = {"Average AS-Path size (bytes)", + "averageAsPathSizeBytes"}, + [BGP_STATS_ASN_HIGHEST] = {"Highest public ASN", "highestPublicAsn"}, + [BGP_STATS_MAX] = {NULL, NULL} }; struct bgp_table_stats { @@ -10236,51 +11469,49 @@ ravg_tally (unsigned long count, unsigned long oldavg, unsigned long newval) } #endif -static void bgp_table_stats_rn(struct bgp_node *rn, struct bgp_node *top, +static void bgp_table_stats_rn(struct bgp_dest *dest, struct bgp_dest *top, struct bgp_table_stats *ts, unsigned int space) { - struct bgp_node *prn = bgp_node_parent_nolock(rn); + struct bgp_dest *pdest = bgp_dest_parent_nolock(dest); struct bgp_path_info *pi; + const struct prefix *rn_p; - if (rn == top) - return; - - if (!bgp_node_has_bgp_path_info_data(rn)) + if (!bgp_dest_has_bgp_path_info_data(dest)) return; + rn_p = bgp_dest_get_prefix(dest); ts->counts[BGP_STATS_PREFIXES]++; - ts->counts[BGP_STATS_TOTPLEN] += rn->p.prefixlen; + ts->counts[BGP_STATS_TOTPLEN] += rn_p->prefixlen; #if 0 ts->counts[BGP_STATS_AVGPLEN] = ravg_tally (ts->counts[BGP_STATS_PREFIXES], ts->counts[BGP_STATS_AVGPLEN], - rn->p.prefixlen); + rn_p->prefixlen); #endif /* check if the prefix is included by any other announcements */ - while (prn && !bgp_node_has_bgp_path_info_data(prn)) - prn = bgp_node_parent_nolock(prn); + while (pdest && !bgp_dest_has_bgp_path_info_data(pdest)) + pdest = bgp_dest_parent_nolock(pdest); - if (prn == NULL || prn == top) { + if (pdest == NULL || pdest == top) { ts->counts[BGP_STATS_UNAGGREGATEABLE]++; /* announced address space */ if (space) - ts->total_space += pow(2.0, space - rn->p.prefixlen); - } else if (bgp_node_has_bgp_path_info_data(prn)) + ts->total_space += pow(2.0, space - rn_p->prefixlen); + } else if (bgp_dest_has_bgp_path_info_data(pdest)) ts->counts[BGP_STATS_MAX_AGGREGATEABLE]++; - for (pi = bgp_node_get_bgp_path_info(rn); pi; pi = pi->next) { + for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) { ts->counts[BGP_STATS_RIB]++; - if (pi->attr - && (CHECK_FLAG(pi->attr->flag, - ATTR_FLAG_BIT(BGP_ATTR_ATOMIC_AGGREGATE)))) + if (CHECK_FLAG(pi->attr->flag, + ATTR_FLAG_BIT(BGP_ATTR_ATOMIC_AGGREGATE))) ts->counts[BGP_STATS_AGGREGATES]++; /* as-path stats */ - if (pi->attr && pi->attr->aspath) { + if (pi->attr->aspath) { unsigned int hops = aspath_count_hops(pi->attr->aspath); unsigned int size = aspath_size(pi->attr->aspath); as_t highest = aspath_highest(pi->attr->aspath); @@ -10313,8 +11544,8 @@ static void bgp_table_stats_rn(struct bgp_node *rn, struct bgp_node *top, static int bgp_table_stats_walker(struct thread *t) { - struct bgp_node *rn, *nrn; - struct bgp_node *top; + struct bgp_dest *dest, *ndest; + struct bgp_dest *top; struct bgp_table_stats *ts = THREAD_ARG(t); unsigned int space = 0; @@ -10328,45 +11559,80 @@ static int bgp_table_stats_walker(struct thread *t) case AFI_IP6: space = IPV6_MAX_BITLEN; break; + case AFI_L2VPN: + space = EVPN_ROUTE_PREFIXLEN; + break; default: return 0; } ts->counts[BGP_STATS_MAXBITLEN] = space; - for (rn = top; rn; rn = bgp_route_next(rn)) { - if (ts->table->safi == SAFI_MPLS_VPN) { + for (dest = top; dest; dest = bgp_route_next(dest)) { + if (ts->table->safi == SAFI_MPLS_VPN + || ts->table->safi == SAFI_ENCAP + || ts->table->safi == SAFI_EVPN) { struct bgp_table *table; - table = bgp_node_get_bgp_table_info(rn); + table = bgp_dest_get_bgp_table_info(dest); if (!table) continue; top = bgp_table_top(table); - for (nrn = bgp_table_top(table); nrn; - nrn = bgp_route_next(nrn)) - bgp_table_stats_rn(nrn, top, ts, space); + for (ndest = bgp_table_top(table); ndest; + ndest = bgp_route_next(ndest)) + bgp_table_stats_rn(ndest, top, ts, space); } else { - bgp_table_stats_rn(rn, top, ts, space); + bgp_table_stats_rn(dest, top, ts, space); } } return 0; } -static int bgp_table_stats(struct vty *vty, struct bgp *bgp, afi_t afi, - safi_t safi) +static void bgp_table_stats_all(struct vty *vty, afi_t afi, safi_t safi, + struct json_object *json_array) +{ + struct listnode *node, *nnode; + struct bgp *bgp; + + for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) + bgp_table_stats_single(vty, bgp, afi, safi, json_array); +} + +static int bgp_table_stats_single(struct vty *vty, struct bgp *bgp, afi_t afi, + safi_t safi, struct json_object *json_array) { struct bgp_table_stats ts; unsigned int i; + int ret = CMD_SUCCESS; + char temp_buf[20]; + struct json_object *json = NULL; + + if (json_array) + json = json_object_new_object(); if (!bgp->rib[afi][safi]) { - vty_out(vty, "%% No RIB exist's for the AFI(%d)/SAFI(%d)\n", - afi, safi); - return CMD_WARNING; + char warning_msg[50]; + + snprintf(warning_msg, sizeof(warning_msg), + "%% No RIB exist's for the AFI(%d)/SAFI(%d)", afi, + safi); + + if (!json) + vty_out(vty, "%s\n", warning_msg); + else + json_object_string_add(json, "warning", warning_msg); + + ret = CMD_WARNING; + goto end_table_stats; } - vty_out(vty, "BGP %s RIB statistics\n", afi_safi_print(afi, safi)); + if (!json) + vty_out(vty, "BGP %s RIB statistics (%s)\n", + get_afi_safi_str(afi, safi, false), bgp->name_pretty); + else + json_object_string_add(json, "instance", bgp->name_pretty); /* labeled-unicast routes live in the unicast table */ if (safi == SAFI_LABELED_UNICAST) @@ -10377,7 +11643,8 @@ static int bgp_table_stats(struct vty *vty, struct bgp *bgp, afi_t afi, thread_execute(bm->master, bgp_table_stats_walker, &ts, 0); for (i = 0; i < BGP_STATS_MAX; i++) { - if (!table_stats_strs[i]) + if ((!json && !table_stats_strs[i][TABLE_STATS_IDX_VTY]) + || (json && !table_stats_strs[i][TABLE_STATS_IDX_JSON])) continue; switch (i) { @@ -10392,54 +11659,177 @@ static int bgp_table_stats(struct vty *vty, struct bgp *bgp, afi_t afi, #endif case BGP_STATS_ASPATH_TOTHOPS: case BGP_STATS_ASPATH_TOTSIZE: - vty_out(vty, "%-30s: ", table_stats_strs[i]); - vty_out(vty, "%12.2f", - ts.counts[i] - ? (float)ts.counts[i] - / (float)ts.counts + if (!json) { + snprintf( + temp_buf, sizeof(temp_buf), "%12.2f", + ts.counts[i] + ? (float)ts.counts[i] + / (float)ts.counts + [BGP_STATS_ASPATH_COUNT] + : 0); + vty_out(vty, "%-30s: %s", + table_stats_strs[i] + [TABLE_STATS_IDX_VTY], + temp_buf); + } else { + json_object_double_add( + json, + table_stats_strs[i] + [TABLE_STATS_IDX_JSON], + ts.counts[i] + ? (double)ts.counts[i] + / (double)ts.counts [BGP_STATS_ASPATH_COUNT] - : 0); + : 0); + } break; case BGP_STATS_TOTPLEN: - vty_out(vty, "%-30s: ", table_stats_strs[i]); - vty_out(vty, "%12.2f", - ts.counts[i] - ? (float)ts.counts[i] - / (float)ts.counts + if (!json) { + snprintf( + temp_buf, sizeof(temp_buf), "%12.2f", + ts.counts[i] + ? (float)ts.counts[i] + / (float)ts.counts + [BGP_STATS_PREFIXES] + : 0); + vty_out(vty, "%-30s: %s", + table_stats_strs[i] + [TABLE_STATS_IDX_VTY], + temp_buf); + } else { + json_object_double_add( + json, + table_stats_strs[i] + [TABLE_STATS_IDX_JSON], + ts.counts[i] + ? (double)ts.counts[i] + / (double)ts.counts [BGP_STATS_PREFIXES] - : 0); + : 0); + } break; case BGP_STATS_SPACE: - vty_out(vty, "%-30s: ", table_stats_strs[i]); - vty_out(vty, "%12g\n", ts.total_space); - + if (!json) { + snprintf(temp_buf, sizeof(temp_buf), "%12g", + ts.total_space); + vty_out(vty, "%-30s: %s\n", + table_stats_strs[i] + [TABLE_STATS_IDX_VTY], + temp_buf); + } else { + json_object_double_add( + json, + table_stats_strs[i] + [TABLE_STATS_IDX_JSON], + (double)ts.total_space); + } if (afi == AFI_IP6) { - vty_out(vty, "%30s: ", "/32 equivalent "); - vty_out(vty, "%12g\n", - ts.total_space * pow(2.0, -128 + 32)); - vty_out(vty, "%30s: ", "/48 equivalent "); - vty_out(vty, "%12g\n", - ts.total_space * pow(2.0, -128 + 48)); + if (!json) { + snprintf(temp_buf, sizeof(temp_buf), + "%12g", + ts.total_space + * pow(2.0, -128 + 32)); + vty_out(vty, "%30s: %s\n", + "/32 equivalent %s\n", + temp_buf); + } else { + json_object_double_add( + json, "/32equivalent", + (double)(ts.total_space + * pow(2.0, + -128 + 32))); + } + if (!json) { + snprintf(temp_buf, sizeof(temp_buf), + "%12g", + ts.total_space + * pow(2.0, -128 + 48)); + vty_out(vty, "%30s: %s\n", + "/48 equivalent %s\n", + temp_buf); + } else { + json_object_double_add( + json, "/48equivalent", + (double)(ts.total_space + * pow(2.0, + -128 + 48))); + } } else { - vty_out(vty, "%30s: ", "% announced "); - vty_out(vty, "%12.2f\n", - ts.total_space * 100. * pow(2.0, -32)); - vty_out(vty, "%30s: ", "/8 equivalent "); - vty_out(vty, "%12.2f\n", - ts.total_space * pow(2.0, -32 + 8)); - vty_out(vty, "%30s: ", "/24 equivalent "); - vty_out(vty, "%12.2f\n", - ts.total_space * pow(2.0, -32 + 24)); + if (!json) { + snprintf(temp_buf, sizeof(temp_buf), + "%12.2f", + ts.total_space * 100. + * pow(2.0, -32)); + vty_out(vty, "%30s: %s\n", + "% announced ", temp_buf); + } else { + json_object_double_add( + json, "%announced", + (double)(ts.total_space * 100. + * pow(2.0, -32))); + } + if (!json) { + snprintf(temp_buf, sizeof(temp_buf), + "%12.2f", + ts.total_space + * pow(2.0, -32 + 8)); + vty_out(vty, "%30s: %s\n", + "/8 equivalent ", temp_buf); + } else { + json_object_double_add( + json, "/8equivalent", + (double)(ts.total_space + * pow(2.0, -32 + 8))); + } + if (!json) { + snprintf(temp_buf, sizeof(temp_buf), + "%12.2f", + ts.total_space + * pow(2.0, -32 + 24)); + vty_out(vty, "%30s: %s\n", + "/24 equivalent ", temp_buf); + } else { + json_object_double_add( + json, "/24equivalent", + (double)(ts.total_space + * pow(2.0, -32 + 24))); + } } break; default: - vty_out(vty, "%-30s: ", table_stats_strs[i]); - vty_out(vty, "%12llu", ts.counts[i]); + if (!json) { + snprintf(temp_buf, sizeof(temp_buf), "%12llu", + ts.counts[i]); + vty_out(vty, "%-30s: %s", + table_stats_strs[i] + [TABLE_STATS_IDX_VTY], + temp_buf); + } else { + json_object_int_add( + json, + table_stats_strs[i] + [TABLE_STATS_IDX_JSON], + ts.counts[i]); + } } + if (!json) + vty_out(vty, "\n"); + } +end_table_stats: + if (json) + json_object_array_add(json_array, json); + return ret; +} - vty_out(vty, "\n"); +static int bgp_table_stats(struct vty *vty, struct bgp *bgp, afi_t afi, + safi_t safi, struct json_object *json_array) +{ + if (!bgp) { + bgp_table_stats_all(vty, afi, safi, json_array); + return CMD_SUCCESS; } - return CMD_SUCCESS; + + return bgp_table_stats_single(vty, bgp, afi, safi, json_array); } enum bgp_pcounts { @@ -10451,11 +11841,12 @@ enum bgp_pcounts { PCOUNT_VALID, PCOUNT_ALL, PCOUNT_COUNTED, + PCOUNT_BPATH_SELECTED, PCOUNT_PFCNT, /* the figure we display to users */ PCOUNT_MAX, }; -static const char *pcount_strs[] = { +static const char *const pcount_strs[] = { [PCOUNT_ADJ_IN] = "Adj-in", [PCOUNT_DAMPED] = "Damped", [PCOUNT_REMOVED] = "Removed", @@ -10464,6 +11855,7 @@ static const char *pcount_strs[] = { [PCOUNT_VALID] = "Valid", [PCOUNT_ALL] = "All RIB", [PCOUNT_COUNTED] = "PfxCt counted", + [PCOUNT_BPATH_SELECTED] = "PfxCt Best Selected", [PCOUNT_PFCNT] = "Useable", [PCOUNT_MAX] = NULL, }; @@ -10472,56 +11864,77 @@ struct peer_pcounts { unsigned int count[PCOUNT_MAX]; const struct peer *peer; const struct bgp_table *table; + safi_t safi; }; -static int bgp_peer_count_walker(struct thread *t) +static void bgp_peer_count_proc(struct bgp_dest *rn, struct peer_pcounts *pc) { - struct bgp_node *rn; - struct peer_pcounts *pc = THREAD_ARG(t); + const struct bgp_adj_in *ain; + const struct bgp_path_info *pi; const struct peer *peer = pc->peer; - for (rn = bgp_table_top(pc->table); rn; rn = bgp_route_next(rn)) { - struct bgp_adj_in *ain; - struct bgp_path_info *pi; + for (ain = rn->adj_in; ain; ain = ain->next) + if (ain->peer == peer) + pc->count[PCOUNT_ADJ_IN]++; - for (ain = rn->adj_in; ain; ain = ain->next) - if (ain->peer == peer) - pc->count[PCOUNT_ADJ_IN]++; + for (pi = bgp_dest_get_bgp_path_info(rn); pi; pi = pi->next) { - for (pi = bgp_node_get_bgp_path_info(rn); pi; pi = pi->next) { + if (pi->peer != peer) + continue; - if (pi->peer != peer) - continue; + pc->count[PCOUNT_ALL]++; - pc->count[PCOUNT_ALL]++; + if (CHECK_FLAG(pi->flags, BGP_PATH_DAMPED)) + pc->count[PCOUNT_DAMPED]++; + if (CHECK_FLAG(pi->flags, BGP_PATH_HISTORY)) + pc->count[PCOUNT_HISTORY]++; + if (CHECK_FLAG(pi->flags, BGP_PATH_REMOVED)) + pc->count[PCOUNT_REMOVED]++; + if (CHECK_FLAG(pi->flags, BGP_PATH_STALE)) + pc->count[PCOUNT_STALE]++; + if (CHECK_FLAG(pi->flags, BGP_PATH_VALID)) + pc->count[PCOUNT_VALID]++; + if (!CHECK_FLAG(pi->flags, BGP_PATH_UNUSEABLE)) + pc->count[PCOUNT_PFCNT]++; + if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED)) + pc->count[PCOUNT_BPATH_SELECTED]++; - if (CHECK_FLAG(pi->flags, BGP_PATH_DAMPED)) - pc->count[PCOUNT_DAMPED]++; - if (CHECK_FLAG(pi->flags, BGP_PATH_HISTORY)) - pc->count[PCOUNT_HISTORY]++; - if (CHECK_FLAG(pi->flags, BGP_PATH_REMOVED)) - pc->count[PCOUNT_REMOVED]++; - if (CHECK_FLAG(pi->flags, BGP_PATH_STALE)) - pc->count[PCOUNT_STALE]++; - if (CHECK_FLAG(pi->flags, BGP_PATH_VALID)) - pc->count[PCOUNT_VALID]++; + if (CHECK_FLAG(pi->flags, BGP_PATH_COUNTED)) { + pc->count[PCOUNT_COUNTED]++; + if (CHECK_FLAG(pi->flags, BGP_PATH_UNUSEABLE)) + flog_err( + EC_LIB_DEVELOPMENT, + "Attempting to count but flags say it is unusable"); + } else { if (!CHECK_FLAG(pi->flags, BGP_PATH_UNUSEABLE)) - pc->count[PCOUNT_PFCNT]++; - - if (CHECK_FLAG(pi->flags, BGP_PATH_COUNTED)) { - pc->count[PCOUNT_COUNTED]++; - if (CHECK_FLAG(pi->flags, BGP_PATH_UNUSEABLE)) - flog_err( - EC_LIB_DEVELOPMENT, - "Attempting to count but flags say it is unusable"); - } else { - if (!CHECK_FLAG(pi->flags, BGP_PATH_UNUSEABLE)) - flog_err( - EC_LIB_DEVELOPMENT, - "Not counted but flags say we should"); - } + flog_err( + EC_LIB_DEVELOPMENT, + "Not counted but flags say we should"); } } +} + +static int bgp_peer_count_walker(struct thread *t) +{ + struct bgp_dest *rn, *rm; + const struct bgp_table *table; + struct peer_pcounts *pc = THREAD_ARG(t); + + if (pc->safi == SAFI_MPLS_VPN || pc->safi == SAFI_ENCAP + || pc->safi == SAFI_EVPN) { + /* Special handling for 2-level routing tables. */ + for (rn = bgp_table_top(pc->table); rn; + rn = bgp_route_next(rn)) { + table = bgp_dest_get_bgp_table_info(rn); + if (table != NULL) + for (rm = bgp_table_top(table); rm; + rm = bgp_route_next(rm)) + bgp_peer_count_proc(rm, pc); + } + } else + for (rn = bgp_table_top(pc->table); rn; rn = bgp_route_next(rn)) + bgp_peer_count_proc(rn, pc); + return 0; } @@ -10546,6 +11959,7 @@ static int bgp_peer_counts(struct vty *vty, struct peer *peer, afi_t afi, "No such neighbor or address family"); vty_out(vty, "%s\n", json_object_to_json_string(json)); json_object_free(json); + json_object_free(json_loop); } else vty_out(vty, "%% No such neighbor or address family\n"); @@ -10555,6 +11969,7 @@ static int bgp_peer_counts(struct vty *vty, struct peer *peer, afi_t afi, memset(&pcounts, 0, sizeof(pcounts)); pcounts.peer = peer; pcounts.table = peer->bgp->rib[afi][safi]; + pcounts.safi = safi; /* in-place call via thread subsystem so as to record execution time * stats for the thread-walk (i.e. ensure this can't be blamed on @@ -10565,7 +11980,7 @@ static int bgp_peer_counts(struct vty *vty, struct peer *peer, afi_t afi, if (use_json) { json_object_string_add(json, "prefixCountsFor", peer->host); json_object_string_add(json, "multiProtocol", - afi_safi_print(afi, safi)); + get_afi_safi_str(afi, safi, true)); json_object_int_add(json, "pfxCounter", peer->pcount[afi][safi]); @@ -10588,16 +12003,16 @@ static int bgp_peer_counts(struct vty *vty, struct peer *peer, afi_t afi, } else { if (peer->hostname - && bgp_flag_check(peer->bgp, BGP_FLAG_SHOW_HOSTNAME)) { + && CHECK_FLAG(peer->bgp->flags, BGP_FLAG_SHOW_HOSTNAME)) { vty_out(vty, "Prefix counts for %s/%s, %s\n", peer->hostname, peer->host, - afi_safi_print(afi, safi)); + get_afi_safi_str(afi, safi, false)); } else { vty_out(vty, "Prefix counts for %s, %s\n", peer->host, - afi_safi_print(afi, safi)); + get_afi_safi_str(afi, safi, false)); } - vty_out(vty, "PfxCt: %ld\n", peer->pcount[afi][safi]); + vty_out(vty, "PfxCt: %u\n", peer->pcount[afi][safi]); vty_out(vty, "\nCounts from RIB table walk:\n\n"); for (i = 0; i < PCOUNT_MAX; i++) @@ -10616,8 +12031,7 @@ static int bgp_peer_counts(struct vty *vty, struct peer *peer, afi_t afi, DEFUN (show_ip_bgp_instance_neighbor_prefix_counts, show_ip_bgp_instance_neighbor_prefix_counts_cmd, - "show [ip] bgp [ VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_CMD_STR"]] " - "neighbors prefix-counts [json]", + "show [ip] bgp [ VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_CMD_STR"]] neighbors prefix-counts [json]", SHOW_STR IP_STR BGP_STR @@ -10651,7 +12065,7 @@ DEFUN (show_ip_bgp_instance_neighbor_prefix_counts, if (!peer) return CMD_WARNING; - return bgp_peer_counts(vty, peer, AFI_IP, SAFI_UNICAST, uj); + return bgp_peer_counts(vty, peer, afi, safi, uj); } #ifdef KEEP_OLD_VPN_COMMANDS @@ -10715,45 +12129,96 @@ DEFUN (show_ip_bgp_vpn_all_route_prefix, } #endif /* KEEP_OLD_VPN_COMMANDS */ -DEFUN (show_ip_bgp_l2vpn_evpn_all_route_prefix, - show_ip_bgp_l2vpn_evpn_all_route_prefix_cmd, - "show [ip] bgp l2vpn evpn all [json]", +DEFUN (show_bgp_l2vpn_evpn_route_prefix, + show_bgp_l2vpn_evpn_route_prefix_cmd, + "show bgp l2vpn evpn [json]", SHOW_STR - IP_STR BGP_STR L2VPN_HELP_STR EVPN_HELP_STR - "Display information about all EVPN NLRIs\n" + "Network in the BGP routing table to display\n" + "Network in the BGP routing table to display\n" "Network in the BGP routing table to display\n" "Network in the BGP routing table to display\n" JSON_STR) { int idx = 0; char *network = NULL; + int prefix_check = 0; - if (argv_find(argv, argc, "A.B.C.D", &idx)) + if (argv_find(argv, argc, "A.B.C.D", &idx) || + argv_find(argv, argc, "X:X::X:X", &idx)) network = argv[idx]->arg; - else if (argv_find(argv, argc, "A.B.C.D/M", &idx)) + else if (argv_find(argv, argc, "A.B.C.D/M", &idx) || + argv_find(argv, argc, "X:X::X:X/M", &idx)) { network = argv[idx]->arg; - else { + prefix_check = 1; + } else { vty_out(vty, "Unable to figure out Network\n"); return CMD_WARNING; } - return bgp_show_route(vty, NULL, network, AFI_L2VPN, SAFI_EVPN, NULL, 0, - BGP_PATH_SHOW_ALL, use_json(argc, argv)); + return bgp_show_route(vty, NULL, network, AFI_L2VPN, SAFI_EVPN, NULL, + prefix_check, BGP_PATH_SHOW_ALL, + use_json(argc, argv)); +} + +static void show_adj_route_header(struct vty *vty, struct bgp *bgp, + struct bgp_table *table, int *header1, + int *header2, json_object *json, + json_object *json_scode, + json_object *json_ocode, bool wide) +{ + uint64_t version = table ? table->version : 0; + + if (*header1) { + if (json) { + json_object_int_add(json, "bgpTableVersion", version); + json_object_string_add(json, "bgpLocalRouterId", + inet_ntoa(bgp->router_id)); + json_object_int_add(json, "defaultLocPrf", + bgp->default_local_pref); + json_object_int_add(json, "localAS", bgp->as); + json_object_object_add(json, "bgpStatusCodes", + json_scode); + json_object_object_add(json, "bgpOriginCodes", + json_ocode); + } else { + vty_out(vty, + "BGP table version is %" PRIu64 ", local router ID is %s, vrf id ", + version, inet_ntoa(bgp->router_id)); + if (bgp->vrf_id == VRF_UNKNOWN) + vty_out(vty, "%s", VRFID_NONE_STR); + else + vty_out(vty, "%u", bgp->vrf_id); + vty_out(vty, "\n"); + vty_out(vty, "Default local pref %u, ", + bgp->default_local_pref); + vty_out(vty, "local AS %u\n", bgp->as); + vty_out(vty, BGP_SHOW_SCODE_HEADER); + vty_out(vty, BGP_SHOW_NCODE_HEADER); + vty_out(vty, BGP_SHOW_OCODE_HEADER); + } + *header1 = 0; + } + if (*header2) { + if (!json) + vty_out(vty, (wide ? BGP_SHOW_HEADER_WIDE + : BGP_SHOW_HEADER)); + *header2 = 0; + } } static void show_adj_route(struct vty *vty, struct peer *peer, afi_t afi, safi_t safi, enum bgp_show_adj_route_type type, const char *rmap_name, bool use_json, - json_object *json) + json_object *json, bool wide) { struct bgp_table *table; struct bgp_adj_in *ain; struct bgp_adj_out *adj; - unsigned long output_count; - unsigned long filtered_count; - struct bgp_node *rn; + unsigned long output_count = 0; + unsigned long filtered_count = 0; + struct bgp_dest *dest; int header1 = 1; struct bgp *bgp; int header2 = 1; @@ -10826,8 +12291,7 @@ static void show_adj_route(struct vty *vty, struct peer *peer, afi_t afi, json, "bgpOriginatingDefaultNetwork", (afi == AFI_IP) ? "0.0.0.0/0" : "::/0"); } else { - vty_out(vty, "BGP table version is %" PRIu64 - ", local router ID is %s, vrf id ", + vty_out(vty, "BGP table version is %" PRIu64", local router ID is %s, vrf id ", table->version, inet_ntoa(bgp->router_id)); if (bgp->vrf_id == VRF_UNKNOWN) vty_out(vty, "%s", VRFID_NONE_STR); @@ -10847,79 +12311,34 @@ static void show_adj_route(struct vty *vty, struct peer *peer, afi_t afi, header1 = 0; } - for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) { + for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) { if (type == bgp_show_adj_route_received || type == bgp_show_adj_route_filtered) { - for (ain = rn->adj_in; ain; ain = ain->next) { - if (ain->peer != peer || !ain->attr) + for (ain = dest->adj_in; ain; ain = ain->next) { + if (ain->peer != peer) continue; - if (header1) { - if (use_json) { - json_object_int_add( - json, "bgpTableVersion", - 0); - json_object_string_add( - json, - "bgpLocalRouterId", - inet_ntoa( - bgp->router_id)); - json_object_int_add(json, - "defaultLocPrf", - bgp->default_local_pref); - json_object_int_add(json, - "localAS", bgp->as); - json_object_object_add( - json, "bgpStatusCodes", - json_scode); - json_object_object_add( - json, "bgpOriginCodes", - json_ocode); - } else { - vty_out(vty, - "BGP table version is 0, local router ID is %s, vrf id ", - inet_ntoa( - bgp->router_id)); - if (bgp->vrf_id == VRF_UNKNOWN) - vty_out(vty, "%s", - VRFID_NONE_STR); - else - vty_out(vty, "%u", - bgp->vrf_id); - vty_out(vty, "\n"); - vty_out(vty, - "Default local pref %u, ", - bgp->default_local_pref); - vty_out(vty, "local AS %u\n", - bgp->as); - vty_out(vty, - BGP_SHOW_SCODE_HEADER); - vty_out(vty, - BGP_SHOW_NCODE_HEADER); - vty_out(vty, - BGP_SHOW_OCODE_HEADER); - } - header1 = 0; - } - if (header2) { - if (!use_json) - vty_out(vty, BGP_SHOW_HEADER); - header2 = 0; - } + show_adj_route_header( + vty, bgp, table, &header1, &header2, + json, json_scode, json_ocode, wide); - bgp_attr_dup(&attr, ain->attr); + attr = *ain->attr; route_filtered = false; /* Filter prefix using distribute list, * filter list or prefix list */ - if ((bgp_input_filter(peer, &rn->p, &attr, afi, - safi)) == FILTER_DENY) + const struct prefix *rn_p = + bgp_dest_get_prefix(dest); + if ((bgp_input_filter(peer, rn_p, &attr, afi, + safi)) + == FILTER_DENY) route_filtered = true; /* Filter prefix using route-map */ - ret = bgp_input_modifier(peer, &rn->p, &attr, - afi, safi, rmap_name); + ret = bgp_input_modifier(peer, rn_p, &attr, afi, + safi, rmap_name, NULL, + 0, NULL); if (type == bgp_show_adj_route_filtered && !route_filtered && ret != RMAP_DENY) { @@ -10931,93 +12350,35 @@ static void show_adj_route(struct vty *vty, struct peer *peer, afi_t afi, (route_filtered || ret == RMAP_DENY)) filtered_count++; - route_vty_out_tmp(vty, &rn->p, &attr, safi, - use_json, json_ar); + route_vty_out_tmp(vty, rn_p, &attr, safi, + use_json, json_ar, wide); bgp_attr_undup(&attr, ain->attr); output_count++; } } else if (type == bgp_show_adj_route_advertised) { - RB_FOREACH (adj, bgp_adj_out_rb, &rn->adj_out) + RB_FOREACH (adj, bgp_adj_out_rb, &dest->adj_out) SUBGRP_FOREACH_PEER (adj->subgroup, paf) { if (paf->peer != peer || !adj->attr) continue; - if (header1) { - if (use_json) { - json_object_int_add( - json, - "bgpTableVersion", - table->version); - json_object_string_add( - json, - "bgpLocalRouterId", - inet_ntoa( - bgp->router_id)); - json_object_int_add( - json, "defaultLocPrf", - bgp->default_local_pref - ); - json_object_int_add( - json, "localAS", - bgp->as); - json_object_object_add( - json, - "bgpStatusCodes", - json_scode); - json_object_object_add( - json, - "bgpOriginCodes", - json_ocode); - } else { - vty_out(vty, - "BGP table version is %" PRIu64 - ", local router ID is %s, vrf id ", - table->version, - inet_ntoa( - bgp->router_id)); - if (bgp->vrf_id == - VRF_UNKNOWN) - vty_out(vty, - "%s", - VRFID_NONE_STR); - else - vty_out(vty, - "%u", - bgp->vrf_id); - vty_out(vty, "\n"); - vty_out(vty, - "Default local pref %u, ", - bgp->default_local_pref - ); - vty_out(vty, - "local AS %u\n", - bgp->as); - vty_out(vty, - BGP_SHOW_SCODE_HEADER); - vty_out(vty, - BGP_SHOW_NCODE_HEADER); - vty_out(vty, - BGP_SHOW_OCODE_HEADER); - } - header1 = 0; - } - if (header2) { - if (!use_json) - vty_out(vty, - BGP_SHOW_HEADER); - header2 = 0; - } + show_adj_route_header( + vty, bgp, table, &header1, + &header2, json, json_scode, + json_ocode, wide); - bgp_attr_dup(&attr, adj->attr); + const struct prefix *rn_p = + bgp_dest_get_prefix(dest); + + attr = *adj->attr; ret = bgp_output_modifier( - peer, &rn->p, &attr, afi, safi, + peer, rn_p, &attr, afi, safi, rmap_name); if (ret != RMAP_DENY) { - route_vty_out_tmp(vty, &rn->p, - &attr, safi, - use_json, - json_ar); + route_vty_out_tmp( + vty, rn_p, &attr, safi, + use_json, json_ar, + wide); output_count++; } else { filtered_count++; @@ -11025,6 +12386,27 @@ static void show_adj_route(struct vty *vty, struct peer *peer, afi_t afi, bgp_attr_undup(&attr, adj->attr); } + } else if (type == bgp_show_adj_route_bestpath) { + struct bgp_path_info *pi; + + show_adj_route_header(vty, bgp, table, &header1, + &header2, json, json_scode, + json_ocode, wide); + + for (pi = bgp_dest_get_bgp_path_info(dest); pi; + pi = pi->next) { + if (pi->peer != peer) + continue; + + if (!CHECK_FLAG(pi->flags, BGP_PATH_SELECTED)) + continue; + + route_vty_out_tmp(vty, + bgp_dest_get_prefix(dest), + pi->attr, safi, use_json, + json_ar, wide); + output_count++; + } } } @@ -11036,6 +12418,12 @@ static void show_adj_route(struct vty *vty, struct peer *peer, afi_t afi, vty_out(vty, "%s\n", json_object_to_json_string_ext( json, JSON_C_TO_STRING_PRETTY)); + + if (!output_count && !filtered_count) { + json_object_free(json_scode); + json_object_free(json_ocode); + } + json_object_free(json); } else if (output_count > 0) { if (filtered_count > 0) @@ -11050,7 +12438,7 @@ static void show_adj_route(struct vty *vty, struct peer *peer, afi_t afi, static int peer_adj_routes(struct vty *vty, struct peer *peer, afi_t afi, safi_t safi, enum bgp_show_adj_route_type type, - const char *rmap_name, bool use_json) + const char *rmap_name, bool use_json, bool wide) { json_object *json = NULL; @@ -11087,15 +12475,57 @@ static int peer_adj_routes(struct vty *vty, struct peer *peer, afi_t afi, return CMD_WARNING; } - show_adj_route(vty, peer, afi, safi, type, rmap_name, use_json, json); + show_adj_route(vty, peer, afi, safi, type, rmap_name, use_json, json, + wide); return CMD_SUCCESS; } -DEFUN (show_ip_bgp_instance_neighbor_advertised_route, +DEFPY (show_ip_bgp_instance_neighbor_bestpath_route, + show_ip_bgp_instance_neighbor_bestpath_route_cmd, + "show [ip] bgp [ VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] neighbors bestpath-routes [json$uj | wide$wide]", + SHOW_STR + IP_STR + BGP_STR + BGP_INSTANCE_HELP_STR + BGP_AFI_HELP_STR + BGP_SAFI_WITH_LABEL_HELP_STR + "Detailed information on TCP and BGP neighbor connections\n" + "Neighbor to display information about\n" + "Neighbor to display information about\n" + "Neighbor on BGP configured interface\n" + "Display the routes selected by best path\n" + JSON_STR + "Increase table width for longer prefixes\n") +{ + afi_t afi = AFI_IP6; + safi_t safi = SAFI_UNICAST; + char *rmap_name = NULL; + char *peerstr = NULL; + struct bgp *bgp = NULL; + struct peer *peer; + enum bgp_show_adj_route_type type = bgp_show_adj_route_bestpath; + int idx = 0; + + bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi, + &bgp, uj); + + if (!idx) + return CMD_WARNING; + + argv_find(argv, argc, "neighbors", &idx); + peerstr = argv[++idx]->arg; + + peer = peer_lookup_in_view(vty, bgp, peerstr, uj); + if (!peer) + return CMD_WARNING; + + return peer_adj_routes(vty, peer, afi, safi, type, rmap_name, uj, wide); +} + +DEFPY (show_ip_bgp_instance_neighbor_advertised_route, show_ip_bgp_instance_neighbor_advertised_route_cmd, - "show [ip] bgp [ VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] " - "neighbors [route-map WORD] [json]", + "show [ip] bgp [ VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] neighbors [route-map WORD] [json$uj | wide$wide]", SHOW_STR IP_STR BGP_STR @@ -11111,7 +12541,8 @@ DEFUN (show_ip_bgp_instance_neighbor_advertised_route, "Display the filtered routes received from neighbor\n" "Route-map to modify the attributes\n" "Name of the route map\n" - JSON_STR) + JSON_STR + "Increase table width for longer prefixes\n") { afi_t afi = AFI_IP6; safi_t safi = SAFI_UNICAST; @@ -11121,7 +12552,6 @@ DEFUN (show_ip_bgp_instance_neighbor_advertised_route, struct peer *peer; enum bgp_show_adj_route_type type = bgp_show_adj_route_advertised; int idx = 0; - bool uj = use_json(argc, argv); if (uj) argc--; @@ -11149,7 +12579,7 @@ DEFUN (show_ip_bgp_instance_neighbor_advertised_route, if (argv_find(argv, argc, "route-map", &idx)) rmap_name = argv[++idx]->arg; - return peer_adj_routes(vty, peer, afi, safi, type, rmap_name, uj); + return peer_adj_routes(vty, peer, afi, safi, type, rmap_name, uj, wide); } DEFUN (show_ip_bgp_neighbor_received_prefix_filter, @@ -11217,12 +12647,12 @@ DEFUN (show_ip_bgp_neighbor_received_prefix_filter, } } - sprintf(name, "%s.%d.%d", peer->host, afi, safi); + snprintf(name, sizeof(name), "%s.%d.%d", peer->host, afi, safi); count = prefix_bgp_show_prefix_list(NULL, afi, name, uj); if (count) { if (!uj) vty_out(vty, "Address Family: %s\n", - afi_safi_print(afi, safi)); + get_afi_safi_str(afi, safi, false)); prefix_bgp_show_prefix_list(vty, afi, name, uj); } else { if (uj) @@ -11238,10 +12668,6 @@ static int bgp_show_neighbor_route(struct vty *vty, struct peer *peer, afi_t afi, safi_t safi, enum bgp_show_type type, bool use_json) { - /* labeled-unicast routes live in the unicast table */ - if (safi == SAFI_LABELED_UNICAST) - safi = SAFI_UNICAST; - if (!peer || !peer->afc[afi][safi]) { if (use_json) { json_object *json_no = NULL; @@ -11257,7 +12683,12 @@ static int bgp_show_neighbor_route(struct vty *vty, struct peer *peer, return CMD_WARNING; } - return bgp_show(vty, peer->bgp, afi, safi, type, &peer->su, use_json); + /* labeled-unicast routes live in the unicast table */ + if (safi == SAFI_LABELED_UNICAST) + safi = SAFI_UNICAST; + + return bgp_show(vty, peer->bgp, afi, safi, type, &peer->su, use_json, + false); } DEFUN (show_ip_bgp_flowspec_routes_detailed, @@ -11286,13 +12717,13 @@ DEFUN (show_ip_bgp_flowspec_routes_detailed, if (!idx) return CMD_WARNING; - return bgp_show(vty, bgp, afi, safi, bgp_show_type_detail, NULL, uj); + return bgp_show(vty, bgp, afi, safi, bgp_show_type_detail, NULL, uj, + false); } DEFUN (show_ip_bgp_neighbor_routes, show_ip_bgp_neighbor_routes_cmd, - "show [ip] bgp [ VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] " - "neighbors [json]", + "show [ip] bgp [ VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] neighbors [json]", SHOW_STR IP_STR BGP_STR @@ -11404,7 +12835,7 @@ static int bgp_distance_set(struct vty *vty, const char *distance_str, safi_t safi; struct prefix p; uint8_t distance; - struct bgp_node *rn; + struct bgp_dest *dest; struct bgp_distance *bdistance; afi = bgp_node_afi(vty); @@ -11419,23 +12850,20 @@ static int bgp_distance_set(struct vty *vty, const char *distance_str, distance = atoi(distance_str); /* Get BGP distance node. */ - rn = bgp_node_get(bgp_distance_table[afi][safi], (struct prefix *)&p); - bdistance = bgp_node_get_bgp_distance_info(rn); + dest = bgp_node_get(bgp_distance_table[afi][safi], &p); + bdistance = bgp_dest_get_bgp_distance_info(dest); if (bdistance) - bgp_unlock_node(rn); + bgp_dest_unlock_node(dest); else { bdistance = bgp_distance_new(); - bgp_node_set_bgp_distance_info(rn, bdistance); + bgp_dest_set_bgp_distance_info(dest, bdistance); } /* Set distance value. */ bdistance->distance = distance; /* Reset access-list configuration. */ - if (bdistance->access_list) { - XFREE(MTYPE_AS_LIST, bdistance->access_list); - bdistance->access_list = NULL; - } + XFREE(MTYPE_AS_LIST, bdistance->access_list); if (access_list_str) bdistance->access_list = XSTRDUP(MTYPE_AS_LIST, access_list_str); @@ -11451,7 +12879,7 @@ static int bgp_distance_unset(struct vty *vty, const char *distance_str, safi_t safi; struct prefix p; int distance; - struct bgp_node *rn; + struct bgp_dest *dest; struct bgp_distance *bdistance; afi = bgp_node_afi(vty); @@ -11463,14 +12891,13 @@ static int bgp_distance_unset(struct vty *vty, const char *distance_str, return CMD_WARNING_CONFIG_FAILED; } - rn = bgp_node_lookup(bgp_distance_table[afi][safi], - (struct prefix *)&p); - if (!rn) { + dest = bgp_node_lookup(bgp_distance_table[afi][safi], &p); + if (!dest) { vty_out(vty, "Can't find specified prefix\n"); return CMD_WARNING_CONFIG_FAILED; } - bdistance = bgp_node_get_bgp_distance_info(rn); + bdistance = bgp_dest_get_bgp_distance_info(dest); distance = atoi(distance_str); if (bdistance->distance != distance) { @@ -11481,18 +12908,18 @@ static int bgp_distance_unset(struct vty *vty, const char *distance_str, XFREE(MTYPE_AS_LIST, bdistance->access_list); bgp_distance_free(bdistance); - bgp_node_set_bgp_path_info(rn, NULL); - bgp_unlock_node(rn); - bgp_unlock_node(rn); + bgp_dest_set_bgp_path_info(dest, NULL); + bgp_dest_unlock_node(dest); + bgp_dest_unlock_node(dest); return CMD_SUCCESS; } /* Apply BGP information to distance method. */ -uint8_t bgp_distance_apply(struct prefix *p, struct bgp_path_info *pinfo, +uint8_t bgp_distance_apply(const struct prefix *p, struct bgp_path_info *pinfo, afi_t afi, safi_t safi, struct bgp *bgp) { - struct bgp_node *rn; + struct bgp_dest *dest; struct prefix q; struct peer *peer; struct bgp_distance *bdistance; @@ -11504,12 +12931,15 @@ uint8_t bgp_distance_apply(struct prefix *p, struct bgp_path_info *pinfo, peer = pinfo->peer; + if (pinfo->attr->distance) + return pinfo->attr->distance; + /* Check source address. */ sockunion2hostprefix(&peer->su, &q); - rn = bgp_node_match(bgp_distance_table[afi][safi], &q); - if (rn) { - bdistance = bgp_node_get_bgp_distance_info(rn); - bgp_unlock_node(rn); + dest = bgp_node_match(bgp_distance_table[afi][safi], &q); + if (dest) { + bdistance = bgp_dest_get_bgp_distance_info(dest); + bgp_dest_unlock_node(dest); if (bdistance->access_list) { alist = access_list_lookup(afi, bdistance->access_list); @@ -11521,10 +12951,10 @@ uint8_t bgp_distance_apply(struct prefix *p, struct bgp_path_info *pinfo, } /* Backdoor check. */ - rn = bgp_node_lookup(bgp->route[afi][safi], p); - if (rn) { - bgp_static = bgp_node_get_bgp_static_info(rn); - bgp_unlock_node(rn); + dest = bgp_node_lookup(bgp->route[afi][safi], p); + if (dest) { + bgp_static = bgp_dest_get_bgp_static_info(dest); + bgp_dest_unlock_node(dest); if (bgp_static->backdoor) { if (bgp->distance_local[afi][safi]) @@ -11538,10 +12968,40 @@ uint8_t bgp_distance_apply(struct prefix *p, struct bgp_path_info *pinfo, if (bgp->distance_ebgp[afi][safi]) return bgp->distance_ebgp[afi][safi]; return ZEBRA_EBGP_DISTANCE_DEFAULT; - } else { + } else if (peer->sort == BGP_PEER_IBGP) { if (bgp->distance_ibgp[afi][safi]) return bgp->distance_ibgp[afi][safi]; return ZEBRA_IBGP_DISTANCE_DEFAULT; + } else { + if (bgp->distance_local[afi][safi]) + return bgp->distance_local[afi][safi]; + return ZEBRA_IBGP_DISTANCE_DEFAULT; + } +} + +/* If we enter `distance bgp (1-255) (1-255) (1-255)`, + * we should tell ZEBRA update the routes for a specific + * AFI/SAFI to reflect changes in RIB. + */ +static void bgp_announce_routes_distance_update(struct bgp *bgp, + afi_t update_afi, + safi_t update_safi) +{ + afi_t afi; + safi_t safi; + + FOREACH_AFI_SAFI (afi, safi) { + if (!bgp_fibupd_safi(safi)) + continue; + + if (afi != update_afi && safi != update_safi) + continue; + + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug( + "%s: Announcing routes due to distance change afi/safi (%d/%d)", + __func__, afi, safi); + bgp_zebra_announce_table(bgp, afi, safi); } } @@ -11558,15 +13018,23 @@ DEFUN (bgp_distance, int idx_number = 2; int idx_number_2 = 3; int idx_number_3 = 4; + int distance_ebgp = atoi(argv[idx_number]->arg); + int distance_ibgp = atoi(argv[idx_number_2]->arg); + int distance_local = atoi(argv[idx_number_3]->arg); afi_t afi; safi_t safi; afi = bgp_node_afi(vty); safi = bgp_node_safi(vty); - bgp->distance_ebgp[afi][safi] = atoi(argv[idx_number]->arg); - bgp->distance_ibgp[afi][safi] = atoi(argv[idx_number_2]->arg); - bgp->distance_local[afi][safi] = atoi(argv[idx_number_3]->arg); + if (bgp->distance_ebgp[afi][safi] != distance_ebgp + || bgp->distance_ibgp[afi][safi] != distance_ibgp + || bgp->distance_local[afi][safi] != distance_local) { + bgp->distance_ebgp[afi][safi] = distance_ebgp; + bgp->distance_ibgp[afi][safi] = distance_ibgp; + bgp->distance_local[afi][safi] = distance_local; + bgp_announce_routes_distance_update(bgp, afi, safi); + } return CMD_SUCCESS; } @@ -11587,9 +13055,14 @@ DEFUN (no_bgp_distance, afi = bgp_node_afi(vty); safi = bgp_node_safi(vty); - bgp->distance_ebgp[afi][safi] = 0; - bgp->distance_ibgp[afi][safi] = 0; - bgp->distance_local[afi][safi] = 0; + if (bgp->distance_ebgp[afi][safi] != 0 + || bgp->distance_ibgp[afi][safi] != 0 + || bgp->distance_local[afi][safi] != 0) { + bgp->distance_ebgp[afi][safi] = 0; + bgp->distance_ibgp[afi][safi] = 0; + bgp->distance_local[afi][safi] = 0; + bgp_announce_routes_distance_update(bgp, afi, safi); + } return CMD_SUCCESS; } @@ -11734,6 +13207,12 @@ DEFUN (bgp_damp_set, max = 4 * half; } + /* + * These can't be 0 but our SA doesn't understand the + * way our cli is constructed + */ + assert(reuse); + assert(half); if (suppress < reuse) { vty_out(vty, "Suppress value cannot be less than reuse value \n"); @@ -11766,8 +13245,8 @@ static int bgp_clear_damp_route(struct vty *vty, const char *view_name, { int ret; struct prefix match; - struct bgp_node *rn; - struct bgp_node *rm; + struct bgp_dest *dest; + struct bgp_dest *rm; struct bgp_path_info *pi; struct bgp_path_info *pi_temp; struct bgp *bgp; @@ -11800,52 +13279,58 @@ static int bgp_clear_damp_route(struct vty *vty, const char *view_name, if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP) || (safi == SAFI_EVPN)) { - for (rn = bgp_table_top(bgp->rib[AFI_IP][safi]); rn; - rn = bgp_route_next(rn)) { - if (prd && memcmp(rn->p.u.val, prd->val, 8) != 0) + for (dest = bgp_table_top(bgp->rib[AFI_IP][safi]); dest; + dest = bgp_route_next(dest)) { + const struct prefix *dest_p = bgp_dest_get_prefix(dest); + + if (prd && memcmp(dest_p->u.val, prd->val, 8) != 0) continue; - table = bgp_node_get_bgp_table_info(rn); + table = bgp_dest_get_bgp_table_info(dest); if (!table) continue; if ((rm = bgp_node_match(table, &match)) == NULL) continue; + const struct prefix *rm_p = bgp_dest_get_prefix(dest); + if (!prefix_check - || rm->p.prefixlen == match.prefixlen) { - pi = bgp_node_get_bgp_path_info(rm); + || rm_p->prefixlen == match.prefixlen) { + pi = bgp_dest_get_bgp_path_info(rm); while (pi) { if (pi->extra && pi->extra->damp_info) { pi_temp = pi->next; bgp_damp_info_free( pi->extra->damp_info, - 1); + 1, afi, safi); pi = pi_temp; } else pi = pi->next; } } - bgp_unlock_node(rm); + bgp_dest_unlock_node(rm); } } else { - if ((rn = bgp_node_match(bgp->rib[afi][safi], &match)) + if ((dest = bgp_node_match(bgp->rib[afi][safi], &match)) != NULL) { + const struct prefix *dest_p = bgp_dest_get_prefix(dest); + if (!prefix_check - || rn->p.prefixlen == match.prefixlen) { - pi = bgp_node_get_bgp_path_info(rn); + || dest_p->prefixlen == match.prefixlen) { + pi = bgp_dest_get_bgp_path_info(dest); while (pi) { if (pi->extra && pi->extra->damp_info) { pi_temp = pi->next; bgp_damp_info_free( pi->extra->damp_info, - 1); + 1, afi, safi); pi = pi_temp; } else pi = pi->next; } } - bgp_unlock_node(rn); + bgp_dest_unlock_node(dest); } } @@ -11860,7 +13345,7 @@ DEFUN (clear_ip_bgp_dampening, BGP_STR "Clear route flap dampening information\n") { - bgp_damp_info_clean(); + bgp_damp_info_clean(AFI_IP, SAFI_UNICAST); return CMD_SUCCESS; } @@ -11928,6 +13413,18 @@ static void show_bgp_peerhash_entry(struct hash_bucket *bucket, void *arg) sockunion2str(&peer->su, buf, sizeof(buf))); } +DEFUN (show_bgp_listeners, + show_bgp_listeners_cmd, + "show bgp listeners", + SHOW_STR + BGP_STR + "Display Listen Sockets and who created them\n") +{ + bgp_dump_listener_info(vty); + + return CMD_SUCCESS; +} + DEFUN (show_bgp_peerhash, show_bgp_peerhash_cmd, "show bgp peerhash", @@ -11952,30 +13449,32 @@ DEFUN (show_bgp_peerhash, static void bgp_config_write_network_vpn(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi) { - struct bgp_node *prn; - struct bgp_node *rn; + struct bgp_dest *pdest; + struct bgp_dest *dest; struct bgp_table *table; - struct prefix *p; - struct prefix_rd *prd; + const struct prefix *p; + const struct prefix_rd *prd; struct bgp_static *bgp_static; mpls_label_t label; char buf[SU_ADDRSTRLEN]; char rdbuf[RD_ADDRSTRLEN]; /* Network configuration. */ - for (prn = bgp_table_top(bgp->route[afi][safi]); prn; - prn = bgp_route_next(prn)) { - table = bgp_node_get_bgp_table_info(prn); + for (pdest = bgp_table_top(bgp->route[afi][safi]); pdest; + pdest = bgp_route_next(pdest)) { + table = bgp_dest_get_bgp_table_info(pdest); if (!table) continue; - for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) { - bgp_static = bgp_node_get_bgp_static_info(rn); + for (dest = bgp_table_top(table); dest; + dest = bgp_route_next(dest)) { + bgp_static = bgp_dest_get_bgp_static_info(dest); if (bgp_static == NULL) continue; - p = &rn->p; - prd = (struct prefix_rd *)&prn->p; + p = bgp_dest_get_prefix(dest); + prd = (const struct prefix_rd *)bgp_dest_get_prefix( + pdest); /* "network" configuration display. */ prefix_rd2str(prd, rdbuf, sizeof(rdbuf)); @@ -12003,38 +13502,40 @@ static void bgp_config_write_network_vpn(struct vty *vty, struct bgp *bgp, static void bgp_config_write_network_evpn(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi) { - struct bgp_node *prn; - struct bgp_node *rn; + struct bgp_dest *pdest; + struct bgp_dest *dest; struct bgp_table *table; - struct prefix *p; - struct prefix_rd *prd; + const struct prefix *p; + const struct prefix_rd *prd; struct bgp_static *bgp_static; char buf[PREFIX_STRLEN * 2]; char buf2[SU_ADDRSTRLEN]; char rdbuf[RD_ADDRSTRLEN]; + char esi_buf[ESI_BYTES]; /* Network configuration. */ - for (prn = bgp_table_top(bgp->route[afi][safi]); prn; - prn = bgp_route_next(prn)) { - table = bgp_node_get_bgp_table_info(prn); + for (pdest = bgp_table_top(bgp->route[afi][safi]); pdest; + pdest = bgp_route_next(pdest)) { + table = bgp_dest_get_bgp_table_info(pdest); if (!table) continue; - for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) { - bgp_static = bgp_node_get_bgp_static_info(rn); + for (dest = bgp_table_top(table); dest; + dest = bgp_route_next(dest)) { + bgp_static = bgp_dest_get_bgp_static_info(dest); if (bgp_static == NULL) continue; char *macrouter = NULL; - char *esi = NULL; if (bgp_static->router_mac) macrouter = prefix_mac2str( bgp_static->router_mac, NULL, 0); if (bgp_static->eth_s_id) - esi = esi2str(bgp_static->eth_s_id); - p = &rn->p; - prd = (struct prefix_rd *)&prn->p; + esi_to_str(bgp_static->eth_s_id, + esi_buf, sizeof(esi_buf)); + p = bgp_dest_get_prefix(dest); + prd = (struct prefix_rd *)bgp_dest_get_prefix(pdest); /* "network" configuration display. */ prefix_rd2str(prd, rdbuf, sizeof(rdbuf)); @@ -12047,8 +13548,9 @@ static void bgp_config_write_network_evpn(struct vty *vty, struct bgp *bgp, inet_ntop(family, &p->u.prefix_evpn.prefix_addr.ip.ip.addr, local_buf, PREFIX_STRLEN); - sprintf(buf, "%s/%u", local_buf, - p->u.prefix_evpn.prefix_addr.ip_prefix_length); + snprintf(buf, sizeof(buf), "%s/%u", local_buf, + p->u.prefix_evpn.prefix_addr + .ip_prefix_length); } else { prefix2str(p, buf, sizeof(buf)); } @@ -12062,11 +13564,10 @@ static void bgp_config_write_network_evpn(struct vty *vty, struct bgp *bgp, " network %s rd %s ethtag %u label %u esi %s gwip %s routermac %s\n", buf, rdbuf, p->u.prefix_evpn.prefix_addr.eth_tag, - decode_label(&bgp_static->label), esi, buf2, + decode_label(&bgp_static->label), esi_buf, buf2, macrouter); XFREE(MTYPE_TMP, macrouter); - XFREE(MTYPE_TMP, esi); } } } @@ -12076,8 +13577,8 @@ static void bgp_config_write_network_evpn(struct vty *vty, struct bgp *bgp, void bgp_config_write_network(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi) { - struct bgp_node *rn; - struct prefix *p; + struct bgp_dest *dest; + const struct prefix *p; struct bgp_static *bgp_static; struct bgp_aggregate *bgp_aggregate; char buf[SU_ADDRSTRLEN]; @@ -12093,38 +13594,17 @@ void bgp_config_write_network(struct vty *vty, struct bgp *bgp, afi_t afi, } /* Network configuration. */ - for (rn = bgp_table_top(bgp->route[afi][safi]); rn; - rn = bgp_route_next(rn)) { - bgp_static = bgp_node_get_bgp_static_info(rn); + for (dest = bgp_table_top(bgp->route[afi][safi]); dest; + dest = bgp_route_next(dest)) { + bgp_static = bgp_dest_get_bgp_static_info(dest); if (bgp_static == NULL) continue; - p = &rn->p; - - /* "network" configuration display. */ - if (bgp_option_check(BGP_OPT_CONFIG_CISCO) && afi == AFI_IP) { - uint32_t destination; - struct in_addr netmask; + p = bgp_dest_get_prefix(dest); - destination = ntohl(p->u.prefix4.s_addr); - masklen2ip(p->prefixlen, &netmask); - vty_out(vty, " network %s", - inet_ntop(p->family, &p->u.prefix, buf, - SU_ADDRSTRLEN)); - - if ((IN_CLASSC(destination) && p->prefixlen == 24) - || (IN_CLASSB(destination) && p->prefixlen == 16) - || (IN_CLASSA(destination) && p->prefixlen == 8) - || p->u.prefix4.s_addr == 0) { - /* Natural mask is not display. */ - } else - vty_out(vty, " mask %s", inet_ntoa(netmask)); - } else { - vty_out(vty, " network %s/%d", - inet_ntop(p->family, &p->u.prefix, buf, - SU_ADDRSTRLEN), - p->prefixlen); - } + vty_out(vty, " network %s/%d", + inet_ntop(p->family, &p->u.prefix, buf, SU_ADDRSTRLEN), + p->prefixlen); if (bgp_static->label_index != BGP_INVALID_LABEL_INDEX) vty_out(vty, " label-index %u", @@ -12140,28 +13620,17 @@ void bgp_config_write_network(struct vty *vty, struct bgp *bgp, afi_t afi, } /* Aggregate-address configuration. */ - for (rn = bgp_table_top(bgp->aggregate[afi][safi]); rn; - rn = bgp_route_next(rn)) { - bgp_aggregate = bgp_node_get_bgp_aggregate_info(rn); + for (dest = bgp_table_top(bgp->aggregate[afi][safi]); dest; + dest = bgp_route_next(dest)) { + bgp_aggregate = bgp_dest_get_bgp_aggregate_info(dest); if (bgp_aggregate == NULL) continue; - p = &rn->p; + p = bgp_dest_get_prefix(dest); - if (bgp_option_check(BGP_OPT_CONFIG_CISCO) && afi == AFI_IP) { - struct in_addr netmask; - - masklen2ip(p->prefixlen, &netmask); - vty_out(vty, " aggregate-address %s %s", - inet_ntop(p->family, &p->u.prefix, buf, - SU_ADDRSTRLEN), - inet_ntoa(netmask)); - } else { - vty_out(vty, " aggregate-address %s/%d", - inet_ntop(p->family, &p->u.prefix, buf, - SU_ADDRSTRLEN), - p->prefixlen); - } + vty_out(vty, " aggregate-address %s/%d", + inet_ntop(p->family, &p->u.prefix, buf, SU_ADDRSTRLEN), + p->prefixlen); if (bgp_aggregate->as_set) vty_out(vty, " as-set"); @@ -12169,6 +13638,13 @@ void bgp_config_write_network(struct vty *vty, struct bgp *bgp, afi_t afi, if (bgp_aggregate->summary_only) vty_out(vty, " summary-only"); + if (bgp_aggregate->rmap.name) + vty_out(vty, " route-map %s", bgp_aggregate->rmap.name); + + if (bgp_aggregate->origin != BGP_ORIGIN_UNSPECIFIED) + vty_out(vty, " origin %s", + bgp_origin2str(bgp_aggregate->origin)); + vty_out(vty, "\n"); } } @@ -12176,7 +13652,7 @@ void bgp_config_write_network(struct vty *vty, struct bgp *bgp, afi_t afi, void bgp_config_write_distance(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi) { - struct bgp_node *rn; + struct bgp_dest *dest; struct bgp_distance *bdistance; /* Distance configuration. */ @@ -12192,18 +13668,14 @@ void bgp_config_write_distance(struct vty *vty, struct bgp *bgp, afi_t afi, bgp->distance_local[afi][safi]); } - for (rn = bgp_table_top(bgp_distance_table[afi][safi]); rn; - rn = bgp_route_next(rn)) { - bdistance = bgp_node_get_bgp_distance_info(rn); - if (bdistance != NULL) { - char buf[PREFIX_STRLEN]; - - vty_out(vty, " distance %d %s %s\n", - bdistance->distance, - prefix2str(&rn->p, buf, sizeof(buf)), + for (dest = bgp_table_top(bgp_distance_table[afi][safi]); dest; + dest = bgp_route_next(dest)) { + bdistance = bgp_dest_get_bgp_distance_info(dest); + if (bdistance != NULL) + vty_out(vty, " distance %d %pRN %s\n", + bdistance->distance, dest, bdistance->access_list ? bdistance->access_list : ""); - } } } @@ -12247,14 +13719,25 @@ void bgp_route_init(void) install_element(BGP_IPV4M_NODE, &no_aggregate_address_mask_cmd); /* IPv4 labeled-unicast configuration. */ + install_element(BGP_IPV4L_NODE, &bgp_network_cmd); + install_element(BGP_IPV4L_NODE, &aggregate_address_cmd); + install_element(BGP_IPV4L_NODE, &aggregate_address_mask_cmd); + install_element(BGP_IPV4L_NODE, &no_aggregate_address_cmd); + install_element(BGP_IPV4L_NODE, &no_aggregate_address_mask_cmd); + install_element(VIEW_NODE, &show_ip_bgp_instance_all_cmd); install_element(VIEW_NODE, &show_ip_bgp_cmd); + install_element(VIEW_NODE, &show_ip_bgp_afi_safi_statistics_cmd); + install_element(VIEW_NODE, &show_ip_bgp_l2vpn_evpn_statistics_cmd); install_element(VIEW_NODE, &show_ip_bgp_json_cmd); install_element(VIEW_NODE, &show_ip_bgp_route_cmd); install_element(VIEW_NODE, &show_ip_bgp_regexp_cmd); + install_element(VIEW_NODE, &show_ip_bgp_statistics_all_cmd); install_element(VIEW_NODE, &show_ip_bgp_instance_neighbor_advertised_route_cmd); + install_element(VIEW_NODE, + &show_ip_bgp_instance_neighbor_bestpath_route_cmd); install_element(VIEW_NODE, &show_ip_bgp_neighbor_routes_cmd); install_element(VIEW_NODE, &show_ip_bgp_neighbor_received_prefix_filter_cmd); @@ -12263,7 +13746,7 @@ void bgp_route_init(void) #endif /* KEEP_OLD_VPN_COMMANDS */ install_element(VIEW_NODE, &show_bgp_afi_vpn_rd_route_cmd); install_element(VIEW_NODE, - &show_ip_bgp_l2vpn_evpn_all_route_prefix_cmd); + &show_bgp_l2vpn_evpn_route_prefix_cmd); /* BGP dampening clear commands */ install_element(ENABLE_NODE, &clear_ip_bgp_dampening_cmd); @@ -12290,6 +13773,11 @@ void bgp_route_init(void) install_element(BGP_IPV6M_NODE, &ipv6_bgp_network_cmd); + /* IPv6 labeled unicast address family. */ + install_element(BGP_IPV6L_NODE, &ipv6_bgp_network_cmd); + install_element(BGP_IPV6L_NODE, &ipv6_aggregate_address_cmd); + install_element(BGP_IPV6L_NODE, &no_ipv6_aggregate_address_cmd); + install_element(BGP_NODE, &bgp_distance_cmd); install_element(BGP_NODE, &no_bgp_distance_cmd); install_element(BGP_NODE, &bgp_distance_source_cmd); @@ -12326,14 +13814,21 @@ void bgp_route_init(void) install_element(BGP_IPV6M_NODE, &no_ipv6_bgp_distance_source_access_list_cmd); + /* BGP dampening */ install_element(BGP_NODE, &bgp_damp_set_cmd); install_element(BGP_NODE, &bgp_damp_unset_cmd); install_element(BGP_IPV4_NODE, &bgp_damp_set_cmd); install_element(BGP_IPV4_NODE, &bgp_damp_unset_cmd); - - /* IPv4 Multicast Mode */ install_element(BGP_IPV4M_NODE, &bgp_damp_set_cmd); install_element(BGP_IPV4M_NODE, &bgp_damp_unset_cmd); + install_element(BGP_IPV4L_NODE, &bgp_damp_set_cmd); + install_element(BGP_IPV4L_NODE, &bgp_damp_unset_cmd); + install_element(BGP_IPV6_NODE, &bgp_damp_set_cmd); + install_element(BGP_IPV6_NODE, &bgp_damp_unset_cmd); + install_element(BGP_IPV6M_NODE, &bgp_damp_set_cmd); + install_element(BGP_IPV6M_NODE, &bgp_damp_unset_cmd); + install_element(BGP_IPV6L_NODE, &bgp_damp_set_cmd); + install_element(BGP_IPV6L_NODE, &bgp_damp_unset_cmd); /* Large Communities */ install_element(VIEW_NODE, &show_ip_bgp_large_community_list_cmd); @@ -12342,6 +13837,7 @@ void bgp_route_init(void) /* show bgp ipv4 flowspec detailed */ install_element(VIEW_NODE, &show_ip_bgp_flowspec_routes_detailed_cmd); + install_element(VIEW_NODE, &show_bgp_listeners_cmd); install_element(VIEW_NODE, &show_bgp_peerhash_cmd); } diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h index 7bbc14b46f..9b711a1444 100644 --- a/bgpd/bgp_route.h +++ b/bgpd/bgp_route.h @@ -21,6 +21,9 @@ #ifndef _QUAGGA_BGP_ROUTE_H #define _QUAGGA_BGP_ROUTE_H +#include + +#include "hook.h" #include "queue.h" #include "nexthop.h" #include "bgp_table.h" @@ -45,7 +48,9 @@ enum bgp_show_type { bgp_show_type_community_list_exact, bgp_show_type_lcommunity_all, bgp_show_type_lcommunity, + bgp_show_type_lcommunity_exact, bgp_show_type_lcommunity_list, + bgp_show_type_lcommunity_list_exact, bgp_show_type_flap_statistics, bgp_show_type_flap_neighbor, bgp_show_type_dampend_paths, @@ -57,6 +62,7 @@ enum bgp_show_adj_route_type { bgp_show_adj_route_advertised, bgp_show_adj_route_received, bgp_show_adj_route_filtered, + bgp_show_adj_route_bestpath, }; @@ -67,12 +73,16 @@ enum bgp_show_adj_route_type { #define BGP_SHOW_OCODE_HEADER "Origin codes: i - IGP, e - EGP, ? - incomplete\n\n" #define BGP_SHOW_NCODE_HEADER "Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self\n" #define BGP_SHOW_HEADER " Network Next Hop Metric LocPrf Weight Path\n" +#define BGP_SHOW_HEADER_WIDE " Network Next Hop Metric LocPrf Weight Path\n" /* Maximum number of labels we can process or send with a prefix. We * really do only 1 for MPLS (BGP-LU) but we can do 2 for EVPN-VxLAN. */ #define BGP_MAX_LABELS 2 +/* Maximum number of sids we can process or send with a prefix. */ +#define BGP_MAX_SIDS 6 + /* Error codes for handling NLRI */ #define BGP_NLRI_PARSE_OK 0 #define BGP_NLRI_PARSE_ERROR_PREFIX_OVERFLOW -1 @@ -89,6 +99,7 @@ enum bgp_show_adj_route_type { #define BGP_NLRI_PARSE_ERROR_FLOWSPEC_NLRI_SIZELIMIT -12 #define BGP_NLRI_PARSE_ERROR_FLOWSPEC_BAD_FORMAT -13 #define BGP_NLRI_PARSE_ERROR_ADDRESS_FAMILY -14 +#define BGP_NLRI_PARSE_ERROR_EVPN_TYPE1_SIZE -15 #define BGP_NLRI_PARSE_ERROR -32 /* Ancillary information to struct bgp_path_info, @@ -109,7 +120,15 @@ struct bgp_path_info_extra { mpls_label_t label[BGP_MAX_LABELS]; uint32_t num_labels; -#if ENABLE_BGP_VNC + /* af specific flags */ + uint16_t af_flags; +#define BGP_EVPN_MACIP_TYPE_SVI_IP (1 << 0) + + /* SRv6 SID(s) for SRv6-VPN */ + struct in6_addr sid[BGP_MAX_SIDS]; + uint32_t num_sids; + +#ifdef ENABLE_BGP_VNC union { struct { @@ -180,7 +199,7 @@ struct bgp_path_info { LIST_ENTRY(bgp_path_info) nh_thread; /* Back pointer to the prefix node */ - struct bgp_node *net; + struct bgp_dest *net; /* Back pointer to the nexthop structure */ struct bgp_nexthop_cache *nexthop; @@ -221,6 +240,7 @@ struct bgp_path_info { #define BGP_PATH_MULTIPATH_CHG (1 << 12) #define BGP_PATH_RIB_ATTR_CHG (1 << 13) #define BGP_PATH_ANNC_NH_SELF (1 << 14) +#define BGP_PATH_LINK_BW_CHG (1 << 15) /* BGP route type. This can be static, RIP, OSPF, BGP etc. */ uint8_t type; @@ -231,9 +251,9 @@ struct bgp_path_info { #define BGP_ROUTE_NORMAL 0 #define BGP_ROUTE_STATIC 1 #define BGP_ROUTE_AGGREGATE 2 -#define BGP_ROUTE_REDISTRIBUTE 3 +#define BGP_ROUTE_REDISTRIBUTE 3 #ifdef ENABLE_BGP_VNC -# define BGP_ROUTE_RFP 4 +# define BGP_ROUTE_RFP 4 #endif #define BGP_ROUTE_IMPORTED 5 /* from another bgp instance/safi */ @@ -284,7 +304,7 @@ struct bgp_static { mpls_label_t label; /* EVPN */ - struct eth_segment_id *eth_s_id; + esi_t *eth_s_id; struct ethaddr *router_mac; uint16_t encap_tunneltype; struct prefix gatewayIp; @@ -308,7 +328,10 @@ struct bgp_aggregate { uint8_t as_set; /* Route-map for aggregated route. */ - struct route_map *map; + struct { + char *name; + struct route_map *map; + } rmap; /* Suppress-count. */ unsigned long count; @@ -319,6 +342,9 @@ struct bgp_aggregate { /* Count of routes of origin type egp under this aggregate. */ unsigned long egp_origin_count; + /* Optional modify flag to override ORIGIN */ + uint8_t origin; + /* Hash containing the communities of all the * routes under this aggregate. */ @@ -413,9 +439,9 @@ enum bgp_path_type { BGP_PATH_SHOW_MULTIPATH }; -static inline void bgp_bump_version(struct bgp_node *node) +static inline void bgp_bump_version(struct bgp_dest *dest) { - node->version = bgp_table_next_version(bgp_node_table(node)); + dest->version = bgp_table_next_version(bgp_dest_table(dest)); } static inline int bgp_fibupd_safi(safi_t safi) @@ -432,12 +458,12 @@ static inline bool is_pi_family_matching(struct bgp_path_info *pi, afi_t afi, safi_t safi) { struct bgp_table *table; - struct bgp_node *rn; + struct bgp_dest *dest; - rn = pi->net; - if (!rn) + dest = pi->net; + if (!dest) return false; - table = bgp_node_table(rn); + table = bgp_dest_table(dest); if (table && table->afi == afi && table->safi == safi) @@ -445,8 +471,35 @@ static inline bool is_pi_family_matching(struct bgp_path_info *pi, return false; } +static inline void prep_for_rmap_apply(struct bgp_path_info *dst_pi, + struct bgp_path_info_extra *dst_pie, + struct bgp_dest *dest, + struct bgp_path_info *src_pi, + struct peer *peer, struct attr *attr) +{ + memset(dst_pi, 0, sizeof(struct bgp_path_info)); + dst_pi->peer = peer; + dst_pi->attr = attr; + dst_pi->net = dest; + dst_pi->flags = src_pi->flags; + dst_pi->type = src_pi->type; + dst_pi->sub_type = src_pi->sub_type; + dst_pi->mpath = src_pi->mpath; + if (src_pi->extra) { + memcpy(dst_pie, src_pi->extra, + sizeof(struct bgp_path_info_extra)); + dst_pi->extra = dst_pie; + } +} + +/* called before bgp_process() */ +DECLARE_HOOK(bgp_process, + (struct bgp * bgp, afi_t afi, safi_t safi, struct bgp_dest *bn, + struct peer *peer, bool withdraw), + (bgp, afi, safi, bn, peer, withdraw)) + /* Prototypes. */ -extern void bgp_rib_remove(struct bgp_node *rn, struct bgp_path_info *pi, +extern void bgp_rib_remove(struct bgp_dest *dest, struct bgp_path_info *pi, struct peer *peer, afi_t afi, safi_t safi); extern void bgp_process_queue_init(void); extern void bgp_route_init(void); @@ -461,30 +514,31 @@ extern void bgp_clear_route(struct peer *, afi_t, safi_t); extern void bgp_clear_route_all(struct peer *); extern void bgp_clear_adj_in(struct peer *, afi_t, safi_t); extern void bgp_clear_stale_route(struct peer *, afi_t, safi_t); -extern int bgp_outbound_policy_exists(struct peer *, struct bgp_filter *); -extern int bgp_inbound_policy_exists(struct peer *, struct bgp_filter *); +extern bool bgp_outbound_policy_exists(struct peer *, struct bgp_filter *); +extern bool bgp_inbound_policy_exists(struct peer *, struct bgp_filter *); -extern struct bgp_node *bgp_afi_node_get(struct bgp_table *table, afi_t afi, - safi_t safi, struct prefix *p, +extern struct bgp_dest *bgp_afi_node_get(struct bgp_table *table, afi_t afi, + safi_t safi, const struct prefix *p, struct prefix_rd *prd); extern struct bgp_path_info *bgp_path_info_lock(struct bgp_path_info *path); extern struct bgp_path_info *bgp_path_info_unlock(struct bgp_path_info *path); -extern void bgp_path_info_add(struct bgp_node *rn, struct bgp_path_info *pi); +extern void bgp_path_info_add(struct bgp_dest *dest, struct bgp_path_info *pi); extern void bgp_path_info_extra_free(struct bgp_path_info_extra **extra); -extern void bgp_path_info_reap(struct bgp_node *rn, struct bgp_path_info *pi); -extern void bgp_path_info_delete(struct bgp_node *rn, struct bgp_path_info *pi); +extern void bgp_path_info_reap(struct bgp_dest *dest, struct bgp_path_info *pi); +extern void bgp_path_info_delete(struct bgp_dest *dest, + struct bgp_path_info *pi); extern struct bgp_path_info_extra * bgp_path_info_extra_get(struct bgp_path_info *path); -extern void bgp_path_info_set_flag(struct bgp_node *rn, +extern void bgp_path_info_set_flag(struct bgp_dest *dest, struct bgp_path_info *path, uint32_t flag); -extern void bgp_path_info_unset_flag(struct bgp_node *rn, +extern void bgp_path_info_unset_flag(struct bgp_dest *dest, struct bgp_path_info *path, uint32_t flag); extern void bgp_path_info_path_with_addpath_rx_str(struct bgp_path_info *pi, char *buf); extern int bgp_nlri_parse_ip(struct peer *, struct attr *, struct bgp_nlri *); -extern int bgp_maximum_prefix_overflow(struct peer *, afi_t, safi_t, int); +extern bool bgp_maximum_prefix_overflow(struct peer *, afi_t, safi_t, int); extern void bgp_redistribute_add(struct bgp *bgp, struct prefix *p, const union g_addr *nexthop, ifindex_t ifindex, @@ -499,9 +553,10 @@ extern void bgp_static_add(struct bgp *); extern void bgp_static_delete(struct bgp *); extern void bgp_static_redo_import_check(struct bgp *); extern void bgp_purge_static_redist_routes(struct bgp *bgp); -extern void bgp_static_update(struct bgp *, struct prefix *, - struct bgp_static *, afi_t, safi_t); -extern void bgp_static_withdraw(struct bgp *, struct prefix *, afi_t, safi_t); +extern void bgp_static_update(struct bgp *bgp, const struct prefix *p, + struct bgp_static *s, afi_t afi, safi_t safi); +extern void bgp_static_withdraw(struct bgp *bgp, const struct prefix *p, + afi_t afi, safi_t safi); extern int bgp_static_set_safi(afi_t afi, safi_t safi, struct vty *vty, const char *, const char *, const char *, @@ -513,15 +568,20 @@ extern int bgp_static_unset_safi(afi_t afi, safi_t safi, struct vty *, const char *, const char *, const char *); /* this is primarily for MPLS-VPN */ -extern int bgp_update(struct peer *, struct prefix *, uint32_t, struct attr *, - afi_t, safi_t, int, int, struct prefix_rd *, - mpls_label_t *, uint32_t, int, struct bgp_route_evpn *); -extern int bgp_withdraw(struct peer *, struct prefix *, uint32_t, struct attr *, - afi_t, safi_t, int, int, struct prefix_rd *, - mpls_label_t *, uint32_t, struct bgp_route_evpn *); +extern int bgp_update(struct peer *peer, const struct prefix *p, + uint32_t addpath_id, struct attr *attr, + afi_t afi, safi_t safi, int type, int sub_type, + struct prefix_rd *prd, mpls_label_t *label, + uint32_t num_labels, int soft_reconfig, + struct bgp_route_evpn *evpn); +extern int bgp_withdraw(struct peer *peer, const struct prefix *p, + uint32_t addpath_id, struct attr *attr, afi_t afi, + safi_t safi, int type, int sub_type, + struct prefix_rd *prd, mpls_label_t *label, + uint32_t num_labels, struct bgp_route_evpn *evpn); /* for bgp_nexthop and bgp_damp */ -extern void bgp_process(struct bgp *, struct bgp_node *, afi_t, safi_t); +extern void bgp_process(struct bgp *, struct bgp_dest *, afi_t, safi_t); /* * Add an end-of-initial-update marker to the process queue. This is just a @@ -534,15 +594,22 @@ extern void bgp_config_write_network(struct vty *, struct bgp *, afi_t, safi_t); extern void bgp_config_write_distance(struct vty *, struct bgp *, afi_t, safi_t); -extern void bgp_aggregate_increment(struct bgp *bgp, struct prefix *p, +extern void bgp_aggregate_delete(struct bgp *bgp, const struct prefix *p, + afi_t afi, safi_t safi, + struct bgp_aggregate *aggregate); +extern void bgp_aggregate_route(struct bgp *bgp, const struct prefix *p, + afi_t afi, safi_t safi, + struct bgp_aggregate *aggregate); +extern void bgp_aggregate_increment(struct bgp *bgp, const struct prefix *p, struct bgp_path_info *path, afi_t afi, safi_t safi); -extern void bgp_aggregate_decrement(struct bgp *bgp, struct prefix *p, +extern void bgp_aggregate_decrement(struct bgp *bgp, const struct prefix *p, struct bgp_path_info *path, afi_t afi, safi_t safi); -extern uint8_t bgp_distance_apply(struct prefix *p, struct bgp_path_info *path, - afi_t afi, safi_t safi, struct bgp *bgp); +extern uint8_t bgp_distance_apply(const struct prefix *p, + struct bgp_path_info *path, afi_t afi, + safi_t safi, struct bgp *bgp); extern afi_t bgp_node_afi(struct vty *); extern safi_t bgp_node_safi(struct vty *); @@ -550,65 +617,72 @@ extern safi_t bgp_node_safi(struct vty *); extern struct bgp_path_info *info_make(int type, int sub_type, unsigned short instance, struct peer *peer, struct attr *attr, - struct bgp_node *rn); + struct bgp_dest *dest); -extern void route_vty_out(struct vty *vty, struct prefix *p, +extern void route_vty_out(struct vty *vty, const struct prefix *p, struct bgp_path_info *path, int display, safi_t safi, - json_object *json_paths); -extern void route_vty_out_tag(struct vty *vty, struct prefix *p, + json_object *json_paths, bool wide); +extern void route_vty_out_tag(struct vty *vty, const struct prefix *p, struct bgp_path_info *path, int display, safi_t safi, json_object *json); -extern void route_vty_out_tmp(struct vty *vty, struct prefix *p, +extern void route_vty_out_tmp(struct vty *vty, const struct prefix *p, struct attr *attr, safi_t safi, bool use_json, - json_object *json_ar); -extern void route_vty_out_overlay(struct vty *vty, struct prefix *p, + json_object *json_ar, bool wide); +extern void route_vty_out_overlay(struct vty *vty, const struct prefix *p, struct bgp_path_info *path, int display, json_object *json); -extern int subgroup_process_announce_selected(struct update_subgroup *subgrp, - struct bgp_path_info *selected, - struct bgp_node *rn, - uint32_t addpath_tx_id); +extern void subgroup_process_announce_selected(struct update_subgroup *subgrp, + struct bgp_path_info *selected, + struct bgp_dest *dest, + uint32_t addpath_tx_id); -extern int subgroup_announce_check(struct bgp_node *rn, - struct bgp_path_info *pi, - struct update_subgroup *subgrp, - struct prefix *p, struct attr *attr); +extern bool subgroup_announce_check(struct bgp_dest *dest, + struct bgp_path_info *pi, + struct update_subgroup *subgrp, + const struct prefix *p, struct attr *attr); extern void bgp_peer_clear_node_queue_drain_immediate(struct peer *peer); extern void bgp_process_queues_drain_immediate(void); /* for encap/vpn */ -extern struct bgp_node *bgp_afi_node_lookup(struct bgp_table *table, afi_t afi, - safi_t safi, struct prefix *p, +extern struct bgp_dest *bgp_afi_node_lookup(struct bgp_table *table, afi_t afi, + safi_t safi, const struct prefix *p, struct prefix_rd *prd); -extern void bgp_path_info_restore(struct bgp_node *rn, +extern void bgp_path_info_restore(struct bgp_dest *dest, struct bgp_path_info *path); extern int bgp_path_info_cmp_compatible(struct bgp *bgp, struct bgp_path_info *new, struct bgp_path_info *exist, - char *pfx_buf, afi_t afi, safi_t safi); + char *pfx_buf, afi_t afi, safi_t safi, + enum bgp_path_selection_reason *reason); extern void bgp_attr_add_gshut_community(struct attr *attr); -extern void bgp_best_selection(struct bgp *bgp, struct bgp_node *rn, +extern void bgp_best_selection(struct bgp *bgp, struct bgp_dest *dest, struct bgp_maxpaths_cfg *mpath_cfg, struct bgp_path_info_pair *result, afi_t afi, safi_t safi); -extern void bgp_zebra_clear_route_change_flags(struct bgp_node *rn); -extern int bgp_zebra_has_route_changed(struct bgp_node *rn, - struct bgp_path_info *selected); +extern void bgp_zebra_clear_route_change_flags(struct bgp_dest *dest); +extern bool bgp_zebra_has_route_changed(struct bgp_path_info *selected); extern void route_vty_out_detail_header(struct vty *vty, struct bgp *bgp, - struct bgp_node *rn, + struct bgp_dest *dest, struct prefix_rd *prd, afi_t afi, safi_t safi, json_object *json); extern void route_vty_out_detail(struct vty *vty, struct bgp *bgp, - struct prefix *p, struct bgp_path_info *path, - afi_t afi, safi_t safi, - json_object *json_paths); + struct bgp_dest *bn, + struct bgp_path_info *path, afi_t afi, + safi_t safi, json_object *json_paths); extern int bgp_show_table_rd(struct vty *vty, struct bgp *bgp, safi_t safi, struct bgp_table *table, struct prefix_rd *prd, enum bgp_show_type type, void *output_arg, bool use_json); +extern int bgp_best_path_select_defer(struct bgp *bgp, afi_t afi, safi_t safi); +extern bool bgp_update_martian_nexthop(struct bgp *bgp, afi_t afi, safi_t safi, + uint8_t type, uint8_t stype, + struct attr *attr, struct bgp_dest *dest); +extern int bgp_evpn_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, + struct bgp_path_info *exist, int *paths_eq); +extern void subgroup_announce_reset_nhop(uint8_t family, struct attr *attr); #endif /* _QUAGGA_BGP_ROUTE_H */ diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index c276f5ef7b..09cc775d47 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -28,7 +28,7 @@ #include "plist.h" #include "memory.h" #include "log.h" -#include "lua.h" +#include "frrlua.h" #ifdef HAVE_LIBPCREPOSIX #include #else @@ -39,6 +39,7 @@ #include "hash.h" #include "queue.h" #include "frrstr.h" +#include "network.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_table.h" @@ -60,8 +61,12 @@ #include "bgpd/bgp_evpn_private.h" #include "bgpd/bgp_evpn_vty.h" #include "bgpd/bgp_mplsvpn.h" +#include "bgpd/bgp_pbr.h" +#include "bgpd/bgp_flowspec_util.h" +#include "bgpd/bgp_encap_types.h" +#include "bgpd/bgp_mpath.h" -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC #include "bgpd/rfapi/bgp_rfapi_cfg.h" #endif @@ -111,6 +116,7 @@ o Cisco route-map origin : Done tag : Done weight : Done + table : Done o Local extensions @@ -237,10 +243,9 @@ struct bgp_match_peer_compiled { /* Compares the peer specified in the 'match peer' clause with the peer received in bgp_path_info->peer. If it is the same, or if the peer structure received is a peer_group containing it, returns RMAP_MATCH. */ -static route_map_result_t route_match_peer(void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_match_peer(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { struct bgp_match_peer_compiled *pc; union sockunion *su; @@ -267,13 +272,16 @@ static route_map_result_t route_match_peer(void *rule, /* If su='0.0.0.0' (command 'match peer local'), and it's a NETWORK, - REDISTRIBUTE or DEFAULT_GENERATED route => return RMAP_MATCH + REDISTRIBUTE, AGGREGATE-ADDRESS or DEFAULT_GENERATED route + => return RMAP_MATCH */ if (sockunion_same(su, &su_def)) { int ret; if (CHECK_FLAG(peer->rmap_type, PEER_RMAP_TYPE_NETWORK) || CHECK_FLAG(peer->rmap_type, PEER_RMAP_TYPE_REDISTRIBUTE) + || CHECK_FLAG(peer->rmap_type, + PEER_RMAP_TYPE_AGGREGATE) || CHECK_FLAG(peer->rmap_type, PEER_RMAP_TYPE_DEFAULT)) ret = RMAP_MATCH; @@ -328,15 +336,17 @@ static void route_match_peer_free(void *rule) } /* Route map commands for ip address matching. */ -struct route_map_rule_cmd route_match_peer_cmd = {"peer", route_match_peer, - route_match_peer_compile, - route_match_peer_free}; +static const struct route_map_rule_cmd route_match_peer_cmd = { + "peer", + route_match_peer, + route_match_peer_compile, + route_match_peer_free +}; #if defined(HAVE_LUA) -static route_map_result_t route_match_command(void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_match_command(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { int status = RMAP_NOMATCH; u_int32_t locpref = 0; @@ -420,7 +430,7 @@ route_match_command_free(void *rule) XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); } -struct route_map_rule_cmd route_match_command_cmd = { +static const struct route_map_rule_cmd route_match_command_cmd = { "command", route_match_command, route_match_command_compile, @@ -432,10 +442,9 @@ struct route_map_rule_cmd route_match_command_cmd = { /* Match function should return 1 if match is success else return zero. */ -static route_map_result_t route_match_ip_address(void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_match_ip_address(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { struct access_list *alist; @@ -465,17 +474,19 @@ static void route_match_ip_address_free(void *rule) } /* Route map commands for ip address matching. */ -struct route_map_rule_cmd route_match_ip_address_cmd = { - "ip address", route_match_ip_address, route_match_ip_address_compile, - route_match_ip_address_free}; +static const struct route_map_rule_cmd route_match_ip_address_cmd = { + "ip address", + route_match_ip_address, + route_match_ip_address_compile, + route_match_ip_address_free +}; /* `match ip next-hop IP_ADDRESS' */ /* Match function return 1 if match is success else return zero. */ -static route_map_result_t route_match_ip_next_hop(void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_match_ip_next_hop(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { struct access_list *alist; struct bgp_path_info *path; @@ -512,17 +523,19 @@ static void route_match_ip_next_hop_free(void *rule) } /* Route map commands for ip next-hop matching. */ -struct route_map_rule_cmd route_match_ip_next_hop_cmd = { - "ip next-hop", route_match_ip_next_hop, route_match_ip_next_hop_compile, - route_match_ip_next_hop_free}; +static const struct route_map_rule_cmd route_match_ip_next_hop_cmd = { + "ip next-hop", + route_match_ip_next_hop, + route_match_ip_next_hop_compile, + route_match_ip_next_hop_free +}; /* `match ip route-source ACCESS-LIST' */ /* Match function return 1 if match is success else return zero. */ -static route_map_result_t route_match_ip_route_source(void *rule, - const struct prefix *pfx, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_match_ip_route_source(void *rule, const struct prefix *pfx, + route_map_object_t type, void *object) { struct access_list *alist; struct bgp_path_info *path; @@ -565,30 +578,79 @@ static void route_match_ip_route_source_free(void *rule) } /* Route map commands for ip route-source matching. */ -struct route_map_rule_cmd route_match_ip_route_source_cmd = { - "ip route-source", route_match_ip_route_source, - route_match_ip_route_source_compile, route_match_ip_route_source_free}; - -/* `match ip address prefix-list PREFIX_LIST' */ +static const struct route_map_rule_cmd route_match_ip_route_source_cmd = { + "ip route-source", + route_match_ip_route_source, + route_match_ip_route_source_compile, + route_match_ip_route_source_free +}; -static route_map_result_t -route_match_ip_address_prefix_list(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static enum route_map_cmd_result_t +route_match_prefix_list_flowspec(afi_t afi, struct prefix_list *plist, + const struct prefix *p) { - struct prefix_list *plist; + int ret; + struct bgp_pbr_entry_main api; - if (type == RMAP_BGP && prefix->family == AF_INET) { - plist = prefix_list_lookup(AFI_IP, (char *)rule); - if (plist == NULL) - return RMAP_NOMATCH; + memset(&api, 0, sizeof(api)); - return (prefix_list_apply(plist, prefix) == PREFIX_DENY - ? RMAP_NOMATCH - : RMAP_MATCH); + if (family2afi(p->u.prefix_flowspec.family) != afi) + return RMAP_NOMATCH; + + /* extract match from flowspec entries */ + ret = bgp_flowspec_match_rules_fill( + (uint8_t *)p->u.prefix_flowspec.ptr, + p->u.prefix_flowspec.prefixlen, &api, + afi); + if (ret < 0) + return RMAP_NOMATCH; + if (api.match_bitmask & PREFIX_DST_PRESENT || + api.match_bitmask_iprule & PREFIX_DST_PRESENT) { + if (family2afi((&api.dst_prefix)->family) != afi) + return RMAP_NOMATCH; + return prefix_list_apply(plist, &api.dst_prefix) == PREFIX_DENY + ? RMAP_NOMATCH + : RMAP_MATCH; + } else if (api.match_bitmask & PREFIX_SRC_PRESENT || + api.match_bitmask_iprule & PREFIX_SRC_PRESENT) { + if (family2afi((&api.src_prefix)->family) != afi) + return RMAP_NOMATCH; + return (prefix_list_apply(plist, &api.src_prefix) == PREFIX_DENY + ? RMAP_NOMATCH + : RMAP_MATCH); } return RMAP_NOMATCH; } +static enum route_map_cmd_result_t +route_match_address_prefix_list(void *rule, afi_t afi, + const struct prefix *prefix, + route_map_object_t type, void *object) +{ + struct prefix_list *plist; + + if (type != RMAP_BGP) + return RMAP_NOMATCH; + + plist = prefix_list_lookup(afi, (char *)rule); + if (plist == NULL) + return RMAP_NOMATCH; + + if (prefix->family == AF_FLOWSPEC) + return route_match_prefix_list_flowspec(afi, plist, + prefix); + return (prefix_list_apply(plist, prefix) == PREFIX_DENY ? RMAP_NOMATCH + : RMAP_MATCH); +} + +static enum route_map_cmd_result_t +route_match_ip_address_prefix_list(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) +{ + return route_match_address_prefix_list(rule, AFI_IP, prefix, type, + object); +} + static void *route_match_ip_address_prefix_list_compile(const char *arg) { return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg); @@ -599,14 +661,17 @@ static void route_match_ip_address_prefix_list_free(void *rule) XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); } -struct route_map_rule_cmd route_match_ip_address_prefix_list_cmd = { - "ip address prefix-list", route_match_ip_address_prefix_list, +static const struct route_map_rule_cmd + route_match_ip_address_prefix_list_cmd = { + "ip address prefix-list", + route_match_ip_address_prefix_list, route_match_ip_address_prefix_list_compile, - route_match_ip_address_prefix_list_free}; + route_match_ip_address_prefix_list_free +}; /* `match ip next-hop prefix-list PREFIX_LIST' */ -static route_map_result_t +static enum route_map_cmd_result_t route_match_ip_next_hop_prefix_list(void *rule, const struct prefix *prefix, route_map_object_t type, void *object) { @@ -641,14 +706,17 @@ static void route_match_ip_next_hop_prefix_list_free(void *rule) XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); } -struct route_map_rule_cmd route_match_ip_next_hop_prefix_list_cmd = { - "ip next-hop prefix-list", route_match_ip_next_hop_prefix_list, +static const struct route_map_rule_cmd + route_match_ip_next_hop_prefix_list_cmd = { + "ip next-hop prefix-list", + route_match_ip_next_hop_prefix_list, route_match_ip_next_hop_prefix_list_compile, - route_match_ip_next_hop_prefix_list_free}; + route_match_ip_next_hop_prefix_list_free +}; /* `match ip next-hop type ' */ -static route_map_result_t +static enum route_map_cmd_result_t route_match_ip_next_hop_type(void *rule, const struct prefix *prefix, route_map_object_t type, void *object) { @@ -656,8 +724,8 @@ route_match_ip_next_hop_type(void *rule, const struct prefix *prefix, if (type == RMAP_BGP && prefix->family == AF_INET) { path = (struct bgp_path_info *)object; - if (!path || !path->attr) - return RMAP_DENYMATCH; + if (!path) + return RMAP_NOMATCH; /* If nexthop interface's index can't be resolved and nexthop is set to any address then mark it as type `blackhole`. @@ -680,14 +748,17 @@ static void route_match_ip_next_hop_type_free(void *rule) XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); } -static struct route_map_rule_cmd route_match_ip_next_hop_type_cmd = { - "ip next-hop type", route_match_ip_next_hop_type, +static const struct route_map_rule_cmd + route_match_ip_next_hop_type_cmd = { + "ip next-hop type", + route_match_ip_next_hop_type, route_match_ip_next_hop_type_compile, - route_match_ip_next_hop_type_free}; + route_match_ip_next_hop_type_free +}; /* `match ip route-source prefix-list PREFIX_LIST' */ -static route_map_result_t +static enum route_map_cmd_result_t route_match_ip_route_source_prefix_list(void *rule, const struct prefix *prefix, route_map_object_t type, void *object) @@ -729,18 +800,20 @@ static void route_match_ip_route_source_prefix_list_free(void *rule) XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); } -struct route_map_rule_cmd route_match_ip_route_source_prefix_list_cmd = { - "ip route-source prefix-list", route_match_ip_route_source_prefix_list, +static const struct route_map_rule_cmd + route_match_ip_route_source_prefix_list_cmd = { + "ip route-source prefix-list", + route_match_ip_route_source_prefix_list, route_match_ip_route_source_prefix_list_compile, - route_match_ip_route_source_prefix_list_free}; + route_match_ip_route_source_prefix_list_free +}; /* `match evpn default-route' */ /* Match function should return 1 if match is success else 0 */ -static route_map_result_t route_match_evpn_default_route(void *rule, - const struct prefix *p, - route_map_object_t - type, void *object) +static enum route_map_cmd_result_t +route_match_evpn_default_route(void *rule, const struct prefix *p, + route_map_object_t type, void *object) { if (type == RMAP_BGP && is_evpn_prefix_default(p)) return RMAP_MATCH; @@ -749,17 +822,21 @@ static route_map_result_t route_match_evpn_default_route(void *rule, } /* Route map commands for default-route matching. */ -struct route_map_rule_cmd route_match_evpn_default_route_cmd = { - "evpn default-route", route_match_evpn_default_route, NULL, NULL}; +static const struct route_map_rule_cmd + route_match_evpn_default_route_cmd = { + "evpn default-route", + route_match_evpn_default_route, + NULL, + NULL +}; /* `match mac address MAC_ACCESS_LIST' */ /* Match function should return 1 if match is success else return zero. */ -static route_map_result_t route_match_mac_address(void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_match_mac_address(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { struct access_list *alist; struct prefix p; @@ -798,30 +875,57 @@ static void route_match_mac_address_free(void *rule) } /* Route map commands for mac address matching. */ -struct route_map_rule_cmd route_match_mac_address_cmd = { - "mac address", route_match_mac_address, route_match_mac_address_compile, - route_match_mac_address_free}; - -/* `match vni' */ +static const struct route_map_rule_cmd route_match_mac_address_cmd = { + "mac address", + route_match_mac_address, + route_match_mac_address_compile, + route_match_mac_address_free +}; -/* Match function should return 1 if match is success else return - zero. */ -static route_map_result_t route_match_vni(void *rule, - const struct prefix *prefix, - route_map_object_t type, void *object) +/* + * Match function returns: + * ...RMAP_MATCH if match is found. + * ...RMAP_NOMATCH if match is not found. + * ...RMAP_NOOP to ignore this match check. + */ +static enum route_map_cmd_result_t +route_match_vni(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { vni_t vni = 0; + unsigned int label_cnt = 0; struct bgp_path_info *path = NULL; + struct prefix_evpn *evp = (struct prefix_evpn *) prefix; if (type == RMAP_BGP) { vni = *((vni_t *)rule); path = (struct bgp_path_info *)object; + /* + * This rmap filter is valid for vxlan tunnel type only. + * For any other tunnel type, return noop to ignore + * this check. + */ + if (path->attr->encap_tunneltype != BGP_ENCAP_TYPE_VXLAN) + return RMAP_NOOP; + + /* + * Apply filter to type 1, 2, 5 routes only. + * Other route types do not have vni label. + */ + if (evp && (evp->prefix.route_type != BGP_EVPN_AD_ROUTE && + evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE && + evp->prefix.route_type != BGP_EVPN_IP_PREFIX_ROUTE)) + return RMAP_NOOP; + if (path->extra == NULL) return RMAP_NOMATCH; - if (vni == label2vni(&path->extra->label[0])) - return RMAP_MATCH; + for ( ; label_cnt < BGP_MAX_LABELS && + label_cnt < path->extra->num_labels; label_cnt++) { + if (vni == label2vni(&path->extra->label[label_cnt])) + return RMAP_MATCH; + } } return RMAP_NOMATCH; @@ -851,18 +955,20 @@ static void route_match_vni_free(void *rule) } /* Route map commands for vni matching. */ -struct route_map_rule_cmd route_match_evpn_vni_cmd = { - "evpn vni", route_match_vni, route_match_vni_compile, - route_match_vni_free}; +static const struct route_map_rule_cmd route_match_evpn_vni_cmd = { + "evpn vni", + route_match_vni, + route_match_vni_compile, + route_match_vni_free +}; /* `match evpn route-type' */ /* Match function should return 1 if match is success else return zero. */ -static route_map_result_t route_match_evpn_route_type(void *rule, - const struct prefix *pfx, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_match_evpn_route_type(void *rule, const struct prefix *pfx, + route_map_object_t type, void *object) { uint8_t route_type = 0; @@ -900,12 +1006,76 @@ static void route_match_evpn_route_type_free(void *rule) } /* Route map commands for evpn route-type matching. */ -struct route_map_rule_cmd route_match_evpn_route_type_cmd = { - "evpn route-type", route_match_evpn_route_type, - route_match_evpn_route_type_compile, route_match_evpn_route_type_free}; +static const struct route_map_rule_cmd route_match_evpn_route_type_cmd = { + "evpn route-type", + route_match_evpn_route_type, + route_match_evpn_route_type_compile, + route_match_evpn_route_type_free +}; + +/* `match rd' */ + +/* Match function should return 1 if match is success else return zero. */ +static enum route_map_cmd_result_t +route_match_rd(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) +{ + struct prefix_rd *prd_rule = NULL; + const struct prefix_rd *prd_route = NULL; + struct bgp_path_info *path = NULL; + + if (type == RMAP_BGP) { + if (prefix->family != AF_EVPN) + return RMAP_NOMATCH; + + prd_rule = (struct prefix_rd *)rule; + path = (struct bgp_path_info *)object; + + if (path->net == NULL || path->net->pdest == NULL) + return RMAP_NOMATCH; + + prd_route = (struct prefix_rd *)bgp_dest_get_prefix( + path->net->pdest); + if (memcmp(prd_route->val, prd_rule->val, ECOMMUNITY_SIZE) == 0) + return RMAP_MATCH; + } + + return RMAP_NOMATCH; +} + +/* Route map `rd' match statement. */ +static void *route_match_rd_compile(const char *arg) +{ + struct prefix_rd *prd; + int ret; + + prd = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(struct prefix_rd)); + + ret = str2prefix_rd(arg, prd); + if (!ret) { + XFREE(MTYPE_ROUTE_MAP_COMPILED, prd); + return NULL; + } + + return prd; +} + +/* Free route map's compiled `rd' value. */ +static void route_match_rd_free(void *rule) +{ + XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); +} + +/* Route map commands for rd matching. */ +static const struct route_map_rule_cmd route_match_evpn_rd_cmd = { + "evpn rd", + route_match_rd, + route_match_rd_compile, + route_match_rd_free +}; /* Route map commands for VRF route leak with source vrf matching */ -static route_map_result_t +static enum route_map_cmd_result_t route_match_vrl_source_vrf(void *rule, const struct prefix *prefix, route_map_object_t type, void *object) { @@ -946,18 +1116,19 @@ static void route_match_vrl_source_vrf_free(void *rule) XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); } -struct route_map_rule_cmd route_match_vrl_source_vrf_cmd = { - "source-vrf", route_match_vrl_source_vrf, +static const struct route_map_rule_cmd route_match_vrl_source_vrf_cmd = { + "source-vrf", + route_match_vrl_source_vrf, route_match_vrl_source_vrf_compile, - route_match_vrl_source_vrf_free}; + route_match_vrl_source_vrf_free +}; /* `match local-preference LOCAL-PREF' */ /* Match function return 1 if match is success else return zero. */ -static route_map_result_t route_match_local_pref(void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_match_local_pref(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { uint32_t *local_pref; struct bgp_path_info *path; @@ -974,8 +1145,10 @@ static route_map_result_t route_match_local_pref(void *rule, return RMAP_NOMATCH; } -/* Route map `match local-preference' match statement. - `arg' is local-pref value */ +/* + * Route map `match local-preference' match statement. + * `arg' is local-pref value + */ static void *route_match_local_pref_compile(const char *arg) { uint32_t *local_pref; @@ -1004,17 +1177,19 @@ static void route_match_local_pref_free(void *rule) } /* Route map commands for metric matching. */ -struct route_map_rule_cmd route_match_local_pref_cmd = { - "local-preference", route_match_local_pref, - route_match_local_pref_compile, route_match_local_pref_free}; +static const struct route_map_rule_cmd route_match_local_pref_cmd = { + "local-preference", + route_match_local_pref, + route_match_local_pref_compile, + route_match_local_pref_free +}; /* `match metric METRIC' */ /* Match function return 1 if match is success else return zero. */ -static route_map_result_t route_match_metric(void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_match_metric(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { struct rmap_value *rv; struct bgp_path_info *path; @@ -1028,17 +1203,19 @@ static route_map_result_t route_match_metric(void *rule, } /* Route map commands for metric matching. */ -struct route_map_rule_cmd route_match_metric_cmd = { - "metric", route_match_metric, route_value_compile, route_value_free, +static const struct route_map_rule_cmd route_match_metric_cmd = { + "metric", + route_match_metric, + route_value_compile, + route_value_free, }; /* `match as-path ASPATH' */ /* Match function for as-path match. I assume given object is */ -static route_map_result_t route_match_aspath(void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_match_aspath(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { struct as_list *as_list; @@ -1073,9 +1250,12 @@ static void route_match_aspath_free(void *rule) } /* Route map commands for aspath matching. */ -struct route_map_rule_cmd route_match_aspath_cmd = { - "as-path", route_match_aspath, route_match_aspath_compile, - route_match_aspath_free}; +static const struct route_map_rule_cmd route_match_aspath_cmd = { + "as-path", + route_match_aspath, + route_match_aspath_compile, + route_match_aspath_free +}; /* `match community COMMUNIY' */ struct rmap_community { @@ -1085,10 +1265,9 @@ struct rmap_community { }; /* Match function for community match. */ -static route_map_result_t route_match_community(void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_match_community(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { struct community_list *list; struct bgp_path_info *path; @@ -1149,16 +1328,35 @@ static void route_match_community_free(void *rule) XFREE(MTYPE_ROUTE_MAP_COMPILED, rcom); } +/* + * In routemap processing there is a need to add the + * name as a rule_key in the dependency table. Routemap + * lib is unaware of rule_key when exact-match clause + * is in use. routemap lib uses the compiled output to + * get the rule_key value. + */ +static void *route_match_get_community_key(void *rule) +{ + struct rmap_community *rcom; + + rcom = rule; + return rcom->name; +} + + /* Route map commands for community matching. */ -struct route_map_rule_cmd route_match_community_cmd = { - "community", route_match_community, route_match_community_compile, - route_match_community_free}; +static const struct route_map_rule_cmd route_match_community_cmd = { + "community", + route_match_community, + route_match_community_compile, + route_match_community_free, + route_match_get_community_key +}; /* Match function for lcommunity match. */ -static route_map_result_t route_match_lcommunity(void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_match_lcommunity(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { struct community_list *list; struct bgp_path_info *path; @@ -1173,8 +1371,17 @@ static route_map_result_t route_match_lcommunity(void *rule, if (!list) return RMAP_NOMATCH; - if (lcommunity_list_match(path->attr->lcommunity, list)) - return RMAP_MATCH; + if (rcom->exact) { + if (lcommunity_list_exact_match( + path->attr->lcommunity, + list)) + return RMAP_MATCH; + } else { + if (lcommunity_list_match( + path->attr->lcommunity, + list)) + return RMAP_MATCH; + } } return RMAP_NOMATCH; } @@ -1193,6 +1400,7 @@ static void *route_match_lcommunity_compile(const char *arg) len = p - arg; rcom->name = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, len + 1); memcpy(rcom->name, arg, len); + rcom->exact = 1; } else { rcom->name = XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg); rcom->exact = 0; @@ -1212,16 +1420,19 @@ static void route_match_lcommunity_free(void *rule) } /* Route map commands for community matching. */ -struct route_map_rule_cmd route_match_lcommunity_cmd = { - "large-community", route_match_lcommunity, - route_match_lcommunity_compile, route_match_lcommunity_free}; +static const struct route_map_rule_cmd route_match_lcommunity_cmd = { + "large-community", + route_match_lcommunity, + route_match_lcommunity_compile, + route_match_lcommunity_free, + route_match_get_community_key +}; /* Match function for extcommunity match. */ -static route_map_result_t route_match_ecommunity(void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_match_ecommunity(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { struct community_list *list; struct bgp_path_info *path; @@ -1264,18 +1475,20 @@ static void route_match_ecommunity_free(void *rule) } /* Route map commands for community matching. */ -struct route_map_rule_cmd route_match_ecommunity_cmd = { - "extcommunity", route_match_ecommunity, route_match_ecommunity_compile, - route_match_ecommunity_free}; +static const struct route_map_rule_cmd route_match_ecommunity_cmd = { + "extcommunity", + route_match_ecommunity, + route_match_ecommunity_compile, + route_match_ecommunity_free +}; /* `match nlri` and `set nlri` are replaced by `address-family ipv4` and `address-family vpnv4'. */ /* `match origin' */ -static route_map_result_t route_match_origin(void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_match_origin(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { uint8_t *origin; struct bgp_path_info *path; @@ -1314,18 +1527,20 @@ static void route_match_origin_free(void *rule) } /* Route map commands for origin matching. */ -struct route_map_rule_cmd route_match_origin_cmd = { - "origin", route_match_origin, route_match_origin_compile, - route_match_origin_free}; +static const struct route_map_rule_cmd route_match_origin_cmd = { + "origin", + route_match_origin, + route_match_origin_compile, + route_match_origin_free +}; /* match probability { */ -static route_map_result_t route_match_probability(void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_match_probability(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { - long r = random(); + long r = frr_weak_random(); switch (*(long *)rule) { case 0: @@ -1368,17 +1583,19 @@ static void route_match_probability_free(void *rule) XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); } -struct route_map_rule_cmd route_match_probability_cmd = { - "probability", route_match_probability, route_match_probability_compile, - route_match_probability_free}; +static const struct route_map_rule_cmd route_match_probability_cmd = { + "probability", + route_match_probability, + route_match_probability_compile, + route_match_probability_free +}; /* `match interface IFNAME' */ /* Match function should return 1 if match is success else return zero. */ -static route_map_result_t route_match_interface(void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_match_interface(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { struct interface *ifp; struct bgp_path_info *path; @@ -1386,7 +1603,7 @@ static route_map_result_t route_match_interface(void *rule, if (type == RMAP_BGP) { path = object; - if (!path || !path->attr) + if (!path) return RMAP_NOMATCH; ifp = if_lookup_by_name_all_vrf((char *)rule); @@ -1413,18 +1630,21 @@ static void route_match_interface_free(void *rule) } /* Route map commands for ip address matching. */ -struct route_map_rule_cmd route_match_interface_cmd = { - "interface", route_match_interface, route_match_interface_compile, - route_match_interface_free}; +static const struct route_map_rule_cmd route_match_interface_cmd = { + "interface", + route_match_interface, + route_match_interface_compile, + route_match_interface_free +}; /* } */ /* `set ip next-hop IP_ADDRESS' */ /* Match function return 1 if match is success else return zero. */ -static route_map_result_t route_match_tag(void *rule, - const struct prefix *prefix, - route_map_object_t type, void *object) +static enum route_map_cmd_result_t +route_match_tag(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { route_tag_t *tag; struct bgp_path_info *path; @@ -1441,11 +1661,52 @@ static route_map_result_t route_match_tag(void *rule, /* Route map commands for tag matching. */ -static struct route_map_rule_cmd route_match_tag_cmd = { - "tag", route_match_tag, route_map_rule_tag_compile, +static const struct route_map_rule_cmd route_match_tag_cmd = { + "tag", + route_match_tag, + route_map_rule_tag_compile, route_map_rule_tag_free, }; +static enum route_map_cmd_result_t +route_set_srte_color(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) +{ + uint32_t *srte_color = rule; + struct bgp_path_info *path; + + if (type != RMAP_BGP) + return RMAP_OKAY; + + path = object; + + path->attr->srte_color = *srte_color; + path->attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_SRTE_COLOR); + + return RMAP_OKAY; +} + +/* Route map `sr-te color' compile function */ +static void *route_set_srte_color_compile(const char *arg) +{ + uint32_t *color; + + color = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(uint32_t)); + *color = atoi(arg); + + return color; +} + +/* Free route map's compiled `sr-te color' value. */ +static void route_set_srte_color_free(void *rule) +{ + XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); +} + +/* Route map commands for sr-te color set. */ +struct route_map_rule_cmd route_set_srte_color_cmd = { + "sr-te color", route_set_srte_color, route_set_srte_color_compile, + route_set_srte_color_free}; /* Set nexthop to object. ojbect must be pointer to struct attr. */ struct rmap_ip_nexthop_set { @@ -1454,10 +1715,9 @@ struct rmap_ip_nexthop_set { int unchanged; }; -static route_map_result_t route_set_ip_nexthop(void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_set_ip_nexthop(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { struct rmap_ip_nexthop_set *rins = rule; struct bgp_path_info *path; @@ -1491,7 +1751,7 @@ static route_map_result_t route_set_ip_nexthop(void *rule, */ SET_FLAG(path->attr->rmap_change_flags, BATTR_RMAP_NEXTHOP_PEER_ADDRESS); - path->attr->nexthop.s_addr = 0; + path->attr->nexthop.s_addr = INADDR_ANY; } } else { /* Set next hop value. */ @@ -1553,17 +1813,19 @@ static void route_set_ip_nexthop_free(void *rule) } /* Route map commands for ip nexthop set. */ -struct route_map_rule_cmd route_set_ip_nexthop_cmd = { - "ip next-hop", route_set_ip_nexthop, route_set_ip_nexthop_compile, - route_set_ip_nexthop_free}; +static const struct route_map_rule_cmd route_set_ip_nexthop_cmd = { + "ip next-hop", + route_set_ip_nexthop, + route_set_ip_nexthop_compile, + route_set_ip_nexthop_free +}; /* `set local-preference LOCAL_PREF' */ /* Set local preference. */ -static route_map_result_t route_set_local_pref(void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_set_local_pref(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { struct rmap_value *rv; struct bgp_path_info *path; @@ -1587,18 +1849,19 @@ static route_map_result_t route_set_local_pref(void *rule, } /* Set local preference rule structure. */ -struct route_map_rule_cmd route_set_local_pref_cmd = { - "local-preference", route_set_local_pref, route_value_compile, +static const struct route_map_rule_cmd route_set_local_pref_cmd = { + "local-preference", + route_set_local_pref, + route_value_compile, route_value_free, }; /* `set weight WEIGHT' */ /* Set weight. */ -static route_map_result_t route_set_weight(void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_set_weight(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { struct rmap_value *rv; struct bgp_path_info *path; @@ -1616,17 +1879,43 @@ static route_map_result_t route_set_weight(void *rule, } /* Set local preference rule structure. */ -struct route_map_rule_cmd route_set_weight_cmd = { - "weight", route_set_weight, route_value_compile, route_value_free, +static const struct route_map_rule_cmd route_set_weight_cmd = { + "weight", + route_set_weight, + route_value_compile, + route_value_free, +}; + +/* `set distance DISTANCE */ +static enum route_map_cmd_result_t +route_set_distance(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) +{ + struct bgp_path_info *path = object; + struct rmap_value *rv = rule; + + if (type != RMAP_BGP) + return RMAP_OKAY; + + path->attr->distance = rv->value; + + return RMAP_OKAY; +} + +/* set distance rule structure */ +static const struct route_map_rule_cmd route_set_distance_cmd = { + "distance", + route_set_distance, + route_value_compile, + route_value_free, }; /* `set metric METRIC' */ /* Set metric to attribute. */ -static route_map_result_t route_set_metric(void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_set_metric(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { struct rmap_value *rv; struct bgp_path_info *path; @@ -1647,17 +1936,47 @@ static route_map_result_t route_set_metric(void *rule, } /* Set metric rule structure. */ -struct route_map_rule_cmd route_set_metric_cmd = { - "metric", route_set_metric, route_value_compile, route_value_free, +static const struct route_map_rule_cmd route_set_metric_cmd = { + "metric", + route_set_metric, + route_value_compile, + route_value_free, +}; + +/* `set table (1-4294967295)' */ + +static enum route_map_cmd_result_t route_set_table_id(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object) +{ + struct rmap_value *rv; + struct bgp_path_info *path; + + if (type == RMAP_BGP) { + /* Fetch routemap's rule information. */ + rv = rule; + path = object; + + path->attr->rmap_table_id = rv->value; + } + return RMAP_OKAY; +} + +/* Set table_id rule structure. */ +static const struct route_map_rule_cmd route_set_table_id_cmd = { + "table", + route_set_table_id, + route_value_compile, + route_value_free }; /* `set as-path prepend ASPATH' */ /* For AS path prepend mechanism. */ -static route_map_result_t route_set_aspath_prepend(void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_set_aspath_prepend(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { struct aspath *aspath; struct aspath *new; @@ -1705,9 +2024,11 @@ static void route_set_aspath_prepend_free(void *rule) /* Set as-path prepend rule structure. */ -struct route_map_rule_cmd route_set_aspath_prepend_cmd = { - "as-path prepend", route_set_aspath_prepend, - route_set_aspath_prepend_compile, route_set_aspath_prepend_free, +static const struct route_map_rule_cmd route_set_aspath_prepend_cmd = { + "as-path prepend", + route_set_aspath_prepend, + route_set_aspath_prepend_compile, + route_set_aspath_prepend_free, }; /* `set as-path exclude ASn' */ @@ -1717,10 +2038,9 @@ struct route_map_rule_cmd route_set_aspath_prepend_cmd = { * one. * Make a deep copy of existing AS_PATH, but for the first ASn only. */ -static route_map_result_t route_set_aspath_exclude(void *rule, - const struct prefix *dummy, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_set_aspath_exclude(void *rule, const struct prefix *dummy, + route_map_object_t type, void *object) { struct aspath *new_path, *exclude_path; struct bgp_path_info *path; @@ -1739,8 +2059,10 @@ static route_map_result_t route_set_aspath_exclude(void *rule, } /* Set ASn exlude rule structure. */ -struct route_map_rule_cmd route_set_aspath_exclude_cmd = { - "as-path exclude", route_set_aspath_exclude, route_aspath_compile, +static const struct route_map_rule_cmd route_set_aspath_exclude_cmd = { + "as-path exclude", + route_set_aspath_exclude, + route_aspath_compile, route_aspath_free, }; @@ -1752,10 +2074,9 @@ struct rmap_com_set { }; /* For community set mechanism. */ -static route_map_result_t route_set_community(void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_set_community(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { struct rmap_com_set *rcs; struct bgp_path_info *path; @@ -1854,8 +2175,10 @@ static void route_set_community_free(void *rule) } /* Set community rule structure. */ -struct route_map_rule_cmd route_set_community_cmd = { - "community", route_set_community, route_set_community_compile, +static const struct route_map_rule_cmd route_set_community_cmd = { + "community", + route_set_community, + route_set_community_compile, route_set_community_free, }; @@ -1868,10 +2191,9 @@ struct rmap_lcom_set { /* For lcommunity set mechanism. */ -static route_map_result_t route_set_lcommunity(void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_set_lcommunity(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { struct rmap_lcom_set *rcs; struct bgp_path_info *path; @@ -1973,18 +2295,19 @@ static void route_set_lcommunity_free(void *rule) } /* Set community rule structure. */ -struct route_map_rule_cmd route_set_lcommunity_cmd = { - "large-community", route_set_lcommunity, route_set_lcommunity_compile, +static const struct route_map_rule_cmd route_set_lcommunity_cmd = { + "large-community", + route_set_lcommunity, + route_set_lcommunity_compile, route_set_lcommunity_free, }; /* `set large-comm-list (<1-99>|<100-500>|WORD) delete' */ /* For large community set mechanism. */ -static route_map_result_t route_set_lcommunity_delete(void *rule, - const struct prefix *pfx, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_set_lcommunity_delete(void *rule, const struct prefix *pfx, + route_map_object_t type, void *object) { struct community_list *list; struct lcommunity *merge; @@ -2037,12 +2360,19 @@ static route_map_result_t route_set_lcommunity_delete(void *rule, static void *route_set_lcommunity_delete_compile(const char *arg) { struct rmap_community *rcom; + char **splits; + int num; - rcom = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(struct rmap_community)); + frrstr_split(arg, " ", &splits, &num); - rcom->name = XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg); + rcom = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(struct rmap_community)); + rcom->name = XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, splits[0]); rcom->name_hash = bgp_clist_hash_key(rcom->name); + for (int i = 0; i < num; i++) + XFREE(MTYPE_TMP, splits[i]); + XFREE(MTYPE_TMP, splits); + return rcom; } @@ -2056,20 +2386,20 @@ static void route_set_lcommunity_delete_free(void *rule) } /* Set lcommunity rule structure. */ -struct route_map_rule_cmd route_set_lcommunity_delete_cmd = { - "large-comm-list", route_set_lcommunity_delete, - route_set_lcommunity_delete_compile, route_set_lcommunity_delete_free, +static const struct route_map_rule_cmd route_set_lcommunity_delete_cmd = { + "large-comm-list", + route_set_lcommunity_delete, + route_set_lcommunity_delete_compile, + route_set_lcommunity_delete_free, }; /* `set comm-list (<1-99>|<100-500>|WORD) delete' */ /* For community set mechanism. */ -static route_map_result_t route_set_community_delete( - void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_set_community_delete(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { struct community_list *list; struct community *merge; @@ -2122,12 +2452,19 @@ static route_map_result_t route_set_community_delete( static void *route_set_community_delete_compile(const char *arg) { struct rmap_community *rcom; + char **splits; + int num; - rcom = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(struct rmap_community)); + frrstr_split(arg, " ", &splits, &num); - rcom->name = XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg); + rcom = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(struct rmap_community)); + rcom->name = XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, splits[0]); rcom->name_hash = bgp_clist_hash_key(rcom->name); + for (int i = 0; i < num; i++) + XFREE(MTYPE_TMP, splits[i]); + XFREE(MTYPE_TMP, splits); + return rcom; } @@ -2141,18 +2478,19 @@ static void route_set_community_delete_free(void *rule) } /* Set community rule structure. */ -struct route_map_rule_cmd route_set_community_delete_cmd = { - "comm-list", route_set_community_delete, - route_set_community_delete_compile, route_set_community_delete_free, +static const struct route_map_rule_cmd route_set_community_delete_cmd = { + "comm-list", + route_set_community_delete, + route_set_community_delete_compile, + route_set_community_delete_free, }; /* `set extcommunity rt COMMUNITY' */ /* For community set mechanism. Used by _rt and _soo. */ -static route_map_result_t route_set_ecommunity(void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_set_ecommunity(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { struct ecommunity *ecom; struct ecommunity *new_ecom; @@ -2209,9 +2547,11 @@ static void route_set_ecommunity_free(void *rule) } /* Set community rule structure. */ -struct route_map_rule_cmd route_set_ecommunity_rt_cmd = { - "extcommunity rt", route_set_ecommunity, - route_set_ecommunity_rt_compile, route_set_ecommunity_free, +static const struct route_map_rule_cmd route_set_ecommunity_rt_cmd = { + "extcommunity rt", + route_set_ecommunity, + route_set_ecommunity_rt_compile, + route_set_ecommunity_free, }; /* `set extcommunity soo COMMUNITY' */ @@ -2229,18 +2569,160 @@ static void *route_set_ecommunity_soo_compile(const char *arg) } /* Set community rule structure. */ -struct route_map_rule_cmd route_set_ecommunity_soo_cmd = { - "extcommunity soo", route_set_ecommunity, - route_set_ecommunity_soo_compile, route_set_ecommunity_free, +static const struct route_map_rule_cmd route_set_ecommunity_soo_cmd = { + "extcommunity soo", + route_set_ecommunity, + route_set_ecommunity_soo_compile, + route_set_ecommunity_free, +}; + +/* `set extcommunity bandwidth' */ + +struct rmap_ecomm_lb_set { + uint8_t lb_type; +#define RMAP_ECOMM_LB_SET_VALUE 1 +#define RMAP_ECOMM_LB_SET_CUMUL 2 +#define RMAP_ECOMM_LB_SET_NUM_MPATH 3 + bool non_trans; + uint32_t bw; +}; + +static enum route_map_cmd_result_t +route_set_ecommunity_lb(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) +{ + struct rmap_ecomm_lb_set *rels = rule; + struct bgp_path_info *path; + struct peer *peer; + struct ecommunity ecom_lb = {0}; + struct ecommunity_val lb_eval; + uint32_t bw_bytes = 0; + uint16_t mpath_count = 0; + struct ecommunity *new_ecom; + struct ecommunity *old_ecom; + as_t as; + + if (type != RMAP_BGP) + return RMAP_OKAY; + + path = object; + peer = path->peer; + if (!peer || !peer->bgp) + return RMAP_ERROR; + + /* Build link bandwidth extended community */ + as = (peer->bgp->as > BGP_AS_MAX) ? BGP_AS_TRANS : peer->bgp->as; + if (rels->lb_type == RMAP_ECOMM_LB_SET_VALUE) { + bw_bytes = ((uint64_t)rels->bw * 1000 * 1000) / 8; + } else if (rels->lb_type == RMAP_ECOMM_LB_SET_CUMUL) { + /* process this only for the best path. */ + if (!CHECK_FLAG(path->flags, BGP_PATH_SELECTED)) + return RMAP_OKAY; + + bw_bytes = (uint32_t)bgp_path_info_mpath_cumbw(path); + if (!bw_bytes) + return RMAP_OKAY; + + } else if (rels->lb_type == RMAP_ECOMM_LB_SET_NUM_MPATH) { + + /* process this only for the best path. */ + if (!CHECK_FLAG(path->flags, BGP_PATH_SELECTED)) + return RMAP_OKAY; + + bw_bytes = ((uint64_t)peer->bgp->lb_ref_bw * 1000 * 1000) / 8; + mpath_count = bgp_path_info_mpath_count(path) + 1; + bw_bytes *= mpath_count; + } + + encode_lb_extcomm(as, bw_bytes, rels->non_trans, &lb_eval); + + /* add to route or merge with existing */ + old_ecom = path->attr->ecommunity; + if (old_ecom) { + new_ecom = ecommunity_dup(old_ecom); + ecommunity_add_val(new_ecom, &lb_eval, true, true); + if (!old_ecom->refcnt) + ecommunity_free(&old_ecom); + } else { + ecom_lb.size = 1; + ecom_lb.unit_size = ECOMMUNITY_SIZE; + ecom_lb.val = (uint8_t *)lb_eval.val; + new_ecom = ecommunity_dup(&ecom_lb); + } + + /* new_ecom will be intern()'d or attr_flush()'d in call stack */ + path->attr->ecommunity = new_ecom; + path->attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES); + + /* Mark that route-map has set link bandwidth; used in attribute + * setting decisions. + */ + SET_FLAG(path->attr->rmap_change_flags, BATTR_RMAP_LINK_BW_SET); + + return RMAP_OKAY; +} + +static void *route_set_ecommunity_lb_compile(const char *arg) +{ + struct rmap_ecomm_lb_set *rels; + uint8_t lb_type; + uint32_t bw = 0; + char bw_str[40] = {0}; + char *p, *str; + bool non_trans = false; + + str = (char *)arg; + p = strchr(arg, ' '); + if (p) { + int len; + + len = p - arg; + memcpy(bw_str, arg, len); + non_trans = true; + str = bw_str; + } + + if (strcmp(str, "cumulative") == 0) + lb_type = RMAP_ECOMM_LB_SET_CUMUL; + else if (strcmp(str, "num-multipaths") == 0) + lb_type = RMAP_ECOMM_LB_SET_NUM_MPATH; + else { + char *end = NULL; + + bw = strtoul(str, &end, 10); + if (*end != '\0') + return NULL; + lb_type = RMAP_ECOMM_LB_SET_VALUE; + } + + rels = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, + sizeof(struct rmap_ecomm_lb_set)); + rels->lb_type = lb_type; + rels->bw = bw; + rels->non_trans = non_trans; + + return rels; +} + +static void route_set_ecommunity_lb_free(void *rule) +{ + XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); +} + +/* Set community rule structure. */ +struct route_map_rule_cmd route_set_ecommunity_lb_cmd = { + "extcommunity bandwidth", + route_set_ecommunity_lb, + route_set_ecommunity_lb_compile, + route_set_ecommunity_lb_free, }; /* `set origin ORIGIN' */ /* For origin set. */ -static route_map_result_t route_set_origin(void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_set_origin(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { uint8_t *origin; struct bgp_path_info *path; @@ -2279,18 +2761,19 @@ static void route_set_origin_free(void *rule) } /* Set origin rule structure. */ -struct route_map_rule_cmd route_set_origin_cmd = { - "origin", route_set_origin, route_set_origin_compile, +static const struct route_map_rule_cmd route_set_origin_cmd = { + "origin", + route_set_origin, + route_set_origin_compile, route_set_origin_free, }; /* `set atomic-aggregate' */ /* For atomic aggregate set. */ -static route_map_result_t route_set_atomic_aggregate(void *rule, - const struct prefix *pfx, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_set_atomic_aggregate(void *rule, const struct prefix *pfx, + route_map_object_t type, void *object) { struct bgp_path_info *path; @@ -2315,9 +2798,11 @@ static void route_set_atomic_aggregate_free(void *rule) } /* Set atomic aggregate rule structure. */ -struct route_map_rule_cmd route_set_atomic_aggregate_cmd = { - "atomic-aggregate", route_set_atomic_aggregate, - route_set_atomic_aggregate_compile, route_set_atomic_aggregate_free, +static const struct route_map_rule_cmd route_set_atomic_aggregate_cmd = { + "atomic-aggregate", + route_set_atomic_aggregate, + route_set_atomic_aggregate_compile, + route_set_atomic_aggregate_free, }; /* `set aggregator as AS A.B.C.D' */ @@ -2326,10 +2811,9 @@ struct aggregator { struct in_addr address; }; -static route_map_result_t route_set_aggregator_as(void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_set_aggregator_as(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { struct bgp_path_info *path; struct aggregator *aggregator; @@ -2374,15 +2858,17 @@ static void route_set_aggregator_as_free(void *rule) XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); } -struct route_map_rule_cmd route_set_aggregator_as_cmd = { - "aggregator as", route_set_aggregator_as, - route_set_aggregator_as_compile, route_set_aggregator_as_free, +static const struct route_map_rule_cmd route_set_aggregator_as_cmd = { + "aggregator as", + route_set_aggregator_as, + route_set_aggregator_as_compile, + route_set_aggregator_as_free, }; /* Set tag to object. object must be pointer to struct bgp_path_info */ -static route_map_result_t route_set_tag(void *rule, - const struct prefix *prefix, - route_map_object_t type, void *object) +static enum route_map_cmd_result_t +route_set_tag(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { route_tag_t *tag; struct bgp_path_info *path; @@ -2399,16 +2885,17 @@ static route_map_result_t route_set_tag(void *rule, } /* Route map commands for tag set. */ -static struct route_map_rule_cmd route_set_tag_cmd = { - "tag", route_set_tag, route_map_rule_tag_compile, +static const struct route_map_rule_cmd route_set_tag_cmd = { + "tag", + route_set_tag, + route_map_rule_tag_compile, route_map_rule_tag_free, }; /* Set label-index to object. object must be pointer to struct bgp_path_info */ -static route_map_result_t route_set_label_index(void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_set_label_index(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { struct rmap_value *rv; struct bgp_path_info *path; @@ -2431,17 +2918,18 @@ static route_map_result_t route_set_label_index(void *rule, } /* Route map commands for label-index set. */ -static struct route_map_rule_cmd route_set_label_index_cmd = { - "label-index", route_set_label_index, route_value_compile, +static const struct route_map_rule_cmd route_set_label_index_cmd = { + "label-index", + route_set_label_index, + route_value_compile, route_value_free, }; /* `match ipv6 address IP_ACCESS_LIST' */ -static route_map_result_t route_match_ipv6_address(void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_match_ipv6_address(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { struct access_list *alist; @@ -2468,16 +2956,18 @@ static void route_match_ipv6_address_free(void *rule) } /* Route map commands for ip address matching. */ -struct route_map_rule_cmd route_match_ipv6_address_cmd = { - "ipv6 address", route_match_ipv6_address, - route_match_ipv6_address_compile, route_match_ipv6_address_free}; +static const struct route_map_rule_cmd route_match_ipv6_address_cmd = { + "ipv6 address", + route_match_ipv6_address, + route_match_ipv6_address_compile, + route_match_ipv6_address_free +}; /* `match ipv6 next-hop IP_ADDRESS' */ -static route_map_result_t route_match_ipv6_next_hop(void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_match_ipv6_next_hop(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { struct in6_addr *addr = rule; struct bgp_path_info *path; @@ -2520,30 +3010,74 @@ static void route_match_ipv6_next_hop_free(void *rule) XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); } -struct route_map_rule_cmd route_match_ipv6_next_hop_cmd = { - "ipv6 next-hop", route_match_ipv6_next_hop, - route_match_ipv6_next_hop_compile, route_match_ipv6_next_hop_free}; +static const struct route_map_rule_cmd route_match_ipv6_next_hop_cmd = { + "ipv6 next-hop", + route_match_ipv6_next_hop, + route_match_ipv6_next_hop_compile, + route_match_ipv6_next_hop_free +}; -/* `match ipv6 address prefix-list PREFIX_LIST' */ +/* `match ip next-hop IP_ADDRESS' */ -static route_map_result_t -route_match_ipv6_address_prefix_list(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static enum route_map_cmd_result_t +route_match_ipv4_next_hop(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { - struct prefix_list *plist; + struct in_addr *addr = rule; + struct bgp_path_info *path; - if (type == RMAP_BGP && prefix->family == AF_INET6) { - plist = prefix_list_lookup(AFI_IP6, (char *)rule); - if (plist == NULL) - return RMAP_NOMATCH; + if (type == RMAP_BGP) { + path = object; - return (prefix_list_apply(plist, prefix) == PREFIX_DENY - ? RMAP_NOMATCH - : RMAP_MATCH); + if (path->attr->nexthop.s_addr == addr->s_addr || + (path->attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV4 && + IPV4_ADDR_SAME(&path->attr->mp_nexthop_global_in, addr))) + return RMAP_MATCH; + + return RMAP_NOMATCH; } + return RMAP_NOMATCH; } +static void *route_match_ipv4_next_hop_compile(const char *arg) +{ + struct in_addr *address; + int ret; + + address = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(struct in_addr)); + + ret = inet_pton(AF_INET, arg, address); + if (!ret) { + XFREE(MTYPE_ROUTE_MAP_COMPILED, address); + return NULL; + } + + return address; +} + +static void route_match_ipv4_next_hop_free(void *rule) +{ + XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); +} + +static const struct route_map_rule_cmd route_match_ipv4_next_hop_cmd = { + "ip next-hop address", + route_match_ipv4_next_hop, + route_match_ipv4_next_hop_compile, + route_match_ipv4_next_hop_free +}; + +/* `match ipv6 address prefix-list PREFIX_LIST' */ + +static enum route_map_cmd_result_t +route_match_ipv6_address_prefix_list(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) +{ + return route_match_address_prefix_list(rule, AFI_IP6, prefix, type, + object); +} + static void *route_match_ipv6_address_prefix_list_compile(const char *arg) { return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg); @@ -2554,24 +3088,27 @@ static void route_match_ipv6_address_prefix_list_free(void *rule) XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); } -struct route_map_rule_cmd route_match_ipv6_address_prefix_list_cmd = { - "ipv6 address prefix-list", route_match_ipv6_address_prefix_list, +static const struct route_map_rule_cmd + route_match_ipv6_address_prefix_list_cmd = { + "ipv6 address prefix-list", + route_match_ipv6_address_prefix_list, route_match_ipv6_address_prefix_list_compile, - route_match_ipv6_address_prefix_list_free}; + route_match_ipv6_address_prefix_list_free +}; /* `match ipv6 next-hop type ' */ -static route_map_result_t +static enum route_map_cmd_result_t route_match_ipv6_next_hop_type(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) + route_map_object_t type, void *object) { struct bgp_path_info *path; struct in6_addr *addr = rule; if (type == RMAP_BGP && prefix->family == AF_INET6) { path = (struct bgp_path_info *)object; - if (!path || !path->attr) - return RMAP_DENYMATCH; + if (!path) + return RMAP_NOMATCH; if (IPV6_ADDR_SAME(&path->attr->mp_nexthop_global, addr) && !path->attr->nh_ifindex) @@ -2601,18 +3138,20 @@ static void route_match_ipv6_next_hop_type_free(void *rule) XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); } -struct route_map_rule_cmd route_match_ipv6_next_hop_type_cmd = { - "ipv6 next-hop type", route_match_ipv6_next_hop_type, +static const struct route_map_rule_cmd + route_match_ipv6_next_hop_type_cmd = { + "ipv6 next-hop type", + route_match_ipv6_next_hop_type, route_match_ipv6_next_hop_type_compile, - route_match_ipv6_next_hop_type_free}; + route_match_ipv6_next_hop_type_free +}; /* `set ipv6 nexthop global IP_ADDRESS' */ /* Set nexthop to object. ojbect must be pointer to struct attr. */ -static route_map_result_t route_set_ipv6_nexthop_global(void *rule, - const struct prefix *p, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_set_ipv6_nexthop_global(void *rule, const struct prefix *p, + route_map_object_t type, void *object) { struct in6_addr *address; struct bgp_path_info *path; @@ -2662,13 +3201,16 @@ static void route_set_ipv6_nexthop_global_free(void *rule) } /* Route map commands for ip nexthop set. */ -struct route_map_rule_cmd route_set_ipv6_nexthop_global_cmd = { - "ipv6 next-hop global", route_set_ipv6_nexthop_global, +static const struct route_map_rule_cmd + route_set_ipv6_nexthop_global_cmd = { + "ipv6 next-hop global", + route_set_ipv6_nexthop_global, route_set_ipv6_nexthop_global_compile, - route_set_ipv6_nexthop_global_free}; + route_set_ipv6_nexthop_global_free +}; /* Set next-hop preference value. */ -static route_map_result_t +static enum route_map_cmd_result_t route_set_ipv6_nexthop_prefer_global(void *rule, const struct prefix *prefix, route_map_object_t type, void *object) { @@ -2680,16 +3222,14 @@ route_set_ipv6_nexthop_prefer_global(void *rule, const struct prefix *prefix, path = object; peer = path->peer; - if ((CHECK_FLAG(peer->rmap_type, PEER_RMAP_TYPE_IN) - || CHECK_FLAG(peer->rmap_type, PEER_RMAP_TYPE_IMPORT)) - && peer->su_remote - && sockunion_family(peer->su_remote) == AF_INET6) { + if (CHECK_FLAG(peer->rmap_type, PEER_RMAP_TYPE_IN) + || CHECK_FLAG(peer->rmap_type, PEER_RMAP_TYPE_IMPORT)) { /* Set next hop preference to global */ - path->attr->mp_nexthop_prefer_global = TRUE; + path->attr->mp_nexthop_prefer_global = true; SET_FLAG(path->attr->rmap_change_flags, BATTR_RMAP_IPV6_PREFER_GLOBAL_CHANGED); } else { - path->attr->mp_nexthop_prefer_global = FALSE; + path->attr->mp_nexthop_prefer_global = false; SET_FLAG(path->attr->rmap_change_flags, BATTR_RMAP_IPV6_PREFER_GLOBAL_CHANGED); } @@ -2714,18 +3254,20 @@ static void route_set_ipv6_nexthop_prefer_global_free(void *rule) } /* Route map commands for ip nexthop set preferred. */ -struct route_map_rule_cmd route_set_ipv6_nexthop_prefer_global_cmd = { - "ipv6 next-hop prefer-global", route_set_ipv6_nexthop_prefer_global, +static const struct route_map_rule_cmd + route_set_ipv6_nexthop_prefer_global_cmd = { + "ipv6 next-hop prefer-global", + route_set_ipv6_nexthop_prefer_global, route_set_ipv6_nexthop_prefer_global_compile, - route_set_ipv6_nexthop_prefer_global_free}; + route_set_ipv6_nexthop_prefer_global_free +}; /* `set ipv6 nexthop local IP_ADDRESS' */ /* Set nexthop to object. ojbect must be pointer to struct attr. */ -static route_map_result_t route_set_ipv6_nexthop_local(void *rule, - const struct prefix *p, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_set_ipv6_nexthop_local(void *rule, const struct prefix *p, + route_map_object_t type, void *object) { struct in6_addr *address; struct bgp_path_info *path; @@ -2777,18 +3319,20 @@ static void route_set_ipv6_nexthop_local_free(void *rule) } /* Route map commands for ip nexthop set. */ -struct route_map_rule_cmd route_set_ipv6_nexthop_local_cmd = { - "ipv6 next-hop local", route_set_ipv6_nexthop_local, +static const struct route_map_rule_cmd + route_set_ipv6_nexthop_local_cmd = { + "ipv6 next-hop local", + route_set_ipv6_nexthop_local, route_set_ipv6_nexthop_local_compile, - route_set_ipv6_nexthop_local_free}; + route_set_ipv6_nexthop_local_free +}; /* `set ipv6 nexthop peer-address' */ /* Set nexthop to object. ojbect must be pointer to struct attr. */ -static route_map_result_t route_set_ipv6_nexthop_peer(void *rule, - const struct prefix *pfx, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_set_ipv6_nexthop_peer(void *rule, const struct prefix *pfx, + route_map_object_t type, void *object) { struct in6_addr peer_address; struct bgp_path_info *path; @@ -2807,12 +3351,15 @@ static route_map_result_t route_set_ipv6_nexthop_peer(void *rule, /* Set next hop value and length in attribute. */ if (IN6_IS_ADDR_LINKLOCAL(&peer_address)) { path->attr->mp_nexthop_local = peer_address; - if (path->attr->mp_nexthop_len != 32) - path->attr->mp_nexthop_len = 32; + if (path->attr->mp_nexthop_len + != BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL) + path->attr->mp_nexthop_len = + BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL; } else { path->attr->mp_nexthop_global = peer_address; if (path->attr->mp_nexthop_len == 0) - path->attr->mp_nexthop_len = 16; + path->attr->mp_nexthop_len = + BGP_ATTR_NHLEN_IPV6_GLOBAL; } } else if (CHECK_FLAG(peer->rmap_type, PEER_RMAP_TYPE_OUT)) { @@ -2857,16 +3404,18 @@ static void route_set_ipv6_nexthop_peer_free(void *rule) } /* Route map commands for ip nexthop set. */ -struct route_map_rule_cmd route_set_ipv6_nexthop_peer_cmd = { - "ipv6 next-hop peer-address", route_set_ipv6_nexthop_peer, - route_set_ipv6_nexthop_peer_compile, route_set_ipv6_nexthop_peer_free}; +static const struct route_map_rule_cmd route_set_ipv6_nexthop_peer_cmd = { + "ipv6 next-hop peer-address", + route_set_ipv6_nexthop_peer, + route_set_ipv6_nexthop_peer_compile, + route_set_ipv6_nexthop_peer_free +}; /* `set ipv4 vpn next-hop A.B.C.D' */ -static route_map_result_t route_set_vpnv4_nexthop(void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_set_vpnv4_nexthop(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { struct in_addr *address; struct bgp_path_info *path; @@ -2878,7 +3427,7 @@ static route_map_result_t route_set_vpnv4_nexthop(void *rule, /* Set next hop value. */ path->attr->mp_nexthop_global_in = *address; - path->attr->mp_nexthop_len = 4; + path->attr->mp_nexthop_len = BGP_ATTR_NHLEN_IPV4; } return RMAP_OKAY; @@ -2903,10 +3452,9 @@ static void *route_set_vpnv4_nexthop_compile(const char *arg) /* `set ipv6 vpn next-hop A.B.C.D' */ -static route_map_result_t route_set_vpnv6_nexthop(void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_set_vpnv6_nexthop(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { struct in6_addr *address; struct bgp_path_info *path; @@ -2947,22 +3495,27 @@ static void route_set_vpn_nexthop_free(void *rule) } /* Route map commands for ipv4 next-hop set. */ -struct route_map_rule_cmd route_set_vpnv4_nexthop_cmd = { - "ipv4 vpn next-hop", route_set_vpnv4_nexthop, - route_set_vpnv4_nexthop_compile, route_set_vpn_nexthop_free}; +static const struct route_map_rule_cmd route_set_vpnv4_nexthop_cmd = { + "ipv4 vpn next-hop", + route_set_vpnv4_nexthop, + route_set_vpnv4_nexthop_compile, + route_set_vpn_nexthop_free +}; /* Route map commands for ipv6 next-hop set. */ -struct route_map_rule_cmd route_set_vpnv6_nexthop_cmd = { - "ipv6 vpn next-hop", route_set_vpnv6_nexthop, - route_set_vpnv6_nexthop_compile, route_set_vpn_nexthop_free}; +static const struct route_map_rule_cmd route_set_vpnv6_nexthop_cmd = { + "ipv6 vpn next-hop", + route_set_vpnv6_nexthop, + route_set_vpnv6_nexthop_compile, + route_set_vpn_nexthop_free +}; /* `set originator-id' */ /* For origin set. */ -static route_map_result_t route_set_originator_id(void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_set_originator_id(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { struct in_addr *address; struct bgp_path_info *path; @@ -3003,9 +3556,11 @@ static void route_set_originator_id_free(void *rule) } /* Set originator-id rule structure. */ -struct route_map_rule_cmd route_set_originator_id_cmd = { - "originator-id", route_set_originator_id, - route_set_originator_id_compile, route_set_originator_id_free, +static const struct route_map_rule_cmd route_set_originator_id_cmd = { + "originator-id", + route_set_originator_id, + route_set_originator_id_compile, + route_set_originator_id_free, }; /* Add bgp route map rule. */ @@ -3014,9 +3569,9 @@ static int bgp_route_match_add(struct vty *vty, const char *command, { VTY_DECLVAR_CONTEXT(route_map_index, index); int retval = CMD_SUCCESS; - int ret; + enum rmap_compile_rets ret; - ret = route_map_add_match(index, command, arg); + ret = route_map_add_match(index, command, arg, type); switch (ret) { case RMAP_RULE_MISSING: vty_out(vty, "%% BGP Can't find rule.\n"); @@ -3027,9 +3582,9 @@ static int bgp_route_match_add(struct vty *vty, const char *command, retval = CMD_WARNING_CONFIG_FAILED; break; case RMAP_COMPILE_SUCCESS: - if (type != RMAP_EVENT_MATCH_ADDED) { - route_map_upd8_dependency(type, arg, index->map->name); - } + /* + * Intentionally doing nothing here. + */ break; } @@ -3041,7 +3596,7 @@ static int bgp_route_match_delete(struct vty *vty, const char *command, const char *arg, route_map_event_t type) { VTY_DECLVAR_CONTEXT(route_map_index, index); - int ret; + enum rmap_compile_rets ret; int retval = CMD_SUCCESS; char *dep_name = NULL; const char *tmpstr; @@ -3060,7 +3615,7 @@ static int bgp_route_match_delete(struct vty *vty, const char *command, rmap_name = XSTRDUP(MTYPE_ROUTE_MAP_NAME, index->map->name); } - ret = route_map_delete_match(index, command, dep_name); + ret = route_map_delete_match(index, command, dep_name, type); switch (ret) { case RMAP_RULE_MISSING: vty_out(vty, "%% BGP Can't find rule.\n"); @@ -3071,8 +3626,9 @@ static int bgp_route_match_delete(struct vty *vty, const char *command, retval = CMD_WARNING_CONFIG_FAILED; break; case RMAP_COMPILE_SUCCESS: - if (type != RMAP_EVENT_MATCH_DELETED && dep_name) - route_map_upd8_dependency(type, dep_name, rmap_name); + /* + * Nothing to do here + */ break; } @@ -3090,8 +3646,6 @@ static void bgp_route_map_process_peer(const char *rmap_name, struct route_map *map, struct peer *peer, int afi, int safi, int route_update) { - - int update; struct bgp_filter *filter; if (!peer || !rmap_name) @@ -3102,52 +3656,16 @@ static void bgp_route_map_process_peer(const char *rmap_name, * in is for non-route-server clients, * out is for all peers */ - if (!CHECK_FLAG(peer->flags, PEER_FLAG_RSERVER_CLIENT)) { - if (filter->map[RMAP_IN].name - && (strcmp(rmap_name, filter->map[RMAP_IN].name) == 0)) { - filter->map[RMAP_IN].map = map; - - if (route_update && peer->status == Established) { - if (CHECK_FLAG(peer->af_flags[afi][safi], - PEER_FLAG_SOFT_RECONFIG)) { - if (bgp_debug_update(peer, NULL, NULL, - 1)) - zlog_debug( - "Processing route_map %s update on " - "peer %s (inbound, soft-reconfig)", - rmap_name, peer->host); - - bgp_soft_reconfig_in(peer, afi, safi); - } else if ( - CHECK_FLAG(peer->cap, - PEER_CAP_REFRESH_OLD_RCV) - || CHECK_FLAG( - peer->cap, - PEER_CAP_REFRESH_NEW_RCV)) { - - if (bgp_debug_update(peer, NULL, NULL, - 1)) - zlog_debug( - "Processing route_map %s update on " - "peer %s (inbound, route-refresh)", - rmap_name, peer->host); - bgp_route_refresh_send(peer, afi, safi, - 0, 0, 0); - } - } - } - } + if (filter->map[RMAP_IN].name + && (strcmp(rmap_name, filter->map[RMAP_IN].name) == 0)) { + filter->map[RMAP_IN].map = map; - if (CHECK_FLAG(peer->flags, PEER_FLAG_RSERVER_CLIENT)) { - update = 0; - - if (update && route_update && peer->status == Established) { + if (route_update && peer->status == Established) { if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_SOFT_RECONFIG)) { if (bgp_debug_update(peer, NULL, NULL, 1)) zlog_debug( - "Processing route_map %s update on " - "peer %s (import, soft-reconfig)", + "Processing route_map %s update on peer %s (inbound, soft-reconfig)", rmap_name, peer->host); bgp_soft_reconfig_in(peer, afi, safi); @@ -3157,13 +3675,11 @@ static void bgp_route_map_process_peer(const char *rmap_name, PEER_CAP_REFRESH_NEW_RCV)) { if (bgp_debug_update(peer, NULL, NULL, 1)) zlog_debug( - "Processing route_map %s update on " - "peer %s (import, route-refresh)", + "Processing route_map %s update on peer %s (inbound, route-refresh)", rmap_name, peer->host); bgp_route_refresh_send(peer, afi, safi, 0, 0, 0); } - /* DD: Else, what else do we do ? Reset peer ? */ } } @@ -3233,8 +3749,9 @@ static void bgp_route_map_process_update(struct bgp *bgp, const char *rmap_name, afi_t afi; safi_t safi; struct peer *peer; - struct bgp_node *bn; + struct bgp_dest *bn; struct bgp_static *bgp_static; + struct bgp_aggregate *aggregate; struct listnode *node, *nnode; struct route_map *map; char buf[INET6_ADDRSTRLEN]; @@ -3283,8 +3800,7 @@ static void bgp_route_map_process_update(struct bgp *bgp, const char *rmap_name, if (BGP_DEBUG(zebra, ZEBRA)) zlog_debug( - "Processing route_map %s update on " - "table map", + "Processing route_map %s update on table map", rmap_name); if (route_update) bgp_zebra_announce_table(bgp, afi, safi); @@ -3293,7 +3809,7 @@ static void bgp_route_map_process_update(struct bgp *bgp, const char *rmap_name, /* For network route-map updates. */ for (bn = bgp_table_top(bgp->route[afi][safi]); bn; bn = bgp_route_next(bn)) { - bgp_static = bgp_node_get_bgp_static_info(bn); + bgp_static = bgp_dest_get_bgp_static_info(bn); if (!bgp_static) continue; @@ -3307,17 +3823,52 @@ static void bgp_route_map_process_update(struct bgp *bgp, const char *rmap_name, bgp_static->rmap.map = map; if (route_update && !bgp_static->backdoor) { - if (bgp_debug_zebra(&bn->p)) + const struct prefix *bn_p = + bgp_dest_get_prefix(bn); + + if (bgp_debug_zebra(bn_p)) zlog_debug( "Processing route_map %s update on static route %s", rmap_name, - inet_ntop(bn->p.family, - &bn->p.u.prefix, buf, + inet_ntop(bn_p->family, + &bn_p->u.prefix, buf, INET6_ADDRSTRLEN)); - bgp_static_update(bgp, &bn->p, bgp_static, afi, + bgp_static_update(bgp, bn_p, bgp_static, afi, safi); } } + + /* For aggregate-address route-map updates. */ + for (bn = bgp_table_top(bgp->aggregate[afi][safi]); bn; + bn = bgp_route_next(bn)) { + aggregate = bgp_dest_get_bgp_aggregate_info(bn); + if (!aggregate) + continue; + + if (!aggregate->rmap.name + || (strcmp(rmap_name, aggregate->rmap.name) != 0)) + continue; + + if (!aggregate->rmap.map) + route_map_counter_increment(map); + + aggregate->rmap.map = map; + + if (route_update) { + const struct prefix *bn_p = + bgp_dest_get_prefix(bn); + + if (bgp_debug_zebra(bn_p)) + zlog_debug( + "Processing route_map %s update on aggregate-address route %s", + rmap_name, + inet_ntop(bn_p->family, + &bn_p->u.prefix, buf, + INET6_ADDRSTRLEN)); + bgp_aggregate_route(bgp, bn_p, afi, safi, + aggregate); + } + } } /* For redistribute route-map updates. */ @@ -3383,7 +3934,7 @@ static void bgp_route_map_process_update_cb(char *rmap_name) for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) { bgp_route_map_process_update(bgp, rmap_name, 1); -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC /* zlog_debug("%s: calling vnc_routemap_update", __func__); */ vnc_routemap_update(bgp, __func__); #endif @@ -3398,36 +3949,39 @@ int bgp_route_map_update_timer(struct thread *thread) route_map_walk_update_list(bgp_route_map_process_update_cb); - return (0); + return 0; } static void bgp_route_map_mark_update(const char *rmap_name) { - if (bm->t_rmap_update == NULL) { - struct listnode *node, *nnode; - struct bgp *bgp; - - /* rmap_update_timer of 0 means don't do route updates */ - if (bm->rmap_update_timer) { - bm->t_rmap_update = NULL; - thread_add_timer(bm->master, bgp_route_map_update_timer, - NULL, bm->rmap_update_timer, - &bm->t_rmap_update); - - /* Signal the groups that a route-map update event has - * started */ - for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) - update_group_policy_update(bgp, - BGP_POLICY_ROUTE_MAP, - rmap_name, 1, 1); - } else { - for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) - bgp_route_map_process_update(bgp, rmap_name, 0); -#if ENABLE_BGP_VNC - zlog_debug("%s: calling vnc_routemap_update", __func__); - vnc_routemap_update(bgp, __func__); + struct listnode *node, *nnode; + struct bgp *bgp; + + /* If new update is received before the current timer timed out, + * turn it off and start a new timer. + */ + if (bm->t_rmap_update != NULL) + THREAD_OFF(bm->t_rmap_update); + + /* rmap_update_timer of 0 means don't do route updates */ + if (bm->rmap_update_timer) { + thread_add_timer(bm->master, bgp_route_map_update_timer, + NULL, bm->rmap_update_timer, + &bm->t_rmap_update); + + /* Signal the groups that a route-map update event has + * started */ + for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) + update_group_policy_update(bgp, + BGP_POLICY_ROUTE_MAP, + rmap_name, 1, 1); + } else { + for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) + bgp_route_map_process_update(bgp, rmap_name, 0); +#ifdef ENABLE_BGP_VNC + zlog_debug("%s: calling vnc_routemap_update", __func__); + vnc_routemap_update(bgp, __func__); #endif - } } } @@ -3447,7 +4001,7 @@ static void bgp_route_map_delete(const char *rmap_name) route_map_notify_dependencies(rmap_name, RMAP_EVENT_MATCH_DELETED); } -static void bgp_route_map_event(route_map_event_t event, const char *rmap_name) +static void bgp_route_map_event(const char *rmap_name) { if (route_map_mark_updated(rmap_name) == 0) bgp_route_map_mark_update(rmap_name); @@ -3558,6 +4112,31 @@ DEFUN (no_match_evpn_default_route, RMAP_EVENT_MATCH_DELETED); } +DEFUN (match_evpn_rd, + match_evpn_rd_cmd, + "match evpn rd ASN:NN_OR_IP-ADDRESS:NN", + MATCH_STR + EVPN_HELP_STR + "Route Distinguisher\n" + "ASN:XX or A.B.C.D:XX\n") +{ + return bgp_route_match_add(vty, "evpn rd", argv[3]->arg, + RMAP_EVENT_MATCH_ADDED); +} + +DEFUN (no_match_evpn_rd, + no_match_evpn_rd_cmd, + "no match evpn rd ASN:NN_OR_IP-ADDRESS:NN", + NO_STR + MATCH_STR + EVPN_HELP_STR + "Route Distinguisher\n" + "ASN:XX or A.B.C.D:XX\n") +{ + return bgp_route_match_delete(vty, "evpn rd", argv[4]->arg, + RMAP_EVENT_MATCH_DELETED); +} + DEFPY(match_vrl_source_vrf, match_vrl_source_vrf_cmd, "match source-vrf NAME$vrf_name", @@ -3836,26 +4415,45 @@ DEFUN (no_match_community, DEFUN (match_lcommunity, match_lcommunity_cmd, - "match large-community <(1-99)|(100-500)|WORD>", + "match large-community <(1-99)|(100-500)|WORD> [exact-match]", MATCH_STR "Match BGP large community list\n" "Large Community-list number (standard)\n" "Large Community-list number (expanded)\n" - "Large Community-list name\n") + "Large Community-list name\n" + "Do exact matching of communities\n") { - return bgp_route_match_add(vty, "large-community", argv[2]->arg, + int idx_lcomm_list = 2; + int ret; + char *argstr; + + if (argc == 4) { + argstr = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, + strlen(argv[idx_lcomm_list]->arg) + + strlen("exact-match") + 2); + + sprintf(argstr, "%s exact-match", argv[idx_lcomm_list]->arg); + } else + argstr = argv[idx_lcomm_list]->arg; + + ret = bgp_route_match_add(vty, "large-community", argstr, RMAP_EVENT_LLIST_ADDED); + if (argstr != argv[idx_lcomm_list]->arg) + XFREE(MTYPE_ROUTE_MAP_COMPILED, argstr); + + return ret; } DEFUN (no_match_lcommunity, no_match_lcommunity_cmd, - "no match large-community [<(1-99)|(100-500)|WORD>]", + "no match large-community [<(1-99)|(100-500)|WORD> [exact-match]]", NO_STR MATCH_STR "Match BGP large community list\n" "Large Community-list number (standard)\n" "Large Community-list number (expanded)\n" - "Large Community-list name\n") + "Large Community-list name\n" + "Do exact matching of communities\n") { return bgp_route_match_delete(vty, "large-community", NULL, RMAP_EVENT_LLIST_DELETED); @@ -3957,6 +4555,32 @@ DEFUN (no_match_origin, RMAP_EVENT_MATCH_DELETED); } +DEFUN (set_table_id, + set_table_id_cmd, + "set table (1-4294967295)", + SET_STR + "export route to non-main kernel table\n" + "Kernel routing table id\n") +{ + int idx_id = 2; + + VTY_DECLVAR_CONTEXT(route_map_index, index); + + return generic_set_add(vty, index, "table", argv[idx_id]->arg); +} + +DEFUN (no_set_table_id, + no_set_table_id_cmd, + "no set table", + NO_STR + SET_STR + "export route to non-main kernel table\n") +{ + VTY_DECLVAR_CONTEXT(route_map_index, index); + + return generic_set_delete(vty, index, "table", NULL); +} + DEFUN (set_ip_nexthop_peer, set_ip_nexthop_peer_cmd, "[no] set ip next-hop peer-address", @@ -3993,13 +4617,36 @@ DEFUN (set_ip_nexthop_unchanged, "unchanged"); } +DEFUN (set_distance, + set_distance_cmd, + "set distance (0-255)", + SET_STR + "BGP Administrative Distance to use\n" + "Distance value\n") +{ + int idx_number = 2; + + return generic_set_add(vty, VTY_GET_CONTEXT(route_map_index), + "distance", argv[idx_number]->arg); +} + +DEFUN (no_set_distance, + no_set_distance_cmd, + "no set distance [(0-255)]", + NO_STR SET_STR + "BGP Administrative Distance to use\n" + "Distance value\n") +{ + return generic_set_delete(vty, VTY_GET_CONTEXT(route_map_index), + "distance", NULL); +} DEFUN (set_local_pref, set_local_pref_cmd, - "set local-preference (0-4294967295)", + "set local-preference WORD", SET_STR "BGP local preference path attribute\n" - "Preference value\n") + "Preference value (0-4294967295)\n") { int idx_number = 2; return generic_set_add(vty, VTY_GET_CONTEXT(route_map_index), @@ -4009,11 +4656,11 @@ DEFUN (set_local_pref, DEFUN (no_set_local_pref, no_set_local_pref_cmd, - "no set local-preference [(0-4294967295)]", + "no set local-preference [WORD]", NO_STR SET_STR "BGP local preference path attribute\n" - "Preference value\n") + "Preference value (0-4294967295)\n") { int idx_localpref = 3; if (argc <= idx_localpref) @@ -4133,6 +4780,18 @@ DEFUN (no_set_aspath_prepend, return ret; } +DEFUN (no_set_aspath_prepend_lastas, + no_set_aspath_prepend_lastas_cmd, + "no set as-path prepend last-as [(1-10)]", + NO_STR + SET_STR + "Transform BGP AS_PATH attribute\n" + "Prepend to the as-path\n" + "Use the peers AS-number\n" + "Number of times to insert\n") +{ + return no_set_aspath_prepend(self, vty, argc, argv); +} DEFUN (set_aspath_exclude, set_aspath_exclude_cmd, @@ -4262,10 +4921,10 @@ DEFUN (set_community, str = community_str(com, false); if (additive) { - argstr = XCALLOC(MTYPE_TMP, - strlen(str) + strlen(" additive") + 1); - strcpy(argstr, str); - strcpy(argstr + strlen(str), " additive"); + size_t argstr_sz = strlen(str) + strlen(" additive") + 1; + argstr = XCALLOC(MTYPE_TMP, argstr_sz); + strlcpy(argstr, str, argstr_sz); + strlcat(argstr, " additive", argstr_sz); ret = generic_set_add(vty, VTY_GET_CONTEXT(route_map_index), "community", argstr); XFREE(MTYPE_TMP, argstr); @@ -4320,9 +4979,12 @@ DEFUN (set_community_delete, "Delete matching communities\n") { int idx_comm_list = 2; + char *args; + args = argv_concat(argv, argc, idx_comm_list); generic_set_add(vty, VTY_GET_CONTEXT(route_map_index), "comm-list", - argv[idx_comm_list]->arg); + args); + XFREE(MTYPE_TMP, args); return CMD_SUCCESS; } @@ -4412,8 +5074,13 @@ DEFUN (set_lcommunity_delete, "Large Community-list name\n" "Delete matching large communities\n") { + int idx_lcomm_list = 2; + char *args; + + args = argv_concat(argv, argc, idx_lcomm_list); generic_set_add(vty, VTY_GET_CONTEXT(route_map_index), - "large-comm-list", argv[2]->arg); + "large-comm-list", args); + XFREE(MTYPE_TMP, args); return CMD_SUCCESS; } @@ -4522,6 +5189,53 @@ ALIAS (no_set_ecommunity_soo, "GP extended community attribute\n" "Site-of-Origin extended community\n") +DEFUN (set_ecommunity_lb, + set_ecommunity_lb_cmd, + "set extcommunity bandwidth <(1-25600)|cumulative|num-multipaths> [non-transitive]", + SET_STR + "BGP extended community attribute\n" + "Link bandwidth extended community\n" + "Bandwidth value in Mbps\n" + "Cumulative bandwidth of all multipaths (outbound-only)\n" + "Internally computed bandwidth based on number of multipaths (outbound-only)\n" + "Attribute is set as non-transitive\n") +{ + int idx_lb = 3; + int ret; + char *str; + + str = argv_concat(argv, argc, idx_lb); + ret = generic_set_add(vty, VTY_GET_CONTEXT(route_map_index), + "extcommunity bandwidth", str); + XFREE(MTYPE_TMP, str); + return ret; +} + + +DEFUN (no_set_ecommunity_lb, + no_set_ecommunity_lb_cmd, + "no set extcommunity bandwidth <(1-25600)|cumulative|num-multipaths> [non-transitive]", + NO_STR + SET_STR + "BGP extended community attribute\n" + "Link bandwidth extended community\n" + "Bandwidth value in Mbps\n" + "Cumulative bandwidth of all multipaths (outbound-only)\n" + "Internally computed bandwidth based on number of multipaths (outbound-only)\n" + "Attribute is set as non-transitive\n") +{ + return generic_set_delete(vty, VTY_GET_CONTEXT(route_map_index), + "extcommunity bandwidth", NULL); +} + +ALIAS (no_set_ecommunity_lb, + no_set_ecommunity_lb_short_cmd, + "no set extcommunity bandwidth", + NO_STR + SET_STR + "BGP extended community attribute\n" + "Link bandwidth extended community\n") + DEFUN (set_origin, set_origin_cmd, "set origin ", @@ -4686,6 +5400,34 @@ DEFUN (no_match_ipv6_next_hop, RMAP_EVENT_MATCH_DELETED); } +DEFPY (match_ipv4_next_hop, + match_ipv4_next_hop_cmd, + "match ip next-hop address A.B.C.D", + MATCH_STR + IP_STR + "Match IP next-hop address of route\n" + "IP address\n" + "IP address of next-hop\n") +{ + int idx_ipv4 = 4; + + return bgp_route_match_add(vty, "ip next-hop address", + argv[idx_ipv4]->arg, RMAP_EVENT_MATCH_ADDED); +} + +DEFPY (no_match_ipv4_next_hop, + no_match_ipv4_next_hop_cmd, + "no match ip next-hop address [A.B.C.D]", + NO_STR + MATCH_STR + IP_STR + "Match IP next-hop address of route\n" + "IP address\n" + "IP address of next-hop\n") +{ + return bgp_route_match_delete(vty, "ip next-hop address", NULL, + RMAP_EVENT_MATCH_DELETED); +} DEFUN (set_ipv6_nexthop_peer, set_ipv6_nexthop_peer_cmd, @@ -4983,6 +5725,9 @@ void bgp_route_map_init(void) route_map_match_tag_hook(generic_match_add); route_map_no_match_tag_hook(generic_match_delete); + route_map_set_srte_color_hook(generic_set_add); + route_map_no_set_srte_color_hook(generic_set_delete); + route_map_set_ip_nexthop_hook(generic_set_add); route_map_no_set_ip_nexthop_hook(generic_set_delete); @@ -5020,14 +5765,18 @@ void bgp_route_map_init(void) route_map_install_match(&route_match_mac_address_cmd); route_map_install_match(&route_match_evpn_vni_cmd); route_map_install_match(&route_match_evpn_route_type_cmd); + route_map_install_match(&route_match_evpn_rd_cmd); route_map_install_match(&route_match_evpn_default_route_cmd); route_map_install_match(&route_match_vrl_source_vrf_cmd); + route_map_install_set(&route_set_table_id_cmd); + route_map_install_set(&route_set_srte_color_cmd); route_map_install_set(&route_set_ip_nexthop_cmd); route_map_install_set(&route_set_local_pref_cmd); route_map_install_set(&route_set_weight_cmd); route_map_install_set(&route_set_label_index_cmd); route_map_install_set(&route_set_metric_cmd); + route_map_install_set(&route_set_distance_cmd); route_map_install_set(&route_set_aspath_prepend_cmd); route_map_install_set(&route_set_aspath_exclude_cmd); route_map_install_set(&route_set_origin_cmd); @@ -5042,6 +5791,7 @@ void bgp_route_map_init(void) route_map_install_set(&route_set_originator_id_cmd); route_map_install_set(&route_set_ecommunity_rt_cmd); route_map_install_set(&route_set_ecommunity_soo_cmd); + route_map_install_set(&route_set_ecommunity_lb_cmd); route_map_install_set(&route_set_tag_cmd); route_map_install_set(&route_set_label_index_cmd); @@ -5058,6 +5808,8 @@ void bgp_route_map_init(void) install_element(RMAP_NODE, &no_match_evpn_vni_cmd); install_element(RMAP_NODE, &match_evpn_route_type_cmd); install_element(RMAP_NODE, &no_match_evpn_route_type_cmd); + install_element(RMAP_NODE, &match_evpn_rd_cmd); + install_element(RMAP_NODE, &no_match_evpn_rd_cmd); install_element(RMAP_NODE, &match_evpn_default_route_cmd); install_element(RMAP_NODE, &no_match_evpn_default_route_cmd); install_element(RMAP_NODE, &match_vrl_source_vrf_cmd); @@ -5078,9 +5830,13 @@ void bgp_route_map_init(void) install_element(RMAP_NODE, &match_probability_cmd); install_element(RMAP_NODE, &no_match_probability_cmd); + install_element(RMAP_NODE, &no_set_table_id_cmd); + install_element(RMAP_NODE, &set_table_id_cmd); install_element(RMAP_NODE, &set_ip_nexthop_peer_cmd); install_element(RMAP_NODE, &set_ip_nexthop_unchanged_cmd); install_element(RMAP_NODE, &set_local_pref_cmd); + install_element(RMAP_NODE, &set_distance_cmd); + install_element(RMAP_NODE, &no_set_distance_cmd); install_element(RMAP_NODE, &no_set_local_pref_cmd); install_element(RMAP_NODE, &set_weight_cmd); install_element(RMAP_NODE, &set_label_index_cmd); @@ -5090,6 +5846,7 @@ void bgp_route_map_init(void) install_element(RMAP_NODE, &set_aspath_prepend_lastas_cmd); install_element(RMAP_NODE, &set_aspath_exclude_cmd); install_element(RMAP_NODE, &no_set_aspath_prepend_cmd); + install_element(RMAP_NODE, &no_set_aspath_prepend_lastas_cmd); install_element(RMAP_NODE, &no_set_aspath_exclude_cmd); install_element(RMAP_NODE, &no_set_aspath_exclude_all_cmd); install_element(RMAP_NODE, &set_origin_cmd); @@ -5118,6 +5875,9 @@ void bgp_route_map_init(void) install_element(RMAP_NODE, &set_ecommunity_soo_cmd); install_element(RMAP_NODE, &no_set_ecommunity_soo_cmd); install_element(RMAP_NODE, &no_set_ecommunity_soo_short_cmd); + install_element(RMAP_NODE, &set_ecommunity_lb_cmd); + install_element(RMAP_NODE, &no_set_ecommunity_lb_cmd); + install_element(RMAP_NODE, &no_set_ecommunity_lb_short_cmd); #ifdef KEEP_OLD_VPN_COMMANDS install_element(RMAP_NODE, &set_vpn_nexthop_cmd); install_element(RMAP_NODE, &no_set_vpn_nexthop_cmd); @@ -5129,6 +5889,7 @@ void bgp_route_map_init(void) route_map_install_match(&route_match_ipv6_address_cmd); route_map_install_match(&route_match_ipv6_next_hop_cmd); + route_map_install_match(&route_match_ipv4_next_hop_cmd); route_map_install_match(&route_match_ipv6_address_prefix_list_cmd); route_map_install_match(&route_match_ipv6_next_hop_type_cmd); route_map_install_set(&route_set_ipv6_nexthop_global_cmd); @@ -5138,6 +5899,8 @@ void bgp_route_map_init(void) install_element(RMAP_NODE, &match_ipv6_next_hop_cmd); install_element(RMAP_NODE, &no_match_ipv6_next_hop_cmd); + install_element(RMAP_NODE, &match_ipv4_next_hop_cmd); + install_element(RMAP_NODE, &no_match_ipv4_next_hop_cmd); install_element(RMAP_NODE, &set_ipv6_nexthop_global_cmd); install_element(RMAP_NODE, &no_set_ipv6_nexthop_global_cmd); install_element(RMAP_NODE, &set_ipv6_nexthop_prefer_global_cmd); diff --git a/bgpd/bgp_rpki.c b/bgpd/bgp_rpki.c index c63d4f9ad2..63cfacf678 100644 --- a/bgpd/bgp_rpki.c +++ b/bgpd/bgp_rpki.c @@ -51,12 +51,6 @@ #include "lib/thread.h" #ifndef VTYSH_EXTRACT_PL #include "rtrlib/rtrlib.h" -#include "rtrlib/rtr_mgr.h" -#include "rtrlib/lib/ip.h" -#include "rtrlib/transport/tcp/tcp_transport.h" -#if defined(FOUND_SSH) -#include "rtrlib/transport/ssh/ssh_transport.h" -#endif #endif #include "hook.h" #include "libfrr.h" @@ -76,8 +70,6 @@ DEFINE_MTYPE_STATIC(BGPD, BGP_RPKI_CACHE_GROUP, "BGP RPKI Cache server group") #define POLLING_PERIOD_DEFAULT 3600 #define EXPIRE_INTERVAL_DEFAULT 7200 #define RETRY_INTERVAL_DEFAULT 600 -#define TIMEOUT_DEFAULT 600 -#define INITIAL_SYNCHRONISATION_TIMEOUT_DEFAULT 30 #define RPKI_DEBUG(...) \ if (rpki_debug) { \ @@ -102,6 +94,7 @@ enum return_values { SUCCESS = 0, ERROR = -1 }; struct rpki_for_each_record_arg { struct vty *vty; unsigned int *prefix_amount; + as_t as; }; static int start(void); @@ -111,7 +104,7 @@ static struct rtr_mgr_group *get_connected_group(void); static void print_prefix_table(struct vty *vty); static void install_cli_commands(void); static int config_write(struct vty *vty); -static void overwrite_exit_commands(void); +static int config_on_exit(struct vty *vty); static void free_cache(struct cache *cache); static struct rtr_mgr_group *get_groups(void); #if defined(FOUND_SSH) @@ -129,30 +122,35 @@ static void print_record(const struct pfx_record *record, struct vty *vty); static int is_synchronized(void); static int is_running(void); static void route_match_free(void *rule); -static route_map_result_t route_match(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object); +static enum route_map_cmd_result_t route_match(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object); static void *route_match_compile(const char *arg); -static void revalidate_bgp_node(struct bgp_node *bgp_node, afi_t afi, - safi_t safi); +static void revalidate_bgp_node(struct bgp_dest *dest, afi_t afi, safi_t safi); static void revalidate_all_routes(void); static struct rtr_mgr_config *rtr_config; static struct list *cache_list; static int rtr_is_running; static int rtr_is_stopping; -static int rtr_is_starting; static _Atomic int rtr_update_overflow; static int rpki_debug; static unsigned int polling_period; static unsigned int expire_interval; static unsigned int retry_interval; -static unsigned int timeout; -static unsigned int initial_synchronisation_timeout; static int rpki_sync_socket_rtr; static int rpki_sync_socket_bgpd; -static struct cmd_node rpki_node = {RPKI_NODE, "%s(config-rpki)# ", 1}; -static struct route_map_rule_cmd route_match_rpki_cmd = { +static struct cmd_node rpki_node = { + .name = "rpki", + .node = RPKI_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-rpki)# ", + .config_write = config_write, + .node_exit = config_on_exit, +}; +static const struct route_map_rule_cmd route_match_rpki_cmd = { "rpki", route_match, route_match_compile, route_match_free}; static void *malloc_wrapper(size_t size) @@ -213,8 +211,10 @@ static void ipv6_addr_to_host_byte_order(const uint32_t *src, uint32_t *dest) dest[i] = ntohl(src[i]); } -static route_map_result_t route_match(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static enum route_map_cmd_result_t route_match(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object) { int *rpki_status = rule; struct bgp_path_info *path; @@ -280,6 +280,17 @@ static void print_record(const struct pfx_record *record, struct vty *vty) record->max_len, record->asn); } +static void print_record_by_asn(const struct pfx_record *record, void *data) +{ + struct rpki_for_each_record_arg *arg = data; + struct vty *vty = arg->vty; + + if (record->asn == arg->as) { + (*arg->prefix_amount)++; + print_record(record, vty); + } +} + static void print_record_cb(const struct pfx_record *record, void *data) { struct rpki_for_each_record_arg *arg = data; @@ -390,42 +401,40 @@ static int bgpd_sync_callback(struct thread *thread) if (!peer->bgp->rib[afi][safi]) continue; - struct list *matches = list_new(); - - matches->del = - (void (*)(void *))bgp_unlock_node; - - bgp_table_range_lookup( - peer->bgp->rib[afi][safi], prefix, - rec.max_len, matches); - + struct bgp_dest *match; + struct bgp_dest *node; - struct bgp_node *bgp_node; - struct listnode *bgp_listnode; + match = bgp_table_subtree_lookup( + peer->bgp->rib[afi][safi], prefix); + node = match; - for (ALL_LIST_ELEMENTS_RO(matches, bgp_listnode, - bgp_node)) - revalidate_bgp_node(bgp_node, afi, - safi); + while (node) { + if (bgp_dest_has_bgp_path_info_data( + node)) { + revalidate_bgp_node(node, afi, + safi); + } - list_delete(&matches); + node = bgp_route_next_until(node, + match); + } } } } - prefix_free(prefix); + prefix_free(&prefix); return 0; } -static void revalidate_bgp_node(struct bgp_node *bgp_node, afi_t afi, +static void revalidate_bgp_node(struct bgp_dest *bgp_dest, afi_t afi, safi_t safi) { struct bgp_adj_in *ain; - for (ain = bgp_node->adj_in; ain; ain = ain->next) { + for (ain = bgp_dest->adj_in; ain; ain = ain->next) { int ret; struct bgp_path_info *path = - bgp_node_get_bgp_path_info(bgp_node); + bgp_dest_get_bgp_path_info(bgp_dest); mpls_label_t *label = NULL; uint32_t num_labels = 0; @@ -433,10 +442,10 @@ static void revalidate_bgp_node(struct bgp_node *bgp_node, afi_t afi, label = path->extra->label; num_labels = path->extra->num_labels; } - ret = bgp_update(ain->peer, &bgp_node->p, ain->addpath_rx_id, - ain->attr, afi, safi, ZEBRA_ROUTE_BGP, - BGP_ROUTE_NORMAL, NULL, label, num_labels, 1, - NULL); + ret = bgp_update(ain->peer, bgp_dest_get_prefix(bgp_dest), + ain->addpath_rx_id, ain->attr, afi, safi, + ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, NULL, label, + num_labels, 1, NULL); if (ret < 0) return; @@ -474,7 +483,7 @@ static void rpki_update_cb_sync_rtr(struct pfx_table *p __attribute__((unused)), const struct pfx_record rec, const bool added __attribute__((unused))) { - if (rtr_is_stopping || rtr_is_starting + if (rtr_is_stopping || atomic_load_explicit(&rtr_update_overflow, memory_order_seq_cst)) return; @@ -535,9 +544,6 @@ static int bgp_rpki_init(struct thread_master *master) polling_period = POLLING_PERIOD_DEFAULT; expire_interval = EXPIRE_INTERVAL_DEFAULT; retry_interval = RETRY_INTERVAL_DEFAULT; - timeout = TIMEOUT_DEFAULT; - initial_synchronisation_timeout = - INITIAL_SYNCHRONISATION_TIMEOUT_DEFAULT; install_cli_commands(); rpki_init_sync_socket(); return 0; @@ -566,11 +572,9 @@ static int bgp_rpki_module_init(void) static int start(void) { - unsigned int waiting_time = 0; int ret; rtr_is_stopping = 0; - rtr_is_starting = 1; rtr_update_overflow = 0; if (list_isempty(cache_list)) { @@ -599,23 +603,6 @@ static int start(void) return ERROR; } rtr_is_running = 1; - RPKI_DEBUG("Waiting for rtr connection to synchronize."); - while (waiting_time++ <= initial_synchronisation_timeout) { - if (rtr_mgr_conf_in_sync(rtr_config)) - break; - - sleep(1); - } - if (rtr_mgr_conf_in_sync(rtr_config)) { - RPKI_DEBUG("Got synchronisation with at least one RPKI cache!"); - RPKI_DEBUG("Forcing revalidation."); - rtr_is_starting = 0; - revalidate_all_routes(); - } else { - RPKI_DEBUG( - "Timeout expired! Proceeding without RPKI validation data."); - rtr_is_starting = 0; - } XFREE(MTYPE_BGP_RPKI_CACHE_GROUP, groups); @@ -650,6 +637,36 @@ static struct rtr_mgr_group *get_connected_group(void) return rtr_mgr_get_first_group(rtr_config); } +static void print_prefix_table_by_asn(struct vty *vty, as_t as) +{ + unsigned int number_of_ipv4_prefixes = 0; + unsigned int number_of_ipv6_prefixes = 0; + struct rtr_mgr_group *group = get_connected_group(); + struct rpki_for_each_record_arg arg; + + arg.vty = vty; + arg.as = as; + + if (!group) { + vty_out(vty, "Cannot find a connected group.\n"); + return; + } + + struct pfx_table *pfx_table = group->sockets[0]->pfx_table; + + vty_out(vty, "RPKI/RTR prefix table\n"); + vty_out(vty, "%-40s %s %s\n", "Prefix", "Prefix Length", "Origin-AS"); + + arg.prefix_amount = &number_of_ipv4_prefixes; + pfx_table_for_each_ipv4_record(pfx_table, print_record_by_asn, &arg); + + arg.prefix_amount = &number_of_ipv6_prefixes; + pfx_table_for_each_ipv6_record(pfx_table, print_record_by_asn, &arg); + + vty_out(vty, "Number of IPv4 Prefixes: %u\n", number_of_ipv4_prefixes); + vty_out(vty, "Number of IPv6 Prefixes: %u\n", number_of_ipv6_prefixes); +} + static void print_prefix_table(struct vty *vty) { struct rpki_for_each_record_arg arg; @@ -737,28 +754,27 @@ static int rpki_validate_prefix(struct peer *peer, struct attr *attr, prefix->prefixlen, &result); // Print Debug output - prefix_string = - inet_ntop(prefix->family, &prefix->u.prefix, buf, BUFSIZ); + prefix_string = prefix2str(prefix, buf, sizeof(buf)); switch (result) { case BGP_PFXV_STATE_VALID: RPKI_DEBUG( - "Validating Prefix %s/%hhu from asn %u Result: VALID", - prefix_string, prefix->prefixlen, as_number); + "Validating Prefix %s from asn %u Result: VALID", + prefix_string, as_number); return RPKI_VALID; case BGP_PFXV_STATE_NOT_FOUND: RPKI_DEBUG( - "Validating Prefix %s/%hhu from asn %u Result: NOT FOUND", - prefix_string, prefix->prefixlen, as_number); + "Validating Prefix %s from asn %u Result: NOT FOUND", + prefix_string, as_number); return RPKI_NOTFOUND; case BGP_PFXV_STATE_INVALID: RPKI_DEBUG( - "Validating Prefix %s/%hhu from asn %u Result: INVALID", - prefix_string, prefix->prefixlen, as_number); + "Validating Prefix %s from asn %u Result: INVALID", + prefix_string, as_number); return RPKI_INVALID; default: RPKI_DEBUG( - "Validating Prefix %s/%hhu from asn %u Result: CANNOT VALIDATE", - prefix_string, prefix->prefixlen, as_number); + "Validating Prefix %s from asn %u Result: CANNOT VALIDATE", + prefix_string, as_number); break; } return 0; @@ -773,8 +789,6 @@ static int add_cache(struct cache *cache) group.sockets_len = 1; group.sockets = &cache->rtr_socket; - listnode_add(cache_list, cache); - if (rtr_is_running) { init_tr_socket(cache); @@ -784,6 +798,8 @@ static int add_cache(struct cache *cache) } } + listnode_add(cache_list, cache); + return SUCCESS; } @@ -792,7 +808,7 @@ static int add_tcp_cache(const char *host, const char *port, { struct rtr_socket *rtr_socket; struct tr_tcp_config *tcp_config = - XMALLOC(MTYPE_BGP_RPKI_CACHE, sizeof(struct tr_tcp_config)); + XCALLOC(MTYPE_BGP_RPKI_CACHE, sizeof(struct tr_tcp_config)); struct tr_socket *tr_socket = XMALLOC(MTYPE_BGP_RPKI_CACHE, sizeof(struct tr_socket)); struct cache *cache = @@ -810,7 +826,12 @@ static int add_tcp_cache(const char *host, const char *port, cache->rtr_socket = rtr_socket; cache->preference = preference; - return add_cache(cache); + int ret = add_cache(cache); + if (ret != SUCCESS) { + free_cache(cache); + } + + return ret; } #if defined(FOUND_SSH) @@ -821,7 +842,7 @@ static int add_ssh_cache(const char *host, const unsigned int port, const uint8_t preference) { struct tr_ssh_config *ssh_config = - XMALLOC(MTYPE_BGP_RPKI_CACHE, sizeof(struct tr_ssh_config)); + XCALLOC(MTYPE_BGP_RPKI_CACHE, sizeof(struct tr_ssh_config)); struct cache *cache = XMALLOC(MTYPE_BGP_RPKI_CACHE, sizeof(struct cache)); struct tr_socket *tr_socket = @@ -846,7 +867,12 @@ static int add_ssh_cache(const char *host, const unsigned int port, cache->rtr_socket = rtr_socket; cache->preference = preference; - return add_cache(cache); + int ret = add_cache(cache); + if (ret != SUCCESS) { + free_cache(cache); + } + + return ret; } #endif @@ -886,9 +912,6 @@ static int config_write(struct vty *vty) vty_out(vty, "!\n"); vty_out(vty, "rpki\n"); vty_out(vty, " rpki polling_period %d\n", polling_period); - vty_out(vty, " rpki timeout %d\n", timeout); - vty_out(vty, " rpki initial-synchronisation-timeout %d\n", - initial_synchronisation_timeout); for (ALL_LIST_ELEMENTS_RO(cache_list, cache_node, cache)) { switch (cache->type) { struct tr_tcp_config *tcp_config; @@ -1037,56 +1060,70 @@ DEFUN (no_rpki_retry_interval, return CMD_SUCCESS; } -DEFPY (rpki_timeout, +#if (CONFDATE > 20200901) +CPP_NOTICE("bgpd: time to remove rpki timeout") +CPP_NOTICE("bgpd: this includes rpki_timeout and rpki_synchronisation_timeout") +#endif + +DEFPY_HIDDEN (rpki_timeout, rpki_timeout_cmd, "rpki timeout (1-4294967295)$to_arg", RPKI_OUTPUT_STRING "Set timeout\n" "Timeout value\n") { - timeout = to_arg; + vty_out(vty, + "This config option is deprecated, and is scheduled for removal.\n"); + vty_out(vty, + "This functionality has also already been removed because it caused bugs and was pointless\n"); return CMD_SUCCESS; } -DEFUN (no_rpki_timeout, +DEFUN_HIDDEN (no_rpki_timeout, no_rpki_timeout_cmd, "no rpki timeout", NO_STR RPKI_OUTPUT_STRING "Set timeout back to default\n") { - timeout = TIMEOUT_DEFAULT; + vty_out(vty, + "This config option is deprecated, and is scheduled for removal.\n"); + vty_out(vty, + "This functionality has also already been removed because it caused bugs and was pointless\n"); return CMD_SUCCESS; } -DEFPY (rpki_synchronisation_timeout, +DEFPY_HIDDEN (rpki_synchronisation_timeout, rpki_synchronisation_timeout_cmd, "rpki initial-synchronisation-timeout (1-4294967295)$ito_arg", RPKI_OUTPUT_STRING "Set a timeout for the initial synchronisation of prefix validation data\n" "Timeout value\n") { - initial_synchronisation_timeout = ito_arg; + vty_out(vty, + "This config option is deprecated, and is scheduled for removal.\n"); + vty_out(vty, + "This functionality has also already been removed because it caused bugs and was pointless\n"); return CMD_SUCCESS; } -DEFUN (no_rpki_synchronisation_timeout, +DEFUN_HIDDEN (no_rpki_synchronisation_timeout, no_rpki_synchronisation_timeout_cmd, "no rpki initial-synchronisation-timeout", NO_STR RPKI_OUTPUT_STRING "Set the initial synchronisation timeout back to default (30 sec.)\n") { - initial_synchronisation_timeout = - INITIAL_SYNCHRONISATION_TIMEOUT_DEFAULT; + vty_out(vty, + "This config option is deprecated, and is scheduled for removal.\n"); + vty_out(vty, + "This functionality has also already been removed because it caused bugs and was pointless\n"); return CMD_SUCCESS; } DEFPY (rpki_cache, rpki_cache_cmd, - "rpki cache " - " " - "preference (1-255)", + "rpki cache preference (1-255)", RPKI_OUTPUT_STRING "Install a cache server to current group\n" "IP address of cache server\n Hostname of cache server\n" @@ -1100,6 +1137,18 @@ DEFPY (rpki_cache, "Preference value\n") { int return_value; + struct listnode *cache_node; + struct cache *current_cache; + + for (ALL_LIST_ELEMENTS_RO(cache_list, cache_node, current_cache)) { + if (current_cache->preference == preference) { + vty_out(vty, + "Cache with preference %ld is already configured\n", + preference); + return CMD_WARNING; + } + } + // use ssh connection if (ssh_uname) { @@ -1110,9 +1159,7 @@ DEFPY (rpki_cache, #else return_value = SUCCESS; vty_out(vty, - "ssh sockets are not supported. " - "Please recompile rtrlib and frr with ssh support. " - "If you want to use it"); + "ssh sockets are not supported. Please recompile rtrlib and frr with ssh support. If you want to use it\n"); #endif } else { // use tcp connection return_value = add_tcp_cache(cache, tcpport, preference); @@ -1145,11 +1192,11 @@ DEFPY (no_rpki_cache, return CMD_WARNING; } - if (rtr_is_running) { + if (rtr_is_running && listcount(cache_list) == 1) { + stop(); + } else if (rtr_is_running) { if (rtr_mgr_remove_group(rtr_config, preference) == RTR_ERROR) { vty_out(vty, "Could not remove cache %ld", preference); - if (listcount(cache_list) == 1) - vty_out(vty, " because it is the last cache"); vty_out(vty, "\n"); return CMD_WARNING; @@ -1185,6 +1232,21 @@ DEFUN (show_rpki_prefix_table, return CMD_SUCCESS; } +DEFPY(show_rpki_as_number, show_rpki_as_number_cmd, + "show rpki as-number (1-4294967295)$by_asn", + SHOW_STR RPKI_OUTPUT_STRING + "Lookup by ASN in prefix table\n" + "AS Number\n") +{ + if (!is_synchronized()) { + vty_out(vty, "No Connection to RPKI cache server.\n"); + return CMD_WARNING; + } + + print_prefix_table_by_asn(vty, by_asn); + return CMD_SUCCESS; +} + DEFPY (show_rpki_prefix, show_rpki_prefix_cmd, "show rpki prefix [(1-4294967295)$asn]", @@ -1197,7 +1259,7 @@ DEFPY (show_rpki_prefix, { if (!is_synchronized()) { - vty_out(vty, "No Conection to RPKI cache server.\n"); + vty_out(vty, "No Connection to RPKI cache server.\n"); return CMD_WARNING; } @@ -1229,7 +1291,8 @@ DEFPY (show_rpki_prefix, const struct pfx_record *record = &matches[i]; if (record->max_len >= prefix->prefixlen - && ((asn != 0 && asn == record->asn) || asn == 0)) { + && ((asn != 0 && (uint32_t)asn == record->asn) + || asn == 0)) { print_record(&matches[i], vty); } } @@ -1253,10 +1316,10 @@ DEFUN (show_rpki_cache_server, cache->tr_config.tcp_config->host, cache->tr_config.tcp_config->port); +#if defined(FOUND_SSH) } else if (cache->type == SSH) { vty_out(vty, - "host: %s port: %d username: %s " - "server_hostkey_path: %s client_privkey_path: %s\n", + "host: %s port: %d username: %s server_hostkey_path: %s client_privkey_path: %s\n", cache->tr_config.ssh_config->host, cache->tr_config.ssh_config->port, cache->tr_config.ssh_config->username, @@ -1264,6 +1327,7 @@ DEFUN (show_rpki_cache_server, ->server_hostkey_path, cache->tr_config.ssh_config ->client_privkey_path); +#endif } } @@ -1329,35 +1393,10 @@ DEFUN (show_rpki_cache_connection, return CMD_SUCCESS; } -DEFUN_NOSH (rpki_exit, - rpki_exit_cmd, - "exit", - "Exit rpki configuration and restart rpki session\n") +static int config_on_exit(struct vty *vty) { reset(false); - - vty->node = CONFIG_NODE; - return CMD_SUCCESS; -} - -DEFUN_NOSH (rpki_quit, - rpki_quit_cmd, - "quit", - "Exit rpki configuration mode\n") -{ - return rpki_exit(self, vty, argc, argv); -} - -DEFUN_NOSH (rpki_end, - rpki_end_cmd, - "end", - "End rpki configuration, restart rpki session and change to enable mode.\n") -{ - int ret = reset(false); - - vty_config_exit(vty); - vty->node = ENABLE_NODE; - return ret == SUCCESS ? CMD_SUCCESS : CMD_WARNING; + return 1; } DEFUN (rpki_reset, @@ -1400,19 +1439,22 @@ DEFUN (match_rpki, "Prefix not found\n") { VTY_DECLVAR_CONTEXT(route_map_index, index); - int ret; + enum rmap_compile_rets ret; - ret = route_map_add_match(index, "rpki", argv[2]->arg); - if (ret) { - switch (ret) { - case RMAP_RULE_MISSING: - vty_out(vty, "%% BGP Can't find rule.\n"); - return CMD_WARNING_CONFIG_FAILED; - case RMAP_COMPILE_ERROR: - vty_out(vty, "%% BGP Argument is malformed.\n"); - return CMD_WARNING_CONFIG_FAILED; - } + ret = route_map_add_match(index, "rpki", argv[2]->arg, + RMAP_EVENT_MATCH_ADDED); + switch (ret) { + case RMAP_RULE_MISSING: + vty_out(vty, "%% BGP Can't find rule.\n"); + return CMD_WARNING_CONFIG_FAILED; + case RMAP_COMPILE_ERROR: + vty_out(vty, "%% BGP Argument is malformed.\n"); + return CMD_WARNING_CONFIG_FAILED; + case RMAP_COMPILE_SUCCESS: + return CMD_SUCCESS; + break; } + return CMD_SUCCESS; } @@ -1427,50 +1469,32 @@ DEFUN (no_match_rpki, "Prefix not found\n") { VTY_DECLVAR_CONTEXT(route_map_index, index); - int ret; + enum rmap_compile_rets ret; - ret = route_map_delete_match(index, "rpki", argv[3]->arg); - if (ret) { - switch (ret) { - case RMAP_RULE_MISSING: - vty_out(vty, "%% BGP Can't find rule.\n"); - break; - case RMAP_COMPILE_ERROR: - vty_out(vty, "%% BGP Argument is malformed.\n"); - break; - } + ret = route_map_delete_match(index, "rpki", argv[3]->arg, + RMAP_EVENT_MATCH_DELETED); + switch (ret) { + case RMAP_RULE_MISSING: + vty_out(vty, "%% BGP Can't find rule.\n"); return CMD_WARNING_CONFIG_FAILED; + break; + case RMAP_COMPILE_ERROR: + vty_out(vty, "%% BGP Argument is malformed.\n"); + return CMD_WARNING_CONFIG_FAILED; + break; + case RMAP_COMPILE_SUCCESS: + return CMD_SUCCESS; + break; } return CMD_SUCCESS; } -static void overwrite_exit_commands(void) -{ - unsigned int i; - vector cmd_vector = rpki_node.cmd_vector; - - for (i = 0; i < cmd_vector->active; ++i) { - struct cmd_element *cmd = vector_lookup(cmd_vector, i); - - if (strcmp(cmd->string, "exit") == 0 - || strcmp(cmd->string, "quit") == 0 - || strcmp(cmd->string, "end") == 0) { - uninstall_element(RPKI_NODE, cmd); - } - } - - install_element(RPKI_NODE, &rpki_exit_cmd); - install_element(RPKI_NODE, &rpki_quit_cmd); - install_element(RPKI_NODE, &rpki_end_cmd); -} - static void install_cli_commands(void) { // TODO: make config write work - install_node(&rpki_node, &config_write); + install_node(&rpki_node); install_default(RPKI_NODE); - overwrite_exit_commands(); install_element(CONFIG_NODE, &rpki_cmd); install_element(ENABLE_NODE, &rpki_cmd); @@ -1509,6 +1533,7 @@ static void install_cli_commands(void) install_element(VIEW_NODE, &show_rpki_cache_connection_cmd); install_element(VIEW_NODE, &show_rpki_cache_server_cmd); install_element(VIEW_NODE, &show_rpki_prefix_cmd); + install_element(VIEW_NODE, &show_rpki_as_number_cmd); /* Install debug commands */ install_element(CONFIG_NODE, &debug_rpki_cmd); diff --git a/bgpd/bgp_snmp.c b/bgpd/bgp_snmp.c index 44cbeabd69..303f4ca56e 100644 --- a/bgpd/bgp_snmp.c +++ b/bgpd/bgp_snmp.c @@ -46,7 +46,7 @@ /* BGP TRAP. */ #define BGPESTABLISHED 1 -#define BGPBACKWARDTRANSITION 2 +#define BGPBACKWARDTRANSITION 2 /* BGP MIB bgpVersion. */ #define BGPVERSION 0 @@ -330,7 +330,7 @@ static uint8_t *bgpVersion(struct variable *v, oid name[], size_t *length, /* Return octet string length 1. */ *var_len = 1; - return (uint8_t *)&version; + return &version; } static uint8_t *bgpLocalAs(struct variable *v, oid name[], size_t *length, @@ -356,17 +356,16 @@ static struct peer *peer_lookup_addr_ipv4(struct in_addr *src) struct bgp *bgp; struct peer *peer; struct listnode *node; + struct listnode *bgpnode; - bgp = bgp_get_default(); - if (!bgp) - return NULL; - - for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer)) { - if (sockunion_family(&peer->su) != AF_INET) - continue; + for (ALL_LIST_ELEMENTS_RO(bm->bgp, bgpnode, bgp)) { + for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer)) { + if (sockunion_family(&peer->su) != AF_INET) + continue; - if (sockunion2ip(&peer->su) == src->s_addr) - return peer; + if (sockunion2ip(&peer->su) == src->s_addr) + return peer; + } } return NULL; @@ -378,21 +377,20 @@ static struct peer *bgp_peer_lookup_next(struct in_addr *src) struct peer *peer; struct peer *next_peer = NULL; struct listnode *node; - - bgp = bgp_get_default(); - if (!bgp) - return NULL; - - for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer)) { - if (sockunion_family(&peer->su) != AF_INET) - continue; - if (ntohl(sockunion2ip(&peer->su)) <= ntohl(src->s_addr)) - continue; - - if (!next_peer - || ntohl(sockunion2ip(&next_peer->su)) - > ntohl(sockunion2ip(&peer->su))) { - next_peer = peer; + struct listnode *bgpnode; + + for (ALL_LIST_ELEMENTS_RO(bm->bgp, bgpnode, bgp)) { + for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer)) { + if (sockunion_family(&peer->su) != AF_INET) + continue; + if (ntohl(sockunion2ip(&peer->su)) <= ntohl(src->s_addr)) + continue; + + if (!next_peer + || ntohl(sockunion2ip(&next_peer->su)) + > ntohl(sockunion2ip(&peer->su))) { + next_peer = peer; + } } } @@ -528,10 +526,8 @@ static uint8_t *bgpPeerTable(struct variable *v, oid name[], size_t *length, switch (v->magic) { case BGPPEERIDENTIFIER: return SNMP_IPADDRESS(peer->remote_id); - break; case BGPPEERSTATE: return SNMP_INTEGER(peer->status); - break; case BGPPEERADMINSTATUS: *write_method = write_bgpPeerTable; #define BGP_PeerAdmin_stop 1 @@ -540,108 +536,87 @@ static uint8_t *bgpPeerTable(struct variable *v, oid name[], size_t *length, return SNMP_INTEGER(BGP_PeerAdmin_stop); else return SNMP_INTEGER(BGP_PeerAdmin_start); - break; case BGPPEERNEGOTIATEDVERSION: return SNMP_INTEGER(BGP_VERSION_4); - break; case BGPPEERLOCALADDR: if (peer->su_local) return SNMP_IPADDRESS(peer->su_local->sin.sin_addr); else return SNMP_IPADDRESS(bgp_empty_addr); - break; case BGPPEERLOCALPORT: if (peer->su_local) return SNMP_INTEGER( ntohs(peer->su_local->sin.sin_port)); else return SNMP_INTEGER(0); - break; case BGPPEERREMOTEADDR: if (peer->su_remote) return SNMP_IPADDRESS(peer->su_remote->sin.sin_addr); else return SNMP_IPADDRESS(bgp_empty_addr); - break; case BGPPEERREMOTEPORT: if (peer->su_remote) return SNMP_INTEGER( ntohs(peer->su_remote->sin.sin_port)); else return SNMP_INTEGER(0); - break; case BGPPEERREMOTEAS: return SNMP_INTEGER(peer->as); - break; case BGPPEERINUPDATES: ui = atomic_load_explicit(&peer->update_in, memory_order_relaxed); return SNMP_INTEGER(ui); - break; case BGPPEEROUTUPDATES: uo = atomic_load_explicit(&peer->update_out, memory_order_relaxed); return SNMP_INTEGER(uo); - break; case BGPPEERINTOTALMESSAGES: return SNMP_INTEGER(PEER_TOTAL_RX(peer)); - break; case BGPPEEROUTTOTALMESSAGES: return SNMP_INTEGER(PEER_TOTAL_TX(peer)); - break; case BGPPEERLASTERROR: { static uint8_t lasterror[2]; lasterror[0] = peer->notify.code; lasterror[1] = peer->notify.subcode; *var_len = 2; return (uint8_t *)&lasterror; - } break; + } case BGPPEERFSMESTABLISHEDTRANSITIONS: return SNMP_INTEGER(peer->established); - break; case BGPPEERFSMESTABLISHEDTIME: if (peer->uptime == 0) return SNMP_INTEGER(0); else return SNMP_INTEGER(bgp_clock() - peer->uptime); - break; case BGPPEERCONNECTRETRYINTERVAL: *write_method = write_bgpPeerTable; return SNMP_INTEGER(peer->v_connect); - break; case BGPPEERHOLDTIME: return SNMP_INTEGER(peer->v_holdtime); - break; case BGPPEERKEEPALIVE: return SNMP_INTEGER(peer->v_keepalive); - break; case BGPPEERHOLDTIMECONFIGURED: *write_method = write_bgpPeerTable; if (CHECK_FLAG(peer->flags, PEER_FLAG_TIMER)) return SNMP_INTEGER(peer->holdtime); else return SNMP_INTEGER(peer->v_holdtime); - break; case BGPPEERKEEPALIVECONFIGURED: *write_method = write_bgpPeerTable; if (CHECK_FLAG(peer->flags, PEER_FLAG_TIMER)) return SNMP_INTEGER(peer->keepalive); else return SNMP_INTEGER(peer->v_keepalive); - break; case BGPPEERMINROUTEADVERTISEMENTINTERVAL: *write_method = write_bgpPeerTable; return SNMP_INTEGER(peer->v_routeadv); - break; case BGPPEERINUPDATEELAPSEDTIME: if (peer->update_time == 0) return SNMP_INTEGER(0); else return SNMP_INTEGER(bgp_clock() - peer->update_time); - break; default: return NULL; - break; } return NULL; } @@ -683,7 +658,7 @@ static struct bgp_path_info *bgp4PathAttrLookup(struct variable *v, oid name[], int offsetlen; struct bgp_path_info *path; struct bgp_path_info *min; - struct bgp_node *rn; + struct bgp_dest *dest; union sockunion su; unsigned int len; struct in_addr paddr; @@ -710,12 +685,12 @@ static struct bgp_path_info *bgp4PathAttrLookup(struct variable *v, oid name[], oid2in_addr(offset, IN_ADDR_SIZE, &su.sin.sin_addr); /* Lookup node. */ - rn = bgp_node_lookup(bgp->rib[AFI_IP][SAFI_UNICAST], - (struct prefix *)addr); - if (rn) { - bgp_unlock_node(rn); + dest = bgp_node_lookup(bgp->rib[AFI_IP][SAFI_UNICAST], + (struct prefix *)addr); + if (dest) { + bgp_dest_unlock_node(dest); - for (path = bgp_node_get_bgp_path_info(rn); path; + for (path = bgp_dest_get_bgp_path_info(dest); path; path = path->next) if (sockunion_same(&path->peer->su, &su)) return path; @@ -726,7 +701,7 @@ static struct bgp_path_info *bgp4PathAttrLookup(struct variable *v, oid name[], len = offsetlen; if (offsetlen == 0) - rn = bgp_table_top(bgp->rib[AFI_IP][SAFI_UNICAST]); + dest = bgp_table_top(bgp->rib[AFI_IP][SAFI_UNICAST]); else { if (len > IN_ADDR_SIZE) len = IN_ADDR_SIZE; @@ -741,8 +716,8 @@ static struct bgp_path_info *bgp4PathAttrLookup(struct variable *v, oid name[], else addr->prefixlen = len * 8; - rn = bgp_node_get(bgp->rib[AFI_IP][SAFI_UNICAST], - (struct prefix *)addr); + dest = bgp_node_get(bgp->rib[AFI_IP][SAFI_UNICAST], + (struct prefix *)addr); offset++; offsetlen--; @@ -755,15 +730,15 @@ static struct bgp_path_info *bgp4PathAttrLookup(struct variable *v, oid name[], oid2in_addr(offset, len, &paddr); } else - paddr.s_addr = 0; + paddr.s_addr = INADDR_ANY; - if (!rn) + if (!dest) return NULL; do { min = NULL; - for (path = bgp_node_get_bgp_path_info(rn); path; + for (path = bgp_dest_get_bgp_path_info(dest); path; path = path->next) { if (path->peer->su.sin.sin_family == AF_INET && ntohl(paddr.s_addr) @@ -784,28 +759,31 @@ static struct bgp_path_info *bgp4PathAttrLookup(struct variable *v, oid name[], } if (min) { + const struct prefix *rn_p = + bgp_dest_get_prefix(dest); + *length = v->namelen + BGP_PATHATTR_ENTRY_OFFSET; offset = name + v->namelen; - oid_copy_addr(offset, &rn->p.u.prefix4, + oid_copy_addr(offset, &rn_p->u.prefix4, IN_ADDR_SIZE); offset += IN_ADDR_SIZE; - *offset = rn->p.prefixlen; + *offset = rn_p->prefixlen; offset++; oid_copy_addr(offset, &min->peer->su.sin.sin_addr, IN_ADDR_SIZE); - addr->prefix = rn->p.u.prefix4; - addr->prefixlen = rn->p.prefixlen; + addr->prefix = rn_p->u.prefix4; + addr->prefixlen = rn_p->prefixlen; - bgp_unlock_node(rn); + bgp_dest_unlock_node(dest); return min; } - paddr.s_addr = 0; - } while ((rn = bgp_route_next(rn)) != NULL); + paddr.s_addr = INADDR_ANY; + } while ((dest = bgp_route_next(dest)) != NULL); } return NULL; } @@ -834,40 +812,28 @@ static uint8_t *bgp4PathAttrTable(struct variable *v, oid name[], switch (v->magic) { case BGP4PATHATTRPEER: /* 1 */ return SNMP_IPADDRESS(path->peer->su.sin.sin_addr); - break; case BGP4PATHATTRIPADDRPREFIXLEN: /* 2 */ return SNMP_INTEGER(addr.prefixlen); - break; case BGP4PATHATTRIPADDRPREFIX: /* 3 */ return SNMP_IPADDRESS(addr.prefix); - break; case BGP4PATHATTRORIGIN: /* 4 */ return SNMP_INTEGER(path->attr->origin); - break; case BGP4PATHATTRASPATHSEGMENT: /* 5 */ return aspath_snmp_pathseg(path->attr->aspath, var_len); - break; case BGP4PATHATTRNEXTHOP: /* 6 */ return SNMP_IPADDRESS(path->attr->nexthop); - break; case BGP4PATHATTRMULTIEXITDISC: /* 7 */ return SNMP_INTEGER(path->attr->med); - break; case BGP4PATHATTRLOCALPREF: /* 8 */ return SNMP_INTEGER(path->attr->local_pref); - break; case BGP4PATHATTRATOMICAGGREGATE: /* 9 */ return SNMP_INTEGER(1); - break; case BGP4PATHATTRAGGREGATORAS: /* 10 */ return SNMP_INTEGER(path->attr->aggregator_as); - break; case BGP4PATHATTRAGGREGATORADDR: /* 11 */ return SNMP_IPADDRESS(path->attr->aggregator_addr); - break; case BGP4PATHATTRCALCLOCALPREF: /* 12 */ return SNMP_INTEGER(-1); - break; case BGP4PATHATTRBEST: /* 13 */ #define BGP4_PathAttrBest_false 1 #define BGP4_PathAttrBest_true 2 @@ -875,11 +841,9 @@ static uint8_t *bgp4PathAttrTable(struct variable *v, oid name[], return SNMP_INTEGER(BGP4_PathAttrBest_true); else return SNMP_INTEGER(BGP4_PathAttrBest_false); - break; case BGP4PATHATTRUNKNOWN: /* 14 */ *var_len = 0; return NULL; - break; } return NULL; } @@ -894,6 +858,10 @@ static int bgpTrapEstablished(struct peer *peer) struct in_addr addr; oid index[sizeof(oid) * IN_ADDR_SIZE]; + /* Check if this peer just went to Established */ + if ((peer->ostatus != OpenConfirm) || !(peer_established(peer))) + return 0; + ret = inet_aton(peer->host, &addr); if (ret == 0) return 0; @@ -902,7 +870,7 @@ static int bgpTrapEstablished(struct peer *peer) smux_trap(bgp_variables, array_size(bgp_variables), bgp_trap_oid, array_size(bgp_trap_oid), bgp_oid, - sizeof bgp_oid / sizeof(oid), index, IN_ADDR_SIZE, + sizeof(bgp_oid) / sizeof(oid), index, IN_ADDR_SIZE, bgpTrapList, array_size(bgpTrapList), BGPESTABLISHED); return 0; } @@ -921,7 +889,7 @@ static int bgpTrapBackwardTransition(struct peer *peer) smux_trap(bgp_variables, array_size(bgp_variables), bgp_trap_oid, array_size(bgp_trap_oid), bgp_oid, - sizeof bgp_oid / sizeof(oid), index, IN_ADDR_SIZE, + sizeof(bgp_oid) / sizeof(oid), index, IN_ADDR_SIZE, bgpTrapList, array_size(bgpTrapList), BGPBACKWARDTRANSITION); return 0; } @@ -935,7 +903,7 @@ static int bgp_snmp_init(struct thread_master *tm) static int bgp_snmp_module_init(void) { - hook_register(peer_established, bgpTrapEstablished); + hook_register(peer_status_changed, bgpTrapEstablished); hook_register(peer_backward_transition, bgpTrapBackwardTransition); hook_register(frr_late_init, bgp_snmp_init); return 0; diff --git a/bgpd/bgp_table.c b/bgpd/bgp_table.c index ecde71279d..185cb251c7 100644 --- a/bgpd/bgp_table.c +++ b/bgpd/bgp_table.c @@ -69,7 +69,7 @@ static struct route_node *bgp_node_create(route_table_delegate_t *delegate, node = XCALLOC(MTYPE_BGP_NODE, sizeof(struct bgp_node)); RB_INIT(bgp_adj_out_rb, &node->adj_out); - return bgp_node_to_rnode(node); + return bgp_dest_to_rnode(node); } /* @@ -80,7 +80,7 @@ static void bgp_node_destroy(route_table_delegate_t *delegate, { struct bgp_node *bgp_node; struct bgp_table *rt; - bgp_node = bgp_node_from_rnode(node); + bgp_node = bgp_dest_from_rnode(node); rt = table->info; if (rt->bgp) { @@ -127,65 +127,79 @@ struct bgp_table *bgp_table_init(struct bgp *bgp, afi_t afi, safi_t safi) return rt; } -static struct bgp_node * -bgp_route_next_until_maxlen(struct bgp_node *node, const struct bgp_node *limit, - const uint8_t maxlen) +/* Delete the route node from the selection deferral route list */ +void bgp_delete_listnode(struct bgp_node *node) { - if (node->l_left && node->p.prefixlen < maxlen - && node->l_left->p.prefixlen <= maxlen) { - return bgp_node_from_rnode(node->l_left); - } - if (node->l_right && node->p.prefixlen < maxlen - && node->l_right->p.prefixlen <= maxlen) { - return bgp_node_from_rnode(node->l_right); - } - - while (node->parent && node != limit) { - if (bgp_node_from_rnode(node->parent->l_left) == node - && node->parent->l_right) { - return bgp_node_from_rnode(node->parent->l_right); + struct route_node *rn = NULL; + struct bgp_table *table = NULL; + struct bgp *bgp = NULL; + afi_t afi; + safi_t safi; + + /* If the route to be deleted is selection pending, update the + * route node in gr_info + */ + if (CHECK_FLAG(node->flags, BGP_NODE_SELECT_DEFER)) { + table = bgp_dest_table(node); + + if (table) { + bgp = table->bgp; + afi = table->afi; + safi = table->safi; + } else + return; + + rn = bgp_dest_to_rnode(node); + + if (bgp && rn && rn->lock == 1) { + /* Delete the route from the selection pending list */ + if ((node->rt_node) + && (bgp->gr_info[afi][safi].route_list)) { + list_delete_node( + bgp->gr_info[afi][safi].route_list, + node->rt_node); + node->rt_node = NULL; + } } - node = bgp_node_from_rnode(node->parent); } - return NULL; } -void bgp_table_range_lookup(const struct bgp_table *table, struct prefix *p, - uint8_t maxlen, struct list *matches) +struct bgp_node *bgp_table_subtree_lookup(const struct bgp_table *table, + const struct prefix *p) { - struct bgp_node *node = bgp_node_from_rnode(table->route_table->top); + struct bgp_node *node = bgp_dest_from_rnode(table->route_table->top); struct bgp_node *matched = NULL; - while (node && node->p.prefixlen <= p->prefixlen - && prefix_match(&node->p, p)) { - if (bgp_node_has_bgp_path_info_data(node) - && node->p.prefixlen == p->prefixlen) { + if (node == NULL) + return NULL; + + + while (node) { + const struct prefix *node_p = bgp_dest_get_prefix(node); + + if (node_p->prefixlen >= p->prefixlen) { + if (!prefix_match(p, node_p)) + return NULL; + matched = node; break; } - node = bgp_node_from_rnode(node->link[prefix_bit( - &p->u.prefix, node->p.prefixlen)]); - } - if (node == NULL) - return; + if (!prefix_match(node_p, p)) + return NULL; - if ((matched == NULL && node->p.prefixlen > maxlen) || !node->parent) - return; - else if (matched == NULL) - matched = node = bgp_node_from_rnode(node->parent); + if (node_p->prefixlen == p->prefixlen) { + matched = node; + break; + } - if (bgp_node_has_bgp_path_info_data(matched)) { - bgp_lock_node(matched); - listnode_add(matches, matched); + node = bgp_dest_from_rnode(node->link[prefix_bit( + &p->u.prefix, node_p->prefixlen)]); } - while ((node = bgp_route_next_until_maxlen(node, matched, maxlen))) { - if (prefix_match(p, &node->p)) { - if (bgp_node_has_bgp_path_info_data(node)) { - bgp_lock_node(node); - listnode_add(matches, node); - } - } - } + if (!matched) + return NULL; + + bgp_dest_lock_node(matched); + return matched; } diff --git a/bgpd/bgp_table.h b/bgpd/bgp_table.h index 040e83a8cd..cf0086b52e 100644 --- a/bgpd/bgp_table.h +++ b/bgpd/bgp_table.h @@ -21,6 +21,10 @@ #ifndef _QUAGGA_BGP_TABLE_H #define _QUAGGA_BGP_TABLE_H +/* XXX BEGIN TEMPORARY COMPAT */ +#define bgp_dest bgp_node +/* XXX END TEMPORARY COMPAT */ + #include "mpls.h" #include "table.h" #include "queue.h" @@ -42,6 +46,33 @@ struct bgp_table { uint64_t version; }; +enum bgp_path_selection_reason { + bgp_path_selection_none, + bgp_path_selection_first, + bgp_path_selection_evpn_sticky_mac, + bgp_path_selection_evpn_seq, + bgp_path_selection_evpn_local_path, + bgp_path_selection_evpn_non_proxy, + bgp_path_selection_evpn_lower_ip, + bgp_path_selection_weight, + bgp_path_selection_local_pref, + bgp_path_selection_local_route, + bgp_path_selection_confed_as_path, + bgp_path_selection_as_path, + bgp_path_selection_origin, + bgp_path_selection_med, + bgp_path_selection_peer, + bgp_path_selection_confed, + bgp_path_selection_igp_metric, + bgp_path_selection_older, + bgp_path_selection_router_id, + bgp_path_selection_cluster_length, + bgp_path_selection_stale, + bgp_path_selection_local_configured, + bgp_path_selection_neighbor_ip, + bgp_path_selection_default, +}; + struct bgp_node { /* * CAUTION @@ -57,9 +88,9 @@ struct bgp_node { struct bgp_adj_in *adj_in; - struct bgp_node *prn; + struct bgp_dest *pdest; - STAILQ_ENTRY(bgp_node) pq; + STAILQ_ENTRY(bgp_dest) pq; uint64_t version; @@ -70,10 +101,15 @@ struct bgp_node { #define BGP_NODE_USER_CLEAR (1 << 1) #define BGP_NODE_LABEL_CHANGED (1 << 2) #define BGP_NODE_REGISTERED_FOR_LABEL (1 << 3) - +#define BGP_NODE_SELECT_DEFER (1 << 4) + /* list node pointer */ + struct listnode *rt_node; struct bgp_addpath_node_data tx_addpath; + + enum bgp_path_selection_reason reason; }; +extern void bgp_delete_listnode(struct bgp_dest *dest); /* * bgp_table_iter_t * @@ -91,149 +127,161 @@ extern void bgp_table_finish(struct bgp_table **); /* - * bgp_node_from_rnode + * bgp_dest_from_rnode * - * Returns the bgp_node structure corresponding to a route_node. + * Returns the bgp_dest structure corresponding to a route_node. */ -static inline struct bgp_node *bgp_node_from_rnode(struct route_node *rnode) +static inline struct bgp_dest *bgp_dest_from_rnode(struct route_node *rnode) { - return (struct bgp_node *)rnode; + return (struct bgp_dest *)rnode; } /* - * bgp_node_to_rnode + * bgp_dest_to_rnode * - * Returns the route_node structure corresponding to a bgp_node. + * Returns the route_node structure corresponding to a bgp_dest. */ -static inline struct route_node *bgp_node_to_rnode(struct bgp_node *node) +static inline struct route_node *bgp_dest_to_rnode(const struct bgp_dest *dest) { - return (struct route_node *)node; + return (struct route_node *)dest; } /* - * bgp_node_table + * bgp_dest_table * - * Returns the bgp_table that the given node is in. + * Returns the bgp_table that the given dest is in. */ -static inline struct bgp_table *bgp_node_table(struct bgp_node *node) +static inline struct bgp_table *bgp_dest_table(struct bgp_dest *dest) { - return route_table_get_info(bgp_node_to_rnode(node)->table); + return route_table_get_info(bgp_dest_to_rnode(dest)->table); } /* - * bgp_node_parent_nolock + * bgp_dest_parent_nolock * - * Gets the parent node of the given node without locking it. + * Gets the parent dest of the given node without locking it. */ -static inline struct bgp_node *bgp_node_parent_nolock(struct bgp_node *node) +static inline struct bgp_dest *bgp_dest_parent_nolock(struct bgp_dest *dest) { - return bgp_node_from_rnode(node->parent); + struct route_node *rn = bgp_dest_to_rnode(dest)->parent; + + return bgp_dest_from_rnode(rn); } /* - * bgp_unlock_node + * bgp_dest_unlock_node */ -static inline void bgp_unlock_node(struct bgp_node *node) +static inline void bgp_dest_unlock_node(struct bgp_dest *dest) { - route_unlock_node(bgp_node_to_rnode(node)); + bgp_delete_listnode(dest); + route_unlock_node(bgp_dest_to_rnode(dest)); } /* * bgp_table_top_nolock * - * Gets the top node in the table without locking it. + * Gets the top dest in the table without locking it. * * @see bgp_table_top */ -static inline struct bgp_node * +static inline struct bgp_dest * bgp_table_top_nolock(const struct bgp_table *const table) { - return bgp_node_from_rnode(table->route_table->top); + return bgp_dest_from_rnode(table->route_table->top); } /* * bgp_table_top */ -static inline struct bgp_node * +static inline struct bgp_dest * bgp_table_top(const struct bgp_table *const table) { - return bgp_node_from_rnode(route_top(table->route_table)); + return bgp_dest_from_rnode(route_top(table->route_table)); } /* * bgp_route_next */ -static inline struct bgp_node *bgp_route_next(struct bgp_node *node) +static inline struct bgp_dest *bgp_route_next(struct bgp_dest *dest) { - return bgp_node_from_rnode(route_next(bgp_node_to_rnode(node))); + return bgp_dest_from_rnode(route_next(bgp_dest_to_rnode(dest))); } /* * bgp_route_next_until */ -static inline struct bgp_node *bgp_route_next_until(struct bgp_node *node, - struct bgp_node *limit) +static inline struct bgp_dest *bgp_route_next_until(struct bgp_dest *dest, + struct bgp_dest *limit) { struct route_node *rnode; - rnode = route_next_until(bgp_node_to_rnode(node), - bgp_node_to_rnode(limit)); - return bgp_node_from_rnode(rnode); + rnode = route_next_until(bgp_dest_to_rnode(dest), + bgp_dest_to_rnode(limit)); + + return bgp_dest_from_rnode(rnode); } /* * bgp_node_get */ -static inline struct bgp_node *bgp_node_get(struct bgp_table *const table, - struct prefix *p) +static inline struct bgp_dest *bgp_node_get(struct bgp_table *const table, + const struct prefix *p) { - return bgp_node_from_rnode(route_node_get(table->route_table, p)); + return bgp_dest_from_rnode(route_node_get(table->route_table, p)); } /* * bgp_node_lookup */ -static inline struct bgp_node * -bgp_node_lookup(const struct bgp_table *const table, struct prefix *p) +static inline struct bgp_dest * +bgp_node_lookup(const struct bgp_table *const table, const struct prefix *p) { - return bgp_node_from_rnode(route_node_lookup(table->route_table, p)); + struct route_node *rn = route_node_lookup(table->route_table, p); + + return bgp_dest_from_rnode(rn); } /* - * bgp_lock_node + * bgp_dest_lock_node */ -static inline struct bgp_node *bgp_lock_node(struct bgp_node *node) +static inline struct bgp_dest *bgp_dest_lock_node(struct bgp_dest *dest) { - return bgp_node_from_rnode(route_lock_node(bgp_node_to_rnode(node))); + struct route_node *rn = route_lock_node(bgp_dest_to_rnode(dest)); + + return bgp_dest_from_rnode(rn); } /* * bgp_node_match */ -static inline struct bgp_node *bgp_node_match(const struct bgp_table *table, - struct prefix *p) +static inline struct bgp_dest *bgp_node_match(const struct bgp_table *table, + const struct prefix *p) { - return bgp_node_from_rnode(route_node_match(table->route_table, p)); + struct route_node *rn = route_node_match(table->route_table, p); + + return bgp_dest_from_rnode(rn); } /* * bgp_node_match_ipv4 */ -static inline struct bgp_node * +static inline struct bgp_dest * bgp_node_match_ipv4(const struct bgp_table *table, struct in_addr *addr) { - return bgp_node_from_rnode( - route_node_match_ipv4(table->route_table, addr)); + struct route_node *rn = route_node_match_ipv4(table->route_table, addr); + + return bgp_dest_from_rnode(rn); } /* * bgp_node_match_ipv6 */ -static inline struct bgp_node * +static inline struct bgp_dest * bgp_node_match_ipv6(const struct bgp_table *table, struct in6_addr *addr) { - return bgp_node_from_rnode( - route_node_match_ipv6(table->route_table, addr)); + struct route_node *rn = route_node_match_ipv6(table->route_table, addr); + + return bgp_dest_from_rnode(rn); } static inline unsigned long bgp_table_count(const struct bgp_table *const table) @@ -244,10 +292,10 @@ static inline unsigned long bgp_table_count(const struct bgp_table *const table) /* * bgp_table_get_next */ -static inline struct bgp_node *bgp_table_get_next(const struct bgp_table *table, - struct prefix *p) +static inline struct bgp_dest *bgp_table_get_next(const struct bgp_table *table, + const struct prefix *p) { - return bgp_node_from_rnode(route_table_get_next(table->route_table, p)); + return bgp_dest_from_rnode(route_table_get_next(table->route_table, p)); } /* @@ -264,9 +312,9 @@ static inline void bgp_table_iter_init(bgp_table_iter_t *iter, /* * bgp_table_iter_next */ -static inline struct bgp_node *bgp_table_iter_next(bgp_table_iter_t *iter) +static inline struct bgp_dest *bgp_table_iter_next(bgp_table_iter_t *iter) { - return bgp_node_from_rnode(route_table_iter_next(&iter->rt_iter)); + return bgp_dest_from_rnode(route_table_iter_next(&iter->rt_iter)); } /* @@ -315,99 +363,114 @@ static inline uint64_t bgp_table_version(struct bgp_table *table) return table->version; } -void bgp_table_range_lookup(const struct bgp_table *table, struct prefix *p, - uint8_t maxlen, struct list *matches); - +/* Find the subtree of the prefix p + * + * This will return the first node that belongs the the subtree of p. Including + * p itself, if it is in the tree. + * + * If the subtree is not present in the table, NULL is returned. + */ +struct bgp_dest *bgp_table_subtree_lookup(const struct bgp_table *table, + const struct prefix *p); static inline struct bgp_aggregate * -bgp_node_get_bgp_aggregate_info(struct bgp_node *node) +bgp_dest_get_bgp_aggregate_info(struct bgp_dest *dest) { - return node->info; + return dest ? dest->info : NULL; } static inline void -bgp_node_set_bgp_aggregate_info(struct bgp_node *node, +bgp_dest_set_bgp_aggregate_info(struct bgp_dest *dest, struct bgp_aggregate *aggregate) { - node->info = aggregate; + dest->info = aggregate; } static inline struct bgp_distance * -bgp_node_get_bgp_distance_info(struct bgp_node *node) +bgp_dest_get_bgp_distance_info(struct bgp_dest *dest) { - return node->info; + return dest ? dest->info : NULL; } -static inline void bgp_node_set_bgp_distance_info(struct bgp_node *node, +static inline void bgp_dest_set_bgp_distance_info(struct bgp_dest *dest, struct bgp_distance *distance) { - node->info = distance; + dest->info = distance; } static inline struct bgp_static * -bgp_node_get_bgp_static_info(struct bgp_node *node) +bgp_dest_get_bgp_static_info(struct bgp_dest *dest) { - return node->info; + return dest ? dest->info : NULL; } -static inline void bgp_node_set_bgp_static_info(struct bgp_node *node, +static inline void bgp_dest_set_bgp_static_info(struct bgp_dest *dest, struct bgp_static *bgp_static) { - node->info = bgp_static; + dest->info = bgp_static; } static inline struct bgp_connected_ref * -bgp_node_get_bgp_connected_ref_info(struct bgp_node *node) +bgp_dest_get_bgp_connected_ref_info(struct bgp_dest *dest) { - return node->info; + return dest ? dest->info : NULL; } static inline void -bgp_node_set_bgp_connected_ref_info(struct bgp_node *node, +bgp_dest_set_bgp_connected_ref_info(struct bgp_dest *dest, struct bgp_connected_ref *bc) { - node->info = bc; + dest->info = bc; } static inline struct bgp_nexthop_cache * -bgp_node_get_bgp_nexthop_info(struct bgp_node *node) +bgp_dest_get_bgp_nexthop_info(struct bgp_dest *dest) { - return node->info; + return dest ? dest->info : NULL; } -static inline void bgp_node_set_bgp_nexthop_info(struct bgp_node *node, - struct bgp_nexthop_cache *bnc) +static inline void bgp_dest_set_bgp_nexthop_info(struct bgp_dest *dest, + struct bgp_nexthop_cache *bnc) { - node->info = bnc; + dest->info = bnc; } static inline struct bgp_path_info * -bgp_node_get_bgp_path_info(struct bgp_node *node) +bgp_dest_get_bgp_path_info(struct bgp_dest *dest) { - return node->info; + return dest ? dest->info : NULL; } -static inline void bgp_node_set_bgp_path_info(struct bgp_node *node, +static inline void bgp_dest_set_bgp_path_info(struct bgp_dest *dest, struct bgp_path_info *bi) { - node->info = bi; + dest->info = bi; } static inline struct bgp_table * -bgp_node_get_bgp_table_info(struct bgp_node *node) +bgp_dest_get_bgp_table_info(struct bgp_dest *dest) { - return node->info; + return dest->info; } -static inline void bgp_node_set_bgp_table_info(struct bgp_node *node, +static inline void bgp_dest_set_bgp_table_info(struct bgp_dest *dest, struct bgp_table *table) { - node->info = table; + dest->info = table; } -static inline bool bgp_node_has_bgp_path_info_data(struct bgp_node *node) +static inline bool bgp_dest_has_bgp_path_info_data(struct bgp_dest *dest) { - return !!node->info; + return !!dest->info; } +static inline const struct prefix *bgp_dest_get_prefix(const struct bgp_dest *dest) +{ + return &dest->p; +} + +#ifdef _FRR_ATTRIBUTE_PRINTFRR +#pragma FRR printfrr_ext "%pRN" (struct bgp_node *) +#endif + #endif /* _QUAGGA_BGP_TABLE_H */ diff --git a/bgpd/bgp_updgrp.c b/bgpd/bgp_updgrp.c index 57717bf59b..d2e563b237 100644 --- a/bgpd/bgp_updgrp.c +++ b/bgpd/bgp_updgrp.c @@ -111,7 +111,6 @@ static void sync_init(struct update_subgroup *subgrp) static void sync_delete(struct update_subgroup *subgrp) { XFREE(MTYPE_BGP_SYNCHRONISE, subgrp->sync); - subgrp->sync = NULL; if (subgrp->hash) hash_free(subgrp->hash); subgrp->hash = NULL; @@ -143,6 +142,7 @@ static void conf_copy(struct peer *dst, struct peer *src, afi_t afi, dst->v_routeadv = src->v_routeadv; dst->flags = src->flags; dst->af_flags[afi][safi] = src->af_flags[afi][safi]; + dst->pmax_out[afi][safi] = src->pmax_out[afi][safi]; XFREE(MTYPE_BGP_PEER_HOST, dst->host); dst->host = XSTRDUP(MTYPE_BGP_PEER_HOST, src->host); @@ -219,7 +219,6 @@ static void conf_release(struct peer *src, afi_t afi, safi_t safi) XFREE(MTYPE_BGP_FILTER_NAME, srcfilter->usmap.name); XFREE(MTYPE_BGP_PEER_HOST, src->host); - src->host = NULL; } static void peer2_updgrp_copy(struct update_group *updgrp, struct peer_af *paf) @@ -288,7 +287,7 @@ static void *updgrp_hash_alloc(void *p) * 16. Local-as should match, if configured. * ) */ -static unsigned int updgrp_hash_key_make(void *p) +static unsigned int updgrp_hash_key_make(const void *p) { const struct update_group *updgrp; const struct peer *peer; @@ -374,11 +373,13 @@ static unsigned int updgrp_hash_key_make(void *p) * There are certain peers that must get their own update-group: * - lonesoul peers * - peers that negotiated ORF + * - maximum-prefix-out is set */ if (CHECK_FLAG(peer->flags, PEER_FLAG_LONESOUL) || CHECK_FLAG(peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_RCV) || CHECK_FLAG(peer->af_cap[afi][safi], - PEER_CAP_ORF_PREFIX_SM_OLD_RCV)) + PEER_CAP_ORF_PREFIX_SM_OLD_RCV) + || CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_OUT)) key = jhash_1word(jhash(peer->host, strlen(peer->host), SEED2), key); @@ -570,8 +571,7 @@ static int update_group_show_walkcb(struct update_group *updgrp, void *arg) vty_out(vty, " Created: %s", timestamp_string(updgrp->uptime)); filter = &updgrp->conf->filter[updgrp->afi][updgrp->safi]; if (filter->map[RMAP_OUT].name) - vty_out(vty, " Outgoing route map: %s%s\n", - filter->map[RMAP_OUT].map ? "X" : "", + vty_out(vty, " Outgoing route map: %s\n", filter->map[RMAP_OUT].name); vty_out(vty, " MRAI value (seconds): %d\n", updgrp->conf->v_routeadv); if (updgrp->conf->change_local_as) @@ -613,6 +613,9 @@ static int update_group_show_walkcb(struct update_group *updgrp, void *arg) subgrp->peer_refreshes_combined); vty_out(vty, " Merge checks triggered: %u\n", subgrp->merge_checks_triggered); + vty_out(vty, " Coalesce Time: %u%s\n", + (UPDGRP_INST(subgrp->update_group))->coalesce_time, + subgrp->t_coalesce ? "(Running)" : ""); vty_out(vty, " Version: %" PRIu64 "\n", subgrp->version); vty_out(vty, " Packet queue length: %d\n", bpacket_queue_length(SUBGRP_PKTQ(subgrp))); @@ -732,7 +735,6 @@ static void update_group_delete(struct update_group *updgrp) conf_release(updgrp->conf, updgrp->afi, updgrp->safi); XFREE(MTYPE_BGP_PEER_HOST, updgrp->conf->host); - updgrp->conf->host = NULL; XFREE(MTYPE_BGP_PEER_IFNAME, updgrp->conf->ifname); @@ -829,19 +831,19 @@ void update_subgroup_inherit_info(struct update_subgroup *to, * * Delete a subgroup if it is ready to be deleted. * - * Returns TRUE if the subgroup was deleted. + * Returns true if the subgroup was deleted. */ -static int update_subgroup_check_delete(struct update_subgroup *subgrp) +static bool update_subgroup_check_delete(struct update_subgroup *subgrp) { if (!subgrp) - return 0; + return false; if (!LIST_EMPTY(&(subgrp->peers))) - return 0; + return false; update_subgroup_delete(subgrp); - return 1; + return true; } /* @@ -885,6 +887,9 @@ static void update_subgroup_add_peer(struct update_subgroup *subgrp, bpacket_add_peer(pkt, paf); bpacket_queue_sanity_check(SUBGRP_PKTQ(subgrp)); + if (BGP_DEBUG(update_groups, UPDATE_GROUPS)) + zlog_debug("peer %s added to subgroup s%" PRIu64, + paf->peer->host, subgrp->id); } /* @@ -910,6 +915,10 @@ static void update_subgroup_remove_peer_internal(struct update_subgroup *subgrp, paf->subgroup = NULL; subgrp->peer_count--; + if (BGP_DEBUG(update_groups, UPDATE_GROUPS)) + zlog_debug("peer %s deleted from subgroup s%" + PRIu64 " peer cnt %d", + paf->peer->host, subgrp->id, subgrp->peer_count); SUBGRP_INCR_STAT(subgrp, prune_events); } @@ -972,10 +981,10 @@ static struct update_subgroup *update_subgroup_find(struct update_group *updgrp, /* * update_subgroup_ready_for_merge * - * Returns TRUE if this subgroup is in a state that allows it to be + * Returns true if this subgroup is in a state that allows it to be * merged into another subgroup. */ -static int update_subgroup_ready_for_merge(struct update_subgroup *subgrp) +static bool update_subgroup_ready_for_merge(struct update_subgroup *subgrp) { /* @@ -983,13 +992,13 @@ static int update_subgroup_ready_for_merge(struct update_subgroup *subgrp) * out to peers. */ if (!bpacket_queue_is_empty(SUBGRP_PKTQ(subgrp))) - return 0; + return false; /* * Not ready if there enqueued updates waiting to be encoded. */ if (!advertise_list_is_empty(subgrp)) - return 0; + return false; /* * Don't attempt to merge a subgroup that needs a refresh. For one, @@ -997,15 +1006,15 @@ static int update_subgroup_ready_for_merge(struct update_subgroup *subgrp) * another group. */ if (update_subgroup_needs_refresh(subgrp)) - return 0; + return false; - return 1; + return true; } /* * update_subgrp_can_merge_into * - * Returns TRUE if the first subgroup can merge into the second + * Returns true if the first subgroup can merge into the second * subgroup. */ static int update_subgroup_can_merge_into(struct update_subgroup *subgrp, @@ -1068,10 +1077,7 @@ static void update_subgroup_merge(struct update_subgroup *subgrp, SUBGRP_INCR_STAT(target, merge_events); if (BGP_DEBUG(update_groups, UPDATE_GROUPS)) - zlog_debug("u%" PRIu64 ":s%" PRIu64 - " (%d peers) merged into u%" PRIu64 ":s%" PRIu64 - ", " - "trigger: %s", + zlog_debug("u%" PRIu64 ":s%" PRIu64" (%d peers) merged into u%" PRIu64 ":s%" PRIu64", trigger: %s", subgrp->update_group->id, subgrp->id, peer_count, target->update_group->id, target->id, reason ? reason : "unknown"); @@ -1085,16 +1091,16 @@ static void update_subgroup_merge(struct update_subgroup *subgrp, * * Merge this subgroup into another subgroup if possible. * - * Returns TRUE if the subgroup has been merged. The subgroup pointer + * Returns true if the subgroup has been merged. The subgroup pointer * should not be accessed in this case. */ -int update_subgroup_check_merge(struct update_subgroup *subgrp, - const char *reason) +bool update_subgroup_check_merge(struct update_subgroup *subgrp, + const char *reason) { struct update_subgroup *target; if (!update_subgroup_ready_for_merge(subgrp)) - return 0; + return false; /* * Look for a subgroup to merge into. @@ -1105,10 +1111,10 @@ int update_subgroup_check_merge(struct update_subgroup *subgrp, } if (!target) - return 0; + return false; update_subgroup_merge(subgrp, target, reason); - return 1; + return true; } /* @@ -1134,16 +1140,16 @@ static int update_subgroup_merge_check_thread_cb(struct thread *thread) * @param force If true, the merge check will be triggered even if the * subgroup doesn't currently look ready for a merge. * - * Returns TRUE if a merge check will be performed shortly. + * Returns true if a merge check will be performed shortly. */ -int update_subgroup_trigger_merge_check(struct update_subgroup *subgrp, - int force) +bool update_subgroup_trigger_merge_check(struct update_subgroup *subgrp, + int force) { if (subgrp->t_merge_check) - return 1; + return true; if (!force && !update_subgroup_ready_for_merge(subgrp)) - return 0; + return false; subgrp->t_merge_check = NULL; thread_add_timer_msec(bm->master, update_subgroup_merge_check_thread_cb, @@ -1151,7 +1157,7 @@ int update_subgroup_trigger_merge_check(struct update_subgroup *subgrp, SUBGRP_INCR_STAT(subgrp, merge_checks_triggered); - return 1; + return true; } /* @@ -1170,8 +1176,8 @@ static void update_subgroup_copy_adj_out(struct update_subgroup *source, /* * Copy the adj out. */ - aout_copy = - bgp_adj_out_alloc(dest, aout->rn, aout->addpath_tx_id); + aout_copy = bgp_adj_out_alloc(dest, aout->dest, + aout->addpath_tx_id); aout_copy->attr = aout->attr ? bgp_attr_intern(aout->attr) : NULL; } @@ -1205,8 +1211,8 @@ static int update_subgroup_copy_packets(struct update_subgroup *dest, return count; } -static int updgrp_prefix_list_update(struct update_group *updgrp, - const char *name) +static bool updgrp_prefix_list_update(struct update_group *updgrp, + const char *name) { struct peer *peer; struct bgp_filter *filter; @@ -1218,13 +1224,13 @@ static int updgrp_prefix_list_update(struct update_group *updgrp, && (strcmp(name, PREFIX_LIST_OUT_NAME(filter)) == 0)) { PREFIX_LIST_OUT(filter) = prefix_list_lookup( UPDGRP_AFI(updgrp), PREFIX_LIST_OUT_NAME(filter)); - return 1; + return true; } - return 0; + return false; } -static int updgrp_filter_list_update(struct update_group *updgrp, - const char *name) +static bool updgrp_filter_list_update(struct update_group *updgrp, + const char *name) { struct peer *peer; struct bgp_filter *filter; @@ -1236,13 +1242,13 @@ static int updgrp_filter_list_update(struct update_group *updgrp, && (strcmp(name, FILTER_LIST_OUT_NAME(filter)) == 0)) { FILTER_LIST_OUT(filter) = as_list_lookup(FILTER_LIST_OUT_NAME(filter)); - return 1; + return true; } - return 0; + return false; } -static int updgrp_distribute_list_update(struct update_group *updgrp, - const char *name) +static bool updgrp_distribute_list_update(struct update_group *updgrp, + const char *name) { struct peer *peer; struct bgp_filter *filter; @@ -1254,9 +1260,9 @@ static int updgrp_distribute_list_update(struct update_group *updgrp, && (strcmp(name, DISTRIBUTE_OUT_NAME(filter)) == 0)) { DISTRIBUTE_OUT(filter) = access_list_lookup( UPDGRP_AFI(updgrp), DISTRIBUTE_OUT_NAME(filter)); - return 1; + return true; } - return 0; + return false; } static int updgrp_route_map_update(struct update_group *updgrp, @@ -1361,8 +1367,7 @@ static int updgrp_policy_update_walkcb(struct update_group *updgrp, void *arg) if (changed) { if (bgp_debug_update(NULL, NULL, updgrp, 0)) zlog_debug( - "u%" PRIu64 ":s%" PRIu64 - " announcing routes upon policy %s (type %d) change", + "u%" PRIu64 ":s%" PRIu64" announcing routes upon policy %s (type %d) change", updgrp->id, subgrp->id, ctx->policy_name, ctx->policy_type); subgroup_announce_route(subgrp); @@ -1370,8 +1375,7 @@ static int updgrp_policy_update_walkcb(struct update_group *updgrp, void *arg) if (def_changed) { if (bgp_debug_update(NULL, NULL, updgrp, 0)) zlog_debug( - "u%" PRIu64 ":s%" PRIu64 - " announcing default upon default routemap %s change", + "u%" PRIu64 ":s%" PRIu64" announcing default upon default routemap %s change", updgrp->id, subgrp->id, ctx->policy_name); subgroup_default_originate(subgrp, 0); @@ -1477,8 +1481,7 @@ void update_subgroup_split_peer(struct peer_af *paf, UPDGRP_PEER_DBG_EN(updgrp); } if (BGP_DEBUG(update_groups, UPDATE_GROUPS)) - zlog_debug("u%" PRIu64 ":s%" PRIu64 - " peer %s moved to u%" PRIu64 ":s%" PRIu64, + zlog_debug("u%" PRIu64 ":s%" PRIu64" peer %s moved to u%" PRIu64 ":s%" PRIu64, old_id, subgrp->id, paf->peer->host, updgrp->id, subgrp->id); @@ -1512,9 +1515,7 @@ void update_subgroup_split_peer(struct peer_af *paf, update_subgroup_copy_packets(subgrp, paf->next_pkt_to_send); if (BGP_DEBUG(update_groups, UPDATE_GROUPS)) - zlog_debug("u%" PRIu64 ":s%" PRIu64 - " peer %s split and moved into u%" PRIu64 - ":s%" PRIu64, + zlog_debug("u%" PRIu64 ":s%" PRIu64" peer %s split and moved into u%" PRIu64":s%" PRIu64, paf->subgroup->update_group->id, paf->subgroup->id, paf->peer->host, updgrp->id, subgrp->id); @@ -1773,7 +1774,7 @@ int update_group_refresh_default_originate_route_map(struct thread *thread) THREAD_TIMER_OFF(bgp->t_rmap_def_originate_eval); bgp_unlock(bgp); - return (0); + return 0; } /* @@ -1781,7 +1782,7 @@ int update_group_refresh_default_originate_route_map(struct thread *thread) * * Refreshes routes out to a peer_af immediately. * - * If the combine parameter is TRUE, then this function will try to + * If the combine parameter is true, then this function will try to * gather other peers in the subgroup for which a route announcement * is pending and efficently announce routes to all of them. * @@ -1826,12 +1827,11 @@ void peer_af_announce_route(struct peer_af *paf, int combine) */ if (!combine || !all_pending) { update_subgroup_split_peer(paf, NULL); - if (!paf->subgroup) - return; + subgrp = paf->subgroup; + assert(subgrp && subgrp->update_group); if (bgp_debug_update(paf->peer, NULL, subgrp->update_group, 0)) - zlog_debug("u%" PRIu64 ":s%" PRIu64 - " %s announcing routes", + zlog_debug("u%" PRIu64 ":s%" PRIu64" %s announcing routes", subgrp->update_group->id, subgrp->id, paf->peer->host); @@ -1852,8 +1852,7 @@ void peer_af_announce_route(struct peer_af *paf, int combine) } if (bgp_debug_update(paf->peer, NULL, subgrp->update_group, 0)) - zlog_debug("u%" PRIu64 ":s%" PRIu64 - " announcing routes to %s, combined into %d peers", + zlog_debug("u%" PRIu64 ":s%" PRIu64" announcing routes to %s, combined into %d peers", subgrp->update_group->id, subgrp->id, paf->peer->host, subgrp->peer_count); diff --git a/bgpd/bgp_updgrp.h b/bgpd/bgp_updgrp.h index 39e67bf608..9cad78c26d 100644 --- a/bgpd/bgp_updgrp.h +++ b/bgpd/bgp_updgrp.h @@ -56,6 +56,7 @@ #define PEER_UPDGRP_AF_FLAGS \ (PEER_FLAG_SEND_COMMUNITY | PEER_FLAG_SEND_EXT_COMMUNITY \ + | PEER_FLAG_SEND_LARGE_COMMUNITY \ | PEER_FLAG_DEFAULT_ORIGINATE | PEER_FLAG_REFLECTOR_CLIENT \ | PEER_FLAG_RSERVER_CLIENT | PEER_FLAG_NEXTHOP_SELF \ | PEER_FLAG_NEXTHOP_UNCHANGED | PEER_FLAG_FORCE_NEXTHOP_SELF \ @@ -207,7 +208,7 @@ struct update_subgroup { struct bgp_synchronize *sync; /* send prefix count */ - unsigned long scount; + uint32_t scount; /* announcement attribute hash */ struct hash *hash; @@ -291,7 +292,7 @@ typedef int (*updgrp_walkcb)(struct update_group *updgrp, void *ctx); /* really a private structure */ struct updwalk_context { struct vty *vty; - struct bgp_node *rn; + struct bgp_dest *dest; struct bgp_path_info *pi; uint64_t updgrp_id; uint64_t subgrp_id; @@ -372,9 +373,9 @@ extern void update_subgroup_remove_peer(struct update_subgroup *, struct peer_af *); extern struct bgp_table *update_subgroup_rib(struct update_subgroup *); extern void update_subgroup_split_peer(struct peer_af *, struct update_group *); -extern int update_subgroup_check_merge(struct update_subgroup *, const char *); -extern int update_subgroup_trigger_merge_check(struct update_subgroup *, - int force); +extern bool update_subgroup_check_merge(struct update_subgroup *, const char *); +extern bool update_subgroup_trigger_merge_check(struct update_subgroup *, + int force); extern void update_group_policy_update(struct bgp *bgp, bgp_policy_type_e ptype, const char *pname, int route_update, int start_event); @@ -403,13 +404,13 @@ extern struct bpacket *bpacket_queue_first(struct bpacket_queue *q); struct bpacket *bpacket_queue_last(struct bpacket_queue *q); unsigned int bpacket_queue_length(struct bpacket_queue *q); unsigned int bpacket_queue_hwm_length(struct bpacket_queue *q); -int bpacket_queue_is_full(struct bgp *bgp, struct bpacket_queue *q); +bool bpacket_queue_is_full(struct bgp *bgp, struct bpacket_queue *q); extern void bpacket_queue_advance_peer(struct peer_af *paf); extern void bpacket_queue_remove_peer(struct peer_af *paf); extern void bpacket_add_peer(struct bpacket *pkt, struct peer_af *paf); unsigned int bpacket_queue_virtual_length(struct peer_af *paf); extern void bpacket_queue_show_vty(struct bpacket_queue *q, struct vty *vty); -int subgroup_packets_to_build(struct update_subgroup *subgrp); +bool subgroup_packets_to_build(struct update_subgroup *subgrp); extern struct bpacket *subgroup_update_packet(struct update_subgroup *s); extern struct bpacket *subgroup_withdraw_packet(struct update_subgroup *s); extern struct stream *bpacket_reformat_for_peer(struct bpacket *pkt, @@ -441,22 +442,23 @@ extern void subgroup_announce_all(struct update_subgroup *subgrp); extern void subgroup_default_originate(struct update_subgroup *subgrp, int withdraw); extern void group_announce_route(struct bgp *bgp, afi_t afi, safi_t safi, - struct bgp_node *rn, struct bgp_path_info *pi); + struct bgp_dest *dest, + struct bgp_path_info *pi); extern void subgroup_clear_table(struct update_subgroup *subgrp); extern void update_group_announce(struct bgp *bgp); extern void update_group_announce_rrclients(struct bgp *bgp); extern void peer_af_announce_route(struct peer_af *paf, int combine); extern struct bgp_adj_out *bgp_adj_out_alloc(struct update_subgroup *subgrp, - struct bgp_node *rn, + struct bgp_dest *dest, uint32_t addpath_tx_id); -extern void bgp_adj_out_remove_subgroup(struct bgp_node *rn, +extern void bgp_adj_out_remove_subgroup(struct bgp_dest *dest, struct bgp_adj_out *adj, struct update_subgroup *subgrp); -extern void bgp_adj_out_set_subgroup(struct bgp_node *rn, +extern void bgp_adj_out_set_subgroup(struct bgp_dest *dest, struct update_subgroup *subgrp, struct attr *attr, struct bgp_path_info *path); -extern void bgp_adj_out_unset_subgroup(struct bgp_node *rn, +extern void bgp_adj_out_unset_subgroup(struct bgp_dest *dest, struct update_subgroup *subgrp, char withdraw, uint32_t addpath_tx_id); void subgroup_announce_table(struct update_subgroup *subgrp, diff --git a/bgpd/bgp_updgrp_adv.c b/bgpd/bgp_updgrp_adv.c index b64c51f341..7b361fba1d 100644 --- a/bgpd/bgp_updgrp_adv.c +++ b/bgpd/bgp_updgrp_adv.c @@ -64,40 +64,31 @@ static int bgp_adj_out_compare(const struct bgp_adj_out *o1, if (o1->subgroup > o2->subgroup) return 1; + if (o1->addpath_tx_id < o2->addpath_tx_id) + return -1; + + if (o1->addpath_tx_id > o2->addpath_tx_id) + return 1; + return 0; } RB_GENERATE(bgp_adj_out_rb, bgp_adj_out, adj_entry, bgp_adj_out_compare); -static inline struct bgp_adj_out *adj_lookup(struct bgp_node *rn, +static inline struct bgp_adj_out *adj_lookup(struct bgp_dest *dest, struct update_subgroup *subgrp, uint32_t addpath_tx_id) { - struct bgp_adj_out *adj, lookup; - struct peer *peer; - afi_t afi; - safi_t safi; - int addpath_capable; + struct bgp_adj_out lookup; - if (!rn || !subgrp) + if (!dest || !subgrp) return NULL; - peer = SUBGRP_PEER(subgrp); - afi = SUBGRP_AFI(subgrp); - safi = SUBGRP_SAFI(subgrp); - addpath_capable = bgp_addpath_encode_tx(peer, afi, safi); - /* update-groups that do not support addpath will pass 0 for - * addpath_tx_id so do not both matching against it */ + * addpath_tx_id. */ lookup.subgroup = subgrp; - adj = RB_FIND(bgp_adj_out_rb, &rn->adj_out, &lookup); - if (adj) { - if (addpath_capable) { - if (adj->addpath_tx_id == addpath_tx_id) - return adj; - } else - return adj; - } - return NULL; + lookup.addpath_tx_id = addpath_tx_id; + + return RB_FIND(bgp_adj_out_rb, &dest->adj_out, &lookup); } static void adj_free(struct bgp_adj_out *adj) @@ -119,11 +110,11 @@ static void subgrp_withdraw_stale_addpath(struct updwalk_context *ctx, /* Look through all of the paths we have advertised for this rn and send * a withdraw for the ones that are no longer present */ - RB_FOREACH_SAFE (adj, bgp_adj_out_rb, &ctx->rn->adj_out, adj_next) { + RB_FOREACH_SAFE (adj, bgp_adj_out_rb, &ctx->dest->adj_out, adj_next) { if (adj->subgroup == subgrp) { - for (pi = bgp_node_get_bgp_path_info(ctx->rn); - pi; pi = pi->next) { + for (pi = bgp_dest_get_bgp_path_info(ctx->dest); pi; + pi = pi->next) { id = bgp_addpath_id_for_peer(peer, afi, safi, &pi->tx_addpath); @@ -134,7 +125,7 @@ static void subgrp_withdraw_stale_addpath(struct updwalk_context *ctx, if (!pi) { subgroup_process_announce_selected( - subgrp, NULL, ctx->rn, + subgrp, NULL, ctx->dest, adj->addpath_tx_id); } } @@ -157,13 +148,10 @@ static int group_announce_route_walkcb(struct update_group *updgrp, void *arg) peer = UPDGRP_PEER(updgrp); addpath_capable = bgp_addpath_encode_tx(peer, afi, safi); - if (BGP_DEBUG(update, UPDATE_OUT)) { - char buf_prefix[PREFIX_STRLEN]; - prefix2str(&ctx->rn->p, buf_prefix, sizeof(buf_prefix)); - zlog_debug("%s: afi=%s, safi=%s, p=%s", __func__, afi2str(afi), - safi2str(safi), buf_prefix); - } - + if (BGP_DEBUG(update, UPDATE_OUT)) + zlog_debug("%s: afi=%s, safi=%s, p=%pRN", __func__, + afi2str(afi), safi2str(safi), + bgp_dest_to_rnode(ctx->dest)); UPDGRP_FOREACH_SUBGRP (updgrp, subgrp) { @@ -177,14 +165,14 @@ static int group_announce_route_walkcb(struct update_group *updgrp, void *arg) if (addpath_capable) { subgrp_withdraw_stale_addpath(ctx, subgrp); - for (pi = bgp_node_get_bgp_path_info(ctx->rn); + for (pi = bgp_dest_get_bgp_path_info(ctx->dest); pi; pi = pi->next) { /* Skip the bestpath for now */ if (pi == ctx->pi) continue; subgroup_process_announce_selected( - subgrp, pi, ctx->rn, + subgrp, pi, ctx->dest, bgp_addpath_id_for_peer( peer, afi, safi, &pi->tx_addpath)); @@ -196,7 +184,7 @@ static int group_announce_route_walkcb(struct update_group *updgrp, void *arg) */ if (ctx->pi) subgroup_process_announce_selected( - subgrp, ctx->pi, ctx->rn, + subgrp, ctx->pi, ctx->dest, bgp_addpath_id_for_peer( peer, afi, safi, &ctx->pi->tx_addpath)); @@ -206,7 +194,7 @@ static int group_announce_route_walkcb(struct update_group *updgrp, void *arg) else { if (ctx->pi) { subgroup_process_announce_selected( - subgrp, ctx->pi, ctx->rn, + subgrp, ctx->pi, ctx->dest, bgp_addpath_id_for_peer( peer, afi, safi, &ctx->pi->tx_addpath)); @@ -215,12 +203,12 @@ static int group_announce_route_walkcb(struct update_group *updgrp, void *arg) * had advertised and * send a withdraw */ RB_FOREACH_SAFE (adj, bgp_adj_out_rb, - &ctx->rn->adj_out, + &ctx->dest->adj_out, adj_next) { if (adj->subgroup == subgrp) { subgroup_process_announce_selected( subgrp, NULL, - ctx->rn, + ctx->dest, adj->addpath_tx_id); } } @@ -238,7 +226,7 @@ static void subgrp_show_adjq_vty(struct update_subgroup *subgrp, struct bgp_table *table; struct bgp_adj_out *adj; unsigned long output_count; - struct bgp_node *rn; + struct bgp_dest *dest; int header1 = 1; struct bgp *bgp; int header2 = 1; @@ -251,13 +239,14 @@ static void subgrp_show_adjq_vty(struct update_subgroup *subgrp, output_count = 0; - for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) - RB_FOREACH (adj, bgp_adj_out_rb, &rn->adj_out) + for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) { + const struct prefix *dest_p = bgp_dest_get_prefix(dest); + + RB_FOREACH (adj, bgp_adj_out_rb, &dest->adj_out) if (adj->subgroup == subgrp) { if (header1) { vty_out(vty, - "BGP table version is %" PRIu64 - ", local router ID is %s\n", + "BGP table version is %" PRIu64", local router ID is %s\n", table->version, inet_ntoa(bgp->router_id)); vty_out(vty, BGP_SHOW_SCODE_HEADER); @@ -270,20 +259,22 @@ static void subgrp_show_adjq_vty(struct update_subgroup *subgrp, } if ((flags & UPDWALK_FLAGS_ADVQUEUE) && adj->adv && adj->adv->baa) { - route_vty_out_tmp(vty, &rn->p, + route_vty_out_tmp(vty, dest_p, adj->adv->baa->attr, SUBGRP_SAFI(subgrp), - 0, NULL); + 0, NULL, false); output_count++; } if ((flags & UPDWALK_FLAGS_ADVERTISED) && adj->attr) { - route_vty_out_tmp( - vty, &rn->p, adj->attr, - SUBGRP_SAFI(subgrp), 0, NULL); + route_vty_out_tmp(vty, dest_p, + adj->attr, + SUBGRP_SAFI(subgrp), + 0, NULL, false); output_count++; } } + } if (output_count != 0) vty_out(vty, "\nTotal number of prefixes %ld\n", output_count); } @@ -323,9 +314,9 @@ static int subgroup_coalesce_timer(struct thread *thread) subgrp = THREAD_ARG(thread); if (bgp_debug_update(NULL, NULL, subgrp->update_group, 0)) - zlog_debug("u%" PRIu64 ":s%" PRIu64 - " announcing routes upon coalesce timer expiry", - (SUBGRP_UPDGRP(subgrp))->id, subgrp->id); + zlog_debug("u%" PRIu64 ":s%" PRIu64" announcing routes upon coalesce timer expiry(%u ms)", + (SUBGRP_UPDGRP(subgrp))->id, subgrp->id, + subgrp->v_coalesce); subgrp->t_coalesce = NULL; subgrp->v_coalesce = 0; subgroup_announce_route(subgrp); @@ -395,20 +386,21 @@ static int update_group_announce_rrc_walkcb(struct update_group *updgrp, * primarily its association with the subgroup and the prefix. */ struct bgp_adj_out *bgp_adj_out_alloc(struct update_subgroup *subgrp, - struct bgp_node *rn, + struct bgp_dest *dest, uint32_t addpath_tx_id) { struct bgp_adj_out *adj; adj = XCALLOC(MTYPE_BGP_ADJ_OUT, sizeof(struct bgp_adj_out)); adj->subgroup = subgrp; - if (rn) { - RB_INSERT(bgp_adj_out_rb, &rn->adj_out, adj); - bgp_lock_node(rn); - adj->rn = rn; + adj->addpath_tx_id = addpath_tx_id; + + if (dest) { + RB_INSERT(bgp_adj_out_rb, &dest->adj_out, adj); + bgp_dest_lock_node(dest); + adj->dest = dest; } - adj->addpath_tx_id = addpath_tx_id; TAILQ_INSERT_TAIL(&(subgrp->adjq), adj, subgrp_adj_train); SUBGRP_INCR_STAT(subgrp, adj_count); return adj; @@ -453,7 +445,7 @@ bgp_advertise_clean_subgroup(struct update_subgroup *subgrp, return next; } -void bgp_adj_out_set_subgroup(struct bgp_node *rn, +void bgp_adj_out_set_subgroup(struct bgp_dest *dest, struct update_subgroup *subgrp, struct attr *attr, struct bgp_path_info *path) { @@ -472,14 +464,14 @@ void bgp_adj_out_set_subgroup(struct bgp_node *rn, /* Look for adjacency information. */ adj = adj_lookup( - rn, subgrp, + dest, subgrp, bgp_addpath_id_for_peer(peer, afi, safi, &path->tx_addpath)); if (!adj) { adj = bgp_adj_out_alloc( - subgrp, rn, + subgrp, dest, bgp_addpath_id_for_peer(peer, afi, safi, - &path->tx_addpath)); + &path->tx_addpath)); if (!adj) return; } @@ -489,7 +481,7 @@ void bgp_adj_out_set_subgroup(struct bgp_node *rn, adj->adv = bgp_advertise_new(); adv = adj->adv; - adv->rn = rn; + adv->dest = dest; assert(adv->pathi == NULL); /* bgp_path_info adj_out reference */ adv->pathi = bgp_path_info_lock(path); @@ -517,14 +509,14 @@ void bgp_adj_out_set_subgroup(struct bgp_node *rn, bgp_adv_fifo_add_tail(&subgrp->sync->update, adv); - subgrp->version = max(subgrp->version, rn->version); + subgrp->version = max(subgrp->version, dest->version); } /* The only time 'withdraw' will be false is if we are sending * the "neighbor x.x.x.x default-originate" default and need to clear * bgp_adj_out for the 0.0.0.0/0 route in the BGP table. */ -void bgp_adj_out_unset_subgroup(struct bgp_node *rn, +void bgp_adj_out_unset_subgroup(struct bgp_dest *dest, struct update_subgroup *subgrp, char withdraw, uint32_t addpath_tx_id) { @@ -536,16 +528,24 @@ void bgp_adj_out_unset_subgroup(struct bgp_node *rn, return; /* Lookup existing adjacency */ - if ((adj = adj_lookup(rn, subgrp, addpath_tx_id)) != NULL) { + if ((adj = adj_lookup(dest, subgrp, addpath_tx_id)) != NULL) { /* Clean up previous advertisement. */ if (adj->adv) bgp_advertise_clean_subgroup(subgrp, adj); + /* If default originate is enabled and the route is default + * route, do not send withdraw. This will prevent deletion of + * the default route at the peer. + */ + if (CHECK_FLAG(subgrp->sflags, SUBGRP_STATUS_DEFAULT_ORIGINATE) + && is_default_prefix(bgp_dest_get_prefix(dest))) + return; + if (adj->attr && withdraw) { /* We need advertisement structure. */ adj->adv = bgp_advertise_new(); adv = adj->adv; - adv->rn = rn; + adv->dest = dest; adv->adj = adj; /* Note if we need to trigger a packet write */ @@ -560,19 +560,19 @@ void bgp_adj_out_unset_subgroup(struct bgp_node *rn, subgroup_trigger_write(subgrp); } else { /* Remove myself from adjacency. */ - RB_REMOVE(bgp_adj_out_rb, &rn->adj_out, adj); + RB_REMOVE(bgp_adj_out_rb, &dest->adj_out, adj); /* Free allocated information. */ adj_free(adj); - bgp_unlock_node(rn); + bgp_dest_unlock_node(dest); } } - subgrp->version = max(subgrp->version, rn->version); + subgrp->version = max(subgrp->version, dest->version); } -void bgp_adj_out_remove_subgroup(struct bgp_node *rn, struct bgp_adj_out *adj, +void bgp_adj_out_remove_subgroup(struct bgp_dest *dest, struct bgp_adj_out *adj, struct update_subgroup *subgrp) { if (adj->attr) @@ -581,7 +581,7 @@ void bgp_adj_out_remove_subgroup(struct bgp_node *rn, struct bgp_adj_out *adj, if (adj->adv) bgp_advertise_clean_subgroup(subgrp, adj); - RB_REMOVE(bgp_adj_out_rb, &rn->adj_out, adj); + RB_REMOVE(bgp_adj_out_rb, &dest->adj_out, adj); adj_free(adj); } @@ -594,9 +594,9 @@ void subgroup_clear_table(struct update_subgroup *subgrp) struct bgp_adj_out *aout, *taout; SUBGRP_FOREACH_ADJ_SAFE (subgrp, aout, taout) { - struct bgp_node *rn = aout->rn; - bgp_adj_out_remove_subgroup(rn, aout, subgrp); - bgp_unlock_node(rn); + struct bgp_dest *dest = aout->dest; + bgp_adj_out_remove_subgroup(dest, aout, subgrp); + bgp_dest_unlock_node(dest); } } @@ -606,7 +606,7 @@ void subgroup_clear_table(struct update_subgroup *subgrp) void subgroup_announce_table(struct update_subgroup *subgrp, struct bgp_table *table) { - struct bgp_node *rn; + struct bgp_dest *dest; struct bgp_path_info *ri; struct attr attr; struct peer *peer; @@ -630,25 +630,41 @@ void subgroup_announce_table(struct update_subgroup *subgrp, PEER_FLAG_DEFAULT_ORIGINATE)) subgroup_default_originate(subgrp, 0); - for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) - for (ri = bgp_node_get_bgp_path_info(rn); ri; ri = ri->next) + for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) { + const struct prefix *dest_p = bgp_dest_get_prefix(dest); + + for (ri = bgp_dest_get_bgp_path_info(dest); ri; ri = ri->next) if (CHECK_FLAG(ri->flags, BGP_PATH_SELECTED) || (addpath_capable && bgp_addpath_tx_path( peer->addpath_type[afi][safi], ri))) { - if (subgroup_announce_check(rn, ri, subgrp, - &rn->p, &attr)) - bgp_adj_out_set_subgroup(rn, subgrp, + if (subgroup_announce_check(dest, ri, subgrp, + dest_p, &attr)) + bgp_adj_out_set_subgroup(dest, subgrp, &attr, ri); - else + else { + /* If default originate is enabled for + * the peer, do not send explicit + * withdraw. This will prevent deletion + * of default route advertised through + * default originate + */ + if (CHECK_FLAG( + peer->af_flags[afi][safi], + PEER_FLAG_DEFAULT_ORIGINATE) + && is_default_prefix(bgp_dest_get_prefix(dest))) + break; + bgp_adj_out_unset_subgroup( - rn, subgrp, 1, + dest, subgrp, 1, bgp_addpath_id_for_peer( peer, afi, safi, &ri->tx_addpath)); + } } + } /* * We walked through the whole table -- make sure our version number @@ -673,7 +689,7 @@ void subgroup_announce_table(struct update_subgroup *subgrp, */ void subgroup_announce_route(struct update_subgroup *subgrp) { - struct bgp_node *rn; + struct bgp_dest *dest; struct bgp_table *table; struct peer *onlypeer; @@ -696,9 +712,9 @@ void subgroup_announce_route(struct update_subgroup *subgrp) && SUBGRP_SAFI(subgrp) != SAFI_EVPN) subgroup_announce_table(subgrp, NULL); else - for (rn = bgp_table_top(update_subgroup_rib(subgrp)); rn; - rn = bgp_route_next(rn)) { - table = bgp_node_get_bgp_table_info(rn); + for (dest = bgp_table_top(update_subgroup_rib(subgrp)); dest; + dest = bgp_route_next(dest)) { + table = bgp_dest_get_bgp_table_info(dest); if (!table) continue; subgroup_announce_table(subgrp, table); @@ -709,14 +725,14 @@ void subgroup_default_originate(struct update_subgroup *subgrp, int withdraw) { struct bgp *bgp; struct attr attr; - struct aspath *aspath; - struct bgp_path_info tmp_info; + struct attr *new_attr = &attr; struct prefix p; struct peer *from; - struct bgp_node *rn; - struct bgp_path_info *ri; + struct bgp_dest *dest; + struct bgp_path_info *pi; struct peer *peer; - int ret = RMAP_DENYMATCH; + struct bgp_adj_out *adj; + route_map_result_t ret = RMAP_DENYMATCH; afi_t afi; safi_t safi; @@ -734,14 +750,9 @@ void subgroup_default_originate(struct update_subgroup *subgrp, int withdraw) from = bgp->peer_self; bgp_attr_default_set(&attr, BGP_ORIGIN_IGP); - aspath = attr.aspath; attr.local_pref = bgp->default_local_pref; - memset(&p, 0, sizeof(p)); - p.family = afi2family(afi); - p.prefixlen = 0; - if ((afi == AFI_IP6) || peer_cap_enhe(peer, afi, safi)) { /* IPv6 global nexthop must be included. */ attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV6_GLOBAL; @@ -755,31 +766,52 @@ void subgroup_default_originate(struct update_subgroup *subgrp, int withdraw) if (peer->default_rmap[afi][safi].name) { SET_FLAG(bgp->peer_self->rmap_type, PEER_RMAP_TYPE_DEFAULT); - for (rn = bgp_table_top(bgp->rib[afi][safi]); rn; - rn = bgp_route_next(rn)) { - for (ri = bgp_node_get_bgp_path_info(rn); - ri; ri = ri->next) { - struct attr dummy_attr; - - /* Provide dummy so the route-map can't modify - * the attributes */ - bgp_attr_dup(&dummy_attr, ri->attr); - tmp_info.peer = ri->peer; - tmp_info.attr = &dummy_attr; + + /* Iterate over the RIB to see if we can announce + * the default route. We announce the default + * route only if route-map has a match. + */ + for (dest = bgp_table_top(bgp->rib[afi][safi]); dest; + dest = bgp_route_next(dest)) { + if (!bgp_dest_has_bgp_path_info_data(dest)) + continue; + + for (pi = bgp_dest_get_bgp_path_info(dest); pi; + pi = pi->next) { + struct attr tmp_attr; + struct bgp_path_info tmp_pi; + struct bgp_path_info_extra tmp_pie; + + tmp_attr = *pi->attr; + + prep_for_rmap_apply(&tmp_pi, &tmp_pie, dest, pi, + pi->peer, &tmp_attr); ret = route_map_apply( peer->default_rmap[afi][safi].map, - &rn->p, RMAP_BGP, &tmp_info); + bgp_dest_get_prefix(dest), RMAP_BGP, + &tmp_pi); + + if (ret == RMAP_DENYMATCH) { + bgp_attr_flush(&tmp_attr); + continue; + } else { + new_attr = bgp_attr_intern(&tmp_attr); + new_attr->aspath = attr.aspath; + + subgroup_announce_reset_nhop( + (peer_cap_enhe(peer, afi, safi) + ? AF_INET6 + : AF_INET), + new_attr); - /* The route map might have set attributes. If - * we don't flush them - * here, they will be leaked. */ - bgp_attr_flush(&dummy_attr); - if (ret != RMAP_DENYMATCH) break; + } } - if (ret != RMAP_DENYMATCH) + if (ret == RMAP_PERMITMATCH) { + bgp_dest_unlock_node(dest); break; + } } bgp->peer_self->rmap_type = 0; @@ -787,22 +819,42 @@ void subgroup_default_originate(struct update_subgroup *subgrp, int withdraw) withdraw = 1; } + /* Check if the default route is in local BGP RIB which is + * installed through redistribute or network command + */ + memset(&p, 0, sizeof(p)); + p.family = afi2family(afi); + p.prefixlen = 0; + dest = bgp_afi_node_lookup(bgp->rib[afi][safi], afi, safi, &p, NULL); + if (withdraw) { + /* Withdraw the default route advertised using default + * originate + */ if (CHECK_FLAG(subgrp->sflags, SUBGRP_STATUS_DEFAULT_ORIGINATE)) subgroup_default_withdraw_packet(subgrp); UNSET_FLAG(subgrp->sflags, SUBGRP_STATUS_DEFAULT_ORIGINATE); + + /* If default route is present in the local RIB, advertise the + * route + */ + if (dest != NULL) { + for (pi = bgp_dest_get_bgp_path_info(dest); pi; + pi = pi->next) { + if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED)) + if (subgroup_announce_check( + dest, pi, subgrp, + bgp_dest_get_prefix(dest), + &attr)) + bgp_adj_out_set_subgroup( + dest, subgrp, &attr, + pi); + } + } } else { if (!CHECK_FLAG(subgrp->sflags, SUBGRP_STATUS_DEFAULT_ORIGINATE)) { - if (bgp_flag_check(bgp, BGP_FLAG_GRACEFUL_SHUTDOWN)) { - bgp_attr_add_gshut_community(&attr); - } - - SET_FLAG(subgrp->sflags, - SUBGRP_STATUS_DEFAULT_ORIGINATE); - subgroup_default_update_packet(subgrp, &attr, from); - /* The 'neighbor x.x.x.x default-originate' default will * act as an * implicit withdraw for any previous UPDATEs sent for @@ -810,19 +862,39 @@ void subgroup_default_originate(struct update_subgroup *subgrp, int withdraw) * clear adj_out for the 0.0.0.0/0 prefix in the BGP * table. */ - memset(&p, 0, sizeof(p)); - p.family = afi2family(afi); - p.prefixlen = 0; - - rn = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi, - &p, NULL); - bgp_adj_out_unset_subgroup( - rn, subgrp, 0, - BGP_ADDPATH_TX_ID_FOR_DEFAULT_ORIGINATE); + if (dest != NULL) { + /* Remove the adjacency for the previously + * advertised default route + */ + adj = adj_lookup( + dest, subgrp, + BGP_ADDPATH_TX_ID_FOR_DEFAULT_ORIGINATE); + if (adj != NULL) { + /* Clean up previous advertisement. */ + if (adj->adv) + bgp_advertise_clean_subgroup( + subgrp, adj); + + /* Remove from adjacency. */ + RB_REMOVE(bgp_adj_out_rb, + &dest->adj_out, adj); + + /* Free allocated information. */ + adj_free(adj); + + bgp_dest_unlock_node(dest); + } + } + + /* Advertise the default route */ + if (CHECK_FLAG(bgp->flags, BGP_FLAG_GRACEFUL_SHUTDOWN)) + bgp_attr_add_gshut_community(new_attr); + + SET_FLAG(subgrp->sflags, + SUBGRP_STATUS_DEFAULT_ORIGINATE); + subgroup_default_update_packet(subgrp, new_attr, from); } } - - aspath_unintern(&aspath); } /* @@ -843,8 +915,7 @@ void subgroup_announce_all(struct update_subgroup *subgrp) */ if (!subgrp->v_coalesce) { if (bgp_debug_update(NULL, NULL, subgrp->update_group, 0)) - zlog_debug("u%" PRIu64 ":s%" PRIu64 - " announcing all routes", + zlog_debug("u%" PRIu64 ":s%" PRIu64" announcing all routes", subgrp->update_group->id, subgrp->id); subgroup_announce_route(subgrp); return; @@ -865,11 +936,11 @@ void subgroup_announce_all(struct update_subgroup *subgrp) * input route. */ void group_announce_route(struct bgp *bgp, afi_t afi, safi_t safi, - struct bgp_node *rn, struct bgp_path_info *pi) + struct bgp_dest *dest, struct bgp_path_info *pi) { struct updwalk_context ctx; ctx.pi = pi; - ctx.rn = rn; + ctx.dest = dest; update_group_af_walk(bgp, afi, safi, group_announce_route_walkcb, &ctx); } diff --git a/bgpd/bgp_updgrp_packet.c b/bgpd/bgp_updgrp_packet.c index 3556b236a7..f706b834fe 100644 --- a/bgpd/bgp_updgrp_packet.c +++ b/bgpd/bgp_updgrp_packet.c @@ -226,11 +226,11 @@ unsigned int bpacket_queue_hwm_length(struct bpacket_queue *q) return q->hwm_count - 1; } -int bpacket_queue_is_full(struct bgp *bgp, struct bpacket_queue *q) +bool bpacket_queue_is_full(struct bgp *bgp, struct bpacket_queue *q) { if (q->curr_count >= bgp->default_subgroup_pkt_queue_max) - return 1; - return 0; + return true; + return false; } void bpacket_add_peer(struct bpacket *pkt, struct peer_af *paf) @@ -330,8 +330,6 @@ void bpacket_queue_remove_peer(struct peer_af *paf) q = PAF_PKTQ(paf); assert(q); - if (!q) - return; LIST_REMOVE(paf, pkt_train); paf->next_pkt_to_send = NULL; @@ -391,248 +389,263 @@ struct stream *bpacket_reformat_for_peer(struct bpacket *pkt, struct peer *peer; char buf[BUFSIZ]; char buf2[BUFSIZ]; + struct bgp_filter *filter; s = stream_dup(pkt->buffer); peer = PAF_PEER(paf); vec = &pkt->arr.entries[BGP_ATTR_VEC_NH]; - if (CHECK_FLAG(vec->flags, BPKT_ATTRVEC_FLAGS_UPDATED)) { - uint8_t nhlen; - afi_t nhafi; - int route_map_sets_nh; - nhlen = stream_getc_from(s, vec->offset); - if (peer_cap_enhe(peer, paf->afi, paf->safi)) - nhafi = AFI_IP6; - else - nhafi = BGP_NEXTHOP_AFI_FROM_NHLEN(nhlen); - - if (nhafi == AFI_IP) { - struct in_addr v4nh, *mod_v4nh; - int nh_modified = 0; - size_t offset_nh = vec->offset + 1; - - route_map_sets_nh = - (CHECK_FLAG( - vec->flags, - BPKT_ATTRVEC_FLAGS_RMAP_IPV4_NH_CHANGED) - || CHECK_FLAG( - vec->flags, - BPKT_ATTRVEC_FLAGS_RMAP_NH_PEER_ADDRESS)); - - switch (nhlen) { - case BGP_ATTR_NHLEN_IPV4: - break; - case BGP_ATTR_NHLEN_VPNV4: - offset_nh += 8; - break; - default: - /* TODO: handle IPv6 nexthops */ - flog_warn( - EC_BGP_INVALID_NEXTHOP_LENGTH, - "%s: %s: invalid MP nexthop length (AFI IP): %u", - __func__, peer->host, nhlen); - stream_free(s); - return NULL; - } - stream_get_from(&v4nh, s, offset_nh, IPV4_MAX_BYTELEN); - mod_v4nh = &v4nh; - - /* - * If route-map has set the nexthop, that is always - * used; if it is - * specified as peer-address, the peering address is - * picked up. - * Otherwise, if NH is unavailable from attribute, the - * peering addr - * is picked up; the "NH unavailable" case also covers - * next-hop-self - * and some other scenarios -- see - * subgroup_announce_check(). In - * all other cases, use the nexthop carried in the - * attribute unless - * it is EBGP non-multiaccess and there is no - * next-hop-unchanged setting. - * Note: It is assumed route-map cannot set the nexthop - * to an - * invalid value. - */ - if (route_map_sets_nh) { - if (CHECK_FLAG( - vec->flags, - BPKT_ATTRVEC_FLAGS_RMAP_NH_PEER_ADDRESS)) { - mod_v4nh = &peer->nexthop.v4; - nh_modified = 1; - } - } else if (!v4nh.s_addr) { - mod_v4nh = &peer->nexthop.v4; - nh_modified = 1; - } else if ( - peer->sort == BGP_PEER_EBGP - && (bgp_multiaccess_check_v4(v4nh, peer) == 0) - && !CHECK_FLAG( - vec->flags, - BPKT_ATTRVEC_FLAGS_RMAP_NH_UNCHANGED) - && !peer_af_flag_check( - peer, paf->afi, paf->safi, - PEER_FLAG_NEXTHOP_UNCHANGED)) { - /* NOTE: not handling case where NH has new AFI - */ + if (!CHECK_FLAG(vec->flags, BPKT_ATTRVEC_FLAGS_UPDATED)) + return s; + + uint8_t nhlen; + afi_t nhafi; + int route_map_sets_nh; + + nhlen = stream_getc_from(s, vec->offset); + filter = &peer->filter[paf->afi][paf->safi]; + + if (peer_cap_enhe(peer, paf->afi, paf->safi)) + nhafi = AFI_IP6; + else + nhafi = BGP_NEXTHOP_AFI_FROM_NHLEN(nhlen); + + if (nhafi == AFI_IP) { + struct in_addr v4nh, *mod_v4nh; + int nh_modified = 0; + size_t offset_nh = vec->offset + 1; + + route_map_sets_nh = + (CHECK_FLAG(vec->flags, + BPKT_ATTRVEC_FLAGS_RMAP_IPV4_NH_CHANGED) + || CHECK_FLAG( + vec->flags, + BPKT_ATTRVEC_FLAGS_RMAP_NH_PEER_ADDRESS)); + + switch (nhlen) { + case BGP_ATTR_NHLEN_IPV4: + break; + case BGP_ATTR_NHLEN_VPNV4: + offset_nh += 8; + break; + default: + /* TODO: handle IPv6 nexthops */ + flog_warn( + EC_BGP_INVALID_NEXTHOP_LENGTH, + "%s: %s: invalid MP nexthop length (AFI IP): %u", + __func__, peer->host, nhlen); + stream_free(s); + return NULL; + } + + stream_get_from(&v4nh, s, offset_nh, IPV4_MAX_BYTELEN); + mod_v4nh = &v4nh; + + /* + * If route-map has set the nexthop, that is normally + * used; if it is specified as peer-address, the peering + * address is picked up. Otherwise, if NH is unavailable + * from attribute, the peering addr is picked up; the + * "NH unavailable" case also covers next-hop-self and + * some other scenarios - see subgroup_announce_check(). + * In all other cases, use the nexthop carried in the + * attribute unless it is EBGP non-multiaccess and there + * is no next-hop-unchanged setting or the peer is EBGP + * and the route-map that changed the next-hop value + * was applied inbound rather than outbound. Updates to + * an EBGP peer should only modify the next-hop if it + * was set in an outbound route-map to that peer. + * Note: It is assumed route-map cannot set the nexthop + * to an invalid value. + */ + if (route_map_sets_nh + && ((peer->sort != BGP_PEER_EBGP) + || ROUTE_MAP_OUT(filter))) { + if (CHECK_FLAG( + vec->flags, + BPKT_ATTRVEC_FLAGS_RMAP_NH_PEER_ADDRESS)) { mod_v4nh = &peer->nexthop.v4; nh_modified = 1; } + } else if (v4nh.s_addr == INADDR_ANY) { + mod_v4nh = &peer->nexthop.v4; + nh_modified = 1; + } else if (peer->sort == BGP_PEER_EBGP + && (bgp_multiaccess_check_v4(v4nh, peer) == 0) + && !CHECK_FLAG(vec->flags, + BPKT_ATTRVEC_FLAGS_RMAP_NH_UNCHANGED) + && !peer_af_flag_check( + peer, paf->afi, paf->safi, + PEER_FLAG_NEXTHOP_UNCHANGED)) { + /* NOTE: not handling case where NH has new AFI + */ + mod_v4nh = &peer->nexthop.v4; + nh_modified = 1; + } - if (nh_modified) /* allow for VPN RD */ - stream_put_in_addr_at(s, offset_nh, mod_v4nh); + if (nh_modified) /* allow for VPN RD */ + stream_put_in_addr_at(s, offset_nh, mod_v4nh); + + if (bgp_debug_update(peer, NULL, NULL, 0)) + zlog_debug("u%" PRIu64 ":s%" PRIu64" %s send UPDATE w/ nexthop %s%s", + PAF_SUBGRP(paf)->update_group->id, + PAF_SUBGRP(paf)->id, peer->host, + inet_ntoa(*mod_v4nh), + (nhlen == BGP_ATTR_NHLEN_VPNV4 ? " and RD" + : "")); + } else if (nhafi == AFI_IP6) { + struct in6_addr v6nhglobal, *mod_v6nhg; + struct in6_addr v6nhlocal, *mod_v6nhl; + int gnh_modified, lnh_modified; + size_t offset_nhglobal = vec->offset + 1; + size_t offset_nhlocal = vec->offset + 1; + + gnh_modified = lnh_modified = 0; + mod_v6nhg = &v6nhglobal; + mod_v6nhl = &v6nhlocal; + + route_map_sets_nh = + (CHECK_FLAG(vec->flags, + BPKT_ATTRVEC_FLAGS_RMAP_IPV6_GNH_CHANGED) + || CHECK_FLAG( + vec->flags, + BPKT_ATTRVEC_FLAGS_RMAP_NH_PEER_ADDRESS)); - if (bgp_debug_update(peer, NULL, NULL, 0)) - zlog_debug("u%" PRIu64 ":s%" PRIu64 - " %s send UPDATE w/ nexthop %s%s", - PAF_SUBGRP(paf)->update_group->id, - PAF_SUBGRP(paf)->id, peer->host, - inet_ntoa(*mod_v4nh), - (nhlen == 12 ? " and RD" : "")); - } else if (nhafi == AFI_IP6) { - struct in6_addr v6nhglobal, *mod_v6nhg; - struct in6_addr v6nhlocal, *mod_v6nhl; - int gnh_modified, lnh_modified; - size_t offset_nhglobal = vec->offset + 1; - size_t offset_nhlocal = vec->offset + 1; - - gnh_modified = lnh_modified = 0; - mod_v6nhg = &v6nhglobal; - mod_v6nhl = &v6nhlocal; - - route_map_sets_nh = - (CHECK_FLAG( - vec->flags, - BPKT_ATTRVEC_FLAGS_RMAP_IPV6_GNH_CHANGED) - || CHECK_FLAG( - vec->flags, - BPKT_ATTRVEC_FLAGS_RMAP_NH_PEER_ADDRESS)); - - /* - * The logic here is rather similar to that for IPv4, - * the - * additional work being to handle 1 or 2 nexthops. - * Also, 3rd - * party nexthop is not propagated for EBGP right now. - */ - switch (nhlen) { - case BGP_ATTR_NHLEN_IPV6_GLOBAL: - break; - case BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL: - offset_nhlocal += IPV6_MAX_BYTELEN; - break; - case BGP_ATTR_NHLEN_VPNV6_GLOBAL: - offset_nhglobal += 8; - break; - case BGP_ATTR_NHLEN_VPNV6_GLOBAL_AND_LL: - offset_nhglobal += 8; - offset_nhlocal += 8 * 2 + IPV6_MAX_BYTELEN; - break; - default: - /* TODO: handle IPv4 nexthops */ - flog_warn( - EC_BGP_INVALID_NEXTHOP_LENGTH, - "%s: %s: invalid MP nexthop length (AFI IP6): %u", - __func__, peer->host, nhlen); - stream_free(s); - return NULL; - } + /* + * The logic here is rather similar to that for IPv4, the + * additional work being to handle 1 or 2 nexthops. + * Also, 3rd party nexthop is not propagated for EBGP + * right now. + */ + switch (nhlen) { + case BGP_ATTR_NHLEN_IPV6_GLOBAL: + break; + case BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL: + offset_nhlocal += IPV6_MAX_BYTELEN; + break; + case BGP_ATTR_NHLEN_VPNV6_GLOBAL: + offset_nhglobal += 8; + break; + case BGP_ATTR_NHLEN_VPNV6_GLOBAL_AND_LL: + offset_nhglobal += 8; + offset_nhlocal += 8 * 2 + IPV6_MAX_BYTELEN; + break; + default: + /* TODO: handle IPv4 nexthops */ + flog_warn( + EC_BGP_INVALID_NEXTHOP_LENGTH, + "%s: %s: invalid MP nexthop length (AFI IP6): %u", + __func__, peer->host, nhlen); + stream_free(s); + return NULL; + } - stream_get_from(&v6nhglobal, s, offset_nhglobal, - IPV6_MAX_BYTELEN); - if (route_map_sets_nh) { - if (CHECK_FLAG( - vec->flags, - BPKT_ATTRVEC_FLAGS_RMAP_NH_PEER_ADDRESS)) { - mod_v6nhg = &peer->nexthop.v6_global; - gnh_modified = 1; - } - } else if (IN6_IS_ADDR_UNSPECIFIED(&v6nhglobal)) { - mod_v6nhg = &peer->nexthop.v6_global; - gnh_modified = 1; - } else if ( - peer->sort == BGP_PEER_EBGP - && !CHECK_FLAG( - vec->flags, - BPKT_ATTRVEC_FLAGS_RMAP_NH_UNCHANGED) - && !peer_af_flag_check( - peer, nhafi, paf->safi, - PEER_FLAG_NEXTHOP_UNCHANGED)) { - /* NOTE: not handling case where NH has new AFI - */ + stream_get_from(&v6nhglobal, s, offset_nhglobal, + IPV6_MAX_BYTELEN); + + /* + * Updates to an EBGP peer should only modify the + * next-hop if it was set in an outbound route-map + * to that peer. + */ + if (route_map_sets_nh + && ((peer->sort != BGP_PEER_EBGP) + || ROUTE_MAP_OUT(filter))) { + if (CHECK_FLAG( + vec->flags, + BPKT_ATTRVEC_FLAGS_RMAP_NH_PEER_ADDRESS)) { mod_v6nhg = &peer->nexthop.v6_global; gnh_modified = 1; } + } else if (IN6_IS_ADDR_UNSPECIFIED(&v6nhglobal)) { + mod_v6nhg = &peer->nexthop.v6_global; + gnh_modified = 1; + } else if ((peer->sort == BGP_PEER_EBGP) + && (!bgp_multiaccess_check_v6(v6nhglobal, peer)) + && !CHECK_FLAG(vec->flags, + BPKT_ATTRVEC_FLAGS_RMAP_NH_UNCHANGED) + && !peer_af_flag_check( + peer, nhafi, paf->safi, + PEER_FLAG_NEXTHOP_UNCHANGED)) { + /* NOTE: not handling case where NH has new AFI + */ + mod_v6nhg = &peer->nexthop.v6_global; + gnh_modified = 1; + } - - if (nhlen == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL - || nhlen == BGP_ATTR_NHLEN_VPNV6_GLOBAL_AND_LL) { - stream_get_from(&v6nhlocal, s, offset_nhlocal, - IPV6_MAX_BYTELEN); - if (IN6_IS_ADDR_UNSPECIFIED(&v6nhlocal)) { - mod_v6nhl = &peer->nexthop.v6_local; - lnh_modified = 1; - } - } - - if (gnh_modified) - stream_put_in6_addr_at(s, offset_nhglobal, - mod_v6nhg); - if (lnh_modified) - stream_put_in6_addr_at(s, offset_nhlocal, - mod_v6nhl); - - if (bgp_debug_update(peer, NULL, NULL, 0)) { - if (nhlen == 32 || nhlen == 48) - zlog_debug( - "u%" PRIu64 ":s%" PRIu64 - " %s send UPDATE w/ mp_nexthops %s, %s%s", - PAF_SUBGRP(paf) - ->update_group->id, - PAF_SUBGRP(paf)->id, peer->host, - inet_ntop(AF_INET6, mod_v6nhg, - buf, BUFSIZ), - inet_ntop(AF_INET6, mod_v6nhl, - buf2, BUFSIZ), - (nhlen == 48 ? " and RD" : "")); - else - zlog_debug( - "u%" PRIu64 ":s%" PRIu64 - " %s send UPDATE w/ mp_nexthop %s%s", - PAF_SUBGRP(paf) - ->update_group->id, - PAF_SUBGRP(paf)->id, peer->host, - inet_ntop(AF_INET6, mod_v6nhg, - buf, BUFSIZ), - (nhlen == 24 ? " and RD" : "")); + if (IN6_IS_ADDR_UNSPECIFIED(mod_v6nhg)) { + if (peer->nexthop.v4.s_addr) { + ipv4_to_ipv4_mapped_ipv6(mod_v6nhg, + peer->nexthop.v4); } - } else if (paf->afi == AFI_L2VPN) { - struct in_addr v4nh, *mod_v4nh; - int nh_modified = 0; + } - stream_get_from(&v4nh, s, vec->offset + 1, 4); - mod_v4nh = &v4nh; + if (IS_MAPPED_IPV6(&peer->nexthop.v6_global)) { + mod_v6nhg = &peer->nexthop.v6_global; + gnh_modified = 1; + } - /* No route-map changes allowed for EVPN nexthops. */ - if (!v4nh.s_addr) { - mod_v4nh = &peer->nexthop.v4; - nh_modified = 1; + if (nhlen == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL + || nhlen == BGP_ATTR_NHLEN_VPNV6_GLOBAL_AND_LL) { + stream_get_from(&v6nhlocal, s, offset_nhlocal, + IPV6_MAX_BYTELEN); + if (IN6_IS_ADDR_UNSPECIFIED(&v6nhlocal)) { + mod_v6nhl = &peer->nexthop.v6_local; + lnh_modified = 1; } + } - if (nh_modified) - stream_put_in_addr_at(s, vec->offset + 1, - mod_v4nh); + if (gnh_modified) + stream_put_in6_addr_at(s, offset_nhglobal, mod_v6nhg); + if (lnh_modified) + stream_put_in6_addr_at(s, offset_nhlocal, mod_v6nhl); - if (bgp_debug_update(peer, NULL, NULL, 0)) - zlog_debug("u%" PRIu64 ":s%" PRIu64 - " %s send UPDATE w/ nexthop %s", + if (bgp_debug_update(peer, NULL, NULL, 0)) { + if (nhlen == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL + || nhlen == BGP_ATTR_NHLEN_VPNV6_GLOBAL_AND_LL) + zlog_debug( + "u%" PRIu64 ":s%" PRIu64" %s send UPDATE w/ mp_nexthops %s, %s%s", + PAF_SUBGRP(paf)->update_group->id, + PAF_SUBGRP(paf)->id, peer->host, + inet_ntop(AF_INET6, mod_v6nhg, buf, + BUFSIZ), + inet_ntop(AF_INET6, mod_v6nhl, buf2, + BUFSIZ), + (nhlen == BGP_ATTR_NHLEN_VPNV6_GLOBAL_AND_LL + ? " and RD" + : "")); + else + zlog_debug("u%" PRIu64 ":s%" PRIu64" %s send UPDATE w/ mp_nexthop %s%s", PAF_SUBGRP(paf)->update_group->id, PAF_SUBGRP(paf)->id, peer->host, - inet_ntoa(*mod_v4nh)); + inet_ntop(AF_INET6, mod_v6nhg, buf, + BUFSIZ), + (nhlen == BGP_ATTR_NHLEN_VPNV6_GLOBAL + ? " and RD" + : "")); } + } else if (paf->afi == AFI_L2VPN) { + struct in_addr v4nh, *mod_v4nh; + int nh_modified = 0; + + stream_get_from(&v4nh, s, vec->offset + 1, 4); + mod_v4nh = &v4nh; + + /* No route-map changes allowed for EVPN nexthops. */ + if (v4nh.s_addr == INADDR_ANY) { + mod_v4nh = &peer->nexthop.v4; + nh_modified = 1; + } + + if (nh_modified) + stream_put_in_addr_at(s, vec->offset + 1, mod_v4nh); + + if (bgp_debug_update(peer, NULL, NULL, 0)) + zlog_debug("u%" PRIu64 ":s%" PRIu64" %s send UPDATE w/ nexthop %s", + PAF_SUBGRP(paf)->update_group->id, + PAF_SUBGRP(paf)->id, peer->host, + inet_ntoa(*mod_v4nh)); } return s; @@ -657,22 +670,22 @@ static void bpacket_attr_vec_arr_update(struct bpacket_attr_vec_arr *vecarr, /* * Return if there are packets to build for this subgroup. */ -int subgroup_packets_to_build(struct update_subgroup *subgrp) +bool subgroup_packets_to_build(struct update_subgroup *subgrp) { struct bgp_advertise *adv; if (!subgrp) - return 0; + return false; adv = bgp_adv_fifo_first(&subgrp->sync->withdraw); if (adv) - return 1; + return true; adv = bgp_adv_fifo_first(&subgrp->sync->update); if (adv) - return 1; + return true; - return 0; + return false; } /* Make BGP update packet. */ @@ -686,7 +699,7 @@ struct bpacket *subgroup_update_packet(struct update_subgroup *subgrp) struct stream *packet; struct bgp_adj_out *adj; struct bgp_advertise *adv; - struct bgp_node *rn = NULL; + struct bgp_dest *dest = NULL; struct bgp_path_info *path = NULL; bgp_size_t total_attr_len = 0; unsigned long attrlen_pos = 0; @@ -727,17 +740,35 @@ struct bpacket *subgroup_update_packet(struct update_subgroup *subgrp) adv = bgp_adv_fifo_first(&subgrp->sync->update); while (adv) { - assert(adv->rn); - rn = adv->rn; + const struct prefix *dest_p; + + assert(adv->dest); + dest = adv->dest; + dest_p = bgp_dest_get_prefix(dest); adj = adv->adj; addpath_tx_id = adj->addpath_tx_id; path = adv->pathi; + /* Check if we need to add a prefix to the packet if + * maximum-prefix-out is set for the peer. + */ + if (CHECK_FLAG(peer->af_flags[afi][safi], + PEER_FLAG_MAX_PREFIX_OUT) + && subgrp->scount >= peer->pmax_out[afi][safi]) { + if (BGP_DEBUG(update, UPDATE_OUT) + || BGP_DEBUG(update, UPDATE_PREFIX)) { + zlog_debug( + "%s reached maximum prefix to be send (%u)", + peer->host, peer->pmax_out[afi][safi]); + } + goto next; + } + space_remaining = STREAM_CONCAT_REMAIN(s, snlri, STREAM_SIZE(s)) - BGP_MAX_PACKET_SIZE_OVERFLOW; space_needed = BGP_NLRI_LENGTH + addpath_overhead - + bgp_packet_mpattr_prefix_size(afi, safi, &rn->p); + + bgp_packet_mpattr_prefix_size(afi, safi, dest_p); /* When remaining space can't include NLRI and it's length. */ if (space_remaining < space_needed) @@ -783,7 +814,7 @@ struct bpacket *subgroup_update_packet(struct update_subgroup *subgrp) - BGP_MAX_PACKET_SIZE_OVERFLOW; space_needed = BGP_NLRI_LENGTH + addpath_overhead + bgp_packet_mpattr_prefix_size( - afi, safi, &rn->p); + afi, safi, dest_p); /* If the attributes alone do not leave any room for * NLRI then @@ -791,8 +822,7 @@ struct bpacket *subgroup_update_packet(struct update_subgroup *subgrp) if (space_remaining < space_needed) { flog_err( EC_BGP_UPDGRP_ATTR_LEN, - "u%" PRIu64 ":s%" PRIu64 - " attributes too long, cannot send UPDATE", + "u%" PRIu64 ":s%" PRIu64" attributes too long, cannot send UPDATE", subgrp->update_group->id, subgrp->id); /* Flush the FIFO update queue */ @@ -807,21 +837,22 @@ struct bpacket *subgroup_update_packet(struct update_subgroup *subgrp) memset(send_attr_str, 0, BUFSIZ); send_attr_printed = 0; bgp_dump_attr(adv->baa->attr, send_attr_str, - BUFSIZ); + sizeof(send_attr_str)); } } if ((afi == AFI_IP && safi == SAFI_UNICAST) && !peer_cap_enhe(peer, afi, safi)) - stream_put_prefix_addpath(s, &rn->p, addpath_encode, + stream_put_prefix_addpath(s, dest_p, addpath_encode, addpath_tx_id); else { /* Encode the prefix in MP_REACH_NLRI attribute */ - if (rn->prn) - prd = (struct prefix_rd *)&rn->prn->p; + if (dest->pdest) + prd = (struct prefix_rd *)bgp_dest_get_prefix( + dest->pdest); if (safi == SAFI_LABELED_UNICAST) { - label = bgp_adv_label(rn, path, peer, afi, + label = bgp_adv_label(dest, path, peer, afi, safi); label_pnt = &label; num_labels = 1; @@ -835,7 +866,7 @@ struct bpacket *subgroup_update_packet(struct update_subgroup *subgrp) snlri, peer, afi, safi, &vecarr, adv->baa->attr); - bgp_packet_mpattr_prefix(snlri, afi, safi, &rn->p, prd, + bgp_packet_mpattr_prefix(snlri, afi, safi, dest_p, prd, label_pnt, num_labels, addpath_encode, addpath_tx_id, adv->baa->attr); @@ -843,12 +874,11 @@ struct bpacket *subgroup_update_packet(struct update_subgroup *subgrp) num_pfx++; - if (bgp_debug_update(NULL, &rn->p, subgrp->update_group, 0)) { + if (bgp_debug_update(NULL, dest_p, subgrp->update_group, 0)) { char pfx_buf[BGP_PRD_PATH_STRLEN]; if (!send_attr_printed) { - zlog_debug("u%" PRIu64 ":s%" PRIu64 - " send UPDATE w/ attr: %s", + zlog_debug("u%" PRIu64 ":s%" PRIu64" send UPDATE w/ attr: %s", subgrp->update_group->id, subgrp->id, send_attr_str); if (!stream_empty(snlri)) { @@ -858,8 +888,7 @@ struct bpacket *subgroup_update_packet(struct update_subgroup *subgrp) pkt_afi = afi_int2iana(afi); pkt_safi = safi_int2iana(safi); zlog_debug( - "u%" PRIu64 ":s%" PRIu64 - " send MP_REACH for afi/safi %d/%d", + "u%" PRIu64 ":s%" PRIu64" send MP_REACH for afi/safi %d/%d", subgrp->update_group->id, subgrp->id, pkt_afi, pkt_safi); } @@ -867,7 +896,7 @@ struct bpacket *subgroup_update_packet(struct update_subgroup *subgrp) send_attr_printed = 1; } - bgp_debug_rdpfxpath2str(afi, safi, prd, &rn->p, + bgp_debug_rdpfxpath2str(afi, safi, prd, dest_p, label_pnt, num_labels, addpath_encode, addpath_tx_id, pfx_buf, sizeof(pfx_buf)); @@ -883,7 +912,7 @@ struct bpacket *subgroup_update_packet(struct update_subgroup *subgrp) subgrp->scount++; adj->attr = bgp_attr_intern(adv->baa->attr); - +next: adv = bgp_advertise_clean_subgroup(subgrp, adj); } @@ -903,8 +932,7 @@ struct bpacket *subgroup_update_packet(struct update_subgroup *subgrp) packet = stream_dup(s); bgp_packet_set_size(packet); if (bgp_debug_update(NULL, NULL, subgrp->update_group, 0)) - zlog_debug("u%" PRIu64 ":s%" PRIu64 - " send UPDATE len %zd numpfx %d", + zlog_debug("u%" PRIu64 ":s%" PRIu64" send UPDATE len %zd numpfx %d", subgrp->update_group->id, subgrp->id, (stream_get_endp(packet) - stream_get_getp(packet)), @@ -934,7 +962,7 @@ struct bpacket *subgroup_withdraw_packet(struct update_subgroup *subgrp) struct bgp_adj_out *adj; struct bgp_advertise *adv; struct peer *peer; - struct bgp_node *rn; + struct bgp_dest *dest; bgp_size_t unfeasible_len; bgp_size_t total_attr_len; size_t mp_start = 0; @@ -949,7 +977,7 @@ struct bpacket *subgroup_withdraw_packet(struct update_subgroup *subgrp) int addpath_encode = 0; int addpath_overhead = 0; uint32_t addpath_tx_id = 0; - struct prefix_rd *prd = NULL; + const struct prefix_rd *prd = NULL; if (!subgrp) @@ -967,16 +995,19 @@ struct bpacket *subgroup_withdraw_packet(struct update_subgroup *subgrp) addpath_overhead = addpath_encode ? BGP_ADDPATH_ID_LEN : 0; while ((adv = bgp_adv_fifo_first(&subgrp->sync->withdraw)) != NULL) { - assert(adv->rn); + const struct prefix *dest_p; + + assert(adv->dest); adj = adv->adj; - rn = adv->rn; + dest = adv->dest; + dest_p = bgp_dest_get_prefix(dest); addpath_tx_id = adj->addpath_tx_id; space_remaining = STREAM_WRITEABLE(s) - BGP_MAX_PACKET_SIZE_OVERFLOW; space_needed = BGP_NLRI_LENGTH + addpath_overhead + BGP_TOTAL_ATTR_LEN - + bgp_packet_mpattr_prefix_size(afi, safi, &rn->p); + + bgp_packet_mpattr_prefix_size(afi, safi, dest_p); if (space_remaining < space_needed) break; @@ -989,13 +1020,15 @@ struct bpacket *subgroup_withdraw_packet(struct update_subgroup *subgrp) if (afi == AFI_IP && safi == SAFI_UNICAST && !peer_cap_enhe(peer, afi, safi)) - stream_put_prefix_addpath(s, &rn->p, addpath_encode, + stream_put_prefix_addpath(s, dest_p, addpath_encode, addpath_tx_id); else { - if (rn->prn) - prd = (struct prefix_rd *)&rn->prn->p; + if (dest->pdest) + prd = (struct prefix_rd *)bgp_dest_get_prefix( + dest->pdest); - /* If first time, format the MP_UNREACH header */ + /* If first time, format the MP_UNREACH header + */ if (first_time) { iana_afi_t pkt_afi; iana_safi_t pkt_safi; @@ -1004,8 +1037,8 @@ struct bpacket *subgroup_withdraw_packet(struct update_subgroup *subgrp) pkt_safi = safi_int2iana(safi); attrlen_pos = stream_get_endp(s); - /* total attr length = 0 for now. reevaluate - * later */ + /* total attr length = 0 for now. + * reevaluate later */ stream_putw(s, 0); mp_start = stream_get_endp(s); mplen_pos = bgp_packet_mpunreach_start(s, afi, @@ -1013,35 +1046,33 @@ struct bpacket *subgroup_withdraw_packet(struct update_subgroup *subgrp) if (bgp_debug_update(NULL, NULL, subgrp->update_group, 0)) zlog_debug( - "u%" PRIu64 ":s%" PRIu64 - " send MP_UNREACH for afi/safi %d/%d", + "u%" PRIu64 ":s%" PRIu64" send MP_UNREACH for afi/safi %d/%d", subgrp->update_group->id, subgrp->id, pkt_afi, pkt_safi); } - bgp_packet_mpunreach_prefix(s, &rn->p, afi, safi, prd, + bgp_packet_mpunreach_prefix(s, dest_p, afi, safi, prd, NULL, 0, addpath_encode, addpath_tx_id, NULL); } num_pfx++; - if (bgp_debug_update(NULL, &rn->p, subgrp->update_group, 0)) { + if (bgp_debug_update(NULL, dest_p, subgrp->update_group, 0)) { char pfx_buf[BGP_PRD_PATH_STRLEN]; - bgp_debug_rdpfxpath2str(afi, safi, prd, &rn->p, NULL, 0, + bgp_debug_rdpfxpath2str(afi, safi, prd, dest_p, NULL, 0, addpath_encode, addpath_tx_id, pfx_buf, sizeof(pfx_buf)); - zlog_debug("u%" PRIu64 ":s%" PRIu64 - " send UPDATE %s -- unreachable", + zlog_debug("u%" PRIu64 ":s%" PRIu64" send UPDATE %s -- unreachable", subgrp->update_group->id, subgrp->id, pfx_buf); } subgrp->scount--; - bgp_adj_out_remove_subgroup(rn, adj, subgrp); - bgp_unlock_node(rn); + bgp_adj_out_remove_subgroup(dest, adj, subgrp); + bgp_dest_unlock_node(dest); } if (!stream_empty(s)) { @@ -1061,8 +1092,7 @@ struct bpacket *subgroup_withdraw_packet(struct update_subgroup *subgrp) } bgp_packet_set_size(s); if (bgp_debug_update(NULL, NULL, subgrp->update_group, 0)) - zlog_debug("u%" PRIu64 ":s%" PRIu64 - " send UPDATE (withdraw) len %zd numpfx %d", + zlog_debug("u%" PRIu64 ":s%" PRIu64" send UPDATE (withdraw) len %zd numpfx %d", subgrp->update_group->id, subgrp->id, (stream_get_endp(s) - stream_get_getp(s)), num_pfx); @@ -1117,7 +1147,7 @@ void subgroup_default_update_packet(struct update_subgroup *subgrp, attrstr[0] = '\0'; - bgp_dump_attr(attr, attrstr, BUFSIZ); + bgp_dump_attr(attr, attrstr, sizeof(attrstr)); if (addpath_encode) snprintf(tx_id_buf, sizeof(tx_id_buf), @@ -1205,8 +1235,7 @@ void subgroup_default_withdraw_packet(struct update_subgroup *subgrp) " with addpath ID %u", BGP_ADDPATH_TX_ID_FOR_DEFAULT_ORIGINATE); - zlog_debug("u%" PRIu64 ":s%" PRIu64 - " send UPDATE %s%s -- unreachable", + zlog_debug("u%" PRIu64 ":s%" PRIu64" send UPDATE %s%s -- unreachable", (SUBGRP_UPDGRP(subgrp))->id, subgrp->id, prefix2str(&p, buf, sizeof(buf)), tx_id_buf); } diff --git a/bgpd/bgp_vnc_types.h b/bgpd/bgp_vnc_types.h index f4202ff75e..04847ce6c9 100644 --- a/bgpd/bgp_vnc_types.h +++ b/bgpd/bgp_vnc_types.h @@ -19,7 +19,7 @@ #ifndef _QUAGGA_BGP_VNC_TYPES_H #define _QUAGGA_BGP_VNC_TYPES_H -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC typedef enum { BGP_VNC_SUBTLV_TYPE_LIFETIME = 1, BGP_VNC_SUBTLV_TYPE_RFPOPTION = 2, /* deprecated */ diff --git a/bgpd/bgp_vpn.c b/bgpd/bgp_vpn.c index 54ca980cad..0b5d156e6d 100644 --- a/bgpd/bgp_vpn.c +++ b/bgpd/bgp_vpn.c @@ -29,6 +29,7 @@ #include "bgpd/bgp_attr.h" #include "bgpd/bgp_mplsvpn.h" #include "bgpd/bgp_vpn.h" +#include "bgpd/bgp_updgrp.h" int show_adj_route_vpn(struct vty *vty, struct peer *peer, struct prefix_rd *prd, afi_t afi, safi_t safi, @@ -36,16 +37,17 @@ int show_adj_route_vpn(struct vty *vty, struct peer *peer, { struct bgp *bgp; struct bgp_table *table; - struct bgp_node *rn; - struct bgp_node *rm; - struct bgp_path_info *path; + struct bgp_dest *dest; + struct bgp_dest *rm; int rd_header; int header = 1; json_object *json = NULL; json_object *json_scode = NULL; json_object *json_ocode = NULL; + json_object *json_adv = NULL; json_object *json_routes = NULL; - json_object *json_array = NULL; + char rd_str[BUFSIZ]; + unsigned long output_count = 0; bgp = bgp_get_default(); if (bgp == NULL) { @@ -59,8 +61,8 @@ int show_adj_route_vpn(struct vty *vty, struct peer *peer, if (use_json) { json_scode = json_object_new_object(); json_ocode = json_object_new_object(); - json_routes = json_object_new_object(); json = json_object_new_object(); + json_adv = json_object_new_object(); json_object_string_add(json_scode, "suppressed", "s"); json_object_string_add(json_scode, "damped", "d"); @@ -74,25 +76,43 @@ int show_adj_route_vpn(struct vty *vty, struct peer *peer, json_object_string_add(json_ocode, "incomplete", "?"); } - for (rn = bgp_table_top(bgp->rib[afi][SAFI_MPLS_VPN]); rn; - rn = bgp_route_next(rn)) { - if (prd && memcmp(rn->p.u.val, prd->val, 8) != 0) + for (dest = bgp_table_top(bgp->rib[afi][safi]); dest; + dest = bgp_route_next(dest)) { + const struct prefix *dest_p = bgp_dest_get_prefix(dest); + + if (prd && memcmp(dest_p->u.val, prd->val, 8) != 0) continue; - table = bgp_node_get_bgp_table_info(rn); + table = bgp_dest_get_bgp_table_info(dest); if (table == NULL) continue; - if (use_json) - json_array = json_object_new_array(); - else - json_array = NULL; - + /* + * Initialize variables for each RD + * All prefixes under an RD is aggregated within "json_routes" + */ rd_header = 1; + memset(rd_str, 0, sizeof(rd_str)); + json_routes = NULL; for (rm = bgp_table_top(table); rm; rm = bgp_route_next(rm)) { - path = bgp_node_get_bgp_path_info(rm); - if (path == NULL) + struct bgp_adj_out *adj = NULL; + struct attr *attr = NULL; + struct peer_af *paf = NULL; + + RB_FOREACH (adj, bgp_adj_out_rb, &rm->adj_out) + SUBGRP_FOREACH_PEER (adj->subgroup, paf) { + if (paf->peer != peer || !adj->attr) + continue; + + attr = adj->attr; + break; + } + + if (bgp_dest_get_bgp_path_info(rm) == NULL) + continue; + + if (!attr) continue; if (header) { @@ -102,6 +122,13 @@ int show_adj_route_vpn(struct vty *vty, struct peer *peer, json_object_string_add( json, "bgpLocalRouterId", inet_ntoa(bgp->router_id)); + json_object_int_add( + json, + "defaultLocPrf", + bgp->default_local_pref); + json_object_int_add( + json, "localAS", + bgp->as); json_object_object_add(json, "bgpStatusCodes", json_scode); @@ -112,6 +139,9 @@ int show_adj_route_vpn(struct vty *vty, struct peer *peer, vty_out(vty, "BGP table version is 0, local router ID is %s\n", inet_ntoa(bgp->router_id)); + vty_out(vty, "Default local pref %u, ", + bgp->default_local_pref); + vty_out(vty, "local AS %u\n", bgp->as); vty_out(vty, "Status codes: s suppressed, d damped, h history, * valid, > best, i - internal\n"); vty_out(vty, @@ -125,12 +155,12 @@ int show_adj_route_vpn(struct vty *vty, struct peer *peer, uint16_t type; struct rd_as rd_as = {0}; struct rd_ip rd_ip = {0}; -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC struct rd_vnc_eth rd_vnc_eth = {0}; #endif - uint8_t *pnt; + const uint8_t *pnt; - pnt = rn->p.u.val; + pnt = dest_p->u.val; /* Decode RD type. */ type = decode_rd_type(pnt); @@ -141,23 +171,26 @@ int show_adj_route_vpn(struct vty *vty, struct peer *peer, decode_rd_as4(pnt + 2, &rd_as); else if (type == RD_TYPE_IP) decode_rd_ip(pnt + 2, &rd_ip); -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC else if (type == RD_TYPE_VNC_ETH) decode_rd_vnc_eth(pnt, &rd_vnc_eth); #endif if (use_json) { - char buffer[BUFSIZ]; + json_routes = json_object_new_object(); + if (type == RD_TYPE_AS || type == RD_TYPE_AS4) - sprintf(buffer, "%u:%d", - rd_as.as, rd_as.val); + snprintf(rd_str, sizeof(rd_str), + "%u:%d", rd_as.as, + rd_as.val); else if (type == RD_TYPE_IP) - sprintf(buffer, "%s:%d", - inet_ntoa(rd_ip.ip), - rd_ip.val); + snprintf(rd_str, sizeof(rd_str), + "%s:%d", + inet_ntoa(rd_ip.ip), + rd_ip.val); json_object_string_add( json_routes, - "routeDistinguisher", buffer); + "rd", rd_str); } else { vty_out(vty, "Route Distinguisher: "); @@ -169,7 +202,7 @@ int show_adj_route_vpn(struct vty *vty, struct peer *peer, vty_out(vty, "%s:%d", inet_ntoa(rd_ip.ip), rd_ip.val); -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC else if (type == RD_TYPE_VNC_ETH) vty_out(vty, "%u:%02x:%02x:%02x:%02x:%02x:%02x", @@ -192,24 +225,24 @@ int show_adj_route_vpn(struct vty *vty, struct peer *peer, } rd_header = 0; } - if (use_json) { - char buf[BUFSIZ]; - - prefix2str(&rm->p, buf, sizeof(buf)); - json_object_object_add(json_routes, buf, - json_array); - } else { - route_vty_out_tmp(vty, &rm->p, path->attr, - SAFI_MPLS_VPN, use_json, - json_array); - } + route_vty_out_tmp(vty, bgp_dest_get_prefix(rm), attr, + safi, use_json, json_routes, false); + output_count++; } + + if (use_json && json_routes) + json_object_object_add(json_adv, rd_str, json_routes); } + if (use_json) { - json_object_object_add(json, "routes", json_routes); + json_object_object_add(json, "advertisedRoutes", json_adv); + json_object_int_add(json, + "totalPrefixCounter", output_count); vty_out(vty, "%s\n", json_object_to_json_string_ext( json, JSON_C_TO_STRING_PRETTY)); json_object_free(json); - } + } else + vty_out(vty, "\nTotal number of prefixes %ld\n", output_count); + return CMD_SUCCESS; } diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 01144f5c78..af7113c974 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -22,6 +22,7 @@ #include "command.h" #include "lib/json.h" +#include "lib_errors.h" #include "lib/zclient.h" #include "prefix.h" #include "plist.h" @@ -31,7 +32,7 @@ #include "thread.h" #include "log.h" #include "memory.h" -#include "memory_vty.h" +#include "lib_vty.h" #include "hash.h" #include "queue.h" #include "filter.h" @@ -63,12 +64,91 @@ #include "bgpd/bgp_bfd.h" #include "bgpd/bgp_io.h" #include "bgpd/bgp_evpn.h" +#include "bgpd/bgp_evpn_vty.h" +#include "bgpd/bgp_evpn_mh.h" #include "bgpd/bgp_addpath.h" #include "bgpd/bgp_mac.h" +#include "bgpd/bgp_flowspec.h" +#ifdef ENABLE_BGP_VNC +#include "bgpd/rfapi/bgp_rfapi_cfg.h" +#endif + +FRR_CFG_DEFAULT_BOOL(BGP_IMPORT_CHECK, + { + .val_bool = false, + .match_profile = "traditional", + .match_version = "< 7.4", + }, + { .val_bool = true }, +) +FRR_CFG_DEFAULT_BOOL(BGP_SHOW_HOSTNAME, + { .val_bool = true, .match_profile = "datacenter", }, + { .val_bool = false }, +) +FRR_CFG_DEFAULT_BOOL(BGP_SHOW_NEXTHOP_HOSTNAME, + { .val_bool = true, .match_profile = "datacenter", }, + { .val_bool = false }, +) +FRR_CFG_DEFAULT_BOOL(BGP_LOG_NEIGHBOR_CHANGES, + { .val_bool = true, .match_profile = "datacenter", }, + { .val_bool = false }, +) +FRR_CFG_DEFAULT_BOOL(BGP_DETERMINISTIC_MED, + { .val_bool = true, .match_profile = "datacenter", }, + { .val_bool = false }, +) +FRR_CFG_DEFAULT_ULONG(BGP_CONNECT_RETRY, + { .val_ulong = 10, .match_profile = "datacenter", }, + { .val_ulong = 120 }, +) +FRR_CFG_DEFAULT_ULONG(BGP_HOLDTIME, + { .val_ulong = 9, .match_profile = "datacenter", }, + { .val_ulong = 180 }, +) +FRR_CFG_DEFAULT_ULONG(BGP_KEEPALIVE, + { .val_ulong = 3, .match_profile = "datacenter", }, + { .val_ulong = 60 }, +) +FRR_CFG_DEFAULT_BOOL(BGP_EBGP_REQUIRES_POLICY, + { .val_bool = false, .match_profile = "datacenter", }, + { .val_bool = false, .match_version = "< 7.4", }, + { .val_bool = true }, +) + +DEFINE_HOOK(bgp_inst_config_write, + (struct bgp *bgp, struct vty *vty), + (bgp, vty)) + +#define GR_NO_OPER \ + "The Graceful Restart No Operation was executed as cmd same as previous one." +#define GR_INVALID \ + "The Graceful Restart command used is not valid at this moment." +static struct peer_group *listen_range_exists(struct bgp *bgp, + struct prefix *range, int exact); + +/* Show BGP peer's information. */ +enum show_type { + show_all, + show_peer, + show_ipv4_all, + show_ipv6_all, + show_ipv4_peer, + show_ipv6_peer +}; static struct peer_group *listen_range_exists(struct bgp *bgp, struct prefix *range, int exact); +static void bgp_show_global_graceful_restart_mode_vty(struct vty *vty, + struct bgp *bgp, + bool use_json, + json_object *json); + +static int bgp_show_neighbor_graceful_restart_afi_all(struct vty *vty, + enum show_type type, + const char *ip_str, + afi_t afi, bool use_json); + static enum node_type bgp_node_type(afi_t afi, safi_t safi) { switch (afi) { @@ -76,59 +156,118 @@ static enum node_type bgp_node_type(afi_t afi, safi_t safi) switch (safi) { case SAFI_UNICAST: return BGP_IPV4_NODE; - break; case SAFI_MULTICAST: return BGP_IPV4M_NODE; - break; case SAFI_LABELED_UNICAST: return BGP_IPV4L_NODE; - break; case SAFI_MPLS_VPN: return BGP_VPNV4_NODE; - break; case SAFI_FLOWSPEC: return BGP_FLOWSPECV4_NODE; default: /* not expected */ return BGP_IPV4_NODE; - break; } break; case AFI_IP6: switch (safi) { case SAFI_UNICAST: return BGP_IPV6_NODE; - break; case SAFI_MULTICAST: return BGP_IPV6M_NODE; - break; case SAFI_LABELED_UNICAST: return BGP_IPV6L_NODE; - break; case SAFI_MPLS_VPN: return BGP_VPNV6_NODE; - break; case SAFI_FLOWSPEC: return BGP_FLOWSPECV6_NODE; default: /* not expected */ return BGP_IPV4_NODE; - break; } break; case AFI_L2VPN: return BGP_EVPN_NODE; - break; + case AFI_UNSPEC: case AFI_MAX: // We should never be here but to clarify the switch statement.. return BGP_IPV4_NODE; - break; } // Impossible to happen return BGP_IPV4_NODE; } +static const char *get_afi_safi_vty_str(afi_t afi, safi_t safi) +{ + if (afi == AFI_IP && safi == SAFI_UNICAST) + return "IPv4 Unicast"; + else if (afi == AFI_IP && safi == SAFI_MULTICAST) + return "IPv4 Multicast"; + else if (afi == AFI_IP && safi == SAFI_LABELED_UNICAST) + return "IPv4 Labeled Unicast"; + else if (afi == AFI_IP && safi == SAFI_MPLS_VPN) + return "IPv4 VPN"; + else if (afi == AFI_IP && safi == SAFI_ENCAP) + return "IPv4 Encap"; + else if (afi == AFI_IP && safi == SAFI_FLOWSPEC) + return "IPv4 Flowspec"; + else if (afi == AFI_IP6 && safi == SAFI_UNICAST) + return "IPv6 Unicast"; + else if (afi == AFI_IP6 && safi == SAFI_MULTICAST) + return "IPv6 Multicast"; + else if (afi == AFI_IP6 && safi == SAFI_LABELED_UNICAST) + return "IPv6 Labeled Unicast"; + else if (afi == AFI_IP6 && safi == SAFI_MPLS_VPN) + return "IPv6 VPN"; + else if (afi == AFI_IP6 && safi == SAFI_ENCAP) + return "IPv6 Encap"; + else if (afi == AFI_IP6 && safi == SAFI_FLOWSPEC) + return "IPv6 Flowspec"; + else if (afi == AFI_L2VPN && safi == SAFI_EVPN) + return "L2VPN EVPN"; + else + return "Unknown"; +} + +/* + * Please note that we have intentionally camelCased + * the return strings here. So if you want + * to use this function, please ensure you + * are doing this within json output + */ +static const char *get_afi_safi_json_str(afi_t afi, safi_t safi) +{ + if (afi == AFI_IP && safi == SAFI_UNICAST) + return "ipv4Unicast"; + else if (afi == AFI_IP && safi == SAFI_MULTICAST) + return "ipv4Multicast"; + else if (afi == AFI_IP && safi == SAFI_LABELED_UNICAST) + return "ipv4LabeledUnicast"; + else if (afi == AFI_IP && safi == SAFI_MPLS_VPN) + return "ipv4Vpn"; + else if (afi == AFI_IP && safi == SAFI_ENCAP) + return "ipv4Encap"; + else if (afi == AFI_IP && safi == SAFI_FLOWSPEC) + return "ipv4Flowspec"; + else if (afi == AFI_IP6 && safi == SAFI_UNICAST) + return "ipv6Unicast"; + else if (afi == AFI_IP6 && safi == SAFI_MULTICAST) + return "ipv6Multicast"; + else if (afi == AFI_IP6 && safi == SAFI_LABELED_UNICAST) + return "ipv6LabeledUnicast"; + else if (afi == AFI_IP6 && safi == SAFI_MPLS_VPN) + return "ipv6Vpn"; + else if (afi == AFI_IP6 && safi == SAFI_ENCAP) + return "ipv6Encap"; + else if (afi == AFI_IP6 && safi == SAFI_FLOWSPEC) + return "ipv6Flowspec"; + else if (afi == AFI_L2VPN && safi == SAFI_EVPN) + return "l2VpnEvpn"; + else + return "Unknown"; +} + /* Utility function to get address family from current node. */ afi_t bgp_node_afi(struct vty *vty) { @@ -216,6 +355,10 @@ int argv_find_and_parse_afi(struct cmd_token **argv, int argc, int *index, ret = 1; if (afi) *afi = AFI_IP6; + } else if (argv_find(argv, argc, "l2vpn", index)) { + ret = 1; + if (afi) + *afi = AFI_L2VPN; } return ret; } @@ -259,6 +402,10 @@ int argv_find_and_parse_safi(struct cmd_token **argv, int argc, int *index, ret = 1; if (safi) *safi = SAFI_MPLS_VPN; + } else if (argv_find(argv, argc, "evpn", index)) { + ret = 1; + if (safi) + *safi = SAFI_EVPN; } else if (argv_find(argv, argc, "flowspec", index)) { ret = 1; if (safi) @@ -267,6 +414,33 @@ int argv_find_and_parse_safi(struct cmd_token **argv, int argc, int *index, return ret; } +int bgp_get_vty(struct bgp **bgp, as_t *as, const char *name, + enum bgp_instance_type inst_type) +{ + int ret = bgp_get(bgp, as, name, inst_type); + + if (ret == BGP_CREATED) { + bgp_timers_set(*bgp, DFLT_BGP_KEEPALIVE, DFLT_BGP_HOLDTIME, + DFLT_BGP_CONNECT_RETRY); + + if (DFLT_BGP_IMPORT_CHECK) + SET_FLAG((*bgp)->flags, BGP_FLAG_IMPORT_CHECK); + if (DFLT_BGP_SHOW_HOSTNAME) + SET_FLAG((*bgp)->flags, BGP_FLAG_SHOW_HOSTNAME); + if (DFLT_BGP_SHOW_NEXTHOP_HOSTNAME) + SET_FLAG((*bgp)->flags, BGP_FLAG_SHOW_NEXTHOP_HOSTNAME); + if (DFLT_BGP_LOG_NEIGHBOR_CHANGES) + SET_FLAG((*bgp)->flags, BGP_FLAG_LOG_NEIGHBOR_CHANGES); + if (DFLT_BGP_DETERMINISTIC_MED) + SET_FLAG((*bgp)->flags, BGP_FLAG_DETERMINISTIC_MED); + if (DFLT_BGP_EBGP_REQUIRES_POLICY) + SET_FLAG((*bgp)->flags, BGP_FLAG_EBGP_REQUIRES_POLICY); + + ret = BGP_SUCCESS; + } + return ret; +} + /* * bgp_vty_find_and_parse_afi_safi_bgp * @@ -295,6 +469,7 @@ int argv_find_and_parse_safi(struct cmd_token **argv, int argc, int *index, * afi -> The parsed afi if it was included in the show command, returned here * safi -> The parsed safi if it was included in the show command, returned here * bgp -> Pointer to the bgp data structure we need to fill in. + * use_json -> json is configured or not * * The function returns the correct location in the parse tree for the * last token found. @@ -329,8 +504,17 @@ int bgp_vty_find_and_parse_afi_safi_bgp(struct vty *vty, else { *bgp = bgp_lookup_by_name(vrf_name); if (!*bgp) { - if (use_json) - vty_out(vty, "{}\n"); + if (use_json) { + json_object *json = NULL; + json = json_object_new_object(); + json_object_string_add( + json, "warning", + "View/Vrf is unknown"); + vty_out(vty, "%s\n", + json_object_to_json_string_ext(json, + JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } else vty_out(vty, "View/Vrf %s is unknown\n", vrf_name); @@ -341,8 +525,17 @@ int bgp_vty_find_and_parse_afi_safi_bgp(struct vty *vty, } else { *bgp = bgp_get_default(); if (!*bgp) { - if (use_json) - vty_out(vty, "{}\n"); + if (use_json) { + json_object *json = NULL; + json = json_object_new_object(); + json_object_string_add( + json, "warning", + "Default BGP instance not found"); + vty_out(vty, "%s\n", + json_object_to_json_string_ext(json, + JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } else vty_out(vty, "Default BGP instance not found\n"); @@ -358,7 +551,7 @@ int bgp_vty_find_and_parse_afi_safi_bgp(struct vty *vty, return *idx; } -static int peer_address_self_check(struct bgp *bgp, union sockunion *su) +static bool peer_address_self_check(struct bgp *bgp, union sockunion *su) { struct interface *ifp = NULL; @@ -370,9 +563,9 @@ static int peer_address_self_check(struct bgp *bgp, union sockunion *su) bgp->vrf_id); if (ifp) - return 1; + return true; - return 0; + return false; } /* Utility function for looking up peer from VTY. */ @@ -532,6 +725,12 @@ int bgp_vty_return(struct vty *vty, int ret) case BGP_ERR_PEER_SAFI_CONFLICT: str = "Cannot activate peer for both 'ipv4 unicast' and 'ipv4 labeled-unicast'"; break; + case BGP_ERR_GR_INVALID_CMD: + str = "The Graceful Restart command used is not valid at this moment."; + break; + case BGP_ERR_GR_OPERATION_FAILED: + str = "The Graceful Restart Operation failed due to an err."; + break; } if (str) { vty_out(vty, "%% %s\n", str); @@ -556,7 +755,7 @@ static void bgp_clear_vty_error(struct vty *vty, struct peer *peer, afi_t afi, case BGP_ERR_AF_UNCONFIGURED: vty_out(vty, "%%BGP: Enable %s address family for the neighbor %s\n", - afi_safi_print(afi, safi), peer->host); + get_afi_safi_str(afi, safi, false), peer->host); break; case BGP_ERR_SOFT_RECONFIG_UNCONFIGURED: vty_out(vty, @@ -568,15 +767,65 @@ static void bgp_clear_vty_error(struct vty *vty, struct peer *peer, afi_t afi, } } +static int bgp_peer_clear(struct peer *peer, afi_t afi, safi_t safi, + struct listnode **nnode, enum bgp_clear_type stype) +{ + int ret = 0; + + /* if afi/.safi not specified, spin thru all of them */ + if ((afi == AFI_UNSPEC) && (safi == SAFI_UNSPEC)) { + afi_t tmp_afi; + safi_t tmp_safi; + + FOREACH_AFI_SAFI (tmp_afi, tmp_safi) { + if (!peer->afc[tmp_afi][tmp_safi]) + continue; + + if (stype == BGP_CLEAR_SOFT_NONE) + ret = peer_clear(peer, nnode); + else + ret = peer_clear_soft(peer, tmp_afi, tmp_safi, + stype); + } + /* if afi specified and safi not, spin thru safis on this afi */ + } else if (safi == SAFI_UNSPEC) { + safi_t tmp_safi; + + for (tmp_safi = SAFI_UNICAST; + tmp_safi < SAFI_MAX; tmp_safi++) { + if (!peer->afc[afi][tmp_safi]) + continue; + + if (stype == BGP_CLEAR_SOFT_NONE) + ret = peer_clear(peer, nnode); + else + ret = peer_clear_soft(peer, afi, + tmp_safi, stype); + } + /* both afi/safi specified, let the caller know if not defined */ + } else { + if (!peer->afc[afi][safi]) + return 1; + + if (stype == BGP_CLEAR_SOFT_NONE) + ret = peer_clear(peer, nnode); + else + ret = peer_clear_soft(peer, afi, safi, stype); + } + + return ret; +} + /* `clear ip bgp' functions. */ static int bgp_clear(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, enum clear_sort sort, enum bgp_clear_type stype, const char *arg) { - int ret; + int ret = 0; bool found = false; struct peer *peer; - struct listnode *node, *nnode; + + VTY_BGP_GR_DEFINE_LOOP_VARIABLE; /* Clear all neighbors. */ /* @@ -586,28 +835,31 @@ static int bgp_clear(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, */ if (sort == clear_all) { for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { - if (!peer->afc[afi][safi]) - continue; - if (stype == BGP_CLEAR_SOFT_NONE) - ret = peer_clear(peer, &nnode); - else - ret = peer_clear_soft(peer, afi, safi, stype); + bgp_peer_gr_flags_update(peer); + + if (CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART)) + gr_router_detected = true; + + ret = bgp_peer_clear(peer, afi, safi, &nnode, + stype); if (ret < 0) bgp_clear_vty_error(vty, peer, afi, safi, ret); - else - found = true; + } + + if (gr_router_detected + && bgp->present_zebra_gr_state == ZEBRA_GR_DISABLE) { + bgp_zebra_send_capabilities(bgp, false); + } else if (!gr_router_detected + && bgp->present_zebra_gr_state == ZEBRA_GR_ENABLE) { + bgp_zebra_send_capabilities(bgp, true); } /* This is to apply read-only mode on this clear. */ if (stype == BGP_CLEAR_SOFT_NONE) bgp->update_delay_over = 0; - if (!found) - vty_out(vty, "%%BGP: No %s peer configured\n", - afi_safi_print(afi, safi)); - return CMD_SUCCESS; } @@ -638,12 +890,14 @@ static int bgp_clear(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, } } - if (!peer->afc[afi][safi]) + VTY_BGP_GR_ROUTER_DETECT(bgp, peer, peer->bgp->peer); + VTY_SEND_BGP_GR_CAPABILITY_TO_ZEBRA(peer->bgp, ret); + + ret = bgp_peer_clear(peer, afi, safi, NULL, stype); + + /* if afi/safi not defined for this peer, let caller know */ + if (ret == 1) ret = BGP_ERR_AF_UNCONFIGURED; - else if (stype == BGP_CLEAR_SOFT_NONE) - ret = peer_clear(peer, NULL); - else - ret = peer_clear_soft(peer, afi, safi, stype); if (ret < 0) bgp_clear_vty_error(vty, peer, afi, safi, ret); @@ -662,13 +916,7 @@ static int bgp_clear(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, } for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) { - if (!peer->afc[afi][safi]) - continue; - - if (stype == BGP_CLEAR_SOFT_NONE) - ret = peer_clear(peer, NULL); - else - ret = peer_clear_soft(peer, afi, safi, stype); + ret = bgp_peer_clear(peer, afi, safi, &nnode, stype); if (ret < 0) bgp_clear_vty_error(vty, peer, afi, safi, ret); @@ -679,7 +927,7 @@ static int bgp_clear(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, if (!found) vty_out(vty, "%%BGP: No %s peer belonging to peer-group %s is configured\n", - afi_safi_print(afi, safi), arg); + get_afi_safi_str(afi, safi, false), arg); return CMD_SUCCESS; } @@ -690,13 +938,12 @@ static int bgp_clear(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, if (peer->sort == BGP_PEER_IBGP) continue; - if (!peer->afc[afi][safi]) - continue; + bgp_peer_gr_flags_update(peer); - if (stype == BGP_CLEAR_SOFT_NONE) - ret = peer_clear(peer, &nnode); - else - ret = peer_clear_soft(peer, afi, safi, stype); + if (CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART)) + gr_router_detected = true; + + ret = bgp_peer_clear(peer, afi, safi, &nnode, stype); if (ret < 0) bgp_clear_vty_error(vty, peer, afi, safi, ret); @@ -704,10 +951,18 @@ static int bgp_clear(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, found = true; } + if (gr_router_detected + && bgp->present_zebra_gr_state == ZEBRA_GR_DISABLE) { + bgp_zebra_send_capabilities(bgp, false); + } else if (!gr_router_detected + && bgp->present_zebra_gr_state == ZEBRA_GR_ENABLE) { + bgp_zebra_send_capabilities(bgp, true); + } + if (!found) vty_out(vty, "%%BGP: No external %s peer is configured\n", - afi_safi_print(afi, safi)); + get_afi_safi_str(afi, safi, false)); return CMD_SUCCESS; } @@ -720,12 +975,12 @@ static int bgp_clear(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, if (peer->as != as) continue; - if (!peer->afc[afi][safi]) - ret = BGP_ERR_AF_UNCONFIGURED; - else if (stype == BGP_CLEAR_SOFT_NONE) - ret = peer_clear(peer, &nnode); - else - ret = peer_clear_soft(peer, afi, safi, stype); + bgp_peer_gr_flags_update(peer); + + if (CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART)) + gr_router_detected = true; + + ret = bgp_peer_clear(peer, afi, safi, &nnode, stype); if (ret < 0) bgp_clear_vty_error(vty, peer, afi, safi, ret); @@ -733,10 +988,18 @@ static int bgp_clear(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, found = true; } + if (gr_router_detected + && bgp->present_zebra_gr_state == ZEBRA_GR_DISABLE) { + bgp_zebra_send_capabilities(bgp, false); + } else if (!gr_router_detected + && bgp->present_zebra_gr_state == ZEBRA_GR_ENABLE) { + bgp_zebra_send_capabilities(bgp, true); + } + if (!found) vty_out(vty, "%%BGP: No %s peer is configured with AS %s\n", - afi_safi_print(afi, safi), arg); + get_afi_safi_str(afi, safi, false), arg); return CMD_SUCCESS; } @@ -795,41 +1058,6 @@ static void bgp_clear_star_soft_out(struct vty *vty, const char *name) #include "bgpd/bgp_vty_clippy.c" #endif -/* BGP global configuration. */ -#if (CONFDATE > 20190601) -CPP_NOTICE("bgpd: time to remove deprecated bgp multiple-instance") -CPP_NOTICE("This includes BGP_OPT_MULTIPLE_INSTANCE") -#endif -DEFUN_HIDDEN (bgp_multiple_instance_func, - bgp_multiple_instance_cmd, - "bgp multiple-instance", - BGP_STR - "Enable bgp multiple instance\n") -{ - bgp_option_set(BGP_OPT_MULTIPLE_INSTANCE); - return CMD_SUCCESS; -} - -DEFUN_HIDDEN (no_bgp_multiple_instance, - no_bgp_multiple_instance_cmd, - "no bgp multiple-instance", - NO_STR - BGP_STR - "BGP multiple instance\n") -{ - int ret; - - vty_out(vty, "This config option is deprecated, and is scheduled for removal.\n"); - vty_out(vty, "if you are using this please let the developers know\n"); - zlog_info("Deprecated option: `bgp multiple-instance` being used"); - ret = bgp_option_unset(BGP_OPT_MULTIPLE_INSTANCE); - if (ret < 0) { - vty_out(vty, "%% There are more than two BGP instances\n"); - return CMD_WARNING_CONFIG_FAILED; - } - return CMD_SUCCESS; -} - DEFUN_HIDDEN (bgp_local_mac, bgp_local_mac_cmd, "bgp local-mac vni " CMD_VNI_RANGE " mac WORD seq (0-4294967295)", @@ -863,7 +1091,8 @@ DEFUN_HIDDEN (bgp_local_mac, return CMD_WARNING; } - rv = bgp_evpn_local_macip_add(bgp, vni, &mac, &ip, 0 /* flags */, seq); + rv = bgp_evpn_local_macip_add(bgp, vni, &mac, &ip, 0 /* flags */, seq, + zero_esi); if (rv < 0) { vty_out(vty, "Internal error\n"); return CMD_WARNING; @@ -911,44 +1140,6 @@ DEFUN_HIDDEN (no_bgp_local_mac, return CMD_SUCCESS; } -#if (CONFDATE > 20190601) -CPP_NOTICE("bgpd: time to remove deprecated cli bgp config-type cisco") -CPP_NOTICE("This includes BGP_OPT_CISCO_CONFIG") -#endif -DEFUN_HIDDEN (bgp_config_type, - bgp_config_type_cmd, - "bgp config-type ", - BGP_STR - "Configuration type\n" - "cisco\n" - "zebra\n") -{ - int idx = 0; - if (argv_find(argv, argc, "cisco", &idx)) { - vty_out(vty, "This config option is deprecated, and is scheduled for removal.\n"); - vty_out(vty, "if you are using this please let the developers know!\n"); - zlog_info("Deprecated option: `bgp config-type cisco` being used"); - bgp_option_set(BGP_OPT_CONFIG_CISCO); - } else - bgp_option_unset(BGP_OPT_CONFIG_CISCO); - - return CMD_SUCCESS; -} - -DEFUN_HIDDEN (no_bgp_config_type, - no_bgp_config_type_cmd, - "no bgp config-type []", - NO_STR - BGP_STR - "Display configuration type\n" - "cisco\n" - "zebra\n") -{ - bgp_option_unset(BGP_OPT_CONFIG_CISCO); - return CMD_SUCCESS; -} - - DEFUN (no_synchronization, no_synchronization_cmd, "no synchronization", @@ -970,7 +1161,7 @@ DEFUN (no_auto_summary, /* "router bgp" commands. */ DEFUN_NOSH (router_bgp, router_bgp_cmd, - "router bgp [(1-4294967295) [ VIEWVRFNAME]]", + "router bgp [(1-4294967295)$instasn [ VIEWVRFNAME]]", ROUTER_STR BGP_STR AS_STR @@ -1022,12 +1213,8 @@ DEFUN_NOSH (router_bgp, if (inst_type == BGP_INSTANCE_TYPE_DEFAULT) is_new_bgp = (bgp_lookup(as, name) == NULL); - ret = bgp_get(&bgp, &as, name, inst_type); + ret = bgp_get_vty(&bgp, &as, name, inst_type); switch (ret) { - case BGP_ERR_MULTIPLE_INSTANCE_NOT_SET: - vty_out(vty, - "Please specify 'bgp multiple-instance' first\n"); - return CMD_WARNING_CONFIG_FAILED; case BGP_ERR_AS_MISMATCH: vty_out(vty, "BGP is already running; AS is %u\n", as); return CMD_WARNING_CONFIG_FAILED; @@ -1048,6 +1235,8 @@ DEFUN_NOSH (router_bgp, if (is_new_bgp && inst_type == BGP_INSTANCE_TYPE_DEFAULT) vpn_leak_postchange_all(); + if (inst_type == BGP_INSTANCE_TYPE_VRF) + bgp_vpn_leak_export(bgp); /* Pending: handle when user tries to change a view to vrf n vv. */ } @@ -1062,7 +1251,7 @@ DEFUN_NOSH (router_bgp, /* "no router bgp" commands. */ DEFUN (no_router_bgp, no_router_bgp_cmd, - "no router bgp [(1-4294967295) [ VIEWVRFNAME]]", + "no router bgp [(1-4294967295)$instasn [ VIEWVRFNAME]]", NO_STR ROUTER_STR BGP_STR @@ -1109,10 +1298,43 @@ DEFUN (no_router_bgp, } if (bgp->l3vni) { - vty_out(vty, "%% Please unconfigure l3vni %u", + vty_out(vty, "%% Please unconfigure l3vni %u\n", bgp->l3vni); return CMD_WARNING_CONFIG_FAILED; } + + /* Cannot delete default instance if vrf instances exist */ + if (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT) { + struct listnode *node; + struct bgp *tmp_bgp; + + for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, tmp_bgp)) { + if (tmp_bgp->inst_type != BGP_INSTANCE_TYPE_VRF) + continue; + if (CHECK_FLAG(tmp_bgp->af_flags[AFI_IP][SAFI_UNICAST], + BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT) || + CHECK_FLAG(tmp_bgp->af_flags[AFI_IP6][SAFI_UNICAST], + BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT) || + CHECK_FLAG(tmp_bgp->af_flags[AFI_IP][SAFI_UNICAST], + BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT) || + CHECK_FLAG(tmp_bgp->af_flags[AFI_IP6][SAFI_UNICAST], + BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT) || + CHECK_FLAG(tmp_bgp->af_flags[AFI_IP][SAFI_UNICAST], + BGP_CONFIG_VRF_TO_VRF_EXPORT) || + CHECK_FLAG(tmp_bgp->af_flags[AFI_IP6][SAFI_UNICAST], + BGP_CONFIG_VRF_TO_VRF_EXPORT) || + (bgp == bgp_get_evpn() && + (CHECK_FLAG(tmp_bgp->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADVERTISE_IPV4_UNICAST) || + CHECK_FLAG(tmp_bgp->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADVERTISE_IPV6_UNICAST))) || + (tmp_bgp->vnihash && hashcount(tmp_bgp->vnihash))) { + vty_out(vty, + "%% Cannot delete default BGP instance. Dependent VRF instances exist\n"); + return CMD_WARNING_CONFIG_FAILED; + } + } + } } bgp_delete(bgp); @@ -1432,40 +1654,120 @@ DEFUN (no_bgp_maxmed_onstartup, return CMD_SUCCESS; } -static int bgp_update_delay_config_vty(struct vty *vty, const char *delay, - const char *wait) +static int bgp_global_update_delay_config_vty(struct vty *vty, + uint16_t update_delay, + uint16_t establish_wait) { - VTY_DECLVAR_CONTEXT(bgp, bgp); - uint16_t update_delay; - uint16_t establish_wait; - - update_delay = strtoul(delay, NULL, 10); + struct listnode *node, *nnode; + struct bgp *bgp; + bool vrf_cfg = false; - if (!wait) /* update-delay */ - { - bgp->v_update_delay = update_delay; - bgp->v_establish_wait = bgp->v_update_delay; - return CMD_SUCCESS; + /* + * See if update-delay is set per-vrf and warn user to delete it + * Note that we only need to check this if this is the first time + * setting the global config. + */ + if (bm->v_update_delay == BGP_UPDATE_DELAY_DEF) { + for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) { + if (bgp->v_update_delay != BGP_UPDATE_DELAY_DEF) { + vty_out(vty, + "%% update-delay configuration found in vrf %s\n", + bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT + ? VRF_DEFAULT_NAME + : bgp->name); + vrf_cfg = true; + } + } } - /* update-delay */ - establish_wait = atoi(wait); - if (update_delay < establish_wait) { + if (vrf_cfg) { vty_out(vty, - "%%Failed: update-delay less than the establish-wait!\n"); - return CMD_WARNING_CONFIG_FAILED; + "%%Failed: global update-delay config not permitted\n"); + return CMD_WARNING; } - bgp->v_update_delay = update_delay; - bgp->v_establish_wait = establish_wait; + if (!establish_wait) { /* update-delay */ + bm->v_update_delay = update_delay; + bm->v_establish_wait = bm->v_update_delay; + } else { + /* update-delay */ + if (update_delay < establish_wait) { + vty_out(vty, + "%%Failed: update-delay less than the establish-wait!\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + bm->v_update_delay = update_delay; + bm->v_establish_wait = establish_wait; + } + + for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) { + bgp->v_update_delay = bm->v_update_delay; + bgp->v_establish_wait = bm->v_establish_wait; + } return CMD_SUCCESS; } -static int bgp_update_delay_deconfig_vty(struct vty *vty) +static int bgp_global_update_delay_deconfig_vty(struct vty *vty) { - VTY_DECLVAR_CONTEXT(bgp, bgp); + struct listnode *node, *nnode; + struct bgp *bgp; + + bm->v_update_delay = BGP_UPDATE_DELAY_DEF; + bm->v_establish_wait = bm->v_update_delay; + + for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) { + bgp->v_update_delay = bm->v_update_delay; + bgp->v_establish_wait = bm->v_establish_wait; + } + + return CMD_SUCCESS; +} + +static int bgp_update_delay_config_vty(struct vty *vty, uint16_t update_delay, + uint16_t establish_wait) +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + + /* if configured globally, per-instance config is not allowed */ + if (bm->v_update_delay) { + vty_out(vty, + "%%Failed: per-vrf update-delay config not permitted with global update-delay\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + + if (!establish_wait) /* update-delay */ + { + bgp->v_update_delay = update_delay; + bgp->v_establish_wait = bgp->v_update_delay; + return CMD_SUCCESS; + } + + /* update-delay */ + if (update_delay < establish_wait) { + vty_out(vty, + "%%Failed: update-delay less than the establish-wait!\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + bgp->v_update_delay = update_delay; + bgp->v_establish_wait = establish_wait; + + return CMD_SUCCESS; +} + +static int bgp_update_delay_deconfig_vty(struct vty *vty) +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + /* If configured globally, cannot remove from one bgp instance */ + if (bm->v_update_delay) { + vty_out(vty, + "%%Failed: bgp update-delay configured globally. Delete per-vrf not permitted\n"); + return CMD_WARNING_CONFIG_FAILED; + } bgp->v_update_delay = BGP_UPDATE_DELAY_DEF; bgp->v_establish_wait = bgp->v_update_delay; @@ -1474,7 +1776,8 @@ static int bgp_update_delay_deconfig_vty(struct vty *vty) void bgp_config_write_update_delay(struct vty *vty, struct bgp *bgp) { - if (bgp->v_update_delay != BGP_UPDATE_DELAY_DEF) { + /* If configured globally, no need to display per-instance value */ + if (bgp->v_update_delay != bm->v_update_delay) { vty_out(vty, " update-delay %d", bgp->v_update_delay); if (bgp->v_update_delay != bgp->v_establish_wait) vty_out(vty, " %d", bgp->v_establish_wait); @@ -1482,74 +1785,74 @@ void bgp_config_write_update_delay(struct vty *vty, struct bgp *bgp) } } +/* Global update-delay configuration */ +DEFPY (bgp_global_update_delay, + bgp_global_update_delay_cmd, + "bgp update-delay (0-3600)$delay [(1-3600)$wait]", + BGP_STR + "Force initial delay for best-path and updates for all bgp instances\n" + "Max delay in seconds\n" + "Establish wait in seconds\n") +{ + return bgp_global_update_delay_config_vty(vty, delay, wait); +} -/* Update-delay configuration */ -DEFUN (bgp_update_delay, - bgp_update_delay_cmd, - "update-delay (0-3600)", +/* Global update-delay deconfiguration */ +DEFPY (no_bgp_global_update_delay, + no_bgp_global_update_delay_cmd, + "no bgp update-delay [(0-3600) [(1-3600)]]", + NO_STR + BGP_STR "Force initial delay for best-path and updates\n" - "Seconds\n") + "Max delay in seconds\n" + "Establish wait in seconds\n") { - int idx_number = 1; - return bgp_update_delay_config_vty(vty, argv[idx_number]->arg, NULL); + return bgp_global_update_delay_deconfig_vty(vty); } -DEFUN (bgp_update_delay_establish_wait, - bgp_update_delay_establish_wait_cmd, - "update-delay (0-3600) (1-3600)", +/* Update-delay configuration */ + +DEFPY (bgp_update_delay, + bgp_update_delay_cmd, + "update-delay (0-3600)$delay [(1-3600)$wait]", "Force initial delay for best-path and updates\n" - "Seconds\n" - "Seconds\n") + "Max delay in seconds\n" + "Establish wait in seconds\n") { - int idx_number = 1; - int idx_number_2 = 2; - return bgp_update_delay_config_vty(vty, argv[idx_number]->arg, - argv[idx_number_2]->arg); + return bgp_update_delay_config_vty(vty, delay, wait); } /* Update-delay deconfiguration */ -DEFUN (no_bgp_update_delay, +DEFPY (no_bgp_update_delay, no_bgp_update_delay_cmd, "no update-delay [(0-3600) [(1-3600)]]", NO_STR "Force initial delay for best-path and updates\n" - "Seconds\n" - "Seconds\n") + "Max delay in seconds\n" + "Establish wait in seconds\n") { return bgp_update_delay_deconfig_vty(vty); } -static int bgp_wpkt_quanta_config_vty(struct vty *vty, const char *num, - char set) +static int bgp_wpkt_quanta_config_vty(struct vty *vty, uint32_t quanta, + bool set) { VTY_DECLVAR_CONTEXT(bgp, bgp); - if (set) { - uint32_t quanta = strtoul(num, NULL, 10); - atomic_store_explicit(&bgp->wpkt_quanta, quanta, - memory_order_relaxed); - } else { - atomic_store_explicit(&bgp->wpkt_quanta, BGP_WRITE_PACKET_MAX, - memory_order_relaxed); - } + quanta = set ? quanta : BGP_WRITE_PACKET_MAX; + atomic_store_explicit(&bgp->wpkt_quanta, quanta, memory_order_relaxed); return CMD_SUCCESS; } -static int bgp_rpkt_quanta_config_vty(struct vty *vty, const char *num, - char set) +static int bgp_rpkt_quanta_config_vty(struct vty *vty, uint32_t quanta, + bool set) { VTY_DECLVAR_CONTEXT(bgp, bgp); - if (set) { - uint32_t quanta = strtoul(num, NULL, 10); - atomic_store_explicit(&bgp->rpkt_quanta, quanta, - memory_order_relaxed); - } else { - atomic_store_explicit(&bgp->rpkt_quanta, BGP_READ_PACKET_MAX, - memory_order_relaxed); - } + quanta = set ? quanta : BGP_READ_PACKET_MAX; + atomic_store_explicit(&bgp->rpkt_quanta, quanta, memory_order_relaxed); return CMD_SUCCESS; } @@ -1570,47 +1873,32 @@ void bgp_config_write_rpkt_quanta(struct vty *vty, struct bgp *bgp) vty_out(vty, " read-quanta %d\n", quanta); } -/* Packet quanta configuration */ -DEFUN (bgp_wpkt_quanta, +/* Packet quanta configuration + * + * XXX: The value set here controls the size of a stack buffer in the IO + * thread. When changing these limits be careful to prevent stack overflow. + * + * Furthermore, the maximums used here should correspond to + * BGP_WRITE_PACKET_MAX and BGP_READ_PACKET_MAX. + */ +DEFPY (bgp_wpkt_quanta, bgp_wpkt_quanta_cmd, - "write-quanta (1-10)", - "How many packets to write to peer socket per run\n" - "Number of packets\n") -{ - int idx_number = 1; - return bgp_wpkt_quanta_config_vty(vty, argv[idx_number]->arg, 1); -} - -DEFUN (no_bgp_wpkt_quanta, - no_bgp_wpkt_quanta_cmd, - "no write-quanta (1-10)", + "[no] write-quanta (1-64)$quanta", NO_STR - "How many packets to write to peer socket per I/O cycle\n" + "How many packets to write to peer socket per run\n" "Number of packets\n") { - int idx_number = 2; - return bgp_wpkt_quanta_config_vty(vty, argv[idx_number]->arg, 0); + return bgp_wpkt_quanta_config_vty(vty, quanta, !no); } -DEFUN (bgp_rpkt_quanta, +DEFPY (bgp_rpkt_quanta, bgp_rpkt_quanta_cmd, - "read-quanta (1-10)", - "How many packets to read from peer socket per I/O cycle\n" - "Number of packets\n") -{ - int idx_number = 1; - return bgp_rpkt_quanta_config_vty(vty, argv[idx_number]->arg, 1); -} - -DEFUN (no_bgp_rpkt_quanta, - no_bgp_rpkt_quanta_cmd, - "no read-quanta (1-10)", + "[no] read-quanta (1-10)$quanta", NO_STR "How many packets to read from peer socket per I/O cycle\n" "Number of packets\n") { - int idx_number = 2; - return bgp_rpkt_quanta_config_vty(vty, argv[idx_number]->arg, 0); + return bgp_rpkt_quanta_config_vty(vty, quanta, !no); } void bgp_config_write_coalesce_time(struct vty *vty, struct bgp *bgp) @@ -1742,15 +2030,15 @@ ALIAS_HIDDEN(no_bgp_maxpaths_ibgp, no_bgp_maxpaths_ibgp_hidden_cmd, "Number of paths\n" "Match the cluster length\n") -void bgp_config_write_maxpaths(struct vty *vty, struct bgp *bgp, afi_t afi, - safi_t safi) +static void bgp_config_write_maxpaths(struct vty *vty, struct bgp *bgp, + afi_t afi, safi_t safi) { - if (bgp->maxpaths[afi][safi].maxpaths_ebgp != MULTIPATH_NUM) { + if (bgp->maxpaths[afi][safi].maxpaths_ebgp != multipath_num) { vty_out(vty, " maximum-paths %d\n", bgp->maxpaths[afi][safi].maxpaths_ebgp); } - if (bgp->maxpaths[afi][safi].maxpaths_ibgp != MULTIPATH_NUM) { + if (bgp->maxpaths[afi][safi].maxpaths_ibgp != multipath_num) { vty_out(vty, " maximum-paths ibgp %d", bgp->maxpaths[afi][safi].maxpaths_ibgp); if (CHECK_FLAG(bgp->maxpaths[afi][safi].ibgp_flags, @@ -1786,7 +2074,7 @@ DEFUN (bgp_timers, return CMD_WARNING_CONFIG_FAILED; } - bgp_timers_set(bgp, keepalive, holdtime); + bgp_timers_set(bgp, keepalive, holdtime, DFLT_BGP_CONNECT_RETRY); return CMD_SUCCESS; } @@ -1801,7 +2089,8 @@ DEFUN (no_bgp_timers, "Holdtime\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); - bgp_timers_unset(bgp); + bgp_timers_set(bgp, DFLT_BGP_KEEPALIVE, DFLT_BGP_HOLDTIME, + DFLT_BGP_CONNECT_RETRY); return CMD_SUCCESS; } @@ -1815,7 +2104,7 @@ DEFUN (bgp_client_to_client_reflection, "reflection of routes allowed\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); - bgp_flag_unset(bgp, BGP_FLAG_NO_CLIENT_TO_CLIENT); + UNSET_FLAG(bgp->flags, BGP_FLAG_NO_CLIENT_TO_CLIENT); bgp_clear_star_soft_out(vty, bgp->name); return CMD_SUCCESS; @@ -1830,7 +2119,7 @@ DEFUN (no_bgp_client_to_client_reflection, "reflection of routes allowed\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); - bgp_flag_set(bgp, BGP_FLAG_NO_CLIENT_TO_CLIENT); + SET_FLAG(bgp->flags, BGP_FLAG_NO_CLIENT_TO_CLIENT); bgp_clear_star_soft_out(vty, bgp->name); return CMD_SUCCESS; @@ -1844,7 +2133,7 @@ DEFUN (bgp_always_compare_med, "Allow comparing MED from different neighbors\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); - bgp_flag_set(bgp, BGP_FLAG_ALWAYS_COMPARE_MED); + SET_FLAG(bgp->flags, BGP_FLAG_ALWAYS_COMPARE_MED); bgp_recalculate_all_bestpaths(bgp); return CMD_SUCCESS; @@ -1858,7 +2147,7 @@ DEFUN (no_bgp_always_compare_med, "Allow comparing MED from different neighbors\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); - bgp_flag_unset(bgp, BGP_FLAG_ALWAYS_COMPARE_MED); + UNSET_FLAG(bgp->flags, BGP_FLAG_ALWAYS_COMPARE_MED); bgp_recalculate_all_bestpaths(bgp); return CMD_SUCCESS; @@ -1871,7 +2160,7 @@ DEFUN(bgp_ebgp_requires_policy, bgp_ebgp_requires_policy_cmd, "Require in and out policy for eBGP peers (RFC8212)\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); - bgp->ebgp_requires_policy = DEFAULT_EBGP_POLICY_ENABLED; + SET_FLAG(bgp->flags, BGP_FLAG_EBGP_REQUIRES_POLICY); return CMD_SUCCESS; } @@ -1882,10 +2171,60 @@ DEFUN(no_bgp_ebgp_requires_policy, no_bgp_ebgp_requires_policy_cmd, "Require in and out policy for eBGP peers (RFC8212)\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); - bgp->ebgp_requires_policy = DEFAULT_EBGP_POLICY_DISABLED; + UNSET_FLAG(bgp->flags, BGP_FLAG_EBGP_REQUIRES_POLICY); + return CMD_SUCCESS; +} + +DEFUN(bgp_reject_as_sets, bgp_reject_as_sets_cmd, + "bgp reject-as-sets", + "BGP specific commands\n" + "Reject routes with AS_SET or AS_CONFED_SET flag\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + struct listnode *node, *nnode; + struct peer *peer; + + bgp->reject_as_sets = true; + + /* Reset existing BGP sessions to reject routes + * with aspath containing AS_SET or AS_CONFED_SET. + */ + for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) { + peer->last_reset = PEER_DOWN_AS_SETS_REJECT; + bgp_notify_send(peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + } + } + return CMD_SUCCESS; } +DEFUN(no_bgp_reject_as_sets, no_bgp_reject_as_sets_cmd, + "no bgp reject-as-sets", + NO_STR + "BGP specific commands\n" + "Reject routes with AS_SET or AS_CONFED_SET flag\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + struct listnode *node, *nnode; + struct peer *peer; + + bgp->reject_as_sets = false; + + /* Reset existing BGP sessions to reject routes + * with aspath containing AS_SET or AS_CONFED_SET. + */ + for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) { + peer->last_reset = PEER_DOWN_AS_SETS_REJECT; + bgp_notify_send(peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + } + } + + return CMD_SUCCESS; +} /* "bgp deterministic-med" configuration. */ DEFUN (bgp_deterministic_med, @@ -1896,8 +2235,8 @@ DEFUN (bgp_deterministic_med, { VTY_DECLVAR_CONTEXT(bgp, bgp); - if (!bgp_flag_check(bgp, BGP_FLAG_DETERMINISTIC_MED)) { - bgp_flag_set(bgp, BGP_FLAG_DETERMINISTIC_MED); + if (!CHECK_FLAG(bgp->flags, BGP_FLAG_DETERMINISTIC_MED)) { + SET_FLAG(bgp->flags, BGP_FLAG_DETERMINISTIC_MED); bgp_recalculate_all_bestpaths(bgp); } @@ -1918,7 +2257,7 @@ DEFUN (no_bgp_deterministic_med, struct peer *peer; struct listnode *node, *nnode; - if (bgp_flag_check(bgp, BGP_FLAG_DETERMINISTIC_MED)) { + if (CHECK_FLAG(bgp->flags, BGP_FLAG_DETERMINISTIC_MED)) { bestpath_per_as_used = 0; for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { @@ -1938,7 +2277,7 @@ DEFUN (no_bgp_deterministic_med, "bgp deterministic-med cannot be disabled while addpath-tx-bestpath-per-AS is in use\n"); return CMD_WARNING_CONFIG_FAILED; } else { - bgp_flag_unset(bgp, BGP_FLAG_DETERMINISTIC_MED); + UNSET_FLAG(bgp->flags, BGP_FLAG_DETERMINISTIC_MED); bgp_recalculate_all_bestpaths(bgp); } } @@ -1946,37 +2285,68 @@ DEFUN (no_bgp_deterministic_med, return CMD_SUCCESS; } -/* "bgp graceful-restart" configuration. */ +/* "bgp graceful-restart mode" configuration. */ DEFUN (bgp_graceful_restart, - bgp_graceful_restart_cmd, - "bgp graceful-restart", - "BGP specific commands\n" - "Graceful restart capability parameters\n") + bgp_graceful_restart_cmd, + "bgp graceful-restart", + "BGP specific commands\n" + GR_CMD + ) { + int ret = BGP_GR_FAILURE; + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug("[BGP_GR] bgp_graceful_restart_cmd : START "); + VTY_DECLVAR_CONTEXT(bgp, bgp); - bgp_flag_set(bgp, BGP_FLAG_GRACEFUL_RESTART); - return CMD_SUCCESS; + + ret = bgp_gr_update_all(bgp, GLOBAL_GR_CMD); + + VTY_BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(bgp, bgp->peer, + ret); + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug("[BGP_GR] bgp_graceful_restart_cmd : END "); + vty_out(vty, + "Graceful restart configuration changed, reset all peers to take effect\n"); + return bgp_vty_return(vty, ret); } DEFUN (no_bgp_graceful_restart, - no_bgp_graceful_restart_cmd, - "no bgp graceful-restart", - NO_STR - "BGP specific commands\n" - "Graceful restart capability parameters\n") + no_bgp_graceful_restart_cmd, + "no bgp graceful-restart", + NO_STR + "BGP specific commands\n" + NO_GR_CMD + ) { VTY_DECLVAR_CONTEXT(bgp, bgp); - bgp_flag_unset(bgp, BGP_FLAG_GRACEFUL_RESTART); - return CMD_SUCCESS; + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug("[BGP_GR] no_bgp_graceful_restart_cmd : START "); + + int ret = BGP_GR_FAILURE; + + ret = bgp_gr_update_all(bgp, NO_GLOBAL_GR_CMD); + + VTY_BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(bgp, bgp->peer, + ret); + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug("[BGP_GR] no_bgp_graceful_restart_cmd : END "); + vty_out(vty, + "Graceful restart configuration changed, reset all peers to take effect\n"); + + return bgp_vty_return(vty, ret); } DEFUN (bgp_graceful_restart_stalepath_time, - bgp_graceful_restart_stalepath_time_cmd, - "bgp graceful-restart stalepath-time (1-4095)", - "BGP specific commands\n" - "Graceful restart capability parameters\n" - "Set the max time to hold onto restarting peer's stale paths\n" - "Delay value (seconds)\n") + bgp_graceful_restart_stalepath_time_cmd, + "bgp graceful-restart stalepath-time (1-4095)", + "BGP specific commands\n" + "Graceful restart capability parameters\n" + "Set the max time to hold onto restarting peer's stale paths\n" + "Delay value (seconds)\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); int idx_number = 3; @@ -1988,12 +2358,12 @@ DEFUN (bgp_graceful_restart_stalepath_time, } DEFUN (bgp_graceful_restart_restart_time, - bgp_graceful_restart_restart_time_cmd, - "bgp graceful-restart restart-time (1-4095)", - "BGP specific commands\n" - "Graceful restart capability parameters\n" - "Set the time to wait to delete stale routes before a BGP open message is received\n" - "Delay value (seconds)\n") + bgp_graceful_restart_restart_time_cmd, + "bgp graceful-restart restart-time (1-4095)", + "BGP specific commands\n" + "Graceful restart capability parameters\n" + "Set the time to wait to delete stale routes before a BGP open message is received\n" + "Delay value (seconds)\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); int idx_number = 3; @@ -2004,191 +2374,557 @@ DEFUN (bgp_graceful_restart_restart_time, return CMD_SUCCESS; } -DEFUN (no_bgp_graceful_restart_stalepath_time, - no_bgp_graceful_restart_stalepath_time_cmd, - "no bgp graceful-restart stalepath-time [(1-4095)]", - NO_STR +DEFUN (bgp_graceful_restart_select_defer_time, + bgp_graceful_restart_select_defer_time_cmd, + "bgp graceful-restart select-defer-time (0-3600)", "BGP specific commands\n" "Graceful restart capability parameters\n" - "Set the max time to hold onto restarting peer's stale paths\n" - "Delay value (seconds)\n") + "Set the time to defer the BGP route selection after restart\n" + "Delay value (seconds, 0 - disable)\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); + int idx_number = 3; + uint32_t defer_time; + + defer_time = strtoul(argv[idx_number]->arg, NULL, 10); + bgp->select_defer_time = defer_time; + if (defer_time == 0) + SET_FLAG(bgp->flags, BGP_FLAG_SELECT_DEFER_DISABLE); + else + UNSET_FLAG(bgp->flags, BGP_FLAG_SELECT_DEFER_DISABLE); - bgp->stalepath_time = BGP_DEFAULT_STALEPATH_TIME; return CMD_SUCCESS; } -DEFUN (no_bgp_graceful_restart_restart_time, - no_bgp_graceful_restart_restart_time_cmd, - "no bgp graceful-restart restart-time [(1-4095)]", - NO_STR - "BGP specific commands\n" - "Graceful restart capability parameters\n" - "Set the time to wait to delete stale routes before a BGP open message is received\n" - "Delay value (seconds)\n") +DEFUN (no_bgp_graceful_restart_stalepath_time, + no_bgp_graceful_restart_stalepath_time_cmd, + "no bgp graceful-restart stalepath-time [(1-4095)]", + NO_STR + "BGP specific commands\n" + "Graceful restart capability parameters\n" + "Set the max time to hold onto restarting peer's stale paths\n" + "Delay value (seconds)\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); - bgp->restart_time = BGP_DEFAULT_RESTART_TIME; + bgp->stalepath_time = BGP_DEFAULT_STALEPATH_TIME; return CMD_SUCCESS; } -DEFUN (bgp_graceful_restart_preserve_fw, - bgp_graceful_restart_preserve_fw_cmd, - "bgp graceful-restart preserve-fw-state", - "BGP specific commands\n" - "Graceful restart capability parameters\n" - "Sets F-bit indication that fib is preserved while doing Graceful Restart\n") +DEFUN (no_bgp_graceful_restart_restart_time, + no_bgp_graceful_restart_restart_time_cmd, + "no bgp graceful-restart restart-time [(1-4095)]", + NO_STR + "BGP specific commands\n" + "Graceful restart capability parameters\n" + "Set the time to wait to delete stale routes before a BGP open message is received\n" + "Delay value (seconds)\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); - bgp_flag_set(bgp, BGP_FLAG_GR_PRESERVE_FWD); + + bgp->restart_time = BGP_DEFAULT_RESTART_TIME; return CMD_SUCCESS; } -DEFUN (no_bgp_graceful_restart_preserve_fw, - no_bgp_graceful_restart_preserve_fw_cmd, - "no bgp graceful-restart preserve-fw-state", +DEFUN (no_bgp_graceful_restart_select_defer_time, + no_bgp_graceful_restart_select_defer_time_cmd, + "no bgp graceful-restart select-defer-time [(0-3600)]", NO_STR "BGP specific commands\n" "Graceful restart capability parameters\n" - "Unsets F-bit indication that fib is preserved while doing Graceful Restart\n") -{ - VTY_DECLVAR_CONTEXT(bgp, bgp); - bgp_flag_unset(bgp, BGP_FLAG_GR_PRESERVE_FWD); - return CMD_SUCCESS; -} - -/* "bgp graceful-shutdown" configuration */ -DEFUN (bgp_graceful_shutdown, - bgp_graceful_shutdown_cmd, - "bgp graceful-shutdown", - BGP_STR - "Graceful shutdown parameters\n") + "Set the time to defer the BGP route selection after restart\n" + "Delay value (seconds)\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); - if (!bgp_flag_check(bgp, BGP_FLAG_GRACEFUL_SHUTDOWN)) { - bgp_flag_set(bgp, BGP_FLAG_GRACEFUL_SHUTDOWN); - bgp_static_redo_import_check(bgp); - bgp_redistribute_redo(bgp); - bgp_clear_star_soft_out(vty, bgp->name); - bgp_clear_star_soft_in(vty, bgp->name); - } + bgp->select_defer_time = BGP_DEFAULT_SELECT_DEFERRAL_TIME; + UNSET_FLAG(bgp->flags, BGP_FLAG_SELECT_DEFER_DISABLE); return CMD_SUCCESS; } -DEFUN (no_bgp_graceful_shutdown, - no_bgp_graceful_shutdown_cmd, - "no bgp graceful-shutdown", - NO_STR - BGP_STR - "Graceful shutdown parameters\n") +DEFUN (bgp_graceful_restart_preserve_fw, + bgp_graceful_restart_preserve_fw_cmd, + "bgp graceful-restart preserve-fw-state", + "BGP specific commands\n" + "Graceful restart capability parameters\n" + "Sets F-bit indication that fib is preserved while doing Graceful Restart\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); - - if (bgp_flag_check(bgp, BGP_FLAG_GRACEFUL_SHUTDOWN)) { - bgp_flag_unset(bgp, BGP_FLAG_GRACEFUL_SHUTDOWN); - bgp_static_redo_import_check(bgp); - bgp_redistribute_redo(bgp); - bgp_clear_star_soft_out(vty, bgp->name); - bgp_clear_star_soft_in(vty, bgp->name); - } - + SET_FLAG(bgp->flags, BGP_FLAG_GR_PRESERVE_FWD); return CMD_SUCCESS; } -/* "bgp fast-external-failover" configuration. */ -DEFUN (bgp_fast_external_failover, - bgp_fast_external_failover_cmd, - "bgp fast-external-failover", - BGP_STR - "Immediately reset session if a link to a directly connected external peer goes down\n") +DEFUN (no_bgp_graceful_restart_preserve_fw, + no_bgp_graceful_restart_preserve_fw_cmd, + "no bgp graceful-restart preserve-fw-state", + NO_STR + "BGP specific commands\n" + "Graceful restart capability parameters\n" + "Unsets F-bit indication that fib is preserved while doing Graceful Restart\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); - bgp_flag_unset(bgp, BGP_FLAG_NO_FAST_EXT_FAILOVER); + UNSET_FLAG(bgp->flags, BGP_FLAG_GR_PRESERVE_FWD); return CMD_SUCCESS; } -DEFUN (no_bgp_fast_external_failover, - no_bgp_fast_external_failover_cmd, - "no bgp fast-external-failover", - NO_STR - BGP_STR - "Immediately reset session if a link to a directly connected external peer goes down\n") +DEFUN (bgp_graceful_restart_disable, + bgp_graceful_restart_disable_cmd, + "bgp graceful-restart-disable", + "BGP specific commands\n" + GR_DISABLE) { - VTY_DECLVAR_CONTEXT(bgp, bgp); - bgp_flag_set(bgp, BGP_FLAG_NO_FAST_EXT_FAILOVER); - return CMD_SUCCESS; -} + int ret = BGP_GR_FAILURE; -/* "bgp enforce-first-as" configuration. */ -#if CONFDATE > 20190517 -CPP_NOTICE("bgpd: remove deprecated '[no] bgp enforce-first-as' commands") -#endif + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] bgp_graceful_restart_disable_cmd : START "); -DEFUN_HIDDEN (bgp_enforce_first_as, - bgp_enforce_first_as_cmd, - "[no] bgp enforce-first-as", - NO_STR - BGP_STR - "Enforce the first AS for EBGP routes\n") -{ VTY_DECLVAR_CONTEXT(bgp, bgp); - if (strmatch(argv[0]->text, "no")) - bgp_flag_unset(bgp, BGP_FLAG_ENFORCE_FIRST_AS); - else - bgp_flag_set(bgp, BGP_FLAG_ENFORCE_FIRST_AS); + ret = bgp_gr_update_all(bgp, GLOBAL_DISABLE_CMD); - return CMD_SUCCESS; -} + VTY_BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(bgp, + bgp->peer, ret); -/* "bgp bestpath compare-routerid" configuration. */ -DEFUN (bgp_bestpath_compare_router_id, - bgp_bestpath_compare_router_id_cmd, - "bgp bestpath compare-routerid", - "BGP specific commands\n" - "Change the default bestpath selection\n" - "Compare router-id for identical EBGP paths\n") -{ - VTY_DECLVAR_CONTEXT(bgp, bgp); - bgp_flag_set(bgp, BGP_FLAG_COMPARE_ROUTER_ID); - bgp_recalculate_all_bestpaths(bgp); + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] bgp_graceful_restart_disable_cmd : END "); + vty_out(vty, + "Graceful restart configuration changed, reset all peers to take effect\n"); - return CMD_SUCCESS; + return bgp_vty_return(vty, ret); } -DEFUN (no_bgp_bestpath_compare_router_id, - no_bgp_bestpath_compare_router_id_cmd, - "no bgp bestpath compare-routerid", - NO_STR - "BGP specific commands\n" - "Change the default bestpath selection\n" - "Compare router-id for identical EBGP paths\n") +DEFUN (no_bgp_graceful_restart_disable, + no_bgp_graceful_restart_disable_cmd, + "no bgp graceful-restart-disable", + NO_STR + "BGP specific commands\n" + NO_GR_DISABLE + ) { VTY_DECLVAR_CONTEXT(bgp, bgp); - bgp_flag_unset(bgp, BGP_FLAG_COMPARE_ROUTER_ID); - bgp_recalculate_all_bestpaths(bgp); - return CMD_SUCCESS; -} + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] no_bgp_graceful_restart_disable_cmd : START "); -/* "bgp bestpath as-path ignore" configuration. */ -DEFUN (bgp_bestpath_aspath_ignore, - bgp_bestpath_aspath_ignore_cmd, - "bgp bestpath as-path ignore", - "BGP specific commands\n" - "Change the default bestpath selection\n" - "AS-path attribute\n" - "Ignore as-path length in selecting a route\n") -{ - VTY_DECLVAR_CONTEXT(bgp, bgp); - bgp_flag_set(bgp, BGP_FLAG_ASPATH_IGNORE); - bgp_recalculate_all_bestpaths(bgp); + int ret = BGP_GR_FAILURE; - return CMD_SUCCESS; + ret = bgp_gr_update_all(bgp, NO_GLOBAL_DISABLE_CMD); + + VTY_BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(bgp, bgp->peer, + ret); + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] no_bgp_graceful_restart_disable_cmd : END "); + vty_out(vty, + "Graceful restart configuration changed, reset all peers to take effect\n"); + + return bgp_vty_return(vty, ret); +} + +DEFUN (bgp_neighbor_graceful_restart_set, + bgp_neighbor_graceful_restart_set_cmd, + "neighbor graceful-restart", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + GR_NEIGHBOR_CMD + ) +{ + int idx_peer = 1; + struct peer *peer; + int ret = BGP_GR_FAILURE; + + VTY_BGP_GR_DEFINE_LOOP_VARIABLE; + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] bgp_neighbor_graceful_restart_set_cmd : START "); + + peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg); + if (!peer) + return CMD_WARNING_CONFIG_FAILED; + + ret = bgp_neighbor_graceful_restart(peer, PEER_GR_CMD); + + VTY_BGP_GR_ROUTER_DETECT(bgp, peer, peer->bgp->peer); + VTY_SEND_BGP_GR_CAPABILITY_TO_ZEBRA(peer->bgp, ret); + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] bgp_neighbor_graceful_restart_set_cmd : END "); + vty_out(vty, + "Graceful restart configuration changed, reset this peer to take effect\n"); + + return bgp_vty_return(vty, ret); +} + +DEFUN (no_bgp_neighbor_graceful_restart, + no_bgp_neighbor_graceful_restart_set_cmd, + "no neighbor graceful-restart", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + NO_GR_NEIGHBOR_CMD + ) +{ + int idx_peer = 2; + int ret = BGP_GR_FAILURE; + struct peer *peer; + + VTY_BGP_GR_DEFINE_LOOP_VARIABLE; + + peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg); + if (!peer) + return CMD_WARNING_CONFIG_FAILED; + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] no_bgp_neighbor_graceful_restart_set_cmd : START "); + + ret = bgp_neighbor_graceful_restart(peer, NO_PEER_GR_CMD); + + VTY_BGP_GR_ROUTER_DETECT(bgp, peer, peer->bgp->peer); + VTY_SEND_BGP_GR_CAPABILITY_TO_ZEBRA(peer->bgp, ret); + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] no_bgp_neighbor_graceful_restart_set_cmd : END "); + vty_out(vty, + "Graceful restart configuration changed, reset this peer to take effect\n"); + + return bgp_vty_return(vty, ret); +} + +DEFUN (bgp_neighbor_graceful_restart_helper_set, + bgp_neighbor_graceful_restart_helper_set_cmd, + "neighbor graceful-restart-helper", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + GR_NEIGHBOR_HELPER_CMD + ) +{ + int idx_peer = 1; + struct peer *peer; + int ret = BGP_GR_FAILURE; + + VTY_BGP_GR_DEFINE_LOOP_VARIABLE; + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] bgp_neighbor_graceful_restart_helper_set_cmd : START "); + + peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg); + + if (!peer) + return CMD_WARNING_CONFIG_FAILED; + + + ret = bgp_neighbor_graceful_restart(peer, PEER_HELPER_CMD); + + VTY_BGP_GR_ROUTER_DETECT(bgp, peer, peer->bgp->peer); + VTY_SEND_BGP_GR_CAPABILITY_TO_ZEBRA(peer->bgp, ret); + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] bgp_neighbor_graceful_restart_helper_set_cmd : END "); + vty_out(vty, + "Graceful restart configuration changed, reset this peer to take effect\n"); + + return bgp_vty_return(vty, ret); +} + +DEFUN (no_bgp_neighbor_graceful_restart_helper, + no_bgp_neighbor_graceful_restart_helper_set_cmd, + "no neighbor graceful-restart-helper", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + NO_GR_NEIGHBOR_HELPER_CMD + ) +{ + int idx_peer = 2; + int ret = BGP_GR_FAILURE; + struct peer *peer; + + VTY_BGP_GR_DEFINE_LOOP_VARIABLE; + + peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg); + if (!peer) + return CMD_WARNING_CONFIG_FAILED; + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] no_bgp_neighbor_graceful_restart_helper_set_cmd : START "); + + ret = bgp_neighbor_graceful_restart(peer, NO_PEER_HELPER_CMD); + + VTY_BGP_GR_ROUTER_DETECT(bgp, peer, peer->bgp->peer); + VTY_SEND_BGP_GR_CAPABILITY_TO_ZEBRA(peer->bgp, ret); + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] no_bgp_neighbor_graceful_restart_helper_set_cmd : END "); + vty_out(vty, + "Graceful restart configuration changed, reset this peer to take effect\n"); + + return bgp_vty_return(vty, ret); +} + +DEFUN (bgp_neighbor_graceful_restart_disable_set, + bgp_neighbor_graceful_restart_disable_set_cmd, + "neighbor graceful-restart-disable", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + GR_NEIGHBOR_DISABLE_CMD + ) +{ + int idx_peer = 1; + struct peer *peer; + int ret = BGP_GR_FAILURE; + + VTY_BGP_GR_DEFINE_LOOP_VARIABLE; + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] bgp_neighbor_graceful_restart_disable_set_cmd : START "); + + peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg); + if (!peer) + return CMD_WARNING_CONFIG_FAILED; + + ret = bgp_neighbor_graceful_restart(peer, PEER_DISABLE_CMD); + + if (peer->bgp->t_startup) + bgp_peer_gr_flags_update(peer); + + VTY_BGP_GR_ROUTER_DETECT(bgp, peer, peer->bgp->peer); + VTY_SEND_BGP_GR_CAPABILITY_TO_ZEBRA(peer->bgp, ret); + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR]bgp_neighbor_graceful_restart_disable_set_cmd : END "); + vty_out(vty, + "Graceful restart configuration changed, reset this peer to take effect\n"); + + return bgp_vty_return(vty, ret); +} + +DEFUN (no_bgp_neighbor_graceful_restart_disable, + no_bgp_neighbor_graceful_restart_disable_set_cmd, + "no neighbor graceful-restart-disable", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + NO_GR_NEIGHBOR_DISABLE_CMD + ) +{ + int idx_peer = 2; + int ret = BGP_GR_FAILURE; + struct peer *peer; + + VTY_BGP_GR_DEFINE_LOOP_VARIABLE; + + peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg); + if (!peer) + return CMD_WARNING_CONFIG_FAILED; + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] no_bgp_neighbor_graceful_restart_disable_set_cmd : START "); + + ret = bgp_neighbor_graceful_restart(peer, NO_PEER_DISABLE_CMD); + + VTY_BGP_GR_ROUTER_DETECT(bgp, peer, peer->bgp->peer); + VTY_SEND_BGP_GR_CAPABILITY_TO_ZEBRA(peer->bgp, ret); + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug( + "[BGP_GR] no_bgp_neighbor_graceful_restart_disable_set_cmd : END "); + vty_out(vty, + "Graceful restart configuration changed, reset this peer to take effect\n"); + + return bgp_vty_return(vty, ret); +} + +DEFUN_HIDDEN (bgp_graceful_restart_disable_eor, + bgp_graceful_restart_disable_eor_cmd, + "bgp graceful-restart disable-eor", + "BGP specific commands\n" + "Graceful restart configuration parameters\n" + "Disable EOR Check\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + SET_FLAG(bgp->flags, BGP_FLAG_GR_DISABLE_EOR); + + return CMD_SUCCESS; +} + +DEFUN_HIDDEN (no_bgp_graceful_restart_disable_eor, + no_bgp_graceful_restart_disable_eor_cmd, + "no bgp graceful-restart disable-eor", + NO_STR + "BGP specific commands\n" + "Graceful restart configuration parameters\n" + "Disable EOR Check\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + UNSET_FLAG(bgp->flags, BGP_FLAG_GR_DISABLE_EOR); + + return CMD_SUCCESS; +} + +DEFUN (bgp_graceful_restart_rib_stale_time, + bgp_graceful_restart_rib_stale_time_cmd, + "bgp graceful-restart rib-stale-time (1-3600)", + "BGP specific commands\n" + "Graceful restart configuration parameters\n" + "Specify the stale route removal timer in rib\n" + "Delay value (seconds)\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + int idx_number = 3; + uint32_t stale_time; + + stale_time = strtoul(argv[idx_number]->arg, NULL, 10); + bgp->rib_stale_time = stale_time; + /* Send the stale timer update message to RIB */ + if (bgp_zebra_stale_timer_update(bgp)) + return CMD_WARNING; + + return CMD_SUCCESS; +} + +DEFUN (no_bgp_graceful_restart_rib_stale_time, + no_bgp_graceful_restart_rib_stale_time_cmd, + "no bgp graceful-restart rib-stale-time [(1-3600)]", + NO_STR + "BGP specific commands\n" + "Graceful restart configuration parameters\n" + "Specify the stale route removal timer in rib\n" + "Delay value (seconds)\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + + bgp->rib_stale_time = BGP_DEFAULT_RIB_STALE_TIME; + /* Send the stale timer update message to RIB */ + if (bgp_zebra_stale_timer_update(bgp)) + return CMD_WARNING; + + return CMD_SUCCESS; +} + +/* "bgp graceful-shutdown" configuration */ +DEFUN (bgp_graceful_shutdown, + bgp_graceful_shutdown_cmd, + "bgp graceful-shutdown", + BGP_STR + "Graceful shutdown parameters\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + + if (!CHECK_FLAG(bgp->flags, BGP_FLAG_GRACEFUL_SHUTDOWN)) { + SET_FLAG(bgp->flags, BGP_FLAG_GRACEFUL_SHUTDOWN); + bgp_static_redo_import_check(bgp); + bgp_redistribute_redo(bgp); + bgp_clear_star_soft_out(vty, bgp->name); + bgp_clear_star_soft_in(vty, bgp->name); + } + + return CMD_SUCCESS; +} + +DEFUN (no_bgp_graceful_shutdown, + no_bgp_graceful_shutdown_cmd, + "no bgp graceful-shutdown", + NO_STR + BGP_STR + "Graceful shutdown parameters\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + + if (CHECK_FLAG(bgp->flags, BGP_FLAG_GRACEFUL_SHUTDOWN)) { + UNSET_FLAG(bgp->flags, BGP_FLAG_GRACEFUL_SHUTDOWN); + bgp_static_redo_import_check(bgp); + bgp_redistribute_redo(bgp); + bgp_clear_star_soft_out(vty, bgp->name); + bgp_clear_star_soft_in(vty, bgp->name); + } + + return CMD_SUCCESS; +} + +/* "bgp fast-external-failover" configuration. */ +DEFUN (bgp_fast_external_failover, + bgp_fast_external_failover_cmd, + "bgp fast-external-failover", + BGP_STR + "Immediately reset session if a link to a directly connected external peer goes down\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + UNSET_FLAG(bgp->flags, BGP_FLAG_NO_FAST_EXT_FAILOVER); + return CMD_SUCCESS; +} + +DEFUN (no_bgp_fast_external_failover, + no_bgp_fast_external_failover_cmd, + "no bgp fast-external-failover", + NO_STR + BGP_STR + "Immediately reset session if a link to a directly connected external peer goes down\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + SET_FLAG(bgp->flags, BGP_FLAG_NO_FAST_EXT_FAILOVER); + return CMD_SUCCESS; +} + +/* "bgp bestpath compare-routerid" configuration. */ +DEFUN (bgp_bestpath_compare_router_id, + bgp_bestpath_compare_router_id_cmd, + "bgp bestpath compare-routerid", + "BGP specific commands\n" + "Change the default bestpath selection\n" + "Compare router-id for identical EBGP paths\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + SET_FLAG(bgp->flags, BGP_FLAG_COMPARE_ROUTER_ID); + bgp_recalculate_all_bestpaths(bgp); + + return CMD_SUCCESS; +} + +DEFUN (no_bgp_bestpath_compare_router_id, + no_bgp_bestpath_compare_router_id_cmd, + "no bgp bestpath compare-routerid", + NO_STR + "BGP specific commands\n" + "Change the default bestpath selection\n" + "Compare router-id for identical EBGP paths\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + UNSET_FLAG(bgp->flags, BGP_FLAG_COMPARE_ROUTER_ID); + bgp_recalculate_all_bestpaths(bgp); + + return CMD_SUCCESS; +} + +/* "bgp bestpath as-path ignore" configuration. */ +DEFUN (bgp_bestpath_aspath_ignore, + bgp_bestpath_aspath_ignore_cmd, + "bgp bestpath as-path ignore", + "BGP specific commands\n" + "Change the default bestpath selection\n" + "AS-path attribute\n" + "Ignore as-path length in selecting a route\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + SET_FLAG(bgp->flags, BGP_FLAG_ASPATH_IGNORE); + bgp_recalculate_all_bestpaths(bgp); + + return CMD_SUCCESS; } DEFUN (no_bgp_bestpath_aspath_ignore, @@ -2201,7 +2937,7 @@ DEFUN (no_bgp_bestpath_aspath_ignore, "Ignore as-path length in selecting a route\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); - bgp_flag_unset(bgp, BGP_FLAG_ASPATH_IGNORE); + UNSET_FLAG(bgp->flags, BGP_FLAG_ASPATH_IGNORE); bgp_recalculate_all_bestpaths(bgp); return CMD_SUCCESS; @@ -2217,7 +2953,7 @@ DEFUN (bgp_bestpath_aspath_confed, "Compare path lengths including confederation sets & sequences in selecting a route\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); - bgp_flag_set(bgp, BGP_FLAG_ASPATH_CONFED); + SET_FLAG(bgp->flags, BGP_FLAG_ASPATH_CONFED); bgp_recalculate_all_bestpaths(bgp); return CMD_SUCCESS; @@ -2233,7 +2969,7 @@ DEFUN (no_bgp_bestpath_aspath_confed, "Compare path lengths including confederation sets & sequences in selecting a route\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); - bgp_flag_unset(bgp, BGP_FLAG_ASPATH_CONFED); + UNSET_FLAG(bgp->flags, BGP_FLAG_ASPATH_CONFED); bgp_recalculate_all_bestpaths(bgp); return CMD_SUCCESS; @@ -2252,14 +2988,14 @@ DEFUN (bgp_bestpath_aspath_multipath_relax, { VTY_DECLVAR_CONTEXT(bgp, bgp); int idx = 0; - bgp_flag_set(bgp, BGP_FLAG_ASPATH_MULTIPATH_RELAX); + SET_FLAG(bgp->flags, BGP_FLAG_ASPATH_MULTIPATH_RELAX); /* no-as-set is now the default behavior so we can silently * ignore it */ if (argv_find(argv, argc, "as-set", &idx)) - bgp_flag_set(bgp, BGP_FLAG_MULTIPATH_RELAX_AS_SET); + SET_FLAG(bgp->flags, BGP_FLAG_MULTIPATH_RELAX_AS_SET); else - bgp_flag_unset(bgp, BGP_FLAG_MULTIPATH_RELAX_AS_SET); + UNSET_FLAG(bgp->flags, BGP_FLAG_MULTIPATH_RELAX_AS_SET); bgp_recalculate_all_bestpaths(bgp); @@ -2278,8 +3014,8 @@ DEFUN (no_bgp_bestpath_aspath_multipath_relax, "Do not generate an AS_SET\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); - bgp_flag_unset(bgp, BGP_FLAG_ASPATH_MULTIPATH_RELAX); - bgp_flag_unset(bgp, BGP_FLAG_MULTIPATH_RELAX_AS_SET); + UNSET_FLAG(bgp->flags, BGP_FLAG_ASPATH_MULTIPATH_RELAX); + UNSET_FLAG(bgp->flags, BGP_FLAG_MULTIPATH_RELAX_AS_SET); bgp_recalculate_all_bestpaths(bgp); return CMD_SUCCESS; @@ -2293,7 +3029,7 @@ DEFUN (bgp_log_neighbor_changes, "Log neighbor up/down and reset reason\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); - bgp_flag_set(bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES); + SET_FLAG(bgp->flags, BGP_FLAG_LOG_NEIGHBOR_CHANGES); return CMD_SUCCESS; } @@ -2305,7 +3041,7 @@ DEFUN (no_bgp_log_neighbor_changes, "Log neighbor up/down and reset reason\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); - bgp_flag_unset(bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES); + UNSET_FLAG(bgp->flags, BGP_FLAG_LOG_NEIGHBOR_CHANGES); return CMD_SUCCESS; } @@ -2325,10 +3061,10 @@ DEFUN (bgp_bestpath_med, int idx = 0; if (argv_find(argv, argc, "confed", &idx)) - bgp_flag_set(bgp, BGP_FLAG_MED_CONFED); + SET_FLAG(bgp->flags, BGP_FLAG_MED_CONFED); idx = 0; if (argv_find(argv, argc, "missing-as-worst", &idx)) - bgp_flag_set(bgp, BGP_FLAG_MED_MISSING_AS_WORST); + SET_FLAG(bgp->flags, BGP_FLAG_MED_MISSING_AS_WORST); bgp_recalculate_all_bestpaths(bgp); @@ -2351,27 +3087,91 @@ DEFUN (no_bgp_bestpath_med, int idx = 0; if (argv_find(argv, argc, "confed", &idx)) - bgp_flag_unset(bgp, BGP_FLAG_MED_CONFED); + UNSET_FLAG(bgp->flags, BGP_FLAG_MED_CONFED); idx = 0; if (argv_find(argv, argc, "missing-as-worst", &idx)) - bgp_flag_unset(bgp, BGP_FLAG_MED_MISSING_AS_WORST); + UNSET_FLAG(bgp->flags, BGP_FLAG_MED_MISSING_AS_WORST); bgp_recalculate_all_bestpaths(bgp); return CMD_SUCCESS; } -/* "no bgp default ipv4-unicast". */ -DEFUN (no_bgp_default_ipv4_unicast, - no_bgp_default_ipv4_unicast_cmd, - "no bgp default ipv4-unicast", - NO_STR +/* "bgp bestpath bandwidth" configuration. */ +DEFPY (bgp_bestpath_bw, + bgp_bestpath_bw_cmd, + "bgp bestpath bandwidth $bw_cfg", "BGP specific commands\n" - "Configure BGP defaults\n" - "Activate ipv4-unicast for a peer by default\n") + "Change the default bestpath selection\n" + "Link Bandwidth attribute\n" + "Ignore link bandwidth (i.e., do regular ECMP, not weighted)\n" + "Ignore paths without link bandwidth for ECMP (if other paths have it)\n" + "Assign a low default weight (value 1) to paths not having link bandwidth\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); - bgp_flag_set(bgp, BGP_FLAG_NO_DEFAULT_IPV4); + afi_t afi; + safi_t safi; + + if (!bw_cfg) { + vty_out(vty, "%% Bandwidth configuration must be specified\n"); + return CMD_ERR_INCOMPLETE; + } + if (!strcmp(bw_cfg, "ignore")) + bgp->lb_handling = BGP_LINK_BW_IGNORE_BW; + else if (!strcmp(bw_cfg, "skip-missing")) + bgp->lb_handling = BGP_LINK_BW_SKIP_MISSING; + else if (!strcmp(bw_cfg, "default-weight-for-missing")) + bgp->lb_handling = BGP_LINK_BW_DEFWT_4_MISSING; + else + return CMD_ERR_NO_MATCH; + + /* This config is used in route install, so redo that. */ + FOREACH_AFI_SAFI (afi, safi) { + if (!bgp_fibupd_safi(safi)) + continue; + bgp_zebra_announce_table(bgp, afi, safi); + } + + return CMD_SUCCESS; +} + +DEFPY (no_bgp_bestpath_bw, + no_bgp_bestpath_bw_cmd, + "no bgp bestpath bandwidth [$bw_cfg]", + NO_STR + "BGP specific commands\n" + "Change the default bestpath selection\n" + "Link Bandwidth attribute\n" + "Ignore link bandwidth (i.e., do regular ECMP, not weighted)\n" + "Ignore paths without link bandwidth for ECMP (if other paths have it)\n" + "Assign a low default weight (value 1) to paths not having link bandwidth\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + afi_t afi; + safi_t safi; + + bgp->lb_handling = BGP_LINK_BW_ECMP; + + /* This config is used in route install, so redo that. */ + FOREACH_AFI_SAFI (afi, safi) { + if (!bgp_fibupd_safi(safi)) + continue; + bgp_zebra_announce_table(bgp, afi, safi); + } + return CMD_SUCCESS; +} + +/* "no bgp default ipv4-unicast". */ +DEFUN (no_bgp_default_ipv4_unicast, + no_bgp_default_ipv4_unicast_cmd, + "no bgp default ipv4-unicast", + NO_STR + "BGP specific commands\n" + "Configure BGP defaults\n" + "Activate ipv4-unicast for a peer by default\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + SET_FLAG(bgp->flags, BGP_FLAG_NO_DEFAULT_IPV4); return CMD_SUCCESS; } @@ -2383,7 +3183,7 @@ DEFUN (bgp_default_ipv4_unicast, "Activate ipv4-unicast for a peer by default\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); - bgp_flag_unset(bgp, BGP_FLAG_NO_DEFAULT_IPV4); + UNSET_FLAG(bgp->flags, BGP_FLAG_NO_DEFAULT_IPV4); return CMD_SUCCESS; } @@ -2396,7 +3196,7 @@ DEFUN (bgp_default_show_hostname, "Show hostname in certain command outputs\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); - bgp_flag_set(bgp, BGP_FLAG_SHOW_HOSTNAME); + SET_FLAG(bgp->flags, BGP_FLAG_SHOW_HOSTNAME); return CMD_SUCCESS; } @@ -2409,7 +3209,33 @@ DEFUN (no_bgp_default_show_hostname, "Show hostname in certain command outputs\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); - bgp_flag_unset(bgp, BGP_FLAG_SHOW_HOSTNAME); + UNSET_FLAG(bgp->flags, BGP_FLAG_SHOW_HOSTNAME); + return CMD_SUCCESS; +} + +/* Display hostname in certain command outputs */ +DEFUN (bgp_default_show_nexthop_hostname, + bgp_default_show_nexthop_hostname_cmd, + "bgp default show-nexthop-hostname", + "BGP specific commands\n" + "Configure BGP defaults\n" + "Show hostname for nexthop in certain command outputs\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + SET_FLAG(bgp->flags, BGP_FLAG_SHOW_NEXTHOP_HOSTNAME); + return CMD_SUCCESS; +} + +DEFUN (no_bgp_default_show_nexthop_hostname, + no_bgp_default_show_nexthop_hostname_cmd, + "no bgp default show-nexthop-hostname", + NO_STR + "BGP specific commands\n" + "Configure BGP defaults\n" + "Show hostname for nexthop in certain command outputs\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + UNSET_FLAG(bgp->flags, BGP_FLAG_SHOW_NEXTHOP_HOSTNAME); return CMD_SUCCESS; } @@ -2422,8 +3248,8 @@ DEFUN (bgp_network_import_check, "Check BGP network route exists in IGP\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); - if (!bgp_flag_check(bgp, BGP_FLAG_IMPORT_CHECK)) { - bgp_flag_set(bgp, BGP_FLAG_IMPORT_CHECK); + if (!CHECK_FLAG(bgp->flags, BGP_FLAG_IMPORT_CHECK)) { + SET_FLAG(bgp->flags, BGP_FLAG_IMPORT_CHECK); bgp_static_redo_import_check(bgp); } @@ -2446,8 +3272,8 @@ DEFUN (no_bgp_network_import_check, "Check BGP network route exists in IGP\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); - if (bgp_flag_check(bgp, BGP_FLAG_IMPORT_CHECK)) { - bgp_flag_unset(bgp, BGP_FLAG_IMPORT_CHECK); + if (CHECK_FLAG(bgp->flags, BGP_FLAG_IMPORT_CHECK)) { + UNSET_FLAG(bgp->flags, BGP_FLAG_IMPORT_CHECK); bgp_static_redo_import_check(bgp); } @@ -2534,8 +3360,8 @@ DEFUN (bgp_rr_allow_outbound_policy, { VTY_DECLVAR_CONTEXT(bgp, bgp); - if (!bgp_flag_check(bgp, BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY)) { - bgp_flag_set(bgp, BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY); + if (!CHECK_FLAG(bgp->flags, BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY)) { + SET_FLAG(bgp->flags, BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY); update_group_announce_rrclients(bgp); bgp_clear_star_soft_out(vty, bgp->name); } @@ -2553,8 +3379,8 @@ DEFUN (no_bgp_rr_allow_outbound_policy, { VTY_DECLVAR_CONTEXT(bgp, bgp); - if (bgp_flag_check(bgp, BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY)) { - bgp_flag_unset(bgp, BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY); + if (CHECK_FLAG(bgp->flags, BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY)) { + UNSET_FLAG(bgp->flags, BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY); update_group_announce_rrclients(bgp); bgp_clear_star_soft_out(vty, bgp->name); } @@ -2566,8 +3392,8 @@ DEFUN (bgp_listen_limit, bgp_listen_limit_cmd, "bgp listen limit (1-5000)", "BGP specific commands\n" - "Configure BGP defaults\n" - "maximum number of BGP Dynamic Neighbors that can be created\n" + "BGP Dynamic Neighbors listen commands\n" + "Maximum number of BGP Dynamic Neighbors that can be created\n" "Configure Dynamic Neighbors listen limit value\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); @@ -2584,10 +3410,10 @@ DEFUN (bgp_listen_limit, DEFUN (no_bgp_listen_limit, no_bgp_listen_limit_cmd, "no bgp listen limit [(1-5000)]", + NO_STR "BGP specific commands\n" - "Configure BGP defaults\n" - "unset maximum number of BGP Dynamic Neighbors that can be created\n" - "Configure Dynamic Neighbors listen limit value to default\n" + "BGP Dynamic Neighbors listen commands\n" + "Maximum number of BGP Dynamic Neighbors that can be created\n" "Configure Dynamic Neighbors listen limit value\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); @@ -2629,7 +3455,7 @@ static struct peer_group *listen_range_exists(struct bgp *bgp, DEFUN (bgp_listen_range, bgp_listen_range_cmd, - "bgp listen range peer-group WORD", + "bgp listen range peer-group PGNAME", "BGP specific commands\n" "Configure BGP dynamic neighbors listen range\n" "Configure BGP dynamic neighbors listen range\n" @@ -2647,7 +3473,7 @@ DEFUN (bgp_listen_range, argv_find(argv, argc, "A.B.C.D/M", &idx); argv_find(argv, argc, "X:X::X:X/M", &idx); char *prefix = argv[idx]->arg; - argv_find(argv, argc, "WORD", &idx); + argv_find(argv, argc, "PGNAME", &idx); char *peergroup = argv[idx]->arg; /* Convert IP prefix string to struct prefix. */ @@ -2699,7 +3525,7 @@ DEFUN (bgp_listen_range, DEFUN (no_bgp_listen_range, no_bgp_listen_range_cmd, - "no bgp listen range peer-group WORD", + "no bgp listen range peer-group PGNAME", NO_STR "BGP specific commands\n" "Unconfigure BGP dynamic neighbors listen range\n" @@ -2718,7 +3544,7 @@ DEFUN (no_bgp_listen_range, argv_find(argv, argc, "A.B.C.D/M", &idx); argv_find(argv, argc, "X:X::X:X/M", &idx); char *prefix = argv[idx]->arg; - argv_find(argv, argc, "WORD", &idx); + argv_find(argv, argc, "PGNAME", &idx); char *peergroup = argv[idx]->arg; /* Convert IP prefix string to struct prefix. */ @@ -2781,7 +3607,7 @@ DEFUN (bgp_disable_connected_route_check, "Disable checking if nexthop is connected on ebgp sessions\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); - bgp_flag_set(bgp, BGP_FLAG_DISABLE_NH_CONNECTED_CHK); + SET_FLAG(bgp->flags, BGP_FLAG_DISABLE_NH_CONNECTED_CHK); bgp_clear_star_soft_in(vty, bgp->name); return CMD_SUCCESS; @@ -2795,7 +3621,7 @@ DEFUN (no_bgp_disable_connected_route_check, "Disable checking if nexthop is connected on ebgp sessions\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); - bgp_flag_unset(bgp, BGP_FLAG_DISABLE_NH_CONNECTED_CHK); + UNSET_FLAG(bgp->flags, BGP_FLAG_DISABLE_NH_CONNECTED_CHK); bgp_clear_star_soft_in(vty, bgp->name); return CMD_SUCCESS; @@ -2879,6 +3705,50 @@ DEFUN (bgp_default_shutdown, return CMD_SUCCESS; } +DEFPY(bgp_shutdown_msg, bgp_shutdown_msg_cmd, "bgp shutdown message MSG...", + BGP_STR + "Administrative shutdown of the BGP instance\n" + "Add a shutdown message (RFC 8203)\n" + "Shutdown message\n") +{ + char *msgstr = NULL; + + VTY_DECLVAR_CONTEXT(bgp, bgp); + + if (argc > 3) + msgstr = argv_concat(argv, argc, 3); + + bgp_shutdown_enable(bgp, msgstr); + XFREE(MTYPE_TMP, msgstr); + + return CMD_SUCCESS; +} + +DEFPY(bgp_shutdown, bgp_shutdown_cmd, "bgp shutdown", + BGP_STR "Administrative shutdown of the BGP instance\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + + bgp_shutdown_enable(bgp, NULL); + + return CMD_SUCCESS; +} + +DEFPY(no_bgp_shutdown, no_bgp_shutdown_cmd, "no bgp shutdown", + NO_STR BGP_STR "Administrative shutdown of the BGP instance\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + + bgp_shutdown_disable(bgp); + + return CMD_SUCCESS; +} + +ALIAS(no_bgp_shutdown, no_bgp_shutdown_msg_cmd, + "no bgp shutdown message MSG...", NO_STR BGP_STR + "Administrative shutdown of the BGP instance\n" + "Add a shutdown message (RFC 8203)\n" "Shutdown message\n") + DEFUN (neighbor_remote_as, neighbor_remote_as_cmd, "neighbor remote-as <(1-4294967295)|internal|external>", @@ -2934,7 +3804,7 @@ static int peer_conf_interface_get(struct vty *vty, const char *conf_if, ret = peer_remote_as(bgp, NULL, conf_if, &as, as_type, afi, safi); } else { - if (bgp_flag_check(bgp, BGP_FLAG_NO_DEFAULT_IPV4) + if (CHECK_FLAG(bgp->flags, BGP_FLAG_NO_DEFAULT_IPV4) && afi == AFI_IP && safi == SAFI_UNICAST) peer = peer_create(NULL, conf_if, bgp, bgp->as, as, as_type, 0, 0, NULL); @@ -2999,7 +3869,7 @@ static int peer_conf_interface_get(struct vty *vty, const char *conf_if, DEFUN (neighbor_interface_config, neighbor_interface_config_cmd, - "neighbor WORD interface [peer-group WORD]", + "neighbor WORD interface [peer-group PGNAME]", NEIGHBOR_STR "Interface name or neighbor tag\n" "Enable BGP on interface\n" @@ -3020,7 +3890,7 @@ DEFUN (neighbor_interface_config, DEFUN (neighbor_interface_config_v6only, neighbor_interface_config_v6only_cmd, - "neighbor WORD interface v6only [peer-group WORD]", + "neighbor WORD interface v6only [peer-group PGNAME]", NEIGHBOR_STR "Interface name or neighbor tag\n" "Enable BGP on interface\n" @@ -3133,14 +4003,16 @@ DEFUN (no_neighbor, * interface. */ if (peer->ifp) bgp_zebra_terminate_radv(peer->bgp, peer); + peer_notify_unconfig(peer); peer_delete(peer); return CMD_SUCCESS; } group = peer_group_lookup(bgp, argv[idx_peer]->arg); - if (group) + if (group) { + peer_group_notify_unconfig(group); peer_group_delete(group); - else { + } else { vty_out(vty, "%% Create the peer-group first\n"); return CMD_WARNING_CONFIG_FAILED; } @@ -3154,9 +4026,16 @@ DEFUN (no_neighbor, } other = peer->doppelganger; + + if (CHECK_FLAG(peer->flags, PEER_FLAG_CAPABILITY_ENHE)) + bgp_zebra_terminate_radv(peer->bgp, peer); + + peer_notify_unconfig(peer); peer_delete(peer); - if (other && other->status != Deleted) + if (other && other->status != Deleted) { + peer_notify_unconfig(other); peer_delete(other); + } } } @@ -3165,7 +4044,7 @@ DEFUN (no_neighbor, DEFUN (no_neighbor_interface_config, no_neighbor_interface_config_cmd, - "no neighbor WORD interface [v6only] [peer-group WORD] [remote-as <(1-4294967295)|internal|external>]", + "no neighbor WORD interface [v6only] [peer-group PGNAME] [remote-as <(1-4294967295)|internal|external>]", NO_STR NEIGHBOR_STR "Interface name\n" @@ -3188,6 +4067,7 @@ DEFUN (no_neighbor_interface_config, /* Request zebra to terminate IPv6 RAs on this interface. */ if (peer->ifp) bgp_zebra_terminate_radv(peer->bgp, peer); + peer_notify_unconfig(peer); peer_delete(peer); } else { vty_out(vty, "%% Create the bgp interface first\n"); @@ -3209,9 +4089,10 @@ DEFUN (no_neighbor_peer_group, struct peer_group *group; group = peer_group_lookup(bgp, argv[idx_word]->arg); - if (group) + if (group) { + peer_group_notify_unconfig(group); peer_group_delete(group); - else { + } else { vty_out(vty, "%% Create the peer-group first\n"); return CMD_WARNING_CONFIG_FAILED; } @@ -3480,7 +4361,7 @@ ALIAS_HIDDEN(no_neighbor_activate, no_neighbor_activate_hidden_cmd, DEFUN (neighbor_set_peer_group, neighbor_set_peer_group_cmd, - "neighbor peer-group WORD", + "neighbor peer-group PGNAME", NEIGHBOR_STR NEIGHBOR_ADDR_STR2 "Member of the peer-group\n" @@ -3538,14 +4419,14 @@ DEFUN (neighbor_set_peer_group, } ALIAS_HIDDEN(neighbor_set_peer_group, neighbor_set_peer_group_hidden_cmd, - "neighbor peer-group WORD", + "neighbor peer-group PGNAME", NEIGHBOR_STR NEIGHBOR_ADDR_STR2 "Member of the peer-group\n" "Peer-group name\n") DEFUN (no_neighbor_set_peer_group, no_neighbor_set_peer_group_cmd, - "no neighbor peer-group WORD", + "no neighbor peer-group [PGNAME]", NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2 @@ -3554,28 +4435,24 @@ DEFUN (no_neighbor_set_peer_group, { VTY_DECLVAR_CONTEXT(bgp, bgp); int idx_peer = 2; - int idx_word = 4; int ret; struct peer *peer; - struct peer_group *group; peer = peer_lookup_vty(vty, argv[idx_peer]->arg); if (!peer) return CMD_WARNING_CONFIG_FAILED; - group = peer_group_lookup(bgp, argv[idx_word]->arg); - if (!group) { - vty_out(vty, "%% Configure the peer-group first\n"); - return CMD_WARNING_CONFIG_FAILED; - } + if (CHECK_FLAG(peer->flags, PEER_FLAG_CAPABILITY_ENHE)) + bgp_zebra_terminate_radv(peer->bgp, peer); + peer_notify_unconfig(peer); ret = peer_delete(peer); return bgp_vty_return(vty, ret); } ALIAS_HIDDEN(no_neighbor_set_peer_group, no_neighbor_set_peer_group_hidden_cmd, - "no neighbor peer-group WORD", + "no neighbor peer-group [PGNAME]", NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2 "Member of the peer-group\n" "Peer-group name\n") @@ -3596,8 +4473,7 @@ static int peer_flag_modify_vty(struct vty *vty, const char *ip_str, */ if (peer->conf_if && (flag == PEER_FLAG_DISABLE_CONNECTED_CHECK)) { vty_out(vty, - "%s is directly connected peer, cannot accept disable-" - "connected-check\n", + "%s is directly connected peer, cannot accept disable-connected-check\n", ip_str); return CMD_WARNING_CONFIG_FAILED; } @@ -3655,7 +4531,7 @@ DEFUN (neighbor_shutdown_msg, NEIGHBOR_STR NEIGHBOR_ADDR_STR2 "Administratively shut down this neighbor\n" - "Add a shutdown message (draft-ietf-idr-shutdown-06)\n" + "Add a shutdown message (RFC 8203)\n" "Shutdown message\n") { int idx_peer = 1; @@ -3687,7 +4563,7 @@ DEFUN (no_neighbor_shutdown_msg, NEIGHBOR_STR NEIGHBOR_ADDR_STR2 "Administratively shut down this neighbor\n" - "Remove a shutdown message (draft-ietf-idr-shutdown-06)\n" + "Remove a shutdown message (RFC 8203)\n" "Shutdown message\n") { int idx_peer = 2; @@ -3701,6 +4577,64 @@ ALIAS(no_neighbor_shutdown_msg, no_neighbor_shutdown_cmd, NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2 "Administratively shut down this neighbor\n") +DEFUN(neighbor_shutdown_rtt, + neighbor_shutdown_rtt_cmd, + "neighbor shutdown rtt (1-65535) [count (1-255)]", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Administratively shut down this neighbor\n" + "Shutdown if round-trip-time is higher than expected\n" + "Round-trip-time in milliseconds\n" + "Specify the number of keepalives before shutdown\n" + "The number of keepalives with higher RTT to shutdown\n") +{ + int idx_peer = 1; + int idx_rtt = 4; + int idx_count = 0; + struct peer *peer; + + peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg); + + if (!peer) + return CMD_WARNING_CONFIG_FAILED; + + peer->rtt_expected = strtol(argv[idx_rtt]->arg, NULL, 10); + + if (argv_find(argv, argc, "count", &idx_count)) + peer->rtt_keepalive_conf = + strtol(argv[idx_count + 1]->arg, NULL, 10); + + return peer_flag_set_vty(vty, argv[idx_peer]->arg, + PEER_FLAG_RTT_SHUTDOWN); +} + +DEFUN(no_neighbor_shutdown_rtt, + no_neighbor_shutdown_rtt_cmd, + "no neighbor shutdown rtt [(1-65535) [count (1-255)]]", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Administratively shut down this neighbor\n" + "Shutdown if round-trip-time is higher than expected\n" + "Round-trip-time in milliseconds\n" + "Specify the number of keepalives before shutdown\n" + "The number of keepalives with higher RTT to shutdown\n") +{ + int idx_peer = 2; + struct peer *peer; + + peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg); + + if (!peer) + return CMD_WARNING_CONFIG_FAILED; + + peer->rtt_expected = 0; + peer->rtt_keepalive_conf = 1; + + return peer_flag_unset_vty(vty, argv[idx_peer]->arg, + PEER_FLAG_RTT_SHUTDOWN); +} + /* neighbor capability dynamic. */ DEFUN (neighbor_capability_dynamic, neighbor_capability_dynamic_cmd, @@ -3827,23 +4761,31 @@ DEFUN (neighbor_capability_orf_prefix, "Capability to RECEIVE the ORF from this neighbor\n" "Capability to SEND the ORF to this neighbor\n") { - int idx_peer = 1; int idx_send_recv = 5; - uint16_t flag = 0; + char *peer_str = argv[1]->arg; + struct peer *peer; + afi_t afi = bgp_node_afi(vty); + safi_t safi = bgp_node_safi(vty); - if (strmatch(argv[idx_send_recv]->text, "send")) - flag = PEER_FLAG_ORF_PREFIX_SM; - else if (strmatch(argv[idx_send_recv]->text, "receive")) - flag = PEER_FLAG_ORF_PREFIX_RM; - else if (strmatch(argv[idx_send_recv]->text, "both")) - flag = PEER_FLAG_ORF_PREFIX_SM | PEER_FLAG_ORF_PREFIX_RM; - else { - vty_out(vty, "%% BGP invalid orf prefix-list option\n"); + peer = peer_and_group_lookup_vty(vty, peer_str); + if (!peer) return CMD_WARNING_CONFIG_FAILED; - } - return peer_af_flag_set_vty(vty, argv[idx_peer]->arg, bgp_node_afi(vty), - bgp_node_safi(vty), flag); + if (strmatch(argv[idx_send_recv]->text, "send")) + return peer_af_flag_set_vty(vty, peer_str, afi, safi, + PEER_FLAG_ORF_PREFIX_SM); + + if (strmatch(argv[idx_send_recv]->text, "receive")) + return peer_af_flag_set_vty(vty, peer_str, afi, safi, + PEER_FLAG_ORF_PREFIX_RM); + + if (strmatch(argv[idx_send_recv]->text, "both")) + return peer_af_flag_set_vty(vty, peer_str, afi, safi, + PEER_FLAG_ORF_PREFIX_SM) + | peer_af_flag_set_vty(vty, peer_str, afi, safi, + PEER_FLAG_ORF_PREFIX_RM); + + return CMD_WARNING_CONFIG_FAILED; } ALIAS_HIDDEN( @@ -3871,24 +4813,31 @@ DEFUN (no_neighbor_capability_orf_prefix, "Capability to RECEIVE the ORF from this neighbor\n" "Capability to SEND the ORF to this neighbor\n") { - int idx_peer = 2; int idx_send_recv = 6; - uint16_t flag = 0; + char *peer_str = argv[2]->arg; + struct peer *peer; + afi_t afi = bgp_node_afi(vty); + safi_t safi = bgp_node_safi(vty); - if (strmatch(argv[idx_send_recv]->text, "send")) - flag = PEER_FLAG_ORF_PREFIX_SM; - else if (strmatch(argv[idx_send_recv]->text, "receive")) - flag = PEER_FLAG_ORF_PREFIX_RM; - else if (strmatch(argv[idx_send_recv]->text, "both")) - flag = PEER_FLAG_ORF_PREFIX_SM | PEER_FLAG_ORF_PREFIX_RM; - else { - vty_out(vty, "%% BGP invalid orf prefix-list option\n"); + peer = peer_and_group_lookup_vty(vty, peer_str); + if (!peer) return CMD_WARNING_CONFIG_FAILED; - } - return peer_af_flag_unset_vty(vty, argv[idx_peer]->arg, - bgp_node_afi(vty), bgp_node_safi(vty), - flag); + if (strmatch(argv[idx_send_recv]->text, "send")) + return peer_af_flag_unset_vty(vty, peer_str, afi, safi, + PEER_FLAG_ORF_PREFIX_SM); + + if (strmatch(argv[idx_send_recv]->text, "receive")) + return peer_af_flag_unset_vty(vty, peer_str, afi, safi, + PEER_FLAG_ORF_PREFIX_RM); + + if (strmatch(argv[idx_send_recv]->text, "both")) + return peer_af_flag_unset_vty(vty, peer_str, afi, safi, + PEER_FLAG_ORF_PREFIX_SM) + | peer_af_flag_unset_vty(vty, peer_str, afi, safi, + PEER_FLAG_ORF_PREFIX_RM); + + return CMD_WARNING_CONFIG_FAILED; } ALIAS_HIDDEN( @@ -4263,27 +5212,40 @@ DEFUN (neighbor_send_community_type, "Send Standard Community attributes\n" "Send Large Community attributes\n") { - int idx_peer = 1; - uint32_t flag = 0; const char *type = argv[argc - 1]->text; + char *peer_str = argv[1]->arg; + struct peer *peer; + afi_t afi = bgp_node_afi(vty); + safi_t safi = bgp_node_safi(vty); - if (strmatch(type, "standard")) { - SET_FLAG(flag, PEER_FLAG_SEND_COMMUNITY); - } else if (strmatch(type, "extended")) { - SET_FLAG(flag, PEER_FLAG_SEND_EXT_COMMUNITY); - } else if (strmatch(type, "large")) { - SET_FLAG(flag, PEER_FLAG_SEND_LARGE_COMMUNITY); - } else if (strmatch(type, "both")) { - SET_FLAG(flag, PEER_FLAG_SEND_COMMUNITY); - SET_FLAG(flag, PEER_FLAG_SEND_EXT_COMMUNITY); - } else { /* if (strmatch(type, "all")) */ - SET_FLAG(flag, PEER_FLAG_SEND_COMMUNITY); - SET_FLAG(flag, PEER_FLAG_SEND_EXT_COMMUNITY); - SET_FLAG(flag, PEER_FLAG_SEND_LARGE_COMMUNITY); - } + peer = peer_and_group_lookup_vty(vty, peer_str); + if (!peer) + return CMD_WARNING_CONFIG_FAILED; - return peer_af_flag_set_vty(vty, argv[idx_peer]->arg, bgp_node_afi(vty), - bgp_node_safi(vty), flag); + if (strmatch(type, "standard")) + return peer_af_flag_set_vty(vty, peer_str, afi, safi, + PEER_FLAG_SEND_COMMUNITY); + + if (strmatch(type, "extended")) + return peer_af_flag_set_vty(vty, peer_str, afi, safi, + PEER_FLAG_SEND_EXT_COMMUNITY); + + if (strmatch(type, "large")) + return peer_af_flag_set_vty(vty, peer_str, afi, safi, + PEER_FLAG_SEND_LARGE_COMMUNITY); + + if (strmatch(type, "both")) { + return peer_af_flag_set_vty(vty, peer_str, afi, safi, + PEER_FLAG_SEND_COMMUNITY) + | peer_af_flag_set_vty(vty, peer_str, afi, safi, + PEER_FLAG_SEND_EXT_COMMUNITY); + } + return peer_af_flag_set_vty(vty, peer_str, afi, safi, + PEER_FLAG_SEND_COMMUNITY) + | peer_af_flag_set_vty(vty, peer_str, afi, safi, + PEER_FLAG_SEND_EXT_COMMUNITY) + | peer_af_flag_set_vty(vty, peer_str, afi, safi, + PEER_FLAG_SEND_LARGE_COMMUNITY); } ALIAS_HIDDEN( @@ -4310,28 +5272,42 @@ DEFUN (no_neighbor_send_community_type, "Send Standard Community attributes\n" "Send Large Community attributes\n") { - int idx_peer = 2; - uint32_t flag = 0; const char *type = argv[argc - 1]->text; + char *peer_str = argv[2]->arg; + struct peer *peer; + afi_t afi = bgp_node_afi(vty); + safi_t safi = bgp_node_safi(vty); + + peer = peer_and_group_lookup_vty(vty, peer_str); + if (!peer) + return CMD_WARNING_CONFIG_FAILED; + + if (strmatch(type, "standard")) + return peer_af_flag_unset_vty(vty, peer_str, afi, safi, + PEER_FLAG_SEND_COMMUNITY); + + if (strmatch(type, "extended")) + return peer_af_flag_unset_vty(vty, peer_str, afi, safi, + PEER_FLAG_SEND_EXT_COMMUNITY); - if (strmatch(type, "standard")) { - SET_FLAG(flag, PEER_FLAG_SEND_COMMUNITY); - } else if (strmatch(type, "extended")) { - SET_FLAG(flag, PEER_FLAG_SEND_EXT_COMMUNITY); - } else if (strmatch(type, "large")) { - SET_FLAG(flag, PEER_FLAG_SEND_LARGE_COMMUNITY); - } else if (strmatch(type, "both")) { - SET_FLAG(flag, PEER_FLAG_SEND_COMMUNITY); - SET_FLAG(flag, PEER_FLAG_SEND_EXT_COMMUNITY); - } else { /* if (strmatch(type, "all")) */ - SET_FLAG(flag, PEER_FLAG_SEND_COMMUNITY); - SET_FLAG(flag, PEER_FLAG_SEND_EXT_COMMUNITY); - SET_FLAG(flag, PEER_FLAG_SEND_LARGE_COMMUNITY); + if (strmatch(type, "large")) + return peer_af_flag_unset_vty(vty, peer_str, afi, safi, + PEER_FLAG_SEND_LARGE_COMMUNITY); + + if (strmatch(type, "both")) { + + return peer_af_flag_unset_vty(vty, peer_str, afi, safi, + PEER_FLAG_SEND_COMMUNITY) + | peer_af_flag_unset_vty(vty, peer_str, afi, safi, + PEER_FLAG_SEND_EXT_COMMUNITY); } - return peer_af_flag_unset_vty(vty, argv[idx_peer]->arg, - bgp_node_afi(vty), bgp_node_safi(vty), - flag); + return peer_af_flag_unset_vty(vty, peer_str, afi, safi, + PEER_FLAG_SEND_COMMUNITY) + | peer_af_flag_unset_vty(vty, peer_str, afi, safi, + PEER_FLAG_SEND_EXT_COMMUNITY) + | peer_af_flag_unset_vty(vty, peer_str, afi, safi, + PEER_FLAG_SEND_LARGE_COMMUNITY); } ALIAS_HIDDEN( @@ -4523,52 +5499,74 @@ DEFUN (neighbor_attr_unchanged, int idx = 0; char *peer_str = argv[1]->arg; struct peer *peer; - uint16_t flags = 0; + bool aspath = false; + bool nexthop = false; + bool med = false; afi_t afi = bgp_node_afi(vty); safi_t safi = bgp_node_safi(vty); + int ret = 0; peer = peer_and_group_lookup_vty(vty, peer_str); if (!peer) return CMD_WARNING_CONFIG_FAILED; if (argv_find(argv, argc, "as-path", &idx)) - SET_FLAG(flags, PEER_FLAG_AS_PATH_UNCHANGED); + aspath = true; + idx = 0; if (argv_find(argv, argc, "next-hop", &idx)) - SET_FLAG(flags, PEER_FLAG_NEXTHOP_UNCHANGED); + nexthop = true; + idx = 0; if (argv_find(argv, argc, "med", &idx)) - SET_FLAG(flags, PEER_FLAG_MED_UNCHANGED); + med = true; /* no flags means all of them! */ - if (!flags) { - SET_FLAG(flags, PEER_FLAG_AS_PATH_UNCHANGED); - SET_FLAG(flags, PEER_FLAG_NEXTHOP_UNCHANGED); - SET_FLAG(flags, PEER_FLAG_MED_UNCHANGED); + if (!aspath && !nexthop && !med) { + ret = peer_af_flag_set_vty(vty, peer_str, afi, safi, + PEER_FLAG_AS_PATH_UNCHANGED); + ret |= peer_af_flag_set_vty(vty, peer_str, afi, safi, + PEER_FLAG_NEXTHOP_UNCHANGED); + ret |= peer_af_flag_set_vty(vty, peer_str, afi, safi, + PEER_FLAG_MED_UNCHANGED); } else { - if (!CHECK_FLAG(flags, PEER_FLAG_AS_PATH_UNCHANGED) - && peer_af_flag_check(peer, afi, safi, - PEER_FLAG_AS_PATH_UNCHANGED)) { - peer_af_flag_unset_vty(vty, peer_str, afi, safi, - PEER_FLAG_AS_PATH_UNCHANGED); - } - - if (!CHECK_FLAG(flags, PEER_FLAG_NEXTHOP_UNCHANGED) - && peer_af_flag_check(peer, afi, safi, - PEER_FLAG_NEXTHOP_UNCHANGED)) { - peer_af_flag_unset_vty(vty, peer_str, afi, safi, - PEER_FLAG_NEXTHOP_UNCHANGED); - } - - if (!CHECK_FLAG(flags, PEER_FLAG_MED_UNCHANGED) - && peer_af_flag_check(peer, afi, safi, - PEER_FLAG_MED_UNCHANGED)) { - peer_af_flag_unset_vty(vty, peer_str, afi, safi, - PEER_FLAG_MED_UNCHANGED); - } + if (!aspath) { + if (peer_af_flag_check(peer, afi, safi, + PEER_FLAG_AS_PATH_UNCHANGED)) { + ret |= peer_af_flag_unset_vty( + vty, peer_str, afi, safi, + PEER_FLAG_AS_PATH_UNCHANGED); + } + } else + ret |= peer_af_flag_set_vty( + vty, peer_str, afi, safi, + PEER_FLAG_AS_PATH_UNCHANGED); + + if (!nexthop) { + if (peer_af_flag_check(peer, afi, safi, + PEER_FLAG_NEXTHOP_UNCHANGED)) { + ret |= peer_af_flag_unset_vty( + vty, peer_str, afi, safi, + PEER_FLAG_NEXTHOP_UNCHANGED); + } + } else + ret |= peer_af_flag_set_vty( + vty, peer_str, afi, safi, + PEER_FLAG_NEXTHOP_UNCHANGED); + + if (!med) { + if (peer_af_flag_check(peer, afi, safi, + PEER_FLAG_MED_UNCHANGED)) { + ret |= peer_af_flag_unset_vty( + vty, peer_str, afi, safi, + PEER_FLAG_MED_UNCHANGED); + } + } else + ret |= peer_af_flag_set_vty(vty, peer_str, afi, safi, + PEER_FLAG_MED_UNCHANGED); } - return peer_af_flag_set_vty(vty, peer_str, afi, safi, flags); + return ret; } ALIAS_HIDDEN( @@ -4592,27 +5590,51 @@ DEFUN (no_neighbor_attr_unchanged, "Med attribute\n") { int idx = 0; - char *peer = argv[2]->arg; - uint16_t flags = 0; + char *peer_str = argv[2]->arg; + struct peer *peer; + bool aspath = false; + bool nexthop = false; + bool med = false; + afi_t afi = bgp_node_afi(vty); + safi_t safi = bgp_node_safi(vty); + int ret = 0; + + peer = peer_and_group_lookup_vty(vty, peer_str); + if (!peer) + return CMD_WARNING_CONFIG_FAILED; if (argv_find(argv, argc, "as-path", &idx)) - SET_FLAG(flags, PEER_FLAG_AS_PATH_UNCHANGED); + aspath = true; + idx = 0; if (argv_find(argv, argc, "next-hop", &idx)) - SET_FLAG(flags, PEER_FLAG_NEXTHOP_UNCHANGED); + nexthop = true; + idx = 0; if (argv_find(argv, argc, "med", &idx)) - SET_FLAG(flags, PEER_FLAG_MED_UNCHANGED); + med = true; - if (!flags) // no flags means all of them! - { - SET_FLAG(flags, PEER_FLAG_AS_PATH_UNCHANGED); - SET_FLAG(flags, PEER_FLAG_NEXTHOP_UNCHANGED); - SET_FLAG(flags, PEER_FLAG_MED_UNCHANGED); - } + if (!aspath && !nexthop && !med) // no flags means all of them! + return peer_af_flag_unset_vty(vty, peer_str, afi, safi, + PEER_FLAG_AS_PATH_UNCHANGED) + | peer_af_flag_unset_vty(vty, peer_str, afi, safi, + PEER_FLAG_NEXTHOP_UNCHANGED) + | peer_af_flag_unset_vty(vty, peer_str, afi, safi, + PEER_FLAG_MED_UNCHANGED); + + if (aspath) + ret |= peer_af_flag_unset_vty(vty, peer_str, afi, safi, + PEER_FLAG_AS_PATH_UNCHANGED); - return peer_af_flag_unset_vty(vty, peer, bgp_node_afi(vty), - bgp_node_safi(vty), flags); + if (nexthop) + ret |= peer_af_flag_unset_vty(vty, peer_str, afi, safi, + PEER_FLAG_NEXTHOP_UNCHANGED); + + if (med) + ret |= peer_af_flag_unset_vty(vty, peer_str, afi, safi, + PEER_FLAG_MED_UNCHANGED); + + return ret; } ALIAS_HIDDEN( @@ -4877,14 +5899,15 @@ static int peer_default_originate_set_vty(struct vty *vty, const char *peer_str, { int ret; struct peer *peer; - struct route_map *route_map; + struct route_map *route_map = NULL; peer = peer_and_group_lookup_vty(vty, peer_str); if (!peer) return CMD_WARNING_CONFIG_FAILED; if (set) { - route_map = route_map_lookup_warn_noexist(vty, rmap); + if (rmap) + route_map = route_map_lookup_warn_noexist(vty, rmap); ret = peer_default_originate_set(peer, afi, safi, rmap, route_map); } else @@ -5880,7 +6903,8 @@ static int peer_maximum_prefix_set_vty(struct vty *vty, const char *ip_str, afi_t afi, safi_t safi, const char *num_str, const char *threshold_str, int warning, - const char *restart_str) + const char *restart_str, + const char *force_str) { int ret; struct peer *peer; @@ -5904,7 +6928,7 @@ static int peer_maximum_prefix_set_vty(struct vty *vty, const char *ip_str, restart = 0; ret = peer_maximum_prefix_set(peer, afi, safi, max, threshold, warning, - restart); + restart, force_str ? true : false); return bgp_vty_return(vty, ret); } @@ -5924,172 +6948,271 @@ static int peer_maximum_prefix_unset_vty(struct vty *vty, const char *ip_str, return bgp_vty_return(vty, ret); } -/* Maximum number of prefix configuration. prefix count is different - for each peer configuration. So this configuration can be set for +/* Maximum number of prefix to be sent to the neighbor. */ +DEFUN(neighbor_maximum_prefix_out, + neighbor_maximum_prefix_out_cmd, + "neighbor maximum-prefix-out (1-4294967295)", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Maximum number of prefixes to be sent to this peer\n" + "Maximum no. of prefix limit\n") +{ + int idx_peer = 1; + int idx_number = 3; + struct peer *peer; + uint32_t max; + afi_t afi = bgp_node_afi(vty); + safi_t safi = bgp_node_safi(vty); + + peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg); + if (!peer) + return CMD_WARNING_CONFIG_FAILED; + + max = strtoul(argv[idx_number]->arg, NULL, 10); + + SET_FLAG(peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_OUT); + peer->pmax_out[afi][safi] = max; + + return CMD_SUCCESS; +} + +DEFUN(no_neighbor_maximum_prefix_out, + no_neighbor_maximum_prefix_out_cmd, + "no neighbor maximum-prefix-out", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Maximum number of prefixes to be sent to this peer\n") +{ + int idx_peer = 2; + struct peer *peer; + afi_t afi = bgp_node_afi(vty); + safi_t safi = bgp_node_safi(vty); + + peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg); + if (!peer) + return CMD_WARNING_CONFIG_FAILED; + + UNSET_FLAG(peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_OUT); + peer->pmax_out[afi][safi] = 0; + + return CMD_SUCCESS; +} + +/* Maximum number of prefix configuration. Prefix count is different + for each peer configuration. So this configuration can be set for each peer configuration. */ DEFUN (neighbor_maximum_prefix, neighbor_maximum_prefix_cmd, - "neighbor maximum-prefix (1-4294967295)", + "neighbor maximum-prefix (1-4294967295) [force]", NEIGHBOR_STR NEIGHBOR_ADDR_STR2 "Maximum number of prefix accept from this peer\n" - "maximum no. of prefix limit\n") + "maximum no. of prefix limit\n" + "Force checking all received routes not only accepted\n") { int idx_peer = 1; int idx_number = 3; + int idx_force = 0; + char *force = NULL; + + if (argv_find(argv, argc, "force", &idx_force)) + force = argv[idx_force]->arg; + return peer_maximum_prefix_set_vty( vty, argv[idx_peer]->arg, bgp_node_afi(vty), bgp_node_safi(vty), - argv[idx_number]->arg, NULL, 0, NULL); + argv[idx_number]->arg, NULL, 0, NULL, force); } ALIAS_HIDDEN(neighbor_maximum_prefix, neighbor_maximum_prefix_hidden_cmd, - "neighbor maximum-prefix (1-4294967295)", + "neighbor maximum-prefix (1-4294967295) [force]", NEIGHBOR_STR NEIGHBOR_ADDR_STR2 "Maximum number of prefix accept from this peer\n" - "maximum no. of prefix limit\n") + "maximum no. of prefix limit\n" + "Force checking all received routes not only accepted\n") DEFUN (neighbor_maximum_prefix_threshold, neighbor_maximum_prefix_threshold_cmd, - "neighbor maximum-prefix (1-4294967295) (1-100)", + "neighbor maximum-prefix (1-4294967295) (1-100) [force]", NEIGHBOR_STR NEIGHBOR_ADDR_STR2 "Maximum number of prefix accept from this peer\n" "maximum no. of prefix limit\n" - "Threshold value (%) at which to generate a warning msg\n") + "Threshold value (%) at which to generate a warning msg\n" + "Force checking all received routes not only accepted\n") { int idx_peer = 1; int idx_number = 3; int idx_number_2 = 4; + int idx_force = 0; + char *force = NULL; + + if (argv_find(argv, argc, "force", &idx_force)) + force = argv[idx_force]->arg; + return peer_maximum_prefix_set_vty( vty, argv[idx_peer]->arg, bgp_node_afi(vty), bgp_node_safi(vty), - argv[idx_number]->arg, argv[idx_number_2]->arg, 0, NULL); + argv[idx_number]->arg, argv[idx_number_2]->arg, 0, NULL, force); } ALIAS_HIDDEN( neighbor_maximum_prefix_threshold, neighbor_maximum_prefix_threshold_hidden_cmd, - "neighbor maximum-prefix (1-4294967295) (1-100)", + "neighbor maximum-prefix (1-4294967295) (1-100) [force]", NEIGHBOR_STR NEIGHBOR_ADDR_STR2 "Maximum number of prefix accept from this peer\n" "maximum no. of prefix limit\n" - "Threshold value (%) at which to generate a warning msg\n") + "Threshold value (%) at which to generate a warning msg\n" + "Force checking all received routes not only accepted\n") DEFUN (neighbor_maximum_prefix_warning, neighbor_maximum_prefix_warning_cmd, - "neighbor maximum-prefix (1-4294967295) warning-only", + "neighbor maximum-prefix (1-4294967295) warning-only [force]", NEIGHBOR_STR NEIGHBOR_ADDR_STR2 "Maximum number of prefix accept from this peer\n" "maximum no. of prefix limit\n" - "Only give warning message when limit is exceeded\n") + "Only give warning message when limit is exceeded\n" + "Force checking all received routes not only accepted\n") { int idx_peer = 1; int idx_number = 3; + int idx_force = 0; + char *force = NULL; + + if (argv_find(argv, argc, "force", &idx_force)) + force = argv[idx_force]->arg; + return peer_maximum_prefix_set_vty( vty, argv[idx_peer]->arg, bgp_node_afi(vty), bgp_node_safi(vty), - argv[idx_number]->arg, NULL, 1, NULL); + argv[idx_number]->arg, NULL, 1, NULL, force); } ALIAS_HIDDEN( neighbor_maximum_prefix_warning, neighbor_maximum_prefix_warning_hidden_cmd, - "neighbor maximum-prefix (1-4294967295) warning-only", + "neighbor maximum-prefix (1-4294967295) warning-only [force]", NEIGHBOR_STR NEIGHBOR_ADDR_STR2 "Maximum number of prefix accept from this peer\n" "maximum no. of prefix limit\n" - "Only give warning message when limit is exceeded\n") + "Only give warning message when limit is exceeded\n" + "Force checking all received routes not only accepted\n") DEFUN (neighbor_maximum_prefix_threshold_warning, neighbor_maximum_prefix_threshold_warning_cmd, - "neighbor maximum-prefix (1-4294967295) (1-100) warning-only", + "neighbor maximum-prefix (1-4294967295) (1-100) warning-only [force]", NEIGHBOR_STR NEIGHBOR_ADDR_STR2 "Maximum number of prefix accept from this peer\n" "maximum no. of prefix limit\n" "Threshold value (%) at which to generate a warning msg\n" - "Only give warning message when limit is exceeded\n") + "Only give warning message when limit is exceeded\n" + "Force checking all received routes not only accepted\n") { int idx_peer = 1; int idx_number = 3; int idx_number_2 = 4; + int idx_force = 0; + char *force = NULL; + + if (argv_find(argv, argc, "force", &idx_force)) + force = argv[idx_force]->arg; + return peer_maximum_prefix_set_vty( vty, argv[idx_peer]->arg, bgp_node_afi(vty), bgp_node_safi(vty), - argv[idx_number]->arg, argv[idx_number_2]->arg, 1, NULL); + argv[idx_number]->arg, argv[idx_number_2]->arg, 1, NULL, force); } ALIAS_HIDDEN( neighbor_maximum_prefix_threshold_warning, neighbor_maximum_prefix_threshold_warning_hidden_cmd, - "neighbor maximum-prefix (1-4294967295) (1-100) warning-only", + "neighbor maximum-prefix (1-4294967295) (1-100) warning-only [force]", NEIGHBOR_STR NEIGHBOR_ADDR_STR2 "Maximum number of prefix accept from this peer\n" "maximum no. of prefix limit\n" "Threshold value (%) at which to generate a warning msg\n" - "Only give warning message when limit is exceeded\n") + "Only give warning message when limit is exceeded\n" + "Force checking all received routes not only accepted\n") DEFUN (neighbor_maximum_prefix_restart, neighbor_maximum_prefix_restart_cmd, - "neighbor maximum-prefix (1-4294967295) restart (1-65535)", + "neighbor maximum-prefix (1-4294967295) restart (1-65535) [force]", NEIGHBOR_STR NEIGHBOR_ADDR_STR2 "Maximum number of prefix accept from this peer\n" "maximum no. of prefix limit\n" "Restart bgp connection after limit is exceeded\n" - "Restart interval in minutes\n") + "Restart interval in minutes\n" + "Force checking all received routes not only accepted\n") { int idx_peer = 1; int idx_number = 3; int idx_number_2 = 5; + int idx_force = 0; + char *force = NULL; + + if (argv_find(argv, argc, "force", &idx_force)) + force = argv[idx_force]->arg; + return peer_maximum_prefix_set_vty( vty, argv[idx_peer]->arg, bgp_node_afi(vty), bgp_node_safi(vty), - argv[idx_number]->arg, NULL, 0, argv[idx_number_2]->arg); + argv[idx_number]->arg, NULL, 0, argv[idx_number_2]->arg, force); } ALIAS_HIDDEN( neighbor_maximum_prefix_restart, neighbor_maximum_prefix_restart_hidden_cmd, - "neighbor maximum-prefix (1-4294967295) restart (1-65535)", + "neighbor maximum-prefix (1-4294967295) restart (1-65535) [force]", NEIGHBOR_STR NEIGHBOR_ADDR_STR2 "Maximum number of prefix accept from this peer\n" "maximum no. of prefix limit\n" "Restart bgp connection after limit is exceeded\n" - "Restart interval in minutes\n") + "Restart interval in minutes\n" + "Force checking all received routes not only accepted\n") DEFUN (neighbor_maximum_prefix_threshold_restart, neighbor_maximum_prefix_threshold_restart_cmd, - "neighbor maximum-prefix (1-4294967295) (1-100) restart (1-65535)", + "neighbor maximum-prefix (1-4294967295) (1-100) restart (1-65535) [force]", NEIGHBOR_STR NEIGHBOR_ADDR_STR2 "Maximum number of prefixes to accept from this peer\n" "maximum no. of prefix limit\n" "Threshold value (%) at which to generate a warning msg\n" "Restart bgp connection after limit is exceeded\n" - "Restart interval in minutes\n") + "Restart interval in minutes\n" + "Force checking all received routes not only accepted\n") { int idx_peer = 1; int idx_number = 3; int idx_number_2 = 4; int idx_number_3 = 6; + int idx_force = 0; + char *force = NULL; + + if (argv_find(argv, argc, "force", &idx_force)) + force = argv[idx_force]->arg; + return peer_maximum_prefix_set_vty( vty, argv[idx_peer]->arg, bgp_node_afi(vty), bgp_node_safi(vty), argv[idx_number]->arg, argv[idx_number_2]->arg, 0, - argv[idx_number_3]->arg); + argv[idx_number_3]->arg, force); } ALIAS_HIDDEN( neighbor_maximum_prefix_threshold_restart, neighbor_maximum_prefix_threshold_restart_hidden_cmd, - "neighbor maximum-prefix (1-4294967295) (1-100) restart (1-65535)", + "neighbor maximum-prefix (1-4294967295) (1-100) restart (1-65535) [force]", NEIGHBOR_STR NEIGHBOR_ADDR_STR2 "Maximum number of prefixes to accept from this peer\n" "maximum no. of prefix limit\n" "Threshold value (%) at which to generate a warning msg\n" "Restart bgp connection after limit is exceeded\n" - "Restart interval in minutes\n") + "Restart interval in minutes\n" + "Force checking all received routes not only accepted\n") DEFUN (no_neighbor_maximum_prefix, no_neighbor_maximum_prefix_cmd, - "no neighbor maximum-prefix [(1-4294967295) [(1-100)] [restart (1-65535)] [warning-only]]", + "no neighbor maximum-prefix [(1-4294967295) [(1-100)] [restart (1-65535)] [warning-only] [force]]", NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2 @@ -6098,7 +7221,8 @@ DEFUN (no_neighbor_maximum_prefix, "Threshold value (%) at which to generate a warning msg\n" "Restart bgp connection after limit is exceeded\n" "Restart interval in minutes\n" - "Only give warning message when limit is exceeded\n") + "Only give warning message when limit is exceeded\n" + "Force checking all received routes not only accepted\n") { int idx_peer = 2; return peer_maximum_prefix_unset_vty(vty, argv[idx_peer]->arg, @@ -6108,14 +7232,15 @@ DEFUN (no_neighbor_maximum_prefix, ALIAS_HIDDEN( no_neighbor_maximum_prefix, no_neighbor_maximum_prefix_hidden_cmd, - "no neighbor maximum-prefix [(1-4294967295) [(1-100)] [restart (1-65535)] [warning-only]]", + "no neighbor maximum-prefix [(1-4294967295) [(1-100)] [restart (1-65535)] [warning-only] [force]]", NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2 "Maximum number of prefixes to accept from this peer\n" "maximum no. of prefix limit\n" "Threshold value (%) at which to generate a warning msg\n" "Restart bgp connection after limit is exceeded\n" "Restart interval in minutes\n" - "Only give warning message when limit is exceeded\n") + "Only give warning message when limit is exceeded\n" + "Force checking all received routes not only accepted\n") /* "neighbor allowas-in" */ @@ -6125,7 +7250,7 @@ DEFUN (neighbor_allowas_in, NEIGHBOR_STR NEIGHBOR_ADDR_STR2 "Accept as-path with my AS present in it\n" - "Number of occurences of AS number\n" + "Number of occurrences of AS number\n" "Only accept my AS in the as-path if the route was originated in my AS\n") { int idx_peer = 1; @@ -6159,7 +7284,7 @@ ALIAS_HIDDEN( "neighbor allowas-in [<(1-10)|origin>]", NEIGHBOR_STR NEIGHBOR_ADDR_STR2 "Accept as-path with my AS present in it\n" - "Number of occurences of AS number\n" + "Number of occurrences of AS number\n" "Only accept my AS in the as-path if the route was originated in my AS\n") DEFUN (no_neighbor_allowas_in, @@ -6169,7 +7294,7 @@ DEFUN (no_neighbor_allowas_in, NEIGHBOR_STR NEIGHBOR_ADDR_STR2 "allow local ASN appears in aspath attribute\n" - "Number of occurences of AS number\n" + "Number of occurrences of AS number\n" "Only accept my AS in the as-path if the route was originated in my AS\n") { int idx_peer = 2; @@ -6191,7 +7316,7 @@ ALIAS_HIDDEN( "no neighbor allowas-in [<(1-10)|origin>]", NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2 "allow local ASN appears in aspath attribute\n" - "Number of occurences of AS number\n" + "Number of occurrences of AS number\n" "Only accept my AS in the as-path if the route was originated in my AS\n") DEFUN (neighbor_ttl_security, @@ -6218,7 +7343,7 @@ DEFUN (neighbor_ttl_security, * If 'neighbor swpX', then this is for directly connected peers, * we should not accept a ttl-security hops value greater than 1. */ - if (peer->conf_if && (gtsm_hops > 1)) { + if (peer->conf_if && (gtsm_hops > BGP_GTSM_HOPS_CONNECTED)) { vty_out(vty, "%s is directly connected peer, hops cannot exceed 1\n", argv[idx_peer]->arg); @@ -6367,16 +7492,59 @@ ALIAS_HIDDEN(no_neighbor_addpath_tx_bestpath_per_as, NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2 "Use addpath to advertise the bestpath per each neighboring AS\n") +DEFPY( + neighbor_aspath_loop_detection, neighbor_aspath_loop_detection_cmd, + "neighbor $neighbor sender-as-path-loop-detection", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Detect AS loops before sending to neighbor\n") +{ + struct peer *peer; + + peer = peer_and_group_lookup_vty(vty, neighbor); + if (!peer) + return CMD_WARNING_CONFIG_FAILED; + + peer->as_path_loop_detection = true; + + return CMD_SUCCESS; +} + +DEFPY( + no_neighbor_aspath_loop_detection, + no_neighbor_aspath_loop_detection_cmd, + "no neighbor $neighbor sender-as-path-loop-detection", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Detect AS loops before sending to neighbor\n") +{ + struct peer *peer; + + peer = peer_and_group_lookup_vty(vty, neighbor); + if (!peer) + return CMD_WARNING_CONFIG_FAILED; + + peer->as_path_loop_detection = false; + + return CMD_SUCCESS; +} + static int set_ecom_list(struct vty *vty, int argc, struct cmd_token **argv, - struct ecommunity **list) + struct ecommunity **list, bool is_rt6) { struct ecommunity *ecom = NULL; struct ecommunity *ecomadd; for (; argc; --argc, ++argv) { - - ecomadd = ecommunity_str2com(argv[0]->arg, - ECOMMUNITY_ROUTE_TARGET, 0); + if (is_rt6) + ecomadd = ecommunity_str2com_ipv6(argv[0]->arg, + ECOMMUNITY_ROUTE_TARGET, + 0); + else + ecomadd = ecommunity_str2com(argv[0]->arg, + ECOMMUNITY_ROUTE_TARGET, + 0); if (!ecomadd) { vty_out(vty, "Malformed community-list value\n"); if (ecom) @@ -6456,10 +7624,10 @@ DEFPY (af_rd_vpn_export, int ret; afi_t afi; int idx = 0; - int yes = 1; + bool yes = true; if (argv_find(argv, argc, "no", &idx)) - yes = 0; + yes = false; if (yes) { ret = str2prefix_rd(rd_str, &prd); @@ -6517,10 +7685,10 @@ DEFPY (af_label_vpn_export, mpls_label_t label = MPLS_LABEL_NONE; afi_t afi; int idx = 0; - int yes = 1; + bool yes = true; if (argv_find(argv, argc, "no", &idx)) - yes = 0; + yes = false; /* If "no ...", squash trailing parameter */ if (!yes) @@ -6595,7 +7763,7 @@ ALIAS (af_label_vpn_export, DEFPY (af_nexthop_vpn_export, af_nexthop_vpn_export_cmd, - "[no] nexthop vpn export $nexthop_str", + "[no] nexthop vpn export [$nexthop_su]", NO_STR "Specify next hop to use for VRF advertised prefixes\n" "Between current address-family and vpn\n" @@ -6606,14 +7774,13 @@ DEFPY (af_nexthop_vpn_export, VTY_DECLVAR_CONTEXT(bgp, bgp); afi_t afi; struct prefix p; - int idx = 0; - int yes = 1; - - if (argv_find(argv, argc, "no", &idx)) - yes = 0; - if (yes) { - if (!sockunion2hostprefix(nexthop_str, &p)) + if (!no) { + if (!nexthop_su) { + vty_out(vty, "%% Nexthop required\n"); + return CMD_WARNING_CONFIG_FAILED; + } + if (!sockunion2hostprefix(nexthop_su, &p)) return CMD_WARNING_CONFIG_FAILED; } @@ -6627,7 +7794,7 @@ DEFPY (af_nexthop_vpn_export, vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, afi, bgp_get_default(), bgp); - if (yes) { + if (!no) { bgp->vpn_policy[afi].tovpn_nexthop = p; SET_FLAG(bgp->vpn_policy[afi].flags, BGP_VPN_POLICY_TOVPN_NEXTHOP_SET); @@ -6643,14 +7810,6 @@ DEFPY (af_nexthop_vpn_export, return CMD_SUCCESS; } -ALIAS (af_nexthop_vpn_export, - af_no_nexthop_vpn_export_cmd, - "no nexthop vpn export", - NO_STR - "Specify next hop to use for VRF advertised prefixes\n" - "Between current address-family and vpn\n" - "For routes leaked from current address-family to vpn\n") - static int vpn_policy_getdirs(struct vty *vty, const char *dstr, int *dodir) { if (!strcmp(dstr, "import")) { @@ -6686,10 +7845,10 @@ DEFPY (af_rt_vpn_imexport, vpn_policy_direction_t dir; afi_t afi; int idx = 0; - int yes = 1; + bool yes = true; if (argv_find(argv, argc, "no", &idx)) - yes = 0; + yes = false; afi = vpn_policy_getafi(vty, bgp, false); if (afi == AFI_MAX) @@ -6704,7 +7863,7 @@ DEFPY (af_rt_vpn_imexport, vty_out(vty, "%% Missing RTLIST\n"); return CMD_WARNING_CONFIG_FAILED; } - ret = set_ecom_list(vty, argc - idx, argv + idx, &ecom); + ret = set_ecom_list(vty, argc - idx, argv + idx, &ecom, false); if (ret != CMD_SUCCESS) { return ret; } @@ -6766,10 +7925,10 @@ DEFPY (af_route_map_vpn_imexport, vpn_policy_direction_t dir; afi_t afi; int idx = 0; - int yes = 1; + bool yes = true; if (argv_find(argv, argc, "no", &idx)) - yes = 0; + yes = false; afi = vpn_policy_getafi(vty, bgp, false); if (afi == AFI_MAX) @@ -6819,8 +7978,7 @@ ALIAS (af_route_map_vpn_imexport, "For routes leaked from current address-family to vpn\n") DEFPY(af_import_vrf_route_map, af_import_vrf_route_map_cmd, - "[no] import vrf route-map RMAP$rmap_str", - NO_STR + "import vrf route-map RMAP$rmap_str", "Import routes from another VRF\n" "Vrf routes being filtered\n" "Specify route map\n" @@ -6829,13 +7987,8 @@ DEFPY(af_import_vrf_route_map, af_import_vrf_route_map_cmd, VTY_DECLVAR_CONTEXT(bgp, bgp); vpn_policy_direction_t dir = BGP_VPN_POLICY_DIR_FROMVPN; afi_t afi; - int idx = 0; - int yes = 1; struct bgp *bgp_default; - if (argv_find(argv, argc, "no", &idx)) - yes = 0; - afi = vpn_policy_getafi(vty, bgp, true); if (afi == AFI_MAX) return CMD_WARNING_CONFIG_FAILED; @@ -6846,8 +7999,8 @@ DEFPY(af_import_vrf_route_map, af_import_vrf_route_map_cmd, as_t as = bgp->as; /* Auto-create assuming the same AS */ - ret = bgp_get(&bgp_default, &as, NULL, - BGP_INSTANCE_TYPE_DEFAULT); + ret = bgp_get_vty(&bgp_default, &as, NULL, + BGP_INSTANCE_TYPE_DEFAULT); if (ret) { vty_out(vty, @@ -6858,35 +8011,56 @@ DEFPY(af_import_vrf_route_map, af_import_vrf_route_map_cmd, vpn_leak_prechange(dir, afi, bgp_get_default(), bgp); - if (yes) { - if (bgp->vpn_policy[afi].rmap_name[dir]) - XFREE(MTYPE_ROUTE_MAP_NAME, - bgp->vpn_policy[afi].rmap_name[dir]); - bgp->vpn_policy[afi].rmap_name[dir] = - XSTRDUP(MTYPE_ROUTE_MAP_NAME, rmap_str); - bgp->vpn_policy[afi].rmap[dir] = - route_map_lookup_warn_noexist(vty, rmap_str); - if (!bgp->vpn_policy[afi].rmap[dir]) - return CMD_SUCCESS; - } else { - if (bgp->vpn_policy[afi].rmap_name[dir]) - XFREE(MTYPE_ROUTE_MAP_NAME, - bgp->vpn_policy[afi].rmap_name[dir]); - bgp->vpn_policy[afi].rmap_name[dir] = NULL; - bgp->vpn_policy[afi].rmap[dir] = NULL; - } + if (bgp->vpn_policy[afi].rmap_name[dir]) + XFREE(MTYPE_ROUTE_MAP_NAME, + bgp->vpn_policy[afi].rmap_name[dir]); + bgp->vpn_policy[afi].rmap_name[dir] = + XSTRDUP(MTYPE_ROUTE_MAP_NAME, rmap_str); + bgp->vpn_policy[afi].rmap[dir] = + route_map_lookup_warn_noexist(vty, rmap_str); + if (!bgp->vpn_policy[afi].rmap[dir]) + return CMD_SUCCESS; + + SET_FLAG(bgp->af_flags[afi][SAFI_UNICAST], + BGP_CONFIG_VRF_TO_VRF_IMPORT); vpn_leak_postchange(dir, afi, bgp_get_default(), bgp); return CMD_SUCCESS; } -ALIAS(af_import_vrf_route_map, af_no_import_vrf_route_map_cmd, - "no import vrf route-map", +DEFPY(af_no_import_vrf_route_map, af_no_import_vrf_route_map_cmd, + "no import vrf route-map [RMAP$rmap_str]", NO_STR "Import routes from another VRF\n" "Vrf routes being filtered\n" - "Specify route map\n") + "Specify route map\n" + "name of route-map\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + vpn_policy_direction_t dir = BGP_VPN_POLICY_DIR_FROMVPN; + afi_t afi; + + afi = vpn_policy_getafi(vty, bgp, true); + if (afi == AFI_MAX) + return CMD_WARNING_CONFIG_FAILED; + + vpn_leak_prechange(dir, afi, bgp_get_default(), bgp); + + if (bgp->vpn_policy[afi].rmap_name[dir]) + XFREE(MTYPE_ROUTE_MAP_NAME, + bgp->vpn_policy[afi].rmap_name[dir]); + bgp->vpn_policy[afi].rmap_name[dir] = NULL; + bgp->vpn_policy[afi].rmap[dir] = NULL; + + if (bgp->vpn_policy[afi].import_vrf->count == 0) + UNSET_FLAG(bgp->af_flags[afi][SAFI_UNICAST], + BGP_CONFIG_VRF_TO_VRF_IMPORT); + + vpn_leak_postchange(dir, afi, bgp_get_default(), bgp); + + return CMD_SUCCESS; +} DEFPY(bgp_imexport_vrf, bgp_imexport_vrf_cmd, "[no] import vrf VIEWVRFNAME$import_name", @@ -6912,6 +8086,11 @@ DEFPY(bgp_imexport_vrf, bgp_imexport_vrf_cmd, return CMD_WARNING; } + if (strcmp(import_name, "route-map") == 0) { + vty_out(vty, "%% Must include route-map name\n"); + return CMD_WARNING; + } + if (argv_find(argv, argc, "no", &idx)) remove = true; @@ -6932,8 +8111,8 @@ DEFPY(bgp_imexport_vrf, bgp_imexport_vrf_cmd, bgp_default = bgp_get_default(); if (!bgp_default) { /* Auto-create assuming the same AS */ - ret = bgp_get(&bgp_default, &as, NULL, - BGP_INSTANCE_TYPE_DEFAULT); + ret = bgp_get_vty(&bgp_default, &as, NULL, + BGP_INSTANCE_TYPE_DEFAULT); if (ret) { vty_out(vty, @@ -6948,7 +8127,7 @@ DEFPY(bgp_imexport_vrf, bgp_imexport_vrf_cmd, vrf_bgp = bgp_default; else /* Auto-create assuming the same AS */ - ret = bgp_get(&vrf_bgp, &as, import_name, bgp_type); + ret = bgp_get_vty(&vrf_bgp, &as, import_name, bgp_type); if (ret) { vty_out(vty, @@ -6988,12 +8167,12 @@ DEFPY (bgp_imexport_vpn, afi_t afi; safi_t safi; int idx = 0; - int yes = 1; + bool yes = true; int flag; vpn_policy_direction_t dir; if (argv_find(argv, argc, "no", &idx)) - yes = 0; + yes = false; if (BGP_INSTANCE_TYPE_VRF != bgp->inst_type && BGP_INSTANCE_TYPE_DEFAULT != bgp->inst_type) { @@ -7041,34 +8220,44 @@ DEFPY (bgp_imexport_vpn, DEFPY (af_routetarget_import, af_routetarget_import_cmd, - "[no] redirect import RTLIST...", + "[no] redirect import RTLIST...", NO_STR "Specify route target list\n" "Specify route target list\n" + "Specify route target list\n" + "Specify route target list\n" "Flow-spec redirect type route target\n" "Import routes to this address-family\n" - "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n") + "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN|IPV6:MN)\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); int ret; struct ecommunity *ecom = NULL; afi_t afi; - int idx = 0; - int yes = 1; + int idx = 0, idx_unused = 0; + bool yes = true; + bool rt6 = false; if (argv_find(argv, argc, "no", &idx)) - yes = 0; + yes = false; + + if (argv_find(argv, argc, "rt6", &idx_unused) || + argv_find(argv, argc, "route-target6", &idx_unused)) + rt6 = true; afi = vpn_policy_getafi(vty, bgp, false); if (afi == AFI_MAX) return CMD_WARNING_CONFIG_FAILED; + if (rt6 && afi != AFI_IP6) + return CMD_WARNING_CONFIG_FAILED; + if (yes) { if (!argv_find(argv, argc, "RTLIST", &idx)) { vty_out(vty, "%% Missing RTLIST\n"); return CMD_WARNING_CONFIG_FAILED; } - ret = set_ecom_list(vty, argc - idx, argv + idx, &ecom); + ret = set_ecom_list(vty, argc - idx, argv + idx, &ecom, rt6); if (ret != CMD_SUCCESS) return ret; } @@ -7200,8 +8389,8 @@ static int bgp_clear_prefix(struct vty *vty, const char *view_name, { int ret; struct prefix match; - struct bgp_node *rn; - struct bgp_node *rm; + struct bgp_dest *dest; + struct bgp_dest *rm; struct bgp *bgp; struct bgp_table *table; struct bgp_table *rib; @@ -7233,32 +8422,38 @@ static int bgp_clear_prefix(struct vty *vty, const char *view_name, rib = bgp->rib[afi][safi]; if (safi == SAFI_MPLS_VPN) { - for (rn = bgp_table_top(rib); rn; rn = bgp_route_next(rn)) { - if (prd && memcmp(rn->p.u.val, prd->val, 8) != 0) + for (dest = bgp_table_top(rib); dest; + dest = bgp_route_next(dest)) { + const struct prefix *dest_p = bgp_dest_get_prefix(dest); + + if (prd && memcmp(dest_p->u.val, prd->val, 8) != 0) continue; - table = bgp_node_get_bgp_table_info(rn); - if (table != NULL) { + table = bgp_dest_get_bgp_table_info(dest); + if (table == NULL) + continue; - if ((rm = bgp_node_match(table, &match)) - != NULL) { - if (rm->p.prefixlen - == match.prefixlen) { - SET_FLAG(rm->flags, - BGP_NODE_USER_CLEAR); - bgp_process(bgp, rm, afi, safi); - } - bgp_unlock_node(rm); + if ((rm = bgp_node_match(table, &match)) != NULL) { + const struct prefix *rm_p = + bgp_dest_get_prefix(rm); + + if (rm_p->prefixlen == match.prefixlen) { + SET_FLAG(rm->flags, + BGP_NODE_USER_CLEAR); + bgp_process(bgp, rm, afi, safi); } + bgp_dest_unlock_node(rm); } } } else { - if ((rn = bgp_node_match(rib, &match)) != NULL) { - if (rn->p.prefixlen == match.prefixlen) { - SET_FLAG(rn->flags, BGP_NODE_USER_CLEAR); - bgp_process(bgp, rn, afi, safi); + if ((dest = bgp_node_match(rib, &match)) != NULL) { + const struct prefix *dest_p = bgp_dest_get_prefix(dest); + + if (dest_p->prefixlen == match.prefixlen) { + SET_FLAG(dest->flags, BGP_NODE_USER_CLEAR); + bgp_process(bgp, dest, afi, safi); } - bgp_unlock_node(rn); + bgp_dest_unlock_node(dest); } } @@ -7268,7 +8463,7 @@ static int bgp_clear_prefix(struct vty *vty, const char *view_name, /* one clear bgp command to rule them all */ DEFUN (clear_ip_bgp_all, clear_ip_bgp_all_cmd, - "clear [ip] bgp [ VIEWVRFNAME] [ []] <*|A.B.C.D|X:X::X:X|WORD|(1-4294967295)|external|peer-group WORD> []|in [prefix-filter]|out>]", + "clear [ip] bgp [ VIEWVRFNAME] [ []] <*|A.B.C.D$neighbor|X:X::X:X$neighbor|WORD$neighbor|(1-4294967295)|external|peer-group PGNAME> []|in [prefix-filter]|out>]", CLEAR_STR IP_STR BGP_STR @@ -7278,7 +8473,7 @@ DEFUN (clear_ip_bgp_all, BGP_SAFI_WITH_LABEL_HELP_STR "Address Family modifier\n" "Clear all peers\n" - "BGP neighbor address to clear\n" + "BGP IPv4 neighbor to clear\n" "BGP IPv6 neighbor to clear\n" "BGP neighbor on interface to clear\n" "Clear peers with the AS number\n" @@ -7294,8 +8489,8 @@ DEFUN (clear_ip_bgp_all, { char *vrf = NULL; - afi_t afi = AFI_IP6; - safi_t safi = SAFI_UNICAST; + afi_t afi = AFI_UNSPEC; + safi_t safi = SAFI_UNSPEC; enum clear_sort clr_sort = clear_peer; enum bgp_clear_type clr_type; char *clr_arg = NULL; @@ -7321,7 +8516,7 @@ DEFUN (clear_ip_bgp_all, if (argv_find_and_parse_afi(argv, argc, &idx, &afi)) argv_find_and_parse_safi(argv, argc, &idx, &safi); - /* <*|A.B.C.D|X:X::X:X|WORD|(1-4294967295)|external|peer-group WORD> */ + /* <*|A.B.C.D|X:X::X:X|WORD|(1-4294967295)|external|peer-group PGNAME> */ if (argv_find(argv, argc, "*", &idx)) { clr_sort = clear_all; } else if (argv_find(argv, argc, "A.B.C.D", &idx)) { @@ -7334,6 +8529,9 @@ DEFUN (clear_ip_bgp_all, clr_sort = clear_group; idx++; clr_arg = argv[idx]->arg; + } else if (argv_find(argv, argc, "PGNAME", &idx)) { + clr_sort = clear_peer; + clr_arg = argv[idx]->arg; } else if (argv_find(argv, argc, "WORD", &idx)) { clr_sort = clear_peer; clr_arg = argv[idx]->arg; @@ -7468,11 +8666,6 @@ DEFUN (show_bgp_views, struct listnode *node; struct bgp *bgp; - if (!bgp_option_check(BGP_OPT_MULTIPLE_INSTANCE)) { - vty_out(vty, "BGP Multiple Instance is not enabled\n"); - return CMD_WARNING; - } - vty_out(vty, "Defined BGP views:\n"); for (ALL_LIST_ELEMENTS_RO(inst, node, bgp)) { /* Skip VRFs. */ @@ -7503,11 +8696,6 @@ DEFUN (show_bgp_vrfs, json_object *json_vrfs = NULL; int count = 0; - if (!bgp_option_check(BGP_OPT_MULTIPLE_INSTANCE)) { - vty_out(vty, "BGP Multiple Instance is not enabled\n"); - return CMD_WARNING; - } - if (uj) { json = json_object_new_object(); json_vrfs = json_object_new_object(); @@ -7525,11 +8713,14 @@ DEFUN (show_bgp_vrfs, continue; count++; - if (!uj && count == 1) + if (!uj && count == 1) { vty_out(vty, - "%4s %-5s %-16s %9s %10s %-37s %-10s %-15s\n", - "Type", "Id", "routerId", "#PeersVfg", - "#PeersEstb", "Name", "L3-VNI", "Rmac"); + "%4s %-5s %-16s %9s %10s %-37s\n", + "Type", "Id", "routerId", "#PeersCfg", + "#PeersEstb", "Name"); + vty_out(vty, "%11s %-16s %-21s %-6s\n", " ", + "L3-VNI", "RouterMAC", "Interface"); + } peers_cfg = peers_estb = 0; if (uj) @@ -7570,16 +8761,24 @@ DEFUN (show_bgp_vrfs, json_object_string_add( json_vrf, "rmac", prefix_mac2str(&bgp->rmac, buf, sizeof(buf))); + json_object_string_add(json_vrf, "interface", + ifindex2ifname(bgp->l3vni_svi_ifindex, + bgp->vrf_id)); json_object_object_add(json_vrfs, name, json_vrf); - } else + } else { vty_out(vty, - "%4s %-5d %-16s %9u %10u %-37s %-10u %-15s\n", + "%4s %-5d %-16s %-9u %-10u %-37s\n", type, bgp->vrf_id == VRF_UNKNOWN ? -1 : (int)bgp->vrf_id, inet_ntoa(bgp->router_id), peers_cfg, - peers_estb, name, bgp->l3vni, - prefix_mac2str(&bgp->rmac, buf, sizeof(buf))); + peers_estb, name); + vty_out(vty,"%11s %-16u %-21s %-20s\n", " ", + bgp->l3vni, + prefix_mac2str(&bgp->rmac, buf, sizeof(buf)), + ifindex2ifname(bgp->l3vni_svi_ifindex, + bgp->vrf_id)); + } } if (uj) { @@ -7680,7 +8879,7 @@ DEFUN (show_bgp_memory, count = mtype_stats_alloc(MTYPE_BGP_NODE); vty_out(vty, "%ld RIB nodes, using %s of memory\n", count, mtype_memstr(memstrbuf, sizeof(memstrbuf), - count * sizeof(struct bgp_node))); + count * sizeof(struct bgp_dest))); count = mtype_stats_alloc(MTYPE_BGP_ROUTE); vty_out(vty, "%ld BGP routes, using %s of memory\n", count, @@ -7777,14 +8976,6 @@ DEFUN (show_bgp_memory, count * sizeof(struct peer_group))); /* Other */ - if ((count = mtype_stats_alloc(MTYPE_HASH))) - vty_out(vty, "%ld hash tables, using %s of memory\n", count, - mtype_memstr(memstrbuf, sizeof(memstrbuf), - count * sizeof(struct hash))); - if ((count = mtype_stats_alloc(MTYPE_HASH_BACKET))) - vty_out(vty, "%ld hash buckets, using %s of memory\n", count, - mtype_memstr(memstrbuf, sizeof(memstrbuf), - count * sizeof(struct hash_bucket))); if ((count = mtype_stats_alloc(MTYPE_BGP_REGEXP))) vty_out(vty, "%ld compiled regexes, using %s of memory\n", count, mtype_memstr(memstrbuf, sizeof(memstrbuf), @@ -7796,14 +8987,14 @@ static void bgp_show_bestpath_json(struct bgp *bgp, json_object *json) { json_object *bestpath = json_object_new_object(); - if (bgp_flag_check(bgp, BGP_FLAG_ASPATH_IGNORE)) + if (CHECK_FLAG(bgp->flags, BGP_FLAG_ASPATH_IGNORE)) json_object_string_add(bestpath, "asPath", "ignore"); - if (bgp_flag_check(bgp, BGP_FLAG_ASPATH_CONFED)) + if (CHECK_FLAG(bgp->flags, BGP_FLAG_ASPATH_CONFED)) json_object_string_add(bestpath, "asPath", "confed"); - if (bgp_flag_check(bgp, BGP_FLAG_ASPATH_MULTIPATH_RELAX)) { - if (bgp_flag_check(bgp, BGP_FLAG_MULTIPATH_RELAX_AS_SET)) + if (CHECK_FLAG(bgp->flags, BGP_FLAG_ASPATH_MULTIPATH_RELAX)) { + if (CHECK_FLAG(bgp->flags, BGP_FLAG_MULTIPATH_RELAX_AS_SET)) json_object_string_add(bestpath, "multiPathRelax", "as-set"); else @@ -7812,13 +9003,13 @@ static void bgp_show_bestpath_json(struct bgp *bgp, json_object *json) } else json_object_string_add(bestpath, "multiPathRelax", "false"); - if (bgp_flag_check(bgp, BGP_FLAG_COMPARE_ROUTER_ID)) + if (CHECK_FLAG(bgp->flags, BGP_FLAG_COMPARE_ROUTER_ID)) json_object_string_add(bestpath, "compareRouterId", "true"); - if (bgp_flag_check(bgp, BGP_FLAG_MED_CONFED) - || bgp_flag_check(bgp, BGP_FLAG_MED_MISSING_AS_WORST)) { - if (bgp_flag_check(bgp, BGP_FLAG_MED_CONFED)) + if (CHECK_FLAG(bgp->flags, BGP_FLAG_MED_CONFED) + || CHECK_FLAG(bgp->flags, BGP_FLAG_MED_MISSING_AS_WORST)) { + if (CHECK_FLAG(bgp->flags, BGP_FLAG_MED_CONFED)) json_object_string_add(bestpath, "med", "confed"); - if (bgp_flag_check(bgp, BGP_FLAG_MED_MISSING_AS_WORST)) + if (CHECK_FLAG(bgp->flags, BGP_FLAG_MED_MISSING_AS_WORST)) json_object_string_add(bestpath, "med", "missing-as-worst"); else @@ -7828,9 +9019,147 @@ static void bgp_show_bestpath_json(struct bgp *bgp, json_object *json) json_object_object_add(json, "bestPath", bestpath); } +/* Print the error code/subcode for why the peer is down */ +static void bgp_show_peer_reset(struct vty * vty, struct peer *peer, + json_object *json_peer, bool use_json) +{ + const char *code_str; + const char *subcode_str; + + if (use_json) { + if (peer->last_reset == PEER_DOWN_NOTIFY_SEND + || peer->last_reset == PEER_DOWN_NOTIFY_RECEIVED) { + char errorcodesubcode_hexstr[5]; + char errorcodesubcode_str[256]; + + code_str = bgp_notify_code_str(peer->notify.code); + subcode_str = bgp_notify_subcode_str( + peer->notify.code, + peer->notify.subcode); + + snprintf(errorcodesubcode_hexstr, + sizeof(errorcodesubcode_hexstr), "%02X%02X", + peer->notify.code, peer->notify.subcode); + json_object_string_add(json_peer, + "lastErrorCodeSubcode", + errorcodesubcode_hexstr); + snprintf(errorcodesubcode_str, 255, "%s%s", + code_str, subcode_str); + json_object_string_add(json_peer, + "lastNotificationReason", + errorcodesubcode_str); + if (peer->last_reset == PEER_DOWN_NOTIFY_RECEIVED + && peer->notify.code == BGP_NOTIFY_CEASE + && (peer->notify.subcode + == BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN + || peer->notify.subcode + == BGP_NOTIFY_CEASE_ADMIN_RESET) + && peer->notify.length) { + char msgbuf[1024]; + const char *msg_str; + + msg_str = bgp_notify_admin_message( + msgbuf, sizeof(msgbuf), + (uint8_t *)peer->notify.data, + peer->notify.length); + if (msg_str) + json_object_string_add( + json_peer, + "lastShutdownDescription", + msg_str); + } + + } + json_object_string_add(json_peer, "lastResetDueTo", + peer_down_str[(int)peer->last_reset]); + json_object_int_add(json_peer, "lastResetCode", + peer->last_reset); + } else { + if (peer->last_reset == PEER_DOWN_NOTIFY_SEND + || peer->last_reset == PEER_DOWN_NOTIFY_RECEIVED) { + code_str = bgp_notify_code_str(peer->notify.code); + subcode_str = + bgp_notify_subcode_str(peer->notify.code, + peer->notify.subcode); + vty_out(vty, " Notification %s (%s%s)\n", + peer->last_reset == PEER_DOWN_NOTIFY_SEND + ? "sent" + : "received", + code_str, subcode_str); + } else { + vty_out(vty, " %s\n", + peer_down_str[(int)peer->last_reset]); + } + } +} + +static inline bool bgp_has_peer_failed(struct peer *peer, afi_t afi, + safi_t safi) +{ + return ((peer->status != Established) || + !peer->afc_recv[afi][safi]); +} + +static void bgp_show_failed_summary(struct vty *vty, struct bgp *bgp, + struct peer *peer, json_object *json_peer, + int max_neighbor_width, bool use_json) +{ + char timebuf[BGP_UPTIME_LEN], dn_flag[2]; + int len; + + if (use_json) { + if (peer_dynamic_neighbor(peer)) + json_object_boolean_true_add(json_peer, + "dynamicPeer"); + if (peer->hostname) + json_object_string_add(json_peer, "hostname", + peer->hostname); + + if (peer->domainname) + json_object_string_add(json_peer, "domainname", + peer->domainname); + json_object_int_add(json_peer, "connectionsEstablished", + peer->established); + json_object_int_add(json_peer, "connectionsDropped", + peer->dropped); + peer_uptime(peer->uptime, timebuf, BGP_UPTIME_LEN, + use_json, json_peer); + if (peer->status == Established) + json_object_string_add(json_peer, "lastResetDueTo", + "AFI/SAFI Not Negotiated"); + else + bgp_show_peer_reset(NULL, peer, json_peer, true); + } else { + dn_flag[1] = '\0'; + dn_flag[0] = peer_dynamic_neighbor(peer) ? '*' : '\0'; + if (peer->hostname + && CHECK_FLAG(bgp->flags, BGP_FLAG_SHOW_HOSTNAME)) + len = vty_out(vty, "%s%s(%s)", dn_flag, + peer->hostname, peer->host); + else + len = vty_out(vty, "%s%s", dn_flag, peer->host); + + /* pad the neighbor column with spaces */ + if (len < max_neighbor_width) + vty_out(vty, "%*s", max_neighbor_width - len, + " "); + vty_out(vty, "%7d %7d %9s", peer->established, + peer->dropped, + peer_uptime(peer->uptime, timebuf, + BGP_UPTIME_LEN, 0, NULL)); + if (peer->status == Established) + vty_out(vty, " AFI/SAFI Not Negotiated\n"); + else + bgp_show_peer_reset(vty, peer, NULL, + false); + } +} + + /* Show BGP peer's summary information. */ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, - bool use_json, json_object *json) + bool show_failed, bool show_established, + bool use_json) { struct peer *peer; struct listnode *node, *nnode; @@ -7838,27 +9167,42 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, char timebuf[BGP_UPTIME_LEN], dn_flag[2]; char neighbor_buf[VTY_BUFSIZ]; int neighbor_col_default_width = 16; - int len; + int len, failed_count = 0; int max_neighbor_width = 0; int pfx_rcd_safi; + json_object *json = NULL; json_object *json_peer = NULL; json_object *json_peers = NULL; struct peer_af *paf; + struct bgp_filter *filter; /* labeled-unicast routes are installed in the unicast table so in order * to * display the correct PfxRcd value we must look at SAFI_UNICAST */ + if (safi == SAFI_LABELED_UNICAST) pfx_rcd_safi = SAFI_UNICAST; else pfx_rcd_safi = safi; if (use_json) { - if (json == NULL) - json = json_object_new_object(); - + json = json_object_new_object(); json_peers = json_object_new_object(); + for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { + if (!CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE)) + continue; + + if (peer->afc[afi][safi]) { + /* See if we have at least a single failed peer */ + if (bgp_has_peer_failed(peer, afi, safi)) + failed_count++; + count++; + } + if (peer_dynamic_neighbor(peer)) + dn_count++; + } + } else { /* Loop over all neighbors that will be displayed to determine * how many @@ -7874,19 +9218,26 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, dn_flag[0] = '*'; if (peer->hostname - && bgp_flag_check(bgp, - BGP_FLAG_SHOW_HOSTNAME)) - sprintf(neighbor_buf, "%s%s(%s) ", - dn_flag, peer->hostname, - peer->host); + && CHECK_FLAG(bgp->flags, + BGP_FLAG_SHOW_HOSTNAME)) + snprintf(neighbor_buf, + sizeof(neighbor_buf), + "%s%s(%s) ", dn_flag, + peer->hostname, peer->host); else - sprintf(neighbor_buf, "%s%s ", dn_flag, - peer->host); + snprintf(neighbor_buf, + sizeof(neighbor_buf), "%s%s ", + dn_flag, peer->host); len = strlen(neighbor_buf); if (len > max_neighbor_width) max_neighbor_width = len; + + /* See if we have at least a single failed peer */ + if (bgp_has_peer_failed(peer, afi, safi)) + failed_count++; + count++; } } @@ -7897,6 +9248,23 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, max_neighbor_width = neighbor_col_default_width; } + if (show_failed && !failed_count) { + if (use_json) { + json_object_int_add(json, "failedPeersCount", 0); + json_object_int_add(json, "dynamicPeers", dn_count); + json_object_int_add(json, "totalPeers", count); + + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } else { + vty_out(vty, "%% No failed BGP neighbors found\n"); + vty_out(vty, "\nTotal number of neighbors %d\n", count); + } + return CMD_SUCCESS; + } + + count = 0; /* Reset the value as its used again */ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { if (!CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE)) continue; @@ -8029,7 +9397,7 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, json_object_int_add(json, "ribCount", ents); json_object_int_add( json, "ribMemory", - ents * sizeof(struct bgp_node)); + ents * sizeof(struct bgp_dest)); ents = bgp->af_peer_count[afi][safi]; json_object_int_add(json, "peerCount", ents); @@ -8065,10 +9433,11 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, vty_out(vty, "RIB entries %ld, using %s of memory\n", ents, - mtype_memstr(memstrbuf, - sizeof(memstrbuf), - ents * sizeof(struct - bgp_node))); + mtype_memstr( + memstrbuf, sizeof(memstrbuf), + ents + * sizeof(struct + bgp_dest))); /* Peer related usage */ ents = bgp->af_peer_count[afi][safi]; @@ -8098,78 +9467,107 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, vty_out(vty, "Neighbor"); vty_out(vty, "%*s", max_neighbor_width - 8, " "); - vty_out(vty, - "V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd\n"); + if (show_failed) + vty_out(vty, "EstdCnt DropCnt ResetTime Reason\n"); + else + vty_out(vty, + "V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt\n"); } } + paf = peer_af_find(peer, afi, safi); + filter = &peer->filter[afi][safi]; + count++; + /* Works for both failed & successful cases */ + if (peer_dynamic_neighbor(peer)) + dn_count++; if (use_json) { - json_peer = json_object_new_object(); + json_peer = NULL; + + if (show_failed && + bgp_has_peer_failed(peer, afi, safi)) { + json_peer = json_object_new_object(); + bgp_show_failed_summary(vty, bgp, peer, + json_peer, 0, use_json); + } else if (!show_failed) { + if (show_established + && bgp_has_peer_failed(peer, afi, safi)) + continue; + + json_peer = json_object_new_object(); + if (peer_dynamic_neighbor(peer)) { + json_object_boolean_true_add(json_peer, + "dynamicPeer"); + } - if (peer_dynamic_neighbor(peer)) { - dn_count++; - json_object_boolean_true_add(json_peer, - "dynamicPeer"); + if (peer->hostname) + json_object_string_add(json_peer, "hostname", + peer->hostname); + + if (peer->domainname) + json_object_string_add(json_peer, "domainname", + peer->domainname); + + json_object_int_add(json_peer, "remoteAs", peer->as); + json_object_int_add(json_peer, "version", 4); + json_object_int_add(json_peer, "msgRcvd", + PEER_TOTAL_RX(peer)); + json_object_int_add(json_peer, "msgSent", + PEER_TOTAL_TX(peer)); + + atomic_size_t outq_count, inq_count; + outq_count = atomic_load_explicit( + &peer->obuf->count, + memory_order_relaxed); + inq_count = atomic_load_explicit( + &peer->ibuf->count, + memory_order_relaxed); + + json_object_int_add(json_peer, "tableVersion", + peer->version[afi][safi]); + json_object_int_add(json_peer, "outq", + outq_count); + json_object_int_add(json_peer, "inq", + inq_count); + peer_uptime(peer->uptime, timebuf, BGP_UPTIME_LEN, + use_json, json_peer); + + json_object_int_add(json_peer, "pfxRcd", + peer->pcount[afi][pfx_rcd_safi]); + + if (paf && PAF_SUBGRP(paf)) + json_object_int_add(json_peer, + "pfxSnt", + (PAF_SUBGRP(paf))->scount); + if (CHECK_FLAG(peer->flags, PEER_FLAG_SHUTDOWN) + || CHECK_FLAG(peer->bgp->flags, + BGP_FLAG_SHUTDOWN)) + json_object_string_add(json_peer, "state", + "Idle (Admin)"); + else if (peer->afc_recv[afi][safi]) + json_object_string_add( + json_peer, "state", + lookup_msg(bgp_status_msg, peer->status, + NULL)); + else if (CHECK_FLAG(peer->sflags, + PEER_STATUS_PREFIX_OVERFLOW)) + json_object_string_add(json_peer, "state", + "Idle (PfxCt)"); + else + json_object_string_add( + json_peer, "state", + lookup_msg(bgp_status_msg, peer->status, + NULL)); + json_object_int_add(json_peer, "connectionsEstablished", + peer->established); + json_object_int_add(json_peer, "connectionsDropped", + peer->dropped); } - - if (peer->hostname) - json_object_string_add(json_peer, "hostname", - peer->hostname); - - if (peer->domainname) - json_object_string_add(json_peer, "domainname", - peer->domainname); - - json_object_int_add(json_peer, "remoteAs", peer->as); - json_object_int_add(json_peer, "version", 4); - json_object_int_add(json_peer, "msgRcvd", - PEER_TOTAL_RX(peer)); - json_object_int_add(json_peer, "msgSent", - PEER_TOTAL_TX(peer)); - - json_object_int_add(json_peer, "tableVersion", - peer->version[afi][safi]); - json_object_int_add(json_peer, "outq", - peer->obuf->count); - json_object_int_add(json_peer, "inq", 0); - peer_uptime(peer->uptime, timebuf, BGP_UPTIME_LEN, - use_json, json_peer); - - /* - * Adding "pfxRcd" field to match with the corresponding - * CLI. "prefixReceivedCount" will be deprecated in - * future. - */ - json_object_int_add(json_peer, "prefixReceivedCount", - peer->pcount[afi][pfx_rcd_safi]); - json_object_int_add(json_peer, "pfxRcd", - peer->pcount[afi][pfx_rcd_safi]); - - paf = peer_af_find(peer, afi, pfx_rcd_safi); - if (paf && PAF_SUBGRP(paf)) - json_object_int_add(json_peer, - "pfxSnt", - (PAF_SUBGRP(paf))->scount); - - if (CHECK_FLAG(peer->flags, PEER_FLAG_SHUTDOWN)) - json_object_string_add(json_peer, "state", - "Idle (Admin)"); - else if (peer->afc_recv[afi][safi]) - json_object_string_add( - json_peer, "state", - lookup_msg(bgp_status_msg, peer->status, - NULL)); - else if (CHECK_FLAG(peer->sflags, - PEER_STATUS_PREFIX_OVERFLOW)) - json_object_string_add(json_peer, "state", - "Idle (PfxCt)"); - else - json_object_string_add( - json_peer, "state", - lookup_msg(bgp_status_msg, peer->status, - NULL)); + /* Avoid creating empty peer dicts in JSON */ + if (json_peer == NULL) + continue; if (peer->conf_if) json_object_string_add(json_peer, "idType", @@ -8180,65 +9578,120 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, else if (peer->su.sa.sa_family == AF_INET6) json_object_string_add(json_peer, "idType", "ipv6"); - json_object_object_add(json_peers, peer->host, json_peer); } else { - memset(dn_flag, '\0', sizeof(dn_flag)); - if (peer_dynamic_neighbor(peer)) { - dn_count++; - dn_flag[0] = '*'; - } + if (show_failed && + bgp_has_peer_failed(peer, afi, safi)) { + bgp_show_failed_summary(vty, bgp, peer, NULL, + max_neighbor_width, + use_json); + } else if (!show_failed) { + if (show_established + && bgp_has_peer_failed(peer, afi, safi)) + continue; - if (peer->hostname - && bgp_flag_check(bgp, BGP_FLAG_SHOW_HOSTNAME)) - len = vty_out(vty, "%s%s(%s)", dn_flag, - peer->hostname, peer->host); - else - len = vty_out(vty, "%s%s", dn_flag, peer->host); + memset(dn_flag, '\0', sizeof(dn_flag)); + if (peer_dynamic_neighbor(peer)) { + dn_flag[0] = '*'; + } - /* pad the neighbor column with spaces */ - if (len < max_neighbor_width) - vty_out(vty, "%*s", max_neighbor_width - len, - " "); + if (peer->hostname + && CHECK_FLAG(bgp->flags, + BGP_FLAG_SHOW_HOSTNAME)) + len = vty_out(vty, "%s%s(%s)", dn_flag, + peer->hostname, + peer->host); + else + len = vty_out(vty, "%s%s", dn_flag, peer->host); - vty_out(vty, "4 %10u %7u %7u %8" PRIu64 " %4d %4zd %8s", - peer->as, PEER_TOTAL_RX(peer), - PEER_TOTAL_TX(peer), peer->version[afi][safi], - 0, peer->obuf->count, - peer_uptime(peer->uptime, timebuf, - BGP_UPTIME_LEN, 0, NULL)); + /* pad the neighbor column with spaces */ + if (len < max_neighbor_width) + vty_out(vty, "%*s", max_neighbor_width - len, + " "); - if (peer->status == Established) - if (peer->afc_recv[afi][safi]) - vty_out(vty, " %12ld", - peer->pcount[afi] - [pfx_rcd_safi]); - else - vty_out(vty, " NoNeg"); - else { - if (CHECK_FLAG(peer->flags, PEER_FLAG_SHUTDOWN)) - vty_out(vty, " Idle (Admin)"); - else if (CHECK_FLAG( - peer->sflags, - PEER_STATUS_PREFIX_OVERFLOW)) - vty_out(vty, " Idle (PfxCt)"); - else - vty_out(vty, " %12s", - lookup_msg(bgp_status_msg, - peer->status, NULL)); + atomic_size_t outq_count, inq_count; + outq_count = atomic_load_explicit( + &peer->obuf->count, + memory_order_relaxed); + inq_count = atomic_load_explicit( + &peer->ibuf->count, + memory_order_relaxed); + + vty_out(vty, + "4 %10u %9u %9u %8" PRIu64" %4zu %4zu %8s", + peer->as, PEER_TOTAL_RX(peer), + PEER_TOTAL_TX(peer), + peer->version[afi][safi], inq_count, + outq_count, + peer_uptime(peer->uptime, timebuf, + BGP_UPTIME_LEN, 0, NULL)); + + if (peer->status == Established) { + if (peer->afc_recv[afi][safi]) { + if (CHECK_FLAG( + bgp->flags, + BGP_FLAG_EBGP_REQUIRES_POLICY) + && !bgp_inbound_policy_exists( + peer, filter)) + vty_out(vty, " %12s", + "(Policy)"); + else + vty_out(vty, + " %12u", + peer->pcount + [afi] + [pfx_rcd_safi]); + } else { + vty_out(vty, " NoNeg"); + } + + if (paf && PAF_SUBGRP(paf)) { + if (CHECK_FLAG( + bgp->flags, + BGP_FLAG_EBGP_REQUIRES_POLICY) + && !bgp_outbound_policy_exists( + peer, filter)) + vty_out(vty, " %8s", + "(Policy)"); + else + vty_out(vty, + " %8u", + (PAF_SUBGRP( + paf)) + ->scount); + } + } else { + if (CHECK_FLAG(peer->flags, + PEER_FLAG_SHUTDOWN) + || CHECK_FLAG(peer->bgp->flags, + BGP_FLAG_SHUTDOWN)) + vty_out(vty, " Idle (Admin)"); + else if (CHECK_FLAG( + peer->sflags, + PEER_STATUS_PREFIX_OVERFLOW)) + vty_out(vty, " Idle (PfxCt)"); + else + vty_out(vty, " %12s", + lookup_msg(bgp_status_msg, + peer->status, NULL)); + + vty_out(vty, " %8u", 0); + } + vty_out(vty, "\n"); } - vty_out(vty, "\n"); + } } if (use_json) { json_object_object_add(json, "peers", json_peers); - + json_object_int_add(json, "failedPeers", failed_count); json_object_int_add(json, "totalPeers", count); json_object_int_add(json, "dynamicPeers", dn_count); - bgp_show_bestpath_json(bgp, json); + if (!show_failed) + bgp_show_bestpath_json(bgp, json); vty_out(vty, "%s\n", json_object_to_json_string_ext( json, JSON_C_TO_STRING_PRETTY)); @@ -8248,7 +9701,7 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, vty_out(vty, "\nTotal number of neighbors %d\n", count); else { vty_out(vty, "No %s neighbor is configured\n", - afi_safi_print(afi, safi)); + get_afi_safi_str(afi, safi, false)); } if (dn_count) { @@ -8262,8 +9715,8 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, } static void bgp_show_summary_afi_safi(struct vty *vty, struct bgp *bgp, int afi, - int safi, bool use_json, - json_object *json) + int safi, bool show_failed, + bool show_established, bool use_json) { int is_first = 1; int afi_wildcard = (afi == AFI_MAX); @@ -8281,6 +9734,7 @@ static void bgp_show_summary_afi_safi(struct vty *vty, struct bgp *bgp, int afi, while (safi < SAFI_MAX) { if (bgp_afi_safi_peer_exists(bgp, afi, safi)) { nbr_output = true; + if (is_wildcard) { /* * So limit output to those afi/safi @@ -8289,24 +9743,25 @@ static void bgp_show_summary_afi_safi(struct vty *vty, struct bgp *bgp, int afi, * them */ if (use_json) { - json = json_object_new_object(); - if (!is_first) vty_out(vty, ",\n"); else is_first = 0; vty_out(vty, "\"%s\":", - afi_safi_json(afi, - safi)); + get_afi_safi_str(afi, + safi, + true)); } else { vty_out(vty, "\n%s Summary:\n", - afi_safi_print(afi, - safi)); + get_afi_safi_str(afi, + safi, + false)); } } - bgp_show_summary(vty, bgp, afi, safi, use_json, - json); + bgp_show_summary(vty, bgp, afi, safi, + show_failed, show_established, + use_json); } safi++; if (!safi_wildcard) @@ -8328,11 +9783,12 @@ static void bgp_show_summary_afi_safi(struct vty *vty, struct bgp *bgp, int afi, } static void bgp_show_all_instances_summary_vty(struct vty *vty, afi_t afi, - safi_t safi, bool use_json) + safi_t safi, bool show_failed, + bool show_established, + bool use_json) { struct listnode *node, *nnode; struct bgp *bgp; - json_object *json = NULL; int is_first = 1; bool nbr_output = false; @@ -8342,8 +9798,6 @@ static void bgp_show_all_instances_summary_vty(struct vty *vty, afi_t afi, for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) { nbr_output = true; if (use_json) { - json = json_object_new_object(); - if (!is_first) vty_out(vty, ",\n"); else @@ -8359,7 +9813,8 @@ static void bgp_show_all_instances_summary_vty(struct vty *vty, afi_t afi, ? VRF_DEFAULT_NAME : bgp->name); } - bgp_show_summary_afi_safi(vty, bgp, afi, safi, use_json, json); + bgp_show_summary_afi_safi(vty, bgp, afi, safi, show_failed, + show_established, use_json); } if (use_json) @@ -8369,14 +9824,16 @@ static void bgp_show_all_instances_summary_vty(struct vty *vty, afi_t afi, } int bgp_show_summary_vty(struct vty *vty, const char *name, afi_t afi, - safi_t safi, bool use_json) + safi_t safi, bool show_failed, bool show_established, + bool use_json) { struct bgp *bgp; if (name) { if (strmatch(name, "all")) { - bgp_show_all_instances_summary_vty(vty, afi, safi, - use_json); + bgp_show_all_instances_summary_vty( + vty, afi, safi, show_failed, show_established, + use_json); return CMD_SUCCESS; } else { bgp = bgp_lookup_by_name(name); @@ -8390,8 +9847,9 @@ int bgp_show_summary_vty(struct vty *vty, const char *name, afi_t afi, return CMD_WARNING; } - bgp_show_summary_afi_safi(vty, bgp, afi, safi, use_json, - NULL); + bgp_show_summary_afi_safi(vty, bgp, afi, safi, + show_failed, show_established, + use_json); return CMD_SUCCESS; } } @@ -8399,7 +9857,8 @@ int bgp_show_summary_vty(struct vty *vty, const char *name, afi_t afi, bgp = bgp_get_default(); if (bgp) - bgp_show_summary_afi_safi(vty, bgp, afi, safi, use_json, NULL); + bgp_show_summary_afi_safi(vty, bgp, afi, safi, show_failed, + show_established, use_json); else { if (use_json) vty_out(vty, "{}\n"); @@ -8414,7 +9873,7 @@ int bgp_show_summary_vty(struct vty *vty, const char *name, afi_t afi, /* `show [ip] bgp summary' commands. */ DEFUN (show_ip_bgp_summary, show_ip_bgp_summary_cmd, - "show [ip] bgp [ VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] summary [json]", + "show [ip] bgp [ VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] summary [established|failed] [json]", SHOW_STR IP_STR BGP_STR @@ -8422,11 +9881,15 @@ DEFUN (show_ip_bgp_summary, BGP_AFI_HELP_STR BGP_SAFI_WITH_LABEL_HELP_STR "Summary of BGP neighbor status\n" + "Show only sessions in Established state\n" + "Show only sessions not in Established state\n" JSON_STR) { char *vrf = NULL; afi_t afi = AFI_MAX; safi_t safi = SAFI_MAX; + bool show_failed = false; + bool show_established = false; int idx = 0; @@ -8446,83 +9909,25 @@ DEFUN (show_ip_bgp_summary, argv_find_and_parse_safi(argv, argc, &idx, &safi); } + if (argv_find(argv, argc, "failed", &idx)) + show_failed = true; + if (argv_find(argv, argc, "established", &idx)) + show_established = true; + bool uj = use_json(argc, argv); - return bgp_show_summary_vty(vty, vrf, afi, safi, uj); + return bgp_show_summary_vty(vty, vrf, afi, safi, show_failed, + show_established, uj); } -const char *afi_safi_print(afi_t afi, safi_t safi) +const char *get_afi_safi_str(afi_t afi, safi_t safi, bool for_json) { - if (afi == AFI_IP && safi == SAFI_UNICAST) - return "IPv4 Unicast"; - else if (afi == AFI_IP && safi == SAFI_MULTICAST) - return "IPv4 Multicast"; - else if (afi == AFI_IP && safi == SAFI_LABELED_UNICAST) - return "IPv4 Labeled Unicast"; - else if (afi == AFI_IP && safi == SAFI_MPLS_VPN) - return "IPv4 VPN"; - else if (afi == AFI_IP && safi == SAFI_ENCAP) - return "IPv4 Encap"; - else if (afi == AFI_IP && safi == SAFI_FLOWSPEC) - return "IPv4 Flowspec"; - else if (afi == AFI_IP6 && safi == SAFI_UNICAST) - return "IPv6 Unicast"; - else if (afi == AFI_IP6 && safi == SAFI_MULTICAST) - return "IPv6 Multicast"; - else if (afi == AFI_IP6 && safi == SAFI_LABELED_UNICAST) - return "IPv6 Labeled Unicast"; - else if (afi == AFI_IP6 && safi == SAFI_MPLS_VPN) - return "IPv6 VPN"; - else if (afi == AFI_IP6 && safi == SAFI_ENCAP) - return "IPv6 Encap"; - else if (afi == AFI_IP6 && safi == SAFI_FLOWSPEC) - return "IPv6 Flowspec"; - else if (afi == AFI_L2VPN && safi == SAFI_EVPN) - return "L2VPN EVPN"; + if (for_json) + return get_afi_safi_json_str(afi, safi); else - return "Unknown"; + return get_afi_safi_vty_str(afi, safi); } -/* - * Please note that we have intentionally camelCased - * the return strings here. So if you want - * to use this function, please ensure you - * are doing this within json output - */ -const char *afi_safi_json(afi_t afi, safi_t safi) -{ - if (afi == AFI_IP && safi == SAFI_UNICAST) - return "ipv4Unicast"; - else if (afi == AFI_IP && safi == SAFI_MULTICAST) - return "ipv4Multicast"; - else if (afi == AFI_IP && safi == SAFI_LABELED_UNICAST) - return "ipv4LabeledUnicast"; - else if (afi == AFI_IP && safi == SAFI_MPLS_VPN) - return "ipv4Vpn"; - else if (afi == AFI_IP && safi == SAFI_ENCAP) - return "ipv4Encap"; - else if (afi == AFI_IP && safi == SAFI_FLOWSPEC) - return "ipv4Flowspec"; - else if (afi == AFI_IP6 && safi == SAFI_UNICAST) - return "ipv6Unicast"; - else if (afi == AFI_IP6 && safi == SAFI_MULTICAST) - return "ipv6Multicast"; - else if (afi == AFI_IP6 && safi == SAFI_LABELED_UNICAST) - return "ipv6LabeledUnicast"; - else if (afi == AFI_IP6 && safi == SAFI_MPLS_VPN) - return "ipv6Vpn"; - else if (afi == AFI_IP6 && safi == SAFI_ENCAP) - return "ipv6Encap"; - else if (afi == AFI_IP6 && safi == SAFI_FLOWSPEC) - return "ipv6Flowspec"; - else if (afi == AFI_L2VPN && safi == SAFI_EVPN) - return "l2VpnEvpn"; - else - return "Unknown"; -} - -/* Show BGP peer's information. */ -enum show_type { show_all, show_peer, show_ipv4_all, show_ipv6_all, show_ipv4_peer, show_ipv6_peer }; static void bgp_show_peer_afi_orf_cap(struct vty *vty, struct peer *p, afi_t afi, safi_t safi, @@ -8587,6 +9992,382 @@ static void bgp_show_peer_afi_orf_cap(struct vty *vty, struct peer *p, } } +static void bgp_show_neighnor_graceful_restart_rbit(struct vty *vty, + struct peer *p, + bool use_json, + json_object *json) +{ + bool rbit_status = false; + + if (!use_json) + vty_out(vty, "\n R bit: "); + + if (CHECK_FLAG(p->cap, PEER_CAP_RESTART_ADV) + && (CHECK_FLAG(p->cap, PEER_CAP_RESTART_RCV)) + && (p->status == Established)) { + + if (CHECK_FLAG(p->cap, PEER_CAP_RESTART_BIT_RCV)) + rbit_status = true; + else + rbit_status = false; + } + + if (rbit_status) { + if (use_json) + json_object_boolean_true_add(json, "rBit"); + else + vty_out(vty, "True\n"); + } else { + if (use_json) + json_object_boolean_false_add(json, "rBit"); + else + vty_out(vty, "False\n"); + } +} + +static void bgp_show_neighbor_graceful_restart_remote_mode(struct vty *vty, + struct peer *peer, + bool use_json, + json_object *json) +{ + const char *mode = "NotApplicable"; + + if (!use_json) + vty_out(vty, "\n Remote GR Mode: "); + + if (CHECK_FLAG(peer->cap, PEER_CAP_RESTART_ADV) + && (peer->status == Established)) { + + if ((peer->nsf_af_count == 0) + && !CHECK_FLAG(peer->cap, PEER_CAP_RESTART_RCV)) { + + mode = "Disable"; + + } else if (peer->nsf_af_count == 0 + && CHECK_FLAG(peer->cap, PEER_CAP_RESTART_RCV)) { + + mode = "Helper"; + + } else if (peer->nsf_af_count != 0 + && CHECK_FLAG(peer->cap, PEER_CAP_RESTART_RCV)) { + + mode = "Restart"; + } + } + + if (use_json) { + json_object_string_add(json, "remoteGrMode", mode); + } else + vty_out(vty, mode, "\n"); +} + +static void bgp_show_neighbor_graceful_restart_local_mode(struct vty *vty, + struct peer *p, + bool use_json, + json_object *json) +{ + const char *mode = "Invalid"; + + if (!use_json) + vty_out(vty, " Local GR Mode: "); + + if (bgp_peer_gr_mode_get(p) == PEER_HELPER) + mode = "Helper"; + else if (bgp_peer_gr_mode_get(p) == PEER_GR) + mode = "Restart"; + else if (bgp_peer_gr_mode_get(p) == PEER_DISABLE) + mode = "Disable"; + else if (bgp_peer_gr_mode_get(p) == PEER_GLOBAL_INHERIT) { + if (bgp_global_gr_mode_get(p->bgp) == GLOBAL_HELPER) + mode = "Helper*"; + else if (bgp_global_gr_mode_get(p->bgp) == GLOBAL_GR) + mode = "Restart*"; + else if (bgp_global_gr_mode_get(p->bgp) == GLOBAL_DISABLE) + mode = "Disable*"; + else + mode = "Invalid*"; + } + + if (use_json) { + json_object_string_add(json, "localGrMode", mode); + } else { + vty_out(vty, mode, "\n"); + } +} + +static void bgp_show_neighbor_graceful_restart_capability_per_afi_safi( + struct vty *vty, struct peer *peer, bool use_json, json_object *json) +{ + afi_t afi; + safi_t safi; + json_object *json_afi_safi = NULL; + json_object *json_timer = NULL; + json_object *json_endofrib_status = NULL; + bool eor_flag = false; + + for (afi = AFI_IP; afi < AFI_MAX; afi++) { + for (safi = SAFI_UNICAST; safi <= SAFI_MPLS_VPN; safi++) { + if (!peer->afc[afi][safi]) + continue; + + if (!CHECK_FLAG(peer->cap, PEER_CAP_RESTART_ADV) + || !CHECK_FLAG(peer->cap, PEER_CAP_RESTART_RCV)) + continue; + + if (use_json) { + json_afi_safi = json_object_new_object(); + json_endofrib_status = json_object_new_object(); + json_timer = json_object_new_object(); + } + + if (peer->eor_stime[afi][safi] + >= peer->pkt_stime[afi][safi]) + eor_flag = true; + else + eor_flag = false; + + if (!use_json) { + vty_out(vty, " %s:\n", + get_afi_safi_str(afi, safi, false)); + + vty_out(vty, " F bit: "); + } + + if (peer->nsf[afi][safi] + && CHECK_FLAG(peer->af_cap[afi][safi], + PEER_CAP_RESTART_AF_PRESERVE_RCV)) { + + if (use_json) { + json_object_boolean_true_add( + json_afi_safi, "fBit"); + } else + vty_out(vty, "True\n"); + } else { + if (use_json) + json_object_boolean_false_add( + json_afi_safi, "fBit"); + else + vty_out(vty, "False\n"); + } + + if (!use_json) + vty_out(vty, " End-of-RIB sent: "); + + if (CHECK_FLAG(peer->af_sflags[afi][safi], + PEER_STATUS_EOR_SEND)) { + if (use_json) { + json_object_boolean_true_add( + json_endofrib_status, + "endOfRibSend"); + + PRINT_EOR_JSON(eor_flag); + } else { + vty_out(vty, "Yes\n"); + vty_out(vty, + " End-of-RIB sent after update: "); + + PRINT_EOR(eor_flag); + } + } else { + if (use_json) { + json_object_boolean_false_add( + json_endofrib_status, + "endOfRibSend"); + json_object_boolean_false_add( + json_endofrib_status, + "endOfRibSentAfterUpdate"); + } else { + vty_out(vty, "No\n"); + vty_out(vty, + " End-of-RIB sent after update: "); + vty_out(vty, "No\n"); + } + } + + if (!use_json) + vty_out(vty, " End-of-RIB received: "); + + if (CHECK_FLAG(peer->af_sflags[afi][safi], + PEER_STATUS_EOR_RECEIVED)) { + if (use_json) + json_object_boolean_true_add( + json_endofrib_status, + "endOfRibRecv"); + else + vty_out(vty, "Yes\n"); + } else { + if (use_json) + json_object_boolean_false_add( + json_endofrib_status, + "endOfRibRecv"); + else + vty_out(vty, "No\n"); + } + + if (use_json) { + json_object_int_add(json_timer, + "stalePathTimer", + peer->bgp->stalepath_time); + + if (peer->t_gr_stale != NULL) { + json_object_int_add( + json_timer, + "stalePathTimerRemaining", + thread_timer_remain_second( + peer->t_gr_stale)); + } + + /* Display Configured Selection + * Deferral only when when + * Gr mode is enabled. + */ + if (CHECK_FLAG(peer->flags, + PEER_FLAG_GRACEFUL_RESTART)) { + json_object_int_add( + json_timer, + "selectionDeferralTimer", + peer->bgp->stalepath_time); + } + + if (peer->bgp->gr_info[afi][safi] + .t_select_deferral + != NULL) { + + json_object_int_add( + json_timer, + "selectionDeferralTimerRemaining", + thread_timer_remain_second( + peer->bgp + ->gr_info[afi] + [safi] + .t_select_deferral)); + } + } else { + vty_out(vty, " Timers:\n"); + vty_out(vty, + " Configured Stale Path Time(sec): %u\n", + peer->bgp->stalepath_time); + + if (peer->t_gr_stale != NULL) + vty_out(vty, + " Stale Path Remaining(sec): %ld\n", + thread_timer_remain_second( + peer->t_gr_stale)); + /* Display Configured Selection + * Deferral only when when + * Gr mode is enabled. + */ + if (CHECK_FLAG(peer->flags, + PEER_FLAG_GRACEFUL_RESTART)) + vty_out(vty, + " Configured Selection Deferral Time(sec): %u\n", + peer->bgp->select_defer_time); + + if (peer->bgp->gr_info[afi][safi] + .t_select_deferral + != NULL) + vty_out(vty, + " Selection Deferral Time Remaining(sec): %ld\n", + thread_timer_remain_second( + peer->bgp + ->gr_info[afi] + [safi] + .t_select_deferral)); + } + if (use_json) { + json_object_object_add(json_afi_safi, + "endOfRibStatus", + json_endofrib_status); + json_object_object_add(json_afi_safi, "timers", + json_timer); + json_object_object_add( + json, get_afi_safi_str(afi, safi, true), + json_afi_safi); + } + } + } +} + +static void bgp_show_neighbor_graceful_restart_time(struct vty *vty, + struct peer *p, + bool use_json, + json_object *json) +{ + if (use_json) { + json_object *json_timer = NULL; + + json_timer = json_object_new_object(); + + json_object_int_add(json_timer, "configuredRestartTimer", + p->bgp->restart_time); + + json_object_int_add(json_timer, "receivedRestartTimer", + p->v_gr_restart); + + if (p->t_gr_restart != NULL) + json_object_int_add( + json_timer, "restartTimerRemaining", + thread_timer_remain_second(p->t_gr_restart)); + + json_object_object_add(json, "timers", json_timer); + } else { + + vty_out(vty, " Timers:\n"); + vty_out(vty, " Configured Restart Time(sec): %u\n", + p->bgp->restart_time); + + vty_out(vty, " Received Restart Time(sec): %u\n", + p->v_gr_restart); + if (p->t_gr_restart != NULL) + vty_out(vty, " Restart Time Remaining(sec): %ld\n", + thread_timer_remain_second(p->t_gr_restart)); + if (p->t_gr_restart != NULL) { + vty_out(vty, " Restart Time Remaining(sec): %ld\n", + thread_timer_remain_second(p->t_gr_restart)); + } + } +} + +static void bgp_show_peer_gr_status(struct vty *vty, struct peer *p, + bool use_json, json_object *json) +{ + char buf[SU_ADDRSTRLEN] = {0}; + char dn_flag[2] = {0}; + /* '*' + v6 address of neighbor */ + char neighborAddr[INET6_ADDRSTRLEN + 1] = {0}; + + if (!p->conf_if && peer_dynamic_neighbor(p)) + dn_flag[0] = '*'; + + if (p->conf_if) { + if (use_json) + json_object_string_add( + json, "neighborAddr", + BGP_PEER_SU_UNSPEC(p) + ? "none" + : sockunion2str(&p->su, buf, + SU_ADDRSTRLEN)); + else + vty_out(vty, "BGP neighbor on %s: %s\n", p->conf_if, + BGP_PEER_SU_UNSPEC(p) + ? "none" + : sockunion2str(&p->su, buf, + SU_ADDRSTRLEN)); + } else { + snprintf(neighborAddr, sizeof(neighborAddr), "%s%s", dn_flag, + p->host); + + if (use_json) + json_object_string_add(json, "neighborAddr", + neighborAddr); + else + vty_out(vty, "BGP neighbor is %s\n", neighborAddr); + } + + /* more gr info in new format */ + BGP_SHOW_PEER_GR_CAPABILITY(vty, p, use_json, json); +} + static void bgp_show_peer_afi(struct vty *vty, struct peer *p, afi_t afi, safi_t safi, bool use_json, json_object *json_neigh) @@ -8675,7 +10456,8 @@ static void bgp_show_peer_afi(struct vty *vty, struct peer *p, afi_t afi, else json_object_free(json_af); - sprintf(orf_pfx_name, "%s.%d.%d", p->host, afi, safi); + snprintf(orf_pfx_name, sizeof(orf_pfx_name), "%s.%d.%d", + p->host, afi, safi); orf_pfx_count = prefix_bgp_show_prefix_list( NULL, afi, orf_pfx_name, use_json); @@ -8853,14 +10635,14 @@ static void bgp_show_peer_afi(struct vty *vty, struct peer *p, afi_t afi, filter->map[RMAP_OUT].name); /* ebgp-requires-policy (inbound) */ - if (p->bgp->ebgp_requires_policy == DEFAULT_EBGP_POLICY_ENABLED + if (CHECK_FLAG(p->bgp->flags, BGP_FLAG_EBGP_REQUIRES_POLICY) && !bgp_inbound_policy_exists(p, filter)) json_object_string_add( json_addr, "inboundEbgpRequiresPolicy", "Inbound updates discarded due to missing policy"); /* ebgp-requires-policy (outbound) */ - if (p->bgp->ebgp_requires_policy == DEFAULT_EBGP_POLICY_ENABLED + if (CHECK_FLAG(p->bgp->flags, BGP_FLAG_EBGP_REQUIRES_POLICY) && (!bgp_outbound_policy_exists(p, filter))) json_object_string_add( json_addr, "outboundEbgpRequiresPolicy", @@ -8879,6 +10661,11 @@ static void bgp_show_peer_afi(struct vty *vty, struct peer *p, afi_t afi, json_object_int_add(json_addr, "sentPrefixCounter", (PAF_SUBGRP(paf))->scount); + /* Maximum prefix */ + if (CHECK_FLAG(p->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_OUT)) + json_object_int_add(json_addr, "prefixOutAllowedMax", + p->pmax_out[afi][safi]); + /* Maximum prefix */ if (CHECK_FLAG(p->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX)) { json_object_int_add(json_addr, "prefixAllowedMax", @@ -8896,14 +10683,15 @@ static void bgp_show_peer_afi(struct vty *vty, struct peer *p, afi_t afi, "prefixAllowedRestartIntervalMsecs", p->pmax_restart[afi][safi] * 60000); } - json_object_object_add(json_neigh, afi_safi_print(afi, safi), + json_object_object_add(json_neigh, + get_afi_safi_str(afi, safi, true), json_addr); } else { filter = &p->filter[afi][safi]; vty_out(vty, " For address family: %s\n", - afi_safi_print(afi, safi)); + get_afi_safi_str(afi, safi, false)); if (peer_group_active(p)) vty_out(vty, " %s peer-group member\n", @@ -8911,8 +10699,7 @@ static void bgp_show_peer_afi(struct vty *vty, struct peer *p, afi_t afi, paf = peer_af_find(p, afi, safi); if (paf && PAF_SUBGRP(paf)) { - vty_out(vty, " Update group %" PRIu64 - ", subgroup %" PRIu64 "\n", + vty_out(vty, " Update group %" PRIu64", subgroup %" PRIu64 "\n", PAF_UPDGRP(paf)->id, PAF_SUBGRP(paf)->id); vty_out(vty, " Packet Queue length %d\n", bpacket_queue_virtual_length(paf)); @@ -8965,7 +10752,8 @@ static void bgp_show_peer_afi(struct vty *vty, struct peer *p, afi_t afi, PEER_CAP_ORF_PREFIX_RM_OLD_RCV, use_json, NULL); } - sprintf(orf_pfx_name, "%s.%d.%d", p->host, afi, safi); + snprintf(orf_pfx_name, sizeof(orf_pfx_name), "%s.%d.%d", + p->host, afi, safi); orf_pfx_count = prefix_bgp_show_prefix_list( NULL, afi, orf_pfx_name, use_json); @@ -9143,13 +10931,13 @@ static void bgp_show_peer_afi(struct vty *vty, struct peer *p, afi_t afi, filter->map[RMAP_OUT].name); /* ebgp-requires-policy (inbound) */ - if (p->bgp->ebgp_requires_policy == DEFAULT_EBGP_POLICY_ENABLED + if (CHECK_FLAG(p->bgp->flags, BGP_FLAG_EBGP_REQUIRES_POLICY) && !bgp_inbound_policy_exists(p, filter)) vty_out(vty, " Inbound updates discarded due to missing policy\n"); /* ebgp-requires-policy (outbound) */ - if (p->bgp->ebgp_requires_policy == DEFAULT_EBGP_POLICY_ENABLED + if (CHECK_FLAG(p->bgp->flags, BGP_FLAG_EBGP_REQUIRES_POLICY) && !bgp_outbound_policy_exists(p, filter)) vty_out(vty, " Outbound updates discarded due to missing policy\n"); @@ -9162,11 +10950,20 @@ static void bgp_show_peer_afi(struct vty *vty, struct peer *p, afi_t afi, filter->usmap.name); /* Receive prefix count */ - vty_out(vty, " %ld accepted prefixes\n", p->pcount[afi][safi]); + vty_out(vty, " %u accepted prefixes\n", + p->pcount[afi][safi]); + + /* maximum-prefix-out */ + if (CHECK_FLAG(p->af_flags[afi][safi], + PEER_FLAG_MAX_PREFIX_OUT)) + vty_out(vty, + " Maximum allowed prefixes sent %u\n", + p->pmax_out[afi][safi]); /* Maximum prefix */ if (CHECK_FLAG(p->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX)) { - vty_out(vty, " Maximum prefixes allowed %ld%s\n", + vty_out(vty, + " Maximum prefixes allowed %u%s\n", p->pmax[afi][safi], CHECK_FLAG(p->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING) @@ -9191,8 +10988,6 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, char buf1[PREFIX2STR_BUFFER], buf[SU_ADDRSTRLEN]; char timebuf[BGP_UPTIME_LEN]; char dn_flag[2]; - const char *subcode_str; - const char *code_str; afi_t afi; safi_t safi; uint16_t i; @@ -9357,10 +11152,9 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, p->group, &prefix); if (range) { - prefix2str(range, buf1, sizeof(buf1)); vty_out(vty, - " Belongs to the subnet range group: %s\n", - buf1); + " Belongs to the subnet range group: %pFX\n", + range); } } } @@ -9368,7 +11162,8 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, if (use_json) { /* Administrative shutdown. */ - if (CHECK_FLAG(p->flags, PEER_FLAG_SHUTDOWN)) + if (CHECK_FLAG(p->flags, PEER_FLAG_SHUTDOWN) + || CHECK_FLAG(p->bgp->flags, BGP_FLAG_SHUTDOWN)) json_object_boolean_true_add(json_neigh, "adminShutDown"); @@ -9400,23 +11195,6 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, uptime -= p->uptime; epoch_tbuf = time(NULL) - uptime; -#if CONFDATE > 20200101 - CPP_NOTICE( - "bgpTimerUp should be deprecated and can be removed now"); -#endif - /* - * bgpTimerUp was miliseconds that was accurate - * up to 1 day, then the value returned - * became garbage. So in order to provide - * some level of backwards compatability, - * we still provde the data, but now - * we are returning the correct value - * and also adding a new bgpTimerUpMsec - * which will allow us to deprecate - * this eventually - */ - json_object_int_add(json_neigh, "bgpTimerUp", - uptime * 1000); json_object_int_add(json_neigh, "bgpTimerUpMsec", uptime * 1000); json_object_string_add(json_neigh, "bgpTimerUpString", @@ -9439,28 +11217,31 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, /* read timer */ time_t uptime; - struct tm *tm; + struct tm tm; uptime = bgp_clock(); uptime -= p->readtime; - tm = gmtime(&uptime); + gmtime_r(&uptime, &tm); + json_object_int_add(json_neigh, "bgpTimerLastRead", - (tm->tm_sec * 1000) + (tm->tm_min * 60000) - + (tm->tm_hour * 3600000)); + (tm.tm_sec * 1000) + (tm.tm_min * 60000) + + (tm.tm_hour * 3600000)); uptime = bgp_clock(); uptime -= p->last_write; - tm = gmtime(&uptime); + gmtime_r(&uptime, &tm); + json_object_int_add(json_neigh, "bgpTimerLastWrite", - (tm->tm_sec * 1000) + (tm->tm_min * 60000) - + (tm->tm_hour * 3600000)); + (tm.tm_sec * 1000) + (tm.tm_min * 60000) + + (tm.tm_hour * 3600000)); uptime = bgp_clock(); uptime -= p->update_time; - tm = gmtime(&uptime); + gmtime_r(&uptime, &tm); + json_object_int_add(json_neigh, "bgpInUpdateElapsedTimeMsecs", - (tm->tm_sec * 1000) + (tm->tm_min * 60000) - + (tm->tm_hour * 3600000)); + (tm.tm_sec * 1000) + (tm.tm_min * 60000) + + (tm.tm_hour * 3600000)); /* Configured timer values. */ json_object_int_add(json_neigh, "bgpTimerHoldTimeMsecs", @@ -9476,9 +11257,8 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, json_neigh, "bgpTimerConfiguredKeepAliveIntervalMsecs", p->keepalive * 1000); - } else if ((bgp->default_holdtime != BGP_DEFAULT_HOLDTIME) - || (bgp->default_keepalive - != BGP_DEFAULT_KEEPALIVE)) { + } else if ((bgp->default_holdtime != SAVE_BGP_HOLDTIME) + || (bgp->default_keepalive != SAVE_BGP_KEEPALIVE)) { json_object_int_add(json_neigh, "bgpTimerConfiguredHoldTimeMsecs", bgp->default_holdtime); @@ -9489,7 +11269,8 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, } } else { /* Administrative shutdown. */ - if (CHECK_FLAG(p->flags, PEER_FLAG_SHUTDOWN)) + if (CHECK_FLAG(p->flags, PEER_FLAG_SHUTDOWN) + || CHECK_FLAG(p->bgp->flags, BGP_FLAG_SHUTDOWN)) vty_out(vty, " Administratively shut down\n"); /* BGP Version. */ @@ -9540,9 +11321,8 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, p->holdtime); vty_out(vty, ", keepalive interval is %d seconds\n", p->keepalive); - } else if ((bgp->default_holdtime != BGP_DEFAULT_HOLDTIME) - || (bgp->default_keepalive - != BGP_DEFAULT_KEEPALIVE)) { + } else if ((bgp->default_holdtime != SAVE_BGP_HOLDTIME) + || (bgp->default_keepalive != SAVE_BGP_KEEPALIVE)) { vty_out(vty, " Configured hold time is %d", bgp->default_holdtime); vty_out(vty, ", keepalive interval is %d seconds\n", @@ -9610,8 +11390,8 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, json_object *json_sub = NULL; json_sub = json_object_new_object(); - print_store = afi_safi_print( - afi, safi); + print_store = get_afi_safi_str( + afi, safi, true); if (CHECK_FLAG( p->af_cap[afi] @@ -9789,9 +11569,9 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, [AFI_IP] [safi], PEER_CAP_ENHE_AF_RCV)) { - print_store = afi_safi_print( + print_store = get_afi_safi_str( AFI_IP, - safi); + safi, true); json_object_string_add( json_nxt, print_store, @@ -9891,8 +11671,9 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, json_object_object_add( json_multi, - afi_safi_print(afi, - safi), + get_afi_safi_str(afi, + safi, + true), json_exten); } } @@ -9998,9 +11779,10 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, restart_af_count++; json_object_object_add( json_restart, - afi_safi_print( + get_afi_safi_str( afi, - safi), + safi, + true), json_sub); } } @@ -10059,9 +11841,10 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, PEER_CAP_ADDPATH_AF_TX_RCV)) { vty_out(vty, " %s: TX ", - afi_safi_print( + get_afi_safi_str( afi, - safi)); + safi, + false)); if (CHECK_FLAG( p->af_cap @@ -10070,9 +11853,10 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, PEER_CAP_ADDPATH_AF_TX_ADV)) vty_out(vty, "advertised %s", - afi_safi_print( + get_afi_safi_str( afi, - safi)); + safi, + false)); if (CHECK_FLAG( p->af_cap @@ -10102,9 +11886,10 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, PEER_CAP_ADDPATH_AF_RX_RCV)) { vty_out(vty, " %s: RX ", - afi_safi_print( + get_afi_safi_str( afi, - safi)); + safi, + false)); if (CHECK_FLAG( p->af_cap @@ -10113,9 +11898,10 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, PEER_CAP_ADDPATH_AF_RX_ADV)) vty_out(vty, "advertised %s", - afi_safi_print( + get_afi_safi_str( afi, - safi)); + safi, + false)); if (CHECK_FLAG( p->af_cap @@ -10186,9 +11972,10 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, PEER_CAP_ENHE_AF_RCV)) vty_out(vty, " %s\n", - afi_safi_print( + get_afi_safi_str( AFI_IP, - safi)); + safi, + false)); } } @@ -10235,8 +12022,10 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, || p->afc_recv[afi][safi]) { vty_out(vty, " Address Family %s:", - afi_safi_print(afi, - safi)); + get_afi_safi_str( + afi, + safi, + false)); if (p->afc_adv[afi][safi]) vty_out(vty, " advertised"); @@ -10281,12 +12070,12 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, vty_out(vty, "\n"); - /* Gracefull Restart */ + /* Graceful Restart */ if (CHECK_FLAG(p->cap, PEER_CAP_RESTART_RCV) || CHECK_FLAG(p->cap, PEER_CAP_RESTART_ADV)) { vty_out(vty, - " Graceful Restart Capabilty:"); + " Graceful Restart Capability:"); if (CHECK_FLAG(p->cap, PEER_CAP_RESTART_ADV)) vty_out(vty, " advertised"); @@ -10321,9 +12110,10 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, restart_af_count ? ", " : "", - afi_safi_print( + get_afi_safi_str( afi, - safi), + safi, + false), CHECK_FLAG( p->af_cap [afi] @@ -10337,14 +12127,12 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, vty_out(vty, "none"); vty_out(vty, "\n"); } - } + } /* Gracefull Restart */ } } } /* graceful restart information */ - if (CHECK_FLAG(p->cap, PEER_CAP_RESTART_RCV) || p->t_gr_restart - || p->t_gr_stale) { json_object *json_grace = NULL; json_object *json_grace_send = NULL; json_object *json_grace_recv = NULL; @@ -10356,14 +12144,16 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, json_grace_send = json_object_new_object(); json_grace_recv = json_object_new_object(); - if (p->status == Established) { + if ((p->status == Established) + && CHECK_FLAG(p->cap, PEER_CAP_RESTART_RCV)) { FOREACH_AFI_SAFI (afi, safi) { if (CHECK_FLAG(p->af_sflags[afi][safi], PEER_STATUS_EOR_SEND)) { json_object_boolean_true_add( json_grace_send, - afi_safi_print(afi, - safi)); + get_afi_safi_str(afi, + safi, + true)); eor_send_af_count++; } } @@ -10373,18 +12163,19 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, PEER_STATUS_EOR_RECEIVED)) { json_object_boolean_true_add( json_grace_recv, - afi_safi_print(afi, - safi)); + get_afi_safi_str(afi, + safi, + true)); eor_receive_af_count++; } } } - json_object_object_add(json_grace, "endOfRibSend", json_grace_send); json_object_object_add(json_grace, "endOfRibRecv", json_grace_recv); + if (p->t_gr_restart) json_object_int_add(json_grace, "gracefulRestartTimerMsecs", @@ -10399,12 +12190,16 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, thread_timer_remain_second( p->t_gr_stale) * 1000); - + /* more gr info in new format */ + BGP_SHOW_PEER_GR_CAPABILITY(vty, p, use_json, + json_grace); json_object_object_add( json_neigh, "gracefulRestartInfo", json_grace); } else { vty_out(vty, " Graceful restart information:\n"); - if (p->status == Established) { + if ((p->status == Established) + && CHECK_FLAG(p->cap, PEER_CAP_RESTART_RCV)) { + vty_out(vty, " End-of-RIB send: "); FOREACH_AFI_SAFI (afi, safi) { if (CHECK_FLAG(p->af_sflags[afi][safi], @@ -10412,8 +12207,9 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, vty_out(vty, "%s%s", eor_send_af_count ? ", " : "", - afi_safi_print(afi, - safi)); + get_afi_safi_str( + afi, safi, + false)); eor_send_af_count++; } } @@ -10427,8 +12223,9 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, eor_receive_af_count ? ", " : "", - afi_safi_print(afi, - safi)); + get_afi_safi_str(afi, + safi, + false)); eor_receive_af_count++; } } @@ -10446,15 +12243,26 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, " The remaining time of stalepath timer is %ld\n", thread_timer_remain_second( p->t_gr_stale)); + + /* more gr info in new format */ + BGP_SHOW_PEER_GR_CAPABILITY(vty, p, use_json, NULL); } - } + if (use_json) { json_object *json_stat = NULL; json_stat = json_object_new_object(); /* Packet counts. */ - json_object_int_add(json_stat, "depthInq", 0); + + atomic_size_t outq_count, inq_count; + outq_count = atomic_load_explicit(&p->obuf->count, + memory_order_relaxed); + inq_count = atomic_load_explicit(&p->ibuf->count, + memory_order_relaxed); + + json_object_int_add(json_stat, "depthInq", + (unsigned long)inq_count); json_object_int_add(json_stat, "depthOutq", - (unsigned long)p->obuf->count); + (unsigned long)outq_count); json_object_int_add(json_stat, "opensSent", atomic_load_explicit(&p->open_out, memory_order_relaxed)); @@ -10495,11 +12303,16 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, json_object_int_add(json_stat, "totalRecv", PEER_TOTAL_RX(p)); json_object_object_add(json_neigh, "messageStats", json_stat); } else { + atomic_size_t outq_count, inq_count; + outq_count = atomic_load_explicit(&p->obuf->count, + memory_order_relaxed); + inq_count = atomic_load_explicit(&p->ibuf->count, + memory_order_relaxed); + /* Packet counts. */ vty_out(vty, " Message statistics:\n"); - vty_out(vty, " Inq depth is 0\n"); - vty_out(vty, " Outq depth is %lu\n", - (unsigned long)p->obuf->count); + vty_out(vty, " Inq depth is %zu\n", inq_count); + vty_out(vty, " Outq depth is %zu\n", outq_count); vty_out(vty, " Sent Rcvd\n"); vty_out(vty, " Opens: %10d %10d\n", atomic_load_explicit(&p->open_out, @@ -10605,97 +12418,23 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, } else { if (use_json) { time_t uptime; - struct tm *tm; + struct tm tm; uptime = bgp_clock(); uptime -= p->resettime; - tm = gmtime(&uptime); + gmtime_r(&uptime, &tm); + json_object_int_add(json_neigh, "lastResetTimerMsecs", - (tm->tm_sec * 1000) - + (tm->tm_min * 60000) - + (tm->tm_hour * 3600000)); - json_object_string_add( - json_neigh, "lastResetDueTo", - peer_down_str[(int)p->last_reset]); - if (p->last_reset == PEER_DOWN_NOTIFY_SEND - || p->last_reset == PEER_DOWN_NOTIFY_RECEIVED) { - char errorcodesubcode_hexstr[5]; - char errorcodesubcode_str[256]; - - code_str = bgp_notify_code_str(p->notify.code); - subcode_str = bgp_notify_subcode_str( - p->notify.code, p->notify.subcode); - - sprintf(errorcodesubcode_hexstr, "%02X%02X", - p->notify.code, p->notify.subcode); - json_object_string_add(json_neigh, - "lastErrorCodeSubcode", - errorcodesubcode_hexstr); - snprintf(errorcodesubcode_str, 255, "%s%s", - code_str, subcode_str); - json_object_string_add(json_neigh, - "lastNotificationReason", - errorcodesubcode_str); - if (p->last_reset == PEER_DOWN_NOTIFY_RECEIVED - && p->notify.code == BGP_NOTIFY_CEASE - && (p->notify.subcode - == BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN - || p->notify.subcode - == BGP_NOTIFY_CEASE_ADMIN_RESET) - && p->notify.length) { - char msgbuf[1024]; - const char *msg_str; - - msg_str = bgp_notify_admin_message( - msgbuf, sizeof(msgbuf), - (uint8_t *)p->notify.data, - p->notify.length); - if (msg_str) - json_object_string_add( - json_neigh, - "lastShutdownDescription", - msg_str); - } - } + (tm.tm_sec * 1000) + + (tm.tm_min * 60000) + + (tm.tm_hour * 3600000)); + bgp_show_peer_reset(NULL, p, json_neigh, true); } else { vty_out(vty, " Last reset %s, ", peer_uptime(p->resettime, timebuf, BGP_UPTIME_LEN, 0, NULL)); - if (p->last_reset == PEER_DOWN_NOTIFY_SEND - || p->last_reset == PEER_DOWN_NOTIFY_RECEIVED) { - code_str = bgp_notify_code_str(p->notify.code); - subcode_str = bgp_notify_subcode_str( - p->notify.code, p->notify.subcode); - vty_out(vty, "due to NOTIFICATION %s (%s%s)\n", - p->last_reset == PEER_DOWN_NOTIFY_SEND - ? "sent" - : "received", - code_str, subcode_str); - if (p->last_reset == PEER_DOWN_NOTIFY_RECEIVED - && p->notify.code == BGP_NOTIFY_CEASE - && (p->notify.subcode - == BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN - || p->notify.subcode - == BGP_NOTIFY_CEASE_ADMIN_RESET) - && p->notify.length) { - char msgbuf[1024]; - const char *msg_str; - - msg_str = bgp_notify_admin_message( - msgbuf, sizeof(msgbuf), - (uint8_t *)p->notify.data, - p->notify.length); - if (msg_str) - vty_out(vty, - " Message: \"%s\"\n", - msg_str); - } - } else { - vty_out(vty, "due to %s\n", - peer_down_str[(int)p->last_reset]); - } - + bgp_show_peer_reset(vty, p, NULL, false); if (p->last_reset_cause_size) { msg = p->last_reset_cause; vty_out(vty, @@ -10754,26 +12493,26 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, /* EBGP Multihop and GTSM */ if (p->sort != BGP_PEER_IBGP) { if (use_json) { - if (p->gtsm_hops > 0) + if (p->gtsm_hops > BGP_GTSM_HOPS_DISABLED) json_object_int_add(json_neigh, "externalBgpNbrMaxHopsAway", p->gtsm_hops); - else if (p->ttl > 1) + else if (p->ttl > BGP_DEFAULT_TTL) json_object_int_add(json_neigh, "externalBgpNbrMaxHopsAway", p->ttl); } else { - if (p->gtsm_hops > 0) + if (p->gtsm_hops > BGP_GTSM_HOPS_DISABLED) vty_out(vty, " External BGP neighbor may be up to %d hops away.\n", p->gtsm_hops); - else if (p->ttl > 1) + else if (p->ttl > BGP_DEFAULT_TTL) vty_out(vty, " External BGP neighbor may be up to %d hops away.\n", p->ttl); } } else { - if (p->gtsm_hops > 0) { + if (p->gtsm_hops > BGP_GTSM_HOPS_DISABLED) { if (use_json) json_object_int_add(json_neigh, "internalBgpNbrMaxHopsAway", @@ -10911,11 +12650,11 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, if (p->password) vty_out(vty, "Peer Authentication Enabled\n"); - vty_out(vty, "Read thread: %s Write thread: %s\n", + vty_out(vty, "Read thread: %s Write thread: %s FD used: %d\n", p->t_read ? "on" : "off", CHECK_FLAG(p->thread_flags, PEER_THREAD_WRITES_ON) ? "on" - : "off"); + : "off", p->fd); } if (p->notify.code == BGP_NOTIFY_OPEN_ERR @@ -10936,41 +12675,128 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, } } -static int bgp_show_neighbor(struct vty *vty, struct bgp *bgp, - enum show_type type, union sockunion *su, - const char *conf_if, bool use_json, - json_object *json) +static int bgp_show_neighbor_graceful_restart(struct vty *vty, struct bgp *bgp, + enum show_type type, + union sockunion *su, + const char *conf_if, afi_t afi, + bool use_json) { struct listnode *node, *nnode; struct peer *peer; int find = 0; - bool nbr_output = false; - afi_t afi = AFI_MAX; - safi_t safi = SAFI_MAX; + safi_t safi = SAFI_UNICAST; + json_object *json = NULL; + json_object *json_neighbor = NULL; - if (type == show_ipv4_peer || type == show_ipv4_all) { - afi = AFI_IP; - } else if (type == show_ipv6_peer || type == show_ipv6_all) { - afi = AFI_IP6; + if (use_json) { + json = json_object_new_object(); + json_neighbor = json_object_new_object(); } for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { + if (!CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE)) continue; - switch (type) { - case show_all: - bgp_show_peer(vty, peer, use_json, json); - nbr_output = true; - break; - case show_peer: + if ((peer->afc[afi][safi]) == 0) + continue; + + if (type == show_all) { + bgp_show_peer_gr_status(vty, peer, use_json, + json_neighbor); + + if (use_json) { + json_object_object_add(json, peer->host, + json_neighbor); + json_neighbor = NULL; + } + + } else if (type == show_peer) { if (conf_if) { if ((peer->conf_if && !strcmp(peer->conf_if, conf_if)) || (peer->hostname && !strcmp(peer->hostname, conf_if))) { find = 1; - bgp_show_peer(vty, peer, use_json, + bgp_show_peer_gr_status(vty, peer, + use_json, + json_neighbor); + } + } else { + if (sockunion_same(&peer->su, su)) { + find = 1; + bgp_show_peer_gr_status(vty, peer, + use_json, + json_neighbor); + } + } + if (use_json && find) + json_object_object_add(json, peer->host, + json_neighbor); + } + + if (find) { + json_neighbor = NULL; + break; + } + } + + if (type == show_peer && !find) { + if (use_json) + json_object_boolean_true_add(json, "bgpNoSuchNeighbor"); + else + vty_out(vty, "%% No such neighbor\n"); + } + if (use_json) { + vty_out(vty, "%s\n", + json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + + if (json_neighbor) + json_object_free(json_neighbor); + json_object_free(json); + } else { + vty_out(vty, "\n"); + } + + return CMD_SUCCESS; +} + +static int bgp_show_neighbor(struct vty *vty, struct bgp *bgp, + enum show_type type, union sockunion *su, + const char *conf_if, bool use_json, + json_object *json) +{ + struct listnode *node, *nnode; + struct peer *peer; + int find = 0; + bool nbr_output = false; + afi_t afi = AFI_MAX; + safi_t safi = SAFI_MAX; + + if (type == show_ipv4_peer || type == show_ipv4_all) { + afi = AFI_IP; + } else if (type == show_ipv6_peer || type == show_ipv6_all) { + afi = AFI_IP6; + } + + for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { + if (!CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE)) + continue; + + switch (type) { + case show_all: + bgp_show_peer(vty, peer, use_json, json); + nbr_output = true; + break; + case show_peer: + if (conf_if) { + if ((peer->conf_if + && !strcmp(peer->conf_if, conf_if)) + || (peer->hostname + && !strcmp(peer->hostname, conf_if))) { + find = 1; + bgp_show_peer(vty, peer, use_json, json); } } else { @@ -11041,6 +12867,38 @@ static int bgp_show_neighbor(struct vty *vty, struct bgp *bgp, return CMD_SUCCESS; } +static void bgp_show_neighbor_graceful_restart_vty(struct vty *vty, + enum show_type type, + const char *ip_str, + afi_t afi, bool use_json) +{ + + int ret; + struct bgp *bgp; + union sockunion su; + + bgp = bgp_get_default(); + + if (!bgp) + return; + + if (!use_json) + bgp_show_global_graceful_restart_mode_vty(vty, bgp, use_json, + NULL); + + if (ip_str) { + ret = str2sockunion(ip_str, &su); + if (ret < 0) + bgp_show_neighbor_graceful_restart( + vty, bgp, type, NULL, ip_str, afi, use_json); + else + bgp_show_neighbor_graceful_restart(vty, bgp, type, &su, + NULL, afi, use_json); + } else + bgp_show_neighbor_graceful_restart(vty, bgp, type, NULL, NULL, + afi, use_json); +} + static void bgp_show_all_instances_neighbors_vty(struct vty *vty, enum show_type type, const char *ip_str, @@ -11108,12 +12966,11 @@ static void bgp_show_all_instances_neighbors_vty(struct vty *vty, use_json, json); } json_object_free(json); + json = NULL; } - if (use_json) { + if (use_json) vty_out(vty, "}\n"); - json_object_free(json); - } else if (!nbr_output) vty_out(vty, "%% BGP instance not found\n"); } @@ -11178,6 +13035,50 @@ static int bgp_show_neighbor_vty(struct vty *vty, const char *name, return CMD_SUCCESS; } + + +/* "show [ip] bgp neighbors graceful-restart" commands. */ +DEFUN (show_ip_bgp_neighbors_gracrful_restart, + show_ip_bgp_neighbors_graceful_restart_cmd, + "show bgp [] neighbors [] graceful-restart [json]", + SHOW_STR + BGP_STR + IP_STR + IPV6_STR + NEIGHBOR_STR + "Neighbor to display information about\n" + "Neighbor to display information about\n" + "Neighbor on BGP configured interface\n" + GR_SHOW + JSON_STR) +{ + char *sh_arg = NULL; + enum show_type sh_type; + int idx = 0; + afi_t afi = AFI_MAX; + bool uj = use_json(argc, argv); + + if (!argv_find_and_parse_afi(argv, argc, &idx, &afi)) + afi = AFI_MAX; + + idx++; + + if (argv_find(argv, argc, "A.B.C.D", &idx) + || argv_find(argv, argc, "X:X::X:X", &idx) + || argv_find(argv, argc, "WORD", &idx)) { + sh_type = show_peer; + sh_arg = argv[idx]->arg; + } else + sh_type = show_all; + + if (!argv_find(argv, argc, "graceful-restart", &idx)) + return CMD_SUCCESS; + + + return bgp_show_neighbor_graceful_restart_afi_all(vty, sh_type, sh_arg, + afi, uj); +} + /* "show [ip] bgp neighbors" commands. */ DEFUN (show_ip_bgp_neighbors, show_ip_bgp_neighbors_cmd, @@ -11316,7 +13217,65 @@ DEFUN (show_ip_bgp_lcommunity_info, return CMD_SUCCESS; } +/* Graceful Restart */ + +static void bgp_show_global_graceful_restart_mode_vty(struct vty *vty, + struct bgp *bgp, + bool use_json, + json_object *json) +{ + + + vty_out(vty, "\n%s", SHOW_GR_HEADER); + + enum global_mode bgp_global_gr_mode = bgp_global_gr_mode_get(bgp); + + switch (bgp_global_gr_mode) { + + case GLOBAL_HELPER: + vty_out(vty, "Global BGP GR Mode : Helper\n"); + break; + + case GLOBAL_GR: + vty_out(vty, "Global BGP GR Mode : Restart\n"); + break; + + case GLOBAL_DISABLE: + vty_out(vty, "Global BGP GR Mode : Disable\n"); + break; + + case GLOBAL_INVALID: + vty_out(vty, + "Global BGP GR Mode Invalid\n"); + break; + } + vty_out(vty, "\n"); +} +static int bgp_show_neighbor_graceful_restart_afi_all(struct vty *vty, + enum show_type type, + const char *ip_str, + afi_t afi, bool use_json) +{ + if ((afi == AFI_MAX) && (ip_str == NULL)) { + afi = AFI_IP; + + while ((afi != AFI_L2VPN) && (afi < AFI_MAX)) { + + bgp_show_neighbor_graceful_restart_vty( + vty, type, ip_str, afi, use_json); + afi++; + } + } else if (afi != AFI_MAX) { + bgp_show_neighbor_graceful_restart_vty(vty, type, ip_str, afi, + use_json); + } else { + return CMD_ERR_INCOMPLETE; + } + + return CMD_SUCCESS; +} +/* Graceful Restart */ DEFUN (show_ip_bgp_attr_info, show_ip_bgp_attr_info_cmd, @@ -11360,7 +13319,7 @@ static int bgp_show_route_leak_vty(struct vty *vty, const char *name, /* Provide context for the block */ json_object_string_add(json, "vrf", name ? name : "default"); json_object_string_add(json, "afiSafi", - afi_safi_print(afi, safi)); + get_afi_safi_str(afi, safi, true)); if (!CHECK_FLAG(bgp->af_flags[afi][safi], BGP_CONFIG_VRF_TO_VRF_IMPORT)) { @@ -11375,15 +13334,19 @@ static int bgp_show_route_leak_vty(struct vty *vty, const char *name, json_object_array_add(json_import_vrfs, json_object_new_string(vname)); + json_object_object_add(json, "importFromVrfs", + json_import_vrfs); dir = BGP_VPN_POLICY_DIR_FROMVPN; - ecom_str = ecommunity_ecom2str( + if (bgp->vpn_policy[afi].rtlist[dir]) { + ecom_str = ecommunity_ecom2str( bgp->vpn_policy[afi].rtlist[dir], ECOMMUNITY_FORMAT_ROUTE_MAP, 0); - json_object_object_add(json, "importFromVrfs", - json_import_vrfs); - json_object_string_add(json, "importRts", ecom_str); - - XFREE(MTYPE_ECOMMUNITY_STR, ecom_str); + json_object_string_add(json, "importRts", + ecom_str); + XFREE(MTYPE_ECOMMUNITY_STR, ecom_str); + } else + json_object_string_add(json, "importRts", + "none"); } if (!CHECK_FLAG(bgp->af_flags[afi][safi], @@ -11407,12 +13370,16 @@ static int bgp_show_route_leak_vty(struct vty *vty, const char *name, buf1, RD_ADDRSTRLEN)); dir = BGP_VPN_POLICY_DIR_TOVPN; - ecom_str = ecommunity_ecom2str( + if (bgp->vpn_policy[afi].rtlist[dir]) { + ecom_str = ecommunity_ecom2str( bgp->vpn_policy[afi].rtlist[dir], ECOMMUNITY_FORMAT_ROUTE_MAP, 0); - json_object_string_add(json, "exportRts", ecom_str); - - XFREE(MTYPE_ECOMMUNITY_STR, ecom_str); + json_object_string_add(json, "exportRts", + ecom_str); + XFREE(MTYPE_ECOMMUNITY_STR, ecom_str); + } else + json_object_string_add(json, "exportRts", + "none"); } if (use_json) { @@ -11433,11 +13400,11 @@ static int bgp_show_route_leak_vty(struct vty *vty, const char *name, BGP_CONFIG_VRF_TO_VRF_IMPORT)) vty_out(vty, "This VRF is not importing %s routes from any other VRF\n", - afi_safi_print(afi, safi)); + get_afi_safi_str(afi, safi, false)); else { vty_out(vty, "This VRF is importing %s routes from the following VRFs:\n", - afi_safi_print(afi, safi)); + get_afi_safi_str(afi, safi, false)); for (ALL_LIST_ELEMENTS_RO( bgp->vpn_policy[afi].import_vrf, @@ -11445,23 +13412,27 @@ static int bgp_show_route_leak_vty(struct vty *vty, const char *name, vty_out(vty, " %s\n", vname); dir = BGP_VPN_POLICY_DIR_FROMVPN; - ecom_str = ecommunity_ecom2str( + ecom_str = NULL; + if (bgp->vpn_policy[afi].rtlist[dir]) { + ecom_str = ecommunity_ecom2str( bgp->vpn_policy[afi].rtlist[dir], ECOMMUNITY_FORMAT_ROUTE_MAP, 0); - vty_out(vty, "Import RT(s): %s\n", ecom_str); + vty_out(vty, "Import RT(s): %s\n", ecom_str); - XFREE(MTYPE_ECOMMUNITY_STR, ecom_str); + XFREE(MTYPE_ECOMMUNITY_STR, ecom_str); + } else + vty_out(vty, "Import RT(s):\n"); } if (!CHECK_FLAG(bgp->af_flags[afi][safi], BGP_CONFIG_VRF_TO_VRF_EXPORT)) vty_out(vty, "This VRF is not exporting %s routes to any other VRF\n", - afi_safi_print(afi, safi)); + get_afi_safi_str(afi, safi, false)); else { vty_out(vty, "This VRF is exporting %s routes to the following VRFs:\n", - afi_safi_print(afi, safi)); + get_afi_safi_str(afi, safi, false)); for (ALL_LIST_ELEMENTS_RO( bgp->vpn_policy[afi].export_vrf, @@ -11473,11 +13444,14 @@ static int bgp_show_route_leak_vty(struct vty *vty, const char *name, buf1, RD_ADDRSTRLEN)); dir = BGP_VPN_POLICY_DIR_TOVPN; - ecom_str = ecommunity_ecom2str( + if (bgp->vpn_policy[afi].rtlist[dir]) { + ecom_str = ecommunity_ecom2str( bgp->vpn_policy[afi].rtlist[dir], ECOMMUNITY_FORMAT_ROUTE_MAP, 0); - vty_out(vty, "Export RT: %s\n", ecom_str); - XFREE(MTYPE_ECOMMUNITY_STR, ecom_str); + vty_out(vty, "Export RT: %s\n", ecom_str); + XFREE(MTYPE_ECOMMUNITY_STR, ecom_str); + } else + vty_out(vty, "Import RT(s):\n"); } } @@ -11816,10 +13790,10 @@ static int bgp_show_one_peer_group(struct vty *vty, struct peer_group *group) conf = group->conf; if (conf->as_type == AS_SPECIFIED || conf->as_type == AS_EXTERNAL) { - vty_out(vty, "\nBGP peer-group %s, remote AS %" PRIu32 "\n", + vty_out(vty, "\nBGP peer-group %s, remote AS %u\n", group->name, conf->as); } else if (conf->as_type == AS_INTERNAL) { - vty_out(vty, "\nBGP peer-group %s, remote AS %" PRIu32 "\n", + vty_out(vty, "\nBGP peer-group %s, remote AS %u\n", group->name, group->bgp->as); } else { vty_out(vty, "\nBGP peer-group %s\n", group->name); @@ -11835,7 +13809,7 @@ static int bgp_show_one_peer_group(struct vty *vty, struct peer_group *group) FOREACH_AFI_SAFI (afi, safi) { if (conf->afc[afi][safi]) { af_cfgd = 1; - vty_out(vty, " %s;", afi_safi_print(afi, safi)); + vty_out(vty, " %s;", get_afi_safi_str(afi, safi, false)); } } if (!af_cfgd) @@ -11869,7 +13843,8 @@ static int bgp_show_one_peer_group(struct vty *vty, struct peer_group *group) if (listcount(group->peer)) { vty_out(vty, " Peer-group members:\n"); for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) { - if (CHECK_FLAG(peer->flags, PEER_FLAG_SHUTDOWN)) + if (CHECK_FLAG(peer->flags, PEER_FLAG_SHUTDOWN) + || CHECK_FLAG(peer->bgp->flags, BGP_FLAG_SHUTDOWN)) peer_status = "Idle (Admin)"; else if (CHECK_FLAG(peer->sflags, PEER_STATUS_PREFIX_OVERFLOW)) @@ -12360,7 +14335,7 @@ ALIAS_HIDDEN( DEFUN (no_bgp_redistribute_ipv4_ospf, no_bgp_redistribute_ipv4_ospf_cmd, - "no redistribute (1-65535) [metric (0-4294967295)] [route-map WORD]", + "no redistribute (1-65535) [{metric (0-4294967295)|route-map WORD}]", NO_STR "Redistribute information from another routing protocol\n" "Open Shortest Path First (OSPFv2)\n" @@ -12388,7 +14363,7 @@ DEFUN (no_bgp_redistribute_ipv4_ospf, ALIAS_HIDDEN( no_bgp_redistribute_ipv4_ospf, no_bgp_redistribute_ipv4_ospf_hidden_cmd, - "no redistribute (1-65535) [metric (0-4294967295)] [route-map WORD]", + "no redistribute (1-65535) [{metric (0-4294967295)|route-map WORD}]", NO_STR "Redistribute information from another routing protocol\n" "Open Shortest Path First (OSPFv2)\n" @@ -12401,7 +14376,7 @@ ALIAS_HIDDEN( DEFUN (no_bgp_redistribute_ipv4, no_bgp_redistribute_ipv4_cmd, - "no redistribute " FRR_IP_REDIST_STR_BGPD " [metric (0-4294967295)] [route-map WORD]", + "no redistribute " FRR_IP_REDIST_STR_BGPD " [{metric (0-4294967295)|route-map WORD}]", NO_STR "Redistribute information from another routing protocol\n" FRR_IP_REDIST_HELP_STR_BGPD @@ -12425,7 +14400,7 @@ DEFUN (no_bgp_redistribute_ipv4, ALIAS_HIDDEN( no_bgp_redistribute_ipv4, no_bgp_redistribute_ipv4_hidden_cmd, "no redistribute " FRR_IP_REDIST_STR_BGPD - " [metric (0-4294967295)] [route-map WORD]", + " [{metric (0-4294967295)|route-map WORD}]", NO_STR "Redistribute information from another routing protocol\n" FRR_IP_REDIST_HELP_STR_BGPD "Metric for redistributed routes\n" @@ -12584,7 +14559,7 @@ DEFUN (bgp_redistribute_ipv6_metric_rmap, DEFUN (no_bgp_redistribute_ipv6, no_bgp_redistribute_ipv6_cmd, - "no redistribute " FRR_IP6_REDIST_STR_BGPD " [metric (0-4294967295)] [route-map WORD]", + "no redistribute " FRR_IP6_REDIST_STR_BGPD " [{metric (0-4294967295)|route-map WORD}]", NO_STR "Redistribute information from another routing protocol\n" FRR_IP6_REDIST_HELP_STR_BGPD @@ -12606,8 +14581,8 @@ DEFUN (no_bgp_redistribute_ipv6, return bgp_redistribute_unset(bgp, AFI_IP6, type, 0); } -void bgp_config_write_redistribute(struct vty *vty, struct bgp *bgp, afi_t afi, - safi_t safi) +static void bgp_config_write_redistribute(struct vty *vty, struct bgp *bgp, + afi_t afi, safi_t safi) { int i; @@ -12644,154 +14619,1335 @@ void bgp_config_write_redistribute(struct vty *vty, struct bgp *bgp, afi_t afi, } } -/* This is part of the address-family block (unicast only) */ -void bgp_vpn_policy_config_write_afi(struct vty *vty, struct bgp *bgp, - afi_t afi) +/* peer-group helpers for config-write */ + +static bool peergroup_flag_check(struct peer *peer, uint32_t flag) +{ + if (!peer_group_active(peer)) { + if (CHECK_FLAG(peer->flags_invert, flag)) + return !CHECK_FLAG(peer->flags, flag); + else + return !!CHECK_FLAG(peer->flags, flag); + } + + return !!CHECK_FLAG(peer->flags_override, flag); +} + +static bool peergroup_af_flag_check(struct peer *peer, afi_t afi, safi_t safi, + uint32_t flag) +{ + if (!peer_group_active(peer)) { + if (CHECK_FLAG(peer->af_flags_invert[afi][safi], flag)) + return !peer_af_flag_check(peer, afi, safi, flag); + else + return !!peer_af_flag_check(peer, afi, safi, flag); + } + + return !!CHECK_FLAG(peer->af_flags_override[afi][safi], flag); +} + +static bool peergroup_filter_check(struct peer *peer, afi_t afi, safi_t safi, + uint8_t type, int direct) +{ + struct bgp_filter *filter; + + if (peer_group_active(peer)) + return !!CHECK_FLAG(peer->filter_override[afi][safi][direct], + type); + + filter = &peer->filter[afi][safi]; + switch (type) { + case PEER_FT_DISTRIBUTE_LIST: + return !!(filter->dlist[direct].name); + case PEER_FT_FILTER_LIST: + return !!(filter->aslist[direct].name); + case PEER_FT_PREFIX_LIST: + return !!(filter->plist[direct].name); + case PEER_FT_ROUTE_MAP: + return !!(filter->map[direct].name); + case PEER_FT_UNSUPPRESS_MAP: + return !!(filter->usmap.name); + default: + return false; + } +} + +/* Return true if the addpath type is set for peer and different from + * peer-group. + */ +static bool peergroup_af_addpath_check(struct peer *peer, afi_t afi, + safi_t safi) +{ + enum bgp_addpath_strat type, g_type; + + type = peer->addpath_type[afi][safi]; + + if (type != BGP_ADDPATH_NONE) { + if (peer_group_active(peer)) { + g_type = peer->group->conf->addpath_type[afi][safi]; + + if (type != g_type) + return true; + else + return false; + } + + return true; + } + + return false; +} + +/* This is part of the address-family block (unicast only) */ +static void bgp_vpn_policy_config_write_afi(struct vty *vty, struct bgp *bgp, + afi_t afi) +{ + int indent = 2; + + if (bgp->vpn_policy[afi].rmap_name[BGP_VPN_POLICY_DIR_FROMVPN]) { + if (CHECK_FLAG(bgp->af_flags[afi][SAFI_UNICAST], + BGP_CONFIG_VRF_TO_VRF_IMPORT)) + vty_out(vty, "%*simport vrf route-map %s\n", indent, "", + bgp->vpn_policy[afi] + .rmap_name[BGP_VPN_POLICY_DIR_FROMVPN]); + else + vty_out(vty, "%*sroute-map vpn import %s\n", indent, "", + bgp->vpn_policy[afi] + .rmap_name[BGP_VPN_POLICY_DIR_FROMVPN]); + } + if (CHECK_FLAG(bgp->af_flags[afi][SAFI_UNICAST], + BGP_CONFIG_VRF_TO_VRF_IMPORT) + || CHECK_FLAG(bgp->af_flags[afi][SAFI_UNICAST], + BGP_CONFIG_VRF_TO_VRF_EXPORT)) + return; + + if (CHECK_FLAG(bgp->vpn_policy[afi].flags, + BGP_VPN_POLICY_TOVPN_LABEL_AUTO)) { + + vty_out(vty, "%*slabel vpn export %s\n", indent, "", "auto"); + + } else { + if (bgp->vpn_policy[afi].tovpn_label != MPLS_LABEL_NONE) { + vty_out(vty, "%*slabel vpn export %u\n", indent, "", + bgp->vpn_policy[afi].tovpn_label); + } + } + if (CHECK_FLAG(bgp->vpn_policy[afi].flags, + BGP_VPN_POLICY_TOVPN_RD_SET)) { + char buf[RD_ADDRSTRLEN]; + vty_out(vty, "%*srd vpn export %s\n", indent, "", + prefix_rd2str(&bgp->vpn_policy[afi].tovpn_rd, buf, + sizeof(buf))); + } + if (CHECK_FLAG(bgp->vpn_policy[afi].flags, + BGP_VPN_POLICY_TOVPN_NEXTHOP_SET)) { + + char buf[PREFIX_STRLEN]; + if (inet_ntop(bgp->vpn_policy[afi].tovpn_nexthop.family, + &bgp->vpn_policy[afi].tovpn_nexthop.u.prefix, buf, + sizeof(buf))) { + + vty_out(vty, "%*snexthop vpn export %s\n", + indent, "", buf); + } + } + if (bgp->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_FROMVPN] + && bgp->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_TOVPN] + && ecommunity_cmp( + bgp->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_FROMVPN], + bgp->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_TOVPN])) { + + char *b = ecommunity_ecom2str( + bgp->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_TOVPN], + ECOMMUNITY_FORMAT_ROUTE_MAP, ECOMMUNITY_ROUTE_TARGET); + vty_out(vty, "%*srt vpn both %s\n", indent, "", b); + XFREE(MTYPE_ECOMMUNITY_STR, b); + } else { + if (bgp->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_FROMVPN]) { + char *b = ecommunity_ecom2str( + bgp->vpn_policy[afi] + .rtlist[BGP_VPN_POLICY_DIR_FROMVPN], + ECOMMUNITY_FORMAT_ROUTE_MAP, + ECOMMUNITY_ROUTE_TARGET); + vty_out(vty, "%*srt vpn import %s\n", indent, "", b); + XFREE(MTYPE_ECOMMUNITY_STR, b); + } + if (bgp->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_TOVPN]) { + char *b = ecommunity_ecom2str( + bgp->vpn_policy[afi] + .rtlist[BGP_VPN_POLICY_DIR_TOVPN], + ECOMMUNITY_FORMAT_ROUTE_MAP, + ECOMMUNITY_ROUTE_TARGET); + vty_out(vty, "%*srt vpn export %s\n", indent, "", b); + XFREE(MTYPE_ECOMMUNITY_STR, b); + } + } + + if (bgp->vpn_policy[afi].rmap_name[BGP_VPN_POLICY_DIR_TOVPN]) + vty_out(vty, "%*sroute-map vpn export %s\n", indent, "", + bgp->vpn_policy[afi] + .rmap_name[BGP_VPN_POLICY_DIR_TOVPN]); + + if (bgp->vpn_policy[afi].import_redirect_rtlist) { + char *b = ecommunity_ecom2str( + bgp->vpn_policy[afi] + .import_redirect_rtlist, + ECOMMUNITY_FORMAT_ROUTE_MAP, + ECOMMUNITY_ROUTE_TARGET); + + if (bgp->vpn_policy[afi].import_redirect_rtlist->unit_size + != ECOMMUNITY_SIZE) + vty_out(vty, "%*srt6 redirect import %s\n", + indent, "", b); + else + vty_out(vty, "%*srt redirect import %s\n", + indent, "", b); + XFREE(MTYPE_ECOMMUNITY_STR, b); + } +} + +static void bgp_config_write_filter(struct vty *vty, struct peer *peer, + afi_t afi, safi_t safi) +{ + struct bgp_filter *filter; + char *addr; + + addr = peer->host; + filter = &peer->filter[afi][safi]; + + /* distribute-list. */ + if (peergroup_filter_check(peer, afi, safi, PEER_FT_DISTRIBUTE_LIST, + FILTER_IN)) + vty_out(vty, " neighbor %s distribute-list %s in\n", addr, + filter->dlist[FILTER_IN].name); + + if (peergroup_filter_check(peer, afi, safi, PEER_FT_DISTRIBUTE_LIST, + FILTER_OUT)) + vty_out(vty, " neighbor %s distribute-list %s out\n", addr, + filter->dlist[FILTER_OUT].name); + + /* prefix-list. */ + if (peergroup_filter_check(peer, afi, safi, PEER_FT_PREFIX_LIST, + FILTER_IN)) + vty_out(vty, " neighbor %s prefix-list %s in\n", addr, + filter->plist[FILTER_IN].name); + + if (peergroup_filter_check(peer, afi, safi, PEER_FT_PREFIX_LIST, + FILTER_OUT)) + vty_out(vty, " neighbor %s prefix-list %s out\n", addr, + filter->plist[FILTER_OUT].name); + + /* route-map. */ + if (peergroup_filter_check(peer, afi, safi, PEER_FT_ROUTE_MAP, RMAP_IN)) + vty_out(vty, " neighbor %s route-map %s in\n", addr, + filter->map[RMAP_IN].name); + + if (peergroup_filter_check(peer, afi, safi, PEER_FT_ROUTE_MAP, + RMAP_OUT)) + vty_out(vty, " neighbor %s route-map %s out\n", addr, + filter->map[RMAP_OUT].name); + + /* unsuppress-map */ + if (peergroup_filter_check(peer, afi, safi, PEER_FT_UNSUPPRESS_MAP, 0)) + vty_out(vty, " neighbor %s unsuppress-map %s\n", addr, + filter->usmap.name); + + /* filter-list. */ + if (peergroup_filter_check(peer, afi, safi, PEER_FT_FILTER_LIST, + FILTER_IN)) + vty_out(vty, " neighbor %s filter-list %s in\n", addr, + filter->aslist[FILTER_IN].name); + + if (peergroup_filter_check(peer, afi, safi, PEER_FT_FILTER_LIST, + FILTER_OUT)) + vty_out(vty, " neighbor %s filter-list %s out\n", addr, + filter->aslist[FILTER_OUT].name); +} + +/* BGP peer configuration display function. */ +static void bgp_config_write_peer_global(struct vty *vty, struct bgp *bgp, + struct peer *peer) +{ + struct peer *g_peer = NULL; + char buf[SU_ADDRSTRLEN]; + char *addr; + int if_pg_printed = false; + int if_ras_printed = false; + + /* Skip dynamic neighbors. */ + if (peer_dynamic_neighbor(peer)) + return; + + if (peer->conf_if) + addr = peer->conf_if; + else + addr = peer->host; + + /************************************ + ****** Global to the neighbor ****** + ************************************/ + if (peer->conf_if) { + if (CHECK_FLAG(peer->flags, PEER_FLAG_IFPEER_V6ONLY)) + vty_out(vty, " neighbor %s interface v6only", addr); + else + vty_out(vty, " neighbor %s interface", addr); + + if (peer_group_active(peer)) { + vty_out(vty, " peer-group %s", peer->group->name); + if_pg_printed = true; + } else if (peer->as_type == AS_SPECIFIED) { + vty_out(vty, " remote-as %u", peer->as); + if_ras_printed = true; + } else if (peer->as_type == AS_INTERNAL) { + vty_out(vty, " remote-as internal"); + if_ras_printed = true; + } else if (peer->as_type == AS_EXTERNAL) { + vty_out(vty, " remote-as external"); + if_ras_printed = true; + } + + vty_out(vty, "\n"); + } + + /* remote-as and peer-group */ + /* peer is a member of a peer-group */ + if (peer_group_active(peer)) { + g_peer = peer->group->conf; + + if (g_peer->as_type == AS_UNSPECIFIED && !if_ras_printed) { + if (peer->as_type == AS_SPECIFIED) { + vty_out(vty, " neighbor %s remote-as %u\n", + addr, peer->as); + } else if (peer->as_type == AS_INTERNAL) { + vty_out(vty, + " neighbor %s remote-as internal\n", + addr); + } else if (peer->as_type == AS_EXTERNAL) { + vty_out(vty, + " neighbor %s remote-as external\n", + addr); + } + } + + /* For swpX peers we displayed the peer-group + * via 'neighbor swpX interface peer-group PGNAME' */ + if (!if_pg_printed) + vty_out(vty, " neighbor %s peer-group %s\n", addr, + peer->group->name); + } + + /* peer is NOT a member of a peer-group */ + else { + /* peer is a peer-group, declare the peer-group */ + if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { + vty_out(vty, " neighbor %s peer-group\n", addr); + } + + if (!if_ras_printed) { + if (peer->as_type == AS_SPECIFIED) { + vty_out(vty, " neighbor %s remote-as %u\n", + addr, peer->as); + } else if (peer->as_type == AS_INTERNAL) { + vty_out(vty, + " neighbor %s remote-as internal\n", + addr); + } else if (peer->as_type == AS_EXTERNAL) { + vty_out(vty, + " neighbor %s remote-as external\n", + addr); + } + } + } + + /* local-as */ + if (peergroup_flag_check(peer, PEER_FLAG_LOCAL_AS)) { + vty_out(vty, " neighbor %s local-as %u", addr, + peer->change_local_as); + if (peergroup_flag_check(peer, PEER_FLAG_LOCAL_AS_NO_PREPEND)) + vty_out(vty, " no-prepend"); + if (peergroup_flag_check(peer, PEER_FLAG_LOCAL_AS_REPLACE_AS)) + vty_out(vty, " replace-as"); + vty_out(vty, "\n"); + } + + /* description */ + if (peer->desc) { + vty_out(vty, " neighbor %s description %s\n", addr, peer->desc); + } + + /* shutdown */ + if (peergroup_flag_check(peer, PEER_FLAG_SHUTDOWN)) { + if (peer->tx_shutdown_message) + vty_out(vty, " neighbor %s shutdown message %s\n", addr, + peer->tx_shutdown_message); + else + vty_out(vty, " neighbor %s shutdown\n", addr); + } + + if (peergroup_flag_check(peer, PEER_FLAG_RTT_SHUTDOWN)) + vty_out(vty, " neighbor %s shutdown rtt %u count %u\n", addr, + peer->rtt_expected, peer->rtt_keepalive_conf); + + /* bfd */ + if (peer->bfd_info) { + if (!peer_group_active(peer) || !g_peer->bfd_info) { + bgp_bfd_peer_config_write(vty, peer, addr); + } + } + + /* password */ + if (peergroup_flag_check(peer, PEER_FLAG_PASSWORD)) + vty_out(vty, " neighbor %s password %s\n", addr, + peer->password); + + /* neighbor solo */ + if (CHECK_FLAG(peer->flags, PEER_FLAG_LONESOUL)) { + if (!peer_group_active(peer)) { + vty_out(vty, " neighbor %s solo\n", addr); + } + } + + /* BGP port */ + if (peer->port != BGP_PORT_DEFAULT) { + vty_out(vty, " neighbor %s port %d\n", addr, peer->port); + } + + /* Local interface name */ + if (peer->ifname) { + vty_out(vty, " neighbor %s interface %s\n", addr, peer->ifname); + } + + /* passive */ + if (peergroup_flag_check(peer, PEER_FLAG_PASSIVE)) + vty_out(vty, " neighbor %s passive\n", addr); + + /* ebgp-multihop */ + if (peer->sort != BGP_PEER_IBGP && peer->ttl != BGP_DEFAULT_TTL + && !(peer->gtsm_hops != BGP_GTSM_HOPS_DISABLED + && peer->ttl == MAXTTL)) { + if (!peer_group_active(peer) || g_peer->ttl != peer->ttl) { + vty_out(vty, " neighbor %s ebgp-multihop %d\n", addr, + peer->ttl); + } + } + + /* ttl-security hops */ + if (peer->gtsm_hops != BGP_GTSM_HOPS_DISABLED) { + if (!peer_group_active(peer) + || g_peer->gtsm_hops != peer->gtsm_hops) { + vty_out(vty, " neighbor %s ttl-security hops %d\n", + addr, peer->gtsm_hops); + } + } + + /* disable-connected-check */ + if (peergroup_flag_check(peer, PEER_FLAG_DISABLE_CONNECTED_CHECK)) + vty_out(vty, " neighbor %s disable-connected-check\n", addr); + + /* enforce-first-as */ + if (peergroup_flag_check(peer, PEER_FLAG_ENFORCE_FIRST_AS)) + vty_out(vty, " neighbor %s enforce-first-as\n", addr); + + /* update-source */ + if (peergroup_flag_check(peer, PEER_FLAG_UPDATE_SOURCE)) { + if (peer->update_source) + vty_out(vty, " neighbor %s update-source %s\n", addr, + sockunion2str(peer->update_source, buf, + SU_ADDRSTRLEN)); + else if (peer->update_if) + vty_out(vty, " neighbor %s update-source %s\n", addr, + peer->update_if); + } + + /* advertisement-interval */ + if (peergroup_flag_check(peer, PEER_FLAG_ROUTEADV)) + vty_out(vty, " neighbor %s advertisement-interval %u\n", addr, + peer->routeadv); + + /* timers */ + if (peergroup_flag_check(peer, PEER_FLAG_TIMER)) + vty_out(vty, " neighbor %s timers %u %u\n", addr, + peer->keepalive, peer->holdtime); + + /* timers connect */ + if (peergroup_flag_check(peer, PEER_FLAG_TIMER_CONNECT)) + vty_out(vty, " neighbor %s timers connect %u\n", addr, + peer->connect); + /* need special-case handling for changed default values due to + * config profile / version (because there is no "timers bgp connect" + * command, we need to save this per-peer :/) + */ + else if (!peer_group_active(peer) && !peer->connect && + peer->bgp->default_connect_retry != SAVE_BGP_CONNECT_RETRY) + vty_out(vty, " neighbor %s timers connect %u\n", addr, + peer->bgp->default_connect_retry); + + /* capability dynamic */ + if (peergroup_flag_check(peer, PEER_FLAG_DYNAMIC_CAPABILITY)) + vty_out(vty, " neighbor %s capability dynamic\n", addr); + + /* capability extended-nexthop */ + if (peergroup_flag_check(peer, PEER_FLAG_CAPABILITY_ENHE)) { + if (!peer->conf_if) { + if (CHECK_FLAG(peer->flags_invert, + PEER_FLAG_CAPABILITY_ENHE)) + vty_out(vty, + " no neighbor %s capability extended-nexthop\n", + addr); + else + vty_out(vty, + " neighbor %s capability extended-nexthop\n", + addr); + } + } + + /* dont-capability-negotiation */ + if (peergroup_flag_check(peer, PEER_FLAG_DONT_CAPABILITY)) + vty_out(vty, " neighbor %s dont-capability-negotiate\n", addr); + + /* override-capability */ + if (peergroup_flag_check(peer, PEER_FLAG_OVERRIDE_CAPABILITY)) + vty_out(vty, " neighbor %s override-capability\n", addr); + + /* strict-capability-match */ + if (peergroup_flag_check(peer, PEER_FLAG_STRICT_CAP_MATCH)) + vty_out(vty, " neighbor %s strict-capability-match\n", addr); + + /* Sender side AS path loop detection. */ + if (peer->as_path_loop_detection) + vty_out(vty, " neighbor %s sender-as-path-loop-detection\n", + addr); + + if (!CHECK_FLAG(peer->peer_gr_new_status_flag, + PEER_GRACEFUL_RESTART_NEW_STATE_INHERIT)) { + + if (CHECK_FLAG(peer->peer_gr_new_status_flag, + PEER_GRACEFUL_RESTART_NEW_STATE_HELPER)) { + vty_out(vty, + " neighbor %s graceful-restart-helper\n", addr); + } else if (CHECK_FLAG( + peer->peer_gr_new_status_flag, + PEER_GRACEFUL_RESTART_NEW_STATE_RESTART)) { + vty_out(vty, + " neighbor %s graceful-restart\n", addr); + } else if ( + (!(CHECK_FLAG(peer->peer_gr_new_status_flag, + PEER_GRACEFUL_RESTART_NEW_STATE_HELPER)) + && !(CHECK_FLAG( + peer->peer_gr_new_status_flag, + PEER_GRACEFUL_RESTART_NEW_STATE_RESTART)))) { + vty_out(vty, " neighbor %s graceful-restart-disable\n", + addr); + } + } +} + +/* BGP peer configuration display function. */ +static void bgp_config_write_peer_af(struct vty *vty, struct bgp *bgp, + struct peer *peer, afi_t afi, safi_t safi) +{ + struct peer *g_peer = NULL; + char *addr; + bool flag_scomm, flag_secomm, flag_slcomm; + + /* Skip dynamic neighbors. */ + if (peer_dynamic_neighbor(peer)) + return; + + if (peer->conf_if) + addr = peer->conf_if; + else + addr = peer->host; + + /************************************ + ****** Per AF to the neighbor ****** + ************************************/ + if (peer_group_active(peer)) { + g_peer = peer->group->conf; + + /* If the peer-group is active but peer is not, print a 'no + * activate' */ + if (g_peer->afc[afi][safi] && !peer->afc[afi][safi]) { + vty_out(vty, " no neighbor %s activate\n", addr); + } + + /* If the peer-group is not active but peer is, print an + 'activate' */ + else if (!g_peer->afc[afi][safi] && peer->afc[afi][safi]) { + vty_out(vty, " neighbor %s activate\n", addr); + } + } else { + if (peer->afc[afi][safi]) { + if ((afi == AFI_IP) && (safi == SAFI_UNICAST)) { + if (CHECK_FLAG(bgp->flags, + BGP_FLAG_NO_DEFAULT_IPV4)) { + vty_out(vty, " neighbor %s activate\n", + addr); + } + } else + vty_out(vty, " neighbor %s activate\n", addr); + } else { + if ((afi == AFI_IP) && (safi == SAFI_UNICAST)) { + if (!CHECK_FLAG(bgp->flags, + BGP_FLAG_NO_DEFAULT_IPV4)) { + vty_out(vty, + " no neighbor %s activate\n", + addr); + } + } + } + } + + /* addpath TX knobs */ + if (peergroup_af_addpath_check(peer, afi, safi)) { + switch (peer->addpath_type[afi][safi]) { + case BGP_ADDPATH_ALL: + vty_out(vty, " neighbor %s addpath-tx-all-paths\n", + addr); + break; + case BGP_ADDPATH_BEST_PER_AS: + vty_out(vty, + " neighbor %s addpath-tx-bestpath-per-AS\n", + addr); + break; + case BGP_ADDPATH_MAX: + case BGP_ADDPATH_NONE: + break; + } + } + + /* ORF capability. */ + if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_ORF_PREFIX_SM) + || peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_ORF_PREFIX_RM)) { + vty_out(vty, " neighbor %s capability orf prefix-list", addr); + + if (peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_ORF_PREFIX_SM) + && peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_ORF_PREFIX_RM)) + vty_out(vty, " both"); + else if (peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_ORF_PREFIX_SM)) + vty_out(vty, " send"); + else + vty_out(vty, " receive"); + vty_out(vty, "\n"); + } + + /* Route reflector client. */ + if (peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_REFLECTOR_CLIENT)) { + vty_out(vty, " neighbor %s route-reflector-client\n", addr); + } + + /* next-hop-self force */ + if (peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_FORCE_NEXTHOP_SELF)) { + vty_out(vty, " neighbor %s next-hop-self force\n", addr); + } + + /* next-hop-self */ + if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_NEXTHOP_SELF)) { + vty_out(vty, " neighbor %s next-hop-self\n", addr); + } + + /* remove-private-AS */ + if (peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_REMOVE_PRIVATE_AS_ALL_REPLACE)) { + vty_out(vty, " neighbor %s remove-private-AS all replace-AS\n", + addr); + } + + else if (peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_REMOVE_PRIVATE_AS_REPLACE)) { + vty_out(vty, " neighbor %s remove-private-AS replace-AS\n", + addr); + } + + else if (peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_REMOVE_PRIVATE_AS_ALL)) { + vty_out(vty, " neighbor %s remove-private-AS all\n", addr); + } + + else if (peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_REMOVE_PRIVATE_AS)) { + vty_out(vty, " neighbor %s remove-private-AS\n", addr); + } + + /* as-override */ + if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_AS_OVERRIDE)) { + vty_out(vty, " neighbor %s as-override\n", addr); + } + + /* send-community print. */ + flag_scomm = peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_SEND_COMMUNITY); + flag_secomm = peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_SEND_EXT_COMMUNITY); + flag_slcomm = peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_SEND_LARGE_COMMUNITY); + + if (flag_scomm && flag_secomm && flag_slcomm) { + vty_out(vty, " no neighbor %s send-community all\n", addr); + } else { + if (flag_scomm) + vty_out(vty, " no neighbor %s send-community\n", addr); + if (flag_secomm) + vty_out(vty, + " no neighbor %s send-community extended\n", + addr); + + if (flag_slcomm) + vty_out(vty, " no neighbor %s send-community large\n", + addr); + } + + /* Default information */ + if (peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_DEFAULT_ORIGINATE)) { + vty_out(vty, " neighbor %s default-originate", addr); + + if (peer->default_rmap[afi][safi].name) + vty_out(vty, " route-map %s", + peer->default_rmap[afi][safi].name); + + vty_out(vty, "\n"); + } + + /* Soft reconfiguration inbound. */ + if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_SOFT_RECONFIG)) { + vty_out(vty, " neighbor %s soft-reconfiguration inbound\n", + addr); + } + + /* maximum-prefix. */ + if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_MAX_PREFIX)) { + vty_out(vty, " neighbor %s maximum-prefix %u", addr, + peer->pmax[afi][safi]); + + if (peer->pmax_threshold[afi][safi] + != MAXIMUM_PREFIX_THRESHOLD_DEFAULT) + vty_out(vty, " %u", peer->pmax_threshold[afi][safi]); + if (peer_af_flag_check(peer, afi, safi, + PEER_FLAG_MAX_PREFIX_WARNING)) + vty_out(vty, " warning-only"); + if (peer->pmax_restart[afi][safi]) + vty_out(vty, " restart %u", + peer->pmax_restart[afi][safi]); + if (peer_af_flag_check(peer, afi, safi, + PEER_FLAG_MAX_PREFIX_FORCE)) + vty_out(vty, " force"); + + vty_out(vty, "\n"); + } + + /* maximum-prefix-out */ + if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_MAX_PREFIX_OUT)) + vty_out(vty, " neighbor %s maximum-prefix-out %u\n", + addr, peer->pmax_out[afi][safi]); + + /* Route server client. */ + if (peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_RSERVER_CLIENT)) { + vty_out(vty, " neighbor %s route-server-client\n", addr); + } + + /* Nexthop-local unchanged. */ + if (peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED)) { + vty_out(vty, " neighbor %s nexthop-local unchanged\n", addr); + } + + /* allowas-in <1-10> */ + if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_ALLOWAS_IN)) { + if (peer_af_flag_check(peer, afi, safi, + PEER_FLAG_ALLOWAS_IN_ORIGIN)) { + vty_out(vty, " neighbor %s allowas-in origin\n", addr); + } else if (peer->allowas_in[afi][safi] == 3) { + vty_out(vty, " neighbor %s allowas-in\n", addr); + } else { + vty_out(vty, " neighbor %s allowas-in %d\n", addr, + peer->allowas_in[afi][safi]); + } + } + + /* weight */ + if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_WEIGHT)) + vty_out(vty, " neighbor %s weight %lu\n", addr, + peer->weight[afi][safi]); + + /* Filter. */ + bgp_config_write_filter(vty, peer, afi, safi); + + /* atribute-unchanged. */ + if (peer_af_flag_check(peer, afi, safi, PEER_FLAG_AS_PATH_UNCHANGED) + || (safi != SAFI_EVPN + && peer_af_flag_check(peer, afi, safi, + PEER_FLAG_NEXTHOP_UNCHANGED)) + || peer_af_flag_check(peer, afi, safi, PEER_FLAG_MED_UNCHANGED)) { + + if (!peer_group_active(peer) + || peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_AS_PATH_UNCHANGED) + || peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_NEXTHOP_UNCHANGED) + || peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_MED_UNCHANGED)) { + + vty_out(vty, + " neighbor %s attribute-unchanged%s%s%s\n", + addr, + peer_af_flag_check(peer, afi, safi, + PEER_FLAG_AS_PATH_UNCHANGED) + ? " as-path" + : "", + peer_af_flag_check(peer, afi, safi, + PEER_FLAG_NEXTHOP_UNCHANGED) + ? " next-hop" + : "", + peer_af_flag_check(peer, afi, safi, + PEER_FLAG_MED_UNCHANGED) + ? " med" + : ""); + } + } +} + +/* Address family based peer configuration display. */ +static void bgp_config_write_family(struct vty *vty, struct bgp *bgp, afi_t afi, + safi_t safi) +{ + struct peer *peer; + struct peer_group *group; + struct listnode *node, *nnode; + + + vty_frame(vty, " !\n address-family "); + if (afi == AFI_IP) { + if (safi == SAFI_UNICAST) + vty_frame(vty, "ipv4 unicast"); + else if (safi == SAFI_LABELED_UNICAST) + vty_frame(vty, "ipv4 labeled-unicast"); + else if (safi == SAFI_MULTICAST) + vty_frame(vty, "ipv4 multicast"); + else if (safi == SAFI_MPLS_VPN) + vty_frame(vty, "ipv4 vpn"); + else if (safi == SAFI_ENCAP) + vty_frame(vty, "ipv4 encap"); + else if (safi == SAFI_FLOWSPEC) + vty_frame(vty, "ipv4 flowspec"); + } else if (afi == AFI_IP6) { + if (safi == SAFI_UNICAST) + vty_frame(vty, "ipv6 unicast"); + else if (safi == SAFI_LABELED_UNICAST) + vty_frame(vty, "ipv6 labeled-unicast"); + else if (safi == SAFI_MULTICAST) + vty_frame(vty, "ipv6 multicast"); + else if (safi == SAFI_MPLS_VPN) + vty_frame(vty, "ipv6 vpn"); + else if (safi == SAFI_ENCAP) + vty_frame(vty, "ipv6 encap"); + else if (safi == SAFI_FLOWSPEC) + vty_frame(vty, "ipv6 flowspec"); + } else if (afi == AFI_L2VPN) { + if (safi == SAFI_EVPN) + vty_frame(vty, "l2vpn evpn"); + } + vty_frame(vty, "\n"); + + bgp_config_write_distance(vty, bgp, afi, safi); + + bgp_config_write_network(vty, bgp, afi, safi); + + bgp_config_write_redistribute(vty, bgp, afi, safi); + + /* BGP flag dampening. */ + if (CHECK_FLAG(bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING)) + bgp_config_write_damp(vty, afi, safi); + + for (ALL_LIST_ELEMENTS(bgp->group, node, nnode, group)) + bgp_config_write_peer_af(vty, bgp, group->conf, afi, safi); + + for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { + /* Skip dynamic neighbors. */ + if (peer_dynamic_neighbor(peer)) + continue; + + /* Do not display doppelganger peers */ + if (CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE)) + bgp_config_write_peer_af(vty, bgp, peer, afi, safi); + } + + bgp_config_write_maxpaths(vty, bgp, afi, safi); + bgp_config_write_table_map(vty, bgp, afi, safi); + + if (safi == SAFI_EVPN) + bgp_config_write_evpn_info(vty, bgp, afi, safi); + + if (safi == SAFI_FLOWSPEC) + bgp_fs_config_write_pbr(vty, bgp, afi, safi); + + if (safi == SAFI_UNICAST) { + bgp_vpn_policy_config_write_afi(vty, bgp, afi); + if (CHECK_FLAG(bgp->af_flags[afi][safi], + BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT)) { + + vty_out(vty, " export vpn\n"); + } + if (CHECK_FLAG(bgp->af_flags[afi][safi], + BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT)) { + + vty_out(vty, " import vpn\n"); + } + if (CHECK_FLAG(bgp->af_flags[afi][safi], + BGP_CONFIG_VRF_TO_VRF_IMPORT)) { + char *name; + + for (ALL_LIST_ELEMENTS_RO( + bgp->vpn_policy[afi].import_vrf, node, + name)) + vty_out(vty, " import vrf %s\n", name); + } + } + + vty_endframe(vty, " exit-address-family\n"); +} + +int bgp_config_write(struct vty *vty) { - int indent = 2; + struct bgp *bgp; + struct peer_group *group; + struct peer *peer; + struct listnode *node, *nnode; + struct listnode *mnode, *mnnode; - if (bgp->vpn_policy[afi].rmap_name[BGP_VPN_POLICY_DIR_FROMVPN]) { - if (listcount(bgp->vpn_policy[afi].import_vrf)) - vty_out(vty, "%*simport vrf route-map %s\n", indent, "", - bgp->vpn_policy[afi] - .rmap_name[BGP_VPN_POLICY_DIR_FROMVPN]); - else - vty_out(vty, "%*sroute-map vpn import %s\n", indent, "", - bgp->vpn_policy[afi] - .rmap_name[BGP_VPN_POLICY_DIR_FROMVPN]); + if (bm->rmap_update_timer != RMAP_DEFAULT_UPDATE_TIMER) + vty_out(vty, "bgp route-map delay-timer %u\n", + bm->rmap_update_timer); + + if (bm->v_update_delay != BGP_UPDATE_DELAY_DEF) { + vty_out(vty, "bgp update-delay %d", bm->v_update_delay); + if (bm->v_update_delay != bm->v_establish_wait) + vty_out(vty, " %d", bm->v_establish_wait); + vty_out(vty, "\n"); } - if (CHECK_FLAG(bgp->af_flags[afi][SAFI_UNICAST], - BGP_CONFIG_VRF_TO_VRF_IMPORT) - || CHECK_FLAG(bgp->af_flags[afi][SAFI_UNICAST], - BGP_CONFIG_VRF_TO_VRF_EXPORT)) - return; - if (CHECK_FLAG(bgp->vpn_policy[afi].flags, - BGP_VPN_POLICY_TOVPN_LABEL_AUTO)) { + /* BGP configuration. */ + for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp)) { - vty_out(vty, "%*slabel vpn export %s\n", indent, "", "auto"); + /* skip all auto created vrf as they dont have user config */ + if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_AUTO)) + continue; - } else { - if (bgp->vpn_policy[afi].tovpn_label != MPLS_LABEL_NONE) { - vty_out(vty, "%*slabel vpn export %u\n", indent, "", - bgp->vpn_policy[afi].tovpn_label); + /* Router bgp ASN */ + vty_out(vty, "router bgp %u", bgp->as); + + if (bgp->name) + vty_out(vty, " %s %s", + (bgp->inst_type == BGP_INSTANCE_TYPE_VIEW) + ? "view" : "vrf", bgp->name); + vty_out(vty, "\n"); + + /* BGP fast-external-failover. */ + if (CHECK_FLAG(bgp->flags, BGP_FLAG_NO_FAST_EXT_FAILOVER)) + vty_out(vty, " no bgp fast-external-failover\n"); + + /* BGP router ID. */ + if (bgp->router_id_static.s_addr != 0) + vty_out(vty, " bgp router-id %s\n", + inet_ntoa(bgp->router_id_static)); + + /* BGP log-neighbor-changes. */ + if (!!CHECK_FLAG(bgp->flags, BGP_FLAG_LOG_NEIGHBOR_CHANGES) + != SAVE_BGP_LOG_NEIGHBOR_CHANGES) + vty_out(vty, " %sbgp log-neighbor-changes\n", + CHECK_FLAG(bgp->flags, + BGP_FLAG_LOG_NEIGHBOR_CHANGES) + ? "" + : "no "); + + /* BGP configuration. */ + if (CHECK_FLAG(bgp->flags, BGP_FLAG_ALWAYS_COMPARE_MED)) + vty_out(vty, " bgp always-compare-med\n"); + + /* RFC8212 default eBGP policy. */ + if (!!CHECK_FLAG(bgp->flags, BGP_FLAG_EBGP_REQUIRES_POLICY) + != SAVE_BGP_EBGP_REQUIRES_POLICY) + vty_out(vty, " %sbgp ebgp-requires-policy\n", + CHECK_FLAG(bgp->flags, + BGP_FLAG_EBGP_REQUIRES_POLICY) + ? "" + : "no "); + + /* draft-ietf-idr-deprecate-as-set-confed-set */ + if (bgp->reject_as_sets) + vty_out(vty, " bgp reject-as-sets\n"); + + /* BGP default ipv4-unicast. */ + if (CHECK_FLAG(bgp->flags, BGP_FLAG_NO_DEFAULT_IPV4)) + vty_out(vty, " no bgp default ipv4-unicast\n"); + + /* BGP default local-preference. */ + if (bgp->default_local_pref != BGP_DEFAULT_LOCAL_PREF) + vty_out(vty, " bgp default local-preference %u\n", + bgp->default_local_pref); + + /* BGP default show-hostname */ + if (!!CHECK_FLAG(bgp->flags, BGP_FLAG_SHOW_HOSTNAME) + != SAVE_BGP_SHOW_HOSTNAME) + vty_out(vty, " %sbgp default show-hostname\n", + CHECK_FLAG(bgp->flags, BGP_FLAG_SHOW_HOSTNAME) + ? "" + : "no "); + + /* BGP default show-nexthop-hostname */ + if (!!CHECK_FLAG(bgp->flags, BGP_FLAG_SHOW_NEXTHOP_HOSTNAME) + != SAVE_BGP_SHOW_HOSTNAME) + vty_out(vty, " %sbgp default show-nexthop-hostname\n", + CHECK_FLAG(bgp->flags, + BGP_FLAG_SHOW_NEXTHOP_HOSTNAME) + ? "" + : "no "); + + /* BGP default subgroup-pkt-queue-max. */ + if (bgp->default_subgroup_pkt_queue_max + != BGP_DEFAULT_SUBGROUP_PKT_QUEUE_MAX) + vty_out(vty, " bgp default subgroup-pkt-queue-max %u\n", + bgp->default_subgroup_pkt_queue_max); + + /* BGP client-to-client reflection. */ + if (CHECK_FLAG(bgp->flags, BGP_FLAG_NO_CLIENT_TO_CLIENT)) + vty_out(vty, " no bgp client-to-client reflection\n"); + + /* BGP cluster ID. */ + if (CHECK_FLAG(bgp->config, BGP_CONFIG_CLUSTER_ID)) + vty_out(vty, " bgp cluster-id %s\n", + inet_ntoa(bgp->cluster_id)); + + /* Disable ebgp connected nexthop check */ + if (CHECK_FLAG(bgp->flags, BGP_FLAG_DISABLE_NH_CONNECTED_CHK)) + vty_out(vty, + " bgp disable-ebgp-connected-route-check\n"); + + /* Confederation identifier*/ + if (CHECK_FLAG(bgp->config, BGP_CONFIG_CONFEDERATION)) + vty_out(vty, " bgp confederation identifier %u\n", + bgp->confed_id); + + /* Confederation peer */ + if (bgp->confed_peers_cnt > 0) { + int i; + + vty_out(vty, " bgp confederation peers"); + + for (i = 0; i < bgp->confed_peers_cnt; i++) + vty_out(vty, " %u", bgp->confed_peers[i]); + + vty_out(vty, "\n"); } - } - if (CHECK_FLAG(bgp->vpn_policy[afi].flags, - BGP_VPN_POLICY_TOVPN_RD_SET)) { - char buf[RD_ADDRSTRLEN]; - vty_out(vty, "%*srd vpn export %s\n", indent, "", - prefix_rd2str(&bgp->vpn_policy[afi].tovpn_rd, buf, - sizeof(buf))); - } - if (CHECK_FLAG(bgp->vpn_policy[afi].flags, - BGP_VPN_POLICY_TOVPN_NEXTHOP_SET)) { - char buf[PREFIX_STRLEN]; - if (inet_ntop(bgp->vpn_policy[afi].tovpn_nexthop.family, - &bgp->vpn_policy[afi].tovpn_nexthop.u.prefix, buf, - sizeof(buf))) { + /* BGP deterministic-med. */ + if (!!CHECK_FLAG(bgp->flags, BGP_FLAG_DETERMINISTIC_MED) + != SAVE_BGP_DETERMINISTIC_MED) + vty_out(vty, " %sbgp deterministic-med\n", + CHECK_FLAG(bgp->flags, + BGP_FLAG_DETERMINISTIC_MED) + ? "" + : "no "); + + /* BGP update-delay. */ + bgp_config_write_update_delay(vty, bgp); + + if (bgp->v_maxmed_onstartup + != BGP_MAXMED_ONSTARTUP_UNCONFIGURED) { + vty_out(vty, " bgp max-med on-startup %u", + bgp->v_maxmed_onstartup); + if (bgp->maxmed_onstartup_value + != BGP_MAXMED_VALUE_DEFAULT) + vty_out(vty, " %u", + bgp->maxmed_onstartup_value); + vty_out(vty, "\n"); + } + if (bgp->v_maxmed_admin != BGP_MAXMED_ADMIN_UNCONFIGURED) { + vty_out(vty, " bgp max-med administrative"); + if (bgp->maxmed_admin_value != BGP_MAXMED_VALUE_DEFAULT) + vty_out(vty, " %u", bgp->maxmed_admin_value); + vty_out(vty, "\n"); + } - vty_out(vty, "%*snexthop vpn export %s\n", - indent, "", buf); + /* write quanta */ + bgp_config_write_wpkt_quanta(vty, bgp); + /* read quanta */ + bgp_config_write_rpkt_quanta(vty, bgp); + + /* coalesce time */ + bgp_config_write_coalesce_time(vty, bgp); + + /* BGP graceful-restart. */ + if (bgp->stalepath_time != BGP_DEFAULT_STALEPATH_TIME) + vty_out(vty, + " bgp graceful-restart stalepath-time %u\n", + bgp->stalepath_time); + + if (bgp->restart_time != BGP_DEFAULT_RESTART_TIME) + vty_out(vty, " bgp graceful-restart restart-time %u\n", + bgp->restart_time); + + if (bgp->select_defer_time != BGP_DEFAULT_SELECT_DEFERRAL_TIME) + vty_out(vty, + " bgp graceful-restart select-defer-time %u\n", + bgp->select_defer_time); + + if (bgp_global_gr_mode_get(bgp) == GLOBAL_GR) + vty_out(vty, " bgp graceful-restart\n"); + + if (bgp_global_gr_mode_get(bgp) == GLOBAL_DISABLE) + vty_out(vty, " bgp graceful-restart-disable\n"); + + /* BGP graceful-shutdown */ + if (CHECK_FLAG(bgp->flags, BGP_FLAG_GRACEFUL_SHUTDOWN)) + vty_out(vty, " bgp graceful-shutdown\n"); + + /* BGP graceful-restart Preserve State F bit. */ + if (CHECK_FLAG(bgp->flags, BGP_FLAG_GR_PRESERVE_FWD)) + vty_out(vty, + " bgp graceful-restart preserve-fw-state\n"); + + /* Stale timer for RIB */ + if (bgp->rib_stale_time != BGP_DEFAULT_RIB_STALE_TIME) + vty_out(vty, + " bgp graceful-restart rib-stale-time %u\n", + bgp->rib_stale_time); + + /* BGP bestpath method. */ + if (CHECK_FLAG(bgp->flags, BGP_FLAG_ASPATH_IGNORE)) + vty_out(vty, " bgp bestpath as-path ignore\n"); + if (CHECK_FLAG(bgp->flags, BGP_FLAG_ASPATH_CONFED)) + vty_out(vty, " bgp bestpath as-path confed\n"); + + if (CHECK_FLAG(bgp->flags, BGP_FLAG_ASPATH_MULTIPATH_RELAX)) { + if (CHECK_FLAG(bgp->flags, + BGP_FLAG_MULTIPATH_RELAX_AS_SET)) { + vty_out(vty, + " bgp bestpath as-path multipath-relax as-set\n"); + } else { + vty_out(vty, + " bgp bestpath as-path multipath-relax\n"); + } } - } - if (bgp->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_FROMVPN] - && bgp->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_TOVPN] - && ecommunity_cmp( - bgp->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_FROMVPN], - bgp->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_TOVPN])) { - char *b = ecommunity_ecom2str( - bgp->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_TOVPN], - ECOMMUNITY_FORMAT_ROUTE_MAP, ECOMMUNITY_ROUTE_TARGET); - vty_out(vty, "%*srt vpn both %s\n", indent, "", b); - XFREE(MTYPE_ECOMMUNITY_STR, b); - } else { - if (bgp->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_FROMVPN]) { - char *b = ecommunity_ecom2str( - bgp->vpn_policy[afi] - .rtlist[BGP_VPN_POLICY_DIR_FROMVPN], - ECOMMUNITY_FORMAT_ROUTE_MAP, - ECOMMUNITY_ROUTE_TARGET); - vty_out(vty, "%*srt vpn import %s\n", indent, "", b); - XFREE(MTYPE_ECOMMUNITY_STR, b); + if (CHECK_FLAG(bgp->flags, BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY)) { + vty_out(vty, + " bgp route-reflector allow-outbound-policy\n"); } - if (bgp->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_TOVPN]) { - char *b = ecommunity_ecom2str( - bgp->vpn_policy[afi] - .rtlist[BGP_VPN_POLICY_DIR_TOVPN], - ECOMMUNITY_FORMAT_ROUTE_MAP, - ECOMMUNITY_ROUTE_TARGET); - vty_out(vty, "%*srt vpn export %s\n", indent, "", b); - XFREE(MTYPE_ECOMMUNITY_STR, b); + if (CHECK_FLAG(bgp->flags, BGP_FLAG_COMPARE_ROUTER_ID)) + vty_out(vty, " bgp bestpath compare-routerid\n"); + if (CHECK_FLAG(bgp->flags, BGP_FLAG_MED_CONFED) + || CHECK_FLAG(bgp->flags, BGP_FLAG_MED_MISSING_AS_WORST)) { + vty_out(vty, " bgp bestpath med"); + if (CHECK_FLAG(bgp->flags, BGP_FLAG_MED_CONFED)) + vty_out(vty, " confed"); + if (CHECK_FLAG(bgp->flags, + BGP_FLAG_MED_MISSING_AS_WORST)) + vty_out(vty, " missing-as-worst"); + vty_out(vty, "\n"); } - } - if (bgp->vpn_policy[afi].rmap_name[BGP_VPN_POLICY_DIR_TOVPN]) - vty_out(vty, "%*sroute-map vpn export %s\n", indent, "", - bgp->vpn_policy[afi] - .rmap_name[BGP_VPN_POLICY_DIR_TOVPN]); + /* Link bandwidth handling. */ + if (bgp->lb_handling == BGP_LINK_BW_IGNORE_BW) + vty_out(vty, " bgp bestpath bandwidth ignore\n"); + else if (bgp->lb_handling == BGP_LINK_BW_SKIP_MISSING) + vty_out(vty, " bgp bestpath bandwidth skip-missing\n"); + else if (bgp->lb_handling == BGP_LINK_BW_DEFWT_4_MISSING) + vty_out(vty, " bgp bestpath bandwidth default-weight-for-missing\n"); + + /* BGP network import check. */ + if (!!CHECK_FLAG(bgp->flags, BGP_FLAG_IMPORT_CHECK) + != SAVE_BGP_IMPORT_CHECK) + vty_out(vty, " %sbgp network import-check\n", + CHECK_FLAG(bgp->flags, BGP_FLAG_IMPORT_CHECK) + ? "" + : "no "); + + /* BGP timers configuration. */ + if (bgp->default_keepalive != SAVE_BGP_KEEPALIVE + && bgp->default_holdtime != SAVE_BGP_HOLDTIME) + vty_out(vty, " timers bgp %u %u\n", + bgp->default_keepalive, bgp->default_holdtime); + + /* peer-group */ + for (ALL_LIST_ELEMENTS(bgp->group, node, nnode, group)) { + bgp_config_write_peer_global(vty, bgp, group->conf); + } - if (bgp->vpn_policy[afi].import_redirect_rtlist) { - char *b = ecommunity_ecom2str( - bgp->vpn_policy[afi] - .import_redirect_rtlist, - ECOMMUNITY_FORMAT_ROUTE_MAP, - ECOMMUNITY_ROUTE_TARGET); + /* Normal neighbor configuration. */ + for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { + if (CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE)) + bgp_config_write_peer_global(vty, bgp, peer); + } - vty_out(vty, "%*srt redirect import %s\n", indent, "", b); - XFREE(MTYPE_ECOMMUNITY_STR, b); + /* listen range and limit for dynamic BGP neighbors */ + bgp_config_write_listen(vty, bgp); + + /* + * BGP default autoshutdown neighbors + * + * This must be placed after any peer and peer-group + * configuration, to avoid setting all peers to shutdown after + * a daemon restart, which is undesired behavior. (see #2286) + */ + if (bgp->autoshutdown) + vty_out(vty, " bgp default shutdown\n"); + + /* BGP instance administrative shutdown */ + if (CHECK_FLAG(bgp->flags, BGP_FLAG_SHUTDOWN)) + vty_out(vty, " bgp shutdown\n"); + + /* IPv4 unicast configuration. */ + bgp_config_write_family(vty, bgp, AFI_IP, SAFI_UNICAST); + + /* IPv4 multicast configuration. */ + bgp_config_write_family(vty, bgp, AFI_IP, SAFI_MULTICAST); + + /* IPv4 labeled-unicast configuration. */ + bgp_config_write_family(vty, bgp, AFI_IP, SAFI_LABELED_UNICAST); + + /* IPv4 VPN configuration. */ + bgp_config_write_family(vty, bgp, AFI_IP, SAFI_MPLS_VPN); + + /* ENCAPv4 configuration. */ + bgp_config_write_family(vty, bgp, AFI_IP, SAFI_ENCAP); + + /* FLOWSPEC v4 configuration. */ + bgp_config_write_family(vty, bgp, AFI_IP, SAFI_FLOWSPEC); + + /* IPv6 unicast configuration. */ + bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_UNICAST); + + /* IPv6 multicast configuration. */ + bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_MULTICAST); + + /* IPv6 labeled-unicast configuration. */ + bgp_config_write_family(vty, bgp, AFI_IP6, + SAFI_LABELED_UNICAST); + + /* IPv6 VPN configuration. */ + bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_MPLS_VPN); + + /* ENCAPv6 configuration. */ + bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_ENCAP); + + /* FLOWSPEC v6 configuration. */ + bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_FLOWSPEC); + + /* EVPN configuration. */ + bgp_config_write_family(vty, bgp, AFI_L2VPN, SAFI_EVPN); + + hook_call(bgp_inst_config_write, bgp, vty); + +#ifdef ENABLE_BGP_VNC + bgp_rfapi_cfg_write(vty, bgp); +#endif + + vty_out(vty, "!\n"); } + return 0; } /* BGP node structure. */ static struct cmd_node bgp_node = { - BGP_NODE, "%s(config-router)# ", 1, + .name = "bgp", + .node = BGP_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-router)# ", + .config_write = bgp_config_write, }; static struct cmd_node bgp_ipv4_unicast_node = { - BGP_IPV4_NODE, "%s(config-router-af)# ", 1, + .name = "bgp ipv4 unicast", + .node = BGP_IPV4_NODE, + .parent_node = BGP_NODE, + .prompt = "%s(config-router-af)# ", }; static struct cmd_node bgp_ipv4_multicast_node = { - BGP_IPV4M_NODE, "%s(config-router-af)# ", 1, + .name = "bgp ipv4 multicast", + .node = BGP_IPV4M_NODE, + .parent_node = BGP_NODE, + .prompt = "%s(config-router-af)# ", }; static struct cmd_node bgp_ipv4_labeled_unicast_node = { - BGP_IPV4L_NODE, "%s(config-router-af)# ", 1, + .name = "bgp ipv4 labeled unicast", + .node = BGP_IPV4L_NODE, + .parent_node = BGP_NODE, + .prompt = "%s(config-router-af)# ", }; static struct cmd_node bgp_ipv6_unicast_node = { - BGP_IPV6_NODE, "%s(config-router-af)# ", 1, + .name = "bgp ipv6", + .node = BGP_IPV6_NODE, + .parent_node = BGP_NODE, + .prompt = "%s(config-router-af)# ", }; static struct cmd_node bgp_ipv6_multicast_node = { - BGP_IPV6M_NODE, "%s(config-router-af)# ", 1, + .name = "bgp ipv6 multicast", + .node = BGP_IPV6M_NODE, + .parent_node = BGP_NODE, + .prompt = "%s(config-router-af)# ", }; static struct cmd_node bgp_ipv6_labeled_unicast_node = { - BGP_IPV6L_NODE, "%s(config-router-af)# ", 1, + .name = "bgp ipv6 labeled unicast", + .node = BGP_IPV6L_NODE, + .parent_node = BGP_NODE, + .prompt = "%s(config-router-af)# ", }; -static struct cmd_node bgp_vpnv4_node = {BGP_VPNV4_NODE, - "%s(config-router-af)# ", 1}; +static struct cmd_node bgp_vpnv4_node = { + .name = "bgp vpnv4", + .node = BGP_VPNV4_NODE, + .parent_node = BGP_NODE, + .prompt = "%s(config-router-af)# ", +}; -static struct cmd_node bgp_vpnv6_node = {BGP_VPNV6_NODE, - "%s(config-router-af-vpnv6)# ", 1}; +static struct cmd_node bgp_vpnv6_node = { + .name = "bgp vpnv6", + .node = BGP_VPNV6_NODE, + .parent_node = BGP_NODE, + .prompt = "%s(config-router-af-vpnv6)# ", +}; -static struct cmd_node bgp_evpn_node = {BGP_EVPN_NODE, - "%s(config-router-evpn)# ", 1}; +static struct cmd_node bgp_evpn_node = { + .name = "bgp evpn", + .node = BGP_EVPN_NODE, + .parent_node = BGP_NODE, + .prompt = "%s(config-router-evpn)# ", +}; -static struct cmd_node bgp_evpn_vni_node = {BGP_EVPN_VNI_NODE, - "%s(config-router-af-vni)# ", 1}; +static struct cmd_node bgp_evpn_vni_node = { + .name = "bgp evpn vni", + .node = BGP_EVPN_VNI_NODE, + .parent_node = BGP_EVPN_NODE, + .prompt = "%s(config-router-af-vni)# ", +}; -static struct cmd_node bgp_flowspecv4_node = {BGP_FLOWSPECV4_NODE, - "%s(config-router-af)# ", 1}; +static struct cmd_node bgp_flowspecv4_node = { + .name = "bgp ipv4 flowspec", + .node = BGP_FLOWSPECV4_NODE, + .parent_node = BGP_NODE, + .prompt = "%s(config-router-af)# ", +}; -static struct cmd_node bgp_flowspecv6_node = {BGP_FLOWSPECV6_NODE, - "%s(config-router-af-vpnv6)# ", 1}; +static struct cmd_node bgp_flowspecv6_node = { + .name = "bgp ipv6 flowspec", + .node = BGP_FLOWSPECV6_NODE, + .parent_node = BGP_NODE, + .prompt = "%s(config-router-af-vpnv6)# ", +}; static void community_list_vty(void); @@ -12854,19 +16010,19 @@ void bgp_vty_init(void) cmd_variable_handler_register(bgp_var_peergroup); /* Install bgp top node. */ - install_node(&bgp_node, bgp_config_write); - install_node(&bgp_ipv4_unicast_node, NULL); - install_node(&bgp_ipv4_multicast_node, NULL); - install_node(&bgp_ipv4_labeled_unicast_node, NULL); - install_node(&bgp_ipv6_unicast_node, NULL); - install_node(&bgp_ipv6_multicast_node, NULL); - install_node(&bgp_ipv6_labeled_unicast_node, NULL); - install_node(&bgp_vpnv4_node, NULL); - install_node(&bgp_vpnv6_node, NULL); - install_node(&bgp_evpn_node, NULL); - install_node(&bgp_evpn_vni_node, NULL); - install_node(&bgp_flowspecv4_node, NULL); - install_node(&bgp_flowspecv6_node, NULL); + install_node(&bgp_node); + install_node(&bgp_ipv4_unicast_node); + install_node(&bgp_ipv4_multicast_node); + install_node(&bgp_ipv4_labeled_unicast_node); + install_node(&bgp_ipv6_unicast_node); + install_node(&bgp_ipv6_multicast_node); + install_node(&bgp_ipv6_labeled_unicast_node); + install_node(&bgp_vpnv4_node); + install_node(&bgp_vpnv6_node); + install_node(&bgp_evpn_node); + install_node(&bgp_evpn_vni_node); + install_node(&bgp_flowspecv4_node); + install_node(&bgp_flowspecv6_node); /* Install default VTY commands to new nodes. */ install_default(BGP_NODE); @@ -12883,14 +16039,6 @@ void bgp_vty_init(void) install_default(BGP_EVPN_NODE); install_default(BGP_EVPN_VNI_NODE); - /* "bgp multiple-instance" commands. */ - install_element(CONFIG_NODE, &bgp_multiple_instance_cmd); - install_element(CONFIG_NODE, &no_bgp_multiple_instance_cmd); - - /* "bgp config-type" commands. */ - install_element(CONFIG_NODE, &bgp_config_type_cmd); - install_element(CONFIG_NODE, &no_bgp_config_type_cmd); - /* "bgp local-mac" hidden commands. */ install_element(CONFIG_NODE, &bgp_local_mac_cmd); install_element(CONFIG_NODE, &no_bgp_local_mac_cmd); @@ -12899,6 +16047,10 @@ void bgp_vty_init(void) install_element(CONFIG_NODE, &bgp_set_route_map_delay_timer_cmd); install_element(CONFIG_NODE, &no_bgp_set_route_map_delay_timer_cmd); + /* global bgp update-delay command */ + install_element(CONFIG_NODE, &bgp_global_update_delay_cmd); + install_element(CONFIG_NODE, &no_bgp_global_update_delay_cmd); + /* Dummy commands (Currently not supported) */ install_element(BGP_NODE, &no_synchronization_cmd); install_element(BGP_NODE, &no_auto_summary_cmd); @@ -12939,12 +16091,9 @@ void bgp_vty_init(void) /* bgp update-delay command */ install_element(BGP_NODE, &bgp_update_delay_cmd); install_element(BGP_NODE, &no_bgp_update_delay_cmd); - install_element(BGP_NODE, &bgp_update_delay_establish_wait_cmd); install_element(BGP_NODE, &bgp_wpkt_quanta_cmd); - install_element(BGP_NODE, &no_bgp_wpkt_quanta_cmd); install_element(BGP_NODE, &bgp_rpkt_quanta_cmd); - install_element(BGP_NODE, &no_bgp_rpkt_quanta_cmd); install_element(BGP_NODE, &bgp_coalesce_time_cmd); install_element(BGP_NODE, &no_bgp_coalesce_time_cmd); @@ -12966,6 +16115,11 @@ void bgp_vty_init(void) install_element(BGP_IPV6_NODE, &bgp_maxpaths_ibgp_cluster_cmd); install_element(BGP_IPV6_NODE, &no_bgp_maxpaths_ibgp_cmd); + install_element(BGP_IPV4L_NODE, &bgp_maxpaths_cmd); + install_element(BGP_IPV4L_NODE, &no_bgp_maxpaths_cmd); + install_element(BGP_IPV4L_NODE, &bgp_maxpaths_ibgp_cmd); + install_element(BGP_IPV4L_NODE, &bgp_maxpaths_ibgp_cluster_cmd); + install_element(BGP_IPV4L_NODE, &no_bgp_maxpaths_ibgp_cmd); install_element(BGP_IPV6L_NODE, &bgp_maxpaths_cmd); install_element(BGP_IPV6L_NODE, &no_bgp_maxpaths_cmd); install_element(BGP_IPV6L_NODE, &bgp_maxpaths_ibgp_cmd); @@ -12993,21 +16147,53 @@ void bgp_vty_init(void) install_element(BGP_NODE, &bgp_ebgp_requires_policy_cmd); install_element(BGP_NODE, &no_bgp_ebgp_requires_policy_cmd); + /* bgp reject-as-sets */ + install_element(BGP_NODE, &bgp_reject_as_sets_cmd); + install_element(BGP_NODE, &no_bgp_reject_as_sets_cmd); + /* "bgp deterministic-med" commands */ install_element(BGP_NODE, &bgp_deterministic_med_cmd); install_element(BGP_NODE, &no_bgp_deterministic_med_cmd); - /* "bgp graceful-restart" commands */ + /* "bgp graceful-restart" command */ install_element(BGP_NODE, &bgp_graceful_restart_cmd); install_element(BGP_NODE, &no_bgp_graceful_restart_cmd); + + /* "bgp graceful-restart-disable" command */ + install_element(BGP_NODE, &bgp_graceful_restart_disable_cmd); + install_element(BGP_NODE, &no_bgp_graceful_restart_disable_cmd); + + /* "neighbor a:b:c:d graceful-restart" command */ + install_element(BGP_NODE, &bgp_neighbor_graceful_restart_set_cmd); + install_element(BGP_NODE, &no_bgp_neighbor_graceful_restart_set_cmd); + + /* "neighbor a:b:c:d graceful-restart-disable" command */ + install_element(BGP_NODE, + &bgp_neighbor_graceful_restart_disable_set_cmd); + install_element(BGP_NODE, + &no_bgp_neighbor_graceful_restart_disable_set_cmd); + + /* "neighbor a:b:c:d graceful-restart-helper" command */ + install_element(BGP_NODE, + &bgp_neighbor_graceful_restart_helper_set_cmd); + install_element(BGP_NODE, + &no_bgp_neighbor_graceful_restart_helper_set_cmd); + install_element(BGP_NODE, &bgp_graceful_restart_stalepath_time_cmd); install_element(BGP_NODE, &no_bgp_graceful_restart_stalepath_time_cmd); install_element(BGP_NODE, &bgp_graceful_restart_restart_time_cmd); install_element(BGP_NODE, &no_bgp_graceful_restart_restart_time_cmd); - + install_element(BGP_NODE, &bgp_graceful_restart_select_defer_time_cmd); + install_element(BGP_NODE, + &no_bgp_graceful_restart_select_defer_time_cmd); install_element(BGP_NODE, &bgp_graceful_restart_preserve_fw_cmd); install_element(BGP_NODE, &no_bgp_graceful_restart_preserve_fw_cmd); + install_element(BGP_NODE, &bgp_graceful_restart_disable_eor_cmd); + install_element(BGP_NODE, &no_bgp_graceful_restart_disable_eor_cmd); + install_element(BGP_NODE, &bgp_graceful_restart_rib_stale_time_cmd); + install_element(BGP_NODE, &no_bgp_graceful_restart_rib_stale_time_cmd); + /* "bgp graceful-shutdown" commands */ install_element(BGP_NODE, &bgp_graceful_shutdown_cmd); install_element(BGP_NODE, &no_bgp_graceful_shutdown_cmd); @@ -13016,9 +16202,6 @@ void bgp_vty_init(void) install_element(BGP_NODE, &bgp_fast_external_failover_cmd); install_element(BGP_NODE, &no_bgp_fast_external_failover_cmd); - /* "bgp enforce-first-as" commands */ - install_element(BGP_NODE, &bgp_enforce_first_as_cmd); - /* "bgp bestpath compare-routerid" commands */ install_element(BGP_NODE, &bgp_bestpath_compare_router_id_cmd); install_element(BGP_NODE, &no_bgp_bestpath_compare_router_id_cmd); @@ -13043,6 +16226,10 @@ void bgp_vty_init(void) install_element(BGP_NODE, &bgp_bestpath_med_cmd); install_element(BGP_NODE, &no_bgp_bestpath_med_cmd); + /* "bgp bestpath bandwidth" commands */ + install_element(BGP_NODE, &bgp_bestpath_bw_cmd); + install_element(BGP_NODE, &no_bgp_bestpath_bw_cmd); + /* "no bgp default ipv4-unicast" commands. */ install_element(BGP_NODE, &no_bgp_default_ipv4_unicast_cmd); install_element(BGP_NODE, &bgp_default_ipv4_unicast_cmd); @@ -13060,6 +16247,10 @@ void bgp_vty_init(void) install_element(BGP_NODE, &bgp_default_show_hostname_cmd); install_element(BGP_NODE, &no_bgp_default_show_hostname_cmd); + /* bgp default show-nexthop-hostname */ + install_element(BGP_NODE, &bgp_default_show_nexthop_hostname_cmd); + install_element(BGP_NODE, &no_bgp_default_show_nexthop_hostname_cmd); + /* "bgp default subgroup-pkt-queue-max" commands. */ install_element(BGP_NODE, &bgp_default_subgroup_pkt_queue_max_cmd); install_element(BGP_NODE, &no_bgp_default_subgroup_pkt_queue_max_cmd); @@ -13079,6 +16270,12 @@ void bgp_vty_init(void) /* "bgp default shutdown" command */ install_element(BGP_NODE, &bgp_default_shutdown_cmd); + /* "bgp shutdown" commands */ + install_element(BGP_NODE, &bgp_shutdown_cmd); + install_element(BGP_NODE, &bgp_shutdown_msg_cmd); + install_element(BGP_NODE, &no_bgp_shutdown_cmd); + install_element(BGP_NODE, &no_bgp_shutdown_msg_cmd); + /* "neighbor remote-as" commands. */ install_element(BGP_NODE, &neighbor_remote_as_cmd); install_element(BGP_NODE, &neighbor_interface_config_cmd); @@ -13252,20 +16449,46 @@ void bgp_vty_init(void) install_element(BGP_NODE, &no_neighbor_nexthop_self_all_hidden_cmd); install_element(BGP_IPV4_NODE, &neighbor_nexthop_self_force_cmd); install_element(BGP_IPV4_NODE, &no_neighbor_nexthop_self_force_cmd); + install_element(BGP_IPV4_NODE, &neighbor_nexthop_self_all_hidden_cmd); + install_element(BGP_IPV4_NODE, + &no_neighbor_nexthop_self_all_hidden_cmd); install_element(BGP_IPV4M_NODE, &neighbor_nexthop_self_force_cmd); install_element(BGP_IPV4M_NODE, &no_neighbor_nexthop_self_force_cmd); + install_element(BGP_IPV4M_NODE, &neighbor_nexthop_self_all_hidden_cmd); + install_element(BGP_IPV4M_NODE, + &no_neighbor_nexthop_self_all_hidden_cmd); install_element(BGP_IPV4L_NODE, &neighbor_nexthop_self_force_cmd); install_element(BGP_IPV4L_NODE, &no_neighbor_nexthop_self_force_cmd); + install_element(BGP_IPV4L_NODE, &neighbor_nexthop_self_all_hidden_cmd); + install_element(BGP_IPV4L_NODE, + &no_neighbor_nexthop_self_all_hidden_cmd); install_element(BGP_IPV6_NODE, &neighbor_nexthop_self_force_cmd); install_element(BGP_IPV6_NODE, &no_neighbor_nexthop_self_force_cmd); + install_element(BGP_IPV6_NODE, &neighbor_nexthop_self_all_hidden_cmd); + install_element(BGP_IPV6_NODE, + &no_neighbor_nexthop_self_all_hidden_cmd); install_element(BGP_IPV6M_NODE, &neighbor_nexthop_self_force_cmd); install_element(BGP_IPV6M_NODE, &no_neighbor_nexthop_self_force_cmd); + install_element(BGP_IPV6M_NODE, &neighbor_nexthop_self_all_hidden_cmd); + install_element(BGP_IPV6M_NODE, + &no_neighbor_nexthop_self_all_hidden_cmd); install_element(BGP_IPV6L_NODE, &neighbor_nexthop_self_force_cmd); install_element(BGP_IPV6L_NODE, &no_neighbor_nexthop_self_force_cmd); + install_element(BGP_IPV6L_NODE, &neighbor_nexthop_self_all_hidden_cmd); + install_element(BGP_IPV6L_NODE, + &no_neighbor_nexthop_self_all_hidden_cmd); install_element(BGP_VPNV4_NODE, &neighbor_nexthop_self_force_cmd); install_element(BGP_VPNV4_NODE, &no_neighbor_nexthop_self_force_cmd); + install_element(BGP_VPNV4_NODE, &neighbor_nexthop_self_all_hidden_cmd); + install_element(BGP_VPNV4_NODE, + &no_neighbor_nexthop_self_all_hidden_cmd); install_element(BGP_VPNV6_NODE, &neighbor_nexthop_self_force_cmd); install_element(BGP_VPNV6_NODE, &no_neighbor_nexthop_self_force_cmd); + install_element(BGP_VPNV6_NODE, &neighbor_nexthop_self_all_hidden_cmd); + install_element(BGP_VPNV6_NODE, + &no_neighbor_nexthop_self_all_hidden_cmd); + install_element(BGP_EVPN_NODE, &neighbor_nexthop_self_force_cmd); + install_element(BGP_EVPN_NODE, &no_neighbor_nexthop_self_force_cmd); /* "neighbor as-override" commands. */ install_element(BGP_NODE, &neighbor_as_override_hidden_cmd); @@ -13560,6 +16783,10 @@ void bgp_vty_init(void) install_element(BGP_VPNV6_NODE, &no_neighbor_addpath_tx_bestpath_per_as_cmd); + /* "neighbor sender-as-path-loop-detection" commands. */ + install_element(BGP_NODE, &neighbor_aspath_loop_detection_cmd); + install_element(BGP_NODE, &no_neighbor_aspath_loop_detection_cmd); + /* "neighbor passive" commands. */ install_element(BGP_NODE, &neighbor_passive_cmd); install_element(BGP_NODE, &no_neighbor_passive_cmd); @@ -13570,6 +16797,8 @@ void bgp_vty_init(void) install_element(BGP_NODE, &no_neighbor_shutdown_cmd); install_element(BGP_NODE, &neighbor_shutdown_msg_cmd); install_element(BGP_NODE, &no_neighbor_shutdown_msg_cmd); + install_element(BGP_NODE, &neighbor_shutdown_rtt_cmd); + install_element(BGP_NODE, &no_neighbor_shutdown_rtt_cmd); /* "neighbor capability extended-nexthop" commands.*/ install_element(BGP_NODE, &neighbor_capability_enhe_cmd); @@ -13808,6 +17037,26 @@ void bgp_vty_init(void) install_element(BGP_VPNV6_NODE, &neighbor_unsuppress_map_cmd); install_element(BGP_VPNV6_NODE, &no_neighbor_unsuppress_map_cmd); + /* neighbor maximum-prefix-out commands. */ + install_element(BGP_NODE, &neighbor_maximum_prefix_out_cmd); + install_element(BGP_NODE, &no_neighbor_maximum_prefix_out_cmd); + install_element(BGP_IPV4_NODE, &neighbor_maximum_prefix_out_cmd); + install_element(BGP_IPV4_NODE, &no_neighbor_maximum_prefix_out_cmd); + install_element(BGP_IPV4M_NODE, &neighbor_maximum_prefix_out_cmd); + install_element(BGP_IPV4M_NODE, &no_neighbor_maximum_prefix_out_cmd); + install_element(BGP_IPV4L_NODE, &neighbor_maximum_prefix_out_cmd); + install_element(BGP_IPV4L_NODE, &no_neighbor_maximum_prefix_out_cmd); + install_element(BGP_IPV6_NODE, &neighbor_maximum_prefix_out_cmd); + install_element(BGP_IPV6_NODE, &no_neighbor_maximum_prefix_out_cmd); + install_element(BGP_IPV6M_NODE, &neighbor_maximum_prefix_out_cmd); + install_element(BGP_IPV6M_NODE, &no_neighbor_maximum_prefix_out_cmd); + install_element(BGP_IPV6L_NODE, &neighbor_maximum_prefix_out_cmd); + install_element(BGP_IPV6L_NODE, &no_neighbor_maximum_prefix_out_cmd); + install_element(BGP_VPNV4_NODE, &neighbor_maximum_prefix_out_cmd); + install_element(BGP_VPNV4_NODE, &no_neighbor_maximum_prefix_out_cmd); + install_element(BGP_VPNV6_NODE, &neighbor_maximum_prefix_out_cmd); + install_element(BGP_VPNV6_NODE, &no_neighbor_maximum_prefix_out_cmd); + /* "neighbor maximum-prefix" commands. */ install_element(BGP_NODE, &neighbor_maximum_prefix_hidden_cmd); install_element(BGP_NODE, @@ -13957,6 +17206,8 @@ void bgp_vty_init(void) /* "show [ip] bgp neighbors" commands. */ install_element(VIEW_NODE, &show_ip_bgp_neighbors_cmd); + install_element(VIEW_NODE, &show_ip_bgp_neighbors_graceful_restart_cmd); + /* "show [ip] bgp peer-group" commands. */ install_element(VIEW_NODE, &show_ip_bgp_peer_groups_cmd); @@ -14061,8 +17312,6 @@ void bgp_vty_init(void) install_element(BGP_IPV6_NODE, &af_no_rd_vpn_export_cmd); install_element(BGP_IPV4_NODE, &af_no_label_vpn_export_cmd); install_element(BGP_IPV6_NODE, &af_no_label_vpn_export_cmd); - install_element(BGP_IPV4_NODE, &af_no_nexthop_vpn_export_cmd); - install_element(BGP_IPV6_NODE, &af_no_nexthop_vpn_export_cmd); install_element(BGP_IPV4_NODE, &af_no_rt_vpn_imexport_cmd); install_element(BGP_IPV6_NODE, &af_no_rt_vpn_imexport_cmd); install_element(BGP_IPV4_NODE, &af_no_route_map_vpn_imexport_cmd); @@ -14118,29 +17367,29 @@ static void community_list_perror(struct vty *vty, int ret) /*community-list standard */ DEFUN (community_list_standard, bgp_community_list_standard_cmd, - "bgp community-list <(1-99)|standard WORD> AA:NN...", + "bgp community-list <(1-99)|standard WORD> [seq (1-4294967295)] AA:NN...", BGP_STR COMMUNITY_LIST_STR "Community list number (standard)\n" "Add an standard community-list entry\n" "Community list name\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify community to reject\n" "Specify community to accept\n" COMMUNITY_VAL_STR) { char *cl_name_or_number = NULL; + char *seq = NULL; int direct = 0; int style = COMMUNITY_LIST_STANDARD; - int idx = 0; - if (argv_find(argv, argc, "ip", &idx)) { - vty_out(vty, "This config option is deprecated, and is scheduled for removal.\n"); - vty_out(vty, "if you are using this please migrate to the below command.\n"); - vty_out(vty, "'bgp community-list <(1-99)|(100-500)|standard|expanded> AA:NN'\n"); - zlog_warn("Deprecated option: 'ip community-list <(1-99)|(100-500)|standard|expanded> AA:NN' being used"); - } + argv_find(argv, argc, "(1-4294967295)", &idx); + if (idx) + seq = argv[idx]->arg; + idx = 0; argv_find(argv, argc, "(1-99)", &idx); argv_find(argv, argc, "WORD", &idx); cl_name_or_number = argv[idx]->arg; @@ -14149,8 +17398,8 @@ DEFUN (community_list_standard, argv_find(argv, argc, "AA:NN", &idx); char *str = argv_concat(argv, argc, idx); - int ret = community_list_set(bgp_clist, cl_name_or_number, str, direct, - style); + int ret = community_list_set(bgp_clist, cl_name_or_number, str, seq, + direct, style); XFREE(MTYPE_TMP, str); @@ -14163,30 +17412,17 @@ DEFUN (community_list_standard, return CMD_SUCCESS; } -#if CONFDATE > 20191005 -CPP_NOTICE("bgpd: remove deprecated 'ip community-list <(1-99)|(100-500)|standard|expanded> AA:NN' command") -#endif -ALIAS (community_list_standard, - ip_community_list_standard_cmd, - "ip community-list <(1-99)|standard WORD> AA:NN...", - IP_STR - COMMUNITY_LIST_STR - "Community list number (standard)\n" - "Add an standard community-list entry\n" - "Community list name\n" - "Specify community to reject\n" - "Specify community to accept\n" - COMMUNITY_VAL_STR) - DEFUN (no_community_list_standard_all, no_bgp_community_list_standard_all_cmd, - "no bgp community-list <(1-99)|standard WORD> AA:NN...", + "no bgp community-list <(1-99)|standard WORD> [seq (1-4294967295)] AA:NN...", NO_STR BGP_STR COMMUNITY_LIST_STR "Community list number (standard)\n" "Add an standard community-list entry\n" "Community list name\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify community to reject\n" "Specify community to accept\n" COMMUNITY_VAL_STR) @@ -14195,16 +17431,14 @@ DEFUN (no_community_list_standard_all, char *str = NULL; int direct = 0; int style = COMMUNITY_LIST_STANDARD; - + char *seq = NULL; int idx = 0; - if (argv_find(argv, argc, "ip", &idx)) { - vty_out(vty, "This config option is deprecated, and is scheduled for removal\n"); - vty_out(vty, "if you are using this please migrate to the below command.\n"); - vty_out(vty, "'no bgp community-list <(1-99)|(100-500)|standard|expanded> AA:NN'\n"); - zlog_warn("Deprecated option: 'no ip community-list <(1-99)|(100-500)|standard|expanded> |AA:NN' being used"); - } + argv_find(argv, argc, "(1-4294967295)", &idx); + if (idx) + seq = argv[idx]->arg; + idx = 0; argv_find(argv, argc, "permit", &idx); argv_find(argv, argc, "deny", &idx); @@ -14223,7 +17457,7 @@ DEFUN (no_community_list_standard_all, argv_find(argv, argc, "WORD", &idx); cl_name_or_number = argv[idx]->arg; - int ret = community_list_unset(bgp_clist, cl_name_or_number, str, + int ret = community_list_unset(bgp_clist, cl_name_or_number, str, seq, direct, style); XFREE(MTYPE_TMP, str); @@ -14235,18 +17469,6 @@ DEFUN (no_community_list_standard_all, return CMD_SUCCESS; } -ALIAS (no_community_list_standard_all, - no_ip_community_list_standard_all_cmd, - "no ip community-list <(1-99)|standard WORD> AA:NN...", - NO_STR - IP_STR - COMMUNITY_LIST_STR - "Community list number (standard)\n" - "Add an standard community-list entry\n" - "Community list name\n" - "Specify community to reject\n" - "Specify community to accept\n" - COMMUNITY_VAL_STR) ALIAS(no_community_list_standard_all, no_bgp_community_list_standard_all_list_cmd, "no bgp community-list <(1-99)|standard WORD>", @@ -14255,37 +17477,33 @@ ALIAS(no_community_list_standard_all, no_bgp_community_list_standard_all_list_cm "Add an standard community-list entry\n" "Community list name\n") -ALIAS(no_community_list_standard_all, no_ip_community_list_standard_all_list_cmd, - "no ip community-list <(1-99)|standard WORD>", - NO_STR BGP_STR COMMUNITY_LIST_STR - "Community list number (standard)\n" - "Add an standard community-list entry\n" - "Community list name\n") - /*community-list expanded */ DEFUN (community_list_expanded_all, bgp_community_list_expanded_all_cmd, - "bgp community-list <(100-500)|expanded WORD> AA:NN...", + "bgp community-list <(100-500)|expanded WORD> [seq (1-4294967295)] AA:NN...", BGP_STR COMMUNITY_LIST_STR "Community list number (expanded)\n" "Add an expanded community-list entry\n" "Community list name\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify community to reject\n" "Specify community to accept\n" COMMUNITY_VAL_STR) { char *cl_name_or_number = NULL; + char *seq = NULL; int direct = 0; int style = COMMUNITY_LIST_EXPANDED; - int idx = 0; - if (argv_find(argv, argc, "ip", &idx)) { - vty_out(vty, "This config option is deprecated, and is scheduled for removal.\n"); - vty_out(vty, "if you are using this please migrate to the below command.\n"); - vty_out(vty, "'bgp community-list <(1-99)|(100-500)|standard|expanded> AA:NN'\n"); - zlog_warn("Deprecated option: 'ip community-list <(1-99)|(100-500)|standard|expanded> AA:NN' being used"); - } + + argv_find(argv, argc, "(1-4294967295)", &idx); + if (idx) + seq = argv[idx]->arg; + + idx = 0; + argv_find(argv, argc, "(100-500)", &idx); argv_find(argv, argc, "WORD", &idx); cl_name_or_number = argv[idx]->arg; @@ -14294,8 +17512,8 @@ DEFUN (community_list_expanded_all, argv_find(argv, argc, "AA:NN", &idx); char *str = argv_concat(argv, argc, idx); - int ret = community_list_set(bgp_clist, cl_name_or_number, str, direct, - style); + int ret = community_list_set(bgp_clist, cl_name_or_number, str, seq, + direct, style); XFREE(MTYPE_TMP, str); @@ -14308,43 +17526,31 @@ DEFUN (community_list_expanded_all, return CMD_SUCCESS; } -ALIAS (community_list_expanded_all, - ip_community_list_expanded_all_cmd, - "ip community-list <(100-500)|expanded WORD> AA:NN...", - IP_STR - COMMUNITY_LIST_STR - "Community list number (expanded)\n" - "Add an expanded community-list entry\n" - "Community list name\n" - "Specify community to reject\n" - "Specify community to accept\n" - COMMUNITY_VAL_STR) - DEFUN (no_community_list_expanded_all, no_bgp_community_list_expanded_all_cmd, - "no bgp community-list <(100-500)|expanded WORD> AA:NN...", + "no bgp community-list <(100-500)|expanded WORD> [seq (1-4294967295)] AA:NN...", NO_STR BGP_STR COMMUNITY_LIST_STR "Community list number (expanded)\n" "Add an expanded community-list entry\n" "Community list name\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify community to reject\n" "Specify community to accept\n" COMMUNITY_VAL_STR) { char *cl_name_or_number = NULL; + char *seq = NULL; char *str = NULL; int direct = 0; int style = COMMUNITY_LIST_EXPANDED; - int idx = 0; - if (argv_find(argv, argc, "ip", &idx)) { - vty_out(vty, "This config option is deprecated, and is scheduled for removal.\n"); - vty_out(vty, "if you are using this please migrate to the below command.\n"); - vty_out(vty, "'no bgp community-list <(1-99)|(100-500)|standard|expanded> AA:NN'\n"); - zlog_warn("Deprecated option: 'no ip community-list <(1-99)|(100-500)|standard|expanded> AA:NN' being used"); - } + + argv_find(argv, argc, "(1-4294967295)", &idx); + if (idx) + seq = argv[idx]->arg; idx = 0; argv_find(argv, argc, "permit", &idx); @@ -14365,7 +17571,7 @@ DEFUN (no_community_list_expanded_all, argv_find(argv, argc, "WORD", &idx); cl_name_or_number = argv[idx]->arg; - int ret = community_list_unset(bgp_clist, cl_name_or_number, str, + int ret = community_list_unset(bgp_clist, cl_name_or_number, str, seq, direct, style); XFREE(MTYPE_TMP, str); @@ -14375,32 +17581,13 @@ DEFUN (no_community_list_expanded_all, return CMD_WARNING_CONFIG_FAILED; } - return CMD_SUCCESS; -} - -ALIAS (no_community_list_expanded_all, - no_ip_community_list_expanded_all_cmd, - "no ip community-list <(100-500)|expanded WORD> AA:NN...", - NO_STR - IP_STR - COMMUNITY_LIST_STR - "Community list number (expanded)\n" - "Add an expanded community-list entry\n" - "Community list name\n" - "Specify community to reject\n" - "Specify community to accept\n" - COMMUNITY_VAL_STR) - -ALIAS(no_community_list_expanded_all, no_bgp_community_list_expanded_all_list_cmd, - "no bgp community-list <(100-500)|expanded WORD>", - NO_STR IP_STR COMMUNITY_LIST_STR - "Community list number (expanded)\n" - "Add an expanded community-list entry\n" - "Community list name\n") + return CMD_SUCCESS; +} -ALIAS(no_community_list_expanded_all, no_ip_community_list_expanded_all_list_cmd, - "no ip community-list <(100-500)|expanded WORD>", - NO_STR IP_STR COMMUNITY_LIST_STR +ALIAS(no_community_list_expanded_all, + no_bgp_community_list_expanded_all_list_cmd, + "no bgp community-list <(100-500)|expanded WORD>", + NO_STR BGP_STR COMMUNITY_LIST_STR "Community list number (expanded)\n" "Add an expanded community-list entry\n" "Community list name\n") @@ -14462,13 +17649,6 @@ DEFUN (show_community_list, struct community_list *list; struct community_list_master *cm; - int idx = 0; - if (argv_find(argv, argc, "ip", &idx)) { - vty_out(vty, "This config option is deprecated, and is scheduled for removal.\n"); - vty_out(vty, "if you are using this please migrate to the below command.\n"); - vty_out(vty, "'show bgp community-list <(1-500)|WORD>'\n"); - zlog_warn("Deprecated option: 'ip show community-list <(1-500)|WORD>' being used"); - } cm = community_list_master_lookup(bgp_clist, COMMUNITY_LIST_MASTER); if (!cm) return CMD_SUCCESS; @@ -14482,32 +17662,19 @@ DEFUN (show_community_list, return CMD_SUCCESS; } -ALIAS (show_community_list, - show_ip_community_list_cmd, - "show ip community-list", - SHOW_STR - IP_STR - "List community-list\n") - DEFUN (show_community_list_arg, show_bgp_community_list_arg_cmd, - "show bgp community-list <(1-500)|WORD>", + "show bgp community-list <(1-500)|WORD> detail", SHOW_STR BGP_STR "List community-list\n" "Community-list number\n" - "Community-list name\n") + "Community-list name\n" + "Detailed information on community-list\n") { int idx_comm_list = 3; struct community_list *list; - int idx = 0; - if (argv_find(argv, argc, "ip", &idx)) { - vty_out(vty, "This config option is deprecated, and is scheduled for removal.\n"); - vty_out(vty, "if you are using this please migrate to the below command.\n"); - vty_out(vty, "'show bgp community-list <(1-500)|WORD>'\n"); - zlog_warn("Deprecated option: 'ip show community-list <(1-500)|WORD>' being used"); - } list = community_list_lookup(bgp_clist, argv[idx_comm_list]->arg, 0, COMMUNITY_LIST_MASTER); if (!list) { @@ -14520,15 +17687,6 @@ DEFUN (show_community_list_arg, return CMD_SUCCESS; } -ALIAS (show_community_list_arg, - show_ip_community_list_arg_cmd, - "show ip community-list <(1-500)|WORD>", - SHOW_STR - IP_STR - "List community-list\n" - "Community-list number\n" - "Community-list name\n") - /* * Large Community code. */ @@ -14541,13 +17699,12 @@ static int lcommunity_list_set_vty(struct vty *vty, int argc, char *str; int idx = 0; char *cl_name; + char *seq = NULL; - if (argv_find(argv, argc, "ip", &idx)) { - vty_out(vty, "This config option is deprecated, and is scheduled for removal.\n"); - vty_out(vty, "if you are using this please migrate to the below command.\n"); - vty_out(vty, "'bgp large-community-list <(1-99)|(100-500)|standard|expanded> '\n"); - zlog_warn("Deprecated option: 'large-community-list <(1-99)|(100-500)|standard|expanded> ' being used"); - } + if (argv_find(argv, argc, "(1-4294967295)", &idx)) + seq = argv[idx]->arg; + + idx = 0; direct = argv_find(argv, argc, "permit", &idx) ? COMMUNITY_PERMIT : COMMUNITY_DENY; @@ -14571,7 +17728,7 @@ static int lcommunity_list_set_vty(struct vty *vty, int argc, else str = NULL; - ret = lcommunity_list_set(bgp_clist, cl_name, str, direct, style); + ret = lcommunity_list_set(bgp_clist, cl_name, str, seq, direct, style); /* Free temporary community list string allocated by argv_concat(). */ @@ -14591,13 +17748,12 @@ static int lcommunity_list_unset_vty(struct vty *vty, int argc, int direct = 0; char *str = NULL; int idx = 0; + char *seq = NULL; - if (argv_find(argv, argc, "ip", &idx)) { - vty_out(vty, "This config option is deprecated, and is scheduled for removal.\n"); - vty_out(vty, "if you are using this please migrate to the below command.\n"); - vty_out(vty, "'no bgp large-community-list <(1-99)|(100-500)|standard|expanded> '\n"); - zlog_warn("Deprecated option: 'no ip large-community-list <(1-99)|(100-500)|standard|expanded> ' being used"); - } + if (argv_find(argv, argc, "(1-4294967295)", &idx)) + seq = argv[idx]->arg; + + idx = 0; argv_find(argv, argc, "permit", &idx); argv_find(argv, argc, "deny", &idx); @@ -14621,7 +17777,7 @@ static int lcommunity_list_unset_vty(struct vty *vty, int argc, argv_find(argv, argc, "WORD", &idx); /* Unset community list. */ - ret = lcommunity_list_unset(bgp_clist, argv[idx]->arg, str, direct, + ret = lcommunity_list_unset(bgp_clist, argv[idx]->arg, str, seq, direct, style); /* Free temporary community list string allocated by @@ -14640,37 +17796,14 @@ static int lcommunity_list_unset_vty(struct vty *vty, int argc, #define LCOMMUNITY_LIST_STR "Add a large community list entry\n" #define LCOMMUNITY_VAL_STR "large community in 'aa:bb:cc' format\n" -#if CONFDATE > 20191005 -CPP_NOTICE("bgpd: remove deprecated 'ip large-community-list <(1-99)|(100-500)|standard|expanded> ' command") -#endif DEFUN (lcommunity_list_standard, bgp_lcommunity_list_standard_cmd, - "bgp large-community-list (1-99) ", - BGP_STR - LCOMMUNITY_LIST_STR - "Large Community list number (standard)\n" - "Specify large community to reject\n" - "Specify large community to accept\n") -{ - return lcommunity_list_set_vty(vty, argc, argv, - LARGE_COMMUNITY_LIST_STANDARD, 0); -} - -ALIAS (lcommunity_list_standard, - ip_lcommunity_list_standard_cmd, - "ip large-community-list (1-99) ", - IP_STR - LCOMMUNITY_LIST_STR - "Large Community list number (standard)\n" - "Specify large community to reject\n" - "Specify large community to accept\n") - -DEFUN (lcommunity_list_standard1, - bgp_lcommunity_list_standard1_cmd, - "bgp large-community-list (1-99) AA:BB:CC...", + "bgp large-community-list (1-99) [seq (1-4294967295)] AA:BB:CC...", BGP_STR LCOMMUNITY_LIST_STR "Large Community list number (standard)\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify large community to reject\n" "Specify large community to accept\n" LCOMMUNITY_VAL_STR) @@ -14679,22 +17812,14 @@ DEFUN (lcommunity_list_standard1, LARGE_COMMUNITY_LIST_STANDARD, 0); } -ALIAS (lcommunity_list_standard1, - ip_lcommunity_list_standard1_cmd, - "ip large-community-list (1-99) AA:BB:CC...", - IP_STR - LCOMMUNITY_LIST_STR - "Large Community list number (standard)\n" - "Specify large community to reject\n" - "Specify large community to accept\n" - LCOMMUNITY_VAL_STR) - DEFUN (lcommunity_list_expanded, bgp_lcommunity_list_expanded_cmd, - "bgp large-community-list (100-500) LINE...", + "bgp large-community-list (100-500) [seq (1-4294967295)] LINE...", BGP_STR LCOMMUNITY_LIST_STR "Large Community list number (expanded)\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify large community to reject\n" "Specify large community to accept\n" "An ordered list as a regular-expression\n") @@ -14703,47 +17828,15 @@ DEFUN (lcommunity_list_expanded, LARGE_COMMUNITY_LIST_EXPANDED, 0); } -ALIAS (lcommunity_list_expanded, - ip_lcommunity_list_expanded_cmd, - "ip large-community-list (100-500) LINE...", - IP_STR - LCOMMUNITY_LIST_STR - "Large Community list number (expanded)\n" - "Specify large community to reject\n" - "Specify large community to accept\n" - "An ordered list as a regular-expression\n") - DEFUN (lcommunity_list_name_standard, bgp_lcommunity_list_name_standard_cmd, - "bgp large-community-list standard WORD ", - BGP_STR - LCOMMUNITY_LIST_STR - "Specify standard large-community-list\n" - "Large Community list name\n" - "Specify large community to reject\n" - "Specify large community to accept\n") -{ - return lcommunity_list_set_vty(vty, argc, argv, - LARGE_COMMUNITY_LIST_STANDARD, 1); -} - -ALIAS (lcommunity_list_name_standard, - ip_lcommunity_list_name_standard_cmd, - "ip large-community-list standard WORD ", - IP_STR - LCOMMUNITY_LIST_STR - "Specify standard large-community-list\n" - "Large Community list name\n" - "Specify large community to reject\n" - "Specify large community to accept\n") - -DEFUN (lcommunity_list_name_standard1, - bgp_lcommunity_list_name_standard1_cmd, - "bgp large-community-list standard WORD AA:BB:CC...", + "bgp large-community-list standard WORD [seq (1-4294967295)] AA:BB:CC...", BGP_STR LCOMMUNITY_LIST_STR "Specify standard large-community-list\n" "Large Community list name\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify large community to reject\n" "Specify large community to accept\n" LCOMMUNITY_VAL_STR) @@ -14752,24 +17845,15 @@ DEFUN (lcommunity_list_name_standard1, LARGE_COMMUNITY_LIST_STANDARD, 1); } -ALIAS (lcommunity_list_name_standard1, - ip_lcommunity_list_name_standard1_cmd, - "ip large-community-list standard WORD AA:BB:CC...", - IP_STR - LCOMMUNITY_LIST_STR - "Specify standard large-community-list\n" - "Large Community list name\n" - "Specify large community to reject\n" - "Specify large community to accept\n" - LCOMMUNITY_VAL_STR) - DEFUN (lcommunity_list_name_expanded, bgp_lcommunity_list_name_expanded_cmd, - "bgp large-community-list expanded WORD LINE...", + "bgp large-community-list expanded WORD [seq (1-4294967295)] LINE...", BGP_STR LCOMMUNITY_LIST_STR "Specify expanded large-community-list\n" "Large Community list name\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify large community to reject\n" "Specify large community to accept\n" "An ordered list as a regular-expression\n") @@ -14778,19 +17862,8 @@ DEFUN (lcommunity_list_name_expanded, LARGE_COMMUNITY_LIST_EXPANDED, 1); } -ALIAS (lcommunity_list_name_expanded, - ip_lcommunity_list_name_expanded_cmd, - "ip large-community-list expanded WORD LINE...", - IP_STR - LCOMMUNITY_LIST_STR - "Specify expanded large-community-list\n" - "Large Community list name\n" - "Specify large community to reject\n" - "Specify large community to accept\n" - "An ordered list as a regular-expression\n") - -DEFUN (no_lcommunity_list_standard_all, - no_bgp_lcommunity_list_standard_all_cmd, +DEFUN (no_lcommunity_list_all, + no_bgp_lcommunity_list_all_cmd, "no bgp large-community-list <(1-99)|(100-500)|WORD>", NO_STR BGP_STR @@ -14803,15 +17876,18 @@ DEFUN (no_lcommunity_list_standard_all, LARGE_COMMUNITY_LIST_STANDARD); } -ALIAS (no_lcommunity_list_standard_all, - no_ip_lcommunity_list_standard_all_cmd, - "no ip large-community-list <(1-99)|(100-500)|WORD>", +DEFUN (no_lcommunity_list_name_standard_all, + no_bgp_lcommunity_list_name_standard_all_cmd, + "no bgp large-community-list standard WORD", NO_STR - IP_STR + BGP_STR LCOMMUNITY_LIST_STR - "Large Community list number (standard)\n" - "Large Community list number (expanded)\n" + "Specify standard large-community-list\n" "Large Community list name\n") +{ + return lcommunity_list_unset_vty(vty, argc, argv, + LARGE_COMMUNITY_LIST_STANDARD); +} DEFUN (no_lcommunity_list_name_expanded_all, no_bgp_lcommunity_list_name_expanded_all_cmd, @@ -14826,22 +17902,15 @@ DEFUN (no_lcommunity_list_name_expanded_all, LARGE_COMMUNITY_LIST_EXPANDED); } -ALIAS (no_lcommunity_list_name_expanded_all, - no_ip_lcommunity_list_name_expanded_all_cmd, - "no ip large-community-list expanded WORD", - NO_STR - IP_STR - LCOMMUNITY_LIST_STR - "Specify expanded large-community-list\n" - "Large Community list name\n") - DEFUN (no_lcommunity_list_standard, no_bgp_lcommunity_list_standard_cmd, - "no bgp large-community-list (1-99) AA:AA:NN...", + "no bgp large-community-list (1-99) [seq (1-4294967295)] AA:AA:NN...", NO_STR BGP_STR LCOMMUNITY_LIST_STR "Large Community list number (standard)\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify large community to reject\n" "Specify large community to accept\n" LCOMMUNITY_VAL_STR) @@ -14850,24 +17919,15 @@ DEFUN (no_lcommunity_list_standard, LARGE_COMMUNITY_LIST_STANDARD); } -ALIAS (no_lcommunity_list_standard, - no_ip_lcommunity_list_standard_cmd, - "no ip large-community-list (1-99) AA:AA:NN...", - NO_STR - IP_STR - LCOMMUNITY_LIST_STR - "Large Community list number (standard)\n" - "Specify large community to reject\n" - "Specify large community to accept\n" - LCOMMUNITY_VAL_STR) - DEFUN (no_lcommunity_list_expanded, no_bgp_lcommunity_list_expanded_cmd, - "no bgp large-community-list (100-500) LINE...", + "no bgp large-community-list (100-500) [seq (1-4294967295)] LINE...", NO_STR BGP_STR LCOMMUNITY_LIST_STR "Large Community list number (expanded)\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify large community to reject\n" "Specify large community to accept\n" "An ordered list as a regular-expression\n") @@ -14876,25 +17936,16 @@ DEFUN (no_lcommunity_list_expanded, LARGE_COMMUNITY_LIST_EXPANDED); } -ALIAS (no_lcommunity_list_expanded, - no_ip_lcommunity_list_expanded_cmd, - "no ip large-community-list (100-500) LINE...", - NO_STR - IP_STR - LCOMMUNITY_LIST_STR - "Large Community list number (expanded)\n" - "Specify large community to reject\n" - "Specify large community to accept\n" - "An ordered list as a regular-expression\n") - DEFUN (no_lcommunity_list_name_standard, no_bgp_lcommunity_list_name_standard_cmd, - "no bgp large-community-list standard WORD AA:AA:NN...", + "no bgp large-community-list standard WORD [seq (1-4294967295)] AA:AA:NN...", NO_STR BGP_STR LCOMMUNITY_LIST_STR "Specify standard large-community-list\n" "Large Community list name\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify large community to reject\n" "Specify large community to accept\n" LCOMMUNITY_VAL_STR) @@ -14903,26 +17954,16 @@ DEFUN (no_lcommunity_list_name_standard, LARGE_COMMUNITY_LIST_STANDARD); } -ALIAS (no_lcommunity_list_name_standard, - no_ip_lcommunity_list_name_standard_cmd, - "no ip large-community-list standard WORD AA:AA:NN...", - NO_STR - IP_STR - LCOMMUNITY_LIST_STR - "Specify standard large-community-list\n" - "Large Community list name\n" - "Specify large community to reject\n" - "Specify large community to accept\n" - LCOMMUNITY_VAL_STR) - DEFUN (no_lcommunity_list_name_expanded, no_bgp_lcommunity_list_name_expanded_cmd, - "no bgp large-community-list expanded WORD LINE...", + "no bgp large-community-list expanded WORD [seq (1-4294967295)] LINE...", NO_STR BGP_STR LCOMMUNITY_LIST_STR "Specify expanded large-community-list\n" "Large community list name\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify large community to reject\n" "Specify large community to accept\n" "An ordered list as a regular-expression\n") @@ -14931,18 +17972,6 @@ DEFUN (no_lcommunity_list_name_expanded, LARGE_COMMUNITY_LIST_EXPANDED); } -ALIAS (no_lcommunity_list_name_expanded, - no_ip_lcommunity_list_name_expanded_cmd, - "no ip large-community-list expanded WORD LINE...", - NO_STR - IP_STR - LCOMMUNITY_LIST_STR - "Specify expanded large-community-list\n" - "Large community list name\n" - "Specify large community to reject\n" - "Specify large community to accept\n" - "An ordered list as a regular-expression\n") - static void lcommunity_list_show(struct vty *vty, struct community_list *list) { struct community_entry *entry; @@ -14951,14 +17980,16 @@ static void lcommunity_list_show(struct vty *vty, struct community_list *list) if (entry == list->head) { if (all_digit(list->name)) vty_out(vty, "Large community %s list %s\n", - entry->style == EXTCOMMUNITY_LIST_STANDARD + entry->style == + LARGE_COMMUNITY_LIST_STANDARD ? "standard" : "(expanded) access", list->name); else vty_out(vty, "Named large community %s list %s\n", - entry->style == EXTCOMMUNITY_LIST_STANDARD + entry->style == + LARGE_COMMUNITY_LIST_STANDARD ? "standard" : "expanded", list->name); @@ -14982,14 +18013,6 @@ DEFUN (show_lcommunity_list, { struct community_list *list; struct community_list_master *cm; - int idx = 0; - - if (argv_find(argv, argc, "ip", &idx)) { - vty_out(vty, "This config option is deprecated, and is scheduled for removal.\n"); - vty_out(vty, "if you are using this please migrate to the below command.\n"); - vty_out(vty, "'show bgp large-community-list <(1-500)|WORD>'\n"); - zlog_warn("Deprecated option: 'ip show large-community-list <(1-500)|WORD>' being used"); - } cm = community_list_master_lookup(bgp_clist, LARGE_COMMUNITY_LIST_MASTER); @@ -15005,36 +18028,22 @@ DEFUN (show_lcommunity_list, return CMD_SUCCESS; } -ALIAS (show_lcommunity_list, - show_ip_lcommunity_list_cmd, - "show ip large-community-list", - SHOW_STR - IP_STR - "List large-community list\n") - DEFUN (show_lcommunity_list_arg, show_bgp_lcommunity_list_arg_cmd, - "show bgp large-community-list <(1-500)|WORD>", + "show bgp large-community-list <(1-500)|WORD> detail", SHOW_STR BGP_STR "List large-community list\n" - "large-community-list number\n" - "large-community-list name\n") + "Large-community-list number\n" + "Large-community-list name\n" + "Detailed information on large-community-list\n") { struct community_list *list; - int idx = 0; - - if (argv_find(argv, argc, "ip", &idx)) { - vty_out(vty, "This config option is deprecated, and is scheduled for removal.\n"); - vty_out(vty, "if you are using this please migrate to the below command.\n"); - vty_out(vty, "'show bgp large-community-list <(1-500)|WORD>'\n"); - zlog_warn("Deprecated option: 'ip show large-community-list <(1-500)|WORD>' being used"); - } list = community_list_lookup(bgp_clist, argv[3]->arg, 0, LARGE_COMMUNITY_LIST_MASTER); if (!list) { - vty_out(vty, "%% Can't find extcommunity-list\n"); + vty_out(vty, "%% Can't find large-community-list\n"); return CMD_WARNING; } @@ -15043,27 +18052,20 @@ DEFUN (show_lcommunity_list_arg, return CMD_SUCCESS; } -ALIAS (show_lcommunity_list_arg, - show_ip_lcommunity_list_arg_cmd, - "show ip large-community-list <(1-500)|WORD>", - SHOW_STR - IP_STR - "List large-community list\n" - "large-community-list number\n" - "large-community-list name\n") - /* "extcommunity-list" keyword help string. */ #define EXTCOMMUNITY_LIST_STR "Add a extended community list entry\n" #define EXTCOMMUNITY_VAL_STR "Extended community attribute in 'rt aa:nn_or_IPaddr:nn' OR 'soo aa:nn_or_IPaddr:nn' format\n" DEFUN (extcommunity_list_standard, bgp_extcommunity_list_standard_cmd, - "bgp extcommunity-list <(1-99)|standard WORD> AA:NN...", + "bgp extcommunity-list <(1-99)|standard WORD> [seq (1-4294967295)] AA:NN...", BGP_STR EXTCOMMUNITY_LIST_STR "Extended Community list number (standard)\n" "Specify standard extcommunity-list\n" "Community list name\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify community to reject\n" "Specify community to accept\n" EXTCOMMUNITY_VAL_STR) @@ -15071,23 +18073,23 @@ DEFUN (extcommunity_list_standard, int style = EXTCOMMUNITY_LIST_STANDARD; int direct = 0; char *cl_number_or_name = NULL; + char *seq = NULL; int idx = 0; - if (argv_find(argv, argc, "ip", &idx)) { - vty_out(vty, "This config option is deprecated, and is scheduled for removal.\n"); - vty_out(vty, "if you are using this please migrate to the below command.\n"); - vty_out(vty, "'bgp extcommunity-list <(1-99)|(100-500)|standard|expanded> '\n"); - zlog_warn("Deprecated option: 'ip extcommunity-list <(1-99)|(100-500)|standard|expanded> ' being used"); - } + argv_find(argv, argc, "(1-99)", &idx); argv_find(argv, argc, "WORD", &idx); cl_number_or_name = argv[idx]->arg; + + if (argv_find(argv, argc, "(1-4294967295)", &idx)) + seq = argv[idx]->arg; + direct = argv_find(argv, argc, "permit", &idx) ? COMMUNITY_PERMIT : COMMUNITY_DENY; argv_find(argv, argc, "AA:NN", &idx); char *str = argv_concat(argv, argc, idx); - int ret = extcommunity_list_set(bgp_clist, cl_number_or_name, str, + int ret = extcommunity_list_set(bgp_clist, cl_number_or_name, str, seq, direct, style); XFREE(MTYPE_TMP, str); @@ -15100,29 +18102,16 @@ DEFUN (extcommunity_list_standard, return CMD_SUCCESS; } -#if CONFDATE > 20191005 -CPP_NOTICE("bgpd: remove deprecated 'ip extcommunity-list <(1-99)|(100-500)|standard|expanded> ' command") -#endif -ALIAS (extcommunity_list_standard, - ip_extcommunity_list_standard_cmd, - "ip extcommunity-list <(1-99)|standard WORD> AA:NN...", - IP_STR - EXTCOMMUNITY_LIST_STR - "Extended Community list number (standard)\n" - "Specify standard extcommunity-list\n" - "Community list name\n" - "Specify community to reject\n" - "Specify community to accept\n" - EXTCOMMUNITY_VAL_STR) - DEFUN (extcommunity_list_name_expanded, bgp_extcommunity_list_name_expanded_cmd, - "bgp extcommunity-list <(100-500)|expanded WORD> LINE...", + "bgp extcommunity-list <(100-500)|expanded WORD> [seq (1-4294967295)] LINE...", BGP_STR EXTCOMMUNITY_LIST_STR "Extended Community list number (expanded)\n" "Specify expanded extcommunity-list\n" "Extended Community list name\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify community to reject\n" "Specify community to accept\n" "An ordered list as a regular-expression\n") @@ -15130,24 +18119,22 @@ DEFUN (extcommunity_list_name_expanded, int style = EXTCOMMUNITY_LIST_EXPANDED; int direct = 0; char *cl_number_or_name = NULL; - + char *seq = NULL; int idx = 0; - if (argv_find(argv, argc, "ip", &idx)) { - vty_out(vty, "This config option is deprecated, and is scheduled for removal.\n"); - vty_out(vty, "if you are using this please migrate to the below command.\n"); - vty_out(vty, "'extcommunity-list <(1-99)|(100-500)|standard|expanded> '\n"); - zlog_warn("Deprecated option: ‘ip extcommunity-list <(1-99)|(100-500)|standard|expanded> ' being used"); - } argv_find(argv, argc, "(100-500)", &idx); argv_find(argv, argc, "WORD", &idx); cl_number_or_name = argv[idx]->arg; + + if (argv_find(argv, argc, "(1-4294967295)", &idx)) + seq = argv[idx]->arg; + direct = argv_find(argv, argc, "permit", &idx) ? COMMUNITY_PERMIT : COMMUNITY_DENY; argv_find(argv, argc, "LINE", &idx); char *str = argv_concat(argv, argc, idx); - int ret = extcommunity_list_set(bgp_clist, cl_number_or_name, str, + int ret = extcommunity_list_set(bgp_clist, cl_number_or_name, str, seq, direct, style); XFREE(MTYPE_TMP, str); @@ -15160,27 +18147,17 @@ DEFUN (extcommunity_list_name_expanded, return CMD_SUCCESS; } -ALIAS (extcommunity_list_name_expanded, - ip_extcommunity_list_name_expanded_cmd, - "ip extcommunity-list <(100-500)|expanded WORD> LINE...", - IP_STR - EXTCOMMUNITY_LIST_STR - "Extended Community list number (expanded)\n" - "Specify expanded extcommunity-list\n" - "Extended Community list name\n" - "Specify community to reject\n" - "Specify community to accept\n" - "An ordered list as a regular-expression\n") - DEFUN (no_extcommunity_list_standard_all, no_bgp_extcommunity_list_standard_all_cmd, - "no bgp extcommunity-list <(1-99)|standard WORD> AA:NN...", + "no bgp extcommunity-list <(1-99)|standard WORD> [seq (1-4294967295)] AA:NN...", NO_STR BGP_STR EXTCOMMUNITY_LIST_STR "Extended Community list number (standard)\n" "Specify standard extcommunity-list\n" "Community list name\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify community to reject\n" "Specify community to accept\n" EXTCOMMUNITY_VAL_STR) @@ -15189,19 +18166,15 @@ DEFUN (no_extcommunity_list_standard_all, int direct = 0; char *cl_number_or_name = NULL; char *str = NULL; - + char *seq = NULL; int idx = 0; - if (argv_find(argv, argc, "ip", &idx)) { - vty_out(vty, "This config option is deprecated, and is scheduled for removal\n"); - vty_out(vty, "if you are using this please migrate to the below command.\n"); - vty_out(vty, "'no bgp extcommunity-list <(1-99)|(100-500)|standard|expanded> '\n"); - zlog_warn("Deprecated option: ‘no ip extcommunity-list <(1-99)|(100-500)|standard|expanded> ' being used"); - } + + if (argv_find(argv, argc, "(1-4294967295)", &idx)) + seq = argv[idx]->arg; idx = 0; argv_find(argv, argc, "permit", &idx); argv_find(argv, argc, "deny", &idx); - if (idx) { direct = argv_find(argv, argc, "permit", &idx) ? COMMUNITY_PERMIT @@ -15218,7 +18191,7 @@ DEFUN (no_extcommunity_list_standard_all, cl_number_or_name = argv[idx]->arg; int ret = extcommunity_list_unset(bgp_clist, cl_number_or_name, str, - direct, style); + seq, direct, style); XFREE(MTYPE_TMP, str); @@ -15230,44 +18203,25 @@ DEFUN (no_extcommunity_list_standard_all, return CMD_SUCCESS; } -ALIAS (no_extcommunity_list_standard_all, - no_ip_extcommunity_list_standard_all_cmd, - "no ip extcommunity-list <(1-99)|standard WORD> AA:NN...", - NO_STR - IP_STR - EXTCOMMUNITY_LIST_STR - "Extended Community list number (standard)\n" - "Specify standard extcommunity-list\n" - "Community list name\n" - "Specify community to reject\n" - "Specify community to accept\n" - EXTCOMMUNITY_VAL_STR) - ALIAS(no_extcommunity_list_standard_all, no_bgp_extcommunity_list_standard_all_list_cmd, "no bgp extcommunity-list <(1-99)|standard WORD>", - NO_STR IP_STR EXTCOMMUNITY_LIST_STR - "Extended Community list number (standard)\n" - "Specify standard extcommunity-list\n" - "Community list name\n") - -ALIAS(no_extcommunity_list_standard_all, - no_ip_extcommunity_list_standard_all_list_cmd, - "no ip extcommunity-list <(1-99)|standard WORD>", - NO_STR IP_STR EXTCOMMUNITY_LIST_STR + NO_STR BGP_STR EXTCOMMUNITY_LIST_STR "Extended Community list number (standard)\n" "Specify standard extcommunity-list\n" "Community list name\n") DEFUN (no_extcommunity_list_expanded_all, no_bgp_extcommunity_list_expanded_all_cmd, - "no bgp extcommunity-list <(100-500)|expanded WORD> LINE...", + "no bgp extcommunity-list <(100-500)|expanded WORD> [seq (1-4294967295)] LINE...", NO_STR BGP_STR EXTCOMMUNITY_LIST_STR "Extended Community list number (expanded)\n" "Specify expanded extcommunity-list\n" "Extended Community list name\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify community to reject\n" "Specify community to accept\n" "An ordered list as a regular-expression\n") @@ -15276,14 +18230,11 @@ DEFUN (no_extcommunity_list_expanded_all, int direct = 0; char *cl_number_or_name = NULL; char *str = NULL; - + char *seq = NULL; int idx = 0; - if (argv_find(argv, argc, "ip", &idx)) { - vty_out(vty, "This config option is deprecated, and is scheduled for removal.\n"); - vty_out(vty, "if you are using this please migrate to the below command.\n"); - vty_out(vty, "'no bgp extcommunity-list <(1-99)|(100-500)|standard|expanded> '\n"); - zlog_warn("Deprecated option: ‘no ip extcommunity-list <(1-99)|(100-500)|standard|expanded> ' being used"); - } + + if (argv_find(argv, argc, "(1-4294967295)", &idx)) + seq = argv[idx]->arg; idx = 0; argv_find(argv, argc, "permit", &idx); @@ -15305,7 +18256,7 @@ DEFUN (no_extcommunity_list_expanded_all, cl_number_or_name = argv[idx]->arg; int ret = extcommunity_list_unset(bgp_clist, cl_number_or_name, str, - direct, style); + seq, direct, style); XFREE(MTYPE_TMP, str); @@ -15317,31 +18268,10 @@ DEFUN (no_extcommunity_list_expanded_all, return CMD_SUCCESS; } -ALIAS (no_extcommunity_list_expanded_all, - no_ip_extcommunity_list_expanded_all_cmd, - "no ip extcommunity-list <(100-500)|expanded WORD> LINE...", - NO_STR - IP_STR - EXTCOMMUNITY_LIST_STR - "Extended Community list number (expanded)\n" - "Specify expanded extcommunity-list\n" - "Extended Community list name\n" - "Specify community to reject\n" - "Specify community to accept\n" - "An ordered list as a regular-expression\n") - -ALIAS(no_extcommunity_list_expanded_all, - no_ip_extcommunity_list_expanded_all_list_cmd, - "no ip extcommunity-list <(100-500)|expanded WORD>", - NO_STR IP_STR EXTCOMMUNITY_LIST_STR - "Extended Community list number (expanded)\n" - "Specify expanded extcommunity-list\n" - "Extended Community list name\n") - ALIAS(no_extcommunity_list_expanded_all, no_bgp_extcommunity_list_expanded_all_list_cmd, "no bgp extcommunity-list <(100-500)|expanded WORD>", - NO_STR IP_STR EXTCOMMUNITY_LIST_STR + NO_STR BGP_STR EXTCOMMUNITY_LIST_STR "Extended Community list number (expanded)\n" "Specify expanded extcommunity-list\n" "Extended Community list name\n") @@ -15385,14 +18315,7 @@ DEFUN (show_extcommunity_list, { struct community_list *list; struct community_list_master *cm; - int idx = 0; - if (argv_find(argv, argc, "ip", &idx)) { - vty_out(vty, "This config option is deprecated, and is scheduled for removal\n"); - vty_out(vty, "if you are using this please migrate to the below command.\n"); - vty_out(vty, "'show bgp extcommunity-list <(1-500)|WORD>'\n"); - zlog_warn("Deprecated option: 'ip show extcommunity-list <(1-500)|WORD>' being used"); - } cm = community_list_master_lookup(bgp_clist, EXTCOMMUNITY_LIST_MASTER); if (!cm) return CMD_SUCCESS; @@ -15406,32 +18329,19 @@ DEFUN (show_extcommunity_list, return CMD_SUCCESS; } -ALIAS (show_extcommunity_list, - show_ip_extcommunity_list_cmd, - "show ip extcommunity-list", - SHOW_STR - IP_STR - "List extended-community list\n") - DEFUN (show_extcommunity_list_arg, show_bgp_extcommunity_list_arg_cmd, - "show bgp extcommunity-list <(1-500)|WORD>", + "show bgp extcommunity-list <(1-500)|WORD> detail", SHOW_STR BGP_STR "List extended-community list\n" "Extcommunity-list number\n" - "Extcommunity-list name\n") + "Extcommunity-list name\n" + "Detailed information on extcommunity-list\n") { int idx_comm_list = 3; struct community_list *list; - int idx = 0; - if (argv_find(argv, argc, "ip", &idx)) { - vty_out(vty, "This config option is deprecated, and is scheduled for removal.\n"); - vty_out(vty, "if you are using this please migrate to the below command.\n"); - vty_out(vty, "'show bgp extcommunity-list <(1-500)|WORD>'\n"); - zlog_warn("Deprecated option: 'ip show extcommunity-list <(1-500)|WORD>' being used"); - } list = community_list_lookup(bgp_clist, argv[idx_comm_list]->arg, 0, EXTCOMMUNITY_LIST_MASTER); if (!list) { @@ -15444,15 +18354,6 @@ DEFUN (show_extcommunity_list_arg, return CMD_SUCCESS; } -ALIAS (show_extcommunity_list_arg, - show_ip_extcommunity_list_arg_cmd, - "show ip extcommunity-list <(1-500)|WORD>", - SHOW_STR - IP_STR - "List extended-community list\n" - "Extcommunity-list number\n" - "Extcommunity-list name\n") - /* Display community-list and extcommunity-list configuration. */ static int community_list_config_write(struct vty *vty) { @@ -15466,18 +18367,22 @@ static int community_list_config_write(struct vty *vty) for (list = cm->num.head; list; list = list->next) for (entry = list->head; entry; entry = entry->next) { - vty_out(vty, "bgp community-list %s %s %s\n", list->name, + vty_out(vty, + "bgp community-list %s seq %" PRId64 " %s %s\n", + list->name, entry->seq, community_direct_str(entry->direct), community_list_config_str(entry)); write++; } for (list = cm->str.head; list; list = list->next) for (entry = list->head; entry; entry = entry->next) { - vty_out(vty, "bgp community-list %s %s %s %s\n", + vty_out(vty, + "bgp community-list %s %s seq %" PRId64 " %s %s\n", entry->style == COMMUNITY_LIST_STANDARD ? "standard" : "expanded", - list->name, community_direct_str(entry->direct), + list->name, entry->seq, + community_direct_str(entry->direct), community_list_config_str(entry)); write++; } @@ -15487,18 +18392,22 @@ static int community_list_config_write(struct vty *vty) for (list = cm->num.head; list; list = list->next) for (entry = list->head; entry; entry = entry->next) { - vty_out(vty, "bgp extcommunity-list %s %s %s\n", - list->name, community_direct_str(entry->direct), + vty_out(vty, + "bgp extcommunity-list %s seq %" PRId64 " %s %s\n", + list->name, entry->seq, + community_direct_str(entry->direct), community_list_config_str(entry)); write++; } for (list = cm->str.head; list; list = list->next) for (entry = list->head; entry; entry = entry->next) { - vty_out(vty, "bgp extcommunity-list %s %s %s %s\n", + vty_out(vty, + "bgp extcommunity-list %s %s seq %" PRId64" %s %s\n", entry->style == EXTCOMMUNITY_LIST_STANDARD ? "standard" : "expanded", - list->name, community_direct_str(entry->direct), + list->name, entry->seq, + community_direct_str(entry->direct), community_list_config_str(entry)); write++; } @@ -15510,18 +18419,22 @@ static int community_list_config_write(struct vty *vty) for (list = cm->num.head; list; list = list->next) for (entry = list->head; entry; entry = entry->next) { - vty_out(vty, "bgp large-community-list %s %s %s\n", - list->name, community_direct_str(entry->direct), + vty_out(vty, + "bgp large-community-list %s seq %" PRId64" %s %s\n", + list->name, entry->seq, + community_direct_str(entry->direct), community_list_config_str(entry)); write++; } for (list = cm->str.head; list; list = list->next) for (entry = list->head; entry; entry = entry->next) { - vty_out(vty, "bgp large-community-list %s %s %s %s\n", + vty_out(vty, + "bgp large-community-list %s %s seq %" PRId64" %s %s\n", + entry->style == LARGE_COMMUNITY_LIST_STANDARD ? "standard" : "expanded", - list->name, community_direct_str(entry->direct), + list->name, entry->seq, community_direct_str(entry->direct), community_list_config_str(entry)); write++; } @@ -15529,13 +18442,17 @@ static int community_list_config_write(struct vty *vty) return write; } +static int community_list_config_write(struct vty *vty); static struct cmd_node community_list_node = { - COMMUNITY_LIST_NODE, "", 1 /* Export to vtysh. */ + .name = "community list", + .node = COMMUNITY_LIST_NODE, + .prompt = "", + .config_write = community_list_config_write, }; static void community_list_vty(void) { - install_node(&community_list_node, community_list_config_write); + install_node(&community_list_node); /* Community-list. */ install_element(CONFIG_NODE, &bgp_community_list_standard_cmd); @@ -15546,14 +18463,6 @@ static void community_list_vty(void) install_element(CONFIG_NODE, &no_bgp_community_list_expanded_all_list_cmd); install_element(VIEW_NODE, &show_bgp_community_list_cmd); install_element(VIEW_NODE, &show_bgp_community_list_arg_cmd); - install_element(CONFIG_NODE, &ip_community_list_standard_cmd); - install_element(CONFIG_NODE, &ip_community_list_expanded_all_cmd); - install_element(CONFIG_NODE, &no_ip_community_list_standard_all_cmd); - install_element(CONFIG_NODE, &no_ip_community_list_standard_all_list_cmd); - install_element(CONFIG_NODE, &no_ip_community_list_expanded_all_cmd); - install_element(CONFIG_NODE, &no_ip_community_list_expanded_all_list_cmd); - install_element(VIEW_NODE, &show_ip_community_list_cmd); - install_element(VIEW_NODE, &show_ip_community_list_arg_cmd); /* Extcommunity-list. */ install_element(CONFIG_NODE, &bgp_extcommunity_list_standard_cmd); @@ -15566,23 +18475,15 @@ static void community_list_vty(void) &no_bgp_extcommunity_list_expanded_all_list_cmd); install_element(VIEW_NODE, &show_bgp_extcommunity_list_cmd); install_element(VIEW_NODE, &show_bgp_extcommunity_list_arg_cmd); - install_element(CONFIG_NODE, &ip_extcommunity_list_standard_cmd); - install_element(CONFIG_NODE, &ip_extcommunity_list_name_expanded_cmd); - install_element(CONFIG_NODE, &no_ip_extcommunity_list_standard_all_cmd); - install_element(CONFIG_NODE, &no_ip_extcommunity_list_standard_all_list_cmd); - install_element(CONFIG_NODE, &no_ip_extcommunity_list_expanded_all_cmd); - install_element(CONFIG_NODE, &no_ip_extcommunity_list_expanded_all_list_cmd); - install_element(VIEW_NODE, &show_ip_extcommunity_list_cmd); - install_element(VIEW_NODE, &show_ip_extcommunity_list_arg_cmd); /* Large Community List */ install_element(CONFIG_NODE, &bgp_lcommunity_list_standard_cmd); - install_element(CONFIG_NODE, &bgp_lcommunity_list_standard1_cmd); install_element(CONFIG_NODE, &bgp_lcommunity_list_expanded_cmd); install_element(CONFIG_NODE, &bgp_lcommunity_list_name_standard_cmd); - install_element(CONFIG_NODE, &bgp_lcommunity_list_name_standard1_cmd); install_element(CONFIG_NODE, &bgp_lcommunity_list_name_expanded_cmd); - install_element(CONFIG_NODE, &no_bgp_lcommunity_list_standard_all_cmd); + install_element(CONFIG_NODE, &no_bgp_lcommunity_list_all_cmd); + install_element(CONFIG_NODE, + &no_bgp_lcommunity_list_name_standard_all_cmd); install_element(CONFIG_NODE, &no_bgp_lcommunity_list_name_expanded_all_cmd); install_element(CONFIG_NODE, &no_bgp_lcommunity_list_standard_cmd); @@ -15591,19 +18492,4 @@ static void community_list_vty(void) install_element(CONFIG_NODE, &no_bgp_lcommunity_list_name_expanded_cmd); install_element(VIEW_NODE, &show_bgp_lcommunity_list_cmd); install_element(VIEW_NODE, &show_bgp_lcommunity_list_arg_cmd); - install_element(CONFIG_NODE, &ip_lcommunity_list_standard_cmd); - install_element(CONFIG_NODE, &ip_lcommunity_list_standard1_cmd); - install_element(CONFIG_NODE, &ip_lcommunity_list_expanded_cmd); - install_element(CONFIG_NODE, &ip_lcommunity_list_name_standard_cmd); - install_element(CONFIG_NODE, &ip_lcommunity_list_name_standard1_cmd); - install_element(CONFIG_NODE, &ip_lcommunity_list_name_expanded_cmd); - install_element(CONFIG_NODE, &no_ip_lcommunity_list_standard_all_cmd); - install_element(CONFIG_NODE, - &no_ip_lcommunity_list_name_expanded_all_cmd); - install_element(CONFIG_NODE, &no_ip_lcommunity_list_standard_cmd); - install_element(CONFIG_NODE, &no_ip_lcommunity_list_expanded_cmd); - install_element(CONFIG_NODE, &no_ip_lcommunity_list_name_standard_cmd); - install_element(CONFIG_NODE, &no_ip_lcommunity_list_name_expanded_cmd); - install_element(VIEW_NODE, &show_ip_lcommunity_list_cmd); - install_element(VIEW_NODE, &show_ip_lcommunity_list_arg_cmd); } diff --git a/bgpd/bgp_vty.h b/bgpd/bgp_vty.h index d9df2b4cfe..95eefbc36f 100644 --- a/bgpd/bgp_vty.h +++ b/bgpd/bgp_vty.h @@ -21,6 +21,8 @@ #ifndef _QUAGGA_BGP_VTY_H #define _QUAGGA_BGP_VTY_H +#include "bgpd/bgpd.h" +#include "stream.h" struct bgp; #define BGP_INSTANCE_HELP_STR "BGP view\nBGP VRF\nView/VRF name\n" @@ -44,9 +46,111 @@ struct bgp; "Address Family modifier\n" \ "Address Family modifier\n" +#define SHOW_GR_HEADER \ + "Codes: GR - Graceful Restart," \ + " * - Inheriting Global GR Config,\n" \ + " Restart - GR Mode-Restarting," \ + " Helper - GR Mode-Helper,\n" \ + " Disable - GR Mode-Disable.\n\n" + +#define BGP_SHOW_PEER_GR_CAPABILITY( \ + vty, p, use_json, json) \ + do { \ + bgp_show_neighbor_graceful_restart_local_mode( \ + vty, p, use_json, json); \ + bgp_show_neighbor_graceful_restart_remote_mode( \ + vty, p, use_json, json); \ + bgp_show_neighnor_graceful_restart_rbit( \ + vty, p, use_json, json); \ + bgp_show_neighbor_graceful_restart_time( \ + vty, p, use_json, json); \ + bgp_show_neighbor_graceful_restart_capability_per_afi_safi(\ + vty, p, use_json, json); \ + } while (0) + +#define VTY_BGP_GR_DEFINE_LOOP_VARIABLE \ + struct peer *peer_loop = NULL; \ + struct listnode *node = NULL; \ + struct listnode *nnode = NULL; \ + bool gr_router_detected = false + +#define VTY_BGP_GR_ROUTER_DETECT(_bgp, _peer, _peer_list) \ + do { \ + if (_peer->bgp->t_startup) \ + bgp_peer_gr_flags_update(_peer); \ + for (ALL_LIST_ELEMENTS(_peer_list, node, nnode, peer_loop)) { \ + if (CHECK_FLAG(peer_loop->flags, \ + PEER_FLAG_GRACEFUL_RESTART)) \ + gr_router_detected = true; \ + } \ + } while (0) + + +#define VTY_SEND_BGP_GR_CAPABILITY_TO_ZEBRA(_bgp, _ret) \ + do { \ + if (gr_router_detected \ + && _bgp->present_zebra_gr_state == ZEBRA_GR_DISABLE) { \ + if (bgp_zebra_send_capabilities(_bgp, false)) \ + _ret = BGP_ERR_INVALID_VALUE; \ + } else if (!gr_router_detected \ + && _bgp->present_zebra_gr_state \ + == ZEBRA_GR_ENABLE) { \ + if (bgp_zebra_send_capabilities(_bgp, true)) \ + _ret = BGP_ERR_INVALID_VALUE; \ + } \ + } while (0) + +#define VTY_BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA( \ + _bgp, _peer_list, _ret) \ + do { \ + struct peer *peer_loop; \ + bool gr_router_detected = false; \ + struct listnode *node = {0}; \ + struct listnode *nnode = {0}; \ + for (ALL_LIST_ELEMENTS(_peer_list, node, nnode, peer_loop)) { \ + if (peer_loop->bgp->t_startup) \ + bgp_peer_gr_flags_update(peer_loop); \ + if (CHECK_FLAG(peer_loop->flags, \ + PEER_FLAG_GRACEFUL_RESTART)) \ + gr_router_detected = true; \ + } \ + if (gr_router_detected \ + && _bgp->present_zebra_gr_state == ZEBRA_GR_DISABLE) { \ + if (bgp_zebra_send_capabilities(_bgp, false)) \ + _ret = BGP_ERR_INVALID_VALUE; \ + } else if (!gr_router_detected \ + && _bgp->present_zebra_gr_state \ + == ZEBRA_GR_ENABLE) { \ + if (bgp_zebra_send_capabilities(_bgp, true)) \ + _ret = BGP_ERR_INVALID_VALUE; \ + } \ + } while (0) + + +#define PRINT_EOR(_eor_flag) \ + do { \ + if (eor_flag) \ + vty_out(vty, "Yes\n"); \ + else \ + vty_out(vty, "No\n"); \ + } while (0) + +#define PRINT_EOR_JSON(_eor_flag) \ + do { \ + if (eor_flag) \ + json_object_boolean_true_add( \ + json_endofrib_status, \ + "endOfRibSentAfterUpdate"); \ + else \ + json_object_boolean_false_add( \ + json_endofrib_status, \ + "endOfRibSentAfterUpdate"); \ + } while (0) + extern void bgp_vty_init(void); -extern const char *afi_safi_print(afi_t afi, safi_t safi); -extern const char *afi_safi_json(afi_t afi, safi_t safi); +extern const char *get_afi_safi_str(afi_t afi, safi_t safi, bool for_json); +extern int bgp_get_vty(struct bgp **bgp, as_t *as, const char *name, + enum bgp_instance_type inst_type); extern void bgp_config_write_update_delay(struct vty *vty, struct bgp *bgp); extern void bgp_config_write_wpkt_quanta(struct vty *vty, struct bgp *bgp); extern void bgp_config_write_rpkt_quanta(struct vty *vty, struct bgp *bgp); @@ -71,8 +175,10 @@ extern int bgp_vty_find_and_parse_afi_safi_bgp(struct vty *vty, int argc, int *idx, afi_t *afi, safi_t *safi, struct bgp **bgp, bool use_json); +int bgp_vty_find_and_parse_bgp(struct vty *vty, struct cmd_token **argv, + int argc, struct bgp **bgp, bool use_json); extern int bgp_show_summary_vty(struct vty *vty, const char *name, afi_t afi, - safi_t safi, bool use_json); -extern void bgp_vpn_policy_config_write_afi(struct vty *vty, struct bgp *bgp, - afi_t afi); + safi_t safi, bool show_failed, + bool show_established, bool use_json); + #endif /* _QUAGGA_BGP_VTY_H */ diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index e42d6ee260..de1db4f8e2 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -51,7 +51,7 @@ #include "bgpd/bgp_nht.h" #include "bgpd/bgp_bfd.h" #include "bgpd/bgp_label.h" -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC #include "bgpd/rfapi/rfapi_backend.h" #include "bgpd/rfapi/vnc_export_bgp.h" #endif @@ -60,31 +60,32 @@ #include "bgpd/bgp_labelpool.h" #include "bgpd/bgp_pbr.h" #include "bgpd/bgp_evpn_private.h" +#include "bgpd/bgp_evpn_mh.h" #include "bgpd/bgp_mac.h" /* All information about zebra. */ struct zclient *zclient = NULL; /* Can we install into zebra? */ -static inline int bgp_install_info_to_zebra(struct bgp *bgp) +static inline bool bgp_install_info_to_zebra(struct bgp *bgp) { if (zclient->sock <= 0) - return 0; + return false; if (!IS_BGP_INST_KNOWN_TO_ZEBRA(bgp)) { - zlog_debug("%s: No zebra instance to talk to, not installing information", - __PRETTY_FUNCTION__); - return 0; + zlog_debug( + "%s: No zebra instance to talk to, not installing information", + __func__); + return false; } - return 1; + return true; } int zclient_num_connects; /* Router-id update message from zebra. */ -static int bgp_router_id_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int bgp_router_id_update(ZAPI_CALLBACK_ARGS) { struct prefix router_id; @@ -101,17 +102,15 @@ static int bgp_router_id_update(int command, struct zclient *zclient, } /* Nexthop update message from zebra. */ -static int bgp_read_nexthop_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int bgp_read_nexthop_update(ZAPI_CALLBACK_ARGS) { - bgp_parse_nexthop_update(command, vrf_id); + bgp_parse_nexthop_update(cmd, vrf_id); return 0; } -static int bgp_read_import_check_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int bgp_read_import_check_update(ZAPI_CALLBACK_ARGS) { - bgp_parse_nexthop_update(command, vrf_id); + bgp_parse_nexthop_update(cmd, vrf_id); return 0; } @@ -205,78 +204,36 @@ static void bgp_nbr_connected_delete(struct bgp *bgp, struct nbr_connected *ifc, } } -/* Inteface addition message from zebra. */ -static int bgp_interface_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) -{ - struct interface *ifp; - struct bgp *bgp; - - ifp = zebra_interface_add_read(zclient->ibuf, vrf_id); - if (!ifp) // unexpected - return 0; - - if (BGP_DEBUG(zebra, ZEBRA) && ifp) - zlog_debug("Rx Intf add VRF %u IF %s", vrf_id, ifp->name); - - bgp = bgp_lookup_by_vrf_id(vrf_id); - if (!bgp) - return 0; - - bgp_mac_add_mac_entry(ifp); - - bgp_update_interface_nbrs(bgp, ifp, ifp); - return 0; -} - -static int bgp_interface_delete(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int bgp_ifp_destroy(struct interface *ifp) { - struct stream *s; - struct interface *ifp; struct bgp *bgp; - bgp = bgp_lookup_by_vrf_id(vrf_id); - - s = zclient->ibuf; - ifp = zebra_interface_state_read(s, vrf_id); - if (!ifp) /* This may happen if we've just unregistered for a VRF. */ - return 0; + bgp = bgp_lookup_by_vrf_id(ifp->vrf_id); if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug("Rx Intf del VRF %u IF %s", vrf_id, ifp->name); + zlog_debug("Rx Intf del VRF %u IF %s", ifp->vrf_id, ifp->name); if (bgp) bgp_update_interface_nbrs(bgp, ifp, NULL); bgp_mac_del_mac_entry(ifp); - if_set_index(ifp, IFINDEX_INTERNAL); return 0; } -static int bgp_interface_up(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int bgp_ifp_up(struct interface *ifp) { - struct stream *s; - struct interface *ifp; struct connected *c; struct nbr_connected *nc; struct listnode *node, *nnode; struct bgp *bgp; - bgp = bgp_lookup_by_vrf_id(vrf_id); - - s = zclient->ibuf; - ifp = zebra_interface_state_read(s, vrf_id); - - if (!ifp) - return 0; + bgp = bgp_lookup_by_vrf_id(ifp->vrf_id); bgp_mac_add_mac_entry(ifp); if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug("Rx Intf up VRF %u IF %s", vrf_id, ifp->name); + zlog_debug("Rx Intf up VRF %u IF %s", ifp->vrf_id, ifp->name); if (!bgp) return 0; @@ -290,28 +247,20 @@ static int bgp_interface_up(int command, struct zclient *zclient, return 0; } -static int bgp_interface_down(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int bgp_ifp_down(struct interface *ifp) { - struct stream *s; - struct interface *ifp; struct connected *c; struct nbr_connected *nc; struct listnode *node, *nnode; struct bgp *bgp; struct peer *peer; - bgp = bgp_lookup_by_vrf_id(vrf_id); - - s = zclient->ibuf; - ifp = zebra_interface_state_read(s, vrf_id); - if (!ifp) - return 0; + bgp = bgp_lookup_by_vrf_id(ifp->vrf_id); bgp_mac_del_mac_entry(ifp); if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug("Rx Intf down VRF %u IF %s", vrf_id, ifp->name); + zlog_debug("Rx Intf down VRF %u IF %s", ifp->vrf_id, ifp->name); if (!bgp) return 0; @@ -331,12 +280,14 @@ static int bgp_interface_down(int command, struct zclient *zclient, * 1-hop BFD * tracked (directly connected) IBGP peers. */ - if ((peer->ttl != 1) && (peer->gtsm_hops != 1) + if ((peer->ttl != BGP_DEFAULT_TTL) + && (peer->gtsm_hops != BGP_GTSM_HOPS_CONNECTED) && (!peer->bfd_info || bgp_bfd_is_peer_multihop(peer))) #else /* Take down directly connected EBGP peers */ - if ((peer->ttl != 1) && (peer->gtsm_hops != 1)) + if ((peer->ttl != BGP_DEFAULT_TTL) + && (peer->gtsm_hops != BGP_GTSM_HOPS_CONNECTED)) #endif continue; @@ -350,15 +301,14 @@ static int bgp_interface_down(int command, struct zclient *zclient, return 0; } -static int bgp_interface_address_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int bgp_interface_address_add(ZAPI_CALLBACK_ARGS) { struct connected *ifc; struct bgp *bgp; bgp = bgp_lookup_by_vrf_id(vrf_id); - ifc = zebra_interface_address_read(command, zclient->ibuf, vrf_id); + ifc = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); if (ifc == NULL) return 0; @@ -388,15 +338,14 @@ static int bgp_interface_address_add(int command, struct zclient *zclient, return 0; } -static int bgp_interface_address_delete(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int bgp_interface_address_delete(ZAPI_CALLBACK_ARGS) { struct connected *ifc; struct bgp *bgp; bgp = bgp_lookup_by_vrf_id(vrf_id); - ifc = zebra_interface_address_read(command, zclient->ibuf, vrf_id); + ifc = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); if (ifc == NULL) return 0; @@ -412,18 +361,17 @@ static int bgp_interface_address_delete(int command, struct zclient *zclient, bgp_connected_delete(bgp, ifc); } - connected_free(ifc); + connected_free(&ifc); return 0; } -static int bgp_interface_nbr_address_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int bgp_interface_nbr_address_add(ZAPI_CALLBACK_ARGS) { struct nbr_connected *ifc = NULL; struct bgp *bgp; - ifc = zebra_interface_nbr_address_read(command, zclient->ibuf, vrf_id); + ifc = zebra_interface_nbr_address_read(cmd, zclient->ibuf, vrf_id); if (ifc == NULL) return 0; @@ -444,15 +392,12 @@ static int bgp_interface_nbr_address_add(int command, struct zclient *zclient, return 0; } -static int bgp_interface_nbr_address_delete(int command, - struct zclient *zclient, - zebra_size_t length, - vrf_id_t vrf_id) +static int bgp_interface_nbr_address_delete(ZAPI_CALLBACK_ARGS) { struct nbr_connected *ifc = NULL; struct bgp *bgp; - ifc = zebra_interface_nbr_address_read(command, zclient->ibuf, vrf_id); + ifc = zebra_interface_nbr_address_read(cmd, zclient->ibuf, vrf_id); if (ifc == NULL) return 0; @@ -476,8 +421,7 @@ static int bgp_interface_nbr_address_delete(int command, } /* VRF update for an interface. */ -static int bgp_interface_vrf_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int bgp_interface_vrf_update(ZAPI_CALLBACK_ARGS) { struct interface *ifp; vrf_id_t new_vrf_id; @@ -508,7 +452,9 @@ static int bgp_interface_vrf_update(int command, struct zclient *zclient, /* Fast external-failover */ if (!CHECK_FLAG(bgp->flags, BGP_FLAG_NO_FAST_EXT_FAILOVER)) { for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { - if ((peer->ttl != 1) && (peer->gtsm_hops != 1)) + if ((peer->ttl != BGP_DEFAULT_TTL) + && (peer->gtsm_hops + != BGP_GTSM_HOPS_CONNECTED)) continue; if (ifp == peer->nexthop.ifp) @@ -532,8 +478,7 @@ static int bgp_interface_vrf_update(int command, struct zclient *zclient, } /* Zebra route add and delete treatment. */ -static int zebra_read_route(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int zebra_read_route(ZAPI_CALLBACK_ARGS) { enum nexthop_types_t nhtype; struct zapi_route api; @@ -562,7 +507,7 @@ static int zebra_read_route(int command, struct zclient *zclient, ifindex = api.nexthops[0].ifindex; nhtype = api.nexthops[0].type; - add = (command == ZEBRA_REDISTRIBUTE_ROUTE_ADD); + add = (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD); if (add) { /* * The ADD message is actually an UPDATE and there is no @@ -808,6 +753,7 @@ bool bgp_zebra_nexthop_set(union sockunion *local, union sockunion *remote, peer->bgp->vrf_id); } if (local->sa.sa_family == AF_INET6) { + memcpy(&nexthop->v6_global, &local->sin6.sin6_addr, IPV6_MAX_BYTELEN); if (IN6_IS_ADDR_LINKLOCAL(&local->sin6.sin6_addr)) { if (peer->conf_if || peer->ifname) ifp = if_lookup_by_name(peer->conf_if @@ -876,7 +822,7 @@ bool bgp_zebra_nexthop_set(union sockunion *local, union sockunion *remote, /* IPv4 nexthop. */ ret = if_get_ipv4_address(ifp, &nexthop->v4); - if (!ret && peer->local_id.s_addr) + if (!ret && peer->local_id.s_addr != INADDR_ANY) nexthop->v4 = peer->local_id; /* Global address*/ @@ -965,6 +911,7 @@ bgp_path_info_to_ipv6_nexthop(struct bgp_path_info *path, ifindex_t *ifindex) /* Workaround for Cisco's nexthop bug. */ if (IN6_IS_ADDR_UNSPECIFIED( &path->attr->mp_nexthop_global) + && path->peer->su_remote && path->peer->su_remote->sa.sa_family == AF_INET6) { nexthop = @@ -983,8 +930,8 @@ bgp_path_info_to_ipv6_nexthop(struct bgp_path_info *path, ifindex_t *ifindex) return nexthop; } -static int bgp_table_map_apply(struct route_map *map, struct prefix *p, - struct bgp_path_info *path) +static bool bgp_table_map_apply(struct route_map *map, const struct prefix *p, + struct bgp_path_info *path) { route_map_result_t ret; @@ -992,7 +939,7 @@ static int bgp_table_map_apply(struct route_map *map, struct prefix *p, bgp_attr_flush(path->attr); if (ret != RMAP_DENYMATCH) - return 1; + return true; if (bgp_debug_zebra(p)) { if (p->family == AF_INET) { @@ -1020,7 +967,7 @@ static int bgp_table_map_apply(struct route_map *map, struct prefix *p, buf[1], sizeof(buf[1]))); } } - return 0; + return false; } static struct thread *bgp_tm_thread_connect; @@ -1113,12 +1060,10 @@ int bgp_zebra_get_table_range(uint32_t chunk_size, return 0; } -static int update_ipv4nh_for_route_install(int nh_othervrf, - struct bgp *nh_bgp, - struct in_addr *nexthop, - struct attr *attr, - bool is_evpn, - struct zapi_nexthop *api_nh) +static bool update_ipv4nh_for_route_install(int nh_othervrf, struct bgp *nh_bgp, + struct in_addr *nexthop, + struct attr *attr, bool is_evpn, + struct zapi_nexthop *api_nh) { api_nh->gate.ipv4 = *nexthop; api_nh->vrf_id = nh_bgp->vrf_id; @@ -1129,7 +1074,7 @@ static int update_ipv4nh_for_route_install(int nh_othervrf, */ if (is_evpn) { api_nh->type = NEXTHOP_TYPE_IPV4_IFINDEX; - api_nh->onlink = true; + SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_ONLINK); api_nh->ifindex = nh_bgp->l3vni_svi_ifindex; } else if (nh_othervrf && api_nh->gate.ipv4.s_addr == INADDR_ANY) { @@ -1138,15 +1083,16 @@ static int update_ipv4nh_for_route_install(int nh_othervrf, } else api_nh->type = NEXTHOP_TYPE_IPV4; - return 1; + return true; } -static int -update_ipv6nh_for_route_install(int nh_othervrf, struct bgp *nh_bgp, - struct in6_addr *nexthop, - ifindex_t ifindex, struct bgp_path_info *pi, - struct bgp_path_info *best_pi, bool is_evpn, - struct zapi_nexthop *api_nh) +static bool update_ipv6nh_for_route_install(int nh_othervrf, struct bgp *nh_bgp, + struct in6_addr *nexthop, + ifindex_t ifindex, + struct bgp_path_info *pi, + struct bgp_path_info *best_pi, + bool is_evpn, + struct zapi_nexthop *api_nh) { struct attr *attr; @@ -1155,7 +1101,7 @@ update_ipv6nh_for_route_install(int nh_othervrf, struct bgp *nh_bgp, if (is_evpn) { api_nh->type = NEXTHOP_TYPE_IPV6_IFINDEX; - api_nh->onlink = true; + SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_ONLINK); api_nh->ifindex = nh_bgp->l3vni_svi_ifindex; } else if (nh_othervrf) { if (IN6_IS_ADDR_UNSPECIFIED(nexthop)) { @@ -1163,7 +1109,7 @@ update_ipv6nh_for_route_install(int nh_othervrf, struct bgp *nh_bgp, api_nh->ifindex = attr->nh_ifindex; } else if (IN6_IS_ADDR_LINKLOCAL(nexthop)) { if (ifindex == 0) - return 0; + return false; api_nh->type = NEXTHOP_TYPE_IPV6_IFINDEX; api_nh->ifindex = ifindex; } else { @@ -1191,7 +1137,7 @@ update_ipv6nh_for_route_install(int nh_othervrf, struct bgp *nh_bgp, } if (ifindex == 0) - return 0; + return false; api_nh->type = NEXTHOP_TYPE_IPV6_IFINDEX; api_nh->ifindex = ifindex; } else { @@ -1199,16 +1145,42 @@ update_ipv6nh_for_route_install(int nh_othervrf, struct bgp *nh_bgp, api_nh->ifindex = 0; } } - api_nh->gate.ipv6 = *nexthop; + if (nexthop) + api_nh->gate.ipv6 = *nexthop; + + return true; +} + +static bool bgp_zebra_use_nhop_weighted(struct bgp *bgp, struct attr *attr, + uint64_t tot_bw, uint32_t *nh_weight) +{ + uint32_t bw; + uint64_t tmp; + + bw = attr->link_bw; + /* zero link-bandwidth and link-bandwidth not present are treated + * as the same situation. + */ + if (!bw) { + /* the only situations should be if we're either told + * to skip or use default weight. + */ + if (bgp->lb_handling == BGP_LINK_BW_SKIP_MISSING) + return false; + *nh_weight = BGP_ZEBRA_DEFAULT_NHOP_WEIGHT; + } else { + tmp = (uint64_t)bw * 100; + *nh_weight = ((uint32_t)(tmp / tot_bw)); + } - return 1; + return true; } -void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, +void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p, struct bgp_path_info *info, struct bgp *bgp, afi_t afi, safi_t safi) { - struct zapi_route api; + struct zapi_route api = { 0 }; struct zapi_nexthop *api_nh; int nh_family; unsigned int valid_nh_count = 0; @@ -1225,7 +1197,9 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, int nh_othervrf = 0; char buf_prefix[PREFIX_STRLEN]; /* filled in if we are debugging */ bool is_evpn; - int nh_updated; + bool nh_updated = false; + bool do_wt_ecmp; + uint64_t cum_bw = 0; /* Don't try to install if we're not connected to Zebra or Zebra doesn't * know of this instance. @@ -1240,7 +1214,8 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, prefix2str(p, buf_prefix, sizeof(buf_prefix)); if (safi == SAFI_FLOWSPEC) { - bgp_pbr_update_entry(bgp, &rn->p, info, afi, safi, true); + bgp_pbr_update_entry(bgp, bgp_dest_get_prefix(dest), info, afi, + safi, true); return; } @@ -1251,7 +1226,6 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, nh_othervrf = 1; /* Make Zebra API structure. */ - memset(&api, 0, sizeof(api)); api.vrf_id = bgp->vrf_id; api.type = ZEBRA_ROUTE_BGP; api.safi = safi; @@ -1282,19 +1256,36 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, SET_FLAG(api.flags, ZEBRA_FLAG_ALLOW_RECURSION); } - if ((peer->sort == BGP_PEER_EBGP && peer->ttl != 1) + if ((peer->sort == BGP_PEER_EBGP && peer->ttl != BGP_DEFAULT_TTL) || CHECK_FLAG(peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK) - || bgp_flag_check(bgp, BGP_FLAG_DISABLE_NH_CONNECTED_CHK)) + || CHECK_FLAG(bgp->flags, BGP_FLAG_DISABLE_NH_CONNECTED_CHK)) SET_FLAG(api.flags, ZEBRA_FLAG_ALLOW_RECURSION); + if (info->attr->rmap_table_id) { + SET_FLAG(api.message, ZAPI_MESSAGE_TABLEID); + api.tableid = info->attr->rmap_table_id; + } + + if (CHECK_FLAG(info->attr->flag, ATTR_FLAG_BIT(BGP_ATTR_SRTE_COLOR))) + SET_FLAG(api.message, ZAPI_MESSAGE_SRTE); + /* Metric is currently based on the best-path only */ metric = info->attr->med; + + /* Determine if we're doing weighted ECMP or not */ + do_wt_ecmp = bgp_path_info_mpath_chkwtd(bgp, info); + if (do_wt_ecmp) + cum_bw = bgp_path_info_mpath_cumbw(info); + for (mpinfo = info; mpinfo; mpinfo = bgp_path_info_mpath_next(mpinfo)) { + uint32_t nh_weight; + if (valid_nh_count >= multipath_num) break; *mpinfo_cp = *mpinfo; + nh_weight = 0; /* Get nexthop address-family */ if (p->family == AF_INET @@ -1307,7 +1298,21 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, else continue; + /* If processing for weighted ECMP, determine the next hop's + * weight. Based on user setting, we may skip the next hop + * in some situations. + */ + if (do_wt_ecmp) { + if (!bgp_zebra_use_nhop_weighted(bgp, mpinfo->attr, + cum_bw, &nh_weight)) + continue; + } api_nh = &api.nexthops[valid_nh_count]; + + if (CHECK_FLAG(info->attr->flag, + ATTR_FLAG_BIT(BGP_ATTR_SRTE_COLOR))) + api_nh->srte_color = info->attr->srte_color; + if (nh_family == AF_INET) { if (bgp_debug_zebra(&api.prefix)) { if (mpinfo->extra) { @@ -1382,11 +1387,21 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, } nexthop = bgp_path_info_to_ipv6_nexthop(mpinfo_cp, &ifindex); - nh_updated = update_ipv6nh_for_route_install( - nh_othervrf, nh_othervrf ? - info->extra->bgp_orig : bgp, - nexthop, ifindex, - mpinfo, info, is_evpn, api_nh); + + if (!nexthop) + nh_updated = update_ipv4nh_for_route_install( + nh_othervrf, + nh_othervrf ? info->extra->bgp_orig + : bgp, + &mpinfo_cp->attr->nexthop, + mpinfo_cp->attr, is_evpn, api_nh); + else + nh_updated = update_ipv6nh_for_route_install( + nh_othervrf, + nh_othervrf ? info->extra->bgp_orig + : bgp, + nexthop, ifindex, mpinfo, info, is_evpn, + api_nh); } /* Did we get proper nexthop info to update zebra? */ @@ -1399,19 +1414,18 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, has_valid_label = 1; label = label_pton(&mpinfo->extra->label[0]); + SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_LABEL); + api_nh->label_num = 1; api_nh->labels[0] = label; } memcpy(&api_nh->rmac, &(mpinfo->attr->rmac), sizeof(struct ethaddr)); + api_nh->weight = nh_weight; + valid_nh_count++; } - - /* if this is a evpn route we don't have to include the label */ - if (has_valid_label && !(CHECK_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE))) - SET_FLAG(api.message, ZAPI_MESSAGE_LABEL); - /* * When we create an aggregate route we must also * install a Null0 route in the RIB, so overwrite @@ -1439,6 +1453,8 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, if (bgp_debug_zebra(p)) { char prefix_buf[PREFIX_STRLEN]; char nh_buf[INET6_ADDRSTRLEN]; + char eth_buf[ETHER_ADDR_STRLEN + 7] = {'\0'}; + char buf1[ETHER_ADDR_STRLEN]; char label_buf[20]; int i; @@ -1450,25 +1466,46 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, for (i = 0; i < api.nexthop_num; i++) { api_nh = &api.nexthops[i]; - if (api_nh->type == NEXTHOP_TYPE_IFINDEX) + switch (api_nh->type) { + case NEXTHOP_TYPE_IFINDEX: nh_buf[0] = '\0'; - else { - if (api_nh->type == NEXTHOP_TYPE_IPV4) - nh_family = AF_INET; - else - nh_family = AF_INET6; + break; + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + nh_family = AF_INET; inet_ntop(nh_family, &api_nh->gate, nh_buf, sizeof(nh_buf)); + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + nh_family = AF_INET6; + inet_ntop(nh_family, &api_nh->gate, nh_buf, + sizeof(nh_buf)); + break; + case NEXTHOP_TYPE_BLACKHOLE: + strlcpy(nh_buf, "blackhole", sizeof(nh_buf)); + break; + default: + /* Note: add new nexthop case */ + assert(0); + break; } label_buf[0] = '\0'; + eth_buf[0] = '\0'; if (has_valid_label && !CHECK_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE)) - sprintf(label_buf, "label %u", - api_nh->labels[0]); - zlog_debug(" nhop [%d]: %s if %u VRF %u %s", + snprintf(label_buf, sizeof(label_buf), + "label %u", api_nh->labels[0]); + if (CHECK_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE) + && !is_zero_mac(&api_nh->rmac)) + snprintf(eth_buf, sizeof(eth_buf), " RMAC %s", + prefix_mac2str(&api_nh->rmac, + buf1, sizeof(buf1))); + zlog_debug(" nhop [%d]: %s if %u VRF %u wt %u %s %s", i + 1, nh_buf, api_nh->ifindex, - api_nh->vrf_id, label_buf); + api_nh->vrf_id, api_nh->weight, + label_buf, eth_buf); } } @@ -1490,7 +1527,7 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, /* Announce all routes of a table to zebra */ void bgp_zebra_announce_table(struct bgp *bgp, afi_t afi, safi_t safi) { - struct bgp_node *rn; + struct bgp_dest *dest; struct bgp_table *table; struct bgp_path_info *pi; @@ -1504,19 +1541,20 @@ void bgp_zebra_announce_table(struct bgp *bgp, afi_t afi, safi_t safi) if (!table) return; - for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) - for (pi = bgp_node_get_bgp_path_info(rn); pi; pi = pi->next) + for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) + for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED) && (pi->type == ZEBRA_ROUTE_BGP && (pi->sub_type == BGP_ROUTE_NORMAL || pi->sub_type == BGP_ROUTE_IMPORTED))) - bgp_zebra_announce(rn, &rn->p, pi, bgp, afi, - safi); + bgp_zebra_announce(dest, + bgp_dest_get_prefix(dest), + pi, bgp, afi, safi); } -void bgp_zebra_withdraw(struct prefix *p, struct bgp_path_info *info, +void bgp_zebra_withdraw(const struct prefix *p, struct bgp_path_info *info, struct bgp *bgp, safi_t safi) { struct zapi_route api; @@ -1540,6 +1578,11 @@ void bgp_zebra_withdraw(struct prefix *p, struct bgp_path_info *info, api.safi = safi; api.prefix = *p; + if (info->attr->rmap_table_id) { + SET_FLAG(api.message, ZAPI_MESSAGE_TABLEID); + api.tableid = info->attr->rmap_table_id; + } + /* If the route's source is EVPN, flag as such. */ if (is_route_parent_evpn(info)) SET_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE); @@ -1631,7 +1674,7 @@ int bgp_redistribute_set(struct bgp *bgp, afi_t afi, int type, if (vrf_bitmap_check(zclient->redist[afi][type], bgp->vrf_id)) return CMD_WARNING; -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC if (EVPN_ENABLED(bgp) && type == ZEBRA_ROUTE_VNC_DIRECT) { vnc_export_bgp_enable( bgp, afi); /* only enables if mode bits cfg'd */ @@ -1686,11 +1729,11 @@ int bgp_redistribute_resend(struct bgp *bgp, afi_t afi, int type, } /* Redistribute with route-map specification. */ -int bgp_redistribute_rmap_set(struct bgp_redist *red, const char *name, - struct route_map *route_map) +bool bgp_redistribute_rmap_set(struct bgp_redist *red, const char *name, + struct route_map *route_map) { if (red->rmap.name && (strcmp(red->rmap.name, name) == 0)) - return 0; + return false; XFREE(MTYPE_ROUTE_MAP_NAME, red->rmap.name); /* Decrement the count for existing routemap and @@ -1701,45 +1744,45 @@ int bgp_redistribute_rmap_set(struct bgp_redist *red, const char *name, red->rmap.map = route_map; route_map_counter_increment(red->rmap.map); - return 1; + return true; } /* Redistribute with metric specification. */ -int bgp_redistribute_metric_set(struct bgp *bgp, struct bgp_redist *red, - afi_t afi, int type, uint32_t metric) +bool bgp_redistribute_metric_set(struct bgp *bgp, struct bgp_redist *red, + afi_t afi, int type, uint32_t metric) { - struct bgp_node *rn; + struct bgp_dest *dest; struct bgp_path_info *pi; if (red->redist_metric_flag && red->redist_metric == metric) - return 0; + return false; red->redist_metric_flag = 1; red->redist_metric = metric; - for (rn = bgp_table_top(bgp->rib[afi][SAFI_UNICAST]); rn; - rn = bgp_route_next(rn)) { - for (pi = bgp_node_get_bgp_path_info(rn); pi; pi = pi->next) { + for (dest = bgp_table_top(bgp->rib[afi][SAFI_UNICAST]); dest; + dest = bgp_route_next(dest)) { + for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) { if (pi->sub_type == BGP_ROUTE_REDISTRIBUTE && pi->type == type && pi->instance == red->instance) { struct attr *old_attr; struct attr new_attr; - bgp_attr_dup(&new_attr, pi->attr); + new_attr = *pi->attr; new_attr.med = red->redist_metric; old_attr = pi->attr; pi->attr = bgp_attr_intern(&new_attr); bgp_attr_unintern(&old_attr); - bgp_path_info_set_flag(rn, pi, + bgp_path_info_set_flag(dest, pi, BGP_PATH_ATTR_CHANGED); - bgp_process(bgp, rn, afi, SAFI_UNICAST); + bgp_process(bgp, dest, afi, SAFI_UNICAST); } } } - return 1; + return true; } /* Unset redistribution. */ @@ -1792,7 +1835,7 @@ int bgp_redistribute_unset(struct bgp *bgp, afi_t afi, int type, * they operate within bgpd irrespective of zebra connection * status. red lookup fails if there is no zebra connection. */ -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC if (EVPN_ENABLED(bgp) && type == ZEBRA_ROUTE_VNC_DIRECT) { vnc_export_bgp_disable(bgp, afi); } @@ -1807,7 +1850,6 @@ int bgp_redistribute_unset(struct bgp *bgp, afi_t afi, int type, /* Unset route-map. */ XFREE(MTYPE_ROUTE_MAP_NAME, red->rmap.name); route_map_counter_decrement(red->rmap.map); - red->rmap.name = NULL; red->rmap.map = NULL; /* Unset metric. */ @@ -1918,8 +1960,14 @@ void bgp_zebra_initiate_radv(struct bgp *bgp, struct peer *peer) zlog_debug("%u: Initiating RA for peer %s", bgp->vrf_id, peer->host); - zclient_send_interface_radv_req(zclient, bgp->vrf_id, peer->ifp, 1, - ra_interval); + /* + * If unnumbered peer (peer->ifp) call thru zapi to start RAs. + * If we don't have an ifp pointer, call function to find the + * ifps for a numbered enhe peer to turn RAs on. + */ + peer->ifp ? zclient_send_interface_radv_req(zclient, bgp->vrf_id, + peer->ifp, 1, ra_interval) + : bgp_nht_reg_enhe_cap_intfs(peer); } void bgp_zebra_terminate_radv(struct bgp *bgp, struct peer *peer) @@ -1932,7 +1980,14 @@ void bgp_zebra_terminate_radv(struct bgp *bgp, struct peer *peer) zlog_debug("%u: Terminating RA for peer %s", bgp->vrf_id, peer->host); - zclient_send_interface_radv_req(zclient, bgp->vrf_id, peer->ifp, 0, 0); + /* + * If unnumbered peer (peer->ifp) call thru zapi to stop RAs. + * If we don't have an ifp pointer, call function to find the + * ifps for a numbered enhe peer to turn RAs off. + */ + peer->ifp ? zclient_send_interface_radv_req(zclient, bgp->vrf_id, + peer->ifp, 0, 0) + : bgp_nht_dereg_enhe_cap_intfs(peer); } int bgp_zebra_advertise_subnet(struct bgp *bgp, int advertise, vni_t vni) @@ -1946,8 +2001,9 @@ int bgp_zebra_advertise_subnet(struct bgp *bgp, int advertise, vni_t vni) /* Don't try to register if Zebra doesn't know of this instance. */ if (!IS_BGP_INST_KNOWN_TO_ZEBRA(bgp)) { if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug("%s: No zebra instance to talk to, cannot advertise subnet", - __PRETTY_FUNCTION__); + zlog_debug( + "%s: No zebra instance to talk to, cannot advertise subnet", + __func__); return 0; } @@ -1996,8 +2052,9 @@ int bgp_zebra_advertise_gw_macip(struct bgp *bgp, int advertise, vni_t vni) /* Don't try to register if Zebra doesn't know of this instance. */ if (!IS_BGP_INST_KNOWN_TO_ZEBRA(bgp)) { if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug("%s: No zebra instance to talk to, not installing gw_macip", - __PRETTY_FUNCTION__); + zlog_debug( + "%s: No zebra instance to talk to, not installing gw_macip", + __func__); return 0; } @@ -2024,8 +2081,9 @@ int bgp_zebra_vxlan_flood_control(struct bgp *bgp, /* Don't try to register if Zebra doesn't know of this instance. */ if (!IS_BGP_INST_KNOWN_TO_ZEBRA(bgp)) { if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug("%s: No zebra instance to talk to, not installing all vni", - __PRETTY_FUNCTION__); + zlog_debug( + "%s: No zebra instance to talk to, not installing all vni", + __func__); return 0; } @@ -2101,17 +2159,16 @@ int bgp_zebra_dup_addr_detection(struct bgp *bgp) return zclient_send_message(zclient); } -static int rule_notify_owner(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int rule_notify_owner(ZAPI_CALLBACK_ARGS) { uint32_t seqno, priority, unique; enum zapi_rule_notify_owner note; struct bgp_pbr_action *bgp_pbra; struct bgp_pbr_rule *bgp_pbr = NULL; - ifindex_t ifi; + char ifname[INTERFACE_NAMSIZ + 1]; if (!zapi_rule_notify_decode(zclient->ibuf, &seqno, &priority, &unique, - &ifi, ¬e)) + ifname, ¬e)) return -1; bgp_pbra = bgp_pbr_action_rule_lookup(vrf_id, unique); @@ -2121,7 +2178,7 @@ static int rule_notify_owner(int command, struct zclient *zclient, if (!bgp_pbr && note != ZAPI_RULE_REMOVED) { if (BGP_DEBUG(zebra, ZEBRA)) zlog_debug("%s: Fail to look BGP rule (%u)", - __PRETTY_FUNCTION__, unique); + __func__, unique); return 0; } } @@ -2129,8 +2186,7 @@ static int rule_notify_owner(int command, struct zclient *zclient, switch (note) { case ZAPI_RULE_FAIL_INSTALL: if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug("%s: Received RULE_FAIL_INSTALL", - __PRETTY_FUNCTION__); + zlog_debug("%s: Received RULE_FAIL_INSTALL", __func__); if (bgp_pbra) { bgp_pbra->installed = false; bgp_pbra->install_in_progress = false; @@ -2157,22 +2213,19 @@ static int rule_notify_owner(int command, struct zclient *zclient, bgp_pbr); } if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug("%s: Received RULE_INSTALLED", - __PRETTY_FUNCTION__); + zlog_debug("%s: Received RULE_INSTALLED", __func__); break; case ZAPI_RULE_FAIL_REMOVE: case ZAPI_RULE_REMOVED: if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug("%s: Received RULE REMOVED", - __PRETTY_FUNCTION__); + zlog_debug("%s: Received RULE REMOVED", __func__); break; } return 0; } -static int ipset_notify_owner(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ipset_notify_owner(ZAPI_CALLBACK_ARGS) { uint32_t unique; enum zapi_ipset_notify_owner note; @@ -2187,15 +2240,14 @@ static int ipset_notify_owner(int command, struct zclient *zclient, if (!bgp_pbim) { if (BGP_DEBUG(zebra, ZEBRA)) zlog_debug("%s: Fail to look BGP match ( %u, ID %u)", - __PRETTY_FUNCTION__, note, unique); + __func__, note, unique); return 0; } switch (note) { case ZAPI_IPSET_FAIL_INSTALL: if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug("%s: Received IPSET_FAIL_INSTALL", - __PRETTY_FUNCTION__); + zlog_debug("%s: Received IPSET_FAIL_INSTALL", __func__); bgp_pbim->installed = false; bgp_pbim->install_in_progress = false; break; @@ -2203,22 +2255,19 @@ static int ipset_notify_owner(int command, struct zclient *zclient, bgp_pbim->installed = true; bgp_pbim->install_in_progress = false; if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug("%s: Received IPSET_INSTALLED", - __PRETTY_FUNCTION__); + zlog_debug("%s: Received IPSET_INSTALLED", __func__); break; case ZAPI_IPSET_FAIL_REMOVE: case ZAPI_IPSET_REMOVED: if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug("%s: Received IPSET REMOVED", - __PRETTY_FUNCTION__); + zlog_debug("%s: Received IPSET REMOVED", __func__); break; } return 0; } -static int ipset_entry_notify_owner(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ipset_entry_notify_owner(ZAPI_CALLBACK_ARGS) { uint32_t unique; char ipset_name[ZEBRA_IPSET_NAME_SIZE]; @@ -2236,8 +2285,9 @@ static int ipset_entry_notify_owner(int command, struct zclient *zclient, unique); if (!bgp_pbime) { if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug("%s: Fail to look BGP match entry (%u, ID %u)", - __PRETTY_FUNCTION__, note, unique); + zlog_debug( + "%s: Fail to look BGP match entry (%u, ID %u)", + __func__, note, unique); return 0; } @@ -2245,7 +2295,7 @@ static int ipset_entry_notify_owner(int command, struct zclient *zclient, case ZAPI_IPSET_ENTRY_FAIL_INSTALL: if (BGP_DEBUG(zebra, ZEBRA)) zlog_debug("%s: Received IPSET_ENTRY_FAIL_INSTALL", - __PRETTY_FUNCTION__); + __func__); bgp_pbime->installed = false; bgp_pbime->install_in_progress = false; break; @@ -2258,7 +2308,7 @@ static int ipset_entry_notify_owner(int command, struct zclient *zclient, bgp_pbime->install_in_progress = false; if (BGP_DEBUG(zebra, ZEBRA)) zlog_debug("%s: Received IPSET_ENTRY_INSTALLED", - __PRETTY_FUNCTION__); + __func__); /* link bgp_path_info to bpme */ path = (struct bgp_path_info *)bgp_pbime->path; extra = bgp_path_info_extra_get(path); @@ -2269,14 +2319,13 @@ static int ipset_entry_notify_owner(int command, struct zclient *zclient, case ZAPI_IPSET_ENTRY_REMOVED: if (BGP_DEBUG(zebra, ZEBRA)) zlog_debug("%s: Received IPSET_ENTRY_REMOVED", - __PRETTY_FUNCTION__); + __func__); break; } return 0; } -static int iptable_notify_owner(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int iptable_notify_owner(ZAPI_CALLBACK_ARGS) { uint32_t unique; enum zapi_iptable_notify_owner note; @@ -2291,14 +2340,14 @@ static int iptable_notify_owner(int command, struct zclient *zclient, if (!bgpm) { if (BGP_DEBUG(zebra, ZEBRA)) zlog_debug("%s: Fail to look BGP iptable (%u %u)", - __PRETTY_FUNCTION__, note, unique); + __func__, note, unique); return 0; } switch (note) { case ZAPI_IPTABLE_FAIL_INSTALL: if (BGP_DEBUG(zebra, ZEBRA)) zlog_debug("%s: Received IPTABLE_FAIL_INSTALL", - __PRETTY_FUNCTION__); + __func__); bgpm->installed_in_iptable = false; bgpm->install_iptable_in_progress = false; break; @@ -2306,15 +2355,13 @@ static int iptable_notify_owner(int command, struct zclient *zclient, bgpm->installed_in_iptable = true; bgpm->install_iptable_in_progress = false; if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug("%s: Received IPTABLE_INSTALLED", - __PRETTY_FUNCTION__); + zlog_debug("%s: Received IPTABLE_INSTALLED", __func__); bgpm->action->refcnt++; break; case ZAPI_IPTABLE_FAIL_REMOVE: case ZAPI_IPTABLE_REMOVED: if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug("%s: Received IPTABLE REMOVED", - __PRETTY_FUNCTION__); + zlog_debug("%s: Received IPTABLE REMOVED", __func__); break; } return 0; @@ -2322,14 +2369,18 @@ static int iptable_notify_owner(int command, struct zclient *zclient, /* this function is used to forge ip rule, * - either for iptable/ipset using fwmark id - * - or for sample ip rule command + * - or for sample ip rule cmd */ static void bgp_encode_pbr_rule_action(struct stream *s, struct bgp_pbr_action *pbra, struct bgp_pbr_rule *pbr) { struct prefix pfx; + uint8_t fam = AF_INET; + char ifname[INTERFACE_NAMSIZ]; + if (pbra->nh.type == NEXTHOP_TYPE_IPV6) + fam = AF_INET6; stream_putl(s, 0); /* seqno unused */ if (pbr) stream_putl(s, pbr->priority); @@ -2349,7 +2400,7 @@ static void bgp_encode_pbr_rule_action(struct stream *s, memcpy(&pfx, &(pbr->src), sizeof(struct prefix)); else { memset(&pfx, 0, sizeof(pfx)); - pfx.family = AF_INET; + pfx.family = fam; } stream_putc(s, pfx.family); stream_putc(s, pfx.prefixlen); @@ -2361,14 +2412,14 @@ static void bgp_encode_pbr_rule_action(struct stream *s, memcpy(&pfx, &(pbr->dst), sizeof(struct prefix)); else { memset(&pfx, 0, sizeof(pfx)); - pfx.family = AF_INET; + pfx.family = fam; } stream_putc(s, pfx.family); stream_putc(s, pfx.prefixlen); stream_put(s, &pfx.u.prefix, prefix_blen(&pfx)); stream_putw(s, 0); /* dst port */ - + stream_putc(s, 0); /* dsfield */ /* if pbr present, fwmark is not used */ if (pbr) stream_putl(s, 0); @@ -2377,7 +2428,8 @@ static void bgp_encode_pbr_rule_action(struct stream *s, stream_putl(s, pbra->table_id); - stream_putl(s, 0); /* ifindex unused */ + memset(ifname, 0, sizeof(ifname)); + stream_put(s, ifname, INTERFACE_NAMSIZ); /* ifname unused */ } static void bgp_encode_pbr_ipset_match(struct stream *s, @@ -2385,7 +2437,7 @@ static void bgp_encode_pbr_ipset_match(struct stream *s, { stream_putl(s, pbim->unique); stream_putl(s, pbim->type); - + stream_putc(s, pbim->family); stream_put(s, pbim->ipset_name, ZEBRA_IPSET_NAME_SIZE); } @@ -2434,12 +2486,15 @@ static void bgp_encode_pbr_iptable_match(struct stream *s, stream_putl(s, bpa->fwmark); stream_put(s, pbm->ipset_name, ZEBRA_IPSET_NAME_SIZE); + stream_putc(s, pbm->family); stream_putw(s, pbm->pkt_len_min); stream_putw(s, pbm->pkt_len_max); stream_putw(s, pbm->tcp_flags); stream_putw(s, pbm->tcp_mask_flags); stream_putc(s, pbm->dscp_value); stream_putc(s, pbm->fragment); + stream_putc(s, pbm->protocol); + stream_putw(s, pbm->flow_label); } /* BGP has established connection with Zebra. */ @@ -2460,7 +2515,7 @@ static void bgp_zebra_connected(struct zclient *zclient) bgp_zebra_instance_register(bgp); /* Send the client registration */ - bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER); + bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, bgp->vrf_id); /* tell label pool that zebra is connected */ bgp_lp_event_zebra_up(); @@ -2468,20 +2523,69 @@ static void bgp_zebra_connected(struct zclient *zclient) /* TODO - What if we have peers and networks configured, do we have to * kick-start them? */ + BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(bgp, bgp->peer); } -static int bgp_zebra_process_local_es(int cmd, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int bgp_zebra_process_local_es_add(ZAPI_CALLBACK_ARGS) +{ + esi_t esi; + struct bgp *bgp = NULL; + struct stream *s = NULL; + char buf[ESI_STR_LEN]; + struct in_addr originator_ip; + uint8_t active; + + bgp = bgp_lookup_by_vrf_id(vrf_id); + if (!bgp) + return 0; + + s = zclient->ibuf; + stream_get(&esi, s, sizeof(esi_t)); + originator_ip.s_addr = stream_get_ipv4(s); + active = stream_getc(s); + + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("Rx add ESI %s originator-ip %s active %u", + esi_to_str(&esi, buf, sizeof(buf)), + inet_ntoa(originator_ip), + active); + + bgp_evpn_local_es_add(bgp, &esi, originator_ip, active); + + return 0; +} + +static int bgp_zebra_process_local_es_del(ZAPI_CALLBACK_ARGS) { esi_t esi; struct bgp *bgp = NULL; struct stream *s = NULL; char buf[ESI_STR_LEN]; - char buf1[INET6_ADDRSTRLEN]; - struct ipaddr originator_ip; memset(&esi, 0, sizeof(esi_t)); - memset(&originator_ip, 0, sizeof(struct ipaddr)); + bgp = bgp_lookup_by_vrf_id(vrf_id); + if (!bgp) + return 0; + + s = zclient->ibuf; + stream_get(&esi, s, sizeof(esi_t)); + + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("Rx del ESI %s", + esi_to_str(&esi, buf, sizeof(buf))); + + bgp_evpn_local_es_del(bgp, &esi); + + return 0; +} + +static int bgp_zebra_process_local_es_evi(ZAPI_CALLBACK_ARGS) +{ + esi_t esi; + vni_t vni; + struct bgp *bgp; + struct stream *s; + char buf[ESI_STR_LEN]; bgp = bgp_lookup_by_vrf_id(vrf_id); if (!bgp) @@ -2489,51 +2593,57 @@ static int bgp_zebra_process_local_es(int cmd, struct zclient *zclient, s = zclient->ibuf; stream_get(&esi, s, sizeof(esi_t)); - stream_get(&originator_ip, s, sizeof(struct ipaddr)); + vni = stream_getl(s); if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug("Rx %s ESI %s originator-ip %s", - (cmd == ZEBRA_LOCAL_ES_ADD) ? "add" : "del", - esi_to_str(&esi, buf, sizeof(buf)), - ipaddr2str(&originator_ip, buf1, sizeof(buf1))); + zlog_debug("Rx %s ESI %s VNI %u", + ZEBRA_VNI_ADD ? "add" : "del", + esi_to_str(&esi, buf, sizeof(buf)), vni); - if (cmd == ZEBRA_LOCAL_ES_ADD) - bgp_evpn_local_es_add(bgp, &esi, &originator_ip); + if (cmd == ZEBRA_LOCAL_ES_EVI_ADD) + bgp_evpn_local_es_evi_add(bgp, &esi, vni); else - bgp_evpn_local_es_del(bgp, &esi, &originator_ip); + bgp_evpn_local_es_evi_del(bgp, &esi, vni); + return 0; } -static int bgp_zebra_process_local_l3vni(int cmd, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int bgp_zebra_process_local_l3vni(ZAPI_CALLBACK_ARGS) { int filter = 0; char buf[ETHER_ADDR_STRLEN]; vni_t l3vni = 0; - struct ethaddr rmac; + struct ethaddr svi_rmac, vrr_rmac = {.octet = {0} }; struct in_addr originator_ip; struct stream *s; ifindex_t svi_ifindex; + bool is_anycast_mac = false; + char buf1[ETHER_ADDR_STRLEN]; - memset(&rmac, 0, sizeof(struct ethaddr)); + memset(&svi_rmac, 0, sizeof(struct ethaddr)); memset(&originator_ip, 0, sizeof(struct in_addr)); s = zclient->ibuf; l3vni = stream_getl(s); if (cmd == ZEBRA_L3VNI_ADD) { - stream_get(&rmac, s, sizeof(struct ethaddr)); + stream_get(&svi_rmac, s, sizeof(struct ethaddr)); originator_ip.s_addr = stream_get_ipv4(s); stream_get(&filter, s, sizeof(int)); svi_ifindex = stream_getl(s); + stream_get(&vrr_rmac, s, sizeof(struct ethaddr)); + is_anycast_mac = stream_getl(s); if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug("Rx L3-VNI ADD VRF %s VNI %u RMAC %s filter %s svi-if %u", + zlog_debug("Rx L3-VNI ADD VRF %s VNI %u RMAC svi-mac %s vrr-mac %s filter %s svi-if %u", vrf_id_to_name(vrf_id), l3vni, - prefix_mac2str(&rmac, buf, sizeof(buf)), + prefix_mac2str(&svi_rmac, buf, sizeof(buf)), + prefix_mac2str(&vrr_rmac, buf1, + sizeof(buf1)), filter ? "prefix-routes-only" : "none", svi_ifindex); - bgp_evpn_local_l3vni_add(l3vni, vrf_id, &rmac, originator_ip, - filter, svi_ifindex); + bgp_evpn_local_l3vni_add(l3vni, vrf_id, &svi_rmac, &vrr_rmac, + originator_ip, filter, svi_ifindex, + is_anycast_mac); } else { if (BGP_DEBUG(zebra, ZEBRA)) zlog_debug("Rx L3-VNI DEL VRF %s VNI %u", @@ -2545,8 +2655,7 @@ static int bgp_zebra_process_local_l3vni(int cmd, struct zclient *zclient, return 0; } -static int bgp_zebra_process_local_vni(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int bgp_zebra_process_local_vni(ZAPI_CALLBACK_ARGS) { struct stream *s; vni_t vni; @@ -2557,7 +2666,7 @@ static int bgp_zebra_process_local_vni(int command, struct zclient *zclient, s = zclient->ibuf; vni = stream_getl(s); - if (command == ZEBRA_VNI_ADD) { + if (cmd == ZEBRA_VNI_ADD) { vtep_ip.s_addr = stream_get_ipv4(s); stream_get(&tenant_vrf_id, s, sizeof(vrf_id_t)); mcast_grp.s_addr = stream_get_ipv4(s); @@ -2569,20 +2678,20 @@ static int bgp_zebra_process_local_vni(int command, struct zclient *zclient, if (BGP_DEBUG(zebra, ZEBRA)) zlog_debug("Rx VNI %s VRF %s VNI %u tenant-vrf %s", - (command == ZEBRA_VNI_ADD) ? "add" : "del", + (cmd == ZEBRA_VNI_ADD) ? "add" : "del", vrf_id_to_name(vrf_id), vni, vrf_id_to_name(tenant_vrf_id)); - if (command == ZEBRA_VNI_ADD) + if (cmd == ZEBRA_VNI_ADD) return bgp_evpn_local_vni_add( - bgp, vni, vtep_ip.s_addr ? vtep_ip : bgp->router_id, + bgp, vni, + vtep_ip.s_addr != INADDR_ANY ? vtep_ip : bgp->router_id, tenant_vrf_id, mcast_grp); else return bgp_evpn_local_vni_del(bgp, vni); } -static int bgp_zebra_process_local_macip(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int bgp_zebra_process_local_macip(ZAPI_CALLBACK_ARGS) { struct stream *s; vni_t vni; @@ -2595,6 +2704,8 @@ static int bgp_zebra_process_local_macip(int command, struct zclient *zclient, uint8_t flags = 0; uint32_t seqnum = 0; int state = 0; + char buf2[ESI_STR_LEN]; + esi_t esi; memset(&ip, 0, sizeof(ip)); s = zclient->ibuf; @@ -2605,7 +2716,7 @@ static int bgp_zebra_process_local_macip(int command, struct zclient *zclient, && ipa_len != IPV6_MAX_BYTELEN) { flog_err(EC_BGP_MACIP_LEN, "%u:Recv MACIP %s with invalid IP addr length %d", - vrf_id, (command == ZEBRA_MACIP_ADD) ? "Add" : "Del", + vrf_id, (cmd == ZEBRA_MACIP_ADD) ? "Add" : "Del", ipa_len); return -1; } @@ -2615,11 +2726,13 @@ static int bgp_zebra_process_local_macip(int command, struct zclient *zclient, (ipa_len == IPV4_MAX_BYTELEN) ? IPADDR_V4 : IPADDR_V6; stream_get(&ip.ip.addr, s, ipa_len); } - if (command == ZEBRA_MACIP_ADD) { + if (cmd == ZEBRA_MACIP_ADD) { flags = stream_getc(s); seqnum = stream_getl(s); + stream_get(&esi, s, sizeof(esi_t)); } else { state = stream_getl(s); + memset(&esi, 0, sizeof(esi_t)); } bgp = bgp_lookup_by_vrf_id(vrf_id); @@ -2627,22 +2740,20 @@ static int bgp_zebra_process_local_macip(int command, struct zclient *zclient, return 0; if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug("%u:Recv MACIP %s flags 0x%x MAC %s IP %s VNI %u seq %u state %d", - vrf_id, (command == ZEBRA_MACIP_ADD) ? "Add" : "Del", + zlog_debug("%u:Recv MACIP %s f 0x%x MAC %s IP %s VNI %u seq %u state %d ESI %s", + vrf_id, (cmd == ZEBRA_MACIP_ADD) ? "Add" : "Del", flags, prefix_mac2str(&mac, buf, sizeof(buf)), ipaddr2str(&ip, buf1, sizeof(buf1)), vni, seqnum, - state); + state, esi_to_str(&esi, buf2, sizeof(buf2))); - if (command == ZEBRA_MACIP_ADD) + if (cmd == ZEBRA_MACIP_ADD) return bgp_evpn_local_macip_add(bgp, vni, &mac, &ip, - flags, seqnum); + flags, seqnum, &esi); else return bgp_evpn_local_macip_del(bgp, vni, &mac, &ip, state); } -static void bgp_zebra_process_local_ip_prefix(int cmd, struct zclient *zclient, - zebra_size_t length, - vrf_id_t vrf_id) +static void bgp_zebra_process_local_ip_prefix(ZAPI_CALLBACK_ARGS) { struct stream *s = NULL; struct bgp *bgp_vrf = NULL; @@ -2682,11 +2793,7 @@ static void bgp_zebra_process_local_ip_prefix(int cmd, struct zclient *zclient, } } -static void bgp_zebra_process_label_chunk( - int cmd, - struct zclient *zclient, - zebra_size_t length, - vrf_id_t vrf_id) +static void bgp_zebra_process_label_chunk(ZAPI_CALLBACK_ARGS) { struct stream *s = NULL; uint8_t response_keep; @@ -2734,17 +2841,35 @@ static void bgp_zebra_process_label_chunk( extern struct zebra_privs_t bgpd_privs; +static int bgp_ifp_create(struct interface *ifp) +{ + struct bgp *bgp; + + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("Rx Intf add VRF %u IF %s", ifp->vrf_id, ifp->name); + + bgp = bgp_lookup_by_vrf_id(ifp->vrf_id); + if (!bgp) + return 0; + + bgp_mac_add_mac_entry(ifp); + + bgp_update_interface_nbrs(bgp, ifp, ifp); + return 0; +} + void bgp_zebra_init(struct thread_master *master, unsigned short instance) { zclient_num_connects = 0; + if_zapi_callbacks(bgp_ifp_create, bgp_ifp_up, + bgp_ifp_down, bgp_ifp_destroy); + /* Set default values. */ zclient = zclient_new(master, &zclient_options_default); zclient_init(zclient, ZEBRA_ROUTE_BGP, 0, &bgpd_privs); zclient->zebra_connected = bgp_zebra_connected; zclient->router_id_update = bgp_router_id_update; - zclient->interface_add = bgp_interface_add; - zclient->interface_delete = bgp_interface_delete; zclient->interface_address_add = bgp_interface_address_add; zclient->interface_address_delete = bgp_interface_address_delete; zclient->interface_nbr_address_add = bgp_interface_nbr_address_add; @@ -2753,14 +2878,14 @@ void bgp_zebra_init(struct thread_master *master, unsigned short instance) zclient->interface_vrf_update = bgp_interface_vrf_update; zclient->redistribute_route_add = zebra_read_route; zclient->redistribute_route_del = zebra_read_route; - zclient->interface_up = bgp_interface_up; - zclient->interface_down = bgp_interface_down; zclient->nexthop_update = bgp_read_nexthop_update; zclient->import_check_update = bgp_read_import_check_update; zclient->fec_update = bgp_read_fec_update; - zclient->local_es_add = bgp_zebra_process_local_es; - zclient->local_es_del = bgp_zebra_process_local_es; + zclient->local_es_add = bgp_zebra_process_local_es_add; + zclient->local_es_del = bgp_zebra_process_local_es_del; zclient->local_vni_add = bgp_zebra_process_local_vni; + zclient->local_es_evi_add = bgp_zebra_process_local_es_evi; + zclient->local_es_evi_del = bgp_zebra_process_local_es_evi; zclient->local_vni_del = bgp_zebra_process_local_vni; zclient->local_macip_add = bgp_zebra_process_local_macip; zclient->local_macip_del = bgp_zebra_process_local_macip; @@ -2802,12 +2927,10 @@ void bgp_send_pbr_rule_action(struct bgp_pbr_action *pbra, return; if (BGP_DEBUG(zebra, ZEBRA)) { if (pbr) - zlog_debug("%s: table %d (ip rule) %d", - __PRETTY_FUNCTION__, + zlog_debug("%s: table %d (ip rule) %d", __func__, pbra->table_id, install); else - zlog_debug("%s: table %d fwmark %d %d", - __PRETTY_FUNCTION__, + zlog_debug("%s: table %d fwmark %d %d", __func__, pbra->table_id, pbra->fwmark, install); } s = zclient->obuf; @@ -2836,10 +2959,9 @@ void bgp_send_pbr_ipset_match(struct bgp_pbr_match *pbrim, bool install) if (pbrim->install_in_progress) return; if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug("%s: name %s type %d %d, ID %u", - __PRETTY_FUNCTION__, - pbrim->ipset_name, pbrim->type, - install, pbrim->unique); + zlog_debug("%s: name %s type %d %d, ID %u", __func__, + pbrim->ipset_name, pbrim->type, install, + pbrim->unique); s = zclient->obuf; stream_reset(s); @@ -2865,9 +2987,9 @@ void bgp_send_pbr_ipset_entry_match(struct bgp_pbr_match_entry *pbrime, if (pbrime->install_in_progress) return; if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug("%s: name %s %d %d, ID %u", __PRETTY_FUNCTION__, - pbrime->backpointer->ipset_name, - pbrime->unique, install, pbrime->unique); + zlog_debug("%s: name %s %d %d, ID %u", __func__, + pbrime->backpointer->ipset_name, pbrime->unique, + install, pbrime->unique); s = zclient->obuf; stream_reset(s); @@ -2885,7 +3007,8 @@ void bgp_send_pbr_ipset_entry_match(struct bgp_pbr_match_entry *pbrime, pbrime->install_in_progress = true; } -static void bgp_encode_pbr_interface_list(struct bgp *bgp, struct stream *s) +static void bgp_encode_pbr_interface_list(struct bgp *bgp, struct stream *s, + uint8_t family) { struct bgp_pbr_config *bgp_pbr_cfg = bgp->bgp_pbr_cfg; struct bgp_pbr_interface_head *head; @@ -2894,8 +3017,10 @@ static void bgp_encode_pbr_interface_list(struct bgp *bgp, struct stream *s) if (!bgp_pbr_cfg) return; - head = &(bgp_pbr_cfg->ifaces_by_name_ipv4); - + if (family == AF_INET) + head = &(bgp_pbr_cfg->ifaces_by_name_ipv4); + else + head = &(bgp_pbr_cfg->ifaces_by_name_ipv6); RB_FOREACH (pbr_if, bgp_pbr_interface_head, head) { ifp = if_lookup_by_name(pbr_if->name, bgp->vrf_id); if (ifp) @@ -2903,7 +3028,7 @@ static void bgp_encode_pbr_interface_list(struct bgp *bgp, struct stream *s) } } -static int bgp_pbr_get_ifnumber(struct bgp *bgp) +static int bgp_pbr_get_ifnumber(struct bgp *bgp, uint8_t family) { struct bgp_pbr_config *bgp_pbr_cfg = bgp->bgp_pbr_cfg; struct bgp_pbr_interface_head *head; @@ -2912,8 +3037,10 @@ static int bgp_pbr_get_ifnumber(struct bgp *bgp) if (!bgp_pbr_cfg) return 0; - head = &(bgp_pbr_cfg->ifaces_by_name_ipv4); - + if (family == AF_INET) + head = &(bgp_pbr_cfg->ifaces_by_name_ipv4); + else + head = &(bgp_pbr_cfg->ifaces_by_name_ipv6); RB_FOREACH (pbr_if, bgp_pbr_interface_head, head) { if (if_lookup_by_name(pbr_if->name, bgp->vrf_id)) cnt++; @@ -2932,9 +3059,8 @@ void bgp_send_pbr_iptable(struct bgp_pbr_action *pba, if (pbm->install_iptable_in_progress) return; if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug("%s: name %s type %d mark %d %d, ID %u", - __PRETTY_FUNCTION__, pbm->ipset_name, - pbm->type, pba->fwmark, install, + zlog_debug("%s: name %s type %d mark %d %d, ID %u", __func__, + pbm->ipset_name, pbm->type, pba->fwmark, install, pbm->unique2); s = zclient->obuf; stream_reset(s); @@ -2945,10 +3071,10 @@ void bgp_send_pbr_iptable(struct bgp_pbr_action *pba, VRF_DEFAULT); bgp_encode_pbr_iptable_match(s, pba, pbm); - nb_interface = bgp_pbr_get_ifnumber(pba->bgp); + nb_interface = bgp_pbr_get_ifnumber(pba->bgp, pbm->family); stream_putl(s, nb_interface); if (nb_interface) - bgp_encode_pbr_interface_list(pba->bgp, s); + bgp_encode_pbr_interface_list(pba->bgp, s, pbm->family); stream_putw_at(s, 0, stream_get_endp(s)); ret = zclient_send_message(zclient); if (install) { @@ -2970,14 +3096,14 @@ void bgp_zebra_announce_default(struct bgp *bgp, struct nexthop *nh, struct zapi_route api; struct prefix p; - if (!nh || nh->type != NEXTHOP_TYPE_IPV4 + if (!nh || (nh->type != NEXTHOP_TYPE_IPV4 + && nh->type != NEXTHOP_TYPE_IPV6) || nh->vrf_id == VRF_UNKNOWN) return; memset(&p, 0, sizeof(struct prefix)); - /* default route */ - if (afi != AFI_IP) + if (afi != AFI_IP && afi != AFI_IP6) return; - p.family = AF_INET; + p.family = afi2family(afi); memset(&api, 0, sizeof(api)); api.vrf_id = bgp->vrf_id; api.type = ZEBRA_ROUTE_BGP; @@ -2989,8 +3115,11 @@ void bgp_zebra_announce_default(struct bgp *bgp, struct nexthop *nh, SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP); api_nh = &api.nexthops[0]; + api.distance = ZEBRA_EBGP_DISTANCE_DEFAULT; + SET_FLAG(api.message, ZAPI_MESSAGE_DISTANCE); + /* redirect IP */ - if (nh->gate.ipv4.s_addr) { + if (afi == AFI_IP && nh->gate.ipv4.s_addr != INADDR_ANY) { char buff[PREFIX_STRLEN]; api_nh->vrf_id = nh->vrf_id; @@ -2999,7 +3128,25 @@ void bgp_zebra_announce_default(struct bgp *bgp, struct nexthop *nh, inet_ntop(AF_INET, &(nh->gate.ipv4), buff, INET_ADDRSTRLEN); if (BGP_DEBUG(zebra, ZEBRA)) - zlog_info("BGP: %s default route to %s table %d (redirect IP)", + zlog_debug("BGP: %s default route to %s table %d (redirect IP)", + announce ? "adding" : "withdrawing", + buff, table_id); + zclient_route_send(announce ? ZEBRA_ROUTE_ADD + : ZEBRA_ROUTE_DELETE, + zclient, &api); + } else if (afi == AFI_IP6 && + memcmp(&nh->gate.ipv6, + &in6addr_any, sizeof(struct in6_addr))) { + char buff[PREFIX_STRLEN]; + + api_nh->vrf_id = nh->vrf_id; + memcpy(&api_nh->gate.ipv6, &nh->gate.ipv6, + sizeof(struct in6_addr)); + api_nh->type = NEXTHOP_TYPE_IPV6; + + inet_ntop(AF_INET6, &(nh->gate.ipv6), buff, INET_ADDRSTRLEN); + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("BGP: %s default route to %s table %d (redirect IP)", announce ? "adding" : "withdrawing", buff, table_id); zclient_route_send(announce ? ZEBRA_ROUTE_ADD @@ -3031,3 +3178,120 @@ void bgp_zebra_announce_default(struct bgp *bgp, struct nexthop *nh, return; } } + +/* Send capabilities to RIB */ +int bgp_zebra_send_capabilities(struct bgp *bgp, bool disable) +{ + struct zapi_cap api; + int ret = BGP_GR_SUCCESS; + + if (zclient == NULL) { + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("zclient invalid"); + return BGP_GR_FAILURE; + } + + /* Check if the client is connected */ + if ((zclient->sock < 0) || (zclient->t_connect)) { + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("client not connected"); + return BGP_GR_FAILURE; + } + + /* Check if capability is already sent. If the flag force is set + * send the capability since this can be initial bgp configuration + */ + memset(&api, 0, sizeof(struct zapi_cap)); + if (disable) { + api.cap = ZEBRA_CLIENT_GR_DISABLE; + api.vrf_id = bgp->vrf_id; + } else { + api.cap = ZEBRA_CLIENT_GR_CAPABILITIES; + api.stale_removal_time = bgp->rib_stale_time; + api.vrf_id = bgp->vrf_id; + } + + if (zclient_capabilities_send(ZEBRA_CLIENT_CAPABILITIES, zclient, &api) + < 0) { + zlog_err("error sending capability"); + ret = BGP_GR_FAILURE; + } else { + if (disable) + bgp->present_zebra_gr_state = ZEBRA_GR_DISABLE; + else + bgp->present_zebra_gr_state = ZEBRA_GR_ENABLE; + + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("send capabilty success"); + ret = BGP_GR_SUCCESS; + } + return ret; +} + +/* Send route update pesding or completed status to RIB for the + * specific AFI, SAFI + */ +int bgp_zebra_update(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type) +{ + struct zapi_cap api = {0}; + + if (zclient == NULL) { + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("zclient == NULL, invalid"); + return BGP_GR_FAILURE; + } + + /* Check if the client is connected */ + if ((zclient->sock < 0) || (zclient->t_connect)) { + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("client not connected"); + return BGP_GR_FAILURE; + } + + api.afi = afi; + api.safi = safi; + api.vrf_id = vrf_id; + api.cap = type; + + if (zclient_capabilities_send(ZEBRA_CLIENT_CAPABILITIES, zclient, &api) + < 0) { + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("error sending capability"); + return BGP_GR_FAILURE; + } + return BGP_GR_SUCCESS; +} + + +/* Send RIB stale timer update */ +int bgp_zebra_stale_timer_update(struct bgp *bgp) +{ + struct zapi_cap api; + + if (zclient == NULL) { + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("zclient invalid"); + return BGP_GR_FAILURE; + } + + /* Check if the client is connected */ + if ((zclient->sock < 0) || (zclient->t_connect)) { + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("client not connected"); + return BGP_GR_FAILURE; + } + + memset(&api, 0, sizeof(struct zapi_cap)); + api.cap = ZEBRA_CLIENT_RIB_STALE_TIME; + api.stale_removal_time = bgp->rib_stale_time; + api.vrf_id = bgp->vrf_id; + if (zclient_capabilities_send(ZEBRA_CLIENT_CAPABILITIES, zclient, &api) + < 0) { + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("error sending capability"); + return BGP_GR_FAILURE; + } + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("send capabilty success"); + return BGP_GR_SUCCESS; +} diff --git a/bgpd/bgp_zebra.h b/bgpd/bgp_zebra.h index b912870b80..a068c03717 100644 --- a/bgpd/bgp_zebra.h +++ b/bgpd/bgp_zebra.h @@ -23,6 +23,9 @@ #include "vxlan.h" +/* Default weight for next hop, if doing weighted ECMP. */ +#define BGP_ZEBRA_DEFAULT_NHOP_WEIGHT 1 + extern void bgp_zebra_init(struct thread_master *master, unsigned short instance); extern void bgp_zebra_init_tm_connect(struct bgp *bgp); @@ -32,16 +35,13 @@ extern void bgp_zebra_destroy(void); extern int bgp_zebra_get_table_range(uint32_t chunk_size, uint32_t *start, uint32_t *end); extern int bgp_if_update_all(void); -extern void bgp_config_write_maxpaths(struct vty *, struct bgp *, afi_t, - safi_t); -extern void bgp_config_write_redistribute(struct vty *, struct bgp *, afi_t, - safi_t); -extern void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, +extern void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p, struct bgp_path_info *path, struct bgp *bgp, afi_t afi, safi_t safi); extern void bgp_zebra_announce_table(struct bgp *, afi_t, safi_t); -extern void bgp_zebra_withdraw(struct prefix *p, struct bgp_path_info *path, - struct bgp *bgp, safi_t safi); +extern void bgp_zebra_withdraw(const struct prefix *p, + struct bgp_path_info *path, struct bgp *bgp, + safi_t safi); extern void bgp_zebra_initiate_radv(struct bgp *bgp, struct peer *peer); extern void bgp_zebra_terminate_radv(struct bgp *bgp, struct peer *peer); @@ -57,10 +57,10 @@ extern struct bgp_redist *bgp_redist_add(struct bgp *, afi_t, uint8_t, extern int bgp_redistribute_set(struct bgp *, afi_t, int, unsigned short, bool changed); extern int bgp_redistribute_resend(struct bgp *, afi_t, int, unsigned short); -extern int bgp_redistribute_rmap_set(struct bgp_redist *red, const char *name, - struct route_map *route_map); -extern int bgp_redistribute_metric_set(struct bgp *, struct bgp_redist *, afi_t, - int, uint32_t); +extern bool bgp_redistribute_rmap_set(struct bgp_redist *red, const char *name, + struct route_map *route_map); +extern bool bgp_redistribute_metric_set(struct bgp *, struct bgp_redist *, + afi_t, int, uint32_t); extern int bgp_redistribute_unset(struct bgp *, afi_t, int, unsigned short); extern int bgp_redistribute_unreg(struct bgp *, afi_t, int, unsigned short); @@ -84,11 +84,11 @@ extern int bgp_zebra_num_connects(void); extern bool bgp_zebra_nexthop_set(union sockunion *, union sockunion *, struct bgp_nexthop *, struct peer *); - struct bgp_pbr_action; struct bgp_pbr_match; struct bgp_pbr_rule; struct bgp_pbr_match_entry; + extern void bgp_send_pbr_rule_action(struct bgp_pbr_action *pbra, struct bgp_pbr_rule *pbr, bool install); @@ -102,5 +102,7 @@ extern void bgp_send_pbr_iptable(struct bgp_pbr_action *pba, extern void bgp_zebra_announce_default(struct bgp *bgp, struct nexthop *nh, afi_t afi, uint32_t table_id, bool announce); - +extern int bgp_zebra_send_capabilities(struct bgp *bgp, bool disable); +extern int bgp_zebra_update(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type); +extern int bgp_zebra_stale_timer_update(struct bgp *bgp); #endif /* _QUAGGA_BGP_ZEBRA_H */ diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index b2925cd512..4d3812bf60 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -65,7 +65,7 @@ #include "bgpd/bgp_nexthop.h" #include "bgpd/bgp_damp.h" #include "bgpd/bgp_mplsvpn.h" -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC #include "bgpd/rfapi/bgp_rfapi_cfg.h" #include "bgpd/rfapi/rfapi_backend.h" #endif @@ -87,6 +87,7 @@ #include "bgpd/bgp_pbr.h" #include "bgpd/bgp_addpath.h" #include "bgpd/bgp_evpn_private.h" +#include "bgpd/bgp_evpn_mh.h" #include "bgpd/bgp_mac.h" DEFINE_MTYPE_STATIC(BGPD, PEER_TX_SHUTDOWN_MSG, "Peer shutdown message (TX)"); @@ -94,6 +95,7 @@ DEFINE_MTYPE_STATIC(BGPD, BGP_EVPN_INFO, "BGP EVPN instance information"); DEFINE_QOBJ_TYPE(bgp_master) DEFINE_QOBJ_TYPE(bgp) DEFINE_QOBJ_TYPE(peer) +DEFINE_HOOK(bgp_inst_delete, (struct bgp *bgp), (bgp)) /* BGP process wide configuration. */ static struct bgp_master bgp_master; @@ -171,8 +173,6 @@ int bgp_option_set(int flag) { switch (flag) { case BGP_OPT_NO_FIB: - case BGP_OPT_MULTIPLE_INSTANCE: - case BGP_OPT_CONFIG_CISCO: case BGP_OPT_NO_LISTEN: case BGP_OPT_NO_ZEBRA: SET_FLAG(bm->options, flag); @@ -186,13 +186,9 @@ int bgp_option_set(int flag) int bgp_option_unset(int flag) { switch (flag) { - case BGP_OPT_MULTIPLE_INSTANCE: - if (listcount(bm->bgp) > 1) - return BGP_ERR_MULTIPLE_INSTANCE_USED; /* Fall through. */ case BGP_OPT_NO_ZEBRA: case BGP_OPT_NO_FIB: - case BGP_OPT_CONFIG_CISCO: UNSET_FLAG(bm->options, flag); break; default: @@ -206,24 +202,6 @@ int bgp_option_check(int flag) return CHECK_FLAG(bm->options, flag); } -/* BGP flag manipulation. */ -int bgp_flag_set(struct bgp *bgp, int flag) -{ - SET_FLAG(bgp->flags, flag); - return 0; -} - -int bgp_flag_unset(struct bgp *bgp, int flag) -{ - UNSET_FLAG(bgp->flags, flag); - return 0; -} - -int bgp_flag_check(struct bgp *bgp, int flag) -{ - return CHECK_FLAG(bgp->flags, flag); -} - /* Internal function to set BGP structure configureation flag. */ static void bgp_config_set(struct bgp *bgp, int config) { @@ -240,8 +218,11 @@ static int bgp_config_check(struct bgp *bgp, int config) return CHECK_FLAG(bgp->config, config); } -/* Set BGP router identifier. */ -static int bgp_router_id_set(struct bgp *bgp, const struct in_addr *id) +/* Set BGP router identifier; distinguish between explicit config and other + * cases. + */ +static int bgp_router_id_set(struct bgp *bgp, const struct in_addr *id, + bool is_config) { struct peer *peer; struct listnode *node, *nnode; @@ -251,9 +232,9 @@ static int bgp_router_id_set(struct bgp *bgp, const struct in_addr *id) /* EVPN uses router id in RD, withdraw them */ if (is_evpn_enabled()) - bgp_evpn_handle_router_id_update(bgp, TRUE); + bgp_evpn_handle_router_id_update(bgp, true); - vpn_handle_router_id_update(bgp, TRUE); + vpn_handle_router_id_update(bgp, true, is_config); IPV4_ADDR_COPY(&bgp->router_id, id); @@ -270,9 +251,9 @@ static int bgp_router_id_set(struct bgp *bgp, const struct in_addr *id) /* EVPN uses router id in RD, update them */ if (is_evpn_enabled()) - bgp_evpn_handle_router_id_update(bgp, FALSE); + bgp_evpn_handle_router_id_update(bgp, false); - vpn_handle_router_id_update(bgp, FALSE); + vpn_handle_router_id_update(bgp, false, is_config); return 0; } @@ -304,9 +285,20 @@ void bgp_router_id_zebra_bump(vrf_id_t vrf_id, const struct prefix *router_id) */ if (bgp->established_peers == 0) { if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug("RID change : vrf %u, RTR ID %s", - bgp->vrf_id, inet_ntoa(*addr)); - bgp_router_id_set(bgp, addr); + zlog_debug( + "RID change : vrf %s(%u), RTR ID %s", + bgp->name_pretty, + bgp->vrf_id, + inet_ntoa(*addr)); + /* + * 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); } } } @@ -324,9 +316,20 @@ void bgp_router_id_zebra_bump(vrf_id_t vrf_id, const struct prefix *router_id) */ if (bgp->established_peers == 0) { if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug("RID change : vrf %u, RTR ID %s", - bgp->vrf_id, inet_ntoa(*addr)); - bgp_router_id_set(bgp, addr); + zlog_debug( + "RID change : vrf %s(%u), RTR ID %s", + bgp->name_pretty, + bgp->vrf_id, + inet_ntoa(*addr)); + /* + * 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); } } @@ -334,11 +337,12 @@ void bgp_router_id_zebra_bump(vrf_id_t vrf_id, const struct prefix *router_id) } } -int bgp_router_id_static_set(struct bgp *bgp, struct in_addr id) +void bgp_router_id_static_set(struct bgp *bgp, struct in_addr id) { bgp->router_id_static = id; - bgp_router_id_set(bgp, id.s_addr ? &id : &bgp->router_id_zebra); - return 0; + bgp_router_id_set(bgp, + id.s_addr != INADDR_ANY ? &id : &bgp->router_id_zebra, + true /* is config */); } /* BGP's cluster-id control. */ @@ -405,21 +409,21 @@ time_t bgp_clock(void) } /* BGP timer configuration. */ -int bgp_timers_set(struct bgp *bgp, uint32_t keepalive, uint32_t holdtime) +void bgp_timers_set(struct bgp *bgp, uint32_t keepalive, uint32_t holdtime, + uint32_t connect_retry) { bgp->default_keepalive = (keepalive < holdtime / 3 ? keepalive : holdtime / 3); bgp->default_holdtime = holdtime; - - return 0; + bgp->default_connect_retry = connect_retry; } -int bgp_timers_unset(struct bgp *bgp) +/* mostly for completeness - CLI uses its own defaults */ +void bgp_timers_unset(struct bgp *bgp) { bgp->default_keepalive = BGP_DEFAULT_KEEPALIVE; bgp->default_holdtime = BGP_DEFAULT_HOLDTIME; - - return 0; + bgp->default_connect_retry = BGP_DEFAULT_CONNECT_RETRY; } /* BGP confederation configuration. */ @@ -441,10 +445,12 @@ int bgp_confederation_id_set(struct bgp *bgp, as_t as) AS change. Just Reset EBGP sessions, not CONFED sessions. If we were not doing confederation before, reset all EBGP sessions. */ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { + bgp_peer_sort_t ptype = peer_sort(peer); + /* We're looking for peers who's AS is not local or part of our confederation. */ if (already_confed) { - if (peer_sort(peer) == BGP_PEER_EBGP) { + if (ptype == BGP_PEER_EBGP) { peer->local_as = as; if (BGP_IS_VALID_STATE_FOR_NOTIF( peer->status)) { @@ -460,9 +466,9 @@ int bgp_confederation_id_set(struct bgp *bgp, as_t as) /* Not doign confederation before, so reset every non-local session */ - if (peer_sort(peer) != BGP_PEER_IBGP) { + if (ptype != BGP_PEER_IBGP) { /* Reset the local_as to be our EBGP one */ - if (peer_sort(peer) == BGP_PEER_EBGP) + if (ptype == BGP_PEER_EBGP) peer->local_as = as; if (BGP_IS_VALID_STATE_FOR_NOTIF( peer->status)) { @@ -505,18 +511,18 @@ int bgp_confederation_id_unset(struct bgp *bgp) } /* Is an AS part of the confed or not? */ -int bgp_confederation_peers_check(struct bgp *bgp, as_t as) +bool bgp_confederation_peers_check(struct bgp *bgp, as_t as) { int i; if (!bgp) - return 0; + return false; for (i = 0; i < bgp->confed_peers_cnt; i++) if (bgp->confed_peers[i] == as) - return 1; + return true; - return 0; + return false; } /* Add an AS to the confederation set. */ @@ -812,9 +818,9 @@ int peer_cmp(struct peer *p1, struct peer *p2) return sockunion_cmp(&p1->su, &p2->su); } -static unsigned int peer_hash_key_make(void *p) +static unsigned int peer_hash_key_make(const void *p) { - struct peer *peer = p; + const struct peer *peer = p; return sockunion_hash(&peer->su); } @@ -881,82 +887,6 @@ void peer_af_flag_inherit(struct peer *peer, afi_t afi, safi_t safi, COND_FLAG(peer->af_flags[afi][safi], flag, group_val); } -static bool peergroup_flag_check(struct peer *peer, uint32_t flag) -{ - if (!peer_group_active(peer)) { - if (CHECK_FLAG(peer->flags_invert, flag)) - return !CHECK_FLAG(peer->flags, flag); - else - return !!CHECK_FLAG(peer->flags, flag); - } - - return !!CHECK_FLAG(peer->flags_override, flag); -} - -static bool peergroup_af_flag_check(struct peer *peer, afi_t afi, safi_t safi, - uint32_t flag) -{ - if (!peer_group_active(peer)) { - if (CHECK_FLAG(peer->af_flags_invert[afi][safi], flag)) - return !peer_af_flag_check(peer, afi, safi, flag); - else - return !!peer_af_flag_check(peer, afi, safi, flag); - } - - return !!CHECK_FLAG(peer->af_flags_override[afi][safi], flag); -} - -static bool peergroup_filter_check(struct peer *peer, afi_t afi, safi_t safi, - uint8_t type, int direct) -{ - struct bgp_filter *filter; - - if (peer_group_active(peer)) - return !!CHECK_FLAG(peer->filter_override[afi][safi][direct], - type); - - filter = &peer->filter[afi][safi]; - switch (type) { - case PEER_FT_DISTRIBUTE_LIST: - return !!(filter->dlist[direct].name); - case PEER_FT_FILTER_LIST: - return !!(filter->aslist[direct].name); - case PEER_FT_PREFIX_LIST: - return !!(filter->plist[direct].name); - case PEER_FT_ROUTE_MAP: - return !!(filter->map[direct].name); - case PEER_FT_UNSUPPRESS_MAP: - return !!(filter->usmap.name); - default: - return false; - } -} - -/* Return true if the addpath type is set for peer and different from - * peer-group. - */ -static int peergroup_af_addpath_check(struct peer *peer, afi_t afi, safi_t safi) -{ - enum bgp_addpath_strat type, g_type; - - type = peer->addpath_type[afi][safi]; - - if (type != BGP_ADDPATH_NONE) { - if (peer_group_active(peer)) { - g_type = peer->group->conf->addpath_type[afi][safi]; - - if (type != g_type) - return 1; - else - return 0; - } - - return 1; - } - - return 0; -} - /* Check peer's AS number and determines if this peer is IBGP or EBGP */ static inline bgp_peer_sort_t peer_calc_sort(struct peer *peer) { @@ -1051,6 +981,11 @@ bgp_peer_sort_t peer_sort(struct peer *peer) return peer->sort; } +bgp_peer_sort_t peer_sort_lookup(struct peer *peer) +{ + return peer->sort; +} + static void peer_free(struct peer *peer) { afi_t afi; @@ -1080,26 +1015,10 @@ static void peer_free(struct peer *peer) XFREE(MTYPE_PEER_TX_SHUTDOWN_MSG, peer->tx_shutdown_message); - if (peer->desc) { - XFREE(MTYPE_PEER_DESC, peer->desc); - peer->desc = NULL; - } - - /* Free allocated host character. */ - if (peer->host) { - XFREE(MTYPE_BGP_PEER_HOST, peer->host); - peer->host = NULL; - } - - if (peer->domainname) { - XFREE(MTYPE_BGP_PEER_HOST, peer->domainname); - peer->domainname = NULL; - } - - if (peer->ifname) { - XFREE(MTYPE_BGP_PEER_IFNAME, peer->ifname); - peer->ifname = NULL; - } + XFREE(MTYPE_PEER_DESC, peer->desc); + XFREE(MTYPE_BGP_PEER_HOST, peer->host); + XFREE(MTYPE_BGP_PEER_HOST, peer->domainname); + XFREE(MTYPE_BGP_PEER_IFNAME, peer->ifname); /* Update source configuration. */ if (peer->update_source) { @@ -1107,10 +1026,7 @@ static void peer_free(struct peer *peer) peer->update_source = NULL; } - if (peer->update_if) { - XFREE(MTYPE_PEER_UPDATE_SOURCE, peer->update_if); - peer->update_if = NULL; - } + XFREE(MTYPE_PEER_UPDATE_SOURCE, peer->update_if); XFREE(MTYPE_TMP, peer->notify.data); memset(&peer->notify, 0, sizeof(struct bgp_notify)); @@ -1120,10 +1036,7 @@ static void peer_free(struct peer *peer) bgp_sync_delete(peer); - if (peer->conf_if) { - XFREE(MTYPE_PEER_CONF_IF, peer->conf_if); - peer->conf_if = NULL; - } + XFREE(MTYPE_PEER_CONF_IF, peer->conf_if); bfd_info_free(&(peer->bfd_info)); @@ -1175,6 +1088,119 @@ struct peer *peer_unlock_with_caller(const char *name, struct peer *peer) return peer; } +/* BGP GR changes */ + +int bgp_global_gr_init(struct bgp *bgp) +{ + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug("%s called ..", __func__); + + int local_GLOBAL_GR_FSM[BGP_GLOBAL_GR_MODE][BGP_GLOBAL_GR_EVENT_CMD] = { + /* GLOBAL_HELPER Mode */ + { + /*Event -> */ + /*GLOBAL_GR_cmd*/ /*no_Global_GR_cmd*/ + GLOBAL_GR, GLOBAL_INVALID, + /*GLOBAL_DISABLE_cmd*/ /*no_Global_Disable_cmd*/ + GLOBAL_DISABLE, GLOBAL_INVALID + }, + /* GLOBAL_GR Mode */ + { + /*Event -> */ + /*GLOBAL_GR_cmd*/ /*no_Global_GR_cmd*/ + GLOBAL_INVALID, GLOBAL_HELPER, + /*GLOBAL_DISABLE_cmd*/ /*no_Global_Disable_cmd*/ + GLOBAL_DISABLE, GLOBAL_INVALID + }, + /* GLOBAL_DISABLE Mode */ + { + /*Event -> */ + /*GLOBAL_GR_cmd */ /*no_Global_GR_cmd*/ + GLOBAL_GR, GLOBAL_INVALID, + /*GLOBAL_DISABLE_cmd*//*no_Global_Disable_cmd*/ + GLOBAL_INVALID, GLOBAL_HELPER + }, + /* GLOBAL_INVALID Mode */ + { + /*Event -> */ + /*GLOBAL_GR_cmd*/ /*no_Global_GR_cmd*/ + GLOBAL_INVALID, GLOBAL_INVALID, + /*GLOBAL_DISABLE_cmd*/ /*no_Global_Disable_cmd*/ + GLOBAL_INVALID, GLOBAL_INVALID + } + }; + memcpy(bgp->GLOBAL_GR_FSM, local_GLOBAL_GR_FSM, + sizeof(local_GLOBAL_GR_FSM)); + + bgp->global_gr_present_state = GLOBAL_HELPER; + bgp->present_zebra_gr_state = ZEBRA_GR_DISABLE; + + return BGP_GR_SUCCESS; +} + +int bgp_peer_gr_init(struct peer *peer) +{ + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug("%s called ..", __func__); + + struct bgp_peer_gr local_Peer_GR_FSM[BGP_PEER_GR_MODE] + [BGP_PEER_GR_EVENT_CMD] = { + { + /* PEER_HELPER Mode */ + /* Event-> */ /* PEER_GR_CMD */ /* NO_PEER_GR_CMD */ + { PEER_GR, bgp_peer_gr_action }, {PEER_INVALID, NULL }, + /* Event-> */ /* PEER_DISABLE_CMD */ /* NO_PEER_DISABLE_CMD */ + {PEER_DISABLE, bgp_peer_gr_action }, {PEER_INVALID, NULL }, + /* Event-> */ /* PEER_HELPER_cmd */ /* NO_PEER_HELPER_CMD */ + { PEER_INVALID, NULL }, {PEER_GLOBAL_INHERIT, + bgp_peer_gr_action } + }, + { + /* PEER_GR Mode */ + /* Event-> */ /* PEER_GR_CMD */ /* NO_PEER_GR_CMD */ + { PEER_INVALID, NULL }, { PEER_GLOBAL_INHERIT, + bgp_peer_gr_action }, + /* Event-> */ /* PEER_DISABLE_CMD */ /* NO_PEER_DISABLE_CMD */ + {PEER_DISABLE, bgp_peer_gr_action }, { PEER_INVALID, NULL }, + /* Event-> */ /* PEER_HELPER_cmd */ /* NO_PEER_HELPER_CMD */ + { PEER_HELPER, bgp_peer_gr_action }, { PEER_INVALID, NULL } + }, + { + /* PEER_DISABLE Mode */ + /* Event-> */ /* PEER_GR_CMD */ /* NO_PEER_GR_CMD */ + { PEER_GR, bgp_peer_gr_action }, { PEER_INVALID, NULL }, + /* Event-> */ /* PEER_DISABLE_CMD */ /* NO_PEER_DISABLE_CMD */ + { PEER_INVALID, NULL }, { PEER_GLOBAL_INHERIT, + bgp_peer_gr_action }, + /* Event-> */ /* PEER_HELPER_cmd */ /* NO_PEER_HELPER_CMD */ + { PEER_HELPER, bgp_peer_gr_action }, { PEER_INVALID, NULL } + }, + { + /* PEER_INVALID Mode */ + /* Event-> */ /* PEER_GR_CMD */ /* NO_PEER_GR_CMD */ + { PEER_INVALID, NULL }, { PEER_INVALID, NULL }, + /* Event-> */ /* PEER_DISABLE_CMD */ /* NO_PEER_DISABLE_CMD */ + { PEER_INVALID, NULL }, { PEER_INVALID, NULL }, + /* Event-> */ /* PEER_HELPER_cmd */ /* NO_PEER_HELPER_CMD */ + { PEER_INVALID, NULL }, { PEER_INVALID, NULL }, + }, + { + /* PEER_GLOBAL_INHERIT Mode */ + /* Event-> */ /* PEER_GR_CMD */ /* NO_PEER_GR_CMD */ + { PEER_GR, bgp_peer_gr_action }, { PEER_INVALID, NULL }, + /* Event-> */ /* PEER_DISABLE_CMD */ /* NO_PEER_DISABLE_CMD */ + { PEER_DISABLE, bgp_peer_gr_action}, { PEER_INVALID, NULL }, + /* Event-> */ /* PEER_HELPER_cmd */ /* NO_PEER_HELPER_CMD */ + { PEER_HELPER, bgp_peer_gr_action }, { PEER_INVALID, NULL } + } + }; + memcpy(&peer->PEER_GR_FSM, local_Peer_GR_FSM, + sizeof(local_Peer_GR_FSM)); + peer->peer_gr_present_state = PEER_GLOBAL_INHERIT; + bgp_peer_move_to_gr_mode(peer, PEER_GLOBAL_INHERIT); + + return BGP_GR_SUCCESS; +} /* Allocate new peer object, implicitely locked. */ struct peer *peer_new(struct bgp *bgp) @@ -1186,8 +1212,6 @@ struct peer *peer_new(struct bgp *bgp) /* bgp argument is absolutely required */ assert(bgp); - if (!bgp) - return NULL; /* Allocate new peer. */ peer = XCALLOC(MTYPE_BGP_PEER, sizeof(struct peer)); @@ -1195,7 +1219,7 @@ struct peer *peer_new(struct bgp *bgp) /* Set default value. */ peer->fd = -1; peer->v_start = BGP_INIT_START_TIMER; - peer->v_connect = BGP_DEFAULT_CONNECT_RETRY; + peer->v_connect = bgp->default_connect_retry; peer->status = Idle; peer->ostatus = Idle; peer->cur_event = peer->last_event = peer->last_major_event = 0; @@ -1205,21 +1229,18 @@ struct peer *peer_new(struct bgp *bgp) /* Set default flags. */ FOREACH_AFI_SAFI (afi, safi) { - if (!bgp_option_check(BGP_OPT_CONFIG_CISCO)) { - SET_FLAG(peer->af_flags[afi][safi], - PEER_FLAG_SEND_COMMUNITY); - SET_FLAG(peer->af_flags[afi][safi], - PEER_FLAG_SEND_EXT_COMMUNITY); - SET_FLAG(peer->af_flags[afi][safi], - PEER_FLAG_SEND_LARGE_COMMUNITY); - - SET_FLAG(peer->af_flags_invert[afi][safi], - PEER_FLAG_SEND_COMMUNITY); - SET_FLAG(peer->af_flags_invert[afi][safi], - PEER_FLAG_SEND_EXT_COMMUNITY); - SET_FLAG(peer->af_flags_invert[afi][safi], - PEER_FLAG_SEND_LARGE_COMMUNITY); - } + SET_FLAG(peer->af_flags[afi][safi], PEER_FLAG_SEND_COMMUNITY); + SET_FLAG(peer->af_flags[afi][safi], + PEER_FLAG_SEND_EXT_COMMUNITY); + SET_FLAG(peer->af_flags[afi][safi], + PEER_FLAG_SEND_LARGE_COMMUNITY); + + SET_FLAG(peer->af_flags_invert[afi][safi], + PEER_FLAG_SEND_COMMUNITY); + SET_FLAG(peer->af_flags_invert[afi][safi], + PEER_FLAG_SEND_EXT_COMMUNITY); + SET_FLAG(peer->af_flags_invert[afi][safi], + PEER_FLAG_SEND_LARGE_COMMUNITY); peer->addpath_type[afi][safi] = BGP_ADDPATH_NONE; } @@ -1229,6 +1250,9 @@ struct peer *peer_new(struct bgp *bgp) SET_FLAG(peer->sflags, PEER_STATUS_CAPABILITY_OPEN); + /* Initialize per peer bgp GR FSM */ + bgp_peer_gr_init(peer); + /* Create buffers. */ peer->ibuf = stream_fifo_new(); peer->obuf = stream_fifo_new(); @@ -1288,6 +1312,9 @@ void peer_xfer_config(struct peer *peer_dst, struct peer *peer_src) peer_dst->flags = peer_src->flags; peer_dst->cap = peer_src->cap; + peer_dst->peer_gr_present_state = peer_src->peer_gr_present_state; + peer_dst->peer_gr_new_status_flag = peer_src->peer_gr_new_status_flag; + peer_dst->local_as = peer_src->local_as; peer_dst->port = peer_src->port; (void)peer_sort(peer_dst); @@ -1327,10 +1354,7 @@ void peer_xfer_config(struct peer *peer_dst, struct peer *peer_src) if (peer_src->update_source) { if (peer_dst->update_source) sockunion_free(peer_dst->update_source); - if (peer_dst->update_if) { - XFREE(MTYPE_PEER_UPDATE_SOURCE, peer_dst->update_if); - peer_dst->update_if = NULL; - } + XFREE(MTYPE_PEER_UPDATE_SOURCE, peer_dst->update_if); peer_dst->update_source = sockunion_dup(peer_src->update_source); } else if (peer_src->update_if) { @@ -1403,8 +1427,8 @@ static int bgp_peer_conf_if_to_su_update_v4(struct peer *peer, return 0; } -static int bgp_peer_conf_if_to_su_update_v6(struct peer *peer, - struct interface *ifp) +static bool bgp_peer_conf_if_to_su_update_v6(struct peer *peer, + struct interface *ifp) { struct nbr_connected *ifc_nbr; @@ -1418,10 +1442,10 @@ static int bgp_peer_conf_if_to_su_update_v6(struct peer *peer, peer->su.sin6.sin6_len = sizeof(struct sockaddr_in6); #endif peer->su.sin6.sin6_scope_id = ifp->ifindex; - return 1; + return true; } - return 0; + return false; } /* @@ -1493,22 +1517,22 @@ void bgp_peer_conf_if_to_su_update(struct peer *peer) static void bgp_recalculate_afi_safi_bestpaths(struct bgp *bgp, afi_t afi, safi_t safi) { - struct bgp_node *rn, *nrn; + struct bgp_dest *dest, *ndest; struct bgp_table *table; - for (rn = bgp_table_top(bgp->rib[afi][safi]); rn; - rn = bgp_route_next(rn)) { - table = bgp_node_get_bgp_table_info(rn); + for (dest = bgp_table_top(bgp->rib[afi][safi]); dest; + dest = bgp_route_next(dest)) { + table = bgp_dest_get_bgp_table_info(dest); if (table != NULL) { /* Special handling for 2-level routing * tables. */ if (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP || safi == SAFI_EVPN) { - for (nrn = bgp_table_top(table); - nrn; nrn = bgp_route_next(nrn)) - bgp_process(bgp, nrn, afi, safi); + for (ndest = bgp_table_top(table); ndest; + ndest = bgp_route_next(ndest)) + bgp_process(bgp, ndest, afi, safi); } else - bgp_process(bgp, rn, afi, safi); + bgp_process(bgp, dest, afi, safi); } } } @@ -1583,12 +1607,21 @@ struct peer *peer_create(union sockunion *su, const char *conf_if, } active = peer_active(peer); + if (!active) { + if (peer->su.sa.sa_family == AF_UNSPEC) + peer->last_reset = PEER_DOWN_NBR_ADDR; + else + peer->last_reset = PEER_DOWN_NOAFI_ACTIVATED; + } /* Last read and reset time set */ peer->readtime = peer->resettime = bgp_clock(); /* Default TTL set. */ - peer->ttl = (peer->sort == BGP_PEER_IBGP) ? MAXTTL : 1; + peer->ttl = (peer->sort == BGP_PEER_IBGP) ? MAXTTL : BGP_DEFAULT_TTL; + + /* Default configured keepalives count for shutdown rtt command */ + peer->rtt_keepalive_conf = 1; SET_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE); @@ -1604,6 +1637,9 @@ struct peer *peer_create(union sockunion *su, const char *conf_if, else if (!active && peer_active(peer)) bgp_timer_set(peer); + bgp_peer_gr_flags_update(peer); + BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(bgp, bgp->peer); + return peer; } @@ -1642,7 +1678,7 @@ int bgp_afi_safi_peer_exists(struct bgp *bgp, afi_t afi, safi_t safi) /* Change peer's AS number. */ void peer_as_change(struct peer *peer, as_t as, int as_specified) { - bgp_peer_sort_t type; + bgp_peer_sort_t origtype, newtype; /* Stop peer. */ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { @@ -1653,7 +1689,7 @@ void peer_as_change(struct peer *peer, as_t as, int as_specified) } else bgp_session_reset(peer); } - type = peer_sort(peer); + origtype = peer_sort_lookup(peer); peer->as = as; peer->as_type = as_specified; @@ -1664,21 +1700,22 @@ void peer_as_change(struct peer *peer, as_t as, int as_specified) else peer->local_as = peer->bgp->as; + newtype = peer_sort(peer); /* Advertisement-interval reset */ if (!CHECK_FLAG(peer->flags, PEER_FLAG_ROUTEADV)) { - peer->v_routeadv = (peer_sort(peer) == BGP_PEER_IBGP) + peer->v_routeadv = (newtype == BGP_PEER_IBGP) ? BGP_DEFAULT_IBGP_ROUTEADV : BGP_DEFAULT_EBGP_ROUTEADV; } /* TTL reset */ - if (peer_sort(peer) == BGP_PEER_IBGP) + if (newtype == BGP_PEER_IBGP) peer->ttl = MAXTTL; - else if (type == BGP_PEER_IBGP) - peer->ttl = 1; + else if (origtype == BGP_PEER_IBGP) + peer->ttl = BGP_DEFAULT_TTL; /* reflector-client reset */ - if (peer_sort(peer) != BGP_PEER_IBGP) { + if (newtype != BGP_PEER_IBGP) { UNSET_FLAG(peer->af_flags[AFI_IP][SAFI_UNICAST], PEER_FLAG_REFLECTOR_CLIENT); UNSET_FLAG(peer->af_flags[AFI_IP][SAFI_MULTICAST], @@ -1708,7 +1745,7 @@ void peer_as_change(struct peer *peer, as_t as, int as_specified) } /* local-as reset */ - if (peer_sort(peer) != BGP_PEER_EBGP) { + if (newtype != BGP_PEER_EBGP) { peer->change_local_as = 0; peer_flag_unset(peer, PEER_FLAG_LOCAL_AS); peer_flag_unset(peer, PEER_FLAG_LOCAL_AS_NO_PREPEND); @@ -1790,7 +1827,7 @@ int peer_remote_as(struct bgp *bgp, union sockunion *su, const char *conf_if, /* If this is IPv4 unicast configuration and "no bgp default ipv4-unicast" is specified. */ - if (bgp_flag_check(bgp, BGP_FLAG_NO_DEFAULT_IPV4) + if (CHECK_FLAG(bgp->flags, BGP_FLAG_NO_DEFAULT_IPV4) && afi == AFI_IP && safi == SAFI_UNICAST) peer_create(su, conf_if, bgp, local_as, *as, as_type, 0, 0, NULL); @@ -2044,11 +2081,11 @@ int peer_activate(struct peer *peer, afi_t afi, safi_t safi) /* If this is the first peer to be activated for this * afi/labeled-unicast recalc bestpaths to trigger label allocation */ - if (safi == SAFI_LABELED_UNICAST + if (ret != BGP_ERR_PEER_SAFI_CONFLICT && safi == SAFI_LABELED_UNICAST && !bgp->allocate_mpls_labels[afi][SAFI_UNICAST]) { if (BGP_DEBUG(zebra, ZEBRA)) - zlog_info( + zlog_debug( "peer(s) are now active for labeled-unicast, allocate MPLS labels"); bgp->allocate_mpls_labels[afi][SAFI_UNICAST] = 1; @@ -2062,27 +2099,27 @@ int peer_activate(struct peer *peer, afi_t afi, safi_t safi) return ret; } -static int non_peergroup_deactivate_af(struct peer *peer, afi_t afi, - safi_t safi) +static bool non_peergroup_deactivate_af(struct peer *peer, afi_t afi, + safi_t safi) { if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { flog_err(EC_BGP_PEER_GROUP, "%s was called for peer-group %s", __func__, peer->host); - return 1; + return true; } /* Nothing to do if we've already deactivated this peer */ if (!peer->afc[afi][safi]) - return 0; + return false; /* De-activate the address family configuration. */ peer->afc[afi][safi] = 0; if (peer_af_delete(peer, afi, safi) != 0) { flog_err(EC_BGP_PEER_DELETE, - "couldn't delete af structure for peer %s", - peer->host); - return 1; + "couldn't delete af structure for peer %s(%s, %s)", + peer->host, afi2str(afi), safi2str(safi)); + return true; } if (peer->status == Established) { @@ -2108,7 +2145,7 @@ static int non_peergroup_deactivate_af(struct peer *peer, afi_t afi, } } - return 0; + return false; } int peer_deactivate(struct peer *peer, afi_t afi, safi_t safi) @@ -2130,9 +2167,10 @@ int peer_deactivate(struct peer *peer, afi_t afi, safi_t safi) group = peer->group; if (peer_af_delete(peer, afi, safi) != 0) { - flog_err(EC_BGP_PEER_DELETE, - "couldn't delete af structure for peer %s", - peer->host); + flog_err( + EC_BGP_PEER_DELETE, + "couldn't delete af structure for peer %s(%s, %s)", + peer->host, afi2str(afi), safi2str(safi)); } for (ALL_LIST_ELEMENTS(group->peer, node, nnode, tmp_peer)) { @@ -2151,7 +2189,7 @@ int peer_deactivate(struct peer *peer, afi_t afi, safi_t safi) && !bgp_afi_safi_peer_exists(bgp, afi, safi)) { if (BGP_DEBUG(zebra, ZEBRA)) - zlog_info( + zlog_debug( "peer(s) are no longer active for labeled-unicast, deallocate MPLS labels"); bgp->allocate_mpls_labels[afi][SAFI_UNICAST] = 0; @@ -2160,15 +2198,7 @@ int peer_deactivate(struct peer *peer, afi_t afi, safi_t safi) return ret; } -int peer_afc_set(struct peer *peer, afi_t afi, safi_t safi, int enable) -{ - if (enable) - return peer_activate(peer, afi, safi); - else - return peer_deactivate(peer, afi, safi); -} - -static void peer_nsf_stop(struct peer *peer) +void peer_nsf_stop(struct peer *peer) { afi_t afi; safi_t safi; @@ -2222,16 +2252,20 @@ int peer_delete(struct peer *peer) bgp = peer->bgp; accept_peer = CHECK_FLAG(peer->sflags, PEER_STATUS_ACCEPT_PEER); + bgp_keepalives_off(peer); bgp_reads_off(peer); bgp_writes_off(peer); assert(!CHECK_FLAG(peer->thread_flags, PEER_THREAD_WRITES_ON)); assert(!CHECK_FLAG(peer->thread_flags, PEER_THREAD_READS_ON)); + assert(!CHECK_FLAG(peer->thread_flags, PEER_THREAD_KEEPALIVES_ON)); if (CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT)) peer_nsf_stop(peer); SET_FLAG(peer->flags, PEER_FLAG_DELETE); + bgp_bfd_deregister_peer(peer); + /* If this peer belongs to peer group, clear up the relationship. */ if (peer->group) { @@ -2269,9 +2303,9 @@ int peer_delete(struct peer *peer) /* Password configuration */ if (CHECK_FLAG(peer->flags, PEER_FLAG_PASSWORD)) { XFREE(MTYPE_PEER_PASSWORD, peer->password); - if (!accept_peer && !BGP_PEER_SU_UNSPEC(peer) - && !CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) + && !CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP) + && !CHECK_FLAG(peer->flags, PEER_FLAG_DYNAMIC_NEIGHBOR)) bgp_md5_unset(peer); } @@ -2327,57 +2361,24 @@ int peer_delete(struct peer *peer) filter = &peer->filter[afi][safi]; for (i = FILTER_IN; i < FILTER_MAX; i++) { - if (filter->dlist[i].name) { - XFREE(MTYPE_BGP_FILTER_NAME, - filter->dlist[i].name); - filter->dlist[i].name = NULL; - } - - if (filter->plist[i].name) { - XFREE(MTYPE_BGP_FILTER_NAME, - filter->plist[i].name); - filter->plist[i].name = NULL; - } - - if (filter->aslist[i].name) { - XFREE(MTYPE_BGP_FILTER_NAME, - filter->aslist[i].name); - filter->aslist[i].name = NULL; - } + XFREE(MTYPE_BGP_FILTER_NAME, filter->dlist[i].name); + XFREE(MTYPE_BGP_FILTER_NAME, filter->plist[i].name); + XFREE(MTYPE_BGP_FILTER_NAME, filter->aslist[i].name); } for (i = RMAP_IN; i < RMAP_MAX; i++) { - if (filter->map[i].name) { - XFREE(MTYPE_BGP_FILTER_NAME, - filter->map[i].name); - filter->map[i].name = NULL; - } + XFREE(MTYPE_BGP_FILTER_NAME, filter->map[i].name); } - if (filter->usmap.name) { - XFREE(MTYPE_BGP_FILTER_NAME, filter->usmap.name); - filter->usmap.name = NULL; - } - - if (peer->default_rmap[afi][safi].name) { - XFREE(MTYPE_ROUTE_MAP_NAME, - peer->default_rmap[afi][safi].name); - peer->default_rmap[afi][safi].name = NULL; - } + XFREE(MTYPE_BGP_FILTER_NAME, filter->usmap.name); + XFREE(MTYPE_ROUTE_MAP_NAME, peer->default_rmap[afi][safi].name); } FOREACH_AFI_SAFI (afi, safi) peer_af_delete(peer, afi, safi); - if (peer->hostname) { - XFREE(MTYPE_BGP_PEER_HOST, peer->hostname); - peer->hostname = NULL; - } - - if (peer->domainname) { - XFREE(MTYPE_BGP_PEER_HOST, peer->domainname); - peer->domainname = NULL; - } + XFREE(MTYPE_BGP_PEER_HOST, peer->hostname); + XFREE(MTYPE_BGP_PEER_HOST, peer->domainname); peer_unlock(peer); /* initial reference */ @@ -2429,14 +2430,14 @@ struct peer_group *peer_group_get(struct bgp *bgp, const char *name) for (afi = AFI_IP; afi < AFI_MAX; afi++) group->listen_range[afi] = list_new(); group->conf = peer_new(bgp); - if (!bgp_flag_check(bgp, BGP_FLAG_NO_DEFAULT_IPV4)) + if (!CHECK_FLAG(bgp->flags, BGP_FLAG_NO_DEFAULT_IPV4)) group->conf->afc[AFI_IP][SAFI_UNICAST] = 1; XFREE(MTYPE_BGP_PEER_HOST, group->conf->host); group->conf->host = XSTRDUP(MTYPE_BGP_PEER_HOST, name); group->conf->group = group; group->conf->as = 0; - group->conf->ttl = 1; - group->conf->gtsm_hops = 0; + group->conf->ttl = BGP_DEFAULT_TTL; + group->conf->gtsm_hops = BGP_GTSM_HOPS_DISABLED; group->conf->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV; SET_FLAG(group->conf->sflags, PEER_STATUS_GROUP); listnode_add_sort(bgp->group, group); @@ -2460,8 +2461,9 @@ static void peer_group2peer_config_copy(struct peer_group *group, if (!CHECK_FLAG(peer->flags_override, PEER_FLAG_LOCAL_AS)) peer->change_local_as = conf->change_local_as; - /* TTL */ - peer->ttl = conf->ttl; + /* If peer-group has configured TTL then override it */ + if (conf->ttl != BGP_DEFAULT_TTL) + peer->ttl = conf->ttl; /* GTSM hops */ peer->gtsm_hops = conf->gtsm_hops; @@ -2486,7 +2488,7 @@ static void peer_group2peer_config_copy(struct peer_group *group, if (CHECK_FLAG(conf->flags, PEER_FLAG_TIMER_CONNECT)) peer->v_connect = conf->connect; else - peer->v_connect = BGP_DEFAULT_CONNECT_RETRY; + peer->v_connect = peer->bgp->default_connect_retry; } /* advertisement-interval apply */ @@ -2500,6 +2502,11 @@ static void peer_group2peer_config_copy(struct peer_group *group, : BGP_DEFAULT_EBGP_ROUTEADV; } + /* capability extended-nexthop apply */ + if (!CHECK_FLAG(peer->flags_override, PEER_FLAG_CAPABILITY_ENHE)) + if (CHECK_FLAG(conf->flags, PEER_FLAG_CAPABILITY_ENHE)) + SET_FLAG(peer->flags, PEER_FLAG_CAPABILITY_ENHE); + /* password apply */ if (!CHECK_FLAG(peer->flags_override, PEER_FLAG_PASSWORD)) PEER_STR_ATTR_INHERIT(peer, group, password, @@ -2520,6 +2527,9 @@ static void peer_group2peer_config_copy(struct peer_group *group, } } + /* Update GR flags for the peer. */ + bgp_peer_gr_flags_update(peer); + bgp_bfd_peer_group2peer_copy(conf, peer); } @@ -2552,6 +2562,28 @@ int peer_group_remote_as(struct bgp *bgp, const char *group_name, as_t *as, return 0; } +void peer_notify_unconfig(struct peer *peer) +{ + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) + bgp_notify_send(peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_PEER_UNCONFIG); +} + +void peer_group_notify_unconfig(struct peer_group *group) +{ + struct peer *peer, *other; + struct listnode *node, *nnode; + + for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) { + other = peer->doppelganger; + if (other && other->status != Deleted) { + other->group = NULL; + peer_notify_unconfig(other); + } else + peer_notify_unconfig(peer); + } +} + int peer_group_delete(struct peer_group *group) { struct bgp *bgp; @@ -2565,6 +2597,10 @@ int peer_group_delete(struct peer_group *group) for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) { other = peer->doppelganger; + + if (CHECK_FLAG(peer->flags, PEER_FLAG_CAPABILITY_ENHE)) + bgp_zebra_terminate_radv(bgp, peer); + peer_delete(peer); if (other && other->status != Deleted) { other->group = NULL; @@ -2576,7 +2612,7 @@ int peer_group_delete(struct peer_group *group) for (afi = AFI_IP; afi < AFI_MAX; afi++) { for (ALL_LIST_ELEMENTS(group->listen_range[afi], node, nnode, prefix)) { - prefix_free(prefix); + prefix_free(&prefix); } list_delete(&group->listen_range[afi]); } @@ -2609,6 +2645,9 @@ int peer_group_remote_as_delete(struct peer_group *group) for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) { other = peer->doppelganger; + if (CHECK_FLAG(peer->flags, PEER_FLAG_CAPABILITY_ENHE)) + bgp_zebra_terminate_radv(peer->bgp, peer); + peer_delete(peer); if (other && other->status != Deleted) { @@ -2648,7 +2687,7 @@ int peer_group_listen_range_add(struct peer_group *group, struct prefix *range) /* Update passwords for new ranges */ if (group->conf->password) - bgp_md5_set_prefix(prefix, group->conf->password); + bgp_md5_set_prefix(group->bgp, prefix, group->conf->password); return 0; } @@ -2684,8 +2723,7 @@ int peer_group_listen_range_del(struct peer_group *group, struct prefix *range) if (prefix_match(prefix, &prefix2)) { if (bgp_debug_neighbor_events(peer)) zlog_debug( - "Deleting dynamic neighbor %s group %s upon " - "delete of listen range %s", + "Deleting dynamic neighbor %s group %s upon delete of listen range %s", peer->host, group->name, buf); peer_delete(peer); } @@ -2696,7 +2734,7 @@ int peer_group_listen_range_del(struct peer_group *group, struct prefix *range) /* Remove passwords for deleted ranges */ if (group->conf->password) - bgp_md5_unset_prefix(prefix); + bgp_md5_unset_prefix(group->bgp, prefix); return 0; } @@ -2708,6 +2746,7 @@ int peer_group_bind(struct bgp *bgp, union sockunion *su, struct peer *peer, int first_member = 0; afi_t afi; safi_t safi; + bgp_peer_sort_t ptype, gtype; /* Lookup the peer. */ if (!peer) @@ -2736,15 +2775,16 @@ int peer_group_bind(struct bgp *bgp, union sockunion *su, struct peer *peer, peer->sort = group->conf->sort; } - if (!group->conf->as && peer_sort(peer)) { - if (peer_sort(group->conf) != BGP_PEER_INTERNAL - && peer_sort(group->conf) != peer_sort(peer)) { + ptype = peer_sort(peer); + if (!group->conf->as && ptype != BGP_PEER_UNSPECIFIED) { + gtype = peer_sort(group->conf); + if ((gtype != BGP_PEER_INTERNAL) && (gtype != ptype)) { if (as) *as = peer->as; return BGP_ERR_PEER_GROUP_PEER_TYPE_DIFFERENT; } - if (peer_sort(group->conf) == BGP_PEER_INTERNAL) + if (gtype == BGP_PEER_INTERNAL) first_member = 1; } @@ -2776,22 +2816,22 @@ int peer_group_bind(struct bgp *bgp, union sockunion *su, struct peer *peer, } if (first_member) { + gtype = peer_sort(group->conf); /* Advertisement-interval reset */ if (!CHECK_FLAG(group->conf->flags, PEER_FLAG_ROUTEADV)) { group->conf->v_routeadv = - (peer_sort(group->conf) - == BGP_PEER_IBGP) + (gtype == BGP_PEER_IBGP) ? BGP_DEFAULT_IBGP_ROUTEADV : BGP_DEFAULT_EBGP_ROUTEADV; } /* ebgp-multihop reset */ - if (peer_sort(group->conf) == BGP_PEER_IBGP) + if (gtype == BGP_PEER_IBGP) group->conf->ttl = MAXTTL; /* local-as reset */ - if (peer_sort(group->conf) != BGP_PEER_EBGP) { + if (gtype != BGP_PEER_EBGP) { group->conf->change_local_as = 0; peer_flag_unset(group->conf, PEER_FLAG_LOCAL_AS); @@ -2909,18 +2949,12 @@ static struct bgp *bgp_create(as_t *as, const char *name, XFREE(MTYPE_BGP_PEER_HOST, bgp->peer_self->host); bgp->peer_self->host = XSTRDUP(MTYPE_BGP_PEER_HOST, "Static announcement"); - if (bgp->peer_self->hostname != NULL) { - XFREE(MTYPE_BGP_PEER_HOST, bgp->peer_self->hostname); - bgp->peer_self->hostname = NULL; - } + XFREE(MTYPE_BGP_PEER_HOST, bgp->peer_self->hostname); if (cmd_hostname_get()) bgp->peer_self->hostname = XSTRDUP(MTYPE_BGP_PEER_HOST, cmd_hostname_get()); - if (bgp->peer_self->domainname != NULL) { - XFREE(MTYPE_BGP_PEER_HOST, bgp->peer_self->domainname); - bgp->peer_self->domainname = NULL; - } + XFREE(MTYPE_BGP_PEER_HOST, bgp->peer_self->domainname); if (cmd_domainname_get()) bgp->peer_self->domainname = XSTRDUP(MTYPE_BGP_PEER_HOST, cmd_domainname_get()); @@ -2943,36 +2977,34 @@ static struct bgp *bgp_create(as_t *as, const char *name, multipath_num, 0); bgp_maximum_paths_set(bgp, afi, safi, BGP_PEER_IBGP, multipath_num, 0); + /* Initialize graceful restart info */ + bgp->gr_info[afi][safi].eor_required = 0; + bgp->gr_info[afi][safi].eor_received = 0; + bgp->gr_info[afi][safi].t_select_deferral = NULL; + bgp->gr_info[afi][safi].t_route_select = NULL; + bgp->gr_info[afi][safi].route_list = list_new(); } - bgp->v_update_delay = BGP_UPDATE_DELAY_DEF; + bgp->v_update_delay = bm->v_update_delay; + bgp->v_establish_wait = bm->v_establish_wait; bgp->default_local_pref = BGP_DEFAULT_LOCAL_PREF; bgp->default_subgroup_pkt_queue_max = BGP_DEFAULT_SUBGROUP_PKT_QUEUE_MAX; - bgp->default_holdtime = BGP_DEFAULT_HOLDTIME; - bgp->default_keepalive = BGP_DEFAULT_KEEPALIVE; + bgp_timers_unset(bgp); bgp->restart_time = BGP_DEFAULT_RESTART_TIME; bgp->stalepath_time = BGP_DEFAULT_STALEPATH_TIME; + bgp->select_defer_time = BGP_DEFAULT_SELECT_DEFERRAL_TIME; + bgp->rib_stale_time = BGP_DEFAULT_RIB_STALE_TIME; bgp->dynamic_neighbors_limit = BGP_DYNAMIC_NEIGHBORS_LIMIT_DEFAULT; bgp->dynamic_neighbors_count = 0; - bgp->ebgp_requires_policy = DEFAULT_EBGP_POLICY_DISABLED; -#if DFLT_BGP_IMPORT_CHECK - bgp_flag_set(bgp, BGP_FLAG_IMPORT_CHECK); -#endif -#if DFLT_BGP_SHOW_HOSTNAME - bgp_flag_set(bgp, BGP_FLAG_SHOW_HOSTNAME); -#endif -#if DFLT_BGP_LOG_NEIGHBOR_CHANGES - bgp_flag_set(bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES); -#endif -#if DFLT_BGP_DETERMINISTIC_MED - bgp_flag_set(bgp, BGP_FLAG_DETERMINISTIC_MED); -#endif + bgp->lb_ref_bw = BGP_LINK_BW_REF_BW; + bgp->lb_handling = BGP_LINK_BW_ECMP; + bgp->reject_as_sets = false; bgp_addpath_init_bgp_data(&bgp->tx_addpath); bgp->as = *as; -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC if (inst_type != BGP_INSTANCE_TYPE_VRF) { bgp->rfapi = bgp_rfapi_new(bgp); assert(bgp->rfapi); @@ -2994,14 +3026,11 @@ static struct bgp *bgp_create(as_t *as, const char *name, bgp->vpn_policy[afi].export_vrf->del = bgp_vrf_string_name_delete; } - if (name) { + if (name) bgp->name = XSTRDUP(MTYPE_BGP, name); - } else { - /* TODO - The startup timer needs to be run for the whole of BGP - */ - thread_add_timer(bm->master, bgp_startup_timer_expire, bgp, - bgp->restart_time, &bgp->t_startup); - } + + thread_add_timer(bm->master, bgp_startup_timer_expire, bgp, + bgp->restart_time, &bgp->t_startup); /* printable name we can use in debug messages */ if (inst_type == BGP_INSTANCE_TYPE_DEFAULT) { @@ -3043,6 +3072,9 @@ static struct bgp *bgp_create(as_t *as, const char *name, bgp_evpn_init(bgp); bgp_pbr_init(bgp); + + /*initilize global GR FSM */ + bgp_global_gr_init(bgp); return bgp; } @@ -3136,7 +3168,7 @@ int bgp_handle_socket(struct bgp *bgp, struct vrf *vrf, vrf_id_t old_vrf_id, /* * suppress vrf socket */ - if (create == FALSE) { + if (!create) { bgp_close_vrf_socket(bgp); return 0; } @@ -3172,46 +3204,27 @@ int bgp_get(struct bgp **bgp_val, as_t *as, const char *name, struct vrf *vrf = NULL; /* Multiple instance check. */ - if (bgp_option_check(BGP_OPT_MULTIPLE_INSTANCE)) { - if (name) - bgp = bgp_lookup_by_name(name); - else - bgp = bgp_get_default(); - - /* Already exists. */ - if (bgp) { - if (bgp->as != *as) { - *as = bgp->as; - return BGP_ERR_INSTANCE_MISMATCH; - } - if (bgp->inst_type != inst_type) - return BGP_ERR_INSTANCE_MISMATCH; - *bgp_val = bgp; - return 0; - } - } else { - /* BGP instance name can not be specified for single instance. - */ - if (name) - return BGP_ERR_MULTIPLE_INSTANCE_NOT_SET; - - /* Get default BGP structure if exists. */ + if (name) + bgp = bgp_lookup_by_name(name); + else bgp = bgp_get_default(); - if (bgp) { - if (bgp->as != *as) { - *as = bgp->as; - return BGP_ERR_AS_MISMATCH; - } - *bgp_val = bgp; - return 0; + /* Already exists. */ + if (bgp) { + if (bgp->as != *as) { + *as = bgp->as; + return BGP_ERR_INSTANCE_MISMATCH; } + if (bgp->inst_type != inst_type) + return BGP_ERR_INSTANCE_MISMATCH; + *bgp_val = bgp; + return BGP_SUCCESS; } bgp = bgp_create(as, name, inst_type); if (bgp_option_check(BGP_OPT_NO_ZEBRA) && name) bgp->vrf_id = vrf_generate_id(); - bgp_router_id_set(bgp, &bgp->router_id_zebra); + bgp_router_id_set(bgp, &bgp->router_id_zebra, true); bgp_address_init(bgp); bgp_tip_hash_init(bgp); bgp_scan_init(bgp); @@ -3235,11 +3248,11 @@ int bgp_get(struct bgp **bgp_val, as_t *as, const char *name, if (IS_BGP_INST_KNOWN_TO_ZEBRA(bgp)) { if (BGP_DEBUG(zebra, ZEBRA)) zlog_debug("%s: Registering BGP instance %s to zebra", - __PRETTY_FUNCTION__, name); + __func__, name); bgp_zebra_instance_register(bgp); } - return 0; + return BGP_CREATED; } /* @@ -3305,14 +3318,42 @@ int bgp_delete(struct bgp *bgp) struct listnode *node, *next; struct vrf *vrf; afi_t afi; + safi_t safi; int i; + struct graceful_restart_info *gr_info; assert(bgp); + + /* make sure we withdraw any exported routes */ + vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, AFI_IP, bgp_get_default(), + bgp); + vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, AFI_IP6, bgp_get_default(), + bgp); + + bgp_vpn_leak_unimport(bgp); + + hook_call(bgp_inst_delete, bgp); + THREAD_OFF(bgp->t_startup); THREAD_OFF(bgp->t_maxmed_onstartup); THREAD_OFF(bgp->t_update_delay); THREAD_OFF(bgp->t_establish_wait); + /* Set flag indicating bgp instance delete in progress */ + SET_FLAG(bgp->flags, BGP_FLAG_DELETE_IN_PROGRESS); + + /* Delete the graceful restart info */ + FOREACH_AFI_SAFI (afi, safi) { + gr_info = &bgp->gr_info[afi][safi]; + if (!gr_info) + continue; + + BGP_TIMER_OFF(gr_info->t_select_deferral); + BGP_TIMER_OFF(gr_info->t_route_select); + if (gr_info->route_list) + list_delete(&gr_info->route_list); + } + if (BGP_DEBUG(zebra, ZEBRA)) { if (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT) zlog_debug("Deleting Default VRF"); @@ -3370,7 +3411,7 @@ int bgp_delete(struct bgp *bgp) /* TODO - Other memory may need to be freed - e.g., NHT */ -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC rfapi_delete(bgp); #endif bgp_cleanup_routes(bgp); @@ -3387,8 +3428,9 @@ int bgp_delete(struct bgp *bgp) /* Deregister from Zebra, if needed */ if (IS_BGP_INST_KNOWN_TO_ZEBRA(bgp)) { if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug("%s: deregistering this bgp %s instance from zebra", - __PRETTY_FUNCTION__, bgp->name); + zlog_debug( + "%s: deregistering this bgp %s instance from zebra", + __func__, bgp->name); bgp_zebra_instance_deregister(bgp); } @@ -3424,7 +3466,7 @@ void bgp_free(struct bgp *bgp) afi_t afi; safi_t safi; struct bgp_table *table; - struct bgp_node *rn; + struct bgp_dest *dest; struct bgp_rmap *rmap; QOBJ_UNREG(bgp); @@ -3441,9 +3483,9 @@ void bgp_free(struct bgp *bgp) /* Special handling for 2-level routing tables. */ if (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP || safi == SAFI_EVPN) { - for (rn = bgp_table_top(bgp->rib[afi][safi]); rn; - rn = bgp_route_next(rn)) { - table = bgp_node_get_bgp_table_info(rn); + for (dest = bgp_table_top(bgp->rib[afi][safi]); dest; + dest = bgp_route_next(dest)) { + table = bgp_dest_get_bgp_table_info(dest); bgp_table_finish(&table); } } @@ -3758,10 +3800,10 @@ static void peer_drop_dynamic_neighbor(struct peer *peer) } /* If peer is configured at least one address family return 1. */ -int peer_active(struct peer *peer) +bool peer_active(struct peer *peer) { if (BGP_PEER_SU_UNSPEC(peer)) - return 0; + return false; if (peer->afc[AFI_IP][SAFI_UNICAST] || peer->afc[AFI_IP][SAFI_MULTICAST] || peer->afc[AFI_IP][SAFI_LABELED_UNICAST] || peer->afc[AFI_IP][SAFI_MPLS_VPN] || peer->afc[AFI_IP][SAFI_ENCAP] @@ -3773,12 +3815,12 @@ int peer_active(struct peer *peer) || peer->afc[AFI_IP6][SAFI_ENCAP] || peer->afc[AFI_IP6][SAFI_FLOWSPEC] || peer->afc[AFI_L2VPN][SAFI_EVPN]) - return 1; - return 0; + return true; + return false; } /* If peer is negotiated at least one address family return 1. */ -int peer_active_nego(struct peer *peer) +bool peer_active_nego(struct peer *peer) { if (peer->afc_nego[AFI_IP][SAFI_UNICAST] || peer->afc_nego[AFI_IP][SAFI_MULTICAST] @@ -3793,8 +3835,8 @@ int peer_active_nego(struct peer *peer) || peer->afc_nego[AFI_IP6][SAFI_ENCAP] || peer->afc_nego[AFI_IP6][SAFI_FLOWSPEC] || peer->afc_nego[AFI_L2VPN][SAFI_EVPN]) - return 1; - return 0; + return true; + return false; } void peer_change_action(struct peer *peer, afi_t afi, safi_t safi, @@ -3851,6 +3893,7 @@ struct peer_flag_action { static const struct peer_flag_action peer_flag_action_list[] = { {PEER_FLAG_PASSIVE, 0, peer_change_reset}, {PEER_FLAG_SHUTDOWN, 0, peer_change_reset}, + {PEER_FLAG_RTT_SHUTDOWN, 0, peer_change_none}, {PEER_FLAG_DONT_CAPABILITY, 0, peer_change_none}, {PEER_FLAG_OVERRIDE_CAPABILITY, 0, peer_change_none}, {PEER_FLAG_STRICT_CAP_MATCH, 0, peer_change_none}, @@ -3888,6 +3931,7 @@ static const struct peer_flag_action peer_af_flag_action_list[] = { {PEER_FLAG_ORF_PREFIX_RM, 1, peer_change_reset}, {PEER_FLAG_MAX_PREFIX, 0, peer_change_none}, {PEER_FLAG_MAX_PREFIX_WARNING, 0, peer_change_none}, + {PEER_FLAG_MAX_PREFIX_FORCE, 0, peer_change_none}, {PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED, 0, peer_change_reset_out}, {PEER_FLAG_FORCE_NEXTHOP_SELF, 1, peer_change_reset_out}, {PEER_FLAG_REMOVE_PRIVATE_AS_ALL, 1, peer_change_reset_out}, @@ -3952,6 +3996,7 @@ static void peer_flag_modify_action(struct peer *peer, uint32_t flag) peer_nsf_stop(peer); UNSET_FLAG(peer->sflags, PEER_STATUS_PREFIX_OVERFLOW); + if (peer->t_pmax_restart) { BGP_TIMER_OFF(peer->t_pmax_restart); if (bgp_debug_neighbor_events(peer)) @@ -3960,9 +4005,6 @@ static void peer_flag_modify_action(struct peer *peer, uint32_t flag) peer->host); } - if (CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT)) - peer_nsf_stop(peer); - if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) { char *msg = peer->tx_shutdown_message; size_t msglen; @@ -4008,6 +4050,65 @@ static void peer_flag_modify_action(struct peer *peer, uint32_t flag) bgp_session_reset(peer); } +/* Enable global administrative shutdown of all peers of BGP instance */ +void bgp_shutdown_enable(struct bgp *bgp, const char *msg) +{ + struct peer *peer; + struct listnode *node; + + /* do nothing if already shut down */ + if (CHECK_FLAG(bgp->flags, BGP_FLAG_SHUTDOWN)) + return; + + /* informational log message */ + zlog_info("Enabled administrative shutdown on BGP instance AS %u", + bgp->as); + + /* iterate through peers of BGP instance */ + for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer)) { + /* continue, if peer is already in administrative shutdown. */ + if (CHECK_FLAG(peer->flags, PEER_FLAG_SHUTDOWN)) + continue; + + /* send a RFC 4486 notification message if necessary */ + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) { + if (msg) + bgp_notify_send_with_data( + peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN, + (uint8_t *)(msg), strlen(msg)); + else + bgp_notify_send( + peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN); + } + + /* reset start timer to initial value */ + peer->v_start = BGP_INIT_START_TIMER; + + /* trigger a RFC 4271 ManualStop event */ + BGP_EVENT_ADD(peer, BGP_Stop); + } + + /* set the BGP instances shutdown flag */ + SET_FLAG(bgp->flags, BGP_FLAG_SHUTDOWN); +} + +/* Disable global administrative shutdown of all peers of BGP instance */ +void bgp_shutdown_disable(struct bgp *bgp) +{ + /* do nothing if not shut down. */ + if (!CHECK_FLAG(bgp->flags, BGP_FLAG_SHUTDOWN)) + return; + + /* informational log message */ + zlog_info("Disabled administrative shutdown on BGP instance AS %u", + bgp->as); + + /* clear the BGP instances shutdown flag */ + UNSET_FLAG(bgp->flags, BGP_FLAG_SHUTDOWN); +} + /* Change specified peer flag. */ static int peer_flag_modify(struct peer *peer, uint32_t flag, int set) { @@ -4019,7 +4120,7 @@ static int peer_flag_modify(struct peer *peer, uint32_t flag, int set) struct peer_flag_action action; memset(&action, 0, sizeof(struct peer_flag_action)); - size = sizeof peer_flag_action_list / sizeof(struct peer_flag_action); + size = sizeof(peer_flag_action_list) / sizeof(struct peer_flag_action); invert = CHECK_FLAG(peer->flags_invert, flag); found = peer_flag_action_set(peer_flag_action_list, size, &action, @@ -4058,6 +4159,23 @@ static int peer_flag_modify(struct peer *peer, uint32_t flag, int set) /* Update flag override state accordingly. */ COND_FLAG(peer->flags_override, flag, set != invert); + /* + * For the extended next-hop encoding flag we need to turn RAs + * on if flag is being set, but only turn RAs off if the flag + * is being unset on this peer and if this peer is a member of a + * peer-group, the peer-group also doesn't have the flag set. + */ + if (flag == PEER_FLAG_CAPABILITY_ENHE) { + if (set) { + bgp_zebra_initiate_radv(peer->bgp, peer); + } else if (peer_group_active(peer)) { + if (!CHECK_FLAG(peer->group->conf->flags, flag)) + bgp_zebra_terminate_radv(peer->bgp, + peer); + } else + bgp_zebra_terminate_radv(peer->bgp, peer); + } + /* Execute flag action on peer. */ if (action.type == peer_change_reset) peer_flag_modify_action(peer, flag); @@ -4066,9 +4184,6 @@ static int peer_flag_modify(struct peer *peer, uint32_t flag, int set) return 0; } - if (set && flag == PEER_FLAG_CAPABILITY_ENHE) - bgp_nht_register_enhe_capability_interfaces(peer); - /* * Update peer-group members, unless they are explicitely overriding * peer-group configuration. @@ -4092,8 +4207,9 @@ static int peer_flag_modify(struct peer *peer, uint32_t flag, int set) /* Update flag on peer-group member. */ COND_FLAG(member->flags, flag, set != member_invert); - if (set && flag == PEER_FLAG_CAPABILITY_ENHE) - bgp_nht_register_enhe_capability_interfaces(member); + if (flag == PEER_FLAG_CAPABILITY_ENHE) + set ? bgp_zebra_initiate_radv(member->bgp, member) + : bgp_zebra_terminate_radv(member->bgp, member); /* Execute flag action on peer-group member. */ if (action.type == peer_change_reset) @@ -4122,9 +4238,10 @@ static int peer_af_flag_modify(struct peer *peer, afi_t afi, safi_t safi, struct peer *member; struct listnode *node, *nnode; struct peer_flag_action action; + bgp_peer_sort_t ptype; memset(&action, 0, sizeof(struct peer_flag_action)); - size = sizeof peer_af_flag_action_list + size = sizeof(peer_af_flag_action_list) / sizeof(struct peer_flag_action); invert = CHECK_FLAG(peer->af_flags_invert[afi][safi], flag); @@ -4135,18 +4252,17 @@ static int peer_af_flag_modify(struct peer *peer, afi_t afi, safi_t safi, if (!found) return BGP_ERR_INVALID_FLAG; + ptype = peer_sort(peer); /* Special check for reflector client. */ - if (flag & PEER_FLAG_REFLECTOR_CLIENT - && peer_sort(peer) != BGP_PEER_IBGP) + if (flag & PEER_FLAG_REFLECTOR_CLIENT && ptype != BGP_PEER_IBGP) return BGP_ERR_NOT_INTERNAL_PEER; /* Special check for remove-private-AS. */ - if (flag & PEER_FLAG_REMOVE_PRIVATE_AS - && peer_sort(peer) == BGP_PEER_IBGP) + if (flag & PEER_FLAG_REMOVE_PRIVATE_AS && ptype == BGP_PEER_IBGP) return BGP_ERR_REMOVE_PRIVATE_AS; /* as-override is not allowed for IBGP peers */ - if (flag & PEER_FLAG_AS_OVERRIDE && peer_sort(peer) == BGP_PEER_IBGP) + if (flag & PEER_FLAG_AS_OVERRIDE && ptype == BGP_PEER_IBGP) return BGP_ERR_AS_OVERRIDE; /* Handle flag updates where desired state matches current state. */ @@ -4193,6 +4309,19 @@ static int peer_af_flag_modify(struct peer *peer, afi_t afi, safi_t safi, } } + /* + * If the peer is a route server client let's not + * muck with the nexthop on the way out the door + */ + if (flag & PEER_FLAG_RSERVER_CLIENT) { + if (set) + SET_FLAG(peer->af_flags[afi][safi], + PEER_FLAG_NEXTHOP_UNCHANGED); + else + UNSET_FLAG(peer->af_flags[afi][safi], + PEER_FLAG_NEXTHOP_UNCHANGED); + } + /* Inherit from peer-group or set/unset flags accordingly. */ if (peer_group_active(peer) && set == invert) peer_af_flag_inherit(peer, afi, safi, flag); @@ -4295,18 +4424,16 @@ int peer_af_flag_unset(struct peer *peer, afi_t afi, safi_t safi, uint32_t flag) } -int peer_tx_shutdown_message_set(struct peer *peer, const char *msg) +void peer_tx_shutdown_message_set(struct peer *peer, const char *msg) { XFREE(MTYPE_PEER_TX_SHUTDOWN_MSG, peer->tx_shutdown_message); peer->tx_shutdown_message = msg ? XSTRDUP(MTYPE_PEER_TX_SHUTDOWN_MSG, msg) : NULL; - return 0; } -int peer_tx_shutdown_message_unset(struct peer *peer) +void peer_tx_shutdown_message_unset(struct peer *peer) { XFREE(MTYPE_PEER_TX_SHUTDOWN_MSG, peer->tx_shutdown_message); - return 0; } @@ -4320,11 +4447,15 @@ int peer_ebgp_multihop_set(struct peer *peer, int ttl) if (peer->sort == BGP_PEER_IBGP || peer->conf_if) return 0; + /* is there anything to do? */ + if (peer->ttl == ttl) + return 0; + /* see comment in peer_ttl_security_hops_set() */ if (ttl != MAXTTL) { if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { group = peer->group; - if (group->conf->gtsm_hops != 0) + if (group->conf->gtsm_hops != BGP_GTSM_HOPS_DISABLED) return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK; for (ALL_LIST_ELEMENTS(group->peer, node, nnode, @@ -4332,11 +4463,11 @@ int peer_ebgp_multihop_set(struct peer *peer, int ttl) if (peer1->sort == BGP_PEER_IBGP) continue; - if (peer1->gtsm_hops != 0) + if (peer1->gtsm_hops != BGP_GTSM_HOPS_DISABLED) return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK; } } else { - if (peer->gtsm_hops != 0) + if (peer->gtsm_hops != BGP_GTSM_HOPS_DISABLED) return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK; } } @@ -4344,7 +4475,7 @@ int peer_ebgp_multihop_set(struct peer *peer, int ttl) peer->ttl = ttl; if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { - if (peer->fd >= 0 && peer->sort != BGP_PEER_IBGP) { + if (peer->sort != BGP_PEER_IBGP) { if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) bgp_notify_send(peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_CONFIG_CHANGE); @@ -4377,13 +4508,13 @@ int peer_ebgp_multihop_unset(struct peer *peer) if (peer->sort == BGP_PEER_IBGP) return 0; - if (peer->gtsm_hops != 0 && peer->ttl != MAXTTL) + if (peer->gtsm_hops != BGP_GTSM_HOPS_DISABLED && peer->ttl != MAXTTL) return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK; if (peer_group_active(peer)) peer->ttl = peer->group->conf->ttl; else - peer->ttl = 1; + peer->ttl = BGP_DEFAULT_TTL; if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) @@ -4397,7 +4528,7 @@ int peer_ebgp_multihop_unset(struct peer *peer) if (peer->sort == BGP_PEER_IBGP) continue; - peer->ttl = 1; + peer->ttl = BGP_DEFAULT_TTL; if (peer->fd >= 0) { if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) @@ -4413,22 +4544,16 @@ int peer_ebgp_multihop_unset(struct peer *peer) } /* Neighbor description. */ -int peer_description_set(struct peer *peer, const char *desc) +void peer_description_set(struct peer *peer, const char *desc) { XFREE(MTYPE_PEER_DESC, peer->desc); peer->desc = XSTRDUP(MTYPE_PEER_DESC, desc); - - return 0; } -int peer_description_unset(struct peer *peer) +void peer_description_unset(struct peer *peer) { XFREE(MTYPE_PEER_DESC, peer->desc); - - peer->desc = NULL; - - return 0; } /* Neighbor update-source. */ @@ -4758,36 +4883,34 @@ int peer_default_originate_unset(struct peer *peer, afi_t afi, safi_t safi) continue; /* Remove flag and configuration on peer-group member. */ - UNSET_FLAG(peer->af_flags[afi][safi], + UNSET_FLAG(member->af_flags[afi][safi], PEER_FLAG_DEFAULT_ORIGINATE); - if (peer->default_rmap[afi][safi].name) + if (member->default_rmap[afi][safi].name) XFREE(MTYPE_ROUTE_MAP_NAME, - peer->default_rmap[afi][safi].name); - route_map_counter_decrement(peer->default_rmap[afi][safi].map); - peer->default_rmap[afi][safi].name = NULL; - peer->default_rmap[afi][safi].map = NULL; + member->default_rmap[afi][safi].name); + route_map_counter_decrement(member->default_rmap[afi][safi].map); + member->default_rmap[afi][safi].name = NULL; + member->default_rmap[afi][safi].map = NULL; /* Update peer route announcements. */ - if (peer->status == Established && peer->afc_nego[afi][safi]) { - update_group_adjust_peer(peer_af_find(peer, afi, safi)); - bgp_default_originate(peer, afi, safi, 1); - bgp_announce_route(peer, afi, safi); + if (member->status == Established && member->afc_nego[afi][safi]) { + update_group_adjust_peer(peer_af_find(member, afi, safi)); + bgp_default_originate(member, afi, safi, 1); + bgp_announce_route(member, afi, safi); } } return 0; } -int peer_port_set(struct peer *peer, uint16_t port) +void peer_port_set(struct peer *peer, uint16_t port) { peer->port = port; - return 0; } -int peer_port_unset(struct peer *peer) +void peer_port_unset(struct peer *peer) { peer->port = BGP_PORT_DEFAULT; - return 0; } /* @@ -4937,8 +5060,8 @@ int peer_timers_set(struct peer *peer, uint32_t keepalive, uint32_t holdtime) /* Set flag and configuration on peer-group member. */ SET_FLAG(member->flags, PEER_FLAG_TIMER); - PEER_ATTR_INHERIT(peer, peer->group, holdtime); - PEER_ATTR_INHERIT(peer, peer->group, keepalive); + PEER_ATTR_INHERIT(member, peer->group, holdtime); + PEER_ATTR_INHERIT(member, peer->group, keepalive); } return 0; @@ -5037,7 +5160,7 @@ int peer_timers_connect_unset(struct peer *peer) if (peer->connect) peer->v_connect = peer->connect; else - peer->v_connect = BGP_DEFAULT_CONNECT_RETRY; + peer->v_connect = peer->bgp->default_connect_retry; /* Skip peer-group mechanics for regular peers. */ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) @@ -5055,7 +5178,7 @@ int peer_timers_connect_unset(struct peer *peer) /* Remove flag and configuration on peer-group member. */ UNSET_FLAG(member->flags, PEER_FLAG_TIMER_CONNECT); member->connect = 0; - member->v_connect = BGP_DEFAULT_CONNECT_RETRY; + member->v_connect = peer->bgp->default_connect_retry; } return 0; @@ -5177,7 +5300,6 @@ void peer_interface_set(struct peer *peer, const char *str) void peer_interface_unset(struct peer *peer) { XFREE(MTYPE_BGP_PEER_IFNAME, peer->ifname); - peer->ifname = NULL; } /* Allow-as in. */ @@ -5293,11 +5415,6 @@ int peer_allowas_in_unset(struct peer *peer, afi_t afi, safi_t safi) PEER_FLAG_ALLOWAS_IN)) continue; - /* Skip peers where flag is already disabled. */ - if (!CHECK_FLAG(member->af_flags[afi][safi], - PEER_FLAG_ALLOWAS_IN)) - continue; - /* Remove flags and configuration on peer-group member. */ UNSET_FLAG(member->af_flags[afi][safi], PEER_FLAG_ALLOWAS_IN); UNSET_FLAG(member->af_flags[afi][safi], @@ -5316,9 +5433,9 @@ int peer_local_as_set(struct peer *peer, as_t as, int no_prepend, struct bgp *bgp = peer->bgp; struct peer *member; struct listnode *node, *nnode; + bgp_peer_sort_t ptype = peer_sort(peer); - if (peer_sort(peer) != BGP_PEER_EBGP - && peer_sort(peer) != BGP_PEER_INTERNAL) + if (ptype != BGP_PEER_EBGP && ptype != BGP_PEER_INTERNAL) return BGP_ERR_LOCAL_AS_ALLOWED_ONLY_FOR_EBGP; if (bgp->as == as) @@ -5533,9 +5650,9 @@ int peer_password_set(struct peer *peer, const char *password) struct prefix *lr; for (ALL_LIST_ELEMENTS_RO(peer->group->listen_range[AFI_IP], ln, lr)) - bgp_md5_set_prefix(lr, password); + bgp_md5_set_prefix(peer->bgp, lr, password); for (ALL_LIST_ELEMENTS_RO(peer->group->listen_range[AFI_IP6], ln, lr)) - bgp_md5_set_prefix(lr, password); + bgp_md5_set_prefix(peer->bgp, lr, password); return ret; } @@ -5571,7 +5688,6 @@ int peer_password_unset(struct peer *peer) /* Attempt to uninstall password on socket. */ if (!BGP_PEER_SU_UNSPEC(peer)) bgp_md5_unset(peer); - /* Skip peer-group mechanics for regular peers. */ return 0; } @@ -5606,9 +5722,9 @@ int peer_password_unset(struct peer *peer) struct prefix *lr; for (ALL_LIST_ELEMENTS_RO(peer->group->listen_range[AFI_IP], ln, lr)) - bgp_md5_unset_prefix(lr); + bgp_md5_unset_prefix(peer->bgp, lr); for (ALL_LIST_ELEMENTS_RO(peer->group->listen_range[AFI_IP6], ln, lr)) - bgp_md5_unset_prefix(lr); + bgp_md5_unset_prefix(peer->bgp, lr); return 0; } @@ -5792,7 +5908,7 @@ static void peer_distribute_update(struct access_list *access) } } } -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC vnc_prefix_list_update(bgp); #endif } @@ -6163,8 +6279,7 @@ static void peer_aslist_update(const char *aslist_name) static void peer_aslist_add(char *aslist_name) { peer_aslist_update(aslist_name); - route_map_notify_dependencies((char *)aslist_name, - RMAP_EVENT_ASLIST_ADDED); + route_map_notify_dependencies(aslist_name, RMAP_EVENT_ASLIST_ADDED); } static void peer_aslist_del(const char *aslist_name) @@ -6186,8 +6301,15 @@ int peer_route_map_set(struct peer *peer, afi_t afi, safi_t safi, int direct, /* Set configuration on peer. */ filter = &peer->filter[afi][safi]; - if (filter->map[direct].name) + if (filter->map[direct].name) { + /* If the neighbor is configured with the same route-map + * again then, ignore the duplicate configuration. + */ + if (strcmp(filter->map[direct].name, name) == 0) + return 0; + XFREE(MTYPE_BGP_FILTER_NAME, filter->map[direct].name); + } route_map_counter_decrement(filter->map[direct].map); filter->map[direct].name = XSTRDUP(MTYPE_BGP_FILTER_NAME, name); filter->map[direct].map = route_map; @@ -6414,13 +6536,19 @@ int peer_unsuppress_map_unset(struct peer *peer, afi_t afi, safi_t safi) int peer_maximum_prefix_set(struct peer *peer, afi_t afi, safi_t safi, uint32_t max, uint8_t threshold, int warning, - uint16_t restart) + uint16_t restart, bool force) { struct peer *member; struct listnode *node, *nnode; /* Set flags and configuration on peer. */ peer_af_flag_set(peer, afi, safi, PEER_FLAG_MAX_PREFIX); + + if (force) + peer_af_flag_set(peer, afi, safi, PEER_FLAG_MAX_PREFIX_FORCE); + else + peer_af_flag_unset(peer, afi, safi, PEER_FLAG_MAX_PREFIX_FORCE); + if (warning) peer_af_flag_set(peer, afi, safi, PEER_FLAG_MAX_PREFIX_WARNING); else @@ -6455,6 +6583,14 @@ int peer_maximum_prefix_set(struct peer *peer, afi_t afi, safi_t safi, member->pmax[afi][safi] = max; member->pmax_threshold[afi][safi] = threshold; member->pmax_restart[afi][safi] = restart; + + if (force) + SET_FLAG(member->af_flags[afi][safi], + PEER_FLAG_MAX_PREFIX_FORCE); + else + UNSET_FLAG(member->af_flags[afi][safi], + PEER_FLAG_MAX_PREFIX_FORCE); + if (warning) SET_FLAG(member->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING); @@ -6475,6 +6611,8 @@ int peer_maximum_prefix_unset(struct peer *peer, afi_t afi, safi_t safi) /* Inherit configuration from peer-group if peer is member. */ if (peer_group_active(peer)) { peer_af_flag_inherit(peer, afi, safi, PEER_FLAG_MAX_PREFIX); + peer_af_flag_inherit(peer, afi, safi, + PEER_FLAG_MAX_PREFIX_FORCE); peer_af_flag_inherit(peer, afi, safi, PEER_FLAG_MAX_PREFIX_WARNING); PEER_ATTR_INHERIT(peer, peer->group, pmax[afi][safi]); @@ -6486,6 +6624,7 @@ int peer_maximum_prefix_unset(struct peer *peer, afi_t afi, safi_t safi) /* Remove flags and configuration from peer. */ peer_af_flag_unset(peer, afi, safi, PEER_FLAG_MAX_PREFIX); + peer_af_flag_unset(peer, afi, safi, PEER_FLAG_MAX_PREFIX_FORCE); peer_af_flag_unset(peer, afi, safi, PEER_FLAG_MAX_PREFIX_WARNING); peer->pmax[afi][safi] = 0; peer->pmax_threshold[afi][safi] = 0; @@ -6509,6 +6648,8 @@ int peer_maximum_prefix_unset(struct peer *peer, afi_t afi, safi_t safi) */ UNSET_FLAG(member->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX); + UNSET_FLAG(member->af_flags[afi][safi], + PEER_FLAG_MAX_PREFIX_FORCE); UNSET_FLAG(member->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING); member->pmax[afi][safi] = 0; @@ -6529,16 +6670,17 @@ int is_ebgp_multihop_configured(struct peer *peer) if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { group = peer->group; if ((peer_sort(peer) != BGP_PEER_IBGP) - && (group->conf->ttl != 1)) + && (group->conf->ttl != BGP_DEFAULT_TTL)) return 1; for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer1)) { if ((peer_sort(peer1) != BGP_PEER_IBGP) - && (peer1->ttl != 1)) + && (peer1->ttl != BGP_DEFAULT_TTL)) return 1; } } else { - if ((peer_sort(peer) != BGP_PEER_IBGP) && (peer->ttl != 1)) + if ((peer_sort(peer) != BGP_PEER_IBGP) + && (peer->ttl != BGP_DEFAULT_TTL)) return 1; } return 0; @@ -6548,6 +6690,7 @@ int is_ebgp_multihop_configured(struct peer *peer) int peer_ttl_security_hops_set(struct peer *peer, int gtsm_hops) { struct peer_group *group; + struct peer *gpeer; struct listnode *node, *nnode; int ret; @@ -6565,7 +6708,8 @@ int peer_ttl_security_hops_set(struct peer *peer, int gtsm_hops) mess of this configuration parameter, and OpenBGPD got it right. */ - if ((peer->gtsm_hops == 0) && (peer->sort != BGP_PEER_IBGP)) { + if ((peer->gtsm_hops == BGP_GTSM_HOPS_DISABLED) + && (peer->sort != BGP_PEER_IBGP)) { if (is_ebgp_multihop_configured(peer)) return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK; @@ -6583,9 +6727,10 @@ int peer_ttl_security_hops_set(struct peer *peer, int gtsm_hops) return ret; } else { group = peer->group; + group->conf->gtsm_hops = gtsm_hops; for (ALL_LIST_ELEMENTS(group->peer, node, nnode, - peer)) { - peer->gtsm_hops = group->conf->gtsm_hops; + gpeer)) { + gpeer->gtsm_hops = group->conf->gtsm_hops; /* Calling ebgp multihop also resets the * session. @@ -6595,7 +6740,7 @@ int peer_ttl_security_hops_set(struct peer *peer, int gtsm_hops) * value is * irrelevant. */ - peer_ebgp_multihop_set(peer, MAXTTL); + peer_ebgp_multihop_set(gpeer, MAXTTL); } } } else { @@ -6616,9 +6761,10 @@ int peer_ttl_security_hops_set(struct peer *peer, int gtsm_hops) MAXTTL + 1 - gtsm_hops); } else { group = peer->group; + group->conf->gtsm_hops = gtsm_hops; for (ALL_LIST_ELEMENTS(group->peer, node, nnode, - peer)) { - peer->gtsm_hops = group->conf->gtsm_hops; + gpeer)) { + gpeer->gtsm_hops = group->conf->gtsm_hops; /* Change setting of existing peer * established then change value (may break @@ -6628,15 +6774,18 @@ int peer_ttl_security_hops_set(struct peer *peer, int gtsm_hops) * no session then do nothing (will get * handled by next connection) */ - if (peer->fd >= 0 && peer->gtsm_hops != 0) + if (gpeer->fd >= 0 + && gpeer->gtsm_hops + != BGP_GTSM_HOPS_DISABLED) sockopt_minttl( - peer->su.sa.sa_family, peer->fd, - MAXTTL + 1 - peer->gtsm_hops); - if ((peer->status < Established) - && peer->doppelganger - && (peer->doppelganger->fd >= 0)) - sockopt_minttl(peer->su.sa.sa_family, - peer->doppelganger->fd, + gpeer->su.sa.sa_family, + gpeer->fd, + MAXTTL + 1 - gpeer->gtsm_hops); + if ((gpeer->status < Established) + && gpeer->doppelganger + && (gpeer->doppelganger->fd >= 0)) + sockopt_minttl(gpeer->su.sa.sa_family, + gpeer->doppelganger->fd, MAXTTL + 1 - gtsm_hops); } } @@ -6659,7 +6808,7 @@ int peer_ttl_security_hops_unset(struct peer *peer) if (peer_group_active(peer)) peer->gtsm_hops = peer->group->conf->gtsm_hops; else - peer->gtsm_hops = 0; + peer->gtsm_hops = BGP_GTSM_HOPS_DISABLED; if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { /* Invoking ebgp_multihop_set will set the TTL back to the @@ -6682,7 +6831,7 @@ int peer_ttl_security_hops_unset(struct peer *peer) } else { group = peer->group; for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) { - peer->gtsm_hops = 0; + peer->gtsm_hops = BGP_GTSM_HOPS_DISABLED; if (peer->sort == BGP_PEER_EBGP) ret = peer_ebgp_multihop_unset(peer); else { @@ -6712,7 +6861,8 @@ int peer_ttl_security_hops_unset(struct peer *peer) */ int peer_clear(struct peer *peer, struct listnode **nnode) { - if (!CHECK_FLAG(peer->flags, PEER_FLAG_SHUTDOWN)) { + if (!CHECK_FLAG(peer->flags, PEER_FLAG_SHUTDOWN) + || !CHECK_FLAG(peer->bgp->flags, BGP_FLAG_SHUTDOWN)) { if (CHECK_FLAG(peer->sflags, PEER_STATUS_PREFIX_OVERFLOW)) { UNSET_FLAG(peer->sflags, PEER_STATUS_PREFIX_OVERFLOW); if (peer->t_pmax_restart) { @@ -6827,7 +6977,7 @@ char *peer_uptime(time_t uptime2, char *buf, size_t len, bool use_json, json_object *json) { time_t uptime1, epoch_tbuf; - struct tm *tm; + struct tm tm; /* If there is no connection has been done before print `never'. */ if (uptime2 == 0) { @@ -6842,21 +6992,21 @@ char *peer_uptime(time_t uptime2, char *buf, size_t len, bool use_json, /* Get current time. */ uptime1 = bgp_clock(); uptime1 -= uptime2; - tm = gmtime(&uptime1); + gmtime_r(&uptime1, &tm); if (uptime1 < ONE_DAY_SECOND) - snprintf(buf, len, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min, - tm->tm_sec); + snprintf(buf, len, "%02d:%02d:%02d", tm.tm_hour, tm.tm_min, + tm.tm_sec); else if (uptime1 < ONE_WEEK_SECOND) - snprintf(buf, len, "%dd%02dh%02dm", tm->tm_yday, tm->tm_hour, - tm->tm_min); + snprintf(buf, len, "%dd%02dh%02dm", tm.tm_yday, tm.tm_hour, + tm.tm_min); else if (uptime1 < ONE_YEAR_SECOND) - snprintf(buf, len, "%02dw%dd%02dh", tm->tm_yday / 7, - tm->tm_yday - ((tm->tm_yday / 7) * 7), tm->tm_hour); + snprintf(buf, len, "%02dw%dd%02dh", tm.tm_yday / 7, + tm.tm_yday - ((tm.tm_yday / 7) * 7), tm.tm_hour); else - snprintf(buf, len, "%02dy%02dw%dd", tm->tm_year - 70, - tm->tm_yday / 7, - tm->tm_yday - ((tm->tm_yday / 7) * 7)); + snprintf(buf, len, "%02dy%02dw%dd", tm.tm_year - 70, + tm.tm_yday / 7, + tm.tm_yday - ((tm.tm_yday / 7) * 7)); if (use_json) { epoch_tbuf = time(NULL) - uptime1; @@ -6869,1022 +7019,7 @@ char *peer_uptime(time_t uptime2, char *buf, size_t len, bool use_json, return buf; } -static void bgp_config_write_filter(struct vty *vty, struct peer *peer, - afi_t afi, safi_t safi) -{ - struct bgp_filter *filter; - char *addr; - - addr = peer->host; - filter = &peer->filter[afi][safi]; - - /* distribute-list. */ - if (peergroup_filter_check(peer, afi, safi, PEER_FT_DISTRIBUTE_LIST, - FILTER_IN)) - vty_out(vty, " neighbor %s distribute-list %s in\n", addr, - filter->dlist[FILTER_IN].name); - - if (peergroup_filter_check(peer, afi, safi, PEER_FT_DISTRIBUTE_LIST, - FILTER_OUT)) - vty_out(vty, " neighbor %s distribute-list %s out\n", addr, - filter->dlist[FILTER_OUT].name); - - /* prefix-list. */ - if (peergroup_filter_check(peer, afi, safi, PEER_FT_PREFIX_LIST, - FILTER_IN)) - vty_out(vty, " neighbor %s prefix-list %s in\n", addr, - filter->plist[FILTER_IN].name); - - if (peergroup_filter_check(peer, afi, safi, PEER_FT_PREFIX_LIST, - FILTER_OUT)) - vty_out(vty, " neighbor %s prefix-list %s out\n", addr, - filter->plist[FILTER_OUT].name); - - /* route-map. */ - if (peergroup_filter_check(peer, afi, safi, PEER_FT_ROUTE_MAP, RMAP_IN)) - vty_out(vty, " neighbor %s route-map %s in\n", addr, - filter->map[RMAP_IN].name); - - if (peergroup_filter_check(peer, afi, safi, PEER_FT_ROUTE_MAP, - RMAP_OUT)) - vty_out(vty, " neighbor %s route-map %s out\n", addr, - filter->map[RMAP_OUT].name); - - /* unsuppress-map */ - if (peergroup_filter_check(peer, afi, safi, PEER_FT_UNSUPPRESS_MAP, 0)) - vty_out(vty, " neighbor %s unsuppress-map %s\n", addr, - filter->usmap.name); - - /* filter-list. */ - if (peergroup_filter_check(peer, afi, safi, PEER_FT_FILTER_LIST, - FILTER_IN)) - vty_out(vty, " neighbor %s filter-list %s in\n", addr, - filter->aslist[FILTER_IN].name); - - if (peergroup_filter_check(peer, afi, safi, PEER_FT_FILTER_LIST, - FILTER_OUT)) - vty_out(vty, " neighbor %s filter-list %s out\n", addr, - filter->aslist[FILTER_OUT].name); -} - -/* BGP peer configuration display function. */ -static void bgp_config_write_peer_global(struct vty *vty, struct bgp *bgp, - struct peer *peer) -{ - struct peer *g_peer = NULL; - char buf[SU_ADDRSTRLEN]; - char *addr; - int if_pg_printed = FALSE; - int if_ras_printed = FALSE; - - /* Skip dynamic neighbors. */ - if (peer_dynamic_neighbor(peer)) - return; - - if (peer->conf_if) - addr = peer->conf_if; - else - addr = peer->host; - - /************************************ - ****** Global to the neighbor ****** - ************************************/ - if (peer->conf_if) { - if (CHECK_FLAG(peer->flags, PEER_FLAG_IFPEER_V6ONLY)) - vty_out(vty, " neighbor %s interface v6only", addr); - else - vty_out(vty, " neighbor %s interface", addr); - - if (peer_group_active(peer)) { - vty_out(vty, " peer-group %s", peer->group->name); - if_pg_printed = TRUE; - } else if (peer->as_type == AS_SPECIFIED) { - vty_out(vty, " remote-as %u", peer->as); - if_ras_printed = TRUE; - } else if (peer->as_type == AS_INTERNAL) { - vty_out(vty, " remote-as internal"); - if_ras_printed = TRUE; - } else if (peer->as_type == AS_EXTERNAL) { - vty_out(vty, " remote-as external"); - if_ras_printed = TRUE; - } - - vty_out(vty, "\n"); - } - - /* remote-as and peer-group */ - /* peer is a member of a peer-group */ - if (peer_group_active(peer)) { - g_peer = peer->group->conf; - - if (g_peer->as_type == AS_UNSPECIFIED && !if_ras_printed) { - if (peer->as_type == AS_SPECIFIED) { - vty_out(vty, " neighbor %s remote-as %u\n", - addr, peer->as); - } else if (peer->as_type == AS_INTERNAL) { - vty_out(vty, - " neighbor %s remote-as internal\n", - addr); - } else if (peer->as_type == AS_EXTERNAL) { - vty_out(vty, - " neighbor %s remote-as external\n", - addr); - } - } - - /* For swpX peers we displayed the peer-group - * via 'neighbor swpX interface peer-group WORD' */ - if (!if_pg_printed) - vty_out(vty, " neighbor %s peer-group %s\n", addr, - peer->group->name); - } - - /* peer is NOT a member of a peer-group */ - else { - /* peer is a peer-group, declare the peer-group */ - if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { - vty_out(vty, " neighbor %s peer-group\n", addr); - } - - if (!if_ras_printed) { - if (peer->as_type == AS_SPECIFIED) { - vty_out(vty, " neighbor %s remote-as %u\n", - addr, peer->as); - } else if (peer->as_type == AS_INTERNAL) { - vty_out(vty, - " neighbor %s remote-as internal\n", - addr); - } else if (peer->as_type == AS_EXTERNAL) { - vty_out(vty, - " neighbor %s remote-as external\n", - addr); - } - } - } - - /* local-as */ - if (peergroup_flag_check(peer, PEER_FLAG_LOCAL_AS)) { - vty_out(vty, " neighbor %s local-as %u", addr, - peer->change_local_as); - if (peergroup_flag_check(peer, PEER_FLAG_LOCAL_AS_NO_PREPEND)) - vty_out(vty, " no-prepend"); - if (peergroup_flag_check(peer, PEER_FLAG_LOCAL_AS_REPLACE_AS)) - vty_out(vty, " replace-as"); - vty_out(vty, "\n"); - } - - /* description */ - if (peer->desc) { - vty_out(vty, " neighbor %s description %s\n", addr, peer->desc); - } - - /* shutdown */ - if (peergroup_flag_check(peer, PEER_FLAG_SHUTDOWN)) { - if (peer->tx_shutdown_message) - vty_out(vty, " neighbor %s shutdown message %s\n", addr, - peer->tx_shutdown_message); - else - vty_out(vty, " neighbor %s shutdown\n", addr); - } - - /* bfd */ - if (peer->bfd_info) { - if (!peer_group_active(peer) || !g_peer->bfd_info) { - bgp_bfd_peer_config_write(vty, peer, addr); - } - } - - /* password */ - if (peergroup_flag_check(peer, PEER_FLAG_PASSWORD)) - vty_out(vty, " neighbor %s password %s\n", addr, - peer->password); - - /* neighbor solo */ - if (CHECK_FLAG(peer->flags, PEER_FLAG_LONESOUL)) { - if (!peer_group_active(peer)) { - vty_out(vty, " neighbor %s solo\n", addr); - } - } - - /* BGP port */ - if (peer->port != BGP_PORT_DEFAULT) { - vty_out(vty, " neighbor %s port %d\n", addr, peer->port); - } - - /* Local interface name */ - if (peer->ifname) { - vty_out(vty, " neighbor %s interface %s\n", addr, peer->ifname); - } - - /* passive */ - if (peergroup_flag_check(peer, PEER_FLAG_PASSIVE)) - vty_out(vty, " neighbor %s passive\n", addr); - - /* ebgp-multihop */ - if (peer->sort != BGP_PEER_IBGP && peer->ttl != 1 - && !(peer->gtsm_hops != 0 && peer->ttl == MAXTTL)) { - if (!peer_group_active(peer) || g_peer->ttl != peer->ttl) { - vty_out(vty, " neighbor %s ebgp-multihop %d\n", addr, - peer->ttl); - } - } - - /* ttl-security hops */ - if (peer->gtsm_hops != 0) { - if (!peer_group_active(peer) - || g_peer->gtsm_hops != peer->gtsm_hops) { - vty_out(vty, " neighbor %s ttl-security hops %d\n", - addr, peer->gtsm_hops); - } - } - - /* disable-connected-check */ - if (peergroup_flag_check(peer, PEER_FLAG_DISABLE_CONNECTED_CHECK)) - vty_out(vty, " neighbor %s disable-connected-check\n", addr); - - /* enforce-first-as */ - if (peergroup_flag_check(peer, PEER_FLAG_ENFORCE_FIRST_AS)) - vty_out(vty, " neighbor %s enforce-first-as\n", addr); - - /* update-source */ - if (peergroup_flag_check(peer, PEER_FLAG_UPDATE_SOURCE)) { - if (peer->update_source) - vty_out(vty, " neighbor %s update-source %s\n", addr, - sockunion2str(peer->update_source, buf, - SU_ADDRSTRLEN)); - else if (peer->update_if) - vty_out(vty, " neighbor %s update-source %s\n", addr, - peer->update_if); - } - - /* advertisement-interval */ - if (peergroup_flag_check(peer, PEER_FLAG_ROUTEADV)) - vty_out(vty, " neighbor %s advertisement-interval %u\n", addr, - peer->routeadv); - - /* timers */ - if (peergroup_flag_check(peer, PEER_FLAG_TIMER)) - vty_out(vty, " neighbor %s timers %u %u\n", addr, - peer->keepalive, peer->holdtime); - - /* timers connect */ - if (peergroup_flag_check(peer, PEER_FLAG_TIMER_CONNECT)) - vty_out(vty, " neighbor %s timers connect %u\n", addr, - peer->connect); - - /* capability dynamic */ - if (peergroup_flag_check(peer, PEER_FLAG_DYNAMIC_CAPABILITY)) - vty_out(vty, " neighbor %s capability dynamic\n", addr); - - /* capability extended-nexthop */ - if (peergroup_flag_check(peer, PEER_FLAG_CAPABILITY_ENHE)) { - if (!peer->conf_if) { - if (CHECK_FLAG(peer->flags_invert, - PEER_FLAG_CAPABILITY_ENHE)) - vty_out(vty, - " no neighbor %s capability extended-nexthop\n", - addr); - else - vty_out(vty, - " neighbor %s capability extended-nexthop\n", - addr); - } - } - - /* dont-capability-negotiation */ - if (peergroup_flag_check(peer, PEER_FLAG_DONT_CAPABILITY)) - vty_out(vty, " neighbor %s dont-capability-negotiate\n", addr); - - /* override-capability */ - if (peergroup_flag_check(peer, PEER_FLAG_OVERRIDE_CAPABILITY)) - vty_out(vty, " neighbor %s override-capability\n", addr); - - /* strict-capability-match */ - if (peergroup_flag_check(peer, PEER_FLAG_STRICT_CAP_MATCH)) - vty_out(vty, " neighbor %s strict-capability-match\n", addr); -} - -/* BGP peer configuration display function. */ -static void bgp_config_write_peer_af(struct vty *vty, struct bgp *bgp, - struct peer *peer, afi_t afi, safi_t safi) -{ - struct peer *g_peer = NULL; - char *addr; - bool flag_scomm, flag_secomm, flag_slcomm; - - /* Skip dynamic neighbors. */ - if (peer_dynamic_neighbor(peer)) - return; - - if (peer->conf_if) - addr = peer->conf_if; - else - addr = peer->host; - - /************************************ - ****** Per AF to the neighbor ****** - ************************************/ - if (peer_group_active(peer)) { - g_peer = peer->group->conf; - - /* If the peer-group is active but peer is not, print a 'no - * activate' */ - if (g_peer->afc[afi][safi] && !peer->afc[afi][safi]) { - vty_out(vty, " no neighbor %s activate\n", addr); - } - - /* If the peer-group is not active but peer is, print an - 'activate' */ - else if (!g_peer->afc[afi][safi] && peer->afc[afi][safi]) { - vty_out(vty, " neighbor %s activate\n", addr); - } - } else { - if (peer->afc[afi][safi]) { - if ((afi == AFI_IP) && (safi == SAFI_UNICAST)) { - if (bgp_flag_check(bgp, - BGP_FLAG_NO_DEFAULT_IPV4)) { - vty_out(vty, " neighbor %s activate\n", - addr); - } - } else - vty_out(vty, " neighbor %s activate\n", addr); - } else { - if ((afi == AFI_IP) && (safi == SAFI_UNICAST)) { - if (!bgp_flag_check(bgp, - BGP_FLAG_NO_DEFAULT_IPV4)) { - vty_out(vty, - " no neighbor %s activate\n", - addr); - } - } - } - } - - /* addpath TX knobs */ - if (peergroup_af_addpath_check(peer, afi, safi)) { - switch (peer->addpath_type[afi][safi]) { - case BGP_ADDPATH_ALL: - vty_out(vty, " neighbor %s addpath-tx-all-paths\n", - addr); - break; - case BGP_ADDPATH_BEST_PER_AS: - vty_out(vty, - " neighbor %s addpath-tx-bestpath-per-AS\n", - addr); - break; - case BGP_ADDPATH_MAX: - case BGP_ADDPATH_NONE: - break; - } - } - - /* ORF capability. */ - if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_ORF_PREFIX_SM) - || peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_ORF_PREFIX_RM)) { - vty_out(vty, " neighbor %s capability orf prefix-list", addr); - - if (peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_ORF_PREFIX_SM) - && peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_ORF_PREFIX_RM)) - vty_out(vty, " both"); - else if (peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_ORF_PREFIX_SM)) - vty_out(vty, " send"); - else - vty_out(vty, " receive"); - vty_out(vty, "\n"); - } - - /* Route reflector client. */ - if (peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_REFLECTOR_CLIENT)) { - vty_out(vty, " neighbor %s route-reflector-client\n", addr); - } - - /* next-hop-self force */ - if (peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_FORCE_NEXTHOP_SELF)) { - vty_out(vty, " neighbor %s next-hop-self force\n", addr); - } - - /* next-hop-self */ - if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_NEXTHOP_SELF)) { - vty_out(vty, " neighbor %s next-hop-self\n", addr); - } - - /* remove-private-AS */ - if (peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_REMOVE_PRIVATE_AS_ALL_REPLACE)) { - vty_out(vty, " neighbor %s remove-private-AS all replace-AS\n", - addr); - } - - else if (peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_REMOVE_PRIVATE_AS_REPLACE)) { - vty_out(vty, " neighbor %s remove-private-AS replace-AS\n", - addr); - } - - else if (peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_REMOVE_PRIVATE_AS_ALL)) { - vty_out(vty, " neighbor %s remove-private-AS all\n", addr); - } - - else if (peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_REMOVE_PRIVATE_AS)) { - vty_out(vty, " neighbor %s remove-private-AS\n", addr); - } - - /* as-override */ - if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_AS_OVERRIDE)) { - vty_out(vty, " neighbor %s as-override\n", addr); - } - - /* send-community print. */ - flag_scomm = peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_SEND_COMMUNITY); - flag_secomm = peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_SEND_EXT_COMMUNITY); - flag_slcomm = peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_SEND_LARGE_COMMUNITY); - - if (!bgp_option_check(BGP_OPT_CONFIG_CISCO)) { - if (flag_scomm && flag_secomm && flag_slcomm) { - vty_out(vty, " no neighbor %s send-community all\n", - addr); - } else { - if (flag_scomm) - vty_out(vty, - " no neighbor %s send-community\n", - addr); - if (flag_secomm) - vty_out(vty, - " no neighbor %s send-community extended\n", - addr); - - if (flag_slcomm) - vty_out(vty, - " no neighbor %s send-community large\n", - addr); - } - } else { - if (flag_scomm && flag_secomm && flag_slcomm) { - vty_out(vty, " neighbor %s send-community all\n", - addr); - } else if (flag_scomm && flag_secomm) { - vty_out(vty, " neighbor %s send-community both\n", - addr); - } else { - if (flag_scomm) - vty_out(vty, " neighbor %s send-community\n", - addr); - if (flag_secomm) - vty_out(vty, - " neighbor %s send-community extended\n", - addr); - if (flag_slcomm) - vty_out(vty, - " neighbor %s send-community large\n", - addr); - } - } - - /* Default information */ - if (peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_DEFAULT_ORIGINATE)) { - vty_out(vty, " neighbor %s default-originate", addr); - - if (peer->default_rmap[afi][safi].name) - vty_out(vty, " route-map %s", - peer->default_rmap[afi][safi].name); - - vty_out(vty, "\n"); - } - - /* Soft reconfiguration inbound. */ - if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_SOFT_RECONFIG)) { - vty_out(vty, " neighbor %s soft-reconfiguration inbound\n", - addr); - } - - /* maximum-prefix. */ - if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_MAX_PREFIX)) { - vty_out(vty, " neighbor %s maximum-prefix %lu", addr, - peer->pmax[afi][safi]); - - if (peer->pmax_threshold[afi][safi] - != MAXIMUM_PREFIX_THRESHOLD_DEFAULT) - vty_out(vty, " %u", peer->pmax_threshold[afi][safi]); - if (peer_af_flag_check(peer, afi, safi, - PEER_FLAG_MAX_PREFIX_WARNING)) - vty_out(vty, " warning-only"); - if (peer->pmax_restart[afi][safi]) - vty_out(vty, " restart %u", - peer->pmax_restart[afi][safi]); - - vty_out(vty, "\n"); - } - - /* Route server client. */ - if (peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_RSERVER_CLIENT)) { - vty_out(vty, " neighbor %s route-server-client\n", addr); - } - - /* Nexthop-local unchanged. */ - if (peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED)) { - vty_out(vty, " neighbor %s nexthop-local unchanged\n", addr); - } - - /* allowas-in <1-10> */ - if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_ALLOWAS_IN)) { - if (peer_af_flag_check(peer, afi, safi, - PEER_FLAG_ALLOWAS_IN_ORIGIN)) { - vty_out(vty, " neighbor %s allowas-in origin\n", addr); - } else if (peer->allowas_in[afi][safi] == 3) { - vty_out(vty, " neighbor %s allowas-in\n", addr); - } else { - vty_out(vty, " neighbor %s allowas-in %d\n", addr, - peer->allowas_in[afi][safi]); - } - } - - /* weight */ - if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_WEIGHT)) - vty_out(vty, " neighbor %s weight %lu\n", addr, - peer->weight[afi][safi]); - - /* Filter. */ - bgp_config_write_filter(vty, peer, afi, safi); - - /* atribute-unchanged. */ - if (peer_af_flag_check(peer, afi, safi, PEER_FLAG_AS_PATH_UNCHANGED) - || (safi != SAFI_EVPN - && peer_af_flag_check(peer, afi, safi, - PEER_FLAG_NEXTHOP_UNCHANGED)) - || peer_af_flag_check(peer, afi, safi, PEER_FLAG_MED_UNCHANGED)) { - - if (!peer_group_active(peer) - || peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_AS_PATH_UNCHANGED) - || peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_NEXTHOP_UNCHANGED) - || peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_MED_UNCHANGED)) { - - vty_out(vty, - " neighbor %s attribute-unchanged%s%s%s\n", - addr, - peer_af_flag_check(peer, afi, safi, - PEER_FLAG_AS_PATH_UNCHANGED) - ? " as-path" - : "", - peer_af_flag_check(peer, afi, safi, - PEER_FLAG_NEXTHOP_UNCHANGED) - ? " next-hop" - : "", - peer_af_flag_check(peer, afi, safi, - PEER_FLAG_MED_UNCHANGED) - ? " med" - : ""); - } - } -} - -/* Address family based peer configuration display. */ -static void bgp_config_write_family(struct vty *vty, struct bgp *bgp, afi_t afi, - safi_t safi) -{ - struct peer *peer; - struct peer_group *group; - struct listnode *node, *nnode; - - - vty_frame(vty, " !\n address-family "); - if (afi == AFI_IP) { - if (safi == SAFI_UNICAST) - vty_frame(vty, "ipv4 unicast"); - else if (safi == SAFI_LABELED_UNICAST) - vty_frame(vty, "ipv4 labeled-unicast"); - else if (safi == SAFI_MULTICAST) - vty_frame(vty, "ipv4 multicast"); - else if (safi == SAFI_MPLS_VPN) - vty_frame(vty, "ipv4 vpn"); - else if (safi == SAFI_ENCAP) - vty_frame(vty, "ipv4 encap"); - else if (safi == SAFI_FLOWSPEC) - vty_frame(vty, "ipv4 flowspec"); - } else if (afi == AFI_IP6) { - if (safi == SAFI_UNICAST) - vty_frame(vty, "ipv6 unicast"); - else if (safi == SAFI_LABELED_UNICAST) - vty_frame(vty, "ipv6 labeled-unicast"); - else if (safi == SAFI_MULTICAST) - vty_frame(vty, "ipv6 multicast"); - else if (safi == SAFI_MPLS_VPN) - vty_frame(vty, "ipv6 vpn"); - else if (safi == SAFI_ENCAP) - vty_frame(vty, "ipv6 encap"); - else if (safi == SAFI_FLOWSPEC) - vty_frame(vty, "ipv6 flowspec"); - } else if (afi == AFI_L2VPN) { - if (safi == SAFI_EVPN) - vty_frame(vty, "l2vpn evpn"); - } - vty_frame(vty, "\n"); - - bgp_config_write_distance(vty, bgp, afi, safi); - - bgp_config_write_network(vty, bgp, afi, safi); - - bgp_config_write_redistribute(vty, bgp, afi, safi); - - for (ALL_LIST_ELEMENTS(bgp->group, node, nnode, group)) - bgp_config_write_peer_af(vty, bgp, group->conf, afi, safi); - - for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { - /* Skip dynamic neighbors. */ - if (peer_dynamic_neighbor(peer)) - continue; - - /* Do not display doppelganger peers */ - if (CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE)) - bgp_config_write_peer_af(vty, bgp, peer, afi, safi); - } - - bgp_config_write_maxpaths(vty, bgp, afi, safi); - bgp_config_write_table_map(vty, bgp, afi, safi); - - if (safi == SAFI_EVPN) - bgp_config_write_evpn_info(vty, bgp, afi, safi); - - if (safi == SAFI_FLOWSPEC) - bgp_fs_config_write_pbr(vty, bgp, afi, safi); - - if (safi == SAFI_UNICAST) { - bgp_vpn_policy_config_write_afi(vty, bgp, afi); - if (CHECK_FLAG(bgp->af_flags[afi][safi], - BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT)) { - - vty_out(vty, " export vpn\n"); - } - if (CHECK_FLAG(bgp->af_flags[afi][safi], - BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT)) { - - vty_out(vty, " import vpn\n"); - } - if (CHECK_FLAG(bgp->af_flags[afi][safi], - BGP_CONFIG_VRF_TO_VRF_IMPORT)) { - char *name; - - for (ALL_LIST_ELEMENTS_RO( - bgp->vpn_policy[afi].import_vrf, node, - name)) - vty_out(vty, " import vrf %s\n", name); - } - } - - vty_endframe(vty, " exit-address-family\n"); -} - -/* clang-format off */ -#if CONFDATE > 20190517 -CPP_NOTICE("bgpd: remove 'bgp enforce-first-as' config migration from bgp_config_write") -#endif -/* clang-format on */ - -int bgp_config_write(struct vty *vty) -{ - int write = 0; - struct bgp *bgp; - struct peer_group *group; - struct peer *peer; - struct listnode *node, *nnode; - struct listnode *mnode, *mnnode; - - /* BGP Multiple instance. */ - if (!bgp_option_check(BGP_OPT_MULTIPLE_INSTANCE)) { - vty_out(vty, "no bgp multiple-instance\n"); - write++; - } - - /* BGP Config type. */ - if (bgp_option_check(BGP_OPT_CONFIG_CISCO)) { - vty_out(vty, "bgp config-type cisco\n"); - write++; - } - - if (bm->rmap_update_timer != RMAP_DEFAULT_UPDATE_TIMER) - vty_out(vty, "bgp route-map delay-timer %u\n", - bm->rmap_update_timer); - - if (write) - vty_out(vty, "!\n"); - - /* BGP configuration. */ - for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp)) { - - /* skip all auto created vrf as they dont have user config */ - if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_AUTO)) - continue; - - /* Migrate deprecated 'bgp enforce-first-as' - * config to 'neighbor * enforce-first-as' configs - */ - if (bgp_flag_check(bgp, BGP_FLAG_ENFORCE_FIRST_AS)) { - for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) - peer_flag_set(peer, PEER_FLAG_ENFORCE_FIRST_AS); - bgp_flag_unset(bgp, BGP_FLAG_ENFORCE_FIRST_AS); - } - - /* Router bgp ASN */ - vty_out(vty, "router bgp %u", bgp->as); - - if (bgp_option_check(BGP_OPT_MULTIPLE_INSTANCE)) { - if (bgp->name) - vty_out(vty, " %s %s", - (bgp->inst_type - == BGP_INSTANCE_TYPE_VIEW) - ? "view" - : "vrf", - bgp->name); - } - vty_out(vty, "\n"); - - /* No Synchronization */ - if (bgp_option_check(BGP_OPT_CONFIG_CISCO)) - vty_out(vty, " no synchronization\n"); - - /* BGP fast-external-failover. */ - if (CHECK_FLAG(bgp->flags, BGP_FLAG_NO_FAST_EXT_FAILOVER)) - vty_out(vty, " no bgp fast-external-failover\n"); - - /* BGP router ID. */ - if (bgp->router_id_static.s_addr != 0) - vty_out(vty, " bgp router-id %s\n", - inet_ntoa(bgp->router_id_static)); - - /* BGP log-neighbor-changes. */ - if (!!bgp_flag_check(bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES) - != DFLT_BGP_LOG_NEIGHBOR_CHANGES) - vty_out(vty, " %sbgp log-neighbor-changes\n", - bgp_flag_check(bgp, - BGP_FLAG_LOG_NEIGHBOR_CHANGES) - ? "" - : "no "); - - /* BGP configuration. */ - if (bgp_flag_check(bgp, BGP_FLAG_ALWAYS_COMPARE_MED)) - vty_out(vty, " bgp always-compare-med\n"); - - /* RFC8212 default eBGP policy. */ - if (bgp->ebgp_requires_policy - == DEFAULT_EBGP_POLICY_ENABLED) - vty_out(vty, " bgp ebgp-requires-policy\n"); - - /* BGP default ipv4-unicast. */ - if (bgp_flag_check(bgp, BGP_FLAG_NO_DEFAULT_IPV4)) - vty_out(vty, " no bgp default ipv4-unicast\n"); - - /* BGP default local-preference. */ - if (bgp->default_local_pref != BGP_DEFAULT_LOCAL_PREF) - vty_out(vty, " bgp default local-preference %u\n", - bgp->default_local_pref); - - /* BGP default show-hostname */ - if (!!bgp_flag_check(bgp, BGP_FLAG_SHOW_HOSTNAME) - != DFLT_BGP_SHOW_HOSTNAME) - vty_out(vty, " %sbgp default show-hostname\n", - bgp_flag_check(bgp, BGP_FLAG_SHOW_HOSTNAME) - ? "" - : "no "); - - /* BGP default subgroup-pkt-queue-max. */ - if (bgp->default_subgroup_pkt_queue_max - != BGP_DEFAULT_SUBGROUP_PKT_QUEUE_MAX) - vty_out(vty, " bgp default subgroup-pkt-queue-max %u\n", - bgp->default_subgroup_pkt_queue_max); - - /* BGP client-to-client reflection. */ - if (bgp_flag_check(bgp, BGP_FLAG_NO_CLIENT_TO_CLIENT)) - vty_out(vty, " no bgp client-to-client reflection\n"); - - /* BGP cluster ID. */ - if (CHECK_FLAG(bgp->config, BGP_CONFIG_CLUSTER_ID)) - vty_out(vty, " bgp cluster-id %s\n", - inet_ntoa(bgp->cluster_id)); - - /* Disable ebgp connected nexthop check */ - if (bgp_flag_check(bgp, BGP_FLAG_DISABLE_NH_CONNECTED_CHK)) - vty_out(vty, - " bgp disable-ebgp-connected-route-check\n"); - - /* Confederation identifier*/ - if (CHECK_FLAG(bgp->config, BGP_CONFIG_CONFEDERATION)) - vty_out(vty, " bgp confederation identifier %u\n", - bgp->confed_id); - - /* Confederation peer */ - if (bgp->confed_peers_cnt > 0) { - int i; - - vty_out(vty, " bgp confederation peers"); - - for (i = 0; i < bgp->confed_peers_cnt; i++) - vty_out(vty, " %u", bgp->confed_peers[i]); - - vty_out(vty, "\n"); - } - - /* BGP deterministic-med. */ - if (!!bgp_flag_check(bgp, BGP_FLAG_DETERMINISTIC_MED) - != DFLT_BGP_DETERMINISTIC_MED) - vty_out(vty, " %sbgp deterministic-med\n", - bgp_flag_check(bgp, BGP_FLAG_DETERMINISTIC_MED) - ? "" - : "no "); - - /* BGP update-delay. */ - bgp_config_write_update_delay(vty, bgp); - - if (bgp->v_maxmed_onstartup - != BGP_MAXMED_ONSTARTUP_UNCONFIGURED) { - vty_out(vty, " bgp max-med on-startup %u", - bgp->v_maxmed_onstartup); - if (bgp->maxmed_onstartup_value - != BGP_MAXMED_VALUE_DEFAULT) - vty_out(vty, " %u", - bgp->maxmed_onstartup_value); - vty_out(vty, "\n"); - } - if (bgp->v_maxmed_admin != BGP_MAXMED_ADMIN_UNCONFIGURED) { - vty_out(vty, " bgp max-med administrative"); - if (bgp->maxmed_admin_value != BGP_MAXMED_VALUE_DEFAULT) - vty_out(vty, " %u", bgp->maxmed_admin_value); - vty_out(vty, "\n"); - } - - /* write quanta */ - bgp_config_write_wpkt_quanta(vty, bgp); - /* read quanta */ - bgp_config_write_rpkt_quanta(vty, bgp); - - /* coalesce time */ - bgp_config_write_coalesce_time(vty, bgp); - - /* BGP graceful-restart. */ - if (bgp->stalepath_time != BGP_DEFAULT_STALEPATH_TIME) - vty_out(vty, - " bgp graceful-restart stalepath-time %u\n", - bgp->stalepath_time); - if (bgp->restart_time != BGP_DEFAULT_RESTART_TIME) - vty_out(vty, " bgp graceful-restart restart-time %u\n", - bgp->restart_time); - if (bgp_flag_check(bgp, BGP_FLAG_GRACEFUL_RESTART)) - vty_out(vty, " bgp graceful-restart\n"); - - /* BGP graceful-shutdown */ - if (bgp_flag_check(bgp, BGP_FLAG_GRACEFUL_SHUTDOWN)) - vty_out(vty, " bgp graceful-shutdown\n"); - - /* BGP graceful-restart Preserve State F bit. */ - if (bgp_flag_check(bgp, BGP_FLAG_GR_PRESERVE_FWD)) - vty_out(vty, - " bgp graceful-restart preserve-fw-state\n"); - - /* BGP bestpath method. */ - if (bgp_flag_check(bgp, BGP_FLAG_ASPATH_IGNORE)) - vty_out(vty, " bgp bestpath as-path ignore\n"); - if (bgp_flag_check(bgp, BGP_FLAG_ASPATH_CONFED)) - vty_out(vty, " bgp bestpath as-path confed\n"); - - if (bgp_flag_check(bgp, BGP_FLAG_ASPATH_MULTIPATH_RELAX)) { - if (bgp_flag_check(bgp, - BGP_FLAG_MULTIPATH_RELAX_AS_SET)) { - vty_out(vty, - " bgp bestpath as-path multipath-relax as-set\n"); - } else { - vty_out(vty, - " bgp bestpath as-path multipath-relax\n"); - } - } - - if (bgp_flag_check(bgp, BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY)) { - vty_out(vty, - " bgp route-reflector allow-outbound-policy\n"); - } - if (bgp_flag_check(bgp, BGP_FLAG_COMPARE_ROUTER_ID)) - vty_out(vty, " bgp bestpath compare-routerid\n"); - if (bgp_flag_check(bgp, BGP_FLAG_MED_CONFED) - || bgp_flag_check(bgp, BGP_FLAG_MED_MISSING_AS_WORST)) { - vty_out(vty, " bgp bestpath med"); - if (bgp_flag_check(bgp, BGP_FLAG_MED_CONFED)) - vty_out(vty, " confed"); - if (bgp_flag_check(bgp, BGP_FLAG_MED_MISSING_AS_WORST)) - vty_out(vty, " missing-as-worst"); - vty_out(vty, "\n"); - } - - /* BGP network import check. */ - if (!!bgp_flag_check(bgp, BGP_FLAG_IMPORT_CHECK) - != DFLT_BGP_IMPORT_CHECK) - vty_out(vty, " %sbgp network import-check\n", - bgp_flag_check(bgp, BGP_FLAG_IMPORT_CHECK) - ? "" - : "no "); - - /* BGP flag dampening. */ - if (CHECK_FLAG(bgp->af_flags[AFI_IP][SAFI_UNICAST], - BGP_CONFIG_DAMPENING)) - bgp_config_write_damp(vty); - - /* BGP timers configuration. */ - if (bgp->default_keepalive != BGP_DEFAULT_KEEPALIVE - && bgp->default_holdtime != BGP_DEFAULT_HOLDTIME) - vty_out(vty, " timers bgp %u %u\n", - bgp->default_keepalive, bgp->default_holdtime); - - /* peer-group */ - for (ALL_LIST_ELEMENTS(bgp->group, node, nnode, group)) { - bgp_config_write_peer_global(vty, bgp, group->conf); - } - - /* Normal neighbor configuration. */ - for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { - if (CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE)) - bgp_config_write_peer_global(vty, bgp, peer); - } - - /* listen range and limit for dynamic BGP neighbors */ - bgp_config_write_listen(vty, bgp); - - /* - * BGP default autoshutdown neighbors - * - * This must be placed after any peer and peer-group - * configuration, to avoid setting all peers to shutdown after - * a daemon restart, which is undesired behavior. (see #2286) - */ - if (bgp->autoshutdown) - vty_out(vty, " bgp default shutdown\n"); - - /* No auto-summary */ - if (bgp_option_check(BGP_OPT_CONFIG_CISCO)) - vty_out(vty, " no auto-summary\n"); - - /* IPv4 unicast configuration. */ - bgp_config_write_family(vty, bgp, AFI_IP, SAFI_UNICAST); - - /* IPv4 multicast configuration. */ - bgp_config_write_family(vty, bgp, AFI_IP, SAFI_MULTICAST); - - /* IPv4 labeled-unicast configuration. */ - bgp_config_write_family(vty, bgp, AFI_IP, SAFI_LABELED_UNICAST); - - /* IPv4 VPN configuration. */ - bgp_config_write_family(vty, bgp, AFI_IP, SAFI_MPLS_VPN); - - /* ENCAPv4 configuration. */ - bgp_config_write_family(vty, bgp, AFI_IP, SAFI_ENCAP); - - /* FLOWSPEC v4 configuration. */ - bgp_config_write_family(vty, bgp, AFI_IP, SAFI_FLOWSPEC); - - /* IPv6 unicast configuration. */ - bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_UNICAST); - - /* IPv6 multicast configuration. */ - bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_MULTICAST); - - /* IPv6 labeled-unicast configuration. */ - bgp_config_write_family(vty, bgp, AFI_IP6, - SAFI_LABELED_UNICAST); - - /* IPv6 VPN configuration. */ - bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_MPLS_VPN); - - /* ENCAPv6 configuration. */ - bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_ENCAP); - - /* FLOWSPEC v6 configuration. */ - bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_FLOWSPEC); - - /* EVPN configuration. */ - bgp_config_write_family(vty, bgp, AFI_L2VPN, SAFI_EVPN); - -#if ENABLE_BGP_VNC - bgp_rfapi_cfg_write(vty, bgp); -#endif - - vty_out(vty, "!\n"); - } - return 0; -} - -void bgp_master_init(struct thread_master *master) +void bgp_master_init(struct thread_master *master, const int buffer_size) { qobj_init(); @@ -7898,7 +7033,10 @@ void bgp_master_init(struct thread_master *master) bm->start_time = bgp_clock(); bm->t_rmap_update = NULL; bm->rmap_update_timer = RMAP_DEFAULT_UPDATE_TIMER; + bm->v_update_delay = BGP_UPDATE_DELAY_DEF; + bm->v_establish_wait = BGP_UPDATE_DELAY_DEF; bm->terminating = false; + bm->socket_buffer = buffer_size; bgp_process_queue_init(); @@ -7910,12 +7048,10 @@ void bgp_master_init(struct thread_master *master) bf_init(bm->rd_idspace, UINT16_MAX); bf_assign_zero_index(bm->rd_idspace); - /* Enable multiple instances by default. */ - bgp_option_set(BGP_OPT_MULTIPLE_INSTANCE); - /* mpls label dynamic allocation pool */ bgp_lp_init(bm->master, &bm->labelpool); + bgp_evpn_mh_init(); QOBJ_REG(bm, bgp_master); } @@ -7959,8 +7095,31 @@ static void bgp_viewvrf_autocomplete(vector comps, struct cmd_token *token) } } +static void bgp_instasn_autocomplete(vector comps, struct cmd_token *token) +{ + struct listnode *next, *next2; + struct bgp *bgp, *bgp2; + char buf[11]; + + for (ALL_LIST_ELEMENTS_RO(bm->bgp, next, bgp)) { + /* deduplicate */ + for (ALL_LIST_ELEMENTS_RO(bm->bgp, next2, bgp2)) { + if (bgp2->as == bgp->as) + break; + if (bgp2 == bgp) + break; + } + if (bgp2 != bgp) + continue; + + snprintf(buf, sizeof(buf), "%u", bgp->as); + vector_set(comps, XSTRDUP(MTYPE_COMPLETION, buf)); + } +} + static const struct cmd_variable_handler bgp_viewvrf_var_handlers[] = { {.tokenname = "VIEWVRFNAME", .completions = bgp_viewvrf_autocomplete}, + {.varname = "instasn", .completions = bgp_instasn_autocomplete}, {.completions = NULL}, }; @@ -7972,8 +7131,6 @@ static void bgp_pthreads_init(void) assert(!bgp_pth_io); assert(!bgp_pth_ka); - frr_pthread_init(); - struct frr_pthread_attr io = { .start = frr_pthread_attr_default.start, .stop = frr_pthread_attr_default.stop, @@ -7999,7 +7156,6 @@ void bgp_pthreads_run(void) void bgp_pthreads_finish(void) { frr_pthread_stop_all(); - frr_pthread_finish(); } void bgp_init(unsigned short instance) @@ -8014,7 +7170,7 @@ void bgp_init(unsigned short instance) /* Init zebra. */ bgp_zebra_init(bm->master, instance); -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC vnc_zebra_init(bm->master); #endif @@ -8029,7 +7185,7 @@ void bgp_init(unsigned short instance) bgp_route_map_init(); bgp_scan_vty_init(); bgp_mplsvpn_init(); -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC rfapi_init(); #endif bgp_ethernetvpn_init(); @@ -8096,3 +7252,89 @@ void bgp_terminate(void) bgp_mac_finish(); } + +struct peer *peer_lookup_in_view(struct vty *vty, struct bgp *bgp, + const char *ip_str, bool use_json) +{ + int ret; + struct peer *peer; + union sockunion su; + + /* Get peer sockunion. */ + ret = str2sockunion(ip_str, &su); + if (ret < 0) { + peer = peer_lookup_by_conf_if(bgp, ip_str); + if (!peer) { + peer = peer_lookup_by_hostname(bgp, ip_str); + + if (!peer) { + if (use_json) { + json_object *json_no = NULL; + json_no = json_object_new_object(); + json_object_string_add( + json_no, + "malformedAddressOrName", + ip_str); + vty_out(vty, "%s\n", + json_object_to_json_string_ext( + json_no, + JSON_C_TO_STRING_PRETTY)); + json_object_free(json_no); + } else + vty_out(vty, + "%% Malformed address or name: %s\n", + ip_str); + return NULL; + } + } + return peer; + } + + /* Peer structure lookup. */ + peer = peer_lookup(bgp, &su); + if (!peer) { + if (use_json) { + json_object *json_no = NULL; + json_no = json_object_new_object(); + json_object_string_add(json_no, "warning", + "No such neighbor in this view/vrf"); + vty_out(vty, "%s\n", + json_object_to_json_string_ext( + json_no, JSON_C_TO_STRING_PRETTY)); + json_object_free(json_no); + } else + vty_out(vty, "No such neighbor in this view/vrf\n"); + return NULL; + } + + return peer; +} + +void bgp_gr_apply_running_config(void) +{ + struct peer *peer = NULL; + struct bgp *bgp = NULL; + struct listnode *node, *nnode; + bool gr_router_detected = false; + + if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) + zlog_debug("[BGP_GR] %s called !", __func__); + + for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) { + for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { + bgp_peer_gr_flags_update(peer); + if (CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART)) + gr_router_detected = true; + } + + if (gr_router_detected + && bgp->present_zebra_gr_state == ZEBRA_GR_DISABLE) { + bgp_zebra_send_capabilities(bgp, true); + } else if (!gr_router_detected + && bgp->present_zebra_gr_state == ZEBRA_GR_ENABLE) { + bgp_zebra_send_capabilities(bgp, false); + } + + gr_router_detected = false; + } +} diff --git a/bgpd/bgpd.conf.sample b/bgpd/bgpd.conf.sample index cb12a92522..1fb4f1600b 100644 --- a/bgpd/bgpd.conf.sample +++ b/bgpd/bgpd.conf.sample @@ -8,7 +8,6 @@ hostname bgpd password zebra !enable password please-set-at-here ! -!bgp multiple-instance ! router bgp 7675 ! bgp router-id 10.0.0.1 diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index b0f6567534..19aa1ce5fb 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -24,10 +24,12 @@ #include "qobj.h" #include +#include "hook.h" #include "frr_pthread.h" #include "lib/json.h" #include "vrf.h" #include "vty.h" +#include "iana_afi.h" /* For union sockunion. */ #include "queue.h" @@ -40,6 +42,7 @@ #include "vxlan.h" #include "bgp_labelpool.h" #include "bgp_addpath_types.h" +#include "bgp_nexthop.h" #define BGP_MAX_HOSTNAME 64 /* Linux max, is larger than most other sys */ #define BGP_PEER_MAX_HASH_SIZE 16384 @@ -63,6 +66,12 @@ enum { AS_UNSPECIFIED = 0, AS_EXTERNAL, }; +/* Zebra Gracaful Restart states */ +enum zebra_gr_mode { + ZEBRA_GR_DISABLE = 0, + ZEBRA_GR_ENABLE +}; + /* Typedef BGP specific types. */ typedef uint32_t as_t; typedef uint16_t as16_t; /* we may still encounter 16 Bit asnums */ @@ -134,10 +143,8 @@ struct bgp_master { /* Various BGP global configuration. */ uint8_t options; #define BGP_OPT_NO_FIB (1 << 0) -#define BGP_OPT_MULTIPLE_INSTANCE (1 << 1) -#define BGP_OPT_CONFIG_CISCO (1 << 2) -#define BGP_OPT_NO_LISTEN (1 << 3) -#define BGP_OPT_NO_ZEBRA (1 << 4) +#define BGP_OPT_NO_LISTEN (1 << 1) +#define BGP_OPT_NO_ZEBRA (1 << 2) uint64_t updgrp_idspace; uint64_t subgrp_idspace; @@ -156,6 +163,16 @@ struct bgp_master { /* BGP-EVPN VRF ID. Defaults to default VRF (if any) */ struct bgp* bgp_evpn; + /* How big should we set the socket buffer size */ + uint32_t socket_buffer; + + /* EVPN multihoming */ + struct bgp_evpn_mh_info *mh_info; + + /* global update-delay timer values */ + uint16_t v_update_delay; + uint16_t v_establish_wait; + bool terminating; /* global flag that sigint terminate seen */ QOBJ_FIELDS }; @@ -229,6 +246,66 @@ enum bgp_instance_type { BGP_INSTANCE_TYPE_VIEW }; +#define BGP_SEND_EOR(bgp, afi, safi) \ + (!CHECK_FLAG(bgp->flags, BGP_FLAG_GR_DISABLE_EOR) \ + && ((bgp->gr_info[afi][safi].t_select_deferral == NULL) \ + || (bgp->gr_info[afi][safi].eor_required \ + == bgp->gr_info[afi][safi].eor_received))) + +/* BGP GR Global ds */ + +#define BGP_GLOBAL_GR_MODE 4 +#define BGP_GLOBAL_GR_EVENT_CMD 4 + +/* Graceful restart selection deferral timer info */ +struct graceful_restart_info { + /* Count of EOR message expected */ + uint32_t eor_required; + /* Count of EOR received */ + uint32_t eor_received; + /* Deferral Timer */ + struct thread *t_select_deferral; + /* Route list */ + struct list *route_list; + /* Best route select */ + struct thread *t_route_select; + /* AFI, SAFI enabled */ + bool af_enabled[AFI_MAX][SAFI_MAX]; + /* Route update completed */ + bool route_sync[AFI_MAX][SAFI_MAX]; +}; + +enum global_mode { + GLOBAL_HELPER = 0, /* This is the default mode */ + GLOBAL_GR, + GLOBAL_DISABLE, + GLOBAL_INVALID +}; + +enum global_gr_command { + GLOBAL_GR_CMD = 0, + NO_GLOBAL_GR_CMD, + GLOBAL_DISABLE_CMD, + NO_GLOBAL_DISABLE_CMD +}; + +#define BGP_GR_SUCCESS 0 +#define BGP_GR_FAILURE 1 + +/* Handling of BGP link bandwidth (LB) on receiver - whether and how to + * do weighted ECMP. Note: This applies after multipath computation. + */ +enum bgp_link_bw_handling { + /* Do ECMP if some paths don't have LB - default */ + BGP_LINK_BW_ECMP, + /* Completely ignore LB, just do regular ECMP */ + BGP_LINK_BW_IGNORE_BW, + /* Skip paths without LB, do wECMP on others */ + BGP_LINK_BW_SKIP_MISSING, + /* Do wECMP with default weight for paths not having LB */ + BGP_LINK_BW_DEFWT_4_MISSING +}; + /* BGP instance structure. */ struct bgp { /* AS number of this BGP instance. */ @@ -340,6 +417,14 @@ struct bgp { #define BGP_UPDATE_DELAY_MIN 0 #define BGP_UPDATE_DELAY_MAX 3600 + /* Reference bandwidth for BGP link-bandwidth. Used when + * the LB value has to be computed based on some other + * factor (e.g., number of multipaths for the prefix) + * Value is in Mbps + */ + uint32_t lb_ref_bw; +#define BGP_LINK_BW_REF_BW 1 + /* BGP flags. */ uint32_t flags; #define BGP_FLAG_ALWAYS_COMPARE_MED (1 << 0) @@ -348,13 +433,15 @@ struct bgp { #define BGP_FLAG_MED_CONFED (1 << 3) #define BGP_FLAG_NO_DEFAULT_IPV4 (1 << 4) #define BGP_FLAG_NO_CLIENT_TO_CLIENT (1 << 5) -#define BGP_FLAG_ENFORCE_FIRST_AS (1 << 6) #define BGP_FLAG_COMPARE_ROUTER_ID (1 << 7) #define BGP_FLAG_ASPATH_IGNORE (1 << 8) #define BGP_FLAG_IMPORT_CHECK (1 << 9) #define BGP_FLAG_NO_FAST_EXT_FAILOVER (1 << 10) #define BGP_FLAG_LOG_NEIGHBOR_CHANGES (1 << 11) + +/* This flag is set when we have full BGP Graceful-Restart mode enable */ #define BGP_FLAG_GRACEFUL_RESTART (1 << 12) + #define BGP_FLAG_ASPATH_CONFED (1 << 13) #define BGP_FLAG_ASPATH_MULTIPATH_RELAX (1 << 14) #define BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY (1 << 15) @@ -364,6 +451,23 @@ struct bgp { #define BGP_FLAG_SHOW_HOSTNAME (1 << 19) #define BGP_FLAG_GR_PRESERVE_FWD (1 << 20) #define BGP_FLAG_GRACEFUL_SHUTDOWN (1 << 21) +#define BGP_FLAG_DELETE_IN_PROGRESS (1 << 22) +#define BGP_FLAG_SELECT_DEFER_DISABLE (1 << 23) +#define BGP_FLAG_GR_DISABLE_EOR (1 << 24) +#define BGP_FLAG_EBGP_REQUIRES_POLICY (1 << 25) +#define BGP_FLAG_SHOW_NEXTHOP_HOSTNAME (1 << 26) + +/* This flag is set if the instance is in administrative shutdown */ +#define BGP_FLAG_SHUTDOWN (1 << 27) + + enum global_mode GLOBAL_GR_FSM[BGP_GLOBAL_GR_MODE] + [BGP_GLOBAL_GR_EVENT_CMD]; + enum global_mode global_gr_present_state; + + /* This variable stores the current Graceful Restart state of Zebra + * - ZEBRA_GR_ENABLE / ZEBRA_GR_DISABLE + */ + enum zebra_gr_mode present_zebra_gr_state; /* BGP Per AF flags */ uint16_t af_flags[AFI_MAX][SAFI_MAX]; @@ -383,11 +487,11 @@ struct bgp { /* BGP per AF peer count */ uint32_t af_peer_count[AFI_MAX][SAFI_MAX]; - /* Route table for next-hop lookup cache. */ - struct bgp_table *nexthop_cache_table[AFI_MAX]; + /* Tree for next-hop lookup cache. */ + struct bgp_nexthop_cache_head nexthop_cache_table[AFI_MAX]; - /* Route table for import-check */ - struct bgp_table *import_check_table[AFI_MAX]; + /* Tree for import-check */ + struct bgp_nexthop_cache_head import_check_table[AFI_MAX]; struct bgp_table *connected_table[AFI_MAX]; @@ -454,11 +558,17 @@ struct bgp { /* BGP default timer. */ uint32_t default_holdtime; uint32_t default_keepalive; + uint32_t default_connect_retry; /* BGP graceful restart */ uint32_t restart_time; uint32_t stalepath_time; + uint32_t select_defer_time; + struct graceful_restart_info gr_info[AFI_MAX][SAFI_MAX]; + uint32_t rib_stale_time; +#define BGP_ROUTE_SELECT_DELAY 1 +#define BGP_MAX_BEST_ROUTE_SELECT 10000 /* Maximum-paths configuration */ struct bgp_maxpaths_cfg { uint16_t maxpaths_ebgp; @@ -480,7 +590,7 @@ struct bgp { struct bgp_addpath_bgp_data tx_addpath; -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC struct rfapi_cfg *rfapi_cfg; struct rfapi *rfapi; #endif @@ -496,10 +606,10 @@ struct bgp { /* EVPN enable - advertise local VNIs and their MACs etc. */ int advertise_all_vni; - /* RFC 8212 - prevent route leaks. */ - int ebgp_requires_policy; -#define DEFAULT_EBGP_POLICY_DISABLED 0 -#define DEFAULT_EBGP_POLICY_ENABLED 1 + /* draft-ietf-idr-deprecate-as-set-confed-set + * Reject aspaths with AS_SET and/or AS_CONFED_SET. + */ + bool reject_as_sets; struct bgp_evpn_info *evpn_info; @@ -537,7 +647,6 @@ struct bgp { #define BGP_VRF_RD_CFGD (1 << 3) #define BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY (1 << 4) - /* unique ID for auto derivation of RD for this vrf */ uint16_t vrf_rd_id; @@ -563,16 +672,28 @@ struct bgp { struct bgp_pbr_config *bgp_pbr_cfg; - /* local esi hash table */ - struct hash *esihash; - /* Count of peers in established state */ uint32_t established_peers; + /* Weighted ECMP related config. */ + enum bgp_link_bw_handling lb_handling; + QOBJ_FIELDS }; DECLARE_QOBJ_TYPE(bgp) +DECLARE_HOOK(bgp_inst_delete, (struct bgp *bgp), (bgp)) +DECLARE_HOOK(bgp_inst_config_write, + (struct bgp *bgp, struct vty *vty), + (bgp, vty)) + +/* Thread callback information */ +struct afi_safi_info { + afi_t afi; + safi_t safi; + struct bgp *bgp; +}; + #define BGP_ROUTE_ADV_HOLD(bgp) (bgp->main_peers_update_hold) #define IS_BGP_INST_KNOWN_TO_ZEBRA(bgp) \ @@ -580,6 +701,9 @@ DECLARE_QOBJ_TYPE(bgp) || (bgp->inst_type == BGP_INSTANCE_TYPE_VRF \ && bgp->vrf_id != VRF_UNKNOWN)) +#define BGP_SELECT_DEFER_DISABLE(bgp) \ + (CHECK_FLAG(bgp->flags, BGP_FLAG_SELECT_DEFER_DISABLE)) + /* BGP peer-group support. */ struct peer_group { /* Name of the peer-group. */ @@ -627,6 +751,10 @@ struct bgp_nexthop { #define RMAP_OUT 1 #define RMAP_MAX 2 +#define BGP_DEFAULT_TTL 1 +#define BGP_GTSM_HOPS_DISABLED 0 +#define BGP_GTSM_HOPS_CONNECTED 1 + #include "filter.h" /* BGP filter structure. */ @@ -708,6 +836,107 @@ struct peer_af { safi_t safi; int afid; }; +/* BGP GR per peer ds */ + +#define BGP_PEER_GR_MODE 5 +#define BGP_PEER_GR_EVENT_CMD 6 + +enum peer_mode { + PEER_HELPER = 0, + PEER_GR, + PEER_DISABLE, + PEER_INVALID, + PEER_GLOBAL_INHERIT /* This is the default mode */ + +}; + +enum peer_gr_command { + PEER_GR_CMD = 0, + NO_PEER_GR_CMD, + PEER_DISABLE_CMD, + NO_PEER_DISABLE_CMD, + PEER_HELPER_CMD, + NO_PEER_HELPER_CMD +}; + +typedef unsigned int (*bgp_peer_gr_action_ptr)(struct peer *, int, int); + +struct bgp_peer_gr { + enum peer_mode next_state; + bgp_peer_gr_action_ptr action_fun; +}; + +/* + * BGP FSM event codes, per RFC 4271 ss. 8.1 + */ +enum bgp_fsm_rfc_codes { + BGP_FSM_ManualStart = 1, + BGP_FSM_ManualStop = 2, + BGP_FSM_AutomaticStart = 3, + BGP_FSM_ManualStart_with_PassiveTcpEstablishment = 4, + BGP_FSM_AutomaticStart_with_PassiveTcpEstablishment = 5, + BGP_FSM_AutomaticStart_with_DampPeerOscillations = 6, + BGP_FSM_AutomaticStart_with_DampPeerOscillations_and_PassiveTcpEstablishment = + 7, + BGP_FSM_AutomaticStop = 8, + BGP_FSM_ConnectRetryTimer_Expires = 9, + BGP_FSM_HoldTimer_Expires = 10, + BGP_FSM_KeepaliveTimer_Expires = 11, + BGP_FSM_DelayOpenTimer_Expires = 12, + BGP_FSM_IdleHoldTimer_Expires = 13, + BGP_FSM_TcpConnection_Valid = 14, + BGP_FSM_Tcp_CR_Invalid = 15, + BGP_FSM_Tcp_CR_Acked = 16, + BGP_FSM_TcpConnectionConfirmed = 17, + BGP_FSM_TcpConnectionFails = 18, + BGP_FSM_BGPOpen = 19, + BGP_FSM_BGPOpen_with_DelayOpenTimer_running = 20, + BGP_FSM_BGPHeaderErr = 21, + BGP_FSM_BGPOpenMsgErr = 22, + BGP_FSM_OpenCollisionDump = 23, + BGP_FSM_NotifMsgVerErr = 24, + BGP_FSM_NotifMsg = 25, + BGP_FSM_KeepAliveMsg = 26, + BGP_FSM_UpdateMsg = 27, + BGP_FSM_UpdateMsgErr = 28 +}; + +/* + * BGP finite state machine events + * + * Note: these do not correspond to RFC-defined event codes. Those are + * defined elsewhere. + */ +enum bgp_fsm_events { + BGP_Start = 1, + BGP_Stop, + TCP_connection_open, + TCP_connection_closed, + TCP_connection_open_failed, + TCP_fatal_error, + ConnectRetry_timer_expired, + Hold_Timer_expired, + KeepAlive_timer_expired, + Receive_OPEN_message, + Receive_KEEPALIVE_message, + Receive_UPDATE_message, + Receive_NOTIFICATION_message, + Clearing_Completed, + BGP_EVENTS_MAX, +}; + +/* BGP finite state machine status. */ +enum bgp_fsm_status { + Idle = 1, + Connect, + Active, + OpenSent, + OpenConfirm, + Established, + Clearing, + Deleted, + BGP_STATUS_MAX, +}; /* BGP neighbor structure. */ struct peer { @@ -766,15 +995,15 @@ struct peer { struct peer *doppelganger; /* Status of the peer. */ - int status; - int ostatus; + enum bgp_fsm_status status; + enum bgp_fsm_status ostatus; /* FSM events, stored for debug purposes. * Note: uchar used for reduced memory usage. */ - unsigned char cur_event; - unsigned char last_event; - unsigned char last_major_event; + enum bgp_fsm_events cur_event; + enum bgp_fsm_events last_event; + enum bgp_fsm_events last_major_event; /* Peer index, used for dumping TABLE_DUMP_V2 format */ uint16_t table_dump_index; @@ -783,6 +1012,9 @@ struct peer { int fd; /* File descriptor */ int ttl; /* TTL of TCP connection to the peer. */ int rtt; /* Estimated round-trip-time from TCP_INFO */ + int rtt_expected; /* Expected round-trip-time for a peer */ + uint8_t rtt_keepalive_rcv; /* Received count for RTT shutdown */ + uint8_t rtt_keepalive_conf; /* Configured count for RTT shutdown */ int gtsm_hops; /* minimum hopcount to peer */ char *desc; /* Description of the peer. */ unsigned short port; /* Destination port for peer */ @@ -812,41 +1044,41 @@ struct peer { /* Capability flags (reset in bgp_stop) */ uint32_t cap; -#define PEER_CAP_REFRESH_ADV (1 << 0) /* refresh advertised */ -#define PEER_CAP_REFRESH_OLD_RCV (1 << 1) /* refresh old received */ -#define PEER_CAP_REFRESH_NEW_RCV (1 << 2) /* refresh rfc received */ -#define PEER_CAP_DYNAMIC_ADV (1 << 3) /* dynamic advertised */ -#define PEER_CAP_DYNAMIC_RCV (1 << 4) /* dynamic received */ -#define PEER_CAP_RESTART_ADV (1 << 5) /* restart advertised */ -#define PEER_CAP_RESTART_RCV (1 << 6) /* restart received */ -#define PEER_CAP_AS4_ADV (1 << 7) /* as4 advertised */ -#define PEER_CAP_AS4_RCV (1 << 8) /* as4 received */ -#define PEER_CAP_RESTART_BIT_ADV (1 << 9) /* sent restart state */ -#define PEER_CAP_RESTART_BIT_RCV (1 << 10) /* peer restart state */ -#define PEER_CAP_ADDPATH_ADV (1 << 11) /* addpath advertised */ -#define PEER_CAP_ADDPATH_RCV (1 << 12) /* addpath received */ -#define PEER_CAP_ENHE_ADV (1 << 13) /* Extended nexthop advertised */ -#define PEER_CAP_ENHE_RCV (1 << 14) /* Extended nexthop received */ -#define PEER_CAP_HOSTNAME_ADV (1 << 15) /* hostname advertised */ -#define PEER_CAP_HOSTNAME_RCV (1 << 16) /* hostname received */ +#define PEER_CAP_REFRESH_ADV (1U << 0) /* refresh advertised */ +#define PEER_CAP_REFRESH_OLD_RCV (1U << 1) /* refresh old received */ +#define PEER_CAP_REFRESH_NEW_RCV (1U << 2) /* refresh rfc received */ +#define PEER_CAP_DYNAMIC_ADV (1U << 3) /* dynamic advertised */ +#define PEER_CAP_DYNAMIC_RCV (1U << 4) /* dynamic received */ +#define PEER_CAP_RESTART_ADV (1U << 5) /* restart advertised */ +#define PEER_CAP_RESTART_RCV (1U << 6) /* restart received */ +#define PEER_CAP_AS4_ADV (1U << 7) /* as4 advertised */ +#define PEER_CAP_AS4_RCV (1U << 8) /* as4 received */ +#define PEER_CAP_RESTART_BIT_ADV (1U << 9) /* sent restart state */ +#define PEER_CAP_RESTART_BIT_RCV (1U << 10) /* peer restart state */ +#define PEER_CAP_ADDPATH_ADV (1U << 11) /* addpath advertised */ +#define PEER_CAP_ADDPATH_RCV (1U << 12) /* addpath received */ +#define PEER_CAP_ENHE_ADV (1U << 13) /* Extended nexthop advertised */ +#define PEER_CAP_ENHE_RCV (1U << 14) /* Extended nexthop received */ +#define PEER_CAP_HOSTNAME_ADV (1U << 15) /* hostname advertised */ +#define PEER_CAP_HOSTNAME_RCV (1U << 16) /* hostname received */ /* Capability flags (reset in bgp_stop) */ uint32_t af_cap[AFI_MAX][SAFI_MAX]; -#define PEER_CAP_ORF_PREFIX_SM_ADV (1 << 0) /* send-mode advertised */ -#define PEER_CAP_ORF_PREFIX_RM_ADV (1 << 1) /* receive-mode advertised */ -#define PEER_CAP_ORF_PREFIX_SM_RCV (1 << 2) /* send-mode received */ -#define PEER_CAP_ORF_PREFIX_RM_RCV (1 << 3) /* receive-mode received */ -#define PEER_CAP_ORF_PREFIX_SM_OLD_RCV (1 << 4) /* send-mode received */ -#define PEER_CAP_ORF_PREFIX_RM_OLD_RCV (1 << 5) /* receive-mode received */ -#define PEER_CAP_RESTART_AF_RCV (1 << 6) /* graceful restart afi/safi received */ -#define PEER_CAP_RESTART_AF_PRESERVE_RCV (1 << 7) /* graceful restart afi/safi F-bit received */ -#define PEER_CAP_ADDPATH_AF_TX_ADV (1 << 8) /* addpath tx advertised */ -#define PEER_CAP_ADDPATH_AF_TX_RCV (1 << 9) /* addpath tx received */ -#define PEER_CAP_ADDPATH_AF_RX_ADV (1 << 10) /* addpath rx advertised */ -#define PEER_CAP_ADDPATH_AF_RX_RCV (1 << 11) /* addpath rx received */ -#define PEER_CAP_ENHE_AF_ADV (1 << 12) /* Extended nexthopi afi/safi advertised */ -#define PEER_CAP_ENHE_AF_RCV (1 << 13) /* Extended nexthop afi/safi received */ -#define PEER_CAP_ENHE_AF_NEGO (1 << 14) /* Extended nexthop afi/safi negotiated */ +#define PEER_CAP_ORF_PREFIX_SM_ADV (1U << 0) /* send-mode advertised */ +#define PEER_CAP_ORF_PREFIX_RM_ADV (1U << 1) /* receive-mode advertised */ +#define PEER_CAP_ORF_PREFIX_SM_RCV (1U << 2) /* send-mode received */ +#define PEER_CAP_ORF_PREFIX_RM_RCV (1U << 3) /* receive-mode received */ +#define PEER_CAP_ORF_PREFIX_SM_OLD_RCV (1U << 4) /* send-mode received */ +#define PEER_CAP_ORF_PREFIX_RM_OLD_RCV (1U << 5) /* receive-mode received */ +#define PEER_CAP_RESTART_AF_RCV (1U << 6) /* graceful restart afi/safi received */ +#define PEER_CAP_RESTART_AF_PRESERVE_RCV (1U << 7) /* graceful restart afi/safi F-bit received */ +#define PEER_CAP_ADDPATH_AF_TX_ADV (1U << 8) /* addpath tx advertised */ +#define PEER_CAP_ADDPATH_AF_TX_RCV (1U << 9) /* addpath tx received */ +#define PEER_CAP_ADDPATH_AF_RX_ADV (1U << 10) /* addpath rx advertised */ +#define PEER_CAP_ADDPATH_AF_RX_RCV (1U << 11) /* addpath rx received */ +#define PEER_CAP_ENHE_AF_ADV (1U << 12) /* Extended nexthopi afi/safi advertised */ +#define PEER_CAP_ENHE_AF_RCV (1U << 13) /* Extended nexthop afi/safi received */ +#define PEER_CAP_ENHE_AF_NEGO (1U << 14) /* Extended nexthop afi/safi negotiated */ /* Global configuration flags. */ /* @@ -905,35 +1137,61 @@ struct peer { * flags_invert) must be respected. */ uint32_t flags; -#define PEER_FLAG_PASSIVE (1 << 0) /* passive mode */ -#define PEER_FLAG_SHUTDOWN (1 << 1) /* shutdown */ -#define PEER_FLAG_DONT_CAPABILITY (1 << 2) /* dont-capability */ -#define PEER_FLAG_OVERRIDE_CAPABILITY (1 << 3) /* override-capability */ -#define PEER_FLAG_STRICT_CAP_MATCH (1 << 4) /* strict-match */ -#define PEER_FLAG_DYNAMIC_CAPABILITY (1 << 5) /* dynamic capability */ -#define PEER_FLAG_DISABLE_CONNECTED_CHECK (1 << 6) /* disable-connected-check */ -#define PEER_FLAG_LOCAL_AS_NO_PREPEND (1 << 7) /* local-as no-prepend */ -#define PEER_FLAG_LOCAL_AS_REPLACE_AS (1 << 8) /* local-as no-prepend replace-as */ -#define PEER_FLAG_DELETE (1 << 9) /* mark the peer for deleting */ -#define PEER_FLAG_CONFIG_NODE (1 << 10) /* the node to update configs on */ -#define PEER_FLAG_LONESOUL (1 << 11) -#define PEER_FLAG_DYNAMIC_NEIGHBOR (1 << 12) /* dynamic neighbor */ -#define PEER_FLAG_CAPABILITY_ENHE (1 << 13) /* Extended next-hop (rfc 5549)*/ -#define PEER_FLAG_IFPEER_V6ONLY (1 << 14) /* if-based peer is v6 only */ -#define PEER_FLAG_IS_RFAPI_HD (1 << 15) /* attached to rfapi HD */ -#define PEER_FLAG_ENFORCE_FIRST_AS (1 << 16) /* enforce-first-as */ -#define PEER_FLAG_ROUTEADV (1 << 17) /* route advertise */ -#define PEER_FLAG_TIMER (1 << 18) /* keepalive & holdtime */ -#define PEER_FLAG_TIMER_CONNECT (1 << 19) /* connect timer */ -#define PEER_FLAG_PASSWORD (1 << 20) /* password */ -#define PEER_FLAG_LOCAL_AS (1 << 21) /* local-as */ -#define PEER_FLAG_UPDATE_SOURCE (1 << 22) /* update-source */ +#define PEER_FLAG_PASSIVE (1U << 0) /* passive mode */ +#define PEER_FLAG_SHUTDOWN (1U << 1) /* shutdown */ +#define PEER_FLAG_DONT_CAPABILITY (1U << 2) /* dont-capability */ +#define PEER_FLAG_OVERRIDE_CAPABILITY (1U << 3) /* override-capability */ +#define PEER_FLAG_STRICT_CAP_MATCH (1U << 4) /* strict-match */ +#define PEER_FLAG_DYNAMIC_CAPABILITY (1U << 5) /* dynamic capability */ +#define PEER_FLAG_DISABLE_CONNECTED_CHECK (1U << 6) /* disable-connected-check */ +#define PEER_FLAG_LOCAL_AS_NO_PREPEND (1U << 7) /* local-as no-prepend */ +#define PEER_FLAG_LOCAL_AS_REPLACE_AS (1U << 8) /* local-as no-prepend replace-as */ +#define PEER_FLAG_DELETE (1U << 9) /* mark the peer for deleting */ +#define PEER_FLAG_CONFIG_NODE (1U << 10) /* the node to update configs on */ +#define PEER_FLAG_LONESOUL (1U << 11) +#define PEER_FLAG_DYNAMIC_NEIGHBOR (1U << 12) /* dynamic neighbor */ +#define PEER_FLAG_CAPABILITY_ENHE (1U << 13) /* Extended next-hop (rfc 5549)*/ +#define PEER_FLAG_IFPEER_V6ONLY (1U << 14) /* if-based peer is v6 only */ +#define PEER_FLAG_IS_RFAPI_HD (1U << 15) /* attached to rfapi HD */ +#define PEER_FLAG_ENFORCE_FIRST_AS (1U << 16) /* enforce-first-as */ +#define PEER_FLAG_ROUTEADV (1U << 17) /* route advertise */ +#define PEER_FLAG_TIMER (1U << 18) /* keepalive & holdtime */ +#define PEER_FLAG_TIMER_CONNECT (1U << 19) /* connect timer */ +#define PEER_FLAG_PASSWORD (1U << 20) /* password */ +#define PEER_FLAG_LOCAL_AS (1U << 21) /* local-as */ +#define PEER_FLAG_UPDATE_SOURCE (1U << 22) /* update-source */ + + /* BGP-GR Peer related flags */ +#define PEER_FLAG_GRACEFUL_RESTART_HELPER (1U << 23) /* Helper */ +#define PEER_FLAG_GRACEFUL_RESTART (1U << 24) /* Graceful Restart */ +#define PEER_FLAG_GRACEFUL_RESTART_GLOBAL_INHERIT (1U << 25) /* Global-Inherit */ +#define PEER_FLAG_RTT_SHUTDOWN (1U << 26) /* shutdown rtt */ + + /* + *GR-Disabled mode means unset PEER_FLAG_GRACEFUL_RESTART + *& PEER_FLAG_GRACEFUL_RESTART_HELPER + *and PEER_FLAG_GRACEFUL_RESTART_GLOBAL_INHERIT + */ + + struct bgp_peer_gr PEER_GR_FSM[BGP_PEER_GR_MODE][BGP_PEER_GR_EVENT_CMD]; + enum peer_mode peer_gr_present_state; + /* Non stop forwarding afi-safi count for BGP gr feature*/ + uint8_t nsf_af_count; + + uint8_t peer_gr_new_status_flag; +#define PEER_GRACEFUL_RESTART_NEW_STATE_HELPER (1U << 0) +#define PEER_GRACEFUL_RESTART_NEW_STATE_RESTART (1U << 1) +#define PEER_GRACEFUL_RESTART_NEW_STATE_INHERIT (1U << 2) /* outgoing message sent in CEASE_ADMIN_SHUTDOWN notify */ char *tx_shutdown_message; /* NSF mode (graceful restart) */ uint8_t nsf[AFI_MAX][SAFI_MAX]; + /* EOR Send time */ + time_t eor_stime[AFI_MAX][SAFI_MAX]; + /* Last update packet sent time */ + time_t pkt_stime[AFI_MAX][SAFI_MAX]; /* Peer Per AF flags */ /* @@ -945,31 +1203,33 @@ struct peer { uint32_t af_flags_override[AFI_MAX][SAFI_MAX]; uint32_t af_flags_invert[AFI_MAX][SAFI_MAX]; uint32_t af_flags[AFI_MAX][SAFI_MAX]; -#define PEER_FLAG_SEND_COMMUNITY (1 << 0) /* send-community */ -#define PEER_FLAG_SEND_EXT_COMMUNITY (1 << 1) /* send-community ext. */ -#define PEER_FLAG_NEXTHOP_SELF (1 << 2) /* next-hop-self */ -#define PEER_FLAG_REFLECTOR_CLIENT (1 << 3) /* reflector-client */ -#define PEER_FLAG_RSERVER_CLIENT (1 << 4) /* route-server-client */ -#define PEER_FLAG_SOFT_RECONFIG (1 << 5) /* soft-reconfiguration */ -#define PEER_FLAG_AS_PATH_UNCHANGED (1 << 6) /* transparent-as */ -#define PEER_FLAG_NEXTHOP_UNCHANGED (1 << 7) /* transparent-next-hop */ -#define PEER_FLAG_MED_UNCHANGED (1 << 8) /* transparent-next-hop */ -#define PEER_FLAG_DEFAULT_ORIGINATE (1 << 9) /* default-originate */ -#define PEER_FLAG_REMOVE_PRIVATE_AS (1 << 10) /* remove-private-as */ -#define PEER_FLAG_ALLOWAS_IN (1 << 11) /* set allowas-in */ -#define PEER_FLAG_ORF_PREFIX_SM (1 << 12) /* orf capability send-mode */ -#define PEER_FLAG_ORF_PREFIX_RM (1 << 13) /* orf capability receive-mode */ -#define PEER_FLAG_MAX_PREFIX (1 << 14) /* maximum prefix */ -#define PEER_FLAG_MAX_PREFIX_WARNING (1 << 15) /* maximum prefix warning-only */ -#define PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED (1 << 16) /* leave link-local nexthop unchanged */ -#define PEER_FLAG_FORCE_NEXTHOP_SELF (1 << 17) /* next-hop-self force */ -#define PEER_FLAG_REMOVE_PRIVATE_AS_ALL (1 << 18) /* remove-private-as all */ -#define PEER_FLAG_REMOVE_PRIVATE_AS_REPLACE (1 << 19) /* remove-private-as replace-as */ -#define PEER_FLAG_AS_OVERRIDE (1 << 20) /* as-override */ -#define PEER_FLAG_REMOVE_PRIVATE_AS_ALL_REPLACE (1 << 21) /* remove-private-as all replace-as */ -#define PEER_FLAG_WEIGHT (1 << 24) /* weight */ -#define PEER_FLAG_ALLOWAS_IN_ORIGIN (1 << 25) /* allowas-in origin */ -#define PEER_FLAG_SEND_LARGE_COMMUNITY (1 << 26) /* Send large Communities */ +#define PEER_FLAG_SEND_COMMUNITY (1U << 0) /* send-community */ +#define PEER_FLAG_SEND_EXT_COMMUNITY (1U << 1) /* send-community ext. */ +#define PEER_FLAG_NEXTHOP_SELF (1U << 2) /* next-hop-self */ +#define PEER_FLAG_REFLECTOR_CLIENT (1U << 3) /* reflector-client */ +#define PEER_FLAG_RSERVER_CLIENT (1U << 4) /* route-server-client */ +#define PEER_FLAG_SOFT_RECONFIG (1U << 5) /* soft-reconfiguration */ +#define PEER_FLAG_AS_PATH_UNCHANGED (1U << 6) /* transparent-as */ +#define PEER_FLAG_NEXTHOP_UNCHANGED (1U << 7) /* transparent-next-hop */ +#define PEER_FLAG_MED_UNCHANGED (1U << 8) /* transparent-next-hop */ +#define PEER_FLAG_DEFAULT_ORIGINATE (1U << 9) /* default-originate */ +#define PEER_FLAG_REMOVE_PRIVATE_AS (1U << 10) /* remove-private-as */ +#define PEER_FLAG_ALLOWAS_IN (1U << 11) /* set allowas-in */ +#define PEER_FLAG_ORF_PREFIX_SM (1U << 12) /* orf capability send-mode */ +#define PEER_FLAG_ORF_PREFIX_RM (1U << 13) /* orf capability receive-mode */ +#define PEER_FLAG_MAX_PREFIX (1U << 14) /* maximum prefix */ +#define PEER_FLAG_MAX_PREFIX_WARNING (1U << 15) /* maximum prefix warning-only */ +#define PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED (1U << 16) /* leave link-local nexthop unchanged */ +#define PEER_FLAG_FORCE_NEXTHOP_SELF (1U << 17) /* next-hop-self force */ +#define PEER_FLAG_REMOVE_PRIVATE_AS_ALL (1U << 18) /* remove-private-as all */ +#define PEER_FLAG_REMOVE_PRIVATE_AS_REPLACE (1U << 19) /* remove-private-as replace-as */ +#define PEER_FLAG_AS_OVERRIDE (1U << 20) /* as-override */ +#define PEER_FLAG_REMOVE_PRIVATE_AS_ALL_REPLACE (1U << 21) /* remove-private-as all replace-as */ +#define PEER_FLAG_WEIGHT (1U << 24) /* weight */ +#define PEER_FLAG_ALLOWAS_IN_ORIGIN (1U << 25) /* allowas-in origin */ +#define PEER_FLAG_SEND_LARGE_COMMUNITY (1U << 26) /* Send large Communities */ +#define PEER_FLAG_MAX_PREFIX_OUT (1U << 27) /* outgoing maximum prefix */ +#define PEER_FLAG_MAX_PREFIX_FORCE (1U << 28) /* maximum-prefix force */ enum bgp_addpath_strat addpath_type[AFI_MAX][SAFI_MAX]; @@ -984,22 +1244,22 @@ struct peer { /* Peer status flags. */ uint16_t sflags; -#define PEER_STATUS_ACCEPT_PEER (1 << 0) /* accept peer */ -#define PEER_STATUS_PREFIX_OVERFLOW (1 << 1) /* prefix-overflow */ -#define PEER_STATUS_CAPABILITY_OPEN (1 << 2) /* capability open send */ -#define PEER_STATUS_HAVE_ACCEPT (1 << 3) /* accept peer's parent */ -#define PEER_STATUS_GROUP (1 << 4) /* peer-group conf */ -#define PEER_STATUS_NSF_MODE (1 << 5) /* NSF aware peer */ -#define PEER_STATUS_NSF_WAIT (1 << 6) /* wait comeback peer */ +#define PEER_STATUS_ACCEPT_PEER (1U << 0) /* accept peer */ +#define PEER_STATUS_PREFIX_OVERFLOW (1U << 1) /* prefix-overflow */ +#define PEER_STATUS_CAPABILITY_OPEN (1U << 2) /* capability open send */ +#define PEER_STATUS_HAVE_ACCEPT (1U << 3) /* accept peer's parent */ +#define PEER_STATUS_GROUP (1U << 4) /* peer-group conf */ +#define PEER_STATUS_NSF_MODE (1U << 5) /* NSF aware peer */ +#define PEER_STATUS_NSF_WAIT (1U << 6) /* wait comeback peer */ /* Peer status af flags (reset in bgp_stop) */ uint16_t af_sflags[AFI_MAX][SAFI_MAX]; -#define PEER_STATUS_ORF_PREFIX_SEND (1 << 0) /* prefix-list send peer */ -#define PEER_STATUS_ORF_WAIT_REFRESH (1 << 1) /* wait refresh received peer */ -#define PEER_STATUS_PREFIX_THRESHOLD (1 << 2) /* exceed prefix-threshold */ -#define PEER_STATUS_PREFIX_LIMIT (1 << 3) /* exceed prefix-limit */ -#define PEER_STATUS_EOR_SEND (1 << 4) /* end-of-rib send to peer */ -#define PEER_STATUS_EOR_RECEIVED (1 << 5) /* end-of-rib received from peer */ +#define PEER_STATUS_ORF_PREFIX_SEND (1U << 0) /* prefix-list send peer */ +#define PEER_STATUS_ORF_WAIT_REFRESH (1U << 1) /* wait refresh received peer */ +#define PEER_STATUS_PREFIX_THRESHOLD (1U << 2) /* exceed prefix-threshold */ +#define PEER_STATUS_PREFIX_LIMIT (1U << 3) /* exceed prefix-limit */ +#define PEER_STATUS_EOR_SEND (1U << 4) /* end-of-rib send to peer */ +#define PEER_STATUS_EOR_RECEIVED (1U << 5) /* end-of-rib received from peer */ /* Configured timer values. */ _Atomic uint32_t holdtime; @@ -1033,9 +1293,9 @@ struct peer { /* Thread flags. */ _Atomic uint32_t thread_flags; -#define PEER_THREAD_WRITES_ON (1 << 0) -#define PEER_THREAD_READS_ON (1 << 1) -#define PEER_THREAD_KEEPALIVES_ON (1 << 2) +#define PEER_THREAD_WRITES_ON (1U << 0) +#define PEER_THREAD_READS_ON (1U << 1) +#define PEER_THREAD_KEEPALIVES_ON (1U << 2) /* workqueues */ struct work_queue *clear_node_queue; @@ -1078,6 +1338,14 @@ struct peer { _Atomic uint32_t dynamic_cap_in; /* Dynamic Capability input count. */ _Atomic uint32_t dynamic_cap_out; /* Dynamic Capability output count. */ + uint32_t stat_pfx_filter; + uint32_t stat_pfx_aspath_loop; + uint32_t stat_pfx_originator_loop; + uint32_t stat_pfx_cluster_loop; + uint32_t stat_pfx_nh_invalid; + uint32_t stat_pfx_dup_withdraw; + uint32_t stat_upd_7606; /* RFC7606: treat-as-withdraw */ + /* BGP state count */ uint32_t established; /* Established */ uint32_t dropped; /* Dropped */ @@ -1094,9 +1362,6 @@ struct peer { /* timestamp when the last msg was written */ _Atomic time_t last_update; - /* Send prefix count. */ - unsigned long scount[AFI_MAX][SAFI_MAX]; - /* Notify data. */ struct bgp_notify notify; @@ -1120,14 +1385,12 @@ struct peer { * - This does *not* contain the filter values, rather it contains * whether the filter in filter (struct bgp_filter) is peer-specific. */ - uint8_t filter_override[AFI_MAX][SAFI_MAX][(FILTER_MAX > RMAP_MAX) - ? FILTER_MAX - : RMAP_MAX]; -#define PEER_FT_DISTRIBUTE_LIST (1 << 0) /* distribute-list */ -#define PEER_FT_FILTER_LIST (1 << 1) /* filter-list */ -#define PEER_FT_PREFIX_LIST (1 << 2) /* prefix-list */ -#define PEER_FT_ROUTE_MAP (1 << 3) /* route-map */ -#define PEER_FT_UNSUPPRESS_MAP (1 << 4) /* unsuppress-map */ + uint8_t filter_override[AFI_MAX][SAFI_MAX][FILTER_MAX]; +#define PEER_FT_DISTRIBUTE_LIST (1U << 0) /* distribute-list */ +#define PEER_FT_FILTER_LIST (1U << 1) /* filter-list */ +#define PEER_FT_PREFIX_LIST (1U << 2) /* prefix-list */ +#define PEER_FT_ROUTE_MAP (1U << 3) /* route-map */ +#define PEER_FT_UNSUPPRESS_MAP (1U << 4) /* unsuppress-map */ /* ORF Prefix-list */ struct prefix_list *orf_plist[AFI_MAX][SAFI_MAX]; @@ -1138,15 +1401,18 @@ struct peer { /* Track if we printed the attribute in debugs */ int rcvd_attr_printed; - /* Prefix count. */ - unsigned long pcount[AFI_MAX][SAFI_MAX]; + /* Accepted prefix count */ + uint32_t pcount[AFI_MAX][SAFI_MAX]; /* Max prefix count. */ - unsigned long pmax[AFI_MAX][SAFI_MAX]; + uint32_t pmax[AFI_MAX][SAFI_MAX]; uint8_t pmax_threshold[AFI_MAX][SAFI_MAX]; uint16_t pmax_restart[AFI_MAX][SAFI_MAX]; #define MAXIMUM_PREFIX_THRESHOLD_DEFAULT 75 + /* Send prefix count. */ + uint32_t pmax_out[AFI_MAX][SAFI_MAX]; + /* allowas-in. */ char allowas_in[AFI_MAX][SAFI_MAX]; @@ -1154,46 +1420,59 @@ struct peer { unsigned long weight[AFI_MAX][SAFI_MAX]; /* peer reset cause */ - char last_reset; -#define PEER_DOWN_RID_CHANGE 1 /* bgp router-id command */ -#define PEER_DOWN_REMOTE_AS_CHANGE 2 /* neighbor remote-as command */ -#define PEER_DOWN_LOCAL_AS_CHANGE 3 /* neighbor local-as command */ -#define PEER_DOWN_CLID_CHANGE 4 /* bgp cluster-id command */ -#define PEER_DOWN_CONFED_ID_CHANGE 5 /* bgp confederation identifier command */ -#define PEER_DOWN_CONFED_PEER_CHANGE 6 /* bgp confederation peer command */ -#define PEER_DOWN_RR_CLIENT_CHANGE 7 /* neighbor route-reflector-client command */ -#define PEER_DOWN_RS_CLIENT_CHANGE 8 /* neighbor route-server-client command */ -#define PEER_DOWN_UPDATE_SOURCE_CHANGE 9 /* neighbor update-source command */ -#define PEER_DOWN_AF_ACTIVATE 10 /* neighbor activate command */ -#define PEER_DOWN_USER_SHUTDOWN 11 /* neighbor shutdown command */ -#define PEER_DOWN_USER_RESET 12 /* clear ip bgp command */ -#define PEER_DOWN_NOTIFY_RECEIVED 13 /* notification received */ -#define PEER_DOWN_NOTIFY_SEND 14 /* notification send */ -#define PEER_DOWN_CLOSE_SESSION 15 /* tcp session close */ -#define PEER_DOWN_NEIGHBOR_DELETE 16 /* neghbor delete */ -#define PEER_DOWN_RMAP_BIND 17 /* neghbor peer-group command */ -#define PEER_DOWN_RMAP_UNBIND 18 /* no neighbor peer-group command */ -#define PEER_DOWN_CAPABILITY_CHANGE 19 /* neighbor capability command */ -#define PEER_DOWN_PASSIVE_CHANGE 20 /* neighbor passive command */ -#define PEER_DOWN_MULTIHOP_CHANGE 21 /* neighbor multihop command */ -#define PEER_DOWN_NSF_CLOSE_SESSION 22 /* NSF tcp session close */ -#define PEER_DOWN_V6ONLY_CHANGE 23 /* if-based peering v6only toggled */ -#define PEER_DOWN_BFD_DOWN 24 /* BFD down */ -#define PEER_DOWN_IF_DOWN 25 /* Interface down */ -#define PEER_DOWN_NBR_ADDR_DEL 26 /* Peer address lost */ - unsigned long last_reset_cause_size; + uint8_t last_reset; +#define PEER_DOWN_RID_CHANGE 1U /* bgp router-id command */ +#define PEER_DOWN_REMOTE_AS_CHANGE 2U /* neighbor remote-as command */ +#define PEER_DOWN_LOCAL_AS_CHANGE 3U /* neighbor local-as command */ +#define PEER_DOWN_CLID_CHANGE 4U /* bgp cluster-id command */ +#define PEER_DOWN_CONFED_ID_CHANGE 5U /* bgp confederation id command */ +#define PEER_DOWN_CONFED_PEER_CHANGE 6U /* bgp confederation peer command */ +#define PEER_DOWN_RR_CLIENT_CHANGE 7U /* neighbor rr-client command */ +#define PEER_DOWN_RS_CLIENT_CHANGE 8U /* neighbor rs-client command */ +#define PEER_DOWN_UPDATE_SOURCE_CHANGE 9U /* neighbor update-source command */ +#define PEER_DOWN_AF_ACTIVATE 10U /* neighbor activate command */ +#define PEER_DOWN_USER_SHUTDOWN 11U /* neighbor shutdown command */ +#define PEER_DOWN_USER_RESET 12U /* clear ip bgp command */ +#define PEER_DOWN_NOTIFY_RECEIVED 13U /* notification received */ +#define PEER_DOWN_NOTIFY_SEND 14U /* notification send */ +#define PEER_DOWN_CLOSE_SESSION 15U /* tcp session close */ +#define PEER_DOWN_NEIGHBOR_DELETE 16U /* neghbor delete */ +#define PEER_DOWN_RMAP_BIND 17U /* neghbor peer-group command */ +#define PEER_DOWN_RMAP_UNBIND 18U /* no neighbor peer-group command */ +#define PEER_DOWN_CAPABILITY_CHANGE 19U /* neighbor capability command */ +#define PEER_DOWN_PASSIVE_CHANGE 20U /* neighbor passive command */ +#define PEER_DOWN_MULTIHOP_CHANGE 21U /* neighbor multihop command */ +#define PEER_DOWN_NSF_CLOSE_SESSION 22U /* NSF tcp session close */ +#define PEER_DOWN_V6ONLY_CHANGE 23U /* if-based peering v6only toggled */ +#define PEER_DOWN_BFD_DOWN 24U /* BFD down */ +#define PEER_DOWN_IF_DOWN 25U /* Interface down */ +#define PEER_DOWN_NBR_ADDR_DEL 26U /* Peer address lost */ +#define PEER_DOWN_WAITING_NHT 27U /* Waiting for NHT to resolve */ +#define PEER_DOWN_NBR_ADDR 28U /* Waiting for peer IPv6 IP Addr */ +#define PEER_DOWN_VRF_UNINIT 29U /* Associated VRF is not init yet */ +#define PEER_DOWN_NOAFI_ACTIVATED 30U /* No AFI/SAFI activated for peer */ +#define PEER_DOWN_AS_SETS_REJECT 31U /* Reject routes with AS_SET */ +#define PEER_DOWN_WAITING_OPEN 32U /* Waiting for open to succeed */ +#define PEER_DOWN_PFX_COUNT 33U /* Reached received prefix count */ + /* + * Remember to update peer_down_str in bgp_fsm.c when you add + * a new value to the last_reset reason + */ + + size_t last_reset_cause_size; uint8_t last_reset_cause[BGP_MAX_PACKET_SIZE]; /* The kind of route-map Flags.*/ - uint8_t rmap_type; -#define PEER_RMAP_TYPE_IN (1 << 0) /* neighbor route-map in */ -#define PEER_RMAP_TYPE_OUT (1 << 1) /* neighbor route-map out */ -#define PEER_RMAP_TYPE_NETWORK (1 << 2) /* network route-map */ -#define PEER_RMAP_TYPE_REDISTRIBUTE (1 << 3) /* redistribute route-map */ -#define PEER_RMAP_TYPE_DEFAULT (1 << 4) /* default-originate route-map */ -#define PEER_RMAP_TYPE_NOSET (1 << 5) /* not allow to set commands */ -#define PEER_RMAP_TYPE_IMPORT (1 << 6) /* neighbor route-map import */ -#define PEER_RMAP_TYPE_EXPORT (1 << 7) /* neighbor route-map export */ + uint16_t rmap_type; +#define PEER_RMAP_TYPE_IN (1U << 0) /* neighbor route-map in */ +#define PEER_RMAP_TYPE_OUT (1U << 1) /* neighbor route-map out */ +#define PEER_RMAP_TYPE_NETWORK (1U << 2) /* network route-map */ +#define PEER_RMAP_TYPE_REDISTRIBUTE (1U << 3) /* redistribute route-map */ +#define PEER_RMAP_TYPE_DEFAULT (1U << 4) /* default-originate route-map */ +#define PEER_RMAP_TYPE_NOSET (1U << 5) /* not allow to set commands */ +#define PEER_RMAP_TYPE_IMPORT (1U << 6) /* neighbor route-map import */ +#define PEER_RMAP_TYPE_EXPORT (1U << 7) /* neighbor route-map export */ +#define PEER_RMAP_TYPE_AGGREGATE (1U << 8) /* aggregate-address route-map */ /* peer specific BFD information */ struct bfd_info *bfd_info; @@ -1202,6 +1481,9 @@ struct peer { char *hostname; char *domainname; + /* Sender side AS path loop detection. */ + bool as_path_loop_detection; + QOBJ_FIELDS }; DECLARE_QOBJ_TYPE(peer) @@ -1211,8 +1493,7 @@ DECLARE_QOBJ_TYPE(peer) ((peer)->attr = (group)->conf->attr) #define PEER_STR_ATTR_INHERIT(peer, group, attr, mt) \ do { \ - if ((peer)->attr) \ - XFREE(mt, (peer)->attr); \ + XFREE(mt, (peer)->attr); \ if ((group)->conf->attr) \ (peer)->attr = XSTRDUP(mt, (group)->conf->attr); \ else \ @@ -1231,7 +1512,8 @@ DECLARE_QOBJ_TYPE(peer) /* Check if suppress start/restart of sessions to peer. */ #define BGP_PEER_START_SUPPRESSED(P) \ (CHECK_FLAG((P)->flags, PEER_FLAG_SHUTDOWN) \ - || CHECK_FLAG((P)->sflags, PEER_STATUS_PREFIX_OVERFLOW)) + || CHECK_FLAG((P)->sflags, PEER_STATUS_PREFIX_OVERFLOW) \ + || CHECK_FLAG((P)->bgp->flags, BGP_FLAG_SHUTDOWN)) #define PEER_PASSWORD_MINLEN (1) #define PEER_PASSWORD_MAXLEN (80) @@ -1301,9 +1583,11 @@ struct bgp_nlri { #define BGP_ATTR_AS_PATHLIMIT 21 #define BGP_ATTR_PMSI_TUNNEL 22 #define BGP_ATTR_ENCAP 23 +#define BGP_ATTR_IPV6_EXT_COMMUNITIES 25 #define BGP_ATTR_LARGE_COMMUNITIES 32 #define BGP_ATTR_PREFIX_SID 40 -#if ENABLE_BGP_VNC_ATTR +#define BGP_ATTR_SRTE_COLOR 51 +#ifdef ENABLE_BGP_VNC_ATTR #define BGP_ATTR_VNC 255 #endif @@ -1311,6 +1595,7 @@ struct bgp_nlri { #define BGP_ORIGIN_IGP 0 #define BGP_ORIGIN_EGP 1 #define BGP_ORIGIN_INCOMPLETE 2 +#define BGP_ORIGIN_UNSPECIFIED 255 /* BGP notify message codes. */ #define BGP_NOTIFY_HEADER_ERR 1 @@ -1321,6 +1606,12 @@ struct bgp_nlri { #define BGP_NOTIFY_CEASE 6 #define BGP_NOTIFY_CAPABILITY_ERR 7 +/* Subcodes for BGP Finite State Machine Error */ +#define BGP_NOTIFY_FSM_ERR_SUBCODE_UNSPECIFIC 0 +#define BGP_NOTIFY_FSM_ERR_SUBCODE_OPENSENT 1 +#define BGP_NOTIFY_FSM_ERR_SUBCODE_OPENCONFIRM 2 +#define BGP_NOTIFY_FSM_ERR_SUBCODE_ESTABLISHED 3 + #define BGP_NOTIFY_SUBCODE_UNSPECIFIC 0 /* BGP_NOTIFY_HEADER_ERR sub codes. */ @@ -1366,42 +1657,18 @@ struct bgp_nlri { #define BGP_NOTIFY_CAPABILITY_INVALID_LENGTH 2 #define BGP_NOTIFY_CAPABILITY_MALFORMED_CODE 3 -/* BGP finite state machine status. */ -#define Idle 1 -#define Connect 2 -#define Active 3 -#define OpenSent 4 -#define OpenConfirm 5 -#define Established 6 -#define Clearing 7 -#define Deleted 8 -#define BGP_STATUS_MAX 9 - -/* BGP finite state machine events. */ -#define BGP_Start 1 -#define BGP_Stop 2 -#define TCP_connection_open 3 -#define TCP_connection_closed 4 -#define TCP_connection_open_failed 5 -#define TCP_fatal_error 6 -#define ConnectRetry_timer_expired 7 -#define Hold_Timer_expired 8 -#define KeepAlive_timer_expired 9 -#define Receive_OPEN_message 10 -#define Receive_KEEPALIVE_message 11 -#define Receive_UPDATE_message 12 -#define Receive_NOTIFICATION_message 13 -#define Clearing_Completed 14 -#define BGP_EVENTS_MAX 15 - /* BGP timers default value. */ -/* note: the DFLT_ ones depend on compile-time "defaults" selection */ #define BGP_INIT_START_TIMER 1 -#define BGP_DEFAULT_HOLDTIME DFLT_BGP_HOLDTIME -#define BGP_DEFAULT_KEEPALIVE DFLT_BGP_KEEPALIVE +/* The following 3 are RFC defaults that are overridden in bgp_vty.c with + * version-/profile-specific values. The values here do not matter, they only + * exist to provide a clear layering separation between core and CLI. + */ +#define BGP_DEFAULT_HOLDTIME 180 +#define BGP_DEFAULT_KEEPALIVE 60 +#define BGP_DEFAULT_CONNECT_RETRY 120 + #define BGP_DEFAULT_EBGP_ROUTEADV 0 #define BGP_DEFAULT_IBGP_ROUTEADV 0 -#define BGP_DEFAULT_CONNECT_RETRY DFLT_BGP_TIMERS_CONNECT /* BGP default local preference. */ #define BGP_DEFAULT_LOCAL_PREF 100 @@ -1416,6 +1683,8 @@ struct bgp_nlri { /* BGP graceful restart */ #define BGP_DEFAULT_RESTART_TIME 120 #define BGP_DEFAULT_STALEPATH_TIME 360 +#define BGP_DEFAULT_SELECT_DEFERRAL_TIME 360 +#define BGP_DEFAULT_RIB_STALE_TIME 500 /* BGP uptime string length. */ #define BGP_UPTIME_LEN 25 @@ -1424,9 +1693,6 @@ struct bgp_nlri { #define BGP_VTY_PORT 2605 #define BGP_DEFAULT_CONFIG "bgpd.conf" -/* Check AS path loop when we send NLRI. */ -/* #define BGP_SEND_ASPATH_CHECK */ - /* BGP Dynamic Neighbors feature */ #define BGP_DYNAMIC_NEIGHBORS_LIMIT_DEFAULT 100 #define BGP_DYNAMIC_NEIGHBORS_LIMIT_MIN 1 @@ -1449,17 +1715,16 @@ enum bgp_clear_type { /* BGP error codes. */ #define BGP_SUCCESS 0 +#define BGP_CREATED 1 #define BGP_ERR_INVALID_VALUE -1 #define BGP_ERR_INVALID_FLAG -2 #define BGP_ERR_INVALID_AS -3 #define BGP_ERR_INVALID_BGP -4 #define BGP_ERR_PEER_GROUP_MEMBER -5 -#define BGP_ERR_MULTIPLE_INSTANCE_USED -6 #define BGP_ERR_PEER_GROUP_NO_REMOTE_AS -7 #define BGP_ERR_PEER_GROUP_CANT_CHANGE -8 #define BGP_ERR_PEER_GROUP_MISMATCH -9 #define BGP_ERR_PEER_GROUP_PEER_TYPE_DIFFERENT -10 -#define BGP_ERR_MULTIPLE_INSTANCE_NOT_SET -11 #define BGP_ERR_AS_MISMATCH -12 #define BGP_ERR_PEER_FLAG_CONFLICT -13 #define BGP_ERR_PEER_GROUP_SHUTDOWN -14 @@ -1485,6 +1750,11 @@ enum bgp_clear_type { #define BGP_ERR_INVALID_FOR_DIRECT_PEER -34 #define BGP_ERR_PEER_SAFI_CONFLICT -35 +/* BGP GR ERRORS */ +#define BGP_ERR_GR_INVALID_CMD -36 +#define BGP_ERR_GR_OPERATION_FAILED -37 +#define BGP_GR_NO_OPERATION -38 + /* * Enumeration of different policy kinds a peer can be configured with. */ @@ -1546,8 +1816,10 @@ extern struct peer *peer_unlock_with_caller(const char *, struct peer *); #define peer_lock(B) peer_lock_with_caller(__FUNCTION__, (B)) extern bgp_peer_sort_t peer_sort(struct peer *peer); -extern int peer_active(struct peer *); -extern int peer_active_nego(struct peer *); +extern bgp_peer_sort_t peer_sort_lookup(struct peer *peer); + +extern bool peer_active(struct peer *); +extern bool peer_active_nego(struct peer *); extern void bgp_recalculate_all_bestpaths(struct bgp *bgp); extern struct peer *peer_create(union sockunion *, const char *, struct bgp *, as_t, as_t, int, afi_t, safi_t, @@ -1559,7 +1831,8 @@ extern char *peer_uptime(time_t uptime2, char *buf, size_t len, bool use_json, extern int bgp_config_write(struct vty *); -extern void bgp_master_init(struct thread_master *master); +extern void bgp_master_init(struct thread_master *master, + const int buffer_size); extern void bgp_init(unsigned short instance); extern void bgp_pthreads_run(void); @@ -1579,25 +1852,22 @@ extern int bgp_delete(struct bgp *); extern int bgp_handle_socket(struct bgp *bgp, struct vrf *vrf, vrf_id_t old_vrf_id, bool create); -extern int bgp_flag_set(struct bgp *, int); -extern int bgp_flag_unset(struct bgp *, int); -extern int bgp_flag_check(struct bgp *, int); - extern void bgp_router_id_zebra_bump(vrf_id_t, const struct prefix *); -extern int bgp_router_id_static_set(struct bgp *, struct in_addr); +extern void bgp_router_id_static_set(struct bgp *, struct in_addr); extern int bgp_cluster_id_set(struct bgp *, struct in_addr *); extern int bgp_cluster_id_unset(struct bgp *); extern int bgp_confederation_id_set(struct bgp *, as_t); extern int bgp_confederation_id_unset(struct bgp *); -extern int bgp_confederation_peers_check(struct bgp *, as_t); +extern bool bgp_confederation_peers_check(struct bgp *, as_t); extern int bgp_confederation_peers_add(struct bgp *, as_t); extern int bgp_confederation_peers_remove(struct bgp *, as_t); -extern int bgp_timers_set(struct bgp *, uint32_t keepalive, uint32_t holdtime); -extern int bgp_timers_unset(struct bgp *); +extern void bgp_timers_set(struct bgp *, uint32_t keepalive, uint32_t holdtime, + uint32_t connect_retry); +extern void bgp_timers_unset(struct bgp *); extern int bgp_default_local_preference_set(struct bgp *, uint32_t); extern int bgp_default_local_preference_unset(struct bgp *); @@ -1608,21 +1878,22 @@ extern int bgp_default_subgroup_pkt_queue_max_unset(struct bgp *bgp); extern int bgp_listen_limit_set(struct bgp *, int); extern int bgp_listen_limit_unset(struct bgp *); -extern int bgp_update_delay_active(struct bgp *); -extern int bgp_update_delay_configured(struct bgp *); +extern bool bgp_update_delay_active(struct bgp *); +extern bool bgp_update_delay_configured(struct bgp *); extern int bgp_afi_safi_peer_exists(struct bgp *bgp, afi_t afi, safi_t safi); extern void peer_as_change(struct peer *, as_t, int); extern int peer_remote_as(struct bgp *, union sockunion *, const char *, as_t *, int, afi_t, safi_t); extern int peer_group_remote_as(struct bgp *, const char *, as_t *, int); extern int peer_delete(struct peer *peer); +extern void peer_notify_unconfig(struct peer *peer); extern int peer_group_delete(struct peer_group *); extern int peer_group_remote_as_delete(struct peer_group *); extern int peer_group_listen_range_add(struct peer_group *, struct prefix *); +extern void peer_group_notify_unconfig(struct peer_group *group); extern int peer_activate(struct peer *, afi_t, safi_t); extern int peer_deactivate(struct peer *, afi_t, safi_t); -extern int peer_afc_set(struct peer *, afi_t, safi_t, int); extern int peer_group_bind(struct bgp *, union sockunion *, struct peer *, struct peer_group *, as_t *); @@ -1643,8 +1914,8 @@ extern int peer_ebgp_multihop_set(struct peer *, int); extern int peer_ebgp_multihop_unset(struct peer *); extern int is_ebgp_multihop_configured(struct peer *peer); -extern int peer_description_set(struct peer *, const char *); -extern int peer_description_unset(struct peer *); +extern void peer_description_set(struct peer *, const char *); +extern void peer_description_unset(struct peer *); extern int peer_update_source_if_set(struct peer *, const char *); extern int peer_update_source_addr_set(struct peer *, const union sockunion *); @@ -1655,8 +1926,8 @@ extern int peer_default_originate_set(struct peer *peer, afi_t afi, safi_t safi, struct route_map *route_map); extern int peer_default_originate_unset(struct peer *, afi_t, safi_t); -extern int peer_port_set(struct peer *, uint16_t); -extern int peer_port_unset(struct peer *); +extern void peer_port_set(struct peer *, uint16_t); +extern void peer_port_unset(struct peer *); extern int peer_weight_set(struct peer *, afi_t, safi_t, uint16_t); extern int peer_weight_unset(struct peer *, afi_t, safi_t); @@ -1704,7 +1975,7 @@ extern int peer_password_unset(struct peer *); extern int peer_unsuppress_map_unset(struct peer *, afi_t, safi_t); extern int peer_maximum_prefix_set(struct peer *, afi_t, safi_t, uint32_t, - uint8_t, int, uint16_t); + uint8_t, int, uint16_t, bool force); extern int peer_maximum_prefix_unset(struct peer *, afi_t, safi_t); extern int peer_clear(struct peer *, struct listnode **); @@ -1713,8 +1984,8 @@ extern int peer_clear_soft(struct peer *, afi_t, safi_t, enum bgp_clear_type); extern int peer_ttl_security_hops_set(struct peer *, int); extern int peer_ttl_security_hops_unset(struct peer *); -extern int peer_tx_shutdown_message_set(struct peer *, const char *msg); -extern int peer_tx_shutdown_message_unset(struct peer *); +extern void peer_tx_shutdown_message_set(struct peer *, const char *msg); +extern void peer_tx_shutdown_message_unset(struct peer *); extern int bgp_route_map_update_timer(struct thread *thread); extern void bgp_route_map_terminate(void); @@ -1731,8 +2002,37 @@ extern struct peer_af *peer_af_create(struct peer *, afi_t, safi_t); extern struct peer_af *peer_af_find(struct peer *, afi_t, safi_t); extern int peer_af_delete(struct peer *, afi_t, safi_t); +extern void bgp_shutdown_enable(struct bgp *bgp, const char *msg); +extern void bgp_shutdown_disable(struct bgp *bgp); + extern void bgp_close(void); extern void bgp_free(struct bgp *); +void bgp_gr_apply_running_config(void); + +/* BGP GR */ +int bgp_global_gr_init(struct bgp *bgp); +int bgp_peer_gr_init(struct peer *peer); + + +#define BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(_bgp, _peer_list) \ + do { \ + struct peer *peer_loop; \ + bool gr_router_detected = false; \ + struct listnode *node = {0}; \ + for (ALL_LIST_ELEMENTS_RO(_peer_list, node, peer_loop)) { \ + if (CHECK_FLAG(peer_loop->flags, \ + PEER_FLAG_GRACEFUL_RESTART)) \ + gr_router_detected = true; \ + } \ + if (gr_router_detected \ + && _bgp->present_zebra_gr_state == ZEBRA_GR_DISABLE) { \ + bgp_zebra_send_capabilities(_bgp, false); \ + } else if (!gr_router_detected \ + && _bgp->present_zebra_gr_state \ + == ZEBRA_GR_ENABLE) { \ + bgp_zebra_send_capabilities(_bgp, true); \ + } \ + } while (0) static inline struct bgp *bgp_lock(struct bgp *bgp) { @@ -1754,62 +2054,47 @@ static inline int afindex(afi_t afi, safi_t safi) switch (safi) { case SAFI_UNICAST: return BGP_AF_IPV4_UNICAST; - break; case SAFI_MULTICAST: return BGP_AF_IPV4_MULTICAST; - break; case SAFI_LABELED_UNICAST: return BGP_AF_IPV4_LBL_UNICAST; - break; case SAFI_MPLS_VPN: return BGP_AF_IPV4_VPN; - break; case SAFI_ENCAP: return BGP_AF_IPV4_ENCAP; - break; case SAFI_FLOWSPEC: return BGP_AF_IPV4_FLOWSPEC; default: return BGP_AF_MAX; - break; } break; case AFI_IP6: switch (safi) { case SAFI_UNICAST: return BGP_AF_IPV6_UNICAST; - break; case SAFI_MULTICAST: return BGP_AF_IPV6_MULTICAST; - break; case SAFI_LABELED_UNICAST: return BGP_AF_IPV6_LBL_UNICAST; - break; case SAFI_MPLS_VPN: return BGP_AF_IPV6_VPN; - break; case SAFI_ENCAP: return BGP_AF_IPV6_ENCAP; - break; case SAFI_FLOWSPEC: return BGP_AF_IPV6_FLOWSPEC; default: return BGP_AF_MAX; - break; } break; case AFI_L2VPN: switch (safi) { case SAFI_EVPN: return BGP_AF_L2VPN_EVPN; - break; default: return BGP_AF_MAX; - break; } default: return BGP_AF_MAX; - break; } } @@ -1916,4 +2201,12 @@ extern void bgp_unset_redist_vrf_bitmaps(struct bgp *, vrf_id_t); /* For benefit of rfapi */ extern struct peer *peer_new(struct bgp *bgp); + +extern struct peer *peer_lookup_in_view(struct vty *vty, struct bgp *bgp, + const char *ip_str, bool use_json); + +/* Hooks */ +DECLARE_HOOK(peer_status_changed, (struct peer * peer), (peer)) +void peer_nsf_stop(struct peer *peer); + #endif /* _QUAGGA_BGPD_H */ diff --git a/bgpd/rfapi/bgp_rfapi_cfg.c b/bgpd/rfapi/bgp_rfapi_cfg.c index 2220f0ed9a..2bcef97fc3 100644 --- a/bgpd/rfapi/bgp_rfapi_cfg.c +++ b/bgpd/rfapi/bgp_rfapi_cfg.c @@ -47,7 +47,7 @@ #include "bgpd/rfapi/vnc_import_bgp.h" #include "bgpd/rfapi/vnc_debug.h" -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC #undef BGP_VNC_DEBUG_MATCH_GROUP @@ -168,7 +168,7 @@ struct rfapi_nve_group_cfg *bgp_rfapi_cfg_match_group(struct rfapi_cfg *hc, agg_unlock_node(rn_un); } -#if BGP_VNC_DEBUG_MATCH_GROUP +#ifdef BGP_VNC_DEBUG_MATCH_GROUP { char buf[PREFIX_STRLEN]; @@ -2208,24 +2208,6 @@ void vnc_routemap_update(struct bgp *bgp, const char *unused) vnc_zlog_debug_verbose("%s done", __func__); } -#if 0 /* superseded */ -static void vnc_routemap_event(route_map_event_t type, /* ignored */ - const char *rmap_name) /* ignored */ -{ - struct listnode *mnode, *mnnode; - struct bgp *bgp; - - vnc_zlog_debug_verbose("%s(event type=%d)", __func__, type); - if (bm->bgp == NULL) /* may be called during cleanup */ - return; - - for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp)) - vnc_routemap_update(bgp, rmap_name); - - vnc_zlog_debug_verbose("%s: done", __func__); -} -#endif - /*------------------------------------------------------------------------- * nve-group *-----------------------------------------------------------------------*/ @@ -2983,10 +2965,18 @@ DEFUN_NOSH (exit_vnc, } static struct cmd_node bgp_vnc_defaults_node = { - BGP_VNC_DEFAULTS_NODE, "%s(config-router-vnc-defaults)# ", 1}; + .name = "bgp vnc defaults", + .node = BGP_VNC_DEFAULTS_NODE, + .parent_node = BGP_NODE, + .prompt = "%s(config-router-vnc-defaults)# ", +}; static struct cmd_node bgp_vnc_nve_group_node = { - BGP_VNC_NVE_GROUP_NODE, "%s(config-router-vnc-nve-group)# ", 1}; + .name = "bgp vnc nve", + .node = BGP_VNC_NVE_GROUP_NODE, + .parent_node = BGP_NODE, + .prompt = "%s(config-router-vnc-nve-group)# ", +}; /*------------------------------------------------------------------------- * VNC nve-group @@ -3406,7 +3396,11 @@ DEFUN_NOSH (exit_vrf_policy, } static struct cmd_node bgp_vrf_policy_node = { - BGP_VRF_POLICY_NODE, "%s(config-router-vrf-policy)# ", 1}; + .name = "bgp vrf policy", + .node = BGP_VRF_POLICY_NODE, + .parent_node = BGP_NODE, + .prompt = "%s(config-router-vrf-policy)# ", +}; /*------------------------------------------------------------------------- * vnc-l2-group @@ -3642,7 +3636,11 @@ DEFUN (vnc_l2_group_rt, static struct cmd_node bgp_vnc_l2_group_node = { - BGP_VNC_L2_GROUP_NODE, "%s(config-router-vnc-l2-group)# ", 1}; + .name = "bgp vnc l2", + .node = BGP_VNC_L2_GROUP_NODE, + .parent_node = BGP_NODE, + .prompt = "%s(config-router-vnc-l2-group)# ", +}; struct rfapi_l2_group_cfg * bgp_rfapi_get_group_by_lni_label(struct bgp *bgp, uint32_t logical_net_id, @@ -3699,14 +3697,10 @@ bgp_rfapi_get_ecommunity_by_lni_label(struct bgp *bgp, uint32_t is_import, void bgp_rfapi_cfg_init(void) { - /* main bgpd code does not use this hook, but vnc does */ - /* superseded by bgp_route_map_process_update_cb() */ - /* bgp_route_map_event_hook_add(vnc_routemap_event); */ - - install_node(&bgp_vnc_defaults_node, NULL); - install_node(&bgp_vnc_nve_group_node, NULL); - install_node(&bgp_vrf_policy_node, NULL); - install_node(&bgp_vnc_l2_group_node, NULL); + install_node(&bgp_vnc_defaults_node); + install_node(&bgp_vnc_nve_group_node); + install_node(&bgp_vrf_policy_node); + install_node(&bgp_vnc_l2_group_node); install_default(BGP_VRF_POLICY_NODE); install_default(BGP_VNC_DEFAULTS_NODE); install_default(BGP_VNC_NVE_GROUP_NODE); @@ -4633,7 +4627,8 @@ void bgp_rfapi_show_summary(struct bgp *bgp, struct vty *vty) (hc->rfp_cfg.download_type == RFAPI_RFP_DOWNLOAD_PARTIAL ? "(default)" : "")); - sprintf(tmp, "%u seconds", hc->rfp_cfg.ftd_advertisement_interval); + snprintf(tmp, sizeof(tmp), "%u seconds", + hc->rfp_cfg.ftd_advertisement_interval); vty_out(vty, "%-39s %-19s %s\n", " Advertisement Interval:", tmp, (hc->rfp_cfg.ftd_advertisement_interval == RFAPI_RFP_CFG_DEFAULT_FTD_ADVERTISEMENT_INTERVAL diff --git a/bgpd/rfapi/bgp_rfapi_cfg.h b/bgpd/rfapi/bgp_rfapi_cfg.h index b72d38220b..f1548a6173 100644 --- a/bgpd/rfapi/bgp_rfapi_cfg.h +++ b/bgpd/rfapi/bgp_rfapi_cfg.h @@ -24,7 +24,7 @@ #include "lib/table.h" #include "lib/routemap.h" -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC #include "rfapi.h" struct rfapi_l2_group_cfg { diff --git a/bgpd/rfapi/rfapi.c b/bgpd/rfapi/rfapi.c index 5525547454..0ff4b2c825 100644 --- a/bgpd/rfapi/rfapi.c +++ b/bgpd/rfapi/rfapi.c @@ -355,12 +355,12 @@ int rfapi_check(void *handle) void del_vnc_route(struct rfapi_descriptor *rfd, struct peer *peer, /* rfd->peer for RFP regs */ - struct bgp *bgp, safi_t safi, struct prefix *p, + struct bgp *bgp, safi_t safi, const struct prefix *p, struct prefix_rd *prd, uint8_t type, uint8_t sub_type, struct rfapi_nexthop *lnh, int kill) { afi_t afi; /* of the VN address */ - struct bgp_node *bn; + struct bgp_dest *bn; struct bgp_path_info *bpi; char buf[PREFIX_STRLEN]; char buf2[RD_ADDRSTRLEN]; @@ -382,21 +382,19 @@ void del_vnc_route(struct rfapi_descriptor *rfd, vnc_zlog_debug_verbose( "%s: peer=%p, prefix=%s, prd=%s afi=%d, safi=%d bn=%p, bn->info=%p", __func__, peer, buf, prefix_rd2str(prd, buf2, sizeof(buf2)), - afi, safi, bn, (bn ? bgp_node_get_bgp_path_info(bn) : NULL)); + afi, safi, bn, (bn ? bgp_dest_get_bgp_path_info(bn) : NULL)); - for (bpi = (bn ? bgp_node_get_bgp_path_info(bn) : NULL); bpi; + for (bpi = (bn ? bgp_dest_get_bgp_path_info(bn) : NULL); bpi; bpi = bpi->next) { vnc_zlog_debug_verbose( - "%s: trying bpi=%p, bpi->peer=%p, bpi->type=%d, bpi->sub_type=%d, bpi->extra->vnc.export.rfapi_handle=%p, local_pref=%u", + "%s: trying bpi=%p, bpi->peer=%p, bpi->type=%d, bpi->sub_type=%d, bpi->extra->vnc.export.rfapi_handle=%p, local_pref=%" PRIu64, __func__, bpi, bpi->peer, bpi->type, bpi->sub_type, (bpi->extra ? bpi->extra->vnc.export.rfapi_handle : NULL), - ((bpi->attr - && CHECK_FLAG(bpi->attr->flag, - ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))) - ? bpi->attr->local_pref - : 0)); + CHECK_FLAG(bpi->attr->flag, + ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF) + ? bpi->attr->local_pref : 0)); if (bpi->peer == peer && bpi->type == type && bpi->sub_type == sub_type && bpi->extra @@ -467,16 +465,16 @@ void del_vnc_route(struct rfapi_descriptor *rfd, __func__, safi, buf); if (safi == SAFI_MPLS_VPN) { - struct bgp_node *prn = NULL; + struct bgp_dest *pdest = NULL; struct bgp_table *table = NULL; - prn = bgp_node_get(bgp->rib[afi][safi], - (struct prefix *)prd); - table = bgp_node_get_bgp_table_info(prn); + pdest = bgp_node_get(bgp->rib[afi][safi], + (struct prefix *)prd); + table = bgp_dest_get_bgp_table_info(pdest); if (table) vnc_import_bgp_del_vnc_host_route_mode_resolve_nve( bgp, prd, table, p, bpi); - bgp_unlock_node(prn); + bgp_dest_unlock_node(pdest); } /* @@ -494,7 +492,7 @@ void del_vnc_route(struct rfapi_descriptor *rfd, __func__, safi, buf); } done: - bgp_unlock_node(bn); + bgp_dest_unlock_node(bn); } struct rfapi_nexthop *rfapi_nexthop_new(struct rfapi_nexthop *copyme) @@ -559,7 +557,7 @@ void rfapi_vn_options_free(struct rfapi_vn_option *p) /* Based on bgp_redistribute_add() */ void add_vnc_route(struct rfapi_descriptor *rfd, /* cookie, VPN UN addr, peer */ - struct bgp *bgp, int safi, struct prefix *p, + struct bgp *bgp, int safi, const struct prefix *p, struct prefix_rd *prd, struct rfapi_ip_addr *nexthop, uint32_t *local_pref, uint32_t *lifetime, /* NULL => dont send lifetime */ @@ -575,7 +573,7 @@ void add_vnc_route(struct rfapi_descriptor *rfd, /* cookie, VPN UN addr, peer */ afi_t afi; /* of the VN address */ struct bgp_path_info *new; struct bgp_path_info *bpi; - struct bgp_node *bn; + struct bgp_dest *bn; struct attr attr = {0}; struct attr *new_attr; @@ -840,7 +838,7 @@ void add_vnc_route(struct rfapi_descriptor *rfd, /* cookie, VPN UN addr, peer */ beec.val[1] = ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP; beec.val[6] = ((TunnelType) >> 8) & 0xff; beec.val[7] = (TunnelType)&0xff; - ecommunity_add_val(attr.ecommunity, &beec); + ecommunity_add_val(attr.ecommunity, &beec, false, false); } /* @@ -880,12 +878,12 @@ void add_vnc_route(struct rfapi_descriptor *rfd, /* cookie, VPN UN addr, peer */ attr.nexthop.s_addr = nexthop->addr.v4.s_addr; attr.mp_nexthop_global_in = nexthop->addr.v4; - attr.mp_nexthop_len = 4; + attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4; break; case AF_INET6: attr.mp_nexthop_global = nexthop->addr.v6; - attr.mp_nexthop_len = 16; + attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV6_GLOBAL; break; default: @@ -944,7 +942,7 @@ void add_vnc_route(struct rfapi_descriptor *rfd, /* cookie, VPN UN addr, peer */ * ecommunity: POINTS TO interned/refcounted dynamic 2-part AS attr * aspath: POINTS TO interned/refcounted hashed block */ - for (bpi = bgp_node_get_bgp_path_info(bn); bpi; bpi = bpi->next) { + for (bpi = bgp_dest_get_bgp_path_info(bn); bpi; bpi = bpi->next) { /* probably only need to check * bpi->extra->vnc.export.rfapi_handle */ if (bpi->peer == rfd->peer && bpi->type == type @@ -999,7 +997,7 @@ void add_vnc_route(struct rfapi_descriptor *rfd, /* cookie, VPN UN addr, peer */ if (attrhash_cmp(bpi->attr, new_attr) && !CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) { bgp_attr_unintern(&new_attr); - bgp_unlock_node(bn); + bgp_dest_unlock_node(bn); vnc_zlog_debug_any( "%s: Found route (safi=%d) at prefix %s, no change", @@ -1011,16 +1009,16 @@ void add_vnc_route(struct rfapi_descriptor *rfd, /* cookie, VPN UN addr, peer */ bgp_path_info_set_flag(bn, bpi, BGP_PATH_ATTR_CHANGED); if (safi == SAFI_MPLS_VPN) { - struct bgp_node *prn = NULL; + struct bgp_dest *pdest = NULL; struct bgp_table *table = NULL; - prn = bgp_node_get(bgp->rib[afi][safi], - (struct prefix *)prd); - table = bgp_node_get_bgp_table_info(prn); + pdest = bgp_node_get(bgp->rib[afi][safi], + (struct prefix *)prd); + table = bgp_dest_get_bgp_table_info(pdest); if (table) vnc_import_bgp_del_vnc_host_route_mode_resolve_nve( bgp, prd, table, p, bpi); - bgp_unlock_node(prn); + bgp_dest_unlock_node(pdest); } /* Rewrite BGP route information. */ @@ -1034,22 +1032,22 @@ void add_vnc_route(struct rfapi_descriptor *rfd, /* cookie, VPN UN addr, peer */ if (safi == SAFI_MPLS_VPN) { - struct bgp_node *prn = NULL; + struct bgp_dest *pdest = NULL; struct bgp_table *table = NULL; - prn = bgp_node_get(bgp->rib[afi][safi], - (struct prefix *)prd); - table = bgp_node_get_bgp_table_info(prn); + pdest = bgp_node_get(bgp->rib[afi][safi], + (struct prefix *)prd); + table = bgp_dest_get_bgp_table_info(pdest); if (table) vnc_import_bgp_add_vnc_host_route_mode_resolve_nve( bgp, prd, table, p, bpi); - bgp_unlock_node(prn); + bgp_dest_unlock_node(pdest); } /* Process change. */ bgp_aggregate_increment(bgp, p, bpi, afi, safi); bgp_process(bgp, bn, afi, safi); - bgp_unlock_node(bn); + bgp_dest_unlock_node(bn); vnc_zlog_debug_any( "%s: Found route (safi=%d) at prefix %s, changed attr", @@ -1078,19 +1076,19 @@ void add_vnc_route(struct rfapi_descriptor *rfd, /* cookie, VPN UN addr, peer */ bgp_path_info_add(bn, new); if (safi == SAFI_MPLS_VPN) { - struct bgp_node *prn = NULL; + struct bgp_dest *pdest = NULL; struct bgp_table *table = NULL; - prn = bgp_node_get(bgp->rib[afi][safi], (struct prefix *)prd); - table = bgp_node_get_bgp_table_info(prn); + pdest = bgp_node_get(bgp->rib[afi][safi], (struct prefix *)prd); + table = bgp_dest_get_bgp_table_info(pdest); if (table) vnc_import_bgp_add_vnc_host_route_mode_resolve_nve( bgp, prd, table, p, new); - bgp_unlock_node(prn); + bgp_dest_unlock_node(pdest); encode_label(label_val, &bn->local_label); } - bgp_unlock_node(bn); + bgp_dest_unlock_node(bn); bgp_process(bgp, bn, afi, safi); vnc_zlog_debug_any( @@ -1282,8 +1280,7 @@ static int rfapi_open_inner(struct rfapi_descriptor *rfd, struct bgp *bgp, * since this peer is not on the I/O thread, this lock is not strictly * necessary, but serves as a reminder to those who may meddle... */ - pthread_mutex_lock(&rfd->peer->io_mtx); - { + frr_with_mutex(&rfd->peer->io_mtx) { // we don't need any I/O related facilities if (rfd->peer->ibuf) stream_fifo_free(rfd->peer->ibuf); @@ -1300,7 +1297,6 @@ static int rfapi_open_inner(struct rfapi_descriptor *rfd, struct bgp *bgp, rfd->peer->obuf_work = NULL; rfd->peer->ibuf_work = NULL; } - pthread_mutex_unlock(&rfd->peer->io_mtx); { /* base code assumes have valid host pointer */ char buf[BUFSIZ]; @@ -1493,10 +1489,7 @@ void rfapiFreeBgpTeaOptionChain(struct bgp_tea_options *p) while (p) { next = p->next; - if (p->value) { - XFREE(MTYPE_BGP_TEA_OPTIONS_VALUE, p->value); - p->value = NULL; - } + XFREE(MTYPE_BGP_TEA_OPTIONS_VALUE, p->value); XFREE(MTYPE_BGP_TEA_OPTIONS, p); p = next; @@ -2157,7 +2150,7 @@ int rfapi_close(void *handle) vnc_zlog_debug_verbose("%s: rfd=%p", __func__, rfd); -#if RFAPI_WHO_IS_CALLING_ME +#ifdef RFAPI_WHO_IS_CALLING_ME #ifdef HAVE_GLIBC_BACKTRACE #define RFAPI_DEBUG_BACKTRACE_NENTRIES 5 { @@ -2657,7 +2650,8 @@ int rfapi_register(void *handle, struct rfapi_ip_prefix *prefix, ecom_value.val[7] = (l2o->logical_net_id >> 0) & 0xff; rtlist = ecommunity_new(); - ecommunity_add_val(rtlist, &ecom_value); + ecommunity_add_val(rtlist, &ecom_value, + false, false); } if (l2o->tag_id) { as_t as = bgp->as; @@ -2682,7 +2676,8 @@ int rfapi_register(void *handle, struct rfapi_ip_prefix *prefix, ecom_value.val[7] = val & 0xff; if (rtlist == NULL) rtlist = ecommunity_new(); - ecommunity_add_val(rtlist, &ecom_value); + ecommunity_add_val(rtlist, &ecom_value, + false, false); } } @@ -2929,7 +2924,7 @@ DEFUN (debug_rfapi_open, { struct rfapi_ip_addr vn; struct rfapi_ip_addr un; - uint32_t lifetime; + uint32_t lifetime = 0; int rc; rfapi_handle handle; @@ -3194,12 +3189,8 @@ DEFUN (debug_rfapi_register_vn_un_l2o, return CMD_WARNING_CONFIG_FAILED; } optary[opt_next].type = RFAPI_VN_OPTION_TYPE_L2ADDR; - if (opt_next) { - optary[opt_next - 1].next = optary + opt_next; - } else { - opt = optary; - } - ++opt_next; + opt = optary; + /* L2 option parsing END */ /* TBD fixme */ @@ -3515,7 +3506,7 @@ DEFUN (debug_rfapi_show_import, "\nLNI-based Ethernet Tables:\n"); first_l2 = 0; } - snprintf(buf, BUFSIZ, "L2VPN LNI=%u", lni); + snprintf(buf, sizeof(buf), "L2VPN LNI=%u", lni); rfapiShowImportTable( vty, buf, it->imported_vpn[AFI_L2VPN], 1); @@ -3674,51 +3665,55 @@ void rfapi_init(void) #ifdef DEBUG_RFAPI static void rfapi_print_exported(struct bgp *bgp) { - struct bgp_node *rdn; - struct bgp_node *rn; + struct bgp_dest *destn; + struct bgp_dest *dest; struct bgp_path_info *bpi; if (!bgp) return; - for (rdn = bgp_table_top(bgp->rib[AFI_IP][SAFI_MPLS_VPN]); rdn; - rdn = bgp_route_next(rdn)) { + for (destn = bgp_table_top(bgp->rib[AFI_IP][SAFI_MPLS_VPN]); destn; + destn = bgp_route_next(destn)) { struct bgp_table *table; - table = bgp_node_get_bgp_table_info(rdn); + table = bgp_dest_get_bgp_table_info(destn); if (!table) continue; - fprintf(stderr, "%s: vpn rdn=%p\n", __func__, rdn); - for (rn = bgp_table_top(table); rn; - rn = bgp_route_next(rn)) { - bpi = bgp_node_get_bgp_path_info(rn); + fprintf(stderr, "%s: vpn destn=%p\n", __func__, destn); + for (dest = bgp_table_top(table); dest; + dest = bgp_route_next(dest)) { + bpi = bgp_dest_get_bgp_path_info(dest); if (!bpi) continue; - fprintf(stderr, "%s: rn=%p\n", __func__, rn); + fprintf(stderr, "%s: dest=%p\n", __func__, dest); for (; bpi; bpi = bpi->next) { rfapiPrintBi((void *)2, bpi); /* 2 => stderr */ } } } - for (rdn = bgp_table_top(bgp->rib[AFI_IP][SAFI_ENCAP]); rdn; - rdn = bgp_route_next(rdn)) { + for (destn = bgp_table_top(bgp->rib[AFI_IP][SAFI_ENCAP]); destn; + destn = bgp_route_next(destn)) { struct bgp_table *table; - table = bgp_node_get_bgp_table_info(rdn); + table = bgp_dest_get_bgp_table_info(destn); if (!table) continue; - fprintf(stderr, "%s: encap rdn=%p\n", __func__, rdn); - for (rn = bgp_table_top(table)); rn; - rn = bgp_route_next(rn)) { - bpi = bgp_node_get_bgp_path_info(rn); - if (!bpi) - continue; - fprintf(stderr, "%s: rn=%p\n", __func__, rn); - for (; bpi; bpi = bpi->next) { - rfapiPrintBi((void *)2, bpi); /* 2 => stderr */ - } - } + fprintf(stderr, "%s: encap destn=%p\n", __func__, destn); + for (dest = bgp_table_top(table)) + ; + dest; + dest = bgp_route_next(dest)) + { + bpi = bgp_dest_get_bgp_path_info(dest); + if (!bpi) + continue; + fprintf(stderr, "%s: dest=%p\n", __func__, dest); + for (; bpi; bpi = bpi->next) { + rfapiPrintBi((void *)2, + bpi); /* 2 => stderr */ + } + } } } #endif /* defined(DEBUG_RFAPI) */ @@ -3752,8 +3747,7 @@ int rfapi_set_autord_from_vn(struct prefix_rd *rd, struct rfapi_ip_addr *vn) vnc_zlog_debug_verbose("%s: auto-assigning RD", __func__); if (vn->addr_family != AF_INET && vn->addr_family != AF_INET6) { vnc_zlog_debug_verbose( - "%s: can't auto-assign RD, VN addr family is not IPv4" - "|v6", + "%s: can't auto-assign RD, VN addr family is not IPv4|v6", __func__); return EAFNOSUPPORT; } diff --git a/bgpd/rfapi/rfapi.h b/bgpd/rfapi/rfapi.h index 6af2ebeeb8..beb44aa780 100644 --- a/bgpd/rfapi/rfapi.h +++ b/bgpd/rfapi/rfapi.h @@ -21,7 +21,7 @@ #ifndef _QUAGGA_BGP_RFAPI_H #define _QUAGGA_BGP_RFAPI_H -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC #include #include diff --git a/bgpd/rfapi/rfapi_ap.c b/bgpd/rfapi/rfapi_ap.c index c5fda15d33..abb18aeb2c 100644 --- a/bgpd/rfapi/rfapi_ap.c +++ b/bgpd/rfapi/rfapi_ap.c @@ -81,10 +81,10 @@ * is used to spread out the sort for adbs with the same lifetime * and thereby make the skip list operations more efficient. */ -static int sl_adb_lifetime_cmp(void *adb1, void *adb2) +static int sl_adb_lifetime_cmp(const void *adb1, const void *adb2) { - struct rfapi_adb *a1 = adb1; - struct rfapi_adb *a2 = adb2; + const struct rfapi_adb *a1 = adb1; + const struct rfapi_adb *a2 = adb2; if (a1->lifetime < a2->lifetime) return -1; diff --git a/bgpd/rfapi/rfapi_backend.h b/bgpd/rfapi/rfapi_backend.h index 96e464d2ae..4d2ae0b02f 100644 --- a/bgpd/rfapi/rfapi_backend.h +++ b/bgpd/rfapi/rfapi_backend.h @@ -21,7 +21,7 @@ #ifndef _QUAGGA_BGP_RFAPI_BACKEND_H #define _QUAGGA_BGP_RFAPI_BACKEND_H -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC #include "bgpd/bgp_route.h" #include "bgpd/bgp_nexthop.h" @@ -35,16 +35,16 @@ extern void rfapi_delete(struct bgp *); struct rfapi *bgp_rfapi_new(struct bgp *bgp); void bgp_rfapi_destroy(struct bgp *bgp, struct rfapi *h); -extern void rfapiProcessUpdate(struct peer *peer, void *rfd, struct prefix *p, - struct prefix_rd *prd, struct attr *attr, - afi_t afi, safi_t safi, uint8_t type, - uint8_t sub_type, uint32_t *label); +extern void rfapiProcessUpdate(struct peer *peer, void *rfd, + const struct prefix *p, struct prefix_rd *prd, + struct attr *attr, afi_t afi, safi_t safi, + uint8_t type, uint8_t sub_type, uint32_t *label); -extern void rfapiProcessWithdraw(struct peer *peer, void *rfd, struct prefix *p, - struct prefix_rd *prd, struct attr *attr, - afi_t afi, safi_t safi, uint8_t type, - int kill); +extern void rfapiProcessWithdraw(struct peer *peer, void *rfd, + const struct prefix *p, struct prefix_rd *prd, + struct attr *attr, afi_t afi, safi_t safi, + uint8_t type, int kill); extern void rfapiProcessPeerDown(struct peer *peer); @@ -56,7 +56,7 @@ extern void vnc_zebra_withdraw(struct prefix *p, struct bgp_path_info *old_select); -extern void rfapi_vty_out_vncinfo(struct vty *vty, struct prefix *p, +extern void rfapi_vty_out_vncinfo(struct vty *vty, const struct prefix *p, struct bgp_path_info *bpi, safi_t safi); diff --git a/bgpd/rfapi/rfapi_encap_tlv.c b/bgpd/rfapi/rfapi_encap_tlv.c index f31342e192..a7bc909c58 100644 --- a/bgpd/rfapi/rfapi_encap_tlv.c +++ b/bgpd/rfapi/rfapi_encap_tlv.c @@ -248,7 +248,6 @@ struct rfapi_un_option *rfapi_encap_tlv_to_un_option(struct attr *attr) } if (rc) { XFREE(MTYPE_RFAPI_UN_OPTION, uo); - uo = NULL; } return uo; } diff --git a/bgpd/rfapi/rfapi_import.c b/bgpd/rfapi/rfapi_import.c index 568f8d68e8..c3ad95ff28 100644 --- a/bgpd/rfapi/rfapi_import.c +++ b/bgpd/rfapi/rfapi_import.c @@ -200,7 +200,7 @@ void rfapiCheckRouteCount(void) } } -#if DEBUG_ROUTE_COUNTERS +#ifdef DEBUG_ROUTE_COUNTERS #define VNC_ITRCCK do {rfapiCheckRouteCount();} while (0) #else #define VNC_ITRCCK @@ -374,42 +374,15 @@ int rfapiGetVncLifetime(struct attr *attr, uint32_t *lifetime) return ENOENT; } -/* - * Extract the tunnel type from the extended community - */ -int rfapiGetTunnelType(struct attr *attr, bgp_encap_types *type) -{ - *type = BGP_ENCAP_TYPE_MPLS; /* default to MPLS */ - if (attr && attr->ecommunity) { - struct ecommunity *ecom = attr->ecommunity; - int i; - - for (i = 0; i < (ecom->size * ECOMMUNITY_SIZE); - i += ECOMMUNITY_SIZE) { - uint8_t *ep; - - ep = ecom->val + i; - if (ep[0] == ECOMMUNITY_ENCODE_OPAQUE - && ep[1] == ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP) { - *type = (ep[6] << 8) + ep[7]; - return 0; - } - } - } - - return ENOENT; -} - - /* * Look for UN address in Encap attribute */ int rfapiGetVncTunnelUnAddr(struct attr *attr, struct prefix *p) { struct bgp_attr_encap_subtlv *pEncap; - bgp_encap_types tun_type; + bgp_encap_types tun_type = BGP_ENCAP_TYPE_MPLS;/*Default tunnel type*/ - rfapiGetTunnelType(attr, &tun_type); + bgp_attr_extcom_tunnel_type(attr, &tun_type); if (tun_type == BGP_ENCAP_TYPE_MPLS) { if (!p) return 0; @@ -485,7 +458,7 @@ int rfapiGetUnAddrOfVpnBi(struct bgp_path_info *bpi, struct prefix *p) default: if (p) p->family = 0; -#if DEBUG_ENCAP_MONITOR +#ifdef DEBUG_ENCAP_MONITOR vnc_zlog_debug_verbose( "%s: bpi->extra->vnc.import.un_family is 0, no UN addr", __func__); @@ -511,8 +484,7 @@ static struct bgp_path_info *rfapiBgpInfoCreate(struct attr *attr, new = info_make(type, sub_type, 0, peer, attr, NULL); - if (attr) - new->attr = bgp_attr_intern(attr); + new->attr = bgp_attr_intern(attr); bgp_path_info_extra_get(new); if (prd) { @@ -543,9 +515,8 @@ static void rfapiBgpInfoFree(struct bgp_path_info *goner) peer_unlock(goner->peer); } - if (goner->attr) { - bgp_attr_unintern(&goner->attr); - } + bgp_attr_unintern(&goner->attr); + if (goner->extra) bgp_path_info_extra_free(&goner->extra); XFREE(MTYPE_BGP_ROUTE, goner); @@ -602,7 +573,7 @@ struct rfapi_import_table *rfapiMacImportTableGet(struct bgp *bgp, uint32_t lni) eval.val[7] = (lni >> 0) & 0xff; enew = ecommunity_new(); - ecommunity_add_val(enew, &eval); + ecommunity_add_val(enew, &eval, false, false); it->rt_import_list = enew; for (afi = AFI_IP; afi < AFI_MAX; ++afi) { @@ -638,7 +609,7 @@ rfapiMonitorMoveShorter(struct agg_node *original_vpn_node, int lockoffset) RFAPI_CHECK_REFCOUNT(original_vpn_node, SAFI_MPLS_VPN, lockoffset); -#if DEBUG_MONITOR_MOVE_SHORTER +#ifdef DEBUG_MONITOR_MOVE_SHORTER { char buf[PREFIX_STRLEN]; @@ -657,7 +628,7 @@ rfapiMonitorMoveShorter(struct agg_node *original_vpn_node, int lockoffset) struct prefix pfx; if (!rfapiGetUnAddrOfVpnBi(bpi, &pfx)) { -#if DEBUG_MONITOR_MOVE_SHORTER +#ifdef DEBUG_MONITOR_MOVE_SHORTER vnc_zlog_debug_verbose( "%s: have valid UN at original node, no change", __func__); @@ -692,14 +663,17 @@ rfapiMonitorMoveShorter(struct agg_node *original_vpn_node, int lockoffset) * If no less-specific routes, try to use the 0/0 node */ if (!par) { + const struct prefix *p; /* this isn't necessarily 0/0 */ par = agg_route_table_top(original_vpn_node); + if (par) + p = agg_node_get_prefix(par); /* * If we got the top node but it wasn't 0/0, * ignore it */ - if (par && par->p.prefixlen) { + if (par && p->prefixlen) { agg_unlock_node(par); /* maybe free */ par = NULL; } @@ -714,9 +688,10 @@ rfapiMonitorMoveShorter(struct agg_node *original_vpn_node, int lockoffset) */ if (!par) { struct prefix pfx_default; + const struct prefix *p = agg_node_get_prefix(original_vpn_node); memset(&pfx_default, 0, sizeof(pfx_default)); - pfx_default.family = original_vpn_node->p.family; + pfx_default.family = p->family; /* creates default node if none exists */ par = agg_node_get(agg_get_table(original_vpn_node), @@ -773,7 +748,7 @@ rfapiMonitorMoveShorter(struct agg_node *original_vpn_node, int lockoffset) agg_unlock_node(original_vpn_node); } -#if DEBUG_MONITOR_MOVE_SHORTER +#ifdef DEBUG_MONITOR_MOVE_SHORTER { char buf[PREFIX_STRLEN]; @@ -797,6 +772,7 @@ static void rfapiMonitorMoveLonger(struct agg_node *new_vpn_node) struct rfapi_monitor_vpn *mlast; struct bgp_path_info *bpi; struct agg_node *par; + const struct prefix *new_vpn_node_p = agg_node_get_prefix(new_vpn_node); RFAPI_CHECK_REFCOUNT(new_vpn_node, SAFI_MPLS_VPN, 0); @@ -837,12 +813,11 @@ static void rfapiMonitorMoveLonger(struct agg_node *new_vpn_node) * specific updated node */ for (mlast = NULL, monitor = RFAPI_MONITOR_VPN(par); monitor;) { - /* * If new longest match for monitor prefix is the new * route's prefix, move monitor to new route's prefix */ - if (prefix_match(&new_vpn_node->p, &monitor->p)) { + if (prefix_match(new_vpn_node_p, &monitor->p)) { /* detach */ if (mlast) { mlast->next = monitor->next; @@ -982,7 +957,7 @@ void rfapiImportTableRefDelByIt(struct bgp *bgp, } } -#if RFAPI_REQUIRE_ENCAP_BEEC +#ifdef RFAPI_REQUIRE_ENCAP_BEEC /* * Look for magic BGP Encapsulation Extended Community value * Format in RFC 5512 Sect. 4.5 @@ -1069,7 +1044,7 @@ int rfapiEcommunityGetEthernetTag(struct ecommunity *ecom, uint16_t *tag_id) for (i = 0; i < ecom->size; ++i) { as_t as = 0; int encode = 0; - uint8_t *p = ecom->val + (i * ECOMMUNITY_SIZE); + const uint8_t *p = ecom->val + (i * ECOMMUNITY_SIZE); /* High-order octet of type. */ encode = *p++; @@ -1121,7 +1096,6 @@ static int rfapiVpnBiNhEqualsPt(struct bgp_path_info *bpi, default: return 0; - break; } return 1; @@ -1140,9 +1114,6 @@ static int rfapiVpnBiSamePtUn(struct bgp_path_info *bpi1, if (!bpi1 || !bpi2) return 0; - if (!bpi1->attr || !bpi2->attr) - return 0; - /* * VN address comparisons */ @@ -1167,9 +1138,11 @@ static int rfapiVpnBiSamePtUn(struct bgp_path_info *bpi1, default: return 0; - break; } + memset(&pfx_un1, 0, sizeof(pfx_un1)); + memset(&pfx_un2, 0, sizeof(pfx_un2)); + /* * UN address comparisons */ @@ -1211,7 +1184,7 @@ static int rfapiVpnBiSamePtUn(struct bgp_path_info *bpi1, } } - if (!pfx_un1.family || !pfx_un2.family) + if (pfx_un1.family == 0 || pfx_un2.family == 0) return 0; if (pfx_un1.family != pfx_un2.family) @@ -1297,8 +1270,9 @@ rfapiRouteInfo2NextHopEntry(struct rfapi_ip_prefix *rprefix, { struct rfapi_next_hop_entry *new; int have_vnc_tunnel_un = 0; + const struct prefix *p = agg_node_get_prefix(rn); -#if DEBUG_ENCAP_MONITOR +#ifdef DEBUG_ENCAP_MONITOR vnc_zlog_debug_verbose("%s: entry, bpi %p, rn %p", __func__, bpi, rn); #endif @@ -1320,16 +1294,12 @@ rfapiRouteInfo2NextHopEntry(struct rfapi_ip_prefix *rprefix, vo->type = RFAPI_VN_OPTION_TYPE_L2ADDR; - memcpy(&vo->v.l2addr.macaddr, &rn->p.u.prefix_eth.octet, - ETH_ALEN); + memcpy(&vo->v.l2addr.macaddr, &p->u.prefix_eth.octet, ETH_ALEN); /* only low 3 bytes of this are significant */ - if (bpi->attr) { - (void)rfapiEcommunityGetLNI( - bpi->attr->ecommunity, - &vo->v.l2addr.logical_net_id); - (void)rfapiEcommunityGetEthernetTag( - bpi->attr->ecommunity, &vo->v.l2addr.tag_id); - } + (void)rfapiEcommunityGetLNI(bpi->attr->ecommunity, + &vo->v.l2addr.logical_net_id); + (void)rfapiEcommunityGetEthernetTag(bpi->attr->ecommunity, + &vo->v.l2addr.tag_id); /* local_nve_id comes from lower byte of RD type */ vo->v.l2addr.local_nve_id = bpi->extra->vnc.import.rd.val[1]; @@ -1349,129 +1319,116 @@ rfapiRouteInfo2NextHopEntry(struct rfapi_ip_prefix *rprefix, } } - if (bpi->attr) { - bgp_encap_types tun_type; - new->prefix.cost = rfapiRfpCost(bpi->attr); + bgp_encap_types tun_type = BGP_ENCAP_TYPE_MPLS; /*Default*/ + new->prefix.cost = rfapiRfpCost(bpi->attr); - struct bgp_attr_encap_subtlv *pEncap; - - switch (BGP_MP_NEXTHOP_FAMILY(bpi->attr->mp_nexthop_len)) { - case AF_INET: - new->vn_address.addr_family = AF_INET; - new->vn_address.addr.v4 = - bpi->attr->mp_nexthop_global_in; - break; + struct bgp_attr_encap_subtlv *pEncap; - case AF_INET6: - new->vn_address.addr_family = AF_INET6; - new->vn_address.addr.v6 = bpi->attr->mp_nexthop_global; - break; + switch (BGP_MP_NEXTHOP_FAMILY(bpi->attr->mp_nexthop_len)) { + case AF_INET: + new->vn_address.addr_family = AF_INET; + new->vn_address.addr.v4 = bpi->attr->mp_nexthop_global_in; + break; - default: - zlog_warn("%s: invalid vpn nexthop length: %d", - __func__, bpi->attr->mp_nexthop_len); - rfapi_free_next_hop_list(new); - return NULL; - } + case AF_INET6: + new->vn_address.addr_family = AF_INET6; + new->vn_address.addr.v6 = bpi->attr->mp_nexthop_global; + break; - for (pEncap = bpi->attr->vnc_subtlvs; pEncap; - pEncap = pEncap->next) { - switch (pEncap->type) { - case BGP_VNC_SUBTLV_TYPE_LIFETIME: - /* use configured lifetime, not attr lifetime */ - break; + default: + zlog_warn("%s: invalid vpn nexthop length: %d", __func__, + bpi->attr->mp_nexthop_len); + rfapi_free_next_hop_list(new); + return NULL; + } - default: - zlog_warn("%s: unknown VNC option type %d", - __func__, pEncap->type); + for (pEncap = bpi->attr->vnc_subtlvs; pEncap; pEncap = pEncap->next) { + switch (pEncap->type) { + case BGP_VNC_SUBTLV_TYPE_LIFETIME: + /* use configured lifetime, not attr lifetime */ + break; + default: + zlog_warn("%s: unknown VNC option type %d", __func__, + pEncap->type); - break; - } + break; } + } - rfapiGetTunnelType(bpi->attr, &tun_type); - if (tun_type == BGP_ENCAP_TYPE_MPLS) { - struct prefix p; - /* MPLS carries UN address in next hop */ - rfapiNexthop2Prefix(bpi->attr, &p); - if (p.family != 0) { - rfapiQprefix2Raddr(&p, &new->un_address); - have_vnc_tunnel_un = 1; - } + bgp_attr_extcom_tunnel_type(bpi->attr, &tun_type); + if (tun_type == BGP_ENCAP_TYPE_MPLS) { + struct prefix p; + /* MPLS carries UN address in next hop */ + rfapiNexthop2Prefix(bpi->attr, &p); + if (p.family != 0) { + rfapiQprefix2Raddr(&p, &new->un_address); + have_vnc_tunnel_un = 1; } + } - for (pEncap = bpi->attr->encap_subtlvs; pEncap; - pEncap = pEncap->next) { - switch (pEncap->type) { - case BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT: - /* - * Overrides ENCAP UN address, if any - */ - switch (pEncap->length) { - - case 8: - new->un_address.addr_family = AF_INET; - memcpy(&new->un_address.addr.v4, - pEncap->value, 4); - have_vnc_tunnel_un = 1; - break; + for (pEncap = bpi->attr->encap_subtlvs; pEncap; pEncap = pEncap->next) { + switch (pEncap->type) { + case BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT: + /* + * Overrides ENCAP UN address, if any + */ + switch (pEncap->length) { - case 20: - new->un_address.addr_family = AF_INET6; - memcpy(&new->un_address.addr.v6, - pEncap->value, 16); - have_vnc_tunnel_un = 1; - break; + case 8: + new->un_address.addr_family = AF_INET; + memcpy(&new->un_address.addr.v4, pEncap->value, + 4); + have_vnc_tunnel_un = 1; + break; - default: - zlog_warn( - "%s: invalid tunnel subtlv UN addr length (%d) for bpi %p", - __func__, pEncap->length, bpi); - } + case 20: + new->un_address.addr_family = AF_INET6; + memcpy(&new->un_address.addr.v6, pEncap->value, + 16); + have_vnc_tunnel_un = 1; break; default: zlog_warn( - "%s: unknown Encap Attribute option type %d", - __func__, pEncap->type); - - - break; + "%s: invalid tunnel subtlv UN addr length (%d) for bpi %p", + __func__, pEncap->length, bpi); } + break; + + default: + zlog_warn("%s: unknown Encap Attribute option type %d", + __func__, pEncap->type); + break; } + } - new->un_options = rfapi_encap_tlv_to_un_option(bpi->attr); + new->un_options = rfapi_encap_tlv_to_un_option(bpi->attr); -#if DEBUG_ENCAP_MONITOR - vnc_zlog_debug_verbose("%s: line %d: have_vnc_tunnel_un=%d", - __func__, __LINE__, have_vnc_tunnel_un); +#ifdef DEBUG_ENCAP_MONITOR + vnc_zlog_debug_verbose("%s: line %d: have_vnc_tunnel_un=%d", __func__, + __LINE__, have_vnc_tunnel_un); #endif - if (!have_vnc_tunnel_un && bpi->extra) { - /* - * use cached UN address from ENCAP route - */ - new->un_address.addr_family = - bpi->extra->vnc.import.un_family; - switch (new->un_address.addr_family) { - case AF_INET: - new->un_address.addr.v4 = - bpi->extra->vnc.import.un.addr4; - break; - case AF_INET6: - new->un_address.addr.v6 = - bpi->extra->vnc.import.un.addr6; - break; - default: - zlog_warn( - "%s: invalid UN addr family (%d) for bpi %p", - __func__, new->un_address.addr_family, - bpi); - rfapi_free_next_hop_list(new); - return NULL; - break; - } + if (!have_vnc_tunnel_un && bpi->extra) { + /* + * use cached UN address from ENCAP route + */ + new->un_address.addr_family = bpi->extra->vnc.import.un_family; + switch (new->un_address.addr_family) { + case AF_INET: + new->un_address.addr.v4 = + bpi->extra->vnc.import.un.addr4; + break; + case AF_INET6: + new->un_address.addr.v6 = + bpi->extra->vnc.import.un.addr6; + break; + default: + zlog_warn("%s: invalid UN addr family (%d) for bpi %p", + __func__, new->un_address.addr_family, bpi); + rfapi_free_next_hop_list(new); + return NULL; } } @@ -1495,7 +1452,7 @@ int rfapiHasNonRemovedRoutes(struct agg_node *rn) return 0; } -#if DEBUG_IT_NODES +#ifdef DEBUG_IT_NODES /* * DEBUG FUNCTION */ @@ -1540,7 +1497,8 @@ static int rfapiNhlAddNodeRoutes( struct prefix pfx_un; struct skiplist *seen_nexthops; int count = 0; - int is_l2 = (rn->p.family == AF_ETHERNET); + const struct prefix *p = agg_node_get_prefix(rn); + int is_l2 = (p->family == AF_ETHERNET); if (rfd_rib_node) { struct agg_table *atable = agg_get_table(rfd_rib_node); @@ -1556,7 +1514,7 @@ static int rfapiNhlAddNodeRoutes( } seen_nexthops = - skiplist_new(0, vnc_prefix_cmp, (void (*)(void *))prefix_free); + skiplist_new(0, vnc_prefix_cmp, prefix_free_lists); for (bpi = rn->info; bpi; bpi = bpi->next) { @@ -1564,7 +1522,7 @@ static int rfapiNhlAddNodeRoutes( struct prefix *newpfx; if (removed && !CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) { -#if DEBUG_RETURNED_NHL +#ifdef DEBUG_RETURNED_NHL vnc_zlog_debug_verbose( "%s: want holddown, this route not holddown, skip", __func__); @@ -1596,7 +1554,7 @@ static int rfapiNhlAddNodeRoutes( rfapiNexthop2Prefix(bpi->attr, &pfx_vn); } if (!skiplist_search(seen_nexthops, &pfx_vn, NULL)) { -#if DEBUG_RETURNED_NHL +#ifdef DEBUG_RETURNED_NHL char buf[PREFIX_STRLEN]; prefix2str(&pfx_vn, buf, sizeof(buf)); @@ -1608,7 +1566,7 @@ static int rfapiNhlAddNodeRoutes( } if (rfapiGetUnAddrOfVpnBi(bpi, &pfx_un)) { -#if DEBUG_ENCAP_MONITOR +#ifdef DEBUG_ENCAP_MONITOR vnc_zlog_debug_verbose( "%s: failed to get UN address of this VPN bpi", __func__); @@ -1673,14 +1631,14 @@ static int rfapiNhlAddSubtree( * hands in node->link */ if (agg_node_left(rn) && agg_node_left(rn) != omit_node) { if (agg_node_left(rn)->info) { + const struct prefix *p = + agg_node_get_prefix(agg_node_left(rn)); int count = 0; struct agg_node *rib_rn = NULL; - rfapiQprefix2Rprefix(&agg_node_left(rn)->p, &rprefix); - if (rfd_rib_table) { - rib_rn = agg_node_get(rfd_rib_table, - &agg_node_left(rn)->p); - } + rfapiQprefix2Rprefix(p, &rprefix); + if (rfd_rib_table) + rib_rn = agg_node_get(rfd_rib_table, p); count = rfapiNhlAddNodeRoutes( agg_node_left(rn), &rprefix, lifetime, 0, head, @@ -1700,14 +1658,15 @@ static int rfapiNhlAddSubtree( if (agg_node_right(rn) && agg_node_right(rn) != omit_node) { if (agg_node_right(rn)->info) { + const struct prefix *p = + agg_node_get_prefix(agg_node_right(rn)); int count = 0; struct agg_node *rib_rn = NULL; - rfapiQprefix2Rprefix(&agg_node_right(rn)->p, &rprefix); - if (rfd_rib_table) { - rib_rn = agg_node_get(rfd_rib_table, - &agg_node_right(rn)->p); - } + rfapiQprefix2Rprefix(p, &rprefix); + if (rfd_rib_table) + rib_rn = agg_node_get(rfd_rib_table, p); + count = rfapiNhlAddNodeRoutes( agg_node_right(rn), &rprefix, lifetime, 0, head, tail, exclude_vnaddr, rib_rn, @@ -1759,23 +1718,18 @@ struct rfapi_next_hop_entry *rfapiRouteNode2NextHopList( struct rfapi_next_hop_entry *answer = NULL; struct rfapi_next_hop_entry *last = NULL; struct agg_node *parent; + const struct prefix *p = agg_node_get_prefix(rn); int count = 0; struct agg_node *rib_rn; -#if DEBUG_RETURNED_NHL - { - char buf[PREFIX_STRLEN]; - - prefix2str(&rn->p, buf, sizeof(buf)); - vnc_zlog_debug_verbose("%s: called with node pfx=%s", __func__, - buf); - } +#ifdef DEBUG_RETURNED_NHL + vnc_zlog_debug_verbose("%s: called with node pfx=%rRN", __func__, rn); rfapiDebugBacktrace(); #endif - rfapiQprefix2Rprefix(&rn->p, &rprefix); + rfapiQprefix2Rprefix(p, &rprefix); - rib_rn = rfd_rib_table ? agg_node_get(rfd_rib_table, &rn->p) : NULL; + rib_rn = rfd_rib_table ? agg_node_get(rfd_rib_table, p) : NULL; /* * Add non-withdrawn routes at this node @@ -1793,7 +1747,7 @@ struct rfapi_next_hop_entry *rfapiRouteNode2NextHopList( pfx_target_original); vnc_zlog_debug_verbose("%s: %d nexthops, answer=%p", __func__, count, answer); -#if DEBUG_RETURNED_NHL +#ifdef DEBUG_RETURNED_NHL rfapiPrintNhl(NULL, answer); #endif if (rib_rn) @@ -1827,9 +1781,10 @@ struct rfapi_next_hop_entry *rfapiRouteNode2NextHopList( * Add non-withdrawn routes from less-specific prefix */ if (parent) { - rib_rn = rfd_rib_table ? agg_node_get(rfd_rib_table, &parent->p) - : NULL; - rfapiQprefix2Rprefix(&parent->p, &rprefix); + const struct prefix *p = agg_node_get_prefix(parent); + + rib_rn = rfd_rib_table ? agg_node_get(rfd_rib_table, p) : NULL; + rfapiQprefix2Rprefix(p, &rprefix); count += rfapiNhlAddNodeRoutes(parent, &rprefix, lifetime, 0, &answer, &last, exclude_vnaddr, rib_rn, pfx_target_original); @@ -1853,7 +1808,7 @@ struct rfapi_next_hop_entry *rfapiRouteNode2NextHopList( vnc_zlog_debug_verbose("%s: %d nexthops, answer=%p", __func__, count, answer); -#if DEBUG_RETURNED_NHL +#ifdef DEBUG_RETURNED_NHL rfapiPrintNhl(NULL, answer); #endif return answer; @@ -1910,12 +1865,14 @@ struct rfapi_next_hop_entry *rfapiEthRouteNode2NextHopList( struct rfapi_next_hop_entry *last = NULL; struct agg_node *rib_rn; - rib_rn = rfd_rib_table ? agg_node_get(rfd_rib_table, &rn->p) : NULL; + rib_rn = rfd_rib_table + ? agg_node_get(rfd_rib_table, agg_node_get_prefix(rn)) + : NULL; count = rfapiNhlAddNodeRoutes(rn, rprefix, lifetime, 0, &answer, &last, NULL, rib_rn, pfx_target_original); -#if DEBUG_ENCAP_MONITOR +#ifdef DEBUG_ENCAP_MONITOR vnc_zlog_debug_verbose("%s: node %p: %d non-holddown routes", __func__, rn, count); #endif @@ -1931,7 +1888,7 @@ struct rfapi_next_hop_entry *rfapiEthRouteNode2NextHopList( if (rib_rn) agg_unlock_node(rib_rn); -#if DEBUG_RETURNED_NHL +#ifdef DEBUG_RETURNED_NHL rfapiPrintNhl(NULL, answer); #endif @@ -2011,11 +1968,14 @@ static void rfapiBgpInfoAttachSorted(struct agg_node *rn, for (prev = NULL, next = rn->info; next; prev = next, next = next->next) { + enum bgp_path_selection_reason reason; + if (!bgp || (!CHECK_FLAG(info_new->flags, BGP_PATH_REMOVED) && CHECK_FLAG(next->flags, BGP_PATH_REMOVED)) || bgp_path_info_cmp_compatible(bgp, info_new, next, - pfx_buf, afi, safi) + pfx_buf, afi, safi, + &reason) == -1) { /* -1 if 1st is better */ break; } @@ -2050,10 +2010,10 @@ static void rfapiBgpInfoDetach(struct agg_node *rn, struct bgp_path_info *bpi) /* * For L3-indexed import tables */ -static int rfapi_bi_peer_rd_cmp(void *b1, void *b2) +static int rfapi_bi_peer_rd_cmp(const void *b1, const void *b2) { - struct bgp_path_info *bpi1 = b1; - struct bgp_path_info *bpi2 = b2; + const struct bgp_path_info *bpi1 = b1; + const struct bgp_path_info *bpi2 = b2; /* * Compare peers @@ -2066,8 +2026,9 @@ static int rfapi_bi_peer_rd_cmp(void *b1, void *b2) /* * compare RDs */ - return vnc_prefix_cmp((struct prefix *)&bpi1->extra->vnc.import.rd, - (struct prefix *)&bpi2->extra->vnc.import.rd); + return vnc_prefix_cmp( + (const struct prefix *)&bpi1->extra->vnc.import.rd, + (const struct prefix *)&bpi2->extra->vnc.import.rd); } /* @@ -2075,10 +2036,10 @@ static int rfapi_bi_peer_rd_cmp(void *b1, void *b2) * The BPIs in these tables should ALWAYS have an aux_prefix set because * they arrive via IPv4 or IPv6 advertisements. */ -static int rfapi_bi_peer_rd_aux_cmp(void *b1, void *b2) +static int rfapi_bi_peer_rd_aux_cmp(const void *b1, const void *b2) { - struct bgp_path_info *bpi1 = b1; - struct bgp_path_info *bpi2 = b2; + const struct bgp_path_info *bpi1 = b1; + const struct bgp_path_info *bpi2 = b2; int rc; /* @@ -2133,6 +2094,7 @@ static void rfapiItBiIndexAdd(struct agg_node *rn, /* Import table VPN node */ struct bgp_path_info *bpi) /* new BPI */ { struct skiplist *sl; + const struct prefix *p; assert(rn); assert(bpi); @@ -2149,7 +2111,8 @@ static void rfapiItBiIndexAdd(struct agg_node *rn, /* Import table VPN node */ sl = RFAPI_RDINDEX_W_ALLOC(rn); if (!sl) { - if (AF_ETHERNET == rn->p.family) { + p = agg_node_get_prefix(rn); + if (AF_ETHERNET == p->family) { sl = skiplist_new(0, rfapi_bi_peer_rd_aux_cmp, NULL); } else { sl = skiplist_new(0, rfapi_bi_peer_rd_cmp, NULL); @@ -2196,19 +2159,19 @@ static void rfapiItBiIndexDump(struct agg_node *rn) static struct bgp_path_info *rfapiItBiIndexSearch( struct agg_node *rn, /* Import table VPN node */ struct prefix_rd *prd, struct peer *peer, - struct prefix *aux_prefix) /* optional L3 addr for L2 ITs */ + const struct prefix *aux_prefix) /* optional L3 addr for L2 ITs */ { struct skiplist *sl; int rc; - struct bgp_path_info bpi_fake; - struct bgp_path_info_extra bpi_extra; + struct bgp_path_info bpi_fake = {0}; + struct bgp_path_info_extra bpi_extra = {0}; struct bgp_path_info *bpi_result; sl = RFAPI_RDINDEX(rn); if (!sl) return NULL; -#if DEBUG_BI_SEARCH +#ifdef DEBUG_BI_SEARCH { char buf[RD_ADDRSTRLEN]; char buf_aux_pfx[PREFIX_STRLEN]; @@ -2229,13 +2192,13 @@ static struct bgp_path_info *rfapiItBiIndexSearch( /* threshold is a WAG */ if (sl->count < 3) { -#if DEBUG_BI_SEARCH +#ifdef DEBUG_BI_SEARCH vnc_zlog_debug_verbose("%s: short list algorithm", __func__); #endif /* if short list, linear search might be faster */ for (bpi_result = rn->info; bpi_result; bpi_result = bpi_result->next) { -#if DEBUG_BI_SEARCH +#ifdef DEBUG_BI_SEARCH { char buf[RD_ADDRSTRLEN]; @@ -2252,7 +2215,7 @@ static struct bgp_path_info *rfapiItBiIndexSearch( ->vnc.import.rd, (struct prefix *)prd)) { -#if DEBUG_BI_SEARCH +#ifdef DEBUG_BI_SEARCH vnc_zlog_debug_verbose( "%s: peer and RD same, doing aux_prefix check", __func__); @@ -2263,7 +2226,7 @@ static struct bgp_path_info *rfapiItBiIndexSearch( &bpi_result->extra->vnc.import .aux_prefix)) { -#if DEBUG_BI_SEARCH +#ifdef DEBUG_BI_SEARCH vnc_zlog_debug_verbose("%s: match", __func__); #endif @@ -2276,7 +2239,7 @@ static struct bgp_path_info *rfapiItBiIndexSearch( bpi_fake.peer = peer; bpi_fake.extra = &bpi_extra; - bpi_fake.extra->vnc.import.rd = *(struct prefix_rd *)prd; + bpi_fake.extra->vnc.import.rd = *prd; if (aux_prefix) { bpi_fake.extra->vnc.import.aux_prefix = *aux_prefix; } else { @@ -2288,13 +2251,13 @@ static struct bgp_path_info *rfapiItBiIndexSearch( rc = skiplist_search(sl, (void *)&bpi_fake, (void *)&bpi_result); if (rc) { -#if DEBUG_BI_SEARCH +#ifdef DEBUG_BI_SEARCH vnc_zlog_debug_verbose("%s: no match", __func__); #endif return NULL; } -#if DEBUG_BI_SEARCH +#ifdef DEBUG_BI_SEARCH vnc_zlog_debug_verbose("%s: matched bpi=%p", __func__, bpi_result); #endif @@ -2419,10 +2382,22 @@ static int rfapiWithdrawTimerVPN(struct thread *t) struct rfapi_withdraw *wcb = t->arg; struct bgp_path_info *bpi = wcb->info; struct bgp *bgp = bgp_get_default(); - + const struct prefix *p; struct rfapi_monitor_vpn *moved; afi_t afi; + if (bgp == NULL) { + vnc_zlog_debug_verbose( + "%s: NULL BGP pointer, assume shutdown race condition!!!", + __func__); + return 0; + } + if (CHECK_FLAG(bgp->flags, BGP_FLAG_DELETE_IN_PROGRESS)) { + vnc_zlog_debug_verbose( + "%s: BGP delete in progress, assume shutdown race condition!!!", + __func__); + return 0; + } assert(wcb->node); assert(bpi); assert(wcb->import_table); @@ -2430,15 +2405,8 @@ static int rfapiWithdrawTimerVPN(struct thread *t) RFAPI_CHECK_REFCOUNT(wcb->node, SAFI_MPLS_VPN, wcb->lockoffset); - { - char buf[BUFSIZ]; - - vnc_zlog_debug_verbose( - "%s: removing bpi %p at prefix %s/%d", __func__, bpi, - rfapi_ntop(wcb->node->p.family, &wcb->node->p.u.prefix, - buf, BUFSIZ), - wcb->node->p.prefixlen); - } + vnc_zlog_debug_verbose("%s: removing bpi %p at prefix %pRN", __func__, + bpi, wcb->node); /* * Remove the route (doubly-linked) @@ -2447,7 +2415,8 @@ static int rfapiWithdrawTimerVPN(struct thread *t) && VALID_INTERIOR_TYPE(bpi->type)) RFAPI_MONITOR_EXTERIOR(wcb->node)->valid_interior_count--; - afi = family2afi(wcb->node->p.family); + p = agg_node_get_prefix(wcb->node); + afi = family2afi(p->family); wcb->import_table->holddown_count[afi] -= 1; /* keep count consistent */ rfapiItBiIndexDel(wcb->node, bpi); rfapiBgpInfoDetach(wcb->node, bpi); /* with removed bpi */ @@ -2616,12 +2585,6 @@ static int rfapiAttrNexthopAddrDifferent(struct prefix *p1, struct prefix *p2) static void rfapiCopyUnEncap2VPN(struct bgp_path_info *encap_bpi, struct bgp_path_info *vpn_bpi) { - if (!encap_bpi->attr) { - zlog_warn("%s: no encap bpi attr/extra, can't copy UN address", - __func__); - return; - } - if (!vpn_bpi || !vpn_bpi->extra) { zlog_warn("%s: no vpn bpi attr/extra, can't copy UN address", __func__); @@ -2884,11 +2847,13 @@ rfapiBiStartWithdrawTimer(struct rfapi_import_table *import_table, } -typedef void(rfapi_bi_filtered_import_f)(struct rfapi_import_table *, int, - struct peer *, void *, struct prefix *, - struct prefix *, afi_t, - struct prefix_rd *, struct attr *, - uint8_t, uint8_t, uint32_t *); +typedef void(rfapi_bi_filtered_import_f)(struct rfapi_import_table *table, + int action, struct peer *peer, + void *rfd, const struct prefix *prefix, + const struct prefix *aux_prefix, + afi_t afi, struct prefix_rd *prd, + struct attr *attr, uint8_t type, + uint8_t sub_type, uint32_t *label); static void rfapiExpireEncapNow(struct rfapi_import_table *it, @@ -2937,11 +2902,11 @@ static int rfapiGetNexthop(struct attr *attr, struct prefix *prefix) static void rfapiBgpInfoFilteredImportEncap( struct rfapi_import_table *import_table, int action, struct peer *peer, void *rfd, /* set for looped back routes */ - struct prefix *p, - struct prefix *aux_prefix, /* Unused for encap routes */ + const struct prefix *p, + const struct prefix *aux_prefix, /* Unused for encap routes */ afi_t afi, struct prefix_rd *prd, struct attr *attr, /* part of bgp_path_info */ - uint8_t type, /* part of bgp_path_info */ + uint8_t type, /* part of bgp_path_info */ uint8_t sub_type, /* part of bgp_path_info */ uint32_t *label) /* part of bgp_path_info */ { @@ -2996,7 +2961,7 @@ static void rfapiBgpInfoFilteredImportEncap( __func__); return; } -#if RFAPI_REQUIRE_ENCAP_BEEC +#ifdef RFAPI_REQUIRE_ENCAP_BEEC if (!rfapiEcommunitiesMatchBeec(attr->ecommunity)) { vnc_zlog_debug_verbose( "%s: it=%p: no match for BGP Encapsulation ecommunity", @@ -3045,7 +3010,7 @@ static void rfapiBgpInfoFilteredImportEncap( */ rn = agg_node_lookup(rt, p); -#if DEBUG_ENCAP_MONITOR +#ifdef DEBUG_ENCAP_MONITOR vnc_zlog_debug_verbose("%s: initial encap lookup(it=%p) rn=%p", __func__, import_table, rn); #endif @@ -3112,11 +3077,8 @@ static void rfapiBgpInfoFilteredImportEncap( if (action == FIF_ACTION_WITHDRAW) { vnc_zlog_debug_verbose( - "%s: withdrawing at prefix %s/%d", - __func__, - inet_ntop(rn->p.family, &rn->p.u.prefix, - buf, BUFSIZ), - rn->p.prefixlen); + "%s: withdrawing at prefix %pRN", + __func__, rn); rfapiBiStartWithdrawTimer( import_table, rn, bpi, afi, SAFI_ENCAP, @@ -3124,13 +3086,11 @@ static void rfapiBgpInfoFilteredImportEncap( } else { vnc_zlog_debug_verbose( - "%s: %s at prefix %s/%d", __func__, + "%s: %s at prefix %pRN", __func__, ((action == FIF_ACTION_KILL) ? "killing" : "replacing"), - inet_ntop(rn->p.family, &rn->p.u.prefix, - buf, BUFSIZ), - rn->p.prefixlen); + rn); /* * If this route is waiting to be deleted @@ -3198,10 +3158,8 @@ static void rfapiBgpInfoFilteredImportEncap( rn = agg_node_get(rt, p); } - vnc_zlog_debug_verbose( - "%s: (afi=%d, rn=%p) inserting at prefix %s/%d", __func__, afi, - rn, inet_ntop(rn->p.family, &rn->p.u.prefix, buf, BUFSIZ), - rn->p.prefixlen); + vnc_zlog_debug_verbose("%s: (afi=%d, rn=%p) inserting at prefix %pRN", + __func__, afi, rn, rn); rfapiBgpInfoAttachSorted(rn, info_new, afi, SAFI_ENCAP); @@ -3279,11 +3237,12 @@ static void rfapiBgpInfoFilteredImportEncap( /* * iterate over the set of monitors at this ENCAP node. */ -#if DEBUG_ENCAP_MONITOR +#ifdef DEBUG_ENCAP_MONITOR vnc_zlog_debug_verbose("%s: examining monitors at rn=%p", __func__, rn); #endif for (m = RFAPI_MONITOR_ENCAP(rn); m; m = m->next) { + const struct prefix *p; /* * For each referenced bpi/route, copy the ENCAP route's @@ -3311,9 +3270,9 @@ static void rfapiBgpInfoFilteredImportEncap( * list * per prefix. */ - + p = agg_node_get_prefix(m->node); referenced_vpn_prefix = - agg_node_get(referenced_vpn_table, &m->node->p); + agg_node_get(referenced_vpn_table, p); assert(referenced_vpn_prefix); for (mnext = referenced_vpn_prefix->info; mnext; mnext = mnext->next) { @@ -3398,11 +3357,11 @@ static void rfapiExpireVpnNow(struct rfapi_import_table *it, void rfapiBgpInfoFilteredImportVPN( struct rfapi_import_table *import_table, int action, struct peer *peer, void *rfd, /* set for looped back routes */ - struct prefix *p, - struct prefix *aux_prefix, /* AFI_L2VPN: optional IP */ + const struct prefix *p, + const struct prefix *aux_prefix, /* AFI_L2VPN: optional IP */ afi_t afi, struct prefix_rd *prd, struct attr *attr, /* part of bgp_path_info */ - uint8_t type, /* part of bgp_path_info */ + uint8_t type, /* part of bgp_path_info */ uint8_t sub_type, /* part of bgp_path_info */ uint32_t *label) /* part of bgp_path_info */ { @@ -3563,11 +3522,8 @@ void rfapiBgpInfoFilteredImportVPN( BGP_PATH_REMOVED); vnc_zlog_debug_verbose( - "%s: withdrawing at prefix %s/%d%s", - __func__, rfapi_ntop(rn->p.family, - &rn->p.u.prefix, - buf, BUFSIZ), - rn->p.prefixlen, + "%s: withdrawing at prefix %pRN%s", + __func__, rn, (washolddown ? " (already being withdrawn)" : "")); @@ -3586,14 +3542,11 @@ void rfapiBgpInfoFilteredImportVPN( VNC_ITRCCK; } else { vnc_zlog_debug_verbose( - "%s: %s at prefix %s/%d", __func__, + "%s: %s at prefix %pRN", __func__, ((action == FIF_ACTION_KILL) ? "killing" : "replacing"), - rfapi_ntop(rn->p.family, - &rn->p.u.prefix, buf, - BUFSIZ), - rn->p.prefixlen); + rn); /* * If this route is waiting to be deleted @@ -3711,10 +3664,8 @@ void rfapiBgpInfoFilteredImportVPN( info_new->extra->vnc.import.aux_prefix = *aux_prefix; } - vnc_zlog_debug_verbose( - "%s: inserting bpi %p at prefix %s/%d #%d", __func__, info_new, - rfapi_ntop(rn->p.family, &rn->p.u.prefix, buf, BUFSIZ), - rn->p.prefixlen, rn->lock); + vnc_zlog_debug_verbose("%s: inserting bpi %p at prefix %pRN #%d", + __func__, info_new, rn, rn->lock); rfapiBgpInfoAttachSorted(rn, info_new, afi, SAFI_MPLS_VPN); rfapiItBiIndexAdd(rn, info_new); @@ -3877,11 +3828,11 @@ void rfapiBgpInfoFilteredImportVPN( static void rfapiBgpInfoFilteredImportBadSafi( struct rfapi_import_table *import_table, int action, struct peer *peer, void *rfd, /* set for looped back routes */ - struct prefix *p, - struct prefix *aux_prefix, /* AFI_L2VPN: optional IP */ + const struct prefix *p, + const struct prefix *aux_prefix, /* AFI_L2VPN: optional IP */ afi_t afi, struct prefix_rd *prd, struct attr *attr, /* part of bgp_path_info */ - uint8_t type, /* part of bgp_path_info */ + uint8_t type, /* part of bgp_path_info */ uint8_t sub_type, /* part of bgp_path_info */ uint32_t *label) /* part of bgp_path_info */ { @@ -3907,7 +3858,7 @@ rfapiBgpInfoFilteredImportFunction(safi_t safi) void rfapiProcessUpdate(struct peer *peer, void *rfd, /* set when looped from RFP/RFAPI */ - struct prefix *p, struct prefix_rd *prd, + const struct prefix *p, struct prefix_rd *prd, struct attr *attr, afi_t afi, safi_t safi, uint8_t type, uint8_t sub_type, uint32_t *label) { @@ -3991,7 +3942,7 @@ void rfapiProcessUpdate(struct peer *peer, } -void rfapiProcessWithdraw(struct peer *peer, void *rfd, struct prefix *p, +void rfapiProcessWithdraw(struct peer *peer, void *rfd, const struct prefix *p, struct prefix_rd *prd, struct attr *attr, afi_t afi, safi_t safi, uint8_t type, int kill) { @@ -4035,7 +3986,7 @@ void rfapiProcessWithdraw(struct peer *peer, void *rfd, struct prefix *p, rc == 0; rc = skiplist_next(h->import_mac, NULL, (void **)&it, &cursor)) { -#if DEBUG_L2_EXTRA +#ifdef DEBUG_L2_EXTRA vnc_zlog_debug_verbose( "%s: calling rfapiBgpInfoFilteredImportVPN(it=%p, afi=AFI_L2VPN)", __func__, it); @@ -4135,6 +4086,9 @@ static void rfapiProcessPeerDownRt(struct peer *peer, timer_service_func = rfapiWithdrawTimerEncap; break; default: + /* Suppress uninitialized variable warning */ + rt = NULL; + timer_service_func = NULL; assert(0); } @@ -4223,8 +4177,8 @@ static void rfapiBgpTableFilteredImport(struct bgp *bgp, struct rfapi_import_table *it, afi_t afi, safi_t safi) { - struct bgp_node *rn1; - struct bgp_node *rn2; + struct bgp_dest *dest1; + struct bgp_dest *dest2; /* Only these SAFIs have 2-level RIBS */ assert(safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP); @@ -4234,17 +4188,18 @@ static void rfapiBgpTableFilteredImport(struct bgp *bgp, * route tables attached to them, and import the routes * if they have matching route targets */ - for (rn1 = bgp_table_top(bgp->rib[afi][safi]); rn1; - rn1 = bgp_route_next(rn1)) { + for (dest1 = bgp_table_top(bgp->rib[afi][safi]); dest1; + dest1 = bgp_route_next(dest1)) { - if (bgp_node_has_bgp_path_info_data(rn1)) { + if (bgp_dest_has_bgp_path_info_data(dest1)) { - for (rn2 = bgp_table_top(bgp_node_get_bgp_table_info(rn1)); rn2; - rn2 = bgp_route_next(rn2)) { + for (dest2 = bgp_table_top( + bgp_dest_get_bgp_table_info(dest1)); + dest2; dest2 = bgp_route_next(dest2)) { struct bgp_path_info *bpi; - for (bpi = bgp_node_get_bgp_path_info(rn2); + for (bpi = bgp_dest_get_bgp_path_info(dest2); bpi; bpi = bpi->next) { uint32_t label = 0; @@ -4259,9 +4214,12 @@ static void rfapiBgpTableFilteredImport(struct bgp *bgp, safi))( it, /* which import table */ FIF_ACTION_UPDATE, bpi->peer, - NULL, &rn2->p, /* prefix */ + NULL, + bgp_dest_get_prefix(dest2), NULL, afi, - (struct prefix_rd *)&rn1->p, + (struct prefix_rd *) + bgp_dest_get_prefix( + dest1), bpi->attr, bpi->type, bpi->sub_type, &label); } @@ -4391,7 +4349,7 @@ rfapiImportTableRefAdd(struct bgp *bgp, struct ecommunity *rt_import_list, it->rt_import_list = ecommunity_dup(rt_import_list); it->rfg = rfg; it->monitor_exterior_orphans = - skiplist_new(0, NULL, (void (*)(void *))prefix_free); + skiplist_new(0, NULL, prefix_free_lists); /* * fill import route tables from RIBs @@ -4443,7 +4401,7 @@ static void rfapiDeleteRemotePrefixesIt( { afi_t afi; -#if DEBUG_L2_EXTRA +#ifdef DEBUG_L2_EXTRA { char buf_pfx[PREFIX_STRLEN]; @@ -4479,27 +4437,20 @@ static void rfapiDeleteRemotePrefixesIt( for (rn = agg_route_top(rt); rn; rn = agg_route_next(rn)) { struct bgp_path_info *bpi; struct bgp_path_info *next; + const struct prefix *rn_p = agg_node_get_prefix(rn); if (p && VNC_DEBUG(IMPORT_DEL_REMOTE)) { char p1line[PREFIX_STRLEN]; - char p2line[PREFIX_STRLEN]; prefix2str(p, p1line, sizeof(p1line)); - prefix2str(&rn->p, p2line, sizeof(p2line)); - vnc_zlog_debug_any("%s: want %s, have %s", - __func__, p1line, p2line); + vnc_zlog_debug_any("%s: want %s, have %pRN", + __func__, p1line, rn); } - if (p && prefix_cmp(p, &rn->p)) + if (p && prefix_cmp(p, rn_p)) continue; - { - char buf_pfx[PREFIX_STRLEN]; - - prefix2str(&rn->p, buf_pfx, sizeof(buf_pfx)); - vnc_zlog_debug_verbose("%s: rn pfx=%s", - __func__, buf_pfx); - } + vnc_zlog_debug_verbose("%s: rn pfx=%pRN", __func__, rn); /* TBD is this valid for afi == AFI_L2VPN? */ RFAPI_CHECK_REFCOUNT(rn, SAFI_MPLS_VPN, 1); @@ -4516,14 +4467,13 @@ static void rfapiDeleteRemotePrefixesIt( vnc_zlog_debug_verbose("%s: examining bpi %p", __func__, bpi); - if (bpi->attr) { - if (!rfapiGetNexthop(bpi->attr, &qpt)) - qpt_valid = 1; - } + if (!rfapiGetNexthop(bpi->attr, &qpt)) + qpt_valid = 1; + if (vn) { if (!qpt_valid || !prefix_match(vn, &qpt)) { -#if DEBUG_L2_EXTRA +#ifdef DEBUG_L2_EXTRA vnc_zlog_debug_verbose( "%s: continue at vn && !qpt_valid || !prefix_match(vn, &qpt)", __func__); @@ -4538,7 +4488,7 @@ static void rfapiDeleteRemotePrefixesIt( if (un) { if (!qct_valid || !prefix_match(un, &qct)) { -#if DEBUG_L2_EXTRA +#ifdef DEBUG_L2_EXTRA vnc_zlog_debug_verbose( "%s: continue at un && !qct_valid || !prefix_match(un, &qct)", __func__); @@ -4632,7 +4582,7 @@ static void rfapiDeleteRemotePrefixesIt( } } - vnc_direct_bgp_rh_del_route(bgp, afi, &rn->p, + vnc_direct_bgp_rh_del_route(bgp, afi, rn_p, bpi->peer); RFAPI_UPDATE_ITABLE_COUNT(bpi, it, afi, -1); diff --git a/bgpd/rfapi/rfapi_import.h b/bgpd/rfapi/rfapi_import.h index 1ab9cc5193..50093111c2 100644 --- a/bgpd/rfapi/rfapi_import.h +++ b/bgpd/rfapi/rfapi_import.h @@ -143,11 +143,11 @@ extern void rfapiUnicastNexthop2Prefix(afi_t afi, struct attr *attr, extern void rfapiBgpInfoFilteredImportVPN( struct rfapi_import_table *import_table, int action, struct peer *peer, void *rfd, /* set for looped back routes */ - struct prefix *p, - struct prefix *aux_prefix, /* AFI_ETHER: optional IP */ + const struct prefix *p, + const struct prefix *aux_prefix, /* AFI_ETHER: optional IP */ afi_t afi, struct prefix_rd *prd, struct attr *attr, /* part of bgp_path_info */ - uint8_t type, /* part of bgp_path_info */ + uint8_t type, /* part of bgp_path_info */ uint8_t sub_type, /* part of bgp_path_info */ uint32_t *label); /* part of bgp_path_info */ diff --git a/bgpd/rfapi/rfapi_monitor.c b/bgpd/rfapi/rfapi_monitor.c index dc1f7e0fbb..cd26892b84 100644 --- a/bgpd/rfapi/rfapi_monitor.c +++ b/bgpd/rfapi/rfapi_monitor.c @@ -789,7 +789,8 @@ static void rfapiMonitorTimerRestart(struct rfapi_monitor_vpn *m) * been responsible for the response, i.e., any monitors for * the exact prefix or a parent of it. */ -void rfapiMonitorTimersRestart(struct rfapi_descriptor *rfd, struct prefix *p) +void rfapiMonitorTimersRestart(struct rfapi_descriptor *rfd, + const struct prefix *p) { struct agg_node *rn; @@ -818,12 +819,14 @@ void rfapiMonitorTimersRestart(struct rfapi_descriptor *rfd, struct prefix *p) for (rn = agg_route_top(rfd->mon); rn; rn = agg_route_next(rn)) { struct rfapi_monitor_vpn *m; + const struct prefix *p_node; if (!((m = rn->info))) continue; + p_node = agg_node_get_prefix(m->node); /* NB order of test is significant ! */ - if (!m->node || prefix_match(&m->node->p, p)) { + if (!m->node || prefix_match(p_node, p)) { rfapiMonitorTimerRestart(m); } } @@ -841,7 +844,8 @@ void rfapiMonitorItNodeChanged( struct skiplist *nves_seen; struct agg_node *rn = it_node; struct bgp *bgp = bgp_get_default(); - afi_t afi = family2afi(rn->p.family); + const struct prefix *p = agg_node_get_prefix(rn); + afi_t afi = family2afi(p->family); #if DEBUG_L2_EXTRA char buf_prefix[PREFIX_STRLEN]; #endif @@ -866,10 +870,9 @@ void rfapiMonitorItNodeChanged( if ((sl = RFAPI_MONITOR_ETH(rn))) { for (cursor = NULL, - rc = skiplist_next(sl, NULL, (void **)&m, - (void **)&cursor); + rc = skiplist_next(sl, NULL, (void **)&m, &cursor); !rc; rc = skiplist_next(sl, NULL, (void **)&m, - (void **)&cursor)) { + &cursor)) { if (skiplist_search(nves_seen, m->rfd, NULL)) { /* @@ -931,17 +934,14 @@ void rfapiMonitorItNodeChanged( assert(!skiplist_insert(nves_seen, m->rfd, NULL)); - char buf_attach_pfx[PREFIX_STRLEN]; char buf_target_pfx[PREFIX_STRLEN]; - prefix2str(&m->node->p, buf_attach_pfx, - sizeof(buf_attach_pfx)); prefix2str(&m->p, buf_target_pfx, sizeof(buf_target_pfx)); vnc_zlog_debug_verbose( - "%s: update rfd %p attached to pfx %s (targ=%s)", - __func__, m->rfd, - buf_attach_pfx, buf_target_pfx); + "%s: update rfd %p attached to pfx %pRN (targ=%s)", + __func__, m->rfd, m->node, + buf_target_pfx); /* * update its RIB @@ -1103,10 +1103,10 @@ static void rfapiMonitorEthTimerRestart(struct rfapi_monitor_eth *m) m->rfd->response_lifetime, &m->timer); } -static int mon_eth_cmp(void *a, void *b) +static int mon_eth_cmp(const void *a, const void *b) { - struct rfapi_monitor_eth *m1; - struct rfapi_monitor_eth *m2; + const struct rfapi_monitor_eth *m1; + const struct rfapi_monitor_eth *m2; int i; @@ -1272,7 +1272,7 @@ static void rfapiMonitorEthDetachImport( #if DEBUG_L2_EXTRA char buf_prefix[PREFIX_STRLEN]; - prefix2str(&rn->p, buf_prefix, sizeof(buf_prefix)); + prefix2str(agg_node_get_prefix(rn), buf_prefix, sizeof(buf_prefix)); #endif /* diff --git a/bgpd/rfapi/rfapi_monitor.h b/bgpd/rfapi/rfapi_monitor.h index b8eec56475..3a2248aa60 100644 --- a/bgpd/rfapi/rfapi_monitor.h +++ b/bgpd/rfapi/rfapi_monitor.h @@ -167,7 +167,7 @@ extern void rfapiMonitorResponseRemovalOn(struct bgp *bgp); extern void rfapiMonitorExtraPrune(safi_t safi, struct agg_node *rn); extern void rfapiMonitorTimersRestart(struct rfapi_descriptor *rfd, - struct prefix *p); + const struct prefix *p); extern void rfapiMonitorItNodeChanged(struct rfapi_import_table *import_table, struct agg_node *it_node, diff --git a/bgpd/rfapi/rfapi_nve_addr.c b/bgpd/rfapi/rfapi_nve_addr.c index ee54d88c3f..b8193f1431 100644 --- a/bgpd/rfapi/rfapi_nve_addr.c +++ b/bgpd/rfapi/rfapi_nve_addr.c @@ -58,10 +58,10 @@ static void logdifferent(const char *tag, struct rfapi_nve_addr *a, #endif -int rfapi_nve_addr_cmp(void *k1, void *k2) +int rfapi_nve_addr_cmp(const void *k1, const void *k2) { - struct rfapi_nve_addr *a = (struct rfapi_nve_addr *)k1; - struct rfapi_nve_addr *b = (struct rfapi_nve_addr *)k2; + const struct rfapi_nve_addr *a = (struct rfapi_nve_addr *)k1; + const struct rfapi_nve_addr *b = (struct rfapi_nve_addr *)k2; int ret = 0; if (!a || !b) { diff --git a/bgpd/rfapi/rfapi_nve_addr.h b/bgpd/rfapi/rfapi_nve_addr.h index 2d54d4a3cc..7bcb3cab69 100644 --- a/bgpd/rfapi/rfapi_nve_addr.h +++ b/bgpd/rfapi/rfapi_nve_addr.h @@ -30,7 +30,7 @@ struct rfapi_nve_addr { }; -extern int rfapi_nve_addr_cmp(void *k1, void *k2); +extern int rfapi_nve_addr_cmp(const void *k1, const void *k2); extern void rfapiNveAddr2Str(struct rfapi_nve_addr *na, char *buf, int bufsize); diff --git a/bgpd/rfapi/rfapi_private.h b/bgpd/rfapi/rfapi_private.h index 87d9a32f67..68caba600a 100644 --- a/bgpd/rfapi/rfapi_private.h +++ b/bgpd/rfapi/rfapi_private.h @@ -272,16 +272,13 @@ struct rfapi { ? ((prefix)->prefixlen == 128) \ : 0)) -extern void rfapiQprefix2Rprefix(struct prefix *qprefix, - struct rfapi_ip_prefix *rprefix); - extern int rfapi_find_rfd(struct bgp *bgp, struct rfapi_ip_addr *vn_addr, struct rfapi_ip_addr *un_addr, struct rfapi_descriptor **rfd); extern void add_vnc_route(struct rfapi_descriptor *rfd, /* cookie + UN addr for VPN */ - struct bgp *bgp, int safi, struct prefix *p, + struct bgp *bgp, int safi, const struct prefix *p, struct prefix_rd *prd, struct rfapi_ip_addr *nexthop, uint32_t *local_pref, /* host byte order */ uint32_t *lifetime, /* host byte order */ @@ -297,7 +294,7 @@ add_vnc_route(struct rfapi_descriptor *rfd, /* cookie + UN addr for VPN */ #endif extern void del_vnc_route(struct rfapi_descriptor *rfd, struct peer *peer, - struct bgp *bgp, safi_t safi, struct prefix *p, + struct bgp *bgp, safi_t safi, const struct prefix *p, struct prefix_rd *prd, uint8_t type, uint8_t sub_type, struct rfapi_nexthop *lnh, int kill); @@ -306,8 +303,6 @@ extern int rfapiCliGetPrefixAddr(struct vty *vty, const char *str, extern int rfapiGetVncLifetime(struct attr *attr, uint32_t *lifetime); -extern int rfapiGetTunnelType(struct attr *attr, bgp_encap_types *type); - extern int rfapiGetVncTunnelUnAddr(struct attr *attr, struct prefix *p); extern int rfapi_reopen(struct rfapi_descriptor *rfd, struct bgp *bgp); diff --git a/bgpd/rfapi/rfapi_rib.c b/bgpd/rfapi/rfapi_rib.c index 8e8acbfb91..e068eb7af6 100644 --- a/bgpd/rfapi/rfapi_rib.c +++ b/bgpd/rfapi/rfapi_rib.c @@ -268,8 +268,8 @@ static void rfapi_info_free(struct rfapi_info *goner) if (goner->timer) { struct rfapi_rib_tcb *tcb; - tcb = ((struct thread *)goner->timer)->arg; - thread_cancel((struct thread *)goner->timer); + tcb = goner->timer->arg; + thread_cancel(goner->timer); XFREE(MTYPE_RFAPI_RECENT_DELETE, tcb); goner->timer = NULL; } @@ -340,7 +340,6 @@ static void rfapiRibStartTimer(struct rfapi_descriptor *rfd, { struct thread *t = ri->timer; struct rfapi_rib_tcb *tcb = NULL; - char buf_prefix[PREFIX_STRLEN]; if (t) { tcb = t->arg; @@ -361,9 +360,8 @@ static void rfapiRibStartTimer(struct rfapi_descriptor *rfd, UNSET_FLAG(tcb->flags, RFAPI_RIB_TCB_FLAG_DELETED); } - prefix2str(&rn->p, buf_prefix, sizeof(buf_prefix)); - vnc_zlog_debug_verbose("%s: rfd %p pfx %s life %u", __func__, rfd, - buf_prefix, ri->lifetime); + vnc_zlog_debug_verbose("%s: rfd %p pfx %pRN life %u", __func__, rfd, rn, + ri->lifetime); ri->timer = NULL; thread_add_timer(bm->master, rfapiRibExpireTimer, tcb, ri->lifetime, &ri->timer); @@ -388,10 +386,10 @@ extern void rfapi_rib_key_init(struct prefix *prefix, /* may be NULL */ /* * Compares two s */ -int rfapi_rib_key_cmp(void *k1, void *k2) +int rfapi_rib_key_cmp(const void *k1, const void *k2) { - struct rfapi_rib_key *a = (struct rfapi_rib_key *)k1; - struct rfapi_rib_key *b = (struct rfapi_rib_key *)k2; + const struct rfapi_rib_key *a = (struct rfapi_rib_key *)k1; + const struct rfapi_rib_key *b = (struct rfapi_rib_key *)k2; int ret; if (!a || !b) @@ -479,7 +477,7 @@ void rfapiRibClear(struct rfapi_descriptor *rfd) bgp = rfd->bgp; else bgp = bgp_get_default(); -#if DEBUG_L2_EXTRA +#ifdef DEBUG_L2_EXTRA vnc_zlog_debug_verbose("%s: rfd=%p", __func__, rfd); #endif @@ -648,9 +646,7 @@ static void rfapiRibBi2Ri(struct bgp_path_info *bpi, struct rfapi_info *ri, pEncap->length - 2); if (hop->length > pEncap->length - 2) { zlog_warn( - "%s: VNC subtlv length mismatch: " - "RFP option says %d, attr says %d " - "(shrinking)", + "%s: VNC subtlv length mismatch: RFP option says %d, attr says %d (shrinking)", __func__, hop->length, pEncap->length - 2); hop->length = pEncap->length - 2; @@ -741,11 +737,12 @@ int rfapiRibPreloadBi( struct rfapi_rib_key rk; struct agg_node *trn; afi_t afi; + const struct prefix *p = agg_node_get_prefix(rfd_rib_node); if (!rfd_rib_node) return 0; - afi = family2afi(rfd_rib_node->p.family); + afi = family2afi(p->family); rfd = agg_get_table_info(agg_get_table(rfd_rib_node)); @@ -803,8 +800,7 @@ int rfapiRibPreloadBi( /* * Update last sent time for prefix */ - trn = agg_node_get(rfd->rsp_times[afi], - &rfd_rib_node->p); /* locks trn */ + trn = agg_node_get(rfd->rsp_times[afi], p); /* locks trn */ trn->info = (void *)(uintptr_t)bgp_clock(); if (trn->lock > 1) agg_unlock_node(trn); @@ -852,10 +848,9 @@ static void process_pending_node(struct bgp *bgp, struct rfapi_descriptor *rfd, struct list *lPendCost = NULL; struct list *delete_list = NULL; int printedprefix = 0; - char buf_prefix[PREFIX_STRLEN]; int rib_node_started_nonempty = 0; int sendingsomeroutes = 0; - + const struct prefix *p; #if DEBUG_PROCESS_PENDING_NODE unsigned int count_rib_initial = 0; unsigned int count_pend_vn_initial = 0; @@ -863,12 +858,12 @@ static void process_pending_node(struct bgp *bgp, struct rfapi_descriptor *rfd, #endif assert(pn); - prefix2str(&pn->p, buf_prefix, sizeof(buf_prefix)); - vnc_zlog_debug_verbose("%s: afi=%d, %s pn->info=%p", __func__, afi, - buf_prefix, pn->info); + p = agg_node_get_prefix(pn); + vnc_zlog_debug_verbose("%s: afi=%d, %pRN pn->info=%p", __func__, afi, + pn, pn->info); if (AFI_L2VPN != afi) { - rfapiQprefix2Rprefix(&pn->p, &hp); + rfapiQprefix2Rprefix(p, &hp); } RFAPI_RIB_CHECK_COUNTS(1, 0); @@ -876,7 +871,7 @@ static void process_pending_node(struct bgp *bgp, struct rfapi_descriptor *rfd, /* * Find corresponding RIB node */ - rn = agg_node_get(rfd->rib[afi], &pn->p); /* locks rn */ + rn = agg_node_get(rfd->rib[afi], p); /* locks rn */ /* * RIB skiplist has key=rfapi_addr={vn,un}, val = rfapi_info, @@ -935,9 +930,9 @@ static void process_pending_node(struct bgp *bgp, struct rfapi_descriptor *rfd, prefix2str(&ri->rk.vn, buf, sizeof(buf)); prefix2str(&ri->un, buf2, sizeof(buf2)); vnc_zlog_debug_verbose( - "%s: put dl pfx=%s vn=%s un=%s cost=%d life=%d vn_options=%p", - __func__, buf_prefix, buf, buf2, - ri->cost, ri->lifetime, ri->vn_options); + "%s: put dl pfx=%pRN vn=%s un=%s cost=%d life=%d vn_options=%p", + __func__, pn, buf, buf2, ri->cost, + ri->lifetime, ri->vn_options); skiplist_delete_first(slRibPt); } @@ -1186,8 +1181,7 @@ static void process_pending_node(struct bgp *bgp, struct rfapi_descriptor *rfd, vnc_zlog_debug_verbose("%s: lPendCost->count now %d", __func__, lPendCost->count); - vnc_zlog_debug_verbose("%s: For prefix %s (a)", __func__, - buf_prefix); + vnc_zlog_debug_verbose("%s: For prefix %pRN (a)", __func__, pn); printedprefix = 1; for (ALL_LIST_ELEMENTS(lPendCost, node, nnode, ri)) { @@ -1246,7 +1240,7 @@ static void process_pending_node(struct bgp *bgp, struct rfapi_descriptor *rfd, * update this NVE's timestamp for this prefix */ trn = agg_node_get(rfd->rsp_times[afi], - &pn->p); /* locks trn */ + p); /* locks trn */ trn->info = (void *)(uintptr_t)bgp_clock(); if (trn->lock > 1) agg_unlock_node(trn); @@ -1268,8 +1262,8 @@ static void process_pending_node(struct bgp *bgp, struct rfapi_descriptor *rfd, char buf2[BUFSIZ]; if (!printedprefix) { - vnc_zlog_debug_verbose("%s: For prefix %s (d)", - __func__, buf_prefix); + vnc_zlog_debug_verbose("%s: For prefix %pRN (d)", + __func__, pn); } vnc_zlog_debug_verbose("%s: delete_list has %d elements", __func__, delete_list->count); @@ -1465,7 +1459,7 @@ static void process_pending_node(struct bgp *bgp, struct rfapi_descriptor *rfd, } if (sendingsomeroutes) - rfapiMonitorTimersRestart(rfd, &pn->p); + rfapiMonitorTimersRestart(rfd, p); agg_unlock_node(rn); /* agg_node_get() */ @@ -1487,7 +1481,7 @@ static void rib_do_callback_onepass(struct rfapi_descriptor *rfd, afi_t afi) struct rfapi_next_hop_entry *tail = NULL; struct agg_node *rn; -#if DEBUG_L2_EXTRA +#ifdef DEBUG_L2_EXTRA vnc_zlog_debug_verbose("%s: rfd=%p, afi=%d", __func__, rfd, afi); #endif @@ -1589,7 +1583,7 @@ void rfapiRibUpdatePendingNode( struct rfapi_import_table *it, /* needed for L2 */ struct agg_node *it_node, uint32_t lifetime) { - struct prefix *prefix; + const struct prefix *prefix; struct bgp_path_info *bpi; struct agg_node *pn; afi_t afi; @@ -1606,7 +1600,7 @@ void rfapiRibUpdatePendingNode( RFAPI_RIB_CHECK_COUNTS(1, 0); - prefix = &it_node->p; + prefix = agg_node_get_prefix(it_node); afi = family2afi(prefix->family); prefix2str(prefix, buf, sizeof(buf)); vnc_zlog_debug_verbose("%s: prefix=%s", __func__, buf); @@ -1647,11 +1641,6 @@ void rfapiRibUpdatePendingNode( struct rfapi_info *ri; struct prefix pfx_nh; - if (!bpi->attr) { - /* shouldn't happen */ - /* TBD increment error stats counter */ - continue; - } if (!bpi->extra) { /* shouldn't happen */ /* TBD increment error stats counter */ @@ -1799,7 +1788,8 @@ int rfapiRibFTDFilterRecentPrefix( struct prefix *pfx_target_original) /* query target */ { struct bgp *bgp = rfd->bgp; - afi_t afi = family2afi(it_rn->p.family); + const struct prefix *p = agg_node_get_prefix(it_rn); + afi_t afi = family2afi(p->family); time_t prefix_time; struct agg_node *trn; @@ -1814,14 +1804,15 @@ int rfapiRibFTDFilterRecentPrefix( * This matches behavior of now-obsolete rfapiRibFTDFilterRecent(), * but we need to decide if that is correct. */ - if (it_rn->p.family == AF_ETHERNET) + if (p->family == AF_ETHERNET) return 0; -#if DEBUG_FTD_FILTER_RECENT +#ifdef DEBUG_FTD_FILTER_RECENT { char buf_pfx[PREFIX_STRLEN]; - prefix2str(&it_rn->p, buf_pfx, sizeof(buf_pfx)); + prefix2str(agg_node_get_prefix(it_rn), buf_pfx, + sizeof(buf_pfx)); vnc_zlog_debug_verbose("%s: prefix %s", __func__, buf_pfx); } #endif @@ -1829,8 +1820,8 @@ int rfapiRibFTDFilterRecentPrefix( /* * prefix covers target address, so allow prefix */ - if (prefix_match(&it_rn->p, pfx_target_original)) { -#if DEBUG_FTD_FILTER_RECENT + if (prefix_match(p, pfx_target_original)) { +#ifdef DEBUG_FTD_FILTER_RECENT vnc_zlog_debug_verbose("%s: prefix covers target, allowed", __func__); #endif @@ -1840,12 +1831,12 @@ int rfapiRibFTDFilterRecentPrefix( /* * check this NVE's timestamp for this prefix */ - trn = agg_node_get(rfd->rsp_times[afi], &it_rn->p); /* locks trn */ + trn = agg_node_get(rfd->rsp_times[afi], p); /* locks trn */ prefix_time = (time_t)trn->info; if (trn->lock > 1) agg_unlock_node(trn); -#if DEBUG_FTD_FILTER_RECENT +#ifdef DEBUG_FTD_FILTER_RECENT vnc_zlog_debug_verbose("%s: last sent time %lu, last allowed time %lu", __func__, prefix_time, rfd->ftd_last_allowed_time); @@ -2002,7 +1993,8 @@ rfapiRibPreload(struct bgp *bgp, struct rfapi_descriptor *rfd, } vnc_zlog_debug_verbose( "%s: RIB skiplist for this prefix follows", __func__); - rfapiRibShowRibSl(NULL, &rn->p, (struct skiplist *)rn->info); + rfapiRibShowRibSl(NULL, agg_node_get_prefix(rn), + (struct skiplist *)rn->info); #endif @@ -2119,11 +2111,10 @@ void rfapiRibPendingDeleteRoute(struct bgp *bgp, struct rfapi_import_table *it, { struct rfapi_descriptor *rfd; struct listnode *node; - char buf[PREFIX_STRLEN]; + const struct prefix *p = agg_node_get_prefix(it_node); - prefix2str(&it_node->p, buf, sizeof(buf)); - vnc_zlog_debug_verbose("%s: entry, it=%p, afi=%d, it_node=%p, pfx=%s", - __func__, it, afi, it_node, buf); + vnc_zlog_debug_verbose("%s: entry, it=%p, afi=%d, it_node=%p, pfx=%pRN", + __func__, it, afi, it_node, it_node); if (AFI_L2VPN == afi) { /* @@ -2162,7 +2153,7 @@ void rfapiRibPendingDeleteRoute(struct bgp *bgp, struct rfapi_import_table *it, * delete */ if ((rn = agg_node_lookup(m->rfd->rib[afi], - &it_node->p))) { + p))) { rfapiRibUpdatePendingNode( bgp, m->rfd, it, it_node, m->rfd->response_lifetime); @@ -2184,11 +2175,11 @@ void rfapiRibPendingDeleteRoute(struct bgp *bgp, struct rfapi_import_table *it, * this * NVE, it's OK to send an update with the delete */ - if ((rn = agg_node_lookup(m->rfd->rib[afi], - &it_node->p))) { + if ((rn = agg_node_lookup(m->rfd->rib[afi], p))) { rfapiRibUpdatePendingNode( bgp, m->rfd, it, it_node, m->rfd->response_lifetime); + agg_unlock_node(rn); } } @@ -2216,8 +2207,7 @@ void rfapiRibPendingDeleteRoute(struct bgp *bgp, struct rfapi_import_table *it, * prefix * previously, we should send an updated response. */ - if ((rn = agg_node_lookup(rfd->rib[afi], - &it_node->p))) { + if ((rn = agg_node_lookup(rfd->rib[afi], p))) { rfapiRibUpdatePendingNode( bgp, rfd, it, it_node, rfd->response_lifetime); @@ -2315,7 +2305,7 @@ static int print_rib_sl(int (*fp)(void *, const char *, ...), struct vty *vty, *p = 0; rfapiFormatSeconds(ri->lifetime, str_lifetime, BUFSIZ); -#if RFAPI_REGISTRATIONS_REPORT_AGE +#ifdef RFAPI_REGISTRATIONS_REPORT_AGE rfapiFormatAge(ri->last_sent_time, str_age, BUFSIZ); #else { @@ -2420,7 +2410,8 @@ void rfapiRibShowResponses(void *stream, struct prefix *pfx_match, for (rn = agg_route_top(rfd->rib[afi]); rn; rn = agg_route_next(rn)) { - + const struct prefix *p = + agg_node_get_prefix(rn); struct skiplist *sl; char str_pfx[PREFIX_STRLEN]; int printedprefix = 0; @@ -2437,9 +2428,8 @@ void rfapiRibShowResponses(void *stream, struct prefix *pfx_match, nhs_total += skiplist_count(sl); ++prefixes_total; - if (pfx_match - && !prefix_match(pfx_match, &rn->p) - && !prefix_match(&rn->p, pfx_match)) + if (pfx_match && !prefix_match(pfx_match, p) + && !prefix_match(p, pfx_match)) continue; ++prefixes_displayed; @@ -2455,12 +2445,12 @@ void rfapiRibShowResponses(void *stream, struct prefix *pfx_match, " %-20s %-15s %-15s %4s %-8s %-8s\n", "Prefix", "Registered VN", "Registered UN", "Cost", "Lifetime", -#if RFAPI_REGISTRATIONS_REPORT_AGE +#ifdef RFAPI_REGISTRATIONS_REPORT_AGE "Age" #else "Remaining" #endif - ); + ); } if (!printednve) { char str_vn[BUFSIZ]; @@ -2476,7 +2466,7 @@ void rfapiRibShowResponses(void *stream, struct prefix *pfx_match, str_un, BUFSIZ)); } - prefix2str(&rn->p, str_pfx, sizeof(str_pfx)); + prefix2str(p, str_pfx, sizeof(str_pfx)); // fp(out, " %s\n", buf); /* prefix */ routes_displayed++; diff --git a/bgpd/rfapi/rfapi_rib.h b/bgpd/rfapi/rfapi_rib.h index 38a6df9fbf..3ad021b4f4 100644 --- a/bgpd/rfapi/rfapi_rib.h +++ b/bgpd/rfapi/rfapi_rib.h @@ -147,7 +147,7 @@ extern void rfapi_rib_key_init(struct prefix *prefix, /* may be NULL */ struct prefix *aux, /* may be NULL */ struct rfapi_rib_key *rk); -extern int rfapi_rib_key_cmp(void *k1, void *k2); +extern int rfapi_rib_key_cmp(const void *k1, const void *k2); extern void rfapiAdbFree(struct rfapi_adb *adb); diff --git a/bgpd/rfapi/rfapi_vty.c b/bgpd/rfapi/rfapi_vty.c index ea82c254bc..d74404ea56 100644 --- a/bgpd/rfapi/rfapi_vty.c +++ b/bgpd/rfapi/rfapi_vty.c @@ -25,7 +25,6 @@ #include "lib/memory.h" #include "lib/routemap.h" #include "lib/log.h" -#include "lib/log_int.h" #include "lib/linklist.h" #include "lib/command.h" @@ -127,8 +126,8 @@ void rfapiRprefixApplyMask(struct rfapi_ip_prefix *rprefix) int index; int offset; - static uint8_t maskbit[] = {0x00, 0x80, 0xc0, 0xe0, 0xf0, - 0xf8, 0xfc, 0xfe, 0xff}; + static const uint8_t maskbit[] = {0x00, 0x80, 0xc0, 0xe0, 0xf0, + 0xf8, 0xfc, 0xfe, 0xff}; switch (rprefix->prefix.addr_family) { case AF_INET: @@ -194,7 +193,7 @@ int rfapiQprefix2Raddr(struct prefix *qprefix, struct rfapi_ip_addr *raddr) * Translate Quagga prefix to RFAPI prefix */ /* rprefix->cost set to 0 */ -void rfapiQprefix2Rprefix(struct prefix *qprefix, +void rfapiQprefix2Rprefix(const struct prefix *qprefix, struct rfapi_ip_prefix *rprefix) { memset(rprefix, 0, sizeof(struct rfapi_ip_prefix)); @@ -371,7 +370,7 @@ int rfapiStream2Vty(void *stream, /* input */ *fp = (int (*)(void *, const char *, ...))rfapiDebugPrintf; *outstream = NULL; *vty_newline = str_vty_newline(*vty); - return (vzlog_test(LOG_DEBUG)); + return 1; } if (((uintptr_t)stream == (uintptr_t)1) @@ -393,7 +392,7 @@ int rfapiStream2Vty(void *stream, /* input */ } /* called from bgpd/bgp_vty.c'route_vty_out() */ -void rfapi_vty_out_vncinfo(struct vty *vty, struct prefix *p, +void rfapi_vty_out_vncinfo(struct vty *vty, const struct prefix *p, struct bgp_path_info *bpi, safi_t safi) { char *s; @@ -418,7 +417,7 @@ void rfapi_vty_out_vncinfo(struct vty *vty, struct prefix *p, } } - if (bpi->attr && bpi->attr->ecommunity) { + if (bpi->attr->ecommunity) { s = ecommunity_ecom2str(bpi->attr->ecommunity, ECOMMUNITY_FORMAT_ROUTE_MAP, 0); vty_out(vty, " EC{%s}", s); @@ -431,6 +430,14 @@ void rfapi_vty_out_vncinfo(struct vty *vty, struct prefix *p, else vty_out(vty, " label=%u", decode_label(&bpi->extra->label[0])); + + if (bpi->extra->num_sids) { + char buf[BUFSIZ]; + + vty_out(vty, " sid=%s", + inet_ntop(AF_INET6, &bpi->extra->sid[0], buf, + sizeof(buf))); + } } if (!rfapiGetVncLifetime(bpi->attr, &lifetime)) { @@ -538,82 +545,78 @@ void rfapiPrintBi(void *stream, struct bgp_path_info *bpi) * RFP option sizes (they are opaque values) * extended communities (RTs) */ - if (bpi->attr) { - uint32_t lifetime; - int printed_1st_gol = 0; - struct bgp_attr_encap_subtlv *pEncap; - struct prefix pfx_un; - int af = BGP_MP_NEXTHOP_FAMILY(bpi->attr->mp_nexthop_len); - - /* Nexthop */ - if (af == AF_INET) { - r = snprintf(p, REMAIN, "%s", - inet_ntop(AF_INET, - &bpi->attr->mp_nexthop_global_in, - buf, BUFSIZ)); - INCP; - } else if (af == AF_INET6) { - r = snprintf(p, REMAIN, "%s", - inet_ntop(AF_INET6, - &bpi->attr->mp_nexthop_global, - buf, BUFSIZ)); - INCP; - } else { - r = snprintf(p, REMAIN, "?"); - INCP; - } + uint32_t lifetime; + int printed_1st_gol = 0; + struct bgp_attr_encap_subtlv *pEncap; + struct prefix pfx_un; + int af = BGP_MP_NEXTHOP_FAMILY(bpi->attr->mp_nexthop_len); + + /* Nexthop */ + if (af == AF_INET) { + r = snprintf(p, REMAIN, "%s", + inet_ntop(AF_INET, + &bpi->attr->mp_nexthop_global_in, buf, + BUFSIZ)); + INCP; + } else if (af == AF_INET6) { + r = snprintf(p, REMAIN, "%s", + inet_ntop(AF_INET6, &bpi->attr->mp_nexthop_global, + buf, BUFSIZ)); + INCP; + } else { + r = snprintf(p, REMAIN, "?"); + INCP; + } - /* - * VNC tunnel subtlv, if present, contains UN address - */ - if (!rfapiGetVncTunnelUnAddr(bpi->attr, &pfx_un)) { - r = snprintf(p, REMAIN, " un=%s", - inet_ntop(pfx_un.family, pfx_un.u.val, buf, - BUFSIZ)); - INCP; - } + /* + * VNC tunnel subtlv, if present, contains UN address + */ + if (!rfapiGetVncTunnelUnAddr(bpi->attr, &pfx_un)) { + r = snprintf( + p, REMAIN, " un=%s", + inet_ntop(pfx_un.family, pfx_un.u.val, buf, BUFSIZ)); + INCP; + } - /* Lifetime */ - if (rfapiGetVncLifetime(bpi->attr, &lifetime)) { - r = snprintf(p, REMAIN, " nolife"); - INCP; - } else { - if (lifetime == 0xffffffff) - r = snprintf(p, REMAIN, " %6s", "infini"); - else - r = snprintf(p, REMAIN, " %6u", lifetime); - INCP; - } + /* Lifetime */ + if (rfapiGetVncLifetime(bpi->attr, &lifetime)) { + r = snprintf(p, REMAIN, " nolife"); + INCP; + } else { + if (lifetime == 0xffffffff) + r = snprintf(p, REMAIN, " %6s", "infini"); + else + r = snprintf(p, REMAIN, " %6u", lifetime); + INCP; + } - /* RFP option lengths */ - for (pEncap = bpi->attr->vnc_subtlvs; pEncap; - pEncap = pEncap->next) { + /* RFP option lengths */ + for (pEncap = bpi->attr->vnc_subtlvs; pEncap; pEncap = pEncap->next) { - if (pEncap->type == BGP_VNC_SUBTLV_TYPE_RFPOPTION) { - if (printed_1st_gol) { - r = snprintf(p, REMAIN, ","); - INCP; - } else { - r = snprintf(p, REMAIN, - " "); /* leading space */ - INCP; - } - r = snprintf(p, REMAIN, "%d", pEncap->length); + if (pEncap->type == BGP_VNC_SUBTLV_TYPE_RFPOPTION) { + if (printed_1st_gol) { + r = snprintf(p, REMAIN, ","); + INCP; + } else { + r = snprintf(p, REMAIN, + " "); /* leading space */ INCP; - printed_1st_gol = 1; } - } - - /* RT list */ - if (bpi->attr->ecommunity) { - s = ecommunity_ecom2str(bpi->attr->ecommunity, - ECOMMUNITY_FORMAT_ROUTE_MAP, 0); - r = snprintf(p, REMAIN, " %s", s); + r = snprintf(p, REMAIN, "%d", pEncap->length); INCP; - XFREE(MTYPE_ECOMMUNITY_STR, s); + printed_1st_gol = 1; } } + /* RT list */ + if (bpi->attr->ecommunity) { + s = ecommunity_ecom2str(bpi->attr->ecommunity, + ECOMMUNITY_FORMAT_ROUTE_MAP, 0); + r = snprintf(p, REMAIN, " %s", s); + INCP; + XFREE(MTYPE_ECOMMUNITY_STR, s); + } + r = snprintf(p, REMAIN, " bpi@%p", bpi); INCP; @@ -628,21 +631,17 @@ void rfapiPrintBi(void *stream, struct bgp_path_info *bpi) INCP; } - if (bpi->attr) { - - if (bpi->attr->weight) { - r = snprintf(p, REMAIN, " W=%d", bpi->attr->weight); - INCP; - } + if (bpi->attr->weight) { + r = snprintf(p, REMAIN, " W=%d", bpi->attr->weight); + INCP; + } - if (bpi->attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF)) { - r = snprintf(p, REMAIN, " LP=%d", - bpi->attr->local_pref); - INCP; - } else { - r = snprintf(p, REMAIN, " LP=unset"); - INCP; - } + if (bpi->attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF)) { + r = snprintf(p, REMAIN, " LP=%d", bpi->attr->local_pref); + INCP; + } else { + r = snprintf(p, REMAIN, " LP=unset"); + INCP; } r = snprintf(p, REMAIN, " %c:%u", zebra_route_char(bpi->type), @@ -743,7 +742,6 @@ static void rfapiDebugPrintMonitorEncap(void *stream, void rfapiShowItNode(void *stream, struct agg_node *rn) { struct bgp_path_info *bpi; - char buf[BUFSIZ]; int (*fp)(void *, const char *, ...); struct vty *vty; @@ -753,9 +751,7 @@ void rfapiShowItNode(void *stream, struct agg_node *rn) if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0) return; - fp(out, "%s/%d @%p #%d%s", - rfapi_ntop(rn->p.family, &rn->p.u.prefix, buf, BUFSIZ), - rn->p.prefixlen, rn, rn->lock, HVTYNL); + fp(out, "%pRN @%p #%d%s", rn, rn, rn->lock, HVTYNL); for (bpi = rn->info; bpi; bpi = bpi->next) { rfapiPrintBi(stream, bpi); @@ -782,14 +778,15 @@ void rfapiShowImportTable(void *stream, const char *label, struct agg_table *rt, for (rn = agg_route_top(rt); rn; rn = agg_route_next(rn)) { struct bgp_path_info *bpi; + const struct prefix *p = agg_node_get_prefix(rn); - if (rn->p.family == AF_ETHERNET) { - rfapiEthAddr2Str(&rn->p.u.prefix_eth, buf, BUFSIZ); + if (p->family == AF_ETHERNET) { + rfapiEthAddr2Str(&p->u.prefix_eth, buf, BUFSIZ); } else { - inet_ntop(rn->p.family, &rn->p.u.prefix, buf, BUFSIZ); + inet_ntop(p->family, &p->u.prefix, buf, BUFSIZ); } - fp(out, "%s/%d @%p #%d%s", buf, rn->p.prefixlen, rn, + fp(out, "%s/%d @%p #%d%s", buf, p->prefixlen, rn, rn->lock - 1, /* account for loop iterator locking */ HVTYNL); @@ -868,6 +865,8 @@ int rfapiShowVncQueries(void *stream, struct prefix *pfx_match) if (rfd->mon) { for (rn = agg_route_top(rfd->mon); rn; rn = agg_route_next(rn)) { + const struct prefix *p = + agg_node_get_prefix(rn); struct rfapi_monitor_vpn *m; char buf_remain[BUFSIZ]; char buf_pfx[BUFSIZ]; @@ -879,9 +878,8 @@ int rfapiShowVncQueries(void *stream, struct prefix *pfx_match) ++queries_total; - if (pfx_match - && !prefix_match(pfx_match, &rn->p) - && !prefix_match(&rn->p, pfx_match)) + if (pfx_match && !prefix_match(pfx_match, p) + && !prefix_match(p, pfx_match)) continue; ++queries_displayed; @@ -1020,7 +1018,7 @@ static int rfapiPrintRemoteRegBi(struct bgp *bgp, void *stream, struct prefix pfx_vn; uint8_t cost; uint32_t lifetime; - bgp_encap_types tun_type; + bgp_encap_types tun_type = BGP_ENCAP_TYPE_MPLS;/*Default tunnel type*/ char buf_pfx[BUFSIZ]; char buf_ntop[BUFSIZ]; @@ -1028,6 +1026,7 @@ static int rfapiPrintRemoteRegBi(struct bgp *bgp, void *stream, char buf_vn[BUFSIZ]; char buf_lifetime[BUFSIZ]; int nlines = 0; + const struct prefix *p = agg_node_get_prefix(rn); if (!stream) return 0; /* for debug log, print into buf & call output once */ @@ -1039,9 +1038,9 @@ static int rfapiPrintRemoteRegBi(struct bgp *bgp, void *stream, * Prefix */ buf_pfx[0] = 0; - snprintf(buf_pfx, BUFSIZ, "%s/%d", - rfapi_ntop(rn->p.family, &rn->p.u.prefix, buf_ntop, BUFSIZ), - rn->p.prefixlen); + snprintf(buf_pfx, sizeof(buf_pfx), "%s/%d", + rfapi_ntop(p->family, &p->u.prefix, buf_ntop, BUFSIZ), + p->prefixlen); buf_pfx[BUFSIZ - 1] = 0; nlines++; @@ -1050,12 +1049,12 @@ static int rfapiPrintRemoteRegBi(struct bgp *bgp, void *stream, */ buf_un[0] = 0; if (!rfapiGetUnAddrOfVpnBi(bpi, &pfx_un)) { - snprintf(buf_un, BUFSIZ, "%s", + snprintf(buf_un, sizeof(buf_un), "%s", inet_ntop(pfx_un.family, &pfx_un.u.prefix, buf_ntop, BUFSIZ)); } - rfapiGetTunnelType(bpi->attr, &tun_type); + bgp_attr_extcom_tunnel_type(bpi->attr, &tun_type); /* * VN addr */ @@ -1064,18 +1063,18 @@ static int rfapiPrintRemoteRegBi(struct bgp *bgp, void *stream, if (tun_type == BGP_ENCAP_TYPE_MPLS) { /* MPLS carries un in nrli next hop (same as vn for IP tunnels) */ - snprintf(buf_un, BUFSIZ, "%s", + snprintf(buf_un, sizeof(buf_un), "%s", inet_ntop(pfx_vn.family, &pfx_vn.u.prefix, buf_ntop, BUFSIZ)); if (bpi->extra) { uint32_t l = decode_label(&bpi->extra->label[0]); - snprintf(buf_vn, BUFSIZ, "Label: %d", l); + snprintf(buf_vn, sizeof(buf_vn), "Label: %d", l); } else /* should never happen */ { - snprintf(buf_vn, BUFSIZ, "Label: N/A"); + snprintf(buf_vn, sizeof(buf_vn), "Label: N/A"); } } else { - snprintf(buf_vn, BUFSIZ, "%s", + snprintf(buf_vn, sizeof(buf_vn), "%s", inet_ntop(pfx_vn.family, &pfx_vn.u.prefix, buf_ntop, BUFSIZ)); } @@ -1087,16 +1086,13 @@ static int rfapiPrintRemoteRegBi(struct bgp *bgp, void *stream, * See rfapi_import.c'rfapiRouteInfo2NextHopEntry() for conversion * back to cost. */ - if (bpi->attr) { - uint32_t local_pref; - if (bpi->attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF)) - local_pref = bpi->attr->local_pref; - else - local_pref = 0; - cost = (local_pref > 255) ? 0 : 255 - local_pref; - } else { - cost = 0; - } + uint32_t local_pref; + + if (bpi->attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF)) + local_pref = bpi->attr->local_pref; + else + local_pref = 0; + cost = (local_pref > 255) ? 0 : 255 - local_pref; fp(out, "%-20s ", buf_pfx); fp(out, "%-15s ", buf_vn); @@ -1126,7 +1122,7 @@ static int rfapiPrintRemoteRegBi(struct bgp *bgp, void *stream, (struct thread *)bpi->extra->vnc.import.timer; remaining = thread_timer_remain_second(t); -#if RFAPI_REGISTRATIONS_REPORT_AGE +#ifdef RFAPI_REGISTRATIONS_REPORT_AGE /* * Calculate when the timer started. Doing so here saves * us a timestamp field in "struct bgp_path_info". @@ -1158,7 +1154,7 @@ static int rfapiPrintRemoteRegBi(struct bgp *bgp, void *stream, } fp(out, "%s", HVTYNL); - if (rn->p.family == AF_ETHERNET) { + if (p->family == AF_ETHERNET) { /* * If there is a corresponding IP address && != VN address, * print that on the next line @@ -1224,13 +1220,13 @@ static int rfapiShowRemoteRegistrationsIt(struct bgp *bgp, void *stream, for (rn = agg_route_top(it->imported_vpn[afi]); rn; rn = agg_route_next(rn)) { - + const struct prefix *p = agg_node_get_prefix(rn); struct bgp_path_info *bpi; int count_only; /* allow for wider or more narrow mask from user */ - if (prefix_only && !prefix_match(prefix_only, &rn->p) - && !prefix_match(&rn->p, prefix_only)) + if (prefix_only && !prefix_match(prefix_only, p) + && !prefix_match(p, prefix_only)) count_only = 1; else count_only = 0; @@ -1314,7 +1310,7 @@ static int rfapiShowRemoteRegistrationsIt(struct bgp *bgp, void *stream, } fp(out, "%s", HVTYNL); if (show_expiring) { -#if RFAPI_REGISTRATIONS_REPORT_AGE +#ifdef RFAPI_REGISTRATIONS_REPORT_AGE agetype = "Age"; #else agetype = "Remaining"; @@ -1539,7 +1535,7 @@ void rfapiPrintAdvertisedInfo(struct vty *vty, struct rfapi_descriptor *rfd, safi_t safi, struct prefix *p) { afi_t afi; /* of the VN address */ - struct bgp_node *bn; + struct bgp_dest *bd; struct bgp_path_info *bpi; uint8_t type = ZEBRA_ROUTE_BGP; struct bgp *bgp; @@ -1566,11 +1562,11 @@ void rfapiPrintAdvertisedInfo(struct vty *vty, struct rfapi_descriptor *rfd, } else { prd = &rfd->rd; } - bn = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi, p, prd); + bd = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi, p, prd); - vty_out(vty, " bn=%p%s", bn, HVTYNL); + vty_out(vty, " bd=%p%s", bd, HVTYNL); - for (bpi = bgp_node_get_bgp_path_info(bn); bpi; bpi = bpi->next) { + for (bpi = bgp_dest_get_bgp_path_info(bd); bpi; bpi = bpi->next) { if (bpi->peer == rfd->peer && bpi->type == type && bpi->sub_type == BGP_ROUTE_RFP && bpi->extra && bpi->extra->vnc.export.rfapi_handle == (void *)rfd) { @@ -2757,10 +2753,10 @@ static void nve_addr_free(void *hap) XFREE(MTYPE_RFAPI_NVE_ADDR, hap); } -static int nve_addr_cmp(void *k1, void *k2) +static int nve_addr_cmp(const void *k1, const void *k2) { - struct nve_addr *a = (struct nve_addr *)k1; - struct nve_addr *b = (struct nve_addr *)k2; + const struct nve_addr *a = (struct nve_addr *)k1; + const struct nve_addr *b = (struct nve_addr *)k2; int ret = 0; if (!a || !b) { @@ -3431,7 +3427,7 @@ static void clear_vnc_nve_closer(struct rfapi_local_reg_delete_arg *cda) &cursor)) { if (pValue->rfd) { - ((struct rfapi_descriptor *)pValue->rfd)->flags |= + pValue->rfd->flags |= RFAPI_HD_FLAG_CLOSING_ADMINISTRATIVELY; rfapi_close(pValue->rfd); } @@ -4719,8 +4715,6 @@ static int vnc_add_vrf_prefix(struct vty *vty, const char *arg_vrf, rfapiQprefix2Rprefix(&pfx, &rpfx); memset(optary, 0, sizeof(optary)); if (arg_rd) { - if (opt != NULL) - opt->next = &optary[cur_opt]; opt = &optary[cur_opt++]; opt->type = RFAPI_VN_OPTION_TYPE_INTERNAL_RD; if (!str2prefix_rd(arg_rd, &opt->v.internal_rd)) { diff --git a/bgpd/rfapi/rfapi_vty.h b/bgpd/rfapi/rfapi_vty.h index 8b881292ac..b5e1c38b0d 100644 --- a/bgpd/rfapi/rfapi_vty.h +++ b/bgpd/rfapi/rfapi_vty.h @@ -43,7 +43,7 @@ extern void rfapiRprefixApplyMask(struct rfapi_ip_prefix *rprefix); extern int rfapiQprefix2Raddr(struct prefix *qprefix, struct rfapi_ip_addr *raddr); -extern void rfapiQprefix2Rprefix(struct prefix *qprefix, +extern void rfapiQprefix2Rprefix(const struct prefix *qprefix, struct rfapi_ip_prefix *rprefix); extern int rfapiRprefix2Qprefix(struct rfapi_ip_prefix *rprefix, diff --git a/bgpd/rfapi/vnc_debug.c b/bgpd/rfapi/vnc_debug.c index 7cac7ac7a1..5c627efbee 100644 --- a/bgpd/rfapi/vnc_debug.c +++ b/bgpd/rfapi/vnc_debug.c @@ -38,7 +38,7 @@ struct vnc_debug { const char *name; }; -struct vnc_debug vncdebug[] = { +static const struct vnc_debug vncdebug[] = { {VNC_DEBUG_RFAPI_QUERY, "rfapi-query"}, {VNC_DEBUG_IMPORT_BI_ATTACH, "import-bi-attach"}, {VNC_DEBUG_IMPORT_DEL_REMOTE, "import-del-remote"}, @@ -173,11 +173,17 @@ static int bgp_vnc_config_write_debug(struct vty *vty) return write; } -static struct cmd_node debug_node = {DEBUG_VNC_NODE, "", 1}; +static int bgp_vnc_config_write_debug(struct vty *vty); +static struct cmd_node debug_node = { + .name = "vnc debug", + .node = DEBUG_VNC_NODE, + .prompt = "", + .config_write = bgp_vnc_config_write_debug, +}; void vnc_debug_init(void) { - install_node(&debug_node, bgp_vnc_config_write_debug); + install_node(&debug_node); install_element(ENABLE_NODE, &show_debugging_bgp_vnc_cmd); install_element(ENABLE_NODE, &debug_bgp_vnc_cmd); diff --git a/bgpd/rfapi/vnc_debug.h b/bgpd/rfapi/vnc_debug.h index dd49383072..c472b6366e 100644 --- a/bgpd/rfapi/vnc_debug.h +++ b/bgpd/rfapi/vnc_debug.h @@ -20,7 +20,7 @@ #ifndef _QUAGGA_BGP_VNC_DEBUG_H #define _QUAGGA_BGP_VNC_DEBUG_H -#if ENABLE_BGP_VNC +#ifdef ENABLE_BGP_VNC /* * debug state storage diff --git a/bgpd/rfapi/vnc_export_bgp.c b/bgpd/rfapi/vnc_export_bgp.c index 3d8d5bccb0..0480704b27 100644 --- a/bgpd/rfapi/vnc_export_bgp.c +++ b/bgpd/rfapi/vnc_export_bgp.c @@ -78,7 +78,7 @@ static void encap_attr_export_ce(struct attr *new, struct attr *orig, * Make "new" a ghost attr copy of "orig" */ memset(new, 0, sizeof(struct attr)); - bgp_attr_dup(new, orig); + *new = *orig; /* * Set nexthop @@ -86,13 +86,13 @@ static void encap_attr_export_ce(struct attr *new, struct attr *orig, switch (use_nexthop->family) { case AF_INET: new->nexthop = use_nexthop->u.prefix4; - new->mp_nexthop_len = 4; /* bytes */ + new->mp_nexthop_len = BGP_ATTR_NHLEN_IPV4; /* bytes */ new->flag |= ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP); break; case AF_INET6: new->mp_nexthop_global = use_nexthop->u.prefix6; - new->mp_nexthop_len = 16; /* bytes */ + new->mp_nexthop_len = BGP_ATTR_NHLEN_IPV6_GLOBAL; /* bytes */ break; default: @@ -177,9 +177,9 @@ void vnc_direct_bgp_add_route_ce(struct bgp *bgp, struct agg_node *rn, { struct attr *attr = bpi->attr; struct peer *peer = bpi->peer; - struct prefix *prefix = &rn->p; + const struct prefix *prefix = agg_node_get_prefix(rn); afi_t afi = family2afi(prefix->family); - struct bgp_node *urn; + struct bgp_dest *udest; struct bgp_path_info *ubpi; struct attr hattr; struct attr *iattr; @@ -254,9 +254,10 @@ void vnc_direct_bgp_add_route_ce(struct bgp *bgp, struct agg_node *rn, * Is this route already represented in the unicast RIB? * (look up prefix; compare route type, sub_type, peer, nexthop) */ - urn = bgp_afi_node_get(bgp->rib[afi][SAFI_UNICAST], afi, SAFI_UNICAST, - prefix, NULL); - for (ubpi = bgp_node_get_bgp_path_info(urn); ubpi; ubpi = ubpi->next) { + udest = bgp_afi_node_get(bgp->rib[afi][SAFI_UNICAST], afi, SAFI_UNICAST, + prefix, NULL); + for (ubpi = bgp_dest_get_bgp_path_info(udest); ubpi; + ubpi = ubpi->next) { struct prefix unicast_nexthop; if (CHECK_FLAG(ubpi->flags, BGP_PATH_REMOVED)) @@ -330,7 +331,8 @@ void vnc_direct_bgp_add_route_ce(struct bgp *bgp, struct agg_node *rn, void vnc_direct_bgp_del_route_ce(struct bgp *bgp, struct agg_node *rn, struct bgp_path_info *bpi) { - afi_t afi = family2afi(rn->p.family); + const struct prefix *p = agg_node_get_prefix(rn); + afi_t afi = family2afi(p->family); struct bgp_path_info *vbpi; struct prefix ce_nexthop; @@ -395,8 +397,8 @@ void vnc_direct_bgp_del_route_ce(struct bgp *bgp, struct agg_node *rn, /* * withdraw the route */ - bgp_withdraw(bpi->peer, &rn->p, 0, /* addpath_id */ - NULL, /* attr, ignored */ + bgp_withdraw(bpi->peer, p, 0, /* addpath_id */ + NULL, /* attr, ignored */ afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT, BGP_ROUTE_REDISTRIBUTE, NULL, /* RD not used for unicast */ NULL, 0, NULL); /* tag not used for unicast */ @@ -432,17 +434,11 @@ static void vnc_direct_bgp_vpn_enable_ce(struct bgp *bgp, afi_t afi) */ for (rn = agg_route_top(bgp->rfapi->it_ce->imported_vpn[afi]); rn; rn = agg_route_next(rn)) { - if (!rn->info) continue; - { - char prefixstr[PREFIX_STRLEN]; - - prefix2str(&rn->p, prefixstr, sizeof(prefixstr)); - vnc_zlog_debug_verbose("%s: checking prefix %s", - __func__, prefixstr); - } + vnc_zlog_debug_verbose("%s: checking prefix %pRN", __func__, + rn); for (ri = rn->info; ri; ri = ri->next) { @@ -461,7 +457,7 @@ static void vnc_direct_bgp_vpn_enable_ce(struct bgp *bgp, afi_t afi) static void vnc_direct_bgp_vpn_disable_ce(struct bgp *bgp, afi_t afi) { - struct bgp_node *rn; + struct bgp_dest *dest; vnc_zlog_debug_verbose("%s: entry, afi=%d", __func__, afi); @@ -477,14 +473,14 @@ static void vnc_direct_bgp_vpn_disable_ce(struct bgp *bgp, afi_t afi) * Go through the entire BGP unicast table and remove routes that * originated from us */ - for (rn = bgp_table_top(bgp->rib[afi][SAFI_UNICAST]); rn; - rn = bgp_route_next(rn)) { + for (dest = bgp_table_top(bgp->rib[afi][SAFI_UNICAST]); dest; + dest = bgp_route_next(dest)) { struct bgp_path_info *ri; struct bgp_path_info *next; - for (ri = bgp_node_get_bgp_path_info(rn), next = NULL; - ri; ri = next) { + for (ri = bgp_dest_get_bgp_path_info(dest), next = NULL; ri; + ri = next) { next = ri->next; @@ -492,9 +488,9 @@ static void vnc_direct_bgp_vpn_disable_ce(struct bgp *bgp, afi_t afi) && ri->sub_type == BGP_ROUTE_REDISTRIBUTE) { bgp_withdraw( - ri->peer, &rn->p, /* prefix */ - 0, /* addpath_id */ - NULL, /* ignored */ + ri->peer, bgp_dest_get_prefix(dest), + 0, /* addpath_id */ + NULL, /* ignored */ AFI_IP, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT, BGP_ROUTE_REDISTRIBUTE, @@ -537,7 +533,7 @@ static struct ecommunity *vnc_route_origin_ecom(struct agg_node *rn) &bpi->attr->mp_nexthop_global_in.s_addr, 4); roec.val[6] = 0; roec.val[7] = 0; - ecommunity_add_val(new, &roec); + ecommunity_add_val(new, &roec, false, false); break; case AF_INET6: /* No support for IPv6 addresses in extended communities @@ -568,7 +564,7 @@ static struct ecommunity *vnc_route_origin_ecom_single(struct in_addr *origin) new = ecommunity_new(); assert(new); - ecommunity_add_val(new, &roec); + ecommunity_add_val(new, &roec, false, false); if (!new->size) { ecommunity_free(&new); @@ -616,7 +612,7 @@ encap_attr_export(struct attr *new, struct attr *orig, * Make "new" a ghost attr copy of "orig" */ memset(new, 0, sizeof(struct attr)); - bgp_attr_dup(new, orig); + *new = *orig; /* * Set nexthop @@ -624,13 +620,13 @@ encap_attr_export(struct attr *new, struct attr *orig, switch (use_nexthop->family) { case AF_INET: new->nexthop = use_nexthop->u.prefix4; - new->mp_nexthop_len = 4; /* bytes */ + new->mp_nexthop_len = BGP_ATTR_NHLEN_IPV4; /* bytes */ new->flag |= ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP); break; case AF_INET6: new->mp_nexthop_global = use_nexthop->u.prefix6; - new->mp_nexthop_len = 16; /* bytes */ + new->mp_nexthop_len = BGP_ATTR_NHLEN_IPV6_GLOBAL; /* bytes */ break; default: @@ -698,7 +694,8 @@ void vnc_direct_bgp_add_prefix(struct bgp *bgp, struct attr attr = {0}; struct listnode *node, *nnode; struct rfapi_rfg_name *rfgn; - afi_t afi = family2afi(rn->p.family); + const struct prefix *p = agg_node_get_prefix(rn); + afi_t afi = family2afi(p->family); if (!afi) { flog_err(EC_LIB_DEVELOPMENT, "%s: can't get afi of route node", @@ -769,7 +766,7 @@ void vnc_direct_bgp_add_prefix(struct bgp *bgp, */ if (rfgn->rfg->plist_export_bgp[afi]) { if (prefix_list_apply(rfgn->rfg->plist_export_bgp[afi], - &rn->p) + p) == PREFIX_DENY) continue; @@ -808,7 +805,8 @@ void vnc_direct_bgp_del_prefix(struct bgp *bgp, { struct listnode *node, *nnode; struct rfapi_rfg_name *rfgn; - afi_t afi = family2afi(rn->p.family); + const struct prefix *p = agg_node_get_prefix(rn); + afi_t afi = family2afi(p->family); if (!afi) { flog_err(EC_LIB_DEVELOPMENT, "%s: can't get afi route node", @@ -877,9 +875,9 @@ void vnc_direct_bgp_del_prefix(struct bgp *bgp, if (rfapiRaddr2Qprefix(&irfd->vn_addr, &nhp)) continue; - bgp_withdraw(irfd->peer, &rn->p, /* prefix */ - 0, /* addpath_id */ - NULL, /* attr, ignored */ + bgp_withdraw(irfd->peer, p, /* prefix */ + 0, /* addpath_id */ + NULL, /* attr, ignored */ afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT, BGP_ROUTE_REDISTRIBUTE, NULL, /* RD not used for unicast */ @@ -907,9 +905,9 @@ void vnc_direct_bgp_del_prefix(struct bgp *bgp, if (rfapiRaddr2Qprefix(&irfd->vn_addr, &nhp)) continue; - bgp_withdraw(irfd->peer, &rn->p, /* prefix */ - 0, /* addpath_id */ - NULL, /* attr, ignored */ + bgp_withdraw(irfd->peer, p, /* prefix */ + 0, /* addpath_id */ + NULL, /* attr, ignored */ afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT, BGP_ROUTE_REDISTRIBUTE, NULL, /* RD not used for unicast */ @@ -998,6 +996,8 @@ void vnc_direct_bgp_add_nve(struct bgp *bgp, struct rfapi_descriptor *rfd) struct attr hattr; struct attr *iattr; struct bgp_path_info info; + const struct prefix *p = + agg_node_get_prefix(rn); if (rfapiRaddr2Qprefix(&irfd->vn_addr, &nhp)) @@ -1010,7 +1010,7 @@ void vnc_direct_bgp_add_nve(struct bgp *bgp, struct rfapi_descriptor *rfd) if (prefix_list_apply( rfgn->rfg->plist_export_bgp [afi], - &rn->p) + p) == PREFIX_DENY) continue; @@ -1033,8 +1033,7 @@ void vnc_direct_bgp_add_nve(struct bgp *bgp, struct rfapi_descriptor *rfd) ret = route_map_apply( rfgn->rfg ->routemap_export_bgp, - &rn->p, RMAP_BGP, - &info); + p, RMAP_BGP, &info); if (ret == RMAP_DENYMATCH) { bgp_attr_flush(&hattr); continue; @@ -1044,8 +1043,8 @@ void vnc_direct_bgp_add_nve(struct bgp *bgp, struct rfapi_descriptor *rfd) iattr = bgp_attr_intern(&hattr); bgp_attr_flush(&hattr); bgp_update( - irfd->peer, &rn->p, /* prefix */ - 0, /* addpath_id */ + irfd->peer, p, /* prefix */ + 0, /* addpath_id */ iattr, /* bgp_update copies it */ afi, SAFI_UNICAST, @@ -1134,7 +1133,8 @@ void vnc_direct_bgp_del_nve(struct bgp *bgp, struct rfapi_descriptor *rfd) rn = agg_route_next(rn)) { if (rn->info) { - + const struct prefix *p = + agg_node_get_prefix(rn); struct prefix nhp; struct rfapi_descriptor *irfd = rfd; @@ -1142,10 +1142,9 @@ void vnc_direct_bgp_del_nve(struct bgp *bgp, struct rfapi_descriptor *rfd) &nhp)) continue; - bgp_withdraw(irfd->peer, - &rn->p, /* prefix */ - 0, /* addpath_id */ - NULL, /* attr, ignored */ + bgp_withdraw(irfd->peer, p, /* prefix */ + 0, /* addpath_id */ + NULL, /* attr, ignored */ afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT, BGP_ROUTE_REDISTRIBUTE, @@ -1169,6 +1168,7 @@ static void vnc_direct_add_rn_group_rd(struct bgp *bgp, struct bgp_path_info info; struct attr hattr; struct attr *iattr; + const struct prefix *p = agg_node_get_prefix(rn); if (irfd == NULL && rfg->type != RFAPI_GROUP_CFG_VRF) { /* need new rfapi_handle, for peer strcture @@ -1189,7 +1189,7 @@ static void vnc_direct_add_rn_group_rd(struct bgp *bgp, } if (rfg->label > MPLS_LABEL_MAX) { vnc_zlog_debug_verbose( - "%s: VRF \"%s\" is missing defaul label configuration.\n", + "%s: VRF \"%s\" is missing default label configuration.\n", __func__, rfg->name); return; } @@ -1242,8 +1242,8 @@ static void vnc_direct_add_rn_group_rd(struct bgp *bgp, info.peer = irfd->peer; info.attr = &hattr; - ret = route_map_apply(rfg->routemap_export_bgp, &rn->p, - RMAP_BGP, &info); + ret = route_map_apply(rfg->routemap_export_bgp, p, RMAP_BGP, + &info); if (ret == RMAP_DENYMATCH) { bgp_attr_flush(&hattr); vnc_zlog_debug_verbose( @@ -1261,9 +1261,9 @@ static void vnc_direct_add_rn_group_rd(struct bgp *bgp, iattr = bgp_attr_intern(&hattr); bgp_attr_flush(&hattr); - bgp_update(irfd->peer, &rn->p, /* prefix */ - 0, /* addpath_id */ - iattr, /* bgp_update copies it */ + bgp_update(irfd->peer, p, /* prefix */ + 0, /* addpath_id */ + iattr, /* bgp_update copies it */ afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT, BGP_ROUTE_REDISTRIBUTE, NULL, /* RD not used for unicast */ NULL, /* tag not used for unicast */ @@ -1317,7 +1317,7 @@ static void vnc_direct_bgp_add_group_afi(struct bgp *bgp, for (rn = agg_route_top(rt); rn; rn = agg_route_next(rn)) { if (rn->info) { - + const struct prefix *p = agg_node_get_prefix(rn); struct listnode *ln; /* @@ -1325,7 +1325,7 @@ static void vnc_direct_bgp_add_group_afi(struct bgp *bgp, */ if (rfg->plist_export_bgp[afi]) { if (prefix_list_apply( - rfg->plist_export_bgp[afi], &rn->p) + rfg->plist_export_bgp[afi], p) == PREFIX_DENY) continue; @@ -1374,9 +1374,10 @@ static void vnc_direct_del_rn_group_rd(struct bgp *bgp, { if (irfd == NULL) return; - bgp_withdraw(irfd->peer, &rn->p, /* prefix */ - 0, /* addpath_id */ - NULL, /* attr, ignored */ + + bgp_withdraw(irfd->peer, agg_node_get_prefix(rn), /* prefix */ + 0, /* addpath_id */ + NULL, /* attr, ignored */ afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT, BGP_ROUTE_REDISTRIBUTE, NULL, /* RD not used for unicast */ NULL, 0, NULL); /* tag not used for unicast */ @@ -1493,9 +1494,9 @@ static void vnc_direct_bgp_unexport_table(afi_t afi, struct agg_table *rt, irfd)) { bgp_withdraw(irfd->peer, - &rn->p, /* prefix */ - 0, /* addpath_id */ - NULL, /* attr, ignored */ + agg_node_get_prefix(rn), + 0, /* addpath_id */ + NULL, /* attr, ignored */ afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT, BGP_ROUTE_REDISTRIBUTE, @@ -1633,7 +1634,7 @@ void vnc_direct_bgp_vpn_disable(struct bgp *bgp, afi_t afi) * caller do it? */ void vnc_direct_bgp_rh_add_route(struct bgp *bgp, afi_t afi, - struct prefix *prefix, struct peer *peer, + const struct prefix *prefix, struct peer *peer, struct attr *attr) { struct vnc_export_info *eti; @@ -1732,13 +1733,14 @@ void vnc_direct_bgp_rh_add_route(struct bgp *bgp, afi_t afi, static int vncExportWithdrawTimer(struct thread *t) { struct vnc_export_info *eti = t->arg; + const struct prefix *p = agg_node_get_prefix(eti->node); /* * withdraw the route */ - bgp_withdraw(eti->peer, &eti->node->p, 0, /* addpath_id */ - NULL, /* attr, ignored */ - family2afi(eti->node->p.family), SAFI_UNICAST, eti->type, + bgp_withdraw(eti->peer, p, 0, /* addpath_id */ + NULL, /* attr, ignored */ + family2afi(p->family), SAFI_UNICAST, eti->type, eti->subtype, NULL, /* RD not used for unicast */ NULL, 0, NULL); /* tag not used for unicast, EVPN neither */ @@ -1757,7 +1759,7 @@ static int vncExportWithdrawTimer(struct thread *t) * caller do it? */ void vnc_direct_bgp_rh_del_route(struct bgp *bgp, afi_t afi, - struct prefix *prefix, struct peer *peer) + const struct prefix *prefix, struct peer *peer) { struct vnc_export_info *eti; @@ -1804,7 +1806,7 @@ void vnc_direct_bgp_rh_del_route(struct bgp *bgp, afi_t afi, void vnc_direct_bgp_rh_vpn_enable(struct bgp *bgp, afi_t afi) { struct prefix_rd prd; - struct bgp_node *prn; + struct bgp_dest *pdest; struct rfapi_cfg *hc; vnc_zlog_debug_verbose("%s: entry, afi=%d", __func__, afi); @@ -1834,47 +1836,46 @@ void vnc_direct_bgp_rh_vpn_enable(struct bgp *bgp, afi_t afi) vnc_zlog_debug_verbose("%s: starting RD loop", __func__); /* Loop over all the RDs */ - for (prn = bgp_table_top(bgp->rib[afi][SAFI_MPLS_VPN]); prn; - prn = bgp_route_next(prn)) { + for (pdest = bgp_table_top(bgp->rib[afi][SAFI_MPLS_VPN]); pdest; + pdest = bgp_route_next(pdest)) { struct bgp_table *table; - struct bgp_node *rn; + struct bgp_dest *dest; struct bgp_path_info *ri; + const struct prefix *pdest_p = bgp_dest_get_prefix(pdest); memset(&prd, 0, sizeof(prd)); prd.family = AF_UNSPEC; prd.prefixlen = 64; - memcpy(prd.val, prn->p.u.val, 8); + memcpy(prd.val, pdest_p->u.val, 8); /* This is the per-RD table of prefixes */ - table = bgp_node_get_bgp_table_info(prn); + table = bgp_dest_get_bgp_table_info(pdest); if (!table) continue; - for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) { + for (dest = bgp_table_top(table); dest; + dest = bgp_route_next(dest)) { + const struct prefix *dest_p; /* * skip prefix list check if no routes here */ - if (!bgp_node_has_bgp_path_info_data(rn)) + if (!bgp_dest_has_bgp_path_info_data(dest)) continue; - { - char prefixstr[PREFIX_STRLEN]; + vnc_zlog_debug_verbose("%s: checking prefix %pRN", + __func__, dest); - prefix2str(&rn->p, prefixstr, - sizeof(prefixstr)); - vnc_zlog_debug_verbose("%s: checking prefix %s", - __func__, prefixstr); - } + dest_p = bgp_dest_get_prefix(dest); /* * prefix list check */ if (hc->plist_export_bgp[afi]) { if (prefix_list_apply(hc->plist_export_bgp[afi], - &rn->p) + dest_p) == PREFIX_DENY) { vnc_zlog_debug_verbose( @@ -1884,8 +1885,8 @@ void vnc_direct_bgp_rh_vpn_enable(struct bgp *bgp, afi_t afi) } } - for (ri = bgp_node_get_bgp_path_info(rn); - ri; ri = ri->next) { + for (ri = bgp_dest_get_bgp_path_info(dest); ri; + ri = ri->next) { vnc_zlog_debug_verbose("%s: ri->sub_type: %d", __func__, ri->sub_type); @@ -1919,7 +1920,7 @@ void vnc_direct_bgp_rh_vpn_enable(struct bgp *bgp, afi_t afi) info.attr = &hattr; ret = route_map_apply( hc->routemap_export_bgp, - &rn->p, RMAP_BGP, + dest_p, RMAP_BGP, &info); if (ret == RMAP_DENYMATCH) { bgp_attr_flush(&hattr); @@ -1939,7 +1940,7 @@ void vnc_direct_bgp_rh_vpn_enable(struct bgp *bgp, afi_t afi) * this route */ eti = vnc_eti_get( - bgp, EXPORT_TYPE_BGP, &rn->p, + bgp, EXPORT_TYPE_BGP, dest_p, ri->peer, ZEBRA_ROUTE_VNC_DIRECT_RH, BGP_ROUTE_REDISTRIBUTE); @@ -1961,7 +1962,7 @@ void vnc_direct_bgp_rh_vpn_enable(struct bgp *bgp, afi_t afi) __func__); bgp_update( - ri->peer, &rn->p, /* prefix */ + ri->peer, dest_p, /* prefix */ 0, /* addpath_id */ iattr, /* bgp_update copies it */ @@ -1983,7 +1984,7 @@ void vnc_direct_bgp_rh_vpn_enable(struct bgp *bgp, afi_t afi) void vnc_direct_bgp_rh_vpn_disable(struct bgp *bgp, afi_t afi) { - struct bgp_node *rn; + struct bgp_dest *dest; vnc_zlog_debug_verbose("%s: entry, afi=%d", __func__, afi); @@ -1999,13 +2000,14 @@ void vnc_direct_bgp_rh_vpn_disable(struct bgp *bgp, afi_t afi) * Go through the entire BGP unicast table and remove routes that * originated from us */ - for (rn = bgp_table_top(bgp->rib[afi][SAFI_UNICAST]); rn; - rn = bgp_route_next(rn)) { - + for (dest = bgp_table_top(bgp->rib[afi][SAFI_UNICAST]); dest; + dest = bgp_route_next(dest)) { + const struct prefix *dest_p = bgp_dest_get_prefix(dest); struct bgp_path_info *ri; struct bgp_path_info *next; - for (ri = bgp_node_get_bgp_path_info(rn), next = NULL; ri; ri = next) { + for (ri = bgp_dest_get_bgp_path_info(dest), next = NULL; ri; + ri = next) { next = ri->next; @@ -2018,7 +2020,7 @@ void vnc_direct_bgp_rh_vpn_disable(struct bgp *bgp, afi_t afi) * Delete routes immediately (no timer) */ eti = vnc_eti_checktimer( - bgp, EXPORT_TYPE_BGP, &rn->p, ri->peer, + bgp, EXPORT_TYPE_BGP, dest_p, ri->peer, ZEBRA_ROUTE_VNC_DIRECT_RH, BGP_ROUTE_REDISTRIBUTE); if (eti) { @@ -2027,9 +2029,9 @@ void vnc_direct_bgp_rh_vpn_disable(struct bgp *bgp, afi_t afi) vnc_eti_delete(eti); } - bgp_withdraw(ri->peer, &rn->p, /* prefix */ + bgp_withdraw(ri->peer, dest_p, /* prefix */ 0, /* addpath_id */ - NULL, /* ignored */ + NULL, /* ignored */ AFI_IP, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT_RH, BGP_ROUTE_REDISTRIBUTE, diff --git a/bgpd/rfapi/vnc_export_bgp_p.h b/bgpd/rfapi/vnc_export_bgp_p.h index a1cb972740..bf292abb0a 100644 --- a/bgpd/rfapi/vnc_export_bgp_p.h +++ b/bgpd/rfapi/vnc_export_bgp_p.h @@ -61,12 +61,12 @@ extern void vnc_direct_bgp_reexport_group_afi(struct bgp *bgp, extern void vnc_direct_bgp_rh_add_route(struct bgp *bgp, afi_t afi, - struct prefix *prefix, + const struct prefix *prefix, struct peer *peer, struct attr *attr); extern void vnc_direct_bgp_rh_del_route(struct bgp *bgp, afi_t afi, - struct prefix *prefix, + const struct prefix *prefix, struct peer *peer); extern void vnc_direct_bgp_reexport(struct bgp *bgp, afi_t afi); diff --git a/bgpd/rfapi/vnc_export_table.c b/bgpd/rfapi/vnc_export_table.c index 5e00a1017b..255f868bdf 100644 --- a/bgpd/rfapi/vnc_export_table.c +++ b/bgpd/rfapi/vnc_export_table.c @@ -34,7 +34,7 @@ #include "bgpd/rfapi/vnc_debug.h" struct agg_node *vnc_etn_get(struct bgp *bgp, vnc_export_type_t type, - struct prefix *p) + const struct prefix *p) { struct agg_table *t = NULL; struct agg_node *rn = NULL; @@ -66,7 +66,7 @@ struct agg_node *vnc_etn_get(struct bgp *bgp, vnc_export_type_t type, } struct agg_node *vnc_etn_lookup(struct bgp *bgp, vnc_export_type_t type, - struct prefix *p) + const struct prefix *p) { struct agg_table *t = NULL; struct agg_node *rn = NULL; @@ -98,7 +98,7 @@ struct agg_node *vnc_etn_lookup(struct bgp *bgp, vnc_export_type_t type, } struct vnc_export_info *vnc_eti_get(struct bgp *bgp, vnc_export_type_t etype, - struct prefix *p, struct peer *peer, + const struct prefix *p, struct peer *peer, uint8_t type, uint8_t subtype) { struct agg_node *etn; @@ -165,8 +165,9 @@ void vnc_eti_delete(struct vnc_export_info *goner) struct vnc_export_info *vnc_eti_checktimer(struct bgp *bgp, vnc_export_type_t etype, - struct prefix *p, struct peer *peer, - uint8_t type, uint8_t subtype) + const struct prefix *p, + struct peer *peer, uint8_t type, + uint8_t subtype) { struct agg_node *etn; struct vnc_export_info *eti; diff --git a/bgpd/rfapi/vnc_export_table.h b/bgpd/rfapi/vnc_export_table.h index fdb35e81e1..8a1fc9aaef 100644 --- a/bgpd/rfapi/vnc_export_table.h +++ b/bgpd/rfapi/vnc_export_table.h @@ -46,21 +46,21 @@ struct vnc_export_info { }; extern struct agg_node *vnc_etn_get(struct bgp *bgp, vnc_export_type_t type, - struct prefix *p); + const struct prefix *p); extern struct agg_node *vnc_etn_lookup(struct bgp *bgp, vnc_export_type_t type, - struct prefix *p); + const struct prefix *p); -extern struct vnc_export_info *vnc_eti_get(struct bgp *bgp, - vnc_export_type_t etype, - struct prefix *p, struct peer *peer, - uint8_t type, uint8_t subtype); +extern struct vnc_export_info * +vnc_eti_get(struct bgp *bgp, vnc_export_type_t etype, const struct prefix *p, + struct peer *peer, uint8_t type, uint8_t subtype); extern void vnc_eti_delete(struct vnc_export_info *goner); extern struct vnc_export_info * -vnc_eti_checktimer(struct bgp *bgp, vnc_export_type_t etype, struct prefix *p, - struct peer *peer, uint8_t type, uint8_t subtype); +vnc_eti_checktimer(struct bgp *bgp, vnc_export_type_t etype, + const struct prefix *p, struct peer *peer, uint8_t type, + uint8_t subtype); #endif /* _QUAGGA_VNC_VNC_EXPORT_TABLE_H_ */ diff --git a/bgpd/rfapi/vnc_import_bgp.c b/bgpd/rfapi/vnc_import_bgp.c index eb2d0fd889..85d64b5a72 100644 --- a/bgpd/rfapi/vnc_import_bgp.c +++ b/bgpd/rfapi/vnc_import_bgp.c @@ -104,7 +104,7 @@ uint32_t calc_local_pref(struct attr *attr, struct peer *peer) return local_pref; } -static int is_host_prefix(struct prefix *p) +static int is_host_prefix(const struct prefix *p) { switch (p->family) { case AF_INET: @@ -128,14 +128,14 @@ struct prefix_bag { static const uint8_t maskbit[] = {0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff}; -int vnc_prefix_cmp(void *pfx1, void *pfx2) +int vnc_prefix_cmp(const void *pfx1, const void *pfx2) { int offset; int shift; uint8_t mask; - struct prefix *p1 = pfx1; - struct prefix *p2 = pfx2; + const struct prefix *p1 = pfx1; + const struct prefix *p2 = pfx2; if (p1->family < p2->family) return -1; @@ -275,7 +275,12 @@ static void vnc_rhnck(char *tag) vnc_zlog_debug_verbose("%s: vnc_rhnck OK", tag); } -#define VNC_RHNCK(n) do {char buf[BUFSIZ];sprintf(buf,"%s: %s", __func__, #n);vnc_rhnck(buf);} while (0) +#define VNC_RHNCK(n) \ + do { \ + char buf[BUFSIZ]; \ + snprintf(buf, sizeof(buf), "%s: %s", __func__, #n); \ + vnc_rhnck(buf); \ + } while (0) #else @@ -299,9 +304,9 @@ static void vnc_rhnck(char *tag) */ static int process_unicast_route(struct bgp *bgp, /* in */ afi_t afi, /* in */ - struct prefix *prefix, /* in */ - struct bgp_path_info *info, /* in */ - struct ecommunity **ecom, /* OUT */ + const struct prefix *prefix, /* in */ + struct bgp_path_info *info, /* in */ + struct ecommunity **ecom, /* OUT */ struct prefix *unicast_nexthop) /* OUT */ { struct rfapi_cfg *hc = bgp->rfapi_cfg; @@ -356,7 +361,8 @@ static int process_unicast_route(struct bgp *bgp, /* in */ * all of the possible returns above. */ memset(&hattr, 0, sizeof(struct attr)); - bgp_attr_dup(&hattr, attr); /* hattr becomes a ghost attr */ + /* hattr becomes a ghost attr */ + hattr = *attr; if (rmap) { struct bgp_path_info info; @@ -414,7 +420,7 @@ static int process_unicast_route(struct bgp *bgp, /* in */ localadmin = htons(hc->resolve_nve_roo_local_admin); memcpy(vnc_gateway_magic.val + 6, (char *)&localadmin, 2); - ecommunity_add_val(*ecom, &vnc_gateway_magic); + ecommunity_add_val(*ecom, &vnc_gateway_magic, false, false); } return 0; @@ -424,10 +430,10 @@ static int process_unicast_route(struct bgp *bgp, /* in */ static void vnc_import_bgp_add_route_mode_resolve_nve_one_bi( struct bgp *bgp, afi_t afi, struct bgp_path_info *bpi, /* VPN bpi */ struct prefix_rd *prd, /* RD */ - struct prefix *prefix, /* unicast route prefix */ - uint32_t *local_pref, /* NULL = no local_pref */ - uint32_t *med, /* NULL = no med */ - struct ecommunity *ecom) /* generated ecoms */ + const struct prefix *prefix, /* unicast route prefix */ + uint32_t *local_pref, /* NULL = no local_pref */ + uint32_t *med, /* NULL = no med */ + struct ecommunity *ecom) /* generated ecoms */ { struct prefix un; struct prefix nexthop; @@ -476,25 +482,19 @@ static void vnc_import_bgp_add_route_mode_resolve_nve_one_bi( plifetime = &lifetime; } - if (bpi->attr) { - encaptlvs = bpi->attr->vnc_subtlvs; - if (bpi->attr->encap_tunneltype != BGP_ENCAP_TYPE_RESERVED - && bpi->attr->encap_tunneltype != BGP_ENCAP_TYPE_MPLS) { - if (opt != NULL) - opt->next = &optary[cur_opt]; - opt = &optary[cur_opt++]; - memset(opt, 0, sizeof(struct rfapi_un_option)); - opt->type = RFAPI_UN_OPTION_TYPE_TUNNELTYPE; - opt->v.tunnel.type = bpi->attr->encap_tunneltype; - /* TBD parse bpi->attr->extra->encap_subtlvs */ - } - } else { - encaptlvs = NULL; + encaptlvs = bpi->attr->vnc_subtlvs; + if (bpi->attr->encap_tunneltype != BGP_ENCAP_TYPE_RESERVED + && bpi->attr->encap_tunneltype != BGP_ENCAP_TYPE_MPLS) { + opt = &optary[cur_opt++]; + memset(opt, 0, sizeof(struct rfapi_un_option)); + opt->type = RFAPI_UN_OPTION_TYPE_TUNNELTYPE; + opt->v.tunnel.type = bpi->attr->encap_tunneltype; + /* TBD parse bpi->attr->extra->encap_subtlvs */ } struct ecommunity *new_ecom = ecommunity_dup(ecom); - if (bpi->attr && bpi->attr->ecommunity) + if (bpi->attr->ecommunity) ecommunity_merge(new_ecom, bpi->attr->ecommunity); if (bpi->extra) @@ -514,15 +514,16 @@ static void vnc_import_bgp_add_route_mode_resolve_nve_one_bi( } static void vnc_import_bgp_add_route_mode_resolve_nve_one_rd( - struct prefix_rd *prd, /* RD */ + struct prefix_rd *prd, /* RD */ struct bgp_table *table_rd, /* per-rd VPN route table */ - afi_t afi, struct bgp *bgp, struct prefix *prefix, /* unicast prefix */ - struct ecommunity *ecom, /* generated ecoms */ - uint32_t *local_pref, /* NULL = no local_pref */ + afi_t afi, struct bgp *bgp, + const struct prefix *prefix, /* unicast prefix */ + struct ecommunity *ecom, /* generated ecoms */ + uint32_t *local_pref, /* NULL = no local_pref */ uint32_t *med, /* NULL = no med */ struct prefix *ubpi_nexthop) /* unicast nexthop */ { - struct bgp_node *bn; + struct bgp_dest *bd; struct bgp_path_info *bpi; if (!table_rd) @@ -537,8 +538,8 @@ static void vnc_import_bgp_add_route_mode_resolve_nve_one_rd( } /* exact match */ - bn = bgp_node_lookup(table_rd, ubpi_nexthop); - if (!bn) { + bd = bgp_node_lookup(table_rd, ubpi_nexthop); + if (!bd) { vnc_zlog_debug_verbose( "%s: no match in RD's table for ubpi_nexthop", __func__); @@ -546,19 +547,19 @@ static void vnc_import_bgp_add_route_mode_resolve_nve_one_rd( } /* Iterate over bgp_info items at this node */ - for (bpi = bgp_node_get_bgp_path_info(bn); bpi; bpi = bpi->next) { + for (bpi = bgp_dest_get_bgp_path_info(bd); bpi; bpi = bpi->next) { vnc_import_bgp_add_route_mode_resolve_nve_one_bi( bgp, afi, bpi, /* VPN bpi */ prd, prefix, local_pref, med, ecom); } - bgp_unlock_node(bn); + bgp_dest_unlock_node(bd); } static void vnc_import_bgp_add_route_mode_resolve_nve( - struct bgp *bgp, struct prefix *prefix, /* unicast prefix */ - struct bgp_path_info *info) /* unicast info */ + struct bgp *bgp, const struct prefix *prefix, /* unicast prefix */ + struct bgp_path_info *info) /* unicast info */ { afi_t afi = family2afi(prefix->family); @@ -569,7 +570,7 @@ static void vnc_import_bgp_add_route_mode_resolve_nve( uint32_t *med = NULL; struct prefix_bag *pb; - struct bgp_node *bnp; /* prd table node */ + struct bgp_dest *bdp; /* prd table node */ /*debugging */ if (VNC_DEBUG(VERBOSE)) { @@ -635,12 +636,8 @@ static void vnc_import_bgp_add_route_mode_resolve_nve( } local_pref = calc_local_pref(info->attr, info->peer); - if (info->attr - && (info->attr->flag & ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC))) { - + if (info->attr->flag & ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC)) med = &info->attr->med; - } - /* * At this point, we have allocated: @@ -671,19 +668,20 @@ static void vnc_import_bgp_add_route_mode_resolve_nve( * (exact match, /32). If an exact match is found, call add_vnc_route. */ - for (bnp = bgp_table_top(bgp->rib[afi][SAFI_MPLS_VPN]); bnp; - bnp = bgp_route_next(bnp)) { + for (bdp = bgp_table_top(bgp->rib[afi][SAFI_MPLS_VPN]); bdp; + bdp = bgp_route_next(bdp)) { struct bgp_table *table; - table = bgp_node_get_bgp_table_info(bnp); + table = bgp_dest_get_bgp_table_info(bdp); if (!table) continue; vnc_import_bgp_add_route_mode_resolve_nve_one_rd( - (struct prefix_rd *)&bnp->p, table, afi, bgp, prefix, - ecom, &local_pref, med, &pfx_unicast_nexthop); + (struct prefix_rd *)bgp_dest_get_prefix(bdp), table, + afi, bgp, prefix, ecom, &local_pref, med, + &pfx_unicast_nexthop); } @@ -695,7 +693,7 @@ static void vnc_import_bgp_add_route_mode_resolve_nve( static void vnc_import_bgp_add_route_mode_plain(struct bgp *bgp, - struct prefix *prefix, + const struct prefix *prefix, struct bgp_path_info *info) { afi_t afi = family2afi(prefix->family); @@ -808,7 +806,8 @@ static void vnc_import_bgp_add_route_mode_plain(struct bgp *bgp, * all of the possible returns above. */ memset(&hattr, 0, sizeof(struct attr)); - bgp_attr_dup(&hattr, attr); /* hattr becomes a ghost attr */ + /* hattr becomes a ghost attr */ + hattr = *attr; if (rmap) { struct bgp_path_info info; @@ -882,10 +881,9 @@ static void vnc_import_bgp_add_route_mode_plain(struct bgp *bgp, ecommunity_free(&ecom); } -static void -vnc_import_bgp_add_route_mode_nvegroup(struct bgp *bgp, struct prefix *prefix, - struct bgp_path_info *info, - struct rfapi_nve_group_cfg *rfg) +static void vnc_import_bgp_add_route_mode_nvegroup( + struct bgp *bgp, const struct prefix *prefix, + struct bgp_path_info *info, struct rfapi_nve_group_cfg *rfg) { afi_t afi = family2afi(prefix->family); struct peer *peer = info->peer; @@ -1010,7 +1008,8 @@ vnc_import_bgp_add_route_mode_nvegroup(struct bgp *bgp, struct prefix *prefix, * all of the possible returns above. */ memset(&hattr, 0, sizeof(struct attr)); - bgp_attr_dup(&hattr, attr); /* hattr becomes a ghost attr */ + /* hattr becomes a ghost attr */ + hattr = *attr; if (rmap) { struct bgp_path_info path; @@ -1087,7 +1086,7 @@ vnc_import_bgp_add_route_mode_nvegroup(struct bgp *bgp, struct prefix *prefix, } static void vnc_import_bgp_del_route_mode_plain(struct bgp *bgp, - struct prefix *prefix, + const struct prefix *prefix, struct bgp_path_info *info) { struct prefix_rd prd; @@ -1103,7 +1102,7 @@ static void vnc_import_bgp_del_route_mode_plain(struct bgp *bgp, * Compute VN address */ - if (info && info->attr) { + if (info) { rfapiUnicastNexthop2Prefix(afi, info->attr, &vn_pfx_space); } else { vnc_zlog_debug_verbose("%s: no attr, can't delete route", @@ -1160,7 +1159,7 @@ static void vnc_import_bgp_del_route_mode_plain(struct bgp *bgp, } static void vnc_import_bgp_del_route_mode_nvegroup(struct bgp *bgp, - struct prefix *prefix, + const struct prefix *prefix, struct bgp_path_info *info) { struct prefix_rd prd; @@ -1243,7 +1242,7 @@ static void vnc_import_bgp_del_route_mode_nvegroup(struct bgp *bgp, static void vnc_import_bgp_del_route_mode_resolve_nve_one_bi( struct bgp *bgp, afi_t afi, struct bgp_path_info *bpi, /* VPN bpi */ struct prefix_rd *prd, /* RD */ - struct prefix *prefix) /* unicast route prefix */ + const struct prefix *prefix) /* unicast route prefix */ { struct prefix un; @@ -1279,10 +1278,11 @@ static void vnc_import_bgp_del_route_mode_resolve_nve_one_bi( static void vnc_import_bgp_del_route_mode_resolve_nve_one_rd( struct prefix_rd *prd, struct bgp_table *table_rd, /* per-rd VPN route table */ - afi_t afi, struct bgp *bgp, struct prefix *prefix, /* unicast prefix */ - struct prefix *ubpi_nexthop) /* unicast bpi's nexthop */ + afi_t afi, struct bgp *bgp, + const struct prefix *prefix, /* unicast prefix */ + const struct prefix *ubpi_nexthop) /* unicast bpi's nexthop */ { - struct bgp_node *bn; + struct bgp_dest *bd; struct bgp_path_info *bpi; if (!table_rd) @@ -1297,8 +1297,8 @@ static void vnc_import_bgp_del_route_mode_resolve_nve_one_rd( /* exact match */ - bn = bgp_node_lookup(table_rd, ubpi_nexthop); - if (!bn) { + bd = bgp_node_lookup(table_rd, ubpi_nexthop); + if (!bd) { vnc_zlog_debug_verbose( "%s: no match in RD's table for ubpi_nexthop", __func__); @@ -1306,7 +1306,7 @@ static void vnc_import_bgp_del_route_mode_resolve_nve_one_rd( } /* Iterate over bgp_info items at this node */ - for (bpi = bgp_node_get_bgp_path_info(bn); bpi; bpi = bpi->next) { + for (bpi = bgp_dest_get_bgp_path_info(bd); bpi; bpi = bpi->next) { vnc_import_bgp_del_route_mode_resolve_nve_one_bi( bgp, afi, bpi, /* VPN bpi */ @@ -1314,12 +1314,12 @@ static void vnc_import_bgp_del_route_mode_resolve_nve_one_rd( prefix); /* unicast route prefix */ } - bgp_unlock_node(bn); + bgp_dest_unlock_node(bd); } static void vnc_import_bgp_del_route_mode_resolve_nve(struct bgp *bgp, afi_t afi, - struct prefix *prefix, + const struct prefix *prefix, struct bgp_path_info *info) { struct ecommunity *ecom = NULL; @@ -1331,7 +1331,7 @@ vnc_import_bgp_del_route_mode_resolve_nve(struct bgp *bgp, afi_t afi, void *cursor; struct skiplist *sl = bgp->rfapi->resolve_nve_nexthop; int rc; - struct bgp_node *bnp; /* prd table node */ + struct bgp_dest *bdp; /* prd table node */ if (!sl) { vnc_zlog_debug_verbose("%s: no RHN entries, skipping", @@ -1372,19 +1372,19 @@ vnc_import_bgp_del_route_mode_resolve_nve(struct bgp *bgp, afi_t afi, * (exact match, /32). If an exact match is found, call add_vnc_route. */ - for (bnp = bgp_table_top(bgp->rib[afi][SAFI_MPLS_VPN]); bnp; - bnp = bgp_route_next(bnp)) { + for (bdp = bgp_table_top(bgp->rib[afi][SAFI_MPLS_VPN]); bdp; + bdp = bgp_route_next(bdp)) { struct bgp_table *table; - table = bgp_node_get_bgp_table_info(bnp); + table = bgp_dest_get_bgp_table_info(bdp); if (!table) continue; vnc_import_bgp_del_route_mode_resolve_nve_one_rd( - (struct prefix_rd *)&bnp->p, table, afi, bgp, prefix, - &pfx_unicast_nexthop); /* TBD how is this set? */ + (struct prefix_rd *)bgp_dest_get_prefix(bdp), table, + afi, bgp, prefix, &pfx_unicast_nexthop); } if (ecom) @@ -1403,7 +1403,7 @@ vnc_import_bgp_del_route_mode_resolve_nve(struct bgp *bgp, afi_t afi, void vnc_import_bgp_add_vnc_host_route_mode_resolve_nve( struct bgp *bgp, struct prefix_rd *prd, /* RD */ struct bgp_table *table_rd, /* per-rd VPN route table */ - struct prefix *prefix, /* VPN prefix */ + const struct prefix *prefix, /* VPN prefix */ struct bgp_path_info *bpi) /* new VPN host route */ { afi_t afi = family2afi(prefix->family); @@ -1489,12 +1489,9 @@ void vnc_import_bgp_add_vnc_host_route_mode_resolve_nve( } local_pref = calc_local_pref(pb->ubpi->attr, pb->ubpi->peer); - if (pb->ubpi->attr - && (pb->ubpi->attr->flag - & ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC))) { - + if (pb->ubpi->attr->flag + & ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC)) med = &pb->ubpi->attr->med; - } /* * Sanity check @@ -1543,7 +1540,7 @@ void vnc_import_bgp_add_vnc_host_route_mode_resolve_nve( void vnc_import_bgp_del_vnc_host_route_mode_resolve_nve( struct bgp *bgp, struct prefix_rd *prd, /* RD */ struct bgp_table *table_rd, /* per-rd VPN route table */ - struct prefix *prefix, /* VPN prefix */ + const struct prefix *prefix, /* VPN prefix */ struct bgp_path_info *bpi) /* old VPN host route */ { afi_t afi = family2afi(prefix->family); @@ -1685,8 +1682,8 @@ static int is_usable_interior_route(struct bgp_path_info *bpi_interior) */ static void vnc_import_bgp_exterior_add_route_it( struct bgp *bgp, /* exterior instance, we hope */ - struct prefix *prefix, /* unicast prefix */ - struct bgp_path_info *info, /* unicast info */ + const struct prefix *prefix, /* unicast prefix */ + struct bgp_path_info *info, /* unicast info */ struct rfapi_import_table *it_only) /* NULL, or limit to this IT */ { struct rfapi *h; @@ -1729,11 +1726,6 @@ static void vnc_import_bgp_exterior_add_route_it( return; } - if (!info->attr) { - vnc_zlog_debug_verbose("%s: no info, skipping", __func__); - return; - } - /* * Extract nexthop from exterior route * @@ -1797,7 +1789,7 @@ static void vnc_import_bgp_exterior_add_route_it( /* use local_pref from unicast route */ memset(&new_attr, 0, sizeof(struct attr)); - bgp_attr_dup(&new_attr, bpi_interior->attr); + new_attr = *bpi_interior->attr; if (info->attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF)) { new_attr.local_pref = @@ -1825,8 +1817,7 @@ static void vnc_import_bgp_exterior_add_route_it( RFAPI_MONITOR_EXTERIOR(rn)->source = skiplist_new( 0, NULL, - (void (*)(void *)) - prefix_free); + prefix_free_lists); agg_lock_node(rn); /* for skiplist */ } agg_lock_node(rn); /* for skiplist entry */ @@ -1860,9 +1851,9 @@ static void vnc_import_bgp_exterior_add_route_it( } void vnc_import_bgp_exterior_add_route( - struct bgp *bgp, /* exterior instance, we hope */ - struct prefix *prefix, /* unicast prefix */ - struct bgp_path_info *info) /* unicast info */ + struct bgp *bgp, /* exterior instance, we hope */ + const struct prefix *prefix, /* unicast prefix */ + struct bgp_path_info *info) /* unicast info */ { vnc_import_bgp_exterior_add_route_it(bgp, prefix, info, NULL); } @@ -1877,8 +1868,8 @@ void vnc_import_bgp_exterior_add_route( * right routes. */ void vnc_import_bgp_exterior_del_route( - struct bgp *bgp, struct prefix *prefix, /* unicast prefix */ - struct bgp_path_info *info) /* unicast info */ + struct bgp *bgp, const struct prefix *prefix, /* unicast prefix */ + struct bgp_path_info *info) /* unicast info */ { struct rfapi *h; struct rfapi_cfg *hc; @@ -1920,11 +1911,6 @@ void vnc_import_bgp_exterior_del_route( return; } - if (!info->attr) { - vnc_zlog_debug_verbose("%s: no info, skipping", __func__); - return; - } - /* * Extract nexthop from exterior route * @@ -2048,7 +2034,8 @@ void vnc_import_bgp_exterior_add_route_interior( struct agg_node *rn_interior, /* VPN IT node */ struct bgp_path_info *bpi_interior) /* VPN IT route */ { - afi_t afi = family2afi(rn_interior->p.family); + const struct prefix *p = agg_node_get_prefix(rn_interior); + afi_t afi = family2afi(p->family); struct agg_node *par; struct bgp_path_info *bpi_exterior; struct prefix *pfx_exterior; /* exterior pfx */ @@ -2078,13 +2065,8 @@ void vnc_import_bgp_exterior_add_route_interior( } /*debugging */ - { - char str_pfx[PREFIX_STRLEN]; - - prefix2str(&rn_interior->p, str_pfx, sizeof(str_pfx)); - vnc_zlog_debug_verbose("%s: interior prefix=%s, bpi type=%d", - __func__, str_pfx, bpi_interior->type); - } + vnc_zlog_debug_verbose("%s: interior prefix=%pRN, bpi type=%d", + __func__, rn_interior, bpi_interior->type); if (RFAPI_HAS_MONITOR_EXTERIOR(rn_interior)) { @@ -2128,7 +2110,7 @@ void vnc_import_bgp_exterior_add_route_interior( /* use local_pref from unicast route */ memset(&new_attr, 0, sizeof(struct attr)); - bgp_attr_dup(&new_attr, bpi_interior->attr); + new_attr = *bpi_interior->attr; if (bpi_exterior && (bpi_exterior->attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))) { @@ -2199,7 +2181,7 @@ void vnc_import_bgp_exterior_add_route_interior( rfapiUnicastNexthop2Prefix(afi, bpi_exterior->attr, &pfx_nexthop); - if (prefix_match(&rn_interior->p, &pfx_nexthop)) { + if (prefix_match(p, &pfx_nexthop)) { struct bgp_path_info *bpi; struct prefix_rd *prd; @@ -2217,8 +2199,7 @@ void vnc_import_bgp_exterior_add_route_interior( ->source) { RFAPI_MONITOR_EXTERIOR(rn_interior) ->source = skiplist_new( - 0, NULL, - (void (*)(void *))prefix_free); + 0, NULL, prefix_free_lists); agg_lock_node(rn_interior); } skiplist_insert( @@ -2265,7 +2246,7 @@ void vnc_import_bgp_exterior_add_route_interior( /* use local_pref from unicast route */ memset(&new_attr, 0, sizeof(struct attr)); - bgp_attr_dup(&new_attr, bpi_interior->attr); + new_attr = *bpi_interior->attr; if (bpi_exterior && (bpi_exterior->attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))) { @@ -2343,7 +2324,7 @@ void vnc_import_bgp_exterior_add_route_interior( rfapiUnicastNexthop2Prefix(afi, bpi_exterior->attr, &pfx_nexthop); - if (prefix_match(&rn_interior->p, &pfx_nexthop)) { + if (prefix_match(p, &pfx_nexthop)) { struct prefix_rd *prd; struct attr new_attr; @@ -2360,8 +2341,7 @@ void vnc_import_bgp_exterior_add_route_interior( if (!RFAPI_MONITOR_EXTERIOR(rn_interior)->source) { RFAPI_MONITOR_EXTERIOR(rn_interior)->source = skiplist_new( - 0, NULL, - (void (*)(void *))prefix_free); + 0, NULL, prefix_free_lists); agg_lock_node(rn_interior); /* sl */ } skiplist_insert( @@ -2386,7 +2366,7 @@ void vnc_import_bgp_exterior_add_route_interior( /* use local_pref from unicast route */ memset(&new_attr, 0, sizeof(struct attr)); - bgp_attr_dup(&new_attr, bpi_interior->attr); + new_attr = *bpi_interior->attr; if (bpi_exterior && (bpi_exterior->attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))) { @@ -2432,7 +2412,8 @@ void vnc_import_bgp_exterior_del_route_interior( struct agg_node *rn_interior, /* VPN IT node */ struct bgp_path_info *bpi_interior) /* VPN IT route */ { - afi_t afi = family2afi(rn_interior->p.family); + const struct prefix *p = agg_node_get_prefix(rn_interior); + afi_t afi = family2afi(p->family); struct agg_node *par; struct bgp_path_info *bpi_exterior; struct prefix *pfx_exterior; /* exterior pfx */ @@ -2466,14 +2447,8 @@ void vnc_import_bgp_exterior_del_route_interior( } /*debugging */ - { - char str_pfx[PREFIX_STRLEN]; - - prefix2str(&rn_interior->p, str_pfx, sizeof(str_pfx)); - - vnc_zlog_debug_verbose("%s: interior prefix=%s, bpi type=%d", - __func__, str_pfx, bpi_interior->type); - } + vnc_zlog_debug_verbose("%s: interior prefix=%pRN, bpi type=%d", + __func__, rn_interior, bpi_interior->type); /* * Remove constructed routes based on the deleted interior route @@ -2550,8 +2525,7 @@ void vnc_import_bgp_exterior_del_route_interior( if (!RFAPI_MONITOR_EXTERIOR(par)->source) { RFAPI_MONITOR_EXTERIOR(par)->source = skiplist_new( - 0, NULL, - (void (*)(void *))prefix_free); + 0, NULL, prefix_free_lists); agg_lock_node(par); /* sl */ } skiplist_insert(RFAPI_MONITOR_EXTERIOR(par)->source, @@ -2577,7 +2551,7 @@ void vnc_import_bgp_exterior_del_route_interior( /* use local_pref from unicast route */ memset(&new_attr, 0, sizeof(struct attr)); - bgp_attr_dup(&new_attr, bpi->attr); + new_attr = *bpi->attr; if (bpi_exterior && (bpi_exterior->attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))) { @@ -2621,7 +2595,7 @@ void vnc_import_bgp_exterior_del_route_interior( * Generic add/delete unicast routes ***********************************************************************/ -void vnc_import_bgp_add_route(struct bgp *bgp, struct prefix *prefix, +void vnc_import_bgp_add_route(struct bgp *bgp, const struct prefix *prefix, struct bgp_path_info *info) { afi_t afi = family2afi(prefix->family); @@ -2690,7 +2664,7 @@ void vnc_import_bgp_add_route(struct bgp *bgp, struct prefix *prefix, /* * "Withdrawing a Route" import process */ -void vnc_import_bgp_del_route(struct bgp *bgp, struct prefix *prefix, +void vnc_import_bgp_del_route(struct bgp *bgp, const struct prefix *prefix, struct bgp_path_info *info) /* unicast info */ { afi_t afi = family2afi(prefix->family); @@ -2763,7 +2737,7 @@ void vnc_import_bgp_redist_enable(struct bgp *bgp, afi_t afi) /* iterate over bgp unicast v4 and v6 routes, call * vnc_import_bgp_add_route */ - struct bgp_node *rn; + struct bgp_dest *dest; vnc_zlog_debug_verbose("%s: entry, afi=%d", __func__, afi); @@ -2775,18 +2749,19 @@ void vnc_import_bgp_redist_enable(struct bgp *bgp, afi_t afi) } bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT] = 1; - for (rn = bgp_table_top(bgp->rib[afi][SAFI_UNICAST]); rn; - rn = bgp_route_next(rn)) { + for (dest = bgp_table_top(bgp->rib[afi][SAFI_UNICAST]); dest; + dest = bgp_route_next(dest)) { struct bgp_path_info *bpi; - for (bpi = bgp_node_get_bgp_path_info(rn); bpi; + for (bpi = bgp_dest_get_bgp_path_info(dest); bpi; bpi = bpi->next) { if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) continue; - vnc_import_bgp_add_route(bgp, &rn->p, bpi); + vnc_import_bgp_add_route(bgp, bgp_dest_get_prefix(dest), + bpi); } } vnc_zlog_debug_verbose( @@ -2797,7 +2772,7 @@ void vnc_import_bgp_redist_enable(struct bgp *bgp, afi_t afi) void vnc_import_bgp_exterior_redist_enable(struct bgp *bgp, afi_t afi) { struct bgp *bgp_exterior; - struct bgp_node *rn; + struct bgp_dest *dest; bgp_exterior = bgp->rfapi_cfg->redist_bgp_exterior_view; @@ -2816,19 +2791,19 @@ void vnc_import_bgp_exterior_redist_enable(struct bgp *bgp, afi_t afi) return; } - for (rn = bgp_table_top(bgp_exterior->rib[afi][SAFI_UNICAST]); rn; - rn = bgp_route_next(rn)) { + for (dest = bgp_table_top(bgp_exterior->rib[afi][SAFI_UNICAST]); dest; + dest = bgp_route_next(dest)) { struct bgp_path_info *bpi; - for (bpi = bgp_node_get_bgp_path_info(rn); bpi; + for (bpi = bgp_dest_get_bgp_path_info(dest); bpi; bpi = bpi->next) { if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) continue; - vnc_import_bgp_exterior_add_route(bgp_exterior, &rn->p, - bpi); + vnc_import_bgp_exterior_add_route( + bgp_exterior, bgp_dest_get_prefix(dest), bpi); } } vnc_zlog_debug_verbose( @@ -2843,7 +2818,7 @@ void vnc_import_bgp_exterior_redist_enable_it( struct bgp *bgp, afi_t afi, struct rfapi_import_table *it_only) { struct bgp *bgp_exterior; - struct bgp_node *rn; + struct bgp_dest *dest; vnc_zlog_debug_verbose("%s: entry", __func__); @@ -2862,19 +2837,20 @@ void vnc_import_bgp_exterior_redist_enable_it( return; } - for (rn = bgp_table_top(bgp_exterior->rib[afi][SAFI_UNICAST]); rn; - rn = bgp_route_next(rn)) { + for (dest = bgp_table_top(bgp_exterior->rib[afi][SAFI_UNICAST]); dest; + dest = bgp_route_next(dest)) { struct bgp_path_info *bpi; - for (bpi = bgp_node_get_bgp_path_info(rn); bpi; + for (bpi = bgp_dest_get_bgp_path_info(dest); bpi; bpi = bpi->next) { if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) continue; vnc_import_bgp_exterior_add_route_it( - bgp_exterior, &rn->p, bpi, it_only); + bgp_exterior, bgp_dest_get_prefix(dest), bpi, + it_only); } } } @@ -2886,8 +2862,8 @@ void vnc_import_bgp_redist_disable(struct bgp *bgp, afi_t afi) * iterate over vpn routes, find routes of type ZEBRA_ROUTE_BGP_DIRECT, * delete (call timer expire immediately) */ - struct bgp_node *rn1; - struct bgp_node *rn2; + struct bgp_dest *dest1; + struct bgp_dest *dest2; vnc_zlog_debug_verbose("%s: entry", __func__); @@ -2902,60 +2878,52 @@ void vnc_import_bgp_redist_disable(struct bgp *bgp, afi_t afi) * Two-level table for SAFI_MPLS_VPN * Be careful when changing the things we iterate over */ - for (rn1 = bgp_table_top(bgp->rib[afi][SAFI_MPLS_VPN]); rn1; - rn1 = bgp_route_next(rn1)) { - - if (bgp_node_has_bgp_path_info_data(rn1)) { + for (dest1 = bgp_table_top(bgp->rib[afi][SAFI_MPLS_VPN]); dest1; + dest1 = bgp_route_next(dest1)) { + const struct prefix *dest1_p; - for (rn2 = bgp_table_top( - bgp_node_get_bgp_table_info(rn1)); - rn2; rn2 = bgp_route_next(rn2)) { - - struct bgp_path_info *bpi; - struct bgp_path_info *nextbpi; - - for (bpi = bgp_node_get_bgp_path_info(rn2); bpi; - bpi = nextbpi) { - - nextbpi = bpi->next; + if (!bgp_dest_has_bgp_path_info_data(dest1)) + continue; - if (bpi->type - == ZEBRA_ROUTE_BGP_DIRECT) { + dest1_p = bgp_dest_get_prefix(dest1); + for (dest2 = bgp_table_top(bgp_dest_get_bgp_table_info(dest1)); + dest2; dest2 = bgp_route_next(dest2)) { + const struct prefix *dest2_p = + bgp_dest_get_prefix(dest2); + struct bgp_path_info *bpi; + struct bgp_path_info *nextbpi; - struct rfapi_descriptor *rfd; - vncHDBgpDirect.peer = bpi->peer; + for (bpi = bgp_dest_get_bgp_path_info(dest2); bpi; + bpi = nextbpi) { - assert(bpi->extra); + nextbpi = bpi->next; - rfd = bpi->extra->vnc.export - .rfapi_handle; + if (bpi->type != ZEBRA_ROUTE_BGP_DIRECT) + continue; - vnc_zlog_debug_verbose( - "%s: deleting bpi=%p, bpi->peer=%p, bpi->type=%d, bpi->sub_type=%d, bpi->extra->vnc.export.rfapi_handle=%p [passing rfd=%p]", - __func__, bpi, - bpi->peer, bpi->type, - bpi->sub_type, - (bpi->extra - ? bpi->extra - ->vnc - .export - .rfapi_handle - : NULL), - rfd); + struct rfapi_descriptor *rfd; + vncHDBgpDirect.peer = bpi->peer; + assert(bpi->extra); - del_vnc_route( - rfd, bpi->peer, bgp, - SAFI_MPLS_VPN, &rn2->p, - (struct prefix_rd *)&rn1 - ->p, - bpi->type, - bpi->sub_type, NULL, - 1); /* kill */ + rfd = bpi->extra->vnc.export.rfapi_handle; - vncHDBgpDirect.peer = NULL; - } - } + vnc_zlog_debug_verbose( + "%s: deleting bpi=%p, bpi->peer=%p, bpi->type=%d, bpi->sub_type=%d, bpi->extra->vnc.export.rfapi_handle=%p [passing rfd=%p]", + __func__, bpi, bpi->peer, bpi->type, + bpi->sub_type, + (bpi->extra ? bpi->extra->vnc.export + .rfapi_handle + : NULL), + rfd); + + del_vnc_route(rfd, bpi->peer, bgp, + SAFI_MPLS_VPN, dest2_p, + (struct prefix_rd *)dest1_p, + bpi->type, bpi->sub_type, NULL, + 1); /* kill */ + + vncHDBgpDirect.peer = NULL; } } } @@ -2999,20 +2967,21 @@ void vnc_import_bgp_exterior_redist_disable(struct bgp *bgp, afi_t afi) { - struct bgp_node *rn; - for (rn = bgp_table_top(bgp_exterior->rib[afi][SAFI_UNICAST]); - rn; rn = bgp_route_next(rn)) { + struct bgp_dest *dest; + for (dest = bgp_table_top(bgp_exterior->rib[afi][SAFI_UNICAST]); + dest; dest = bgp_route_next(dest)) { struct bgp_path_info *bpi; - for (bpi = bgp_node_get_bgp_path_info(rn); bpi; + for (bpi = bgp_dest_get_bgp_path_info(dest); bpi; bpi = bpi->next) { if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) continue; - vnc_import_bgp_exterior_del_route(bgp_exterior, - &rn->p, bpi); + vnc_import_bgp_exterior_del_route( + bgp_exterior, bgp_dest_get_prefix(dest), + bpi); } } #if DEBUG_RHN_LIST diff --git a/bgpd/rfapi/vnc_import_bgp.h b/bgpd/rfapi/vnc_import_bgp.h index 3db6f4010a..ab2ec1a748 100644 --- a/bgpd/rfapi/vnc_import_bgp.h +++ b/bgpd/rfapi/vnc_import_bgp.h @@ -32,12 +32,14 @@ extern uint32_t calc_local_pref(struct attr *attr, struct peer *peer); -extern int vnc_prefix_cmp(void *pfx1, void *pfx2); +extern int vnc_prefix_cmp(const void *pfx1, const void *pfx2); -extern void vnc_import_bgp_add_route(struct bgp *bgp, struct prefix *prefix, +extern void vnc_import_bgp_add_route(struct bgp *bgp, + const struct prefix *prefix, struct bgp_path_info *info); -extern void vnc_import_bgp_del_route(struct bgp *bgp, struct prefix *prefix, +extern void vnc_import_bgp_del_route(struct bgp *bgp, + const struct prefix *prefix, struct bgp_path_info *info); extern void vnc_import_bgp_redist_enable(struct bgp *bgp, afi_t afi); @@ -51,23 +53,23 @@ extern void vnc_import_bgp_exterior_redist_disable(struct bgp *bgp, afi_t afi); extern void vnc_import_bgp_exterior_add_route( struct bgp *bgp, /* exterior instance, we hope */ - struct prefix *prefix, /* unicast prefix */ + const struct prefix *prefix, /* unicast prefix */ struct bgp_path_info *info); /* unicast info */ extern void vnc_import_bgp_exterior_del_route( - struct bgp *bgp, struct prefix *prefix, /* unicast prefix */ - struct bgp_path_info *info); /* unicast info */ + struct bgp *bgp, const struct prefix *prefix, /* unicast prefix */ + struct bgp_path_info *info); /* unicast info */ extern void vnc_import_bgp_add_vnc_host_route_mode_resolve_nve( struct bgp *bgp, struct prefix_rd *prd, /* RD */ struct bgp_table *table_rd, /* per-rd VPN route table */ - struct prefix *prefix, /* VPN prefix */ + const struct prefix *prefix, /* VPN prefix */ struct bgp_path_info *bpi); /* new VPN host route */ extern void vnc_import_bgp_del_vnc_host_route_mode_resolve_nve( struct bgp *bgp, struct prefix_rd *prd, /* RD */ struct bgp_table *table_rd, /* per-rd VPN route table */ - struct prefix *prefix, /* VPN prefix */ + const struct prefix *prefix, /* VPN prefix */ struct bgp_path_info *bpi); /* old VPN host route */ #endif /* _QUAGGA_RFAPI_VNC_IMPORT_BGP_H_ */ diff --git a/bgpd/rfapi/vnc_zebra.c b/bgpd/rfapi/vnc_zebra.c index b08e922962..008c7b28b7 100644 --- a/bgpd/rfapi/vnc_zebra.c +++ b/bgpd/rfapi/vnc_zebra.c @@ -191,8 +191,7 @@ static void vnc_redistribute_add(struct prefix *p, uint32_t metric, * is not strictly necessary, but serves as a reminder * to those who may meddle... */ - pthread_mutex_lock(&vncHD1VR.peer->io_mtx); - { + frr_with_mutex(&vncHD1VR.peer->io_mtx) { // we don't need any I/O related facilities if (vncHD1VR.peer->ibuf) stream_fifo_free(vncHD1VR.peer->ibuf); @@ -209,7 +208,6 @@ static void vnc_redistribute_add(struct prefix *p, uint32_t metric, vncHD1VR.peer->obuf_work = NULL; vncHD1VR.peer->ibuf_work = NULL; } - pthread_mutex_unlock(&vncHD1VR.peer->io_mtx); /* base code assumes have valid host pointer */ vncHD1VR.peer->host = @@ -288,8 +286,8 @@ static void vnc_redistribute_withdraw(struct bgp *bgp, afi_t afi, uint8_t type) { struct prefix_rd prd; struct bgp_table *table; - struct bgp_node *prn; - struct bgp_node *rn; + struct bgp_dest *pdest; + struct bgp_dest *dest; vnc_zlog_debug_verbose("%s: entry", __func__); @@ -304,23 +302,26 @@ static void vnc_redistribute_withdraw(struct bgp *bgp, afi_t afi, uint8_t type) /* * Loop over all the RDs */ - for (prn = bgp_table_top(bgp->rib[afi][SAFI_MPLS_VPN]); prn; - prn = bgp_route_next(prn)) { + for (pdest = bgp_table_top(bgp->rib[afi][SAFI_MPLS_VPN]); pdest; + pdest = bgp_route_next(pdest)) { + const struct prefix *pdest_p = bgp_dest_get_prefix(pdest); + memset(&prd, 0, sizeof(prd)); prd.family = AF_UNSPEC; prd.prefixlen = 64; - memcpy(prd.val, prn->p.u.val, 8); + memcpy(prd.val, pdest_p->u.val, 8); /* This is the per-RD table of prefixes */ - table = bgp_node_get_bgp_table_info(prn); + table = bgp_dest_get_bgp_table_info(pdest); if (!table) continue; - for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) { + for (dest = bgp_table_top(table); dest; + dest = bgp_route_next(dest)) { struct bgp_path_info *ri; - for (ri = bgp_node_get_bgp_path_info(rn); ri; + for (ri = bgp_dest_get_bgp_path_info(dest); ri; ri = ri->next) { if (ri->type == type) { /* has matching redist type */ @@ -331,7 +332,7 @@ static void vnc_redistribute_withdraw(struct bgp *bgp, afi_t afi, uint8_t type) del_vnc_route( &vncHD1VR, /* use dummy ptr as cookie */ vncHD1VR.peer, bgp, SAFI_MPLS_VPN, - &(rn->p), &prd, type, + bgp_dest_get_prefix(dest), &prd, type, BGP_ROUTE_REDISTRIBUTE, NULL, 0); } } @@ -344,8 +345,7 @@ static void vnc_redistribute_withdraw(struct bgp *bgp, afi_t afi, uint8_t type) * * Assumes 1 nexthop */ -static int vnc_zebra_read_route(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int vnc_zebra_read_route(ZAPI_CALLBACK_ARGS) { struct zapi_route api; int add; @@ -357,7 +357,7 @@ static int vnc_zebra_read_route(int command, struct zclient *zclient, if (CHECK_FLAG(api.message, ZAPI_MESSAGE_SRCPFX)) return 0; - add = (command == ZEBRA_REDISTRIBUTE_ROUTE_ADD); + add = (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD); if (add) vnc_redistribute_add(&api.prefix, api.metric, api.type); else @@ -383,7 +383,7 @@ static int vnc_zebra_read_route(int command, struct zclient *zclient, /* * low-level message builder */ -static void vnc_zebra_route_msg(struct prefix *p, unsigned int nhp_count, +static void vnc_zebra_route_msg(const struct prefix *p, unsigned int nhp_count, void *nhp_ary, int add) /* 1 = add, 0 = del */ { struct zapi_route api; @@ -563,7 +563,7 @@ static void vnc_zebra_add_del_prefix(struct bgp *bgp, int add) /* !0 = add, 0 = del */ { struct list *nves; - + const struct prefix *p = agg_node_get_prefix(rn); unsigned int nexthop_count = 0; void *nh_ary = NULL; void *nhp_ary = NULL; @@ -573,15 +573,15 @@ static void vnc_zebra_add_del_prefix(struct bgp *bgp, if (zclient_vnc->sock < 0) return; - if (rn->p.family != AF_INET && rn->p.family != AF_INET6) { + if (p->family != AF_INET && p->family != AF_INET6) { flog_err(EC_LIB_DEVELOPMENT, "%s: invalid route node addr family", __func__); return; } - if (!vrf_bitmap_check(zclient_vnc->redist[family2afi(rn->p.family)] - [ZEBRA_ROUTE_VNC], - VRF_DEFAULT)) + if (!vrf_bitmap_check( + zclient_vnc->redist[family2afi(p->family)][ZEBRA_ROUTE_VNC], + VRF_DEFAULT)) return; if (!bgp->rfapi_cfg) { @@ -595,17 +595,16 @@ static void vnc_zebra_add_del_prefix(struct bgp *bgp, return; } - import_table_to_nve_list_zebra(bgp, import_table, &nves, rn->p.family); + import_table_to_nve_list_zebra(bgp, import_table, &nves, p->family); if (nves) { - nve_list_to_nh_array(rn->p.family, nves, &nexthop_count, - &nh_ary, &nhp_ary); + nve_list_to_nh_array(p->family, nves, &nexthop_count, &nh_ary, + &nhp_ary); list_delete(&nves); if (nexthop_count) - vnc_zebra_route_msg(&rn->p, nexthop_count, nhp_ary, - add); + vnc_zebra_route_msg(p, nexthop_count, nhp_ary, add); } XFREE(MTYPE_TMP, nhp_ary); @@ -698,15 +697,14 @@ static void vnc_zebra_add_del_nve(struct bgp *bgp, struct rfapi_descriptor *rfd, */ for (rn = agg_route_top(rt); rn; rn = agg_route_next(rn)) { - - if (rn->info) { - - vnc_zlog_debug_verbose( - "%s: sending %s", __func__, - (add ? "add" : "del")); - vnc_zebra_route_msg(&rn->p, 1, &pAddr, - add); - } + if (!rn->info) + continue; + + vnc_zlog_debug_verbose("%s: sending %s", + __func__, + (add ? "add" : "del")); + vnc_zebra_route_msg(agg_node_get_prefix(rn), 1, + &pAddr, add); } } } @@ -781,9 +779,9 @@ static void vnc_zebra_add_del_group_afi(struct bgp *bgp, for (rn = agg_route_top(rt); rn; rn = agg_route_next(rn)) { if (rn->info) { - vnc_zebra_route_msg(&rn->p, - nexthop_count, - nhp_ary, add); + vnc_zebra_route_msg( + agg_node_get_prefix(rn), + nexthop_count, nhp_ary, add); } } } diff --git a/bgpd/subdir.am b/bgpd/subdir.am index d281fe4e59..a5393e25ac 100644 --- a/bgpd/subdir.am +++ b/bgpd/subdir.am @@ -12,28 +12,30 @@ dist_examples_DATA += \ bgpd/bgpd.conf.vnc.sample \ # end vtysh_scan += \ - $(top_srcdir)/bgpd/bgp_bfd.c \ - $(top_srcdir)/bgpd/bgp_debug.c \ - $(top_srcdir)/bgpd/bgp_dump.c \ - $(top_srcdir)/bgpd/bgp_evpn_vty.c \ - $(top_srcdir)/bgpd/bgp_filter.c \ - $(top_srcdir)/bgpd/bgp_mplsvpn.c \ - $(top_srcdir)/bgpd/bgp_nexthop.c \ - $(top_srcdir)/bgpd/bgp_route.c \ - $(top_srcdir)/bgpd/bgp_routemap.c \ - $(top_srcdir)/bgpd/bgp_vty.c \ - $(top_srcdir)/bgpd/bgp_flowspec_vty.c \ + bgpd/bgp_bfd.c \ + bgpd/bgp_debug.c \ + bgpd/bgp_dump.c \ + bgpd/bgp_evpn_mh.c \ + bgpd/bgp_evpn_vty.c \ + bgpd/bgp_filter.c \ + bgpd/bgp_mplsvpn.c \ + bgpd/bgp_nexthop.c \ + bgpd/bgp_route.c \ + bgpd/bgp_routemap.c \ + bgpd/bgp_vty.c \ + bgpd/bgp_flowspec_vty.c \ # end # can be loaded as DSO - always include for vtysh -vtysh_scan += $(top_srcdir)/bgpd/bgp_rpki.c +vtysh_scan += bgpd/bgp_rpki.c +vtysh_scan += bgpd/bgp_bmp.c if ENABLE_BGP_VNC vtysh_scan += \ - $(top_srcdir)/bgpd/rfapi/bgp_rfapi_cfg.c \ - $(top_srcdir)/bgpd/rfapi/rfapi.c \ - $(top_srcdir)/bgpd/rfapi/rfapi_vty.c \ - $(top_srcdir)/bgpd/rfapi/vnc_debug.c \ + bgpd/rfapi/bgp_rfapi_cfg.c \ + bgpd/rfapi/rfapi.c \ + bgpd/rfapi/rfapi_vty.c \ + bgpd/rfapi/vnc_debug.c \ # end endif if SNMP @@ -42,7 +44,10 @@ endif if RPKI module_LTLIBRARIES += bgpd/bgpd_rpki.la endif -man8 += $(MANBUILD)/bgpd.8 +if BGP_BMP +module_LTLIBRARIES += bgpd/bgpd_bmp.la +endif +man8 += $(MANBUILD)/frr-bgpd.8 endif bgpd_libbgp_a_SOURCES = \ @@ -61,6 +66,7 @@ bgpd_libbgp_a_SOURCES = \ bgpd/bgp_encap_tlv.c \ bgpd/bgp_errors.c \ bgpd/bgp_evpn.c \ + bgpd/bgp_evpn_mh.c \ bgpd/bgp_evpn_vty.c \ bgpd/bgp_filter.c \ bgpd/bgp_flowspec.c \ @@ -129,11 +135,13 @@ noinst_HEADERS += \ bgpd/bgp_damp.h \ bgpd/bgp_debug.h \ bgpd/bgp_dump.h \ + bgpd/bgp_bmp.h \ bgpd/bgp_ecommunity.h \ bgpd/bgp_encap_tlv.h \ bgpd/bgp_encap_types.h \ bgpd/bgp_errors.h \ bgpd/bgp_evpn.h \ + bgpd/bgp_evpn_mh.h \ bgpd/bgp_evpn_private.h \ bgpd/bgp_evpn_vty.h \ bgpd/bgp_filter.h \ @@ -194,14 +202,6 @@ bgpd_bgp_btoa_SOURCES = bgpd/bgp_btoa.c bgpd_bgpd_CFLAGS = $(AM_CFLAGS) bgpd_bgp_btoa_CFLAGS = $(AM_CFLAGS) -if ENABLE_BGP_VNC -bgpd_bgpd_SOURCES += bgpd/rfapi/rfapi_descriptor_rfp_utils.c -bgpd_bgpd_CFLAGS += -Irfapi -I@top_srcdir@/$(RFPINC) - -bgpd_bgp_btoa_SOURCES += bgpd/rfapi/rfapi_descriptor_rfp_utils.c -bgpd_bgp_btoa_CFLAGS += -Irfapi -I@top_srcdir@/$(RFPINC) -endif - # RFPLDADD is set in bgpd/rfp-example/librfp/subdir.am bgpd_bgpd_LDADD = bgpd/libbgp.a $(RFPLDADD) lib/libfrr.la $(LIBCAP) $(LIBM) bgpd_bgp_btoa_LDADD = bgpd/libbgp.a $(RFPLDADD) lib/libfrr.la $(LIBCAP) $(LIBM) @@ -216,16 +216,16 @@ bgpd_bgpd_rpki_la_CFLAGS = $(WERROR) $(RTRLIB_CFLAGS) bgpd_bgpd_rpki_la_LDFLAGS = -avoid-version -module -shared -export-dynamic bgpd_bgpd_rpki_la_LIBADD = $(RTRLIB_LIBS) -bgpd/bgp_evpn_vty_clippy.c: $(CLIPPY_DEPS) -bgpd/bgp_evpn_vty.$(OBJEXT): bgpd/bgp_evpn_vty_clippy.c -bgpd/bgp_vty_clippy.c: $(CLIPPY_DEPS) -bgpd/bgp_vty.$(OBJEXT): bgpd/bgp_vty_clippy.c -bgpd/bgp_route_clippy.c: $(CLIPPY_DEPS) -bgpd/bgp_route.$(OBJEXT): bgpd/bgp_route_clippy.c -bgpd/bgp_debug_clippy.c: $(CLIPPY_DEPS) -bgpd/bgp_debug.$(OBJEXT): bgpd/bgp_debug_clippy.c -bgpd/bgp_routemap_clippy.c: $(CLIPPY_DEPS) -bgpd/bgp_routemap.$(OBJEXT): bgpd/bgp_routemap_clippy.c -bgpd/bgp_rpki_clippy.c: $(CLIPPY_DEPS) -$(AUTOMAKE_DUMMY)bgpd/bgpd_bgpd_rpki_la-bgp_rpki.lo: bgpd/bgp_rpki_clippy.c -$(AUTOMAKE_DUMMY)bgpd/bgpd_rpki_la-bgp_rpki.lo: bgpd/bgp_rpki_clippy.c +bgpd_bgpd_bmp_la_SOURCES = bgpd/bgp_bmp.c +bgpd_bgpd_bmp_la_LIBADD = lib/libfrrcares.la +bgpd_bgpd_bmp_la_LDFLAGS = -avoid-version -module -shared -export-dynamic + +clippy_scan += \ + bgpd/bgp_bmp.c \ + bgpd/bgp_debug.c \ + bgpd/bgp_evpn_vty.c \ + bgpd/bgp_route.c \ + bgpd/bgp_routemap.c \ + bgpd/bgp_rpki.c \ + bgpd/bgp_vty.c \ + # end diff --git a/bgpd/valgrind.supp b/bgpd/valgrind.supp index 7a25c88363..31f2477a58 100644 --- a/bgpd/valgrind.supp +++ b/bgpd/valgrind.supp @@ -1,9 +1,18 @@ { - + Memcheck:Leak + match-leak-kinds: reachable + fun:calloc + fun:qcalloc + fun:zlog_target_clone +} +{ + + Memcheck:Leak + match-leak-kinds: reachable fun:calloc fun:_dlerror_run fun:dlopen@@GLIBC_2.2.5 - fun:ly_load_plugins_dir + obj:/usr/lib/x86_64-linux-gnu/libyang.so.1.9.2 fun:ly_load_plugins } diff --git a/changelog-auto.in b/changelog-auto.in index 127d7fe147..24876bb267 100644 --- a/changelog-auto.in +++ b/changelog-auto.in @@ -4,7 +4,160 @@ frr (@VERSION@-0) UNRELEASED; urgency=medium remove and replace when creating releases! (tools/tarsource.sh will handle this) - -- FRRouting-Dev Thu, 25 Oct 2018 16:36:50 +0200 + -- FRRouting-Dev Thu, 4 Mar 2021 00:10:00 +0100 + +frr (7.5.1-0) RELEASED; urgency=medium + BABEL + Fix connected route leak on change + BFD + Session lookup was sometimes wrong + Memory leak and handling cleanups + In some situations handle vrf appropriately when receiving packets + BGP + Peer Group Inheritance Fixes + Dissallow attempt to peer peers reachable via blackholes + Send BMP down message when reachability fails + Cleanup handling of aggregator data when the AGG AS is 0 + Handle `neighbor Wed, 3 Mar 2021 23:56:00 +0100 + +frr (7.5-0) RELEASED; urgency=medium + BFD + Profile support + Minimum ttl support + BGP + rpki VRF support + GR fixes + Add wide option to display of routes + Add `maximum-prefix force` + Add `bestpath-routes` to neighbor command + Add `bgp shutdown message MSG...` command + Add v6 Flowspec support + Add `neighbor shutdown rtt` command + Allow update-delay to be applied globaly + EVPN + Beginning of MultiHoming Support + ISIS + Segment Routing Support + VRF Support + Guard against adj timer display overflow + Add support for Anycast-SIDs + Add support for Topology Independent LFA (TI-LFA) + Add `lsp-gen-interval 2` to isis configuration + OSPF + Segment Routing support for ECMP + Various LSA fixes + Prevent crash if transferring config amongst instances + PBR + Adding json support to commands + DSCP/ECN based PBR Matching + PIM + Add more json support to commands + Fix missing mesh-group commands + MSDP SA forwarding + Clear (s,g,rpt) ifchannel on (*, G) prune received + Fix igmp querier election and IP address mapping + Crash fix when RP is removed + STATIC + Northbound Support + YANG + Filter and route-map Support + OSPF model definition + BGP model definition + VTYSH + Speed up output across daemons + Fix build-time errors for some --enable flags + Speed up output of configuration across daemons + ZEBRA + nexthop group support for FPM + northbound support for rib model + Backup nexthop support + netlink batching support + Allow upper level protocols to request ARP + Add json output for zebra ES, ES-EVI and access vlan dumps + + Upgrade to using libyang1.0.184 + + RPM + Moved RPKI to subpackage + Added SNMP subpackage + + As always there are too many bugfixes to list individually. This release + compromises just over 1k of commits by the community, with contributors from + 70 people. + + -- FRRouting-Dev Tue, 2 Nov 2020 08:04:23 +0400 frr (6.0-2) testing; urgency=medium diff --git a/configure.ac b/configure.ac index 18c16634ba..1e68c91a78 100755 --- a/configure.ac +++ b/configure.ac @@ -7,7 +7,7 @@ ## AC_PREREQ([2.60]) -AC_INIT([frr], [7.1-dev], [https://github.com/frrouting/frr/issues]) +AC_INIT([frr], [7.5.1], [https://github.com/frrouting/frr/issues]) PACKAGE_URL="https://frrouting.org/" AC_SUBST([PACKAGE_URL]) PACKAGE_FULLNAME="FRRouting" @@ -25,9 +25,25 @@ dnl ----------------------------------- AC_CANONICAL_BUILD() AC_CANONICAL_HOST() -AS_IF([test "$host" != "$build"], [ +hosttools_clippy="false" +build_clippy="true" + +dnl case 1: external clippy +if test -n "$with_clippy" -a "$with_clippy" != "no" -a "$with_clippy" != "yes"; then + if test "$enable_clippy_only" = "yes"; then + AC_MSG_ERROR([--enable-clippy-only does not make sense with --with-clippy]) + fi + + CLIPPY="$with_clippy" + build_clippy="false" + if test ! -x "$with_clippy"; then + AC_MSG_ERROR([clippy tool ($with_clippy) is not executable]) + fi + +dnl case 2: cross-compiling internal clippy +elif test "$host" != "$build"; then if test "$srcdir" = "."; then - AC_MSG_ERROR([cross-compilation is only possible with builddir separate from srcdir. create a separate directory and run as .../path-to-frr/configure.]) + AC_MSG_ERROR([cross-compilation is only possible with builddir separate from srcdir or by building clippy separately and using the --with-clippy option. create a separate directory and run as .../path-to-frr/configure.]) fi test -d hosttools || mkdir hosttools abssrc="`cd \"${srcdir}\"; pwd`" @@ -37,23 +53,47 @@ AS_IF([test "$host" != "$build"], [ AC_MSG_NOTICE([... use HOST_CPPFLAGS / HOST_CFLAGS / HOST_LDFLAGS if neccessary]) AC_MSG_NOTICE([...]) - ( CPPFLAGS="$HOST_CPPFLAGS"; \ - CFLAGS="$HOST_CFLAGS"; \ - LDFLAGS="$HOST_LDFLAGS"; \ - cd hosttools; "${abssrc}/configure" "--host=$build" "--build=$build" "--enable-clippy-only" "--disable-nhrpd" "--disable-vtysh"; ) + ( + for var in $ac_precious_vars; do + dnl special cases + case "$var" in + YACC|YFLAGS) continue;; + PYTHON*) retain=true;; + *) retain=false; + esac + + eval "hostvar=\"\${HOST_$var}\"" + eval "targetvar=\"\${$var}\"" + if test -n "$hostvar"; then + eval "$var='$hostvar'" + _AS_ECHO_LOG([host $var='$hostvar']) + elif $retain; then + _AS_ECHO_LOG([host retain $var='$targetvar']) + else + eval "unset $var" + _AS_ECHO_LOG([host unset $var]) + fi + done + cd hosttools + "${abssrc}/configure" "--host=$build" "--build=$build" "--enable-clippy-only" "--disable-nhrpd" "--disable-vtysh" + ) || exit 1 AC_MSG_NOTICE([...]) AC_MSG_NOTICE([... cross-compilation: finished self-configuring for build platform tools]) AC_MSG_NOTICE([...]) build_clippy="false" - HOSTTOOLS="hosttools/" -], [ - build_clippy="true" - HOSTTOOLS="" -]) -AC_SUBST([HOSTTOOLS]) + hosttools_clippy="true" + CLIPPY="hosttools/lib/clippy" + +dnl case 3: normal build internal clippy +else + CLIPPY="lib/clippy\$(EXEEXT)" +fi +AC_SUBST([CLIPPY]) AM_CONDITIONAL([BUILD_CLIPPY], [$build_clippy]) +AM_CONDITIONAL([HOSTTOOLS_CLIPPY], [$hosttools_clippy]) +AM_CONDITIONAL([ONLY_CLIPPY], [test "$enable_clippy_only" = "yes"]) # Disable portability warnings -- our automake code (in particular # common.am) uses some constructs specific to gmake. @@ -82,7 +122,7 @@ AC_ARG_ENABLE([pkgsrcrcdir], pkgsrcrcdir="$enableval",) dnl XXX add --pkgsrcrcdir to autoconf standard directory list somehow AC_SUBST([pkgsrcrcdir]) -AM_CONDITIONAL([PKGSRC], [test "x$pkgsrcrcdir" != "x"]) +AM_CONDITIONAL([PKGSRC], [test "$pkgsrcrcdir" != ""]) AC_ARG_WITH([moduledir], [AS_HELP_STRING([--with-moduledir=DIR], [module directory (${libdir}/frr/modules)])], [ moduledir="$withval" @@ -99,13 +139,6 @@ AC_ARG_WITH([yangmodelsdir], [AS_HELP_STRING([--with-yangmodelsdir=DIR], [yang m ]) AC_SUBST([yangmodelsdir]) -AC_ARG_WITH([libyang_pluginsdir], [AS_HELP_STRING([--with-libyang-pluginsdir=DIR], [yangmodule plugins directory (${libdir}/frr/libyang_plugins)])], [ - libyang_pluginsdir="$withval" -], [ - libyang_pluginsdir="\${libdir}/frr/libyang_plugins" -]) -AC_SUBST([libyang_pluginsdir]) - AC_ARG_ENABLE(tcmalloc, AS_HELP_STRING([--enable-tcmalloc], [Turn on tcmalloc]), [case "${enableval}" in @@ -126,34 +159,36 @@ dnl Check CC and friends dnl -------------------- dnl note orig_cflags is also used further down orig_cflags="$CFLAGS" +orig_cxxflags="$CXXFLAGS" AC_LANG([C]) AC_PROG_CC AC_PROG_CPP +AC_PROG_CXX AM_PROG_CC_C_O dnl remove autoconf default "-g -O2" CFLAGS="$orig_cflags" +CXXFLAGS="$orig_cxxflags" AC_PROG_CC_C99 dnl NB: see C11 below -AC_PROG_EGREP PKG_PROG_PKG_CONFIG -dnl autoconf 2.59 appears not to support AC_PROG_SED -dnl AC_PROG_SED -AC_CHECK_PROG([SED],[sed],[sed],[/bin/false]) +dnl it's 2019, sed is sed. +SED=sed +AC_SUBST([SED]) dnl try and enable CFLAGS that are useful for FRR dnl - specifically, options to control warnings AC_USE_SYSTEM_EXTENSIONS AC_DEFUN([AC_C_FLAG], [{ - m4_pushdef([cachename],[m4_translit([frr_cv_$1],[ =-+],[____])]) + m4_pushdef([cachename],[m4_translit([frr_cv_$1],[ =-+/{}$],[________])]) AC_CACHE_CHECK([[whether $CC supports $1]], cachename, [ AC_LANG_PUSH([C]) ac_c_flag_save="$CFLAGS" CFLAGS="$CFLAGS $1" AC_COMPILE_IFELSE( - [AC_LANG_PROGRAM([[]])], + [AC_LANG_PROGRAM([[$4]])], [ cachename=yes ], [ @@ -162,7 +197,7 @@ AC_DEFUN([AC_C_FLAG], [{ CFLAGS="$ac_c_flag_save" AC_LANG_POP([C]) ]) - if test "${cachename}" = yes; then + if test "$cachename" = "yes"; then m4_if([$3], [], [CFLAGS="$CFLAGS $1"], [$3]) else : @@ -180,15 +215,17 @@ AC_DEFUN([AC_LINK_IFELSE_FLAGS], [{ AC_LINK_IFELSE( [$3], [ - AC_MSG_RESULT([yes]) CFLAGS="$ac_cflags_save" LIBS="$ac_libs_save" - $5 + m4_default([$5], [ + AC_MSG_RESULT([yes]) + ]) ], [ - AC_MSG_RESULT([no]) CFLAGS="$ac_cflags_save" LIBS="$ac_libs_save" - $4 + m4_default([$4], [ + AC_MSG_RESULT([no]) + ]) ]) AC_LANG_POP([C]) }]) @@ -205,36 +242,48 @@ CC="${CC% -std=c99}" AC_C_FLAG([-std=gnu11], [CC="$ac_cc"], [CC="$CC -std=gnu11"]) dnl if the user has specified any CFLAGS, override our settings -if test "x${enable_gcov}" = "xyes"; then - if test "z$orig_cflags" = "z"; then +if test "$enable_gcov" = "yes"; then + if test "$orig_cflags" = ""; then AC_C_FLAG([-coverage]) AC_C_FLAG([-O0]) fi LDFLAGS="${LDFLAGS} -lgcov" -elif test "x${enable_dev_build}" = "xyes"; then +fi + +if test "$enable_clang_coverage" = "yes"; then + AC_C_FLAG([-fprofile-instr-generate], [ + AC_MSG_ERROR([$CC does not support -fprofile-instr-generate.]) + ]) + AC_C_FLAG([-fcoverage-mapping], [ + AC_MSG_ERROR([$CC does not support -fcoverage-mapping.]) + ]) +fi + +if test "$enable_dev_build" = "yes"; then AC_DEFINE([DEV_BUILD], [1], [Build for development]) - if test "z$orig_cflags" = "z"; then + if test "$orig_cflags" = ""; then AC_C_FLAG([-g3]) AC_C_FLAG([-O0]) fi - if test "x${enable_lua}" = "xyes"; then - AC_CHECK_LIB([lua], [lua_newstate], - [LIBS="$LIBS -llua"]) - AC_DEFINE([HAVE_LUA], [1], [Lua enabled for development]) + if test "$enable_lua" = "yes"; then + AX_PROG_LUA([5.3]) + AX_LUA_HEADERS + AX_LUA_LIBS([ + AC_DEFINE([HAVE_LUA], [1], [Have support for Lua interpreter]) + LIBS="$LIBS $LUA_LIB" + ]) fi else - if test "x${enable_lua}" = "xyes"; then + if test "$enable_lua" = "yes"; then AC_MSG_ERROR([Lua is not meant to be built/used outside of development at this time]) fi - if test "z$orig_cflags" = "z"; then + if test "$orig_cflags" = ""; then AC_C_FLAG([-g]) - AC_C_FLAG([-Os], [ - AC_C_FLAG([-O2]) - ]) + AC_C_FLAG([-O2]) fi fi -AM_CONDITIONAL([DEV_BUILD], [test "x$enable_dev_build" = "xyes"]) +AM_CONDITIONAL([DEV_BUILD], [test "$enable_dev_build" = "yes"]) dnl always want these CFLAGS AC_C_FLAG([-fno-omit-frame-pointer]) @@ -246,7 +295,8 @@ AC_C_FLAG([-Wmissing-declarations]) AC_C_FLAG([-Wpointer-arith]) AC_C_FLAG([-Wbad-function-cast]) AC_C_FLAG([-Wwrite-strings]) -if test x"${enable_gcc_ultra_verbose}" = x"yes" ; then +AC_C_FLAG([-Wundef]) +if test "$enable_gcc_ultra_verbose" = "yes" ; then AC_C_FLAG([-Wcast-qual]) AC_C_FLAG([-Wstrict-prototypes]) AC_C_FLAG([-Wmissing-noreturn]) @@ -269,7 +319,7 @@ dnl for some reason the string consts get 'promoted' to char *, dnl triggering a const to non-const conversion warning. AC_C_FLAG([-diag-disable 3179]) -if test x"${enable_werror}" = x"yes" ; then +if test "$enable_werror" = "yes" ; then WERROR="-Werror" fi AC_SUBST([WERROR]) @@ -290,14 +340,59 @@ if test "$enable_thread_sanitizer" = "yes"; then ]) fi if test "$enable_memory_sanitizer" = "yes"; then - AC_C_FLAG([-fsanitize=thread -fPIE -pie], [ - AC_MSG_ERROR([$CC does not support Thread Sanitizer.]) + AC_C_FLAG([-fsanitize=memory -fPIE -pie], [ + AC_MSG_ERROR([$CC does not support Memory Sanitizer.]) + ], [ + SAN_FLAGS="$SAN_FLAGS -fsanitize=memory -fPIE -pie" + ]) +fi +if test "$enable_undefined_sanitizer" = "yes"; then + AC_C_FLAG([-fsanitize=undefined], [ + AC_MSG_ERROR([$CC does not support UndefinedBehaviorSanitizer.]) ], [ - SAN_FLAGS="-fsanitize=memory -fPIE -pie" + SAN_FLAGS="$SAN_FLAGS -fsanitize=undefined" ]) fi AC_SUBST([SAN_FLAGS]) +dnl frr-format.so +if test "$with_frr_format" != "no" -a "$with_frr_format" != "yes" -a -n "$with_frr_format"; then + AC_C_FLAG([-fplugin=${with_frr_format}], [ + AC_MSG_ERROR([specified frr-format plugin ($with_frr_format) does not work]) + ],,[ +#ifndef _FRR_ATTRIBUTE_PRINTFRR +#error plugin not loaded +#endif +#if _FRR_ATTRIBUTE_PRINTFRR < 0x10000 +#error plugin too old +#endif + ]) +elif test "$with_frr_format" = "no"; then + : #nothing +else + AC_C_FLAG([-fplugin=tools/gcc-plugins/frr-format.so],[ + AC_C_FLAG([-fplugin=frr-format],[ + if test "$with_frr_format" = "yes"; then + AC_MSG_ERROR([frr-format plugin requested but not found]) + fi + ],,[ +#ifndef _FRR_ATTRIBUTE_PRINTFRR +#error plugin not loaded +#endif +#if _FRR_ATTRIBUTE_PRINTFRR < 0x10000 +#error plugin too old +#endif + ]) + ],,[ +#ifndef _FRR_ATTRIBUTE_PRINTFRR +#error plugin not loaded +#endif +#if _FRR_ATTRIBUTE_PRINTFRR < 0x10000 +#error plugin too old +#endif + ]) +fi + dnl ---------- dnl Essentials dnl ---------- @@ -313,7 +408,7 @@ AX_PTHREAD([ AC_SEARCH_LIBS([pthread_condattr_setclock], [], [frr_cv_pthread_condattr_setclock=yes], [frr_cv_pthread_condattr_setclock=no]) -if test "$frr_cv_pthread_condattr_setclock" = yes; then +if test "$frr_cv_pthread_condattr_setclock" = "yes"; then AC_DEFINE([HAVE_PTHREAD_CONDATTR_SETCLOCK], [1], [Have pthread.h pthread_condattr_setclock]) fi @@ -322,7 +417,6 @@ dnl Check programs dnl -------------- AC_PROG_INSTALL AC_PROG_LN_S -AC_PROG_MAKE_SET AC_CHECK_TOOL([AR], [ar]) dnl ------- @@ -334,6 +428,9 @@ LT_INIT _LT_CONFIG_LIBTOOL([ patch -N -i "${srcdir}/m4/libtool-whole-archive.patch" libtool >&AS_MESSAGE_LOG_FD || \ AC_MSG_WARN([Could not patch libtool for static linking support. Loading modules into a statically linked daemon will fail.]) + sed -e 's%func_warning "relinking%true #\0%' -i libtool || true + sed -e 's%func_warning "remember to run%true #\0%' -i libtool || true + sed -e 's%func_warning ".*has not been installed in%true #\0%' -i libtool || true ]) if test "$enable_static_bin" = "yes"; then AC_LDFLAGS="-static" @@ -345,7 +442,7 @@ if test "$enable_shared" != "yes"; then AC_MSG_ERROR([FRR cannot be built with --disable-shared. If you want statically linked daemons, use --enable-shared --enable-static --enable-static-bin]) fi AC_SUBST([AC_LDFLAGS]) -AM_CONDITIONAL([STATIC_BIN], [test "x$enable_static_bin" = "xyes"]) +AM_CONDITIONAL([STATIC_BIN], [test "$enable_static_bin" = "yes"]) dnl $AR and $RANLIB are set by LT_INIT above AC_MSG_CHECKING([whether $AR supports D option]) @@ -363,14 +460,19 @@ AC_SUBST([ARFLAGS]) AC_SUBST([AR_FLAGS]) AC_MSG_CHECKING([whether $RANLIB supports D option]) -if $RANLIB -D conftest.a >/dev/null 2>/dev/null; then - AC_MSG_RESULT([yes]) - RANLIB="$RANLIB -D" +if $RANLIB -D conftest.a >conftest.err 2>&1; then + if grep -q -- '-D' conftest.err; then + AC_MSG_RESULT([no]) + else + AC_MSG_RESULT([yes]) + RANLIB="$RANLIB -D" + fi else AC_MSG_RESULT([no]) fi AC_SUBST([RANLIB]) +test -f conftest.err && rm conftest.err test -f conftest.a && rm conftest.a dnl ---------------------- @@ -391,7 +493,9 @@ AC_ARG_WITH([pkg-extra-version], ], []) AC_ARG_WITH([pkg-git-version], AS_HELP_STRING([--with-pkg-git-version], [add git information to MOTD and build version string]), - [ test "x$withval" != "xno" && with_pkg_git_version="yes" ]) + [ test "$withval" != "no" && with_pkg_git_version="yes" ]) +AC_ARG_WITH([clippy], + AS_HELP_STRING([--with-clippy=PATH], [use external clippy helper program])) AC_ARG_WITH([vtysh_pager], AS_HELP_STRING([--with-vtysh-pager=PAGER], [control what pager is compiled in as default]), VTYSH_PAGER=$withval, VTYSH_PAGER="more") @@ -435,10 +539,14 @@ AC_ARG_ENABLE([staticd], AS_HELP_STRING([--disable-staticd], [do not build staticd])) AC_ARG_ENABLE([fabricd], AS_HELP_STRING([--disable-fabricd], [do not build 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], + AS_HELP_STRING([--disable-bgp-bmp],[turn off BGP BMP support])) AC_ARG_ENABLE([snmp], AS_HELP_STRING([--enable-snmp], [enable SNMP support for agentx])) AC_ARG_ENABLE([config_rollbacks], @@ -447,6 +555,8 @@ AC_ARG_ENABLE([confd], AS_HELP_STRING([--enable-confd=ARG], [enable confd integration])) AC_ARG_ENABLE([sysrepo], AS_HELP_STRING([--enable-sysrepo], [enable sysrepo integration])) +AC_ARG_ENABLE([grpc], + AS_HELP_STRING([--enable-grpc], [enable the gRPC northbound plugin])) AC_ARG_ENABLE([zeromq], AS_HELP_STRING([--enable-zeromq], [enable ZeroMQ handler (libfrrzmq)])) AC_ARG_WITH([libpam], @@ -475,7 +585,7 @@ AC_ARG_ENABLE([realms], AC_ARG_ENABLE([rtadv], AS_HELP_STRING([--disable-rtadv], [disable IPV6 router advertisement feature])) AC_ARG_ENABLE([irdp], - AS_HELP_STRING([--disable-irdp], [enable IRDP server support in zebra (default if supported)])) + AS_HELP_STRING([--disable-irdp], [disable IRDP server support in zebra (enabled by default if supported)])) AC_ARG_ENABLE([capabilities], AS_HELP_STRING([--disable-capabilities], [disable using POSIX capabilities])) AC_ARG_ENABLE([rusage], @@ -483,9 +593,11 @@ 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], + AS_HELP_STRING([--disable-cpu-time], [disable cpu usage data gathering])) AC_ARG_ENABLE([pcreposix], AS_HELP_STRING([--enable-pcreposix], [enable using PCRE Posix libs for regex functions])) AC_ARG_ENABLE([fpm], @@ -498,10 +610,6 @@ AC_ARG_ENABLE([cumulus], AS_HELP_STRING([--enable-cumulus], [enable Cumulus Switch Special Extensions])) AC_ARG_ENABLE([datacenter], AS_HELP_STRING([--enable-datacenter], [enable Compilation for Data Center Extensions])) -AC_ARG_ENABLE([fuzzing], - AS_HELP_STRING([--enable-fuzzing], [enable ability to fuzz various parts of FRR])) -AC_ARG_ENABLE([netlink_fuzzing], - AS_HELP_STRING([--enable-netlink-fuzzing], [enable ability to fuzz netlink listening socket in zebra])) AC_ARG_ENABLE([rr-semantics], AS_HELP_STRING([--disable-rr-semantics], [disable the v6 Route Replace semantics])) AC_ARG_ENABLE([protobuf], @@ -515,7 +623,9 @@ AC_ARG_ENABLE([clippy-only], AC_ARG_ENABLE([numeric_version], AS_HELP_STRING([--enable-numeric-version], [Only numeric digits allowed in version (for Alpine)])) AC_ARG_ENABLE([gcov], - AS_HELP_STRING([--enable-gcov], [Add code coverage information])) + AS_HELP_STRING([--enable-gcov], [Collect coverage information with gcov])) +AC_ARG_ENABLE([clang_coverage], + AS_HELP_STRING([--enable-clang-coverage], [Collect coverage information with Clang Coverage])) AC_ARG_ENABLE([bfdd], AS_HELP_STRING([--disable-bfdd], [do not build bfdd])) AC_ARG_ENABLE([address-sanitizer], @@ -524,13 +634,34 @@ AC_ARG_ENABLE([thread-sanitizer], AS_HELP_STRING([--enable-thread-sanitizer], [enable ThreadSanitizer support for detecting data races])) AC_ARG_ENABLE([memory-sanitizer], AS_HELP_STRING([--enable-memory-sanitizer], [enable MemorySanitizer support for detecting uninitialized memory reads])) +AC_ARG_ENABLE([undefined-sanitizer], + AS_HELP_STRING([--undefined-sanitizer], [enable UndefinedBehaviorSanitizer support for detecting undefined behavior])) +AC_ARG_WITH([crypto], + AS_HELP_STRING([--with-crypto=], [choose between different implementations of cryptographic functions(default value is --with-crypto=internal)])) +AC_ARG_WITH([frr-format], + AS_HELP_STRING([--with-frr-format[=<.../frr-format.so>]], [use frr-format GCC plugin])) + +AC_ARG_ENABLE([version-build-config], + AS_HELP_STRING([--disable-version-build-config], [do not include build configs in show version command])) + +#if openssl, else use the internal +AS_IF([test "$with_crypto" = "openssl"], [ +AC_CHECK_LIB([crypto], [EVP_DigestInit], [LIBS="$LIBS -lcrypto"], [], []) +if test "$ac_cv_lib_crypto_EVP_DigestInit" = "no"; then + AC_MSG_ERROR([build with openssl has been specified but openssl library was not found on your system]) +else + AC_DEFINE([CRYPTO_OPENSSL], [1], [Compile with openssl support]) +fi +], [test "$with_crypto" = "internal" || test "$with_crypto" = "" ], [AC_DEFINE([CRYPTO_INTERNAL], [1], [Compile with internal cryptographic implementation]) +], [AC_MSG_ERROR([Unknown value for --with-crypto])] +) -AS_IF([test "${enable_clippy_only}" != "yes"], [ +AS_IF([test "$enable_clippy_only" != "yes"], [ AC_CHECK_HEADERS([json-c/json.h]) AC_CHECK_LIB([json-c], [json_object_get], [LIBS="$LIBS -ljson-c"], [], [-lm]) -if test "$ac_cv_lib_json_c_json_object_get" = no; then +if test "$ac_cv_lib_json_c_json_object_get" = "no"; then AC_CHECK_LIB([json], [json_object_get], [LIBS="$LIBS -ljson"]) - if test "$ac_cv_lib_json_json_object_get" = no; then + if test "$ac_cv_lib_json_json_object_get" = "no"; then AC_MSG_ERROR([libjson is needed to compile]) fi fi @@ -542,19 +673,27 @@ AC_ARG_ENABLE([dev_build], AC_ARG_ENABLE([lua], AS_HELP_STRING([--enable-lua], [Build Lua scripting])) -if test x"${enable_time_check}" != x"no" ; then - if test x"${enable_time_check}" = x"yes" -o x"${enable_time_check}" = x ; then +if test "$enable_time_check" != "no" ; then + if test "$enable_time_check" = "yes" -o "$enable_time_check" = "" ; then AC_DEFINE([CONSUMED_TIME_CHECK], [5000000], [Consumed Time Check]) else AC_DEFINE_UNQUOTED([CONSUMED_TIME_CHECK], [$enable_time_check], [Consumed Time Check]) fi fi +case "${enable_cpu_time}" in + "no") + AC_DEFINE([EXCLUDE_CPU_TIME], [1], [Exclude getrusage data gathering]) + ;; + "*") + ;; +esac + case "${enable_systemd}" in "no") ;; "yes") AC_CHECK_LIB([systemd], [sd_notify], [LIBS="$LIBS -lsystemd"]) - if test $ac_cv_lib_systemd_sd_notify = no; then + if test "$ac_cv_lib_systemd_sd_notify" = "no"; then AC_MSG_ERROR([enable systemd has been specified but systemd development env not found on your system]) else AC_DEFINE([HAVE_SYSTEMD], [1], [Compile systemd support in]) @@ -563,153 +702,57 @@ case "${enable_systemd}" in "*") ;; esac -if test "${enable_rr_semantics}" != "no" ; then +if test "$enable_rr_semantics" != "no" ; then AC_DEFINE([HAVE_V6_RR_SEMANTICS], [1], [Compile in v6 Route Replacement Semantics]) fi -if test "${enable_datacenter}" = "yes" ; then +if test "$enable_datacenter" = "yes" ; then AC_DEFINE([HAVE_DATACENTER], [1], [Compile extensions for a DataCenter]) + AC_MSG_WARN([The --enable-datacenter compile time option is deprecated. Please modify the init script to pass -F datacenter to the daemons instead.]) DFLT_NAME="datacenter" else DFLT_NAME="traditional" fi -if test "${enable_fuzzing}" = "yes" ; then - AC_DEFINE([HANDLE_ZAPI_FUZZING], [1], [Compile extensions to use with a fuzzer]) -fi - -if test "${enable_netlink_fuzzing}" = "yes" ; then - AC_DEFINE([HANDLE_NETLINK_FUZZING], [1], [Compile extensions to use with a fuzzer for netlink]) -fi - -if test "${enable_cumulus}" = "yes" ; then +if test "$enable_cumulus" = "yes" ; then AC_DEFINE([HAVE_CUMULUS], [1], [Compile Special Cumulus Code in]) fi AC_SUBST([DFLT_NAME]) AC_DEFINE_UNQUOTED([DFLT_NAME], ["$DFLT_NAME"], [Name of the configuration default set]) -if test "${enable_shell_access}" = "yes"; then +if test "$enable_shell_access" = "yes"; then AC_DEFINE([HAVE_SHELL_ACCESS], [1], [Allow user to use ssh/telnet/bash, be aware this is considered insecure]) fi -AM_CONDITIONAL([FPM], [test "x$enable_fpm" = "xyes"]) - # # Python for clippy # -AC_DEFUN([FRR_PYTHON_CHECK_WORKING], [ - AC_MSG_CHECKING([whether we found a working Python version]) - AC_LINK_IFELSE_FLAGS([$PYTHON_CFLAGS], [$PYTHON_LIBS], [AC_LANG_PROGRAM([ -#include -#if PY_VERSION_HEX < 0x02070000 -#error python too old -#endif -int main(void); -], -[ -{ - Py_Initialize(); - return 0; -} -])], [ - # some python installs are missing the zlib dependency... - PYTHON_LIBS="${PYTHON_LIBS} -lz" - AC_LINK_IFELSE_FLAGS([$PYTHON_CFLAGS], [$PYTHON_LIBS], [AC_LANG_PROGRAM([ -#include -#if PY_VERSION_HEX < 0x02070000 -#error python too old -#endif -int main(void); -], -[ -{ - Py_Initialize(); - return 0; -} -])], [ - m4_if([$1], [], [ - PYTHONCONFIG="" - unset PYTHON_LIBS - unset PYTHON_CFLAGS - ], [$1]) - ]) - ]) -]) - AS_IF([test "$host" = "$build"], [ - PYTHONCONFIG="" - - # ordering: - # 1. try python3, but respect the user's preference on which minor ver - # 2. try python, which might be py3 or py2 again on the user's preference - # 3. try python2 (can really only be 2.7 but eh) - # 4. try 3.6 > 3.5 > 3.4 > 3.3 > 3.2 > 2.7 through pkg-config (no user pref) - # - # (AX_PYTHON_DEVEL has no clue about py3 vs py2) - # (AX_PYTHON does not do what we need) - - AC_CHECK_TOOLS([PYTHONCONFIG], [ \ - python3-config \ - python-config \ - python2-config \ - python3.6-config \ - python3.5-config \ - python3.4-config \ - python3.3-config \ - python3.2-config \ - python2.7-config ]) - if test -n "$PYTHONCONFIG"; then - PYTHON_CFLAGS="`\"${PYTHONCONFIG}\" --includes`" - PYTHON_LIBS="`\"${PYTHONCONFIG}\" --ldflags`" - - FRR_PYTHON_CHECK_WORKING([]) - fi - - if test -z "$PYTHONCONFIG"; then - PKG_CHECK_MODULES([PYTHON], [python-3.6], [], [ - PKG_CHECK_MODULES([PYTHON], [python-3.5], [], [ - PKG_CHECK_MODULES([PYTHON], [python-3.4], [], [ - PKG_CHECK_MODULES([PYTHON], [python-3.3], [], [ - PKG_CHECK_MODULES([PYTHON], [python-3.2], [], [ - PKG_CHECK_MODULES([PYTHON], [python-2.7], [], [ - AC_MSG_FAILURE([could not find python-config or pkg-config python, please install Python development files from libpython-dev or similar]) - ])])])])])]) - - - FRR_PYTHON_CHECK_WORKING([ - AC_MSG_FAILURE([could not find python-config or pkg-config python, please install Python development files from libpython-dev or similar]) - ]) - fi + FRR_PYTHON_DEV +], [ + FRR_PYTHON ]) -AC_SUBST([PYTHON_CFLAGS]) -AC_SUBST([PYTHON_LIBS]) - -# -# Logic for protobuf support. -# -if test "$enable_protobuf" = "yes"; then - # Check for protoc & protoc-c - # protoc is not required, it's only for a "be nice" helper target - AC_CHECK_PROGS([PROTOC], [protoc], [/bin/false]) +FRR_PYTHON_MODULES([pytest]) - AC_CHECK_PROGS([PROTOC_C], [protoc-c], [/bin/false]) - if test "$PROTOC_C" = "/bin/false"; then - AC_MSG_FAILURE([protobuf requested but protoc-c not found. Install protobuf-c.]) - fi - - PKG_CHECK_MODULES([PROTOBUF_C], [libprotobuf-c >= 0.14],, [ - AC_MSG_FAILURE([protobuf requested but libprotobuf-c not found. Install protobuf-c.]) - ]) - AC_CHECK_HEADER([google/protobuf-c/protobuf-c.h], [], [ - AC_MSG_FAILURE([protobuf requested but protobuf-c.h not found. Install protobuf-c.]) +if test "$enable_doc" != "no"; then + FRR_PYTHON_MODULES([sphinx], , [ + if test "$enable_doc" = "yes"; then + AC_MSG_ERROR([Documentation was explicitly requested with --enable-doc but sphinx is not available for $PYTHON. Please disable docs or install sphinx.]) + fi ]) - - AC_DEFINE([HAVE_PROTOBUF], [1], [protobuf]) fi -AM_CONDITIONAL([HAVE_PROTOBUF], [test "x$enable_protobuf" = "xyes"]) +AM_CONDITIONAL([DOC], [test "$enable_doc" != "no" -a "$frr_py_mod_sphinx" != "false"]) +AM_CONDITIONAL([DOC_HTML], [test "$enable_doc_html" = "yes"]) + +FRR_PYTHON_MOD_EXEC([sphinx], [--version], [ + PYSPHINX="-m sphinx" +], [ + PYSPHINX="-c 'import sys; from sphinx import main; sys.exit(main(sys.argv))'" +]) +AC_SUBST([PYSPHINX]) # # Logic for old vpn commands support. @@ -723,35 +766,35 @@ fi # AC_MSG_CHECKING([if zebra should be configurable to send Route Advertisements]) -if test "${enable_rtadv}" != "no"; then +if test "$enable_rtadv" != "no"; then AC_MSG_RESULT([yes]) AC_DEFINE([HAVE_RTADV], [1], [Enable IPv6 Routing Advertisement support]) else AC_MSG_RESULT([no]) fi -if test x"${enable_user}" = x"no"; then +if test "$enable_user" = "no"; then enable_user="" else - if test x"${enable_user}" = x"yes" || test x"${enable_user}" = x""; then + if test "$enable_user" = "yes" || test "$enable_user" = ""; then enable_user="frr" fi AC_DEFINE_UNQUOTED([FRR_USER], ["${enable_user}"], [frr User]) fi -if test x"${enable_group}" = x"no"; then +if test "$enable_group" = "no"; then enable_group="" else - if test x"${enable_group}" = x"yes" || test x"${enable_group}" = x""; then + if test "$enable_group" = "yes" || test "$enable_group" = ""; then enable_group="frr" fi AC_DEFINE_UNQUOTED([FRR_GROUP], ["${enable_group}"], [frr Group]) fi -if test x"${enable_vty_group}" = x"yes" ; then +if test "$enable_vty_group" = "yes" ; then AC_MSG_ERROR([--enable-vty-group requires a group as argument, not yes]) -elif test x"${enable_vty_group}" != x""; then - if test x"${enable_vty_group}" != x"no"; then +elif test "$enable_vty_group" != ""; then + if test "$enable_vty_group" != "no"; then AC_DEFINE_UNQUOTED([VTY_GROUP], ["${enable_vty_group}"], [VTY Sockets Group]) fi fi @@ -761,6 +804,7 @@ AC_SUBST([enable_vty_group]) enable_configfile_mask=${enable_configfile_mask:-0600} AC_DEFINE_UNQUOTED([CONFIGFILE_MASK], [${enable_configfile_mask}], [Mask for config files]) +AC_SUBST([enable_configfile_mask]) enable_logfile_mask=${enable_logfile_mask:-0600} AC_DEFINE_UNQUOTED([LOGFILE_MASK], [${enable_logfile_mask}], [Mask for log files]) @@ -776,7 +820,7 @@ case "${enable_multipath}" in ;; "") ;; - *) + *) AC_MSG_FAILURE([Please specify digit to enable multipath ARG]) ;; esac @@ -788,12 +832,12 @@ AC_DEFINE_UNQUOTED([VTYSH_PAGER], ["$VTYSH_PAGER"], [What pager to use]) dnl -------------------- dnl Enable code coverage dnl -------------------- -AM_CONDITIONAL([HAVE_GCOV], [test '!' "$enable_gcov" = no]) +AM_CONDITIONAL([HAVE_GCOV], [test "$enable_gcov" != "no"]) dnl ------------------------------------ dnl Alpine only accepts numeric versions dnl ------------------------------------ -if test "x${enable_numeric_version}" != "x" ; then +if test "$enable_numeric_version" != "" ; then VERSION="`echo ${VERSION} | tr -c -d '[[.0-9]]'`" PACKAGE_VERSION="`echo ${PACKAGE_VERSION} | tr -c -d '[[.0-9]]'`" fi @@ -802,7 +846,7 @@ dnl ----------------------------------- dnl Add extra version string to package dnl name, string and version fields. dnl ----------------------------------- -if test "x${EXTRAVERSION}" != "x" ; then +if test "$EXTRAVERSION" != "" ; then VERSION="${VERSION}${EXTRAVERSION}" PACKAGE_VERSION="${PACKAGE_VERSION}${EXTRAVERSION}" AC_SUBST(PACKAGE_EXTRAVERSION, ["${EXTRAVERSION}"]) @@ -810,17 +854,17 @@ if test "x${EXTRAVERSION}" != "x" ; then fi AC_SUBST([EXTRAVERSION]) -if test "x$with_pkg_git_version" = "xyes"; then - if test -d "${srcdir}/.git"; then +if test "$with_pkg_git_version" = "yes"; then + if test -e "${srcdir}/.git"; then AC_DEFINE([GIT_VERSION], [1], [include git version info]) else with_pkg_git_version="no" AC_MSG_WARN([--with-pkg-git-version given, but this is not a git checkout]) fi fi -AM_CONDITIONAL([GIT_VERSION], [test "x$with_pkg_git_version" = "xyes"]) +AM_CONDITIONAL([GIT_VERSION], [test "$with_pkg_git_version" = "yes"]) AC_CHECK_TOOL([OBJCOPY], [objcopy], [:]) -if test "x${OBJCOPY}" != "x:"; then +if test "$OBJCOPY" != ":"; then AC_CACHE_CHECK([for .interp value to use], [frr_cv_interp], [ frr_cv_interp="" AC_LINK_IFELSE([AC_LANG_SOURCE([[int main() { return 0; }]])], [ @@ -835,25 +879,11 @@ if test -n "$frr_cv_interp"; then AC_DEFINE_UNQUOTED([INTERP], ["$frr_cv_interp"], [.interp value]) fi -dnl ------------------------------------ -dnl Check C keywords and standard types -dnl ------------------------------------ -AC_C_CONST -AC_C_INLINE -AC_C_VOLATILE -AC_HEADER_STDC -dnl AC_TYPE_PID_T -AC_TYPE_UID_T -AC_TYPE_MODE_T -AC_TYPE_SIZE_T -AC_STRUCT_TM - dnl ------------------------- dnl Check other header files. dnl ------------------------- AC_CHECK_HEADERS([stropts.h sys/ksym.h \ - linux/version.h asm/types.h \ - sys/cdefs.h]) + linux/version.h asm/types.h]) ac_stdatomic_ok=false AC_DEFINE([FRR_AUTOCONF_ATOMIC], [1], [did autoconf checks for atomic funcs]) @@ -924,7 +954,7 @@ int main(int argc, char **argv) { AC_CHECK_HEADERS([pthread_np.h],,, [ #include ]) -AC_CHECK_FUNCS([pthread_setname_np pthread_set_name_np]) +AC_CHECK_FUNCS([pthread_setname_np pthread_set_name_np pthread_getthreadid_np]) needsync=true @@ -1032,7 +1062,7 @@ AC_CHECK_HEADERS([net/if_var.h], [], [], [FRR_INCLUDES]) m4_define([FRR_INCLUDES], FRR_INCLUDES -[#if HAVE_NET_IF_VAR_H +[#ifdef HAVE_NET_IF_VAR_H # include #endif ])dnl @@ -1040,7 +1070,7 @@ FRR_INCLUDES AC_CHECK_HEADERS([netinet/in_var.h \ net/if_dl.h net/netopt.h \ inet/nd.h netinet/ip_icmp.h \ - sys/sysctl.h sys/sockio.h kvm.h sys/conf.h], + sys/sysctl.h sys/sockio.h sys/conf.h], [], [], [FRR_INCLUDES]) AC_CHECK_HEADERS([ucontext.h], [], [], @@ -1067,28 +1097,30 @@ FRR_INCLUDES [ #include #include -#if HAVE_NETINET_IN_VAR_H +#ifdef HAVE_NETINET_IN_VAR_H # include #endif -#if HAVE_NET_IF_DL_H +#ifdef HAVE_NET_IF_DL_H # include #endif -#if HAVE_NET_NETOPT_H +#ifdef HAVE_NET_NETOPT_H # include #endif #include -#if HAVE_INET_ND_H +#ifdef HAVE_INET_ND_H # include #endif #include /* Required for IDRP */ -#if HAVE_NETINET_IP_ICMP_H +#ifdef HAVE_NETINET_IP_ICMP_H # include #endif ])dnl dnl V6 headers are checked below, after we check for v6 +is_linux=false + AC_MSG_CHECKING([which operating system interface to use]) case "$host_os" in sunos* | solaris2*) @@ -1102,6 +1134,7 @@ case "$host_os" in AC_CHECK_LIB([nsl], [main]) AC_CHECK_LIB([umem], [main]) SOLARIS="solaris" + AC_MSG_WARN([--Solaris support is being considered for deprecation, please let us know if you are still using this--]) ;; linux*) AC_MSG_RESULT([Linux]) @@ -1116,6 +1149,8 @@ case "$host_os" in dnl how to fix it but no real progress on implementation dnl when they fix it, remove this AC_DEFINE([IPV6_MINHOPCOUNT], [73], [Linux ipv6 Min Hop Count]) + + is_linux=true ;; openbsd*) AC_MSG_RESULT([OpenBSD]) @@ -1123,16 +1158,6 @@ case "$host_os" in AC_DEFINE([OPEN_BSD], [1], [OpenBSD]) AC_DEFINE([KAME], [1], [KAME IPv6]) AC_DEFINE([BSD_V6_SYSCTL], [1], [BSD v6 sysctl to turn on and off forwarding]) - - if test "x${enable_pimd}" != "xno"; then - case "$host_os" in - openbsd6.0) - ;; - openbsd[6-9]*) - AC_MSG_FAILURE([pimd cannot be enabled as PIM support has been removed from OpenBSD 6.1]) - ;; - esac - fi ;; *) AC_MSG_RESULT([BSD]) @@ -1142,14 +1167,15 @@ case "$host_os" in AC_DEFINE([BSD_V6_SYSCTL], [1], [BSD v6 sysctl to turn on and off forwarding]) ;; esac -AM_CONDITIONAL([SOLARIS], [test "${SOLARIS}" = "solaris"]) +AM_CONDITIONAL([SOLARIS], [test "$SOLARIS" = "solaris"]) +AM_CONDITIONAL([LINUX], [${is_linux}]) AC_SYS_LARGEFILE dnl ------------------------ dnl Integrated REALMS option dnl ------------------------ -if test "${enable_realms}" = "yes"; then +if test "$enable_realms" = "yes"; then case "$host_os" in linux*) AC_DEFINE([SUPPORT_REALMS], [1], [Realms support]) @@ -1160,7 +1186,59 @@ if test "${enable_realms}" = "yes"; then ;; esac fi -AM_CONDITIONAL([SUPPORT_REALMS], [test "${enable_realms}" = "yes"]) + +dnl ------------------------------- +dnl Endian-ness check +dnl ------------------------------- +AC_WORDS_BIGENDIAN + +dnl --------------- +dnl other functions +dnl --------------- +AC_CHECK_FUNCS([ \ + strlcat strlcpy \ + getgrouplist \ + openat \ + unlinkat \ + posix_fallocate \ + ]) + +dnl ########################################################################## +dnl LARGE if block spans a lot of "configure"! +if test "$enable_clippy_only" != "yes"; then +dnl ########################################################################## + +# +# Logic for protobuf support. +# +PROTO3=false +if test "$enable_protobuf" = "yes"; then + # Check for protoc & protoc-c + + # protoc is not required, it's only for a "be nice" helper target + AC_CHECK_PROGS([PROTOC], [protoc], [/bin/false]) + + AC_CHECK_PROGS([PROTOC_C], [protoc-c], [/bin/false]) + if test "$PROTOC_C" = "/bin/false"; then + AC_MSG_FAILURE([protobuf requested but protoc-c not found. Install protobuf-c.]) + fi + + PKG_CHECK_MODULES([PROTOBUF_C], [libprotobuf-c >= 0.14],, [ + AC_MSG_FAILURE([protobuf requested but libprotobuf-c not found. Install protobuf-c.]) + ]) + + PROTO3=true + AC_CHECK_HEADER([google/protobuf-c/protobuf-c.h], + [AC_CHECK_DECLS(PROTOBUF_C_LABEL_NONE, + AC_DEFINE([HAVE_PROTOBUF_VERSION_3], + [1], [Have Protobuf version 3]), + [PROTO3=false], + [#include ])], + [PROTO3=false && AC_MSG_FAILURE([protobuf requested but protobuf-c.h not found. Install protobuf-c.])]) + + AC_DEFINE([HAVE_PROTOBUF], [1], [protobuf]) +fi + dnl --------------------- dnl Integrated VTY option @@ -1195,21 +1273,20 @@ case "${enable_vtysh}" in LIBS="$prev_libs" AC_CHECK_HEADER([readline/history.h]) - if test $ac_cv_header_readline_history_h = no;then + if test "$ac_cv_header_readline_history_h" = "no"; then AC_MSG_ERROR([readline is too old to have readline/history.h, please update to the latest readline library.]) fi AC_CHECK_LIB([readline], [rl_completion_matches], [true], [], [$LIBREADLINE]) - if test $ac_cv_lib_readline_rl_completion_matches = no; then + if test "$ac_cv_lib_readline_rl_completion_matches" = "no"; then AC_DEFINE([rl_completion_matches], [completion_matches], [Old readline]) fi AC_CHECK_LIB([readline], [append_history], [frr_cv_append_history=yes], [frr_cv_append_history=no], [$LIBREADLINE]) - if test "$frr_cv_append_history" = yes; then + if test "$frr_cv_append_history" = "yes"; then AC_DEFINE([HAVE_APPEND_HISTORY], [1], [Have history.h append_history]) fi ;; esac AC_SUBST([LIBREADLINE]) -AM_CONDITIONAL([VTYSH], test "x$VTYSH" = "xvtysh") dnl ---------- dnl PAM module @@ -1271,43 +1348,17 @@ AC_CHECK_LIB([pam], [pam_start], fi AC_SUBST([LIBPAM]) -dnl ------------------------------- -dnl Endian-ness check -dnl ------------------------------- -AC_WORDS_BIGENDIAN - -dnl ------------------------------- -dnl check the size in byte of the C -dnl ------------------------------- -dnl AC_CHECK_SIZEOF(char) -dnl AC_CHECK_SIZEOF(int) -dnl AC_CHECK_SIZEOF(short) -dnl AC_CHECK_SIZEOF(long) - -dnl ---------------------------- -dnl check existance of functions -dnl ---------------------------- -AC_FUNC_FNMATCH -AC_FUNC_FORK -AC_FUNC_MKTIME -AC_FUNC_STAT - dnl ------------------------------- dnl bgpd needs pow() and hence libm dnl ------------------------------- TMPLIBS="$LIBS" -AC_CHECK_HEADER([math.h], - [AC_CHECK_LIB([m], [pow], - [LIBM="-lm" - LIBS="$LIBS $LIBM" - AC_CHECK_FUNCS(pow,[],[LIBM=""]) - ]) -]) -if test x"$LIBM" = x ; then +LIBS="" +AC_SEARCH_LIBS([pow], [m], [ + LIBM="$LIBS" +], [ AC_MSG_WARN([Unable to find working pow function - bgpd may not link]) -fi +]) LIBS="$TMPLIBS" - AC_SUBST([LIBM]) AC_CHECK_FUNCS([ppoll], [ @@ -1317,13 +1368,6 @@ AC_CHECK_FUNCS([pollts], [ AC_DEFINE([HAVE_POLLTS], [1], [have NetBSD pollts()]) ]) -dnl --------------- -dnl other functions -dnl --------------- -AC_CHECK_FUNCS([ \ - strlcat strlcpy \ - getgrouplist]) - AC_CHECK_HEADER([asm-generic/unistd.h], [AC_CHECK_DECL(__NR_setns, AC_DEFINE([HAVE_NETNS], [1], [Have netns]),, @@ -1352,10 +1396,10 @@ case "$host_os" in ISIS_METHOD_MACRO="ISIS_METHOD_DLPI" ;; *) - if test $ac_cv_header_net_bpf_h = no; then - if test $ac_cv_header_sys_dlpi_h = no; then + if test "$ac_cv_header_net_bpf_h" = "no"; then + if test "$ac_cv_header_sys_dlpi_h" = "no"; then AC_MSG_RESULT([none]) - if test "${enable_isisd}" = yes -o "${enable_fabricd}" = yes; then + if test "$enable_isisd" = "yes" -o "$enable_fabricd" = "yes"; then AC_MSG_FAILURE([IS-IS support requested but no packet backend found]) fi AC_MSG_WARN([*** IS-IS support will not be built ***]) @@ -1387,7 +1431,7 @@ AC_CHECK_HEADERS([linux/mroute.h], [], [],[ m4_define([FRR_INCLUDES], FRR_INCLUDES -[#if HAVE_LINUX_MROUTE_H +[#ifdef HAVE_LINUX_MROUTE_H # include #endif ])dnl @@ -1401,7 +1445,7 @@ AC_CHECK_HEADERS([netinet/ip_mroute.h], [], [],[ m4_define([FRR_INCLUDES], FRR_INCLUDES -[#if HAVE_NETINET_IP_MROUTE_H +[#ifdef HAVE_NETINET_IP_MROUTE_H # include #endif ])dnl @@ -1459,7 +1503,7 @@ AC_CHECK_HEADER([netinet/tcp.h], AC_CHECK_DECLS([TCP_MD5SIG], [], [], MD5_INCLUDES)], [], FRR_INCLUDES) -if test $ac_cv_have_decl_TCP_MD5SIG = no; then +if test "$ac_cv_have_decl_TCP_MD5SIG" = "no"; then AC_CHECK_HEADER([linux/tcp.h], [m4_define([MD5_INCLUDES], FRR_INCLUDES @@ -1468,6 +1512,38 @@ if test $ac_cv_have_decl_TCP_MD5SIG = no; then AC_CHECK_DECLS([TCP_MD5SIG], [], [], MD5_INCLUDES)]) fi +AC_SUBST([SOLARIS]) +AC_CHECK_LIB([crypt], [crypt], [], + [AC_CHECK_LIB([crypto], [DES_crypt])]) +AC_CHECK_LIB([resolv], [res_init]) + +dnl --------------------------- +dnl check system has PCRE regexp +dnl --------------------------- +if test "$enable_pcreposix" = "yes"; then + AC_CHECK_LIB([pcreposix], [regexec], [], [ + AC_MSG_ERROR([--enable-pcreposix given but unable to find libpcreposix]) + ]) +fi +AC_SUBST([HAVE_LIBPCREPOSIX]) + +dnl ########################################################################## +dnl test "$enable_clippy_only" != "yes" +fi +dnl END OF LARGE if block +dnl ########################################################################## + +dnl ------------------ +dnl check C-Ares library +dnl ------------------ +PKG_CHECK_MODULES([CARES], [libcares], [ + c_ares_found=true +],[ + c_ares_found=false +]) +AM_CONDITIONAL([CARES], [$c_ares_found]) + + dnl ---------------------------------------------------------------------------- dnl figure out if domainname is available in the utsname struct (GNU extension). dnl ---------------------------------------------------------------------------- @@ -1482,48 +1558,26 @@ AC_CHECK_HEADERS([netinet6/in6.h netinet/in6_var.h \ m4_define([FRR_INCLUDES],dnl FRR_INCLUDES -[#if HAVE_NETINET6_IN6_H +[#ifdef HAVE_NETINET6_IN6_H #include #endif -#if HAVE_NETINET_IN6_VAR_H +#ifdef HAVE_NETINET_IN6_VAR_H #include #endif #include -#if HAVE_NETINET6_IN6_VAR_H +#ifdef HAVE_NETINET6_IN6_VAR_H # include #endif -#if HAVE_NETINET6_ND6_H +#ifdef HAVE_NETINET6_ND6_H # include #endif ])dnl -dnl disable doc check -AC_CHECK_PROGS([SPHINXBUILD], [sphinx-build sphinx-build3 sphinx-build2], [/bin/false]) -if test "$SPHINXBUILD" = "/bin/false"; then - if test "${enable_doc}" = "yes"; then - AC_MSG_ERROR([Documentation was explicitly requested with --enable-doc but sphinx-build is not available. Please disable docs or install sphinx.]) - fi -fi -AM_CONDITIONAL([DOC], [test "${enable_doc}" != "no" -a "$SPHINXBUILD" != "/bin/false"]) -AM_CONDITIONAL([DOC_HTML], [test "${enable_doc_html}" = "yes"]) - dnl -------------------- dnl Daemon disable check dnl -------------------- -AM_CONDITIONAL([ZEBRA], [test "${enable_zebra}" != "no"]) - -if test "${enable_bgpd}" = "no";then - BGPD="" -else - BGPD="bgpd" -fi -AM_CONDITIONAL([BGPD], [test "x$BGPD" = "xbgpd"]) - -AM_CONDITIONAL([RIPD], [test "${enable_ripd}" != "no"]) -AM_CONDITIONAL([OSPFD], [test "${enable_ospfd}" != "no"]) -AM_CONDITIONAL([LDPD], [test "${enable_ldpd}" != "no"]) -AS_IF([test "${enable_ldpd}" != "no"], [ +AS_IF([test "$enable_ldpd" != "no"], [ AC_DEFINE([HAVE_LDPD], [1], [ldpd]) ]) @@ -1545,98 +1599,95 @@ else esac fi -AM_CONDITIONAL([BFDD], [test "x$BFDD" = "xbfdd"]) - -if test "$ac_cv_lib_json_c_json_object_get" = no -a "x$BFDD" = "xbfdd"; then +if test "$ac_cv_lib_json_c_json_object_get" = "no" -a "$BFDD" = "bfdd"; then AC_MSG_ERROR(["you must use json-c library to use bfdd"]) fi NHRPD="" case "$host_os" in linux*) - if test "${enable_nhrpd}" != "no"; then - NHRPD="nhrpd" - fi + case "${enable_nhrpd}" in + no) + ;; + yes) + if test "$enable_clippy_only" != "yes"; then + if test "$c_ares_found" != "true" ; then + AC_MSG_ERROR([nhrpd requires libcares. Please install c-ares and its -dev headers.]) + fi + fi + NHRPD="nhrpd" + ;; + *) + if test "$c_ares_found" = "true" ; then + NHRPD="nhrpd" + fi + ;; + esac ;; *) - if test "${enable_nhrpd}" = "yes"; then + if test "$enable_nhrpd" = "yes"; then AC_MSG_ERROR([nhrpd requires kernel APIs that are only present on Linux.]) fi ;; esac -AM_CONDITIONAL([NHRPD], [test "x$NHRPD" = "xnhrpd"]) -AM_CONDITIONAL([EIGRPD], [test "${enable_eigrpd}" != "no"]) - -if test "${enable_watchfrr}" = "no";then +if test "$enable_watchfrr" = "no";then WATCHFRR="" else WATCHFRR="watchfrr" fi -AM_CONDITIONAL([WATCHFRR], [test "x$WATCHFRR" = "xwatchfrr"]) OSPFCLIENT="" -if test "${enable_ospfapi}" != "no";then +if test "$enable_ospfapi" != "no";then AC_DEFINE([SUPPORT_OSPF_API], [1], [OSPFAPI]) - if test "${enable_ospfclient}" != "no";then + if test "$enable_ospfclient" != "no";then OSPFCLIENT="ospfclient" fi fi -AM_CONDITIONAL([OSPFCLIENT], [test "x$OSPFCLIENT" = "xospfclient"]) -AM_CONDITIONAL([RIPNGD], [test "${enable_ripngd}" != "no"]) -AM_CONDITIONAL([BABELD], [test "${enable_babeld}" != "no"]) -AM_CONDITIONAL([OSPF6D], [test "${enable_ospf6d}" != "no"]) -AM_CONDITIONAL([ISISD], [test "${enable_isisd}" != "no"]) -AM_CONDITIONAL([PIMD], [test "${enable_pimd}" != "no"]) -AM_CONDITIONAL([PBRD], [test "${enable_pbrd}" != "no"]) -AM_CONDITIONAL([SHARPD], [test "${enable_sharpd}" = "yes"]) -AM_CONDITIONAL([STATICD], [test "${enable_staticd}" != "no"]) -AM_CONDITIONAL([FABRICD], [test "${enable_fabricd}" != "no"]) - -if test "${enable_bgp_announce}" = "no";then +if test "$enable_bgp_announce" = "no";then AC_DEFINE([DISABLE_BGP_ANNOUNCE], [1], [Disable BGP installation to zebra]) else AC_DEFINE([DISABLE_BGP_ANNOUNCE], [0], [Disable BGP installation to zebra]) fi -if test "${enable_bgp_vnc}" != "no";then +if test "$enable_bgp_vnc" != "no";then AC_DEFINE([ENABLE_BGP_VNC], [1], [Enable BGP VNC support]) fi -AM_CONDITIONAL([ENABLE_BGP_VNC], [test x${enable_bgp_vnc} != xno]) - -AC_SUBST([SOLARIS]) -AC_CHECK_LIB([crypt], [crypt], [], - [AC_CHECK_LIB([crypto], [DES_crypt])]) -AC_CHECK_LIB([resolv], [res_init]) -dnl --------------------------- -dnl check system has PCRE regexp -dnl --------------------------- -if test "x$enable_pcreposix" = "xyes"; then - AC_CHECK_LIB([pcreposix], [regexec], [], [ - AC_MSG_ERROR([--enable-pcreposix given but unable to find libpcreposix]) - ]) -fi -AC_SUBST([HAVE_LIBPCREPOSIX]) +bgpd_bmp=false +case "${enable_bmp}" in + no) + ;; + yes) + if test "$c_ares_found" != "true" ; then + AC_MSG_ERROR([BMP support requires libcares. Please install c-ares and its -dev headers.]) + fi + bgpd_bmp=true + ;; + *) + if test "$c_ares_found" = "true" ; then + bgpd_bmp=true + fi + ;; +esac -dnl ------------------ -dnl check C-Ares library -dnl ------------------ -if test "${NHRPD}" != ""; then - PKG_CHECK_MODULES([CARES], [libcares], ,[ - AC_MSG_ERROR([trying to build nhrpd, but libcares not found. install c-ares and its -dev headers.]) - ]) +if test "$enable_version_build_config" != "no";then + AC_DEFINE([ENABLE_VERSION_BUILD_CONFIG], [1], [Report build configs in show version]) fi +dnl ########################################################################## +dnl LARGE if block +if test "$enable_clippy_only" != "yes"; then +dnl ########################################################################## dnl ------------------ dnl check Net-SNMP library dnl ------------------ -if test "${enable_snmp}" != "" -a "${enable_snmp}" != "no"; then +if test "$enable_snmp" != "" -a "$enable_snmp" != "no"; then AC_PATH_TOOL([NETSNMP_CONFIG], [net-snmp-config], [no]) - if test x"$NETSNMP_CONFIG" = x"no"; then + if test "$NETSNMP_CONFIG" = "no"; then AC_MSG_ERROR([--enable-snmp given but unable to find net-snmp-config]) fi SNMP_LIBS="`${NETSNMP_CONFIG} --agent-libs`" @@ -1655,6 +1706,7 @@ int main(void); return 0; } ])], [ + AC_MSG_RESULT([no]) AC_MSG_ERROR([--enable-snmp given but not usable])]) case "${enable_snmp}" in yes) @@ -1670,15 +1722,14 @@ int main(void); AH_TEMPLATE([SNMP_AGENTX], [Use SNMP AgentX to interface with snmpd]) AC_DEFINE_UNQUOTED(AS_TR_CPP(SNMP_${SNMP_METHOD}),,[SNMP method to interface with snmpd]) fi -AM_CONDITIONAL([SNMP], [test "x${SNMP_METHOD}" != "x"]) AC_SUBST([SNMP_LIBS]) AC_SUBST([SNMP_CFLAGS]) dnl --------------- dnl libyang dnl --------------- -PKG_CHECK_MODULES([LIBYANG], [libyang >= 0.16.7], , [ - AC_MSG_ERROR([libyang (>= 0.16.7) was not found on your system.]) +PKG_CHECK_MODULES([LIBYANG], [libyang >= 1.0.184], , [ + AC_MSG_ERROR([libyang (>= 1.0.184) was not found on your system.]) ]) ac_cflags_save="$CFLAGS" CFLAGS="$CFLAGS $LIBYANG_CFLAGS" @@ -1690,24 +1741,6 @@ AC_CHECK_MEMBER([struct lyd_node.priv], [], [ ], [[#include ]]) CFLAGS="$ac_cflags_save" -ac_libs_save="$LIBS" -LIBS="$LIBS $LIBYANG_LIBS" -AC_CHECK_FUNC([ly_register_types], [ - libyang_ext_builtin=true - AC_DEFINE([LIBYANG_EXT_BUILTIN], [1], [have ly_register_types()]) -], [ - libyang_ext_builtin=false - AC_MSG_WARN([===== old libyang (before 0.16.74) detected =====]) - AC_MSG_WARN([The available version of libyang does not seem to support]) - AC_MSG_WARN([built-in YANG extension modules. This will cause "make check"]) - AC_MSG_WARN([to fail and may create installation and version mismatch issues.]) - AC_MSG_WARN([Support for the old mechanism will be removed at some point.]) - AC_MSG_WARN([Please update libyang to version 0.16.74 or newer.]) - AC_MSG_WARN([===== old libyang (before 0.16.74) detected =====]) -]) -AM_CONDITIONAL([LIBYANG_EXT_BUILTIN], [$libyang_ext_builtin]) -LIBS="$ac_libs_save" - dnl --------------- dnl configuration rollbacks dnl --------------- @@ -1721,14 +1754,13 @@ if test "$enable_config_rollbacks" = "yes"; then AC_MSG_ERROR([--enable-config-rollbacks given but sqlite3 was not found on your system.]) ]) fi -AM_CONDITIONAL([SQLITE3], [$SQLITE3]) dnl --------------- dnl confd dnl --------------- if test "$enable_confd" != "" -a "$enable_confd" != "no"; then AC_CHECK_PROG([CONFD], [confd], [confd], [/bin/false], "${enable_confd}/bin") - if test "x$CONFD" = "x/bin/false"; then + if test "$CONFD" = "/bin/false"; then AC_MSG_ERROR([confd was not found on your system.])] fi CONFD_CFLAGS="-I${enable_confd}/include -L${enable_confd}/lib" @@ -1737,25 +1769,108 @@ if test "$enable_confd" != "" -a "$enable_confd" != "no"; then AC_SUBST([CONFD_LIBS]) AC_DEFINE([HAVE_CONFD], [1], [Enable confd integration]) fi -AM_CONDITIONAL([CONFD], [test "x$enable_confd" != "x"]) dnl --------------- dnl sysrepo dnl --------------- if test "$enable_sysrepo" = "yes"; then - PKG_CHECK_MODULES([SYSREPO], [libsysrepo], + PKG_CHECK_MODULES([SYSREPO], [sysrepo], [AC_DEFINE([HAVE_SYSREPO], [1], [Enable sysrepo integration]) SYSREPO=true], [SYSREPO=false AC_MSG_ERROR([sysrepo was not found on your system.])] ) fi -AM_CONDITIONAL([SYSREPO], [test "x$enable_sysrepo" = "xyes"]) dnl --------------- -dnl math +dnl gRPC dnl --------------- -AC_SEARCH_LIBS([sqrt], [m]) +if test "$enable_grpc" = "yes"; then + PKG_CHECK_MODULES([GRPC], [grpc grpc++ protobuf], [ + AC_CHECK_PROGS([PROTOC], [protoc], [/bin/false]) + if test "$PROTOC" = "/bin/false"; then + AC_MSG_FAILURE([grpc requested but protoc not found.]) + fi + + AC_DEFINE([HAVE_GRPC], [1], [Enable the gRPC northbound plugin]) + GRPC=true + ], [ + GRPC=false + AC_MSG_ERROR([grpc/grpc++ were not found on your system.]) + ]) +fi + +dnl ------ +dnl ZeroMQ +dnl ------ +if test "$enable_zeromq" != "no"; then + PKG_CHECK_MODULES([ZEROMQ], [libzmq >= 4.0.0], [ + AC_DEFINE([HAVE_ZEROMQ], [1], [Enable ZeroMQ support]) + ZEROMQ=true + ], [ + if test "$enable_zeromq" = "yes"; then + AC_MSG_ERROR([configuration specifies --enable-zeromq but libzmq was not found]) + fi + ]) +fi + +dnl ------------------------------------ +dnl Enable RPKI and add librtr to libs +dnl ------------------------------------ +if test "$enable_rpki" = "yes"; then + PKG_CHECK_MODULES([RTRLIB], [rtrlib >= 0.5.0], + [RPKI=true], + [RPKI=false + AC_MSG_ERROR([rtrlib was not found on your system or is too old.])] + ) +fi + +dnl ------------------------------------ +dnl pimd is not supported on OpenBSD and MacOS +dnl ------------------------------------ +if test "$enable_pimd" != "no"; then +AC_MSG_CHECKING([for pimd OS support]) +case "$host_os" in + darwin*) + AC_MSG_RESULT([no]) + enable_pimd="no" + ;; + openbsd*) + AC_MSG_RESULT([no]) + enable_pimd="no" + ;; + *) + AC_MSG_RESULT([yes]) + ;; +esac +fi + +dnl ------------------------------------- +dnl VRRP is only supported on linux +dnl ------------------------------------- +if test "$enable_vrrpd" != "no"; then +AC_MSG_CHECKING([for VRRP OS support]) +case "$host_os" in + linux*) + AC_MSG_RESULT([yes]) + ;; + *) + AC_MSG_RESULT([no]) + enable_vrrpd="no" + ;; +esac +fi + +dnl ------------------------------------------ +dnl Check whether rtrlib was build with ssh support +dnl ------------------------------------------ +AC_MSG_CHECKING([whether the RTR Library is compiled with SSH]) +AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include "rtrlib/rtrlib.h"]], + [[struct tr_ssh_config config;]])], + [AC_MSG_RESULT([yes]) + AC_DEFINE([FOUND_SSH], [1], [found_ssh])], + AC_MSG_RESULT([no]) +) dnl --------------- dnl dlopen & dlinfo @@ -1782,7 +1897,7 @@ AC_CACHE_CHECK([for dlinfo(RTLD_DI_ORIGIN)], [frr_cv_rtld_di_origin], [ frr_cv_rtld_di_origin=no ]) ]) -if test "$frr_cv_rtld_di_origin" = yes; then +if test "$frr_cv_rtld_di_origin" = "yes"; then AC_DEFINE([HAVE_DLINFO_ORIGIN], [1], [Have dlinfo RTLD_DI_ORIGIN]) fi @@ -1802,11 +1917,15 @@ AC_CACHE_CHECK([for dlinfo(RTLD_DI_LINKMAP)], [frr_cv_rtld_di_linkmap], [ frr_cv_rtld_di_linkmap=no ]) ]) -if test "$frr_cv_rtld_di_linkmap" = yes; then +if test "$frr_cv_rtld_di_linkmap" = "yes"; then AC_DEFINE([HAVE_DLINFO_LINKMAP], [1], [Have dlinfo RTLD_DI_LINKMAP]) fi -AM_CONDITIONAL([SNMP], [test "x$SNMP_METHOD" = "xagentx"]) +dnl ########################################################################## +dnl test "$enable_clippy_only" != "yes" +fi +dnl END OF LARGE if block +dnl ########################################################################## dnl --------------------------- dnl sockaddr and netinet checks @@ -1816,7 +1935,7 @@ AC_CHECK_TYPES([ struct vifctl, struct mfcctl, struct sioc_sg_req, vifi_t, struct sioc_vif_req, struct igmpmsg, struct ifaliasreq, struct if6_aliasreq, struct in6_aliasreq, - struct nd_opt_adv_interval, struct rt_addrinfo, + struct nd_opt_adv_interval, struct nd_opt_homeagent_info, struct nd_opt_adv_interval, struct nd_opt_rdnss, struct nd_opt_dnssl], [], [], FRR_INCLUDES) @@ -1851,7 +1970,6 @@ no) ;; esac -AM_CONDITIONAL([IRDP], [$IRDP]) dnl ----------------------- dnl checking for IP_PKTINFO @@ -1932,6 +2050,10 @@ AC_CHECK_DECL([CLOCK_MONOTONIC], AC_DEFINE([HAVE_CLOCK_MONOTONIC], [1], [Have monotonic clock]) ], [AC_MSG_RESULT([no])], [FRR_INCLUDES]) +AC_SEARCH_LIBS([clock_nanosleep], [rt], [ + AC_DEFINE([HAVE_CLOCK_NANOSLEEP], [1], [Have clock_nanosleep()]) +]) + dnl -------------------------------------- dnl checking for flex and bison dnl -------------------------------------- @@ -1958,7 +2080,7 @@ AC_MSG_CHECKING([version of bison]) frr_ac_bison_version="$(eval $YACC -V | grep bison | head -n 1)" frr_ac_bison_version="${frr_ac_bison_version##* }" frr_ac_bison_missing="false" -case "x${frr_ac_bison_version}" in +case "x${frr_ac_bison_version}x" in x2.7*) BISON_OPENBRACE='"' BISON_CLOSEBRACE='"' @@ -1975,11 +2097,17 @@ case "x${frr_ac_bison_version}" in AC_MSG_WARN([could not determine bison version. Please install GNU bison 2.7.x or newer.]) frr_ac_bison_missing="true" ;; - *) + x3.[012][^0-9]*) BISON_OPENBRACE='{' BISON_CLOSEBRACE='}' BISON_VERBOSE='-Dparse.error=verbose' - AC_MSG_RESULT([$frr_ac_bison_version - 3.0 or newer]) + AC_MSG_RESULT([$frr_ac_bison_version - 3.0 to 3.2]) + ;; + *) + BISON_OPENBRACE='{' + BISON_CLOSEBRACE='}' + BISON_VERBOSE='-Dparse.error=verbose -Wno-yacc' + AC_MSG_RESULT([$frr_ac_bison_version - 3.3 or newer]) ;; esac AC_SUBST([BISON_OPENBRACE]) @@ -1998,7 +2126,7 @@ fi dnl ------------------- dnl capabilities checks dnl ------------------- -if test "${enable_capabilities}" != "no"; then +if test "$enable_capabilities" != "no"; then AC_MSG_CHECKING([whether prctl PR_SET_KEEPCAPS is available]) AC_TRY_COMPILE([#include ], [prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);], [AC_MSG_RESULT([yes]) @@ -2006,11 +2134,11 @@ if test "${enable_capabilities}" != "no"; then frr_ac_keepcaps="yes"], AC_MSG_RESULT([no]) ) - if test x"${frr_ac_keepcaps}" = x"yes"; then + if test "$frr_ac_keepcaps" = "yes"; then AC_CHECK_HEADERS([sys/capability.h]) fi - if test x"${ac_cv_header_sys_capability_h}" = x"yes"; then - AC_CHECK_LIB([cap], [cap_init], + if test "$ac_cv_header_sys_capability_h" = "yes"; then + AC_CHECK_LIB([cap], [cap_init], [AC_DEFINE([HAVE_LCAPS], [1], [Capabilities]) LIBCAP="-lcap" frr_ac_lcaps="yes"] @@ -2027,41 +2155,60 @@ if test "${enable_capabilities}" != "no"; then ] ) fi - if test x"${frr_ac_scaps}" = x"yes" \ - -o x"${frr_ac_lcaps}" = x"yes"; then + if test "$frr_ac_scaps" = "yes" \ + -o "$frr_ac_lcaps" = "yes"; then AC_DEFINE([HAVE_CAPABILITIES], [1], [capabilities]) fi + + case "$host_os" in + linux*) + if test "$enable_clippy_only" != "yes"; then + if test "$frr_ac_lcaps" != "yes"; then + AC_MSG_ERROR([libcap and/or its headers were not found. Running FRR without libcap support built in causes a huge performance penalty.]) + fi + fi + ;; + esac +else + case "$host_os" in + linux*) + AC_MSG_WARN([Running FRR without libcap support built in causes a huge performance penalty.]) + ;; + esac fi AC_SUBST([LIBCAP]) dnl --------------------------- dnl check for glibc 'backtrace' -dnl --------------------------- -if test x"${enable_backtrace}" != x"no" ; then +dnl --------------------------- +if test "$enable_backtrace" != "no" ; then backtrace_ok=no PKG_CHECK_MODULES([UNWIND], [libunwind], [ AC_DEFINE([HAVE_LIBUNWIND], [1], [libunwind]) backtrace_ok=yes ], [ - case "$host_os" in - sunos* | solaris2*) - AC_CHECK_FUNCS([printstack], [ - AC_DEFINE([HAVE_PRINTSTACK], [1], [Solaris printstack]) + true + ]) + + if test "$backtrace_ok" = "no"; then + AC_CHECK_HEADER([unwind.h], [ + AC_SEARCH_LIBS([unw_getcontext], [unwind], [ + AC_DEFINE([HAVE_LIBUNWIND], [1], [libunwind]) backtrace_ok=yes ]) - ;; - esac - if test "$backtrace_ok" = no; then - AC_CHECK_HEADER([execinfo.h], [ - AC_SEARCH_LIBS([backtrace], [execinfo], [ - AC_DEFINE([HAVE_GLIBC_BACKTRACE], [1], [Glibc backtrace]) - backtrace_ok=yes - ],, [-lm]) - ]) - fi - ]) + ]) + fi + + if test "$backtrace_ok" = "no"; then + AC_CHECK_HEADER([execinfo.h], [ + AC_SEARCH_LIBS([backtrace], [execinfo], [ + AC_DEFINE([HAVE_GLIBC_BACKTRACE], [1], [Glibc backtrace]) + backtrace_ok=yes + ],, [-lm]) + ]) + fi - if test x"${enable_backtrace}" = x"yes" -a x"${backtrace_ok}" = x"no"; then + if test "$enable_backtrace" = "yes" -a "$backtrace_ok" = "no"; then dnl user explicitly requested backtrace but we failed to find support AC_MSG_FAILURE([failed to find backtrace or libunwind support]) fi @@ -2095,7 +2242,7 @@ struct mallinfo ac_x; ac_x = mallinfo (); frr_cv_mallinfo=no ]) ]) -if test "$frr_cv_mallinfo" = yes; then +if test "$frr_cv_mallinfo" = "yes"; then AC_DEFINE([HAVE_MALLINFO], [1], [mallinfo]) fi @@ -2133,27 +2280,12 @@ size_t ac_x; ac_x = malloc_size(NULL); ]) ]) -dnl ------ -dnl ZeroMQ -dnl ------ -if test "x$enable_zeromq" != "xno"; then - PKG_CHECK_MODULES([ZEROMQ], [libzmq >= 4.0.0], [ - AC_DEFINE([HAVE_ZEROMQ], [1], [Enable ZeroMQ support]) - ZEROMQ=true - ], [ - if test "x$enable_zeromq" = "xyes"; then - AC_MSG_ERROR([configuration specifies --enable-zeromq but libzmq was not found]) - fi - ]) -fi -AM_CONDITIONAL([ZEROMQ], [test "x$ZEROMQ" = "xtrue"]) - dnl ---------- dnl configure date dnl ---------- dev_version=`echo $VERSION | grep dev` #don't expire deprecated code in non 'dev' branch -if test "${dev_version}" = ""; then +if test "$dev_version" = ""; then CONFDATE=0 else CONFDATE=`date '+%Y%m%d'` @@ -2164,12 +2296,12 @@ dnl ------------------------------ dnl set paths for state directory dnl ------------------------------ AC_MSG_CHECKING([directory to use for state file]) -if test "${prefix}" = "NONE"; then +if test "$prefix" = "NONE"; then frr_statedir_prefix=""; else frr_statedir_prefix=${prefix} fi -if test "${localstatedir}" = '${prefix}/var'; then +if test "$localstatedir" = '${prefix}/var'; then for FRR_STATE_DIR in ${frr_statedir_prefix}/var/run dnl ${frr_statedir_prefix}/var/adm dnl ${frr_statedir_prefix}/etc dnl @@ -2184,21 +2316,21 @@ if test "${localstatedir}" = '${prefix}/var'; then else frr_statedir=${localstatedir} fi -if test $frr_statedir = "/dev/null"; then +if test "$frr_statedir" = "/dev/null"; then AC_MSG_ERROR([STATE DIRECTORY NOT FOUND! FIX OR SPECIFY --localstatedir!]) fi AC_MSG_RESULT([${frr_statedir}]) AC_SUBST([frr_statedir]) -AC_DEFINE_UNQUOTED([LDPD_SOCKET], ["$frr_statedir/ldpd.sock"], [ldpd control socket]) -AC_DEFINE_UNQUOTED([ZEBRA_SERV_PATH], ["$frr_statedir/zserv.api"], [zebra api socket]) -AC_DEFINE_UNQUOTED([BFDD_CONTROL_SOCKET], ["$frr_statedir/bfdd.sock"], [bfdd control socket]) -AC_DEFINE_UNQUOTED([DAEMON_VTY_DIR], ["$frr_statedir"], [daemon vty directory]) +AC_DEFINE_UNQUOTED([LDPD_SOCKET], ["$frr_statedir%s%s/ldpd.sock"], [ldpd control socket]) +AC_DEFINE_UNQUOTED([ZEBRA_SERV_PATH], ["$frr_statedir%s%s/zserv.api"], [zebra api socket]) +AC_DEFINE_UNQUOTED([BFDD_CONTROL_SOCKET], ["$frr_statedir%s%s/bfdd.sock"], [bfdd control socket]) +AC_DEFINE_UNQUOTED([DAEMON_VTY_DIR], ["$frr_statedir%s%s"], [daemon vty directory]) AC_DEFINE_UNQUOTED([DAEMON_DB_DIR], ["$frr_statedir"], [daemon database directory]) dnl autoconf does this, but it does it too late... -test "x$prefix" = xNONE && prefix=$ac_default_prefix -test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' +test "$prefix" = "NONE" && prefix=$ac_default_prefix +test "$exec_prefix" = "NONE" && exec_prefix='${prefix}' dnl get the full path, recursing through variables... vtysh_bin="$bindir/vtysh" @@ -2213,61 +2345,69 @@ CFG_SBIN="$sbindir" CFG_STATE="$frr_statedir" CFG_MODULE="$moduledir" CFG_YANGMODELS="$yangmodelsdir" -CFG_LIBYANG_PLUGINS="$libyang_pluginsdir" for I in 1 2 3 4 5 6 7 8 9 10; do eval CFG_SYSCONF="\"$CFG_SYSCONF\"" eval CFG_SBIN="\"$CFG_SBIN\"" eval CFG_STATE="\"$CFG_STATE\"" eval CFG_MODULE="\"$CFG_MODULE\"" eval CFG_YANGMODELS="\"$CFG_YANGMODELS\"" - eval CFG_LIBYANG_PLUGINS="\"$CFG_LIBYANG_PLUGINS\"" done AC_SUBST([CFG_SYSCONF]) AC_SUBST([CFG_SBIN]) AC_SUBST([CFG_STATE]) AC_SUBST([CFG_MODULE]) AC_SUBST([CFG_YANGMODELS]) -AC_SUBST([CFG_LIBYANG_PLUGINS]) AC_DEFINE_UNQUOTED([MODULE_PATH], ["$CFG_MODULE"], [path to modules]) AC_DEFINE_UNQUOTED([YANG_MODELS_PATH], ["$CFG_YANGMODELS"], [path to YANG data models]) -AC_DEFINE_UNQUOTED([LIBYANG_PLUGINS_PATH], ["$CFG_LIBYANG_PLUGINS"], [path to libyang plugins]) AC_DEFINE_UNQUOTED([WATCHFRR_SH_PATH], ["${CFG_SBIN%/}/watchfrr.sh"], [path to watchfrr.sh]) -dnl ------------------------------------ -dnl Enable RPKI and add librtr to libs -dnl ------------------------------------ -if test "${enable_rpki}" = "yes"; then - PKG_CHECK_MODULES([RTRLIB], [rtrlib >= 0.5.0], - [RPKI=true], - [RPKI=false - AC_MSG_ERROR([rtrlib was not found on your system or is too old.])] - ) -fi -AM_CONDITIONAL([RPKI], [test "x$RPKI" = "xtrue"]) - -dnl ------------------------------------------ -dnl Check whether rtrlib was build with ssh support -dnl ------------------------------------------ -AC_MSG_CHECKING([whether the RTR Library is compiled with SSH]) -AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include "rtrlib/rtrlib.h"]], - [[struct tr_ssh_config config;]])], - [AC_MSG_RESULT([yes]) - AC_DEFINE([FOUND_SSH], [1], [found_ssh])], - AC_MSG_RESULT([no]) -) - -dnl --------------------------- -dnl Check htonl works correctly -dnl --------------------------- -AC_MSG_CHECKING([for working htonl]) -AC_CACHE_VAL(ac_cv_htonl_works, - [AC_LINK_IFELSE([AC_LANG_PROGRAM([FRR_INCLUDES],[htonl (0);])], - [ac_cv_htonl_works=yes], [ac_cv_htonl_works=no]) - ] -) -AC_MSG_RESULT([$ac_cv_htonl_works]) - -AC_CONFIG_FILES([Makefile],[sed -e 's/^#AUTODERP# //' -i Makefile]) +dnl various features +AM_CONDITIONAL([SUPPORT_REALMS], [test "$enable_realms" = "yes"]) +AM_CONDITIONAL([ENABLE_BGP_VNC], [test "$enable_bgp_vnc" != "no"]) +AM_CONDITIONAL([BGP_BMP], [$bgpd_bmp]) +dnl northbound +AM_CONDITIONAL([SQLITE3], [$SQLITE3]) +AM_CONDITIONAL([CONFD], [test "$enable_confd" != ""]) +AM_CONDITIONAL([SYSREPO], [test "$enable_sysrepo" = "yes"]) +AM_CONDITIONAL([GRPC], [test "$enable_grpc" = "yes"]) +AM_CONDITIONAL([ZEROMQ], [test "$ZEROMQ" = "true"]) +dnl plugins +AM_CONDITIONAL([RPKI], [test "$RPKI" = "true"]) +AM_CONDITIONAL([SNMP], [test "$SNMP_METHOD" = "agentx"]) +AM_CONDITIONAL([IRDP], [$IRDP]) +AM_CONDITIONAL([FPM], [test "$enable_fpm" = "yes"]) +AM_CONDITIONAL([HAVE_PROTOBUF], [test "$enable_protobuf" = "yes"]) +AM_CONDITIONAL([HAVE_PROTOBUF3], [$PROTO3]) +dnl daemons +AM_CONDITIONAL([VTYSH], [test "$VTYSH" = "vtysh"]) +AM_CONDITIONAL([ZEBRA], [test "$enable_zebra" != "no"]) +AM_CONDITIONAL([BGPD], [test "$enable_bgpd" != "no"]) +AM_CONDITIONAL([RIPD], [test "$enable_ripd" != "no"]) +AM_CONDITIONAL([OSPFD], [test "$enable_ospfd" != "no"]) +AM_CONDITIONAL([LDPD], [test "$enable_ldpd" != "no"]) +AM_CONDITIONAL([BFDD], [test "$BFDD" = "bfdd"]) +AM_CONDITIONAL([NHRPD], [test "$NHRPD" = "nhrpd"]) +AM_CONDITIONAL([EIGRPD], [test "$enable_eigrpd" != "no"]) +AM_CONDITIONAL([WATCHFRR], [test "$WATCHFRR" = "watchfrr"]) +AM_CONDITIONAL([OSPFCLIENT], [test "$OSPFCLIENT" = "ospfclient"]) +AM_CONDITIONAL([RIPNGD], [test "$enable_ripngd" != "no"]) +AM_CONDITIONAL([BABELD], [test "$enable_babeld" != "no"]) +AM_CONDITIONAL([OSPF6D], [test "$enable_ospf6d" != "no"]) +AM_CONDITIONAL([ISISD], [test "$enable_isisd" != "no"]) +AM_CONDITIONAL([PIMD], [test "$enable_pimd" != "no"]) +AM_CONDITIONAL([PBRD], [test "$enable_pbrd" != "no"]) +AM_CONDITIONAL([SHARPD], [test "$enable_sharpd" = "yes"]) +AM_CONDITIONAL([STATICD], [test "$enable_staticd" != "no"]) +AM_CONDITIONAL([FABRICD], [test "$enable_fabricd" != "no"]) +AM_CONDITIONAL([VRRPD], [test "$enable_vrrpd" != "no"]) + +AC_CONFIG_FILES([Makefile],[ + test "$enable_dev_build" = "yes" && makefile_devbuild="--dev-build" + ${PYTHON} "${ac_abs_top_srcdir}/python/makefile.py" ${makefile_devbuild} || exit 1 +], [ + PYTHON="$PYTHON" + enable_dev_build="$enable_dev_build" +]) AC_CONFIG_FILES([ config.version @@ -2293,7 +2433,7 @@ AC_CONFIG_COMMANDS([lib/route_types.h], [ ${PERL} "${ac_abs_top_srcdir}/lib/route_types.pl" \ < "${ac_abs_top_srcdir}/lib/route_types.txt" \ > "${dst}.tmp" - test -f "${dst}" \ + test -f "$dst" \ && diff "${dst}.tmp" "${dst}" >/dev/null 2>/dev/null \ && rm "${dst}.tmp" \ || mv "${dst}.tmp" "${dst}" @@ -2301,13 +2441,13 @@ AC_CONFIG_COMMANDS([lib/route_types.h], [ PERL="$PERL" ]) -AS_IF([test "x$with_pkg_git_version" = "xyes"], [ +AS_IF([test "$with_pkg_git_version" = "yes"], [ AC_CONFIG_COMMANDS([lib/gitversion.h], [ dst="${ac_abs_top_builddir}/lib/gitversion.h" ${PERL} "${ac_abs_top_srcdir}/lib/gitversion.pl" \ "${ac_abs_top_srcdir}" \ > "${dst}.tmp" - test -f "${dst}" \ + test -f "$dst" \ && diff "${dst}.tmp" "${dst}" >/dev/null 2>/dev/null \ && rm "${dst}.tmp" \ || mv "${dst}.tmp" "${dst}" @@ -2344,7 +2484,9 @@ zebra protobuf enabled : ${enable_protobuf:-no} The above user and group must have read/write access to the state file directory and to the config files in the config file directory." -if test "${enable_doc}" != "no";then - AS_IF([test "$SPHINXBUILD" = /bin/false], - AC_MSG_WARN([sphinx-build is missing but required to build documentation])) +if test "$enable_doc" != "no" -a "$frr_py_mod_sphinx" = "false"; then + AC_MSG_WARN([sphinx is missing but required to build documentation]) +fi +if test "$frr_py_mod_pytest" = "false"; then + AC_MSG_WARN([pytest is missing, unit tests cannot be performed]) fi diff --git a/debian/.gitignore b/debian/.gitignore new file mode 100644 index 0000000000..0b267c6f5c --- /dev/null +++ b/debian/.gitignore @@ -0,0 +1,13 @@ + +/.debhelper/ +/*/ +!/tests/ +!/source + +/*.log +/*.substvars +/*.debhelper +/autoreconf.* +/files +/frr.init +/frr.service diff --git a/debian/README.Debian b/debian/README.Debian index 47a353310d..01b9213ae4 100644 --- a/debian/README.Debian +++ b/debian/README.Debian @@ -52,31 +52,6 @@ used. This option should only be used for systems that do not have systemd, e.g. Ubuntu 14.04. -* Why has SNMP support been disabled? -===================================== -FRR used to link against the NetSNMP libraries to provide SNMP -support. Those libraries sadly link against the OpenSSL libraries -to provide crypto support for SNMPv3 among others. -OpenSSL now is not compatible with the GNU GENERAL PUBLIC LICENSE (GPL) -licence that FRR is distributed under. For more explanation read: - http://www.gnome.org/~markmc/openssl-and-the-gpl.html - http://www.gnu.org/licenses/gpl-faq.html#GPLIncompatibleLibs -Updating the licence to explecitly allow linking against OpenSSL -would requite the affirmation of all people that ever contributed -a significant part to Zebra / Quagga or FRR and thus are the collective -"copyright holder". That's too much work. Using a shrinked down -version of NetSNMP without OpenSSL or convincing the NetSNMP people -to change to GnuTLS are maybe good solutions but not reachable -during the last days before the Sarge release :-( - - *BUT* - -It is allowed by the used licence mix that you fetch the sources and -build FRR yourself with SNMP with - # apt-get -b source -Ppkg.frr.snmp frr -Just distributing it in binary form, linked against OpenSSL, is forbidden. - - * Debian Policy compliance notes ================================ diff --git a/debian/control b/debian/control index ab2df20432..fca6956760 100644 --- a/debian/control +++ b/debian/control @@ -24,17 +24,17 @@ Build-Depends: libsnmp-dev, libssh-dev , libsystemd-dev , - libyang-dev (>= 0.16.74), + libyang-dev (>= 1.0.184), pkg-config, python3, python3-dev, python3-sphinx, python3-pytest , texinfo (>= 4.7) -Standards-Version: 4.2.1 +Standards-Version: 4.4.1 Homepage: https://www.frrouting.org/ -Vcs-Browser: https://github.com/FRRouting/frr/ -Vcs-Git: https://github.com/FRRouting/frr.git +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 @@ -104,6 +104,7 @@ Build-Profiles: Package: frr-doc Section: doc Architecture: all +Multi-Arch: foreign Depends: ${misc:Depends}, libjs-jquery, diff --git a/debian/copyright b/debian/copyright index d1f28a65a2..edd73020bd 100644 --- a/debian/copyright +++ b/debian/copyright @@ -12,6 +12,7 @@ Copyright: 1996-2003 by the original Zebra authors: 2016-2018 by the FRRouting Project Adam Fitzgerald 2017 Alex Couloumbis 2017 + Alexandre Cassen 2001-2017 Alexandre Chappuis 2011 Alexis Fasquel 2015 Ali Rezaee 2018 @@ -47,6 +48,7 @@ Copyright: 1996-2003 by the original Zebra authors: Christoffer Hansen 2018 Christoph Dwertmann 2018 Colin Petrie 2016 + Cumulus Networks 2013-2019 Daniel Kozlowski 2012 Daniel Ng 2008 Daniel Walton 2015-2018 @@ -153,6 +155,7 @@ Copyright: 1996-2003 by the original Zebra authors: Olivier Cochard-Labbé 2014 Olivier Dugeon 2014-2018 Ondrej Zajicek 2009 + Open Source Routing / NetDEF 2012-2017 Pascal Mathis 2018 Paul Jakma 2002-2016 Paul P Komkoff Jr 2008 @@ -266,7 +269,7 @@ Copyright: Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek Copyright 2007, 2008 by Grégoire Henry, Julien Cristau and Juliusz Chroboczek -Files: babeld/babel_errors.* babeld/babel_memory.* +Files: babeld/babel_errors.* License: GPL-2+ Copyright: Copyright (C) 2017-2018 Donald Sharp, Cumulus Networks, Inc. diff --git a/debian/frr-pythontools.install b/debian/frr-pythontools.install index 28140382f6..5f7eaebed5 100644 --- a/debian/frr-pythontools.install +++ b/debian/frr-pythontools.install @@ -1 +1,2 @@ usr/lib/frr/frr-reload.py +usr/lib/frr/generate_support_bundle.py diff --git a/debian/frr.install b/debian/frr.install index ebb87a0b3e..e2485fe8b8 100644 --- a/debian/frr.install +++ b/debian/frr.install @@ -2,13 +2,17 @@ etc/ usr/bin/vtysh usr/bin/mtracebis usr/lib/*/frr/libfrr.* +usr/lib/*/frr/libfrrcares.* usr/lib/*/frr/libfrrospfapiclient.* 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/ diff --git a/debian/frr.lintian-overrides b/debian/frr.lintian-overrides index a3e6fcdc25..616f265e01 100644 --- a/debian/frr.lintian-overrides +++ b/debian/frr.lintian-overrides @@ -6,5 +6,8 @@ frr binary: spelling-error-in-binary usr/lib/frr/zebra writen written frr binary: spelling-error-in-binary usr/lib/frr/pimd writen written frr binary: spelling-error-in-binary usr/lib/frr/pimd iif if +# prefixed man pages for off-PATH daemons +manpage-without-executable + # personal name spelling-error-in-copyright Ang And diff --git a/debian/frr.logrotate b/debian/frr.logrotate index 1dc9122ac4..a56a908bdf 100644 --- a/debian/frr.logrotate +++ b/debian/frr.logrotate @@ -16,8 +16,8 @@ # between file and syslog, rsyslogd might still have file # open, as well as the daemons, so always signal the daemons. # It's safe, a NOP if (only) syslog is being used. - for i in babeld bgpd eigrpd isisd ldpd nhrpd ospf6d ospfd \ - pimd ripd ripngd zebra staticd fabricd; do + for i in babeld bgpd eigrpd isisd ldpd nhrpd ospf6d ospfd sharpd \ + pimd ripd ripngd zebra pbrd staticd bfdd fabricd vrrpd; do if [ -e /var/run/frr/$i.pid ] ; then pids="$pids $(cat /var/run/frr/$i.pid)" fi diff --git a/debian/frr.manpages b/debian/frr.manpages index f5aa972304..5075fd763d 100644 --- a/debian/frr.manpages +++ b/debian/frr.manpages @@ -1,16 +1,16 @@ +doc/manpages/_build/man/frr-bgpd.8 +doc/manpages/_build/man/frr-eigrpd.8 +doc/manpages/_build/man/frr-fabricd.8 +doc/manpages/_build/man/frr-isisd.8 +doc/manpages/_build/man/frr-ldpd.8 +doc/manpages/_build/man/frr-nhrpd.8 +doc/manpages/_build/man/frr-ospf6d.8 +doc/manpages/_build/man/frr-ospfd.8 +doc/manpages/_build/man/frr-pimd.8 +doc/manpages/_build/man/frr-ripd.8 +doc/manpages/_build/man/frr-ripngd.8 +doc/manpages/_build/man/frr-watchfrr.8 +doc/manpages/_build/man/frr-zebra.8 doc/manpages/_build/man/frr.1 -doc/manpages/_build/man/bgpd.8 -doc/manpages/_build/man/pimd.8 -doc/manpages/_build/man/eigrpd.8 -doc/manpages/_build/man/ldpd.8 -doc/manpages/_build/man/nhrpd.8 -doc/manpages/_build/man/ospf6d.8 -doc/manpages/_build/man/ospfd.8 -doc/manpages/_build/man/ripd.8 -doc/manpages/_build/man/ripngd.8 -doc/manpages/_build/man/vtysh.1 -doc/manpages/_build/man/zebra.8 -doc/manpages/_build/man/isisd.8 -doc/manpages/_build/man/watchfrr.8 doc/manpages/_build/man/mtracebis.8 -doc/manpages/_build/man/fabricd.8 +doc/manpages/_build/man/vtysh.1 diff --git a/debian/rules b/debian/rules index a546f38d70..c8550ecb52 100755 --- a/debian/rules +++ b/debian/rules @@ -71,6 +71,7 @@ override_dh_auto_install: dh_auto_install sed -e '1c #!/usr/bin/python3' -i debian/tmp/usr/lib/frr/frr-reload.py + sed -e '1c #!/usr/bin/python3' -i debian/tmp/usr/lib/frr/generate_support_bundle.py # let dh_systemd_* and dh_installinit do their thing automatically ifeq ($(filter pkg.frr.nosystemd,$(DEB_BUILD_PROFILES)),) diff --git a/defaults.h b/defaults.h deleted file mode 100644 index fd38aaed70..0000000000 --- a/defaults.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * FRR switchable defaults. - * Copyright (C) 2017 David Lamparter for NetDEF, Inc. - * - * This file is part of FRRouting (FRR). - * - * FRR is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2, or (at your option) any later version. - * - * FRR is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along - * with this program; see the file COPYING; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef _FRR_DEFAULTS_H -#define _FRR_DEFAULTS_H - -#include "config.h" - -#ifdef HAVE_DATACENTER - -#define DFLT_BGP_IMPORT_CHECK 1 -#define DFLT_BGP_TIMERS_CONNECT 10 -#define DFLT_BGP_HOLDTIME 9 -#define DFLT_BGP_KEEPALIVE 3 -#define DFLT_BGP_LOG_NEIGHBOR_CHANGES 1 -#define DFLT_BGP_SHOW_HOSTNAME 1 -#define DFLT_BGP_DETERMINISTIC_MED 1 - -#define DFLT_OSPF_LOG_ADJACENCY_CHANGES 1 -#define DFLT_OSPF6_LOG_ADJACENCY_CHANGES 1 - -#else /* !HAVE_DATACENTER */ - -#define DFLT_BGP_IMPORT_CHECK 0 -#define DFLT_BGP_TIMERS_CONNECT 120 -#define DFLT_BGP_HOLDTIME 180 -#define DFLT_BGP_KEEPALIVE 60 -#define DFLT_BGP_LOG_NEIGHBOR_CHANGES 0 -#define DFLT_BGP_SHOW_HOSTNAME 0 -#define DFLT_BGP_DETERMINISTIC_MED 0 - -#define DFLT_OSPF_LOG_ADJACENCY_CHANGES 0 -#define DFLT_OSPF6_LOG_ADJACENCY_CHANGES 0 - -#endif /* !HAVE_DATACENTER */ - -#endif /* _FRR_DEFAULTS_H */ diff --git a/doc/developer/building-docker.rst b/doc/developer/building-docker.rst new file mode 100644 index 0000000000..852a295fd0 --- /dev/null +++ b/doc/developer/building-docker.rst @@ -0,0 +1,101 @@ +Docker +====== + +This page covers how to build FRR Docker images. + +Images +"""""" +FRR has Docker build infrastructure to produce Docker images containing +source-built FRR on the following base platforms: + +* Alpine +* Centos 7 +* Centos 8 + +The following platform images may also be built, but these simply install a +binary package from an existing repository and do not perform source builds: + +* Debian 10 + +Some of these are available on `DockerHub +`_. + +There is no guarantee on what is and is not available from DockerHub at time of +writing. + +Scripts +""""""" + +Some platforms contain an included build script that may be run from the host. +This will set appropriate packaging environment variables and clean up +intermediate build images. + +These scripts serve another purpose. They allow building platform packages +without needing the platform. For example, the Centos 8 docker image can also +be leveraged to build Centos 8 RPMs that can then be used separately from +Docker. + +If you are only interested in the Docker images and don't want the cleanup +functionality of the scripts you can ignore them and perform a normal Docker +build. If you want to build multi-arch docker images this is required as the +scripts do not support using Buildkit for multi-arch builds. + +Building Alpine Image +--------------------- + +Script:: + + ./docker/alpine/build.sh + +No script:: + + docker build -f docker/alpine/Dockerfile . + +No script, multi-arch (ex. amd64, arm64, armv7):: + + docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -f docker/alpine/Dockerfile -t frr:latest . + + +Building Debian Image +--------------------- + +:: + + cd docker/debian + docker build . + +Multi-arch (ex. amd64, arm64, armv7):: + + cd docker/debian + docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t frr-debian:latest . + +Building Centos 7 Image +----------------------- + +Script:: + + ./docker/centos-7/build.sh + +No script:: + + docker build -f docker/centos-7/Dockerfile . + +No script, multi-arch (ex. amd64, arm64):: + + docker buildx build --platform linux/amd64,linux/arm64 -f docker/centos-7/Dockerfile -t frr-centos7:latest . + + +Building Centos 8 Image +----------------------- + +Script:: + + ./docker/centos-8/build.sh + +No script:: + + docker build -f docker/centos-8/Dockerfile . + +No script, multi-arch (ex. amd64, arm64):: + + docker buildx build --platform linux/amd64,linux/arm64 -f docker/centos-8/Dockerfile -t frr-centos8:latest . diff --git a/doc/developer/building-frr-for-archlinux.rst b/doc/developer/building-frr-for-archlinux.rst new file mode 100644 index 0000000000..f62add5963 --- /dev/null +++ b/doc/developer/building-frr-for-archlinux.rst @@ -0,0 +1,129 @@ +Arch Linux +================ + +Installing Dependencies +----------------------- + +.. code-block:: console + + sudo pacman -Syu + sudo pacman -S \ + git autoconf automake libtool make cmake pcre readline texinfo \ + pkg-config pam json-c bison flex python-pytest \ + c-ares python systemd python2-ipaddress python-sphinx \ + systemd-libs net-snmp perl libcap + +.. include:: building-libyang.rst + +Protobuf +^^^^^^^^ + +.. code-block:: console + + sudo pacman -S protobuf-c + +ZeroMQ +^^^^^^ + +.. code-block:: console + + sudo pacman -S zeromq + +Building & Installing FRR +------------------------- + +Add FRR user and groups +^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: console + + sudo groupadd -r -g 92 frr + sudo groupadd -r -g 85 frrvty + sudo useradd --system -g frr --home-dir /var/run/frr/ \ + -c "FRR suite" --shell /sbin/nologin frr + sudo usermod -a -G frrvty frr + +Compile +^^^^^^^ + +.. include:: include-compile.rst + +Install FRR configuration files +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: console + + sudo install -m 775 -o frr -g frr -d /var/log/frr + sudo install -m 775 -o frr -g frrvty -d /etc/frr + sudo install -m 640 -o frr -g frrvty tools/etc/frr/vtysh.conf /etc/frr/vtysh.conf + sudo install -m 640 -o frr -g frr tools/etc/frr/frr.conf /etc/frr/frr.conf + sudo install -m 640 -o frr -g frr tools/etc/frr/daemons.conf /etc/frr/daemons.conf + sudo install -m 640 -o frr -g frr tools/etc/frr/daemons /etc/frr/daemons + +Tweak sysctls +^^^^^^^^^^^^^ + +Some sysctls need to be changed in order to enable IPv4/IPv6 forwarding and +MPLS (if supported by your platform). If your platform does not support MPLS, +skip the MPLS related configuration in this section. + +Edit :file:`/etc/sysctl.conf` [*Create the file if it doesn't exist*] and +append the following values (ignore the other settings): + +:: + + # Enable packet forwarding for IPv4 + net.ipv4.ip_forward=1 + + # Enable packet forwarding for IPv6 + net.ipv6.conf.all.forwarding=1 + +Reboot or use ``sysctl -p`` to apply the same config to the running system. + +Add MPLS kernel modules +""""""""""""""""""""""" + +To +enable, add the following lines to :file:`/etc/modules-load.d/modules.conf`: + +:: + + # Load MPLS Kernel Modules + mpls_router + mpls_iptunnel + + +And load the kernel modules on the running system: + +.. code-block:: console + + sudo modprobe mpls-router mpls-iptunnel + +Enable MPLS Forwarding +"""""""""""""""""""""" + +Edit :file:`/etc/sysctl.conf` and the following lines. Make sure to add a line +equal to :file:`net.mpls.conf.eth0.input` for each interface used with MPLS. + +:: + + # Enable MPLS Label processing on all interfaces + net.mpls.conf.eth0.input=1 + net.mpls.conf.eth1.input=1 + net.mpls.conf.eth2.input=1 + net.mpls.platform_labels=100000 + +Install service files +^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: console + + sudo install -m 644 tools/frr.service /etc/systemd/system/frr.service + sudo systemctl enable frr + +Start FRR +^^^^^^^^^ + +.. code-block:: shell + + systemctl start frr diff --git a/doc/developer/building-frr-for-centos6.rst b/doc/developer/building-frr-for-centos6.rst index f1ec2ad3ea..b730a5ee32 100644 --- a/doc/developer/building-frr-for-centos6.rst +++ b/doc/developer/building-frr-for-centos6.rst @@ -45,7 +45,7 @@ Add packages: sudo yum install git autoconf automake libtool make \ readline-devel texinfo net-snmp-devel groff pkgconfig \ - json-c-devel pam-devel flex epel-release c-ares-devel + json-c-devel pam-devel flex epel-release c-ares-devel libcap-devel Install newer version of bison (CentOS 6 package source is too old) from CentOS 7: @@ -116,7 +116,19 @@ Update rpm database & Install newer sphinx sudo yum update sudo yum install python27-sphinx -.. include:: building-libyang.rst +Install libyang and its dependencies: + +.. code-block:: shell + + sudo yum install pcre-devel doxygen cmake + git clone https://github.com/CESNET/libyang.git + cd libyang + git checkout 090926a89d59a3c4000719505d563aaf6ac60f2 + mkdir build ; cd build + cmake -DENABLE_LYD_PRIV=ON -DCMAKE_INSTALL_PREFIX:PATH=/usr -D CMAKE_BUILD_TYPE:String="Release" .. + make build-rpm + sudo yum install ./rpms/RPMS/x86_64/libyang-0.16.111-0.x86_64.rpm ./rpms/RPMS/x86_64/libyang-devel-0.16.111-0.x86_64.rpm + cd ../.. Get FRR, compile it and install it (from Git) --------------------------------------------- @@ -163,10 +175,9 @@ an example.) --disable-ldpd \ --enable-fpm \ --with-pkg-git-version \ - --with-pkg-extra-version=-MyOwnFRRVersion \ - SPHINXBUILD=sphinx-build2.7 + --with-pkg-extra-version=-MyOwnFRRVersion make - make check PYTHON=/usr/bin/python2.7 + make check sudo make install Create empty FRR configuration files @@ -214,7 +225,7 @@ Install daemon config file .. code-block:: shell - sudo install -p -m 644 redhat/daemons /etc/frr/ + sudo install -p -m 644 tools/etc/frr/daemons /etc/frr/ sudo chown frr:frr /etc/frr/daemons Edit /etc/frr/daemons as needed to select the required daemons @@ -242,12 +253,12 @@ Load the modified sysctl's on the system: sudo sysctl -p /etc/sysctl.d/90-routing-sysctl.conf -Add init.d startup files -^^^^^^^^^^^^^^^^^^^^^^^^ +Add init.d startup file +^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: shell - sudo install -p -m 755 redhat/frr.init /etc/init.d/frr + sudo install -p -m 755 tools/frr /etc/init.d/frr sudo chkconfig --add frr Enable FRR daemon at startup diff --git a/doc/developer/building-frr-for-centos7.rst b/doc/developer/building-frr-for-centos7.rst index ea3c44478c..9b9ff2f050 100644 --- a/doc/developer/building-frr-for-centos7.rst +++ b/doc/developer/building-frr-for-centos7.rst @@ -14,14 +14,20 @@ CentOS 7 restrictions: Install required packages ------------------------- +Add EPEL Repository: + +:: + + sudo yum -y install epel-release + Add packages: :: - sudo yum install git autoconf automake libtool make \ + sudo yum install git autoconf automake libtool make cmake \ readline-devel texinfo net-snmp-devel groff pkgconfig \ json-c-devel pam-devel bison flex pytest c-ares-devel \ - python-devel systemd-devel python-sphinx + python3-devel systemd-devel python3-sphinx libcap-devel .. include:: building-libyang.rst @@ -70,7 +76,8 @@ an example.) --disable-ldpd \ --enable-fpm \ --with-pkg-git-version \ - --with-pkg-extra-version=-MyOwnFRRVersion + --with-pkg-extra-version=-MyOwnFRRVersion \ + SPHINXBUILD=/usr/bin/sphinx-build make make check sudo make install @@ -103,7 +110,7 @@ Install daemon config file :: - sudo install -p -m 644 redhat/daemons /etc/frr/ + sudo install -p -m 644 tools/etc/frr/daemons /etc/frr/ sudo chown frr:frr /etc/frr/daemons Edit /etc/frr/daemons as needed to select the required daemons @@ -132,13 +139,12 @@ Load the modified sysctl's on the system: sudo sysctl -p /etc/sysctl.d/90-routing-sysctl.conf -Install frr Service and redhat init files -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Install frr Service +^^^^^^^^^^^^^^^^^^^ :: - sudo install -p -m 644 redhat/frr.service /usr/lib/systemd/system/frr.service - sudo install -p -m 755 redhat/frr.init /usr/lib/frr/frr + sudo install -p -m 644 tools/frr.service /usr/lib/systemd/system/frr.service Register the systemd files ^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/doc/developer/building-frr-for-centos8.rst b/doc/developer/building-frr-for-centos8.rst new file mode 100644 index 0000000000..75beb53378 --- /dev/null +++ b/doc/developer/building-frr-for-centos8.rst @@ -0,0 +1,155 @@ +CentOS 8 +======== + +This document describes installation from source. If you want to build an RPM, +see :ref:`packaging-redhat`. + +Install required packages +------------------------- + +Add packages: + +:: + + sudo dnf install --enablerepo=PowerTools git autoconf pcre-devel \ + automake libtool make readline-devel texinfo net-snmp-devel pkgconfig \ + groff pkgconfig json-c-devel pam-devel bison flex python2-pytest \ + c-ares-devel python2-devel systemd-devel libcap-devel + +.. include:: building-libyang.rst + +Get FRR, compile it and install it (from Git) +--------------------------------------------- + +**This assumes you want to build and install FRR from source and not +using any packages** + +Add frr groups and user +^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + sudo groupadd -g 92 frr + sudo groupadd -r -g 85 frrvty + sudo useradd -u 92 -g 92 -M -r -G frrvty -s /sbin/nologin \ + -c "FRR FRRouting suite" -d /var/run/frr frr + +Download Source, configure and compile it +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +(You may prefer different options on configure statement. These are just +an example.) + +:: + + git clone https://github.com/frrouting/frr.git frr + cd frr + ./bootstrap.sh + ./configure \ + --bindir=/usr/bin \ + --sbindir=/usr/lib/frr \ + --sysconfdir=/etc/frr \ + --libdir=/usr/lib/frr \ + --libexecdir=/usr/lib/frr \ + --localstatedir=/var/run/frr \ + --with-moduledir=/usr/lib/frr/modules \ + --enable-snmp=agentx \ + --enable-multipath=64 \ + --enable-user=frr \ + --enable-group=frr \ + --enable-vty-group=frrvty \ + --enable-systemd=yes \ + --disable-exampledir \ + --disable-ldpd \ + --enable-fpm \ + --with-pkg-git-version \ + --with-pkg-extra-version=-MyOwnFRRVersion \ + SPHINXBUILD=/usr/bin/sphinx-build + make + make check + sudo make install + +Create empty FRR configuration files +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + sudo mkdir /var/log/frr + sudo mkdir /etc/frr + sudo touch /etc/frr/zebra.conf + sudo touch /etc/frr/bgpd.conf + sudo touch /etc/frr/ospfd.conf + sudo touch /etc/frr/ospf6d.conf + sudo touch /etc/frr/isisd.conf + sudo touch /etc/frr/ripd.conf + sudo touch /etc/frr/ripngd.conf + sudo touch /etc/frr/pimd.conf + sudo touch /etc/frr/nhrpd.conf + sudo touch /etc/frr/eigrpd.conf + sudo touch /etc/frr/babeld.conf + sudo chown -R frr:frr /etc/frr/ + sudo touch /etc/frr/vtysh.conf + sudo chown frr:frrvty /etc/frr/vtysh.conf + sudo chmod 640 /etc/frr/*.conf + +Install daemon config file +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + sudo install -p -m 644 tools/etc/frr/daemons /etc/frr/ + sudo chown frr:frr /etc/frr/daemons + +Edit /etc/frr/daemons as needed to select the required daemons +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Look for the section with ``watchfrr_enable=...`` and ``zebra=...`` etc. +Enable the daemons as required by changing the value to ``yes`` + +Enable IP & IPv6 forwarding +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Create a new file ``/etc/sysctl.d/90-routing-sysctl.conf`` with the +following content: + +:: + + # Sysctl for routing + # + # Routing: We need to forward packets + net.ipv4.conf.all.forwarding=1 + net.ipv6.conf.all.forwarding=1 + +Load the modified sysctl's on the system: + +:: + + sudo sysctl -p /etc/sysctl.d/90-routing-sysctl.conf + +Install frr Service +^^^^^^^^^^^^^^^^^^^ + +:: + + sudo install -p -m 644 tools/frr.service /usr/lib/systemd/system/frr.service + +Register the systemd files +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + sudo systemctl preset frr.service + +Enable required frr at startup +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + sudo systemctl enable frr + +Reboot or start FRR manually +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + sudo systemctl start frr diff --git a/doc/developer/building-frr-for-debian8.rst b/doc/developer/building-frr-for-debian8.rst index 9c6e48a6f8..c12bf46f8d 100644 --- a/doc/developer/building-frr-for-debian8.rst +++ b/doc/developer/building-frr-for-debian8.rst @@ -16,14 +16,15 @@ Add packages: :: sudo apt-get install git autoconf automake libtool make \ - libreadline-dev texinfo libjson-c-dev pkg-config bison flex python-pip \ - libc-ares-dev python3-dev python3-sphinx build-essential libsystemd-dev + libreadline-dev texinfo libjson-c-dev pkg-config bison flex python3-pip \ + libc-ares-dev python3-dev python3-sphinx build-essential libsystemd-dev \ + libsnmp-dev libcap-dev Install newer pytest (>3.0) from pip :: - sudo pip install pytest + sudo pip3 install pytest .. include:: building-libyang.rst diff --git a/doc/developer/building-frr-for-debian9.rst b/doc/developer/building-frr-for-debian9.rst index 39e7488cd7..f976b9f49a 100644 --- a/doc/developer/building-frr-for-debian9.rst +++ b/doc/developer/building-frr-for-debian9.rst @@ -9,9 +9,9 @@ Add packages: :: sudo apt-get install git autoconf automake libtool make \ - libreadline-dev texinfo libjson-c-dev pkg-config bison flex python-pip \ - libc-ares-dev python3-dev python-pytest python3-sphinx build-essential \ - libsystemd-dev + libreadline-dev texinfo libjson-c-dev pkg-config bison flex \ + libc-ares-dev python3-dev python3-pytest python3-sphinx build-essential \ + libsnmp-dev libsystemd-dev libcap-dev .. include:: building-libyang.rst diff --git a/doc/developer/building-frr-for-fedora.rst b/doc/developer/building-frr-for-fedora.rst index 204c185f56..4ab59490fd 100644 --- a/doc/developer/building-frr-for-fedora.rst +++ b/doc/developer/building-frr-for-fedora.rst @@ -13,8 +13,8 @@ Installing Dependencies sudo dnf install git autoconf automake libtool make \ readline-devel texinfo net-snmp-devel groff pkgconfig json-c-devel \ - pam-devel pytest bison flex c-ares-devel python3-devel python2-sphinx \ - perl-core patch + pam-devel python3-pytest bison flex c-ares-devel python3-devel \ + python3-sphinx perl-core patch systemd-devel libcap-devel .. include:: building-libyang.rst @@ -98,13 +98,24 @@ And load the kernel modules on the running system: sudo modprobe mpls-router mpls-iptunnel -Install service files -^^^^^^^^^^^^^^^^^^^^^ + +.. note:: + Fedora ships with the ``firewalld`` service enabled. You may run into some + issues with the iptables rules it installs by default. If you wish to just + stop the service and clear `ALL` rules do these commands: + + .. code-block:: console + + sudo systemctl disable firewalld.service + sudo systemctl stop firewalld.service + sudo iptables -F + +Install frr Service +^^^^^^^^^^^^^^^^^^^ .. code-block:: console - sudo install -p -m 644 redhat/frr.service /usr/lib/systemd/system/frr.service - sudo install -p -m 755 redhat/frr.init /usr/lib/frr/frr + sudo install -p -m 644 tools/frr.service /usr/lib/systemd/system/frr.service sudo systemctl enable frr Enable daemons diff --git a/doc/developer/building-frr-for-freebsd10.rst b/doc/developer/building-frr-for-freebsd10.rst index 86c44f4d90..e85cb80053 100644 --- a/doc/developer/building-frr-for-freebsd10.rst +++ b/doc/developer/building-frr-for-freebsd10.rst @@ -17,7 +17,7 @@ is first package install and asked) :: pkg install git autoconf automake libtool gmake json-c pkgconf \ - bison flex py27-pytest c-ares python3 py-sphinx + bison flex py36-pytest c-ares python3.6 py36-sphinx Make sure there is no /usr/bin/flex preinstalled (and use the newly installed in /usr/local/bin): (FreeBSD frequently provides a older flex diff --git a/doc/developer/building-frr-for-freebsd11.rst b/doc/developer/building-frr-for-freebsd11.rst index 5e56c8cd7a..b97538b763 100644 --- a/doc/developer/building-frr-for-freebsd11.rst +++ b/doc/developer/building-frr-for-freebsd11.rst @@ -17,7 +17,7 @@ is first package install and asked) .. code-block:: shell pkg install git autoconf automake libtool gmake json-c pkgconf \ - bison flex py27-pytest c-ares python3 py36-sphinx texinfo + bison flex py36-pytest c-ares python3.6 py36-sphinx texinfo Make sure there is no /usr/bin/flex preinstalled (and use the newly installed in /usr/local/bin): (FreeBSD frequently provides a older flex diff --git a/doc/developer/building-frr-for-freebsd9.rst b/doc/developer/building-frr-for-freebsd9.rst index 59241d1d13..1e97749795 100644 --- a/doc/developer/building-frr-for-freebsd9.rst +++ b/doc/developer/building-frr-for-freebsd9.rst @@ -17,8 +17,8 @@ is first package install and asked) :: pkg install -y git autoconf automake libtool gmake \ - pkgconf texinfo json-c bison flex py27-pytest c-ares \ - python3 py-sphinx libexecinfo + pkgconf texinfo json-c bison flex py36-pytest c-ares \ + python3 py36-sphinx libexecinfo Make sure there is no /usr/bin/flex preinstalled (and use the newly installed in /usr/local/bin): (FreeBSD frequently provides a older flex diff --git a/doc/developer/building-frr-for-netbsd6.rst b/doc/developer/building-frr-for-netbsd6.rst index 49091c49b4..e50d11130a 100644 --- a/doc/developer/building-frr-for-netbsd6.rst +++ b/doc/developer/building-frr-for-netbsd6.rst @@ -23,7 +23,7 @@ Add packages: :: sudo pkg_add git autoconf automake libtool gmake openssl \ - pkg-config json-c python27 py27-test python35 py-sphinx + pkg-config json-c py36-test python36 py36-sphinx Install SSL Root Certificates (for git https access): @@ -33,13 +33,6 @@ Install SSL Root Certificates (for git https access): sudo touch /etc/openssl/openssl.cnf sudo mozilla-rootcerts install -Select default Python and py.test - -:: - - sudo ln -s /usr/pkg/bin/python2.7 /usr/bin/python - sudo ln -s /usr/pkg/bin/py.test-2.7 /usr/bin/py.test - .. include:: building-libyang.rst Get FRR, compile it and install it (from Git) diff --git a/doc/developer/building-frr-for-netbsd7.rst b/doc/developer/building-frr-for-netbsd7.rst index 64c462a5c8..32d1145edc 100644 --- a/doc/developer/building-frr-for-netbsd7.rst +++ b/doc/developer/building-frr-for-netbsd7.rst @@ -14,7 +14,7 @@ Install required packages :: sudo pkgin install git autoconf automake libtool gmake openssl \ - pkg-config json-c python27 py27-test python35 py-sphinx + pkg-config json-c python36 py36-test py36-sphinx Install SSL Root Certificates (for git https access): @@ -24,13 +24,6 @@ Install SSL Root Certificates (for git https access): sudo touch /etc/openssl/openssl.cnf sudo mozilla-rootcerts install -Select default Python and py.test - -:: - - sudo ln -s /usr/pkg/bin/python2.7 /usr/bin/python - sudo ln -s /usr/pkg/bin/py.test-2.7 /usr/bin/py.test - .. include:: building-libyang.rst Get FRR, compile it and install it (from Git) diff --git a/doc/developer/building-frr-for-omnios.rst b/doc/developer/building-frr-for-omnios.rst index ffc7a078e5..3a69279b0c 100644 --- a/doc/developer/building-frr-for-omnios.rst +++ b/doc/developer/building-frr-for-omnios.rst @@ -60,7 +60,7 @@ Add pytest: :: - pip install pytest + pip install "pytest<5" Install Sphinx::: diff --git a/doc/developer/building-frr-for-openwrt.rst b/doc/developer/building-frr-for-openwrt.rst index 5d8f82f27e..47cf2cbd43 100644 --- a/doc/developer/building-frr-for-openwrt.rst +++ b/doc/developer/building-frr-for-openwrt.rst @@ -1,6 +1,8 @@ -OpenWRT +OpenWrt ======= +General info about OpenWrt buildsystem: `link `_. + Prepare build environment ------------------------- @@ -13,16 +15,16 @@ For Debian based distributions, run: For other environments, instructions can be found in the `official documentation -`_. +`_. -Get OpenWRT Sources (from Git) +Get OpenWrt Sources (from Git) ------------------------------ .. note:: - The OpenWRT build will fail if you run it as root. So take care to run it as a nonprivileged user. + The OpenWrt build will fail if you run it as root. So take care to run it as a nonprivileged user. -Clone the OpenWRT sources and retrieve the package feeds +Clone the OpenWrt sources and retrieve the package feeds :: @@ -30,21 +32,15 @@ Clone the OpenWRT sources and retrieve the package feeds cd openwrt ./scripts/feeds update -a ./scripts/feeds install -a - cd feeds/routing - git fetch origin pull/319/head - git read-tree --prefix=frr/ -u FETCH_HEAD:frr - cd ../../package/feeds/routing/ - ln -sv ../../../feeds/routing/frr . - cd ../../.. - -Configure OpenWRT for your target and select the needed FRR packages in Network -> Routing and Redirection -> frr, + +Configure OpenWrt for your target and select the needed FRR packages in Network -> Routing and Redirection -> frr, exit and save :: make menuconfig -Then, to compile either a complete OpenWRT image, or the FRR packages, run: +Then, to compile either a complete OpenWrt image, or the FRR packages, run: :: @@ -54,10 +50,16 @@ It may be possible that on first build ``make package/frr/compile`` not to work and it may be needed to run a ``make`` for the entire build environment. Add ``V=s`` to get more debugging output. +More information about OpenWrt buildsystem can be found `here +`__. + Work with sources ----------------- -To update to a newer version, or change other options, you need to edit the ``feeds/routing/frr/Makefile``. +To update to a newer version, or change other options, you need to edit the ``feeds/packages/frr/Makefile``. + +More information about working with patches in OpenWrt buildsystem can be found `here +`__. Usage ----- diff --git a/doc/developer/building-frr-for-ubuntu1404.rst b/doc/developer/building-frr-for-ubuntu1404.rst index 6e2765c1c8..cc54415266 100644 --- a/doc/developer/building-frr-for-ubuntu1404.rst +++ b/doc/developer/building-frr-for-ubuntu1404.rst @@ -12,9 +12,9 @@ Installing Dependencies apt-get update apt-get install \ git autoconf automake libtool make libreadline-dev texinfo \ - pkg-config libpam0g-dev libjson-c-dev bison flex python-pytest \ + pkg-config libpam0g-dev libjson-c-dev bison flex python3-pytest \ libc-ares-dev python3-dev python3-sphinx install-info build-essential \ - libsnmp-dev perl + libsnmp-dev perl libcap-dev .. include:: building-libyang.rst diff --git a/doc/developer/building-frr-for-ubuntu1604.rst b/doc/developer/building-frr-for-ubuntu1604.rst index a9a0a2f733..63c6f8648c 100644 --- a/doc/developer/building-frr-for-ubuntu1604.rst +++ b/doc/developer/building-frr-for-ubuntu1604.rst @@ -12,9 +12,9 @@ Installing Dependencies apt-get update apt-get install \ git autoconf automake libtool make libreadline-dev texinfo \ - pkg-config libpam0g-dev libjson-c-dev bison flex python-pytest \ + pkg-config libpam0g-dev libjson-c-dev bison flex python3-pytest \ libc-ares-dev python3-dev libsystemd-dev python-ipaddress python3-sphinx \ - install-info build-essential libsystemd-dev libsnmp-dev perl + install-info build-essential libsystemd-dev libsnmp-dev perl libcap-dev .. include:: building-libyang.rst diff --git a/doc/developer/building-frr-for-ubuntu1804.rst b/doc/developer/building-frr-for-ubuntu1804.rst index 1320bda577..9d85957d88 100644 --- a/doc/developer/building-frr-for-ubuntu1804.rst +++ b/doc/developer/building-frr-for-ubuntu1804.rst @@ -12,9 +12,9 @@ Installing Dependencies sudo apt update sudo apt-get install \ git autoconf automake libtool make libreadline-dev texinfo \ - pkg-config libpam0g-dev libjson-c-dev bison flex python-pytest \ + pkg-config libpam0g-dev libjson-c-dev bison flex python3-pytest \ libc-ares-dev python3-dev libsystemd-dev python-ipaddress python3-sphinx \ - install-info build-essential libsystemd-dev libsnmp-dev perl + install-info build-essential libsystemd-dev libsnmp-dev perl libcap-dev .. include:: building-libyang.rst @@ -104,6 +104,10 @@ And load the kernel modules on the running system: sudo modprobe mpls-router mpls-iptunnel +If the above command returns an error, you may need to install the appropriate +or latest linux-modules-extra--generic package. For example +``apt-get install linux-modules-extra-`uname -r`-generic`` + Enable MPLS Forwarding """""""""""""""""""""" diff --git a/doc/developer/building-frr-for-ubuntu2004.rst b/doc/developer/building-frr-for-ubuntu2004.rst new file mode 100644 index 0000000000..ef5d8da551 --- /dev/null +++ b/doc/developer/building-frr-for-ubuntu2004.rst @@ -0,0 +1,162 @@ +Ubuntu 20.04 LTS +================ + +This document describes installation from source. If you want to build a +``deb``, see :ref:`packaging-debian`. + +Installing Dependencies +----------------------- + +.. code-block:: console + + sudo apt update + sudo apt-get install \ + git autoconf automake libtool make libreadline-dev texinfo \ + pkg-config libpam0g-dev libjson-c-dev bison flex python3-pytest \ + libc-ares-dev python3-dev libsystemd-dev python-ipaddress python3-sphinx \ + install-info build-essential libsystemd-dev libsnmp-dev perl \ + libcap-dev python2 + +Note that Ubuntu 20 no longer installs python 2.x, so it must be +installed explicitly. Ensure that your system has a symlink named +``/usr/bin/python`` pointing at ``/usr/bin/python3``. + +In addition, ``pip`` for python2 must be installed if you wish to run +the FRR topotests. That version of ``pip`` is not available from the +ubuntu apt repositories; in order to install it: + +.. code-block:: shell + + curl https://bootstrap.pypa.io/2.7/get-pip.py --output get-pip.py + sudo python2 ./get-pip.py + + # And verify the installation + pip2 --version + +.. include:: building-libyang.rst + +Protobuf +^^^^^^^^ + +.. code-block:: console + + sudo apt-get install protobuf-c-compiler libprotobuf-c-dev + +ZeroMQ +^^^^^^ + +.. code-block:: console + + sudo apt-get install libzmq5 libzmq3-dev + +Building & Installing FRR +------------------------- + +Add FRR user and groups +^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: console + + sudo groupadd -r -g 92 frr + sudo groupadd -r -g 85 frrvty + sudo adduser --system --ingroup frr --home /var/run/frr/ \ + --gecos "FRR suite" --shell /sbin/nologin frr + sudo usermod -a -G frrvty frr + +Compile +^^^^^^^ + +.. include:: include-compile.rst + +Install FRR configuration files +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: console + + sudo install -m 775 -o frr -g frr -d /var/log/frr + sudo install -m 775 -o frr -g frrvty -d /etc/frr + sudo install -m 640 -o frr -g frrvty tools/etc/frr/vtysh.conf /etc/frr/vtysh.conf + sudo install -m 640 -o frr -g frr tools/etc/frr/frr.conf /etc/frr/frr.conf + sudo install -m 640 -o frr -g frr tools/etc/frr/daemons.conf /etc/frr/daemons.conf + sudo install -m 640 -o frr -g frr tools/etc/frr/daemons /etc/frr/daemons + +Tweak sysctls +^^^^^^^^^^^^^ + +Some sysctls need to be changed in order to enable IPv4/IPv6 forwarding and +MPLS (if supported by your platform). If your platform does not support MPLS, +skip the MPLS related configuration in this section. + +Edit :file:`/etc/sysctl.conf` and uncomment the following values (ignore the +other settings): + +:: + + # Uncomment the next line to enable packet forwarding for IPv4 + net.ipv4.ip_forward=1 + + # Uncomment the next line to enable packet forwarding for IPv6 + # Enabling this option disables Stateless Address Autoconfiguration + # based on Router Advertisements for this host + net.ipv6.conf.all.forwarding=1 + +Reboot or use ``sysctl -p`` to apply the same config to the running system. + +Add MPLS kernel modules +""""""""""""""""""""""" + +Ubuntu 20.04 ships with kernel 5.4; MPLS modules are present by default. To +enable, add the following lines to :file:`/etc/modules-load.d/modules.conf`: + +:: + + # Load MPLS Kernel Modules + mpls_router + mpls_iptunnel + + +And load the kernel modules on the running system: + +.. code-block:: console + + sudo modprobe mpls-router mpls-iptunnel + +If the above command returns an error, you may need to install the appropriate +or latest linux-modules-extra--generic package. For example +``apt-get install linux-modules-extra-`uname -r`-generic`` + +Enable MPLS Forwarding +"""""""""""""""""""""" + +Edit :file:`/etc/sysctl.conf` and the following lines. Make sure to add a line +equal to :file:`net.mpls.conf.eth0.input` for each interface used with MPLS. + +:: + + # Enable MPLS Label processing on all interfaces + net.mpls.conf.eth0.input=1 + net.mpls.conf.eth1.input=1 + net.mpls.conf.eth2.input=1 + net.mpls.platform_labels=100000 + +Install service files +^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: console + + sudo install -m 644 tools/frr.service /etc/systemd/system/frr.service + sudo systemctl enable frr + +Enable daemons +^^^^^^^^^^^^^^ + +Open :file:`/etc/frr/daemons` with your text editor of choice. Look for the +section with ``watchfrr_enable=...`` and ``zebra=...`` etc. Enable the daemons +as required by changing the value to ``yes``. + +Start FRR +^^^^^^^^^ + +.. code-block:: shell + + systemctl start frr diff --git a/doc/developer/building-libyang.rst b/doc/developer/building-libyang.rst index ed3e029908..5f82447d74 100644 --- a/doc/developer/building-libyang.rst +++ b/doc/developer/building-libyang.rst @@ -5,12 +5,16 @@ library. **Option 1: Binary Install** -The FRR project builds binary ``libyang`` packages, which we offer for download -`here `_. +The FRR project builds some binary ``libyang`` packages. + +RPM packages are at our `RPM repository `_. + +DEB packages are available as CI artifacts `here +`_. .. warning:: - ``libyang`` version 0.16.74 or newer is required to build FRR. + ``libyang`` version 1.0.184 or newer is required to build FRR. .. note:: @@ -50,8 +54,3 @@ The FRR project builds binary ``libyang`` packages, which we offer for download -D CMAKE_BUILD_TYPE:String="Release" .. make sudo make install - -When building ``libyang`` version ``0.16.x`` it's also necessary to pass the -``-DENABLE_CACHE=OFF`` parameter to ``cmake`` to work around a -`known bug `_ in libyang. - diff --git a/doc/developer/building.rst b/doc/developer/building.rst index c13fb10ffc..fbe1f24d35 100644 --- a/doc/developer/building.rst +++ b/doc/developer/building.rst @@ -7,9 +7,11 @@ Building FRR .. toctree:: :maxdepth: 2 + static-linking building-frr-for-alpine building-frr-for-centos6 building-frr-for-centos7 + building-frr-for-centos8 building-frr-for-debian8 building-frr-for-debian9 building-frr-for-fedora @@ -24,3 +26,6 @@ Building FRR building-frr-for-ubuntu1404 building-frr-for-ubuntu1604 building-frr-for-ubuntu1804 + building-frr-for-ubuntu2004 + building-frr-for-archlinux + building-docker diff --git a/doc/developer/cli.rst b/doc/developer/cli.rst index c0716a5c93..edabe61d92 100644 --- a/doc/developer/cli.rst +++ b/doc/developer/cli.rst @@ -101,7 +101,7 @@ Definition Grammar FRR uses its own grammar for defining CLI commands. The grammar draws from syntax commonly seen in \*nix manpages and should be fairly intuitive. The parser is implemented in Bison and the lexer in Flex. These may be found in -``lib/command_lex.l`` and ``lib/command_parse.y``, respectively. +``lib/command_parse.y`` and ``lib/command_lex.l``, respectively. **ProTip**: if you define a new command and find that the parser is throwing syntax or other errors, the parser is the last place you want @@ -160,27 +160,27 @@ parser, but this is merely a dumb copy job. Here is a brief summary of the various token types along with examples. -+-----------------+-----------------+-------------------------------------------------------------+ -| Token type | Syntax | Description | -+=================+=================+=============================================================+ -| ``WORD`` | ``show ip bgp`` | Matches itself. In the given example every token is a WORD. | -+-----------------+-----------------+-------------------------------------------------------------+ -| ``IPV4`` | ``A.B.C.D`` | Matches an IPv4 address. | -+-----------------+-----------------+-------------------------------------------------------------+ -| ``IPV6`` | ``X:X::X:X`` | Matches an IPv6 address. | -+-----------------+-----------------+-------------------------------------------------------------+ -| ``IPV4_PREFIX`` | ``A.B.C.D/M`` | Matches an IPv4 prefix in CIDR notation. | -+-----------------+-----------------+-------------------------------------------------------------+ -| ``IPV6_PREFIX`` | ``X:X::X:X/M`` | Matches an IPv6 prefix in CIDR notation. | -+-----------------+-----------------+-------------------------------------------------------------+ -| ``MAC`` | ``M:A:C`` | Matches a 48-bit mac address. | -+-----------------+-----------------+-------------------------------------------------------------+ -| ``MAC_PREFIX`` | ``M:A:C/M`` | Matches a 48-bit mac address with a mask. | -+-----------------+-----------------+-------------------------------------------------------------+ -| ``VARIABLE`` | ``FOOBAR`` | Matches anything. | -+-----------------+-----------------+-------------------------------------------------------------+ -| ``RANGE`` | ``(X-Y)`` | Matches numbers in the range X..Y inclusive. | -+-----------------+-----------------+-------------------------------------------------------------+ ++-----------------+-------------------+-------------------------------------------------------------+ +| Token type | Syntax | Description | ++=================+===================+=============================================================+ +| ``WORD`` | ``show ip bgp`` | Matches itself. In the given example every token is a WORD. | ++-----------------+-------------------+-------------------------------------------------------------+ +| ``IPV4`` | ``A.B.C.D`` | Matches an IPv4 address. | ++-----------------+-------------------+-------------------------------------------------------------+ +| ``IPV6`` | ``X:X::X:X`` | Matches an IPv6 address. | ++-----------------+-------------------+-------------------------------------------------------------+ +| ``IPV4_PREFIX`` | ``A.B.C.D/M`` | Matches an IPv4 prefix in CIDR notation. | ++-----------------+-------------------+-------------------------------------------------------------+ +| ``IPV6_PREFIX`` | ``X:X::X:X/M`` | Matches an IPv6 prefix in CIDR notation. | ++-----------------+-------------------+-------------------------------------------------------------+ +| ``MAC`` | ``X:X:X:X:X:X`` | Matches a 48-bit mac address. | ++-----------------+-------------------+-------------------------------------------------------------+ +| ``MAC_PREFIX`` | ``X:X:X:X:X:X/M`` | Matches a 48-bit mac address with a mask. | ++-----------------+-------------------+-------------------------------------------------------------+ +| ``VARIABLE`` | ``FOOBAR`` | Matches anything. | ++-----------------+-------------------+-------------------------------------------------------------+ +| ``RANGE`` | ``(X-Y)`` | Matches numbers in the range X..Y inclusive. | ++-----------------+-------------------+-------------------------------------------------------------+ When presented with user input, the parser will search over all defined commands in the current context to find a match. It is aware of the various @@ -371,11 +371,11 @@ Type rules +----------------------------+--------------------------------+--------------------------+ | ``A.B.C.D + X:X::X:X`` | ``const union sockunion *`` | ``NULL`` | +----------------------------+--------------------------------+--------------------------+ -| ``A.B.C.D/M`` | ``const struct prefix_ipv4 *`` | ``NULL`` | +| ``A.B.C.D/M`` | ``const struct prefix_ipv4 *`` | ``all-zeroes struct`` | +----------------------------+--------------------------------+--------------------------+ -| ``X:X::X:X/M`` | ``const struct prefix_ipv6 *`` | ``NULL`` | +| ``X:X::X:X/M`` | ``const struct prefix_ipv6 *`` | ``all-zeroes struct`` | +----------------------------+--------------------------------+--------------------------+ -| ``A.B.C.D/M + X:X::X:X/M`` | ``const struct prefix *`` | ``NULL`` | +| ``A.B.C.D/M + X:X::X:X/M`` | ``const struct prefix *`` | ``all-zeroes struct`` | +----------------------------+--------------------------------+--------------------------+ | ``(0-9)`` | ``long`` | ``0`` | +----------------------------+--------------------------------+--------------------------+ @@ -395,8 +395,10 @@ Note the following details: ``word`` tokens (e.g. constant words). This is useful if some parts of a command are optional. The type will be ``const char *``. - ``[no]`` will be passed as ``const char *no``. -- Pointers will be ``NULL`` when the argument is optional and the user did not - use it. +- Most pointers will be ``NULL`` when the argument is optional and the + user did not supply it. As noted in the table above, some prefix + struct type arguments are passed as pointers to all-zeroes structs, + not as ``NULL`` pointers. - If a parameter is not a pointer, but is optional and the user didn't use it, the default value will be passed. Check the ``_str`` argument if you need to determine whether the parameter was omitted. diff --git a/doc/developer/fpm.rst b/doc/developer/fpm.rst new file mode 100644 index 0000000000..9849869133 --- /dev/null +++ b/doc/developer/fpm.rst @@ -0,0 +1,103 @@ +FPM +=== + +FPM stands for Forwarding Plane Manager and it's a module for use with Zebra. + +The encapsulation header for the messages exchanged with the FPM is +defined by the file :file:`fpm/fpm.h` in the frr tree. The routes +themselves are encoded in Netlink or protobuf format, with Netlink +being the default. + +Netlink is standard format for encoding messages to talk with kernel space +in Linux and it is also the name of the socket type used by it. +The FPM netlink usage differs from Linux's in: + +- Linux netlink sockets use datagrams in a multicast fashion, FPM uses + as a stream and it is unicast. +- FPM netlink messages might have more or less information than a normal + Linux netlink socket message (example: RTM_NEWROUTE might add an extra + route attribute to signalize VxLAN encapsulation). + +Protobuf is one of a number of new serialization formats wherein the +message schema is expressed in a purpose-built language. Code for +encoding/decoding to/from the wire format is generated from the +schema. Protobuf messages can be extended easily while maintaining +backward-compatibility with older code. Protobuf has the following +advantages over Netlink: + +- Code for serialization/deserialization is generated automatically. This + reduces the likelihood of bugs, allows third-party programs to be integrated + quickly, and makes it easy to add fields. +- The message format is not tied to an OS (Linux), and can be evolved + independently. + +.. note:: + + Currently there are two FPM modules in ``zebra``: + + * ``fpm`` + * ``dplane_fpm_nl`` + +fpm +^^^ + +The first FPM implementation that was built using hooks in ``zebra`` route +handling functions. It uses its own netlink/protobuf encoding functions to +translate ``zebra`` route data structures into formatted binary data. + + +dplane_fpm_nl +^^^^^^^^^^^^^ + +The newer FPM implementation that was built using ``zebra``'s data plane +framework as a plugin. It only supports netlink and it shares ``zebra``'s +netlink functions to translate route event snapshots into formatted binary +data. + + +Protocol Specification +---------------------- + +FPM (in any mode) uses a TCP connection to talk with external applications. +It operates as TCP client and uses the CLI configured address/port to connect +to the FPM server (defaults to port ``2620``). + +FPM frames all data with a header to help the external reader figure how +many bytes it has to read in order to read the full message (this helps +simulates datagrams like in the original netlink Linux kernel usage). + +Frame header: + +:: + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +---------------+---------------+-------------------------------+ + | Version | Message type | Message length | + +---------------+---------------+-------------------------------+ + | Data... | + +---------------------------------------------------------------+ + + +Version +^^^^^^^ + +Currently there is only one version, so it should be always ``1``. + + +Message Type +^^^^^^^^^^^^ + +Defines what underlining protocol we are using: netlink (``1``) or protobuf (``2``). + + +Message Length +^^^^^^^^^^^^^^ + +Amount of data in this frame in network byte order. + + +Data +^^^^ + +The netlink or protobuf message payload. diff --git a/doc/developer/frr-release-procedure.rst b/doc/developer/frr-release-procedure.rst new file mode 100644 index 0000000000..ff95aa04a9 --- /dev/null +++ b/doc/developer/frr-release-procedure.rst @@ -0,0 +1,181 @@ +.. _frr-release-procedure: + +FRR Release Procedure +===================== + +```` - version to be released, e.g. 7.3 +``origin`` - FRR upstream repository + +1. Checkout ``dev/``. + + .. code-block:: console + + git checkout dev/ + +2. Create and push a new branch called ``stable/`` based on the + ``dev/`` branch. + + .. code-block:: console + + git checkout -b stable/ + git push origin stable/:refs/heads/stable/ + +3. Update Changelog for Red Hat Packages: + + Edit :file:`redhat/frr.spec.in` and look for the ``%changelog`` section: + + - Change last (top of list) entry from ``%{version}`` to the **last** + released version number. For example, if ```` is ``7.3`` and the + last public release was ``7.2``, you would use ``7.2``, changing the file + like so:: + + * Tue Nov 7 2017 Martin Winter - %{version} + + to:: + + * Tue Nov 7 2017 Martin Winter - 7.2 + + - Add new entry to the top of the list with ``%{version}`` tag. Make sure + to watch the format, i.e. the day is always 2 characters, with the 1st + character being a space if the day is one digit. + + - Add the changelog text below this entry. + +4. Update Changelog for Debian Packages: + + Edit :file:`changelog-auto.in`: + + - Change last (top of list) entry from ``@VERSION@`` to the **last** + released version number. For example, if ```` is ``7.3`` and the + last public release was ``7.2``, you would use ``7.2``, changing the file + like so:: + + frr (@VERSION@) RELEASED; urgency=medium + + to:: + + frr (7.2) RELEASED; urgency=medium + + - Add a new entry to the top of the list with a ``@VERSION@`` tag. Make sure + to watch the format. + + - Add the changelog text below this entry. + + - Verify the changelog format using ``dpkg-parsechangelog``. In the + repository root: + + .. code-block:: console + + dpkg-parsechangelog + + You should see output like this:: + + vagrant@local ~/frr> dpkg-parsechangelog + Source: frr + Version: 7.3-dev-0 + Distribution: UNRELEASED + Urgency: medium + Maintainer: FRRouting-Dev + Timestamp: 1540478210 + Date: Thu, 25 Oct 2018 16:36:50 +0200 + Changes: + frr (7.3-dev-0) RELEASED; urgency=medium + . + * Your Changes Here + +5. Change main version number: + + - Edit :file:`configure.ac` and change version in the ``AC_INIT`` command + to ```` + +6. Commit the changes, adding the changelog to the commit message. Follow all + existing commit guidelines. + +7. Create and submit a GitHub pull request, with the ``HEAD`` set to + ``stable/`` and the base set to the upstream ``master`` branch. + Allow NetDef CI to complete its run and verify that all package builds were + successful. + +8. Create a git tag for the version: + + .. code-block:: console + + git tag -a frr- -m "FRRouting Release " + +9. Push the commit and new tag. + + .. code-block:: console + + git push origin stable/:refs/head/stable/ + git push origin frr- + +10. Kick off the Release build plan on the CI system for the correct release. + Contact Martin Winter for this step. Ensure all release packages build + successfully. + +11. Kick off the Snapcraft build plan for the release. + +12. Acquire the release RPM binary packages from Martin Winter. + +13. On GitHub, go to the _ and click + "Draft a new release". Write a release announcement. The release + announcement should follow the template in + ``release-announcement-template.md``, located next to this document. Check + for spelling errors, and optionally (but preferably) have other maintainers + proofread the announcement text. + + Attach **only** the binary RPM packages to the GitHub release using + GitHub's attachment functionality. Do not attach Debian packages. Do not + attach source tarballs - these will be generated and attached by GitHub + automatically. Do not publish the release yet. + +14. Contact the current Debian maintainer for FRR to get new Debian packages + built and published on our APT repository at https://deb.frrouting.net/. + Ensure the webpage text is updated. Verify that new packages install + successfully on a vanilla Debian installation using the instructions on the + webpage. + +15. Deploy Snapcraft release (after CI system finishes the tests for snapcraft + testplan). + +16. Update the Read The Docs instance to being publishing documentation built + off the ``stable/`` branch. Contact Quentin Young for this step. + +17. Publish the GitHub release. + +18. Clone the ``frr-www`` repository: + + .. code-block:: console + + git clone https://github.com/FRRouting/frr-www.git + +19. Add a new release announcement, using a previous announcement as template: + + .. code-block:: console + + cp -launch.md -launch.md + + Paste the GitHub release announcement text into this document, and **remove + line breaks**. In other words, this:: + + This is one continuous + sentence that should be + rendered on one line + + Needs to be changed to this:: + + This is one continuous sentence that should be rendered on one line + + This is very important otherwise the announcement will be unreadable on the + website. + + Make sure to add a link to the GitHub releases page at the top. + + Once finished, manually add a new entry into ``index.html`` to link to this + new announcement. Look at past commits to see how to do this. + +20. Deploy the updated ``frr-www`` on the frrouting.org web server and verify + that the announcement text is visible. + +21. Send an email to ``announce@lists.frrouting.org``. The text of this email + should include the text from the GitHub release. diff --git a/doc/developer/grpc.rst b/doc/developer/grpc.rst new file mode 100644 index 0000000000..8029a08b73 --- /dev/null +++ b/doc/developer/grpc.rst @@ -0,0 +1,249 @@ +.. _grpc-dev: + +*************** +Northbound gRPC +*************** + +.. _grpc-languages-bindings: + +Programming Language Bindings +============================= + +The gRPC supported programming language bindings can be found here: +https://grpc.io/docs/languages/ + +After picking a programming language that supports gRPC bindings, the +next step is to generate the FRR northbound bindings. To generate the +northbound bindings you'll need the programming language binding +generator tools and those are language specific. + +Next sections will use Ruby as an example for writing scripts to use +the northbound. + + +.. _grpc-ruby-generate: + +Generating Ruby FRR Bindings +---------------------------- + +Generating FRR northbound bindings for Ruby example: + +:: + + # Install the required gems: + # - grpc: the gem that will talk with FRR's gRPC plugin. + # - grpc-tools: the gem that provides the code generator. + gem install grpc + gem install grpc-tools + + # Create your project/scripts directory: + mkdir /tmp/frr-ruby + + # Go to FRR's grpc directory: + cd grpc + + # Generate the ruby bindings: + grpc_tools_ruby_protoc \ + --ruby_out=/tmp/frr-ruby \ + --grpc_out=/tmp/frr-ruby \ + frr-northbound.proto + + +.. _grpc-ruby-if-sample: + +Using Ruby To Get Interfaces State +---------------------------------- + +Here is a sample script to print all interfaces FRR discovered: + +:: + + require 'frr-northbound_services_pb' + + # Create the connection with FRR's gRPC: + stub = Frr::Northbound::Stub.new('localhost:50051', :this_channel_is_insecure) + + # Create a new state request to get interface state: + request = Frr::GetRequest.new + request.type = :STATE + request.path.push('/frr-interface:lib') + + # Ask FRR. + response = stub.get(request) + + # Print the response. + response.each do |result| + result.data.data.each_line do |line| + puts line + end + end + + +.. note:: + + The generated files will assume that they are in the search path (e.g. + inside gem) so you'll need to either edit it to use ``require_relative`` or + tell Ruby where to look for them. For simplicity we'll use ``-I .`` to tell + it is in the current directory. + + +The previous script will output something like this: + +:: + + $ cd /tmp/frr-ruby + # Add `-I.` so ruby finds the FRR generated file locally. + $ ruby -I. interface.rb + { + "frr-interface:lib": { + "interface": [ + { + "name": "eth0", + "vrf": "default", + "state": { + "if-index": 2, + "mtu": 1500, + "mtu6": 1500, + "speed": 1000, + "metric": 0, + "phy-address": "11:22:33:44:55:66" + }, + "frr-zebra:zebra": { + "state": { + "up-count": 0, + "down-count": 0 + } + } + }, + { + "name": "lo", + "vrf": "default", + "state": { + "if-index": 1, + "mtu": 0, + "mtu6": 65536, + "speed": 0, + "metric": 0, + "phy-address": "00:00:00:00:00:00" + }, + "frr-zebra:zebra": { + "state": { + "up-count": 0, + "down-count": 0 + } + } + } + ] + } + } + + +.. _grpc-ruby-bfd-profile-sample: + +Using Ruby To Create BFD Profiles +--------------------------------- + +In this example you'll learn how to edit configuration using JSON +and programmatic (XPath) format. + +:: + + require 'frr-northbound_services_pb' + + # Create the connection with FRR's gRPC: + stub = Frr::Northbound::Stub.new('localhost:50051', :this_channel_is_insecure) + + # Create a new candidate configuration change. + new_candidate = stub.create_candidate(Frr::CreateCandidateRequest.new) + + # Use JSON to configure. + request = Frr::LoadToCandidateRequest.new + request.candidate_id = new_candidate.candidate_id + request.type = :MERGE + request.config = Frr::DataTree.new + request.config.encoding = :JSON + request.config.data = <<-EOJ + { + "frr-bfdd:bfdd": { + "bfd": { + "profile": [ + { + "name": "test-prof", + "detection-multiplier": 4, + "required-receive-interval": 800000 + } + ] + } + } + } + EOJ + + # Load configuration to candidate. + stub.load_to_candidate(request) + + # Commit candidate. + stub.commit( + Frr::CommitRequest.new( + candidate_id: new_candidate.candidate_id, + phase: :ALL, + comment: 'create test-prof' + ) + ) + + # + # Now lets delete the previous profile and create a new one. + # + + # Create a new candidate configuration change. + new_candidate = stub.create_candidate(Frr::CreateCandidateRequest.new) + + # Edit the configuration candidate. + request = Frr::EditCandidateRequest.new + request.candidate_id = new_candidate.candidate_id + + # Delete previously created profile. + request.delete.push( + Frr::PathValue.new( + path: "/frr-bfdd:bfdd/bfd/profile[name='test-prof']", + ) + ) + + # Add new profile with two configurations. + request.update.push( + Frr::PathValue.new( + path: "/frr-bfdd:bfdd/bfd/profile[name='test-prof-2']/detection-multiplier", + value: 5.to_s + ) + ) + request.update.push( + Frr::PathValue.new( + path: "/frr-bfdd:bfdd/bfd/profile[name='test-prof-2']/desired-transmission-interval", + value: 900_000.to_s + ) + ) + + # Modify the candidate. + stub.edit_candidate(request) + + # Commit the candidate configuration. + stub.commit( + Frr::CommitRequest.new( + candidate_id: new_candidate.candidate_id, + phase: :ALL, + comment: 'replace test-prof with test-prof-2' + ) + ) + + +And here is the new FRR configuration: + +:: + + $ sudo vtysh -c 'show running-config' + ... + bfd + profile test-prof-2 + detect-multiplier 5 + transmit-interval 900 + ! + ! diff --git a/doc/developer/index.rst b/doc/developer/index.rst index 3a33d9a5ec..1f803b3772 100644 --- a/doc/developer/index.rst +++ b/doc/developer/index.rst @@ -11,6 +11,8 @@ FRRouting Developer's Guide library testing bgpd + fpm + grpc ospf zebra vtysh diff --git a/doc/developer/library.rst b/doc/developer/library.rst index 4ba0c0ebc6..3d5c6a2a15 100644 --- a/doc/developer/library.rst +++ b/doc/developer/library.rst @@ -8,10 +8,13 @@ Library Facilities (libfrr) :maxdepth: 2 memtypes + rcu lists logging + locking hooks cli modules + lua diff --git a/doc/developer/lists.rst b/doc/developer/lists.rst index 6d60420b2f..28b21533c0 100644 --- a/doc/developer/lists.rst +++ b/doc/developer/lists.rst @@ -1,3 +1,5 @@ +.. _lists: + List implementations ==================== @@ -13,15 +15,22 @@ FRR includes a set of list-like data structure implementations with abstracted common APIs. The purpose of this is easily allow swapping out one data structure for another while also making the code easier to read and write. There is one API for unsorted lists and a similar but not identical API for -sorted lists. +sorted lists - and heaps use a middle ground of both. For unsorted lists, the following implementations exist: - single-linked list with tail pointer (e.g. STAILQ in BSD) +- double-linked list + - atomic single-linked list with tail pointer +Being partially sorted, the oddball structure: + +- an 8-ary heap + + For sorted lists, these data structures are implemented: - single-linked list @@ -38,6 +47,8 @@ Except for hash tables, each of the sorted data structures has a variant with unique and non-unique list items. Hash tables always require unique items and mostly follow the "sorted" API but use the hash value as sorting key. Also, iterating while modifying does not work with hash tables. +Conversely, the heap always has non-unique items, but iterating while modifying +doesn't work either. The following sorted structures are likely to be implemented at some point @@ -61,6 +72,8 @@ Only the following pieces use dynamically allocated memory: - skiplists store up to 4 next pointers inline but will dynamically allocate memory to hold an item's 5th up to 16th next pointer (if they exist) +- the heap uses a dynamically grown and shrunk array of items + Cheat sheet ----------- @@ -70,6 +83,9 @@ Available types: DECLARE_LIST DECLARE_ATOMLIST + DECLARE_DLIST + + DECLARE_HEAP DECLARE_SORTLIST_UNIQ DECLARE_SORTLIST_NONUNIQ @@ -84,25 +100,28 @@ Available types: Functions provided: -+------------------------------------+------+------+---------+------------+ -| Function | LIST | HASH | \*_UNIQ | \*_NONUNIQ | -+====================================+======+======+=========+============+ -| _init, _fini | yes | yes | yes | yes | -+------------------------------------+------+------+---------+------------+ -| _first, _next, _next_safe | yes | yes | yes | yes | -+------------------------------------+------+------+---------+------------+ -| _add_head, _add_tail, _add_after | yes | -- | -- | -- | -+------------------------------------+------+------+---------+------------+ -| _add | -- | yes | yes | yes | -+------------------------------------+------+------+---------+------------+ -| _del, _pop | yes | yes | yes | yes | -+------------------------------------+------+------+---------+------------+ -| _find | -- | yes | yes | -- | -+------------------------------------+------+------+---------+------------+ -| _find_lt, _find_gteq | -- | -- | yes | yes | -+------------------------------------+------+------+---------+------------+ -| use with for_each() macros | yes | yes | yes | yes | -+------------------------------------+------+------+---------+------------+ ++------------------------------------+------+------+------+---------+------------+ +| Function | LIST | HEAP | HASH | \*_UNIQ | \*_NONUNIQ | ++====================================+======+======+======+=========+============+ +| _init, _fini | yes | yes | yes | yes | yes | ++------------------------------------+------+------+------+---------+------------+ +| _first, _next, _next_safe, | yes | yes | yes | yes | yes | +| _const_first, _const_next | | | | | | ++------------------------------------+------+------+------+---------+------------+ +| _add_head, _add_tail, _add_after | yes | -- | -- | -- | -- | ++------------------------------------+------+------+------+---------+------------+ +| _add | -- | yes | yes | yes | yes | ++------------------------------------+------+------+------+---------+------------+ +| _del, _pop | yes | yes | yes | yes | yes | ++------------------------------------+------+------+------+---------+------------+ +| _find, _const_find | -- | -- | yes | yes | -- | ++------------------------------------+------+------+------+---------+------------+ +| _find_lt, _find_gteq, | -- | -- | -- | yes | yes | +| _const_find_lt, _const_find_gteq | | | | | | ++------------------------------------+------+------+------+---------+------------+ +| use with frr_each() macros | yes | yes | yes | yes | yes | ++------------------------------------+------+------+------+---------+------------+ + Datastructure type setup @@ -119,6 +138,8 @@ The common setup pattern will look like this: .. code-block:: c + #include + PREDECL_XXX(Z) struct item { int otherdata; @@ -159,26 +180,26 @@ Common iteration macros The following iteration macros work across all data structures: -.. c:function:: for_each(Z, head, item) +.. c:function:: frr_each(Z, &head, item) Equivalent to: .. code-block:: c - for (item = Z_first(head); item; item = Z_next(head, item)) + for (item = Z_first(&head); item; item = Z_next(&head, item)) Note that this will fail if the list is modified while being iterated over. -.. c:function:: for_each_safe(Z, head, item) +.. c:function:: frr_each_safe(Z, &head, item) Same as the previous, but the next element is pre-loaded into a "hidden" variable (named ``Z_safe``.) Equivalent to: .. code-block:: c - for (item = Z_first(head); item; item = next) { - next = Z_next_safe(head, item); + for (item = Z_first(&head); item; item = next) { + next = Z_next_safe(&head, item); ... } @@ -189,7 +210,7 @@ The following iteration macros work across all data structures: tables is resized while iterating. This will cause items to be skipped or iterated over twice. -.. c:function:: for_each_from(Z, head, item, from) +.. c:function:: frr_each_from(Z, &head, item, from) Iterates over the list, starting at item ``from``. This variant is "safe" as in the previous macro. Equivalent to: @@ -197,7 +218,7 @@ The following iteration macros work across all data structures: .. code-block:: c for (item = from; item; item = from) { - from = Z_next_safe(head, item); + from = Z_next_safe(&head, item); ... } @@ -207,6 +228,10 @@ The following iteration macros work across all data structures: resume iteration after breaking out of the loop by keeping the ``from`` value persistent and reusing it for the next loop. +To iterate over ``const`` pointers, add ``_const`` to the name of the +datastructure (``Z`` above), e.g. ``frr_each (mylist, head, item)`` becomes +``frr_each (mylist_const, head, item)``. + Common API ---------- @@ -229,7 +254,7 @@ The following documentation assumes that a list has been defined using This function may ``assert()`` if the list is not empty. -.. c:function:: size_t Z_count(struct Z_head *) +.. c:function:: size_t Z_count(const struct Z_head *) Returns the number of items in a structure. All structures store a counter in their `Z_head` so that calling this function completes @@ -241,6 +266,7 @@ The following documentation assumes that a list has been defined using outdated by the time this function returns and can therefore only be used as an estimate. +.. c:function:: const itemtype *Z_const_first(const struct Z_head *) .. c:function:: itemtype *Z_first(struct Z_head *) Returns the first item in the structure, or ``NULL`` if the structure is @@ -269,6 +295,7 @@ The following documentation assumes that a list has been defined using affected by the "modification while iterating" problem. To remove all items from a hash table, use the loop demonstrated above. +.. c:function:: const itemtype *Z_next(const struct Z_head *, const itemtype *prev) .. c:function:: itemtype *Z_next(struct Z_head *, itemtype *prev) Return the item that follows after ``prev``, or ``NULL`` if ``prev`` is @@ -311,8 +338,8 @@ are several functions exposed to insert data: .. c:function:: DECLARE_XXX(Z, type, field) - :param listtype XXX: ``LIST`` or ``ATOMLIST`` to select a data structure - implementation. + :param listtype XXX: ``LIST``, ``DLIST`` or ``ATOMLIST`` to select a data + structure implementation. :param token Z: Gives the name prefix that is used for the functions created for this instantiation. ``DECLARE_XXX(foo, ...)`` gives ``struct foo_item``, ``foo_add_head()``, ``foo_count()``, etc. Note @@ -346,7 +373,7 @@ are several functions exposed to insert data: itemtype *prev = NULL, *item; - for_each_safe(Z, head, item) { + frr_each_safe(Z, head, item) { if (something) { Z_add_after(head, prev, item); break; @@ -402,6 +429,7 @@ sorted lists can be searched for a value. For ``_NONUNIQ`` lists, this function always returns NULL since ``item`` can always be successfully added to the list. +.. c:function:: const itemtype *Z_find(const struct Z_head *, const itemtype *ref) .. c:function:: itemtype *Z_find(struct Z_head *, const itemtype *ref) Search the list for an item that compares equal to ``ref``. If no equal @@ -423,11 +451,13 @@ sorted lists can be searched for a value. containing non-unique items, more than one item may compare as equal to the item that is searched for. +.. c:function:: const itemtype *Z_find_gteq(const struct Z_head *, const itemtype *ref) .. c:function:: itemtype *Z_find_gteq(struct Z_head *, const itemtype *ref) Search the list for an item that compares greater or equal to ``ref``. See :c:func:`Z_find()` above. +.. c:function:: const itemtype *Z_find_lt(const struct Z_head *, const itemtype *ref) .. c:function:: itemtype *Z_find_lt(struct Z_head *, const itemtype *ref) Search the list for an item that compares less than @@ -467,6 +497,7 @@ API for hash tables Items that compare as equal cannot be inserted. Refer to the notes about sorted structures in the previous section. + .. c:function:: void Z_init_size(struct Z_head *, size_t size) Same as :c:func:`Z_init()` but preset the minimum hash table to @@ -476,6 +507,81 @@ Hash tables also support :c:func:`Z_add()` and :c:func:`Z_find()` with the same semantics as noted above. :c:func:`Z_find_gteq()` and :c:func:`Z_find_lt()` are **not** provided for hash tables. +Hash table invariants +^^^^^^^^^^^^^^^^^^^^^ + +There are several ways to injure yourself using the hash table API. + +First, note that there are two functions related to computing uniqueness of +objects inserted into the hash table. There is a hash function and a comparison +function. The hash function computes the hash of the object. Our hash table +implementation uses `chaining +`_. +This means that your hash function does not have to be perfect; multiple +objects having the same computed hash will be placed into a linked list +corresponding to that key. The closer to perfect the hash function, the better +performance, as items will be more evenly distributed and the chain length will +not be long on any given lookup, minimizing the number of list operations +required to find the correct item. However, the comparison function *must* be +perfect, in the sense that any two unique items inserted into the hash table +must compare not equal. At insertion time, if you try to insert an item that +compares equal to an existing item the insertion will not happen and +``hash_get()`` will return the existing item. However, this invariant *must* be +maintained while the object is in the hash table. Suppose you insert items +``A`` and ``B`` into the hash table which both hash to the same value ``1234`` +but do not compare equal. They will be placed in a chain like so:: + + 1234 : A -> B + +Now suppose you do something like this elsewhere in the code:: + + *A = *B + +I.e. you copy all fields of ``B`` into ``A``, such that the comparison function +now says that they are equal based on their contents. At this point when you +look up ``B`` in the hash table, ``hash_get()`` will search the chain for the +first item that compares equal to ``B``, which will be ``A``. This leads to +insidious bugs. + +.. warning:: + + Never modify the values looked at by the comparison or hash functions after + inserting an item into a hash table. + +A similar situation can occur with the hash allocation function. ``hash_get()`` +accepts a function pointer that it will call to get the item that should be +inserted into the list if the provided item is not already present. There is a +builtin function, ``hash_alloc_intern``, that will simply return the item you +provided; if you always want to store the value you pass to ``hash_get`` you +should use this one. If you choose to provide a different one, that function +*must* return a new item that hashes and compares equal to the one you provided +to ``hash_get()``. If it does not the behavior of the hash table is undefined. + +.. warning:: + + Always make sure your hash allocation function returns a value that hashes + and compares equal to the item you provided to ``hash_get()``. + +Finally, if you maintain pointers to items you have inserted into a hash table, +then before deallocating them you must release them from the hash table. This +is basic memory management but worth repeating as bugs have arisen from failure +to do this. + + +API for heaps +------------- + +Heaps provide the same API as the sorted data structures, except: + +* none of the find functions (:c:func:`Z_find()`, :c:func:`Z_find_gteq()` + or :c:func:`Z_find_lt()`) are available. +* iterating over the heap yields the items in semi-random order, only the + first item is guaranteed to be in order and actually the "lowest" item + on the heap. Being a heap, only the rebalancing performed on removing the + first item (either through :c:func:`Z_pop()` or :c:func:`Z_del()`) causes + the new lowest item to bubble up to the front. +* all heap modifications are O(log n). However, cacheline efficiency and + latency is likely quite a bit better than with other data structures. Atomic lists ------------ @@ -553,7 +659,7 @@ Iteration: struct item *i; pthread_rwlock_rdlock(&itemhead_rwlock); - for_each(itemlist, &itemhead, i) { + frr_each(itemlist, &itemhead, i) { /* lock must remain held while iterating */ ... } @@ -570,7 +676,7 @@ Head removal (pop) and deallocation: pthread_rwlock_unlock(&itemhead_rwlock); /* i might still be visible for another thread doing an - * for_each() (but won't be returned by another pop()) */ + * frr_each() (but won't be returned by another pop()) */ ... pthread_rwlock_wrlock(&itemhead_rwlock); @@ -579,6 +685,26 @@ Head removal (pop) and deallocation: * note nothing between wrlock() and unlock() */ XFREE(MTYPE_ITEM, i); +FAQ +--- + +What are the semantics of ``const`` in the list APIs? + ``const`` pointers to list heads and/or items are interpreted to mean that + both the list itself as well as the data items are read-only. + +Why is there no "is this item on a/the list" test? + It's slow for several of the data structures, and the work of adding it + just hasn't been done. It can certainly be added if it's needed. + +Why is it ``PREDECL`` + ``DECLARE`` instead of ``DECLARE`` + ``DEFINE``? + The rule is that a ``DEFINE`` must be in a ``.c`` file, and linked exactly + once because it defines some kind of global symbol. This is not the case + for the data structure macros; they only define ``static`` symbols and it + is perfectly fine to include both ``PREDECL`` and ``DECLARE`` in a header + file. It is also perfectly fine to have the same ``DECLARE`` statement in + 2 ``.c`` files, but only **if the macro arguments are identical.** Maybe + don't do that unless you really need it. + FRR lists --------- diff --git a/doc/developer/locking.rst b/doc/developer/locking.rst new file mode 100644 index 0000000000..d698789f9f --- /dev/null +++ b/doc/developer/locking.rst @@ -0,0 +1,75 @@ +.. _locking: + +Locking +======= + +FRR ships two small wrappers around ``pthread_mutex_lock()`` / +``pthread_mutex_unlock``. Use ``#include "frr_pthread.h"`` to get these +macros. + +.. c:function:: frr_with_mutex(pthread_mutex_t *mutex) + + Begin a C statement block that is executed with the mutex locked. Any + exit from the block (``break``, ``return``, ``goto``, end of block) will + cause the mutex to be unlocked:: + + int somefunction(int option) + { + frr_with_mutex(&my_mutex) { + /* mutex will be locked */ + + if (!option) + /* mutex will be unlocked before return */ + return -1; + + if (something(option)) + /* mutex will be unlocked before goto */ + goto out_err; + + somethingelse(); + + /* mutex will be unlocked at end of block */ + } + + return 0; + + out_err: + somecleanup(); + return -1; + } + + This is a macro that internally uses a ``for`` loop. It is explicitly + acceptable to use ``break`` to get out of the block. Even though a single + statement works correctly, FRR coding style requires that this macro always + be used with a ``{ ... }`` block. + +.. c:function:: frr_mutex_lock_autounlock(pthread_mutex_t *mutex) + + Lock mutex and unlock at the end of the current C statement block:: + + int somefunction(int option) + { + frr_mutex_lock_autounlock(&my_mutex); + /* mutex will be locked */ + + ... + if (error) + /* mutex will be unlocked before return */ + return -1; + ... + + /* mutex will be unlocked before return */ + return 0; + } + + This is a macro that internally creates a variable with a destructor. + When the variable goes out of scope (i.e. the block ends), the mutex is + released. + + .. warning:: + + This macro should only used when :c:func:`frr_with_mutex` would + result in excessively/weirdly nested code. This generally is an + indicator that the code might be trying to do too many things with + the lock held. Try any possible venues to reduce the amount of + code covered by the lock and move to :c:func:`frr_with_mutex`. diff --git a/doc/developer/logging.rst b/doc/developer/logging.rst index 1dc1885158..0430ad72a3 100644 --- a/doc/developer/logging.rst +++ b/doc/developer/logging.rst @@ -1,13 +1,126 @@ -Developer's Guide to Logging -============================ +.. _logging: + +Logging +======= One of the most frequent decisions to make while writing code for FRR is what to log, what level to log it at, and when to log it. Here is a list of recommendations for these decisions. +printfrr() +---------- + +``printfrr()`` is FRR's modified version of ``printf()``, designed to make +life easier when printing nontrivial datastructures. The following variants +are available: + +.. c:function:: ssize_t snprintfrr(char *buf, size_t len, const char *fmt, ...) +.. c:function:: ssize_t vsnprintfrr(char *buf, size_t len, const char *fmt, va_list) + + These correspond to ``snprintf``/``vsnprintf``. If you pass NULL for buf + or 0 for len, no output is written but the return value is still calculated. + + The return value is always the full length of the output, unconstrained by + `len`. It does **not** include the terminating ``\0`` character. A + malformed format string can result in a ``-1`` return value. + +.. c:function:: ssize_t csnprintfrr(char *buf, size_t len, const char *fmt, ...) +.. c:function:: ssize_t vcsnprintfrr(char *buf, size_t len, const char *fmt, va_list) + + Same as above, but the ``c`` stands for "continue" or "concatenate". The + output is appended to the string instead of overwriting it. + +.. c:function:: char *asprintfrr(struct memtype *mt, const char *fmt, ...) +.. c:function:: char *vasprintfrr(struct memtype *mt, const char *fmt, va_list) + + These functions allocate a dynamic buffer (using MTYPE `mt`) and print to + that. If the format string is malformed, they return a copy of the format + string, so the return value is always non-NULL and always dynamically + allocated with `mt`. + +.. c:function:: char *asnprintfrr(struct memtype *mt, char *buf, size_t len, const char *fmt, ...) +.. c:function:: char *vasnprintfrr(struct memtype *mt, char *buf, size_t len, const char *fmt, va_list) + + This variant tries to use the static buffer provided, but falls back to + dynamic allocation if it is insufficient. + + The return value can be either `buf` or a newly allocated string using + `mt`. You MUST free it like this:: + + char *ret = asnprintfrr(MTYPE_FOO, buf, sizeof(buf), ...); + if (ret != buf) + XFREE(MTYPE_FOO, ret); + +Extensions +^^^^^^^^^^ + +``printfrr()`` format strings can be extended with suffixes after `%p` or +`%d`. The following extended format specifiers are available: + ++-----------+--------------------------+----------------------------------------------+ +| Specifier | Argument | Output | ++===========+==========================+==============================================+ +| ``%Lu`` | ``uint64_t`` | ``12345`` | ++-----------+--------------------------+----------------------------------------------+ +| ``%Ld`` | ``int64_t`` | ``-12345`` | ++-----------+--------------------------+----------------------------------------------+ +| ``%pI4`` | ``struct in_addr *`` | ``1.2.3.4`` | +| | | | +| | ``in_addr_t *`` | | ++-----------+--------------------------+----------------------------------------------+ +| ``%pI6`` | ``struct in6_addr *`` | ``fe80::1234`` | ++-----------+--------------------------+----------------------------------------------+ +| ``%pFX`` | ``struct prefix *`` | ``fe80::1234/64`` | ++-----------+--------------------------+----------------------------------------------+ +| ``%pSG4`` | ``struct prefix_sg *`` | ``(*,1.2.3.4)`` | ++-----------+--------------------------+----------------------------------------------+ +| ``%pRN`` | ``struct route_node *`` | ``192.168.1.0/24`` (dst-only node) | +| | | | +| | | ``2001:db8::/32 from fe80::/64`` (SADR node) | ++-----------+--------------------------+----------------------------------------------+ +| ``%pNHv`` | ``struct nexthop *`` | ``1.2.3.4, via eth0`` | ++-----------+--------------------------+----------------------------------------------+ +| ``%pNHs`` | ``struct nexthop *`` | ``1.2.3.4 if 15`` | ++-----------+--------------------------+----------------------------------------------+ + +Printf features like field lengths can be used normally with these extensions, +e.g. ``%-15pI4`` works correctly. + +The extension specifier after ``%p`` or ``%d`` is always an uppercase letter; +by means of established pattern uppercase letters and numbers form the type +identifier which may be followed by lowercase flags. + +You can grep the FRR source for ``printfrr_ext_autoreg`` to see all extended +printers and what exactly they do. More printers are likely to be added as +needed/useful, so the list above may become outdated. + +``%Ld`` is not an "extension" for printfrr; it's wired directly into the main +printf logic. + +.. note:: + + The ``zlog_*``/``flog_*`` and ``vty_out`` functions all use printfrr + internally, so these extensions are available there. However, they are + **not** available when calling ``snprintf`` directly. You need to call + ``snprintfrr`` instead. + +AS-Safety +^^^^^^^^^ + +``printfrr()`` are AS-Safe under the following conditions: + +* the ``[v]as[n]printfrr`` variants are not AS-Safe (allocating memory) +* floating point specifiers are not AS-Safe (system printf is used for these) +* the positional ``%1$d`` syntax should not be used (8 arguments are supported + while AS-Safe) +* extensions are only AS-Safe if their printer is AS-Safe + +Log levels +---------- + Errors and warnings -------------------- +^^^^^^^^^^^^^^^^^^^ If it is something that the user will want to look at and maybe do something, it is either an **error** or a **warning**. @@ -53,7 +166,7 @@ Examples for errors: Informational messages ----------------------- +^^^^^^^^^^^^^^^^^^^^^^ Anything that provides introspection to the user during normal operation is an **info** message. @@ -92,7 +205,7 @@ Examples: Debug messages and asserts --------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^ Everything that is only interesting on-demand, or only while developing, is a **debug** message. It might be interesting to the user for a @@ -129,3 +242,180 @@ Examples: * some field that is absolutely needed is :code:`NULL` * any other kind of data structure corruption that will cause the daemon to crash sooner or later, one way or another + +Thread-local buffering +---------------------- + +The core logging code in :file:`lib/zlog.c` allows setting up per-thread log +message buffers in order to improve logging performance. The following rules +apply for this buffering: + +* Only messages of priority *DEBUG* or *INFO* are buffered. +* Any higher-priority message causes the thread's entire buffer to be flushed, + thus message ordering is preserved on a per-thread level. +* There is no guarantee on ordering between different threads; in most cases + this is arbitrary to begin with since the threads essentially race each + other in printing log messages. If an order is established with some + synchronization primitive, add calls to :c:func:`zlog_tls_buffer_flush()`. +* The buffers are only ever accessed by the thread they are created by. This + means no locking is necessary. + +Both the main/default thread and additional threads created by +:c:func:`frr_pthread_new()` with the default :c:func:`frr_run()` handler will +initialize thread-local buffering and call :c:func:`zlog_tls_buffer_flush()` +when idle. + +If some piece of code runs for an extended period, it may be useful to insert +calls to :c:func:`zlog_tls_buffer_flush()` in appropriate places: + +.. c:function:: void zlog_tls_buffer_flush(void) + + Write out any pending log messages that the calling thread may have in its + buffer. This function is safe to call regardless of the per-thread log + buffer being set up / in use or not. + +When working with threads that do not use the :c:type:`struct thread_master` +event loop, per-thread buffers can be managed with: + +.. c:function:: void zlog_tls_buffer_init(void) + + Set up thread-local buffering for log messages. This function may be + called repeatedly without adverse effects, but remember to call + :c:func:`zlog_tls_buffer_fini()` at thread exit. + + .. warning:: + + If this function is called, but :c:func:`zlog_tls_buffer_flush()` is + not used, log message output will lag behind since messages will only be + written out when the buffer is full. + + Exiting the thread without calling :c:func:`zlog_tls_buffer_fini()` + will cause buffered log messages to be lost. + +.. c:function:: void zlog_tls_buffer_fini(void) + + Flush pending messages and tear down thread-local log message buffering. + This function may be called repeatedly regardless of whether + :c:func:`zlog_tls_buffer_init()` was ever called. + +Log targets +----------- + +The actual logging subsystem (in :file:`lib/zlog.c`) is heavily separated +from the actual log writers. It uses an atomic linked-list (`zlog_targets`) +with RCU to maintain the log targets to be called. This list is intended to +function as "backend" only, it **is not used for configuration**. + +Logging targets provide their configuration layer on top of this and maintain +their own capability to enumerate and store their configuration. Some targets +(e.g. syslog) are inherently single instance and just stuff their config in +global variables. Others (e.g. file/fd output) are multi-instance capable. +There is another layer boundary here between these and the VTY configuration +that they use. + +Basic internals +^^^^^^^^^^^^^^^ + +.. c:type:: struct zlog_target + + This struct needs to be filled in by any log target and then passed to + :c:func:`zlog_target_replace()`. After it has been registered, + **RCU semantics apply**. Most changes to associated data should make a + copy, change that, and then replace the entire struct. + + Additional per-target data should be "appended" by embedding this struct + into a larger one, for use with `containerof()`, and + :c:func:`zlog_target_clone()` and :c:func:`zlog_target_free()` should be + used to allocate/free the entire container struct. + + Do not use this structure to maintain configuration. It should only + contain (a copy of) the data needed to perform the actual logging. For + example, the syslog target uses this: + + .. code-block:: c + + struct zlt_syslog { + struct zlog_target zt; + int syslog_facility; + }; + + static void zlog_syslog(struct zlog_target *zt, struct zlog_msg *msgs[], size_t nmsgs) + { + struct zlt_syslog *zte = container_of(zt, struct zlt_syslog, zt); + size_t i; + + for (i = 0; i < nmsgs; i++) + if (zlog_msg_prio(msgs[i]) <= zt->prio_min) + syslog(zlog_msg_prio(msgs[i]) | zte->syslog_facility, "%s", + zlog_msg_text(msgs[i], NULL)); + } + + +.. c:function:: struct zlog_target *zlog_target_clone(struct memtype *mt, struct zlog_target *oldzt, size_t size) + + Allocates a logging target struct. Note that the ``oldzt`` argument may be + ``NULL`` to allocate a "from scratch". If ``oldzt`` is not ``NULL``, the + generic bits in :c:type:`struct zlog_target` are copied. **Target specific + bits are not copied.** + +.. c:function:: struct zlog_target *zlog_target_replace(struct zlog_target *oldzt, struct zlog_target *newzt) + + Adds, replaces or deletes a logging target (either ``oldzt`` or ``newzt`` may be ``NULL``.) + + Returns ``oldzt`` for freeing. The target remains possibly in use by + other threads until the RCU cycle ends. This implies you cannot release + resources (e.g. memory, file descriptors) immediately. + + The replace operation is not atomic; for a brief period it is possible that + messages are delivered on both ``oldzt`` and ``newzt``. + + .. warning:: + + ``oldzt`` must remain **functional** until the RCU cycle ends. + +.. c:function:: void zlog_target_free(struct memtype *mt, struct zlog_target *zt) + + Counterpart to :c:func:`zlog_target_clone()`, frees a target (using RCU.) + +.. c:member:: void (*zlog_target.logfn)(struct zlog_target *zt, struct zlog_msg *msgs[], size_t nmsg) + + Called on a target to deliver "normal" logging messages. ``msgs`` is an + array of opaque structs containing the actual message. Use ``zlog_msg_*`` + functions to access message data (this is done to allow some optimizations, + e.g. lazy formatting the message text and timestamp as needed.) + + .. note:: + + ``logfn()`` must check each individual message's priority value against + the configured ``prio_min``. While the ``prio_min`` field is common to + all targets and used by the core logging code to early-drop unneeded log + messages, the array is **not** filtered for each ``logfn()`` call. + +.. c:member:: void (*zlog_target.logfn_sigsafe)(struct zlog_target *zt, const char *text, size_t len) + + Called to deliver "exception" logging messages (i.e. SEGV messages.) + Must be Async-Signal-Safe (may not allocate memory or call "complicated" + libc functions.) May be ``NULL`` if the log target cannot handle this. + +Standard targets +^^^^^^^^^^^^^^^^ + +:file:`lib/zlog_targets.c` provides the standard file / fd / syslog targets. +The syslog target is single-instance while file / fd targets can be +instantiated as needed. There are 3 built-in targets that are fully +autonomous without any config: + +- startup logging to `stderr`, until either :c:func:`zlog_startup_end()` or + :c:func:`zlog_aux_init()` is called. +- stdout logging for non-daemon programs using :c:func:`zlog_aux_init()` +- crashlogs written to :file:`/var/tmp/frr.daemon.crashlog` + +The regular CLI/command-line logging setup is handled by :file:`lib/log_vty.c` +which makes the appropriate instantiations of syslog / file / fd targets. + +.. todo:: + + :c:func:`zlog_startup_end()` should do an explicit switchover from + startup stderr logging to configured logging. Currently, configured logging + starts in parallel as soon as the respective setup is executed. This results + in some duplicate logging. diff --git a/doc/developer/lua.rst b/doc/developer/lua.rst new file mode 100644 index 0000000000..3315c31ad7 --- /dev/null +++ b/doc/developer/lua.rst @@ -0,0 +1,65 @@ +.. _lua: + +Lua +=== + +Lua is currently experimental within FRR and has very limited +support. If you would like to compile FRR with Lua you must +follow these steps: + +1. Installation of Relevant Libraries + + .. code-block:: shell + + apt-get install lua5.3 liblua5-3 liblua5.3-dev + + These are the Debian libraries that are needed. There should + be equivalent RPM's that can be found + +2. Compilation + + Configure needs these options + + .. code-block:: shell + + ./configure --enable-dev-build --enable-lua + + Typically you just include the two new enable lines to build with it. + +3. Using Lua + + * Copy tools/lua.scr into /etc/frr + + * Create a route-map match command + + .. code-block:: console + + ! + router bgp 55 + neighbor 10.50.11.116 remote-as external + address-family ipv4 unicast + neighbor 10.50.11.116 route-map TEST in + exit-address-family + ! + route-map TEST permit 10 + match command mooey + ! + + * In the lua.scr file make sure that you have a function named 'mooey' + + .. code-block:: console + + function mooey () + zlog_debug(string.format("afi: %d: %s %d ifdx: %d aspath: %s localpref: %d", + prefix.family, prefix.route, nexthop.metric, + nexthop.ifindex, nexthop.aspath, nexthop.localpref)) + + nexthop.metric = 33 + nexthop.localpref = 13 + return 3 + end + +4. General Comments + + Please be aware that this is extremely experimental and needs a ton of work + to get this up into a state that is usable. diff --git a/doc/developer/maintainer-release-build.rst b/doc/developer/maintainer-release-build.rst deleted file mode 100644 index 7792173034..0000000000 --- a/doc/developer/maintainer-release-build.rst +++ /dev/null @@ -1,89 +0,0 @@ -Release Build Procedure for FRR Maintainers -=========================================== - -1. Rename branch (if needed) - -.. code-block:: shell - - git clone git@github.com:FRRouting/frr.git - cd frr - git checkout dev/5.0 - git push origin :refs/heads/dev/5.0 - git push origin dev/5.0:refs/heads/stable/5.0 - -2. Checkout the new stable branch: - -.. code-block:: shell - - git checkout stable/5.0 - -3. Update Changelog for RedHat Package: - - Edit :file:`redhat/frr.spec.in` and look for the ``%changelog`` section: - - - Change last (top of list) entry from ``%{version}`` to previous fixed - version number, i.e.:: - - * Tue Nov 7 2017 Martin Winter - %{version} - - to:: - - * Tue Nov 7 2017 Martin Winter - 3.0.2 - - - Add new entry to the top of the list with ``%{version}`` tag and changelog - for version. - Make sure to watch the format, i.e. the day is always 2 characters, with - the 1st character being a space if the day is one digit. - -4. Update Changelog for Debian Packages: - - Edit :file:`debian/changelog-auto.in`: - - - Change last (top of list) entry from ``@VERSION@`` to previous fixed - version number, i.e.:: - - frr (@VERSION@) RELEASED; urgency=medium - - to:: - - frr (3.0.2) RELEASED; urgency=medium - - - Add a new entry to the top of the list with a ``@VERSION@`` tag and - changelog for version. - -5. Change main version number: - - - Edit :file:`configure.ac` and change version in the ``AC_INIT`` command - - Create a new entry with the version as ``%{version}`` tag - -6. Test building at least a Red Hat and Ubuntu package (or create a PR to have - the CI system test them) - -7. Commit the changes, adding the changelog to the commit message - -8. Create a git tag for the version: - - .. code-block:: shell - - git tag -a frr-5.0 -m "FRRouting Release 5.0" - -9. Push the commit and tag(s) and watch for errors on CI: - - .. code-block:: shell - - git push - git push --tags - -10. Kick off the Release build plan on the CI system for the correct release - -11. Send a Release Announcement with changes to - ``announce@lists.frrouting.org`` - -12. Kick off the Snapcraft build plan for the correct release - -13. After CI plans succeed, release on GitHub by going to - https://github.com/FRRouting/frr/releases and selecting "Draft a new - release". - -14. Deploy Snapcraft release (after CI system finishes the tests for snapcraft - testplan) diff --git a/doc/developer/memtypes.rst b/doc/developer/memtypes.rst index 153131bab9..e04049001d 100644 --- a/doc/developer/memtypes.rst +++ b/doc/developer/memtypes.rst @@ -42,6 +42,22 @@ Example: Definition ---------- +.. c:type:: struct memtype + + This is the (internal) type used for MTYPE definitions. The macros below + should be used to create these, but in some cases it is useful to pass a + ``struct memtype *`` pointer to some helper function. + + The ``MTYPE_name`` created by the macros is declared as a pointer, i.e. + a function taking a ``struct memtype *`` argument can be called with an + ``MTYPE_name`` argument (as opposed to ``&MTYPE_name``.) + + .. note:: + + As ``MTYPE_name`` is a variable assigned from ``&_mt_name`` and not a + constant expression, it cannot be used as initializer for static + variables. In the case please fall back to ``&_mt_name``. + .. c:macro:: DECLARE_MGROUP(name) This macro forward-declares a memory group and should be placed in a diff --git a/doc/developer/modules.rst b/doc/developer/modules.rst index 763d8b1b8d..02330ddfe4 100644 --- a/doc/developer/modules.rst +++ b/doc/developer/modules.rst @@ -1,3 +1,5 @@ +.. _modules: + Modules ======= diff --git a/doc/developer/next-hop-tracking.rst b/doc/developer/next-hop-tracking.rst index a9af5e749c..99e1d65c2b 100644 --- a/doc/developer/next-hop-tracking.rst +++ b/doc/developer/next-hop-tracking.rst @@ -111,8 +111,6 @@ provides the following APIs: +============================+==================================================+ | bgp_find_or_add_nexthop() | find or add a nexthop in BGP nexthop table | +----------------------------+--------------------------------------------------+ -| bgp_find_nexthop() | find a nexthop in BGP nexthop table | -+----------------------------+--------------------------------------------------+ | bgp_parse_nexthop_update() | parse a nexthop update message coming from zebra | +----------------------------+--------------------------------------------------+ diff --git a/doc/developer/ospf-sr.rst b/doc/developer/ospf-sr.rst index d798ba78ef..efe9b1b12f 100644 --- a/doc/developer/ospf-sr.rst +++ b/doc/developer/ospf-sr.rst @@ -1,8 +1,7 @@ OSPF Segment Routing ==================== -This is an EXPERIMENTAL support of draft -`draft-ietf-ospf-segment-routing-extensions-24`. +This is an EXPERIMENTAL support of `RFC 8665`. DON'T use it for production network. Supported Features @@ -10,19 +9,20 @@ Supported Features * Automatic computation of Primary and Backup Adjacency SID with Cisco experimental remote IP address -* SRGB configuration +* SRGB & SRLB configuration * Prefix configuration for Node SID with optional NO-PHP flag (Linux kernel support both mode) * Node MSD configuration (with Linux Kernel >= 4.10 a maximum of 32 labels could be stack) * Automatic provisioning of MPLS table +* Equal Cost Multi-Path (ECMP) * Static route configuration with label stack up to 32 labels Interoperability ---------------- * Tested on various topology including point-to-point and LAN interfaces - in a mix of Free Range Routing instance and Cisco IOS-XR 6.0.x + in a mix of FRRouting instance and Cisco IOS-XR 6.0.x * Check OSPF LSA conformity with latest wireshark release 2.5.0-rc Implementation details @@ -243,16 +243,16 @@ Routing. router ospf ospf router-id 192.168.1.11 capability opaque - mpls-te on - mpls-te router-address 192.168.1.11 - router-info area 0.0.0.0 segment-routing on segment-routing global-block 10000 19999 + segment-routing local-block 5000 5999 segment-routing node-msd 8 segment-routing prefix 192.168.1.11/32 index 1100 -The first segment-routing statement enable it. The Second one set the SRGB, -third line the MSD and finally, set the Prefix SID index for a given prefix. +The first segment-routing statement enables it. The second and third one set +the SRGB and SRLB respectively, fourth line the MSD and finally, set the +Prefix SID index for a given prefix. + Note that only prefix of Loopback interface could be configured with a Prefix SID. It is possible to add `no-php-flag` at the end of the prefix command to disable Penultimate Hop Popping. This advertises to peers that they MUST NOT pop @@ -265,9 +265,6 @@ Known limitations * Only single Area is supported. ABR is not yet supported * Only SPF algorithm is supported * Extended Prefix Range is not supported -* MPLS table are not flush at startup. Thus, restarting zebra process is - mandatory to remove old MPLS entries in the data plane after a crash of - ospfd daemon * With NO Penultimate Hop Popping, it is not possible to express a Segment Path with an Adjacency SID due to the impossibility for the Linux Kernel to perform double POP instruction. diff --git a/doc/developer/packaging-redhat.rst b/doc/developer/packaging-redhat.rst index f6b9931156..22a2ab1b07 100644 --- a/doc/developer/packaging-redhat.rst +++ b/doc/developer/packaging-redhat.rst @@ -3,7 +3,7 @@ Packaging Red Hat ================= -Tested on CentOS 6, CentOS 7 and Fedora 24. +Tested on CentOS 6, CentOS 7, CentOS 8 and Fedora 24. 1. On CentOS 6, refer to :ref:`building-centos6` for details on installing sufficiently up-to-date package versions to enable building FRR. @@ -22,17 +22,39 @@ Tested on CentOS 6, CentOS 7 and Fedora 24. yum install systemd-devel + For CentOS 7 and CentOS 8, the package will be built using python3 + and requires additional python3 packages:: + + yum install python3-devel python3-sphinx + + .. note:: + + For CentOS 8 you need to install ``platform-python-devel`` package + to provide ``/usr/bin/pathfix.py``:: + + yum install platform-python-devel + + If ``yum`` is not present on your system, use ``dnf`` instead. -3. Checkout FRR:: + You should enable ``PowerTools`` repo if using CentOS 8 which + is disabled by default. + +4. Add librtr-devel. Install librtr-devel from rpm.frrouting.org + repository (See https://rpm.frrouting.org to add repository + if it's not yet added) + + yum install librtr-devel + +5. Checkout FRR:: git clone https://github.com/frrouting/frr.git frr -4. Run Bootstrap and make distribution tar.gz:: +6. Run Bootstrap and make distribution tar.gz:: cd frr ./bootstrap.sh - ./configure --with-pkg-extra-version=-MyRPMVersion SPHINXBUILD=sphinx-build2.7 + ./configure --with-pkg-extra-version=-MyRPMVersion make dist .. note:: @@ -40,7 +62,7 @@ Tested on CentOS 6, CentOS 7 and Fedora 24. The only ``configure`` option respected when building RPMs is ``--with-pkg-extra-version``. -5. Create RPM directory structure and populate with sources:: +7. Create RPM directory structure and populate with sources:: mkdir rpmbuild mkdir rpmbuild/SOURCES @@ -48,7 +70,7 @@ Tested on CentOS 6, CentOS 7 and Fedora 24. cp redhat/*.spec rpmbuild/SPECS/ cp frr*.tar.gz rpmbuild/SOURCES/ -6. Edit :file:`rpm/SPECS/frr.spec` with configuration as needed. +8. Edit :file:`rpm/SPECS/frr.spec` with configuration as needed. Look at the beginning of the file and adjust the following parameters to enable or disable features as required:: @@ -73,7 +95,7 @@ Tested on CentOS 6, CentOS 7 and Fedora 24. %{!?with_pimd: %global with_pimd 1 } %{!?with_rpki: %global with_rpki 0 } -7. Build the RPM:: +9. Build the RPM:: rpmbuild --define "_topdir `pwd`/rpmbuild" -ba rpmbuild/SPECS/frr.spec diff --git a/doc/developer/packaging.rst b/doc/developer/packaging.rst index b174a9660c..0c072e4d16 100644 --- a/doc/developer/packaging.rst +++ b/doc/developer/packaging.rst @@ -1,10 +1,10 @@ -********* -Packaging -********* +******************** +Releases & Packaging +******************** .. toctree:: :maxdepth: 2 - maintainer-release-build + frr-release-procedure packaging-debian packaging-redhat diff --git a/doc/developer/rcu.rst b/doc/developer/rcu.rst new file mode 100644 index 0000000000..c2ddf93f53 --- /dev/null +++ b/doc/developer/rcu.rst @@ -0,0 +1,269 @@ +.. highlight:: c + +RCU +=== + +Introduction +------------ + +RCU (Read-Copy-Update) is, fundamentally, a paradigm of multithreaded +operation (and not a set of APIs.) The core ideas are: + +* longer, complicated updates to structures are made only on private, + "invisible" copies. Other threads, when they access the structure, see an + older (but consistent) copy. + +* once done, the updated copy is swapped in in a single operation so that + other threads see either the old or the new data but no inconsistent state + between. + +* the old instance is only released after making sure that it is impossible + any other thread might still be reading it. + +For more information, please search for general or Linux kernel RCU +documentation; there is no way this doc can be comprehensive in explaining the +interactions: + +* https://en.wikipedia.org/wiki/Read-copy-update +* https://www.kernel.org/doc/html/latest/kernel-hacking/locking.html#avoiding-locks-read-copy-update +* https://lwn.net/Articles/262464/ +* http://www.rdrop.com/users/paulmck/RCU/rclock_OLS.2001.05.01c.pdf +* http://lse.sourceforge.net/locking/rcupdate.html + +RCU, the TL;DR +^^^^^^^^^^^^^^ + +#. data structures are always consistent for reading. That's the "R" part. +#. reading never blocks / takes a lock. +#. rcu_read_lock is not a lock in the traditional sense. Think of it as a + "reservation"; it notes what the *oldest* possible thing the thread might + be seeing is, and which thus can't be deleted yet. +#. you create some object, finish it up, and then publish it. +#. publishing is an ``atomic_*`` call with ``memory_order_release``, which + tells the compiler to make sure prior memory writes have completed before + doing the atomic op. +#. ``ATOMLIST_*`` ``add`` operations do the ``memory_order_release`` for you. +#. you can't touch the object after it is published, except with atomic ops. +#. because you can't touch it, if you want to change it you make a new copy, + work on that, and then publish the new copy. That's the "CU" part. +#. deleting the object is also an atomic op. +#. other threads that started working before you published / deleted an object + might not see the new object / still see the deleted object. +#. because other threads may still see deleted objects, the ``free()`` needs + to be delayed. That's what :c:func:`rcu_free()` is for. + + +When (not) to use RCU +^^^^^^^^^^^^^^^^^^^^^ + +RCU is designed for read-heavy workloads where objects are updated relatively +rarely, but frequently accessed. Do *not* indiscriminately replace locking by +RCU patterns. + +The "copy" part of RCU implies that, while updating, several copies of a given +object exist in parallel. Even after the updated copy is swapped in, the old +object remains queued for freeing until all other threads are guaranteed to +not be accessing it anymore, due to passing a sequence point. In addition to +the increased memory usage, there may be some bursted (due to batching) malloc +contention when the RCU cleanup thread does its thing and frees memory. + +Other useful patterns +^^^^^^^^^^^^^^^^^^^^^ + +In addition to the full "copy object, apply changes, atomically update" +approach, there are 2 "reduced" usage cases that can be done: + +* atomically updating single pieces of a particular object, e.g. some flags + or configuration piece + +* straight up read-only / immutable objects + +Both of these cases can be considered RCU "subsets". For example, when +maintaining an atomic list of items, but these items only have a single +integer value that needs to be updated, that value can be atomically updated +without copying the entire object. However, the object still needs to be +free'd through :c:func:`rcu_free()` since reading/updating and deleting might +be happening concurrently. The same applies for immutable objects; deletion +might still race with reading so they need to be free'd through RCU. + +FRR API +------- + +Before diving into detail on the provided functions, it is important to note +that the FRR RCU API covers the **cleanup part of RCU, not the read-copy-update +paradigm itself**. These parts are handled by standard C11 atomic operations, +and by extension through the atomic data structures (ATOMLIST, ATOMSORT & co.) + +The ``rcu_*`` functions only make sense in conjunction with these RCU access +patterns. If you're calling the RCU API but not using these, something is +wrong. The other way around is not necessarily true; it is possible to use +atomic ops & datastructures with other types of locking, e.g. rwlocks. + +.. c:function:: void rcu_read_lock() +.. c:function:: void rcu_read_unlock() + + These functions acquire / release the RCU read-side lock. All access to + RCU-guarded data must be inside a block guarded by these. Any number of + threads may hold the RCU read-side lock at a given point in time, including + both no threads at all and all threads. + + The functions implement a depth counter, i.e. can be nested. The nested + calls are cheap, since they only increment/decrement the counter. + Therefore, any place that uses RCU data and doesn't have a guarantee that + the caller holds RCU (e.g. ``lib/`` code) should just have its own + rcu_read_lock/rcu_read_unlock pair. + + At the "root" level (e.g. un-nested), these calls can incur the cost of one + syscall (to ``futex()``). That puts them on about the same cost as a + mutex lock/unlock. + + The ``thread_master`` code currently always holds RCU everywhere, except + while doing the actual ``poll()`` syscall. This is both an optimization as + well as an "easement" into getting RCU going. The current implementation + contract is that any ``struct thread *`` callback is called with a RCU + holding depth of 1, and that this is owned by the thread so it may (should) + drop and reacquire it when doing some longer-running work. + + .. warning:: + + The RCU read-side lock must be held **continuously** for the entire time + any piece of RCU data is used. This includes any access to RCU data + after the initial ``atomic_load``. If the RCU read-side lock is + released, any RCU-protected pointers as well as the data they refer to + become invalid, as another thread may have called :c:func:`rcu_free` on + them. + +.. c:type:: struct rcu_head +.. c:type:: struct rcu_head_close +.. c:type:: struct rcu_action + + The ``rcu_head`` structures are small (16-byte) bits that contain the + queueing machinery for the RCU sweeper/cleanup mechanisms. + + Any piece of data that is cleaned up by RCU needs to have a matching + ``rcu_head`` embedded in it. If there is more than one cleanup operation + to be done (e.g. closing a file descriptor), more than one ``rcu_head`` may + be embedded. + + .. warning:: + + It is not possible to reuse a ``rcu_head``. It is owned by the RCU code + as soon as ``rcu_*`` is called on it. + + The ``_close`` variant carries an extra ``int fd`` field to store the fd to + be closed. + + To minimize the amount of memory used for ``rcu_head``, details about the + RCU operation to be performed are moved into the ``rcu_action`` structure. + It contains e.g. the MTYPE for :c:func:`rcu_free` calls. The pointer to be + freed is stored as an offset relative to the ``rcu_head``, which means it + must be embedded as a struct field so the offset is constant. + + The ``rcu_action`` structure is an implementation detail. Using + ``rcu_free`` or ``rcu_close`` will set it up correctly without further + code needed. + + The ``rcu_head`` may be put in an union with other data if the other data + is only used during "life" of the data, since the ``rcu_head`` is used only + for the "death" of data. But note that other threads may still be reading + a piece of data while a thread is working to free it. + +.. c:function:: void rcu_free(struct memtype *mtype, struct X *ptr, field) + + Free a block of memory after RCU has ensured no other thread can be + accessing it anymore. The pointer remains valid for any other thread that + has called :c:func:`rcu_read_lock` before the ``rcu_free`` call. + + .. warning:: + + In some other RCU implementations, the pointer remains valid to the + *calling* thread if it is holding the RCU read-side lock. This is not + the case in FRR, particularly when running single-threaded. Enforcing + this rule also allows static analysis to find use-after-free issues. + + ``mtype`` is the libfrr ``MTYPE_FOO`` allocation type to pass to + :c:func:`XFREE`. + + ``field`` must be the name of a ``struct rcu_head`` member field in ``ptr``. + The offset of this field (which must be constant) is used to reduce the + memory size of ``struct rcu_head``. + + .. note:: + + ``rcu_free`` (and ``rcu_close``) calls are more efficient if they are + put close to each other. When freeing several RCU'd resources, try to + move the calls next to each other (even if the data structures do not + directly point to each other.) + + Having the calls bundled reduces the cost of adding the ``rcu_head`` to + the RCU queue; the RCU queue is an atomic data structure whose usage + will require the CPU to acquire an exclusive hold on relevant cache + lines. + +.. c:function:: void rcu_close(struct rcu_head_close *head, int fd) + + Close a file descriptor after ensuring no other thread might be using it + anymore. Same as :c:func:`rcu_free`, except it calls ``close`` instead of + ``free``. + +Internals +^^^^^^^^^ + +.. c:type:: struct rcu_thread + + Per-thread state maintained by the RCU code, set up by the following + functions. A pointer to a thread's own ``rcu_thread`` is saved in + thread-local storage. + +.. c:function:: struct rcu_thread *rcu_thread_prepare(void) +.. c:function:: void rcu_thread_unprepare(struct rcu_thread *rcu_thread) +.. c:function:: void rcu_thread_start(struct rcu_thread *rcu_thread) + + Since the RCU code needs to have a list of all active threads, these + functions are used by the ``frr_pthread`` code to set up threads. Teardown + is automatic. It should not be necessary to call these functions. + + Any thread that accesses RCU-protected data needs to be registered with + these functions. Threads that do not access RCU-protected data may call + these functions but do not need to. + + Note that passing a pointer to RCU-protected data to some library which + accesses that pointer makes the library "access RCU-protected data". In + that case, either all of the library's threads must be registered for RCU, + or the code must instead pass a (non-RCU) copy of the data to the library. + +.. c:function:: void rcu_shutdown(void) + + Stop the RCU sweeper thread and make sure all cleanup has finished. + + This function is called on daemon exit by the libfrr code to ensure pending + RCU operations are completed. This is mostly to get a clean exit without + memory leaks from queued RCU operations. It should not be necessary to + call this function as libfrr handles this. + +FRR specifics and implementation details +---------------------------------------- + +The FRR RCU infrastructure has the following characteristics: + +* it is Epoch-based with a 32-bit wrapping counter. (This is somewhat + different from other Epoch-based approaches which may be designed to only + use 3 counter values, but works out to a simple implementation.) + +* instead of tracking CPUs as the Linux kernel does, threads are tracked. This + has exactly zero semantic impact, RCU just cares about "threads of + execution", which the kernel can optimize to CPUs but we can't. But it + really boils down to the same thing. + +* there are no ``rcu_dereference`` and ``rcu_assign_pointer`` - use + ``atomic_load`` and ``atomic_store`` instead. (These didn't exist when the + Linux RCU code was created.) + +* there is no ``synchronize_rcu``; this is a design choice but may be revisited + at a later point. ``synchronize_rcu`` blocks a thread until it is guaranteed + that no other threads might still be accessing data structures that they may + have access to at the beginning of the function call. This is a blocking + design and probably not appropriate for FRR. Instead, ``rcu_call`` can be + used to have the RCU sweeper thread make a callback after the same constraint + is fulfilled in an asynchronous way. Most needs should be covered by + ``rcu_free`` and ``rcu_close``. diff --git a/doc/developer/release-announcement-template.md b/doc/developer/release-announcement-template.md new file mode 100644 index 0000000000..658b87eada --- /dev/null +++ b/doc/developer/release-announcement-template.md @@ -0,0 +1,40 @@ + + +We are pleased to announce FRR . + + + +Thank you to all contributors! + +Changelog +--------- + + + +**All daemons:** +- + + +**New daemon: ** +- Adds support for + +**daemon 1** +- + +**daemon 2** +- + +**daemon N** +- + +### Internal improvements + +- + +### Packaging changes + +- diff --git a/doc/developer/static-linking.rst b/doc/developer/static-linking.rst new file mode 100644 index 0000000000..1e45c48dc3 --- /dev/null +++ b/doc/developer/static-linking.rst @@ -0,0 +1,98 @@ +.. _static-linking: + +Static Linking +============== + +This document describes how to build FRR without hard dependencies on shared +libraries. Note that it's not possible to build FRR *completely* statically. +This document just covers how to statically link the dependencies that aren't +likely to be present on a given platform - libfrr and libyang. The resultant +binaries should still be fairly portable. For example, here is the DSO +dependency list for `bgpd` after using these steps: + +.. code-block:: shell + + $ ldd bgpd + linux-vdso.so.1 (0x00007ffe3a989000) + libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f9dc10c0000) + libcap.so.2 => /lib/x86_64-linux-gnu/libcap.so.2 (0x00007f9dc0eba000) + libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f9dc0b1c000) + libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f9dc0918000) + libcrypt.so.1 => /lib/x86_64-linux-gnu/libcrypt.so.1 (0x00007f9dc06e0000) + libjson-c.so.3 => /lib/x86_64-linux-gnu/libjson-c.so.3 (0x00007f9dc04d5000) + librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007f9dc02cd000) + libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f9dc00ae000) + libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f9dbfe96000) + libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f9dbfaa5000) + /lib64/ld-linux-x86-64.so.2 (0x00007f9dc1449000) + +Procedure +--------- +Note that these steps have only been tested with LLVM 9 / clang. + +Today, libfrr can already be statically linked by passing these configure +options:: + + --enable-static --enable-static-bin --enable-shared + +libyang is more complicated. You must build and install libyang as a static +library. To do this, follow the usual libyang build procedure as listed in the +FRR developer docs, but set the ``ENABLE_STATIC`` option in your cmake +invocation. You also need to build with PIC enabled, which today is disabled +when building libyang statically. + +The resultant cmake command is:: + + cmake -DENABLE_STATIC=ON -DENABLE_LYD_PRIV=ON \ + -DCMAKE_INSTALL_PREFIX:PATH=/usr \ + -DCMAKE_POSITION_INDEPENDENT_CODE=TRUE \ + -DCMAKE_BUILD_TYPE:String="Release" .. + +This produces a bunch of ``.a`` static archives that need to ultimately be linked +into FRR. However, not only is it 6 archives rather than the usual ``libyang.so``, +you will now also need to link FRR with ``libpcre.a``. Ubuntu's ``libpcre3-dev`` +package provides this, but it hasn't been built with PIC enabled, so it's not +usable for our purposes. So download ``libpcre`` from +`SourceForge `_, and build it +like this: + +.. code-block:: shell + + ./configure --with-pic + make + +Hopefully you get a nice, usable, PIC ``libpcre.a``. + +So now we have to link all these static libraries into FRR. Rather than modify +FRR to accomodate this, the best option is to create an archive with all of +libyang's dependencies. Then to avoid making any changes to FRR build foo, +rename this ``libyang.a`` and copy it over the usual static library location. +Ugly but it works. To do this, go into your libyang build directory, which +should have a bunch of ``.a`` files. Copy ``libpcre.a`` into this directory. +Write the following into a shell script and run it: + +.. code-block:: shell + + #!/bin/bash + ar -M <`_ +* Create a Pull Request + +.. Note:: + + BGP tests MUST use generous convergence timeouts - you must ensure + that any test involving BGP uses a convergence timeout of at least + 130 seconds. + +File Hierarchy +^^^^^^^^^^^^^^ + +Before starting to write any tests one must know the file hierarchy. The +repository hierarchy looks like this: + +.. code-block:: console + + $ cd path/to/topotests + $ find ./* + ... + ./example-topojson-test # the basic example test topology-1 + ./example-topojson-test/test_example_topojson.json # input json file, having + topology, interfaces, bgp and other configuration + ./example-topojson-test/test_example_topojson.py # test script to write and + execute testcases + ... + ./lib # shared test/topology functions + ./lib/topojson.py # library to create topology and configurations dynamically + from json file + ./lib/common_config.py # library to create protocol's common configurations ex- + static_routes, prefix_lists, route_maps etc. + ./lib/bgp.py # library to create only bgp configurations + +Defining the Topology and initial configuration in JSON file +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The first step to write a new test is to define the topology and initial +configuration. User has to define topology and initial configuration in JSON +file. Here is an example of JSON file:: + + BGP neihghborship with single phy-link, sample JSON file: + { + "ipv4base": "192.168.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": {"ipv4": "192.168.0.0", "v4mask": 30, "ipv6": "fd00::", "v6mask": 64}, + "lo_prefix": {"ipv4": "1.0.", "v4mask": 32, "ipv6": "2001:DB8:F::", "v6mask": 128}, + "routers": { + "r1": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2": {"ipv4": "auto", "ipv6": "auto"}, + "r3": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "64512", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {} + } + }, + "r3": { + "dest_link": { + "r1": {} + } + } + } + } + } + } + } + }, + "r2": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1": {"ipv4": "auto", "ipv6": "auto"}, + "r3": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "64512", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + { + "redist_type": "static" + } + ], + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r3": { + "dest_link": { + "r2": {} + } + } + } + } + } + } + } + } + ... + + +BGP neighboship with loopback interface, sample JSON file:: + + { + "ipv4base": "192.168.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": {"ipv4": "192.168.0.0", "v4mask": 30, "ipv6": "fd00::", "v6mask": 64}, + "lo_prefix": {"ipv4": "1.0.", "v4mask": 32, "ipv6": "2001:DB8:F::", "v6mask": 128}, + "routers": { + "r1": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback", + "add_static_route":"yes"}, + "r2": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "64512", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "lo": { + "source_link": "lo" + } + } + } + } + } + } + } + }, + "static_routes": [ + { + "network": "1.0.2.17/32", + "next_hop": "192.168.0.1 + } + ] + }, + "r2": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback", + "add_static_route":"yes"}, + "r1": {"ipv4": "auto", "ipv6": "auto"}, + "r3": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "64512", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + { + "redist_type": "static" + } + ], + "neighbor": { + "r1": { + "dest_link": { + "lo": { + "source_link": "lo" + } + } + }, + "r3": { + "dest_link": { + "lo": { + "source_link": "lo" + } + } + } + } + } + } + } + }, + "static_routes": [ + { + "network": "192.0.20.1/32", + "no_of_ip": 9, + "admin_distance": 100, + "next_hop": "192.168.0.1", + "tag": 4001 + } + ], + } + ... + +BGP neighborship with Multiple phy-links, sample JSON file:: + + { + "ipv4base": "192.168.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": {"ipv4": "192.168.0.0", "v4mask": 30, "ipv6": "fd00::", "v6mask": 64}, + "lo_prefix": {"ipv4": "1.0.", "v4mask": 32, "ipv6": "2001:DB8:F::", "v6mask": 128}, + "routers": { + "r1": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r2-link2": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "64512", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {} + } + } + } + } + } + } + } + }, + "r2": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r1-link2": {"ipv4": "auto", "ipv6": "auto"}, + "r3-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r3-link2": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "64512", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + { + "redist_type": "static" + } + ], + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {} + } + }, + "r3": { + "dest_link": { + "r2-link1": {} + } + } + } + } + } + } + } + } + ... + + +JSON File Explained +""""""""""""""""""" + +Mandatory keywords/options in JSON: + +* ``ipv4base`` : base ipv4 address to generate ips, ex - 192.168.0.0 +* ``ipv4mask`` : mask for ipv4 address, ex - 30 +* ``ipv6base`` : base ipv6 address to generate ips, ex - fd00: +* ``ipv6mask`` : mask for ipv6 address, ex - 64 +* ``link_ip_start`` : physical interface base ipv4 and ipv6 address +* ``lo_prefix`` : loopback interface base ipv4 and ipv6 address +* ``routers`` : user can add number of routers as per topology, router's name + can be any logical name, ex- r1 or a0. +* ``r1`` : name of the router +* ``lo`` : loopback interface dict, ipv4 and/or ipv6 addresses generated automatically +* ``type`` : type of interface, to identify loopback interface +* ``links`` : physical interfaces dict, ipv4 and/or ipv6 addresses generated + automatically +* ``r2-link1`` : it will be used when routers have multiple links. 'r2' is router + name, 'link' is any logical name, '1' is to identify link number, + router name and link must be seperated by hyphen (``-``), ex- a0-peer1 + +Optional keywords/options in JSON: + +* ``bgp`` : bgp configuration +* ``local_as`` : Local AS number +* ``unicast`` : All SAFI configuration +* ``neighbor``: All neighbor details +* ``dest_link`` : Destination link to which router will connect +* ``router_id`` : bgp router-id +* ``source_link`` : if user wants to establish bgp neighborship with loopback + interface, add ``source_link``: ``lo`` +* ``keepalivetimer`` : Keep alive timer for BGP neighbor +* ``holddowntimer`` : Hold down timer for BGP neighbor +* ``static_routes`` : create static routes for routers +* ``redistribute`` : redistribute static and/or connected routes +* ``prefix_lists`` : create Prefix-lists for routers + +Building topology and configurations +"""""""""""""""""""""""""""""""""""" + +Topology and initial configuration will be created in setup_module(). Following +is the sample code:: + + class TemplateTopo(Topo): + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + def setup_module(mod): + tgen = Topogen(TemplateTopo, mod.__name__) + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + def teardown_module(mod): + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + stop_topology(tgen) + + +* Note: Topology will be created in setup module but routers will not be + started until we load zebra.conf and bgpd.conf to routers. For all routers + dirs will be created in /tmp/topotests// + zebra.conf and bgpd.conf empty files will be created and laoded to routers. + All folder and files are deleted in teardown module.. + +Creating configuration files +"""""""""""""""""""""""""""" + +Router's configuration would be saved in config file frr_json.conf. Common +configurations are like, static routes, prefixlists and route maps etc configs, +these configs can be used by any other protocols as it is. +BGP config will be specific to BGP protocol testing. + +* JSON file is passed to API build_config_from_json(), which looks for + configuration tags in JSON file. +* If tag is found in JSON, configuration is created as per input and written + to file frr_json.conf +* Once JSON parsing is over, frr_json.conf is loaded onto respective router. + Config loading is done using 'vtysh -f '. Initial config at this point + is also saved frr_json_initial.conf. This file can be used to reset + configuration on router, during the course of execution. +* Reset of configuration is done using frr "reload.py" utility, which + calculates the difference between router's running config and user's config + and loads delta file to router. API used - reset_config_on_router() + +Writing Tests +""""""""""""" + +Test topologies should always be bootstrapped from the +example-test/test_example.py, because it contains important boilerplate code +that can't be avoided, like: + +imports: os, sys, pytest, topotest/topogen and mininet topology class + +The global variable CWD (Current Working directory): which is most likely going +to be used to reference the routers configuration file location + +Example: + + +* The topology class that inherits from Mininet Topo class; + + .. code-block:: python + + class TemplateTopo(Topo): + def build(self, *_args, **_opts): + tgen = get_topogen(self) + # topology build code + + +* pytest setup_module() and teardown_module() to start the topology: + + .. code-block:: python + + def setup_module(_m): + tgen = Topogen(TemplateTopo) + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen, CWD) + + def teardown_module(_m): + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + stop_topology(tgen, CWD) + + +* ``__main__`` initialization code (to support running the script directly) + + .. code-block:: python + + if **name** == '\ **main**\ ': + sys.exit(pytest.main(["-s"])) + diff --git a/doc/developer/topotests.rst b/doc/developer/topotests.rst index 605b9c9a0c..b32f2bbf49 100644 --- a/doc/developer/topotests.rst +++ b/doc/developer/topotests.rst @@ -22,7 +22,7 @@ Installing Mininet Infrastructure apt-get install python-pip apt-get install iproute pip install ipaddr - pip install pytest + pip install "pytest<5" pip install exabgp==3.4.17 (Newer 4.0 version of exabgp is not yet supported) useradd -d /var/run/exabgp/ -s /bin/false exabgp @@ -79,6 +79,7 @@ If you prefer to manually build FRR, then use the following suggested config: --sysconfdir=/etc/frr \ --enable-vtysh \ --enable-pimd \ + --enable-sharpd \ --enable-multipath=64 \ --enable-user=frr \ --enable-group=frr \ @@ -105,6 +106,8 @@ Execute all tests with output to console py.test -s -v --tb=no +The above command must be executed from inside the topotests directory. + All test\_\* scripts in subdirectories are detected and executed (unless disabled in ``pytest.ini`` file). @@ -119,6 +122,13 @@ Execute single test cd test_to_be_run ./test_to_be_run.py +For example, and assuming you are inside the frr directory: + +.. code:: shell + + cd tests/topotests/bgp_l3vpn_to_bgp_vrf + ./test_bgp_l3vpn_to_bgp_vrf.py + For further options, refer to pytest documentation. Test will set exit code which can be used with ``git bisect``. @@ -136,30 +146,23 @@ the following env variable can be set:: export TOPOTESTS_CHECK_STDERR=Yes -(The value doesn't matter at this time. The check is if the env variable exists -or not) There is no pass/fail on this reporting. The Output will be reported to -the console:: - - export TOPOTESTS_CHECK_MEMLEAK="/home/mydir/memleak_" - -This will enable the check and output to console and the writing of the -information to files with the given prefix (followed by testname), ie -:file:`/home/mydir/memcheck_test_bgp_multiview_topo1.txt` in case of a memory -leak. +(The value doesn't matter at this time. The check is whether the env +variable exists or not.) There is no pass/fail on this reporting; the +Output will be reported to the console. Collect Memory Leak Information ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -FRR processes have the capabilities to report remaining memory allocations upon -exit. To enable the reporting of the memory, define an environment variable +FRR processes can report unfreed memory allocations upon exit. To +enable the reporting of memory leaks, define an environment variable ``TOPOTESTS_CHECK_MEMLEAK`` with the file prefix, i.e.:: export TOPOTESTS_CHECK_MEMLEAK="/home/mydir/memleak_" -This will enable the check and output to console and the writing of the -information to files with the given prefix (followed by testname), ie -:file:`/home/mydir/memcheck_test_bgp_multiview_topo1.txt` in case of a memory -leak. +This will enable the check and output to console and the writing of +the information to files with the given prefix (followed by testname), +ie :file:`/home/mydir/memcheck_test_bgp_multiview_topo1.txt` in case +of a memory leak. Running Topotests with AddressSanitizer ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -180,26 +183,23 @@ If found, then this is added with context (calling test) to Compiling for GCC AddressSanitizer requires to use ``gcc`` as a linker as well (instead of ``ld``). Here is a suggest way to compile frr with AddressSanitizer -for ``stable/3.0`` branch: +for ``master`` branch: .. code:: shell git clone https://github.com/FRRouting/frr.git cd frr - git checkout stable/3.0 ./bootstrap.sh - export CC=gcc - export CFLAGS="-O1 -g -fsanitize=address -fno-omit-frame-pointer" - export LD=gcc - export LDFLAGS="-g -fsanitize=address -ldl" - ./configure --enable-shared=no \ + ./configure \ + --enable-address-sanitizer \ --prefix=/usr/lib/frr --sysconfdir=/etc/frr \ --localstatedir=/var/run/frr \ --sbindir=/usr/lib/frr --bindir=/usr/lib/frr \ --enable-exampledir=/usr/lib/frr/examples \ --with-moduledir=/usr/lib/frr/modules \ --enable-multipath=0 --enable-rtadv \ - --enable-tcp-zebra --enable-fpm --enable-pimd + --enable-tcp-zebra --enable-fpm --enable-pimd \ + --enable-sharpd make sudo make install # Create symlink for vtysh, so topotest finds it in /usr/lib/frr @@ -360,8 +360,15 @@ This is the recommended test writing routine: - Write a topology (Graphviz recommended) - Obtain configuration files - Write the test itself +- Format the new code using `black `_ - Create a Pull Request +.. Note:: + + BGP tests MUST use generous convergence timeouts - you must ensure + that any test involving BGP uses a convergence timeout of at least + 130 seconds. + Topotest File Hierarchy """"""""""""""""""""""" @@ -760,6 +767,8 @@ Requirements: inside folders named after the equipment. - Tests must be able to run without any interaction. To make sure your test conforms with this, run it without the :option:`-s` parameter. +- Use `black `_ code formatter before creating + a pull request. This ensures we have a unified code style. Tips: diff --git a/doc/developer/workflow.rst b/doc/developer/workflow.rst index b3b3a47cb0..f345464a35 100644 --- a/doc/developer/workflow.rst +++ b/doc/developer/workflow.rst @@ -73,14 +73,20 @@ Releases FRR employs a ``..`` versioning scheme. ``MAJOR`` - Significant new features or multiple minor features. The addition of a new - routing protocol or daemon would fall under this class. + Significant new features or multiple minor features. This should mostly + cover any kind of disruptive change that is visible or "risky" to operators. + New features or protocols do not necessarily trigger this. (This was changed + for FRR 7.x after feedback from users that the pace of major version number + increments was too high.) ``MINOR`` - Small features, e.g. options for automatic BGP shutdown. + General incremental development releases, excluding "major" changes + mentioned above. Not necessarily fully backwards compatible, as smaller + (but still visible) changes or deprecated feature removals may still happen. + However, there shouldn't be any huge "surprises" between minor releases. ``BUGFIX`` - Fixes for actual bugs and/or security issues. + Fixes for actual bugs and/or security issues. Fully compatible. We will pull a new development branch for the next release every 4 months. The current schedule is Feb/June/October 1. The decision for a ``MAJOR/MINOR`` @@ -107,6 +113,9 @@ Security fixes are backported to all releases less than or equal to at least one year old. Security fixes may also be backported to older releases depending on severity. +For detailed instructions on how to produce an FRR release, refer to +:ref:`frr-release-procedure`. + Long term support branches ( LTS ) ----------------------------------------- @@ -154,6 +163,49 @@ releases have support for this feature request. Moreover, introducing features requests may result in breaking the stability of the branch. LTS branches are first done to bring long term support for stability. +Development Branches +-------------------- + +Occassionally the community will desire the ability to work together +on a feature that is considered useful to FRR. In this case the +parties may ask the Maintainers for the creation of a development +branch in the main FRR repository. Requirements for this to happen +are: + +- A one paragraph description of the feature being implemented to + allow for the facilitation of discussion about the feature. This + might include pointers to relevant RFC's or presentations that + explain what is planned. This is intended to set a somewhat + low bar for organization. +- A branch maintainer must be named. This person is responsible for + keeping the branch up to date, and general communication about the + project with the other FRR Maintainers. Additionally this person + must already be a FRR Maintainer. +- Commits to this branch must follow the normal PR and commit process + as outlined in other areas of this document. The goal of this is + to prevent the current state where large features are submitted + and are so large they are difficult to review. + +After a development branch has completed the work together, a final +review can be made and the branch merged into master. If a development +branch is becomes un-maintained or not being actively worked on after +three months then the Maintainers can decide to remove the branch. + +Debian Branches +--------------- + +The Debian project contains "official" packages for FRR. While FRR +Maintainers may participate in creating these, it is entirely the Debian +project's decision what to ship and how to work on this. + +As a courtesy and for FRR's benefit, this packaging work is currently visible +in git branches named ``debian/*`` on the main FRR git repository. These +branches are for the exclusive use by people involved in Debian packaging work +for FRR. Direct commit access may be handed out and FRR git rules (review, +testing, etc.) do not apply. Do not push to these branches without talking +to the people noted under ``Maintainer:`` and ``Uploaders:`` in +``debian/control`` on the target branch -- even if you are a FRR Maintainer. + Changelog --------- The changelog will be the base for the release notes. A changelog entry for @@ -166,7 +218,6 @@ Submitting Patches and Enhancements FRR accepts patches from two sources: -- Email (git format-patch) - GitHub pull request Contributors are highly encouraged to use GitHub's fork-and-PR workflow. It is @@ -191,29 +242,6 @@ summary of the included patches. The description should provide additional details that will help the reviewer to understand the context of the included patches. -Patch Submission via Mailing List ---------------------------------- - -As an alternative submission method, a patch can be mailed to the -development mailing list. Patches received on the mailing list will be -picked up by Patchwork and tested against the latest development branch. - -The recommended way to send the patch (or series of NN patches) to the -list is by using ``git send-email`` as follows (assuming they are the N -most recent commit(s) in your git history):: - - git send-email -NN --annotate --to=dev@lists.frrouting.org - -If your commits do not already contain a ``Signed-off-by`` line, then -use the following command to add it (after making sure you agree to the -Developer Certificate of Origin as outlined above):: - - git send-email -NN --annotate --signoff --to=dev@lists.frrouting.org - -Submitting multi-commit patches as a GitHub pull request is **strongly -encouraged** and increases the probability of your patch getting reviewed and -merged in a timely manner. - .. _license-for-contributions: License for Contributions @@ -246,7 +274,10 @@ Pre-submission Checklist - ``make test`` - In the case of a major new feature or other significant change, document - plans for continued maintenance of the feature + plans for continued maintenance of the feature. In addition it is a + requirement that automated testing must be written that exercises + the new feature within our existing CI infrastructure. Also the + addition of automated testing to cover any pull request is encouraged. .. _signing-off: @@ -256,6 +287,11 @@ Code submitted to FRR must be signed off. We have the same requirements for using the signed-off-by process as the Linux kernel. In short, you must include a ``Signed-off-by`` tag in every patch. +An easy way to do this is to use ``git commit -s`` where ``-s`` will automatically +append a signed-off line to the end of your commit message. Also, if you commit +and forgot to add the line you can use ``git commit --amend -s`` to add the +signed-off line to the last commit. + ``Signed-off-by`` is a developer's certification that they have the right to submit the patch for inclusion into the project. It is an agreement to the :ref:`Developer's Certificate of Origin `. @@ -332,6 +368,14 @@ After Submitting Your Changes - An author must never delete or manually dismiss someone else's comments or review. (A review may be overridden by agreement in the weekly technical meeting.) + - When you have addressed someone's review comments, please click the + "re-request review" button (in the top-right corner of the PR page, next + to the reviewer's name, an icon that looks like "reload") + - The responsibility for keeping a PR moving rests with the author at + least as long as there are either negative CI results or negative review + comments. If you forget to mark a review comment as addressed (by + clicking re-request review), the reviewer may very well not notice and + won't come back to your PR. - Automatically generated comments, e.g., those generated by CI systems, may be deleted by authors and others when such comments are not the most recent results from that automated comment source. @@ -362,6 +406,19 @@ Documentation should be written in reStructuredText. Sphinx extensions may be utilized but pure ReST is preferred where possible. See :ref:`documentation`. +Use of C++ +---------- + +While C++ is not accepted for core components of FRR, extensions, modules or +other distinct components may want to use C++ and include FRR header files. +There is no requirement on contributors to work to retain C++ compatibility, +but fixes for C++ compatibility are welcome. + +This implies that the burden of work to keep C++ compatibility is placed with +the people who need it, and they may provide it at their leisure to the extent +it is useful to them. So, if only a subset of header files, or even parts of +a header file are made available to C++, this is perfectly fine. + Code Reviews ============ @@ -397,6 +454,28 @@ Guidelines for code review may originate with a reviewer or document agreement reached on Slack, the Development mailing list, or the weekly technical meeting. +- Reviewers may ask for new automated testing if they feel that the + code change is large enough/significant enough to warrant such + a requirement. + +For project members with merge permissions, the following patterns have +emerged: + +- a PR with any reviews requesting changes may not be merged. + +- a PR with any negative CI result may not be merged. + +- an open "yellow" review mark ("review requested, but not done") should be + given some time (a few days up to weeks, depending on the size of the PR), + but is not a merge blocker. + +- a "textbubble" review mark ("review comments, but not positive/negative") + should be read through but is not a merge blocker. + +- non-trivial PRs are generally given some time (again depending on the size) + for people to mark an interest in reviewing. Trivial PRs may be merged + immediately when CI is green. + Coding Practices & Style ======================== @@ -477,11 +556,60 @@ your new claim at the end of the list. * ... */ +Defensive coding requirements +----------------------------- + +In general, code submitted into FRR will be rejected if it uses unsafe +programming practices. While there is no enforced overall ruleset, the +following requirements have achieved consensus: + +- ``strcpy``, ``strcat`` and ``sprintf`` are inacceptable without exception. + Use ``strlcpy``, ``strlcat`` and ``snprintf`` instead. (Rationale: even if + you know the operation cannot overflow the buffer, a future code change may + inadvertedly introduce an overflow.) + +- buffer size arguments, particularly to ``strlcpy`` and ``snprintf``, must + use ``sizeof()`` whereever possible. Particularly, do not use a size + constant in these cases. (Rationale: changing a buffer to another size + constant may leave the write operations on a now-incorrect size limit.) + +- For stack allocated structs and arrays that should be zero initialized, + prefer initializer expressions over ``memset()`` wherever possible. This + helps prevent ``memset()`` calls being missed in branches, and eliminates the + error class of an incorrect ``size`` argument to ``memset()``. + + For example, instead of: + + .. code-block:: c + + struct foo mystruct; + ... + memset(&mystruct, 0x00, sizeof(struct foo)); + + Prefer: + + .. code-block:: c + + struct foo mystruct = {}; + +- Do not zero initialize stack allocated values that must be initialized with a + nonzero value in order to be used. This way the compiler and memory checking + tools can catch uninitialized value use that would otherwise be suppressed by + the (incorrect) zero initialization. + +Other than these specific rules, coding practices from the Linux kernel as +well as CERT or MISRA C guidelines may provide useful input on safe C code. +However, these rules are not applied as-is; some of them expressly collide +with established practice. + Code Formatting --------------- -FRR uses Linux kernel style except where noted below. Code which does not -comply with these style guidelines will not be accepted. +C Code +^^^^^^ + +For C code, FRR uses Linux kernel style except where noted below. Code which +does not comply with these style guidelines will not be accepted. The project provides multiple tools to allow you to correctly style your code as painlessly as possible, primarily built around ``clang-format``. @@ -688,6 +816,28 @@ BSD coding style applies to: - ``ldpd/`` +YANG +^^^^ + +FRR uses YANG to define data models for its northbound interface. YANG models +should follow conventions used by the IETF standard models. From a practical +standpoint, this corresponds to the output produced by the ``yanglint`` tool +included in the ``libyang`` project, which is used by FRR to parse and validate +YANG models. You should run the following command on all YANG documents you +write: + +.. code-block:: console + + yanglint -f yang + +The output of this command should be identical to the input file. The sole +exception to this is comments. ``yanglint`` does not support comments and will +strip them from its output. You may include comments in your YANG documents, +but they should be indented appropriately (use spaces). Where possible, +comments should be eschewed in favor of a suitable ``description`` statement. + +In short, a diff between your input file and the output of ``yanglint`` should +either be empty or contain only comments. Specific Exceptions ^^^^^^^^^^^^^^^^^^^ @@ -748,9 +898,32 @@ ways that can be unexpected for the original implementor. As such debugs ability to turn on/off debugs from the CLI and it is expected that the developer will use this convention to allow control of their debugs. +Custom syntax-like block macros +------------------------------- + +FRR uses some macros that behave like the ``for`` or ``if`` C keywords. These +macros follow these patterns: + +- loop-style macros are named ``frr_each_*`` (and ``frr_each``) +- single run macros are named ``frr_with_*`` +- to avoid confusion, ``frr_with_*`` macros must always use a ``{ ... }`` + block even if the block only contains one statement. The ``frr_each`` + constructs are assumed to be well-known enough to use normal ``for`` rules. +- ``break``, ``return`` and ``goto`` all work correctly. For loop-style + macros, ``continue`` works correctly too. + +Both the ``each`` and ``with`` keywords are inspired by other (more +higher-level) programming languages that provide these constructs. + +There are also some older iteration macros, e.g. ``ALL_LIST_ELEMENTS`` and +``FOREACH_AFI_SAFI``. These macros in some cases do **not** fulfill the above +pattern (e.g. ``break`` does not work in ``FOREACH_AFI_SAFI`` because it +expands to 2 nested loops.) + Static Analysis and Sanitizers ------------------------------ -Clang/LLVM comes with a variety of tools that can be used to help find bugs in FRR. +Clang/LLVM and GCC come with a variety of tools that can be used to help find +bugs in FRR. clang-analyze This is a static analyzer that scans the source code looking for patterns @@ -794,11 +967,31 @@ All of the above tools are available in the Clang/LLVM toolchain since 3.4. AddressSanitizer and ThreadSanitizer are available in recent versions of GCC, but are no longer actively maintained. MemorySanitizer is not available in GCC. +.. note:: + + The different Sanitizers are mostly incompatible with each other. Please + refer to GCC/LLVM documentation for details. + Additionally, the FRR codebase is regularly scanned with Coverity. Unfortunately Coverity does not have the ability to handle scanning pull requests, but after code is merged it will send an email notifying project members with Coverity access of newly introduced defects. +Executing non-installed dynamic binaries +---------------------------------------- + +Since FRR uses the GNU autotools build system, it inherits its shortcomings. +To execute a binary directly from the build tree under a wrapper like +`valgrind`, `gdb` or `strace`, use:: + + ./libtool --mode=execute valgrind [--valgrind-opts] zebra/zebra [--zebra-opts] + +While replacing valgrind/zebra as needed. The `libtool` script is found in +the root of the build directory after `./configure` has completed. Its purpose +is to correctly set up `LD_LIBRARY_PATH` so that libraries from the build tree +are used. (On some systems, `libtool` is also available from PATH, but this is +not always the case.) + CLI changes ----------- @@ -862,6 +1055,22 @@ Miscellaneous When in doubt, follow the guidelines in the Linux kernel style guide, or ask on the development mailing list / public Slack instance. +JSON Output +^^^^^^^^^^^ + +* All JSON keys are to be camelCased, with no spaces +* Commands which output JSON should produce ``{}`` if they have nothing to + display + +Use of const +^^^^^^^^^^^^ + +Please consider using ``const`` when possible: it's a useful hint to +callers about the limits to side-effects from your apis, and it makes +it possible to use your apis in paths that involve ``const`` +objects. If you encounter existing apis that *could* be ``const``, +consider including changes in your own pull-request. + .. _documentation: diff --git a/doc/developer/zebra.rst b/doc/developer/zebra.rst index 74a8605bf2..d51cbc9a14 100644 --- a/doc/developer/zebra.rst +++ b/doc/developer/zebra.rst @@ -9,13 +9,20 @@ Zebra Overview of the Zebra Protocol ============================== -The Zebra protocol is used by protocol daemons to communicate with the -**zebra** daemon. - -Each protocol daemon may request and send information to and from the **zebra** -daemon such as interface states, routing state, nexthop-validation, and so on. -Protocol daemons may also install routes with **zebra**. The **zebra** daemon -manages which routes are installed into the forwarding table with the kernel. +The Zebra protocol (or ``ZAPI``) is used by protocol daemons to +communicate with the **zebra** daemon. + +Each protocol daemon may request and send information to and from the +**zebra** daemon such as interface states, routing state, +nexthop-validation, and so on. Protocol daemons may also install +routes with **zebra**. The **zebra** daemon manages which routes are +installed into the forwarding table with the kernel. Some daemons use +more than one ZAPI connection. This is supported: each ZAPI session is +identified by a tuple of: ``{protocol, instance, session_id}``. LDPD +is an example: it uses a second, synchronous ZAPI session to manage +label blocks. The default value for ``session_id`` is zero; daemons +who use multiple ZAPI sessions must assign unique values to the +sessions' ids. The Zebra protocol is a streaming protocol, with a common header. Version 0 lacks a version field and is implicitly versioned. Version 1 and all subsequent @@ -169,175 +176,281 @@ Zebra Protocol Commands +------------------------------------+-------+ | ZEBRA_INTERFACE_SET_MASTER | 6 | +------------------------------------+-------+ -| ZEBRA_ROUTE_ADD | 7 | +| ZEBRA_INTERFACE_SET_PROTODOWN | 7 | ++------------------------------------+-------+ +| ZEBRA_ROUTE_ADD | 8 | ++------------------------------------+-------+ +| ZEBRA_ROUTE_DELETE | 9 | ++------------------------------------+-------+ +| ZEBRA_ROUTE_NOTIFY_OWNER | 10 | ++------------------------------------+-------+ +| ZEBRA_REDISTRIBUTE_ADD | 11 | ++------------------------------------+-------+ +| ZEBRA_REDISTRIBUTE_DELETE | 12 | ++------------------------------------+-------+ +| ZEBRA_REDISTRIBUTE_DEFAULT_ADD | 13 | ++------------------------------------+-------+ +| ZEBRA_REDISTRIBUTE_DEFAULT_DELETE | 14 | ++------------------------------------+-------+ +| ZEBRA_ROUTER_ID_ADD | 15 | ++------------------------------------+-------+ +| ZEBRA_ROUTER_ID_DELETE | 16 | ++------------------------------------+-------+ +| ZEBRA_ROUTER_ID_UPDATE | 17 | ++------------------------------------+-------+ +| ZEBRA_HELLO | 18 | ++------------------------------------+-------+ +| ZEBRA_CAPABILITIES | 19 | ++------------------------------------+-------+ +| ZEBRA_NEXTHOP_REGISTER | 20 | ++------------------------------------+-------+ +| ZEBRA_NEXTHOP_UNREGISTER | 21 | ++------------------------------------+-------+ +| ZEBRA_NEXTHOP_UPDATE | 22 | ++------------------------------------+-------+ +| ZEBRA_INTERFACE_NBR_ADDRESS_ADD | 23 | ++------------------------------------+-------+ +| ZEBRA_INTERFACE_NBR_ADDRESS_DELETE | 24 | +------------------------------------+-------+ -| ZEBRA_ROUTE_DELETE | 8 | +| ZEBRA_INTERFACE_BFD_DEST_UPDATE | 25 | +------------------------------------+-------+ -| ZEBRA_ROUTE_NOTIFY_OWNER | 9 | +| ZEBRA_IMPORT_ROUTE_REGISTER | 26 | +------------------------------------+-------+ -| ZEBRA_REDISTRIBUTE_ADD | 10 | +| ZEBRA_IMPORT_ROUTE_UNREGISTER | 27 | +------------------------------------+-------+ -| ZEBRA_REDISTRIBUTE_DELETE | 11 | +| ZEBRA_IMPORT_CHECK_UPDATE | 28 | +------------------------------------+-------+ -| ZEBRA_REDISTRIBUTE_DEFAULT_ADD | 12 | +| ZEBRA_BFD_DEST_REGISTER | 29 | +------------------------------------+-------+ -| ZEBRA_REDISTRIBUTE_DEFAULT_DELETE | 13 | +| ZEBRA_BFD_DEST_DEREGISTER | 30 | +------------------------------------+-------+ -| ZEBRA_ROUTER_ID_ADD | 14 | +| ZEBRA_BFD_DEST_UPDATE | 31 | +------------------------------------+-------+ -| ZEBRA_ROUTER_ID_DELETE | 15 | +| ZEBRA_BFD_DEST_REPLAY | 32 | +------------------------------------+-------+ -| ZEBRA_ROUTER_ID_UPDATE | 16 | +| ZEBRA_REDISTRIBUTE_ROUTE_ADD | 33 | +------------------------------------+-------+ -| ZEBRA_HELLO | 17 | +| ZEBRA_REDISTRIBUTE_ROUTE_DEL | 34 | +------------------------------------+-------+ -| ZEBRA_CAPABILITIES | 18 | +| ZEBRA_VRF_UNREGISTER | 35 | +------------------------------------+-------+ -| ZEBRA_NEXTHOP_REGISTER | 19 | +| ZEBRA_VRF_ADD | 36 | +------------------------------------+-------+ -| ZEBRA_NEXTHOP_UNREGISTER | 20 | +| ZEBRA_VRF_DELETE | 37 | +------------------------------------+-------+ -| ZEBRA_NEXTHOP_UPDATE | 21 | +| ZEBRA_VRF_LABEL | 38 | +------------------------------------+-------+ -| ZEBRA_INTERFACE_NBR_ADDRESS_ADD | 22 | +| ZEBRA_INTERFACE_VRF_UPDATE | 39 | +------------------------------------+-------+ -| ZEBRA_INTERFACE_NBR_ADDRESS_DELETE | 23 | +| ZEBRA_BFD_CLIENT_REGISTER | 40 | +------------------------------------+-------+ -| ZEBRA_INTERFACE_BFD_DEST_UPDATE | 24 | +| ZEBRA_BFD_CLIENT_DEREGISTER | 41 | +------------------------------------+-------+ -| ZEBRA_IMPORT_ROUTE_REGISTER | 25 | +| ZEBRA_INTERFACE_ENABLE_RADV | 42 | +------------------------------------+-------+ -| ZEBRA_IMPORT_ROUTE_UNREGISTER | 26 | +| ZEBRA_INTERFACE_DISABLE_RADV | 43 | +------------------------------------+-------+ -| ZEBRA_IMPORT_CHECK_UPDATE | 27 | +| ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB | 44 | +------------------------------------+-------+ -| ZEBRA_BFD_DEST_REGISTER | 28 | +| ZEBRA_INTERFACE_LINK_PARAMS | 45 | +------------------------------------+-------+ -| ZEBRA_BFD_DEST_DEREGISTER | 29 | +| ZEBRA_MPLS_LABELS_ADD | 46 | +------------------------------------+-------+ -| ZEBRA_BFD_DEST_UPDATE | 30 | +| ZEBRA_MPLS_LABELS_DELETE | 47 | +------------------------------------+-------+ -| ZEBRA_BFD_DEST_REPLAY | 31 | +| ZEBRA_MPLS_LABELS_REPLACE | 48 | +------------------------------------+-------+ -| ZEBRA_REDISTRIBUTE_ROUTE_ADD | 32 | +| ZEBRA_IPMR_ROUTE_STATS | 49 | +------------------------------------+-------+ -| ZEBRA_REDISTRIBUTE_ROUTE_DEL | 33 | +| ZEBRA_LABEL_MANAGER_CONNECT | 50 | +------------------------------------+-------+ -| ZEBRA_VRF_UNREGISTER | 34 | +| ZEBRA_LABEL_MANAGER_CONNECT_ASYNC | 51 | +------------------------------------+-------+ -| ZEBRA_VRF_ADD | 35 | +| ZEBRA_GET_LABEL_CHUNK | 52 | +------------------------------------+-------+ -| ZEBRA_VRF_DELETE | 36 | +| ZEBRA_RELEASE_LABEL_CHUNK | 53 | +------------------------------------+-------+ -| ZEBRA_VRF_LABEL | 37 | +| ZEBRA_FEC_REGISTER | 54 | +------------------------------------+-------+ -| ZEBRA_INTERFACE_VRF_UPDATE | 38 | +| ZEBRA_FEC_UNREGISTER | 55 | +------------------------------------+-------+ -| ZEBRA_BFD_CLIENT_REGISTER | 39 | +| ZEBRA_FEC_UPDATE | 56 | +------------------------------------+-------+ -| ZEBRA_BFD_CLIENT_DEREGISTER | 40 | +| ZEBRA_ADVERTISE_DEFAULT_GW | 57 | +------------------------------------+-------+ -| ZEBRA_INTERFACE_ENABLE_RADV | 41 | +| ZEBRA_ADVERTISE_SVI_MACIP | 58 | +------------------------------------+-------+ -| ZEBRA_INTERFACE_DISABLE_RADV | 42 | +| ZEBRA_ADVERTISE_SUBNET | 59 | +------------------------------------+-------+ -| ZEBRA_IPV3_NEXTHOP_LOOKUP_MRIB | 43 | +| ZEBRA_ADVERTISE_ALL_VNI | 60 | +------------------------------------+-------+ -| ZEBRA_INTERFACE_LINK_PARAMS | 44 | +| ZEBRA_LOCAL_ES_ADD | 61 | +------------------------------------+-------+ -| ZEBRA_MPLS_LABELS_ADD | 45 | +| ZEBRA_LOCAL_ES_DEL | 62 | +------------------------------------+-------+ -| ZEBRA_MPLS_LABELS_DELETE | 46 | +| ZEBRA_VNI_ADD | 63 | +------------------------------------+-------+ -| ZEBRA_IPMR_ROUTE_STATS | 47 | +| ZEBRA_VNI_DEL | 64 | +------------------------------------+-------+ -| ZEBRA_LABEL_MANAGER_CONNECT | 48 | +| ZEBRA_L3VNI_ADD | 65 | +------------------------------------+-------+ -| ZEBRA_LABEL_MANAGER_CONNECT_ASYNC | 49 | +| ZEBRA_L3VNI_DEL | 66 | +------------------------------------+-------+ -| ZEBRA_GET_LABEL_CHUNK | 50 | +| ZEBRA_REMOTE_VTEP_ADD | 67 | +------------------------------------+-------+ -| ZEBRA_RELEASE_LABEL_CHUNK | 51 | +| ZEBRA_REMOTE_VTEP_DEL | 68 | +------------------------------------+-------+ -| ZEBRA_FEC_REGISTER | 52 | +| ZEBRA_MACIP_ADD | 69 | +------------------------------------+-------+ -| ZEBRA_FEC_UNREGISTER | 53 | +| ZEBRA_MACIP_DEL | 70 | +------------------------------------+-------+ -| ZEBRA_FEC_UPDATE | 54 | +| ZEBRA_IP_PREFIX_ROUTE_ADD | 71 | +------------------------------------+-------+ -| ZEBRA_ADVERTISE_DEFAULT_GW | 55 | +| ZEBRA_IP_PREFIX_ROUTE_DEL | 72 | +------------------------------------+-------+ -| ZEBRA_ADVERTISE_SUBNET | 56 | +| ZEBRA_REMOTE_MACIP_ADD | 73 | +------------------------------------+-------+ -| ZEBRA_ADVERTISE_ALL_VNI | 57 | +| ZEBRA_REMOTE_MACIP_DEL | 74 | +------------------------------------+-------+ -| ZEBRA_LOCAL_ES_ADD | 58 | +| ZEBRA_DUPLICATE_ADDR_DETECTION | 75 | +------------------------------------+-------+ -| ZEBRA_LOCAL_ES_DEL | 59 | +| ZEBRA_PW_ADD | 76 | +------------------------------------+-------+ -| ZEBRA_VNI_ADD | 60 | +| ZEBRA_PW_DELETE | 77 | +------------------------------------+-------+ -| ZEBRA_VNI_DEL | 61 | +| ZEBRA_PW_SET | 78 | +------------------------------------+-------+ -| ZEBRA_L3VNI_ADD | 62 | +| ZEBRA_PW_UNSET | 79 | +------------------------------------+-------+ -| ZEBRA_L3VNI_DEL | 63 | +| ZEBRA_PW_STATUS_UPDATE | 80 | +------------------------------------+-------+ -| ZEBRA_REMOTE_VTEP_ADD | 64 | +| ZEBRA_RULE_ADD | 81 | +------------------------------------+-------+ -| ZEBRA_REMOTE_VTEP_DEL | 65 | +| ZEBRA_RULE_DELETE | 82 | +------------------------------------+-------+ -| ZEBRA_MACIP_ADD | 66 | +| ZEBRA_RULE_NOTIFY_OWNER | 83 | +------------------------------------+-------+ -| ZEBRA_MACIP_DEL | 67 | +| ZEBRA_TABLE_MANAGER_CONNECT | 84 | +------------------------------------+-------+ -| ZEBRA_IP_PREFIX_ROUTE_ADD | 68 | +| ZEBRA_GET_TABLE_CHUNK | 85 | +------------------------------------+-------+ -| ZEBRA_IP_PREFIX_ROUTE_DEL | 69 | +| ZEBRA_RELEASE_TABLE_CHUNK | 86 | +------------------------------------+-------+ -| ZEBRA_REMOTE_MACIP_ADD | 70 | +| ZEBRA_IPSET_CREATE | 87 | +------------------------------------+-------+ -| ZEBRA_REMOTE_MACIP_DEL | 71 | +| ZEBRA_IPSET_DESTROY | 88 | +------------------------------------+-------+ -| ZEBRA_PW_ADD | 72 | +| ZEBRA_IPSET_ENTRY_ADD | 89 | +------------------------------------+-------+ -| ZEBRA_PW_DELETE | 73 | +| ZEBRA_IPSET_ENTRY_DELETE | 90 | +------------------------------------+-------+ -| ZEBRA_PW_SET | 74 | +| ZEBRA_IPSET_NOTIFY_OWNER | 91 | +------------------------------------+-------+ -| ZEBRA_PW_UNSET | 75 | +| ZEBRA_IPSET_ENTRY_NOTIFY_OWNER | 92 | +------------------------------------+-------+ -| ZEBRA_PW_STATUS_UPDATE | 76 | +| ZEBRA_IPTABLE_ADD | 93 | +------------------------------------+-------+ -| ZEBRA_RULE_ADD | 77 | +| ZEBRA_IPTABLE_DELETE | 94 | +------------------------------------+-------+ -| ZEBRA_RULE_DELETE | 78 | +| ZEBRA_IPTABLE_NOTIFY_OWNER | 95 | +------------------------------------+-------+ -| ZEBRA_RULE_NOTIFY_OWNER | 79 | +| ZEBRA_VXLAN_FLOOD_CONTROL | 96 | +------------------------------------+-------+ -| ZEBRA_TABLE_MANAGER_CONNECT | 80 | +| ZEBRA_VXLAN_SG_ADD | 97 | +------------------------------------+-------+ -| ZEBRA_GET_TABLE_CHUNK | 81 | +| ZEBRA_VXLAN_SG_DEL | 98 | +------------------------------------+-------+ -| ZEBRA_RELEASE_TABLE_CHUNK | 82 | +| ZEBRA_VXLAN_SG_REPLAY | 99 | +------------------------------------+-------+ -| ZEBRA_IPSET_CREATE | 83 | +| ZEBRA_MLAG_PROCESS_UP | 100 | +------------------------------------+-------+ -| ZEBRA_IPSET_DESTROY | 84 | +| ZEBRA_MLAG_PROCESS_DOWN | 101 | +------------------------------------+-------+ -| ZEBRA_IPSET_ENTRY_ADD | 85 | +| ZEBRA_MLAG_CLIENT_REGISTER | 102 | +------------------------------------+-------+ -| ZEBRA_IPSET_ENTRY_DELETE | 86 | +| ZEBRA_MLAG_CLIENT_UNREGISTER | 103 | +------------------------------------+-------+ -| ZEBRA_IPSET_NOTIFY_OWNER | 87 | +| ZEBRA_MLAG_FORWARD_MSG | 104 | +------------------------------------+-------+ -| ZEBRA_IPSET_ENTRY_NOTIFY_OWNER | 88 | +| ZEBRA_ERROR | 105 | +------------------------------------+-------+ -| ZEBRA_IPTABLE_ADD | 89 | +| ZEBRA_CLIENT_CAPABILITIES | 106 | +------------------------------------+-------+ -| ZEBRA_IPTABLE_DELETE | 90 | +| ZEBRA_OPAQUE_MESSAGE | 107 | +------------------------------------+-------+ -| ZEBRA_IPTABLE_NOTIFY_OWNER | 91 | +| ZEBRA_OPAQUE_REGISTER | 108 | +------------------------------------+-------+ -| ZEBRA_VXLAN_FLOOD_CONTROL | 92 | +| ZEBRA_OPAQUE_UNREGISTER | 109 | +------------------------------------+-------+ +| ZEBRA_NEIGH_DISCOVER | 110 | ++------------------------------------+-------+ + +Dataplane batching +================== + +Dataplane batching is an optimization feature that reduces the processing +time involved in the user space to kernel space transition for every message we +want to send. + +Design +----------- + +With our dataplane abstraction, we create a queue of dataplane context objects +for the messages we want to send to the kernel. In a separate pthread, we +loop over this queue and send the context objects to the appropriate +dataplane. A batching enhancement tightly integrates with the dataplane +context objects so they are able to be batch sent to dataplanes that support +it. + +There is one main change in the dataplane code. It does not call +kernel-dependent functions one-by-one, but instead it hands a list of work down +to the kernel level for processing. + +Netlink +^^^^^^^ + +At the moment, this is the only dataplane that allows for batch sending +messages to it. + +When messages must be sent to the kernel, they are consecutively added +to the batch represented by the `struct nl_batch`. Context objects are firstly +encoded to their binary representation. All the encoding functions use the same +interface: take a context object, a buffer and a size of the buffer as an +argument. It is important that they should handle a situation in which a message +wouldn't fit in the buffer and return a proper error. To achieve a zero-copy +(in the user space only) messages are encoded to the same buffer which will +be passed to the kernel. Hence, we can theoretically hit the boundary of the +buffer. + +Messages stored in the batch are sent if one of the conditions occurs: + +- When an encoding function returns the buffer overflow error. The context + object that caused this error is re-added to the new, empty batch. + +- When the size of the batch hits certain limit. + +- When the namespace of a currently being processed context object is + different from all the previous ones. They have to be sent through + distinct sockets, so the messages cannot share the same buffer. + +- After the last message from the list is processed. + +As mentioned earlier, there is a special threshold which is smaller than +the size of the underlying buffer. It prevents the overflow error and thus +eliminates the case, in which a message is encoded twice. + +The buffer used in the batching is global, since allocating that big amount of +memory every time wouldn't be most effective. However, its size can be changed +dynamically, using hidden vtysh command: +``zebra kernel netlink batch-tx-buf (1-1048576) (1-1048576)``. This feature is +only used in tests and shouldn't be utilized in any other place. + +For every failed message in the batch, the kernel responds with an error +message. Error messages are kept in the same order as they were sent, so parsing the +response is straightforward. We use the two pointer technique to match +requests with responses and then set appropriate status of dataplane context +objects. There is also a global receive buffer and it is assumed that whatever +the kernel sends it will fit in this buffer. The payload of netlink error messages +consists of a error code and the original netlink message of the request, so +the batch response won't be bigger than the batch request increased by +some space for the headers. diff --git a/doc/extra/spelling_wordlist.txt b/doc/extra/spelling_wordlist.txt index 2944592962..271f5e49f1 100644 --- a/doc/extra/spelling_wordlist.txt +++ b/doc/extra/spelling_wordlist.txt @@ -80,6 +80,9 @@ IP iptables ipv IPv +IPvX +IPv4 +IPv6 isis isisd lan @@ -99,6 +102,8 @@ LSAs Masaki Mbit Mbits +macvlan +macvlans mib motd mpls @@ -227,6 +232,7 @@ VN VNC vrf vrfs +vrrp vty Vty vtysh diff --git a/doc/figures/fig_dmvpn_topologies.png b/doc/figures/fig_dmvpn_topologies.png new file mode 100644 index 0000000000..a0dcc3e67d Binary files /dev/null and b/doc/figures/fig_dmvpn_topologies.png differ diff --git a/doc/figures/nodes.dot b/doc/figures/nodes.dot index d0e234958f..4ce147b2c4 100644 --- a/doc/figures/nodes.dot +++ b/doc/figures/nodes.dot @@ -47,7 +47,7 @@ digraph climodes { CONFIG_NODE -> OSPF_NODE [ label="router ospf [(1-65535)] [vrf NAME]" ]; CONFIG_NODE -> OSPF6_NODE [ label="router ospf6" ]; CONFIG_NODE -> LDP_NODE [ label="mpls ldp" ]; - CONFIG_NODE -> ISIS_NODE [ label="router isis WORD" ]; + CONFIG_NODE -> ISIS_NODE [ label="router isis WORD [vrf NAME]" ]; CONFIG_NODE -> RMAP_NODE [ label="route-map WORD (1-65535)" ]; CONFIG_NODE -> PW_NODE [ label="pseudowire IFNAME" ]; CONFIG_NODE -> VTY_NODE [ label="line vty" ]; @@ -55,7 +55,6 @@ digraph climodes { CONFIG_NODE -> KEYCHAIN_KEY_NODE [ label="key (0-2147483647)" ]; KEYCHAIN_NODE -> KEYCHAIN_KEY_NODE [ label="key (0-2147483647)" ]; KEYCHAIN_KEY_NODE -> KEYCHAIN_NODE [ label="no key (0-2147483647)" ]; - CONFIG_NODE -> LOGICALROUTER_NODE [ label="logical-router (1-65535) ns NAME" ]; CONFIG_NODE -> VRF_NODE [ label="vrf NAME" ]; CONFIG_NODE -> INTERFACE_NODE [ label="interface IFNAME vrf NAME" ]; INTERFACE_NODE -> LINK_PARAMS_NODE [ label="link-params" ]; diff --git a/doc/manpages/common-options.rst b/doc/manpages/common-options.rst index a5977a6ebb..a47a233c08 100644 --- a/doc/manpages/common-options.rst +++ b/doc/manpages/common-options.rst @@ -126,6 +126,7 @@ These following options control the daemon's VTY (interactive command line) inte staticd 2616 bfdd 2617 fabricd 2618 + vrrpd 2619 Port 2607 is used for ospfd's Opaque LSA API. diff --git a/doc/manpages/conf.py b/doc/manpages/conf.py index 46240de1c0..8b9bb021a3 100644 --- a/doc/manpages/conf.py +++ b/doc/manpages/conf.py @@ -192,7 +192,7 @@ # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +html_static_path = [] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied @@ -313,27 +313,28 @@ fwfrr = "{0} routing engine for use with FRRouting." man_pages = [ - ('bgpd', 'bgpd', fwfrr.format("a BGPv4, BGPv4+, BGPv4- "), [], 8), - ('eigrpd', 'eigrpd', fwfrr.format("an EIGRP "), [], 8), - ('ospf6d', 'ospf6d', fwfrr.format("an OSPFv3 "), [], 8), - ('ospfd', 'ospfd', fwfrr.format("an OSPFv2 "), [], 8), - ('isisd', 'isisd', fwfrr.format("an IS-IS "), [], 8), - ('ospfclient', 'ospfclient', 'an example ospf-api client', [], 8), - ('ldpd', 'ldpd', fwfrr.format("an LDP "), [], 8), - ('nhrpd', 'nhrpd', fwfrr.format("a Next Hop Routing Protocol "), [], 8), - ('pimd', 'pimd', fwfrr.format("a PIM "), [], 8), - ('pbrd', 'pbrd', fwfrr.format("a PBR "), [], 8), - ('sharpd', 'sharpd', fwfrr.format("a SHARP "), [], 8), - ('staticd', 'staticd', fwfrr.format("a static route manager "), [], 8), - ('mtracebis', 'mtracebis', "a multicast trace client", [], 8), - ('ripd', 'ripd', fwfrr.format("a RIP "), [], 8), - ('ripngd', 'ripngd', fwfrr.format("a RIPNG "), [], 8), - ('zebra', 'zebra', 'a routing manager for use with associated FRRouting components.', [], 8), - ('watchfrr', 'watchfrr', 'a program to monitor the status of FRRouting daemons', [], 8), - ('vtysh', 'vtysh', 'an integrated shell for FRRouting.', [], 1), + ('frr-bfdd', 'frr-bfdd', fwfrr.format("a bfd"), [], 8), + ('frr-bgpd', 'frr-bgpd', fwfrr.format("a BGPv4, BGPv4+, BGPv4-"), [], 8), + ('frr-eigrpd', 'frr-eigrpd', fwfrr.format("an EIGRP"), [], 8), + ('frr-fabricd', 'frr-fabricd', fwfrr.format("an OpenFabric"), [], 8), + ('frr-isisd', 'frr-isisd', fwfrr.format("an IS-IS"), [], 8), + ('frr-ldpd', 'frr-ldpd', fwfrr.format("an LDP"), [], 8), + ('frr-nhrpd', 'frr-nhrpd', fwfrr.format("a Next Hop Routing Protocol"), [], 8), + ('frr-ospf6d', 'frr-ospf6d', fwfrr.format("an OSPFv3"), [], 8), + ('frr-ospfclient', 'frr-ospfclient', 'an example ospf-api client', [], 8), + ('frr-ospfd', 'frr-ospfd', fwfrr.format("an OSPFv2"), [], 8), + ('frr-pbrd', 'frr-pbrd', fwfrr.format("a PBR"), [], 8), + ('frr-pimd', 'frr-pimd', fwfrr.format("a PIM"), [], 8), + ('frr-ripd', 'frr-ripd', fwfrr.format("a RIP"), [], 8), + ('frr-ripngd', 'frr-ripngd', fwfrr.format("a RIPNG"), [], 8), + ('frr-sharpd', 'frr-sharpd', fwfrr.format("a SHARP"), [], 8), + ('frr-staticd', 'frr-staticd', fwfrr.format("a static route manager"), [], 8), + ('frr-vrrpd', 'frr-vrrpd', fwfrr.format("a VRRP"), [], 8), + ('frr-watchfrr', 'frr-watchfrr', 'a program to monitor the status of FRRouting daemons', [], 8), + ('frr-zebra', 'frr-zebra', 'a routing manager for use with associated FRRouting components.', [], 8), ('frr', 'frr', 'a systemd interaction script', [], 1), - ('bfdd', 'bfdd', fwfrr.format("a bfd"), [], 8), - ('fabricd', 'fabricd', fwfrr.format("an OpenFabric "), [], 8), + ('mtracebis', 'mtracebis', "a multicast trace client", [], 8), + ('vtysh', 'vtysh', 'an integrated shell for FRRouting.', [], 1), ] # -- Options for Texinfo output ------------------------------------------- diff --git a/doc/manpages/defines.rst b/doc/manpages/defines.rst index cdf5e1967e..ac24cfa8dc 100644 --- a/doc/manpages/defines.rst +++ b/doc/manpages/defines.rst @@ -1,3 +1,3 @@ .. |synopsis-options| replace:: [-d|-t|-dt] [-C] [-f config-file] [-i pid-file] [-z zclient-path] [-u user] [-g group] [-A vty-addr] [-P vty-port] [-M module[:options]] [-N pathspace] [--vty_socket vty-path] [--moduledir module-path] .. |synopsis-options-hv| replace:: [-h] [-v] -.. |seealso-programs| replace:: zebra(8), vtysh(1), ripd(8), ripngd(8), ospfd(8), ospf6d(8), bgpd(8), isisd(8), babeld(8), nhrpd(8), pimd(8), pbrd(8), ldpd(8), eigrpd(8), staticd(8), fabricd(8), mtracebis(8) +.. |seealso-programs| replace:: frr-zebra(8), vtysh(1), frr-ripd(8), frr-ripngd(8), frr-ospfd(8), frr-ospf6d(8), frr-bgpd(8), frr-isisd(8), frr-babeld(8), frr-nhrpd(8), frr-pimd(8), frr-pbrd(8), frr-ldpd(8), frr-eigrpd(8), frr-staticd(8), frr-fabricd(8), frr-vrrpd(8), mtracebis(8) diff --git a/doc/manpages/bfdd.rst b/doc/manpages/frr-bfdd.rst similarity index 100% rename from doc/manpages/bfdd.rst rename to doc/manpages/frr-bfdd.rst diff --git a/doc/manpages/frr-bgpd.rst b/doc/manpages/frr-bgpd.rst new file mode 100644 index 0000000000..f7e20265e5 --- /dev/null +++ b/doc/manpages/frr-bgpd.rst @@ -0,0 +1,82 @@ +**** +BGPD +**** + +.. include:: defines.rst +.. |DAEMON| replace:: bgpd + +SYNOPSIS +======== +|DAEMON| |synopsis-options-hv| + +|DAEMON| |synopsis-options| + +DESCRIPTION +=========== +|DAEMON| is a routing component that works with the FRRouting routing engine. + +OPTIONS +======= +OPTIONS available for the |DAEMON| command: + +.. include:: common-options.rst + +.. option:: -p, --bgp_port + + Set the bgp protocol's port number. When port number is 0, that means do not + listen bgp port. + +.. option:: -l, --listenon + + Specify a specific IP address for bgpd to listen on, rather than its default + of ``0.0.0.0`` / ``::``. This can be useful to constrain bgpd to an internal + address, or to run multiple bgpd processes on one host. + +.. option:: -n, --no_kernel + + Do not install learned routes into the linux kernel. This option is useful + for a route-reflector environment or if you are running multiple bgp + processes in the same namespace. This option is different than the --no_zebra + option in that a ZAPI connection is made. + +.. option:: -e, --ecmp + + Run BGP with a limited ecmp capability, that is different than what BGP + was compiled with. The value specified must be greater than 0 and less + than or equal to the MULTIPATH_NUM specified on compilation. + +.. option:: -Z, --no_zebra + + Do not communicate with zebra at all. This is different than the --no_kernel + option in that we do not even open a ZAPI connection to the zebra process. + +.. option:: -s, --socket_size + + When opening tcp connections to our peers, set the socket send buffer + size that the kernel will use for the peers socket. This option + is only really useful at a very large scale. Experimentation should + be done to see if this is helping or not at the scale you are running + at. + +LABEL MANAGER +------------- + +.. option:: -I, --int_num + + Set zclient id. This is required when using Zebra label manager in proxy mode. + +FILES +===== + +|INSTALL_PREFIX_SBIN|/|DAEMON| + The default location of the |DAEMON| binary. + +|INSTALL_PREFIX_ETC|/|DAEMON|.conf + The default location of the |DAEMON| config file. + +$(PWD)/|DAEMON|.log + If the |DAEMON| process is configured to output logs to a file, then you + will find this file in the directory where you started |DAEMON|. + +.. include:: epilogue.rst + diff --git a/doc/manpages/eigrpd.rst b/doc/manpages/frr-eigrpd.rst similarity index 100% rename from doc/manpages/eigrpd.rst rename to doc/manpages/frr-eigrpd.rst diff --git a/doc/manpages/fabricd.rst b/doc/manpages/frr-fabricd.rst similarity index 100% rename from doc/manpages/fabricd.rst rename to doc/manpages/frr-fabricd.rst diff --git a/doc/manpages/isisd.rst b/doc/manpages/frr-isisd.rst similarity index 100% rename from doc/manpages/isisd.rst rename to doc/manpages/frr-isisd.rst diff --git a/doc/manpages/ldpd.rst b/doc/manpages/frr-ldpd.rst similarity index 100% rename from doc/manpages/ldpd.rst rename to doc/manpages/frr-ldpd.rst diff --git a/doc/manpages/nhrpd.rst b/doc/manpages/frr-nhrpd.rst similarity index 100% rename from doc/manpages/nhrpd.rst rename to doc/manpages/frr-nhrpd.rst diff --git a/doc/manpages/ospf6d.rst b/doc/manpages/frr-ospf6d.rst similarity index 100% rename from doc/manpages/ospf6d.rst rename to doc/manpages/frr-ospf6d.rst diff --git a/doc/manpages/ospfclient.rst b/doc/manpages/frr-ospfclient.rst similarity index 100% rename from doc/manpages/ospfclient.rst rename to doc/manpages/frr-ospfclient.rst diff --git a/doc/manpages/ospfd.rst b/doc/manpages/frr-ospfd.rst similarity index 100% rename from doc/manpages/ospfd.rst rename to doc/manpages/frr-ospfd.rst diff --git a/doc/manpages/pbrd.rst b/doc/manpages/frr-pbrd.rst similarity index 100% rename from doc/manpages/pbrd.rst rename to doc/manpages/frr-pbrd.rst diff --git a/doc/manpages/pimd.rst b/doc/manpages/frr-pimd.rst similarity index 100% rename from doc/manpages/pimd.rst rename to doc/manpages/frr-pimd.rst diff --git a/doc/manpages/ripd.rst b/doc/manpages/frr-ripd.rst similarity index 100% rename from doc/manpages/ripd.rst rename to doc/manpages/frr-ripd.rst diff --git a/doc/manpages/ripngd.rst b/doc/manpages/frr-ripngd.rst similarity index 100% rename from doc/manpages/ripngd.rst rename to doc/manpages/frr-ripngd.rst diff --git a/doc/manpages/sharpd.rst b/doc/manpages/frr-sharpd.rst similarity index 100% rename from doc/manpages/sharpd.rst rename to doc/manpages/frr-sharpd.rst diff --git a/doc/manpages/staticd.rst b/doc/manpages/frr-staticd.rst similarity index 100% rename from doc/manpages/staticd.rst rename to doc/manpages/frr-staticd.rst diff --git a/doc/manpages/bgpd.rst b/doc/manpages/frr-vrrpd.rst similarity index 78% rename from doc/manpages/bgpd.rst rename to doc/manpages/frr-vrrpd.rst index f1736ffd0b..0e73b07cda 100644 --- a/doc/manpages/bgpd.rst +++ b/doc/manpages/frr-vrrpd.rst @@ -1,9 +1,9 @@ -**** -BGPD -**** +***** +VRRPD +***** .. include:: defines.rst -.. |DAEMON| replace:: bgpd +.. |DAEMON| replace:: vrrpd SYNOPSIS ======== @@ -14,6 +14,8 @@ SYNOPSIS DESCRIPTION =========== |DAEMON| is a routing component that works with the FRRouting routing engine. +It implements the Virtual Router Redundancy Protocol. Support for both VRRPv2 +and VRRPv3 is present. OPTIONS ======= @@ -21,13 +23,6 @@ OPTIONS available for the |DAEMON| command: .. include:: common-options.rst -LABEL MANAGER -------------- - -.. option:: -I, --int_num - - Set zclient id. This is required when using Zebra label manager in proxy mode. - FILES ===== diff --git a/doc/manpages/watchfrr.rst b/doc/manpages/frr-watchfrr.rst similarity index 87% rename from doc/manpages/watchfrr.rst rename to doc/manpages/frr-watchfrr.rst index dceb423f82..d8c82eafa9 100644 --- a/doc/manpages/watchfrr.rst +++ b/doc/manpages/frr-watchfrr.rst @@ -35,6 +35,22 @@ OPTIONS Set the VTY socket directory (the default value is "/var/run/frr"). +.. option:: -N , --pathspace + + Insert the given name into paths used by the FRR daemons. This is appended + to the VTY socket directory and passed to the daemons which also add it to + their paths in /etc. + +.. option:: --netns[=] + + (Linux only.) Switch network namespaces when starting watchfrr. The name + defaults to the value passed with -N (which it should be used in conjunction + with.) If the name is not specified, the option has no effect. + + If the network namespace does not exist, it is created in a manner + compatible with iproute2. Network namespaces are not removed by FRR, this + must be done with "ip netns delete". + .. option:: -l , --loglevel Set the logging level (the default value is "6"). The value should range from 0 (LOG_EMERG) to 7 (LOG_DEBUG), but higher number can be supplied if extra debugging messages are required. diff --git a/doc/manpages/zebra.rst b/doc/manpages/frr-zebra.rst similarity index 100% rename from doc/manpages/zebra.rst rename to doc/manpages/frr-zebra.rst diff --git a/doc/manpages/index.rst b/doc/manpages/index.rst index 053555c4e4..58a1d9e0db 100644 --- a/doc/manpages/index.rst +++ b/doc/manpages/index.rst @@ -6,24 +6,25 @@ .. toctree:: :maxdepth: 2 - bfdd - bgpd - eigrpd - isisd - fabricd - ldpd - nhrpd - ospf6d - ospfclient - ospfd - pimd - pbrd + frr + frr-bfdd + frr-bgpd + frr-eigrpd + frr-isisd + frr-fabricd + frr-ldpd + frr-nhrpd + frr-ospf6d + frr-ospfclient + frr-ospfd + frr-pimd + frr-pbrd + frr-ripd + frr-ripngd + frr-sharpd + frr-staticd + frr-watchfrr + frr-zebra + frr-vrrpd mtracebis - ripd - ripngd - sharpd - staticd - watchfrr - zebra vtysh - frr diff --git a/doc/manpages/subdir.am b/doc/manpages/subdir.am index a4457c9c45..9284212099 100644 --- a/doc/manpages/subdir.am +++ b/doc/manpages/subdir.am @@ -3,33 +3,34 @@ # man_RSTFILES = \ - doc/manpages/bgpd.rst \ + doc/manpages/bfd-options.rst \ doc/manpages/common-options.rst \ doc/manpages/conf.py \ doc/manpages/defines.rst \ - doc/manpages/eigrpd.rst \ doc/manpages/epilogue.rst \ - doc/manpages/fabricd.rst \ + doc/manpages/frr-bfdd.rst \ + doc/manpages/frr-bgpd.rst \ + doc/manpages/frr-eigrpd.rst \ + doc/manpages/frr-fabricd.rst \ + doc/manpages/frr-isisd.rst \ + doc/manpages/frr-ldpd.rst \ + doc/manpages/frr-nhrpd.rst \ + doc/manpages/frr-ospf6d.rst \ + doc/manpages/frr-ospfclient.rst \ + doc/manpages/frr-ospfd.rst \ + doc/manpages/frr-pbrd.rst \ + doc/manpages/frr-pimd.rst \ + doc/manpages/frr-ripd.rst \ + doc/manpages/frr-ripngd.rst \ + doc/manpages/frr-sharpd.rst \ + doc/manpages/frr-staticd.rst \ + doc/manpages/frr-vrrpd.rst \ + doc/manpages/frr-watchfrr.rst \ + doc/manpages/frr-zebra.rst \ doc/manpages/frr.rst \ doc/manpages/index.rst \ - doc/manpages/isisd.rst \ - doc/manpages/ldpd.rst \ doc/manpages/mtracebis.rst \ - doc/manpages/nhrpd.rst \ - doc/manpages/ospf6d.rst \ - doc/manpages/ospfclient.rst \ - doc/manpages/ospfd.rst \ - doc/manpages/pimd.rst \ - doc/manpages/ripd.rst \ - doc/manpages/pbrd.rst \ - doc/manpages/ripngd.rst \ - doc/manpages/sharpd.rst \ - doc/manpages/staticd.rst \ doc/manpages/vtysh.rst \ - doc/manpages/watchfrr.rst \ - doc/manpages/zebra.rst \ - doc/manpages/bfdd.rst \ - doc/manpages/bfd-options.rst \ # end EXTRA_DIST += $(man_RSTFILES) diff --git a/doc/subdir.am b/doc/subdir.am index 7d3792bf2b..a1297a4f81 100644 --- a/doc/subdir.am +++ b/doc/subdir.am @@ -4,7 +4,6 @@ # You can set these variables from the command line. SPHINXOPTS ?= -SPHINXBUILD ?= sphinx-build PAPER ?= # Internal variables. @@ -32,20 +31,20 @@ am__v_MAKEINFO_1 = doc/%/_build/.doctrees/environment.pickle: $(AM_V_SPHINX) ( \ subdoc="$@"; subdoc="$${subdoc#doc/}"; subdoc="doc/$${subdoc%%/*}"; \ - $(SPHINXBUILD) -a -q -b text -d "$${subdoc}/_build/.doctrees" \ + $(PYTHON) $(PYSPHINX) -a -q -b text -d "$${subdoc}/_build/.doctrees" \ $(ALLSPHINXOPTS) "$(top_srcdir)/$${subdoc}" "$${subdoc}/_build/text" \ ) doc/%/_build/html/.buildinfo: doc/%/_build/.doctrees/environment.pickle $(AM_V_SPHINX) ( \ subdoc="$@"; subdoc="$${subdoc#doc/}"; subdoc="doc/$${subdoc%%/*}"; \ - $(SPHINXBUILD) -q -b html -d "$${subdoc}/_build/.doctrees" \ + $(PYTHON) $(PYSPHINX) -q -b html -d "$${subdoc}/_build/.doctrees" \ $(ALLSPHINXOPTS) "$(top_srcdir)/$${subdoc}" "$${subdoc}/_build/html" \ ) .PRECIOUS: doc/%/_build/texinfo/frr.texi doc/%/_build/texinfo/frr.texi: doc/%/_build/.doctrees/environment.pickle $(AM_V_SPHINX) ( \ subdoc="$@"; subdoc="$${subdoc#doc/}"; subdoc="doc/$${subdoc%%/*}"; \ - $(SPHINXBUILD) -q -b texinfo -d "$${subdoc}/_build/.doctrees" \ + $(PYTHON) $(PYSPHINX) -q -b texinfo -d "$${subdoc}/_build/.doctrees" \ $(ALLSPHINXOPTS) "$(top_srcdir)/$${subdoc}" "$${subdoc}/_build/texinfo" \ ) doc/%/_build/texinfo/frr.info: doc/%/_build/texinfo/frr.texi @@ -54,7 +53,7 @@ doc/%/_build/man/man.stamp: doc/%/_build/.doctrees/environment.pickle $(AM_V_SPHINX) ( \ subdoc="$@"; subdoc="$${subdoc#doc/}"; subdoc="doc/$${subdoc%%/*}"; \ $(MKDIR_P) "$${subdoc}/_build/man"; touch $@.tmp; \ - $(SPHINXBUILD) -a -q -b man -d "$${subdoc}/_build/.doctrees" \ + $(PYTHON) $(PYSPHINX) -a -q -b man -d "$${subdoc}/_build/.doctrees" \ $(ALLSPHINXOPTS) "$(top_srcdir)/$${subdoc}" "$${subdoc}/_build/man" && \ mv -f $@.tmp $@ \ ) @@ -80,7 +79,7 @@ $(M_SPHINXTARGETS): doc/%/_build/.doctrees/environment.pickle builder="$${target##*/}"; \ subdoc="$${target#doc/}"; subdoc="doc/$${subdoc%%/*}"; \ rm -rf "$@"; \ - $(SPHINXBUILD) -q -b $${builder} -d $${subdoc}/_build/.doctrees \ + $(PYTHON) $(PYSPHINX) -q -b $${builder} -d $${subdoc}/_build/.doctrees \ $(ALLSPHINXOPTS) $(top_srcdir)/$${subdoc} $@ \ ) diff --git a/doc/user/basic.rst b/doc/user/basic.rst index 8fbea29ee7..7117c5427d 100644 --- a/doc/user/basic.rst +++ b/doc/user/basic.rst @@ -25,10 +25,35 @@ forms the initial command set for a routing beast as it is starting. Config files are generally found in |INSTALL_PREFIX_ETC|. -Each of the daemons has its own config file. The daemon name plus ``.conf`` is -the default config file name. For example, zebra's default config file name is -:file:`zebra.conf`. You can specify a config file using the :option:`-f` or -:option:`--config_file` options when starting the daemon. +Config Methods +-------------- + +There are two ways of configuring FRR. + +Traditionally each of the daemons had its own config file. The daemon name plus +``.conf`` was the default config file name. For example, zebra's default config +file was :file:`zebra.conf`. This method is deprecated. + +Because of the amount of config files this creates, and the tendency of one +daemon to rely on others for certain functionality, most deployments now use +"integrated" configuration. In this setup all configuration goes into a single +file, typically :file:`/etc/frr/frr.conf`. When starting up FRR using an init +script or systemd, ``vtysh`` is invoked to read the config file and send the +appropriate portions to only the daemons interested in them. Running +configuration updates are persisted back to this single file using ``vtysh``. +This is the recommended method. To use this method, add the following line to +:file:`/etc/frr/vtysh.conf`: + +.. code-block:: frr + + service integrated-vtysh-config + +If you installed from source or used a package, this is probably already +present. + +If desired, you can specify a config file using the :option:`-f` or +:option:`--config_file` options when starting a daemon. + .. _basic-config-commands: @@ -86,6 +111,15 @@ Basic Config Commands debugging. Note that the existing code logs its most important messages with severity ``errors``. + .. warning:: + + FRRouting uses the ``writev()`` system call to write log messages. This + call is supposed to be atomic, but in reality this does not hold for + pipes or terminals, only regular files. This means that in rare cases, + concurrent log messages from distinct threads may get jumbled in + terminal output. Use a log file and ``tail -f`` if this rare chance is + inacceptable to your setup. + .. index:: single: no log file [FILENAME [LEVEL]] single: log file FILENAME [LEVEL] @@ -104,14 +138,6 @@ Basic Config Commands deprecated ``log trap`` command) will be used. The ``no`` form of the command disables logging to a file. - .. note:: - - If you do not configure any file logging, and a daemon crashes due to a - signal or an assertion failure, it will attempt to save the crash - information in a file named :file:`/var/tmp/frr..crashlog`. - For security reasons, this will not happen if the file exists already, so - it is important to delete the file after reporting the crash information. - .. index:: single: no log syslog [LEVEL] single: log syslog [LEVEL] @@ -180,13 +206,37 @@ Basic Config Commands In this example, the precision is set to provide timestamps with millisecond accuracy. -.. index:: log commands -.. clicmd:: log commands +.. index:: [no] log commands +.. clicmd:: [no] log commands This command enables the logging of all commands typed by a user to all enabled log destinations. The note that logging includes full command lines, - including passwords. Once set, command logging can only be turned off by - restarting the daemon. + including passwords. If the daemon startup option `--command-log-always` + is used to start the daemon then this command is turned on by default + and cannot be turned off and the [no] form of the command is dissallowed. + +.. index:: + single: no log-filter WORD [DAEMON] + single: log-filter WORD [DAEMON] + +.. clicmd:: [no] log-filter WORD [DAEMON] + + This command forces logs to be filtered on a specific string. A log message + will only be printed if it matches on one of the filters in the log-filter + table. Can be daemon independent. + + .. note:: + + Log filters help when you need to turn on debugs that cause significant + load on the system (enabling certain debugs can bring FRR to a halt). + Log filters prevent this but you should still expect a small performance + hit due to filtering each of all those logs. + +.. index:: log-filter clear [DAEMON] +.. clicmd:: log-filter clear [DAEMON] + + This command clears all current filters in the log-filter table. Can be + daemon independent. .. index:: service password-encryption .. clicmd:: service password-encryption @@ -214,6 +264,17 @@ Basic Config Commands Set default motd string. +.. index:: banner motd file FILE +.. clicmd:: banner motd file FILE + + Set motd string from file. The file must be in directory specified + under ``--sysconfdir``. + +.. index:: banner motd line LINE +.. clicmd:: banner motd line LINE + + Set motd string from an input. + .. index:: no banner motd .. clicmd:: no banner motd @@ -251,6 +312,9 @@ Below is a sample configuration file for the zebra daemon. ! ! Zebra configuration file ! + frr version 6.0 + frr defaults traditional + ! hostname Router password zebra enable password zebra @@ -272,6 +336,137 @@ If a comment character is not the first character of the word, it's a normal character. So in the above example ``!`` will not be regarded as a comment and the password is set to ``zebra!password``. + +Configuration versioning, profiles and upgrade behavior +------------------------------------------------------- + +All |PACKAGE_NAME| daemons share a mechanism to specify a configuration profile +and version for loading and saving configuration. Specific configuration +settings take different default values depending on the selected profile and +version. + +While the profile can be selected by user configuration and will remain over +upgrades, |PACKAGE_NAME| will always write configurations using its current +version. This means that, after upgrading, a ``write file`` may write out a +slightly different configuration than what was read in. + +Since the previous configuration is loaded with its version's defaults, but +the new configuration is written with the new defaults, any default that +changed between versions will result in an appropriate configuration entry +being written out. **FRRouting configuration is sticky, staying consistent +over upgrades.** Changed defaults will only affect new configuration. + +Note that the loaded version persists into interactive configuration +sessions. Commands executed in an interactive configuration session are +no different from configuration loaded at startup. This means that when, +say, you configure a new BGP peer, the defaults used for configuration +are the ones selected by the last ``frr version`` command. + +.. warning:: + + Saving the configuration does not bump the daemons forward to use the new + version for their defaults, but restarting them will, since they will then + apply the new ``frr version`` command that was written out. Manually + execute the ``frr version`` command in ``show running-config`` to avoid + this intermediate state. + +This is visible in ``show running-config``: + +.. code-block:: frr + + Current configuration: + ! + ! loaded from 6.0 + frr version 6.1-dev + frr defaults traditional + ! + +If you save and then restart with this configuration, the old defaults will +no longer apply. Similarly, you could execute ``frr version 6.1-dev``, causing +the new defaults to apply and the ``loaded from 6.0`` comment to disappear. + + +Profiles +^^^^^^^^ + +|PACKAGE_NAME| provides configuration profiles to adapt its default settings +to various usage scenarios. Currently, the following profiles are +implemented: + +* ``traditional`` - reflects defaults adhering mostly to IETF standards or + common practices in wide-area internet routing. +* ``datacenter`` - reflects a single administrative domain with intradomain + links using aggressive timers. + +Your distribution/installation may pre-set a profile through the ``-F`` command +line option on all daemons. All daemons must be configured for the same +profile. The value specified on the command line is only a pre-set and any +``frr defaults`` statement in the configuration will take precedence. + +.. note:: + + The profile must be the same across all daemons. Mismatches may result + in undefined behavior. + +You can freely switch between profiles without causing any interruption or +configuration changes. All settings remain at their previous values, and +``show running-configuration`` output will have new output listing the previous +default values as explicit configuration. New configuration, e.g. adding a +BGP peer, will use the new defaults. To apply the new defaults for existing +configuration, the previously-invisible old defaults that are now shown must +be removed from the configuration. + + +Upgrade practices for interactive configuration +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If you configure |PACKAGE_NAME| interactively and use the configuration +writing functionality to make changes persistent, the following +recommendations apply in regards to upgrades: + +1. Skipping major versions should generally work but is still inadvisable. + To avoid unneeded issue, upgrade one major version at a time and write + out the configuration after each update. + +2. After installing a new |PACKAGE_NAME| version, check the configuration + for differences against your old configuration. If any defaults changed + that affect your setup, lines may appear or disappear. If a new line + appears, it was previously the default (or not supported) and is now + neccessary to retain previous behavior. If a line disappears, it + previously wasn't the default, but now is, so it is no longer necessary. + +3. Check the log files for deprecation warnings by using ``grep -i deprecat``. + +4. After completing each upgrade, save the configuration and either restart + |PACKAGE_NAME| or execute ``frr version `` to ensure defaults of + the new version are fully applied. + + +Upgrade practices for autogenerated configuration +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +When using |PACKAGE_NAME| with generated configurations (e.g. Ansible, +Puppet, etc.), upgrade considerations differ somewhat: + +1. Always write out a ``frr version`` statement in the configurations you + generate. This ensures that defaults are applied consistently. + +2. Try to not run more distinct versions of |PACKAGE_NAME| than necessary. + Each version may need to be checked individually. If running a mix of + older and newer installations, use the oldest version for the + ``frr version`` statement. + +3. When rolling out upgrades, generate a configuration as usual with the old + version identifier and load it. Check for any differences or deprecation + warnings. If there are differences in the configuration, propagate these + back to the configuration generator to minimize relying on actual default + values. + +4. After the last installation of an old version is removed, change the + configuration generation to a newer ``frr version`` as appropriate. Perform + the same checks as when rolling out upgrades. + + .. _terminal-mode-commands: Terminal Mode Commands @@ -287,8 +482,8 @@ Terminal Mode Commands Write current configuration to configuration file. -.. index:: configure terminal -.. clicmd:: configure terminal +.. index:: configure [terminal] +.. clicmd:: configure [terminal] Change to configuration mode. This command is the first step to configuration. @@ -320,6 +515,11 @@ Terminal Mode Commands Shows the current configuration of the logging system. This includes the status of all logging destinations. +.. index:: show log-filter +.. clicmd:: show log-filter + + Shows the current log filters applied to each daemon. + .. index:: show memory .. clicmd:: show memory @@ -414,6 +614,23 @@ Terminal Mode Commands (view) show [ip] bgp l2vpn evpn all overlay ... +.. _common-show-commands: + +.. index:: show thread cpu +.. clicmd:: show thread cpu [r|w|t|e|x] + + This command displays system run statistics for all the different event + types. If no options is specified all different run types are displayed + together. Additionally you can ask to look at (r)ead, (w)rite, (t)imer, + (e)vent and e(x)ecute thread event types. If you have compiled with + disable-cpu-time then this command will not show up. + +.. index:: show thread poll +.. clicmd:: show thread poll + + This command displays FRR's poll data. It allows a glimpse into how + we are setting each individual fd for the poll command at that point + in time. .. _common-invocation-options: @@ -462,10 +679,23 @@ These options apply to all |PACKAGE_NAME| daemons. Set the user and group to run as. +.. option:: -N + + Set the namespace that the daemon will run in. A "/" will + be added to all files that use the statedir. If you have "/var/run/frr" + as the default statedir then it will become "/var/run/frr/". + .. option:: -v, --version Print program version. +.. option:: --command-log-always + + Cause the daemon to always log commands entered to the specified log file. + This also makes the `no log commands` command dissallowed. Enabling this + is suggested if you have need to track what the operator is doing on + this router. + .. option:: --log When initializing the daemon, setup the log to go to either stdout, diff --git a/doc/user/bfd.rst b/doc/user/bfd.rst index 986d1494a5..86b0c28002 100644 --- a/doc/user/bfd.rst +++ b/doc/user/bfd.rst @@ -47,6 +47,9 @@ may also be specified (:ref:`common-invocation-options`). #define BFDD_CONTROL_SOCKET "|INSTALL_PREFIX_STATE|/bfdd.sock" + This option overrides the location addition that the -N option provides + to the bfdd.sock + .. _bfd-commands: @@ -72,31 +75,50 @@ BFDd Commands peer listener to and the address we should use to send the packets. This option is mandatory for IPv6. - `interface` selects which interface we should use. This option - conflicts with `vrf`. + `interface` selects which interface we should use. `vrf` selects which domain we want to use. -.. index:: no peer $peer [{multihop|local-address $local|interface IFNAME$ifname|vrf NAME$vrfname}] -.. clicmd:: no peer $peer [{multihop|local-address $local|interface IFNAME$ifname|vrf NAME$vrfname}] +.. index:: no peer $peer [{multihop|local-address $local|interface IFNAME$ifname|vrf NAME$vrf_name}] +.. clicmd:: no peer $peer [{multihop|local-address $local|interface IFNAME$ifname|vrf NAME$vrf_name}] Stops and removes the selected peer. -.. index:: show bfd peers [json] -.. clicmd:: show bfd peers [json] + +.. index:: profile WORD +.. clicmd:: profile WORD + + Creates a peer profile that can be configured in multiple peers. + + +.. index:: no profile WORD +.. clicmd:: no profile WORD + + Deletes a peer profile. Any peer using the profile will have their + configurations reset to the default values. + + +.. index:: show bfd [vrf NAME] peers [json] +.. clicmd:: show bfd [vrf NAME] peers [json] Show all configured BFD peers information and current status. -.. index:: show bfd peer $peer [{multihop|local-address $local|interface IFNAME$ifname|vrf NAME$vrfname}]> [json] -.. clicmd:: show bfd peer $peer [{multihop|local-address $local|interface IFNAME$ifname|vrf NAME$vrfname}]> [json] +.. index:: show bfd [vrf NAME$vrf_name] peer $peer [{multihop|local-address $local|interface IFNAME$ifname}]> [json] +.. clicmd:: show bfd [vrf NAME$vrf_name] peer $peer [{multihop|local-address $local|interface IFNAME$ifname}]> [json] Show status for a specific BFD peer. +.. index:: show bfd [vrf NAME] peers brief [json] +.. clicmd:: show bfd [vrf NAME] peers brief [json] + + Show all configured BFD peers information and current status in brief. .. _bfd-peer-config: -Peer Configurations -------------------- +Peer / Profile Configuration +---------------------------- + +BFD peers and profiles share the same BFD session configuration commands. .. index:: detect-multiplier (2-255) .. clicmd:: detect-multiplier (2-255) @@ -121,7 +143,7 @@ Peer Configurations .. clicmd:: transmit-interval (10-60000) The minimum transmission interval (less jitter) that this system - wants to use to send BFD control packets. + wants to use to send BFD control packets. Defaults to 300ms. .. index:: echo-interval (10-60000) .. clicmd:: echo-interval (10-60000) @@ -137,7 +159,7 @@ Peer Configurations It is recommended that the transmission interval of control packets to be increased after enabling echo-mode to reduce bandwidth usage. - For example: `transmission-interval 2000`. + For example: `transmit-interval 2000`. Echo mode is not supported on multi-hop setups (see :rfc:`5883` section 3). @@ -148,6 +170,37 @@ Peer Configurations Enables or disables the peer. When the peer is disabled an 'administrative down' message is sent to the remote peer. + +.. index:: [no] passive-mode +.. clicmd:: [no] passive-mode + + Mark session as passive: a passive session will not attempt to start + the connection and will wait for control packets from peer before it + begins replying. + + This feature is useful when you have a router that acts as the + central node of a star network and you want to avoid sending BFD + control packets you don't need to. + + The default is active-mode (or ``no passive-mode``). + +.. index:: [no] minimum-ttl (1-254) +.. clicmd:: [no] minimum-ttl (1-254) + + For multi hop sessions only: configure the minimum expected TTL for + an incoming BFD control packet. + + This feature serves the purpose of thightening the packet validation + requirements to avoid receiving BFD control packets from other + sessions. + + The default value is 254 (which means we only expect one hop between + this system and the peer). + + +BFD Peer Specific Commands +-------------------------- + .. index:: label WORD .. clicmd:: label WORD @@ -155,6 +208,21 @@ Peer Configurations later on other daemons to refer to a specific peer. +.. index:: profile BFDPROF +.. clicmd:: profile BFDPROF + + Configure peer to use the profile configurations. + + Notes: + + - Profile configurations can be overriden on a peer basis by specifying + new parameters in peer configuration node. + - Non existing profiles can be configured and they will only be applied + once they start to exist. + - If the profile gets updated the new configuration will be applied to all + peers with the profile without interruptions. + + .. _bfd-bgp-peer-config: BGP BFD Configuration @@ -175,6 +243,68 @@ The following commands are available inside the BGP configuration node. Removes any notification registration for this neighbor. +.. index:: neighbor bfd check-control-plane-failure +.. clicmd:: neighbor bfd check-control-plane-failure + + Allow to write CBIT independence in BFD outgoing packets. Also allow to + read both C-BIT value of BFD and lookup BGP peer status. This command is + useful when a BFD down event is caught, while the BGP peer requested that + local BGP keeps the remote BGP entries as staled if such issue is detected. + This is the case when graceful restart is enabled, and it is wished to + ignore the BD event while waiting for the remote router to restart. + +.. index:: no neighbor bfd check-control-plane-failure +.. clicmd:: no neighbor bfd check-control-plane-failure + + Disallow to write CBIT independence in BFD outgoing packets. Also disallow + to ignore BFD down notification. This is the default behaviour. + + +.. index:: neighbor bfd profile BFDPROF +.. clicmd:: neighbor bfd profile BFDPROF + + Same as command ``neighbor bfd``, but applies the + BFD profile to the sessions it creates or that already exist. + + +.. index:: no neighbor bfd profile BFDPROF +.. clicmd:: no neighbor bfd profile BFDPROF + + Removes the BFD profile configuration from peer session(s). + + +.. _bfd-isis-peer-config: + +IS-IS BFD Configuration +----------------------- + +The following commands are available inside the interface configuration node. + +.. index:: isis bfd +.. clicmd:: isis bfd + + Listen for BFD events on peers created on the interface. Every time + a new neighbor is found a BFD peer is created to monitor the link + status for fast convergence. + +.. index:: no isis bfd +.. clicmd:: no isis bfd + + Removes any notification registration for this interface peers. + + Note that there will be just one BFD session per interface. In case both + IPv4 and IPv6 support are configured then just a IPv6 based session is + created. + +.. index:: isis bfd profile BFDPROF +.. clicmd:: isis bfd profile BFDPROF + + Use a BFD profile BFDPROF as provided in the BFD configuration. + +.. index:: no isis bfd profile BFDPROF +.. clicmd:: no isis bfd profile BFDPROF + + Removes any BFD profile if present. .. _bfd-ospf-peer-config: @@ -271,6 +401,24 @@ Here are the available peer configurations: :: bfd + ! Configure a fast profile + profile fast + receive-interval 150 + transmit-interval 150 + ! + + ! Configure peer with fast profile + peer 192.168.0.6 + profile fast + no shutdown + ! + + ! Configure peer with fast profile and override receive speed. + peer 192.168.0.7 + profile fast + receive-interval 500 + no shutdown + ! ! configure a peer on an specific interface peer 192.168.0.1 interface eth0 @@ -296,6 +444,11 @@ Here are the available peer configurations: shutdown ! + ! configure a peer on an interface from a separate vrf + peer 192.168.0.5 interface eth1 vrf vrf2 + no shutdown + ! + ! remove a peer no peer 192.168.0.3 vrf foo @@ -318,11 +471,14 @@ You can inspect the current BFD peer status with the following commands: Uptime: 1 minute(s), 51 second(s) Diagnostics: ok Remote diagnostics: ok + Peer Type: dynamic Local timers: + Detect-multiplier: 3 Receive interval: 300ms Transmission interval: 300ms Echo transmission interval: disabled Remote timers: + Detect-multiplier: 3 Receive interval: 300ms Transmission interval: 300ms Echo transmission interval: 50ms @@ -335,11 +491,14 @@ You can inspect the current BFD peer status with the following commands: Uptime: 1 minute(s), 53 second(s) Diagnostics: ok Remote diagnostics: ok + Peer Type: configured Local timers: + Detect-multiplier: 3 Receive interval: 300ms Transmission interval: 300ms Echo transmission interval: disabled Remote timers: + Detect-multiplier: 3 Receive interval: 300ms Transmission interval: 300ms Echo transmission interval: 50ms @@ -354,17 +513,31 @@ You can inspect the current BFD peer status with the following commands: Uptime: 3 minute(s), 4 second(s) Diagnostics: ok Remote diagnostics: ok + Peer Type: dynamic Local timers: + Detect-multiplier: 3 Receive interval: 300ms Transmission interval: 300ms Echo transmission interval: disabled Remote timers: + Detect-multiplier: 3 Receive interval: 300ms Transmission interval: 300ms Echo transmission interval: 50ms frr# show bfd peer 192.168.0.1 json - {"multihop":false,"peer":"192.168.0.1","id":1,"remote-id":1,"status":"up","uptime":161,"diagnostic":"ok","remote-diagnostic":"ok","receive-interval":300,"transmit-interval":300,"echo-interval":50,"remote-receive-interval":300,"remote-transmit-interval":300,"remote-echo-interval":50} + {"multihop":false,"peer":"192.168.0.1","id":1,"remote-id":1,"status":"up","uptime":161,"diagnostic":"ok","remote-diagnostic":"ok","receive-interval":300,"transmit-interval":300,"echo-interval":50,"detect-multiplier":3,"remote-receive-interval":300,"remote-transmit-interval":300,"remote-echo-interval":50,"remote-detect-multiplier":3,"peer-type":"dynamic"} + + +You can inspect the current BFD peer status in brief with the following commands: + +:: + + frr# show bfd peers brief + Session count: 1 + SessionId LocalAddress PeerAddress Status + ========= ============ =========== ====== + 1 192.168.0.1 192.168.0.2 up You can also inspect peer session counters with the following commands: @@ -403,3 +576,63 @@ You can also inspect peer session counters with the following commands: frr# show bfd peer 192.168.0.1 counters json {"multihop":false,"peer":"192.168.0.1","control-packet-input":348,"control-packet-output":685,"echo-packet-input":6815,"echo-packet-output":6816,"session-up":1,"session-down":0,"zebra-notifications":4} + +You can also clear packet counters per session with the following commands, only the packet counters will be reset: + +:: + + frr# clear bfd peers counters + + frr# show bfd peers counters + BFD Peers: + peer 192.168.2.1 interface r2-eth2 + Control packet input: 0 packets + Control packet output: 0 packets + Echo packet input: 0 packets + Echo packet output: 0 packets + Session up events: 1 + Session down events: 0 + Zebra notifications: 2 + + peer 192.168.0.1 + Control packet input: 0 packets + Control packet output: 0 packets + Echo packet input: 0 packets + Echo packet output: 0 packets + Session up events: 1 + Session down events: 0 + Zebra notifications: 4 + +Debugging +========= + +By default only informational, warning and errors messages are going to be +displayed. If you want to get debug messages and other diagnostics then make +sure you have `debugging` level enabled: + +:: + + config + log file /var/log/frr/frr.log debugging + log syslog debugging + +You may also fine tune the debug messages by selecting one or more of the +debug levels: + +.. index:: [no] debug bfd network +.. clicmd:: [no] debug bfd network + + Toggle network events: show messages about socket failures and unexpected + BFD messages that may not belong to registered peers. + +.. index:: [no] debug bfd peer +.. clicmd:: [no] debug bfd peer + + Toggle peer event log messages: show messages about peer creation/removal + and state changes. + +.. index:: [no] debug bfd zebra +.. clicmd:: [no] debug bfd zebra + + Toggle zebra message events: show messages about interfaces, local + addresses, VRF and daemon peer registrations. diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index 35e42d95cb..6f3ff3a5f4 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -35,6 +35,44 @@ be specified (:ref:`common-invocation-options`). of ``0.0.0.0`` / ``::``. This can be useful to constrain bgpd to an internal address, or to run multiple bgpd processes on one host. +.. option:: -n, --no_kernel + + Do not install learned routes into the linux kernel. This option is useful + for a route-reflector environment or if you are running multiple bgp + processes in the same namespace. This option is different than the --no_zebra + option in that a ZAPI connection is made. + +.. option:: -S, --skip_runas + + Skip the normal process of checking capabilities and changing user and group + information. + +.. option:: -e, --ecmp + + Run BGP with a limited ecmp capability, that is different than what BGP + was compiled with. The value specified must be greater than 0 and less + than or equal to the MULTIPATH_NUM specified on compilation. + +.. option:: -Z, --no_zebra + + Do not communicate with zebra at all. This is different than the --no_kernel + option in that we do not even open a ZAPI connection to the zebra process. + +.. option:: -s, --socket_size + + When opening tcp connections to our peers, set the socket send buffer + size that the kernel will use for the peers socket. This option + is only really useful at a very large scale. Experimentation should + be done to see if this is helping or not at the scale you are running + at. + +LABEL MANAGER +------------- + +.. option:: -I, --int_num + + Set zclient id. This is required when using Zebra label manager in proxy mode. + .. _bgp-basic-concepts: Basic Concepts @@ -267,29 +305,6 @@ An example configuration with multiple autonomous systems might look like this: neighbor 10.0.0.6 remote-as 70 ... -In the past this feature done differently and the following commands were -required to enable the functionality. They are now deprecated. - -.. deprecated:: 5.0 - This command is deprecated and may be safely removed from the config. - -.. index:: bgp multiple-instance -.. clicmd:: bgp multiple-instance - - Enable BGP multiple instance feature. Because this is now the default - configuration this command will not be displayed in the running - configuration. - -.. deprecated:: 5.0 - This command is deprecated and may be safely removed from the config. - -.. index:: no bgp multiple-instance -.. clicmd:: no bgp multiple-instance - - In previous versions of FRR, this command disabled the BGP multiple instance - feature. This functionality is automatically turned on when BGP multiple - instances or views exist so this command no longer does anything. - .. seealso:: :ref:`bgp-vrf-route-leaking` .. seealso:: :ref:`zebra-vrf` @@ -391,6 +406,75 @@ Administrative Distance Metrics Sets the administrative distance for a particular route. +.. _bgp-requires-policy: + +Require policy on EBGP +------------------------------- + +.. index:: [no] bgp ebgp-requires-policy +.. clicmd:: [no] bgp ebgp-requires-policy + + This command requires incoming and outgoing filters to be applied + for eBGP sessions as part of RFC-8212 compliance. Without the incoming + filter, no routes will be accepted. Without the outgoing filter, no + routes will be announced. + + This is enabled by default for the traditional configuration and + turned off by default for datacenter configuration. + + When you enable/disable this option you MUST clear the session. + + When the incoming or outgoing filter is missing you will see + "(Policy)" sign under ``show bgp summary``: + + .. code-block:: frr + + exit1# show bgp summary + + IPv4 Unicast Summary: + BGP router identifier 10.10.10.1, local AS number 65001 vrf-id 0 + BGP table version 4 + RIB entries 7, using 1344 bytes of memory + Peers 2, using 43 KiB of memory + + Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt + 192.168.0.2 4 65002 8 10 0 0 0 00:03:09 5 (Policy) + fe80:1::2222 4 65002 9 11 0 0 0 00:03:09 (Policy) (Policy) + + Additionally a `show bgp neighbor` command would indicate in the `For address family:` + block that: + + .. code-block:: frr + + exit1# show bgp neighbor + ... + For address family: IPv4 Unicast + Update group 1, subgroup 1 + Packet Queue length 0 + Inbound soft reconfiguration allowed + Community attribute sent to this neighbor(all) + Inbound updates discarded due to missing policy + Outbound updates discarded due to missing policy + 0 accepted prefixes + +Reject routes with AS_SET or AS_CONFED_SET types +------------------------------------------------ + +.. index:: [no] bgp reject-as-sets +.. clicmd:: [no] bgp reject-as-sets + + This command enables rejection of incoming and outgoing routes having AS_SET or AS_CONFED_SET type. + +Disable checking if nexthop is connected on EBGP sessions +--------------------------------------------------------- + +.. index:: [no] bgp disable-ebgp-connected-route-check +.. clicmd:: [no] bgp disable-ebgp-connected-route-check + + This command is used to disable the connection verification process for EBGP peering sessions + that are reachable by a single hop but are configured on a loopback interface or otherwise + configured with a non-directly connected IP address. + .. _bgp-route-flap-dampening: Route Flap Dampening @@ -415,6 +499,9 @@ Route Flap Dampening The route-flap damping algorithm is compatible with :rfc:`2439`. The use of this command is not recommended nowadays. + At the moment, route-flap dampening is not working per VRF and is working only + for IPv4 unicast and multicast. + .. seealso:: https://www.ripe.net/publications/docs/ripe-378 @@ -658,6 +745,200 @@ from eBGP peers, :ref:`bgp-route-selection`. MED as an intra-AS metric to steer equal-length AS_PATH routes to, e.g., desired exit points. + +.. _bgp-graceful-restart: + +Graceful Restart +---------------- + +BGP graceful restart functionality as defined in +`RFC-4724 `_ defines the mechanisms that +allows BGP speaker to continue to forward data packets along known routes +while the routing protocol information is being restored. + + +Usually, when BGP on a router restarts, all the BGP peers detect that the +session went down and then came up. This "down/up" transition results in a +"routing flap" and causes BGP route re-computation, generation of BGP routing +updates, and unnecessary churn to the forwarding tables. + +The following functionality is provided by graceful restart: + +1. The feature allows the restarting router to indicate to the helping peer the + routes it can preserve in its forwarding plane during control plane restart + by sending graceful restart capability in the OPEN message sent during + session establishment. +2. The feature allows helping router to advertise to all other peers the routes + received from the restarting router which are preserved in the forwarding + plane of the restarting router during control plane restart. + + +:: + + + + (R1)-----------------------------------------------------------------(R2) + + 1. BGP Graceful Restart Capability exchanged between R1 & R2. + + <---------------------------------------------------------------------> + + 2. Kill BGP Process at R1. + + ----------------------------------------------------------------------> + + 3. R2 Detects the above BGP Restart & verifies BGP Restarting + Capability of R1. + + 4. Start BGP Process at R1. + + 5. Re-establish the BGP session between R1 & R2. + + <---------------------------------------------------------------------> + + 6. R2 Send initial route updates, followed by End-Of-Rib. + + <---------------------------------------------------------------------- + + 7. R1 was waiting for End-Of-Rib from R2 & which has been received + now. + + 8. R1 now runs BGP Best-Path algorithm. Send Initial BGP Update, + followed by End-Of Rib + + <---------------------------------------------------------------------> + + +.. _bgp-end-of-rib-message: + +End-of-RIB (EOR) message +^^^^^^^^^^^^^^^^^^^^^^^^ + +An UPDATE message with no reachable Network Layer Reachability Information +(NLRI) and empty withdrawn NLRI is specified as the End-of-RIB marker that can +be used by a BGP speaker to indicate to its peer the completion of the initial +routing update after the session is established. + +For the IPv4 unicast address family, the End-of-RIB marker is an UPDATE message +with the minimum length. For any other address family, it is an UPDATE message +that contains only the MP_UNREACH_NLRI attribute with no withdrawn routes for +that . + +Although the End-of-RIB marker is specified for the purpose of BGP graceful +restart, it is noted that the generation of such a marker upon completion of +the initial update would be useful for routing convergence in general, and thus +the practice is recommended. + +.. _bgp-route-selection-deferral-timer: + +Route Selection Deferral Timer +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Specifies the time the restarting router defers the route selection process +after restart. + +Restarting Router : The usage of route election deferral timer is specified +in https://tools.ietf.org/html/rfc4724#section-4.1 + +Once the session between the Restarting Speaker and the Receiving Speaker is +re-established, the Restarting Speaker will receive and process BGP messages +from its peers. + +However, it MUST defer route selection for an address family until it either. + +1. Receives the End-of-RIB marker from all its peers (excluding the ones with + the "Restart State" bit set in the received capability and excluding the ones + that do not advertise the graceful restart capability). +2. The Selection_Deferral_Timer timeout. + +.. index:: bgp graceful-restart select-defer-time (0-3600) +.. clicmd:: bgp graceful-restart select-defer-time (0-3600) + + This is command, will set deferral time to value specified. + + +.. index:: bgp graceful-restart rib-stale-time (1-3600) +.. clicmd:: bgp graceful-restart rib-stale-time (1-3600) + + This is command, will set the time for which stale routes are kept in RIB. + +.. _bgp-per-peer-graceful-restart: + +BGP Per Peer Graceful Restart +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Ability to enable and disable graceful restart, helper and no GR at all mode +functionality at peer level. + +So bgp graceful restart can be enabled at modes global BGP level or at per +peer level. There are two FSM, one for BGP GR global mode and other for peer +per GR. + +Default global mode is helper and default peer per mode is inherit from global. +If per peer mode is configured, the GR mode of this particular peer will +override the global mode. + +.. _bgp-GR-global-mode-cmd: + +BGP GR Global Mode Commands +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: bgp graceful-restart +.. clicmd:: bgp graceful-restart + + This command will enable BGP graceful restart ifunctionality at the global + level. + +.. index:: bgp graceful-restart disable +.. clicmd:: bgp graceful-restart disable + + This command will disable both the functionality graceful restart and helper + mode. + + +.. _bgp-GR-peer-mode-cmd: + +BGP GR Peer Mode Commands +^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: neighbor A.B.C.D graceful-restart +.. clicmd:: neighbor A.B.C.D graceful-restart + + This command will enable BGP graceful restart ifunctionality at the peer + level. + +.. index:: neighbor A.B.C.D graceful-restart-helper +.. clicmd:: neighbor A.B.C.D graceful-restart-helper + + This command will enable BGP graceful restart helper only functionality + at the peer level. + +.. index:: neighbor A.B.C.D graceful-restart-disable +.. clicmd:: neighbor A.B.C.D graceful-restart-disable + + This command will disable the entire BGP graceful restart functionality + at the peer level. + + +.. _bgp-shutdown: + +Administrative Shutdown +----------------------- + +.. index:: [no] bgp shutdown [message MSG...] +.. clicmd:: [no] bgp shutdown [message MSG...] + + Administrative shutdown of all peers of a bgp instance. Drop all BGP peers, + but preserve their configurations. The peers are notified in accordance with + `RFC 8203 `_ by sending a + ``NOTIFICATION`` message with error code ``Cease`` and subcode + ``Administrative Shutdown`` prior to terminating connections. This global + shutdown is independent of the neighbor shutdown, meaning that individually + shut down peers will not be affected by lifting it. + + An optional shutdown message `MSG` can be specified. + + .. _bgp-network: Networks @@ -683,16 +964,92 @@ Networks .. index:: no network A.B.C.D/M .. clicmd:: no network A.B.C.D/M +.. index:: [no] bgp network import-check +.. clicmd:: [no] bgp network import-check + + This configuration modifies the behavior of the network statement. + If you have this configured the underlying network must exist in + the rib. If you have the [no] form configured then BGP will not + check for the networks existence in the rib. For versions 7.3 and + before frr defaults for datacenter were the network must exist, + traditional did not check for existence. For versions 7.4 and beyond + both traditional and datacenter the network must exist. + +.. _bgp-ipv6-support: + +IPv6 Support +------------ + +.. index:: [no] neighbor A.B.C.D activate +.. clicmd:: [no] neighbor A.B.C.D activate + + This configuration modifies whether to enable an address family for a + specific neighbor. By default only the IPv4 unicast address family is + enabled. + + .. code-block:: frr + + router bgp 1 + address-family ipv6 unicast + neighbor 2001:0DB8::1 activate + network 2001:0DB8:5009::/64 + exit-address-family + + This configuration example says that network 2001:0DB8:5009::/64 will be + announced and enables the neighbor 2001:0DB8::1 to receive this announcement. + +.. index:: [no] bgp default ipv4-unicast +.. clicmd:: [no] bgp default ipv4-unicast + + By default, only the IPv4 unicast address family is announced to all + neighbors. Using the 'no bgp default ipv4-unicast' configuration overrides + this default so that all address families need to be enabled explicitly. + + .. code-block:: frr + + router bgp 1 + no bgp default ipv4-unicast + neighbor 10.10.10.1 remote-as 2 + neighbor 2001:0DB8::1 remote-as 3 + address-family ipv4 unicast + neighbor 10.10.10.1 activate + network 192.168.1.0/24 + exit-address-family + address-family ipv6 unicast + neighbor 2001:0DB8::1 activate + network 2001:0DB8:5009::/64 + exit-address-family + + This configuration demonstrates how the 'no bgp default ipv4-unicast' might + be used in a setup with two upstreams where each of the upstreams should only + receive either IPv4 or IPv6 annocuments. + + .. _bgp-route-aggregation: Route Aggregation ----------------- +.. _bgp-route-aggregation-ipv4: + +Route Aggregation-IPv4 Address Family +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + .. index:: aggregate-address A.B.C.D/M .. clicmd:: aggregate-address A.B.C.D/M This command specifies an aggregate address. +.. index:: aggregate-address A.B.C.D/M route-map NAME +.. clicmd:: aggregate-address A.B.C.D/M route-map NAME + + Apply a route-map for an aggregated prefix. + +.. index:: aggregate-address A.B.C.D/M origin +.. clicmd:: aggregate-address A.B.C.D/M origin + + Override ORIGIN for an aggregated prefix. + .. index:: aggregate-address A.B.C.D/M as-set .. clicmd:: aggregate-address A.B.C.D/M as-set @@ -703,11 +1060,79 @@ Route Aggregation .. clicmd:: aggregate-address A.B.C.D/M summary-only This command specifies an aggregate address. Aggregated routes will - not be announce. + not be announced. .. index:: no aggregate-address A.B.C.D/M .. clicmd:: no aggregate-address A.B.C.D/M + This command removes an aggregate address. + + + This configuration example setup the aggregate-address under + ipv4 address-family. + + .. code-block:: frr + + router bgp 1 + address-family ipv4 unicast + aggregate-address 10.0.0.0/8 + aggregate-address 20.0.0.0/8 as-set + aggregate-address 40.0.0.0/8 summary-only + aggregate-address 50.0.0.0/8 route-map aggr-rmap + exit-address-family + + +.. _bgp-route-aggregation-ipv6: + +Route Aggregation-IPv6 Address Family +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: aggregate-address X:X::X:X/M +.. clicmd:: aggregate-address X:X::X:X/M + + This command specifies an aggregate address. + +.. index:: aggregate-address X:X::X:X/M route-map NAME +.. clicmd:: aggregate-address X:X::X:X/M route-map NAME + + Apply a route-map for an aggregated prefix. + +.. index:: aggregate-address X:X::X:X/M origin +.. clicmd:: aggregate-address X:X::X:X/M origin + + Override ORIGIN for an aggregated prefix. + +.. index:: aggregate-address X:X::X:X/M as-set +.. clicmd:: aggregate-address X:X::X:X/M as-set + + This command specifies an aggregate address. Resulting routes include + AS set. + +.. index:: aggregate-address X:X::X:X/M summary-only +.. clicmd:: aggregate-address X:X::X:X/M summary-only + + This command specifies an aggregate address. Aggregated routes will + not be announced. + +.. index:: no aggregate-address X:X::X:X/M +.. clicmd:: no aggregate-address X:X::X:X/M + + This command removes an aggregate address. + + + This configuration example setup the aggregate-address under + ipv6 address-family. + + .. code-block:: frr + + router bgp 1 + address-family ipv6 unicast + aggregate-address 10::0/64 + aggregate-address 20::0/64 as-set + aggregate-address 40::0/64 summary-only + aggregate-address 50::0/64 route-map aggr-rmap + exit-address-family + .. _bgp-redistribute-to-bgp: Redistribution @@ -738,11 +1163,51 @@ Redistribution Redistribute OSPF route to BGP process. -.. index:: redistribute vpn -.. clicmd:: redistribute vpn +.. index:: redistribute vnc +.. clicmd:: redistribute vnc Redistribute VNC routes to BGP process. +.. index:: redistribute vnc-direct +.. clicmd:: redistribute vnc-direct + + Redistribute VNC direct (not via zebra) routes to BGP process. + +.. index:: bgp update-delay MAX-DELAY +.. clicmd:: bgp update-delay MAX-DELAY + +.. index:: bgp update-delay MAX-DELAY ESTABLISH-WAIT +.. clicmd:: bgp update-delay MAX-DELAY ESTABLISH-WAIT + + This feature is used to enable read-only mode on BGP process restart or when + a BGP process is cleared using 'clear ip bgp \*'. Note that this command is + configured at the global level and applies to all bgp instances/vrfs. It + cannot be used at the same time as the "update-delay" command described below, + which is entered in each bgp instance/vrf desired to delay update installation + and advertisements. The global and per-vrf approaches to defining update-delay + are mutually exclusive. + + When applicable, read-only mode would begin as soon as the first peer reaches + Established status and a timer for max-delay seconds is started. During this + mode BGP doesn't run any best-path or generate any updates to its peers. This + mode continues until: + + 1. All the configured peers, except the shutdown peers, have sent explicit EOR + (End-Of-RIB) or an implicit-EOR. The first keep-alive after BGP has reached + Established is considered an implicit-EOR. + If the establish-wait optional value is given, then BGP will wait for + peers to reach established from the beginning of the update-delay till the + establish-wait period is over, i.e. the minimum set of established peers for + which EOR is expected would be peers established during the establish-wait + window, not necessarily all the configured neighbors. + 2. max-delay period is over. + + On hitting any of the above two conditions, BGP resumes the decision process + and generates updates to its peers. + + Default max-delay is 0, i.e. the feature is off by default. + + .. index:: update-delay MAX-DELAY .. clicmd:: update-delay MAX-DELAY @@ -750,12 +1215,17 @@ Redistribution .. clicmd:: update-delay MAX-DELAY ESTABLISH-WAIT This feature is used to enable read-only mode on BGP process restart or when - BGP process is cleared using 'clear ip bgp \*'. When applicable, read-only - mode would begin as soon as the first peer reaches Established status and a - timer for max-delay seconds is started. - - During this mode BGP doesn't run any best-path or generate any updates to its - peers. This mode continues until: + a BGP process is cleared using 'clear ip bgp \*'. Note that this command is + configured under the specific bgp instance/vrf that the feaure is enabled for. + It cannot be used at the same time as the global "bgp update-delay" described + above, which is entered at the global level and applies to all bgp instances. + The global and per-vrf approaches to defining update-delay are mutually + exclusive. + + When applicable, read-only mode would begin as soon as the first peer reaches + Established status and a timer for max-delay seconds is started. During this + mode BGP doesn't run any best-path or generate any updates to its peers. This + mode continues until: 1. All the configured peers, except the shutdown peers, have sent explicit EOR (End-Of-RIB) or an implicit-EOR. The first keep-alive after BGP has reached @@ -826,8 +1296,8 @@ Defining Peers peers ASN is the same as mine as specified under the :clicmd:`router bgp ASN` command the connection will be denied. -.. index:: [no] bgp listen range peer-group WORD -.. clicmd:: [no] bgp listen range peer-group WORD +.. index:: [no] bgp listen range peer-group PGNAME +.. clicmd:: [no] bgp listen range peer-group PGNAME Accept connections from any peers in the specified prefix. Configuration from the specified peer-group is used to configure these peers. @@ -850,19 +1320,34 @@ Defining Peers ``net.core.optmem_max`` to allow the kernel to allocate the necessary option memory. +.. index:: [no] coalesce-time (0-4294967295) +.. clicmd:: [no] coalesce-time (0-4294967295) + + The time in milliseconds that BGP will delay before deciding what peers + can be put into an update-group together in order to generate a single + update for them. The default time is 1000. + .. _bgp-configuring-peers: Configuring Peers ^^^^^^^^^^^^^^^^^ -.. index:: [no] neighbor PEER shutdown -.. clicmd:: [no] neighbor PEER shutdown +.. index:: [no] neighbor PEER shutdown [message MSG...] [rtt (1-65535) [count (1-255)]] +.. clicmd:: [no] neighbor PEER shutdown [message MSG...] [rtt (1-65535) [count (1-255)]] Shutdown the peer. We can delete the neighbor's configuration by ``no neighbor PEER remote-as ASN`` but all configuration of the neighbor will be deleted. When you want to preserve the configuration, but want to drop the BGP peer, use this syntax. + Optionally you can specify a shutdown message `MSG`. + + Also, you can specify optionally _rtt_ in milliseconds to automatically + shutdown the peer if round-trip-time becomes higher than defined. + + Additional _count_ parameter is the number of keepalive messages to count + before shutdown the peer if round-trip-time becomes higher than defined. + .. index:: [no] neighbor PEER disable-connected-check .. clicmd:: [no] neighbor PEER disable-connected-check @@ -872,6 +1357,11 @@ Configuring Peers .. index:: [no] neighbor PEER ebgp-multihop .. clicmd:: [no] neighbor PEER ebgp-multihop + Specifying ``ebgp-multihop`` allows sessions with eBGP neighbors to + establish when they are multiple hops away. When the neighbor is not + directly connected and this knob is not enabled, the session will not + establish. + .. index:: [no] neighbor PEER description ... .. clicmd:: [no] neighbor PEER description ... @@ -906,6 +1396,14 @@ Configuring Peers keyword `all` is specified the modification is done also for routes learned via iBGP. +.. index:: neighbor PEER attribute-unchanged [{as-path|next-hop|med}] +.. clicmd:: neighbor PEER attribute-unchanged [{as-path|next-hop|med}] + + This command specifies attributes to be left unchanged for advertisements + sent to a peer. Use this to leave the next-hop unchanged in ipv6 + configurations, as the route-map directive to leave the next-hop unchanged + is only available for ipv4. + .. index:: [no] neighbor PEER update-source .. clicmd:: [no] neighbor PEER update-source @@ -931,6 +1429,15 @@ Configuring Peers .. index:: neighbor PEER port PORT .. clicmd:: neighbor PEER port PORT +.. index:: [no] neighbor PEER password PASSWORD +.. clicmd:: [no] neighbor PEER password PASSWORD + + Set a MD5 password to be used with the tcp socket that is being used + to connect to the remote peer. Please note if you are using this + command with a large number of peers on linux you should consider + modifying the `net.core.optmem_max` sysctl to a larger value to + avoid out of memory errors from the linux kernel. + .. index:: neighbor PEER send-community .. clicmd:: neighbor PEER send-community @@ -939,17 +1446,34 @@ Configuring Peers This command specifies a default `weight` value for the neighbor's routes. -.. index:: [no] neighbor PEER maximum-prefix NUMBER -.. clicmd:: [no] neighbor PEER maximum-prefix NUMBER +.. index:: [no] neighbor PEER maximum-prefix NUMBER [force] +.. clicmd:: [no] neighbor PEER maximum-prefix NUMBER [force] + + Sets a maximum number of prefixes we can receive from a given peer. If this + number is exceeded, the BGP session will be destroyed. -.. index:: [no] neighbor PEER local-as AS-NUMBER no-prepend -.. clicmd:: [no] neighbor PEER local-as AS-NUMBER no-prepend + In practice, it is generally preferable to use a prefix-list to limit what + prefixes are received from the peer instead of using this knob. Tearing down + the BGP session when a limit is exceeded is far more destructive than merely + rejecting undesired prefixes. The prefix-list method is also much more + granular and offers much smarter matching criterion than number of received + prefixes, making it more suited to implementing policy. -.. index:: [no] neighbor PEER local-as AS-NUMBER no-prepend replace-as -.. clicmd:: [no] neighbor PEER local-as AS-NUMBER no-prepend replace-as + If _force_ is set, then ALL prefixes are counted for maximum instead of + accepted only. This is useful for cases where an inbound filter is applied, + but you want maximum-prefix to act on ALL (including filtered) prefixes. This + option requires `soft-reconfiguration inbound` to be enabled for the peer. -.. index:: [no] neighbor PEER local-as AS-NUMBER -.. clicmd:: [no] neighbor PEER local-as AS-NUMBER +.. index:: [no] neighbor PEER maximum-prefix-out NUMBER +.. clicmd:: [no] neighbor PEER maximum-prefix-out NUMBER + + Sets a maximum number of prefixes we can send to a given peer. + + Since sent prefix count is managed by update-groups, this option + creates a separate update-group for outgoing updates. + +.. index:: [no] neighbor PEER local-as AS-NUMBER [no-prepend] [replace-as] +.. clicmd:: [no] neighbor PEER local-as AS-NUMBER [no-prepend] [replace-as] Specify an alternate AS for this BGP process when interacting with the specified peer. With no modifiers, the specified local-as is prepended to @@ -967,6 +1491,49 @@ Configuring Peers This command is only allowed for eBGP peers. +.. index:: [no] neighbor as-override +.. clicmd:: [no] neighbor as-override + + Override AS number of the originating router with the local AS number. + + Usually this configuration is used in PEs (Provider Edge) to replace + the incoming customer AS number so the connected CE (Customer Edge) + can use the same AS number as the other customer sites. This allows + customers of the provider network to use the same AS number across + their sites. + + This command is only allowed for eBGP peers. + +.. index:: [no] neighbor allowas-in [<(1-10)|origin>] +.. clicmd:: [no] neighbor allowas-in [<(1-10)|origin>] + + Accept incoming routes with AS path containing AS number with the same value + as the current system AS. + + This is used when you want to use the same AS number in your sites, but you + can't connect them directly. This is an alternative to + `neighbor WORD as-override`. + + The parameter `(1-10)` configures the amount of accepted occurences of the + system AS number in AS path. + + The parameter `origin` configures BGP to only accept routes originated with + the same AS number as the system. + + This command is only allowed for eBGP peers. + +.. index:: [no] neighbor addpath-tx-all-paths +.. clicmd:: [no] neighbor addpath-tx-all-paths + + Configure BGP to send all known paths to neighbor in order to preserve multi + path capabilities inside a network. + +.. index:: [no] neighbor addpath-tx-bestpath-per-AS +.. clicmd:: [no] neighbor addpath-tx-bestpath-per-AS + + Configure BGP to send best known paths to neighbor in order to preserve multi + path capabilities inside a network. + .. index:: [no] neighbor PEER ttl-security hops NUMBER .. clicmd:: [no] neighbor PEER ttl-security hops NUMBER @@ -999,6 +1566,35 @@ Configuring Peers on by default or not. This command defaults to on and is not displayed. The `no bgp default ipv4-unicast` form of the command is displayed. +.. index:: [no] bgp default show-hostname +.. clicmd:: [no] bgp default show-hostname + + This command shows the hostname of the peer in certain BGP commands + outputs. It's easier to troubleshoot if you have a number of BGP peers. + +.. index:: [no] bgp default show-nexthop-hostname +.. clicmd:: [no] bgp default show-nexthop-hostname + + This command shows the hostname of the next-hop in certain BGP commands + outputs. It's easier to troubleshoot if you have a number of BGP peers + and a number of routes to check. + +.. index:: [no] neighbor PEER advertisement-interval (0-600) +.. clicmd:: [no] neighbor PEER advertisement-interval (0-600) + + Setup the minimum route advertisement interval(mrai) for the + peer in question. This number is between 0 and 600 seconds, + with the default advertisement interval being 0. + +Displaying Information about Peers +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: show bgp neighbors WORD bestpath-routes [json] [wide] +.. clicmd:: show bgp neighbors WORD bestpath-routes [json] [wide] + + For the given neighbor, WORD, that is specified list the routes selected + by BGP as having the best path. + .. _bgp-peer-filtering: Peer Filtering @@ -1028,6 +1624,14 @@ Peer Filtering on reflected routes. This option allows the modifications to be reflected as well. Once enabled, it affects all reflected routes. +.. index:: [no] neighbor PEER sender-as-path-loop-detection +.. clicmd:: [no] neighbor PEER sender-as-path-loop-detection + + Enable the detection of sender side AS path loops and filter the + bad routes before they are sent. + + This setting is disabled by default. + .. _bgp-peer-group: Peer Groups @@ -1046,8 +1650,8 @@ and will share updates. This command defines a new peer group. -.. index:: neighbor PEER peer-group WORD -.. clicmd:: neighbor PEER peer-group WORD +.. index:: neighbor PEER peer-group PGNAME +.. clicmd:: neighbor PEER peer-group PGNAME This command bind specific peer to peer group WORD. @@ -1076,11 +1680,8 @@ Capability Negotiation Negotiation. Please use *dont-capability-negotiate* command to disable the feature. -.. index:: neighbor PEER dont-capability-negotiate -.. clicmd:: neighbor PEER dont-capability-negotiate - -.. index:: no neighbor PEER dont-capability-negotiate -.. clicmd:: no neighbor PEER dont-capability-negotiate +.. index:: [no] neighbor PEER dont-capability-negotiate +.. clicmd:: [no] neighbor PEER dont-capability-negotiate Suppress sending Capability Negotiation as OPEN message optional parameter to the peer. This command only affects the peer is configured other than @@ -1095,6 +1696,11 @@ Capability Negotiation configured by *override-capability*, *bgpd* ignores received capabilities then override negotiated capabilities with configured values. + Additionally the operator should be reminded that this feature fundamentally + disables the ability to use widely deployed BGP features. BGP unnumbered, + hostname support, AS4, Addpath, Route Refresh, ORF, Dynamic Capabilities, + and graceful restart. + .. index:: neighbor PEER override-capability .. clicmd:: neighbor PEER override-capability @@ -1111,35 +1717,51 @@ AS Path Access Lists AS path access list is user defined AS path. -.. index:: ip as-path access-list WORD permit|deny LINE -.. clicmd:: ip as-path access-list WORD permit|deny LINE +.. index:: bgp as-path access-list WORD permit|deny LINE +.. clicmd:: bgp as-path access-list WORD permit|deny LINE This command defines a new AS path access list. -.. index:: no ip as-path access-list WORD -.. clicmd:: no ip as-path access-list WORD +.. index:: no bgp as-path access-list WORD +.. clicmd:: no bgp as-path access-list WORD + +.. index:: no bgp as-path access-list WORD permit|deny LINE +.. clicmd:: no bgp as-path access-list WORD permit|deny LINE + +.. _bgp-bogon-filter-example: -.. index:: no ip as-path access-list WORD permit|deny LINE -.. clicmd:: no ip as-path access-list WORD permit|deny LINE +Bogon ASN filter policy configuration example +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: frr + + bgp as-path access-list 99 permit _0_ + bgp as-path access-list 99 permit _23456_ + bgp as-path access-list 99 permit _1310[0-6][0-9]_|_13107[0-1]_ .. _bgp-using-as-path-in-route-map: Using AS Path in Route Map -------------------------- -.. index:: match as-path WORD -.. clicmd:: match as-path WORD +.. index:: [no] match as-path WORD +.. clicmd:: [no] match as-path WORD + For a given as-path, WORD, match it on the BGP as-path given for the prefix + and if it matches do normal route-map actions. The no form of the command + removes this match from the route-map. -.. index:: set as-path prepend AS-PATH -.. clicmd:: set as-path prepend AS-PATH +.. index:: [no] set as-path prepend AS-PATH +.. clicmd:: [no] set as-path prepend AS-PATH - Prepend the given string of AS numbers to the AS_PATH. + Prepend the given string of AS numbers to the AS_PATH of the BGP path's NLRI. + The no form of this command removes this set operation from the route-map. -.. index:: set as-path prepend last-as NUM -.. clicmd:: set as-path prepend last-as NUM +.. index:: [no] set as-path prepend last-as NUM +.. clicmd:: [no] set as-path prepend last-as NUM Prepend the existing last AS number (the leftmost ASN) to the AS_PATH. + The no form of this command removes this set operation from the route-map. .. _bgp-communities-attribute: @@ -1282,8 +1904,8 @@ expanded interpreted on each use expanded community lists are slower than standard lists. -.. index:: ip community-list standard NAME permit|deny COMMUNITY -.. clicmd:: ip community-list standard NAME permit|deny COMMUNITY +.. index:: bgp community-list standard NAME permit|deny COMMUNITY +.. clicmd:: bgp community-list standard NAME permit|deny COMMUNITY This command defines a new standard community list. ``COMMUNITY`` is communities value. The ``COMMUNITY`` is compiled into community structure. @@ -1293,8 +1915,8 @@ expanded community list definition. When there is no matched entry, deny will be returned. When ``COMMUNITY`` is empty it matches to any routes. -.. index:: ip community-list expanded NAME permit|deny COMMUNITY -.. clicmd:: ip community-list expanded NAME permit|deny COMMUNITY +.. index:: bgp community-list expanded NAME permit|deny COMMUNITY +.. clicmd:: bgp community-list expanded NAME permit|deny COMMUNITY This command defines a new expanded community list. ``COMMUNITY`` is a string expression of communities attribute. ``COMMUNITY`` can be a regular @@ -1305,8 +1927,8 @@ expanded .. deprecated:: 5.0 It is recommended to use the more explicit versions of this command. -.. index:: ip community-list NAME permit|deny COMMUNITY -.. clicmd:: ip community-list NAME permit|deny COMMUNITY +.. index:: bgp community-list NAME permit|deny COMMUNITY +.. clicmd:: bgp community-list NAME permit|deny COMMUNITY When the community list type is not specified, the community list type is automatically detected. If ``COMMUNITY`` can be compiled into communities @@ -1315,29 +1937,29 @@ expanded for backward compatibility. Use of this feature is not recommended. -.. index:: no ip community-list [standard|expanded] NAME -.. clicmd:: no ip community-list [standard|expanded] NAME +.. index:: no bgp community-list [standard|expanded] NAME +.. clicmd:: no bgp community-list [standard|expanded] NAME Deletes the community list specified by ``NAME``. All community lists share the same namespace, so it's not necessary to specify ``standard`` or ``expanded``; these modifiers are purely aesthetic. -.. index:: show ip community-list [NAME] -.. clicmd:: show ip community-list [NAME] +.. index:: show bgp community-list [NAME detail] +.. clicmd:: show bgp community-list [NAME detail] Displays community list information. When ``NAME`` is specified the specified community list's information is shown. :: - # show ip community-list + # show bgp community-list Named Community standard list CLIST permit 7675:80 7675:100 no-export deny internet Named Community expanded list EXPAND permit : - # show ip community-list CLIST + # show bgp community-list CLIST detail Named Community standard list CLIST permit 7675:80 7675:100 no-export deny internet @@ -1355,14 +1977,14 @@ to 199 is expanded community list. These community lists are called as numbered community lists. On the other hand normal community lists is called as named community lists. -.. index:: ip community-list (1-99) permit|deny COMMUNITY -.. clicmd:: ip community-list (1-99) permit|deny COMMUNITY +.. index:: bgp community-list (1-99) permit|deny COMMUNITY +.. clicmd:: bgp community-list (1-99) permit|deny COMMUNITY This command defines a new community list. The argument to (1-99) defines the list identifier. -.. index:: ip community-list (100-199) permit|deny COMMUNITY -.. clicmd:: ip community-list (100-199) permit|deny COMMUNITY +.. index:: bgp community-list (100-199) permit|deny COMMUNITY +.. clicmd:: bgp community-list (100-199) permit|deny COMMUNITY This command defines a new expanded community list. The argument to (100-199) defines the list identifier. @@ -1376,7 +1998,7 @@ In :ref:`route-map` we can match on or set the BGP communities attribute. Using this feature network operator can implement their network policy based on BGP communities attribute. -The ollowing commands can be used in route maps: +The following commands can be used in route maps: .. index:: match community WORD exact-match [exact-match] .. clicmd:: match community WORD exact-match [exact-match] @@ -1428,12 +2050,12 @@ setting BGP communities attribute to the updates. neighbor 192.168.0.1 route-map RMAP in exit-address-family ! - ip community-list 70 permit 7675:70 - ip community-list 70 deny - ip community-list 80 permit 7675:80 - ip community-list 80 deny - ip community-list 90 permit 7675:90 - ip community-list 90 deny + bgp community-list 70 permit 7675:70 + bgp community-list 70 deny + bgp community-list 80 permit 7675:80 + bgp community-list 80 deny + bgp community-list 90 permit 7675:90 + bgp community-list 90 deny ! route-map RMAP permit 10 match community 70 @@ -1482,7 +2104,7 @@ announcements into the internal network. neighbor 192.168.0.1 route-map RMAP in exit-address-family ! - ip community-list 1 permit 0:80 0:90 + bgp community-list 1 permit 0:80 0:90 ! route-map RMAP permit in match community 1 @@ -1501,8 +2123,8 @@ community-list. neighbor 192.168.0.1 route-map RMAP in exit-address-family ! - ip community-list standard FILTER deny 1:1 - ip community-list standard FILTER permit + bgp community-list standard FILTER deny 1:1 + bgp community-list standard FILTER permit ! route-map RMAP permit 10 match community FILTER @@ -1515,8 +2137,8 @@ if the route does not have communities attribute at all. So community list .. code-block:: frr - ip community-list standard INTERNET deny 1:1 - ip community-list standard INTERNET permit internet + bgp community-list standard INTERNET deny 1:1 + bgp community-list standard INTERNET permit internet The following configuration is an example of communities value deletion. With @@ -1532,7 +2154,7 @@ community-list is used. ``deny`` community-list is ignored. neighbor 192.168.0.1 route-map RMAP in exit-address-family ! - ip community-list standard DEL permit 100:1 100:2 + bgp community-list standard DEL permit 100:1 100:2 ! route-map RMAP permit 10 set comm-list DEL delete @@ -1577,8 +2199,8 @@ the other is IP address based format. Extended Community Lists ^^^^^^^^^^^^^^^^^^^^^^^^ -.. index:: ip extcommunity-list standard NAME permit|deny EXTCOMMUNITY -.. clicmd:: ip extcommunity-list standard NAME permit|deny EXTCOMMUNITY +.. index:: bgp extcommunity-list standard NAME permit|deny EXTCOMMUNITY +.. clicmd:: bgp extcommunity-list standard NAME permit|deny EXTCOMMUNITY This command defines a new standard extcommunity-list. `extcommunity` is extended communities value. The `extcommunity` is compiled into extended @@ -1589,37 +2211,37 @@ Extended Community Lists there is no matched entry, deny will be returned. When `extcommunity` is empty it matches to any routes. -.. index:: ip extcommunity-list expanded NAME permit|deny LINE -.. clicmd:: ip extcommunity-list expanded NAME permit|deny LINE +.. index:: bgp extcommunity-list expanded NAME permit|deny LINE +.. clicmd:: bgp extcommunity-list expanded NAME permit|deny LINE This command defines a new expanded extcommunity-list. `line` is a string expression of extended communities attribute. `line` can be a regular expression (:ref:`bgp-regular-expressions`) to match an extended communities attribute in BGP updates. -.. index:: no ip extcommunity-list NAME -.. clicmd:: no ip extcommunity-list NAME +.. index:: no bgp extcommunity-list NAME +.. clicmd:: no bgp extcommunity-list NAME -.. index:: no ip extcommunity-list standard NAME -.. clicmd:: no ip extcommunity-list standard NAME +.. index:: no bgp extcommunity-list standard NAME +.. clicmd:: no bgp extcommunity-list standard NAME -.. index:: no ip extcommunity-list expanded NAME -.. clicmd:: no ip extcommunity-list expanded NAME +.. index:: no bgp extcommunity-list expanded NAME +.. clicmd:: no bgp extcommunity-list expanded NAME These commands delete extended community lists specified by `name`. All of extended community lists shares a single name space. So extended community lists can be removed simply specifying the name. -.. index:: show ip extcommunity-list -.. clicmd:: show ip extcommunity-list +.. index:: show bgp extcommunity-list +.. clicmd:: show bgp extcommunity-list -.. index:: show ip extcommunity-list NAME -.. clicmd:: show ip extcommunity-list NAME +.. index:: show bgp extcommunity-list NAME detail +.. clicmd:: show bgp extcommunity-list NAME detail This command displays current extcommunity-list information. When `name` is specified the community list's information is shown.:: - # show ip extcommunity-list + # show bgp extcommunity-list .. _bgp-extended-communities-in-route-map: @@ -1640,6 +2262,18 @@ BGP Extended Communities in Route Map This command set Site of Origin value. +.. index:: set extcommunity bandwidth <(1-25600) | cumulative | num-multipaths> [non-transitive] +.. clicmd:: set extcommunity bandwidth <(1-25600) | cumulative | num-multipaths> [non-transitive] + + This command sets the BGP link-bandwidth extended community for the prefix + (best path) for which it is applied. The link-bandwidth can be specified as + an ``explicit value`` (specified in Mbps), or the router can be told to use + the ``cumulative bandwidth`` of all multipaths for the prefix or to compute + it based on the ``number of multipaths``. The link bandwidth extended + community is encoded as ``transitive`` unless the set command explicitly + configures it as ``non-transitive``. + +.. seealso:: :ref:`wecmp_linkbw` Note that the extended expanded community is only used for `match` rule, not for `set` actions. @@ -1680,8 +2314,8 @@ Large Community Lists Two types of large community lists are supported, namely `standard` and `expanded`. -.. index:: ip large-community-list standard NAME permit|deny LARGE-COMMUNITY -.. clicmd:: ip large-community-list standard NAME permit|deny LARGE-COMMUNITY +.. index:: bgp large-community-list standard NAME permit|deny LARGE-COMMUNITY +.. clicmd:: bgp large-community-list standard NAME permit|deny LARGE-COMMUNITY This command defines a new standard large-community-list. `large-community` is the Large Community value. We can add multiple large communities under @@ -1691,8 +2325,8 @@ Two types of large community lists are supported, namely `standard` and definition. When there is no matched entry, a deny will be returned. When `large-community` is empty it matches any routes. -.. index:: ip large-community-list expanded NAME permit|deny LINE -.. clicmd:: ip large-community-list expanded NAME permit|deny LINE +.. index:: bgp large-community-list expanded NAME permit|deny LINE +.. clicmd:: bgp large-community-list expanded NAME permit|deny LINE This command defines a new expanded large-community-list. Where `line` is a string matching expression, it will be compared to the entire Large @@ -1700,24 +2334,24 @@ Two types of large community lists are supported, namely `standard` and lowest to highest. `line` can also be a regular expression which matches this Large Community attribute. -.. index:: no ip large-community-list NAME -.. clicmd:: no ip large-community-list NAME +.. index:: no bgp large-community-list NAME +.. clicmd:: no bgp large-community-list NAME -.. index:: no ip large-community-list standard NAME -.. clicmd:: no ip large-community-list standard NAME +.. index:: no bgp large-community-list standard NAME +.. clicmd:: no bgp large-community-list standard NAME -.. index:: no ip large-community-list expanded NAME -.. clicmd:: no ip large-community-list expanded NAME +.. index:: no bgp large-community-list expanded NAME +.. clicmd:: no bgp large-community-list expanded NAME These commands delete Large Community lists specified by `name`. All Large Community lists share a single namespace. This means Large Community lists can be removed by simply specifying the name. -.. index:: show ip large-community-list -.. clicmd:: show ip large-community-list +.. index:: show bgp large-community-list +.. clicmd:: show bgp large-community-list -.. index:: show ip large-community-list NAME -.. clicmd:: show ip large-community-list NAME +.. index:: show bgp large-community-list NAME detail +.. clicmd:: show bgp large-community-list NAME detail This command display current large-community-list information. When `name` is specified the community list information is shown. @@ -1732,13 +2366,15 @@ Two types of large community lists are supported, namely `standard` and Large Communities in Route Map """""""""""""""""""""""""""""" -.. index:: match large-community LINE -.. clicmd:: match large-community LINE +.. index:: match large-community LINE [exact-match] +.. clicmd:: match large-community LINE [exact-match] Where `line` can be a simple string to match, or a regular expression. It is very important to note that this match occurs on the entire large-community string as a whole, where each large-community is ordered - from lowest to highest. + from lowest to highest. When `exact-match` keyword is specified, match + happen only when BGP updates have completely same large communities value + specified in the large community list. .. index:: set large-community LARGE-COMMUNITY .. clicmd:: set large-community LARGE-COMMUNITY @@ -1858,11 +2494,11 @@ address-family: .. index:: label vpn export (0..1048575)|auto .. clicmd:: label vpn export (0..1048575)|auto - Specifies an optional MPLS label to be attached to a route exported from the - current unicast VRF to VPN. If label is specified as ``auto``, the label - value is automatically assigned from a pool maintained by the zebra - daemon. If zebra is not running, automatic label assignment will not - complete, which will block corresponding route export. + Enables an MPLS label to be attached to a route exported from the current + unicast VRF to VPN. If the value specified is ``auto``, the label value is + automatically assigned from a pool maintained by the Zebra daemon. If Zebra + is not running, or if this command is not configured, automatic label + assignment will not complete, which will block corresponding route export. .. index:: no label vpn export [(0..1048575)|auto] .. clicmd:: no label vpn export [(0..1048575)|auto] @@ -1922,71 +2558,81 @@ address-family: the VPN RIB as intermediary. -.. _bgp-cisco-compatibility: +.. _bgp-evpn: -Cisco Compatibility -------------------- +Ethernet Virtual Network - EVPN +------------------------------- -FRR has commands that change some configuration syntax and default behavior to -behave more closely to Cisco conventions. These are deprecated and will be -removed in a future version of FRR. +.. _bgp-evpn-advertise-pip: -.. deprecated:: 5.0 - Please transition to using the FRR specific syntax for your configuration. +EVPN advertise-PIP +^^^^^^^^^^^^^^^^^^ -.. index:: bgp config-type cisco -.. clicmd:: bgp config-type cisco +In a EVPN symmetric routing MLAG deployment, all EVPN routes advertised +with anycast-IP as next-hop IP and anycast MAC as the Router MAC (RMAC - in +BGP EVPN Extended-Community). +EVPN picks up the next-hop IP from the VxLAN interface's local tunnel IP and +the RMAC is obtained from the MAC of the L3VNI's SVI interface. +Note: Next-hop IP is used for EVPN routes whether symmetric routing is +deployed or not but the RMAC is only relevant for symmetric routing scenario. - Cisco compatible BGP configuration output. +Current behavior is not ideal for Prefix (type-5) and self (type-2) +routes. This is because the traffic from remote VTEPs routed sub optimally +if they land on the system where the route does not belong. - When this configuration line is specified: +The advertise-pip feature advertises Prefix (type-5) and self (type-2) +routes with system's individual (primary) IP as the next-hop and individual +(system) MAC as Router-MAC (RMAC), while leaving the behavior unchanged for +other EVPN routes. - - ``no synchronization`` is displayed. This command does nothing and is for - display purposes only. - - ``no auto-summary`` is displayed. - - The ``network`` and ``aggregate-address`` arguments are displayed as: +To support this feature there needs to have ability to co-exist a +(system-MAC, system-IP) pair with a (anycast-MAC, anycast-IP) pair with the +ability to terminate VxLAN-encapsulated packets received for either pair on +the same L3VNI (i.e associated VLAN). This capability is need per tenant +VRF instance. - :: +To derive the system-MAC and the anycast MAC, there needs to have a +separate/additional MAC-VLAN interface corresponding to L3VNI’s SVI. +The SVI interface’s MAC address can be interpreted as system-MAC +and MAC-VLAN interface's MAC as anycast MAC. - A.B.C.D M.M.M.M +To derive system-IP and anycast-IP, the default BGP instance's router-id is used +as system-IP and the VxLAN interface’s local tunnel IP as the anycast-IP. - FRR: network 10.0.0.0/8 - Cisco: network 10.0.0.0 +User has an option to configure the system-IP and/or system-MAC value if the +auto derived value is not preferred. - FRR: aggregate-address 192.168.0.0/24 - Cisco: aggregate-address 192.168.0.0 255.255.255.0 +Note: By default, advertise-pip feature is enabled and user has an option to +disable the feature via configuration CLI. Once the feature is disable under +bgp vrf instance or MAC-VLAN interface is not configured, all the routes follow +the same behavior of using same next-hop and RMAC values. - Community attribute handling is also different. If no configuration is - specified community attribute and extended community attribute are sent to - the neighbor. If a user manually disables the feature, the community - attribute is not sent to the neighbor. When ``bgp config-type cisco`` is - specified, the community attribute is not sent to the neighbor by default. - To send the community attribute user has to specify - :clicmd:`neighbor A.B.C.D send-community` like so: +.. index:: [no] advertise-pip [ip [mac ]] +.. clicmd:: [no] advertise-pip [ip [mac ]] - .. code-block:: frr +Enables or disables advertise-pip feature, specifiy system-IP and/or system-MAC +parameters. - ! - router bgp 1 - neighbor 10.0.0.1 remote-as 1 - address-family ipv4 unicast - no neighbor 10.0.0.1 send-community - exit-address-family - ! - router bgp 1 - neighbor 10.0.0.1 remote-as 1 - address-family ipv4 unicast - neighbor 10.0.0.1 send-community - exit-address-family - ! ++Support with VRF network namespace backend ++^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +It is possible to separate overlay networks contained in VXLAN interfaces from +underlay networks by using VRFs. VRF-lite and VRF-netns backends can be used for +that. In the latter case, it is necessary to set both bridge and vxlan interface +in the same network namespace, as below example illustrates: -.. deprecated:: 5.0 - Please transition to using the FRR specific syntax for your configuration. +.. code-block:: shell -.. index:: bgp config-type zebra -.. clicmd:: bgp config-type zebra + # linux shell + ip netns add vrf1 + ip link add name vxlan101 type vxlan id 101 dstport 4789 dev eth0 local 10.1.1.1 + ip link set dev vxlan101 netns vrf1 + ip netns exec vrf1 ip link set dev lo up + ip netns exec vrf1 brctl addbr bridge101 + ip netns exec vrf1 brctl addif bridge101 vxlan101 - FRR style BGP configuration. This is the default. +This makes it possible to separate not only layer 3 networks like VRF-lite networks. +Also, VRF netns based make possible to separate layer 2 networks on separate VRF +instances. .. _bgp-debugging: @@ -1998,6 +2644,12 @@ Debugging Show all enabled debugs. +.. index:: show bgp listeners +.. clicmd:: show bgp listeners + + Display Listen sockets and the vrf that created them. Useful for debugging of when + listen is not working and this is considered a developer debug statement. + .. index:: [no] debug bgp neighbor-events .. clicmd:: [no] debug bgp neighbor-events @@ -2094,21 +2746,61 @@ Dumping Messages and Routing Tables Other BGP Commands ------------------ +The following are available in the top level *enable* mode: + +.. index:: clear bgp \* +.. clicmd:: clear bgp \* + + Clear all peers. + .. index:: clear bgp ipv4|ipv6 \* .. clicmd:: clear bgp ipv4|ipv6 \* - Clear all address family peers. + Clear all peers with this address-family activated. + +.. index:: clear bgp ipv4|ipv6 unicast \* +.. clicmd:: clear bgp ipv4|ipv6 unicast \* + + Clear all peers with this address-family and sub-address-family activated. .. index:: clear bgp ipv4|ipv6 PEER .. clicmd:: clear bgp ipv4|ipv6 PEER - Clear peers which have addresses of X.X.X.X + Clear peers with address of X.X.X.X and this address-family activated. + +.. index:: clear bgp ipv4|ipv6 unicast PEER +.. clicmd:: clear bgp ipv4|ipv6 unicast PEER + + Clear peer with address of X.X.X.X and this address-family and sub-address-family activated. + +.. index:: clear bgp ipv4|ipv6 PEER soft|in|out +.. clicmd:: clear bgp ipv4|ipv6 PEER soft|in|out + + Clear peer using soft reconfiguration in this address-family. -.. index:: clear bgp ipv4|ipv6 PEER soft in -.. clicmd:: clear bgp ipv4|ipv6 PEER soft in +.. index:: clear bgp ipv4|ipv6 unicast PEER soft|in|out +.. clicmd:: clear bgp ipv4|ipv6 unicast PEER soft|in|out - Clear peer using soft reconfiguration. + Clear peer using soft reconfiguration in this address-family and sub-address-family. +The following are available in the ``router bgp`` mode: + +.. index:: write-quanta (1-64) +.. clicmd:: write-quanta (1-64) + + BGP message Tx I/O is vectored. This means that multiple packets are written + to the peer socket at the same time each I/O cycle, in order to minimize + system call overhead. This value controls how many are written at a time. + Under certain load conditions, reducing this value could make peer traffic + less 'bursty'. In practice, leave this settings on the default (64) unless + you truly know what you are doing. + +.. index:: read-quanta (1-10) +.. clicmd:: read-quanta (1-10) + + Unlike Tx, BGP Rx traffic is not vectored. Packets are read off the wire one + at a time in a loop. This setting controls how many iterations the loop runs + for. As with write-quanta, it is best to leave this setting on the default. .. _bgp-displaying-bgp-information: @@ -2122,17 +2814,17 @@ daemon project, while :clicmd:`show bgp` command is the new format. The choice has been done to keep old format with IPv4 routing table, while new format displays IPv6 routing table. -.. index:: show ip bgp -.. clicmd:: show ip bgp +.. index:: show ip bgp [wide] +.. clicmd:: show ip bgp [wide] -.. index:: show ip bgp A.B.C.D -.. clicmd:: show ip bgp A.B.C.D +.. index:: show ip bgp A.B.C.D [wide] +.. clicmd:: show ip bgp A.B.C.D [wide] -.. index:: show bgp -.. clicmd:: show bgp +.. index:: show bgp [wide] +.. clicmd:: show bgp [wide] -.. index:: show bgp X:X::X:X -.. clicmd:: show bgp X:X::X:X +.. index:: show bgp X:X::X:X [wide] +.. clicmd:: show bgp X:X::X:X [wide] These commands display BGP routes. When no route is specified, the default is to display all BGP routes. @@ -2148,6 +2840,12 @@ displays IPv6 routing table. Total number of prefixes 1 + If _wide_ option is specified, then the prefix table's width is increased + to fully display the prefix and the nexthop. + + This is especially handy dealing with IPv6 prefixes and + if :clicmd:`[no] bgp default show-nexthop-hostname` is enabled. + Some other commands provide additional options for filtering the output. .. index:: show [ip] bgp regexp LINE @@ -2174,7 +2872,14 @@ structure is extended with :clicmd:`show bgp [afi] [safi]`. These commands display BGP routes for the specific routing table indicated by the selected afi and the selected safi. If no afi and no safi value is given, - the command falls back to the default IPv6 routing table + the command falls back to the default IPv6 routing table. + For EVPN prefixes, you can display the full BGP table for this AFI/SAFI + using the standard `show bgp [afi] [safi]` syntax. + +.. index:: show bgp l2vpn evpn route [type ] +.. clicmd:: show bgp l2vpn evpn route [type ] + + Additionally, you can also filter this output by route type. .. index:: show bgp [afi] [safi] summary .. clicmd:: show bgp [afi] [safi] summary @@ -2182,6 +2887,18 @@ structure is extended with :clicmd:`show bgp [afi] [safi]`. Show a bgp peer summary for the specified address family, and subsequent address-family. +.. index:: show bgp [afi] [safi] summary failed [json] +.. clicmd:: show bgp [afi] [safi] summary failed [json] + + Show a bgp peer summary for peers that are not succesfully exchanging routes + for the specified address family, and subsequent address-family. + +.. index:: show bgp [afi] [safi] summary established [json] +.. clicmd:: show bgp [afi] [safi] summary established [json] + + Show a bgp peer summary for peers that are succesfully exchanging routes + for the specified address family, and subsequent address-family. + .. index:: show bgp [afi] [safi] neighbor [PEER] .. clicmd:: show bgp [afi] [safi] neighbor [PEER] @@ -2199,6 +2916,16 @@ structure is extended with :clicmd:`show bgp [afi] [safi]`. Display flap statistics of routes of the selected afi and safi selected. +.. index:: show bgp [afi] [safi] statistics +.. clicmd:: show bgp [afi] [safi] statistics + + Display statistics of routes of the selected afi and safi. + +.. index:: show bgp statistics-all +.. clicmd:: show bgp statistics-all + + Display statistics of routes of all the afi and safi. + .. _bgp-display-routes-by-community: Displaying Routes by Community Attribute @@ -2231,8 +2958,49 @@ attribute. match the specified community list. When `exact-match` is specified, it displays only routes that have an exact match. +.. _bgp-display-routes-by-lcommunity: + +Displaying Routes by Large Community Attribute +---------------------------------------------- + +The following commands allow displaying routes based on their +large community attribute. + +.. index:: show [ip] bgp large-community +.. clicmd:: show [ip] bgp large-community + +.. index:: show [ip] bgp large-community LARGE-COMMUNITY +.. clicmd:: show [ip] bgp large-community LARGE-COMMUNITY + +.. index:: show [ip] bgp large-community LARGE-COMMUNITY exact-match +.. clicmd:: show [ip] bgp large-community LARGE-COMMUNITY exact-match + +.. index:: show [ip] bgp large-community LARGE-COMMUNITY json +.. clicmd:: show [ip] bgp large-community LARGE-COMMUNITY json + + These commands display BGP routes which have the large community attribute. + attribute. When ``LARGE-COMMUNITY`` is specified, BGP routes that match that + large community are displayed. When `exact-match` is specified, it display + only routes that have an exact match. When `json` is specified, it display + routes in json format. + +.. index:: show [ip] bgp large-community-list WORD +.. clicmd:: show [ip] bgp large-community-list WORD + +.. index:: show [ip] bgp large-community-list WORD exact-match +.. clicmd:: show [ip] bgp large-community-list WORD exact-match + +.. index:: show [ip] bgp large-community-list WORD json +.. clicmd:: show [ip] bgp large-community-list WORD json + + These commands display BGP routes for the address family specified that + match the specified large community list. When `exact-match` is specified, + it displays only routes that have an exact match. When `json` is specified, + it display routes in json format. + .. _bgp-display-routes-by-as-path: + Displaying Routes by AS Path ---------------------------- @@ -2258,6 +3026,23 @@ Displaying Routes by AS Path Print a summary of neighbor connections for the specified AFI/SAFI combination. +Displaying Update Group Information +----------------------------------- + +.. index:: show bgp update-groups SUBGROUP-ID [advertise-queue|advertised-routes|packet-queue] +.. clicmd:: show bgp update-groups [advertise-queue|advertised-routes|packet-queue] + + Display Information about each individual update-group being used. + If SUBGROUP-ID is specified only display about that particular group. If + advertise-queue is specified the list of routes that need to be sent + to the peers in the update-group is displayed, advertised-routes means + the list of routes we have sent to the peers in the update-group and + packet-queue specifies the list of packets in the queue to be sent. + +.. index:: show bgp update-groups statistics +.. clicmd:: show bgp update-groups statistics + + Display Information about update-group events in FRR. .. _bgp-route-reflector: @@ -2298,7 +3083,6 @@ different filter for a peer. .. code-block:: frr - bgp multiple-instance ! router bgp 1 view 1 neighbor 10.0.0.1 remote-as 2 @@ -2446,8 +3230,8 @@ certainly contains silly mistakes, if not serious flaws. ip prefix-list pl-peer2-network permit 192.168.2.0/24 ip prefix-list pl-peer2-network permit 172.16.1/24 ! - ip as-path access-list asp-own-as permit ^$ - ip as-path access-list asp-own-as permit _64512_ + bgp as-path access-list asp-own-as permit ^$ + bgp as-path access-list asp-own-as permit _64512_ ! ! ################################################################# ! Match communities we provide actions for, on routes receives from @@ -2461,26 +3245,26 @@ certainly contains silly mistakes, if not serious flaws. ! 2X00 - set local_preference to X00 ! ! blackhole the prefix of the route - ip community-list standard cm-blackhole permit 64512:100 + bgp community-list standard cm-blackhole permit 64512:100 ! ! set no-export community before advertising - ip community-list standard cm-set-no-export permit 64512:200 + bgp community-list standard cm-set-no-export permit 64512:200 ! ! advertise only to other customers - ip community-list standard cm-cust-only permit 64512:300 + bgp community-list standard cm-cust-only permit 64512:300 ! ! advertise only to upstreams - ip community-list standard cm-upstream-only permit 64512:400 + bgp community-list standard cm-upstream-only permit 64512:400 ! ! advertise to upstreams with no-export - ip community-list standard cm-upstream-noexport permit 64512:500 + bgp community-list standard cm-upstream-noexport permit 64512:500 ! ! set local-pref to least significant 3 digits of the community - ip community-list standard cm-prefmod-100 permit 64512:2100 - ip community-list standard cm-prefmod-200 permit 64512:2200 - ip community-list standard cm-prefmod-300 permit 64512:2300 - ip community-list standard cm-prefmod-400 permit 64512:2400 - ip community-list expanded cme-prefmod-range permit 64512:2... + bgp community-list standard cm-prefmod-100 permit 64512:2100 + bgp community-list standard cm-prefmod-200 permit 64512:2200 + bgp community-list standard cm-prefmod-300 permit 64512:2300 + bgp community-list standard cm-prefmod-400 permit 64512:2400 + bgp community-list expanded cme-prefmod-range permit 64512:2... ! ! Informational communities ! @@ -2488,9 +3272,9 @@ certainly contains silly mistakes, if not serious flaws. ! 3100 - learned from customer ! 3200 - learned from peer ! - ip community-list standard cm-learnt-upstream permit 64512:3000 - ip community-list standard cm-learnt-cust permit 64512:3100 - ip community-list standard cm-learnt-peer permit 64512:3200 + bgp community-list standard cm-learnt-upstream permit 64512:3000 + bgp community-list standard cm-learnt-cust permit 64512:3100 + bgp community-list standard cm-learnt-peer permit 64512:3200 ! ! ################################################################### ! Utility route-maps @@ -2629,6 +3413,8 @@ Example of how to set up a 6-Bone connection. .. include:: rpki.rst +.. include:: wecmp_linkbw.rst + .. include:: flowspec.rst .. [#med-transitivity-rant] For some set of objects to have an order, there *must* be some binary ordering relation that is defined for *every* combination of those objects, and that relation *must* be transitive. I.e.:, if the relation operator is <, and if a < b and b < c then that relation must carry over and it *must* be that a < c for the objects to have an order. The ordering relation may allow for equality, i.e. a < b and b < a may both be true and imply that a and b are equal in the order and not distinguished by it, in which case the set has a partial order. Otherwise, if there is an order, all the objects have a distinct place in the order and the set has a total order) diff --git a/doc/user/bmp.rst b/doc/user/bmp.rst new file mode 100644 index 0000000000..061800c14e --- /dev/null +++ b/doc/user/bmp.rst @@ -0,0 +1,170 @@ +.. _bmp: + +*** +BMP +*** + +:abbr:`BMP` (BGP Monitoring Protocol, :rfc:`7854`) is used to send monitoring +data from BGP routers to network management entities. + +Implementation characteristics +============================== + +The `BMP` implementation in FRR has the following properties: + +- only the :rfc:`7854` features are currently implemented. This means protocol + version 3 without any extensions. It is not possible to use an older draft + protocol version of BMP. + +- the following statistics codes are implemented: + + - 0: count of prefixes rejected + - 2: count of duplicate prefix withdrawals + - 3: count of **prefixes** with loop in cluster id + - 4: count of **prefixes** with loop in AS-path + - 5: count of **prefixes** with loop in originator + - 11: count of updates subjected to :rfc:`7607` "treat as withdrawal" + handling due to errors + - 65531: *experimental* count of prefixes rejected due to invalid next-hop + + Note that stat items 3, 4 and 5 are specified to count updates, but FRR + implements them as prefix-based counters. + +- **route mirroring** is fully implemented, however BGP OPEN messages are not + currently included in route mirroring messages. Their contents can be + extracted from the "peer up" notification for sessions that established + successfully. OPEN messages for failed sessions cannot currently be + mirrored. + +- **route monitoring** is available for IPv4 and IPv6 AFIs, unicast and + multicast SAFIs. Other SAFIs (VPN, Labeled-Unicast, Flowspec, etc.) are not + currently supported. + +- monitoring peers that have BGP **add-path** enabled on the session will + result in somewhat unpredictable behaviour. Currently, the outcome is: + + - route mirroring functions as intended, messages are copied verbatim + - the add-path ID is never included in route monitoring messages + - if multiple paths were received from a peer, an unpredictable path is + picked and sent on the BMP session. The selection will differ for + pre-policy and post-policy monitoring sessions. + - as long as any path is present, something will be advertised on BMP + sessions. Only after the last path is gone a withdrawal will be sent on + BMP sessions. + - updates to additional paths will trigger BMP route monitoring messages. + There is no guarantee on consistency regarding which path is sent in these + messages. + +- monitoring peers with :rfc:`5549` extended next-hops has not been tested. + +Starting BMP +============ + +BMP is implemented as a loadable module. This means that to use BMP, ``bgpd`` +must be started with the ``-M bmp`` option. It is not possible to enable BMP +if ``bgpd`` was started without this option. + +Configuring BMP +=============== + +All of FRR's BMP configuration options are located inside the +:clicmd:`router bgp ASN` block. Configure BGP first before proceeding to BMP +setup. + +There is one option that applies to the BGP instance as a whole: + +.. index:: bmp mirror buffer-limit(0-4294967294) +.. clicmd:: [no] bmp mirror buffer-limit(0-4294967294) + + This sets the maximum amount of memory used for buffering BGP messages + (updates, keepalives, ...) for sending in BMP Route Mirroring. + + The buffer is for the entire BGP instance; if multiple BMP targets are + configured they reference the same buffer and do not consume additional + memory. Queue overhead is included in accounting this memory, so the + actual space available for BGP messages is slightly less than the value + configured here. + + If the buffer fills up, the oldest messages are removed from the buffer and + any BMP sessions where the now-removed messages were still pending have + their **entire** queue flushed and a "Mirroring Messages Lost" BMP message + is sent. + + BMP Route Monitoring is not affected by this option. + +All other configuration is managed per targets: + +.. index:: bmp targets NAME +.. clicmd:: [no] bmp targets NAME + + Create/delete a targets group. As implied by the plural name, targets may + cover multiple outbound active BMP sessions as well as inbound passive + listeners. + + If BMP sessions have the same configuration, putting them in the same + ``bmp targets`` will reduce overhead. + +BMP session configuration +------------------------- + +Inside a ``bmp targets`` block, the following commands control session +establishment: + +.. index:: bmp connect HOSTNAME port (1-65535) {min-retry MSEC|max-retry MSEC} +.. clicmd:: [no] bmp connect HOSTNAME port (1-65535) {min-retry MSEC|max-retry MSEC} + + Add/remove an active outbound BMP session. HOSTNAME is resolved via DNS, + if multiple addresses are returned they are tried in nondeterministic + order. Only one connection will be established even if multiple addresses + are returned. ``min-retry`` and ``max-retry`` specify (in milliseconds) + bounds for exponential backoff. + +.. warning:: + + ``ip access-list`` and ``ipv6 access-list`` are checked for outbound + connections resulting from ``bmp connect`` statements. + +.. index:: bmp listener port (1-65535) +.. clicmd:: [no] bmp listener port (1-65535) + + Accept incoming BMP sessions on the specified address and port. You can + use ``0.0.0.0`` and ``::`` to listen on all IPv4/IPv6 addresses. + +.. clicmd:: [no] ip access-list NAME +.. clicmd:: [no] ipv6 access-list NAME + + Restrict BMP sessions to the addresses allowed by the respective access + lists. The access lists are checked for both passive and active BMP + sessions. Changes do not affect currently established sessions. + +BMP data feed configuration +--------------------------- + +The following commands configure what BMP messages are sent on sessions +associated with a particular ``bmp targets``: + +.. index:: bmp stats [interval (100-86400000)] +.. clicmd:: [no] bmp stats [interval (100-86400000)] + + Send BMP Statistics (counter) messages at the specified interval (in + milliseconds.) + +.. index:: bmp monitor AFI SAFI +.. clicmd:: [no] bmp monitor AFI SAFI + + Perform Route Monitoring for the specified AFI and SAFI. Only IPv4 and + IPv6 are currently valid for AFI, and only unicast and multicast are valid + for SAFI. Other AFI/SAFI combinations may be added in the future. + + All BGP neighbors are included in Route Monitoring. Options to select + a subset of BGP sessions may be added in the future. + +.. index:: bmp mirror +.. clicmd:: [no] bmp mirror + + Perform Route Mirroring for all BGP neighbors. Since this provides a + direct feed of BGP messages, there are no AFI/SAFI options to be + configured. + + All BGP neighbors are included in Route Mirroring. Options to select + a subset of BGP sessions may be added in the future. diff --git a/doc/user/conf.py b/doc/user/conf.py index 5582847431..1f6f050bcf 100644 --- a/doc/user/conf.py +++ b/doc/user/conf.py @@ -16,6 +16,7 @@ import os import re import pygments +import sphinx from sphinx.highlighting import lexers # If extensions (or modules to document with autodoc) are in another directory, @@ -132,7 +133,8 @@ # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build', 'rpki.rst', 'routeserver.rst', - 'ospf_fundamentals.rst', 'flowspec.rst', 'snmptrap.rst'] + 'ospf_fundamentals.rst', 'flowspec.rst', 'snmptrap.rst', + 'wecmp_linkbw.rst'] # The reST default role (used for this markup: `text`) to use for all # documents. @@ -358,14 +360,36 @@ with open('../extra/frrlexer.py', 'rb') as lex: frrlexerpy = lex.read() +# Parse version string into int array +def vparse(s): + a = [] + + for c in s: + if c != '.': + a.append(int(c)) + + while len(a) < 3: + a.append(0) + + return a[:3] + # custom extensions here def setup(app): # object type for FRR CLI commands, can be extended to document parent CLI # node later on app.add_object_type('clicmd', 'clicmd') + # css overrides for HTML theme - app.add_stylesheet('overrides.css') - app.add_javascript('overrides.js') + # Note sphinx version differences + sver = vparse(sphinx.__version__) + + if sver < vparse('1.8.0') : + app.add_stylesheet('overrides.css') + app.add_javascript('overrides.js') + else: + app.add_css_file('overrides.css') + app.add_js_file('overrides.js') + # load Pygments lexer for FRR config syntax # # NB: in Pygments 2.2+ this can be done with `load_lexer_from_file`, but we diff --git a/doc/user/eigrpd.rst b/doc/user/eigrpd.rst index 330267a8ee..915270a562 100644 --- a/doc/user/eigrpd.rst +++ b/doc/user/eigrpd.rst @@ -65,15 +65,16 @@ Certain signals have special meanings to *eigrpd*. EIGRP Configuration =================== -.. index:: router eigrp (1-65535) -.. clicmd:: router eigrp (1-65535) +.. index:: router eigrp (1-65535) [vrf NAME] +.. clicmd:: router eigrp (1-65535) [vrf NAME] The `router eigrp` command is necessary to enable EIGRP. To disable EIGRP, use the `no router eigrp (1-65535)` command. EIGRP must be enabled before - carrying out any of the EIGRP commands. + carrying out any of the EIGRP commands. Specify vrf NAME if you want + eigrp to work within the specified vrf. -.. index:: no router eigrp (1-65535) -.. clicmd:: no router eigrp (1-65535) +.. index:: no router eigrp (1-65535) [vrf NAME] +.. clicmd:: no router eigrp (1-65535) [vrf NAME] Disable EIGRP. @@ -189,8 +190,8 @@ How to Announce EIGRP route Show EIGRP Information ====================== -.. index:: show ip eigrp topology -.. clicmd:: show ip eigrp topology +.. index:: show ip eigrp [vrf NAME] topology +.. clicmd:: show ip eigrp [vrf NAME] topology Display current EIGRP status. @@ -207,6 +208,17 @@ Show EIGRP Information P 10.0.2.0/24, 1 successors, FD is 256256, serno: 0 via Connected, enp0s3 +.. index:: show ip eigrp [vrf NAME] interface +.. clicmd:: show ip eigrp [vrf NAME] interface + + Display the list of interfaces associated with a particular eigrp + instance. + +..index:: show ip eigrp [vrf NAME] neighbor +..clicmd:: show ip eigrp [vrf NAME] neighbor + + Display the list of neighbors that have been established within + a particular eigrp instance. EIGRP Debug Commands ==================== diff --git a/doc/user/filter.rst b/doc/user/filter.rst index 9d7361443d..98b768412d 100644 --- a/doc/user/filter.rst +++ b/doc/user/filter.rst @@ -9,11 +9,24 @@ defined, it can be applied in any direction. IP Access List ============== -.. index:: access-list NAME permit IPV4-NETWORK -.. clicmd:: access-list NAME permit IPV4-NETWORK +.. index:: access-list NAME [seq (1-4294967295)] permit IPV4-NETWORK +.. clicmd:: access-list NAME [seq (1-4294967295)] permit IPV4-NETWORK -.. index:: access-list NAME deny IPV4-NETWORK -.. clicmd:: access-list NAME deny IPV4-NETWORK +.. index:: access-list NAME [seq (1-4294967295)] deny IPV4-NETWORK +.. clicmd:: access-list NAME [seq (1-4294967295)] deny IPV4-NETWORK + + seq + seq `number` can be set either automatically or manually. In the + case that sequential numbers are set manually, the user may pick any + number less than 4294967295. In the case that sequential number are set + automatically, the sequential number will increase by a unit of five (5) + per list. If a list with no specified sequential number is created + after a list with a specified sequential number, the list will + automatically pick the next multiple of five (5) as the list number. + For example, if a list with number 2 already exists and a new list with + no specified number is created, the next list will be numbered 5. If + lists 2 and 7 already exist and a new list with no specified number is + created, the new list will be numbered 10. Basic filtering is done by `access-list` as shown in the following example. @@ -22,6 +35,7 @@ IP Access List access-list filter deny 10.0.0.0/9 access-list filter permit 10.0.0.0/8 + access-list filter seq 13 permit 10.0.0.0/7 IP Prefix List @@ -165,14 +179,8 @@ Showing ip prefix-list Clear counter of ip prefix-list ------------------------------- -.. index:: clear ip prefix-list -.. clicmd:: clear ip prefix-list +.. index:: clear ip prefix-list [NAME [A.B.C.D/M]] +.. clicmd:: clear ip prefix-list [NAME [A.B.C.D/M]] Clears the counters of all IP prefix lists. Clear IP Prefix List can be used - with a specified name and prefix. - -.. index:: clear ip prefix-list NAME -.. clicmd:: clear ip prefix-list NAME - -.. index:: clear ip prefix-list NAME A.B.C.D/M -.. clicmd:: clear ip prefix-list NAME A.B.C.D/M + with a specified NAME or NAME and prefix. diff --git a/doc/user/flowspec.rst b/doc/user/flowspec.rst index b274afe8a2..d3eb25a7c7 100644 --- a/doc/user/flowspec.rst +++ b/doc/user/flowspec.rst @@ -22,6 +22,9 @@ more or less complex combination of the following: - Layer 3 information: DSCP value, Protocol type, packet length, fragmentation. - Misc layer 4 TCP flags. +Note that if originally Flowspec defined IPv4 rules, this is also possible to use +IPv6 address-family. The same set of combinations as defined for IPv4 can be used. + A combination of the above rules is applied for traffic filtering. This is encoded as part of specific BGP extended communities and the action can range from the obvious rerouting (to nexthop or to separate VRF) to shaping, or @@ -31,6 +34,7 @@ The following IETF drafts and RFCs have been used to implement FRR Flowspec: - :rfc:`5575` - [Draft-IETF-IDR-Flowspec-redirect-IP]_ +- [Draft-IETF-IDR-Flow-Spec-V6]_ .. _design-principles-flowspec: @@ -108,9 +112,13 @@ As of today, it is only possible to configure Flowspec on the default VRF. router bgp neighbor remote-as + neighbor remote-as address-family ipv4 flowspec neighbor activate - exit + exit + address-family ipv6 flowspec + neighbor activate + exit exit You can see Flowspec entries, by using one of the following show commands: @@ -118,6 +126,8 @@ You can see Flowspec entries, by using one of the following show commands: .. index:: show bgp ipv4 flowspec [detail | A.B.C.D] .. clicmd:: show bgp ipv4 flowspec [detail | A.B.C.D] +.. index:: show bgp ipv6 flowspec [detail | A:B::C:D] +.. clicmd:: show bgp ipv6 flowspec [detail | A:B::C:D] Per-interface configuration ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -183,6 +193,28 @@ interfaces are created with private IP addressing scheme. exit exit +Similarly, it is possible to do the same for IPv6 flowspec rules, by using +an IPv6 extended community. The format is defined on :rfc:`5701`, and that +community contains an IPv6 address encoded in the attribute, and matches the +locally configured imported route target IPv6 defined under the appropriate +BGP VRF instance. Below example defines an IPv6 extended community containing +`E:F::G:H` address followed by 2 bytes chosen by admin ( here `JJ`). + +.. code-block:: frr + + router bgp + neighbor remote-as + address-family ipv6 flowspec + neighbor A:B::C:D activate + exit + exit + router bgp vrf vrf2 + address-family ipv6 unicast + rt6 redirect import + exit + exit + + Flowspec monitoring & troubleshooting ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -348,4 +380,5 @@ inside FRRouting. .. [Draft-IETF-IDR-Flowspec-redirect-IP] .. [Draft-IETF-IDR-Flowspec-Interface-Set] +.. [Draft-IETF-IDR-Flow-Spec-V6] .. [Presentation] diff --git a/doc/user/frr-reload.rst b/doc/user/frr-reload.rst new file mode 100644 index 0000000000..bd295dbbad --- /dev/null +++ b/doc/user/frr-reload.rst @@ -0,0 +1,41 @@ +.. _frr-reload: + + +The frr-reload.py script +======================== + +The ``frr-reload.py`` script attempts to update the configuration of running +daemons. It takes as argument the path of the configuration file that we want +to apply. The script will attempt to retrieve the running configuration from +daemons, calculate the delta between that config and the intended one, and +execute the required sequence of vtysh commands to enforce the changes. + +Options +------- + +There are several options that control the behavior of ``frr-reload``: + +* ``--input INPUT``: uses the specified input file as the running configuration + instead of retrieving it from a ``show running-config`` in vtysh +* ``--reload``: applies the configuration delta to the daemons. Either this or + ``--test`` MUST be specified. +* ``--test``: only outputs the configuration delta, without enforcing it. + Either this or ``--reload`` MUST be specified. +* ``--debug``: enable debug messages +* ``--stdout``: print output to stdout +* ``--bindir BINDIR``: path to the vtysh executable +* ``--confdir CONFDIR``: path to the existing daemon config files +* ``--rundir RUNDIR``: path to a folder to be used to write the temporary files + needed by the script to do its job. The script should have write access to it +* ``--daemon DAEMON``: by default ``frr-reload.py`` assumes that we are using + integrated config and attempting to update the configuration for all daemons. + If this is not the case, e.g. each daemon has its individual config file, + then the delta can only be computed on a per-daemon basis. This option allows + the user to specify the daemon for which the config is intended. DAEMON + should be one of the keywords allowed in vtysh as an option for ``show + running-config``. +* ``--vty_socket VTY_SOCKET``: the socket to be used by vtysh to connect to the + running daemons. +* ``--overwrite``: overwrite the existing daemon config file with the new + config after the delta has been applied. The file name will be ``frr.conf`` + for integrate config, or ``DAEMON.conf`` when using per-daemon config files. diff --git a/doc/user/grpc.rst b/doc/user/grpc.rst new file mode 100644 index 0000000000..d6767fc866 --- /dev/null +++ b/doc/user/grpc.rst @@ -0,0 +1,66 @@ +.. _grpc: + +*************** +Northbound gRPC +*************** + +.. program:: configure + +*gRPC* provides a combined front end to all FRR daemons using the YANG +northbound. It is currently disabled by default due its experimental +stage, but it can be enabled with :option:`--enable-grpc` option in the +configure script. + + +.. _grpc-features: + +Northbound gRPC Features +======================== + +* Get/set configuration using JSON/XML/XPath encondings. +* Execute YANG RPC calls. +* Lock/unlock configuration. +* Create/edit/load/update/commit candidate configuration. +* List/get transactions. + + +.. note:: + + There is currently no support for YANG notifications. + + +.. note:: + + You can find more information on how to code programs to interact + with FRR by reading the gRPC Programming Language Bindings section + in the `developer's documentation + `_. + + +.. _grpc-config: + +Daemon gRPC Configuration +========================= + +The *gRPC* module accepts the following run time option: + +- ``port``: the port to listen to (defaults to ``50051``). + + +.. note:: + + At the moment only localhost connections with no SSL/TLS are + supported. + + +To configure FRR daemons to listen to gRPC you need to append the +following parameter to the daemon's command line: ``-M grpc`` +(optionally ``-M grpc:PORT`` to specify listening port). + +To do that in production you need to edit the ``/etc/frr/daemons`` file +so the daemons get started with the command line argument. Example: + +:: + + # other daemons... + bfdd_options=" --daemon -A 127.0.0.1 -M grpc" diff --git a/doc/user/index.rst b/doc/user/index.rst index 4c218c6580..8ac997f8dd 100644 --- a/doc/user/index.rst +++ b/doc/user/index.rst @@ -23,6 +23,7 @@ Basics basic vtysh + grpc filter routemap ipv6 @@ -56,6 +57,9 @@ Protocols sharp static vnc + vrrp + bmp + watchfrr ######## Appendix @@ -68,6 +72,7 @@ Appendix bugs packet-dumps glossary + frr-reload ################ Copyright notice diff --git a/doc/user/installation.rst b/doc/user/installation.rst index 964297292f..bab4bdcb39 100644 --- a/doc/user/installation.rst +++ b/doc/user/installation.rst @@ -31,8 +31,11 @@ From Source Building FRR from source is the best way to ensure you have the latest features and bug fixes. Details for each supported platform, including dependency -package listings, permissions, and other gotchas, are in the developer's -documentation. This section provides a brief overview on the process. +package listings, permissions, and other gotchas, are in the `developer's +documentation +`_. This +section provides a brief overview on the process. + Getting the Source ^^^^^^^^^^^^^^^^^^ @@ -144,11 +147,21 @@ options from the list below. Build watchfrr with systemd integration, this will allow FRR to communicate with systemd to tell systemd if FRR has come up properly. +.. option:: --enable-werror + + Build with all warnings converted to errors as a compile option. This + is recommended for developers only. + .. option:: --disable-pimd Turn off building of pimd. On some BSD platforms pimd will not build properly due to lack of kernel support. +.. option:: --disable-vrrpd + + Turn off building of vrrpd. Linux is required for vrrpd support; + other platforms are not supported. + .. option:: --disable-pbrd Turn off building of pbrd. This daemon currently requires linux in order to function @@ -176,6 +189,10 @@ options from the list below. Turn off bgpd's ability to use VNC. +.. option:: --disable-bgp-bmp + + Turn off BGP BMP support + .. option:: --enable-datacenter Enable system defaults to work as if in a Data Center. See defaults.h @@ -194,11 +211,6 @@ options from the list below. Disable building of the example OSPF-API client. -.. option:: --disable-ospf-ri - - Disable support for OSPF Router Information (RFC4970 & RFC5088) this - requires support for Opaque LSAs and Traffic Engineering. - .. option:: --disable-isisd Do not build isisd. @@ -211,16 +223,17 @@ options from the list below. Enable IS-IS topology generator. -.. option:: --enable-isis-te - - Enable Traffic Engineering Extension for ISIS (RFC5305) - .. option:: --enable-realms Enable the support of Linux Realms. Convert tag values from 1-255 into a realm value when inserting into the Linux kernel. Then routing policy can be assigned to the realm. See the tc man page. +.. option:: --disable-irdp + + Disable IRDP server support. This is enabled by default if we have + both `struct in_pktinfo` and `struct icmphdr` available to us. + .. option:: --disable-rtadv Disable support IPV6 router advertisement in zebra. @@ -245,12 +258,6 @@ options from the list below. mind. Specifically turn on -g3 -O0 for compiling options and add inclusion of grammar sandbox. -.. option:: --enable-fuzzing - - Turn on some compile options to allow you to run fuzzing tools against the - system. This flag is intended as a developer only tool and should not be - used for normal operations. - .. option:: --disable-snmp Build without SNMP support. @@ -269,6 +276,18 @@ options from the list below. With this option, we provide a way to strip out these characters for APK dev package builds. +..option:: --disable-version-build-config + + Remove the "configuerd with" field that has all of the build configuration + arguments when reporting the version string in `show version` command. + +..option:: --with-pkg-extra-version=VER + Add extra version field, for packagers/distributions + +..option:: --with-pkg-git-version + + Add git information to MOTD and build version string + .. option:: --enable-multipath=X Compile FRR with up to X way ECMP supported. This number can be from 0-999. @@ -306,6 +325,39 @@ options from the list below. Build the Sysrepo northbound plugin. +.. option:: --enable-grpc + + Enable the gRPC northbound plugin. + +.. option:: --enable-zeromq + + Enable the ZeroMQ handler. + +.. option:: --with-libpam + + Use libpam for PAM support in vtysh. + +.. option:: --enable-time-check XXX + + When this is enabled with a XXX value in microseconds, any thread that + runs for over this value will cause a warning to be issued to the log. + If you do not specify any value or don't include this option then + the default time is 5 seconds. If --disable-time-check is specified + then no warning is issued for any thread run length. + +.. option:: --disable-cpu-time + + Disable cpu process accounting, this command also disables the `show thread cpu` + command. If this option is disabled, --enable-time-check is ignored. This + disabling of cpu time effectively means that the getrusage call is skipped. + Since this is a process switch into the kernel, systems with high FRR + load might see improvement in behavior. Be aware that `show thread cpu` + is considered a good data gathering tool from the perspective of developers. + +.. option:: --enable-pcreposix + + Turn on the usage of PCRE Posix libs for regex functionality. + You may specify any combination of the above options to the configure script. By default, the executables are placed in :file:`/usr/local/sbin` and the configuration files in :file:`/usr/local/etc`. The :file:`/usr/local/` @@ -331,21 +383,26 @@ options to the configuration script. Look for YANG modules in `dir` [`prefix`/share/yang]. Note that the FRR YANG modules will be installed here. -.. option:: --with-libyang-pluginsdir +Python dependency, documentation and tests +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Look for libyang plugins in `dir` [`prefix`/lib/frr/libyang_plugins]. - Note that the FRR libyang plugins will be installed here. +FRR's documentation and basic unit tests heavily use code written in Python. +Additionally, FRR ships Python extensions written in C which are used during +its build process. - This option is meaningless with libyang 0.16.74 or newer and will be - removed once support for older libyang versions is dropped. +To this extent, FRR needs the following: -When it's desired to run FRR without installing it in the system, it's possible -to configure it as follows to look for YANG modules and libyang plugins in the -compile directory: -.. code-block:: shell +* an installation of CPython, preferably version 3.2 or newer (2.7 works but + is end of life and will stop working at some point.) +* development files (mostly headers) for that version of CPython +* an installation of `sphinx` for that version of CPython, to build the + documentation +* an installation of `pytest` for that version of CPython, to run the unit + tests - ./configure --with-libyang-pluginsdir="`pwd`/yang/libyang_plugins/.libs" \ - --with-yangmodelsdir="`pwd`/yang" +The `sphinx` and `pytest` dependencies can be avoided by not building +documentation / not running ``make check``, but the CPython dependency is a +hard dependency of the FRR build process (for the `clippy` tool.) .. _least-privilege-support: @@ -444,7 +501,8 @@ Additional kernel modules are also needed to support MPLS forwarding. mpls_router mpls_iptunnel - The following is an example to enable MPLS forwarding in the kernel: + The following is an example to enable MPLS forwarding in the + kernel, typically by editing :file:`/etc/sysctl.conf`: .. code-block:: shell @@ -467,34 +525,13 @@ Additional kernel modules are also needed to support MPLS forwarding. features can be found in http://schd.ws/hosted_files/ossna2017/fe/vrf-tutorial-oss.pdf. - The following impacts how BGP TCP sockets are managed across VRFs: - - .. code-block:: shell - - net.ipv4.tcp_l3mdev_accept=0 - - With this setting a BGP TCP socket is opened per VRF. This setting - ensures that other TCP services, such as SSH, provided for non-VRF - purposes are blocked from VRF associated Linux interfaces. - - .. code-block:: shell - - net.ipv4.tcp_l3mdev_accept=1 - - With this setting a single BGP TCP socket is shared across the - system. This setting exposes any TCP service running on the system, - e.g., SSH, to all VRFs. Generally this setting is not used in - environments where VRFs are used to support multiple administrative - groups. + A separate BGP TCP socket is opened per VRF. **Important note** as of June 2018, Kernel versions 4.14-4.18 have a known bug where VRF-specific TCP sockets are not properly handled. When running these kernel versions, if unable to establish any VRF BGP - adjacencies, either downgrade to 4.13 or set - 'net.ipv4.tcp_l3mdev_accept=1'. The fix for this issue is planned to be - included in future kernel versions. So upgrading your kernel may also - address this issue. - + adjacencies, downgrade to 4.13. The issue was fixed in 4.14.57, 4.17.9 + and more recent kernel versions. Building ^^^^^^^^ diff --git a/doc/user/ipv6.rst b/doc/user/ipv6.rst index cc8fd18fee..d1477ddcdb 100644 --- a/doc/user/ipv6.rst +++ b/doc/user/ipv6.rst @@ -76,6 +76,43 @@ Router Advertisement advertisements from the interface, in milliseconds. Default: ``600000`` +.. index:: + single: ipv6 nd ra-fast-retrans + single: no ipv6 nd ra-fast-retrans +.. clicmd:: [no] ipv6 nd ra-fast-retrans + + RFC4861 states that consecutive RA packets should be sent no more + frequently than three seconds apart. FRR by default allows faster + transmissions of RA packets in order to speed convergence and + neighbor establishment, particularly for unnumbered peering. By + turning off ipv6 nd ra-fast-retrans, the implementation is + compliant with the RFC at the cost of slower convergence + and neighbor establishment. + Default: enabled + +.. index:: + single: ipv6 nd ra-retrans-interval (0-4294967295) + single: no ipv6 nd retrans-interval [(0-4294967295)] +.. clicmd:: [no] ipv6 nd ra-retrans-interval [(0-4294967295)] + + The value to be placed in the retrans timer field of router advertisements + sent from the interface, in msec. Indicates the interval between router + advertisement retransmissions. Setting the value to zero indicates that + the value is unspecified by this router. Must be between zero or 4294967295 + msec. + Default: ``0`` + +.. index:: + single: ipv6 nd ra-hop-limit (0-255) + single: no ipv6 nd ra-hop-limit [(0-255)] +.. clicmd:: [no] ipv6 nd ra-hop-limit [(0-255)] + + The value to be placed in the hop count field of router advertisements sent + from the interface, in hops. Indicates the maximum diameter of the network. + Setting the value to zero indicates that the value is unspecified by this + router. Must be between zero or 255 hops. + Default: ``64`` + .. index:: single: ipv6 nd ra-lifetime (0-9000) single: no ipv6 nd ra-lifetime [(0-9000)] diff --git a/doc/user/isisd.rst b/doc/user/isisd.rst index f7607a54f7..8cbbe0809f 100644 --- a/doc/user/isisd.rst +++ b/doc/user/isisd.rst @@ -33,8 +33,8 @@ ISIS router To start the ISIS process you have to specify the ISIS router. As of this writing, *isisd* does not support multiple ISIS processes. -.. index:: [no] router isis WORD -.. clicmd:: [no] router isis WORD +.. index:: [no] router isis WORD [vrf NAME] +.. clicmd:: [no] router isis WORD [vrf NAME] Enable or disable the ISIS process by specifying the ISIS domain with 'WORD'. *isisd* does not yet support multiple ISIS processes but you must @@ -111,6 +111,12 @@ writing, *isisd* does not support multiple ISIS processes. Enable or disable :rfc:`6232` purge originator identification. +.. index:: [no] lsp-mtu (128-4352) +.. clicmd:: [no] lsp-mtu (128-4352) + + Configure the maximum size of generated LSPs, in bytes. + + .. _isis-timer: ISIS Timer @@ -196,8 +202,8 @@ ISIS interface .. _ip-router-isis-word: -.. index:: [no] router isis WORD -.. clicmd:: [no] router isis WORD +.. index:: [no] router isis WORD [vrf NAME] +.. clicmd:: [no] router isis WORD [vrf NAME] Activate ISIS adjacency on this interface. Note that the name of ISIS instance must be the same as the one used to configure the ISIS process (see @@ -412,8 +418,8 @@ Showing ISIS information Show topology IS-IS paths to Intermediate Systems, globally, in area (level-1) or domain (level-2). -.. index:: show ip route isis -.. clicmd:: show ip route isis +.. index:: show isis route [level-1|level-2] +.. clicmd:: show isis route [level-1|level-2] Show the ISIS routing table, as determined by the most recent SPF calculation. @@ -423,6 +429,12 @@ Showing ISIS information Traffic Engineering =================== +.. note:: + + At this time, FRR offers partial support for some of the routing + protocol extensions that can be used with MPLS-TE. FRR does not + support a complete RSVP-TE solution currently. + .. index:: mpls-te on .. clicmd:: mpls-te on @@ -458,6 +470,65 @@ Traffic Engineering .. _debugging-isis: +Segment Routing +=============== + +This is an EXPERIMENTAL support of Segment Routing as per RFC8667 +for MPLS dataplane. It supports IPv4, IPv6 and ECMP and has been +tested against Cisco & Juniper routers. + +Known limitations: + - No support for level redistribution (L1 to L2 or L2 to L1) + - No support for binding SID + - No support for SRMS + - No support for SRLB + - Only one SRGB and default SPF Algorithm is supported + +.. index:: [no] segment-routing on +.. clicmd:: [no] segment-routing on + + Enable Segment Routing. + +.. index:: [no] segment-routing global-block (0-1048575) (0-1048575) +.. clicmd:: [no] segment-routing global-block (0-1048575) (0-1048575) + + Set the Segment Routing Global Block i.e. the label range used by MPLS + to store label in the MPLS FIB for Prefix SID. Note that the block size + may not exceed 65535. + +.. index:: [no] segment-routing local-block (0-1048575) (0-1048575) +.. clicmd:: [no] segment-routing local-block (0-1048575) (0-1048575) + + Set the Segment Routing Local Block i.e. the label range used by MPLS + to store label in the MPLS FIB for Adjacency SID. Note that the block size + may not exceed 65535. + +.. index:: [no] segment-routing node-msd (1-16) +.. clicmd:: [no] segment-routing node-msd (1-16) + + Set the Maximum Stack Depth supported by the router. The value depend of the + MPLS dataplane. E.g. for Linux kernel, since version 4.13 the maximum value + is 32. + +.. index:: [no] segment-routing prefix [no-php-flag|explicit-null] +.. clicmd:: [no] segment-routing prefix `_ +https://git.alpinelinux.org/user/tteras/strongswan/log/?h=tteras-release and -`http://git.alpinelinux.org/cgit/user/tteras/strongswan/log/?h=tteras,working tree `_ +https://git.alpinelinux.org/user/tteras/strongswan/log/?h=tteras git repositories for the patches. .. _nhrp-events: @@ -134,10 +194,239 @@ git repositories for the patches. NHRP Events =========== -FIXME +.. index:: nhrp event socket SOCKET +.. clicmd:: nhrp event socket SOCKET + + Configure the Unix path for the event socket. + +.. _show-nhrp: + +Show NHRP +========== + +.. index:: show [ip|ipv6] nhrp cache [json] +.. clicmd:: show [ip|ipv6] nhrp cache [json] + + Dump the cache entries. + +.. index:: show [ip|ipv6] nhrp opennhrp [json] +.. clicmd:: show [ip|ipv6] nhrp opennhrp [json] + + Dump the cache entries with opennhrp format. + +.. index:: show [ip|ipv6] nhrp nhs [json] +.. clicmd:: show [ip|ipv6] nhrp nhs [json] + + Dump the hub context. + +.. index:: show dmvpn [json] +.. clicmd:: show dmvpn [json] + + Dump the security contexts. Configuration Example ===================== -FIXME +.. figure:: ../figures/fig_dmvpn_topologies.png + :alt: image + + image + +IPSec configurration example +---------------------------- + +This changes required on all nodes as HUB and Spokes. + +ipsec.conf file + +.. code-block:: shell + + config setup + conn dmvpn + authby=secret + auto=add + keyexchange=ikev2 + ike=aes256-aes256-sha256-modp2048 + esp=aes256-aes256-sha256-modp2048 + dpdaction=clear + dpddelay=300s + left=%any + leftid=%any + right=%any + rightid=%any + leftprotoport=gre + rightprotoport=gre + type=transport + keyingtries=%forever + +ipsec.secrets file + +.. code-block:: shell + + %any : PSK "some_s3cret!" + + +HUB configuration example +------------------------- + +Creating gre interface + +.. code-block:: console + + ip tunnel add gre1 mode gre key 42 ttl 64 + ip addr add 10.0.0.254/32 dev gre1 + ip link set gre1 up + +Adding iptables rules to provide possibility shortcut tunnels and connect spokes directly + +.. code-block:: shell + + iptables -A FORWARD -i gre1 -o gre1 \\ + -m hashlimit --hashlimit-upto 4/minute --hashlimit-burst 1 \\ + --hashlimit-mode srcip,dstip --hashlimit-srcmask 24 --hashlimit-dstmask 24 \\ + --hashlimit-name loglimit-0 -j NFLOG --nflog-group 1 --nflog-range 128 + +FRR config on HUB + +.. code-block:: frr + + nhrp nflog-group 1 + ! + interface gre1 + description DMVPN Tunnel Interface + ip address 10.0.0.254/32 + ip nhrp network-id 1 + ip nhrp redirect + ip nhrp registration no-unique + ip nhrp shortcut + tunnel protection vici profile dmvpn + tunnel source eth0 + ! + router bgp 65000 + bgp router-id 10.0.0.254 + no bgp ebgp-requires-policy + neighbor SPOKES peer-group + neighbor SPOKES disable-connected-check + neighbor 10.0.0.1 remote-as 65001 + neighbor 10.0.0.1 peer-group SPOKES + neighbor 10.0.0.2 remote-as 65002 + neighbor 10.0.0.2 peer-group SPOKES + neighbor 10.0.0.3 remote-as 65003 + neighbor 10.0.0.3 peer-group SPOKES + ! + address-family ipv4 unicast + network 172.16.0.0/24 + redistribute nhrp + exit-address-family + +Spoke1 configuration +-------------------- + +Creating gre interface + +.. code-block:: console + + ip tunnel add gre1 mode gre key 42 ttl 64 + ip addr add 10.0.0.1/32 dev gre1 + ip link set gre1 up + + +FRR config on Spoke1 + +.. code-block:: frr + + interface gre1 + description DMVPN Tunnel Interface + ip address 10.0.0.1/32 + ip nhrp network-id 1 + ip nhrp nhs dynamic nbma 198.51.100.1 + ip nhrp redirect + ip nhrp registration no-unique + ip nhrp shortcut + no link-detect + tunnel protection vici profile dmvpn + tunnel source eth0 + ! + router bgp 65001 + no bgp ebgp-requires-policy + neighbor 10.0.0.254 remote-as 65000 + neighbor 10.0.0.254 disable-connected-check + ! + address-family ipv4 unicast + network 172.16.1.0/24 + exit-address-family + + +Spoke2 configuration +-------------------- + +Creating gre interface + +.. code-block:: console + + ip tunnel add gre1 mode gre key 42 ttl 64 + ip addr add 10.0.0.1/32 dev gre1 + ip link set gre1 up + +FRR config on Spoke2 + +.. code-block:: frr + + interface gre1 + description DMVPN Tunnel Interface + ip address 10.0.0.2/32 + ip nhrp network-id 1 + ip nhrp nhs dynamic nbma 198.51.100.1 + ip nhrp redirect + ip nhrp registration no-unique + ip nhrp shortcut + no link-detect + tunnel protection vici profile dmvpn + tunnel source eth0 + ! + router bgp 65002 + no bgp ebgp-requires-policy + neighbor 10.0.0.254 remote-as 65000 + neighbor 10.0.0.254 disable-connected-check + ! + address-family ipv4 unicast + network 172.16.2.0/24 + exit-address-family + + +Spoke3 configuration +-------------------- + +Creating gre interface + +.. code-block:: console + + ip tunnel add gre1 mode gre key 42 ttl 64 + ip addr add 10.0.0.3/32 dev gre1 + ip link set gre1 up + +FRR config on Spoke3 + +.. code-block:: frr + + interface gre1 + description DMVPN Tunnel Interface + ip address 10.0.0.3/32 + ip nhrp network-id 1 + ip nhrp nhs dynamic nbma 198.51.100.1 + ip nhrp redirect + ip nhrp registration no-unique + ip nhrp shortcut + no link-detect + tunnel protection vici profile dmvpn + tunnel source eth0 + ! + router bgp 65003 + no bgp ebgp-requires-policy + neighbor 10.0.0.254 remote-as 65000 + neighbor 10.0.0.254 disable-connected-check + ! + address-family ipv4 unicast + network 172.16.3.0/24 + exit-address-family diff --git a/doc/user/ospf6d.rst b/doc/user/ospf6d.rst index 71bc047720..6295ba9293 100644 --- a/doc/user/ospf6d.rst +++ b/doc/user/ospf6d.rst @@ -15,13 +15,16 @@ OSPF6 router .. index:: router ospf6 .. clicmd:: router ospf6 -.. index:: router-id A.B.C.D -.. clicmd:: router-id A.B.C.D +.. index:: ospf6 router-id A.B.C.D +.. clicmd:: ospf6 router-id A.B.C.D Set router's Router-ID. -.. index:: interface IFNAME area AREA -.. clicmd:: interface IFNAME area AREA +.. index:: interface IFNAME area (0-4294967295) +.. clicmd:: interface IFNAME area (0-4294967295) + +.. index:: interface IFNAME area A.B.C.D +.. clicmd:: interface IFNAME area A.B.C.D Bind interface to specified area, and start sending OSPF packets. `area` can be specified as 0. @@ -128,6 +131,18 @@ OSPF6 interface Set explicitly network type for specified interface. +OSPF6 route-map +=============== + +Usage of *ospfd6*'s route-map support. + +.. index:: set metric [+|-](0-4294967295) +.. clicmd:: set metric [+|-](0-4294967295) + + Set a metric for matched route when sending announcement. Use plus (+) sign + to add a metric value to an existing metric. Use minus (-) sign to + substract a metric value from an existing metric. + .. _redistribute-routes-to-ospf6: Redistribute routes to OSPF6 @@ -195,7 +210,7 @@ Example of ospf6d configured on one interface and area: ipv6 ospf6 instance-id 0 ! router ospf6 - router-id 212.17.55.53 + ospf6 router-id 212.17.55.53 area 0.0.0.0 range 2001:770:105:2::/64 interface eth0 area 0.0.0.0 ! diff --git a/doc/user/ospf_fundamentals.rst b/doc/user/ospf_fundamentals.rst index 7db822c820..b0eb018107 100644 --- a/doc/user/ospf_fundamentals.rst +++ b/doc/user/ospf_fundamentals.rst @@ -24,7 +24,7 @@ Each router thus builds up an :abbr:`LSDB (Link State Database)` of all the link-state messages. From this collection of LSAs in the LSDB, each router can then calculate the shortest path to any other router, based on some common metric, by using an algorithm such as -`Edgar Djikstra's `_ +`Edsger Dijkstra's `_ :abbr:`SPF (Shortest Path First)` algorithm. .. index:: Link-state routing protocol advantages @@ -83,8 +83,8 @@ sharing a link, for example: - DR/BDR election results. - Any optional capabilities supported by each router. -The Hello protocol is comparatively trivial and will not be explored in greater -detail than here. +The Hello protocol is comparatively trivial and will not be explored in more +detail. .. index:: OSPF LSA overview .. _ospf-lsas: @@ -233,7 +233,7 @@ called `intra-area routes`. Cost The output cost of that interface, scaled inversely to some commonly known - reference value, :clicmd:`auto-cost reference-bandwidth (1-4294967`. + reference value, :clicmd:`auto-cost reference-bandwidth (1-4294967)`. Link Type Transit Network diff --git a/doc/user/ospfd.rst b/doc/user/ospfd.rst index 83e14d474f..010526b637 100644 --- a/doc/user/ospfd.rst +++ b/doc/user/ospfd.rst @@ -69,7 +69,7 @@ The instance number should be specified in the config when addressing a particul .. code-block:: frr router ospf 5 - router-id 1.2.3.4 + ospf router-id 1.2.3.4 area 0.0.0.0 authentication message-digest ... @@ -177,8 +177,8 @@ To start OSPF process you have to specify the OSPF router. OSPF (:ref:`redistribute-routes-to-ospf`). This is the only way to advertise non-OSPF links into stub areas. -.. index:: timers throttle spf DELAY INITIAL-HOLDTIME MAX-HOLDTIME -.. clicmd:: timers throttle spf DELAY INITIAL-HOLDTIME MAX-HOLDTIME +.. index:: timers throttle spf (0-600000) (0-600000) (0-600000) +.. clicmd:: timers throttle spf (0-600000) (0-600000) (0-600000) .. index:: no timers throttle spf .. clicmd:: no timers throttle spf @@ -310,6 +310,17 @@ To start OSPF process you have to specify the OSPF router. In some cases it may be more convenient to enable OSPF on a per interface/subnet basis (:clicmd:`ip ospf area AREA [ADDR]`). +.. index:: proactive-arp +.. clicmd:: proactive-arp + +.. index:: no proactive-arp +.. clicmd:: no proactive-arp + + This command enables or disables sending ARP requests to update neighbor + table entries. It speeds up convergence for /32 networks on a P2P + connection. + + This feature is enabled by default. .. _ospf-area: @@ -662,6 +673,12 @@ Interfaces .. index:: ip ospf network (broadcast|non-broadcast|point-to-multipoint|point-to-point) .. clicmd:: ip ospf network (broadcast|non-broadcast|point-to-multipoint|point-to-point) + When configuring a point-to-point network on an interface and the interface + has a /32 address associated with then OSPF will treat the interface + as being `unnumbered`. If you are doing this you *must* set the + net.ipv4.conf..rp_filter value to 0. In order for + the ospf multicast packets to be delivered by the kernel. + .. index:: no ip ospf network .. clicmd:: no ip ospf network @@ -687,11 +704,11 @@ Interfaces retransmitting Database Description and Link State Request packets. The default value is 5 seconds. -.. index:: ip ospf transmit-delay -.. clicmd:: ip ospf transmit-delay +.. index:: ip ospf transmit-delay (1-65535) [A.B.C.D] +.. clicmd:: ip ospf transmit-delay (1-65535) [A.B.C.D] -.. index:: no ip ospf transmit-delay -.. clicmd:: no ip ospf transmit-delay +.. index:: no ip ospf transmit-delay [(1-65535)] [A.B.C.D] +.. clicmd:: no ip ospf transmit-delay [(1-65535)] [A.B.C.D] Set number of seconds for InfTransDelay value. LSAs' age should be incremented by this value when transmitting. The default value is 1 second. @@ -704,6 +721,18 @@ Interfaces Enable ospf on an interface and set associated area. +OSPF route-map +============== + +Usage of *ospfd*'s route-map support. + +.. index:: set metric [+|-](0-4294967295) +.. clicmd:: set metric [+|-](0-4294967295) + + Set a metric for matched route when sending announcement. Use plus (+) sign + to add a metric value to an existing metric. Use minus (-) sign to + substract a metric value from an existing metric. + .. _redistribute-routes-to-ospf: Redistribution @@ -906,10 +935,13 @@ Opaque LSA .. index:: no capability opaque .. clicmd:: no capability opaque - *ospfd* supports Opaque LSA (:rfc:`2370`) as fundamental for MPLS Traffic - Engineering LSA. Prior to used MPLS TE, opaque-lsa must be enable in the - configuration file. Alternate command could be "mpls-te on" - (:ref:`ospf-traffic-engineering`). + *ospfd* supports Opaque LSA (:rfc:`2370`) as partial support for + MPLS Traffic Engineering LSAs. The opaque-lsa capability must be + enabled in the configuration. An alternate command could be + "mpls-te on" (:ref:`ospf-traffic-engineering`). Note that FRR + offers only partial support for some of the routing protocol + extensions that are used with MPLS-TE; it does not support a + complete RSVP-TE solution. .. index:: show ip ospf database (opaque-link|opaque-area|opaque-external) .. clicmd:: show ip ospf database (opaque-link|opaque-area|opaque-external) @@ -936,6 +968,12 @@ Opaque LSA Traffic Engineering =================== +.. note:: + + At this time, FRR offers partial support for some of the routing + protocol extensions that can be used with MPLS-TE. FRR does not + support a complete RSVP-TE solution currently. + .. index:: mpls-te on .. clicmd:: mpls-te on @@ -1044,8 +1082,8 @@ Router Information Segment Routing =============== -This is an EXPERIMENTAL support of Segment Routing as per draft -`draft-ietf-ospf-segment-routing-extensions-24.txt` for MPLS dataplane. +This is an EXPERIMENTAL support of Segment Routing as per `RFC 8665` for MPLS +dataplane. .. index:: [no] segment-routing on .. clicmd:: [no] segment-routing on @@ -1058,7 +1096,13 @@ This is an EXPERIMENTAL support of Segment Routing as per draft .. clicmd:: [no] segment-routing global-block (0-1048575) (0-1048575) Fix the Segment Routing Global Block i.e. the label range used by MPLS to - store label in the MPLS FIB. + store label in the MPLS FIB for Prefix SID. + +.. index:: [no] segment-routing local-block (0-1048575) (0-1048575) +.. clicmd:: [no] segment-routing local-block (0-1048575) (0-1048575) + + Fix the Segment Routing Local Block i.e. the label range used by MPLS to + store label in the MPLS FIB for Adjacency SID. .. index:: [no] segment-routing node-msd (1-16) .. clicmd:: [no] segment-routing node-msd (1-16) @@ -1066,13 +1110,15 @@ This is an EXPERIMENTAL support of Segment Routing as per draft Fix the Maximum Stack Depth supported by the router. The value depend of the MPLS dataplane. E.g. for Linux kernel, since version 4.13 it is 32. -.. index:: [no] segment-routing prefix A.B.C.D/M index (0-65535) [no-php-flag] -.. clicmd:: [no] segment-routing prefix A.B.C.D/M index (0-65535) [no-php-flag] +.. index:: [no] segment-routing prefix A.B.C.D/M index (0-65535) [no-php-flag|explicit-null] +.. clicmd:: [no] segment-routing prefix A.B.C.D/M [index (0-65535)|no-php-flag|explicit-null] Set the Segment Routing index for the specified prefix. Note that, only prefix with /32 corresponding to a loopback interface are currently supported. The 'no-php-flag' means NO Penultimate Hop Popping that allows SR - node to request to its neighbor to not pop the label. + node to request to its neighbor to not pop the label. The 'explicit-null' means that + neighbor nodes must swap the incoming label by the MPLS Explicit Null label + before delivering the packet. .. index:: show ip ospf database segment-routing [json] .. clicmd:: show ip ospf database segment-routing [json] diff --git a/doc/user/overview.rst b/doc/user/overview.rst index 7f8ea392dc..724d1d0b9c 100644 --- a/doc/user/overview.rst +++ b/doc/user/overview.rst @@ -4,85 +4,80 @@ Overview ******** -`FRR`_ is a routing software package that provides TCP/IP based routing -services with routing protocols support such as BGP, RIP, OSPF, IS-IS and more -(see :ref:`supported-protocols`). FRR also supports -special BGP Route Reflector and Route Server behavior. In addition to -traditional IPv4 routing protocols, FRR also supports IPv6 routing protocols. -With an SNMP daemon that supports the AgentX protocol, FRR provides routing -protocol MIB read-only access (:ref:`snmp-support`). - -FRR uses an advanced software architecture to provide you with a high quality, -multi server routing engine. FRR has an interactive user interface for each -routing protocol and supports common client commands. Due to this design, you -can add new protocol daemons to FRR easily. You can use FRR library as your -program's client user interface. - -FRR is distributed under the GNU General Public License. +`FRR`_ is a fully featured, high performance, free software IP routing suite. + +FRR implements all standard routing protocols such as BGP, RIP, OSPF, IS-IS and +more (see :ref:`feature-matrix`), as well as many of their extensions. + +FRR is a high performance suite written primarily in C. It can easily handle +full Internet routing tables and is suitable for use on hardware ranging from +cheap SBCs to commercial grade routers. It is actively used in production by +hundreds of companies, universities, research labs and governments. + +FRR is distributed under GPLv2, with development modeled after the Linux +kernel. Anyone may contribute features, bug fixes, tools, documentation +updates, or anything else. FRR is a fork of `Quagga `_. +.. _how-to-get-frr: + +How to get FRR +============== + +The official FRR website is located at |PACKAGE_URL| and contains further +information, as well as links to additional resources. + +Several distributions provide packages for FRR. Check your distribution's +repositories to find out if a suitable version is available. + +Up-to-date Debian & Redhat packages are available at https://deb.frrouting.org/ +& https://rpm.frrouting.org/ respectively. + +For instructions on installing from source, refer to the +`developer documentation `_. + + .. _about-frr: About FRR ========= -Today, TCP/IP networks are covering all of the world. The Internet has been -deployed in many countries, companies, and to the home. When you connect to -the Internet your packet will pass many routers which have TCP/IP routing -functionality. - -A system with FRR installed acts as a dedicated router. With FRR, your machine -exchanges routing information with other routers using routing protocols. FRR -uses this information to update the kernel routing table so that the right data -goes to the right place. You can dynamically change the configuration and you -may view routing table information from the FRR terminal interface. - -Adding to routing protocol support, FRR can setup interface's flags, -interface's address, static routes and so on. If you have a small network, or -a stub network, or xDSL connection, configuring the FRR routing software is -very easy. The only thing you have to do is to set up the interfaces and put a -few commands about static routes and/or default routes. If the network is -rather large, or if the network structure changes frequently, you will want to -take advantage of FRR's dynamic routing protocol support for protocols such as -RIP, OSPF, IS-IS or BGP. - -Traditionally, UNIX based router configuration is done by *ifconfig* and -*route* commands. Status of routing table is displayed by *netstat* utility. -Almost of these commands work only if the user has root privileges. FRR has a -different system administration method. There are two user modes in FRR. One -is normal mode, the other is enable mode. Normal mode user can only view -system status, enable mode user can change system configuration. This UNIX -account independent feature will be great help to the router administrator. - -Currently, FRR supports common unicast routing protocols, that is BGP, OSPF, -RIP and IS-IS. Upcoming for MPLS support, an implementation of LDP is -currently being prepared for merging. Implementations of BFD and PIM-SSM -(IPv4) also exist, but are not actively being worked on. - -The ultimate goal of the FRR project is making a production-grade, high -quality, featureful and free IP routing software suite. +FRR provides IP routing services. Its role in a networking stack is to exchange +routing information with other routers, make routing and policy decisions, and +inform other layers of these decisions. In the most common scenario, FRR +installs routing decisions into the OS kernel, allowing the kernel networking +stack to make the corresponding forwarding decisions. + +In addition to dynamic routing FRR supports the full range of L3 configuration, +including static routes, addresses, router advertisements etc. It has some +light L2 functionality as well, but this is mostly left to the platform. This +makes it suitable for deployments ranging from small home networks with static +routes to Internet exchanges running full Internet tables. + +FRR runs on all modern \*NIX operating systems, including Linux and the BSDs. +Feature support varies by platform; see the :ref:`feature-matrix`. System Architecture -=================== +------------------- .. index:: System architecture - .. index:: Software architecture - .. index:: Software internals Traditional routing software is made as a one process program which provides all of the routing protocol functionalities. FRR takes a different approach. -FRR is a suite of daemons that work together to build the routing table. There -is a daemon for each major supported protocol as well as a middleman daemon -(*Zebra*) which serves as the broker between these daemons and the kernel. +FRR is a suite of daemons that work together to build the routing table. Each +major protocol is implemented in its own daemon, and these daemons talk to a +middleman daemon (*zebra*), which is responsible for coordinating routing +decisions and talking to the dataplane. This architecture allows for high resiliency, since an error, crash or exploit -in one protocol daemon will generally not affect the others. It is also +in one protocol daemon will generally not affect the others. It is also flexible and extensible since the modularity makes it easy to implement new -protocols and tie them into the suite. +protocols and tie them into the suite. Additionally, each daemon implements a +plugin system allowing new functionality to be loaded at runtime. An illustration of the large scale architecture is given below. @@ -106,17 +101,23 @@ An illustration of the large scale architecture is given below. +-------------+ +------------------+ +-------------+ -The multi-process architecture brings extensibility, modularity and -maintainability. All of the FRR daemons can be managed through a single -integrated user interface shell called *vtysh*. *vtysh* connects to each -daemon through a UNIX domain socket and then works as a proxy for user input. -In addition to a unified frontend, *vtysh* also provides the ability to -configure all the daemons using a single configuration file through the -integrated configuration mode avoiding the problem of having to maintain a -separate configuration file for each daemon. +All of the FRR daemons can be managed through a single integrated user +interface shell called *vtysh*. *vtysh* connects to each daemon through a UNIX +domain socket and then works as a proxy for user input. In addition to a +unified frontend, *vtysh* also provides the ability to configure all the +daemons using a single configuration file through the integrated configuration +mode. This avoids the overhead of maintaining a separate configuration file for +each daemon. + +FRR is currently currently implementing a new internal configuration system +based on YANG data models. When this work is completed, FRR will be a fully +programmable routing stack. + + +.. _supported-platforms: Supported Platforms -=================== +------------------- .. index:: Supported platforms .. index:: FRR on other systems @@ -135,7 +136,7 @@ us know if you can get FRR to run on a platform which is not listed below: Versions of these platforms that are older than around 2 years from the point of their original release (in case of GNU/Linux, this is since the kernel's -release on https://kernel.org/) may need some work. Similarly, the following +release on https://kernel.org/) may need some work. Similarly, the following platforms may work with some effort: - Solaris @@ -147,14 +148,15 @@ Recent versions of the following compilers are well tested: - LLVM's Clang - Intel's ICC -.. _supported-protocols: +.. _feature-matrix: -Supported Protocols vs. Platform -================================ +Feature Matrix +^^^^^^^^^^^^^^ -The following table lists all protocols cross-refrenced to all operating -systems that have at least CI build tests. Note that for features, only -features with system dependencies are included here. +The following table lists all protocols cross-referenced to all operating +systems that have at least CI build tests. Note that for features, only +features with system dependencies are included here; if you don't see the +feature you're interested in, it should be supported on your platform. .. role:: mark @@ -230,6 +232,8 @@ features with system dependencies are included here. +-----------------------------------+----------------+--------------+------------+------------+------------+ | EVPN BUM Forwarding | :mark:`≥5.0` | :mark:`N` | :mark:`N` | :mark:`N` | :mark:`N` | +-----------------------------------+----------------+--------------+------------+------------+------------+ +| `vrrpd` (VRRP) | :mark:`≥5.1` | :mark:`N` | :mark:`N` | :mark:`N` | :mark:`N` | ++-----------------------------------+----------------+--------------+------------+------------+------------+ The indicators have the following semantics: @@ -239,6 +243,17 @@ The indicators have the following semantics: * :mark:`CP` - control plane only (i.e. BGP route server / route reflector) * :mark:`N` - daemon/feature not supported by operating system + +Known Kernel Issues +------------------- + +- Linux < 4.11 + + v6 Route Replacement - Linux kernels before 4.11 can cause issues with v6 + route deletion when you have ECMP routes installed into the kernel. This + especially becomes apparent if the route is being transformed from one ECMP + path to another. + .. _supported-rfcs: Supported RFCs @@ -248,78 +263,153 @@ FRR implements the following RFCs: .. note:: This list is incomplete. -- :rfc:`1058` - :t:`Routing Information Protocol. C.L. Hedrick. Jun-01-1988.` -- :rfc:`2082` - :t:`RIP-2 MD5 Authentication. F. Baker, R. Atkinson. January 1997.` -- :rfc:`2453` - :t:`RIP Version 2. G. Malkin. November 1998.` -- :rfc:`2080` - :t:`RIPng for IPv6. G. Malkin, R. Minnear. January 1997.` -- :rfc:`2328` - :t:`OSPF Version 2. J. Moy. April 1998.` -- :rfc:`2370` - :t:`The OSPF Opaque LSA Option R. Coltun. July 1998.` -- :rfc:`3101` - :t:`The OSPF Not-So-Stubby Area (NSSA) Option P. Murphy. January 2003.` -- :rfc:`2740` - :t:`OSPF for IPv6. R. Coltun, D. Ferguson, J. Moy. December 1999.` +BGP +---- + - :rfc:`1771` :t:`A Border Gateway Protocol 4 (BGP-4). Y. Rekhter & T. Li. March 1995.` - :rfc:`1965` :t:`Autonomous System Confederations for BGP. P. Traina. June 1996.` - :rfc:`1997` :t:`BGP Communities Attribute. R. Chandra, P. Traina & T. Li. August 1996.` +- :rfc:`2439` + :t:`BGP Route Flap Damping. C. Villamizar, R. Chandra, R. Govindan. November 1998.` - :rfc:`2545` :t:`Use of BGP-4 Multiprotocol Extensions for IPv6 Inter-Domain Routing. P. Marques, F. Dupont. March 1999.` - :rfc:`2796` - :t:`BGP Route Reflection An alternative to full mesh IBGP. T. Bates & R. - Chandrasekeran. June 1996.` -- :rfc:`2858` - :t:`Multiprotocol Extensions for BGP-4. T. Bates, Y. Rekhter, R. Chandra, D. - Katz. June 2000.` + :t:`BGP Route Reflection An alternative to full mesh IBGP. T. Bates & R. Chandrasekeran. June 1996.` - :rfc:`2842` :t:`Capabilities Advertisement with BGP-4. R. Chandra, J. Scudder. May 2000.` +- :rfc:`2858` + :t:`Multiprotocol Extensions for BGP-4. T. Bates, Y. Rekhter, R. Chandra, D.` +- :rfc:`3107` + :t:`Carrying Label Information in BGP-4. Y. Rekhter & E. Rosen. May 2001.` +- :rfc:`3765` + :t:`NOPEER Community for Border Gateway Protocol (BGP) Route Scope Control. G.Huston, April 2001.` +- :rfc:`4271` + :t:`A Border Gateway Protocol 4 (BGP-4). Updates RFC1771. Y. Rekhter, T. Li & S. Hares. January 2006.` +- :rfc:`4364` + :t:`BGP/MPLS IP Virtual Private Networks (VPNs). Y. Rekhter. Feb 2006.` +- :rfc:`4486` + :t:`Subcodes for BGP Cease Notification Message. E. Chen, V. Gillet. April 2006.` +- :rfc:`4659` + :t:`BGP-MPLS IP Virtual Private Network (VPN) Extension for IPv6 VPN. J. De Clercq, D. Ooms, M. Carugi, F. Le Faucheur. September 2006.` +- :rfc:`4893` + :t:`BGP Support for Four-octet AS Number Space. Q. Vohra, E. Chen May 2007.` +- :rfc:`5004` + :t:`Avoid BGP Best Path Transitions from One External to Another. E. Chen & S. Sangli. September 2007 (Partial support).` +- :rfc:`5082` + :t:`The Generalized TTL Security Mechanism (GTSM). V. Gill, J. Heasley, D. Meyer, P. Savola, C. Pingnataro. October 2007.` +- :rfc:`5575` + :t:`Dissemination of Flow Specification Rules. P. Marques, N. Sheth, R. Raszuk, B. Greene, J. Mauch, D. McPherson. August 2009` +- :rfc:`6286` + :t:`Autonomous-System-Wide Unique BGP Identifier for BGP-4. E. Chen, J. Yuan, June 2011.` +- :rfc:`6608` + :t:`Subcodes for BGP Finite State Machine Error. J. Dong, M. Chen, Huawei Technologies, A. Suryanarayana, Cisco Systems. May 2012.` +- :rfc:`6810` + :t:`The Resource Public Key Infrastructure (RPKI) to Router Protocol. R. Bush, R. Austein. January 2013.` +- :rfc:`6811` + :t:`BGP Prefix Origin Validation. P. Mohapatra, J. Scudder, D. Ward, R. Bush, R. Austein. January 2013.` +- :rfc:`7606` + :t:`Revised Error Handling for BGP UPDATE Messages. E. Chen, J. Scudder, P. Mohapatra, K. Patel. August 2015.` +- :rfc:`7607` + :t:`Codification of AS 0 Processing. W. Kumari, R. Bush, H. Schiller, K. Patel. August 2015.` +- :rfc:`7611` + :t:`BGP ACCEPT_OWN Community Attribute. J. Uttaro, P. Mohapatra, D. Smith, R. Raszuk, J. Scudder. August 2015.` +- :rfc:`7999` + :t:`BLACKHOLE Community. T. King, C. Dietzel, J. Snijders, G. Doering, G. Hankins. Oct 2016.` +- :rfc:`8092` + :t:`BGP Large Communities Attribute. J. Heitz, Ed., J. Snijders, Ed, K. Patel, I. Bagdonas, N. Hilliard. February 2017` +- :rfc:`8195` + :t:`Use of BGP Large Communities. J. Snijders, J. Heasley, M. Schmidt, June 2017` +- :rfc:`8203` + :t:`BGP Administrative Shutdown Communication. J. Snijders, J. Heitz, J. Scudder. July 2017.` +- :rfc:`8212` + :t:`Default External BGP (EBGP) Route Propagation Behavior without Policies. J. Mauch, J. Snijders, G. Hankins. July 2017` +- :rfc:`8277` + :t:`Using BGP to Bind MPLS Labels to Address Prefixes. E. Rosen. October 2017` + + +OSPF +---- + +- :rfc:`2328` + :t:`OSPF Version 2. J. Moy. April 1998.` +- :rfc:`2370` + :t:`The OSPF Opaque LSA Option R. Coltun. July 1998.` +- :rfc:`3101` + :t:`The OSPF Not-So-Stubby Area (NSSA) Option P. Murphy. January 2003.` +- :rfc:`2740` + :t:`OSPF for IPv6. R. Coltun, D. Ferguson, J. Moy. December 1999.` - :rfc:`3137` - :t:`OSPF Stub Router Advertisement, A. Retana, L. Nguyen, R. White, A. Zinin, - D. McPherson. June 2001` + :t:`OSPF Stub Router Advertisement, A. Retana, L. Nguyen, R. White, A. Zinin, D. McPherson. June 2001` + +ISIS +---- + +RIP +---- + +- :rfc:`1058` + :t:`Routing Information Protocol. C.L. Hedrick. Jun-01-1988.` +- :rfc:`2082` + :t:`RIP-2 MD5 Authentication. F. Baker, R. Atkinson. January 1997.` +- :rfc:`2453` + :t:`RIP Version 2. G. Malkin. November 1998.` +- :rfc:`2080` + :t:`RIPng for IPv6. G. Malkin, R. Minnear. January 1997.` + +PIM +---- + +BFD +---- +- :rfc:`5880` + :t:`Bidirectional Forwarding Detection (BFD), D. Katz, D. Ward. June 2010` +- :rfc:`5881` + :t:`Bidirectional Forwarding Detection (BFD) for IPv4 and IPv6 (Single Hop), D. Katz, D. Ward. June 2010` +- :rfc:`5883` + :t:`Bidirectional Forwarding Detection (BFD) for Multihop Paths, D. Katz, D. Ward. June 2010` + +MPLS +---- + +- :rfc:`2858` + :t:`Multiprotocol Extensions for BGP-4. T. Bates, Y. Rekhter, R. Chandra, D. Katz. June 2000.` +- :rfc:`4364` + :t:`BGP/MPLS IP Virtual Private Networks (VPNs). Y. Rekhter. Feb 2006.` - :rfc:`4447` - :t:`Pseudowire Setup and Maintenance Using the Label Distribution Protocol - (LDP), L. Martini, E. Rosen, N. El-Aawar, T. Smith, and G. Heron. April - 2006.` + :t:`Pseudowire Setup and Maintenance Using the Label Distribution Protocol (LDP), L. Martini, E. Rosen, N. El-Aawar, T. Smith, and G. Heron. April 2006.` +- :rfc:`4659` + :t:`BGP-MPLS IP Virtual Private Network (VPN) Extension for IPv6 VPN. J. De Clercq, D. Ooms, M. Carugi, F. Le Faucheur. September 2006` - :rfc:`4762` - :t:`Virtual Private LAN Service (VPLS) Using Label Distribution Protocol - (LDP) Signaling, M. Lasserre and V. Kompella. January 2007.` + :t:`Virtual Private LAN Service (VPLS) Using Label Distribution Protocol (LDP) Signaling, M. Lasserre and V. Kompella. January 2007.` - :rfc:`5036` :t:`LDP Specification, L. Andersson, I. Minei, and B. Thomas. October 2007.` - :rfc:`5561` - :t:`LDP Capabilities, B. Thomas, K. Raza, S. Aggarwal, R. Aggarwal, and - JL. Le Roux. July 2009.` + :t:`LDP Capabilities, B. Thomas, K. Raza, S. Aggarwal, R. Aggarwal, and JL. Le Roux. July 2009.` - :rfc:`5918` - :t:`Label Distribution Protocol (LDP) 'Typed Wildcard' Forward Equivalence - Class (FEC), R. Asati, I. Minei, and B. Thomas. August 2010.` + :t:`Label Distribution Protocol (LDP) 'Typed Wildcard' Forward Equivalence Class (FEC), R. Asati, I. Minei, and B. Thomas. August 2010.` - :rfc:`5919` - :t:`Signaling LDP Label Advertisement Completion, R. Asati, P. Mohapatra, - E. Chen, and B. Thomas. August 2010.` + :t:`Signaling LDP Label Advertisement Completion, R. Asati, P. Mohapatra, E. Chen, and B. Thomas. August 2010.` - :rfc:`6667` - :t:`LDP 'Typed Wildcard' Forwarding Equivalence Class (FEC) for PWid and - Generalized PWid FEC Elements, K. Raza, S. Boutros, and C. Pignataro. July - 2012.` + :t:`LDP 'Typed Wildcard' Forwarding Equivalence Class (FEC) for PWid and Generalized PWid FEC Elements, K. Raza, S. Boutros, and C. Pignataro. July 2012.` - :rfc:`6720` - :t:`The Generalized TTL Security Mechanism (GTSM) for the Label Distribution - Protocol (LDP), C. Pignataro and R. Asati. August 2012.` + :t:`The Generalized TTL Security Mechanism (GTSM) for the Label Distribution Protocol (LDP), C. Pignataro and R. Asati. August 2012.` - :rfc:`7552` - :t:`Updates to LDP for IPv6, R. Asati, C. Pignataro, K. Raza, V. Manral, - and R. Papneja. June 2015.` -- :rfc:`5880` - :t:`Bidirectional Forwarding Detection (BFD), D. Katz, D. Ward. June 2010` -- :rfc:`5881` - :t:`Bidirectional Forwarding Detection (BFD) for IPv4 and IPv6 (Single Hop), - D. Katz, D. Ward. June 2010` -- :rfc:`5883` - :t:`Bidirectional Forwarding Detection (BFD) for Multihop Paths, D. Katz, - D. Ward. June 2010` + :t:`Updates to LDP for IPv6, R. Asati, C. Pignataro, K. Raza, V. Manral, and R. Papneja. June 2015.` + +VRRP +---- + +- :rfc:`3768` + :t:`Virtual Router Redundancy Protocol (VRRP). R. Hinden. April 2004.` +- :rfc:`5798` + :t:`Virtual Router Redundancy Protocol (VRRP) Version 3 for IPv4 and IPv6. S. Nadas. June 2000.` + +SNMP +---- **When SNMP support is enabled, the following RFCs are also supported:** @@ -337,15 +427,6 @@ FRR implements the following RFCs: - :rfc:`2741` :t:`Agent Extensibility (AgentX) Protocol. M. Daniele, B. Wijnen. January 2000.` -How to get FRR -============== - -The official FRR website is located at |PACKAGE_URL| and contains further -information, as well as links to additional resources. - -Several distributions provide packages for FRR. Check your distribution's -repositories to find out if a suitable version is available. - Mailing Lists ============= @@ -386,4 +467,4 @@ For information on reporting bugs, please see :ref:`bug-reports`. .. _frr: |package-url| .. _github: https://github.com/frrouting/frr/ .. _github issues: https://github.com/frrouting/frr/issues -.. _slack: https://frrouting.slack.com/ +.. _slack: https://frrouting.org/#participate diff --git a/doc/user/pbr.rst b/doc/user/pbr.rst index 6230bf777a..99ef258cb2 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] +.. clicmd:: nexthop [A.B.C.D|X:X::X:XX] [interface] [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 @@ -56,10 +56,36 @@ listing of ECMP nexthops used to forward packets for when a pbr-map is matched. Showing Nexthop Group Information --------------------------------- -.. clicmd:: show pbr nexthop-groups [NAME] +.. clicmd:: show pbr nexthop-groups [NAME] [json] Display information on a PBR nexthop-group. If ``NAME`` is omitted, all - nexthop groups are shown. + nexthop groups are shown. Setting ``json`` will provide the same + information in an array of objects which obey the schema below: + + +-----------+----------------------------+---------+ + | Key | Description | Type | + +===========+============================+=========+ + | id | Unique ID | Integer | + +-----------+----------------------------+---------+ + | name | Name of this group | String | + +-----------+----------------------------+---------+ + | valid | Is this group well-formed? | Boolean | + +-----------+----------------------------+---------+ + | installed | ... and is it installed? | Boolean | + +-----------+----------------------------+---------+ + | nexthops | Nexthops within this group | Array | + +-----------+----------------------------+---------+ + + Each element within ``nexthops`` describes a single target within this + group, and its structure is described by the JSON below: + + +---------+------------------------------+---------+ + | Key | Description | Type | + +=========+==============================+=========+ + | nexthop | Name of this nexthop | String | + +---------+------------------------------+---------+ + | valid | Is this nexthop well-formed? | Boolean | + +---------+------------------------------+---------+ .. _pbr-maps: @@ -91,6 +117,28 @@ end destination. both v4 and v6 prefixes. This command is used in conjunction of the :clicmd:`match src-ip PREFIX` command for matching. +.. clicmd:: match mark (1-4294967295) + + Select the mark to match. This is a linux only command and if attempted + on another platform it will be denied. This mark translates to the + underlying `ip rule .... fwmark XXXX` command. + +.. clicmd:: match dscp (DSCP|0-63) + + Match packets according to the specified differentiated services code point + (DSCP) in the IP header; if this value matches then forward the packet + according to the nexthop(s) specified. The passed DSCP value may also be a + standard name for a differentiated service code point like cs0 or af11. + + You may only specify one dscp per route map sequence; to match on multiple + dscp values you will need to create several sequences, one for each value. + +.. clicmd:: match ecn (0-3) + + Match packets according to the specified explicit congestion notification + (ECN) field in the IP header; if this value matches then forward the packet + according to the nexthop(s) specified. + .. clicmd:: set nexthop-group NAME Use the nexthop-group NAME as the place to forward packets when the match @@ -101,6 +149,77 @@ end destination. Use this individual nexthop as the place to forward packets when the match commands have matched a packet. +.. clicmd:: set vrf unchanged|NAME + + If unchanged is set, the rule will use the vrf table the interface is in + as its lookup. If NAME is specified, the rule will use that vrf table as + its lookup. + + Not supported with NETNS VRF backend. + +.. clicmd:: show pbr map [NAME] [detail|json] + + Display pbr maps either all or by ``NAME``. If ``detail`` is set, it will + give information about the rules unique ID used internally and some extra + debugging information about install state for the nexthop/nexthop group. + Setting ``json`` will provide the same information in an array of objects + which obey the schema below: + + +----------+--------------------------------+---------+ + | Key | Description | Type | + +==========+================================+=========+ + | name | Map name | String | + +----------+--------------------------------+---------+ + | valid | Is the map well-formed? | Boolean | + +----------+--------------------------------+---------+ + | policies | Rules to match packets against | Array | + +----------+--------------------------------+---------+ + + Each element of the ``policies`` array is composed of a handful of objects + representing the policies associated with this map. Each policy is + described as below (not all fields are required): + + +-----------------+-------------------------------------------+---------+ + | Key | Description | Type | + +=================+===========================================+=========+ + | id | Unique ID | Integer | + +-----------------+-------------------------------------------+---------+ + | sequenceNumber | Order of this policy within the map | Integer | + +-----------------+-------------------------------------------+---------+ + | ruleNumber | Rule number to install into | Integer | + +-----------------+-------------------------------------------+---------+ + | vrfUnchanged | Use interface's VRF | Boolean | + +-----------------+-------------------------------------------+---------+ + | installed | Is this policy installed? | Boolean | + +-----------------+-------------------------------------------+---------+ + | installedReason | Why (or why not?) | String | + +-----------------+-------------------------------------------+---------+ + | matchSrc | Match packets with this source address | String | + +-----------------+-------------------------------------------+---------+ + | matchDst | ... or with this destination address | String | + +-----------------+-------------------------------------------+---------+ + | matchMark | ... or with this marker | Integer | + +-----------------+-------------------------------------------+---------+ + | vrfName | Associated VRF (if relevant) | String | + +-----------------+-------------------------------------------+---------+ + | nexthopGroup | This policy's nexthop group (if relevant) | Object | + +-----------------+-------------------------------------------+---------+ + + Finally, the ``nexthopGroup`` object above cotains information we know + about the configured nexthop for this policy: + + +---------------------+--------------------------------------+---------+ + | Key | Description | Type | + +=====================+======================================+=========+ + | tableId | Nexthop table ID | Integer | + +---------------------+--------------------------------------+---------+ + | name | Name of the nexthop group | String | + +---------------------+--------------------------------------+---------+ + | installed | Is this nexthop group installed? | Boolean | + +---------------------+--------------------------------------+---------+ + | installedInternally | Do we think this group is installed? | Integer | + +---------------------+--------------------------------------+---------+ + .. _pbr-policy: PBR Policy @@ -116,6 +235,29 @@ causes the policy to be installed into the kernel. This command is available under interface sub-mode. This turns on the PBR map NAME and allows it to work properly. +.. note:: + This will not dynamically create PBR maps on sub-interfaces (i.e. vlans) + even if one is on the master. Each must have the PBR map explicitly added + to the interface. + +.. clicmd:: show pbr interface [NAME] [json] + + Enumerates all interfaces which ``pbrd`` is keeping track of. Passing + ``json`` will return an array of interfaces; each returned interface will + adhere to the JSON schema below: + + +--------+----------------------------+---------+ + | Key | Description | Type | + +========+============================+=========+ + | name | Interface name | String | + +--------+----------------------------+---------+ + | index | Device Index | Integer | + +--------+----------------------------+---------+ + | policy | PBR map for this interface | String | + +--------+----------------------------+---------+ + | valid | Is the map well-formed? | Boolean | + +--------+----------------------------+---------+ + .. _pbr-details: PBR Details diff --git a/doc/user/pim.rst b/doc/user/pim.rst index 5148d3baff..d30a5ed647 100644 --- a/doc/user/pim.rst +++ b/doc/user/pim.rst @@ -11,6 +11,13 @@ vrf aware and can work within the context of vrf's in order to do S,G mrouting. Additionally PIM can be used in the EVPN underlay network for optimizing forwarding of overlay BUM traffic. +.. note:: + + On Linux for PIM-SM operation you *must* have kernel version 4.18 or greater. + To use PIM for EVPN BUM forwarding, kernels 5.0 or greater are required. + OpenBSD has no multicast support and FreeBSD, NetBSD and Solaris only + have support for SSM. + .. _starting-and-stopping-pimd: Starting and Stopping pimd @@ -59,6 +66,14 @@ Certain signals have special meanings to *pimd*. prefix of group ranges covered. This command is vrf aware, to configure for a vrf, enter the vrf submode. +.. index:: ip pim register-accept-list PLIST +.. clicmd:: ip pim register-accept-list PLIST + + When pim receives a register packet the source of the packet will be compared + to the prefix-list specified, PLIST, and if a permit is received normal + processing continues. If a deny is returned for the source address of the + register packet a register stop message is sent to the source. + .. index:: ip pim spt-switchover infinity-and-beyond .. clicmd:: ip pim spt-switchover infinity-and-beyond @@ -151,6 +166,37 @@ Certain signals have special meanings to *pimd*. urib-only Lookup in the Unicast Rib only. +.. index:: [no] ip msdp mesh-group [WORD] +.. clicmd:: [no] ip msdp mesh-group [WORD] + + Create or Delete a multicast source discovery protocol mesh-group using + [WORD] as the group name. + +.. index:: [no] ip msdp mesh-group WORD member A.B.C.D +.. clicmd:: [no] ip msdp mesh-group WORD member A.B.C.D + + Attach or Delete A.B.C.D to the MSDP mesh group WORD specified. + +.. index:: [no] ip msdp mesh-group WORD source A.B.C.D +.. clicmd:: [no] ip msdp mesh-group WORD source A.B.C.D + + For the address specified A.B.C.D use that as the source address for + mesh group packets being sent. + +.. index:: ip igmp generate-query-once [version (2-3)] +.. clicmd:: ip igmp generate-query-once [version (2-3)] + + Generate IGMP query (v2/v3) on user requirement. This will not depend on + the existing IGMP general query timer.If no version is provided in the cli, + it will be considered as default v2 query.This is a hidden command. + +.. index:: [no] ip igmp watermark-warn (10-60000) +.. clicmd:: [no] ip igmp watermark-warn (10-60000) + + Configure watermark warning generation for an igmp group limit. Generates + warning once the configured group limit is reached while adding new groups. + 'no' form of the command disables the warning generation. This command is + vrf aware. To configure per vrf, enter vrf submode. .. _pim-interface-configuration: @@ -161,11 +207,32 @@ PIM interface commands allow you to configure an interface as either a Receiver or a interface that you would like to form pim neighbors on. If the interface is in a vrf, enter the interface command with the vrf keyword at the end. +.. index:: ip pim active-active +.. clicmd:: ip pim active-active + + Turn on pim active-active configuration for a Vxlan interface. This + command will not do anything if you do not have the underlying ability + of a mlag implementation. + .. index:: ip pim bfd .. clicmd:: ip pim bfd Turns on BFD support for PIM for this interface. +.. index:: ip pim bsm +.. clicmd:: ip pim bsm + + Tell pim that we would like to use this interface to process bootstrap + messages. This is enabled by default. 'no' form of this command is used to + restrict bsm messages on this interface. + +.. index:: ip pim unicast-bsm +.. clicmd:: ip pim unicast-bsm + + Tell pim that we would like to allow interface to process unicast bootstrap + messages. This is enabled by default. 'no' form of this command is used to + restrict processing of unicast bsm messages on this interface. + .. index:: ip pim drpriority (1-4294967295) .. clicmd:: ip pim drpriority (1-4294967295) @@ -177,23 +244,31 @@ is in a vrf, enter the interface command with the vrf keyword at the end. Set the pim hello and hold interval for a interface. -.. index:: ip pim sm -.. clicmd:: ip pim sm +.. index:: ip pim +.. clicmd:: ip pim Tell pim that we would like to use this interface to form pim neighbors - over. Please note we will *not* accept igmp reports over this interface with - this command. + over. Please note that this command does not enable the reception of IGMP + reports on the interface. Refer to the next `ip igmp` command for IGMP + management. + +.. index:: [no] ip pim use-source A.B.C.D +.. clicmd:: [no] ip pim use-source A.B.C.D + + If you have multiple addresses configured on a particular interface + and would like pim to use a specific source address associated with + that interface. .. index:: ip igmp .. clicmd:: ip igmp Tell pim to receive IGMP reports and Query on this interface. The default - version is v3. This command is useful on the LHR. + version is v3. This command is useful on a LHR. -.. index:: ip igmp join A.B.C.D A.B.C.D -.. clicmd:: ip igmp join A.B.C.D A.B.C.D +.. index:: ip igmp join A.B.C.D [A.B.C.D] +.. clicmd:: ip igmp join A.B.C.D [A.B.C.D] - Join multicast source-group on an interface. + Join multicast group or source-group on an interface. .. index:: ip igmp query-interval (1-1800) .. clicmd:: ip igmp query-interval (1-1800) @@ -218,6 +293,26 @@ is in a vrf, enter the interface command with the vrf keyword at the end. or IGMP report is received on this interface and the Group is denied by the prefix-list, PIM will ignore the join or report. +.. index:: ip igmp last-member-query-count (1-7) +.. clicmd:: ip igmp last-member-query-count (1-7) + + Set the IGMP last member query count. The default value is 2. 'no' form of + this command is used to to configure back to the default value. + +.. index:: ip igmp last-member-query-interval (1-255) +.. clicmd:: ip igmp last-member-query-interval (1-255) + + Set the IGMP last member query interval in deciseconds. The default value is + 10 deciseconds. 'no' form of this command is used to to configure back to the + default value. + +.. index:: ip mroute INTERFACE A.B.C.D [A.B.C.D] +.. clicmd:: ip mroute INTERFACE A.B.C.D [A.B.C.D] + + Set a static multicast route for a traffic coming on the current interface to + be forwarded on the given interface if the traffic matches the group address + and optionally the source address. + .. _pim-multicast-rib-insertion: PIM Multicast RIB insertion: @@ -302,11 +397,30 @@ cause great confusion. Source Group. The keyword `fill` says to fill in all assumed data for test/data gathering purposes. -.. index:: show ip mroute count -.. clicmd:: show ip mroute count +.. index:: show ip mroute [vrf NAME] count [json] +.. clicmd:: show ip mroute [vrf NAME] count [json] Display information about installed into the kernel S,G mroutes and in - addition display data about packet flow for the mroutes. + addition display data about packet flow for the mroutes for a specific + vrf. + +.. index:: show ip mroute vrf all count [json] +.. clicmd:: show ip mroute vrf all count [json] + + Display information about installed into the kernel S,G mroutes and in + addition display data about packet flow for the mroutes for all vrfs. + +.. index:: show ip mroute [vrf NAME] summary [json] +.. clicmd:: show ip mroute [vrf NAME] summary [json] + + Display total number of S,G mroutes and number of S,G mroutes installed + into the kernel for a specific vrf. + +.. index:: show ip mroute vrf all summary [json] +.. clicmd:: show ip mroute vrf all summary [json] + + Display total number of S,G mroutes and number of S,G mroutes + installed into the kernel for all vrfs. .. index:: show ip pim assert .. clicmd:: show ip pim assert @@ -338,6 +452,11 @@ cause great confusion. Display information about interfaces PIM is using. +.. index:: show ip pim mlag [vrf NAME] interface [detail|WORD] [json] +.. clicmd:: show ip pim mlag [vrf NAME|all] interface [detail|WORD] [json] + + Display mlag interface information. + .. index:: show ip pim [vrf NAME] join [A.B.C.D [A.B.C.D]] [json] .. clicmd:: show ip pim join @@ -350,6 +469,11 @@ cause great confusion. Display information about PIM interface local-membership. +.. index:: show ip pim mlag summary [json] +.. clicmd:: show ip pim mlag summary [json] + + Display mlag information state that PIM is keeping track of. + .. index:: show ip pim neighbor .. clicmd:: show ip pim neighbor @@ -407,6 +531,33 @@ cause great confusion. Display upstream information for S,G's and the RPF data associated with them. +.. index:: show ip pim [vrf NAME] mlag upstream [A.B.C.D [A.B.C.D]] [json] +.. clicmd:: show ip pim mlag upstream + + Display upstream entries that are synced across MLAG switches. + Allow the user to specify sub Source and Groups address filters. + +.. index:: show ip pim mlag summary +.. clicmd:: show ip pim mlag summary + + Display PIM MLAG (multi-chassis link aggregation) session status and + control message statistics. + +.. index:: show ip pim bsr +.. clicmd:: show ip pim bsr + + Display current bsr, its uptime and last received bsm age. + +.. index:: show ip pim bsrp-info +.. clicmd:: show ip pim bsrp-info + + Display group-to-rp mappings received from E-BSR. + +.. index:: show ip pim bsm-database +.. clicmd:: show ip pim bsm-database + + Display all fragments ofstored bootstrap message in user readable format. + .. index:: show ip rpf .. clicmd:: show ip rpf @@ -417,6 +568,16 @@ cause great confusion. Display multicast traceroute towards source, optionally for particular group. +.. index:: show ip multicast count [vrf NAME] [json] +.. clicmd:: show ip multicast count [vrf NAME] [json] + + Display multicast data packets count per interface for a vrf. + +.. index:: show ip multicast count vrf all [json] +.. clicmd:: show ip multicast count vrf all [json] + + Display multicast data packets count per interface for all vrf. + PIM Debug Commands ================== @@ -470,6 +631,11 @@ the config was written out. This traces pim code and how it is running. +.. index:: debug pim bsm +.. clicmd:: debug pim bsm + + This turns on debugging for BSR message processing. + .. index:: debug pim zebra .. clicmd:: debug pim zebra @@ -494,6 +660,13 @@ Clear commands reset various variables. Reset multicast routes. +.. index:: clear ip mroute [vrf NAME] count +.. clicmd:: clear ip mroute [vrf NAME] count + + When this command is issued, reset the counts of data shown for + packet count, byte count and wrong interface to 0 and start count + up from this spot. + .. index:: clear ip pim interfaces .. clicmd:: clear ip pim interfaces diff --git a/doc/user/routemap.rst b/doc/user/routemap.rst index ef9ebe8ddc..3d19306a75 100644 --- a/doc/user/routemap.rst +++ b/doc/user/routemap.rst @@ -7,6 +7,9 @@ Route Maps Route maps provide a means to both filter and/or apply actions to route, hence allowing policy to be applied to routes. +For a route reflector to apply a ``route-map`` to reflected routes, be sure to +include ``bgp route-reflector allow-outbound-policy`` in ``router bgp`` mode. + Route maps are an ordered list of route map entries. Each entry may specify up to four distinct sets of clauses: @@ -85,6 +88,23 @@ deny cont goto next route-map entry +.. _route-map-show-command: + +.. index:: show route-map [WORD] +.. clicmd:: show route-map [WORD] + + Display data about each daemons knowledge of individual route-maps. + If WORD is supplied narrow choice to that particular route-map. + +.. _route-map-clear-counter-command: + +.. index:: clear route-map counter [WORD] +.. clicmd:: clear route-map counter [WORD] + + Clear counters that are being stored about the route-map utilization + so that subsuquent show commands will indicate since the last clear. + If WORD is specified clear just that particular route-map's counters. + .. _route-map-command: Route Map Command @@ -131,13 +151,18 @@ Route Map Match Command Matches the specified `prefix-len`. This is a Zebra specific command. -.. index:: match ip next-hop IPV4_ADDR -.. clicmd:: match ip next-hop IPV4_ADDR +.. index:: match ip next-hop address IPV4_ADDR +.. clicmd:: match ip next-hop address IPV4_ADDR - Matches the specified `ipv4_addr`. + This is a BGP specific match command. Matches the specified `ipv4_addr`. -.. index:: match aspath AS_PATH -.. clicmd:: match aspath AS_PATH +.. index:: match ipv6 next-hop IPV6_ADDR +.. clicmd:: match ipv6 next-hop IPV6_ADDR + + This is a BGP specific match command. Matches the specified `ipv6_addr`. + +.. index:: match as-path AS_PATH +.. clicmd:: match as-path AS_PATH Matches the specified `as_path`. @@ -253,15 +278,34 @@ Route Map Set Command Set the BGP local preference to `local_pref`. +.. index:: set local-preference +LOCAL_PREF +.. clicmd:: set local-preference +LOCAL_PREF + + Add the BGP local preference to an existing `local_pref`. + +.. index:: set local-preference -LOCAL_PREF +.. clicmd:: set local-preference -LOCAL_PREF + + Subtract the BGP local preference from an existing `local_pref`. + +.. index:: [no] set distance DISTANCE +.. clicmd:: [no] set distance DISTANCE + + Set the Administrative distance to DISTANCE to use for the route. + This is only locally significant and will not be dispersed to peers. + .. index:: set weight WEIGHT .. clicmd:: set weight WEIGHT Set the route's weight. -.. index:: set metric METRIC -.. clicmd:: set metric METRIC +.. index:: [no] set metric <[+|-](1-4294967295)|rtt|+rtt|-rtt> +.. clicmd:: [no] set metric <[+|-](1-4294967295)|rtt|+rtt|-rtt> - Set the BGP attribute MED. + Set the BGP attribute MED to a specific value. Use `+`/`-` to add or subtract + the specified value to/from the MED. Use `rtt` to set the MED to the round + trip time or `+rtt`/`-rtt` to add/subtract the round trip time to/from the + MED. .. index:: set as-path prepend AS_PATH .. clicmd:: set as-path prepend AS_PATH @@ -283,6 +327,17 @@ Route Map Set Command Set BGP route origin. +.. index:: set table (1-4294967295) +.. clicmd:: set table (1-4294967295) + + Set the BGP table to a given table identifier + +.. index:: set sr-te color (1-4294967295) +.. clicmd:: set sr-te color (1-4294967295) + + Set the color of a SR-TE Policy to be applied to a learned route. The SR-TE + Policy is uniquely determined by the color and the BGP nexthop. + .. _route-map-call-command: Route Map Call Command @@ -315,6 +370,28 @@ Route Map Exit Action Command Proceed processing the route-map at the first entry whose order is >= N +.. _route-map-optimization-command: + +Route Map Optimization Command +============================== + +.. index:: route-map optimization +.. clicmd:: route-map optimization + + Enable route-map processing optimization. The optimization is + enabled by default. + Instead of sequentially passing through all the route-map indexes + until a match is found, the search for the best-match index will be + based on a look-up in a prefix-tree. A per-route-map prefix-tree + will be constructed for this purpose. The prefix-tree will compose + of all the prefixes in all the prefix-lists that are included in the + match rule of all the sequences of a route-map. + +.. index:: no route-map optimization +.. clicmd:: no route-map optimization + + Disable the route-map processing optimization. + Route Map Examples ================== diff --git a/doc/user/routeserver.rst b/doc/user/routeserver.rst index e677a3030d..474a68db25 100644 --- a/doc/user/routeserver.rst +++ b/doc/user/routeserver.rst @@ -369,8 +369,6 @@ the policies for client RA): hostname RS password ix ! - bgp multiple-instance - ! router bgp 65000 view RS no bgp default ipv4-unicast neighbor 2001:0DB8::A remote-as 65001 diff --git a/doc/user/rpki.rst b/doc/user/rpki.rst index 295a26fda9..f8ec98c964 100644 --- a/doc/user/rpki.rst +++ b/doc/user/rpki.rst @@ -112,31 +112,6 @@ The following commands are independent of a specific cache server. The default value is 300 seconds. -.. index:: rpki timeout <1-4,294,967,296> -.. clicmd:: rpki timeout <1-4,294,967,296> - -.. index:: no rpki timeout -.. clicmd:: no rpki timeout - - Set the number of seconds the router waits for the cache reply. If the cache - server is not replying within this time period, the router deletes all - received prefix records from the prefix table. - - The default value is 600 seconds. - -.. index:: rpki initial-synchronisation-timeout <1-4,294,967,296> -.. clicmd:: rpki initial-synchronisation-timeout <1-4,294,967,296> - -.. index:: no rpki initial-synchronisation-timeout -.. clicmd:: no rpki initial-synchronisation-timeout - - Set the number of seconds until the first synchronization with the cache - server needs to be completed. If the timeout expires, BGP routing is started - without RPKI. The router will try to establish the cache server connection in - the background. - - The default value is 30 seconds. - The following commands configure one or multiple cache servers. .. index:: rpki cache (A.B.C.D|WORD) PORT [SSH_USERNAME] [SSH_PRIVKEY_PATH] [SSH_PUBKEY_PATH] [KNOWN_HOSTS_PATH] PREFERENCE @@ -188,10 +163,6 @@ Validating BGP Updates Create a clause for a route map to match prefixes with the specified RPKI state. - **Note** that the matching of invalid prefixes requires that invalid - prefixes are considered for best path selection, i.e., - ``bgp bestpath prefix-validate disallow-invalid`` is not enabled. - In the following example, the router prefers valid routes over invalid prefixes because invalid routes have a lower local preference. @@ -229,6 +200,18 @@ Debugging Displaying RPKI --------------- +.. index:: show rpki prefix [(1-4294967295)] +.. clicmd:: show rpki prefix [(1-4294967295)] + + Display validated prefixes received from the cache servers filtered + by the specified prefix. + +.. index:: show rpki as-number ASN +.. clicmd:: show rpki as-number ASN + + Display validated prefixes received from the cache servers filtered + by ASN. + .. index:: show rpki prefix-table .. clicmd:: show rpki prefix-table diff --git a/doc/user/setup.rst b/doc/user/setup.rst index ffefe37905..b2b71cf012 100644 --- a/doc/user/setup.rst +++ b/doc/user/setup.rst @@ -6,8 +6,24 @@ Basic Setup After installing FRR, some basic configuration must be completed before it is ready to use. -Daemons File ------------- +Crash logs +---------- + +If any daemon should crash for some reason (segmentation fault, assertion +failure, etc.), it will attempt to write a backtrace to a file located in +:file:`/var/tmp/frr/[-]./crashlog`. This feature is +not affected by any configuration options. + +The crashlog file's directory also contains files corresponding to per-thread +message buffers in files named +:file:`/var/tmp/frr/[-]./logbuf.`. In case of a +crash, these may contain unwritten buffered log messages. To show the contents +of these buffers, pipe their contents through ``tr '\0' '\n'``. A blank line +marks the end of valid unwritten data (it will generally be followed by +garbled, older log messages since the buffer is not cleared.) + +Daemons Configuration File +-------------------------- After a fresh install, starting FRR will do nothing. This is because daemons must be explicitly enabled by editing a file in your configuration directory. This file is usually located at :file:`/etc/frr/daemons` and determines which @@ -34,19 +50,6 @@ systemd. The file initially looks like this: bfdd=no fabricd=no -To enable a particular daemon, simply change the corresponding 'no' to 'yes'. -Subsequent service restarts should start the daemon. - -Daemons Configuration File --------------------------- -There is another file that controls the default options passed to daemons when -starting FRR as a service. This file is located in your configuration -directory, usually at :file:`/etc/frr/daemons`. - -This file has several parts. Here is an example: - -:: - # # If this option is set the /etc/init.d/frr script automatically loads # the config via "vtysh -b" when the servers are started. @@ -71,6 +74,7 @@ This file has several parts. Here is an example: bfdd_options=" --daemon -A 127.0.0.1" fabricd_options=" --daemon -A 127.0.0.1" + #MAX_FDS=1024 # The list of daemons to watch is automatically generated by the init script. #watchfrr_options="" @@ -83,6 +87,13 @@ This file has several parts. Here is an example: Breaking this file down: +:: + + bgpd=yes + +To enable a particular daemon, simply change the corresponding 'no' to 'yes'. +Subsequent service restarts should start the daemon. + :: vtysh_enable=yes @@ -91,6 +102,16 @@ As the comment says, this causes :ref:`VTYSH ` to apply configuration when starting the daemons. This is useful for a variety of reasons touched on in the VTYSH documentation and should generally be enabled. +:: + + MAX_FDS=1024 + +This allows the operator to control the number of open file descriptors +each daemon is allowed to start with. The current assumed value on +most operating systems is 1024. If the operator plans to run bgp with +several thousands of peers than this is where we would modify FRR to +allow this to happen. + :: zebra_options=" -s 90000000 --daemon -A 127.0.0.1" @@ -129,6 +150,7 @@ add the following entries to :file:`/etc/services`. eigprd 2613/tcp # EIGRPd vty bfdd 2617/tcp # bfdd vty fabricd 2618/tcp # fabricd vty + vrrpd 2619/tcp # vrrpd vty If you use a FreeBSD newer than 2.2.8, the above entries are already added to @@ -137,7 +159,7 @@ number when starting the daemon, these entries may not be needed. You may need to make changes to the config files in |INSTALL_PREFIX_ETC|. -systemd +Systemd ------- Although not installed when installing from source, FRR provides a service file for use with ``systemd``. It is located in :file:`tools/frr.service` in the Git @@ -148,3 +170,73 @@ location. A good place is usually ``/etc/systemd/system/``. After issuing a ``systemctl daemon-reload``, you should be able to start the FRR service via ``systemctl start frr``. If this fails, or no daemons are started. check the ``journalctl`` logs for an indication of what went wrong. + +Operations +---------- + +This section covers a few common operational tasks and how to perform them. + +Restarting +^^^^^^^^^^ + +Restarting kills all running FRR daemons and starts them again. Any unsaved +configuration will be lost. + +.. code-block:: console + + service frr restart + +.. note:: + + Alternatively, you can invoke the init script directly:: + + /etc/init.d/frr restart + + Or, if using systemd:: + + systemctl restart frr + +Reloading +^^^^^^^^^ + +Reloading applies the differential between on-disk configuration and the +current effective configuration of running FRR processes. This includes +starting daemons that were previously stopped and any changes made to +individual or unified daemon configuration files. + +.. code-block:: console + + service frr reload + +.. note:: + + Alternatively, you can invoke the init script directly:: + + /etc/init.d/frr reload + + Or, if using systemd:: + + systemctl reload frr + +See :ref:`FRR-RELOAD ` for more about the `frr-reload.py` script. + + +Starting a new daemon +^^^^^^^^^^^^^^^^^^^^^ + +Suppose *bgpd* and *zebra* are running, and you wish to start *pimd*. In +``/etc/frr/daemons`` make the following change: + +.. code-block:: diff + + - pimd=no + + pimd=yes + +Then perform a reload. + +Currently there is no way to stop or restart an individual daemon. This is +because FRR's monitoring program cannot currently distinguish between a crashed +/ killed daemon versus one that has been intentionally stopped or restarted. +The closest that can be achieved is to remove all configuration for the daemon, +and set its line in ``/etc/frr/daemons`` to ``=no``. Once this is done, the +daemon will be stopped the next time FRR is restarted. diff --git a/doc/user/sharp.rst b/doc/user/sharp.rst index 4568c2a901..57ef141c7e 100644 --- a/doc/user/sharp.rst +++ b/doc/user/sharp.rst @@ -71,14 +71,15 @@ keyword. At present, no sharp commands will be preserved in the config. be used for pop and forward operations when the specified label is seen. .. index:: sharp watch -.. clicmd:: [no] sharp watch [connected] +.. clicmd:: [no] sharp watch |import [connected] Instruct zebra to monitor and notify sharp when the specified nexthop is changed. The notification from zebra is written into the debug log. The nexthop or import choice chooses the type of nexthop we are asking zebra to watch for us. This choice affects zebra's decision on what matches. Connected tells zebra whether or not that we want the route - matched against to be a static or connected route. The no form of + matched against to be a static or connected route for the nexthop keyword, + for the import keyword connected means exact match. The no form of the command obviously turns this watching off. .. index:: sharp data nexthop @@ -86,3 +87,60 @@ keyword. At present, no sharp commands will be preserved in the config. Allow end user to dump associated data with the nexthop tracking that may have been turned on. + +.. index:: sharp lsp +.. clicmd:: sharp lsp [update] (0-100000) nexthop-group NAME [prefix A.B.C.D/M TYPE [instance (0-255)]] + + Install an LSP using the specified in-label, with nexthops as + listed in nexthop-group ``NAME``. If ``update`` is included, the + update path is used. The LSP is installed as type ZEBRA_LSP_SHARP. + If ``prefix`` is specified, an existing route with type ``TYPE`` + (and optional ``instance`` id) will be updated to use the LSP. + +.. index:: sharp remove lsp +.. clicmd:: sharp remove lsp (0-100000) nexthop-group NAME [prefix A.B.C.D/M TYPE [instance (0-255)]] + + Remove a SHARPD LSP that uses the specified in-label, where the + nexthops are specified in nexthop-group ``NAME``. If ``prefix`` is + specified, remove label bindings from the route of type ``TYPE`` + also. + +.. index:: sharp send opaque +.. clicmd:: sharp send opaque type (1-255) (1-1000) + + Send opaque ZAPI messages with subtype ``type``. Sharpd will send + a stream of messages if the count is greater than one. + +.. index:: sharp send opaque unicast +.. clicmd:: sharp send opaque unicast type (1-255) $proto_str [{instance (0-1000) | session (1-1000)}] (1-1000) + + Send unicast opaque ZAPI messages with subtype ``type``. The + protocol, instance, and session_id identify a single target zapi + client. Sharpd will send a stream of messages if the count is + greater than one. + +.. index:: sharp send opaque reg unreg +.. clicmd:: sharp send opaque $proto_str [{instance (0-1000) | session (1-1000)}] type (1-1000) + + Send opaque ZAPI registration and unregistration messages for a + single subtype. The messages must specify a protocol daemon by + name, and can include optional zapi ``instance`` and ``session`` + values. + +.. index:: sharp create session +.. clicmd:: sharp create session (1-1024) + + Create an additional zapi client session for testing, using the + specified session id. + +.. index:: sharp remove session +.. clicmd:: sharp remove session (1-1024) + + Remove a test zapi client session that was created with the + specified session id. + +.. index:: sharp neigh discover +.. clicmd:: sharp neigh discover [vrf NAME] IFNAME + + Send an ARP/NDP request to trigger the addition of a neighbor in the ARP + table. diff --git a/doc/user/snmp.rst b/doc/user/snmp.rst index 5579969c0e..0087d41a23 100644 --- a/doc/user/snmp.rst +++ b/doc/user/snmp.rst @@ -23,6 +23,41 @@ the latest version of `net-snmp` which was formerly known as `ucd-snmp`. It is free and open software and available at `http://www.net-snmp.org/ `_ and as binary package for most Linux distributions. +.. _net-smtp-configuration: + +NET-SNMP configuration +====================== + +Routers with a heavy amount of routes (e.g. BGP full table) might experience +problems with a hanging vtysh from time to time, 100% CPU on the snmpd or +even crashes of the frr daemon(s) due to stalls within AgentX. Once snmp +agents connects they start receiving a heavy amount of SNMP data (all the +routes) which cannot be handled quick enough. It's recommended (by several +vendors as well) to exclude these OID's unless you really need them, which +can be achieved by amending the default view from SNMP + +:file:`/etc/snmp/snmpd.conf`: + +:: + + # This is the default view + view all included .1 80 + # Remove ipRouteTable from view + view all excluded .1.3.6.1.2.1.4.21 + # Remove ipNetToMediaTable from view + view all excluded .1.3.6.1.2.1.4.22 + # Remove ipNetToPhysicalPhysAddress from view + view all excluded .1.3.6.1.2.1.4.35 + # Remove ipCidrRouteTable from view + view all excluded .1.3.6.1.2.1.4.24 + # Optionally protect SNMP private/secret values + view all excluded .1.3.6.1.6.3.15 + view all excluded .1.3.6.1.6.3.16 + view all excluded .1.3.6.1.6.3.18 + # Optionally allow SNMP public info (sysName, location, etc) + view system included .iso.org.dod.internet.mgmt.mib-2.system + + .. _agentx-configuration: AgentX configuration diff --git a/doc/user/static.rst b/doc/user/static.rst index 1705b6379e..6302d1b148 100644 --- a/doc/user/static.rst +++ b/doc/user/static.rst @@ -123,8 +123,20 @@ but this time, the route command will apply to the VRF. .. code-block:: frr # case with VRF - configure terminal + configure vrf r1-cust1 ip route 10.0.0.0/24 10.0.0.2 exit-vrf + +SR-TE Route Commands +==================== + +It is possible to specify a route using a SR-TE policy configured in Zebra. + +e.g. to use the SR-TE policy with endpoint 6.6.6.6 and color 123 to reach the +network 9.9.9.9/24: + +.. code-block:: frr + + ip route 9.9.9.9/24 6.6.6.6 color 123 diff --git a/doc/user/subdir.am b/doc/user/subdir.am index 08b5dc954c..dd7a193e34 100644 --- a/doc/user/subdir.am +++ b/doc/user/subdir.am @@ -7,12 +7,15 @@ user_RSTFILES = \ doc/user/ldpd.rst \ doc/user/basic.rst \ doc/user/bgp.rst \ + doc/user/bmp.rst \ doc/user/bugs.rst \ doc/user/conf.py \ doc/user/eigrpd.rst \ doc/user/fabricd.rst \ doc/user/filter.rst \ + doc/user/frr-reload.rst \ doc/user/glossary.rst \ + doc/user/grpc.rst \ doc/user/index.rst \ doc/user/installation.rst \ doc/user/ipv6.rst \ @@ -37,10 +40,13 @@ user_RSTFILES = \ doc/user/snmptrap.rst \ doc/user/static.rst \ doc/user/vnc.rst \ + doc/user/vrrp.rst \ doc/user/vtysh.rst \ doc/user/zebra.rst \ doc/user/bfd.rst \ doc/user/flowspec.rst \ + doc/user/watchfrr.rst \ + doc/user/wecmp_linkbw.rst \ # end EXTRA_DIST += \ diff --git a/doc/user/vnc.rst b/doc/user/vnc.rst index d0934fe6fa..cb9c74ceea 100644 --- a/doc/user/vnc.rst +++ b/doc/user/vnc.rst @@ -468,8 +468,8 @@ redistributed to VNC as bgp-direct-to-nve-groups routes. These routes are NOT announced via BGP, but they are made available for local RFP lookup in response to queries from NVEs. -A non-main/default BGP instance is configured using the `bgp multiple-instance` -and `router bgp AS view NAME` commands as described elsewhere in this document. +A non-main/default BGP instance is configured using the +`router bgp AS view NAME` command as described elsewhere in this document. In order for a route in the unicast BGP RIB to be made available to a querying NVE, there must already be, available to that NVE, an (interior) VNC route diff --git a/doc/user/vrrp.rst b/doc/user/vrrp.rst new file mode 100644 index 0000000000..cb70da3f3b --- /dev/null +++ b/doc/user/vrrp.rst @@ -0,0 +1,563 @@ +.. _vrrp: + +**** +VRRP +**** + +:abbr:`VRRP` stands for Virtual Router Redundancy Protocol. This protocol is +used to allow multiple backup routers on the same segment to take over +operation of each others' IP addresses if the primary router fails. This is +typically used to provide fault-tolerant gateways to hosts on the segment. + +FRR implements VRRPv2 (:rfc:`3768`) and VRRPv3 (:rfc:`5798`). For VRRPv2, no +authentication methods are supported; these are deprecated in the VRRPv2 +specification as they do not provide any additional security over the base +protocol. + +.. note:: + + - VRRP is supported on Linux 5.1+ + - VRRP does not implement Accept_Mode + +.. _vrrp-starting: + +Starting VRRP +============= + +The configuration file for *vrrpd* is :file:`vrrpd.conf`. The typical location +of :file:`vrrpd.conf` is |INSTALL_PREFIX_ETC|/vrrpd.conf. + +If using integrated config, then :file:`vrrpd.conf` need not be present and +:file:`frr.conf` is read instead. + +.. program:: vrrpd + +:abbr:`VRRP` supports all the common FRR daemon start options which are +documented elsewhere. + +.. _vrrp-protocol-overview: + +Protocol Overview +================= + +From :rfc:`5798`: + + VRRP specifies an election protocol that dynamically assigns responsibility + for a virtual router to one of the VRRP routers on a LAN. The VRRP router + controlling the IPv4 or IPv6 address(es) associated with a virtual router is + called the Master, and it forwards packets sent to these IPv4 or IPv6 + addresses. VRRP Master routers are configured with virtual IPv4 or IPv6 + addresses, and VRRP Backup routers infer the address family of the virtual + addresses being carried based on the transport protocol. Within a VRRP + router, the virtual routers in each of the IPv4 and IPv6 address families + are a domain unto themselves and do not overlap. The election process + provides dynamic failover in the forwarding responsibility should the Master + become unavailable. For IPv4, the advantage gained from using VRRP is a + higher-availability default path without requiring configuration of dynamic + routing or router discovery protocols on every end-host. For IPv6, the + advantage gained from using VRRP for IPv6 is a quicker switchover to Backup + routers than can be obtained with standard IPv6 Neighbor Discovery + mechanisms. + +VRRP accomplishes these goals primarily by using a virtual MAC address shared +between the physical routers participating in a VRRP virtual router. This +reduces churn in the neighbor tables of hosts and downstream switches and makes +router failover theoretically transparent to these devices. + +FRR implements the election protocol and handles changing the operating system +interface configuration in response to protocol state changes. + +As a consequence of the shared virtual MAC requirement, VRRP is currently +supported only on Linux, as Linux is the only operating system that provides +the necessary features in its network stack to make implementing this protocol +feasible. + +When a VRRP router is acting as the Master router, FRR allows the interface(s) +with the backed-up IP addresses to remain up and functional. When the router +transitions to Backup state, these interfaces are set into ``protodown`` mode. +This is an interface mode that is functionally equivalent to ``NO-CARRIER``. +Physical drivers typically use this state indication to drop traffic on an +interface. In the case of VRRP, the interfaces in question are macvlan devices, +which are virtual interfaces. Since the IP addresses managed by VRRP are on +these interfaces, this has the same effect as removing these addresses from the +interface, but is implemented as a state flag. + +.. _vrrp-configuration: + +Configuring VRRP +================ + +VRRP is configured on a per-interface basis, with some global defaults +accessible outside the interface context. + +.. _vrrp-system-configuration: + +System Configuration +-------------------- + +FRR's VRRP implementation uses Linux macvlan devices to to implement the shared +virtual MAC feature of the protocol. Currently, it does not create those system +interfaces - they must be configured outside of FRR before VRRP can be enabled +on them. + +Each interface on which VRRP will be enabled must have at least one macvlan +device configured with the virtual MAC and placed in the proper operation mode. +The addresses backed up by VRRP are assigned to these interfaces. + +Suppose you have an interface ``eth0`` with the following configuration: + +.. code-block:: console + + $ ip addr show eth0 + 2: eth0: mtu 1500 qdisc fq_codel state UP group default qlen 1000 + link/ether 02:17:45:00:aa:aa brd ff:ff:ff:ff:ff:ff + inet 10.0.2.15/24 brd 10.0.2.255 scope global dynamic eth0 + valid_lft 72532sec preferred_lft 72532sec + inet6 fe80::17:45ff:fe00:aaaa/64 scope link + valid_lft forever preferred_lft forever + +Suppose that the IPv4 and IPv6 addresses you want to back up are ``10.0.2.16`` +and ``2001:db8::370:7334``, and that they will be managed by the virtual router +with id ``5``. A macvlan device with the appropriate MAC address must be created +before VRRP can begin to operate. + +If you are using ``ifupdown2``, the configuration is as follows: + +.. code-block:: console + + iface eth0 + ... + vrrp 5 10.0.2.16/24 2001:0db8::0370:7334/64 + +Applying this configuration with ``ifreload -a`` will create the appropriate +macvlan device. If you are using ``iproute2``, the equivalent configuration is: + +.. code-block:: console + + ip link add vrrp4-2-1 link eth0 addrgenmode random type macvlan mode bridge + ip link set dev vrrp4-2-1 address 00:00:5e:00:01:05 + ip addr add 10.0.2.16/24 dev vrrp4-2-1 + ip link set dev vrrp4-2-1 up + + ip link add vrrp6-2-1 link eth0 addrgenmode random type macvlan mode bridge + ip link set dev vrrp6-2-1 address 00:00:5e:00:02:05 + ip addr add 2001:db8::370:7334/64 dev vrrp6-2-1 + ip link set dev vrrp6-2-1 up + +In either case, the created interfaces will look like this: + +.. code-block:: console + + $ ip addr show vrrp4-2-1 + 5: vrrp4-2-1@eth0: mtu 1500 qdisc noqueue state UP group default qlen 1000 + link/ether 00:00:5e:00:01:05 brd ff:ff:ff:ff:ff:ff + inet 10.0.2.16/24 scope global vrrp4-2-1 + valid_lft forever preferred_lft forever + inet6 fe80::dc56:d11a:e69d:ea72/64 scope link stable-privacy + valid_lft forever preferred_lft forever + + $ ip addr show vrrp6-2-1 + 8: vrrp6-2-1@eth0: mtu 1500 qdisc noqueue state UP group default qlen 1000 + link/ether 00:00:5e:00:02:05 brd ff:ff:ff:ff:ff:ff + inet6 2001:db8::370:7334/64 scope global + valid_lft forever preferred_lft forever + inet6 fe80::f8b7:c9dd:a1e8:9844/64 scope link stable-privacy + valid_lft forever preferred_lft forever + +Using ``vrrp4-2-1`` as an example, a few things to note about this interface: + +- It is slaved to ``eth0``; any packets transmitted on this interface will + egress via ``eth0`` +- Its MAC address is set to the VRRP IPv4 virtual MAC specified by the RFC for + :abbr:`VRID (Virtual Router ID)` ``5`` +- The :abbr:`VIP (Virtual IP)` address ``10.0.2.16`` must not be present on + the parent interface ``eth0``. +- The link local address on the interface is not derived from the interface + MAC + +First to note is that packets transmitted on this interface will egress via +``eth0``, but with their Ethernet source MAC set to the VRRP virtual MAC. This +is how FRR's VRRP implementation accomplishes the virtual MAC requirement on +real hardware. + +Ingress traffic is a more complicated matter. Macvlan devices have multiple +operating modes that change how ingress traffic is handled. Of relevance to +FRR's implementation are the ``bridge`` and ``private`` modes. In ``private`` +mode, any ingress traffic on ``eth0`` (in our example) with a source MAC +address equal to the MAC address on any of ``eth0``'s macvlan devices will be +placed *only* on that macvlan device. This curious behavior is undesirable, +since FRR's implementation of VRRP needs to be able to receive advertisements +from neighbors while in Backup mode - i.e., while its macvlan devices are in +``protodown on``. If the macvlan devices are instead set to ``bridge`` mode, +all ingress traffic shows up on all interfaces - including ``eth0`` - +regardless of source MAC or any other factor. Consequently, macvlans used by +FRR for VRRP must be set to ``bridge`` mode or the protocol will not function +correctly. + +As for the MAC address assigned to this interface, the last byte of the address +holds the :abbr:`VRID (Virtual Router Identifier)`, in this case ``0x05``. The +second to last byte is ``0x01``, as specified by the RFC for IPv4 operation. +The IPv6 MAC address is be identical except that the second to last byte is +defined to be ``0x02``. Two things to note from this arrangement: + +1. There can only be up to 255 unique Virtual Routers on an interface (only 1 + byte is available for the VRID) +2. IPv4 and IPv6 addresses must be assigned to different macvlan devices, + because they have different MAC addresses + +Finally, take note of the generated IPv6 link local address on the interface. +For interfaces on which VRRP will operate in IPv6 mode, this link local +*cannot* be derived using the usual EUI-64 method. This is because VRRP +advertisements are sent from the link local address of this interface, and VRRP +uses the source address of received advertisements as part of its election +algorithm. If the IPv6 link local of a router is equivalent to the IPv6 link +local in a received advertisement, this can cause both routers to assume the +Master role (very bad). ``ifupdown`` knows to set the ``addrgenmode`` of the +interface properly, but when using ``iproute2`` to create the macvlan devices, +you must be careful to manually specify ``addrgenmode random``. + +A brief note on the Backup state +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +It is worth noting here that an alternate choice for the implementation of the +Backup state, such as removing all the IP addresses assigned to the macvlan +device or deleting their local routes instead of setting the device into +``protodown on``, would allow the protocol to function regardless of whether +the macvlan device(s) are set to ``private`` or ``bridge`` mode. Indeed, the +strange behavior of the kernel macvlan driver in ``private`` mode, whereby it +performs what may be thought of as a sort of interface-level layer 2 "NAT" +based on source MAC, can be traced back to a patch clearly designed to +accommodate a VRRP implementation from a different vendor. However, the +``protodown`` based implementation allows for a configuration model in which +FRR does not dynamically manage the addresses assigned on a system, but instead +just manages interface state. Such a scenario was in mind when this protocol +implementation was initially built, which is why the other choices are not +currently present. Since support for placing macvlan devices into ``protodown`` +was not added to Linux until version 5.1, this also explains the relatively +restrictive kernel versioning requirement. + +In the future other methods of implementing Backup state may be added along +with a configuration knob to choose between them. + +.. _vrrp-interface-configuration: + +Interface Configuration +----------------------- + +Continuing with the example from the previous section, we assume the macvlan +interfaces have been properly configured with the proper MAC addresses and the +IPvX addresses assigned. + +In FRR, a possible VRRPv3 configuration for this interface is: + +.. code-block:: frr + + interface eth0 + vrrp 5 version 3 + vrrp 5 priority 200 + vrrp 5 advertisement-interval 1500 + vrrp 5 ip 10.0.2.16 + vrrp 5 ipv6 2001:0db8::0370:7334 + +VRRP will activate as soon as the first IPvX address configuration line is +encountered. If you do not want this behavior, use the :clicmd:`vrrp (1-255) +shutdown` command, and apply the ``no`` form when you are ready to activate +VRRP. + +At this point executing ``show vrrp`` will display the following: + +.. code-block:: console + + ubuntu-bionic# show vrrp + + Virtual Router ID 5 + Protocol Version 3 + Autoconfigured Yes + Shutdown No + Interface eth0 + VRRP interface (v4) vrrp4-2-5 + VRRP interface (v6) vrrp6-2-5 + Primary IP (v4) 10.0.2.15 + Primary IP (v6) fe80::9b91:7155:bf6a:d386 + Virtual MAC (v4) 00:00:5e:00:01:05 + Virtual MAC (v6) 00:00:5e:00:02:05 + Status (v4) Master + Status (v6) Master + Priority 200 + Effective Priority (v4) 200 + Effective Priority (v6) 200 + Preempt Mode Yes + Accept Mode Yes + Advertisement Interval 1500 ms + Master Advertisement Interval (v4) 1000 ms + Master Advertisement Interval (v6) 1000 ms + Advertisements Tx (v4) 14 + Advertisements Tx (v6) 14 + Advertisements Rx (v4) 0 + Advertisements Rx (v6) 0 + Gratuitous ARP Tx (v4) 1 + Neigh. Adverts Tx (v6) 1 + State transitions (v4) 2 + State transitions (v6) 2 + Skew Time (v4) 210 ms + Skew Time (v6) 210 ms + Master Down Interval (v4) 3210 ms + Master Down Interval (v6) 3210 ms + IPv4 Addresses 1 + .................................. 10.0.2.16 + IPv6 Addresses 1 + .................................. 2001:db8::370:7334 + +At this point, VRRP has sent gratuitous ARP requests for the IPv4 address, +Unsolicited Neighbor Advertisements for the IPv6 address, and has asked Zebra +to send Router Advertisements on its behalf. It is also transmitting VRRPv3 +advertisements on the macvlan interfaces. + +The Primary IP fields are of some interest, as the behavior may be +counterintuitive. These fields show the source address used for VRRP +advertisements. Although VRRPv3 advertisements are always transmitted on the +macvlan interfaces, in the IPv4 case the source address is set to the primary +IPv4 address on the base interface, ``eth0`` in this case. This is a protocol +requirement, and IPv4 VRRP will not function unless the base interface has an +IPv4 address assigned. In the IPv6 case the link local of the macvlan interface +is used. + +If any misconfiguration errors are detected, VRRP for the misconfigured address +family will not come up and the configuration issue will be logged to FRR's +configured logging destination. + +Per the RFC, IPv4 and IPv6 virtual routers are independent of each other. For +instance, it is possible for the IPv4 router to be in Backup state while the +IPv6 router is in Master state; or for either to be completely inoperative +while the other is operative, etc. Instances sharing the same base interface +and VRID are shown together in the show output for conceptual convenience. + +To complete your VRRP deployment, configure other routers on the segment with +the exact same system and FRR configuration as shown above. Provided each +router receives the others' VRRP advertisements, the Master election protocol +will run, one Master will be elected, and the other routers will place their +macvlan interfaces into ``protodown on`` until Master fails or priority values +are changed to favor another router. + +Switching the protocol version to VRRPv2 is accomplished simply by changing +``version 3`` to ``version 2`` in the VRID configuration line. Note that VRRPv2 +does not support IPv6, so any IPv6 configuration will be rejected by FRR when +using VRRPv2. + +.. note:: + + All VRRP routers initially start in Backup state, and wait for the + calculated Master Down Interval to pass before they assume Master status. + This prevents downstream neighbor table churn if another router is already + Master with higher priority, meaning this box will ultimately assume Backup + status once the first advertisement is received. However, if the calculated + Master Down Interval is high and this router is configured such that it will + ultimately assume Master status, then it will take a while for this to + happen. This is a known issue. + + +All interface configuration commands are documented below. + +.. index:: [no] vrrp (1-255) [version (2-3)] +.. clicmd:: [no] vrrp (1-255) [version (2-3)] + + Create a VRRP router with the specified VRID on the interface. Optionally + specify the protocol version. If the protocol version is not specified, the + default is VRRPv3. + +.. index:: [no] vrrp (1-255) advertisement-interval (10-40950) +.. clicmd:: [no] vrrp (1-255) advertisement-interval (10-40950) + + Set the advertisement interval. This is the interval at which VRRP + advertisements will be sent. Values are given in milliseconds, but must be + multiples of 10, as VRRP itself uses centiseconds. + +.. index:: [no] vrrp (1-255) ip A.B.C.D +.. clicmd:: [no] vrrp (1-255) ip A.B.C.D + + Add an IPv4 address to the router. This address must already be configured + on the appropriate macvlan device. Adding an IP address to the router will + implicitly activate the router; see :clicmd:`[no] vrrp (1-255) shutdown` to + override this behavior. + +.. index:: [no] vrrp (1-255) ipv6 X:X::X:X +.. clicmd:: [no] vrrp (1-255) ipv6 X:X::X:X + + Add an IPv6 address to the router. This address must already be configured + on the appropriate macvlan device. Adding an IP address to the router will + implicitly activate the router; see :clicmd:`[no] vrrp (1-255) shutdown` to + override this behavior. + + This command will fail if the protocol version is set to VRRPv2, as VRRPv2 + does not support IPv6. + +.. index:: [no] vrrp (1-255) preempt +.. clicmd:: [no] vrrp (1-255) preempt + + Toggle preempt mode. When enabled, preemption allows Backup routers with + higher priority to take over Master status from the existing Master. Enabled + by default. + +.. index:: [no] vrrp (1-255) priority (1-254) +.. clicmd:: [no] vrrp (1-255) priority (1-254) + + Set the router priority. The router with the highest priority is elected as + the Master. If all routers in the VRRP virtual router are configured with + the same priority, the router with the highest primary IP address is elected + as the Master. Priority value 255 is reserved for the acting Master router. + +.. index:: [no] vrrp (1-255) shutdown +.. clicmd:: [no] vrrp (1-255) shutdown + + Place the router into administrative shutdown. VRRP will not activate for + this router until this command is removed with the ``no`` form. + +.. _vrrp-global-configuration: + +Global Configuration +-------------------- + +Show commands, global defaults and debugging configuration commands. + +.. index:: show vrrp [interface INTERFACE] [(1-255)] [json] +.. clicmd:: show vrrp [interface INTERFACE] [(1-255)] [json] + + Shows VRRP status for some or all configured VRRP routers. Specifying an + interface will only show routers configured on that interface. Specifying a + VRID will only show routers with that VRID. Specifying ``json`` will dump + each router state in a JSON array. + +.. index:: [no] debug vrrp [{protocol|autoconfigure|packets|sockets|ndisc|arp|zebra}] +.. clicmd:: [no] debug vrrp [{protocol|autoconfigure|packets|sockets|ndisc|arp|zebra}] + + Toggle debugging logs for VRRP components. + If no component is specified, debugging for all components are turned on/off. + + protocol + Logs state changes, election protocol decisions, and interface status + changes. + + autoconfigure + Logs actions taken by the autoconfiguration procedures. See + :ref:`vrrp-autoconfiguration`. + + packets + Logs details of ingress and egress packets. Includes packet decodes and + hex dumps. + + sockets + Logs details of socket configuration and initialization. + + ndisc + Logs actions taken by the Neighbor Discovery component of VRRP. + + arp + Logs actions taken by the ARP component of VRRP. + + zebra + Logs communications with Zebra. + +.. index:: [no] vrrp default +.. clicmd:: [no] vrrp default + + Configure defaults for new VRRP routers. These values will not affect + already configured VRRP routers, but will be applied to newly configured + ones. + +.. _vrrp-autoconfiguration: + +Autoconfiguration +----------------- + +In light of the complicated configuration required on the base system before +VRRP can be enabled, FRR has the ability to automatically configure VRRP +sessions by inspecting the interfaces present on the system. Since it is quite +unlikely that macvlan devices with VRRP virtual MACs will exist on systems not +using VRRP, this can be a convenient shortcut to automatically generate FRR +configuration. + +After configuring the interfaces as described in +:ref:`vrrp-system-configuration`, and configuring any defaults you may want, +execute the following command: + +.. index:: [no] vrrp autoconfigure [version (2-3)] +.. clicmd:: [no] vrrp autoconfigure [version (2-3)] + + Generates VRRP configuration based on the interface configuration on the + base system. If the protocol version is not specified, the default is VRRPv3. + Any existing interfaces that are configured properly for VRRP - + i.e. have the correct MAC address, link local address (when required), IPv4 + and IPv6 addresses - are used to create a VRRP router on their parent + interfaces, with VRRP IPvX addresses taken from the addresses assigned to + the macvlan devices. The generated configuration appears in the output of + ``show run``, which can then be modified as needed and written to the config + file. The ``version`` parameter controls the protocol version; if using + VRRPv2, keep in mind that IPv6 is not supported and will not be configured. + +The following configuration is then generated for you: + +.. code-block:: frr + + interface eth0 + vrrp 5 + vrrp 5 ip 10.0.2.16 + vrrp 5 ipv6 2001:db8::370:7334 + + +VRRP is automatically activated. Global defaults, if set, are applied. + +You can then edit this configuration with **vtysh** as needed, and commit it by +writing to the configuration file. + + +Troubleshooting +--------------- + +My virtual routers are not seeing each others' advertisements +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Check: + +- Is your kernel at least 5.1? +- Did you set the macvlan devices to ``bridge`` mode? +- If using IPv4 virtual addresses, does the parent of the macvlan devices have + an IPv4 address? +- If using IPv6 virtual addresses, is ``addrgenmode`` correctly set to + ``random`` and not the default ``eui64``? +- Is a firewall (``iptables``) or policy (``ip rule``) dropping multicast + traffic? +- Do you have unusual ``sysctls`` enabled that could affect the operation of + multicast traffic? +- Are you running in ESXi? See below. + + +My master router is not forwarding traffic +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +There's several possible causes here. If you're sure your configuration is +otherwise correct, the following sysctl likely needs to be turned on: + +.. code-block:: console + + sysctl -w net.ipv4.conf.eth0.ignore_routes_with_linkdown=1 + +Without this setting, it's possible to create topologies in which virtual +routers holding mastership status will not forward traffic. + +Issue reference: https://github.com/FRRouting/frr/issues/7391 + + +My router is running in ESXi and VRRP isn't working +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +By default, ESXi traffic security settings don't allow traffic to egress a VNIC +that does not have the MAC address assigned to the VNIC. This breaks VRRP, +since virtual MACs are the basis of the protocol. + +On ESXi before 6.7, you need to enable Promiscuous Mode in the ESXi settings. +This is a significant security issue in some deployments so make sure you +understand what you're doing. On 6.7 and later, you can use the MAC Learning +feature instead, explained `here +`_. + +Issue reference: https://github.com/FRRouting/frr/issues/5386 diff --git a/doc/user/vtysh.rst b/doc/user/vtysh.rst index 6d569f5fb0..dd754a92ee 100644 --- a/doc/user/vtysh.rst +++ b/doc/user/vtysh.rst @@ -22,6 +22,14 @@ administrator with an external editor. have effect for vtysh) need to be manually updated in :file:`vtysh.conf`. +.. index:: copy FILENAME running-config +.. clicmd:: copy FILENAME running-config + + Process and load a configuration file manually; each line in the + file is read and processed as if it were being typed (or piped) to + vtysh. + + Pager usage =========== diff --git a/doc/user/watchfrr.rst b/doc/user/watchfrr.rst new file mode 100644 index 0000000000..df04a1e375 --- /dev/null +++ b/doc/user/watchfrr.rst @@ -0,0 +1,30 @@ +.. _watchfrr: + +******** +WATCHFRR +******** + +:abbr:`WATCHFRR` is a daemon that handles failed daemon processes and +intelligently restarts them as needed. + +Starting WATCHFRR +================= + +WATCHFRR is started as per normal systemd startup and typically does not +require end users management. + +WATCHFRR commands +================= + +.. index:: show watchfrr +.. clicmd:: show watchfrr + + Give status information about the state of the different daemons being + watched by WATCHFRR + +.. index:: [no] watchfrr ignore DAEMON +.. clicmd:: [no] watchfrr ignore DAEMON + + Tell WATCHFRR to ignore a particular DAEMON if it goes unresponsive. + This is particularly useful when you are a developer and need to debug + a working system, without watchfrr pulling the rug out from under you. diff --git a/doc/user/wecmp_linkbw.rst b/doc/user/wecmp_linkbw.rst new file mode 100644 index 0000000000..6e516bcf9f --- /dev/null +++ b/doc/user/wecmp_linkbw.rst @@ -0,0 +1,298 @@ +.. _wecmp_linkbw: + +Weighted ECMP using BGP link bandwidth +====================================== + +.. _features-of-wecmp-linkbw: + +Overview +-------- + +In normal equal cost multipath (ECMP), the route to a destination has +multiple next hops and traffic is expected to be equally distributed +across these next hops. In practice, flow-based hashing is used so that +all traffic associated with a particular flow uses the same next hop, +and by extension, the same path across the network. + +Weighted ECMP using BGP link bandwidth introduces support for network-wide +unequal cost multipathing (UCMP) to an IP destination. The unequal cost +load balancing is implemented by the forwarding plane based on the weights +associated with the next hops of the IP prefix. These weights are computed +based on the bandwidths of the corresponding multipaths which are encoded +in the ``BGP link bandwidth extended community`` as specified in +[Draft-IETF-idr-link-bandwidth]_. Exchange of an appropriate BGP link +bandwidth value for a prefix across the network results in network-wide +unequal cost multipathing. + +One of the primary use cases of this capability is in the data center when +a service (represented by its anycast IP) has an unequal set of resources +across the regions (e.g., PODs) of the data center and the network itself +provides the load balancing function instead of an external load balancer. +Refer to [Draft-IETF-mohanty-bess-ebgp-dmz]_ and :rfc:`7938` for details +on this use case. This use case is applicable in a pure L3 network as +well as in a EVPN network. + +The traditional use case for BGP link bandwidth to load balance traffic +to the exit routers in the AS based on the bandwidth of their external +eBGP peering links is also supported. + + +Design Principles +----------------- + +Next hop weight computation and usage +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +As described, in UCMP, there is a weight associated with each next hop of an +IP prefix, and traffic is expected to be distributed across the next hops in +proportion to their weight. The weight of a next hop is a simple factoring +of the bandwidth of the corresponding path against the total bandwidth of +all multipaths, mapped to the range 1 to 100. What happens if not all the +paths in the multipath set have link bandwidth associated with them? In such +a case, in adherence to [Draft-IETF-idr-link-bandwidth]_, the behavior +reverts to standard ECMP among all the multipaths, with the link bandwidth +being effectively ignored. + +Note that there is no change to either the BGP best path selection algorithm +or to the multipath computation algorithm; the mapping of link bandwidth to +weight happens at the time of installation of the route in the RIB. + +If data forwarding is implemented by means of the Linux kernel, the next hop’s +weight is used in the hash calculation. The kernel uses the Hash threshold +algorithm and use of the next hop weight is built into it; next hops need +not be expanded to achieve UCMP. UCMP for IPv4 is available in older Linux +kernels too, while UCMP for IPv6 is available from the 4.16 kernel onwards. + +If data forwarding is realized in hardware, common implementations expand +the next hops (i.e., they are repeated) in the ECMP container in proportion +to their weight. For example, if the weights associated with 3 next hops for +a particular route are 50, 25 and 25 and the ECMP container has a size of 16 +next hops, the first next hop will be repeated 8 times and the other 2 next +hops repeated 4 times each. Other implementations are also possible. + +Unequal cost multipath across a network +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +For the use cases listed above, it is not sufficient to support UCMP on just +one router (e.g., egress router), or individually, on multiple routers; UCMP +must be deployed across the entire network. This is achieved by employing the +BGP link-bandwidth extended community. + +At the router which originates the BGP link bandwidth, there has to be user +configuration to trigger it, which is described below. Receiving routers +would use the received link bandwidth from their downstream routers to +determine the next hop weight as described in the earlier section. Further, +if the received link bandwidth is a transitive attribute, it would be +propagated to eBGP peers, with the additional change that if the next hop +is set to oneself, the cumulative link bandwidth of all downstream paths +is propagated to other routers. In this manner, the entire network will +know how to distribute traffic to an anycast service across the network. + +The BGP link-bandwidth extended community is encoded in bytes-per-second. +In the use case where UCMP must be based on the number of paths, a reference +bandwidth of 1 Mbps is used. So, for example, if there are 4 equal cost paths +to an anycast IP, the encoded bandwidth in the extended community will be +500,000. The actual value itself doesn’t matter as long as all routers +originating the link-bandwidth are doing it in the same way. + + +Configuration Guide +------------------- + +The configuration for weighted ECMP using BGP link bandwidth requires +one essential step - using a route-map to inject the link bandwidth +extended community. An additional option is provided to control the +processing of received link bandwidth. + +Injecting link bandwidth into the network +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +At the "entry point" router that is injecting the prefix to which weighted +load balancing must be performed, a route-map must be configured to +attach the link bandwidth extended community. + +For the use case of providing weighted load balancing for an anycast service, +this configuration will typically need to be applied at the TOR or Leaf +router that is connected to servers which provide the anycast service and +the bandwidth would be based on the number of multipaths for the destination. + +For the use case of load balancing to the exit router, the exit router should +be configured with the route map specifying the a bandwidth value that +corresponds to the bandwidth of the link connecting to its eBGP peer in the +adjoining AS. In addition, the link bandwidth extended community must be +explicitly configured to be non-transitive. + +The complete syntax of the route-map set command can be found at +:ref:`bgp-extended-communities-in-route-map` + +This route-map is supported only at two attachment points: +(a) the outbound route-map attached to a peer or peer-group, per address-family +(b) the EVPN advertise route-map used to inject IPv4 or IPv6 unicast routes +into EVPN as type-5 routes. + +Since the link bandwidth origination is done by using a route-map, it can +be constrained to certain prefixes (e.g., only for anycast services) or it +can be generated for all prefixes. Further, when the route-map is used in +the neighbor context, the link bandwidth usage can be constrained to certain +peers only. + +A sample configuration is shown below and illustrates link bandwidth +advertisement towards the "SPINE" peer-group for anycast IPs in the +range 192.168.x.x + +.. code-block:: frr + + ip prefix-list anycast_ip seq 10 permit 192.168.0.0/16 le 32 + route-map anycast_ip permit 10 + match ip address prefix-list anycast_ip + set extcommunity bandwidth num-multipaths + route-map anycast_ip permit 20 + ! + router bgp 65001 + neighbor SPINE peer-group + neighbor SPINE remote-as external + neighbor 172.16.35.1 peer-group SPINE + neighbor 172.16.36.1 peer-group SPINE + ! + address-family ipv4 unicast + network 110.0.0.1/32 + network 192.168.44.1/32 + neighbor SPINE route-map anycast_ip out + exit-address-family + ! + + +Controlling link bandwidth processing on the receiver +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +There is no configuration necessary to process received link bandwidth and +translate it into the weight associated with the corresponding next hop; +that happens by default. If some of the multipaths do not have the link +bandwidth extended community, the default behavior is to revert to normal +ECMP as recommended in [Draft-IETF-idr-link-bandwidth]_. + +The operator can change these behaviors with the following configuration: + +.. index:: bgp bestpath bandwidth +.. clicmd:: bgp bestpath bandwidth + +The different options imply behavior as follows: + +- ignore: Ignore link bandwidth completely for route installation + (i.e., do regular ECMP, not weighted) +- skip-missing: Skip paths without link bandwidth and do UCMP among + the others (if at least some paths have link-bandwidth) +- default-weight-for-missing: Assign a low default weight (value 1) + to paths not having link bandwidth + +This configuration is per BGP instance similar to other BGP route-selection +controls; it operates on both IPv4-unicast and IPv6-unicast routes in that +instance. In an EVPN network, this configuration (if required) should be +implemented in the tenant VRF and is again applicable for IPv4-unicast and +IPv6-unicast, including the ones sourced from EVPN type-5 routes. + +A sample snippet of FRR configuration on a receiver to skip paths without +link bandwidth and do weighted ECMP among the other paths (if some of them +have link bandwidth) is as shown below. + +.. code-block:: frr + + router bgp 65021 + bgp bestpath as-path multipath-relax + bgp bestpath bandwidth skip-missing + neighbor LEAF peer-group + neighbor LEAF remote-as external + neighbor 172.16.35.2 peer-group LEAF + neighbor 172.16.36.2 peer-group LEAF + ! + address-family ipv4 unicast + network 130.0.0.1/32 + exit-address-family + ! + + +Stopping the propagation of the link bandwidth outside a domain +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The link bandwidth extended community will get automatically propagated +with the prefix to EBGP peers, if it is encoded as a transitive attribute +by the originator. If this propagation has to be stopped outside of a +particular domain (e.g., stopped from being propagated to routers outside +of the data center core network), the mechanism available is to disable +the advertisement of all BGP extended communities on the specific peering/s. +In other words, the propagation cannot be blocked just for the link bandwidth +extended community. The configuration to disable all extended communities +can be applied to a peer or peer-group (per address-family). + +Of course, the other common way to stop the propagation of the link bandwidth +outside the domain is to block the prefixes themselves from being advertised +and possibly, announce only an aggregate route. This would be quite common +in a EVPN network. + +BGP link bandwidth and UCMP monitoring & troubleshooting +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Existing operational commands to display the BGP routing table for a specific +prefix will show the link bandwidth extended community also, if present. + +An example of an IPv4-unicast route received with the link bandwidth +attribute from two peers is shown below: + +.. code-block:: frr + + CLI# show bgp ipv4 unicast 192.168.10.1/32 + BGP routing table entry for 192.168.10.1/32 + Paths: (2 available, best #2, table default) + Advertised to non peer-group peers: + l1(swp1) l2(swp2) l3(swp3) l4(swp4) + 65002 + fe80::202:ff:fe00:1b from l2(swp2) (110.0.0.2) + (fe80::202:ff:fe00:1b) (used) + Origin IGP, metric 0, valid, external, multipath, bestpath-from-AS 65002 + Extended Community: LB:65002:125000000 (1000.000 Mbps) + Last update: Thu Feb 20 18:34:16 2020 + + 65001 + fe80::202:ff:fe00:15 from l1(swp1) (110.0.0.1) + (fe80::202:ff:fe00:15) (used) + Origin IGP, metric 0, valid, external, multipath, bestpath-from-AS 65001, best (Older Path) + Extended Community: LB:65001:62500000 (500.000 Mbps) + Last update: Thu Feb 20 18:22:34 2020 + +The weights associated with the next hops of a route can be seen by querying +the RIB for a specific route. + +For example, the next hop weights corresponding to the link bandwidths in the +above example is illustrated below: + +.. code-block:: frr + + spine1# show ip route 192.168.10.1/32 + Routing entry for 192.168.10.1/32 + Known via "bgp", distance 20, metric 0, best + Last update 00:00:32 ago + * fe80::202:ff:fe00:1b, via swp2, weight 66 + * fe80::202:ff:fe00:15, via swp1, weight 33 + +For troubleshooting, existing debug logs ``debug bgp updates``, +``debug bgp bestpath ``, ``debug bgp zebra`` and +``debug zebra kernel`` can be used. + +A debug log snippet when ``debug bgp zebra`` is enabled and a route is +installed by BGP in the RIB with next hop weights is shown below: + +.. code-block:: frr + + 2020-02-29T06:26:19.927754+00:00 leaf1 bgpd[5459]: bgp_zebra_announce: p=192.168.150.1/32, bgp_is_valid_label: 0 + 2020-02-29T06:26:19.928096+00:00 leaf1 bgpd[5459]: Tx route add VRF 33 192.168.150.1/32 metric 0 tag 0 count 2 + 2020-02-29T06:26:19.928289+00:00 leaf1 bgpd[5459]: nhop [1]: 110.0.0.6 if 35 VRF 33 wt 50 RMAC 0a:11:2f:7d:35:20 + 2020-02-29T06:26:19.928479+00:00 leaf1 bgpd[5459]: nhop [2]: 110.0.0.5 if 35 VRF 33 wt 50 RMAC 32:1e:32:a3:6c:bf + 2020-02-29T06:26:19.928668+00:00 leaf1 bgpd[5459]: bgp_zebra_announce: 192.168.150.1/32: announcing to zebra (recursion NOT set) + + +References +---------- + +.. [Draft-IETF-idr-link-bandwidth] +.. [Draft-IETF-mohanty-bess-ebgp-dmz] + diff --git a/doc/user/zebra.rst b/doc/user/zebra.rst index a7bf0c74da..a9ab162b40 100644 --- a/doc/user/zebra.rst +++ b/doc/user/zebra.rst @@ -23,9 +23,12 @@ Besides the common invocation options (:ref:`common-invocation-options`), the Runs in batch mode. *zebra* parses configuration file and terminates immediately. -.. option:: -k, --keep_kernel +.. option:: -K TIME, --graceful_restart TIME - When zebra starts up, don't delete old self inserted routes. + If this option is specified, the graceful restart time is TIME seconds. + Zebra, when started, will read in routes. Those routes that Zebra + identifies that it was the originator of will be swept in TIME seconds. + If no time is specified then we will sweep those routes immediately. .. option:: -r, --retain @@ -55,6 +58,12 @@ Besides the common invocation options (:ref:`common-invocation-options`), the .. seealso:: :ref:`zebra-vrf` +.. option:: -z , --socket + + If this option is supplied on the cli, the path to the zebra + control socket(zapi), is used. This option overrides a -N + option if handed to it on the cli. + .. option:: --v6-rr-semantics The linux kernel is receiving the ability to use the same route @@ -184,18 +193,25 @@ Standard Commands Link Parameters Commands ------------------------ +.. note:: + + At this time, FRR offers partial support for some of the routing + protocol extensions that can be used with MPLS-TE. FRR does not + support a complete RSVP-TE solution currently. + .. index:: link-params .. clicmd:: link-params .. index:: no link-param .. clicmd:: no link-param - Enter into the link parameters sub node. At least 'enable' must be set to - activate the link parameters, and consequently Traffic Engineering on this - interface. MPLS-TE must be enable at the OSPF - (:ref:`ospf-traffic-engineering`) or ISIS (:ref:`isis-traffic-engineering`) - router level in complement to this. Disable link parameters for this - interface. + Enter into the link parameters sub node. At least 'enable' must be + set to activate the link parameters, and consequently routing + information that could be used as part of Traffic Engineering on + this interface. MPLS-TE must be enable at the OSPF + (:ref:`ospf-traffic-engineering`) or ISIS + (:ref:`isis-traffic-engineering`) router level in complement to + this. Disable link parameters for this interface. Under link parameter statement, the following commands set the different TE values: @@ -268,14 +284,6 @@ Link Parameters Commands for InterASv2 link in OSPF (RFC5392). Note that this option is not yet supported for ISIS (RFC5316). -.. index:: table TABLENO -.. clicmd:: table TABLENO - - Select the primary kernel routing table to be used. This only works for - kernels supporting multiple routing tables (like GNU/Linux 2.2.x and later). - After setting TABLENO with this command, static routes defined after this - are added to the specified table. - .. index:: ip nht resolve-via-default .. clicmd:: ip nht resolve-via-default @@ -284,6 +292,62 @@ Link Parameters Commands .. _zebra-vrf: +Administrative Distance +======================= + +Administrative distance allows FRR to make decisions about what routes +should be installed in the rib based upon the originating protocol. +The lowest Admin Distance is the route selected. This is purely a +subjective decision about ordering and care has been taken to choose +the same distances that other routing suites have choosen. + ++------------+-----------+ +| Protocol | Distance | ++------------+-----------+ +| System | 0 | ++------------+-----------+ +| Kernel | 0 | ++------------+-----------+ +| Connect | 0 | ++------------+-----------+ +| Static | 1 | ++------------+-----------+ +| NHRP | 10 | ++------------+-----------+ +| EBGP | 20 | ++------------+-----------+ +| EIGRP | 90 | ++------------+-----------+ +| BABEL | 100 | ++------------+-----------+ +| OSPF | 110 | ++------------+-----------+ +| ISIS | 115 | ++------------+-----------+ +| OPENFABRIC | 115 | ++------------+-----------+ +| RIP | 120 | ++------------+-----------+ +| Table | 150 | ++------------+-----------+ +| SHARP | 150 | ++------------+-----------+ +| IBGP | 200 | ++------------+-----------+ +| PBR | 200 | ++------------+-----------+ + +An admin distance of 255 indicates to Zebra that the route should not be +installed into the Data Plane. Additionally routes with an admin distance +of 255 will not be redistributed. + +Zebra does treat Kernel routes as special case for the purposes of Admin +Distance. Upon learning about a route that is not originated by FRR +we read the metric value as a uint32_t. The top byte of the value +is interpreted as the Administrative Distance and the low three bytes +are read in as the metric. This special case is to facilitate VRF +default routes. + Virtual Routing and Forwarding ============================== @@ -360,6 +424,20 @@ commands in relationship to VRF. Here is an extract of some of those commands: will dump the routing table ``TABLENO`` of the *Linux network namespace* ``VRF``. +.. index:: show ip route vrf VRF tables +.. clicmd:: show ip route vrf VRF tables + + This command will dump the routing tables within the vrf scope. If `vrf all` + is executed, all routing tables will be dumped. + +.. index:: show route summary [vrf VRF] [table TABLENO] [prefix] +.. clicmd:: show route summary [vrf VRF] [table TABLENO] [prefix] + + This command will dump a summary output of the specified VRF and TABLENO + combination. If neither VRF or TABLENO is specified FRR defaults to + the default vrf and default table. If prefix is specified dump the + number of prefix routes. + By using the :option:`-n` option, the *Linux network namespace* will be mapped over the *Zebra* VRF. One nice feature that is possible by handling *Linux network namespace* is the ability to name default VRF. At startup, *Zebra* @@ -585,19 +663,30 @@ kernel. .. clicmd:: ip protocol PROTOCOL route-map ROUTEMAP Apply a route-map filter to routes for the specified protocol. PROTOCOL can - be **any** or one of + be: - - system, - - kernel, + - any, + - babel, + - bgp, - connected, - - static, - - rip, - - ripng, + - eigrp, + - isis, + - kernel, + - nhrp, + - openfabric, - ospf, - ospf6, - - isis, - - bgp, - - hsls. + - rip, + - sharp, + - static, + - ripng, + - table, + - vnc. + + If you choose any as the option that will cause all protocols that are sending + routes to zebra. You can specify a :dfn:`ip protocol PROTOCOL route-map ROUTEMAP` + on a per vrf basis, by entering this command under vrf mode for the vrf you + want to apply the route-map against. .. index:: set src ADDRESS .. clicmd:: set src ADDRESS @@ -619,6 +708,24 @@ that sets the preferred source address, and applies the route-map to all ip protocol rip route-map RM1 +IPv6 example for OSPFv3. + +.. code-block:: frr + + ipv6 prefix-list ANY seq 10 permit any + route-map RM6 permit 10 + match ipv6 address prefix-list ANY + set src 2001:db8:425:1000::3 + + ipv6 protocol ospf6 route-map RM6 + + +.. note:: + + For both IPv4 and IPv6, the IP address has to exist at the point the + route-map is created. Be wary of race conditions if the interface is + not created at startup. On Debian, FRR might start before ifupdown + completes. Consider a reboot test. .. _zebra-fib-push-interface: @@ -647,43 +754,30 @@ these cases, the FIB needs to be maintained reliably in the fast path as well. We refer to the component that programs the forwarding plane (directly or indirectly) as the Forwarding Plane Manager or FPM. -The FIB push interface comprises of a TCP connection between zebra and -the FPM. The connection is initiated by zebra -- that is, the FPM acts -as the TCP server. - .. program:: configure The relevant zebra code kicks in when zebra is configured with the -:option:`--enable-fpm` flag. Zebra periodically attempts to connect to -the well-known FPM port. Once the connection is up, zebra starts -sending messages containing routes over the socket to the FPM. Zebra -sends a complete copy of the forwarding table to the FPM, including -routes that it may have picked up from the kernel. The existing -interaction of zebra with the kernel remains unchanged -- that is, the -kernel continues to receive FIB updates as before. - -The encapsulation header for the messages exchanged with the FPM is -defined by the file :file:`fpm/fpm.h` in the frr tree. The routes -themselves are encoded in Netlink or protobuf format, with Netlink -being the default. - -Protobuf is one of a number of new serialization formats wherein the -message schema is expressed in a purpose-built language. Code for -encoding/decoding to/from the wire format is generated from the -schema. Protobuf messages can be extended easily while maintaining -backward-compatibility with older code. Protobuf has the following -advantages over Netlink: - -- Code for serialization/deserialization is generated automatically. This - reduces the likelihood of bugs, allows third-party programs to be integrated - quickly, and makes it easy to add fields. -- The message format is not tied to an OS (Linux), and can be evolved - independently. - -As mentioned before, zebra encodes routes sent to the FPM in Netlink -format by default. The format can be controlled via the FPM module's -load-time option to zebra, which currently takes the values `Netlink` -and `protobuf`. +:option:`--enable-fpm` flag and started with the module (``-M fpm`` +or ``-M dplane_fpm_nl``). + +.. note:: + + The ``fpm`` implementation attempts to connect to ``127.0.0.1`` port ``2620`` + by default without configurations. The ``dplane_fpm_nl`` only attempts to + connect to a server if configured. + +Zebra periodically attempts to connect to the well-known FPM port (``2620``). +Once the connection is up, zebra starts sending messages containing routes +over the socket to the FPM. Zebra sends a complete copy of the forwarding +table to the FPM, including routes that it may have picked up from the kernel. +The existing interaction of zebra with the kernel remains unchanged -- that +is, the kernel continues to receive FIB updates as before. + +The default FPM message format is netlink, however it can be controlled +with the module load-time option. The modules accept the following options: + +- ``fpm``: ``netlink`` and ``protobuf``. +- ``dplane_fpm_nl``: none, it only implements netlink. The zebra FPM interface uses replace semantics. That is, if a 'route add' message for a prefix is followed by another 'route add' message, @@ -693,6 +787,169 @@ replaces the information sent in the first message. If the connection to the FPM goes down for some reason, zebra sends the FPM a complete copy of the forwarding table(s) when it reconnects. +For more details on the implementation, please read the developer's manual FPM +section. + +FPM Commands +============ + +``fpm`` implementation +---------------------- + +.. index:: fpm connection ip A.B.C.D port (1-65535) +.. clicmd:: fpm connection ip A.B.C.D port (1-65535) + + Configure ``zebra`` to connect to a different FPM server than + ``127.0.0.1`` port ``2620``. + + +.. index:: no fpm connection ip A.B.C.D port (1-65535) +.. clicmd:: no fpm connection ip A.B.C.D port (1-65535) + + Configure ``zebra`` to connect to the default FPM server at ``127.0.0.1`` + port ``2620``. + + +.. index:: show zebra fpm stats +.. clicmd:: show zebra fpm stats + + Shows the FPM statistics. + + Sample output: + + :: + + Counter Total Last 10 secs + + connect_calls 3 2 + connect_no_sock 0 0 + read_cb_calls 2 2 + write_cb_calls 2 0 + write_calls 1 0 + partial_writes 0 0 + max_writes_hit 0 0 + t_write_yields 0 0 + nop_deletes_skipped 6 0 + route_adds 5 0 + route_dels 0 0 + updates_triggered 11 0 + redundant_triggers 0 0 + dests_del_after_update 0 0 + t_conn_down_starts 0 0 + t_conn_down_dests_processed 0 0 + t_conn_down_yields 0 0 + t_conn_down_finishes 0 0 + t_conn_up_starts 1 0 + t_conn_up_dests_processed 11 0 + t_conn_up_yields 0 0 + t_conn_up_aborts 0 0 + t_conn_up_finishes 1 0 + + +.. index:: clear zebra fpm stats +.. clicmd:: clear zebra fpm stats + + Reset statistics related to the zebra code that interacts with the + optional Forwarding Plane Manager (FPM) component. + + +``dplane_fpm_nl`` implementation +-------------------------------- + +.. index:: fpm address [port (1-65535)] +.. clicmd:: fpm address [port (1-65535)] + + Configures the FPM server address. Once configured ``zebra`` will attempt + to connect to it immediately. + + +.. index:: no fpm address [ [port (1-65535)]] +.. clicmd:: no fpm address [ [port (1-65535)]] + + Disables FPM entirely. ``zebra`` will close any current connections and + will not attempt to connect to it anymore. + + +.. index:: fpm use-next-hop-groups +.. clicmd:: fpm use-next-hop-groups + + Use the new netlink messages ``RTM_NEWNEXTHOP`` / ``RTM_DELNEXTHOP`` to + group repeated route next hop information. + + +.. index:: no fpm use-next-hop-groups +.. clicmd:: no fpm use-next-hop-groups + + Use the old known FPM behavior of including next hop information in the + route (e.g. ``RTM_NEWROUTE``) messages. + + +.. index:: show fpm counters [json] +.. clicmd:: show fpm counters [json] + + Show the FPM statistics (plain text or JSON formatted). + + Sample output: + + :: + + FPM counters + ============ + Input bytes: 0 + Output bytes: 308 + Output buffer current size: 0 + Output buffer peak size: 308 + Connection closes: 0 + Connection errors: 0 + Data plane items processed: 0 + Data plane items enqueued: 0 + Data plane items queue peak: 0 + Buffer full hits: 0 + User FPM configurations: 1 + User FPM disable requests: 0 + + +.. index:: clear fpm counters +.. clicmd:: clear fpm counters + + Reset statistics related to the zebra code that interacts with the + optional Forwarding Plane Manager (FPM) component. + + +.. _zebra-dplane: + +Dataplane Commands +================== + +The zebra dataplane subsystem provides a framework for FIB +programming. Zebra uses the dataplane to program the local kernel as +it makes changes to objects such as IP routes, MPLS LSPs, and +interface IP addresses. The dataplane runs in its own pthread, in +order to off-load work from the main zebra pthread. + + +.. index:: show zebra dplane [detailed] +.. clicmd:: show zebra dplane [detailed] + + Display statistics about the updates and events passing through the + dataplane subsystem. + + +.. index:: show zebra dplane providers +.. clicmd:: show zebra dplane providers + + Display information about the running dataplane plugins that are + providing updates to a FIB. By default, the local kernel plugin is + present. + + +.. index:: zebra dplane limit [NUMBER] +.. clicmd:: zebra dplane limit [NUMBER] + + Configure the limit on the number of pending updates that are + waiting to be processed by the dataplane pthread. + + zebra Terminal Mode Commands ============================ @@ -716,8 +973,22 @@ zebra Terminal Mode Commands .. index:: show ipv6 route .. clicmd:: show ipv6 route -.. index:: show interface -.. clicmd:: show interface +.. index:: show [ip|ipv6] route [PREFIX] [nexthop-group] +.. clicmd:: show [ip|ipv6] route [PREFIX] [nexthop-group] + + Display detailed information about a route. If [nexthop-group] is + included, it will display the nexthop group ID the route is using as well. + +.. index:: show interface [NAME] [{vrf VRF|brief}] [nexthop-group] +.. clicmd:: show interface [NAME] [{vrf VRF|brief}] [nexthop-group] + +.. index:: show interface [NAME] [{vrf all|brief}] [nexthop-group] +.. clicmd:: show interface [NAME] [{vrf all|brief}] [nexthop-group] + + Display interface information. If no extra information is added, it will + dump information on all interfaces. If [NAME] is specified, it will display + detailed information about that single interface. If [nexthop-group] is + specified, it will display nexthop groups pointing out that interface. .. index:: show ip prefix-list [NAME] .. clicmd:: show ip prefix-list [NAME] @@ -728,15 +999,15 @@ zebra Terminal Mode Commands .. index:: show ip protocol .. clicmd:: show ip protocol -.. index:: show ipforward -.. clicmd:: show ipforward +.. index:: show ip forward +.. clicmd:: show ip forward Display whether the host's IP forwarding function is enabled or not. Almost any UNIX kernel can be configured with IP forwarding disabled. If so, the box can't work as a router. -.. index:: show ipv6forward -.. clicmd:: show ipv6forward +.. index:: show ipv6 forward +.. clicmd:: show ipv6 forward Display whether the host's IP v6 forwarding is enabled or not. @@ -746,15 +1017,67 @@ zebra Terminal Mode Commands Display various statistics related to the installation and deletion of routes, neighbor updates, and LSP's into the kernel. -.. index:: show zebra fpm stats -.. clicmd:: show zebra fpm stats +.. index:: show zebra client [summary] +.. clicmd:: show zebra client [summary] - Display statistics related to the zebra code that interacts with the - optional Forwarding Plane Manager (FPM) component. + Display statistics about clients that are connected to zebra. This is + useful for debugging and seeing how much data is being passed between + zebra and it's clients. If the summary form of the command is choosen + a table is displayed with shortened information. -.. index:: clear zebra fpm stats -.. clicmd:: clear zebra fpm stats +.. index:: show zebra router table summary +.. clicmd:: show zebra router table summary - Reset statistics related to the zebra code that interacts with the - optional Forwarding Plane Manager (FPM) component. + Display summarized data about tables created, their afi/safi/tableid + and how many routes each table contains. Please note this is the + total number of route nodes in the table. Which will be higher than + the actual number of routes that are held. + +.. index:: show nexthop-group rib [ID] [vrf NAME] [singleton [ip|ip6]] +.. clicmd:: show nexthop-group rib [ID] [vrf NAME] + + Display nexthop groups created by zebra. The [vrf NAME] option + is only meaningful if you have started zebra with the --vrfwnetns + option as that nexthop groups are per namespace in linux. + If you specify singleton you would like to see the singleton + nexthop groups that do have an afi. + + +Router-id +========= + +Many routing protocols require a router-id to be configured. To have a +consistent router-id across all daemons, the following commands are available +to configure and display the router-id: + +.. index:: [no] [ip] router-id A.B.C.D +.. clicmd:: [no] [ip] router-id A.B.C.D + + Allow entering of the router-id. This command also works under the + vrf subnode, to allow router-id's per vrf. + +.. index:: [no] [ip] router-id A.B.C.D vrf NAME +.. clicmd:: [no] [ip] router-id A.B.C.D vrf NAME + + Configure the router-id of this router from the configure NODE. + A show run of this command will display the router-id command + under the vrf sub node. This command is deprecated and will + be removed at some point in time in the future. + +.. index:: show [ip] router-id [vrf NAME] +.. clicmd:: show [ip] router-id [vrf NAME] + + Display the user configured router-id. + +For protocols requiring an IPv6 router-id, the following commands are available: + +.. index:: [no] ipv6 router-id X:X::X:X +.. clicmd:: [no] ipv6 router-id X:X::X:X + + Configure the IPv6 router-id of this router. Like its IPv4 counterpart, + this command works under the vrf subnode, to allow router-id's per vrf. + +.. index:: show ipv6 router-id [vrf NAME] +.. clicmd:: show ipv6 router-id [vrf NAME] + Display the user configured IPv6 router-id. diff --git a/docker/alpine/Dockerfile b/docker/alpine/Dockerfile index 815983a394..126710f8c2 100644 --- a/docker/alpine/Dockerfile +++ b/docker/alpine/Dockerfile @@ -1,15 +1,15 @@ # This stage builds a dist tarball from the source -FROM alpine:edge as source-builder +FROM alpine:latest as source-builder RUN mkdir -p /src/alpine COPY alpine/APKBUILD.in /src/alpine RUN source /src/alpine/APKBUILD.in \ - && echo 'http://dl-cdn.alpinelinux.org/alpine/edge/testing' >> /etc/apk/repositories \ && apk add \ --no-cache \ --update-cache \ $makedepends \ - gzip + gzip \ + && pip install pytest COPY . /src ARG PKGVER @@ -21,14 +21,15 @@ RUN cd /src \ && make dist # This stage builds an apk from the dist tarball -FROM alpine:edge as alpine-builder +FROM alpine:latest as alpine-builder # Don't use nocache here so that abuild can use the cache -RUN echo 'http://dl-cdn.alpinelinux.org/alpine/edge/testing' >> /etc/apk/repositories \ - && apk add \ +RUN apk add \ --update-cache \ abuild \ alpine-conf \ alpine-sdk \ + py-pip \ + && pip install pytest \ && setup-apkcache /var/cache/apk \ && mkdir -p /pkgs/apk \ && echo 'builder ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers @@ -39,14 +40,14 @@ USER builder RUN cd /dist \ && abuild-keygen -a -n \ && abuild checksum \ + && git init \ && abuild -r -P /pkgs/apk # This stage installs frr from the apk -FROM alpine:edge +FROM alpine:latest RUN mkdir -p /pkgs/apk COPY --from=alpine-builder /pkgs/apk/ /pkgs/apk/ -RUN echo 'http://dl-cdn.alpinelinux.org/alpine/edge/testing' >> /etc/apk/repositories \ - && apk add \ +RUN apk add \ --no-cache \ --update-cache \ tini \ diff --git a/docker/alpine/docker-start b/docker/alpine/docker-start index 52cfb664ce..3f7737d3bf 100755 --- a/docker/alpine/docker-start +++ b/docker/alpine/docker-start @@ -5,7 +5,7 @@ set -e ## # For volume mounts... ## -chown -R frr:frr /etc/frr +chown -R frr:frr /etc/frr || true /usr/lib/frr/frrinit.sh start # Sleep forever diff --git a/docker/centos-7/Dockerfile b/docker/centos-7/Dockerfile new file mode 100644 index 0000000000..a92326fcf3 --- /dev/null +++ b/docker/centos-7/Dockerfile @@ -0,0 +1,43 @@ +# This stage builds an rpm from the source +FROM centos:centos7 as centos-7-builder +RUN yum install -y epel-release +RUN yum install -y rpm-build autoconf automake libtool make \ + readline-devel texinfo net-snmp-devel groff pkgconfig \ + json-c-devel pam-devel bison flex pytest c-ares-devel \ + python3-devel python3-sphinx systemd-devel libcap-devel \ + https://ci1.netdef.org/artifact/LIBYANG-LY1REL/shared/build-4/CentOS-7-x86_64-Packages/libyang1-1.0.184-0.x86_64.rpm \ + https://ci1.netdef.org/artifact/LIBYANG-LY1REL/shared/build-4/CentOS-7-x86_64-Packages/libyang-devel-1.0.184-0.x86_64.rpm \ + https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-110/CentOS-7-x86_64-Packages/librtr-0.7.0-1.el7.centos.x86_64.rpm \ + https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-110/CentOS-7-x86_64-Packages/librtr-devel-0.7.0-1.el7.centos.x86_64.rpm + +COPY . /src +ARG PKGVER + +RUN echo '%_smp_mflags %( echo "-j$(/usr/bin/getconf _NPROCESSORS_ONLN)"; )' >> /root/.rpmmacros \ + && cd /src \ + && ./bootstrap.sh \ + && ./configure \ + --enable-rpki \ + --enable-numeric-version \ + --with-pkg-extra-version="_git$PKGVER" \ + && make dist \ + && cd / \ + && mkdir -p /rpmbuild/{SOURCES,SPECS} \ + && cp /src/frr*.tar.gz /rpmbuild/SOURCES \ + && cp /src/redhat/frr.spec /rpmbuild/SPECS \ + && rpmbuild \ + --define "_topdir /rpmbuild" \ + -ba /rpmbuild/SPECS/frr.spec + +# This stage installs frr from the rpm +FROM centos:centos7 +RUN mkdir -p /pkgs/rpm \ + && yum install -y https://ci1.netdef.org/artifact/LIBYANG-LY1REL/shared/build-4/CentOS-7-x86_64-Packages/libyang1-1.0.184-0.x86_64.rpm \ + https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-110/CentOS-7-x86_64-Packages/librtr-0.7.0-1.el7.centos.x86_64.rpm + +COPY --from=centos-7-builder /rpmbuild/RPMS/ /pkgs/rpm/ + +RUN yum install -y /pkgs/rpm/*/*.rpm \ + && rm -rf /pkgs +COPY docker/centos-7/docker-start /usr/lib/frr/docker-start +ENTRYPOINT [ "/usr/lib/frr/docker-start" ] diff --git a/docker/centos-7/build.sh b/docker/centos-7/build.sh new file mode 100755 index 0000000000..b3022d7c78 --- /dev/null +++ b/docker/centos-7/build.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +set -e + +## +# Package version needs to be decimal +## +GITREV="$(git rev-parse --short=10 HEAD)" +PKGVER="$(printf '%u\n' 0x$GITREV)" + +mkdir -p docker/centos-7/pkgs +docker build \ + --file=docker/centos-7/Dockerfile \ + --build-arg="PKGVER=$PKGVER" \ + --tag="frr:centos-7-builder-$GITREV" \ + --target=centos-7-builder \ + . + +# Copy RPM package from container to host +CONTAINER_ID="$(docker create "frr:centos-7-builder-$GITREV")" +docker cp "${CONTAINER_ID}:/rpmbuild/RPMS/x86_64/" docker/centos-7/pkgs +docker rm "${CONTAINER_ID}" + +docker build \ + --cache-from="frr:centos-7-builder-$GITREV" \ + --file=docker/centos-7/Dockerfile \ + --build-arg="PKGVER=$PKGVER" \ + --tag="frr:centos-7-$GITREV" \ + . + +docker rmi "frr:centos-7-builder-$GITREV" diff --git a/docker/centos-7/docker-start b/docker/centos-7/docker-start new file mode 100755 index 0000000000..a3913245b6 --- /dev/null +++ b/docker/centos-7/docker-start @@ -0,0 +1,12 @@ +#!/bin/sh + +set -e + +## +# Change owner for docker volume mount +## +chown -R frr:frr /etc/frr +/usr/lib/frr/frrinit.sh start + +# Sleep forever +exec tail -f /dev/null diff --git a/docker/centos-8/Dockerfile b/docker/centos-8/Dockerfile new file mode 100644 index 0000000000..7ed7948927 --- /dev/null +++ b/docker/centos-8/Dockerfile @@ -0,0 +1,44 @@ +# This stage builds an rpm from the source +FROM centos:centos8 as centos-8-builder + +RUN dnf install --enablerepo=powertools -y rpm-build git autoconf pcre-devel \ + automake libtool make readline-devel texinfo net-snmp-devel pkgconfig \ + groff pkgconfig json-c-devel pam-devel bison flex python3-pytest \ + c-ares-devel python3-devel python3-sphinx systemd-devel libcap-devel platform-python-devel \ + https://ci1.netdef.org/artifact/LIBYANG-LY1REL/shared/build-4/CentOS-8-x86_64-Packages/libyang1-1.0.184-0.x86_64.rpm \ + https://ci1.netdef.org/artifact/LIBYANG-LY1REL/shared/build-4/CentOS-8-x86_64-Packages/libyang-devel-1.0.184-0.x86_64.rpm \ + https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-110/CentOS-7-x86_64-Packages/librtr-0.7.0-1.el7.centos.x86_64.rpm \ + https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-110/CentOS-7-x86_64-Packages/librtr-devel-0.7.0-1.el7.centos.x86_64.rpm + +COPY . /src + +ARG PKGVER + +RUN echo '%_smp_mflags %( echo "-j$(/usr/bin/getconf _NPROCESSORS_ONLN)"; )' >> /root/.rpmmacros \ + && cd /src \ + && ./bootstrap.sh \ + && ./configure \ + --enable-rpki \ + --enable-numeric-version \ + --with-pkg-extra-version="_git$PKGVER" \ + && make dist \ + && cd / \ + && mkdir -p /rpmbuild/{SOURCES,SPECS} \ + && cp /src/frr*.tar.gz /rpmbuild/SOURCES \ + && cp /src/redhat/frr.spec /rpmbuild/SPECS \ + && rpmbuild \ + --define "_topdir /rpmbuild" \ + -ba /rpmbuild/SPECS/frr.spec + +# This stage installs frr from the rpm +FROM centos:centos8 +RUN mkdir -p /pkgs/rpm \ + && yum install -y https://ci1.netdef.org/artifact/LIBYANG-LY1REL/shared/build-4/CentOS-8-x86_64-Packages/libyang1-1.0.184-0.x86_64.rpm \ + https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-110/CentOS-7-x86_64-Packages/librtr-0.7.0-1.el7.centos.x86_64.rpm + +COPY --from=centos-8-builder /rpmbuild/RPMS/ /pkgs/rpm/ + +RUN yum install -y /pkgs/rpm/*/*.rpm \ + && rm -rf /pkgs +COPY docker/centos-8/docker-start /usr/lib/frr/docker-start +ENTRYPOINT [ "/usr/lib/frr/docker-start" ] diff --git a/docker/centos-8/build.sh b/docker/centos-8/build.sh new file mode 100755 index 0000000000..4a9918486c --- /dev/null +++ b/docker/centos-8/build.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +set -e + +## +# Package version needs to be decimal +## +GITREV="$(git rev-parse --short=10 HEAD)" +PKGVER="$(printf '%u\n' 0x$GITREV)" + +mkdir -p docker/centos-8/pkgs +docker build \ + --file=docker/centos-8/Dockerfile \ + --build-arg="PKGVER=$PKGVER" \ + --tag="frr:centos-8-builder-$GITREV" \ + --target=centos-8-builder \ + . + +# Copy RPM package from container to host +CONTAINER_ID="$(docker create "frr:centos-8-builder-$GITREV")" +docker cp "${CONTAINER_ID}:/rpmbuild/RPMS/x86_64/" docker/centos-8/pkgs +docker rm "${CONTAINER_ID}" + +docker build \ + --cache-from="frr:centos-8-builder-$GITREV" \ + --file=docker/centos-8/Dockerfile \ + --build-arg="PKGVER=$PKGVER" \ + --tag="frr:centos-8-$GITREV" \ + . + +docker rmi "frr:centos-8-builder-$GITREV" diff --git a/docker/centos-8/docker-start b/docker/centos-8/docker-start new file mode 100755 index 0000000000..935b22209e --- /dev/null +++ b/docker/centos-8/docker-start @@ -0,0 +1,9 @@ +#!/bin/sh + +set -e + +chown -R frr:frr /etc/frr +/usr/lib/frr/frrinit.sh start + +# Sleep forever +exec tail -f /dev/null diff --git a/docker/debian/Dockerfile b/docker/debian/Dockerfile index 4f192ec33e..3f830348bc 100644 --- a/docker/debian/Dockerfile +++ b/docker/debian/Dockerfile @@ -1,10 +1,20 @@ -FROM debian:stretch +FROM debian:buster MAINTAINER Rob Gil (rob@rem5.com) -RUN apt-get update -RUN apt-get install -y libpcre3-dev apt-transport-https ca-certificates curl wget logrotate \ - libc-ares2 libjson-c3 vim systemd procps -RUN curl -sLO https://ci1.netdef.org/artifact/LIBYANG-YANGRELEASE/shared/build-1/Debian-9-x86_64-Packages/libyang_0.16.46_amd64.deb && dpkg -i libyang_0.16.46_amd64.deb -RUN curl -sLO https://github.com/FRRouting/frr/releases/download/frr-6.0.2/frr_6.0.2-0.deb9u1_amd64.deb && dpkg -i frr_6.0.2-0.deb9u1_amd64.deb -ADD daemons /etc/frr/daemons + +ENV DEBIAN_FRONTEND noninteractive +ENV APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=DontWarn + +RUN apt-get update && \ + apt-get install -y libpcre3-dev apt-transport-https ca-certificates curl wget logrotate \ + libc-ares2 libjson-c3 vim procps libreadline7 gnupg2 lsb-release apt-utils && \ + rm -rf /var/lib/apt/lists/* + +RUN curl -s https://deb.frrouting.org/frr/keys.asc | apt-key add - +RUN echo deb https://deb.frrouting.org/frr $(lsb_release -s -c) frr-stable | tee -a /etc/apt/sources.list.d/frr.list + +RUN apt-get update && \ + apt-get install -y frr frr-pythontools && \ + rm -rf /var/lib/apt/lists/* + ADD docker-start /usr/sbin/docker-start ENTRYPOINT ["/usr/sbin/docker-start"] diff --git a/docker/debian/README.md b/docker/debian/README.md index b10d696a78..3c1209bc97 100644 --- a/docker/debian/README.md +++ b/docker/debian/README.md @@ -1,14 +1,17 @@ -# Debian9 Docker -This is a binary docker container build of debian9. +# Debian 10 Docker + +This is a binary docker container build of Debian 10 (buster) with FRR. # Build + ``` -docker build --rm -t frr:6.0.2 . +docker build -t frr-debian:latest . ``` # Running + ``` -docker run -itd --privileged --name frr frr:latest +docker run -itd --privileged --name frr frr-debian:latest ``` vtysh diff --git a/docker/debian/daemons b/docker/debian/daemons deleted file mode 100644 index ed4d98e1f8..0000000000 --- a/docker/debian/daemons +++ /dev/null @@ -1,65 +0,0 @@ -# This file tells the frr package which daemons to start. -# -# Sample configurations for these daemons can be found in -# /usr/share/doc/frr/examples/. -# -# ATTENTION: -# -# When activation a daemon at the first time, a config file, even if it is -# empty, has to be present *and* be owned by the user and group "frr", else -# the daemon will not be started by /etc/init.d/frr. The permissions should -# be u=rw,g=r,o=. -# When using "vtysh" such a config file is also needed. It should be owned by -# group "frrvty" and set to ug=rw,o= though. Check /etc/pam.d/frr, too. -# -# The watchfrr and zebra daemons are always started. -# -bgpd=yes -ospfd=no -ospf6d=no -ripd=no -ripngd=no -isisd=no -pimd=no -ldpd=no -nhrpd=no -eigrpd=no -babeld=no -sharpd=no -pbrd=no -bfdd=no -fabricd=no - -# -# If this option is set the /etc/init.d/frr script automatically loads -# the config via "vtysh -b" when the servers are started. -# Check /etc/pam.d/frr if you intend to use "vtysh"! -# -vtysh_enable=yes -zebra_options=" -A 127.0.0.1 -s 90000000" -bgpd_options=" -A 127.0.0.1" -ospfd_options=" -A 127.0.0.1" -ospf6d_options=" -A ::1" -ripd_options=" -A 127.0.0.1" -ripngd_options=" -A ::1" -isisd_options=" -A 127.0.0.1" -pimd_options=" -A 127.0.0.1" -ldpd_options=" -A 127.0.0.1" -nhrpd_options=" -A 127.0.0.1" -eigrpd_options=" -A 127.0.0.1" -babeld_options=" -A 127.0.0.1" -sharpd_options=" -A 127.0.0.1" -pbrd_options=" -A 127.0.0.1" -staticd_options="-A 127.0.0.1" -bfdd_options=" -A 127.0.0.1" -fabricd_options="-A 127.0.0.1" - -# The list of daemons to watch is automatically generated by the init script. -watchfrr_options="-r '/usr/lib/frr/watchfrr.sh restart %s' -s '/usr/lib/frr/watchfrr.sh start %s' -k '/usr/lib/frr/watchfrr.sh stop %s'" - -# for debugging purposes, you can specify a "wrap" command to start instead -# of starting the daemon directly, e.g. to use valgrind on ospfd: -# ospfd_wrap="/usr/bin/valgrind" -# or you can use "all_wrap" for all daemons, e.g. to use perf record: -# all_wrap="/usr/bin/perf record --call-graph -" -# the normal daemon command is added to this at the end. diff --git a/eigrpd/eigrp_cli.c b/eigrpd/eigrp_cli.c new file mode 100644 index 0000000000..0b4774ba42 --- /dev/null +++ b/eigrpd/eigrp_cli.c @@ -0,0 +1,938 @@ +/* + * EIGRP daemon CLI implementation. + * + * Copyright (C) 2019 Network Device Education Foundation, Inc. ("NetDEF") + * Rafael Zalamena + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#include + +#include "lib/command.h" +#include "lib/log.h" +#include "lib/northbound_cli.h" + +#include "eigrp_structs.h" +#include "eigrpd.h" +#include "eigrp_zebra.h" + +#ifndef VTYSH_EXTRACT_PL +#include "eigrpd/eigrp_cli_clippy.c" +#endif /* VTYSH_EXTRACT_PL */ + +/* + * XPath: /frr-eigrpd:eigrpd/instance + */ +DEFPY_YANG_NOSH( + router_eigrp, + router_eigrp_cmd, + "router eigrp (1-65535)$as [vrf NAME]", + ROUTER_STR + EIGRP_STR + AS_STR + VRF_CMD_HELP_STR) +{ + char xpath[XPATH_MAXLEN]; + int rv; + + snprintf(xpath, sizeof(xpath), + "/frr-eigrpd:eigrpd/instance[asn='%s'][vrf='%s']", + as_str, vrf ? vrf : VRF_DEFAULT_NAME); + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + rv = nb_cli_apply_changes(vty, NULL); + if (rv == CMD_SUCCESS) + VTY_PUSH_XPATH(EIGRP_NODE, xpath); + + return rv; +} + +DEFPY_YANG( + no_router_eigrp, + no_router_eigrp_cmd, + "no router eigrp (1-65535)$as [vrf NAME]", + NO_STR + ROUTER_STR + EIGRP_STR + AS_STR + VRF_CMD_HELP_STR) +{ + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "/frr-eigrpd:eigrpd/instance[asn='%s'][vrf='%s']", + as_str, vrf ? vrf : VRF_DEFAULT_NAME); + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +void eigrp_cli_show_header(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + const char *asn = yang_dnode_get_string(dnode, "./asn"); + const char *vrf = yang_dnode_get_string(dnode, "./vrf"); + + vty_out(vty, "router eigrp %s", asn); + if (strcmp(vrf, VRF_DEFAULT_NAME)) + vty_out(vty, " vrf %s", vrf); + vty_out(vty, "\n"); +} + +void eigrp_cli_show_end_header(struct vty *vty, struct lyd_node *dnode) +{ + vty_out(vty, "!\n"); +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/router-id + */ +DEFPY_YANG( + eigrp_router_id, + eigrp_router_id_cmd, + "eigrp router-id A.B.C.D$addr", + EIGRP_STR + "Router ID for this EIGRP process\n" + "EIGRP Router-ID in IP address format\n") +{ + nb_cli_enqueue_change(vty, "./router-id", NB_OP_MODIFY, addr_str); + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + no_eigrp_router_id, + no_eigrp_router_id_cmd, + "no eigrp router-id [A.B.C.D]", + NO_STR + EIGRP_STR + "Router ID for this EIGRP process\n" + "EIGRP Router-ID in IP address format\n") +{ + nb_cli_enqueue_change(vty, "./router-id", NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +void eigrp_cli_show_router_id(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + const char *router_id = yang_dnode_get_string(dnode, NULL); + + vty_out(vty, " eigrp router-id %s\n", router_id); +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/passive-interface + */ +DEFPY_YANG( + eigrp_passive_interface, + eigrp_passive_interface_cmd, + "[no] passive-interface IFNAME", + NO_STR + "Suppress routing updates on an interface\n" + "Interface to suppress on\n") +{ + if (no) + nb_cli_enqueue_change(vty, "./passive-interface", + NB_OP_DESTROY, ifname); + else + nb_cli_enqueue_change(vty, "./passive-interface", + NB_OP_CREATE, ifname); + + return nb_cli_apply_changes(vty, NULL); +} + +void eigrp_cli_show_passive_interface(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + const char *ifname = yang_dnode_get_string(dnode, NULL); + + vty_out(vty, " passive-interface %s\n", ifname); +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/active-time + */ +DEFPY_YANG( + eigrp_timers_active, + eigrp_timers_active_cmd, + "timers active-time <(1-65535)$timer|disabled$disabled>", + "Adjust routing timers\n" + "Time limit for active state\n" + "Active state time limit in seconds\n" + "Disable time limit for active state\n") +{ + if (disabled) + nb_cli_enqueue_change(vty, "./active-time", NB_OP_MODIFY, "0"); + else + nb_cli_enqueue_change(vty, "./active-time", + NB_OP_MODIFY, timer_str); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + no_eigrp_timers_active, + no_eigrp_timers_active_cmd, + "no timers active-time [<(1-65535)|disabled>]", + NO_STR + "Adjust routing timers\n" + "Time limit for active state\n" + "Active state time limit in seconds\n" + "Disable time limit for active state\n") +{ + nb_cli_enqueue_change(vty, "./active-time", NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +void eigrp_cli_show_active_time(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + const char *timer = yang_dnode_get_string(dnode, NULL); + + vty_out(vty, " timers active-time %s\n", timer); +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/variance + */ +DEFPY_YANG( + eigrp_variance, + eigrp_variance_cmd, + "variance (1-128)$variance", + "Control load balancing variance\n" + "Metric variance multiplier\n") +{ + nb_cli_enqueue_change(vty, "./variance", NB_OP_MODIFY, variance_str); + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + no_eigrp_variance, + no_eigrp_variance_cmd, + "no variance [(1-128)]", + NO_STR + "Control load balancing variance\n" + "Metric variance multiplier\n") +{ + nb_cli_enqueue_change(vty, "./variance", NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +void eigrp_cli_show_variance(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + const char *variance = yang_dnode_get_string(dnode, NULL); + + vty_out(vty, " variance %s\n", variance); +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/maximum-paths + */ +DEFPY_YANG( + eigrp_maximum_paths, + eigrp_maximum_paths_cmd, + "maximum-paths (1-32)$maximum_paths", + "Forward packets over multiple paths\n" + "Number of paths\n") +{ + nb_cli_enqueue_change(vty, "./maximum-paths", NB_OP_MODIFY, + maximum_paths_str); + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + no_eigrp_maximum_paths, + no_eigrp_maximum_paths_cmd, + "no maximum-paths [(1-32)]", + NO_STR + "Forward packets over multiple paths\n" + "Number of paths\n") +{ + nb_cli_enqueue_change(vty, "./maximum-paths", NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +void eigrp_cli_show_maximum_paths(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + const char *maximum_paths = yang_dnode_get_string(dnode, NULL); + + vty_out(vty, " maximum-paths %s\n", maximum_paths); +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/metric-weights/K1 + * XPath: /frr-eigrpd:eigrpd/instance/metric-weights/K2 + * XPath: /frr-eigrpd:eigrpd/instance/metric-weights/K3 + * XPath: /frr-eigrpd:eigrpd/instance/metric-weights/K4 + * XPath: /frr-eigrpd:eigrpd/instance/metric-weights/K5 + * XPath: /frr-eigrpd:eigrpd/instance/metric-weights/K6 + */ +DEFPY_YANG( + eigrp_metric_weights, + eigrp_metric_weights_cmd, + "metric weights (0-255)$k1 (0-255)$k2 (0-255)$k3 (0-255)$k4 (0-255)$k5 [(0-255)$k6]", + "Modify metrics and parameters for advertisement\n" + "Modify metric coefficients\n" + "K1\n" + "K2\n" + "K3\n" + "K4\n" + "K5\n" + "K6\n") +{ + nb_cli_enqueue_change(vty, "./metric-weights/K1", NB_OP_MODIFY, k1_str); + nb_cli_enqueue_change(vty, "./metric-weights/K2", NB_OP_MODIFY, k2_str); + nb_cli_enqueue_change(vty, "./metric-weights/K3", NB_OP_MODIFY, k3_str); + nb_cli_enqueue_change(vty, "./metric-weights/K4", NB_OP_MODIFY, k4_str); + nb_cli_enqueue_change(vty, "./metric-weights/K5", NB_OP_MODIFY, k5_str); + if (k6) + nb_cli_enqueue_change(vty, "./metric-weights/K6", + NB_OP_MODIFY, k6_str); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + no_eigrp_metric_weights, + no_eigrp_metric_weights_cmd, + "no metric weights [(0-255) (0-255) (0-255) (0-255) (0-255) (0-255)]", + NO_STR + "Modify metrics and parameters for advertisement\n" + "Modify metric coefficients\n" + "K1\n" + "K2\n" + "K3\n" + "K4\n" + "K5\n" + "K6\n") +{ + nb_cli_enqueue_change(vty, "./metric-weights/K1", NB_OP_DESTROY, NULL); + nb_cli_enqueue_change(vty, "./metric-weights/K2", NB_OP_DESTROY, NULL); + nb_cli_enqueue_change(vty, "./metric-weights/K3", NB_OP_DESTROY, NULL); + nb_cli_enqueue_change(vty, "./metric-weights/K4", NB_OP_DESTROY, NULL); + nb_cli_enqueue_change(vty, "./metric-weights/K5", NB_OP_DESTROY, NULL); + nb_cli_enqueue_change(vty, "./metric-weights/K6", NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +void eigrp_cli_show_metrics(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + const char *k1, *k2, *k3, *k4, *k5, *k6; + + k1 = yang_dnode_exists(dnode, "./K1") ? + yang_dnode_get_string(dnode, "./K1") : "0"; + k2 = yang_dnode_exists(dnode, "./K2") ? + yang_dnode_get_string(dnode, "./K2") : "0"; + k3 = yang_dnode_exists(dnode, "./K3") ? + yang_dnode_get_string(dnode, "./K3") : "0"; + k4 = yang_dnode_exists(dnode, "./K4") ? + yang_dnode_get_string(dnode, "./K4") : "0"; + k5 = yang_dnode_exists(dnode, "./K5") ? + yang_dnode_get_string(dnode, "./K5") : "0"; + k6 = yang_dnode_exists(dnode, "./K6") ? + yang_dnode_get_string(dnode, "./K6") : "0"; + + vty_out(vty, " metric weights %s %s %s %s %s", + k1, k2, k3, k4, k5); + if (k6) + vty_out(vty, " %s", k6); + vty_out(vty, "\n"); +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/network + */ +DEFPY_YANG( + eigrp_network, + eigrp_network_cmd, + "[no] network A.B.C.D/M$prefix", + NO_STR + "Enable routing on an IP network\n" + "EIGRP network prefix\n") +{ + if (no) + nb_cli_enqueue_change(vty, "./network", NB_OP_DESTROY, + prefix_str); + else + nb_cli_enqueue_change(vty, "./network", NB_OP_CREATE, + prefix_str); + + return nb_cli_apply_changes(vty, NULL); +} + +void eigrp_cli_show_network(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + const char *prefix = yang_dnode_get_string(dnode, NULL); + + vty_out(vty, " network %s\n", prefix); +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/neighbor + */ +DEFPY_YANG( + eigrp_neighbor, + eigrp_neighbor_cmd, + "[no] neighbor A.B.C.D$addr", + NO_STR + "Specify a neighbor router\n" + "Neighbor address\n") +{ + if (no) + nb_cli_enqueue_change(vty, "./neighbor", NB_OP_DESTROY, + addr_str); + else + nb_cli_enqueue_change(vty, "./neighbor", NB_OP_CREATE, + addr_str); + + return nb_cli_apply_changes(vty, NULL); +} + +void eigrp_cli_show_neighbor(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + const char *prefix = yang_dnode_get_string(dnode, NULL); + + vty_out(vty, " neighbor %s\n", prefix); +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/redistribute + * XPath: /frr-eigrpd:eigrpd/instance/redistribute/route-map + * XPath: /frr-eigrpd:eigrpd/instance/redistribute/metrics/bandwidth + * XPath: /frr-eigrpd:eigrpd/instance/redistribute/metrics/delay + * XPath: /frr-eigrpd:eigrpd/instance/redistribute/metrics/reliability + * XPath: /frr-eigrpd:eigrpd/instance/redistribute/metrics/load + * XPath: /frr-eigrpd:eigrpd/instance/redistribute/metrics/mtu + */ +DEFPY_YANG( + eigrp_redistribute_source_metric, + eigrp_redistribute_source_metric_cmd, + "[no] redistribute " FRR_REDIST_STR_EIGRPD + "$proto [metric (1-4294967295)$bw (0-4294967295)$delay (0-255)$rlbt (1-255)$load (1-65535)$mtu]", + NO_STR + REDIST_STR + FRR_REDIST_HELP_STR_EIGRPD + "Metric for redistributed routes\n" + "Bandwidth metric in Kbits per second\n" + "EIGRP delay metric, in 10 microsecond units\n" + "EIGRP reliability metric where 255 is 100% reliable2 ?\n" + "EIGRP Effective bandwidth metric (Loading) where 255 is 100% loaded\n" + "EIGRP MTU of the path\n") +{ + char xpath[XPATH_MAXLEN], xpath_metric[XPATH_MAXLEN + 64]; + + snprintf(xpath, sizeof(xpath), "./redistribute[protocol='%s']", proto); + + if (no) { + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); + } + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + if (bw == 0 || delay == 0 || rlbt == 0 || load == 0 || mtu == 0) + return nb_cli_apply_changes(vty, NULL); + + snprintf(xpath_metric, sizeof(xpath_metric), "%s/metrics/bandwidth", + xpath); + nb_cli_enqueue_change(vty, xpath_metric, NB_OP_MODIFY, bw_str); + snprintf(xpath_metric, sizeof(xpath_metric), "%s/metrics/delay", xpath); + nb_cli_enqueue_change(vty, xpath_metric, NB_OP_MODIFY, delay_str); + snprintf(xpath_metric, sizeof(xpath_metric), "%s/metrics/reliability", + xpath); + nb_cli_enqueue_change(vty, xpath_metric, NB_OP_MODIFY, rlbt_str); + snprintf(xpath_metric, sizeof(xpath_metric), "%s/metrics/load", xpath); + nb_cli_enqueue_change(vty, xpath_metric, NB_OP_MODIFY, load_str); + snprintf(xpath_metric, sizeof(xpath_metric), "%s/metrics/mtu", xpath); + nb_cli_enqueue_change(vty, xpath_metric, NB_OP_MODIFY, mtu_str); + return nb_cli_apply_changes(vty, NULL); +} + +void eigrp_cli_show_redistribute(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + const char *proto = yang_dnode_get_string(dnode, "./protocol"); + const char *bw, *delay, *load, *mtu, *rlbt; + + bw = yang_dnode_exists(dnode, "./metrics/bandwidth") ? + yang_dnode_get_string(dnode, "./metrics/bandwidth") : NULL; + delay = yang_dnode_exists(dnode, "./metrics/delay") ? + yang_dnode_get_string(dnode, "./metrics/delay") : NULL; + rlbt = yang_dnode_exists(dnode, "./metrics/reliability") ? + yang_dnode_get_string(dnode, "./metrics/reliability") : NULL; + load = yang_dnode_exists(dnode, "./metrics/load") ? + yang_dnode_get_string(dnode, "./metrics/load") : NULL; + mtu = yang_dnode_exists(dnode, "./metrics/mtu") ? + yang_dnode_get_string(dnode, "./metrics/mtu") : NULL; + + vty_out(vty, " redistribute %s", proto); + if (bw || rlbt || delay || load || mtu) + vty_out(vty, " metric %s %s %s %s %s", bw, delay, rlbt, load, + mtu); + vty_out(vty, "\n"); +} + +/* + * XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/delay + */ +DEFPY_YANG( + eigrp_if_delay, + eigrp_if_delay_cmd, + "delay (1-16777215)$delay", + "Specify interface throughput delay\n" + "Throughput delay (tens of microseconds)\n") +{ + nb_cli_enqueue_change(vty, "./frr-eigrpd:eigrp/delay", + NB_OP_MODIFY, delay_str); + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + no_eigrp_if_delay, + no_eigrp_if_delay_cmd, + "no delay [(1-16777215)]", + NO_STR + "Specify interface throughput delay\n" + "Throughput delay (tens of microseconds)\n") +{ + nb_cli_enqueue_change(vty, "./frr-eigrpd:eigrp/delay", + NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +void eigrp_cli_show_delay(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + const char *delay = yang_dnode_get_string(dnode, NULL); + + vty_out(vty, " delay %s\n", delay); +} + +/* + * XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/bandwidth + */ +DEFPY_YANG( + eigrp_if_bandwidth, + eigrp_if_bandwidth_cmd, + "eigrp bandwidth (1-10000000)$bw", + EIGRP_STR + "Set bandwidth informational parameter\n" + "Bandwidth in kilobits\n") +{ + nb_cli_enqueue_change(vty, "./frr-eigrpd:eigrp/bandwidth", + NB_OP_MODIFY, bw_str); + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + no_eigrp_if_bandwidth, + no_eigrp_if_bandwidth_cmd, + "no eigrp bandwidth [(1-10000000)]", + NO_STR + EIGRP_STR + "Set bandwidth informational parameter\n" + "Bandwidth in kilobits\n") +{ + nb_cli_enqueue_change(vty, "./frr-eigrpd:eigrp/bandwidth", + NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +void eigrp_cli_show_bandwidth(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + const char *bandwidth = yang_dnode_get_string(dnode, NULL); + + vty_out(vty, " eigrp bandwidth %s\n", bandwidth); +} + +/* + * XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/hello-interval + */ +DEFPY_YANG( + eigrp_if_ip_hellointerval, + eigrp_if_ip_hellointerval_cmd, + "ip hello-interval eigrp (1-65535)$hello", + "Interface Internet Protocol config commands\n" + "Configures EIGRP hello interval\n" + EIGRP_STR + "Seconds between hello transmissions\n") +{ + nb_cli_enqueue_change(vty, "./frr-eigrpd:eigrp/hello-interval", + NB_OP_MODIFY, hello_str); + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + no_eigrp_if_ip_hellointerval, + no_eigrp_if_ip_hellointerval_cmd, + "no ip hello-interval eigrp [(1-65535)]", + NO_STR + "Interface Internet Protocol config commands\n" + "Configures EIGRP hello interval\n" + EIGRP_STR + "Seconds between hello transmissions\n") +{ + nb_cli_enqueue_change(vty, "./frr-eigrpd:eigrp/hello-interval", + NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + + +void eigrp_cli_show_hello_interval(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + const char *hello = yang_dnode_get_string(dnode, NULL); + + vty_out(vty, " ip hello-interval eigrp %s\n", hello); +} + +/* + * XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/hold-time + */ +DEFPY_YANG( + eigrp_if_ip_holdinterval, + eigrp_if_ip_holdinterval_cmd, + "ip hold-time eigrp (1-65535)$hold", + "Interface Internet Protocol config commands\n" + "Configures EIGRP IPv4 hold time\n" + EIGRP_STR + "Seconds before neighbor is considered down\n") +{ + nb_cli_enqueue_change(vty, "./frr-eigrpd:eigrp/hold-time", + NB_OP_MODIFY, hold_str); + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + no_eigrp_if_ip_holdinterval, + no_eigrp_if_ip_holdinterval_cmd, + "no ip hold-time eigrp [(1-65535)]", + NO_STR + "Interface Internet Protocol config commands\n" + "Configures EIGRP IPv4 hold time\n" + EIGRP_STR + "Seconds before neighbor is considered down\n") +{ + nb_cli_enqueue_change(vty, "./frr-eigrpd:eigrp/hold-time", + NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +void eigrp_cli_show_hold_time(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + const char *holdtime = yang_dnode_get_string(dnode, NULL); + + vty_out(vty, " ip hold-time eigrp %s\n", holdtime); +} + +/* + * XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/split-horizon + */ +/* NOT implemented. */ + +/* + * XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/instance + * XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/instance/summarize-addresses + */ +DEFPY_YANG( + eigrp_ip_summary_address, + eigrp_ip_summary_address_cmd, + "ip summary-address eigrp (1-65535)$as A.B.C.D/M$prefix", + "Interface Internet Protocol config commands\n" + "Perform address summarization\n" + EIGRP_STR + AS_STR + "Summary /, e.g. 192.168.0.0/16\n") +{ + char xpath[XPATH_MAXLEN], xpath_auth[XPATH_MAXLEN + 64]; + + snprintf(xpath, sizeof(xpath), "./frr-eigrpd:eigrp/instance[asn='%s']", + as_str); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + snprintf(xpath_auth, sizeof(xpath_auth), "%s/summarize-addresses", xpath); + nb_cli_enqueue_change(vty, xpath_auth, NB_OP_CREATE, prefix_str); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + no_eigrp_ip_summary_address, + no_eigrp_ip_summary_address_cmd, + "no ip summary-address eigrp (1-65535)$as A.B.C.D/M$prefix", + NO_STR + "Interface Internet Protocol config commands\n" + "Perform address summarization\n" + EIGRP_STR + AS_STR + "Summary /, e.g. 192.168.0.0/16\n") +{ + char xpath[XPATH_MAXLEN], xpath_auth[XPATH_MAXLEN + 64]; + + snprintf(xpath, sizeof(xpath), "./frr-eigrpd:eigrp/instance[asn='%s']", + as_str); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + snprintf(xpath_auth, sizeof(xpath_auth), "%s/summarize-addresses", xpath); + nb_cli_enqueue_change(vty, xpath_auth, NB_OP_DESTROY, prefix_str); + + return nb_cli_apply_changes(vty, NULL); +} + +void eigrp_cli_show_summarize_address(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + const struct lyd_node *instance = yang_dnode_get_parent(dnode, "instance"); + uint16_t asn = yang_dnode_get_uint16(instance, "./asn"); + const char *summarize_address = yang_dnode_get_string(dnode, NULL); + + vty_out(vty, " ip summary-address eigrp %d %s\n", asn, + summarize_address); +} + +/* + * XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/instance + * XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/instance/authentication + */ +DEFPY_YANG( + eigrp_authentication_mode, + eigrp_authentication_mode_cmd, + "ip authentication mode eigrp (1-65535)$as $crypt", + "Interface Internet Protocol config commands\n" + "Authentication subcommands\n" + "Mode\n" + EIGRP_STR + AS_STR + "Keyed message digest\n" + "HMAC SHA256 algorithm \n") +{ + char xpath[XPATH_MAXLEN], xpath_auth[XPATH_MAXLEN + 64]; + + snprintf(xpath, sizeof(xpath), "./frr-eigrpd:eigrp/instance[asn='%s']", + as_str); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + snprintf(xpath_auth, sizeof(xpath_auth), "%s/authentication", xpath); + nb_cli_enqueue_change(vty, xpath_auth, NB_OP_MODIFY, crypt); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + no_eigrp_authentication_mode, + no_eigrp_authentication_mode_cmd, + "no ip authentication mode eigrp (1-65535)$as []", + NO_STR + "Interface Internet Protocol config commands\n" + "Authentication subcommands\n" + "Mode\n" + EIGRP_STR + AS_STR + "Keyed message digest\n" + "HMAC SHA256 algorithm \n") +{ + char xpath[XPATH_MAXLEN], xpath_auth[XPATH_MAXLEN + 64]; + + snprintf(xpath, sizeof(xpath), "./frr-eigrpd:eigrp/instance[asn='%s']", + as_str); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + snprintf(xpath_auth, sizeof(xpath_auth), "%s/authentication", xpath); + nb_cli_enqueue_change(vty, xpath_auth, NB_OP_MODIFY, "none"); + + return nb_cli_apply_changes(vty, NULL); +} + +void eigrp_cli_show_authentication(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + const struct lyd_node *instance = yang_dnode_get_parent(dnode, "instance"); + uint16_t asn = yang_dnode_get_uint16(instance, "./asn"); + const char *crypt = yang_dnode_get_string(dnode, NULL); + + vty_out(vty, " ip authentication mode eigrp %d %s\n", asn, crypt); +} + +/* + * XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/instance + * XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/instance/keychain + */ +DEFPY_YANG( + eigrp_authentication_keychain, + eigrp_authentication_keychain_cmd, + "ip authentication key-chain eigrp (1-65535)$as WORD$name", + "Interface Internet Protocol config commands\n" + "Authentication subcommands\n" + "Key-chain\n" + EIGRP_STR + AS_STR + "Name of key-chain\n") +{ + char xpath[XPATH_MAXLEN], xpath_auth[XPATH_MAXLEN + 64]; + + snprintf(xpath, sizeof(xpath), "./frr-eigrpd:eigrp/instance[asn='%s']", + as_str); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + snprintf(xpath_auth, sizeof(xpath_auth), "%s/keychain", xpath); + nb_cli_enqueue_change(vty, xpath_auth, NB_OP_MODIFY, name); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + no_eigrp_authentication_keychain, + no_eigrp_authentication_keychain_cmd, + "no ip authentication key-chain eigrp (1-65535)$as [WORD]", + NO_STR + "Interface Internet Protocol config commands\n" + "Authentication subcommands\n" + "Key-chain\n" + EIGRP_STR + AS_STR + "Name of key-chain\n") +{ + char xpath[XPATH_MAXLEN], xpath_auth[XPATH_MAXLEN + 64]; + + snprintf(xpath, sizeof(xpath), "./frr-eigrpd:eigrp/instance[asn='%s']", + as_str); + snprintf(xpath_auth, sizeof(xpath_auth), "%s/keychain", xpath); + nb_cli_enqueue_change(vty, xpath_auth, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +void eigrp_cli_show_keychain(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + const struct lyd_node *instance = yang_dnode_get_parent(dnode, "instance"); + uint16_t asn = yang_dnode_get_uint16(instance, "./asn"); + const char *keychain = yang_dnode_get_string(dnode, NULL); + + vty_out(vty, " ip authentication key-chain eigrp %d %s\n", asn, + keychain); +} + + +/* + * CLI installation procedures. + */ +static int eigrp_config_write(struct vty *vty); +static struct cmd_node eigrp_node = { + .name = "eigrp", + .node = EIGRP_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-router)# ", + .config_write = eigrp_config_write, +}; + +static int eigrp_config_write(struct vty *vty) +{ + struct lyd_node *dnode; + int written = 0; + + dnode = yang_dnode_get(running_config->dnode, "/frr-eigrpd:eigrpd"); + if (dnode) { + nb_cli_show_dnode_cmds(vty, dnode, false); + written = 1; + } + + return written; +} + +static int eigrp_write_interface(struct vty *vty); +static struct cmd_node eigrp_interface_node = { + .name = "interface", + .node = INTERFACE_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-if)# ", + .config_write = eigrp_write_interface, +}; + + +static int eigrp_write_interface(struct vty *vty) +{ + struct lyd_node *dnode; + struct interface *ifp; + struct vrf *vrf; + int written = 0; + + RB_FOREACH(vrf, vrf_name_head, &vrfs_by_name) { + FOR_ALL_INTERFACES(vrf, ifp) { + dnode = yang_dnode_get( + running_config->dnode, + "/frr-interface:lib/interface[name='%s'][vrf='%s']", + ifp->name, vrf->name); + if (dnode == NULL) + continue; + + written = 1; + nb_cli_show_dnode_cmds(vty, dnode, false); + } + } + + return written; +} + +void +eigrp_cli_init(void) +{ + install_element(CONFIG_NODE, &router_eigrp_cmd); + install_element(CONFIG_NODE, &no_router_eigrp_cmd); + + install_node(&eigrp_node); + install_default(EIGRP_NODE); + + install_element(EIGRP_NODE, &eigrp_router_id_cmd); + install_element(EIGRP_NODE, &no_eigrp_router_id_cmd); + install_element(EIGRP_NODE, &eigrp_passive_interface_cmd); + install_element(EIGRP_NODE, &eigrp_timers_active_cmd); + install_element(EIGRP_NODE, &no_eigrp_timers_active_cmd); + install_element(EIGRP_NODE, &eigrp_variance_cmd); + install_element(EIGRP_NODE, &no_eigrp_variance_cmd); + install_element(EIGRP_NODE, &eigrp_maximum_paths_cmd); + install_element(EIGRP_NODE, &no_eigrp_maximum_paths_cmd); + install_element(EIGRP_NODE, &eigrp_metric_weights_cmd); + install_element(EIGRP_NODE, &no_eigrp_metric_weights_cmd); + install_element(EIGRP_NODE, &eigrp_network_cmd); + install_element(EIGRP_NODE, &eigrp_neighbor_cmd); + install_element(EIGRP_NODE, &eigrp_redistribute_source_metric_cmd); + + install_node(&eigrp_interface_node); + if_cmd_init(); + + install_element(INTERFACE_NODE, &eigrp_if_delay_cmd); + install_element(INTERFACE_NODE, &no_eigrp_if_delay_cmd); + install_element(INTERFACE_NODE, &eigrp_if_bandwidth_cmd); + install_element(INTERFACE_NODE, &no_eigrp_if_bandwidth_cmd); + install_element(INTERFACE_NODE, &eigrp_if_ip_hellointerval_cmd); + install_element(INTERFACE_NODE, &no_eigrp_if_ip_hellointerval_cmd); + install_element(INTERFACE_NODE, &eigrp_if_ip_holdinterval_cmd); + install_element(INTERFACE_NODE, &no_eigrp_if_ip_holdinterval_cmd); + install_element(INTERFACE_NODE, &eigrp_ip_summary_address_cmd); + install_element(INTERFACE_NODE, &no_eigrp_ip_summary_address_cmd); + install_element(INTERFACE_NODE, &eigrp_authentication_mode_cmd); + install_element(INTERFACE_NODE, &no_eigrp_authentication_mode_cmd); + install_element(INTERFACE_NODE, &eigrp_authentication_keychain_cmd); + install_element(INTERFACE_NODE, &no_eigrp_authentication_keychain_cmd); +} diff --git a/eigrpd/eigrp_const.h b/eigrpd/eigrp_const.h index 895a141e4a..3a103fb9f2 100644 --- a/eigrpd/eigrp_const.h +++ b/eigrpd/eigrp_const.h @@ -32,8 +32,6 @@ #ifndef _ZEBRA_EIGRP_CONST_H_ #define _ZEBRA_EIGRP_CONST_H_ -#define FALSE 0 - #define EIGRP_NEIGHBOR_DOWN 0 #define EIGRP_NEIGHBOR_PENDING 1 #define EIGRP_NEIGHBOR_UP 2 diff --git a/eigrpd/eigrp_dump.c b/eigrpd/eigrp_dump.c index 583db6622d..97de73116b 100644 --- a/eigrpd/eigrp_dump.c +++ b/eigrpd/eigrp_dump.c @@ -144,62 +144,17 @@ void eigrp_header_dump(struct eigrp_header *eigrph) const char *eigrp_if_name_string(struct eigrp_interface *ei) { - static char buf[EIGRP_IF_STRING_MAXLEN] = ""; - - if (!ei) - return "inactive"; - - snprintf(buf, EIGRP_IF_STRING_MAXLEN, "%s", ei->ifp->name); - return buf; -} - -const char *eigrp_topology_ip_string(struct eigrp_prefix_entry *tn) -{ - static char buf[EIGRP_IF_STRING_MAXLEN] = ""; - uint32_t ifaddr; - - ifaddr = ntohl(tn->destination->u.prefix4.s_addr); - snprintf(buf, EIGRP_IF_STRING_MAXLEN, "%u.%u.%u.%u", - (ifaddr >> 24) & 0xff, (ifaddr >> 16) & 0xff, - (ifaddr >> 8) & 0xff, ifaddr & 0xff); - return buf; -} - - -const char *eigrp_if_ip_string(struct eigrp_interface *ei) -{ - static char buf[EIGRP_IF_STRING_MAXLEN] = ""; - uint32_t ifaddr; - if (!ei) return "inactive"; - ifaddr = ntohl(ei->address.u.prefix4.s_addr); - snprintf(buf, EIGRP_IF_STRING_MAXLEN, "%u.%u.%u.%u", - (ifaddr >> 24) & 0xff, (ifaddr >> 16) & 0xff, - (ifaddr >> 8) & 0xff, ifaddr & 0xff); - - return buf; -} - -const char *eigrp_neigh_ip_string(struct eigrp_neighbor *nbr) -{ - static char buf[EIGRP_IF_STRING_MAXLEN] = ""; - uint32_t ifaddr; - - ifaddr = ntohl(nbr->src.s_addr); - snprintf(buf, EIGRP_IF_STRING_MAXLEN, "%u.%u.%u.%u", - (ifaddr >> 24) & 0xff, (ifaddr >> 16) & 0xff, - (ifaddr >> 8) & 0xff, ifaddr & 0xff); - - return buf; + return ei->ifp->name; } void show_ip_eigrp_interface_header(struct vty *vty, struct eigrp *eigrp) { vty_out(vty, - "\nEIGRP interfaces for AS(%d)\n\n %-10s %-10s %-10s %-6s %-12s %-7s %-14s %-12s %-8s %-8s %-8s\n %-39s %-12s %-7s %-14s %-12s %-8s\n", + "\nEIGRP interfaces for AS(%d)\n\n%-16s %-10s %-10s %-6s %-12s %-7s %-14s %-12s %-8s %-8s %-8s\n %-44s %-12s %-7s %-14s %-12s %-8s\n", eigrp->AS, "Interface", "Bandwidth", "Delay", "Peers", "Xmit Queue", "Mean", "Pacing Time", "Multicast", "Pending", "Hello", "Holdtime", "", "Un/Reliable", "SRTT", "Un/Reliable", @@ -209,7 +164,7 @@ void show_ip_eigrp_interface_header(struct vty *vty, struct eigrp *eigrp) void show_ip_eigrp_interface_sub(struct vty *vty, struct eigrp *eigrp, struct eigrp_interface *ei) { - vty_out(vty, "%-11s ", eigrp_if_name_string(ei)); + vty_out(vty, "%-16s ", IF_NAME(ei)); vty_out(vty, "%-11u", ei->params.bandwidth); vty_out(vty, "%-11u", ei->params.delay); vty_out(vty, "%-7u", ei->nbrs->count); @@ -250,7 +205,7 @@ void show_ip_eigrp_neighbor_sub(struct vty *vty, struct eigrp_neighbor *nbr, { vty_out(vty, "%-3u %-17s %-21s", 0, eigrp_neigh_ip_string(nbr), - eigrp_if_name_string(nbr->ei)); + IF_NAME(nbr->ei)); if (nbr->t_holddown) vty_out(vty, "%-7lu", thread_timer_remain_second(nbr->t_holddown)); @@ -279,8 +234,7 @@ void show_ip_eigrp_topology_header(struct vty *vty, struct eigrp *eigrp) vty_out(vty, "\nEIGRP Topology Table for AS(%d)/ID(%s)\n\n", eigrp->AS, inet_ntoa(eigrp->router_id)); vty_out(vty, - "Codes: P - Passive, A - Active, U - Update, Q - Query, " - "R - Reply\n r - reply Status, s - sia Status\n\n"); + "Codes: P - Passive, A - Active, U - Update, Q - Query, R - Reply\n r - reply Status, s - sia Status\n\n"); } void show_ip_eigrp_prefix_entry(struct vty *vty, struct eigrp_prefix_entry *tn) @@ -313,11 +267,11 @@ void show_ip_eigrp_nexthop_entry(struct vty *vty, struct eigrp *eigrp, if (te->adv_router == eigrp->neighbor_self) vty_out(vty, "%-7s%s, %s\n", " ", "via Connected", - eigrp_if_name_string(te->ei)); + IF_NAME(te->ei)); else { vty_out(vty, "%-7s%s%s (%u/%u), %s\n", " ", "via ", inet_ntoa(te->adv_router->src), te->distance, - te->reported_distance, eigrp_if_name_string(te->ei)); + te->reported_distance, IF_NAME(te->ei)); } } @@ -600,14 +554,18 @@ DEFUN (no_debug_eigrp_packets, } /* Debug node. */ +static int config_write_debug(struct vty *vty); static struct cmd_node eigrp_debug_node = { - DEBUG_NODE, "", 1 /* VTYSH */ + .name = "debug", + .node = DEBUG_NODE, + .prompt = "", + .config_write = config_write_debug, }; /* Initialize debug commands. */ void eigrp_debug_init(void) { - install_node(&eigrp_debug_node, config_write_debug); + install_node(&eigrp_debug_node); install_element(ENABLE_NODE, &show_debugging_eigrp_cmd); install_element(ENABLE_NODE, &debug_eigrp_packets_all_cmd); diff --git a/eigrpd/eigrp_dump.h b/eigrpd/eigrp_dump.h index 34b55ab419..f141f3cbc6 100644 --- a/eigrpd/eigrp_dump.h +++ b/eigrpd/eigrp_dump.h @@ -138,9 +138,21 @@ extern unsigned long term_debug_eigrp_zebra; /* Prototypes. */ extern const char *eigrp_if_name_string(struct eigrp_interface *); -extern const char *eigrp_if_ip_string(struct eigrp_interface *); -extern const char *eigrp_neigh_ip_string(struct eigrp_neighbor *); -extern const char *eigrp_topology_ip_string(struct eigrp_prefix_entry *); +static inline const char +*eigrp_topology_ip_string(struct eigrp_prefix_entry *tn) +{ + return inet_ntoa(tn->destination->u.prefix4); +} + +static inline const char *eigrp_if_ip_string(struct eigrp_interface *ei) +{ + return ei ? inet_ntoa(ei->address.u.prefix4) : "inactive"; +} + +static inline const char *eigrp_neigh_ip_string(struct eigrp_neighbor *nbr) +{ + return inet_ntoa(nbr->src); +} extern void eigrp_ip_header_dump(struct ip *); extern void eigrp_header_dump(struct eigrp_header *); diff --git a/eigrpd/eigrp_filter.c b/eigrpd/eigrp_filter.c index 93eed9452c..9d5d45ca50 100644 --- a/eigrpd/eigrp_filter.c +++ b/eigrpd/eigrp_filter.c @@ -65,17 +65,15 @@ void eigrp_distribute_update(struct distribute_ctx *ctx, struct distribute *dist) { + struct eigrp *e = eigrp_lookup(ctx->vrf->vrf_id); struct interface *ifp; struct eigrp_interface *ei = NULL; struct access_list *alist; struct prefix_list *plist; // struct route_map *routemap; - struct eigrp *e; /* if no interface address is present, set list to eigrp process struct */ - e = eigrp_lookup(); - assert(e != NULL); /* Check if distribute-list was set for process or interface */ if (!dist->ifname) { @@ -174,7 +172,7 @@ void eigrp_distribute_update(struct distribute_ctx *ctx, return; } - ifp = if_lookup_by_name(dist->ifname, VRF_DEFAULT); + ifp = if_lookup_by_name(dist->ifname, e->vrf_id); if (ifp == NULL) return; @@ -288,7 +286,7 @@ void eigrp_distribute_update_interface(struct interface *ifp) struct distribute *dist; struct eigrp *eigrp; - eigrp = eigrp_lookup(); + eigrp = eigrp_lookup(ifp->vrf_id); if (!eigrp) return; dist = distribute_lookup(eigrp->distribute_ctx, ifp->name); @@ -302,11 +300,13 @@ void eigrp_distribute_update_interface(struct interface *ifp) */ void eigrp_distribute_update_all(struct prefix_list *notused) { - struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT); + struct vrf *vrf; struct interface *ifp; - FOR_ALL_INTERFACES (vrf, ifp) - eigrp_distribute_update_interface(ifp); + RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { + FOR_ALL_INTERFACES (vrf, ifp) + eigrp_distribute_update_interface(ifp); + } } /* diff --git a/eigrpd/eigrp_filter.h b/eigrpd/eigrp_filter.h index 34d00ecc13..03f45cedbf 100644 --- a/eigrpd/eigrp_filter.h +++ b/eigrpd/eigrp_filter.h @@ -35,10 +35,10 @@ extern void eigrp_distribute_update(struct distribute_ctx *ctx, struct distribute *dist); -extern void eigrp_distribute_update_interface(struct interface *); -extern void eigrp_distribute_update_all(struct prefix_list *); -extern void eigrp_distribute_update_all_wrapper(struct access_list *); -extern int eigrp_distribute_timer_process(struct thread *); -extern int eigrp_distribute_timer_interface(struct thread *); +extern void eigrp_distribute_update_interface(struct interface *ifp); +extern void eigrp_distribute_update_all(struct prefix_list *plist); +extern void eigrp_distribute_update_all_wrapper(struct access_list *alist); +extern int eigrp_distribute_timer_process(struct thread *thread); +extern int eigrp_distribute_timer_interface(struct thread *thread); #endif /* EIGRPD_EIGRP_FILTER_H_ */ diff --git a/eigrpd/eigrp_fsm.c b/eigrpd/eigrp_fsm.c index 4d6d73e202..e43eca0e0d 100644 --- a/eigrpd/eigrp_fsm.c +++ b/eigrpd/eigrp_fsm.c @@ -110,7 +110,7 @@ int eigrp_fsm_event_qact(struct eigrp_fsm_action_message *); * NSM[actual/starting state][occurred event].func * Functions are should be executed within separate thread. */ -struct { +const struct { int (*func)(struct eigrp_fsm_action_message *); } NSM[EIGRP_FSM_STATE_MAX][EIGRP_FSM_EVENT_MAX] = { { @@ -445,7 +445,7 @@ int eigrp_fsm_event_nq_fcn(struct eigrp_fsm_action_message *msg) prefix->rdistance = prefix->distance = prefix->fdistance = ne->distance; prefix->reported_metric = ne->total_metric; - if (eigrp_nbr_count_get()) { + if (eigrp_nbr_count_get(eigrp)) { prefix->req_action |= EIGRP_FSM_NEED_QUERY; listnode_add(eigrp->topology_changes_internalIPV4, prefix); } else { @@ -471,7 +471,7 @@ int eigrp_fsm_event_q_fcn(struct eigrp_fsm_action_message *msg) prefix->state = EIGRP_FSM_STATE_ACTIVE_3; prefix->rdistance = prefix->distance = prefix->fdistance = ne->distance; prefix->reported_metric = ne->total_metric; - if (eigrp_nbr_count_get()) { + if (eigrp_nbr_count_get(eigrp)) { prefix->req_action |= EIGRP_FSM_NEED_QUERY; listnode_add(eigrp->topology_changes_internalIPV4, prefix); } else { @@ -486,7 +486,7 @@ int eigrp_fsm_event_q_fcn(struct eigrp_fsm_action_message *msg) int eigrp_fsm_event_keep_state(struct eigrp_fsm_action_message *msg) { - struct eigrp *eigrp; + struct eigrp *eigrp = msg->eigrp; struct eigrp_prefix_entry *prefix = msg->prefix; struct eigrp_nexthop_entry *ne = listnode_head(prefix->entries); @@ -499,13 +499,11 @@ int eigrp_fsm_event_keep_state(struct eigrp_fsm_action_message *msg) if (msg->packet_type == EIGRP_OPC_QUERY) eigrp_send_reply(msg->adv_router, prefix); prefix->req_action |= EIGRP_FSM_NEED_UPDATE; - eigrp = eigrp_lookup(); - assert(eigrp); listnode_add(eigrp->topology_changes_internalIPV4, prefix); } - eigrp_topology_update_node_flags(prefix); - eigrp_update_routing_table(prefix); + eigrp_topology_update_node_flags(eigrp, prefix); + eigrp_update_routing_table(eigrp, prefix); } if (msg->packet_type == EIGRP_OPC_QUERY) @@ -536,9 +534,10 @@ int eigrp_fsm_event_lr(struct eigrp_fsm_action_message *msg) prefix->state = EIGRP_FSM_STATE_PASSIVE; prefix->req_action |= EIGRP_FSM_NEED_UPDATE; listnode_add(eigrp->topology_changes_internalIPV4, prefix); - eigrp_topology_update_node_flags(prefix); - eigrp_update_routing_table(prefix); - eigrp_update_topology_table_prefix(eigrp->topology_table, prefix); + eigrp_topology_update_node_flags(eigrp, prefix); + eigrp_update_routing_table(eigrp, prefix); + eigrp_update_topology_table_prefix(eigrp, eigrp->topology_table, + prefix); return 1; } @@ -588,9 +587,10 @@ int eigrp_fsm_event_lr_fcs(struct eigrp_fsm_action_message *msg) } prefix->req_action |= EIGRP_FSM_NEED_UPDATE; listnode_add(eigrp->topology_changes_internalIPV4, prefix); - eigrp_topology_update_node_flags(prefix); - eigrp_update_routing_table(prefix); - eigrp_update_topology_table_prefix(eigrp->topology_table, prefix); + eigrp_topology_update_node_flags(eigrp, prefix); + eigrp_update_routing_table(eigrp, prefix); + eigrp_update_topology_table_prefix(eigrp, eigrp->topology_table, + prefix); return 1; } @@ -612,7 +612,7 @@ int eigrp_fsm_event_lr_fcn(struct eigrp_fsm_action_message *msg) prefix->rdistance = prefix->distance = best_successor->distance; prefix->reported_metric = best_successor->total_metric; - if (eigrp_nbr_count_get()) { + if (eigrp_nbr_count_get(eigrp)) { prefix->req_action |= EIGRP_FSM_NEED_QUERY; listnode_add(eigrp->topology_changes_internalIPV4, prefix); } else { diff --git a/eigrpd/eigrp_hello.c b/eigrpd/eigrp_hello.c index dacd5caeb5..6f93cd7b3e 100644 --- a/eigrpd/eigrp_hello.c +++ b/eigrpd/eigrp_hello.c @@ -147,7 +147,7 @@ eigrp_hello_parameter_decode(struct eigrp_neighbor *nbr, zlog_info("Neighbor %s (%s) is pending: new adjacency", inet_ntoa(nbr->src), ifindex2ifname(nbr->ei->ifp->ifindex, - VRF_DEFAULT)); + eigrp->vrf_id)); /* Expedited hello sent */ eigrp_hello_send(nbr->ei, EIGRP_HELLO_NORMAL, NULL); @@ -167,7 +167,7 @@ eigrp_hello_parameter_decode(struct eigrp_neighbor *nbr, "Neighbor %s (%s) is down: Interface PEER-TERMINATION received", inet_ntoa(nbr->src), ifindex2ifname(nbr->ei->ifp->ifindex, - VRF_DEFAULT)); + eigrp->vrf_id)); eigrp_nbr_delete(nbr); return NULL; } else { @@ -175,7 +175,7 @@ eigrp_hello_parameter_decode(struct eigrp_neighbor *nbr, "Neighbor %s (%s) going down: Kvalue mismatch", inet_ntoa(nbr->src), ifindex2ifname(nbr->ei->ifp->ifindex, - VRF_DEFAULT)); + eigrp->vrf_id)); eigrp_nbr_state_set(nbr, EIGRP_NEIGHBOR_DOWN); } } @@ -245,6 +245,7 @@ static void eigrp_sw_version_decode(struct eigrp_neighbor *nbr, static void eigrp_peer_termination_decode(struct eigrp_neighbor *nbr, struct eigrp_tlv_hdr_type *tlv) { + struct eigrp *eigrp = nbr->ei->eigrp; struct TLV_Peer_Termination_type *param = (struct TLV_Peer_Termination_type *)tlv; @@ -254,7 +255,7 @@ static void eigrp_peer_termination_decode(struct eigrp_neighbor *nbr, if (my_ip == received_ip) { zlog_info("Neighbor %s (%s) is down: Peer Termination received", inet_ntoa(nbr->src), - ifindex2ifname(nbr->ei->ifp->ifindex, VRF_DEFAULT)); + ifindex2ifname(nbr->ei->ifp->ifindex, eigrp->vrf_id)); /* set neighbor to DOWN */ nbr->state = EIGRP_NEIGHBOR_DOWN; /* delete neighbor */ @@ -330,7 +331,7 @@ void eigrp_hello_receive(struct eigrp *eigrp, struct ip *iph, if (IS_DEBUG_EIGRP_PACKET(eigrph->opcode - 1, RECV)) zlog_debug("Processing Hello size[%u] int(%s) nbr(%s)", size, - ifindex2ifname(nbr->ei->ifp->ifindex, VRF_DEFAULT), + ifindex2ifname(nbr->ei->ifp->ifindex, eigrp->vrf_id), inet_ntoa(nbr->src)); size -= EIGRP_HEADER_LEN; @@ -484,21 +485,15 @@ static uint16_t eigrp_tidlist_encode(struct stream *s) * Part of conditional receive process * */ -static uint16_t eigrp_sequence_encode(struct stream *s) +static uint16_t eigrp_sequence_encode(struct eigrp *eigrp, struct stream *s) { uint16_t length = EIGRP_TLV_SEQ_BASE_LEN; - struct eigrp *eigrp; struct eigrp_interface *ei; struct listnode *node, *node2, *nnode2; struct eigrp_neighbor *nbr; size_t backup_end, size_end; int found; - eigrp = eigrp_lookup(); - if (eigrp == NULL) { - return 0; - } - // add in the parameters TLV backup_end = stream_get_endp(s); stream_putw(s, EIGRP_TLV_SEQ); @@ -541,15 +536,10 @@ static uint16_t eigrp_sequence_encode(struct stream *s) * Part of conditional receive process * */ -static uint16_t eigrp_next_sequence_encode(struct stream *s) +static uint16_t eigrp_next_sequence_encode(struct eigrp *eigrp, + struct stream *s) { uint16_t length = EIGRP_NEXT_SEQUENCE_TLV_SIZE; - struct eigrp *eigrp; - - eigrp = eigrp_lookup(); - if (eigrp == NULL) { - return 0; - } // add in the parameters TLV stream_putw(s, EIGRP_TLV_NEXT_MCAST_SEQ); @@ -659,8 +649,8 @@ static struct eigrp_packet *eigrp_hello_encode(struct eigrp_interface *ei, length += eigrp_sw_version_encode(ep->s); if (flags & EIGRP_HELLO_ADD_SEQUENCE) { - length += eigrp_sequence_encode(ep->s); - length += eigrp_next_sequence_encode(ep->s); + length += eigrp_sequence_encode(ei->eigrp, ep->s); + length += eigrp_next_sequence_encode(ei->eigrp, ep->s); } // add in the TID list if doing multi-topology diff --git a/eigrpd/eigrp_interface.c b/eigrpd/eigrp_interface.c index c52a98ee25..3a45771eaa 100644 --- a/eigrpd/eigrp_interface.c +++ b/eigrpd/eigrp_interface.c @@ -54,6 +54,7 @@ #include "eigrpd/eigrp_topology.h" #include "eigrpd/eigrp_memory.h" #include "eigrpd/eigrp_fsm.h" +#include "eigrpd/eigrp_dump.h" struct eigrp_interface *eigrp_if_new(struct eigrp *eigrp, struct interface *ifp, struct prefix *p) @@ -98,6 +99,9 @@ struct eigrp_interface *eigrp_if_new(struct eigrp *eigrp, struct interface *ifp, ei->params.auth_type = EIGRP_AUTH_TYPE_NONE; ei->params.auth_keychain = NULL; + ei->curr_bandwidth = ifp->bandwidth; + ei->curr_mtu = ifp->mtu; + return ei; } @@ -117,7 +121,90 @@ int eigrp_if_delete_hook(struct interface *ifp) eigrp_fifo_free(ei->obuf); XFREE(MTYPE_EIGRP_IF_INFO, ifp->info); - ifp->info = NULL; + + return 0; +} + +static int eigrp_ifp_create(struct interface *ifp) +{ + struct eigrp_interface *ei = ifp->info; + + if (!ei) + return 0; + + ei->params.type = eigrp_default_iftype(ifp); + + eigrp_if_update(ifp); + + return 0; +} + +static int eigrp_ifp_up(struct interface *ifp) +{ + struct eigrp_interface *ei = ifp->info; + + if (IS_DEBUG_EIGRP(zebra, ZEBRA_INTERFACE)) + zlog_debug("Zebra: Interface[%s] state change to up.", + ifp->name); + + if (!ei) + return 0; + + if (ei->curr_bandwidth != ifp->bandwidth) { + if (IS_DEBUG_EIGRP(zebra, ZEBRA_INTERFACE)) + zlog_debug( + "Zebra: Interface[%s] bandwidth change %d -> %d.", + ifp->name, ei->curr_bandwidth, + ifp->bandwidth); + + ei->curr_bandwidth = ifp->bandwidth; + // eigrp_if_recalculate_output_cost (ifp); + } + + if (ei->curr_mtu != ifp->mtu) { + if (IS_DEBUG_EIGRP(zebra, ZEBRA_INTERFACE)) + zlog_debug( + "Zebra: Interface[%s] MTU change %u -> %u.", + ifp->name, ei->curr_mtu, ifp->mtu); + + ei->curr_mtu = ifp->mtu; + /* Must reset the interface (simulate down/up) when MTU + * changes. */ + eigrp_if_reset(ifp); + return 0; + } + + eigrp_if_up(ifp->info); + + return 0; +} + +static int eigrp_ifp_down(struct interface *ifp) +{ + if (IS_DEBUG_EIGRP(zebra, ZEBRA_INTERFACE)) + zlog_debug("Zebra: Interface[%s] state change to down.", + ifp->name); + + if (ifp->info) + eigrp_if_down(ifp->info); + + return 0; +} + +static int eigrp_ifp_destroy(struct interface *ifp) +{ + if (if_is_up(ifp)) + zlog_warn("Zebra: got delete of %s, but interface is still up", + ifp->name); + + if (IS_DEBUG_EIGRP(zebra, ZEBRA_INTERFACE)) + zlog_debug( + "Zebra: interface delete %s index %d flags %llx metric %d mtu %d", + ifp->name, ifp->ifindex, (unsigned long long)ifp->flags, + ifp->metric, ifp->mtu); + + if (ifp->info) + eigrp_if_free(ifp->info, INTERFACE_DOWN_BY_ZEBRA); return 0; } @@ -126,6 +213,8 @@ struct list *eigrp_iflist; void eigrp_if_init(void) { + if_zapi_callbacks(eigrp_ifp_create, eigrp_ifp_up, + eigrp_ifp_down, eigrp_ifp_destroy); /* Initialize Zebra interface data structure. */ // hook_register_prio(if_add, 0, eigrp_if_new); hook_register_prio(if_del, 0, eigrp_if_delete_hook); @@ -138,6 +227,20 @@ void eigrp_del_if_params(struct eigrp_if_params *eip) free(eip->auth_keychain); } +/* + * Set the network byte order of the 3 bytes we send + * of the mtu of the link. + */ +static void eigrp_mtu_convert(struct eigrp_metrics *metric, uint32_t host_mtu) +{ + uint32_t network_mtu = htonl(host_mtu); + uint8_t *nm = (uint8_t *)&network_mtu; + + metric->mtu[0] = nm[1]; + metric->mtu[1] = nm[2]; + metric->mtu[2] = nm[3]; +} + int eigrp_if_up(struct eigrp_interface *ei) { struct eigrp_prefix_entry *pe; @@ -165,9 +268,7 @@ int eigrp_if_up(struct eigrp_interface *ei) metric.delay = eigrp_delay_to_scaled(ei->params.delay); metric.load = ei->params.load; metric.reliability = ei->params.reliability; - metric.mtu[0] = 0xDC; - metric.mtu[1] = 0x05; - metric.mtu[2] = 0x00; + eigrp_mtu_convert(&metric, ei->ifp->mtu); metric.hop_count = 0; metric.flags = 0; metric.tag = 0; @@ -206,7 +307,7 @@ int eigrp_if_up(struct eigrp_interface *ei) eigrp_prefix_entry_add(eigrp->topology_table, pe); listnode_add(eigrp->topology_changes_internalIPV4, pe); - eigrp_nexthop_entry_add(pe, ne); + eigrp_nexthop_entry_add(eigrp, pe, ne); for (ALL_LIST_ELEMENTS(eigrp->eiflist, node, nnode, ei2)) { eigrp_update_send(ei2); @@ -218,7 +319,7 @@ int eigrp_if_up(struct eigrp_interface *ei) struct eigrp_fsm_action_message msg; ne->prefix = pe; - eigrp_nexthop_entry_add(pe, ne); + eigrp_nexthop_entry_add(eigrp, pe, ne); msg.packet_type = EIGRP_OPC_UPDATE; msg.eigrp = eigrp; @@ -329,10 +430,7 @@ void eigrp_if_free(struct eigrp_interface *ei, int source) { struct prefix dest_addr; struct eigrp_prefix_entry *pe; - struct eigrp *eigrp = eigrp_lookup(); - - if (!eigrp) - return; + struct eigrp *eigrp = ei->eigrp; if (source == INTERFACE_DOWN_BY_VTY) { THREAD_OFF(ei->t_hello); @@ -344,7 +442,7 @@ void eigrp_if_free(struct eigrp_interface *ei, int source) pe = eigrp_topology_table_lookup_ipv4(eigrp->topology_table, &dest_addr); if (pe) - eigrp_prefix_entry_delete(eigrp->topology_table, pe); + eigrp_prefix_entry_delete(eigrp, eigrp->topology_table, pe); eigrp_if_down(ei); diff --git a/eigrpd/eigrp_interface.h b/eigrpd/eigrp_interface.h index a18b0b7015..1e66dafde2 100644 --- a/eigrpd/eigrp_interface.h +++ b/eigrpd/eigrp_interface.h @@ -63,5 +63,4 @@ extern uint32_t eigrp_scaled_to_bandwidth(uint32_t); extern uint32_t eigrp_delay_to_scaled(uint32_t); extern uint32_t eigrp_scaled_to_delay(uint32_t); - #endif /* ZEBRA_EIGRP_INTERFACE_H_ */ diff --git a/eigrpd/eigrp_main.c b/eigrpd/eigrp_main.c index 3db59a838b..6c44ce361c 100644 --- a/eigrpd/eigrp_main.c +++ b/eigrpd/eigrp_main.c @@ -65,6 +65,7 @@ #include "eigrpd/eigrp_snmp.h" #include "eigrpd/eigrp_filter.h" #include "eigrpd/eigrp_errors.h" +#include "eigrpd/eigrp_vrf.h" //#include "eigrpd/eigrp_routemap.h" /* eigprd privileges */ @@ -90,10 +91,16 @@ struct option longopts[] = {{0}}; /* Master of threads. */ struct thread_master *master; +/* Forward declaration of daemon info structure. */ +static struct frr_daemon_info eigrpd_di; + /* SIGHUP handler. */ static void sighup(void) { zlog_info("SIGHUP received"); + + /* Reload config file. */ + vty_read_config(NULL, eigrpd_di.config_file, config_default); } /* SIGINT / SIGTERM handler. */ @@ -130,8 +137,12 @@ struct quagga_signal_t eigrp_signals[] = { }, }; -static const struct frr_yang_module_info *eigrpd_yang_modules[] = { +static const struct frr_yang_module_info *const eigrpd_yang_modules[] = { + &frr_eigrpd_info, + &frr_filter_info, &frr_interface_info, + &frr_route_map_info, + &frr_vrf_info, }; FRR_DAEMON_INFO(eigrpd, EIGRP, .vty_port = EIGRP_VTY_PORT, @@ -175,6 +186,7 @@ int main(int argc, char **argv, char **envp) master = eigrp_om->master; eigrp_error_init(); + eigrp_vrf_init(); vrf_init(NULL, NULL, NULL, NULL, NULL); /*EIGRPd init*/ @@ -187,7 +199,7 @@ int main(int argc, char **argv, char **envp) eigrp_vty_init(); keychain_init(); eigrp_vty_show_init(); - eigrp_vty_if_init(); + eigrp_cli_init(); #ifdef HAVE_SNMP eigrp_snmp_init(); @@ -219,5 +231,5 @@ int main(int argc, char **argv, char **envp) frr_run(master); /* Not reached. */ - return (0); + return 0; } diff --git a/eigrpd/eigrp_neighbor.c b/eigrpd/eigrp_neighbor.c index 66dd5f3419..2ae3997fae 100644 --- a/eigrpd/eigrp_neighbor.c +++ b/eigrpd/eigrp_neighbor.c @@ -194,13 +194,12 @@ void eigrp_nbr_delete(struct eigrp_neighbor *nbr) int holddown_timer_expired(struct thread *thread) { - struct eigrp_neighbor *nbr; - - nbr = THREAD_ARG(thread); + struct eigrp_neighbor *nbr = THREAD_ARG(thread); + struct eigrp *eigrp = nbr->ei->eigrp; zlog_info("Neighbor %s (%s) is down: holding time expired", inet_ntoa(nbr->src), - ifindex2ifname(nbr->ei->ifp->ifindex, VRF_DEFAULT)); + ifindex2ifname(nbr->ei->ifp->ifindex, eigrp->vrf_id)); nbr->state = EIGRP_NEIGHBOR_DOWN; eigrp_nbr_delete(nbr); @@ -297,19 +296,13 @@ void eigrp_nbr_state_update(struct eigrp_neighbor *nbr) } } -int eigrp_nbr_count_get(void) +int eigrp_nbr_count_get(struct eigrp *eigrp) { struct eigrp_interface *iface; struct listnode *node, *node2, *nnode2; struct eigrp_neighbor *nbr; - struct eigrp *eigrp = eigrp_lookup(); uint32_t counter; - if (eigrp == NULL) { - zlog_debug("EIGRP Routing Process not enabled"); - return 0; - } - counter = 0; for (ALL_LIST_ELEMENTS_RO(eigrp->eiflist, node, iface)) { for (ALL_LIST_ELEMENTS(iface->nbrs, node2, nnode2, nbr)) { @@ -335,20 +328,16 @@ int eigrp_nbr_count_get(void) */ void eigrp_nbr_hard_restart(struct eigrp_neighbor *nbr, struct vty *vty) { - if (nbr == NULL) { - flog_err(EC_EIGRP_CONFIG, - "Nbr Hard restart: Neighbor not specified."); - return; - } + struct eigrp *eigrp = nbr->ei->eigrp; zlog_debug("Neighbor %s (%s) is down: manually cleared", inet_ntoa(nbr->src), - ifindex2ifname(nbr->ei->ifp->ifindex, VRF_DEFAULT)); + ifindex2ifname(nbr->ei->ifp->ifindex, eigrp->vrf_id)); if (vty != NULL) { vty_time_print(vty, 0); vty_out(vty, "Neighbor %s (%s) is down: manually cleared\n", inet_ntoa(nbr->src), - ifindex2ifname(nbr->ei->ifp->ifindex, VRF_DEFAULT)); + ifindex2ifname(nbr->ei->ifp->ifindex, eigrp->vrf_id)); } /* send Hello with Peer Termination TLV */ diff --git a/eigrpd/eigrp_neighbor.h b/eigrpd/eigrp_neighbor.h index 21a8c6a004..1c308fa981 100644 --- a/eigrpd/eigrp_neighbor.h +++ b/eigrpd/eigrp_neighbor.h @@ -33,24 +33,25 @@ #define _ZEBRA_EIGRP_NEIGHBOR_H /* Prototypes */ -extern struct eigrp_neighbor *eigrp_nbr_get(struct eigrp_interface *, - struct eigrp_header *, struct ip *); -extern struct eigrp_neighbor *eigrp_nbr_new(struct eigrp_interface *); -extern void eigrp_nbr_delete(struct eigrp_neighbor *); +extern struct eigrp_neighbor *eigrp_nbr_get(struct eigrp_interface *ei, + struct eigrp_header *, + struct ip *addr); +extern struct eigrp_neighbor *eigrp_nbr_new(struct eigrp_interface *ei); +extern void eigrp_nbr_delete(struct eigrp_neighbor *neigh); -extern int holddown_timer_expired(struct thread *); +extern int holddown_timer_expired(struct thread *thread); -extern int eigrp_neighborship_check(struct eigrp_neighbor *, - struct TLV_Parameter_Type *); -extern void eigrp_nbr_state_update(struct eigrp_neighbor *); -extern void eigrp_nbr_state_set(struct eigrp_neighbor *, uint8_t state); -extern uint8_t eigrp_nbr_state_get(struct eigrp_neighbor *); -extern int eigrp_nbr_count_get(void); -extern const char *eigrp_nbr_state_str(struct eigrp_neighbor *); -extern struct eigrp_neighbor *eigrp_nbr_lookup_by_addr(struct eigrp_interface *, - struct in_addr *); -extern struct eigrp_neighbor *eigrp_nbr_lookup_by_addr_process(struct eigrp *, - struct in_addr); +extern int eigrp_neighborship_check(struct eigrp_neighbor *neigh, + struct TLV_Parameter_Type *tlv); +extern void eigrp_nbr_state_update(struct eigrp_neighbor *neigh); +extern void eigrp_nbr_state_set(struct eigrp_neighbor *neigh, uint8_t state); +extern uint8_t eigrp_nbr_state_get(struct eigrp_neighbor *neigh); +extern int eigrp_nbr_count_get(struct eigrp *eigrp); +extern const char *eigrp_nbr_state_str(struct eigrp_neighbor *neigh); +extern struct eigrp_neighbor * +eigrp_nbr_lookup_by_addr(struct eigrp_interface *ei, struct in_addr *addr); +extern struct eigrp_neighbor * +eigrp_nbr_lookup_by_addr_process(struct eigrp *eigrp, struct in_addr addr); extern void eigrp_nbr_hard_restart(struct eigrp_neighbor *nbr, struct vty *vty); extern int eigrp_nbr_split_horizon_check(struct eigrp_nexthop_entry *ne, diff --git a/eigrpd/eigrp_network.c b/eigrpd/eigrp_network.c index bbb9487b4d..92b5ce3482 100644 --- a/eigrpd/eigrp_network.c +++ b/eigrpd/eigrp_network.c @@ -53,16 +53,21 @@ static int eigrp_network_match_iface(const struct prefix *connected_prefix, static void eigrp_network_run_interface(struct eigrp *, struct prefix *, struct interface *); -int eigrp_sock_init(void) +int eigrp_sock_init(struct vrf *vrf) { - int eigrp_sock; + int eigrp_sock = -1; int ret; #ifdef IP_HDRINCL int hincl = 1; #endif - frr_elevate_privs(&eigrpd_privs) { - eigrp_sock = socket(AF_INET, SOCK_RAW, IPPROTO_EIGRPIGP); + if (!vrf) + return eigrp_sock; + + frr_with_privs(&eigrpd_privs) { + eigrp_sock = vrf_socket( + AF_INET, SOCK_RAW, IPPROTO_EIGRPIGP, vrf->vrf_id, + vrf->vrf_id != VRF_DEFAULT ? vrf->name : NULL); if (eigrp_sock < 0) { zlog_err("eigrp_read_sock_init: socket: %s", safe_strerror(errno)); @@ -155,8 +160,7 @@ int eigrp_if_ipmulticast(struct eigrp *top, struct prefix *p, ret = setsockopt_ipv4_multicast_if(top->fd, p->u.prefix4, ifindex); if (ret < 0) zlog_warn( - "can't setsockopt IP_MULTICAST_IF (fd %d, addr %s, " - "ifindex %u): %s", + "can't setsockopt IP_MULTICAST_IF (fd %d, addr %s, ifindex %u): %s", top->fd, inet_ntoa(p->u.prefix4), ifindex, safe_strerror(errno)); @@ -174,9 +178,7 @@ int eigrp_if_add_allspfrouters(struct eigrp *top, struct prefix *p, htonl(EIGRP_MULTICAST_ADDRESS), ifindex); if (ret < 0) zlog_warn( - "can't setsockopt IP_ADD_MEMBERSHIP (fd %d, addr %s, " - "ifindex %u, AllSPFRouters): %s; perhaps a kernel limit " - "on # of multicast group memberships has been exceeded?", + "can't setsockopt IP_ADD_MEMBERSHIP (fd %d, addr %s, ifindex %u, AllSPFRouters): %s; perhaps a kernel limit on # of multicast group memberships has been exceeded?", top->fd, inet_ntoa(p->u.prefix4), ifindex, safe_strerror(errno)); else @@ -196,8 +198,7 @@ int eigrp_if_drop_allspfrouters(struct eigrp *top, struct prefix *p, htonl(EIGRP_MULTICAST_ADDRESS), ifindex); if (ret < 0) zlog_warn( - "can't setsockopt IP_DROP_MEMBERSHIP (fd %d, addr %s, " - "ifindex %u, AllSPFRouters): %s", + "can't setsockopt IP_DROP_MEMBERSHIP (fd %d, addr %s, ifindex %u, AllSPFRouters): %s", top->fd, inet_ntoa(p->u.prefix4), ifindex, safe_strerror(errno)); else @@ -209,11 +210,11 @@ int eigrp_if_drop_allspfrouters(struct eigrp *top, struct prefix *p, int eigrp_network_set(struct eigrp *eigrp, struct prefix *p) { - struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT); + struct vrf *vrf = vrf_lookup_by_id(eigrp->vrf_id); struct route_node *rn; struct interface *ifp; - rn = route_node_get(eigrp->networks, (struct prefix *)p); + rn = route_node_get(eigrp->networks, p); if (rn->info) { /* There is already same network statement. */ route_unlock_node(rn); @@ -225,7 +226,7 @@ int eigrp_network_set(struct eigrp *eigrp, struct prefix *p) rn->info = (void *)pref; /* Schedule Router ID Update. */ - if (eigrp->router_id.s_addr == 0) + if (eigrp->router_id.s_addr == INADDR_ANY) eigrp_router_id_update(eigrp); /* Run network config now. */ /* Get target interface. */ @@ -290,6 +291,9 @@ void eigrp_if_update(struct interface *ifp) * we need to check eac one and add the interface as approperate */ for (ALL_LIST_ELEMENTS(eigrp_om->eigrp, node, nnode, eigrp)) { + if (ifp->vrf_id != eigrp->vrf_id) + continue; + /* EIGRP must be on and Router-ID must be configured. */ if (eigrp->router_id.s_addr == 0) continue; @@ -319,8 +323,7 @@ int eigrp_network_unset(struct eigrp *eigrp, struct prefix *p) if (!IPV4_ADDR_SAME(&pref->u.prefix4, &p->u.prefix4)) return 0; - prefix_ipv4_free(rn->info); - rn->info = NULL; + prefix_ipv4_free((struct prefix_ipv4 **)&rn->info); route_unlock_node(rn); /* initial reference */ /* Find interfaces that not configured already. */ diff --git a/eigrpd/eigrp_network.h b/eigrpd/eigrp_network.h index b3c76bbecc..7839fc946b 100644 --- a/eigrpd/eigrp_network.h +++ b/eigrpd/eigrp_network.h @@ -30,7 +30,7 @@ /* Prototypes */ -extern int eigrp_sock_init(void); +extern int eigrp_sock_init(struct vrf *vrf); extern int eigrp_if_ipmulticast(struct eigrp *, struct prefix *, unsigned int); extern int eigrp_network_set(struct eigrp *eigrp, struct prefix *p); extern int eigrp_network_unset(struct eigrp *eigrp, struct prefix *p); diff --git a/eigrpd/eigrp_northbound.c b/eigrpd/eigrp_northbound.c new file mode 100644 index 0000000000..13887368f7 --- /dev/null +++ b/eigrpd/eigrp_northbound.c @@ -0,0 +1,1510 @@ +/* + * EIGRP daemon northbound implementation. + * + * Copyright (C) 2019 Network Device Education Foundation, Inc. ("NetDEF") + * Rafael Zalamena + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#include + +#include "lib/keychain.h" +#include "lib/log.h" +#include "lib/northbound.h" +#include "lib/table.h" +#include "lib/vrf.h" +#include "lib/zclient.h" + +#include "eigrp_structs.h" +#include "eigrpd.h" +#include "eigrp_interface.h" +#include "eigrp_network.h" +#include "eigrp_zebra.h" + +/* Helper functions. */ +static void redistribute_get_metrics(const struct lyd_node *dnode, + struct eigrp_metrics *em) +{ + memset(em, 0, sizeof(*em)); + + if (yang_dnode_exists(dnode, "./bandwidth")) + em->bandwidth = yang_dnode_get_uint32(dnode, "./bandwidth"); + if (yang_dnode_exists(dnode, "./delay")) + em->delay = yang_dnode_get_uint32(dnode, "./delay"); +#if 0 /* TODO: How does MTU work? */ + if (yang_dnode_exists(dnode, "./mtu")) + em->mtu[0] = yang_dnode_get_uint32(dnode, "./mtu"); +#endif + if (yang_dnode_exists(dnode, "./load")) + em->load = yang_dnode_get_uint32(dnode, "./load"); + if (yang_dnode_exists(dnode, "./reliability")) + em->reliability = yang_dnode_get_uint32(dnode, "./reliability"); +} + +static struct eigrp_interface *eigrp_interface_lookup(const struct eigrp *eigrp, + const char *ifname) +{ + struct eigrp_interface *eif; + struct listnode *ln; + + for (ALL_LIST_ELEMENTS_RO(eigrp->eiflist, ln, eif)) { + if (strcmp(ifname, eif->ifp->name)) + continue; + + return eif; + } + + return NULL; +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance + */ +static int eigrpd_instance_create(struct nb_cb_create_args *args) +{ + struct eigrp *eigrp; + const char *vrf; + vrf_id_t vrfid; + + switch (args->event) { + case NB_EV_VALIDATE: + /* NOTHING */ + break; + case NB_EV_PREPARE: + vrf = yang_dnode_get_string(args->dnode, "./vrf"); + vrfid = vrf_name_to_id(vrf); + + eigrp = eigrp_get(yang_dnode_get_uint16(args->dnode, "./asn"), + vrfid); + args->resource->ptr = eigrp; + break; + case NB_EV_ABORT: + eigrp_finish_final(args->resource->ptr); + break; + case NB_EV_APPLY: + nb_running_set_entry(args->dnode, args->resource->ptr); + break; + } + + return NB_OK; +} + +static int eigrpd_instance_destroy(struct nb_cb_destroy_args *args) +{ + struct eigrp *eigrp; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eigrp = nb_running_unset_entry(args->dnode); + eigrp_finish_final(eigrp); + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/router-id + */ +static int eigrpd_instance_router_id_modify(struct nb_cb_modify_args *args) +{ + struct eigrp *eigrp; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eigrp = nb_running_get_entry(args->dnode, NULL, true); + yang_dnode_get_ipv4(&eigrp->router_id_static, args->dnode, + NULL); + break; + } + + return NB_OK; +} + +static int eigrpd_instance_router_id_destroy(struct nb_cb_destroy_args *args) +{ + struct eigrp *eigrp; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eigrp = nb_running_get_entry(args->dnode, NULL, true); + eigrp->router_id_static.s_addr = INADDR_ANY; + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/passive-interface + */ +static int +eigrpd_instance_passive_interface_create(struct nb_cb_create_args *args) +{ + struct eigrp_interface *eif; + struct eigrp *eigrp; + const char *ifname; + + switch (args->event) { + case NB_EV_VALIDATE: + eigrp = nb_running_get_entry(args->dnode, NULL, false); + if (eigrp == NULL) { + /* + * XXX: we can't verify if the interface exists + * and is active until EIGRP is up. + */ + break; + } + + ifname = yang_dnode_get_string(args->dnode, NULL); + eif = eigrp_interface_lookup(eigrp, ifname); + if (eif == NULL) + return NB_ERR_INCONSISTENCY; + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eigrp = nb_running_get_entry(args->dnode, NULL, true); + ifname = yang_dnode_get_string(args->dnode, NULL); + eif = eigrp_interface_lookup(eigrp, ifname); + if (eif == NULL) + return NB_ERR_INCONSISTENCY; + + eif->params.passive_interface = EIGRP_IF_PASSIVE; + break; + } + + return NB_OK; +} + +static int +eigrpd_instance_passive_interface_destroy(struct nb_cb_destroy_args *args) +{ + struct eigrp_interface *eif; + struct eigrp *eigrp; + const char *ifname; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eigrp = nb_running_get_entry(args->dnode, NULL, true); + ifname = yang_dnode_get_string(args->dnode, NULL); + eif = eigrp_interface_lookup(eigrp, ifname); + if (eif == NULL) + break; + + eif->params.passive_interface = EIGRP_IF_ACTIVE; + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/active-time + */ +static int eigrpd_instance_active_time_modify(struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + /* TODO: Not implemented. */ + return NB_ERR_INCONSISTENCY; + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* NOTHING */ + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/variance + */ +static int eigrpd_instance_variance_modify(struct nb_cb_modify_args *args) +{ + struct eigrp *eigrp; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eigrp = nb_running_get_entry(args->dnode, NULL, true); + eigrp->variance = yang_dnode_get_uint8(args->dnode, NULL); + break; + } + + return NB_OK; +} + +static int eigrpd_instance_variance_destroy(struct nb_cb_destroy_args *args) +{ + struct eigrp *eigrp; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eigrp = nb_running_get_entry(args->dnode, NULL, true); + eigrp->variance = EIGRP_VARIANCE_DEFAULT; + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/maximum-paths + */ +static int eigrpd_instance_maximum_paths_modify(struct nb_cb_modify_args *args) +{ + struct eigrp *eigrp; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eigrp = nb_running_get_entry(args->dnode, NULL, true); + eigrp->max_paths = yang_dnode_get_uint8(args->dnode, NULL); + break; + } + + return NB_OK; +} + +static int +eigrpd_instance_maximum_paths_destroy(struct nb_cb_destroy_args *args) +{ + struct eigrp *eigrp; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eigrp = nb_running_get_entry(args->dnode, NULL, true); + eigrp->max_paths = EIGRP_MAX_PATHS_DEFAULT; + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/metric-weights/K1 + */ +static int +eigrpd_instance_metric_weights_K1_modify(struct nb_cb_modify_args *args) +{ + struct eigrp *eigrp; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eigrp = nb_running_get_entry(args->dnode, NULL, true); + eigrp->k_values[0] = yang_dnode_get_uint8(args->dnode, NULL); + break; + } + + return NB_OK; +} + +static int +eigrpd_instance_metric_weights_K1_destroy(struct nb_cb_destroy_args *args) +{ + struct eigrp *eigrp; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eigrp = nb_running_get_entry(args->dnode, NULL, true); + eigrp->k_values[0] = EIGRP_K1_DEFAULT; + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/metric-weights/K2 + */ +static int +eigrpd_instance_metric_weights_K2_modify(struct nb_cb_modify_args *args) +{ + struct eigrp *eigrp; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eigrp = nb_running_get_entry(args->dnode, NULL, true); + eigrp->k_values[1] = yang_dnode_get_uint8(args->dnode, NULL); + break; + } + + return NB_OK; +} + +static int +eigrpd_instance_metric_weights_K2_destroy(struct nb_cb_destroy_args *args) +{ + struct eigrp *eigrp; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eigrp = nb_running_get_entry(args->dnode, NULL, true); + eigrp->k_values[1] = EIGRP_K2_DEFAULT; + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/metric-weights/K3 + */ +static int +eigrpd_instance_metric_weights_K3_modify(struct nb_cb_modify_args *args) +{ + struct eigrp *eigrp; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eigrp = nb_running_get_entry(args->dnode, NULL, true); + eigrp->k_values[2] = yang_dnode_get_uint8(args->dnode, NULL); + break; + } + + return NB_OK; +} + +static int +eigrpd_instance_metric_weights_K3_destroy(struct nb_cb_destroy_args *args) +{ + struct eigrp *eigrp; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eigrp = nb_running_get_entry(args->dnode, NULL, true); + eigrp->k_values[2] = EIGRP_K3_DEFAULT; + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/metric-weights/K4 + */ +static int +eigrpd_instance_metric_weights_K4_modify(struct nb_cb_modify_args *args) +{ + struct eigrp *eigrp; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eigrp = nb_running_get_entry(args->dnode, NULL, true); + eigrp->k_values[3] = yang_dnode_get_uint8(args->dnode, NULL); + break; + } + + return NB_OK; +} + +static int +eigrpd_instance_metric_weights_K4_destroy(struct nb_cb_destroy_args *args) +{ + struct eigrp *eigrp; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eigrp = nb_running_get_entry(args->dnode, NULL, true); + eigrp->k_values[3] = EIGRP_K4_DEFAULT; + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/metric-weights/K5 + */ +static int +eigrpd_instance_metric_weights_K5_modify(struct nb_cb_modify_args *args) +{ + struct eigrp *eigrp; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eigrp = nb_running_get_entry(args->dnode, NULL, true); + eigrp->k_values[4] = yang_dnode_get_uint8(args->dnode, NULL); + break; + } + + return NB_OK; +} + +static int +eigrpd_instance_metric_weights_K5_destroy(struct nb_cb_destroy_args *args) +{ + struct eigrp *eigrp; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eigrp = nb_running_get_entry(args->dnode, NULL, true); + eigrp->k_values[4] = EIGRP_K5_DEFAULT; + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/metric-weights/K6 + */ +static int +eigrpd_instance_metric_weights_K6_modify(struct nb_cb_modify_args *args) +{ + struct eigrp *eigrp; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eigrp = nb_running_get_entry(args->dnode, NULL, true); + eigrp->k_values[5] = yang_dnode_get_uint8(args->dnode, NULL); + break; + } + + return NB_OK; +} + +static int +eigrpd_instance_metric_weights_K6_destroy(struct nb_cb_destroy_args *args) +{ + struct eigrp *eigrp; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eigrp = nb_running_get_entry(args->dnode, NULL, true); + eigrp->k_values[5] = EIGRP_K6_DEFAULT; + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/network + */ +static int eigrpd_instance_network_create(struct nb_cb_create_args *args) +{ + struct route_node *rnode; + struct prefix prefix; + struct eigrp *eigrp; + int exists; + + yang_dnode_get_ipv4p(&prefix, args->dnode, NULL); + + switch (args->event) { + case NB_EV_VALIDATE: + eigrp = nb_running_get_entry(args->dnode, NULL, false); + /* If entry doesn't exist it means the list is empty. */ + if (eigrp == NULL) + break; + + rnode = route_node_get(eigrp->networks, &prefix); + exists = (rnode->info != NULL); + route_unlock_node(rnode); + if (exists) + return NB_ERR_INCONSISTENCY; + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eigrp = nb_running_get_entry(args->dnode, NULL, true); + if (eigrp_network_set(eigrp, &prefix) == 0) + return NB_ERR_INCONSISTENCY; + break; + } + + return NB_OK; +} + +static int eigrpd_instance_network_destroy(struct nb_cb_destroy_args *args) +{ + struct route_node *rnode; + struct prefix prefix; + struct eigrp *eigrp; + int exists = 0; + + yang_dnode_get_ipv4p(&prefix, args->dnode, NULL); + + switch (args->event) { + case NB_EV_VALIDATE: + eigrp = nb_running_get_entry(args->dnode, NULL, false); + /* If entry doesn't exist it means the list is empty. */ + if (eigrp == NULL) + break; + + rnode = route_node_get(eigrp->networks, &prefix); + exists = (rnode->info != NULL); + route_unlock_node(rnode); + if (exists == 0) + return NB_ERR_INCONSISTENCY; + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eigrp = nb_running_get_entry(args->dnode, NULL, true); + eigrp_network_unset(eigrp, &prefix); + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/neighbor + */ +static int eigrpd_instance_neighbor_create(struct nb_cb_create_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + /* TODO: Not implemented. */ + return NB_ERR_INCONSISTENCY; + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* NOTHING */ + break; + } + + return NB_OK; +} + +static int eigrpd_instance_neighbor_destroy(struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + /* TODO: Not implemented. */ + return NB_ERR_INCONSISTENCY; + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* NOTHING */ + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/redistribute + */ +static int eigrpd_instance_redistribute_create(struct nb_cb_create_args *args) +{ + struct eigrp_metrics metrics; + const char *vrfname; + struct eigrp *eigrp; + uint32_t proto; + vrf_id_t vrfid; + + switch (args->event) { + case NB_EV_VALIDATE: + proto = yang_dnode_get_enum(args->dnode, "./protocol"); + vrfname = yang_dnode_get_string(args->dnode, "../vrf"); + vrfid = vrf_name_to_id(vrfname); + if (vrf_bitmap_check(zclient->redist[AFI_IP][proto], vrfid)) + return NB_ERR_INCONSISTENCY; + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eigrp = nb_running_get_entry(args->dnode, NULL, true); + proto = yang_dnode_get_enum(args->dnode, "./protocol"); + redistribute_get_metrics(args->dnode, &metrics); + eigrp_redistribute_set(eigrp, proto, metrics); + break; + } + + return NB_OK; +} + +static int eigrpd_instance_redistribute_destroy(struct nb_cb_destroy_args *args) +{ + struct eigrp *eigrp; + uint32_t proto; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eigrp = nb_running_get_entry(args->dnode, NULL, true); + proto = yang_dnode_get_enum(args->dnode, "./protocol"); + eigrp_redistribute_unset(eigrp, proto); + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/redistribute/route-map + */ +static int +eigrpd_instance_redistribute_route_map_modify(struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + /* TODO: Not implemented. */ + return NB_ERR_INCONSISTENCY; + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* NOTHING */ + break; + } + + return NB_OK; +} + +static int +eigrpd_instance_redistribute_route_map_destroy(struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + /* TODO: Not implemented. */ + return NB_ERR_INCONSISTENCY; + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* NOTHING */ + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/redistribute/metrics/bandwidth + */ +static int eigrpd_instance_redistribute_metrics_bandwidth_modify( + struct nb_cb_modify_args *args) +{ + struct eigrp_metrics metrics; + struct eigrp *eigrp; + uint32_t proto; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eigrp = nb_running_get_entry(args->dnode, NULL, true); + proto = yang_dnode_get_enum(args->dnode, "../../protocol"); + redistribute_get_metrics(args->dnode, &metrics); + eigrp_redistribute_set(eigrp, proto, metrics); + break; + } + + return NB_OK; +} + +static int eigrpd_instance_redistribute_metrics_bandwidth_destroy( + struct nb_cb_destroy_args *args) +{ + struct eigrp_metrics metrics; + struct eigrp *eigrp; + uint32_t proto; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eigrp = nb_running_get_entry(args->dnode, NULL, true); + proto = yang_dnode_get_enum(args->dnode, "../../protocol"); + redistribute_get_metrics(args->dnode, &metrics); + eigrp_redistribute_set(eigrp, proto, metrics); + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/redistribute/metrics/delay + */ +static int eigrpd_instance_redistribute_metrics_delay_modify( + struct nb_cb_modify_args *args) +{ + return eigrpd_instance_redistribute_metrics_bandwidth_modify(args); +} + +static int eigrpd_instance_redistribute_metrics_delay_destroy( + struct nb_cb_destroy_args *args) +{ + return eigrpd_instance_redistribute_metrics_bandwidth_destroy(args); +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/redistribute/metrics/reliability + */ +static int eigrpd_instance_redistribute_metrics_reliability_modify( + struct nb_cb_modify_args *args) +{ + return eigrpd_instance_redistribute_metrics_bandwidth_modify(args); +} + +static int eigrpd_instance_redistribute_metrics_reliability_destroy( + struct nb_cb_destroy_args *args) +{ + return eigrpd_instance_redistribute_metrics_bandwidth_destroy(args); +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/redistribute/metrics/load + */ +static int +eigrpd_instance_redistribute_metrics_load_modify(struct nb_cb_modify_args *args) +{ + return eigrpd_instance_redistribute_metrics_bandwidth_modify(args); +} + +static int eigrpd_instance_redistribute_metrics_load_destroy( + struct nb_cb_destroy_args *args) +{ + return eigrpd_instance_redistribute_metrics_bandwidth_destroy(args); +} + +/* + * XPath: /frr-eigrpd:eigrpd/instance/redistribute/metrics/mtu + */ +static int +eigrpd_instance_redistribute_metrics_mtu_modify(struct nb_cb_modify_args *args) +{ + return eigrpd_instance_redistribute_metrics_bandwidth_modify(args); +} + +static int eigrpd_instance_redistribute_metrics_mtu_destroy( + struct nb_cb_destroy_args *args) +{ + return eigrpd_instance_redistribute_metrics_bandwidth_destroy(args); +} + +/* + * XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/delay + */ +static int lib_interface_eigrp_delay_modify(struct nb_cb_modify_args *args) +{ + struct eigrp_interface *ei; + struct interface *ifp; + + switch (args->event) { + case NB_EV_VALIDATE: + ifp = nb_running_get_entry(args->dnode, NULL, false); + if (ifp == NULL) { + /* + * XXX: we can't verify if the interface exists + * and is active until EIGRP is up. + */ + break; + } + + ei = ifp->info; + if (ei == NULL) + return NB_ERR_INCONSISTENCY; + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + ifp = nb_running_get_entry(args->dnode, NULL, true); + ei = ifp->info; + if (ei == NULL) + return NB_ERR_INCONSISTENCY; + + ei->params.delay = yang_dnode_get_uint32(args->dnode, NULL); + eigrp_if_reset(ifp); + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/bandwidth + */ +static int lib_interface_eigrp_bandwidth_modify(struct nb_cb_modify_args *args) +{ + struct interface *ifp; + struct eigrp_interface *ei; + + switch (args->event) { + case NB_EV_VALIDATE: + ifp = nb_running_get_entry(args->dnode, NULL, false); + if (ifp == NULL) { + /* + * XXX: we can't verify if the interface exists + * and is active until EIGRP is up. + */ + break; + } + + ei = ifp->info; + if (ei == NULL) + return NB_ERR_INCONSISTENCY; + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + ifp = nb_running_get_entry(args->dnode, NULL, true); + ei = ifp->info; + if (ei == NULL) + return NB_ERR_INCONSISTENCY; + + ei->params.bandwidth = yang_dnode_get_uint32(args->dnode, NULL); + eigrp_if_reset(ifp); + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/hello-interval + */ +static int +lib_interface_eigrp_hello_interval_modify(struct nb_cb_modify_args *args) +{ + struct interface *ifp; + struct eigrp_interface *ei; + + switch (args->event) { + case NB_EV_VALIDATE: + ifp = nb_running_get_entry(args->dnode, NULL, false); + if (ifp == NULL) { + /* + * XXX: we can't verify if the interface exists + * and is active until EIGRP is up. + */ + break; + } + + ei = ifp->info; + if (ei == NULL) + return NB_ERR_INCONSISTENCY; + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + ifp = nb_running_get_entry(args->dnode, NULL, true); + ei = ifp->info; + if (ei == NULL) + return NB_ERR_INCONSISTENCY; + + ei->params.v_hello = yang_dnode_get_uint16(args->dnode, NULL); + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/hold-time + */ +static int lib_interface_eigrp_hold_time_modify(struct nb_cb_modify_args *args) +{ + struct interface *ifp; + struct eigrp_interface *ei; + + switch (args->event) { + case NB_EV_VALIDATE: + ifp = nb_running_get_entry(args->dnode, NULL, false); + if (ifp == NULL) { + /* + * XXX: we can't verify if the interface exists + * and is active until EIGRP is up. + */ + break; + } + + ei = ifp->info; + if (ei == NULL) + return NB_ERR_INCONSISTENCY; + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + ifp = nb_running_get_entry(args->dnode, NULL, true); + ei = ifp->info; + if (ei == NULL) + return NB_ERR_INCONSISTENCY; + + ei->params.v_wait = yang_dnode_get_uint16(args->dnode, NULL); + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/split-horizon + */ +static int +lib_interface_eigrp_split_horizon_modify(struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + /* TODO: Not implemented. */ + return NB_ERR_INCONSISTENCY; + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* NOTHING */ + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/instance + */ +static int lib_interface_eigrp_instance_create(struct nb_cb_create_args *args) +{ + struct eigrp_interface *eif; + struct interface *ifp; + struct eigrp *eigrp; + + switch (args->event) { + case NB_EV_VALIDATE: + ifp = nb_running_get_entry(args->dnode, NULL, false); + if (ifp == NULL) { + /* + * XXX: we can't verify if the interface exists + * and is active until EIGRP is up. + */ + break; + } + + eigrp = eigrp_get(yang_dnode_get_uint16(args->dnode, "./asn"), + ifp->vrf_id); + eif = eigrp_interface_lookup(eigrp, ifp->name); + if (eif == NULL) + return NB_ERR_INCONSISTENCY; + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + ifp = nb_running_get_entry(args->dnode, NULL, true); + eigrp = eigrp_get(yang_dnode_get_uint16(args->dnode, "./asn"), + ifp->vrf_id); + eif = eigrp_interface_lookup(eigrp, ifp->name); + if (eif == NULL) + return NB_ERR_INCONSISTENCY; + + nb_running_set_entry(args->dnode, eif); + break; + } + + return NB_OK; +} + +static int lib_interface_eigrp_instance_destroy(struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + nb_running_unset_entry(args->dnode); + break; + } + + return NB_OK; +} + +/* + * XPath: + * /frr-interface:lib/interface/frr-eigrpd:eigrp/instance/summarize-addresses + */ +static int lib_interface_eigrp_instance_summarize_addresses_create( + struct nb_cb_create_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + /* TODO: Not implemented. */ + return NB_ERR_INCONSISTENCY; + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* NOTHING */ + break; + } + + return NB_OK; +} + +static int lib_interface_eigrp_instance_summarize_addresses_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + /* TODO: Not implemented. */ + return NB_ERR_INCONSISTENCY; + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* NOTHING */ + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/instance/authentication + */ +static int lib_interface_eigrp_instance_authentication_modify( + struct nb_cb_modify_args *args) +{ + struct eigrp_interface *eif; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eif = nb_running_get_entry(args->dnode, NULL, true); + eif->params.auth_type = yang_dnode_get_enum(args->dnode, NULL); + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/instance/keychain + */ +static int +lib_interface_eigrp_instance_keychain_modify(struct nb_cb_modify_args *args) +{ + struct eigrp_interface *eif; + struct keychain *keychain; + + switch (args->event) { + case NB_EV_VALIDATE: + keychain = keychain_lookup( + yang_dnode_get_string(args->dnode, NULL)); + if (keychain == NULL) + return NB_ERR_INCONSISTENCY; + break; + case NB_EV_PREPARE: + args->resource->ptr = + strdup(yang_dnode_get_string(args->dnode, NULL)); + if (args->resource->ptr == NULL) + return NB_ERR_RESOURCE; + break; + case NB_EV_ABORT: + free(args->resource->ptr); + args->resource->ptr = NULL; + break; + case NB_EV_APPLY: + eif = nb_running_get_entry(args->dnode, NULL, true); + if (eif->params.auth_keychain) + free(eif->params.auth_keychain); + + eif->params.auth_keychain = args->resource->ptr; + break; + } + + return NB_OK; +} + +static int +lib_interface_eigrp_instance_keychain_destroy(struct nb_cb_destroy_args *args) +{ + struct eigrp_interface *eif; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + eif = nb_running_get_entry(args->dnode, NULL, true); + if (eif->params.auth_keychain) + free(eif->params.auth_keychain); + + eif->params.auth_keychain = NULL; + break; + } + + return NB_OK; +} + +/* clang-format off */ +const struct frr_yang_module_info frr_eigrpd_info = { + .name = "frr-eigrpd", + .nodes = { + { + .xpath = "/frr-eigrpd:eigrpd/instance", + .cbs = { + .create = eigrpd_instance_create, + .destroy = eigrpd_instance_destroy, + .cli_show = eigrp_cli_show_header, + .cli_show_end = eigrp_cli_show_end_header, + } + }, + { + .xpath = "/frr-eigrpd:eigrpd/instance/router-id", + .cbs = { + .modify = eigrpd_instance_router_id_modify, + .destroy = eigrpd_instance_router_id_destroy, + .cli_show = eigrp_cli_show_router_id, + } + }, + { + .xpath = "/frr-eigrpd:eigrpd/instance/passive-interface", + .cbs = { + .create = eigrpd_instance_passive_interface_create, + .destroy = eigrpd_instance_passive_interface_destroy, + .cli_show = eigrp_cli_show_passive_interface, + } + }, + { + .xpath = "/frr-eigrpd:eigrpd/instance/active-time", + .cbs = { + .modify = eigrpd_instance_active_time_modify, + .cli_show = eigrp_cli_show_active_time, + } + }, + { + .xpath = "/frr-eigrpd:eigrpd/instance/variance", + .cbs = { + .modify = eigrpd_instance_variance_modify, + .destroy = eigrpd_instance_variance_destroy, + .cli_show = eigrp_cli_show_variance, + } + }, + { + .xpath = "/frr-eigrpd:eigrpd/instance/maximum-paths", + .cbs = { + .modify = eigrpd_instance_maximum_paths_modify, + .destroy = eigrpd_instance_maximum_paths_destroy, + .cli_show = eigrp_cli_show_maximum_paths, + } + }, + { + .xpath = "/frr-eigrpd:eigrpd/instance/metric-weights", + .cbs = { + .cli_show = eigrp_cli_show_metrics, + } + }, + { + .xpath = "/frr-eigrpd:eigrpd/instance/metric-weights/K1", + .cbs = { + .modify = eigrpd_instance_metric_weights_K1_modify, + .destroy = eigrpd_instance_metric_weights_K1_destroy, + } + }, + { + .xpath = "/frr-eigrpd:eigrpd/instance/metric-weights/K2", + .cbs = { + .modify = eigrpd_instance_metric_weights_K2_modify, + .destroy = eigrpd_instance_metric_weights_K2_destroy, + } + }, + { + .xpath = "/frr-eigrpd:eigrpd/instance/metric-weights/K3", + .cbs = { + .modify = eigrpd_instance_metric_weights_K3_modify, + .destroy = eigrpd_instance_metric_weights_K3_destroy, + } + }, + { + .xpath = "/frr-eigrpd:eigrpd/instance/metric-weights/K4", + .cbs = { + .modify = eigrpd_instance_metric_weights_K4_modify, + .destroy = eigrpd_instance_metric_weights_K4_destroy, + } + }, + { + .xpath = "/frr-eigrpd:eigrpd/instance/metric-weights/K5", + .cbs = { + .modify = eigrpd_instance_metric_weights_K5_modify, + .destroy = eigrpd_instance_metric_weights_K5_destroy, + } + }, + { + .xpath = "/frr-eigrpd:eigrpd/instance/metric-weights/K6", + .cbs = { + .modify = eigrpd_instance_metric_weights_K6_modify, + .destroy = eigrpd_instance_metric_weights_K6_destroy, + } + }, + { + .xpath = "/frr-eigrpd:eigrpd/instance/network", + .cbs = { + .create = eigrpd_instance_network_create, + .destroy = eigrpd_instance_network_destroy, + .cli_show = eigrp_cli_show_network, + } + }, + { + .xpath = "/frr-eigrpd:eigrpd/instance/neighbor", + .cbs = { + .create = eigrpd_instance_neighbor_create, + .destroy = eigrpd_instance_neighbor_destroy, + .cli_show = eigrp_cli_show_neighbor, + } + }, + { + .xpath = "/frr-eigrpd:eigrpd/instance/redistribute", + .cbs = { + .create = eigrpd_instance_redistribute_create, + .destroy = eigrpd_instance_redistribute_destroy, + .cli_show = eigrp_cli_show_redistribute, + } + }, + { + .xpath = "/frr-eigrpd:eigrpd/instance/redistribute/route-map", + .cbs = { + .modify = eigrpd_instance_redistribute_route_map_modify, + .destroy = eigrpd_instance_redistribute_route_map_destroy, + } + }, + { + .xpath = "/frr-eigrpd:eigrpd/instance/redistribute/metrics/bandwidth", + .cbs = { + .modify = eigrpd_instance_redistribute_metrics_bandwidth_modify, + .destroy = eigrpd_instance_redistribute_metrics_bandwidth_destroy, + } + }, + { + .xpath = "/frr-eigrpd:eigrpd/instance/redistribute/metrics/delay", + .cbs = { + .modify = eigrpd_instance_redistribute_metrics_delay_modify, + .destroy = eigrpd_instance_redistribute_metrics_delay_destroy, + } + }, + { + .xpath = "/frr-eigrpd:eigrpd/instance/redistribute/metrics/reliability", + .cbs = { + .modify = eigrpd_instance_redistribute_metrics_reliability_modify, + .destroy = eigrpd_instance_redistribute_metrics_reliability_destroy, + } + }, + { + .xpath = "/frr-eigrpd:eigrpd/instance/redistribute/metrics/load", + .cbs = { + .modify = eigrpd_instance_redistribute_metrics_load_modify, + .destroy = eigrpd_instance_redistribute_metrics_load_destroy, + } + }, + { + .xpath = "/frr-eigrpd:eigrpd/instance/redistribute/metrics/mtu", + .cbs = { + .modify = eigrpd_instance_redistribute_metrics_mtu_modify, + .destroy = eigrpd_instance_redistribute_metrics_mtu_destroy, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-eigrpd:eigrp/delay", + .cbs = { + .modify = lib_interface_eigrp_delay_modify, + .cli_show = eigrp_cli_show_delay, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-eigrpd:eigrp/bandwidth", + .cbs = { + .modify = lib_interface_eigrp_bandwidth_modify, + .cli_show = eigrp_cli_show_bandwidth, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-eigrpd:eigrp/hello-interval", + .cbs = { + .modify = lib_interface_eigrp_hello_interval_modify, + .cli_show = eigrp_cli_show_hello_interval, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-eigrpd:eigrp/hold-time", + .cbs = { + .modify = lib_interface_eigrp_hold_time_modify, + .cli_show = eigrp_cli_show_hold_time, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-eigrpd:eigrp/split-horizon", + .cbs = { + .modify = lib_interface_eigrp_split_horizon_modify, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-eigrpd:eigrp/instance", + .cbs = { + .create = lib_interface_eigrp_instance_create, + .destroy = lib_interface_eigrp_instance_destroy, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-eigrpd:eigrp/instance/summarize-addresses", + .cbs = { + .create = lib_interface_eigrp_instance_summarize_addresses_create, + .destroy = lib_interface_eigrp_instance_summarize_addresses_destroy, + .cli_show = eigrp_cli_show_summarize_address, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-eigrpd:eigrp/instance/authentication", + .cbs = { + .modify = lib_interface_eigrp_instance_authentication_modify, + .cli_show = eigrp_cli_show_authentication, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-eigrpd:eigrp/instance/keychain", + .cbs = { + .modify = lib_interface_eigrp_instance_keychain_modify, + .destroy = lib_interface_eigrp_instance_keychain_destroy, + .cli_show = eigrp_cli_show_keychain, + } + }, + { + .xpath = NULL, + }, + } +}; diff --git a/eigrpd/eigrp_packet.c b/eigrpd/eigrp_packet.c index bedaf15c47..cfff63f839 100644 --- a/eigrpd/eigrp_packet.c +++ b/eigrpd/eigrp_packet.c @@ -77,11 +77,13 @@ const struct message eigrp_packet_type_str[] = { static unsigned char zeropad[16] = {0}; /* Forward function reference*/ -static struct stream *eigrp_recv_packet(int, struct interface **, - struct stream *); -static int eigrp_verify_header(struct stream *, struct eigrp_interface *, - struct ip *, struct eigrp_header *); -static int eigrp_check_network_mask(struct eigrp_interface *, struct in_addr); +static struct stream *eigrp_recv_packet(struct eigrp *eigrp, int fd, + struct interface **ifp, + struct stream *s); +static int eigrp_verify_header(struct stream *s, struct eigrp_interface *ei, + struct ip *addr, struct eigrp_header *header); +static int eigrp_check_network_mask(struct eigrp_interface *ei, + struct in_addr mask); static int eigrp_retrans_count_exceeded(struct eigrp_packet *ep, struct eigrp_neighbor *nbr) @@ -349,13 +351,13 @@ int eigrp_write(struct thread *thread) ep = eigrp_fifo_next(ei->obuf); if (!ep) { flog_err(EC_LIB_DEVELOPMENT, - "%s: Interface %s no packet on queue?", - __PRETTY_FUNCTION__, ei->ifp->name); + "%s: Interface %s no packet on queue?", __func__, + ei->ifp->name); goto out; } if (ep->length < EIGRP_HEADER_LEN) { flog_err(EC_EIGRP_PACKET, "%s: Packet just has a header?", - __PRETTY_FUNCTION__); + __func__); eigrp_header_dump((struct eigrp_header *)ep->s->data); eigrp_packet_delete(ei); goto out; @@ -447,8 +449,7 @@ int eigrp_write(struct thread *thread) if (ret < 0) zlog_warn( - "*** sendmsg in eigrp_write failed to %s, " - "id %d, off %d, len %d, interface %s, mtu %u: %s", + "*** sendmsg in eigrp_write failed to %s, id %d, off %d, len %d, interface %s, mtu %u: %s", inet_ntoa(iph.ip_dst), iph.ip_id, iph.ip_off, iph.ip_len, ei->ifp->name, ei->ifp->mtu, safe_strerror(errno)); @@ -483,7 +484,7 @@ int eigrp_read(struct thread *thread) struct eigrp_header *eigrph; struct interface *ifp; struct eigrp_neighbor *nbr; - + struct in_addr srcaddr; uint16_t opcode = 0; uint16_t length = 0; @@ -495,7 +496,7 @@ int eigrp_read(struct thread *thread) thread_add_read(master, eigrp_read, eigrp, eigrp->fd, &eigrp->t_read); stream_reset(eigrp->ibuf); - if (!(ibuf = eigrp_recv_packet(eigrp->fd, &ifp, eigrp->ibuf))) { + if (!(ibuf = eigrp_recv_packet(eigrp, eigrp->fd, &ifp, eigrp->ibuf))) { /* This raw packet is known to be at least as big as its IP * header. */ return -1; @@ -509,6 +510,7 @@ int eigrp_read(struct thread *thread) if (iph->ip_v == 4) length = (iph->ip_len) - 20U; + srcaddr = iph->ip_src; /* IP Header dump. */ if (IS_DEBUG_EIGRP_TRANSMIT(0, RECV) @@ -524,8 +526,8 @@ int eigrp_read(struct thread *thread) and also platforms (such as Solaris 8) that claim to support ifindex retrieval but do not. */ - c = if_lookup_address((void *)&iph->ip_src, AF_INET, - VRF_DEFAULT); + c = if_lookup_address((void *)&srcaddr, AF_INET, + eigrp->vrf_id); if (c == NULL) return 0; @@ -547,11 +549,11 @@ int eigrp_read(struct thread *thread) /* Self-originated packet should be discarded silently. */ if (eigrp_if_lookup_by_local_addr(eigrp, NULL, iph->ip_src) - || (IPV4_ADDR_SAME(&iph->ip_src, &ei->address.u.prefix4))) { + || (IPV4_ADDR_SAME(&srcaddr, &ei->address.u.prefix4))) { if (IS_DEBUG_EIGRP_TRANSMIT(0, RECV)) zlog_debug( "eigrp_read[%s]: Dropping self-originated packet", - inet_ntoa(iph->ip_src)); + inet_ntoa(srcaddr)); return 0; } @@ -575,8 +577,7 @@ int eigrp_read(struct thread *thread) if (IS_DEBUG_EIGRP_TRANSMIT(0, RECV)) zlog_debug( - "ignoring packet from router %s sent to %s, " - "received on a passive interface, %s", + "ignoring packet from router %s sent to %s, received on a passive interface, %s", inet_ntop(AF_INET, &eigrph->vrid, buf[0], sizeof(buf[0])), inet_ntop(AF_INET, &iph->ip_dst, buf[1], @@ -706,7 +707,8 @@ int eigrp_read(struct thread *thread) return 0; } -static struct stream *eigrp_recv_packet(int fd, struct interface **ifp, +static struct stream *eigrp_recv_packet(struct eigrp *eigrp, + int fd, struct interface **ifp, struct stream *ibuf) { int ret; @@ -732,8 +734,7 @@ static struct stream *eigrp_recv_packet(int fd, struct interface **ifp, if ((unsigned int)ret < sizeof(*iph)) /* ret must be > 0 now */ { zlog_warn( - "eigrp_recv_packet: discarding runt packet of length %d " - "(ip header size is %u)", + "eigrp_recv_packet: discarding runt packet of length %d (ip header size is %u)", ret, (unsigned int)sizeof(*iph)); return NULL; } @@ -745,7 +746,7 @@ static struct stream *eigrp_recv_packet(int fd, struct interface **ifp, ip_len = iph->ip_len; -#if !defined(GNU_LINUX) && (OpenBSD < 200311) && (__FreeBSD_version < 1000000) +#if defined(__FreeBSD__) && (__FreeBSD_version < 1000000) /* * Kernel network code touches incoming IP header parameters, * before protocol specific processing. @@ -774,12 +775,11 @@ static struct stream *eigrp_recv_packet(int fd, struct interface **ifp, ifindex = getsockopt_ifindex(AF_INET, &msgh); - *ifp = if_lookup_by_index(ifindex, VRF_DEFAULT); + *ifp = if_lookup_by_index(ifindex, eigrp->vrf_id); if (ret != ip_len) { zlog_warn( - "eigrp_recv_packet read length mismatch: ip_len is %d, " - "but recvmsg returned %d", + "eigrp_recv_packet read length mismatch: ip_len is %d, but recvmsg returned %d", ip_len, ret); return NULL; } @@ -1114,6 +1114,7 @@ static struct TLV_IPv4_Internal_type *eigrp_IPv4_InternalTLV_new(void) struct TLV_IPv4_Internal_type *eigrp_read_ipv4_tlv(struct stream *s) { struct TLV_IPv4_Internal_type *tlv; + uint32_t destination_tmp; tlv = eigrp_IPv4_InternalTLV_new(); @@ -1133,31 +1134,16 @@ struct TLV_IPv4_Internal_type *eigrp_read_ipv4_tlv(struct stream *s) tlv->prefix_length = stream_getc(s); - if (tlv->prefix_length <= 8) { - tlv->destination_part[0] = stream_getc(s); - tlv->destination.s_addr = (tlv->destination_part[0]); - } else if (tlv->prefix_length > 8 && tlv->prefix_length <= 16) { - tlv->destination_part[0] = stream_getc(s); - tlv->destination_part[1] = stream_getc(s); - tlv->destination.s_addr = ((tlv->destination_part[1] << 8) - + tlv->destination_part[0]); - } else if (tlv->prefix_length > 16 && tlv->prefix_length <= 24) { - tlv->destination_part[0] = stream_getc(s); - tlv->destination_part[1] = stream_getc(s); - tlv->destination_part[2] = stream_getc(s); - tlv->destination.s_addr = ((tlv->destination_part[2] << 16) - + (tlv->destination_part[1] << 8) - + tlv->destination_part[0]); - } else if (tlv->prefix_length > 24 && tlv->prefix_length <= 32) { - tlv->destination_part[0] = stream_getc(s); - tlv->destination_part[1] = stream_getc(s); - tlv->destination_part[2] = stream_getc(s); - tlv->destination_part[3] = stream_getc(s); - tlv->destination.s_addr = ((tlv->destination_part[3] << 24) - + (tlv->destination_part[2] << 16) - + (tlv->destination_part[1] << 8) - + tlv->destination_part[0]); - } + destination_tmp = stream_getc(s) << 24; + if (tlv->prefix_length > 8) + destination_tmp |= stream_getc(s) << 16; + if (tlv->prefix_length > 16) + destination_tmp |= stream_getc(s) << 8; + if (tlv->prefix_length > 24) + destination_tmp |= stream_getc(s); + + tlv->destination.s_addr = htonl(destination_tmp); + return tlv; } @@ -1215,7 +1201,7 @@ uint16_t eigrp_add_internalTLV_to_stream(struct stream *s, break; default: flog_err(EC_LIB_DEVELOPMENT, "%s: Unexpected prefix length: %d", - __PRETTY_FUNCTION__, pe->destination->prefixlen); + __func__, pe->destination->prefixlen); return 0; } stream_putl(s, 0x00000000); @@ -1234,15 +1220,13 @@ uint16_t eigrp_add_internalTLV_to_stream(struct stream *s, stream_putc(s, pe->destination->prefixlen); - stream_putc(s, pe->destination->u.prefix4.s_addr & 0xFF); + stream_putc(s, (ntohl(pe->destination->u.prefix4.s_addr) >> 24) & 0xFF); if (pe->destination->prefixlen > 8) - stream_putc(s, (pe->destination->u.prefix4.s_addr >> 8) & 0xFF); + stream_putc(s, (ntohl(pe->destination->u.prefix4.s_addr) >> 16) & 0xFF); if (pe->destination->prefixlen > 16) - stream_putc(s, - (pe->destination->u.prefix4.s_addr >> 16) & 0xFF); + stream_putc(s, (ntohl(pe->destination->u.prefix4.s_addr) >> 8) & 0xFF); if (pe->destination->prefixlen > 24) - stream_putc(s, - (pe->destination->u.prefix4.s_addr >> 24) & 0xFF); + stream_putc(s, ntohl(pe->destination->u.prefix4.s_addr) & 0xFF); return length; } diff --git a/eigrpd/eigrp_reply.c b/eigrpd/eigrp_reply.c index ccf0496736..79405efbbf 100644 --- a/eigrpd/eigrp_reply.c +++ b/eigrpd/eigrp_reply.c @@ -173,7 +173,7 @@ void eigrp_reply_receive(struct eigrp *eigrp, struct ip *iph, flog_err( EC_EIGRP_PACKET, "%s: Received prefix %s which we do not know about", - __PRETTY_FUNCTION__, + __func__, prefix2str(&dest_addr, buf, sizeof(buf))); eigrp_IPv4_InternalTLV_free(tlv); continue; diff --git a/eigrpd/eigrp_routemap.c b/eigrpd/eigrp_routemap.c index ee8d5f7582..e15f777954 100644 --- a/eigrpd/eigrp_routemap.c +++ b/eigrpd/eigrp_routemap.c @@ -135,18 +135,20 @@ void eigrp_rmap_update(const char *notused) static int eigrp_route_match_add(struct vty *vty, struct route_map_index *index, const char *command, const char *arg) { - int ret; - ret = route_map_add_match(index, command, arg); + enum rmap_compile_rets ret; + + ret = route_map_add_match(index, command, arg, type); switch (ret) { case RMAP_RULE_MISSING: vty_out(vty, "%% Can't find rule.\n"); return CMD_WARNING_CONFIG_FAILED; - break; case RMAP_COMPILE_ERROR: vty_out(vty, "%% Argument is malformed.\n"); return CMD_WARNING_CONFIG_FAILED; - break; case RMAP_COMPILE_SUCCESS: + /* + * Intentionally not handling these cases + */ break; } @@ -158,18 +160,20 @@ static int eigrp_route_match_delete(struct vty *vty, struct route_map_index *index, const char *command, const char *arg) { - int ret; - ret = route_map_delete_match(index, command, arg); + enum rmap_compile_rets ret; + + ret = route_map_delete_match(index, command, arg, type); switch (ret) { case RMAP_RULE_MISSING: vty_out(vty, "%% Can't find rule.\n"); return CMD_WARNING_CONFIG_FAILED; - break; case RMAP_COMPILE_ERROR: vty_out(vty, "%% Argument is malformed.\n"); return CMD_WARNING_CONFIG_FAILED; - break; case RMAP_COMPILE_SUCCESS: + /* + * These cases intentionally ignored + */ break; } @@ -180,14 +184,13 @@ static int eigrp_route_match_delete(struct vty *vty, static int eigrp_route_set_add(struct vty *vty, struct route_map_index *index, const char *command, const char *arg) { - int ret; + enum rmap_compile_rets ret; ret = route_map_add_set(index, command, arg); switch (ret) { case RMAP_RULE_MISSING: vty_out(vty, "%% Can't find rule.\n"); return CMD_WARNING_CONFIG_FAILED; - break; case RMAP_COMPILE_ERROR: /* * rip, ripng and other protocols share the set metric command @@ -201,6 +204,9 @@ static int eigrp_route_set_add(struct vty *vty, struct route_map_index *index, } break; case RMAP_COMPILE_SUCCESS: + /* + * These cases intentionally left blank here + */ break; } @@ -212,19 +218,20 @@ static int eigrp_route_set_delete(struct vty *vty, struct route_map_index *index, const char *command, const char *arg) { - int ret; + enum rmap_compile_rets ret; ret = route_map_delete_set(index, command, arg); switch (ret) { case RMAP_RULE_MISSING: vty_out(vty, "%% Can't find rule.\n"); return CMD_WARNING_CONFIG_FAILED; - break; case RMAP_COMPILE_ERROR: vty_out(vty, "%% Argument is malformed.\n"); return CMD_WARNING_CONFIG_FAILED; - break; case RMAP_COMPILE_SUCCESS: + /* + * These cases intentionally not handled + */ break; } @@ -251,9 +258,9 @@ void eigrp_route_map_update(const char *notused) /* `match metric METRIC' */ /* Match function return 1 if match is success else return zero. */ -static route_map_result_t route_match_metric(void *rule, struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_match_metric(void *rule, struct prefix *prefix, route_map_object_t type, + void *object) { // uint32_t *metric; // uint32_t check; @@ -288,7 +295,7 @@ static void *route_match_metric_compile(const char *arg) { // uint32_t *metric; // - // metric = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (uint32_t)); + // metric = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof(uint32_t)); // *metric = atoi (arg); // // if(*metric > 0) @@ -305,16 +312,18 @@ static void route_match_metric_free(void *rule) } /* Route map commands for metric matching. */ -struct route_map_rule_cmd route_match_metric_cmd = { - "metric", route_match_metric, route_match_metric_compile, - route_match_metric_free}; +static const struct route_map_rule_cmd route_match_metric_cmd = { + "metric", + route_match_metric, + route_match_metric_compile, + route_match_metric_free +}; /* `match interface IFNAME' */ /* Match function return 1 if match is success else return zero. */ -static route_map_result_t route_match_interface(void *rule, - struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_match_interface(void *rule, struct prefix *prefix, + route_map_object_t type, void *object) { // struct rip_info *rinfo; // struct interface *ifp; @@ -353,17 +362,19 @@ static void route_match_interface_free(void *rule) } /* Route map commands for interface matching. */ -struct route_map_rule_cmd route_match_interface_cmd = { - "interface", route_match_interface, route_match_interface_compile, - route_match_interface_free}; +static const struct route_map_rule_cmd route_match_interface_cmd = { + "interface", + route_match_interface, + route_match_interface_compile, + route_match_interface_free +}; /* `match ip next-hop IP_ACCESS_LIST' */ /* Match function return 1 if match is success else return zero. */ -static route_map_result_t route_match_ip_next_hop(void *rule, - struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_match_ip_next_hop(void *rule, struct prefix *prefix, + route_map_object_t type, void *object) { // struct access_list *alist; // struct rip_info *rinfo; @@ -401,13 +412,16 @@ static void route_match_ip_next_hop_free(void *rule) } /* Route map commands for ip next-hop matching. */ -static struct route_map_rule_cmd route_match_ip_next_hop_cmd = { - "ip next-hop", route_match_ip_next_hop, route_match_ip_next_hop_compile, - route_match_ip_next_hop_free}; +static const struct route_map_rule_cmd route_match_ip_next_hop_cmd = { + "ip next-hop", + route_match_ip_next_hop, + route_match_ip_next_hop_compile, + route_match_ip_next_hop_free +}; /* `match ip next-hop prefix-list PREFIX_LIST' */ -static route_map_result_t +static enum route_map_cmd_result_t route_match_ip_next_hop_prefix_list(void *rule, struct prefix *prefix, route_map_object_t type, void *object) { @@ -443,19 +457,21 @@ static void route_match_ip_next_hop_prefix_list_free(void *rule) XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); } -static struct route_map_rule_cmd route_match_ip_next_hop_prefix_list_cmd = { - "ip next-hop prefix-list", route_match_ip_next_hop_prefix_list, +static const struct route_map_rule_cmd + route_match_ip_next_hop_prefix_list_cmd = { + "ip next-hop prefix-list", + route_match_ip_next_hop_prefix_list, route_match_ip_next_hop_prefix_list_compile, - route_match_ip_next_hop_prefix_list_free}; + route_match_ip_next_hop_prefix_list_free +}; /* `match ip address IP_ACCESS_LIST' */ /* Match function should return 1 if match is success else return zero. */ -static route_map_result_t route_match_ip_address(void *rule, - struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_match_ip_address(void *rule, struct prefix *prefix, + route_map_object_t type, void *object) { struct access_list *alist; @@ -485,13 +501,16 @@ static void route_match_ip_address_free(void *rule) } /* Route map commands for ip address matching. */ -static struct route_map_rule_cmd route_match_ip_address_cmd = { - "ip address", route_match_ip_address, route_match_ip_address_compile, - route_match_ip_address_free}; +static const struct route_map_rule_cmd route_match_ip_address_cmd = { + "ip address", + route_match_ip_address, + route_match_ip_address_compile, + route_match_ip_address_free +}; /* `match ip address prefix-list PREFIX_LIST' */ -static route_map_result_t +static enum route_map_cmd_result_t route_match_ip_address_prefix_list(void *rule, struct prefix *prefix, route_map_object_t type, void *object) { @@ -519,15 +538,19 @@ static void route_match_ip_address_prefix_list_free(void *rule) XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); } -static struct route_map_rule_cmd route_match_ip_address_prefix_list_cmd = { - "ip address prefix-list", route_match_ip_address_prefix_list, +static const struct route_map_rule_cmd + route_match_ip_address_prefix_list_cmd = { + "ip address prefix-list", + route_match_ip_address_prefix_list, route_match_ip_address_prefix_list_compile, - route_match_ip_address_prefix_list_free}; + route_match_ip_address_prefix_list_free +}; /* `match tag TAG' */ /* Match function return 1 if match is success else return zero. */ -static route_map_result_t route_match_tag(void *rule, struct prefix *prefix, - route_map_object_t type, void *object) +static enum route_map_cmd_result_t +route_match_tag(void *rule, struct prefix *prefix, route_map_object_t type, + void *object) { // unsigned short *tag; // struct rip_info *rinfo; @@ -551,7 +574,7 @@ static void *route_match_tag_compile(const char *arg) { // unsigned short *tag; // - // tag = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (unsigned short)); + // tag = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof(unsigned short)); // *tag = atoi (arg); // // return tag; @@ -564,13 +587,17 @@ static void route_match_tag_free(void *rule) } /* Route map commands for tag matching. */ -struct route_map_rule_cmd route_match_tag_cmd = { - "tag", route_match_tag, route_match_tag_compile, route_match_tag_free}; +static const struct route_map_rule_cmd route_match_tag_cmd = { + "tag", + route_match_tag, + route_match_tag_compile, + route_match_tag_free +}; /* Set metric to attribute. */ -static route_map_result_t route_set_metric(void *rule, struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_set_metric(void *rule, struct prefix *prefix, + route_map_object_t type, void *object) { // if (type == RMAP_RIP) // { @@ -640,7 +667,7 @@ static void *route_set_metric_compile(const char *arg) // return NULL;*/ // // mod = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, - // sizeof (struct rip_metric_modifier)); + // sizeof(struct rip_metric_modifier)); // mod->type = type; // mod->metric = metric; @@ -654,18 +681,19 @@ static void route_set_metric_free(void *rule) } /* Set metric rule structure. */ -static struct route_map_rule_cmd route_set_metric_cmd = { - "metric", route_set_metric, route_set_metric_compile, +static const struct route_map_rule_cmd route_set_metric_cmd = { + "metric", + route_set_metric, + route_set_metric_compile, route_set_metric_free, }; /* `set ip next-hop IP_ADDRESS' */ /* Set nexthop to object. ojbect must be pointer to struct attr. */ -static route_map_result_t route_set_ip_nexthop(void *rule, - struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_set_ip_nexthop(void *rule, struct prefix *prefix, + route_map_object_t type, void *object) { // struct in_addr *address; // struct rip_info *rinfo; @@ -690,7 +718,7 @@ static void *route_set_ip_nexthop_compile(const char *arg) // int ret; // struct in_addr *address; // - // address = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (struct + // address = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof(struct // in_addr)); // // ret = inet_aton (arg, address); @@ -711,15 +739,19 @@ static void route_set_ip_nexthop_free(void *rule) } /* Route map commands for ip nexthop set. */ -static struct route_map_rule_cmd route_set_ip_nexthop_cmd = { - "ip next-hop", route_set_ip_nexthop, route_set_ip_nexthop_compile, - route_set_ip_nexthop_free}; +static const struct route_map_rule_cmd route_set_ip_nexthop_cmd = { + "ip next-hop", + route_set_ip_nexthop, + route_set_ip_nexthop_compile, + route_set_ip_nexthop_free +}; /* `set tag TAG' */ /* Set tag to object. ojbect must be pointer to struct attr. */ -static route_map_result_t route_set_tag(void *rule, struct prefix *prefix, - route_map_object_t type, void *object) +static enum route_map_cmd_result_t +route_set_tag(void *rule, struct prefix *prefix, + route_map_object_t type, void *object) { // unsigned short *tag; // struct rip_info *rinfo; @@ -743,7 +775,7 @@ static void *route_set_tag_compile(const char *arg) { // unsigned short *tag; // - // tag = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (unsigned short)); + // tag = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof(unsigned short)); // *tag = atoi (arg); // // return tag; @@ -756,15 +788,19 @@ static void route_set_tag_free(void *rule) } /* Route map commands for tag set. */ -static struct route_map_rule_cmd route_set_tag_cmd = { - "tag", route_set_tag, route_set_tag_compile, route_set_tag_free}; +static const struct route_map_rule_cmd route_set_tag_cmd = { + "tag", + route_set_tag, + route_set_tag_compile, + route_set_tag_free +}; #define MATCH_STR "Match values from routing table\n" #define SET_STR "Set values in destination routing protocol\n" DEFUN (match_metric, match_metric_cmd, - "match metric <0-4294967295>", + "match metric (0-4294967295)", MATCH_STR "Match metric of route\n" "Metric value\n") @@ -787,7 +823,7 @@ DEFUN (no_match_metric, } ALIAS(no_match_metric, no_match_metric_val_cmd, - "no match metric <0-4294967295>", NO_STR MATCH_STR + "no match metric (0-4294967295)", NO_STR MATCH_STR "Match metric of route\n" "Metric value\n") @@ -822,7 +858,7 @@ ALIAS(no_match_interface, no_match_interface_val_cmd, "no match interface WORD", DEFUN (match_ip_next_hop, match_ip_next_hop_cmd, - "match ip next-hop (<1-199>|<1300-2699>|WORD)", + "match ip next-hop ((1-199)|(1300-2699)|WORD)", MATCH_STR IP_STR "Match next-hop address of route\n" @@ -850,7 +886,7 @@ DEFUN (no_match_ip_next_hop, } ALIAS(no_match_ip_next_hop, no_match_ip_next_hop_val_cmd, - "no match ip next-hop (<1-199>|<1300-2699>|WORD)", NO_STR MATCH_STR IP_STR + "no match ip next-hop ((1-199)|(1300-2699)|WORD)", NO_STR MATCH_STR IP_STR "Match next-hop address of route\n" "IP access-list number\n" "IP access-list number (expanded range)\n" @@ -895,7 +931,7 @@ ALIAS(no_match_ip_next_hop_prefix_list, DEFUN (match_ip_address, match_ip_address_cmd, - "match ip address (<1-199>|<1300-2699>|WORD)", + "match ip address ((1-199)|(1300-2699)|WORD)", MATCH_STR IP_STR "Match address of route\n" @@ -922,7 +958,7 @@ DEFUN (no_match_ip_address, } ALIAS(no_match_ip_address, no_match_ip_address_val_cmd, - "no match ip address (<1-199>|<1300-2699>|WORD)", NO_STR MATCH_STR IP_STR + "no match ip address ((1-199)|(1300-2699)|WORD)", NO_STR MATCH_STR IP_STR "Match address of route\n" "IP access-list number\n" "IP access-list number (expanded range)\n" @@ -966,7 +1002,7 @@ ALIAS(no_match_ip_address_prefix_list, no_match_ip_address_prefix_list_val_cmd, DEFUN (match_tag, match_tag_cmd, - "match tag <0-65535>", + "match tag (0-65535)", MATCH_STR "Match tag of route\n" "Metric value\n") @@ -987,7 +1023,7 @@ DEFUN (no_match_tag, return eigrp_route_match_delete(vty, vty->index, "tag", argv[0]); } -ALIAS(no_match_tag, no_match_tag_val_cmd, "no match tag <0-65535>", +ALIAS(no_match_tag, no_match_tag_val_cmd, "no match tag (0-65535)", NO_STR MATCH_STR "Match tag of route\n" "Metric value\n") @@ -996,7 +1032,7 @@ ALIAS(no_match_tag, no_match_tag_val_cmd, "no match tag <0-65535>", DEFUN (set_metric, set_metric_cmd, - "set metric <0-4294967295>", + "set metric (0-4294967295)", SET_STR "Metric value for destination routing protocol\n" "Metric value\n") @@ -1022,7 +1058,7 @@ DEFUN (no_set_metric, } ALIAS(no_set_metric, no_set_metric_val_cmd, - "no set metric (<0-4294967295>|<+/-metric>)", NO_STR SET_STR + "no set metric ((0-4294967295)|<+/-metric>)", NO_STR SET_STR "Metric value for destination routing protocol\n" "Metric value\n" "Add or subtract metric\n") @@ -1069,7 +1105,7 @@ ALIAS(no_set_ip_nexthop, no_set_ip_nexthop_val_cmd, DEFUN (set_tag, set_tag_cmd, - "set tag <0-65535>", + "set tag (0-65535)", SET_STR "Tag value for routing protocol\n" "Tag value\n") @@ -1090,7 +1126,7 @@ DEFUN (no_set_tag, return eigrp_route_set_delete(vty, vty->index, "tag", argv[0]); } -ALIAS(no_set_tag, no_set_tag_val_cmd, "no set tag <0-65535>", NO_STR SET_STR +ALIAS(no_set_tag, no_set_tag_val_cmd, "no set tag (0-65535)", NO_STR SET_STR "Tag value for routing protocol\n" "Tag value\n") diff --git a/eigrpd/eigrp_snmp.c b/eigrpd/eigrp_snmp.c index 21c92386ae..3b232be386 100644 --- a/eigrpd/eigrp_snmp.c +++ b/eigrpd/eigrp_snmp.c @@ -619,14 +619,12 @@ static uint8_t *eigrpVpnEntry(struct variable *v, oid *name, size_t *length, return SNMP_INTEGER(1); } else return SNMP_INTEGER(0); - break; case EIGRPVPNNAME: /* 2 */ /* The name given to the VPN */ if (eigrp) { return SNMP_INTEGER(1); } else return SNMP_INTEGER(0); - break; default: return NULL; } @@ -680,14 +678,12 @@ static uint8_t *eigrpTraffStatsEntry(struct variable *v, oid *name, return SNMP_INTEGER(eigrp->AS); else return SNMP_INTEGER(0); - break; case EIGRPNBRCOUNT: /* 2 */ /* Neighbor count of this EIGRP instance */ if (eigrp) return SNMP_INTEGER(eigrp_neighbor_count(eigrp)); else return SNMP_INTEGER(0); - break; case EIGRPHELLOSSENT: /* 3 */ /* Hello packets output count */ if (eigrp) { @@ -699,7 +695,6 @@ static uint8_t *eigrpTraffStatsEntry(struct variable *v, oid *name, return SNMP_INTEGER(counter); } else return SNMP_INTEGER(0); - break; case EIGRPHELLOSRCVD: /* 4 */ /* Hello packets input count */ if (eigrp) { @@ -711,7 +706,6 @@ static uint8_t *eigrpTraffStatsEntry(struct variable *v, oid *name, return SNMP_INTEGER(counter); } else return SNMP_INTEGER(0); - break; case EIGRPUPDATESSENT: /* 5 */ /* Update packets output count */ if (eigrp) { @@ -723,7 +717,6 @@ static uint8_t *eigrpTraffStatsEntry(struct variable *v, oid *name, return SNMP_INTEGER(counter); } else return SNMP_INTEGER(0); - break; case EIGRPUPDATESRCVD: /* 6 */ /* Update packets input count */ if (eigrp) { @@ -735,7 +728,6 @@ static uint8_t *eigrpTraffStatsEntry(struct variable *v, oid *name, return SNMP_INTEGER(counter); } else return SNMP_INTEGER(0); - break; case EIGRPQUERIESSENT: /* 7 */ /* Querry packets output count */ if (eigrp) { @@ -747,7 +739,6 @@ static uint8_t *eigrpTraffStatsEntry(struct variable *v, oid *name, return SNMP_INTEGER(counter); } else return SNMP_INTEGER(0); - break; case EIGRPQUERIESRCVD: /* 8 */ /* Querry packets input count */ if (eigrp) { @@ -759,7 +750,6 @@ static uint8_t *eigrpTraffStatsEntry(struct variable *v, oid *name, return SNMP_INTEGER(counter); } else return SNMP_INTEGER(0); - break; case EIGRPREPLIESSENT: /* 9 */ /* Reply packets output count */ if (eigrp) { @@ -771,7 +761,6 @@ static uint8_t *eigrpTraffStatsEntry(struct variable *v, oid *name, return SNMP_INTEGER(counter); } else return SNMP_INTEGER(0); - break; case EIGRPREPLIESRCVD: /* 10 */ /* Reply packets input count */ if (eigrp) { @@ -783,7 +772,6 @@ static uint8_t *eigrpTraffStatsEntry(struct variable *v, oid *name, return SNMP_INTEGER(counter); } else return SNMP_INTEGER(0); - break; case EIGRPACKSSENT: /* 11 */ /* Acknowledgement packets output count */ if (eigrp) { @@ -795,7 +783,6 @@ static uint8_t *eigrpTraffStatsEntry(struct variable *v, oid *name, return SNMP_INTEGER(counter); } else return SNMP_INTEGER(0); - break; case EIGRPACKSRCVD: /* 12 */ /* Acknowledgement packets input count */ if (eigrp) { @@ -807,21 +794,18 @@ static uint8_t *eigrpTraffStatsEntry(struct variable *v, oid *name, return SNMP_INTEGER(counter); } else return SNMP_INTEGER(0); - break; case EIGRPINPUTQHIGHMARK: /* 13 */ /* The highest number of EIGRP packets in the input queue */ if (eigrp) { return SNMP_INTEGER(1); } else return SNMP_INTEGER(0); - break; case EIGRPINPUTQDROPS: /* 14 */ /* The number of EIGRP packets dropped from the input queue */ if (eigrp) { return SNMP_INTEGER(1); } else return SNMP_INTEGER(0); - break; case EIGRPSIAQUERIESSENT: /* 15 */ /* SIA querry packets output count */ if (eigrp) { @@ -833,7 +817,6 @@ static uint8_t *eigrpTraffStatsEntry(struct variable *v, oid *name, return SNMP_INTEGER(counter); } else return SNMP_INTEGER(0); - break; case EIGRPSIAQUERIESRCVD: /* 16 */ /* SIA querry packets input count */ if (eigrp) { @@ -845,7 +828,6 @@ static uint8_t *eigrpTraffStatsEntry(struct variable *v, oid *name, return SNMP_INTEGER(counter); } else return SNMP_INTEGER(0); - break; case EIGRPASROUTERIDTYPE: /* 17 */ /* Whether the router ID is set manually or automatically */ if (eigrp) @@ -855,7 +837,6 @@ static uint8_t *eigrpTraffStatsEntry(struct variable *v, oid *name, return SNMP_INTEGER(1); else return SNMP_INTEGER(0); - break; case EIGRPASROUTERID: /* 18 */ /* Router ID for this EIGRP AS */ if (eigrp) @@ -865,7 +846,6 @@ static uint8_t *eigrpTraffStatsEntry(struct variable *v, oid *name, return SNMP_INTEGER(eigrp->router_id); else return SNMP_INTEGER(0); - break; case EIGRPTOPOROUTES: /* 19 */ /* The total number of EIGRP derived routes currently existing in the topology table for the AS */ @@ -873,7 +853,6 @@ static uint8_t *eigrpTraffStatsEntry(struct variable *v, oid *name, return SNMP_INTEGER(1); } else return SNMP_INTEGER(0); - break; case EIGRPHEADSERIAL: /* 20 */ /* The serial number of the first route in the internal sequence for an AS*/ @@ -881,7 +860,6 @@ static uint8_t *eigrpTraffStatsEntry(struct variable *v, oid *name, return SNMP_INTEGER(1); } else return SNMP_INTEGER(0); - break; case EIGRPNEXTSERIAL: /* 21 */ /* The serial number that would be assigned to the next new or changed route in the topology table for the AS*/ @@ -889,7 +867,6 @@ static uint8_t *eigrpTraffStatsEntry(struct variable *v, oid *name, return SNMP_INTEGER(1); } else return SNMP_INTEGER(0); - break; case EIGRPXMITPENDREPLIES: /* 22 */ /* Total number of outstanding replies expected to queries that have been sent to peers in the current AS*/ @@ -897,7 +874,6 @@ static uint8_t *eigrpTraffStatsEntry(struct variable *v, oid *name, return SNMP_INTEGER(1); } else return SNMP_INTEGER(0); - break; case EIGRPXMITDUMMIES: /* 23 */ /* Total number of currently existing dummies associated with * the AS*/ @@ -905,7 +881,6 @@ static uint8_t *eigrpTraffStatsEntry(struct variable *v, oid *name, return SNMP_INTEGER(1); } else return SNMP_INTEGER(0); - break; default: return NULL; } @@ -934,7 +909,6 @@ static uint8_t *eigrpTopologyEntry(struct variable *v, oid *name, return SNMP_INTEGER(1); } else return SNMP_INTEGER(0); - break; case EIGRPDESTNET: /* 2 */ /* The destination IP network number for a single route in the * topology table*/ @@ -942,7 +916,6 @@ static uint8_t *eigrpTopologyEntry(struct variable *v, oid *name, return SNMP_INTEGER(1); } else return SNMP_INTEGER(0); - break; case EIGRPDESTNETPREFIXLEN: /* 4 */ /* The prefix length associated with the destination IP network address @@ -951,7 +924,6 @@ static uint8_t *eigrpTopologyEntry(struct variable *v, oid *name, return SNMP_INTEGER(1); } else return SNMP_INTEGER(0); - break; case EIGRPACTIVE: /* 5 */ /* A value of true(1) indicates the route to the destination network has failed @@ -961,7 +933,6 @@ static uint8_t *eigrpTopologyEntry(struct variable *v, oid *name, return SNMP_INTEGER(1); } else return SNMP_INTEGER(0); - break; case EIGRPSTUCKINACTIVE: /* 6 */ /* A value of true(1) indicates that that this route which is in active state @@ -971,14 +942,12 @@ static uint8_t *eigrpTopologyEntry(struct variable *v, oid *name, return SNMP_INTEGER(1); } else return SNMP_INTEGER(0); - break; case EIGRPDESTSUCCESSORS: /* 7 */ /* Next routing hop for a path to the destination IP network */ if (eigrp) { return SNMP_INTEGER(1); } else return SNMP_INTEGER(0); - break; case EIGRPFDISTANCE: /* 8 */ /* Minimum distance from this router to the destination IP * network */ @@ -986,7 +955,6 @@ static uint8_t *eigrpTopologyEntry(struct variable *v, oid *name, return SNMP_INTEGER(1); } else return SNMP_INTEGER(0); - break; case EIGRPROUTEORIGINTYPE: /* 9 */ /* Text string describing the internal origin of the EIGRP route */ @@ -994,7 +962,6 @@ static uint8_t *eigrpTopologyEntry(struct variable *v, oid *name, return SNMP_INTEGER(1); } else return SNMP_INTEGER(0); - break; case EIGRPROUTEORIGINADDRTYPE: /* 10 */ /* The format of the IP address defined as the origin of this topology route entry */ @@ -1002,7 +969,6 @@ static uint8_t *eigrpTopologyEntry(struct variable *v, oid *name, return SNMP_INTEGER(1); } else return SNMP_INTEGER(0); - break; case EIGRPROUTEORIGINADDR: /* 11 */ /* If the origin of the topology route entry is external to this router, @@ -1012,21 +978,18 @@ static uint8_t *eigrpTopologyEntry(struct variable *v, oid *name, return SNMP_INTEGER(1); } else return SNMP_INTEGER(0); - break; case EIGRPNEXTHOPADDRESSTYPE: /* 12 */ /* The format of the next hop IP address */ if (eigrp) { return SNMP_INTEGER(1); } else return SNMP_INTEGER(0); - break; case EIGRPNEXTHOPADDRESS: /* 13 */ /* Next hop IP address for the route */ if (eigrp) { return SNMP_INTEGER(1); } else return SNMP_INTEGER(0); - break; case EIGRPNEXTHOPINTERFACE: /* 14 */ /* The interface through which the next hop IP address is * reached */ @@ -1034,7 +997,6 @@ static uint8_t *eigrpTopologyEntry(struct variable *v, oid *name, return SNMP_INTEGER(1); } else return SNMP_INTEGER(0); - break; case EIGRPDISTANCE: /* 15 */ /* The computed distance to the destination network entry from * this router */ @@ -1042,7 +1004,6 @@ static uint8_t *eigrpTopologyEntry(struct variable *v, oid *name, return SNMP_INTEGER(1); } else return SNMP_INTEGER(0); - break; case EIGRPREPORTDISTANCE: /* 16 */ /* The computed distance to the destination network in the topology entry @@ -1051,7 +1012,6 @@ static uint8_t *eigrpTopologyEntry(struct variable *v, oid *name, return SNMP_INTEGER(1); } else return SNMP_INTEGER(0); - break; default: return NULL; } @@ -1093,7 +1053,6 @@ static uint8_t *eigrpPeerEntry(struct variable *v, oid *name, size_t *length, return SNMP_INTEGER(1); } else return SNMP_INTEGER(0); - break; case EIGRPPEERADDRTYPE: /* 2 */ /* The format of the remote source IP address used by the peer */ @@ -1101,21 +1060,18 @@ static uint8_t *eigrpPeerEntry(struct variable *v, oid *name, size_t *length, return SNMP_INTEGER(1); } else return SNMP_INTEGER(0); - break; case EIGRPPEERADDR: /* 3 */ /* The source IP address used by the peer */ if (eigrp) { return SNMP_INTEGER(1); } else return SNMP_INTEGER(0); - break; case EIGRPPEERIFINDEX: /* 4 */ /* The ifIndex of the interface on this router */ if (eigrp) { return SNMP_INTEGER(1); } else return SNMP_INTEGER(0); - break; case EIGRPHOLDTIME: /* 5 */ /* How much time must pass without receiving a hello packet from this @@ -1124,7 +1080,6 @@ static uint8_t *eigrpPeerEntry(struct variable *v, oid *name, size_t *length, return SNMP_INTEGER(1); } else return SNMP_INTEGER(0); - break; case EIGRPUPTIME: /* 6 */ /* The elapsed time since the EIGRP adjacency was first * established */ @@ -1132,7 +1087,6 @@ static uint8_t *eigrpPeerEntry(struct variable *v, oid *name, size_t *length, return SNMP_INTEGER(1); } else return SNMP_INTEGER(0); - break; case EIGRPSRTT: /* 7 */ /* The computed smooth round trip time for packets to and from * the peer */ @@ -1140,42 +1094,36 @@ static uint8_t *eigrpPeerEntry(struct variable *v, oid *name, size_t *length, return SNMP_INTEGER(1); } else return SNMP_INTEGER(0); - break; case EIGRPRTO: /* 8 */ /* The computed retransmission timeout for the peer */ if (eigrp) { return SNMP_INTEGER(1); } else return SNMP_INTEGER(0); - break; case EIGRPPKTSENQUEUED: /* 9 */ /* The number of any EIGRP packets currently enqueued */ if (eigrp) { return SNMP_INTEGER(1); } else return SNMP_INTEGER(0); - break; case EIGRPLASTSEQ: /* 10 */ /* sequence number of the last EIGRP packet sent to this peer */ if (eigrp) { return SNMP_INTEGER(1); } else return SNMP_INTEGER(0); - break; case EIGRPVERSION: /* 11 */ /* The EIGRP version information reported by the remote peer */ if (eigrp) { return SNMP_INTEGER(1); } else return SNMP_INTEGER(0); - break; case EIGRPRETRANS: /* 12 */ /* The cumulative number of retransmissions to this peer */ if (eigrp) { return SNMP_INTEGER(1); } else return SNMP_INTEGER(0); - break; case EIGRPRETRIES: /* 13 */ /* The number of times the current unacknowledged packet has * been retried */ @@ -1183,7 +1131,6 @@ static uint8_t *eigrpPeerEntry(struct variable *v, oid *name, size_t *length, return SNMP_INTEGER(1); } else return SNMP_INTEGER(0); - break; default: return NULL; } @@ -1215,7 +1162,6 @@ static uint8_t *eigrpInterfaceEntry(struct variable *v, oid *name, return SNMP_INTEGER(eigrp_neighbor_count(eigrp)); } else return SNMP_INTEGER(0); - break; case EIGRPXMITRELIABLEQ: /* 4 */ /* The number of EIGRP packets currently waiting in the reliable transport transmission queue */ @@ -1223,7 +1169,6 @@ static uint8_t *eigrpInterfaceEntry(struct variable *v, oid *name, return SNMP_INTEGER(1); } else return SNMP_INTEGER(0); - break; case EIGRPXMITUNRELIABLEQ: /* 5 */ /* The number of EIGRP packets currently waiting in the unreliable @@ -1232,7 +1177,6 @@ static uint8_t *eigrpInterfaceEntry(struct variable *v, oid *name, return SNMP_INTEGER(1); } else return SNMP_INTEGER(0); - break; case EIGRPMEANSRTT: /* 6 */ /* The average of all the computed smooth round trip time values for a packet to and from all peers established on this @@ -1241,7 +1185,6 @@ static uint8_t *eigrpInterfaceEntry(struct variable *v, oid *name, return SNMP_INTEGER(1); } else return SNMP_INTEGER(0); - break; case EIGRPPACINGRELIABLE: /* 7 */ /* The configured time interval between EIGRP packet * transmissions */ @@ -1249,7 +1192,6 @@ static uint8_t *eigrpInterfaceEntry(struct variable *v, oid *name, return SNMP_INTEGER(1); } else return SNMP_INTEGER(0); - break; case EIGRPPACINGUNRELIABLE: /* 8 */ /* The configured time interval between EIGRP packet transmissions @@ -1259,14 +1201,12 @@ static uint8_t *eigrpInterfaceEntry(struct variable *v, oid *name, return SNMP_INTEGER(1); } else return SNMP_INTEGER(0); - break; case EIGRPMFLOWTIMER: /* 9 */ /* The configured multicast flow control timer value */ if (eigrp) { return SNMP_INTEGER(1); } else return SNMP_INTEGER(0); - break; case EIGRPPENDINGROUTES: /* 10 */ /* The number of queued EIGRP routing updates awaiting * transmission */ @@ -1274,7 +1214,6 @@ static uint8_t *eigrpInterfaceEntry(struct variable *v, oid *name, return SNMP_INTEGER(1); } else return SNMP_INTEGER(0); - break; case EIGRPHELLOINTERVAL: /* 11 */ /* The configured time interval between Hello packet * transmissions */ @@ -1282,7 +1221,6 @@ static uint8_t *eigrpInterfaceEntry(struct variable *v, oid *name, return SNMP_INTEGER(1); } else return SNMP_INTEGER(0); - break; case EIGRPXMITNEXTSERIAL: /* 12 */ /* The serial number of the next EIGRP packet that is to be queued @@ -1291,7 +1229,6 @@ static uint8_t *eigrpInterfaceEntry(struct variable *v, oid *name, return SNMP_INTEGER(1); } else return SNMP_INTEGER(0); - break; case EIGRPUMCASTS: /* 13 */ /* The total number of unreliable EIGRP multicast packets sent on this interface */ @@ -1299,7 +1236,6 @@ static uint8_t *eigrpInterfaceEntry(struct variable *v, oid *name, return SNMP_INTEGER(1); } else return SNMP_INTEGER(0); - break; case EIGRPRMCASTS: /* 14 */ /* The total number of reliable EIGRP multicast packets sent on this interface */ @@ -1307,7 +1243,6 @@ static uint8_t *eigrpInterfaceEntry(struct variable *v, oid *name, return SNMP_INTEGER(1); } else return SNMP_INTEGER(0); - break; case EIGRPUUCASTS: /* 15 */ /* The total number of unreliable EIGRP unicast packets sent on this interface */ @@ -1315,7 +1250,6 @@ static uint8_t *eigrpInterfaceEntry(struct variable *v, oid *name, return SNMP_INTEGER(1); } else return SNMP_INTEGER(0); - break; case EIGRPRUCASTS: /* 16 */ /* The total number of reliable EIGRP unicast packets sent on this interface */ @@ -1323,7 +1257,6 @@ static uint8_t *eigrpInterfaceEntry(struct variable *v, oid *name, return SNMP_INTEGER(1); } else return SNMP_INTEGER(0); - break; case EIGRPMCASTEXCEPTS: /* 17 */ /* The total number of EIGRP multicast exception transmissions */ @@ -1331,7 +1264,6 @@ static uint8_t *eigrpInterfaceEntry(struct variable *v, oid *name, return SNMP_INTEGER(1); } else return SNMP_INTEGER(0); - break; case EIGRPCRPKTS: /* 18 */ /* The total number EIGRP Conditional-Receive packets sent on * this interface */ @@ -1339,7 +1271,6 @@ static uint8_t *eigrpInterfaceEntry(struct variable *v, oid *name, return SNMP_INTEGER(1); } else return SNMP_INTEGER(0); - break; case EIGRPACKSSUPPRESSED: /* 19 */ /* The total number of individual EIGRP acknowledgement packets that have been @@ -1349,7 +1280,6 @@ static uint8_t *eigrpInterfaceEntry(struct variable *v, oid *name, return SNMP_INTEGER(1); } else return SNMP_INTEGER(0); - break; case EIGRPRETRANSSENT: /* 20 */ /* The total number EIGRP packet retransmissions sent on the * interface */ @@ -1357,21 +1287,18 @@ static uint8_t *eigrpInterfaceEntry(struct variable *v, oid *name, return SNMP_INTEGER(1); } else return SNMP_INTEGER(0); - break; case EIGRPOOSRCVD: /* 21 */ /* The total number of out-of-sequence EIGRP packets received */ if (eigrp) { return SNMP_INTEGER(1); } else return SNMP_INTEGER(0); - break; case EIGRPAUTHMODE: /* 22 */ /* The EIGRP authentication mode of the interface */ if (eigrp) { return SNMP_INTEGER(1); } else return SNMP_INTEGER(0); - break; case EIGRPAUTHKEYCHAIN: /* 23 */ /* The name of the authentication key-chain configured on this interface. */ diff --git a/eigrpd/eigrp_structs.h b/eigrpd/eigrp_structs.h index a78e5a53cf..1337ec966f 100644 --- a/eigrpd/eigrp_structs.h +++ b/eigrpd/eigrp_structs.h @@ -60,7 +60,7 @@ struct eigrp_master { struct eigrp_metrics { uint32_t delay; uint32_t bandwidth; - unsigned char mtu[3]; + uint8_t mtu[3]; uint8_t hop_count; uint8_t reliability; uint8_t load; @@ -69,6 +69,8 @@ struct eigrp_metrics { }; struct eigrp { + vrf_id_t vrf_id; + uint16_t AS; /* Autonomous system number */ uint16_t vrid; /* Virtual Router ID */ uint8_t k_values[6]; /*Array for K values configuration*/ @@ -85,7 +87,7 @@ struct eigrp { struct list *eiflist; /* eigrp interfaces */ uint8_t passive_interface_default; /* passive-interface default */ - unsigned int fd; + int fd; unsigned int maxsndbuflen; uint32_t sequence_number; /*Global EIGRP sequence number*/ @@ -174,6 +176,8 @@ struct eigrp_interface { /* To which multicast groups do we currently belong? */ + uint32_t curr_bandwidth; + uint32_t curr_mtu; uint8_t multicast_memberships; @@ -409,7 +413,6 @@ struct TLV_IPv4_Internal_type { uint8_t prefix_length; - unsigned char destination_part[4]; struct in_addr destination; } __attribute__((packed)); diff --git a/eigrpd/eigrp_topology.c b/eigrpd/eigrp_topology.c index e861cdb333..5247413d2f 100644 --- a/eigrpd/eigrp_topology.c +++ b/eigrpd/eigrp_topology.c @@ -117,9 +117,9 @@ struct eigrp_nexthop_entry *eigrp_nexthop_entry_new(void) /* * Freeing topology table list */ -void eigrp_topology_free(struct route_table *table) +void eigrp_topology_free(struct eigrp *eigrp, struct route_table *table) { - eigrp_topology_delete_all(table); + eigrp_topology_delete_all(eigrp, table); route_table_finish(table); } @@ -138,7 +138,7 @@ void eigrp_prefix_entry_add(struct route_table *topology, zlog_debug( "%s: %s Should we have found this entry in the topo table?", - __PRETTY_FUNCTION__, + __func__, prefix2str(pe->destination, buf, sizeof(buf))); } route_unlock_node(rn); @@ -150,7 +150,8 @@ void eigrp_prefix_entry_add(struct route_table *topology, /* * Adding topology entry to topology node */ -void eigrp_nexthop_entry_add(struct eigrp_prefix_entry *node, +void eigrp_nexthop_entry_add(struct eigrp *eigrp, + struct eigrp_prefix_entry *node, struct eigrp_nexthop_entry *entry) { struct list *l = list_new(); @@ -161,7 +162,8 @@ void eigrp_nexthop_entry_add(struct eigrp_prefix_entry *node, listnode_add_sort(node->entries, entry); entry->prefix = node; - eigrp_zebra_route_add(node->destination, l, node->fdistance); + eigrp_zebra_route_add(eigrp, node->destination, + l, node->fdistance); } list_delete(&l); @@ -170,10 +172,9 @@ void eigrp_nexthop_entry_add(struct eigrp_prefix_entry *node, /* * Deleting topology node from topology table */ -void eigrp_prefix_entry_delete(struct route_table *table, +void eigrp_prefix_entry_delete(struct eigrp *eigrp, struct route_table *table, struct eigrp_prefix_entry *pe) { - struct eigrp *eigrp = eigrp_lookup(); struct eigrp_nexthop_entry *ne; struct listnode *node, *nnode; struct route_node *rn; @@ -192,11 +193,11 @@ void eigrp_prefix_entry_delete(struct route_table *table, listnode_delete(eigrp->topology_changes_internalIPV4, pe); for (ALL_LIST_ELEMENTS(pe->entries, node, nnode, ne)) - eigrp_nexthop_entry_delete(pe, ne); + eigrp_nexthop_entry_delete(eigrp, pe, ne); list_delete(&pe->entries); list_delete(&pe->rij); - eigrp_zebra_route_delete(pe->destination); - prefix_free(pe->destination); + eigrp_zebra_route_delete(eigrp, pe->destination); + prefix_free(&pe->destination); rn->info = NULL; route_unlock_node(rn); // Lookup above @@ -207,12 +208,13 @@ void eigrp_prefix_entry_delete(struct route_table *table, /* * Deleting topology entry from topology node */ -void eigrp_nexthop_entry_delete(struct eigrp_prefix_entry *node, +void eigrp_nexthop_entry_delete(struct eigrp *eigrp, + struct eigrp_prefix_entry *node, struct eigrp_nexthop_entry *entry) { if (listnode_lookup(node->entries, entry) != NULL) { listnode_delete(node->entries, entry); - eigrp_zebra_route_delete(node->destination); + eigrp_zebra_route_delete(eigrp, node->destination); XFREE(MTYPE_EIGRP_NEXTHOP_ENTRY, entry); } } @@ -220,7 +222,8 @@ void eigrp_nexthop_entry_delete(struct eigrp_prefix_entry *node, /* * Deleting all nodes from topology table */ -void eigrp_topology_delete_all(struct route_table *topology) +void eigrp_topology_delete_all(struct eigrp *eigrp, + struct route_table *topology) { struct route_node *rn; struct eigrp_prefix_entry *pe; @@ -231,7 +234,7 @@ void eigrp_topology_delete_all(struct route_table *topology) if (!pe) continue; - eigrp_prefix_entry_delete(topology, pe); + eigrp_prefix_entry_delete(eigrp, topology, pe); } } @@ -399,7 +402,7 @@ eigrp_topology_update_distance(struct eigrp_fsm_action_message *msg) break; default: flog_err(EC_LIB_DEVELOPMENT, "%s: Please implement handler", - __PRETTY_FUNCTION__); + __func__); break; } distance_done: @@ -426,17 +429,15 @@ void eigrp_topology_update_all_node_flags(struct eigrp *eigrp) if (!pe) continue; - eigrp_topology_update_node_flags(pe); + eigrp_topology_update_node_flags(eigrp, pe); } } -void eigrp_topology_update_node_flags(struct eigrp_prefix_entry *dest) +void eigrp_topology_update_node_flags(struct eigrp *eigrp, + struct eigrp_prefix_entry *dest) { struct listnode *node; struct eigrp_nexthop_entry *entry; - struct eigrp *eigrp = eigrp_lookup(); - - assert(eigrp); for (ALL_LIST_ELEMENTS_RO(dest->entries, node, entry)) { if (entry->reported_distance < dest->fdistance) { @@ -464,27 +465,24 @@ void eigrp_topology_update_node_flags(struct eigrp_prefix_entry *dest) } } -void eigrp_update_routing_table(struct eigrp_prefix_entry *prefix) +void eigrp_update_routing_table(struct eigrp *eigrp, + struct eigrp_prefix_entry *prefix) { - struct eigrp *eigrp = eigrp_lookup(); struct list *successors; struct listnode *node; struct eigrp_nexthop_entry *entry; - if (!eigrp) - return; - successors = eigrp_topology_get_successor_max(prefix, eigrp->max_paths); if (successors) { - eigrp_zebra_route_add(prefix->destination, successors, + eigrp_zebra_route_add(eigrp, prefix->destination, successors, prefix->fdistance); for (ALL_LIST_ELEMENTS_RO(successors, node, entry)) entry->flags |= EIGRP_NEXTHOP_ENTRY_INTABLE_FLAG; list_delete(&successors); } else { - eigrp_zebra_route_delete(prefix->destination); + eigrp_zebra_route_delete(eigrp, prefix->destination); for (ALL_LIST_ELEMENTS_RO(prefix->entries, node, entry)) entry->flags &= ~EIGRP_NEXTHOP_ENTRY_INTABLE_FLAG; } @@ -510,6 +508,7 @@ void eigrp_topology_neighbor_down(struct eigrp *eigrp, if (entry->adv_router != nbr) continue; + memset(&msg, 0, sizeof(msg)); msg.metrics.delay = EIGRP_MAX_METRIC; msg.packet_type = EIGRP_OPC_UPDATE; msg.eigrp = eigrp; @@ -525,7 +524,8 @@ void eigrp_topology_neighbor_down(struct eigrp *eigrp, eigrp_update_send_all(eigrp, nbr->ei); } -void eigrp_update_topology_table_prefix(struct route_table *table, +void eigrp_update_topology_table_prefix(struct eigrp *eigrp, + struct route_table *table, struct eigrp_prefix_entry *prefix) { struct listnode *node1, *node2; @@ -533,11 +533,11 @@ void eigrp_update_topology_table_prefix(struct route_table *table, struct eigrp_nexthop_entry *entry; for (ALL_LIST_ELEMENTS(prefix->entries, node1, node2, entry)) { if (entry->distance == EIGRP_MAX_METRIC) { - eigrp_nexthop_entry_delete(prefix, entry); + eigrp_nexthop_entry_delete(eigrp, prefix, entry); } } if (prefix->distance == EIGRP_MAX_METRIC && prefix->nt != EIGRP_TOPOLOGY_TYPE_CONNECTED) { - eigrp_prefix_entry_delete(table, prefix); + eigrp_prefix_entry_delete(eigrp, table, prefix); } } diff --git a/eigrpd/eigrp_topology.h b/eigrpd/eigrp_topology.h index 16bf2261cc..718cece403 100644 --- a/eigrpd/eigrp_topology.h +++ b/eigrpd/eigrp_topology.h @@ -37,34 +37,41 @@ extern struct route_table *eigrp_topology_new(void); extern void eigrp_topology_init(struct route_table *table); extern struct eigrp_prefix_entry *eigrp_prefix_entry_new(void); extern struct eigrp_nexthop_entry *eigrp_nexthop_entry_new(void); -extern void eigrp_topology_free(struct route_table *table); +extern void eigrp_topology_free(struct eigrp *eigrp, struct route_table *table); extern void eigrp_prefix_entry_add(struct route_table *table, struct eigrp_prefix_entry *pe); -extern void eigrp_nexthop_entry_add(struct eigrp_prefix_entry *, - struct eigrp_nexthop_entry *); -extern void eigrp_prefix_entry_delete(struct route_table *table, +extern void eigrp_nexthop_entry_add(struct eigrp *eigrp, + struct eigrp_prefix_entry *pe, + struct eigrp_nexthop_entry *ne); +extern void eigrp_prefix_entry_delete(struct eigrp *eigrp, + struct route_table *table, struct eigrp_prefix_entry *pe); -extern void eigrp_nexthop_entry_delete(struct eigrp_prefix_entry *, - struct eigrp_nexthop_entry *); -extern void eigrp_topology_delete_all(struct route_table *table); +extern void eigrp_nexthop_entry_delete(struct eigrp *eigrp, + struct eigrp_prefix_entry *pe, + struct eigrp_nexthop_entry *ne); +extern void eigrp_topology_delete_all(struct eigrp *eigrp, + struct route_table *table); extern struct eigrp_prefix_entry * eigrp_topology_table_lookup_ipv4(struct route_table *table, struct prefix *p); -extern struct list *eigrp_topology_get_successor(struct eigrp_prefix_entry *); +extern struct list *eigrp_topology_get_successor(struct eigrp_prefix_entry *pe); extern struct list * eigrp_topology_get_successor_max(struct eigrp_prefix_entry *pe, unsigned int maxpaths); extern struct eigrp_nexthop_entry * -eigrp_prefix_entry_lookup(struct list *, struct eigrp_neighbor *); -extern struct list *eigrp_neighbor_prefixes_lookup(struct eigrp *, - struct eigrp_neighbor *); -extern void eigrp_topology_update_all_node_flags(struct eigrp *); -extern void eigrp_topology_update_node_flags(struct eigrp_prefix_entry *); +eigrp_prefix_entry_lookup(struct list *entries, struct eigrp_neighbor *neigh); +extern struct list *eigrp_neighbor_prefixes_lookup(struct eigrp *eigrp, + struct eigrp_neighbor *n); +extern void eigrp_topology_update_all_node_flags(struct eigrp *eigrp); +extern void eigrp_topology_update_node_flags(struct eigrp *eigrp, + struct eigrp_prefix_entry *pe); extern enum metric_change -eigrp_topology_update_distance(struct eigrp_fsm_action_message *); -extern void eigrp_update_routing_table(struct eigrp_prefix_entry *); -extern void eigrp_topology_neighbor_down(struct eigrp *, - struct eigrp_neighbor *); -extern void eigrp_update_topology_table_prefix(struct route_table *table, +eigrp_topology_update_distance(struct eigrp_fsm_action_message *msg); +extern void eigrp_update_routing_table(struct eigrp *eigrp, + struct eigrp_prefix_entry *pe); +extern void eigrp_topology_neighbor_down(struct eigrp *eigrp, + struct eigrp_neighbor *neigh); +extern void eigrp_update_topology_table_prefix(struct eigrp *eigrp, + struct route_table *table, struct eigrp_prefix_entry *pe); #endif diff --git a/eigrpd/eigrp_update.c b/eigrpd/eigrp_update.c index 8db4903077..6e2a81e32a 100644 --- a/eigrpd/eigrp_update.c +++ b/eigrpd/eigrp_update.c @@ -211,7 +211,7 @@ void eigrp_update_receive(struct eigrp *eigrp, struct ip *iph, zlog_debug( "Processing Update size[%u] int(%s) nbr(%s) seq [%u] flags [%0x]", size, - ifindex2ifname(nbr->ei->ifp->ifindex, VRF_DEFAULT), + ifindex2ifname(nbr->ei->ifp->ifindex, eigrp->vrf_id), inet_ntoa(nbr->src), nbr->recv_sequence_number, flags); @@ -221,7 +221,7 @@ void eigrp_update_receive(struct eigrp *eigrp, struct ip *iph, zlog_info("Neighbor %s (%s) is resync: peer graceful-restart", inet_ntoa(nbr->src), - ifindex2ifname(nbr->ei->ifp->ifindex, VRF_DEFAULT)); + ifindex2ifname(nbr->ei->ifp->ifindex, eigrp->vrf_id)); /* get all prefixes from neighbor from topology table */ nbr_prefixes = eigrp_neighbor_prefixes_lookup(eigrp, nbr); @@ -233,7 +233,7 @@ void eigrp_update_receive(struct eigrp *eigrp, struct ip *iph, zlog_info("Neighbor %s (%s) is resync: peer graceful-restart", inet_ntoa(nbr->src), - ifindex2ifname(nbr->ei->ifp->ifindex, VRF_DEFAULT)); + ifindex2ifname(nbr->ei->ifp->ifindex, eigrp->vrf_id)); /* get all prefixes from neighbor from topology table */ nbr_prefixes = eigrp_neighbor_prefixes_lookup(eigrp, nbr); @@ -282,12 +282,12 @@ void eigrp_update_receive(struct eigrp *eigrp, struct ip *iph, zlog_info("Neighbor %s (%s) is down: peer restarted", inet_ntoa(nbr->src), ifindex2ifname(nbr->ei->ifp->ifindex, - VRF_DEFAULT)); + eigrp->vrf_id)); eigrp_nbr_state_set(nbr, EIGRP_NEIGHBOR_PENDING); zlog_info("Neighbor %s (%s) is pending: new adjacency", inet_ntoa(nbr->src), ifindex2ifname(nbr->ei->ifp->ifindex, - VRF_DEFAULT)); + eigrp->vrf_id)); eigrp_update_send_init(nbr); } } @@ -366,11 +366,11 @@ void eigrp_update_receive(struct eigrp *eigrp, struct ip *iph, eigrp_prefix_entry_add(eigrp->topology_table, pe); - eigrp_nexthop_entry_add(pe, ne); + eigrp_nexthop_entry_add(eigrp, pe, ne); pe->distance = pe->fdistance = pe->rdistance = ne->distance; pe->reported_metric = ne->total_metric; - eigrp_topology_update_node_flags(pe); + eigrp_topology_update_node_flags(eigrp, pe); pe->req_action |= EIGRP_FSM_NEED_UPDATE; listnode_add( @@ -965,19 +965,20 @@ void eigrp_update_send_GR(struct eigrp_neighbor *nbr, enum GR_type gr_type, zlog_info( "Neighbor %s (%s) is resync: route configuration changed", inet_ntoa(nbr->src), - ifindex2ifname(ei->ifp->ifindex, VRF_DEFAULT)); + ifindex2ifname(ei->ifp->ifindex, eigrp->vrf_id)); } else if (gr_type == EIGRP_GR_MANUAL) { /* Graceful restart was called manually */ zlog_info("Neighbor %s (%s) is resync: manually cleared", inet_ntoa(nbr->src), - ifindex2ifname(ei->ifp->ifindex, VRF_DEFAULT)); + ifindex2ifname(ei->ifp->ifindex, eigrp->vrf_id)); if (vty != NULL) { vty_time_print(vty, 0); vty_out(vty, "Neighbor %s (%s) is resync: manually cleared\n", inet_ntoa(nbr->src), - ifindex2ifname(ei->ifp->ifindex, VRF_DEFAULT)); + ifindex2ifname(ei->ifp->ifindex, + eigrp->vrf_id)); } } diff --git a/eigrpd/eigrp_vrf.c b/eigrpd/eigrp_vrf.c new file mode 100644 index 0000000000..c8c8491056 --- /dev/null +++ b/eigrpd/eigrp_vrf.c @@ -0,0 +1,50 @@ +/* + * eigrp - vrf code + * Copyright (C) 2019 Cumulus Networks, Inc. + * Donald Sharp + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include + +#include "vrf.h" + +#include "eigrpd/eigrp_vrf.h" + +static int eigrp_vrf_new(struct vrf *vrf) +{ + return 0; +} + +static int eigrp_vrf_enable(struct vrf *vrf) +{ + return 0; +} + +static int eigrp_vrf_disable(struct vrf *vrf) +{ + return 0; +} + +static int eigrp_vrf_delete(struct vrf *vrf) +{ + return 0; +} + +void eigrp_vrf_init(void) +{ + vrf_init(eigrp_vrf_new, eigrp_vrf_enable, + eigrp_vrf_disable, eigrp_vrf_delete, NULL); +} diff --git a/isisd/isis_vty_common.h b/eigrpd/eigrp_vrf.h similarity index 54% rename from isisd/isis_vty_common.h rename to eigrpd/eigrp_vrf.h index 297da0e2c1..423a4be551 100644 --- a/isisd/isis_vty_common.h +++ b/eigrpd/eigrp_vrf.h @@ -1,17 +1,14 @@ /* - * IS-IS Rout(e)ing protocol - isis_vty_common.h - * - * Copyright (C) 2001,2002 Sampo Saaristo - * Tampere University of Technology - * Institute of Communications Engineering - * Copyright (C) 2016 David Lamparter, for NetDEF, Inc. + * eigrp - vrf code + * Copyright (C) 2019 Cumulus Networks, Inc. + * Donald Sharp * * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public Licenseas published by the Free + * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * - * This program is distributed in the hope that it will be useful,but WITHOUT + * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. @@ -20,13 +17,7 @@ * with this program; see the file COPYING; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#ifndef __EIGRP_VRF_H__ -#ifndef ISIS_VTY_COMMON_H -#define ISIS_VTY_COMMON_H - -struct isis_circuit *isis_circuit_lookup(struct vty *vty); - -void isis_vty_daemon_init(void); -void isis_vty_init(void); - +extern void eigrp_vrf_init(void); #endif diff --git a/eigrpd/eigrp_vty.c b/eigrpd/eigrp_vty.c index fc5bdbdbc5..4426cf67e9 100644 --- a/eigrpd/eigrp_vty.c +++ b/eigrpd/eigrp_vty.c @@ -59,1185 +59,284 @@ #include "eigrpd/eigrp_vty_clippy.c" #endif -static int config_write_network(struct vty *vty, struct eigrp *eigrp) -{ - struct route_node *rn; - int i; - - /* `network area' print. */ - for (rn = route_top(eigrp->networks); rn; rn = route_next(rn)) - if (rn->info) { - /* Network print. */ - vty_out(vty, " network %s/%d \n", - inet_ntoa(rn->p.u.prefix4), rn->p.prefixlen); - } - - if (eigrp->max_paths != EIGRP_MAX_PATHS_DEFAULT) - vty_out(vty, " maximum-paths %d\n", eigrp->max_paths); - - if (eigrp->variance != EIGRP_VARIANCE_DEFAULT) - vty_out(vty, " variance %d\n", eigrp->variance); - - for (i = 0; i < ZEBRA_ROUTE_MAX; i++) - if (i != zclient->redist_default - && vrf_bitmap_check(zclient->redist[AFI_IP][i], - VRF_DEFAULT)) - vty_out(vty, " redistribute %s\n", - zebra_route_string(i)); - - /*Separate EIGRP configuration from the rest of the config*/ - vty_out(vty, "!\n"); - - return 0; -} - -static int config_write_interfaces(struct vty *vty, struct eigrp *eigrp) -{ - struct eigrp_interface *ei; - struct listnode *node; - - for (ALL_LIST_ELEMENTS_RO(eigrp->eiflist, node, ei)) { - vty_frame(vty, "interface %s\n", ei->ifp->name); - - if (ei->params.auth_type == EIGRP_AUTH_TYPE_MD5) { - vty_out(vty, " ip authentication mode eigrp %d md5\n", - eigrp->AS); - } - - if (ei->params.auth_type == EIGRP_AUTH_TYPE_SHA256) { - vty_out(vty, - " ip authentication mode eigrp %d hmac-sha-256\n", - eigrp->AS); - } - - if (ei->params.auth_keychain) { - vty_out(vty, - " ip authentication key-chain eigrp %d %s\n", - eigrp->AS, ei->params.auth_keychain); - } - - if (ei->params.v_hello != EIGRP_HELLO_INTERVAL_DEFAULT) { - vty_out(vty, " ip hello-interval eigrp %d\n", - ei->params.v_hello); - } - - if (ei->params.v_wait != EIGRP_HOLD_INTERVAL_DEFAULT) { - vty_out(vty, " ip hold-time eigrp %d\n", - ei->params.v_wait); - } - - /*Separate this EIGRP interface configuration from the others*/ - vty_endframe(vty, "!\n"); - } - - return 0; -} - -static int eigrp_write_interface(struct vty *vty) -{ - struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT); - struct interface *ifp; - struct eigrp_interface *ei; - - FOR_ALL_INTERFACES (vrf, ifp) { - ei = ifp->info; - if (!ei) - continue; - - vty_frame(vty, "interface %s\n", ifp->name); - - if (ifp->desc) - vty_out(vty, " description %s\n", ifp->desc); - - if (ei->params.bandwidth != EIGRP_BANDWIDTH_DEFAULT) - vty_out(vty, " bandwidth %u\n", ei->params.bandwidth); - if (ei->params.delay != EIGRP_DELAY_DEFAULT) - vty_out(vty, " delay %u\n", ei->params.delay); - if (ei->params.v_hello != EIGRP_HELLO_INTERVAL_DEFAULT) - vty_out(vty, " ip hello-interval eigrp %u\n", - ei->params.v_hello); - if (ei->params.v_wait != EIGRP_HOLD_INTERVAL_DEFAULT) - vty_out(vty, " ip hold-time eigrp %u\n", - ei->params.v_wait); - - vty_endframe(vty, "!\n"); - } - - return 0; -} - -/** - * Writes distribute lists to config - */ -static int config_write_eigrp_distribute(struct vty *vty, struct eigrp *eigrp) -{ - int write = 0; - - /* Distribute configuration. */ - write += config_write_distribute(vty, eigrp->distribute_ctx); - - return write; -} - -/** - * Writes 'router eigrp' section to config - */ -static int config_write_eigrp_router(struct vty *vty, struct eigrp *eigrp) -{ - int write = 0; - - /* `router eigrp' print. */ - vty_out(vty, "router eigrp %d\n", eigrp->AS); - - write++; - - /* Router ID print. */ - if (eigrp->router_id_static.s_addr != 0) { - vty_out(vty, " eigrp router-id %s\n", - inet_ntoa(eigrp->router_id_static)); - } - - /* Network area print. */ - config_write_network(vty, eigrp); - - /* Distribute-list and default-information print. */ - config_write_eigrp_distribute(vty, eigrp); - - /*Separate EIGRP configuration from the rest of the config*/ - vty_out(vty, "!\n"); - - return write; -} - -DEFUN_NOSH (router_eigrp, - router_eigrp_cmd, - "router eigrp (1-65535)", - "Enable a routing process\n" - "Start EIGRP configuration\n" - "AS Number to use\n") -{ - struct eigrp *eigrp = eigrp_get(argv[2]->arg); - VTY_PUSH_CONTEXT(EIGRP_NODE, eigrp); - - return CMD_SUCCESS; -} - -DEFUN (no_router_eigrp, - no_router_eigrp_cmd, - "no router eigrp (1-65535)", - NO_STR - "Routing process\n" - "EIGRP configuration\n" - "AS number to use\n") -{ - vty->node = CONFIG_NODE; - - struct eigrp *eigrp; - - eigrp = eigrp_lookup(); - if (eigrp == NULL) { - vty_out(vty, " EIGRP Routing Process not enabled\n"); - return CMD_SUCCESS; - } - - if (eigrp->AS != atoi(argv[3]->arg)) { - vty_out(vty, "%% Attempting to deconfigure non-existent AS\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - eigrp_finish_final(eigrp); - - return CMD_SUCCESS; -} - -DEFPY (eigrp_router_id, - eigrp_router_id_cmd, - "eigrp router-id A.B.C.D$addr", - "EIGRP specific commands\n" - "Router ID for this EIGRP process\n" - "EIGRP Router-ID in IP address format\n") -{ - VTY_DECLVAR_CONTEXT(eigrp, eigrp); - - eigrp->router_id_static = addr; - - return CMD_SUCCESS; -} - -DEFPY (no_eigrp_router_id, - no_eigrp_router_id_cmd, - "no eigrp router-id [A.B.C.D$addr]", - NO_STR - "EIGRP specific commands\n" - "Router ID for this EIGRP process\n" - "EIGRP Router-ID in IP address format\n") -{ - VTY_DECLVAR_CONTEXT(eigrp, eigrp); - - eigrp->router_id_static.s_addr = 0; - - return CMD_SUCCESS; -} - -DEFUN (eigrp_passive_interface, - eigrp_passive_interface_cmd, - "passive-interface IFNAME", - "Suppress routing updates on an interface\n" - "Interface to suppress on\n") -{ - VTY_DECLVAR_CONTEXT(eigrp, eigrp); - struct eigrp_interface *ei; - struct listnode *node; - char *ifname = argv[1]->arg; - - for (ALL_LIST_ELEMENTS_RO(eigrp->eiflist, node, ei)) { - if (strcmp(ifname, ei->ifp->name) == 0) { - ei->params.passive_interface = EIGRP_IF_PASSIVE; - return CMD_SUCCESS; - } - } - return CMD_SUCCESS; -} - -DEFUN (no_eigrp_passive_interface, - no_eigrp_passive_interface_cmd, - "no passive-interface IFNAME", - NO_STR - "Suppress routing updates on an interface\n" - "Interface to suppress on\n") -{ - VTY_DECLVAR_CONTEXT(eigrp, eigrp); - struct eigrp_interface *ei; - struct listnode *node; - char *ifname = argv[2]->arg; - - for (ALL_LIST_ELEMENTS_RO(eigrp->eiflist, node, ei)) { - if (strcmp(ifname, ei->ifp->name) == 0) { - ei->params.passive_interface = EIGRP_IF_ACTIVE; - return CMD_SUCCESS; - } - } - - return CMD_SUCCESS; -} - -DEFUN (eigrp_timers_active, - eigrp_timers_active_cmd, - "timers active-time <(1-65535)|disabled>", - "Adjust routing timers\n" - "Time limit for active state\n" - "Active state time limit in minutes\n" - "Disable time limit for active state\n") -{ - // struct eigrp *eigrp = vty->index; - /*TODO: */ - - return CMD_SUCCESS; -} - -DEFUN (no_eigrp_timers_active, - no_eigrp_timers_active_cmd, - "no timers active-time <(1-65535)|disabled>", - NO_STR - "Adjust routing timers\n" - "Time limit for active state\n" - "Active state time limit in minutes\n" - "Disable time limit for active state\n") -{ - // struct eigrp *eigrp = vty->index; - /*TODO: */ - - return CMD_SUCCESS; -} - - -DEFUN (eigrp_metric_weights, - eigrp_metric_weights_cmd, - "metric weights (0-255) (0-255) (0-255) (0-255) (0-255) ", - "Modify metrics and parameters for advertisement\n" - "Modify metric coefficients\n" - "K1\n" - "K2\n" - "K3\n" - "K4\n" - "K5\n") -{ - // struct eigrp *eigrp = vty->index; - /*TODO: */ - - return CMD_SUCCESS; -} - -DEFUN (no_eigrp_metric_weights, - no_eigrp_metric_weights_cmd, - "no metric weights <0-255> <0-255> <0-255> <0-255> <0-255>", - NO_STR - "Modify metrics and parameters for advertisement\n" - "Modify metric coefficients\n" - "K1\n" - "K2\n" - "K3\n" - "K4\n" - "K5\n") -{ - // struct eigrp *eigrp = vty->index; - /*TODO: */ - - return CMD_SUCCESS; -} - - -DEFUN (eigrp_network, - eigrp_network_cmd, - "network A.B.C.D/M", - "Enable routing on an IP network\n" - "EIGRP network prefix\n") -{ - VTY_DECLVAR_CONTEXT(eigrp, eigrp); - struct prefix p; - int ret; - - (void)str2prefix(argv[1]->arg, &p); - - ret = eigrp_network_set(eigrp, &p); - - if (ret == 0) { - vty_out(vty, "There is already same network statement.\n"); - return CMD_WARNING; - } - - return CMD_SUCCESS; -} - -DEFUN (no_eigrp_network, - no_eigrp_network_cmd, - "no network A.B.C.D/M", - NO_STR - "Disable routing on an IP network\n" - "EIGRP network prefix\n") -{ - VTY_DECLVAR_CONTEXT(eigrp, eigrp); - struct prefix p; - int ret; - - (void)str2prefix(argv[2]->arg, &p); - - ret = eigrp_network_unset(eigrp, &p); - - if (ret == 0) { - vty_out(vty, "Can't find specified network configuration.\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - return CMD_SUCCESS; -} - -DEFUN (eigrp_neighbor, - eigrp_neighbor_cmd, - "neighbor A.B.C.D", - "Specify a neighbor router\n" - "Neighbor address\n") -{ - // struct eigrp *eigrp = vty->index; - - return CMD_SUCCESS; -} - -DEFUN (no_eigrp_neighbor, - no_eigrp_neighbor_cmd, - "no neighbor A.B.C.D", - NO_STR - "Specify a neighbor router\n" - "Neighbor address\n") -{ - // struct eigrp *eigrp = vty->index; - - return CMD_SUCCESS; -} - static void eigrp_vty_display_prefix_entry(struct vty *vty, struct eigrp *eigrp, struct eigrp_prefix_entry *pe, bool all) { - bool first = true; - struct eigrp_nexthop_entry *te; - struct listnode *node; - - for (ALL_LIST_ELEMENTS_RO(pe->entries, node, te)) { - if (all - || (((te->flags - & EIGRP_NEXTHOP_ENTRY_SUCCESSOR_FLAG) - == EIGRP_NEXTHOP_ENTRY_SUCCESSOR_FLAG) - || ((te->flags - & EIGRP_NEXTHOP_ENTRY_FSUCCESSOR_FLAG) - == EIGRP_NEXTHOP_ENTRY_FSUCCESSOR_FLAG))) { - show_ip_eigrp_nexthop_entry(vty, eigrp, te, - &first); - first = false; - } - } -} - -DEFPY (show_ip_eigrp_topology_all, - show_ip_eigrp_topology_all_cmd, - "show ip eigrp topology [all-links$all]", - SHOW_STR - IP_STR - "IP-EIGRP show commands\n" - "IP-EIGRP topology\n" - "Show all links in topology table\n") -{ - struct eigrp *eigrp; - struct eigrp_prefix_entry *tn; - struct route_node *rn; - - eigrp = eigrp_lookup(); - if (eigrp == NULL) { - vty_out(vty, " EIGRP Routing Process not enabled\n"); - return CMD_SUCCESS; - } - - show_ip_eigrp_topology_header(vty, eigrp); - - for (rn = route_top(eigrp->topology_table); rn; rn = route_next(rn)) { - if (!rn->info) - continue; - - tn = rn->info; - eigrp_vty_display_prefix_entry(vty, eigrp, tn, - all ? true : false); - } - - return CMD_SUCCESS; - -} - -DEFPY (show_ip_eigrp_topology, - show_ip_eigrp_topology_cmd, - "show ip eigrp topology ", - SHOW_STR - IP_STR - "IP-EIGRP show commands\n" - "IP-EIGRP topology\n" - "For a specific address\n" - "For a specific prefix\n") -{ - struct eigrp *eigrp; - struct eigrp_prefix_entry *tn; - struct route_node *rn; - struct prefix cmp; - - eigrp = eigrp_lookup(); - if (eigrp == NULL) { - vty_out(vty, " EIGRP Routing Process not enabled\n"); - return CMD_SUCCESS; - } - - show_ip_eigrp_topology_header(vty, eigrp); - - if (address_str) - prefix_str = address_str; - - if (str2prefix(prefix_str, &cmp) < 0) { - vty_out(vty, "%% Malformed address\n"); - return CMD_WARNING; - } - - rn = route_node_match(eigrp->topology_table, &cmp); - if (!rn) { - vty_out(vty, "%% Network not in table\n"); - return CMD_WARNING; - } - - if (!rn->info) { - vty_out(vty, "%% Network not in table\n"); - route_unlock_node(rn); - return CMD_WARNING; - } - - tn = rn->info; - eigrp_vty_display_prefix_entry(vty, eigrp, tn, argc == 5); - - route_unlock_node(rn); - return CMD_SUCCESS; -} - -DEFUN (show_ip_eigrp_interfaces, - show_ip_eigrp_interfaces_cmd, - "show ip eigrp interfaces [IFNAME] [detail]", - SHOW_STR - IP_STR - "IP-EIGRP show commands\n" - "IP-EIGRP interfaces\n" - "Interface name to look at\n" - "Detailed information\n") -{ - struct eigrp_interface *ei; - struct eigrp *eigrp; - struct listnode *node; - int idx = 0; - bool detail = false; - const char *ifname = NULL; - - eigrp = eigrp_lookup(); - if (eigrp == NULL) { - vty_out(vty, "EIGRP Routing Process not enabled\n"); - return CMD_SUCCESS; - } - - if (argv_find(argv, argc, "IFNAME", &idx)) - ifname = argv[idx]->arg; - - if (argv_find(argv, argc, "detail", &idx)) - detail = true; - - if (!ifname) - show_ip_eigrp_interface_header(vty, eigrp); - - for (ALL_LIST_ELEMENTS_RO(eigrp->eiflist, node, ei)) { - if (!ifname || strcmp(ei->ifp->name, ifname) == 0) { - show_ip_eigrp_interface_sub(vty, eigrp, ei); - if (detail) - show_ip_eigrp_interface_detail(vty, eigrp, ei); - } - } - - return CMD_SUCCESS; -} - -DEFUN (show_ip_eigrp_neighbors, - show_ip_eigrp_neighbors_cmd, - "show ip eigrp neighbors [IFNAME] [detail]", - SHOW_STR - IP_STR - "IP-EIGRP show commands\n" - "IP-EIGRP neighbors\n" - "Interface to show on\n" - "Detailed Information\n") -{ - struct eigrp *eigrp; - struct eigrp_interface *ei; - struct listnode *node, *node2, *nnode2; - struct eigrp_neighbor *nbr; - bool detail = false; - int idx = 0; - const char *ifname = NULL; - - eigrp = eigrp_lookup(); - if (eigrp == NULL) { - vty_out(vty, " EIGRP Routing Process not enabled\n"); - return CMD_SUCCESS; - } - - if (argv_find(argv, argc, "IFNAME", &idx)) - ifname = argv[idx]->arg; - - detail = (argv_find(argv, argc, "detail", &idx)); - - show_ip_eigrp_neighbor_header(vty, eigrp); - - for (ALL_LIST_ELEMENTS_RO(eigrp->eiflist, node, ei)) { - if (!ifname || strcmp(ei->ifp->name, ifname) == 0) { - for (ALL_LIST_ELEMENTS(ei->nbrs, node2, nnode2, nbr)) { - if (detail || (nbr->state == EIGRP_NEIGHBOR_UP)) - show_ip_eigrp_neighbor_sub(vty, nbr, - detail); - } - } - } - - return CMD_SUCCESS; -} - -DEFUN (eigrp_if_delay, - eigrp_if_delay_cmd, - "delay (1-16777215)", - "Specify interface throughput delay\n" - "Throughput delay (tens of microseconds)\n") -{ - VTY_DECLVAR_CONTEXT(interface, ifp); - struct eigrp_interface *ei = ifp->info; - struct eigrp *eigrp; - uint32_t delay; - - eigrp = eigrp_lookup(); - if (eigrp == NULL) { - vty_out(vty, " EIGRP Routing Process not enabled\n"); - - return CMD_SUCCESS; - } - - if (!ei) { - vty_out(vty, " EIGRP not configured on this interface\n"); - return CMD_SUCCESS; - } - delay = atoi(argv[1]->arg); - - ei->params.delay = delay; - eigrp_if_reset(ifp); - - return CMD_SUCCESS; -} - -DEFUN (no_eigrp_if_delay, - no_eigrp_if_delay_cmd, - "no delay (1-16777215)", - NO_STR - "Specify interface throughput delay\n" - "Throughput delay (tens of microseconds)\n") -{ - VTY_DECLVAR_CONTEXT(interface, ifp); - struct eigrp_interface *ei = ifp->info; - struct eigrp *eigrp; - - eigrp = eigrp_lookup(); - if (eigrp == NULL) { - vty_out(vty, " EIGRP Routing Process not enabled\n"); - - return CMD_SUCCESS; - } - if (!ei) { - vty_out(vty, " EIGRP not configured on this interface\n"); - return CMD_SUCCESS; - } - - ei->params.delay = EIGRP_DELAY_DEFAULT; - eigrp_if_reset(ifp); - - return CMD_SUCCESS; -} - -DEFPY (eigrp_if_bandwidth, - eigrp_if_bandwidth_cmd, - "eigrp bandwidth (1-10000000)$bw", - "EIGRP specific commands\n" - "Set bandwidth informational parameter\n" - "Bandwidth in kilobits\n") -{ - VTY_DECLVAR_CONTEXT(interface, ifp); - struct eigrp_interface *ei = ifp->info; - struct eigrp *eigrp; - - eigrp = eigrp_lookup(); - if (eigrp == NULL) { - vty_out(vty, " EIGRP Routing Process not enabled\n"); - return CMD_SUCCESS; - } - - if (!ei) { - vty_out(vty, " EIGRP not configured on this interface\n"); - return CMD_SUCCESS; - } - - ei->params.bandwidth = bw; - eigrp_if_reset(ifp); - - return CMD_SUCCESS; -} - -DEFUN (no_eigrp_if_bandwidth, - no_eigrp_if_bandwidth_cmd, - "no eigrp bandwidth [(1-10000000)]", - NO_STR - "EIGRP specific commands\n" - "Set bandwidth informational parameter\n" - "Bandwidth in kilobits\n") -{ - VTY_DECLVAR_CONTEXT(interface, ifp); - struct eigrp_interface *ei = ifp->info; - struct eigrp *eigrp; - - eigrp = eigrp_lookup(); - if (eigrp == NULL) { - vty_out(vty, " EIGRP Routing Process not enabled\n"); - return CMD_SUCCESS; - } + bool first = true; + struct eigrp_nexthop_entry *te; + struct listnode *node; - if (!ei) { - vty_out(vty, " EIGRP not configured on this interface\n"); - return CMD_SUCCESS; + for (ALL_LIST_ELEMENTS_RO(pe->entries, node, te)) { + if (all + || (((te->flags + & EIGRP_NEXTHOP_ENTRY_SUCCESSOR_FLAG) + == EIGRP_NEXTHOP_ENTRY_SUCCESSOR_FLAG) + || ((te->flags + & EIGRP_NEXTHOP_ENTRY_FSUCCESSOR_FLAG) + == EIGRP_NEXTHOP_ENTRY_FSUCCESSOR_FLAG))) { + show_ip_eigrp_nexthop_entry(vty, eigrp, te, + &first); + first = false; + } } - - ei->params.bandwidth = EIGRP_BANDWIDTH_DEFAULT; - eigrp_if_reset(ifp); - - return CMD_SUCCESS; } -DEFUN (eigrp_if_ip_hellointerval, - eigrp_if_ip_hellointerval_cmd, - "ip hello-interval eigrp (1-65535)", - "Interface Internet Protocol config commands\n" - "Configures EIGRP hello interval\n" - "Enhanced Interior Gateway Routing Protocol (EIGRP)\n" - "Seconds between hello transmissions\n") +static struct eigrp *eigrp_vty_get_eigrp(struct vty *vty, const char *vrf_name) { - VTY_DECLVAR_CONTEXT(interface, ifp); - struct eigrp_interface *ei = ifp->info; - uint32_t hello; - struct eigrp *eigrp; + struct vrf *vrf; - eigrp = eigrp_lookup(); - if (eigrp == NULL) { - vty_out(vty, " EIGRP Routing Process not enabled\n"); - return CMD_SUCCESS; - } + if (vrf_name) + vrf = vrf_lookup_by_name(vrf_name); + else + vrf = vrf_lookup_by_id(VRF_DEFAULT); - if (!ei) { - vty_out(vty, " EIGRP not configured on this interface\n"); - return CMD_SUCCESS; + if (!vrf) { + vty_out(vty, "VRF %s specified does not exist", + vrf_name ? vrf_name : VRF_DEFAULT_NAME); + return NULL; } - hello = atoi(argv[3]->arg); - - ei->params.v_hello = hello; - - return CMD_SUCCESS; + return eigrp_lookup(vrf->vrf_id); } -DEFUN (no_eigrp_if_ip_hellointerval, - no_eigrp_if_ip_hellointerval_cmd, - "no ip hello-interval eigrp [(1-65535)]", - NO_STR - "Interface Internet Protocol config commands\n" - "Configures EIGRP hello interval\n" - "Enhanced Interior Gateway Routing Protocol (EIGRP)\n" - "Seconds between hello transmissions\n") +static void eigrp_topology_helper(struct vty *vty, struct eigrp *eigrp, + const char *all) { - VTY_DECLVAR_CONTEXT(interface, ifp); - struct eigrp_interface *ei = ifp->info; - struct eigrp *eigrp; - - eigrp = eigrp_lookup(); - if (eigrp == NULL) { - vty_out(vty, " EIGRP Routing Process not enabled\n"); - return CMD_SUCCESS; - } - - if (!ei) { - vty_out(vty, " EIGRP not configured on this interface\n"); - return CMD_SUCCESS; - } + struct eigrp_prefix_entry *tn; + struct route_node *rn; - ei->params.v_hello = EIGRP_HELLO_INTERVAL_DEFAULT; + show_ip_eigrp_topology_header(vty, eigrp); - THREAD_TIMER_OFF(ei->t_hello); - thread_add_timer(master, eigrp_hello_timer, ei, 1, &ei->t_hello); + for (rn = route_top(eigrp->topology_table); rn; rn = route_next(rn)) { + if (!rn->info) + continue; - return CMD_SUCCESS; + tn = rn->info; + eigrp_vty_display_prefix_entry(vty, eigrp, tn, + all ? true : false); + } } -DEFUN (eigrp_if_ip_holdinterval, - eigrp_if_ip_holdinterval_cmd, - "ip hold-time eigrp (1-65535)", - "Interface Internet Protocol config commands\n" - "Configures EIGRP IPv4 hold time\n" - "Enhanced Interior Gateway Routing Protocol (EIGRP)\n" - "Seconds before neighbor is considered down\n") +DEFPY (show_ip_eigrp_topology_all, + show_ip_eigrp_topology_all_cmd, + "show ip eigrp [vrf NAME] topology [all-links$all]", + SHOW_STR + IP_STR + "IP-EIGRP show commands\n" + VRF_CMD_HELP_STR + "IP-EIGRP topology\n" + "Show all links in topology table\n") { - VTY_DECLVAR_CONTEXT(interface, ifp); - struct eigrp_interface *ei = ifp->info; - uint32_t hold; struct eigrp *eigrp; - eigrp = eigrp_lookup(); - if (eigrp == NULL) { - vty_out(vty, " EIGRP Routing Process not enabled\n"); - return CMD_SUCCESS; - } - - if (!ei) { - vty_out(vty, " EIGRP not configured on this interface\n"); - return CMD_SUCCESS; - } + if (vrf && strncmp(vrf, "all", sizeof("all")) == 0) { + struct vrf *v; - hold = atoi(argv[3]->arg); + RB_FOREACH (v, vrf_name_head, &vrfs_by_name) { + eigrp = eigrp_lookup(v->vrf_id); + if (!eigrp) + continue; - ei->params.v_wait = hold; - - return CMD_SUCCESS; -} + vty_out(vty, "VRF %s:\n", v->name); -DEFUN (eigrp_ip_summary_address, - eigrp_ip_summary_address_cmd, - "ip summary-address eigrp (1-65535) A.B.C.D/M", - "Interface Internet Protocol config commands\n" - "Perform address summarization\n" - "Enhanced Interior Gateway Routing Protocol (EIGRP)\n" - "AS number\n" - "Summary /, e.g. 192.168.0.0/16\n") -{ - // VTY_DECLVAR_CONTEXT(interface, ifp); - // uint32_t AS; - struct eigrp *eigrp; + eigrp_topology_helper(vty, eigrp, all); + } + } else { + eigrp = eigrp_vty_get_eigrp(vty, vrf); + if (eigrp == NULL) { + vty_out(vty, " EIGRP Routing Process not enabled\n"); + return CMD_SUCCESS; + } - eigrp = eigrp_lookup(); - if (eigrp == NULL) { - vty_out(vty, " EIGRP Routing Process not enabled\n"); - return CMD_SUCCESS; + eigrp_topology_helper(vty, eigrp, all); } - // AS = atoi (argv[3]->arg); - - /*TODO: */ - return CMD_SUCCESS; } -DEFUN (no_eigrp_ip_summary_address, - no_eigrp_ip_summary_address_cmd, - "no ip summary-address eigrp (1-65535) A.B.C.D/M", - NO_STR - "Interface Internet Protocol config commands\n" - "Perform address summarization\n" - "Enhanced Interior Gateway Routing Protocol (EIGRP)\n" - "AS number\n" - "Summary /, e.g. 192.168.0.0/16\n") +DEFPY (show_ip_eigrp_topology, + show_ip_eigrp_topology_cmd, + "show ip eigrp [vrf NAME] topology ", + SHOW_STR + IP_STR + "IP-EIGRP show commands\n" + VRF_CMD_HELP_STR + "IP-EIGRP topology\n" + "For a specific address\n" + "For a specific prefix\n") { - // VTY_DECLVAR_CONTEXT(interface, ifp); - // uint32_t AS; struct eigrp *eigrp; + struct eigrp_prefix_entry *tn; + struct route_node *rn; + struct prefix cmp; - eigrp = eigrp_lookup(); - if (eigrp == NULL) { - vty_out(vty, " EIGRP Routing Process not enabled\n"); + if (vrf && strncmp(vrf, "all", sizeof("all")) == 0) { + vty_out(vty, "Specifying vrf `all` for a particular address/prefix makes no sense\n"); return CMD_SUCCESS; } - // AS = atoi (argv[4]->arg); - - /*TODO: */ - - return CMD_SUCCESS; -} - -DEFUN (no_eigrp_if_ip_holdinterval, - no_eigrp_if_ip_holdinterval_cmd, - "no ip hold-time eigrp", - NO_STR - "Interface Internet Protocol config commands\n" - "Configures EIGRP hello interval\n" - "Enhanced Interior Gateway Routing Protocol (EIGRP)\n") -{ - VTY_DECLVAR_CONTEXT(interface, ifp); - struct eigrp_interface *ei = ifp->info; - struct eigrp *eigrp; - - eigrp = eigrp_lookup(); + eigrp = eigrp_vty_get_eigrp(vty, vrf); if (eigrp == NULL) { vty_out(vty, " EIGRP Routing Process not enabled\n"); return CMD_SUCCESS; } - if (!ei) { - vty_out(vty, " EIGRP not configured on this interface\n"); - return CMD_SUCCESS; - } - - ei->params.v_wait = EIGRP_HOLD_INTERVAL_DEFAULT; - - return CMD_SUCCESS; -} + show_ip_eigrp_topology_header(vty, eigrp); -static int str2auth_type(const char *str, struct eigrp_interface *ei) -{ - /* Sanity check. */ - if (str == NULL) - return CMD_WARNING_CONFIG_FAILED; + if (address_str) + prefix_str = address_str; - if (strncmp(str, "md5", 3) == 0) { - ei->params.auth_type = EIGRP_AUTH_TYPE_MD5; - return CMD_SUCCESS; - } else if (strncmp(str, "hmac-sha-256", 12) == 0) { - ei->params.auth_type = EIGRP_AUTH_TYPE_SHA256; - return CMD_SUCCESS; + if (str2prefix(prefix_str, &cmp) < 0) { + vty_out(vty, "%% Malformed address\n"); + return CMD_WARNING; } - return CMD_WARNING_CONFIG_FAILED; -} - -DEFUN (eigrp_authentication_mode, - eigrp_authentication_mode_cmd, - "ip authentication mode eigrp (1-65535) ", - "Interface Internet Protocol config commands\n" - "Authentication subcommands\n" - "Mode\n" - "Enhanced Interior Gateway Routing Protocol (EIGRP)\n" - "Autonomous system number\n" - "Keyed message digest\n" - "HMAC SHA256 algorithm \n") -{ - VTY_DECLVAR_CONTEXT(interface, ifp); - struct eigrp_interface *ei = ifp->info; - struct eigrp *eigrp; - - eigrp = eigrp_lookup(); - if (eigrp == NULL) { - vty_out(vty, " EIGRP Routing Process not enabled\n"); - return CMD_SUCCESS; + rn = route_node_match(eigrp->topology_table, &cmp); + if (!rn) { + vty_out(vty, "%% Network not in table\n"); + return CMD_WARNING; } - if (!ei) { - vty_out(vty, " EIGRP not configured on this interface\n"); - return CMD_SUCCESS; + if (!rn->info) { + vty_out(vty, "%% Network not in table\n"); + route_unlock_node(rn); + return CMD_WARNING; } - // if(strncmp(argv[2], "md5",3)) - // IF_DEF_PARAMS (ifp)->auth_type = EIGRP_AUTH_TYPE_MD5; - // else if(strncmp(argv[2], "hmac-sha-256",12)) - // IF_DEF_PARAMS (ifp)->auth_type = EIGRP_AUTH_TYPE_SHA256; + tn = rn->info; + eigrp_vty_display_prefix_entry(vty, eigrp, tn, argc == 5); - return str2auth_type(argv[5]->arg, ei); + route_unlock_node(rn); + return CMD_SUCCESS; } -DEFUN (no_eigrp_authentication_mode, - no_eigrp_authentication_mode_cmd, - "no ip authentication mode eigrp (1-65535) ", - "Disable\n" - "Interface Internet Protocol config commands\n" - "Authentication subcommands\n" - "Mode\n" - "Enhanced Interior Gateway Routing Protocol (EIGRP)\n" - "Autonomous system number\n" - "Keyed message digest\n" - "HMAC SHA256 algorithm \n") +static void eigrp_interface_helper(struct vty *vty, struct eigrp *eigrp, + const char *ifname, const char *detail) { - VTY_DECLVAR_CONTEXT(interface, ifp); - struct eigrp_interface *ei = ifp->info; - struct eigrp *eigrp; + struct eigrp_interface *ei; + struct listnode *node; - eigrp = eigrp_lookup(); - if (eigrp == NULL) { - vty_out(vty, " EIGRP Routing Process not enabled\n"); - return CMD_SUCCESS; - } + if (!ifname) + show_ip_eigrp_interface_header(vty, eigrp); - if (!ei) { - vty_out(vty, " EIGRP not configured on this interface\n"); - return CMD_SUCCESS; + for (ALL_LIST_ELEMENTS_RO(eigrp->eiflist, node, ei)) { + if (!ifname || strcmp(ei->ifp->name, ifname) == 0) { + show_ip_eigrp_interface_sub(vty, eigrp, ei); + if (detail) + show_ip_eigrp_interface_detail(vty, eigrp, ei); + } } - - ei->params.auth_type = EIGRP_AUTH_TYPE_NONE; - - return CMD_SUCCESS; } -DEFPY (eigrp_authentication_keychain, - eigrp_authentication_keychain_cmd, - "[no] ip authentication key-chain eigrp (1-65535)$as WORD$name", - NO_STR - "Interface Internet Protocol config commands\n" - "Authentication subcommands\n" - "Key-chain\n" - "Enhanced Interior Gateway Routing Protocol (EIGRP)\n" - "Autonomous system number\n" - "Name of key-chain\n") +DEFPY (show_ip_eigrp_interfaces, + show_ip_eigrp_interfaces_cmd, + "show ip eigrp [vrf NAME] interfaces [IFNAME] [detail]$detail", + SHOW_STR + IP_STR + "IP-EIGRP show commands\n" + VRF_CMD_HELP_STR + "IP-EIGRP interfaces\n" + "Interface name to look at\n" + "Detailed information\n") { - VTY_DECLVAR_CONTEXT(interface, ifp); - struct eigrp_interface *ei = ifp->info; struct eigrp *eigrp; - struct keychain *keychain; - eigrp = eigrp_lookup(); - if (eigrp == NULL) { - vty_out(vty, "EIGRP Routing Process not enabled\n"); - return CMD_SUCCESS; - } + if (vrf && strncmp(vrf, "all", sizeof("all")) == 0) { + struct vrf *vrf; - if (!ei) { - vty_out(vty, " EIGRP not configured on this interface\n"); - return CMD_SUCCESS; - } + RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { + eigrp = eigrp_lookup(vrf->vrf_id); + if (!eigrp) + continue; - if (no) { - if ((ei->params.auth_keychain != NULL) - && (strcmp(ei->params.auth_keychain, name) == 0)) { - free(ei->params.auth_keychain); - ei->params.auth_keychain = NULL; - } else - vty_out(vty, - "Key chain with specified name not configured on interface\n"); - return CMD_SUCCESS; - } + vty_out(vty, "VRF %s:\n", vrf->name); - keychain = keychain_lookup(name); - if (keychain != NULL) { - if (ei->params.auth_keychain) { - free(ei->params.auth_keychain); - ei->params.auth_keychain = strdup(keychain->name); - } else - ei->params.auth_keychain = strdup(keychain->name); + eigrp_interface_helper(vty, eigrp, ifname, detail); + } } else { - vty_out(vty, - "Key chain with specified name not found\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - return CMD_SUCCESS; -} - -DEFUN (eigrp_redistribute_source_metric, - eigrp_redistribute_source_metric_cmd, - "redistribute " FRR_REDIST_STR_EIGRPD - " [metric (1-4294967295) (0-4294967295) (0-255) (1-255) (1-65535)]", - REDIST_STR - FRR_REDIST_HELP_STR_EIGRPD - "Metric for redistributed routes\n" - "Bandwidth metric in Kbits per second\n" - "EIGRP delay metric, in 10 microsecond units\n" - "EIGRP reliability metric where 255 is 100% reliable2 ?\n" - "EIGRP Effective bandwidth metric (Loading) where 255 is 100% loaded\n" - "EIGRP MTU of the path\n") -{ - VTY_DECLVAR_CONTEXT(eigrp, eigrp); - struct eigrp_metrics metrics_from_command = {0}; - int source; - int idx = 0; - - /* Get distribute source. */ - argv_find(argv, argc, "redistribute", &idx); - source = proto_redistnum(AFI_IP, argv[idx + 1]->text); - if (source < 0) { - vty_out(vty, "%% Invalid route type\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - /* Get metrics values */ - - return eigrp_redistribute_set(eigrp, source, metrics_from_command); -} - -DEFUN (no_eigrp_redistribute_source_metric, - no_eigrp_redistribute_source_metric_cmd, - "no redistribute " FRR_REDIST_STR_EIGRPD - " [metric (1-4294967295) (0-4294967295) (0-255) (1-255) (1-65535)]", - "Disable\n" - REDIST_STR - FRR_REDIST_HELP_STR_EIGRPD - "Metric for redistributed routes\n" - "Bandwidth metric in Kbits per second\n" - "EIGRP delay metric, in 10 microsecond units\n" - "EIGRP reliability metric where 255 is 100% reliable2 ?\n" - "EIGRP Effective bandwidth metric (Loading) where 255 is 100% loaded\n" - "EIGRP MTU of the path\n") -{ - VTY_DECLVAR_CONTEXT(eigrp, eigrp); - int source; - int idx = 0; - - /* Get distribute source. */ - argv_find(argv, argc, "redistribute", &idx); - source = proto_redistnum(AFI_IP, argv[idx + 1]->text); - if (source < 0) { - vty_out(vty, "%% Invalid route type\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - /* Get metrics values */ - - return eigrp_redistribute_unset(eigrp, source); -} - -DEFUN (eigrp_variance, - eigrp_variance_cmd, - "variance (1-128)", - "Control load balancing variance\n" - "Metric variance multiplier\n") -{ - struct eigrp *eigrp; - uint8_t variance; + eigrp = eigrp_vty_get_eigrp(vty, vrf); + if (eigrp == NULL) { + vty_out(vty, "EIGRP Routing Process not enabled\n"); + return CMD_SUCCESS; + } - eigrp = eigrp_lookup(); - if (eigrp == NULL) { - vty_out(vty, "EIGRP Routing Process not enabled\n"); - return CMD_SUCCESS; + eigrp_interface_helper(vty, eigrp, ifname, detail); } - variance = atoi(argv[1]->arg); - - eigrp->variance = variance; - /*TODO: */ return CMD_SUCCESS; } -DEFUN (no_eigrp_variance, - no_eigrp_variance_cmd, - "no variance (1-128)", - "Disable\n" - "Control load balancing variance\n" - "Metric variance multiplier\n") +static void eigrp_neighbors_helper(struct vty *vty, struct eigrp *eigrp, + const char *ifname, const char *detail) { - struct eigrp *eigrp; - eigrp = eigrp_lookup(); - if (eigrp == NULL) { - vty_out(vty, "EIGRP Routing Process not enabled\n"); - return CMD_SUCCESS; - } - - eigrp->variance = EIGRP_VARIANCE_DEFAULT; + struct eigrp_interface *ei; + struct listnode *node, *node2, *nnode2; + struct eigrp_neighbor *nbr; - /*TODO: */ + show_ip_eigrp_neighbor_header(vty, eigrp); - return CMD_SUCCESS; + for (ALL_LIST_ELEMENTS_RO(eigrp->eiflist, node, ei)) { + if (!ifname || strcmp(ei->ifp->name, ifname) == 0) { + for (ALL_LIST_ELEMENTS(ei->nbrs, node2, nnode2, nbr)) { + if (detail || (nbr->state == EIGRP_NEIGHBOR_UP)) + show_ip_eigrp_neighbor_sub(vty, nbr, + !!detail); + } + } + } } -DEFUN (eigrp_maximum_paths, - eigrp_maximum_paths_cmd, - "maximum-paths (1-32)", - "Forward packets over multiple paths\n" - "Number of paths\n") +DEFPY (show_ip_eigrp_neighbors, + show_ip_eigrp_neighbors_cmd, + "show ip eigrp [vrf NAME] neighbors [IFNAME] [detail]$detail", + SHOW_STR + IP_STR + "IP-EIGRP show commands\n" + VRF_CMD_HELP_STR + "IP-EIGRP neighbors\n" + "Interface to show on\n" + "Detailed Information\n") { struct eigrp *eigrp; - uint8_t max; - - eigrp = eigrp_lookup(); - if (eigrp == NULL) { - vty_out(vty, "EIGRP Routing Process not enabled\n"); - return CMD_SUCCESS; - } - - max = atoi(argv[1]->arg); - eigrp->max_paths = max; + if (vrf && strncmp(vrf, "all", sizeof("all")) == 0) { + struct vrf *vrf; - /*TODO: */ + RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { + eigrp = eigrp_lookup(vrf->vrf_id); + if (!eigrp) + continue; - return CMD_SUCCESS; -} + vty_out(vty, "VRF %s:\n", vrf->name); -DEFUN (no_eigrp_maximum_paths, - no_eigrp_maximum_paths_cmd, - "no maximum-paths <1-32>", - NO_STR - "Forward packets over multiple paths\n" - "Number of paths\n") -{ - struct eigrp *eigrp; + eigrp_neighbors_helper(vty, eigrp, ifname, detail); + } + } else { + eigrp = eigrp_vty_get_eigrp(vty, vrf); + if (eigrp == NULL) { + vty_out(vty, " EIGRP Routing Process not enabled\n"); + return CMD_SUCCESS; + } - eigrp = eigrp_lookup(); - if (eigrp == NULL) { - vty_out(vty, "EIGRP Routing Process not enabled\n"); - return CMD_SUCCESS; + eigrp_neighbors_helper(vty, eigrp, ifname, detail); } - eigrp->max_paths = EIGRP_MAX_PATHS_DEFAULT; - - /*TODO: */ - return CMD_SUCCESS; } /* * Execute hard restart for all neighbors */ -DEFUN (clear_ip_eigrp_neighbors, +DEFPY (clear_ip_eigrp_neighbors, clear_ip_eigrp_neighbors_cmd, - "clear ip eigrp neighbors", + "clear ip eigrp [vrf NAME] neighbors", CLEAR_STR IP_STR "Clear IP-EIGRP\n" + VRF_CMD_HELP_STR "Clear IP-EIGRP neighbors\n") { struct eigrp *eigrp; @@ -1246,7 +345,7 @@ DEFUN (clear_ip_eigrp_neighbors, struct eigrp_neighbor *nbr; /* Check if eigrp process is enabled */ - eigrp = eigrp_lookup(); + eigrp = eigrp_vty_get_eigrp(vty, vrf); if (eigrp == NULL) { vty_out(vty, " EIGRP Routing Process not enabled\n"); return CMD_SUCCESS; @@ -1264,13 +363,13 @@ DEFUN (clear_ip_eigrp_neighbors, "Neighbor %s (%s) is down: manually cleared", inet_ntoa(nbr->src), ifindex2ifname(nbr->ei->ifp->ifindex, - VRF_DEFAULT)); + eigrp->vrf_id)); vty_time_print(vty, 0); vty_out(vty, "Neighbor %s (%s) is down: manually cleared\n", inet_ntoa(nbr->src), ifindex2ifname(nbr->ei->ifp->ifindex, - VRF_DEFAULT)); + eigrp->vrf_id)); /* set neighbor to DOWN */ nbr->state = EIGRP_NEIGHBOR_DOWN; @@ -1286,12 +385,13 @@ DEFUN (clear_ip_eigrp_neighbors, /* * Execute hard restart for all neighbors on interface */ -DEFUN (clear_ip_eigrp_neighbors_int, +DEFPY (clear_ip_eigrp_neighbors_int, clear_ip_eigrp_neighbors_int_cmd, - "clear ip eigrp neighbors IFNAME", + "clear ip eigrp [vrf NAME] neighbors IFNAME", CLEAR_STR IP_STR "Clear IP-EIGRP\n" + VRF_CMD_HELP_STR "Clear IP-EIGRP neighbors\n" "Interface's name\n") { @@ -1299,20 +399,18 @@ DEFUN (clear_ip_eigrp_neighbors_int, struct eigrp_interface *ei; struct listnode *node2, *nnode2; struct eigrp_neighbor *nbr; - int idx = 0; /* Check if eigrp process is enabled */ - eigrp = eigrp_lookup(); + eigrp = eigrp_vty_get_eigrp(vty, vrf); if (eigrp == NULL) { vty_out(vty, " EIGRP Routing Process not enabled\n"); return CMD_SUCCESS; } /* lookup interface by specified name */ - argv_find(argv, argc, "IFNAME", &idx); - ei = eigrp_if_lookup_by_name(eigrp, argv[idx]->arg); + ei = eigrp_if_lookup_by_name(eigrp, ifname); if (ei == NULL) { - vty_out(vty, " Interface (%s) doesn't exist\n", argv[idx]->arg); + vty_out(vty, " Interface (%s) doesn't exist\n", ifname); return CMD_WARNING; } @@ -1325,13 +423,13 @@ DEFUN (clear_ip_eigrp_neighbors_int, zlog_debug("Neighbor %s (%s) is down: manually cleared", inet_ntoa(nbr->src), ifindex2ifname(nbr->ei->ifp->ifindex, - VRF_DEFAULT)); + eigrp->vrf_id)); vty_time_print(vty, 0); vty_out(vty, "Neighbor %s (%s) is down: manually cleared\n", inet_ntoa(nbr->src), ifindex2ifname(nbr->ei->ifp->ifindex, - VRF_DEFAULT)); + eigrp->vrf_id)); /* set neighbor to DOWN */ nbr->state = EIGRP_NEIGHBOR_DOWN; @@ -1346,26 +444,21 @@ DEFUN (clear_ip_eigrp_neighbors_int, /* * Execute hard restart for neighbor specified by IP */ -DEFUN (clear_ip_eigrp_neighbors_IP, +DEFPY (clear_ip_eigrp_neighbors_IP, clear_ip_eigrp_neighbors_IP_cmd, - "clear ip eigrp neighbors A.B.C.D", + "clear ip eigrp [vrf NAME] neighbors A.B.C.D$nbr_addr", CLEAR_STR IP_STR "Clear IP-EIGRP\n" + VRF_CMD_HELP_STR "Clear IP-EIGRP neighbors\n" "IP-EIGRP neighbor address\n") { struct eigrp *eigrp; struct eigrp_neighbor *nbr; - struct in_addr nbr_addr; - - if (!inet_aton(argv[4]->arg, &nbr_addr)) { - vty_out(vty, "Unable to parse %s", argv[4]->arg); - return CMD_WARNING; - } /* Check if eigrp process is enabled */ - eigrp = eigrp_lookup(); + eigrp = eigrp_vty_get_eigrp(vty, vrf); if (eigrp == NULL) { vty_out(vty, " EIGRP Routing Process not enabled\n"); return CMD_SUCCESS; @@ -1389,19 +482,20 @@ DEFUN (clear_ip_eigrp_neighbors_IP, /* * Execute graceful restart for all neighbors */ -DEFUN (clear_ip_eigrp_neighbors_soft, +DEFPY (clear_ip_eigrp_neighbors_soft, clear_ip_eigrp_neighbors_soft_cmd, - "clear ip eigrp neighbors soft", + "clear ip eigrp [vrf NAME] neighbors soft", CLEAR_STR IP_STR "Clear IP-EIGRP\n" + VRF_CMD_HELP_STR "Clear IP-EIGRP neighbors\n" "Resync with peers without adjacency reset\n") { struct eigrp *eigrp; /* Check if eigrp process is enabled */ - eigrp = eigrp_lookup(); + eigrp = eigrp_vty_get_eigrp(vty, vrf); if (eigrp == NULL) { vty_out(vty, " EIGRP Routing Process not enabled\n"); return CMD_SUCCESS; @@ -1416,12 +510,13 @@ DEFUN (clear_ip_eigrp_neighbors_soft, /* * Execute graceful restart for all neighbors on interface */ -DEFUN (clear_ip_eigrp_neighbors_int_soft, +DEFPY (clear_ip_eigrp_neighbors_int_soft, clear_ip_eigrp_neighbors_int_soft_cmd, - "clear ip eigrp neighbors IFNAME soft", + "clear ip eigrp [vrf NAME] neighbors IFNAME soft", CLEAR_STR IP_STR "Clear IP-EIGRP\n" + VRF_CMD_HELP_STR "Clear IP-EIGRP neighbors\n" "Interface's name\n" "Resync with peer without adjacency reset\n") @@ -1430,14 +525,14 @@ DEFUN (clear_ip_eigrp_neighbors_int_soft, struct eigrp_interface *ei; /* Check if eigrp process is enabled */ - eigrp = eigrp_lookup(); + eigrp = eigrp_vty_get_eigrp(vty, vrf); if (eigrp == NULL) { vty_out(vty, " EIGRP Routing Process not enabled\n"); return CMD_SUCCESS; } /* lookup interface by specified name */ - ei = eigrp_if_lookup_by_name(eigrp, argv[4]->arg); + ei = eigrp_if_lookup_by_name(eigrp, ifname); if (ei == NULL) { vty_out(vty, " Interface (%s) doesn't exist\n", argv[4]->arg); return CMD_WARNING; @@ -1451,27 +546,23 @@ DEFUN (clear_ip_eigrp_neighbors_int_soft, /* * Execute graceful restart for neighbor specified by IP */ -DEFUN (clear_ip_eigrp_neighbors_IP_soft, +DEFPY (clear_ip_eigrp_neighbors_IP_soft, clear_ip_eigrp_neighbors_IP_soft_cmd, - "clear ip eigrp neighbors A.B.C.D soft", + "clear ip eigrp [vrf NAME] neighbors A.B.C.D$nbr_addr soft", CLEAR_STR IP_STR "Clear IP-EIGRP\n" + VRF_CMD_HELP_STR "Clear IP-EIGRP neighbors\n" "IP-EIGRP neighbor address\n" "Resync with peer without adjacency reset\n") { struct eigrp *eigrp; struct eigrp_neighbor *nbr; - struct in_addr nbr_addr; - if (!inet_aton(argv[4]->arg, &nbr_addr)) { - vty_out(vty, "Unable to parse: %s", argv[4]->arg); - return CMD_WARNING; - } /* Check if eigrp process is enabled */ - eigrp = eigrp_lookup(); + eigrp = eigrp_vty_get_eigrp(vty, vrf); if (eigrp == NULL) { vty_out(vty, " EIGRP Routing Process not enabled\n"); return CMD_SUCCESS; @@ -1492,40 +583,6 @@ DEFUN (clear_ip_eigrp_neighbors_IP_soft, return CMD_SUCCESS; } -static struct cmd_node eigrp_node = {EIGRP_NODE, "%s(config-router)# ", 1}; - -/* Save EIGRP configuration */ -static int eigrp_config_write(struct vty *vty) -{ - struct eigrp *eigrp; - - eigrp = eigrp_lookup(); - if (eigrp != NULL) { - /* Writes 'router eigrp' section to config */ - config_write_eigrp_router(vty, eigrp); - - /* Interface config print */ - config_write_interfaces(vty, eigrp); - // - // /* static neighbor print. */ - // config_write_eigrp_nbr_nbma (vty, eigrp); - // - // /* Virtual-Link print. */ - // config_write_virtual_link (vty, eigrp); - // - // /* Default metric configuration. */ - // config_write_eigrp_default_metric (vty, eigrp); - // - // /* Distribute-list and default-information print. */ - // config_write_eigrp_distribute (vty, eigrp); - // - // /* Distance configuration. */ - // config_write_eigrp_distance (vty, eigrp) - } - - return 0; -} - void eigrp_vty_show_init(void) { install_element(VIEW_NODE, &show_ip_eigrp_interfaces_cmd); @@ -1536,69 +593,9 @@ void eigrp_vty_show_init(void) install_element(VIEW_NODE, &show_ip_eigrp_topology_all_cmd); } -/* eigrpd's interface node. */ -static struct cmd_node eigrp_interface_node = {INTERFACE_NODE, - "%s(config-if)# ", 1}; - -void eigrp_vty_if_init(void) -{ - install_node(&eigrp_interface_node, eigrp_write_interface); - if_cmd_init(); - - /* Delay and bandwidth configuration commands*/ - install_element(INTERFACE_NODE, &eigrp_if_delay_cmd); - install_element(INTERFACE_NODE, &no_eigrp_if_delay_cmd); - install_element(INTERFACE_NODE, &eigrp_if_bandwidth_cmd); - install_element(INTERFACE_NODE, &no_eigrp_if_bandwidth_cmd); - - /*Hello-interval and hold-time interval configuration commands*/ - install_element(INTERFACE_NODE, &eigrp_if_ip_holdinterval_cmd); - install_element(INTERFACE_NODE, &no_eigrp_if_ip_holdinterval_cmd); - install_element(INTERFACE_NODE, &eigrp_if_ip_hellointerval_cmd); - install_element(INTERFACE_NODE, &no_eigrp_if_ip_hellointerval_cmd); - - /* "Authentication configuration commands */ - install_element(INTERFACE_NODE, &eigrp_authentication_mode_cmd); - install_element(INTERFACE_NODE, &no_eigrp_authentication_mode_cmd); - install_element(INTERFACE_NODE, &eigrp_authentication_keychain_cmd); - - /*EIGRP Summarization commands*/ - install_element(INTERFACE_NODE, &eigrp_ip_summary_address_cmd); - install_element(INTERFACE_NODE, &no_eigrp_ip_summary_address_cmd); -} - -static void eigrp_vty_zebra_init(void) -{ - install_element(EIGRP_NODE, &eigrp_redistribute_source_metric_cmd); - install_element(EIGRP_NODE, &no_eigrp_redistribute_source_metric_cmd); -} - /* Install EIGRP related vty commands. */ void eigrp_vty_init(void) { - install_node(&eigrp_node, eigrp_config_write); - - install_default(EIGRP_NODE); - - install_element(CONFIG_NODE, &router_eigrp_cmd); - install_element(CONFIG_NODE, &no_router_eigrp_cmd); - install_element(EIGRP_NODE, &eigrp_network_cmd); - install_element(EIGRP_NODE, &no_eigrp_network_cmd); - install_element(EIGRP_NODE, &eigrp_variance_cmd); - install_element(EIGRP_NODE, &no_eigrp_variance_cmd); - install_element(EIGRP_NODE, &eigrp_router_id_cmd); - install_element(EIGRP_NODE, &no_eigrp_router_id_cmd); - install_element(EIGRP_NODE, &eigrp_passive_interface_cmd); - install_element(EIGRP_NODE, &no_eigrp_passive_interface_cmd); - install_element(EIGRP_NODE, &eigrp_timers_active_cmd); - install_element(EIGRP_NODE, &no_eigrp_timers_active_cmd); - install_element(EIGRP_NODE, &eigrp_metric_weights_cmd); - install_element(EIGRP_NODE, &no_eigrp_metric_weights_cmd); - install_element(EIGRP_NODE, &eigrp_maximum_paths_cmd); - install_element(EIGRP_NODE, &no_eigrp_maximum_paths_cmd); - install_element(EIGRP_NODE, &eigrp_neighbor_cmd); - install_element(EIGRP_NODE, &no_eigrp_neighbor_cmd); - /* commands for manual hard restart */ install_element(ENABLE_NODE, &clear_ip_eigrp_neighbors_cmd); install_element(ENABLE_NODE, &clear_ip_eigrp_neighbors_int_cmd); @@ -1607,6 +604,4 @@ void eigrp_vty_init(void) install_element(ENABLE_NODE, &clear_ip_eigrp_neighbors_soft_cmd); install_element(ENABLE_NODE, &clear_ip_eigrp_neighbors_int_soft_cmd); install_element(ENABLE_NODE, &clear_ip_eigrp_neighbors_IP_soft_cmd); - - eigrp_vty_zebra_init(); } diff --git a/eigrpd/eigrp_vty.h b/eigrpd/eigrp_vty.h index 3fbadf6dfb..ebbf503857 100644 --- a/eigrpd/eigrp_vty.h +++ b/eigrpd/eigrp_vty.h @@ -35,6 +35,5 @@ /* Prototypes. */ extern void eigrp_vty_init(void); extern void eigrp_vty_show_init(void); -extern void eigrp_vty_if_init(void); #endif /* _Quagga_EIGRP_VTY_H_ */ diff --git a/eigrpd/eigrp_zebra.c b/eigrpd/eigrp_zebra.c index dc1ae675b0..3205f13922 100644 --- a/eigrpd/eigrp_zebra.c +++ b/eigrpd/eigrp_zebra.c @@ -53,21 +53,10 @@ #include "eigrpd/eigrp_topology.h" #include "eigrpd/eigrp_fsm.h" -static int eigrp_interface_add(int, struct zclient *, zebra_size_t, vrf_id_t); -static int eigrp_interface_delete(int, struct zclient *, zebra_size_t, - vrf_id_t); -static int eigrp_interface_address_add(int, struct zclient *, zebra_size_t, - vrf_id_t vrf_id); -static int eigrp_interface_address_delete(int, struct zclient *, zebra_size_t, - vrf_id_t vrf_id); -static int eigrp_interface_state_up(int, struct zclient *, zebra_size_t, - vrf_id_t vrf_id); -static int eigrp_interface_state_down(int, struct zclient *, zebra_size_t, - vrf_id_t vrf_id); -static struct interface *zebra_interface_if_lookup(struct stream *); - -static int eigrp_zebra_read_route(int, struct zclient *, zebra_size_t, - vrf_id_t vrf_id); +static int eigrp_interface_address_add(ZAPI_CALLBACK_ARGS); +static int eigrp_interface_address_delete(ZAPI_CALLBACK_ARGS); + +static int eigrp_zebra_read_route(ZAPI_CALLBACK_ARGS); /* Zebra structure to hold current status. */ struct zclient *zclient = NULL; @@ -77,8 +66,7 @@ extern struct thread_master *master; struct in_addr router_id_zebra; /* Router-id update message from zebra. */ -static int eigrp_router_id_update_zebra(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int eigrp_router_id_update_zebra(ZAPI_CALLBACK_ARGS) { struct eigrp *eigrp; struct prefix router_id; @@ -86,7 +74,7 @@ static int eigrp_router_id_update_zebra(int command, struct zclient *zclient, router_id_zebra = router_id.u.prefix4; - eigrp = eigrp_lookup(); + eigrp = eigrp_lookup(vrf_id); if (eigrp != NULL) eigrp_router_id_update(eigrp); @@ -94,8 +82,7 @@ static int eigrp_router_id_update_zebra(int command, struct zclient *zclient, return 0; } -static int eigrp_zebra_route_notify_owner(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int eigrp_zebra_route_notify_owner(ZAPI_CALLBACK_ARGS) { struct prefix p; enum zapi_route_notify_owner note; @@ -121,10 +108,6 @@ void eigrp_zebra_init(void) zclient_init(zclient, ZEBRA_ROUTE_EIGRP, 0, &eigrpd_privs); zclient->zebra_connected = eigrp_zebra_connected; zclient->router_id_update = eigrp_router_id_update_zebra; - zclient->interface_add = eigrp_interface_add; - zclient->interface_delete = eigrp_interface_delete; - zclient->interface_up = eigrp_interface_state_up; - zclient->interface_down = eigrp_interface_state_down; zclient->interface_address_add = eigrp_interface_address_add; zclient->interface_address_delete = eigrp_interface_address_delete; zclient->redistribute_route_add = eigrp_zebra_read_route; @@ -134,8 +117,7 @@ void eigrp_zebra_init(void) /* Zebra route add and delete treatment. */ -static int eigrp_zebra_read_route(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int eigrp_zebra_read_route(ZAPI_CALLBACK_ARGS) { struct zapi_route api; struct eigrp *eigrp; @@ -146,77 +128,24 @@ static int eigrp_zebra_read_route(int command, struct zclient *zclient, if (IPV4_NET127(ntohl(api.prefix.u.prefix4.s_addr))) return 0; - eigrp = eigrp_lookup(); + eigrp = eigrp_lookup(vrf_id); if (eigrp == NULL) return 0; - if (command == ZEBRA_REDISTRIBUTE_ROUTE_ADD) { + if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD) { - } else /* if (command == ZEBRA_REDISTRIBUTE_ROUTE_DEL) */ + } else /* if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_DEL) */ { } return 0; } -/* Inteface addition message from zebra. */ -static int eigrp_interface_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) -{ - struct interface *ifp; - struct eigrp_interface *ei; - - ifp = zebra_interface_add_read(zclient->ibuf, vrf_id); - - if (!ifp->info) - return 0; - - ei = ifp->info; - - ei->params.type = eigrp_default_iftype(ifp); - - eigrp_if_update(ifp); - - return 0; -} - -static int eigrp_interface_delete(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) -{ - struct interface *ifp; - struct stream *s; - - s = zclient->ibuf; - /* zebra_interface_state_read () updates interface structure in iflist - */ - ifp = zebra_interface_state_read(s, vrf_id); - - if (ifp == NULL) - return 0; - - if (if_is_up(ifp)) - zlog_warn("Zebra: got delete of %s, but interface is still up", - ifp->name); - - if (IS_DEBUG_EIGRP(zebra, ZEBRA_INTERFACE)) - zlog_debug( - "Zebra: interface delete %s index %d flags %llx metric %d mtu %d", - ifp->name, ifp->ifindex, (unsigned long long)ifp->flags, - ifp->metric, ifp->mtu); - - if (ifp->info) - eigrp_if_free(ifp->info, INTERFACE_DOWN_BY_ZEBRA); - - if_set_index(ifp, IFINDEX_INTERNAL); - return 0; -} - -static int eigrp_interface_address_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int eigrp_interface_address_add(ZAPI_CALLBACK_ARGS) { struct connected *c; - c = zebra_interface_address_read(command, zclient->ibuf, vrf_id); + c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); if (c == NULL) return 0; @@ -233,14 +162,13 @@ static int eigrp_interface_address_add(int command, struct zclient *zclient, return 0; } -static int eigrp_interface_address_delete(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int eigrp_interface_address_delete(ZAPI_CALLBACK_ARGS) { struct connected *c; struct interface *ifp; struct eigrp_interface *ei; - c = zebra_interface_address_read(command, zclient->ibuf, vrf_id); + c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); if (c == NULL) return 0; @@ -261,101 +189,13 @@ static int eigrp_interface_address_delete(int command, struct zclient *zclient, if (prefix_cmp(&ei->address, c->address) == 0) eigrp_if_free(ei, INTERFACE_DOWN_BY_ZEBRA); - connected_free(c); - - return 0; -} - -static int eigrp_interface_state_up(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) -{ - struct interface *ifp; - - ifp = zebra_interface_if_lookup(zclient->ibuf); - - if (ifp == NULL) - return 0; - - /* Interface is already up. */ - if (if_is_operative(ifp)) { - /* Temporarily keep ifp values. */ - struct interface if_tmp; - memcpy(&if_tmp, ifp, sizeof(struct interface)); - - zebra_interface_if_set_value(zclient->ibuf, ifp); - - if (IS_DEBUG_EIGRP(zebra, ZEBRA_INTERFACE)) - zlog_debug("Zebra: Interface[%s] state update.", - ifp->name); - - if (if_tmp.bandwidth != ifp->bandwidth) { - if (IS_DEBUG_EIGRP(zebra, ZEBRA_INTERFACE)) - zlog_debug( - "Zebra: Interface[%s] bandwidth change %d -> %d.", - ifp->name, if_tmp.bandwidth, - ifp->bandwidth); - - // eigrp_if_recalculate_output_cost (ifp); - } - - if (if_tmp.mtu != ifp->mtu) { - if (IS_DEBUG_EIGRP(zebra, ZEBRA_INTERFACE)) - zlog_debug( - "Zebra: Interface[%s] MTU change %u -> %u.", - ifp->name, if_tmp.mtu, ifp->mtu); - - /* Must reset the interface (simulate down/up) when MTU - * changes. */ - eigrp_if_reset(ifp); - } - return 0; - } - - zebra_interface_if_set_value(zclient->ibuf, ifp); - - if (IS_DEBUG_EIGRP(zebra, ZEBRA_INTERFACE)) - zlog_debug("Zebra: Interface[%s] state change to up.", - ifp->name); - - if (ifp->info) - eigrp_if_up(ifp->info); + connected_free(&c); return 0; } -static int eigrp_interface_state_down(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) -{ - struct interface *ifp; - - ifp = zebra_interface_state_read(zclient->ibuf, vrf_id); - - if (ifp == NULL) - return 0; - - if (IS_DEBUG_EIGRP(zebra, ZEBRA_INTERFACE)) - zlog_debug("Zebra: Interface[%s] state change to down.", - ifp->name); - - if (ifp->info) - eigrp_if_down(ifp->info); - - return 0; -} - -static struct interface *zebra_interface_if_lookup(struct stream *s) -{ - char ifname_tmp[INTERFACE_NAMSIZ]; - - /* Read interface name. */ - stream_get(ifname_tmp, s, INTERFACE_NAMSIZ); - - /* And look it up. */ - return if_lookup_by_name(ifname_tmp, VRF_DEFAULT); -} - -void eigrp_zebra_route_add(struct prefix *p, struct list *successors, - uint32_t distance) +void eigrp_zebra_route_add(struct eigrp *eigrp, struct prefix *p, + struct list *successors, uint32_t distance) { struct zapi_route api; struct zapi_nexthop *api_nh; @@ -367,7 +207,7 @@ void eigrp_zebra_route_add(struct prefix *p, struct list *successors, return; memset(&api, 0, sizeof(api)); - api.vrf_id = VRF_DEFAULT; + api.vrf_id = eigrp->vrf_id; api.type = ZEBRA_ROUTE_EIGRP; api.safi = SAFI_UNICAST; api.metric = distance; @@ -381,7 +221,7 @@ void eigrp_zebra_route_add(struct prefix *p, struct list *successors, if (count >= MULTIPATH_NUM) break; api_nh = &api.nexthops[count]; - api_nh->vrf_id = VRF_DEFAULT; + api_nh->vrf_id = eigrp->vrf_id; if (te->adv_router->src.s_addr) { api_nh->gate.ipv4 = te->adv_router->src; api_nh->type = NEXTHOP_TYPE_IPV4_IFINDEX; @@ -403,7 +243,7 @@ void eigrp_zebra_route_add(struct prefix *p, struct list *successors, zclient_route_send(ZEBRA_ROUTE_ADD, zclient, &api); } -void eigrp_zebra_route_delete(struct prefix *p) +void eigrp_zebra_route_delete(struct eigrp *eigrp, struct prefix *p) { struct zapi_route api; @@ -411,7 +251,7 @@ void eigrp_zebra_route_delete(struct prefix *p) return; memset(&api, 0, sizeof(api)); - api.vrf_id = VRF_DEFAULT; + api.vrf_id = eigrp->vrf_id; api.type = ZEBRA_ROUTE_EIGRP; api.safi = SAFI_UNICAST; memcpy(&api.prefix, p, sizeof(*p)); @@ -426,20 +266,20 @@ void eigrp_zebra_route_delete(struct prefix *p) return; } -int eigrp_is_type_redistributed(int type) +static int eigrp_is_type_redistributed(int type, vrf_id_t vrf_id) { return ((DEFAULT_ROUTE_TYPE(type)) ? vrf_bitmap_check(zclient->default_information[AFI_IP], - VRF_DEFAULT) + vrf_id) : vrf_bitmap_check(zclient->redist[AFI_IP][type], - VRF_DEFAULT)); + vrf_id)); } int eigrp_redistribute_set(struct eigrp *eigrp, int type, struct eigrp_metrics metric) { - if (eigrp_is_type_redistributed(type)) { + if (eigrp_is_type_redistributed(type, eigrp->vrf_id)) { if (eigrp_metrics_is_same(metric, eigrp->dmetric[type])) { eigrp->dmetric[type] = metric; } @@ -458,7 +298,7 @@ int eigrp_redistribute_set(struct eigrp *eigrp, int type, eigrp->dmetric[type] = metric; zclient_redistribute(ZEBRA_REDISTRIBUTE_ADD, zclient, AFI_IP, type, 0, - VRF_DEFAULT); + eigrp->vrf_id); ++eigrp->redistribute; @@ -468,10 +308,10 @@ int eigrp_redistribute_set(struct eigrp *eigrp, int type, int eigrp_redistribute_unset(struct eigrp *eigrp, int type) { - if (eigrp_is_type_redistributed(type)) { + if (eigrp_is_type_redistributed(type, eigrp->vrf_id)) { memset(&eigrp->dmetric[type], 0, sizeof(struct eigrp_metrics)); zclient_redistribute(ZEBRA_REDISTRIBUTE_DELETE, zclient, AFI_IP, - type, 0, VRF_DEFAULT); + type, 0, eigrp->vrf_id); --eigrp->redistribute; } diff --git a/eigrpd/eigrp_zebra.h b/eigrpd/eigrp_zebra.h index 86b337cfe6..112f2584ce 100644 --- a/eigrpd/eigrp_zebra.h +++ b/eigrpd/eigrp_zebra.h @@ -33,11 +33,10 @@ extern void eigrp_zebra_init(void); -extern void eigrp_zebra_route_add(struct prefix *, struct list *, - uint32_t distance); -extern void eigrp_zebra_route_delete(struct prefix *); +extern void eigrp_zebra_route_add(struct eigrp *eigrp, struct prefix *p, + struct list *successors, uint32_t distance); +extern void eigrp_zebra_route_delete(struct eigrp *eigrp, struct prefix *); extern int eigrp_redistribute_set(struct eigrp *, int, struct eigrp_metrics); extern int eigrp_redistribute_unset(struct eigrp *, int); -extern int eigrp_is_type_redistributed(int); #endif /* _ZEBRA_EIGRP_ZEBRA_H_ */ diff --git a/eigrpd/eigrpd.c b/eigrpd/eigrpd.c index 93f8b6f90e..820f015b57 100644 --- a/eigrpd/eigrpd.c +++ b/eigrpd/eigrpd.c @@ -64,8 +64,6 @@ static struct eigrp_master eigrp_master; struct eigrp_master *eigrp_om; -static struct eigrp *eigrp_new(const char *); - extern struct zclient *zclient; extern struct in_addr router_id_zebra; @@ -95,16 +93,16 @@ extern struct in_addr router_id_zebra; */ void eigrp_router_id_update(struct eigrp *eigrp) { - struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT); + struct vrf *vrf = vrf_lookup_by_id(eigrp->vrf_id); struct interface *ifp; struct in_addr router_id, router_id_old; router_id_old = eigrp->router_id; - if (eigrp->router_id_static.s_addr != 0) + if (eigrp->router_id_static.s_addr != INADDR_ANY) router_id = eigrp->router_id_static; - else if (eigrp->router_id.s_addr != 0) + else if (eigrp->router_id.s_addr != INADDR_ANY) router_id = eigrp->router_id; else @@ -136,16 +134,16 @@ void eigrp_master_init(void) } /* Allocate new eigrp structure. */ -static struct eigrp *eigrp_new(const char *AS) +static struct eigrp *eigrp_new(uint16_t as, vrf_id_t vrf_id) { struct eigrp *eigrp = XCALLOC(MTYPE_EIGRP_TOP, sizeof(struct eigrp)); - int eigrp_socket; /* init information relevant to peers */ + eigrp->vrf_id = vrf_id; eigrp->vrid = 0; - eigrp->AS = atoi(AS); - eigrp->router_id.s_addr = 0; - eigrp->router_id_static.s_addr = 0; + eigrp->AS = as; + eigrp->router_id.s_addr = INADDR_ANY; + eigrp->router_id_static.s_addr = INADDR_ANY; eigrp->sequence_number = 1; /*Configure default K Values for EIGRP Process*/ @@ -161,14 +159,15 @@ static struct eigrp *eigrp_new(const char *AS) eigrp->passive_interface_default = EIGRP_IF_ACTIVE; eigrp->networks = eigrp_topology_new(); - if ((eigrp_socket = eigrp_sock_init()) < 0) { + eigrp->fd = eigrp_sock_init(vrf_lookup_by_id(vrf_id)); + + if (eigrp->fd < 0) { flog_err_sys( EC_LIB_SOCKET, "eigrp_new: fatal error: eigrp_sock_init was unable to open a socket"); exit(1); } - eigrp->fd = eigrp_socket; eigrp->maxsndbuflen = getsockopt_so_sendbuf(eigrp->fd); eigrp->ibuf = stream_new(EIGRP_PACKET_MAX_LEN + 1); @@ -200,16 +199,15 @@ static struct eigrp *eigrp_new(const char *AS) eigrp->routemap[EIGRP_FILTER_OUT] = NULL; /* Distribute list install. */ - eigrp->distribute_ctx = distribute_list_ctx_create( - vrf_lookup_by_id(VRF_DEFAULT)); + eigrp->distribute_ctx = + distribute_list_ctx_create(vrf_lookup_by_id(eigrp->vrf_id)); distribute_list_add_hook(eigrp->distribute_ctx, eigrp_distribute_update); distribute_list_delete_hook(eigrp->distribute_ctx, eigrp_distribute_update); /* - eigrp->if_rmap_ctx = if_rmap_ctx_create( - VRF_DEFAULT_NAME); + eigrp->if_rmap_ctx = if_rmap_ctx_create(eigrp->vrf_id); if_rmap_hook_add (eigrp_if_rmap_update); if_rmap_hook_delete (eigrp_if_rmap_update); */ @@ -217,13 +215,13 @@ static struct eigrp *eigrp_new(const char *AS) return eigrp; } -struct eigrp *eigrp_get(const char *AS) +struct eigrp *eigrp_get(uint16_t as, vrf_id_t vrf_id) { struct eigrp *eigrp; - eigrp = eigrp_lookup(); + eigrp = eigrp_lookup(vrf_id); if (eigrp == NULL) { - eigrp = eigrp_new(AS); + eigrp = eigrp_new(as, vrf_id); listnode_add(eigrp_om->eigrp, eigrp); } @@ -285,7 +283,7 @@ void eigrp_finish_final(struct eigrp *eigrp) list_delete(&eigrp->eiflist); list_delete(&eigrp->oi_write_q); - eigrp_topology_free(eigrp->topology_table); + eigrp_topology_free(eigrp, eigrp->topology_table); eigrp_nbr_delete(eigrp->neighbor_self); @@ -300,10 +298,14 @@ void eigrp_finish_final(struct eigrp *eigrp) } /*Look for existing eigrp process*/ -struct eigrp *eigrp_lookup(void) +struct eigrp *eigrp_lookup(vrf_id_t vrf_id) { - if (listcount(eigrp_om->eigrp) == 0) - return NULL; + struct eigrp *eigrp; + struct listnode *node, *nnode; + + for (ALL_LIST_ELEMENTS(eigrp_om->eigrp, node, nnode, eigrp)) + if (eigrp->vrf_id == vrf_id) + return eigrp; - return listgetdata(listhead(eigrp_om->eigrp)); + return NULL; } diff --git a/eigrpd/eigrpd.h b/eigrpd/eigrpd.h index de7c881ac0..6b4d45d1fc 100644 --- a/eigrpd/eigrpd.h +++ b/eigrpd/eigrpd.h @@ -48,8 +48,55 @@ extern void eigrp_master_init(void); extern void eigrp_terminate(void); extern void eigrp_finish_final(struct eigrp *); extern void eigrp_finish(struct eigrp *); -extern struct eigrp *eigrp_get(const char *); -extern struct eigrp *eigrp_lookup(void); +extern struct eigrp *eigrp_get(uint16_t as, vrf_id_t vrf_id); +extern struct eigrp *eigrp_lookup(vrf_id_t vrf_id); extern void eigrp_router_id_update(struct eigrp *); +/* eigrp_cli.c */ +extern void eigrp_cli_show_header(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +extern void eigrp_cli_show_end_header(struct vty *vty, struct lyd_node *dnode); +extern void eigrp_cli_show_router_id(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +extern void eigrp_cli_show_passive_interface(struct vty *vty, + struct lyd_node *dnode, + bool show_defaults); +extern void eigrp_cli_show_active_time(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +extern void eigrp_cli_show_variance(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +extern void eigrp_cli_show_maximum_paths(struct vty *vty, + struct lyd_node *dnode, + bool show_defaults); +extern void eigrp_cli_show_metrics(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +extern void eigrp_cli_show_network(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +extern void eigrp_cli_show_neighbor(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +extern void eigrp_cli_show_redistribute(struct vty *vty, + struct lyd_node *dnode, + bool show_defaults); +extern void eigrp_cli_show_delay(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +extern void eigrp_cli_show_bandwidth(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +extern void eigrp_cli_show_hello_interval(struct vty *vty, + struct lyd_node *dnode, + bool show_defaults); +extern void eigrp_cli_show_hold_time(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +extern void eigrp_cli_show_summarize_address(struct vty *vty, + struct lyd_node *dnode, + bool show_defaults); +extern void eigrp_cli_show_authentication(struct vty *vty, + struct lyd_node *dnode, + bool show_defaults); +extern void eigrp_cli_show_keychain(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +extern void eigrp_cli_init(void); + +/* eigrp_northbound.c */ +extern const struct frr_yang_module_info frr_eigrpd_info; + #endif /* _ZEBRA_EIGRPD_H */ diff --git a/eigrpd/subdir.am b/eigrpd/subdir.am index 4503030fdf..8b86b9c8a7 100644 --- a/eigrpd/subdir.am +++ b/eigrpd/subdir.am @@ -7,14 +7,16 @@ noinst_LIBRARIES += eigrpd/libeigrp.a sbin_PROGRAMS += eigrpd/eigrpd dist_examples_DATA += eigrpd/eigrpd.conf.sample vtysh_scan += \ - $(top_srcdir)/eigrpd/eigrp_dump.c \ - $(top_srcdir)/eigrpd/eigrp_vty.c \ + eigrpd/eigrp_cli.c \ + eigrpd/eigrp_dump.c \ + eigrpd/eigrp_vty.c \ # end -# $(top_srcdir)/eigrpd/eigrp_routemap.c -man8 += $(MANBUILD)/eigrpd.8 +# eigrpd/eigrp_routemap.c +man8 += $(MANBUILD)/frr-eigrpd.8 endif eigrpd_libeigrp_a_SOURCES = \ + eigrpd/eigrp_cli.c \ eigrpd/eigrp_dump.c \ eigrpd/eigrp_errors.c \ eigrpd/eigrp_filter.c \ @@ -24,6 +26,7 @@ eigrpd_libeigrp_a_SOURCES = \ eigrpd/eigrp_memory.c \ eigrpd/eigrp_neighbor.c \ eigrpd/eigrp_network.c \ + eigrpd/eigrp_northbound.c \ eigrpd/eigrp_packet.c \ eigrpd/eigrp_query.c \ eigrpd/eigrp_reply.c \ @@ -32,6 +35,7 @@ eigrpd_libeigrp_a_SOURCES = \ eigrpd/eigrp_snmp.c \ eigrpd/eigrp_topology.c \ eigrpd/eigrp_update.c \ + eigrpd/eigrp_vrf.c \ eigrpd/eigrp_vty.c \ eigrpd/eigrp_zebra.c \ eigrpd/eigrpd.c \ @@ -44,8 +48,10 @@ eigrpdheader_HEADERS = \ eigrpd/eigrpd.h \ # end -eigrpd/eigrp_vty_clippy.c: $(CLIPPY_DEPS) -eigrpd/eigrp_vty.$(OBJEXT): eigrpd/eigrp_vty_clippy.c +clippy_scan += \ + eigrpd/eigrp_cli.c \ + eigrpd/eigrp_vty.c \ + # end noinst_HEADERS += \ eigrpd/eigrp_const.h \ @@ -60,9 +66,14 @@ noinst_HEADERS += \ eigrpd/eigrp_packet.h \ eigrpd/eigrp_snmp.h \ eigrpd/eigrp_structs.h \ + eigrpd/eigrp_vrf.h \ eigrpd/eigrp_vty.h \ eigrpd/eigrp_zebra.h \ # end +nodist_eigrpd_eigrpd_SOURCES = \ + yang/frr-eigrpd.yang.c \ + # end + eigrpd_eigrpd_SOURCES = eigrpd/eigrp_main.c eigrpd_eigrpd_LDADD = eigrpd/libeigrp.a lib/libfrr.la $(LIBCAP) diff --git a/fpm/fpm.h b/fpm/fpm.h index ec1da6657c..0da8a5a8be 100644 --- a/fpm/fpm.h +++ b/fpm/fpm.h @@ -175,7 +175,7 @@ static inline size_t fpm_msg_align(size_t len) * The (rounded up) size of the FPM message header. This ensures that * the message payload always starts at an aligned address. */ -#define FPM_MSG_HDR_LEN (sizeof (fpm_msg_hdr_t)) +#define FPM_MSG_HDR_LEN (sizeof(fpm_msg_hdr_t)) #ifndef COMPILE_ASSERT #define COMPILE_ASSERT(x) extern int __dummy[2 * !!(x) - 1] @@ -245,7 +245,7 @@ static inline fpm_msg_hdr_t *fpm_msg_next(fpm_msg_hdr_t *hdr, size_t *len) /* * fpm_msg_hdr_ok * - * Returns TRUE if a message header looks well-formed. + * Returns true if a message header looks well-formed. */ static inline int fpm_msg_hdr_ok(const fpm_msg_hdr_t *hdr) { @@ -272,7 +272,7 @@ static inline int fpm_msg_hdr_ok(const fpm_msg_hdr_t *hdr) /* * fpm_msg_ok * - * Returns TRUE if a message looks well-formed. + * Returns true if a message looks well-formed. * * @param len The length in bytes from 'hdr' to the end of the buffer. */ diff --git a/fpm/subdir.am b/fpm/subdir.am index a0fa3d274f..a645ca2b03 100644 --- a/fpm/subdir.am +++ b/fpm/subdir.am @@ -12,9 +12,13 @@ fpm_libfrrfpm_pb_la_SOURCES = \ fpm/fpm_pb.c \ # end +if FPM +if HAVE_PROTOBUF nodist_fpm_libfrrfpm_pb_la_SOURCES = \ fpm/fpm.pb-c.c \ # end +endif +endif CLEANFILES += \ fpm/fpm.pb-c.c \ diff --git a/grpc/Makefile b/grpc/Makefile new file mode 100644 index 0000000000..8748286629 --- /dev/null +++ b/grpc/Makefile @@ -0,0 +1,10 @@ +all: ALWAYS + @$(MAKE) -s -C .. grpc/libfrrgrpc_pb.la +%: ALWAYS + @$(MAKE) -s -C .. grpc/$@ + +Makefile: + #nothing +ALWAYS: +.PHONY: ALWAYS makefiles +.SUFFIXES: diff --git a/grpc/frr-northbound.proto b/grpc/frr-northbound.proto new file mode 100644 index 0000000000..32544ba694 --- /dev/null +++ b/grpc/frr-northbound.proto @@ -0,0 +1,422 @@ +// +// Copyright 2019 FRRouting +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS +// BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +syntax = "proto3"; + +package frr; + +// Service specification for the FRR northbound interface. +service Northbound { + // Retrieve the capabilities supported by the target. + rpc GetCapabilities(GetCapabilitiesRequest) returns (GetCapabilitiesResponse) {} + + // Retrieve configuration data, state data or both from the target. + rpc Get(GetRequest) returns (stream GetResponse) {} + + // Create a new candidate configuration and return a reference to it. The + // created candidate is a copy of the running configuration. + rpc CreateCandidate(CreateCandidateRequest) returns (CreateCandidateResponse) {} + + // Delete a candidate configuration. + rpc DeleteCandidate(DeleteCandidateRequest) returns (DeleteCandidateResponse) {} + + // Update a candidate configuration by rebasing the changes on top of the + // latest running configuration. Resolve conflicts automatically by giving + // preference to the changes done in the candidate configuration. + rpc UpdateCandidate(UpdateCandidateRequest) returns (UpdateCandidateResponse) {} + + // Edit a candidate configuration. All changes are discarded if any error + // happens. + rpc EditCandidate(EditCandidateRequest) returns (EditCandidateResponse) {} + + // Load configuration data into a candidate configuration. Both merge and + // replace semantics are supported. + rpc LoadToCandidate(LoadToCandidateRequest) returns (LoadToCandidateResponse) {} + + // Create a new configuration transaction using a two-phase commit protocol. + rpc Commit(CommitRequest) returns (CommitResponse) {} + + // List the metadata of all configuration transactions recorded in the + // transactions database. + rpc ListTransactions(ListTransactionsRequest) returns (stream ListTransactionsResponse) {} + + // Fetch a configuration (identified by its transaction ID) from the + // transactions database. + rpc GetTransaction(GetTransactionRequest) returns (GetTransactionResponse) {} + + // Lock the running configuration, preventing other users from changing it. + rpc LockConfig(LockConfigRequest) returns (LockConfigResponse) {} + + // Unlock the running configuration. + rpc UnlockConfig(UnlockConfigRequest) returns (UnlockConfigResponse) {} + + // Execute a YANG RPC. + rpc Execute(ExecuteRequest) returns (ExecuteResponse) {} +} + +// ----------------------- Parameters and return types ------------------------- + +// +// RPC: GetCapabilities() +// +message GetCapabilitiesRequest { + // Empty. +} + +message GetCapabilitiesResponse { + // Return values: + // - grpc::StatusCode::OK: Success. + + // FRR version. + string frr_version = 1; + + // Indicates whether FRR was compiled with support for configuration + // rollbacks or not (--enable-config-rollbacks). + bool rollback_support = 2; + + // Supported schema modules. + repeated ModuleData supported_modules = 3; + + // Supported encodings. + repeated Encoding supported_encodings = 4; +} + +// +// RPC: Get() +// +message GetRequest { + // Type of elements within the data tree. + enum DataType { + // All data elements. + ALL = 0; + + // Config elements. + CONFIG = 1; + + // State elements. + STATE = 2; + } + + // The type of data being requested. + DataType type = 1; + + // Encoding to be used. + Encoding encoding = 2; + + // Include implicit default nodes. + bool with_defaults = 3; + + // Paths requested by the client. + repeated string path = 4; +} + +message GetResponse { + // Return values: + // - grpc::StatusCode::OK: Success. + // - grpc::StatusCode::INVALID_ARGUMENT: Invalid YANG data path. + + // Timestamp in nanoseconds since Epoch. + int64 timestamp = 1; + + // The requested data. + DataTree data = 2; +} + +// +// RPC: CreateCandidate() +// +message CreateCandidateRequest { + // Empty. +} + +message CreateCandidateResponse { + // Return values: + // - grpc::StatusCode::OK: Success. + // - grpc::StatusCode::RESOURCE_EXHAUSTED: can't create candidate + // configuration. + + // Handle to the new created candidate configuration. + uint32 candidate_id = 1; +} + +// +// RPC: DeleteCandidate() +// +message DeleteCandidateRequest { + // Candidate configuration to delete. + uint32 candidate_id = 1; +} + +message DeleteCandidateResponse { + // Return values: + // - grpc::StatusCode::OK: Success. + // - grpc::StatusCode::NOT_FOUND: Candidate wasn't found. +} + +// +// RPC: UpdateCandidate() +// +message UpdateCandidateRequest { + // Candidate configuration to update. + uint32 candidate_id = 1; +} + +message UpdateCandidateResponse { + // Return values: + // - grpc::StatusCode::OK: Success. + // - grpc::StatusCode::NOT_FOUND: Candidate wasn't found. +} + +// +// RPC: EditCandidate() +// +message EditCandidateRequest { + // Candidate configuration that is going to be edited. + uint32 candidate_id = 1; + + // Data elements to be created or updated. + repeated PathValue update = 2; + + // Paths to be deleted from the data tree. + repeated PathValue delete = 3; +} + +message EditCandidateResponse { + // Return values: + // - grpc::StatusCode::OK: Success. + // - grpc::StatusCode::NOT_FOUND: Candidate wasn't found. + // - grpc::StatusCode::INVALID_ARGUMENT: An error occurred while editing the + // candidate configuration. +} + +// +// RPC: LoadToCandidate() +// +message LoadToCandidateRequest { + enum LoadType { + // Merge the data tree into the candidate configuration. + MERGE = 0; + + // Replace the candidate configuration by the provided data tree. + REPLACE = 1; + } + + // Candidate configuration that is going to be edited. + uint32 candidate_id = 1; + + // Load operation to apply. + LoadType type = 2; + + // Configuration data. + DataTree config = 3; +} + +message LoadToCandidateResponse { + // Return values: + // - grpc::StatusCode::OK: Success. + // - grpc::StatusCode::INVALID_ARGUMENT: An error occurred while performing + // the load operation. +} + +// +// RPC: Commit() +// +message CommitRequest { + enum Phase { + // Validate if the configuration changes are valid (phase 0). + VALIDATE = 0; + + // Prepare resources to apply the configuration changes (phase 1). + PREPARE = 1; + + // Release previously allocated resources (phase 2). + ABORT = 2; + + // Apply the configuration changes (phase 2). + APPLY = 3; + + // All of the above (VALIDATE + PREPARE + ABORT/APPLY). + // + // This option can't be used to implement network-wide transactions, + // since they require the manager entity to take into account the results + // of the preparation phase of multiple managed devices. + ALL = 4; + } + + // Candidate configuration that is going to be committed. + uint32 candidate_id = 1; + + // Transaction phase. + Phase phase = 2; + + // Assign a comment to this commit. + string comment = 3; +} + +message CommitResponse { + // Return values: + // - grpc::StatusCode::OK: Success. + // - grpc::StatusCode::FAILED_PRECONDITION: misuse of the two-phase commit + // protocol. + // - grpc::StatusCode::INVALID_ARGUMENT: Validation error. + // - grpc::StatusCode::RESOURCE_EXHAUSTED: Failure to allocate resource. + + // ID of the created configuration transaction (when the phase is APPLY + // or ALL). + uint32 transaction_id = 1; + + // Human-readable error or warning message(s). + string error_message = 2; +} + +// +// RPC: ListTransactions() +// +message ListTransactionsRequest { + // Empty. +} + +message ListTransactionsResponse { + // Return values: + // - grpc::StatusCode::OK: Success. + + // Transaction ID. + uint32 id = 1; + + // Client that committed the transaction. + string client = 2; + + // Date and time the transaction was committed. + string date = 3; + + // Comment assigned to the transaction. + string comment = 4; +} + +// +// RPC: GetTransaction() +// +message GetTransactionRequest { + // Transaction to retrieve. + uint32 transaction_id = 1; + + // Encoding to be used. + Encoding encoding = 2; + + // Include implicit default nodes. + bool with_defaults = 3; +} + +message GetTransactionResponse { + // Return values: + // - grpc::StatusCode::OK: Success. + // - grpc::StatusCode::NOT_FOUND: Transaction wasn't found in the transactions + // database. + + DataTree config = 1; +} + +// +// RPC: LockConfig() +// +message LockConfigRequest { + // Empty. +} + +message LockConfigResponse { + // Return values: + // - grpc::StatusCode::OK: Success. + // - grpc::StatusCode::FAILED_PRECONDITION: Running configuration is + // locked already. +} + +// +// RPC: UnlockConfig() +// +message UnlockConfigRequest { + // Empty. +} + +message UnlockConfigResponse { + // Return values: + // - grpc::StatusCode::OK: Success. + // - grpc::StatusCode::FAILED_PRECONDITION: Running configuration isn't + // locked. +} + +// +// RPC: Execute() +// +message ExecuteRequest { + // Path of the YANG RPC or YANG Action. + string path = 1; + + // Input parameters. + repeated PathValue input = 2; +} + +message ExecuteResponse { + // Return values: + // - grpc::StatusCode::OK: Success. + + // Output parameters. + repeated PathValue output = 1; +} + +// -------------------------------- Definitions -------------------------------- + +// YANG module. +message ModuleData { + // Name of the YANG module; + string name = 1; + + // Organization publishing the module. + string organization = 2; + + // Latest revision of the module; + string revision = 3; +} + +// Supported encodings for YANG instance data. +enum Encoding { + JSON = 0; + XML = 1; +} + +// Path-value pair representing a data element. +message PathValue { + // YANG data path. + string path = 1; + + // Data value. + string value = 2; +} + +// YANG instance data. +message DataTree { + Encoding encoding = 1; + string data = 2; +} diff --git a/grpc/subdir.am b/grpc/subdir.am new file mode 100644 index 0000000000..045848aee7 --- /dev/null +++ b/grpc/subdir.am @@ -0,0 +1,34 @@ +if GRPC +lib_LTLIBRARIES += grpc/libfrrgrpc_pb.la +endif + +grpc_libfrrgrpc_pb_la_LDFLAGS = -version-info 0:0:0 +grpc_libfrrgrpc_pb_la_CPPFLAGS = $(AM_CPPFLAGS) $(GRPC_CXXFLAGS) + +if GRPC +nodist_grpc_libfrrgrpc_pb_la_SOURCES = \ + grpc/frr-northbound.pb.cc \ + grpc/frr-northbound.grpc.pb.cc \ + # end +endif + +CLEANFILES += \ + grpc/frr-northbound.pb.cc \ + grpc/frr-northbound.pb.h \ + grpc/frr-northbound.grpc.pb.cc \ + grpc/frr-northbound.grpc.pb.h \ + # end + +EXTRA_DIST += grpc/frr-northbound.proto + +AM_V_PROTOC = $(am__v_PROTOC_$(V)) +am__v_PROTOC_ = $(am__v_PROTOC_$(AM_DEFAULT_VERBOSITY)) +am__v_PROTOC_0 = @echo " PROTOC" $@; +am__v_PROTOC_1 = + +SUFFIXES += .pb.h .pb.cc .grpc.pb.cc + +.proto.pb.cc: + $(AM_V_PROTOC)$(PROTOC) -I$(top_srcdir) --cpp_out=$(top_srcdir) $(top_srcdir)/$^ +.proto.grpc.pb.cc: + $(AM_V_PROTOC)$(PROTOC) -I$(top_srcdir) --grpc_out=$(top_srcdir) --plugin=protoc-gen-grpc=`which grpc_cpp_plugin` $(top_srcdir)/$^ diff --git a/include/linux/fib_rules.h b/include/linux/fib_rules.h index bc6688012c..232df14e12 100644 --- a/include/linux/fib_rules.h +++ b/include/linux/fib_rules.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef __LINUX_FIB_RULES_H #define __LINUX_FIB_RULES_H @@ -22,13 +23,23 @@ struct fib_rule_hdr { __u8 tos; __u8 table; - __u8 res1; /* reserved */ + __u8 res1; /* reserved */ __u8 res2; /* reserved */ __u8 action; __u32 flags; }; +struct fib_rule_uid_range { + __u32 start; + __u32 end; +}; + +struct fib_rule_port_range { + __u16 start; + __u16 end; +}; + enum { FRA_UNSPEC, FRA_DST, /* destination address */ @@ -43,7 +54,7 @@ enum { FRA_UNUSED5, FRA_FWMARK, /* mark */ FRA_FLOW, /* flow/class id */ - FRA_UNUSED6, + FRA_TUN_ID, FRA_SUPPRESS_IFGROUP, FRA_SUPPRESS_PREFIXLEN, FRA_TABLE, /* Extended table id */ @@ -51,6 +62,11 @@ enum { FRA_OIFNAME, FRA_PAD, FRA_L3MDEV, /* iif or oif is l3mdev goto its table */ + FRA_UID_RANGE, /* UID range */ + FRA_PROTOCOL, /* Originator of the rule */ + FRA_IP_PROTO, /* ip proto */ + FRA_SPORT_RANGE, /* sport */ + FRA_DPORT_RANGE, /* dport */ __FRA_MAX }; diff --git a/include/linux/if_addr.h b/include/linux/if_addr.h index a924606f36..dfcf3ce009 100644 --- a/include/linux/if_addr.h +++ b/include/linux/if_addr.h @@ -34,6 +34,7 @@ enum { IFA_MULTICAST, IFA_FLAGS, IFA_RT_PRIORITY, /* u32, priority/metric for prefix route */ + IFA_TARGET_NETNSID, __IFA_MAX, }; @@ -63,7 +64,9 @@ struct ifa_cacheinfo { }; /* backwards compatibility for userspace */ +#ifndef __KERNEL__ #define IFA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg)))) #define IFA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifaddrmsg)) +#endif #endif diff --git a/include/linux/if_bridge.h b/include/linux/if_bridge.h index 156f4434ca..50011d55ec 100644 --- a/include/linux/if_bridge.h +++ b/include/linux/if_bridge.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ /* * Linux ethernet bridge * @@ -10,8 +11,8 @@ * 2 of the License, or (at your option) any later version. */ -#ifndef _LINUX_IF_BRIDGE_H -#define _LINUX_IF_BRIDGE_H +#ifndef _UAPI_LINUX_IF_BRIDGE_H +#define _UAPI_LINUX_IF_BRIDGE_H #include #include @@ -96,7 +97,7 @@ struct __fdb_entry { __u32 ageing_timer_value; __u8 port_hi; __u8 pad0; - __u16 unused; + __u16 vlan; }; /* Bridge Flags */ @@ -236,6 +237,7 @@ struct br_mdb_entry { #define MDB_PERMANENT 1 __u8 state; #define MDB_FLAGS_OFFLOAD (1 << 0) +#define MDB_FLAGS_FAST_LEAVE (1 << 1) __u8 flags; __u16 vid; struct { @@ -291,4 +293,15 @@ struct br_mcast_stats { __u64 mcast_bytes[BR_MCAST_DIR_SIZE]; __u64 mcast_packets[BR_MCAST_DIR_SIZE]; }; -#endif /* _LINUX_IF_BRIDGE_H */ + +/* FDB notification bits for NDA_NOTIFY: + * - BR_FDB_NFY_STATIC - notify on activity/expire even for a static entry + * - BR_FDB_NFY_INACTIVE - mark as inactive to avoid double notification, + * used with BR_FDB_NFY_STATIC (kernel controlled) + */ +enum { + BR_FDB_NFY_STATIC, + BR_FDB_NFY_INACTIVE, + BR_FDB_NFY_MAX +}; +#endif /* _UAPI_LINUX_IF_BRIDGE_H */ diff --git a/include/linux/if_link.h b/include/linux/if_link.h index 1f97d0560b..22a45914a2 100644 --- a/include/linux/if_link.h +++ b/include/linux/if_link.h @@ -1,5 +1,6 @@ -#ifndef _LINUX_IF_LINK_H -#define _LINUX_IF_LINK_H +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _UAPI_LINUX_IF_LINK_H +#define _UAPI_LINUX_IF_LINK_H #include #include @@ -158,6 +159,14 @@ enum { IFLA_PAD, IFLA_XDP, IFLA_EVENT, + IFLA_NEW_NETNSID, + IFLA_IF_NETNSID, + IFLA_TARGET_NETNSID = IFLA_IF_NETNSID, /* new alias */ + IFLA_CARRIER_UP_COUNT, + IFLA_CARRIER_DOWN_COUNT, + IFLA_NEW_IFINDEX, + IFLA_MIN_MTU, + IFLA_MAX_MTU, __IFLA_MAX }; @@ -165,8 +174,10 @@ enum { #define IFLA_MAX (__IFLA_MAX - 1) /* backwards compatibility for userspace */ +#ifndef __KERNEL__ #define IFLA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg)))) #define IFLA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifinfomsg)) +#endif enum { IFLA_INET_UNSPEC, @@ -276,6 +287,7 @@ enum { IFLA_BR_MCAST_STATS_ENABLED, IFLA_BR_MCAST_IGMP_VERSION, IFLA_BR_MCAST_MLD_VERSION, + IFLA_BR_VLAN_STATS_PER_PORT, __IFLA_BR_MAX, }; @@ -323,6 +335,14 @@ enum { IFLA_BRPORT_MCAST_TO_UCAST, IFLA_BRPORT_VLAN_TUNNEL, IFLA_BRPORT_BCAST_FLOOD, + IFLA_BRPORT_GROUP_FWD_MASK, + IFLA_BRPORT_NEIGH_SUPPRESS, + IFLA_BRPORT_ISOLATED, + IFLA_BRPORT_BACKUP_PORT, + IFLA_BRPORT_PEER_LINK = 60, /* MLAG peer link */ + IFLA_BRPORT_DUAL_LINK, /* MLAG Dual Connected link */ + IFLA_BRPORT_GROUP_FWD_MASKHI, + IFLA_BRPORT_DUAL_LINK_READY, __IFLA_BRPORT_MAX }; #define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1) @@ -448,6 +468,16 @@ enum { #define IFLA_MACSEC_MAX (__IFLA_MACSEC_MAX - 1) +/* XFRM section */ +enum { + IFLA_XFRM_UNSPEC, + IFLA_XFRM_LINK, + IFLA_XFRM_IF_ID, + __IFLA_XFRM_MAX +}; + +#define IFLA_XFRM_MAX (__IFLA_XFRM_MAX - 1) + enum macsec_validation_type { MACSEC_VALIDATE_DISABLED = 0, MACSEC_VALIDATE_CHECK = 1, @@ -460,6 +490,7 @@ enum macsec_validation_type { enum { IFLA_IPVLAN_UNSPEC, IFLA_IPVLAN_MODE, + IFLA_IPVLAN_FLAGS, __IFLA_IPVLAN_MAX }; @@ -472,6 +503,9 @@ enum ipvlan_mode { IPVLAN_MODE_MAX }; +#define IPVLAN_F_PRIVATE 0x01 +#define IPVLAN_F_VEPA 0x02 + /* VXLAN section */ enum { IFLA_VXLAN_UNSPEC, @@ -502,6 +536,7 @@ enum { IFLA_VXLAN_COLLECT_METADATA, IFLA_VXLAN_LABEL, IFLA_VXLAN_GPE, + IFLA_VXLAN_TTL_INHERIT, __IFLA_VXLAN_MAX }; #define IFLA_VXLAN_MAX (__IFLA_VXLAN_MAX - 1) @@ -585,6 +620,8 @@ enum { IFLA_BOND_AD_USER_PORT_KEY, IFLA_BOND_AD_ACTOR_SYSTEM, IFLA_BOND_TLB_DYNAMIC_LB, + IFLA_BOND_CL_START = 60, + IFLA_BOND_AD_LACP_BYPASS = IFLA_BOND_CL_START, __IFLA_BOND_MAX, }; @@ -612,6 +649,9 @@ enum { IFLA_BOND_SLAVE_AD_AGGREGATOR_ID, IFLA_BOND_SLAVE_AD_ACTOR_OPER_PORT_STATE, IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_STATE, + + IFLA_BOND_SLAVE_CL_START = 50, + IFLA_BOND_SLAVE_AD_RX_BYPASS = IFLA_BOND_SLAVE_CL_START, __IFLA_BOND_SLAVE_MAX, }; @@ -721,6 +761,8 @@ enum { IFLA_VF_STATS_BROADCAST, IFLA_VF_STATS_MULTICAST, IFLA_VF_STATS_PAD, + IFLA_VF_STATS_RX_DROPPED, + IFLA_VF_STATS_TX_DROPPED, __IFLA_VF_STATS_MAX, }; @@ -872,6 +914,7 @@ enum { enum { LINK_XSTATS_TYPE_UNSPEC, LINK_XSTATS_TYPE_BRIDGE, + LINK_XSTATS_TYPE_BOND, __LINK_XSTATS_TYPE_MAX }; #define LINK_XSTATS_TYPE_MAX (__LINK_XSTATS_TYPE_MAX - 1) @@ -902,6 +945,7 @@ enum { XDP_ATTACHED_DRV, XDP_ATTACHED_SKB, XDP_ATTACHED_HW, + XDP_ATTACHED_MULTI, }; enum { @@ -910,6 +954,9 @@ enum { IFLA_XDP_ATTACHED, IFLA_XDP_FLAGS, IFLA_XDP_PROG_ID, + IFLA_XDP_DRV_PROG_ID, + IFLA_XDP_SKB_PROG_ID, + IFLA_XDP_HW_PROG_ID, __IFLA_XDP_MAX, }; @@ -925,4 +972,43 @@ enum { IFLA_EVENT_BONDING_OPTIONS, /* change in bonding options */ }; -#endif /* _LINUX_IF_LINK_H */ +/* tun section */ + +enum { + IFLA_TUN_UNSPEC, + IFLA_TUN_OWNER, + IFLA_TUN_GROUP, + IFLA_TUN_TYPE, + IFLA_TUN_PI, + IFLA_TUN_VNET_HDR, + IFLA_TUN_PERSIST, + IFLA_TUN_MULTI_QUEUE, + IFLA_TUN_NUM_QUEUES, + IFLA_TUN_NUM_DISABLED_QUEUES, + __IFLA_TUN_MAX, +}; + +#define IFLA_TUN_MAX (__IFLA_TUN_MAX - 1) + +/* rmnet section */ + +#define RMNET_FLAGS_INGRESS_DEAGGREGATION (1U << 0) +#define RMNET_FLAGS_INGRESS_MAP_COMMANDS (1U << 1) +#define RMNET_FLAGS_INGRESS_MAP_CKSUMV4 (1U << 2) +#define RMNET_FLAGS_EGRESS_MAP_CKSUMV4 (1U << 3) + +enum { + IFLA_RMNET_UNSPEC, + IFLA_RMNET_MUX_ID, + IFLA_RMNET_FLAGS, + __IFLA_RMNET_MAX, +}; + +#define IFLA_RMNET_MAX (__IFLA_RMNET_MAX - 1) + +struct ifla_rmnet_flags { + __u32 flags; + __u32 mask; +}; + +#endif /* _UAPI_LINUX_IF_LINK_H */ diff --git a/include/linux/lwtunnel.h b/include/linux/lwtunnel.h index 3298426271..de696ca12f 100644 --- a/include/linux/lwtunnel.h +++ b/include/linux/lwtunnel.h @@ -1,5 +1,6 @@ -#ifndef _LWTUNNEL_H_ -#define _LWTUNNEL_H_ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _UAPI_LWTUNNEL_H_ +#define _UAPI_LWTUNNEL_H_ #include @@ -67,4 +68,4 @@ enum { #define LWT_BPF_MAX_HEADROOM 256 -#endif /* _LWTUNNEL_H_ */ +#endif /* _UAPI_LWTUNNEL_H_ */ diff --git a/include/linux/mpls_iptunnel.h b/include/linux/mpls_iptunnel.h index 1a0e57b45a..521f2e605f 100644 --- a/include/linux/mpls_iptunnel.h +++ b/include/linux/mpls_iptunnel.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ /* * mpls tunnel api * @@ -10,8 +11,8 @@ * 2 of the License, or (at your option) any later version. */ -#ifndef _LINUX_MPLS_IPTUNNEL_H -#define _LINUX_MPLS_IPTUNNEL_H +#ifndef _UAPI_LINUX_MPLS_IPTUNNEL_H +#define _UAPI_LINUX_MPLS_IPTUNNEL_H /* MPLS tunnel attributes * [RTA_ENCAP] = { @@ -27,4 +28,4 @@ enum { }; #define MPLS_IPTUNNEL_MAX (__MPLS_IPTUNNEL_MAX - 1) -#endif /* _LINUX_MPLS_IPTUNNEL_H */ +#endif /* _UAPI_LINUX_MPLS_IPTUNNEL_H */ diff --git a/include/linux/neighbour.h b/include/linux/neighbour.h index 3199d28980..33c17af1cc 100644 --- a/include/linux/neighbour.h +++ b/include/linux/neighbour.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ #ifndef __LINUX_NEIGHBOUR_H #define __LINUX_NEIGHBOUR_H @@ -27,6 +28,9 @@ enum { NDA_MASTER, NDA_LINK_NETNSID, NDA_SRC_VNI, + NDA_PROTOCOL, /* Originator of entry */ + NDA_NH_ID, + NDA_NOTIFY, __NDA_MAX }; @@ -42,6 +46,7 @@ enum { #define NTF_PROXY 0x08 /* == ATF_PUBL */ #define NTF_EXT_LEARNED 0x10 #define NTF_OFFLOADED 0x20 +#define NTF_STICKY 0x40 #define NTF_ROUTER 0x80 /* diff --git a/include/linux/net_namespace.h b/include/linux/net_namespace.h index 9a92b7e14a..0ed9dd61d3 100644 --- a/include/linux/net_namespace.h +++ b/include/linux/net_namespace.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ /* Copyright (c) 2015 6WIND S.A. * Author: Nicolas Dichtel * @@ -5,8 +6,8 @@ * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. */ -#ifndef _LINUX_NET_NAMESPACE_H_ -#define _LINUX_NET_NAMESPACE_H_ +#ifndef _UAPI_LINUX_NET_NAMESPACE_H_ +#define _UAPI_LINUX_NET_NAMESPACE_H_ /* Attributes of RTM_NEWNSID/RTM_GETNSID messages */ enum { @@ -15,9 +16,10 @@ enum { NETNSA_NSID, NETNSA_PID, NETNSA_FD, + NETNSA_TARGET_NSID, __NETNSA_MAX, }; #define NETNSA_MAX (__NETNSA_MAX - 1) -#endif /* _LINUX_NET_NAMESPACE_H_ */ +#endif /* _UAPI_LINUX_NET_NAMESPACE_H_ */ diff --git a/include/linux/netlink.h b/include/linux/netlink.h index 0b2c29bd08..750a36be67 100644 --- a/include/linux/netlink.h +++ b/include/linux/netlink.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -#ifndef __LINUX_NETLINK_H -#define __LINUX_NETLINK_H +#ifndef _UAPI__LINUX_NETLINK_H +#define _UAPI__LINUX_NETLINK_H #include #include /* for __kernel_sa_family_t */ @@ -16,7 +16,7 @@ #define NETLINK_SELINUX 7 /* SELinux event notifications */ #define NETLINK_ISCSI 8 /* Open-iSCSI */ #define NETLINK_AUDIT 9 /* auditing */ -#define NETLINK_FIB_LOOKUP 10 +#define NETLINK_FIB_LOOKUP 10 #define NETLINK_CONNECTOR 11 #define NETLINK_NETFILTER 12 /* netfilter subsystem */ #define NETLINK_IP6_FW 13 @@ -32,7 +32,7 @@ #define NETLINK_INET_DIAG NETLINK_SOCK_DIAG -#define MAX_LINKS 32 +#define MAX_LINKS 32 struct sockaddr_nl { __kernel_sa_family_t nl_family; /* AF_NETLINK */ @@ -147,12 +147,15 @@ enum nlmsgerr_attrs { #define NETLINK_PKTINFO 3 #define NETLINK_BROADCAST_ERROR 4 #define NETLINK_NO_ENOBUFS 5 +#ifndef __KERNEL__ #define NETLINK_RX_RING 6 #define NETLINK_TX_RING 7 +#endif #define NETLINK_LISTEN_ALL_NSID 8 #define NETLINK_LIST_MEMBERSHIPS 9 #define NETLINK_CAP_ACK 10 #define NETLINK_EXT_ACK 11 +#define NETLINK_GET_STRICT_CHK 12 struct nl_pktinfo { __u32 group; @@ -175,6 +178,7 @@ struct nl_mmap_hdr { __u32 nm_gid; }; +#ifndef __KERNEL__ enum nl_mmap_status { NL_MMAP_STATUS_UNUSED, NL_MMAP_STATUS_RESERVED, @@ -186,6 +190,7 @@ enum nl_mmap_status { #define NL_MMAP_MSG_ALIGNMENT NLMSG_ALIGNTO #define NL_MMAP_MSG_ALIGN(sz) __ALIGN_KERNEL(sz, NL_MMAP_MSG_ALIGNMENT) #define NL_MMAP_HDRLEN NL_MMAP_MSG_ALIGN(sizeof(struct nl_mmap_hdr)) +#endif #define NET_MAJOR 36 /* Major 36 is reserved for networking */ @@ -244,4 +249,4 @@ struct nla_bitfield32 { __u32 selector; }; -#endif /* __LINUX_NETLINK_H */ +#endif /* _UAPI__LINUX_NETLINK_H */ diff --git a/include/linux/nexthop.h b/include/linux/nexthop.h new file mode 100644 index 0000000000..ee2a15b9c7 --- /dev/null +++ b/include/linux/nexthop.h @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_NEXTHOP_H +#define _LINUX_NEXTHOP_H + +#include + +#define RTM_NHA(h) ((struct rtattr *)(((char *)(h)) + \ + NLMSG_ALIGN(sizeof(struct nhmsg)))) + +struct nhmsg { + unsigned char nh_family; + unsigned char nh_scope; /* return only */ + unsigned char nh_protocol; /* Routing protocol that installed nh */ + unsigned char resvd; + unsigned int nh_flags; /* RTNH_F flags */ +}; + +struct nexthop_grp { + __u32 id; /* nexthop id - must exist */ + __u8 weight; /* weight of this nexthop */ + __u8 resvd1; + __u16 resvd2; +}; + +enum { + NEXTHOP_GRP_TYPE_MPATH, /* default type if not specified */ + __NEXTHOP_GRP_TYPE_MAX, +}; + +#define NEXTHOP_GRP_TYPE_MAX (__NEXTHOP_GRP_TYPE_MAX - 1) + +enum { + NHA_UNSPEC, + NHA_ID, /* u32; id for nexthop. id == 0 means auto-assign */ + + NHA_GROUP, /* array of nexthop_grp */ + NHA_GROUP_TYPE, /* u16 one of NEXTHOP_GRP_TYPE */ + /* if NHA_GROUP attribute is added, no other attributes can be set */ + + NHA_BLACKHOLE, /* flag; nexthop used to blackhole packets */ + /* if NHA_BLACKHOLE is added, OIF, GATEWAY, ENCAP can not be set */ + + NHA_OIF, /* u32; nexthop device */ + NHA_GATEWAY, /* be32 (IPv4) or in6_addr (IPv6) gw address */ + NHA_ENCAP_TYPE, /* u16; lwt encap type */ + NHA_ENCAP, /* lwt encap data */ + + /* NHA_OIF can be appended to dump request to return only + * nexthops using given device + */ + NHA_GROUPS, /* flag; only return nexthop groups in dump */ + NHA_MASTER, /* u32; only return nexthops with given master dev */ + NHA_FDB, /* nexthop belongs to a bridge fdb */ + + __NHA_MAX, +}; + +#define NHA_MAX (__NHA_MAX - 1) +#endif diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h index 813e9e0767..eddbc04504 100644 --- a/include/linux/rtnetlink.h +++ b/include/linux/rtnetlink.h @@ -1,5 +1,6 @@ -#ifndef __LINUX_RTNETLINK_H -#define __LINUX_RTNETLINK_H +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _UAPI__LINUX_RTNETLINK_H +#define _UAPI__LINUX_RTNETLINK_H #include #include @@ -149,6 +150,20 @@ enum { RTM_NEWCACHEREPORT = 96, #define RTM_NEWCACHEREPORT RTM_NEWCACHEREPORT + RTM_NEWCHAIN = 100, +#define RTM_NEWCHAIN RTM_NEWCHAIN + RTM_DELCHAIN, +#define RTM_DELCHAIN RTM_DELCHAIN + RTM_GETCHAIN, +#define RTM_GETCHAIN RTM_GETCHAIN + + RTM_NEWNEXTHOP = 104, +#define RTM_NEWNEXTHOP RTM_NEWNEXTHOP + RTM_DELNEXTHOP, +#define RTM_DELNEXTHOP RTM_DELNEXTHOP + RTM_GETNEXTHOP, +#define RTM_GETNEXTHOP RTM_GETNEXTHOP + __RTM_MAX, #define RTM_MAX (((__RTM_MAX + 3) & ~3) - 1) }; @@ -157,7 +172,7 @@ enum { #define RTM_NR_FAMILIES (RTM_NR_MSGTYPES >> 2) #define RTM_FAM(cmd) (((cmd) - RTM_BASE) >> 2) -/* +/* Generic structure for encapsulation of optional route information. It is reminiscent of sockaddr, but with sa_family replaced with attribute type. @@ -197,7 +212,7 @@ struct rtmsg { unsigned char rtm_table; /* Routing table id */ unsigned char rtm_protocol; /* Routing protocol; see below */ - unsigned char rtm_scope; /* See below */ + unsigned char rtm_scope; /* See below */ unsigned char rtm_type; /* See below */ unsigned rtm_flags; @@ -253,6 +268,11 @@ enum { #define RTPROT_DHCP 16 /* DHCP client */ #define RTPROT_MROUTED 17 /* Multicast daemon */ #define RTPROT_BABEL 42 /* Babel daemon */ +#define RTPROT_BGP 186 /* BGP Routes */ +#define RTPROT_ISIS 187 /* ISIS Routes */ +#define RTPROT_OSPF 188 /* OSPF Routes */ +#define RTPROT_RIP 189 /* RIP Routes */ +#define RTPROT_EIGRP 192 /* EIGRP Routes */ /* rtm_scope @@ -326,6 +346,10 @@ enum rtattr_type_t { RTA_PAD, RTA_UID, RTA_TTL_PROPAGATE, + RTA_IP_PROTO, + RTA_SPORT, + RTA_DPORT, + RTA_NH_ID, __RTA_MAX }; @@ -430,6 +454,8 @@ enum { #define RTAX_QUICKACK RTAX_QUICKACK RTAX_CC_ALGO, #define RTAX_CC_ALGO RTAX_CC_ALGO + RTAX_FASTOPEN_NO_COOKIE, +#define RTAX_FASTOPEN_NO_COOKIE RTAX_FASTOPEN_NO_COOKIE __RTAX_MAX }; @@ -497,7 +523,7 @@ struct ifinfomsg { }; /******************************************************************** - * prefix information + * prefix information ****/ struct prefixmsg { @@ -511,7 +537,7 @@ struct prefixmsg { unsigned char prefix_pad3; }; -enum +enum { PREFIX_UNSPEC, PREFIX_ADDRESS, @@ -538,9 +564,19 @@ struct tcmsg { int tcm_ifindex; __u32 tcm_handle; __u32 tcm_parent; +/* tcm_block_index is used instead of tcm_parent + * in case tcm_ifindex == TCM_IFINDEX_MAGIC_BLOCK + */ +#define tcm_block_index tcm_parent __u32 tcm_info; }; +/* For manipulation of filters in shared block, tcm_ifindex is set to + * TCM_IFINDEX_MAGIC_BLOCK, and tcm_parent is aliased to tcm_block_index + * which is the block index. + */ +#define TCM_IFINDEX_MAGIC_BLOCK (0xFFFFFFFFU) + enum { TCA_UNSPEC, TCA_KIND, @@ -554,6 +590,9 @@ enum { TCA_PAD, TCA_DUMP_INVISIBLE, TCA_CHAIN, + TCA_HW_OFFLOAD, + TCA_INGRESS_BLOCK, + TCA_EGRESS_BLOCK, __TCA_MAX }; @@ -586,6 +625,7 @@ enum { #define NDUSEROPT_MAX (__NDUSEROPT_MAX - 1) +#ifndef __KERNEL__ /* RTnetlink multicast groups - backwards compatibility for userspace */ #define RTMGRP_LINK 1 #define RTMGRP_NOTIFY 2 @@ -606,6 +646,7 @@ enum { #define RTMGRP_DECnet_ROUTE 0x4000 #define RTMGRP_IPV6_PREFIX 0x20000 +#endif /* RTnetlink multicast groups */ enum rtnetlink_groups { @@ -671,6 +712,8 @@ enum rtnetlink_groups { #define RTNLGRP_IPV4_MROUTE_R RTNLGRP_IPV4_MROUTE_R RTNLGRP_IPV6_MROUTE_R, #define RTNLGRP_IPV6_MROUTE_R RTNLGRP_IPV6_MROUTE_R + RTNLGRP_NEXTHOP, +#define RTNLGRP_NEXTHOP RTNLGRP_NEXTHOP __RTNLGRP_MAX }; #define RTNLGRP_MAX (__RTNLGRP_MAX - 1) @@ -715,4 +758,4 @@ enum { -#endif /* __LINUX_RTNETLINK_H */ +#endif /* _UAPI__LINUX_RTNETLINK_H */ diff --git a/include/linux/seg6.h b/include/linux/seg6.h new file mode 100644 index 0000000000..329163e4a0 --- /dev/null +++ b/include/linux/seg6.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * SR-IPv6 implementation + * + * Author: + * David Lebrun + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _LINUX_SEG6_H +#define _LINUX_SEG6_H + +#include +#include /* For struct in6_addr. */ + +/* + * SRH + */ +struct ipv6_sr_hdr { + __u8 nexthdr; + __u8 hdrlen; + __u8 type; + __u8 segments_left; + __u8 first_segment; /* Represents the last_entry field of SRH */ + __u8 flags; + __u16 tag; + + struct in6_addr segments[0]; +}; + +#define SR6_FLAG1_PROTECTED (1 << 6) +#define SR6_FLAG1_OAM (1 << 5) +#define SR6_FLAG1_ALERT (1 << 4) +#define SR6_FLAG1_HMAC (1 << 3) + +#define SR6_TLV_INGRESS 1 +#define SR6_TLV_EGRESS 2 +#define SR6_TLV_OPAQUE 3 +#define SR6_TLV_PADDING 4 +#define SR6_TLV_HMAC 5 + +#define sr_has_hmac(srh) ((srh)->flags & SR6_FLAG1_HMAC) + +struct sr6_tlv { + __u8 type; + __u8 len; + __u8 data[0]; +}; + +#endif diff --git a/include/linux/seg6_genl.h b/include/linux/seg6_genl.h new file mode 100644 index 0000000000..0c230524e0 --- /dev/null +++ b/include/linux/seg6_genl.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_SEG6_GENL_H +#define _LINUX_SEG6_GENL_H + +#define SEG6_GENL_NAME "SEG6" +#define SEG6_GENL_VERSION 0x1 + +enum { + SEG6_ATTR_UNSPEC, + SEG6_ATTR_DST, + SEG6_ATTR_DSTLEN, + SEG6_ATTR_HMACKEYID, + SEG6_ATTR_SECRET, + SEG6_ATTR_SECRETLEN, + SEG6_ATTR_ALGID, + SEG6_ATTR_HMACINFO, + __SEG6_ATTR_MAX, +}; + +#define SEG6_ATTR_MAX (__SEG6_ATTR_MAX - 1) + +enum { + SEG6_CMD_UNSPEC, + SEG6_CMD_SETHMAC, + SEG6_CMD_DUMPHMAC, + SEG6_CMD_SET_TUNSRC, + SEG6_CMD_GET_TUNSRC, + __SEG6_CMD_MAX, +}; + +#define SEG6_CMD_MAX (__SEG6_CMD_MAX - 1) + +#endif diff --git a/include/linux/seg6_hmac.h b/include/linux/seg6_hmac.h new file mode 100644 index 0000000000..3fb3412e1e --- /dev/null +++ b/include/linux/seg6_hmac.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_SEG6_HMAC_H +#define _LINUX_SEG6_HMAC_H + +#include +#include + +#define SEG6_HMAC_SECRET_LEN 64 +#define SEG6_HMAC_FIELD_LEN 32 + +struct sr6_tlv_hmac { + struct sr6_tlv tlvhdr; + __u16 reserved; + __be32 hmackeyid; + __u8 hmac[SEG6_HMAC_FIELD_LEN]; +}; + +enum { + SEG6_HMAC_ALGO_SHA1 = 1, + SEG6_HMAC_ALGO_SHA256 = 2, +}; + +#endif diff --git a/include/linux/seg6_iptunnel.h b/include/linux/seg6_iptunnel.h new file mode 100644 index 0000000000..3004e982c2 --- /dev/null +++ b/include/linux/seg6_iptunnel.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * SR-IPv6 implementation + * + * Author: + * David Lebrun + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _LINUX_SEG6_IPTUNNEL_H +#define _LINUX_SEG6_IPTUNNEL_H + +#include /* For struct ipv6_sr_hdr. */ + +enum { + SEG6_IPTUNNEL_UNSPEC, + SEG6_IPTUNNEL_SRH, + __SEG6_IPTUNNEL_MAX, +}; +#define SEG6_IPTUNNEL_MAX (__SEG6_IPTUNNEL_MAX - 1) + +struct seg6_iptunnel_encap { + int mode; + struct ipv6_sr_hdr srh[0]; +}; + +#define SEG6_IPTUN_ENCAP_SIZE(x) ((sizeof(*x)) + (((x)->srh->hdrlen + 1) << 3)) + +enum { + SEG6_IPTUN_MODE_INLINE, + SEG6_IPTUN_MODE_ENCAP, + SEG6_IPTUN_MODE_L2ENCAP, +}; + + +#endif diff --git a/include/linux/seg6_local.h b/include/linux/seg6_local.h new file mode 100644 index 0000000000..5312de80bc --- /dev/null +++ b/include/linux/seg6_local.h @@ -0,0 +1,80 @@ +/* + * SR-IPv6 implementation + * + * Author: + * David Lebrun + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _LINUX_SEG6_LOCAL_H +#define _LINUX_SEG6_LOCAL_H + +#include + +enum { + SEG6_LOCAL_UNSPEC, + SEG6_LOCAL_ACTION, + SEG6_LOCAL_SRH, + SEG6_LOCAL_TABLE, + SEG6_LOCAL_NH4, + SEG6_LOCAL_NH6, + SEG6_LOCAL_IIF, + SEG6_LOCAL_OIF, + SEG6_LOCAL_BPF, + __SEG6_LOCAL_MAX, +}; +#define SEG6_LOCAL_MAX (__SEG6_LOCAL_MAX - 1) + +enum { + SEG6_LOCAL_ACTION_UNSPEC = 0, + /* node segment */ + SEG6_LOCAL_ACTION_END = 1, + /* adjacency segment (IPv6 cross-connect) */ + SEG6_LOCAL_ACTION_END_X = 2, + /* lookup of next seg NH in table */ + SEG6_LOCAL_ACTION_END_T = 3, + /* decap and L2 cross-connect */ + SEG6_LOCAL_ACTION_END_DX2 = 4, + /* decap and IPv6 cross-connect */ + SEG6_LOCAL_ACTION_END_DX6 = 5, + /* decap and IPv4 cross-connect */ + SEG6_LOCAL_ACTION_END_DX4 = 6, + /* decap and lookup of DA in v6 table */ + SEG6_LOCAL_ACTION_END_DT6 = 7, + /* decap and lookup of DA in v4 table */ + SEG6_LOCAL_ACTION_END_DT4 = 8, + /* binding segment with insertion */ + SEG6_LOCAL_ACTION_END_B6 = 9, + /* binding segment with encapsulation */ + SEG6_LOCAL_ACTION_END_B6_ENCAP = 10, + /* binding segment with MPLS encap */ + SEG6_LOCAL_ACTION_END_BM = 11, + /* lookup last seg in table */ + SEG6_LOCAL_ACTION_END_S = 12, + /* forward to SR-unaware VNF with static proxy */ + SEG6_LOCAL_ACTION_END_AS = 13, + /* forward to SR-unaware VNF with masquerading */ + SEG6_LOCAL_ACTION_END_AM = 14, + /* custom BPF action */ + SEG6_LOCAL_ACTION_END_BPF = 15, + + __SEG6_LOCAL_ACTION_MAX, +}; + +#define SEG6_LOCAL_ACTION_MAX (__SEG6_LOCAL_ACTION_MAX - 1) + +enum { + SEG6_LOCAL_BPF_PROG_UNSPEC, + SEG6_LOCAL_BPF_PROG, + SEG6_LOCAL_BPF_PROG_NAME, + __SEG6_LOCAL_BPF_PROG_MAX, +}; + +#define SEG6_LOCAL_BPF_PROG_MAX (__SEG6_LOCAL_BPF_PROG_MAX - 1) + +#endif diff --git a/include/linux/socket.h b/include/linux/socket.h index 8c1e501774..8eb9602170 100644 --- a/include/linux/socket.h +++ b/include/linux/socket.h @@ -1,5 +1,6 @@ -#ifndef _LINUX_SOCKET_H -#define _LINUX_SOCKET_H +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _UAPI_LINUX_SOCKET_H +#define _UAPI_LINUX_SOCKET_H /* * Desired design of maximum size and alignment (see RFC2553) @@ -18,4 +19,4 @@ struct __kernel_sockaddr_storage { /* _SS_MAXSIZE value minus size of ss_family */ } __attribute__ ((aligned(_K_SS_ALIGNSIZE))); /* force desired alignment */ -#endif /* _LINUX_SOCKET_H */ +#endif /* _UAPI_LINUX_SOCKET_H */ diff --git a/include/subdir.am b/include/subdir.am index 0d7fed2852..86129c4d68 100644 --- a/include/subdir.am +++ b/include/subdir.am @@ -6,8 +6,14 @@ noinst_HEADERS += \ include/linux/mpls_iptunnel.h \ include/linux/neighbour.h \ include/linux/netlink.h \ + include/linux/nexthop.h \ include/linux/rtnetlink.h \ include/linux/socket.h \ include/linux/net_namespace.h \ include/linux/fib_rules.h \ + include/linux/seg6.h \ + include/linux/seg6_genl.h \ + include/linux/seg6_hmac.h \ + include/linux/seg6_iptunnel.h \ + include/linux/seg6_local.h \ # end diff --git a/isisd/fabricd.c b/isisd/fabricd.c index 96af28f0a1..1a081bbea6 100644 --- a/isisd/fabricd.c +++ b/isisd/fabricd.c @@ -3,7 +3,7 @@ * * Copyright (C) 2018 Christian Franke * - * This file is part of FreeRangeRouting (FRR) + * This file is part of FRRouting (FRR) * * FRR is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -108,9 +108,9 @@ static void neighbor_lists_clear(struct fabricd *f) hash_clean(f->neighbors_neighbors, neighbor_entry_del_void); } -static unsigned neighbor_entry_hash_key(void *np) +static unsigned neighbor_entry_hash_key(const void *np) { - struct neighbor_entry *n = np; + const struct neighbor_entry *n = np; return jhash(n->id, sizeof(n->id), 0x55aa5a5a); } @@ -122,9 +122,9 @@ static bool neighbor_entry_hash_cmp(const void *a, const void *b) return memcmp(na->id, nb->id, sizeof(na->id)) == 0; } -static int neighbor_entry_list_cmp(void *a, void *b) +static int neighbor_entry_list_cmp(const void *a, const void *b) { - struct neighbor_entry *na = a, *nb = b; + const struct neighbor_entry *na = a, *nb = b; return -memcmp(na->id, nb->id, sizeof(na->id)); } @@ -221,7 +221,10 @@ struct fabricd *fabricd_new(struct isis_area *area) rv->area = area; rv->initial_sync_state = FABRICD_SYNC_PENDING; - rv->spftree = isis_spftree_new(area); + rv->spftree = + isis_spftree_new(area, &area->lspdb[IS_LEVEL_2 - 1], + area->isis->sysid, ISIS_LEVEL2, SPFTREE_IPV4, + SPF_TYPE_FORWARD, F_SPFTREE_HOPCOUNT_METRIC); rv->neighbors = skiplist_new(0, neighbor_entry_list_cmp, neighbor_entry_del_void); rv->neighbors_neighbors = hash_create(neighbor_entry_hash_key, @@ -372,8 +375,7 @@ static uint8_t fabricd_calculate_fabric_tier(struct isis_area *area) return ISIS_TIER_UNDEFINED; } - zlog_info("OpenFabric: Found %s as furthest t0 from local system, dist == %" - PRIu32, rawlspid_print(furthest_t0->N.id), furthest_t0->d_N); + zlog_info("OpenFabric: Found %s as furthest t0 from local system, dist == %u", rawlspid_print(furthest_t0->N.id), furthest_t0->d_N); struct isis_spftree *remote_tree = isis_run_hopcount_spf(area, furthest_t0->N.id, NULL); @@ -386,8 +388,7 @@ static uint8_t fabricd_calculate_fabric_tier(struct isis_area *area) isis_spftree_del(remote_tree); return ISIS_TIER_UNDEFINED; } else { - zlog_info("OpenFabric: Found %s as furthest from remote dist == %" - PRIu32, rawlspid_print(furthest_from_remote->N.id), + zlog_info("OpenFabric: Found %s as furthest from remote dist == %u", rawlspid_print(furthest_from_remote->N.id), furthest_from_remote->d_N); } @@ -423,7 +424,7 @@ static int fabricd_tier_calculation_cb(struct thread *thread) if (tier == ISIS_TIER_UNDEFINED) return 0; - zlog_info("OpenFabric: Got tier %" PRIu8 " from algorithm. Arming timer.", + zlog_info("OpenFabric: Got tier %hhu from algorithm. Arming timer.", tier); f->tier_pending = tier; thread_add_timer(master, fabricd_tier_set_timer, f, @@ -463,7 +464,7 @@ static void fabricd_set_tier(struct fabricd *f, uint8_t tier) if (f->tier == tier) return; - zlog_info("OpenFabric: Set own tier to %" PRIu8, tier); + zlog_info("OpenFabric: Set own tier to %hhu", tier); f->tier = tier; fabricd_bump_tier_calculation_timer(f); @@ -477,7 +478,7 @@ void fabricd_run_spf(struct isis_area *area) if (!f) return; - isis_run_hopcount_spf(area, isis->sysid, f->spftree); + isis_run_hopcount_spf(area, area->isis->sysid, f->spftree); neighbors_neighbors_update(f); fabricd_bump_tier_calculation_timer(f); } @@ -522,7 +523,7 @@ int fabricd_write_settings(struct isis_area *area, struct vty *vty) return written; if (f->tier_config != ISIS_TIER_UNDEFINED) { - vty_out(vty, " fabric-tier %" PRIu8 "\n", f->tier_config); + vty_out(vty, " fabric-tier %hhu\n", f->tier_config); written++; } @@ -543,7 +544,7 @@ static void move_to_queue(struct isis_lsp *lsp, struct neighbor_entry *n, if (n->adj && n->adj->circuit == circuit) return; - if (isis->debugs & DEBUG_FLOODING) { + if (IS_DEBUG_FLOODING) { zlog_debug("OpenFabric: Adding %s to %s", print_sys_hostname(n->id), (type == TX_LSP_NORMAL) ? "RF" : "DNR"); @@ -576,7 +577,7 @@ static void handle_firsthops(struct hash_bucket *bucket, void *arg) n = neighbor_entry_lookup_list(f->neighbors, vertex->N.id); if (n) { - if (isis->debugs & DEBUG_FLOODING) { + if (IS_DEBUG_FLOODING) { zlog_debug("Removing %s from NL as its in the reverse path", print_sys_hostname(n->id)); } @@ -585,7 +586,7 @@ static void handle_firsthops(struct hash_bucket *bucket, void *arg) n = neighbor_entry_lookup_hash(f->neighbors_neighbors, vertex->N.id); if (n) { - if (isis->debugs & DEBUG_FLOODING) { + if (IS_DEBUG_FLOODING) { zlog_debug("Removing %s from NN as its in the reverse path", print_sys_hostname(n->id)); } @@ -673,7 +674,7 @@ void fabricd_lsp_flood(struct isis_lsp *lsp, struct isis_circuit *circuit) struct isis_lsp *nlsp = lsp_for_neighbor(f, n); if (!nlsp || !nlsp->tlvs) { - if (isis->debugs & DEBUG_FLOODING) { + if (IS_DEBUG_FLOODING) { zlog_debug("Moving %s to DNR as it has no LSP", print_sys_hostname(n->id)); } @@ -682,7 +683,7 @@ void fabricd_lsp_flood(struct isis_lsp *lsp, struct isis_circuit *circuit) continue; } - if (isis->debugs & DEBUG_FLOODING) { + if (IS_DEBUG_FLOODING) { zlog_debug("Considering %s from NL...", print_sys_hostname(n->id)); } @@ -699,7 +700,7 @@ void fabricd_lsp_flood(struct isis_lsp *lsp, struct isis_circuit *circuit) er->id); if (nn) { - if (isis->debugs & DEBUG_FLOODING) { + if (IS_DEBUG_FLOODING) { zlog_debug("Found neighbor %s in NN, removing it from NN and setting reflood.", print_sys_hostname(nn->id)); } @@ -714,7 +715,7 @@ void fabricd_lsp_flood(struct isis_lsp *lsp, struct isis_circuit *circuit) circuit); } - if (isis->debugs & DEBUG_FLOODING) { + if (IS_DEBUG_FLOODING) { zlog_debug("OpenFabric: Flooding algorithm complete."); } } diff --git a/isisd/fabricd.h b/isisd/fabricd.h index 315cfba3f0..9455cdb0f0 100644 --- a/isisd/fabricd.h +++ b/isisd/fabricd.h @@ -3,7 +3,7 @@ * * Copyright (C) 2018 Christian Franke * - * This file is part of FreeRangeRouting (FRR) + * This file is part of FRRouting (FRR) * * FRR is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -53,4 +53,6 @@ void fabricd_update_lsp_no_flood(struct isis_lsp *lsp, void fabricd_configure_triggered_csnp(struct isis_area *area, int delay, bool always_send_csnp); void fabricd_init(void); +void isis_vty_daemon_init(void); + #endif diff --git a/isisd/isis_adjacency.c b/isisd/isis_adjacency.c index 9b368cc404..5bfbb2cf7e 100644 --- a/isisd/isis_adjacency.c +++ b/isisd/isis_adjacency.c @@ -43,13 +43,11 @@ #include "isisd/isis_dynhn.h" #include "isisd/isis_pdu.h" #include "isisd/isis_lsp.h" -#include "isisd/isis_spf.h" #include "isisd/isis_events.h" #include "isisd/isis_mt.h" #include "isisd/isis_tlvs.h" #include "isisd/fabricd.h" - -extern struct isis *isis; +#include "isisd/isis_nb.h" static struct isis_adjacency *adj_alloc(const uint8_t *id) { @@ -92,6 +90,8 @@ struct isis_adjacency *isis_new_adj(const uint8_t *id, const uint8_t *snpa, .last_dis_change = time(NULL); } } + adj->adj_sids = list_new(); + listnode_add(circuit->area->adjacency_list, adj); return adj; } @@ -121,6 +121,23 @@ struct isis_adjacency *isis_adj_lookup_snpa(const uint8_t *ssnpa, return NULL; } +struct isis_adjacency *isis_adj_find(const struct isis_area *area, int level, + const uint8_t *sysid) +{ + struct isis_adjacency *adj; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(area->adjacency_list, node, adj)) { + if (!(adj->level & level)) + continue; + + if (!memcmp(adj->sysid, sysid, ISIS_SYS_ID_LEN)) + return adj; + } + + return NULL; +} + DEFINE_HOOK(isis_adj_state_change_hook, (struct isis_adjacency *adj), (adj)) void isis_delete_adj(void *arg) @@ -131,20 +148,19 @@ void isis_delete_adj(void *arg) return; THREAD_TIMER_OFF(adj->t_expire); - if (adj->adj_state != ISIS_ADJ_DOWN) { + if (adj->adj_state != ISIS_ADJ_DOWN) adj->adj_state = ISIS_ADJ_DOWN; - hook_call(isis_adj_state_change_hook, adj); - } - /* remove from SPF trees */ - spftree_area_adj_del(adj->circuit->area, adj); + hook_call(isis_adj_state_change_hook, adj); XFREE(MTYPE_ISIS_ADJACENCY_INFO, adj->area_addresses); XFREE(MTYPE_ISIS_ADJACENCY_INFO, adj->ipv4_addresses); XFREE(MTYPE_ISIS_ADJACENCY_INFO, adj->ipv6_addresses); adj_mt_finish(adj); + list_delete(&adj->adj_sids); + listnode_delete(adj->circuit->area->adjacency_list, adj); XFREE(MTYPE_ISIS_ADJACENCY, adj); return; } @@ -166,6 +182,22 @@ static const char *adj_state2string(int state) return NULL; /* not reached */ } +static const char *adj_level2string(int level) +{ + switch (level) { + case IS_LEVEL_1: + return "level-1"; + case IS_LEVEL_2: + return "level-2"; + case IS_LEVEL_1_AND_2: + return "level-1-2"; + default: + return "unknown"; + } + + return NULL; /* not reached */ +} + void isis_adj_process_threeway(struct isis_adjacency *adj, struct isis_threeway_adj *tw_adj, enum isis_adj_usage adj_usage) @@ -188,7 +220,7 @@ void isis_adj_process_threeway(struct isis_adjacency *adj, } if (next_tw_state != adj->threeway_state) { - if (isis->debugs & DEBUG_ADJ_PACKETS) { + if (IS_DEBUG_ADJ_PACKETS) { zlog_info("ISIS-Adj (%s): Threeway state change %s to %s", adj->circuit->area->area_tag, isis_threeway_state_name(adj->threeway_state), @@ -200,13 +232,14 @@ void isis_adj_process_threeway(struct isis_adjacency *adj, fabricd_initial_sync_hello(adj->circuit); if (next_tw_state == ISIS_THREEWAY_DOWN) { - isis_adj_state_change(adj, ISIS_ADJ_DOWN, "Neighbor restarted"); + isis_adj_state_change(&adj, ISIS_ADJ_DOWN, + "Neighbor restarted"); return; } if (next_tw_state == ISIS_THREEWAY_UP) { if (adj->adj_state != ISIS_ADJ_UP) { - isis_adj_state_change(adj, ISIS_ADJ_UP, NULL); + isis_adj_state_change(&adj, ISIS_ADJ_UP, NULL); adj->adj_usage = adj_usage; } } @@ -217,61 +250,65 @@ void isis_adj_process_threeway(struct isis_adjacency *adj, adj->threeway_state = next_tw_state; } +void isis_log_adj_change(struct isis_adjacency *adj, + enum isis_adj_state old_state, + enum isis_adj_state new_state, const char *reason) +{ + const char *adj_name; + struct isis_dynhn *dyn; -void isis_adj_state_change(struct isis_adjacency *adj, + dyn = dynhn_find_by_id(adj->sysid); + if (dyn) + adj_name = dyn->hostname; + else + adj_name = sysid_print(adj->sysid); + + zlog_info( + "%%ADJCHANGE: Adjacency to %s (%s) for %s changed from %s to %s, %s", + adj_name, adj->circuit->interface->name, + adj_level2string(adj->level), adj_state2string(old_state), + adj_state2string(new_state), reason ? reason : "unspecified"); +} +void isis_adj_state_change(struct isis_adjacency **padj, enum isis_adj_state new_state, const char *reason) { + struct isis_adjacency *adj = *padj; enum isis_adj_state old_state = adj->adj_state; struct isis_circuit *circuit = adj->circuit; - bool del; + bool del = false; + + if (new_state == old_state) + return; adj->adj_state = new_state; - if (new_state != old_state) { - send_hello_sched(circuit, adj->level, TRIGGERED_IIH_DELAY); - } + send_hello_sched(circuit, adj->level, TRIGGERED_IIH_DELAY); - if (isis->debugs & DEBUG_ADJ_PACKETS) { + if (IS_DEBUG_ADJ_PACKETS) { zlog_debug("ISIS-Adj (%s): Adjacency state change %d->%d: %s", circuit->area->area_tag, old_state, new_state, reason ? reason : "unspecified"); } - if (circuit->area->log_adj_changes) { - const char *adj_name; - struct isis_dynhn *dyn; - - dyn = dynhn_find_by_id(adj->sysid); - if (dyn) - adj_name = dyn->hostname; - else - adj_name = sysid_print(adj->sysid); - - zlog_info( - "%%ADJCHANGE: Adjacency to %s (%s) changed from %s to %s, %s", - adj_name, adj->circuit->interface->name, - adj_state2string(old_state), - adj_state2string(new_state), - reason ? reason : "unspecified"); - } + if (circuit->area->log_adj_changes) + isis_log_adj_change(adj, old_state, new_state, reason); + circuit->adj_state_changes++; #ifndef FABRICD /* send northbound notification */ isis_notif_adj_state_change(adj, new_state, reason); #endif /* ifndef FABRICD */ if (circuit->circ_type == CIRCUIT_T_BROADCAST) { - del = false; for (int level = IS_LEVEL_1; level <= IS_LEVEL_2; level++) { if ((adj->level & level) == 0) 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); adj->flaps++; - } else if (new_state == ISIS_ADJ_DOWN) { + } else if (old_state == ISIS_ADJ_UP) { listnode_delete(circuit->u.bc.adjdb[level - 1], adj); @@ -279,8 +316,8 @@ void isis_adj_state_change(struct isis_adjacency *adj, if (circuit->upadjcount[level - 1] == 0) isis_tx_queue_clean(circuit->tx_queue); - hook_call(isis_adj_state_change_hook, adj); - del = true; + if (new_state == ISIS_ADJ_DOWN) + del = true; } if (circuit->u.bc.lan_neighs[level - 1]) { @@ -297,17 +334,12 @@ void isis_adj_state_change(struct isis_adjacency *adj, lsp_regenerate_schedule_pseudo(circuit, level); } - if (del) - isis_delete_adj(adj); - } else if (circuit->circ_type == CIRCUIT_T_P2P) { - del = false; for (int level = IS_LEVEL_1; level <= IS_LEVEL_2; level++) { if ((adj->level & level) == 0) 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 */ @@ -323,20 +355,24 @@ void isis_adj_state_change(struct isis_adjacency *adj, circuit, 0, &circuit->t_send_csnp[1]); } - } else if (new_state == ISIS_ADJ_DOWN) { + } else if (old_state == ISIS_ADJ_UP) { if (adj->circuit->u.p2p.neighbor == adj) adj->circuit->u.p2p.neighbor = NULL; circuit->upadjcount[level - 1]--; if (circuit->upadjcount[level - 1] == 0) isis_tx_queue_clean(circuit->tx_queue); - hook_call(isis_adj_state_change_hook, adj); - del = true; + if (new_state == ISIS_ADJ_DOWN) + del = true; } } + } - if (del) - isis_delete_adj(adj); + hook_call(isis_adj_state_change_hook, adj); + + if (del) { + isis_delete_adj(adj); + *padj = NULL; } } @@ -351,7 +387,7 @@ void isis_adj_print(struct isis_adjacency *adj) if (dyn) zlog_debug("%s", dyn->hostname); - zlog_debug("SystemId %20s SNPA %s, level %d\nHolding Time %d", + zlog_debug("SystemId %20s SNPA %s, level %d; Holding Time %d", sysid_print(adj->sysid), snpa_print(adj->snpa), adj->level, adj->hold_time); if (adj->ipv4_address_count) { @@ -374,6 +410,20 @@ void isis_adj_print(struct isis_adjacency *adj) return; } +const char *isis_adj_yang_state(enum isis_adj_state state) +{ + switch (state) { + case ISIS_ADJ_DOWN: + return "down"; + case ISIS_ADJ_UP: + return "up"; + case ISIS_ADJ_INITIALIZING: + return "init"; + default: + return "failed"; + } +} + int isis_adj_expire(struct thread *thread) { struct isis_adjacency *adj; @@ -386,7 +436,7 @@ int isis_adj_expire(struct thread *thread) adj->t_expire = NULL; /* trigger the adj expire event */ - isis_adj_state_change(adj, ISIS_ADJ_DOWN, "holding time expired"); + isis_adj_state_change(&adj, ISIS_ADJ_DOWN, "holding time expired"); return 0; } @@ -415,17 +465,24 @@ 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"); } if (detail == ISIS_UI_LEVEL_DETAIL) { + struct sr_adjacency *sra; + struct listnode *anode; + level = adj->level; vty_out(vty, "\n"); if (adj->circuit) @@ -436,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"); @@ -514,6 +575,31 @@ void isis_adj_print_vty(struct isis_adjacency *adj, struct vty *vty, vty_out(vty, " %s\n", buf); } } + for (ALL_LIST_ELEMENTS_RO(adj->adj_sids, anode, sra)) { + const char *adj_type; + const char *backup; + uint32_t sid; + + switch (sra->adj->circuit->circ_type) { + case CIRCUIT_T_BROADCAST: + adj_type = "LAN Adjacency-SID"; + sid = sra->u.ladj_sid->sid; + break; + case CIRCUIT_T_P2P: + adj_type = "Adjacency-SID"; + sid = sra->u.adj_sid->sid; + break; + default: + continue; + } + backup = (sra->type == ISIS_SR_LAN_BACKUP) ? " (backup)" + : ""; + + vty_out(vty, " %s %s%s: %u\n", + (sra->nexthop.family == AF_INET) ? "IPv4" + : "IPv6", + adj_type, backup, sid); + } vty_out(vty, "\n"); } return; diff --git a/isisd/isis_adjacency.h b/isisd/isis_adjacency.h index 2a4cb6e614..3c3a211a52 100644 --- a/isisd/isis_adjacency.h +++ b/isisd/isis_adjacency.h @@ -69,6 +69,7 @@ struct isis_dis_record { }; struct bfd_session; +struct isis_area; struct isis_adjacency { uint8_t snpa[ETH_ALEN]; /* NeighbourSNPAAddress */ @@ -103,6 +104,7 @@ struct isis_adjacency { uint16_t *mt_set; /* Topologies this adjacency is valid for */ unsigned int mt_count; /* Number of entries in mt_set */ struct bfd_session *bfd_session; + struct list *adj_sids; /* Segment Routing Adj-SIDs. */ }; struct isis_threeway_adj; @@ -111,6 +113,8 @@ struct isis_adjacency *isis_adj_lookup(const uint8_t *sysid, struct list *adjdb); struct isis_adjacency *isis_adj_lookup_snpa(const uint8_t *ssnpa, struct list *adjdb); +struct isis_adjacency *isis_adj_find(const struct isis_area *area, int level, + const uint8_t *sysid); struct isis_adjacency *isis_new_adj(const uint8_t *id, const uint8_t *snpa, int level, struct isis_circuit *circuit); void isis_delete_adj(void *adj); @@ -118,9 +122,17 @@ void isis_adj_process_threeway(struct isis_adjacency *adj, struct isis_threeway_adj *tw_adj, enum isis_adj_usage adj_usage); DECLARE_HOOK(isis_adj_state_change_hook, (struct isis_adjacency *adj), (adj)) -void isis_adj_state_change(struct isis_adjacency *adj, +DECLARE_HOOK(isis_adj_ip_enabled_hook, + (struct isis_adjacency *adj, int family), (adj, family)) +DECLARE_HOOK(isis_adj_ip_disabled_hook, + (struct isis_adjacency *adj, int family), (adj, family)) +void isis_log_adj_change(struct isis_adjacency *adj, + enum isis_adj_state old_state, + enum isis_adj_state new_state, const char *reason); +void isis_adj_state_change(struct isis_adjacency **adj, enum isis_adj_state state, const char *reason); void isis_adj_print(struct isis_adjacency *adj); +const char *isis_adj_yang_state(enum isis_adj_state state); int isis_adj_expire(struct thread *thread); void isis_adj_print_vty(struct isis_adjacency *adj, struct vty *vty, char detail); diff --git a/isisd/isis_bfd.c b/isisd/isis_bfd.c index 81976f8dd2..f6255d5a59 100644 --- a/isisd/isis_bfd.c +++ b/isisd/isis_bfd.c @@ -19,7 +19,9 @@ #include #include "zclient.h" +#include "nexthop.h" #include "bfd.h" +#include "lib_errors.h" #include "isisd/isis_bfd.h" #include "isisd/isis_zebra.h" @@ -33,17 +35,19 @@ DEFINE_MTYPE_STATIC(ISISD, BFD_SESSION, "ISIS BFD Session") struct bfd_session { - struct in_addr dst_ip; - struct in_addr src_ip; + int family; + union g_addr dst_ip; + union g_addr src_ip; int status; }; -static struct bfd_session *bfd_session_new(struct in_addr *dst_ip, - struct in_addr *src_ip) +static struct bfd_session *bfd_session_new(int family, union g_addr *dst_ip, + union g_addr *src_ip) { struct bfd_session *rv; rv = XCALLOC(MTYPE_BFD_SESSION, sizeof(*rv)); + rv->family = family; rv->dst_ip = *dst_ip; rv->src_ip = *src_ip; return rv; @@ -55,7 +59,34 @@ static void bfd_session_free(struct bfd_session **session) return; XFREE(MTYPE_BFD_SESSION, *session); - *session = NULL; +} + +static bool bfd_session_same(const struct bfd_session *session, int family, + const union g_addr *src, const union g_addr *dst) +{ + if (session->family != family) + return false; + + switch (session->family) { + case AF_INET: + if (!IPV4_ADDR_SAME(&session->dst_ip.ipv4, &dst->ipv4)) + return false; + if (!IPV4_ADDR_SAME(&session->src_ip.ipv4, &src->ipv4)) + return false; + break; + case AF_INET6: + if (!IPV6_ADDR_SAME(&session->dst_ip.ipv6, &dst->ipv6)) + return false; + if (!IPV6_ADDR_SAME(&session->src_ip.ipv6, &src->ipv6)) + return false; + break; + default: + flog_err(EC_LIB_DEVELOPMENT, "%s: unknown address-family: %u", + __func__, session->family); + exit(1); + } + + return true; } static void bfd_adj_event(struct isis_adjacency *adj, struct prefix *dst, @@ -64,19 +95,37 @@ static void bfd_adj_event(struct isis_adjacency *adj, struct prefix *dst, if (!adj->bfd_session) return; - if (adj->bfd_session->dst_ip.s_addr != dst->u.prefix4.s_addr) + if (adj->bfd_session->family != dst->family) return; + switch (adj->bfd_session->family) { + case AF_INET: + if (!IPV4_ADDR_SAME(&adj->bfd_session->dst_ip.ipv4, + &dst->u.prefix4)) + return; + break; + case AF_INET6: + if (!IPV6_ADDR_SAME(&adj->bfd_session->dst_ip.ipv6, + &dst->u.prefix6)) + return; + break; + default: + flog_err(EC_LIB_DEVELOPMENT, "%s: unknown address-family: %u", + __func__, adj->bfd_session->family); + exit(1); + } + int old_status = adj->bfd_session->status; - adj->bfd_session->status = new_status; + BFD_SET_CLIENT_STATUS(adj->bfd_session->status, new_status); + if (old_status == new_status) return; - if (isis->debugs & DEBUG_BFD) { + if (IS_DEBUG_BFD) { char dst_str[INET6_ADDRSTRLEN]; - inet_ntop(AF_INET, &adj->bfd_session->dst_ip, + inet_ntop(adj->bfd_session->family, &adj->bfd_session->dst_ip, dst_str, sizeof(dst_str)); zlog_debug("ISIS-BFD: Peer %s on %s changed from %s to %s", dst_str, adj->circuit->interface->name, @@ -89,25 +138,27 @@ static void bfd_adj_event(struct isis_adjacency *adj, struct prefix *dst, return; } - isis_adj_state_change(adj, ISIS_ADJ_DOWN, "bfd session went down"); + adj->circuit->area->bfd_signalled_down = true; + + isis_adj_state_change(&adj, ISIS_ADJ_DOWN, "bfd session went down"); } -static int isis_bfd_interface_dest_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int isis_bfd_interface_dest_update(ZAPI_CALLBACK_ARGS) { struct interface *ifp; - struct prefix dst_ip; + struct prefix dst_ip, src_ip; int status; - ifp = bfd_get_peer_info(zclient->ibuf, &dst_ip, NULL, &status, vrf_id); - if (!ifp || dst_ip.family != AF_INET) + ifp = bfd_get_peer_info(zclient->ibuf, &dst_ip, &src_ip, &status, NULL, + vrf_id); + if (!ifp || (dst_ip.family != AF_INET && dst_ip.family != AF_INET6)) return 0; - if (isis->debugs & DEBUG_BFD) { + if (IS_DEBUG_BFD) { char dst_buf[INET6_ADDRSTRLEN]; - inet_ntop(AF_INET, &dst_ip.u.prefix4, - dst_buf, sizeof(dst_buf)); + inet_ntop(dst_ip.family, &dst_ip.u.prefix, dst_buf, + sizeof(dst_buf)); zlog_debug("ISIS-BFD: Received update for %s on %s: Changed state to %s", dst_buf, ifp->name, bfd_get_status_str(status)); @@ -138,15 +189,22 @@ static int isis_bfd_interface_dest_update(int command, struct zclient *zclient, return 0; } -static int isis_bfd_nbr_replay(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int isis_bfd_nbr_replay(ZAPI_CALLBACK_ARGS) { - bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER); + bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, vrf_id); struct listnode *anode; struct isis_area *area; + struct isis *isis = NULL; + + isis = isis_lookup_by_vrfid(vrf_id); + + if (isis == NULL) { + zlog_warn(" %s : ISIS routing instance not found", __func__); + return -1; + } - if (isis->debugs & DEBUG_BFD) + if (IS_DEBUG_BFD) zlog_debug("ISIS-BFD: Got neighbor replay request, resending neighbors."); for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode, area)) { @@ -157,7 +215,7 @@ static int isis_bfd_nbr_replay(int command, struct zclient *zclient, isis_bfd_circuit_cmd(circuit, ZEBRA_BFD_DEST_UPDATE); } - if (isis->debugs & DEBUG_BFD) + if (IS_DEBUG_BFD) zlog_debug("ISIS-BFD: Done with replay."); return 0; @@ -169,20 +227,20 @@ static void isis_bfd_zebra_connected(struct zclient *zclient) if (orig_zebra_connected) orig_zebra_connected(zclient); - bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER); + bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, VRF_DEFAULT); } -static void bfd_debug(struct in_addr *dst, struct in_addr *src, +static void bfd_debug(int family, union g_addr *dst, union g_addr *src, const char *interface, int command) { - if (!(isis->debugs & DEBUG_BFD)) + if (!(IS_DEBUG_BFD)) return; char dst_str[INET6_ADDRSTRLEN]; char src_str[INET6_ADDRSTRLEN]; - inet_ntop(AF_INET, dst, dst_str, sizeof(dst_str)); - inet_ntop(AF_INET, src, src_str, sizeof(src_str)); + inet_ntop(family, dst, dst_str, sizeof(dst_str)); + inet_ntop(family, src, src_str, sizeof(src_str)); const char *command_str; @@ -205,64 +263,112 @@ static void bfd_debug(struct in_addr *dst, struct in_addr *src, command_str, dst_str, interface, src_str); } +static void bfd_command(int command, struct bfd_info *bfd_info, int family, + const void *dst_ip, const void *src_ip, + const char *if_name) +{ + struct bfd_session_arg args = {}; + size_t addrlen; + + args.cbit = 1; + args.family = family; + args.vrf_id = VRF_DEFAULT; + args.command = command; + args.bfd_info = bfd_info; + if (args.bfd_info) { + args.min_rx = bfd_info->required_min_rx; + args.min_tx = bfd_info->desired_min_tx; + args.detection_multiplier = bfd_info->detect_mult; + if (bfd_info->profile[0]) { + args.profilelen = strlen(bfd_info->profile); + strlcpy(args.profile, bfd_info->profile, + sizeof(args.profile)); + } + } + + addrlen = family == AF_INET ? sizeof(struct in_addr) + : sizeof(struct in6_addr); + memcpy(&args.dst, dst_ip, addrlen); + if (src_ip) + memcpy(&args.src, src_ip, addrlen); + + if (if_name) { + strlcpy(args.ifname, if_name, sizeof(args.ifname)); + args.ifnamelen = strlen(args.ifname); + } + + zclient_bfd_command(zclient, &args); +} + static void bfd_handle_adj_down(struct isis_adjacency *adj) { if (!adj->bfd_session) return; - bfd_debug(&adj->bfd_session->dst_ip, &adj->bfd_session->src_ip, - adj->circuit->interface->name, ZEBRA_BFD_DEST_DEREGISTER); - - bfd_peer_sendmsg(zclient, NULL, AF_INET, - &adj->bfd_session->dst_ip, - &adj->bfd_session->src_ip, - adj->circuit->interface->name, - 0, /* ttl */ - 0, /* multihop */ - ZEBRA_BFD_DEST_DEREGISTER, - 0, /* set_flag */ - VRF_DEFAULT); + bfd_debug(adj->bfd_session->family, &adj->bfd_session->dst_ip, + &adj->bfd_session->src_ip, adj->circuit->interface->name, + ZEBRA_BFD_DEST_DEREGISTER); + + bfd_command(ZEBRA_BFD_DEST_DEREGISTER, NULL, adj->bfd_session->family, + &adj->bfd_session->dst_ip, &adj->bfd_session->src_ip, + (adj->circuit->interface) ? adj->circuit->interface->name + : NULL); + bfd_session_free(&adj->bfd_session); } static void bfd_handle_adj_up(struct isis_adjacency *adj, int command) { struct isis_circuit *circuit = adj->circuit; + int family; + union g_addr dst_ip; + union g_addr src_ip; + struct list *local_ips; + struct prefix *local_ip; - if (!circuit->bfd_info - || !circuit->ip_router - || !adj->ipv4_address_count) + if (!circuit->bfd_info) goto out; - struct list *local_ips = fabricd_ip_addrs(adj->circuit); - - if (!local_ips) + /* + * If IS-IS is enabled for both IPv4 and IPv6 on the circuit, prefer + * creating a BFD session over IPv6. + */ + if (circuit->ipv6_router && adj->ipv6_address_count) { + family = AF_INET6; + dst_ip.ipv6 = adj->ipv6_addresses[0]; + local_ips = circuit->ipv6_link; + if (!local_ips || list_isempty(local_ips)) + goto out; + local_ip = listgetdata(listhead(local_ips)); + src_ip.ipv6 = local_ip->u.prefix6; + } else if (circuit->ip_router && adj->ipv4_address_count) { + family = AF_INET; + dst_ip.ipv4 = adj->ipv4_addresses[0]; + local_ips = fabricd_ip_addrs(adj->circuit); + if (!local_ips || list_isempty(local_ips)) + goto out; + local_ip = listgetdata(listhead(local_ips)); + src_ip.ipv4 = local_ip->u.prefix4; + } else goto out; - struct in_addr *dst_ip = &adj->ipv4_addresses[0]; - struct prefix_ipv4 *local_ip = listgetdata(listhead(local_ips)); - struct in_addr *src_ip = &local_ip->prefix; - if (adj->bfd_session) { - if (adj->bfd_session->dst_ip.s_addr != dst_ip->s_addr - || adj->bfd_session->src_ip.s_addr != src_ip->s_addr) + if (bfd_session_same(adj->bfd_session, family, &src_ip, + &dst_ip)) bfd_handle_adj_down(adj); } if (!adj->bfd_session) - adj->bfd_session = bfd_session_new(dst_ip, src_ip); - - bfd_debug(&adj->bfd_session->dst_ip, &adj->bfd_session->src_ip, - circuit->interface->name, command); - bfd_peer_sendmsg(zclient, circuit->bfd_info, AF_INET, - &adj->bfd_session->dst_ip, - &adj->bfd_session->src_ip, - circuit->interface->name, - 0, /* ttl */ - 0, /* multihop */ - command, - 0, /* set flag */ - VRF_DEFAULT); + adj->bfd_session = bfd_session_new(family, &dst_ip, &src_ip); + + bfd_debug(adj->bfd_session->family, &adj->bfd_session->dst_ip, + &adj->bfd_session->src_ip, circuit->interface->name, command); + + bfd_command(command, circuit->bfd_info, family, + &adj->bfd_session->dst_ip, &adj->bfd_session->src_ip, + (adj->circuit->interface) ? adj->circuit->interface->name + : NULL); + return; out: bfd_handle_adj_down(adj); @@ -310,19 +416,20 @@ void isis_bfd_circuit_cmd(struct isis_circuit *circuit, int command) } } -void isis_bfd_circuit_param_set(struct isis_circuit *circuit, - uint32_t min_rx, uint32_t min_tx, - uint32_t detect_mult, int defaults) +void isis_bfd_circuit_param_set(struct isis_circuit *circuit, uint32_t min_rx, + uint32_t min_tx, uint32_t detect_mult, + const char *profile, int defaults) { int command = 0; - bfd_set_param(&circuit->bfd_info, min_rx, - min_tx, detect_mult, defaults, &command); + bfd_set_param(&circuit->bfd_info, min_rx, min_tx, detect_mult, profile, + defaults, &command); if (command) isis_bfd_circuit_cmd(circuit, command); } +#ifdef FABRICD static int bfd_circuit_write_settings(struct isis_circuit *circuit, struct vty *vty) { @@ -334,6 +441,7 @@ static int bfd_circuit_write_settings(struct isis_circuit *circuit, vty_out(vty, " %s bfd\n", PROTO_NAME); return 1; } +#endif void isis_bfd_init(void) { @@ -345,6 +453,8 @@ void isis_bfd_init(void) zclient->bfd_dest_replay = isis_bfd_nbr_replay; hook_register(isis_adj_state_change_hook, bfd_handle_adj_state_change); +#ifdef FABRICD hook_register(isis_circuit_config_write, bfd_circuit_write_settings); +#endif } diff --git a/isisd/isis_bfd.h b/isisd/isis_bfd.h index 3193f16061..6ce630688c 100644 --- a/isisd/isis_bfd.h +++ b/isisd/isis_bfd.h @@ -22,9 +22,9 @@ struct isis_circuit; void isis_bfd_circuit_cmd(struct isis_circuit *circuit, int command); -void isis_bfd_circuit_param_set(struct isis_circuit *circuit, - uint32_t min_rx, uint32_t min_tx, - uint32_t detect_mult, int defaults); +void isis_bfd_circuit_param_set(struct isis_circuit *circuit, uint32_t min_rx, + uint32_t min_tx, uint32_t detect_mult, + const char *profile, int defaults); void isis_bfd_init(void); #endif diff --git a/isisd/isis_bpf.c b/isisd/isis_bpf.c index 4e9aef47ad..454da99e09 100644 --- a/isisd/isis_bpf.c +++ b/isisd/isis_bpf.c @@ -65,12 +65,15 @@ uint8_t *readbuff = NULL; * ISO 10589 - 8.4.8 */ -uint8_t ALL_L1_ISS[6] = {0x01, 0x80, 0xC2, 0x00, 0x00, 0x14}; -uint8_t ALL_L2_ISS[6] = {0x01, 0x80, 0xC2, 0x00, 0x00, 0x15}; -uint8_t ALL_ISS[6] = {0x09, 0x00, 0x2B, 0x00, 0x00, 0x05}; -uint8_t ALL_ESS[6] = {0x09, 0x00, 0x2B, 0x00, 0x00, 0x04}; +static const uint8_t ALL_L1_ISS[6] = {0x01, 0x80, 0xC2, 0x00, 0x00, 0x14}; +static const uint8_t ALL_L2_ISS[6] = {0x01, 0x80, 0xC2, 0x00, 0x00, 0x15}; +#if 0 +/* missing support for P2P-over-LAN / ES-IS on BSD */ +static const uint8_t ALL_ISS[6] = {0x09, 0x00, 0x2B, 0x00, 0x00, 0x05}; +static const uint8_t ALL_ESS[6] = {0x09, 0x00, 0x2B, 0x00, 0x00, 0x04}; +#endif -static char sock_buff[8192]; +static char sock_buff[16384]; static int open_bpf_dev(struct isis_circuit *circuit) { @@ -187,7 +190,7 @@ int isis_sock_init(struct isis_circuit *circuit) { int retval = ISIS_OK; - frr_elevate_privs(&isisd_privs) { + frr_with_privs(&isisd_privs) { retval = open_bpf_dev(circuit); @@ -212,7 +215,7 @@ int isis_sock_init(struct isis_circuit *circuit) int isis_recv_pdu_bcast(struct isis_circuit *circuit, uint8_t *ssnpa) { - int bytesread = 0, bytestoread, offset, one = 1; + int bytesread = 0, bytestoread = 0, offset, one = 1; uint8_t *buff_ptr; struct bpf_hdr *bpf_hdr; @@ -270,8 +273,7 @@ int isis_send_pdu_bcast(struct isis_circuit *circuit, int level) buflen = stream_get_endp(circuit->snd_stream) + LLC_LEN + ETHER_HDR_LEN; if (buflen > sizeof(sock_buff)) { zlog_warn( - "isis_send_pdu_bcast: sock_buff size %zu is less than " - "output pdu size %zu on circuit %s", + "isis_send_pdu_bcast: sock_buff size %zu is less than output pdu size %zu on circuit %s", sizeof(sock_buff), buflen, circuit->interface->name); return ISIS_WARNING; } diff --git a/isisd/isis_circuit.c b/isisd/isis_circuit.c index f9a0285872..b98c265605 100644 --- a/isisd/isis_circuit.c +++ b/isisd/isis_circuit.c @@ -58,13 +58,15 @@ #include "isisd/isis_mt.h" #include "isisd/isis_errors.h" #include "isisd/isis_tx_queue.h" +#include "isisd/isis_nb.h" DEFINE_QOBJ_TYPE(isis_circuit) +DEFINE_HOOK(isis_if_new_hook, (struct interface *ifp), (ifp)) + /* * Prototypes. */ -int isis_interface_config_write(struct vty *); int isis_if_new_hook(struct interface *); int isis_if_delete_hook(struct interface *); @@ -135,8 +137,6 @@ struct isis_circuit *isis_circuit_new(void) } #endif /* ifndef FABRICD */ - circuit->mtc = mpls_te_circuit_new(); - circuit_mt_init(circuit); QOBJ_REG(circuit, isis_circuit); @@ -224,10 +224,17 @@ struct isis_circuit *circuit_scan_by_ifp(struct interface *ifp) struct isis_area *area; struct listnode *node; struct isis_circuit *circuit; + struct isis *isis = NULL; if (ifp->info) return (struct isis_circuit *)ifp->info; + isis = isis_lookup_by_vrfid(ifp->vrf_id); + if (isis == NULL) { + zlog_warn(" %s : ISIS routing instance not found", __func__); + return NULL; + } + if (isis->area_list) { for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) { circuit = @@ -266,8 +273,12 @@ void isis_circuit_add_addr(struct isis_circuit *circuit, ipv4->prefix = connected->address->u.prefix4; listnode_add(circuit->ip_addrs, ipv4); - /* Update MPLS TE Local IP address parameter */ - set_circuitparams_local_ipaddr(circuit->mtc, ipv4->prefix); + /* Update Local IP address parameter if MPLS TE is enable */ + if (circuit->ext && circuit->area + && IS_MPLS_TE(circuit->area->mta)) { + circuit->ext->local_addr.s_addr = ipv4->prefix.s_addr; + SET_SUBTLV(circuit->ext, EXT_LOCAL_ADDR); + } if (circuit->area) lsp_regenerate_schedule(circuit->area, circuit->is_type, @@ -334,15 +345,14 @@ void isis_circuit_del_addr(struct isis_circuit *circuit, if (ip) { listnode_delete(circuit->ip_addrs, ip); - prefix_ipv4_free(ip); + prefix_ipv4_free(&ip); if (circuit->area) lsp_regenerate_schedule(circuit->area, circuit->is_type, 0); } else { prefix2str(connected->address, buf, sizeof(buf)); zlog_warn( - "Nonexistent ip address %s removal attempt from \ - circuit %s", + "Nonexistent ip address %s removal attempt from circuit %s", buf, circuit->interface->name); zlog_warn("Current ip addresses on %s:", circuit->interface->name); @@ -354,7 +364,7 @@ void isis_circuit_del_addr(struct isis_circuit *circuit, zlog_warn("End of addresses"); } - prefix_ipv4_free(ipv4); + prefix_ipv4_free(&ipv4); } if (connected->address->family == AF_INET6) { ipv6 = prefix_ipv6_new(); @@ -370,7 +380,7 @@ void isis_circuit_del_addr(struct isis_circuit *circuit, } if (ip6) { listnode_delete(circuit->ipv6_link, ip6); - prefix_ipv6_free(ip6); + prefix_ipv6_free(&ip6); found = 1; } } else { @@ -382,7 +392,7 @@ void isis_circuit_del_addr(struct isis_circuit *circuit, } if (ip6) { listnode_delete(circuit->ipv6_non_link, ip6); - prefix_ipv6_free(ip6); + prefix_ipv6_free(&ip6); found = 1; } } @@ -390,8 +400,7 @@ void isis_circuit_del_addr(struct isis_circuit *circuit, if (!found) { prefix2str(connected->address, buf, sizeof(buf)); zlog_warn( - "Nonexistent ip address %s removal attempt from \ - circuit %s", + "Nonexistent ip address %s removal attempt from circuit %s", buf, circuit->interface->name); zlog_warn("Current ip addresses on %s:", circuit->interface->name); @@ -413,7 +422,7 @@ void isis_circuit_del_addr(struct isis_circuit *circuit, lsp_regenerate_schedule(circuit->area, circuit->is_type, 0); - prefix_ipv6_free(ipv6); + prefix_ipv6_free(&ipv6); } return; } @@ -467,7 +476,7 @@ void isis_circuit_if_add(struct isis_circuit *circuit, struct interface *ifp) circuit->is_passive = 1; } else { /* It's normal in case of loopback etc. */ - if (isis->debugs & DEBUG_EVENTS) + if (IS_DEBUG_EVENTS) zlog_debug("isis_circuit_if_add: unsupported media"); circuit->circ_type = CIRCUIT_T_UNKNOWN; } @@ -478,6 +487,7 @@ void isis_circuit_if_add(struct isis_circuit *circuit, struct interface *ifp) for (ALL_LIST_ELEMENTS(ifp->connected, node, nnode, conn)) isis_circuit_add_addr(circuit, conn); + } void isis_circuit_if_del(struct isis_circuit *circuit, struct interface *ifp) @@ -521,7 +531,6 @@ void isis_circuit_if_bind(struct isis_circuit *circuit, struct interface *ifp) assert(ifp->info == circuit); else ifp->info = circuit; - isis_link_params_update(circuit, ifp); } void isis_circuit_if_unbind(struct isis_circuit *circuit, struct interface *ifp) @@ -551,7 +560,7 @@ static void isis_circuit_update_all_srmflags(struct isis_circuit *circuit, if (!lspdb_count(&area->lspdb[level - 1])) continue; - for_each (lspdb, &area->lspdb[level - 1], lsp) { + frr_each (lspdb, &area->lspdb[level - 1], lsp) { if (is_set) { isis_tx_queue_add(circuit->tx_queue, lsp, TX_LSP_NORMAL); @@ -602,8 +611,20 @@ int isis_circuit_up(struct isis_circuit *circuit) if (circuit->state == C_STATE_UP) return ISIS_OK; - if (circuit->is_passive) + if (circuit->is_passive) { + /* make sure the union fields are initialized, else we + * could end with garbage values from a previous circuit + * type, which would then cause a segfault when building + * LSPs or computing the SPF tree + */ + if (circuit->circ_type == CIRCUIT_T_BROADCAST) { + circuit->u.bc.adjdb[0] = list_new(); + circuit->u.bc.adjdb[1] = list_new(); + } else if (circuit->circ_type == CIRCUIT_T_P2P) { + circuit->u.p2p.neighbor = NULL; + } return ISIS_OK; + } if (circuit->area->lsp_mtu > isis_circuit_pdu_size(circuit)) { flog_err( @@ -616,7 +637,8 @@ int isis_circuit_up(struct isis_circuit *circuit) } if (circuit->circ_type == CIRCUIT_T_BROADCAST) { - circuit->circuit_id = isis_circuit_id_gen(isis, circuit->interface); + circuit->circuit_id = isis_circuit_id_gen(circuit->area->isis, + circuit->interface); if (!circuit->circuit_id) { flog_err( EC_ISIS_CONFIG, @@ -718,6 +740,43 @@ void isis_circuit_down(struct isis_circuit *circuit) isis_notif_if_state_change(circuit, true); #endif /* ifndef FABRICD */ + /* log adjacency changes if configured to do so */ + if (circuit->area->log_adj_changes) { + struct isis_adjacency *adj = NULL; + if (circuit->circ_type == CIRCUIT_T_P2P) { + adj = circuit->u.p2p.neighbor; + if (adj) + isis_log_adj_change( + adj, adj->adj_state, ISIS_ADJ_DOWN, + "circuit is being brought down"); + } else if (circuit->circ_type == CIRCUIT_T_BROADCAST) { + struct list *adj_list; + struct listnode *node; + if (circuit->u.bc.adjdb[0]) { + adj_list = list_new(); + isis_adj_build_up_list(circuit->u.bc.adjdb[0], + adj_list); + for (ALL_LIST_ELEMENTS_RO(adj_list, node, adj)) + isis_log_adj_change( + adj, adj->adj_state, + ISIS_ADJ_DOWN, + "circuit is being brought down"); + list_delete(&adj_list); + } + if (circuit->u.bc.adjdb[1]) { + adj_list = list_new(); + isis_adj_build_up_list(circuit->u.bc.adjdb[1], + adj_list); + for (ALL_LIST_ELEMENTS_RO(adj_list, node, adj)) + isis_log_adj_change( + adj, adj->adj_state, + ISIS_ADJ_DOWN, + "circuit is being brought down"); + list_delete(&adj_list); + } + } + } + /* Clear the flags for all the lsps of the circuit. */ isis_circuit_update_all_srmflags(circuit, 0); @@ -763,7 +822,8 @@ void isis_circuit_down(struct isis_circuit *circuit) circuit->lsp_regenerate_pending[0] = 0; circuit->lsp_regenerate_pending[1] = 0; - _ISIS_CLEAR_FLAG(isis->circuit_ids_used, circuit->circuit_id); + _ISIS_CLEAR_FLAG(circuit->area->isis->circuit_ids_used, + circuit->circuit_id); circuit->circuit_id = 0; } else if (circuit->circ_type == CIRCUIT_T_P2P) { isis_delete_adj(circuit->u.p2p.neighbor); @@ -875,15 +935,13 @@ void isis_circuit_print_vty(struct isis_circuit *circuit, struct vty *vty, vty_out(vty, ", Active neighbors: %u\n", circuit->upadjcount[0]); vty_out(vty, - " Hello interval: %u, " - "Holddown count: %u %s\n", + " Hello interval: %u, Holddown count: %u %s\n", circuit->hello_interval[0], circuit->hello_multiplier[0], (circuit->pad_hellos ? "(pad)" : "(no-pad)")); vty_out(vty, - " CNSP interval: %u, " - "PSNP interval: %u\n", + " CNSP interval: %u, PSNP interval: %u\n", circuit->csnp_interval[0], circuit->psnp_interval[0]); if (circuit->circ_type == CIRCUIT_T_BROADCAST) @@ -909,15 +967,13 @@ void isis_circuit_print_vty(struct isis_circuit *circuit, struct vty *vty, vty_out(vty, ", Active neighbors: %u\n", circuit->upadjcount[1]); vty_out(vty, - " Hello interval: %u, " - "Holddown count: %u %s\n", + " Hello interval: %u, Holddown count: %u %s\n", circuit->hello_interval[1], circuit->hello_multiplier[1], (circuit->pad_hellos ? "(pad)" : "(no-pad)")); vty_out(vty, - " CNSP interval: %u, " - "PSNP interval: %u\n", + " CNSP interval: %u, PSNP interval: %u\n", circuit->csnp_interval[1], circuit->psnp_interval[1]); if (circuit->circ_type == CIRCUIT_T_BROADCAST) @@ -962,12 +1018,12 @@ void isis_circuit_print_vty(struct isis_circuit *circuit, struct vty *vty, return; } +#ifdef FABRICD DEFINE_HOOK(isis_circuit_config_write, (struct isis_circuit *circuit, struct vty *vty), (circuit, vty)) -#ifdef FABRICD -int isis_interface_config_write(struct vty *vty) +static int isis_interface_config_write(struct vty *vty) { struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT); int write = 0; @@ -976,6 +1032,14 @@ int isis_interface_config_write(struct vty *vty) struct isis_area *area; struct isis_circuit *circuit; int i; + struct isis *isis = NULL; + + isis = isis_lookup_by_vrfid(vrf->vrf_id); + + if (isis == NULL) { + vty_out(vty, "ISIS routing instance not found"); + return 0; + } FOR_ALL_INTERFACES (vrf, ifp) { /* IF name */ @@ -1190,28 +1254,26 @@ int isis_interface_config_write(struct vty *vty) return write; } #else -int isis_interface_config_write(struct vty *vty) +static int isis_interface_config_write(struct vty *vty) { - struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT); + struct vrf *vrf = NULL; int write = 0; - struct interface *ifp; - struct isis_circuit *circuit; - struct lyd_node *dnode; - FOR_ALL_INTERFACES (vrf, ifp) { - dnode = yang_dnode_get( - running_config->dnode, - "/frr-interface:lib/interface[name='%s'][vrf='%s']", - ifp->name, vrf->name); - if (dnode == NULL) - continue; + RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { + struct interface *ifp; - write++; - nb_cli_show_dnode_cmds(vty, dnode, false); - circuit = circuit_scan_by_ifp(ifp); - if (circuit) - write += hook_call(isis_circuit_config_write, circuit, - vty); + FOR_ALL_INTERFACES (vrf, ifp) { + struct lyd_node *dnode; + dnode = yang_dnode_get( + running_config->dnode, + "/frr-interface:lib/interface[name='%s'][vrf='%s']", + ifp->name, vrf->name); + if (dnode == NULL) + continue; + + write++; + nb_cli_show_dnode_cmds(vty, dnode, false); + } } return write; } @@ -1221,12 +1283,15 @@ struct isis_circuit *isis_circuit_create(struct isis_area *area, struct interface *ifp) { struct isis_circuit *circuit = circuit_scan_by_ifp(ifp); + if (circuit && circuit->area) return NULL; circuit = isis_csm_state_change(ISIS_ENABLE, circuit, area); if (circuit->state != C_STATE_CONF && circuit->state != C_STATE_UP) return circuit; isis_circuit_if_bind(circuit, ifp); + if (circuit->area->mta && circuit->area->mta->status) + isis_link_params_update(circuit, ifp); return circuit; } @@ -1234,23 +1299,35 @@ void isis_circuit_af_set(struct isis_circuit *circuit, bool ip_router, bool ipv6_router) { struct isis_area *area = circuit->area; - bool change = circuit->ip_router != ip_router - || circuit->ipv6_router != ipv6_router; + int old_ipr = circuit->ip_router; + int old_ipv6r = circuit->ipv6_router; + + /* is there something to do? */ + if (old_ipr == ip_router && old_ipv6r == ipv6_router) + return; - area->ip_circuits += ip_router - circuit->ip_router; - area->ipv6_circuits += ipv6_router - circuit->ipv6_router; circuit->ip_router = ip_router; circuit->ipv6_router = ipv6_router; + circuit_update_nlpids(circuit); - if (!change) + /* the area should always be there if we get here, but in the past + * there were corner cases where the area was NULL (e.g. because the + * circuit was deconfigured following a validation error). Do not + * segfault if this happens again. + */ + if (!area) { + zlog_err("%s: NULL area for circuit %u", __func__, + circuit->circuit_id); return; + } - circuit_update_nlpids(circuit); + area->ip_circuits += ip_router - old_ipr; + area->ipv6_circuits += ipv6_router - old_ipv6r; if (!ip_router && !ipv6_router) isis_csm_state_change(ISIS_DISABLE, circuit, area); else - lsp_regenerate_schedule(circuit->area, circuit->is_type, 0); + lsp_regenerate_schedule(area, circuit->is_type, 0); } ferr_r isis_circuit_passive_set(struct isis_circuit *circuit, bool passive) @@ -1334,7 +1411,11 @@ ferr_r isis_circuit_passwd_hmac_md5_set(struct isis_circuit *circuit, } struct cmd_node interface_node = { - INTERFACE_NODE, "%s(config-if)# ", 1, + .name = "interface", + .node = INTERFACE_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-if)# ", + .config_write = isis_interface_config_write, }; void isis_circuit_circ_type_set(struct isis_circuit *circuit, int circ_type) @@ -1381,9 +1462,58 @@ int isis_if_delete_hook(struct interface *ifp) /* Clean up the circuit data */ if (ifp && ifp->info) { circuit = ifp->info; - isis_csm_state_change(IF_DOWN_FROM_Z, circuit, circuit->area); - isis_csm_state_change(ISIS_DISABLE, circuit, circuit->area); + isis_csm_state_change(IF_DOWN_FROM_Z, circuit, ifp); + } + + return 0; +} + +static int isis_ifp_create(struct interface *ifp) +{ + struct vrf *vrf = NULL; + + if (if_is_operative(ifp)) { + vrf = vrf_lookup_by_id(ifp->vrf_id); + if (vrf) + isis_global_instance_create(vrf->name); + isis_csm_state_change(IF_UP_FROM_Z, circuit_scan_by_ifp(ifp), + ifp); } + hook_call(isis_if_new_hook, ifp); + + return 0; +} + +static int isis_ifp_up(struct interface *ifp) +{ + isis_csm_state_change(IF_UP_FROM_Z, circuit_scan_by_ifp(ifp), ifp); + + return 0; +} + +static int isis_ifp_down(struct interface *ifp) +{ + struct isis_circuit *circuit; + + circuit = isis_csm_state_change(IF_DOWN_FROM_Z, + circuit_scan_by_ifp(ifp), ifp); + if (circuit) + SET_FLAG(circuit->flags, ISIS_CIRCUIT_FLAPPED_AFTER_SPF); + + return 0; +} + +static int isis_ifp_destroy(struct interface *ifp) +{ + if (if_is_operative(ifp)) + zlog_warn("Zebra: got delete of %s, but interface is still up", + ifp->name); + + isis_csm_state_change(IF_DOWN_FROM_Z, circuit_scan_by_ifp(ifp), ifp); + + /* Cannot call if_delete because we should retain the pseudo interface + in case there is configuration info attached to it. */ + if_delete_retain(ifp); return 0; } @@ -1395,6 +1525,8 @@ void isis_circuit_init(void) hook_register_prio(if_del, 0, isis_if_delete_hook); /* Install interface node */ - install_node(&interface_node, isis_interface_config_write); + install_node(&interface_node); if_cmd_init(); + if_zapi_callbacks(isis_ifp_create, isis_ifp_up, + isis_ifp_down, isis_ifp_destroy); } diff --git a/isisd/isis_circuit.h b/isisd/isis_circuit.h index 2371c0b73a..008d7444ff 100644 --- a/isisd/isis_circuit.h +++ b/isisd/isis_circuit.h @@ -32,6 +32,8 @@ #include "isis_constants.h" #include "isis_common.h" +DECLARE_HOOK(isis_if_new_hook, (struct interface *ifp), (ifp)); + struct isis_lsp; struct password { @@ -48,16 +50,18 @@ struct metric { }; struct isis_bcast_info { - uint8_t snpa[ETH_ALEN]; /* SNPA of this circuit */ - char run_dr_elect[2]; /* Should we run dr election ? */ - struct thread *t_run_dr[2]; /* DR election thread */ - struct thread *t_send_lan_hello[2]; /* send LAN IIHs in this thread */ - struct list *adjdb[2]; /* adjacency dbs */ - struct list *lan_neighs[2]; /* list of lx neigh snpa */ - char is_dr[2]; /* Are we level x DR ? */ + uint8_t snpa[ETH_ALEN]; /* SNPA of this circuit */ + char run_dr_elect[ISIS_LEVELS]; /* Should we run dr election ? */ + struct thread *t_run_dr[ISIS_LEVELS]; /* DR election thread */ + struct thread *t_send_lan_hello[ISIS_LEVELS]; /* send LAN IIHs in this + thread */ + struct list *adjdb[ISIS_LEVELS]; /* adjacency dbs */ + struct list *lan_neighs[ISIS_LEVELS]; /* list of lx neigh snpa */ + char is_dr[ISIS_LEVELS]; /* Are we level x DR ? */ uint8_t l1_desig_is[ISIS_SYS_ID_LEN + 1]; /* level-1 DR */ uint8_t l2_desig_is[ISIS_SYS_ID_LEN + 1]; /* level-2 DR */ - struct thread *t_refresh_pseudo_lsp[2]; /* refresh pseudo-node LSPs */ + struct thread *t_refresh_pseudo_lsp[ISIS_LEVELS]; /* refresh pseudo-node + LSPs */ }; struct isis_p2p_info { @@ -75,6 +79,7 @@ struct isis_circuit_arg { struct isis_circuit { int state; uint8_t circuit_id; /* l1/l2 bcast CircuitID */ + struct isis *isis; struct isis_area *area; /* back pointer to the area */ struct interface *interface; /* interface info from z */ int fd; /* IS-IS l1/2 socket */ @@ -84,10 +89,11 @@ struct isis_circuit { * Threads */ struct thread *t_read; - struct thread *t_send_csnp[2]; - struct thread *t_send_psnp[2]; + struct thread *t_send_csnp[ISIS_LEVELS]; + struct thread *t_send_psnp[ISIS_LEVELS]; struct isis_tx_queue *tx_queue; - struct isis_circuit_arg level_arg[2]; /* used as argument for threads */ + struct isis_circuit_arg + level_arg[ISIS_LEVELS]; /* used as argument for threads */ /* there is no real point in two streams, just for programming kicker */ int (*rx)(struct isis_circuit *circuit, uint8_t *ssnpa); @@ -105,7 +111,7 @@ struct isis_circuit { struct isis_bcast_info bc; struct isis_p2p_info p2p; } u; - uint8_t priority[2]; /* l1/2 IS configured priority */ + uint8_t priority[ISIS_LEVELS]; /* l1/2 IS configured priority */ int pad_hellos; /* add padding to Hello PDUs ? */ char ext_domain; /* externalDomain (boolean) */ int lsp_regenerate_pending[ISIS_LEVELS]; @@ -115,13 +121,13 @@ struct isis_circuit { struct isis_passwd passwd; /* Circuit rx/tx password */ int is_type; /* circuit is type == level of circuit * differentiated from circuit type (media) */ - uint32_t hello_interval[2]; /* hello-interval in seconds */ - uint16_t hello_multiplier[2]; /* hello-multiplier */ - uint16_t csnp_interval[2]; /* csnp-interval in seconds */ - uint16_t psnp_interval[2]; /* psnp-interval in seconds */ - uint8_t metric[2]; - uint32_t te_metric[2]; - struct mpls_te_circuit *mtc; /* MPLS-TE parameters */ + uint32_t hello_interval[ISIS_LEVELS]; /* hello-interval in seconds */ + uint16_t hello_multiplier[ISIS_LEVELS]; /* hello-multiplier */ + uint16_t csnp_interval[ISIS_LEVELS]; /* csnp-interval in seconds */ + uint16_t psnp_interval[ISIS_LEVELS]; /* psnp-interval in seconds */ + uint8_t metric[ISIS_LEVELS]; + uint32_t te_metric[ISIS_LEVELS]; + struct isis_ext_subtlvs *ext; /* Extended parameters (TE + Adj SID */ int ip_router; /* Route IP ? */ int is_passive; /* Is Passive ? */ struct list *mt_settings; /* IS-IS MT Settings */ @@ -129,7 +135,7 @@ struct isis_circuit { int ipv6_router; /* Route IPv6 ? */ struct list *ipv6_link; /* our link local IPv6 addresses */ struct list *ipv6_non_link; /* our non-link local IPv6 addresses */ - uint16_t upadjcount[2]; + uint16_t upadjcount[ISIS_LEVELS]; #define ISIS_CIRCUIT_FLAPPED_AFTER_SPF 0x01 uint8_t flags; bool disable_threeway_adj; @@ -141,9 +147,16 @@ struct isis_circuit { uint32_t init_failures; /* intialisationFailures */ uint32_t ctrl_pdus_rxed; /* controlPDUsReceived */ uint32_t ctrl_pdus_txed; /* controlPDUsSent */ - uint32_t - desig_changes[2]; /* lanLxDesignatedIntermediateSystemChanges */ + uint32_t desig_changes[ISIS_LEVELS]; /* lanLxDesignatedIntermediateSystemChanges + */ uint32_t rej_adjacencies; /* rejectedAdjacencies */ + /* + * Counters as in ietf-isis@2019-09-09.yang + */ + uint32_t id_len_mismatches; /* id-len-mismatch */ + uint32_t max_area_addr_mismatches; /* max-area-addresses-mismatch */ + uint32_t auth_type_failures; /*authentication-type-fails */ + uint32_t auth_failures; /* authentication-fails */ QOBJ_FIELDS }; @@ -199,8 +212,10 @@ ferr_r isis_circuit_passwd_hmac_md5_set(struct isis_circuit *circuit, int isis_circuit_mt_enabled_set(struct isis_circuit *circuit, uint16_t mtid, bool enabled); +#ifdef FABRICD DECLARE_HOOK(isis_circuit_config_write, (struct isis_circuit *circuit, struct vty *vty), (circuit, vty)) +#endif #endif /* _ZEBRA_ISIS_CIRCUIT_H */ diff --git a/isisd/isis_cli.c b/isisd/isis_cli.c index 0094852825..5a6dc84a27 100644 --- a/isisd/isis_cli.c +++ b/isisd/isis_cli.c @@ -32,7 +32,7 @@ #include "yang.h" #include "lib/linklist.h" #include "isisd/isisd.h" -#include "isisd/isis_cli.h" +#include "isisd/isis_nb.h" #include "isisd/isis_misc.h" #include "isisd/isis_circuit.h" #include "isisd/isis_csm.h" @@ -46,23 +46,33 @@ /* * XPath: /frr-isisd:isis/instance */ -DEFPY_NOSH(router_isis, router_isis_cmd, "router isis WORD$tag", - ROUTER_STR - "ISO IS-IS\n" - "ISO Routing area tag\n") +DEFPY_YANG_NOSH(router_isis, router_isis_cmd, + "router isis WORD$tag [vrf NAME$vrf_name]", + ROUTER_STR + "ISO IS-IS\n" + "ISO Routing area tag\n" VRF_CMD_HELP_STR) { int ret; char base_xpath[XPATH_MAXLEN]; + if (!vrf_name) + vrf_name = VRF_DEFAULT_NAME; + snprintf(base_xpath, XPATH_MAXLEN, - "/frr-isisd:isis/instance[area-tag='%s']", tag); + "/frr-isisd:isis/instance[area-tag='%s'][vrf='%s']", tag, + vrf_name); nb_cli_enqueue_change(vty, ".", NB_OP_CREATE, NULL); /* default value in yang for is-type is level-1, but in FRR * the first instance is assigned is-type level-1-2. We * need to make sure to set it in the yang model so that it * is consistent with what FRR sees. */ - if (listcount(isis->area_list) == 0) + + if (!im) { + return CMD_SUCCESS; + } + + if (listcount(im->isis) == 0) nb_cli_enqueue_change(vty, "./is-type", NB_OP_MODIFY, "level-1-2"); ret = nb_cli_apply_changes(vty, base_xpath); @@ -72,25 +82,30 @@ DEFPY_NOSH(router_isis, router_isis_cmd, "router isis WORD$tag", return ret; } -DEFPY(no_router_isis, no_router_isis_cmd, "no router isis WORD$tag", - NO_STR ROUTER_STR - "ISO IS-IS\n" - "ISO Routing area tag\n") +DEFPY_YANG(no_router_isis, no_router_isis_cmd, + "no router isis WORD$tag [vrf NAME$vrf_name]", + NO_STR ROUTER_STR + "ISO IS-IS\n" + "ISO Routing area tag\n" VRF_CMD_HELP_STR) { char temp_xpath[XPATH_MAXLEN]; struct listnode *node, *nnode; struct isis_circuit *circuit = NULL; struct isis_area *area = NULL; - if (!yang_dnode_exists(vty->candidate_config->dnode, - "/frr-isisd:isis/instance[area-tag='%s']", - tag)) { + if (!vrf_name) + vrf_name = VRF_DEFAULT_NAME; + + if (!yang_dnode_exists( + vty->candidate_config->dnode, + "/frr-isisd:isis/instance[area-tag='%s'][vrf='%s']", tag, + vrf_name)) { vty_out(vty, "ISIS area %s not found.\n", tag); return CMD_ERR_NOTHING_TODO; } nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, NULL); - area = isis_area_lookup(tag); + area = isis_area_lookup_by_vrf(tag, vrf_name); if (area && area->circuit_list && listcount(area->circuit_list)) { for (ALL_LIST_ELEMENTS(area->circuit_list, node, nnode, circuit)) { @@ -109,15 +124,23 @@ DEFPY(no_router_isis, no_router_isis_cmd, "no router isis WORD$tag", } return nb_cli_apply_changes( - vty, "/frr-isisd:isis/instance[area-tag='%s']", tag); + vty, "/frr-isisd:isis/instance[area-tag='%s'][vrf='%s']", tag, + vrf_name); } void cli_show_router_isis(struct vty *vty, struct lyd_node *dnode, bool show_defaults) { + const char *vrf = NULL; + + vrf = yang_dnode_get_string(dnode, "./vrf"); + vty_out(vty, "!\n"); - vty_out(vty, "router isis %s\n", + vty_out(vty, "router isis %s", yang_dnode_get_string(dnode, "./area-tag")); + if (!strmatch(vrf, VRF_DEFAULT_NAME)) + vty_out(vty, " vrf %s", yang_dnode_get_string(dnode, "./vrf")); + vty_out(vty, "\n"); } /* @@ -126,41 +149,66 @@ void cli_show_router_isis(struct vty *vty, struct lyd_node *dnode, * XPath: /frr-interface:lib/interface/frr-isisd:isis/ipv6-routing * XPath: /frr-isisd:isis/instance */ -DEFPY(ip_router_isis, ip_router_isis_cmd, "ip router isis WORD$tag", - "Interface Internet Protocol config commands\n" - "IP router interface commands\n" - "IS-IS routing protocol\n" - "Routing process tag\n") +DEFPY_YANG(ip_router_isis, ip_router_isis_cmd, + "ip router isis WORD$tag [vrf NAME$vrf_name]", + "Interface Internet Protocol config commands\n" + "IP router interface commands\n" + "IS-IS routing protocol\n" + "Routing process tag\n" VRF_CMD_HELP_STR) { char temp_xpath[XPATH_MAXLEN]; const char *circ_type; - struct isis_area *area; + struct isis_area *area = NULL; struct interface *ifp; + struct vrf *vrf; /* area will be created if it is not present. make sure the yang model * is synced with FRR and call the appropriate NB cb. */ - area = isis_area_lookup(tag); + + if (!im) { + return CMD_SUCCESS; + } + ifp = nb_running_get_entry(NULL, VTY_CURR_XPATH, false); + if (!vrf_name) { + if (ifp) { + if (ifp->vrf_id == VRF_DEFAULT) + vrf_name = VRF_DEFAULT_NAME; + else { + vrf = vrf_lookup_by_id(ifp->vrf_id); + if (vrf && !vrf_name) + vrf_name = vrf->name; + } + } else + vrf_name = VRF_DEFAULT_NAME; + } + + area = isis_area_lookup_by_vrf(tag, vrf_name); if (!area) { + isis_global_instance_create(vrf_name); snprintf(temp_xpath, XPATH_MAXLEN, - "/frr-isisd:isis/instance[area-tag='%s']", tag); + "/frr-isisd:isis/instance[area-tag='%s'][vrf='%s']", + tag, vrf_name); nb_cli_enqueue_change(vty, temp_xpath, NB_OP_CREATE, tag); - snprintf(temp_xpath, XPATH_MAXLEN, - "/frr-isisd:isis/instance[area-tag='%s']/is-type", - tag); - nb_cli_enqueue_change( - vty, temp_xpath, NB_OP_MODIFY, - listcount(isis->area_list) == 0 ? "level-1-2" : NULL); + snprintf( + temp_xpath, XPATH_MAXLEN, + "/frr-isisd:isis/instance[area-tag='%s'][vrf='%s']/is-type", + tag, vrf_name); + nb_cli_enqueue_change(vty, temp_xpath, NB_OP_MODIFY, + listcount(im->isis) == 0 ? "level-1-2" + : NULL); nb_cli_enqueue_change(vty, "./frr-isisd:isis", NB_OP_CREATE, NULL); nb_cli_enqueue_change(vty, "./frr-isisd:isis/area-tag", NB_OP_MODIFY, tag); + + nb_cli_enqueue_change(vty, "./frr-isisd:isis/vrf", NB_OP_MODIFY, + vrf_name); nb_cli_enqueue_change(vty, "./frr-isisd:isis/ipv4-routing", NB_OP_MODIFY, "true"); nb_cli_enqueue_change( vty, "./frr-isisd:isis/circuit-type", NB_OP_MODIFY, - listcount(isis->area_list) == 0 ? "level-1-2" - : "level-1"); + listcount(im->isis) == 0 ? "level-1-2" : "level-1"); } else { /* area exists, circuit type defaults to its area's is_type */ switch (area->is_type) { @@ -181,6 +229,9 @@ DEFPY(ip_router_isis, ip_router_isis_cmd, "ip router isis WORD$tag", NULL); nb_cli_enqueue_change(vty, "./frr-isisd:isis/area-tag", NB_OP_MODIFY, tag); + nb_cli_enqueue_change(vty, "./frr-isisd:isis/vrf", NB_OP_MODIFY, + vrf_name); + nb_cli_enqueue_change(vty, "./frr-isisd:isis/ipv4-routing", NB_OP_MODIFY, "true"); nb_cli_enqueue_change(vty, "./frr-isisd:isis/circuit-type", @@ -188,7 +239,6 @@ DEFPY(ip_router_isis, ip_router_isis_cmd, "ip router isis WORD$tag", } /* check if the interface is a loopback and if so set it as passive */ - ifp = nb_running_get_entry(NULL, VTY_CURR_XPATH, false); if (ifp && if_is_loopback(ifp)) nb_cli_enqueue_change(vty, "./frr-isisd:isis/passive", NB_OP_MODIFY, "true"); @@ -196,41 +246,66 @@ DEFPY(ip_router_isis, ip_router_isis_cmd, "ip router isis WORD$tag", return nb_cli_apply_changes(vty, NULL); } -DEFPY(ip6_router_isis, ip6_router_isis_cmd, "ipv6 router isis WORD$tag", - "Interface Internet Protocol config commands\n" - "IP router interface commands\n" - "IS-IS routing protocol\n" - "Routing process tag\n") +DEFPY_YANG(ip6_router_isis, ip6_router_isis_cmd, + "ipv6 router isis WORD$tag [vrf NAME$vrf_name]", + "Interface Internet Protocol config commands\n" + "IP router interface commands\n" + "IS-IS routing protocol\n" + "Routing process tag\n" VRF_CMD_HELP_STR) { char temp_xpath[XPATH_MAXLEN]; const char *circ_type; - struct isis_area *area; struct interface *ifp; + struct isis_area *area; + struct vrf *vrf; /* area will be created if it is not present. make sure the yang model * is synced with FRR and call the appropriate NB cb. */ - area = isis_area_lookup(tag); + + if (!im) + return CMD_SUCCESS; + + ifp = nb_running_get_entry(NULL, VTY_CURR_XPATH, false); + if (!vrf_name) { + if (ifp) { + if (ifp->vrf_id == VRF_DEFAULT) + vrf_name = VRF_DEFAULT_NAME; + else { + vrf = vrf_lookup_by_id(ifp->vrf_id); + if (vrf && !vrf_name) + vrf_name = vrf->name; + } + } else + vrf_name = VRF_DEFAULT_NAME; + } + + area = isis_area_lookup_by_vrf(tag, vrf_name); if (!area) { + isis_global_instance_create(vrf_name); snprintf(temp_xpath, XPATH_MAXLEN, - "/frr-isisd:isis/instance[area-tag='%s']", tag); + "/frr-isisd:isis/instance[area-tag='%s'][vrf='%s']", + tag, vrf_name); nb_cli_enqueue_change(vty, temp_xpath, NB_OP_CREATE, tag); - snprintf(temp_xpath, XPATH_MAXLEN, - "/frr-isisd:isis/instance[area-tag='%s']/is-type", - tag); - nb_cli_enqueue_change( - vty, temp_xpath, NB_OP_MODIFY, - listcount(isis->area_list) == 0 ? "level-1-2" : NULL); + snprintf( + temp_xpath, XPATH_MAXLEN, + "/frr-isisd:isis/instance[area-tag='%s'][vrf='%s']/is-type", + tag, vrf_name); + nb_cli_enqueue_change(vty, temp_xpath, NB_OP_MODIFY, + listcount(im->isis) == 0 ? "level-1-2" + : NULL); nb_cli_enqueue_change(vty, "./frr-isisd:isis", NB_OP_CREATE, NULL); nb_cli_enqueue_change(vty, "./frr-isisd:isis/area-tag", NB_OP_MODIFY, tag); + nb_cli_enqueue_change(vty, "./frr-isisd:isis/vrf", NB_OP_MODIFY, + vrf_name); + nb_cli_enqueue_change(vty, "./frr-isisd:isis/ipv6-routing", NB_OP_MODIFY, "true"); nb_cli_enqueue_change( vty, "./frr-isisd:isis/circuit-type", NB_OP_MODIFY, - listcount(isis->area_list) == 0 ? "level-1-2" - : "level-1"); + listcount(im->isis) == 0 ? "level-1-2" : "level-1"); } else { /* area exists, circuit type defaults to its area's is_type */ switch (area->is_type) { @@ -251,6 +326,8 @@ DEFPY(ip6_router_isis, ip6_router_isis_cmd, "ipv6 router isis WORD$tag", NULL); nb_cli_enqueue_change(vty, "./frr-isisd:isis/area-tag", NB_OP_MODIFY, tag); + nb_cli_enqueue_change(vty, "./frr-isisd:isis/vrf", NB_OP_MODIFY, + vrf_name); nb_cli_enqueue_change(vty, "./frr-isisd:isis/ipv6-routing", NB_OP_MODIFY, "true"); nb_cli_enqueue_change(vty, "./frr-isisd:isis/circuit-type", @@ -258,7 +335,6 @@ DEFPY(ip6_router_isis, ip6_router_isis_cmd, "ipv6 router isis WORD$tag", } /* check if the interface is a loopback and if so set it as passive */ - ifp = nb_running_get_entry(NULL, VTY_CURR_XPATH, false); if (ifp && if_is_loopback(ifp)) nb_cli_enqueue_change(vty, "./frr-isisd:isis/passive", NB_OP_MODIFY, "true"); @@ -266,14 +342,15 @@ DEFPY(ip6_router_isis, ip6_router_isis_cmd, "ipv6 router isis WORD$tag", return nb_cli_apply_changes(vty, NULL); } -DEFPY(no_ip_router_isis, no_ip_router_isis_cmd, - "no $ip router isis [WORD]$tag", - NO_STR - "Interface Internet Protocol config commands\n" - "IP router interface commands\n" - "IP router interface commands\n" - "IS-IS routing protocol\n" - "Routing process tag\n") +DEFPY_YANG(no_ip_router_isis, no_ip_router_isis_cmd, + "no $ip router isis [WORD]$tag [vrf NAME$vrf_name]", + NO_STR + "Interface Internet Protocol config commands\n" + "IP router interface commands\n" + "IP router interface commands\n" + "IS-IS routing protocol\n" + "Routing process tag\n" + VRF_CMD_HELP_STR) { const struct lyd_node *dnode; @@ -309,25 +386,106 @@ DEFPY(no_ip_router_isis, no_ip_router_isis_cmd, void cli_show_ip_isis_ipv4(struct vty *vty, struct lyd_node *dnode, bool show_defaults) { + const char *vrf; + + vrf = yang_dnode_get_string(dnode, "../vrf"); + if (!yang_dnode_get_bool(dnode, NULL)) vty_out(vty, " no"); - vty_out(vty, " ip router isis %s\n", + vty_out(vty, " ip router isis %s", yang_dnode_get_string(dnode, "../area-tag")); + if (!strmatch(vrf, VRF_DEFAULT_NAME)) + vty_out(vty, " vrf %s", vrf); + vty_out(vty, "\n"); } void cli_show_ip_isis_ipv6(struct vty *vty, struct lyd_node *dnode, bool show_defaults) { + const char *vrf; + + vrf = yang_dnode_get_string(dnode, "../vrf"); + if (!yang_dnode_get_bool(dnode, NULL)) vty_out(vty, " no"); - vty_out(vty, " ipv6 router isis %s\n", + vty_out(vty, " ipv6 router isis %s", yang_dnode_get_string(dnode, "../area-tag")); + if (!strmatch(vrf, VRF_DEFAULT_NAME)) + vty_out(vty, " vrf %s", vrf); + vty_out(vty, "\n"); +} + +/* + * XPath: /frr-interface:lib/interface/frr-isisd:isis/bfd-monitoring + */ +DEFPY_YANG(isis_bfd, + isis_bfd_cmd, + "[no] isis bfd", + NO_STR PROTO_HELP + "Enable BFD support\n") +{ + const struct lyd_node *dnode; + + dnode = yang_dnode_get(vty->candidate_config->dnode, + "%s/frr-isisd:isis", VTY_CURR_XPATH); + if (dnode == NULL) { + vty_out(vty, "ISIS is not enabled on this circuit\n"); + return CMD_SUCCESS; + } + + nb_cli_enqueue_change(vty, "./frr-isisd:isis/bfd-monitoring/enabled", + NB_OP_MODIFY, no ? "false" : "true"); + + return nb_cli_apply_changes(vty, NULL); +} + +/* + * XPath: /frr-interface:lib/interface/frr-isisd:isis/bfd-monitoring/profile + */ +DEFPY_YANG(isis_bfd_profile, + isis_bfd_profile_cmd, + "[no] isis bfd profile WORD", + NO_STR PROTO_HELP + "Enable BFD support\n" + "Use a pre-configured profile\n" + "Profile name\n") +{ + const struct lyd_node *dnode; + + dnode = yang_dnode_get(vty->candidate_config->dnode, + "%s/frr-isisd:isis", VTY_CURR_XPATH); + if (dnode == NULL) { + vty_out(vty, "ISIS is not enabled on this circuit\n"); + return CMD_SUCCESS; + } + + nb_cli_enqueue_change(vty, "./frr-isisd:isis/bfd-monitoring/profile", + NB_OP_MODIFY, no ? NULL : profile); + + return nb_cli_apply_changes(vty, NULL); +} + +void cli_show_ip_isis_bfd_monitoring(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + const char *profile; + + if (!yang_dnode_get_bool(dnode, "./enabled")) + vty_out(vty, " no"); + + vty_out(vty, " isis bfd\n"); + + if (yang_dnode_exists(dnode, "./profile")) { + profile = yang_dnode_get_string(dnode, "./profile"); + if (profile[0] != '\0') + vty_out(vty, " isis bfd profile %s\n", profile); + } } /* * XPath: /frr-isisd:isis/instance/area-address */ -DEFPY(net, net_cmd, "[no] net WORD", +DEFPY_YANG(net, net_cmd, "[no] net WORD", "Remove an existing Network Entity Title for this process\n" "A Network Entity Title for this process (OSI only)\n" "XX.XXXX. ... .XXX.XX Network entity title (NET)\n") @@ -347,7 +505,7 @@ void cli_show_isis_area_address(struct vty *vty, struct lyd_node *dnode, /* * XPath: /frr-isisd:isis/instance/is-type */ -DEFPY(is_type, is_type_cmd, "is-type $level", +DEFPY_YANG(is_type, is_type_cmd, "is-type $level", "IS Level for this routing process (OSI only)\n" "Act as a station router only\n" "Act as both a station router and an area router\n" @@ -360,7 +518,7 @@ DEFPY(is_type, is_type_cmd, "is-type $level", return nb_cli_apply_changes(vty, NULL); } -DEFPY(no_is_type, no_is_type_cmd, +DEFPY_YANG(no_is_type, no_is_type_cmd, "no is-type []", NO_STR "IS Level for this routing process (OSI only)\n" @@ -368,21 +526,7 @@ DEFPY(no_is_type, no_is_type_cmd, "Act as both a station router and an area router\n" "Act as an area router only\n") { - const char *value = NULL; - struct isis_area *area; - - area = nb_running_get_entry(NULL, VTY_CURR_XPATH, false); - - /* - * Put the is-type back to defaults: - * - level-1-2 on first area - * - level-1 for the rest - */ - if (area && listgetdata(listhead(isis->area_list)) == area) - value = "level-1-2"; - else - value = NULL; - nb_cli_enqueue_change(vty, "./is-type", NB_OP_MODIFY, value); + nb_cli_enqueue_change(vty, "./is-type", NB_OP_MODIFY, NULL); return nb_cli_apply_changes(vty, NULL); } @@ -408,7 +552,7 @@ void cli_show_isis_is_type(struct vty *vty, struct lyd_node *dnode, /* * XPath: /frr-isisd:isis/instance/dynamic-hostname */ -DEFPY(dynamic_hostname, dynamic_hostname_cmd, "[no] hostname dynamic", +DEFPY_YANG(dynamic_hostname, dynamic_hostname_cmd, "[no] hostname dynamic", NO_STR "Dynamic hostname for IS-IS\n" "Dynamic hostname\n") @@ -431,7 +575,7 @@ void cli_show_isis_dynamic_hostname(struct vty *vty, struct lyd_node *dnode, /* * XPath: /frr-isisd:isis/instance/overload */ -DEFPY(set_overload_bit, set_overload_bit_cmd, "[no] set-overload-bit", +DEFPY_YANG(set_overload_bit, set_overload_bit_cmd, "[no] set-overload-bit", "Reset overload bit to accept transit traffic\n" "Set overload bit to avoid any transit traffic\n") { @@ -452,7 +596,7 @@ void cli_show_isis_overload(struct vty *vty, struct lyd_node *dnode, /* * XPath: /frr-isisd:isis/instance/attached */ -DEFPY(set_attached_bit, set_attached_bit_cmd, "[no] set-attached-bit", +DEFPY_YANG(set_attached_bit, set_attached_bit_cmd, "[no] set-attached-bit", "Reset attached bit\n" "Set attached bit to identify as L1/L2 router for inter-area traffic\n") { @@ -473,7 +617,7 @@ void cli_show_isis_attached(struct vty *vty, struct lyd_node *dnode, /* * XPath: /frr-isisd:isis/instance/metric-style */ -DEFPY(metric_style, metric_style_cmd, +DEFPY_YANG(metric_style, metric_style_cmd, "metric-style $style", "Use old-style (ISO 10589) or new-style packet formats\n" "Use old style of TLVs with narrow metric\n" @@ -485,7 +629,7 @@ DEFPY(metric_style, metric_style_cmd, return nb_cli_apply_changes(vty, NULL); } -DEFPY(no_metric_style, no_metric_style_cmd, +DEFPY_YANG(no_metric_style, no_metric_style_cmd, "no metric-style [narrow|transition|wide]", NO_STR "Use old-style (ISO 10589) or new-style packet formats\n" @@ -493,7 +637,7 @@ DEFPY(no_metric_style, no_metric_style_cmd, "Send and accept both styles of TLVs during transition\n" "Use new style of TLVs to carry wider metric\n") { - nb_cli_enqueue_change(vty, "./metric-style", NB_OP_MODIFY, "narrow"); + nb_cli_enqueue_change(vty, "./metric-style", NB_OP_MODIFY, NULL); return nb_cli_apply_changes(vty, NULL); } @@ -519,7 +663,7 @@ void cli_show_isis_metric_style(struct vty *vty, struct lyd_node *dnode, /* * XPath: /frr-isisd:isis/instance/area-password */ -DEFPY(area_passwd, area_passwd_cmd, +DEFPY_YANG(area_passwd, area_passwd_cmd, "area-password $pwd_type WORD$pwd [authenticate snp $snp]", "Configure the authentication password for an area\n" "Clear-text authentication type\n" @@ -558,7 +702,7 @@ void cli_show_isis_area_pwd(struct vty *vty, struct lyd_node *dnode, /* * XPath: /frr-isisd:isis/instance/domain-password */ -DEFPY(domain_passwd, domain_passwd_cmd, +DEFPY_YANG(domain_passwd, domain_passwd_cmd, "domain-password $pwd_type WORD$pwd [authenticate snp $snp]", "Set the authentication password for a routing domain\n" "Clear-text authentication type\n" @@ -580,7 +724,7 @@ DEFPY(domain_passwd, domain_passwd_cmd, return nb_cli_apply_changes(vty, NULL); } -DEFPY(no_area_passwd, no_area_passwd_cmd, +DEFPY_YANG(no_area_passwd, no_area_passwd_cmd, "no $cmd", NO_STR "Configure the authentication password for an area\n" @@ -606,9 +750,10 @@ void cli_show_isis_domain_pwd(struct vty *vty, struct lyd_node *dnode, } /* - * XPath: /frr-isisd:isis/instance/lsp/generation-interval + * XPath: /frr-isisd:isis/instance/lsp/timers/level-1/generation-interval + * XPath: /frr-isisd:isis/instance/lsp/timers/level-2/generation-interval */ -DEFPY(lsp_gen_interval, lsp_gen_interval_cmd, +DEFPY_YANG(lsp_gen_interval, lsp_gen_interval_cmd, "lsp-gen-interval [level-1|level-2]$level (1-120)$val", "Minimum interval between regenerating same LSP\n" "Set interval for level 1 only\n" @@ -616,16 +761,18 @@ DEFPY(lsp_gen_interval, lsp_gen_interval_cmd, "Minimum interval in seconds\n") { if (!level || strmatch(level, "level-1")) - nb_cli_enqueue_change(vty, "./lsp/generation-interval/level-1", - NB_OP_MODIFY, val_str); + nb_cli_enqueue_change( + vty, "./lsp/timers/level-1/generation-interval", + NB_OP_MODIFY, val_str); if (!level || strmatch(level, "level-2")) - nb_cli_enqueue_change(vty, "./lsp/generation-interval/level-2", - NB_OP_MODIFY, val_str); + nb_cli_enqueue_change( + vty, "./lsp/timers/level-2/generation-interval", + NB_OP_MODIFY, val_str); return nb_cli_apply_changes(vty, NULL); } -DEFPY(no_lsp_gen_interval, no_lsp_gen_interval_cmd, +DEFPY_YANG(no_lsp_gen_interval, no_lsp_gen_interval_cmd, "no lsp-gen-interval [level-1|level-2]$level [(1-120)]", NO_STR "Minimum interval between regenerating same LSP\n" @@ -634,33 +781,22 @@ DEFPY(no_lsp_gen_interval, no_lsp_gen_interval_cmd, "Minimum interval in seconds\n") { if (!level || strmatch(level, "level-1")) - nb_cli_enqueue_change(vty, "./lsp/generation-interval/level-1", - NB_OP_MODIFY, NULL); + nb_cli_enqueue_change( + vty, "./lsp/timers/level-1/generation-interval", + NB_OP_MODIFY, NULL); if (!level || strmatch(level, "level-2")) - nb_cli_enqueue_change(vty, "./lsp/generation-interval/level-2", - NB_OP_MODIFY, NULL); + nb_cli_enqueue_change( + vty, "./lsp/timers/level-2/generation-interval", + NB_OP_MODIFY, NULL); return nb_cli_apply_changes(vty, NULL); } -void cli_show_isis_lsp_gen_interval(struct vty *vty, struct lyd_node *dnode, - bool show_defaults) -{ - const char *l1 = yang_dnode_get_string(dnode, "./level-1"); - const char *l2 = yang_dnode_get_string(dnode, "./level-2"); - - if (strmatch(l1, l2)) - vty_out(vty, " lsp-gen-interval %s\n", l1); - else { - vty_out(vty, " lsp-gen-interval level-1 %s\n", l1); - vty_out(vty, " lsp-gen-interval level-2 %s\n", l2); - } -} - /* - * XPath: /frr-isisd:isis/instance/lsp/refresh-interval + * XPath: /frr-isisd:isis/instance/lsp/timers/level-1/refresh-interval + * XPath: /frr-isisd:isis/instance/lsp/timers/level-2/refresh-interval */ -DEFPY(lsp_refresh_interval, lsp_refresh_interval_cmd, +DEFPY_YANG(lsp_refresh_interval, lsp_refresh_interval_cmd, "lsp-refresh-interval [level-1|level-2]$level (1-65235)$val", "LSP refresh interval\n" "LSP refresh interval for Level 1 only\n" @@ -668,16 +804,18 @@ DEFPY(lsp_refresh_interval, lsp_refresh_interval_cmd, "LSP refresh interval in seconds\n") { if (!level || strmatch(level, "level-1")) - nb_cli_enqueue_change(vty, "./lsp/refresh-interval/level-1", + nb_cli_enqueue_change(vty, + "./lsp/timers/level-1/refresh-interval", NB_OP_MODIFY, val_str); if (!level || strmatch(level, "level-2")) - nb_cli_enqueue_change(vty, "./lsp/refresh-interval/level-2", + nb_cli_enqueue_change(vty, + "./lsp/timers/level-2/refresh-interval", NB_OP_MODIFY, val_str); return nb_cli_apply_changes(vty, NULL); } -DEFPY(no_lsp_refresh_interval, no_lsp_refresh_interval_cmd, +DEFPY_YANG(no_lsp_refresh_interval, no_lsp_refresh_interval_cmd, "no lsp-refresh-interval [level-1|level-2]$level [(1-65235)]", NO_STR "LSP refresh interval\n" @@ -686,33 +824,23 @@ DEFPY(no_lsp_refresh_interval, no_lsp_refresh_interval_cmd, "LSP refresh interval in seconds\n") { if (!level || strmatch(level, "level-1")) - nb_cli_enqueue_change(vty, "./lsp/refresh-interval/level-1", + nb_cli_enqueue_change(vty, + "./lsp/timers/level-1/refresh-interval", NB_OP_MODIFY, NULL); if (!level || strmatch(level, "level-2")) - nb_cli_enqueue_change(vty, "./lsp/refresh-interval/level-2", + nb_cli_enqueue_change(vty, + "./lsp/timers/level-2/refresh-interval", NB_OP_MODIFY, NULL); return nb_cli_apply_changes(vty, NULL); } -void cli_show_isis_lsp_ref_interval(struct vty *vty, struct lyd_node *dnode, - bool show_defaults) -{ - const char *l1 = yang_dnode_get_string(dnode, "./level-1"); - const char *l2 = yang_dnode_get_string(dnode, "./level-2"); - - if (strmatch(l1, l2)) - vty_out(vty, " lsp-refresh-interval %s\n", l1); - else { - vty_out(vty, " lsp-refresh-interval level-1 %s\n", l1); - vty_out(vty, " lsp-refresh-interval level-2 %s\n", l2); - } -} - /* - * XPath: /frr-isisd:isis/instance/lsp/maximum-lifetime + * XPath: /frr-isisd:isis/instance/lsp/timers/level-1/maximum-lifetime + * XPath: /frr-isisd:isis/instance/lsp/timers/level-1/maximum-lifetime */ -DEFPY(max_lsp_lifetime, max_lsp_lifetime_cmd, + +DEFPY_YANG(max_lsp_lifetime, max_lsp_lifetime_cmd, "max-lsp-lifetime [level-1|level-2]$level (350-65535)$val", "Maximum LSP lifetime\n" "Maximum LSP lifetime for Level 1 only\n" @@ -720,16 +848,18 @@ DEFPY(max_lsp_lifetime, max_lsp_lifetime_cmd, "LSP lifetime in seconds\n") { if (!level || strmatch(level, "level-1")) - nb_cli_enqueue_change(vty, "./lsp/maximum-lifetime/level-1", + nb_cli_enqueue_change(vty, + "./lsp/timers/level-1/maximum-lifetime", NB_OP_MODIFY, val_str); if (!level || strmatch(level, "level-2")) - nb_cli_enqueue_change(vty, "./lsp/maximum-lifetime/level-2", + nb_cli_enqueue_change(vty, + "./lsp/timers/level-2/maximum-lifetime", NB_OP_MODIFY, val_str); return nb_cli_apply_changes(vty, NULL); } -DEFPY(no_max_lsp_lifetime, no_max_lsp_lifetime_cmd, +DEFPY_YANG(no_max_lsp_lifetime, no_max_lsp_lifetime_cmd, "no max-lsp-lifetime [level-1|level-2]$level [(350-65535)]", NO_STR "Maximum LSP lifetime\n" @@ -738,33 +868,132 @@ DEFPY(no_max_lsp_lifetime, no_max_lsp_lifetime_cmd, "LSP lifetime in seconds\n") { if (!level || strmatch(level, "level-1")) - nb_cli_enqueue_change(vty, "./lsp/maximum-lifetime/level-1", + nb_cli_enqueue_change(vty, + "./lsp/timers/level-1/maximum-lifetime", NB_OP_MODIFY, NULL); if (!level || strmatch(level, "level-2")) - nb_cli_enqueue_change(vty, "./lsp/maximum-lifetime/level-2", + nb_cli_enqueue_change(vty, + "./lsp/timers/level-2/maximum-lifetime", NB_OP_MODIFY, NULL); return nb_cli_apply_changes(vty, NULL); } -void cli_show_isis_lsp_max_lifetime(struct vty *vty, struct lyd_node *dnode, - bool show_defaults) +/* unified LSP timers command + * XPath: /frr-isisd:isis/instance/lsp/timers + */ + +DEFPY_YANG(lsp_timers, lsp_timers_cmd, + "lsp-timers [level-1|level-2]$level gen-interval (1-120)$gen refresh-interval (1-65235)$refresh max-lifetime (350-65535)$lifetime", + "LSP-related timers\n" + "LSP-related timers for Level 1 only\n" + "LSP-related timers for Level 2 only\n" + "Minimum interval between regenerating same LSP\n" + "Generation interval in seconds\n" + "LSP refresh interval\n" + "LSP refresh interval in seconds\n" + "Maximum LSP lifetime\n" + "Maximum LSP lifetime in seconds\n") { - const char *l1 = yang_dnode_get_string(dnode, "./level-1"); - const char *l2 = yang_dnode_get_string(dnode, "./level-2"); + if (!level || strmatch(level, "level-1")) { + nb_cli_enqueue_change( + vty, "./lsp/timers/level-1/generation-interval", + NB_OP_MODIFY, gen_str); + nb_cli_enqueue_change(vty, + "./lsp/timers/level-1/refresh-interval", + NB_OP_MODIFY, refresh_str); + nb_cli_enqueue_change(vty, + "./lsp/timers/level-1/maximum-lifetime", + NB_OP_MODIFY, lifetime_str); + } + if (!level || strmatch(level, "level-2")) { + nb_cli_enqueue_change( + vty, "./lsp/timers/level-2/generation-interval", + NB_OP_MODIFY, gen_str); + nb_cli_enqueue_change(vty, + "./lsp/timers/level-2/refresh-interval", + NB_OP_MODIFY, refresh_str); + nb_cli_enqueue_change(vty, + "./lsp/timers/level-2/maximum-lifetime", + NB_OP_MODIFY, lifetime_str); + } - if (strmatch(l1, l2)) - vty_out(vty, " max-lsp-lifetime %s\n", l1); + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG(no_lsp_timers, no_lsp_timers_cmd, + "no lsp-timers [level-1|level-2]$level [gen-interval (1-120) refresh-interval (1-65235) max-lifetime (350-65535)]", + NO_STR + "LSP-related timers\n" + "LSP-related timers for Level 1 only\n" + "LSP-related timers for Level 2 only\n" + "Minimum interval between regenerating same LSP\n" + "Generation interval in seconds\n" + "LSP refresh interval\n" + "LSP refresh interval in seconds\n" + "Maximum LSP lifetime\n" + "Maximum LSP lifetime in seconds\n") +{ + if (!level || strmatch(level, "level-1")) { + nb_cli_enqueue_change( + vty, "./lsp/timers/level-1/generation-interval", + NB_OP_MODIFY, NULL); + nb_cli_enqueue_change(vty, + "./lsp/timers/level-1/refresh-interval", + NB_OP_MODIFY, NULL); + nb_cli_enqueue_change(vty, + "./lsp/timers/level-1/maximum-lifetime", + NB_OP_MODIFY, NULL); + } + if (!level || strmatch(level, "level-2")) { + nb_cli_enqueue_change( + vty, "./lsp/timers/level-2/generation-interval", + NB_OP_MODIFY, NULL); + nb_cli_enqueue_change(vty, + "./lsp/timers/level-2/refresh-interval", + NB_OP_MODIFY, NULL); + nb_cli_enqueue_change(vty, + "./lsp/timers/level-2/maximum-lifetime", + NB_OP_MODIFY, NULL); + } + + return nb_cli_apply_changes(vty, NULL); +} + +void cli_show_isis_lsp_timers(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + const char *l1_refresh = + yang_dnode_get_string(dnode, "./level-1/refresh-interval"); + const char *l2_refresh = + yang_dnode_get_string(dnode, "./level-2/refresh-interval"); + const char *l1_lifetime = + yang_dnode_get_string(dnode, "./level-1/maximum-lifetime"); + const char *l2_lifetime = + yang_dnode_get_string(dnode, "./level-2/maximum-lifetime"); + const char *l1_gen = + yang_dnode_get_string(dnode, "./level-1/generation-interval"); + const char *l2_gen = + yang_dnode_get_string(dnode, "./level-2/generation-interval"); + if (strmatch(l1_refresh, l2_refresh) + && strmatch(l1_lifetime, l2_lifetime) && strmatch(l1_gen, l2_gen)) + vty_out(vty, + " lsp-timers gen-interval %s refresh-interval %s max-lifetime %s\n", + l1_gen, l1_refresh, l1_lifetime); else { - vty_out(vty, " max-lsp-lifetime level-1 %s\n", l1); - vty_out(vty, " max-lsp-lifetime level-2 %s\n", l2); + vty_out(vty, + " lsp-timers level-1 gen-interval %s refresh-interval %s max-lifetime %s\n", + l1_gen, l1_refresh, l1_lifetime); + vty_out(vty, + " lsp-timers level-2 gen-interval %s refresh-interval %s max-lifetime %s\n", + l2_gen, l2_refresh, l2_lifetime); } } /* * XPath: /frr-isisd:isis/instance/lsp/mtu */ -DEFPY(area_lsp_mtu, area_lsp_mtu_cmd, "lsp-mtu (128-4352)$val", +DEFPY_YANG(area_lsp_mtu, area_lsp_mtu_cmd, "lsp-mtu (128-4352)$val", "Configure the maximum size of generated LSPs\n" "Maximum size of generated LSPs\n") { @@ -773,7 +1002,7 @@ DEFPY(area_lsp_mtu, area_lsp_mtu_cmd, "lsp-mtu (128-4352)$val", return nb_cli_apply_changes(vty, NULL); } -DEFPY(no_area_lsp_mtu, no_area_lsp_mtu_cmd, "no lsp-mtu [(128-4352)]", +DEFPY_YANG(no_area_lsp_mtu, no_area_lsp_mtu_cmd, "no lsp-mtu [(128-4352)]", NO_STR "Configure the maximum size of generated LSPs\n" "Maximum size of generated LSPs\n") @@ -792,7 +1021,7 @@ void cli_show_isis_lsp_mtu(struct vty *vty, struct lyd_node *dnode, /* * XPath: /frr-isisd:isis/instance/spf/minimum-interval */ -DEFPY(spf_interval, spf_interval_cmd, +DEFPY_YANG(spf_interval, spf_interval_cmd, "spf-interval [level-1|level-2]$level (1-120)$val", "Minimum interval between SPF calculations\n" "Set interval for level 1 only\n" @@ -809,7 +1038,7 @@ DEFPY(spf_interval, spf_interval_cmd, return nb_cli_apply_changes(vty, NULL); } -DEFPY(no_spf_interval, no_spf_interval_cmd, +DEFPY_YANG(no_spf_interval, no_spf_interval_cmd, "no spf-interval [level-1|level-2]$level [(1-120)]", NO_STR "Minimum interval between SPF calculations\n" @@ -844,7 +1073,7 @@ void cli_show_isis_spf_min_interval(struct vty *vty, struct lyd_node *dnode, /* * XPath: /frr-isisd:isis/instance/spf/ietf-backoff-delay */ -DEFPY(spf_delay_ietf, spf_delay_ietf_cmd, +DEFPY_YANG(spf_delay_ietf, spf_delay_ietf_cmd, "spf-delay-ietf init-delay (0-60000) short-delay (0-60000) long-delay (0-60000) holddown (0-60000) time-to-learn (0-60000)", "IETF SPF delay algorithm\n" "Delay used while in QUIET state\n" @@ -874,7 +1103,7 @@ DEFPY(spf_delay_ietf, spf_delay_ietf_cmd, return nb_cli_apply_changes(vty, NULL); } -DEFPY(no_spf_delay_ietf, no_spf_delay_ietf_cmd, +DEFPY_YANG(no_spf_delay_ietf, no_spf_delay_ietf_cmd, "no spf-delay-ietf [init-delay (0-60000) short-delay (0-60000) long-delay (0-60000) holddown (0-60000) time-to-learn (0-60000)]", NO_STR "IETF SPF delay algorithm\n" @@ -910,7 +1139,7 @@ void cli_show_isis_spf_ietf_backoff(struct vty *vty, struct lyd_node *dnode, /* * XPath: /frr-isisd:isis/instance/purge-originator */ -DEFPY(area_purge_originator, area_purge_originator_cmd, "[no] purge-originator", +DEFPY_YANG(area_purge_originator, area_purge_originator_cmd, "[no] purge-originator", NO_STR "Use the RFC 6232 purge-originator\n") { nb_cli_enqueue_change(vty, "./purge-originator", NB_OP_MODIFY, @@ -930,7 +1159,7 @@ void cli_show_isis_purge_origin(struct vty *vty, struct lyd_node *dnode, /* * XPath: /frr-isisd:isis/instance/mpls-te */ -DEFPY(isis_mpls_te_on, isis_mpls_te_on_cmd, "mpls-te on", +DEFPY_YANG(isis_mpls_te_on, isis_mpls_te_on_cmd, "mpls-te on", MPLS_TE_STR "Enable the MPLS-TE functionality\n") { nb_cli_enqueue_change(vty, "./mpls-te", NB_OP_CREATE, @@ -939,7 +1168,7 @@ DEFPY(isis_mpls_te_on, isis_mpls_te_on_cmd, "mpls-te on", return nb_cli_apply_changes(vty, NULL); } -DEFPY(no_isis_mpls_te_on, no_isis_mpls_te_on_cmd, "no mpls-te [on]", +DEFPY_YANG(no_isis_mpls_te_on, no_isis_mpls_te_on_cmd, "no mpls-te [on]", NO_STR "Disable the MPLS-TE functionality\n" "Disable the MPLS-TE functionality\n") @@ -959,7 +1188,7 @@ void cli_show_isis_mpls_te(struct vty *vty, struct lyd_node *dnode, /* * XPath: /frr-isisd:isis/instance/mpls-te/router-address */ -DEFPY(isis_mpls_te_router_addr, isis_mpls_te_router_addr_cmd, +DEFPY_YANG(isis_mpls_te_router_addr, isis_mpls_te_router_addr_cmd, "mpls-te router-address A.B.C.D", MPLS_TE_STR "Stable IP address of the advertising router\n" @@ -971,7 +1200,7 @@ DEFPY(isis_mpls_te_router_addr, isis_mpls_te_router_addr_cmd, return nb_cli_apply_changes(vty, NULL); } -DEFPY(no_isis_mpls_te_router_addr, no_isis_mpls_te_router_addr_cmd, +DEFPY_YANG(no_isis_mpls_te_router_addr, no_isis_mpls_te_router_addr_cmd, "no mpls-te router-address [A.B.C.D]", NO_STR MPLS_TE_STR "Delete IP address of the advertising router\n" @@ -990,7 +1219,7 @@ void cli_show_isis_mpls_te_router_addr(struct vty *vty, struct lyd_node *dnode, yang_dnode_get_string(dnode, NULL)); } -DEFPY(isis_mpls_te_inter_as, isis_mpls_te_inter_as_cmd, +DEFPY_YANG(isis_mpls_te_inter_as, isis_mpls_te_inter_as_cmd, "[no] mpls-te inter-as [level-1|level-1-2|level-2-only]", NO_STR MPLS_TE_STR "Configure MPLS-TE Inter-AS support\n" @@ -1005,10 +1234,8 @@ DEFPY(isis_mpls_te_inter_as, isis_mpls_te_inter_as_cmd, /* * XPath: /frr-isisd:isis/instance/default-information-originate */ -DEFPY(isis_default_originate, isis_default_originate_cmd, - "[no] default-information originate $ip" - " $level [always]$always" - " [{metric (0-16777215)$metric|route-map WORD$rmap}]", +DEFPY_YANG(isis_default_originate, isis_default_originate_cmd, + "[no] default-information originate $ip $level [always]$always [{metric (0-16777215)$metric|route-map WORD$rmap}]", NO_STR "Control distribution of default information\n" "Distribute a default route\n" @@ -1018,7 +1245,7 @@ DEFPY(isis_default_originate, isis_default_originate_cmd, "Distribute default route into level-2\n" "Always advertise default route\n" "Metric for default route\n" - "ISIS default metric\n" + "IS-IS default metric\n" "Route map reference\n" "Pointer to route-map entries\n") { @@ -1083,18 +1310,16 @@ void cli_show_isis_def_origin_ipv6(struct vty *vty, struct lyd_node *dnode, /* * XPath: /frr-isisd:isis/instance/redistribute */ -DEFPY(isis_redistribute, isis_redistribute_cmd, +DEFPY_YANG(isis_redistribute, isis_redistribute_cmd, "[no] redistribute $ip " PROTO_REDIST_STR - "$proto" - " $level" - " [{metric (0-16777215)|route-map WORD}]", + "$proto $level [{metric (0-16777215)|route-map WORD}]", NO_STR REDIST_STR "Redistribute IPv4 routes\n" "Redistribute IPv6 routes\n" PROTO_REDIST_HELP "Redistribute into level-1\n" "Redistribute into level-2\n" "Metric for redistributed routes\n" - "ISIS default metric\n" + "IS-IS default metric\n" "Route map reference\n" "Pointer to route-map entries\n") { @@ -1144,16 +1369,8 @@ void cli_show_isis_redistribute_ipv6(struct vty *vty, struct lyd_node *dnode, /* * XPath: /frr-isisd:isis/instance/multi-topology */ -DEFPY(isis_topology, isis_topology_cmd, - "[no] topology " - "$topology " - "[overload]$overload", +DEFPY_YANG(isis_topology, isis_topology_cmd, + "[no] topology $topology [overload]$overload", NO_STR "Configure IS-IS topologies\n" "IPv4 unicast topology\n" @@ -1250,10 +1467,263 @@ void cli_show_isis_mt_ipv6_dstsrc(struct vty *vty, struct lyd_node *dnode, vty_out(vty, "\n"); } +/* + * XPath: /frr-isisd:isis/instance/segment-routing/enabled + */ +DEFPY_YANG (isis_sr_enable, + isis_sr_enable_cmd, + "segment-routing on", + SR_STR + "Enable Segment Routing\n") +{ + nb_cli_enqueue_change(vty, "./segment-routing/enabled", NB_OP_MODIFY, + "true"); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG (no_isis_sr_enable, + no_isis_sr_enable_cmd, + "no segment-routing [on]", + NO_STR + SR_STR + "Disable Segment Routing\n") +{ + nb_cli_enqueue_change(vty, "./segment-routing/enabled", NB_OP_MODIFY, + "false"); + + return nb_cli_apply_changes(vty, NULL); +} + +void cli_show_isis_sr_enabled(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + if (!yang_dnode_get_bool(dnode, NULL)) + vty_out(vty, " no"); + + vty_out(vty, " segment-routing on\n"); +} + +/* + * XPath: /frr-isisd:isis/instance/segment-routing/srgb + */ +DEFPY_YANG (isis_sr_global_block_label_range, + isis_sr_global_block_label_range_cmd, + "segment-routing global-block (16-1048575)$lower_bound (16-1048575)$upper_bound", + SR_STR + "Segment Routing Global Block label range\n" + "The lower bound of the block\n" + "The upper bound of the block (block size may not exceed 65535)\n") +{ + nb_cli_enqueue_change(vty, "./segment-routing/srgb/lower-bound", + NB_OP_MODIFY, lower_bound_str); + nb_cli_enqueue_change(vty, "./segment-routing/srgb/upper-bound", + NB_OP_MODIFY, upper_bound_str); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG (no_isis_sr_global_block_label_range, + no_isis_sr_global_block_label_range_cmd, + "no segment-routing global-block [(16-1048575) (16-1048575)]", + NO_STR + SR_STR + "Segment Routing Global Block label range\n" + "The lower bound of the block\n" + "The upper bound of the block (block size may not exceed 65535)\n") +{ + nb_cli_enqueue_change(vty, "./segment-routing/srgb/lower-bound", + NB_OP_MODIFY, NULL); + nb_cli_enqueue_change(vty, "./segment-routing/srgb/upper-bound", + NB_OP_MODIFY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +void cli_show_isis_srgb(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + vty_out(vty, " segment-routing global-block %s %s\n", + yang_dnode_get_string(dnode, "./lower-bound"), + yang_dnode_get_string(dnode, "./upper-bound")); +} + +/* + * XPath: /frr-isisd:isis/instance/segment-routing/srlb + */ +DEFPY_YANG (isis_sr_local_block_label_range, + isis_sr_local_block_label_range_cmd, + "segment-routing local-block (16-1048575)$lower_bound (16-1048575)$upper_bound", + SR_STR + "Segment Routing Local Block label range\n" + "The lower bound of the block\n" + "The upper bound of the block (block size may not exceed 65535)\n") +{ + nb_cli_enqueue_change(vty, "./segment-routing/srlb/lower-bound", + NB_OP_MODIFY, lower_bound_str); + nb_cli_enqueue_change(vty, "./segment-routing/srlb/upper-bound", + NB_OP_MODIFY, upper_bound_str); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG (no_isis_sr_local_block_label_range, + no_isis_sr_local_block_label_range_cmd, + "no segment-routing local-block [(16-1048575) (16-1048575)]", + NO_STR + SR_STR + "Segment Routing Local Block label range\n" + "The lower bound of the block\n" + "The upper bound of the block (block size may not exceed 65535)\n") +{ + nb_cli_enqueue_change(vty, "./segment-routing/srlb/lower-bound", + NB_OP_MODIFY, NULL); + nb_cli_enqueue_change(vty, "./segment-routing/srlb/upper-bound", + NB_OP_MODIFY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +void cli_show_isis_srlb(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + vty_out(vty, " segment-routing local-block %s %s\n", + yang_dnode_get_string(dnode, "./lower-bound"), + yang_dnode_get_string(dnode, "./upper-bound")); +} + +/* + * XPath: /frr-isisd:isis/instance/segment-routing/msd/node-msd + */ +DEFPY_YANG (isis_sr_node_msd, + isis_sr_node_msd_cmd, + "segment-routing node-msd (1-16)$msd", + SR_STR + "Maximum Stack Depth for this router\n" + "Maximum number of label that can be stack (1-16)\n") +{ + nb_cli_enqueue_change(vty, "./segment-routing/msd/node-msd", + NB_OP_MODIFY, msd_str); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG (no_isis_sr_node_msd, + no_isis_sr_node_msd_cmd, + "no segment-routing node-msd [(1-16)]", + NO_STR + SR_STR + "Maximum Stack Depth for this router\n" + "Maximum number of label that can be stack (1-16)\n") +{ + nb_cli_enqueue_change(vty, "./segment-routing/msd/node-msd", + NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +void cli_show_isis_node_msd(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + vty_out(vty, " segment-routing node-msd %s\n", + yang_dnode_get_string(dnode, NULL)); +} + +/* + * XPath: /frr-isisd:isis/instance/segment-routing/prefix-sid-map/prefix-sid + */ +DEFPY_YANG (isis_sr_prefix_sid, + isis_sr_prefix_sid_cmd, + "segment-routing prefix\ + $prefix\ + \ + [$lh_behavior]", + SR_STR + "Prefix SID\n" + "IPv4 Prefix\n" + "IPv6 Prefix\n" + "Specify the absolute value of Prefix Segment ID\n" + "The Prefix Segment ID value\n" + "Specify the index of Prefix Segment ID\n" + "The Prefix Segment ID index\n" + "Don't request Penultimate Hop Popping (PHP)\n" + "Upstream neighbor must replace prefix-sid with explicit null label\n") +{ + nb_cli_enqueue_change(vty, ".", NB_OP_CREATE, NULL); + nb_cli_enqueue_change(vty, "./sid-value-type", NB_OP_MODIFY, sid_type); + nb_cli_enqueue_change(vty, "./sid-value", NB_OP_MODIFY, sid_value_str); + if (lh_behavior) { + const char *value; + + if (strmatch(lh_behavior, "no-php-flag")) + value = "no-php"; + else + value = "explicit-null"; + + nb_cli_enqueue_change(vty, "./last-hop-behavior", NB_OP_MODIFY, + value); + } else + nb_cli_enqueue_change(vty, "./last-hop-behavior", NB_OP_MODIFY, + NULL); + + return nb_cli_apply_changes( + vty, "./segment-routing/prefix-sid-map/prefix-sid[prefix='%s']", + prefix_str); +} + +DEFPY_YANG (no_isis_sr_prefix_sid, + no_isis_sr_prefix_sid_cmd, + "no segment-routing prefix $prefix\ + [ []]", + NO_STR + SR_STR + "Prefix SID\n" + "IPv4 Prefix\n" + "IPv6 Prefix\n" + "Specify the absolute value of Prefix Segment ID\n" + "The Prefix Segment ID value\n" + "Specify the index of Prefix Segment ID\n" + "The Prefix Segment ID index\n" + "Don't request Penultimate Hop Popping (PHP)\n" + "Upstream neighbor must replace prefix-sid with explicit null label\n") +{ + nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes( + vty, "./segment-routing/prefix-sid-map/prefix-sid[prefix='%s']", + prefix_str); +} + +void cli_show_isis_prefix_sid(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + const char *prefix; + const char *lh_behavior; + const char *sid_value_type; + const char *sid_value; + + prefix = yang_dnode_get_string(dnode, "./prefix"); + lh_behavior = yang_dnode_get_string(dnode, "./last-hop-behavior"); + sid_value_type = yang_dnode_get_string(dnode, "./sid-value-type"); + sid_value = yang_dnode_get_string(dnode, "./sid-value"); + + vty_out(vty, " segment-routing prefix %s", prefix); + if (strmatch(sid_value_type, "absolute")) + vty_out(vty, " absolute"); + else + vty_out(vty, " index"); + vty_out(vty, " %s", sid_value); + if (strmatch(lh_behavior, "no-php")) + vty_out(vty, " no-php-flag"); + else if (strmatch(lh_behavior, "explicit-null")) + vty_out(vty, " explicit-null"); + vty_out(vty, "\n"); +} + /* * XPath: /frr-interface:lib/interface/frr-isisd:isis/passive */ -DEFPY(isis_passive, isis_passive_cmd, "[no] isis passive", +DEFPY_YANG(isis_passive, isis_passive_cmd, "[no] isis passive", NO_STR "IS-IS routing protocol\n" "Configure the passive mode for interface\n") @@ -1276,7 +1746,7 @@ void cli_show_ip_isis_passive(struct vty *vty, struct lyd_node *dnode, * XPath: /frr-interface:lib/interface/frr-isisd:isis/password */ -DEFPY(isis_passwd, isis_passwd_cmd, "isis password $type WORD$pwd", +DEFPY_YANG(isis_passwd, isis_passwd_cmd, "isis password $type WORD$pwd", "IS-IS routing protocol\n" "Configure the authentication password for a circuit\n" "HMAC-MD5 authentication\n" @@ -1293,7 +1763,7 @@ DEFPY(isis_passwd, isis_passwd_cmd, "isis password $type WORD$pwd", return nb_cli_apply_changes(vty, NULL); } -DEFPY(no_isis_passwd, no_isis_passwd_cmd, "no isis password [ WORD]", +DEFPY_YANG(no_isis_passwd, no_isis_passwd_cmd, "no isis password [ WORD]", NO_STR "IS-IS routing protocol\n" "Configure the authentication password for a circuit\n" @@ -1318,7 +1788,7 @@ void cli_show_ip_isis_password(struct vty *vty, struct lyd_node *dnode, /* * XPath: /frr-interface:lib/interface/frr-isisd:isis/metric */ -DEFPY(isis_metric, isis_metric_cmd, +DEFPY_YANG(isis_metric, isis_metric_cmd, "isis metric [level-1|level-2]$level (0-16777215)$met", "IS-IS routing protocol\n" "Set default metric for circuit\n" @@ -1336,7 +1806,7 @@ DEFPY(isis_metric, isis_metric_cmd, return nb_cli_apply_changes(vty, NULL); } -DEFPY(no_isis_metric, no_isis_metric_cmd, +DEFPY_YANG(no_isis_metric, no_isis_metric_cmd, "no isis metric [level-1|level-2]$level [(0-16777215)]", NO_STR "IS-IS routing protocol\n" @@ -1364,15 +1834,15 @@ void cli_show_ip_isis_metric(struct vty *vty, struct lyd_node *dnode, if (strmatch(l1, l2)) vty_out(vty, " isis metric %s\n", l1); else { - vty_out(vty, " isis metric %s level-1\n", l1); - vty_out(vty, " isis metric %s level-2\n", l2); + vty_out(vty, " isis metric level-1 %s\n", l1); + vty_out(vty, " isis metric level-2 %s\n", l2); } } /* * XPath: /frr-interface:lib/interface/frr-isisd:isis/hello/interval */ -DEFPY(isis_hello_interval, isis_hello_interval_cmd, +DEFPY_YANG(isis_hello_interval, isis_hello_interval_cmd, "isis hello-interval [level-1|level-2]$level (1-600)$intv", "IS-IS routing protocol\n" "Set Hello interval\n" @@ -1392,7 +1862,7 @@ DEFPY(isis_hello_interval, isis_hello_interval_cmd, return nb_cli_apply_changes(vty, NULL); } -DEFPY(no_isis_hello_interval, no_isis_hello_interval_cmd, +DEFPY_YANG(no_isis_hello_interval, no_isis_hello_interval_cmd, "no isis hello-interval [level-1|level-2]$level [(1-600)]", NO_STR "IS-IS routing protocol\n" @@ -1422,15 +1892,15 @@ void cli_show_ip_isis_hello_interval(struct vty *vty, struct lyd_node *dnode, if (strmatch(l1, l2)) vty_out(vty, " isis hello-interval %s\n", l1); else { - vty_out(vty, " isis hello-interval %s level-1\n", l1); - vty_out(vty, " isis hello-interval %s level-2\n", l2); + vty_out(vty, " isis hello-interval level-1 %s\n", l1); + vty_out(vty, " isis hello-interval level-2 %s\n", l2); } } /* * XPath: /frr-interface:lib/interface/frr-isisd:isis/hello/multiplier */ -DEFPY(isis_hello_multiplier, isis_hello_multiplier_cmd, +DEFPY_YANG(isis_hello_multiplier, isis_hello_multiplier_cmd, "isis hello-multiplier [level-1|level-2]$level (2-100)$mult", "IS-IS routing protocol\n" "Set multiplier for Hello holding time\n" @@ -1450,7 +1920,7 @@ DEFPY(isis_hello_multiplier, isis_hello_multiplier_cmd, return nb_cli_apply_changes(vty, NULL); } -DEFPY(no_isis_hello_multiplier, no_isis_hello_multiplier_cmd, +DEFPY_YANG(no_isis_hello_multiplier, no_isis_hello_multiplier_cmd, "no isis hello-multiplier [level-1|level-2]$level [(2-100)]", NO_STR "IS-IS routing protocol\n" @@ -1480,8 +1950,8 @@ void cli_show_ip_isis_hello_multi(struct vty *vty, struct lyd_node *dnode, if (strmatch(l1, l2)) vty_out(vty, " isis hello-multiplier %s\n", l1); else { - vty_out(vty, " isis hello-multiplier %s level-1\n", l1); - vty_out(vty, " isis hello-multiplier %s level-2\n", l2); + vty_out(vty, " isis hello-multiplier level-1 %s\n", l1); + vty_out(vty, " isis hello-multiplier level-2 %s\n", l2); } } @@ -1489,7 +1959,7 @@ void cli_show_ip_isis_hello_multi(struct vty *vty, struct lyd_node *dnode, * XPath: * /frr-interface:lib/interface/frr-isisd:isis/disable-three-way-handshake */ -DEFPY(isis_threeway_adj, isis_threeway_adj_cmd, "[no] isis three-way-handshake", +DEFPY_YANG(isis_threeway_adj, isis_threeway_adj_cmd, "[no] isis three-way-handshake", NO_STR "IS-IS commands\n" "Enable/Disable three-way handshake\n") @@ -1512,7 +1982,7 @@ void cli_show_ip_isis_threeway_shake(struct vty *vty, struct lyd_node *dnode, /* * XPath: /frr-interface:lib/interface/frr-isisd:isis/hello/padding */ -DEFPY(isis_hello_padding, isis_hello_padding_cmd, "[no] isis hello padding", +DEFPY_YANG(isis_hello_padding, isis_hello_padding_cmd, "[no] isis hello padding", NO_STR "IS-IS routing protocol\n" "Add padding to IS-IS hello packets\n" @@ -1536,7 +2006,7 @@ void cli_show_ip_isis_hello_padding(struct vty *vty, struct lyd_node *dnode, /* * XPath: /frr-interface:lib/interface/frr-isisd:isis/csnp-interval */ -DEFPY(csnp_interval, csnp_interval_cmd, +DEFPY_YANG(csnp_interval, csnp_interval_cmd, "isis csnp-interval (1-600)$intv [level-1|level-2]$level", "IS-IS routing protocol\n" "Set CSNP interval in seconds\n" @@ -1556,7 +2026,7 @@ DEFPY(csnp_interval, csnp_interval_cmd, return nb_cli_apply_changes(vty, NULL); } -DEFPY(no_csnp_interval, no_csnp_interval_cmd, +DEFPY_YANG(no_csnp_interval, no_csnp_interval_cmd, "no isis csnp-interval [(1-600)] [level-1|level-2]$level", NO_STR "IS-IS routing protocol\n" @@ -1594,7 +2064,7 @@ void cli_show_ip_isis_csnp_interval(struct vty *vty, struct lyd_node *dnode, /* * XPath: /frr-interface:lib/interface/frr-isisd:isis/psnp-interval */ -DEFPY(psnp_interval, psnp_interval_cmd, +DEFPY_YANG(psnp_interval, psnp_interval_cmd, "isis psnp-interval (1-120)$intv [level-1|level-2]$level", "IS-IS routing protocol\n" "Set PSNP interval in seconds\n" @@ -1614,7 +2084,7 @@ DEFPY(psnp_interval, psnp_interval_cmd, return nb_cli_apply_changes(vty, NULL); } -DEFPY(no_psnp_interval, no_psnp_interval_cmd, +DEFPY_YANG(no_psnp_interval, no_psnp_interval_cmd, "no isis psnp-interval [(1-120)] [level-1|level-2]$level", NO_STR "IS-IS routing protocol\n" @@ -1652,16 +2122,8 @@ void cli_show_ip_isis_psnp_interval(struct vty *vty, struct lyd_node *dnode, /* * XPath: /frr-interface:lib/interface/frr-isisd:isis/multi-topology */ -DEFPY(circuit_topology, circuit_topology_cmd, - "[no] isis topology" - "$topology", +DEFPY_YANG(circuit_topology, circuit_topology_cmd, + "[no] isis topology$topology", NO_STR "IS-IS routing protocol\n" "Configure interface IS-IS topologies\n" @@ -1745,7 +2207,7 @@ void cli_show_ip_isis_mt_ipv6_dstsrc(struct vty *vty, struct lyd_node *dnode, /* * XPath: /frr-interface:lib/interface/frr-isisd:isis/circuit-type */ -DEFPY(isis_circuit_type, isis_circuit_type_cmd, +DEFPY_YANG(isis_circuit_type, isis_circuit_type_cmd, "isis circuit-type $type", "IS-IS routing protocol\n" "Configure circuit type for interface\n" @@ -1760,7 +2222,7 @@ DEFPY(isis_circuit_type, isis_circuit_type_cmd, return nb_cli_apply_changes(vty, NULL); } -DEFPY(no_isis_circuit_type, no_isis_circuit_type_cmd, +DEFPY_YANG(no_isis_circuit_type, no_isis_circuit_type_cmd, "no isis circuit-type [level-1|level-1-2|level-2-only]", NO_STR "IS-IS routing protocol\n" @@ -1838,7 +2300,7 @@ void cli_show_ip_isis_circ_type(struct vty *vty, struct lyd_node *dnode, /* * XPath: /frr-interface:lib/interface/frr-isisd:isis/network-type */ -DEFPY(isis_network, isis_network_cmd, "[no] isis network point-to-point", +DEFPY_YANG(isis_network, isis_network_cmd, "[no] isis network point-to-point", NO_STR "IS-IS routing protocol\n" "Set network type\n" @@ -1863,7 +2325,7 @@ void cli_show_ip_isis_network_type(struct vty *vty, struct lyd_node *dnode, /* * XPath: /frr-interface:lib/interface/frr-isisd:isis/priority */ -DEFPY(isis_priority, isis_priority_cmd, +DEFPY_YANG(isis_priority, isis_priority_cmd, "isis priority (0-127)$prio [level-1|level-2]$level", "IS-IS routing protocol\n" "Set priority for Designated Router election\n" @@ -1881,7 +2343,7 @@ DEFPY(isis_priority, isis_priority_cmd, return nb_cli_apply_changes(vty, NULL); } -DEFPY(no_isis_priority, no_isis_priority_cmd, +DEFPY_YANG(no_isis_priority, no_isis_priority_cmd, "no isis priority [(0-127)] [level-1|level-2]$level", NO_STR "IS-IS routing protocol\n" @@ -1917,7 +2379,7 @@ void cli_show_ip_isis_priority(struct vty *vty, struct lyd_node *dnode, /* * XPath: /frr-isisd:isis/instance/log-adjacency-changes */ -DEFPY(log_adj_changes, log_adj_changes_cmd, "[no] log-adjacency-changes", +DEFPY_YANG(log_adj_changes, log_adj_changes_cmd, "[no] log-adjacency-changes", NO_STR "Log changes in adjacency state\n") { nb_cli_enqueue_change(vty, "./log-adjacency-changes", NB_OP_MODIFY, @@ -1942,6 +2404,8 @@ void isis_cli_init(void) install_element(INTERFACE_NODE, &ip_router_isis_cmd); install_element(INTERFACE_NODE, &ip6_router_isis_cmd); install_element(INTERFACE_NODE, &no_ip_router_isis_cmd); + install_element(INTERFACE_NODE, &isis_bfd_cmd); + install_element(INTERFACE_NODE, &isis_bfd_profile_cmd); install_element(ISIS_NODE, &net_cmd); @@ -1966,6 +2430,8 @@ void isis_cli_init(void) install_element(ISIS_NODE, &no_lsp_refresh_interval_cmd); install_element(ISIS_NODE, &max_lsp_lifetime_cmd); install_element(ISIS_NODE, &no_max_lsp_lifetime_cmd); + install_element(ISIS_NODE, &lsp_timers_cmd); + install_element(ISIS_NODE, &no_lsp_timers_cmd); install_element(ISIS_NODE, &area_lsp_mtu_cmd); install_element(ISIS_NODE, &no_area_lsp_mtu_cmd); @@ -1987,6 +2453,17 @@ void isis_cli_init(void) install_element(ISIS_NODE, &isis_topology_cmd); + install_element(ISIS_NODE, &isis_sr_enable_cmd); + install_element(ISIS_NODE, &no_isis_sr_enable_cmd); + install_element(ISIS_NODE, &isis_sr_global_block_label_range_cmd); + install_element(ISIS_NODE, &no_isis_sr_global_block_label_range_cmd); + install_element(ISIS_NODE, &isis_sr_local_block_label_range_cmd); + install_element(ISIS_NODE, &no_isis_sr_local_block_label_range_cmd); + install_element(ISIS_NODE, &isis_sr_node_msd_cmd); + install_element(ISIS_NODE, &no_isis_sr_node_msd_cmd); + install_element(ISIS_NODE, &isis_sr_prefix_sid_cmd); + install_element(ISIS_NODE, &no_isis_sr_prefix_sid_cmd); + install_element(INTERFACE_NODE, &isis_passive_cmd); install_element(INTERFACE_NODE, &isis_passwd_cmd); diff --git a/isisd/isis_cli.h b/isisd/isis_cli.h deleted file mode 100644 index 8dadf60981..0000000000 --- a/isisd/isis_cli.h +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (C) 2018 Volta Networks - * Emanuele Di Pascale - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; see the file COPYING; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef ISISD_ISIS_CLI_H_ -#define ISISD_ISIS_CLI_H_ - -/* add cli_show declarations here as externs */ -void cli_show_router_isis(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -void cli_show_ip_isis_ipv4(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -void cli_show_ip_isis_ipv6(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -void cli_show_isis_area_address(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -void cli_show_isis_is_type(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -void cli_show_isis_dynamic_hostname(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -void cli_show_isis_attached(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -void cli_show_isis_overload(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -void cli_show_isis_metric_style(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -void cli_show_isis_area_pwd(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -void cli_show_isis_domain_pwd(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -void cli_show_isis_lsp_gen_interval(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -void cli_show_isis_lsp_ref_interval(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -void cli_show_isis_lsp_max_lifetime(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -void cli_show_isis_lsp_mtu(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -void cli_show_isis_spf_min_interval(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -void cli_show_isis_spf_ietf_backoff(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -void cli_show_isis_purge_origin(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -void cli_show_isis_mpls_te(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -void cli_show_isis_mpls_te_router_addr(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -void cli_show_isis_def_origin_ipv4(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -void cli_show_isis_def_origin_ipv6(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -void cli_show_isis_redistribute_ipv4(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -void cli_show_isis_redistribute_ipv6(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -void cli_show_isis_mt_ipv4_multicast(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -void cli_show_isis_mt_ipv4_mgmt(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -void cli_show_isis_mt_ipv6_unicast(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -void cli_show_isis_mt_ipv6_multicast(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -void cli_show_isis_mt_ipv6_mgmt(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -void cli_show_isis_mt_ipv6_dstsrc(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -void cli_show_ip_isis_passive(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -void cli_show_ip_isis_password(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -void cli_show_ip_isis_metric(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -void cli_show_ip_isis_hello_interval(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -void cli_show_ip_isis_hello_multi(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -void cli_show_ip_isis_threeway_shake(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -void cli_show_ip_isis_hello_padding(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -void cli_show_ip_isis_csnp_interval(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -void cli_show_ip_isis_psnp_interval(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -void cli_show_ip_isis_mt_ipv4_unicast(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -void cli_show_ip_isis_mt_ipv4_multicast(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -void cli_show_ip_isis_mt_ipv4_mgmt(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -void cli_show_ip_isis_mt_ipv6_unicast(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -void cli_show_ip_isis_mt_ipv6_multicast(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -void cli_show_ip_isis_mt_ipv6_mgmt(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -void cli_show_ip_isis_mt_ipv6_dstsrc(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -void cli_show_ip_isis_circ_type(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -void cli_show_ip_isis_network_type(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -void cli_show_ip_isis_priority(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -void cli_show_isis_log_adjacency(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); - -#endif /* ISISD_ISIS_CLI_H_ */ diff --git a/isisd/isis_csm.c b/isisd/isis_csm.c index 88676dd990..f5ad0aff48 100644 --- a/isisd/isis_csm.c +++ b/isisd/isis_csm.c @@ -48,14 +48,12 @@ #include "isisd/isis_events.h" #include "isisd/isis_errors.h" -extern struct isis *isis; - -static const char *csm_statestr[] = {"C_STATE_NA", "C_STATE_INIT", +static const char *const csm_statestr[] = {"C_STATE_NA", "C_STATE_INIT", "C_STATE_CONF", "C_STATE_UP"}; #define STATE2STR(S) csm_statestr[S] -static const char *csm_eventstr[] = { +static const char *const csm_eventstr[] = { "NO_STATE", "ISIS_ENABLE", "IF_UP_FROM_Z", "ISIS_DISABLE", "IF_DOWN_FROM_Z", }; @@ -66,9 +64,10 @@ struct isis_circuit * isis_csm_state_change(int event, struct isis_circuit *circuit, void *arg) { int old_state; + struct isis *isis = NULL; old_state = circuit ? circuit->state : C_STATE_NA; - if (isis->debugs & DEBUG_EVENTS) + if (IS_DEBUG_EVENTS) zlog_debug("CSM_EVENT: %s", EVENT2STR(event)); switch (old_state) { @@ -84,9 +83,17 @@ isis_csm_state_change(int event, struct isis_circuit *circuit, void *arg) circuit->state = C_STATE_CONF; break; case IF_UP_FROM_Z: + isis = isis_lookup_by_vrfid(((struct interface *)arg)->vrf_id); + if (isis == NULL) { + zlog_warn( + " %s : ISIS routing instance not found", + __func__); + break; + } circuit = isis_circuit_new(); isis_circuit_if_add(circuit, (struct interface *)arg); listnode_add(isis->init_circ_list, circuit); + circuit->isis = isis; circuit->state = C_STATE_INIT; break; case ISIS_DISABLE: @@ -111,7 +118,8 @@ isis_csm_state_change(int event, struct isis_circuit *circuit, void *arg) circuit->state = C_STATE_UP; isis_event_circuit_state_change(circuit, circuit->area, 1); - listnode_delete(isis->init_circ_list, circuit); + listnode_delete(circuit->isis->init_circ_list, + circuit); break; case IF_UP_FROM_Z: assert(circuit); @@ -122,7 +130,8 @@ isis_csm_state_change(int event, struct isis_circuit *circuit, void *arg) break; case IF_DOWN_FROM_Z: isis_circuit_if_del(circuit, (struct interface *)arg); - listnode_delete(isis->init_circ_list, circuit); + listnode_delete(circuit->isis->init_circ_list, + circuit); isis_circuit_del(circuit); circuit = NULL; break; @@ -137,27 +146,11 @@ isis_csm_state_change(int event, struct isis_circuit *circuit, void *arg) case IF_UP_FROM_Z: isis_circuit_if_add(circuit, (struct interface *)arg); if (isis_circuit_up(circuit) != ISIS_OK) { + isis_circuit_if_del(circuit, (struct interface *)arg); flog_err( EC_ISIS_CONFIG, "Could not bring up %s because of invalid config.", circuit->interface->name); - flog_err( - EC_ISIS_CONFIG, - "Clearing config for %s. Please re-examine it.", - circuit->interface->name); - if (circuit->ip_router) { - circuit->ip_router = 0; - circuit->area->ip_circuits--; - } - if (circuit->ipv6_router) { - circuit->ipv6_router = 0; - circuit->area->ipv6_circuits--; - } - circuit_update_nlpids(circuit); - isis_circuit_deconfigure(circuit, - circuit->area); - listnode_add(isis->init_circ_list, circuit); - circuit->state = C_STATE_INIT; break; } circuit->state = C_STATE_UP; @@ -185,6 +178,7 @@ isis_csm_state_change(int event, struct isis_circuit *circuit, void *arg) zlog_warn("circuit already connected"); break; case ISIS_DISABLE: + isis = circuit->area->isis; isis_circuit_down(circuit); isis_circuit_deconfigure(circuit, (struct isis_area *)arg); @@ -192,6 +186,7 @@ isis_csm_state_change(int event, struct isis_circuit *circuit, void *arg) isis_event_circuit_state_change( circuit, (struct isis_area *)arg, 0); listnode_add(isis->init_circ_list, circuit); + circuit->isis = isis; break; case IF_DOWN_FROM_Z: isis_circuit_down(circuit); @@ -207,7 +202,7 @@ isis_csm_state_change(int event, struct isis_circuit *circuit, void *arg) zlog_warn("Invalid circuit state %d", old_state); } - if (isis->debugs & DEBUG_EVENTS) + if (IS_DEBUG_EVENTS) zlog_debug("CSM_STATE_CHANGE: %s -> %s ", STATE2STR(old_state), circuit ? STATE2STR(circuit->state) : STATE2STR(C_STATE_NA)); diff --git a/isisd/isis_dlpi.c b/isisd/isis_dlpi.c index a96dd93804..06fb41430c 100644 --- a/isisd/isis_dlpi.c +++ b/isisd/isis_dlpi.c @@ -54,12 +54,15 @@ static t_uscalar_t dlpi_ctl[1024]; /* DLPI control messages */ * ISO 10589 - 8.4.8 */ -uint8_t ALL_L1_ISS[6] = {0x01, 0x80, 0xC2, 0x00, 0x00, 0x14}; -uint8_t ALL_L2_ISS[6] = {0x01, 0x80, 0xC2, 0x00, 0x00, 0x15}; -uint8_t ALL_ISS[6] = {0x09, 0x00, 0x2B, 0x00, 0x00, 0x05}; -uint8_t ALL_ESS[6] = {0x09, 0x00, 0x2B, 0x00, 0x00, 0x04}; +static const uint8_t ALL_L1_ISS[6] = {0x01, 0x80, 0xC2, 0x00, 0x00, 0x14}; +static const uint8_t ALL_L2_ISS[6] = {0x01, 0x80, 0xC2, 0x00, 0x00, 0x15}; +static const uint8_t ALL_ISS[6] = {0x09, 0x00, 0x2B, 0x00, 0x00, 0x05}; +#if 0 +/* missing support for ES-IS on Solaris */ +static const uint8_t ALL_ESS[6] = {0x09, 0x00, 0x2B, 0x00, 0x00, 0x04}; +#endif -static uint8_t sock_buff[8192]; +static uint8_t sock_buff[16384]; static unsigned short pf_filter[] = { ENF_PUSHWORD + 0, /* Get the SSAP/DSAP values */ @@ -467,7 +470,7 @@ int isis_sock_init(struct isis_circuit *circuit) { int retval = ISIS_OK; - frr_elevate_privs(&isisd_privs) { + frr_with_privs(&isisd_privs) { retval = open_dlpi_dev(circuit); @@ -563,8 +566,7 @@ int isis_send_pdu_bcast(struct isis_circuit *circuit, int level) buflen = stream_get_endp(circuit->snd_stream) + LLC_LEN; if ((size_t)buflen > sizeof(sock_buff)) { zlog_warn( - "isis_send_pdu_bcast: sock_buff size %zu is less than " - "output pdu size %d on circuit %s", + "isis_send_pdu_bcast: sock_buff size %zu is less than output pdu size %d on circuit %s", sizeof(sock_buff), buflen, circuit->interface->name); return ISIS_WARNING; } diff --git a/isisd/isis_dr.c b/isisd/isis_dr.c index 7be5307500..318fb9fab8 100644 --- a/isisd/isis_dr.c +++ b/isisd/isis_dr.c @@ -225,7 +225,7 @@ int isis_dr_resign(struct isis_circuit *circuit, int level) THREAD_TIMER_OFF(circuit->u.bc.t_refresh_pseudo_lsp[level - 1]); circuit->lsp_regenerate_pending[level - 1] = 0; - memcpy(id, isis->sysid, ISIS_SYS_ID_LEN); + memcpy(id, circuit->area->isis->sysid, ISIS_SYS_ID_LEN); LSP_PSEUDO_ID(id) = circuit->circuit_id; LSP_FRAGMENT(id) = 0; lsp_purge_pseudo(id, circuit, level); @@ -264,7 +264,7 @@ int isis_dr_commence(struct isis_circuit *circuit, int level) { uint8_t old_dr[ISIS_SYS_ID_LEN + 2]; - if (isis->debugs & DEBUG_EVENTS) + if (IS_DEBUG_EVENTS) zlog_debug("isis_dr_commence l%d", level); /* Lets keep a pause in DR election */ @@ -278,7 +278,8 @@ int isis_dr_commence(struct isis_circuit *circuit, int level) /* there was a dr elected, purge its LSPs from the db */ lsp_purge_pseudo(old_dr, circuit, level); } - memcpy(circuit->u.bc.l1_desig_is, isis->sysid, ISIS_SYS_ID_LEN); + memcpy(circuit->u.bc.l1_desig_is, circuit->area->isis->sysid, + ISIS_SYS_ID_LEN); *(circuit->u.bc.l1_desig_is + ISIS_SYS_ID_LEN) = circuit->circuit_id; @@ -299,7 +300,8 @@ int isis_dr_commence(struct isis_circuit *circuit, int level) /* there was a dr elected, purge its LSPs from the db */ lsp_purge_pseudo(old_dr, circuit, level); } - memcpy(circuit->u.bc.l2_desig_is, isis->sysid, ISIS_SYS_ID_LEN); + memcpy(circuit->u.bc.l2_desig_is, circuit->area->isis->sysid, + ISIS_SYS_ID_LEN); *(circuit->u.bc.l2_desig_is + ISIS_SYS_ID_LEN) = circuit->circuit_id; diff --git a/isisd/isis_dynhn.c b/isisd/isis_dynhn.c index 921e23d33a..244f388c26 100644 --- a/isisd/isis_dynhn.c +++ b/isisd/isis_dynhn.c @@ -45,33 +45,48 @@ extern struct host host; struct list *dyn_cache = NULL; static int dyn_cache_cleanup(struct thread *); -void dyn_cache_init(void) +void dyn_cache_init(struct isis *isis) { if (dyn_cache == NULL) dyn_cache = list_new(); - thread_add_timer(master, dyn_cache_cleanup, NULL, 120, - &isis->t_dync_clean); + if (!CHECK_FLAG(im->options, F_ISIS_UNIT_TEST)) + thread_add_timer(master, dyn_cache_cleanup, isis, 120, + &isis->t_dync_clean); return; } +void dyn_cache_cleanup_all(void) +{ + struct listnode *node, *nnode; + struct isis_dynhn *dyn; + + for (ALL_LIST_ELEMENTS(dyn_cache, node, nnode, dyn)) { + list_delete_node(dyn_cache, node); + XFREE(MTYPE_ISIS_DYNHN, dyn); + } +} + static int dyn_cache_cleanup(struct thread *thread) { struct listnode *node, *nnode; struct isis_dynhn *dyn; time_t now = time(NULL); + struct isis *isis = NULL; + + isis = THREAD_ARG(thread); isis->t_dync_clean = NULL; for (ALL_LIST_ELEMENTS(dyn_cache, node, nnode, dyn)) { if ((now - dyn->refresh) < MAX_LSP_LIFETIME) continue; - list_delete_node(dyn_cache, node); XFREE(MTYPE_ISIS_DYNHN, dyn); } - thread_add_timer(master, dyn_cache_cleanup, NULL, 120, - &isis->t_dync_clean); + thread_add_timer(master, dyn_cache_cleanup, isis, 120, + &isis->t_dync_clean); + return ISIS_OK; } @@ -132,11 +147,14 @@ void isis_dynhn_remove(const uint8_t *id) * 2 0000.0000.0002 bar-gw * * 0000.0000.0004 this-gw */ -void dynhn_print_all(struct vty *vty) +void dynhn_print_all(struct vty *vty, struct isis *isis) { struct listnode *node; struct isis_dynhn *dyn; + vty_out(vty, "vrf : %s\n", isis->name); + if (!isis->sysid_set) + return; vty_out(vty, "Level System ID Dynamic Hostname\n"); for (ALL_LIST_ELEMENTS_RO(dyn_cache, node, dyn)) { vty_out(vty, "%-7d", dyn->level); diff --git a/isisd/isis_dynhn.h b/isisd/isis_dynhn.h index 27133bd3fd..973fde8307 100644 --- a/isisd/isis_dynhn.h +++ b/isisd/isis_dynhn.h @@ -30,11 +30,12 @@ struct isis_dynhn { int level; }; -void dyn_cache_init(void); +void dyn_cache_init(struct isis *isis); +void dyn_cache_cleanup_all(void); void isis_dynhn_insert(const uint8_t *id, const char *hostname, int level); void isis_dynhn_remove(const uint8_t *id); struct isis_dynhn *dynhn_find_by_id(const uint8_t *id); struct isis_dynhn *dynhn_find_by_name(const char *hostname); -void dynhn_print_all(struct vty *vty); +void dynhn_print_all(struct vty *vty, struct isis *isis); #endif /* _ZEBRA_ISIS_DYNHN_H */ diff --git a/isisd/isis_errors.c b/isisd/isis_errors.c index 755e70dbf6..7530d0b966 100644 --- a/isisd/isis_errors.c +++ b/isisd/isis_errors.c @@ -37,6 +37,12 @@ static struct log_ref ferr_isis_err[] = { .description = "Isis has detected an error within configuration for the router", .suggestion = "Ensure configuration is correct" }, + { + .code = EC_ISIS_SID_OVERFLOW, + .title = "SID index overflow", + .description = "Isis has detected that a SID index falls outside of its associated SRGB range", + .suggestion = "Configure a larger SRGB" + }, { .code = END_FERR, } diff --git a/isisd/isis_errors.h b/isisd/isis_errors.h index 0732737607..d5674dbf30 100644 --- a/isisd/isis_errors.h +++ b/isisd/isis_errors.h @@ -26,6 +26,7 @@ enum isis_log_refs { EC_ISIS_PACKET = ISIS_FERR_START, EC_ISIS_CONFIG, + EC_ISIS_SID_OVERFLOW, }; extern void isis_error_init(void); diff --git a/isisd/isis_events.c b/isisd/isis_events.c index 6a5dcfb075..717a5fd046 100644 --- a/isisd/isis_events.c +++ b/isisd/isis_events.c @@ -62,7 +62,7 @@ void isis_event_circuit_state_change(struct isis_circuit *circuit, { area->circuit_state_changes++; - if (isis->debugs & DEBUG_EVENTS) + if (IS_DEBUG_EVENTS) zlog_debug("ISIS-Evt (%s) circuit %s", area->area_tag, up ? "up" : "down"); @@ -76,6 +76,12 @@ void isis_event_circuit_state_change(struct isis_circuit *circuit, static void circuit_commence_level(struct isis_circuit *circuit, int level) { + if (IS_DEBUG_EVENTS) + zlog_debug( + "ISIS-Evt (%s) circuit %u on iface %s commencing on L%d", + circuit->area->area_tag, circuit->circuit_id, + circuit->interface->name, level); + if (!circuit->is_passive) { if (level == 1) { thread_add_timer(master, send_l1_psnp, circuit, @@ -105,6 +111,12 @@ static void circuit_resign_level(struct isis_circuit *circuit, int level) { int idx = level - 1; + if (IS_DEBUG_EVENTS) + zlog_debug( + "ISIS-Evt (%s) circuit %u on iface %s resigning on L%d", + circuit->area->area_tag, circuit->circuit_id, + circuit->interface->name, level); + THREAD_TIMER_OFF(circuit->t_send_csnp[idx]); THREAD_TIMER_OFF(circuit->t_send_psnp[idx]); @@ -114,6 +126,7 @@ static void circuit_resign_level(struct isis_circuit *circuit, int level) THREAD_TIMER_OFF(circuit->u.bc.t_refresh_pseudo_lsp[idx]); circuit->lsp_regenerate_pending[idx] = 0; circuit->u.bc.run_dr_elect[idx] = 0; + circuit->u.bc.is_dr[idx] = 0; if (circuit->u.bc.lan_neighs[idx] != NULL) list_delete(&circuit->u.bc.lan_neighs[idx]); } @@ -128,7 +141,7 @@ void isis_circuit_is_type_set(struct isis_circuit *circuit, int newtype) return; } - if (isis->debugs & DEBUG_EVENTS) + if (IS_DEBUG_EVENTS) zlog_debug("ISIS-Evt (%s) circuit type change %s -> %s", circuit->area->area_tag, circuit_t2string(circuit->is_type), @@ -208,7 +221,7 @@ int isis_event_dis_status_change(struct thread *thread) /* invalid arguments */ if (!circuit || !circuit->area) return 0; - if (isis->debugs & DEBUG_EVENTS) + if (IS_DEBUG_EVENTS) zlog_debug("ISIS-Evt (%s) DIS status change", circuit->area->area_tag); @@ -221,7 +234,7 @@ int isis_event_dis_status_change(struct thread *thread) void isis_event_auth_failure(char *area_tag, const char *error_string, uint8_t *sysid) { - if (isis->debugs & DEBUG_EVENTS) + if (IS_DEBUG_EVENTS) zlog_debug("ISIS-Evt (%s) Authentication failure %s from %s", area_tag, error_string, sysid_print(sysid)); diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c index 1991666954..5b105bc855 100644 --- a/isisd/isis_lsp.c +++ b/isisd/isis_lsp.c @@ -52,11 +52,13 @@ #include "isisd/isis_csm.h" #include "isisd/isis_adjacency.h" #include "isisd/isis_spf.h" -#include "isisd/isis_te.h" #include "isisd/isis_mt.h" #include "isisd/isis_tlvs.h" +#include "isisd/isis_te.h" +#include "isisd/isis_sr.h" #include "isisd/fabricd.h" #include "isisd/isis_tx_queue.h" +#include "isisd/isis_nb.h" static int lsp_refresh(struct thread *thread); static int lsp_l1_refresh_pseudo(struct thread *thread); @@ -196,18 +198,14 @@ int lsp_compare(char *areatag, struct isis_lsp *lsp, uint32_t seqno, if (lsp->hdr.seqno == seqno && lsp->hdr.checksum == checksum && ((lsp->hdr.rem_lifetime == 0 && rem_lifetime == 0) || (lsp->hdr.rem_lifetime != 0 && rem_lifetime != 0))) { - if (isis->debugs & DEBUG_SNP_PACKETS) { + if (IS_DEBUG_SNP_PACKETS) { zlog_debug( - "ISIS-Snp (%s): Compare LSP %s seq 0x%08" PRIx32 - ", cksum 0x%04" PRIx16 ", lifetime %" PRIu16 - "s", + "ISIS-Snp (%s): Compare LSP %s seq 0x%08x, cksum 0x%04hx, lifetime %hus", areatag, rawlspid_print(lsp->hdr.lsp_id), lsp->hdr.seqno, lsp->hdr.checksum, lsp->hdr.rem_lifetime); zlog_debug( - "ISIS-Snp (%s): is equal to ours seq 0x%08" PRIx32 - ", cksum 0x%04" PRIx16 ", lifetime %" PRIu16 - "s", + "ISIS-Snp (%s): is equal to ours seq 0x%08x, cksum 0x%04hx, lifetime %hus", areatag, seqno, checksum, rem_lifetime); } return LSP_EQUAL; @@ -230,30 +228,24 @@ int lsp_compare(char *areatag, struct isis_lsp *lsp, uint32_t seqno, && ((lsp->hdr.rem_lifetime != 0 && rem_lifetime == 0) || (lsp->hdr.checksum != checksum && lsp->hdr.rem_lifetime)))) { - if (isis->debugs & DEBUG_SNP_PACKETS) { + if (IS_DEBUG_SNP_PACKETS) { zlog_debug( - "ISIS-Snp (%s): Compare LSP %s seq 0x%08" PRIx32 - ", cksum 0x%04" PRIx16 ", lifetime %" PRIu16 - "s", + "ISIS-Snp (%s): Compare LSP %s seq 0x%08x, cksum 0x%04hx, lifetime %hus", areatag, rawlspid_print(lsp->hdr.lsp_id), seqno, checksum, rem_lifetime); zlog_debug( - "ISIS-Snp (%s): is newer than ours seq 0x%08" PRIx32 - ", cksum 0x%04" PRIx16 ", lifetime %" PRIu16 - "s", + "ISIS-Snp (%s): is newer than ours seq 0x%08x, cksum 0x%04hx, lifetime %hus", areatag, lsp->hdr.seqno, lsp->hdr.checksum, lsp->hdr.rem_lifetime); } return LSP_NEWER; } - if (isis->debugs & DEBUG_SNP_PACKETS) { - zlog_debug("ISIS-Snp (%s): Compare LSP %s seq 0x%08" PRIx32 - ", cksum 0x%04" PRIx16 ", lifetime %" PRIu16 "s", + if (IS_DEBUG_SNP_PACKETS) { + zlog_debug("ISIS-Snp (%s): Compare LSP %s seq 0x%08x, cksum 0x%04hx, lifetime %hus", areatag, rawlspid_print(lsp->hdr.lsp_id), seqno, checksum, rem_lifetime); zlog_debug( - "ISIS-Snp (%s): is older than ours seq 0x%08" PRIx32 - ", cksum 0x%04" PRIx16 ", lifetime %" PRIu16 "s", + "ISIS-Snp (%s): is older than ours seq 0x%08x, cksum 0x%04hx, lifetime %hus", areatag, lsp->hdr.seqno, lsp->hdr.checksum, lsp->hdr.rem_lifetime); } @@ -346,13 +338,17 @@ void lsp_inc_seqno(struct isis_lsp *lsp, uint32_t seqno) static void lsp_purge_add_poi(struct isis_lsp *lsp, const uint8_t *sender) { + if (lsp->area == NULL) + return; + if (!lsp->area->purge_originator) return; /* add purge originator identification */ if (!lsp->tlvs) lsp->tlvs = isis_alloc_tlvs(); - isis_tlvs_set_purge_originator(lsp->tlvs, isis->sysid, sender); + isis_tlvs_set_purge_originator(lsp->tlvs, lsp->area->isis->sysid, + sender); isis_tlvs_set_dynamic_hostname(lsp->tlvs, cmd_hostname_get()); } @@ -546,7 +542,7 @@ struct isis_lsp *lsp_new(struct isis_area *area, uint8_t *lsp_id, lsp_link_fragment(lsp, lsp0); put_lsp_hdr(lsp, NULL, false); - if (isis->debugs & DEBUG_EVENTS) + if (IS_DEBUG_EVENTS) zlog_debug("New LSP with ID %s-%02x-%02x len %d seqnum %08x", sysid_print(lsp_id), LSP_PSEUDO_ID(lsp->hdr.lsp_id), LSP_FRAGMENT(lsp->hdr.lsp_id), lsp->hdr.pdu_len, @@ -574,7 +570,7 @@ void lsp_build_list_nonzero_ht(struct lspdb_head *head, const uint8_t *start_id, memcpy(&searchfor.hdr.lsp_id, start_id, sizeof(searchfor.hdr.lsp_id)); start = lspdb_find_gteq(head, &searchfor); - for_each_from (lspdb, head, lsp, start) { + frr_each_from (lspdb, head, lsp, start) { if (memcmp(lsp->hdr.lsp_id, stop_id, ISIS_SYS_ID_LEN + 2) > 0) break; @@ -599,10 +595,11 @@ static void lsp_set_time(struct isis_lsp *lsp) stream_putw_at(lsp->pdu, 10, lsp->hdr.rem_lifetime); } -void lspid_print(uint8_t *lsp_id, char *dest, char dynhost, char frag) +void lspid_print(uint8_t *lsp_id, char *dest, char dynhost, char frag, + struct isis *isis) { struct isis_dynhn *dyn = NULL; - uint8_t id[SYSID_STRLEN]; + char id[SYSID_STRLEN]; if (dynhost) dyn = dynhn_find_by_id(lsp_id); @@ -610,11 +607,12 @@ void lspid_print(uint8_t *lsp_id, char *dest, char dynhost, char frag) dyn = NULL; if (dyn) - sprintf((char *)id, "%.14s", dyn->hostname); + snprintf(id, sizeof(id), "%.14s", dyn->hostname); else if (!memcmp(isis->sysid, lsp_id, ISIS_SYS_ID_LEN) && dynhost) - sprintf((char *)id, "%.14s", cmd_hostname_get()); + snprintf(id, sizeof(id), "%.14s", cmd_hostname_get()); else memcpy(id, sysid_print(lsp_id), 15); + if (frag) sprintf(dest, "%s.%02x-%02x", id, LSP_PSEUDO_ID(lsp_id), LSP_FRAGMENT(lsp_id)); @@ -646,29 +644,31 @@ static const char *lsp_bits2string(uint8_t lsp_bits, char *buf, size_t buf_size) } /* this function prints the lsp on show isis database */ -void lsp_print(struct isis_lsp *lsp, struct vty *vty, char dynhost) +void lsp_print(struct isis_lsp *lsp, struct vty *vty, char dynhost, + struct isis *isis) { char LSPid[255]; char age_out[8]; char b[200]; - lspid_print(lsp->hdr.lsp_id, LSPid, dynhost, 1); + lspid_print(lsp->hdr.lsp_id, LSPid, dynhost, 1, isis); vty_out(vty, "%-21s%c ", LSPid, lsp->own_lsp ? '*' : ' '); - vty_out(vty, "%5" PRIu16 " ", lsp->hdr.pdu_len); - vty_out(vty, "0x%08" PRIx32 " ", lsp->hdr.seqno); - vty_out(vty, "0x%04" PRIx16 " ", lsp->hdr.checksum); + vty_out(vty, "%5hu ", lsp->hdr.pdu_len); + vty_out(vty, "0x%08x ", lsp->hdr.seqno); + vty_out(vty, "0x%04hx ", lsp->hdr.checksum); if (lsp->hdr.rem_lifetime == 0) { - snprintf(age_out, 8, "(%d)", lsp->age_out); + snprintf(age_out, sizeof(age_out), "(%d)", lsp->age_out); age_out[7] = '\0'; vty_out(vty, "%7s ", age_out); } else - vty_out(vty, " %5" PRIu16 " ", lsp->hdr.rem_lifetime); + vty_out(vty, " %5hu ", lsp->hdr.rem_lifetime); vty_out(vty, "%s\n", lsp_bits2string(lsp->hdr.lsp_bits, b, sizeof(b))); } -void lsp_print_detail(struct isis_lsp *lsp, struct vty *vty, char dynhost) +void lsp_print_detail(struct isis_lsp *lsp, struct vty *vty, char dynhost, + struct isis *isis) { - lsp_print(lsp, vty, dynhost); + lsp_print(lsp, vty, dynhost, isis); if (lsp->tlvs) vty_multiline(vty, " ", "%s", isis_format_tlvs(lsp->tlvs)); vty_out(vty, "\n"); @@ -676,19 +676,19 @@ void lsp_print_detail(struct isis_lsp *lsp, struct vty *vty, char dynhost) /* print all the lsps info in the local lspdb */ int lsp_print_all(struct vty *vty, struct lspdb_head *head, char detail, - char dynhost) + char dynhost, struct isis *isis) { struct isis_lsp *lsp; int lsp_count = 0; if (detail == ISIS_UI_LEVEL_BRIEF) { - for_each (lspdb, head, lsp) { - lsp_print(lsp, vty, dynhost); + frr_each (lspdb, head, lsp) { + lsp_print(lsp, vty, dynhost, isis); lsp_count++; } } else if (detail == ISIS_UI_LEVEL_DETAIL) { - for_each (lspdb, head, lsp) { - lsp_print_detail(lsp, vty, dynhost); + frr_each (lspdb, head, lsp) { + lsp_print_detail(lsp, vty, dynhost, isis); lsp_count++; } } @@ -762,9 +762,15 @@ static void lsp_build_ext_reach_ipv4(struct isis_lsp *lsp, if (area->oldmetric) isis_tlvs_add_oldstyle_ip_reach(lsp->tlvs, ipv4, metric); - if (area->newmetric) - isis_tlvs_add_extended_ip_reach(lsp->tlvs, ipv4, - metric); + if (area->newmetric) { + struct sr_prefix_cfg *pcfg = NULL; + + if (area->srdb.enabled) + pcfg = isis_sr_cfg_prefix_find(area, ipv4); + + isis_tlvs_add_extended_ip_reach(lsp->tlvs, ipv4, metric, + true, pcfg); + } } } @@ -781,8 +787,8 @@ static void lsp_build_ext_reach_ipv6(struct isis_lsp *lsp, if (!rn->info) continue; struct isis_ext_info *info = rn->info; - struct prefix_ipv6 *p, *src_p; + srcdest_rnode_prefixes(rn, (const struct prefix **)&p, (const struct prefix **)&src_p); @@ -791,9 +797,14 @@ static void lsp_build_ext_reach_ipv6(struct isis_lsp *lsp, metric = MAX_WIDE_PATH_METRIC; if (!src_p || !src_p->prefixlen) { + struct sr_prefix_cfg *pcfg = NULL; + + if (area->srdb.enabled) + pcfg = isis_sr_cfg_prefix_find(area, p); + isis_tlvs_add_ipv6_reach(lsp->tlvs, isis_area_ipv6_topology(area), - p, metric); + p, metric, true, pcfg); } else if (isis_area_ipv6_dstsrc_enabled(area)) { isis_tlvs_add_ipv6_dstsrc_reach(lsp->tlvs, ISIS_MT_IPV6_DSTSRC, @@ -863,6 +874,7 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area) /* Protocols Supported */ if (area->ip_circuits > 0 || area->ipv6_circuits > 0) { struct nlpids nlpids = {.count = 0}; + if (area->ip_circuits > 0) { lsp_debug( "ISIS (%s): Found IPv4 circuit, adding IPv4 to NLPIDs", @@ -908,22 +920,63 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area) area->area_tag); } - /* IPv4 address and TE router ID TLVs. In case of the first one we don't - * follow "C" vendor, but "J" vendor behavior - one IPv4 address is put - * into - * LSP and this address is same as router id. */ - if (isis->router_id != 0) { - struct in_addr id = {.s_addr = isis->router_id}; + /* Add Router Capability TLV. */ + if (area->isis->router_id != 0) { + struct isis_router_cap cap = {}; + + cap.router_id.s_addr = area->isis->router_id; + + /* Add SR Sub-TLVs if SR is enabled. */ + if (area->srdb.enabled) { + struct isis_sr_db *srdb = &area->srdb; + uint32_t range_size; + + /* SRGB first */ + range_size = srdb->config.srgb_upper_bound + - srdb->config.srgb_lower_bound + 1; + cap.srgb.flags = ISIS_SUBTLV_SRGB_FLAG_I + | ISIS_SUBTLV_SRGB_FLAG_V; + cap.srgb.range_size = range_size; + cap.srgb.lower_bound = srdb->config.srgb_lower_bound; + /* Then Algorithm */ + cap.algo[0] = SR_ALGORITHM_SPF; + cap.algo[1] = SR_ALGORITHM_UNSET; + /* SRLB */ + cap.srlb.flags = 0; + range_size = srdb->config.srlb_upper_bound + - srdb->config.srlb_lower_bound + 1; + cap.srlb.range_size = range_size; + cap.srlb.lower_bound = srdb->config.srlb_lower_bound; + /* And finally MSD */ + cap.msd = srdb->config.msd; + } + + isis_tlvs_set_router_capability(lsp->tlvs, &cap); + lsp_debug("ISIS (%s): Adding Router Capabilities information", + area->area_tag); + } + + /* IPv4 address and TE router ID TLVs. + * In case of the first one we don't follow "C" vendor, + * but "J" vendor behavior - one IPv4 address is put + * into LSP. TE router ID will be the same if MPLS-TE + * is not activate or MPLS-TE router-id not specified + */ + if (area->isis->router_id != 0) { + struct in_addr id = {.s_addr = area->isis->router_id}; inet_ntop(AF_INET, &id, buf, sizeof(buf)); lsp_debug("ISIS (%s): Adding router ID %s as IPv4 tlv.", area->area_tag, buf); isis_tlvs_add_ipv4_address(lsp->tlvs, &id); - /* Exactly same data is put into TE router ID TLV, but only if - * new style - * TLV's are in use. */ + /* If new style TLV's are in use, add TE router ID TLV + * Check if MPLS-TE is activate and mpls-te router-id set + * otherwise add exactly same data as for IPv4 address + */ if (area->newmetric) { - + if (IS_MPLS_TE(area->mta) + && area->mta->router_id.s_addr != 0) + id.s_addr = area->mta->router_id.s_addr; lsp_debug( "ISIS (%s): Adding router ID also as TE router ID tlv.", area->area_tag); @@ -939,7 +992,7 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area) if (fabricd) { lsp_debug( - "ISIS (%s): Adding tier %" PRIu8 " spine-leaf-extension tlv.", + "ISIS (%s): Adding tier %hhu spine-leaf-extension tlv.", area->area_tag, fabricd_tier(area)); isis_tlvs_add_spine_leaf(lsp->tlvs, fabricd_tier(area), true, false, false, false); @@ -989,13 +1042,21 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area) } if (area->newmetric) { + struct sr_prefix_cfg *pcfg = NULL; + lsp_debug( "ISIS (%s): Adding te-style IP reachability for %s", area->area_tag, prefix2str(ipv4, buf, sizeof(buf))); + + if (area->srdb.enabled) + pcfg = isis_sr_cfg_prefix_find( + area, ipv4); + isis_tlvs_add_extended_ip_reach( - lsp->tlvs, ipv4, metric); + lsp->tlvs, ipv4, metric, false, + pcfg); } } } @@ -1004,16 +1065,24 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area) && circuit->ipv6_non_link->count > 0) { struct listnode *ipnode; struct prefix_ipv6 *ipv6; + for (ALL_LIST_ELEMENTS_RO(circuit->ipv6_non_link, ipnode, ipv6)) { + struct sr_prefix_cfg *pcfg = NULL; + lsp_debug( "ISIS (%s): Adding IPv6 reachability for %s", area->area_tag, prefix2str(ipv6, buf, sizeof(buf))); + + if (area->srdb.enabled) + pcfg = isis_sr_cfg_prefix_find(area, + ipv6); + isis_tlvs_add_ipv6_reach( lsp->tlvs, isis_area_ipv6_topology(area), ipv6, - metric); + metric, false, pcfg); } } @@ -1036,25 +1105,10 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area) lsp->tlvs, ne_id, metric); } - if (area->newmetric) { - uint8_t subtlvs[256]; - uint8_t subtlv_len; - - if (IS_MPLS_TE(area->mta) - && circuit->interface - && HAS_LINK_PARAMS( - circuit->interface)) - subtlv_len = add_te_subtlvs( - subtlvs, - circuit->mtc); - else - subtlv_len = 0; - + if (area->newmetric) tlvs_add_mt_bcast( lsp->tlvs, circuit, - level, ne_id, metric, - subtlvs, subtlv_len); - } + level, ne_id, metric); } } else { lsp_debug( @@ -1079,36 +1133,6 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area) lsp->tlvs, ne_id, metric); } if (area->newmetric) { - uint8_t subtlvs[256]; - uint8_t subtlv_len; - - if (IS_MPLS_TE(area->mta) - && circuit->interface != NULL - && HAS_LINK_PARAMS( - circuit->interface)) - /* Update Local and Remote IP - * address for MPLS TE circuit - * parameters */ - /* NOTE sure that it is the - * pertinent place for that - * updates */ - /* Local IP address could be - * updated in isis_circuit.c - - * isis_circuit_add_addr() */ - /* But, where update remote IP - * address ? in isis_pdu.c - - * process_p2p_hello() ? */ - - /* Add SubTLVs & Adjust real - * size of SubTLVs */ - subtlv_len = add_te_subtlvs( - subtlvs, circuit->mtc); - else - /* Or keep only TE metric with - * no SubTLVs if MPLS_TE is off - */ - subtlv_len = 0; - uint32_t neighbor_metric; if (fabricd_tier(area) == 0) { neighbor_metric = 0xffe; @@ -1117,8 +1141,7 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area) } tlvs_add_mt_p2p(lsp->tlvs, circuit, - ne_id, neighbor_metric, - subtlvs, subtlv_len); + ne_id, neighbor_metric); } } else { lsp_debug( @@ -1195,7 +1218,8 @@ int lsp_generate(struct isis_area *area, int level) return ISIS_ERROR; memset(&lspid, 0, ISIS_SYS_ID_LEN + 2); - memcpy(&lspid, isis->sysid, ISIS_SYS_ID_LEN); + + memcpy(&lspid, area->isis->sysid, ISIS_SYS_ID_LEN); /* only builds the lsp if the area shares the level */ oldlsp = lsp_search(&area->lspdb[level - 1], lspid); @@ -1230,10 +1254,8 @@ int lsp_generate(struct isis_area *area, int level) &area->lsp_refresh_arg[level - 1], refresh_time, &area->t_lsp_refresh[level - 1]); - if (isis->debugs & DEBUG_UPDATE_PACKETS) { - zlog_debug("ISIS-Upd (%s): Building L%d LSP %s, len %" PRIu16 - ", seq 0x%08" PRIx32 ", cksum 0x%04" PRIx16 - ", lifetime %" PRIu16 "s refresh %" PRIu16 "s", + if (IS_DEBUG_UPDATE_PACKETS) { + zlog_debug("ISIS-Upd (%s): Building L%d LSP %s, len %hu, seq 0x%08x, cksum 0x%04hx, lifetime %hus refresh %hus", area->area_tag, level, rawlspid_print(newlsp->hdr.lsp_id), newlsp->hdr.pdu_len, newlsp->hdr.seqno, @@ -1268,9 +1290,8 @@ static int lsp_regenerate(struct isis_area *area, int level) return ISIS_ERROR; head = &area->lspdb[level - 1]; - memset(lspid, 0, ISIS_SYS_ID_LEN + 2); - memcpy(lspid, isis->sysid, ISIS_SYS_ID_LEN); + memcpy(lspid, area->isis->sysid, ISIS_SYS_ID_LEN); lsp = lsp_search(head, lspid); @@ -1314,11 +1335,9 @@ static int lsp_regenerate(struct isis_area *area, int level) &area->t_lsp_refresh[level - 1]); area->lsp_regenerate_pending[level - 1] = 0; - if (isis->debugs & DEBUG_UPDATE_PACKETS) { + if (IS_DEBUG_UPDATE_PACKETS) { zlog_debug( - "ISIS-Upd (%s): Refreshed our L%d LSP %s, len %" PRIu16 - ", seq 0x%08" PRIx32 ", cksum 0x%04" PRIx16 - ", lifetime %" PRIu16 "s refresh %" PRIu16 "s", + "ISIS-Upd (%s): Refreshed our L%d LSP %s, len %hu, seq 0x%08x, cksum 0x%04hx, lifetime %hus refresh %hus", area->area_tag, level, rawlspid_print(lsp->hdr.lsp_id), lsp->hdr.pdu_len, lsp->hdr.seqno, lsp->hdr.checksum, lsp->hdr.rem_lifetime, refresh_time); @@ -1351,7 +1370,13 @@ static int lsp_refresh(struct thread *thread) if ((area->is_type & level) == 0) return ISIS_ERROR; - if (monotime_since(&area->last_lsp_refresh_event[level - 1], NULL) < 100000L) { + /* + * Throttle regeneration of LSPs (but not when BFD signalled a 'down' + * message) + */ + if (monotime_since(&area->last_lsp_refresh_event[level - 1], NULL) + < 100000L + && !(area->bfd_force_spf_refresh)) { sched_debug("ISIS (%s): Still unstable, postpone LSP L%d refresh", area->area_tag, level); _lsp_regenerate_schedule(area, level, 0, false, @@ -1382,13 +1407,12 @@ int _lsp_regenerate_schedule(struct isis_area *area, int level, return ISIS_ERROR; sched_debug( - "ISIS (%s): Scheduling regeneration of %s LSPs, %sincluding PSNs" - " Caller: %s %s:%d", + "ISIS (%s): Scheduling regeneration of %s LSPs, %sincluding PSNs Caller: %s %s:%d", area->area_tag, circuit_t2string(level), all_pseudo ? "" : "not ", func, file, line); - memcpy(id, isis->sysid, ISIS_SYS_ID_LEN); + memcpy(id, area->isis->sysid, ISIS_SYS_ID_LEN); LSP_PSEUDO_ID(id) = LSP_FRAGMENT(id) = 0; now = time(NULL); @@ -1404,12 +1428,16 @@ int _lsp_regenerate_schedule(struct isis_area *area, int level, "ISIS (%s): Checking whether L%d needs to be scheduled", area->area_tag, lvl); - if (area->lsp_regenerate_pending[lvl - 1]) { + if (area->lsp_regenerate_pending[lvl - 1] + && !(area->bfd_signalled_down)) { + /* + * Note: in case of a BFD 'down' message the refresh is + * scheduled once again just to be sure + */ struct timeval remain = thread_timer_remain( area->t_lsp_refresh[lvl - 1]); sched_debug( - "ISIS (%s): Regeneration is already pending, nothing todo." - " (Due in %lld.%03lld seconds)", + "ISIS (%s): Regeneration is already pending, nothing todo. (Due in %lld.%03lld seconds)", area->area_tag, (long long)remain.tv_sec, (long long)remain.tv_usec / 1000); continue; @@ -1432,7 +1460,8 @@ int _lsp_regenerate_schedule(struct isis_area *area, int level, (long long)now); THREAD_TIMER_OFF(area->t_lsp_refresh[lvl - 1]); diff = now - lsp->last_generated; - if (diff < area->lsp_gen_interval[lvl - 1]) { + if (diff < area->lsp_gen_interval[lvl - 1] + && !(area->bfd_signalled_down)) { timeout = 1000 * (area->lsp_gen_interval[lvl - 1] - diff); sched_debug( @@ -1440,17 +1469,21 @@ int _lsp_regenerate_schedule(struct isis_area *area, int level, area->area_tag, timeout); } else { /* - * lsps are not regenerated if lsp_regenerate function - * is called - * directly. However if the lsp_regenerate call is - * queued for - * later execution it works. + * Schedule LSP refresh ASAP */ - timeout = 100; - sched_debug( - "ISIS (%s): Last generation was more than lsp_gen_interval ago." - " Scheduling for execution in %ld ms.", - area->area_tag, timeout); + timeout = 0; + + if (area->bfd_signalled_down) { + sched_debug( + "ISIS (%s): Scheduling immediately due to BFD 'down' message.", + area->area_tag); + area->bfd_signalled_down = false; + area->bfd_force_spf_refresh = true; + } else { + sched_debug( + "ISIS (%s): Last generation was more than lsp_gen_interval ago. Scheduling for execution now.", + area->area_tag); + } } area->lsp_regenerate_pending[lvl - 1] = 1; @@ -1500,7 +1533,7 @@ static void lsp_build_pseudo(struct isis_lsp *lsp, struct isis_circuit *circuit, */ uint8_t ne_id[ISIS_SYS_ID_LEN + 1]; - memcpy(ne_id, isis->sysid, ISIS_SYS_ID_LEN); + memcpy(ne_id, area->isis->sysid, ISIS_SYS_ID_LEN); LSP_PSEUDO_ID(ne_id) = 0; if (circuit->area->oldmetric) { @@ -1512,7 +1545,7 @@ static void lsp_build_pseudo(struct isis_lsp *lsp, struct isis_circuit *circuit, } if (circuit->area->newmetric) { isis_tlvs_add_extended_reach(lsp->tlvs, ISIS_MT_IPV4_UNICAST, - ne_id, 0, NULL, 0); + ne_id, 0, NULL); lsp_debug( "ISIS (%s): Adding %s.%02x as te-style neighbor (self)", area->area_tag, sysid_print(ne_id), @@ -1554,7 +1587,7 @@ static void lsp_build_pseudo(struct isis_lsp *lsp, struct isis_circuit *circuit, if (circuit->area->newmetric) { isis_tlvs_add_extended_reach(lsp->tlvs, ISIS_MT_IPV4_UNICAST, - ne_id, 0, NULL, 0); + ne_id, 0, NULL); lsp_debug( "ISIS (%s): Adding %s.%02x as te-style neighbor (peer)", area->area_tag, sysid_print(ne_id), @@ -1578,7 +1611,7 @@ int lsp_generate_pseudo(struct isis_circuit *circuit, int level) || (circuit->u.bc.is_dr[level - 1] == 0)) return ISIS_ERROR; - memcpy(lsp_id, isis->sysid, ISIS_SYS_ID_LEN); + memcpy(lsp_id, circuit->area->isis->sysid, ISIS_SYS_ID_LEN); LSP_FRAGMENT(lsp_id) = 0; LSP_PSEUDO_ID(lsp_id) = circuit->circuit_id; @@ -1613,11 +1646,9 @@ int lsp_generate_pseudo(struct isis_circuit *circuit, int level) master, lsp_l2_refresh_pseudo, circuit, refresh_time, &circuit->u.bc.t_refresh_pseudo_lsp[level - 1]); - if (isis->debugs & DEBUG_UPDATE_PACKETS) { + if (IS_DEBUG_UPDATE_PACKETS) { zlog_debug( - "ISIS-Upd (%s): Built L%d Pseudo LSP %s, len %" PRIu16 - ", seq 0x%08" PRIx32 ", cksum 0x%04" PRIx16 - ", lifetime %" PRIu16 "s, refresh %" PRIu16 "s", + "ISIS-Upd (%s): Built L%d Pseudo LSP %s, len %hu, seq 0x%08x, cksum 0x%04hx, lifetime %hus, refresh %hus", circuit->area->area_tag, level, rawlspid_print(lsp->hdr.lsp_id), lsp->hdr.pdu_len, lsp->hdr.seqno, lsp->hdr.checksum, @@ -1640,7 +1671,7 @@ static int lsp_regenerate_pseudo(struct isis_circuit *circuit, int level) || (circuit->u.bc.is_dr[level - 1] == 0)) return ISIS_ERROR; - memcpy(lsp_id, isis->sysid, ISIS_SYS_ID_LEN); + memcpy(lsp_id, circuit->area->isis->sysid, ISIS_SYS_ID_LEN); LSP_PSEUDO_ID(lsp_id) = circuit->circuit_id; LSP_FRAGMENT(lsp_id) = 0; @@ -1670,11 +1701,9 @@ static int lsp_regenerate_pseudo(struct isis_circuit *circuit, int level) master, lsp_l2_refresh_pseudo, circuit, refresh_time, &circuit->u.bc.t_refresh_pseudo_lsp[level - 1]); - if (isis->debugs & DEBUG_UPDATE_PACKETS) { + if (IS_DEBUG_UPDATE_PACKETS) { zlog_debug( - "ISIS-Upd (%s): Refreshed L%d Pseudo LSP %s, len %" PRIu16 - ", seq 0x%08" PRIx32 ", cksum 0x%04" PRIx16 - ", lifetime %" PRIu16 "s, refresh %" PRIu16 "s", + "ISIS-Upd (%s): Refreshed L%d Pseudo LSP %s, len %hu, seq 0x%08x, cksum 0x%04hx, lifetime %hus, refresh %hus", circuit->area->area_tag, level, rawlspid_print(lsp->hdr.lsp_id), lsp->hdr.pdu_len, lsp->hdr.seqno, lsp->hdr.checksum, @@ -1699,7 +1728,7 @@ static int lsp_l1_refresh_pseudo(struct thread *thread) if ((circuit->u.bc.is_dr[0] == 0) || (circuit->is_type & IS_LEVEL_1) == 0) { - memcpy(id, isis->sysid, ISIS_SYS_ID_LEN); + memcpy(id, circuit->area->isis->sysid, ISIS_SYS_ID_LEN); LSP_PSEUDO_ID(id) = circuit->circuit_id; LSP_FRAGMENT(id) = 0; lsp_purge_pseudo(id, circuit, IS_LEVEL_1); @@ -1721,7 +1750,7 @@ static int lsp_l2_refresh_pseudo(struct thread *thread) if ((circuit->u.bc.is_dr[1] == 0) || (circuit->is_type & IS_LEVEL_2) == 0) { - memcpy(id, isis->sysid, ISIS_SYS_ID_LEN); + memcpy(id, circuit->area->isis->sysid, ISIS_SYS_ID_LEN); LSP_PSEUDO_ID(id) = circuit->circuit_id; LSP_FRAGMENT(id) = 0; lsp_purge_pseudo(id, circuit, IS_LEVEL_2); @@ -1749,7 +1778,7 @@ int lsp_regenerate_schedule_pseudo(struct isis_circuit *circuit, int level) area->area_tag, circuit_t2string(level), circuit->interface->name); - memcpy(lsp_id, isis->sysid, ISIS_SYS_ID_LEN); + memcpy(lsp_id, area->isis->sysid, ISIS_SYS_ID_LEN); LSP_PSEUDO_ID(lsp_id) = circuit->circuit_id; LSP_FRAGMENT(lsp_id) = 0; now = time(NULL); @@ -1776,8 +1805,7 @@ int lsp_regenerate_schedule_pseudo(struct isis_circuit *circuit, int level) struct timeval remain = thread_timer_remain( circuit->u.bc.t_refresh_pseudo_lsp[lvl - 1]); sched_debug( - "ISIS (%s): Regenerate is already pending, nothing todo." - " (Due in %lld.%03lld seconds)", + "ISIS (%s): Regenerate is already pending, nothing todo. (Due in %lld.%03lld seconds)", area->area_tag, (long long)remain.tv_sec, (long long)remain.tv_usec / 1000); continue; @@ -1810,8 +1838,7 @@ int lsp_regenerate_schedule_pseudo(struct isis_circuit *circuit, int level) } else { timeout = 100; sched_debug( - "ISIS (%s): Last generation was more than lsp_gen_interval ago." - " Scheduling for execution in %ld ms.", + "ISIS (%s): Last generation was more than lsp_gen_interval ago. Scheduling for execution in %ld ms.", area->area_tag, timeout); } @@ -1855,7 +1882,7 @@ int lsp_tick(struct thread *thread) */ for (level = 0; level < ISIS_LEVELS; level++) { struct isis_lsp *next = lspdb_first(&area->lspdb[level]); - for_each_from (lspdb, &area->lspdb[level], lsp, next) { + frr_each_from (lspdb, &area->lspdb[level], lsp, next) { /* * The lsp rem_lifetime is kept at 0 for MaxAge * or @@ -1889,8 +1916,7 @@ int lsp_tick(struct thread *thread) if (lsp->age_out == 0) { zlog_debug( - "ISIS-Upd (%s): L%u LSP %s seq " - "0x%08" PRIx32 " aged out", + "ISIS-Upd (%s): L%u LSP %s seq 0x%08x aged out", area->area_tag, lsp->level, rawlspid_print(lsp->hdr.lsp_id), lsp->hdr.seqno); @@ -1997,7 +2023,7 @@ void lsp_set_all_srmflags(struct isis_lsp *lsp, bool set) void _lsp_flood(struct isis_lsp *lsp, struct isis_circuit *circuit, const char *func, const char *file, int line) { - if (isis->debugs & DEBUG_FLOODING) { + if (IS_DEBUG_FLOODING) { zlog_debug("Flooding LSP %s%s%s (From %s %s:%d)", rawlspid_print(lsp->hdr.lsp_id), circuit ? " except on " : "", @@ -2020,6 +2046,160 @@ static int lsp_handle_adj_state_change(struct isis_adjacency *adj) return 0; } +/* + * Iterate over all IP reachability TLVs in a LSP (all fragments) of the given + * address-family and MT-ID. + */ +int isis_lsp_iterate_ip_reach(struct isis_lsp *lsp, int family, uint16_t mtid, + lsp_ip_reach_iter_cb cb, void *arg) +{ + bool pseudo_lsp = LSP_PSEUDO_ID(lsp->hdr.lsp_id); + struct isis_lsp *frag; + struct listnode *node; + + if (lsp->hdr.seqno == 0 || lsp->hdr.rem_lifetime == 0) + return LSP_ITER_CONTINUE; + + /* Parse main LSP. */ + if (lsp->tlvs) { + if (!fabricd && !pseudo_lsp && family == AF_INET + && mtid == ISIS_MT_IPV4_UNICAST) { + struct isis_item_list *reachs[] = { + &lsp->tlvs->oldstyle_ip_reach, + &lsp->tlvs->oldstyle_ip_reach_ext}; + + for (unsigned int i = 0; i < array_size(reachs); i++) { + struct isis_oldstyle_ip_reach *r; + + for (r = (struct isis_oldstyle_ip_reach *) + reachs[i] + ->head; + r; r = r->next) { + bool external = i ? true : false; + + if ((*cb)((struct prefix *)&r->prefix, + r->metric, external, NULL, + arg) + == LSP_ITER_STOP) + return LSP_ITER_STOP; + } + } + } + + if (!pseudo_lsp && family == AF_INET) { + struct isis_item_list *ipv4_reachs; + + if (mtid == ISIS_MT_IPV4_UNICAST) + ipv4_reachs = &lsp->tlvs->extended_ip_reach; + else + ipv4_reachs = isis_lookup_mt_items( + &lsp->tlvs->mt_ip_reach, mtid); + + struct isis_extended_ip_reach *r; + for (r = ipv4_reachs ? (struct isis_extended_ip_reach *) + ipv4_reachs->head + : NULL; + r; r = r->next) { + if ((*cb)((struct prefix *)&r->prefix, + r->metric, false, r->subtlvs, arg) + == LSP_ITER_STOP) + return LSP_ITER_STOP; + } + } + + if (!pseudo_lsp && family == AF_INET6) { + struct isis_item_list *ipv6_reachs; + struct isis_ipv6_reach *r; + + if (mtid == ISIS_MT_IPV4_UNICAST) + ipv6_reachs = &lsp->tlvs->ipv6_reach; + else + ipv6_reachs = isis_lookup_mt_items( + &lsp->tlvs->mt_ipv6_reach, mtid); + + for (r = ipv6_reachs ? (struct isis_ipv6_reach *) + ipv6_reachs->head + : NULL; + r; r = r->next) { + if ((*cb)((struct prefix *)&r->prefix, + r->metric, r->external, r->subtlvs, + arg) + == LSP_ITER_STOP) + return LSP_ITER_STOP; + } + } + } + + /* Parse LSP fragments. */ + for (ALL_LIST_ELEMENTS_RO(lsp->lspu.frags, node, frag)) { + if (!frag->tlvs) + continue; + + isis_lsp_iterate_ip_reach(frag, family, mtid, cb, arg); + } + + return LSP_ITER_CONTINUE; +} + +/* + * Iterate over all IS reachability TLVs in a LSP (all fragments) of the given + * MT-ID. + */ +int isis_lsp_iterate_is_reach(struct isis_lsp *lsp, uint16_t mtid, + lsp_is_reach_iter_cb cb, void *arg) +{ + bool pseudo_lsp = LSP_PSEUDO_ID(lsp->hdr.lsp_id); + struct isis_lsp *frag; + struct listnode *node; + struct isis_item *head; + struct isis_item_list *te_neighs; + + if (lsp->hdr.seqno == 0 || lsp->hdr.rem_lifetime == 0) + return LSP_ITER_CONTINUE; + + /* Parse main LSP. */ + if (lsp->tlvs) { + if (pseudo_lsp || mtid == ISIS_MT_IPV4_UNICAST) { + head = lsp->tlvs->oldstyle_reach.head; + for (struct isis_oldstyle_reach *reach = + (struct isis_oldstyle_reach *)head; + reach; reach = reach->next) { + if ((*cb)(reach->id, reach->metric, true, NULL, + arg) + == LSP_ITER_STOP) + return LSP_ITER_STOP; + } + } + + if (pseudo_lsp || mtid == ISIS_MT_IPV4_UNICAST) + te_neighs = &lsp->tlvs->extended_reach; + else + te_neighs = + isis_get_mt_items(&lsp->tlvs->mt_reach, mtid); + if (te_neighs) { + head = te_neighs->head; + for (struct isis_extended_reach *reach = + (struct isis_extended_reach *)head; + reach; reach = reach->next) { + if ((*cb)(reach->id, reach->metric, false, + reach->subtlvs, arg) + == LSP_ITER_STOP) + return LSP_ITER_STOP; + } + } + } + + /* Parse LSP fragments. */ + for (ALL_LIST_ELEMENTS_RO(lsp->lspu.frags, node, frag)) { + if (!frag->tlvs) + continue; + + isis_lsp_iterate_is_reach(frag, mtid, cb, arg); + } + + return LSP_ITER_CONTINUE; +} + void lsp_init(void) { hook_register(isis_adj_state_change_hook, diff --git a/isisd/isis_lsp.h b/isisd/isis_lsp.h index 4cbca5d517..0783036e49 100644 --- a/isisd/isis_lsp.h +++ b/isisd/isis_lsp.h @@ -29,6 +29,7 @@ PREDECL_RBTREE_UNIQ(lspdb) +struct isis; /* Structure for isis_lsp, this structure will only support the fixed * System ID (Currently 6) (atleast for now). In order to support more * We will have to split the header into two parts, and for readability @@ -115,14 +116,37 @@ void lsp_update(struct isis_lsp *lsp, struct isis_lsp_hdr *hdr, struct isis_tlvs *tlvs, struct stream *stream, struct isis_area *area, int level, bool confusion); void lsp_inc_seqno(struct isis_lsp *lsp, uint32_t seqno); -void lspid_print(uint8_t *lsp_id, char *dest, char dynhost, char frag); -void lsp_print(struct isis_lsp *lsp, struct vty *vty, char dynhost); -void lsp_print_detail(struct isis_lsp *lsp, struct vty *vty, char dynhost); +void lspid_print(uint8_t *lsp_id, char *dest, char dynhost, char frag, + struct isis *isis); +void lsp_print(struct isis_lsp *lsp, struct vty *vty, char dynhost, + struct isis *isis); +void lsp_print_detail(struct isis_lsp *lsp, struct vty *vty, char dynhost, + struct isis *isis); int lsp_print_all(struct vty *vty, struct lspdb_head *head, char detail, - char dynhost); + char dynhost, struct isis *isis); /* sets SRMflags for all active circuits of an lsp */ void lsp_set_all_srmflags(struct isis_lsp *lsp, bool set); +#define LSP_ITER_CONTINUE 0 +#define LSP_ITER_STOP -1 + +/* Callback used by isis_lsp_iterate_ip_reach() function. */ +struct isis_subtlvs; +typedef int (*lsp_ip_reach_iter_cb)(const struct prefix *prefix, + uint32_t metric, bool external, + struct isis_subtlvs *subtlvs, void *arg); + +/* Callback used by isis_lsp_iterate_is_reach() function. */ +typedef int (*lsp_is_reach_iter_cb)(const uint8_t *id, uint32_t metric, + bool oldmetric, + struct isis_ext_subtlvs *subtlvs, + void *arg); + +int isis_lsp_iterate_ip_reach(struct isis_lsp *lsp, int family, uint16_t mtid, + lsp_ip_reach_iter_cb cb, void *arg); +int isis_lsp_iterate_is_reach(struct isis_lsp *lsp, uint16_t mtid, + lsp_is_reach_iter_cb cb, void *arg); + #define lsp_flood(lsp, circuit) \ _lsp_flood((lsp), (circuit), __func__, __FILE__, __LINE__) void _lsp_flood(struct isis_lsp *lsp, struct isis_circuit *circuit, diff --git a/isisd/isis_main.c b/isisd/isis_main.c index 48ae760173..26f5227aae 100644 --- a/isisd/isis_main.c +++ b/isisd/isis_main.c @@ -29,7 +29,6 @@ #include "command.h" #include "vty.h" #include "memory.h" -#include "memory_vty.h" #include "stream.h" #include "if.h" #include "privs.h" @@ -40,6 +39,7 @@ #include "vrf.h" #include "qobj.h" #include "libfrr.h" +#include "routemap.h" #include "isisd/isis_constants.h" #include "isisd/isis_common.h" @@ -53,11 +53,11 @@ #include "isisd/isis_zebra.h" #include "isisd/isis_te.h" #include "isisd/isis_errors.h" -#include "isisd/isis_vty_common.h" #include "isisd/isis_bfd.h" #include "isisd/isis_lsp.h" #include "isisd/isis_mt.h" #include "isisd/fabricd.h" +#include "isisd/isis_nb.h" /* Default configuration file name */ #define ISISD_DEFAULT_CONFIG "isisd.conf" @@ -66,7 +66,7 @@ #define FABRICD_VTY_PORT 2618 /* isisd privileges */ -zebra_capabilities_t _caps_p[] = {ZCAP_NET_RAW, ZCAP_BIND}; +zebra_capabilities_t _caps_p[] = {ZCAP_NET_RAW, ZCAP_BIND, ZCAP_SYS_ADMIN}; struct zebra_privs_t isisd_privs = { #if defined(FRR_USER) @@ -83,7 +83,9 @@ struct zebra_privs_t isisd_privs = { .cap_num_i = 0}; /* isisd options */ -struct option longopts[] = {{0}}; +static const struct option longopts[] = { + {"int_num", required_argument, NULL, 'I'}, + {0}}; /* Master of threads. */ struct thread_master *master; @@ -99,6 +101,8 @@ void sigusr1(void); static __attribute__((__noreturn__)) void terminate(int i) { + isis_terminate(); + isis_sr_term(); isis_zebra_stop(); exit(i); } @@ -162,11 +166,14 @@ struct quagga_signal_t isisd_signals[] = { }; -static const struct frr_yang_module_info *isisd_yang_modules[] = { +static const struct frr_yang_module_info *const isisd_yang_modules[] = { + &frr_filter_info, &frr_interface_info, #ifndef FABRICD &frr_isisd_info, #endif /* ifndef FABRICD */ + &frr_route_map_info, + &frr_vrf_info, }; #ifdef FABRICD @@ -179,8 +186,7 @@ FRR_DAEMON_INFO(isisd, ISIS, .vty_port = ISISD_VTY_PORT, .proghelp = "Implementation of the IS-IS routing protocol.", #endif .copyright = - "Copyright (c) 2001-2002 Sampo Saaristo," - " Ofer Wald and Hannes Gredler", + "Copyright (c) 2001-2002 Sampo Saaristo, Ofer Wald and Hannes Gredler", .signals = isisd_signals, .n_signals = array_size(isisd_signals), @@ -194,13 +200,16 @@ FRR_DAEMON_INFO(isisd, ISIS, .vty_port = ISISD_VTY_PORT, int main(int argc, char **argv, char **envp) { int opt; + int instance = 1; #ifdef FABRICD frr_preinit(&fabricd_di, argc, argv); #else frr_preinit(&isisd_di, argc, argv); #endif - frr_opt_add("", longopts, ""); + frr_opt_add( + "I:", longopts, + " -I, --int_num Set instance number (label-manager)\n"); /* Command line argument treatment. */ while (1) { @@ -212,6 +221,12 @@ int main(int argc, char **argv, char **envp) switch (opt) { case 0: break; + case 'I': + instance = atoi(optarg); + if (instance < 1 || instance > (unsigned short)-1) + zlog_err("Instance %i out of range (1..%u)", + instance, (unsigned short)-1); + break; default: frr_help_exit(1); break; @@ -219,32 +234,35 @@ int main(int argc, char **argv, char **envp) } /* thread master */ - master = frr_init(); - + isis_master_init(frr_init()); + master = im->master; /* * initializations */ isis_error_init(); access_list_init(); - vrf_init(NULL, NULL, NULL, NULL, NULL); + isis_vrf_init(); prefix_list_init(); isis_init(); isis_circuit_init(); - isis_vty_init(); +#ifdef FABRICD + isis_vty_daemon_init(); +#endif /* FABRICD */ #ifndef FABRICD isis_cli_init(); #endif /* ifdef FABRICD */ - isis_spf_cmds_init(); + isis_spf_init(); isis_redist_init(); isis_route_map_init(); isis_mpls_te_init(); + isis_sr_init(); lsp_init(); mt_init(); /* create the global 'isis' instance */ - isis_new(1); + isis_global_instance_create(VRF_DEFAULT_NAME); - isis_zebra_init(master); + isis_zebra_init(master, instance); isis_bfd_init(); fabricd_init(); diff --git a/isisd/isis_memory.c b/isisd/isis_memory.c index 7d1ad6b049..2725459767 100644 --- a/isisd/isis_memory.c +++ b/isisd/isis_memory.c @@ -39,7 +39,6 @@ DEFINE_MTYPE(ISISD, ISIS_SPFTREE, "ISIS SPFtree") DEFINE_MTYPE(ISISD, ISIS_VERTEX, "ISIS vertex") DEFINE_MTYPE(ISISD, ISIS_ROUTE_INFO, "ISIS route info") DEFINE_MTYPE(ISISD, ISIS_NEXTHOP, "ISIS nexthop") -DEFINE_MTYPE(ISISD, ISIS_NEXTHOP6, "ISIS nexthop6") DEFINE_MTYPE(ISISD, ISIS_DICT, "ISIS dictionary") DEFINE_MTYPE(ISISD, ISIS_DICT_NODE, "ISIS dictionary node") DEFINE_MTYPE(ISISD, ISIS_EXT_ROUTE, "ISIS redistributed route") diff --git a/isisd/isis_memory.h b/isisd/isis_memory.h index 4078c7a671..e672340e84 100644 --- a/isisd/isis_memory.h +++ b/isisd/isis_memory.h @@ -38,7 +38,6 @@ DECLARE_MTYPE(ISIS_SPFTREE) DECLARE_MTYPE(ISIS_VERTEX) DECLARE_MTYPE(ISIS_ROUTE_INFO) DECLARE_MTYPE(ISIS_NEXTHOP) -DECLARE_MTYPE(ISIS_NEXTHOP6) DECLARE_MTYPE(ISIS_DICT) DECLARE_MTYPE(ISIS_DICT_NODE) DECLARE_MTYPE(ISIS_EXT_ROUTE) diff --git a/isisd/isis_misc.c b/isisd/isis_misc.c index 0a42adea37..6e9cbaf98e 100644 --- a/isisd/isis_misc.c +++ b/isisd/isis_misc.c @@ -23,11 +23,13 @@ #include +#include "printfrr.h" #include "stream.h" #include "vty.h" #include "hash.h" #include "if.h" #include "command.h" +#include "network.h" #include "isisd/isis_constants.h" #include "isisd/isis_common.h" @@ -116,7 +118,8 @@ int dotformat2buff(uint8_t *buff, const char *dotted) break; } - if ((isxdigit((int)*pos)) && (isxdigit((int)*(pos + 1)))) { + if ((isxdigit((unsigned char)*pos)) && + (isxdigit((unsigned char)*(pos + 1)))) { memcpy(number, pos, 2); pos += 2; } else { @@ -156,7 +159,8 @@ int sysid2buff(uint8_t *buff, const char *dotted) pos++; continue; } - if ((isxdigit((int)*pos)) && (isxdigit((int)*(pos + 1)))) { + if ((isxdigit((unsigned char)*pos)) && + (isxdigit((unsigned char)*(pos + 1)))) { memcpy(number, pos, 2); pos += 2; } else { @@ -186,7 +190,7 @@ const char *nlpid2str(uint8_t nlpid) case NLPID_ESIS: return "ES-IS"; default: - snprintf(buf, sizeof(buf), "%" PRIu8, nlpid); + snprintf(buf, sizeof(buf), "%hhu", nlpid); return buf; } } @@ -410,7 +414,7 @@ unsigned long isis_jitter(unsigned long timer, unsigned long jitter) * most IS-IS timers are no longer than 16 bit */ - j = 1 + (int)((RANDOM_SPREAD * random()) / (RAND_MAX + 1.0)); + j = 1 + (int)((RANDOM_SPREAD * frr_weak_random()) / (RAND_MAX + 1.0)); k = timer - (timer * (100 - jitter)) / 100; @@ -436,12 +440,14 @@ struct in_addr newprefix2inaddr(uint8_t *prefix_start, uint8_t prefix_masklen) const char *print_sys_hostname(const uint8_t *sysid) { struct isis_dynhn *dyn; + struct isis *isis = NULL; if (!sysid) return "nullsysid"; /* For our system ID return our host name */ - if (memcmp(sysid, isis->sysid, ISIS_SYS_ID_LEN) == 0) + isis = isis_lookup_by_sysid(sysid); + if (isis && !CHECK_FLAG(im->options, F_ISIS_UNIT_TEST)) return cmd_hostname_get(); dyn = dynhn_find_by_id(sysid); @@ -511,42 +517,14 @@ void zlog_dump_data(void *data, int len) return; } -static char *qasprintf(const char *format, va_list ap) -{ - va_list aq; - va_copy(aq, ap); - - int size = 0; - char *p = NULL; - - size = vsnprintf(p, size, format, ap); - - if (size < 0) { - va_end(aq); - return NULL; - } - - size++; - p = XMALLOC(MTYPE_TMP, size); - - size = vsnprintf(p, size, format, aq); - va_end(aq); - - if (size < 0) { - XFREE(MTYPE_TMP, p); - return NULL; - } - - return p; -} - void log_multiline(int priority, const char *prefix, const char *format, ...) { + char shortbuf[256]; va_list ap; char *p; va_start(ap, format); - p = qasprintf(format, ap); + p = vasnprintfrr(MTYPE_TMP, shortbuf, sizeof(shortbuf), format, ap); va_end(ap); if (!p) @@ -558,16 +536,38 @@ void log_multiline(int priority, const char *prefix, const char *format, ...) zlog(priority, "%s%s", prefix, line); } - XFREE(MTYPE_TMP, p); + if (p != shortbuf) + XFREE(MTYPE_TMP, p); +} + +char *log_uptime(time_t uptime, char *buf, size_t nbuf) +{ + struct tm *tm; + time_t difftime = time(NULL); + difftime -= uptime; + tm = gmtime(&difftime); + + if (difftime < ONE_DAY_SECOND) + snprintf(buf, nbuf, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min, + tm->tm_sec); + else if (difftime < ONE_WEEK_SECOND) + snprintf(buf, nbuf, "%dd%02dh%02dm", tm->tm_yday, tm->tm_hour, + tm->tm_min); + else + snprintf(buf, nbuf, "%02dw%dd%02dh", tm->tm_yday / 7, + tm->tm_yday - ((tm->tm_yday / 7) * 7), tm->tm_hour); + + return buf; } void vty_multiline(struct vty *vty, const char *prefix, const char *format, ...) { + char shortbuf[256]; va_list ap; char *p; va_start(ap, format); - p = qasprintf(format, ap); + p = vasnprintfrr(MTYPE_TMP, shortbuf, sizeof(shortbuf), format, ap); va_end(ap); if (!p) @@ -579,24 +579,18 @@ void vty_multiline(struct vty *vty, const char *prefix, const char *format, ...) vty_out(vty, "%s%s\n", prefix, line); } - XFREE(MTYPE_TMP, p); + if (p != shortbuf) + XFREE(MTYPE_TMP, p); } void vty_out_timestr(struct vty *vty, time_t uptime) { - struct tm *tm; time_t difftime = time(NULL); + char buf[MONOTIME_STRLEN]; + difftime -= uptime; - tm = gmtime(&difftime); - if (difftime < ONE_DAY_SECOND) - vty_out(vty, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min, - tm->tm_sec); - else if (difftime < ONE_WEEK_SECOND) - vty_out(vty, "%dd%02dh%02dm", tm->tm_yday, tm->tm_hour, - tm->tm_min); - else - vty_out(vty, "%02dw%dd%02dh", tm->tm_yday / 7, - tm->tm_yday - ((tm->tm_yday / 7) * 7), tm->tm_hour); - vty_out(vty, " ago"); + frrtime_to_interval(difftime, buf, sizeof(buf)); + + vty_out(vty, "%s ago", buf); } diff --git a/isisd/isis_misc.h b/isisd/isis_misc.h index c551fde7d1..5cdbbfb058 100644 --- a/isisd/isis_misc.h +++ b/isisd/isis_misc.h @@ -79,9 +79,10 @@ enum { ISIS_UI_LEVEL_BRIEF, #include "lib/log.h" void log_multiline(int priority, const char *prefix, const char *format, ...) - PRINTF_ATTRIBUTE(3, 4); + PRINTFRR(3, 4); +char *log_uptime(time_t uptime, char *buf, size_t nbuf); struct vty; void vty_multiline(struct vty *vty, const char *prefix, const char *format, ...) - PRINTF_ATTRIBUTE(3, 4); + PRINTFRR(3, 4); void vty_out_timestr(struct vty *vty, time_t uptime); #endif diff --git a/isisd/isis_mt.c b/isisd/isis_mt.c index f7d4c7170f..9465c5e75c 100644 --- a/isisd/isis_mt.c +++ b/isisd/isis_mt.c @@ -3,7 +3,7 @@ * * Copyright (C) 2017 Christian Franke * - * This file is part of FreeRangeRouting (FRR) + * This file is part of FRRouting (FRR) * * FRR is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -72,7 +72,7 @@ const char *isis_mtid2str(uint16_t mtid) case ISIS_MT_IPV6_DSTSRC: return "ipv6-dstsrc"; default: - snprintf(buf, sizeof(buf), "%" PRIu16, mtid); + snprintf(buf, sizeof(buf), "%hu", mtid); return buf; } } @@ -302,6 +302,7 @@ circuit_get_mt_setting(struct isis_circuit *circuit, uint16_t mtid) return setting; } +#ifdef FABRICD static int circuit_write_mt_settings(struct isis_circuit *circuit, struct vty *vty) { @@ -318,6 +319,7 @@ static int circuit_write_mt_settings(struct isis_circuit *circuit, } return written; } +#endif struct isis_circuit_mt_setting ** circuit_mt_settings(struct isis_circuit *circuit, unsigned int *mt_count) @@ -511,8 +513,8 @@ static uint16_t *circuit_bcast_mt_set(struct isis_circuit *circuit, int level, static void tlvs_add_mt_set(struct isis_area *area, struct isis_tlvs *tlvs, unsigned int mt_count, uint16_t *mt_set, - uint8_t *id, uint32_t metric, uint8_t *subtlvs, - uint8_t subtlv_len) + uint8_t *id, uint32_t metric, + struct isis_ext_subtlvs *ext) { for (unsigned int i = 0; i < mt_count; i++) { uint16_t mtid = mt_set[i]; @@ -527,34 +529,33 @@ static void tlvs_add_mt_set(struct isis_area *area, struct isis_tlvs *tlvs, area->area_tag, sysid_print(id), LSP_PSEUDO_ID(id), isis_mtid2str(mtid)); } - isis_tlvs_add_extended_reach(tlvs, mtid, id, metric, subtlvs, - subtlv_len); + isis_tlvs_add_extended_reach(tlvs, mtid, id, metric, ext); } } void tlvs_add_mt_bcast(struct isis_tlvs *tlvs, struct isis_circuit *circuit, - int level, uint8_t *id, uint32_t metric, - uint8_t *subtlvs, uint8_t subtlv_len) + int level, uint8_t *id, uint32_t metric) { unsigned int mt_count; uint16_t *mt_set = circuit_bcast_mt_set(circuit, level, &mt_count); tlvs_add_mt_set(circuit->area, tlvs, mt_count, mt_set, id, metric, - subtlvs, subtlv_len); + circuit->ext); } void tlvs_add_mt_p2p(struct isis_tlvs *tlvs, struct isis_circuit *circuit, - uint8_t *id, uint32_t metric, uint8_t *subtlvs, - uint8_t subtlv_len) + uint8_t *id, uint32_t metric) { struct isis_adjacency *adj = circuit->u.p2p.neighbor; tlvs_add_mt_set(circuit->area, tlvs, adj->mt_count, adj->mt_set, id, - metric, subtlvs, subtlv_len); + metric, circuit->ext); } void mt_init(void) { +#ifdef FABRICD hook_register(isis_circuit_config_write, circuit_write_mt_settings); +#endif } diff --git a/isisd/isis_mt.h b/isisd/isis_mt.h index 515b63f50f..fd9ee133ca 100644 --- a/isisd/isis_mt.h +++ b/isisd/isis_mt.h @@ -3,7 +3,7 @@ * * Copyright (C) 2017 Christian Franke * - * This file is part of FreeRangeRouting (FRR) + * This file is part of FRRouting (FRR) * * FRR is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -116,10 +116,8 @@ bool tlvs_to_adj_mt_set(struct isis_tlvs *tlvs, bool v4_usable, bool v6_usable, bool adj_has_mt(struct isis_adjacency *adj, uint16_t mtid); void adj_mt_finish(struct isis_adjacency *adj); void tlvs_add_mt_bcast(struct isis_tlvs *tlvs, struct isis_circuit *circuit, - int level, uint8_t *id, uint32_t metric, - uint8_t *subtlvs, uint8_t subtlv_len); + int level, uint8_t *id, uint32_t metric); void tlvs_add_mt_p2p(struct isis_tlvs *tlvs, struct isis_circuit *circuit, - uint8_t *id, uint32_t metric, uint8_t *subtlvs, - uint8_t subtlv_len); + uint8_t *id, uint32_t metric); void mt_init(void); #endif diff --git a/isisd/isis_nb.c b/isisd/isis_nb.c new file mode 100644 index 0000000000..33b0b4d02c --- /dev/null +++ b/isisd/isis_nb.c @@ -0,0 +1,912 @@ +/* + * Copyright (C) 2018 Volta Networks + * Emanuele Di Pascale + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "northbound.h" +#include "libfrr.h" + +#include "isisd/isis_nb.h" + +/* clang-format off */ +const struct frr_yang_module_info frr_isisd_info = { + .name = "frr-isisd", + .nodes = { + { + .xpath = "/frr-isisd:isis/instance", + .cbs = { + .cli_show = cli_show_router_isis, + .create = isis_instance_create, + .destroy = isis_instance_destroy, + }, + .priority = NB_DFLT_PRIORITY - 1, + }, + { + .xpath = "/frr-isisd:isis/instance/is-type", + .cbs = { + .cli_show = cli_show_isis_is_type, + .modify = isis_instance_is_type_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/area-address", + .cbs = { + .cli_show = cli_show_isis_area_address, + .create = isis_instance_area_address_create, + .destroy = isis_instance_area_address_destroy, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/dynamic-hostname", + .cbs = { + .cli_show = cli_show_isis_dynamic_hostname, + .modify = isis_instance_dynamic_hostname_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/attached", + .cbs = { + .cli_show = cli_show_isis_attached, + .modify = isis_instance_attached_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/overload", + .cbs = { + .cli_show = cli_show_isis_overload, + .modify = isis_instance_overload_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/metric-style", + .cbs = { + .cli_show = cli_show_isis_metric_style, + .modify = isis_instance_metric_style_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/purge-originator", + .cbs = { + .cli_show = cli_show_isis_purge_origin, + .modify = isis_instance_purge_originator_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/lsp/mtu", + .cbs = { + .cli_show = cli_show_isis_lsp_mtu, + .modify = isis_instance_lsp_mtu_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/lsp/timers", + .cbs = { + .cli_show = cli_show_isis_lsp_timers, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/lsp/timers/level-1/refresh-interval", + .cbs = { + .modify = isis_instance_lsp_refresh_interval_level_1_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/lsp/timers/level-1/maximum-lifetime", + .cbs = { + .modify = isis_instance_lsp_maximum_lifetime_level_1_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/lsp/timers/level-1/generation-interval", + .cbs = { + .modify = isis_instance_lsp_generation_interval_level_1_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/lsp/timers/level-2/refresh-interval", + .cbs = { + .modify = isis_instance_lsp_refresh_interval_level_2_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/lsp/timers/level-2/maximum-lifetime", + .cbs = { + .modify = isis_instance_lsp_maximum_lifetime_level_2_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/lsp/timers/level-2/generation-interval", + .cbs = { + .modify = isis_instance_lsp_generation_interval_level_2_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/spf/ietf-backoff-delay", + .cbs = { + .apply_finish = ietf_backoff_delay_apply_finish, + .cli_show = cli_show_isis_spf_ietf_backoff, + .create = isis_instance_spf_ietf_backoff_delay_create, + .destroy = isis_instance_spf_ietf_backoff_delay_destroy, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/spf/ietf-backoff-delay/init-delay", + .cbs = { + .modify = isis_instance_spf_ietf_backoff_delay_init_delay_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/spf/ietf-backoff-delay/short-delay", + .cbs = { + .modify = isis_instance_spf_ietf_backoff_delay_short_delay_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/spf/ietf-backoff-delay/long-delay", + .cbs = { + .modify = isis_instance_spf_ietf_backoff_delay_long_delay_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/spf/ietf-backoff-delay/hold-down", + .cbs = { + .modify = isis_instance_spf_ietf_backoff_delay_hold_down_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/spf/ietf-backoff-delay/time-to-learn", + .cbs = { + .modify = isis_instance_spf_ietf_backoff_delay_time_to_learn_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/spf/minimum-interval", + .cbs = { + .cli_show = cli_show_isis_spf_min_interval, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/spf/minimum-interval/level-1", + .cbs = { + .modify = isis_instance_spf_minimum_interval_level_1_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/spf/minimum-interval/level-2", + .cbs = { + .modify = isis_instance_spf_minimum_interval_level_2_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/area-password", + .cbs = { + .apply_finish = area_password_apply_finish, + .cli_show = cli_show_isis_area_pwd, + .create = isis_instance_area_password_create, + .destroy = isis_instance_area_password_destroy, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/area-password/password", + .cbs = { + .modify = isis_instance_area_password_password_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/area-password/password-type", + .cbs = { + .modify = isis_instance_area_password_password_type_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/area-password/authenticate-snp", + .cbs = { + .modify = isis_instance_area_password_authenticate_snp_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/domain-password", + .cbs = { + .apply_finish = domain_password_apply_finish, + .cli_show = cli_show_isis_domain_pwd, + .create = isis_instance_domain_password_create, + .destroy = isis_instance_domain_password_destroy, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/domain-password/password", + .cbs = { + .modify = isis_instance_domain_password_password_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/domain-password/password-type", + .cbs = { + .modify = isis_instance_domain_password_password_type_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/domain-password/authenticate-snp", + .cbs = { + .modify = isis_instance_domain_password_authenticate_snp_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/default-information-originate/ipv4", + .cbs = { + .apply_finish = default_info_origin_ipv4_apply_finish, + .cli_show = cli_show_isis_def_origin_ipv4, + .create = isis_instance_default_information_originate_ipv4_create, + .destroy = isis_instance_default_information_originate_ipv4_destroy, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/default-information-originate/ipv4/always", + .cbs = { + .modify = isis_instance_default_information_originate_ipv4_always_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/default-information-originate/ipv4/route-map", + .cbs = { + .destroy = isis_instance_default_information_originate_ipv4_route_map_destroy, + .modify = isis_instance_default_information_originate_ipv4_route_map_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/default-information-originate/ipv4/metric", + .cbs = { + .modify = isis_instance_default_information_originate_ipv4_metric_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/default-information-originate/ipv6", + .cbs = { + .apply_finish = default_info_origin_ipv6_apply_finish, + .cli_show = cli_show_isis_def_origin_ipv6, + .create = isis_instance_default_information_originate_ipv6_create, + .destroy = isis_instance_default_information_originate_ipv6_destroy, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/default-information-originate/ipv6/always", + .cbs = { + .modify = isis_instance_default_information_originate_ipv6_always_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/default-information-originate/ipv6/route-map", + .cbs = { + .destroy = isis_instance_default_information_originate_ipv6_route_map_destroy, + .modify = isis_instance_default_information_originate_ipv6_route_map_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/default-information-originate/ipv6/metric", + .cbs = { + .modify = isis_instance_default_information_originate_ipv6_metric_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/redistribute/ipv4", + .cbs = { + .apply_finish = redistribute_ipv4_apply_finish, + .cli_show = cli_show_isis_redistribute_ipv4, + .create = isis_instance_redistribute_ipv4_create, + .destroy = isis_instance_redistribute_ipv4_destroy, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/redistribute/ipv4/route-map", + .cbs = { + .destroy = isis_instance_redistribute_ipv4_route_map_destroy, + .modify = isis_instance_redistribute_ipv4_route_map_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/redistribute/ipv4/metric", + .cbs = { + .modify = isis_instance_redistribute_ipv4_metric_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/redistribute/ipv6", + .cbs = { + .apply_finish = redistribute_ipv6_apply_finish, + .cli_show = cli_show_isis_redistribute_ipv6, + .create = isis_instance_redistribute_ipv6_create, + .destroy = isis_instance_redistribute_ipv6_destroy, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/redistribute/ipv6/route-map", + .cbs = { + .destroy = isis_instance_redistribute_ipv6_route_map_destroy, + .modify = isis_instance_redistribute_ipv6_route_map_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/redistribute/ipv6/metric", + .cbs = { + .modify = isis_instance_redistribute_ipv6_metric_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/multi-topology/ipv4-multicast", + .cbs = { + .cli_show = cli_show_isis_mt_ipv4_multicast, + .create = isis_instance_multi_topology_ipv4_multicast_create, + .destroy = isis_instance_multi_topology_ipv4_multicast_destroy, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/multi-topology/ipv4-multicast/overload", + .cbs = { + .modify = isis_instance_multi_topology_ipv4_multicast_overload_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/multi-topology/ipv4-management", + .cbs = { + .cli_show = cli_show_isis_mt_ipv4_mgmt, + .create = isis_instance_multi_topology_ipv4_management_create, + .destroy = isis_instance_multi_topology_ipv4_management_destroy, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/multi-topology/ipv4-management/overload", + .cbs = { + .modify = isis_instance_multi_topology_ipv4_management_overload_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/multi-topology/ipv6-unicast", + .cbs = { + .cli_show = cli_show_isis_mt_ipv6_unicast, + .create = isis_instance_multi_topology_ipv6_unicast_create, + .destroy = isis_instance_multi_topology_ipv6_unicast_destroy, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/multi-topology/ipv6-unicast/overload", + .cbs = { + .modify = isis_instance_multi_topology_ipv6_unicast_overload_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/multi-topology/ipv6-multicast", + .cbs = { + .cli_show = cli_show_isis_mt_ipv6_multicast, + .create = isis_instance_multi_topology_ipv6_multicast_create, + .destroy = isis_instance_multi_topology_ipv6_multicast_destroy, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/multi-topology/ipv6-multicast/overload", + .cbs = { + .modify = isis_instance_multi_topology_ipv6_multicast_overload_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/multi-topology/ipv6-management", + .cbs = { + .cli_show = cli_show_isis_mt_ipv6_mgmt, + .create = isis_instance_multi_topology_ipv6_management_create, + .destroy = isis_instance_multi_topology_ipv6_management_destroy, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/multi-topology/ipv6-management/overload", + .cbs = { + .modify = isis_instance_multi_topology_ipv6_management_overload_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/multi-topology/ipv6-dstsrc", + .cbs = { + .cli_show = cli_show_isis_mt_ipv6_dstsrc, + .create = isis_instance_multi_topology_ipv6_dstsrc_create, + .destroy = isis_instance_multi_topology_ipv6_dstsrc_destroy, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/multi-topology/ipv6-dstsrc/overload", + .cbs = { + .modify = isis_instance_multi_topology_ipv6_dstsrc_overload_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/log-adjacency-changes", + .cbs = { + .cli_show = cli_show_isis_log_adjacency, + .modify = isis_instance_log_adjacency_changes_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/mpls-te", + .cbs = { + .cli_show = cli_show_isis_mpls_te, + .create = isis_instance_mpls_te_create, + .destroy = isis_instance_mpls_te_destroy, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/mpls-te/router-address", + .cbs = { + .cli_show = cli_show_isis_mpls_te_router_addr, + .destroy = isis_instance_mpls_te_router_address_destroy, + .modify = isis_instance_mpls_te_router_address_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/segment-routing/enabled", + .cbs = { + .modify = isis_instance_segment_routing_enabled_modify, + .cli_show = cli_show_isis_sr_enabled, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/segment-routing/srgb", + .cbs = { + .apply_finish = isis_instance_segment_routing_srgb_apply_finish, + .pre_validate = isis_instance_segment_routing_srgb_pre_validate, + .cli_show = cli_show_isis_srgb, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/segment-routing/srgb/lower-bound", + .cbs = { + .modify = isis_instance_segment_routing_srgb_lower_bound_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/segment-routing/srgb/upper-bound", + .cbs = { + .modify = isis_instance_segment_routing_srgb_upper_bound_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/segment-routing/srlb", + .cbs = { + .apply_finish = isis_instance_segment_routing_srlb_apply_finish, + .pre_validate = isis_instance_segment_routing_srlb_pre_validate, + .cli_show = cli_show_isis_srlb, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/segment-routing/srlb/lower-bound", + .cbs = { + .modify = isis_instance_segment_routing_srlb_lower_bound_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/segment-routing/srlb/upper-bound", + .cbs = { + .modify = isis_instance_segment_routing_srlb_upper_bound_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/segment-routing/msd/node-msd", + .cbs = { + .modify = isis_instance_segment_routing_msd_node_msd_modify, + .destroy = isis_instance_segment_routing_msd_node_msd_destroy, + .cli_show = cli_show_isis_node_msd, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/segment-routing/prefix-sid-map/prefix-sid", + .cbs = { + .create = isis_instance_segment_routing_prefix_sid_map_prefix_sid_create, + .destroy = isis_instance_segment_routing_prefix_sid_map_prefix_sid_destroy, + .pre_validate = isis_instance_segment_routing_prefix_sid_map_prefix_sid_pre_validate, + .apply_finish = isis_instance_segment_routing_prefix_sid_map_prefix_sid_apply_finish, + .cli_show = cli_show_isis_prefix_sid, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/segment-routing/prefix-sid-map/prefix-sid/sid-value-type", + .cbs = { + .modify = isis_instance_segment_routing_prefix_sid_map_prefix_sid_sid_value_type_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/segment-routing/prefix-sid-map/prefix-sid/sid-value", + .cbs = { + .modify = isis_instance_segment_routing_prefix_sid_map_prefix_sid_sid_value_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/segment-routing/prefix-sid-map/prefix-sid/last-hop-behavior", + .cbs = { + .modify = isis_instance_segment_routing_prefix_sid_map_prefix_sid_last_hop_behavior_modify, + }, + }, + { + .xpath = "/frr-interface:lib/interface/frr-isisd:isis", + .cbs = { + .create = lib_interface_isis_create, + .destroy = lib_interface_isis_destroy, + }, + }, + { + .xpath = "/frr-interface:lib/interface/frr-isisd:isis/area-tag", + .cbs = { + .modify = lib_interface_isis_area_tag_modify, + }, + }, + { + .xpath = "/frr-interface:lib/interface/frr-isisd:isis/vrf", + .cbs = { + .modify = lib_interface_isis_vrf_modify, + }, + }, + + { + .xpath = "/frr-interface:lib/interface/frr-isisd:isis/circuit-type", + .cbs = { + .cli_show = cli_show_ip_isis_circ_type, + .modify = lib_interface_isis_circuit_type_modify, + }, + }, + { + .xpath = "/frr-interface:lib/interface/frr-isisd:isis/ipv4-routing", + .cbs = { + .cli_show = cli_show_ip_isis_ipv4, + .modify = lib_interface_isis_ipv4_routing_modify, + }, + }, + { + .xpath = "/frr-interface:lib/interface/frr-isisd:isis/ipv6-routing", + .cbs = { + .cli_show = cli_show_ip_isis_ipv6, + .modify = lib_interface_isis_ipv6_routing_modify, + }, + }, + { + .xpath = "/frr-interface:lib/interface/frr-isisd:isis/bfd-monitoring", + .cbs = { + .apply_finish = lib_interface_isis_bfd_monitoring_apply_finish, + .cli_show = cli_show_ip_isis_bfd_monitoring, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-isisd:isis/bfd-monitoring/enabled", + .cbs = { + .modify = lib_interface_isis_bfd_monitoring_enabled_modify, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-isisd:isis/bfd-monitoring/profile", + .cbs = { + .modify = lib_interface_isis_bfd_monitoring_profile_modify, + .destroy = lib_interface_isis_bfd_monitoring_profile_destroy, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-isisd:isis/csnp-interval", + .cbs = { + .cli_show = cli_show_ip_isis_csnp_interval, + }, + }, + { + .xpath = "/frr-interface:lib/interface/frr-isisd:isis/csnp-interval/level-1", + .cbs = { + .modify = lib_interface_isis_csnp_interval_level_1_modify, + }, + }, + { + .xpath = "/frr-interface:lib/interface/frr-isisd:isis/csnp-interval/level-2", + .cbs = { + .modify = lib_interface_isis_csnp_interval_level_2_modify, + }, + }, + { + .xpath = "/frr-interface:lib/interface/frr-isisd:isis/psnp-interval", + .cbs = { + .cli_show = cli_show_ip_isis_psnp_interval, + }, + }, + { + .xpath = "/frr-interface:lib/interface/frr-isisd:isis/psnp-interval/level-1", + .cbs = { + .modify = lib_interface_isis_psnp_interval_level_1_modify, + }, + }, + { + .xpath = "/frr-interface:lib/interface/frr-isisd:isis/psnp-interval/level-2", + .cbs = { + .modify = lib_interface_isis_psnp_interval_level_2_modify, + }, + }, + { + .xpath = "/frr-interface:lib/interface/frr-isisd:isis/hello/padding", + .cbs = { + .cli_show = cli_show_ip_isis_hello_padding, + .modify = lib_interface_isis_hello_padding_modify, + }, + }, + { + .xpath = "/frr-interface:lib/interface/frr-isisd:isis/hello/interval", + .cbs = { + .cli_show = cli_show_ip_isis_hello_interval, + }, + }, + { + .xpath = "/frr-interface:lib/interface/frr-isisd:isis/hello/interval/level-1", + .cbs = { + .modify = lib_interface_isis_hello_interval_level_1_modify, + }, + }, + { + .xpath = "/frr-interface:lib/interface/frr-isisd:isis/hello/interval/level-2", + .cbs = { + .modify = lib_interface_isis_hello_interval_level_2_modify, + }, + }, + { + .xpath = "/frr-interface:lib/interface/frr-isisd:isis/hello/multiplier", + .cbs = { + .cli_show = cli_show_ip_isis_hello_multi, + }, + }, + { + .xpath = "/frr-interface:lib/interface/frr-isisd:isis/hello/multiplier/level-1", + .cbs = { + .modify = lib_interface_isis_hello_multiplier_level_1_modify, + }, + }, + { + .xpath = "/frr-interface:lib/interface/frr-isisd:isis/hello/multiplier/level-2", + .cbs = { + .modify = lib_interface_isis_hello_multiplier_level_2_modify, + }, + }, + { + .xpath = "/frr-interface:lib/interface/frr-isisd:isis/metric", + .cbs = { + .cli_show = cli_show_ip_isis_metric, + }, + }, + { + .xpath = "/frr-interface:lib/interface/frr-isisd:isis/metric/level-1", + .cbs = { + .modify = lib_interface_isis_metric_level_1_modify, + }, + }, + { + .xpath = "/frr-interface:lib/interface/frr-isisd:isis/metric/level-2", + .cbs = { + .modify = lib_interface_isis_metric_level_2_modify, + }, + }, + { + .xpath = "/frr-interface:lib/interface/frr-isisd:isis/priority", + .cbs = { + .cli_show = cli_show_ip_isis_priority, + }, + }, + { + .xpath = "/frr-interface:lib/interface/frr-isisd:isis/priority/level-1", + .cbs = { + .modify = lib_interface_isis_priority_level_1_modify, + }, + }, + { + .xpath = "/frr-interface:lib/interface/frr-isisd:isis/priority/level-2", + .cbs = { + .modify = lib_interface_isis_priority_level_2_modify, + }, + }, + { + .xpath = "/frr-interface:lib/interface/frr-isisd:isis/network-type", + .cbs = { + .cli_show = cli_show_ip_isis_network_type, + .modify = lib_interface_isis_network_type_modify, + }, + }, + { + .xpath = "/frr-interface:lib/interface/frr-isisd:isis/passive", + .cbs = { + .cli_show = cli_show_ip_isis_passive, + .modify = lib_interface_isis_passive_modify, + }, + }, + { + .xpath = "/frr-interface:lib/interface/frr-isisd:isis/password", + .cbs = { + .cli_show = cli_show_ip_isis_password, + .create = lib_interface_isis_password_create, + .destroy = lib_interface_isis_password_destroy, + }, + }, + { + .xpath = "/frr-interface:lib/interface/frr-isisd:isis/password/password", + .cbs = { + .modify = lib_interface_isis_password_password_modify, + }, + }, + { + .xpath = "/frr-interface:lib/interface/frr-isisd:isis/password/password-type", + .cbs = { + .modify = lib_interface_isis_password_password_type_modify, + }, + }, + { + .xpath = "/frr-interface:lib/interface/frr-isisd:isis/disable-three-way-handshake", + .cbs = { + .cli_show = cli_show_ip_isis_threeway_shake, + .modify = lib_interface_isis_disable_three_way_handshake_modify, + }, + }, + { + .xpath = "/frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv4-unicast", + .cbs = { + .cli_show = cli_show_ip_isis_mt_ipv4_unicast, + .modify = lib_interface_isis_multi_topology_ipv4_unicast_modify, + }, + }, + { + .xpath = "/frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv4-multicast", + .cbs = { + .cli_show = cli_show_ip_isis_mt_ipv4_multicast, + .modify = lib_interface_isis_multi_topology_ipv4_multicast_modify, + }, + }, + { + .xpath = "/frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv4-management", + .cbs = { + .cli_show = cli_show_ip_isis_mt_ipv4_mgmt, + .modify = lib_interface_isis_multi_topology_ipv4_management_modify, + }, + }, + { + .xpath = "/frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv6-unicast", + .cbs = { + .cli_show = cli_show_ip_isis_mt_ipv6_unicast, + .modify = lib_interface_isis_multi_topology_ipv6_unicast_modify, + }, + }, + { + .xpath = "/frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv6-multicast", + .cbs = { + .cli_show = cli_show_ip_isis_mt_ipv6_multicast, + .modify = lib_interface_isis_multi_topology_ipv6_multicast_modify, + }, + }, + { + .xpath = "/frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv6-management", + .cbs = { + .cli_show = cli_show_ip_isis_mt_ipv6_mgmt, + .modify = lib_interface_isis_multi_topology_ipv6_management_modify, + }, + }, + { + .xpath = "/frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv6-dstsrc", + .cbs = { + .cli_show = cli_show_ip_isis_mt_ipv6_dstsrc, + .modify = lib_interface_isis_multi_topology_ipv6_dstsrc_modify, + }, + }, + { + .xpath = "/frr-interface:lib/interface/state/frr-isisd:isis", + .cbs = { + .get_elem = lib_interface_state_isis_get_elem, + } + }, + { + .xpath = "/frr-interface:lib/interface/state/frr-isisd:isis/adjacencies/adjacency", + .cbs = { + .get_next = lib_interface_state_isis_adjacencies_adjacency_get_next, + } + }, + { + .xpath = "/frr-interface:lib/interface/state/frr-isisd:isis/adjacencies/adjacency/neighbor-sys-type", + .cbs = { + .get_elem = lib_interface_state_isis_adjacencies_adjacency_neighbor_sys_type_get_elem, + } + }, + { + .xpath = "/frr-interface:lib/interface/state/frr-isisd:isis/adjacencies/adjacency/neighbor-sysid", + .cbs = { + .get_elem = lib_interface_state_isis_adjacencies_adjacency_neighbor_sysid_get_elem, + } + }, + { + .xpath = "/frr-interface:lib/interface/state/frr-isisd:isis/adjacencies/adjacency/neighbor-extended-circuit-id", + .cbs = { + .get_elem = lib_interface_state_isis_adjacencies_adjacency_neighbor_extended_circuit_id_get_elem, + } + }, + { + .xpath = "/frr-interface:lib/interface/state/frr-isisd:isis/adjacencies/adjacency/neighbor-snpa", + .cbs = { + .get_elem = lib_interface_state_isis_adjacencies_adjacency_neighbor_snpa_get_elem, + } + }, + { + .xpath = "/frr-interface:lib/interface/state/frr-isisd:isis/adjacencies/adjacency/hold-timer", + .cbs = { + .get_elem = lib_interface_state_isis_adjacencies_adjacency_hold_timer_get_elem, + } + }, + { + .xpath = "/frr-interface:lib/interface/state/frr-isisd:isis/adjacencies/adjacency/neighbor-priority", + .cbs = { + .get_elem = lib_interface_state_isis_adjacencies_adjacency_neighbor_priority_get_elem, + } + }, + { + .xpath = "/frr-interface:lib/interface/state/frr-isisd:isis/adjacencies/adjacency/state", + .cbs = { + .get_elem = lib_interface_state_isis_adjacencies_adjacency_state_get_elem, + } + }, + { + .xpath = "/frr-interface:lib/interface/state/frr-isisd:isis/event-counters/adjacency-changes", + .cbs = { + .get_elem = lib_interface_state_isis_event_counters_adjacency_changes_get_elem, + } + }, + { + .xpath = "/frr-interface:lib/interface/state/frr-isisd:isis/event-counters/adjacency-number", + .cbs = { + .get_elem = lib_interface_state_isis_event_counters_adjacency_number_get_elem, + } + }, + { + .xpath = "/frr-interface:lib/interface/state/frr-isisd:isis/event-counters/init-fails", + .cbs = { + .get_elem = lib_interface_state_isis_event_counters_init_fails_get_elem, + } + }, + { + .xpath = "/frr-interface:lib/interface/state/frr-isisd:isis/event-counters/adjacency-rejects", + .cbs = { + .get_elem = lib_interface_state_isis_event_counters_adjacency_rejects_get_elem, + } + }, + { + .xpath = "/frr-interface:lib/interface/state/frr-isisd:isis/event-counters/id-len-mismatch", + .cbs = { + .get_elem = lib_interface_state_isis_event_counters_id_len_mismatch_get_elem, + } + }, + { + .xpath = "/frr-interface:lib/interface/state/frr-isisd:isis/event-counters/max-area-addresses-mismatch", + .cbs = { + .get_elem = lib_interface_state_isis_event_counters_max_area_addresses_mismatch_get_elem, + } + }, + { + .xpath = "/frr-interface:lib/interface/state/frr-isisd:isis/event-counters/authentication-type-fails", + .cbs = { + .get_elem = lib_interface_state_isis_event_counters_authentication_type_fails_get_elem, + } + }, + { + .xpath = "/frr-interface:lib/interface/state/frr-isisd:isis/event-counters/authentication-fails", + .cbs = { + .get_elem = lib_interface_state_isis_event_counters_authentication_fails_get_elem, + } + }, + { + .xpath = NULL, + }, + } +}; diff --git a/isisd/isis_nb.h b/isisd/isis_nb.h new file mode 100644 index 0000000000..a79cb8ff57 --- /dev/null +++ b/isisd/isis_nb.h @@ -0,0 +1,477 @@ +/* + * Copyright (C) 2018 Volta Networks + * Emanuele Di Pascale + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef ISISD_ISIS_NB_H_ +#define ISISD_ISIS_NB_H_ + +extern const struct frr_yang_module_info frr_isisd_info; + +/* Forward declaration(s). */ +struct isis_area; +struct isis_circuit; +struct isis_adjacency; + +/* Mandatory callbacks. */ +int isis_instance_create(struct nb_cb_create_args *args); +int isis_instance_destroy(struct nb_cb_destroy_args *args); +int isis_instance_is_type_modify(struct nb_cb_modify_args *args); +int isis_instance_area_address_create(struct nb_cb_create_args *args); +int isis_instance_area_address_destroy(struct nb_cb_destroy_args *args); +int isis_instance_dynamic_hostname_modify(struct nb_cb_modify_args *args); +int isis_instance_attached_modify(struct nb_cb_modify_args *args); +int isis_instance_overload_modify(struct nb_cb_modify_args *args); +int isis_instance_metric_style_modify(struct nb_cb_modify_args *args); +int isis_instance_purge_originator_modify(struct nb_cb_modify_args *args); +int isis_instance_lsp_mtu_modify(struct nb_cb_modify_args *args); +int isis_instance_lsp_refresh_interval_level_1_modify( + struct nb_cb_modify_args *args); +int isis_instance_lsp_refresh_interval_level_2_modify( + struct nb_cb_modify_args *args); +int isis_instance_lsp_maximum_lifetime_level_1_modify( + struct nb_cb_modify_args *args); +int isis_instance_lsp_maximum_lifetime_level_2_modify( + struct nb_cb_modify_args *args); +int isis_instance_lsp_generation_interval_level_1_modify( + struct nb_cb_modify_args *args); +int isis_instance_lsp_generation_interval_level_2_modify( + struct nb_cb_modify_args *args); +int isis_instance_spf_ietf_backoff_delay_create(struct nb_cb_create_args *args); +int isis_instance_spf_ietf_backoff_delay_destroy( + struct nb_cb_destroy_args *args); +int isis_instance_spf_ietf_backoff_delay_init_delay_modify( + struct nb_cb_modify_args *args); +int isis_instance_spf_ietf_backoff_delay_short_delay_modify( + struct nb_cb_modify_args *args); +int isis_instance_spf_ietf_backoff_delay_long_delay_modify( + struct nb_cb_modify_args *args); +int isis_instance_spf_ietf_backoff_delay_hold_down_modify( + struct nb_cb_modify_args *args); +int isis_instance_spf_ietf_backoff_delay_time_to_learn_modify( + struct nb_cb_modify_args *args); +int isis_instance_spf_minimum_interval_level_1_modify( + struct nb_cb_modify_args *args); +int isis_instance_spf_minimum_interval_level_2_modify( + struct nb_cb_modify_args *args); +int isis_instance_area_password_create(struct nb_cb_create_args *args); +int isis_instance_area_password_destroy(struct nb_cb_destroy_args *args); +int isis_instance_area_password_password_modify(struct nb_cb_modify_args *args); +int isis_instance_area_password_password_type_modify( + struct nb_cb_modify_args *args); +int isis_instance_area_password_authenticate_snp_modify( + struct nb_cb_modify_args *args); +int isis_instance_domain_password_create(struct nb_cb_create_args *args); +int isis_instance_domain_password_destroy(struct nb_cb_destroy_args *args); +int isis_instance_domain_password_password_modify( + struct nb_cb_modify_args *args); +int isis_instance_domain_password_password_type_modify( + struct nb_cb_modify_args *args); +int isis_instance_domain_password_authenticate_snp_modify( + struct nb_cb_modify_args *args); +int isis_instance_default_information_originate_ipv4_create( + struct nb_cb_create_args *args); +int isis_instance_default_information_originate_ipv4_destroy( + struct nb_cb_destroy_args *args); +int isis_instance_default_information_originate_ipv4_always_modify( + struct nb_cb_modify_args *args); +int isis_instance_default_information_originate_ipv4_route_map_modify( + struct nb_cb_modify_args *args); +int isis_instance_default_information_originate_ipv4_route_map_destroy( + struct nb_cb_destroy_args *args); +int isis_instance_default_information_originate_ipv4_metric_modify( + struct nb_cb_modify_args *args); +int isis_instance_default_information_originate_ipv6_create( + struct nb_cb_create_args *args); +int isis_instance_default_information_originate_ipv6_destroy( + struct nb_cb_destroy_args *args); +int isis_instance_default_information_originate_ipv6_always_modify( + struct nb_cb_modify_args *args); +int isis_instance_default_information_originate_ipv6_route_map_modify( + struct nb_cb_modify_args *args); +int isis_instance_default_information_originate_ipv6_route_map_destroy( + struct nb_cb_destroy_args *args); +int isis_instance_default_information_originate_ipv6_metric_modify( + struct nb_cb_modify_args *args); +int isis_instance_redistribute_ipv4_create(struct nb_cb_create_args *args); +int isis_instance_redistribute_ipv4_destroy(struct nb_cb_destroy_args *args); +int isis_instance_redistribute_ipv4_route_map_modify( + struct nb_cb_modify_args *args); +int isis_instance_redistribute_ipv4_route_map_destroy( + struct nb_cb_destroy_args *args); +int isis_instance_redistribute_ipv4_metric_modify( + struct nb_cb_modify_args *args); +int isis_instance_redistribute_ipv6_create(struct nb_cb_create_args *args); +int isis_instance_redistribute_ipv6_destroy(struct nb_cb_destroy_args *args); +int isis_instance_redistribute_ipv6_route_map_modify( + struct nb_cb_modify_args *args); +int isis_instance_redistribute_ipv6_route_map_destroy( + struct nb_cb_destroy_args *args); +int isis_instance_redistribute_ipv6_metric_modify( + struct nb_cb_modify_args *args); +int isis_instance_multi_topology_ipv4_multicast_create( + struct nb_cb_create_args *args); +int isis_instance_multi_topology_ipv4_multicast_destroy( + struct nb_cb_destroy_args *args); +int isis_instance_multi_topology_ipv4_multicast_overload_modify( + struct nb_cb_modify_args *args); +int isis_instance_multi_topology_ipv4_management_create( + struct nb_cb_create_args *args); +int isis_instance_multi_topology_ipv4_management_destroy( + struct nb_cb_destroy_args *args); +int isis_instance_multi_topology_ipv4_management_overload_modify( + struct nb_cb_modify_args *args); +int isis_instance_multi_topology_ipv6_unicast_create( + struct nb_cb_create_args *args); +int isis_instance_multi_topology_ipv6_unicast_destroy( + struct nb_cb_destroy_args *args); +int isis_instance_multi_topology_ipv6_unicast_overload_modify( + struct nb_cb_modify_args *args); +int isis_instance_multi_topology_ipv6_multicast_create( + struct nb_cb_create_args *args); +int isis_instance_multi_topology_ipv6_multicast_destroy( + struct nb_cb_destroy_args *args); +int isis_instance_multi_topology_ipv6_multicast_overload_modify( + struct nb_cb_modify_args *args); +int isis_instance_multi_topology_ipv6_management_create( + struct nb_cb_create_args *args); +int isis_instance_multi_topology_ipv6_management_destroy( + struct nb_cb_destroy_args *args); +int isis_instance_multi_topology_ipv6_management_overload_modify( + struct nb_cb_modify_args *args); +int isis_instance_multi_topology_ipv6_dstsrc_create( + struct nb_cb_create_args *args); +int isis_instance_multi_topology_ipv6_dstsrc_destroy( + struct nb_cb_destroy_args *args); +int isis_instance_multi_topology_ipv6_dstsrc_overload_modify( + struct nb_cb_modify_args *args); +int isis_instance_log_adjacency_changes_modify(struct nb_cb_modify_args *args); +int isis_instance_mpls_te_create(struct nb_cb_create_args *args); +int isis_instance_mpls_te_destroy(struct nb_cb_destroy_args *args); +int isis_instance_mpls_te_router_address_modify(struct nb_cb_modify_args *args); +int isis_instance_mpls_te_router_address_destroy( + struct nb_cb_destroy_args *args); +int lib_interface_isis_create(struct nb_cb_create_args *args); +int lib_interface_isis_destroy(struct nb_cb_destroy_args *args); +int lib_interface_isis_area_tag_modify(struct nb_cb_modify_args *args); +int lib_interface_isis_vrf_modify(struct nb_cb_modify_args *args); +int lib_interface_isis_ipv4_routing_modify(struct nb_cb_modify_args *args); +int lib_interface_isis_ipv6_routing_modify(struct nb_cb_modify_args *args); +int lib_interface_isis_circuit_type_modify(struct nb_cb_modify_args *args); +void lib_interface_isis_bfd_monitoring_apply_finish( + struct nb_cb_apply_finish_args *args); +int lib_interface_isis_bfd_monitoring_enabled_modify( + struct nb_cb_modify_args *args); +int lib_interface_isis_bfd_monitoring_profile_modify( + struct nb_cb_modify_args *args); +int lib_interface_isis_bfd_monitoring_profile_destroy( + struct nb_cb_destroy_args *args); +int isis_instance_segment_routing_enabled_modify( + struct nb_cb_modify_args *args); +int isis_instance_segment_routing_enabled_modify( + struct nb_cb_modify_args *args); +int isis_instance_segment_routing_srgb_lower_bound_modify( + struct nb_cb_modify_args *args); +int isis_instance_segment_routing_srgb_upper_bound_modify( + struct nb_cb_modify_args *args); +int isis_instance_segment_routing_srlb_lower_bound_modify( + struct nb_cb_modify_args *args); +int isis_instance_segment_routing_srlb_upper_bound_modify( + struct nb_cb_modify_args *args); +int isis_instance_segment_routing_msd_node_msd_modify( + struct nb_cb_modify_args *args); +int isis_instance_segment_routing_msd_node_msd_destroy( + struct nb_cb_destroy_args *args); +int isis_instance_segment_routing_prefix_sid_map_prefix_sid_create( + struct nb_cb_create_args *args); +int isis_instance_segment_routing_prefix_sid_map_prefix_sid_destroy( + struct nb_cb_destroy_args *args); +int isis_instance_segment_routing_prefix_sid_map_prefix_sid_sid_value_type_modify( + struct nb_cb_modify_args *args); +int isis_instance_segment_routing_prefix_sid_map_prefix_sid_sid_value_modify( + struct nb_cb_modify_args *args); +int isis_instance_segment_routing_prefix_sid_map_prefix_sid_last_hop_behavior_modify( + struct nb_cb_modify_args *args); +int lib_interface_isis_csnp_interval_level_1_modify( + struct nb_cb_modify_args *args); +int lib_interface_isis_csnp_interval_level_2_modify( + struct nb_cb_modify_args *args); +int lib_interface_isis_psnp_interval_level_1_modify( + struct nb_cb_modify_args *args); +int lib_interface_isis_psnp_interval_level_2_modify( + struct nb_cb_modify_args *args); +int lib_interface_isis_hello_padding_modify(struct nb_cb_modify_args *args); +int lib_interface_isis_hello_interval_level_1_modify( + struct nb_cb_modify_args *args); +int lib_interface_isis_hello_interval_level_2_modify( + struct nb_cb_modify_args *args); +int lib_interface_isis_hello_multiplier_level_1_modify( + struct nb_cb_modify_args *args); +int lib_interface_isis_hello_multiplier_level_2_modify( + struct nb_cb_modify_args *args); +int lib_interface_isis_metric_level_1_modify(struct nb_cb_modify_args *args); +int lib_interface_isis_metric_level_2_modify(struct nb_cb_modify_args *args); +int lib_interface_isis_priority_level_1_modify(struct nb_cb_modify_args *args); +int lib_interface_isis_priority_level_2_modify(struct nb_cb_modify_args *args); +int lib_interface_isis_network_type_modify(struct nb_cb_modify_args *args); +int lib_interface_isis_passive_modify(struct nb_cb_modify_args *args); +int lib_interface_isis_password_create(struct nb_cb_create_args *args); +int lib_interface_isis_password_destroy(struct nb_cb_destroy_args *args); +int lib_interface_isis_password_password_modify(struct nb_cb_modify_args *args); +int lib_interface_isis_password_password_type_modify( + struct nb_cb_modify_args *args); +int lib_interface_isis_disable_three_way_handshake_modify( + struct nb_cb_modify_args *args); +int lib_interface_isis_multi_topology_ipv4_unicast_modify( + struct nb_cb_modify_args *args); +int lib_interface_isis_multi_topology_ipv4_multicast_modify( + struct nb_cb_modify_args *args); +int lib_interface_isis_multi_topology_ipv4_management_modify( + struct nb_cb_modify_args *args); +int lib_interface_isis_multi_topology_ipv6_unicast_modify( + struct nb_cb_modify_args *args); +int lib_interface_isis_multi_topology_ipv6_multicast_modify( + struct nb_cb_modify_args *args); +int lib_interface_isis_multi_topology_ipv6_management_modify( + struct nb_cb_modify_args *args); +int lib_interface_isis_multi_topology_ipv6_dstsrc_modify( + struct nb_cb_modify_args *args); +struct yang_data * +lib_interface_state_isis_get_elem(struct nb_cb_get_elem_args *args); +const void *lib_interface_state_isis_adjacencies_adjacency_get_next( + struct nb_cb_get_next_args *args); +struct yang_data * +lib_interface_state_isis_adjacencies_adjacency_neighbor_sys_type_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +lib_interface_state_isis_adjacencies_adjacency_neighbor_sysid_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +lib_interface_state_isis_adjacencies_adjacency_neighbor_extended_circuit_id_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +lib_interface_state_isis_adjacencies_adjacency_neighbor_snpa_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +lib_interface_state_isis_adjacencies_adjacency_hold_timer_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +lib_interface_state_isis_adjacencies_adjacency_neighbor_priority_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data *lib_interface_state_isis_adjacencies_adjacency_state_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +lib_interface_state_isis_event_counters_adjacency_changes_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +lib_interface_state_isis_event_counters_adjacency_number_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data *lib_interface_state_isis_event_counters_init_fails_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +lib_interface_state_isis_event_counters_adjacency_rejects_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +lib_interface_state_isis_event_counters_id_len_mismatch_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +lib_interface_state_isis_event_counters_max_area_addresses_mismatch_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +lib_interface_state_isis_event_counters_authentication_type_fails_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +lib_interface_state_isis_event_counters_authentication_fails_get_elem( + struct nb_cb_get_elem_args *args); + +/* Optional 'pre_validate' callbacks. */ +int isis_instance_segment_routing_prefix_sid_map_prefix_sid_pre_validate( + struct nb_cb_pre_validate_args *args); +int isis_instance_segment_routing_srgb_pre_validate( + struct nb_cb_pre_validate_args *args); +int isis_instance_segment_routing_srlb_pre_validate( + struct nb_cb_pre_validate_args *args); + +/* Optional 'apply_finish' callbacks. */ +void ietf_backoff_delay_apply_finish(struct nb_cb_apply_finish_args *args); +void area_password_apply_finish(struct nb_cb_apply_finish_args *args); +void domain_password_apply_finish(struct nb_cb_apply_finish_args *args); +void default_info_origin_apply_finish(const struct lyd_node *dnode, int family); +void default_info_origin_ipv4_apply_finish( + struct nb_cb_apply_finish_args *args); +void default_info_origin_ipv6_apply_finish( + struct nb_cb_apply_finish_args *args); +void redistribute_apply_finish(const struct lyd_node *dnode, int family); +void redistribute_ipv4_apply_finish(struct nb_cb_apply_finish_args *args); +void redistribute_ipv6_apply_finish(struct nb_cb_apply_finish_args *args); +void isis_instance_segment_routing_srgb_apply_finish( + struct nb_cb_apply_finish_args *args); +void isis_instance_segment_routing_srlb_apply_finish( + struct nb_cb_apply_finish_args *args); +void isis_instance_segment_routing_prefix_sid_map_prefix_sid_apply_finish( + struct nb_cb_apply_finish_args *args); + +/* Optional 'cli_show' callbacks. */ +void cli_show_router_isis(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_ip_isis_ipv4(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_ip_isis_ipv6(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_ip_isis_bfd_monitoring(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_isis_area_address(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_isis_is_type(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_isis_dynamic_hostname(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_isis_attached(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_isis_overload(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_isis_metric_style(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_isis_area_pwd(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_isis_domain_pwd(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_isis_lsp_timers(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_isis_lsp_mtu(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_isis_spf_min_interval(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_isis_spf_ietf_backoff(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_isis_purge_origin(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_isis_mpls_te(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_isis_mpls_te_router_addr(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_isis_def_origin_ipv4(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_isis_def_origin_ipv6(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_isis_redistribute_ipv4(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_isis_redistribute_ipv6(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_isis_mt_ipv4_multicast(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_isis_mt_ipv4_mgmt(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_isis_mt_ipv6_unicast(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_isis_mt_ipv6_multicast(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_isis_mt_ipv6_mgmt(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_isis_mt_ipv6_dstsrc(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_isis_sr_enabled(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_isis_srgb(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_isis_srlb(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_isis_node_msd(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_isis_prefix_sid(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_ip_isis_passive(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_ip_isis_password(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_ip_isis_metric(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_ip_isis_hello_interval(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_ip_isis_hello_multi(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_ip_isis_threeway_shake(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_ip_isis_hello_padding(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_ip_isis_csnp_interval(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_ip_isis_psnp_interval(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_ip_isis_mt_ipv4_unicast(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_ip_isis_mt_ipv4_multicast(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_ip_isis_mt_ipv4_mgmt(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_ip_isis_mt_ipv6_unicast(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_ip_isis_mt_ipv6_multicast(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_ip_isis_mt_ipv6_mgmt(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_ip_isis_mt_ipv6_dstsrc(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_ip_isis_circ_type(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_ip_isis_network_type(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_ip_isis_priority(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_isis_log_adjacency(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); + +/* Notifications. */ +void isis_notif_db_overload(const struct isis_area *area, bool overload); +void isis_notif_lsp_too_large(const struct isis_circuit *circuit, + uint32_t pdu_size, const char *lsp_id); +void isis_notif_if_state_change(const struct isis_circuit *circuit, bool down); +void isis_notif_corrupted_lsp(const struct isis_area *area, + const char *lsp_id); /* currently unused */ +void isis_notif_lsp_exceed_max(const struct isis_area *area, + const char *lsp_id); +void isis_notif_max_area_addr_mismatch(const struct isis_circuit *circuit, + uint8_t max_area_addrs, + const char *raw_pdu); +void isis_notif_authentication_type_failure(const struct isis_circuit *circuit, + const char *raw_pdu); +void isis_notif_authentication_failure(const struct isis_circuit *circuit, + const char *raw_pdu); +void isis_notif_adj_state_change(const struct isis_adjacency *adj, + int new_state, const char *reason); +void isis_notif_reject_adjacency(const struct isis_circuit *circuit, + const char *reason, const char *raw_pdu); +void isis_notif_area_mismatch(const struct isis_circuit *circuit, + const char *raw_pdu); +void isis_notif_lsp_received(const struct isis_circuit *circuit, + const char *lsp_id, uint32_t seqno, + uint32_t timestamp, const char *sys_id); +void isis_notif_lsp_gen(const struct isis_area *area, const char *lsp_id, + uint32_t seqno, uint32_t timestamp); +void isis_notif_id_len_mismatch(const struct isis_circuit *circuit, + uint8_t rcv_id_len, const char *raw_pdu); +void isis_notif_version_skew(const struct isis_circuit *circuit, + uint8_t version, const char *raw_pdu); +void isis_notif_lsp_error(const struct isis_circuit *circuit, + const char *lsp_id, const char *raw_pdu, + uint32_t offset, uint8_t tlv_type); +void isis_notif_seqno_skipped(const struct isis_circuit *circuit, + const char *lsp_id); +void isis_notif_own_lsp_purge(const struct isis_circuit *circuit, + const char *lsp_id); + +#endif /* ISISD_ISIS_NB_H_ */ diff --git a/isisd/isis_nb_config.c b/isisd/isis_nb_config.c new file mode 100644 index 0000000000..690f2838c1 --- /dev/null +++ b/isisd/isis_nb_config.c @@ -0,0 +1,2652 @@ +/* + * Copyright (C) 2001,2002 Sampo Saaristo + * Tampere University of Technology + * Institute of Communications Engineering + * Copyright (C) 2018 Volta Networks + * Emanuele Di Pascale + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "northbound.h" +#include "linklist.h" +#include "log.h" +#include "bfd.h" +#include "spf_backoff.h" +#include "lib_errors.h" +#include "vrf.h" + +#include "isisd/isisd.h" +#include "isisd/isis_nb.h" +#include "isisd/isis_common.h" +#include "isisd/isis_bfd.h" +#include "isisd/isis_circuit.h" +#include "isisd/isis_lsp.h" +#include "isisd/isis_dynhn.h" +#include "isisd/isis_misc.h" +#include "isisd/isis_csm.h" +#include "isisd/isis_adjacency.h" +#include "isisd/isis_spf.h" +#include "isisd/isis_te.h" +#include "isisd/isis_memory.h" +#include "isisd/isis_mt.h" +#include "isisd/isis_redist.h" +#include "isisd/isis_dr.h" + +/* + * XPath: /frr-isisd:isis/instance + */ +int isis_instance_create(struct nb_cb_create_args *args) +{ + struct isis_area *area; + const char *area_tag; + const char *vrf_name; + + if (args->event != NB_EV_APPLY) + return NB_OK; + vrf_name = yang_dnode_get_string(args->dnode, "./vrf"); + area_tag = yang_dnode_get_string(args->dnode, "./area-tag"); + isis_global_instance_create(vrf_name); + area = isis_area_lookup_by_vrf(area_tag, vrf_name); + if (area) + return NB_ERR_INCONSISTENCY; + + area = isis_area_create(area_tag, vrf_name); + + /* save area in dnode to avoid looking it up all the time */ + nb_running_set_entry(args->dnode, area); + + return NB_OK; +} + +int isis_instance_destroy(struct nb_cb_destroy_args *args) +{ + struct isis_area *area; + + if (args->event != NB_EV_APPLY) + return NB_OK; + area = nb_running_unset_entry(args->dnode); + isis_area_destroy(area); + + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/is-type + */ +int isis_instance_is_type_modify(struct nb_cb_modify_args *args) +{ + struct isis_area *area; + int type; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + area = nb_running_get_entry(args->dnode, NULL, true); + type = yang_dnode_get_enum(args->dnode, NULL); + isis_area_is_type_set(area, type); + + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/area-address + */ +int isis_instance_area_address_create(struct nb_cb_create_args *args) +{ + struct isis_area *area; + struct area_addr addr, *addrr = NULL, *addrp = NULL; + struct listnode *node; + uint8_t buff[255]; + const char *net_title = yang_dnode_get_string(args->dnode, NULL); + + switch (args->event) { + case NB_EV_VALIDATE: + area = nb_running_get_entry(args->dnode, NULL, true); + if (area == NULL) + return NB_ERR_VALIDATION; + addr.addr_len = dotformat2buff(buff, net_title); + memcpy(addr.area_addr, buff, addr.addr_len); + if (addr.area_addr[addr.addr_len - 1] != 0) { + snprintf( + args->errmsg, args->errmsg_len, + "nsel byte (last byte) in area address must be 0"); + return NB_ERR_VALIDATION; + } + if (area->isis->sysid_set) { + /* Check that the SystemID portions match */ + if (memcmp(area->isis->sysid, GETSYSID((&addr)), + ISIS_SYS_ID_LEN)) { + snprintf( + args->errmsg, args->errmsg_len, + "System ID must not change when defining additional area addresses"); + return NB_ERR_VALIDATION; + } + } + break; + case NB_EV_PREPARE: + addrr = XMALLOC(MTYPE_ISIS_AREA_ADDR, sizeof(struct area_addr)); + addrr->addr_len = dotformat2buff(buff, net_title); + memcpy(addrr->area_addr, buff, addrr->addr_len); + args->resource->ptr = addrr; + break; + case NB_EV_ABORT: + XFREE(MTYPE_ISIS_AREA_ADDR, args->resource->ptr); + break; + case NB_EV_APPLY: + area = nb_running_get_entry(args->dnode, NULL, true); + addrr = args->resource->ptr; + assert(area); + + if (area->isis->sysid_set == 0) { + /* + * First area address - get the SystemID for this router + */ + memcpy(area->isis->sysid, GETSYSID(addrr), + ISIS_SYS_ID_LEN); + area->isis->sysid_set = 1; + } else { + /* check that we don't already have this address */ + for (ALL_LIST_ELEMENTS_RO(area->area_addrs, node, + addrp)) { + if ((addrp->addr_len + ISIS_SYS_ID_LEN + + ISIS_NSEL_LEN) + != (addrr->addr_len)) + continue; + if (!memcmp(addrp->area_addr, addrr->area_addr, + addrr->addr_len)) { + XFREE(MTYPE_ISIS_AREA_ADDR, addrr); + return NB_OK; /* silent fail */ + } + } + } + + /*Forget the systemID part of the address */ + addrr->addr_len -= (ISIS_SYS_ID_LEN + ISIS_NSEL_LEN); + assert(area->area_addrs); /* to silence scan-build sillyness */ + listnode_add(area->area_addrs, addrr); + + /* only now we can safely generate our LSPs for this area */ + if (listcount(area->area_addrs) > 0) { + if (area->is_type & IS_LEVEL_1) + lsp_generate(area, IS_LEVEL_1); + if (area->is_type & IS_LEVEL_2) + lsp_generate(area, IS_LEVEL_2); + } + break; + } + + return NB_OK; +} + +int isis_instance_area_address_destroy(struct nb_cb_destroy_args *args) +{ + struct area_addr addr, *addrp = NULL; + struct listnode *node; + uint8_t buff[255]; + struct isis_area *area; + const char *net_title; + struct listnode *cnode; + struct isis_circuit *circuit; + int lvl; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + net_title = yang_dnode_get_string(args->dnode, NULL); + addr.addr_len = dotformat2buff(buff, net_title); + memcpy(addr.area_addr, buff, (int)addr.addr_len); + area = nb_running_get_entry(args->dnode, NULL, true); + + for (ALL_LIST_ELEMENTS_RO(area->area_addrs, node, addrp)) { + if ((addrp->addr_len + ISIS_SYS_ID_LEN + 1) == addr.addr_len + && !memcmp(addrp->area_addr, addr.area_addr, addr.addr_len)) + break; + } + if (!addrp) + return NB_ERR_INCONSISTENCY; + + listnode_delete(area->area_addrs, addrp); + XFREE(MTYPE_ISIS_AREA_ADDR, addrp); + /* + * Last area address - reset the SystemID for this router + */ + if (listcount(area->area_addrs) == 0) { + for (ALL_LIST_ELEMENTS_RO(area->circuit_list, cnode, circuit)) + for (lvl = IS_LEVEL_1; lvl <= IS_LEVEL_2; ++lvl) { + if (circuit->u.bc.is_dr[lvl - 1]) + isis_dr_resign(circuit, lvl); + } + memset(area->isis->sysid, 0, ISIS_SYS_ID_LEN); + area->isis->sysid_set = 0; + if (IS_DEBUG_EVENTS) + zlog_debug("Router has no SystemID"); + } + + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/dynamic-hostname + */ +int isis_instance_dynamic_hostname_modify(struct nb_cb_modify_args *args) +{ + struct isis_area *area; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + area = nb_running_get_entry(args->dnode, NULL, true); + isis_area_dynhostname_set(area, yang_dnode_get_bool(args->dnode, NULL)); + + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/attached + */ +int isis_instance_attached_modify(struct nb_cb_modify_args *args) +{ + struct isis_area *area; + bool attached; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + area = nb_running_get_entry(args->dnode, NULL, true); + attached = yang_dnode_get_bool(args->dnode, NULL); + isis_area_attached_bit_set(area, attached); + + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/overload + */ +int isis_instance_overload_modify(struct nb_cb_modify_args *args) +{ + struct isis_area *area; + bool overload; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + area = nb_running_get_entry(args->dnode, NULL, true); + overload = yang_dnode_get_bool(args->dnode, NULL); + isis_area_overload_bit_set(area, overload); + + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/metric-style + */ +int isis_instance_metric_style_modify(struct nb_cb_modify_args *args) +{ + struct isis_area *area; + bool old_metric, new_metric; + enum isis_metric_style metric_style = + yang_dnode_get_enum(args->dnode, NULL); + + if (args->event != NB_EV_APPLY) + return NB_OK; + + area = nb_running_get_entry(args->dnode, NULL, true); + old_metric = (metric_style == ISIS_WIDE_METRIC) ? false : true; + new_metric = (metric_style == ISIS_NARROW_METRIC) ? false : true; + isis_area_metricstyle_set(area, old_metric, new_metric); + + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/purge-originator + */ +int isis_instance_purge_originator_modify(struct nb_cb_modify_args *args) +{ + struct isis_area *area; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + area = nb_running_get_entry(args->dnode, NULL, true); + area->purge_originator = yang_dnode_get_bool(args->dnode, NULL); + + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/lsp/mtu + */ +int isis_instance_lsp_mtu_modify(struct nb_cb_modify_args *args) +{ + struct listnode *node; + struct isis_circuit *circuit; + uint16_t lsp_mtu = yang_dnode_get_uint16(args->dnode, NULL); + struct isis_area *area; + + switch (args->event) { + case NB_EV_VALIDATE: + area = nb_running_get_entry(args->dnode, NULL, false); + if (!area) + break; + for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) { + if (circuit->state != C_STATE_INIT + && circuit->state != C_STATE_UP) + continue; + if (lsp_mtu > isis_circuit_pdu_size(circuit)) { + snprintf( + args->errmsg, args->errmsg_len, + "ISIS area contains circuit %s, which has a maximum PDU size of %zu", + circuit->interface->name, + isis_circuit_pdu_size(circuit)); + return NB_ERR_VALIDATION; + } + } + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + area = nb_running_get_entry(args->dnode, NULL, true); + isis_area_lsp_mtu_set(area, lsp_mtu); + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/lsp/timers/level-1/refresh-interval + */ +int isis_instance_lsp_refresh_interval_level_1_modify( + struct nb_cb_modify_args *args) +{ + struct isis_area *area; + uint16_t refr_int; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + refr_int = yang_dnode_get_uint16(args->dnode, NULL); + area = nb_running_get_entry(args->dnode, NULL, true); + isis_area_lsp_refresh_set(area, IS_LEVEL_1, refr_int); + + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/lsp/timers/level-2/refresh-interval + */ +int isis_instance_lsp_refresh_interval_level_2_modify( + struct nb_cb_modify_args *args) +{ + struct isis_area *area; + uint16_t refr_int; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + refr_int = yang_dnode_get_uint16(args->dnode, NULL); + area = nb_running_get_entry(args->dnode, NULL, true); + isis_area_lsp_refresh_set(area, IS_LEVEL_2, refr_int); + + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/lsp/timers/level-1/maximum-lifetime + */ +int isis_instance_lsp_maximum_lifetime_level_1_modify( + struct nb_cb_modify_args *args) +{ + struct isis_area *area; + uint16_t max_lt; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + max_lt = yang_dnode_get_uint16(args->dnode, NULL); + area = nb_running_get_entry(args->dnode, NULL, true); + isis_area_max_lsp_lifetime_set(area, IS_LEVEL_1, max_lt); + + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/lsp/timers/level-2/maximum-lifetime + */ +int isis_instance_lsp_maximum_lifetime_level_2_modify( + struct nb_cb_modify_args *args) +{ + struct isis_area *area; + uint16_t max_lt; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + max_lt = yang_dnode_get_uint16(args->dnode, NULL); + area = nb_running_get_entry(args->dnode, NULL, true); + isis_area_max_lsp_lifetime_set(area, IS_LEVEL_2, max_lt); + + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/lsp/timers/level-1/generation-interval + */ +int isis_instance_lsp_generation_interval_level_1_modify( + struct nb_cb_modify_args *args) +{ + struct isis_area *area; + uint16_t gen_int; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + gen_int = yang_dnode_get_uint16(args->dnode, NULL); + area = nb_running_get_entry(args->dnode, NULL, true); + area->lsp_gen_interval[0] = gen_int; + + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/lsp/timers/level-2/generation-interval + */ +int isis_instance_lsp_generation_interval_level_2_modify( + struct nb_cb_modify_args *args) +{ + struct isis_area *area; + uint16_t gen_int; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + gen_int = yang_dnode_get_uint16(args->dnode, NULL); + area = nb_running_get_entry(args->dnode, NULL, true); + area->lsp_gen_interval[1] = gen_int; + + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/spf/ietf-backoff-delay + */ +void ietf_backoff_delay_apply_finish(struct nb_cb_apply_finish_args *args) +{ + long init_delay = yang_dnode_get_uint16(args->dnode, "./init-delay"); + long short_delay = yang_dnode_get_uint16(args->dnode, "./short-delay"); + long long_delay = yang_dnode_get_uint16(args->dnode, "./long-delay"); + long holddown = yang_dnode_get_uint16(args->dnode, "./hold-down"); + long timetolearn = + yang_dnode_get_uint16(args->dnode, "./time-to-learn"); + struct isis_area *area = nb_running_get_entry(args->dnode, NULL, true); + size_t bufsiz = strlen(area->area_tag) + sizeof("IS-IS Lx"); + char *buf = XCALLOC(MTYPE_TMP, bufsiz); + + snprintf(buf, bufsiz, "IS-IS %s L1", area->area_tag); + spf_backoff_free(area->spf_delay_ietf[0]); + area->spf_delay_ietf[0] = + spf_backoff_new(master, buf, init_delay, short_delay, + long_delay, holddown, timetolearn); + + snprintf(buf, bufsiz, "IS-IS %s L2", area->area_tag); + spf_backoff_free(area->spf_delay_ietf[1]); + area->spf_delay_ietf[1] = + spf_backoff_new(master, buf, init_delay, short_delay, + long_delay, holddown, timetolearn); + + XFREE(MTYPE_TMP, buf); +} + +int isis_instance_spf_ietf_backoff_delay_create(struct nb_cb_create_args *args) +{ + /* All the work is done in the apply_finish */ + return NB_OK; +} + +int isis_instance_spf_ietf_backoff_delay_destroy( + struct nb_cb_destroy_args *args) +{ + struct isis_area *area; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + area = nb_running_get_entry(args->dnode, NULL, true); + spf_backoff_free(area->spf_delay_ietf[0]); + spf_backoff_free(area->spf_delay_ietf[1]); + area->spf_delay_ietf[0] = NULL; + area->spf_delay_ietf[1] = NULL; + + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/spf/ietf-backoff-delay/init-delay + */ +int isis_instance_spf_ietf_backoff_delay_init_delay_modify( + struct nb_cb_modify_args *args) +{ + /* All the work is done in the apply_finish */ + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/spf/ietf-backoff-delay/short-delay + */ +int isis_instance_spf_ietf_backoff_delay_short_delay_modify( + struct nb_cb_modify_args *args) +{ + /* All the work is done in the apply_finish */ + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/spf/ietf-backoff-delay/long-delay + */ +int isis_instance_spf_ietf_backoff_delay_long_delay_modify( + struct nb_cb_modify_args *args) +{ + /* All the work is done in the apply_finish */ + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/spf/ietf-backoff-delay/hold-down + */ +int isis_instance_spf_ietf_backoff_delay_hold_down_modify( + struct nb_cb_modify_args *args) +{ + /* All the work is done in the apply_finish */ + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/spf/ietf-backoff-delay/time-to-learn + */ +int isis_instance_spf_ietf_backoff_delay_time_to_learn_modify( + struct nb_cb_modify_args *args) +{ + /* All the work is done in the apply_finish */ + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/spf/minimum-interval/level-1 + */ +int isis_instance_spf_minimum_interval_level_1_modify( + struct nb_cb_modify_args *args) +{ + struct isis_area *area; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + area = nb_running_get_entry(args->dnode, NULL, true); + area->min_spf_interval[0] = yang_dnode_get_uint16(args->dnode, NULL); + + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/spf/minimum-interval/level-2 + */ +int isis_instance_spf_minimum_interval_level_2_modify( + struct nb_cb_modify_args *args) +{ + struct isis_area *area; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + area = nb_running_get_entry(args->dnode, NULL, true); + area->min_spf_interval[1] = yang_dnode_get_uint16(args->dnode, NULL); + + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/area-password + */ +void area_password_apply_finish(struct nb_cb_apply_finish_args *args) +{ + const char *password = yang_dnode_get_string(args->dnode, "./password"); + struct isis_area *area = nb_running_get_entry(args->dnode, NULL, true); + int pass_type = yang_dnode_get_enum(args->dnode, "./password-type"); + uint8_t snp_auth = + yang_dnode_get_enum(args->dnode, "./authenticate-snp"); + + switch (pass_type) { + case ISIS_PASSWD_TYPE_CLEARTXT: + isis_area_passwd_cleartext_set(area, IS_LEVEL_1, password, + snp_auth); + break; + case ISIS_PASSWD_TYPE_HMAC_MD5: + isis_area_passwd_hmac_md5_set(area, IS_LEVEL_1, password, + snp_auth); + break; + } +} + +int isis_instance_area_password_create(struct nb_cb_create_args *args) +{ + /* actual setting is done in apply_finish */ + return NB_OK; +} + +int isis_instance_area_password_destroy(struct nb_cb_destroy_args *args) +{ + struct isis_area *area; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + area = nb_running_get_entry(args->dnode, NULL, true); + isis_area_passwd_unset(area, IS_LEVEL_1); + + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/area-password/password + */ +int isis_instance_area_password_password_modify(struct nb_cb_modify_args *args) +{ + /* actual setting is done in apply_finish */ + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/area-password/password-type + */ +int isis_instance_area_password_password_type_modify( + struct nb_cb_modify_args *args) +{ + /* actual setting is done in apply_finish */ + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/area-password/authenticate-snp + */ +int isis_instance_area_password_authenticate_snp_modify( + struct nb_cb_modify_args *args) +{ + /* actual setting is done in apply_finish */ + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/domain-password + */ +void domain_password_apply_finish(struct nb_cb_apply_finish_args *args) +{ + const char *password = yang_dnode_get_string(args->dnode, "./password"); + struct isis_area *area = nb_running_get_entry(args->dnode, NULL, true); + int pass_type = yang_dnode_get_enum(args->dnode, "./password-type"); + uint8_t snp_auth = + yang_dnode_get_enum(args->dnode, "./authenticate-snp"); + + switch (pass_type) { + case ISIS_PASSWD_TYPE_CLEARTXT: + isis_area_passwd_cleartext_set(area, IS_LEVEL_2, password, + snp_auth); + break; + case ISIS_PASSWD_TYPE_HMAC_MD5: + isis_area_passwd_hmac_md5_set(area, IS_LEVEL_2, password, + snp_auth); + break; + } +} + +int isis_instance_domain_password_create(struct nb_cb_create_args *args) +{ + /* actual setting is done in apply_finish */ + return NB_OK; +} + +int isis_instance_domain_password_destroy(struct nb_cb_destroy_args *args) +{ + struct isis_area *area; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + area = nb_running_get_entry(args->dnode, NULL, true); + isis_area_passwd_unset(area, IS_LEVEL_2); + + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/domain-password/password + */ +int isis_instance_domain_password_password_modify( + struct nb_cb_modify_args *args) +{ + /* actual setting is done in apply_finish */ + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/domain-password/password-type + */ +int isis_instance_domain_password_password_type_modify( + struct nb_cb_modify_args *args) +{ + /* actual setting is done in apply_finish */ + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/domain-password/authenticate-snp + */ +int isis_instance_domain_password_authenticate_snp_modify( + struct nb_cb_modify_args *args) +{ + /* actual setting is done in apply_finish */ + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/default-information-originate/ipv4 + */ +void default_info_origin_apply_finish(const struct lyd_node *dnode, int family) +{ + int originate_type = DEFAULT_ORIGINATE; + unsigned long metric = 0; + const char *routemap = NULL; + struct isis_area *area = nb_running_get_entry(dnode, NULL, true); + int level = yang_dnode_get_enum(dnode, "./level"); + + if (yang_dnode_get_bool(dnode, "./always")) { + originate_type = DEFAULT_ORIGINATE_ALWAYS; + } else if (family == AF_INET6) { + zlog_warn( + "%s: Zebra doesn't implement default-originate for IPv6 yet, so use with care or use default-originate always.", + __func__); + } + + if (yang_dnode_exists(dnode, "./metric")) + metric = yang_dnode_get_uint32(dnode, "./metric"); + if (yang_dnode_exists(dnode, "./route-map")) + routemap = yang_dnode_get_string(dnode, "./route-map"); + + isis_redist_set(area, level, family, DEFAULT_ROUTE, metric, routemap, + originate_type); +} + +void default_info_origin_ipv4_apply_finish(struct nb_cb_apply_finish_args *args) +{ + default_info_origin_apply_finish(args->dnode, AF_INET); +} + +void default_info_origin_ipv6_apply_finish(struct nb_cb_apply_finish_args *args) +{ + default_info_origin_apply_finish(args->dnode, AF_INET6); +} + +int isis_instance_default_information_originate_ipv4_create( + struct nb_cb_create_args *args) +{ + /* It's all done by default_info_origin_apply_finish */ + return NB_OK; +} + +int isis_instance_default_information_originate_ipv4_destroy( + struct nb_cb_destroy_args *args) +{ + struct isis_area *area; + int level; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + area = nb_running_get_entry(args->dnode, NULL, true); + level = yang_dnode_get_enum(args->dnode, "./level"); + isis_redist_unset(area, level, AF_INET, DEFAULT_ROUTE); + + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/default-information-originate/ipv4/always + */ +int isis_instance_default_information_originate_ipv4_always_modify( + struct nb_cb_modify_args *args) +{ + /* It's all done by default_info_origin_apply_finish */ + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/default-information-originate/ipv4/route-map + */ +int isis_instance_default_information_originate_ipv4_route_map_modify( + struct nb_cb_modify_args *args) +{ + /* It's all done by default_info_origin_apply_finish */ + return NB_OK; +} + +int isis_instance_default_information_originate_ipv4_route_map_destroy( + struct nb_cb_destroy_args *args) +{ + /* It's all done by default_info_origin_apply_finish */ + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/default-information-originate/ipv4/metric + */ +int isis_instance_default_information_originate_ipv4_metric_modify( + struct nb_cb_modify_args *args) +{ + /* It's all done by default_info_origin_apply_finish */ + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/default-information-originate/ipv6 + */ +int isis_instance_default_information_originate_ipv6_create( + struct nb_cb_create_args *args) +{ + /* It's all done by default_info_origin_apply_finish */ + return NB_OK; +} + +int isis_instance_default_information_originate_ipv6_destroy( + struct nb_cb_destroy_args *args) +{ + struct isis_area *area; + int level; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + area = nb_running_get_entry(args->dnode, NULL, true); + level = yang_dnode_get_enum(args->dnode, "./level"); + isis_redist_unset(area, level, AF_INET6, DEFAULT_ROUTE); + + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/default-information-originate/ipv6/always + */ +int isis_instance_default_information_originate_ipv6_always_modify( + struct nb_cb_modify_args *args) +{ + /* It's all done by default_info_origin_apply_finish */ + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/default-information-originate/ipv6/route-map + */ +int isis_instance_default_information_originate_ipv6_route_map_modify( + struct nb_cb_modify_args *args) +{ + /* It's all done by default_info_origin_apply_finish */ + return NB_OK; +} + +int isis_instance_default_information_originate_ipv6_route_map_destroy( + struct nb_cb_destroy_args *args) +{ + /* It's all done by default_info_origin_apply_finish */ + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/default-information-originate/ipv6/metric + */ +int isis_instance_default_information_originate_ipv6_metric_modify( + struct nb_cb_modify_args *args) +{ + /* It's all done by default_info_origin_apply_finish */ + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/redistribute/ipv4 + */ +void redistribute_apply_finish(const struct lyd_node *dnode, int family) +{ + assert(family == AF_INET || family == AF_INET6); + int type, level; + unsigned long metric = 0; + const char *routemap = NULL; + struct isis_area *area; + + type = yang_dnode_get_enum(dnode, "./protocol"); + level = yang_dnode_get_enum(dnode, "./level"); + area = nb_running_get_entry(dnode, NULL, true); + + if (yang_dnode_exists(dnode, "./metric")) + metric = yang_dnode_get_uint32(dnode, "./metric"); + if (yang_dnode_exists(dnode, "./route-map")) + routemap = yang_dnode_get_string(dnode, "./route-map"); + + isis_redist_set(area, level, family, type, metric, routemap, 0); +} + +void redistribute_ipv4_apply_finish(struct nb_cb_apply_finish_args *args) +{ + redistribute_apply_finish(args->dnode, AF_INET); +} + +void redistribute_ipv6_apply_finish(struct nb_cb_apply_finish_args *args) +{ + redistribute_apply_finish(args->dnode, AF_INET6); +} + +int isis_instance_redistribute_ipv4_create(struct nb_cb_create_args *args) +{ + /* It's all done by redistribute_apply_finish */ + return NB_OK; +} + +int isis_instance_redistribute_ipv4_destroy(struct nb_cb_destroy_args *args) +{ + struct isis_area *area; + int level, type; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + area = nb_running_get_entry(args->dnode, NULL, true); + level = yang_dnode_get_enum(args->dnode, "./level"); + type = yang_dnode_get_enum(args->dnode, "./protocol"); + isis_redist_unset(area, level, AF_INET, type); + + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/redistribute/ipv4/route-map + */ +int isis_instance_redistribute_ipv4_route_map_modify( + struct nb_cb_modify_args *args) +{ + /* It's all done by redistribute_apply_finish */ + return NB_OK; +} + +int isis_instance_redistribute_ipv4_route_map_destroy( + struct nb_cb_destroy_args *args) +{ + /* It's all done by redistribute_apply_finish */ + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/redistribute/ipv4/metric + */ +int isis_instance_redistribute_ipv4_metric_modify( + struct nb_cb_modify_args *args) +{ + /* It's all done by redistribute_apply_finish */ + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/redistribute/ipv6 + */ +int isis_instance_redistribute_ipv6_create(struct nb_cb_create_args *args) +{ + /* It's all done by redistribute_apply_finish */ + return NB_OK; +} + +int isis_instance_redistribute_ipv6_destroy(struct nb_cb_destroy_args *args) +{ + struct isis_area *area; + int level, type; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + area = nb_running_get_entry(args->dnode, NULL, true); + level = yang_dnode_get_enum(args->dnode, "./level"); + type = yang_dnode_get_enum(args->dnode, "./protocol"); + isis_redist_unset(area, level, AF_INET6, type); + + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/redistribute/ipv6/route-map + */ +int isis_instance_redistribute_ipv6_route_map_modify( + struct nb_cb_modify_args *args) +{ + /* It's all done by redistribute_apply_finish */ + return NB_OK; +} + +int isis_instance_redistribute_ipv6_route_map_destroy( + struct nb_cb_destroy_args *args) +{ + /* It's all done by redistribute_apply_finish */ + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/redistribute/ipv6/metric + */ +int isis_instance_redistribute_ipv6_metric_modify( + struct nb_cb_modify_args *args) +{ + /* It's all done by redistribute_apply_finish */ + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/multi-topology/ipv4-multicast + */ +static int isis_multi_topology_common(enum nb_event event, + const struct lyd_node *dnode, + char *errmsg, size_t errmsg_len, + const char *topology, bool create) +{ + struct isis_area *area; + struct isis_area_mt_setting *setting; + uint16_t mtid = isis_str2mtid(topology); + + switch (event) { + case NB_EV_VALIDATE: + if (mtid == (uint16_t)-1) { + snprintf(errmsg, errmsg_len, "Unknown topology %s", + topology); + return NB_ERR_VALIDATION; + } + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + area = nb_running_get_entry(dnode, NULL, true); + setting = area_get_mt_setting(area, mtid); + setting->enabled = create; + lsp_regenerate_schedule(area, IS_LEVEL_1 | IS_LEVEL_2, 0); + break; + } + + return NB_OK; +} + +static int isis_multi_topology_overload_common(enum nb_event event, + const struct lyd_node *dnode, + const char *topology) +{ + struct isis_area *area; + struct isis_area_mt_setting *setting; + uint16_t mtid = isis_str2mtid(topology); + + /* validation is done in isis_multi_topology_common */ + if (event != NB_EV_APPLY) + return NB_OK; + + area = nb_running_get_entry(dnode, NULL, true); + setting = area_get_mt_setting(area, mtid); + setting->overload = yang_dnode_get_bool(dnode, NULL); + if (setting->enabled) + lsp_regenerate_schedule(area, IS_LEVEL_1 | IS_LEVEL_2, 0); + + return NB_OK; +} + +int isis_instance_multi_topology_ipv4_multicast_create( + struct nb_cb_create_args *args) +{ + return isis_multi_topology_common(args->event, args->dnode, + args->errmsg, args->errmsg_len, + "ipv4-multicast", true); +} + +int isis_instance_multi_topology_ipv4_multicast_destroy( + struct nb_cb_destroy_args *args) +{ + return isis_multi_topology_common(args->event, args->dnode, + args->errmsg, args->errmsg_len, + "ipv4-multicast", false); +} + +/* + * XPath: /frr-isisd:isis/instance/multi-topology/ipv4-multicast/overload + */ +int isis_instance_multi_topology_ipv4_multicast_overload_modify( + struct nb_cb_modify_args *args) +{ + return isis_multi_topology_overload_common(args->event, args->dnode, + "ipv4-multicast"); +} + +/* + * XPath: /frr-isisd:isis/instance/multi-topology/ipv4-management + */ +int isis_instance_multi_topology_ipv4_management_create( + struct nb_cb_create_args *args) +{ + return isis_multi_topology_common(args->event, args->dnode, + args->errmsg, args->errmsg_len, + "ipv4-mgmt", true); +} + +int isis_instance_multi_topology_ipv4_management_destroy( + struct nb_cb_destroy_args *args) +{ + return isis_multi_topology_common(args->event, args->dnode, + args->errmsg, args->errmsg_len, + "ipv4-mgmt", false); +} + +/* + * XPath: /frr-isisd:isis/instance/multi-topology/ipv4-management/overload + */ +int isis_instance_multi_topology_ipv4_management_overload_modify( + struct nb_cb_modify_args *args) +{ + return isis_multi_topology_overload_common(args->event, args->dnode, + "ipv4-mgmt"); +} + +/* + * XPath: /frr-isisd:isis/instance/multi-topology/ipv6-unicast + */ +int isis_instance_multi_topology_ipv6_unicast_create( + struct nb_cb_create_args *args) +{ + return isis_multi_topology_common(args->event, args->dnode, + args->errmsg, args->errmsg_len, + "ipv6-unicast", true); +} + +int isis_instance_multi_topology_ipv6_unicast_destroy( + struct nb_cb_destroy_args *args) +{ + return isis_multi_topology_common(args->event, args->dnode, + args->errmsg, args->errmsg_len, + "ipv6-unicast", false); +} + +/* + * XPath: /frr-isisd:isis/instance/multi-topology/ipv6-unicast/overload + */ +int isis_instance_multi_topology_ipv6_unicast_overload_modify( + struct nb_cb_modify_args *args) +{ + return isis_multi_topology_overload_common(args->event, args->dnode, + "ipv6-unicast"); +} + +/* + * XPath: /frr-isisd:isis/instance/multi-topology/ipv6-multicast + */ +int isis_instance_multi_topology_ipv6_multicast_create( + struct nb_cb_create_args *args) +{ + return isis_multi_topology_common(args->event, args->dnode, + args->errmsg, args->errmsg_len, + "ipv6-multicast", true); +} + +int isis_instance_multi_topology_ipv6_multicast_destroy( + struct nb_cb_destroy_args *args) +{ + return isis_multi_topology_common(args->event, args->dnode, + args->errmsg, args->errmsg_len, + "ipv6-multicast", false); +} + +/* + * XPath: /frr-isisd:isis/instance/multi-topology/ipv6-multicast/overload + */ +int isis_instance_multi_topology_ipv6_multicast_overload_modify( + struct nb_cb_modify_args *args) +{ + return isis_multi_topology_overload_common(args->event, args->dnode, + "ipv6-multicast"); +} + +/* + * XPath: /frr-isisd:isis/instance/multi-topology/ipv6-management + */ +int isis_instance_multi_topology_ipv6_management_create( + struct nb_cb_create_args *args) +{ + return isis_multi_topology_common(args->event, args->dnode, + args->errmsg, args->errmsg_len, + "ipv6-mgmt", true); +} + +int isis_instance_multi_topology_ipv6_management_destroy( + struct nb_cb_destroy_args *args) +{ + return isis_multi_topology_common(args->event, args->dnode, + args->errmsg, args->errmsg_len, + "ipv6-mgmt", false); +} + +/* + * XPath: /frr-isisd:isis/instance/multi-topology/ipv6-management/overload + */ +int isis_instance_multi_topology_ipv6_management_overload_modify( + struct nb_cb_modify_args *args) +{ + return isis_multi_topology_overload_common(args->event, args->dnode, + "ipv6-mgmt"); +} + +/* + * XPath: /frr-isisd:isis/instance/multi-topology/ipv6-dstsrc + */ +int isis_instance_multi_topology_ipv6_dstsrc_create( + struct nb_cb_create_args *args) +{ + return isis_multi_topology_common(args->event, args->dnode, + args->errmsg, args->errmsg_len, + "ipv6-dstsrc", true); +} + +int isis_instance_multi_topology_ipv6_dstsrc_destroy( + struct nb_cb_destroy_args *args) +{ + return isis_multi_topology_common(args->event, args->dnode, + args->errmsg, args->errmsg_len, + "ipv6-dstsrc", false); +} + +/* + * XPath: /frr-isisd:isis/instance/multi-topology/ipv6-dstsrc/overload + */ +int isis_instance_multi_topology_ipv6_dstsrc_overload_modify( + struct nb_cb_modify_args *args) +{ + return isis_multi_topology_overload_common(args->event, args->dnode, + "ipv6-dstsrc"); +} + +/* + * XPath: /frr-isisd:isis/instance/log-adjacency-changes + */ +int isis_instance_log_adjacency_changes_modify(struct nb_cb_modify_args *args) +{ + struct isis_area *area; + bool log = yang_dnode_get_bool(args->dnode, NULL); + + if (args->event != NB_EV_APPLY) + return NB_OK; + + area = nb_running_get_entry(args->dnode, NULL, true); + area->log_adj_changes = log ? 1 : 0; + + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/mpls-te + */ +int isis_instance_mpls_te_create(struct nb_cb_create_args *args) +{ + struct listnode *node; + struct isis_area *area; + struct isis_circuit *circuit; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + area = nb_running_get_entry(args->dnode, NULL, true); + if (area->mta == NULL) { + + struct mpls_te_area *new; + + zlog_debug("ISIS-TE(%s): Initialize MPLS Traffic Engineering", + area->area_tag); + + new = XCALLOC(MTYPE_ISIS_MPLS_TE, sizeof(struct mpls_te_area)); + + /* Initialize MPLS_TE structure */ + new->status = enable; + new->level = 0; + new->inter_as = off; + new->interas_areaid.s_addr = 0; + new->router_id.s_addr = 0; + + area->mta = new; + } else { + area->mta->status = enable; + } + + /* Update Extended TLVs according to Interface link parameters */ + for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) + isis_link_params_update(circuit, circuit->interface); + + /* Reoriginate STD_TE & GMPLS circuits */ + lsp_regenerate_schedule(area, area->is_type, 0); + + return NB_OK; +} + +int isis_instance_mpls_te_destroy(struct nb_cb_destroy_args *args) +{ + struct listnode *node; + struct isis_area *area; + struct isis_circuit *circuit; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + area = nb_running_get_entry(args->dnode, NULL, true); + if (IS_MPLS_TE(area->mta)) + area->mta->status = disable; + else + return NB_OK; + + /* Flush LSP if circuit engage */ + for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) { + if (!IS_EXT_TE(circuit->ext)) + continue; + + /* disable MPLS_TE Circuit keeping SR one's */ + if (IS_SUBTLV(circuit->ext, EXT_ADJ_SID)) + circuit->ext->status = EXT_ADJ_SID; + else if (IS_SUBTLV(circuit->ext, EXT_LAN_ADJ_SID)) + circuit->ext->status = EXT_LAN_ADJ_SID; + else + circuit->ext->status = 0; + } + + /* Reoriginate STD_TE & GMPLS circuits */ + lsp_regenerate_schedule(area, area->is_type, 0); + + zlog_debug("ISIS-TE(%s): Disabled MPLS Traffic Engineering", + area->area_tag); + + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/mpls-te/router-address + */ +int isis_instance_mpls_te_router_address_modify(struct nb_cb_modify_args *args) +{ + struct in_addr value; + struct isis_area *area; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + area = nb_running_get_entry(args->dnode, NULL, true); + /* only proceed if MPLS-TE is enabled */ + if (!IS_MPLS_TE(area->mta)) + return NB_OK; + + /* Update Area Router ID */ + yang_dnode_get_ipv4(&value, args->dnode, NULL); + area->mta->router_id.s_addr = value.s_addr; + + /* And re-schedule LSP update */ + lsp_regenerate_schedule(area, area->is_type, 0); + + return NB_OK; +} + +int isis_instance_mpls_te_router_address_destroy( + struct nb_cb_destroy_args *args) +{ + struct isis_area *area; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + area = nb_running_get_entry(args->dnode, NULL, true); + /* only proceed if MPLS-TE is enabled */ + if (!IS_MPLS_TE(area->mta)) + return NB_OK; + + /* Reset Area Router ID */ + area->mta->router_id.s_addr = INADDR_ANY; + + /* And re-schedule LSP update */ + lsp_regenerate_schedule(area, area->is_type, 0); + + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/segment-routing/enabled + */ +int isis_instance_segment_routing_enabled_modify( + struct nb_cb_modify_args *args) +{ + struct isis_area *area; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + area = nb_running_get_entry(args->dnode, NULL, true); + area->srdb.config.enabled = yang_dnode_get_bool(args->dnode, NULL); + + if (area->srdb.config.enabled) { + if (IS_DEBUG_EVENTS) + zlog_debug("SR: Segment Routing: OFF -> ON"); + + isis_sr_start(area); + } else { + if (IS_DEBUG_EVENTS) + zlog_debug("SR: Segment Routing: ON -> OFF"); + + isis_sr_stop(area); + } + + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/segment-routing/srgb + */ +int isis_instance_segment_routing_srgb_pre_validate( + struct nb_cb_pre_validate_args *args) +{ + uint32_t srgb_lbound; + uint32_t srgb_ubound; + uint32_t srlb_lbound; + uint32_t srlb_ubound; + + srgb_lbound = yang_dnode_get_uint32(args->dnode, "./lower-bound"); + srgb_ubound = yang_dnode_get_uint32(args->dnode, "./upper-bound"); + srlb_lbound = yang_dnode_get_uint32(args->dnode, "../srlb/lower-bound"); + srlb_ubound = yang_dnode_get_uint32(args->dnode, "../srlb/upper-bound"); + + /* Check that the block size does not exceed 65535 */ + if ((srgb_ubound - srgb_lbound + 1) > 65535) { + zlog_warn( + "New SR Global Block (%u/%u) exceed the limit of 65535", + srgb_lbound, srgb_ubound); + return NB_ERR_VALIDATION; + } + + /* Validate SRGB against SRLB */ + if (!((srgb_ubound < srlb_lbound) || (srgb_lbound > srlb_ubound))) { + zlog_warn( + "New SR Global Block (%u/%u) conflict with Local Block (%u/%u)", + srgb_lbound, srgb_ubound, srlb_lbound, srlb_ubound); + return NB_ERR_VALIDATION; + } + + return NB_OK; +} + +void isis_instance_segment_routing_srgb_apply_finish( + struct nb_cb_apply_finish_args *args) +{ + struct isis_area *area; + uint32_t lower_bound, upper_bound; + + area = nb_running_get_entry(args->dnode, NULL, true); + lower_bound = yang_dnode_get_uint32(args->dnode, "./lower-bound"); + upper_bound = yang_dnode_get_uint32(args->dnode, "./upper-bound"); + + isis_sr_cfg_srgb_update(area, lower_bound, upper_bound); +} + +/* + * XPath: /frr-isisd:isis/instance/segment-routing/srgb/lower-bound + */ +int isis_instance_segment_routing_srgb_lower_bound_modify( + struct nb_cb_modify_args *args) +{ + uint32_t lower_bound = yang_dnode_get_uint32(args->dnode, NULL); + + switch (args->event) { + case NB_EV_VALIDATE: + if (!IS_MPLS_UNRESERVED_LABEL(lower_bound)) { + zlog_warn("Invalid SRGB lower bound: %u", + lower_bound); + return NB_ERR_VALIDATION; + } + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/segment-routing/srgb/upper-bound + */ +int isis_instance_segment_routing_srgb_upper_bound_modify( + struct nb_cb_modify_args *args) +{ + uint32_t upper_bound = yang_dnode_get_uint32(args->dnode, NULL); + + switch (args->event) { + case NB_EV_VALIDATE: + if (!IS_MPLS_UNRESERVED_LABEL(upper_bound)) { + zlog_warn("Invalid SRGB upper bound: %u", + upper_bound); + return NB_ERR_VALIDATION; + } + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/segment-routing/srlb + */ +int isis_instance_segment_routing_srlb_pre_validate( + struct nb_cb_pre_validate_args *args) +{ + uint32_t srgb_lbound; + uint32_t srgb_ubound; + uint32_t srlb_lbound; + uint32_t srlb_ubound; + + srgb_lbound = yang_dnode_get_uint32(args->dnode, "../srgb/lower-bound"); + srgb_ubound = yang_dnode_get_uint32(args->dnode, "../srgb/upper-bound"); + srlb_lbound = yang_dnode_get_uint32(args->dnode, "./lower-bound"); + srlb_ubound = yang_dnode_get_uint32(args->dnode, "./upper-bound"); + + /* Check that the block size does not exceed 65535 */ + if ((srlb_ubound - srlb_lbound + 1) > 65535) { + zlog_warn( + "New SR Local Block (%u/%u) exceed the limit of 65535", + srlb_lbound, srlb_ubound); + return NB_ERR_VALIDATION; + } + + /* Validate SRLB against SRGB */ + if (!((srlb_ubound < srgb_lbound) || (srlb_lbound > srgb_ubound))) { + zlog_warn( + "New SR Local Block (%u/%u) conflict with Global Block (%u/%u)", + srlb_lbound, srlb_ubound, srgb_lbound, srgb_ubound); + return NB_ERR_VALIDATION; + } + + return NB_OK; +} + +void isis_instance_segment_routing_srlb_apply_finish( + struct nb_cb_apply_finish_args *args) +{ + struct isis_area *area; + uint32_t lower_bound, upper_bound; + + area = nb_running_get_entry(args->dnode, NULL, true); + lower_bound = yang_dnode_get_uint32(args->dnode, "./lower-bound"); + upper_bound = yang_dnode_get_uint32(args->dnode, "./upper-bound"); + + isis_sr_cfg_srlb_update(area, lower_bound, upper_bound); +} + +/* + * XPath: /frr-isisd:isis/instance/segment-routing/srlb/lower-bound + */ +int isis_instance_segment_routing_srlb_lower_bound_modify( + struct nb_cb_modify_args *args) +{ + uint32_t lower_bound = yang_dnode_get_uint32(args->dnode, NULL); + + switch (args->event) { + case NB_EV_VALIDATE: + if (!IS_MPLS_UNRESERVED_LABEL(lower_bound)) { + zlog_warn("Invalid SRLB lower bound: %u", + lower_bound); + return NB_ERR_VALIDATION; + } + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/segment-routing/srlb/upper-bound + */ +int isis_instance_segment_routing_srlb_upper_bound_modify( + struct nb_cb_modify_args *args) +{ + uint32_t upper_bound = yang_dnode_get_uint32(args->dnode, NULL); + + switch (args->event) { + case NB_EV_VALIDATE: + if (!IS_MPLS_UNRESERVED_LABEL(upper_bound)) { + zlog_warn("Invalid SRLB upper bound: %u", + upper_bound); + return NB_ERR_VALIDATION; + } + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/segment-routing/msd/node-msd + */ +int isis_instance_segment_routing_msd_node_msd_modify( + struct nb_cb_modify_args *args) +{ + struct isis_area *area; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + area = nb_running_get_entry(args->dnode, NULL, true); + area->srdb.config.msd = yang_dnode_get_uint8(args->dnode, NULL); + + /* Update and regenerate LSP */ + lsp_regenerate_schedule(area, area->is_type, 0); + + return NB_OK; +} + +int isis_instance_segment_routing_msd_node_msd_destroy( + struct nb_cb_destroy_args *args) +{ + struct isis_area *area; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + area = nb_running_get_entry(args->dnode, NULL, true); + area->srdb.config.msd = 0; + + /* Update and regenerate LSP */ + lsp_regenerate_schedule(area, area->is_type, 0); + + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/segment-routing/prefix-sid-map/prefix-sid + */ +int isis_instance_segment_routing_prefix_sid_map_prefix_sid_create( + struct nb_cb_create_args *args) +{ + struct isis_area *area; + struct prefix prefix; + struct sr_prefix_cfg *pcfg; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + area = nb_running_get_entry(args->dnode, NULL, true); + yang_dnode_get_prefix(&prefix, args->dnode, "./prefix"); + + pcfg = isis_sr_cfg_prefix_add(area, &prefix); + nb_running_set_entry(args->dnode, pcfg); + + return NB_OK; +} + +int isis_instance_segment_routing_prefix_sid_map_prefix_sid_destroy( + struct nb_cb_destroy_args *args) +{ + struct sr_prefix_cfg *pcfg; + struct isis_area *area; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + pcfg = nb_running_unset_entry(args->dnode); + area = pcfg->area; + isis_sr_cfg_prefix_del(pcfg); + lsp_regenerate_schedule(area, area->is_type, 0); + + return NB_OK; +} + +int isis_instance_segment_routing_prefix_sid_map_prefix_sid_pre_validate( + struct nb_cb_pre_validate_args *args) +{ + uint32_t srgb_lbound; + uint32_t srgb_ubound; + uint32_t srgb_range; + uint32_t sid; + enum sr_sid_value_type sid_type; + + srgb_lbound = yang_dnode_get_uint32(args->dnode, + "../../srgb/lower-bound"); + srgb_ubound = yang_dnode_get_uint32(args->dnode, + "../../srgb/upper-bound"); + sid = yang_dnode_get_uint32(args->dnode, "./sid-value"); + sid_type = yang_dnode_get_enum(args->dnode, "./sid-value-type"); + + srgb_range = srgb_ubound - srgb_lbound + 1; + switch (sid_type) { + case SR_SID_VALUE_TYPE_INDEX: + if (sid >= srgb_range) { + zlog_warn("SID index %u falls outside local SRGB range", + sid); + return NB_ERR_VALIDATION; + } + break; + case SR_SID_VALUE_TYPE_ABSOLUTE: + if (!IS_MPLS_UNRESERVED_LABEL(sid)) { + zlog_warn("Invalid absolute SID %u", sid); + return NB_ERR_VALIDATION; + } + break; + } + + return NB_OK; +} + +void isis_instance_segment_routing_prefix_sid_map_prefix_sid_apply_finish( + struct nb_cb_apply_finish_args *args) +{ + struct sr_prefix_cfg *pcfg; + struct isis_area *area; + + pcfg = nb_running_get_entry(args->dnode, NULL, true); + area = pcfg->area; + lsp_regenerate_schedule(area, area->is_type, 0); +} + +/* + * XPath: + * /frr-isisd:isis/instance/segment-routing/prefix-sid-map/prefix-sid/sid-value-type + */ +int isis_instance_segment_routing_prefix_sid_map_prefix_sid_sid_value_type_modify( + struct nb_cb_modify_args *args) +{ + struct sr_prefix_cfg *pcfg; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + pcfg = nb_running_get_entry(args->dnode, NULL, true); + pcfg->sid_type = yang_dnode_get_enum(args->dnode, NULL); + + return NB_OK; +} + +/* + * XPath: + * /frr-isisd:isis/instance/segment-routing/prefix-sid-map/prefix-sid/sid-value + */ +int isis_instance_segment_routing_prefix_sid_map_prefix_sid_sid_value_modify( + struct nb_cb_modify_args *args) +{ + struct sr_prefix_cfg *pcfg; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + pcfg = nb_running_get_entry(args->dnode, NULL, true); + pcfg->sid = yang_dnode_get_uint32(args->dnode, NULL); + + return NB_OK; +} + +/* + * XPath: + * /frr-isisd:isis/instance/segment-routing/prefix-sid-map/prefix-sid/last-hop-behavior + */ +int isis_instance_segment_routing_prefix_sid_map_prefix_sid_last_hop_behavior_modify( + struct nb_cb_modify_args *args) +{ + struct sr_prefix_cfg *pcfg; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + pcfg = nb_running_get_entry(args->dnode, NULL, true); + pcfg->last_hop_behavior = yang_dnode_get_enum(args->dnode, NULL); + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-isisd:isis + */ +int lib_interface_isis_create(struct nb_cb_create_args *args) +{ + struct isis_area *area = NULL; + struct interface *ifp; + struct isis_circuit *circuit = NULL; + struct vrf *vrf; + const char *area_tag = yang_dnode_get_string(args->dnode, "./area-tag"); + const char *vrf_name = yang_dnode_get_string(args->dnode, "./vrf"); + uint32_t min_mtu, actual_mtu; + + switch (args->event) { + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_VALIDATE: + /* check if interface mtu is sufficient. If the area has not + * been created yet, assume default MTU for the area + */ + ifp = nb_running_get_entry(args->dnode, NULL, false); + /* zebra might not know yet about the MTU - nothing we can do */ + if (!ifp || ifp->mtu == 0) + break; + vrf = vrf_lookup_by_id(ifp->vrf_id); + if (ifp->vrf_id != VRF_DEFAULT && vrf + && strcmp(vrf->name, vrf_name) != 0) { + snprintf(args->errmsg, args->errmsg_len, + "interface %s not in vrf %s\n", ifp->name, + vrf_name); + return NB_ERR_VALIDATION; + } + actual_mtu = + if_is_broadcast(ifp) ? ifp->mtu - LLC_LEN : ifp->mtu; + + area = isis_area_lookup(area_tag, ifp->vrf_id); + if (area) + min_mtu = area->lsp_mtu; + else +#ifndef FABRICD + min_mtu = yang_get_default_uint16( + "/frr-isisd:isis/instance/lsp/mtu"); +#else + min_mtu = DEFAULT_LSP_MTU; +#endif /* ifndef FABRICD */ + if (actual_mtu < min_mtu) { + snprintf(args->errmsg, args->errmsg_len, + "Interface %s has MTU %u, minimum MTU for the area is %u", + ifp->name, actual_mtu, min_mtu); + return NB_ERR_VALIDATION; + } + break; + case NB_EV_APPLY: + area = isis_area_lookup_by_vrf(area_tag, vrf_name); + /* The area should have already be created. We are + * setting the priority of the global isis area creation + * slightly lower, so it should be executed first, but I + * cannot rely on that so here I have to check. + */ + if (!area) { + flog_err( + EC_LIB_NB_CB_CONFIG_APPLY, + "%s: attempt to create circuit for area %s before the area has been created", + __func__, area_tag); + abort(); + } + ifp = nb_running_get_entry(args->dnode, NULL, true); + circuit = isis_circuit_create(area, ifp); + assert(circuit + && (circuit->state == C_STATE_CONF + || circuit->state == C_STATE_UP)); + nb_running_set_entry(args->dnode, circuit); + break; + } + + return NB_OK; +} + +int lib_interface_isis_destroy(struct nb_cb_destroy_args *args) +{ + struct isis_circuit *circuit; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + circuit = nb_running_unset_entry(args->dnode); + if (!circuit) + return NB_ERR_INCONSISTENCY; + + /* disable both AFs for this circuit. this will also update the + * CSM state by sending an ISIS_DISABLED signal. If there is no + * area associated to the circuit there is nothing to do + */ + if (circuit->area) + isis_circuit_af_set(circuit, false, false); + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-isisd:isis/area-tag + */ +int lib_interface_isis_area_tag_modify(struct nb_cb_modify_args *args) +{ + struct isis_circuit *circuit; + struct interface *ifp; + struct vrf *vrf; + const char *area_tag, *ifname, *vrfname; + + if (args->event == NB_EV_VALIDATE) { + /* libyang doesn't like relative paths across module boundaries + */ + ifname = yang_dnode_get_string(args->dnode->parent->parent, + "./name"); + vrfname = yang_dnode_get_string(args->dnode->parent->parent, + "./vrf"); + vrf = vrf_lookup_by_name(vrfname); + assert(vrf); + ifp = if_lookup_by_name(ifname, vrf->vrf_id); + + if (!ifp) + return NB_OK; + + circuit = circuit_scan_by_ifp(ifp); + area_tag = yang_dnode_get_string(args->dnode, NULL); + if (circuit && circuit->area && circuit->area->area_tag + && strcmp(circuit->area->area_tag, area_tag)) { + snprintf(args->errmsg, args->errmsg_len, + "ISIS circuit is already defined on %s", + circuit->area->area_tag); + return NB_ERR_VALIDATION; + } + } + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-isisd:isis/vrf + */ +int lib_interface_isis_vrf_modify(struct nb_cb_modify_args *args) +{ + struct interface *ifp; + struct vrf *vrf; + const char *ifname, *vrfname, *vrf_name; + struct isis_circuit *circuit; + + if (args->event == NB_EV_VALIDATE) { + /* libyang doesn't like relative paths across module boundaries + */ + ifname = yang_dnode_get_string(args->dnode->parent->parent, + "./name"); + vrfname = yang_dnode_get_string(args->dnode->parent->parent, + "./vrf"); + vrf = vrf_lookup_by_name(vrfname); + assert(vrf); + ifp = if_lookup_by_name(ifname, vrf->vrf_id); + + if (!ifp) + return NB_OK; + + vrf_name = yang_dnode_get_string(args->dnode, NULL); + circuit = circuit_scan_by_ifp(ifp); + if (circuit && circuit->area && circuit->area->isis + && strcmp(circuit->area->isis->name, vrf_name)) { + snprintf(args->errmsg, args->errmsg_len, + "ISIS circuit is already defined on vrf %s", + circuit->area->isis->name); + return NB_ERR_VALIDATION; + } + } + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-isisd:isis/circuit-type + */ +int lib_interface_isis_circuit_type_modify(struct nb_cb_modify_args *args) +{ + int circ_type = yang_dnode_get_enum(args->dnode, NULL); + struct isis_circuit *circuit; + struct interface *ifp; + struct vrf *vrf; + const char *ifname, *vrfname; + struct isis *isis = NULL; + + switch (args->event) { + case NB_EV_VALIDATE: + /* libyang doesn't like relative paths across module boundaries + */ + ifname = yang_dnode_get_string(args->dnode->parent->parent, + "./name"); + vrfname = yang_dnode_get_string(args->dnode->parent->parent, + "./vrf"); + vrf = vrf_lookup_by_name(vrfname); + assert(vrf); + ifp = if_lookup_by_name(ifname, vrf->vrf_id); + if (!ifp) + break; + + isis = isis_lookup_by_vrfid(ifp->vrf_id); + if (isis == NULL) + return NB_ERR_VALIDATION; + + circuit = circuit_lookup_by_ifp(ifp, isis->init_circ_list); + if (circuit && circuit->state == C_STATE_UP + && circuit->area->is_type != IS_LEVEL_1_AND_2 + && circuit->area->is_type != circ_type) { + snprintf(args->errmsg, args->errmsg_len, + "Invalid circuit level for area %s", + circuit->area->area_tag); + return NB_ERR_VALIDATION; + } + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + circuit = nb_running_get_entry(args->dnode, NULL, true); + isis_circuit_is_type_set(circuit, circ_type); + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-isisd:isis/ipv4-routing + */ +int lib_interface_isis_ipv4_routing_modify(struct nb_cb_modify_args *args) +{ + bool ipv4, ipv6; + struct isis_circuit *circuit; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + circuit = nb_running_get_entry(args->dnode, NULL, true); + ipv4 = yang_dnode_get_bool(args->dnode, NULL); + ipv6 = yang_dnode_get_bool(args->dnode, "../ipv6-routing"); + isis_circuit_af_set(circuit, ipv4, ipv6); + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-isisd:isis/ipv6-routing + */ +int lib_interface_isis_ipv6_routing_modify(struct nb_cb_modify_args *args) +{ + bool ipv4, ipv6; + struct isis_circuit *circuit; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + circuit = nb_running_get_entry(args->dnode, NULL, true); + ipv4 = yang_dnode_get_bool(args->dnode, "../ipv4-routing"); + ipv6 = yang_dnode_get_bool(args->dnode, NULL); + isis_circuit_af_set(circuit, ipv4, ipv6); + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-isisd:isis/bfd-monitoring + */ +void lib_interface_isis_bfd_monitoring_apply_finish( + struct nb_cb_apply_finish_args *args) +{ + struct isis_circuit *circuit; + bool enabled; + const char *profile = NULL; + + circuit = nb_running_get_entry(args->dnode, NULL, true); + enabled = yang_dnode_get_bool(args->dnode, "./enabled"); + + if (yang_dnode_exists(args->dnode, "./profile")) + profile = yang_dnode_get_string(args->dnode, "./profile"); + + if (enabled) { + isis_bfd_circuit_param_set(circuit, BFD_DEF_MIN_RX, + BFD_DEF_MIN_TX, BFD_DEF_DETECT_MULT, + profile, true); + } else { + isis_bfd_circuit_cmd(circuit, ZEBRA_BFD_DEST_DEREGISTER); + bfd_info_free(&circuit->bfd_info); + } +} + +/* + * XPath: /frr-interface:lib/interface/frr-isisd:isis/bfd-monitoring/enabled + */ +int lib_interface_isis_bfd_monitoring_enabled_modify( + struct nb_cb_modify_args *args) +{ + /* Everything done in apply_finish */ + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-isisd:isis/bfd-monitoring/profile + */ +int lib_interface_isis_bfd_monitoring_profile_modify( + struct nb_cb_modify_args *args) +{ + /* Everything done in apply_finish */ + return NB_OK; +} + +int lib_interface_isis_bfd_monitoring_profile_destroy( + struct nb_cb_destroy_args *args) +{ + /* Everything done in apply_finish */ + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-isisd:isis/csnp-interval/level-1 + */ +int lib_interface_isis_csnp_interval_level_1_modify( + struct nb_cb_modify_args *args) +{ + struct isis_circuit *circuit; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + circuit = nb_running_get_entry(args->dnode, NULL, true); + circuit->csnp_interval[0] = yang_dnode_get_uint16(args->dnode, NULL); + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-isisd:isis/csnp-interval/level-2 + */ +int lib_interface_isis_csnp_interval_level_2_modify( + struct nb_cb_modify_args *args) +{ + struct isis_circuit *circuit; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + circuit = nb_running_get_entry(args->dnode, NULL, true); + circuit->csnp_interval[1] = yang_dnode_get_uint16(args->dnode, NULL); + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-isisd:isis/psnp-interval/level-1 + */ +int lib_interface_isis_psnp_interval_level_1_modify( + struct nb_cb_modify_args *args) +{ + struct isis_circuit *circuit; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + circuit = nb_running_get_entry(args->dnode, NULL, true); + circuit->psnp_interval[0] = yang_dnode_get_uint16(args->dnode, NULL); + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-isisd:isis/psnp-interval/level-2 + */ +int lib_interface_isis_psnp_interval_level_2_modify( + struct nb_cb_modify_args *args) +{ + struct isis_circuit *circuit; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + circuit = nb_running_get_entry(args->dnode, NULL, true); + circuit->psnp_interval[1] = yang_dnode_get_uint16(args->dnode, NULL); + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-isisd:isis/hello/padding + */ +int lib_interface_isis_hello_padding_modify(struct nb_cb_modify_args *args) +{ + struct isis_circuit *circuit; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + circuit = nb_running_get_entry(args->dnode, NULL, true); + circuit->pad_hellos = yang_dnode_get_bool(args->dnode, NULL); + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-isisd:isis/hello/interval/level-1 + */ +int lib_interface_isis_hello_interval_level_1_modify( + struct nb_cb_modify_args *args) +{ + struct isis_circuit *circuit; + uint32_t interval; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + circuit = nb_running_get_entry(args->dnode, NULL, true); + interval = yang_dnode_get_uint32(args->dnode, NULL); + circuit->hello_interval[0] = interval; + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-isisd:isis/hello/interval/level-2 + */ +int lib_interface_isis_hello_interval_level_2_modify( + struct nb_cb_modify_args *args) +{ + struct isis_circuit *circuit; + uint32_t interval; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + circuit = nb_running_get_entry(args->dnode, NULL, true); + interval = yang_dnode_get_uint32(args->dnode, NULL); + circuit->hello_interval[1] = interval; + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-isisd:isis/hello/multiplier/level-1 + */ +int lib_interface_isis_hello_multiplier_level_1_modify( + struct nb_cb_modify_args *args) +{ + struct isis_circuit *circuit; + uint16_t multi; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + circuit = nb_running_get_entry(args->dnode, NULL, true); + multi = yang_dnode_get_uint16(args->dnode, NULL); + circuit->hello_multiplier[0] = multi; + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-isisd:isis/hello/multiplier/level-2 + */ +int lib_interface_isis_hello_multiplier_level_2_modify( + struct nb_cb_modify_args *args) +{ + struct isis_circuit *circuit; + uint16_t multi; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + circuit = nb_running_get_entry(args->dnode, NULL, true); + multi = yang_dnode_get_uint16(args->dnode, NULL); + circuit->hello_multiplier[1] = multi; + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-isisd:isis/metric/level-1 + */ +int lib_interface_isis_metric_level_1_modify(struct nb_cb_modify_args *args) +{ + struct isis_circuit *circuit; + unsigned int met; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + circuit = nb_running_get_entry(args->dnode, NULL, true); + met = yang_dnode_get_uint32(args->dnode, NULL); + isis_circuit_metric_set(circuit, IS_LEVEL_1, met); + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-isisd:isis/metric/level-2 + */ +int lib_interface_isis_metric_level_2_modify(struct nb_cb_modify_args *args) +{ + struct isis_circuit *circuit; + unsigned int met; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + circuit = nb_running_get_entry(args->dnode, NULL, true); + met = yang_dnode_get_uint32(args->dnode, NULL); + isis_circuit_metric_set(circuit, IS_LEVEL_2, met); + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-isisd:isis/priority/level-1 + */ +int lib_interface_isis_priority_level_1_modify(struct nb_cb_modify_args *args) +{ + struct isis_circuit *circuit; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + circuit = nb_running_get_entry(args->dnode, NULL, true); + circuit->priority[0] = yang_dnode_get_uint8(args->dnode, NULL); + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-isisd:isis/priority/level-2 + */ +int lib_interface_isis_priority_level_2_modify(struct nb_cb_modify_args *args) +{ + struct isis_circuit *circuit; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + circuit = nb_running_get_entry(args->dnode, NULL, true); + circuit->priority[1] = yang_dnode_get_uint8(args->dnode, NULL); + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-isisd:isis/network-type + */ +int lib_interface_isis_network_type_modify(struct nb_cb_modify_args *args) +{ + struct isis_circuit *circuit; + int net_type = yang_dnode_get_enum(args->dnode, NULL); + + switch (args->event) { + case NB_EV_VALIDATE: + circuit = nb_running_get_entry(args->dnode, NULL, false); + if (!circuit) + break; + if (circuit->circ_type == CIRCUIT_T_LOOPBACK) { + snprintf( + args->errmsg, args->errmsg_len, + "Cannot change network type on loopback interface"); + return NB_ERR_VALIDATION; + } + if (net_type == CIRCUIT_T_BROADCAST + && circuit->state == C_STATE_UP + && !if_is_broadcast(circuit->interface)) { + snprintf( + args->errmsg, args->errmsg_len, + "Cannot configure non-broadcast interface for broadcast operation"); + return NB_ERR_VALIDATION; + } + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + circuit = nb_running_get_entry(args->dnode, NULL, true); + isis_circuit_circ_type_set(circuit, net_type); + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-isisd:isis/passive + */ +int lib_interface_isis_passive_modify(struct nb_cb_modify_args *args) +{ + struct isis_circuit *circuit; + struct isis_area *area; + struct interface *ifp; + bool passive = yang_dnode_get_bool(args->dnode, NULL); + + /* validation only applies if we are setting passive to false */ + if (!passive && args->event == NB_EV_VALIDATE) { + circuit = nb_running_get_entry(args->dnode, NULL, false); + if (!circuit) + return NB_OK; + ifp = circuit->interface; + if (!ifp) + return NB_OK; + if (if_is_loopback(ifp)) { + snprintf(args->errmsg, args->errmsg_len, + "Loopback is always passive"); + return NB_ERR_VALIDATION; + } + } + + if (args->event != NB_EV_APPLY) + return NB_OK; + + circuit = nb_running_get_entry(args->dnode, NULL, true); + if (circuit->state != C_STATE_UP) { + circuit->is_passive = passive; + } else { + area = circuit->area; + isis_csm_state_change(ISIS_DISABLE, circuit, area); + circuit->is_passive = passive; + isis_csm_state_change(ISIS_ENABLE, circuit, area); + } + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-isisd:isis/password + */ +int lib_interface_isis_password_create(struct nb_cb_create_args *args) +{ + return NB_OK; +} + +int lib_interface_isis_password_destroy(struct nb_cb_destroy_args *args) +{ + struct isis_circuit *circuit; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + circuit = nb_running_get_entry(args->dnode, NULL, true); + isis_circuit_passwd_unset(circuit); + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-isisd:isis/password/password + */ +int lib_interface_isis_password_password_modify(struct nb_cb_modify_args *args) +{ + struct isis_circuit *circuit; + const char *password; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + password = yang_dnode_get_string(args->dnode, NULL); + circuit = nb_running_get_entry(args->dnode, NULL, true); + + isis_circuit_passwd_set(circuit, circuit->passwd.type, password); + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-isisd:isis/password/password-type + */ +int lib_interface_isis_password_password_type_modify( + struct nb_cb_modify_args *args) +{ + struct isis_circuit *circuit; + uint8_t pass_type; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + pass_type = yang_dnode_get_enum(args->dnode, NULL); + circuit = nb_running_get_entry(args->dnode, NULL, true); + circuit->passwd.type = pass_type; + + return NB_OK; +} + +/* + * XPath: + * /frr-interface:lib/interface/frr-isisd:isis/disable-three-way-handshake + */ +int lib_interface_isis_disable_three_way_handshake_modify( + struct nb_cb_modify_args *args) +{ + struct isis_circuit *circuit; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + circuit = nb_running_get_entry(args->dnode, NULL, true); + circuit->disable_threeway_adj = yang_dnode_get_bool(args->dnode, NULL); + + return NB_OK; +} + +/* + * XPath: + * /frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv4-unicast + */ +static int lib_interface_isis_multi_topology_common( + enum nb_event event, const struct lyd_node *dnode, char *errmsg, + size_t errmsg_len, uint16_t mtid) +{ + struct isis_circuit *circuit; + bool value; + + switch (event) { + case NB_EV_VALIDATE: + circuit = nb_running_get_entry(dnode, NULL, false); + if (circuit && circuit->area && circuit->area->oldmetric) { + snprintf( + errmsg, errmsg_len, + "Multi topology IS-IS can only be used with wide metrics"); + return NB_ERR_VALIDATION; + } + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + circuit = nb_running_get_entry(dnode, NULL, true); + value = yang_dnode_get_bool(dnode, NULL); + isis_circuit_mt_enabled_set(circuit, mtid, value); + break; + } + + return NB_OK; +} + +int lib_interface_isis_multi_topology_ipv4_unicast_modify( + struct nb_cb_modify_args *args) +{ + return lib_interface_isis_multi_topology_common( + args->event, args->dnode, args->errmsg, args->errmsg_len, + ISIS_MT_IPV4_UNICAST); +} + +/* + * XPath: + * /frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv4-multicast + */ +int lib_interface_isis_multi_topology_ipv4_multicast_modify( + struct nb_cb_modify_args *args) +{ + return lib_interface_isis_multi_topology_common( + args->event, args->dnode, args->errmsg, args->errmsg_len, + ISIS_MT_IPV4_MULTICAST); +} + +/* + * XPath: + * /frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv4-management + */ +int lib_interface_isis_multi_topology_ipv4_management_modify( + struct nb_cb_modify_args *args) +{ + return lib_interface_isis_multi_topology_common( + args->event, args->dnode, args->errmsg, args->errmsg_len, + ISIS_MT_IPV4_MGMT); +} + +/* + * XPath: + * /frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv6-unicast + */ +int lib_interface_isis_multi_topology_ipv6_unicast_modify( + struct nb_cb_modify_args *args) +{ + return lib_interface_isis_multi_topology_common( + args->event, args->dnode, args->errmsg, args->errmsg_len, + ISIS_MT_IPV6_UNICAST); +} + +/* + * XPath: + * /frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv6-multicast + */ +int lib_interface_isis_multi_topology_ipv6_multicast_modify( + struct nb_cb_modify_args *args) +{ + return lib_interface_isis_multi_topology_common( + args->event, args->dnode, args->errmsg, args->errmsg_len, + ISIS_MT_IPV6_MULTICAST); +} + +/* + * XPath: + * /frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv6-management + */ +int lib_interface_isis_multi_topology_ipv6_management_modify( + struct nb_cb_modify_args *args) +{ + return lib_interface_isis_multi_topology_common( + args->event, args->dnode, args->errmsg, args->errmsg_len, + ISIS_MT_IPV6_MGMT); +} + +/* + * XPath: /frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv6-dstsrc + */ +int lib_interface_isis_multi_topology_ipv6_dstsrc_modify( + struct nb_cb_modify_args *args) +{ + return lib_interface_isis_multi_topology_common( + args->event, args->dnode, args->errmsg, args->errmsg_len, + ISIS_MT_IPV6_DSTSRC); +} diff --git a/isisd/isis_nb_notifications.c b/isisd/isis_nb_notifications.c new file mode 100644 index 0000000000..ea33ec10ec --- /dev/null +++ b/isisd/isis_nb_notifications.c @@ -0,0 +1,491 @@ +/* + * Copyright (C) 2018 Volta Networks + * Emanuele Di Pascale + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "northbound.h" +#include "log.h" + +#include "isisd/isisd.h" +#include "isisd/isis_nb.h" +#include "isisd/isis_adjacency.h" +#include "isisd/isis_dynhn.h" +#include "isisd/isis_misc.h" + +/* + * Helper functions. + */ +static void notif_prep_instance_hdr(const char *xpath, + const struct isis_area *area, + const char *routing_instance, + struct list *args) +{ + char xpath_arg[XPATH_MAXLEN]; + struct yang_data *data; + + snprintf(xpath_arg, sizeof(xpath_arg), "%s/routing-instance", xpath); + data = yang_data_new_string(xpath_arg, routing_instance); + listnode_add(args, data); + snprintf(xpath_arg, sizeof(xpath_arg), "%s/routing-protocol-name", + xpath); + data = yang_data_new_string(xpath_arg, area->area_tag); + listnode_add(args, data); + snprintf(xpath_arg, sizeof(xpath_arg), "%s/isis-level", xpath); + data = yang_data_new_enum(xpath_arg, area->is_type); + listnode_add(args, data); +} + +static void notif_prepr_iface_hdr(const char *xpath, + const struct isis_circuit *circuit, + struct list *args) +{ + char xpath_arg[XPATH_MAXLEN]; + struct yang_data *data; + + snprintf(xpath_arg, sizeof(xpath_arg), "%s/interface-name", xpath); + data = yang_data_new_string(xpath_arg, circuit->interface->name); + listnode_add(args, data); + snprintf(xpath_arg, sizeof(xpath_arg), "%s/interface-level", xpath); + data = yang_data_new_enum(xpath_arg, circuit->is_type); + listnode_add(args, data); + snprintf(xpath_arg, sizeof(xpath_arg), "%s/extended-circuit-id", xpath); + /* we do not seem to have the extended version of the circuit_id */ + data = yang_data_new_uint32(xpath_arg, (uint32_t)circuit->circuit_id); + listnode_add(args, data); +} + +/* + * XPath: /frr-isisd:database-overload + */ +void isis_notif_db_overload(const struct isis_area *area, bool overload) +{ + const char *xpath = "/frr-isisd:database-overload"; + struct list *arguments = yang_data_list_new(); + char xpath_arg[XPATH_MAXLEN]; + struct yang_data *data; + + notif_prep_instance_hdr(xpath, area, "default", arguments); + snprintf(xpath_arg, sizeof(xpath_arg), "%s/overload", xpath); + data = yang_data_new_enum(xpath_arg, !!overload); + listnode_add(arguments, data); + + nb_notification_send(xpath, arguments); +} + +/* + * XPath: /frr-isisd:lsp-too-large + */ +void isis_notif_lsp_too_large(const struct isis_circuit *circuit, + uint32_t pdu_size, const char *lsp_id) +{ + const char *xpath = "/frr-isisd:lsp-too-large"; + struct list *arguments = yang_data_list_new(); + char xpath_arg[XPATH_MAXLEN]; + struct yang_data *data; + struct isis_area *area = circuit->area; + + notif_prep_instance_hdr(xpath, area, "default", arguments); + notif_prepr_iface_hdr(xpath, circuit, arguments); + snprintf(xpath_arg, sizeof(xpath_arg), "%s/pdu-size", xpath); + data = yang_data_new_uint32(xpath_arg, pdu_size); + listnode_add(arguments, data); + snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath); + data = yang_data_new_string(xpath_arg, lsp_id); + listnode_add(arguments, data); + + nb_notification_send(xpath, arguments); +} + +/* + * XPath: /frr-isisd:if-state-change + */ +void isis_notif_if_state_change(const struct isis_circuit *circuit, bool down) +{ + const char *xpath = "/frr-isisd:if-state-change"; + struct list *arguments = yang_data_list_new(); + char xpath_arg[XPATH_MAXLEN]; + struct yang_data *data; + struct isis_area *area = circuit->area; + + notif_prep_instance_hdr(xpath, area, "default", arguments); + notif_prepr_iface_hdr(xpath, circuit, arguments); + snprintf(xpath_arg, sizeof(xpath_arg), "%s/state", xpath); + data = yang_data_new_enum(xpath_arg, !!down); + listnode_add(arguments, data); + + nb_notification_send(xpath, arguments); +} + +/* + * XPath: /frr-isisd:corrupted-lsp-detected + */ +void isis_notif_corrupted_lsp(const struct isis_area *area, const char *lsp_id) +{ + const char *xpath = "/frr-isisd:corrupted-lsp-detected"; + struct list *arguments = yang_data_list_new(); + char xpath_arg[XPATH_MAXLEN]; + struct yang_data *data; + + notif_prep_instance_hdr(xpath, area, "default", arguments); + snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath); + data = yang_data_new_string(xpath_arg, lsp_id); + listnode_add(arguments, data); + + nb_notification_send(xpath, arguments); +} + +/* + * XPath: /frr-isisd:attempt-to-exceed-max-sequence + */ +void isis_notif_lsp_exceed_max(const struct isis_area *area, const char *lsp_id) +{ + const char *xpath = "/frr-isisd:attempt-to-exceed-max-sequence"; + struct list *arguments = yang_data_list_new(); + char xpath_arg[XPATH_MAXLEN]; + struct yang_data *data; + + notif_prep_instance_hdr(xpath, area, "default", arguments); + snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath); + data = yang_data_new_string(xpath_arg, lsp_id); + listnode_add(arguments, data); + + nb_notification_send(xpath, arguments); +} + +/* + * XPath: /frr-isisd:max-area-addresses-mismatch + */ +void isis_notif_max_area_addr_mismatch(const struct isis_circuit *circuit, + uint8_t max_area_addrs, + const char *raw_pdu) +{ + const char *xpath = "/frr-isisd:max-area-addresses-mismatch"; + struct list *arguments = yang_data_list_new(); + char xpath_arg[XPATH_MAXLEN]; + struct yang_data *data; + struct isis_area *area = circuit->area; + + notif_prep_instance_hdr(xpath, area, "default", arguments); + notif_prepr_iface_hdr(xpath, circuit, arguments); + snprintf(xpath_arg, sizeof(xpath_arg), "%s/max-area-addresses", xpath); + data = yang_data_new_uint8(xpath_arg, max_area_addrs); + listnode_add(arguments, data); + snprintf(xpath_arg, sizeof(xpath_arg), "%s/raw-pdu", xpath); + data = yang_data_new(xpath_arg, raw_pdu); + listnode_add(arguments, data); + + nb_notification_send(xpath, arguments); +} + +/* + * XPath: /frr-isisd:authentication-type-failure + */ +void isis_notif_authentication_type_failure(const struct isis_circuit *circuit, + const char *raw_pdu) +{ + const char *xpath = "/frr-isisd:authentication-type-failure"; + struct list *arguments = yang_data_list_new(); + char xpath_arg[XPATH_MAXLEN]; + struct yang_data *data; + struct isis_area *area = circuit->area; + + notif_prep_instance_hdr(xpath, area, "default", arguments); + notif_prepr_iface_hdr(xpath, circuit, arguments); + snprintf(xpath_arg, sizeof(xpath_arg), "%s/raw-pdu", xpath); + data = yang_data_new(xpath_arg, raw_pdu); + listnode_add(arguments, data); + + nb_notification_send(xpath, arguments); +} + +/* + * XPath: /frr-isisd:authentication-failure + */ +void isis_notif_authentication_failure(const struct isis_circuit *circuit, + const char *raw_pdu) +{ + const char *xpath = "/frr-isisd:authentication-failure"; + struct list *arguments = yang_data_list_new(); + char xpath_arg[XPATH_MAXLEN]; + struct yang_data *data; + struct isis_area *area = circuit->area; + + notif_prep_instance_hdr(xpath, area, "default", arguments); + notif_prepr_iface_hdr(xpath, circuit, arguments); + snprintf(xpath_arg, sizeof(xpath_arg), "%s/raw-pdu", xpath); + data = yang_data_new(xpath_arg, raw_pdu); + listnode_add(arguments, data); + + nb_notification_send(xpath, arguments); +} + +/* + * XPath: /frr-isisd:adjacency-state-change + */ +void isis_notif_adj_state_change(const struct isis_adjacency *adj, + int new_state, const char *reason) +{ + const char *xpath = "/frr-isisd:adjacency-state-change"; + struct list *arguments = yang_data_list_new(); + char xpath_arg[XPATH_MAXLEN]; + struct yang_data *data; + struct isis_circuit *circuit = adj->circuit; + struct isis_area *area = circuit->area; + struct isis_dynhn *dyn = dynhn_find_by_id(adj->sysid); + + notif_prep_instance_hdr(xpath, area, "default", arguments); + notif_prepr_iface_hdr(xpath, circuit, arguments); + if (dyn) { + snprintf(xpath_arg, sizeof(xpath_arg), "%s/neighbor", xpath); + data = yang_data_new_string(xpath_arg, dyn->hostname); + listnode_add(arguments, data); + } + snprintf(xpath_arg, sizeof(xpath_arg), "%s/neighbor-system-id", xpath); + data = yang_data_new_string(xpath_arg, sysid_print(adj->sysid)); + listnode_add(arguments, data); + + snprintf(xpath_arg, sizeof(xpath_arg), "%s/state", xpath); + data = yang_data_new_string(xpath_arg, isis_adj_yang_state(new_state)); + listnode_add(arguments, data); + if (new_state == ISIS_ADJ_DOWN) { + snprintf(xpath_arg, sizeof(xpath_arg), "%s/reason", xpath); + data = yang_data_new_string(xpath_arg, reason); + listnode_add(arguments, data); + } + + nb_notification_send(xpath, arguments); +} + +/* + * XPath: /frr-isisd:rejected-adjacency + */ +void isis_notif_reject_adjacency(const struct isis_circuit *circuit, + const char *reason, const char *raw_pdu) +{ + const char *xpath = "/frr-isisd:rejected-adjacency"; + struct list *arguments = yang_data_list_new(); + char xpath_arg[XPATH_MAXLEN]; + struct yang_data *data; + struct isis_area *area = circuit->area; + + notif_prep_instance_hdr(xpath, area, "default", arguments); + notif_prepr_iface_hdr(xpath, circuit, arguments); + snprintf(xpath_arg, sizeof(xpath_arg), "%s/reason", xpath); + data = yang_data_new_string(xpath_arg, reason); + listnode_add(arguments, data); + snprintf(xpath_arg, sizeof(xpath_arg), "%s/raw-pdu", xpath); + data = yang_data_new(xpath_arg, raw_pdu); + listnode_add(arguments, data); + + nb_notification_send(xpath, arguments); +} + +/* + * XPath: /frr-isisd:area-mismatch + */ +void isis_notif_area_mismatch(const struct isis_circuit *circuit, + const char *raw_pdu) +{ + const char *xpath = "/frr-isisd:area-mismatch"; + struct list *arguments = yang_data_list_new(); + char xpath_arg[XPATH_MAXLEN]; + struct yang_data *data; + struct isis_area *area = circuit->area; + + notif_prep_instance_hdr(xpath, area, "default", arguments); + notif_prepr_iface_hdr(xpath, circuit, arguments); + snprintf(xpath_arg, sizeof(xpath_arg), "%s/raw-pdu", xpath); + data = yang_data_new(xpath_arg, raw_pdu); + listnode_add(arguments, data); + + nb_notification_send(xpath, arguments); +} + +/* + * XPath: /frr-isisd:lsp-received + */ +void isis_notif_lsp_received(const struct isis_circuit *circuit, + const char *lsp_id, uint32_t seqno, + uint32_t timestamp, const char *sys_id) +{ + const char *xpath = "/frr-isisd:lsp-received"; + struct list *arguments = yang_data_list_new(); + char xpath_arg[XPATH_MAXLEN]; + struct yang_data *data; + struct isis_area *area = circuit->area; + + notif_prep_instance_hdr(xpath, area, "default", arguments); + notif_prepr_iface_hdr(xpath, circuit, arguments); + snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath); + data = yang_data_new_string(xpath_arg, lsp_id); + listnode_add(arguments, data); + snprintf(xpath_arg, sizeof(xpath_arg), "%s/sequence", xpath); + data = yang_data_new_uint32(xpath_arg, seqno); + listnode_add(arguments, data); + snprintf(xpath_arg, sizeof(xpath_arg), "%s/received-timestamp", xpath); + data = yang_data_new_uint32(xpath_arg, timestamp); + listnode_add(arguments, data); + snprintf(xpath_arg, sizeof(xpath_arg), "%s/neighbor-system-id", xpath); + data = yang_data_new_string(xpath_arg, sys_id); + listnode_add(arguments, data); + + nb_notification_send(xpath, arguments); +} + +/* + * XPath: /frr-isisd:lsp-generation + */ +void isis_notif_lsp_gen(const struct isis_area *area, const char *lsp_id, + uint32_t seqno, uint32_t timestamp) +{ + const char *xpath = "/frr-isisd:lsp-generation"; + struct list *arguments = yang_data_list_new(); + char xpath_arg[XPATH_MAXLEN]; + struct yang_data *data; + + notif_prep_instance_hdr(xpath, area, "default", arguments); + snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath); + data = yang_data_new_string(xpath_arg, lsp_id); + listnode_add(arguments, data); + snprintf(xpath_arg, sizeof(xpath_arg), "%s/sequence", xpath); + data = yang_data_new_uint32(xpath_arg, seqno); + listnode_add(arguments, data); + snprintf(xpath_arg, sizeof(xpath_arg), "%s/send-timestamp", xpath); + data = yang_data_new_uint32(xpath_arg, timestamp); + listnode_add(arguments, data); + + nb_notification_send(xpath, arguments); +} + +/* + * XPath: /frr-isisd:id-len-mismatch + */ +void isis_notif_id_len_mismatch(const struct isis_circuit *circuit, + uint8_t rcv_id_len, const char *raw_pdu) +{ + const char *xpath = "/frr-isisd:id-len-mismatch"; + struct list *arguments = yang_data_list_new(); + char xpath_arg[XPATH_MAXLEN]; + struct yang_data *data; + struct isis_area *area = circuit->area; + + notif_prep_instance_hdr(xpath, area, "default", arguments); + notif_prepr_iface_hdr(xpath, circuit, arguments); + snprintf(xpath_arg, sizeof(xpath_arg), "%s/pdu-field-len", xpath); + data = yang_data_new_uint8(xpath_arg, rcv_id_len); + listnode_add(arguments, data); + snprintf(xpath_arg, sizeof(xpath_arg), "%s/raw-pdu", xpath); + data = yang_data_new(xpath_arg, raw_pdu); + listnode_add(arguments, data); + + nb_notification_send(xpath, arguments); +} + +/* + * XPath: /frr-isisd:version-skew + */ +void isis_notif_version_skew(const struct isis_circuit *circuit, + uint8_t version, const char *raw_pdu) +{ + const char *xpath = "/frr-isisd:version-skew"; + struct list *arguments = yang_data_list_new(); + char xpath_arg[XPATH_MAXLEN]; + struct yang_data *data; + struct isis_area *area = circuit->area; + + notif_prep_instance_hdr(xpath, area, "default", arguments); + notif_prepr_iface_hdr(xpath, circuit, arguments); + snprintf(xpath_arg, sizeof(xpath_arg), "%s/protocol-version", xpath); + data = yang_data_new_uint8(xpath_arg, version); + listnode_add(arguments, data); + snprintf(xpath_arg, sizeof(xpath_arg), "%s/raw-pdu", xpath); + data = yang_data_new(xpath_arg, raw_pdu); + listnode_add(arguments, data); + + nb_notification_send(xpath, arguments); +} + +/* + * XPath: /frr-isisd:lsp-error-detected + */ +void isis_notif_lsp_error(const struct isis_circuit *circuit, + const char *lsp_id, const char *raw_pdu, + __attribute__((unused)) uint32_t offset, + __attribute__((unused)) uint8_t tlv_type) +{ + const char *xpath = "/frr-isisd:lsp-error-detected"; + struct list *arguments = yang_data_list_new(); + char xpath_arg[XPATH_MAXLEN]; + struct yang_data *data; + struct isis_area *area = circuit->area; + + notif_prep_instance_hdr(xpath, area, "default", arguments); + notif_prepr_iface_hdr(xpath, circuit, arguments); + snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath); + data = yang_data_new_string(xpath_arg, lsp_id); + listnode_add(arguments, data); + snprintf(xpath_arg, sizeof(xpath_arg), "%s/raw-pdu", xpath); + data = yang_data_new(xpath_arg, raw_pdu); + listnode_add(arguments, data); + /* ignore offset and tlv_type which cannot be set properly */ + + nb_notification_send(xpath, arguments); +} + +/* + * XPath: /frr-isisd:sequence-number-skipped + */ +void isis_notif_seqno_skipped(const struct isis_circuit *circuit, + const char *lsp_id) +{ + const char *xpath = "/frr-isisd:sequence-number-skipped"; + struct list *arguments = yang_data_list_new(); + char xpath_arg[XPATH_MAXLEN]; + struct yang_data *data; + struct isis_area *area = circuit->area; + + notif_prep_instance_hdr(xpath, area, "default", arguments); + notif_prepr_iface_hdr(xpath, circuit, arguments); + snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath); + data = yang_data_new_string(xpath_arg, lsp_id); + listnode_add(arguments, data); + + nb_notification_send(xpath, arguments); +} + +/* + * XPath: /frr-isisd:own-lsp-purge + */ +void isis_notif_own_lsp_purge(const struct isis_circuit *circuit, + const char *lsp_id) +{ + const char *xpath = "/frr-isisd:own-lsp-purge"; + struct list *arguments = yang_data_list_new(); + char xpath_arg[XPATH_MAXLEN]; + struct yang_data *data; + struct isis_area *area = circuit->area; + + notif_prep_instance_hdr(xpath, area, "default", arguments); + notif_prepr_iface_hdr(xpath, circuit, arguments); + snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath); + data = yang_data_new_string(xpath_arg, lsp_id); + listnode_add(arguments, data); + + nb_notification_send(xpath, arguments); +} diff --git a/isisd/isis_nb_state.c b/isisd/isis_nb_state.c new file mode 100644 index 0000000000..4e325ed8da --- /dev/null +++ b/isisd/isis_nb_state.c @@ -0,0 +1,415 @@ +/* + * Copyright (C) 2018 Volta Networks + * Emanuele Di Pascale + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "northbound.h" +#include "linklist.h" + +#include "isisd/isisd.h" +#include "isisd/isis_nb.h" +#include "isisd/isis_circuit.h" +#include "isisd/isis_adjacency.h" +#include "isisd/isis_misc.h" + +/* + * XPath: /frr-interface:lib/interface/state/frr-isisd:isis + */ +struct yang_data * +lib_interface_state_isis_get_elem(struct nb_cb_get_elem_args *args) +{ + struct interface *ifp; + struct isis_circuit *circuit; + + ifp = (struct interface *)args->list_entry; + if (!ifp) + return NULL; + + circuit = circuit_scan_by_ifp(ifp); + if (!circuit || !circuit->area) + return NULL; + + return yang_data_new(args->xpath, NULL); +} + +/* + * XPath: + * /frr-interface:lib/interface/state/frr-isisd:isis/adjacencies/adjacency + */ +const void *lib_interface_state_isis_adjacencies_adjacency_get_next( + struct nb_cb_get_next_args *args) +{ + struct interface *ifp; + struct isis_circuit *circuit; + struct isis_adjacency *adj = NULL, *adj_next = NULL; + struct list *list; + struct listnode *node, *node_next; + + /* Get first adjacency. */ + if (args->list_entry == NULL) { + ifp = (struct interface *)args->parent_list_entry; + if (!ifp) + return NULL; + + circuit = circuit_scan_by_ifp(ifp); + if (!circuit) + return NULL; + + switch (circuit->circ_type) { + case CIRCUIT_T_BROADCAST: + for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; + level++) { + struct list *adjdb; + + adjdb = circuit->u.bc.adjdb[level - 1]; + if (adjdb) { + adj = listnode_head(adjdb); + if (adj) + break; + } + } + break; + case CIRCUIT_T_P2P: + adj = circuit->u.p2p.neighbor; + break; + default: + break; + } + + return adj; + } + + /* Get next adjacency. */ + adj = (struct isis_adjacency *)args->list_entry; + circuit = adj->circuit; + switch (circuit->circ_type) { + case CIRCUIT_T_BROADCAST: + list = circuit->u.bc.adjdb[adj->level - 1]; + node = listnode_lookup(list, adj); + node_next = listnextnode(node); + if (node_next) + adj_next = listgetdata(node_next); + else if (adj->level == ISIS_LEVEL1) { + /* + * Once we finish the L1 adjacencies, move to the L2 + * adjacencies list. + */ + list = circuit->u.bc.adjdb[ISIS_LEVEL2 - 1]; + adj_next = listnode_head(list); + } + break; + case CIRCUIT_T_P2P: + /* P2P circuits have at most one adjacency. */ + default: + break; + } + + return adj_next; +} + +/* + * XPath: + * /frr-interface:lib/interface/state/frr-isisd:isis/adjacencies/adjacency/neighbor-sys-type + */ +struct yang_data * +lib_interface_state_isis_adjacencies_adjacency_neighbor_sys_type_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct isis_adjacency *adj = args->list_entry; + + return yang_data_new_enum(args->xpath, adj->level); +} + +/* + * XPath: + * /frr-interface:lib/interface/state/frr-isisd:isis/adjacencies/adjacency/neighbor-sysid + */ +struct yang_data * +lib_interface_state_isis_adjacencies_adjacency_neighbor_sysid_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct isis_adjacency *adj = args->list_entry; + + return yang_data_new_string(args->xpath, sysid_print(adj->sysid)); +} + +/* + * XPath: + * /frr-interface:lib/interface/state/frr-isisd:isis/adjacencies/adjacency/neighbor-extended-circuit-id + */ +struct yang_data * +lib_interface_state_isis_adjacencies_adjacency_neighbor_extended_circuit_id_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct isis_adjacency *adj = args->list_entry; + + return yang_data_new_uint32(args->xpath, adj->circuit->circuit_id); +} + +/* + * XPath: + * /frr-interface:lib/interface/state/frr-isisd:isis/adjacencies/adjacency/neighbor-snpa + */ +struct yang_data * +lib_interface_state_isis_adjacencies_adjacency_neighbor_snpa_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct isis_adjacency *adj = args->list_entry; + + return yang_data_new_string(args->xpath, snpa_print(adj->snpa)); +} + +/* + * XPath: + * /frr-interface:lib/interface/state/frr-isisd:isis/adjacencies/adjacency/hold-timer + */ +struct yang_data * +lib_interface_state_isis_adjacencies_adjacency_hold_timer_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct isis_adjacency *adj = args->list_entry; + + return yang_data_new_uint16(args->xpath, adj->hold_time); +} + +/* + * XPath: + * /frr-interface:lib/interface/state/frr-isisd:isis/adjacencies/adjacency/neighbor-priority + */ +struct yang_data * +lib_interface_state_isis_adjacencies_adjacency_neighbor_priority_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct isis_adjacency *adj = args->list_entry; + + return yang_data_new_uint8(args->xpath, adj->prio[adj->level - 1]); +} + +/* + * XPath: + * /frr-interface:lib/interface/state/frr-isisd:isis/adjacencies/adjacency/state + */ +struct yang_data *lib_interface_state_isis_adjacencies_adjacency_state_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct isis_adjacency *adj = args->list_entry; + + return yang_data_new_string(args->xpath, + isis_adj_yang_state(adj->adj_state)); +} + +/* + * XPath: + * /frr-interface:lib/interface/state/frr-isisd:isis/event-counters/adjacency-changes + */ +struct yang_data * +lib_interface_state_isis_event_counters_adjacency_changes_get_elem( + struct nb_cb_get_elem_args *args) +{ + struct interface *ifp; + struct isis_circuit *circuit; + + ifp = (struct interface *)args->list_entry; + if (!ifp) + return NULL; + + circuit = circuit_scan_by_ifp(ifp); + if (!circuit) + return NULL; + + return yang_data_new_uint32(args->xpath, circuit->adj_state_changes); +} + +/* + * XPath: + * /frr-interface:lib/interface/state/frr-isisd:isis/event-counters/adjacency-number + */ +struct yang_data * +lib_interface_state_isis_event_counters_adjacency_number_get_elem( + struct nb_cb_get_elem_args *args) +{ + struct interface *ifp; + struct isis_circuit *circuit; + struct isis_adjacency *adj; + struct listnode *node; + uint32_t total = 0; + + ifp = (struct interface *)args->list_entry; + if (!ifp) + return NULL; + + circuit = circuit_scan_by_ifp(ifp); + if (!circuit) + return NULL; + + /* + * TODO: keep track of the number of adjacencies instead of calculating + * it on demand. + */ + switch (circuit->circ_type) { + case CIRCUIT_T_BROADCAST: + for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) { + for (ALL_LIST_ELEMENTS_RO( + circuit->u.bc.adjdb[level - 1], node, adj)) + total++; + } + break; + case CIRCUIT_T_P2P: + adj = circuit->u.p2p.neighbor; + if (adj) + total = 1; + break; + default: + break; + } + + return yang_data_new_uint32(args->xpath, total); +} + +/* + * XPath: + * /frr-interface:lib/interface/state/frr-isisd:isis/event-counters/init-fails + */ +struct yang_data *lib_interface_state_isis_event_counters_init_fails_get_elem( + struct nb_cb_get_elem_args *args) +{ + struct interface *ifp; + struct isis_circuit *circuit; + + ifp = (struct interface *)args->list_entry; + if (!ifp) + return NULL; + + circuit = circuit_scan_by_ifp(ifp); + if (!circuit) + return NULL; + + return yang_data_new_uint32(args->xpath, circuit->init_failures); +} + +/* + * XPath: + * /frr-interface:lib/interface/state/frr-isisd:isis/event-counters/adjacency-rejects + */ +struct yang_data * +lib_interface_state_isis_event_counters_adjacency_rejects_get_elem( + struct nb_cb_get_elem_args *args) +{ + struct interface *ifp; + struct isis_circuit *circuit; + + ifp = (struct interface *)args->list_entry; + if (!ifp) + return NULL; + + circuit = circuit_scan_by_ifp(ifp); + if (!circuit) + return NULL; + + return yang_data_new_uint32(args->xpath, circuit->rej_adjacencies); +} + +/* + * XPath: + * /frr-interface:lib/interface/state/frr-isisd:isis/event-counters/id-len-mismatch + */ +struct yang_data * +lib_interface_state_isis_event_counters_id_len_mismatch_get_elem( + struct nb_cb_get_elem_args *args) +{ + struct interface *ifp; + struct isis_circuit *circuit; + + ifp = (struct interface *)args->list_entry; + if (!ifp) + return NULL; + + circuit = circuit_scan_by_ifp(ifp); + if (!circuit) + return NULL; + + return yang_data_new_uint32(args->xpath, circuit->id_len_mismatches); +} + +/* + * XPath: + * /frr-interface:lib/interface/state/frr-isisd:isis/event-counters/max-area-addresses-mismatch + */ +struct yang_data * +lib_interface_state_isis_event_counters_max_area_addresses_mismatch_get_elem( + struct nb_cb_get_elem_args *args) +{ + struct interface *ifp; + struct isis_circuit *circuit; + + ifp = (struct interface *)args->list_entry; + if (!ifp) + return NULL; + + circuit = circuit_scan_by_ifp(ifp); + if (!circuit) + return NULL; + + return yang_data_new_uint32(args->xpath, + circuit->max_area_addr_mismatches); +} + +/* + * XPath: + * /frr-interface:lib/interface/state/frr-isisd:isis/event-counters/authentication-type-fails + */ +struct yang_data * +lib_interface_state_isis_event_counters_authentication_type_fails_get_elem( + struct nb_cb_get_elem_args *args) +{ + struct interface *ifp; + struct isis_circuit *circuit; + + ifp = (struct interface *)args->list_entry; + if (!ifp) + return NULL; + + circuit = circuit_scan_by_ifp(ifp); + if (!circuit) + return NULL; + + return yang_data_new_uint32(args->xpath, circuit->auth_type_failures); +} + +/* + * XPath: + * /frr-interface:lib/interface/state/frr-isisd:isis/event-counters/authentication-fails + */ +struct yang_data * +lib_interface_state_isis_event_counters_authentication_fails_get_elem( + struct nb_cb_get_elem_args *args) +{ + struct interface *ifp; + struct isis_circuit *circuit; + + ifp = (struct interface *)args->list_entry; + if (!ifp) + return NULL; + + circuit = circuit_scan_by_ifp(ifp); + if (!circuit) + return NULL; + + return yang_data_new_uint32(args->xpath, circuit->auth_failures); +} diff --git a/isisd/isis_northbound.c b/isisd/isis_northbound.c deleted file mode 100644 index d5cdec154b..0000000000 --- a/isisd/isis_northbound.c +++ /dev/null @@ -1,3221 +0,0 @@ -/* - * Copyright (C) 2001,2002 Sampo Saaristo - * Tampere University of Technology - * Institute of Communications Engineering - * Copyright (C) 2018 Volta Networks - * Emanuele Di Pascale - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; see the file COPYING; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include "northbound.h" -#include "libfrr.h" -#include "linklist.h" -#include "log.h" -#include "isisd/isis_constants.h" -#include "isisd/isis_common.h" -#include "isisd/isis_flags.h" -#include "isisd/isis_circuit.h" -#include "isisd/isisd.h" -#include "isisd/isis_lsp.h" -#include "isisd/isis_pdu.h" -#include "isisd/isis_dynhn.h" -#include "isisd/isis_misc.h" -#include "isisd/isis_csm.h" -#include "isisd/isis_adjacency.h" -#include "isisd/isis_spf.h" -#include "isisd/isis_te.h" -#include "isisd/isis_memory.h" -#include "isisd/isis_mt.h" -#include "isisd/isis_cli.h" -#include "isisd/isis_redist.h" -#include "lib/spf_backoff.h" -#include "lib/lib_errors.h" -#include "lib/vrf.h" - -/* - * XPath: /frr-isisd:isis/instance - */ -static int isis_instance_create(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct isis_area *area; - const char *area_tag; - - if (event != NB_EV_APPLY) - return NB_OK; - - area_tag = yang_dnode_get_string(dnode, "./area-tag"); - area = isis_area_lookup(area_tag); - if (area) - return NB_ERR_INCONSISTENCY; - - area = isis_area_create(area_tag); - /* save area in dnode to avoid looking it up all the time */ - nb_running_set_entry(dnode, area); - - return NB_OK; -} - -static int isis_instance_destroy(enum nb_event event, - const struct lyd_node *dnode) -{ - struct isis_area *area; - - if (event != NB_EV_APPLY) - return NB_OK; - - area = nb_running_unset_entry(dnode); - isis_area_destroy(area->area_tag); - - return NB_OK; -} - -/* - * XPath: /frr-isisd:isis/instance/is-type - */ -static int isis_instance_is_type_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct isis_area *area; - int type; - - if (event != NB_EV_APPLY) - return NB_OK; - - area = nb_running_get_entry(dnode, NULL, true); - type = yang_dnode_get_enum(dnode, NULL); - isis_area_is_type_set(area, type); - - return NB_OK; -} - -/* - * XPath: /frr-isisd:isis/instance/area-address - */ -static int isis_instance_area_address_create(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct isis_area *area; - struct area_addr addr, *addrr = NULL, *addrp = NULL; - struct listnode *node; - uint8_t buff[255]; - const char *net_title = yang_dnode_get_string(dnode, NULL); - - switch (event) { - case NB_EV_VALIDATE: - addr.addr_len = dotformat2buff(buff, net_title); - memcpy(addr.area_addr, buff, addr.addr_len); - if (addr.area_addr[addr.addr_len - 1] != 0) { - flog_warn( - EC_LIB_NB_CB_CONFIG_VALIDATE, - "nsel byte (last byte) in area address must be 0"); - return NB_ERR_VALIDATION; - } - if (isis->sysid_set) { - /* Check that the SystemID portions match */ - if (memcmp(isis->sysid, GETSYSID((&addr)), - ISIS_SYS_ID_LEN)) { - flog_warn( - EC_LIB_NB_CB_CONFIG_VALIDATE, - "System ID must not change when defining additional area addresses"); - return NB_ERR_VALIDATION; - } - } - break; - case NB_EV_PREPARE: - addrr = XMALLOC(MTYPE_ISIS_AREA_ADDR, sizeof(struct area_addr)); - addrr->addr_len = dotformat2buff(buff, net_title); - memcpy(addrr->area_addr, buff, addrr->addr_len); - resource->ptr = addrr; - break; - case NB_EV_ABORT: - XFREE(MTYPE_ISIS_AREA_ADDR, resource->ptr); - break; - case NB_EV_APPLY: - area = nb_running_get_entry(dnode, NULL, true); - addrr = resource->ptr; - - if (isis->sysid_set == 0) { - /* - * First area address - get the SystemID for this router - */ - memcpy(isis->sysid, GETSYSID(addrr), ISIS_SYS_ID_LEN); - isis->sysid_set = 1; - } else { - /* check that we don't already have this address */ - for (ALL_LIST_ELEMENTS_RO(area->area_addrs, node, - addrp)) { - if ((addrp->addr_len + ISIS_SYS_ID_LEN - + ISIS_NSEL_LEN) - != (addrr->addr_len)) - continue; - if (!memcmp(addrp->area_addr, addrr->area_addr, - addrr->addr_len)) { - XFREE(MTYPE_ISIS_AREA_ADDR, addrr); - return NB_OK; /* silent fail */ - } - } - } - - /*Forget the systemID part of the address */ - addrr->addr_len -= (ISIS_SYS_ID_LEN + ISIS_NSEL_LEN); - assert(area->area_addrs); /* to silence scan-build sillyness */ - listnode_add(area->area_addrs, addrr); - - /* only now we can safely generate our LSPs for this area */ - if (listcount(area->area_addrs) > 0) { - if (area->is_type & IS_LEVEL_1) - lsp_generate(area, IS_LEVEL_1); - if (area->is_type & IS_LEVEL_2) - lsp_generate(area, IS_LEVEL_2); - } - break; - } - - return NB_OK; -} - -static int isis_instance_area_address_destroy(enum nb_event event, - const struct lyd_node *dnode) -{ - struct area_addr addr, *addrp = NULL; - struct listnode *node; - uint8_t buff[255]; - struct isis_area *area; - const char *net_title; - - if (event != NB_EV_APPLY) - return NB_OK; - - net_title = yang_dnode_get_string(dnode, NULL); - addr.addr_len = dotformat2buff(buff, net_title); - memcpy(addr.area_addr, buff, (int)addr.addr_len); - area = nb_running_get_entry(dnode, NULL, true); - for (ALL_LIST_ELEMENTS_RO(area->area_addrs, node, addrp)) { - if ((addrp->addr_len + ISIS_SYS_ID_LEN + 1) == addr.addr_len - && !memcmp(addrp->area_addr, addr.area_addr, addr.addr_len)) - break; - } - if (!addrp) - return NB_ERR_INCONSISTENCY; - - listnode_delete(area->area_addrs, addrp); - XFREE(MTYPE_ISIS_AREA_ADDR, addrp); - /* - * Last area address - reset the SystemID for this router - */ - if (listcount(area->area_addrs) == 0) { - memset(isis->sysid, 0, ISIS_SYS_ID_LEN); - isis->sysid_set = 0; - if (isis->debugs & DEBUG_EVENTS) - zlog_debug("Router has no SystemID"); - } - - return NB_OK; -} - -/* - * XPath: /frr-isisd:isis/instance/dynamic-hostname - */ -static int isis_instance_dynamic_hostname_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct isis_area *area; - - if (event != NB_EV_APPLY) - return NB_OK; - - area = nb_running_get_entry(dnode, NULL, true); - isis_area_dynhostname_set(area, yang_dnode_get_bool(dnode, NULL)); - - return NB_OK; -} - -/* - * XPath: /frr-isisd:isis/instance/attached - */ -static int isis_instance_attached_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct isis_area *area; - bool attached; - - if (event != NB_EV_APPLY) - return NB_OK; - - area = nb_running_get_entry(dnode, NULL, true); - attached = yang_dnode_get_bool(dnode, NULL); - isis_area_attached_bit_set(area, attached); - - return NB_OK; -} - -/* - * XPath: /frr-isisd:isis/instance/overload - */ -static int isis_instance_overload_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct isis_area *area; - bool overload; - - if (event != NB_EV_APPLY) - return NB_OK; - - area = nb_running_get_entry(dnode, NULL, true); - overload = yang_dnode_get_bool(dnode, NULL); - isis_area_overload_bit_set(area, overload); - - return NB_OK; -} - -/* - * XPath: /frr-isisd:isis/instance/metric-style - */ -static int isis_instance_metric_style_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct isis_area *area; - bool old_metric, new_metric; - enum isis_metric_style metric_style = yang_dnode_get_enum(dnode, NULL); - - if (event != NB_EV_APPLY) - return NB_OK; - - area = nb_running_get_entry(dnode, NULL, true); - old_metric = (metric_style == ISIS_WIDE_METRIC) ? false : true; - new_metric = (metric_style == ISIS_NARROW_METRIC) ? false : true; - isis_area_metricstyle_set(area, old_metric, new_metric); - - return NB_OK; -} - -/* - * XPath: /frr-isisd:isis/instance/purge-originator - */ -static int isis_instance_purge_originator_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct isis_area *area; - - if (event != NB_EV_APPLY) - return NB_OK; - - area = nb_running_get_entry(dnode, NULL, true); - area->purge_originator = yang_dnode_get_bool(dnode, NULL); - - return NB_OK; -} - -/* - * XPath: /frr-isisd:isis/instance/lsp/mtu - */ -static int isis_instance_lsp_mtu_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct listnode *node; - struct isis_circuit *circuit; - uint16_t lsp_mtu = yang_dnode_get_uint16(dnode, NULL); - struct isis_area *area; - - switch (event) { - case NB_EV_VALIDATE: - area = nb_running_get_entry(dnode, NULL, false); - if (!area) - break; - for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) { - if (circuit->state != C_STATE_INIT - && circuit->state != C_STATE_UP) - continue; - if (lsp_mtu > isis_circuit_pdu_size(circuit)) { - flog_warn( - EC_LIB_NB_CB_CONFIG_VALIDATE, - "ISIS area contains circuit %s, which has a maximum PDU size of %zu", - circuit->interface->name, - isis_circuit_pdu_size(circuit)); - return NB_ERR_VALIDATION; - } - } - break; - case NB_EV_PREPARE: - case NB_EV_ABORT: - break; - case NB_EV_APPLY: - area = nb_running_get_entry(dnode, NULL, true); - isis_area_lsp_mtu_set(area, lsp_mtu); - break; - } - - return NB_OK; -} - -/* - * XPath: /frr-isisd:isis/instance/lsp/refresh-interval/level-1 - */ -static int -isis_instance_lsp_refresh_interval_level_1_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct isis_area *area; - uint16_t refr_int; - - if (event != NB_EV_APPLY) - return NB_OK; - - refr_int = yang_dnode_get_uint16(dnode, NULL); - area = nb_running_get_entry(dnode, NULL, true); - isis_area_lsp_refresh_set(area, IS_LEVEL_1, refr_int); - - return NB_OK; -} - -/* - * XPath: /frr-isisd:isis/instance/lsp/refresh-interval/level-2 - */ -static int -isis_instance_lsp_refresh_interval_level_2_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct isis_area *area; - uint16_t refr_int; - - if (event != NB_EV_APPLY) - return NB_OK; - - refr_int = yang_dnode_get_uint16(dnode, NULL); - area = nb_running_get_entry(dnode, NULL, true); - isis_area_lsp_refresh_set(area, IS_LEVEL_2, refr_int); - - return NB_OK; -} - -/* - * XPath: /frr-isisd:isis/instance/lsp/maximum-lifetime/level-1 - */ -static int -isis_instance_lsp_maximum_lifetime_level_1_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct isis_area *area; - uint16_t max_lt; - - if (event != NB_EV_APPLY) - return NB_OK; - - max_lt = yang_dnode_get_uint16(dnode, NULL); - area = nb_running_get_entry(dnode, NULL, true); - isis_area_max_lsp_lifetime_set(area, IS_LEVEL_1, max_lt); - - return NB_OK; -} - -/* - * XPath: /frr-isisd:isis/instance/lsp/maximum-lifetime/level-2 - */ -static int -isis_instance_lsp_maximum_lifetime_level_2_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct isis_area *area; - uint16_t max_lt; - - if (event != NB_EV_APPLY) - return NB_OK; - - max_lt = yang_dnode_get_uint16(dnode, NULL); - area = nb_running_get_entry(dnode, NULL, true); - isis_area_max_lsp_lifetime_set(area, IS_LEVEL_2, max_lt); - - return NB_OK; -} - -/* - * XPath: /frr-isisd:isis/instance/lsp/generation-interval/level-1 - */ -static int isis_instance_lsp_generation_interval_level_1_modify( - enum nb_event event, const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct isis_area *area; - uint16_t gen_int; - - if (event != NB_EV_APPLY) - return NB_OK; - - gen_int = yang_dnode_get_uint16(dnode, NULL); - area = nb_running_get_entry(dnode, NULL, true); - area->lsp_gen_interval[0] = gen_int; - - return NB_OK; -} - -/* - * XPath: /frr-isisd:isis/instance/lsp/generation-interval/level-2 - */ -static int isis_instance_lsp_generation_interval_level_2_modify( - enum nb_event event, const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct isis_area *area; - uint16_t gen_int; - - if (event != NB_EV_APPLY) - return NB_OK; - - gen_int = yang_dnode_get_uint16(dnode, NULL); - area = nb_running_get_entry(dnode, NULL, true); - area->lsp_gen_interval[1] = gen_int; - - return NB_OK; -} - -/* - * XPath: /frr-isisd:isis/instance/spf/ietf-backoff-delay - */ -static void ietf_backoff_delay_apply_finish(const struct lyd_node *dnode) -{ - long init_delay = yang_dnode_get_uint16(dnode, "./init-delay"); - long short_delay = yang_dnode_get_uint16(dnode, "./short-delay"); - long long_delay = yang_dnode_get_uint16(dnode, "./long-delay"); - long holddown = yang_dnode_get_uint16(dnode, "./hold-down"); - long timetolearn = yang_dnode_get_uint16(dnode, "./time-to-learn"); - struct isis_area *area = nb_running_get_entry(dnode, NULL, true); - size_t bufsiz = strlen(area->area_tag) + sizeof("IS-IS Lx"); - char *buf = XCALLOC(MTYPE_TMP, bufsiz); - - snprintf(buf, bufsiz, "IS-IS %s L1", area->area_tag); - spf_backoff_free(area->spf_delay_ietf[0]); - area->spf_delay_ietf[0] = - spf_backoff_new(master, buf, init_delay, short_delay, - long_delay, holddown, timetolearn); - - snprintf(buf, bufsiz, "IS-IS %s L2", area->area_tag); - spf_backoff_free(area->spf_delay_ietf[1]); - area->spf_delay_ietf[1] = - spf_backoff_new(master, buf, init_delay, short_delay, - long_delay, holddown, timetolearn); - - XFREE(MTYPE_TMP, buf); -} - -static int -isis_instance_spf_ietf_backoff_delay_create(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - /* All the work is done in the apply_finish */ - return NB_OK; -} - -static int -isis_instance_spf_ietf_backoff_delay_destroy(enum nb_event event, - const struct lyd_node *dnode) -{ - struct isis_area *area; - - if (event != NB_EV_APPLY) - return NB_OK; - - area = nb_running_get_entry(dnode, NULL, true); - spf_backoff_free(area->spf_delay_ietf[0]); - spf_backoff_free(area->spf_delay_ietf[1]); - area->spf_delay_ietf[0] = NULL; - area->spf_delay_ietf[1] = NULL; - - return NB_OK; -} - -/* - * XPath: /frr-isisd:isis/instance/spf/ietf-backoff-delay/init-delay - */ -static int isis_instance_spf_ietf_backoff_delay_init_delay_modify( - enum nb_event event, const struct lyd_node *dnode, - union nb_resource *resource) -{ - /* All the work is done in the apply_finish */ - return NB_OK; -} - -/* - * XPath: /frr-isisd:isis/instance/spf/ietf-backoff-delay/short-delay - */ -static int isis_instance_spf_ietf_backoff_delay_short_delay_modify( - enum nb_event event, const struct lyd_node *dnode, - union nb_resource *resource) -{ - /* All the work is done in the apply_finish */ - return NB_OK; -} - -/* - * XPath: /frr-isisd:isis/instance/spf/ietf-backoff-delay/long-delay - */ -static int isis_instance_spf_ietf_backoff_delay_long_delay_modify( - enum nb_event event, const struct lyd_node *dnode, - union nb_resource *resource) -{ - /* All the work is done in the apply_finish */ - return NB_OK; -} - -/* - * XPath: /frr-isisd:isis/instance/spf/ietf-backoff-delay/hold-down - */ -static int isis_instance_spf_ietf_backoff_delay_hold_down_modify( - enum nb_event event, const struct lyd_node *dnode, - union nb_resource *resource) -{ - /* All the work is done in the apply_finish */ - return NB_OK; -} - -/* - * XPath: /frr-isisd:isis/instance/spf/ietf-backoff-delay/time-to-learn - */ -static int isis_instance_spf_ietf_backoff_delay_time_to_learn_modify( - enum nb_event event, const struct lyd_node *dnode, - union nb_resource *resource) -{ - /* All the work is done in the apply_finish */ - return NB_OK; -} - -/* - * XPath: /frr-isisd:isis/instance/spf/minimum-interval/level-1 - */ -static int -isis_instance_spf_minimum_interval_level_1_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct isis_area *area; - - if (event != NB_EV_APPLY) - return NB_OK; - - area = nb_running_get_entry(dnode, NULL, true); - area->min_spf_interval[0] = yang_dnode_get_uint16(dnode, NULL); - - return NB_OK; -} - -/* - * XPath: /frr-isisd:isis/instance/spf/minimum-interval/level-2 - */ -static int -isis_instance_spf_minimum_interval_level_2_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct isis_area *area; - - if (event != NB_EV_APPLY) - return NB_OK; - - area = nb_running_get_entry(dnode, NULL, true); - area->min_spf_interval[1] = yang_dnode_get_uint16(dnode, NULL); - - return NB_OK; -} - -/* - * XPath: /frr-isisd:isis/instance/area-password - */ -static void area_password_apply_finish(const struct lyd_node *dnode) -{ - const char *password = yang_dnode_get_string(dnode, "./password"); - struct isis_area *area = nb_running_get_entry(dnode, NULL, true); - int pass_type = yang_dnode_get_enum(dnode, "./password-type"); - uint8_t snp_auth = yang_dnode_get_enum(dnode, "./authenticate-snp"); - - switch (pass_type) { - case ISIS_PASSWD_TYPE_CLEARTXT: - isis_area_passwd_cleartext_set(area, IS_LEVEL_1, password, - snp_auth); - break; - case ISIS_PASSWD_TYPE_HMAC_MD5: - isis_area_passwd_hmac_md5_set(area, IS_LEVEL_1, password, - snp_auth); - break; - } -} - -static int isis_instance_area_password_create(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - /* actual setting is done in apply_finish */ - return NB_OK; -} - -static int isis_instance_area_password_destroy(enum nb_event event, - const struct lyd_node *dnode) -{ - struct isis_area *area; - - if (event != NB_EV_APPLY) - return NB_OK; - - area = nb_running_get_entry(dnode, NULL, true); - isis_area_passwd_unset(area, IS_LEVEL_1); - - return NB_OK; -} - -/* - * XPath: /frr-isisd:isis/instance/area-password/password - */ -static int -isis_instance_area_password_password_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - /* actual setting is done in apply_finish */ - return NB_OK; -} - -/* - * XPath: /frr-isisd:isis/instance/area-password/password-type - */ -static int -isis_instance_area_password_password_type_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - /* actual setting is done in apply_finish */ - return NB_OK; -} - -/* - * XPath: /frr-isisd:isis/instance/area-password/authenticate-snp - */ -static int isis_instance_area_password_authenticate_snp_modify( - enum nb_event event, const struct lyd_node *dnode, - union nb_resource *resource) -{ - /* actual setting is done in apply_finish */ - return NB_OK; -} - -/* - * XPath: /frr-isisd:isis/instance/domain-password - */ -static void domain_password_apply_finish(const struct lyd_node *dnode) -{ - const char *password = yang_dnode_get_string(dnode, "./password"); - struct isis_area *area = nb_running_get_entry(dnode, NULL, true); - int pass_type = yang_dnode_get_enum(dnode, "./password-type"); - uint8_t snp_auth = yang_dnode_get_enum(dnode, "./authenticate-snp"); - - switch (pass_type) { - case ISIS_PASSWD_TYPE_CLEARTXT: - isis_area_passwd_cleartext_set(area, IS_LEVEL_2, password, - snp_auth); - break; - case ISIS_PASSWD_TYPE_HMAC_MD5: - isis_area_passwd_hmac_md5_set(area, IS_LEVEL_2, password, - snp_auth); - break; - } -} - -static int isis_instance_domain_password_create(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - /* actual setting is done in apply_finish */ - return NB_OK; -} - -static int isis_instance_domain_password_destroy(enum nb_event event, - const struct lyd_node *dnode) -{ - struct isis_area *area; - - if (event != NB_EV_APPLY) - return NB_OK; - - area = nb_running_get_entry(dnode, NULL, true); - isis_area_passwd_unset(area, IS_LEVEL_2); - - return NB_OK; -} - -/* - * XPath: /frr-isisd:isis/instance/domain-password/password - */ -static int -isis_instance_domain_password_password_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - /* actual setting is done in apply_finish */ - return NB_OK; -} - -/* - * XPath: /frr-isisd:isis/instance/domain-password/password-type - */ -static int -isis_instance_domain_password_password_type_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - /* actual setting is done in apply_finish */ - return NB_OK; -} - -/* - * XPath: /frr-isisd:isis/instance/domain-password/authenticate-snp - */ -static int isis_instance_domain_password_authenticate_snp_modify( - enum nb_event event, const struct lyd_node *dnode, - union nb_resource *resource) -{ - /* actual setting is done in apply_finish */ - return NB_OK; -} - -/* - * XPath: /frr-isisd:isis/instance/default-information-originate/ipv4 - */ -static void default_info_origin_apply_finish(const struct lyd_node *dnode, - int family) -{ - int originate_type = DEFAULT_ORIGINATE; - unsigned long metric = 0; - const char *routemap = NULL; - struct isis_area *area = nb_running_get_entry(dnode, NULL, true); - int level = yang_dnode_get_enum(dnode, "./level"); - - if (yang_dnode_get_bool(dnode, "./always")) { - originate_type = DEFAULT_ORIGINATE_ALWAYS; - } else if (family == AF_INET6) { - zlog_warn( - "%s: Zebra doesn't implement default-originate for IPv6 yet, so use with care or use default-originate always.", - __func__); - } - - if (yang_dnode_exists(dnode, "./metric")) - metric = yang_dnode_get_uint32(dnode, "./metric"); - if (yang_dnode_exists(dnode, "./route-map")) - routemap = yang_dnode_get_string(dnode, "./route-map"); - - isis_redist_set(area, level, family, DEFAULT_ROUTE, metric, routemap, - originate_type); -} - -static void default_info_origin_ipv4_apply_finish(const struct lyd_node *dnode) -{ - default_info_origin_apply_finish(dnode, AF_INET); -} - -static void default_info_origin_ipv6_apply_finish(const struct lyd_node *dnode) -{ - default_info_origin_apply_finish(dnode, AF_INET6); -} - -static int isis_instance_default_information_originate_ipv4_create( - enum nb_event event, const struct lyd_node *dnode, - union nb_resource *resource) -{ - /* It's all done by default_info_origin_apply_finish */ - return NB_OK; -} - -static int isis_instance_default_information_originate_ipv4_destroy( - enum nb_event event, const struct lyd_node *dnode) -{ - struct isis_area *area; - int level; - - if (event != NB_EV_APPLY) - return NB_OK; - - area = nb_running_get_entry(dnode, NULL, true); - level = yang_dnode_get_enum(dnode, "./level"); - isis_redist_unset(area, level, AF_INET, DEFAULT_ROUTE); - - return NB_OK; -} - -/* - * XPath: /frr-isisd:isis/instance/default-information-originate/ipv4/always - */ -static int isis_instance_default_information_originate_ipv4_always_modify( - enum nb_event event, const struct lyd_node *dnode, - union nb_resource *resource) -{ - /* It's all done by default_info_origin_apply_finish */ - return NB_OK; -} - -/* - * XPath: /frr-isisd:isis/instance/default-information-originate/ipv4/route-map - */ -static int isis_instance_default_information_originate_ipv4_route_map_modify( - enum nb_event event, const struct lyd_node *dnode, - union nb_resource *resource) -{ - /* It's all done by default_info_origin_apply_finish */ - return NB_OK; -} - -static int isis_instance_default_information_originate_ipv4_route_map_destroy( - enum nb_event event, const struct lyd_node *dnode) -{ - /* It's all done by default_info_origin_apply_finish */ - return NB_OK; -} - -/* - * XPath: /frr-isisd:isis/instance/default-information-originate/ipv4/metric - */ -static int isis_instance_default_information_originate_ipv4_metric_modify( - enum nb_event event, const struct lyd_node *dnode, - union nb_resource *resource) -{ - /* It's all done by default_info_origin_apply_finish */ - return NB_OK; -} - -/* - * XPath: /frr-isisd:isis/instance/default-information-originate/ipv6 - */ -static int isis_instance_default_information_originate_ipv6_create( - enum nb_event event, const struct lyd_node *dnode, - union nb_resource *resource) -{ - /* It's all done by default_info_origin_apply_finish */ - return NB_OK; -} - -static int isis_instance_default_information_originate_ipv6_destroy( - enum nb_event event, const struct lyd_node *dnode) -{ - struct isis_area *area; - int level; - - if (event != NB_EV_APPLY) - return NB_OK; - - area = nb_running_get_entry(dnode, NULL, true); - level = yang_dnode_get_enum(dnode, "./level"); - isis_redist_unset(area, level, AF_INET6, DEFAULT_ROUTE); - - return NB_OK; -} - -/* - * XPath: /frr-isisd:isis/instance/default-information-originate/ipv6/always - */ -static int isis_instance_default_information_originate_ipv6_always_modify( - enum nb_event event, const struct lyd_node *dnode, - union nb_resource *resource) -{ - /* It's all done by default_info_origin_apply_finish */ - return NB_OK; -} - -/* - * XPath: /frr-isisd:isis/instance/default-information-originate/ipv6/route-map - */ -static int isis_instance_default_information_originate_ipv6_route_map_modify( - enum nb_event event, const struct lyd_node *dnode, - union nb_resource *resource) -{ - /* It's all done by default_info_origin_apply_finish */ - return NB_OK; -} - -static int isis_instance_default_information_originate_ipv6_route_map_destroy( - enum nb_event event, const struct lyd_node *dnode) -{ - /* It's all done by default_info_origin_apply_finish */ - return NB_OK; -} - -/* - * XPath: /frr-isisd:isis/instance/default-information-originate/ipv6/metric - */ -static int isis_instance_default_information_originate_ipv6_metric_modify( - enum nb_event event, const struct lyd_node *dnode, - union nb_resource *resource) -{ - /* It's all done by default_info_origin_apply_finish */ - return NB_OK; -} - -/* - * XPath: /frr-isisd:isis/instance/redistribute/ipv4 - */ -static void redistribute_apply_finish(const struct lyd_node *dnode, int family) -{ - assert(family == AF_INET || family == AF_INET6); - int type, level; - unsigned long metric = 0; - const char *routemap = NULL; - struct isis_area *area; - - type = yang_dnode_get_enum(dnode, "./protocol"); - level = yang_dnode_get_enum(dnode, "./level"); - area = nb_running_get_entry(dnode, NULL, true); - - if (yang_dnode_exists(dnode, "./metric")) - metric = yang_dnode_get_uint32(dnode, "./metric"); - if (yang_dnode_exists(dnode, "./route-map")) - routemap = yang_dnode_get_string(dnode, "./route-map"); - - isis_redist_set(area, level, family, type, metric, routemap, 0); -} - -static void redistribute_ipv4_apply_finish(const struct lyd_node *dnode) -{ - redistribute_apply_finish(dnode, AF_INET); -} - -static void redistribute_ipv6_apply_finish(const struct lyd_node *dnode) -{ - redistribute_apply_finish(dnode, AF_INET6); -} - -static int isis_instance_redistribute_ipv4_create(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - /* It's all done by redistribute_apply_finish */ - return NB_OK; -} - -static int isis_instance_redistribute_ipv4_destroy(enum nb_event event, - const struct lyd_node *dnode) -{ - struct isis_area *area; - int level, type; - - if (event != NB_EV_APPLY) - return NB_OK; - - area = nb_running_get_entry(dnode, NULL, true); - level = yang_dnode_get_enum(dnode, "./level"); - type = yang_dnode_get_enum(dnode, "./protocol"); - isis_redist_unset(area, level, AF_INET, type); - - return NB_OK; -} - -/* - * XPath: /frr-isisd:isis/instance/redistribute/ipv4/route-map - */ -static int -isis_instance_redistribute_ipv4_route_map_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - /* It's all done by redistribute_apply_finish */ - return NB_OK; -} - -static int -isis_instance_redistribute_ipv4_route_map_destroy(enum nb_event event, - const struct lyd_node *dnode) -{ - /* It's all done by redistribute_apply_finish */ - return NB_OK; -} - -/* - * XPath: /frr-isisd:isis/instance/redistribute/ipv4/metric - */ -static int -isis_instance_redistribute_ipv4_metric_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - /* It's all done by redistribute_apply_finish */ - return NB_OK; -} - -/* - * XPath: /frr-isisd:isis/instance/redistribute/ipv6 - */ -static int isis_instance_redistribute_ipv6_create(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - /* It's all done by redistribute_apply_finish */ - return NB_OK; -} - -static int isis_instance_redistribute_ipv6_destroy(enum nb_event event, - const struct lyd_node *dnode) -{ - struct isis_area *area; - int level, type; - - if (event != NB_EV_APPLY) - return NB_OK; - - area = nb_running_get_entry(dnode, NULL, true); - level = yang_dnode_get_enum(dnode, "./level"); - type = yang_dnode_get_enum(dnode, "./protocol"); - isis_redist_unset(area, level, AF_INET6, type); - - return NB_OK; -} - -/* - * XPath: /frr-isisd:isis/instance/redistribute/ipv6/route-map - */ -static int -isis_instance_redistribute_ipv6_route_map_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - /* It's all done by redistribute_apply_finish */ - return NB_OK; -} - -static int -isis_instance_redistribute_ipv6_route_map_destroy(enum nb_event event, - const struct lyd_node *dnode) -{ - /* It's all done by redistribute_apply_finish */ - return NB_OK; -} - -/* - * XPath: /frr-isisd:isis/instance/redistribute/ipv6/metric - */ -static int -isis_instance_redistribute_ipv6_metric_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - /* It's all done by redistribute_apply_finish */ - return NB_OK; -} - -/* - * XPath: /frr-isisd:isis/instance/multi-topology/ipv4-multicast - */ -static int isis_multi_topology_common(enum nb_event event, - const struct lyd_node *dnode, - const char *topology, bool create) -{ - struct isis_area *area; - struct isis_area_mt_setting *setting; - uint16_t mtid = isis_str2mtid(topology); - - switch (event) { - case NB_EV_VALIDATE: - if (mtid == (uint16_t)-1) { - flog_warn(EC_LIB_NB_CB_CONFIG_VALIDATE, - "Unknown topology %s", topology); - return NB_ERR_VALIDATION; - } - break; - case NB_EV_PREPARE: - case NB_EV_ABORT: - break; - case NB_EV_APPLY: - area = nb_running_get_entry(dnode, NULL, true); - setting = area_get_mt_setting(area, mtid); - setting->enabled = create; - lsp_regenerate_schedule(area, IS_LEVEL_1 | IS_LEVEL_2, 0); - break; - } - - return NB_OK; -} - -static int isis_multi_topology_overload_common(enum nb_event event, - const struct lyd_node *dnode, - const char *topology) -{ - struct isis_area *area; - struct isis_area_mt_setting *setting; - uint16_t mtid = isis_str2mtid(topology); - - /* validation is done in isis_multi_topology_common */ - if (event != NB_EV_APPLY) - return NB_OK; - - area = nb_running_get_entry(dnode, NULL, true); - setting = area_get_mt_setting(area, mtid); - setting->overload = yang_dnode_get_bool(dnode, NULL); - if (setting->enabled) - lsp_regenerate_schedule(area, IS_LEVEL_1 | IS_LEVEL_2, 0); - - return NB_OK; -} - -static int -isis_instance_multi_topology_ipv4_multicast_create(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - return isis_multi_topology_common(event, dnode, "ipv4-multicast", true); -} - -static int -isis_instance_multi_topology_ipv4_multicast_destroy(enum nb_event event, - const struct lyd_node *dnode) -{ - return isis_multi_topology_common(event, dnode, "ipv4-multicast", - false); -} - -/* - * XPath: /frr-isisd:isis/instance/multi-topology/ipv4-multicast/overload - */ -static int isis_instance_multi_topology_ipv4_multicast_overload_modify( - enum nb_event event, const struct lyd_node *dnode, - union nb_resource *resource) -{ - return isis_multi_topology_overload_common(event, dnode, - "ipv4-multicast"); -} - -/* - * XPath: /frr-isisd:isis/instance/multi-topology/ipv4-management - */ -static int isis_instance_multi_topology_ipv4_management_create( - enum nb_event event, const struct lyd_node *dnode, - union nb_resource *resource) -{ - return isis_multi_topology_common(event, dnode, "ipv4-mgmt", true); -} - -static int isis_instance_multi_topology_ipv4_management_destroy( - enum nb_event event, const struct lyd_node *dnode) -{ - return isis_multi_topology_common(event, dnode, "ipv4-mgmt", false); -} - -/* - * XPath: /frr-isisd:isis/instance/multi-topology/ipv4-management/overload - */ -static int isis_instance_multi_topology_ipv4_management_overload_modify( - enum nb_event event, const struct lyd_node *dnode, - union nb_resource *resource) -{ - return isis_multi_topology_overload_common(event, dnode, "ipv4-mgmt"); -} - -/* - * XPath: /frr-isisd:isis/instance/multi-topology/ipv6-unicast - */ -static int -isis_instance_multi_topology_ipv6_unicast_create(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - return isis_multi_topology_common(event, dnode, "ipv6-unicast", true); -} - -static int -isis_instance_multi_topology_ipv6_unicast_destroy(enum nb_event event, - const struct lyd_node *dnode) -{ - return isis_multi_topology_common(event, dnode, "ipv6-unicast", false); -} - -/* - * XPath: /frr-isisd:isis/instance/multi-topology/ipv6-unicast/overload - */ -static int isis_instance_multi_topology_ipv6_unicast_overload_modify( - enum nb_event event, const struct lyd_node *dnode, - union nb_resource *resource) -{ - return isis_multi_topology_overload_common(event, dnode, - "ipv6-unicast"); -} - -/* - * XPath: /frr-isisd:isis/instance/multi-topology/ipv6-multicast - */ -static int -isis_instance_multi_topology_ipv6_multicast_create(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - return isis_multi_topology_common(event, dnode, "ipv6-multicast", true); -} - -static int -isis_instance_multi_topology_ipv6_multicast_destroy(enum nb_event event, - const struct lyd_node *dnode) -{ - return isis_multi_topology_common(event, dnode, "ipv6-multicast", - false); -} - -/* - * XPath: /frr-isisd:isis/instance/multi-topology/ipv6-multicast/overload - */ -static int isis_instance_multi_topology_ipv6_multicast_overload_modify( - enum nb_event event, const struct lyd_node *dnode, - union nb_resource *resource) -{ - return isis_multi_topology_overload_common(event, dnode, - "ipv6-multicast"); -} - -/* - * XPath: /frr-isisd:isis/instance/multi-topology/ipv6-management - */ -static int isis_instance_multi_topology_ipv6_management_create( - enum nb_event event, const struct lyd_node *dnode, - union nb_resource *resource) -{ - return isis_multi_topology_common(event, dnode, "ipv6-mgmt", true); -} - -static int isis_instance_multi_topology_ipv6_management_destroy( - enum nb_event event, const struct lyd_node *dnode) -{ - return isis_multi_topology_common(event, dnode, "ipv6-mgmt", false); -} - -/* - * XPath: /frr-isisd:isis/instance/multi-topology/ipv6-management/overload - */ -static int isis_instance_multi_topology_ipv6_management_overload_modify( - enum nb_event event, const struct lyd_node *dnode, - union nb_resource *resource) -{ - return isis_multi_topology_overload_common(event, dnode, "ipv6-mgmt"); -} - -/* - * XPath: /frr-isisd:isis/instance/multi-topology/ipv6-dstsrc - */ -static int -isis_instance_multi_topology_ipv6_dstsrc_create(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - return isis_multi_topology_common(event, dnode, "ipv6-dstsrc", true); -} - -static int -isis_instance_multi_topology_ipv6_dstsrc_destroy(enum nb_event event, - const struct lyd_node *dnode) -{ - return isis_multi_topology_common(event, dnode, "ipv6-dstsrc", false); -} - -/* - * XPath: /frr-isisd:isis/instance/multi-topology/ipv6-dstsrc/overload - */ -static int isis_instance_multi_topology_ipv6_dstsrc_overload_modify( - enum nb_event event, const struct lyd_node *dnode, - union nb_resource *resource) -{ - return isis_multi_topology_overload_common(event, dnode, "ipv6-dstsrc"); -} - -/* - * XPath: /frr-isisd:isis/instance/log-adjacency-changes - */ -static int -isis_instance_log_adjacency_changes_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct isis_area *area; - bool log = yang_dnode_get_bool(dnode, NULL); - - if (event != NB_EV_APPLY) - return NB_OK; - - area = nb_running_get_entry(dnode, NULL, true); - area->log_adj_changes = log ? 1 : 0; - - return NB_OK; -} - -/* - * XPath: /frr-isisd:isis/instance/mpls-te - */ -static int isis_instance_mpls_te_create(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct listnode *node; - struct isis_area *area; - struct isis_circuit *circuit; - - if (event != NB_EV_APPLY) - return NB_OK; - - area = nb_running_get_entry(dnode, NULL, true); - if (area->mta == NULL) { - - struct mpls_te_area *new; - - zlog_debug("ISIS MPLS-TE: Initialize area %s", - area->area_tag); - - new = XCALLOC(MTYPE_ISIS_MPLS_TE, sizeof(struct mpls_te_area)); - - /* Initialize MPLS_TE structure */ - new->status = enable; - new->level = 0; - new->inter_as = off; - new->interas_areaid.s_addr = 0; - new->router_id.s_addr = 0; - - area->mta = new; - } else { - area->mta->status = enable; - } - - /* - * Following code is intended to handle two cases; - * - * 1) MPLS-TE was disabled at startup time, but now become enabled. - * In this case, we must enable MPLS-TE Circuit regarding interface - * MPLS_TE flag - * 2) MPLS-TE was once enabled then disabled, and now enabled again. - */ - for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) { - if (circuit->mtc == NULL || IS_FLOOD_AS(circuit->mtc->type)) - continue; - - if (!IS_MPLS_TE(circuit->mtc) - && HAS_LINK_PARAMS(circuit->interface)) - circuit->mtc->status = enable; - else - continue; - - /* Reoriginate STD_TE & GMPLS circuits */ - if (circuit->area) - lsp_regenerate_schedule(circuit->area, circuit->is_type, - 0); - } - - return NB_OK; -} - -static int isis_instance_mpls_te_destroy(enum nb_event event, - const struct lyd_node *dnode) -{ - struct listnode *node; - struct isis_area *area; - struct isis_circuit *circuit; - - if (event != NB_EV_APPLY) - return NB_OK; - - area = nb_running_get_entry(dnode, NULL, true); - if (IS_MPLS_TE(area->mta)) - area->mta->status = disable; - else - return NB_OK; - - /* Flush LSP if circuit engage */ - for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) { - if (circuit->mtc == NULL || (circuit->mtc->status == disable)) - continue; - - /* disable MPLS_TE Circuit */ - circuit->mtc->status = disable; - - /* Re-originate circuit without STD_TE & GMPLS parameters */ - if (circuit->area) - lsp_regenerate_schedule(circuit->area, circuit->is_type, - 0); - } - - return NB_OK; -} - -/* - * XPath: /frr-isisd:isis/instance/mpls-te/router-address - */ -static int isis_instance_mpls_te_router_address_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct in_addr value; - struct isis_area *area; - - if (event != NB_EV_APPLY) - return NB_OK; - - area = nb_running_get_entry(dnode, NULL, true); - /* only proceed if MPLS-TE is enabled */ - if (!IS_MPLS_TE(area->mta)) - return NB_OK; - - /* Update Area Router ID */ - yang_dnode_get_ipv4(&value, dnode, NULL); - area->mta->router_id.s_addr = value.s_addr; - - /* And re-schedule LSP update */ - if (listcount(area->area_addrs) > 0) - lsp_regenerate_schedule(area, area->is_type, 0); - - return NB_OK; -} - -static int isis_instance_mpls_te_router_address_destroy(enum nb_event event, - const struct lyd_node *dnode) -{ - struct isis_area *area; - - if (event != NB_EV_APPLY) - return NB_OK; - - area = nb_running_get_entry(dnode, NULL, true); - /* only proceed if MPLS-TE is enabled */ - if (!IS_MPLS_TE(area->mta)) - return NB_OK; - - /* Reset Area Router ID */ - area->mta->router_id.s_addr = INADDR_ANY; - - /* And re-schedule LSP update */ - if (listcount(area->area_addrs) > 0) - lsp_regenerate_schedule(area, area->is_type, 0); - - return NB_OK; -} - -/* - * XPath: /frr-interface:lib/interface/frr-isisd:isis - */ -static int lib_interface_isis_create(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct isis_area *area; - struct interface *ifp; - struct isis_circuit *circuit; - const char *area_tag = yang_dnode_get_string(dnode, "./area-tag"); - - if (event != NB_EV_APPLY) - return NB_OK; - - area = isis_area_lookup(area_tag); - /* The area should have already be created. We are - * setting the priority of the global isis area creation - * slightly lower, so it should be executed first, but I - * cannot rely on that so here I have to check. - */ - if (!area) { - flog_err( - EC_LIB_NB_CB_CONFIG_APPLY, - "%s: attempt to create circuit for area %s before the area has been created", - __func__, area_tag); - abort(); - } - - ifp = nb_running_get_entry(dnode, NULL, true); - circuit = isis_circuit_create(area, ifp); - assert(circuit->state == C_STATE_CONF || circuit->state == C_STATE_UP); - nb_running_set_entry(dnode, circuit); - - return NB_OK; -} - -static int lib_interface_isis_destroy(enum nb_event event, - const struct lyd_node *dnode) -{ - struct isis_circuit *circuit; - - if (event != NB_EV_APPLY) - return NB_OK; - - circuit = nb_running_unset_entry(dnode); - if (!circuit) - return NB_ERR_INCONSISTENCY; - /* delete circuit through csm changes */ - switch (circuit->state) { - case C_STATE_UP: - isis_csm_state_change(IF_DOWN_FROM_Z, circuit, - circuit->interface); - isis_csm_state_change(ISIS_DISABLE, circuit, circuit->area); - break; - case C_STATE_CONF: - isis_csm_state_change(ISIS_DISABLE, circuit, circuit->area); - break; - case C_STATE_INIT: - isis_csm_state_change(IF_DOWN_FROM_Z, circuit, - circuit->interface); - break; - } - - return NB_OK; -} - -/* - * XPath: /frr-interface:lib/interface/frr-isisd:isis/area-tag - */ -static int lib_interface_isis_area_tag_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct isis_circuit *circuit; - struct interface *ifp; - struct vrf *vrf; - const char *area_tag, *ifname, *vrfname; - - if (event == NB_EV_VALIDATE) { - /* libyang doesn't like relative paths across module boundaries - */ - ifname = yang_dnode_get_string(dnode->parent->parent, "./name"); - vrfname = yang_dnode_get_string(dnode->parent->parent, "./vrf"); - vrf = vrf_lookup_by_name(vrfname); - assert(vrf); - ifp = if_lookup_by_name(ifname, vrf->vrf_id); - if (!ifp) - return NB_OK; - circuit = circuit_lookup_by_ifp(ifp, isis->init_circ_list); - area_tag = yang_dnode_get_string(dnode, NULL); - if (circuit && circuit->area && circuit->area->area_tag - && strcmp(circuit->area->area_tag, area_tag)) { - flog_warn(EC_LIB_NB_CB_CONFIG_VALIDATE, - "ISIS circuit is already defined on %s", - circuit->area->area_tag); - return NB_ERR_VALIDATION; - } - } - - return NB_OK; -} - -/* - * XPath: /frr-interface:lib/interface/frr-isisd:isis/circuit-type - */ -static int lib_interface_isis_circuit_type_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - int circ_type = yang_dnode_get_enum(dnode, NULL); - struct isis_circuit *circuit; - struct interface *ifp; - struct vrf *vrf; - const char *ifname, *vrfname; - - switch (event) { - case NB_EV_VALIDATE: - /* libyang doesn't like relative paths across module boundaries - */ - ifname = yang_dnode_get_string(dnode->parent->parent, "./name"); - vrfname = yang_dnode_get_string(dnode->parent->parent, "./vrf"); - vrf = vrf_lookup_by_name(vrfname); - assert(vrf); - ifp = if_lookup_by_name(ifname, vrf->vrf_id); - if (!ifp) - break; - circuit = circuit_lookup_by_ifp(ifp, isis->init_circ_list); - if (circuit && circuit->state == C_STATE_UP - && circuit->area->is_type != IS_LEVEL_1_AND_2 - && circuit->area->is_type != circ_type) { - flog_warn(EC_LIB_NB_CB_CONFIG_VALIDATE, - "Invalid circuit level for area %s", - circuit->area->area_tag); - return NB_ERR_VALIDATION; - } - break; - case NB_EV_PREPARE: - case NB_EV_ABORT: - break; - case NB_EV_APPLY: - circuit = nb_running_get_entry(dnode, NULL, true); - isis_circuit_is_type_set(circuit, circ_type); - break; - } - - return NB_OK; -} - -/* - * XPath: /frr-interface:lib/interface/frr-isisd:isis/ipv4-routing - */ -static int lib_interface_isis_ipv4_routing_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - bool ipv4, ipv6; - struct isis_circuit *circuit; - - if (event != NB_EV_APPLY) - return NB_OK; - - circuit = nb_running_get_entry(dnode, NULL, true); - ipv4 = yang_dnode_get_bool(dnode, NULL); - ipv6 = yang_dnode_get_bool(dnode, "../ipv6-routing"); - isis_circuit_af_set(circuit, ipv4, ipv6); - - return NB_OK; -} - -/* - * XPath: /frr-interface:lib/interface/frr-isisd:isis/ipv6-routing - */ -static int lib_interface_isis_ipv6_routing_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - bool ipv4, ipv6; - struct isis_circuit *circuit; - - if (event != NB_EV_APPLY) - return NB_OK; - - circuit = nb_running_get_entry(dnode, NULL, true); - ipv4 = yang_dnode_exists(dnode, "../ipv4-routing"); - ipv6 = yang_dnode_get_bool(dnode, NULL); - isis_circuit_af_set(circuit, ipv4, ipv6); - - return NB_OK; -} - -/* - * XPath: /frr-interface:lib/interface/frr-isisd:isis/csnp-interval/level-1 - */ -static int -lib_interface_isis_csnp_interval_level_1_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct isis_circuit *circuit; - - if (event != NB_EV_APPLY) - return NB_OK; - - circuit = nb_running_get_entry(dnode, NULL, true); - circuit->csnp_interval[0] = yang_dnode_get_uint16(dnode, NULL); - - return NB_OK; -} - -/* - * XPath: /frr-interface:lib/interface/frr-isisd:isis/csnp-interval/level-2 - */ -static int -lib_interface_isis_csnp_interval_level_2_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct isis_circuit *circuit; - - if (event != NB_EV_APPLY) - return NB_OK; - - circuit = nb_running_get_entry(dnode, NULL, true); - circuit->csnp_interval[1] = yang_dnode_get_uint16(dnode, NULL); - - return NB_OK; -} - -/* - * XPath: /frr-interface:lib/interface/frr-isisd:isis/psnp-interval/level-1 - */ -static int -lib_interface_isis_psnp_interval_level_1_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct isis_circuit *circuit; - - if (event != NB_EV_APPLY) - return NB_OK; - - circuit = nb_running_get_entry(dnode, NULL, true); - circuit->psnp_interval[0] = yang_dnode_get_uint16(dnode, NULL); - - return NB_OK; -} - -/* - * XPath: /frr-interface:lib/interface/frr-isisd:isis/psnp-interval/level-2 - */ -static int -lib_interface_isis_psnp_interval_level_2_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct isis_circuit *circuit; - - if (event != NB_EV_APPLY) - return NB_OK; - - circuit = nb_running_get_entry(dnode, NULL, true); - circuit->psnp_interval[1] = yang_dnode_get_uint16(dnode, NULL); - - return NB_OK; -} - -/* - * XPath: /frr-interface:lib/interface/frr-isisd:isis/hello/padding - */ -static int lib_interface_isis_hello_padding_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct isis_circuit *circuit; - - if (event != NB_EV_APPLY) - return NB_OK; - - circuit = nb_running_get_entry(dnode, NULL, true); - circuit->pad_hellos = yang_dnode_get_bool(dnode, NULL); - - return NB_OK; -} - -/* - * XPath: /frr-interface:lib/interface/frr-isisd:isis/hello/interval/level-1 - */ -static int -lib_interface_isis_hello_interval_level_1_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct isis_circuit *circuit; - uint32_t interval; - - if (event != NB_EV_APPLY) - return NB_OK; - - circuit = nb_running_get_entry(dnode, NULL, true); - interval = yang_dnode_get_uint32(dnode, NULL); - circuit->hello_interval[0] = interval; - - return NB_OK; -} - -/* - * XPath: /frr-interface:lib/interface/frr-isisd:isis/hello/interval/level-2 - */ -static int -lib_interface_isis_hello_interval_level_2_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct isis_circuit *circuit; - uint32_t interval; - - if (event != NB_EV_APPLY) - return NB_OK; - - circuit = nb_running_get_entry(dnode, NULL, true); - interval = yang_dnode_get_uint32(dnode, NULL); - circuit->hello_interval[1] = interval; - - return NB_OK; -} - -/* - * XPath: /frr-interface:lib/interface/frr-isisd:isis/hello/multiplier/level-1 - */ -static int -lib_interface_isis_hello_multiplier_level_1_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct isis_circuit *circuit; - uint16_t multi; - - if (event != NB_EV_APPLY) - return NB_OK; - - circuit = nb_running_get_entry(dnode, NULL, true); - multi = yang_dnode_get_uint16(dnode, NULL); - circuit->hello_multiplier[0] = multi; - - return NB_OK; -} - -/* - * XPath: /frr-interface:lib/interface/frr-isisd:isis/hello/multiplier/level-2 - */ -static int -lib_interface_isis_hello_multiplier_level_2_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct isis_circuit *circuit; - uint16_t multi; - - if (event != NB_EV_APPLY) - return NB_OK; - - circuit = nb_running_get_entry(dnode, NULL, true); - multi = yang_dnode_get_uint16(dnode, NULL); - circuit->hello_multiplier[1] = multi; - - return NB_OK; -} - -/* - * XPath: /frr-interface:lib/interface/frr-isisd:isis/metric/level-1 - */ -static int -lib_interface_isis_metric_level_1_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct isis_circuit *circuit; - unsigned int met; - - if (event != NB_EV_APPLY) - return NB_OK; - - circuit = nb_running_get_entry(dnode, NULL, true); - met = yang_dnode_get_uint32(dnode, NULL); - isis_circuit_metric_set(circuit, IS_LEVEL_1, met); - - return NB_OK; -} - -/* - * XPath: /frr-interface:lib/interface/frr-isisd:isis/metric/level-2 - */ -static int -lib_interface_isis_metric_level_2_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct isis_circuit *circuit; - unsigned int met; - - if (event != NB_EV_APPLY) - return NB_OK; - - circuit = nb_running_get_entry(dnode, NULL, true); - met = yang_dnode_get_uint32(dnode, NULL); - isis_circuit_metric_set(circuit, IS_LEVEL_2, met); - - return NB_OK; -} - -/* - * XPath: /frr-interface:lib/interface/frr-isisd:isis/priority/level-1 - */ -static int -lib_interface_isis_priority_level_1_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct isis_circuit *circuit; - - if (event != NB_EV_APPLY) - return NB_OK; - - circuit = nb_running_get_entry(dnode, NULL, true); - circuit->priority[0] = yang_dnode_get_uint8(dnode, NULL); - - return NB_OK; -} - -/* - * XPath: /frr-interface:lib/interface/frr-isisd:isis/priority/level-2 - */ -static int -lib_interface_isis_priority_level_2_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct isis_circuit *circuit; - - if (event != NB_EV_APPLY) - return NB_OK; - - circuit = nb_running_get_entry(dnode, NULL, true); - circuit->priority[1] = yang_dnode_get_uint8(dnode, NULL); - - return NB_OK; -} - -/* - * XPath: /frr-interface:lib/interface/frr-isisd:isis/network-type - */ -static int lib_interface_isis_network_type_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct isis_circuit *circuit; - int net_type = yang_dnode_get_enum(dnode, NULL); - - switch (event) { - case NB_EV_VALIDATE: - circuit = nb_running_get_entry(dnode, NULL, false); - if (!circuit) - break; - if (circuit->circ_type == CIRCUIT_T_LOOPBACK) { - flog_warn( - EC_LIB_NB_CB_CONFIG_VALIDATE, - "Cannot change network type on loopback interface"); - return NB_ERR_VALIDATION; - } - if (net_type == CIRCUIT_T_BROADCAST - && circuit->state == C_STATE_UP - && !if_is_broadcast(circuit->interface)) { - flog_warn( - EC_LIB_NB_CB_CONFIG_VALIDATE, - "Cannot configure non-broadcast interface for broadcast operation"); - return NB_ERR_VALIDATION; - } - break; - case NB_EV_PREPARE: - case NB_EV_ABORT: - break; - case NB_EV_APPLY: - circuit = nb_running_get_entry(dnode, NULL, true); - isis_circuit_circ_type_set(circuit, net_type); - break; - } - - return NB_OK; -} - -/* - * XPath: /frr-interface:lib/interface/frr-isisd:isis/passive - */ -static int lib_interface_isis_passive_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct isis_circuit *circuit; - struct isis_area *area; - struct interface *ifp; - bool passive = yang_dnode_get_bool(dnode, NULL); - - /* validation only applies if we are setting passive to false */ - if (!passive && event == NB_EV_VALIDATE) { - circuit = nb_running_get_entry(dnode, NULL, false); - if (!circuit) - return NB_OK; - ifp = circuit->interface; - if (!ifp) - return NB_OK; - if (if_is_loopback(ifp)) { - flog_warn(EC_LIB_NB_CB_CONFIG_VALIDATE, - "Loopback is always passive"); - return NB_ERR_VALIDATION; - } - } - - if (event != NB_EV_APPLY) - return NB_OK; - - circuit = nb_running_get_entry(dnode, NULL, true); - if (circuit->state != C_STATE_UP) { - circuit->is_passive = passive; - } else { - area = circuit->area; - isis_csm_state_change(ISIS_DISABLE, circuit, area); - circuit->is_passive = passive; - isis_csm_state_change(ISIS_ENABLE, circuit, area); - } - - return NB_OK; -} - -/* - * XPath: /frr-interface:lib/interface/frr-isisd:isis/password - */ -static int lib_interface_isis_password_create(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - return NB_OK; -} - -static int lib_interface_isis_password_destroy(enum nb_event event, - const struct lyd_node *dnode) -{ - struct isis_circuit *circuit; - - if (event != NB_EV_APPLY) - return NB_OK; - - circuit = nb_running_get_entry(dnode, NULL, true); - isis_circuit_passwd_unset(circuit); - - return NB_OK; -} - -/* - * XPath: /frr-interface:lib/interface/frr-isisd:isis/password/password - */ -static int -lib_interface_isis_password_password_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct isis_circuit *circuit; - const char *password; - - if (event != NB_EV_APPLY) - return NB_OK; - - password = yang_dnode_get_string(dnode, NULL); - circuit = nb_running_get_entry(dnode, NULL, true); - - isis_circuit_passwd_set(circuit, circuit->passwd.type, password); - - return NB_OK; -} - -/* - * XPath: /frr-interface:lib/interface/frr-isisd:isis/password/password-type - */ -static int -lib_interface_isis_password_password_type_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct isis_circuit *circuit; - uint8_t pass_type; - - if (event != NB_EV_APPLY) - return NB_OK; - - pass_type = yang_dnode_get_enum(dnode, NULL); - circuit = nb_running_get_entry(dnode, NULL, true); - circuit->passwd.type = pass_type; - - return NB_OK; -} - -/* - * XPath: - * /frr-interface:lib/interface/frr-isisd:isis/disable-three-way-handshake - */ -static int lib_interface_isis_disable_three_way_handshake_modify( - enum nb_event event, const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct isis_circuit *circuit; - - if (event != NB_EV_APPLY) - return NB_OK; - - circuit = nb_running_get_entry(dnode, NULL, true); - circuit->disable_threeway_adj = yang_dnode_get_bool(dnode, NULL); - - return NB_OK; -} - -/* - * XPath: - * /frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv4-unicast - */ -static int lib_interface_isis_multi_topology_common( - enum nb_event event, const struct lyd_node *dnode, uint16_t mtid) -{ - struct isis_circuit *circuit; - bool value; - - switch (event) { - case NB_EV_VALIDATE: - circuit = nb_running_get_entry(dnode, NULL, false); - if (circuit && circuit->area && circuit->area->oldmetric) { - flog_warn( - EC_LIB_NB_CB_CONFIG_VALIDATE, - "Multi topology IS-IS can only be used with wide metrics"); - return NB_ERR_VALIDATION; - } - break; - case NB_EV_PREPARE: - case NB_EV_ABORT: - break; - case NB_EV_APPLY: - circuit = nb_running_get_entry(dnode, NULL, true); - value = yang_dnode_get_bool(dnode, NULL); - isis_circuit_mt_enabled_set(circuit, mtid, value); - break; - } - - return NB_OK; -} - -static int lib_interface_isis_multi_topology_ipv4_unicast_modify( - enum nb_event event, const struct lyd_node *dnode, - union nb_resource *resource) -{ - return lib_interface_isis_multi_topology_common(event, dnode, - ISIS_MT_IPV4_UNICAST); -} - -/* - * XPath: - * /frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv4-multicast - */ -static int lib_interface_isis_multi_topology_ipv4_multicast_modify( - enum nb_event event, const struct lyd_node *dnode, - union nb_resource *resource) -{ - return lib_interface_isis_multi_topology_common(event, dnode, - ISIS_MT_IPV4_MULTICAST); -} - -/* - * XPath: - * /frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv4-management - */ -static int lib_interface_isis_multi_topology_ipv4_management_modify( - enum nb_event event, const struct lyd_node *dnode, - union nb_resource *resource) -{ - return lib_interface_isis_multi_topology_common(event, dnode, - ISIS_MT_IPV4_MGMT); -} - -/* - * XPath: - * /frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv6-unicast - */ -static int lib_interface_isis_multi_topology_ipv6_unicast_modify( - enum nb_event event, const struct lyd_node *dnode, - union nb_resource *resource) -{ - return lib_interface_isis_multi_topology_common(event, dnode, - ISIS_MT_IPV6_UNICAST); -} - -/* - * XPath: - * /frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv6-multicast - */ -static int lib_interface_isis_multi_topology_ipv6_multicast_modify( - enum nb_event event, const struct lyd_node *dnode, - union nb_resource *resource) -{ - return lib_interface_isis_multi_topology_common(event, dnode, - ISIS_MT_IPV6_MULTICAST); -} - -/* - * XPath: - * /frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv6-management - */ -static int lib_interface_isis_multi_topology_ipv6_management_modify( - enum nb_event event, const struct lyd_node *dnode, - union nb_resource *resource) -{ - return lib_interface_isis_multi_topology_common(event, dnode, - ISIS_MT_IPV6_MGMT); -} - -/* - * XPath: /frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv6-dstsrc - */ -static int lib_interface_isis_multi_topology_ipv6_dstsrc_modify( - enum nb_event event, const struct lyd_node *dnode, - union nb_resource *resource) -{ - return lib_interface_isis_multi_topology_common(event, dnode, - ISIS_MT_IPV6_DSTSRC); -} - -/* - * NOTIFICATIONS - */ -static void notif_prep_instance_hdr(const char *xpath, - const struct isis_area *area, - const char *routing_instance, - struct list *args) -{ - char xpath_arg[XPATH_MAXLEN]; - struct yang_data *data; - - snprintf(xpath_arg, sizeof(xpath_arg), "%s/routing-instance", xpath); - data = yang_data_new_string(xpath_arg, routing_instance); - listnode_add(args, data); - snprintf(xpath_arg, sizeof(xpath_arg), "%s/routing-protocol-name", - xpath); - data = yang_data_new_string(xpath_arg, area->area_tag); - listnode_add(args, data); - snprintf(xpath_arg, sizeof(xpath_arg), "%s/isis-level", xpath); - data = yang_data_new_enum(xpath_arg, area->is_type); - listnode_add(args, data); -} - -static void notif_prepr_iface_hdr(const char *xpath, - const struct isis_circuit *circuit, - struct list *args) -{ - char xpath_arg[XPATH_MAXLEN]; - struct yang_data *data; - - snprintf(xpath_arg, sizeof(xpath_arg), "%s/interface-name", xpath); - data = yang_data_new_string(xpath_arg, circuit->interface->name); - listnode_add(args, data); - snprintf(xpath_arg, sizeof(xpath_arg), "%s/interface-level", xpath); - data = yang_data_new_enum(xpath_arg, circuit->is_type); - listnode_add(args, data); - snprintf(xpath_arg, sizeof(xpath_arg), "%s/extended-circuit-id", xpath); - /* we do not seem to have the extended version of the circuit_id */ - data = yang_data_new_uint32(xpath_arg, (uint32_t)circuit->circuit_id); - listnode_add(args, data); -} - -/* - * XPath: - * /frr-isisd:database-overload - */ -void isis_notif_db_overload(const struct isis_area *area, bool overload) -{ - const char *xpath = "/frr-isisd:database-overload"; - struct list *arguments = yang_data_list_new(); - char xpath_arg[XPATH_MAXLEN]; - struct yang_data *data; - - notif_prep_instance_hdr(xpath, area, "default", arguments); - snprintf(xpath_arg, sizeof(xpath_arg), "%s/overload", xpath); - data = yang_data_new_enum(xpath_arg, !!overload); - listnode_add(arguments, data); - - nb_notification_send(xpath, arguments); -} - -/* - * XPath: - * /frr-isisd:lsp-too-large - */ -void isis_notif_lsp_too_large(const struct isis_circuit *circuit, - uint32_t pdu_size, const char *lsp_id) -{ - const char *xpath = "/frr-isisd:lsp-too-large"; - struct list *arguments = yang_data_list_new(); - char xpath_arg[XPATH_MAXLEN]; - struct yang_data *data; - struct isis_area *area = circuit->area; - - notif_prep_instance_hdr(xpath, area, "default", arguments); - notif_prepr_iface_hdr(xpath, circuit, arguments); - snprintf(xpath_arg, sizeof(xpath_arg), "%s/pdu-size", xpath); - data = yang_data_new_uint32(xpath_arg, pdu_size); - listnode_add(arguments, data); - snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath); - data = yang_data_new_string(xpath_arg, lsp_id); - listnode_add(arguments, data); - - nb_notification_send(xpath, arguments); -} - -/* - * XPath: - * /frr-isisd:if-state-change - */ -void isis_notif_if_state_change(const struct isis_circuit *circuit, bool down) -{ - const char *xpath = "/frr-isisd:if-state-change"; - struct list *arguments = yang_data_list_new(); - char xpath_arg[XPATH_MAXLEN]; - struct yang_data *data; - struct isis_area *area = circuit->area; - - notif_prep_instance_hdr(xpath, area, "default", arguments); - notif_prepr_iface_hdr(xpath, circuit, arguments); - snprintf(xpath_arg, sizeof(xpath_arg), "%s/state", xpath); - data = yang_data_new_enum(xpath_arg, !!down); - listnode_add(arguments, data); - - nb_notification_send(xpath, arguments); -} - -/* - * XPath: - * /frr-isisd:corrupted-lsp-detected - */ -void isis_notif_corrupted_lsp(const struct isis_area *area, const char *lsp_id) -{ - const char *xpath = "/frr-isisd:corrupted-lsp-detected"; - struct list *arguments = yang_data_list_new(); - char xpath_arg[XPATH_MAXLEN]; - struct yang_data *data; - - notif_prep_instance_hdr(xpath, area, "default", arguments); - snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath); - data = yang_data_new_string(xpath_arg, lsp_id); - listnode_add(arguments, data); - - nb_notification_send(xpath, arguments); -} - -/* - * XPath: - * /frr-isisd:attempt-to-exceed-max-sequence - */ -void isis_notif_lsp_exceed_max(const struct isis_area *area, const char *lsp_id) -{ - const char *xpath = "/frr-isisd:attempt-to-exceed-max-sequence"; - struct list *arguments = yang_data_list_new(); - char xpath_arg[XPATH_MAXLEN]; - struct yang_data *data; - - notif_prep_instance_hdr(xpath, area, "default", arguments); - snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath); - data = yang_data_new_string(xpath_arg, lsp_id); - listnode_add(arguments, data); - - nb_notification_send(xpath, arguments); -} - -/* - * XPath: - * /frr-isisd:max-area-addresses-mismatch - */ -void isis_notif_max_area_addr_mismatch(const struct isis_circuit *circuit, - uint8_t max_area_addrs, - const char *raw_pdu) -{ - const char *xpath = "/frr-isisd:max-area-addresses-mismatch"; - struct list *arguments = yang_data_list_new(); - char xpath_arg[XPATH_MAXLEN]; - struct yang_data *data; - struct isis_area *area = circuit->area; - - notif_prep_instance_hdr(xpath, area, "default", arguments); - notif_prepr_iface_hdr(xpath, circuit, arguments); - snprintf(xpath_arg, sizeof(xpath_arg), "%s/max-area-addresses", xpath); - data = yang_data_new_uint8(xpath_arg, max_area_addrs); - listnode_add(arguments, data); - snprintf(xpath_arg, sizeof(xpath_arg), "%s/raw-pdu", xpath); - data = yang_data_new(xpath_arg, raw_pdu); - listnode_add(arguments, data); - - nb_notification_send(xpath, arguments); -} - -/* - * XPath: - * /frr-isisd:authentication-type-failure - */ -void isis_notif_authentication_type_failure(const struct isis_circuit *circuit, - const char *raw_pdu) -{ - const char *xpath = "/frr-isisd:authentication-type-failure"; - struct list *arguments = yang_data_list_new(); - char xpath_arg[XPATH_MAXLEN]; - struct yang_data *data; - struct isis_area *area = circuit->area; - - notif_prep_instance_hdr(xpath, area, "default", arguments); - notif_prepr_iface_hdr(xpath, circuit, arguments); - snprintf(xpath_arg, sizeof(xpath_arg), "%s/raw-pdu", xpath); - data = yang_data_new(xpath_arg, raw_pdu); - listnode_add(arguments, data); - - nb_notification_send(xpath, arguments); -} - -/* - * XPath: - * /frr-isisd:authentication-failure - */ -void isis_notif_authentication_failure(const struct isis_circuit *circuit, - const char *raw_pdu) -{ - const char *xpath = "/frr-isisd:authentication-failure"; - struct list *arguments = yang_data_list_new(); - char xpath_arg[XPATH_MAXLEN]; - struct yang_data *data; - struct isis_area *area = circuit->area; - - notif_prep_instance_hdr(xpath, area, "default", arguments); - notif_prepr_iface_hdr(xpath, circuit, arguments); - snprintf(xpath_arg, sizeof(xpath_arg), "%s/raw-pdu", xpath); - data = yang_data_new(xpath_arg, raw_pdu); - listnode_add(arguments, data); - - nb_notification_send(xpath, arguments); -} - -/* - * XPath: - * /frr-isisd:adjacency-state-change - */ -void isis_notif_adj_state_change(const struct isis_adjacency *adj, - int new_state, const char *reason) -{ - const char *xpath = "/frr-isisd:adjacency-state-change"; - struct list *arguments = yang_data_list_new(); - char xpath_arg[XPATH_MAXLEN]; - struct yang_data *data; - struct isis_circuit *circuit = adj->circuit; - struct isis_area *area = circuit->area; - struct isis_dynhn *dyn = dynhn_find_by_id(adj->sysid); - - notif_prep_instance_hdr(xpath, area, "default", arguments); - notif_prepr_iface_hdr(xpath, circuit, arguments); - if (dyn) { - snprintf(xpath_arg, sizeof(xpath_arg), "%s/neighbor", xpath); - data = yang_data_new_string(xpath_arg, dyn->hostname); - listnode_add(arguments, data); - } - snprintf(xpath_arg, sizeof(xpath_arg), "%s/neighbor-system-id", xpath); - data = yang_data_new_string(xpath_arg, sysid_print(adj->sysid)); - listnode_add(arguments, data); - - snprintf(xpath_arg, sizeof(xpath_arg), "%s/state", xpath); - switch (new_state) { - case ISIS_ADJ_DOWN: - data = yang_data_new_string(xpath_arg, "down"); - break; - case ISIS_ADJ_UP: - data = yang_data_new_string(xpath_arg, "up"); - break; - case ISIS_ADJ_INITIALIZING: - data = yang_data_new_string(xpath_arg, "init"); - break; - default: - data = yang_data_new_string(xpath_arg, "failed"); - } - listnode_add(arguments, data); - if (new_state == ISIS_ADJ_DOWN) { - snprintf(xpath_arg, sizeof(xpath_arg), "%s/reason", xpath); - data = yang_data_new_string(xpath_arg, reason); - listnode_add(arguments, data); - } - - nb_notification_send(xpath, arguments); -} - -/* - * XPath: - * /frr-isisd:rejected-adjacency - */ -void isis_notif_reject_adjacency(const struct isis_circuit *circuit, - const char *reason, const char *raw_pdu) -{ - const char *xpath = "/frr-isisd:rejected-adjacency"; - struct list *arguments = yang_data_list_new(); - char xpath_arg[XPATH_MAXLEN]; - struct yang_data *data; - struct isis_area *area = circuit->area; - - notif_prep_instance_hdr(xpath, area, "default", arguments); - notif_prepr_iface_hdr(xpath, circuit, arguments); - snprintf(xpath_arg, sizeof(xpath_arg), "%s/reason", xpath); - data = yang_data_new_string(xpath_arg, reason); - listnode_add(arguments, data); - snprintf(xpath_arg, sizeof(xpath_arg), "%s/raw-pdu", xpath); - data = yang_data_new(xpath_arg, raw_pdu); - listnode_add(arguments, data); - - nb_notification_send(xpath, arguments); -} - -/* - * XPath: - * /frr-isisd:area-mismatch - */ -void isis_notif_area_mismatch(const struct isis_circuit *circuit, - const char *raw_pdu) -{ - const char *xpath = "/frr-isisd:area-mismatch"; - struct list *arguments = yang_data_list_new(); - char xpath_arg[XPATH_MAXLEN]; - struct yang_data *data; - struct isis_area *area = circuit->area; - - notif_prep_instance_hdr(xpath, area, "default", arguments); - notif_prepr_iface_hdr(xpath, circuit, arguments); - snprintf(xpath_arg, sizeof(xpath_arg), "%s/raw-pdu", xpath); - data = yang_data_new(xpath_arg, raw_pdu); - listnode_add(arguments, data); - - nb_notification_send(xpath, arguments); -} - -/* - * XPath: - * /frr-isisd:lsp-received - */ -void isis_notif_lsp_received(const struct isis_circuit *circuit, - const char *lsp_id, uint32_t seqno, - uint32_t timestamp, const char *sys_id) -{ - const char *xpath = "/frr-isisd:lsp-received"; - struct list *arguments = yang_data_list_new(); - char xpath_arg[XPATH_MAXLEN]; - struct yang_data *data; - struct isis_area *area = circuit->area; - - notif_prep_instance_hdr(xpath, area, "default", arguments); - notif_prepr_iface_hdr(xpath, circuit, arguments); - snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath); - data = yang_data_new_string(xpath_arg, lsp_id); - listnode_add(arguments, data); - snprintf(xpath_arg, sizeof(xpath_arg), "%s/sequence", xpath); - data = yang_data_new_uint32(xpath_arg, seqno); - listnode_add(arguments, data); - snprintf(xpath_arg, sizeof(xpath_arg), "%s/received-timestamp", xpath); - data = yang_data_new_uint32(xpath_arg, timestamp); - listnode_add(arguments, data); - snprintf(xpath_arg, sizeof(xpath_arg), "%s/neighbor-system-id", xpath); - data = yang_data_new_string(xpath_arg, sys_id); - listnode_add(arguments, data); - - nb_notification_send(xpath, arguments); -} - -/* - * XPath: - * /frr-isisd:lsp-generation - */ -void isis_notif_lsp_gen(const struct isis_area *area, const char *lsp_id, - uint32_t seqno, uint32_t timestamp) -{ - const char *xpath = "/frr-isisd:lsp-generation"; - struct list *arguments = yang_data_list_new(); - char xpath_arg[XPATH_MAXLEN]; - struct yang_data *data; - - notif_prep_instance_hdr(xpath, area, "default", arguments); - snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath); - data = yang_data_new_string(xpath_arg, lsp_id); - listnode_add(arguments, data); - snprintf(xpath_arg, sizeof(xpath_arg), "%s/sequence", xpath); - data = yang_data_new_uint32(xpath_arg, seqno); - listnode_add(arguments, data); - snprintf(xpath_arg, sizeof(xpath_arg), "%s/send-timestamp", xpath); - data = yang_data_new_uint32(xpath_arg, timestamp); - listnode_add(arguments, data); - - nb_notification_send(xpath, arguments); -} - -/* - * XPath: - * /frr-isisd:id-len-mismatch - */ -void isis_notif_id_len_mismatch(const struct isis_circuit *circuit, - uint8_t rcv_id_len, const char *raw_pdu) -{ - const char *xpath = "/frr-isisd:id-len-mismatch"; - struct list *arguments = yang_data_list_new(); - char xpath_arg[XPATH_MAXLEN]; - struct yang_data *data; - struct isis_area *area = circuit->area; - - notif_prep_instance_hdr(xpath, area, "default", arguments); - notif_prepr_iface_hdr(xpath, circuit, arguments); - snprintf(xpath_arg, sizeof(xpath_arg), "%s/pdu-field-len", xpath); - data = yang_data_new_uint8(xpath_arg, rcv_id_len); - listnode_add(arguments, data); - snprintf(xpath_arg, sizeof(xpath_arg), "%s/raw-pdu", xpath); - data = yang_data_new(xpath_arg, raw_pdu); - listnode_add(arguments, data); - - nb_notification_send(xpath, arguments); -} - -/* - * XPath: - * /frr-isisd:version-skew - */ -void isis_notif_version_skew(const struct isis_circuit *circuit, - uint8_t version, const char *raw_pdu) -{ - const char *xpath = "/frr-isisd:version-skew"; - struct list *arguments = yang_data_list_new(); - char xpath_arg[XPATH_MAXLEN]; - struct yang_data *data; - struct isis_area *area = circuit->area; - - notif_prep_instance_hdr(xpath, area, "default", arguments); - notif_prepr_iface_hdr(xpath, circuit, arguments); - snprintf(xpath_arg, sizeof(xpath_arg), "%s/protocol-version", xpath); - data = yang_data_new_uint8(xpath_arg, version); - listnode_add(arguments, data); - snprintf(xpath_arg, sizeof(xpath_arg), "%s/raw-pdu", xpath); - data = yang_data_new(xpath_arg, raw_pdu); - listnode_add(arguments, data); - - nb_notification_send(xpath, arguments); -} - -/* - * XPath: - * /frr-isisd:lsp-error-detected - */ -void isis_notif_lsp_error(const struct isis_circuit *circuit, - const char *lsp_id, const char *raw_pdu, - __attribute__((unused)) uint32_t offset, - __attribute__((unused)) uint8_t tlv_type) -{ - const char *xpath = "/frr-isisd:lsp-error-detected"; - struct list *arguments = yang_data_list_new(); - char xpath_arg[XPATH_MAXLEN]; - struct yang_data *data; - struct isis_area *area = circuit->area; - - notif_prep_instance_hdr(xpath, area, "default", arguments); - notif_prepr_iface_hdr(xpath, circuit, arguments); - snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath); - data = yang_data_new_string(xpath_arg, lsp_id); - listnode_add(arguments, data); - snprintf(xpath_arg, sizeof(xpath_arg), "%s/raw-pdu", xpath); - data = yang_data_new(xpath_arg, raw_pdu); - listnode_add(arguments, data); - /* ignore offset and tlv_type which cannot be set properly */ - - nb_notification_send(xpath, arguments); -} - -/* - * XPath: - * /frr-isisd:sequence-number-skipped - */ -void isis_notif_seqno_skipped(const struct isis_circuit *circuit, - const char *lsp_id) -{ - const char *xpath = "/frr-isisd:sequence-number-skipped"; - struct list *arguments = yang_data_list_new(); - char xpath_arg[XPATH_MAXLEN]; - struct yang_data *data; - struct isis_area *area = circuit->area; - - notif_prep_instance_hdr(xpath, area, "default", arguments); - notif_prepr_iface_hdr(xpath, circuit, arguments); - snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath); - data = yang_data_new_string(xpath_arg, lsp_id); - listnode_add(arguments, data); - - nb_notification_send(xpath, arguments); -} - -/* - * XPath: - * /frr-isisd:own-lsp-purge - */ -void isis_notif_own_lsp_purge(const struct isis_circuit *circuit, - const char *lsp_id) -{ - const char *xpath = "/frr-isisd:own-lsp-purge"; - struct list *arguments = yang_data_list_new(); - char xpath_arg[XPATH_MAXLEN]; - struct yang_data *data; - struct isis_area *area = circuit->area; - - notif_prep_instance_hdr(xpath, area, "default", arguments); - notif_prepr_iface_hdr(xpath, circuit, arguments); - snprintf(xpath_arg, sizeof(xpath_arg), "%s/lsp-id", xpath); - data = yang_data_new_string(xpath_arg, lsp_id); - listnode_add(arguments, data); - - nb_notification_send(xpath, arguments); -} - -/* clang-format off */ -const struct frr_yang_module_info frr_isisd_info = { - .name = "frr-isisd", - .nodes = { - { - .xpath = "/frr-isisd:isis/instance", - .cbs.create = isis_instance_create, - .cbs.destroy = isis_instance_destroy, - .cbs.cli_show = cli_show_router_isis, - .priority = NB_DFLT_PRIORITY - 1, - }, - { - .xpath = "/frr-isisd:isis/instance/is-type", - .cbs.modify = isis_instance_is_type_modify, - .cbs.cli_show = cli_show_isis_is_type, - }, - { - .xpath = "/frr-isisd:isis/instance/area-address", - .cbs.create = isis_instance_area_address_create, - .cbs.destroy = isis_instance_area_address_destroy, - .cbs.cli_show = cli_show_isis_area_address, - }, - { - .xpath = "/frr-isisd:isis/instance/dynamic-hostname", - .cbs.modify = isis_instance_dynamic_hostname_modify, - .cbs.cli_show = cli_show_isis_dynamic_hostname, - }, - { - .xpath = "/frr-isisd:isis/instance/attached", - .cbs.modify = isis_instance_attached_modify, - .cbs.cli_show = cli_show_isis_attached, - }, - { - .xpath = "/frr-isisd:isis/instance/overload", - .cbs.modify = isis_instance_overload_modify, - .cbs.cli_show = cli_show_isis_overload, - }, - { - .xpath = "/frr-isisd:isis/instance/metric-style", - .cbs.modify = isis_instance_metric_style_modify, - .cbs.cli_show = cli_show_isis_metric_style, - }, - { - .xpath = "/frr-isisd:isis/instance/purge-originator", - .cbs.modify = isis_instance_purge_originator_modify, - .cbs.cli_show = cli_show_isis_purge_origin, - }, - { - .xpath = "/frr-isisd:isis/instance/lsp/mtu", - .cbs.modify = isis_instance_lsp_mtu_modify, - .cbs.cli_show = cli_show_isis_lsp_mtu, - }, - { - .xpath = "/frr-isisd:isis/instance/lsp/refresh-interval", - .cbs.cli_show = cli_show_isis_lsp_ref_interval, - }, - { - .xpath = "/frr-isisd:isis/instance/lsp/refresh-interval/level-1", - .cbs.modify = isis_instance_lsp_refresh_interval_level_1_modify, - }, - { - .xpath = "/frr-isisd:isis/instance/lsp/refresh-interval/level-2", - .cbs.modify = isis_instance_lsp_refresh_interval_level_2_modify, - }, - { - .xpath = "/frr-isisd:isis/instance/lsp/maximum-lifetime", - .cbs.cli_show = cli_show_isis_lsp_max_lifetime, - }, - { - .xpath = "/frr-isisd:isis/instance/lsp/maximum-lifetime/level-1", - .cbs.modify = isis_instance_lsp_maximum_lifetime_level_1_modify, - }, - { - .xpath = "/frr-isisd:isis/instance/lsp/maximum-lifetime/level-2", - .cbs.modify = isis_instance_lsp_maximum_lifetime_level_2_modify, - }, - { - .xpath = "/frr-isisd:isis/instance/lsp/generation-interval", - .cbs.cli_show = cli_show_isis_lsp_gen_interval, - }, - { - .xpath = "/frr-isisd:isis/instance/lsp/generation-interval/level-1", - .cbs.modify = isis_instance_lsp_generation_interval_level_1_modify, - }, - { - .xpath = "/frr-isisd:isis/instance/lsp/generation-interval/level-2", - .cbs.modify = isis_instance_lsp_generation_interval_level_2_modify, - }, - { - .xpath = "/frr-isisd:isis/instance/spf/ietf-backoff-delay", - .cbs.create = isis_instance_spf_ietf_backoff_delay_create, - .cbs.destroy = isis_instance_spf_ietf_backoff_delay_destroy, - .cbs.apply_finish = ietf_backoff_delay_apply_finish, - .cbs.cli_show = cli_show_isis_spf_ietf_backoff, - }, - { - .xpath = "/frr-isisd:isis/instance/spf/ietf-backoff-delay/init-delay", - .cbs.modify = isis_instance_spf_ietf_backoff_delay_init_delay_modify, - }, - { - .xpath = "/frr-isisd:isis/instance/spf/ietf-backoff-delay/short-delay", - .cbs.modify = isis_instance_spf_ietf_backoff_delay_short_delay_modify, - }, - { - .xpath = "/frr-isisd:isis/instance/spf/ietf-backoff-delay/long-delay", - .cbs.modify = isis_instance_spf_ietf_backoff_delay_long_delay_modify, - }, - { - .xpath = "/frr-isisd:isis/instance/spf/ietf-backoff-delay/hold-down", - .cbs.modify = isis_instance_spf_ietf_backoff_delay_hold_down_modify, - }, - { - .xpath = "/frr-isisd:isis/instance/spf/ietf-backoff-delay/time-to-learn", - .cbs.modify = isis_instance_spf_ietf_backoff_delay_time_to_learn_modify, - }, - { - .xpath = "/frr-isisd:isis/instance/spf/minimum-interval", - .cbs.cli_show = cli_show_isis_spf_min_interval, - }, - { - .xpath = "/frr-isisd:isis/instance/spf/minimum-interval/level-1", - .cbs.modify = isis_instance_spf_minimum_interval_level_1_modify, - }, - { - .xpath = "/frr-isisd:isis/instance/spf/minimum-interval/level-2", - .cbs.modify = isis_instance_spf_minimum_interval_level_2_modify, - }, - { - .xpath = "/frr-isisd:isis/instance/area-password", - .cbs.create = isis_instance_area_password_create, - .cbs.destroy = isis_instance_area_password_destroy, - .cbs.apply_finish = area_password_apply_finish, - .cbs.cli_show = cli_show_isis_area_pwd, - }, - { - .xpath = "/frr-isisd:isis/instance/area-password/password", - .cbs.modify = isis_instance_area_password_password_modify, - }, - { - .xpath = "/frr-isisd:isis/instance/area-password/password-type", - .cbs.modify = isis_instance_area_password_password_type_modify, - }, - { - .xpath = "/frr-isisd:isis/instance/area-password/authenticate-snp", - .cbs.modify = isis_instance_area_password_authenticate_snp_modify, - }, - { - .xpath = "/frr-isisd:isis/instance/domain-password", - .cbs.create = isis_instance_domain_password_create, - .cbs.destroy = isis_instance_domain_password_destroy, - .cbs.apply_finish = domain_password_apply_finish, - .cbs.cli_show = cli_show_isis_domain_pwd, - }, - { - .xpath = "/frr-isisd:isis/instance/domain-password/password", - .cbs.modify = isis_instance_domain_password_password_modify, - }, - { - .xpath = "/frr-isisd:isis/instance/domain-password/password-type", - .cbs.modify = isis_instance_domain_password_password_type_modify, - }, - { - .xpath = "/frr-isisd:isis/instance/domain-password/authenticate-snp", - .cbs.modify = isis_instance_domain_password_authenticate_snp_modify, - }, - { - .xpath = "/frr-isisd:isis/instance/default-information-originate/ipv4", - .cbs.create = isis_instance_default_information_originate_ipv4_create, - .cbs.destroy = isis_instance_default_information_originate_ipv4_destroy, - .cbs.apply_finish = default_info_origin_ipv4_apply_finish, - .cbs.cli_show = cli_show_isis_def_origin_ipv4, - }, - { - .xpath = "/frr-isisd:isis/instance/default-information-originate/ipv4/always", - .cbs.modify = isis_instance_default_information_originate_ipv4_always_modify, - }, - { - .xpath = "/frr-isisd:isis/instance/default-information-originate/ipv4/route-map", - .cbs.modify = isis_instance_default_information_originate_ipv4_route_map_modify, - .cbs.destroy = isis_instance_default_information_originate_ipv4_route_map_destroy, - }, - { - .xpath = "/frr-isisd:isis/instance/default-information-originate/ipv4/metric", - .cbs.modify = isis_instance_default_information_originate_ipv4_metric_modify, - }, - { - .xpath = "/frr-isisd:isis/instance/default-information-originate/ipv6", - .cbs.create = isis_instance_default_information_originate_ipv6_create, - .cbs.destroy = isis_instance_default_information_originate_ipv6_destroy, - .cbs.apply_finish = default_info_origin_ipv6_apply_finish, - .cbs.cli_show = cli_show_isis_def_origin_ipv6, - }, - { - .xpath = "/frr-isisd:isis/instance/default-information-originate/ipv6/always", - .cbs.modify = isis_instance_default_information_originate_ipv6_always_modify, - }, - { - .xpath = "/frr-isisd:isis/instance/default-information-originate/ipv6/route-map", - .cbs.modify = isis_instance_default_information_originate_ipv6_route_map_modify, - .cbs.destroy = isis_instance_default_information_originate_ipv6_route_map_destroy, - }, - { - .xpath = "/frr-isisd:isis/instance/default-information-originate/ipv6/metric", - .cbs.modify = isis_instance_default_information_originate_ipv6_metric_modify, - }, - { - .xpath = "/frr-isisd:isis/instance/redistribute/ipv4", - .cbs.create = isis_instance_redistribute_ipv4_create, - .cbs.destroy = isis_instance_redistribute_ipv4_destroy, - .cbs.apply_finish = redistribute_ipv4_apply_finish, - .cbs.cli_show = cli_show_isis_redistribute_ipv4, - }, - { - .xpath = "/frr-isisd:isis/instance/redistribute/ipv4/route-map", - .cbs.modify = isis_instance_redistribute_ipv4_route_map_modify, - .cbs.destroy = isis_instance_redistribute_ipv4_route_map_destroy, - }, - { - .xpath = "/frr-isisd:isis/instance/redistribute/ipv4/metric", - .cbs.modify = isis_instance_redistribute_ipv4_metric_modify, - }, - { - .xpath = "/frr-isisd:isis/instance/redistribute/ipv6", - .cbs.create = isis_instance_redistribute_ipv6_create, - .cbs.destroy = isis_instance_redistribute_ipv6_destroy, - .cbs.apply_finish = redistribute_ipv6_apply_finish, - .cbs.cli_show = cli_show_isis_redistribute_ipv6, - }, - { - .xpath = "/frr-isisd:isis/instance/redistribute/ipv6/route-map", - .cbs.modify = isis_instance_redistribute_ipv6_route_map_modify, - .cbs.destroy = isis_instance_redistribute_ipv6_route_map_destroy, - }, - { - .xpath = "/frr-isisd:isis/instance/redistribute/ipv6/metric", - .cbs.modify = isis_instance_redistribute_ipv6_metric_modify, - }, - { - .xpath = "/frr-isisd:isis/instance/multi-topology/ipv4-multicast", - .cbs.create = isis_instance_multi_topology_ipv4_multicast_create, - .cbs.destroy = isis_instance_multi_topology_ipv4_multicast_destroy, - .cbs.cli_show = cli_show_isis_mt_ipv4_multicast, - }, - { - .xpath = "/frr-isisd:isis/instance/multi-topology/ipv4-multicast/overload", - .cbs.modify = isis_instance_multi_topology_ipv4_multicast_overload_modify, - }, - { - .xpath = "/frr-isisd:isis/instance/multi-topology/ipv4-management", - .cbs.create = isis_instance_multi_topology_ipv4_management_create, - .cbs.destroy = isis_instance_multi_topology_ipv4_management_destroy, - .cbs.cli_show = cli_show_isis_mt_ipv4_mgmt, - }, - { - .xpath = "/frr-isisd:isis/instance/multi-topology/ipv4-management/overload", - .cbs.modify = isis_instance_multi_topology_ipv4_management_overload_modify, - }, - { - .xpath = "/frr-isisd:isis/instance/multi-topology/ipv6-unicast", - .cbs.create = isis_instance_multi_topology_ipv6_unicast_create, - .cbs.destroy = isis_instance_multi_topology_ipv6_unicast_destroy, - .cbs.cli_show = cli_show_isis_mt_ipv6_unicast, - }, - { - .xpath = "/frr-isisd:isis/instance/multi-topology/ipv6-unicast/overload", - .cbs.modify = isis_instance_multi_topology_ipv6_unicast_overload_modify, - }, - { - .xpath = "/frr-isisd:isis/instance/multi-topology/ipv6-multicast", - .cbs.create = isis_instance_multi_topology_ipv6_multicast_create, - .cbs.destroy = isis_instance_multi_topology_ipv6_multicast_destroy, - .cbs.cli_show = cli_show_isis_mt_ipv6_multicast, - }, - { - .xpath = "/frr-isisd:isis/instance/multi-topology/ipv6-multicast/overload", - .cbs.modify = isis_instance_multi_topology_ipv6_multicast_overload_modify, - }, - { - .xpath = "/frr-isisd:isis/instance/multi-topology/ipv6-management", - .cbs.create = isis_instance_multi_topology_ipv6_management_create, - .cbs.destroy = isis_instance_multi_topology_ipv6_management_destroy, - .cbs.cli_show = cli_show_isis_mt_ipv6_mgmt, - }, - { - .xpath = "/frr-isisd:isis/instance/multi-topology/ipv6-management/overload", - .cbs.modify = isis_instance_multi_topology_ipv6_management_overload_modify, - }, - { - .xpath = "/frr-isisd:isis/instance/multi-topology/ipv6-dstsrc", - .cbs.create = isis_instance_multi_topology_ipv6_dstsrc_create, - .cbs.destroy = isis_instance_multi_topology_ipv6_dstsrc_destroy, - .cbs.cli_show = cli_show_isis_mt_ipv6_dstsrc, - }, - { - .xpath = "/frr-isisd:isis/instance/multi-topology/ipv6-dstsrc/overload", - .cbs.modify = isis_instance_multi_topology_ipv6_dstsrc_overload_modify, - }, - { - .xpath = "/frr-isisd:isis/instance/log-adjacency-changes", - .cbs.modify = isis_instance_log_adjacency_changes_modify, - .cbs.cli_show = cli_show_isis_log_adjacency, - }, - { - .xpath = "/frr-isisd:isis/instance/mpls-te", - .cbs.create = isis_instance_mpls_te_create, - .cbs.destroy = isis_instance_mpls_te_destroy, - .cbs.cli_show = cli_show_isis_mpls_te, - }, - { - .xpath = "/frr-isisd:isis/instance/mpls-te/router-address", - .cbs.modify = isis_instance_mpls_te_router_address_modify, - .cbs.destroy = isis_instance_mpls_te_router_address_destroy, - .cbs.cli_show = cli_show_isis_mpls_te_router_addr, - }, - { - .xpath = "/frr-interface:lib/interface/frr-isisd:isis", - .cbs.create = lib_interface_isis_create, - .cbs.destroy = lib_interface_isis_destroy, - }, - { - .xpath = "/frr-interface:lib/interface/frr-isisd:isis/area-tag", - .cbs.modify = lib_interface_isis_area_tag_modify, - }, - { - .xpath = "/frr-interface:lib/interface/frr-isisd:isis/circuit-type", - .cbs.modify = lib_interface_isis_circuit_type_modify, - .cbs.cli_show = cli_show_ip_isis_circ_type, - }, - { - .xpath = "/frr-interface:lib/interface/frr-isisd:isis/ipv4-routing", - .cbs.modify = lib_interface_isis_ipv4_routing_modify, - .cbs.cli_show = cli_show_ip_isis_ipv4, - }, - { - .xpath = "/frr-interface:lib/interface/frr-isisd:isis/ipv6-routing", - .cbs.modify = lib_interface_isis_ipv6_routing_modify, - .cbs.cli_show = cli_show_ip_isis_ipv6, - }, - { - .xpath = "/frr-interface:lib/interface/frr-isisd:isis/csnp-interval", - .cbs.cli_show = cli_show_ip_isis_csnp_interval, - }, - { - .xpath = "/frr-interface:lib/interface/frr-isisd:isis/csnp-interval/level-1", - .cbs.modify = lib_interface_isis_csnp_interval_level_1_modify, - }, - { - .xpath = "/frr-interface:lib/interface/frr-isisd:isis/csnp-interval/level-2", - .cbs.modify = lib_interface_isis_csnp_interval_level_2_modify, - }, - { - .xpath = "/frr-interface:lib/interface/frr-isisd:isis/psnp-interval", - .cbs.cli_show = cli_show_ip_isis_psnp_interval, - }, - { - .xpath = "/frr-interface:lib/interface/frr-isisd:isis/psnp-interval/level-1", - .cbs.modify = lib_interface_isis_psnp_interval_level_1_modify, - }, - { - .xpath = "/frr-interface:lib/interface/frr-isisd:isis/psnp-interval/level-2", - .cbs.modify = lib_interface_isis_psnp_interval_level_2_modify, - }, - { - .xpath = "/frr-interface:lib/interface/frr-isisd:isis/hello/padding", - .cbs.modify = lib_interface_isis_hello_padding_modify, - .cbs.cli_show = cli_show_ip_isis_hello_padding, - }, - { - .xpath = "/frr-interface:lib/interface/frr-isisd:isis/hello/interval", - .cbs.cli_show = cli_show_ip_isis_hello_interval, - }, - { - .xpath = "/frr-interface:lib/interface/frr-isisd:isis/hello/interval/level-1", - .cbs.modify = lib_interface_isis_hello_interval_level_1_modify, - }, - { - .xpath = "/frr-interface:lib/interface/frr-isisd:isis/hello/interval/level-2", - .cbs.modify = lib_interface_isis_hello_interval_level_2_modify, - }, - { - .xpath = "/frr-interface:lib/interface/frr-isisd:isis/hello/multiplier", - .cbs.cli_show = cli_show_ip_isis_hello_multi, - }, - { - .xpath = "/frr-interface:lib/interface/frr-isisd:isis/hello/multiplier/level-1", - .cbs.modify = lib_interface_isis_hello_multiplier_level_1_modify, - }, - { - .xpath = "/frr-interface:lib/interface/frr-isisd:isis/hello/multiplier/level-2", - .cbs.modify = lib_interface_isis_hello_multiplier_level_2_modify, - }, - { - .xpath = "/frr-interface:lib/interface/frr-isisd:isis/metric", - .cbs.cli_show = cli_show_ip_isis_metric, - }, - { - .xpath = "/frr-interface:lib/interface/frr-isisd:isis/metric/level-1", - .cbs.modify = lib_interface_isis_metric_level_1_modify, - }, - { - .xpath = "/frr-interface:lib/interface/frr-isisd:isis/metric/level-2", - .cbs.modify = lib_interface_isis_metric_level_2_modify, - }, - { - .xpath = "/frr-interface:lib/interface/frr-isisd:isis/priority", - .cbs.cli_show = cli_show_ip_isis_priority, - }, - { - .xpath = "/frr-interface:lib/interface/frr-isisd:isis/priority/level-1", - .cbs.modify = lib_interface_isis_priority_level_1_modify, - }, - { - .xpath = "/frr-interface:lib/interface/frr-isisd:isis/priority/level-2", - .cbs.modify = lib_interface_isis_priority_level_2_modify, - }, - { - .xpath = "/frr-interface:lib/interface/frr-isisd:isis/network-type", - .cbs.modify = lib_interface_isis_network_type_modify, - .cbs.cli_show = cli_show_ip_isis_network_type, - }, - { - .xpath = "/frr-interface:lib/interface/frr-isisd:isis/passive", - .cbs.modify = lib_interface_isis_passive_modify, - .cbs.cli_show = cli_show_ip_isis_passive, - }, - { - .xpath = "/frr-interface:lib/interface/frr-isisd:isis/password", - .cbs.create = lib_interface_isis_password_create, - .cbs.destroy = lib_interface_isis_password_destroy, - .cbs.cli_show = cli_show_ip_isis_password, - }, - { - .xpath = "/frr-interface:lib/interface/frr-isisd:isis/password/password", - .cbs.modify = lib_interface_isis_password_password_modify, - }, - { - .xpath = "/frr-interface:lib/interface/frr-isisd:isis/password/password-type", - .cbs.modify = lib_interface_isis_password_password_type_modify, - }, - { - .xpath = "/frr-interface:lib/interface/frr-isisd:isis/disable-three-way-handshake", - .cbs.modify = lib_interface_isis_disable_three_way_handshake_modify, - .cbs.cli_show = cli_show_ip_isis_threeway_shake, - }, - { - .xpath = "/frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv4-unicast", - .cbs.modify = lib_interface_isis_multi_topology_ipv4_unicast_modify, - .cbs.cli_show = cli_show_ip_isis_mt_ipv4_unicast, - }, - { - .xpath = "/frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv4-multicast", - .cbs.modify = lib_interface_isis_multi_topology_ipv4_multicast_modify, - .cbs.cli_show = cli_show_ip_isis_mt_ipv4_multicast, - }, - { - .xpath = "/frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv4-management", - .cbs.modify = lib_interface_isis_multi_topology_ipv4_management_modify, - .cbs.cli_show = cli_show_ip_isis_mt_ipv4_mgmt, - }, - { - .xpath = "/frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv6-unicast", - .cbs.modify = lib_interface_isis_multi_topology_ipv6_unicast_modify, - .cbs.cli_show = cli_show_ip_isis_mt_ipv6_unicast, - }, - { - .xpath = "/frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv6-multicast", - .cbs.modify = lib_interface_isis_multi_topology_ipv6_multicast_modify, - .cbs.cli_show = cli_show_ip_isis_mt_ipv6_multicast, - }, - { - .xpath = "/frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv6-management", - .cbs.modify = lib_interface_isis_multi_topology_ipv6_management_modify, - .cbs.cli_show = cli_show_ip_isis_mt_ipv6_mgmt, - }, - { - .xpath = "/frr-interface:lib/interface/frr-isisd:isis/multi-topology/ipv6-dstsrc", - .cbs.modify = lib_interface_isis_multi_topology_ipv6_dstsrc_modify, - .cbs.cli_show = cli_show_ip_isis_mt_ipv6_dstsrc, - }, - { - .xpath = NULL, - }, - } -}; diff --git a/isisd/isis_pdu.c b/isisd/isis_pdu.c index 9c633117b0..43b9f6685e 100644 --- a/isisd/isis_pdu.c +++ b/isisd/isis_pdu.c @@ -58,6 +58,7 @@ #include "isisd/fabricd.h" #include "isisd/isis_tx_queue.h" #include "isisd/isis_pdu_counter.h" +#include "isisd/isis_nb.h" static int ack_lsp(struct isis_lsp_hdr *hdr, struct isis_circuit *circuit, int level) @@ -73,8 +74,10 @@ static int ack_lsp(struct isis_lsp_hdr *hdr, struct isis_circuit *circuit, fill_fixed_hdr(pdu_type, circuit->snd_stream); lenp = stream_get_endp(circuit->snd_stream); + stream_putw(circuit->snd_stream, 0); /* PDU length */ - stream_put(circuit->snd_stream, isis->sysid, ISIS_SYS_ID_LEN); + stream_put(circuit->snd_stream, circuit->area->isis->sysid, + ISIS_SYS_ID_LEN); stream_putc(circuit->snd_stream, circuit->idx); stream_putc(circuit->snd_stream, 9); /* code */ stream_putc(circuit->snd_stream, 16); /* len */ @@ -127,9 +130,10 @@ struct iih_info { static int process_p2p_hello(struct iih_info *iih) { struct isis_threeway_adj *tw_adj = iih->tlvs->threeway_adj; + if (tw_adj) { if (tw_adj->state > ISIS_THREEWAY_DOWN) { - if (isis->debugs & DEBUG_ADJ_PACKETS) { + if (IS_DEBUG_ADJ_PACKETS) { zlog_debug("ISIS-Adj (%s): Rcvd P2P IIH from (%s) with invalid three-way state: %d", iih->circuit->area->area_tag, iih->circuit->interface->name, @@ -139,10 +143,12 @@ static int process_p2p_hello(struct iih_info *iih) } if (tw_adj->neighbor_set - && (memcmp(tw_adj->neighbor_id, isis->sysid, ISIS_SYS_ID_LEN) - || tw_adj->neighbor_circuit_id != (uint32_t) iih->circuit->idx)) { + && (memcmp(tw_adj->neighbor_id, + iih->circuit->area->isis->sysid, ISIS_SYS_ID_LEN) + || tw_adj->neighbor_circuit_id + != (uint32_t)iih->circuit->idx)) { - if (isis->debugs & DEBUG_ADJ_PACKETS) { + if (IS_DEBUG_ADJ_PACKETS) { zlog_debug("ISIS-Adj (%s): Rcvd P2P IIH from (%s) which lists IS/Circuit different from us as neighbor.", iih->circuit->area->area_tag, iih->circuit->interface->name); @@ -163,7 +169,7 @@ static int process_p2p_hello(struct iih_info *iih) if (memcmp(iih->sys_id, adj->sysid, ISIS_SYS_ID_LEN)) { zlog_debug( "hello source and adjacency do not match, set adj down\n"); - isis_adj_state_change(adj, ISIS_ADJ_DOWN, + isis_adj_state_change(&adj, ISIS_ADJ_DOWN, "adj do not exist"); return ISIS_OK; } @@ -183,11 +189,11 @@ static int process_p2p_hello(struct iih_info *iih) * adjacency entry getting added to the lsp tlv neighbor list. */ adj->circuit_t = iih->circ_type; - isis_adj_state_change(adj, ISIS_ADJ_INITIALIZING, NULL); + isis_adj_state_change(&adj, ISIS_ADJ_INITIALIZING, NULL); adj->sys_type = ISIS_SYSTYPE_UNKNOWN; } - if (tw_adj && adj->threeway_state == ISIS_THREEWAY_DOWN) + if (tw_adj) adj->ext_circuit_id = tw_adj->local_circuit_id; /* 8.2.6 Monitoring point-to-point adjacencies */ @@ -199,13 +205,6 @@ static int process_p2p_hello(struct iih_info *iih) changed |= tlvs_to_adj_mt_set(iih->tlvs, iih->v4_usable, iih->v6_usable, adj); - /* Update MPLS TE Remote IP address parameter if possible */ - if (IS_MPLS_TE(iih->circuit->area->mta) - && IS_MPLS_TE(iih->circuit->mtc) - && adj->ipv4_address_count) - set_circuitparams_rmt_ipaddr(iih->circuit->mtc, - adj->ipv4_addresses[0]); - /* lets take care of the expiry */ THREAD_TIMER_OFF(adj->t_expire); thread_add_timer(master, isis_adj_expire, adj, (long)adj->hold_time, @@ -239,7 +238,7 @@ static int process_p2p_hello(struct iih_info *iih) return ISIS_WARNING; } else if (adj->adj_usage == ISIS_ADJ_LEVEL1) { /* (6) down - wrong system */ - isis_adj_state_change(adj, + isis_adj_state_change(&adj, ISIS_ADJ_DOWN, "Wrong System"); } @@ -260,7 +259,7 @@ static int process_p2p_hello(struct iih_info *iih) || (adj->adj_usage == ISIS_ADJ_LEVEL2)) { /* (8) down - wrong system */ - isis_adj_state_change(adj, + isis_adj_state_change(&adj, ISIS_ADJ_DOWN, "Wrong System"); } @@ -274,7 +273,7 @@ static int process_p2p_hello(struct iih_info *iih) || (adj->adj_usage == ISIS_ADJ_LEVEL1AND2)) { /* (8) down - wrong system */ - isis_adj_state_change(adj, + isis_adj_state_change(&adj, ISIS_ADJ_DOWN, "Wrong System"); } @@ -288,7 +287,7 @@ static int process_p2p_hello(struct iih_info *iih) || (adj->adj_usage == ISIS_ADJ_LEVEL2)) { /* (8) down - wrong system */ - isis_adj_state_change(adj, + isis_adj_state_change(&adj, ISIS_ADJ_DOWN, "Wrong System"); } @@ -310,7 +309,7 @@ static int process_p2p_hello(struct iih_info *iih) || (adj->adj_usage == ISIS_ADJ_LEVEL2)) { /* (6) down - wrong system */ - isis_adj_state_change(adj, + isis_adj_state_change(&adj, ISIS_ADJ_DOWN, "Wrong System"); } @@ -324,7 +323,7 @@ static int process_p2p_hello(struct iih_info *iih) } else if (adj->adj_usage == ISIS_ADJ_LEVEL1AND2) { /* (6) down - wrong system */ - isis_adj_state_change(adj, + isis_adj_state_change(&adj, ISIS_ADJ_DOWN, "Wrong System"); } @@ -337,11 +336,11 @@ static int process_p2p_hello(struct iih_info *iih) if (iih->circuit->area->is_type == IS_LEVEL_1) { /* 8.2.5.2 b) 1) is_type L1 and adj is not up */ if (adj->adj_state != ISIS_ADJ_UP) { - isis_adj_state_change(adj, ISIS_ADJ_DOWN, + isis_adj_state_change(&adj, ISIS_ADJ_DOWN, "Area Mismatch"); /* 8.2.5.2 b) 2)is_type L1 and adj is up */ } else { - isis_adj_state_change(adj, ISIS_ADJ_DOWN, + isis_adj_state_change(&adj, ISIS_ADJ_DOWN, "Down - Area Mismatch"); } } @@ -355,7 +354,7 @@ static int process_p2p_hello(struct iih_info *iih) return ISIS_WARNING; } else if (adj->adj_usage == ISIS_ADJ_LEVEL1) { /* (7) down - area mismatch */ - isis_adj_state_change(adj, + isis_adj_state_change(&adj, ISIS_ADJ_DOWN, "Area Mismatch"); @@ -364,7 +363,7 @@ static int process_p2p_hello(struct iih_info *iih) || (adj->adj_usage == ISIS_ADJ_LEVEL2)) { /* (7) down - wrong system */ - isis_adj_state_change(adj, + isis_adj_state_change(&adj, ISIS_ADJ_DOWN, "Wrong System"); } @@ -377,7 +376,7 @@ static int process_p2p_hello(struct iih_info *iih) ISIS_ADJ_LEVEL2); } else if (adj->adj_usage == ISIS_ADJ_LEVEL1) { /* (7) down - wrong system */ - isis_adj_state_change(adj, + isis_adj_state_change(&adj, ISIS_ADJ_DOWN, "Wrong System"); } else if (adj->adj_usage @@ -385,12 +384,12 @@ static int process_p2p_hello(struct iih_info *iih) if (iih->circ_type == IS_LEVEL_2) { /* (7) down - wrong system */ isis_adj_state_change( - adj, ISIS_ADJ_DOWN, + &adj, ISIS_ADJ_DOWN, "Wrong System"); } else { /* (7) down - area mismatch */ isis_adj_state_change( - adj, ISIS_ADJ_DOWN, + &adj, ISIS_ADJ_DOWN, "Area Mismatch"); } } @@ -399,40 +398,41 @@ static int process_p2p_hello(struct iih_info *iih) } } else { /* down - area mismatch */ - isis_adj_state_change(adj, ISIS_ADJ_DOWN, "Area Mismatch"); + isis_adj_state_change(&adj, ISIS_ADJ_DOWN, "Area Mismatch"); } - if (adj->adj_state == ISIS_ADJ_UP && changed) { - lsp_regenerate_schedule(adj->circuit->area, - isis_adj_usage2levels(adj->adj_usage), - 0); - } + if (adj) { + if (adj->adj_state == ISIS_ADJ_UP && changed) { + lsp_regenerate_schedule( + adj->circuit->area, + isis_adj_usage2levels(adj->adj_usage), 0); + } - /* 8.2.5.2 c) if the action was up - comparing circuit IDs */ - /* FIXME - Missing parts */ + /* 8.2.5.2 c) if the action was up - comparing circuit IDs */ + /* FIXME - Missing parts */ - /* some of my own understanding of the ISO, why the heck does - * it not say what should I change the system_type to... - */ - switch (adj->adj_usage) { - case ISIS_ADJ_LEVEL1: - adj->sys_type = ISIS_SYSTYPE_L1_IS; - break; - case ISIS_ADJ_LEVEL2: - adj->sys_type = ISIS_SYSTYPE_L2_IS; - break; - case ISIS_ADJ_LEVEL1AND2: - adj->sys_type = ISIS_SYSTYPE_L2_IS; - break; - case ISIS_ADJ_NONE: - adj->sys_type = ISIS_SYSTYPE_UNKNOWN; - break; + /* some of my own understanding of the ISO, why the heck does + * it not say what should I change the system_type to... + */ + switch (adj->adj_usage) { + case ISIS_ADJ_LEVEL1: + adj->sys_type = ISIS_SYSTYPE_L1_IS; + break; + case ISIS_ADJ_LEVEL2: + adj->sys_type = ISIS_SYSTYPE_L2_IS; + break; + case ISIS_ADJ_LEVEL1AND2: + adj->sys_type = ISIS_SYSTYPE_L2_IS; + break; + case ISIS_ADJ_NONE: + adj->sys_type = ISIS_SYSTYPE_UNKNOWN; + break; + } } - if (isis->debugs & DEBUG_ADJ_PACKETS) { + if (IS_DEBUG_ADJ_PACKETS) { zlog_debug( - "ISIS-Adj (%s): Rcvd P2P IIH from (%s), cir type %s," - " cir id %hhu, length %" PRIu16, + "ISIS-Adj (%s): Rcvd P2P IIH from (%s), cir type %s, cir id %hhu, length %hu", iih->circuit->area->area_tag, iih->circuit->interface->name, circuit_t2string(iih->circuit->is_type), @@ -461,7 +461,7 @@ static int process_lan_hello(struct iih_info *iih) } adj->level = iih->level; } - isis_adj_state_change(adj, ISIS_ADJ_INITIALIZING, NULL); + isis_adj_state_change(&adj, ISIS_ADJ_INITIALIZING, NULL); if (iih->level == IS_LEVEL_1) adj->sys_type = ISIS_SYSTYPE_L1_IS; @@ -512,13 +512,13 @@ static int process_lan_hello(struct iih_info *iih) if (adj->adj_state != ISIS_ADJ_UP) { if (own_snpa_found) { isis_adj_state_change( - adj, ISIS_ADJ_UP, + &adj, ISIS_ADJ_UP, "own SNPA found in LAN Neighbours TLV"); } } else { if (!own_snpa_found) { isis_adj_state_change( - adj, ISIS_ADJ_INITIALIZING, + &adj, ISIS_ADJ_INITIALIZING, "own SNPA not found in LAN Neighbours TLV"); } } @@ -526,7 +526,7 @@ static int process_lan_hello(struct iih_info *iih) if (adj->adj_state == ISIS_ADJ_UP && changed) lsp_regenerate_schedule(adj->circuit->area, iih->level, 0); - if (isis->debugs & DEBUG_ADJ_PACKETS) { + if (IS_DEBUG_ADJ_PACKETS) { zlog_debug( "ISIS-Adj (%s): Rcvd L%d LAN IIH from %s on %s, cirType %s, cirID %u, length %zd", iih->circuit->area->area_tag, iih->level, @@ -566,16 +566,15 @@ static int process_hello(uint8_t pdu_type, struct isis_circuit *circuit, ? "P2P IIH" : (level == ISIS_LEVEL1) ? "L1 LAN IIH" : "L2 LAN IIH"; - stream_get_from(raw_pdu, circuit->rcv_stream, pdu_start, pdu_end - pdu_start); - if (isis->debugs & DEBUG_ADJ_PACKETS) { + if (IS_DEBUG_ADJ_PACKETS) { zlog_debug("ISIS-Adj (%s): Rcvd %s on %s, cirType %s, cirID %u", circuit->area->area_tag, pdu_name, circuit->interface->name, circuit_t2string(circuit->is_type), circuit->circuit_id); - if (isis->debugs & DEBUG_PACKET_DUMP) + if (IS_DEBUG_PACKET_DUMP) zlog_dump_data(STREAM_DATA(circuit->rcv_stream), stream_get_endp(circuit->rcv_stream)); } @@ -583,6 +582,7 @@ static int process_hello(uint8_t pdu_type, struct isis_circuit *circuit, if (p2p_hello) { if (circuit->circ_type != CIRCUIT_T_P2P) { zlog_warn("p2p hello on non p2p circuit"); + circuit->rej_adjacencies++; #ifndef FABRICD isis_notif_reject_adjacency( circuit, "p2p hello on non p2p circuit", @@ -593,6 +593,7 @@ static int process_hello(uint8_t pdu_type, struct isis_circuit *circuit, } else { if (circuit->circ_type != CIRCUIT_T_BROADCAST) { zlog_warn("lan hello on non broadcast circuit"); + circuit->rej_adjacencies++; #ifndef FABRICD isis_notif_reject_adjacency( circuit, "lan hello on non broadcast circuit", @@ -605,6 +606,7 @@ static int process_hello(uint8_t pdu_type, struct isis_circuit *circuit, zlog_debug( "level %d LAN Hello received over circuit with externalDomain = true", level); + circuit->rej_adjacencies++; #ifndef FABRICD isis_notif_reject_adjacency( circuit, @@ -615,12 +617,13 @@ static int process_hello(uint8_t pdu_type, struct isis_circuit *circuit, } if (!(circuit->is_type & level)) { - if (isis->debugs & DEBUG_ADJ_PACKETS) { + if (IS_DEBUG_ADJ_PACKETS) { zlog_debug( "ISIS-Adj (%s): Interface level mismatch, %s", circuit->area->area_tag, circuit->interface->name); } + circuit->rej_adjacencies++; #ifndef FABRICD isis_notif_reject_adjacency( circuit, "Interface level mismatch", raw_pdu); @@ -647,9 +650,10 @@ static int process_hello(uint8_t pdu_type, struct isis_circuit *circuit, if (pdu_len_validate(iih.pdu_len, circuit)) { zlog_warn( - "ISIS-Adj (%s): Rcvd %s from (%s) with invalid pdu length %" PRIu16, + "ISIS-Adj (%s): Rcvd %s from (%s) with invalid pdu length %hu", circuit->area->area_tag, pdu_name, circuit->interface->name, iih.pdu_len); + circuit->rej_adjacencies++; #ifndef FABRICD isis_notif_reject_adjacency(circuit, "Invalid PDU length", raw_pdu); @@ -661,6 +665,7 @@ static int process_hello(uint8_t pdu_type, struct isis_circuit *circuit, flog_err(EC_ISIS_PACKET, "Level %d LAN Hello with Circuit Type %d", level, iih.circ_type); + circuit->rej_adjacencies++; #ifndef FABRICD isis_notif_reject_adjacency( circuit, "LAN Hello with wrong IS-level", raw_pdu); @@ -674,6 +679,7 @@ static int process_hello(uint8_t pdu_type, struct isis_circuit *circuit, if (isis_unpack_tlvs(STREAM_READABLE(circuit->rcv_stream), circuit->rcv_stream, &iih.tlvs, &error_log)) { zlog_warn("isis_unpack_tlvs() failed: %s", error_log); + circuit->rej_adjacencies++; #ifndef FABRICD isis_notif_reject_adjacency(circuit, "Failed to unpack TLVs", raw_pdu); @@ -692,6 +698,7 @@ static int process_hello(uint8_t pdu_type, struct isis_circuit *circuit, if (!iih.tlvs->protocols_supported.count) { zlog_warn("No supported protocols TLV in %s", pdu_name); + circuit->rej_adjacencies++; #ifndef FABRICD isis_notif_reject_adjacency( circuit, "No supported protocols TLV", raw_pdu); @@ -709,19 +716,23 @@ static int process_hello(uint8_t pdu_type, struct isis_circuit *circuit, /* send northbound notification */ stream_get_from(raw_pdu, circuit->rcv_stream, pdu_start, pdu_end - pdu_start); - if (auth_code == ISIS_AUTH_FAILURE) + if (auth_code == ISIS_AUTH_FAILURE) { + circuit->auth_failures++; isis_notif_authentication_failure(circuit, raw_pdu); - else /* AUTH_TYPE_FAILURE or NO_VALIDATOR */ + } else { /* AUTH_TYPE_FAILURE or NO_VALIDATOR */ + circuit->auth_type_failures++; isis_notif_authentication_type_failure(circuit, raw_pdu); + } #endif /* ifndef FABRICD */ goto out; } - if (!memcmp(iih.sys_id, isis->sysid, ISIS_SYS_ID_LEN)) { + if (!memcmp(iih.sys_id, circuit->area->isis->sysid, ISIS_SYS_ID_LEN)) { zlog_warn( "ISIS-Adj (%s): Received IIH with own sysid - discard", circuit->area->area_tag); + circuit->rej_adjacencies++; #ifndef FABRICD isis_notif_reject_adjacency( circuit, "Received IIH with our own sysid", raw_pdu); @@ -734,7 +745,7 @@ static int process_hello(uint8_t pdu_type, struct isis_circuit *circuit, || (level == ISIS_LEVEL1 && !isis_tlvs_area_addresses_match( iih.tlvs, circuit->area->area_addrs)))) { - if (isis->debugs & DEBUG_ADJ_PACKETS) { + if (IS_DEBUG_ADJ_PACKETS) { zlog_debug( "ISIS-Adj (%s): Area mismatch, level %d IIH on %s", circuit->area->area_tag, level, @@ -754,11 +765,12 @@ static int process_hello(uint8_t pdu_type, struct isis_circuit *circuit, && iih.tlvs->ipv6_address.count); if (!iih.v4_usable && !iih.v6_usable) { - if (isis->debugs & DEBUG_ADJ_PACKETS) { + if (IS_DEBUG_ADJ_PACKETS) { zlog_warn( "ISIS-Adj (%s): Neither IPv4 nor IPv6 considered usable. Ignoring IIH", circuit->area->area_tag); } + circuit->rej_adjacencies++; #ifndef FABRICD isis_notif_reject_adjacency( circuit, "Neither IPv4 not IPv6 considered usable", @@ -822,7 +834,7 @@ static int process_lsp(uint8_t pdu_type, struct isis_circuit *circuit, circuit_scoped = false; } - if (isis->debugs & DEBUG_UPDATE_PACKETS) { + if (IS_DEBUG_UPDATE_PACKETS) { zlog_debug( "ISIS-Upd (%s): Rcvd %sL%d LSP on %s, cirType %s, cirID %u", circuit->area->area_tag, @@ -830,7 +842,7 @@ static int process_lsp(uint8_t pdu_type, struct isis_circuit *circuit, circuit->interface->name, circuit_t2string(circuit->is_type), circuit->circuit_id); - if (isis->debugs & DEBUG_PACKET_DUMP) + if (IS_DEBUG_PACKET_DUMP) zlog_dump_data(STREAM_DATA(circuit->rcv_stream), stream_get_endp(circuit->rcv_stream)); } @@ -851,16 +863,14 @@ static int process_lsp(uint8_t pdu_type, struct isis_circuit *circuit, #endif /* ifndef FABRICD */ if (pdu_len_validate(hdr.pdu_len, circuit)) { - zlog_debug("ISIS-Upd (%s): LSP %s invalid LSP length %" PRIu16, + zlog_debug("ISIS-Upd (%s): LSP %s invalid LSP length %hu", circuit->area->area_tag, rawlspid_print(hdr.lsp_id), hdr.pdu_len); return ISIS_WARNING; } - if (isis->debugs & DEBUG_UPDATE_PACKETS) { - zlog_debug("ISIS-Upd (%s): Rcvd L%d LSP %s, seq 0x%08" PRIx32 - ", cksum 0x%04" PRIx16 ", lifetime %" PRIu16 - "s, len %" PRIu16 ", on %s", + if (IS_DEBUG_UPDATE_PACKETS) { + zlog_debug("ISIS-Upd (%s): Rcvd L%d LSP %s, seq 0x%08x, cksum 0x%04hx, lifetime %hus, len %hu, on %s", circuit->area->area_tag, level, rawlspid_print(hdr.lsp_id), hdr.seqno, hdr.checksum, hdr.rem_lifetime, hdr.pdu_len, @@ -870,7 +880,7 @@ static int process_lsp(uint8_t pdu_type, struct isis_circuit *circuit, /* lsp is_type check */ if ((hdr.lsp_bits & IS_LEVEL_1) != IS_LEVEL_1) { zlog_debug( - "ISIS-Upd (%s): LSP %s invalid LSP is type 0x%" PRIx8, + "ISIS-Upd (%s): LSP %s invalid LSP is type 0x%x", circuit->area->area_tag, rawlspid_print(hdr.lsp_id), hdr.lsp_bits & IS_LEVEL_1_AND_2); /* continue as per RFC1122 Be liberal in what you accept, and @@ -882,7 +892,7 @@ static int process_lsp(uint8_t pdu_type, struct isis_circuit *circuit, if (iso_csum_verify(STREAM_DATA(circuit->rcv_stream) + 12, hdr.pdu_len - 12, hdr.checksum, 12)) { zlog_debug( - "ISIS-Upd (%s): LSP %s invalid LSP checksum 0x%04" PRIx16, + "ISIS-Upd (%s): LSP %s invalid LSP checksum 0x%04hx", circuit->area->area_tag, rawlspid_print(hdr.lsp_id), hdr.checksum); return ISIS_WARNING; @@ -891,8 +901,7 @@ static int process_lsp(uint8_t pdu_type, struct isis_circuit *circuit, /* 7.3.15.1 a) 1 - external domain circuit will discard lsps */ if (circuit->ext_domain) { zlog_debug( - "ISIS-Upd (%s): LSP %s received at level %d over circuit with " - "externalDomain = true", + "ISIS-Upd (%s): LSP %s received at level %d over circuit with externalDomain = true", circuit->area->area_tag, rawlspid_print(hdr.lsp_id), level); return ISIS_WARNING; @@ -901,8 +910,7 @@ static int process_lsp(uint8_t pdu_type, struct isis_circuit *circuit, /* 7.3.15.1 a) 2,3 - manualL2OnlyMode not implemented */ if (!(circuit->is_type & level)) { zlog_debug( - "ISIS-Upd (%s): LSP %s received at level %d over circuit of" - " type %s", + "ISIS-Upd (%s): LSP %s received at level %d over circuit of type %s", circuit->area->area_tag, rawlspid_print(hdr.lsp_id), level, circuit_t2string(circuit->is_type)); return ISIS_WARNING; @@ -947,11 +955,14 @@ static int process_lsp(uint8_t pdu_type, struct isis_circuit *circuit, hdr.lsp_id); #ifndef FABRICD /* send northbound notification */ - if (auth_code == ISIS_AUTH_FAILURE) + if (auth_code == ISIS_AUTH_FAILURE) { + circuit->auth_failures++; isis_notif_authentication_failure(circuit, raw_pdu); - else /* AUTH_TYPE_FAILURE or NO_VALIDATOR */ + } else { /* AUTH_TYPE_FAILURE or NO_VALIDATOR */ + circuit->auth_type_failures++; isis_notif_authentication_type_failure(circuit, raw_pdu); + } #endif /* ifndef FABRICD */ goto out; } @@ -974,9 +985,7 @@ static int process_lsp(uint8_t pdu_type, struct isis_circuit *circuit, if (circuit->circ_type == CIRCUIT_T_BROADCAST) { if (!isis_adj_lookup_snpa(ssnpa, circuit->u.bc.adjdb[level - 1])) { - zlog_debug("(%s): DS ======= LSP %s, seq 0x%08" PRIx32 - ", cksum 0x%04" PRIx16 ", lifetime %" PRIu16 - "s on %s", + zlog_debug("(%s): DS ======= LSP %s, seq 0x%08x, cksum 0x%04hx, lifetime %hus on %s", circuit->area->area_tag, rawlspid_print(hdr.lsp_id), hdr.seqno, hdr.checksum, hdr.rem_lifetime, @@ -1017,8 +1026,7 @@ static int process_lsp(uint8_t pdu_type, struct isis_circuit *circuit, if (lsp && (lsp->hdr.seqno == hdr.seqno) && (lsp->hdr.checksum != hdr.checksum) && hdr.rem_lifetime) { - zlog_warn("ISIS-Upd (%s): LSP %s seq 0x%08" PRIx32 - " with confused checksum received.", + zlog_warn("ISIS-Upd (%s): LSP %s seq 0x%08x with confused checksum received.", circuit->area->area_tag, rawlspid_print(hdr.lsp_id), hdr.seqno); hdr.rem_lifetime = 0; @@ -1036,7 +1044,8 @@ static int process_lsp(uint8_t pdu_type, struct isis_circuit *circuit, ack_lsp(&hdr, circuit, level); goto out; /* FIXME: do we need a purge? */ } else { - if (memcmp(hdr.lsp_id, isis->sysid, ISIS_SYS_ID_LEN)) { + if (memcmp(hdr.lsp_id, circuit->area->isis->sysid, + ISIS_SYS_ID_LEN)) { /* LSP by some other system -> do 7.3.16.4 b) */ /* 7.3.16.4 b) 1) */ if (comp == LSP_NEWER) { @@ -1044,6 +1053,8 @@ static int process_lsp(uint8_t pdu_type, struct isis_circuit *circuit, circuit->rcv_stream, circuit->area, level, lsp_confusion); + if (lsp_confusion) + isis_free_tlvs(tlvs); tlvs = NULL; /* ii */ lsp_flood_or_update(lsp, NULL, @@ -1109,11 +1120,9 @@ static int process_lsp(uint8_t pdu_type, struct isis_circuit *circuit, lsp, TX_LSP_NORMAL); ISIS_CLEAR_FLAG(lsp->SSNflags, circuit); } - if (isis->debugs & DEBUG_UPDATE_PACKETS) + if (IS_DEBUG_UPDATE_PACKETS) zlog_debug( - "ISIS-Upd (%s): (1) " - "re-originating LSP %s new seq " - "0x%08" PRIx32, + "ISIS-Upd (%s): (1) re-originating LSP %s new seq 0x%08x", circuit->area->area_tag, rawlspid_print(hdr.lsp_id), lsp->hdr.seqno); @@ -1130,7 +1139,8 @@ static int process_lsp(uint8_t pdu_type, struct isis_circuit *circuit, } /* 7.3.15.1 c) - If this is our own lsp and we don't have it initiate a * purge */ - if (memcmp(hdr.lsp_id, isis->sysid, ISIS_SYS_ID_LEN) == 0) { + if (memcmp(hdr.lsp_id, circuit->area->isis->sysid, ISIS_SYS_ID_LEN) + == 0) { if (!lsp) { /* 7.3.16.4: initiate a purge */ lsp_purge_non_exist(level, &hdr, circuit->area); @@ -1153,9 +1163,9 @@ static int process_lsp(uint8_t pdu_type, struct isis_circuit *circuit, isis_notif_seqno_skipped(circuit, rawlspid_print(hdr.lsp_id)); #endif /* ifndef FABRICD */ - if (isis->debugs & DEBUG_UPDATE_PACKETS) { + if (IS_DEBUG_UPDATE_PACKETS) { zlog_debug( - "ISIS-Upd (%s): (2) re-originating LSP %s new seq 0x%08" PRIx32, + "ISIS-Upd (%s): (2) re-originating LSP %s new seq 0x%08x", circuit->area->area_tag, rawlspid_print(hdr.lsp_id), lsp->hdr.seqno); @@ -1284,14 +1294,14 @@ static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit, return ISIS_WARNING; } - if (isis->debugs & DEBUG_SNP_PACKETS) { + if (IS_DEBUG_SNP_PACKETS) { zlog_debug( "ISIS-Snp (%s): Rcvd L%d %cSNP on %s, cirType %s, cirID %u", circuit->area->area_tag, level, typechar, circuit->interface->name, circuit_t2string(circuit->is_type), circuit->circuit_id); - if (isis->debugs & DEBUG_PACKET_DUMP) + if (IS_DEBUG_PACKET_DUMP) zlog_dump_data(STREAM_DATA(circuit->rcv_stream), stream_get_endp(circuit->rcv_stream)); } @@ -1300,8 +1310,7 @@ static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit, if (circuit->ext_domain) { zlog_debug( - "ISIS-Snp (%s): Rcvd L%d %cSNP on %s, " - "skipping: circuit externalDomain = true", + "ISIS-Snp (%s): Rcvd L%d %cSNP on %s, skipping: circuit externalDomain = true", circuit->area->area_tag, level, typechar, circuit->interface->name); @@ -1311,8 +1320,7 @@ static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit, /* 7.3.15.2 a) 2,3 - manualL2OnlyMode not implemented */ if (!(circuit->is_type & level)) { zlog_debug( - "ISIS-Snp (%s): Rcvd L%d %cSNP on %s, " - "skipping: circuit type %s does not match level %d", + "ISIS-Snp (%s): Rcvd L%d %cSNP on %s, skipping: circuit type %s does not match level %d", circuit->area->area_tag, level, typechar, circuit->interface->name, circuit_t2string(circuit->is_type), level); @@ -1324,8 +1332,7 @@ static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit, if (!is_csnp && (circuit->circ_type == CIRCUIT_T_BROADCAST) && !circuit->u.bc.is_dr[level - 1]) { zlog_debug( - "ISIS-Snp (%s): Rcvd L%d %cSNP from %s on %s, " - "skipping: we are not the DIS", + "ISIS-Snp (%s): Rcvd L%d %cSNP from %s on %s, skipping: we are not the DIS", circuit->area->area_tag, level, typechar, snpa_print(ssnpa), circuit->interface->name); @@ -1369,6 +1376,7 @@ static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit, struct isis_passwd *passwd = (level == IS_LEVEL_1) ? &circuit->area->area_passwd : &circuit->area->domain_passwd; + if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_RECV)) { int auth_code = isis_tlvs_auth_is_valid( tlvs, passwd, circuit->rcv_stream, false); @@ -1380,12 +1388,15 @@ static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit, /* send northbound notification */ stream_get_from(raw_pdu, circuit->rcv_stream, pdu_start, pdu_end - pdu_start); - if (auth_code == ISIS_AUTH_FAILURE) + if (auth_code == ISIS_AUTH_FAILURE) { + circuit->auth_failures++; isis_notif_authentication_failure(circuit, raw_pdu); - else /* AUTH_TYPE_FAILURE or NO_VALIDATOR */ + } else { /* AUTH_TYPE_FAILURE or NO_VALIDATOR */ + circuit->auth_type_failures++; isis_notif_authentication_type_failure(circuit, raw_pdu); + } #endif /* ifndef FABRICD */ goto out; } @@ -1395,15 +1406,14 @@ static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit, (struct isis_lsp_entry *)tlvs->lsp_entries.head; /* debug isis snp-packets */ - if (isis->debugs & DEBUG_SNP_PACKETS) { + if (IS_DEBUG_SNP_PACKETS) { zlog_debug("ISIS-Snp (%s): Rcvd L%d %cSNP from %s on %s", circuit->area->area_tag, level, typechar, snpa_print(ssnpa), circuit->interface->name); for (struct isis_lsp_entry *entry = entry_head; entry; entry = entry->next) { zlog_debug( - "ISIS-Snp (%s): %cSNP entry %s, seq 0x%08" PRIx32 - ", cksum 0x%04" PRIx16 ", lifetime %" PRIu16 "s", + "ISIS-Snp (%s): %cSNP entry %s, seq 0x%08x, cksum 0x%04hx, lifetime %hus", circuit->area->area_tag, typechar, rawlspid_print(entry->id), entry->seqno, entry->checksum, entry->rem_lifetime); @@ -1417,7 +1427,8 @@ static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit, entry = entry->next) { struct isis_lsp *lsp = lsp_search(&circuit->area->lspdb[level - 1], entry->id); - bool own_lsp = !memcmp(entry->id, isis->sysid, ISIS_SYS_ID_LEN); + bool own_lsp = !memcmp(entry->id, circuit->area->isis->sysid, + ISIS_SYS_ID_LEN); if (lsp) { /* 7.3.15.2 b) 1) is this LSP newer */ int cmp = lsp_compare(circuit->area->area_tag, lsp, @@ -1456,8 +1467,9 @@ static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit, * are not 0, * insert it and set SSN on it */ if (entry->rem_lifetime && entry->checksum - && entry->seqno && memcmp(entry->id, isis->sysid, - ISIS_SYS_ID_LEN)) { + && entry->seqno + && memcmp(entry->id, circuit->area->isis->sysid, + ISIS_SYS_ID_LEN)) { struct isis_lsp *lsp0 = NULL; if (LSP_FRAGMENT(entry->id)) { @@ -1596,18 +1608,18 @@ int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa) if (idrp == ISO9542_ESIS) { flog_err(EC_LIB_DEVELOPMENT, - "No support for ES-IS packet IDRP=%" PRIx8, idrp); + "No support for ES-IS packet IDRP=%hhx", idrp); return ISIS_ERROR; } if (idrp != ISO10589_ISIS) { - flog_err(EC_ISIS_PACKET, "Not an IS-IS packet IDRP=%" PRIx8, + flog_err(EC_ISIS_PACKET, "Not an IS-IS packet IDRP=%hhx", idrp); return ISIS_ERROR; } if (version1 != 1) { - zlog_warn("Unsupported ISIS version %" PRIu8, version1); + zlog_warn("Unsupported ISIS version %hhu", version1); #ifndef FABRICD /* send northbound notification */ isis_notif_version_skew(circuit, version1, raw_pdu); @@ -1618,9 +1630,9 @@ int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa) if (id_len != 0 && id_len != ISIS_SYS_ID_LEN) { flog_err( EC_ISIS_PACKET, - "IDFieldLengthMismatch: ID Length field in a received PDU %" PRIu8 - ", while the parameter for this IS is %u", + "IDFieldLengthMismatch: ID Length field in a received PDU %hhu, while the parameter for this IS is %u", id_len, ISIS_SYS_ID_LEN); + circuit->id_len_mismatches++; #ifndef FABRICD /* send northbound notification */ isis_notif_id_len_mismatch(circuit, id_len, raw_pdu); @@ -1630,14 +1642,13 @@ int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa) uint8_t expected_length; if (pdu_size(pdu_type, &expected_length)) { - zlog_warn("Unsupported ISIS PDU %" PRIu8, pdu_type); + zlog_warn("Unsupported ISIS PDU %hhu", pdu_type); return ISIS_WARNING; } if (length != expected_length) { flog_err(EC_ISIS_PACKET, - "Exepected fixed header length = %" PRIu8 - " but got %" PRIu8, + "Expected fixed header length = %hhu but got %hhu", expected_length, length); return ISIS_ERROR; } @@ -1650,7 +1661,7 @@ int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa) } if (version2 != 1) { - zlog_warn("Unsupported ISIS PDU version %" PRIu8, version2); + zlog_warn("Unsupported ISIS PDU version %hhu", version2); #ifndef FABRICD /* send northbound notification */ isis_notif_version_skew(circuit, version2, raw_pdu); @@ -1665,14 +1676,15 @@ int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa) } /* either 3 or 0 */ - if (pdu_type != FS_LINK_STATE /* FS PDU doesn't contain max area addr field */ + if (pdu_type != FS_LINK_STATE /* FS PDU doesn't contain max area addr + field */ && max_area_addrs != 0 - && max_area_addrs != isis->max_area_addrs) { + && max_area_addrs != circuit->area->isis->max_area_addrs) { flog_err( EC_ISIS_PACKET, - "maximumAreaAddressesMismatch: maximumAreaAdresses in a received PDU %" PRIu8 - " while the parameter for this IS is %u", - max_area_addrs, isis->max_area_addrs); + "maximumAreaAddressesMismatch: maximumAreaAdresses in a received PDU %hhu while the parameter for this IS is %u", + max_area_addrs, circuit->area->isis->max_area_addrs); + circuit->max_area_addr_mismatches++; #ifndef FABRICD /* send northbound notification */ isis_notif_max_area_addr_mismatch(circuit, max_area_addrs, @@ -1896,7 +1908,7 @@ int send_hello(struct isis_circuit *circuit, int level) return ISIS_WARNING; /* XXX: Maybe Log TLV structure? */ } - if (isis->debugs & DEBUG_ADJ_PACKETS) { + if (IS_DEBUG_ADJ_PACKETS) { if (circuit->circ_type == CIRCUIT_T_BROADCAST) { zlog_debug( "ISIS-Adj (%s): Sending L%d LAN IIH on %s, length %zd", @@ -1910,7 +1922,7 @@ int send_hello(struct isis_circuit *circuit, int level) circuit->interface->name, stream_get_endp(circuit->snd_stream)); } - if (isis->debugs & DEBUG_PACKET_DUMP) + if (IS_DEBUG_PACKET_DUMP) zlog_dump_data(STREAM_DATA(circuit->snd_stream), stream_get_endp(circuit->snd_stream)); } @@ -2050,8 +2062,10 @@ int send_csnp(struct isis_circuit *circuit, int level) fill_fixed_hdr(pdu_type, circuit->snd_stream); size_t len_pointer = stream_get_endp(circuit->snd_stream); + stream_putw(circuit->snd_stream, 0); - stream_put(circuit->snd_stream, isis->sysid, ISIS_SYS_ID_LEN); + stream_put(circuit->snd_stream, circuit->area->isis->sysid, + ISIS_SYS_ID_LEN); /* with zero circuit id - ref 9.10, 9.11 */ stream_putc(circuit->snd_stream, 0); @@ -2115,7 +2129,7 @@ int send_csnp(struct isis_circuit *circuit, int level) return ISIS_WARNING; } - if (isis->debugs & DEBUG_SNP_PACKETS) { + if (IS_DEBUG_SNP_PACKETS) { zlog_debug( "ISIS-Snp (%s): Sending L%d CSNP on %s, length %zd", circuit->area->area_tag, level, @@ -2123,7 +2137,7 @@ int send_csnp(struct isis_circuit *circuit, int level) stream_get_endp(circuit->snd_stream)); log_multiline(LOG_DEBUG, " ", "%s", isis_format_tlvs(tlvs)); - if (isis->debugs & DEBUG_PACKET_DUMP) + if (IS_DEBUG_PACKET_DUMP) zlog_dump_data( STREAM_DATA(circuit->snd_stream), stream_get_endp(circuit->snd_stream)); @@ -2228,7 +2242,8 @@ static int send_psnp(int level, struct isis_circuit *circuit) size_t len_pointer = stream_get_endp(circuit->snd_stream); stream_putw(circuit->snd_stream, 0); /* length is filled in later */ - stream_put(circuit->snd_stream, isis->sysid, ISIS_SYS_ID_LEN); + stream_put(circuit->snd_stream, circuit->area->isis->sysid, + ISIS_SYS_ID_LEN); stream_putc(circuit->snd_stream, circuit->idx); struct isis_passwd *passwd = (level == ISIS_LEVEL1) @@ -2258,7 +2273,7 @@ static int send_psnp(int level, struct isis_circuit *circuit) if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_SEND)) isis_tlvs_add_auth(tlvs, passwd); - for_each (lspdb, &circuit->area->lspdb[level - 1], lsp) { + frr_each (lspdb, &circuit->area->lspdb[level - 1], lsp) { if (ISIS_CHECK_FLAG(lsp->SSNflags, circuit)) isis_tlvs_add_lsp_entry(tlvs, lsp); @@ -2278,7 +2293,7 @@ static int send_psnp(int level, struct isis_circuit *circuit) return ISIS_WARNING; } - if (isis->debugs & DEBUG_SNP_PACKETS) { + if (IS_DEBUG_SNP_PACKETS) { zlog_debug( "ISIS-Snp (%s): Sending L%d PSNP on %s, length %zd", circuit->area->area_tag, level, @@ -2286,7 +2301,7 @@ static int send_psnp(int level, struct isis_circuit *circuit) stream_get_endp(circuit->snd_stream)); log_multiline(LOG_DEBUG, " ", "%s", isis_format_tlvs(tlvs)); - if (isis->debugs & DEBUG_PACKET_DUMP) + if (IS_DEBUG_PACKET_DUMP) zlog_dump_data( STREAM_DATA(circuit->snd_stream), stream_get_endp(circuit->snd_stream)); @@ -2390,9 +2405,7 @@ void send_lsp(struct isis_circuit *circuit, struct isis_lsp *lsp, if (stream_get_endp(lsp->pdu) > stream_get_size(circuit->snd_stream)) { flog_err( EC_ISIS_PACKET, - "ISIS-Upd (%s): Can't send L%d LSP %s, seq 0x%08" PRIx32 - ", cksum 0x%04" PRIx16 ", lifetime %" PRIu16 - "s on %s. LSP Size is %zu while interface stream size is %zu.", + "ISIS-Upd (%s): Can't send L%d LSP %s, seq 0x%08x, cksum 0x%04hx, lifetime %hus on %s. LSP Size is %zu while interface stream size is %zu.", circuit->area->area_tag, lsp->level, rawlspid_print(lsp->hdr.lsp_id), lsp->hdr.seqno, lsp->hdr.checksum, lsp->hdr.rem_lifetime, @@ -2403,7 +2416,7 @@ void send_lsp(struct isis_circuit *circuit, struct isis_lsp *lsp, isis_notif_lsp_too_large(circuit, stream_get_endp(lsp->pdu), rawlspid_print(lsp->hdr.lsp_id)); #endif /* ifndef FABRICD */ - if (isis->debugs & DEBUG_PACKET_DUMP) + if (IS_DEBUG_PACKET_DUMP) zlog_dump_data(STREAM_DATA(lsp->pdu), stream_get_endp(lsp->pdu)); retval = ISIS_ERROR; @@ -2419,10 +2432,8 @@ void send_lsp(struct isis_circuit *circuit, struct isis_lsp *lsp, L2_CIRCUIT_FLOODING_SCOPE); } - if (isis->debugs & DEBUG_UPDATE_PACKETS) { - zlog_debug("ISIS-Upd (%s): Sending %sL%d LSP %s, seq 0x%08" PRIx32 - ", cksum 0x%04" PRIx16 ", lifetime %" PRIu16 - "s on %s", + if (IS_DEBUG_UPDATE_PACKETS) { + zlog_debug("ISIS-Upd (%s): Sending %sL%d LSP %s, seq 0x%08x, cksum 0x%04hx, lifetime %hus on %s", circuit->area->area_tag, (tx_type == TX_LSP_CIRCUIT_SCOPED) ? "Circuit scoped " : "", @@ -2430,7 +2441,7 @@ void send_lsp(struct isis_circuit *circuit, struct isis_lsp *lsp, rawlspid_print(lsp->hdr.lsp_id), lsp->hdr.seqno, lsp->hdr.checksum, lsp->hdr.rem_lifetime, circuit->interface->name); - if (isis->debugs & DEBUG_PACKET_DUMP) + if (IS_DEBUG_PACKET_DUMP) zlog_dump_data(STREAM_DATA(circuit->snd_stream), stream_get_endp(circuit->snd_stream)); } diff --git a/isisd/isis_pfpacket.c b/isisd/isis_pfpacket.c index 824acd0ff8..4a884877f0 100644 --- a/isisd/isis_pfpacket.c +++ b/isisd/isis_pfpacket.c @@ -32,6 +32,7 @@ #include "stream.h" #include "if.h" #include "lib_errors.h" +#include "vrf.h" #include "isisd/isis_constants.h" #include "isisd/isis_common.h" @@ -45,7 +46,7 @@ #include "privs.h" /* tcpdump -i eth0 'isis' -dd */ -static struct sock_filter isisfilter[] = { +static const struct sock_filter isisfilter[] = { /* NB: we're in SOCK_DGRAM, so src/dst mac + length are stripped * off! * (OTOH it's a bit more lower-layer agnostic and might work @@ -57,9 +58,9 @@ static struct sock_filter isisfilter[] = { {0x6, 0, 0, 0x00040000}, {0x6, 0, 0, 0x00000000}, }; -static struct sock_fprog bpf = { +static const struct sock_fprog bpf = { .len = array_size(isisfilter), - .filter = isisfilter, + .filter = (struct sock_filter *)isisfilter, }; /* @@ -67,13 +68,12 @@ static struct sock_fprog bpf = { * ISO 10589 - 8.4.8 */ -uint8_t ALL_L1_ISS[6] = {0x01, 0x80, 0xC2, 0x00, 0x00, 0x14}; -uint8_t ALL_L2_ISS[6] = {0x01, 0x80, 0xC2, 0x00, 0x00, 0x15}; -uint8_t ALL_ISS[6] = {0x09, 0x00, 0x2B, 0x00, 0x00, 0x05}; -uint8_t ALL_ESS[6] = {0x09, 0x00, 0x2B, 0x00, 0x00, 0x04}; +static const uint8_t ALL_L1_ISS[6] = {0x01, 0x80, 0xC2, 0x00, 0x00, 0x14}; +static const uint8_t ALL_L2_ISS[6] = {0x01, 0x80, 0xC2, 0x00, 0x00, 0x15}; +static const uint8_t ALL_ISS[6] = {0x09, 0x00, 0x2B, 0x00, 0x00, 0x05}; +static const uint8_t ALL_ESS[6] = {0x09, 0x00, 0x2B, 0x00, 0x00, 0x04}; static uint8_t discard_buff[8192]; -static uint8_t sock_buff[8192]; /* * if level is 0 we are joining p2p multicast @@ -102,8 +102,7 @@ static int isis_multicast_join(int fd, int registerto, int if_num) } #ifdef EXTREME_DEBUG zlog_debug( - "isis_multicast_join(): fd=%d, reg_to=%d, if_num=%d, " - "address = %02x:%02x:%02x:%02x:%02x:%02x", + "isis_multicast_join(): fd=%d, reg_to=%d, if_num=%d, address = %02x:%02x:%02x:%02x:%02x:%02x", fd, registerto, if_num, mreq.mr_address[0], mreq.mr_address[1], mreq.mr_address[2], mreq.mr_address[3], mreq.mr_address[4], mreq.mr_address[5]); @@ -122,8 +121,18 @@ static int open_packet_socket(struct isis_circuit *circuit) { struct sockaddr_ll s_addr; int fd, retval = ISIS_OK; + struct vrf *vrf = NULL; + + vrf = vrf_lookup_by_id(circuit->interface->vrf_id); + + if (vrf == NULL) { + zlog_warn("open_packet_socket(): failed to find vrf node"); + return ISIS_WARNING; + } + + fd = vrf_socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_ALL), + circuit->interface->vrf_id, vrf->name); - fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_ALL)); if (fd < 0) { zlog_warn("open_packet_socket(): socket() failed %s", safe_strerror(errno)); @@ -184,7 +193,7 @@ int isis_sock_init(struct isis_circuit *circuit) { int retval = ISIS_OK; - frr_elevate_privs(&isisd_privs) { + frr_with_privs(&isisd_privs) { retval = open_packet_socket(circuit); @@ -237,16 +246,13 @@ int isis_recv_pdu_bcast(struct isis_circuit *circuit, uint8_t *ssnpa) || (s_addr.sll_ifindex != (int)circuit->interface->ifindex)) { if (bytesread < 0) { zlog_warn( - "isis_recv_packet_bcast(): ifname %s, fd %d, " - "bytesread %d, recvfrom(): %s", + "isis_recv_packet_bcast(): ifname %s, fd %d, bytesread %d, recvfrom(): %s", circuit->interface->name, circuit->fd, bytesread, safe_strerror(errno)); } if (s_addr.sll_ifindex != (int)circuit->interface->ifindex) { zlog_warn( - "packet is received on multiple interfaces: " - "socket interface %d, circuit interface %d, " - "packet type %u", + "packet is received on multiple interfaces: socket interface %d, circuit interface %d, packet type %u", s_addr.sll_ifindex, circuit->interface->ifindex, s_addr.sll_pkttype); } @@ -277,19 +283,22 @@ int isis_recv_pdu_bcast(struct isis_circuit *circuit, uint8_t *ssnpa) return ISIS_WARNING; } - /* on lan we have to read to the static buff first */ - bytesread = recvfrom(circuit->fd, sock_buff, sizeof(sock_buff), - MSG_DONTWAIT, (struct sockaddr *)&s_addr, - (socklen_t *)&addr_len); + /* Ensure that we have enough space for a pdu padded to fill the mtu */ + unsigned int max_size = + circuit->interface->mtu > circuit->interface->mtu6 + ? circuit->interface->mtu + : circuit->interface->mtu6; + uint8_t temp_buff[max_size]; + bytesread = + recvfrom(circuit->fd, temp_buff, max_size, MSG_DONTWAIT, + (struct sockaddr *)&s_addr, (socklen_t *)&addr_len); if (bytesread < 0) { - zlog_warn("isis_recv_pdu_bcast(): recvfrom() failed"); + zlog_warn("%s: recvfrom() failed", __func__); return ISIS_WARNING; } - /* then we lose the LLC */ - stream_write(circuit->rcv_stream, sock_buff + LLC_LEN, + stream_write(circuit->rcv_stream, temp_buff + LLC_LEN, bytesread - LLC_LEN); - memcpy(ssnpa, &s_addr.sll_addr, s_addr.sll_halen); return ISIS_OK; @@ -337,6 +346,7 @@ int isis_send_pdu_bcast(struct isis_circuit *circuit, int level) { struct msghdr msg; struct iovec iov[2]; + char temp_buff[LLC_LEN]; /* we need to do the LLC in here because of P2P circuits, which will * not need it @@ -361,16 +371,16 @@ int isis_send_pdu_bcast(struct isis_circuit *circuit, int level) /* on a broadcast circuit */ /* first we put the LLC in */ - sock_buff[0] = 0xFE; - sock_buff[1] = 0xFE; - sock_buff[2] = 0x03; + temp_buff[0] = 0xFE; + temp_buff[1] = 0xFE; + temp_buff[2] = 0x03; memset(&msg, 0, sizeof(msg)); msg.msg_name = &sa; msg.msg_namelen = sizeof(struct sockaddr_ll); msg.msg_iov = iov; msg.msg_iovlen = 2; - iov[0].iov_base = sock_buff; + iov[0].iov_base = temp_buff; iov[0].iov_len = LLC_LEN; iov[1].iov_base = circuit->snd_stream->data; iov[1].iov_len = stream_get_endp(circuit->snd_stream); diff --git a/isisd/isis_redist.c b/isisd/isis_redist.c index dc23e8ea49..25f0e9596f 100644 --- a/isisd/isis_redist.c +++ b/isisd/isis_redist.c @@ -100,8 +100,7 @@ static void isis_redist_install(struct isis_area *area, int level, if (!er_table) { zlog_warn( - "%s: External reachability table of area %s" - " is not initialized.", + "%s: External reachability table of area %s is not initialized.", __func__, area->area_tag); return; } @@ -134,8 +133,7 @@ static void isis_redist_uninstall(struct isis_area *area, int level, if (!er_table) { zlog_warn( - "%s: External reachability table of area %s" - " is not initialized.", + "%s: External reachability table of area %s is not initialized.", __func__, area->area_tag); return; } @@ -220,8 +218,9 @@ static void isis_redist_ensure_default(struct isis *isis, int family) } /* Handle notification about route being added */ -void isis_redist_add(int type, struct prefix *p, struct prefix_ipv6 *src_p, - uint8_t distance, uint32_t metric) +void isis_redist_add(struct isis *isis, int type, struct prefix *p, + struct prefix_ipv6 *src_p, uint8_t distance, + uint32_t metric) { int family = p->family; struct route_table *ei_table = get_ext_info(isis, family); @@ -272,7 +271,8 @@ void isis_redist_add(int type, struct prefix *p, struct prefix_ipv6 *src_p, } } -void isis_redist_delete(int type, struct prefix *p, struct prefix_ipv6 *src_p) +void isis_redist_delete(struct isis *isis, int type, struct prefix *p, + struct prefix_ipv6 *src_p) { int family = p->family; struct route_table *ei_table = get_ext_info(isis, family); @@ -294,8 +294,8 @@ void isis_redist_delete(int type, struct prefix *p, struct prefix_ipv6 *src_p) * by "default-information originate always". Areas without the * "always" setting will ignore routes with origin * DEFAULT_ROUTE. */ - isis_redist_add(DEFAULT_ROUTE, p, NULL, - 254, MAX_WIDE_PATH_METRIC); + isis_redist_add(isis, DEFAULT_ROUTE, p, NULL, 254, + MAX_WIDE_PATH_METRIC); return; } @@ -310,8 +310,7 @@ void isis_redist_delete(int type, struct prefix *p, struct prefix_ipv6 *src_p) char buf[BUFSIZ]; prefix2str(p, buf, sizeof(buf)); zlog_warn( - "%s: Got a delete for %s route %s, but that route" - " was never added.", + "%s: Got a delete for %s route %s, but that route was never added.", __func__, zebra_route_string(type), buf); if (ei_node) route_unlock_node(ei_node); @@ -389,6 +388,19 @@ static void isis_redist_update_zebra_subscriptions(struct isis *isis) } } +void isis_redist_free(struct isis *isis) +{ + int i; + + for (i = 0; i < REDIST_PROTOCOL_COUNT; i++) { + if (!isis->ext_info[i]) + continue; + + route_table_finish(isis->ext_info[i]); + isis->ext_info[i] = NULL; + } +} + void isis_redist_set(struct isis_area *area, int level, int family, int type, uint32_t metric, const char *routemap, int originate_type) { @@ -605,8 +617,7 @@ DEFUN (no_isis_redistribute, DEFUN (isis_default_originate, isis_default_originate_cmd, - "default-information originate " - " [always] [{metric (0-16777215)|route-map WORD}]", + "default-information originate [always] [{metric (0-16777215)|route-map WORD}]", "Control distribution of default information\n" "Distribute a default route\n" "Distribute default route for IPv4\n" diff --git a/isisd/isis_redist.h b/isisd/isis_redist.h index 9c37c310ea..afce922240 100644 --- a/isisd/isis_redist.h +++ b/isisd/isis_redist.h @@ -40,6 +40,7 @@ struct isis_redist { struct route_map *map; }; +struct isis; struct isis_area; struct prefix; struct prefix_ipv6; @@ -47,9 +48,11 @@ struct vty; struct route_table *get_ext_reach(struct isis_area *area, int family, int level); -void isis_redist_add(int type, struct prefix *p, struct prefix_ipv6 *src_p, - uint8_t distance, uint32_t metric); -void isis_redist_delete(int type, struct prefix *p, struct prefix_ipv6 *src_p); +void isis_redist_add(struct isis *isis, int type, struct prefix *p, + struct prefix_ipv6 *src_p, uint8_t distance, + uint32_t metric); +void isis_redist_delete(struct isis *isis, int type, struct prefix *p, + struct prefix_ipv6 *src_p); int isis_redist_config_write(struct vty *vty, struct isis_area *area, int family); void isis_redist_init(void); @@ -59,4 +62,5 @@ void isis_redist_set(struct isis_area *area, int level, int family, int type, uint32_t metric, const char *routemap, int originate_type); void isis_redist_unset(struct isis_area *area, int level, int family, int type); +void isis_redist_free(struct isis *isis); #endif diff --git a/isisd/isis_route.c b/isisd/isis_route.c index 82005c911e..c83a7c04bb 100644 --- a/isisd/isis_route.c +++ b/isisd/isis_route.c @@ -28,6 +28,7 @@ #include "linklist.h" #include "vty.h" #include "log.h" +#include "lib_errors.h" #include "memory.h" #include "prefix.h" #include "hash.h" @@ -45,157 +46,128 @@ #include "isis_pdu.h" #include "isis_lsp.h" #include "isis_spf.h" +#include "isis_spf_private.h" #include "isis_route.h" #include "isis_zebra.h" -static struct isis_nexthop *isis_nexthop_create(struct in_addr *ip, +DEFINE_HOOK(isis_route_update_hook, + (struct isis_area * area, struct prefix *prefix, + struct isis_route_info *route_info), + (area, prefix, route_info)) + +static struct isis_nexthop *nexthoplookup(struct list *nexthops, int family, + union g_addr *ip, ifindex_t ifindex); +static void isis_route_update(struct isis_area *area, struct prefix *prefix, + struct prefix_ipv6 *src_p, + struct isis_route_info *route_info); + +static struct isis_nexthop *isis_nexthop_create(int family, union g_addr *ip, ifindex_t ifindex) { - struct listnode *node; struct isis_nexthop *nexthop; - for (ALL_LIST_ELEMENTS_RO(isis->nexthops, node, nexthop)) { - if (nexthop->ifindex != ifindex) - continue; - if (ip && memcmp(&nexthop->ip, ip, sizeof(struct in_addr)) != 0) - continue; - - nexthop->lock++; - return nexthop; - } - nexthop = XCALLOC(MTYPE_ISIS_NEXTHOP, sizeof(struct isis_nexthop)); + nexthop->family = family; nexthop->ifindex = ifindex; - memcpy(&nexthop->ip, ip, sizeof(struct in_addr)); - listnode_add(isis->nexthops, nexthop); - nexthop->lock++; + nexthop->ip = *ip; + isis_sr_nexthop_reset(&nexthop->sr); return nexthop; } static void isis_nexthop_delete(struct isis_nexthop *nexthop) { - nexthop->lock--; - if (nexthop->lock == 0) { - listnode_delete(isis->nexthops, nexthop); - XFREE(MTYPE_ISIS_NEXTHOP, nexthop); - } - - return; + XFREE(MTYPE_ISIS_NEXTHOP, nexthop); } -static int nexthoplookup(struct list *nexthops, struct in_addr *ip, - ifindex_t ifindex) +static struct isis_nexthop *nexthoplookup(struct list *nexthops, int family, + union g_addr *ip, ifindex_t ifindex) { struct listnode *node; struct isis_nexthop *nh; for (ALL_LIST_ELEMENTS_RO(nexthops, node, nh)) { - if (!(memcmp(ip, &nh->ip, sizeof(struct in_addr))) - && ifindex == nh->ifindex) - return 1; - } - - return 0; -} - -static struct isis_nexthop6 *isis_nexthop6_new(struct in6_addr *ip6, - ifindex_t ifindex) -{ - struct isis_nexthop6 *nexthop6; - - nexthop6 = XCALLOC(MTYPE_ISIS_NEXTHOP6, sizeof(struct isis_nexthop6)); - - nexthop6->ifindex = ifindex; - memcpy(&nexthop6->ip6, ip6, sizeof(struct in6_addr)); - nexthop6->lock++; - - return nexthop6; -} - -static struct isis_nexthop6 *isis_nexthop6_create(struct in6_addr *ip6, - ifindex_t ifindex) -{ - struct listnode *node; - struct isis_nexthop6 *nexthop6; - - for (ALL_LIST_ELEMENTS_RO(isis->nexthops6, node, nexthop6)) { - if (nexthop6->ifindex != ifindex) + if (nh->family != family) continue; - if (ip6 - && memcmp(&nexthop6->ip6, ip6, sizeof(struct in6_addr)) - != 0) + if (nh->ifindex != ifindex) continue; - nexthop6->lock++; - return nexthop6; - } - - nexthop6 = isis_nexthop6_new(ip6, ifindex); - - return nexthop6; -} - -static void isis_nexthop6_delete(struct isis_nexthop6 *nexthop6) -{ - - nexthop6->lock--; - if (nexthop6->lock == 0) { - listnode_delete(isis->nexthops6, nexthop6); - XFREE(MTYPE_ISIS_NEXTHOP6, nexthop6); - } - - return; -} - -static int nexthop6lookup(struct list *nexthops6, struct in6_addr *ip6, - ifindex_t ifindex) -{ - struct listnode *node; - struct isis_nexthop6 *nh6; + switch (family) { + case AF_INET: + if (IPV4_ADDR_CMP(&nh->ip.ipv4, &ip->ipv4)) + continue; + break; + case AF_INET6: + if (IPV6_ADDR_CMP(&nh->ip.ipv6, &ip->ipv6)) + continue; + break; + default: + flog_err(EC_LIB_DEVELOPMENT, + "%s: unknown address family [%d]", __func__, + family); + exit(1); + } - for (ALL_LIST_ELEMENTS_RO(nexthops6, node, nh6)) { - if (!(memcmp(ip6, &nh6->ip6, sizeof(struct in6_addr))) - && ifindex == nh6->ifindex) - return 1; + return nh; } - return 0; + return NULL; } -static void adjinfo2nexthop(struct list *nexthops, struct isis_adjacency *adj) +static void adjinfo2nexthop(int family, struct list *nexthops, + struct isis_adjacency *adj) { struct isis_nexthop *nh; - - for (unsigned int i = 0; i < adj->ipv4_address_count; i++) { - struct in_addr *ipv4_addr = &adj->ipv4_addresses[i]; - if (!nexthoplookup(nexthops, ipv4_addr, - adj->circuit->interface->ifindex)) { - nh = isis_nexthop_create( - ipv4_addr, adj->circuit->interface->ifindex); - nh->router_address = adj->router_address; - listnode_add(nexthops, nh); - return; + union g_addr ip = {}; + + switch (family) { + case AF_INET: + for (unsigned int i = 0; i < adj->ipv4_address_count; i++) { + ip.ipv4 = adj->ipv4_addresses[i]; + + if (!nexthoplookup(nexthops, AF_INET, &ip, + adj->circuit->interface->ifindex)) { + nh = isis_nexthop_create( + AF_INET, &ip, + adj->circuit->interface->ifindex); + memcpy(nh->sysid, adj->sysid, sizeof(nh->sysid)); + listnode_add(nexthops, nh); + break; + } } + break; + case AF_INET6: + for (unsigned int i = 0; i < adj->ipv6_address_count; i++) { + ip.ipv6 = adj->ipv6_addresses[i]; + + if (!nexthoplookup(nexthops, AF_INET6, &ip, + adj->circuit->interface->ifindex)) { + nh = isis_nexthop_create( + AF_INET6, &ip, + adj->circuit->interface->ifindex); + memcpy(nh->sysid, adj->sysid, sizeof(nh->sysid)); + listnode_add(nexthops, nh); + break; + } + } + break; + default: + flog_err(EC_LIB_DEVELOPMENT, "%s: unknown address family [%d]", + __func__, family); + exit(1); } } -static void adjinfo2nexthop6(struct list *nexthops6, struct isis_adjacency *adj) +static void isis_route_add_dummy_nexthops(struct isis_route_info *rinfo, + const uint8_t *sysid) { - struct isis_nexthop6 *nh6; - - for (unsigned int i = 0; i < adj->ipv6_address_count; i++) { - struct in6_addr *ipv6_addr = &adj->ipv6_addresses[i]; - if (!nexthop6lookup(nexthops6, ipv6_addr, - adj->circuit->interface->ifindex)) { - nh6 = isis_nexthop6_create( - ipv6_addr, adj->circuit->interface->ifindex); - nh6->router_address6 = adj->router_address6; - listnode_add(nexthops6, nh6); - return; - } - } + struct isis_nexthop *nh; + + nh = XCALLOC(MTYPE_ISIS_NEXTHOP, sizeof(struct isis_nexthop)); + memcpy(nh->sysid, sysid, sizeof(nh->sysid)); + isis_sr_nexthop_reset(&nh->sr); + listnode_add(rinfo->nexthops, nh); } static struct isis_route_info *isis_route_info_new(struct prefix *prefix, @@ -205,40 +177,49 @@ static struct isis_route_info *isis_route_info_new(struct prefix *prefix, struct list *adjacencies) { struct isis_route_info *rinfo; - struct isis_adjacency *adj; + struct isis_vertex_adj *vadj; struct listnode *node; rinfo = XCALLOC(MTYPE_ISIS_ROUTE_INFO, sizeof(struct isis_route_info)); - if (prefix->family == AF_INET) { - rinfo->nexthops = list_new(); - for (ALL_LIST_ELEMENTS_RO(adjacencies, node, adj)) { - /* check for force resync this route */ - if (CHECK_FLAG(adj->circuit->flags, - ISIS_CIRCUIT_FLAPPED_AFTER_SPF)) - SET_FLAG(rinfo->flag, - ISIS_ROUTE_FLAG_ZEBRA_RESYNC); - /* update neighbor router address */ + rinfo->nexthops = list_new(); + for (ALL_LIST_ELEMENTS_RO(adjacencies, node, vadj)) { + struct isis_spf_adj *sadj = vadj->sadj; + struct isis_adjacency *adj = sadj->adj; + + /* + * Create dummy nexthops when running SPF on a testing + * environment. + */ + if (CHECK_FLAG(im->options, F_ISIS_UNIT_TEST)) { + isis_route_add_dummy_nexthops(rinfo, sadj->id); + continue; + } + + /* check for force resync this route */ + if (CHECK_FLAG(adj->circuit->flags, + ISIS_CIRCUIT_FLAPPED_AFTER_SPF)) + SET_FLAG(rinfo->flag, ISIS_ROUTE_FLAG_ZEBRA_RESYNC); + + /* update neighbor router address */ + switch (prefix->family) { + case AF_INET: if (depth == 2 && prefix->prefixlen == 32) adj->router_address = prefix->u.prefix4; - adjinfo2nexthop(rinfo->nexthops, adj); - } - } - if (prefix->family == AF_INET6) { - rinfo->nexthops6 = list_new(); - for (ALL_LIST_ELEMENTS_RO(adjacencies, node, adj)) { - /* check for force resync this route */ - if (CHECK_FLAG(adj->circuit->flags, - ISIS_CIRCUIT_FLAPPED_AFTER_SPF)) - SET_FLAG(rinfo->flag, - ISIS_ROUTE_FLAG_ZEBRA_RESYNC); - /* update neighbor router address */ + break; + case AF_INET6: if (depth == 2 && prefix->prefixlen == 128 && (!src_p || !src_p->prefixlen)) { adj->router_address6 = prefix->u.prefix6; } - adjinfo2nexthop6(rinfo->nexthops6, adj); + break; + default: + flog_err(EC_LIB_DEVELOPMENT, + "%s: unknown address family [%d]", __func__, + prefix->family); + exit(1); } + adjinfo2nexthop(prefix->family, rinfo->nexthops, adj); } rinfo->cost = cost; @@ -255,12 +236,6 @@ static void isis_route_info_delete(struct isis_route_info *route_info) list_delete(&route_info->nexthops); } - if (route_info->nexthops6) { - route_info->nexthops6->del = - (void (*)(void *))isis_nexthop6_delete; - list_delete(&route_info->nexthops6); - } - XFREE(MTYPE_ISIS_ROUTE_INFO, route_info); } @@ -280,7 +255,6 @@ static int isis_route_info_same(struct isis_route_info *new, { struct listnode *node; struct isis_nexthop *nexthop; - struct isis_nexthop6 *nexthop6; if (!CHECK_FLAG(old->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED)) return 0; @@ -291,31 +265,15 @@ static int isis_route_info_same(struct isis_route_info *new, if (!isis_route_info_same_attrib(new, old)) return 0; - if (family == AF_INET) { - for (ALL_LIST_ELEMENTS_RO(new->nexthops, node, nexthop)) - if (nexthoplookup(old->nexthops, &nexthop->ip, - nexthop->ifindex) - == 0) - return 0; - - for (ALL_LIST_ELEMENTS_RO(old->nexthops, node, nexthop)) - if (nexthoplookup(new->nexthops, &nexthop->ip, - nexthop->ifindex) - == 0) - return 0; - } else if (family == AF_INET6) { - for (ALL_LIST_ELEMENTS_RO(new->nexthops6, node, nexthop6)) - if (nexthop6lookup(old->nexthops6, &nexthop6->ip6, - nexthop6->ifindex) - == 0) - return 0; - - for (ALL_LIST_ELEMENTS_RO(old->nexthops6, node, nexthop6)) - if (nexthop6lookup(new->nexthops6, &nexthop6->ip6, - nexthop6->ifindex) - == 0) - return 0; - } + for (ALL_LIST_ELEMENTS_RO(new->nexthops, node, nexthop)) + if (!nexthoplookup(old->nexthops, nexthop->family, &nexthop->ip, + nexthop->ifindex)) + return 0; + + for (ALL_LIST_ELEMENTS_RO(old->nexthops, node, nexthop)) + if (!nexthoplookup(new->nexthops, nexthop->family, &nexthop->ip, + nexthop->ifindex)) + return 0; return 1; } @@ -346,24 +304,24 @@ struct isis_route_info *isis_route_create(struct prefix *prefix, rinfo_old = route_node->info; if (!rinfo_old) { - if (isis->debugs & DEBUG_RTE_EVENTS) + if (IS_DEBUG_RTE_EVENTS) zlog_debug("ISIS-Rte (%s) route created: %s", area->area_tag, buff); route_info = rinfo_new; UNSET_FLAG(route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED); } else { route_unlock_node(route_node); - if (isis->debugs & DEBUG_RTE_EVENTS) + if (IS_DEBUG_RTE_EVENTS) zlog_debug("ISIS-Rte (%s) route already exists: %s", area->area_tag, buff); if (isis_route_info_same(rinfo_new, rinfo_old, family)) { - if (isis->debugs & DEBUG_RTE_EVENTS) + if (IS_DEBUG_RTE_EVENTS) zlog_debug("ISIS-Rte (%s) route unchanged: %s", area->area_tag, buff); isis_route_info_delete(rinfo_new); route_info = rinfo_old; } else { - if (isis->debugs & DEBUG_RTE_EVENTS) + if (IS_DEBUG_RTE_EVENTS) zlog_debug("ISIS-Rte (%s) route changed: %s", area->area_tag, buff); isis_route_info_delete(rinfo_old); @@ -379,7 +337,7 @@ struct isis_route_info *isis_route_create(struct prefix *prefix, return route_info; } -static void isis_route_delete(struct route_node *rode, +static void isis_route_delete(struct isis_area *area, struct route_node *rode, struct route_table *table) { struct isis_route_info *rinfo; @@ -395,7 +353,7 @@ static void isis_route_delete(struct route_node *rode, rinfo = rode->info; if (rinfo == NULL) { - if (isis->debugs & DEBUG_RTE_EVENTS) + if (IS_DEBUG_RTE_EVENTS) zlog_debug( "ISIS-Rte: tried to delete non-existant route %s", buff); @@ -404,15 +362,39 @@ static void isis_route_delete(struct route_node *rode, if (CHECK_FLAG(rinfo->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED)) { UNSET_FLAG(rinfo->flag, ISIS_ROUTE_FLAG_ACTIVE); - if (isis->debugs & DEBUG_RTE_EVENTS) + if (IS_DEBUG_RTE_EVENTS) zlog_debug("ISIS-Rte: route delete %s", buff); - isis_zebra_route_update(prefix, src_p, rinfo); + isis_route_update(area, prefix, src_p, rinfo); } isis_route_info_delete(rinfo); rode->info = NULL; route_unlock_node(rode); } +static void isis_route_update(struct isis_area *area, struct prefix *prefix, + struct prefix_ipv6 *src_p, + struct isis_route_info *route_info) +{ + if (CHECK_FLAG(route_info->flag, ISIS_ROUTE_FLAG_ACTIVE)) { + if (CHECK_FLAG(route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED)) + return; + + isis_zebra_route_add_route(area->isis, prefix, src_p, route_info); + hook_call(isis_route_update_hook, area, prefix, route_info); + + SET_FLAG(route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED); + UNSET_FLAG(route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_RESYNC); + } else { + if (!CHECK_FLAG(route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED)) + return; + + isis_zebra_route_del_route(area->isis, prefix, src_p, route_info); + hook_call(isis_route_update_hook, area, prefix, route_info); + + UNSET_FLAG(route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED); + } +} + static void _isis_route_verify_table(struct isis_area *area, struct route_table *table, struct route_table **tables) @@ -434,7 +416,7 @@ static void _isis_route_verify_table(struct isis_area *area, (const struct prefix **)&dst_p, (const struct prefix **)&src_p); - if (isis->debugs & DEBUG_RTE_EVENTS) { + if (IS_DEBUG_RTE_EVENTS) { srcdest2str(dst_p, src_p, buff, sizeof(buff)); zlog_debug( "ISIS-Rte (%s): route validate: %s %s %s %s", @@ -453,7 +435,7 @@ static void _isis_route_verify_table(struct isis_area *area, buff); } - isis_zebra_route_update(dst_p, src_p, rinfo); + isis_route_update(area, dst_p, src_p, rinfo); if (CHECK_FLAG(rinfo->flag, ISIS_ROUTE_FLAG_ACTIVE)) continue; @@ -462,7 +444,7 @@ static void _isis_route_verify_table(struct isis_area *area, * directly for * validating => no problems with deleting routes. */ if (!tables) { - isis_route_delete(rnode, table); + isis_route_delete(area, rnode, table); continue; } @@ -485,7 +467,7 @@ static void _isis_route_verify_table(struct isis_area *area, route_unlock_node(drnode); } - isis_route_delete(rnode, table); + isis_route_delete(area, rnode, table); } } @@ -543,7 +525,8 @@ void isis_route_verify_merge(struct isis_area *area, ISIS_ROUTE_FLAG_ZEBRA_SYNCED ); continue; - } else { + } else if (CHECK_FLAG(rinfo->flag, + ISIS_ROUTE_FLAG_ACTIVE)) { /* Clear the ZEBRA_SYNCED flag on the L1 * route when L2 wins, otherwise L1 * won't get reinstalled when it @@ -553,6 +536,11 @@ void isis_route_verify_merge(struct isis_area *area, mrinfo->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED ); + } else if ( + CHECK_FLAG( + mrinfo->flag, + ISIS_ROUTE_FLAG_ZEBRA_SYNCED)) { + continue; } } mrnode->info = rnode->info; diff --git a/isisd/isis_route.h b/isisd/isis_route.h index 9d6858586b..0356668d7e 100644 --- a/isisd/isis_route.h +++ b/isisd/isis_route.h @@ -25,18 +25,14 @@ #ifndef _ZEBRA_ISIS_ROUTE_H #define _ZEBRA_ISIS_ROUTE_H -struct isis_nexthop6 { - ifindex_t ifindex; - struct in6_addr ip6; - struct in6_addr router_address6; - unsigned int lock; -}; +#include "lib/nexthop.h" struct isis_nexthop { ifindex_t ifindex; - struct in_addr ip; - struct in_addr router_address; - unsigned int lock; + int family; + union g_addr ip; + uint8_t sysid[ISIS_SYS_ID_LEN]; + struct sr_nexthop_info sr; }; struct isis_route_info { @@ -47,9 +43,13 @@ struct isis_route_info { uint32_t cost; uint32_t depth; struct list *nexthops; - struct list *nexthops6; }; +DECLARE_HOOK(isis_route_update_hook, + (struct isis_area * area, struct prefix *prefix, + struct isis_route_info *route_info), + (area, prefix, route_info)) + struct isis_route_info *isis_route_create(struct prefix *prefix, struct prefix_ipv6 *src_p, uint32_t cost, diff --git a/isisd/isis_routemap.c b/isisd/isis_routemap.c index d63676256b..902528e1bb 100644 --- a/isisd/isis_routemap.c +++ b/isisd/isis_routemap.c @@ -48,10 +48,9 @@ #include "isis_zebra.h" #include "isis_routemap.h" -static route_map_result_t route_match_ip_address(void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_match_ip_address(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { struct access_list *alist; @@ -75,13 +74,16 @@ static void route_match_ip_address_free(void *rule) XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); } -static struct route_map_rule_cmd route_match_ip_address_cmd = { - "ip address", route_match_ip_address, route_match_ip_address_compile, - route_match_ip_address_free}; +static const struct route_map_rule_cmd route_match_ip_address_cmd = { + "ip address", + route_match_ip_address, + route_match_ip_address_compile, + route_match_ip_address_free +}; /* ------------------------------------------------------------*/ -static route_map_result_t +static enum route_map_cmd_result_t route_match_ip_address_prefix_list(void *rule, const struct prefix *prefix, route_map_object_t type, void *object) { @@ -107,17 +109,19 @@ static void route_match_ip_address_prefix_list_free(void *rule) XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); } -struct route_map_rule_cmd route_match_ip_address_prefix_list_cmd = { - "ip address prefix-list", route_match_ip_address_prefix_list, +static const struct route_map_rule_cmd + route_match_ip_address_prefix_list_cmd = { + "ip address prefix-list", + route_match_ip_address_prefix_list, route_match_ip_address_prefix_list_compile, - route_match_ip_address_prefix_list_free}; + route_match_ip_address_prefix_list_free +}; /* ------------------------------------------------------------*/ -static route_map_result_t route_match_ipv6_address(void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_match_ipv6_address(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { struct access_list *alist; @@ -141,13 +145,16 @@ static void route_match_ipv6_address_free(void *rule) XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); } -static struct route_map_rule_cmd route_match_ipv6_address_cmd = { - "ipv6 address", route_match_ipv6_address, - route_match_ipv6_address_compile, route_match_ipv6_address_free}; +static const struct route_map_rule_cmd route_match_ipv6_address_cmd = { + "ipv6 address", + route_match_ipv6_address, + route_match_ipv6_address_compile, + route_match_ipv6_address_free +}; /* ------------------------------------------------------------*/ -static route_map_result_t +static enum route_map_cmd_result_t route_match_ipv6_address_prefix_list(void *rule, const struct prefix *prefix, route_map_object_t type, void *object) { @@ -173,17 +180,19 @@ static void route_match_ipv6_address_prefix_list_free(void *rule) XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); } -struct route_map_rule_cmd route_match_ipv6_address_prefix_list_cmd = { - "ipv6 address prefix-list", route_match_ipv6_address_prefix_list, +static const struct route_map_rule_cmd + route_match_ipv6_address_prefix_list_cmd = { + "ipv6 address prefix-list", + route_match_ipv6_address_prefix_list, route_match_ipv6_address_prefix_list_compile, - route_match_ipv6_address_prefix_list_free}; + route_match_ipv6_address_prefix_list_free +}; /* ------------------------------------------------------------*/ -static route_map_result_t route_set_metric(void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_set_metric(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { uint32_t *metric; struct isis_ext_info *info; @@ -218,9 +227,12 @@ static void route_set_metric_free(void *rule) XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); } -static struct route_map_rule_cmd route_set_metric_cmd = { - "metric", route_set_metric, route_set_metric_compile, - route_set_metric_free}; +static const struct route_map_rule_cmd route_set_metric_cmd = { + "metric", + route_set_metric, + route_set_metric_compile, + route_set_metric_free +}; void isis_route_map_init(void) { diff --git a/isisd/isis_spf.c b/isisd/isis_spf.c index a28220eb26..4e523de161 100644 --- a/isisd/isis_spf.c +++ b/isisd/isis_spf.c @@ -29,12 +29,14 @@ #include "vty.h" #include "log.h" #include "command.h" +#include "termtable.h" #include "memory.h" #include "prefix.h" #include "if.h" #include "table.h" #include "spf_backoff.h" #include "srcdest_table.h" +#include "vrf.h" #include "isis_constants.h" #include "isis_common.h" @@ -55,6 +57,13 @@ #include "isis_spf_private.h" DEFINE_MTYPE_STATIC(ISISD, ISIS_SPF_RUN, "ISIS SPF Run Info"); +DEFINE_MTYPE_STATIC(ISISD, ISIS_SPF_ADJ, "ISIS SPF Adjacency"); +DEFINE_MTYPE_STATIC(ISISD, ISIS_VERTEX_ADJ, "ISIS SPF Vertex Adjacency"); + +static void spf_adj_list_parse_lsp(struct isis_spftree *spftree, + struct list *adj_list, struct isis_lsp *lsp, + const uint8_t *pseudo_nodeid, + uint32_t pseudo_metric); /* * supports the given af ? @@ -79,22 +88,29 @@ struct isis_spf_run { static void remove_excess_adjs(struct list *adjs) { struct listnode *node, *excess = NULL; - struct isis_adjacency *adj, *candidate = NULL; + struct isis_vertex_adj *vadj, *candidate = NULL; int comp; - for (ALL_LIST_ELEMENTS_RO(adjs, node, adj)) { + for (ALL_LIST_ELEMENTS_RO(adjs, node, vadj)) { + struct isis_adjacency *adj, *candidate_adj; + + adj = vadj->sadj->adj; + assert(adj); + if (excess == NULL) excess = node; candidate = listgetdata(excess); + candidate_adj = candidate->sadj->adj; - if (candidate->sys_type < adj->sys_type) { + if (candidate_adj->sys_type < adj->sys_type) { excess = node; continue; } - if (candidate->sys_type > adj->sys_type) + if (candidate_adj->sys_type > adj->sys_type) continue; - comp = memcmp(candidate->sysid, adj->sysid, ISIS_SYS_ID_LEN); + comp = memcmp(candidate_adj->sysid, adj->sysid, + ISIS_SYS_ID_LEN); if (comp > 0) { excess = node; continue; @@ -102,15 +118,15 @@ static void remove_excess_adjs(struct list *adjs) if (comp < 0) continue; - if (candidate->circuit->idx > adj->circuit->idx) { + if (candidate_adj->circuit->idx > adj->circuit->idx) { excess = node; continue; } - if (candidate->circuit->idx < adj->circuit->idx) + if (candidate_adj->circuit->idx < adj->circuit->idx) continue; - comp = memcmp(candidate->snpa, adj->snpa, ETH_ALEN); + comp = memcmp(candidate_adj->snpa, adj->snpa, ETH_ALEN); if (comp > 0) { excess = node; continue; @@ -127,34 +143,24 @@ static const char *vtype2string(enum vertextype vtype) switch (vtype) { case VTYPE_PSEUDO_IS: return "pseudo_IS"; - break; case VTYPE_PSEUDO_TE_IS: return "pseudo_TE-IS"; - break; case VTYPE_NONPSEUDO_IS: return "IS"; - break; case VTYPE_NONPSEUDO_TE_IS: return "TE-IS"; - break; case VTYPE_ES: return "ES"; - break; case VTYPE_IPREACH_INTERNAL: return "IP internal"; - break; case VTYPE_IPREACH_EXTERNAL: return "IP external"; - break; case VTYPE_IPREACH_TE: return "IP TE"; - break; case VTYPE_IP6REACH_INTERNAL: return "IP6 internal"; - break; case VTYPE_IP6REACH_EXTERNAL: return "IP6 external"; - break; default: return "UNKNOWN"; } @@ -164,7 +170,9 @@ static const char *vtype2string(enum vertextype vtype) const char *vid2string(struct isis_vertex *vertex, char *buff, int size) { if (VTYPE_IS(vertex->type) || VTYPE_ES(vertex->type)) { - return print_sys_hostname(vertex->N.id); + const char *hostname = print_sys_hostname(vertex->N.id); + strlcpy(buff, hostname, size); + return buff; } if (VTYPE_IP(vertex->type)) { @@ -177,6 +185,13 @@ const char *vid2string(struct isis_vertex *vertex, char *buff, int size) return "UNKNOWN"; } +static void isis_vertex_adj_free(void *arg) +{ + struct isis_vertex_adj *vadj = arg; + + XFREE(MTYPE_ISIS_VERTEX_ADJ, vadj); +} + static struct isis_vertex *isis_vertex_new(struct isis_spftree *spftree, void *id, enum vertextype vtype) @@ -188,9 +203,10 @@ static struct isis_vertex *isis_vertex_new(struct isis_spftree *spftree, isis_vertex_id_init(vertex, id, vtype); vertex->Adj_N = list_new(); + vertex->Adj_N->del = isis_vertex_adj_free; vertex->parents = list_new(); - if (spftree->hopcount_metric) { + if (CHECK_FLAG(spftree->flags, F_SPFTREE_HOPCOUNT_METRIC)) { vertex->firsthops = hash_create(isis_vertex_queue_hash_key, isis_vertex_queue_hash_cmp, NULL); @@ -199,21 +215,69 @@ static struct isis_vertex *isis_vertex_new(struct isis_spftree *spftree, return vertex; } +static struct isis_vertex_adj *isis_vertex_adj_add(struct isis_vertex *vertex, + struct isis_spf_adj *sadj) +{ + struct isis_vertex_adj *vadj; + + vadj = XCALLOC(MTYPE_ISIS_VERTEX_ADJ, sizeof(*vadj)); + vadj->sadj = sadj; + listnode_add(vertex->Adj_N, vadj); + + return vadj; +} + static void isis_vertex_adj_del(struct isis_vertex *vertex, struct isis_adjacency *adj) { + struct isis_vertex_adj *vadj; struct listnode *node, *nextnode; + if (!vertex) return; - for (node = listhead(vertex->Adj_N); node; node = nextnode) { - nextnode = listnextnode(node); - if (listgetdata(node) == adj) - list_delete_node(vertex->Adj_N, node); + + for (ALL_LIST_ELEMENTS(vertex->Adj_N, node, nextnode, vadj)) { + if (vadj->sadj->adj == adj) { + listnode_delete(vertex->Adj_N, vadj); + isis_vertex_adj_free(vadj); + } } return; } -struct isis_spftree *isis_spftree_new(struct isis_area *area) +bool isis_vertex_adj_exists(const struct isis_spftree *spftree, + const struct isis_vertex *vertex, + const struct isis_spf_adj *sadj) +{ + struct isis_vertex_adj *tmp; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(vertex->Adj_N, node, tmp)) { + if (CHECK_FLAG(spftree->flags, F_SPFTREE_NO_ADJACENCIES)) { + if (memcmp(sadj->id, tmp->sadj->id, sizeof(sadj->id)) + == 0) + return true; + } else { + if (sadj->adj == tmp->sadj->adj) + return true; + } + } + + return false; +} + +static void isis_spf_adj_free(void *arg) +{ + struct isis_spf_adj *sadj = arg; + + XFREE(MTYPE_ISIS_SPF_ADJ, sadj); +} + +struct isis_spftree *isis_spftree_new(struct isis_area *area, + struct lspdb_head *lspdb, + const uint8_t *sysid, int level, + enum spf_tree_id tree_id, + enum spf_type type, uint8_t flags) { struct isis_spftree *tree; @@ -223,15 +287,26 @@ struct isis_spftree *isis_spftree_new(struct isis_area *area) isis_vertex_queue_init(&tree->paths, "IS-IS SPF paths", false); tree->route_table = srcdest_table_init(); tree->area = area; + tree->lspdb = lspdb; + tree->sadj_list = list_new(); + tree->sadj_list->del = isis_spf_adj_free; tree->last_run_timestamp = 0; tree->last_run_monotime = 0; tree->last_run_duration = 0; tree->runcount = 0; + tree->type = type; + memcpy(tree->sysid, sysid, ISIS_SYS_ID_LEN); + tree->level = level; + tree->tree_id = tree_id; + tree->family = (tree->tree_id == SPFTREE_IPV4) ? AF_INET : AF_INET6; + tree->flags = flags; + return tree; } void isis_spftree_del(struct isis_spftree *spftree) { + list_delete(&spftree->sadj_list); isis_vertex_queue_free(&spftree->tents); isis_vertex_queue_free(&spftree->paths); route_table_finish(spftree->route_table); @@ -263,7 +338,10 @@ void spftree_area_init(struct isis_area *area) if (area->spftree[tree][level - 1]) continue; - area->spftree[tree][level - 1] = isis_spftree_new(area); + area->spftree[tree][level - 1] = + isis_spftree_new(area, &area->lspdb[level - 1], + area->isis->sysid, level, tree, + SPF_TYPE_FORWARD, 0); } } } @@ -282,8 +360,14 @@ void spftree_area_del(struct isis_area *area) } } -void spftree_area_adj_del(struct isis_area *area, struct isis_adjacency *adj) +static int spf_adj_state_change(struct isis_adjacency *adj) { + struct isis_area *area = adj->circuit->area; + + if (adj->adj_state == ISIS_ADJ_UP) + return 0; + + /* Remove adjacency from all SPF trees. */ for (int tree = SPFTREE_IPV4; tree < SPFTREE_COUNT; tree++) { for (int level = ISIS_LEVEL1; level <= ISIS_LEVEL2; level++) { if (!(area->is_type & level)) @@ -297,13 +381,15 @@ void spftree_area_adj_del(struct isis_area *area, struct isis_adjacency *adj) if (fabricd_spftree(area) != NULL) isis_spftree_adj_del(fabricd_spftree(area), adj); + + return 0; } /* * Find the system LSP: returns the LSP in our LSP database * associated with the given system ID. */ -static struct isis_lsp *isis_root_system_lsp(struct isis_area *area, int level, +static struct isis_lsp *isis_root_system_lsp(struct lspdb_head *lspdb, uint8_t *sysid) { struct isis_lsp *lsp; @@ -312,7 +398,7 @@ static struct isis_lsp *isis_root_system_lsp(struct isis_area *area, int level, memcpy(lspid, sysid, ISIS_SYS_ID_LEN); LSP_PSEUDO_ID(lspid) = 0; LSP_FRAGMENT(lspid) = 0; - lsp = lsp_search(&area->lspdb[level - 1], lspid); + lsp = lsp_search(lspdb, lspid); if (lsp && lsp->hdr.rem_lifetime != 0) return lsp; return NULL; @@ -321,28 +407,21 @@ static struct isis_lsp *isis_root_system_lsp(struct isis_area *area, int level, /* * Add this IS to the root of SPT */ -static struct isis_vertex *isis_spf_add_root(struct isis_spftree *spftree, - uint8_t *sysid) +static struct isis_vertex *isis_spf_add_root(struct isis_spftree *spftree) { struct isis_vertex *vertex; - struct isis_lsp *lsp; #ifdef EXTREME_DEBUG char buff[VID2STR_BUFFER]; #endif /* EXTREME_DEBUG */ - lsp = isis_root_system_lsp(spftree->area, spftree->level, sysid); - if (lsp == NULL) - zlog_warn("ISIS-Spf: could not find own l%d LSP!", - spftree->level); - - vertex = isis_vertex_new(spftree, sysid, + vertex = isis_vertex_new(spftree, spftree->sysid, spftree->area->oldmetric ? VTYPE_NONPSEUDO_IS : VTYPE_NONPSEUDO_TE_IS); isis_vertex_queue_append(&spftree->paths, vertex); #ifdef EXTREME_DEBUG - zlog_debug("ISIS-Spf: added this IS %s %s depth %d dist %d to PATHS", + zlog_debug("ISIS-Spf: added this IS %s %s depth %d dist %d to PATHS", vtype2string(vertex->type), vid2string(vertex, buff, sizeof(buff)), vertex->depth, vertex->d_N); @@ -377,18 +456,30 @@ static void vertex_update_firsthops(struct isis_vertex *vertex, static struct isis_vertex *isis_spf_add2tent(struct isis_spftree *spftree, enum vertextype vtype, void *id, uint32_t cost, int depth, - struct isis_adjacency *adj, + struct isis_spf_adj *sadj, struct isis_vertex *parent) { struct isis_vertex *vertex; struct listnode *node; - struct isis_adjacency *parent_adj; -#ifdef EXTREME_DEBUG char buff[VID2STR_BUFFER]; -#endif - assert(isis_find_vertex(&spftree->paths, id, vtype) == NULL); - assert(isis_find_vertex(&spftree->tents, id, vtype) == NULL); + vertex = isis_find_vertex(&spftree->paths, id, vtype); + if (vertex != NULL) { + zlog_err( + "%s: vertex %s of type %s already in PATH; check for sysId collisions with established neighbors", + __func__, vid2string(vertex, buff, sizeof(buff)), + vtype2string(vertex->type)); + return NULL; + } + vertex = isis_find_vertex(&spftree->tents, id, vtype); + if (vertex != NULL) { + zlog_err( + "%s: vertex %s of type %s already in TENT; check for sysId collisions with established neighbors", + __func__, vid2string(vertex, buff, sizeof(buff)), + vtype2string(vertex->type)); + return NULL; + } + vertex = isis_vertex_new(spftree, id, vtype); vertex->d_N = cost; vertex->depth = depth; @@ -397,14 +488,16 @@ static struct isis_vertex *isis_spf_add2tent(struct isis_spftree *spftree, listnode_add(vertex->parents, parent); } - if (spftree->hopcount_metric) + if (CHECK_FLAG(spftree->flags, F_SPFTREE_HOPCOUNT_METRIC)) vertex_update_firsthops(vertex, parent); if (parent && parent->Adj_N && listcount(parent->Adj_N) > 0) { - for (ALL_LIST_ELEMENTS_RO(parent->Adj_N, node, parent_adj)) - listnode_add(vertex->Adj_N, parent_adj); - } else if (adj) { - listnode_add(vertex->Adj_N, adj); + struct isis_vertex_adj *parent_vadj; + + for (ALL_LIST_ELEMENTS_RO(parent->Adj_N, node, parent_vadj)) + isis_vertex_adj_add(vertex, parent_vadj->sadj); + } else if (sadj) { + isis_vertex_adj_add(vertex, sadj); } #ifdef EXTREME_DEBUG @@ -421,7 +514,7 @@ static struct isis_vertex *isis_spf_add2tent(struct isis_spftree *spftree, static void isis_spf_add_local(struct isis_spftree *spftree, enum vertextype vtype, void *id, - struct isis_adjacency *adj, uint32_t cost, + struct isis_spf_adj *sadj, uint32_t cost, struct isis_vertex *parent) { struct isis_vertex *vertex; @@ -431,10 +524,12 @@ static void isis_spf_add_local(struct isis_spftree *spftree, if (vertex) { /* C.2.5 c) */ if (vertex->d_N == cost) { - if (adj) - listnode_add(vertex->Adj_N, adj); + if (sadj) + isis_vertex_adj_add(vertex, sadj); /* d) */ - if (listcount(vertex->Adj_N) > ISIS_MAX_PATH_SPLITS) + if (!CHECK_FLAG(spftree->flags, + F_SPFTREE_NO_ADJACENCIES) + && listcount(vertex->Adj_N) > ISIS_MAX_PATH_SPLITS) remove_excess_adjs(vertex->Adj_N); if (parent && (listnode_lookup(vertex->parents, parent) == NULL)) @@ -450,7 +545,7 @@ static void isis_spf_add_local(struct isis_spftree *spftree, } } - isis_spf_add2tent(spftree, vtype, id, cost, 1, adj, parent); + isis_spf_add2tent(spftree, vtype, id, cost, 1, sadj, parent); return; } @@ -465,7 +560,7 @@ static void process_N(struct isis_spftree *spftree, enum vertextype vtype, assert(spftree && parent); - if (spftree->hopcount_metric + if (CHECK_FLAG(spftree->flags, F_SPFTREE_HOPCOUNT_METRIC) && !VTYPE_IS(vtype)) return; @@ -515,16 +610,20 @@ static void process_N(struct isis_spftree *spftree, enum vertextype vtype, #endif /* EXTREME_DEBUG */ if (vertex->d_N == dist) { struct listnode *node; - struct isis_adjacency *parent_adj; + struct isis_vertex_adj *parent_vadj; for (ALL_LIST_ELEMENTS_RO(parent->Adj_N, node, - parent_adj)) - if (listnode_lookup(vertex->Adj_N, parent_adj) - == NULL) - listnode_add(vertex->Adj_N, parent_adj); - if (spftree->hopcount_metric) + parent_vadj)) + if (!isis_vertex_adj_exists(spftree, vertex, + parent_vadj->sadj)) + isis_vertex_adj_add(vertex, + parent_vadj->sadj); + if (CHECK_FLAG(spftree->flags, + F_SPFTREE_HOPCOUNT_METRIC)) vertex_update_firsthops(vertex, parent); /* 2) */ - if (listcount(vertex->Adj_N) > ISIS_MAX_PATH_SPLITS) + if (!CHECK_FLAG(spftree->flags, + F_SPFTREE_NO_ADJACENCIES) + && listcount(vertex->Adj_N) > ISIS_MAX_PATH_SPLITS) remove_excess_adjs(vertex->Adj_N); if (listnode_lookup(vertex->parents, parent) == NULL) listnode_add(vertex->parents, parent); @@ -607,7 +706,9 @@ static int isis_spf_process_lsp(struct isis_spftree *spftree, /* C.2.6 a) */ /* Two way connectivity */ - if (!memcmp(r->id, root_sysid, ISIS_SYS_ID_LEN)) + if (!LSP_PSEUDO_ID(r->id) + && !memcmp(r->id, root_sysid, + ISIS_SYS_ID_LEN)) continue; if (!pseudo_lsp && !memcmp(r->id, null_sysid, @@ -636,12 +737,19 @@ static int isis_spf_process_lsp(struct isis_spftree *spftree, te_neighs->head : NULL; er; er = er->next) { - if (!memcmp(er->id, root_sysid, ISIS_SYS_ID_LEN)) + /* C.2.6 a) */ + /* Two way connectivity */ + if (!LSP_PSEUDO_ID(er->id) + && !memcmp(er->id, root_sysid, ISIS_SYS_ID_LEN)) continue; if (!pseudo_lsp && !memcmp(er->id, null_sysid, ISIS_SYS_ID_LEN)) continue; - dist = cost + (spftree->hopcount_metric ? 1 : er->metric); + dist = cost + + (CHECK_FLAG(spftree->flags, + F_SPFTREE_HOPCOUNT_METRIC) + ? 1 + : er->metric); process_N(spftree, LSP_PSEUDO_ID(er->id) ? VTYPE_PSEUDO_TE_IS : VTYPE_NONPSEUDO_TE_IS, @@ -755,239 +863,311 @@ static int isis_spf_process_lsp(struct isis_spftree *spftree, return ISIS_OK; } -static int isis_spf_preload_tent(struct isis_spftree *spftree, - uint8_t *root_sysid, - struct isis_vertex *parent) +static struct isis_adjacency *adj_find(struct list *adj_list, const uint8_t *id, + int level, uint16_t mtid, int family) { - struct isis_circuit *circuit; - struct listnode *cnode, *anode, *ipnode; struct isis_adjacency *adj; - struct isis_lsp *lsp; - struct list *adj_list; - struct list *adjdb; - struct prefix_ipv4 *ipv4; - struct prefix_pair ip_info; - int retval = ISIS_OK; - uint8_t lsp_id[ISIS_SYS_ID_LEN + 2]; - static uint8_t null_lsp_id[ISIS_SYS_ID_LEN + 2]; - struct prefix_ipv6 *ipv6; - struct isis_circuit_mt_setting *circuit_mt; - - for (ALL_LIST_ELEMENTS_RO(spftree->area->circuit_list, cnode, - circuit)) { - circuit_mt = circuit_lookup_mt_setting(circuit, spftree->mtid); - if (circuit_mt && !circuit_mt->enabled) + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(adj_list, node, adj)) { + if (!(adj->level & level)) continue; - if (circuit->state != C_STATE_UP) + if (memcmp(adj->sysid, id, ISIS_SYS_ID_LEN) != 0) continue; - if (!(circuit->is_type & spftree->level)) + if (adj->adj_state != ISIS_ADJ_UP) continue; - if (spftree->family == AF_INET && !circuit->ip_router) + if (!adj_has_mt(adj, mtid)) continue; - if (spftree->family == AF_INET6 && !circuit->ipv6_router) + if (mtid == ISIS_MT_IPV4_UNICAST + && !speaks(adj->nlpids.nlpids, adj->nlpids.count, family)) continue; - /* - * Add IP(v6) addresses of this circuit - */ - if (spftree->family == AF_INET && !spftree->hopcount_metric) { - memset(&ip_info, 0, sizeof(ip_info)); - ip_info.dest.family = AF_INET; - for (ALL_LIST_ELEMENTS_RO(circuit->ip_addrs, ipnode, - ipv4)) { - ip_info.dest.u.prefix4 = ipv4->prefix; - ip_info.dest.prefixlen = ipv4->prefixlen; - apply_mask(&ip_info.dest); - isis_spf_add_local(spftree, - VTYPE_IPREACH_INTERNAL, - &ip_info, NULL, 0, parent); - } + return adj; + } + + return NULL; +} + +struct spf_preload_tent_ip_reach_args { + struct isis_spftree *spftree; + struct isis_vertex *parent; +}; + +static int isis_spf_preload_tent_ip_reach_cb(const struct prefix *prefix, + uint32_t metric, bool external, + struct isis_subtlvs *subtlvs, + void *arg) +{ + struct spf_preload_tent_ip_reach_args *args = arg; + struct isis_spftree *spftree = args->spftree; + struct isis_vertex *parent = args->parent; + struct prefix_pair ip_info; + enum vertextype vtype; + + if (external) + return LSP_ITER_CONTINUE; + + assert(spftree->family == prefix->family); + memset(&ip_info, 0, sizeof(ip_info)); + prefix_copy(&ip_info.dest, prefix); + apply_mask(&ip_info.dest); + + if (prefix->family == AF_INET) + vtype = VTYPE_IPREACH_INTERNAL; + else + vtype = VTYPE_IP6REACH_INTERNAL; + + isis_spf_add_local(spftree, vtype, &ip_info, NULL, 0, parent); + + return LSP_ITER_CONTINUE; +} + +static void isis_spf_preload_tent(struct isis_spftree *spftree, + uint8_t *root_sysid, + struct isis_lsp *root_lsp, + struct isis_vertex *parent) +{ + struct spf_preload_tent_ip_reach_args ip_reach_args; + struct isis_spf_adj *sadj; + struct listnode *node; + + if (!CHECK_FLAG(spftree->flags, F_SPFTREE_HOPCOUNT_METRIC)) { + ip_reach_args.spftree = spftree; + ip_reach_args.parent = parent; + isis_lsp_iterate_ip_reach( + root_lsp, spftree->family, spftree->mtid, + isis_spf_preload_tent_ip_reach_cb, &ip_reach_args); + } + + /* Iterate over adjacencies. */ + for (ALL_LIST_ELEMENTS_RO(spftree->sadj_list, node, sadj)) { + uint32_t metric; + + metric = CHECK_FLAG(spftree->flags, F_SPFTREE_HOPCOUNT_METRIC) + ? 1 + : sadj->metric; + if (!LSP_PSEUDO_ID(sadj->id)) { + isis_spf_add_local(spftree, + CHECK_FLAG(sadj->flags, + F_ISIS_SPF_ADJ_OLDMETRIC) + ? VTYPE_NONPSEUDO_IS + : VTYPE_NONPSEUDO_TE_IS, + sadj->id, sadj, metric, parent); + } else if (sadj->lan.lsp_pseudo) { + isis_spf_process_lsp(spftree, sadj->lan.lsp_pseudo, + metric, 0, spftree->sysid, parent); } - if (spftree->family == AF_INET6 && !spftree->hopcount_metric) { - memset(&ip_info, 0, sizeof(ip_info)); - ip_info.dest.family = AF_INET6; - for (ALL_LIST_ELEMENTS_RO(circuit->ipv6_non_link, - ipnode, ipv6)) { - ip_info.dest.u.prefix6 = ipv6->prefix; - ip_info.dest.prefixlen = ipv6->prefixlen; - apply_mask(&ip_info.dest); - isis_spf_add_local(spftree, - VTYPE_IP6REACH_INTERNAL, - &ip_info, NULL, 0, parent); - } + } +} + +struct spf_adj_find_reverse_metric_args { + const uint8_t *id_self; + uint32_t reverse_metric; +}; + +static int spf_adj_find_reverse_metric_cb(const uint8_t *id, uint32_t metric, + bool oldmetric, + struct isis_ext_subtlvs *subtlvs, + void *arg) +{ + struct spf_adj_find_reverse_metric_args *args = arg; + + if (memcmp(id, args->id_self, ISIS_SYS_ID_LEN)) + return LSP_ITER_CONTINUE; + + args->reverse_metric = metric; + + return LSP_ITER_STOP; +} + +/* + * Change all SPF adjacencies to use the link cost in the direction from the + * next hop back towards root in place of the link cost in the direction away + * from root towards the next hop. + */ +static void spf_adj_get_reverse_metrics(struct isis_spftree *spftree) +{ + struct isis_spf_adj *sadj; + struct listnode *node, *nnode; + + for (ALL_LIST_ELEMENTS(spftree->sadj_list, node, nnode, sadj)) { + uint8_t lspid[ISIS_SYS_ID_LEN + 2]; + struct isis_lsp *lsp_adj; + const uint8_t *id_self; + struct spf_adj_find_reverse_metric_args args; + + /* Skip pseudonodes. */ + if (LSP_PSEUDO_ID(sadj->id)) + continue; + + /* Find LSP of the corresponding adjacency. */ + memcpy(lspid, sadj->id, ISIS_SYS_ID_LEN); + LSP_PSEUDO_ID(lspid) = 0; + LSP_FRAGMENT(lspid) = 0; + lsp_adj = lsp_search(spftree->lspdb, lspid); + if (lsp_adj == NULL || lsp_adj->hdr.rem_lifetime == 0) { + /* Delete one-way adjacency. */ + listnode_delete(spftree->sadj_list, sadj); + continue; } - if (circuit->circ_type == CIRCUIT_T_BROADCAST) { - /* - * Add the adjacencies - */ - adj_list = list_new(); - adjdb = circuit->u.bc.adjdb[spftree->level - 1]; - isis_adj_build_up_list(adjdb, adj_list); - if (listcount(adj_list) == 0) { - list_delete(&adj_list); - if (isis->debugs & DEBUG_SPF_EVENTS) - zlog_debug( - "ISIS-Spf: no L%d adjacencies on circuit %s", - spftree->level, - circuit->interface->name); - continue; - } - for (ALL_LIST_ELEMENTS_RO(adj_list, anode, adj)) { - if (!adj_has_mt(adj, spftree->mtid)) - continue; - if (spftree->mtid == ISIS_MT_IPV4_UNICAST - && !speaks(adj->nlpids.nlpids, - adj->nlpids.count, - spftree->family)) - continue; - switch (adj->sys_type) { - case ISIS_SYSTYPE_ES: - memcpy(lsp_id, adj->sysid, - ISIS_SYS_ID_LEN); - LSP_PSEUDO_ID(lsp_id) = 0; - isis_spf_add_local( - spftree, VTYPE_ES, lsp_id, adj, - spftree->hopcount_metric ? 1 : - circuit->te_metric - [spftree->level - 1], - parent); - break; - case ISIS_SYSTYPE_IS: - case ISIS_SYSTYPE_L1_IS: - case ISIS_SYSTYPE_L2_IS: - memcpy(lsp_id, adj->sysid, - ISIS_SYS_ID_LEN); - LSP_PSEUDO_ID(lsp_id) = 0; - LSP_FRAGMENT(lsp_id) = 0; - isis_spf_add_local( - spftree, - spftree->area->oldmetric - ? VTYPE_NONPSEUDO_IS - : VTYPE_NONPSEUDO_TE_IS, - lsp_id, adj, - spftree->hopcount_metric ? 1 : - circuit->te_metric - [spftree->level - 1], - parent); - lsp = lsp_search( - &spftree->area->lspdb[spftree->level- 1], - lsp_id); - if (lsp == NULL - || lsp->hdr.rem_lifetime == 0) - zlog_warn( - "ISIS-Spf: No LSP %s found for IS adjacency " - "L%d on %s (ID %u)", - rawlspid_print(lsp_id), - spftree->level, - circuit->interface->name, - circuit->circuit_id); - break; - case ISIS_SYSTYPE_UNKNOWN: - default: - zlog_warn( - "isis_spf_preload_tent unknown adj type"); - } - } - list_delete(&adj_list); - /* - * Add the pseudonode - */ - if (spftree->level == 1) - memcpy(lsp_id, circuit->u.bc.l1_desig_is, - ISIS_SYS_ID_LEN + 1); - else - memcpy(lsp_id, circuit->u.bc.l2_desig_is, - ISIS_SYS_ID_LEN + 1); - /* can happen during DR reboot */ - if (memcmp(lsp_id, null_lsp_id, ISIS_SYS_ID_LEN + 1) - == 0) { - if (isis->debugs & DEBUG_SPF_EVENTS) - zlog_debug( - "ISIS-Spf: No L%d DR on %s (ID %d)", - spftree->level, - circuit->interface->name, - circuit->circuit_id); - continue; - } - adj = isis_adj_lookup(lsp_id, adjdb); - /* if no adj, we are the dis or error */ - if (!adj && !circuit->u.bc.is_dr[spftree->level - 1]) { - zlog_warn( - "ISIS-Spf: No adjacency found from root " - "to L%d DR %s on %s (ID %d)", - spftree->level, rawlspid_print(lsp_id), - circuit->interface->name, - circuit->circuit_id); - continue; - } - lsp = lsp_search( - &spftree->area->lspdb[spftree->level - 1], - lsp_id); - if (lsp == NULL || lsp->hdr.rem_lifetime == 0) { - zlog_warn( - "ISIS-Spf: No lsp (%p) found from root " - "to L%d DR %s on %s (ID %d)", - (void *)lsp, spftree->level, - rawlspid_print(lsp_id), - circuit->interface->name, - circuit->circuit_id); - continue; + + /* Find root node in the LSP of the adjacent router. */ + if (CHECK_FLAG(sadj->flags, F_ISIS_SPF_ADJ_BROADCAST)) + id_self = sadj->lan.desig_is_id; + else + id_self = spftree->sysid; + args.id_self = id_self; + args.reverse_metric = UINT32_MAX; + isis_lsp_iterate_is_reach(lsp_adj, spftree->mtid, + spf_adj_find_reverse_metric_cb, + &args); + if (args.reverse_metric == UINT32_MAX) { + /* Delete one-way adjacency. */ + listnode_delete(spftree->sadj_list, sadj); + continue; + } + sadj->metric = args.reverse_metric; + } +} + +static void spf_adj_list_parse_tlv(struct isis_spftree *spftree, + struct list *adj_list, const uint8_t *id, + const uint8_t *desig_is_id, + uint32_t pseudo_metric, uint32_t metric, + bool oldmetric, + struct isis_ext_subtlvs *subtlvs) +{ + struct isis_spf_adj *sadj; + uint8_t flags = 0; + + /* Skip self in the pseudonode. */ + if (desig_is_id && !memcmp(id, spftree->sysid, ISIS_SYS_ID_LEN)) + return; + + sadj = XCALLOC(MTYPE_ISIS_SPF_ADJ, sizeof(*sadj)); + memcpy(sadj->id, id, sizeof(sadj->id)); + if (desig_is_id) { + memcpy(sadj->lan.desig_is_id, desig_is_id, + sizeof(sadj->lan.desig_is_id)); + SET_FLAG(flags, F_ISIS_SPF_ADJ_BROADCAST); + sadj->metric = pseudo_metric; + } else + sadj->metric = metric; + if (oldmetric) + SET_FLAG(flags, F_ISIS_SPF_ADJ_OLDMETRIC); + sadj->subtlvs = subtlvs; + sadj->flags = flags; + + /* Set real adjacency. */ + if (!CHECK_FLAG(spftree->flags, F_SPFTREE_NO_ADJACENCIES) + && !LSP_PSEUDO_ID(id)) { + struct isis_adjacency *adj; + + adj = adj_find(adj_list, id, spftree->level, spftree->mtid, + spftree->family); + if (!adj) { + XFREE(MTYPE_ISIS_SPF_ADJ, sadj); + return; + } + + listnode_delete(adj_list, adj); + sadj->adj = adj; + } + + /* Add adjacency to the list. */ + listnode_add(spftree->sadj_list, sadj); + + /* Parse pseudonode LSP too. */ + if (LSP_PSEUDO_ID(id)) { + uint8_t lspid[ISIS_SYS_ID_LEN + 2]; + struct isis_lsp *lsp_pseudo; + + memcpy(lspid, id, ISIS_SYS_ID_LEN + 1); + LSP_FRAGMENT(lspid) = 0; + lsp_pseudo = lsp_search(spftree->lspdb, lspid); + if (lsp_pseudo == NULL || lsp_pseudo->hdr.rem_lifetime == 0) { + zlog_warn( + "ISIS-Spf: No LSP found from root to L%d DR %s", + spftree->level, rawlspid_print(id)); + return; + } + + sadj->lan.lsp_pseudo = lsp_pseudo; + spf_adj_list_parse_lsp(spftree, adj_list, lsp_pseudo, id, + metric); + } +} + +static void spf_adj_list_parse_lsp(struct isis_spftree *spftree, + struct list *adj_list, struct isis_lsp *lsp, + const uint8_t *pseudo_nodeid, + uint32_t pseudo_metric) +{ + bool pseudo_lsp = LSP_PSEUDO_ID(lsp->hdr.lsp_id); + struct isis_lsp *frag; + struct listnode *node; + struct isis_item *head; + struct isis_item_list *te_neighs; + + if (lsp->hdr.seqno == 0 || lsp->hdr.rem_lifetime == 0) + return; + + /* Parse main LSP. */ + if (lsp->tlvs) { + if (pseudo_lsp || spftree->mtid == ISIS_MT_IPV4_UNICAST) { + head = lsp->tlvs->oldstyle_reach.head; + for (struct isis_oldstyle_reach *reach = + (struct isis_oldstyle_reach *)head; + reach; reach = reach->next) { + spf_adj_list_parse_tlv( + spftree, adj_list, reach->id, + pseudo_nodeid, pseudo_metric, + reach->metric, true, NULL); } - isis_spf_process_lsp(spftree, lsp, - spftree->hopcount_metric ? - 1 : circuit->te_metric[spftree->level - 1], - 0, root_sysid, parent); - } else if (circuit->circ_type == CIRCUIT_T_P2P) { - adj = circuit->u.p2p.neighbor; - if (!adj || adj->adj_state != ISIS_ADJ_UP) - continue; - if (!adj_has_mt(adj, spftree->mtid)) - continue; - switch (adj->sys_type) { - case ISIS_SYSTYPE_ES: - memcpy(lsp_id, adj->sysid, ISIS_SYS_ID_LEN); - LSP_PSEUDO_ID(lsp_id) = 0; - isis_spf_add_local( - spftree, VTYPE_ES, lsp_id, adj, - spftree->hopcount_metric ? 1 : - circuit->te_metric[spftree->level - 1], - parent); - break; - case ISIS_SYSTYPE_IS: - case ISIS_SYSTYPE_L1_IS: - case ISIS_SYSTYPE_L2_IS: - memcpy(lsp_id, adj->sysid, ISIS_SYS_ID_LEN); - LSP_PSEUDO_ID(lsp_id) = 0; - LSP_FRAGMENT(lsp_id) = 0; - if (spftree->mtid != ISIS_MT_IPV4_UNICAST - || speaks(adj->nlpids.nlpids, - adj->nlpids.count, - spftree->family)) - isis_spf_add_local( - spftree, - spftree->area->oldmetric - ? VTYPE_NONPSEUDO_IS - : VTYPE_NONPSEUDO_TE_IS, - lsp_id, adj, - spftree->hopcount_metric ? 1 : - circuit->te_metric - [spftree->level - 1], - parent); - break; - case ISIS_SYSTYPE_UNKNOWN: - default: - zlog_warn( - "isis_spf_preload_tent unknown adj type"); - break; + } + + if (pseudo_lsp || spftree->mtid == ISIS_MT_IPV4_UNICAST) + te_neighs = &lsp->tlvs->extended_reach; + else + te_neighs = isis_get_mt_items(&lsp->tlvs->mt_reach, + spftree->mtid); + if (te_neighs) { + head = te_neighs->head; + for (struct isis_extended_reach *reach = + (struct isis_extended_reach *)head; + reach; reach = reach->next) { + spf_adj_list_parse_tlv( + spftree, adj_list, reach->id, + pseudo_nodeid, pseudo_metric, + reach->metric, false, reach->subtlvs); } - } else if (circuit->circ_type == CIRCUIT_T_LOOPBACK) { - continue; - } else { - zlog_warn("isis_spf_preload_tent unsupported media"); - retval = ISIS_WARNING; } } - return retval; + /* Parse LSP fragments. */ + for (ALL_LIST_ELEMENTS_RO(lsp->lspu.frags, node, frag)) { + if (!frag->tlvs) + continue; + + spf_adj_list_parse_lsp(spftree, adj_list, frag, pseudo_nodeid, + pseudo_metric); + } +} + +static void isis_spf_build_adj_list(struct isis_spftree *spftree, + struct isis_lsp *lsp) +{ + struct list *adj_list = NULL; + + if (!CHECK_FLAG(spftree->flags, F_SPFTREE_NO_ADJACENCIES)) + adj_list = list_dup(spftree->area->adjacency_list); + + spf_adj_list_parse_lsp(spftree, adj_list, lsp, NULL, 0); + + if (!CHECK_FLAG(spftree->flags, F_SPFTREE_NO_ADJACENCIES)) + list_delete(&adj_list); + + if (spftree->type == SPF_TYPE_REVERSE) + spf_adj_get_reverse_metrics(spftree); } /* @@ -1010,17 +1190,16 @@ static void add_to_paths(struct isis_spftree *spftree, vertex->d_N); #endif /* EXTREME_DEBUG */ - if (VTYPE_IP(vertex->type)) { + if (VTYPE_IP(vertex->type) + && !CHECK_FLAG(spftree->flags, F_SPFTREE_NO_ROUTES)) { if (listcount(vertex->Adj_N) > 0) - isis_route_create(&vertex->N.ip.dest, - &vertex->N.ip.src, + isis_route_create(&vertex->N.ip.dest, &vertex->N.ip.src, vertex->d_N, vertex->depth, vertex->Adj_N, spftree->area, spftree->route_table); - else if (isis->debugs & DEBUG_SPF_EVENTS) + else if (IS_DEBUG_SPF_EVENTS) zlog_debug( - "ISIS-Spf: no adjacencies do not install route for " - "%s depth %d dist %d", + "ISIS-Spf: no adjacencies do not install route for %s depth %d dist %d", vid2string(vertex, buff, sizeof(buff)), vertex->depth, vertex->d_N); } @@ -1028,18 +1207,14 @@ static void add_to_paths(struct isis_spftree *spftree, return; } -static void init_spt(struct isis_spftree *spftree, int mtid, int level, - int family, enum spf_tree_id tree_id, - bool hopcount_metric) +static void init_spt(struct isis_spftree *spftree, int mtid) { + /* Clear data from previous run. */ + list_delete_all_node(spftree->sadj_list); isis_vertex_queue_clear(&spftree->tents); isis_vertex_queue_clear(&spftree->paths); spftree->mtid = mtid; - spftree->level = level; - spftree->family = family; - spftree->tree_id = tree_id; - spftree->hopcount_metric = hopcount_metric; } static void isis_spf_loop(struct isis_spftree *spftree, @@ -1080,18 +1255,31 @@ struct isis_spftree *isis_run_hopcount_spf(struct isis_area *area, struct isis_spftree *spftree) { if (!spftree) - spftree = isis_spftree_new(area); - - init_spt(spftree, ISIS_MT_IPV4_UNICAST, ISIS_LEVEL2, - AF_INET, SPFTREE_IPV4, true); - if (!memcmp(sysid, isis->sysid, ISIS_SYS_ID_LEN)) { - /* If we are running locally, initialize with information from adjacencies */ - struct isis_vertex *root = isis_spf_add_root(spftree, sysid); - isis_spf_preload_tent(spftree, sysid, root); + spftree = isis_spftree_new(area, &area->lspdb[IS_LEVEL_2 - 1], + sysid, ISIS_LEVEL2, SPFTREE_IPV4, + SPF_TYPE_FORWARD, + F_SPFTREE_HOPCOUNT_METRIC); + + init_spt(spftree, ISIS_MT_IPV4_UNICAST); + if (!memcmp(sysid, area->isis->sysid, ISIS_SYS_ID_LEN)) { + struct isis_lsp *root_lsp; + struct isis_vertex *root_vertex; + + root_lsp = isis_root_system_lsp(spftree->lspdb, spftree->sysid); + if (root_lsp) { + /* + * If we are running locally, initialize with + * information from adjacencies + */ + root_vertex = isis_spf_add_root(spftree); + + isis_spf_preload_tent(spftree, sysid, root_lsp, + root_vertex); + } } else { - isis_vertex_queue_insert(&spftree->tents, isis_vertex_new( - spftree, sysid, - VTYPE_NONPSEUDO_TE_IS)); + isis_vertex_queue_insert( + &spftree->tents, + isis_vertex_new(spftree, sysid, VTYPE_NONPSEUDO_TE_IS)); } isis_spf_loop(spftree, sysid); @@ -1099,76 +1287,73 @@ struct isis_spftree *isis_run_hopcount_spf(struct isis_area *area, return spftree; } -static int isis_run_spf(struct isis_area *area, int level, - enum spf_tree_id tree_id, - uint8_t *sysid, struct timeval *nowtv) +void isis_run_spf(struct isis_spftree *spftree) { - int retval = ISIS_OK; + struct isis_lsp *root_lsp; struct isis_vertex *root_vertex; - struct isis_spftree *spftree = area->spftree[tree_id][level - 1]; - struct timeval time_now; - unsigned long long start_time, end_time; + struct timeval time_start; + struct timeval time_end; + struct isis_mt_router_info *mt_router_info; uint16_t mtid = 0; /* Get time that can't roll backwards. */ - start_time = nowtv->tv_sec; - start_time = (start_time * 1000000) + nowtv->tv_usec; + monotime(&time_start); - int family = -1; - switch (tree_id) { + root_lsp = isis_root_system_lsp(spftree->lspdb, spftree->sysid); + if (root_lsp == NULL) { + zlog_err("ISIS-Spf: could not find own l%d LSP!", + spftree->level); + return; + } + + /* Get Multi-Topology ID. */ + switch (spftree->tree_id) { case SPFTREE_IPV4: - family = AF_INET; mtid = ISIS_MT_IPV4_UNICAST; break; case SPFTREE_IPV6: - family = AF_INET6; - mtid = isis_area_ipv6_topology(area); + mt_router_info = isis_tlvs_lookup_mt_router_info( + root_lsp->tlvs, ISIS_MT_IPV6_UNICAST); + if (mt_router_info) + mtid = ISIS_MT_IPV6_UNICAST; + else + mtid = ISIS_MT_IPV4_UNICAST; break; case SPFTREE_DSTSRC: - family = AF_INET6; mtid = ISIS_MT_IPV6_DSTSRC; break; case SPFTREE_COUNT: - assert(!"isis_run_spf should never be called with SPFTREE_COUNT as argument!"); - return ISIS_WARNING; + zlog_err( + "isis_run_spf should never be called with SPFTREE_COUNT as argument!"); + exit(1); } - assert(spftree); - assert(sysid); - /* * C.2.5 Step 0 */ - init_spt(spftree, mtid, level, family, tree_id, false); + init_spt(spftree, mtid); /* a) */ - root_vertex = isis_spf_add_root(spftree, sysid); + root_vertex = isis_spf_add_root(spftree); /* b) */ - retval = isis_spf_preload_tent(spftree, sysid, root_vertex); - if (retval != ISIS_OK) { - zlog_warn("ISIS-Spf: failed to load TENT SPF-root:%s", - print_sys_hostname(sysid)); - goto out; - } + isis_spf_build_adj_list(spftree, root_lsp); + isis_spf_preload_tent(spftree, spftree->sysid, root_lsp, root_vertex); /* * C.2.7 Step 2 */ if (!isis_vertex_queue_count(&spftree->tents) - && (isis->debugs & DEBUG_SPF_EVENTS)) { + && (IS_DEBUG_SPF_EVENTS)) { zlog_warn("ISIS-Spf: TENT is empty SPF-root:%s", - print_sys_hostname(sysid)); + print_sys_hostname(spftree->sysid)); } - isis_spf_loop(spftree, sysid); -out: + isis_spf_loop(spftree, spftree->sysid); spftree->runcount++; spftree->last_run_timestamp = time(NULL); - spftree->last_run_monotime = monotime(&time_now); - end_time = time_now.tv_sec; - end_time = (end_time * 1000000) + time_now.tv_usec; - spftree->last_run_duration = end_time - start_time; - - return retval; + spftree->last_run_monotime = monotime(&time_end); + spftree->last_run_duration = + ((time_end.tv_sec - time_start.tv_sec) * 1000000) + + (time_end.tv_usec - time_start.tv_usec); } void isis_spf_verify_routes(struct isis_area *area, struct isis_spftree **trees) @@ -1192,14 +1377,14 @@ static int isis_run_spf_cb(struct thread *thread) { struct isis_spf_run *run = THREAD_ARG(thread); struct isis_area *area = run->area; + struct isis_spftree *spftree; int level = run->level; - int retval = ISIS_OK; XFREE(MTYPE_ISIS_SPF_RUN, run); area->spf_timer[level - 1] = NULL; if (!(area->is_type & level)) { - if (isis->debugs & DEBUG_SPF_EVENTS) + if (IS_DEBUG_SPF_EVENTS) zlog_warn("ISIS-SPF (%s) area does not share level", area->area_tag); return ISIS_WARNING; @@ -1207,23 +1392,30 @@ static int isis_run_spf_cb(struct thread *thread) isis_area_invalidate_routes(area, level); - if (isis->debugs & DEBUG_SPF_EVENTS) + if (IS_DEBUG_SPF_EVENTS) zlog_debug("ISIS-Spf (%s) L%d SPF needed, periodic SPF", area->area_tag, level); - if (area->ip_circuits) - retval = isis_run_spf(area, level, SPFTREE_IPV4, isis->sysid, - &thread->real); - if (area->ipv6_circuits) - retval = isis_run_spf(area, level, SPFTREE_IPV6, isis->sysid, - &thread->real); - if (area->ipv6_circuits - && isis_area_ipv6_dstsrc_enabled(area)) - retval = isis_run_spf(area, level, SPFTREE_DSTSRC, isis->sysid, - &thread->real); + if (area->ip_circuits) { + spftree = area->spftree[SPFTREE_IPV4][level - 1]; + memcpy(spftree->sysid, area->isis->sysid, ISIS_SYS_ID_LEN); + isis_run_spf(spftree); + } + if (area->ipv6_circuits) { + spftree = area->spftree[SPFTREE_IPV6][level - 1]; + memcpy(spftree->sysid, area->isis->sysid, ISIS_SYS_ID_LEN); + isis_run_spf(spftree); + } + if (area->ipv6_circuits && isis_area_ipv6_dstsrc_enabled(area)) { + spftree = area->spftree[SPFTREE_DSTSRC][level - 1]; + memcpy(spftree->sysid, area->isis->sysid, ISIS_SYS_ID_LEN); + isis_run_spf(spftree); + } isis_area_verify_routes(area); + isis_area_verify_sr(area); + /* walk all circuits and reset any spf specific flags */ struct listnode *node; struct isis_circuit *circuit; @@ -1232,7 +1424,7 @@ static int isis_run_spf_cb(struct thread *thread) fabricd_run_spf(area); - return retval; + return 0; } static struct isis_spf_run *isis_run_spf_arg(struct isis_area *area, int level) @@ -1245,6 +1437,11 @@ static struct isis_spf_run *isis_run_spf_arg(struct isis_area *area, int level) return run; } +void isis_spf_timer_free(void *run) +{ + XFREE(MTYPE_ISIS_SPF_RUN, run); +} + int _isis_spf_schedule(struct isis_area *area, int level, const char *func, const char *file, int line) { @@ -1252,13 +1449,15 @@ int _isis_spf_schedule(struct isis_area *area, int level, time_t now = monotime(NULL); int diff = now - spftree->last_run_monotime; + if (CHECK_FLAG(im->options, F_ISIS_UNIT_TEST)) + return 0; + assert(diff >= 0); assert(area->is_type & level); - if (isis->debugs & DEBUG_SPF_EVENTS) { + if (IS_DEBUG_SPF_EVENTS) { zlog_debug( - "ISIS-Spf (%s) L%d SPF schedule called, lastrun %d sec ago" - " Caller: %s %s:%d", + "ISIS-Spf (%s) L%d SPF schedule called, lastrun %d sec ago Caller: %s %s:%d", area->area_tag, level, diff, func, file, line); } @@ -1283,9 +1482,20 @@ int _isis_spf_schedule(struct isis_area *area, int level, /* wait configured min_spf_interval before doing the SPF */ long timer; - if (diff >= area->min_spf_interval[level - 1]) { - /* Last run is more than min interval ago, schedule immediate run */ + if (diff >= area->min_spf_interval[level - 1] + || area->bfd_force_spf_refresh) { + /* + * Last run is more than min interval ago or BFD signalled a + * 'down' message, schedule immediate run + */ timer = 0; + + if (area->bfd_force_spf_refresh) { + zlog_debug( + "ISIS-Spf (%s) L%d SPF scheduled immediately due to BFD 'down' message", + area->area_tag, level); + area->bfd_force_spf_refresh = false; + } } else { timer = area->min_spf_interval[level - 1] - diff; } @@ -1293,7 +1503,7 @@ int _isis_spf_schedule(struct isis_area *area, int level, thread_add_timer(master, isis_run_spf_cb, isis_run_spf_arg(area, level), timer, &area->spf_timer[level - 1]); - if (isis->debugs & DEBUG_SPF_EVENTS) + if (IS_DEBUG_SPF_EVENTS) zlog_debug("ISIS-Spf (%s) L%d SPF scheduled %ld sec from now", area->area_tag, level, timer); @@ -1311,7 +1521,8 @@ static void isis_print_paths(struct vty *vty, struct isis_vertex_queue *queue, "Vertex Type Metric Next-Hop Interface Parent\n"); for (ALL_QUEUE_ELEMENTS_RO(queue, node, vertex)) { - if (memcmp(vertex->N.id, root_sysid, ISIS_SYS_ID_LEN) == 0) { + if (VTYPE_IS(vertex->type) + && memcmp(vertex->N.id, root_sysid, ISIS_SYS_ID_LEN) == 0) { vty_out(vty, "%-20s %-12s %-6s", print_sys_hostname(root_sysid), "", ""); vty_out(vty, "%-30s\n", ""); @@ -1321,7 +1532,7 @@ static void isis_print_paths(struct vty *vty, struct isis_vertex_queue *queue, int rows = 0; struct listnode *anode = listhead(vertex->Adj_N); struct listnode *pnode = listhead(vertex->parents); - struct isis_adjacency *adj; + struct isis_vertex_adj *vadj; struct isis_vertex *pvertex; vty_out(vty, "%-20s %-12s %-6u ", @@ -1332,10 +1543,10 @@ static void isis_print_paths(struct vty *vty, struct isis_vertex_queue *queue, vertex->parents ? listcount(vertex->parents) : 0); i++) { if (anode) { - adj = listgetdata(anode); + vadj = listgetdata(anode); anode = anode->next; } else { - adj = NULL; + vadj = NULL; } if (pnode) { @@ -1350,14 +1561,18 @@ static void isis_print_paths(struct vty *vty, struct isis_vertex_queue *queue, vty_out(vty, "%-20s %-12s %-6s ", "", "", ""); } - if (adj) { + if (vadj) { + struct isis_spf_adj *sadj = vadj->sadj; + vty_out(vty, "%-20s %-9s ", - print_sys_hostname(adj->sysid), - adj->circuit->interface->name); + print_sys_hostname(sadj->id), + sadj->adj ? sadj->adj->circuit + ->interface->name + : "-"); } if (pvertex) { - if (!adj) + if (!vadj) vty_out(vty, "%-20s %-9s ", "", ""); vty_out(vty, "%s(%d)", @@ -1371,13 +1586,14 @@ static void isis_print_paths(struct vty *vty, struct isis_vertex_queue *queue, } } -static void isis_print_spftree(struct vty *vty, int level, - struct isis_area *area, - enum spf_tree_id tree_id) +void isis_print_spftree(struct vty *vty, struct isis_spftree *spftree) { const char *tree_id_text = NULL; - switch (tree_id) { + if (!spftree || !isis_vertex_queue_count(&spftree->paths)) + return; + + switch (spftree->tree_id) { case SPFTREE_IPV4: tree_id_text = "that speak IP"; break; @@ -1392,46 +1608,20 @@ static void isis_print_spftree(struct vty *vty, int level, return; } - if (!area->spftree[tree_id][level - 1] - || !isis_vertex_queue_count( - &area->spftree[tree_id][level - 1]->paths)) - return; - - vty_out(vty, "IS-IS paths to level-%d routers %s\n", - level, tree_id_text); - isis_print_paths(vty, &area->spftree[tree_id][level - 1]->paths, - isis->sysid); + vty_out(vty, "IS-IS paths to level-%d routers %s\n", spftree->level, + tree_id_text); + isis_print_paths(vty, &spftree->paths, spftree->sysid); vty_out(vty, "\n"); } -DEFUN (show_isis_topology, - show_isis_topology_cmd, - "show " PROTO_NAME " topology" -#ifndef FABRICD - " []" -#endif - , SHOW_STR - PROTO_HELP - "IS-IS paths to Intermediate Systems\n" -#ifndef FABRICD - "Paths to all level-1 routers in the area\n" - "Paths to all level-2 routers in the domain\n" -#endif - ) +static void show_isis_topology_common(struct vty *vty, int levels, + struct isis *isis) { - int levels; struct listnode *node; struct isis_area *area; - if (argc < 4) - levels = ISIS_LEVEL1 | ISIS_LEVEL2; - else if (strmatch(argv[3]->text, "level-1")) - levels = ISIS_LEVEL1; - else - levels = ISIS_LEVEL2; - if (!isis->area_list || isis->area_list->count == 0) - return CMD_SUCCESS; + return; for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) { vty_out(vty, "Area %s:\n", @@ -1442,16 +1632,19 @@ DEFUN (show_isis_topology, continue; if (area->ip_circuits > 0) { - isis_print_spftree(vty, level, area, - SPFTREE_IPV4); + isis_print_spftree( + vty, + area->spftree[SPFTREE_IPV4][level - 1]); } if (area->ipv6_circuits > 0) { - isis_print_spftree(vty, level, area, - SPFTREE_IPV6); + isis_print_spftree( + vty, + area->spftree[SPFTREE_IPV6][level - 1]); } if (isis_area_ipv6_dstsrc_enabled(area)) { - isis_print_spftree(vty, level, area, - SPFTREE_DSTSRC); + isis_print_spftree(vty, + area->spftree[SPFTREE_DSTSRC] + [level - 1]); } } @@ -1464,13 +1657,254 @@ DEFUN (show_isis_topology, vty_out(vty, "\n"); } +} + +DEFUN(show_isis_topology, show_isis_topology_cmd, + "show " PROTO_NAME + " [vrf ] topology" +#ifndef FABRICD + " []" +#endif + , + SHOW_STR PROTO_HELP VRF_CMD_HELP_STR + "All VRFs\n" + "IS-IS paths to Intermediate Systems\n" +#ifndef FABRICD + "Paths to all level-1 routers in the area\n" + "Paths to all level-2 routers in the domain\n" +#endif +) +{ + int levels = ISIS_LEVELS; + struct listnode *node; + struct isis *isis = NULL; + int idx = 0; + const char *vrf_name = VRF_DEFAULT_NAME; + bool all_vrf = false; + int idx_vrf = 0; + + if (argv_find(argv, argc, "topology", &idx)) { + if (argc < idx + 2) + levels = ISIS_LEVEL1 | ISIS_LEVEL2; + else if (strmatch(argv[idx + 1]->arg, "level-1")) + levels = ISIS_LEVEL1; + else + levels = ISIS_LEVEL2; + } + + if (!im) { + vty_out(vty, "IS-IS Routing Process not enabled\n"); + return CMD_SUCCESS; + } + ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + + if (vrf_name) { + if (all_vrf) { + for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) + show_isis_topology_common(vty, levels, isis); + return CMD_SUCCESS; + } + isis = isis_lookup_by_vrfname(vrf_name); + if (isis != NULL) + show_isis_topology_common(vty, levels, isis); + } return CMD_SUCCESS; } -void isis_spf_cmds_init(void) +void isis_print_routes(struct vty *vty, struct isis_spftree *spftree) +{ + struct ttable *tt; + struct route_node *rn; + const char *tree_id_text = NULL; + + if (!spftree) + return; + + switch (spftree->tree_id) { + case SPFTREE_IPV4: + tree_id_text = "IPv4"; + break; + case SPFTREE_IPV6: + tree_id_text = "IPv6"; + break; + case SPFTREE_DSTSRC: + tree_id_text = "IPv6 (dst-src routing)"; + break; + case SPFTREE_COUNT: + assert(!"isis_print_routes shouldn't be called with SPFTREE_COUNT as type"); + return; + } + + vty_out(vty, "IS-IS %s %s routing table:\n\n", + circuit_t2string(spftree->level), tree_id_text); + + /* Prepare table. */ + tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]); + ttable_add_row(tt, "Prefix|Metric|Interface|Nexthop|Label(s)"); + tt->style.cell.rpad = 2; + tt->style.corner = '+'; + ttable_restyle(tt); + ttable_rowseps(tt, 0, BOTTOM, true, '-'); + + for (rn = route_top(spftree->route_table); rn; rn = route_next(rn)) { + struct isis_route_info *rinfo; + struct isis_nexthop *nexthop; + struct listnode *node; + bool first = true; + char buf_prefix[BUFSIZ]; + + rinfo = rn->info; + if (!rinfo) + continue; + + (void)prefix2str(&rn->p, buf_prefix, sizeof(buf_prefix)); + for (ALL_LIST_ELEMENTS_RO(rinfo->nexthops, node, nexthop)) { + struct interface *ifp; + char buf_iface[BUFSIZ]; + char buf_nhop[BUFSIZ]; + char buf_labels[BUFSIZ] = {}; + + if (!CHECK_FLAG(spftree->flags, + F_SPFTREE_NO_ADJACENCIES)) { + inet_ntop(nexthop->family, &nexthop->ip, + buf_nhop, sizeof(buf_nhop)); + ifp = if_lookup_by_index(nexthop->ifindex, + VRF_DEFAULT); + if (ifp) + strlcpy(buf_iface, ifp->name, + sizeof(buf_iface)); + else + snprintf(buf_iface, sizeof(buf_iface), + "ifindex %u", + nexthop->ifindex); + } else { + strlcpy(buf_nhop, + print_sys_hostname(nexthop->sysid), + sizeof(buf_nhop)); + strlcpy(buf_iface, "-", sizeof(buf_iface)); + } + + if (nexthop->sr.label != MPLS_INVALID_LABEL) + label2str(nexthop->sr.label, buf_labels, + sizeof(buf_labels)); + else + strlcpy(buf_labels, "-", sizeof(buf_labels)); + + if (first) { + ttable_add_row(tt, "%s|%u|%s|%s|%s", buf_prefix, + rinfo->cost, buf_iface, buf_nhop, + buf_labels); + first = false; + } else + ttable_add_row(tt, "||%s|%s|%s", buf_iface, + buf_nhop, buf_labels); + } + } + + /* Dump the generated table. */ + if (tt->nrows > 1) { + char *table; + + table = ttable_dump(tt, "\n"); + vty_out(vty, "%s\n", table); + XFREE(MTYPE_TMP, table); + } + ttable_del(tt); +} + +static void show_isis_route_common(struct vty *vty, int levels, + struct isis *isis) +{ + struct listnode *node; + struct isis_area *area; + + if (!isis->area_list || isis->area_list->count == 0) + return; + + for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) { + vty_out(vty, "Area %s:\n", + area->area_tag ? area->area_tag : "null"); + + for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) { + if ((level & levels) == 0) + continue; + + if (area->ip_circuits > 0) { + isis_print_routes( + vty, + area->spftree[SPFTREE_IPV4][level - 1]); + } + if (area->ipv6_circuits > 0) { + isis_print_routes( + vty, + area->spftree[SPFTREE_IPV6][level - 1]); + } + if (isis_area_ipv6_dstsrc_enabled(area)) { + isis_print_routes(vty, + area->spftree[SPFTREE_DSTSRC] + [level - 1]); + } + } + } +} + +DEFUN(show_isis_route, show_isis_route_cmd, + "show " PROTO_NAME + " [vrf ] route" +#ifndef FABRICD + " []" +#endif + , + SHOW_STR PROTO_HELP VRF_FULL_CMD_HELP_STR + "IS-IS routing table\n" +#ifndef FABRICD + "level-1 routes\n" + "level-2 routes\n" +#endif +) +{ + int levels; + struct isis *isis; + struct listnode *node; + const char *vrf_name = VRF_DEFAULT_NAME; + bool all_vrf = false; + int idx = 0; + + if (argv_find(argv, argc, "level-1", &idx)) + levels = ISIS_LEVEL1; + else if (argv_find(argv, argc, "level-2", &idx)) + levels = ISIS_LEVEL2; + else + levels = ISIS_LEVEL1 | ISIS_LEVEL2; + + if (!im) { + vty_out(vty, "IS-IS Routing Process not enabled\n"); + return CMD_SUCCESS; + } + ISIS_FIND_VRF_ARGS(argv, argc, idx, vrf_name, all_vrf); + + if (vrf_name) { + if (all_vrf) { + for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) + show_isis_route_common(vty, levels, isis); + return CMD_SUCCESS; + } + isis = isis_lookup_by_vrfname(vrf_name); + if (isis != NULL) + show_isis_route_common(vty, levels, isis); + } + + return CMD_SUCCESS; +} + +void isis_spf_init(void) { install_element(VIEW_NODE, &show_isis_topology_cmd); + install_element(VIEW_NODE, &show_isis_route_cmd); + + /* Register hook(s). */ + hook_register(isis_adj_state_change_hook, spf_adj_state_change); } void isis_spf_print(struct isis_spftree *spftree, struct vty *vty) diff --git a/isisd/isis_spf.h b/isisd/isis_spf.h index 8bf9c9978a..b1a9054a84 100644 --- a/isisd/isis_spf.h +++ b/isisd/isis_spf.h @@ -26,22 +26,49 @@ struct isis_spftree; -struct isis_spftree *isis_spftree_new(struct isis_area *area); +enum spf_type { + SPF_TYPE_FORWARD = 1, + SPF_TYPE_REVERSE, +}; + +struct isis_spf_adj { + uint8_t id[ISIS_SYS_ID_LEN + 1]; + struct isis_adjacency *adj; + uint32_t metric; + struct isis_ext_subtlvs *subtlvs; + struct { + uint8_t desig_is_id[ISIS_SYS_ID_LEN + 1]; + struct isis_lsp *lsp_pseudo; + } lan; + uint8_t flags; +#define F_ISIS_SPF_ADJ_BROADCAST 0x01 +#define F_ISIS_SPF_ADJ_OLDMETRIC 0x02 +}; + +struct isis_spftree *isis_spftree_new(struct isis_area *area, + struct lspdb_head *lspdb, + const uint8_t *sysid, int level, + enum spf_tree_id tree_id, + enum spf_type type, uint8_t flags); void isis_spf_invalidate_routes(struct isis_spftree *tree); void isis_spf_verify_routes(struct isis_area *area, struct isis_spftree **trees); void isis_spftree_del(struct isis_spftree *spftree); void spftree_area_init(struct isis_area *area); void spftree_area_del(struct isis_area *area); -void spftree_area_adj_del(struct isis_area *area, struct isis_adjacency *adj); #define isis_spf_schedule(area, level) \ _isis_spf_schedule((area), (level), __func__, \ __FILE__, __LINE__) int _isis_spf_schedule(struct isis_area *area, int level, const char *func, const char *file, int line); -void isis_spf_cmds_init(void); +void isis_print_spftree(struct vty *vty, struct isis_spftree *spftree); +void isis_print_routes(struct vty *vty, struct isis_spftree *spftree); +void isis_spf_init(void); void isis_spf_print(struct isis_spftree *spftree, struct vty *vty); +void isis_run_spf(struct isis_spftree *spftree); struct isis_spftree *isis_run_hopcount_spf(struct isis_area *area, uint8_t *sysid, struct isis_spftree *spftree); + +void isis_spf_timer_free(void *run); #endif /* _ZEBRA_ISIS_SPF_H */ diff --git a/isisd/isis_spf_private.h b/isisd/isis_spf_private.h index 3a2a52afac..1e61bf0f48 100644 --- a/isisd/isis_spf_private.h +++ b/isisd/isis_spf_private.h @@ -50,6 +50,11 @@ struct prefix_pair { struct prefix_ipv6 src; }; +struct isis_vertex_adj { + struct isis_spf_adj *sadj; + struct mpls_label_stack *label_stack; +}; + /* * Triple */ @@ -79,9 +84,9 @@ struct isis_vertex_queue { }; __attribute__((__unused__)) -static unsigned isis_vertex_queue_hash_key(void *vp) +static unsigned isis_vertex_queue_hash_key(const void *vp) { - struct isis_vertex *vertex = vp; + const struct isis_vertex *vertex = vp; if (VTYPE_IP(vertex->type)) { uint32_t key; @@ -117,11 +122,11 @@ static bool isis_vertex_queue_hash_cmp(const void *a, const void *b) * Compares vertizes for sorting in the TENT list. Returns true * if candidate should be considered before current, false otherwise. */ -__attribute__((__unused__)) -static int isis_vertex_queue_tent_cmp(void *a, void *b) +__attribute__((__unused__)) static int isis_vertex_queue_tent_cmp(const void *a, + const void *b) { - struct isis_vertex *va = a; - struct isis_vertex *vb = b; + const struct isis_vertex *va = a; + const struct isis_vertex *vb = b; if (va->d_N < vb->d_N) return -1; @@ -180,6 +185,10 @@ static void isis_vertex_del(struct isis_vertex *vertex) XFREE(MTYPE_ISIS_VERTEX, vertex); } +bool isis_vertex_adj_exists(const struct isis_spftree *spftree, + const struct isis_vertex *vertex, + const struct isis_spf_adj *sadj); + __attribute__((__unused__)) static void isis_vertex_queue_clear(struct isis_vertex_queue *queue) { @@ -297,18 +306,26 @@ struct isis_spftree { struct isis_vertex_queue paths; /* the SPT */ struct isis_vertex_queue tents; /* TENT */ struct route_table *route_table; + struct lspdb_head *lspdb; /* link-state db */ + struct list *sadj_list; struct isis_area *area; /* back pointer to area */ unsigned int runcount; /* number of runs since uptime */ time_t last_run_timestamp; /* last run timestamp as wall time for display */ time_t last_run_monotime; /* last run as monotime for scheduling */ time_t last_run_duration; /* last run duration in msec */ + enum spf_type type; + uint8_t sysid[ISIS_SYS_ID_LEN]; uint16_t mtid; int family; int level; enum spf_tree_id tree_id; bool hopcount_metric; + uint8_t flags; }; +#define F_SPFTREE_HOPCOUNT_METRIC 0x01 +#define F_SPFTREE_NO_ROUTES 0x02 +#define F_SPFTREE_NO_ADJACENCIES 0x04 __attribute__((__unused__)) static void isis_vertex_id_init(struct isis_vertex *vertex, const void *id, @@ -347,8 +364,7 @@ static struct isis_lsp *lsp_for_vertex(struct isis_spftree *spftree, memcpy(lsp_id, vertex->N.id, ISIS_SYS_ID_LEN + 1); LSP_FRAGMENT(lsp_id) = 0; - struct lspdb_head *lspdb = &spftree->area->lspdb[spftree->level - 1]; - struct isis_lsp *lsp = lsp_search(lspdb, lsp_id); + struct isis_lsp *lsp = lsp_search(spftree->lspdb, lsp_id); if (lsp && lsp->hdr.rem_lifetime != 0) return lsp; diff --git a/isisd/isis_sr.c b/isisd/isis_sr.c new file mode 100644 index 0000000000..d05afaa630 --- /dev/null +++ b/isisd/isis_sr.c @@ -0,0 +1,2329 @@ +/* + * This is an implementation of Segment Routing for IS-IS as per RFC 8667 + * + * Copyright (C) 2019 Orange http://www.orange.com + * + * Author: Olivier Dugeon + * Contributor: Renato Westphal for NetDEF + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "if.h" +#include "linklist.h" +#include "log.h" +#include "command.h" +#include "termtable.h" +#include "memory.h" +#include "prefix.h" +#include "table.h" +#include "vty.h" +#include "zclient.h" +#include "lib/lib_errors.h" + +#include "isisd/isisd.h" +#include "isisd/isis_spf.h" +#include "isisd/isis_spf_private.h" +#include "isisd/isis_adjacency.h" +#include "isisd/isis_route.h" +#include "isisd/isis_mt.h" +#include "isisd/isis_sr.h" +#include "isisd/isis_tlvs.h" +#include "isisd/isis_misc.h" +#include "isisd/isis_zebra.h" +#include "isisd/isis_errors.h" + +/* Local variables and functions */ +DEFINE_MTYPE_STATIC(ISISD, ISIS_SR_INFO, "ISIS segment routing information") + +static void sr_prefix_uninstall(struct sr_prefix *srp); +static void sr_prefix_reinstall(struct sr_prefix *srp, bool make_before_break); +static void sr_local_block_delete(struct isis_area *area); +static int sr_local_block_init(struct isis_area *area); +static void sr_adj_sid_update(struct sr_adjacency *sra, + struct sr_local_block *srlb); + +/* --- RB-Tree Management functions ----------------------------------------- */ + +/** + * SR Prefix comparison for RB-Tree. + * + * @param a First SR prefix + * @param b Second SR prefix + * + * @return -1 (a < b), 0 (a == b) or +1 (a > b) + */ +static inline int sr_prefix_sid_compare(const struct sr_prefix *a, + const struct sr_prefix *b) +{ + return prefix_cmp(&a->prefix, &b->prefix); +} +DECLARE_RBTREE_UNIQ(srdb_node_prefix, struct sr_prefix, node_entry, + sr_prefix_sid_compare) +DECLARE_RBTREE_UNIQ(srdb_area_prefix, struct sr_prefix, area_entry, + sr_prefix_sid_compare) + +/** + * Configured SR Prefix comparison for RB-Tree. + * + * @param a First SR prefix + * @param b Second SR prefix + * + * @return -1 (a < b), 0 (a == b) or +1 (a > b) + */ +static inline int sr_prefix_sid_cfg_compare(const struct sr_prefix_cfg *a, + const struct sr_prefix_cfg *b) +{ + return prefix_cmp(&a->prefix, &b->prefix); +} +DECLARE_RBTREE_UNIQ(srdb_prefix_cfg, struct sr_prefix_cfg, entry, + sr_prefix_sid_cfg_compare) + +/** + * SR Node comparison for RB-Tree. + * + * @param a First SR node + * @param b Second SR node + * + * @return -1 (a < b), 0 (a == b) or +1 (a > b) + */ +static inline int sr_node_compare(const struct sr_node *a, + const struct sr_node *b) +{ + return memcmp(a->sysid, b->sysid, ISIS_SYS_ID_LEN); +} +DECLARE_RBTREE_UNIQ(srdb_node, struct sr_node, entry, sr_node_compare) + +/* --- Functions used for Yang model and CLI to configure Segment Routing --- */ + +/** + * Check if prefix correspond to a Node SID. + * + * @param ifp Interface + * @param prefix Prefix to be checked + * + * @return True if the interface/address pair corresponds to a Node-SID + */ +static bool sr_prefix_is_node_sid(const struct interface *ifp, + const struct prefix *prefix) +{ + return (if_is_loopback(ifp) && is_host_route(prefix)); +} + +/** + * Update local SRGB configuration. SRGB is reserved though Label Manager. + * This function trigger the update of local Prefix-SID installation. + * + * @param area IS-IS area + * @param lower_bound Lower bound of SRGB + * @param upper_bound Upper bound of SRGB + * + * @return 0 on success, -1 otherwise + */ +int isis_sr_cfg_srgb_update(struct isis_area *area, uint32_t lower_bound, + uint32_t upper_bound) +{ + struct isis_sr_db *srdb = &area->srdb; + + sr_debug("ISIS-Sr (%s): Update SRGB with new range [%u/%u]", + area->area_tag, lower_bound, upper_bound); + + /* Just store new SRGB values if Label Manager is not available. + * SRGB will be configured later when SR start */ + if (!isis_zebra_label_manager_ready()) { + srdb->config.srgb_lower_bound = lower_bound; + srdb->config.srgb_upper_bound = upper_bound; + return 0; + } + + /* Label Manager is ready, start by releasing the old SRGB. */ + if (srdb->srgb_active) { + isis_zebra_release_label_range(srdb->config.srgb_lower_bound, + srdb->config.srgb_upper_bound); + srdb->srgb_active = false; + } + + srdb->config.srgb_lower_bound = lower_bound; + srdb->config.srgb_upper_bound = upper_bound; + + if (srdb->enabled) { + struct sr_prefix *srp; + + /* then request new SRGB if SR is enabled. */ + if (isis_zebra_request_label_range( + srdb->config.srgb_lower_bound, + srdb->config.srgb_upper_bound + - srdb->config.srgb_lower_bound + 1) < 0) { + srdb->srgb_active = false; + return -1; + } else + srdb->srgb_active = true; + + + sr_debug(" |- Got new SRGB [%u/%u]", + srdb->config.srgb_lower_bound, + srdb->config.srgb_upper_bound); + + /* Reinstall local Prefix-SIDs to update their input labels. */ + for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) { + frr_each (srdb_area_prefix, + &area->srdb.prefix_sids[level - 1], srp) { + sr_prefix_reinstall(srp, false); + } + } + + lsp_regenerate_schedule(area, area->is_type, 0); + } else if (srdb->config.enabled) { + /* Try to enable SR again using the new SRGB. */ + isis_sr_start(area); + } + + return 0; +} + +/** + * Update Segment Routing Local Block range which is reserved though the + * Label Manager. This function trigger the update of local Adjacency-SID + * installation. + * + * @param area IS-IS area + * @param lower_bound Lower bound of SRLB + * @param upper_bound Upper bound of SRLB + * + * @return 0 on success, -1 otherwise + */ +int isis_sr_cfg_srlb_update(struct isis_area *area, uint32_t lower_bound, + uint32_t upper_bound) +{ + struct isis_sr_db *srdb = &area->srdb; + struct listnode *node; + struct sr_adjacency *sra; + + sr_debug("ISIS-Sr (%s): Update SRLB with new range [%u/%u]", + area->area_tag, lower_bound, upper_bound); + + /* Just store new SRLB values if Label Manager is not available. + * SRLB will be configured later when SR start */ + if (!isis_zebra_label_manager_ready()) { + srdb->config.srlb_lower_bound = lower_bound; + srdb->config.srlb_upper_bound = upper_bound; + return 0; + } + + /* LM is ready, start by deleting the old SRLB */ + sr_local_block_delete(area); + + srdb->config.srlb_lower_bound = lower_bound; + srdb->config.srlb_upper_bound = upper_bound; + + if (srdb->enabled) { + /* Initialize new SRLB */ + if (sr_local_block_init(area) != 0) + return -1; + + /* Reinstall local Adjacency-SIDs with new labels. */ + for (ALL_LIST_ELEMENTS_RO(area->srdb.adj_sids, node, sra)) + sr_adj_sid_update(sra, &srdb->srlb); + + /* Update and Flood LSP */ + lsp_regenerate_schedule(area, area->is_type, 0); + } else if (srdb->config.enabled) { + /* Try to enable SR again using the new SRLB. */ + isis_sr_start(area); + } + + return 0; +} + +/** + * Add new Prefix-SID configuration to the SRDB. + * + * @param area IS-IS area + * @param prefix Prefix to be added + * + * @return Newly added Prefix-SID configuration structure + */ +struct sr_prefix_cfg *isis_sr_cfg_prefix_add(struct isis_area *area, + const struct prefix *prefix) +{ + struct sr_prefix_cfg *pcfg; + struct interface *ifp; + + sr_debug("ISIS-Sr (%s): Add local prefix %pFX", area->area_tag, prefix); + + pcfg = XCALLOC(MTYPE_ISIS_SR_INFO, sizeof(*pcfg)); + pcfg->prefix = *prefix; + pcfg->area = area; + + /* Pull defaults from the YANG module. */ + pcfg->sid_type = yang_get_default_enum( + "%s/prefix-sid-map/prefix-sid/sid-value-type", ISIS_SR); + pcfg->last_hop_behavior = yang_get_default_enum( + "%s/prefix-sid-map/prefix-sid/last-hop-behavior", ISIS_SR); + + /* Set the N-flag when appropriate. */ + ifp = if_lookup_prefix(prefix, VRF_DEFAULT); + if (ifp && sr_prefix_is_node_sid(ifp, prefix)) + pcfg->node_sid = true; + + /* Save prefix-sid configuration. */ + srdb_prefix_cfg_add(&area->srdb.config.prefix_sids, pcfg); + + return pcfg; +} + +/** + * Removal of locally configured Prefix-SID. + * + * @param pcfg Configured Prefix-SID + */ +void isis_sr_cfg_prefix_del(struct sr_prefix_cfg *pcfg) +{ + struct isis_area *area = pcfg->area; + + sr_debug("ISIS-Sr (%s): Delete local Prefix-SID %pFX %s %u", + area->area_tag, &pcfg->prefix, + pcfg->sid_type == SR_SID_VALUE_TYPE_INDEX ? "index" : "label", + pcfg->sid); + + srdb_prefix_cfg_del(&area->srdb.config.prefix_sids, pcfg); + XFREE(MTYPE_ISIS_SR_INFO, pcfg); +} + +/** + * Lookup for Prefix-SID in the local configuration. + * + * @param area IS-IS area + * @param prefix Prefix to lookup + * + * @return Configured Prefix-SID structure if found, NULL otherwise + */ +struct sr_prefix_cfg *isis_sr_cfg_prefix_find(struct isis_area *area, + union prefixconstptr prefix) +{ + struct sr_prefix_cfg pcfg = {}; + + prefix_copy(&pcfg.prefix, prefix.p); + return srdb_prefix_cfg_find(&area->srdb.config.prefix_sids, &pcfg); +} + +/** + * Fill in Prefix-SID Sub-TLV according to the corresponding configuration. + * + * @param pcfg Prefix-SID configuration + * @param external False if prefix is locally configured, true otherwise + * @param psid Prefix-SID sub-TLV to be updated + */ +void isis_sr_prefix_cfg2subtlv(const struct sr_prefix_cfg *pcfg, bool external, + struct isis_prefix_sid *psid) +{ + /* Set SID algorithm. */ + psid->algorithm = SR_ALGORITHM_SPF; + + /* Set SID flags. */ + psid->flags = 0; + switch (pcfg->last_hop_behavior) { + case SR_LAST_HOP_BEHAVIOR_EXP_NULL: + SET_FLAG(psid->flags, ISIS_PREFIX_SID_NO_PHP); + SET_FLAG(psid->flags, ISIS_PREFIX_SID_EXPLICIT_NULL); + break; + case SR_LAST_HOP_BEHAVIOR_NO_PHP: + SET_FLAG(psid->flags, ISIS_PREFIX_SID_NO_PHP); + UNSET_FLAG(psid->flags, ISIS_PREFIX_SID_EXPLICIT_NULL); + break; + case SR_LAST_HOP_BEHAVIOR_PHP: + UNSET_FLAG(psid->flags, ISIS_PREFIX_SID_NO_PHP); + UNSET_FLAG(psid->flags, ISIS_PREFIX_SID_EXPLICIT_NULL); + break; + } + if (external) + SET_FLAG(psid->flags, ISIS_PREFIX_SID_READVERTISED); + if (pcfg->node_sid) + SET_FLAG(psid->flags, ISIS_PREFIX_SID_NODE); + + /* Set SID value. */ + psid->value = pcfg->sid; + if (pcfg->sid_type == SR_SID_VALUE_TYPE_ABSOLUTE) { + SET_FLAG(psid->flags, ISIS_PREFIX_SID_VALUE); + SET_FLAG(psid->flags, ISIS_PREFIX_SID_LOCAL); + } +} + +/* --- Segment Routing Prefix Management functions -------------------------- */ + +/** + * Add Segment Routing Prefix to a given Segment Routing Node. + * + * @param area IS-IS area + * @param srn Segment Routing Node + * @param prefix Prefix to be added + * @param local True if prefix is locally configured, false otherwise + * @param psid Prefix-SID sub-TLVs + * + * @return New Segment Routing Prefix structure + */ +static struct sr_prefix *sr_prefix_add(struct isis_area *area, + struct sr_node *srn, + union prefixconstptr prefix, bool local, + const struct isis_prefix_sid *psid) +{ + struct sr_prefix *srp; + + srp = XCALLOC(MTYPE_ISIS_SR_INFO, sizeof(*srp)); + prefix_copy(&srp->prefix, prefix.p); + srp->sid = *psid; + srp->input_label = MPLS_INVALID_LABEL; + if (local) { + srp->type = ISIS_SR_PREFIX_LOCAL; + isis_sr_nexthop_reset(&srp->u.local.info); + } else { + srp->type = ISIS_SR_PREFIX_REMOTE; + srp->u.remote.rinfo = NULL; + } + srp->srn = srn; + srdb_node_prefix_add(&srn->prefix_sids, srp); + /* TODO: this might fail if we have Anycast SIDs in the IS-IS area. */ + srdb_area_prefix_add(&area->srdb.prefix_sids[srn->level - 1], srp); + + sr_debug(" |- Added new SR Prefix-SID %pFX %s %u to SR Node %s", + &srp->prefix, IS_SID_VALUE(srp->sid.flags) ? "label" : "index", + srp->sid.value, sysid_print(srn->sysid)); + + return srp; +} + +/** + * Remove given Segment Prefix from given Segment Routing Node. + * Prefix-SID is un-installed first. + * + * @param area IS-IS area + * @param srn Segment Routing Node + * @param srp Segment Routing Prefix + */ +static void sr_prefix_del(struct isis_area *area, struct sr_node *srn, + struct sr_prefix *srp) +{ + sr_debug(" |- Delete SR Prefix-SID %pFX %s %u to SR Node %s", + &srp->prefix, IS_SID_VALUE(srp->sid.flags) ? "label" : "index", + srp->sid.value, sysid_print(srn->sysid)); + + sr_prefix_uninstall(srp); + srdb_node_prefix_del(&srn->prefix_sids, srp); + srdb_area_prefix_del(&area->srdb.prefix_sids[srn->level - 1], srp); + XFREE(MTYPE_ISIS_SR_INFO, srp); +} + +/** + * Find Segment Routing Prefix by Area. + * + * @param area IS-IS area + * @param level IS-IS level + * @param prefix Prefix to lookup + * + * @return Segment Routing Prefix structure if found, NULL otherwise + */ +static struct sr_prefix *sr_prefix_find_by_area(struct isis_area *area, + int level, + union prefixconstptr prefix) +{ + struct sr_prefix srp = {}; + + prefix_copy(&srp.prefix, prefix.p); + return srdb_area_prefix_find(&area->srdb.prefix_sids[level - 1], &srp); +} + +/** + * Find Segment Routing Prefix by Segment Routing Node. + * + * @param srn Segment Routing Node + * @param prefix Prefix to lookup + * + * @return Segment Routing Prefix structure if found, NULL otherwise + */ +static struct sr_prefix *sr_prefix_find_by_node(struct sr_node *srn, + union prefixconstptr prefix) +{ + struct sr_prefix srp = {}; + + prefix_copy(&srp.prefix, prefix.p); + return srdb_node_prefix_find(&srn->prefix_sids, &srp); +} + +/* --- Segment Routing Node Management functions ---------------------------- */ + +/** + * Add Segment Routing Node to the Segment Routing Data Base. + * + * @param area IS-IS area + * @param level IS-IS level + * @param sysid Node System ID + * @param cap Segment Routing Capability sub-TLVs + * + * @return New Segment Routing Node structure + */ +static struct sr_node *sr_node_add(struct isis_area *area, int level, + const uint8_t *sysid) +{ + struct sr_node *srn; + + srn = XCALLOC(MTYPE_ISIS_SR_INFO, sizeof(*srn)); + srn->level = level; + memcpy(srn->sysid, sysid, ISIS_SYS_ID_LEN); + srn->area = area; + srdb_node_prefix_init(&srn->prefix_sids); + srdb_node_add(&area->srdb.sr_nodes[level - 1], srn); + + sr_debug(" |- Added new SR Node %s", sysid_print(srn->sysid)); + + return srn; +} + +static void sr_node_del(struct isis_area *area, int level, struct sr_node *srn) +/** + * Remove Segment Routing Node from the Segment Routing Data Base. + * All Prefix-SID attached to this Segment Routing Node are removed first. + * + * @param area IS-IS area + * @param level IS-IS level + * @param srn Segment Routing Node to be deleted + */ +{ + + sr_debug(" |- Delete SR Node %s", sysid_print(srn->sysid)); + + /* Remove and uninstall Prefix-SIDs. */ + while (srdb_node_prefix_count(&srn->prefix_sids) > 0) { + struct sr_prefix *srp; + + srp = srdb_node_prefix_first(&srn->prefix_sids); + sr_prefix_del(area, srn, srp); + } + + srdb_node_del(&area->srdb.sr_nodes[level - 1], srn); + XFREE(MTYPE_ISIS_SR_INFO, srn); +} + +/** + * Find Segment Routing Node in the Segment Routing Data Base per system ID. + * + * @param area IS-IS area + * @param level IS-IS level + * @param sysid Node System ID to lookup + * + * @return Segment Routing Node structure if found, NULL otherwise + */ +static struct sr_node *sr_node_find(struct isis_area *area, int level, + const uint8_t *sysid) +{ + struct sr_node srn = {}; + + memcpy(srn.sysid, sysid, ISIS_SYS_ID_LEN); + return srdb_node_find(&area->srdb.sr_nodes[level - 1], &srn); +} + +/** + * Update Segment Routing Node following an SRGB update. This function + * is called when a neighbor SR Node has updated its SRGB. + * + * @param area IS-IS area + * @param level IS-IS level + * @param sysid Segment Routing Node system ID + */ +static void sr_node_srgb_update(struct isis_area *area, int level, + uint8_t *sysid) +{ + struct sr_prefix *srp; + + sr_debug("ISIS-Sr (%s): Update neighbors SR Node with new SRGB", + area->area_tag); + + frr_each (srdb_area_prefix, &area->srdb.prefix_sids[level - 1], srp) { + struct listnode *node; + struct isis_nexthop *nh; + + if (srp->type == ISIS_SR_PREFIX_LOCAL) + continue; + + if (srp->u.remote.rinfo == NULL) + continue; + + for (ALL_LIST_ELEMENTS_RO(srp->u.remote.rinfo->nexthops, node, + nh)) { + if (memcmp(nh->sysid, sysid, ISIS_SYS_ID_LEN) != 0) + continue; + + /* + * The Prefix-SID input label hasn't changed. We could + * re-install all Prefix-SID with "Make Before Break" + * option. Zebra layer will update output label(s) by + * adding new entry before removing the old one(s). + */ + sr_prefix_reinstall(srp, true); + break; + } + } +} + +/* --- Segment Routing Nexthop information Management functions ------------- */ + +/** + * Update Segment Routing Nexthop. + * + * @param srnh Segment Routing next hop + * @param label Output MPLS label + */ +void isis_sr_nexthop_update(struct sr_nexthop_info *srnh, mpls_label_t label) +{ + srnh->label = label; + if (srnh->uptime == 0) + srnh->uptime = time(NULL); +} + +/** + * Reset Segment Routing Nexthop. + * + * @param srnh Segment Routing Nexthop + */ +void isis_sr_nexthop_reset(struct sr_nexthop_info *srnh) +{ + srnh->label = MPLS_INVALID_LABEL; + srnh->uptime = 0; +} + +/* --- Segment Routing Prefix-SID Management functions to configure LFIB ---- */ + +/** + * Lookup IS-IS route in the Shortest Path Tree. + * + * @param area IS-IS area + * @param tree_id Shortest Path Tree identifier + * @param srp Segment Routing Prefix to lookup + * + * @return Route Information for this prefix if found, NULL otherwise + */ +static struct isis_route_info *sr_prefix_lookup_route(struct isis_area *area, + enum spf_tree_id tree_id, + struct sr_prefix *srp) +{ + struct route_node *rn; + int level = srp->srn->level; + + rn = route_node_lookup(area->spftree[tree_id][level - 1]->route_table, + &srp->prefix); + if (rn) { + route_unlock_node(rn); + if (rn->info) + return rn->info; + } + + return NULL; +} + +/** + * Compute input label for the given Prefix-SID. + * + * @param srp Segment Routing Prefix + * + * @return MPLS label or MPLS_INVALID_LABEL in case of SRGB overflow + */ +static mpls_label_t sr_prefix_in_label(const struct sr_prefix *srp) +{ + const struct sr_node *srn = srp->srn; + struct isis_area *area = srn->area; + + /* Return SID value as MPLS label if it is an Absolute SID */ + if (CHECK_FLAG(srp->sid.flags, + ISIS_PREFIX_SID_VALUE | ISIS_PREFIX_SID_LOCAL)) + return srp->sid.value; + + /* Check that SID index falls inside the SRGB */ + if (srp->sid.value >= (area->srdb.config.srgb_upper_bound + - area->srdb.config.srgb_lower_bound + 1)) { + flog_warn(EC_ISIS_SID_OVERFLOW, + "%s: SID index %u falls outside local SRGB range", + __func__, srp->sid.value); + return MPLS_INVALID_LABEL; + } + + /* Return MPLS label as SID index + SRGB_lower_bound as per RFC 8667 */ + return (area->srdb.config.srgb_lower_bound + srp->sid.value); +} + +/** + * Compute output label for the given Prefix-SID. + * + * @param srp Segment Routing Prefix + * @param srn_nexthop Segment Routing nexthop node + * @param sysid System ID of the SR node which advertised the Prefix-SID + * + * @return MPLS label or MPLS_INVALID_LABEL in case of error + */ +static mpls_label_t sr_prefix_out_label(const struct sr_prefix *srp, + const struct sr_node *srn_nexthop, + const uint8_t *sysid) +{ + const struct sr_node *srn = srp->srn; + + /* Check if the nexthop SR Node is the last hop? */ + if (memcmp(sysid, srn->sysid, ISIS_SYS_ID_LEN) == 0) { + /* SR-Node doesn't request NO-PHP. Return Implicit NULL label */ + if (!CHECK_FLAG(srp->sid.flags, ISIS_PREFIX_SID_NO_PHP)) + return MPLS_LABEL_IMPLICIT_NULL; + + /* SR-Node requests Implicit NULL Label */ + if (CHECK_FLAG(srp->sid.flags, ISIS_PREFIX_SID_EXPLICIT_NULL)) { + if (srp->prefix.family == AF_INET) + return MPLS_LABEL_IPV4_EXPLICIT_NULL; + else + return MPLS_LABEL_IPV6_EXPLICIT_NULL; + } + /* Fallthrough */ + } + + /* Return SID value as MPLS label if it is an Absolute SID */ + if (CHECK_FLAG(srp->sid.flags, + ISIS_PREFIX_SID_VALUE | ISIS_PREFIX_SID_LOCAL)) { + /* + * V/L SIDs have local significance, so only adjacent routers + * can use them (RFC8667 section #2.1.1.1) + */ + if (srp->srn != srn_nexthop) + return MPLS_INVALID_LABEL; + return srp->sid.value; + } + + /* Check that SID index falls inside the SRGB */ + if (srp->sid.value >= srn_nexthop->cap.srgb.range_size) { + flog_warn(EC_ISIS_SID_OVERFLOW, + "%s: SID index %u falls outside remote SRGB range", + __func__, srp->sid.value); + return MPLS_INVALID_LABEL; + } + + /* Return MPLS label as SID index + SRGB_lower_bound as per RFC 8667 */ + return (srn_nexthop->cap.srgb.lower_bound + srp->sid.value); +} + +/** + * Process local Prefix-SID and install it if possible. Input label is + * computed before installing it in LFIB. + * + * @param srp Segment Routing Prefix + * + * @return 0 on success, -1 otherwise + */ +static int sr_prefix_install_local(struct sr_prefix *srp) +{ + mpls_label_t input_label; + const struct sr_node *srn = srp->srn; + + /* + * No need to install Label for local Prefix-SID unless the + * no-PHP option is configured. + */ + if (!CHECK_FLAG(srp->sid.flags, ISIS_PREFIX_SID_NO_PHP) + || CHECK_FLAG(srp->sid.flags, ISIS_PREFIX_SID_EXPLICIT_NULL)) + return -1; + + sr_debug(" |- Installing Prefix-SID %pFX %s %u (%s) with nexthop self", + &srp->prefix, IS_SID_VALUE(srp->sid.flags) ? "label" : "index", + srp->sid.value, circuit_t2string(srn->level)); + + /* Compute input label and check that is valid. */ + input_label = sr_prefix_in_label(srp); + if (input_label == MPLS_INVALID_LABEL) + return -1; + + /* Update internal state. */ + srp->input_label = input_label; + isis_sr_nexthop_update(&srp->u.local.info, MPLS_LABEL_IMPLICIT_NULL); + + /* Install Prefix-SID in the forwarding plane. */ + isis_zebra_send_prefix_sid(ZEBRA_MPLS_LABELS_REPLACE, srp); + + return 0; +} + +/** + * Process remote Prefix-SID and install it if possible. Input and Output + * labels are computed before installing them in LFIB. + * + * @param srp Segment Routing Prefix + * + * @return 0 on success, -1 otherwise + */ +static int sr_prefix_install_remote(struct sr_prefix *srp) +{ + const struct sr_node *srn = srp->srn; + struct isis_area *area = srn->area; + enum spf_tree_id tree_id; + struct listnode *node; + struct isis_nexthop *nexthop; + mpls_label_t input_label; + size_t nexthop_num = 0; + + /* Lookup to associated IS-IS route. */ + tree_id = (srp->prefix.family == AF_INET) ? SPFTREE_IPV4 : SPFTREE_IPV6; + srp->u.remote.rinfo = sr_prefix_lookup_route(area, tree_id, srp); + if (!srp->u.remote.rinfo) + /* SPF hasn't converged for this route yet. */ + return -1; + + /* Compute input label and check that is valid. */ + input_label = sr_prefix_in_label(srp); + if (input_label == MPLS_INVALID_LABEL) + return -1; + + sr_debug(" |- Installing Prefix-SID %pFX %s %u (%s)", &srp->prefix, + IS_SID_VALUE(srp->sid.flags) ? "label" : "index", + srp->sid.value, circuit_t2string(srn->level)); + + /* Process all SPF nexthops */ + for (ALL_LIST_ELEMENTS_RO(srp->u.remote.rinfo->nexthops, node, + nexthop)) { + struct sr_node *srn_nexthop; + mpls_label_t output_label; + + /* Check if the nexthop advertised a SRGB. */ + srn_nexthop = sr_node_find(area, srn->level, nexthop->sysid); + if (!srn_nexthop) + goto next; + + /* + * Check if the nexthop can handle SR-MPLS encapsulated IPv4 or + * IPv6 packets. + */ + if ((nexthop->family == AF_INET + && !IS_SR_IPV4(srn_nexthop->cap.srgb)) + || (nexthop->family == AF_INET6 + && !IS_SR_IPV6(srn_nexthop->cap.srgb))) + goto next; + + /* Compute output label and check if it is valid */ + output_label = + sr_prefix_out_label(srp, srn_nexthop, nexthop->sysid); + if (output_label == MPLS_INVALID_LABEL) + goto next; + + if (IS_DEBUG_SR) { + static char buf[INET6_ADDRSTRLEN]; + + inet_ntop(nexthop->family, &nexthop->ip, buf, + sizeof(buf)); + zlog_debug(" |- nexthop %s label %u", buf, + output_label); + } + + isis_sr_nexthop_update(&nexthop->sr, output_label); + nexthop_num++; + continue; + next: + isis_sr_nexthop_reset(&nexthop->sr); + } + + /* Check that we found at least one valid nexthop */ + if (nexthop_num == 0) { + sr_debug(" |- no valid nexthops"); + return -1; + } + + /* Update internal state. */ + srp->input_label = input_label; + + /* Install Prefix-SID in the forwarding plane. */ + isis_zebra_send_prefix_sid(ZEBRA_MPLS_LABELS_REPLACE, srp); + + return 0; +} + +/** + * Process local or remote Prefix-SID and install it if possible. + * + * @param srp Segment Routing Prefix + */ +static void sr_prefix_install(struct sr_prefix *srp) +{ + const struct sr_node *srn = srp->srn; + struct isis_area *area = srn->area; + int ret; + + sr_debug("ISIS-Sr (%s): Install Prefix-SID %pFX %s %u", area->area_tag, + &srp->prefix, IS_SID_VALUE(srp->sid.flags) ? "label" : "index", + srp->sid.value); + + /* L1 routes are preferred over the L2 ones. */ + if (area->is_type == IS_LEVEL_1_AND_2) { + struct sr_prefix *srp_l1, *srp_l2; + + switch (srn->level) { + case ISIS_LEVEL1: + srp_l2 = sr_prefix_find_by_area(area, ISIS_LEVEL2, + &srp->prefix); + if (srp_l2) + sr_prefix_uninstall(srp_l2); + break; + case ISIS_LEVEL2: + srp_l1 = sr_prefix_find_by_area(area, ISIS_LEVEL1, + &srp->prefix); + if (srp_l1) + return; + break; + default: + break; + } + } + + /* Install corresponding LFIB entry */ + if (srp->type == ISIS_SR_PREFIX_LOCAL) + ret = sr_prefix_install_local(srp); + else + ret = sr_prefix_install_remote(srp); + if (ret != 0) + sr_prefix_uninstall(srp); +} + +/** + * Uninstall local or remote Prefix-SID. + * + * @param srp Segment Routing Prefix + */ +static void sr_prefix_uninstall(struct sr_prefix *srp) +{ + struct listnode *node; + struct isis_nexthop *nexthop; + + /* Check that Input Label is valid */ + if (srp->input_label == MPLS_INVALID_LABEL) + return; + + sr_debug("ISIS-Sr: Un-install Prefix-SID %pFX %s %u", &srp->prefix, + IS_SID_VALUE(srp->sid.flags) ? "label" : "index", + srp->sid.value); + + /* Uninstall Prefix-SID from the forwarding plane. */ + isis_zebra_send_prefix_sid(ZEBRA_MPLS_LABELS_DELETE, srp); + + /* Reset internal state. */ + srp->input_label = MPLS_INVALID_LABEL; + switch (srp->type) { + case ISIS_SR_PREFIX_LOCAL: + isis_sr_nexthop_reset(&srp->u.local.info); + break; + case ISIS_SR_PREFIX_REMOTE: + if (srp->u.remote.rinfo) { + for (ALL_LIST_ELEMENTS_RO(srp->u.remote.rinfo->nexthops, + node, nexthop)) + isis_sr_nexthop_reset(&nexthop->sr); + } + break; + } +} + +/** + * Reinstall local or remote Prefix-SID. + * + * @param srp Segment Routing Prefix + */ +static inline void sr_prefix_reinstall(struct sr_prefix *srp, + bool make_before_break) +{ + /* + * Make Before Break can be used only when we know for sure that + * the Prefix-SID input label hasn't changed. Otherwise we need to + * uninstall the Prefix-SID first using the old input label before + * reinstalling it. + */ + if (!make_before_break) + sr_prefix_uninstall(srp); + + /* New input label is computed in sr_prefix_install() function */ + sr_prefix_install(srp); +} + +/* --- IS-IS LSP Parse functions -------------------------------------------- */ + +/** + * Compare Router Capabilities. Only Flags, SRGB and Algorithm are used for the + * comparison. MSD and SRLB modification must not trigger and SR-Prefix update. + * + * @param r1 First Router Capabilities to compare + * @param r2 Second Router Capabilities to compare + * @return 0 if r1 and r2 are equal or -1 otherwise + */ +static int router_cap_cmp(const struct isis_router_cap *r1, + const struct isis_router_cap *r2) +{ + if (r1->flags == r2->flags + && r1->srgb.lower_bound == r2->srgb.lower_bound + && r1->srgb.range_size == r2->srgb.range_size + && r1->algo[0] == r2->algo[0]) + return 0; + else + return -1; +} + +/** + * Parse all SR-related information from the given Router Capabilities TLV. + * + * @param area IS-IS area + * @param level IS-IS level + * @param sysid System ID of the LSP + * @param router_cap Router Capability subTLVs + * + * @return Segment Routing Node structure for this System ID + */ +static struct sr_node * +parse_router_cap_tlv(struct isis_area *area, int level, const uint8_t *sysid, + const struct isis_router_cap *router_cap) +{ + struct sr_node *srn; + + if (!router_cap || router_cap->srgb.range_size == 0) + return NULL; + + sr_debug("ISIS-Sr (%s): Parse Router Capability TLV", area->area_tag); + + srn = sr_node_find(area, level, sysid); + if (srn) { + if (router_cap_cmp(&srn->cap, router_cap) != 0) { + srn->state = SRDB_STATE_MODIFIED; + } else + srn->state = SRDB_STATE_UNCHANGED; + sr_debug(" |- Found %s SR Node %s", + srn->state == SRDB_STATE_MODIFIED ? "Modified" + : "Unchanged", + sysid_print(srn->sysid)); + } else { + srn = sr_node_add(area, level, sysid); + srn->state = SRDB_STATE_NEW; + } + + /* + * Update Router Capabilities in any case as SRLB or MSD + * modification are not take into account for comparison. + */ + srn->cap = *router_cap; + + return srn; +} + +/** + * Parse list of Prefix-SID Sub-TLVs. + * + * @param srn Segment Routing Node + * @param prefix Prefix to be parsed + * @param local True if prefix comes from own LSP, false otherwise + * @param prefix_sids Prefix SID subTLVs + */ +static void parse_prefix_sid_subtlvs(struct sr_node *srn, + union prefixconstptr prefix, bool local, + struct isis_item_list *prefix_sids) +{ + struct isis_area *area = srn->area; + struct isis_item *i; + + sr_debug("ISIS-Sr (%s): Parse Prefix SID TLV", area->area_tag); + + /* Parse list of Prefix SID subTLVs */ + for (i = prefix_sids->head; i; i = i->next) { + struct isis_prefix_sid *psid = (struct isis_prefix_sid *)i; + struct sr_prefix *srp; + + /* Only SPF algorithm is supported right now */ + if (psid->algorithm != SR_ALGORITHM_SPF) + continue; + + /* Compute corresponding Segment Routing Prefix */ + srp = sr_prefix_find_by_node(srn, prefix); + if (srp) { + if (srp->sid.flags != psid->flags + || srp->sid.algorithm != psid->algorithm + || srp->sid.value != psid->value) { + srp->sid = *psid; + srp->state = SRDB_STATE_MODIFIED; + } else if (srp->state == SRDB_STATE_VALIDATED) + srp->state = SRDB_STATE_UNCHANGED; + sr_debug(" |- Found %s Prefix-SID %pFX", + srp->state == SRDB_STATE_MODIFIED + ? "Modified" + : "Unchanged", + &srp->prefix); + + } else { + srp = sr_prefix_add(area, srn, prefix, local, psid); + srp->state = SRDB_STATE_NEW; + } + /* + * Stop the Prefix-SID iteration since we only support the SPF + * algorithm for now. + */ + break; + } +} + +/** + * Parse all SR-related information from the given LSP. + * + * @param area IS-IS area + * @param level IS-IS level + * @param srn Segment Routing Node + * @param lsp IS-IS LSP + */ +static void parse_lsp(struct isis_area *area, int level, struct sr_node **srn, + struct isis_lsp *lsp) +{ + struct isis_item_list *items; + struct isis_item *i; + bool local = lsp->own_lsp; + + /* Check LSP sequence number */ + if (lsp->hdr.seqno == 0) { + zlog_warn("%s: lsp with 0 seq_num - ignore", __func__); + return; + } + + sr_debug("ISIS-Sr (%s): Parse LSP from node %s", area->area_tag, + sysid_print(lsp->hdr.lsp_id)); + + /* Parse the Router Capability TLV. */ + if (*srn == NULL) { + *srn = parse_router_cap_tlv(area, level, lsp->hdr.lsp_id, + lsp->tlvs->router_cap); + if (!*srn) + return; + } + + /* Parse the Extended IP Reachability TLV. */ + items = &lsp->tlvs->extended_ip_reach; + for (i = items->head; i; i = i->next) { + struct isis_extended_ip_reach *ir; + + ir = (struct isis_extended_ip_reach *)i; + if (!ir->subtlvs) + continue; + + parse_prefix_sid_subtlvs(*srn, &ir->prefix, local, + &ir->subtlvs->prefix_sids); + } + + /* Parse Multi Topology Reachable IPv6 Prefixes TLV. */ + items = isis_lookup_mt_items(&lsp->tlvs->mt_ipv6_reach, + ISIS_MT_IPV6_UNICAST); + for (i = items ? items->head : NULL; i; i = i->next) { + struct isis_ipv6_reach *ir; + + ir = (struct isis_ipv6_reach *)i; + if (!ir->subtlvs) + continue; + + parse_prefix_sid_subtlvs(*srn, &ir->prefix, local, + &ir->subtlvs->prefix_sids); + } +} + +/** + * Parse all SR-related information from the entire LSPDB. + * + * @param area IS-IS area + */ +static void parse_lspdb(struct isis_area *area) +{ + struct isis_lsp *lsp; + + sr_debug("ISIS-Sr (%s): Parse LSP Data Base", area->area_tag); + + /* Process all LSP from Level 1 & 2 */ + for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) { + frr_each (lspdb, &area->lspdb[level - 1], lsp) { + struct isis_lsp *frag; + struct listnode *node; + struct sr_node *srn = NULL; + + /* Skip Pseudo ID LSP and LSP without TLVs */ + if (LSP_PSEUDO_ID(lsp->hdr.lsp_id)) + continue; + if (!lsp->tlvs) + continue; + + /* Parse LSP, then fragment */ + parse_lsp(area, level, &srn, lsp); + for (ALL_LIST_ELEMENTS_RO(lsp->lspu.frags, node, frag)) + parse_lsp(area, level, &srn, frag); + } + } +} + +/** + * Process any new/deleted/modified Prefix-SID in the LSPDB. + * + * @param srn Segment Routing Node + * @param srp Segment Routing Prefix + */ +static void process_prefix_changes(struct sr_node *srn, struct sr_prefix *srp) +{ + struct isis_area *area = srn->area; + + /* Install/reinstall/uninstall Prefix-SID if necessary. */ + switch (srp->state) { + case SRDB_STATE_NEW: + sr_debug("ISIS-Sr (%s): Created Prefix-SID %pFX for SR node %s", + area->area_tag, &srp->prefix, sysid_print(srn->sysid)); + sr_prefix_install(srp); + break; + case SRDB_STATE_MODIFIED: + sr_debug( + "ISIS-Sr (%s): Modified Prefix-SID %pFX for SR node %s", + area->area_tag, &srp->prefix, sysid_print(srn->sysid)); + sr_prefix_reinstall(srp, false); + break; + case SRDB_STATE_UNCHANGED: + break; + default: + sr_debug("ISIS-Sr (%s): Removed Prefix-SID %pFX for SR node %s", + area->area_tag, &srp->prefix, sysid_print(srn->sysid)); + sr_prefix_del(area, srn, srp); + return; + } + + /* Validate SRDB State for next LSPDB parsing */ + srp->state = SRDB_STATE_VALIDATED; +} + +/** + * Process any new/deleted/modified SRGB in the LSPDB. + * + * @param area IS-IS area + * @param level IS-IS level + * @param srn Segment Routing Node + */ +static void process_node_changes(struct isis_area *area, int level, + struct sr_node *srn) +{ + struct sr_prefix *srp; + uint8_t sysid[ISIS_SYS_ID_LEN]; + bool adjacent; + + memcpy(sysid, srn->sysid, sizeof(sysid)); + + /* + * If an neighbor router's SRGB was changed or created, then reinstall + * all Prefix-SIDs from all nodes that use this neighbor as nexthop. + */ + adjacent = !!isis_adj_find(area, level, sysid); + switch (srn->state) { + case SRDB_STATE_NEW: + case SRDB_STATE_MODIFIED: + sr_debug("ISIS-Sr (%s): Create/Update SR node %s", + area->area_tag, sysid_print(srn->sysid)); + if (adjacent) + sr_node_srgb_update(area, level, sysid); + break; + case SRDB_STATE_UNCHANGED: + break; + default: + /* SR capabilities have been removed. Delete SR-Node */ + sr_debug("ISIS-Sr (%s): Remove SR node %s", area->area_tag, + sysid_print(srn->sysid)); + + sr_node_del(area, level, srn); + /* and Update remaining Prefix-SID from all remaining SR Node */ + if (adjacent) + sr_node_srgb_update(area, level, sysid); + return; + } + + /* Validate SRDB State for next LSPDB parsing */ + srn->state = SRDB_STATE_VALIDATED; + + /* Finally, process all Prefix-SID of this SR Node */ + frr_each_safe (srdb_node_prefix, &srn->prefix_sids, srp) + process_prefix_changes(srn, srp); +} + +/** + * Parse and process all SR-related Sub-TLVs after running the SPF algorithm. + * + * @param area IS-IS area + */ +void isis_area_verify_sr(struct isis_area *area) +{ + struct sr_node *srn; + + if (!area->srdb.enabled) + return; + + /* Parse LSPDB to detect new/deleted/modified SR (sub-)TLVs. */ + parse_lspdb(area); + + /* Process possible SR-related changes in the LDPSB. */ + for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) { + frr_each_safe (srdb_node, &area->srdb.sr_nodes[level - 1], srn) + process_node_changes(area, level, srn); + } +} + +/** + * Once a route is updated in the SPT, reinstall or uninstall its corresponding + * Prefix-SID (if any). + * + * @param area IS-IS area + * @param prefix Prefix to be updated + * @param route_info New Route Information + * + * @return 0 + */ +static int sr_route_update(struct isis_area *area, struct prefix *prefix, + struct isis_route_info *route_info) +{ + struct sr_prefix *srp; + + if (!area->srdb.enabled) + return 0; + + sr_debug("ISIS-Sr (%s): Update route for prefix %pFX", area->area_tag, + prefix); + + /* Lookup to Segment Routing Prefix for this prefix */ + switch (area->is_type) { + case IS_LEVEL_1: + srp = sr_prefix_find_by_area(area, ISIS_LEVEL1, prefix); + break; + case IS_LEVEL_2: + srp = sr_prefix_find_by_area(area, ISIS_LEVEL2, prefix); + break; + case IS_LEVEL_1_AND_2: + srp = sr_prefix_find_by_area(area, ISIS_LEVEL1, prefix); + if (!srp) + srp = sr_prefix_find_by_area(area, ISIS_LEVEL2, prefix); + break; + default: + flog_err(EC_LIB_DEVELOPMENT, "%s: unknown area level", + __func__); + exit(1); + } + + /* Skip NULL or local Segment Routing Prefix */ + if (!srp || srp->type == ISIS_SR_PREFIX_LOCAL) + return 0; + + /* Install or unintall Prefix-SID if route is Active or not */ + if (CHECK_FLAG(route_info->flag, ISIS_ROUTE_FLAG_ACTIVE)) { + /* + * The Prefix-SID input label hasn't changed. We could use the + * "Make Before Break" option. Zebra layer will update output + * label by adding new label(s) before removing old one(s). + */ + sr_prefix_reinstall(srp, true); + srp->u.remote.rinfo = route_info; + } else { + sr_prefix_uninstall(srp); + srp->u.remote.rinfo = NULL; + } + + return 0; +} + +/* --- Segment Routing Local Block management functions --------------------- */ + +/** + * Initialize Segment Routing Local Block from SRDB configuration and reserve + * block of bits to manage label allocation. + * + * @param area IS-IS area + */ +static int sr_local_block_init(struct isis_area *area) +{ + struct isis_sr_db *srdb = &area->srdb; + struct sr_local_block *srlb = &srdb->srlb; + + /* Check if SRLB is not already configured */ + if (srlb->active) + return 0; + + /* + * Request SRLB to the label manager. If the allocation fails, return + * an error to disable SR until a new SRLB is successfully allocated. + */ + if (isis_zebra_request_label_range( + srdb->config.srlb_lower_bound, + srdb->config.srlb_upper_bound + - srdb->config.srlb_lower_bound + 1)) { + srlb->active = false; + return -1; + } + + sr_debug("ISIS-Sr (%s): Got new SRLB [%u/%u]", area->area_tag, + srdb->config.srlb_lower_bound, srdb->config.srlb_upper_bound); + + /* Initialize the SRLB */ + srlb->start = srdb->config.srlb_lower_bound; + srlb->end = srdb->config.srlb_upper_bound; + srlb->current = 0; + /* Compute the needed Used Mark number and allocate them */ + srlb->max_block = (srlb->end - srlb->start + 1) / SRLB_BLOCK_SIZE; + if (((srlb->end - srlb->start + 1) % SRLB_BLOCK_SIZE) != 0) + srlb->max_block++; + srlb->used_mark = XCALLOC(MTYPE_ISIS_SR_INFO, + srlb->max_block * SRLB_BLOCK_SIZE); + srlb->active = true; + + return 0; +} + +/** + * Remove Segment Routing Local Block. + * + * @param area IS-IS area + */ +static void sr_local_block_delete(struct isis_area *area) +{ + struct isis_sr_db *srdb = &area->srdb; + struct sr_local_block *srlb = &srdb->srlb; + + /* Check if SRLB is not already delete */ + if (!srlb->active) + return; + + sr_debug("ISIS-Sr (%s): Remove SRLB [%u/%u]", area->area_tag, + srlb->start, srlb->end); + + /* First release the label block */ + isis_zebra_release_label_range(srdb->config.srlb_lower_bound, + srdb->config.srlb_upper_bound); + + /* Then reset SRLB structure */ + if (srlb->used_mark != NULL) + XFREE(MTYPE_ISIS_SR_INFO, srlb->used_mark); + srlb->active = false; +} + +/** + * Request a label from the Segment Routing Local Block. + * + * @param srlb Segment Routing Local Block + * + * @return First available label on success or MPLS_INVALID_LABEL if the + * block of labels is full + */ +static mpls_label_t sr_local_block_request_label(struct sr_local_block *srlb) +{ + + mpls_label_t label; + uint32_t index; + uint32_t pos; + + /* Check if we ran out of available labels */ + if (srlb->current >= srlb->end) + return MPLS_INVALID_LABEL; + + /* Get first available label and mark it used */ + label = srlb->current + srlb->start; + index = srlb->current / SRLB_BLOCK_SIZE; + pos = 1ULL << (srlb->current % SRLB_BLOCK_SIZE); + srlb->used_mark[index] |= pos; + + /* Jump to the next free position */ + srlb->current++; + pos = srlb->current % SRLB_BLOCK_SIZE; + while (srlb->current < srlb->end) { + if (pos == 0) + index++; + if (!((1ULL << pos) & srlb->used_mark[index])) + break; + else { + srlb->current++; + pos = srlb->current % SRLB_BLOCK_SIZE; + } + } + + return label; +} + +/** + * Release label in the Segment Routing Local Block. + * + * @param srlb Segment Routing Local Block + * @param label Label to be release + * + * @return 0 on success or -1 if label falls outside SRLB + */ +static int sr_local_block_release_label(struct sr_local_block *srlb, + mpls_label_t label) +{ + uint32_t index; + uint32_t pos; + + /* Check that label falls inside the SRLB */ + if ((label < srlb->start) || (label > srlb->end)) { + flog_warn(EC_ISIS_SID_OVERFLOW, + "%s: Returning label %u is outside SRLB [%u/%u]", + __func__, label, srlb->start, srlb->end); + return -1; + } + + index = (label - srlb->start) / SRLB_BLOCK_SIZE; + pos = 1ULL << ((label - srlb->start) % SRLB_BLOCK_SIZE); + srlb->used_mark[index] &= ~pos; + /* Reset current to the first available position */ + for (index = 0; index < srlb->max_block; index++) { + if (srlb->used_mark[index] != 0xFFFFFFFFFFFFFFFF) { + for (pos = 0; pos < SRLB_BLOCK_SIZE; pos++) + if (!((1ULL << pos) & srlb->used_mark[index])) { + srlb->current = + index * SRLB_BLOCK_SIZE + pos; + break; + } + break; + } + } + + return 0; +} + +/* --- Segment Routing Adjacency-SID management functions ------------------- */ + +/** + * Add new local Adjacency-SID. + * + * @param adj IS-IS Adjacency + * @param family Inet Family (IPv4 or IPv6) + * @param backup True to initialize backup Adjacency SID + */ +static void sr_adj_sid_add_single(struct isis_adjacency *adj, int family, + bool backup) +{ + struct isis_circuit *circuit = adj->circuit; + struct isis_area *area = circuit->area; + struct sr_adjacency *sra; + struct isis_adj_sid *adj_sid; + struct isis_lan_adj_sid *ladj_sid; + union g_addr nexthop = {}; + uint8_t flags; + mpls_label_t input_label; + + sr_debug("ISIS-Sr (%s): Add %s Adjacency SID", area->area_tag, + backup ? "Backup" : "Primary"); + + /* Determine nexthop IP address */ + switch (family) { + case AF_INET: + if (!circuit->ip_router || !adj->ipv4_address_count) + return; + + nexthop.ipv4 = adj->ipv4_addresses[0]; + break; + case AF_INET6: + if (!circuit->ipv6_router || !adj->ipv6_address_count) + return; + + nexthop.ipv6 = adj->ipv6_addresses[0]; + break; + default: + flog_err(EC_LIB_DEVELOPMENT, + "%s: unexpected address-family: %u", __func__, family); + exit(1); + } + + /* Prepare Segment Routing Adjacency as per RFC8667 section #2.2 */ + flags = EXT_SUBTLV_LINK_ADJ_SID_VFLG | EXT_SUBTLV_LINK_ADJ_SID_LFLG; + if (family == AF_INET6) + SET_FLAG(flags, EXT_SUBTLV_LINK_ADJ_SID_FFLG); + if (backup) + SET_FLAG(flags, EXT_SUBTLV_LINK_ADJ_SID_BFLG); + + /* Get a label from the SRLB for this Adjacency */ + input_label = sr_local_block_request_label(&area->srdb.srlb); + if (input_label == MPLS_INVALID_LABEL) + return; + + if (circuit->ext == NULL) + circuit->ext = isis_alloc_ext_subtlvs(); + + sra = XCALLOC(MTYPE_ISIS_SR_INFO, sizeof(*sra)); + sra->type = backup ? ISIS_SR_LAN_BACKUP : ISIS_SR_ADJ_NORMAL; + sra->nexthop.family = family; + sra->nexthop.address = nexthop; + sra->nexthop.label = input_label; + switch (circuit->circ_type) { + /* LAN Adjacency-SID for Broadcast interface section #2.2.2 */ + case CIRCUIT_T_BROADCAST: + ladj_sid = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*ladj_sid)); + ladj_sid->family = family; + ladj_sid->flags = flags; + ladj_sid->weight = 0; + memcpy(ladj_sid->neighbor_id, adj->sysid, + sizeof(ladj_sid->neighbor_id)); + ladj_sid->sid = input_label; + isis_tlvs_add_lan_adj_sid(circuit->ext, ladj_sid); + sra->u.ladj_sid = ladj_sid; + break; + /* Adjacency-SID for Point to Point interface section #2.2.1 */ + case CIRCUIT_T_P2P: + adj_sid = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*adj_sid)); + adj_sid->family = family; + adj_sid->flags = flags; + adj_sid->weight = 0; + adj_sid->sid = input_label; + isis_tlvs_add_adj_sid(circuit->ext, adj_sid); + sra->u.adj_sid = adj_sid; + break; + default: + flog_err(EC_LIB_DEVELOPMENT, "%s: unexpected circuit type: %u", + __func__, circuit->circ_type); + exit(1); + } + + /* Add Adjacency-SID in SRDB */ + sra->adj = adj; + listnode_add(area->srdb.adj_sids, sra); + listnode_add(adj->adj_sids, sra); + + isis_zebra_send_adjacency_sid(ZEBRA_MPLS_LABELS_ADD, sra); +} + +/** + * Add Primary and Backup local Adjacency SID. + * + * @param adj IS-IS Adjacency + * @param family Inet Family (IPv4 or IPv6) + */ +static void sr_adj_sid_add(struct isis_adjacency *adj, int family) +{ + sr_adj_sid_add_single(adj, family, false); + sr_adj_sid_add_single(adj, family, true); +} + +static void sr_adj_sid_update(struct sr_adjacency *sra, + struct sr_local_block *srlb) +{ + struct isis_circuit *circuit = sra->adj->circuit; + + /* First remove the old MPLS Label */ + isis_zebra_send_adjacency_sid(ZEBRA_MPLS_LABELS_DELETE, sra); + + /* Got new label in the new SRLB */ + sra->nexthop.label = sr_local_block_request_label(srlb); + if (sra->nexthop.label == MPLS_INVALID_LABEL) + return; + + switch (circuit->circ_type) { + case CIRCUIT_T_BROADCAST: + sra->u.ladj_sid->sid = sra->nexthop.label; + break; + case CIRCUIT_T_P2P: + sra->u.adj_sid->sid = sra->nexthop.label; + break; + default: + flog_warn(EC_LIB_DEVELOPMENT, "%s: unexpected circuit type: %u", + __func__, circuit->circ_type); + break; + } + + /* Finally configure the new MPLS Label */ + isis_zebra_send_adjacency_sid(ZEBRA_MPLS_LABELS_ADD, sra); +} + +/** + * Delete local Adj-SID. + * + * @param sra Segment Routing Adjacency + */ +static void sr_adj_sid_del(struct sr_adjacency *sra) +{ + struct isis_circuit *circuit = sra->adj->circuit; + struct isis_area *area = circuit->area; + + sr_debug("ISIS-Sr (%s): Delete Adjacency SID", area->area_tag); + + isis_zebra_send_adjacency_sid(ZEBRA_MPLS_LABELS_DELETE, sra); + + /* Release dynamic label and remove subTLVs */ + switch (circuit->circ_type) { + case CIRCUIT_T_BROADCAST: + sr_local_block_release_label(&area->srdb.srlb, + sra->u.ladj_sid->sid); + isis_tlvs_del_lan_adj_sid(circuit->ext, sra->u.ladj_sid); + break; + case CIRCUIT_T_P2P: + sr_local_block_release_label(&area->srdb.srlb, + sra->u.adj_sid->sid); + isis_tlvs_del_adj_sid(circuit->ext, sra->u.adj_sid); + break; + default: + flog_err(EC_LIB_DEVELOPMENT, "%s: unexpected circuit type: %u", + __func__, circuit->circ_type); + exit(1); + } + + /* Remove Adjacency-SID from the SRDB */ + listnode_delete(area->srdb.adj_sids, sra); + listnode_delete(sra->adj->adj_sids, sra); + XFREE(MTYPE_ISIS_SR_INFO, sra); +} + +/** + * Remove all Adjacency-SIDs associated to an adjacency that is going down. + * + * @param adj IS-IS Adjacency + * + * @return 0 + */ +static int sr_adj_state_change(struct isis_adjacency *adj) +{ + struct sr_adjacency *sra; + struct listnode *node, *nnode; + + if (!adj->circuit->area->srdb.enabled) + return 0; + + if (adj->adj_state == ISIS_ADJ_UP) + return 0; + + for (ALL_LIST_ELEMENTS(adj->adj_sids, node, nnode, sra)) + sr_adj_sid_del(sra); + + return 0; +} + +/** + * When IS-IS Adjacency got one or more IPv4/IPv6 addresses, add new IPv4 or + * IPv6 address to corresponding Adjacency-SID accordingly. + * + * @param adj IS-IS Adjacency + * @param family Inet Family (IPv4 or IPv6) + * + * @return 0 + */ +static int sr_adj_ip_enabled(struct isis_adjacency *adj, int family) +{ + if (!adj->circuit->area->srdb.enabled) + return 0; + + sr_adj_sid_add(adj, family); + + return 0; +} + +/** + * When IS-IS Adjacency doesn't have any IPv4 or IPv6 addresses anymore, + * delete the corresponding Adjacency-SID(s) accordingly. + * + * @param adj IS-IS Adjacency + * @param family Inet Family (IPv4 or IPv6) + * + * @return 0 + */ +static int sr_adj_ip_disabled(struct isis_adjacency *adj, int family) +{ + struct sr_adjacency *sra; + struct listnode *node, *nnode; + + if (!adj->circuit->area->srdb.enabled) + return 0; + + for (ALL_LIST_ELEMENTS(adj->adj_sids, node, nnode, sra)) + if (sra->nexthop.family == family) + sr_adj_sid_del(sra); + + return 0; +} + +/** + * Activate local Prefix-SID when loopback interface goes up for IS-IS. + * + * @param ifp Loopback Interface + * + * @return 0 + */ +static int sr_if_new_hook(struct interface *ifp) +{ + struct isis_circuit *circuit; + struct isis_area *area; + struct connected *connected; + struct listnode *node; + + /* Get corresponding circuit */ + circuit = circuit_scan_by_ifp(ifp); + if (!circuit) + return 0; + + area = circuit->area; + if (!area) + return 0; + + /* + * Update the Node-SID flag of the configured Prefix-SID mappings if + * necessary. This needs to be done here since isisd reads the startup + * configuration before receiving interface information from zebra. + */ + FOR_ALL_INTERFACES_ADDRESSES (ifp, connected, node) { + struct sr_prefix_cfg *pcfg; + + pcfg = isis_sr_cfg_prefix_find(area, connected->address); + if (!pcfg) + continue; + + if (sr_prefix_is_node_sid(ifp, &pcfg->prefix) + && !pcfg->node_sid) { + pcfg->node_sid = true; + lsp_regenerate_schedule(area, area->is_type, 0); + } + } + + return 0; +} + +/* --- Segment Routing Show information functions --------------------------- */ + +/** + * Show LFIB operation in human readable format. + * + * @param buf Buffer to store string output. Must be pre-allocate + * @param size Size of the buffer + * @param label_in Input Label + * @param label_out Output Label + * + * @return String containing LFIB operation in human readable format + */ +static char *sr_op2str(char *buf, size_t size, mpls_label_t label_in, + mpls_label_t label_out) +{ + if (size < 24) + return NULL; + + if (label_in == MPLS_INVALID_LABEL) { + snprintf(buf, size, "no-op."); + return buf; + } + + switch (label_out) { + case MPLS_LABEL_IMPLICIT_NULL: + snprintf(buf, size, "Pop(%u)", label_in); + break; + case MPLS_LABEL_IPV4_EXPLICIT_NULL: + case MPLS_LABEL_IPV6_EXPLICIT_NULL: + snprintf(buf, size, "Swap(%u, null)", label_in); + break; + case MPLS_INVALID_LABEL: + snprintf(buf, size, "no-op."); + break; + default: + snprintf(buf, size, "Swap(%u, %u)", label_in, label_out); + break; + } + return buf; +} + +/** + * Show Local Prefix-SID. + * + * @param vty VTY output + * @param tt Table format + * @param area IS-IS area + * @param srp Segment Routing Prefix + */ +static void show_prefix_sid_local(struct vty *vty, struct ttable *tt, + const struct isis_area *area, + const struct sr_prefix *srp) +{ + const struct sr_nexthop_info *srnh = &srp->u.local.info; + char buf_prefix[BUFSIZ]; + char buf_oper[BUFSIZ]; + char buf_iface[BUFSIZ]; + char buf_uptime[BUFSIZ]; + + if (srnh->label != MPLS_INVALID_LABEL) { + struct interface *ifp; + ifp = if_lookup_prefix(&srp->prefix, VRF_DEFAULT); + if (ifp) + strlcpy(buf_iface, ifp->name, sizeof(buf_iface)); + else + snprintf(buf_iface, sizeof(buf_iface), "-"); + log_uptime(srnh->uptime, buf_uptime, sizeof(buf_uptime)); + } else { + snprintf(buf_iface, sizeof(buf_iface), "-"); + snprintf(buf_uptime, sizeof(buf_uptime), "-"); + } + sr_op2str(buf_oper, sizeof(buf_oper), srp->input_label, + MPLS_LABEL_IMPLICIT_NULL); + + ttable_add_row(tt, "%s|%u|%s|-|%s|%s", + prefix2str(&srp->prefix, buf_prefix, sizeof(buf_prefix)), + srp->sid.value, buf_oper, buf_iface, buf_uptime); +} + +/** + * Show Remote Prefix-SID. + * + * @param vty VTY output + * @param tt Table format + * @param area IS-IS area + * @param srp Segment Routing Prefix + */ +static void show_prefix_sid_remote(struct vty *vty, struct ttable *tt, + const struct isis_area *area, + const struct sr_prefix *srp) +{ + struct isis_nexthop *nexthop; + struct listnode *node; + char buf_prefix[BUFSIZ]; + char buf_oper[BUFSIZ]; + char buf_nhop[BUFSIZ]; + char buf_iface[BUFSIZ]; + char buf_uptime[BUFSIZ]; + bool first = true; + + (void)prefix2str(&srp->prefix, buf_prefix, sizeof(buf_prefix)); + + if (!srp->u.remote.rinfo) { + ttable_add_row(tt, "%s|%u|%s|-|-|-", buf_prefix, srp->sid.value, + sr_op2str(buf_oper, sizeof(buf_oper), + srp->input_label, + MPLS_LABEL_IMPLICIT_NULL)); + return; + } + + for (ALL_LIST_ELEMENTS_RO(srp->u.remote.rinfo->nexthops, node, + nexthop)) { + struct interface *ifp; + + inet_ntop(nexthop->family, &nexthop->ip, buf_nhop, + sizeof(buf_nhop)); + ifp = if_lookup_by_index(nexthop->ifindex, VRF_DEFAULT); + if (ifp) + strlcpy(buf_iface, ifp->name, sizeof(buf_iface)); + else + snprintf(buf_iface, sizeof(buf_iface), "ifindex %u", + nexthop->ifindex); + if (nexthop->sr.label == MPLS_INVALID_LABEL) + snprintf(buf_uptime, sizeof(buf_uptime), "-"); + else + log_uptime(nexthop->sr.uptime, buf_uptime, + sizeof(buf_uptime)); + sr_op2str(buf_oper, sizeof(buf_oper), srp->input_label, + nexthop->sr.label); + + if (first) + ttable_add_row(tt, "%s|%u|%s|%s|%s|%s", buf_prefix, + srp->sid.value, buf_oper, buf_nhop, + buf_iface, buf_uptime); + else + ttable_add_row(tt, "|||%s|%s|%s|%s", buf_oper, buf_nhop, + buf_iface, buf_uptime); + first = false; + } +} + +/** + * Show Prefix-SIDs. + * + * @param vty VTY output + * @param area IS-IS area + * @param level IS-IS level + */ +static void show_prefix_sids(struct vty *vty, struct isis_area *area, int level) +{ + struct sr_prefix *srp; + struct ttable *tt; + + if (srdb_area_prefix_count(&area->srdb.prefix_sids[level - 1]) == 0) + return; + + vty_out(vty, " IS-IS %s Prefix-SIDs:\n\n", circuit_t2string(level)); + + /* Prepare table. */ + tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]); + ttable_add_row(tt, "Prefix|SID|Label Op.|Nexthop|Interface|Uptime"); + tt->style.cell.rpad = 2; + tt->style.corner = '+'; + ttable_restyle(tt); + ttable_rowseps(tt, 0, BOTTOM, true, '-'); + + /* Process all Prefix-SID from the SRDB */ + frr_each (srdb_area_prefix, &area->srdb.prefix_sids[level - 1], srp) { + switch (srp->type) { + case ISIS_SR_PREFIX_LOCAL: + show_prefix_sid_local(vty, tt, area, srp); + break; + case ISIS_SR_PREFIX_REMOTE: + show_prefix_sid_remote(vty, tt, area, srp); + break; + } + } + + /* Dump the generated table. */ + if (tt->nrows > 1) { + char *table; + + table = ttable_dump(tt, "\n"); + vty_out(vty, "%s\n", table); + XFREE(MTYPE_TMP, table); + } + ttable_del(tt); +} + +/** + * Declaration of new show commands. + */ +DEFUN(show_sr_prefix_sids, show_sr_prefix_sids_cmd, + "show isis [vrf ] segment-routing prefix-sids", + SHOW_STR PROTO_HELP VRF_CMD_HELP_STR + "All VRFs\n" + "Segment-Routing\n" + "Segment-Routing Prefix-SIDs\n") +{ + struct listnode *node, *inode; + struct isis_area *area; + struct isis *isis = NULL; + const char *vrf_name = VRF_DEFAULT_NAME; + bool all_vrf = false; + int idx_vrf = 0; + + ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + if (vrf_name) { + if (all_vrf) { + for (ALL_LIST_ELEMENTS_RO(im->isis, inode, isis)) { + for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, + area)) { + vty_out(vty, "Area %s:\n", + area->area_tag ? area->area_tag + : "null"); + for (int level = ISIS_LEVEL1; + level <= ISIS_LEVELS; level++) + show_prefix_sids(vty, area, + level); + } + } + return 0; + } + isis = isis_lookup_by_vrfname(vrf_name); + if (isis != NULL) { + for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, + area)) { + vty_out(vty, "Area %s:\n", + area->area_tag ? area->area_tag + : "null"); + for (int level = ISIS_LEVEL1; + level <= ISIS_LEVELS; level++) + show_prefix_sids(vty, area, level); + } + } + } + + return CMD_SUCCESS; +} + +/** + * Show Segment Routing Node. + * + * @param vty VTY output + * @param area IS-IS area + * @param level IS-IS level + */ +static void show_node(struct vty *vty, struct isis_area *area, int level) +{ + struct sr_node *srn; + struct ttable *tt; + + if (srdb_area_prefix_count(&area->srdb.prefix_sids[level - 1]) == 0) + return; + + vty_out(vty, " IS-IS %s SR-Node:\n\n", circuit_t2string(level)); + + /* Prepare table. */ + tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]); + ttable_add_row(tt, "System ID|SRGB|SRLB|Algorithm|MSD"); + tt->style.cell.rpad = 2; + tt->style.corner = '+'; + ttable_restyle(tt); + ttable_rowseps(tt, 0, BOTTOM, true, '-'); + + /* Process all SR-Node from the SRDB */ + frr_each (srdb_node, &area->srdb.sr_nodes[level - 1], srn) { + ttable_add_row( + tt, "%s|%u - %u|%u - %u|%s|%u", + sysid_print(srn->sysid), + srn->cap.srgb.lower_bound, + srn->cap.srgb.lower_bound + srn->cap.srgb.range_size + - 1, + srn->cap.srlb.lower_bound, + srn->cap.srlb.lower_bound + srn->cap.srlb.range_size + - 1, + srn->cap.algo[0] == SR_ALGORITHM_SPF ? "SPF" : "S-SPF", + srn->cap.msd); + } + + /* Dump the generated table. */ + if (tt->nrows > 1) { + char *table; + + table = ttable_dump(tt, "\n"); + vty_out(vty, "%s\n", table); + XFREE(MTYPE_TMP, table); + } + ttable_del(tt); +} + +DEFUN(show_sr_node, show_sr_node_cmd, + "show isis segment-routing node", + SHOW_STR PROTO_HELP + "Segment-Routing\n" + "Segment-Routing node\n") +{ + struct listnode *node, *inode; + struct isis_area *area; + struct isis *isis; + + for (ALL_LIST_ELEMENTS_RO(im->isis, inode, isis)) { + for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) { + vty_out(vty, "Area %s:\n", + area->area_tag ? area->area_tag : "null"); + + for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; + level++) + show_node(vty, area, level); + } + } + + return CMD_SUCCESS; +} + + +/* --- IS-IS Segment Routing Management function ---------------------------- */ + +/** + * Thread function to re-attempt connection to the Label Manager and thus be + * able to start Segment Routing. + * + * @param start Thread structure that contains area as argument + * + * @return 1 on success + */ +static int sr_start_label_manager(struct thread *start) +{ + struct isis_area *area; + + area = THREAD_ARG(start); + + /* re-attempt to start SR & Label Manager connection */ + isis_sr_start(area); + + return 1; +} + +/** + * Enable SR on the given IS-IS area. + * + * @param area IS-IS area + * + * @return 0 on success, -1 otherwise + */ +int isis_sr_start(struct isis_area *area) +{ + struct isis_sr_db *srdb = &area->srdb; + struct isis_adjacency *adj; + struct listnode *node; + + /* First start Label Manager if not ready */ + if (!isis_zebra_label_manager_ready()) + if (isis_zebra_label_manager_connect() < 0) { + /* Re-attempt to connect to Label Manager in 1 sec. */ + thread_add_timer(master, sr_start_label_manager, area, + 1, &srdb->t_start_lm); + return -1; + } + + /* Label Manager is ready, initialize the SRLB */ + if (sr_local_block_init(area) < 0) + return -1; + + /* + * Request SGRB to the label manager if not already active. If the + * allocation fails, return an error to disable SR until a new SRGB + * is successfully allocated. + */ + if (!srdb->srgb_active) { + if (isis_zebra_request_label_range( + srdb->config.srgb_lower_bound, + srdb->config.srgb_upper_bound + - srdb->config.srgb_lower_bound + 1) + < 0) { + srdb->srgb_active = false; + return -1; + } else + srdb->srgb_active = true; + } + + sr_debug("ISIS-Sr: Starting Segment Routing for area %s", + area->area_tag); + + /* Create Adjacency-SIDs from existing IS-IS Adjacencies. */ + for (ALL_LIST_ELEMENTS_RO(area->adjacency_list, node, adj)) { + if (adj->ipv4_address_count > 0) + sr_adj_sid_add(adj, AF_INET); + if (adj->ipv6_address_count > 0) + sr_adj_sid_add(adj, AF_INET6); + } + + area->srdb.enabled = true; + + /* Regenerate LSPs to advertise Segment Routing capabilities. */ + lsp_regenerate_schedule(area, area->is_type, 0); + + return 0; +} + +/** + * Disable SR on the given IS-IS area. + * + * @param area IS-IS area + */ +void isis_sr_stop(struct isis_area *area) +{ + struct isis_sr_db *srdb = &area->srdb; + struct sr_adjacency *sra; + struct listnode *node, *nnode; + + sr_debug("ISIS-Sr: Stopping Segment Routing for area %s", + area->area_tag); + + /* Disable any re-attempt to connect to Label Manager */ + THREAD_TIMER_OFF(srdb->t_start_lm); + + /* Uninstall all local Adjacency-SIDs. */ + for (ALL_LIST_ELEMENTS(area->srdb.adj_sids, node, nnode, sra)) + sr_adj_sid_del(sra); + + /* Uninstall all Prefix-SIDs from all SR Node. */ + for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) { + while (srdb_node_count(&srdb->sr_nodes[level - 1]) > 0) { + struct sr_node *srn; + + srn = srdb_node_first(&srdb->sr_nodes[level - 1]); + sr_node_del(area, level, srn); + } + } + + /* Release SRGB if active. */ + if (srdb->srgb_active) { + isis_zebra_release_label_range(srdb->config.srgb_lower_bound, + srdb->config.srgb_upper_bound); + srdb->srgb_active = false; + } + + /* Delete SRLB */ + sr_local_block_delete(area); + + area->srdb.enabled = false; + + /* Regenerate LSPs to advertise that the Node is no more SR enable. */ + lsp_regenerate_schedule(area, area->is_type, 0); +} + +/** + * IS-IS Segment Routing initialization for given area. + * + * @param area IS-IS area + */ +void isis_sr_area_init(struct isis_area *area) +{ + struct isis_sr_db *srdb = &area->srdb; + + sr_debug("ISIS-Sr (%s): Initialize Segment Routing SRDB", + area->area_tag); + + /* Initialize Segment Routing Data Base */ + memset(srdb, 0, sizeof(*srdb)); + srdb->adj_sids = list_new(); + + for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) { + srdb_node_init(&srdb->sr_nodes[level - 1]); + srdb_area_prefix_init(&srdb->prefix_sids[level - 1]); + } + + /* Pull defaults from the YANG module. */ +#ifndef FABRICD + srdb->config.enabled = yang_get_default_bool("%s/enabled", ISIS_SR); + srdb->config.srgb_lower_bound = + yang_get_default_uint32("%s/srgb/lower-bound", ISIS_SR); + srdb->config.srgb_upper_bound = + yang_get_default_uint32("%s/srgb/upper-bound", ISIS_SR); + srdb->config.srlb_lower_bound = + yang_get_default_uint32("%s/srlb/lower-bound", ISIS_SR); + srdb->config.srlb_upper_bound = + yang_get_default_uint32("%s/srlb/upper-bound", ISIS_SR); +#else + srdb->config.enabled = false; + srdb->config.srgb_lower_bound = SRGB_LOWER_BOUND; + srdb->config.srgb_upper_bound = SRGB_UPPER_BOUND; + srdb->config.srlb_lower_bound = SRLB_LOWER_BOUND; + srdb->config.srlb_upper_bound = SRLB_UPPER_BOUND; +#endif + srdb->config.msd = 0; + srdb_prefix_cfg_init(&srdb->config.prefix_sids); +} + +/** + * Terminate IS-IS Segment Routing for the given area. + * + * @param area IS-IS area + */ +void isis_sr_area_term(struct isis_area *area) +{ + struct isis_sr_db *srdb = &area->srdb; + + /* Stop Segment Routing */ + if (area->srdb.enabled) + isis_sr_stop(area); + + /* Clear Prefix-SID configuration. */ + while (srdb_prefix_cfg_count(&srdb->config.prefix_sids) > 0) { + struct sr_prefix_cfg *pcfg; + + pcfg = srdb_prefix_cfg_first(&srdb->config.prefix_sids); + isis_sr_cfg_prefix_del(pcfg); + } +} + +/** + * IS-IS Segment Routing global initialization. + */ +void isis_sr_init(void) +{ + install_element(VIEW_NODE, &show_sr_prefix_sids_cmd); + install_element(VIEW_NODE, &show_sr_node_cmd); + + /* Register hooks. */ + hook_register(isis_adj_state_change_hook, sr_adj_state_change); + hook_register(isis_adj_ip_enabled_hook, sr_adj_ip_enabled); + hook_register(isis_adj_ip_disabled_hook, sr_adj_ip_disabled); + hook_register(isis_route_update_hook, sr_route_update); + hook_register(isis_if_new_hook, sr_if_new_hook); +} + +/** + * IS-IS Segment Routing global terminate. + */ +void isis_sr_term(void) +{ + /* Unregister hooks. */ + hook_unregister(isis_adj_state_change_hook, sr_adj_state_change); + hook_unregister(isis_adj_ip_enabled_hook, sr_adj_ip_enabled); + hook_unregister(isis_adj_ip_disabled_hook, sr_adj_ip_disabled); + hook_unregister(isis_route_update_hook, sr_route_update); + hook_unregister(isis_if_new_hook, sr_if_new_hook); +} diff --git a/isisd/isis_sr.h b/isisd/isis_sr.h new file mode 100644 index 0000000000..4379a1dcba --- /dev/null +++ b/isisd/isis_sr.h @@ -0,0 +1,288 @@ +/* + * This is an implementation of Segment Routing for IS-IS as per RFC 8667 + * + * Copyright (C) 2019 Orange http://www.orange.com + * + * Author: Olivier Dugeon + * Contributor: Renato Westphal for NetDEF + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _FRR_ISIS_SR_H +#define _FRR_ISIS_SR_H + +#include "lib/linklist.h" +#include "lib/mpls.h" +#include "lib/nexthop.h" +#include "lib/typesafe.h" + +#include "isisd/isis_tlvs.h" + +/* + * Segment Routing information is transported through the following Sub-TLVs: + * + * Sub-TLV Name Value TLVs + * --------------------------------------------------------------------- + * SID Label 1 + * + * Prefix Segment Identifier 3 135, 235, 236 and 237 + * + * Adjacency Segment Identifier 31 22, 23, 141, 222 and 223 + * LAN Adjacency Segment Identifier 32 22, 23, 141, 222 and 223 + * + * Segment Routing Capability 2 242 + * Segment Routing Algorithm 19 242 + * Node Maximum Stack Depth (MSD) 23 242 + * + * Sub-TLV definitions, serialization and de-serialization are defined + * in isis_tlvs.[c,h]. + */ + +#define SRGB_LOWER_BOUND 16000 +#define SRGB_UPPER_BOUND 23999 +#define SRLB_LOWER_BOUND 15000 +#define SRLB_UPPER_BOUND 15999 + +/* Segment Routing Data Base (SRDB) RB-Tree structure */ +PREDECL_RBTREE_UNIQ(srdb_node) +PREDECL_RBTREE_UNIQ(srdb_node_prefix) +PREDECL_RBTREE_UNIQ(srdb_area_prefix) +PREDECL_RBTREE_UNIQ(srdb_prefix_cfg) + +/* Segment Routing Local Block allocation */ +struct sr_local_block { + bool active; + uint32_t start; + uint32_t end; + uint32_t current; + uint32_t max_block; + uint64_t *used_mark; +}; +#define SRLB_BLOCK_SIZE 64 + +/* Segment Routing Adjacency-SID type. */ +enum sr_adj_type { + ISIS_SR_ADJ_NORMAL = 0, + ISIS_SR_LAN_BACKUP, +}; + +/* Segment Routing Adjacency. */ +struct sr_adjacency { + /* Adjacency type. */ + enum sr_adj_type type; + + /* Adjacency-SID nexthop information. */ + struct { + int family; + union g_addr address; + mpls_label_t label; + } nexthop; + + /* (LAN-)Adjacency-SID Sub-TLV. */ + union { + struct isis_adj_sid *adj_sid; + struct isis_lan_adj_sid *ladj_sid; + } u; + + /* Back pointer to IS-IS adjacency. */ + struct isis_adjacency *adj; +}; + +/* Segment Routing Prefix-SID type. */ +enum sr_prefix_type { + ISIS_SR_PREFIX_LOCAL = 0, + ISIS_SR_PREFIX_REMOTE, +}; + +/* Segment Routing Nexthop Information. */ +struct sr_nexthop_info { + mpls_label_t label; + time_t uptime; +}; + +/* State of Object (SR-Node and SR-Prefix) stored in SRDB */ +enum srdb_state { + SRDB_STATE_VALIDATED = 0, + SRDB_STATE_NEW, + SRDB_STATE_MODIFIED, + SRDB_STATE_UNCHANGED +}; + +/* Segment Routing Prefix-SID. */ +struct sr_prefix { + /* SRDB RB-tree entries. */ + struct srdb_node_prefix_item node_entry; + struct srdb_area_prefix_item area_entry; + + /* IP prefix. */ + struct prefix prefix; + + /* SID value, algorithm and flags subTLVs. */ + struct isis_prefix_sid sid; + + /* Input label value. */ + mpls_label_t input_label; + + /* Prefix-SID type. */ + enum sr_prefix_type type; + union { + struct { + /* Information about this local Prefix-SID. */ + struct sr_nexthop_info info; + } local; + struct { + /* Route associated to this remote Prefix-SID. */ + struct isis_route_info *rinfo; + } remote; + } u; + + /* Backpointer to Segment Routing node. */ + struct sr_node *srn; + + /* SR-Prefix State used while the LSPDB is being parsed. */ + enum srdb_state state; +}; + +/* Segment Routing node. */ +struct sr_node { + /* SRDB RB-tree entry. */ + struct srdb_node_item entry; + + /* IS-IS level: ISIS_LEVEL1 or ISIS_LEVEL2. */ + int level; + + /* IS-IS node identifier. */ + uint8_t sysid[ISIS_SYS_ID_LEN]; + + /* Segment Routing node capabilities (SRGB, SR Algorithms) subTLVs. */ + struct isis_router_cap cap; + + /* List of Prefix-SIDs advertised by this node. */ + struct srdb_node_prefix_head prefix_sids; + + /* Backpointer to IS-IS area. */ + struct isis_area *area; + + /* SR-Node State used while the LSPDB is being parsed. */ + enum srdb_state state; +}; + +/* SID type. NOTE: these values must be in sync with the YANG module. */ +enum sr_sid_value_type { + SR_SID_VALUE_TYPE_INDEX = 0, + SR_SID_VALUE_TYPE_ABSOLUTE = 1, +}; + +#define IS_SID_VALUE(flag) CHECK_FLAG(flag, ISIS_PREFIX_SID_VALUE) + +/* Last Hop Behavior. NOTE: these values must be in sync with the YANG module */ +enum sr_last_hop_behavior { + SR_LAST_HOP_BEHAVIOR_EXP_NULL = 0, + SR_LAST_HOP_BEHAVIOR_NO_PHP = 1, + SR_LAST_HOP_BEHAVIOR_PHP = 2, +}; + +/* Segment Routing Prefix-SID configuration. */ +struct sr_prefix_cfg { + /* SRDB RB-tree entry. */ + struct srdb_prefix_cfg_item entry; + + /* IP prefix. */ + struct prefix prefix; + + /* SID value. */ + uint32_t sid; + + /* SID value type. */ + enum sr_sid_value_type sid_type; + + /* SID last hop behavior. */ + enum sr_last_hop_behavior last_hop_behavior; + + /* Does this Prefix-SID refer to a loopback address (Node-SID)? */ + bool node_sid; + + /* Backpointer to IS-IS area. */ + struct isis_area *area; +}; + +/* Per-area IS-IS Segment Routing Data Base (SRDB). */ +struct isis_sr_db { + /* Global Operational status of Segment Routing. */ + bool enabled; + + /* Thread timer to start Label Manager */ + struct thread *t_start_lm; + + /* List of local Adjacency-SIDs. */ + struct list *adj_sids; + + /* Segment Routing Node information per IS-IS level. */ + struct srdb_node_head sr_nodes[ISIS_LEVELS]; + + /* Segment Routing Prefix-SIDs per IS-IS level. */ + struct srdb_area_prefix_head prefix_sids[ISIS_LEVELS]; + + /* Management of SRLB & SRGB allocation */ + struct sr_local_block srlb; + bool srgb_active; + + /* Area Segment Routing configuration. */ + struct { + /* Administrative status of Segment Routing. */ + bool enabled; + + /* Segment Routing Global Block lower & upper bound. */ + uint32_t srgb_lower_bound; + uint32_t srgb_upper_bound; + + /* Segment Routing Local Block lower & upper bound. */ + uint32_t srlb_lower_bound; + uint32_t srlb_upper_bound; + + /* Maximum SID Depth supported by the node. */ + uint8_t msd; + + /* Prefix-SID mappings. */ + struct srdb_prefix_cfg_head prefix_sids; + } config; +}; + +/* Prototypes. */ +extern int isis_sr_cfg_srgb_update(struct isis_area *area, uint32_t lower_bound, + uint32_t upper_bound); +extern int isis_sr_cfg_srlb_update(struct isis_area *area, uint32_t lower_bound, + uint32_t upper_bound); +extern struct sr_prefix_cfg * +isis_sr_cfg_prefix_add(struct isis_area *area, const struct prefix *prefix); +extern void isis_sr_cfg_prefix_del(struct sr_prefix_cfg *pcfg); +extern struct sr_prefix_cfg * +isis_sr_cfg_prefix_find(struct isis_area *area, union prefixconstptr prefix); +extern void isis_sr_prefix_cfg2subtlv(const struct sr_prefix_cfg *pcfg, + bool external, + struct isis_prefix_sid *psid); +extern void isis_sr_nexthop_update(struct sr_nexthop_info *srnh, + mpls_label_t label); +extern void isis_sr_nexthop_reset(struct sr_nexthop_info *srnh); +extern void isis_area_verify_sr(struct isis_area *area); +extern int isis_sr_start(struct isis_area *area); +extern void isis_sr_stop(struct isis_area *area); +extern void isis_sr_area_init(struct isis_area *area); +extern void isis_sr_area_term(struct isis_area *area); +extern void isis_sr_init(void); +extern void isis_sr_term(void); + +#endif /* _FRR_ISIS_SR_H */ diff --git a/isisd/isis_te.c b/isisd/isis_te.c index 4ea6c2c60d..87c4428155 100644 --- a/isisd/isis_te.c +++ b/isisd/isis_te.c @@ -3,8 +3,9 @@ * * This is an implementation of RFC5305 & RFC 7810 * - * Copyright (C) 2014 Orange Labs - * http://www.orange.com + * Author: Olivier Dugeon + * + * Copyright (C) 2014 - 2019 Orange Labs http://www.orange.com * * This file is part of GNU Zebra. * @@ -47,6 +48,7 @@ #include "isisd/isis_common.h" #include "isisd/isis_flags.h" #include "isisd/isis_circuit.h" +#include "isisd/isis_adjacency.h" #include "isisd/isisd.h" #include "isisd/isis_lsp.h" #include "isisd/isis_pdu.h" @@ -56,431 +58,12 @@ #include "isisd/isis_adjacency.h" #include "isisd/isis_spf.h" #include "isisd/isis_te.h" - -const char *mode2text[] = {"Disable", "Area", "AS", "Emulate"}; +#include "isisd/isis_zebra.h" /*------------------------------------------------------------------------* * Followings are control functions for MPLS-TE parameters management. *------------------------------------------------------------------------*/ -/* Create new MPLS TE Circuit context */ -struct mpls_te_circuit *mpls_te_circuit_new(void) -{ - struct mpls_te_circuit *mtc; - - zlog_debug("ISIS MPLS-TE: Create new MPLS TE Circuit context"); - - mtc = XCALLOC(MTYPE_ISIS_MPLS_TE, sizeof(struct mpls_te_circuit)); - - mtc->status = disable; - mtc->type = STD_TE; - mtc->length = 0; - - return mtc; -} - -/* Copy SUB TLVs parameters into a buffer - No space verification are performed - */ -/* Caller must verify before that there is enough free space in the buffer */ -uint8_t add_te_subtlvs(uint8_t *buf, struct mpls_te_circuit *mtc) -{ - uint8_t size, *tlvs = buf; - - zlog_debug("ISIS MPLS-TE: Add TE Sub TLVs to buffer"); - - if (mtc == NULL) { - zlog_debug( - "ISIS MPLS-TE: Abort! No MPLS TE Circuit available has been specified"); - return 0; - } - - /* Create buffer if not provided */ - if (buf == NULL) { - zlog_debug("ISIS MPLS-TE: Abort! No Buffer has been specified"); - return 0; - } - - /* TE_SUBTLV_ADMIN_GRP */ - if (SUBTLV_TYPE(mtc->admin_grp) != 0) { - size = SUBTLV_SIZE(&(mtc->admin_grp.header)); - memcpy(tlvs, &(mtc->admin_grp), size); - tlvs += size; - } - - /* TE_SUBTLV_LLRI */ - if (SUBTLV_TYPE(mtc->llri) != 0) { - size = SUBTLV_SIZE(&(mtc->llri.header)); - memcpy(tlvs, &(mtc->llri), size); - tlvs += size; - } - - /* TE_SUBTLV_LCLIF_IPADDR */ - if (SUBTLV_TYPE(mtc->local_ipaddr) != 0) { - size = SUBTLV_SIZE(&(mtc->local_ipaddr.header)); - memcpy(tlvs, &(mtc->local_ipaddr), size); - tlvs += size; - } - - /* TE_SUBTLV_RMTIF_IPADDR */ - if (SUBTLV_TYPE(mtc->rmt_ipaddr) != 0) { - size = SUBTLV_SIZE(&(mtc->rmt_ipaddr.header)); - memcpy(tlvs, &(mtc->rmt_ipaddr), size); - tlvs += size; - } - - /* TE_SUBTLV_MAX_BW */ - if (SUBTLV_TYPE(mtc->max_bw) != 0) { - size = SUBTLV_SIZE(&(mtc->max_bw.header)); - memcpy(tlvs, &(mtc->max_bw), size); - tlvs += size; - } - - /* TE_SUBTLV_MAX_RSV_BW */ - if (SUBTLV_TYPE(mtc->max_rsv_bw) != 0) { - size = SUBTLV_SIZE(&(mtc->max_rsv_bw.header)); - memcpy(tlvs, &(mtc->max_rsv_bw), size); - tlvs += size; - } - - /* TE_SUBTLV_UNRSV_BW */ - if (SUBTLV_TYPE(mtc->unrsv_bw) != 0) { - size = SUBTLV_SIZE(&(mtc->unrsv_bw.header)); - memcpy(tlvs, &(mtc->unrsv_bw), size); - tlvs += size; - } - - /* TE_SUBTLV_TE_METRIC */ - if (SUBTLV_TYPE(mtc->te_metric) != 0) { - size = SUBTLV_SIZE(&(mtc->te_metric.header)); - memcpy(tlvs, &(mtc->te_metric), size); - tlvs += size; - } - - /* TE_SUBTLV_AV_DELAY */ - if (SUBTLV_TYPE(mtc->av_delay) != 0) { - size = SUBTLV_SIZE(&(mtc->av_delay.header)); - memcpy(tlvs, &(mtc->av_delay), size); - tlvs += size; - } - - /* TE_SUBTLV_MM_DELAY */ - if (SUBTLV_TYPE(mtc->mm_delay) != 0) { - size = SUBTLV_SIZE(&(mtc->mm_delay.header)); - memcpy(tlvs, &(mtc->mm_delay), size); - tlvs += size; - } - - /* TE_SUBTLV_DELAY_VAR */ - if (SUBTLV_TYPE(mtc->delay_var) != 0) { - size = SUBTLV_SIZE(&(mtc->delay_var.header)); - memcpy(tlvs, &(mtc->delay_var), size); - tlvs += size; - } - - /* TE_SUBTLV_PKT_LOSS */ - if (SUBTLV_TYPE(mtc->pkt_loss) != 0) { - size = SUBTLV_SIZE(&(mtc->pkt_loss.header)); - memcpy(tlvs, &(mtc->pkt_loss), size); - tlvs += size; - } - - /* TE_SUBTLV_RES_BW */ - if (SUBTLV_TYPE(mtc->res_bw) != 0) { - size = SUBTLV_SIZE(&(mtc->res_bw.header)); - memcpy(tlvs, &(mtc->res_bw), size); - tlvs += size; - } - - /* TE_SUBTLV_AVA_BW */ - if (SUBTLV_TYPE(mtc->ava_bw) != 0) { - size = SUBTLV_SIZE(&(mtc->ava_bw.header)); - memcpy(tlvs, &(mtc->ava_bw), size); - tlvs += size; - } - - /* TE_SUBTLV_USE_BW */ - if (SUBTLV_TYPE(mtc->use_bw) != 0) { - size = SUBTLV_SIZE(&(mtc->use_bw.header)); - memcpy(tlvs, &(mtc->use_bw), size); - tlvs += size; - } - - /* Add before this line any other parsing of TLV */ - (void)tlvs; - - /* Update SubTLVs length */ - mtc->length = subtlvs_len(mtc); - - zlog_debug("ISIS MPLS-TE: Add %d bytes length SubTLVs", mtc->length); - - return mtc->length; -} - -/* Compute total Sub-TLVs size */ -uint8_t subtlvs_len(struct mpls_te_circuit *mtc) -{ - int length = 0; - - /* Sanity Check */ - if (mtc == NULL) - return 0; - - /* TE_SUBTLV_ADMIN_GRP */ - if (SUBTLV_TYPE(mtc->admin_grp) != 0) - length += SUBTLV_SIZE(&(mtc->admin_grp.header)); - - /* TE_SUBTLV_LLRI */ - if (SUBTLV_TYPE(mtc->llri) != 0) - length += SUBTLV_SIZE(&mtc->llri.header); - - /* TE_SUBTLV_LCLIF_IPADDR */ - if (SUBTLV_TYPE(mtc->local_ipaddr) != 0) - length += SUBTLV_SIZE(&mtc->local_ipaddr.header); - - /* TE_SUBTLV_RMTIF_IPADDR */ - if (SUBTLV_TYPE(mtc->rmt_ipaddr) != 0) - length += SUBTLV_SIZE(&mtc->rmt_ipaddr.header); - - /* TE_SUBTLV_MAX_BW */ - if (SUBTLV_TYPE(mtc->max_bw) != 0) - length += SUBTLV_SIZE(&mtc->max_bw.header); - - /* TE_SUBTLV_MAX_RSV_BW */ - if (SUBTLV_TYPE(mtc->max_rsv_bw) != 0) - length += SUBTLV_SIZE(&mtc->max_rsv_bw.header); - - /* TE_SUBTLV_UNRSV_BW */ - if (SUBTLV_TYPE(mtc->unrsv_bw) != 0) - length += SUBTLV_SIZE(&mtc->unrsv_bw.header); - - /* TE_SUBTLV_TE_METRIC */ - if (SUBTLV_TYPE(mtc->te_metric) != 0) - length += SUBTLV_SIZE(&mtc->te_metric.header); - - /* TE_SUBTLV_AV_DELAY */ - if (SUBTLV_TYPE(mtc->av_delay) != 0) - length += SUBTLV_SIZE(&mtc->av_delay.header); - - /* TE_SUBTLV_MM_DELAY */ - if (SUBTLV_TYPE(mtc->mm_delay) != 0) - length += SUBTLV_SIZE(&mtc->mm_delay.header); - - /* TE_SUBTLV_DELAY_VAR */ - if (SUBTLV_TYPE(mtc->delay_var) != 0) - length += SUBTLV_SIZE(&mtc->delay_var.header); - - /* TE_SUBTLV_PKT_LOSS */ - if (SUBTLV_TYPE(mtc->pkt_loss) != 0) - length += SUBTLV_SIZE(&mtc->pkt_loss.header); - - /* TE_SUBTLV_RES_BW */ - if (SUBTLV_TYPE(mtc->res_bw) != 0) - length += SUBTLV_SIZE(&mtc->res_bw.header); - - /* TE_SUBTLV_AVA_BW */ - if (SUBTLV_TYPE(mtc->ava_bw) != 0) - length += SUBTLV_SIZE(&mtc->ava_bw.header); - - /* TE_SUBTLV_USE_BW */ - if (SUBTLV_TYPE(mtc->use_bw) != 0) - length += SUBTLV_SIZE(&mtc->use_bw.header); - - /* Check that length is lower than the MAXIMUM SUBTLV size i.e. 256 */ - if (length > MAX_SUBTLV_SIZE) { - mtc->length = 0; - return 0; - } - - mtc->length = (uint8_t)length; - - return mtc->length; -} - -/* Following are various functions to set MPLS TE parameters */ -static void set_circuitparams_admin_grp(struct mpls_te_circuit *mtc, - uint32_t admingrp) -{ - SUBTLV_TYPE(mtc->admin_grp) = TE_SUBTLV_ADMIN_GRP; - SUBTLV_LEN(mtc->admin_grp) = SUBTLV_DEF_SIZE; - mtc->admin_grp.value = htonl(admingrp); - return; -} - -static void __attribute__((unused)) -set_circuitparams_llri(struct mpls_te_circuit *mtc, uint32_t local, - uint32_t remote) -{ - SUBTLV_TYPE(mtc->llri) = TE_SUBTLV_LLRI; - SUBTLV_LEN(mtc->llri) = TE_SUBTLV_LLRI_SIZE; - mtc->llri.local = htonl(local); - mtc->llri.remote = htonl(remote); -} - -void set_circuitparams_local_ipaddr(struct mpls_te_circuit *mtc, - struct in_addr addr) -{ - - SUBTLV_TYPE(mtc->local_ipaddr) = TE_SUBTLV_LOCAL_IPADDR; - SUBTLV_LEN(mtc->local_ipaddr) = SUBTLV_DEF_SIZE; - mtc->local_ipaddr.value.s_addr = addr.s_addr; - return; -} - -void set_circuitparams_rmt_ipaddr(struct mpls_te_circuit *mtc, - struct in_addr addr) -{ - - SUBTLV_TYPE(mtc->rmt_ipaddr) = TE_SUBTLV_RMT_IPADDR; - SUBTLV_LEN(mtc->rmt_ipaddr) = SUBTLV_DEF_SIZE; - mtc->rmt_ipaddr.value.s_addr = addr.s_addr; - return; -} - -static void set_circuitparams_max_bw(struct mpls_te_circuit *mtc, float fp) -{ - SUBTLV_TYPE(mtc->max_bw) = TE_SUBTLV_MAX_BW; - SUBTLV_LEN(mtc->max_bw) = SUBTLV_DEF_SIZE; - mtc->max_bw.value = htonf(fp); - return; -} - -static void set_circuitparams_max_rsv_bw(struct mpls_te_circuit *mtc, float fp) -{ - SUBTLV_TYPE(mtc->max_rsv_bw) = TE_SUBTLV_MAX_RSV_BW; - SUBTLV_LEN(mtc->max_rsv_bw) = SUBTLV_DEF_SIZE; - mtc->max_rsv_bw.value = htonf(fp); - return; -} - -static void set_circuitparams_unrsv_bw(struct mpls_te_circuit *mtc, - int priority, float fp) -{ - /* Note that TLV-length field is the size of array. */ - SUBTLV_TYPE(mtc->unrsv_bw) = TE_SUBTLV_UNRSV_BW; - SUBTLV_LEN(mtc->unrsv_bw) = TE_SUBTLV_UNRSV_SIZE; - mtc->unrsv_bw.value[priority] = htonf(fp); - return; -} - -static void set_circuitparams_te_metric(struct mpls_te_circuit *mtc, - uint32_t te_metric) -{ - SUBTLV_TYPE(mtc->te_metric) = TE_SUBTLV_TE_METRIC; - SUBTLV_LEN(mtc->te_metric) = TE_SUBTLV_TE_METRIC_SIZE; - mtc->te_metric.value[0] = (te_metric >> 16) & 0xFF; - mtc->te_metric.value[1] = (te_metric >> 8) & 0xFF; - mtc->te_metric.value[2] = te_metric & 0xFF; - return; -} - -static void set_circuitparams_inter_as(struct mpls_te_circuit *mtc, - struct in_addr addr, uint32_t as) -{ - - /* Set the Remote ASBR IP address and then the associated AS number */ - SUBTLV_TYPE(mtc->rip) = TE_SUBTLV_RIP; - SUBTLV_LEN(mtc->rip) = SUBTLV_DEF_SIZE; - mtc->rip.value.s_addr = addr.s_addr; - - SUBTLV_TYPE(mtc->ras) = TE_SUBTLV_RAS; - SUBTLV_LEN(mtc->ras) = SUBTLV_DEF_SIZE; - mtc->ras.value = htonl(as); -} - -static void unset_circuitparams_inter_as(struct mpls_te_circuit *mtc) -{ - - /* Reset the Remote ASBR IP address and then the associated AS number */ - SUBTLV_TYPE(mtc->rip) = 0; - SUBTLV_LEN(mtc->rip) = 0; - mtc->rip.value.s_addr = 0; - - SUBTLV_TYPE(mtc->ras) = 0; - SUBTLV_LEN(mtc->ras) = 0; - mtc->ras.value = 0; -} - -static void set_circuitparams_av_delay(struct mpls_te_circuit *mtc, - uint32_t delay, uint8_t anormal) -{ - uint32_t tmp; - /* Note that TLV-length field is the size of array. */ - SUBTLV_TYPE(mtc->av_delay) = TE_SUBTLV_AV_DELAY; - SUBTLV_LEN(mtc->av_delay) = SUBTLV_DEF_SIZE; - tmp = delay & TE_EXT_MASK; - if (anormal) - tmp |= TE_EXT_ANORMAL; - mtc->av_delay.value = htonl(tmp); - return; -} - -static void set_circuitparams_mm_delay(struct mpls_te_circuit *mtc, - uint32_t low, uint32_t high, - uint8_t anormal) -{ - uint32_t tmp; - /* Note that TLV-length field is the size of array. */ - SUBTLV_TYPE(mtc->mm_delay) = TE_SUBTLV_MM_DELAY; - SUBTLV_LEN(mtc->mm_delay) = TE_SUBTLV_MM_DELAY_SIZE; - tmp = low & TE_EXT_MASK; - if (anormal) - tmp |= TE_EXT_ANORMAL; - mtc->mm_delay.low = htonl(tmp); - mtc->mm_delay.high = htonl(high); - return; -} - -static void set_circuitparams_delay_var(struct mpls_te_circuit *mtc, - uint32_t jitter) -{ - /* Note that TLV-length field is the size of array. */ - SUBTLV_TYPE(mtc->delay_var) = TE_SUBTLV_DELAY_VAR; - SUBTLV_LEN(mtc->delay_var) = SUBTLV_DEF_SIZE; - mtc->delay_var.value = htonl(jitter & TE_EXT_MASK); - return; -} - -static void set_circuitparams_pkt_loss(struct mpls_te_circuit *mtc, - uint32_t loss, uint8_t anormal) -{ - uint32_t tmp; - /* Note that TLV-length field is the size of array. */ - SUBTLV_TYPE(mtc->pkt_loss) = TE_SUBTLV_PKT_LOSS; - SUBTLV_LEN(mtc->pkt_loss) = SUBTLV_DEF_SIZE; - tmp = loss & TE_EXT_MASK; - if (anormal) - tmp |= TE_EXT_ANORMAL; - mtc->pkt_loss.value = htonl(tmp); - return; -} - -static void set_circuitparams_res_bw(struct mpls_te_circuit *mtc, float fp) -{ - /* Note that TLV-length field is the size of array. */ - SUBTLV_TYPE(mtc->res_bw) = TE_SUBTLV_RES_BW; - SUBTLV_LEN(mtc->res_bw) = SUBTLV_DEF_SIZE; - mtc->res_bw.value = htonf(fp); - return; -} - -static void set_circuitparams_ava_bw(struct mpls_te_circuit *mtc, float fp) -{ - /* Note that TLV-length field is the size of array. */ - SUBTLV_TYPE(mtc->ava_bw) = TE_SUBTLV_AVA_BW; - SUBTLV_LEN(mtc->ava_bw) = SUBTLV_DEF_SIZE; - mtc->ava_bw.value = htonf(fp); - return; -} - -static void set_circuitparams_use_bw(struct mpls_te_circuit *mtc, float fp) -{ - /* Note that TLV-length field is the size of array. */ - SUBTLV_TYPE(mtc->use_bw) = TE_SUBTLV_USE_BW; - SUBTLV_LEN(mtc->use_bw) = SUBTLV_DEF_SIZE; - mtc->use_bw.value = htonf(fp); - return; -} - /* Main initialization / update function of the MPLS TE Circuit context */ /* Call when interface TE Link parameters are modified */ void isis_link_params_update(struct isis_circuit *circuit, @@ -488,155 +71,222 @@ void isis_link_params_update(struct isis_circuit *circuit, { int i; struct prefix_ipv4 *addr; - struct mpls_te_circuit *mtc; + struct prefix_ipv6 *addr6; + struct isis_ext_subtlvs *ext; + + /* Check if TE is enable or not */ + if (!circuit->area || !IS_MPLS_TE(circuit->area->mta)) + return; /* Sanity Check */ - if ((circuit == NULL) || (ifp == NULL)) + if ((ifp == NULL) || (circuit->state != C_STATE_UP)) return; - zlog_info("MPLS-TE: Initialize circuit parameters for interface %s", - ifp->name); + zlog_debug("TE(%s): Update circuit parameters for interface %s", + circuit->area->area_tag, ifp->name); /* Check if MPLS TE Circuit context has not been already created */ - if (circuit->mtc == NULL) - circuit->mtc = mpls_te_circuit_new(); + if (circuit->ext == NULL) { + circuit->ext = isis_alloc_ext_subtlvs(); + zlog_debug(" |- Allocated new Ext-subTLVs for interface %s", + ifp->name); + } - mtc = circuit->mtc; + ext = circuit->ext; - /* Fulfil MTC TLV from ifp TE Link parameters */ + /* Fulfill Extended subTLVs from interface link parameters */ if (HAS_LINK_PARAMS(ifp)) { - mtc->status = enable; /* STD_TE metrics */ - if (IS_PARAM_SET(ifp->link_params, LP_ADM_GRP)) - set_circuitparams_admin_grp( - mtc, ifp->link_params->admin_grp); - else - SUBTLV_TYPE(mtc->admin_grp) = 0; - - /* If not already set, register local IP addr from ip_addr list - * if it exists */ - if (SUBTLV_TYPE(mtc->local_ipaddr) == 0) { - if (circuit->ip_addrs != NULL - && listcount(circuit->ip_addrs) != 0) { - addr = (struct prefix_ipv4 *)listgetdata( - (struct listnode *)listhead( - circuit->ip_addrs)); - set_circuitparams_local_ipaddr(mtc, - addr->prefix); - } - } - - /* If not already set, try to determine Remote IP addr if - * circuit is P2P */ - if ((SUBTLV_TYPE(mtc->rmt_ipaddr) == 0) - && (circuit->circ_type == CIRCUIT_T_P2P)) { + if (IS_PARAM_SET(ifp->link_params, LP_ADM_GRP)) { + ext->adm_group = ifp->link_params->admin_grp; + SET_SUBTLV(ext, EXT_ADM_GRP); + } else + UNSET_SUBTLV(ext, EXT_ADM_GRP); + + /* If known, register local IPv4 addr from ip_addr list */ + if (circuit->ip_addrs != NULL + && listcount(circuit->ip_addrs) != 0) { + addr = (struct prefix_ipv4 *)listgetdata( + (struct listnode *)listhead(circuit->ip_addrs)); + IPV4_ADDR_COPY(&ext->local_addr, &addr->prefix); + SET_SUBTLV(ext, EXT_LOCAL_ADDR); + } else + UNSET_SUBTLV(ext, EXT_LOCAL_ADDR); + + /* Same for Remote IPv4 address */ + if (circuit->circ_type == CIRCUIT_T_P2P) { struct isis_adjacency *adj = circuit->u.p2p.neighbor; + if (adj && adj->adj_state == ISIS_ADJ_UP && adj->ipv4_address_count) { - set_circuitparams_rmt_ipaddr( - mtc, adj->ipv4_addresses[0]); + IPV4_ADDR_COPY(&ext->neigh_addr, + &adj->ipv4_addresses[0]); + SET_SUBTLV(ext, EXT_NEIGH_ADDR); } - } - - if (IS_PARAM_SET(ifp->link_params, LP_MAX_BW)) - set_circuitparams_max_bw(mtc, ifp->link_params->max_bw); - else - SUBTLV_TYPE(mtc->max_bw) = 0; - - if (IS_PARAM_SET(ifp->link_params, LP_MAX_RSV_BW)) - set_circuitparams_max_rsv_bw( - mtc, ifp->link_params->max_rsv_bw); - else - SUBTLV_TYPE(mtc->max_rsv_bw) = 0; + } else + UNSET_SUBTLV(ext, EXT_NEIGH_ADDR); + + /* If known, register local IPv6 addr from ip_addr list */ + if (circuit->ipv6_non_link != NULL + && listcount(circuit->ipv6_non_link) != 0) { + addr6 = (struct prefix_ipv6 *)listgetdata( + (struct listnode *)listhead( + circuit->ipv6_non_link)); + IPV6_ADDR_COPY(&ext->local_addr6, &addr6->prefix); + SET_SUBTLV(ext, EXT_LOCAL_ADDR6); + } else + UNSET_SUBTLV(ext, EXT_LOCAL_ADDR6); + + /* Same for Remote IPv6 address */ + if (circuit->circ_type == CIRCUIT_T_P2P) { + struct isis_adjacency *adj = circuit->u.p2p.neighbor; - if (IS_PARAM_SET(ifp->link_params, LP_UNRSV_BW)) + if (adj && adj->adj_state == ISIS_ADJ_UP + && adj->ipv6_address_count) { + IPV6_ADDR_COPY(&ext->neigh_addr6, + &adj->ipv6_addresses[0]); + SET_SUBTLV(ext, EXT_NEIGH_ADDR6); + } + } else + UNSET_SUBTLV(ext, EXT_NEIGH_ADDR6); + + if (IS_PARAM_SET(ifp->link_params, LP_MAX_BW)) { + ext->max_bw = ifp->link_params->max_bw; + SET_SUBTLV(ext, EXT_MAX_BW); + } else + UNSET_SUBTLV(ext, EXT_MAX_BW); + + if (IS_PARAM_SET(ifp->link_params, LP_MAX_RSV_BW)) { + ext->max_rsv_bw = ifp->link_params->max_rsv_bw; + SET_SUBTLV(ext, EXT_MAX_RSV_BW); + } else + UNSET_SUBTLV(ext, EXT_MAX_RSV_BW); + + if (IS_PARAM_SET(ifp->link_params, LP_UNRSV_BW)) { for (i = 0; i < MAX_CLASS_TYPE; i++) - set_circuitparams_unrsv_bw( - mtc, i, ifp->link_params->unrsv_bw[i]); - else - SUBTLV_TYPE(mtc->unrsv_bw) = 0; - - if (IS_PARAM_SET(ifp->link_params, LP_TE_METRIC)) - set_circuitparams_te_metric( - mtc, ifp->link_params->te_metric); - else - SUBTLV_TYPE(mtc->te_metric) = 0; - - /* TE metric Extensions */ - if (IS_PARAM_SET(ifp->link_params, LP_DELAY)) - set_circuitparams_av_delay( - mtc, ifp->link_params->av_delay, 0); - else - SUBTLV_TYPE(mtc->av_delay) = 0; + ext->unrsv_bw[i] = + ifp->link_params->unrsv_bw[i]; + SET_SUBTLV(ext, EXT_UNRSV_BW); + } else + UNSET_SUBTLV(ext, EXT_UNRSV_BW); + + if (IS_PARAM_SET(ifp->link_params, LP_TE_METRIC)) { + ext->te_metric = ifp->link_params->te_metric; + SET_SUBTLV(ext, EXT_TE_METRIC); + } else + UNSET_SUBTLV(ext, EXT_TE_METRIC); + + /* TE metric extensions */ + if (IS_PARAM_SET(ifp->link_params, LP_DELAY)) { + ext->delay = ifp->link_params->av_delay; + SET_SUBTLV(ext, EXT_DELAY); + } else + UNSET_SUBTLV(ext, EXT_DELAY); + + if (IS_PARAM_SET(ifp->link_params, LP_MM_DELAY)) { + ext->min_delay = ifp->link_params->min_delay; + ext->max_delay = ifp->link_params->max_delay; + SET_SUBTLV(ext, EXT_MM_DELAY); + } else + UNSET_SUBTLV(ext, EXT_MM_DELAY); + + if (IS_PARAM_SET(ifp->link_params, LP_DELAY_VAR)) { + ext->delay_var = ifp->link_params->delay_var; + SET_SUBTLV(ext, EXT_DELAY_VAR); + } else + UNSET_SUBTLV(ext, EXT_DELAY_VAR); + + if (IS_PARAM_SET(ifp->link_params, LP_PKT_LOSS)) { + ext->pkt_loss = ifp->link_params->pkt_loss; + SET_SUBTLV(ext, EXT_PKT_LOSS); + } else + UNSET_SUBTLV(ext, EXT_PKT_LOSS); + + if (IS_PARAM_SET(ifp->link_params, LP_RES_BW)) { + ext->res_bw = ifp->link_params->res_bw; + SET_SUBTLV(ext, EXT_RES_BW); + } else + UNSET_SUBTLV(ext, EXT_RES_BW); + + if (IS_PARAM_SET(ifp->link_params, LP_AVA_BW)) { + ext->ava_bw = ifp->link_params->ava_bw; + SET_SUBTLV(ext, EXT_AVA_BW); + } else + UNSET_SUBTLV(ext, EXT_AVA_BW); + + if (IS_PARAM_SET(ifp->link_params, LP_USE_BW)) { + ext->use_bw = ifp->link_params->use_bw; + SET_SUBTLV(ext, EXT_USE_BW); + } else + UNSET_SUBTLV(ext, EXT_USE_BW); - if (IS_PARAM_SET(ifp->link_params, LP_MM_DELAY)) - set_circuitparams_mm_delay( - mtc, ifp->link_params->min_delay, - ifp->link_params->max_delay, 0); - else - SUBTLV_TYPE(mtc->mm_delay) = 0; - - if (IS_PARAM_SET(ifp->link_params, LP_DELAY_VAR)) - set_circuitparams_delay_var( - mtc, ifp->link_params->delay_var); - else - SUBTLV_TYPE(mtc->delay_var) = 0; - - if (IS_PARAM_SET(ifp->link_params, LP_PKT_LOSS)) - set_circuitparams_pkt_loss( - mtc, ifp->link_params->pkt_loss, 0); + /* INTER_AS */ + if (IS_PARAM_SET(ifp->link_params, LP_RMT_AS)) { + ext->remote_as = ifp->link_params->rmt_as; + ext->remote_ip = ifp->link_params->rmt_ip; + SET_SUBTLV(ext, EXT_RMT_AS); + SET_SUBTLV(ext, EXT_RMT_IP); + } else { + /* reset inter-as TE params */ + UNSET_SUBTLV(ext, EXT_RMT_AS); + UNSET_SUBTLV(ext, EXT_RMT_IP); + } + zlog_debug(" |- New MPLS-TE link parameters status 0x%x", + ext->status); + } else { + zlog_debug(" |- Reset Extended subTLVs status 0x%x", + ext->status); + /* Reset TE subTLVs keeping SR one's */ + if (IS_SUBTLV(ext, EXT_ADJ_SID)) + ext->status = EXT_ADJ_SID; + else if (IS_SUBTLV(ext, EXT_LAN_ADJ_SID)) + ext->status = EXT_LAN_ADJ_SID; else - SUBTLV_TYPE(mtc->pkt_loss) = 0; + ext->status = 0; + } - if (IS_PARAM_SET(ifp->link_params, LP_RES_BW)) - set_circuitparams_res_bw(mtc, ifp->link_params->res_bw); - else - SUBTLV_TYPE(mtc->res_bw) = 0; + return; +} - if (IS_PARAM_SET(ifp->link_params, LP_AVA_BW)) - set_circuitparams_ava_bw(mtc, ifp->link_params->ava_bw); - else - SUBTLV_TYPE(mtc->ava_bw) = 0; +static int isis_link_update_adj_hook(struct isis_adjacency *adj) +{ - if (IS_PARAM_SET(ifp->link_params, LP_USE_BW)) - set_circuitparams_use_bw(mtc, ifp->link_params->use_bw); - else - SUBTLV_TYPE(mtc->use_bw) = 0; + struct isis_circuit *circuit = adj->circuit; - /* INTER_AS */ - if (IS_PARAM_SET(ifp->link_params, LP_RMT_AS)) - set_circuitparams_inter_as(mtc, - ifp->link_params->rmt_ip, - ifp->link_params->rmt_as); - else - /* reset inter-as TE params */ - unset_circuitparams_inter_as(mtc); + /* Update MPLS TE Remote IP address parameter if possible */ + if (!IS_MPLS_TE(circuit->area->mta) || !IS_EXT_TE(circuit->ext)) + return 0; - /* Compute total length of SUB TLVs */ - mtc->length = subtlvs_len(mtc); + /* IPv4 first */ + if (adj->ipv4_address_count > 0) { + IPV4_ADDR_COPY(&circuit->ext->neigh_addr, + &adj->ipv4_addresses[0]); + SET_SUBTLV(circuit->ext, EXT_NEIGH_ADDR); + } - } else - mtc->status = disable; + /* and IPv6 */ + if (adj->ipv6_address_count > 0) { + IPV6_ADDR_COPY(&circuit->ext->neigh_addr6, + &adj->ipv6_addresses[0]); + SET_SUBTLV(circuit->ext, EXT_NEIGH_ADDR6); + } -/* Finally Update LSP */ -#if 0 - if (circuit->area && IS_MPLS_TE(circuit->area->mta)) - lsp_regenerate_schedule (circuit->area, circuit->is_type, 0); -#endif - return; + return 0; } -void isis_mpls_te_update(struct interface *ifp) +int isis_mpls_te_update(struct interface *ifp) { struct isis_circuit *circuit; + uint8_t rc = 1; /* Sanity Check */ if (ifp == NULL) - return; + return rc; /* Get circuit context from interface */ - if ((circuit = circuit_scan_by_ifp(ifp)) == NULL) - return; + circuit = circuit_scan_by_ifp(ifp); + if (circuit == NULL) + return rc; /* Update TE TLVs ... */ isis_link_params_update(circuit, ifp); @@ -645,492 +295,177 @@ void isis_mpls_te_update(struct interface *ifp) if (circuit->area && IS_MPLS_TE(circuit->area->mta)) lsp_regenerate_schedule(circuit->area, circuit->is_type, 0); - return; -} - -/*------------------------------------------------------------------------* - * Followings are vty session control functions. - *------------------------------------------------------------------------*/ - -static uint8_t print_subtlv_admin_grp(struct sbuf *buf, int indent, - struct te_subtlv_admin_grp *tlv) -{ - sbuf_push(buf, indent, "Administrative Group: 0x%" PRIx32 "\n", - ntohl(tlv->value)); - return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE); -} - -static uint8_t print_subtlv_llri(struct sbuf *buf, int indent, - struct te_subtlv_llri *tlv) -{ - sbuf_push(buf, indent, "Link Local ID: %" PRIu32 "\n", - ntohl(tlv->local)); - sbuf_push(buf, indent, "Link Remote ID: %" PRIu32 "\n", - ntohl(tlv->remote)); - - return (SUBTLV_HDR_SIZE + TE_SUBTLV_LLRI_SIZE); -} - -static uint8_t print_subtlv_local_ipaddr(struct sbuf *buf, int indent, - struct te_subtlv_local_ipaddr *tlv) -{ - sbuf_push(buf, indent, "Local Interface IP Address(es): %s\n", - inet_ntoa(tlv->value)); - - return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE); -} - -static uint8_t print_subtlv_rmt_ipaddr(struct sbuf *buf, int indent, - struct te_subtlv_rmt_ipaddr *tlv) -{ - sbuf_push(buf, indent, "Remote Interface IP Address(es): %s\n", - inet_ntoa(tlv->value)); - - return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE); -} - -static uint8_t print_subtlv_max_bw(struct sbuf *buf, int indent, - struct te_subtlv_max_bw *tlv) -{ - float fval; - - fval = ntohf(tlv->value); - - sbuf_push(buf, indent, "Maximum Bandwidth: %g (Bytes/sec)\n", fval); - - return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE); -} - -static uint8_t print_subtlv_max_rsv_bw(struct sbuf *buf, int indent, - struct te_subtlv_max_rsv_bw *tlv) -{ - float fval; - - fval = ntohf(tlv->value); - - sbuf_push(buf, indent, "Maximum Reservable Bandwidth: %g (Bytes/sec)\n", - fval); - - return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE); -} - -static uint8_t print_subtlv_unrsv_bw(struct sbuf *buf, int indent, - struct te_subtlv_unrsv_bw *tlv) -{ - float fval1, fval2; - int i; - - sbuf_push(buf, indent, "Unreserved Bandwidth:\n"); - - for (i = 0; i < MAX_CLASS_TYPE; i += 2) { - fval1 = ntohf(tlv->value[i]); - fval2 = ntohf(tlv->value[i + 1]); - sbuf_push(buf, indent + 2, - "[%d]: %g (Bytes/sec),\t[%d]: %g (Bytes/sec)\n", i, - fval1, i + 1, fval2); - } - - return (SUBTLV_HDR_SIZE + TE_SUBTLV_UNRSV_SIZE); -} - -static uint8_t print_subtlv_te_metric(struct sbuf *buf, int indent, - struct te_subtlv_te_metric *tlv) -{ - uint32_t te_metric; - - te_metric = tlv->value[2] | tlv->value[1] << 8 | tlv->value[0] << 16; - sbuf_push(buf, indent, "Traffic Engineering Metric: %u\n", te_metric); - - return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE); -} - -static uint8_t print_subtlv_ras(struct sbuf *buf, int indent, - struct te_subtlv_ras *tlv) -{ - sbuf_push(buf, indent, "Inter-AS TE Remote AS number: %" PRIu32 "\n", - ntohl(tlv->value)); - - return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE); -} - -static uint8_t print_subtlv_rip(struct sbuf *buf, int indent, - struct te_subtlv_rip *tlv) -{ - sbuf_push(buf, indent, "Inter-AS TE Remote ASBR IP address: %s\n", - inet_ntoa(tlv->value)); - - return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE); -} - -static uint8_t print_subtlv_av_delay(struct sbuf *buf, int indent, - struct te_subtlv_av_delay *tlv) -{ - uint32_t delay; - uint32_t A; - - delay = (uint32_t)ntohl(tlv->value) & TE_EXT_MASK; - A = (uint32_t)ntohl(tlv->value) & TE_EXT_ANORMAL; - - sbuf_push(buf, indent, - "%s Average Link Delay: %" PRIu32 " (micro-sec)\n", - A ? "Anomalous" : "Normal", delay); - - return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE); -} - -static uint8_t print_subtlv_mm_delay(struct sbuf *buf, int indent, - struct te_subtlv_mm_delay *tlv) -{ - uint32_t low, high; - uint32_t A; - - low = (uint32_t)ntohl(tlv->low) & TE_EXT_MASK; - A = (uint32_t)ntohl(tlv->low) & TE_EXT_ANORMAL; - high = (uint32_t)ntohl(tlv->high) & TE_EXT_MASK; - - sbuf_push(buf, indent, "%s Min/Max Link Delay: %" PRIu32 " / %" PRIu32 " (micro-sec)\n", - A ? "Anomalous" : "Normal", low, high); - - return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE); -} - -static uint8_t print_subtlv_delay_var(struct sbuf *buf, int indent, - struct te_subtlv_delay_var *tlv) -{ - uint32_t jitter; - - jitter = (uint32_t)ntohl(tlv->value) & TE_EXT_MASK; - - sbuf_push(buf, indent, "Delay Variation: %" PRIu32 " (micro-sec)\n", - jitter); - - return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE); -} - -static uint8_t print_subtlv_pkt_loss(struct sbuf *buf, int indent, - struct te_subtlv_pkt_loss *tlv) -{ - uint32_t loss; - uint32_t A; - float fval; - - loss = (uint32_t)ntohl(tlv->value) & TE_EXT_MASK; - fval = (float)(loss * LOSS_PRECISION); - A = (uint32_t)ntohl(tlv->value) & TE_EXT_ANORMAL; - - sbuf_push(buf, indent, "%s Link Packet Loss: %g (%%)\n", - A ? "Anomalous" : "Normal", fval); - - return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE); -} - -static uint8_t print_subtlv_res_bw(struct sbuf *buf, int indent, - struct te_subtlv_res_bw *tlv) -{ - float fval; - - fval = ntohf(tlv->value); - - sbuf_push(buf, indent, - "Unidirectional Residual Bandwidth: %g (Bytes/sec)\n", fval); - - return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE); -} - -static uint8_t print_subtlv_ava_bw(struct sbuf *buf, int indent, - struct te_subtlv_ava_bw *tlv) -{ - float fval; - - fval = ntohf(tlv->value); - - sbuf_push(buf, indent, - "Unidirectional Available Bandwidth: %g (Bytes/sec)\n", fval); - - return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE); -} - -static uint8_t print_subtlv_use_bw(struct sbuf *buf, int indent, - struct te_subtlv_use_bw *tlv) -{ - float fval; - - fval = ntohf(tlv->value); - - sbuf_push(buf, indent, - "Unidirectional Utilized Bandwidth: %g (Bytes/sec)\n", fval); - - return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE); -} - -static uint8_t print_unknown_tlv(struct sbuf *buf, int indent, - struct subtlv_header *tlvh) -{ - int i, rtn; - uint8_t *v = (uint8_t *)tlvh; - - if (tlvh->length != 0) { - sbuf_push(buf, indent, - "Unknown TLV: [type(%#.2x), length(%#.2x)]\n", - tlvh->type, tlvh->length); - sbuf_push(buf, indent + 2, "Dump: [00]"); - rtn = 1; /* initialize end of line counter */ - for (i = 0; i < tlvh->length; i++) { - sbuf_push(buf, 0, " %#.2x", v[i]); - if (rtn == 8) { - sbuf_push(buf, 0, "\n"); - sbuf_push(buf, indent + 8, "[%.2x]", i + 1); - rtn = 1; - } else - rtn++; - } - sbuf_push(buf, 0, "\n"); - } else { - sbuf_push(buf, indent, - "Unknown TLV: [type(%#.2x), length(%#.2x)]\n", - tlvh->type, tlvh->length); - } - - return SUBTLV_SIZE(tlvh); -} - -/* Main Show function */ -void mpls_te_print_detail(struct sbuf *buf, int indent, - uint8_t *subtlvs, uint8_t subtlv_len) -{ - struct subtlv_header *tlvh = (struct subtlv_header *)subtlvs; - uint16_t sum = 0; - - for (; sum < subtlv_len; - tlvh = (struct subtlv_header *)(subtlvs + sum)) { - if (subtlv_len - sum < SUBTLV_SIZE(tlvh)) { - sbuf_push(buf, indent, "Available data %" PRIu8 " is less than TLV size %u!\n", - subtlv_len - sum, SUBTLV_SIZE(tlvh)); - return; - } - - switch (tlvh->type) { - case TE_SUBTLV_ADMIN_GRP: - if (tlvh->length != SUBTLV_DEF_SIZE) { - sbuf_push(buf, indent, "TLV size does not match expected size for Administrative Group!\n"); - return; - } - sum += print_subtlv_admin_grp(buf, indent, - (struct te_subtlv_admin_grp *)tlvh); - break; - case TE_SUBTLV_LLRI: - if (tlvh->length != TE_SUBTLV_LLRI_SIZE) { - sbuf_push(buf, indent, "TLV size does not match expected size for Link ID!\n"); - return; - } - sum += print_subtlv_llri(buf, indent, - (struct te_subtlv_llri *)tlvh); - break; - case TE_SUBTLV_LOCAL_IPADDR: - if (tlvh->length != SUBTLV_DEF_SIZE) { - sbuf_push(buf, indent, "TLV size does not match expected size for Local IP address!\n"); - return; - } - sum += print_subtlv_local_ipaddr(buf, indent, - (struct te_subtlv_local_ipaddr *)tlvh); - break; - case TE_SUBTLV_RMT_IPADDR: - if (tlvh->length != SUBTLV_DEF_SIZE) { - sbuf_push(buf, indent, "TLV size does not match expected size for Remote Interface address!\n"); - return; - } - sum += print_subtlv_rmt_ipaddr(buf, indent, - (struct te_subtlv_rmt_ipaddr *)tlvh); - break; - case TE_SUBTLV_MAX_BW: - if (tlvh->length != SUBTLV_DEF_SIZE) { - sbuf_push(buf, indent, "TLV size does not match expected size for Maximum Bandwidth!\n"); - return; - } - sum += print_subtlv_max_bw(buf, indent, - (struct te_subtlv_max_bw *)tlvh); - break; - case TE_SUBTLV_MAX_RSV_BW: - if (tlvh->length != SUBTLV_DEF_SIZE) { - sbuf_push(buf, indent, "TLV size does not match expected size for Maximum Reservable Bandwidth!\n"); - return; - } - sum += print_subtlv_max_rsv_bw(buf, indent, - (struct te_subtlv_max_rsv_bw *)tlvh); - break; - case TE_SUBTLV_UNRSV_BW: - if (tlvh->length != TE_SUBTLV_UNRSV_SIZE) { - sbuf_push(buf, indent, "TLV size does not match expected size for Unreserved Bandwidth!\n"); - return; - } - sum += print_subtlv_unrsv_bw(buf, indent, - (struct te_subtlv_unrsv_bw *)tlvh); - break; - case TE_SUBTLV_TE_METRIC: - if (tlvh->length != SUBTLV_DEF_SIZE) { - sbuf_push(buf, indent, "TLV size does not match expected size for Traffic Engineering Metric!\n"); - return; - } - sum += print_subtlv_te_metric(buf, indent, - (struct te_subtlv_te_metric *)tlvh); - break; - case TE_SUBTLV_RAS: - if (tlvh->length != SUBTLV_DEF_SIZE) { - sbuf_push(buf, indent, "TLV size does not match expected size for Remote AS number!\n"); - return; - } - sum += print_subtlv_ras(buf, indent, - (struct te_subtlv_ras *)tlvh); - break; - case TE_SUBTLV_RIP: - if (tlvh->length != SUBTLV_DEF_SIZE) { - sbuf_push(buf, indent, "TLV size does not match expected size for Remote ASBR IP Address!\n"); - return; - } - sum += print_subtlv_rip(buf, indent, - (struct te_subtlv_rip *)tlvh); - break; - case TE_SUBTLV_AV_DELAY: - if (tlvh->length != SUBTLV_DEF_SIZE) { - sbuf_push(buf, indent, "TLV size does not match expected size for Average Link Delay!\n"); - return; - } - sum += print_subtlv_av_delay(buf, indent, - (struct te_subtlv_av_delay *)tlvh); - break; - case TE_SUBTLV_MM_DELAY: - if (tlvh->length != SUBTLV_DEF_SIZE) { - sbuf_push(buf, indent, "TLV size does not match expected size for Min/Max Link Delay!\n"); - return; - } - sum += print_subtlv_mm_delay(buf, indent, - (struct te_subtlv_mm_delay *)tlvh); - break; - case TE_SUBTLV_DELAY_VAR: - if (tlvh->length != SUBTLV_DEF_SIZE) { - sbuf_push(buf, indent, "TLV size does not match expected size for Delay Variation!\n"); - return; - } - sum += print_subtlv_delay_var(buf, indent, - (struct te_subtlv_delay_var *)tlvh); - break; - case TE_SUBTLV_PKT_LOSS: - if (tlvh->length != SUBTLV_DEF_SIZE) { - sbuf_push(buf, indent, "TLV size does not match expected size for Link Packet Loss!\n"); - return; - } - sum += print_subtlv_pkt_loss(buf, indent, - (struct te_subtlv_pkt_loss *)tlvh); - break; - case TE_SUBTLV_RES_BW: - if (tlvh->length != SUBTLV_DEF_SIZE) { - sbuf_push(buf, indent, "TLV size does not match expected size for Unidirectional Residual Bandwidth!\n"); - return; - } - sum += print_subtlv_res_bw(buf, indent, - (struct te_subtlv_res_bw *)tlvh); - break; - case TE_SUBTLV_AVA_BW: - if (tlvh->length != SUBTLV_DEF_SIZE) { - sbuf_push(buf, indent, "TLV size does not match expected size for Unidirectional Available Bandwidth!\n"); - return; - } - sum += print_subtlv_ava_bw(buf, indent, - (struct te_subtlv_ava_bw *)tlvh); - break; - case TE_SUBTLV_USE_BW: - if (tlvh->length != SUBTLV_DEF_SIZE) { - sbuf_push(buf, indent, "TLV size does not match expected size for Unidirectional Utilized Bandwidth!\n"); - return; - } - sum += print_subtlv_use_bw(buf, indent, - (struct te_subtlv_use_bw *)tlvh); - break; - default: - sum += print_unknown_tlv(buf, indent, tlvh); - break; - } - } - return; + rc = 0; + return rc; } -/*------------------------------------------------------------------------* - * Followings are vty command functions. - *------------------------------------------------------------------------*/ +/* Followings are vty command functions */ #ifndef FABRICD -DEFUN (show_isis_mpls_te_router, - show_isis_mpls_te_router_cmd, - "show " PROTO_NAME " mpls-te router", - SHOW_STR - PROTO_HELP - MPLS_TE_STR - "Router information\n") +DEFUN(show_isis_mpls_te_router, + show_isis_mpls_te_router_cmd, + "show " PROTO_NAME " [vrf ] mpls-te router", + SHOW_STR + PROTO_HELP + VRF_CMD_HELP_STR "All VRFs\n" + MPLS_TE_STR "Router information\n") { - struct listnode *anode; + struct listnode *anode, *inode; struct isis_area *area; + struct isis *isis = NULL; + const char *vrf_name = VRF_DEFAULT_NAME; + bool all_vrf = false; + int idx_vrf = 0; - if (!isis) { + if (!im) { vty_out(vty, "IS-IS Routing Process not enabled\n"); return CMD_SUCCESS; } - - for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode, area)) { - - if (!IS_MPLS_TE(area->mta)) - continue; - - vty_out(vty, "Area %s:\n", area->area_tag); - if (ntohs(area->mta->router_id.s_addr) != 0) - vty_out(vty, " MPLS-TE Router-Address: %s\n", - inet_ntoa(area->mta->router_id)); - else - vty_out(vty, " N/A\n"); + ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + if (vrf_name) { + if (all_vrf) { + for (ALL_LIST_ELEMENTS_RO(im->isis, inode, isis)) { + for (ALL_LIST_ELEMENTS_RO(isis->area_list, + anode, area)) { + if (!IS_MPLS_TE(area->mta)) + continue; + + vty_out(vty, "Area %s:\n", + area->area_tag); + if (ntohs(area->mta->router_id.s_addr) + != 0) + vty_out(vty, + " MPLS-TE Router-Address: %s\n", + inet_ntoa( + area->mta + ->router_id)); + else + vty_out(vty, " N/A\n"); + } + } + return 0; + } + isis = isis_lookup_by_vrfname(vrf_name); + if (isis != NULL) { + for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode, + area)) { + + if (!IS_MPLS_TE(area->mta)) + continue; + + vty_out(vty, "Area %s:\n", area->area_tag); + if (ntohs(area->mta->router_id.s_addr) != 0) + vty_out(vty, + " MPLS-TE Router-Address: %s\n", + inet_ntoa( + area->mta->router_id)); + else + vty_out(vty, " N/A\n"); + } + } } return CMD_SUCCESS; } -static void show_mpls_te_sub(struct vty *vty, char *name, - struct mpls_te_circuit *mtc) +static void show_ext_sub(struct vty *vty, char *name, + struct isis_ext_subtlvs *ext) { struct sbuf buf; + char ibuf[PREFIX2STR_BUFFER]; sbuf_init(&buf, NULL, 0); - if (mtc->status != enable) + if (!ext || ext->status == EXT_DISABLE) return; vty_out(vty, "-- MPLS-TE link parameters for %s --\n", name); sbuf_reset(&buf); - print_subtlv_admin_grp(&buf, 4, &mtc->admin_grp); - - if (SUBTLV_TYPE(mtc->local_ipaddr) != 0) - print_subtlv_local_ipaddr(&buf, 4, &mtc->local_ipaddr); - if (SUBTLV_TYPE(mtc->rmt_ipaddr) != 0) - print_subtlv_rmt_ipaddr(&buf, 4, &mtc->rmt_ipaddr); - - print_subtlv_max_bw(&buf, 4, &mtc->max_bw); - print_subtlv_max_rsv_bw(&buf, 4, &mtc->max_rsv_bw); - print_subtlv_unrsv_bw(&buf, 4, &mtc->unrsv_bw); - print_subtlv_te_metric(&buf, 4, &mtc->te_metric); - - if (IS_INTER_AS(mtc->type)) { - if (SUBTLV_TYPE(mtc->ras) != 0) - print_subtlv_ras(&buf, 4, &mtc->ras); - if (SUBTLV_TYPE(mtc->rip) != 0) - print_subtlv_rip(&buf, 4, &mtc->rip); - } - print_subtlv_av_delay(&buf, 4, &mtc->av_delay); - print_subtlv_mm_delay(&buf, 4, &mtc->mm_delay); - print_subtlv_delay_var(&buf, 4, &mtc->delay_var); - print_subtlv_pkt_loss(&buf, 4, &mtc->pkt_loss); - print_subtlv_res_bw(&buf, 4, &mtc->res_bw); - print_subtlv_ava_bw(&buf, 4, &mtc->ava_bw); - print_subtlv_use_bw(&buf, 4, &mtc->use_bw); + if (IS_SUBTLV(ext, EXT_ADM_GRP)) + sbuf_push(&buf, 4, "Administrative Group: 0x%x\n", + ext->adm_group); + if (IS_SUBTLV(ext, EXT_LLRI)) { + sbuf_push(&buf, 4, "Link Local ID: %u\n", + ext->local_llri); + sbuf_push(&buf, 4, "Link Remote ID: %u\n", + ext->remote_llri); + } + if (IS_SUBTLV(ext, EXT_LOCAL_ADDR)) + sbuf_push(&buf, 4, "Local Interface IP Address(es): %s\n", + inet_ntoa(ext->local_addr)); + if (IS_SUBTLV(ext, EXT_NEIGH_ADDR)) + sbuf_push(&buf, 4, "Remote Interface IP Address(es): %s\n", + inet_ntoa(ext->neigh_addr)); + if (IS_SUBTLV(ext, EXT_LOCAL_ADDR6)) + sbuf_push(&buf, 4, "Local Interface IPv6 Address(es): %s\n", + inet_ntop(AF_INET6, &ext->local_addr6, ibuf, + PREFIX2STR_BUFFER)); + if (IS_SUBTLV(ext, EXT_NEIGH_ADDR6)) + sbuf_push(&buf, 4, "Remote Interface IPv6 Address(es): %s\n", + inet_ntop(AF_INET6, &ext->local_addr6, ibuf, + PREFIX2STR_BUFFER)); + if (IS_SUBTLV(ext, EXT_MAX_BW)) + sbuf_push(&buf, 4, "Maximum Bandwidth: %g (Bytes/sec)\n", + ext->max_bw); + if (IS_SUBTLV(ext, EXT_MAX_RSV_BW)) + sbuf_push(&buf, 4, + "Maximum Reservable Bandwidth: %g (Bytes/sec)\n", + ext->max_rsv_bw); + if (IS_SUBTLV(ext, EXT_UNRSV_BW)) { + sbuf_push(&buf, 4, "Unreserved Bandwidth:\n"); + for (int j = 0; j < MAX_CLASS_TYPE; j += 2) { + sbuf_push(&buf, 4 + 2, + "[%d]: %g (Bytes/sec),\t[%d]: %g (Bytes/sec)\n", + j, ext->unrsv_bw[j], + j + 1, ext->unrsv_bw[j + 1]); + } + } + if (IS_SUBTLV(ext, EXT_TE_METRIC)) + sbuf_push(&buf, 4, "Traffic Engineering Metric: %u\n", + ext->te_metric); + if (IS_SUBTLV(ext, EXT_RMT_AS)) + sbuf_push(&buf, 4, + "Inter-AS TE Remote AS number: %u\n", + ext->remote_as); + if (IS_SUBTLV(ext, EXT_RMT_IP)) + sbuf_push(&buf, 4, + "Inter-AS TE Remote ASBR IP address: %s\n", + inet_ntoa(ext->remote_ip)); + if (IS_SUBTLV(ext, EXT_DELAY)) + sbuf_push(&buf, 4, + "%s Average Link Delay: %u (micro-sec)\n", + IS_ANORMAL(ext->delay) ? "Anomalous" : "Normal", + ext->delay); + if (IS_SUBTLV(ext, EXT_MM_DELAY)) { + sbuf_push(&buf, 4, "%s Min/Max Link Delay: %u / %u (micro-sec)\n", + IS_ANORMAL(ext->min_delay) ? "Anomalous" : "Normal", + ext->min_delay & TE_EXT_MASK, + ext->max_delay & TE_EXT_MASK); + } + if (IS_SUBTLV(ext, EXT_DELAY_VAR)) + sbuf_push(&buf, 4, + "Delay Variation: %u (micro-sec)\n", + ext->delay_var & TE_EXT_MASK); + if (IS_SUBTLV(ext, EXT_PKT_LOSS)) + sbuf_push(&buf, 4, "%s Link Packet Loss: %g (%%)\n", + IS_ANORMAL(ext->pkt_loss) ? "Anomalous" : "Normal", + (float)((ext->pkt_loss & TE_EXT_MASK) + * LOSS_PRECISION)); + if (IS_SUBTLV(ext, EXT_RES_BW)) + sbuf_push(&buf, 4, + "Unidirectional Residual Bandwidth: %g (Bytes/sec)\n", + ext->res_bw); + if (IS_SUBTLV(ext, EXT_AVA_BW)) + sbuf_push(&buf, 4, + "Unidirectional Available Bandwidth: %g (Bytes/sec)\n", + ext->ava_bw); + if (IS_SUBTLV(ext, EXT_USE_BW)) + sbuf_push(&buf, 4, + "Unidirectional Utilized Bandwidth: %g (Bytes/sec)\n", + ext->use_bw); vty_multiline(vty, "", "%s", sbuf_buf(&buf)); vty_out(vty, "---------------\n\n"); @@ -1148,30 +483,35 @@ DEFUN (show_isis_mpls_te_interface, "Interface information\n" "Interface name\n") { - struct listnode *anode, *cnode; + struct listnode *anode, *cnode, *inode; struct isis_area *area; struct isis_circuit *circuit; struct interface *ifp; int idx_interface = 4; + struct isis *isis = NULL; - if (!isis) { + if (!im) { vty_out(vty, "IS-IS Routing Process not enabled\n"); return CMD_SUCCESS; } if (argc == idx_interface) { /* Show All Interfaces. */ - for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode, area)) { + for (ALL_LIST_ELEMENTS_RO(im->isis, inode, isis)) { + for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode, + area)) { - if (!IS_MPLS_TE(area->mta)) - continue; + if (!IS_MPLS_TE(area->mta)) + continue; - vty_out(vty, "Area %s:\n", area->area_tag); + vty_out(vty, "Area %s:\n", area->area_tag); - for (ALL_LIST_ELEMENTS_RO(area->circuit_list, cnode, - circuit)) - show_mpls_te_sub(vty, circuit->interface->name, - circuit->mtc); + for (ALL_LIST_ELEMENTS_RO(area->circuit_list, + cnode, circuit)) + show_ext_sub(vty, + circuit->interface->name, + circuit->ext); + } } } else { /* Interface name is specified. */ @@ -1185,7 +525,7 @@ DEFUN (show_isis_mpls_te_interface, "ISIS is not enabled on circuit %s\n", ifp->name); else - show_mpls_te_sub(vty, ifp->name, circuit->mtc); + show_ext_sub(vty, ifp->name, circuit->ext); } } @@ -1197,6 +537,11 @@ DEFUN (show_isis_mpls_te_interface, void isis_mpls_te_init(void) { + /* Register Circuit and Adjacency hook */ + hook_register(isis_if_new_hook, isis_mpls_te_update); + hook_register(isis_adj_state_change_hook, isis_link_update_adj_hook); + + #ifndef FABRICD /* Register new VTY commands */ install_element(VIEW_NODE, &show_isis_mpls_te_router_cmd); diff --git a/isisd/isis_te.h b/isisd/isis_te.h index beb0c1836f..2a6911d500 100644 --- a/isisd/isis_te.h +++ b/isisd/isis_te.h @@ -3,8 +3,9 @@ * * This is an implementation of RFC5305, RFC 5307 and RFC 7810 * - * Copyright (C) 2014 Orange Labs - * http://www.orange.com + * Author: Olivier Dugeon + * + * Copyright (C) 2014 - 2019 Orange Labs http://www.orange.com * * This file is part of GNU Zebra. * @@ -50,10 +51,10 @@ * Remote AS number 24 RFC5316 * IPv4 Remote ASBR identifier 25 RFC5316 * + * NOTE: RFC5316 is not fully supported in this version + * only subTLVs decoding is provided */ -/* NOTE: RFC5316 is not yet supported in this version */ - /* Following define the type of TE link regarding the various RFC */ #define STD_TE 0x01 #define GMPLS 0x02 @@ -73,170 +74,9 @@ #define IS_INTER_AS_AS(x) (x & INTER_AS & FLOOD_AS) /* - * Following section defines subTLV (tag, length, value) structures, - * used for Traffic Engineering. + * Note (since release 7.2), subTLVs definition, serialization + * and de-serialization have mode to isis_tlvs.[c,h] */ -struct subtlv_header { - uint8_t type; /* sub_TLV_XXX type (see above) */ - uint8_t length; /* Value portion only, in byte */ -}; - -#define MAX_SUBTLV_SIZE 256 - -#define SUBTLV_HDR_SIZE 2 /* (sizeof (struct sub_tlv_header)) */ - -#define SUBTLV_SIZE(stlvh) (SUBTLV_HDR_SIZE + (stlvh)->length) - -#define SUBTLV_HDR_TOP(lsph) (struct subtlv_header *)((char *)(lsph) + ISIS_LSP_HEADER_SIZE) - -#define SUBTLV_HDR_NEXT(stlvh) (struct subtlv_header *)((char *)(stlvh) + SUBTLV_SIZE(stlvh)) - -#define SUBTLV_TYPE(stlvh) stlvh.header.type -#define SUBTLV_LEN(stlvh) stlvh.header.length -#define SUBTLV_VAL(stlvh) stlvh.value -#define SUBTLV_DATA(stlvh) stlvh + SUBTLV_HDR_SIZE - -#define SUBTLV_DEF_SIZE 4 - -/* Link Sub-TLV: Resource Class/Color - RFC 5305 */ -#define TE_SUBTLV_ADMIN_GRP 3 -struct te_subtlv_admin_grp { - struct subtlv_header header; /* Value length is 4 octets. */ - uint32_t value; /* Admin. group membership. */ -} __attribute__((__packed__)); - -/* Link Local/Remote Identifiers - RFC 5307 */ -#define TE_SUBTLV_LLRI 4 -#define TE_SUBTLV_LLRI_SIZE 8 -struct te_subtlv_llri { - struct subtlv_header header; /* Value length is 8 octets. */ - uint32_t local; /* Link Local Identifier */ - uint32_t remote; /* Link Remote Identifier */ -} __attribute__((__packed__)); - -/* Link Sub-TLV: Local Interface IP Address - RFC 5305 */ -#define TE_SUBTLV_LOCAL_IPADDR 6 -struct te_subtlv_local_ipaddr { - struct subtlv_header header; /* Value length is 4 x N octets. */ - struct in_addr value; /* Local IP address(es). */ -} __attribute__((__packed__)); - -/* Link Sub-TLV: Neighbor Interface IP Address - RFC 5305 */ -#define TE_SUBTLV_RMT_IPADDR 8 -struct te_subtlv_rmt_ipaddr { - struct subtlv_header header; /* Value length is 4 x N octets. */ - struct in_addr value; /* Neighbor's IP address(es). */ -} __attribute__((__packed__)); - -/* Link Sub-TLV: Maximum Bandwidth - RFC 5305 */ -#define TE_SUBTLV_MAX_BW 9 -struct te_subtlv_max_bw { - struct subtlv_header header; /* Value length is 4 octets. */ - float value; /* bytes/sec */ -} __attribute__((__packed__)); - -/* Link Sub-TLV: Maximum Reservable Bandwidth - RFC 5305 */ -#define TE_SUBTLV_MAX_RSV_BW 10 -struct te_subtlv_max_rsv_bw { - struct subtlv_header header; /* Value length is 4 octets. */ - float value; /* bytes/sec */ -} __attribute__((__packed__)); - -/* Link Sub-TLV: Unreserved Bandwidth - RFC 5305 */ -#define TE_SUBTLV_UNRSV_BW 11 -#define TE_SUBTLV_UNRSV_SIZE 32 -struct te_subtlv_unrsv_bw { - struct subtlv_header header; /* Value length is 32 octets. */ - float value[8]; /* One for each priority level. */ -} __attribute__((__packed__)); - -/* Link Sub-TLV: Traffic Engineering Metric - RFC 5305 */ -#define TE_SUBTLV_TE_METRIC 18 -#define TE_SUBTLV_TE_METRIC_SIZE 3 -struct te_subtlv_te_metric { - struct subtlv_header header; /* Value length is 4 octets. */ - uint8_t value[3]; /* Link metric for TE purpose. */ -} __attribute__((__packed__)); - -/* Remote AS Number sub-TLV - RFC5316 */ -#define TE_SUBTLV_RAS 24 -struct te_subtlv_ras { - struct subtlv_header header; /* Value length is 4 octets. */ - uint32_t value; /* Remote AS number */ -} __attribute__((__packed__)); - -/* IPv4 Remote ASBR ID Sub-TLV - RFC5316 */ -#define TE_SUBTLV_RIP 25 -struct te_subtlv_rip { - struct subtlv_header header; /* Value length is 4 octets. */ - struct in_addr value; /* Remote ASBR IP address */ -} __attribute__((__packed__)); - - -/* TE Metric Extensions - RFC 7810 */ -/* Link Sub-TLV: Average Link Delay */ -#define TE_SUBTLV_AV_DELAY 33 -struct te_subtlv_av_delay { - struct subtlv_header header; /* Value length is 4 bytes. */ - uint32_t value; /* Average delay in micro-seconds only 24 bits => 0 ... - 16777215 - with Anomalous Bit (A) as Upper most bit */ -} __attribute__((__packed__)); - -/* Link Sub-TLV: Low/High Link Delay */ -#define TE_SUBTLV_MM_DELAY 34 -#define TE_SUBTLV_MM_DELAY_SIZE 8 -struct te_subtlv_mm_delay { - struct subtlv_header header; /* Value length is 8 bytes. */ - uint32_t low; /* low delay in micro-seconds only 24 bits => 0 ... - 16777215 - with Anomalous Bit (A) as Upper most bit */ - uint32_t high; /* high delay in micro-seconds only 24 bits => 0 ... - 16777215 */ -} __attribute__((__packed__)); - -/* Link Sub-TLV: Link Delay Variation i.e. Jitter */ -#define TE_SUBTLV_DELAY_VAR 35 -struct te_subtlv_delay_var { - struct subtlv_header header; /* Value length is 4 bytes. */ - uint32_t value; /* interval in micro-seconds only 24 bits => 0 ... - 16777215 */ -} __attribute__((__packed__)); - -/* Link Sub-TLV: Routine Unidirectional Link Packet Loss */ -#define TE_SUBTLV_PKT_LOSS 36 -struct te_subtlv_pkt_loss { - struct subtlv_header header; /* Value length is 4 bytes. */ - uint32_t - value; /* in percentage of total traffic only 24 bits (2^24 - 2) - with Anomalous Bit (A) as Upper most bit */ -} __attribute__((__packed__)); - -/* Link Sub-TLV: Unidirectional Residual Bandwidth */ /* Optional */ -#define TE_SUBTLV_RES_BW 37 -struct te_subtlv_res_bw { - struct subtlv_header header; /* Value length is 4 bytes. */ - float value; /* bandwidth in IEEE floating point format with units in - bytes per second */ -} __attribute__((__packed__)); - -/* Link Sub-TLV: Unidirectional Available Bandwidth */ /* Optional */ -#define TE_SUBTLV_AVA_BW 38 -struct te_subtlv_ava_bw { - struct subtlv_header header; /* Value length is 4 octets. */ - float value; /* bandwidth in IEEE floating point format with units in - bytes per second */ -} __attribute__((__packed__)); - -/* Link Sub-TLV: Unidirectional Utilized Bandwidth */ /* Optional */ -#define TE_SUBTLV_USE_BW 39 -struct te_subtlv_use_bw { - struct subtlv_header header; /* Value length is 4 octets. */ - float value; /* bandwidth in IEEE floating point format with units in - bytes per second */ -} __attribute__((__packed__)); - -#define TE_SUBTLV_MAX 40 /* Last SUBTLV + 1 */ /* Following declaration concerns the MPLS-TE and LINk-TE management */ typedef enum _status_t { disable, enable, learn } status_t; @@ -244,7 +84,10 @@ typedef enum _status_t { disable, enable, learn } status_t; /* Mode for Inter-AS LSP */ /* TODO: Check how if LSP is flooded in RFC5316 */ typedef enum _interas_mode_t { off, region, as, emulate } interas_mode_t; -#define IS_MPLS_TE(m) (m && m->status == enable) +#define IS_EXT_TE(e) (e && e->status != 0 \ + && e->status != EXT_ADJ_SID \ + && e->status != EXT_LAN_ADJ_SID) +#define IS_MPLS_TE(a) (a && a->status == enable) /* Per area MPLS-TE parameters */ struct mpls_te_area { @@ -262,56 +105,9 @@ struct mpls_te_area { struct in_addr router_id; }; -/* Per Circuit MPLS-TE parameters */ -struct mpls_te_circuit { - - /* Status of MPLS-TE on this interface */ - status_t status; - - /* Type of MPLS-TE circuit: STD_TE(RFC5305), INTER_AS(RFC5316), - * INTER_AS_EMU(RFC5316 emulated) */ - uint8_t type; - - /* Total size of sub_tlvs */ - uint8_t length; - - /* Store subTLV in network byte order. */ - /* RFC5305 */ - struct te_subtlv_admin_grp admin_grp; - /* RFC5307 */ - struct te_subtlv_llri llri; - /* RFC5305 */ - struct te_subtlv_local_ipaddr local_ipaddr; - struct te_subtlv_rmt_ipaddr rmt_ipaddr; - struct te_subtlv_max_bw max_bw; - struct te_subtlv_max_rsv_bw max_rsv_bw; - struct te_subtlv_unrsv_bw unrsv_bw; - struct te_subtlv_te_metric te_metric; - /* RFC5316 */ - struct te_subtlv_ras ras; - struct te_subtlv_rip rip; - /* RFC7810 */ - struct te_subtlv_av_delay av_delay; - struct te_subtlv_mm_delay mm_delay; - struct te_subtlv_delay_var delay_var; - struct te_subtlv_pkt_loss pkt_loss; - struct te_subtlv_res_bw res_bw; - struct te_subtlv_ava_bw ava_bw; - struct te_subtlv_use_bw use_bw; -}; - /* Prototypes. */ void isis_mpls_te_init(void); -struct mpls_te_circuit *mpls_te_circuit_new(void); -struct sbuf; -void mpls_te_print_detail(struct sbuf *buf, int indent, uint8_t *subtlvs, - uint8_t subtlv_len); -void set_circuitparams_local_ipaddr(struct mpls_te_circuit *, struct in_addr); -void set_circuitparams_rmt_ipaddr(struct mpls_te_circuit *, struct in_addr); -uint8_t subtlvs_len(struct mpls_te_circuit *); -uint8_t add_te_subtlvs(uint8_t *, struct mpls_te_circuit *); -uint8_t build_te_subtlvs(uint8_t *, struct isis_circuit *); void isis_link_params_update(struct isis_circuit *, struct interface *); -void isis_mpls_te_update(struct interface *); +int isis_mpls_te_update(struct interface *); #endif /* _ZEBRA_ISIS_MPLS_TE_H */ diff --git a/isisd/isis_tlvs.c b/isisd/isis_tlvs.c index bc9b514736..a477634d3f 100644 --- a/isisd/isis_tlvs.c +++ b/isisd/isis_tlvs.c @@ -3,6 +3,8 @@ * * Copyright (C) 2015,2017 Christian Franke * + * Copyright (C) 2019 Olivier Dugeon - Orange Labs (for TE and SR) + * * This file is part of FRR. * * FRR is free software; you can redistribute it and/or modify it @@ -22,10 +24,13 @@ */ #include +#ifdef CRYPTO_INTERNAL #include "md5.h" +#endif #include "memory.h" #include "stream.h" #include "sbuf.h" +#include "network.h" #include "isisd/isisd.h" #include "isisd/isis_memory.h" @@ -38,9 +43,10 @@ #include "isisd/isis_pdu.h" #include "isisd/isis_lsp.h" #include "isisd/isis_te.h" +#include "isisd/isis_sr.h" DEFINE_MTYPE_STATIC(ISISD, ISIS_TLV, "ISIS TLVs") -DEFINE_MTYPE_STATIC(ISISD, ISIS_SUBTLV, "ISIS Sub-TLVs") +DEFINE_MTYPE(ISISD, ISIS_SUBTLV, "ISIS Sub-TLVs") DEFINE_MTYPE_STATIC(ISISD, ISIS_MT_ITEM_LIST, "ISIS MT Item Lists") typedef int (*unpack_tlv_func)(enum isis_tlv_context context, uint8_t tlv_type, @@ -83,7 +89,7 @@ struct pack_order_entry { .what_to_pack = offsetof(struct isis_tlvs, w), \ } -static struct pack_order_entry pack_order[] = { +static const struct pack_order_entry pack_order[] = { PACK_ENTRY(OLDSTYLE_REACH, ISIS_ITEMS, oldstyle_reach), PACK_ENTRY(LAN_NEIGHBORS, ISIS_ITEMS, lan_neighbor), PACK_ENTRY(LSP_ENTRY, ISIS_ITEMS, lsp_entries), @@ -96,19 +102,694 @@ static struct pack_order_entry pack_order[] = { PACK_ENTRY(EXTENDED_IP_REACH, ISIS_ITEMS, extended_ip_reach), PACK_ENTRY(MT_IP_REACH, ISIS_MT_ITEMS, mt_ip_reach), PACK_ENTRY(IPV6_REACH, ISIS_ITEMS, ipv6_reach), - PACK_ENTRY(MT_IPV6_REACH, ISIS_MT_ITEMS, mt_ipv6_reach)}; + PACK_ENTRY(MT_IPV6_REACH, ISIS_MT_ITEMS, mt_ipv6_reach) +}; /* This is a forward definition. The table is actually initialized * in at the bottom. */ -static const struct tlv_ops *tlv_table[ISIS_CONTEXT_MAX][ISIS_TLV_MAX]; +static const struct tlv_ops *const tlv_table[ISIS_CONTEXT_MAX][ISIS_TLV_MAX]; /* End of _ops forward definition. */ /* Prototypes */ static void append_item(struct isis_item_list *dest, struct isis_item *item); +static void init_item_list(struct isis_item_list *items); + +/* Functions for Extended IS Reachability SubTLVs a.k.a Traffic Engineering */ +struct isis_ext_subtlvs *isis_alloc_ext_subtlvs(void) +{ + struct isis_ext_subtlvs *ext; + + ext = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(struct isis_ext_subtlvs)); + init_item_list(&ext->adj_sid); + init_item_list(&ext->lan_sid); + + return ext; +} + +/* + * mtid parameter is used to determine if Adjacency is related to IPv4 or IPv6. + * A negative value could be used to skip copy of Adjacency SID. + */ +static struct isis_ext_subtlvs * +copy_item_ext_subtlvs(struct isis_ext_subtlvs *exts, int16_t mtid) +{ + struct isis_ext_subtlvs *rv = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*rv)); + struct isis_adj_sid *adj; + struct isis_lan_adj_sid *lan; + + memcpy(rv, exts, sizeof(struct isis_ext_subtlvs)); + init_item_list(&rv->adj_sid); + init_item_list(&rv->lan_sid); + + UNSET_SUBTLV(rv, EXT_ADJ_SID); + UNSET_SUBTLV(rv, EXT_LAN_ADJ_SID); + + /* Copy Adj SID and LAN Adj SID list for IPv4 if needed */ + for (adj = (struct isis_adj_sid *)exts->adj_sid.head; adj != NULL; + adj = adj->next) { + if ((mtid != -1) + && (((mtid == ISIS_MT_IPV4_UNICAST) + && (adj->family != AF_INET)) + || ((mtid == ISIS_MT_IPV6_UNICAST) + && (adj->family != AF_INET6)))) + continue; + + struct isis_adj_sid *new; + + new = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(struct isis_adj_sid)); + new->family = adj->family; + new->flags = adj->flags; + new->weight = adj->weight; + new->sid = adj->sid; + append_item(&rv->adj_sid, (struct isis_item *)new); + SET_SUBTLV(rv, EXT_ADJ_SID); + } + + for (lan = (struct isis_lan_adj_sid *)exts->lan_sid.head; lan != NULL; + lan = lan->next) { + if ((mtid != -1) + && (((mtid == ISIS_MT_IPV4_UNICAST) + && (lan->family != AF_INET)) + || ((mtid == ISIS_MT_IPV6_UNICAST) + && (lan->family != AF_INET6)))) + continue; + + struct isis_lan_adj_sid *new; + + new = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(struct isis_lan_adj_sid)); + new->family = lan->family; + new->flags = lan->flags; + new->weight = lan->weight; + memcpy(new->neighbor_id, lan->neighbor_id, 6); + new->sid = lan->sid; + append_item(&rv->lan_sid, (struct isis_item *)new); + SET_SUBTLV(rv, EXT_LAN_ADJ_SID); + } + + return rv; +} + +/* mtid parameter is used to manage multi-topology i.e. IPv4 / IPv6 */ +static void format_item_ext_subtlvs(struct isis_ext_subtlvs *exts, + struct sbuf *buf, int indent, + uint16_t mtid) +{ + + char ibuf[PREFIX2STR_BUFFER]; + + /* Standard metrics */ + if (IS_SUBTLV(exts, EXT_ADM_GRP)) + sbuf_push(buf, indent, "Administrative Group: 0x%x\n", + exts->adm_group); + if (IS_SUBTLV(exts, EXT_LLRI)) { + sbuf_push(buf, indent, "Link Local ID: %u\n", + exts->local_llri); + sbuf_push(buf, indent, "Link Remote ID: %u\n", + exts->remote_llri); + } + if (IS_SUBTLV(exts, EXT_LOCAL_ADDR)) + sbuf_push(buf, indent, "Local Interface IP Address(es): %s\n", + inet_ntoa(exts->local_addr)); + if (IS_SUBTLV(exts, EXT_NEIGH_ADDR)) + sbuf_push(buf, indent, "Remote Interface IP Address(es): %s\n", + inet_ntoa(exts->neigh_addr)); + if (IS_SUBTLV(exts, EXT_LOCAL_ADDR6)) + sbuf_push(buf, indent, "Local Interface IPv6 Address(es): %s\n", + inet_ntop(AF_INET6, &exts->local_addr6, ibuf, + PREFIX2STR_BUFFER)); + if (IS_SUBTLV(exts, EXT_NEIGH_ADDR6)) + sbuf_push(buf, indent, "Remote Interface IPv6 Address(es): %s\n", + inet_ntop(AF_INET6, &exts->local_addr6, ibuf, + PREFIX2STR_BUFFER)); + if (IS_SUBTLV(exts, EXT_MAX_BW)) + sbuf_push(buf, indent, "Maximum Bandwidth: %g (Bytes/sec)\n", + exts->max_bw); + if (IS_SUBTLV(exts, EXT_MAX_RSV_BW)) + sbuf_push(buf, indent, + "Maximum Reservable Bandwidth: %g (Bytes/sec)\n", + exts->max_rsv_bw); + if (IS_SUBTLV(exts, EXT_UNRSV_BW)) { + sbuf_push(buf, indent, "Unreserved Bandwidth:\n"); + for (int j = 0; j < MAX_CLASS_TYPE; j += 2) { + sbuf_push(buf, indent + 2, + "[%d]: %g (Bytes/sec),\t[%d]: %g (Bytes/sec)\n", + j, exts->unrsv_bw[j], + j + 1, exts->unrsv_bw[j + 1]); + } + } + if (IS_SUBTLV(exts, EXT_TE_METRIC)) + sbuf_push(buf, indent, "Traffic Engineering Metric: %u\n", + exts->te_metric); + if (IS_SUBTLV(exts, EXT_RMT_AS)) + sbuf_push(buf, indent, + "Inter-AS TE Remote AS number: %u\n", + exts->remote_as); + if (IS_SUBTLV(exts, EXT_RMT_IP)) + sbuf_push(buf, indent, + "Inter-AS TE Remote ASBR IP address: %s\n", + inet_ntoa(exts->remote_ip)); + /* Extended metrics */ + if (IS_SUBTLV(exts, EXT_DELAY)) + sbuf_push(buf, indent, + "%s Average Link Delay: %u (micro-sec)\n", + IS_ANORMAL(exts->delay) ? "Anomalous" : "Normal", + exts->delay); + if (IS_SUBTLV(exts, EXT_MM_DELAY)) { + sbuf_push(buf, indent, "%s Min/Max Link Delay: %u / %u (micro-sec)\n", + IS_ANORMAL(exts->min_delay) ? "Anomalous" : "Normal", + exts->min_delay & TE_EXT_MASK, + exts->max_delay & TE_EXT_MASK); + } + if (IS_SUBTLV(exts, EXT_DELAY_VAR)) { + sbuf_push(buf, indent, + "Delay Variation: %u (micro-sec)\n", + exts->delay_var & TE_EXT_MASK); + } + if (IS_SUBTLV(exts, EXT_PKT_LOSS)) + sbuf_push(buf, indent, "%s Link Packet Loss: %g (%%)\n", + IS_ANORMAL(exts->pkt_loss) ? "Anomalous" : "Normal", + (float)((exts->pkt_loss & TE_EXT_MASK) + * LOSS_PRECISION)); + if (IS_SUBTLV(exts, EXT_RES_BW)) + sbuf_push(buf, indent, + "Unidir. Residual Bandwidth: %g (Bytes/sec)\n", + exts->res_bw); + if (IS_SUBTLV(exts, EXT_AVA_BW)) + sbuf_push(buf, indent, + "Unidir. Available Bandwidth: %g (Bytes/sec)\n", + exts->ava_bw); + if (IS_SUBTLV(exts, EXT_USE_BW)) + sbuf_push(buf, indent, + "Unidir. Utilized Bandwidth: %g (Bytes/sec)\n", + exts->use_bw); + /* Segment Routing Adjacency as per RFC8667 section #2.2.1 */ + if (IS_SUBTLV(exts, EXT_ADJ_SID)) { + struct isis_adj_sid *adj; + + for (adj = (struct isis_adj_sid *)exts->adj_sid.head; adj; + adj = adj->next) { + if (((mtid == ISIS_MT_IPV4_UNICAST) + && (adj->family != AF_INET)) + || ((mtid == ISIS_MT_IPV6_UNICAST) + && (adj->family != AF_INET6))) + continue; + sbuf_push( + buf, indent, + "Adjacency-SID: %u, Weight: %hhu, Flags: F:%c B:%c, V:%c, L:%c, S:%c, P:%c\n", + adj->sid, adj->weight, + adj->flags & EXT_SUBTLV_LINK_ADJ_SID_FFLG ? '1' + : '0', + adj->flags & EXT_SUBTLV_LINK_ADJ_SID_BFLG ? '1' + : '0', + adj->flags & EXT_SUBTLV_LINK_ADJ_SID_VFLG ? '1' + : '0', + adj->flags & EXT_SUBTLV_LINK_ADJ_SID_LFLG ? '1' + : '0', + adj->flags & EXT_SUBTLV_LINK_ADJ_SID_SFLG ? '1' + : '0', + adj->flags & EXT_SUBTLV_LINK_ADJ_SID_PFLG + ? '1' + : '0'); + } + } + /* Segment Routing LAN-Adjacency as per RFC8667 section #2.2.2 */ + if (IS_SUBTLV(exts, EXT_LAN_ADJ_SID)) { + struct isis_lan_adj_sid *lan; + + for (lan = (struct isis_lan_adj_sid *)exts->lan_sid.head; + lan; lan = lan->next) { + if (((mtid == ISIS_MT_IPV4_UNICAST) + && (lan->family != AF_INET)) + || ((mtid == ISIS_MT_IPV6_UNICAST) + && (lan->family != AF_INET6))) + continue; + sbuf_push(buf, indent, + "Lan-Adjacency-SID: %u, Weight: %hhu, Flags: F:%c B:%c, V:%c, L:%c, S:%c, P:%c\n" + " Neighbor-ID: %s\n", + lan->sid, lan->weight, + lan->flags & EXT_SUBTLV_LINK_ADJ_SID_FFLG + ? '1' + : '0', + lan->flags & EXT_SUBTLV_LINK_ADJ_SID_BFLG + ? '1' + : '0', + lan->flags & EXT_SUBTLV_LINK_ADJ_SID_VFLG + ? '1' + : '0', + lan->flags & EXT_SUBTLV_LINK_ADJ_SID_LFLG + ? '1' + : '0', + lan->flags & EXT_SUBTLV_LINK_ADJ_SID_SFLG + ? '1' + : '0', + lan->flags & EXT_SUBTLV_LINK_ADJ_SID_PFLG + ? '1' + : '0', + isis_format_id(lan->neighbor_id, 6)); + } + } +} + +static void free_item_ext_subtlvs(struct isis_ext_subtlvs *exts) +{ + struct isis_item *item, *next_item; + + /* First, free Adj SID and LAN Adj SID list if needed */ + for (item = exts->adj_sid.head; item; item = next_item) { + next_item = item->next; + XFREE(MTYPE_ISIS_SUBTLV, item); + } + for (item = exts->lan_sid.head; item; item = next_item) { + next_item = item->next; + XFREE(MTYPE_ISIS_SUBTLV, item); + } + XFREE(MTYPE_ISIS_SUBTLV, exts); +} + +static int pack_item_ext_subtlvs(struct isis_ext_subtlvs *exts, + struct stream *s) +{ + uint8_t size; + + if (STREAM_WRITEABLE(s) < ISIS_SUBTLV_MAX_SIZE) + return 1; + + if (IS_SUBTLV(exts, EXT_ADM_GRP)) { + stream_putc(s, ISIS_SUBTLV_ADMIN_GRP); + stream_putc(s, ISIS_SUBTLV_DEF_SIZE); + stream_putl(s, exts->adm_group); + } + if (IS_SUBTLV(exts, EXT_LLRI)) { + stream_putc(s, ISIS_SUBTLV_LLRI); + stream_putc(s, ISIS_SUBTLV_LLRI_SIZE); + stream_putl(s, exts->local_llri); + stream_putl(s, exts->remote_llri); + } + if (IS_SUBTLV(exts, EXT_LOCAL_ADDR)) { + stream_putc(s, ISIS_SUBTLV_LOCAL_IPADDR); + stream_putc(s, ISIS_SUBTLV_DEF_SIZE); + stream_put(s, &exts->local_addr.s_addr, 4); + } + if (IS_SUBTLV(exts, EXT_NEIGH_ADDR)) { + stream_putc(s, ISIS_SUBTLV_RMT_IPADDR); + stream_putc(s, ISIS_SUBTLV_DEF_SIZE); + stream_put(s, &exts->neigh_addr.s_addr, 4); + } + if (IS_SUBTLV(exts, EXT_LOCAL_ADDR6)) { + stream_putc(s, ISIS_SUBTLV_LOCAL_IPADDR6); + stream_putc(s, ISIS_SUBTLV_IPV6_ADDR_SIZE); + stream_put(s, &exts->local_addr6, 16); + } + if (IS_SUBTLV(exts, EXT_NEIGH_ADDR6)) { + stream_putc(s, ISIS_SUBTLV_RMT_IPADDR6); + stream_putc(s, ISIS_SUBTLV_IPV6_ADDR_SIZE); + stream_put(s, &exts->neigh_addr6, 16); + } + if (IS_SUBTLV(exts, EXT_MAX_BW)) { + stream_putc(s, ISIS_SUBTLV_MAX_BW); + stream_putc(s, ISIS_SUBTLV_DEF_SIZE); + stream_putf(s, exts->max_bw); + } + if (IS_SUBTLV(exts, EXT_MAX_RSV_BW)) { + stream_putc(s, ISIS_SUBTLV_MAX_RSV_BW); + stream_putc(s, ISIS_SUBTLV_DEF_SIZE); + stream_putf(s, exts->max_rsv_bw); + } + if (IS_SUBTLV(exts, EXT_UNRSV_BW)) { + stream_putc(s, ISIS_SUBTLV_UNRSV_BW); + stream_putc(s, ISIS_SUBTLV_UNRSV_BW_SIZE); + for (int j = 0; j < MAX_CLASS_TYPE; j++) + stream_putf(s, exts->unrsv_bw[j]); + } + if (IS_SUBTLV(exts, EXT_TE_METRIC)) { + stream_putc(s, ISIS_SUBTLV_TE_METRIC); + stream_putc(s, ISIS_SUBTLV_TE_METRIC_SIZE); + stream_put3(s, exts->te_metric); + } + if (IS_SUBTLV(exts, EXT_RMT_AS)) { + stream_putc(s, ISIS_SUBTLV_RAS); + stream_putc(s, ISIS_SUBTLV_DEF_SIZE); + stream_putl(s, exts->remote_as); + } + if (IS_SUBTLV(exts, EXT_RMT_IP)) { + stream_putc(s, ISIS_SUBTLV_RIP); + stream_putc(s, ISIS_SUBTLV_DEF_SIZE); + stream_put(s, &exts->remote_ip.s_addr, 4); + } + if (IS_SUBTLV(exts, EXT_DELAY)) { + stream_putc(s, ISIS_SUBTLV_AV_DELAY); + stream_putc(s, ISIS_SUBTLV_DEF_SIZE); + stream_putl(s, exts->delay); + } + if (IS_SUBTLV(exts, EXT_MM_DELAY)) { + stream_putc(s, ISIS_SUBTLV_MM_DELAY); + stream_putc(s, ISIS_SUBTLV_MM_DELAY_SIZE); + stream_putl(s, exts->min_delay); + stream_putl(s, exts->max_delay); + } + if (IS_SUBTLV(exts, EXT_DELAY_VAR)) { + stream_putc(s, ISIS_SUBTLV_DELAY_VAR); + stream_putc(s, ISIS_SUBTLV_DEF_SIZE); + stream_putl(s, exts->delay_var); + } + if (IS_SUBTLV(exts, EXT_PKT_LOSS)) { + stream_putc(s, ISIS_SUBTLV_PKT_LOSS); + stream_putc(s, ISIS_SUBTLV_DEF_SIZE); + stream_putl(s, exts->pkt_loss); + } + if (IS_SUBTLV(exts, EXT_RES_BW)) { + stream_putc(s, ISIS_SUBTLV_RES_BW); + stream_putc(s, ISIS_SUBTLV_DEF_SIZE); + stream_putf(s, exts->res_bw); + } + if (IS_SUBTLV(exts, EXT_AVA_BW)) { + stream_putc(s, ISIS_SUBTLV_AVA_BW); + stream_putc(s, ISIS_SUBTLV_DEF_SIZE); + stream_putf(s, exts->ava_bw); + } + if (IS_SUBTLV(exts, EXT_USE_BW)) { + stream_putc(s, ISIS_SUBTLV_USE_BW); + stream_putc(s, ISIS_SUBTLV_DEF_SIZE); + stream_putf(s, exts->use_bw); + } + /* Segment Routing Adjacency as per RFC8667 section #2.2.1 */ + if (IS_SUBTLV(exts, EXT_ADJ_SID)) { + struct isis_adj_sid *adj; + + for (adj = (struct isis_adj_sid *)exts->adj_sid.head; adj; + adj = adj->next) { + stream_putc(s, ISIS_SUBTLV_ADJ_SID); + size = ISIS_SUBTLV_ADJ_SID_SIZE; + if (!(adj->flags & EXT_SUBTLV_LINK_ADJ_SID_VFLG)) + size++; + stream_putc(s, size); + stream_putc(s, adj->flags); + stream_putc(s, adj->weight); + if (adj->flags & EXT_SUBTLV_LINK_ADJ_SID_VFLG) + stream_put3(s, adj->sid); + else + stream_putl(s, adj->sid); + + } + } + /* Segment Routing LAN-Adjacency as per RFC8667 section #2.2.2 */ + if (IS_SUBTLV(exts, EXT_LAN_ADJ_SID)) { + struct isis_lan_adj_sid *lan; + + for (lan = (struct isis_lan_adj_sid *)exts->lan_sid.head; lan; + lan = lan->next) { + stream_putc(s, ISIS_SUBTLV_LAN_ADJ_SID); + size = ISIS_SUBTLV_LAN_ADJ_SID_SIZE; + if (!(lan->flags & EXT_SUBTLV_LINK_ADJ_SID_VFLG)) + size++; + stream_putc(s, size); + stream_putc(s, lan->flags); + stream_putc(s, lan->weight); + stream_put(s, lan->neighbor_id, 6); + if (lan->flags & EXT_SUBTLV_LINK_ADJ_SID_VFLG) + stream_put3(s, lan->sid); + else + stream_putl(s, lan->sid); + } + } + + return 0; +} + +static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, + struct sbuf *log, void *dest, int indent) +{ + uint8_t sum = 0; + uint8_t subtlv_type; + uint8_t subtlv_len; + + struct isis_extended_reach *rv = dest; + struct isis_ext_subtlvs *exts = isis_alloc_ext_subtlvs(); + + rv->subtlvs = exts; + + /* + * Parse subTLVs until reach subTLV length + * Check that it remains at least 2 bytes: subTLV Type & Length + */ + while (len > sum + 2) { + /* Read SubTLV Type and Length */ + subtlv_type = stream_getc(s); + subtlv_len = stream_getc(s); + if (subtlv_len > len - sum) { + sbuf_push(log, indent, "TLV %hhu: Available data %u is less than TLV size %u !\n", + subtlv_type, len - sum, subtlv_len); + return 1; + } + + switch (subtlv_type) { + /* Standard Metric as defined in RFC5305 */ + case ISIS_SUBTLV_ADMIN_GRP: + if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) { + sbuf_push(log, indent, + "TLV size does not match expected size for Administrative Group!\n"); + } else { + exts->adm_group = stream_getl(s); + SET_SUBTLV(exts, EXT_ADM_GRP); + } + break; + case ISIS_SUBTLV_LLRI: + if (subtlv_len != ISIS_SUBTLV_LLRI_SIZE) { + sbuf_push(log, indent, + "TLV size does not match expected size for Link ID!\n"); + } else { + exts->local_llri = stream_getl(s); + exts->remote_llri = stream_getl(s); + SET_SUBTLV(exts, EXT_LLRI); + } + break; + case ISIS_SUBTLV_LOCAL_IPADDR: + if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) { + sbuf_push(log, indent, + "TLV size does not match expected size for Local IP address!\n"); + } else { + stream_get(&exts->local_addr.s_addr, s, 4); + SET_SUBTLV(exts, EXT_LOCAL_ADDR); + } + break; + case ISIS_SUBTLV_RMT_IPADDR: + if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) { + sbuf_push(log, indent, + "TLV size does not match expected size for Remote IP address!\n"); + } else { + stream_get(&exts->neigh_addr.s_addr, s, 4); + SET_SUBTLV(exts, EXT_NEIGH_ADDR); + } + break; + case ISIS_SUBTLV_LOCAL_IPADDR6: + if (subtlv_len != ISIS_SUBTLV_IPV6_ADDR_SIZE) { + sbuf_push(log, indent, + "TLV size does not match expected size for Local IPv6 address!\n"); + } else { + stream_get(&exts->local_addr6, s, 16); + SET_SUBTLV(exts, EXT_LOCAL_ADDR6); + } + break; + case ISIS_SUBTLV_RMT_IPADDR6: + if (subtlv_len != ISIS_SUBTLV_IPV6_ADDR_SIZE) { + sbuf_push(log, indent, + "TLV size does not match expected size for Remote IPv6 address!\n"); + } else { + stream_get(&exts->neigh_addr6, s, 16); + SET_SUBTLV(exts, EXT_NEIGH_ADDR6); + } + break; + case ISIS_SUBTLV_MAX_BW: + if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) { + sbuf_push(log, indent, + "TLV size does not match expected size for Maximum Bandwidth!\n"); + } else { + exts->max_bw = stream_getf(s); + SET_SUBTLV(exts, EXT_MAX_BW); + } + break; + case ISIS_SUBTLV_MAX_RSV_BW: + if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) { + sbuf_push(log, indent, + "TLV size does not match expected size for Maximum Reservable Bandwidth!\n"); + } else { + exts->max_rsv_bw = stream_getf(s); + SET_SUBTLV(exts, EXT_MAX_RSV_BW); + } + break; + case ISIS_SUBTLV_UNRSV_BW: + if (subtlv_len != ISIS_SUBTLV_UNRSV_BW_SIZE) { + sbuf_push(log, indent, + "TLV size does not match expected size for Unreserved Bandwidth!\n"); + } else { + for (int i = 0; i < MAX_CLASS_TYPE; i++) + exts->unrsv_bw[i] = stream_getf(s); + SET_SUBTLV(exts, EXT_UNRSV_BW); + } + break; + case ISIS_SUBTLV_TE_METRIC: + if (subtlv_len != ISIS_SUBTLV_TE_METRIC_SIZE) { + sbuf_push(log, indent, + "TLV size does not match expected size for Traffic Engineering Metric!\n"); + } else { + exts->te_metric = stream_get3(s); + SET_SUBTLV(exts, EXT_TE_METRIC); + } + break; + case ISIS_SUBTLV_RAS: + if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) { + sbuf_push(log, indent, + "TLV size does not match expected size for Remote AS number!\n"); + } else { + exts->remote_as = stream_getl(s); + SET_SUBTLV(exts, EXT_RMT_AS); + } + break; + case ISIS_SUBTLV_RIP: + if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) { + sbuf_push(log, indent, + "TLV size does not match expected size for Remote ASBR IP Address!\n"); + } else { + stream_get(&exts->remote_ip.s_addr, s, 4); + SET_SUBTLV(exts, EXT_RMT_IP); + } + break; + /* Extended Metrics as defined in RFC 7810 */ + case ISIS_SUBTLV_AV_DELAY: + if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) { + sbuf_push(log, indent, + "TLV size does not match expected size for Average Link Delay!\n"); + } else { + exts->delay = stream_getl(s); + SET_SUBTLV(exts, EXT_DELAY); + } + break; + case ISIS_SUBTLV_MM_DELAY: + if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) { + sbuf_push(log, indent, + "TLV size does not match expected size for Min/Max Link Delay!\n"); + } else { + exts->min_delay = stream_getl(s); + exts->max_delay = stream_getl(s); + SET_SUBTLV(exts, EXT_MM_DELAY); + } + break; + case ISIS_SUBTLV_DELAY_VAR: + if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) { + sbuf_push(log, indent, + "TLV size does not match expected size for Delay Variation!\n"); + } else { + exts->delay_var = stream_getl(s); + SET_SUBTLV(exts, EXT_DELAY_VAR); + } + break; + case ISIS_SUBTLV_PKT_LOSS: + if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) { + sbuf_push(log, indent, + "TLV size does not match expected size for Link Packet Loss!\n"); + } else { + exts->pkt_loss = stream_getl(s); + SET_SUBTLV(exts, EXT_PKT_LOSS); + } + break; + case ISIS_SUBTLV_RES_BW: + if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) { + sbuf_push(log, indent, + "TLV size does not match expected size for Unidirectional Residual Bandwidth!\n"); + } else { + exts->res_bw = stream_getf(s); + SET_SUBTLV(exts, EXT_RES_BW); + } + break; + case ISIS_SUBTLV_AVA_BW: + if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) { + sbuf_push(log, indent, + "TLV size does not match expected size for Unidirectional Available Bandwidth!\n"); + } else { + exts->ava_bw = stream_getf(s); + SET_SUBTLV(exts, EXT_AVA_BW); + } + break; + case ISIS_SUBTLV_USE_BW: + if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) { + sbuf_push(log, indent, + "TLV size does not match expected size for Unidirectional Utilized Bandwidth!\n"); + } else { + exts->use_bw = stream_getf(s); + SET_SUBTLV(exts, EXT_USE_BW); + } + break; + /* Segment Routing Adjacency as per RFC8667 section #2.2.1 */ + case ISIS_SUBTLV_ADJ_SID: + if (subtlv_len != ISIS_SUBTLV_ADJ_SID_SIZE + && subtlv_len != ISIS_SUBTLV_ADJ_SID_SIZE + 1) { + sbuf_push(log, indent, + "TLV size does not match expected size for Adjacency SID!\n"); + } else { + struct isis_adj_sid *adj; + + adj = XCALLOC(MTYPE_ISIS_SUBTLV, + sizeof(struct isis_adj_sid)); + adj->flags = stream_getc(s); + adj->weight = stream_getc(s); + if (adj->flags & EXT_SUBTLV_LINK_ADJ_SID_VFLG) { + adj->sid = stream_get3(s); + adj->sid &= MPLS_LABEL_VALUE_MASK; + } else { + adj->sid = stream_getl(s); + } + if (mtid == ISIS_MT_IPV4_UNICAST) + adj->family = AF_INET; + if (mtid == ISIS_MT_IPV6_UNICAST) + adj->family = AF_INET6; + append_item(&exts->adj_sid, + (struct isis_item *)adj); + SET_SUBTLV(exts, EXT_ADJ_SID); + } + break; + /* Segment Routing LAN-Adjacency as per RFC8667 section 2.2.2 */ + case ISIS_SUBTLV_LAN_ADJ_SID: + if (subtlv_len != ISIS_SUBTLV_LAN_ADJ_SID_SIZE + && subtlv_len != ISIS_SUBTLV_LAN_ADJ_SID_SIZE + 1) { + sbuf_push(log, indent, + "TLV size does not match expected size for LAN-Adjacency SID!\n"); + } else { + struct isis_lan_adj_sid *lan; + + lan = XCALLOC(MTYPE_ISIS_SUBTLV, + sizeof(struct isis_lan_adj_sid)); + lan->flags = stream_getc(s); + lan->weight = stream_getc(s); + stream_get(&(lan->neighbor_id), s, + ISIS_SYS_ID_LEN); + if (lan->flags & EXT_SUBTLV_LINK_ADJ_SID_VFLG) { + lan->sid = stream_get3(s); + lan->sid &= MPLS_LABEL_VALUE_MASK; + } else { + lan->sid = stream_getl(s); + } + if (mtid == ISIS_MT_IPV4_UNICAST) + lan->family = AF_INET; + if (mtid == ISIS_MT_IPV6_UNICAST) + lan->family = AF_INET6; + append_item(&exts->lan_sid, + (struct isis_item *)lan); + SET_SUBTLV(exts, EXT_LAN_ADJ_SID); + } + break; + default: + /* Skip unknown TLV */ + stream_forward_getp(s, subtlv_len); + break; + } + sum += subtlv_len + ISIS_SUBTLV_HDR_SIZE; + } -/* Functions for Sub-TLV 3 SR Prefix-SID */ + return 0; +} +/* Functions for Sub-TLV 3 SR Prefix-SID as per RFC8667 section 2.1 */ static struct isis_item *copy_item_prefix_sid(struct isis_item *i) { struct isis_prefix_sid *sid = (struct isis_prefix_sid *)i; @@ -125,20 +806,22 @@ static void format_item_prefix_sid(uint16_t mtid, struct isis_item *i, { struct isis_prefix_sid *sid = (struct isis_prefix_sid *)i; - sbuf_push(buf, indent, "SR Prefix-SID:\n"); - sbuf_push(buf, indent, " Flags:%s%s%s%s%s%s\n", - sid->flags & ISIS_PREFIX_SID_READVERTISED ? " READVERTISED" : "", - sid->flags & ISIS_PREFIX_SID_NODE ? " NODE" : "", - sid->flags & ISIS_PREFIX_SID_NO_PHP ? " NO_PHP" : "", - sid->flags & ISIS_PREFIX_SID_EXPLICIT_NULL ? " EXPLICIT-NULL" : "", - sid->flags & ISIS_PREFIX_SID_VALUE ? " VALUE" : "", - sid->flags & ISIS_PREFIX_SID_LOCAL ? " LOCAL" : ""); - sbuf_push(buf, indent, " Algorithm: %" PRIu8 "\n", sid->algorithm); + sbuf_push(buf, indent, "SR Prefix-SID "); if (sid->flags & ISIS_PREFIX_SID_VALUE) { - sbuf_push(buf, indent, "Label: %" PRIu32 "\n", sid->value); + sbuf_push(buf, 0, "Label: %u, ", sid->value); } else { - sbuf_push(buf, indent, "Index: %" PRIu32 "\n", sid->value); + sbuf_push(buf, 0, "Index: %u, ", sid->value); } + sbuf_push(buf, 0, "Algorithm: %hhu, ", sid->algorithm); + sbuf_push(buf, 0, "Flags:%s%s%s%s%s%s\n", + sid->flags & ISIS_PREFIX_SID_READVERTISED ? " READVERTISED" + : "", + sid->flags & ISIS_PREFIX_SID_NODE ? " NODE" : "", + sid->flags & ISIS_PREFIX_SID_NO_PHP ? " NO-PHP" : " PHP", + sid->flags & ISIS_PREFIX_SID_EXPLICIT_NULL ? " EXPLICIT-NULL" + : "", + sid->flags & ISIS_PREFIX_SID_VALUE ? " VALUE" : "", + sid->flags & ISIS_PREFIX_SID_LOCAL ? " LOCAL" : ""); } static void free_item_prefix_sid(struct isis_item *i) @@ -178,31 +861,37 @@ static int unpack_item_prefix_sid(uint16_t mtid, uint8_t len, struct stream *s, if (len < 5) { sbuf_push(log, indent, - "Not enough data left. (expected 5 or more bytes, got %" PRIu8 ")\n", + "Not enough data left. (expected 5 or more bytes, got %hhu)\n", len); return 1; } sid.flags = stream_getc(s); - if ((sid.flags & ISIS_PREFIX_SID_VALUE) - != (sid.flags & ISIS_PREFIX_SID_LOCAL)) { - sbuf_push(log, indent, "Flags inplausible: Local Flag needs to match Value Flag\n"); - return 0; + if (!!(sid.flags & ISIS_PREFIX_SID_VALUE) + != !!(sid.flags & ISIS_PREFIX_SID_LOCAL)) { + sbuf_push(log, indent, "Flags implausible: Local Flag needs to match Value Flag\n"); + return 1; } sid.algorithm = stream_getc(s); - uint8_t expected_size = (sid.flags & ISIS_PREFIX_SID_VALUE) ? 5 : 6; + uint8_t expected_size = (sid.flags & ISIS_PREFIX_SID_VALUE) + ? ISIS_SUBTLV_PREFIX_SID_SIZE + : ISIS_SUBTLV_PREFIX_SID_SIZE + 1; if (len != expected_size) { sbuf_push(log, indent, - "TLV size differs from expected size. " - "(expected %u but got %" PRIu8 ")\n", + "TLV size differs from expected size. (expected %u but got %hhu)\n", expected_size, len); return 1; } if (sid.flags & ISIS_PREFIX_SID_VALUE) { sid.value = stream_get3(s); + if (!IS_MPLS_UNRESERVED_LABEL(sid.value)) { + sbuf_push(log, indent, "Invalid absolute SID %u\n", + sid.value); + return 1; + } } else { sid.value = stream_getl(s); } @@ -267,14 +956,14 @@ static int unpack_subtlv_ipv6_source_prefix(enum isis_tlv_context context, if (tlv_len < 1) { sbuf_push(log, indent, - "Not enough data left. (expected 1 or more bytes, got %" PRIu8 ")\n", + "Not enough data left. (expected 1 or more bytes, got %hhu)\n", tlv_len); return 1; } p.prefixlen = stream_getc(s); if (p.prefixlen > 128) { - sbuf_push(log, indent, "Prefixlen %u is inplausible for IPv6\n", + sbuf_push(log, indent, "Prefixlen %u is implausible for IPv6\n", p.prefixlen); return 1; } @@ -282,8 +971,7 @@ static int unpack_subtlv_ipv6_source_prefix(enum isis_tlv_context context, if (tlv_len != 1 + PSIZE(p.prefixlen)) { sbuf_push( log, indent, - "TLV size differs from expected size for the prefixlen. " - "(expected %u but got %" PRIu8 ")\n", + "TLV size differs from expected size for the prefixlen. (expected %u but got %hhu)\n", 1 + PSIZE(p.prefixlen), tlv_len); return 1; } @@ -303,7 +991,7 @@ static int unpack_subtlv_ipv6_source_prefix(enum isis_tlv_context context, memcpy(subtlvs->source_prefix, &p, sizeof(p)); return 0; } -static void init_item_list(struct isis_item_list *items); + static struct isis_item *copy_item(enum isis_tlv_context context, enum isis_tlv_type type, struct isis_item *item); @@ -318,7 +1006,7 @@ static void free_items(enum isis_tlv_context context, enum isis_tlv_type type, static int pack_items_(uint16_t mtid, enum isis_tlv_context context, enum isis_tlv_type type, struct isis_item_list *items, struct stream *s, struct isis_tlvs **fragment_tlvs, - struct pack_order_entry *pe, + const struct pack_order_entry *pe, struct isis_tlvs *(*new_fragment)(struct list *l), struct list *new_fragment_arg); #define pack_items(...) pack_items_(ISIS_MT_IPV4_UNICAST, __VA_ARGS__) @@ -405,7 +1093,7 @@ static int pack_subtlvs(struct isis_subtlvs *subtlvs, struct stream *s) static int unpack_tlvs(enum isis_tlv_context context, size_t avail_len, struct stream *stream, struct sbuf *log, void *dest, - int indent); + int indent, bool *unpacked_known_tlvs); /* Functions related to TLVs 1 Area Addresses */ @@ -455,8 +1143,7 @@ static int unpack_item_area_address(uint16_t mtid, uint8_t len, if (len < 1) { sbuf_push( log, indent, - "Not enough data left. (Expected 1 byte of address length, got %" PRIu8 - ")\n", + "Not enough data left. (Expected 1 byte of address length, got %hhu)\n", len); goto out; } @@ -465,15 +1152,14 @@ static int unpack_item_area_address(uint16_t mtid, uint8_t len, rv->len = stream_getc(s); if (len < 1 + rv->len) { - sbuf_push(log, indent, "Not enough data left. (Expected %" PRIu8 - " bytes of address, got %" PRIu8 ")\n", + sbuf_push(log, indent, "Not enough data left. (Expected %hhu bytes of address, got %u)\n", rv->len, len - 1); goto out; } if (rv->len < 1 || rv->len > 20) { sbuf_push(log, indent, - "Implausible area address length %" PRIu8 "\n", + "Implausible area address length %hhu\n", rv->len); goto out; } @@ -505,7 +1191,7 @@ static void format_item_oldstyle_reach(uint16_t mtid, struct isis_item *i, { struct isis_oldstyle_reach *r = (struct isis_oldstyle_reach *)i; - sbuf_push(buf, indent, "IS Reachability: %s (Metric: %" PRIu8 ")\n", + sbuf_push(buf, indent, "IS Reachability: %s (Metric: %hhu)\n", isis_format_id(r->id, 7), r->metric); } @@ -540,8 +1226,7 @@ static int unpack_item_oldstyle_reach(uint16_t mtid, uint8_t len, if (len < 11) { sbuf_push( log, indent, - "Not enough data left.(Expected 11 bytes of reach information, got %" PRIu8 - ")\n", + "Not enough data left.(Expected 11 bytes of reach information, got %hhu)\n", len); return 1; } @@ -606,8 +1291,7 @@ static int unpack_item_lan_neighbor(uint16_t mtid, uint8_t len, if (len < 6) { sbuf_push( log, indent, - "Not enough data left.(Expected 6 bytes of mac, got %" PRIu8 - ")\n", + "Not enough data left.(Expected 6 bytes of mac, got %hhu)\n", len); return 1; } @@ -640,8 +1324,7 @@ static void format_item_lsp_entry(uint16_t mtid, struct isis_item *i, struct isis_lsp_entry *e = (struct isis_lsp_entry *)i; sbuf_push(buf, indent, - "LSP Entry: %s, seq 0x%08" PRIx32 ", cksum 0x%04" PRIx16 - ", lifetime %" PRIu16 "s\n", + "LSP Entry: %s, seq 0x%08x, cksum 0x%04hx, lifetime %hus\n", isis_format_id(e->id, 8), e->seqno, e->checksum, e->rem_lifetime); } @@ -675,7 +1358,7 @@ static int unpack_item_lsp_entry(uint16_t mtid, uint8_t len, struct stream *s, if (len < 16) { sbuf_push( log, indent, - "Not enough data left. (Expected 16 bytes of LSP info, got %" PRIu8, + "Not enough data left. (Expected 16 bytes of LSP info, got %hhu", len); return 1; } @@ -701,11 +1384,8 @@ static struct isis_item *copy_item_extended_reach(struct isis_item *i) memcpy(rv->id, r->id, 7); rv->metric = r->metric; - if (r->subtlvs && r->subtlv_len) { - rv->subtlvs = XCALLOC(MTYPE_ISIS_TLV, r->subtlv_len); - memcpy(rv->subtlvs, r->subtlvs, r->subtlv_len); - rv->subtlv_len = r->subtlv_len; - } + if (r->subtlvs) + rv->subtlvs = copy_item_ext_subtlvs(r->subtlvs, -1); return (struct isis_item *)rv; } @@ -722,28 +1402,37 @@ static void format_item_extended_reach(uint16_t mtid, struct isis_item *i, sbuf_push(buf, 0, " %s", isis_mtid2str(mtid)); sbuf_push(buf, 0, "\n"); - if (r->subtlv_len && r->subtlvs) - mpls_te_print_detail(buf, indent + 2, r->subtlvs, - r->subtlv_len); + if (r->subtlvs) + format_item_ext_subtlvs(r->subtlvs, buf, indent + 2, mtid); } static void free_item_extended_reach(struct isis_item *i) { struct isis_extended_reach *item = (struct isis_extended_reach *)i; - XFREE(MTYPE_ISIS_TLV, item->subtlvs); + if (item->subtlvs != NULL) + free_item_ext_subtlvs(item->subtlvs); XFREE(MTYPE_ISIS_TLV, item); } static int pack_item_extended_reach(struct isis_item *i, struct stream *s) { struct isis_extended_reach *r = (struct isis_extended_reach *)i; + size_t len; + size_t len_pos; - if (STREAM_WRITEABLE(s) < 11 + (unsigned)r->subtlv_len) + if (STREAM_WRITEABLE(s) < 11 + ISIS_SUBTLV_MAX_SIZE) return 1; + stream_put(s, r->id, sizeof(r->id)); stream_put3(s, r->metric); - stream_putc(s, r->subtlv_len); - stream_put(s, r->subtlvs, r->subtlv_len); + len_pos = stream_get_endp(s); + /* Real length will be adjust after adding subTLVs */ + stream_putc(s, 11); + if (r->subtlvs) + pack_item_ext_subtlvs(r->subtlvs, s); + /* Adjust length */ + len = stream_get_endp(s) - len_pos - 1; + stream_putc_at(s, len_pos, len); return 0; } @@ -767,8 +1456,7 @@ static int unpack_item_extended_reach(uint16_t mtid, uint8_t len, if (len < 11) { sbuf_push(log, indent, - "Not enough data left. (expected 11 or more bytes, got %" - PRIu8 ")\n", + "Not enough data left. (expected 11 or more bytes, got %hhu)\n", len); goto out; } @@ -778,35 +1466,25 @@ static int unpack_item_extended_reach(uint16_t mtid, uint8_t len, rv->metric = stream_get3(s); subtlv_len = stream_getc(s); - format_item_extended_reach(mtid, (struct isis_item *)rv, log, - indent + 2); - if ((size_t)len < ((size_t)11) + subtlv_len) { sbuf_push(log, indent, - "Not enough data left for subtlv size %" PRIu8 - ", there are only %" PRIu8 " bytes left.\n", + "Not enough data left for subtlv size %hhu, there are only %u bytes left.\n", subtlv_len, len - 11); goto out; } - sbuf_push(log, indent, "Storing %" PRIu8 " bytes of subtlvs\n", + sbuf_push(log, indent, "Storing %hhu bytes of subtlvs\n", subtlv_len); if (subtlv_len) { - size_t subtlv_start = stream_get_getp(s); - - if (unpack_tlvs(ISIS_CONTEXT_SUBTLV_NE_REACH, subtlv_len, s, - log, NULL, indent + 4)) { + if (unpack_item_ext_subtlvs(mtid, subtlv_len, s, log, rv, + indent + 4)) { goto out; } - - stream_set_getp(s, subtlv_start); - - rv->subtlvs = XCALLOC(MTYPE_ISIS_TLV, subtlv_len); - stream_get(rv->subtlvs, s, subtlv_len); - rv->subtlv_len = subtlv_len; } + format_item_extended_reach(mtid, (struct isis_item *)rv, log, + indent + 2); append_item(items, (struct isis_item *)rv); return 0; out: @@ -834,7 +1512,7 @@ static void format_item_oldstyle_ip_reach(uint16_t mtid, struct isis_item *i, struct isis_oldstyle_ip_reach *r = (struct isis_oldstyle_ip_reach *)i; char prefixbuf[PREFIX2STR_BUFFER]; - sbuf_push(buf, indent, "IP Reachability: %s (Metric: %" PRIu8 ")\n", + sbuf_push(buf, indent, "IP Reachability: %s (Metric: %hhu)\n", prefix2str(&r->prefix, prefixbuf, sizeof(prefixbuf)), r->metric); } @@ -872,8 +1550,7 @@ static int unpack_item_oldstyle_ip_reach(uint16_t mtid, uint8_t len, if (len < 12) { sbuf_push( log, indent, - "Not enough data left.(Expected 12 bytes of reach information, got %" PRIu8 - ")\n", + "Not enough data left.(Expected 12 bytes of reach information, got %hhu)\n", len); return 1; } @@ -1022,8 +1699,7 @@ static int unpack_item_ipv4_address(uint16_t mtid, uint8_t len, if (len < 4) { sbuf_push( log, indent, - "Not enough data left.(Expected 4 bytes of IPv4 address, got %" PRIu8 - ")\n", + "Not enough data left.(Expected 4 bytes of IPv4 address, got %hhu)\n", len); return 1; } @@ -1084,8 +1760,7 @@ static int unpack_item_ipv6_address(uint16_t mtid, uint8_t len, if (len < 16) { sbuf_push( log, indent, - "Not enough data left.(Expected 16 bytes of IPv6 address, got %" PRIu8 - ")\n", + "Not enough data left.(Expected 16 bytes of IPv6 address, got %hhu)\n", len); return 1; } @@ -1156,8 +1831,7 @@ static int unpack_item_mt_router_info(uint16_t mtid, uint8_t len, if (len < 2) { sbuf_push( log, indent, - "Not enough data left.(Expected 2 bytes of MT info, got %" PRIu8 - ")\n", + "Not enough data left.(Expected 2 bytes of MT info, got %hhu)\n", len); return 1; } @@ -1255,6 +1929,7 @@ static struct isis_item *copy_item_extended_ip_reach(struct isis_item *i) rv->metric = r->metric; rv->down = r->down; rv->prefix = r->prefix; + rv->subtlvs = copy_subtlvs(r->subtlvs); return (struct isis_item *)rv; } @@ -1272,6 +1947,11 @@ static void format_item_extended_ip_reach(uint16_t mtid, struct isis_item *i, if (mtid != ISIS_MT_IPV4_UNICAST) sbuf_push(buf, 0, " %s", isis_mtid2str(mtid)); sbuf_push(buf, 0, "\n"); + + if (r->subtlvs) { + sbuf_push(buf, indent, " Subtlvs:\n"); + format_subtlvs(r->subtlvs, buf, indent + 4); + } } static void free_item_extended_ip_reach(struct isis_item *i) @@ -1328,7 +2008,7 @@ static int unpack_item_extended_ip_reach(uint16_t mtid, uint8_t len, consume = 5; if (len < consume) { sbuf_push(log, indent, - "Not enough data left. (expected 5 or more bytes, got %" PRIu8 ")\n", + "Not enough data left. (expected 5 or more bytes, got %hhu)\n", len); goto out; } @@ -1341,7 +2021,7 @@ static int unpack_item_extended_ip_reach(uint16_t mtid, uint8_t len, rv->prefix.family = AF_INET; rv->prefix.prefixlen = control & 0x3f; if (rv->prefix.prefixlen > 32) { - sbuf_push(log, indent, "Prefixlen %u is inplausible for IPv4\n", + sbuf_push(log, indent, "Prefixlen %u is implausible for IPv4\n", rv->prefix.prefixlen); goto out; } @@ -1378,18 +2058,23 @@ static int unpack_item_extended_ip_reach(uint16_t mtid, uint8_t len, consume += subtlv_len; if (len < consume) { sbuf_push(log, indent, - "Expected %" PRIu8 - " bytes of subtlvs, but only %u bytes available.\n", + "Expected %hhu bytes of subtlvs, but only %u bytes available.\n", subtlv_len, len - 6 - PSIZE(rv->prefix.prefixlen)); goto out; } rv->subtlvs = isis_alloc_subtlvs(ISIS_CONTEXT_SUBTLV_IP_REACH); + bool unpacked_known_tlvs = false; + if (unpack_tlvs(ISIS_CONTEXT_SUBTLV_IP_REACH, subtlv_len, s, - log, rv->subtlvs, indent + 4)) { + log, rv->subtlvs, indent + 4, &unpacked_known_tlvs)) { goto out; } + if (!unpacked_known_tlvs) { + isis_free_subtlvs(rv->subtlvs); + rv->subtlvs = NULL; + } } append_item(items, (struct isis_item *)rv); @@ -1467,7 +2152,7 @@ static int unpack_tlv_dynamic_hostname(enum isis_tlv_context context, bool sane = true; for (uint8_t i = 0; i < tlv_len; i++) { if ((unsigned char)tlvs->hostname[i] > 127 - || !isprint((int)tlvs->hostname[i])) { + || !isprint((unsigned char)tlvs->hostname[i])) { sane = false; tlvs->hostname[i] = '?'; } @@ -1506,7 +2191,7 @@ static void format_tlv_spine_leaf(const struct isis_spine_leaf *spine_leaf, if (spine_leaf->tier == ISIS_TIER_UNDEFINED) { sbuf_push(buf, indent, " Tier: undefined\n"); } else { - sbuf_push(buf, indent, " Tier: %" PRIu8 "\n", + sbuf_push(buf, indent, " Tier: %hhu\n", spine_leaf->tier); } } @@ -1572,7 +2257,7 @@ static int unpack_tlv_spine_leaf(enum isis_tlv_context context, sbuf_push(log, indent, "Unpacking Spine Leaf Extension TLV...\n"); if (tlv_len < 2) { - sbuf_push(log, indent, "WARNING: Unexepected TLV size\n"); + sbuf_push(log, indent, "WARNING: Unexpected TLV size\n"); stream_forward_getp(s, tlv_len); return 0; } @@ -1639,14 +2324,14 @@ static void format_tlv_threeway_adj(const struct isis_threeway_adj *threeway_adj sbuf_push(buf, indent, " State: %s (%d)\n", isis_threeway_state_name(threeway_adj->state), threeway_adj->state); - sbuf_push(buf, indent, " Extended Local Circuit ID: %" PRIu32 "\n", + sbuf_push(buf, indent, " Extended Local Circuit ID: %u\n", threeway_adj->local_circuit_id); if (!threeway_adj->neighbor_set) return; sbuf_push(buf, indent, " Neighbor System ID: %s\n", isis_format_id(threeway_adj->neighbor_id, 6)); - sbuf_push(buf, indent, " Neighbor Extended Circuit ID: %" PRIu32 "\n", + sbuf_push(buf, indent, " Neighbor Extended Circuit ID: %u\n", threeway_adj->neighbor_circuit_id); } @@ -1688,7 +2373,7 @@ static int unpack_tlv_threeway_adj(enum isis_tlv_context context, sbuf_push(log, indent, "Unpacking P2P Three-Way Adjacency TLV...\n"); if (tlv_len != 5 && tlv_len != 15) { - sbuf_push(log, indent, "WARNING: Unexepected TLV size\n"); + sbuf_push(log, indent, "WARNING: Unexpected TLV size\n"); stream_forward_getp(s, tlv_len); return 0; } @@ -1715,11 +2400,11 @@ static int unpack_tlv_threeway_adj(enum isis_tlv_context context, } /* Functions related to TLVs 236/237 IPv6/MT-IPv6 reach */ - static struct isis_item *copy_item_ipv6_reach(struct isis_item *i) { struct isis_ipv6_reach *r = (struct isis_ipv6_reach *)i; struct isis_ipv6_reach *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv)); + rv->metric = r->metric; rv->down = r->down; rv->external = r->external; @@ -1805,8 +2490,7 @@ static int unpack_item_ipv6_reach(uint16_t mtid, uint8_t len, struct stream *s, consume = 6; if (len < consume) { sbuf_push(log, indent, - "Not enough data left. (expected 6 or more bytes, got %" - PRIu8 ")\n", + "Not enough data left. (expected 6 or more bytes, got %hhu)\n", len); goto out; } @@ -1821,7 +2505,7 @@ static int unpack_item_ipv6_reach(uint16_t mtid, uint8_t len, struct stream *s, rv->prefix.family = AF_INET6; rv->prefix.prefixlen = stream_getc(s); if (rv->prefix.prefixlen > 128) { - sbuf_push(log, indent, "Prefixlen %u is inplausible for IPv6\n", + sbuf_push(log, indent, "Prefixlen %u is implausible for IPv6\n", rv->prefix.prefixlen); goto out; } @@ -1835,6 +2519,7 @@ static int unpack_item_ipv6_reach(uint16_t mtid, uint8_t len, struct stream *s, } stream_get(&rv->prefix.prefix.s6_addr, s, PSIZE(rv->prefix.prefixlen)); struct in6_addr orig_prefix = rv->prefix.prefix; + apply_mask_ipv6(&rv->prefix); if (memcmp(&orig_prefix, &rv->prefix.prefix, sizeof(orig_prefix))) sbuf_push(log, indent + 2, @@ -1857,18 +2542,23 @@ static int unpack_item_ipv6_reach(uint16_t mtid, uint8_t len, struct stream *s, consume += subtlv_len; if (len < consume) { sbuf_push(log, indent, - "Expected %" PRIu8 - " bytes of subtlvs, but only %u bytes available.\n", + "Expected %hhu bytes of subtlvs, but only %u bytes available.\n", subtlv_len, len - 6 - PSIZE(rv->prefix.prefixlen)); goto out; } rv->subtlvs = isis_alloc_subtlvs(ISIS_CONTEXT_SUBTLV_IPV6_REACH); + bool unpacked_known_tlvs = false; + if (unpack_tlvs(ISIS_CONTEXT_SUBTLV_IPV6_REACH, subtlv_len, s, - log, rv->subtlvs, indent + 4)) { + log, rv->subtlvs, indent + 4, &unpacked_known_tlvs)) { goto out; } + if (!unpacked_known_tlvs) { + isis_free_subtlvs(rv->subtlvs); + rv->subtlvs = NULL; + } } append_item(items, (struct isis_item *)rv); @@ -1879,6 +2569,304 @@ static int unpack_item_ipv6_reach(uint16_t mtid, uint8_t len, struct stream *s, return 1; } +/* Functions related to TLV 242 Router Capability as per RFC7981 */ +static struct isis_router_cap *copy_tlv_router_cap( + const struct isis_router_cap *router_cap) +{ + struct isis_router_cap *rv; + + if (!router_cap) + return NULL; + + rv = XMALLOC(MTYPE_ISIS_TLV, sizeof(*rv)); + + memcpy(rv, router_cap, sizeof(*rv)); + + return rv; +} + +static void format_tlv_router_cap(const struct isis_router_cap *router_cap, + struct sbuf *buf, int indent) +{ + char addrbuf[INET_ADDRSTRLEN]; + + if (!router_cap) + return; + + /* Router ID and Flags */ + inet_ntop(AF_INET, &router_cap->router_id, addrbuf, sizeof(addrbuf)); + sbuf_push(buf, indent, "Router Capability:"); + sbuf_push(buf, indent, " %s , D:%c, S:%c\n", addrbuf, + router_cap->flags & ISIS_ROUTER_CAP_FLAG_D ? '1' : '0', + router_cap->flags & ISIS_ROUTER_CAP_FLAG_S ? '1' : '0'); + + /* Segment Routing Global Block as per RFC8667 section #3.1 */ + if (router_cap->srgb.range_size != 0) + sbuf_push( + buf, indent, + " Segment Routing: I:%s V:%s, Global Block Base: %u Range: %u\n", + IS_SR_IPV4(router_cap->srgb) ? "1" : "0", + IS_SR_IPV6(router_cap->srgb) ? "1" : "0", + router_cap->srgb.lower_bound, + router_cap->srgb.range_size); + + /* Segment Routing Local Block as per RFC8667 section #3.3 */ + if (router_cap->srlb.range_size != 0) + sbuf_push(buf, indent, " SR Local Block Base: %u Range: %u\n", + router_cap->srlb.lower_bound, + router_cap->srlb.range_size); + + /* Segment Routing Algorithms as per RFC8667 section #3.2 */ + if (router_cap->algo[0] != SR_ALGORITHM_UNSET) { + sbuf_push(buf, indent, " SR Algorithm:\n"); + for (int i = 0; i < SR_ALGORITHM_COUNT; i++) + if (router_cap->algo[i] != SR_ALGORITHM_UNSET) + sbuf_push(buf, indent, " %u: %s\n", i, + router_cap->algo[i] == 0 + ? "SPF" + : "Strict SPF"); + } + + /* Segment Routing Node MSD as per RFC8491 section #2 */ + if (router_cap->msd != 0) + sbuf_push(buf, indent, " Node Maximum SID Depth: %u\n", + router_cap->msd); +} + +static void free_tlv_router_cap(struct isis_router_cap *router_cap) +{ + XFREE(MTYPE_ISIS_TLV, router_cap); +} + +static int pack_tlv_router_cap(const struct isis_router_cap *router_cap, + struct stream *s) +{ + size_t tlv_len = ISIS_ROUTER_CAP_SIZE; + size_t len_pos; + uint8_t nb_algo; + + if (!router_cap) + return 0; + + /* Compute Maximum TLV size */ + tlv_len += ISIS_SUBTLV_SID_LABEL_RANGE_SIZE + + ISIS_SUBTLV_HDR_SIZE + + ISIS_SUBTLV_ALGORITHM_SIZE + + ISIS_SUBTLV_NODE_MSD_SIZE; + + if (STREAM_WRITEABLE(s) < (unsigned int)(2 + tlv_len)) + return 1; + + /* Add Router Capability TLV 242 with Router ID and Flags */ + stream_putc(s, ISIS_TLV_ROUTER_CAPABILITY); + /* Real length will be adjusted later */ + len_pos = stream_get_endp(s); + stream_putc(s, tlv_len); + stream_put_ipv4(s, router_cap->router_id.s_addr); + stream_putc(s, router_cap->flags); + + /* Add SRGB if set as per RFC8667 section #3.1 */ + if ((router_cap->srgb.range_size != 0) + && (router_cap->srgb.lower_bound != 0)) { + stream_putc(s, ISIS_SUBTLV_SID_LABEL_RANGE); + stream_putc(s, ISIS_SUBTLV_SID_LABEL_RANGE_SIZE); + stream_putc(s, router_cap->srgb.flags); + stream_put3(s, router_cap->srgb.range_size); + stream_putc(s, ISIS_SUBTLV_SID_LABEL); + stream_putc(s, ISIS_SUBTLV_SID_LABEL_SIZE); + stream_put3(s, router_cap->srgb.lower_bound); + + /* Then SR Algorithm if set as per RFC8667 section #3.2 */ + for (nb_algo = 0; nb_algo < SR_ALGORITHM_COUNT; nb_algo++) + if (router_cap->algo[nb_algo] == SR_ALGORITHM_UNSET) + break; + if (nb_algo > 0) { + stream_putc(s, ISIS_SUBTLV_ALGORITHM); + stream_putc(s, nb_algo); + for (int i = 0; i < nb_algo; i++) + stream_putc(s, router_cap->algo[i]); + } + + /* Local Block if defined as per RFC8667 section #3.3 */ + if ((router_cap->srlb.range_size != 0) + && (router_cap->srlb.lower_bound != 0)) { + stream_putc(s, ISIS_SUBTLV_SRLB); + stream_putc(s, ISIS_SUBTLV_SID_LABEL_RANGE_SIZE); + /* No Flags are defined for SRLB */ + stream_putc(s, 0); + stream_put3(s, router_cap->srlb.range_size); + stream_putc(s, ISIS_SUBTLV_SID_LABEL); + stream_putc(s, ISIS_SUBTLV_SID_LABEL_SIZE); + stream_put3(s, router_cap->srlb.lower_bound); + } + + /* And finish with MSD if set as per RFC8491 section #2 */ + if (router_cap->msd != 0) { + stream_putc(s, ISIS_SUBTLV_NODE_MSD); + stream_putc(s, ISIS_SUBTLV_NODE_MSD_SIZE); + stream_putc(s, MSD_TYPE_BASE_MPLS_IMPOSITION); + stream_putc(s, router_cap->msd); + } + } + + /* Adjust TLV length which depends on subTLVs presence */ + tlv_len = stream_get_endp(s) - len_pos - 1; + stream_putc_at(s, len_pos, tlv_len); + + return 0; +} + +static int unpack_tlv_router_cap(enum isis_tlv_context context, + uint8_t tlv_type, uint8_t tlv_len, + struct stream *s, struct sbuf *log, + void *dest, int indent) +{ + struct isis_tlvs *tlvs = dest; + struct isis_router_cap *rcap; + uint8_t type; + uint8_t length; + uint8_t subtlv_len; + uint8_t size; + + sbuf_push(log, indent, "Unpacking Router Capability TLV...\n"); + if (tlv_len < ISIS_ROUTER_CAP_SIZE) { + sbuf_push(log, indent, "WARNING: Unexpected TLV size\n"); + stream_forward_getp(s, tlv_len); + return 0; + } + + if (tlvs->router_cap) { + sbuf_push(log, indent, + "WARNING: Router Capability TLV present multiple times.\n"); + stream_forward_getp(s, tlv_len); + return 0; + } + + /* Allocate router cap structure and initialize SR Algorithms */ + rcap = XCALLOC(MTYPE_ISIS_TLV, sizeof(struct isis_router_cap)); + for (int i = 0; i < SR_ALGORITHM_COUNT; i++) + rcap->algo[i] = SR_ALGORITHM_UNSET; + + /* Get Router ID and Flags */ + rcap->router_id.s_addr = stream_get_ipv4(s); + rcap->flags = stream_getc(s); + + /* Parse remaining part of the TLV if present */ + subtlv_len = tlv_len - ISIS_ROUTER_CAP_SIZE; + while (subtlv_len > 2) { + uint8_t msd_type; + + type = stream_getc(s); + length = stream_getc(s); + switch (type) { + case ISIS_SUBTLV_SID_LABEL_RANGE: + /* Check that SRGB is correctly formated */ + if (length < SUBTLV_RANGE_LABEL_SIZE + || length > SUBTLV_RANGE_INDEX_SIZE) { + stream_forward_getp(s, length); + continue; + } + /* Only one SRGB is supported. Skip subsequent one */ + if (rcap->srgb.range_size != 0) { + stream_forward_getp(s, length); + continue; + } + rcap->srgb.flags = stream_getc(s); + rcap->srgb.range_size = stream_get3(s); + /* Skip Type and get Length of SID Label */ + stream_getc(s); + size = stream_getc(s); + if (size == ISIS_SUBTLV_SID_LABEL_SIZE) + rcap->srgb.lower_bound = stream_get3(s); + else + rcap->srgb.lower_bound = stream_getl(s); + + /* SRGB sanity checks. */ + if (rcap->srgb.range_size == 0 + || (rcap->srgb.lower_bound <= MPLS_LABEL_RESERVED_MAX) + || ((rcap->srgb.lower_bound + rcap->srgb.range_size - 1) + > MPLS_LABEL_UNRESERVED_MAX)) { + sbuf_push(log, indent, "Invalid label range. Reset SRGB\n"); + rcap->srgb.lower_bound = 0; + rcap->srgb.range_size = 0; + } + /* Only one range is supported. Skip subsequent one */ + size = length - (size + SUBTLV_SR_BLOCK_SIZE); + if (size > 0) + stream_forward_getp(s, length); + break; + case ISIS_SUBTLV_ALGORITHM: + /* Only 2 algorithms are supported: SPF & Strict SPF */ + stream_get(&rcap->algo, s, + length > SR_ALGORITHM_COUNT + ? SR_ALGORITHM_COUNT + : length); + if (length > SR_ALGORITHM_COUNT) + stream_forward_getp( + s, length - SR_ALGORITHM_COUNT); + break; + case ISIS_SUBTLV_SRLB: + /* Check that SRLB is correctly formated */ + if (length < SUBTLV_RANGE_LABEL_SIZE + || length > SUBTLV_RANGE_INDEX_SIZE) { + stream_forward_getp(s, length); + continue; + } + /* RFC 8667 section #3.3: Only one SRLB is authorized */ + if (rcap->srlb.range_size != 0) { + stream_forward_getp(s, length); + continue; + } + /* Ignore Flags which are not defined */ + stream_getc(s); + rcap->srlb.range_size = stream_get3(s); + /* Skip Type and get Length of SID Label */ + stream_getc(s); + size = stream_getc(s); + if (size == ISIS_SUBTLV_SID_LABEL_SIZE) + rcap->srlb.lower_bound = stream_get3(s); + else + rcap->srlb.lower_bound = stream_getl(s); + + /* SRLB sanity checks. */ + if (rcap->srlb.range_size == 0 + || (rcap->srlb.lower_bound <= MPLS_LABEL_RESERVED_MAX) + || ((rcap->srlb.lower_bound + rcap->srlb.range_size - 1) + > MPLS_LABEL_UNRESERVED_MAX)) { + sbuf_push(log, indent, "Invalid label range. Reset SRLB\n"); + rcap->srlb.lower_bound = 0; + rcap->srlb.range_size = 0; + } + /* Only one range is supported. Skip subsequent one */ + size = length - (size + SUBTLV_SR_BLOCK_SIZE); + if (size > 0) + stream_forward_getp(s, length); + break; + case ISIS_SUBTLV_NODE_MSD: + /* Check that MSD is correctly formated */ + if (length < MSD_TLV_SIZE) { + stream_forward_getp(s, length); + continue; + } + msd_type = stream_getc(s); + rcap->msd = stream_getc(s); + /* Only BMI-MSD type has been defined in RFC 8491 */ + if (msd_type != MSD_TYPE_BASE_MPLS_IMPOSITION) + rcap->msd = 0; + /* Only one MSD is standardized. Skip others */ + if (length > MSD_TLV_SIZE) + stream_forward_getp(s, length - MSD_TLV_SIZE); + break; + default: + stream_forward_getp(s, length); + break; + } + subtlv_len = subtlv_len - length - 2; + } + tlvs->router_cap = rcap; + return 0; +} + /* Functions related to TLV 10 Authentication */ static struct isis_item *copy_item_auth(struct isis_item *i) { @@ -1906,12 +2894,12 @@ static void format_item_auth(uint16_t mtid, struct isis_item *i, case ISIS_PASSWD_TYPE_HMAC_MD5: for (unsigned int j = 0; j < 16; j++) { snprintf(obuf + 2 * j, sizeof(obuf) - 2 * j, - "%02" PRIx8, auth->value[j]); + "%02hhx", auth->value[j]); } sbuf_push(buf, indent, " HMAC-MD5: %s\n", obuf); break; default: - sbuf_push(buf, indent, " Unknown (%" PRIu8 ")\n", auth->type); + sbuf_push(buf, indent, " Unknown (%hhu)\n", auth->type); break; } } @@ -1957,8 +2945,7 @@ static int unpack_item_auth(uint16_t mtid, uint8_t len, struct stream *s, if (len < 1) { sbuf_push( log, indent, - "Not enough data left.(Expected 1 bytes of auth type, got %" PRIu8 - ")\n", + "Not enough data left.(Expected 1 bytes of auth type, got %hhu)\n", len); return 1; } @@ -1971,8 +2958,7 @@ static int unpack_item_auth(uint16_t mtid, uint8_t len, struct stream *s, if (rv->type == ISIS_PASSWD_TYPE_HMAC_MD5 && rv->length != 16) { sbuf_push( log, indent, - "Unexpected auth length for HMAC-MD5 (expected 16, got %" PRIu8 - ")\n", + "Unexpected auth length for HMAC-MD5 (expected 16, got %hhu)\n", rv->length); XFREE(MTYPE_ISIS_TLV, rv); return 1; @@ -2056,8 +3042,7 @@ static int unpack_tlv_purge_originator(enum isis_tlv_context context, sbuf_push(log, indent, "Unpacking Purge Originator Identification TLV...\n"); if (tlv_len < 7) { - sbuf_push(log, indent, "Not enough data left. (Expected at least 7 bytes, got %" - PRIu8 ")\n", tlv_len); + sbuf_push(log, indent, "Not enough data left. (Expected at least 7 bytes, got %hhu)\n", tlv_len); return 1; } @@ -2068,8 +3053,7 @@ static int unpack_tlv_purge_originator(enum isis_tlv_context context, } else if (number_of_ids == 2) { poi.sender_set = true; } else { - sbuf_push(log, indent, "Got invalid value for number of system IDs: %" - PRIu8 ")\n", number_of_ids); + sbuf_push(log, indent, "Got invalid value for number of system IDs: %hhu)\n", number_of_ids); return 1; } @@ -2178,7 +3162,7 @@ static void free_items(enum isis_tlv_context context, enum isis_tlv_type type, static int pack_item(enum isis_tlv_context context, enum isis_tlv_type type, struct isis_item *i, struct stream *s, struct isis_tlvs **fragment_tlvs, - struct pack_order_entry *pe, uint16_t mtid) + const struct pack_order_entry *pe, uint16_t mtid) { const struct tlv_ops *ops = tlv_table[context][type]; @@ -2190,7 +3174,8 @@ static int pack_item(enum isis_tlv_context context, enum isis_tlv_type type, return 1; } -static void add_item_to_fragment(struct isis_item *i, struct pack_order_entry *pe, +static void add_item_to_fragment(struct isis_item *i, + const struct pack_order_entry *pe, struct isis_tlvs *fragment_tlvs, uint16_t mtid) { struct isis_item_list *l; @@ -2209,7 +3194,7 @@ static void add_item_to_fragment(struct isis_item *i, struct pack_order_entry *p static int pack_items_(uint16_t mtid, enum isis_tlv_context context, enum isis_tlv_type type, struct isis_item_list *items, struct stream *s, struct isis_tlvs **fragment_tlvs, - struct pack_order_entry *pe, + const struct pack_order_entry *pe, struct isis_tlvs *(*new_fragment)(struct list *l), struct list *new_fragment_arg) { @@ -2299,6 +3284,39 @@ static void append_item(struct isis_item_list *dest, struct isis_item *item) dest->count++; } +static void delete_item(struct isis_item_list *dest, struct isis_item *del) +{ + struct isis_item *item, *prev = NULL, *next; + + /* Sanity Check */ + if ((dest == NULL) || (del == NULL)) + return; + + /* + * TODO: delete is tricky because "dest" is a singly linked list. + * We need to switch a doubly linked list. + */ + for (item = dest->head; item; item = next) { + if (item->next == del) { + prev = item; + break; + } + next = item->next; + } + if (prev) + prev->next = del->next; + if (dest->head == del) + dest->head = del->next; + if ((struct isis_item *)dest->tail == del) { + *dest->tail = prev; + if (prev) + dest->tail = &(*dest->tail)->next; + else + dest->tail = &dest->head; + } + dest->count--; +} + static struct isis_item *last_item(struct isis_item_list *list) { return container_of(list->tail, struct isis_item, next); @@ -2451,7 +3469,7 @@ static void format_mt_items(enum isis_tlv_context context, static int pack_mt_items(enum isis_tlv_context context, enum isis_tlv_type type, struct isis_mt_item_list *m, struct stream *s, struct isis_tlvs **fragment_tlvs, - struct pack_order_entry *pe, + const struct pack_order_entry *pe, struct isis_tlvs *(*new_fragment)(struct list *l), struct list *new_fragment_arg) { @@ -2577,6 +3595,8 @@ struct isis_tlvs *isis_copy_tlvs(struct isis_tlvs *tlvs) rv->threeway_adj = copy_tlv_threeway_adj(tlvs->threeway_adj); + rv->router_cap = copy_tlv_router_cap(tlvs->router_cap); + rv->spine_leaf = copy_tlv_spine_leaf(tlvs->spine_leaf); return rv; @@ -2612,6 +3632,7 @@ static void format_tlvs(struct isis_tlvs *tlvs, struct sbuf *buf, int indent) format_tlv_dynamic_hostname(tlvs->hostname, buf, indent); format_tlv_te_router_id(tlvs->te_router_id, buf, indent); + format_tlv_router_cap(tlvs->router_cap, buf, indent); format_items(ISIS_CONTEXT_LSP, ISIS_TLV_EXTENDED_REACH, &tlvs->extended_reach, buf, indent); @@ -2698,6 +3719,7 @@ void isis_free_tlvs(struct isis_tlvs *tlvs) free_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_IPV6_REACH, &tlvs->mt_ipv6_reach); free_tlv_threeway_adj(tlvs->threeway_adj); + free_tlv_router_cap(tlvs->router_cap); free_tlv_spine_leaf(tlvs->spine_leaf); XFREE(MTYPE_ISIS_TLV, tlvs); @@ -2753,8 +3775,16 @@ static void update_auth_hmac_md5(struct isis_auth *auth, struct stream *s, safe_auth_md5(s, &checksum, &rem_lifetime); memset(STREAM_DATA(s) + auth->offset, 0, 16); +#ifdef CRYPTO_OPENSSL + uint8_t *result = (uint8_t *)HMAC(EVP_md5(), auth->passwd, + auth->plength, STREAM_DATA(s), + stream_get_endp(s), NULL, NULL); + + memcpy(digest, result, 16); +#elif CRYPTO_INTERNAL hmac_md5(STREAM_DATA(s), stream_get_endp(s), auth->passwd, auth->plength, digest); +#endif memcpy(auth->value, digest, 16); memcpy(STREAM_DATA(s) + auth->offset, digest, 16); @@ -2772,7 +3802,7 @@ static void update_auth(struct isis_tlvs *tlvs, struct stream *s, bool is_lsp) } } -static int handle_pack_entry(struct pack_order_entry *pe, +static int handle_pack_entry(const struct pack_order_entry *pe, struct isis_tlvs *tlvs, struct stream *stream, struct isis_tlvs **fragment_tlvs, struct isis_tlvs *(*new_fragment)(struct list *l), @@ -2870,6 +3900,14 @@ static int pack_tlvs(struct isis_tlvs *tlvs, struct stream *stream, fragment_tlvs->hostname = copy_tlv_dynamic_hostname(tlvs->hostname); + rv = pack_tlv_router_cap(tlvs->router_cap, stream); + if (rv) + return rv; + if (fragment_tlvs) { + fragment_tlvs->router_cap = + copy_tlv_router_cap(tlvs->router_cap); + } + rv = pack_tlv_te_router_id(tlvs->te_router_id, stream); if (rv) return rv; @@ -2959,14 +3997,14 @@ static int unpack_tlv_unknown(enum isis_tlv_context context, uint8_t tlv_type, { stream_forward_getp(s, tlv_len); sbuf_push(log, indent, - "Skipping unknown TLV %" PRIu8 " (%" PRIu8 " bytes)\n", + "Skipping unknown TLV %hhu (%hhu bytes)\n", tlv_type, tlv_len); return 0; } static int unpack_tlv(enum isis_tlv_context context, size_t avail_len, struct stream *stream, struct sbuf *log, void *dest, - int indent) + int indent, bool *unpacked_known_tlvs) { uint8_t tlv_type, tlv_len; const struct tlv_ops *ops; @@ -2985,18 +4023,20 @@ static int unpack_tlv(enum isis_tlv_context context, size_t avail_len, tlv_len = stream_getc(stream); sbuf_push(log, indent + 2, - "Found TLV of type %" PRIu8 " and len %" PRIu8 ".\n", + "Found TLV of type %hhu and len %hhu.\n", tlv_type, tlv_len); if (avail_len < ((size_t)tlv_len) + 2) { sbuf_push(log, indent + 2, - "Available data %zu too short for claimed TLV len %" PRIu8 ".\n", + "Available data %zu too short for claimed TLV len %hhu.\n", avail_len - 2, tlv_len); return 1; } ops = tlv_table[context][tlv_type]; if (ops && ops->unpack) { + if (unpacked_known_tlvs) + *unpacked_known_tlvs = true; return ops->unpack(context, tlv_type, tlv_len, stream, log, dest, indent + 2); } @@ -3007,7 +4047,7 @@ static int unpack_tlv(enum isis_tlv_context context, size_t avail_len, static int unpack_tlvs(enum isis_tlv_context context, size_t avail_len, struct stream *stream, struct sbuf *log, void *dest, - int indent) + int indent, bool *unpacked_known_tlvs) { int rv; size_t tlv_start, tlv_pos; @@ -3020,7 +4060,7 @@ static int unpack_tlvs(enum isis_tlv_context context, size_t avail_len, while (tlv_pos < avail_len) { rv = unpack_tlv(context, avail_len - tlv_pos, stream, log, dest, - indent + 2); + indent + 2, unpacked_known_tlvs); if (rv) return rv; @@ -3044,15 +4084,14 @@ int isis_unpack_tlvs(size_t avail_len, struct stream *stream, sbuf_reset(&logbuf); if (avail_len > STREAM_READABLE(stream)) { sbuf_push(&logbuf, indent, - "Stream doesn't contain sufficient data. " - "Claimed %zu, available %zu\n", + "Stream doesn't contain sufficient data. Claimed %zu, available %zu\n", avail_len, STREAM_READABLE(stream)); return 1; } result = isis_alloc_tlvs(); rv = unpack_tlvs(ISIS_CONTEXT_LSP, avail_len, stream, &logbuf, result, - indent); + indent, NULL); *log = sbuf_buf(&logbuf); *dest = result; @@ -3102,11 +4141,12 @@ ITEM_TLV_OPS(mt_router_info, "TLV 229 MT Router Information"); TLV_OPS(threeway_adj, "TLV 240 P2P Three-Way Adjacency"); ITEM_TLV_OPS(ipv6_address, "TLV 232 IPv6 Interface Address"); ITEM_TLV_OPS(ipv6_reach, "TLV 236 IPv6 Reachability"); +TLV_OPS(router_cap, "TLV 242 Router Capability"); ITEM_SUBTLV_OPS(prefix_sid, "Sub-TLV 3 SR Prefix-SID"); SUBTLV_OPS(ipv6_source_prefix, "Sub-TLV 22 IPv6 Source Prefix"); -static const struct tlv_ops *tlv_table[ISIS_CONTEXT_MAX][ISIS_TLV_MAX] = { +static const struct tlv_ops *const tlv_table[ISIS_CONTEXT_MAX][ISIS_TLV_MAX] = { [ISIS_CONTEXT_LSP] = { [ISIS_TLV_AREA_ADDRESSES] = &tlv_area_address_ops, [ISIS_TLV_OLDSTYLE_REACH] = &tlv_oldstyle_reach_ops, @@ -3115,21 +4155,22 @@ static const struct tlv_ops *tlv_table[ISIS_CONTEXT_MAX][ISIS_TLV_MAX] = { [ISIS_TLV_AUTH] = &tlv_auth_ops, [ISIS_TLV_PURGE_ORIGINATOR] = &tlv_purge_originator_ops, [ISIS_TLV_EXTENDED_REACH] = &tlv_extended_reach_ops, - [ISIS_TLV_MT_REACH] = &tlv_extended_reach_ops, [ISIS_TLV_OLDSTYLE_IP_REACH] = &tlv_oldstyle_ip_reach_ops, [ISIS_TLV_PROTOCOLS_SUPPORTED] = &tlv_protocols_supported_ops, [ISIS_TLV_OLDSTYLE_IP_REACH_EXT] = &tlv_oldstyle_ip_reach_ops, [ISIS_TLV_IPV4_ADDRESS] = &tlv_ipv4_address_ops, [ISIS_TLV_TE_ROUTER_ID] = &tlv_te_router_id_ops, [ISIS_TLV_EXTENDED_IP_REACH] = &tlv_extended_ip_reach_ops, - [ISIS_TLV_MT_IP_REACH] = &tlv_extended_ip_reach_ops, [ISIS_TLV_DYNAMIC_HOSTNAME] = &tlv_dynamic_hostname_ops, [ISIS_TLV_SPINE_LEAF_EXT] = &tlv_spine_leaf_ops, + [ISIS_TLV_MT_REACH] = &tlv_extended_reach_ops, [ISIS_TLV_MT_ROUTER_INFO] = &tlv_mt_router_info_ops, - [ISIS_TLV_THREE_WAY_ADJ] = &tlv_threeway_adj_ops, [ISIS_TLV_IPV6_ADDRESS] = &tlv_ipv6_address_ops, + [ISIS_TLV_MT_IP_REACH] = &tlv_extended_ip_reach_ops, [ISIS_TLV_IPV6_REACH] = &tlv_ipv6_reach_ops, [ISIS_TLV_MT_IPV6_REACH] = &tlv_ipv6_reach_ops, + [ISIS_TLV_THREE_WAY_ADJ] = &tlv_threeway_adj_ops, + [ISIS_TLV_ROUTER_CAPABILITY] = &tlv_router_cap_ops, }, [ISIS_CONTEXT_SUBTLV_NE_REACH] = {}, [ISIS_CONTEXT_SUBTLV_IP_REACH] = { @@ -3252,13 +4293,18 @@ void isis_tlvs_add_ipv6_addresses(struct isis_tlvs *tlvs, { struct listnode *node; struct prefix_ipv6 *ip_addr; + unsigned int addr_count = 0; for (ALL_LIST_ELEMENTS_RO(addresses, node, ip_addr)) { + if (addr_count >= 15) + break; + struct isis_ipv6_address *a = XCALLOC(MTYPE_ISIS_TLV, sizeof(*a)); a->addr = ip_addr->prefix; append_item(&tlvs->ipv6_address, (struct isis_item *)a); + addr_count++; } } @@ -3286,8 +4332,16 @@ static bool auth_validator_hmac_md5(struct isis_passwd *passwd, safe_auth_md5(stream, &checksum, &rem_lifetime); memset(STREAM_DATA(stream) + auth->offset, 0, 16); +#ifdef CRYPTO_OPENSSL + uint8_t *result = (uint8_t *)HMAC(EVP_md5(), passwd->passwd, + passwd->len, STREAM_DATA(stream), + stream_get_endp(stream), NULL, NULL); + + memcpy(digest, result, 16); +#elif CRYPTO_INTERNAL hmac_md5(STREAM_DATA(stream), stream_get_endp(stream), passwd->passwd, passwd->len, digest); +#endif memcpy(STREAM_DATA(stream) + auth->offset, auth->value, 16); bool rv = !memcmp(digest, auth->value, 16); @@ -3360,11 +4414,19 @@ static void tlvs_area_addresses_to_adj(struct isis_tlvs *tlvs, bool *changed) { if (adj->area_address_count != tlvs->area_addresses.count) { + uint32_t oc = adj->area_address_count; + *changed = true; adj->area_address_count = tlvs->area_addresses.count; adj->area_addresses = XREALLOC( MTYPE_ISIS_ADJACENCY_INFO, adj->area_addresses, adj->area_address_count * sizeof(*adj->area_addresses)); + + for (; oc < adj->area_address_count; oc++) { + adj->area_addresses[oc].addr_len = 0; + memset(&adj->area_addresses[oc].area_addr, 0, + sizeof(adj->area_addresses[oc].area_addr)); + } } struct isis_area_address *addr = NULL; @@ -3425,16 +4487,35 @@ static void tlvs_protocols_supported_to_adj(struct isis_tlvs *tlvs, memcpy(adj->nlpids.nlpids, reduced.nlpids, reduced.count); } +DEFINE_HOOK(isis_adj_ip_enabled_hook, (struct isis_adjacency *adj, int family), + (adj, family)) +DEFINE_HOOK(isis_adj_ip_disabled_hook, + (struct isis_adjacency *adj, int family), (adj, family)) + static void tlvs_ipv4_addresses_to_adj(struct isis_tlvs *tlvs, struct isis_adjacency *adj, bool *changed) { + bool ipv4_enabled = false; + + if (adj->ipv4_address_count == 0 && tlvs->ipv4_address.count > 0) + ipv4_enabled = true; + else if (adj->ipv4_address_count > 0 && tlvs->ipv4_address.count == 0) + hook_call(isis_adj_ip_disabled_hook, adj, AF_INET); + if (adj->ipv4_address_count != tlvs->ipv4_address.count) { + uint32_t oc = adj->ipv4_address_count; + *changed = true; adj->ipv4_address_count = tlvs->ipv4_address.count; adj->ipv4_addresses = XREALLOC( MTYPE_ISIS_ADJACENCY_INFO, adj->ipv4_addresses, adj->ipv4_address_count * sizeof(*adj->ipv4_addresses)); + + for (; oc < adj->ipv4_address_count; oc++) { + memset(&adj->ipv4_addresses[oc], 0, + sizeof(adj->ipv4_addresses[oc])); + } } struct isis_ipv4_address *addr = NULL; @@ -3452,18 +4533,35 @@ static void tlvs_ipv4_addresses_to_adj(struct isis_tlvs *tlvs, *changed = true; adj->ipv4_addresses[i] = addr->addr; } + + if (ipv4_enabled) + hook_call(isis_adj_ip_enabled_hook, adj, AF_INET); } static void tlvs_ipv6_addresses_to_adj(struct isis_tlvs *tlvs, struct isis_adjacency *adj, bool *changed) { + bool ipv6_enabled = false; + + if (adj->ipv6_address_count == 0 && tlvs->ipv6_address.count > 0) + ipv6_enabled = true; + else if (adj->ipv6_address_count > 0 && tlvs->ipv6_address.count == 0) + hook_call(isis_adj_ip_disabled_hook, adj, AF_INET6); + if (adj->ipv6_address_count != tlvs->ipv6_address.count) { + uint32_t oc = adj->ipv6_address_count; + *changed = true; adj->ipv6_address_count = tlvs->ipv6_address.count; adj->ipv6_addresses = XREALLOC( MTYPE_ISIS_ADJACENCY_INFO, adj->ipv6_addresses, adj->ipv6_address_count * sizeof(*adj->ipv6_addresses)); + + for (; oc < adj->ipv6_address_count; oc++) { + memset(&adj->ipv6_addresses[oc], 0, + sizeof(adj->ipv6_addresses[oc])); + } } struct isis_ipv6_address *addr = NULL; @@ -3481,6 +4579,9 @@ static void tlvs_ipv6_addresses_to_adj(struct isis_tlvs *tlvs, *changed = true; adj->ipv6_addresses[i] = addr->addr; } + + if (ipv6_enabled) + hook_call(isis_adj_ip_enabled_hook, adj, AF_INET6); } void isis_tlvs_to_adj(struct isis_tlvs *tlvs, struct isis_adjacency *adj, @@ -3533,7 +4634,7 @@ void isis_tlvs_add_csnp_entries(struct isis_tlvs *tlvs, uint8_t *start_id, if (!first) return; - for_each_from (lspdb, head, lsp, first) { + frr_each_from (lspdb, head, lsp, first) { if (memcmp(lsp->hdr.lsp_id, stop_id, sizeof(lsp->hdr.lsp_id)) > 0 || tlvs->lsp_entries.count == num_lsps) break; @@ -3551,6 +4652,18 @@ void isis_tlvs_set_dynamic_hostname(struct isis_tlvs *tlvs, tlvs->hostname = XSTRDUP(MTYPE_ISIS_TLV, hostname); } +/* Set Router Capability TLV parameters */ +void isis_tlvs_set_router_capability(struct isis_tlvs *tlvs, + const struct isis_router_cap *cap) +{ + XFREE(MTYPE_ISIS_TLV, tlvs->router_cap); + if (!cap) + return; + + tlvs->router_cap = XCALLOC(MTYPE_ISIS_TLV, sizeof(*tlvs->router_cap)); + *tlvs->router_cap = *cap; +} + void isis_tlvs_set_te_router_id(struct isis_tlvs *tlvs, const struct in_addr *id) { @@ -3572,25 +4685,79 @@ void isis_tlvs_add_oldstyle_ip_reach(struct isis_tlvs *tlvs, append_item(&tlvs->oldstyle_ip_reach, (struct isis_item *)r); } +/* Add IS-IS SR Adjacency-SID subTLVs */ +void isis_tlvs_add_adj_sid(struct isis_ext_subtlvs *exts, + struct isis_adj_sid *adj) +{ + append_item(&exts->adj_sid, (struct isis_item *)adj); + SET_SUBTLV(exts, EXT_ADJ_SID); +} + +/* Delete IS-IS SR Adjacency-SID subTLVs */ +void isis_tlvs_del_adj_sid(struct isis_ext_subtlvs *exts, + struct isis_adj_sid *adj) +{ + delete_item(&exts->adj_sid, (struct isis_item *)adj); + XFREE(MTYPE_ISIS_SUBTLV, adj); + if (exts->adj_sid.count == 0) + UNSET_SUBTLV(exts, EXT_ADJ_SID); +} + +/* Add IS-IS SR LAN-Adjacency-SID subTLVs */ +void isis_tlvs_add_lan_adj_sid(struct isis_ext_subtlvs *exts, + struct isis_lan_adj_sid *lan) +{ + append_item(&exts->lan_sid, (struct isis_item *)lan); + SET_SUBTLV(exts, EXT_LAN_ADJ_SID); +} + +/* Delete IS-IS SR LAN-Adjacency-SID subTLVs */ +void isis_tlvs_del_lan_adj_sid(struct isis_ext_subtlvs *exts, + struct isis_lan_adj_sid *lan) +{ + delete_item(&exts->lan_sid, (struct isis_item *)lan); + XFREE(MTYPE_ISIS_SUBTLV, lan); + if (exts->lan_sid.count == 0) + UNSET_SUBTLV(exts, EXT_LAN_ADJ_SID); +} + void isis_tlvs_add_extended_ip_reach(struct isis_tlvs *tlvs, - struct prefix_ipv4 *dest, uint32_t metric) + struct prefix_ipv4 *dest, uint32_t metric, + bool external, struct sr_prefix_cfg *pcfg) { struct isis_extended_ip_reach *r = XCALLOC(MTYPE_ISIS_TLV, sizeof(*r)); r->metric = metric; memcpy(&r->prefix, dest, sizeof(*dest)); apply_mask_ipv4(&r->prefix); + if (pcfg) { + struct isis_prefix_sid *psid = + XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*psid)); + + isis_sr_prefix_cfg2subtlv(pcfg, external, psid); + r->subtlvs = isis_alloc_subtlvs(ISIS_CONTEXT_SUBTLV_IP_REACH); + append_item(&r->subtlvs->prefix_sids, (struct isis_item *)psid); + } append_item(&tlvs->extended_ip_reach, (struct isis_item *)r); } void isis_tlvs_add_ipv6_reach(struct isis_tlvs *tlvs, uint16_t mtid, - struct prefix_ipv6 *dest, uint32_t metric) + struct prefix_ipv6 *dest, uint32_t metric, + bool external, struct sr_prefix_cfg *pcfg) { struct isis_ipv6_reach *r = XCALLOC(MTYPE_ISIS_TLV, sizeof(*r)); r->metric = metric; memcpy(&r->prefix, dest, sizeof(*dest)); apply_mask_ipv6(&r->prefix); + if (pcfg) { + struct isis_prefix_sid *psid = + XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*psid)); + + isis_sr_prefix_cfg2subtlv(pcfg, external, psid); + r->subtlvs = isis_alloc_subtlvs(ISIS_CONTEXT_SUBTLV_IP_REACH); + append_item(&r->subtlvs->prefix_sids, (struct isis_item *)psid); + } struct isis_item_list *l; l = (mtid == ISIS_MT_IPV4_UNICAST) @@ -3604,7 +4771,7 @@ void isis_tlvs_add_ipv6_dstsrc_reach(struct isis_tlvs *tlvs, uint16_t mtid, struct prefix_ipv6 *src, uint32_t metric) { - isis_tlvs_add_ipv6_reach(tlvs, mtid, dest, metric); + isis_tlvs_add_ipv6_reach(tlvs, mtid, dest, metric, false, NULL); struct isis_item_list *l = isis_get_mt_items(&tlvs->mt_ipv6_reach, mtid); @@ -3626,17 +4793,14 @@ void isis_tlvs_add_oldstyle_reach(struct isis_tlvs *tlvs, uint8_t *id, void isis_tlvs_add_extended_reach(struct isis_tlvs *tlvs, uint16_t mtid, uint8_t *id, uint32_t metric, - uint8_t *subtlvs, uint8_t subtlv_len) + struct isis_ext_subtlvs *exts) { struct isis_extended_reach *r = XCALLOC(MTYPE_ISIS_TLV, sizeof(*r)); memcpy(r->id, id, sizeof(r->id)); r->metric = metric; - if (subtlvs && subtlv_len) { - r->subtlvs = XCALLOC(MTYPE_ISIS_TLV, subtlv_len); - memcpy(r->subtlvs, subtlvs, subtlv_len); - r->subtlv_len = subtlv_len; - } + if (exts) + r->subtlvs = copy_item_ext_subtlvs(exts, mtid); struct isis_item_list *l; if (mtid == ISIS_MT_IPV4_UNICAST) @@ -3686,7 +4850,7 @@ void isis_tlvs_add_spine_leaf(struct isis_tlvs *tlvs, uint8_t tier, struct isis_mt_router_info * isis_tlvs_lookup_mt_router_info(struct isis_tlvs *tlvs, uint16_t mtid) { - if (tlvs->mt_router_info_empty) + if (!tlvs || tlvs->mt_router_info_empty) return NULL; struct isis_mt_router_info *rv; diff --git a/isisd/isis_tlvs.h b/isisd/isis_tlvs.h index 4954d791d8..1c0d97f2c3 100644 --- a/isisd/isis_tlvs.h +++ b/isisd/isis_tlvs.h @@ -2,6 +2,8 @@ * IS-IS TLV Serializer/Deserializer * * Copyright (C) 2015,2017 Christian Franke + + * Copyright (C) 2019 Olivier Dugeon - Orange Labs (for TE and SR) * * This file is part of FRR. * @@ -26,8 +28,11 @@ #include "openbsd-tree.h" #include "prefix.h" +DECLARE_MTYPE(ISIS_SUBTLV) + struct lspdb_head; struct isis_subtlvs; +struct sr_prefix_cfg; struct isis_area_address; struct isis_area_address { @@ -66,14 +71,14 @@ struct isis_lsp_entry { }; struct isis_extended_reach; +struct isis_ext_subtlvs; struct isis_extended_reach { struct isis_extended_reach *next; uint8_t id[7]; uint32_t metric; - uint8_t *subtlvs; - uint8_t subtlv_len; + struct isis_ext_subtlvs *subtlvs; }; struct isis_extended_ip_reach; @@ -130,6 +135,98 @@ struct isis_threeway_adj { uint32_t neighbor_circuit_id; }; +/* Segment Routing subTLV's as per RFC8667 */ +#define ISIS_SUBTLV_SRGB_FLAG_I 0x80 +#define ISIS_SUBTLV_SRGB_FLAG_V 0x40 +#define IS_SR_IPV4(srgb) (srgb.flags & ISIS_SUBTLV_SRGB_FLAG_I) +#define IS_SR_IPV6(srgb) (srgb.flags & ISIS_SUBTLV_SRGB_FLAG_V) +#define SUBTLV_SR_BLOCK_SIZE 6 +#define SUBTLV_RANGE_INDEX_SIZE 10 +#define SUBTLV_RANGE_LABEL_SIZE 9 + +/* Structure aggregating SR Global (SRGB) or Local (SRLB) Block info */ +struct isis_sr_block { + uint8_t flags; + uint32_t range_size; + uint32_t lower_bound; +}; + +/* Prefix-SID sub-TLVs flags */ +#define ISIS_PREFIX_SID_READVERTISED 0x80 +#define ISIS_PREFIX_SID_NODE 0x40 +#define ISIS_PREFIX_SID_NO_PHP 0x20 +#define ISIS_PREFIX_SID_EXPLICIT_NULL 0x10 +#define ISIS_PREFIX_SID_VALUE 0x08 +#define ISIS_PREFIX_SID_LOCAL 0x04 + +struct isis_prefix_sid; +struct isis_prefix_sid { + struct isis_prefix_sid *next; + + uint8_t flags; + uint8_t algorithm; + uint32_t value; +}; + +/* Adj-SID and LAN-Ajd-SID sub-TLVs flags */ +#define EXT_SUBTLV_LINK_ADJ_SID_FFLG 0x80 +#define EXT_SUBTLV_LINK_ADJ_SID_BFLG 0x40 +#define EXT_SUBTLV_LINK_ADJ_SID_VFLG 0x20 +#define EXT_SUBTLV_LINK_ADJ_SID_LFLG 0x10 +#define EXT_SUBTLV_LINK_ADJ_SID_SFLG 0x08 +#define EXT_SUBTLV_LINK_ADJ_SID_PFLG 0x04 + +struct isis_adj_sid; +struct isis_adj_sid { + struct isis_adj_sid *next; + + uint8_t family; + uint8_t flags; + uint8_t weight; + uint32_t sid; +}; + +struct isis_lan_adj_sid; +struct isis_lan_adj_sid { + struct isis_lan_adj_sid *next; + + uint8_t family; + uint8_t flags; + uint8_t weight; + uint8_t neighbor_id[ISIS_SYS_ID_LEN]; + uint32_t sid; +}; + +/* RFC 4971 & RFC 7981 */ +#define ISIS_ROUTER_CAP_FLAG_S 0x01 +#define ISIS_ROUTER_CAP_FLAG_D 0x02 +#define ISIS_ROUTER_CAP_SIZE 5 + +/* Number of supported algorithm for Segment Routing. + * Right now only 2 have been standardized: + * - 0: SPF + * - 1: Strict SPF + */ +#define SR_ALGORITHM_COUNT 2 +#define SR_ALGORITHM_SPF 0 +#define SR_ALGORITHM_STRICT_SPF 1 +#define SR_ALGORITHM_UNSET 255 + +#define MSD_TYPE_BASE_MPLS_IMPOSITION 0x01 +#define MSD_TLV_SIZE 2 + +struct isis_router_cap { + struct in_addr router_id; + uint8_t flags; + + /* RFC 8667 section #3 */ + struct isis_sr_block srgb; + struct isis_sr_block srlb; + uint8_t algo[SR_ALGORITHM_COUNT]; + /* RFC 8491 */ + uint8_t msd; +}; + struct isis_item; struct isis_item { struct isis_item *next; @@ -233,26 +330,10 @@ struct isis_tlvs { struct isis_item_list ipv6_reach; struct isis_mt_item_list mt_ipv6_reach; struct isis_threeway_adj *threeway_adj; + struct isis_router_cap *router_cap; struct isis_spine_leaf *spine_leaf; }; -#define ISIS_PREFIX_SID_READVERTISED 0x80 -#define ISIS_PREFIX_SID_NODE 0x40 -#define ISIS_PREFIX_SID_NO_PHP 0x20 -#define ISIS_PREFIX_SID_EXPLICIT_NULL 0x10 -#define ISIS_PREFIX_SID_VALUE 0x08 -#define ISIS_PREFIX_SID_LOCAL 0x04 - -struct isis_prefix_sid; -struct isis_prefix_sid { - struct isis_prefix_sid *next; - - uint8_t flags; - uint8_t algorithm; - - uint32_t value; -}; - enum isis_tlv_context { ISIS_CONTEXT_LSP, ISIS_CONTEXT_SUBTLV_NE_REACH, @@ -266,11 +347,12 @@ struct isis_subtlvs { /* draft-baker-ipv6-isis-dst-src-routing-06 */ struct prefix_ipv6 *source_prefix; - /* draft-ietf-isis-segment-routing-extensions-16 */ + /* RFC 8667 section #2.4 */ struct isis_item_list prefix_sids; }; enum isis_tlv_type { + /* TLVs code point */ ISIS_TLV_AREA_ADDRESSES = 1, ISIS_TLV_OLDSTYLE_REACH = 2, ISIS_TLV_LAN_NEIGHBORS = 6, @@ -295,10 +377,157 @@ enum isis_tlv_type { ISIS_TLV_IPV6_REACH = 236, ISIS_TLV_MT_IPV6_REACH = 237, ISIS_TLV_THREE_WAY_ADJ = 240, + ISIS_TLV_ROUTER_CAPABILITY = 242, ISIS_TLV_MAX = 256, + /* subTLVs code point */ + ISIS_SUBTLV_IPV6_SOURCE_PREFIX = 22, + + /* RFC 5305 & RFC 6119 */ + ISIS_SUBTLV_ADMIN_GRP = 3, + ISIS_SUBTLV_LOCAL_IPADDR = 6, + ISIS_SUBTLV_RMT_IPADDR = 8, + ISIS_SUBTLV_MAX_BW = 9, + ISIS_SUBTLV_MAX_RSV_BW = 10, + ISIS_SUBTLV_UNRSV_BW = 11, + ISIS_SUBTLV_LOCAL_IPADDR6 = 12, + ISIS_SUBTLV_RMT_IPADDR6 = 13, + ISIS_SUBTLV_TE_METRIC = 18, + + /* RFC 5307 */ + ISIS_SUBTLV_LLRI = 4, + + /* RFC 8491 */ + ISIS_SUBTLV_NODE_MSD = 23, + + /* RFC 5316 */ + ISIS_SUBTLV_RAS = 24, + ISIS_SUBTLV_RIP = 25, + + /* RFC 8667 section #4 IANA allocation */ + ISIS_SUBTLV_SID_LABEL = 1, + ISIS_SUBTLV_SID_LABEL_RANGE = 2, + ISIS_SUBTLV_ALGORITHM = 19, + ISIS_SUBTLV_SRLB = 22, ISIS_SUBTLV_PREFIX_SID = 3, - ISIS_SUBTLV_IPV6_SOURCE_PREFIX = 22 + ISIS_SUBTLV_ADJ_SID = 31, + ISIS_SUBTLV_LAN_ADJ_SID = 32, + + /* RFC 7810 */ + ISIS_SUBTLV_AV_DELAY = 33, + ISIS_SUBTLV_MM_DELAY = 34, + ISIS_SUBTLV_DELAY_VAR = 35, + ISIS_SUBTLV_PKT_LOSS = 36, + ISIS_SUBTLV_RES_BW = 37, + ISIS_SUBTLV_AVA_BW = 38, + ISIS_SUBTLV_USE_BW = 39, + + ISIS_SUBTLV_MAX = 40 +}; + +/* subTLVs size for TE and SR */ +enum ext_subtlv_size { + /* RFC 5307 */ + ISIS_SUBTLV_LLRI_SIZE = 8, + + /* RFC 5305 & RFC 6119 */ + ISIS_SUBTLV_UNRSV_BW_SIZE = 32, + ISIS_SUBTLV_TE_METRIC_SIZE = 3, + ISIS_SUBTLV_IPV6_ADDR_SIZE = 16, + + /* RFC 8491 */ + ISIS_SUBTLV_NODE_MSD_SIZE = 2, + + /* RFC 8667 sections #2 & #3 */ + ISIS_SUBTLV_SID_LABEL_SIZE = 3, + ISIS_SUBTLV_SID_LABEL_RANGE_SIZE = 9, + ISIS_SUBTLV_ALGORITHM_SIZE = 4, + ISIS_SUBTLV_ADJ_SID_SIZE = 5, + ISIS_SUBTLV_LAN_ADJ_SID_SIZE = 11, + ISIS_SUBTLV_PREFIX_SID_SIZE = 5, + + /* RFC 7810 */ + ISIS_SUBTLV_MM_DELAY_SIZE = 8, + + ISIS_SUBTLV_HDR_SIZE = 2, + ISIS_SUBTLV_DEF_SIZE = 4, + + ISIS_SUBTLV_MAX_SIZE = 180 +}; + +/* Macros to manage the optional presence of EXT subTLVs */ +#define SET_SUBTLV(s, t) ((s->status) |= (t)) +#define UNSET_SUBTLV(s, t) ((s->status) &= ~(t)) +#define IS_SUBTLV(s, t) (s->status & t) + +#define EXT_DISABLE 0x000000 +#define EXT_ADM_GRP 0x000001 +#define EXT_LLRI 0x000002 +#define EXT_LOCAL_ADDR 0x000004 +#define EXT_NEIGH_ADDR 0x000008 +#define EXT_LOCAL_ADDR6 0x000010 +#define EXT_NEIGH_ADDR6 0x000020 +#define EXT_MAX_BW 0x000040 +#define EXT_MAX_RSV_BW 0x000080 +#define EXT_UNRSV_BW 0x000100 +#define EXT_TE_METRIC 0x000200 +#define EXT_RMT_AS 0x000400 +#define EXT_RMT_IP 0x000800 +#define EXT_ADJ_SID 0x001000 +#define EXT_LAN_ADJ_SID 0x002000 +#define EXT_DELAY 0x004000 +#define EXT_MM_DELAY 0x008000 +#define EXT_DELAY_VAR 0x010000 +#define EXT_PKT_LOSS 0x020000 +#define EXT_RES_BW 0x040000 +#define EXT_AVA_BW 0x080000 +#define EXT_USE_BW 0x100000 + +/* + * This structure groups all Extended IS Reachability subTLVs. + * + * Each bit of the status field indicates if a subTLVs is valid or not. + * SubTLVs values use following units: + * - Bandwidth in bytes/sec following IEEE format, + * - Delay in micro-seconds with only 24 bits significant + * - Packet Loss in percentage of total traffic with only 24 bits (2^24 - 2) + * + * For Delay and packet Loss, upper bit (A) indicates if the value is + * normal (0) or anomalous (1). + */ +#define IS_ANORMAL(v) (v & 0x80000000) + +struct isis_ext_subtlvs { + + uint32_t status; + + uint32_t adm_group; /* Resource Class/Color - RFC 5305 */ + /* Link Local/Remote Identifiers - RFC 5307 */ + uint32_t local_llri; + uint32_t remote_llri; + struct in_addr local_addr; /* Local IP Address - RFC 5305 */ + struct in_addr neigh_addr; /* Neighbor IP Address - RFC 5305 */ + struct in6_addr local_addr6; /* Local IPv6 Address - RFC 6119 */ + struct in6_addr neigh_addr6; /* Neighbor IPv6 Address - RFC 6119 */ + float max_bw; /* Maximum Bandwidth - RFC 5305 */ + float max_rsv_bw; /* Maximum Reservable Bandwidth - RFC 5305 */ + float unrsv_bw[8]; /* Unreserved Bandwidth - RFC 5305 */ + uint32_t te_metric; /* Traffic Engineering Metric - RFC 5305 */ + uint32_t remote_as; /* Remote AS Number sub-TLV - RFC5316 */ + struct in_addr remote_ip; /* IPv4 Remote ASBR ID Sub-TLV - RFC5316 */ + + uint32_t delay; /* Average Link Delay - RFC 8570 */ + uint32_t min_delay; /* Low Link Delay - RFC 8570 */ + uint32_t max_delay; /* High Link Delay - RFC 8570 */ + uint32_t delay_var; /* Link Delay Variation i.e. Jitter - RFC 8570 */ + uint32_t pkt_loss; /* Unidirectional Link Packet Loss - RFC 8570 */ + float res_bw; /* Unidirectional Residual Bandwidth - RFC 8570 */ + float ava_bw; /* Unidirectional Available Bandwidth - RFC 8570 */ + float use_bw; /* Unidirectional Utilized Bandwidth - RFC 8570 */ + + /* Segment Routing Adjacency & LAN Adjacency Segment ID */ + struct isis_item_list adj_sid; + struct isis_item_list lan_sid; }; #define IS_COMPAT_MT_TLV(tlv_type) \ @@ -329,7 +558,6 @@ struct list *isis_fragment_tlvs(struct isis_tlvs *tlvs, size_t size); #define ISIS_MT_AT_MASK 0x4000 #endif - void isis_tlvs_add_auth(struct isis_tlvs *tlvs, struct isis_passwd *passwd); void isis_tlvs_add_area_addresses(struct isis_tlvs *tlvs, struct list *addresses); @@ -359,23 +587,37 @@ void isis_tlvs_add_csnp_entries(struct isis_tlvs *tlvs, uint8_t *start_id, struct isis_lsp **last_lsp); void isis_tlvs_set_dynamic_hostname(struct isis_tlvs *tlvs, const char *hostname); +void isis_tlvs_set_router_capability(struct isis_tlvs *tlvs, + const struct isis_router_cap *cap); void isis_tlvs_set_te_router_id(struct isis_tlvs *tlvs, const struct in_addr *id); void isis_tlvs_add_oldstyle_ip_reach(struct isis_tlvs *tlvs, struct prefix_ipv4 *dest, uint8_t metric); void isis_tlvs_add_extended_ip_reach(struct isis_tlvs *tlvs, - struct prefix_ipv4 *dest, uint32_t metric); + struct prefix_ipv4 *dest, uint32_t metric, + bool external, struct sr_prefix_cfg *pcfg); void isis_tlvs_add_ipv6_reach(struct isis_tlvs *tlvs, uint16_t mtid, - struct prefix_ipv6 *dest, uint32_t metric); + struct prefix_ipv6 *dest, uint32_t metric, + bool external, struct sr_prefix_cfg *pcfg); void isis_tlvs_add_ipv6_dstsrc_reach(struct isis_tlvs *tlvs, uint16_t mtid, struct prefix_ipv6 *dest, struct prefix_ipv6 *src, uint32_t metric); +struct isis_ext_subtlvs *isis_alloc_ext_subtlvs(void); +void isis_tlvs_add_adj_sid(struct isis_ext_subtlvs *exts, + struct isis_adj_sid *adj); +void isis_tlvs_del_adj_sid(struct isis_ext_subtlvs *exts, + struct isis_adj_sid *adj); +void isis_tlvs_add_lan_adj_sid(struct isis_ext_subtlvs *exts, + struct isis_lan_adj_sid *lan); +void isis_tlvs_del_lan_adj_sid(struct isis_ext_subtlvs *exts, + struct isis_lan_adj_sid *lan); + void isis_tlvs_add_oldstyle_reach(struct isis_tlvs *tlvs, uint8_t *id, uint8_t metric); void isis_tlvs_add_extended_reach(struct isis_tlvs *tlvs, uint16_t mtid, uint8_t *id, uint32_t metric, - uint8_t *subtlvs, uint8_t subtlv_len); + struct isis_ext_subtlvs *subtlvs); const char *isis_threeway_state_name(enum isis_threeway_state state); diff --git a/isisd/isis_tx_queue.c b/isisd/isis_tx_queue.c index 6f46e6bec0..1424b55bdc 100644 --- a/isisd/isis_tx_queue.c +++ b/isisd/isis_tx_queue.c @@ -3,7 +3,7 @@ * * Copyright (C) 2018 Christian Franke * - * This file is part of FreeRangeRouting (FRR) + * This file is part of FRRouting (FRR) * * FRR is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -50,9 +50,9 @@ struct isis_tx_queue_entry { struct isis_tx_queue *queue; }; -static unsigned tx_queue_hash_key(void *p) +static unsigned tx_queue_hash_key(const void *p) { - struct isis_tx_queue_entry *e = p; + const struct isis_tx_queue_entry *e = p; uint32_t id_key = jhash(e->lsp->hdr.lsp_id, ISIS_SYS_ID_LEN + 2, 0x55aa5a5a); @@ -144,7 +144,7 @@ void _isis_tx_queue_add(struct isis_tx_queue *queue, if (!queue) return; - if (isis->debugs & DEBUG_TX_QUEUE) { + if (IS_DEBUG_TX_QUEUE) { zlog_debug("Add LSP %s to %s queue as %s LSP. (From %s %s:%d)", rawlspid_print(lsp->hdr.lsp_id), queue->circuit->interface->name, @@ -183,7 +183,7 @@ void _isis_tx_queue_del(struct isis_tx_queue *queue, struct isis_lsp *lsp, if (!e) return; - if (isis->debugs & DEBUG_TX_QUEUE) { + if (IS_DEBUG_TX_QUEUE) { zlog_debug("Remove LSP %s from %s queue. (From %s %s:%d)", rawlspid_print(lsp->hdr.lsp_id), queue->circuit->interface->name, diff --git a/isisd/isis_tx_queue.h b/isisd/isis_tx_queue.h index c2beda45b7..f0f1184d58 100644 --- a/isisd/isis_tx_queue.h +++ b/isisd/isis_tx_queue.h @@ -3,7 +3,7 @@ * * Copyright (C) 2018 Christian Franke * - * This file is part of FreeRangeRouting (FRR) + * This file is part of FRRouting (FRR) * * FRR is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the diff --git a/isisd/isis_vty_common.c b/isisd/isis_vty_common.c deleted file mode 100644 index 06432db0b2..0000000000 --- a/isisd/isis_vty_common.c +++ /dev/null @@ -1,109 +0,0 @@ -/* - * IS-IS Rout(e)ing protocol - isis_vty_common.c - * - * This file contains the CLI that is shared between OpenFabric and IS-IS - * - * Copyright (C) 2001,2002 Sampo Saaristo - * Tampere University of Technology - * Institute of Communications Engineering - * Copyright (C) 2016 David Lamparter, for NetDEF, Inc. - * Copyright (C) 2018 Christian Franke, for NetDEF, Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public Licenseas published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful,but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; see the file COPYING; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include - -#include "command.h" -#include "bfd.h" - -#include "isis_circuit.h" -#include "isis_csm.h" -#include "isis_misc.h" -#include "isisd.h" -#include "isis_bfd.h" -#include "isis_vty_common.h" - -struct isis_circuit *isis_circuit_lookup(struct vty *vty) -{ - struct interface *ifp = VTY_GET_CONTEXT(interface); - struct isis_circuit *circuit; - - if (!ifp) { - vty_out(vty, "Invalid interface \n"); - return NULL; - } - - circuit = circuit_scan_by_ifp(ifp); - if (!circuit) { - vty_out(vty, "ISIS is not enabled on circuit %s\n", ifp->name); - return NULL; - } - - return circuit; -} - -DEFUN (isis_bfd, - isis_bfd_cmd, - PROTO_NAME " bfd", - PROTO_HELP - "Enable BFD support\n") -{ - struct isis_circuit *circuit = isis_circuit_lookup(vty); - - if (!circuit) - return CMD_ERR_NO_MATCH; - - if (circuit->bfd_info - && CHECK_FLAG(circuit->bfd_info->flags, BFD_FLAG_PARAM_CFG)) { - return CMD_SUCCESS; - } - - isis_bfd_circuit_param_set(circuit, BFD_DEF_MIN_RX, - BFD_DEF_MIN_TX, BFD_DEF_DETECT_MULT, true); - - return CMD_SUCCESS; -} - -DEFUN (no_isis_bfd, - no_isis_bfd_cmd, - "no " PROTO_NAME " bfd", - NO_STR - PROTO_HELP - "Disables BFD support\n" -) -{ - struct isis_circuit *circuit = isis_circuit_lookup(vty); - - if (!circuit) - return CMD_ERR_NO_MATCH; - - if (!circuit->bfd_info) - return CMD_SUCCESS; - - isis_bfd_circuit_cmd(circuit, ZEBRA_BFD_DEST_DEREGISTER); - bfd_info_free(&circuit->bfd_info); - return CMD_SUCCESS; -} - -void isis_vty_init(void) -{ - install_element(INTERFACE_NODE, &isis_bfd_cmd); - install_element(INTERFACE_NODE, &no_isis_bfd_cmd); - -#ifdef FABRICD - isis_vty_daemon_init(); -#endif /* ifdef FABRICD */ -} diff --git a/isisd/isis_vty_fabricd.c b/isisd/isis_vty_fabricd.c index 7f2061692f..d0a411a8db 100644 --- a/isisd/isis_vty_fabricd.c +++ b/isisd/isis_vty_fabricd.c @@ -23,8 +23,9 @@ #include "command.h" +#include "lib/bfd.h" +#include "isisd/isis_bfd.h" #include "isisd/isisd.h" -#include "isisd/isis_vty_common.h" #include "isisd/fabricd.h" #include "isisd/isis_tlvs.h" #include "isisd/isis_misc.h" @@ -34,6 +35,25 @@ #include "lib/spf_backoff.h" #include "isisd/isis_mt.h" +static struct isis_circuit *isis_circuit_lookup(struct vty *vty) +{ + struct interface *ifp = VTY_GET_CONTEXT(interface); + struct isis_circuit *circuit; + + if (!ifp) { + vty_out(vty, "Invalid interface \n"); + return NULL; + } + + circuit = circuit_scan_by_ifp(ifp); + if (!circuit) { + vty_out(vty, "ISIS is not enabled on circuit %s\n", ifp->name); + return NULL; + } + + return circuit; +} + DEFUN (fabric_tier, fabric_tier_cmd, "fabric-tier (0-14)", @@ -92,11 +112,13 @@ DEFUN (no_triggered_csnp, return CMD_SUCCESS; } -static void lsp_print_flooding(struct vty *vty, struct isis_lsp *lsp) +static void lsp_print_flooding(struct vty *vty, struct isis_lsp *lsp, + struct isis *isis) { char lspid[255]; + char buf[MONOTIME_STRLEN]; - lspid_print(lsp->hdr.lsp_id, lspid, true, true); + lspid_print(lsp->hdr.lsp_id, lspid, true, true, isis); vty_out(vty, "Flooding information for %s\n", lspid); if (!lsp->flooding_neighbors[TX_LSP_NORMAL]) { @@ -109,23 +131,13 @@ static void lsp_print_flooding(struct vty *vty, struct isis_lsp *lsp) lsp->flooding_interface : "(null)"); time_t uptime = time(NULL) - lsp->flooding_time; - struct tm *tm = gmtime(&uptime); - - if (uptime < ONE_DAY_SECOND) - vty_out(vty, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min, - tm->tm_sec); - else if (uptime < ONE_WEEK_SECOND) - vty_out(vty, "%dd%02dh%02dm", tm->tm_yday, tm->tm_hour, - tm->tm_min); - else - vty_out(vty, "%02dw%dd%02dh", tm->tm_yday / 7, - tm->tm_yday - ((tm->tm_yday / 7) * 7), - tm->tm_hour); - vty_out(vty, " ago)\n"); + + frrtime_to_interval(uptime, buf, sizeof(buf)); + + vty_out(vty, "%s ago)\n", buf); if (lsp->flooding_circuit_scoped) { - vty_out(vty, " Received as circuit-scoped LSP, so not " - "flooded.\n"); + vty_out(vty, " Received as circuit-scoped LSP, so not flooded.\n"); return; } @@ -159,25 +171,29 @@ DEFUN (show_lsp_flooding, struct listnode *node; struct isis_area *area; + struct isis *isis = NULL; + + isis = isis_lookup_by_vrfid(VRF_DEFAULT); + + if (isis == NULL) { + vty_out(vty, "IS-IS Routing Process not enabled\n"); + return CMD_SUCCESS; + } for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) { struct lspdb_head *head = &area->lspdb[ISIS_LEVEL2 - 1]; struct isis_lsp *lsp; - vty_out(vty, "Area %s:\n", area->area_tag ? - area->area_tag : "null"); - + vty_out(vty, "Area %s:\n", + area->area_tag ? area->area_tag : "null"); if (lspid) { - struct isis_lsp *lsp = lsp_for_arg(head, lspid); - + lsp = lsp_for_arg(head, lspid, isis); if (lsp) - lsp_print_flooding(vty, lsp); - + lsp_print_flooding(vty, lsp, isis); continue; } - - for_each (lspdb, head, lsp) { - lsp_print_flooding(vty, lsp); + frr_each (lspdb, head, lsp) { + lsp_print_flooding(vty, lsp, isis); vty_out(vty, "\n"); } } @@ -211,9 +227,9 @@ DEFUN (ip_router_isis, } } - area = isis_area_lookup(area_tag); + area = isis_area_lookup(area_tag, VRF_DEFAULT); if (!area) - area = isis_area_create(area_tag); + area = isis_area_create(area_tag, VRF_DEFAULT_NAME); if (!circuit || !circuit->area) { circuit = isis_circuit_create(area, ifp); @@ -265,7 +281,7 @@ DEFUN (no_ip_router_isis, const char *af = argv[idx_afi]->arg; const char *area_tag = argv[idx_word]->arg; - area = isis_area_lookup(area_tag); + area = isis_area_lookup(area_tag, VRF_DEFAULT); if (!area) { vty_out(vty, "Can't find ISIS instance %s\n", area_tag); @@ -288,6 +304,49 @@ DEFUN (no_ip_router_isis, return CMD_SUCCESS; } +DEFUN (isis_bfd, + isis_bfd_cmd, + PROTO_NAME " bfd", + PROTO_HELP + "Enable BFD support\n") +{ + struct isis_circuit *circuit = isis_circuit_lookup(vty); + + if (!circuit) + return CMD_ERR_NO_MATCH; + + if (circuit->bfd_info + && CHECK_FLAG(circuit->bfd_info->flags, BFD_FLAG_PARAM_CFG)) { + return CMD_SUCCESS; + } + + isis_bfd_circuit_param_set(circuit, BFD_DEF_MIN_RX, BFD_DEF_MIN_TX, + BFD_DEF_DETECT_MULT, NULL, true); + + return CMD_SUCCESS; +} + +DEFUN (no_isis_bfd, + no_isis_bfd_cmd, + "no " PROTO_NAME " bfd", + NO_STR + PROTO_HELP + "Disables BFD support\n" +) +{ + struct isis_circuit *circuit = isis_circuit_lookup(vty); + + if (!circuit) + return CMD_ERR_NO_MATCH; + + if (!circuit->bfd_info) + return CMD_SUCCESS; + + isis_bfd_circuit_cmd(circuit, ZEBRA_BFD_DEST_DEREGISTER); + bfd_info_free(&circuit->bfd_info); + return CMD_SUCCESS; +} + DEFUN (set_overload_bit, set_overload_bit_cmd, "set-overload-bit", @@ -382,8 +441,7 @@ isis_vty_lsp_gen_interval_set(struct vty *vty, int level, uint16_t interval) if (interval >= area->lsp_refresh[lvl - 1]) { vty_out(vty, - "LSP gen interval %us must be less than " - "the LSP refresh interval %us\n", + "LSP gen interval %us must be less than the LSP refresh interval %us\n", interval, area->lsp_refresh[lvl - 1]); return CMD_WARNING_CONFIG_FAILED; } @@ -433,15 +491,13 @@ isis_vty_lsp_refresh_set(struct vty *vty, int level, uint16_t interval) continue; if (interval <= area->lsp_gen_interval[lvl - 1]) { vty_out(vty, - "LSP refresh interval %us must be greater than " - "the configured LSP gen interval %us\n", + "LSP refresh interval %us must be greater than the configured LSP gen interval %us\n", interval, area->lsp_gen_interval[lvl - 1]); return CMD_WARNING_CONFIG_FAILED; } if (interval > (area->max_lsp_lifetime[lvl - 1] - 300)) { vty_out(vty, - "LSP refresh interval %us must be less than " - "the configured LSP lifetime %us less 300\n", + "LSP refresh interval %us must be less than the configured LSP lifetime %us less 300\n", interval, area->max_lsp_lifetime[lvl - 1]); return CMD_WARNING_CONFIG_FAILED; } @@ -491,20 +547,17 @@ isis_vty_max_lsp_lifetime_set(struct vty *vty, int level, uint16_t interval) if (refresh_interval < area->lsp_refresh[lvl - 1]) { vty_out(vty, - "Level %d Max LSP lifetime %us must be 300s greater than " - "the configured LSP refresh interval %us\n", + "Level %d Max LSP lifetime %us must be 300s greater than the configured LSP refresh interval %us\n", lvl, interval, area->lsp_refresh[lvl - 1]); vty_out(vty, - "Automatically reducing level %d LSP refresh interval " - "to %us\n", + "Automatically reducing level %d LSP refresh interval to %us\n", lvl, refresh_interval); set_refresh_interval[lvl - 1] = 1; if (refresh_interval <= area->lsp_gen_interval[lvl - 1]) { vty_out(vty, - "LSP refresh interval %us must be greater than " - "the configured LSP gen interval %us\n", + "LSP refresh interval %us must be greater than the configured LSP gen interval %us\n", refresh_interval, area->lsp_gen_interval[lvl - 1]); return CMD_WARNING_CONFIG_FAILED; @@ -790,8 +843,7 @@ DEFUN (isis_metric, if (circuit->area && circuit->area->oldmetric == 1 && met > MAX_NARROW_LINK_METRIC) { vty_out(vty, - "Invalid metric %d - should be <0-63> " - "when narrow metric type enabled\n", + "Invalid metric %d - should be <0-63> when narrow metric type enabled\n", met); return CMD_WARNING_CONFIG_FAILED; } @@ -800,8 +852,7 @@ DEFUN (isis_metric, if (circuit->area && circuit->area->newmetric == 1 && met > MAX_WIDE_LINK_METRIC) { vty_out(vty, - "Invalid metric %d - should be <0-16777215> " - "when wide metric type enabled\n", + "Invalid metric %d - should be <0-16777215> when wide metric type enabled\n", met); return CMD_WARNING_CONFIG_FAILED; } @@ -1045,6 +1096,8 @@ void isis_vty_daemon_init(void) install_element(INTERFACE_NODE, &ip_router_isis_cmd); install_element(INTERFACE_NODE, &ip6_router_isis_cmd); install_element(INTERFACE_NODE, &no_ip_router_isis_cmd); + install_element(INTERFACE_NODE, &isis_bfd_cmd); + install_element(INTERFACE_NODE, &no_isis_bfd_cmd); install_element(ROUTER_NODE, &set_overload_bit_cmd); install_element(ROUTER_NODE, &no_set_overload_bit_cmd); diff --git a/isisd/isis_zebra.c b/isisd/isis_zebra.c index 904ab99c79..08d5cc1b61 100644 --- a/isisd/isis_zebra.c +++ b/isisd/isis_zebra.c @@ -27,6 +27,7 @@ #include "command.h" #include "memory.h" #include "log.h" +#include "lib_errors.h" #include "if.h" #include "network.h" #include "prefix.h" @@ -48,17 +49,26 @@ #include "isisd/isis_lsp.h" #include "isisd/isis_route.h" #include "isisd/isis_zebra.h" +#include "isisd/isis_adjacency.h" #include "isisd/isis_te.h" +#include "isisd/isis_sr.h" -struct zclient *zclient = NULL; +struct zclient *zclient; +static struct zclient *zclient_sync; /* Router-id update message from zebra. */ -static int isis_router_id_update_zebra(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int isis_router_id_update_zebra(ZAPI_CALLBACK_ARGS) { struct isis_area *area; struct listnode *node; struct prefix router_id; + struct isis *isis = NULL; + + isis = isis_lookup_by_vrfid(vrf_id); + + if (isis == NULL) { + return -1; + } zebra_router_id_update_read(zclient->ibuf, &router_id); if (isis->router_id == router_id.u.prefix4.s_addr) @@ -72,87 +82,14 @@ static int isis_router_id_update_zebra(int command, struct zclient *zclient, return 0; } -static int isis_zebra_if_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) -{ - struct interface *ifp; - - ifp = zebra_interface_add_read(zclient->ibuf, vrf_id); - - if (if_is_operative(ifp)) - isis_csm_state_change(IF_UP_FROM_Z, circuit_scan_by_ifp(ifp), - ifp); - - return 0; -} - -static int isis_zebra_if_del(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) -{ - struct interface *ifp; - struct stream *s; - - s = zclient->ibuf; - ifp = zebra_interface_state_read(s, vrf_id); - - if (!ifp) - return 0; - - if (if_is_operative(ifp)) - zlog_warn("Zebra: got delete of %s, but interface is still up", - ifp->name); - - isis_csm_state_change(IF_DOWN_FROM_Z, circuit_scan_by_ifp(ifp), ifp); - - /* Cannot call if_delete because we should retain the pseudo interface - in case there is configuration info attached to it. */ - if_delete_retain(ifp); - - if_set_index(ifp, IFINDEX_INTERNAL); - - return 0; -} - -static int isis_zebra_if_state_up(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) -{ - struct interface *ifp; - - ifp = zebra_interface_state_read(zclient->ibuf, vrf_id); - - if (ifp == NULL) - return 0; - - isis_csm_state_change(IF_UP_FROM_Z, circuit_scan_by_ifp(ifp), ifp); - - return 0; -} - -static int isis_zebra_if_state_down(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int isis_zebra_if_address_add(ZAPI_CALLBACK_ARGS) { - struct interface *ifp; struct isis_circuit *circuit; - - ifp = zebra_interface_state_read(zclient->ibuf, vrf_id); - - if (ifp == NULL) - return 0; - - circuit = isis_csm_state_change(IF_DOWN_FROM_Z, - circuit_scan_by_ifp(ifp), ifp); - if (circuit) - SET_FLAG(circuit->flags, ISIS_CIRCUIT_FLAPPED_AFTER_SPF); - - return 0; -} - -static int isis_zebra_if_address_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) -{ struct connected *c; +#ifdef EXTREME_DEBUG struct prefix *p; char buf[PREFIX2STR_BUFFER]; +#endif /* EXTREME_DEBUG */ c = zebra_interface_address_read(ZEBRA_INTERFACE_ADDRESS_ADD, zclient->ibuf, vrf_id); @@ -160,26 +97,29 @@ static int isis_zebra_if_address_add(int command, struct zclient *zclient, if (c == NULL) return 0; +#ifdef EXTREME_DEBUG p = c->address; - prefix2str(p, buf, sizeof(buf)); -#ifdef EXTREME_DEBUG + if (p->family == AF_INET) zlog_debug("connected IP address %s", buf); if (p->family == AF_INET6) zlog_debug("connected IPv6 address %s", buf); #endif /* EXTREME_DEBUG */ - if (if_is_operative(c->ifp)) - isis_circuit_add_addr(circuit_scan_by_ifp(c->ifp), c); + + if (if_is_operative(c->ifp)) { + circuit = circuit_scan_by_ifp(c->ifp); + if (circuit) + isis_circuit_add_addr(circuit, c); + } return 0; } -static int isis_zebra_if_address_del(int command, struct zclient *client, - zebra_size_t length, vrf_id_t vrf_id) +static int isis_zebra_if_address_del(ZAPI_CALLBACK_ARGS) { + struct isis_circuit *circuit; struct connected *c; - struct interface *ifp; #ifdef EXTREME_DEBUG struct prefix *p; char buf[PREFIX2STR_BUFFER]; @@ -191,8 +131,6 @@ static int isis_zebra_if_address_del(int command, struct zclient *client, if (c == NULL) return 0; - ifp = c->ifp; - #ifdef EXTREME_DEBUG p = c->address; prefix2str(p, buf, sizeof(buf)); @@ -203,15 +141,18 @@ static int isis_zebra_if_address_del(int command, struct zclient *client, zlog_debug("disconnected IPv6 address %s", buf); #endif /* EXTREME_DEBUG */ - if (if_is_operative(ifp)) - isis_circuit_del_addr(circuit_scan_by_ifp(ifp), c); - connected_free(c); + if (if_is_operative(c->ifp)) { + circuit = circuit_scan_by_ifp(c->ifp); + if (circuit) + isis_circuit_del_addr(circuit, c); + } + + connected_free(&c); return 0; } -static int isis_zebra_link_params(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int isis_zebra_link_params(ZAPI_CALLBACK_ARGS) { struct interface *ifp; @@ -226,22 +167,22 @@ static int isis_zebra_link_params(int command, struct zclient *zclient, return 0; } -static void isis_zebra_route_add_route(struct prefix *prefix, - struct prefix_ipv6 *src_p, - struct isis_route_info *route_info) +void isis_zebra_route_add_route(struct isis *isis, + struct prefix *prefix, + struct prefix_ipv6 *src_p, + struct isis_route_info *route_info) { struct zapi_route api; struct zapi_nexthop *api_nh; struct isis_nexthop *nexthop; - struct isis_nexthop6 *nexthop6; struct listnode *node; int count = 0; - if (CHECK_FLAG(route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED)) + if (zclient->sock < 0) return; memset(&api, 0, sizeof(api)); - api.vrf_id = VRF_DEFAULT; + api.vrf_id = isis->vrf_id; api.type = PROTO_TYPE; api.safi = SAFI_UNICAST; api.prefix = *prefix; @@ -258,47 +199,41 @@ static void isis_zebra_route_add_route(struct prefix *prefix, #endif /* Nexthops */ - switch (prefix->family) { - case AF_INET: - for (ALL_LIST_ELEMENTS_RO(route_info->nexthops, node, - nexthop)) { - if (count >= MULTIPATH_NUM) - break; - api_nh = &api.nexthops[count]; - if (fabricd) - api_nh->onlink = true; - api_nh->vrf_id = VRF_DEFAULT; + for (ALL_LIST_ELEMENTS_RO(route_info->nexthops, node, nexthop)) { + if (count >= MULTIPATH_NUM) + break; + api_nh = &api.nexthops[count]; + if (fabricd) + SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_ONLINK); + api_nh->vrf_id = isis->vrf_id; + + switch (nexthop->family) { + case AF_INET: /* FIXME: can it be ? */ - if (nexthop->ip.s_addr != INADDR_ANY) { + if (nexthop->ip.ipv4.s_addr != INADDR_ANY) { api_nh->type = NEXTHOP_TYPE_IPV4_IFINDEX; - api_nh->gate.ipv4 = nexthop->ip; + api_nh->gate.ipv4 = nexthop->ip.ipv4; } else { api_nh->type = NEXTHOP_TYPE_IFINDEX; } - api_nh->ifindex = nexthop->ifindex; - count++; - } - break; - case AF_INET6: - for (ALL_LIST_ELEMENTS_RO(route_info->nexthops6, node, - nexthop6)) { - if (count >= MULTIPATH_NUM) - break; - if (!IN6_IS_ADDR_LINKLOCAL(&nexthop6->ip6) - && !IN6_IS_ADDR_UNSPECIFIED(&nexthop6->ip6)) { + break; + case AF_INET6: + if (!IN6_IS_ADDR_LINKLOCAL(&nexthop->ip.ipv6) + && !IN6_IS_ADDR_UNSPECIFIED(&nexthop->ip.ipv6)) { continue; } - - api_nh = &api.nexthops[count]; - if (fabricd) - api_nh->onlink = true; - api_nh->vrf_id = VRF_DEFAULT; - api_nh->gate.ipv6 = nexthop6->ip6; - api_nh->ifindex = nexthop6->ifindex; + api_nh->gate.ipv6 = nexthop->ip.ipv6; api_nh->type = NEXTHOP_TYPE_IPV6_IFINDEX; - count++; + break; + default: + flog_err(EC_LIB_DEVELOPMENT, + "%s: unknown address family [%d]", __func__, + nexthop->family); + exit(1); } - break; + + api_nh->ifindex = nexthop->ifindex; + count++; } if (!count) return; @@ -306,21 +241,20 @@ static void isis_zebra_route_add_route(struct prefix *prefix, api.nexthop_num = count; zclient_route_send(ZEBRA_ROUTE_ADD, zclient, &api); - SET_FLAG(route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED); - UNSET_FLAG(route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_RESYNC); } -static void isis_zebra_route_del_route(struct prefix *prefix, - struct prefix_ipv6 *src_p, - struct isis_route_info *route_info) +void isis_zebra_route_del_route(struct isis *isis, + struct prefix *prefix, + struct prefix_ipv6 *src_p, + struct isis_route_info *route_info) { struct zapi_route api; - if (!CHECK_FLAG(route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED)) + if (zclient->sock < 0) return; memset(&api, 0, sizeof(api)); - api.vrf_id = VRF_DEFAULT; + api.vrf_id = isis->vrf_id; api.type = PROTO_TYPE; api.safi = SAFI_UNICAST; api.prefix = *prefix; @@ -330,26 +264,171 @@ static void isis_zebra_route_del_route(struct prefix *prefix, } zclient_route_send(ZEBRA_ROUTE_DELETE, zclient, &api); - UNSET_FLAG(route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED); } -void isis_zebra_route_update(struct prefix *prefix, - struct prefix_ipv6 *src_p, - struct isis_route_info *route_info) +/** + * Install Prefix-SID in the forwarding plane through Zebra. + * + * @param srp Segment Routing Prefix-SID + */ +static void isis_zebra_prefix_install_prefix_sid(const struct sr_prefix *srp) { - if (zclient->sock < 0) + struct zapi_labels zl; + struct zapi_nexthop *znh; + struct listnode *node; + struct isis_nexthop *nexthop; + struct interface *ifp; + + /* Prepare message. */ + memset(&zl, 0, sizeof(zl)); + zl.type = ZEBRA_LSP_ISIS_SR; + zl.local_label = srp->input_label; + + switch (srp->type) { + case ISIS_SR_PREFIX_LOCAL: + ifp = if_lookup_by_name("lo", VRF_DEFAULT); + if (!ifp) { + zlog_warn( + "%s: couldn't install Prefix-SID %pFX: loopback interface not found", + __func__, &srp->prefix); + return; + } + + znh = &zl.nexthops[zl.nexthop_num++]; + znh->type = NEXTHOP_TYPE_IFINDEX; + znh->ifindex = ifp->ifindex; + znh->label_num = 1; + znh->labels[0] = MPLS_LABEL_IMPLICIT_NULL; + break; + case ISIS_SR_PREFIX_REMOTE: + /* Update route in the RIB too. */ + SET_FLAG(zl.message, ZAPI_LABELS_FTN); + zl.route.prefix = srp->prefix; + zl.route.type = ZEBRA_ROUTE_ISIS; + zl.route.instance = 0; + + for (ALL_LIST_ELEMENTS_RO(srp->u.remote.rinfo->nexthops, node, + nexthop)) { + if (nexthop->sr.label == MPLS_INVALID_LABEL) + continue; + + if (zl.nexthop_num >= MULTIPATH_NUM) + break; + + znh = &zl.nexthops[zl.nexthop_num++]; + znh->type = (srp->prefix.family == AF_INET) + ? NEXTHOP_TYPE_IPV4_IFINDEX + : NEXTHOP_TYPE_IPV6_IFINDEX; + znh->gate = nexthop->ip; + znh->ifindex = nexthop->ifindex; + znh->label_num = 1; + znh->labels[0] = nexthop->sr.label; + } + break; + } + + /* Send message to zebra. */ + (void)zebra_send_mpls_labels(zclient, ZEBRA_MPLS_LABELS_REPLACE, &zl); +} + +/** + * Uninstall Prefix-SID from the forwarding plane through Zebra. + * + * @param srp Segment Routing Prefix-SID + */ +static void isis_zebra_uninstall_prefix_sid(const struct sr_prefix *srp) +{ + struct zapi_labels zl; + + /* Prepare message. */ + memset(&zl, 0, sizeof(zl)); + zl.type = ZEBRA_LSP_ISIS_SR; + zl.local_label = srp->input_label; + + if (srp->type == ISIS_SR_PREFIX_REMOTE) { + /* Update route in the RIB too. */ + SET_FLAG(zl.message, ZAPI_LABELS_FTN); + zl.route.prefix = srp->prefix; + zl.route.type = ZEBRA_ROUTE_ISIS; + zl.route.instance = 0; + } + + /* Send message to zebra. */ + (void)zebra_send_mpls_labels(zclient, ZEBRA_MPLS_LABELS_DELETE, &zl); +} + +/** + * Send Prefix-SID to ZEBRA for installation or deletion. + * + * @param cmd ZEBRA_MPLS_LABELS_REPLACE or ZEBRA_ROUTE_DELETE + * @param srp Segment Routing Prefix-SID + */ +void isis_zebra_send_prefix_sid(int cmd, const struct sr_prefix *srp) +{ + + if (cmd != ZEBRA_MPLS_LABELS_REPLACE + && cmd != ZEBRA_MPLS_LABELS_DELETE) { + flog_warn(EC_LIB_DEVELOPMENT, "%s: wrong ZEBRA command", + __func__); return; + } + + sr_debug(" |- %s label %u for prefix %pFX", + cmd == ZEBRA_MPLS_LABELS_REPLACE ? "Update" : "Delete", + srp->input_label, &srp->prefix); - if (CHECK_FLAG(route_info->flag, ISIS_ROUTE_FLAG_ACTIVE)) - isis_zebra_route_add_route(prefix, src_p, route_info); + if (cmd == ZEBRA_MPLS_LABELS_REPLACE) + isis_zebra_prefix_install_prefix_sid(srp); else - isis_zebra_route_del_route(prefix, src_p, route_info); + isis_zebra_uninstall_prefix_sid(srp); } -static int isis_zebra_read(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +/** + * Send (LAN)-Adjacency-SID to ZEBRA for installation or deletion. + * + * @param cmd ZEBRA_MPLS_LABELS_ADD or ZEBRA_ROUTE_DELETE + * @param sra Segment Routing Adjacency-SID + */ +void isis_zebra_send_adjacency_sid(int cmd, const struct sr_adjacency *sra) +{ + struct zapi_labels zl; + struct zapi_nexthop *znh; + + if (cmd != ZEBRA_MPLS_LABELS_ADD && cmd != ZEBRA_MPLS_LABELS_DELETE) { + flog_warn(EC_LIB_DEVELOPMENT, "%s: wrong ZEBRA command", + __func__); + return; + } + + sr_debug(" |- %s label %u for interface %s", + cmd == ZEBRA_MPLS_LABELS_ADD ? "Add" : "Delete", + sra->nexthop.label, sra->adj->circuit->interface->name); + + memset(&zl, 0, sizeof(zl)); + zl.type = ZEBRA_LSP_ISIS_SR; + zl.local_label = sra->nexthop.label; + zl.nexthop_num = 1; + znh = &zl.nexthops[0]; + znh->gate = sra->nexthop.address; + znh->type = (sra->nexthop.family == AF_INET) + ? NEXTHOP_TYPE_IPV4_IFINDEX + : NEXTHOP_TYPE_IPV6_IFINDEX; + znh->ifindex = sra->adj->circuit->interface->ifindex; + znh->label_num = 1; + znh->labels[0] = MPLS_LABEL_IMPLICIT_NULL; + + (void)zebra_send_mpls_labels(zclient, cmd, &zl); +} + +static int isis_zebra_read(ZAPI_CALLBACK_ARGS) { struct zapi_route api; + struct isis *isis = NULL; + + isis = isis_lookup_by_vrfid(vrf_id); + + if (isis == NULL) + return -1; if (zapi_route_decode(zclient->ibuf, &api) < 0) return -1; @@ -367,14 +446,15 @@ static int isis_zebra_read(int command, struct zclient *zclient, if (api.prefix.prefixlen == 0 && api.src_prefix.prefixlen == 0 && api.type == PROTO_TYPE) { - command = ZEBRA_REDISTRIBUTE_ROUTE_DEL; + cmd = ZEBRA_REDISTRIBUTE_ROUTE_DEL; } - if (command == ZEBRA_REDISTRIBUTE_ROUTE_ADD) - isis_redist_add(api.type, &api.prefix, &api.src_prefix, + if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD) + isis_redist_add(isis, api.type, &api.prefix, &api.src_prefix, api.distance, api.metric); else - isis_redist_delete(api.type, &api.prefix, &api.src_prefix); + isis_redist_delete(isis, api.type, &api.prefix, + &api.src_prefix); return 0; } @@ -404,32 +484,159 @@ void isis_zebra_redistribute_unset(afi_t afi, int type) type, 0, VRF_DEFAULT); } +/* Label Manager Functions */ + +/** + * Check if Label Manager is Ready or not. + * + * @return True if Label Manager is ready, False otherwise + */ +bool isis_zebra_label_manager_ready(void) +{ + return (zclient_sync->sock > 0); +} + +/** + * Request Label Range to the Label Manager. + * + * @param base base label of the label range to request + * @param chunk_size size of the label range to request + * + * @return 0 on success, -1 on failure + */ +int isis_zebra_request_label_range(uint32_t base, uint32_t chunk_size) +{ + int ret; + uint32_t start, end; + + if (zclient_sync->sock < 0) + return -1; + + ret = lm_get_label_chunk(zclient_sync, 0, base, chunk_size, &start, + &end); + if (ret < 0) { + zlog_warn("%s: error getting label range!", __func__); + return -1; + } + + return 0; +} + +/** + * Release Label Range to the Label Manager. + * + * @param start start of label range to release + * @param end end of label range to release + * + * @return 0 on success, -1 otherwise + */ +int isis_zebra_release_label_range(uint32_t start, uint32_t end) +{ + int ret; + + if (zclient_sync->sock < 0) + return -1; + + ret = lm_release_label_chunk(zclient_sync, start, end); + if (ret < 0) { + zlog_warn("%s: error releasing label range!", __func__); + return -1; + } + + return 0; +} + +/** + * Connect to the Label Manager. + * + * @return 0 on success, -1 otherwise + */ +int isis_zebra_label_manager_connect(void) +{ + /* Connect to label manager. */ + if (zclient_socket_connect(zclient_sync) < 0) { + zlog_warn("%s: failed connecting synchronous zclient!", + __func__); + return -1; + } + /* make socket non-blocking */ + set_nonblocking(zclient_sync->sock); + + /* Send hello to notify zebra this is a synchronous client */ + if (zclient_send_hello(zclient_sync) < 0) { + zlog_warn("%s: failed sending hello for synchronous zclient!", + __func__); + close(zclient_sync->sock); + zclient_sync->sock = -1; + return -1; + } + + /* Connect to label manager */ + if (lm_label_manager_connect(zclient_sync, 0) != 0) { + zlog_warn("%s: failed connecting to label manager!", __func__); + if (zclient_sync->sock > 0) { + close(zclient_sync->sock); + zclient_sync->sock = -1; + } + return -1; + } + + sr_debug("ISIS-Sr: Successfully connected to the Label Manager"); + + return 0; +} + +void isis_zebra_vrf_register(struct isis *isis) +{ + if (!zclient || zclient->sock < 0 || !isis) + return; + + if (isis->vrf_id != VRF_UNKNOWN) { + if (IS_DEBUG_EVENTS) + zlog_debug("%s: Register VRF %s id %u", __func__, + isis->name, isis->vrf_id); + zclient_send_reg_requests(zclient, isis->vrf_id); + } +} + + static void isis_zebra_connected(struct zclient *zclient) { zclient_send_reg_requests(zclient, VRF_DEFAULT); } -void isis_zebra_init(struct thread_master *master) +void isis_zebra_init(struct thread_master *master, int instance) { + /* Initialize asynchronous zclient. */ zclient = zclient_new(master, &zclient_options_default); zclient_init(zclient, PROTO_TYPE, 0, &isisd_privs); zclient->zebra_connected = isis_zebra_connected; zclient->router_id_update = isis_router_id_update_zebra; - zclient->interface_add = isis_zebra_if_add; - zclient->interface_delete = isis_zebra_if_del; - zclient->interface_up = isis_zebra_if_state_up; - zclient->interface_down = isis_zebra_if_state_down; zclient->interface_address_add = isis_zebra_if_address_add; zclient->interface_address_delete = isis_zebra_if_address_del; zclient->interface_link_params = isis_zebra_link_params; zclient->redistribute_route_add = isis_zebra_read; zclient->redistribute_route_del = isis_zebra_read; - return; + /* Initialize special zclient for synchronous message exchanges. */ + struct zclient_options options = zclient_options_default; + options.synchronous = true; + zclient_sync = zclient_new(master, &options); + zclient_sync->sock = -1; + zclient_sync->redist_default = ZEBRA_ROUTE_ISIS; + zclient_sync->instance = instance; + /* + * session_id must be different from default value (0) to distinguish + * the asynchronous socket from the synchronous one + */ + zclient_sync->session_id = 1; + zclient_sync->privs = &isisd_privs; } void isis_zebra_stop(void) { + zclient_stop(zclient_sync); + zclient_free(zclient_sync); zclient_stop(zclient); zclient_free(zclient); frr_fini(); diff --git a/isisd/isis_zebra.h b/isisd/isis_zebra.h index 20c10d0b23..768919ff46 100644 --- a/isisd/isis_zebra.h +++ b/isisd/isis_zebra.h @@ -22,18 +22,41 @@ #ifndef _ZEBRA_ISIS_ZEBRA_H #define _ZEBRA_ISIS_ZEBRA_H +#include "isisd.h" + extern struct zclient *zclient; -void isis_zebra_init(struct thread_master *); +struct label_chunk { + uint32_t start; + uint32_t end; + uint64_t used_mask; +}; +#define CHUNK_SIZE 64 + +void isis_zebra_init(struct thread_master *master, int instance); void isis_zebra_stop(void); struct isis_route_info; +struct sr_prefix; +struct sr_adjacency; -void isis_zebra_route_update(struct prefix *prefix, - struct prefix_ipv6 *src_p, - struct isis_route_info *route_info); +void isis_zebra_route_add_route(struct isis *isis, + struct prefix *prefix, + struct prefix_ipv6 *src_p, + struct isis_route_info *route_info); +void isis_zebra_route_del_route(struct isis *isis, + struct prefix *prefix, + struct prefix_ipv6 *src_p, + struct isis_route_info *route_info); +void isis_zebra_send_prefix_sid(int cmd, const struct sr_prefix *srp); +void isis_zebra_send_adjacency_sid(int cmd, const struct sr_adjacency *sra); int isis_distribute_list_update(int routetype); void isis_zebra_redistribute_set(afi_t afi, int type); void isis_zebra_redistribute_unset(afi_t afi, int type); +bool isis_zebra_label_manager_ready(void); +int isis_zebra_label_manager_connect(void); +int isis_zebra_request_label_range(uint32_t base, uint32_t chunk_size); +int isis_zebra_release_label_range(uint32_t start, uint32_t end); +void isis_zebra_vrf_register(struct isis *isis); #endif /* _ZEBRA_ISIS_ZEBRA_H */ diff --git a/isisd/isisd.c b/isisd/isisd.c index 07be68d9ae..d7ea3931b7 100644 --- a/isisd/isisd.c +++ b/isisd/isisd.c @@ -35,6 +35,8 @@ #include "prefix.h" #include "table.h" #include "qobj.h" +#include "zclient.h" +#include "vrf.h" #include "spf_backoff.h" #include "lib/northbound_cli.h" @@ -56,63 +58,202 @@ #include "isisd/isis_events.h" #include "isisd/isis_te.h" #include "isisd/isis_mt.h" +#include "isisd/isis_sr.h" #include "isisd/fabricd.h" +#include "isisd/isis_nb.h" + +/* For debug statement. */ +unsigned long debug_adj_pkt; +unsigned long debug_snp_pkt; +unsigned long debug_update_pkt; +unsigned long debug_spf_events; +unsigned long debug_rte_events; +unsigned long debug_events; +unsigned long debug_pkt_dump; +unsigned long debug_lsp_gen; +unsigned long debug_lsp_sched; +unsigned long debug_flooding; +unsigned long debug_bfd; +unsigned long debug_tx_queue; +unsigned long debug_sr; -struct isis *isis = NULL; - -DEFINE_QOBJ_TYPE(isis) DEFINE_QOBJ_TYPE(isis_area) +/* ISIS process wide configuration. */ +static struct isis_master isis_master; + +/* ISIS process wide configuration pointer to export. */ +struct isis_master *im; + /* * Prototypes. */ int isis_area_get(struct vty *, const char *); int area_net_title(struct vty *, const char *); int area_clear_net_title(struct vty *, const char *); -int show_isis_interface_common(struct vty *, const char *ifname, char); -int show_isis_neighbor_common(struct vty *, const char *id, char); -int clear_isis_neighbor_common(struct vty *, const char *id); -int isis_config_write(struct vty *); +int show_isis_interface_common(struct vty *, const char *ifname, char, + const char *vrf_name, bool all_vrf); +int show_isis_neighbor_common(struct vty *, const char *id, char, + const char *vrf_name, bool all_vrf); +int clear_isis_neighbor_common(struct vty *, const char *id, const char *vrf_name, + bool all_vrf); + +static void isis_add(struct isis *isis) +{ + listnode_add(im->isis, isis); +} + +static void isis_delete(struct isis *isis) +{ + listnode_delete(im->isis, isis); +} + +/* Link ISIS instance to VRF. */ +void isis_vrf_link(struct isis *isis, struct vrf *vrf) +{ + isis->vrf_id = vrf->vrf_id; + if (vrf->info != (void *)isis) + vrf->info = (void *)isis; +} + +/* Unlink ISIS instance to VRF. */ +void isis_vrf_unlink(struct isis *isis, struct vrf *vrf) +{ + if (vrf->info == (void *)isis) + vrf->info = NULL; + isis->vrf_id = VRF_UNKNOWN; +} + +struct isis *isis_lookup_by_vrfid(vrf_id_t vrf_id) +{ + struct isis *isis; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) + if (isis->vrf_id == vrf_id) + return isis; + + return NULL; +} + +struct isis *isis_lookup_by_vrfname(const char *vrfname) +{ + struct isis *isis; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) + if (isis->name && vrfname && strcmp(isis->name, vrfname) == 0) + return isis; + + return NULL; +} + +struct isis *isis_lookup_by_sysid(const uint8_t *sysid) +{ + struct isis *isis; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) + if (!memcmp(isis->sysid, sysid, ISIS_SYS_ID_LEN)) + return isis; + + return NULL; +} +void isis_master_init(struct thread_master *master) +{ + memset(&isis_master, 0, sizeof(struct isis_master)); + im = &isis_master; + im->isis = list_new(); + im->master = master; +} + +void isis_global_instance_create(const char *vrf_name) +{ + struct isis *isis; + + isis = isis_lookup_by_vrfname(vrf_name); + if (isis == NULL) { + isis = isis_new(vrf_name); + isis_add(isis); + } +} -void isis_new(unsigned long process_id) +struct isis *isis_new(const char *vrf_name) { + struct vrf *vrf; + struct isis *isis; + isis = XCALLOC(MTYPE_ISIS, sizeof(struct isis)); + vrf = vrf_lookup_by_name(vrf_name); + + if (vrf) { + isis->vrf_id = vrf->vrf_id; + isis_vrf_link(isis, vrf); + isis->name = XSTRDUP(MTYPE_ISIS, vrf->name); + } else { + isis->vrf_id = VRF_UNKNOWN; + isis->name = XSTRDUP(MTYPE_ISIS, vrf_name); + } + + if (IS_DEBUG_EVENTS) + zlog_debug( + "%s: Create new isis instance with vrf_name %s vrf_id %u", + __func__, isis->name, isis->vrf_id); + /* * Default values */ isis->max_area_addrs = 3; - isis->process_id = process_id; + isis->process_id = getpid(); isis->router_id = 0; isis->area_list = list_new(); isis->init_circ_list = list_new(); isis->uptime = time(NULL); - isis->nexthops = list_new(); - isis->nexthops6 = list_new(); - dyn_cache_init(); - /* - * uncomment the next line for full debugs - */ - /* isis->debugs = 0xFFFF; */ + dyn_cache_init(isis); - QOBJ_REG(isis, isis); + return isis; } -struct isis_area *isis_area_create(const char *area_tag) +struct isis_area *isis_area_create(const char *area_tag, const char *vrf_name) { struct isis_area *area; - + struct isis *isis = NULL; + struct vrf *vrf = NULL; area = XCALLOC(MTYPE_ISIS_AREA, sizeof(struct isis_area)); + if (vrf_name) { + vrf = vrf_lookup_by_name(vrf_name); + if (vrf) { + isis = isis_lookup_by_vrfid(vrf->vrf_id); + if (isis == NULL) { + isis = isis_new(vrf_name); + isis_add(isis); + } + } else { + isis = isis_lookup_by_vrfid(VRF_UNKNOWN); + if (isis == NULL) { + isis = isis_new(vrf_name); + isis_add(isis); + } + } + } else { + isis = isis_lookup_by_vrfid(VRF_DEFAULT); + if (isis == NULL) { + isis = isis_new(VRF_DEFAULT_NAME); + isis_add(isis); + } + } + + listnode_add(isis->area_list, area); + area->isis = isis; + /* * Fabricd runs only as level-2. - * For IS-IS, the first instance is level-1-2 rest are level-1, - * unless otherwise configured + * For IS-IS, the default is level-1-2 */ - if (fabricd) { + if (fabricd) area->is_type = IS_LEVEL_2; - } else if (listcount(isis->area_list) == 0) - area->is_type = IS_LEVEL_1_AND_2; else area->is_type = yang_get_default_enum( "/frr-isisd:isis/instance/is-type"); @@ -128,10 +269,14 @@ struct isis_area *isis_area_create(const char *area_tag) spftree_area_init(area); area->circuit_list = list_new(); + area->adjacency_list = list_new(); area->area_addrs = list_new(); - thread_add_timer(master, lsp_tick, area, 1, &area->t_tick); + if (!CHECK_FLAG(im->options, F_ISIS_UNIT_TEST)) + thread_add_timer(master, lsp_tick, area, 1, &area->t_tick); flags_initialize(&area->flags); + isis_sr_area_init(area); + /* * Default values */ @@ -139,17 +284,17 @@ struct isis_area *isis_area_create(const char *area_tag) enum isis_metric_style default_style; area->max_lsp_lifetime[0] = yang_get_default_uint16( - "/frr-isisd:isis/instance/lsp/maximum-lifetime/level-1"); + "/frr-isisd:isis/instance/lsp/timers/level-1/maximum-lifetime"); area->max_lsp_lifetime[1] = yang_get_default_uint16( - "/frr-isisd:isis/instance/lsp/maximum-lifetime/level-2"); + "/frr-isisd:isis/instance/lsp/timers/level-2/maximum-lifetime"); area->lsp_refresh[0] = yang_get_default_uint16( - "/frr-isisd:isis/instance/lsp/refresh-interval/level-1"); + "/frr-isisd:isis/instance/lsp/timers/level-1/refresh-interval"); area->lsp_refresh[1] = yang_get_default_uint16( - "/frr-isisd:isis/instance/lsp/refresh-interval/level-2"); + "/frr-isisd:isis/instance/lsp/timers/level-2/refresh-interval"); area->lsp_gen_interval[0] = yang_get_default_uint16( - "/frr-isisd:isis/instance/lsp/generation-interval/level-1"); + "/frr-isisd:isis/instance/lsp/timers/level-1/generation-interval"); area->lsp_gen_interval[1] = yang_get_default_uint16( - "/frr-isisd:isis/instance/lsp/generation-interval/level-2"); + "/frr-isisd:isis/instance/lsp/timers/level-2/generation-interval"); area->min_spf_interval[0] = yang_get_default_uint16( "/frr-isisd:isis/instance/spf/minimum-interval/level-1"); area->min_spf_interval[1] = yang_get_default_uint16( @@ -182,8 +327,6 @@ struct isis_area *isis_area_create(const char *area_tag) area_mt_init(area); area->area_tag = strdup(area_tag); - listnode_add(isis->area_list, area); - area->isis = isis; if (fabricd) area->fabricd = fabricd_new(area); @@ -193,16 +336,42 @@ struct isis_area *isis_area_create(const char *area_tag) area->lsp_refresh_arg[1].area = area; area->lsp_refresh_arg[1].level = IS_LEVEL_2; + area->bfd_signalled_down = false; + area->bfd_force_spf_refresh = false; + QOBJ_REG(area, isis_area); return area; } -struct isis_area *isis_area_lookup(const char *area_tag) +struct isis_area *isis_area_lookup_by_vrf(const char *area_tag, + const char *vrf_name) { struct isis_area *area; struct listnode *node; + struct isis *isis = NULL; + + isis = isis_lookup_by_vrfname(vrf_name); + if (isis == NULL) + return NULL; + + for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) + if (strcmp(area->area_tag, area_tag) == 0) + return area; + + return NULL; +} + +struct isis_area *isis_area_lookup(const char *area_tag, vrf_id_t vrf_id) +{ + struct isis_area *area; + struct listnode *node; + struct isis *isis; + + isis = isis_lookup_by_vrfid(vrf_id); + if (isis == NULL) + return NULL; for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) if ((area->area_tag == NULL && area_tag == NULL) @@ -217,16 +386,16 @@ int isis_area_get(struct vty *vty, const char *area_tag) { struct isis_area *area; - area = isis_area_lookup(area_tag); + area = isis_area_lookup(area_tag, VRF_DEFAULT); if (area) { VTY_PUSH_CONTEXT(ROUTER_NODE, area); return CMD_SUCCESS; } - area = isis_area_create(area_tag); + area = isis_area_create(area_tag, VRF_DEFAULT_NAME); - if (isis->debugs & DEBUG_EVENTS) + if (IS_DEBUG_EVENTS) zlog_debug("New IS-IS area instance %s", area->area_tag); VTY_PUSH_CONTEXT(ROUTER_NODE, area); @@ -234,21 +403,12 @@ int isis_area_get(struct vty *vty, const char *area_tag) return CMD_SUCCESS; } -int isis_area_destroy(const char *area_tag) +void isis_area_destroy(struct isis_area *area) { - struct isis_area *area; struct listnode *node, *nnode; struct isis_circuit *circuit; struct area_addr *addr; - area = isis_area_lookup(area_tag); - - if (area == NULL) { - zlog_warn("%s: could not find area with area-tag %s", - __func__, area_tag); - return CMD_ERR_NO_MATCH; - } - QOBJ_UNREG(area); if (fabricd) @@ -267,23 +427,31 @@ int isis_area_destroy(const char *area_tag) } list_delete(&area->circuit_list); } + list_delete(&area->adjacency_list); lsp_db_fini(&area->lspdb[0]); lsp_db_fini(&area->lspdb[1]); /* invalidate and verify to delete all routes from zebra */ - isis_area_invalidate_routes(area, ISIS_LEVEL1 & ISIS_LEVEL2); + isis_area_invalidate_routes(area, area->is_type); isis_area_verify_routes(area); + isis_sr_area_term(area); + spftree_area_del(area); + if (area->spf_timer[0]) + isis_spf_timer_free(THREAD_ARG(area->spf_timer[0])); THREAD_TIMER_OFF(area->spf_timer[0]); + if (area->spf_timer[1]) + isis_spf_timer_free(THREAD_ARG(area->spf_timer[1])); THREAD_TIMER_OFF(area->spf_timer[1]); spf_backoff_free(area->spf_delay_ietf[0]); spf_backoff_free(area->spf_delay_ietf[1]); - isis_redist_area_finish(area); + if (!CHECK_FLAG(im->options, F_ISIS_UNIT_TEST)) + isis_redist_area_finish(area); for (ALL_LIST_ELEMENTS(area->area_addrs, node, nnode, addr)) { list_delete_node(area->area_addrs, node); @@ -297,20 +465,142 @@ int isis_area_destroy(const char *area_tag) thread_cancel_event(master, area); - listnode_delete(isis->area_list, area); + listnode_delete(area->isis->area_list, area); free(area->area_tag); area_mt_finish(area); + if (listcount(area->isis->area_list) == 0) { + memset(area->isis->sysid, 0, ISIS_SYS_ID_LEN); + area->isis->sysid_set = 0; + } + XFREE(MTYPE_ISIS_AREA, area); - if (listcount(isis->area_list) == 0) { - memset(isis->sysid, 0, ISIS_SYS_ID_LEN); - isis->sysid_set = 0; +} + +/* This is hook function for vrf create called as part of vrf_init */ +static int isis_vrf_new(struct vrf *vrf) +{ + if (IS_DEBUG_EVENTS) + zlog_debug("%s: VRF Created: %s(%u)", __func__, vrf->name, + vrf->vrf_id); + + return 0; +} + +/* This is hook function for vrf delete call as part of vrf_init */ +static int isis_vrf_delete(struct vrf *vrf) +{ + if (IS_DEBUG_EVENTS) + zlog_debug("%s: VRF Deletion: %s(%u)", __func__, vrf->name, + vrf->vrf_id); + + return 0; +} + +static int isis_vrf_enable(struct vrf *vrf) +{ + struct isis *isis; + vrf_id_t old_vrf_id; + + if (IS_DEBUG_EVENTS) + zlog_debug("%s: VRF %s id %u enabled", __func__, vrf->name, + vrf->vrf_id); + + isis = isis_lookup_by_vrfname(vrf->name); + if (isis) { + if (isis->name && strmatch(vrf->name, VRF_DEFAULT_NAME)) { + XFREE(MTYPE_ISIS, isis->name); + isis->name = NULL; + } + old_vrf_id = isis->vrf_id; + /* We have instance configured, link to VRF and make it "up". */ + isis_vrf_link(isis, vrf); + if (IS_DEBUG_EVENTS) + zlog_debug( + "%s: isis linked to vrf %s vrf_id %u (old id %u)", + __func__, vrf->name, isis->vrf_id, old_vrf_id); + if (old_vrf_id != isis->vrf_id) { + frr_with_privs (&isisd_privs) { + /* stop zebra redist to us for old vrf */ + zclient_send_dereg_requests(zclient, + old_vrf_id); + /* start zebra redist to us for new vrf */ + isis_zebra_vrf_register(isis); + } + } } - return CMD_SUCCESS; + return 0; +} + +static int isis_vrf_disable(struct vrf *vrf) +{ + struct isis *isis; + vrf_id_t old_vrf_id = VRF_UNKNOWN; + + if (vrf->vrf_id == VRF_DEFAULT) + return 0; + + if (IS_DEBUG_EVENTS) + zlog_debug("%s: VRF %s id %d disabled.", __func__, vrf->name, + vrf->vrf_id); + isis = isis_lookup_by_vrfname(vrf->name); + if (isis) { + old_vrf_id = isis->vrf_id; + + /* We have instance configured, unlink + * from VRF and make it "down". + */ + isis_vrf_unlink(isis, vrf); + if (IS_DEBUG_EVENTS) + zlog_debug("%s: isis old_vrf_id %d unlinked", __func__, + old_vrf_id); + } + + return 0; +} + +void isis_vrf_init(void) +{ + vrf_init(isis_vrf_new, isis_vrf_enable, isis_vrf_disable, + isis_vrf_delete, isis_vrf_enable); +} + +void isis_finish(struct isis *isis) +{ + struct vrf *vrf = NULL; + + isis_delete(isis); + if (isis->name) { + vrf = vrf_lookup_by_name(isis->name); + if (vrf) + isis_vrf_unlink(isis, vrf); + XFREE(MTYPE_ISIS, isis->name); + } else { + vrf = vrf_lookup_by_id(VRF_DEFAULT); + if (vrf) + isis_vrf_unlink(isis, vrf); + } + + isis_redist_free(isis); + list_delete(&isis->area_list); + list_delete(&isis->init_circ_list); + XFREE(MTYPE_ISIS, isis); +} + +void isis_terminate() +{ + struct isis *isis; + struct listnode *node, *nnode; + + if (listcount(im->isis) == 0) + return; + + for (ALL_LIST_ELEMENTS(im->isis, node, nnode, isis)) + isis_finish(isis); } #ifdef FABRICD @@ -351,10 +641,10 @@ int area_net_title(struct vty *vty, const char *net_title) uint8_t buff[255]; /* We check that we are not over the maximal number of addresses */ - if (listcount(area->area_addrs) >= isis->max_area_addrs) { + if (listcount(area->area_addrs) >= area->isis->max_area_addrs) { vty_out(vty, "Maximum of area addresses (%d) already reached \n", - isis->max_area_addrs); + area->isis->max_area_addrs); return CMD_ERR_NOTHING_TODO; } @@ -380,20 +670,21 @@ int area_net_title(struct vty *vty, const char *net_title) return CMD_WARNING_CONFIG_FAILED; } - if (isis->sysid_set == 0) { + if (area->isis->sysid_set == 0) { /* * First area address - get the SystemID for this router */ - memcpy(isis->sysid, GETSYSID(addr), ISIS_SYS_ID_LEN); - isis->sysid_set = 1; - if (isis->debugs & DEBUG_EVENTS) + memcpy(area->isis->sysid, GETSYSID(addr), ISIS_SYS_ID_LEN); + area->isis->sysid_set = 1; + if (IS_DEBUG_EVENTS) zlog_debug("Router has SystemID %s", - sysid_print(isis->sysid)); + sysid_print(area->isis->sysid)); } else { /* * Check that the SystemID portions match */ - if (memcmp(isis->sysid, GETSYSID(addr), ISIS_SYS_ID_LEN)) { + if (memcmp(area->isis->sysid, GETSYSID(addr), + ISIS_SYS_ID_LEN)) { vty_out(vty, "System ID must not change when defining additional area addresses\n"); XFREE(MTYPE_ISIS_AREA_ADDR, addr); @@ -465,9 +756,9 @@ int area_clear_net_title(struct vty *vty, const char *net_title) * Last area address - reset the SystemID for this router */ if (listcount(area->area_addrs) == 0) { - memset(isis->sysid, 0, ISIS_SYS_ID_LEN); - isis->sysid_set = 0; - if (isis->debugs & DEBUG_EVENTS) + memset(area->isis->sysid, 0, ISIS_SYS_ID_LEN); + area->isis->sysid_set = 0; + if (IS_DEBUG_EVENTS) zlog_debug("Router has no SystemID"); } @@ -478,100 +769,144 @@ int area_clear_net_title(struct vty *vty, const char *net_title) * 'show isis interface' command */ -int show_isis_interface_common(struct vty *vty, const char *ifname, char detail) +int show_isis_interface_common(struct vty *vty, const char *ifname, char detail, + const char *vrf_name, bool all_vrf) { - struct listnode *anode, *cnode; + struct listnode *anode, *cnode, *inode; struct isis_area *area; struct isis_circuit *circuit; + struct isis *isis; - if (!isis) { + if (!im) { vty_out(vty, "IS-IS Routing Process not enabled\n"); return CMD_SUCCESS; } + if (vrf_name) { + if (all_vrf) { + for (ALL_LIST_ELEMENTS_RO(im->isis, inode, isis)) { + for (ALL_LIST_ELEMENTS_RO(isis->area_list, + anode, area)) { + vty_out(vty, "Area %s:\n", + area->area_tag); + + if (detail == ISIS_UI_LEVEL_BRIEF) + vty_out(vty, + " Interface CircId State Type Level\n"); + + for (ALL_LIST_ELEMENTS_RO( + area->circuit_list, cnode, + circuit)) + if (!ifname) + isis_circuit_print_vty( + circuit, vty, + detail); + else if (strcmp(circuit->interface->name, ifname) == 0) + isis_circuit_print_vty( + circuit, vty, + detail); + } + } + return CMD_SUCCESS; + } + isis = isis_lookup_by_vrfname(vrf_name); + if (isis != NULL) { + for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode, + area)) { + vty_out(vty, "Area %s:\n", area->area_tag); - for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode, area)) { - vty_out(vty, "Area %s:\n", area->area_tag); - - if (detail == ISIS_UI_LEVEL_BRIEF) - vty_out(vty, - " Interface CircId State Type Level\n"); - - for (ALL_LIST_ELEMENTS_RO(area->circuit_list, cnode, circuit)) - if (!ifname) - isis_circuit_print_vty(circuit, vty, detail); - else if (strcmp(circuit->interface->name, ifname) == 0) - isis_circuit_print_vty(circuit, vty, detail); + if (detail == ISIS_UI_LEVEL_BRIEF) + vty_out(vty, + " Interface CircId State Type Level\n"); + + for (ALL_LIST_ELEMENTS_RO(area->circuit_list, + cnode, circuit)) + if (!ifname) + isis_circuit_print_vty( + circuit, vty, detail); + else if ( + strcmp(circuit->interface->name, + ifname) + == 0) + isis_circuit_print_vty( + circuit, vty, detail); + } + } } return CMD_SUCCESS; } -DEFUN (show_isis_interface, - show_isis_interface_cmd, - "show " PROTO_NAME " interface", - SHOW_STR - PROTO_HELP - "ISIS interface\n") +DEFUN(show_isis_interface, + show_isis_interface_cmd, + "show " PROTO_NAME " [vrf ] interface", + SHOW_STR + PROTO_HELP + VRF_CMD_HELP_STR + "All VRFs\n" + "IS-IS interface\n") { - return show_isis_interface_common(vty, NULL, ISIS_UI_LEVEL_BRIEF); + const char *vrf_name = VRF_DEFAULT_NAME; + bool all_vrf = false; + int idx_vrf = 0; + + ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + return show_isis_interface_common(vty, NULL, ISIS_UI_LEVEL_BRIEF, + vrf_name, all_vrf); } -DEFUN (show_isis_interface_detail, - show_isis_interface_detail_cmd, - "show " PROTO_NAME " interface detail", - SHOW_STR - PROTO_HELP - "ISIS interface\n" - "show detailed information\n") +DEFUN(show_isis_interface_detail, + show_isis_interface_detail_cmd, + "show " PROTO_NAME " [vrf ] interface detail", + SHOW_STR + PROTO_HELP + VRF_CMD_HELP_STR + "All VRFs\n" + "IS-IS interface\n" + "show detailed information\n") { - return show_isis_interface_common(vty, NULL, ISIS_UI_LEVEL_DETAIL); + const char *vrf_name = VRF_DEFAULT_NAME; + bool all_vrf = false; + int idx_vrf = 0; + + ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + return show_isis_interface_common(vty, NULL, ISIS_UI_LEVEL_DETAIL, + vrf_name, all_vrf); } -DEFUN (show_isis_interface_arg, - show_isis_interface_arg_cmd, - "show " PROTO_NAME " interface WORD", - SHOW_STR - PROTO_HELP - "ISIS interface\n" - "ISIS interface name\n") +DEFUN(show_isis_interface_arg, + show_isis_interface_arg_cmd, + "show " PROTO_NAME " [vrf ] interface WORD", + SHOW_STR + PROTO_HELP + VRF_CMD_HELP_STR + "All VRFs\n" + "IS-IS interface\n" + "IS-IS interface name\n") { - int idx_word = 3; - return show_isis_interface_common(vty, argv[idx_word]->arg, - ISIS_UI_LEVEL_DETAIL); -} + int idx_word = 0; + const char *vrf_name = VRF_DEFAULT_NAME; + bool all_vrf = false; + int idx_vrf = 0; -/* - * 'show isis neighbor' command - */ + ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); -int show_isis_neighbor_common(struct vty *vty, const char *id, char detail) + char *ifname = argv_find(argv, argc, "WORD", &idx_word) + ? argv[idx_word]->arg + : NULL; + return show_isis_interface_common(vty, ifname, ISIS_UI_LEVEL_DETAIL, + vrf_name, all_vrf); +} + +static void isis_neighbor_common(struct vty *vty, const char *id, char detail, + struct isis *isis, uint8_t *sysid) { struct listnode *anode, *cnode, *node; struct isis_area *area; struct isis_circuit *circuit; struct list *adjdb; struct isis_adjacency *adj; - struct isis_dynhn *dynhn; - uint8_t sysid[ISIS_SYS_ID_LEN]; int i; - if (!isis) { - vty_out(vty, "IS-IS Routing Process not enabled\n"); - return CMD_SUCCESS; - } - - memset(sysid, 0, ISIS_SYS_ID_LEN); - if (id) { - if (sysid2buff(sysid, id) == 0) { - dynhn = dynhn_find_by_name(id); - if (dynhn == NULL) { - vty_out(vty, "Invalid system id %s\n", id); - return CMD_SUCCESS; - } - memcpy(sysid, dynhn->id, ISIS_SYS_ID_LEN); - } - } - for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode, area)) { vty_out(vty, "Area %s:\n", area->area_tag); @@ -587,9 +922,10 @@ int show_isis_neighbor_common(struct vty *vty, const char *id, char detail) for (ALL_LIST_ELEMENTS_RO( adjdb, node, adj)) if (!id - || !memcmp(adj->sysid, - sysid, - ISIS_SYS_ID_LEN)) + || !memcmp( + adj->sysid, + sysid, + ISIS_SYS_ID_LEN)) isis_adj_print_vty( adj, vty, @@ -607,24 +943,20 @@ int show_isis_neighbor_common(struct vty *vty, const char *id, char detail) } } - return CMD_SUCCESS; } - /* - * 'clear isis neighbor' command + * 'show isis neighbor' command */ -int clear_isis_neighbor_common(struct vty *vty, const char *id) + +int show_isis_neighbor_common(struct vty *vty, const char *id, char detail, + const char *vrf_name, bool all_vrf) { - struct listnode *anode, *cnode, *cnextnode, *node, *nnode; - struct isis_area *area; - struct isis_circuit *circuit; - struct list *adjdb; - struct isis_adjacency *adj; + struct listnode *node; struct isis_dynhn *dynhn; uint8_t sysid[ISIS_SYS_ID_LEN]; - int i; + struct isis *isis; - if (!isis) { + if (!im) { vty_out(vty, "IS-IS Routing Process not enabled\n"); return CMD_SUCCESS; } @@ -641,9 +973,34 @@ int clear_isis_neighbor_common(struct vty *vty, const char *id) } } + if (vrf_name) { + if (all_vrf) { + for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) { + isis_neighbor_common(vty, id, detail, isis, + sysid); + } + return CMD_SUCCESS; + } + isis = isis_lookup_by_vrfname(vrf_name); + if (isis != NULL) + isis_neighbor_common(vty, id, detail, isis, sysid); + } + + return CMD_SUCCESS; +} + +static void isis_neighbor_common_clear(struct vty *vty, const char *id, + uint8_t *sysid, struct isis *isis) +{ + struct listnode *anode, *cnode, *node, *nnode; + struct isis_area *area; + struct isis_circuit *circuit; + struct list *adjdb; + struct isis_adjacency *adj; + int i; + for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode, area)) { - for (ALL_LIST_ELEMENTS(area->circuit_list, cnode, cnextnode, - circuit)) { + for (ALL_LIST_ELEMENTS_RO(area->circuit_list, cnode, circuit)) { if (circuit->circ_type == CIRCUIT_T_BROADCAST) { for (i = 0; i < 2; i++) { adjdb = circuit->u.bc.adjdb[i]; @@ -652,11 +1009,12 @@ int clear_isis_neighbor_common(struct vty *vty, const char *id) adjdb, node, nnode, adj)) if (!id - || !memcmp(adj->sysid, - sysid, - ISIS_SYS_ID_LEN)) + || !memcmp( + adj->sysid, + sysid, + ISIS_SYS_ID_LEN)) isis_adj_state_change( - adj, + &adj, ISIS_ADJ_DOWN, "clear user request"); } @@ -668,69 +1026,153 @@ int clear_isis_neighbor_common(struct vty *vty, const char *id) || !memcmp(adj->sysid, sysid, ISIS_SYS_ID_LEN)) isis_adj_state_change( - adj, ISIS_ADJ_DOWN, + &adj, ISIS_ADJ_DOWN, "clear user request"); } } } - - return CMD_SUCCESS; } - -DEFUN (show_isis_neighbor, - show_isis_neighbor_cmd, - "show " PROTO_NAME " neighbor", - SHOW_STR - PROTO_HELP - "ISIS neighbor adjacencies\n") +/* + * 'clear isis neighbor' command + */ +int clear_isis_neighbor_common(struct vty *vty, const char *id, const char *vrf_name, + bool all_vrf) { - return show_isis_neighbor_common(vty, NULL, ISIS_UI_LEVEL_BRIEF); -} + struct listnode *node; + struct isis_dynhn *dynhn; + uint8_t sysid[ISIS_SYS_ID_LEN]; + struct isis *isis; -DEFUN (show_isis_neighbor_detail, - show_isis_neighbor_detail_cmd, - "show " PROTO_NAME " neighbor detail", - SHOW_STR - PROTO_HELP - "ISIS neighbor adjacencies\n" - "show detailed information\n") -{ - return show_isis_neighbor_common(vty, NULL, ISIS_UI_LEVEL_DETAIL); -} + if (!im) { + vty_out(vty, "IS-IS Routing Process not enabled\n"); + return CMD_SUCCESS; + } -DEFUN (show_isis_neighbor_arg, - show_isis_neighbor_arg_cmd, - "show " PROTO_NAME " neighbor WORD", - SHOW_STR - PROTO_HELP - "ISIS neighbor adjacencies\n" - "System id\n") -{ - int idx_word = 3; - return show_isis_neighbor_common(vty, argv[idx_word]->arg, - ISIS_UI_LEVEL_DETAIL); -} + memset(sysid, 0, ISIS_SYS_ID_LEN); + if (id) { + if (sysid2buff(sysid, id) == 0) { + dynhn = dynhn_find_by_name(id); + if (dynhn == NULL) { + vty_out(vty, "Invalid system id %s\n", id); + return CMD_SUCCESS; + } + memcpy(sysid, dynhn->id, ISIS_SYS_ID_LEN); + } + } + if (vrf_name) { + if (all_vrf) { + for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) + isis_neighbor_common_clear(vty, id, sysid, + isis); + return CMD_SUCCESS; + } + isis = isis_lookup_by_vrfname(vrf_name); + if (isis != NULL) + isis_neighbor_common_clear(vty, id, sysid, isis); + } -DEFUN (clear_isis_neighbor, - clear_isis_neighbor_cmd, - "clear " PROTO_NAME " neighbor", - CLEAR_STR - PROTO_HELP - "ISIS neighbor adjacencies\n") -{ - return clear_isis_neighbor_common(vty, NULL); + return CMD_SUCCESS; } -DEFUN (clear_isis_neighbor_arg, - clear_isis_neighbor_arg_cmd, - "clear " PROTO_NAME " neighbor WORD", - CLEAR_STR - PROTO_HELP - "ISIS neighbor adjacencies\n" - "System id\n") -{ - int idx_word = 3; - return clear_isis_neighbor_common(vty, argv[idx_word]->arg); +DEFUN(show_isis_neighbor, + show_isis_neighbor_cmd, + "show " PROTO_NAME " [vrf ] neighbor", + SHOW_STR + PROTO_HELP + VRF_CMD_HELP_STR + "All vrfs\n" + "IS-IS neighbor adjacencies\n") +{ + const char *vrf_name = VRF_DEFAULT_NAME; + bool all_vrf = false; + int idx_vrf = 0; + + ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + return show_isis_neighbor_common(vty, NULL, ISIS_UI_LEVEL_BRIEF, + vrf_name, all_vrf); +} + +DEFUN(show_isis_neighbor_detail, + show_isis_neighbor_detail_cmd, + "show " PROTO_NAME " [vrf ] neighbor detail", + SHOW_STR + PROTO_HELP + VRF_CMD_HELP_STR + "all vrfs\n" + "IS-IS neighbor adjacencies\n" + "show detailed information\n") +{ + const char *vrf_name = VRF_DEFAULT_NAME; + bool all_vrf = false; + int idx_vrf = 0; + + ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + + return show_isis_neighbor_common(vty, NULL, ISIS_UI_LEVEL_DETAIL, + vrf_name, all_vrf); +} + +DEFUN(show_isis_neighbor_arg, + show_isis_neighbor_arg_cmd, + "show " PROTO_NAME " [vrf ] neighbor WORD", + SHOW_STR + PROTO_HELP + VRF_CMD_HELP_STR + "All vrfs\n" + "IS-IS neighbor adjacencies\n" + "System id\n") +{ + int idx_word = 0; + const char *vrf_name = VRF_DEFAULT_NAME; + bool all_vrf = false; + int idx_vrf = 0; + + ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + char *id = argv_find(argv, argc, "WORD", &idx_word) + ? argv[idx_word]->arg + : NULL; + + return show_isis_neighbor_common(vty, id, ISIS_UI_LEVEL_DETAIL, + vrf_name, all_vrf); +} + +DEFUN(clear_isis_neighbor, + clear_isis_neighbor_cmd, + "clear " PROTO_NAME " [vrf ] neighbor", + CLEAR_STR + PROTO_HELP + VRF_CMD_HELP_STR + "All vrfs\n" + "IS-IS neighbor adjacencies\n") +{ + const char *vrf_name = VRF_DEFAULT_NAME; + bool all_vrf = false; + int idx_vrf = 0; + + ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + return clear_isis_neighbor_common(vty, NULL, vrf_name, all_vrf); +} + +DEFUN(clear_isis_neighbor_arg, + clear_isis_neighbor_arg_cmd, + "clear " PROTO_NAME " [vrf ] neighbor WORD", + CLEAR_STR + PROTO_HELP + VRF_CMD_HELP_STR + "All vrfs\n" + "IS-IS neighbor adjacencies\n" + "System id\n") +{ + int idx_word = 0; + const char *vrf_name = VRF_DEFAULT_NAME; + bool all_vrf = false; + int idx_vrf = 0; + + char *id = argv_find(argv, argc, "WORD", &idx_word) + ? argv[idx_word]->arg + : NULL; + ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + return clear_isis_neighbor_common(vty, id, vrf_name, all_vrf); } /* @@ -738,11 +1180,7 @@ DEFUN (clear_isis_neighbor_arg, */ void print_debug(struct vty *vty, int flags, int onoff) { - char onoffs[4]; - if (onoff) - strcpy(onoffs, "on"); - else - strcpy(onoffs, "off"); + const char *onoffs = onoff ? "on" : "off"; if (flags & DEBUG_ADJ_PACKETS) vty_out(vty, @@ -756,6 +1194,9 @@ void print_debug(struct vty *vty, int flags, int onoff) onoffs); if (flags & DEBUG_SPF_EVENTS) vty_out(vty, "IS-IS SPF events debugging is %s\n", onoffs); + if (flags & DEBUG_SR) + vty_out(vty, "IS-IS Segment Routing events debugging is %s\n", + onoffs); if (flags & DEBUG_UPDATE_PACKETS) vty_out(vty, "IS-IS Update related packet debugging is %s\n", onoffs); @@ -784,65 +1225,97 @@ DEFUN_NOSH (show_debugging, { vty_out(vty, PROTO_NAME " debugging status:\n"); - if (isis->debugs) - print_debug(vty, isis->debugs, 1); - + if (IS_DEBUG_ADJ_PACKETS) + print_debug(vty, DEBUG_ADJ_PACKETS, 1); + if (IS_DEBUG_TX_QUEUE) + print_debug(vty, DEBUG_TX_QUEUE, 1); + if (IS_DEBUG_SNP_PACKETS) + print_debug(vty, DEBUG_SNP_PACKETS, 1); + if (IS_DEBUG_SPF_EVENTS) + print_debug(vty, DEBUG_SPF_EVENTS, 1); + if (IS_DEBUG_SR) + print_debug(vty, DEBUG_SR, 1); + if (IS_DEBUG_UPDATE_PACKETS) + print_debug(vty, DEBUG_UPDATE_PACKETS, 1); + if (IS_DEBUG_RTE_EVENTS) + print_debug(vty, DEBUG_RTE_EVENTS, 1); + if (IS_DEBUG_EVENTS) + print_debug(vty, DEBUG_EVENTS, 1); + if (IS_DEBUG_PACKET_DUMP) + print_debug(vty, DEBUG_PACKET_DUMP, 1); + if (IS_DEBUG_LSP_GEN) + print_debug(vty, DEBUG_LSP_GEN, 1); + if (IS_DEBUG_LSP_SCHED) + print_debug(vty, DEBUG_LSP_SCHED, 1); + if (IS_DEBUG_FLOODING) + print_debug(vty, DEBUG_FLOODING, 1); + if (IS_DEBUG_BFD) + print_debug(vty, DEBUG_BFD, 1); return CMD_SUCCESS; } +static int config_write_debug(struct vty *vty); /* Debug node. */ -static struct cmd_node debug_node = {DEBUG_NODE, "", 1}; +static struct cmd_node debug_node = { + .name = "debug", + .node = DEBUG_NODE, + .prompt = "", + .config_write = config_write_debug, +}; static int config_write_debug(struct vty *vty) { int write = 0; - int flags = isis->debugs; - if (flags & DEBUG_ADJ_PACKETS) { + if (IS_DEBUG_ADJ_PACKETS) { vty_out(vty, "debug " PROTO_NAME " adj-packets\n"); write++; } - if (flags & DEBUG_TX_QUEUE) { + if (IS_DEBUG_TX_QUEUE) { vty_out(vty, "debug " PROTO_NAME " tx-queue\n"); write++; } - if (flags & DEBUG_SNP_PACKETS) { + if (IS_DEBUG_SNP_PACKETS) { vty_out(vty, "debug " PROTO_NAME " snp-packets\n"); write++; } - if (flags & DEBUG_SPF_EVENTS) { + if (IS_DEBUG_SPF_EVENTS) { vty_out(vty, "debug " PROTO_NAME " spf-events\n"); write++; } - if (flags & DEBUG_UPDATE_PACKETS) { + if (IS_DEBUG_SR) { + vty_out(vty, "debug " PROTO_NAME " sr-events\n"); + write++; + } + if (IS_DEBUG_UPDATE_PACKETS) { vty_out(vty, "debug " PROTO_NAME " update-packets\n"); write++; } - if (flags & DEBUG_RTE_EVENTS) { + if (IS_DEBUG_RTE_EVENTS) { vty_out(vty, "debug " PROTO_NAME " route-events\n"); write++; } - if (flags & DEBUG_EVENTS) { + if (IS_DEBUG_EVENTS) { vty_out(vty, "debug " PROTO_NAME " events\n"); write++; } - if (flags & DEBUG_PACKET_DUMP) { + if (IS_DEBUG_PACKET_DUMP) { vty_out(vty, "debug " PROTO_NAME " packet-dump\n"); write++; } - if (flags & DEBUG_LSP_GEN) { + if (IS_DEBUG_LSP_GEN) { vty_out(vty, "debug " PROTO_NAME " lsp-gen\n"); write++; } - if (flags & DEBUG_LSP_SCHED) { + if (IS_DEBUG_LSP_SCHED) { vty_out(vty, "debug " PROTO_NAME " lsp-sched\n"); write++; } - if (flags & DEBUG_FLOODING) { + if (IS_DEBUG_FLOODING) { vty_out(vty, "debug " PROTO_NAME " flooding\n"); write++; } - if (flags & DEBUG_BFD) { + if (IS_DEBUG_BFD) { vty_out(vty, "debug " PROTO_NAME " bfd\n"); write++; } @@ -858,7 +1331,7 @@ DEFUN (debug_isis_adj, PROTO_HELP "IS-IS Adjacency related packets\n") { - isis->debugs |= DEBUG_ADJ_PACKETS; + debug_adj_pkt |= DEBUG_ADJ_PACKETS; print_debug(vty, DEBUG_ADJ_PACKETS, 1); return CMD_SUCCESS; @@ -872,7 +1345,7 @@ DEFUN (no_debug_isis_adj, PROTO_HELP "IS-IS Adjacency related packets\n") { - isis->debugs &= ~DEBUG_ADJ_PACKETS; + debug_adj_pkt &= ~DEBUG_ADJ_PACKETS; print_debug(vty, DEBUG_ADJ_PACKETS, 0); return CMD_SUCCESS; @@ -885,7 +1358,7 @@ DEFUN (debug_isis_tx_queue, PROTO_HELP "IS-IS TX queues\n") { - isis->debugs |= DEBUG_TX_QUEUE; + debug_tx_queue |= DEBUG_TX_QUEUE; print_debug(vty, DEBUG_TX_QUEUE, 1); return CMD_SUCCESS; @@ -899,7 +1372,7 @@ DEFUN (no_debug_isis_tx_queue, PROTO_HELP "IS-IS TX queues\n") { - isis->debugs &= ~DEBUG_TX_QUEUE; + debug_tx_queue &= ~DEBUG_TX_QUEUE; print_debug(vty, DEBUG_TX_QUEUE, 0); return CMD_SUCCESS; @@ -912,7 +1385,7 @@ DEFUN (debug_isis_flooding, PROTO_HELP "Flooding algorithm\n") { - isis->debugs |= DEBUG_FLOODING; + debug_flooding |= DEBUG_FLOODING; print_debug(vty, DEBUG_FLOODING, 1); return CMD_SUCCESS; @@ -926,7 +1399,7 @@ DEFUN (no_debug_isis_flooding, PROTO_HELP "Flooding algorithm\n") { - isis->debugs &= ~DEBUG_FLOODING; + debug_flooding &= ~DEBUG_FLOODING; print_debug(vty, DEBUG_FLOODING, 0); return CMD_SUCCESS; @@ -939,7 +1412,7 @@ DEFUN (debug_isis_snp, PROTO_HELP "IS-IS CSNP/PSNP packets\n") { - isis->debugs |= DEBUG_SNP_PACKETS; + debug_snp_pkt |= DEBUG_SNP_PACKETS; print_debug(vty, DEBUG_SNP_PACKETS, 1); return CMD_SUCCESS; @@ -953,7 +1426,7 @@ DEFUN (no_debug_isis_snp, PROTO_HELP "IS-IS CSNP/PSNP packets\n") { - isis->debugs &= ~DEBUG_SNP_PACKETS; + debug_snp_pkt &= ~DEBUG_SNP_PACKETS; print_debug(vty, DEBUG_SNP_PACKETS, 0); return CMD_SUCCESS; @@ -966,7 +1439,7 @@ DEFUN (debug_isis_upd, PROTO_HELP "IS-IS Update related packets\n") { - isis->debugs |= DEBUG_UPDATE_PACKETS; + debug_update_pkt |= DEBUG_UPDATE_PACKETS; print_debug(vty, DEBUG_UPDATE_PACKETS, 1); return CMD_SUCCESS; @@ -980,7 +1453,7 @@ DEFUN (no_debug_isis_upd, PROTO_HELP "IS-IS Update related packets\n") { - isis->debugs &= ~DEBUG_UPDATE_PACKETS; + debug_update_pkt &= ~DEBUG_UPDATE_PACKETS; print_debug(vty, DEBUG_UPDATE_PACKETS, 0); return CMD_SUCCESS; @@ -993,7 +1466,7 @@ DEFUN (debug_isis_spfevents, PROTO_HELP "IS-IS Shortest Path First Events\n") { - isis->debugs |= DEBUG_SPF_EVENTS; + debug_spf_events |= DEBUG_SPF_EVENTS; print_debug(vty, DEBUG_SPF_EVENTS, 1); return CMD_SUCCESS; @@ -1007,12 +1480,39 @@ DEFUN (no_debug_isis_spfevents, PROTO_HELP "IS-IS Shortest Path First Events\n") { - isis->debugs &= ~DEBUG_SPF_EVENTS; + debug_spf_events &= ~DEBUG_SPF_EVENTS; print_debug(vty, DEBUG_SPF_EVENTS, 0); return CMD_SUCCESS; } +DEFUN (debug_isis_srevents, + debug_isis_srevents_cmd, + "debug " PROTO_NAME " sr-events", + DEBUG_STR + PROTO_HELP + "IS-IS Segment Routing Events\n") +{ + debug_sr |= DEBUG_SR; + print_debug(vty, DEBUG_SR, 1); + + return CMD_SUCCESS; +} + +DEFUN (no_debug_isis_srevents, + no_debug_isis_srevents_cmd, + "no debug " PROTO_NAME " sr-events", + NO_STR + UNDEBUG_STR + PROTO_HELP + "IS-IS Segment Routing Events\n") +{ + debug_sr &= ~DEBUG_SR; + print_debug(vty, DEBUG_SR, 0); + + return CMD_SUCCESS; +} + DEFUN (debug_isis_rtevents, debug_isis_rtevents_cmd, "debug " PROTO_NAME " route-events", @@ -1020,7 +1520,7 @@ DEFUN (debug_isis_rtevents, PROTO_HELP "IS-IS Route related events\n") { - isis->debugs |= DEBUG_RTE_EVENTS; + debug_rte_events |= DEBUG_RTE_EVENTS; print_debug(vty, DEBUG_RTE_EVENTS, 1); return CMD_SUCCESS; @@ -1034,7 +1534,7 @@ DEFUN (no_debug_isis_rtevents, PROTO_HELP "IS-IS Route related events\n") { - isis->debugs &= ~DEBUG_RTE_EVENTS; + debug_rte_events &= ~DEBUG_RTE_EVENTS; print_debug(vty, DEBUG_RTE_EVENTS, 0); return CMD_SUCCESS; @@ -1047,7 +1547,7 @@ DEFUN (debug_isis_events, PROTO_HELP "IS-IS Events\n") { - isis->debugs |= DEBUG_EVENTS; + debug_events |= DEBUG_EVENTS; print_debug(vty, DEBUG_EVENTS, 1); return CMD_SUCCESS; @@ -1061,7 +1561,7 @@ DEFUN (no_debug_isis_events, PROTO_HELP "IS-IS Events\n") { - isis->debugs &= ~DEBUG_EVENTS; + debug_events &= ~DEBUG_EVENTS; print_debug(vty, DEBUG_EVENTS, 0); return CMD_SUCCESS; @@ -1074,7 +1574,7 @@ DEFUN (debug_isis_packet_dump, PROTO_HELP "IS-IS packet dump\n") { - isis->debugs |= DEBUG_PACKET_DUMP; + debug_pkt_dump |= DEBUG_PACKET_DUMP; print_debug(vty, DEBUG_PACKET_DUMP, 1); return CMD_SUCCESS; @@ -1088,7 +1588,7 @@ DEFUN (no_debug_isis_packet_dump, PROTO_HELP "IS-IS packet dump\n") { - isis->debugs &= ~DEBUG_PACKET_DUMP; + debug_pkt_dump &= ~DEBUG_PACKET_DUMP; print_debug(vty, DEBUG_PACKET_DUMP, 0); return CMD_SUCCESS; @@ -1101,7 +1601,7 @@ DEFUN (debug_isis_lsp_gen, PROTO_HELP "IS-IS generation of own LSPs\n") { - isis->debugs |= DEBUG_LSP_GEN; + debug_lsp_gen |= DEBUG_LSP_GEN; print_debug(vty, DEBUG_LSP_GEN, 1); return CMD_SUCCESS; @@ -1115,7 +1615,7 @@ DEFUN (no_debug_isis_lsp_gen, PROTO_HELP "IS-IS generation of own LSPs\n") { - isis->debugs &= ~DEBUG_LSP_GEN; + debug_lsp_gen &= ~DEBUG_LSP_GEN; print_debug(vty, DEBUG_LSP_GEN, 0); return CMD_SUCCESS; @@ -1128,7 +1628,7 @@ DEFUN (debug_isis_lsp_sched, PROTO_HELP "IS-IS scheduling of LSP generation\n") { - isis->debugs |= DEBUG_LSP_SCHED; + debug_lsp_sched |= DEBUG_LSP_SCHED; print_debug(vty, DEBUG_LSP_SCHED, 1); return CMD_SUCCESS; @@ -1142,7 +1642,7 @@ DEFUN (no_debug_isis_lsp_sched, PROTO_HELP "IS-IS scheduling of LSP generation\n") { - isis->debugs &= ~DEBUG_LSP_SCHED; + debug_lsp_sched &= ~DEBUG_LSP_SCHED; print_debug(vty, DEBUG_LSP_SCHED, 0); return CMD_SUCCESS; @@ -1155,7 +1655,7 @@ DEFUN (debug_isis_bfd, PROTO_HELP PROTO_NAME " interaction with BFD\n") { - isis->debugs |= DEBUG_BFD; + debug_bfd |= DEBUG_BFD; print_debug(vty, DEBUG_BFD, 1); return CMD_SUCCESS; @@ -1169,40 +1669,47 @@ DEFUN (no_debug_isis_bfd, PROTO_HELP PROTO_NAME " interaction with BFD\n") { - isis->debugs &= ~DEBUG_BFD; + debug_bfd &= ~DEBUG_BFD; print_debug(vty, DEBUG_BFD, 0); return CMD_SUCCESS; } -DEFUN (show_hostname, - show_hostname_cmd, - "show " PROTO_NAME " hostname", - SHOW_STR - PROTO_HELP - "IS-IS Dynamic hostname mapping\n") +DEFUN(show_hostname, show_hostname_cmd, + "show " PROTO_NAME " [vrf ] hostname", + SHOW_STR PROTO_HELP VRF_CMD_HELP_STR + "All VRFs\n" + "IS-IS Dynamic hostname mapping\n") { - dynhn_print_all(vty); + struct listnode *node; + const char *vrf_name = VRF_DEFAULT_NAME; + bool all_vrf = false; + int idx_vrf = 0; + struct isis *isis; + + ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + if (vrf_name) { + if (all_vrf) { + for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) + dynhn_print_all(vty, isis); + + return CMD_SUCCESS; + } + isis = isis_lookup_by_vrfname(vrf_name); + if (isis != NULL) + dynhn_print_all(vty, isis); + } return CMD_SUCCESS; } -DEFUN (show_isis_spf_ietf, - show_isis_spf_ietf_cmd, - "show " PROTO_NAME " spf-delay-ietf", - SHOW_STR - PROTO_HELP - "SPF delay IETF information\n") +static void isis_spf_ietf_common(struct vty *vty, struct isis *isis) { - if (!isis) { - vty_out(vty, "ISIS is not running\n"); - return CMD_SUCCESS; - } - struct listnode *node; struct isis_area *area; - for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) { + + vty_out(vty, "vrf : %s\n", isis->name); vty_out(vty, "Area %s:\n", area->area_tag ? area->area_tag : "null"); @@ -1233,23 +1740,49 @@ DEFUN (show_isis_spf_ietf, } } } +} + +DEFUN(show_isis_spf_ietf, show_isis_spf_ietf_cmd, + "show " PROTO_NAME " [vrf ] spf-delay-ietf", + SHOW_STR PROTO_HELP VRF_CMD_HELP_STR + "All VRFs\n" + "SPF delay IETF information\n") +{ + struct listnode *node; + struct isis *isis; + int idx_vrf = 0; + const char *vrf_name = VRF_DEFAULT_NAME; + bool all_vrf = false; + + ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf) + + if (!im) { + vty_out(vty, "ISIS is not running\n"); + return CMD_SUCCESS; + } + + if (vrf_name) { + if (all_vrf) { + for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) + isis_spf_ietf_common(vty, isis); + + return CMD_SUCCESS; + } + isis = isis_lookup_by_vrfname(vrf_name); + if (isis != NULL) + isis_spf_ietf_common(vty, isis); + } + return CMD_SUCCESS; } -DEFUN (show_isis_summary, - show_isis_summary_cmd, - "show " PROTO_NAME " summary", - SHOW_STR PROTO_HELP "summary\n") +static void common_isis_summary(struct vty *vty, struct isis *isis) { struct listnode *node, *node2; struct isis_area *area; int level; - if (isis == NULL) { - vty_out(vty, PROTO_NAME " is not running\n"); - return CMD_SUCCESS; - } - + vty_out(vty, "vrf : %s\n", isis->name); vty_out(vty, "Process Id : %ld\n", isis->process_id); if (isis->sysid_set) vty_out(vty, "System Id : %s\n", @@ -1271,7 +1804,7 @@ DEFUN (show_isis_summary, if (tier == ISIS_TIER_UNDEFINED) vty_out(vty, " Tier: undefined\n"); else - vty_out(vty, " Tier: %" PRIu8 "\n", tier); + vty_out(vty, " Tier: %hhu\n", tier); } if (listcount(area->area_addrs) > 0) { @@ -1317,25 +1850,68 @@ DEFUN (show_isis_summary, " (not used, IETF SPF delay activated)"); vty_out(vty, "\n"); - vty_out(vty, " IPv4 route computation:\n"); - isis_spf_print(area->spftree[SPFTREE_IPV4][level - 1], - vty); + if (area->ip_circuits) { + vty_out(vty, " IPv4 route computation:\n"); + isis_spf_print( + area->spftree[SPFTREE_IPV4][level - 1], + vty); + } - vty_out(vty, " IPv6 route computation:\n"); - isis_spf_print(area->spftree[SPFTREE_IPV6][level - 1], - vty); + if (area->ipv6_circuits) { + vty_out(vty, " IPv6 route computation:\n"); + isis_spf_print( + area->spftree[SPFTREE_IPV6][level - 1], + vty); + } - vty_out(vty, " IPv6 dst-src route computation:\n"); - isis_spf_print(area->spftree[SPFTREE_DSTSRC][level-1], - vty); + if (area->ipv6_circuits + && isis_area_ipv6_dstsrc_enabled(area)) { + vty_out(vty, + " IPv6 dst-src route computation:\n"); + isis_spf_print(area->spftree[SPFTREE_DSTSRC] + [level - 1], + vty); + } } } +} + +DEFUN(show_isis_summary, show_isis_summary_cmd, + "show " PROTO_NAME " [vrf ] summary", + SHOW_STR PROTO_HELP VRF_CMD_HELP_STR + "All VRFs\n" + "summary\n") +{ + struct listnode *node; + int idx_vrf = 0; + struct isis *isis; + const char *vrf_name = VRF_DEFAULT_NAME; + bool all_vrf = false; + + ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf) + if (!im) { + vty_out(vty, PROTO_NAME " is not running\n"); + return CMD_SUCCESS; + } + if (vrf_name) { + if (all_vrf) { + for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) + common_isis_summary(vty, isis); + + return CMD_SUCCESS; + } + isis = isis_lookup_by_vrfname(vrf_name); + if (isis != NULL) + common_isis_summary(vty, isis); + } + vty_out(vty, "\n"); return CMD_SUCCESS; } -struct isis_lsp *lsp_for_arg(struct lspdb_head *head, const char *argv) +struct isis_lsp *lsp_for_arg(struct lspdb_head *head, const char *argv, + struct isis *isis) { char sysid[255] = {0}; uint8_t number[3]; @@ -1395,6 +1971,62 @@ struct isis_lsp *lsp_for_arg(struct lspdb_head *head, const char *argv) return lsp; } +void show_isis_database_lspdb(struct vty *vty, struct isis_area *area, + int level, struct lspdb_head *lspdb, + const char *argv, int ui_level) +{ + struct isis_lsp *lsp; + int lsp_count; + + if (lspdb_count(lspdb) > 0) { + lsp = lsp_for_arg(lspdb, argv, area->isis); + + if (lsp != NULL || argv == NULL) { + vty_out(vty, "IS-IS Level-%d link-state database:\n", + level + 1); + + /* print the title in all cases */ + vty_out(vty, + "LSP ID PduLen SeqNumber Chksum Holdtime ATT/P/OL\n"); + } + + if (lsp) { + if (ui_level == ISIS_UI_LEVEL_DETAIL) + lsp_print_detail(lsp, vty, area->dynhostname, + area->isis); + else + lsp_print(lsp, vty, area->dynhostname, + area->isis); + } else if (argv == NULL) { + lsp_count = + lsp_print_all(vty, lspdb, ui_level, + area->dynhostname, area->isis); + + vty_out(vty, " %u LSPs\n\n", lsp_count); + } + } +} + +static void show_isis_database_common(struct vty *vty, const char *argv, + int ui_level, struct isis *isis) +{ + struct listnode *node; + struct isis_area *area; + int level; + + if (isis->area_list->count == 0) + return; + + for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) { + vty_out(vty, "Area %s:\n", + area->area_tag ? area->area_tag : "null"); + + for (level = 0; level < ISIS_LEVELS; level++) + show_isis_database_lspdb(vty, area, level, + &area->lspdb[level], argv, + ui_level); + } +} /* * This function supports following display options: * [ show isis database [detail] ] @@ -1411,72 +2043,46 @@ struct isis_lsp *lsp_for_arg(struct lspdb_head *head, const char *argv) * [ show isis database detail .- ] * [ show isis database detail .- ] */ -static int show_isis_database(struct vty *vty, const char *argv, int ui_level) +static int show_isis_database(struct vty *vty, const char *argv, int ui_level, + const char *vrf_name, bool all_vrf) { struct listnode *node; - struct isis_area *area; - struct isis_lsp *lsp; - int level, lsp_count; - - if (isis->area_list->count == 0) - return CMD_SUCCESS; + struct isis *isis; - for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) { - vty_out(vty, "Area %s:\n", - area->area_tag ? area->area_tag : "null"); - - for (level = 0; level < ISIS_LEVELS; level++) { - if (lspdb_count(&area->lspdb[level]) > 0) { - lsp = lsp_for_arg(&area->lspdb[level], argv); - - if (lsp != NULL || argv == NULL) { - vty_out(vty, - "IS-IS Level-%d link-state database:\n", - level + 1); + if (vrf_name) { + if (all_vrf) { + for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) + show_isis_database_common(vty, argv, ui_level, + isis); - /* print the title in all cases */ - vty_out(vty, - "LSP ID PduLen SeqNumber Chksum Holdtime ATT/P/OL\n"); - } - - if (lsp) { - if (ui_level == ISIS_UI_LEVEL_DETAIL) - lsp_print_detail( - lsp, vty, - area->dynhostname); - else - lsp_print(lsp, vty, - area->dynhostname); - } else if (argv == NULL) { - lsp_count = lsp_print_all( - vty, &area->lspdb[level], - ui_level, area->dynhostname); - - vty_out(vty, " %u LSPs\n\n", - lsp_count); - } - } + return CMD_SUCCESS; } + isis = isis_lookup_by_vrfname(vrf_name); + if (isis) + show_isis_database_common(vty, argv, ui_level, isis); } return CMD_SUCCESS; } -DEFUN (show_database, - show_database_cmd, - "show " PROTO_NAME " database [detail] [WORD]", - SHOW_STR - PROTO_HELP - "Link state database\n" - "Detailed information\n" - "LSP ID\n") +DEFUN(show_database, show_database_cmd, + "show " PROTO_NAME " [vrf ] database [detail] [WORD]", + SHOW_STR PROTO_HELP VRF_CMD_HELP_STR + "All VRFs\n" + "Link state database\n" + "Detailed information\n" + "LSP ID\n") { int idx = 0; + int idx_vrf = 0; + const char *vrf_name = VRF_DEFAULT_NAME; + bool all_vrf = false; int uilevel = argv_find(argv, argc, "detail", &idx) ? ISIS_UI_LEVEL_DETAIL : ISIS_UI_LEVEL_BRIEF; char *id = argv_find(argv, argc, "WORD", &idx) ? argv[idx]->arg : NULL; - return show_isis_database(vty, id, uilevel); + ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + return show_isis_database(vty, id, uilevel, vrf_name, all_vrf); } #ifdef FABRICD @@ -1505,8 +2111,20 @@ DEFUN (no_router_openfabric, PROTO_HELP "ISO Routing area tag\n") { + struct isis_area *area; + const char *area_tag; int idx_word = 3; - return isis_area_destroy(argv[idx_word]->arg); + + area_tag = argv[idx_word]->arg; + area = isis_area_lookup(area_tag, VRF_DEFAULT); + if (area == NULL) { + zlog_warn("%s: could not find area with area-tag %s", + __func__, area_tag); + return CMD_ERR_NO_MATCH; + } + + isis_area_destroy(area); + return CMD_SUCCESS; } #endif /* ifdef FABRICD */ #ifdef FABRICD @@ -1698,6 +2316,9 @@ static void area_resign_level(struct isis_area *area, int level) } } + if (area->spf_timer[level - 1]) + isis_spf_timer_free(THREAD_ARG(area->spf_timer[level - 1])); + THREAD_TIMER_OFF(area->spf_timer[level - 1]); sched_debug( @@ -1712,7 +2333,7 @@ void isis_area_is_type_set(struct isis_area *area, int is_type) struct listnode *node; struct isis_circuit *circuit; - if (isis->debugs & DEBUG_EVENTS) + if (IS_DEBUG_EVENTS) zlog_debug("ISIS-Evt (%s) system type change %s -> %s", area->area_tag, circuit_t2string(area->is_type), circuit_t2string(is_type)); @@ -1858,14 +2479,19 @@ DEFUN (no_log_adj_changes, #endif /* ifdef FABRICD */ #ifdef FABRICD /* IS-IS configuration write function */ -int isis_config_write(struct vty *vty) +static int isis_config_write(struct vty *vty) { int write = 0; + struct isis_area *area; + struct listnode *node, *node2, *inode; + struct isis *isis; - if (isis != NULL) { - struct isis_area *area; - struct listnode *node, *node2; + if (!im) { + vty_out(vty, "IS-IS Routing Process not enabled\n"); + return CMD_SUCCESS; + } + for (ALL_LIST_ELEMENTS_RO(im->isis, inode, isis)) { for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) { /* ISIS - Area name */ vty_out(vty, "router " PROTO_NAME " %s\n", area->area_tag); @@ -2130,9 +2756,16 @@ int isis_config_write(struct vty *vty) return write; } +struct cmd_node router_node = { + .name = "openfabric", + .node = OPENFABRIC_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-router)# ", + .config_write = isis_config_write, +}; #else /* IS-IS configuration write function */ -int isis_config_write(struct vty *vty) +static int isis_config_write(struct vty *vty) { int write = 0; struct lyd_node *dnode; @@ -2145,14 +2778,20 @@ int isis_config_write(struct vty *vty) return write; } -#endif /* ifdef FABRICD */ -struct cmd_node router_node = {ROUTER_NODE, "%s(config-router)# ", 1}; +struct cmd_node router_node = { + .name = "isis", + .node = ISIS_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-router)# ", + .config_write = isis_config_write, +}; +#endif /* ifdef FABRICD */ void isis_init(void) { /* Install IS-IS top node */ - install_node(&router_node, isis_config_write); + install_node(&router_node); install_element(VIEW_NODE, &show_isis_summary_cmd); @@ -2165,15 +2804,15 @@ void isis_init(void) install_element(VIEW_NODE, &show_isis_neighbor_cmd); install_element(VIEW_NODE, &show_isis_neighbor_detail_cmd); install_element(VIEW_NODE, &show_isis_neighbor_arg_cmd); - install_element(VIEW_NODE, &clear_isis_neighbor_cmd); - install_element(VIEW_NODE, &clear_isis_neighbor_arg_cmd); + install_element(ENABLE_NODE, &clear_isis_neighbor_cmd); + install_element(ENABLE_NODE, &clear_isis_neighbor_arg_cmd); install_element(VIEW_NODE, &show_hostname_cmd); install_element(VIEW_NODE, &show_database_cmd); install_element(ENABLE_NODE, &show_debugging_isis_cmd); - install_node(&debug_node, config_write_debug); + install_node(&debug_node); install_element(ENABLE_NODE, &debug_isis_adj_cmd); install_element(ENABLE_NODE, &no_debug_isis_adj_cmd); @@ -2187,6 +2826,8 @@ void isis_init(void) install_element(ENABLE_NODE, &no_debug_isis_upd_cmd); install_element(ENABLE_NODE, &debug_isis_spfevents_cmd); install_element(ENABLE_NODE, &no_debug_isis_spfevents_cmd); + install_element(ENABLE_NODE, &debug_isis_srevents_cmd); + install_element(ENABLE_NODE, &no_debug_isis_srevents_cmd); install_element(ENABLE_NODE, &debug_isis_rtevents_cmd); install_element(ENABLE_NODE, &no_debug_isis_rtevents_cmd); install_element(ENABLE_NODE, &debug_isis_events_cmd); @@ -2212,6 +2853,8 @@ void isis_init(void) install_element(CONFIG_NODE, &no_debug_isis_upd_cmd); install_element(CONFIG_NODE, &debug_isis_spfevents_cmd); install_element(CONFIG_NODE, &no_debug_isis_spfevents_cmd); + install_element(CONFIG_NODE, &debug_isis_srevents_cmd); + install_element(CONFIG_NODE, &no_debug_isis_srevents_cmd); install_element(CONFIG_NODE, &debug_isis_rtevents_cmd); install_element(CONFIG_NODE, &no_debug_isis_rtevents_cmd); install_element(CONFIG_NODE, &debug_isis_events_cmd); diff --git a/isisd/isisd.h b/isisd/isisd.h index f8486ae0d6..c26a62dfac 100644 --- a/isisd/isisd.h +++ b/isisd/isisd.h @@ -30,6 +30,7 @@ #include "isisd/isis_redist.h" #include "isisd/isis_pdu_counter.h" #include "isisd/isis_circuit.h" +#include "isisd/isis_sr.h" #include "isis_flags.h" #include "isis_lsp.h" #include "isis_memory.h" @@ -54,37 +55,47 @@ static const bool fabricd = false; extern void isis_cli_init(void); #endif +#define ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf) \ + if (argv_find(argv, argc, "vrf", &idx_vrf)) { \ + vrf_name = argv[idx_vrf + 1]->arg; \ + all_vrf = strmatch(vrf_name, "all"); \ + } + extern struct zebra_privs_t isisd_privs; /* uncomment if you are a developer in bug hunt */ /* #define EXTREME_DEBUG */ -/* #define EXTREME_DICT_DEBUG */ struct fabricd; +struct isis_master { + /* ISIS instance. */ + struct list *isis; + /* ISIS thread master. */ + struct thread_master *master; + uint8_t options; +}; +#define F_ISIS_UNIT_TEST 0x01 + struct isis { + vrf_id_t vrf_id; + char *name; unsigned long process_id; int sysid_set; uint8_t sysid[ISIS_SYS_ID_LEN]; /* SystemID for this IS */ uint32_t router_id; /* Router ID from zebra */ struct list *area_list; /* list of IS-IS areas */ struct list *init_circ_list; - struct list *nexthops; /* IPv4 next hops from this IS */ - struct list *nexthops6; /* IPv6 next hops from this IS */ uint8_t max_area_addrs; /* maximumAreaAdresses */ struct area_addr *man_area_addrs; /* manualAreaAddresses */ - uint32_t debugs; /* bitmap for debug */ time_t uptime; /* when did we start */ struct thread *t_dync_clean; /* dynamic hostname cache cleanup thread */ uint32_t circuit_ids_used[8]; /* 256 bits to track circuit ids 1 through 255 */ struct route_table *ext_info[REDIST_PROTOCOL_COUNT]; - - QOBJ_FIELDS }; -extern struct isis *isis; -DECLARE_QOBJ_TYPE(isis_area) +extern struct isis_master *im; enum spf_tree_id { SPFTREE_IPV4 = 0, @@ -112,6 +123,7 @@ struct isis_area { #define DEFAULT_LSP_MTU 1497 unsigned int lsp_mtu; /* Size of LSPs to generate */ struct list *circuit_list; /* IS-IS circuits */ + struct list *adjacency_list; /* IS-IS adjacencies */ struct flags flags; struct thread *t_tick; /* LSP walker */ struct thread *t_lsp_refresh[ISIS_LEVELS]; @@ -129,6 +141,9 @@ struct isis_area { */ int lsp_regenerate_pending[ISIS_LEVELS]; + bool bfd_signalled_down; + bool bfd_force_spf_refresh; + struct fabricd *fabricd; /* @@ -167,6 +182,8 @@ struct isis_area { struct list *mt_settings; /* MPLS-TE settings */ struct mpls_te_area *mta; + /* Segment Routing information */ + struct isis_sr_db srdb; int ipv6_circuits; bool purge_originator; /* Counters */ @@ -190,14 +207,29 @@ struct isis_area { }; DECLARE_QOBJ_TYPE(isis_area) +void isis_terminate(void); +void isis_finish(struct isis *isis); +void isis_master_init(struct thread_master *master); +void isis_vrf_link(struct isis *isis, struct vrf *vrf); +void isis_vrf_unlink(struct isis *isis, struct vrf *vrf); +void isis_global_instance_create(const char *vrf_name); +struct isis *isis_lookup_by_vrfid(vrf_id_t vrf_id); +struct isis *isis_lookup_by_vrfname(const char *vrfname); +struct isis *isis_lookup_by_sysid(const uint8_t *sysid); + void isis_init(void); -void isis_new(unsigned long); -struct isis_area *isis_area_create(const char *); -struct isis_area *isis_area_lookup(const char *); +void isis_vrf_init(void); + +struct isis *isis_new(const char *vrf_name); +struct isis_area *isis_area_create(const char *, const char *); +struct isis_area *isis_area_lookup(const char *, vrf_id_t vrf_id); +struct isis_area *isis_area_lookup_by_vrf(const char *area_tag, + const char *vrf_name); int isis_area_get(struct vty *vty, const char *area_tag); -int isis_area_destroy(const char *area_tag); +void isis_area_destroy(struct isis_area *area); void print_debug(struct vty *, int, int); -struct isis_lsp *lsp_for_arg(struct lspdb_head *head, const char *argv); +struct isis_lsp *lsp_for_arg(struct lspdb_head *head, const char *argv, + struct isis *isis); void isis_area_invalidate_routes(struct isis_area *area, int levels); void isis_area_verify_routes(struct isis_area *area); @@ -219,56 +251,31 @@ int isis_area_passwd_cleartext_set(struct isis_area *area, int level, const char *passwd, uint8_t snp_auth); int isis_area_passwd_hmac_md5_set(struct isis_area *area, int level, const char *passwd, uint8_t snp_auth); +void show_isis_database_lspdb(struct vty *vty, struct isis_area *area, + int level, struct lspdb_head *lspdb, + const char *argv, int ui_level); -extern const struct frr_yang_module_info frr_isisd_info; -extern void isis_northbound_init(void); - -/* YANG northbound notifications */ -extern void isis_notif_db_overload(const struct isis_area *area, bool overload); -extern void isis_notif_lsp_too_large(const struct isis_circuit *circuit, - uint32_t pdu_size, const char *lsp_id); -extern void isis_notif_if_state_change(const struct isis_circuit *circuit, - bool down); -extern void isis_notif_corrupted_lsp(const struct isis_area *area, - const char *lsp_id); /* currently unused */ -extern void isis_notif_lsp_exceed_max(const struct isis_area *area, - const char *lsp_id); -extern void -isis_notif_max_area_addr_mismatch(const struct isis_circuit *circuit, - uint8_t max_area_addrs, const char *raw_pdu); -extern void -isis_notif_authentication_type_failure(const struct isis_circuit *circuit, - const char *raw_pdu); -extern void -isis_notif_authentication_failure(const struct isis_circuit *circuit, - const char *raw_pdu); -extern void isis_notif_adj_state_change(const struct isis_adjacency *adj, - int new_state, const char *reason); -extern void isis_notif_reject_adjacency(const struct isis_circuit *circuit, - const char *reason, - const char *raw_pdu); -extern void isis_notif_area_mismatch(const struct isis_circuit *circuit, - const char *raw_pdu); -extern void isis_notif_lsp_received(const struct isis_circuit *circuit, - const char *lsp_id, uint32_t seqno, - uint32_t timestamp, const char *sys_id); -extern void isis_notif_lsp_gen(const struct isis_area *area, const char *lsp_id, - uint32_t seqno, uint32_t timestamp); -extern void isis_notif_id_len_mismatch(const struct isis_circuit *circuit, - uint8_t rcv_id_len, const char *raw_pdu); -extern void isis_notif_version_skew(const struct isis_circuit *circuit, - uint8_t version, const char *raw_pdu); -extern void isis_notif_lsp_error(const struct isis_circuit *circuit, - const char *lsp_id, const char *raw_pdu, - uint32_t offset, uint8_t tlv_type); -extern void isis_notif_seqno_skipped(const struct isis_circuit *circuit, - const char *lsp_id); -extern void isis_notif_own_lsp_purge(const struct isis_circuit *circuit, - const char *lsp_id); +/* YANG paths */ +#define ISIS_INSTANCE "/frr-isisd:isis/instance" +#define ISIS_SR "/frr-isisd:isis/instance/segment-routing" /* Master of threads. */ extern struct thread_master *master; +extern unsigned long debug_adj_pkt; +extern unsigned long debug_snp_pkt; +extern unsigned long debug_update_pkt; +extern unsigned long debug_spf_events; +extern unsigned long debug_rte_events; +extern unsigned long debug_events; +extern unsigned long debug_pkt_dump; +extern unsigned long debug_lsp_gen; +extern unsigned long debug_lsp_sched; +extern unsigned long debug_flooding; +extern unsigned long debug_bfd; +extern unsigned long debug_tx_queue; +extern unsigned long debug_sr; + #define DEBUG_ADJ_PACKETS (1<<0) #define DEBUG_SNP_PACKETS (1<<1) #define DEBUG_UPDATE_PACKETS (1<<2) @@ -281,21 +288,41 @@ extern struct thread_master *master; #define DEBUG_FLOODING (1<<9) #define DEBUG_BFD (1<<10) #define DEBUG_TX_QUEUE (1<<11) +#define DEBUG_SR (1<<12) + +/* Debug related macro. */ +#define IS_DEBUG_ADJ_PACKETS (debug_adj_pkt & DEBUG_ADJ_PACKETS) +#define IS_DEBUG_SNP_PACKETS (debug_snp_pkt & DEBUG_SNP_PACKETS) +#define IS_DEBUG_UPDATE_PACKETS (debug_update_pkt & DEBUG_UPDATE_PACKETS) +#define IS_DEBUG_SPF_EVENTS (debug_spf_events & DEBUG_SPF_EVENTS) +#define IS_DEBUG_RTE_EVENTS (debug_rte_events & DEBUG_RTE_EVENTS) +#define IS_DEBUG_EVENTS (debug_events & DEBUG_EVENTS) +#define IS_DEBUG_PACKET_DUMP (debug_pkt_dump & DEBUG_PACKET_DUMP) +#define IS_DEBUG_LSP_GEN (debug_lsp_gen & DEBUG_LSP_GEN) +#define IS_DEBUG_LSP_SCHED (debug_lsp_sched & DEBUG_LSP_SCHED) +#define IS_DEBUG_FLOODING (debug_flooding & DEBUG_FLOODING) +#define IS_DEBUG_BFD (debug_bfd & DEBUG_BFD) +#define IS_DEBUG_TX_QUEUE (debug_tx_queue & DEBUG_TX_QUEUE) +#define IS_DEBUG_SR (debug_sr & DEBUG_SR) #define lsp_debug(...) \ do { \ - if (isis->debugs & DEBUG_LSP_GEN) \ + if (IS_DEBUG_LSP_GEN) \ zlog_debug(__VA_ARGS__); \ } while (0) #define sched_debug(...) \ do { \ - if (isis->debugs & DEBUG_LSP_SCHED) \ + if (IS_DEBUG_LSP_SCHED) \ zlog_debug(__VA_ARGS__); \ } while (0) -#define DEBUG_TE DEBUG_LSP_GEN +#define sr_debug(...) \ + do { \ + if (IS_DEBUG_SR) \ + zlog_debug(__VA_ARGS__); \ + } while (0) -#define IS_DEBUG_ISIS(x) (isis->debugs & x) +#define DEBUG_TE DEBUG_LSP_GEN #endif /* ISISD_H */ diff --git a/isisd/subdir.am b/isisd/subdir.am index bae56309cf..9e855ad8cf 100644 --- a/isisd/subdir.am +++ b/isisd/subdir.am @@ -7,15 +7,15 @@ noinst_LIBRARIES += isisd/libisis.a sbin_PROGRAMS += isisd/isisd dist_examples_DATA += isisd/isisd.conf.sample vtysh_scan += \ - $(top_srcdir)/isisd/isis_cli.c \ - $(top_srcdir)/isisd/isis_redist.c \ - $(top_srcdir)/isisd/isis_spf.c \ - $(top_srcdir)/isisd/isis_te.c \ - $(top_srcdir)/isisd/isis_vty_common.c \ - $(top_srcdir)/isisd/isis_vty_fabricd.c \ - $(top_srcdir)/isisd/isisd.c \ + isisd/isis_cli.c \ + isisd/isis_redist.c \ + isisd/isis_spf.c \ + isisd/isis_te.c \ + isisd/isis_sr.c \ + isisd/isis_vty_fabricd.c \ + isisd/isisd.c \ # end -man8 += $(MANBUILD)/isisd.8 +man8 += $(MANBUILD)/frr-isisd.8 endif if FABRICD @@ -40,6 +40,7 @@ noinst_HEADERS += \ isisd/isis_memory.h \ isisd/isis_misc.h \ isisd/isis_mt.h \ + isisd/isis_nb.h \ isisd/isis_network.h \ isisd/isis_pdu.h \ isisd/isis_pdu_counter.h \ @@ -48,15 +49,14 @@ noinst_HEADERS += \ isisd/isis_routemap.h \ isisd/isis_spf.h \ isisd/isis_spf_private.h \ + isisd/isis_sr.h \ isisd/isis_te.h \ isisd/isis_tlvs.h \ isisd/isis_tx_queue.h \ - isisd/isis_vty_common.h \ isisd/isis_zebra.h \ isisd/isisd.h \ isisd/iso_checksum.h \ isisd/fabricd.h \ - isisd/isis_cli.h \ # end LIBISIS_SOURCES = \ @@ -79,10 +79,10 @@ LIBISIS_SOURCES = \ isisd/isis_route.c \ isisd/isis_routemap.c \ isisd/isis_spf.c \ + isisd/isis_sr.c \ isisd/isis_te.c \ isisd/isis_tlvs.c \ isisd/isis_tx_queue.c \ - isisd/isis_vty_common.c \ isisd/isis_zebra.c \ isisd/isisd.c \ isisd/iso_checksum.c \ @@ -102,12 +102,16 @@ ISIS_LDADD_COMMON = lib/libfrr.la $(LIBCAP) isisd_libisis_a_SOURCES = \ $(LIBISIS_SOURCES) \ - isisd/isis_northbound.c \ + isisd/isis_nb.c \ + isisd/isis_nb_config.c \ + isisd/isis_nb_notifications.c \ + isisd/isis_nb_state.c \ isisd/isis_cli.c \ #end -isisd/isis_cli_clippy.c: $(CLIPPY_DEPS) -isisd/isis_cli.$(OBJEXT): isisd/isis_cli_clippy.c +clippy_scan += \ + isisd/isis_cli.c \ + # end isisd_isisd_LDADD = isisd/libisis.a $(ISIS_LDADD_COMMON) isisd_isisd_SOURCES = $(ISIS_SOURCES) diff --git a/ldpd/address.c b/ldpd/address.c index 9c1564a31f..74a3f5a309 100644 --- a/ldpd/address.c +++ b/ldpd/address.c @@ -67,7 +67,7 @@ send_address(struct nbr *nbr, int af, struct if_addr_head *addr_list, fatalx("send_address: unknown af"); } - while ((if_addr = LIST_FIRST(addr_list)) != NULL) { + while (LIST_FIRST(addr_list) != NULL) { /* * Send as many addresses as possible - respect the session's * negotiated maximum pdu length. diff --git a/ldpd/hello.c b/ldpd/hello.c index d17e80008e..ac24704bca 100644 --- a/ldpd/hello.c +++ b/ldpd/hello.c @@ -169,7 +169,7 @@ recv_hello(struct in_addr lsr_id, struct ldp_msg *msg, int af, int tlvs_rcvd; int ds_tlv; union ldpd_addr trans_addr; - uint32_t scope_id = 0; + ifindex_t scope_id = 0; uint32_t conf_seqnum; uint16_t trans_pref; int r; @@ -234,8 +234,7 @@ recv_hello(struct in_addr lsr_id, struct ldp_msg *msg, int af, * check)". */ if (flags & F_HELLO_TARGETED) { - log_debug("%s: lsr-id %s: invalid targeted hello " - "transport address %s", __func__, inet_ntoa(lsr_id), + log_debug("%s: lsr-id %s: invalid targeted hello transport address %s", __func__, inet_ntoa(lsr_id), log_addr(af, &trans_addr)); return; } @@ -250,8 +249,7 @@ recv_hello(struct in_addr lsr_id, struct ldp_msg *msg, int af, * targeted LDP Hello packet's source or destination addresses". */ if (af == AF_INET6 && IN6_IS_SCOPE_EMBED(&src->v6)) { - log_debug("%s: lsr-id %s: targeted hello with " - "link-local source address", __func__, + log_debug("%s: lsr-id %s: targeted hello with link-local source address", __func__, inet_ntoa(lsr_id)); return; } @@ -318,8 +316,7 @@ recv_hello(struct in_addr lsr_id, struct ldp_msg *msg, int af, * send a fatal Notification message with status code of * 'Transport Connection Mismatch' and reset the session". */ - log_debug("%s: lsr-id %s: remote transport preference does not " - "match the local preference", __func__, inet_ntoa(lsr_id)); + log_debug("%s: lsr-id %s: remote transport preference does not match the local preference", __func__, inet_ntoa(lsr_id)); if (nbr) session_shutdown(nbr, S_TRANS_MISMTCH, msg->id, msg->type); @@ -359,8 +356,7 @@ recv_hello(struct in_addr lsr_id, struct ldp_msg *msg, int af, if (nbr && nbr->af == af && (ldp_addrcmp(af, &nbr->raddr, &trans_addr) || nbr->raddr_scope != scope_id)) { - log_warnx("%s: lsr-id %s: hello packet advertising a different " - "transport address", __func__, inet_ntoa(lsr_id)); + log_warnx("%s: lsr-id %s: hello packet advertising a different transport address", __func__, inet_ntoa(lsr_id)); if (adj) adj_del(adj, S_SHUTDOWN); return; @@ -368,8 +364,7 @@ recv_hello(struct in_addr lsr_id, struct ldp_msg *msg, int af, if (nbr == NULL) { nbrt = nbr_find_addr(af, &trans_addr); if (nbrt) { - log_debug("%s: transport address %s is already being " - "used by lsr-id %s", __func__, log_addr(af, + log_debug("%s: transport address %s is already being used by lsr-id %s", __func__, log_addr(af, &trans_addr), inet_ntoa(nbrt->id)); if (adj) adj_del(adj, S_SHUTDOWN); diff --git a/ldpd/init.c b/ldpd/init.c index 8b2abe85e5..30b78315f9 100644 --- a/ldpd/init.c +++ b/ldpd/init.c @@ -146,8 +146,7 @@ recv_init(struct nbr *nbr, char *buf, uint16_t len) nbr->flags |= F_NBR_CAP_DYNAMIC; - log_debug("%s: lsr-id %s announced the Dynamic " - "Capability Announcement capability", __func__, + log_debug("%s: lsr-id %s announced the Dynamic Capability Announcement capability", __func__, inet_ntoa(nbr->id)); break; case TLV_TYPE_TWCARD_CAP: @@ -166,8 +165,7 @@ recv_init(struct nbr *nbr, char *buf, uint16_t len) nbr->flags |= F_NBR_CAP_TWCARD; - log_debug("%s: lsr-id %s announced the Typed Wildcard " - "FEC capability", __func__, inet_ntoa(nbr->id)); + log_debug("%s: lsr-id %s announced the Typed Wildcard FEC capability", __func__, inet_ntoa(nbr->id)); break; case TLV_TYPE_UNOTIF_CAP: if (tlv_len != CAP_TLV_UNOTIF_LEN) { @@ -185,8 +183,7 @@ recv_init(struct nbr *nbr, char *buf, uint16_t len) nbr->flags |= F_NBR_CAP_UNOTIF; - log_debug("%s: lsr-id %s announced the Unrecognized " - "Notification capability", __func__, + log_debug("%s: lsr-id %s announced the Unrecognized Notification capability", __func__, inet_ntoa(nbr->id)); break; default: @@ -321,8 +318,7 @@ recv_capability(struct nbr *nbr, char *buf, uint16_t len) else nbr->flags &= ~F_NBR_CAP_TWCARD; - log_debug("%s: lsr-id %s %s the Typed Wildcard FEC " - "capability", __func__, inet_ntoa(nbr->id), + log_debug("%s: lsr-id %s %s the Typed Wildcard FEC capability", __func__, inet_ntoa(nbr->id), (enable) ? "announced" : "withdrew"); break; case TLV_TYPE_UNOTIF_CAP: @@ -346,8 +342,7 @@ recv_capability(struct nbr *nbr, char *buf, uint16_t len) else nbr->flags &= ~F_NBR_CAP_UNOTIF; - log_debug("%s: lsr-id %s %s the Unrecognized " - "Notification capability", __func__, + log_debug("%s: lsr-id %s %s the Unrecognized Notification capability", __func__, inet_ntoa(nbr->id), (enable) ? "announced" : "withdrew"); break; diff --git a/ldpd/interface.c b/ldpd/interface.c index 8b45703d22..371c7d0bb1 100644 --- a/ldpd/interface.c +++ b/ldpd/interface.c @@ -109,7 +109,7 @@ ldpe_if_exit(struct iface *iface) } struct iface * -if_lookup(struct ldpd_conf *xconf, unsigned short ifindex) +if_lookup(struct ldpd_conf *xconf, ifindex_t ifindex) { struct iface *iface; @@ -528,8 +528,7 @@ if_leave_ipv4_group(struct iface *iface, struct in_addr *addr) if (setsockopt_ipv4_multicast(global.ipv4.ldp_disc_socket, IP_DROP_MEMBERSHIP, if_addr, addr->s_addr, iface->ifindex) < 0) { - log_warn("%s: error IP_DROP_MEMBERSHIP, interface %s " - "address %s", __func__, iface->name, inet_ntoa(*addr)); + log_warn("%s: error IP_DROP_MEMBERSHIP, interface %s address %s", __func__, iface->name, inet_ntoa(*addr)); return (-1); } diff --git a/ldpd/l2vpn.c b/ldpd/l2vpn.c index 7f2e396a7f..2c68f3edbd 100644 --- a/ldpd/l2vpn.c +++ b/ldpd/l2vpn.c @@ -249,7 +249,7 @@ l2vpn_pw_init(struct l2vpn_pw *pw) l2vpn_pw_fec(pw, &fec); lde_kernel_insert(&fec, AF_INET, (union ldpd_addr*)&pw->lsr_id, 0, 0, - 0, (void *)pw); + 0, 0, (void *)pw); lde_kernel_update(&fec); } @@ -260,7 +260,7 @@ l2vpn_pw_exit(struct l2vpn_pw *pw) struct zapi_pw zpw; l2vpn_pw_fec(pw, &fec); - lde_kernel_remove(&fec, AF_INET, (union ldpd_addr*)&pw->lsr_id, 0, 0); + lde_kernel_remove(&fec, AF_INET, (union ldpd_addr*)&pw->lsr_id, 0, 0, 0); lde_kernel_update(&fec); pw2zpw(pw, &zpw); @@ -294,6 +294,16 @@ l2vpn_pw_reset(struct l2vpn_pw *pw) pw->flags |= F_PW_STATUSTLV; else pw->flags &= ~F_PW_STATUSTLV; + + if (pw->flags & F_PW_STATUSTLV_CONF) { + struct fec_node *fn; + struct fec fec; + l2vpn_pw_fec(pw, &fec); + fn = (struct fec_node *)fec_find(&ft, &fec); + if (fn) + pw->remote_status = fn->pw_remote_status; + } + } int @@ -303,6 +313,7 @@ l2vpn_pw_ok(struct l2vpn_pw *pw, struct fec_nh *fnh) if (fnh->remote_label == NO_LABEL) { log_warnx("%s: pseudowire %s: no remote label", __func__, pw->ifname); + pw->reason = F_PW_NO_REMOTE_LABEL; return (0); } @@ -310,6 +321,7 @@ l2vpn_pw_ok(struct l2vpn_pw *pw, struct fec_nh *fnh) if (pw->l2vpn->mtu != pw->remote_mtu) { log_warnx("%s: pseudowire %s: MTU mismatch detected", __func__, pw->ifname); + pw->reason = F_PW_MTU_MISMATCH; return (0); } @@ -318,9 +330,11 @@ l2vpn_pw_ok(struct l2vpn_pw *pw, struct fec_nh *fnh) pw->remote_status != PW_FORWARDING) { log_warnx("%s: pseudowire %s: remote end is down", __func__, pw->ifname); + pw->reason = F_PW_REMOTE_NOT_FWD; return (0); } + pw->reason = F_PW_NO_ERR; return (1); } @@ -429,11 +443,13 @@ l2vpn_recv_pw_status(struct lde_nbr *ln, struct notify_msg *nm) /* unknown fec */ return; + fn->pw_remote_status = nm->pw_status; + pw = (struct l2vpn_pw *) fn->data; if (pw == NULL) return; - fnh = fec_nh_find(fn, AF_INET, (union ldpd_addr *)&ln->id, 0, 0); + fnh = fec_nh_find(fn, AF_INET, (union ldpd_addr *)&ln->id, 0, 0, 0); if (fnh == NULL) return; @@ -482,7 +498,7 @@ l2vpn_recv_pw_status_wcard(struct lde_nbr *ln, struct notify_msg *nm) } fnh = fec_nh_find(fn, AF_INET, (union ldpd_addr *)&ln->id, - 0, 0); + 0, 0, 0); if (fnh == NULL) continue; @@ -517,10 +533,13 @@ l2vpn_pw_status_update(struct zapi_pw_status *zpw) return (1); } - if (zpw->status == PW_STATUS_UP) + if (zpw->status == PW_FORWARDING) { local_status = PW_FORWARDING; - else - local_status = PW_NOT_FORWARDING; + pw->reason = F_PW_NO_ERR; + } else { + local_status = zpw->status; + pw->reason = F_PW_LOCAL_NOT_FWD; + } /* local status didn't change */ if (pw->local_status == local_status) @@ -564,10 +583,11 @@ l2vpn_pw_ctl(pid_t pid) sizeof(pwctl.ifname)); pwctl.pwid = pw->pwid; pwctl.lsr_id = pw->lsr_id; + pwctl.status = PW_NOT_FORWARDING; if (pw->enabled && pw->local_status == PW_FORWARDING && pw->remote_status == PW_FORWARDING) - pwctl.status = 1; + pwctl.status = PW_FORWARDING; lde_imsg_compose_ldpe(IMSG_CTL_SHOW_L2VPN_PW, 0, pid, &pwctl, sizeof(pwctl)); @@ -604,6 +624,7 @@ l2vpn_binding_ctl(pid_t pid) pwctl.local_ifmtu = pw->l2vpn->mtu; pwctl.local_cword = (pw->flags & F_PW_CWORD_CONF) ? 1 : 0; + pwctl.reason = pw->reason; } else pwctl.local_label = NO_LABEL; diff --git a/ldpd/labelmapping.c b/ldpd/labelmapping.c index 5e1b422a41..a656626356 100644 --- a/ldpd/labelmapping.c +++ b/ldpd/labelmapping.c @@ -723,6 +723,14 @@ tlv_decode_fec_elm(struct nbr *nbr, struct ldp_msg *msg, char *buf, /* Prefix Length */ map->fec.prefix.prefixlen = buf[off]; off += sizeof(uint8_t); + if ((map->fec.prefix.af == AF_IPV4 + && map->fec.prefix.prefixlen > IPV4_MAX_PREFIXLEN) + || (map->fec.prefix.af == AF_IPV6 + && map->fec.prefix.prefixlen > IPV6_MAX_PREFIXLEN)) { + session_shutdown(nbr, S_BAD_TLV_VAL, msg->id, + msg->type); + return (-1); + } if (len < off + PREFIX_SIZE(map->fec.prefix.prefixlen)) { session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type); diff --git a/ldpd/lde.c b/ldpd/lde.c index 2aa96546ec..a1f4530ad7 100644 --- a/ldpd/lde.c +++ b/ldpd/lde.c @@ -56,11 +56,15 @@ static void lde_map_free(void *); static int lde_address_add(struct lde_nbr *, struct lde_addr *); static int lde_address_del(struct lde_nbr *, struct lde_addr *); static void lde_address_list_free(struct lde_nbr *); -static void zclient_sync_init(unsigned short instance); +static void zclient_sync_init(void); static void lde_label_list_init(void); static int lde_get_label_chunk(void); static void on_get_label_chunk_response(uint32_t start, uint32_t end); static uint32_t lde_get_next_label(void); +static bool lde_fec_connected(const struct fec_node *); +static bool lde_fec_outside_mpls_network(const struct fec_node *); +static void lde_check_filter_af(int, struct ldpd_af_conf *, + const char *); RB_GENERATE(nbr_tree, lde_nbr, entry, lde_nbr_compare) RB_GENERATE(lde_map_head, lde_map, entry, lde_map_compare) @@ -69,11 +73,9 @@ struct ldpd_conf *ldeconf; struct nbr_tree lde_nbrs = RB_INITIALIZER(&lde_nbrs); static struct imsgev *iev_ldpe; +static struct imsgev iev_main_sync_data; static struct imsgev *iev_main, *iev_main_sync; -/* Master of threads. */ -struct thread_master *master; - /* lde privileges */ static zebra_capabilities_t _caps_p [] = { @@ -147,8 +149,8 @@ lde(void) &iev_main->ev_read); iev_main->handler_write = ldp_write_handler; - if ((iev_main_sync = calloc(1, sizeof(struct imsgev))) == NULL) - fatal(NULL); + memset(&iev_main_sync_data, 0, sizeof(iev_main_sync_data)); + iev_main_sync = &iev_main_sync_data; imsg_init(&iev_main_sync->ibuf, LDPD_FD_SYNC); /* create base configuration */ @@ -174,8 +176,7 @@ lde_init(struct ldpd_init *init) /* Init synchronous zclient and label list */ frr_zclient_addr(&zclient_addr, &zclient_addr_len, init->zclient_serv_path); - zclient_sync_init(init->instance); - lde_label_list_init(); + zclient_sync_init(); } static void @@ -203,9 +204,10 @@ lde_shutdown(void) if (iev_ldpe) free(iev_ldpe); free(iev_main); - free(iev_main_sync); log_info("label decision engine exiting"); + + zlog_fini(); exit(0); } @@ -294,7 +296,7 @@ lde_dispatch_imsg(struct thread *thread) switch (imsg.hdr.type) { case IMSG_LABEL_MAPPING: - lde_check_mapping(map, ln); + lde_check_mapping(map, ln, 1); break; case IMSG_LABEL_REQUEST: lde_check_request(map, ln); @@ -323,8 +325,7 @@ lde_dispatch_imsg(struct thread *thread) break; } if (lde_address_add(ln, lde_addr) < 0) { - log_debug("%s: cannot add address %s, it " - "already exists", __func__, + log_debug("%s: cannot add address %s, it already exists", __func__, log_addr(lde_addr->af, &lde_addr->addr)); } break; @@ -341,8 +342,7 @@ lde_dispatch_imsg(struct thread *thread) break; } if (lde_address_del(ln, lde_addr) < 0) { - log_debug("%s: cannot delete address %s, it " - "does not exist", __func__, + log_debug("%s: cannot delete address %s, it does not exist", __func__, log_addr(lde_addr->af, &lde_addr->addr)); } break; @@ -380,8 +380,7 @@ lde_dispatch_imsg(struct thread *thread) fatalx("lde_dispatch_imsg: wrong imsg len"); if (lde_nbr_find(imsg.hdr.peerid)) - fatalx("lde_dispatch_imsg: " - "neighbor already exists"); + fatalx("lde_dispatch_imsg: neighbor already exists"); lde_nbr_new(imsg.hdr.peerid, imsg.data); break; case IMSG_NEIGHBOR_DOWN: @@ -444,6 +443,7 @@ lde_dispatch_parent(struct thread *thread) ssize_t n; int shut = 0; struct fec fec; + struct ldp_access *laccess; iev->ev_read = NULL; @@ -468,6 +468,10 @@ lde_dispatch_parent(struct thread *thread) iface = if_lookup_name(ldeconf, kif->ifname); if (iface) { if_update_info(iface, kif); + + /* if up see if any labels need to be updated */ + if (kif->operative) + lde_route_update(iface, AF_UNSPEC); break; } @@ -520,7 +524,8 @@ lde_dispatch_parent(struct thread *thread) switch (imsg.hdr.type) { case IMSG_NETWORK_ADD: lde_kernel_insert(&fec, kr->af, &kr->nexthop, - kr->ifindex, kr->priority, + kr->ifindex, kr->route_type, + kr->route_instance, kr->flags & F_CONNECTED, NULL); break; case IMSG_NETWORK_UPDATE: @@ -530,13 +535,11 @@ lde_dispatch_parent(struct thread *thread) break; case IMSG_SOCKET_IPC: if (iev_ldpe) { - log_warnx("%s: received unexpected imsg fd " - "to ldpe", __func__); + log_warnx("%s: received unexpected imsg fd to ldpe", __func__); break; } if ((fd = imsg.fd) == -1) { - log_warnx("%s: expected to receive imsg fd to " - "ldpe but didn't receive any", __func__); + log_warnx("%s: expected to receive imsg fd to ldpe but didn't receive any", __func__); break; } @@ -635,6 +638,18 @@ lde_dispatch_parent(struct thread *thread) } memcpy(&ldp_debug, imsg.data, sizeof(ldp_debug)); break; + case IMSG_FILTER_UPDATE: + if (imsg.hdr.len != IMSG_HEADER_SIZE + + sizeof(struct ldp_access)) { + log_warnx("%s: wrong imsg len", __func__); + break; + } + laccess = imsg.data; + lde_check_filter_af(AF_INET, &ldeconf->ipv4, + laccess->name); + lde_check_filter_af(AF_INET6, &ldeconf->ipv6, + laccess->name); + break; default: log_debug("%s: unexpected imsg %d", __func__, imsg.hdr.type); @@ -660,18 +675,31 @@ lde_acl_check(char *acl_name, int af, union ldpd_addr *addr, uint8_t prefixlen) return ldp_acl_request(iev_main_sync, acl_name, af, addr, prefixlen); } +static bool lde_fec_connected(const struct fec_node *fn) +{ + struct fec_nh *fnh; + + LIST_FOREACH(fnh, &fn->nexthops, entry) + if (fnh->flags & F_FEC_NH_CONNECTED) + return true; + + return false; +} + +static bool lde_fec_outside_mpls_network(const struct fec_node *fn) +{ + struct fec_nh *fnh; + + LIST_FOREACH(fnh, &fn->nexthops, entry) + if (!(fnh->flags & F_FEC_NH_NO_LDP)) + return false; + + return true; +} + uint32_t lde_update_label(struct fec_node *fn) { - struct fec_nh *fnh; - int connected = 0; - - LIST_FOREACH(fnh, &fn->nexthops, entry) { - if (fnh->flags & F_FEC_NH_CONNECTED) { - connected = 1; - break; - } - } /* should we allocate a label for this fec? */ switch (fn->fec.type) { @@ -697,7 +725,14 @@ lde_update_label(struct fec_node *fn) break; } - if (connected) { + /* + * If connected interface act as egress for fec. + * If LDP is not configured on an interface but there + * are other NHs with interfaces configured with LDP + * then don't act as an egress for the fec, otherwise + * act as an egress for the fec + */ + if (lde_fec_connected(fn) || lde_fec_outside_mpls_network(fn)) { /* choose implicit or explicit-null depending on configuration */ switch (fn->fec.type) { case FEC_TYPE_IPV4: @@ -717,7 +752,6 @@ lde_update_label(struct fec_node *fn) return (MPLS_LABEL_IMPLICIT_NULL); return MPLS_LABEL_IPV6_EXPLICIT_NULL; default: - fatalx("lde_update_label: unexpected fec type"); break; } } @@ -737,6 +771,13 @@ lde_send_change_klabel(struct fec_node *fn, struct fec_nh *fnh) struct zapi_pw zpw; struct l2vpn_pw *pw; + /* + * Ordered Control: don't program label into HW until a + * labelmap msg has been received from upstream router + */ + if (fnh->flags & F_FEC_NH_DEFER) + return; + switch (fn->fec.type) { case FEC_TYPE_IPV4: memset(&kr, 0, sizeof(kr)); @@ -747,8 +788,8 @@ lde_send_change_klabel(struct fec_node *fn, struct fec_nh *fnh) kr.ifindex = fnh->ifindex; kr.local_label = fn->local_label; kr.remote_label = fnh->remote_label; - kr.priority = fnh->priority; - + kr.route_type = fnh->route_type; + kr.route_instance = fnh->route_instance; lde_imsg_compose_parent(IMSG_KLABEL_CHANGE, 0, &kr, sizeof(kr)); break; @@ -761,7 +802,8 @@ lde_send_change_klabel(struct fec_node *fn, struct fec_nh *fnh) kr.ifindex = fnh->ifindex; kr.local_label = fn->local_label; kr.remote_label = fnh->remote_label; - kr.priority = fnh->priority; + kr.route_type = fnh->route_type; + kr.route_instance = fnh->route_instance; lde_imsg_compose_parent(IMSG_KLABEL_CHANGE, 0, &kr, sizeof(kr)); @@ -798,7 +840,8 @@ lde_send_delete_klabel(struct fec_node *fn, struct fec_nh *fnh) kr.ifindex = fnh->ifindex; kr.local_label = fn->local_label; kr.remote_label = fnh->remote_label; - kr.priority = fnh->priority; + kr.route_type = fnh->route_type; + kr.route_instance = fnh->route_instance; lde_imsg_compose_parent(IMSG_KLABEL_DELETE, 0, &kr, sizeof(kr)); @@ -812,7 +855,8 @@ lde_send_delete_klabel(struct fec_node *fn, struct fec_nh *fnh) kr.ifindex = fnh->ifindex; kr.local_label = fn->local_label; kr.remote_label = fnh->remote_label; - kr.priority = fnh->priority; + kr.route_type = fnh->route_type; + kr.route_instance = fnh->route_instance; lde_imsg_compose_parent(IMSG_KLABEL_DELETE, 0, &kr, sizeof(kr)); @@ -899,6 +943,27 @@ lde_send_labelmapping(struct lde_nbr *ln, struct fec_node *fn, int single) struct lde_req *lre; struct map map; struct l2vpn_pw *pw; + struct fec_nh *fnh; + bool allow = false; + + /* + * Ordered Control: do not send a labelmap msg until + * a labelmap message is received from downstream router + * and don't send labelmap back to downstream router + */ + if (ldeconf->flags & F_LDPD_ORDERED_CONTROL) { + LIST_FOREACH(fnh, &fn->nexthops, entry) { + if (fnh->flags & F_FEC_NH_DEFER) + continue; + + if (lde_address_find(ln, fnh->af, &fnh->nexthop)) + return; + allow = true; + break; + } + if (!allow) + return; + } /* * We shouldn't send a new label mapping if we have a pending @@ -908,8 +973,7 @@ lde_send_labelmapping(struct lde_nbr *ln, struct fec_node *fn, int single) lw = (struct lde_wdraw *)fec_find(&ln->sent_wdraw, &fn->fec); if (lw) { if (!fec_find(&ln->sent_map_pending, &fn->fec)) { - debug_evt("%s: FEC %s: scheduling to send label " - "mapping later (waiting for pending label release)", + debug_evt("%s: FEC %s: scheduling to send label mapping later (waiting for pending label release)", __func__, log_fec(&fn->fec)); lde_map_pending_add(ln, fn); } @@ -1150,6 +1214,82 @@ lde_send_labelrelease(struct lde_nbr *ln, struct fec_node *fn, lde_imsg_compose_ldpe(IMSG_RELEASE_ADD_END, ln->peerid, 0, NULL, 0); } +void +lde_send_labelrequest(struct lde_nbr *ln, struct fec_node *fn, + struct map *wcard, int single) +{ + struct map map; + struct fec *f; + struct lde_req *lre; + + if (fn) { + lde_fec2map(&fn->fec, &map); + switch (fn->fec.type) { + case FEC_TYPE_IPV4: + if (!ln->v4_enabled) + return; + break; + case FEC_TYPE_IPV6: + if (!ln->v6_enabled) + return; + break; + default: + fatalx("lde_send_labelrequest: unknown af"); + } + } else + memcpy(&map, wcard, sizeof(map)); + + map.label = NO_LABEL; + + if (fn) { + /* SLR1.1: has label request for FEC been previously sent + * and still outstanding just return, + */ + lre = (struct lde_req *)fec_find(&ln->sent_req, &fn->fec); + if (lre == NULL) { + /* SLRq.3: send label request */ + lde_imsg_compose_ldpe(IMSG_REQUEST_ADD, ln->peerid, 0, + &map, sizeof(map)); + if (single) + lde_imsg_compose_ldpe(IMSG_REQUEST_ADD_END, + ln->peerid, 0, NULL, 0); + + /* SLRq.4: record sent request */ + lde_req_add(ln, &fn->fec, 1); + } + } else { + /* if Wilcard just send label request */ + /* SLRq.3: send label request */ + lde_imsg_compose_ldpe(IMSG_REQUEST_ADD, + ln->peerid, 0, &map, sizeof(map)); + if (single) + lde_imsg_compose_ldpe(IMSG_REQUEST_ADD_END, + ln->peerid, 0, NULL, 0); + + /* SLRq.4: record sent request */ + RB_FOREACH(f, fec_tree, &ft) { + fn = (struct fec_node *)f; + lre = (struct lde_req *)fec_find(&ln->sent_req, &fn->fec); + if (lde_wildcard_apply(wcard, &fn->fec, NULL) == 0) + continue; + if (lre == NULL) + lde_req_add(ln, f, 1); + } + } +} + +void +lde_send_labelrequest_wcard(struct lde_nbr *ln, uint16_t af) +{ + struct map wcard; + + memset(&wcard, 0, sizeof(wcard)); + wcard.type = MAP_TYPE_TYPED_WCARD; + wcard.fec.twcard.type = MAP_TYPE_PREFIX; + wcard.fec.twcard.u.prefix_af = af; + lde_send_labelrequest(ln, NULL, &wcard, 1); +} + void lde_send_notification(struct lde_nbr *ln, uint32_t status_code, uint32_t msg_id, uint16_t msg_type) @@ -1239,6 +1379,7 @@ lde_nbr_del(struct lde_nbr *ln) struct fec_node *fn; struct fec_nh *fnh; struct l2vpn_pw *pw; + struct lde_nbr *lnbr; if (ln == NULL) return; @@ -1254,13 +1395,34 @@ lde_nbr_del(struct lde_nbr *ln) if (!lde_address_find(ln, fnh->af, &fnh->nexthop)) continue; + + /* + * Ordered Control: must mark any non-connected + * NH to wait until we receive a labelmap msg + * before installing in kernel and sending to + * peer, must do this as NHs are not removed + * when lsps go down. Also send label withdraw + * to other neighbors for all fecs from neighbor + * going down + */ + if (ldeconf->flags & F_LDPD_ORDERED_CONTROL) { + fnh->flags |= F_FEC_NH_DEFER; + + RB_FOREACH(lnbr, nbr_tree, &lde_nbrs) { + if (ln->peerid == lnbr->peerid) + continue; + lde_send_labelwithdraw(lnbr, fn, NULL, NULL); + } + } break; case FEC_TYPE_PWID: if (f->u.pwid.lsr_id.s_addr != ln->id.s_addr) continue; pw = (struct l2vpn_pw *) fn->data; - if (pw) + if (pw) { + pw->reason = F_PW_NO_REMOTE_LABEL; l2vpn_pw_reset(pw); + } break; default: break; @@ -1565,6 +1727,276 @@ lde_change_egress_label(int af) NULL, 0); } +void +lde_change_allocate_filter(int af) +{ + struct lde_nbr *ln; + struct fec *f; + struct fec_node *fn; + uint32_t new_label; + + /* reallocate labels for fecs that match this filter */ + RB_FOREACH(f, fec_tree, &ft) { + fn = (struct fec_node *)f; + + switch (af) { + case AF_INET: + if (fn->fec.type != FEC_TYPE_IPV4) + continue; + break; + case AF_INET6: + if (fn->fec.type != FEC_TYPE_IPV6) + continue; + break; + default: + fatalx("lde_change_allocate_filter: unknown af"); + } + + /* + * If the local label has changed to NO_LABEL, send a label + * withdraw to all peers. + * If the local label has changed and it's different from + * NO_LABEL, send a label mapping to all peers advertising + * the new label. + * If the local label hasn't changed, do nothing + */ + new_label = lde_update_label(fn); + if (fn->local_label != new_label) { + if (new_label == NO_LABEL) + RB_FOREACH(ln, nbr_tree, &lde_nbrs) + lde_send_labelwithdraw(ln, fn, + NULL, NULL); + + fn->local_label = new_label; + if (fn->local_label != NO_LABEL) + RB_FOREACH(ln, nbr_tree, &lde_nbrs) + lde_send_labelmapping(ln, fn, 0); + } + } + RB_FOREACH(ln, nbr_tree, &lde_nbrs) + lde_imsg_compose_ldpe(IMSG_MAPPING_ADD_END, ln->peerid, 0, + NULL, 0); +} + +void +lde_change_advertise_filter(int af) +{ + struct lde_nbr *ln; + struct fec *f; + struct fec_node *fn; + char *acl_to_filter; + char *acl_for_filter; + union ldpd_addr *prefix; + uint8_t plen; + struct lde_map *me; + + /* advertise label for fecs to neighbors if matches advertise filters */ + switch (af) { + case AF_INET: + acl_to_filter = ldeconf->ipv4.acl_label_advertise_to; + acl_for_filter = ldeconf->ipv4.acl_label_advertise_for; + break; + case AF_INET6: + acl_to_filter = ldeconf->ipv6.acl_label_advertise_to; + acl_for_filter = ldeconf->ipv6.acl_label_advertise_for; + break; + default: + fatalx("lde_change_advertise_filter: unknown af"); + } + + RB_FOREACH(ln, nbr_tree, &lde_nbrs) { + if (lde_acl_check(acl_to_filter, af, (union ldpd_addr *)&ln->id, + IPV4_MAX_BITLEN) != FILTER_PERMIT) + lde_send_labelwithdraw_wcard(ln, NO_LABEL); + else { + /* This neighbor is allowed in to_filter, so + * send labels if fec also matches for_filter + */ + RB_FOREACH(f, fec_tree, &ft) { + fn = (struct fec_node *)f; + switch (af) { + case AF_INET: + if (fn->fec.type != FEC_TYPE_IPV4) + continue; + prefix = (union ldpd_addr *) + &fn->fec.u.ipv4.prefix; + plen = fn->fec.u.ipv4.prefixlen; + break; + case FEC_TYPE_IPV6: + if (fn->fec.type != FEC_TYPE_IPV6) + continue; + prefix = (union ldpd_addr *) + &fn->fec.u.ipv6.prefix; + plen = fn->fec.u.ipv6.prefixlen; + break; + default: + continue; + } + if (lde_acl_check(acl_for_filter, af, + prefix, plen) != FILTER_PERMIT) { + me = (struct lde_map *)fec_find( + &ln->sent_map, &fn->fec); + if (me) + /* fec filtered withdraw */ + lde_send_labelwithdraw(ln, fn, + NULL, NULL); + } else + /* fec allowed send map */ + lde_send_labelmapping(ln, fn, 0); + } + lde_imsg_compose_ldpe(IMSG_MAPPING_ADD_END, + ln->peerid, 0, NULL, 0); + } + } +} + + +void +lde_change_accept_filter(int af) +{ + struct lde_nbr *ln; + struct fec *f; + struct fec_node *fn; + char *acl_for_filter; + char *acl_from_filter; + union ldpd_addr *prefix; + uint8_t plen; + struct lde_map *me; + enum fec_type type; + + /* accept labels from neighbors specified in the from_filter and for + * fecs defined in the for_filter + */ + switch (af) { + case AF_INET: + acl_for_filter = ldeconf->ipv4.acl_label_accept_for; + acl_from_filter = ldeconf->ipv4.acl_label_accept_from; + type = FEC_TYPE_IPV4; + break; + case AF_INET6: + acl_for_filter = ldeconf->ipv6.acl_label_accept_for; + acl_from_filter = ldeconf->ipv6.acl_label_accept_from; + type = FEC_TYPE_IPV6; + break; + default: + fatalx("lde_change_accept_filter: unknown af"); + } + + RB_FOREACH(ln, nbr_tree, &lde_nbrs) { + if (lde_acl_check(acl_from_filter, AF_INET, (union ldpd_addr *) + &ln->id, IPV4_MAX_BITLEN) != FILTER_PERMIT) { + /* This neighbor is now filtered so remove fecs from + * recv list + */ + RB_FOREACH(f, fec_tree, &ft) { + fn = (struct fec_node *)f; + if (fn->fec.type == type) { + me = (struct lde_map *)fec_find( + &ln->recv_map, &fn->fec); + if (me) + lde_map_del(ln, me, 0); + } + } + } else if (ln->flags & F_NBR_CAP_TWCARD) { + /* This neighbor is allowed and supports type + * wildcard so send a labelrequest + * to get any new labels from neighbor + * and make sure any fecs we currently have + * match for_filter. + */ + RB_FOREACH(f, fec_tree, &ft) { + fn = (struct fec_node *)f; + switch (af) { + case AF_INET: + if (fn->fec.type != FEC_TYPE_IPV4) + continue; + prefix = (union ldpd_addr *) + &fn->fec.u.ipv4.prefix; + plen = fn->fec.u.ipv4.prefixlen; + break; + case AF_INET6: + if (fn->fec.type != FEC_TYPE_IPV6) + continue; + prefix = (union ldpd_addr *) + &fn->fec.u.ipv6.prefix; + plen = fn->fec.u.ipv6.prefixlen; + break; + default: + continue; + } + if (lde_acl_check(acl_for_filter, af, + prefix, plen) != FILTER_PERMIT) { + me = (struct lde_map *)fec_find( + &ln->recv_map, &fn->fec); + if (me) + lde_map_del(ln, me, 0); + } + } + lde_send_labelrequest_wcard(ln, af); + } else + /* Type Wildcard is not supported so restart session */ + lde_imsg_compose_ldpe(IMSG_NBR_SHUTDOWN, ln->peerid, 0, + NULL, 0); + } +} + +void +lde_change_expnull_for_filter(int af) +{ + struct lde_nbr *ln; + struct fec *f; + struct fec_node *fn; + char *acl_name; + uint32_t exp_label; + union ldpd_addr *prefix; + uint8_t plen; + + /* Configure explicit-null advertisement for all fecs in this filter */ + RB_FOREACH(f, fec_tree, &ft) { + fn = (struct fec_node *)f; + + switch (af) { + case AF_INET: + if (fn->fec.type != FEC_TYPE_IPV4) + continue; + acl_name = ldeconf->ipv4.acl_label_expnull_for; + prefix = (union ldpd_addr *)&fn->fec.u.ipv4.prefix; + plen = fn->fec.u.ipv4.prefixlen; + exp_label = MPLS_LABEL_IPV4_EXPLICIT_NULL; + break; + case AF_INET6: + if (fn->fec.type != FEC_TYPE_IPV6) + continue; + acl_name = ldeconf->ipv6.acl_label_expnull_for; + prefix = (union ldpd_addr *)&fn->fec.u.ipv6.prefix; + plen = fn->fec.u.ipv6.prefixlen; + exp_label = MPLS_LABEL_IPV6_EXPLICIT_NULL; + break; + default: + fatalx("lde_change_expnull_for_filter: unknown af"); + } + + if (lde_acl_check(acl_name, af, prefix, plen) == FILTER_PERMIT) { + /* for this fec change any imp-null to exp-null */ + if (fn->local_label == MPLS_LABEL_IMPLICIT_NULL) { + fn->local_label= lde_update_label(fn); + RB_FOREACH(ln, nbr_tree, &lde_nbrs) + lde_send_labelmapping(ln, fn, 0); + } + } else { + /* for this fec change any exp-null back to imp-null */ + if (fn->local_label == exp_label) { + fn->local_label = lde_update_label(fn); + RB_FOREACH(ln, nbr_tree, &lde_nbrs) + lde_send_labelmapping(ln, fn, 0); + } + } + } + RB_FOREACH(ln, nbr_tree, &lde_nbrs) + lde_imsg_compose_ldpe(IMSG_MAPPING_ADD_END, ln->peerid, 0, + NULL, 0); +} + static int lde_address_add(struct lde_nbr *ln, struct lde_addr *lde_addr) { @@ -1624,27 +2056,67 @@ lde_address_list_free(struct lde_nbr *ln) free(lde_addr); } -static void zclient_sync_init(unsigned short instance) +/* + * Event callback used to retry the label-manager sync zapi session. + */ +static int zclient_sync_retry(struct thread *thread) +{ + zclient_sync_init(); + + return 0; +} + +/* + * Initialize and open a synchronous zapi session. This is used by label chunk + * management code, which acquires and releases blocks of labels from the + * zebra label-manager module. + */ +static void zclient_sync_init(void) { + struct zclient_options options = zclient_options_default; + + options.synchronous = true; + /* Initialize special zclient for synchronous message exchanges. */ - zclient_sync = zclient_new(master, &zclient_options_default); + zclient_sync = zclient_new(master, &options); zclient_sync->sock = -1; zclient_sync->redist_default = ZEBRA_ROUTE_LDP; - zclient_sync->instance = instance; + zclient_sync->session_id = 1; /* Distinguish from main session */ zclient_sync->privs = &lde_privs; - while (zclient_socket_connect(zclient_sync) < 0) { + if (zclient_socket_connect(zclient_sync) < 0) { log_warnx("Error connecting synchronous zclient!"); - sleep(1); + goto retry; } /* make socket non-blocking */ sock_set_nonblock(zclient_sync->sock); + /* Send hello to notify zebra this is a synchronous client */ + if (zclient_send_hello(zclient_sync) < 0) { + log_warnx("Error sending hello for synchronous zclient!"); + goto retry; + } + /* Connect to label manager */ - while (lm_label_manager_connect(zclient_sync, 0) != 0) { + if (lm_label_manager_connect(zclient_sync, 0) != 0) { log_warnx("Error connecting to label manager!"); - sleep(1); + goto retry; } + + /* Finish label-manager init once the LM session is running */ + lde_label_list_init(); + + return; + +retry: + + /* Discard failed zclient object */ + zclient_stop(zclient_sync); + zclient_free(zclient_sync); + zclient_sync = NULL; + + /* Retry using a timer */ + thread_add_timer(master, zclient_sync_retry, NULL, 1, NULL); } static void @@ -1653,6 +2125,19 @@ lde_del_label_chunk(void *val) free(val); } +static int +lde_release_label_chunk(uint32_t start, uint32_t end) +{ + int ret; + + ret = lm_release_label_chunk(zclient_sync, start, end); + if (ret < 0) { + log_warnx("Error releasing label chunk!"); + return (-1); + } + return (0); +} + static int lde_get_label_chunk(void) { @@ -1660,7 +2145,8 @@ lde_get_label_chunk(void) uint32_t start, end; debug_labels("getting label chunk (size %u)", CHUNK_SIZE); - ret = lm_get_label_chunk(zclient_sync, 0, CHUNK_SIZE, &start, &end); + ret = lm_get_label_chunk(zclient_sync, 0, MPLS_LABEL_BASE_ANY, + CHUNK_SIZE, &start, &end); if (ret < 0) { log_warnx("Error getting label chunk!"); return -1; @@ -1708,6 +2194,32 @@ on_get_label_chunk_response(uint32_t start, uint32_t end) current_label_chunk = listtail(label_chunk_list); } +void +lde_free_label(uint32_t label) +{ + struct listnode *node; + struct label_chunk *label_chunk; + uint64_t pos; + + for (ALL_LIST_ELEMENTS_RO(label_chunk_list, node, label_chunk)) { + if (label <= label_chunk->end && label >= label_chunk->start) { + pos = 1ULL << (label - label_chunk->start); + label_chunk->used_mask &= ~pos; + /* if nobody is using this chunk and it's not current_label_chunk, then free it */ + if (!label_chunk->used_mask && (current_label_chunk != node)) { + if (lde_release_label_chunk(label_chunk->start, label_chunk->end) != 0) + log_warnx("%s: Error releasing label chunk!", __func__); + else { + listnode_delete(label_chunk_list, label_chunk); + lde_del_label_chunk(label_chunk); + } + } + break; + } + } + return; +} + static uint32_t lde_get_next_label(void) { @@ -1746,3 +2258,172 @@ lde_get_next_label(void) return (label); } + +static void +lde_check_filter_af(int af, struct ldpd_af_conf *af_conf, + const char *filter_name) +{ + if (strcmp(af_conf->acl_label_allocate_for, filter_name) == 0) + lde_change_allocate_filter(af); + if ((strcmp(af_conf->acl_label_advertise_to, filter_name) == 0) + || (strcmp(af_conf->acl_label_advertise_for, filter_name) == 0)) + lde_change_advertise_filter(af); + if ((strcmp(af_conf->acl_label_accept_for, filter_name) == 0) + || (strcmp(af_conf->acl_label_accept_from, filter_name) == 0)) + lde_change_accept_filter(af); + if (strcmp(af_conf->acl_label_expnull_for, filter_name) == 0) + lde_change_expnull_for_filter(af); +} + +void lde_route_update(struct iface *iface, int af) +{ + struct fec *f; + struct fec_node *fn; + struct fec_nh *fnh; + struct lde_nbr *ln; + + /* update label of non-connected routes */ + log_debug("update labels for interface %s", iface->name); + RB_FOREACH(f, fec_tree, &ft) { + fn = (struct fec_node *)f; + if (IS_MPLS_UNRESERVED_LABEL(fn->local_label)) + continue; + + switch (af) { + case AF_INET: + if (fn->fec.type != FEC_TYPE_IPV4) + continue; + break; + case AF_INET6: + if (fn->fec.type != FEC_TYPE_IPV6) + continue; + break; + default: + /* unspecified so process both address families */ + break; + } + + LIST_FOREACH(fnh, &fn->nexthops, entry) { + /* + * If connected leave existing label. If LDP + * configured on interface or a static route + * may need new label. If no LDP configured + * treat fec as a connected route + */ + if (fnh->flags & F_FEC_NH_CONNECTED) + break; + + if (fnh->ifindex != iface->ifindex) + continue; + + fnh->flags &= ~F_FEC_NH_NO_LDP; + if (IS_MPLS_RESERVED_LABEL(fn->local_label)) { + fn->local_label = NO_LABEL; + fn->local_label = lde_update_label(fn); + if (fn->local_label != NO_LABEL) + RB_FOREACH(ln, nbr_tree, &lde_nbrs) + lde_send_labelmapping( + ln, fn, 0); + } + break; + } + } + RB_FOREACH(ln, nbr_tree, &lde_nbrs) + lde_imsg_compose_ldpe(IMSG_MAPPING_ADD_END, ln->peerid, + 0, NULL, 0); +} + +void lde_route_update_release(struct iface *iface, int af) +{ + struct lde_nbr *ln; + struct fec *f; + struct fec_node *fn; + struct fec_nh *fnh; + + /* update label of interfaces no longer running LDP */ + log_debug("release all labels for interface %s af %s", iface->name, + af == AF_INET ? "ipv4" : "ipv6"); + RB_FOREACH(f, fec_tree, &ft) { + fn = (struct fec_node *)f; + + switch (af) { + case AF_INET: + if (fn->fec.type != FEC_TYPE_IPV4) + continue; + break; + case AF_INET6: + if (fn->fec.type != FEC_TYPE_IPV6) + continue; + break; + default: + fatalx("lde_route_update_release: unknown af"); + } + + if (fn->local_label == NO_LABEL) + continue; + + LIST_FOREACH(fnh, &fn->nexthops, entry) { + /* + * If connected leave existing label. If LDP + * removed from interface may need new label + * and would be treated as a connected route + */ + if (fnh->flags & F_FEC_NH_CONNECTED) + break; + + if (fnh->ifindex != iface->ifindex) + continue; + + fnh->flags |= F_FEC_NH_NO_LDP; + RB_FOREACH(ln, nbr_tree, &lde_nbrs) + lde_send_labelwithdraw(ln, fn, NULL, NULL); + lde_free_label(fn->local_label); + fn->local_label = NO_LABEL; + fn->local_label = lde_update_label(fn); + if (fn->local_label != NO_LABEL) + RB_FOREACH(ln, nbr_tree, &lde_nbrs) + lde_send_labelmapping(ln, fn, 0); + break; + } + } + RB_FOREACH(ln, nbr_tree, &lde_nbrs) + lde_imsg_compose_ldpe(IMSG_MAPPING_ADD_END, ln->peerid, + 0, NULL, 0); +} + +void lde_route_update_release_all(int af) +{ + struct lde_nbr *ln; + struct fec *f; + struct fec_node *fn; + struct fec_nh *fnh; + + /* remove labels from all interfaces as LDP is no longer running for + * this address family + */ + log_debug("release all labels for address family %s", + af == AF_INET ? "ipv4" : "ipv6"); + RB_FOREACH(f, fec_tree, &ft) { + fn = (struct fec_node *)f; + switch (af) { + case AF_INET: + if (fn->fec.type != FEC_TYPE_IPV4) + continue; + break; + case AF_INET6: + if (fn->fec.type != FEC_TYPE_IPV6) + continue; + break; + default: + fatalx("lde_route_update_release: unknown af"); + } + + RB_FOREACH(ln, nbr_tree, &lde_nbrs) + lde_send_labelwithdraw(ln, fn, NULL, NULL); + + LIST_FOREACH(fnh, &fn->nexthops, entry) { + fnh->flags |= F_FEC_NH_NO_LDP; + lde_send_delete_klabel(fn, fnh); + } + } +} diff --git a/ldpd/lde.h b/ldpd/lde.h index 94077d1631..660aeafb34 100644 --- a/ldpd/lde.h +++ b/ldpd/lde.h @@ -108,11 +108,14 @@ struct fec_nh { union ldpd_addr nexthop; ifindex_t ifindex; uint32_t remote_label; - uint8_t priority; + uint8_t route_type; + unsigned short route_instance; uint8_t flags; }; #define F_FEC_NH_NEW 0x01 #define F_FEC_NH_CONNECTED 0x02 +#define F_FEC_NH_DEFER 0x04 /* running ordered control */ +#define F_FEC_NH_NO_LDP 0x08 /* no ldp on this interface */ struct fec_node { struct fec fec; @@ -122,6 +125,9 @@ struct fec_node { struct lde_map_head upstream; /* sent mappings */ uint32_t local_label; + + uint32_t pw_remote_status; + void *data; /* fec specific data */ }; @@ -147,6 +153,7 @@ void lde_imsg_compose_parent_sync(int, pid_t, void *, uint16_t); int lde_imsg_compose_ldpe(int, uint32_t, pid_t, void *, uint16_t); int lde_acl_check(char *, int, union ldpd_addr *, uint8_t); uint32_t lde_update_label(struct fec_node *); +void lde_free_label(uint32_t label); void lde_send_change_klabel(struct fec_node *, struct fec_nh *); void lde_send_delete_klabel(struct fec_node *, struct fec_nh *); void lde_fec2map(struct fec *, struct map *); @@ -164,6 +171,9 @@ void lde_send_labelwithdraw_pwid_wcard(struct lde_nbr *, uint16_t, uint32_t); void lde_send_labelrelease(struct lde_nbr *, struct fec_node *, struct map *, uint32_t); +void lde_send_labelrequest(struct lde_nbr *, struct fec_node *, + struct map *, int); +void lde_send_labelrequest_wcard(struct lde_nbr *, uint16_t af); void lde_send_notification(struct lde_nbr *, uint32_t, uint32_t, uint16_t); void lde_send_notification_eol_prefix(struct lde_nbr *, int); @@ -179,6 +189,13 @@ void lde_req_del(struct lde_nbr *, struct lde_req *, int); struct lde_wdraw *lde_wdraw_add(struct lde_nbr *, struct fec_node *); void lde_wdraw_del(struct lde_nbr *, struct lde_wdraw *); void lde_change_egress_label(int); +void lde_change_allocate_filter(int); +void lde_change_advertise_filter(int); +void lde_change_accept_filter(int); +void lde_change_expnull_for_filter(int); +void lde_route_update(struct iface *, int); +void lde_route_update_release(struct iface *, int); +void lde_route_update_release_all(int); struct lde_addr *lde_address_find(struct lde_nbr *, int, union ldpd_addr *); @@ -192,13 +209,13 @@ void rt_dump(pid_t); void fec_snap(struct lde_nbr *); void fec_tree_clear(void); struct fec_nh *fec_nh_find(struct fec_node *, int, union ldpd_addr *, - ifindex_t, uint8_t); + ifindex_t, uint8_t, unsigned short); void lde_kernel_insert(struct fec *, int, union ldpd_addr *, - ifindex_t, uint8_t, int, void *); + ifindex_t, uint8_t, unsigned short, int, void *); void lde_kernel_remove(struct fec *, int, union ldpd_addr *, - ifindex_t, uint8_t); + ifindex_t, uint8_t, unsigned short); void lde_kernel_update(struct fec *); -void lde_check_mapping(struct map *, struct lde_nbr *); +void lde_check_mapping(struct map *, struct lde_nbr *, int); void lde_check_request(struct map *, struct lde_nbr *); void lde_check_request_wcard(struct map *, struct lde_nbr *); void lde_check_release(struct map *, struct lde_nbr *); diff --git a/ldpd/lde_lib.c b/ldpd/lde_lib.c index 28e455c7a5..bed276c7b1 100644 --- a/ldpd/lde_lib.c +++ b/ldpd/lde_lib.c @@ -20,6 +20,7 @@ #include #include "ldpd.h" +#include "ldpe.h" #include "lde.h" #include "log.h" @@ -31,7 +32,7 @@ static int lde_nbr_is_nexthop(struct fec_node *, static void fec_free(void *); static struct fec_node *fec_add(struct fec *fec); static struct fec_nh *fec_nh_add(struct fec_node *, int, union ldpd_addr *, - ifindex_t, uint8_t); + ifindex_t, uint8_t, unsigned short); static void fec_nh_del(struct fec_nh *); RB_GENERATE(fec_tree, fec, entry, fec_compare) @@ -266,6 +267,9 @@ fec_add(struct fec *fec) RB_INIT(lde_map_head, &fn->downstream); LIST_INIT(&fn->nexthops); + if (fec->type == FEC_TYPE_PWID) + fn->pw_remote_status = PW_FORWARDING; + if (fec_insert(&ft, &fn->fec)) log_warnx("failed to add %s to ft tree", log_fec(&fn->fec)); @@ -275,7 +279,7 @@ fec_add(struct fec *fec) struct fec_nh * fec_nh_find(struct fec_node *fn, int af, union ldpd_addr *nexthop, - ifindex_t ifindex, uint8_t priority) + ifindex_t ifindex, uint8_t route_type, unsigned short route_instance) { struct fec_nh *fnh; @@ -283,7 +287,8 @@ fec_nh_find(struct fec_node *fn, int af, union ldpd_addr *nexthop, if (fnh->af == af && ldp_addrcmp(af, &fnh->nexthop, nexthop) == 0 && fnh->ifindex == ifindex && - fnh->priority == priority) + fnh->route_type == route_type && + fnh->route_instance == route_instance) return (fnh); return (NULL); @@ -291,7 +296,7 @@ fec_nh_find(struct fec_node *fn, int af, union ldpd_addr *nexthop, static struct fec_nh * fec_nh_add(struct fec_node *fn, int af, union ldpd_addr *nexthop, - ifindex_t ifindex, uint8_t priority) + ifindex_t ifindex, uint8_t route_type, unsigned short route_instance) { struct fec_nh *fnh; @@ -303,7 +308,8 @@ fec_nh_add(struct fec_node *fn, int af, union ldpd_addr *nexthop, fnh->nexthop = *nexthop; fnh->ifindex = ifindex; fnh->remote_label = NO_LABEL; - fnh->priority = priority; + fnh->route_type = route_type; + fnh->route_instance = route_instance; LIST_INSERT_HEAD(&fn->nexthops, fnh, entry); return (fnh); @@ -318,10 +324,12 @@ fec_nh_del(struct fec_nh *fnh) void lde_kernel_insert(struct fec *fec, int af, union ldpd_addr *nexthop, - ifindex_t ifindex, uint8_t priority, int connected, void *data) + ifindex_t ifindex, uint8_t route_type, unsigned short route_instance, + int connected, void *data) { struct fec_node *fn; struct fec_nh *fnh; + struct iface *iface; fn = (struct fec_node *)fec_find(&ft, fec); if (fn == NULL) @@ -329,9 +337,22 @@ lde_kernel_insert(struct fec *fec, int af, union ldpd_addr *nexthop, if (data) fn->data = data; - fnh = fec_nh_find(fn, af, nexthop, ifindex, priority); - if (fnh == NULL) - fnh = fec_nh_add(fn, af, nexthop, ifindex, priority); + fnh = fec_nh_find(fn, af, nexthop, ifindex, route_type, route_instance); + if (fnh == NULL) { + fnh = fec_nh_add(fn, af, nexthop, ifindex, route_type, + route_instance); + /* + * Ordered Control: if not a connected route and not a route + * learned over an interface not running LDP and not a PW + * then mark to wait until we receive labelmap msg before + * installing in kernel and sending to peer + */ + iface = if_lookup(ldeconf, ifindex); + if ((ldeconf->flags & F_LDPD_ORDERED_CONTROL) && + !connected && iface != NULL && fec->type != FEC_TYPE_PWID) + fnh->flags |= F_FEC_NH_DEFER; + } + fnh->flags |= F_FEC_NH_NEW; if (connected) fnh->flags |= F_FEC_NH_CONNECTED; @@ -339,7 +360,7 @@ lde_kernel_insert(struct fec *fec, int af, union ldpd_addr *nexthop, void lde_kernel_remove(struct fec *fec, int af, union ldpd_addr *nexthop, - ifindex_t ifindex, uint8_t priority) + ifindex_t ifindex, uint8_t route_type, unsigned short route_instance) { struct fec_node *fn; struct fec_nh *fnh; @@ -348,7 +369,7 @@ lde_kernel_remove(struct fec *fec, int af, union ldpd_addr *nexthop, if (fn == NULL) /* route lost */ return; - fnh = fec_nh_find(fn, af, nexthop, ifindex, priority); + fnh = fec_nh_find(fn, af, nexthop, ifindex, route_type, route_instance); if (fnh == NULL) /* route lost */ return; @@ -370,15 +391,30 @@ lde_kernel_update(struct fec *fec) struct fec_nh *fnh, *safe; struct lde_nbr *ln; struct lde_map *me; + struct iface *iface; fn = (struct fec_node *)fec_find(&ft, fec); if (fn == NULL) return; LIST_FOREACH_SAFE(fnh, &fn->nexthops, entry, safe) { - if (fnh->flags & F_FEC_NH_NEW) + if (fnh->flags & F_FEC_NH_NEW) { fnh->flags &= ~F_FEC_NH_NEW; - else { + /* + * if LDP configured on interface or a static route + * clear flag else treat fec as a connected route + */ + if (ldeconf->flags & F_LDPD_ENABLED) { + iface = if_lookup(ldeconf,fnh->ifindex); + if (fnh->flags & F_FEC_NH_CONNECTED || + iface || + fnh->route_type == ZEBRA_ROUTE_STATIC) + fnh->flags &=~F_FEC_NH_NO_LDP; + else + fnh->flags |= F_FEC_NH_NO_LDP; + } else + fnh->flags |= F_FEC_NH_NO_LDP; + } else { lde_send_delete_klabel(fn, fnh); fec_nh_del(fnh); } @@ -406,6 +442,10 @@ lde_kernel_update(struct fec *fec) lde_send_labelmapping(ln, fn, 1); } + /* if no label created yet then don't try to program labeled route */ + if (fn->local_label == NO_LABEL) + return; + LIST_FOREACH(fnh, &fn->nexthops, entry) { lde_send_change_klabel(fn, fnh); @@ -427,13 +467,13 @@ lde_kernel_update(struct fec *fec) me = (struct lde_map *)fec_find(&ln->recv_map, &fn->fec); if (me) /* FEC.5 */ - lde_check_mapping(&me->map, ln); + lde_check_mapping(&me->map, ln, 0); } } } void -lde_check_mapping(struct map *map, struct lde_nbr *ln) +lde_check_mapping(struct map *map, struct lde_nbr *ln, int rcvd_label_mapping) { struct fec fec; struct fec_node *fn; @@ -441,6 +481,7 @@ lde_check_mapping(struct map *map, struct lde_nbr *ln) struct lde_req *lre; struct lde_map *me; struct l2vpn_pw *pw; + bool send_map = false; lde_map2fec(map, ln->id, &fec); @@ -478,8 +519,12 @@ lde_check_mapping(struct map *map, struct lde_nbr *ln) lde_req_del(ln, lre, 1); /* RFC 4447 control word and status tlv negotiation */ - if (map->type == MAP_TYPE_PWID && l2vpn_pw_negotiate(ln, fn, map)) + if (map->type == MAP_TYPE_PWID && l2vpn_pw_negotiate(ln, fn, map)) { + if (rcvd_label_mapping && map->flags & F_MAP_PW_STATUS) + fn->pw_remote_status = map->pw_status; + return; + } /* * LMp.3 - LMp.8: loop detection - unnecessary for frame-mode @@ -521,8 +566,18 @@ lde_check_mapping(struct map *map, struct lde_nbr *ln) if (!lde_address_find(ln, fnh->af, &fnh->nexthop)) continue; + /* + * Ordered Control: labelmap msg received from + * NH so clear flag and send labelmap msg to + * peer + */ + if (ldeconf->flags & F_LDPD_ORDERED_CONTROL) { + send_map = true; + fnh->flags &= ~F_FEC_NH_DEFER; + } fnh->remote_label = map->label; - lde_send_change_klabel(fn, fnh); + if (fn->local_label != NO_LABEL) + lde_send_change_klabel(fn, fnh); break; case FEC_TYPE_PWID: pw = (struct l2vpn_pw *) fn->data; @@ -532,8 +587,10 @@ lde_check_mapping(struct map *map, struct lde_nbr *ln) pw->remote_group = map->fec.pwid.group_id; if (map->flags & F_MAP_PW_IFMTU) pw->remote_mtu = map->fec.pwid.ifmtu; - if (map->flags & F_MAP_PW_STATUS) + if (rcvd_label_mapping && map->flags & F_MAP_PW_STATUS) { pw->remote_status = map->pw_status; + fn->pw_remote_status = map->pw_status; + } else pw->remote_status = PW_FORWARDING; fnh->remote_label = map->label; @@ -554,6 +611,15 @@ lde_check_mapping(struct map *map, struct lde_nbr *ln) * loop detection. LMp.28 - LMp.30 are unnecessary because we are * merging capable. */ + + /* + * Ordered Control: just received a labelmap for this fec from NH so + * need to send labelmap to all peers + * LMp.20 - LMp21 Execute procedure to send Label Mapping + */ + if (send_map && fn->local_label != NO_LABEL) + RB_FOREACH(ln, nbr_tree, &lde_nbrs) + lde_send_labelmapping(ln, fn, 1); } void @@ -753,6 +819,7 @@ lde_check_withdraw(struct map *map, struct lde_nbr *ln) struct fec_nh *fnh; struct lde_map *me; struct l2vpn_pw *pw; + struct lde_nbr *lnbr; /* wildcard label withdraw */ if (map->type == MAP_TYPE_WILDCARD || @@ -799,6 +866,31 @@ lde_check_withdraw(struct map *map, struct lde_nbr *ln) if (me && (map->label == NO_LABEL || map->label == me->map.label)) /* LWd.4: remove record of previously received lbl mapping */ lde_map_del(ln, me, 0); + else + /* LWd.13 done */ + return; + + /* Ordered Control: additional withdraw steps */ + if (ldeconf->flags & F_LDPD_ORDERED_CONTROL) { + /* LWd.8: for each neighbor other that src of withdraw msg */ + RB_FOREACH(lnbr, nbr_tree, &lde_nbrs) { + if (ln->peerid == lnbr->peerid) + continue; + + /* LWd.9: check if previously sent a label mapping */ + me = (struct lde_map *)fec_find(&lnbr->sent_map, + &fn->fec); + + /* + * LWd.10: does label sent to peer "map" to withdraw + * label + */ + if (me && lde_nbr_is_nexthop(fn, lnbr)) + /* LWd.11: send label withdraw */ + lde_send_labelwithdraw(lnbr, fn, NULL, NULL); + } + } + } void @@ -809,6 +901,7 @@ lde_check_withdraw_wcard(struct map *map, struct lde_nbr *ln) struct fec_nh *fnh; struct lde_map *me; struct l2vpn_pw *pw; + struct lde_nbr *lnbr; /* LWd.2: send label release */ lde_send_labelrelease(ln, NULL, map, map->label); @@ -855,6 +948,35 @@ lde_check_withdraw_wcard(struct map *map, struct lde_nbr *ln) * label mapping */ lde_map_del(ln, me, 0); + else + /* LWd.13 done */ + continue; + + /* Ordered Control: additional withdraw steps */ + if (ldeconf->flags & F_LDPD_ORDERED_CONTROL) { + /* + * LWd.8: for each neighbor other that src of + * withdraw msg + */ + RB_FOREACH(lnbr, nbr_tree, &lde_nbrs) { + if (ln->peerid == lnbr->peerid) + continue; + + /* LWd.9: check if previously sent a label + * mapping + */ + me = (struct lde_map *)fec_find( + &lnbr->sent_map, &fn->fec); + /* + * LWd.10: does label sent to peer "map" to + * withdraw label + */ + if (me && lde_nbr_is_nexthop(fn, lnbr)) + /* LWd.11: send label withdraw */ + lde_send_labelwithdraw(lnbr, fn, NULL, + NULL); + } + } } } @@ -919,6 +1041,9 @@ lde_gc_timer(struct thread *thread) !RB_EMPTY(lde_map_head, &fn->upstream)) continue; + if (fn->local_label != NO_LABEL) + lde_free_label(fn->local_label); + fec_remove(&ft, &fn->fec); free(fn); count++; diff --git a/ldpd/ldp.h b/ldpd/ldp.h index cac3da7c55..5d1bf81ec7 100644 --- a/ldpd/ldp.h +++ b/ldpd/ldp.h @@ -326,13 +326,6 @@ struct pw_status_tlv { #define PW_STATUS_TLV_SIZE 8 #define PW_STATUS_TLV_LEN 4 -#define PW_FORWARDING 0 -#define PW_NOT_FORWARDING (1 << 0) -#define PW_LOCAL_RX_FAULT (1 << 1) -#define PW_LOCAL_TX_FAULT (1 << 2) -#define PW_PSN_RX_FAULT (1 << 3) -#define PW_PSN_TX_FAULT (1 << 4) - #define NO_LABEL UINT32_MAX #endif /* !_LDP_H_ */ diff --git a/ldpd/ldp_debug.c b/ldpd/ldp_debug.c index ec70ef510a..b9ef60ff94 100644 --- a/ldpd/ldp_debug.c +++ b/ldpd/ldp_debug.c @@ -29,12 +29,14 @@ struct ldp_debug conf_ldp_debug; struct ldp_debug ldp_debug; +static int ldp_debug_config_write(struct vty *); + /* Debug node. */ -struct cmd_node ldp_debug_node = -{ - DEBUG_NODE, - "", - 1 +struct cmd_node ldp_debug_node = { + .name = "debug", + .node = DEBUG_NODE, + .prompt = "", + .config_write = ldp_debug_config_write, }; int @@ -142,7 +144,7 @@ ldp_vty_show_debugging(struct vty *vty) return (CMD_SUCCESS); } -int +static int ldp_debug_config_write(struct vty *vty) { int write = 0; diff --git a/ldpd/ldp_vty.h b/ldpd/ldp_vty.h index 5e9df4aafe..f6ba8f8c97 100644 --- a/ldpd/ldp_vty.h +++ b/ldpd/ldp_vty.h @@ -33,9 +33,6 @@ extern struct cmd_node ldp_debug_node; union ldpd_addr; int ldp_get_address(const char *, int *, union ldpd_addr *); -int ldp_config_write(struct vty *); -int ldp_l2vpn_config_write(struct vty *); -int ldp_debug_config_write(struct vty *); int ldp_vty_mpls_ldp (struct vty *, const char *); int ldp_vty_address_family (struct vty *, const char *, const char *); int ldp_vty_disc_holdtime(struct vty *, const char *, enum hello_type, long); @@ -52,6 +49,7 @@ int ldp_vty_label_expnull(struct vty *, const char *, const char *); int ldp_vty_label_accept(struct vty *, const char *, const char *, const char *); int ldp_vty_ttl_security(struct vty *, const char *); int ldp_vty_router_id(struct vty *, const char *, struct in_addr); +int ldp_vty_ordered_control(struct vty *, const char *); int ldp_vty_ds_cisco_interop(struct vty *, const char *); int ldp_vty_trans_pref_ipv4(struct vty *, const char *); int ldp_vty_neighbor_password(struct vty *, const char *, struct in_addr, const char *); diff --git a/ldpd/ldp_vty_cmds.c b/ldpd/ldp_vty_cmds.c index c24e1917cc..53a384fe55 100644 --- a/ldpd/ldp_vty_cmds.c +++ b/ldpd/ldp_vty_cmds.c @@ -221,6 +221,15 @@ DEFPY (ldp_router_id, return (ldp_vty_router_id(vty, no, address)); } +DEFPY (ldp_ordered_control, + ldp_ordered_control_cmd, + "[no] ordered-control", + NO_STR + "Configure LDP ordered label distribution control mode\n") +{ + return (ldp_vty_ordered_control(vty, no)); +} + DEFPY (ldp_discovery_targeted_hello_accept, ldp_discovery_targeted_hello_accept_cmd, "[no] discovery targeted-hello accept [from <(1-199)|(1300-2699)|WORD>$from_acl]", @@ -607,6 +616,8 @@ DEFPY (ldp_show_mpls_ldp_binding, "Show detailed information\n" JSON_STR) { + if (!(ldpd_conf->flags & F_LDPD_ENABLED)) + return CMD_SUCCESS; if (!local_label_str) local_label = NO_LABEL; if (!remote_label_str) @@ -770,14 +781,14 @@ ldp_vty_init (void) { cmd_variable_handler_register(l2vpn_var_handlers); - install_node(&ldp_node, ldp_config_write); - install_node(&ldp_ipv4_node, NULL); - install_node(&ldp_ipv6_node, NULL); - install_node(&ldp_ipv4_iface_node, NULL); - install_node(&ldp_ipv6_iface_node, NULL); - install_node(&ldp_l2vpn_node, ldp_l2vpn_config_write); - install_node(&ldp_pseudowire_node, NULL); - install_node(&ldp_debug_node, ldp_debug_config_write); + 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(&ldp_debug_node); install_default(LDP_NODE); install_default(LDP_IPV4_NODE); install_default(LDP_IPV6_NODE); @@ -807,6 +818,7 @@ ldp_vty_init (void) install_element(LDP_NODE, &ldp_neighbor_session_holdtime_cmd); install_element(LDP_NODE, &ldp_neighbor_ttl_security_cmd); install_element(LDP_NODE, &ldp_router_id_cmd); + install_element(LDP_NODE, &ldp_ordered_control_cmd); install_element(LDP_IPV4_NODE, &ldp_discovery_link_holdtime_cmd); install_element(LDP_IPV4_NODE, &ldp_discovery_targeted_holdtime_cmd); diff --git a/ldpd/ldp_vty_conf.c b/ldpd/ldp_vty_conf.c index 4ef57f574a..03dee23b47 100644 --- a/ldpd/ldp_vty_conf.c +++ b/ldpd/ldp_vty_conf.c @@ -30,60 +30,64 @@ #include "vty.h" #include "ldp_vty.h" +static int ldp_config_write(struct vty *); static void ldp_af_iface_config_write(struct vty *, int); static void ldp_af_config_write(struct vty *, int, struct ldpd_conf *, struct ldpd_af_conf *); +static int ldp_l2vpn_config_write(struct vty *); static void ldp_l2vpn_pw_config_write(struct vty *, struct l2vpn_pw *); static int ldp_vty_get_af(struct vty *); static int ldp_iface_is_configured(struct ldpd_conf *, const char *); -struct cmd_node ldp_node = -{ - LDP_NODE, - "%s(config-ldp)# ", - 1, +struct cmd_node ldp_node = { + .name = "ldp", + .node = LDP_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-ldp)# ", + .config_write = ldp_config_write, }; -struct cmd_node ldp_ipv4_node = -{ - LDP_IPV4_NODE, - "%s(config-ldp-af)# ", - 1, +struct cmd_node ldp_ipv4_node = { + .name = "ldp ipv4", + .node = LDP_IPV4_NODE, + .parent_node = LDP_NODE, + .prompt = "%s(config-ldp-af)# ", }; -struct cmd_node ldp_ipv6_node = -{ - LDP_IPV6_NODE, - "%s(config-ldp-af)# ", - 1, +struct cmd_node ldp_ipv6_node = { + .name = "ldp ipv6", + .node = LDP_IPV6_NODE, + .parent_node = LDP_NODE, + .prompt = "%s(config-ldp-af)# ", }; -struct cmd_node ldp_ipv4_iface_node = -{ - LDP_IPV4_IFACE_NODE, - "%s(config-ldp-af-if)# ", - 1, +struct cmd_node ldp_ipv4_iface_node = { + .name = "ldp ipv4 interface", + .node = LDP_IPV4_IFACE_NODE, + .parent_node = LDP_IPV4_NODE, + .prompt = "%s(config-ldp-af-if)# ", }; -struct cmd_node ldp_ipv6_iface_node = -{ - LDP_IPV6_IFACE_NODE, - "%s(config-ldp-af-if)# ", - 1, +struct cmd_node ldp_ipv6_iface_node = { + .name = "ldp ipv6 interface", + .node = LDP_IPV6_IFACE_NODE, + .parent_node = LDP_IPV6_NODE, + .prompt = "%s(config-ldp-af-if)# ", }; -struct cmd_node ldp_l2vpn_node = -{ - LDP_L2VPN_NODE, - "%s(config-l2vpn)# ", - 1, +struct cmd_node ldp_l2vpn_node = { + .name = "ldp l2vpn", + .node = LDP_L2VPN_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-l2vpn)# ", + .config_write = ldp_l2vpn_config_write, }; -struct cmd_node ldp_pseudowire_node = -{ - LDP_PSEUDOWIRE_NODE, - "%s(config-l2vpn-pw)# ", - 1, +struct cmd_node ldp_pseudowire_node = { + .name = "ldp", + .node = LDP_PSEUDOWIRE_NODE, + .parent_node = LDP_L2VPN_NODE, + .prompt = "%s(config-l2vpn-pw)# ", }; int @@ -240,7 +244,7 @@ ldp_af_config_write(struct vty *vty, int af, struct ldpd_conf *conf, vty_out(vty, " exit-address-family\n"); } -int +static int ldp_config_write(struct vty *vty) { struct nbr_params *nbrp; @@ -250,9 +254,8 @@ ldp_config_write(struct vty *vty) vty_out (vty, "mpls ldp\n"); - if (ldpd_conf->rtr_id.s_addr != 0) - vty_out (vty, " router-id %s\n", - inet_ntoa(ldpd_conf->rtr_id)); + if (ldpd_conf->rtr_id.s_addr != INADDR_ANY) + vty_out(vty, " router-id %s\n", inet_ntoa(ldpd_conf->rtr_id)); if (ldpd_conf->lhello_holdtime != LINK_DFLT_HOLDTIME && ldpd_conf->lhello_holdtime != 0) @@ -279,6 +282,9 @@ ldp_config_write(struct vty *vty) if (ldpd_conf->flags & F_LDPD_DS_CISCO_INTEROP) vty_out (vty, " dual-stack cisco-interop\n"); + if (ldpd_conf->flags & F_LDPD_ORDERED_CONTROL) + vty_out (vty, " ordered-control\n"); + RB_FOREACH(nbrp, nbrp_head, &ldpd_conf->nbrp_tree) { if (nbrp->flags & F_NBRP_KEEPALIVE) vty_out (vty, " neighbor %s session holdtime %u\n", @@ -286,12 +292,10 @@ ldp_config_write(struct vty *vty) if (nbrp->flags & F_NBRP_GTSM) { if (nbrp->gtsm_enabled) - vty_out (vty, " neighbor %s ttl-security hops " - "%u\n", inet_ntoa(nbrp->lsr_id), + vty_out (vty, " neighbor %s ttl-security hops %u\n", inet_ntoa(nbrp->lsr_id), nbrp->gtsm_hops); else - vty_out (vty, " neighbor %s ttl-security " - "disable\n",inet_ntoa(nbrp->lsr_id)); + vty_out (vty, " neighbor %s ttl-security disable\n",inet_ntoa(nbrp->lsr_id)); } if (nbrp->auth.method == AUTH_MD5SIG) @@ -343,7 +347,7 @@ ldp_l2vpn_pw_config_write(struct vty *vty, struct l2vpn_pw *pw) vty_out (vty," ! Incomplete config, specify a pw-id\n"); } -int +static int ldp_l2vpn_config_write(struct vty *vty) { struct l2vpn *l2vpn; @@ -997,6 +1001,19 @@ ldp_vty_router_id(struct vty *vty, const char *negate, struct in_addr address) return (CMD_SUCCESS); } +int +ldp_vty_ordered_control(struct vty *vty, const char *negate) +{ + if (negate) + vty_conf->flags &= ~F_LDPD_ORDERED_CONTROL; + else + vty_conf->flags |= F_LDPD_ORDERED_CONTROL; + + ldp_config_apply(vty, vty_conf); + + return (CMD_SUCCESS); +} + int ldp_vty_ds_cisco_interop(struct vty *vty, const char * negate) { @@ -1060,9 +1077,8 @@ ldp_vty_neighbor_password(struct vty *vty, const char *negate, struct in_addr ls password_len = strlcpy(nbrp->auth.md5key, password_str, sizeof(nbrp->auth.md5key)); if (password_len >= sizeof(nbrp->auth.md5key)) - vty_out(vty, "%% password has been truncated to %zu " - "characters.", sizeof(nbrp->auth.md5key) - 1); - nbrp->auth.md5key_len = password_len; + vty_out(vty, "%% password has been truncated to %zu characters.", sizeof(nbrp->auth.md5key) - 1); + nbrp->auth.md5key_len = strlen(nbrp->auth.md5key); nbrp->auth.method = AUTH_MD5SIG; } diff --git a/ldpd/ldp_vty_exec.c b/ldpd/ldp_vty_exec.c index 66c127abdc..d8ed5ccccc 100644 --- a/ldpd/ldp_vty_exec.c +++ b/ldpd/ldp_vty_exec.c @@ -193,7 +193,8 @@ show_interface_msg_json(struct imsg *imsg, struct show_params *params, json_object_int_add(json_iface, "adjacencyCount", iface->adj_cnt); - sprintf(key_name, "%s: %s", iface->name, af_name(iface->af)); + snprintf(key_name, sizeof(key_name), "%s: %s", iface->name, + af_name(iface->af)); json_object_object_add(json, key_name, json_iface); break; case IMSG_CTL_END: @@ -583,8 +584,7 @@ show_nbr_detail_msg(struct vty *vty, struct imsg *imsg, log_addr(nbr->af, &nbr->raddr),ntohs(nbr->rport)); vty_out (vty, " Authentication: %s\n", (nbr->auth_method == AUTH_MD5SIG) ? "TCP MD5 Signature" : "none"); - vty_out(vty, " Session Holdtime: %u secs; " - "KeepAlive interval: %u secs\n", nbr->holdtime, + vty_out(vty, " Session Holdtime: %u secs; KeepAlive interval: %u secs\n", nbr->holdtime, nbr->holdtime / KEEPALIVE_PER_PERIOD); vty_out(vty, " State: %s; Downstream-Unsolicited\n", nbr_state_name(nbr->nbr_state)); @@ -1251,10 +1251,11 @@ show_l2vpn_binding_msg(struct vty *vty, struct imsg *imsg, if (pw->local_label != NO_LABEL) { vty_out (vty, " Local Label: %u\n", pw->local_label); - vty_out (vty, "%-8sCbit: %u, VC Type: %s, " - "GroupID: %u\n", "", pw->local_cword, + vty_out (vty, "%-8sCbit: %u, VC Type: %s, GroupID: %u\n", "", pw->local_cword, pw_type_name(pw->type),pw->local_gid); vty_out (vty, "%-8sMTU: %u\n", "",pw->local_ifmtu); + vty_out (vty, "%-8sLast failure: %s\n", "", + pw_error_code(pw->reason)); } else vty_out (vty," Local Label: unassigned\n"); @@ -1262,8 +1263,7 @@ show_l2vpn_binding_msg(struct vty *vty, struct imsg *imsg, if (pw->remote_label != NO_LABEL) { vty_out (vty, " Remote Label: %u\n", pw->remote_label); - vty_out (vty, "%-8sCbit: %u, VC Type: %s, " - "GroupID: %u\n", "", pw->remote_cword, + vty_out (vty, "%-8sCbit: %u, VC Type: %s, GroupID: %u\n", "", pw->remote_cword, pw_type_name(pw->type),pw->remote_gid); vty_out (vty, "%-8sMTU: %u\n", "",pw->remote_ifmtu); } else @@ -1308,6 +1308,8 @@ show_l2vpn_binding_msg_json(struct imsg *imsg, struct show_params *params, pw->local_gid); json_object_int_add(json_pw, "localIfMtu", pw->local_ifmtu); + json_object_string_add(json_pw, "lastFailureReason", + pw_error_code(pw->reason)); } else json_object_string_add(json_pw, "localLabel", "unassigned"); @@ -1328,7 +1330,8 @@ show_l2vpn_binding_msg_json(struct imsg *imsg, struct show_params *params, json_object_string_add(json_pw, "remoteLabel", "unassigned"); - sprintf(key_name, "%s: %u", inet_ntoa(pw->lsr_id), pw->pwid); + snprintf(key_name, sizeof(key_name), "%s: %u", + inet_ntoa(pw->lsr_id), pw->pwid); json_object_object_add(json, key_name, json_pw); break; case IMSG_CTL_END: @@ -1351,7 +1354,7 @@ show_l2vpn_pw_msg(struct vty *vty, struct imsg *imsg, struct show_params *params vty_out (vty, "%-9s %-15s %-10u %-16s %-10s\n", pw->ifname, inet_ntoa(pw->lsr_id), pw->pwid, pw->l2vpn_name, - (pw->status ? "UP" : "DOWN")); + (pw->status == PW_FORWARDING ? "UP" : "DOWN")); break; case IMSG_CTL_END: vty_out (vty, "\n"); @@ -1378,7 +1381,7 @@ show_l2vpn_pw_msg_json(struct imsg *imsg, struct show_params *params, json_object_string_add(json_pw, "peerId", inet_ntoa(pw->lsr_id)); json_object_int_add(json_pw, "vcId", pw->pwid); json_object_string_add(json_pw, "VpnName", pw->l2vpn_name); - if (pw->status) + if (pw->status == PW_FORWARDING) json_object_string_add(json_pw, "status", "up"); else json_object_string_add(json_pw, "status", "down"); diff --git a/ldpd/ldp_zebra.c b/ldpd/ldp_zebra.c index 9dc5677358..d828fbe977 100644 --- a/ldpd/ldp_zebra.c +++ b/ldpd/ldp_zebra.c @@ -37,24 +37,14 @@ static void ifp2kif(struct interface *, struct kif *); static void ifc2kaddr(struct interface *, struct connected *, struct kaddr *); -static int zebra_send_mpls_labels(int, struct kroute *); -static int ldp_router_id_update(int, struct zclient *, zebra_size_t, - vrf_id_t); -static int ldp_interface_add(int, struct zclient *, zebra_size_t, - vrf_id_t); -static int ldp_interface_delete(int, struct zclient *, zebra_size_t, - vrf_id_t); -static int ldp_interface_status_change(int command, struct zclient *, - zebra_size_t, vrf_id_t); -static int ldp_interface_address_add(int, struct zclient *, zebra_size_t, - vrf_id_t); -static int ldp_interface_address_delete(int, struct zclient *, - zebra_size_t, vrf_id_t); -static int ldp_zebra_read_route(int, struct zclient *, zebra_size_t, - vrf_id_t); -static int ldp_zebra_read_pw_status_update(int, struct zclient *, - zebra_size_t, vrf_id_t); +static int ldp_zebra_send_mpls_labels(int, struct kroute *); +static int ldp_router_id_update(ZAPI_CALLBACK_ARGS); +static int ldp_interface_address_add(ZAPI_CALLBACK_ARGS); +static int ldp_interface_address_delete(ZAPI_CALLBACK_ARGS); +static int ldp_zebra_read_route(ZAPI_CALLBACK_ARGS); +static int ldp_zebra_read_pw_status_update(ZAPI_CALLBACK_ARGS); static void ldp_zebra_connected(struct zclient *); +static void ldp_zebra_filter_update(struct access_list *access); static struct zclient *zclient; @@ -114,12 +104,12 @@ pw2zpw(struct l2vpn_pw *pw, struct zapi_pw *zpw) } static int -zebra_send_mpls_labels(int cmd, struct kroute *kr) +ldp_zebra_send_mpls_labels(int cmd, struct kroute *kr) { - struct stream *s; + struct zapi_labels zl = {}; + struct zapi_nexthop *znh; - if (kr->local_label < MPLS_LABEL_RESERVED_MAX || - kr->remote_label == NO_LABEL) + if (kr->local_label < MPLS_LABEL_RESERVED_MAX) return (0); debug_zebra_out("prefix %s/%u nexthop %s ifindex %u labels %s/%s (%s)", @@ -128,48 +118,75 @@ zebra_send_mpls_labels(int cmd, struct kroute *kr) log_label(kr->local_label), log_label(kr->remote_label), (cmd == ZEBRA_MPLS_LABELS_ADD) ? "add" : "delete"); - /* Reset stream. */ - s = zclient->obuf; - stream_reset(s); + zl.type = ZEBRA_LSP_LDP; + zl.local_label = kr->local_label; - zclient_create_header(s, cmd, VRF_DEFAULT); - stream_putc(s, ZEBRA_LSP_LDP); - stream_putl(s, kr->af); + /* Set prefix. */ + if (kr->remote_label != NO_LABEL) { + SET_FLAG(zl.message, ZAPI_LABELS_FTN); + zl.route.prefix.family = kr->af; + switch (kr->af) { + case AF_INET: + zl.route.prefix.u.prefix4 = kr->prefix.v4; + break; + case AF_INET6: + zl.route.prefix.u.prefix6 = kr->prefix.v6; + break; + default: + fatalx("ldp_zebra_send_mpls_labels: unknown af"); + } + zl.route.prefix.prefixlen = kr->prefixlen; + zl.route.type = kr->route_type; + zl.route.instance = kr->route_instance; + } + + /* + * For broken LSPs, instruct the forwarding plane to pop the top-level + * label and forward packets normally. This is a best-effort attempt + * to deliver labeled IP packets to their final destination (instead of + * dropping them). + */ + if (kr->remote_label == NO_LABEL) + kr->remote_label = MPLS_LABEL_IMPLICIT_NULL; + + /* Set nexthop. */ + zl.nexthop_num = 1; + znh = &zl.nexthops[0]; switch (kr->af) { case AF_INET: - stream_put_in_addr(s, &kr->prefix.v4); - stream_putc(s, kr->prefixlen); - stream_put_in_addr(s, &kr->nexthop.v4); + znh->gate.ipv4 = kr->nexthop.v4; + if (kr->ifindex) + znh->type = NEXTHOP_TYPE_IPV4_IFINDEX; + else + znh->type = NEXTHOP_TYPE_IPV4; break; case AF_INET6: - stream_write(s, (uint8_t *)&kr->prefix.v6, 16); - stream_putc(s, kr->prefixlen); - stream_write(s, (uint8_t *)&kr->nexthop.v6, 16); + znh->gate.ipv6 = kr->nexthop.v6; + if (kr->ifindex) + znh->type = NEXTHOP_TYPE_IPV6_IFINDEX; + else + znh->type = NEXTHOP_TYPE_IPV6; break; default: - fatalx("kr_change: unknown af"); + break; } - stream_putl(s, kr->ifindex); - stream_putc(s, kr->priority); - stream_putl(s, kr->local_label); - stream_putl(s, kr->remote_label); + znh->ifindex = kr->ifindex; + znh->label_num = 1; + znh->labels[0] = kr->remote_label; - /* Put length at the first point of the stream. */ - stream_putw_at(s, 0, stream_get_endp(s)); - - return (zclient_send_message(zclient)); + return zebra_send_mpls_labels(zclient, cmd, &zl); } int kr_change(struct kroute *kr) { - return (zebra_send_mpls_labels(ZEBRA_MPLS_LABELS_ADD, kr)); + return (ldp_zebra_send_mpls_labels(ZEBRA_MPLS_LABELS_ADD, kr)); } int kr_delete(struct kroute *kr) { - return (zebra_send_mpls_labels(ZEBRA_MPLS_LABELS_DELETE, kr)); + return (ldp_zebra_send_mpls_labels(ZEBRA_MPLS_LABELS_DELETE, kr)); } int @@ -235,8 +252,7 @@ kif_redistribute(const char *ifname) } static int -ldp_router_id_update(int command, struct zclient *zclient, zebra_size_t length, - vrf_id_t vrf_id) +ldp_router_id_update(ZAPI_CALLBACK_ARGS) { struct prefix router_id; @@ -255,41 +271,27 @@ ldp_router_id_update(int command, struct zclient *zclient, zebra_size_t length, } static int -ldp_interface_add(int command, struct zclient *zclient, zebra_size_t length, - vrf_id_t vrf_id) +ldp_ifp_create(struct interface *ifp) { - struct interface *ifp; struct kif kif; - ifp = zebra_interface_add_read(zclient->ibuf, vrf_id); debug_zebra_in("interface add %s index %d mtu %d", ifp->name, ifp->ifindex, ifp->mtu); ifp2kif(ifp, &kif); main_imsg_compose_both(IMSG_IFSTATUS, &kif, sizeof(kif)); - return (0); + return 0; } static int -ldp_interface_delete(int command, struct zclient *zclient, zebra_size_t length, - vrf_id_t vrf_id) +ldp_ifp_destroy(struct interface *ifp) { - struct interface *ifp; struct kif kif; - /* zebra_interface_state_read() updates interface structure in iflist */ - ifp = zebra_interface_state_read(zclient->ibuf, vrf_id); - if (ifp == NULL) - return (0); - debug_zebra_in("interface delete %s index %d mtu %d", ifp->name, ifp->ifindex, ifp->mtu); - /* To support pseudo interface do not free interface structure. */ - /* if_delete(ifp); */ - if_set_index(ifp, IFINDEX_INTERNAL); - ifp2kif(ifp, &kif); main_imsg_compose_both(IMSG_IFSTATUS, &kif, sizeof(kif)); @@ -297,23 +299,13 @@ ldp_interface_delete(int command, struct zclient *zclient, zebra_size_t length, } static int -ldp_interface_status_change(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +ldp_interface_status_change_helper(struct interface *ifp) { - struct interface *ifp; struct listnode *node; struct connected *ifc; struct kif kif; struct kaddr ka; - /* - * zebra_interface_state_read() updates interface structure in - * iflist. - */ - ifp = zebra_interface_state_read(zclient->ibuf, vrf_id); - if (ifp == NULL) - return (0); - debug_zebra_in("interface %s state update", ifp->name); ifp2kif(ifp, &kif); @@ -336,15 +328,24 @@ ldp_interface_status_change(int command, struct zclient *zclient, return (0); } +static int ldp_ifp_up(struct interface *ifp) +{ + return ldp_interface_status_change_helper(ifp); +} + +static int ldp_ifp_down(struct interface *ifp) +{ + return ldp_interface_status_change_helper(ifp); +} + static int -ldp_interface_address_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +ldp_interface_address_add(ZAPI_CALLBACK_ARGS) { struct connected *ifc; struct interface *ifp; struct kaddr ka; - ifc = zebra_interface_address_read(command, zclient->ibuf, vrf_id); + ifc = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); if (ifc == NULL) return (0); @@ -365,20 +366,19 @@ ldp_interface_address_add(int command, struct zclient *zclient, } static int -ldp_interface_address_delete(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +ldp_interface_address_delete(ZAPI_CALLBACK_ARGS) { struct connected *ifc; struct interface *ifp; struct kaddr ka; - ifc = zebra_interface_address_read(command, zclient->ibuf, vrf_id); + ifc = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); if (ifc == NULL) return (0); ifp = ifc->ifp; ifc2kaddr(ifp, ifc, &ka); - connected_free(ifc); + connected_free(&ifc); /* Filter invalid addresses. */ if (bad_addr(ka.af, &ka.addr)) @@ -394,8 +394,7 @@ ldp_interface_address_delete(int command, struct zclient *zclient, } static int -ldp_zebra_read_route(int command, struct zclient *zclient, zebra_size_t length, - vrf_id_t vrf_id) +ldp_zebra_read_route(ZAPI_CALLBACK_ARGS) { struct zapi_route api; struct zapi_nexthop *api_nh; @@ -422,7 +421,8 @@ ldp_zebra_read_route(int command, struct zclient *zclient, zebra_size_t length, break; } kr.prefixlen = api.prefix.prefixlen; - kr.priority = api.distance; + kr.route_type = api.type; + kr.route_instance = api.instance; switch (api.type) { case ZEBRA_ROUTE_CONNECT: @@ -439,7 +439,7 @@ ldp_zebra_read_route(int command, struct zclient *zclient, zebra_size_t length, (kr.af == AF_INET6 && IN6_IS_SCOPE_EMBED(&kr.prefix.v6))) return (0); - if (command == ZEBRA_REDISTRIBUTE_ROUTE_ADD) + if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD) add = 1; if (api.nexthop_num == 0) @@ -502,15 +502,15 @@ ldp_zebra_read_route(int command, struct zclient *zclient, zebra_size_t length, * Receive PW status update from Zebra and send it to LDE process. */ static int -ldp_zebra_read_pw_status_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +ldp_zebra_read_pw_status_update(ZAPI_CALLBACK_ARGS) { struct zapi_pw_status zpw; - zebra_read_pw_status_update(command, zclient, length, vrf_id, &zpw); + zebra_read_pw_status_update(cmd, zclient, length, vrf_id, &zpw); - debug_zebra_in("pseudowire %s status %s", zpw.ifname, - (zpw.status == PW_STATUS_UP) ? "up" : "down"); + debug_zebra_in("pseudowire %s status %s 0x%x", zpw.ifname, + (zpw.status == PW_FORWARDING) ? "up" : "down", + zpw.status); main_imsg_compose_lde(IMSG_PW_UPDATE, 0, &zpw, sizeof(zpw)); @@ -527,11 +527,30 @@ ldp_zebra_connected(struct zclient *zclient) ZEBRA_ROUTE_ALL, 0, VRF_DEFAULT); } +static void +ldp_zebra_filter_update(struct access_list *access) +{ + struct ldp_access laccess; + + if (access && access->name[0] != '\0') { + strlcpy(laccess.name, access->name, sizeof(laccess.name)); + laccess.type = access->type; + debug_evt("%s ACL update filter name %s type %d", __func__, + access->name, access->type); + + main_imsg_compose_both(IMSG_FILTER_UPDATE, &laccess, + sizeof(laccess)); + } +} + extern struct zebra_privs_t ldpd_privs; void ldp_zebra_init(struct thread_master *master) { + if_zapi_callbacks(ldp_ifp_create, ldp_ifp_up, + ldp_ifp_down, ldp_ifp_destroy); + /* Set default values. */ zclient = zclient_new(master, &zclient_options_default); zclient_init(zclient, ZEBRA_ROUTE_LDP, 0, &ldpd_privs); @@ -539,15 +558,15 @@ ldp_zebra_init(struct thread_master *master) /* set callbacks */ zclient->zebra_connected = ldp_zebra_connected; zclient->router_id_update = ldp_router_id_update; - zclient->interface_add = ldp_interface_add; - zclient->interface_delete = ldp_interface_delete; - zclient->interface_up = ldp_interface_status_change; - zclient->interface_down = ldp_interface_status_change; zclient->interface_address_add = ldp_interface_address_add; zclient->interface_address_delete = ldp_interface_address_delete; zclient->redistribute_route_add = ldp_zebra_read_route; zclient->redistribute_route_del = ldp_zebra_read_route; zclient->pw_status_update = ldp_zebra_read_pw_status_update; + + /* Access list initialize. */ + access_list_add_hook(ldp_zebra_filter_update); + access_list_delete_hook(ldp_zebra_filter_update); } void diff --git a/ldpd/ldpd.c b/ldpd/ldpd.c index 771d3b7459..dca379e4eb 100644 --- a/ldpd/ldpd.c +++ b/ldpd/ldpd.c @@ -86,6 +86,8 @@ static struct imsgev *iev_lde, *iev_lde_sync; static pid_t ldpe_pid; static pid_t lde_pid; +enum ldpd_process ldpd_process; + #define LDP_DEFAULT_CONFIG "ldpd.conf" #define LDP_VTY_PORT 2612 @@ -116,11 +118,11 @@ struct zebra_privs_t ldpd_privs = }; /* CTL Socket path */ -char ctl_sock_path[MAXPATHLEN] = LDPD_SOCKET; +char ctl_sock_path[MAXPATHLEN]; /* LDPd options. */ #define OPTION_CTLSOCK 1001 -static struct option longopts[] = +static const struct option longopts[] = { { "ctl_socket", required_argument, NULL, OPTION_CTLSOCK}, { "instance", required_argument, NULL, 'n'}, @@ -177,7 +179,9 @@ static struct quagga_signal_t ldp_signals[] = } }; -static const struct frr_yang_module_info *ldpd_yang_modules[] = { +static const struct frr_yang_module_info *const ldpd_yang_modules[] = { + &frr_filter_info, + &frr_vrf_info, }; FRR_DAEMON_INFO(ldpd, LDP, @@ -219,6 +223,10 @@ main(int argc, char *argv[]) int pipe_parent2lde[2], pipe_parent2lde_sync[2]; char *ctl_sock_name; struct thread *thread = NULL; + bool ctl_sock_used = false; + + snprintf(ctl_sock_path, sizeof(ctl_sock_path), LDPD_SOCKET, + "", ""); ldpd_process = PROC_MAIN; log_procname = log_procnames[ldpd_process]; @@ -244,6 +252,7 @@ main(int argc, char *argv[]) case 0: break; case OPTION_CTLSOCK: + ctl_sock_used = true; ctl_sock_name = strrchr(LDPD_SOCKET, '/'); if (ctl_sock_name) /* skip '/' */ @@ -277,6 +286,10 @@ main(int argc, char *argv[]) } } + if (ldpd_di.pathspace && !ctl_sock_used) + snprintf(ctl_sock_path, sizeof(ctl_sock_path), LDPD_SOCKET, + "/", ldpd_di.pathspace); + strlcpy(init.user, ldpd_privs.user, sizeof(init.user)); strlcpy(init.group, ldpd_privs.group, sizeof(init.group)); strlcpy(init.ctl_sock_path, ctl_sock_path, sizeof(init.ctl_sock_path)); @@ -294,9 +307,15 @@ main(int argc, char *argv[]) exit(1); } - if (lflag || eflag) - openzlog(ldpd_di.progname, "LDP", 0, - LOG_CONS | LOG_NDELAY | LOG_PID, LOG_DAEMON); + if (lflag || eflag) { + struct zprivs_ids_t ids; + + zprivs_preinit(&ldpd_privs); + zprivs_get_ids(&ids); + + zlog_init(ldpd_di.progname, "LDP", 0, + ids.uid_normal, ids.gid_normal); + } if (lflag) lde(); else if (eflag) @@ -438,7 +457,7 @@ ldpd_shutdown(void) if (errno == EINTR) continue; /* No more processes were found. */ - if (errno != ECHILD) + if (errno == ECHILD) break; /* Unhandled errno condition. */ @@ -472,11 +491,12 @@ ldpd_shutdown(void) static pid_t start_child(enum ldpd_process p, char *argv0, int fd_async, int fd_sync) { - char *argv[3]; + char *argv[7]; int argc = 0, nullfd; pid_t pid; - switch (pid = fork()) { + pid = fork(); + switch (pid) { case -1: fatal("cannot fork"); case 0: @@ -515,6 +535,11 @@ start_child(enum ldpd_process p, char *argv0, int fd_async, int fd_sync) argv[argc++] = (char *)"-E"; break; } + + argv[argc++] = (char *)"-u"; + argv[argc++] = (char *)ldpd_privs.user; + argv[argc++] = (char *)"-g"; + argv[argc++] = (char *)ldpd_privs.group; argv[argc++] = NULL; execvp(argv0, argv); @@ -637,23 +662,19 @@ main_dispatch_lde(struct thread *thread) switch (imsg.hdr.type) { case IMSG_KPW_ADD: if (kmpw_add(imsg.data)) - log_warnx("%s: error adding " - "pseudowire", __func__); + log_warnx("%s: error adding pseudowire", __func__); break; case IMSG_KPW_DELETE: if (kmpw_del(imsg.data)) - log_warnx("%s: error deleting " - "pseudowire", __func__); + log_warnx("%s: error deleting pseudowire", __func__); break; case IMSG_KPW_SET: if (kmpw_set(imsg.data)) - log_warnx("%s: error setting " - "pseudowire", __func__); + log_warnx("%s: error setting pseudowire", __func__); break; case IMSG_KPW_UNSET: if (kmpw_unset(imsg.data)) - log_warnx("%s: error unsetting " - "pseudowire", __func__); + log_warnx("%s: error unsetting pseudowire", __func__); break; } break; @@ -832,8 +853,7 @@ main_imsg_send_net_socket(int af, enum socket_type type) fd = ldp_create_socket(af, type); if (fd == -1) { - log_warnx("%s: failed to create %s socket for address-family " - "%s", __func__, socket_name(type), af_name(af)); + log_warnx("%s: failed to create %s socket for address-family %s", __func__, socket_name(type), af_name(af)); return; } @@ -846,7 +866,6 @@ ldp_acl_request(struct imsgev *iev, char *acl_name, int af, union ldpd_addr *addr, uint8_t prefixlen) { struct imsg imsg; - ssize_t n; struct acl_check acl_check; if (acl_name[0] == '\0') @@ -864,9 +883,9 @@ ldp_acl_request(struct imsgev *iev, char *acl_name, int af, imsg_flush(&iev->ibuf); /* receive (blocking) and parse result */ - if ((n = imsg_read(&iev->ibuf)) == -1) + if (imsg_read(&iev->ibuf) == -1) fatal("imsg_read error"); - if ((n = imsg_get(&iev->ibuf, &imsg)) == -1) + if (imsg_get(&iev->ibuf, &imsg) == -1) fatal("imsg_get"); if (imsg.hdr.type != IMSG_ACL_CHECK || imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(int)) @@ -1272,6 +1291,14 @@ merge_global(struct ldpd_conf *conf, struct ldpd_conf *xconf) conf->rtr_id = xconf->rtr_id; } + /* + * Configuration of ordered-control or independent-control + * requires resetting all neighborships. + */ + if ((conf->flags & F_LDPD_ORDERED_CONTROL) != + (xconf->flags & F_LDPD_ORDERED_CONTROL)) + ldpe_reset_nbrs(AF_UNSPEC); + conf->lhello_holdtime = xconf->lhello_holdtime; conf->lhello_interval = xconf->lhello_interval; conf->thello_holdtime = xconf->thello_holdtime; @@ -1298,9 +1325,11 @@ merge_af(int af, struct ldpd_af_conf *af_conf, struct ldpd_af_conf *xa) int stop_init_backoff = 0; int remove_dynamic_tnbrs = 0; int change_egress_label = 0; + int change_host_label = 0; int reset_nbrs_ipv4 = 0; int reset_nbrs = 0; int update_sockets = 0; + int change_ldp_disabled = 0; /* update timers */ if (af_conf->keepalive != xa->keepalive) { @@ -1328,6 +1357,17 @@ merge_af(int af, struct ldpd_af_conf *af_conf, struct ldpd_af_conf *xa) if ((af_conf->flags & F_LDPD_AF_EXPNULL) != (xa->flags & F_LDPD_AF_EXPNULL)) change_egress_label = 1; + + /* changing config of host only fec filtering */ + if ((af_conf->flags & F_LDPD_AF_ALLOCHOSTONLY) + != (xa->flags & F_LDPD_AF_ALLOCHOSTONLY)) + change_host_label = 1; + + /* disabling LDP for address family */ + if ((af_conf->flags & F_LDPD_AF_ENABLED) && + !(xa->flags & F_LDPD_AF_ENABLED)) + change_ldp_disabled = 1; + af_conf->flags = xa->flags; /* update the transport address */ @@ -1337,6 +1377,9 @@ merge_af(int af, struct ldpd_af_conf *af_conf, struct ldpd_af_conf *xa) } /* update ACLs */ + if (strcmp(af_conf->acl_label_allocate_for, xa->acl_label_allocate_for)) + change_host_label = 1; + if (strcmp(af_conf->acl_label_advertise_to, xa->acl_label_advertise_to) || strcmp(af_conf->acl_label_advertise_for, @@ -1370,6 +1413,11 @@ merge_af(int af, struct ldpd_af_conf *af_conf, struct ldpd_af_conf *xa) case PROC_LDE_ENGINE: if (change_egress_label) lde_change_egress_label(af); + if (change_host_label) + lde_change_allocate_filter(af); + if (change_ldp_disabled) + lde_route_update_release_all(af); + break; case PROC_LDP_ENGINE: if (stop_init_backoff) @@ -1395,13 +1443,22 @@ merge_ifaces(struct ldpd_conf *conf, struct ldpd_conf *xconf) struct iface *iface, *itmp, *xi; RB_FOREACH_SAFE(iface, iface_head, &conf->iface_tree, itmp) { - /* find deleted interfaces */ - if ((xi = if_lookup_name(xconf, iface->name)) == NULL) { + /* find deleted interfaces, which occurs when LDP is removed + * for all address families + */ + if (if_lookup_name(xconf, iface->name) == NULL) { switch (ldpd_process) { case PROC_LDP_ENGINE: ldpe_if_exit(iface); break; case PROC_LDE_ENGINE: + if (iface->ipv4.enabled) + lde_route_update_release(iface, + AF_INET); + if (iface->ipv6.enabled) + lde_route_update_release(iface, + AF_INET6); + break; case PROC_MAIN: break; } @@ -1429,6 +1486,29 @@ merge_ifaces(struct ldpd_conf *conf, struct ldpd_conf *xconf) continue; } + /* update labels when adding or removing ldp on an + * interface + */ + if (ldpd_process == PROC_LDE_ENGINE) { + /* if we are removing lpd config for an address + * family on an interface then advertise routes + * learned over this interface as if they were + * connected routes + */ + if (iface->ipv4.enabled && !xi->ipv4.enabled) + lde_route_update_release(iface, AF_INET); + if (iface->ipv6.enabled && !xi->ipv6.enabled) + lde_route_update_release(iface, AF_INET6); + + /* if we are adding lpd config for an address + * family on an interface then add proper labels + */ + if (!iface->ipv4.enabled && xi->ipv4.enabled) + lde_route_update(iface, AF_INET); + if (!iface->ipv6.enabled && xi->ipv6.enabled) + lde_route_update(iface, AF_INET6); + } + /* update existing interfaces */ merge_iface_af(&iface->ipv4, &xi->ipv4); merge_iface_af(&iface->ipv6, &xi->ipv6); @@ -1457,7 +1537,7 @@ merge_tnbrs(struct ldpd_conf *conf, struct ldpd_conf *xconf) continue; /* find deleted tnbrs */ - if ((xt = tnbr_find(xconf, tnbr->af, &tnbr->addr)) == NULL) { + if (tnbr_find(xconf, tnbr->af, &tnbr->addr) == NULL) { switch (ldpd_process) { case PROC_LDP_ENGINE: tnbr->flags &= ~F_TNBR_CONFIGURED; @@ -1503,33 +1583,35 @@ merge_nbrps(struct ldpd_conf *conf, struct ldpd_conf *xconf) RB_FOREACH_SAFE(nbrp, nbrp_head, &conf->nbrp_tree, ntmp) { /* find deleted nbrps */ - if ((xn = nbr_params_find(xconf, nbrp->lsr_id)) == NULL) { - switch (ldpd_process) { - case PROC_LDP_ENGINE: - nbr = nbr_find_ldpid(nbrp->lsr_id.s_addr); - if (nbr) { - session_shutdown(nbr, S_SHUTDOWN, 0, 0); + if (nbr_params_find(xconf, nbrp->lsr_id) != NULL) + continue; + + switch (ldpd_process) { + case PROC_LDP_ENGINE: + nbr = nbr_find_ldpid(nbrp->lsr_id.s_addr); + if (nbr) { + session_shutdown(nbr, S_SHUTDOWN, 0, 0); #ifdef __OpenBSD__ - pfkey_remove(nbr); + pfkey_remove(nbr); #else - sock_set_md5sig( - (ldp_af_global_get(&global, - nbr->af))->ldp_session_socket, - nbr->af, &nbr->raddr, NULL); + sock_set_md5sig( + (ldp_af_global_get(&global, nbr->af)) + ->ldp_session_socket, + nbr->af, &nbr->raddr, NULL); #endif - nbr->auth.method = AUTH_NONE; - if (nbr_session_active_role(nbr)) - nbr_establish_connection(nbr); - } - break; - case PROC_LDE_ENGINE: - case PROC_MAIN: - break; + nbr->auth.method = AUTH_NONE; + if (nbr_session_active_role(nbr)) + nbr_establish_connection(nbr); } - RB_REMOVE(nbrp_head, &conf->nbrp_tree, nbrp); - free(nbrp); + break; + case PROC_LDE_ENGINE: + case PROC_MAIN: + break; } + RB_REMOVE(nbrp_head, &conf->nbrp_tree, nbrp); + free(nbrp); } + RB_FOREACH_SAFE(xn, nbrp_head, &xconf->nbrp_tree, ntmp) { /* find new nbrps */ if ((nbrp = nbr_params_find(conf, xn->lsr_id)) == NULL) { @@ -1612,7 +1694,7 @@ merge_l2vpns(struct ldpd_conf *conf, struct ldpd_conf *xconf) RB_FOREACH_SAFE(l2vpn, l2vpn_head, &conf->l2vpn_tree, ltmp) { /* find deleted l2vpns */ - if ((xl = l2vpn_find(xconf, l2vpn->name)) == NULL) { + if (l2vpn_find(xconf, l2vpn->name) == NULL) { switch (ldpd_process) { case PROC_LDE_ENGINE: l2vpn_exit(l2vpn); @@ -1668,14 +1750,14 @@ merge_l2vpn(struct ldpd_conf *xconf, struct l2vpn *l2vpn, struct l2vpn *xl) /* merge intefaces */ RB_FOREACH_SAFE(lif, l2vpn_if_head, &l2vpn->if_tree, ftmp) { /* find deleted interfaces */ - if ((xf = l2vpn_if_find(xl, lif->ifname)) == NULL) { + if (l2vpn_if_find(xl, lif->ifname) == NULL) { RB_REMOVE(l2vpn_if_head, &l2vpn->if_tree, lif); free(lif); } } RB_FOREACH_SAFE(xf, l2vpn_if_head, &xl->if_tree, ftmp) { /* find new interfaces */ - if ((lif = l2vpn_if_find(l2vpn, xf->ifname)) == NULL) { + if (l2vpn_if_find(l2vpn, xf->ifname) == NULL) { COPY(lif, xf); RB_INSERT(l2vpn_if_head, &l2vpn->if_tree, lif); lif->l2vpn = l2vpn; @@ -1694,7 +1776,7 @@ merge_l2vpn(struct ldpd_conf *xconf, struct l2vpn *l2vpn, struct l2vpn *xl) /* merge active pseudowires */ RB_FOREACH_SAFE(pw, l2vpn_pw_head, &l2vpn->pw_tree, ptmp) { /* find deleted active pseudowires */ - if ((xp = l2vpn_pw_find_active(xl, pw->ifname)) == NULL) { + if (l2vpn_pw_find_active(xl, pw->ifname) == NULL) { switch (ldpd_process) { case PROC_LDE_ENGINE: l2vpn_pw_exit(pw); @@ -1795,7 +1877,7 @@ merge_l2vpn(struct ldpd_conf *xconf, struct l2vpn *l2vpn, struct l2vpn *xl) /* merge inactive pseudowires */ RB_FOREACH_SAFE(pw, l2vpn_pw_head, &l2vpn->pw_inactive_tree, ptmp) { /* find deleted inactive pseudowires */ - if ((xp = l2vpn_pw_find_inactive(xl, pw->ifname)) == NULL) { + if (l2vpn_pw_find_inactive(xl, pw->ifname) == NULL) { RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_inactive_tree, pw); free(pw); } diff --git a/ldpd/ldpd.h b/ldpd/ldpd.h index 9113505581..c1bcc56c44 100644 --- a/ldpd/ldpd.h +++ b/ldpd/ldpd.h @@ -151,7 +151,9 @@ enum imsg_type { IMSG_LOG, IMSG_ACL_CHECK, IMSG_INIT, - IMSG_PW_UPDATE + IMSG_PW_UPDATE, + IMSG_FILTER_UPDATE, + IMSG_NBR_SHUTDOWN }; struct ldpd_init { @@ -162,6 +164,11 @@ struct ldpd_init { unsigned short instance; }; +struct ldp_access { + char name[ACL_NAMSIZ]; + enum access_type type; +}; + union ldpd_addr { struct in_addr v4; struct in6_addr v6; @@ -306,7 +313,7 @@ struct iface_af { struct iface { RB_ENTRY(iface) entry; char name[IF_NAMESIZE]; - unsigned int ifindex; + ifindex_t ifindex; struct if_addr_head addr_list; struct in6_addr linklocal; enum iface_type type; @@ -391,7 +398,7 @@ struct l2vpn_if { RB_ENTRY(l2vpn_if) entry; struct l2vpn *l2vpn; char ifname[IF_NAMESIZE]; - unsigned int ifindex; + ifindex_t ifindex; int operative; uint8_t mac[ETH_ALEN]; QOBJ_FIELDS @@ -408,13 +415,14 @@ struct l2vpn_pw { union ldpd_addr addr; uint32_t pwid; char ifname[IF_NAMESIZE]; - unsigned int ifindex; + ifindex_t ifindex; bool enabled; uint32_t remote_group; uint16_t remote_mtu; uint32_t local_status; uint32_t remote_status; uint8_t flags; + uint8_t reason; QOBJ_FIELDS }; RB_HEAD(l2vpn_pw_head, l2vpn_pw); @@ -426,6 +434,12 @@ DECLARE_QOBJ_TYPE(l2vpn_pw) #define F_PW_CWORD 0x08 /* control word negotiated */ #define F_PW_STATIC_NBR_ADDR 0x10 /* static neighbor address configured */ +#define F_PW_NO_ERR 0x00 /* no error reported */ +#define F_PW_LOCAL_NOT_FWD 0x01 /* locally can't forward over PW */ +#define F_PW_REMOTE_NOT_FWD 0x02 /* remote end of PW reported fwd error*/ +#define F_PW_NO_REMOTE_LABEL 0x03 /* have not recvd label from peer */ +#define F_PW_MTU_MISMATCH 0x04 /* mtu mismatch between peers */ + struct l2vpn { RB_ENTRY(l2vpn) entry; char name[L2VPN_NAME_LEN]; @@ -433,7 +447,7 @@ struct l2vpn { int pw_type; int mtu; char br_ifname[IF_NAMESIZE]; - unsigned int br_ifindex; + ifindex_t br_ifindex; struct l2vpn_if_head if_tree; struct l2vpn_pw_head pw_tree; struct l2vpn_pw_head pw_inactive_tree; @@ -446,7 +460,7 @@ DECLARE_QOBJ_TYPE(l2vpn) #define L2VPN_TYPE_VPLS 2 /* ldp_conf */ -enum ldpd_process { +extern enum ldpd_process { PROC_MAIN, PROC_LDP_ENGINE, PROC_LDE_ENGINE @@ -511,6 +525,8 @@ DECLARE_QOBJ_TYPE(ldpd_conf) #define F_LDPD_NO_FIB_UPDATE 0x0001 #define F_LDPD_DS_CISCO_INTEROP 0x0002 #define F_LDPD_ENABLED 0x0004 +#define F_LDPD_ORDERED_CONTROL 0x0008 + struct ldpd_af_global { struct thread *disc_ev; @@ -542,14 +558,15 @@ struct kroute { union ldpd_addr nexthop; uint32_t local_label; uint32_t remote_label; - unsigned short ifindex; - uint8_t priority; + ifindex_t ifindex; + uint8_t route_type; + uint8_t route_instance; uint16_t flags; }; struct kaddr { char ifname[IF_NAMESIZE]; - unsigned short ifindex; + ifindex_t ifindex; int af; union ldpd_addr addr; uint8_t prefixlen; @@ -558,7 +575,7 @@ struct kaddr { struct kif { char ifname[IF_NAMESIZE]; - unsigned short ifindex; + ifindex_t ifindex; int flags; int operative; uint8_t mac[ETH_ALEN]; @@ -576,7 +593,7 @@ struct acl_check { struct ctl_iface { int af; char name[IF_NAMESIZE]; - unsigned int ifindex; + ifindex_t ifindex; int state; enum iface_type type; uint16_t hello_holdtime; @@ -652,6 +669,7 @@ struct ctl_pw { uint16_t remote_ifmtu; uint8_t remote_cword; uint32_t status; + uint8_t reason; }; extern struct ldpd_conf *ldpd_conf, *vty_conf; @@ -759,7 +777,7 @@ int sock_set_bindany(int, int); int sock_set_md5sig(int, int, union ldpd_addr *, const char *); int sock_set_ipv4_tos(int, int); int sock_set_ipv4_pktinfo(int, int); -int sock_set_ipv4_recvdstaddr(int, int); +int sock_set_ipv4_recvdstaddr(int fd, ifindex_t ifindex); int sock_set_ipv4_recvif(int, int); int sock_set_ipv4_minttl(int, int); int sock_set_ipv4_ucast_ttl(int fd, int); @@ -782,7 +800,8 @@ struct fec; const char *log_sockaddr(void *); const char *log_in6addr(const struct in6_addr *); -const char *log_in6addr_scope(const struct in6_addr *, unsigned int); +const char *log_in6addr_scope(const struct in6_addr *addr, + ifindex_t ifidx); const char *log_addr(int, const union ldpd_addr *); char *log_label(uint32_t); const char *log_time(time_t); @@ -797,6 +816,7 @@ const char *if_type_name(enum iface_type); const char *msg_name(uint16_t); const char *status_code_name(uint32_t); const char *pw_type_name(uint16_t); +const char *pw_error_code(uint8_t); /* quagga */ extern struct thread_master *master; diff --git a/ldpd/ldpe.c b/ldpd/ldpe.c index c16d92f28b..105398ff58 100644 --- a/ldpd/ldpe.c +++ b/ldpd/ldpe.c @@ -41,22 +41,21 @@ static int ldpe_dispatch_pfkey(struct thread *); #endif static void ldpe_setup_sockets(int, int, int, int); static void ldpe_close_sockets(int); -static void ldpe_iface_af_ctl(struct ctl_conn *, int, unsigned int); +static void ldpe_iface_af_ctl(struct ctl_conn *c, int af, ifindex_t ifidx); +static void ldpe_check_filter_af(int, struct ldpd_af_conf *, const char *); struct ldpd_conf *leconf; #ifdef __OpenBSD__ struct ldpd_sysdep sysdep; #endif +static struct imsgev iev_main_data; static struct imsgev *iev_main, *iev_main_sync; static struct imsgev *iev_lde; #ifdef __OpenBSD__ static struct thread *pfkey_ev; #endif -/* Master of threads. */ -struct thread_master *master; - /* ldpe privileges */ static zebra_capabilities_t _caps_p [] = { @@ -97,6 +96,8 @@ static struct quagga_signal_t ldpe_signals[] = }, }; +char *pkt_ptr; /* packet buffer */ + /* label distribution protocol engine */ void ldpe(void) @@ -124,8 +125,8 @@ ldpe(void) &iev_main->ev_read); iev_main->handler_write = ldp_write_handler; - if ((iev_main_sync = calloc(1, sizeof(struct imsgev))) == NULL) - fatal(NULL); + memset(&iev_main_data, 0, sizeof(iev_main_data)); + iev_main_sync = &iev_main_data; imsg_init(&iev_main_sync->ibuf, LDPD_FD_SYNC); /* create base configuration */ @@ -231,10 +232,12 @@ ldpe_shutdown(void) if (iev_lde) free(iev_lde); free(iev_main); - free(iev_main_sync); free(pkt_ptr); log_info("ldp engine exiting"); + + zlog_fini(); + exit(0); } @@ -293,7 +296,8 @@ ldpe_dispatch_main(struct thread *thread) struct nbr_params *nbrp; #endif int n, shut = 0; - + struct ldp_access *laccess; + iev->ev_read = NULL; if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) @@ -351,13 +355,11 @@ ldpe_dispatch_main(struct thread *thread) break; case IMSG_SOCKET_IPC: if (iev_lde) { - log_warnx("%s: received unexpected imsg fd " - "to lde", __func__); + log_warnx("%s: received unexpected imsg fd to lde", __func__); break; } if ((fd = imsg.fd) == -1) { - log_warnx("%s: expected to receive imsg fd to " - "lde but didn't receive any", __func__); + log_warnx("%s: expected to receive imsg fd to lde but didn't receive any", __func__); break; } @@ -545,6 +547,18 @@ ldpe_dispatch_main(struct thread *thread) } memcpy(&ldp_debug, imsg.data, sizeof(ldp_debug)); break; + case IMSG_FILTER_UPDATE: + if (imsg.hdr.len != IMSG_HEADER_SIZE + + sizeof(struct ldp_access)) { + log_warnx("%s: wrong imsg len", __func__); + break; + } + laccess = imsg.data; + ldpe_check_filter_af(AF_INET, &leconf->ipv4, + laccess->name); + ldpe_check_filter_af(AF_INET6, &leconf->ipv6, + laccess->name); + break; default: log_debug("ldpe_dispatch_main: error handling imsg %d", imsg.hdr.type); @@ -600,11 +614,8 @@ ldpe_dispatch_lde(struct thread *thread) map = imsg.data; nbr = nbr_find_peerid(imsg.hdr.peerid); - if (nbr == NULL) { - log_debug("ldpe_dispatch_lde: cannot find " - "neighbor"); + if (nbr == NULL) break; - } if (nbr->state != NBR_STA_OPER) break; @@ -628,11 +639,8 @@ ldpe_dispatch_lde(struct thread *thread) case IMSG_REQUEST_ADD_END: case IMSG_WITHDRAW_ADD_END: nbr = nbr_find_peerid(imsg.hdr.peerid); - if (nbr == NULL) { - log_debug("ldpe_dispatch_lde: cannot find " - "neighbor"); + if (nbr == NULL) break; - } if (nbr->state != NBR_STA_OPER) break; @@ -663,8 +671,7 @@ ldpe_dispatch_lde(struct thread *thread) nbr = nbr_find_peerid(imsg.hdr.peerid); if (nbr == NULL) { - log_debug("ldpe_dispatch_lde: cannot find " - "neighbor"); + log_debug("ldpe_dispatch_lde: cannot find neighbor"); break; } if (nbr->state != NBR_STA_OPER) @@ -681,6 +688,16 @@ ldpe_dispatch_lde(struct thread *thread) case IMSG_CTL_SHOW_L2VPN_BINDING: control_imsg_relay(&imsg); break; + case IMSG_NBR_SHUTDOWN: + nbr = nbr_find_peerid(imsg.hdr.peerid); + if (nbr == NULL) { + log_debug("ldpe_dispatch_lde: cannot find neighbor"); + break; + } + if (nbr->state != NBR_STA_OPER) + break; + session_shutdown(nbr,S_SHUTDOWN,0,0); + break; default: log_debug("ldpe_dispatch_lde: error handling imsg %d", imsg.hdr.type); @@ -828,7 +845,7 @@ ldpe_stop_init_backoff(int af) } static void -ldpe_iface_af_ctl(struct ctl_conn *c, int af, unsigned int idx) +ldpe_iface_af_ctl(struct ctl_conn *c, int af, ifindex_t idx) { struct iface *iface; struct iface_af *ia; @@ -848,7 +865,7 @@ ldpe_iface_af_ctl(struct ctl_conn *c, int af, unsigned int idx) } void -ldpe_iface_ctl(struct ctl_conn *c, unsigned int idx) +ldpe_iface_ctl(struct ctl_conn *c, ifindex_t idx) { ldpe_iface_af_ctl(c, AF_INET, idx); ldpe_iface_af_ctl(c, AF_INET6, idx); @@ -981,3 +998,11 @@ mapping_list_clr(struct mapping_head *mh) free(me); } } + +void +ldpe_check_filter_af(int af, struct ldpd_af_conf *af_conf, + const char *filter_name) +{ + if (strcmp(af_conf->acl_thello_accept_from, filter_name) == 0) + ldpe_remove_dynamic_tnbrs(af); +} diff --git a/ldpd/ldpe.h b/ldpd/ldpe.h index ccff1e803d..11650069b7 100644 --- a/ldpd/ldpe.h +++ b/ldpd/ldpe.h @@ -92,7 +92,7 @@ struct nbr { struct in_addr id; /* lsr id */ union ldpd_addr laddr; /* local address */ union ldpd_addr raddr; /* remote address */ - uint32_t raddr_scope; /* remote address scope (v6) */ + ifindex_t raddr_scope; /* remote address scope (v6) */ time_t uptime; int fd; int state; @@ -208,7 +208,7 @@ void ldpe_reset_ds_nbrs(void); void ldpe_remove_dynamic_tnbrs(int); void ldpe_stop_init_backoff(int); struct ctl_conn; -void ldpe_iface_ctl(struct ctl_conn *, unsigned int); +void ldpe_iface_ctl(struct ctl_conn *c, ifindex_t ifidx); void ldpe_adj_ctl(struct ctl_conn *); void ldpe_adj_detail_ctl(struct ctl_conn *); void ldpe_nbr_ctl(struct ctl_conn *); @@ -219,7 +219,7 @@ void mapping_list_clr(struct mapping_head *); struct iface *if_new(const char *); void ldpe_if_init(struct iface *); void ldpe_if_exit(struct iface *); -struct iface *if_lookup(struct ldpd_conf *, unsigned short); +struct iface *if_lookup(struct ldpd_conf *c, ifindex_t ifidx); struct iface *if_lookup_name(struct ldpd_conf *, const char *); void if_update_info(struct iface *, struct kif *); struct iface_af *iface_af_get(struct iface *, int); @@ -291,7 +291,7 @@ struct tcp_conn *tcp_new(int, struct nbr *); void pending_conn_del(struct pending_conn *); struct pending_conn *pending_conn_find(int, union ldpd_addr *); -char *pkt_ptr; /* packet buffer */ +extern char *pkt_ptr; /* packet buffer */ /* pfkey.c */ #ifdef __OpenBSD__ diff --git a/ldpd/log.c b/ldpd/log.c index b138e5754a..1903017588 100644 --- a/ldpd/log.c +++ b/ldpd/log.c @@ -24,7 +24,6 @@ #include "log.h" #include -#include const char *log_procname; diff --git a/ldpd/logmsg.c b/ldpd/logmsg.c index a9b066a3da..6427d0e13b 100644 --- a/ldpd/logmsg.c +++ b/ldpd/logmsg.c @@ -59,7 +59,7 @@ log_in6addr(const struct in6_addr *addr) } const char * -log_in6addr_scope(const struct in6_addr *addr, unsigned int ifindex) +log_in6addr_scope(const struct in6_addr *addr, ifindex_t ifindex) { struct sockaddr_in6 sa_in6; @@ -485,3 +485,25 @@ pw_type_name(uint16_t pw_type) return (buf); } } + +const char * +pw_error_code(uint8_t status) +{ + static char buf[16]; + + switch (status) { + case F_PW_NO_ERR: + return ("No Error"); + case F_PW_LOCAL_NOT_FWD: + return ("local not forwarding"); + case F_PW_REMOTE_NOT_FWD: + return ("remote not forwarding"); + case F_PW_NO_REMOTE_LABEL: + return ("no remote label"); + case F_PW_MTU_MISMATCH: + return ("mtu mismatch between peers"); + default: + snprintf(buf, sizeof(buf), "[%0x]", status); + return (buf); + } +} diff --git a/ldpd/neighbor.c b/ldpd/neighbor.c index 78a6131ca4..6143beb6b9 100644 --- a/ldpd/neighbor.c +++ b/ldpd/neighbor.c @@ -49,7 +49,7 @@ RB_GENERATE(nbr_addr_head, nbr, addr_tree, nbr_addr_compare) RB_GENERATE(nbr_pid_head, nbr, pid_tree, nbr_pid_compare) RB_GENERATE(nbrp_head, nbr_params, entry, nbr_params_compare) -struct { +const struct { int state; enum nbr_event event; enum nbr_action action; @@ -143,8 +143,7 @@ nbr_fsm(struct nbr *nbr, enum nbr_event event) if (nbr_fsm_tbl[i].state == -1) { /* event outside of the defined fsm, ignore it. */ - log_warnx("%s: lsr-id %s, event %s not expected in " - "state %s", __func__, inet_ntoa(nbr->id), + log_warnx("%s: lsr-id %s, event %s not expected in state %s", __func__, inet_ntoa(nbr->id), nbr_event_names[event], nbr_state_name(old_state)); return (0); } @@ -153,8 +152,7 @@ nbr_fsm(struct nbr *nbr, enum nbr_event event) nbr->state = new_state; if (old_state != nbr->state) { - log_debug("%s: event %s resulted in action %s and " - "changing state for lsr-id %s from %s to %s", + log_debug("%s: event %s resulted in action %s and changing state for lsr-id %s from %s to %s", __func__, nbr_event_names[event], nbr_action_names[nbr_fsm_tbl[i].action], inet_ntoa(nbr->id), nbr_state_name(old_state), @@ -619,6 +617,16 @@ nbr_establish_connection(struct nbr *nbr) #endif } + if (nbr->af == AF_INET) { + if (sock_set_ipv4_tos(nbr->fd, IPTOS_PREC_INTERNETCONTROL) == -1) + log_warn("%s: lsr-id %s, sock_set_ipv4_tos error", + __func__, inet_ntoa(nbr->id)); + } else if (nbr->af == AF_INET6) { + if (sock_set_ipv6_dscp(nbr->fd, IPTOS_PREC_INTERNETCONTROL) == -1) + log_warn("%s: lsr-id %s, sock_set_ipv6_dscp error", + __func__, inet_ntoa(nbr->id)); + } + addr2sa(nbr->af, &nbr->laddr, 0, &local_su); addr2sa(nbr->af, &nbr->raddr, LDP_PORT, &remote_su); if (nbr->af == AF_INET6 && nbr->raddr_scope) diff --git a/ldpd/notification.c b/ldpd/notification.c index 4a5f3c8fa4..93be9d3cb0 100644 --- a/ldpd/notification.c +++ b/ldpd/notification.c @@ -309,8 +309,7 @@ void log_msg_notification(int out, struct nbr *nbr, struct notify_msg *nm) { if (nm->status_code & STATUS_FATAL) { - debug_msg(out, "notification: lsr-id %s, status %s " - "(fatal error)", inet_ntoa(nbr->id), + debug_msg(out, "notification: lsr-id %s, status %s (fatal error)", inet_ntoa(nbr->id), status_code_name(nm->status_code)); return; } @@ -321,5 +320,5 @@ log_msg_notification(int out, struct nbr *nbr, struct notify_msg *nm) debug_msg(out, "notification: fec %s", log_map(&nm->fec)); if (nm->flags & F_NOTIF_PW_STATUS) debug_msg(out, "notification: pw-status %s", - (nm->pw_status) ? "not forwarding" : "forwarding"); + (nm->pw_status == PW_FORWARDING) ? "forwarding" : "not forwarding"); } diff --git a/ldpd/packet.c b/ldpd/packet.c index 8ca90841de..c00008d120 100644 --- a/ldpd/packet.c +++ b/ldpd/packet.c @@ -77,8 +77,7 @@ send_packet(int fd, int af, union ldpd_addr *dst, struct iface_af *ia, if (ia && IN_MULTICAST(ntohl(dst->v4.s_addr))) { /* set outgoing interface for multicast traffic */ if (sock_set_ipv4_mcast(ia->iface) == -1) { - log_debug("%s: error setting multicast " - "interface, %s", __func__, ia->iface->name); + log_debug("%s: error setting multicast interface, %s", __func__, ia->iface->name); return (-1); } } @@ -87,8 +86,7 @@ send_packet(int fd, int af, union ldpd_addr *dst, struct iface_af *ia, if (ia && IN6_IS_ADDR_MULTICAST(&dst->v6)) { /* set outgoing interface for multicast traffic */ if (sock_set_ipv6_mcast(ia->iface) == -1) { - log_debug("%s: error setting multicast " - "interface, %s", __func__, ia->iface->name); + log_debug("%s: error setting multicast interface, %s", __func__, ia->iface->name); return (-1); } } @@ -368,8 +366,7 @@ session_accept(struct thread *thread) return (0); } if (nbr->state != NBR_STA_PRESENT) { - log_debug("%s: lsr-id %s: rejecting additional transport " - "connection", __func__, inet_ntoa(nbr->id)); + log_debug("%s: lsr-id %s: rejecting additional transport connection", __func__, inet_ntoa(nbr->id)); close(newfd); return (0); } @@ -618,12 +615,17 @@ session_read(struct thread *thread) len -= msg_size; } free(buf); + buf = NULL; if (len != 0) { session_shutdown(nbr, S_BAD_PDU_LEN, 0, 0); return (0); } } + /* shouldn't happen, session_get_pdu should be > 0 if buf was + * allocated - but let's get rid of the SA warning. + */ + free(buf); return (0); } diff --git a/ldpd/socket.c b/ldpd/socket.c index b31db2c7bc..e865707d44 100644 --- a/ldpd/socket.c +++ b/ldpd/socket.c @@ -79,7 +79,7 @@ ldp_create_socket(int af, enum socket_type type) sock_set_bindany(fd, 1); break; } - frr_elevate_privs(&ldpd_privs) { + frr_with_privs(&ldpd_privs) { if (sock_set_reuse(fd, 1) == -1) { close(fd); return (-1); @@ -209,7 +209,7 @@ sock_set_nonblock(int fd) flags |= O_NONBLOCK; - if ((flags = fcntl(fd, F_SETFL, flags)) == -1) + if (fcntl(fd, F_SETFL, flags) == -1) fatal("fcntl F_SETFL"); } @@ -223,7 +223,7 @@ sock_set_cloexec(int fd) flags |= FD_CLOEXEC; - if ((flags = fcntl(fd, F_SETFD, flags)) == -1) + if (fcntl(fd, F_SETFD, flags) == -1) fatal("fcntl F_SETFD"); } @@ -254,7 +254,7 @@ int sock_set_bindany(int fd, int enable) { #ifdef HAVE_SO_BINDANY - frr_elevate_privs(&ldpd_privs) { + frr_with_privs(&ldpd_privs) { if (setsockopt(fd, SOL_SOCKET, SO_BINDANY, &enable, sizeof(int)) < 0) { log_warn("%s: error setting SO_BINDANY", __func__); @@ -269,7 +269,7 @@ sock_set_bindany(int fd, int enable) } return (0); #elif defined(IP_BINDANY) - frr_elevate_privs(&ldpd_privs) { + frr_with_privs(&ldpd_privs) { if (setsockopt(fd, IPPROTO_IP, IP_BINDANY, &enable, sizeof(int)) < 0) { log_warn("%s: error setting IP_BINDANY", __func__); @@ -304,7 +304,7 @@ sock_set_md5sig(int fd, int af, union ldpd_addr *addr, const char *password) #if HAVE_DECL_TCP_MD5SIG addr2sa(af, addr, 0, &su); - frr_elevate_privs(&ldpe_privs) { + frr_with_privs(&ldpe_privs) { ret = sockopt_tcp_signature(fd, &su, password); save_errno = errno; } @@ -320,7 +320,7 @@ sock_set_md5sig(int fd, int af, union ldpd_addr *addr, const char *password) int sock_set_ipv4_tos(int fd, int tos) { - if (setsockopt(fd, IPPROTO_IP, IP_TOS, (int *)&tos, sizeof(tos)) < 0) { + if (setsockopt(fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) < 0) { log_warn("%s: error setting IP_TOS to 0x%x", __func__, tos); return (-1); } @@ -329,7 +329,7 @@ sock_set_ipv4_tos(int fd, int tos) } int -sock_set_ipv4_recvif(int fd, int enable) +sock_set_ipv4_recvif(int fd, ifindex_t enable) { return (setsockopt_ifindex(AF_INET, fd, enable)); } diff --git a/ldpd/subdir.am b/ldpd/subdir.am index 42c5ad024b..0b38c37872 100644 --- a/ldpd/subdir.am +++ b/ldpd/subdir.am @@ -6,8 +6,8 @@ if LDPD noinst_LIBRARIES += ldpd/libldp.a sbin_PROGRAMS += ldpd/ldpd dist_examples_DATA += ldpd/ldpd.conf.sample -vtysh_scan += $(top_srcdir)/ldpd/ldp_vty_cmds.c -man8 += $(MANBUILD)/ldpd.8 +vtysh_scan += ldpd/ldp_vty_cmds.c +man8 += $(MANBUILD)/frr-ldpd.8 endif ldpd_libldp_a_SOURCES = \ @@ -28,7 +28,6 @@ ldpd_libldp_a_SOURCES = \ ldpd/ldp_vty_conf.c \ ldpd/ldp_vty_exec.c \ ldpd/ldp_zebra.c \ - ldpd/ldpd.c \ ldpd/ldpe.c \ ldpd/log.c \ ldpd/logmsg.c \ @@ -40,8 +39,9 @@ ldpd_libldp_a_SOURCES = \ ldpd/util.c \ # end -ldpd/ldp_vty_cmds_clippy.c: $(CLIPPY_DEPS) -ldpd/ldp_vty_cmds.$(OBJEXT): ldpd/ldp_vty_cmds_clippy.c +clippy_scan += \ + ldpd/ldp_vty_cmds.c \ + # end noinst_HEADERS += \ ldpd/control.h \ diff --git a/lib/agentx.c b/lib/agentx.c index 40cac722ae..7c4bdcbe27 100644 --- a/lib/agentx.c +++ b/lib/agentx.c @@ -55,13 +55,43 @@ static int agentx_timeout(struct thread *t) static int agentx_read(struct thread *t) { fd_set fds; + int flags, new_flags = 0; + int nonblock = false; struct listnode *ln = THREAD_ARG(t); list_delete_node(events, ln); + /* fix for non blocking socket */ + flags = fcntl(THREAD_FD(t), F_GETFL, 0); + if (-1 == flags) { + flog_err(EC_LIB_SYSTEM_CALL, "Failed to get FD settings fcntl: %s(%d)", + strerror(errno), errno); + return -1; + } + + if (flags & O_NONBLOCK) + nonblock = true; + else + new_flags = fcntl(THREAD_FD(t), F_SETFL, flags | O_NONBLOCK); + + if (new_flags == -1) + flog_err(EC_LIB_SYSTEM_CALL, "Failed to set snmp fd non blocking: %s(%d)", + strerror(errno), errno); + FD_ZERO(&fds); FD_SET(THREAD_FD(t), &fds); snmp_read(&fds); + /* Reset the flag */ + if (!nonblock) { + new_flags = fcntl(THREAD_FD(t), F_SETFL, flags); + + if (new_flags == -1) + flog_err( + EC_LIB_SYSTEM_CALL, + "Failed to set snmp fd back to original settings: %s(%d)", + strerror(errno), errno); + } + netsnmp_check_outstanding_agent_requests(); agentx_events_update(); return 0; @@ -128,9 +158,13 @@ static void agentx_events_update(void) } /* AgentX node. */ -static struct cmd_node agentx_node = {SMUX_NODE, - "", /* AgentX has no interface. */ - 1}; +static int config_write_agentx(struct vty *vty); +static struct cmd_node agentx_node = { + .name = "smux", + .node = SMUX_NODE, + .prompt = "", + .config_write = config_write_agentx, +}; /* Logging NetSNMP messages */ static int agentx_log_callback(int major, int minor, void *serverarg, @@ -216,7 +250,7 @@ void smux_init(struct thread_master *tm) agentx_log_callback, NULL); init_agent(FRR_SMUX_NAME); - install_node(&agentx_node, config_write_agentx); + install_node(&agentx_node); install_element(CONFIG_NODE, &agentx_enable_cmd); install_element(CONFIG_NODE, &no_agentx_cmd); } @@ -234,7 +268,7 @@ int smux_trap(struct variable *vp, size_t vp_len, const oid *ename, uint8_t sptrap) { oid objid_snmptrap[] = {1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0}; - size_t objid_snmptrap_len = sizeof objid_snmptrap / sizeof(oid); + size_t objid_snmptrap_len = sizeof(objid_snmptrap) / sizeof(oid); oid notification_oid[MAX_OID_LEN]; size_t notification_oid_len; unsigned int i; diff --git a/lib/agg_table.c b/lib/agg_table.c index dad6a13d67..22b981e284 100644 --- a/lib/agg_table.c +++ b/lib/agg_table.c @@ -41,7 +41,7 @@ static void agg_node_destroy(route_table_delegate_t *delegate, XFREE(MTYPE_TMP, anode); } -route_table_delegate_t agg_table_delegate = { +static route_table_delegate_t agg_table_delegate = { .create_node = agg_node_create, .destroy_node = agg_node_destroy, }; diff --git a/lib/agg_table.h b/lib/agg_table.h index 40ffe8c755..e98476f1b7 100644 --- a/lib/agg_table.h +++ b/lib/agg_table.h @@ -86,13 +86,13 @@ static inline struct agg_node *agg_route_next(struct agg_node *node) } static inline struct agg_node *agg_node_get(struct agg_table *table, - struct prefix *p) + const struct prefix *p) { return agg_node_from_rnode(route_node_get(table->route_table, p)); } static inline struct agg_node * -agg_node_lookup(const struct agg_table *const table, struct prefix *p) +agg_node_lookup(const struct agg_table *const table, const struct prefix *p) { return agg_node_from_rnode(route_node_lookup(table->route_table, p)); } @@ -109,7 +109,7 @@ static inline struct agg_node *agg_route_next_until(struct agg_node *node, } static inline struct agg_node *agg_node_match(struct agg_table *table, - struct prefix *p) + const struct prefix *p) { return agg_node_from_rnode(route_node_match(table->route_table, p)); } @@ -155,6 +155,16 @@ static inline struct agg_table *agg_get_table(struct agg_node *node) return (struct agg_table *)route_table_get_info(node->table); } +static inline const struct prefix * +agg_node_get_prefix(const struct agg_node *node) +{ + return &node->p; +} + +#ifdef _FRR_ATTRIBUTE_PRINTFRR +#pragma FRR printfrr_ext "%pRN" (struct agg_node *) +#endif + #ifdef __cplusplus } #endif diff --git a/lib/atomlist.h b/lib/atomlist.h index 373c481f2a..5ca19cbcd4 100644 --- a/lib/atomlist.h +++ b/lib/atomlist.h @@ -20,6 +20,10 @@ #include "typesafe.h" #include "frratomic.h" +#ifdef __cplusplus +extern "C" { +#endif + /* pointer with lock/deleted/invalid bit in lowest bit * * for atomlist/atomsort, "locked" means "this pointer can't be updated, the @@ -34,7 +38,11 @@ * ATOMPTR_USER is currently unused (and available for atomic hash or skiplist * implementations.) */ -typedef uintptr_t atomptr_t; + +/* atomic_atomptr_t may look a bit odd, it's for the sake of C++ compat */ +typedef uintptr_t atomptr_t; +typedef atomic_uintptr_t atomic_atomptr_t; + #define ATOMPTR_MASK (UINTPTR_MAX - 3) #define ATOMPTR_LOCK (1) #define ATOMPTR_USER (2) @@ -104,13 +112,13 @@ static inline bool atomptr_u(atomptr_t val) /* don't use these structs directly */ struct atomlist_item { - _Atomic atomptr_t next; + atomic_uintptr_t next; }; #define atomlist_itemp(val) ((struct atomlist_item *)atomptr_p(val)) struct atomlist_head { - _Atomic atomptr_t first, last; - _Atomic size_t count; + atomic_uintptr_t first, last; + atomic_size_t count; }; /* use as: @@ -133,10 +141,12 @@ macro_inline void prefix ## _add_head(struct prefix##_head *h, type *item) \ macro_inline void prefix ## _add_tail(struct prefix##_head *h, type *item) \ { atomlist_add_tail(&h->ah, &item->field.ai); } \ macro_inline void prefix ## _del_hint(struct prefix##_head *h, type *item, \ - _Atomic atomptr_t *hint) \ + atomic_atomptr_t *hint) \ { atomlist_del_hint(&h->ah, &item->field.ai, hint); } \ -macro_inline void prefix ## _del(struct prefix##_head *h, type *item) \ -{ atomlist_del_hint(&h->ah, &item->field.ai, NULL); } \ +macro_inline type *prefix ## _del(struct prefix##_head *h, type *item) \ +{ atomlist_del_hint(&h->ah, &item->field.ai, NULL); \ + /* TODO: Return NULL if not found */ \ + return item; } \ macro_inline type *prefix ## _pop(struct prefix##_head *h) \ { char *p = (char *)atomlist_pop(&h->ah); \ return p ? (type *)(p - offsetof(type, field)) : NULL; } \ @@ -152,6 +162,15 @@ macro_inline type *prefix ## _next_safe(struct prefix##_head *h, type *item) \ { return item ? prefix##_next(h, item) : NULL; } \ macro_inline size_t prefix ## _count(struct prefix##_head *h) \ { return atomic_load_explicit(&h->ah.count, memory_order_relaxed); } \ +macro_inline void prefix ## _init(struct prefix##_head *h) \ +{ \ + memset(h, 0, sizeof(*h)); \ +} \ +macro_inline void prefix ## _fini(struct prefix##_head *h) \ +{ \ + assert(prefix ## _count(h) == 0); \ + memset(h, 0, sizeof(*h)); \ +} \ /* ... */ /* add_head: @@ -178,7 +197,7 @@ void atomlist_add_tail(struct atomlist_head *h, struct atomlist_item *item); * reads starting later. */ void atomlist_del_hint(struct atomlist_head *h, struct atomlist_item *item, - _Atomic atomptr_t *hint); + atomic_atomptr_t *hint); /* pop: * @@ -191,13 +210,13 @@ struct atomlist_item *atomlist_pop(struct atomlist_head *h); struct atomsort_item { - _Atomic atomptr_t next; + atomic_atomptr_t next; }; #define atomsort_itemp(val) ((struct atomsort_item *)atomptr_p(val)) struct atomsort_head { - _Atomic atomptr_t first; - _Atomic size_t count; + atomic_atomptr_t first; + atomic_size_t count; }; #define _PREDECL_ATOMSORT(prefix) \ @@ -260,13 +279,15 @@ macro_inline type *prefix ## _find_lt(struct prefix##_head *h, \ return prev; \ } \ macro_inline void prefix ## _del_hint(struct prefix##_head *h, type *item, \ - _Atomic atomptr_t *hint) \ + atomic_atomptr_t *hint) \ { \ atomsort_del_hint(&h->ah, &item->field.ai, hint); \ } \ -macro_inline void prefix ## _del(struct prefix##_head *h, type *item) \ +macro_inline type *prefix ## _del(struct prefix##_head *h, type *item) \ { \ atomsort_del_hint(&h->ah, &item->field.ai, NULL); \ + /* TODO: Return NULL if not found */ \ + return item; \ } \ macro_inline size_t prefix ## _count(struct prefix##_head *h) \ { \ @@ -340,8 +361,12 @@ struct atomsort_item *atomsort_add(struct atomsort_head *h, const struct atomsort_item *)); void atomsort_del_hint(struct atomsort_head *h, - struct atomsort_item *item, _Atomic atomptr_t *hint); + struct atomsort_item *item, atomic_atomptr_t *hint); struct atomsort_item *atomsort_pop(struct atomsort_head *h); +#ifdef __cplusplus +} +#endif + #endif /* _FRR_ATOMLIST_H */ diff --git a/lib/bfd.c b/lib/bfd.c index 7e27a64de7..a309b741dd 100644 --- a/lib/bfd.c +++ b/lib/bfd.c @@ -74,10 +74,7 @@ struct bfd_info *bfd_info_create(void) */ void bfd_info_free(struct bfd_info **bfd_info) { - if (*bfd_info) { - XFREE(MTYPE_BFD_INFO, *bfd_info); - *bfd_info = NULL; - } + XFREE(MTYPE_BFD_INFO, *bfd_info); } /* @@ -97,7 +94,8 @@ int bfd_validate_param(struct vty *vty, const char *dm_str, const char *rx_str, * bfd_set_param - Set the configured BFD paramter values */ void bfd_set_param(struct bfd_info **bfd_info, uint32_t min_rx, uint32_t min_tx, - uint8_t detect_mult, int defaults, int *command) + uint8_t detect_mult, const char *profile, int defaults, + int *command) { if (!*bfd_info) { *bfd_info = bfd_info_create(); @@ -105,7 +103,11 @@ void bfd_set_param(struct bfd_info **bfd_info, uint32_t min_rx, uint32_t min_tx, } else { if (((*bfd_info)->required_min_rx != min_rx) || ((*bfd_info)->desired_min_tx != min_tx) - || ((*bfd_info)->detect_mult != detect_mult)) + || ((*bfd_info)->detect_mult != detect_mult) + || ((*bfd_info)->profile[0] == 0 && profile) + || ((*bfd_info)->profile[0] && profile == NULL) + || (profile && (*bfd_info)->profile[0] + && strcmp((*bfd_info)->profile, profile))) *command = ZEBRA_BFD_DEST_UPDATE; } @@ -113,6 +115,11 @@ void bfd_set_param(struct bfd_info **bfd_info, uint32_t min_rx, uint32_t min_tx, (*bfd_info)->required_min_rx = min_rx; (*bfd_info)->desired_min_tx = min_tx; (*bfd_info)->detect_mult = detect_mult; + if (profile) + strlcpy((*bfd_info)->profile, profile, + BFD_PROFILE_NAME_LEN); + else + (*bfd_info)->profile[0] = '\0'; } if (!defaults) @@ -124,22 +131,23 @@ void bfd_set_param(struct bfd_info **bfd_info, uint32_t min_rx, uint32_t min_tx, /* * bfd_peer_sendmsg - Format and send a peer register/Unregister * command to Zebra to be forwarded to BFD + * + * DEPRECATED: use zclient_bfd_command instead */ void bfd_peer_sendmsg(struct zclient *zclient, struct bfd_info *bfd_info, int family, void *dst_ip, void *src_ip, char *if_name, - int ttl, int multihop, int command, int set_flag, - vrf_id_t vrf_id) + int ttl, int multihop, int cbit, int command, + int set_flag, vrf_id_t vrf_id) { - struct stream *s; - int ret; - int len; + struct bfd_session_arg args = {}; + size_t addrlen; /* Individual reg/dereg messages are suppressed during shutdown. */ if (CHECK_FLAG(bfd_gbl.flags, BFD_GBL_FLAG_IN_SHUTDOWN)) { if (bfd_debug) zlog_debug( "%s: Suppressing BFD peer reg/dereg messages", - __FUNCTION__); + __func__); return; } @@ -147,87 +155,42 @@ void bfd_peer_sendmsg(struct zclient *zclient, struct bfd_info *bfd_info, if (!zclient || zclient->sock < 0) { if (bfd_debug) zlog_debug( - "%s: Can't send BFD peer register, Zebra client not " - "established", - __FUNCTION__); + "%s: Can't send BFD peer register, Zebra client not established", + __func__); return; } - s = zclient->obuf; - stream_reset(s); - zclient_create_header(s, command, vrf_id); - - stream_putl(s, getpid()); - - stream_putw(s, family); - switch (family) { - case AF_INET: - stream_put_in_addr(s, (struct in_addr *)dst_ip); - break; - case AF_INET6: - stream_put(s, dst_ip, 16); - break; - default: - break; - } - - if (command != ZEBRA_BFD_DEST_DEREGISTER) { - stream_putl(s, bfd_info->required_min_rx); - stream_putl(s, bfd_info->desired_min_tx); - stream_putc(s, bfd_info->detect_mult); - } - - if (multihop) { - stream_putc(s, 1); - /* Multi-hop destination send the source IP address to BFD */ - if (src_ip) { - stream_putw(s, family); - switch (family) { - case AF_INET: - stream_put_in_addr(s, (struct in_addr *)src_ip); - break; - case AF_INET6: - stream_put(s, src_ip, 16); - break; - default: - break; - } - } - stream_putc(s, ttl); - } else { - stream_putc(s, 0); - if ((family == AF_INET6) && (src_ip)) { - stream_putw(s, family); - stream_put(s, src_ip, 16); - } - if (if_name) { - len = strlen(if_name); - stream_putc(s, len); - stream_put(s, if_name, len); - } else { - stream_putc(s, 0); + /* Fill in all arguments. */ + args.ttl = ttl; + args.cbit = cbit; + args.family = family; + args.mhop = multihop; + args.vrf_id = vrf_id; + args.command = command; + args.set_flag = set_flag; + args.bfd_info = bfd_info; + if (args.bfd_info) { + args.min_rx = bfd_info->required_min_rx; + args.min_tx = bfd_info->desired_min_tx; + args.detection_multiplier = bfd_info->detect_mult; + if (bfd_info->profile[0]) { + args.profilelen = strlen(bfd_info->profile); + strlcpy(args.profile, bfd_info->profile, + sizeof(args.profile)); } } - stream_putw_at(s, 0, stream_get_endp(s)); - - ret = zclient_send_message(zclient); + addrlen = family == AF_INET ? sizeof(struct in_addr) + : sizeof(struct in6_addr); + memcpy(&args.dst, dst_ip, addrlen); + if (src_ip) + memcpy(&args.src, src_ip, addrlen); - if (ret < 0) { - if (bfd_debug) - zlog_debug( - "bfd_peer_sendmsg: zclient_send_message() failed"); - return; - } + if (if_name) + args.ifnamelen = + strlcpy(args.ifname, if_name, sizeof(args.ifname)); - if (set_flag) { - if (command == ZEBRA_BFD_DEST_REGISTER) - SET_FLAG(bfd_info->flags, BFD_FLAG_BFD_REG); - else if (command == ZEBRA_BFD_DEST_DEREGISTER) - UNSET_FLAG(bfd_info->flags, BFD_FLAG_BFD_REG); - } - - return; + zclient_bfd_command(zclient, &args); } /* @@ -253,11 +216,24 @@ const char *bfd_get_command_dbg_str(int command) */ struct interface *bfd_get_peer_info(struct stream *s, struct prefix *dp, struct prefix *sp, int *status, + int *remote_cbit, vrf_id_t vrf_id) { unsigned int ifindex; struct interface *ifp = NULL; int plen; + int local_remote_cbit; + + /* + * If the ifindex lookup fails the + * rest of the data in the stream is + * not read. All examples of this function + * call immediately use the dp->family which + * is not good. Ensure we are not using + * random data + */ + memset(dp, 0, sizeof(*dp)); + memset(sp, 0, sizeof(*sp)); /* Get interface index. */ ifindex = stream_getl(s); @@ -268,8 +244,7 @@ struct interface *bfd_get_peer_info(struct stream *s, struct prefix *dp, if (ifp == NULL) { if (bfd_debug) zlog_debug( - "zebra_interface_bfd_read: " - "Can't find interface by ifindex: %d ", + "zebra_interface_bfd_read: Can't find interface by ifindex: %d ", ifindex); return NULL; } @@ -285,13 +260,15 @@ struct interface *bfd_get_peer_info(struct stream *s, struct prefix *dp, /* Get BFD status. */ *status = stream_getl(s); - if (sp) { - sp->family = stream_getc(s); + sp->family = stream_getc(s); - plen = prefix_blen(sp); - stream_get(&sp->u.prefix, s, plen); - sp->prefixlen = stream_getc(s); - } + plen = prefix_blen(sp); + stream_get(&sp->u.prefix, s, plen); + sp->prefixlen = stream_getc(s); + + local_remote_cbit = stream_getc(s); + if (remote_cbit) + *remote_cbit = local_remote_cbit; return ifp; } @@ -305,6 +282,8 @@ const char *bfd_get_status_str(int status) return "Down"; case BFD_STATUS_UP: return "Up"; + case BFD_STATUS_ADMIN_DOWN: + return "Admin Down"; case BFD_STATUS_UNKNOWN: default: return "Unknown"; @@ -319,7 +298,7 @@ static void bfd_last_update(time_t last_update, char *buf, size_t len) { time_t curr; time_t diff; - struct tm *tm; + struct tm tm; struct timeval tv; /* If no BFD satatus update has ever been received, print `never'. */ @@ -332,10 +311,10 @@ static void bfd_last_update(time_t last_update, char *buf, size_t len) monotime(&tv); curr = tv.tv_sec; diff = curr - last_update; - tm = gmtime(&diff); + gmtime_r(&diff, &tm); - snprintf(buf, len, "%d:%02d:%02d:%02d", tm->tm_yday, tm->tm_hour, - tm->tm_min, tm->tm_sec); + snprintf(buf, len, "%d:%02d:%02d:%02d", tm.tm_yday, tm.tm_hour, + tm.tm_min, tm.tm_sec); } /* @@ -366,8 +345,7 @@ void bfd_show_param(struct vty *vty, struct bfd_info *bfd_info, int bfd_tag, json_bfd); } else { vty_out(vty, - " %s%sDetect Multiplier: %d, Min Rx interval: %d," - " Min Tx interval: %d\n", + " %s%sDetect Multiplier: %d, Min Rx interval: %d, Min Tx interval: %d\n", (extra_space) ? " " : "", (bfd_tag) ? "BFD: " : " ", bfd_info->detect_mult, bfd_info->required_min_rx, bfd_info->desired_min_tx); @@ -433,7 +411,8 @@ void bfd_show_info(struct vty *vty, struct bfd_info *bfd_info, int multihop, * bfd_client_sendmsg - Format and send a client register * command to Zebra to be forwarded to BFD */ -void bfd_client_sendmsg(struct zclient *zclient, int command) +void bfd_client_sendmsg(struct zclient *zclient, int command, + vrf_id_t vrf_id) { struct stream *s; int ret; @@ -442,15 +421,14 @@ void bfd_client_sendmsg(struct zclient *zclient, int command) if (!zclient || zclient->sock < 0) { if (bfd_debug) zlog_debug( - "%s: Can't send BFD client register, Zebra client not " - "established", - __FUNCTION__); + "%s: Can't send BFD client register, Zebra client not established", + __func__); return; } s = zclient->obuf; stream_reset(s); - zclient_create_header(s, command, VRF_DEFAULT); + zclient_create_header(s, command, vrf_id); stream_putl(s, getpid()); @@ -468,3 +446,102 @@ void bfd_client_sendmsg(struct zclient *zclient, int command) return; } + +int zclient_bfd_command(struct zclient *zc, struct bfd_session_arg *args) +{ + struct stream *s; + size_t addrlen; + + /* Individual reg/dereg messages are suppressed during shutdown. */ + if (CHECK_FLAG(bfd_gbl.flags, BFD_GBL_FLAG_IN_SHUTDOWN)) { + if (bfd_debug) + zlog_debug( + "%s: Suppressing BFD peer reg/dereg messages", + __func__); + return -1; + } + + /* Check socket. */ + if (!zc || zc->sock < 0) { + if (bfd_debug) + zlog_debug("%s: zclient unavailable", __func__); + return -1; + } + + s = zc->obuf; + stream_reset(s); + + /* Create new message. */ + zclient_create_header(s, args->command, args->vrf_id); + stream_putl(s, getpid()); + + /* Encode destination address. */ + stream_putw(s, args->family); + addrlen = (args->family == AF_INET) ? sizeof(struct in_addr) + : sizeof(struct in6_addr); + stream_put(s, &args->dst, addrlen); + + /* Encode timers if this is a registration message. */ + if (args->command != ZEBRA_BFD_DEST_DEREGISTER) { + stream_putl(s, args->min_rx); + stream_putl(s, args->min_tx); + stream_putc(s, args->detection_multiplier); + } + + if (args->mhop) { + /* Multi hop indicator. */ + stream_putc(s, 1); + + /* Multi hop always sends the source address. */ + stream_putw(s, args->family); + stream_put(s, &args->src, addrlen); + + /* Send the expected TTL. */ + stream_putc(s, args->ttl); + } else { + /* Multi hop indicator. */ + stream_putc(s, 0); + + /* Single hop only sends the source address when IPv6. */ + if (args->family == AF_INET6) { + stream_putw(s, args->family); + stream_put(s, &args->src, addrlen); + } + + /* Send interface name if any. */ + stream_putc(s, args->ifnamelen); + if (args->ifnamelen) + stream_put(s, args->ifname, args->ifnamelen); + } + + /* Send the C bit indicator. */ + stream_putc(s, args->cbit); + + /* `ptm-bfd` doesn't support profiles yet. */ +#if HAVE_BFDD > 0 + /* Send profile name if any. */ + stream_putc(s, args->profilelen); + if (args->profilelen) + stream_put(s, args->profile, args->profilelen); +#endif /* HAVE_BFDD */ + + /* Finish the message by writing the size. */ + stream_putw_at(s, 0, stream_get_endp(s)); + + /* Send message to zebra. */ + if (zclient_send_message(zc) == -1) { + if (bfd_debug) + zlog_debug("%s: zclient_send_message failed", __func__); + return -1; + } + + /* Write registration indicator into data structure. */ + if (args->bfd_info && args->set_flag) { + if (args->command == ZEBRA_BFD_DEST_REGISTER) + SET_FLAG(args->bfd_info->flags, BFD_FLAG_BFD_REG); + else if (args->command == ZEBRA_BFD_DEST_DEREGISTER) + UNSET_FLAG(args->bfd_info->flags, BFD_FLAG_BFD_REG); + } + + return 0; +} diff --git a/lib/bfd.h b/lib/bfd.h index a93875c4cf..ceab4628b6 100644 --- a/lib/bfd.h +++ b/lib/bfd.h @@ -48,10 +48,22 @@ struct bfd_gbl { #define BFD_FLAG_PARAM_CFG (1 << 0) /* parameters have been configured */ #define BFD_FLAG_BFD_REG (1 << 1) /* Peer registered with BFD */ #define BFD_FLAG_BFD_TYPE_MULTIHOP (1 << 2) /* Peer registered with BFD as multihop */ +#define BFD_FLAG_BFD_CBIT_ON (1 << 3) /* Peer registered with CBIT set to on */ +#define BFD_FLAG_BFD_CHECK_CONTROLPLANE (1 << 4) /* BFD and controlplane daemon are linked */ -#define BFD_STATUS_UNKNOWN (1 << 0) /* BFD session status never received */ -#define BFD_STATUS_DOWN (1 << 1) /* BFD session status is down */ -#define BFD_STATUS_UP (1 << 2) /* BFD session status is up */ +#define BFD_STATUS_UNKNOWN (1 << 0) /* BFD session status never received */ +#define BFD_STATUS_DOWN (1 << 1) /* BFD session status is down */ +#define BFD_STATUS_UP (1 << 2) /* BFD session status is up */ +#define BFD_STATUS_ADMIN_DOWN (1 << 3) /* BFD session is admin down */ + +#define BFD_PROFILE_NAME_LEN 64 + +#define BFD_SET_CLIENT_STATUS(current_status, new_status) \ + do { \ + (current_status) = \ + (((new_status) == BFD_STATUS_ADMIN_DOWN) ? \ + BFD_STATUS_DOWN : (new_status));\ + } while (0) enum bfd_sess_type { BFD_TYPE_NOT_CONFIGURED, @@ -67,6 +79,7 @@ struct bfd_info { time_t last_update; uint8_t status; enum bfd_sess_type type; + char profile[BFD_PROFILE_NAME_LEN]; }; extern struct bfd_info *bfd_info_create(void); @@ -79,17 +92,18 @@ extern int bfd_validate_param(struct vty *vty, const char *dm_str, uint32_t *tx_val); extern void bfd_set_param(struct bfd_info **bfd_info, uint32_t min_rx, - uint32_t min_tx, uint8_t detect_mult, int defaults, - int *command); + uint32_t min_tx, uint8_t detect_mult, + const char *profile, int defaults, int *command); extern void bfd_peer_sendmsg(struct zclient *zclient, struct bfd_info *bfd_info, int family, void *dst_ip, void *src_ip, - char *if_name, int ttl, int multihop, int command, - int set_flag, vrf_id_t vrf_id); + char *if_name, int ttl, int multihop, int cbit, + int command, int set_flag, vrf_id_t vrf_id); extern const char *bfd_get_command_dbg_str(int command); extern struct interface *bfd_get_peer_info(struct stream *s, struct prefix *dp, struct prefix *sp, int *status, + int *remote_cbit, vrf_id_t vrf_id); const char *bfd_get_status_str(int status); @@ -102,12 +116,95 @@ extern void bfd_show_info(struct vty *vty, struct bfd_info *bfd_info, int multihop, int extra_space, bool use_json, json_object *json_obj); -extern void bfd_client_sendmsg(struct zclient *zclient, int command); +extern void bfd_client_sendmsg(struct zclient *zclient, int command, + vrf_id_t vrf_id); extern void bfd_gbl_init(void); extern void bfd_gbl_exit(void); + +/* + * BFD new API. + */ + +/** + * BFD session registration arguments. + */ +struct bfd_session_arg { + /** + * BFD command. + * + * Valid commands: + * - `ZEBRA_BFD_DEST_REGISTER` + * - `ZEBRA_BFD_DEST_DEREGISTER` + */ + int32_t command; + + /** + * BFD family type. + * + * Supported types: + * - `AF_INET` + * - `AF_INET6`. + */ + uint32_t family; + /** Source address. */ + struct in6_addr src; + /** Source address. */ + struct in6_addr dst; + + /** Multi hop indicator. */ + uint8_t mhop; + /** Expected TTL. */ + uint8_t ttl; + /** C bit (Control Plane Independent bit) indicator. */ + uint8_t cbit; + + /** Interface name size. */ + uint8_t ifnamelen; + /** Interface name. */ + char ifname[64]; + + /** Daemon or session VRF. */ + vrf_id_t vrf_id; + + /** Profile name length. */ + uint8_t profilelen; + /** Profile name. */ + char profile[BFD_PROFILE_NAME_LEN]; + + /* + * Deprecation fields: these fields should be removed once `ptm-bfd` + * no longer uses this interface. + */ + + /** Minimum required receive interval (in microseconds). */ + uint32_t min_rx; + /** Minimum desired transmission interval (in microseconds). */ + uint32_t min_tx; + /** Detection multiplier. */ + uint32_t detection_multiplier; + + /** BFD client information output. */ + struct bfd_info *bfd_info; + + /** Write registration indicator. */ + uint8_t set_flag; +}; + +/** + * Send a message to BFD daemon through the zebra client. + * + * \param zc the zebra client context. + * \param arg the BFD session command arguments. + * + * \returns `-1` on failure otherwise `0`. + * + * \see bfd_session_arg. + */ +extern int zclient_bfd_command(struct zclient *zc, struct bfd_session_arg *arg); + #ifdef __cplusplus } #endif diff --git a/lib/bitfield.h b/lib/bitfield.h index eebfc049d9..244938933b 100644 --- a/lib/bitfield.h +++ b/lib/bitfield.h @@ -58,7 +58,7 @@ typedef unsigned int word_t; * @n: The current word number that is being used. * @m: total number of words in 'data' */ -#define bitfield_t struct { word_t *data; size_t n, m; } +typedef struct {word_t *data; size_t n, m; } bitfield_t; /** * Initialize the bits. @@ -97,6 +97,16 @@ typedef unsigned int word_t; #define bf_release_index(v, id) \ (v).data[bf_index(id)] &= ~(1 << (bf_offset(id))) +/* check if an id is in use */ +#define bf_test_index(v, id) \ + ((v).data[bf_index(id)] & (1 << (bf_offset(id)))) + +/* check if the bit field has been setup */ +#define bf_is_inited(v) ((v).data) + +/* compare two bitmaps of the same length */ +#define bf_cmp(v1, v2) (memcmp((v1).data, (v2).data, ((v1).m * sizeof(word_t)))) + /* * return 0th index back to bitfield */ @@ -146,15 +156,45 @@ typedef unsigned int word_t; (b) += (w * WORD_SIZE); \ } while (0) +static inline unsigned int bf_find_next_set_bit(bitfield_t v, + word_t start_index) +{ + int start_bit; + unsigned long i, offset; + + start_bit = start_index & (WORD_SIZE - 1); + + for (i = bf_index(start_index); i < v.m; ++i) { + if (v.data[i] == 0) { + /* if the whole word is empty move to the next */ + start_bit = 0; + continue; + } + /* scan one word for set bits */ + for (offset = start_bit; offset < WORD_SIZE; ++offset) { + if ((v.data[i] >> offset) & 1) + return ((i * WORD_SIZE) + offset); + } + /* move to the next word */ + start_bit = 0; + } + return WORD_MAX; +} + +/* iterate through all the set bits */ +#define bf_for_each_set_bit(v, b, max) \ + for ((b) = bf_find_next_set_bit((v), 0); \ + (b) < max; \ + (b) = bf_find_next_set_bit((v), (b) + 1)) + /* * Free the allocated memory for data * @v: an instance of bitfield_t struct. */ #define bf_free(v) \ do { \ - if ((v).data) { \ - free((v).data); \ - } \ + free((v).data); \ + (v).data = NULL; \ } while (0) #ifdef __cplusplus diff --git a/lib/buffer.c b/lib/buffer.c index bb2cdb7e54..459d98e75d 100644 --- a/lib/buffer.c +++ b/lib/buffer.c @@ -114,12 +114,6 @@ char *buffer_getstr(struct buffer *b) return s; } -/* Return 1 if buffer is empty. */ -int buffer_empty(struct buffer *b) -{ - return (b->head == NULL); -} - /* Clear and free all allocated data. */ void buffer_reset(struct buffer *b) { @@ -294,7 +288,7 @@ buffer_status_t buffer_flush_window(struct buffer *b, int fd, int width, /* Previously print out is performed. */ if (erase_flag) { iov[iov_index].iov_base = erase; - iov[iov_index].iov_len = sizeof erase; + iov[iov_index].iov_len = sizeof(erase); iov_index++; } @@ -333,8 +327,7 @@ buffer_status_t buffer_flush_window(struct buffer *b, int fd, int width, /* This should absolutely never occur. */ flog_err_sys( EC_LIB_SYSTEM_CALL, - "%s: corruption detected: iov_small overflowed; " - "head %p, tail %p, head->next %p", + "%s: corruption detected: iov_small overflowed; head %p, tail %p, head->next %p", __func__, (void *)b->head, (void *)b->tail, (void *)b->head->next); iov = XMALLOC(MTYPE_TMP, @@ -347,7 +340,7 @@ buffer_status_t buffer_flush_window(struct buffer *b, int fd, int width, /* In case of `more' display need. */ if (b->tail && (b->tail->sp < b->tail->cp) && !no_more_flag) { iov[iov_index].iov_base = more; - iov[iov_index].iov_len = sizeof more; + iov[iov_index].iov_len = sizeof(more); iov_index++; } diff --git a/lib/checksum.c b/lib/checksum.c index 18e3850474..3473370041 100644 --- a/lib/checksum.c +++ b/lib/checksum.c @@ -46,6 +46,24 @@ int /* return checksum in low-order 16 bits */ return (answer); } +int in_cksum_with_ph4(struct ipv4_ph *ph, void *data, int nbytes) +{ + uint8_t dat[sizeof(struct ipv4_ph) + nbytes]; + + memcpy(dat, ph, sizeof(struct ipv4_ph)); + memcpy(dat + sizeof(struct ipv4_ph), data, nbytes); + return in_cksum(dat, sizeof(dat)); +} + +int in_cksum_with_ph6(struct ipv6_ph *ph, void *data, int nbytes) +{ + uint8_t dat[sizeof(struct ipv6_ph) + nbytes]; + + memcpy(dat, ph, sizeof(struct ipv6_ph)); + memcpy(dat + sizeof(struct ipv6_ph), data, nbytes); + return in_cksum(dat, sizeof(dat)); +} + /* Fletcher Checksum -- Refer to RFC1008. */ #define MODX 4102U /* 5802 should be fine */ diff --git a/lib/checksum.h b/lib/checksum.h index 7d50371439..56771d4f24 100644 --- a/lib/checksum.h +++ b/lib/checksum.h @@ -1,8 +1,33 @@ +#include +#include + #ifdef __cplusplus extern "C" { #endif -extern int in_cksum(void *, int); + +/* IPv4 pseudoheader */ +struct ipv4_ph { + struct in_addr src; + struct in_addr dst; + uint8_t rsvd; + uint8_t proto; + uint16_t len; +} __attribute__((packed)); + +/* IPv6 pseudoheader */ +struct ipv6_ph { + struct in6_addr src; + struct in6_addr dst; + uint32_t ulpl; + uint8_t zero[3]; + uint8_t next_hdr; +} __attribute__((packed)); + +extern int in_cksum(void *data, int nbytes); +extern int in_cksum_with_ph4(struct ipv4_ph *ph, void *data, int nbytes); +extern int in_cksum_with_ph6(struct ipv6_ph *ph, void *data, int nbytes); + #define FLETCHER_CHECKSUM_VALIDATE 0xffff extern uint16_t fletcher_checksum(uint8_t *, const size_t len, const uint16_t offset); diff --git a/lib/clippy.c b/lib/clippy.c index 44dcc02eb8..b67662880e 100644 --- a/lib/clippy.c +++ b/lib/clippy.c @@ -51,7 +51,8 @@ int main(int argc, char **argv) #if PY_VERSION_HEX >= 0x03040000 /* 3.4 */ Py_SetStandardStreamEncoding("UTF-8", NULL); #endif - Py_SetProgramName(wconv(argv[0])); + wchar_t *name = wconv(argv[0]); + Py_SetProgramName(name); PyImport_AppendInittab("_clippy", command_py_init); Py_Initialize(); @@ -67,6 +68,8 @@ int main(int argc, char **argv) fp = fopen(pyfile, "r"); if (!fp) { fprintf(stderr, "%s: %s\n", pyfile, strerror(errno)); + + free(name); return 1; } } else { @@ -85,8 +88,8 @@ int main(int argc, char **argv) if (PyRun_AnyFile(fp, pyfile)) { if (PyErr_Occurred()) PyErr_Print(); - else - printf("unknown python failure (?)\n"); + + free(name); return 1; } Py_Finalize(); @@ -95,6 +98,7 @@ int main(int argc, char **argv) for (int i = 1; i < argc; i++) free(wargv[i - 1]); #endif + free(name); free(wargv); return 0; } @@ -109,21 +113,11 @@ int main(int argc, char **argv) #include "log.h" #include "zassert.h" -#define ZLOG_FUNC(FUNCNAME) \ - void FUNCNAME(const char *format, ...) \ - { \ - va_list args; \ - va_start(args, format); \ - vfprintf(stderr, format, args); \ - fputs("\n", stderr); \ - va_end(args); \ - } - -ZLOG_FUNC(zlog_err) -ZLOG_FUNC(zlog_warn) -ZLOG_FUNC(zlog_info) -ZLOG_FUNC(zlog_notice) -ZLOG_FUNC(zlog_debug) +void vzlog(int prio, const char *format, va_list args) +{ + vfprintf(stderr, format, args); + fputs("\n", stderr); +} void _zlog_assert_failed(const char *assertion, const char *file, unsigned int line, const char *function) diff --git a/lib/command.c b/lib/command.c index 559457c119..5bd1e03fc6 100644 --- a/lib/command.c +++ b/lib/command.c @@ -31,7 +31,7 @@ #include "frrstr.h" #include "memory.h" #include "log.h" -#include "log_int.h" +#include "log_vty.h" #include "thread.h" #include "vector.h" #include "linklist.h" @@ -47,8 +47,9 @@ #include "hook.h" #include "lib_errors.h" #include "northbound_cli.h" +#include "network.h" -DEFINE_MTYPE(LIB, HOST, "Host config") +DEFINE_MTYPE_STATIC(LIB, HOST, "Host config") DEFINE_MTYPE(LIB, COMPLETION, "Completion item") #define item(x) \ @@ -73,83 +74,6 @@ const struct message tokennames[] = { item(END_TKN), {0}, }; - -const char *node_names[] = { - "auth", // AUTH_NODE, - "view", // VIEW_NODE, - "auth enable", // AUTH_ENABLE_NODE, - "enable", // ENABLE_NODE, - "config", // CONFIG_NODE, - "debug", // DEBUG_NODE, - "vrf debug", // VRF_DEBUG_NODE, - "northbound debug", // NORTHBOUND_DEBUG_NODE, - "vnc debug", // DEBUG_VNC_NODE, - "aaa", // AAA_NODE, - "keychain", // KEYCHAIN_NODE, - "keychain key", // KEYCHAIN_KEY_NODE, - "logical-router", // LOGICALROUTER_NODE, - "static ip", // IP_NODE, - "vrf", // VRF_NODE, - "interface", // INTERFACE_NODE, - "nexthop-group", // NH_GROUP_NODE, - "zebra", // ZEBRA_NODE, - "table", // TABLE_NODE, - "rip", // RIP_NODE, - "ripng", // RIPNG_NODE, - "babel", // BABEL_NODE, - "eigrp", // EIGRP_NODE, - "bgp", // BGP_NODE, - "bgp vpnv4", // BGP_VPNV4_NODE, - "bgp vpnv6", // BGP_VPNV6_NODE, - "bgp ipv4 unicast", // BGP_IPV4_NODE, - "bgp ipv4 multicast", // BGP_IPV4M_NODE, - "bgp ipv4 labeled unicast", // BGP_IPV4L_NODE, - "bgp ipv6", // BGP_IPV6_NODE, - "bgp ipv6 multicast", // BGP_IPV6M_NODE, - "bgp ipv6 labeled unicast", // BGP_IPV6L_NODE, - "bgp vrf policy", // BGP_VRF_POLICY_NODE, - "bgp vnc defaults", // BGP_VNC_DEFAULTS_NODE, - "bgp vnc nve", // BGP_VNC_NVE_GROUP_NODE, - "bgp vnc l2", // BGP_VNC_L2_GROUP_NODE, - "rfp defaults", // RFP_DEFAULTS_NODE, - "bgp evpn", // BGP_EVPN_NODE, - "ospf", // OSPF_NODE, - "ospf6", // OSPF6_NODE, - "ldp", // LDP_NODE, - "ldp ipv4", // LDP_IPV4_NODE, - "ldp ipv6", // LDP_IPV6_NODE, - "ldp ipv4 interface", // LDP_IPV4_IFACE_NODE, - "ldp ipv6 interface", // LDP_IPV6_IFACE_NODE, - "ldp l2vpn", // LDP_L2VPN_NODE, - "ldp", // LDP_PSEUDOWIRE_NODE, - "isis", // ISIS_NODE, - "ipv4 access list", // ACCESS_NODE, - "ipv4 prefix list", // PREFIX_NODE, - "ipv6 access list", // ACCESS_IPV6_NODE, - "MAC access list", // ACCESS_MAC_NODE, - "ipv6 prefix list", // PREFIX_IPV6_NODE, - "as list", // AS_LIST_NODE, - "community list", // COMMUNITY_LIST_NODE, - "routemap", // RMAP_NODE, - "pbr-map", // PBRMAP_NODE, - "smux", // SMUX_NODE, - "dump", // DUMP_NODE, - "forwarding", // FORWARDING_NODE, - "protocol", // PROTOCOL_NODE, - "mpls", // MPLS_NODE, - "pw", // PW_NODE, - "vty", // VTY_NODE, - "link-params", // LINK_PARAMS_NODE, - "bgp evpn vni", // BGP_EVPN_VNI_NODE, - "rpki", // RPKI_NODE - "bgp ipv4 flowspec", /* BGP_FLOWSPECV4_NODE - */ - "bgp ipv6 flowspec", /* BGP_FLOWSPECV6_NODE - */ - "bfd", /* BFD_NODE */ - "bfd peer", /* BFD_PEER_NODE */ - "openfabric", // OPENFABRIC_NODE -}; /* clang-format on */ /* Command vector which includes some level of command lists. Normally @@ -176,93 +100,53 @@ const char *cmd_domainname_get(void) return host.domainname; } +static int root_on_exit(struct vty *vty); + /* Standard command node structures. */ static struct cmd_node auth_node = { - AUTH_NODE, "Password: ", + .name = "auth", + .node = AUTH_NODE, + .prompt = "Password: ", }; static struct cmd_node view_node = { - VIEW_NODE, "%s> ", + .name = "view", + .node = VIEW_NODE, + .prompt = "%s> ", + .node_exit = root_on_exit, }; static struct cmd_node auth_enable_node = { - AUTH_ENABLE_NODE, "Password: ", + .name = "auth enable", + .node = AUTH_ENABLE_NODE, + .prompt = "Password: ", }; static struct cmd_node enable_node = { - ENABLE_NODE, "%s# ", + .name = "enable", + .node = ENABLE_NODE, + .prompt = "%s# ", + .node_exit = root_on_exit, }; -static struct cmd_node config_node = {CONFIG_NODE, "%s(config)# ", 1}; - -/* Default motd string. */ -static const char *default_motd = FRR_DEFAULT_MOTD; - -static const struct facility_map { - int facility; - const char *name; - size_t match; -} syslog_facilities[] = { - {LOG_KERN, "kern", 1}, - {LOG_USER, "user", 2}, - {LOG_MAIL, "mail", 1}, - {LOG_DAEMON, "daemon", 1}, - {LOG_AUTH, "auth", 1}, - {LOG_SYSLOG, "syslog", 1}, - {LOG_LPR, "lpr", 2}, - {LOG_NEWS, "news", 1}, - {LOG_UUCP, "uucp", 2}, - {LOG_CRON, "cron", 1}, -#ifdef LOG_FTP - {LOG_FTP, "ftp", 1}, -#endif - {LOG_LOCAL0, "local0", 6}, - {LOG_LOCAL1, "local1", 6}, - {LOG_LOCAL2, "local2", 6}, - {LOG_LOCAL3, "local3", 6}, - {LOG_LOCAL4, "local4", 6}, - {LOG_LOCAL5, "local5", 6}, - {LOG_LOCAL6, "local6", 6}, - {LOG_LOCAL7, "local7", 6}, - {0, NULL, 0}, +static int config_write_host(struct vty *vty); +static struct cmd_node config_node = { + .name = "config", + .node = CONFIG_NODE, + .parent_node = ENABLE_NODE, + .prompt = "%s(config)# ", + .config_write = config_write_host, + .node_exit = vty_config_node_exit, }; -static const char *facility_name(int facility) -{ - const struct facility_map *fm; - - for (fm = syslog_facilities; fm->name; fm++) - if (fm->facility == facility) - return fm->name; - return ""; -} - -static int facility_match(const char *str) -{ - const struct facility_map *fm; - - for (fm = syslog_facilities; fm->name; fm++) - if (!strncmp(str, fm->name, fm->match)) - return fm->facility; - return -1; -} - -static int level_match(const char *s) -{ - int level; - - for (level = 0; zlog_priority[level] != NULL; level++) - if (!strncmp(s, zlog_priority[level], 2)) - return level; - return ZLOG_DISABLED; -} - /* This is called from main when a daemon is invoked with -v or --version. */ void print_version(const char *progname) { printf("%s version %s\n", progname, FRR_VERSION); printf("%s\n", FRR_COPYRIGHT); +#ifdef ENABLE_VERSION_BUILD_CONFIG printf("configured with:\n\t%s\n", FRR_CONFIG_ARGS); +#endif } char *argv_concat(struct cmd_token **argv, int argc, int shift) @@ -287,7 +171,7 @@ vector cmd_make_strvec(const char *string) const char *copy = string; /* skip leading whitespace */ - while (isspace((int)*copy) && *copy != '\0') + while (isspace((unsigned char)*copy) && *copy != '\0') copy++; /* if the entire string was whitespace or a comment, return */ @@ -332,7 +216,7 @@ int argv_find(struct cmd_token **argv, int argc, const char *text, int *index) return found; } -static unsigned int cmd_hash_key(void *p) +static unsigned int cmd_hash_key(const void *p) { int size = sizeof(p); @@ -345,10 +229,9 @@ static bool cmd_hash_cmp(const void *a, const void *b) } /* Install top node of command vector. */ -void install_node(struct cmd_node *node, int (*func)(struct vty *)) +void install_node(struct cmd_node *node) { vector_set_index(cmdvec, node->node, node); - node->func = func; node->cmdgraph = graph_new(); node->cmd_vector = vector_init(VECTOR_MIN_SIZE); // add start node @@ -370,7 +253,7 @@ const char *cmd_prompt(enum node_type node) } /* Install a command into a node. */ -void install_element(enum node_type ntype, struct cmd_element *cmd) +void install_element(enum node_type ntype, const struct cmd_element *cmd) { struct cmd_node *cnode; @@ -386,22 +269,22 @@ void install_element(enum node_type ntype, struct cmd_element *cmd) if (cnode == NULL) { fprintf(stderr, "%s[%s]:\n" - "\tnode %d (%s) does not exist.\n" + "\tnode %d does not exist.\n" "\tplease call install_node() before install_element()\n", - cmd->name, cmd->string, ntype, node_names[ntype]); + cmd->name, cmd->string, ntype); exit(EXIT_FAILURE); } - if (hash_lookup(cnode->cmd_hash, cmd) != NULL) { + if (hash_lookup(cnode->cmd_hash, (void *)cmd) != NULL) { fprintf(stderr, "%s[%s]:\n" "\tnode %d (%s) already has this command installed.\n" "\tduplicate install_element call?\n", - cmd->name, cmd->string, ntype, node_names[ntype]); + cmd->name, cmd->string, ntype, cnode->name); return; } - assert(hash_get(cnode->cmd_hash, cmd, hash_alloc_intern)); + assert(hash_get(cnode->cmd_hash, (void *)cmd, hash_alloc_intern)); struct graph *graph = graph_new(); struct cmd_token *token = @@ -413,13 +296,13 @@ void install_element(enum node_type ntype, struct cmd_element *cmd) cmd_graph_merge(cnode->cmdgraph, graph, +1); graph_delete_graph(graph); - vector_set(cnode->cmd_vector, cmd); + vector_set(cnode->cmd_vector, (void *)cmd); if (ntype == VIEW_NODE) install_element(ENABLE_NODE, cmd); } -void uninstall_element(enum node_type ntype, struct cmd_element *cmd) +void uninstall_element(enum node_type ntype, const struct cmd_element *cmd) { struct cmd_node *cnode; @@ -435,22 +318,22 @@ void uninstall_element(enum node_type ntype, struct cmd_element *cmd) if (cnode == NULL) { fprintf(stderr, "%s[%s]:\n" - "\tnode %d (%s) does not exist.\n" + "\tnode %d does not exist.\n" "\tplease call install_node() before uninstall_element()\n", - cmd->name, cmd->string, ntype, node_names[ntype]); + cmd->name, cmd->string, ntype); exit(EXIT_FAILURE); } - if (hash_release(cnode->cmd_hash, cmd) == NULL) { + if (hash_release(cnode->cmd_hash, (void *)cmd) == NULL) { fprintf(stderr, "%s[%s]:\n" "\tnode %d (%s) does not have this command installed.\n" "\tduplicate uninstall_element call?\n", - cmd->name, cmd->string, ntype, node_names[ntype]); + cmd->name, cmd->string, ntype, cnode->name); return; } - vector_unset_value(cnode->cmd_vector, cmd); + vector_unset_value(cnode->cmd_vector, (void *)cmd); struct graph *graph = graph_new(); struct cmd_token *token = @@ -486,13 +369,15 @@ static char *zencrypt(const char *passwd) gettimeofday(&tv, 0); - to64(&salt[0], random(), 3); + to64(&salt[0], frr_weak_random(), 3); to64(&salt[3], tv.tv_usec, 3); salt[5] = '\0'; return crypt(passwd, salt); } +static bool full_cli; + /* This function write configuration of this host. */ static int config_write_host(struct vty *vty) { @@ -508,7 +393,7 @@ static int config_write_host(struct vty *vty) * which would cause other daemons to then switch to syslog when they * parse frr.conf. */ - if (strcmp(zlog_default->protoname, "WATCHFRR")) { + if (full_cli) { if (host.encrypt) { if (host.password_encrypt) vty_out(vty, "password 8 %s\n", @@ -523,59 +408,7 @@ static int config_write_host(struct vty *vty) vty_out(vty, "enable password %s\n", host.enable); } - - if (host.logfile - && (zlog_default->maxlvl[ZLOG_DEST_FILE] - != ZLOG_DISABLED)) { - vty_out(vty, "log file %s", host.logfile); - if (zlog_default->maxlvl[ZLOG_DEST_FILE] - != zlog_default->default_lvl) - vty_out(vty, " %s", - zlog_priority - [zlog_default->maxlvl - [ZLOG_DEST_FILE]]); - vty_out(vty, "\n"); - } - - if (zlog_default->maxlvl[ZLOG_DEST_STDOUT] != ZLOG_DISABLED) { - vty_out(vty, "log stdout"); - if (zlog_default->maxlvl[ZLOG_DEST_STDOUT] - != zlog_default->default_lvl) - vty_out(vty, " %s", - zlog_priority - [zlog_default->maxlvl - [ZLOG_DEST_STDOUT]]); - vty_out(vty, "\n"); - } - - if (zlog_default->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED) - vty_out(vty, "no log monitor\n"); - else if (zlog_default->maxlvl[ZLOG_DEST_MONITOR] - != zlog_default->default_lvl) - vty_out(vty, "log monitor %s\n", - zlog_priority[zlog_default->maxlvl - [ZLOG_DEST_MONITOR]]); - - if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != ZLOG_DISABLED) { - vty_out(vty, "log syslog"); - if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] - != zlog_default->default_lvl) - vty_out(vty, " %s", - zlog_priority[zlog_default->maxlvl - [ZLOG_DEST_SYSLOG]]); - vty_out(vty, "\n"); - } - - if (zlog_default->facility != LOG_DAEMON) - vty_out(vty, "log facility %s\n", - facility_name(zlog_default->facility)); - - if (zlog_default->record_priority == 1) - vty_out(vty, "log record-priority\n"); - - if (zlog_default->timestamp_precision > 0) - vty_out(vty, "log timestamp precision %d\n", - zlog_default->timestamp_precision); + log_config_write(vty); if (host.advanced) vty_out(vty, "service advanced-vty\n"); @@ -589,6 +422,10 @@ static int config_write_host(struct vty *vty) if (host.motdfile) vty_out(vty, "banner motd file %s\n", host.motdfile); + else if (host.motd + && strncmp(host.motd, FRR_DEFAULT_MOTD, + strlen(host.motd))) + vty_out(vty, "banner motd line %s\n", host.motd); else if (!host.motd) vty_out(vty, "no banner motd\n"); } @@ -973,6 +810,7 @@ enum node_type node_parent(enum node_type node) case BGP_IPV6M_NODE: case BGP_EVPN_NODE: case BGP_IPV6L_NODE: + case BMP_NODE: ret = BGP_NODE; break; case BGP_EVPN_VNI_NODE: @@ -1000,6 +838,9 @@ enum node_type node_parent(enum node_type node) case BFD_PEER_NODE: ret = BFD_NODE; break; + case BFD_PROFILE_NODE: + ret = BFD_NODE; + break; default: ret = CONFIG_NODE; break; @@ -1011,13 +852,30 @@ enum node_type node_parent(enum node_type node) /* Execute command by argument vline vector. */ static int cmd_execute_command_real(vector vline, enum cmd_filter_type filter, struct vty *vty, - const struct cmd_element **cmd) + const struct cmd_element **cmd, + unsigned int up_level) { struct list *argv_list; enum matcher_rv status; const struct cmd_element *matched_element = NULL; + unsigned int i; + int xpath_index = vty->xpath_index; + int node = vty->node; - struct graph *cmdgraph = cmd_node_graph(cmdvec, vty->node); + /* only happens for legacy split config file load; need to check for + * a match before calling node_exit handlers below + */ + for (i = 0; i < up_level; i++) { + if (node <= CONFIG_NODE) + return CMD_NO_LEVEL_UP; + + node = node_parent(node); + + if (xpath_index > 0) + xpath_index--; + } + + struct graph *cmdgraph = cmd_node_graph(cmdvec, node); status = command_match(cmdgraph, vline, &argv_list, &matched_element); if (cmd) @@ -1037,12 +895,16 @@ static int cmd_execute_command_real(vector vline, enum cmd_filter_type filter, } } + for (i = 0; i < up_level; i++) + cmd_exit(vty); + // build argv array from argv list struct cmd_token **argv = XMALLOC( MTYPE_TMP, argv_list->count * sizeof(struct cmd_token *)); struct listnode *ln; struct cmd_token *token; - unsigned int i = 0; + + i = 0; for (ALL_LIST_ELEMENTS_RO(argv_list, ln, token)) argv[i++] = token; @@ -1052,9 +914,25 @@ static int cmd_execute_command_real(vector vline, enum cmd_filter_type filter, if (matched_element->daemon) ret = CMD_SUCCESS_DAEMON; else { - /* Clear enqueued configuration changes. */ - vty->num_cfg_changes = 0; - memset(&vty->cfg_changes, 0, sizeof(vty->cfg_changes)); + if (vty->config) { + /* Clear array of enqueued configuration changes. */ + vty->num_cfg_changes = 0; + memset(&vty->cfg_changes, 0, sizeof(vty->cfg_changes)); + + /* Regenerate candidate configuration if necessary. */ + if (frr_get_cli_mode() == FRR_CLI_CLASSIC + && running_config->version + > vty->candidate_config->version) + nb_config_replace(vty->candidate_config, + running_config, true); + + /* + * Perform pending commit (if any) before executing + * non-YANG command. + */ + if (matched_element->attr != CMD_ATTR_YANG) + nb_cli_pending_commit_check(vty); + } ret = matched_element->func(matched_element, vty, argc, argv); } @@ -1107,7 +985,7 @@ int cmd_execute_command(vector vline, struct vty *vty, vector_lookup(vline, index)); ret = cmd_execute_command_real(shifted_vline, FILTER_RELAXED, - vty, cmd); + vty, cmd, 0); vector_free(shifted_vline); vty->node = onode; @@ -1116,7 +994,7 @@ int cmd_execute_command(vector vline, struct vty *vty, } saved_ret = ret = - cmd_execute_command_real(vline, FILTER_RELAXED, vty, cmd); + cmd_execute_command_real(vline, FILTER_RELAXED, vty, cmd, 0); if (vtysh) return saved_ret; @@ -1131,7 +1009,7 @@ int cmd_execute_command(vector vline, struct vty *vty, if (vty->xpath_index > 0) vty->xpath_index--; ret = cmd_execute_command_real(vline, FILTER_RELAXED, - vty, cmd); + vty, cmd, 0); if (ret == CMD_SUCCESS || ret == CMD_WARNING || ret == CMD_NOT_MY_INSTANCE || ret == CMD_WARNING_CONFIG_FAILED) @@ -1162,7 +1040,7 @@ int cmd_execute_command(vector vline, struct vty *vty, int cmd_execute_command_strict(vector vline, struct vty *vty, const struct cmd_element **cmd) { - return cmd_execute_command_real(vline, FILTER_STRICT, vty, cmd); + return cmd_execute_command_real(vline, FILTER_STRICT, vty, cmd, 0); } /* @@ -1207,6 +1085,7 @@ static int handle_pipe_action(struct vty *vty, const char *cmd_in, /* look for `|` */ char *orig, *working, *token, *u; char *pipe = strstr(cmd_in, "| "); + int ret = 0; if (!pipe) return 0; @@ -1225,6 +1104,7 @@ static int handle_pipe_action(struct vty *vty, const char *cmd_in, if (!regexp) { vty_out(vty, "%% Need a regexp to filter with\n"); + ret = 1; goto fail; } @@ -1232,6 +1112,7 @@ static int handle_pipe_action(struct vty *vty, const char *cmd_in, if (!succ) { vty_out(vty, "%% Bad regexp '%s'\n", regexp); + ret = 1; goto fail; } *cmd_out = XSTRDUP(MTYPE_TMP, cmd_in); @@ -1239,12 +1120,13 @@ static int handle_pipe_action(struct vty *vty, const char *cmd_in, strsep(&u, "|"); } else { vty_out(vty, "%% Unknown action '%s'\n", token); + ret = 1; goto fail; } fail: XFREE(MTYPE_TMP, orig); - return 0; + return ret; } static int handle_pipe_action_done(struct vty *vty, const char *cmd_exec) @@ -1260,10 +1142,15 @@ int cmd_execute(struct vty *vty, const char *cmd, { int ret; char *cmd_out = NULL; - const char *cmd_exec; + const char *cmd_exec = NULL; vector vline; - hook_call(cmd_execute, vty, cmd, &cmd_out); + ret = hook_call(cmd_execute, vty, cmd, &cmd_out); + if (ret) { + ret = CMD_WARNING; + goto free; + } + cmd_exec = cmd_out ? (const char *)cmd_out : cmd; vline = cmd_make_strvec(cmd_exec); @@ -1275,6 +1162,7 @@ int cmd_execute(struct vty *vty, const char *cmd, ret = CMD_SUCCESS; } +free: hook_call(cmd_execute_done, vty, cmd_exec); XFREE(MTYPE_TMP, cmd_out); @@ -1303,6 +1191,7 @@ int command_config_read_one_line(struct vty *vty, { vector vline; int ret; + unsigned up_level = 0; vline = cmd_make_strvec(vty->buf); @@ -1313,34 +1202,20 @@ int command_config_read_one_line(struct vty *vty, /* Execute configuration command : this is strict match */ ret = cmd_execute_command_strict(vline, vty, cmd); - // Climb the tree and try the command again at each node - if (!(use_daemon && ret == CMD_SUCCESS_DAEMON) - && !(!use_daemon && ret == CMD_ERR_NOTHING_TODO) - && ret != CMD_SUCCESS && ret != CMD_WARNING - && ret != CMD_NOT_MY_INSTANCE && ret != CMD_WARNING_CONFIG_FAILED - && vty->node != CONFIG_NODE) { - int saved_node = vty->node; - int saved_xpath_index = vty->xpath_index; - - while (!(use_daemon && ret == CMD_SUCCESS_DAEMON) - && !(!use_daemon && ret == CMD_ERR_NOTHING_TODO) - && ret != CMD_SUCCESS && ret != CMD_WARNING - && vty->node > CONFIG_NODE) { - vty->node = node_parent(vty->node); - if (vty->xpath_index > 0) - vty->xpath_index--; - ret = cmd_execute_command_strict(vline, vty, cmd); - } + /* The logic for trying parent nodes is in cmd_execute_command_real() + * since calling ->node_exit() correctly is a bit involved. This is + * also the only reason CMD_NO_LEVEL_UP exists. + */ + while (!(use_daemon && ret == CMD_SUCCESS_DAEMON) + && !(!use_daemon && ret == CMD_ERR_NOTHING_TODO) + && ret != CMD_SUCCESS && ret != CMD_WARNING + && ret != CMD_NOT_MY_INSTANCE && ret != CMD_WARNING_CONFIG_FAILED + && ret != CMD_NO_LEVEL_UP) + ret = cmd_execute_command_real(vline, FILTER_STRICT, vty, cmd, + ++up_level); - // If climbing the tree did not work then ignore the command and - // stay at the same node - if (!(use_daemon && ret == CMD_SUCCESS_DAEMON) - && !(!use_daemon && ret == CMD_ERR_NOTHING_TODO) - && ret != CMD_SUCCESS && ret != CMD_WARNING) { - vty->node = saved_node; - vty->xpath_index = saved_xpath_index; - } - } + if (ret == CMD_NO_LEVEL_UP) + ret = CMD_ERR_NO_MATCH; if (ret != CMD_SUCCESS && ret != CMD_WARNING && @@ -1386,7 +1261,7 @@ int config_from_file(struct vty *vty, FILE *fp, unsigned int *line_num) /* Configuration from terminal */ DEFUN (config_terminal, config_terminal_cmd, - "configure terminal", + "configure [terminal]", "Configuration from vty interface\n" "Configuration terminal\n") { @@ -1430,90 +1305,25 @@ DEFUN (config_exit, return CMD_SUCCESS; } +static int root_on_exit(struct vty *vty) +{ + if (vty_shell(vty)) + exit(0); + else + vty->status = VTY_CLOSE; + return 0; +} + void cmd_exit(struct vty *vty) { - switch (vty->node) { - case VIEW_NODE: - case ENABLE_NODE: - if (vty_shell(vty)) - exit(0); - else - vty->status = VTY_CLOSE; - break; - case CONFIG_NODE: - vty->node = ENABLE_NODE; - vty_config_exit(vty); - break; - case INTERFACE_NODE: - case PW_NODE: - case LOGICALROUTER_NODE: - case VRF_NODE: - case NH_GROUP_NODE: - case ZEBRA_NODE: - case BGP_NODE: - case RIP_NODE: - case EIGRP_NODE: - case BABEL_NODE: - case RIPNG_NODE: - case OSPF_NODE: - case OSPF6_NODE: - case LDP_NODE: - case LDP_L2VPN_NODE: - case ISIS_NODE: - case OPENFABRIC_NODE: - case KEYCHAIN_NODE: - case RMAP_NODE: - case PBRMAP_NODE: - case VTY_NODE: - case BFD_NODE: - vty->node = CONFIG_NODE; - break; - case BGP_IPV4_NODE: - case BGP_IPV4M_NODE: - case BGP_IPV4L_NODE: - case BGP_VPNV4_NODE: - case BGP_VPNV6_NODE: - case BGP_FLOWSPECV4_NODE: - case BGP_FLOWSPECV6_NODE: - case BGP_VRF_POLICY_NODE: - case BGP_VNC_DEFAULTS_NODE: - case BGP_VNC_NVE_GROUP_NODE: - case BGP_VNC_L2_GROUP_NODE: - case BGP_IPV6_NODE: - case BGP_IPV6M_NODE: - case BGP_EVPN_NODE: - case BGP_IPV6L_NODE: - vty->node = BGP_NODE; - break; - case BGP_EVPN_VNI_NODE: - vty->node = BGP_EVPN_NODE; - break; - case LDP_IPV4_NODE: - case LDP_IPV6_NODE: - vty->node = LDP_NODE; - break; - case LDP_IPV4_IFACE_NODE: - vty->node = LDP_IPV4_NODE; - break; - case LDP_IPV6_IFACE_NODE: - vty->node = LDP_IPV6_NODE; - break; - case LDP_PSEUDOWIRE_NODE: - vty->node = LDP_L2VPN_NODE; - break; - case KEYCHAIN_KEY_NODE: - vty->node = KEYCHAIN_NODE; - break; - case LINK_PARAMS_NODE: - vty->node = INTERFACE_NODE; - break; - case BFD_PEER_NODE: - vty->node = BFD_NODE; - break; - default: - break; - } + struct cmd_node *cnode = vector_lookup(cmdvec, vty->node); + if (cnode->node_exit) { + if (!cnode->node_exit(vty)) + return; + } + if (cnode->parent_node) + vty->node = cnode->parent_node; if (vty->xpath_index > 0) vty->xpath_index--; } @@ -1538,7 +1348,6 @@ DEFUN (config_end, vty_config_exit(vty); vty->node = ENABLE_NODE; } - return CMD_SUCCESS; } @@ -1552,20 +1361,9 @@ DEFUN (show_version, vty_out(vty, "%s %s (%s).\n", FRR_FULL_NAME, FRR_VERSION, cmd_hostname_get() ? cmd_hostname_get() : ""); vty_out(vty, "%s%s\n", FRR_COPYRIGHT, GIT_INFO); +#ifdef ENABLE_VERSION_BUILD_CONFIG vty_out(vty, "configured with:\n %s\n", FRR_CONFIG_ARGS); - - return CMD_SUCCESS; -} - -/* "Set" version ... ignore version tags */ -DEFUN (frr_version_defaults, - frr_version_defaults_cmd, - "frr LINE...", - "FRRouting global parameters\n" - "version configuration was written by\n" - "set of configuration defaults used\n" - "version string\n") -{ +#endif return CMD_SUCCESS; } @@ -1642,7 +1440,7 @@ int cmd_list_cmds(struct vty *vty, int do_permute) permute(vector_slot(node->cmdgraph->nodes, 0), vty); else { /* loop over all commands at this node */ - struct cmd_element *element = NULL; + const struct cmd_element *element = NULL; for (unsigned int i = 0; i < vector_active(node->cmd_vector); i++) if ((element = vector_slot(node->cmd_vector, i)) @@ -1696,19 +1494,22 @@ static int vty_write_config(struct vty *vty) if (host.noconfig) return CMD_SUCCESS; + nb_cli_show_config_prepare(running_config, false); + if (vty->type == VTY_TERM) { vty_out(vty, "\nCurrent configuration:\n"); vty_out(vty, "!\n"); } + if (strcmp(frr_defaults_version(), FRR_VER_SHORT)) + vty_out(vty, "! loaded from %s\n", frr_defaults_version()); vty_out(vty, "frr version %s\n", FRR_VER_SHORT); - vty_out(vty, "frr defaults %s\n", DFLT_NAME); + vty_out(vty, "frr defaults %s\n", frr_defaults_profile()); vty_out(vty, "!\n"); for (i = 0; i < vector_active(cmdvec); i++) - if ((node = vector_slot(cmdvec, i)) && node->func - && (node->vtysh || vty->type != VTY_SHELL)) { - if ((*node->func)(vty)) + if ((node = vector_slot(cmdvec, i)) && node->config_write) { + if ((*node->config_write)(vty)) vty_out(vty, "!\n"); } @@ -1755,14 +1556,15 @@ static int file_write_config(struct vty *vty) dirfd = open(".", O_DIRECTORY | O_RDONLY); /* if dirfd is invalid, directory sync fails, but we're still OK */ - config_file_sav = XMALLOC( - MTYPE_TMP, strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1); - strcpy(config_file_sav, config_file); - strcat(config_file_sav, CONF_BACKUP_EXT); + size_t config_file_sav_sz = strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1; + config_file_sav = XMALLOC(MTYPE_TMP, config_file_sav_sz); + strlcpy(config_file_sav, config_file, config_file_sav_sz); + strlcat(config_file_sav, CONF_BACKUP_EXT, config_file_sav_sz); config_file_tmp = XMALLOC(MTYPE_TMP, strlen(config_file) + 8); - sprintf(config_file_tmp, "%s.XXXXXX", config_file); + snprintf(config_file_tmp, strlen(config_file) + 8, "%s.XXXXXX", + config_file); /* Open file to configuration write. */ fd = mkstemp(config_file_tmp); @@ -1922,7 +1724,7 @@ DEFUN(config_domainname, { struct cmd_token *word = argv[1]; - if (!isalpha((int)word->arg[0])) { + if (!isalpha((unsigned char)word->arg[0])) { vty_out(vty, "Please specify string starting with alphabet\n"); return CMD_WARNING_CONFIG_FAILED; } @@ -1956,8 +1758,16 @@ DEFUN (config_hostname, { struct cmd_token *word = argv[1]; - if (!isalnum((int)word->arg[0])) { - vty_out(vty, "Please specify string starting with alphabet\n"); + if (!isalnum((unsigned char)word->arg[0])) { + vty_out(vty, + "Please specify string starting with alphabet or number\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + /* With reference to RFC 1123 Section 2.1 */ + if (strlen(word->arg) > HOSTNAME_LEN) { + vty_out(vty, "Hostname length should be less than %d chars\n", + HOSTNAME_LEN); return CMD_WARNING_CONFIG_FAILED; } @@ -1996,7 +1806,7 @@ DEFUN (config_password, return CMD_SUCCESS; } - if (!isalnum((int)argv[idx_8]->arg[0])) { + if (!isalnum((unsigned char)argv[idx_8]->arg[0])) { vty_out(vty, "Please specify string starting with alphanumeric\n"); return CMD_WARNING_CONFIG_FAILED; @@ -2076,7 +1886,7 @@ DEFUN (config_enable_password, } } - if (!isalnum((int)argv[idx_8]->arg[0])) { + if (!isalnum((unsigned char)argv[idx_8]->arg[0])) { vty_out(vty, "Please specify string starting with alphanumeric\n"); return CMD_WARNING_CONFIG_FAILED; @@ -2255,7 +2065,8 @@ DEFUN (config_logmsg, int level; char *message; - if ((level = level_match(argv[idx_log_level]->arg)) == ZLOG_DISABLED) + level = log_level_match(argv[idx_log_level]->arg); + if (level == ZLOG_DISABLED) return CMD_ERR_NO_MATCH; zlog(level, "%s", @@ -2266,350 +2077,6 @@ DEFUN (config_logmsg, return CMD_SUCCESS; } -DEFUN (show_logging, - show_logging_cmd, - "show logging", - SHOW_STR - "Show current logging configuration\n") -{ - struct zlog *zl = zlog_default; - - vty_out(vty, "Syslog logging: "); - if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED) - vty_out(vty, "disabled"); - else - vty_out(vty, "level %s, facility %s, ident %s", - zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]], - facility_name(zl->facility), zl->ident); - vty_out(vty, "\n"); - - vty_out(vty, "Stdout logging: "); - if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED) - vty_out(vty, "disabled"); - else - vty_out(vty, "level %s", - zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]); - vty_out(vty, "\n"); - - vty_out(vty, "Monitor logging: "); - if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED) - vty_out(vty, "disabled"); - else - vty_out(vty, "level %s", - zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]); - vty_out(vty, "\n"); - - vty_out(vty, "File logging: "); - if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) || !zl->fp) - vty_out(vty, "disabled"); - else - vty_out(vty, "level %s, filename %s", - zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]], - zl->filename); - vty_out(vty, "\n"); - - vty_out(vty, "Protocol name: %s\n", zl->protoname); - vty_out(vty, "Record priority: %s\n", - (zl->record_priority ? "enabled" : "disabled")); - vty_out(vty, "Timestamp precision: %d\n", zl->timestamp_precision); - - return CMD_SUCCESS; -} - -DEFUN (config_log_stdout, - config_log_stdout_cmd, - "log stdout []", - "Logging control\n" - "Set stdout logging level\n" - LOG_LEVEL_DESC) -{ - int idx_log_level = 2; - - if (argc == idx_log_level) { - zlog_set_level(ZLOG_DEST_STDOUT, zlog_default->default_lvl); - return CMD_SUCCESS; - } - int level; - - if ((level = level_match(argv[idx_log_level]->arg)) == ZLOG_DISABLED) - return CMD_ERR_NO_MATCH; - zlog_set_level(ZLOG_DEST_STDOUT, level); - return CMD_SUCCESS; -} - -DEFUN (no_config_log_stdout, - no_config_log_stdout_cmd, - "no log stdout []", - NO_STR - "Logging control\n" - "Cancel logging to stdout\n" - LOG_LEVEL_DESC) -{ - zlog_set_level(ZLOG_DEST_STDOUT, ZLOG_DISABLED); - return CMD_SUCCESS; -} - -DEFUN (config_log_monitor, - config_log_monitor_cmd, - "log monitor []", - "Logging control\n" - "Set terminal line (monitor) logging level\n" - LOG_LEVEL_DESC) -{ - int idx_log_level = 2; - - if (argc == idx_log_level) { - zlog_set_level(ZLOG_DEST_MONITOR, zlog_default->default_lvl); - return CMD_SUCCESS; - } - int level; - - if ((level = level_match(argv[idx_log_level]->arg)) == ZLOG_DISABLED) - return CMD_ERR_NO_MATCH; - zlog_set_level(ZLOG_DEST_MONITOR, level); - return CMD_SUCCESS; -} - -DEFUN (no_config_log_monitor, - no_config_log_monitor_cmd, - "no log monitor []", - NO_STR - "Logging control\n" - "Disable terminal line (monitor) logging\n" - LOG_LEVEL_DESC) -{ - zlog_set_level(ZLOG_DEST_MONITOR, ZLOG_DISABLED); - return CMD_SUCCESS; -} - -static int set_log_file(struct vty *vty, const char *fname, int loglevel) -{ - int ret; - char *p = NULL; - const char *fullpath; - - /* Path detection. */ - if (!IS_DIRECTORY_SEP(*fname)) { - char cwd[MAXPATHLEN + 1]; - cwd[MAXPATHLEN] = '\0'; - - if (getcwd(cwd, MAXPATHLEN) == NULL) { - flog_err_sys(EC_LIB_SYSTEM_CALL, - "config_log_file: Unable to alloc mem!"); - return CMD_WARNING_CONFIG_FAILED; - } - - p = XMALLOC(MTYPE_TMP, strlen(cwd) + strlen(fname) + 2); - sprintf(p, "%s/%s", cwd, fname); - fullpath = p; - } else - fullpath = fname; - - ret = zlog_set_file(fullpath, loglevel); - - XFREE(MTYPE_TMP, p); - - if (!ret) { - if (vty) - vty_out(vty, "can't open logfile %s\n", fname); - return CMD_WARNING_CONFIG_FAILED; - } - - XFREE(MTYPE_HOST, host.logfile); - - host.logfile = XSTRDUP(MTYPE_HOST, fname); - -#if defined(HAVE_CUMULUS) - if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != ZLOG_DISABLED) - zlog_set_level(ZLOG_DEST_SYSLOG, ZLOG_DISABLED); -#endif - return CMD_SUCCESS; -} - -void command_setup_early_logging(const char *dest, const char *level) -{ - char *token; - - if (level) { - int nlevel = level_match(level); - - if (nlevel != ZLOG_DISABLED) - zlog_default->default_lvl = nlevel; - } - - if (!dest) - return; - - if (strcmp(dest, "stdout") == 0) { - zlog_set_level(ZLOG_DEST_STDOUT, zlog_default->default_lvl); - return; - } - - if (strcmp(dest, "syslog") == 0) { - zlog_set_level(ZLOG_DEST_SYSLOG, zlog_default->default_lvl); - return; - } - - token = strstr(dest, ":"); - if (token == NULL) - return; - - token++; - - set_log_file(NULL, token, zlog_default->default_lvl); -} - -DEFUN (config_log_file, - config_log_file_cmd, - "log file FILENAME []", - "Logging control\n" - "Logging to file\n" - "Logging filename\n" - LOG_LEVEL_DESC) -{ - int idx_filename = 2; - int idx_log_levels = 3; - if (argc == 4) { - int level; - if ((level = level_match(argv[idx_log_levels]->arg)) - == ZLOG_DISABLED) - return CMD_ERR_NO_MATCH; - return set_log_file(vty, argv[idx_filename]->arg, level); - } else - return set_log_file(vty, argv[idx_filename]->arg, - zlog_default->default_lvl); -} - -static void disable_log_file(void) -{ - zlog_reset_file(); - - XFREE(MTYPE_HOST, host.logfile); - - host.logfile = NULL; -} - -DEFUN (no_config_log_file, - no_config_log_file_cmd, - "no log file [FILENAME [LEVEL]]", - NO_STR - "Logging control\n" - "Cancel logging to file\n" - "Logging file name\n" - "Logging level\n") -{ - disable_log_file(); - return CMD_SUCCESS; -} - -DEFUN (config_log_syslog, - config_log_syslog_cmd, - "log syslog []", - "Logging control\n" - "Set syslog logging level\n" - LOG_LEVEL_DESC) -{ - int idx_log_levels = 2; - - if (argc == 3) { - int level; - if ((level = level_match(argv[idx_log_levels]->arg)) - == ZLOG_DISABLED) - return CMD_ERR_NO_MATCH; - zlog_set_level(ZLOG_DEST_SYSLOG, level); - return CMD_SUCCESS; - } else { - zlog_set_level(ZLOG_DEST_SYSLOG, zlog_default->default_lvl); - return CMD_SUCCESS; - } -} - -DEFUN (no_config_log_syslog, - no_config_log_syslog_cmd, - "no log syslog [] []", - NO_STR - "Logging control\n" - "Cancel logging to syslog\n" - LOG_FACILITY_DESC - LOG_LEVEL_DESC) -{ - zlog_set_level(ZLOG_DEST_SYSLOG, ZLOG_DISABLED); - return CMD_SUCCESS; -} - -DEFUN (config_log_facility, - config_log_facility_cmd, - "log facility ", - "Logging control\n" - "Facility parameter for syslog messages\n" - LOG_FACILITY_DESC) -{ - int idx_target = 2; - int facility = facility_match(argv[idx_target]->arg); - - zlog_default->facility = facility; - return CMD_SUCCESS; -} - -DEFUN (no_config_log_facility, - no_config_log_facility_cmd, - "no log facility []", - NO_STR - "Logging control\n" - "Reset syslog facility to default (daemon)\n" - LOG_FACILITY_DESC) -{ - zlog_default->facility = LOG_DAEMON; - return CMD_SUCCESS; -} - -DEFUN (config_log_record_priority, - config_log_record_priority_cmd, - "log record-priority", - "Logging control\n" - "Log the priority of the message within the message\n") -{ - zlog_default->record_priority = 1; - return CMD_SUCCESS; -} - -DEFUN (no_config_log_record_priority, - no_config_log_record_priority_cmd, - "no log record-priority", - NO_STR - "Logging control\n" - "Do not log the priority of the message within the message\n") -{ - zlog_default->record_priority = 0; - return CMD_SUCCESS; -} - -DEFUN (config_log_timestamp_precision, - config_log_timestamp_precision_cmd, - "log timestamp precision (0-6)", - "Logging control\n" - "Timestamp configuration\n" - "Set the timestamp precision\n" - "Number of subsecond digits\n") -{ - int idx_number = 3; - zlog_default->timestamp_precision = - strtoul(argv[idx_number]->arg, NULL, 10); - return CMD_SUCCESS; -} - -DEFUN (no_config_log_timestamp_precision, - no_config_log_timestamp_precision_cmd, - "no log timestamp precision", - NO_STR - "Logging control\n" - "Timestamp configuration\n" - "Reset the timestamp precision to the default value of 0\n") -{ - zlog_default->timestamp_precision = 0; - return CMD_SUCCESS; -} - DEFUN (debug_memstats, debug_memstats_cmd, "[no] debug memstats-at-exit", @@ -2641,6 +2108,12 @@ int cmd_banner_motd_file(const char *file) return success; } +void cmd_banner_motd_line(const char *line) +{ + XFREE(MTYPE_HOST, host.motd); + host.motd = XSTRDUP(MTYPE_HOST, line); +} + DEFUN (banner_motd_file, banner_motd_file_cmd, "banner motd file FILE", @@ -2661,6 +2134,26 @@ DEFUN (banner_motd_file, return cmd; } +DEFUN (banner_motd_line, + banner_motd_line_cmd, + "banner motd line LINE...", + "Set banner\n" + "Banner for motd\n" + "Banner from an input\n" + "Text\n") +{ + int idx = 0; + char *motd; + + argv_find(argv, argc, "LINE", &idx); + motd = argv_concat(argv, argc, idx); + + cmd_banner_motd_line(motd); + XFREE(MTYPE_TMP, motd); + + return CMD_SUCCESS; +} + DEFUN (banner_motd_default, banner_motd_default_cmd, "banner motd default", @@ -2668,7 +2161,7 @@ DEFUN (banner_motd_default, "Strings for motd\n" "Default string\n") { - host.motd = default_motd; + cmd_banner_motd_line(FRR_DEFAULT_MOTD); return CMD_SUCCESS; } @@ -2688,15 +2181,66 @@ DEFUN (no_banner_motd, DEFUN(find, find_cmd, - "find COMMAND...", - "Find CLI command containing text\n" - "Text to search for\n") + "find REGEX", + "Find CLI command matching a regular expression\n" + "Search pattern (POSIX regex)\n") { - char *text = argv_concat(argv, argc, 1); + char *pattern = argv[1]->arg; const struct cmd_node *node; const struct cmd_element *cli; vector clis; + regex_t exp = {}; + + int cr = regcomp(&exp, pattern, REG_NOSUB | REG_EXTENDED); + + if (cr != 0) { + switch (cr) { + case REG_BADBR: + vty_out(vty, "%% Invalid {...} expression\n"); + break; + case REG_BADRPT: + vty_out(vty, "%% Bad repetition operator\n"); + break; + case REG_BADPAT: + vty_out(vty, "%% Regex syntax error\n"); + break; + case REG_ECOLLATE: + vty_out(vty, "%% Invalid collating element\n"); + break; + case REG_ECTYPE: + vty_out(vty, "%% Invalid character class name\n"); + break; + case REG_EESCAPE: + vty_out(vty, + "%% Regex ended with escape character (\\)\n"); + break; + case REG_ESUBREG: + vty_out(vty, + "%% Invalid number in \\digit construction\n"); + break; + case REG_EBRACK: + vty_out(vty, "%% Unbalanced square brackets\n"); + break; + case REG_EPAREN: + vty_out(vty, "%% Unbalanced parentheses\n"); + break; + case REG_EBRACE: + vty_out(vty, "%% Unbalanced braces\n"); + break; + case REG_ERANGE: + vty_out(vty, + "%% Invalid endpoint in range expression\n"); + break; + case REG_ESPACE: + vty_out(vty, "%% Failed to compile (out of memory)\n"); + break; + } + + goto done; + } + + for (unsigned int i = 0; i < vector_active(cmdvec); i++) { node = vector_slot(cmdvec, i); if (!node) @@ -2704,14 +2248,15 @@ DEFUN(find, clis = node->cmd_vector; for (unsigned int j = 0; j < vector_active(clis); j++) { cli = vector_slot(clis, j); - if (strcasestr(cli->string, text)) + + if (regexec(&exp, cli->string, 0, NULL, 0) == 0) vty_out(vty, " (%s) %s\n", - node_names[node->node], cli->string); + node->name, cli->string); } } - XFREE(MTYPE_TMP, text); - +done: + regfree(&exp); return CMD_SUCCESS; } @@ -2754,9 +2299,6 @@ void cmd_init(int terminal) { struct utsname names; - if (array_size(node_names) != NODE_TYPE_MAX) - assert(!"Update the CLI node description array!"); - uname(&names); qobj_init(); @@ -2781,26 +2323,26 @@ void cmd_init(int terminal) #endif host.password = NULL; host.enable = NULL; - host.logfile = NULL; host.config = NULL; host.noconfig = (terminal < 0); host.lines = -1; - host.motd = default_motd; + cmd_banner_motd_line(FRR_DEFAULT_MOTD); host.motdfile = NULL; /* Install top nodes. */ - install_node(&view_node, NULL); - install_node(&enable_node, NULL); - install_node(&auth_node, NULL); - install_node(&auth_enable_node, NULL); - install_node(&config_node, config_write_host); + install_node(&view_node); + install_node(&enable_node); + install_node(&auth_node); + install_node(&auth_enable_node); + install_node(&config_node); /* Each node's basic commands. */ install_element(VIEW_NODE, &show_version_cmd); install_element(ENABLE_NODE, &show_startup_config_cmd); - install_element(ENABLE_NODE, &debug_memstats_cmd); if (terminal) { + install_element(ENABLE_NODE, &debug_memstats_cmd); + install_element(VIEW_NODE, &config_list_cmd); install_element(VIEW_NODE, &config_exit_cmd); install_element(VIEW_NODE, &config_quit_cmd); @@ -2808,7 +2350,6 @@ void cmd_init(int terminal) install_element(VIEW_NODE, &config_enable_cmd); install_element(VIEW_NODE, &config_terminal_length_cmd); install_element(VIEW_NODE, &config_terminal_no_length_cmd); - install_element(VIEW_NODE, &show_logging_cmd); install_element(VIEW_NODE, &show_commandtree_cmd); install_element(VIEW_NODE, &echo_cmd); install_element(VIEW_NODE, &autocomplete_cmd); @@ -2833,40 +2374,27 @@ void cmd_init(int terminal) install_element(CONFIG_NODE, &no_hostname_cmd); install_element(CONFIG_NODE, &domainname_cmd); install_element(CONFIG_NODE, &no_domainname_cmd); - install_element(CONFIG_NODE, &frr_version_defaults_cmd); - install_element(CONFIG_NODE, &debug_memstats_cmd); if (terminal > 0) { + full_cli = true; + + install_element(CONFIG_NODE, &debug_memstats_cmd); + install_element(CONFIG_NODE, &password_cmd); install_element(CONFIG_NODE, &no_password_cmd); install_element(CONFIG_NODE, &enable_password_cmd); install_element(CONFIG_NODE, &no_enable_password_cmd); - install_element(CONFIG_NODE, &config_log_stdout_cmd); - install_element(CONFIG_NODE, &no_config_log_stdout_cmd); - install_element(CONFIG_NODE, &config_log_monitor_cmd); - install_element(CONFIG_NODE, &no_config_log_monitor_cmd); - install_element(CONFIG_NODE, &config_log_file_cmd); - install_element(CONFIG_NODE, &no_config_log_file_cmd); - install_element(CONFIG_NODE, &config_log_syslog_cmd); - install_element(CONFIG_NODE, &no_config_log_syslog_cmd); - install_element(CONFIG_NODE, &config_log_facility_cmd); - install_element(CONFIG_NODE, &no_config_log_facility_cmd); - install_element(CONFIG_NODE, &config_log_record_priority_cmd); - install_element(CONFIG_NODE, - &no_config_log_record_priority_cmd); - install_element(CONFIG_NODE, - &config_log_timestamp_precision_cmd); - install_element(CONFIG_NODE, - &no_config_log_timestamp_precision_cmd); install_element(CONFIG_NODE, &service_password_encrypt_cmd); install_element(CONFIG_NODE, &no_service_password_encrypt_cmd); install_element(CONFIG_NODE, &banner_motd_default_cmd); install_element(CONFIG_NODE, &banner_motd_file_cmd); + install_element(CONFIG_NODE, &banner_motd_line_cmd); install_element(CONFIG_NODE, &no_banner_motd_cmd); install_element(CONFIG_NODE, &service_terminal_length_cmd); install_element(CONFIG_NODE, &no_service_terminal_length_cmd); + log_cmd_init(); vrf_install_commands(); } @@ -2904,9 +2432,9 @@ void cmd_terminate(void) XFREE(MTYPE_HOST, host.password_encrypt); XFREE(MTYPE_HOST, host.enable); XFREE(MTYPE_HOST, host.enable_encrypt); - XFREE(MTYPE_HOST, host.logfile); XFREE(MTYPE_HOST, host.motdfile); XFREE(MTYPE_HOST, host.config); + XFREE(MTYPE_HOST, host.motd); list_delete(&varhandlers); qobj_finish(); diff --git a/lib/command.h b/lib/command.h index a5f9616dbf..368e7cd1e1 100644 --- a/lib/command.h +++ b/lib/command.h @@ -34,9 +34,19 @@ extern "C" { #endif -DECLARE_MTYPE(HOST) DECLARE_MTYPE(COMPLETION) +/* + * From RFC 1123 (Requirements for Internet Hosts), Section 2.1 on hostnames: + * One aspect of host name syntax is hereby changed: the restriction on + * the first character is relaxed to allow either a letter or a digit. + * Host software MUST support this more liberal syntax. + * + * Host software MUST handle host names of up to 63 characters and + * SHOULD handle host names of up to 255 characters. + */ +#define HOSTNAME_LEN 255 + /* Host configuration variable */ struct host { /* Host name of this router. */ @@ -56,9 +66,6 @@ struct host { /* System wide terminal lines. */ int lines; - /* Log filename. */ - char *logfile; - /* config file name of this host */ char *config; int noconfig; @@ -68,7 +75,7 @@ struct host { int encrypt; /* Banner configuration. */ - const char *motd; + char *motd; char *motdfile; }; @@ -83,10 +90,11 @@ enum node_type { VRF_DEBUG_NODE, /* Vrf Debug node. */ NORTHBOUND_DEBUG_NODE, /* Northbound Debug node. */ DEBUG_VNC_NODE, /* Debug VNC node. */ + RMAP_DEBUG_NODE, /* Route-map debug node */ + RESOLVER_DEBUG_NODE, /* Resolver debug node */ AAA_NODE, /* AAA node. */ KEYCHAIN_NODE, /* Key-chain node. */ KEYCHAIN_KEY_NODE, /* Key-chain key node. */ - LOGICALROUTER_NODE, /* Logical-Router node. */ IP_NODE, /* Static ip route node. */ VRF_NODE, /* VRF mode node. */ INTERFACE_NODE, /* Interface mode node. */ @@ -138,6 +146,7 @@ enum node_type { MPLS_NODE, /* MPLS config node */ PW_NODE, /* Pseudowire config node */ VTY_NODE, /* Vty node. */ + FPM_NODE, /* Dataplane FPM node. */ LINK_PARAMS_NODE, /* Link-parameters node */ BGP_EVPN_VNI_NODE, /* BGP EVPN VNI */ RPKI_NODE, /* RPKI node for configuration of RPKI cache server @@ -146,28 +155,38 @@ enum node_type { BGP_FLOWSPECV6_NODE, /* BGP IPv6 FLOWSPEC Address-Family */ BFD_NODE, /* BFD protocol mode. */ BFD_PEER_NODE, /* BFD peer configuration mode. */ + BFD_PROFILE_NODE, /* BFD profile configuration mode. */ OPENFABRIC_NODE, /* OpenFabric router configuration node */ + VRRP_NODE, /* VRRP node */ + BMP_NODE, /* BMP config under router bgp */ NODE_TYPE_MAX, /* maximum */ }; extern vector cmdvec; extern const struct message tokennames[]; -extern const char *node_names[]; + +/* for external users depending on struct layout */ +#define FRR_CMD_NODE_20200416 /* Node which has some commands and prompt string and configuration function pointer . */ struct cmd_node { + const char *name; + /* Node index. */ enum node_type node; + enum node_type parent_node; /* Prompt character at vty interface. */ const char *prompt; - /* Is this node's configuration goes to vtysh ? */ - int vtysh; - /* Node's configuration write function */ - int (*func)(struct vty *); + int (*config_write)(struct vty *); + + /* called when leaving the node on a VTY session. + * return 1 if normal exit processing should happen, 0 to suppress + */ + int (*node_exit)(struct vty *); /* Node's command graph */ struct graph *cmdgraph; @@ -195,6 +214,7 @@ struct cmd_node { #define CMD_SUSPEND 12 #define CMD_WARNING_CONFIG_FAILED 13 #define CMD_NOT_MY_INSTANCE 14 +#define CMD_NO_LEVEL_UP 15 /* Argc max counts. */ #define CMD_ARGC_MAX 256 @@ -204,7 +224,7 @@ struct cmd_node { /* helper defines for end-user DEFUN* macros */ #define DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attrs, dnum) \ - static struct cmd_element cmdname = { \ + static const struct cmd_element cmdname = { \ .string = cmdstr, \ .func = funcname, \ .doc = helpstr, \ @@ -238,6 +258,12 @@ struct cmd_node { #define DEFPY_HIDDEN(funcname, cmdname, cmdstr, helpstr) \ DEFPY_ATTR(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN) +#define DEFPY_YANG(funcname, cmdname, cmdstr, helpstr) \ + DEFPY_ATTR(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_YANG) + +#define DEFPY_YANG_NOSH(funcname, cmdname, cmdstr, helpstr) \ + DEFPY_YANG(funcname, cmdname, cmdstr, helpstr) + #define DEFUN(funcname, cmdname, cmdstr, helpstr) \ DEFUN_CMD_FUNC_DECL(funcname) \ DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0) \ @@ -251,10 +277,16 @@ struct cmd_node { #define DEFUN_HIDDEN(funcname, cmdname, cmdstr, helpstr) \ DEFUN_ATTR(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN) +#define DEFUN_YANG(funcname, cmdname, cmdstr, helpstr) \ + DEFUN_ATTR(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_YANG) + /* DEFUN_NOSH for commands that vtysh should ignore */ #define DEFUN_NOSH(funcname, cmdname, cmdstr, helpstr) \ DEFUN(funcname, cmdname, cmdstr, helpstr) +#define DEFUN_YANG_NOSH(funcname, cmdname, cmdstr, helpstr) \ + DEFUN_YANG(funcname, cmdname, cmdstr, helpstr) + /* DEFSH for vtysh. */ #define DEFSH(daemon, cmdname, cmdstr, helpstr) \ DEFUN_CMD_ELEMENT(NULL, cmdname, cmdstr, helpstr, 0, daemon) @@ -263,6 +295,9 @@ struct cmd_node { DEFUN_CMD_ELEMENT(NULL, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN, \ daemon) +#define DEFSH_YANG(daemon, cmdname, cmdstr, helpstr) \ + DEFUN_CMD_ELEMENT(NULL, cmdname, cmdstr, helpstr, CMD_ATTR_YANG, daemon) + /* DEFUN + DEFSH */ #define DEFUNSH(daemon, funcname, cmdname, cmdstr, helpstr) \ DEFUN_CMD_FUNC_DECL(funcname) \ @@ -283,6 +318,9 @@ struct cmd_node { DEFUNSH_ATTR(daemon, funcname, cmdname, cmdstr, helpstr, \ CMD_ATTR_DEPRECATED) +#define DEFUNSH_YANG(daemon, funcname, cmdname, cmdstr, helpstr) \ + DEFUNSH_ATTR(daemon, funcname, cmdname, cmdstr, helpstr, CMD_ATTR_YANG) + /* ALIAS macro which define existing command's alias. */ #define ALIAS(funcname, cmdname, cmdstr, helpstr) \ DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0) @@ -298,6 +336,9 @@ struct cmd_node { DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, \ CMD_ATTR_DEPRECATED, 0) +#define ALIAS_YANG(funcname, cmdname, cmdstr, helpstr) \ + DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_YANG, 0) + #define ALIAS_SH(daemon, funcname, cmdname, cmdstr, helpstr) \ DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, daemon) @@ -309,18 +350,6 @@ struct cmd_node { DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, \ CMD_ATTR_DEPRECATED, daemon) -#else /* VTYSH_EXTRACT_PL */ -#define DEFPY(funcname, cmdname, cmdstr, helpstr) \ - DEFUN(funcname, cmdname, cmdstr, helpstr) - -#define DEFPY_NOSH(funcname, cmdname, cmdstr, helpstr) \ - DEFUN_NOSH(funcname, cmdname, cmdstr, helpstr) - -#define DEFPY_ATTR(funcname, cmdname, cmdstr, helpstr, attr) \ - DEFUN_ATTR(funcname, cmdname, cmdstr, helpstr, attr) - -#define DEFPY_HIDDEN(funcname, cmdname, cmdstr, helpstr) \ - DEFUN_HIDDEN(funcname, cmdname, cmdstr, helpstr) #endif /* VTYSH_EXTRACT_PL */ /* Some macroes */ @@ -339,6 +368,8 @@ struct cmd_node { #define SHOW_STR "Show running system information\n" #define IP_STR "IP information\n" #define IPV6_STR "IPv6 information\n" +#define SRTE_STR "SR-TE information\n" +#define SRTE_COLOR_STR "SR-TE Color information\n" #define NO_STR "Negate a command or set its defaults\n" #define REDIST_STR "Redistribute information from another routing protocol\n" #define CLEAR_STR "Reset functions\n" @@ -384,6 +415,10 @@ struct cmd_node { #define SR_STR "Segment-Routing specific commands\n" #define WATCHFRR_STR "watchfrr information\n" #define ZEBRA_STR "Zebra information\n" +#define FILTER_LOG_STR "Filter Logs\n" +#define BFD_PROFILE_STR "BFD profile.\n" +#define BFD_PROFILE_NAME_STR "BFD profile name.\n" +#define SHARP_STR "Sharp Routing Protocol\n" #define CMD_VNI_RANGE "(1-16777215)" #define CONF_BACKUP_EXT ".sav" @@ -398,14 +433,35 @@ struct cmd_node { #define NEIGHBOR_ADDR_STR2 "Neighbor address\nNeighbor IPv6 address\nInterface name or neighbor tag\n" #define NEIGHBOR_ADDR_STR3 "Neighbor address\nIPv6 address\nInterface name\n" +/* Daemons lists */ +#define DAEMONS_STR \ + "For the zebra daemon\nFor the rip daemon\nFor the ripng daemon\nFor the ospf daemon\nFor the ospfv6 daemon\nFor the bgp daemon\nFor the isis daemon\nFor the pbr daemon\nFor the fabricd daemon\nFor the pim daemon\nFor the static daemon\nFor the sharpd daemon\nFor the vrrpd daemon\nFor the ldpd daemon\n" +#define DAEMONS_LIST \ + "" + +/* Graceful Restart cli help strings */ +#define GR_CMD "Global Graceful Restart command\n" +#define NO_GR_CMD "Undo Global Graceful Restart command\n" +#define GR "Global Graceful Restart - GR Mode\n" +#define GR_DISABLE "Global Graceful Restart - Disable Mode\n" +#define NO_GR_DISABLE "Undo Global Graceful Restart - Disable Mode\n" +#define GR_DEBUG "Graceful Restart - Enable Debug Logs\n" +#define GR_SHOW "Graceful Restart - Show command for Global and all neighbor mode\n" +#define GR_NEIGHBOR_CMD "Graceful Restart command for a neighbor\n" +#define NO_GR_NEIGHBOR_CMD "Undo Graceful Restart command for a neighbor\n" +#define GR_NEIGHBOR_DISABLE_CMD "Graceful Restart Disable command for a neighbor\n" +#define NO_GR_NEIGHBOR_DISABLE_CMD "Undo Graceful Restart Disable command for a neighbor\n" +#define GR_NEIGHBOR_HELPER_CMD "Graceful Restart Helper command for a neighbor\n" +#define NO_GR_NEIGHBOR_HELPER_CMD "Undo Graceful Restart Helper command for a neighbor\n" + /* Prototypes. */ -extern void install_node(struct cmd_node *, int (*)(struct vty *)); +extern void install_node(struct cmd_node *node); extern void install_default(enum node_type); -extern void install_element(enum node_type, struct cmd_element *); +extern void install_element(enum node_type, const struct cmd_element *); /* known issue with uninstall_element: changes to cmd_token->attr (i.e. * deprecated/hidden) are not reversed. */ -extern void uninstall_element(enum node_type, struct cmd_element *); +extern void uninstall_element(enum node_type, const struct cmd_element *); /* Concatenates argv[shift] through argv[argc-1] into a single NUL-terminated string with a space between each element (allocated using @@ -479,6 +535,7 @@ extern void host_config_set(const char *); extern void print_version(const char *); extern int cmd_banner_motd_file(const char *); +extern void cmd_banner_motd_line(const char *line); /* struct host global, ick */ extern struct host host; diff --git a/lib/command_graph.c b/lib/command_graph.c index 4757fd951f..d30d9ab702 100644 --- a/lib/command_graph.c +++ b/lib/command_graph.c @@ -97,7 +97,7 @@ void cmd_token_varname_set(struct cmd_token *token, const char *varname) token->varname[i] = '_'; break; default: - token->varname[i] = tolower((int)varname[i]); + token->varname[i] = tolower((unsigned char)varname[i]); } token->varname[len] = '\0'; } diff --git a/lib/command_graph.h b/lib/command_graph.h index 903d515834..179e104a57 100644 --- a/lib/command_graph.h +++ b/lib/command_graph.h @@ -74,6 +74,7 @@ enum cmd_token_type { enum { CMD_ATTR_NORMAL, CMD_ATTR_DEPRECATED, CMD_ATTR_HIDDEN, + CMD_ATTR_YANG, }; /* Comamand token struct. */ @@ -116,7 +117,7 @@ extern struct cmd_token *cmd_token_dup(struct cmd_token *); extern void cmd_token_del(struct cmd_token *); extern void cmd_token_varname_set(struct cmd_token *token, const char *varname); -extern void cmd_graph_parse(struct graph *graph, struct cmd_element *cmd); +extern void cmd_graph_parse(struct graph *graph, const struct cmd_element *cmd); extern void cmd_graph_names(struct graph *graph); extern void cmd_graph_merge(struct graph *old, struct graph *n, int direction); diff --git a/lib/command_lex.l b/lib/command_lex.l index f361db78e9..0556605d63 100644 --- a/lib/command_lex.l +++ b/lib/command_lex.l @@ -85,7 +85,7 @@ RANGE \({NUMBER}[ ]?\-[ ]?{NUMBER}\) . {return yytext[0];} %% -YY_BUFFER_STATE buffer; +static YY_BUFFER_STATE buffer; void set_lexer_string (yyscan_t *scn, const char *string) { diff --git a/lib/command_match.c b/lib/command_match.c index 8b34d1e3eb..801b05f157 100644 --- a/lib/command_match.c +++ b/lib/command_match.c @@ -88,7 +88,7 @@ enum matcher_rv command_match(struct graph *cmdgraph, vector vline, // prepend a dummy token to match that pesky start node vector vvline = vector_init(vline->alloced + 1); - vector_set_index(vvline, 0, (void *)XSTRDUP(MTYPE_TMP, "dummy")); + vector_set_index(vvline, 0, XSTRDUP(MTYPE_TMP, "dummy")); memcpy(vvline->index + 1, vline->index, sizeof(void *) * vline->alloced); vvline->active = vline->active + 1; @@ -714,7 +714,7 @@ static enum match_type match_ipv4(const char *str) dots++; break; } - if (!isdigit((int)*str)) + if (!isdigit((unsigned char)*str)) return no_match; str++; @@ -723,8 +723,13 @@ static enum match_type match_ipv4(const char *str) if (str - sp > 3) return no_match; - strncpy(buf, sp, str - sp); - if (atoi(buf) > 255) + memcpy(buf, sp, str - sp); + + int v = atoi(buf); + + if (v > 255) + return no_match; + if (v > 0 && buf[0] == '0') return no_match; nums++; @@ -765,7 +770,7 @@ static enum match_type match_ipv4_prefix(const char *str) break; } - if (!isdigit((int)*str)) + if (!isdigit((unsigned char)*str)) return no_match; str++; @@ -774,8 +779,13 @@ static enum match_type match_ipv4_prefix(const char *str) if (str - sp > 3) return no_match; - strncpy(buf, sp, str - sp); - if (atoi(buf) > 255) + memcpy(buf, sp, str - sp); + + int v = atoi(buf); + + if (v > 255) + return no_match; + if (v > 0 && buf[0] == '0') return no_match; if (dots == 3) { @@ -797,7 +807,7 @@ static enum match_type match_ipv4_prefix(const char *str) sp = str; while (*str != '\0') { - if (!isdigit((int)*str)) + if (!isdigit((unsigned char)*str)) return no_match; str++; diff --git a/lib/command_parse.y b/lib/command_parse.y index 1b304a98b2..5dc19d2c9b 100644 --- a/lib/command_parse.y +++ b/lib/command_parse.y @@ -46,6 +46,7 @@ %code requires { #include "config.h" + #include #include #include #include @@ -83,7 +84,7 @@ struct parser_ctx { yyscan_t scanner; - struct cmd_element *el; + const struct cmd_element *el; struct graph *graph; struct graph_node *currnode; @@ -140,6 +141,8 @@ static void cleanup (struct parser_ctx *ctx); + static void loopcheck(struct parser_ctx *ctx, struct subgraph *sg); + #define scanner ctx->scanner } @@ -335,6 +338,7 @@ selector: '{' selector_seq_seq '}' varname_token * just use [{a|b}] if neccessary, that will work perfectly fine, and reason * #1 is good enough to keep it this way. */ + loopcheck(ctx, &$$); cmd_token_varname_set ($2.end->data, $4); XFREE (MTYPE_LEX, $4); }; @@ -375,7 +379,7 @@ selector: '[' selector_seq_seq ']' varname_token DEFINE_MTYPE(LIB, LEX, "Lexer token (temporary)") void -cmd_graph_parse (struct graph *graph, struct cmd_element *cmd) +cmd_graph_parse (struct graph *graph, const struct cmd_element *cmd) { struct parser_ctx ctx = { .graph = graph, .el = cmd }; @@ -396,6 +400,38 @@ cmd_graph_parse (struct graph *graph, struct cmd_element *cmd) /* parser helper functions */ +static bool loopcheck_inner(struct graph_node *start, struct graph_node *node, + struct graph_node *end, size_t depth) +{ + size_t i; + bool ret; + + /* safety check */ + if (depth++ == 64) + return true; + + for (i = 0; i < vector_active(node->to); i++) { + struct graph_node *next = vector_slot(node->to, i); + struct cmd_token *tok = next->data; + + if (next == end || next == start) + return true; + if (tok->type < SPECIAL_TKN) + continue; + ret = loopcheck_inner(start, next, end, depth); + if (ret) + return true; + } + return false; +} + +static void loopcheck(struct parser_ctx *ctx, struct subgraph *sg) +{ + if (loopcheck_inner(sg->start, sg->start, sg->end, 0)) + zlog_err("FATAL: '%s': {} contains an empty path! Use [{...}]", + ctx->el->string); +} + void yyerror (CMD_YYLTYPE *loc, struct parser_ctx *ctx, char const *msg) { @@ -449,11 +485,11 @@ terminate_graph (CMD_YYLTYPE *locp, struct parser_ctx *ctx, { // end of graph should look like this // * -> finalnode -> END_TKN -> cmd_element - struct cmd_element *element = ctx->el; + const struct cmd_element *element = ctx->el; struct graph_node *end_token_node = new_token_node (ctx, END_TKN, CMD_CR_TEXT, ""); struct graph_node *end_element_node = - graph_new_node (ctx->graph, element, NULL); + graph_new_node (ctx->graph, (void *)element, NULL); if (ctx->docstr && strlen (ctx->docstr) > 1) { zlog_debug ("Excessive docstring while parsing '%s'", ctx->el->string); diff --git a/lib/command_py.c b/lib/command_py.c index 58b7982665..4ec116df33 100644 --- a/lib/command_py.c +++ b/lib/command_py.c @@ -22,6 +22,12 @@ * memory leak or SEGV for things that haven't been well-tested. */ +/* This file is "exempt" from having +#include "config.h" + * as the first include statement because Python.h also does environment + * setup & these trample over each other. + */ + #include #include "structmember.h" #include @@ -321,6 +327,7 @@ static struct PyModuleDef pymoddef_clippy = { } while (0) #endif +#pragma GCC diagnostic ignored "-Wstrict-aliasing" PyMODINIT_FUNC command_py_init(void) { PyObject *pymod; diff --git a/lib/compiler.h b/lib/compiler.h index 474adc7c8b..3326e8bd33 100644 --- a/lib/compiler.h +++ b/lib/compiler.h @@ -33,6 +33,9 @@ extern "C" { #endif # define _CONSTRUCTOR(x) constructor(x) # define _DEPRECATED(x) deprecated(x) +# if __has_builtin(assume) +# define assume(x) __builtin_assume(x) +# endif #elif defined(__GNUC__) #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9) # define _RET_NONNULL , returns_nonnull @@ -44,12 +47,38 @@ extern "C" { #endif #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) # define _DEPRECATED(x) deprecated(x) +# define assume(x) do { if (!(x)) __builtin_unreachable(); } while (0) +#endif +#if __GNUC__ < 5 +# define __has_attribute(x) 0 #endif #if __GNUC__ >= 7 # define _FALLTHROUGH __attribute__((fallthrough)); #endif #endif +#if __has_attribute(hot) +# define _OPTIMIZE_HOT __attribute__((hot)) +#else +# define _OPTIMIZE_HOT +#endif +#if __has_attribute(optimize) +# define _OPTIMIZE_O3 __attribute__((optimize("3"))) +#else +# define _OPTIMIZE_O3 +#endif +#define OPTIMIZE _OPTIMIZE_O3 _OPTIMIZE_HOT + +#if !defined(__GNUC__) +#error module code needs GCC visibility extensions +#elif __GNUC__ < 4 +#error module code needs GCC visibility extensions +#else +# define DSO_PUBLIC __attribute__ ((visibility ("default"))) +# define DSO_SELF __attribute__ ((visibility ("protected"))) +# define DSO_LOCAL __attribute__ ((visibility ("hidden"))) +#endif + #ifdef __sun /* Solaris doesn't do constructor priorities due to linker restrictions */ #undef _CONSTRUCTOR @@ -75,11 +104,57 @@ extern "C" { #ifndef _DEPRECATED #define _DEPRECATED(x) deprecated #endif +#ifndef assume +#define assume(x) +#endif /* for helper functions defined inside macros */ #define macro_inline static inline __attribute__((unused)) #define macro_pure static inline __attribute__((unused, pure)) + +/* variadic macros, use like: + * #define V_0() ... + * #define V_1(x) ... + * #define V(...) MACRO_VARIANT(V, ##__VA_ARGS__)(__VA_ARGS__) + */ +#define _MACRO_VARIANT(A0,A1,A2,A3,A4,A5,A6,A7,A8,A9,A10, N, ...) N + +#define _CONCAT2(a, b) a ## b +#define _CONCAT(a, b) _CONCAT2(a,b) + +#define MACRO_VARIANT(NAME, ...) \ + _CONCAT(NAME, _MACRO_VARIANT(0, ##__VA_ARGS__, \ + _10, _9, _8, _7, _6, _5, _4, _3, _2, _1, _0)) + +#define NAMECTR(name) _CONCAT(name, __COUNTER__) + +/* per-arg repeat macros, use like: + * #define PERARG(n) ...n... + * #define FOO(...) MACRO_REPEAT(PERARG, ##__VA_ARGS__) + */ + +#define _MACRO_REPEAT_0(NAME) +#define _MACRO_REPEAT_1(NAME, A1) \ + NAME(A1) +#define _MACRO_REPEAT_2(NAME, A1, A2) \ + NAME(A1) NAME(A2) +#define _MACRO_REPEAT_3(NAME, A1, A2, A3) \ + NAME(A1) NAME(A2) NAME(A3) +#define _MACRO_REPEAT_4(NAME, A1, A2, A3, A4) \ + NAME(A1) NAME(A2) NAME(A3) NAME(A4) +#define _MACRO_REPEAT_5(NAME, A1, A2, A3, A4, A5) \ + NAME(A1) NAME(A2) NAME(A3) NAME(A4) NAME(A5) +#define _MACRO_REPEAT_6(NAME, A1, A2, A3, A4, A5, A6) \ + NAME(A1) NAME(A2) NAME(A3) NAME(A4) NAME(A5) NAME(A6) +#define _MACRO_REPEAT_7(NAME, A1, A2, A3, A4, A5, A6, A7) \ + NAME(A1) NAME(A2) NAME(A3) NAME(A4) NAME(A5) NAME(A6) NAME(A7) +#define _MACRO_REPEAT_8(NAME, A1, A2, A3, A4, A5, A6, A7, A8) \ + NAME(A1) NAME(A2) NAME(A3) NAME(A4) NAME(A5) NAME(A6) NAME(A7) NAME(A8) + +#define MACRO_REPEAT(NAME, ...) \ + MACRO_VARIANT(_MACRO_REPEAT, ##__VA_ARGS__)(NAME, ##__VA_ARGS__) + /* * for warnings on macros, put in the macro content like this: * #define MACRO BLA CPP_WARN("MACRO has been deprecated") @@ -124,6 +199,13 @@ extern "C" { _min_a < _min_b ? _min_a : _min_b; \ }) +#define numcmp(a, b) \ + ({ \ + typeof(a) _cmp_a = (a); \ + typeof(b) _cmp_b = (b); \ + (_cmp_a < _cmp_b) ? -1 : ((_cmp_a > _cmp_b) ? 1 : 0); \ + }) + #ifndef offsetof #ifdef __compiler_offsetof #define offsetof(TYPE, MEMBER) __compiler_offsetof(TYPE,MEMBER) @@ -132,6 +214,11 @@ extern "C" { #endif #endif +#ifdef container_of +#undef container_of +#endif + +#if !(defined(__cplusplus) || defined(test__cplusplus)) /* this variant of container_of() retains 'const' on pointers without needing * to be told to do so. The following will all work without warning: * @@ -150,9 +237,6 @@ extern "C" { * struct cont *x = container_of(cp, const struct cont, member); * struct cont *x = container_of(p, const struct cont, member); */ -#ifdef container_of -#undef container_of -#endif #define container_of(ptr, type, member) \ (__builtin_choose_expr( \ __builtin_types_compatible_p(typeof(&((type *)0)->member), \ @@ -168,6 +252,15 @@ extern "C" { offsetof(type, member)); \ }) \ )) +#else +/* current C++ compilers don't have the builtins used above; so this version + * of the macro doesn't do the const check. */ +#define container_of(ptr, type, member) \ + ({ \ + const typeof(((type *)0)->member) *__mptr = (ptr); \ + (type *)((char *)__mptr - offsetof(type, member)); \ + }) +#endif #define container_of_null(ptr, type, member) \ ({ \ @@ -177,6 +270,75 @@ extern "C" { #define array_size(ar) (sizeof(ar) / sizeof(ar[0])) +/* sigh. this is so ugly, it overflows and wraps to being nice again. + * + * printfrr() supports "%Ld" for , whatever that is typedef'd to. + * However, gcc & clang think that "%Ld" is , which doesn't quite + * match up since int64_t is on a lot of 64-bit systems. + * + * If we have _FRR_ATTRIBUTE_PRINTFRR, we loaded a compiler plugin that + * replaces the whole format checking bits with a custom version that + * understands "%Ld" (along with "%pI4" and co.), so we don't need to do + * anything. + * + * If we don't have that attribute... we still want -Wformat to work. So, + * this is the "f*ck it" approach and we just redefine int64_t to always be + * . This should work until such a time that is + * something else (e.g. 128-bit integer)... let's just guard against that + * with the _Static_assert below and work with the world we have right now, + * where is always 64-bit. + */ + +/* these need to be included before any of the following, so we can + * "overwrite" things. + */ +#include +#include + +#ifdef _FRR_ATTRIBUTE_PRINTFRR +#define PRINTFRR(a, b) __attribute__((frr_format("frr_printf", a, b))) + +#undef PRIu64 +#undef PRId64 +#undef PRIx64 +#define PRIu64 "Lu" +#define PRId64 "Ld" +#define PRIx64 "Lx" + +#else /* !_FRR_ATTRIBUTE_PRINTFRR */ +#define PRINTFRR(a, b) __attribute__((format(printf, a, b))) + +/* these should be typedefs, but might also be #define */ +#ifdef uint64_t +#undef uint64_t +#endif +#ifdef int64_t +#undef int64_t +#endif + +/* can't overwrite the typedef, but we can replace int64_t with _int64_t */ +typedef unsigned long long _uint64_t; +#define uint64_t _uint64_t +typedef signed long long _int64_t; +#define int64_t _int64_t + +/* if this breaks, 128-bit machines may have entered reality (or + * is something weird) + */ +#if __STDC_VERSION__ >= 201112L +_Static_assert(sizeof(_uint64_t) == 8 && sizeof(_int64_t) == 8, + "nobody expects the spanish intquisition"); +#endif + +/* since we redefined int64_t, we also need to redefine PRI*64 */ +#undef PRIu64 +#undef PRId64 +#undef PRIx64 +#define PRIu64 "llu" +#define PRId64 "lld" +#define PRIx64 "llx" +#endif /* !_FRR_ATTRIBUTE_PRINTFRR */ + #ifdef __cplusplus } #endif diff --git a/lib/csv.c b/lib/csv.c index 582106ebd4..b48b79792e 100644 --- a/lib/csv.c +++ b/lib/csv.c @@ -1,5 +1,5 @@ /* CSV - * Copyright (C) 2013 Cumulus Networks, Inc. + * Copyright (C) 2013,2020 Cumulus Networks, Inc. * * This file is part of Quagga. * @@ -22,6 +22,8 @@ #include "config.h" #endif +#include + #include #include #include @@ -83,7 +85,7 @@ csv_t *csv_init(csv_t *csv, char *buf, int buflen) csv = malloc(sizeof(csv_t)); if (csv == NULL) { log_error("CSV Malloc failed\n"); - return (NULL); + return NULL; } } memset(csv, 0, sizeof(csv_t)); @@ -144,7 +146,7 @@ char *csv_field_iter_next(csv_field_t **fld) { *fld = TAILQ_NEXT(*fld, next_field); if ((*fld) == NULL) { - return (NULL); + return NULL; } return ((*fld)->field); } @@ -198,7 +200,7 @@ static csv_field_t *csv_add_field_to_record(csv_t *csv, csv_record_t *rec, if (!fld) { log_error("field malloc failed\n"); /* more cleanup needed */ - return (NULL); + return NULL; } TAILQ_INSERT_TAIL(&(rec->fields), fld, next_field); fld->field = str + rlen; @@ -227,7 +229,7 @@ csv_record_t *csv_encode(csv_t *csv, int count, ...) str = (char *)malloc(csv->buflen); if (!str) { log_error("field str malloc failed\n"); - return (NULL); + return NULL; } } @@ -238,7 +240,7 @@ csv_record_t *csv_encode(csv_t *csv, int count, ...) if (!buf) free(str); va_end(list); - return (NULL); + return NULL; } csv_init_record(rec); rec->record = str; @@ -255,7 +257,7 @@ csv_record_t *csv_encode(csv_t *csv, int count, ...) log_error("fld malloc failed\n"); csv_remove_record(csv, rec); va_end(list); - return (NULL); + return NULL; } if (tempc < (count - 1)) { rec->rec_len += snprintf((str + rec->rec_len), @@ -419,7 +421,7 @@ void csv_clone_record(csv_t *csv, csv_record_t *in_rec, csv_record_t **out_rec) } rec->record = curr; rec->rec_len = in_rec->rec_len; - strcpy(rec->record, in_rec->record); + strlcpy(rec->record, in_rec->record, csv->buflen); /* decode record into fields */ csv_decode_record(rec); @@ -494,21 +496,21 @@ csv_record_t *csv_concat_record(csv_t *csv, csv_record_t *rec1, if (!csv_is_record_valid(csv, rec1) || !csv_is_record_valid(csv, rec2)) { log_error("rec1 and/or rec2 invalid\n"); - return (NULL); + return NULL; } /* we can only concat records if no buf was supplied during csv init */ if (csv->buf) { log_error( "un-supported for this csv type - single buf detected\n"); - return (NULL); + return NULL; } /* create a new rec */ rec = calloc(1, sizeof(csv_record_t)); if (!rec) { log_error("record malloc failed\n"); - return (NULL); + return NULL; } csv_init_record(rec); @@ -635,10 +637,10 @@ void csv_dump(csv_t *csv) static int get_memory_usage(pid_t pid) { int fd, data, stack; - char buf[4096], status_child[BUFSIZ]; + char buf[4096], status_child[PATH_MAX]; char *vm; - sprintf(status_child, "/proc/%d/status", pid); + snprintf(status_child, sizeof(status_child), "/proc/%d/status", pid); if ((fd = open(status_child, O_RDONLY)) < 0) return -1; @@ -670,8 +672,8 @@ int main() log_verbose("Mem: %d\n", get_memory_usage(getpid())); csv_init(&csv, buf, 256); - sprintf(hdr1, "%4d", 0); - sprintf(hdr2, "%4d", 1); + snprintf(hdr1, sizeof(hdr1), "%4d", 0); + snprintf(hdr2, sizeof(hdr2), "%4d", 1); log_verbose("(%zu/%zu/%d/%d)\n", strlen(hdr1), strlen(hdr2), atoi(hdr1), atoi(hdr2)); rec = csv_encode(&csv, 2, hdr1, hdr2); @@ -683,8 +685,8 @@ int main() } csv_encode(&csv, 2, "pdfadfadfadsadsaddfdfdsfdsd", "35444554545454545"); log_verbose("%s\n", buf); - sprintf(hdr1, "%4d", csv.csv_len); - sprintf(hdr2, "%4d", 1); + snprintf(hdr1, sizeof(hdr1), "%4d", csv.csv_len); + snprintf(hdr2, sizeof(hdr2), "%4d", 1); log_verbose("(%zu/%zu/%d/%d)\n", strlen(hdr1), strlen(hdr2), atoi(hdr1), atoi(hdr2)); rec = csv_encode_record(&csv, rec, 2, hdr1, hdr2); diff --git a/lib/debug.c b/lib/debug.c index 72fd4648ee..3248ceb13b 100644 --- a/lib/debug.c +++ b/lib/debug.c @@ -18,29 +18,46 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include +#include "typesafe.h" #include "debug.h" #include "command.h" -static const struct debug_callbacks *callbacks; +static struct debug_cb_list_head cb_head; + +DECLARE_LIST(debug_cb_list, struct debug_callbacks, item) /* All code in this section should be reentrant and MT-safe */ DEFUN_NOSH(debug_all, debug_all_cmd, "[no] debug all", NO_STR DEBUG_STR "Toggle all debugging output\n") { + struct debug_callbacks *cb; + bool set = !strmatch(argv[0]->text, "no"); uint32_t mode = DEBUG_NODE2MODE(vty->node); - if (callbacks->debug_set_all) - callbacks->debug_set_all(mode, set); + frr_each (debug_cb_list, &cb_head, cb) + cb->debug_set_all(mode, set); + return CMD_SUCCESS; } /* ------------------------------------------------------------------------- */ -void debug_init(const struct debug_callbacks *cb) +void debug_init(struct debug_callbacks *cb) +{ + static bool inited = false; + + if (!inited) { + inited = true; + debug_cb_list_init(&cb_head); + } + + debug_cb_list_add_head(&cb_head, cb); +} + +void debug_init_cli(void) { - callbacks = cb; install_element(ENABLE_NODE, &debug_all_cmd); install_element(CONFIG_NODE, &debug_all_cmd); } diff --git a/lib/debug.h b/lib/debug.h index ace060d057..f25cd42691 100644 --- a/lib/debug.h +++ b/lib/debug.h @@ -84,6 +84,7 @@ struct debug { const char *desc; }; +PREDECL_LIST(debug_cb_list) /* * Callback set for debugging code. * @@ -92,6 +93,11 @@ struct debug { * mode set. */ struct debug_callbacks { + /* + * Linked list of Callbacks to call + */ + struct debug_cb_list_item item; + /* * flags * flags to set on debug flag fields @@ -233,7 +239,13 @@ struct debug_callbacks { * * MT-Safe */ -void debug_init(const struct debug_callbacks *cb); +void debug_init(struct debug_callbacks *cb); + +/* + * Turn on the cli to turn on/off debugs. + * Should only be called by libfrr + */ +void debug_init_cli(void); #ifdef __cplusplus } diff --git a/lib/defaults.c b/lib/defaults.c new file mode 100644 index 0000000000..7466aad5b1 --- /dev/null +++ b/lib/defaults.c @@ -0,0 +1,230 @@ +/* + * FRR switchable defaults. + * Copyright (c) 2017-2019 David Lamparter, for NetDEF, Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include "defaults.h" +#include "version.h" + +static char df_version[128] = FRR_VER_SHORT, df_profile[128] = DFLT_NAME; +static struct frr_default *dflt_first = NULL, **dflt_next = &dflt_first; + +/* these are global for all FRR daemons. they have to be, since we write an + * integrated config with the same value for all daemons. + */ +const char *frr_defaults_profiles[] = { + "traditional", + "datacenter", + NULL, +}; + +static int version_value(int ch) +{ + /* non-ASCII shouldn't happen */ + if (ch < 0 || ch >= 128) + return 2; + + /* ~foo sorts older than nothing */ + if (ch == '~') + return 0; + if (ch == '\0') + return 1; + if (isalpha(ch)) + return 0x100 + tolower(ch); + + /* punctuation and digits (and everything else) */ + return 0x200 + ch; +} + +int frr_version_cmp(const char *aa, const char *bb) +{ + const char *apos = aa, *bpos = bb; + + /* || is correct, we won't scan past the end of a string since that + * doesn't compare equal to anything else */ + while (apos[0] || bpos[0]) { + if (isdigit((unsigned char)apos[0]) && + isdigit((unsigned char)bpos[0])) { + unsigned long av, bv; + char *aend = NULL, *bend = NULL; + + av = strtoul(apos, &aend, 10); + bv = strtoul(bpos, &bend, 10); + if (av < bv) + return -1; + if (av > bv) + return 1; + + apos = aend; + bpos = bend; + continue; + } + + int a = version_value(*apos++); + int b = version_value(*bpos++); + + if (a < b) + return -1; + if (a > b) + return 1; + } + return 0; +} + +static void frr_default_apply_one(struct frr_default *dflt, bool check); + +void frr_default_add(struct frr_default *dflt) +{ + dflt->next = NULL; + *dflt_next = dflt; + dflt_next = &dflt->next; + + frr_default_apply_one(dflt, true); +} + +static bool frr_match_version(const char *name, const char *vspec, + const char *version, bool check) +{ + int cmp; + static const struct spec { + const char *str; + int dir, eq; + } specs[] = { + {"<=", -1, 1}, + {">=", 1, 1}, + {"==", 0, 1}, + {"<", -1, 0}, + {">", 1, 0}, + {"=", 0, 1}, + {NULL, 0, 0}, + }; + const struct spec *s; + + if (!vspec) + /* NULL = all versions */ + return true; + + for (s = specs; s->str; s++) + if (!strncmp(s->str, vspec, strlen(s->str))) + break; + if (!s->str) { + if (check) + fprintf(stderr, "invalid version specifier for %s: %s", + name, vspec); + /* invalid version spec, never matches */ + return false; + } + + vspec += strlen(s->str); + while (isspace((unsigned char)*vspec)) + vspec++; + + cmp = frr_version_cmp(version, vspec); + if (cmp == s->dir || (s->eq && cmp == 0)) + return true; + + return false; +} + +static void frr_default_apply_one(struct frr_default *dflt, bool check) +{ + struct frr_default_entry *entry = dflt->entries; + struct frr_default_entry *dfltentry = NULL, *saveentry = NULL; + + for (; entry->match_version || entry->match_profile; entry++) { + if (entry->match_profile + && strcmp(entry->match_profile, df_profile)) + continue; + + if (!dfltentry && frr_match_version(dflt->name, + entry->match_version, df_version, check)) + dfltentry = entry; + if (!saveentry && frr_match_version(dflt->name, + entry->match_version, FRR_VER_SHORT, check)) + saveentry = entry; + + if (dfltentry && saveentry && !check) + break; + } + /* found default or arrived at last entry that has NULL,NULL spec */ + + if (!dfltentry) + dfltentry = entry; + if (!saveentry) + saveentry = entry; + + if (dflt->dflt_bool) + *dflt->dflt_bool = dfltentry->val_bool; + if (dflt->dflt_str) + *dflt->dflt_str = dfltentry->val_str; + if (dflt->dflt_long) + *dflt->dflt_long = dfltentry->val_long; + if (dflt->dflt_ulong) + *dflt->dflt_ulong = dfltentry->val_ulong; + if (dflt->dflt_float) + *dflt->dflt_float = dfltentry->val_float; + if (dflt->save_bool) + *dflt->save_bool = saveentry->val_bool; + if (dflt->save_str) + *dflt->save_str = saveentry->val_str; + if (dflt->save_long) + *dflt->save_long = saveentry->val_long; + if (dflt->save_ulong) + *dflt->save_ulong = saveentry->val_ulong; + if (dflt->save_float) + *dflt->save_float = saveentry->val_float; +} + +void frr_defaults_apply(void) +{ + struct frr_default *dflt; + + for (dflt = dflt_first; dflt; dflt = dflt->next) + frr_default_apply_one(dflt, false); +} + +bool frr_defaults_profile_valid(const char *profile) +{ + const char **p; + + for (p = frr_defaults_profiles; *p; p++) + if (!strcmp(profile, *p)) + return true; + return false; +} + +const char *frr_defaults_version(void) +{ + return df_version; +} + +const char *frr_defaults_profile(void) +{ + return df_profile; +} + +void frr_defaults_version_set(const char *version) +{ + strlcpy(df_version, version, sizeof(df_version)); + frr_defaults_apply(); +} + +void frr_defaults_profile_set(const char *profile) +{ + strlcpy(df_profile, profile, sizeof(df_profile)); + frr_defaults_apply(); +} diff --git a/lib/defaults.h b/lib/defaults.h new file mode 100644 index 0000000000..20ef28db31 --- /dev/null +++ b/lib/defaults.h @@ -0,0 +1,146 @@ +/* + * FRR switchable defaults. + * Copyright (C) 2017-2019 David Lamparter for NetDEF, Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _FRR_DEFAULTS_H +#define _FRR_DEFAULTS_H + +#include + +#include "compiler.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* frr_default wraps information about a default that has different + * values depending on FRR version or default-set + * + * frr_default_entry describes one match rule and the resulting value; + * entries are evaluated in order and the first matching is used. + * + * If both match_version and match_profile are specified, they must both + * match. A NULL value matches everything. + */ +struct frr_default_entry { + /* syntax: "(<|<=|==|>=|>) [whitespace] version", e.g. + * ">= 6.1-dev" "<6.0" + */ + const char *match_version; + /* exact profile string to compare against */ + const char *match_profile; + + /* value to use */ + bool val_bool; + const char *val_str; + long val_long; + unsigned long val_ulong; + float val_float; +}; + +/* one struct frr_default exists for each malleable default value */ +struct frr_default { + struct frr_default *next; + + /* for UI/debug use */ + const char *name; + + /* the following two sets of variables differ because the written + * config always targets the *current* FRR version + * + * e.g. if you load a config that has "frr version 5.0" on 6.0 + * *dflt_long => set to the default value in 5.0 + * *save_long => set to the default value in 6.0 + * config save will write "frr version 6.0" with 6.0 defaults + */ + + /* variable holding the default value for reading/use */ + bool *dflt_bool; + const char **dflt_str; + long *dflt_long; + unsigned long *dflt_ulong; + float *dflt_float; + + /* variable to use when comparing for config save */ + bool *save_bool; + const char **save_str; + long *save_long; + unsigned long *save_ulong; + float *save_float; + + struct frr_default_entry entries[]; +}; + +#define _FRR_CFG_DEFAULT(type, typname, varname, ...) \ + static type DFLT_##varname; \ + static type SAVE_##varname; \ + static struct frr_default _dflt_##varname = { \ + .name = #varname, \ + .dflt_##typname = &DFLT_##varname, \ + .save_##typname = &SAVE_##varname, \ + .entries = { __VA_ARGS__ }, \ + }; \ + static void _dfltinit_##varname(void) \ + __attribute__((_CONSTRUCTOR(1000))); \ + static void _dfltinit_##varname(void) \ + { \ + frr_default_add(&_dflt_##varname); \ + } + +/* use: + * FRR_CFG_DEFAULT_LONG(SHARP_BLUNTNESS, + * { .val_long = 2, .match_version = ">= 10.0" }, + * { .val_long = 1, .match_profile = "datacenter" }, + * { .val_long = 0 }, + * ) + * + * This will create DFLT_SHARP_BLUNTNESS and SAVE_SHARP_BLUNTNESS variables. + * + * Note: preprocessor defines cannot be used as variable names because they + * will be expanded and blow up with a compile error. Use an enum or add an + * extra _ at the beginning (e.g. _SHARP_BLUNTNESS => DFLT__SHARP_BLUNTNESS) + */ +#define FRR_CFG_DEFAULT_BOOL(varname, ...) \ + _FRR_CFG_DEFAULT(bool, bool, varname, ## __VA_ARGS__) +#define FRR_CFG_DEFAULT_LONG(varname, ...) \ + _FRR_CFG_DEFAULT(long, long, varname, ## __VA_ARGS__) +#define FRR_CFG_DEFAULT_ULONG(varname, ...) \ + _FRR_CFG_DEFAULT(unsigned long, ulong, varname, ## __VA_ARGS__) +#define FRR_CFG_DEFAULT_FLOAT(varname, ...) \ + _FRR_CFG_DEFAULT(float, float, varname, ## __VA_ARGS__) +#define FRR_CFG_DEFAULT_STR(varname, ...) \ + _FRR_CFG_DEFAULT(const char *, str, varname, ## __VA_ARGS__) + + +/* daemons don't need to call any of these, libfrr handles that */ +extern void frr_default_add(struct frr_default *dflt); +extern void frr_defaults_version_set(const char *version); +extern void frr_defaults_profile_set(const char *profile); +extern const char *frr_defaults_version(void); +extern const char *frr_defaults_profile(void); +extern void frr_defaults_apply(void); + +extern const char *frr_defaults_profiles[]; +extern bool frr_defaults_profile_valid(const char *profile); + +/* like strcmp(), but with version ordering */ +extern int frr_version_cmp(const char *aa, const char *bb); + +#ifdef __cplusplus +} +#endif + +#endif /* _FRR_DEFAULTS_H */ diff --git a/lib/defun_lex.l b/lib/defun_lex.l index 6c0805a4fa..af506f13d6 100644 --- a/lib/defun_lex.l +++ b/lib/defun_lex.l @@ -80,6 +80,8 @@ static void extendbuf(char **what, const char *arg) } #define extend(x) extendbuf(&value, x) +#ifndef __clang_analyzer__ + %} ID [A-Za-z0-9_]+ @@ -140,6 +142,8 @@ SPECIAL [(),] "DEFPY_NOSH" value = strdup(yytext); return DEFUNNY; "DEFPY_ATTR" value = strdup(yytext); return DEFUNNY; "DEFPY_HIDDEN" value = strdup(yytext); return DEFUNNY; +"DEFPY_YANG" value = strdup(yytext); return DEFUNNY; +"DEFPY_YANG_NOSH" value = strdup(yytext); return DEFUNNY; "ALIAS" value = strdup(yytext); return DEFUNNY; "ALIAS_HIDDEN" value = strdup(yytext); return DEFUNNY; "install_element" value = strdup(yytext); return INSTALL; @@ -155,6 +159,8 @@ SPECIAL [(),] %% +#endif /* __clang_analyzer__ */ + static int yylex_clr(char **retbuf) { int rv = def_yylex(); @@ -163,7 +169,7 @@ static int yylex_clr(char **retbuf) return rv; } -static PyObject *get_args(void) +static PyObject *get_args(const char *filename, int lineno) { PyObject *pyObj = PyList_New(0); PyObject *pyArg = NULL; @@ -190,6 +196,13 @@ static PyObject *get_args(void) free(tval); continue; } + if (token == PREPROC) { + free(tval); + Py_DECREF(pyObj); + return PyErr_Format(PyExc_ValueError, + "%s:%d: cannot process CPP directive within argument list", + filename, lineno); + } if (token == SPECIAL) { if (depth == 1 && (tval[0] == ',' || tval[0] == ')')) { if (pyArg) @@ -244,7 +257,12 @@ PyObject *clippy_parse(PyObject *self, PyObject *args) case DEFUNNY: case INSTALL: case AUXILIARY: - pyArgs = get_args(); + pyArgs = get_args(filename, lineno); + if (!pyArgs) { + free(tval); + Py_DECREF(pyCont); + return NULL; + } pyItem = PyDict_New(); PyDict_SetItemString(pyItem, "type", PyUnicode_FromString(tval)); PyDict_SetItemString(pyItem, "args", pyArgs); @@ -260,6 +278,7 @@ PyObject *clippy_parse(PyObject *self, PyObject *args) pyItem = PyDict_New(); PyDict_SetItemString(pyItem, "type", PyUnicode_FromString("PREPROC")); PyDict_SetItemString(pyItem, "line", PyUnicode_FromString(tval)); + lineno--; break; } if (pyItem) { diff --git a/lib/distribute.c b/lib/distribute.c index be40bd2603..3ea60c8772 100644 --- a/lib/distribute.c +++ b/lib/distribute.c @@ -131,7 +131,7 @@ static struct distribute *distribute_get(struct distribute_ctx *ctx, return ret; } -static unsigned int distribute_hash_make(void *arg) +static unsigned int distribute_hash_make(const void *arg) { const struct distribute *dist = arg; @@ -186,7 +186,6 @@ static int distribute_list_unset(struct distribute_ctx *ctx, return 0; XFREE(MTYPE_DISTRIBUTE_NAME, dist->list[type]); - dist->list[type] = NULL; /* Apply this distribute-list to the interface. */ (ctx->distribute_delete_hook)(ctx, dist); @@ -232,7 +231,6 @@ static int distribute_list_prefix_unset(struct distribute_ctx *ctx, return 0; XFREE(MTYPE_DISTRIBUTE_NAME, dist->prefix[type]); - dist->prefix[type] = NULL; /* Apply this distribute-list to the interface. */ (ctx->distribute_delete_hook)(ctx, dist); diff --git a/lib/ferr.c b/lib/ferr.c index d7fa1a84f8..691da495cf 100644 --- a/lib/ferr.c +++ b/lib/ferr.c @@ -33,6 +33,7 @@ #include "command.h" #include "json.h" #include "linklist.h" +#include "frr_pthread.h" DEFINE_MTYPE_STATIC(LIB, ERRINFO, "error information") @@ -72,9 +73,9 @@ static bool ferr_hash_cmp(const void *a, const void *b) return f_a->code == f_b->code; } -static inline unsigned int ferr_hash_key(void *a) +static inline unsigned int ferr_hash_key(const void *a) { - struct log_ref *f = a; + const struct log_ref *f = a; return f->code; } @@ -83,14 +84,12 @@ void log_ref_add(struct log_ref *ref) { uint32_t i = 0; - pthread_mutex_lock(&refs_mtx); - { + frr_with_mutex(&refs_mtx) { while (ref[i].code != END_FERR) { hash_get(refs, &ref[i], hash_alloc_intern); i++; } } - pthread_mutex_unlock(&refs_mtx); } struct log_ref *log_ref_get(uint32_t code) @@ -99,11 +98,9 @@ struct log_ref *log_ref_get(uint32_t code) struct log_ref *ref; holder.code = code; - pthread_mutex_lock(&refs_mtx); - { + frr_with_mutex(&refs_mtx) { ref = hash_lookup(refs, &holder); } - pthread_mutex_unlock(&refs_mtx); return ref; } @@ -118,16 +115,16 @@ void log_ref_display(struct vty *vty, uint32_t code, bool json) if (json) top = json_object_new_object(); - pthread_mutex_lock(&refs_mtx); - { + frr_with_mutex(&refs_mtx) { errlist = code ? list_new() : hash_to_list(refs); } - pthread_mutex_unlock(&refs_mtx); if (code) { ref = log_ref_get(code); if (!ref) { - vty_out(vty, "Code %"PRIu32" - Unknown\n", code); + if (top) + json_object_free(top); + list_delete(&errlist); return; } listnode_add(errlist, ref); @@ -137,7 +134,7 @@ void log_ref_display(struct vty *vty, uint32_t code, bool json) if (json) { char key[11]; - snprintf(key, sizeof(key), "%"PRIu32, ref->code); + snprintf(key, sizeof(key), "%u", ref->code); obj = json_object_new_object(); json_object_string_add(obj, "title", ref->title); json_object_string_add(obj, "description", @@ -149,7 +146,7 @@ void log_ref_display(struct vty *vty, uint32_t code, bool json) char pbuf[256]; char ubuf[256]; - snprintf(pbuf, sizeof(pbuf), "\nError %"PRIu32" - %s", + snprintf(pbuf, sizeof(pbuf), "\nError %u - %s", ref->code, ref->title); memset(ubuf, '=', strlen(pbuf)); ubuf[strlen(pbuf)] = '\0'; @@ -172,7 +169,7 @@ void log_ref_display(struct vty *vty, uint32_t code, bool json) DEFUN_NOSH(show_error_code, show_error_code_cmd, - "show error <(1-4294967296)|all> [json]", + "show error <(1-4294967295)|all> [json]", SHOW_STR "Information on errors\n" "Error code to get info about\n" @@ -191,27 +188,27 @@ DEFUN_NOSH(show_error_code, void log_ref_init(void) { - pthread_mutex_lock(&refs_mtx); - { + frr_with_mutex(&refs_mtx) { refs = hash_create(ferr_hash_key, ferr_hash_cmp, "Error Reference Texts"); } - pthread_mutex_unlock(&refs_mtx); - - install_element(VIEW_NODE, &show_error_code_cmd); } void log_ref_fini(void) { - pthread_mutex_lock(&refs_mtx); - { + frr_with_mutex(&refs_mtx) { hash_clean(refs, NULL); hash_free(refs); refs = NULL; } - pthread_mutex_unlock(&refs_mtx); } +void log_ref_vty_init(void) +{ + install_element(VIEW_NODE, &show_error_code_cmd); +} + + const struct ferr *ferr_get_last(ferr_r errval) { struct ferr *last_error = pthread_getspecific(errkey); diff --git a/lib/ferr.h b/lib/ferr.h index 93d0ced538..a89b595e87 100644 --- a/lib/ferr.h +++ b/lib/ferr.h @@ -158,6 +158,7 @@ void log_ref_display(struct vty *vty, uint32_t code, bool json); */ void log_ref_init(void); void log_ref_fini(void); +void log_ref_vty_init(void); /* get error details. * diff --git a/lib/filter.c b/lib/filter.c index 276df4b4d7..07512d8667 100644 --- a/lib/filter.c +++ b/lib/filter.c @@ -29,67 +29,12 @@ #include "log.h" #include "routemap.h" #include "libfrr.h" +#include "northbound_cli.h" DEFINE_MTYPE_STATIC(LIB, ACCESS_LIST, "Access List") DEFINE_MTYPE_STATIC(LIB, ACCESS_LIST_STR, "Access List Str") DEFINE_MTYPE_STATIC(LIB, ACCESS_FILTER, "Access Filter") -struct filter_cisco { - /* Cisco access-list */ - int extended; - struct in_addr addr; - struct in_addr addr_mask; - struct in_addr mask; - struct in_addr mask_mask; -}; - -struct filter_zebra { - /* If this filter is "exact" match then this flag is set. */ - int exact; - - /* Prefix information. */ - struct prefix prefix; -}; - -/* Filter element of access list */ -struct filter { - /* For doubly linked list. */ - struct filter *next; - struct filter *prev; - - /* Filter type information. */ - enum filter_type type; - - /* Cisco access-list */ - int cisco; - - union { - struct filter_cisco cfilter; - struct filter_zebra zfilter; - } u; -}; - -/* List of access_list. */ -struct access_list_list { - struct access_list *head; - struct access_list *tail; -}; - -/* Master structure of access_list. */ -struct access_master { - /* List of access_list which name is number. */ - struct access_list_list num; - - /* List of access_list which name is string. */ - struct access_list_list str; - - /* Hook function which is executed when new access_list is added. */ - void (*add_hook)(struct access_list *); - - /* Hook function which is executed when access_list is deleted. */ - void (*delete_hook)(struct access_list *); -}; - /* Static structure for mac access_list's master. */ static struct access_master access_master_mac = { {NULL, NULL}, @@ -126,7 +71,7 @@ static struct access_master *access_master_get(afi_t afi) } /* Allocate new filter structure. */ -static struct filter *filter_new(void) +struct filter *filter_new(void) { return XCALLOC(MTYPE_ACCESS_FILTER, sizeof(struct filter)); } @@ -142,16 +87,12 @@ static const char *filter_type_str(struct filter *filter) switch (filter->type) { case FILTER_PERMIT: return "permit"; - break; case FILTER_DENY: return "deny"; - break; case FILTER_DYNAMIC: return "dynamic"; - break; default: return ""; - break; } } @@ -211,7 +152,7 @@ static void access_list_free(struct access_list *access) } /* Delete access_list from access_master and free it. */ -static void access_list_delete(struct access_list *access) +void access_list_delete(struct access_list *access) { struct filter *filter; struct filter *next; @@ -240,6 +181,11 @@ static void access_list_delete(struct access_list *access) else list->head = access->next; + route_map_notify_dependencies(access->name, RMAP_EVENT_FILTER_DELETED); + + if (master->delete_hook) + master->delete_hook(access); + XFREE(MTYPE_ACCESS_LIST_STR, access->name); XFREE(MTYPE_TMP, access->remark); @@ -270,7 +216,7 @@ static struct access_list *access_list_insert(afi_t afi, const char *name) /* If name is made by all digit character. We treat it as number. */ for (number = 0, i = 0; i < strlen(name); i++) { - if (isdigit((int)name[i])) + if (isdigit((unsigned char)name[i])) number = (number * 10) + (name[i] - '0'); else break; @@ -357,7 +303,7 @@ struct access_list *access_list_lookup(afi_t afi, const char *name) /* Get access list from list of access_list. If there isn't matched access_list create new one and return it. */ -static struct access_list *access_list_get(afi_t afi, const char *name) +struct access_list *access_list_get(afi_t afi, const char *name) { struct access_list *access; @@ -406,38 +352,41 @@ void access_list_delete_hook(void (*func)(struct access_list *access)) access_master_mac.delete_hook = func; } -/* Add new filter to the end of specified access_list. */ -static void access_list_filter_add(struct access_list *access, - struct filter *filter) +/* Calculate new sequential number. */ +int64_t filter_new_seq_get(struct access_list *access) { - filter->next = NULL; - filter->prev = access->tail; + int64_t maxseq; + int64_t newseq; + struct filter *filter; - if (access->tail) - access->tail->next = filter; - else - access->head = filter; - access->tail = filter; + maxseq = 0; - /* Run hook function. */ - if (access->master->add_hook) - (*access->master->add_hook)(access); - route_map_notify_dependencies(access->name, RMAP_EVENT_FILTER_ADDED); + for (filter = access->head; filter; filter = filter->next) { + if (maxseq < filter->seq) + maxseq = filter->seq; + } + + newseq = ((maxseq / 5) * 5) + 5; + + return (newseq > UINT_MAX) ? UINT_MAX : newseq; } -/* If access_list has no filter then return 1. */ -static int access_list_empty(struct access_list *access) +/* Return access list entry which has same seq number. */ +static struct filter *filter_seq_check(struct access_list *access, + int64_t seq) { - if (access->head == NULL && access->tail == NULL) - return 1; - else - return 0; + struct filter *filter; + + for (filter = access->head; filter; filter = filter->next) + if (filter->seq == seq) + return filter; + return NULL; } /* Delete filter from specified access_list. If there is hook function execute it. */ -static void access_list_filter_delete(struct access_list *access, - struct filter *filter) +void access_list_filter_delete(struct access_list *access, + struct filter *filter) { struct access_master *master; @@ -459,10 +408,58 @@ static void access_list_filter_delete(struct access_list *access, /* Run hook function. */ if (master->delete_hook) (*master->delete_hook)(access); +} - /* If access_list becomes empty delete it from access_master. */ - if (access_list_empty(access)) - access_list_delete(access); +/* Add new filter to the end of specified access_list. */ +void access_list_filter_add(struct access_list *access, + struct filter *filter) +{ + struct filter *replace; + struct filter *point; + + /* Automatic asignment of seq no. */ + if (filter->seq == -1) + filter->seq = filter_new_seq_get(access); + + if (access->tail && filter->seq > access->tail->seq) + point = NULL; + else { + /* Is there any same seq access list filter? */ + replace = filter_seq_check(access, filter->seq); + if (replace) + access_list_filter_delete(access, replace); + + /* Check insert point. */ + for (point = access->head; point; point = point->next) + if (point->seq >= filter->seq) + break; + } + + /* In case of this is the first element of the list. */ + filter->next = point; + + if (point) { + if (point->prev) + point->prev->next = filter; + else + access->head = filter; + + filter->prev = point->prev; + point->prev = filter; + } else { + if (access->tail) + access->tail->next = filter; + else + access->head = filter; + + filter->prev = access->tail; + access->tail = filter; + } + + /* Run hook function. */ + if (access->master->add_hook) + (*access->master->add_hook)(access); + route_map_notify_dependencies(access->name, RMAP_EVENT_FILTER_ADDED); } /* @@ -477,8 +474,8 @@ static void access_list_filter_delete(struct access_list *access, host A single host address */ -static struct filter *filter_lookup_cisco(struct access_list *access, - struct filter *mnew) +struct filter *filter_lookup_cisco(struct access_list *access, + struct filter *mnew) { struct filter *mfilter; struct filter_cisco *filter; @@ -509,8 +506,8 @@ static struct filter *filter_lookup_cisco(struct access_list *access, return NULL; } -static struct filter *filter_lookup_zebra(struct access_list *access, - struct filter *mnew) +struct filter *filter_lookup_zebra(struct access_list *access, + struct filter *mnew) { struct filter *mfilter; struct filter_zebra *filter; @@ -530,1184 +527,8 @@ static struct filter *filter_lookup_zebra(struct access_list *access, return NULL; } -static int vty_access_list_remark_unset(struct vty *vty, afi_t afi, - const char *name) -{ - struct access_list *access; - - access = access_list_lookup(afi, name); - if (!access) { - vty_out(vty, "%% access-list %s doesn't exist\n", name); - return CMD_WARNING_CONFIG_FAILED; - } - - if (access->remark) { - XFREE(MTYPE_TMP, access->remark); - access->remark = NULL; - } - - if (access->head == NULL && access->tail == NULL) - access_list_delete(access); - - return CMD_SUCCESS; -} - -static int filter_set_cisco(struct vty *vty, const char *name_str, - const char *type_str, const char *addr_str, - const char *addr_mask_str, const char *mask_str, - const char *mask_mask_str, int extended, int set) -{ - int ret; - enum filter_type type; - struct filter *mfilter; - struct filter_cisco *filter; - struct access_list *access; - struct in_addr addr; - struct in_addr addr_mask; - struct in_addr mask; - struct in_addr mask_mask; - - /* Check of filter type. */ - if (strncmp(type_str, "p", 1) == 0) - type = FILTER_PERMIT; - else if (strncmp(type_str, "d", 1) == 0) - type = FILTER_DENY; - else { - vty_out(vty, "%% filter type must be permit or deny\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - ret = inet_aton(addr_str, &addr); - if (ret <= 0) { - vty_out(vty, "%%Inconsistent address and mask\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - ret = inet_aton(addr_mask_str, &addr_mask); - if (ret <= 0) { - vty_out(vty, "%%Inconsistent address and mask\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - if (extended) { - ret = inet_aton(mask_str, &mask); - if (ret <= 0) { - vty_out(vty, "%%Inconsistent address and mask\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - ret = inet_aton(mask_mask_str, &mask_mask); - if (ret <= 0) { - vty_out(vty, "%%Inconsistent address and mask\n"); - return CMD_WARNING_CONFIG_FAILED; - } - } - - mfilter = filter_new(); - mfilter->type = type; - mfilter->cisco = 1; - filter = &mfilter->u.cfilter; - filter->extended = extended; - filter->addr.s_addr = addr.s_addr & ~addr_mask.s_addr; - filter->addr_mask.s_addr = addr_mask.s_addr; - - if (extended) { - filter->mask.s_addr = mask.s_addr & ~mask_mask.s_addr; - filter->mask_mask.s_addr = mask_mask.s_addr; - } - - /* Install new filter to the access_list. */ - access = access_list_get(AFI_IP, name_str); - - if (set) { - if (filter_lookup_cisco(access, mfilter)) - filter_free(mfilter); - else - access_list_filter_add(access, mfilter); - } else { - struct filter *delete_filter; - - delete_filter = filter_lookup_cisco(access, mfilter); - if (delete_filter) - access_list_filter_delete(access, delete_filter); - - filter_free(mfilter); - } - - return CMD_SUCCESS; -} - -/* Standard access-list */ -DEFUN (access_list_standard, - access_list_standard_cmd, - "access-list <(1-99)|(1300-1999)> A.B.C.D A.B.C.D", - "Add an access list entry\n" - "IP standard access list\n" - "IP standard access list (expanded range)\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Address to match\n" - "Wildcard bits\n") -{ - int idx_acl = 1; - int idx_permit_deny = 2; - int idx_ipv4 = 3; - int idx_ipv4_2 = 4; - return filter_set_cisco(vty, argv[idx_acl]->arg, - argv[idx_permit_deny]->arg, argv[idx_ipv4]->arg, - argv[idx_ipv4_2]->arg, NULL, NULL, 0, 1); -} - -DEFUN (access_list_standard_nomask, - access_list_standard_nomask_cmd, - "access-list <(1-99)|(1300-1999)> A.B.C.D", - "Add an access list entry\n" - "IP standard access list\n" - "IP standard access list (expanded range)\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Address to match\n") -{ - int idx_acl = 1; - int idx_permit_deny = 2; - int idx_ipv4 = 3; - return filter_set_cisco(vty, argv[idx_acl]->arg, - argv[idx_permit_deny]->arg, argv[idx_ipv4]->arg, - "0.0.0.0", NULL, NULL, 0, 1); -} - -DEFUN (access_list_standard_host, - access_list_standard_host_cmd, - "access-list <(1-99)|(1300-1999)> host A.B.C.D", - "Add an access list entry\n" - "IP standard access list\n" - "IP standard access list (expanded range)\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "A single host address\n" - "Address to match\n") -{ - int idx_acl = 1; - int idx_permit_deny = 2; - int idx_ipv4 = 4; - return filter_set_cisco(vty, argv[idx_acl]->arg, - argv[idx_permit_deny]->arg, argv[idx_ipv4]->arg, - "0.0.0.0", NULL, NULL, 0, 1); -} - -DEFUN (access_list_standard_any, - access_list_standard_any_cmd, - "access-list <(1-99)|(1300-1999)> any", - "Add an access list entry\n" - "IP standard access list\n" - "IP standard access list (expanded range)\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Any source host\n") -{ - int idx_acl = 1; - int idx_permit_deny = 2; - return filter_set_cisco(vty, argv[idx_acl]->arg, - argv[idx_permit_deny]->arg, "0.0.0.0", - "255.255.255.255", NULL, NULL, 0, 1); -} - -DEFUN (no_access_list_standard, - no_access_list_standard_cmd, - "no access-list <(1-99)|(1300-1999)> A.B.C.D A.B.C.D", - NO_STR - "Add an access list entry\n" - "IP standard access list\n" - "IP standard access list (expanded range)\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Address to match\n" - "Wildcard bits\n") -{ - int idx_acl = 2; - int idx_permit_deny = 3; - int idx_ipv4 = 4; - int idx_ipv4_2 = 5; - return filter_set_cisco(vty, argv[idx_acl]->arg, - argv[idx_permit_deny]->arg, argv[idx_ipv4]->arg, - argv[idx_ipv4_2]->arg, NULL, NULL, 0, 0); -} - -DEFUN (no_access_list_standard_nomask, - no_access_list_standard_nomask_cmd, - "no access-list <(1-99)|(1300-1999)> A.B.C.D", - NO_STR - "Add an access list entry\n" - "IP standard access list\n" - "IP standard access list (expanded range)\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Address to match\n") -{ - int idx_acl = 2; - int idx_permit_deny = 3; - int idx_ipv4 = 4; - return filter_set_cisco(vty, argv[idx_acl]->arg, - argv[idx_permit_deny]->arg, argv[idx_ipv4]->arg, - "0.0.0.0", NULL, NULL, 0, 0); -} - -DEFUN (no_access_list_standard_host, - no_access_list_standard_host_cmd, - "no access-list <(1-99)|(1300-1999)> host A.B.C.D", - NO_STR - "Add an access list entry\n" - "IP standard access list\n" - "IP standard access list (expanded range)\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "A single host address\n" - "Address to match\n") -{ - int idx_acl = 2; - int idx_permit_deny = 3; - int idx_ipv4 = 5; - return filter_set_cisco(vty, argv[idx_acl]->arg, - argv[idx_permit_deny]->arg, argv[idx_ipv4]->arg, - "0.0.0.0", NULL, NULL, 0, 0); -} - -DEFUN (no_access_list_standard_any, - no_access_list_standard_any_cmd, - "no access-list <(1-99)|(1300-1999)> any", - NO_STR - "Add an access list entry\n" - "IP standard access list\n" - "IP standard access list (expanded range)\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Any source host\n") -{ - int idx_acl = 2; - int idx_permit_deny = 3; - return filter_set_cisco(vty, argv[idx_acl]->arg, - argv[idx_permit_deny]->arg, "0.0.0.0", - "255.255.255.255", NULL, NULL, 0, 0); -} - -/* Extended access-list */ -DEFUN (access_list_extended, - access_list_extended_cmd, - "access-list <(100-199)|(2000-2699)> ip A.B.C.D A.B.C.D A.B.C.D A.B.C.D", - "Add an access list entry\n" - "IP extended access list\n" - "IP extended access list (expanded range)\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Any Internet Protocol\n" - "Source address\n" - "Source wildcard bits\n" - "Destination address\n" - "Destination Wildcard bits\n") -{ - int idx_acl = 1; - int idx_permit_deny = 2; - int idx_ipv4 = 4; - int idx_ipv4_2 = 5; - int idx_ipv4_3 = 6; - int idx_ipv4_4 = 7; - return filter_set_cisco(vty, argv[idx_acl]->arg, - argv[idx_permit_deny]->arg, argv[idx_ipv4]->arg, - argv[idx_ipv4_2]->arg, argv[idx_ipv4_3]->arg, - argv[idx_ipv4_4]->arg, 1, 1); -} - -DEFUN (access_list_extended_mask_any, - access_list_extended_mask_any_cmd, - "access-list <(100-199)|(2000-2699)> ip A.B.C.D A.B.C.D any", - "Add an access list entry\n" - "IP extended access list\n" - "IP extended access list (expanded range)\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Any Internet Protocol\n" - "Source address\n" - "Source wildcard bits\n" - "Any destination host\n") -{ - int idx_acl = 1; - int idx_permit_deny = 2; - int idx_ipv4 = 4; - int idx_ipv4_2 = 5; - return filter_set_cisco(vty, argv[idx_acl]->arg, - argv[idx_permit_deny]->arg, argv[idx_ipv4]->arg, - argv[idx_ipv4_2]->arg, "0.0.0.0", - "255.255.255.255", 1, 1); -} - -DEFUN (access_list_extended_any_mask, - access_list_extended_any_mask_cmd, - "access-list <(100-199)|(2000-2699)> ip any A.B.C.D A.B.C.D", - "Add an access list entry\n" - "IP extended access list\n" - "IP extended access list (expanded range)\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Any Internet Protocol\n" - "Any source host\n" - "Destination address\n" - "Destination Wildcard bits\n") -{ - int idx_acl = 1; - int idx_permit_deny = 2; - int idx_ipv4 = 5; - int idx_ipv4_2 = 6; - return filter_set_cisco(vty, argv[idx_acl]->arg, - argv[idx_permit_deny]->arg, "0.0.0.0", - "255.255.255.255", argv[idx_ipv4]->arg, - argv[idx_ipv4_2]->arg, 1, 1); -} - -DEFUN (access_list_extended_any_any, - access_list_extended_any_any_cmd, - "access-list <(100-199)|(2000-2699)> ip any any", - "Add an access list entry\n" - "IP extended access list\n" - "IP extended access list (expanded range)\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Any Internet Protocol\n" - "Any source host\n" - "Any destination host\n") -{ - int idx_acl = 1; - int idx_permit_deny = 2; - return filter_set_cisco( - vty, argv[idx_acl]->arg, argv[idx_permit_deny]->arg, "0.0.0.0", - "255.255.255.255", "0.0.0.0", "255.255.255.255", 1, 1); -} - -DEFUN (access_list_extended_mask_host, - access_list_extended_mask_host_cmd, - "access-list <(100-199)|(2000-2699)> ip A.B.C.D A.B.C.D host A.B.C.D", - "Add an access list entry\n" - "IP extended access list\n" - "IP extended access list (expanded range)\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Any Internet Protocol\n" - "Source address\n" - "Source wildcard bits\n" - "A single destination host\n" - "Destination address\n") -{ - int idx_acl = 1; - int idx_permit_deny = 2; - int idx_ipv4 = 4; - int idx_ipv4_2 = 5; - int idx_ipv4_3 = 7; - return filter_set_cisco(vty, argv[idx_acl]->arg, - argv[idx_permit_deny]->arg, argv[idx_ipv4]->arg, - argv[idx_ipv4_2]->arg, argv[idx_ipv4_3]->arg, - "0.0.0.0", 1, 1); -} - -DEFUN (access_list_extended_host_mask, - access_list_extended_host_mask_cmd, - "access-list <(100-199)|(2000-2699)> ip host A.B.C.D A.B.C.D A.B.C.D", - "Add an access list entry\n" - "IP extended access list\n" - "IP extended access list (expanded range)\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Any Internet Protocol\n" - "A single source host\n" - "Source address\n" - "Destination address\n" - "Destination Wildcard bits\n") -{ - int idx_acl = 1; - int idx_permit_deny = 2; - int idx_ipv4 = 5; - int idx_ipv4_2 = 6; - int idx_ipv4_3 = 7; - return filter_set_cisco(vty, argv[idx_acl]->arg, - argv[idx_permit_deny]->arg, argv[idx_ipv4]->arg, - "0.0.0.0", argv[idx_ipv4_2]->arg, - argv[idx_ipv4_3]->arg, 1, 1); -} - -DEFUN (access_list_extended_host_host, - access_list_extended_host_host_cmd, - "access-list <(100-199)|(2000-2699)> ip host A.B.C.D host A.B.C.D", - "Add an access list entry\n" - "IP extended access list\n" - "IP extended access list (expanded range)\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Any Internet Protocol\n" - "A single source host\n" - "Source address\n" - "A single destination host\n" - "Destination address\n") -{ - int idx_acl = 1; - int idx_permit_deny = 2; - int idx_ipv4 = 5; - int idx_ipv4_2 = 7; - return filter_set_cisco(vty, argv[idx_acl]->arg, - argv[idx_permit_deny]->arg, argv[idx_ipv4]->arg, - "0.0.0.0", argv[idx_ipv4_2]->arg, "0.0.0.0", 1, - 1); -} - -DEFUN (access_list_extended_any_host, - access_list_extended_any_host_cmd, - "access-list <(100-199)|(2000-2699)> ip any host A.B.C.D", - "Add an access list entry\n" - "IP extended access list\n" - "IP extended access list (expanded range)\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Any Internet Protocol\n" - "Any source host\n" - "A single destination host\n" - "Destination address\n") -{ - int idx_acl = 1; - int idx_permit_deny = 2; - int idx_ipv4 = 6; - return filter_set_cisco( - vty, argv[idx_acl]->arg, argv[idx_permit_deny]->arg, "0.0.0.0", - "255.255.255.255", argv[idx_ipv4]->arg, "0.0.0.0", 1, 1); -} - -DEFUN (access_list_extended_host_any, - access_list_extended_host_any_cmd, - "access-list <(100-199)|(2000-2699)> ip host A.B.C.D any", - "Add an access list entry\n" - "IP extended access list\n" - "IP extended access list (expanded range)\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Any Internet Protocol\n" - "A single source host\n" - "Source address\n" - "Any destination host\n") -{ - int idx_acl = 1; - int idx_permit_deny = 2; - int idx_ipv4 = 5; - return filter_set_cisco(vty, argv[idx_acl]->arg, - argv[idx_permit_deny]->arg, argv[idx_ipv4]->arg, - "0.0.0.0", "0.0.0.0", "255.255.255.255", 1, 1); -} - -DEFUN (no_access_list_extended, - no_access_list_extended_cmd, - "no access-list <(100-199)|(2000-2699)> ip A.B.C.D A.B.C.D A.B.C.D A.B.C.D", - NO_STR - "Add an access list entry\n" - "IP extended access list\n" - "IP extended access list (expanded range)\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Any Internet Protocol\n" - "Source address\n" - "Source wildcard bits\n" - "Destination address\n" - "Destination Wildcard bits\n") -{ - int idx_acl = 2; - int idx_permit_deny = 3; - int idx_ipv4 = 5; - int idx_ipv4_2 = 6; - int idx_ipv4_3 = 7; - int idx_ipv4_4 = 8; - return filter_set_cisco(vty, argv[idx_acl]->arg, - argv[idx_permit_deny]->arg, argv[idx_ipv4]->arg, - argv[idx_ipv4_2]->arg, argv[idx_ipv4_3]->arg, - argv[idx_ipv4_4]->arg, 1, 0); -} - -DEFUN (no_access_list_extended_mask_any, - no_access_list_extended_mask_any_cmd, - "no access-list <(100-199)|(2000-2699)> ip A.B.C.D A.B.C.D any", - NO_STR - "Add an access list entry\n" - "IP extended access list\n" - "IP extended access list (expanded range)\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Any Internet Protocol\n" - "Source address\n" - "Source wildcard bits\n" - "Any destination host\n") -{ - int idx_acl = 2; - int idx_permit_deny = 3; - int idx_ipv4 = 5; - int idx_ipv4_2 = 6; - return filter_set_cisco(vty, argv[idx_acl]->arg, - argv[idx_permit_deny]->arg, argv[idx_ipv4]->arg, - argv[idx_ipv4_2]->arg, "0.0.0.0", - "255.255.255.255", 1, 0); -} - -DEFUN (no_access_list_extended_any_mask, - no_access_list_extended_any_mask_cmd, - "no access-list <(100-199)|(2000-2699)> ip any A.B.C.D A.B.C.D", - NO_STR - "Add an access list entry\n" - "IP extended access list\n" - "IP extended access list (expanded range)\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Any Internet Protocol\n" - "Any source host\n" - "Destination address\n" - "Destination Wildcard bits\n") -{ - int idx_acl = 2; - int idx_permit_deny = 3; - int idx_ipv4 = 6; - int idx_ipv4_2 = 7; - return filter_set_cisco(vty, argv[idx_acl]->arg, - argv[idx_permit_deny]->arg, "0.0.0.0", - "255.255.255.255", argv[idx_ipv4]->arg, - argv[idx_ipv4_2]->arg, 1, 0); -} - -DEFUN (no_access_list_extended_any_any, - no_access_list_extended_any_any_cmd, - "no access-list <(100-199)|(2000-2699)> ip any any", - NO_STR - "Add an access list entry\n" - "IP extended access list\n" - "IP extended access list (expanded range)\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Any Internet Protocol\n" - "Any source host\n" - "Any destination host\n") -{ - int idx_acl = 2; - int idx_permit_deny = 3; - return filter_set_cisco( - vty, argv[idx_acl]->arg, argv[idx_permit_deny]->arg, "0.0.0.0", - "255.255.255.255", "0.0.0.0", "255.255.255.255", 1, 0); -} - -DEFUN (no_access_list_extended_mask_host, - no_access_list_extended_mask_host_cmd, - "no access-list <(100-199)|(2000-2699)> ip A.B.C.D A.B.C.D host A.B.C.D", - NO_STR - "Add an access list entry\n" - "IP extended access list\n" - "IP extended access list (expanded range)\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Any Internet Protocol\n" - "Source address\n" - "Source wildcard bits\n" - "A single destination host\n" - "Destination address\n") -{ - int idx_acl = 2; - int idx_permit_deny = 3; - int idx_ipv4 = 5; - int idx_ipv4_2 = 6; - int idx_ipv4_3 = 8; - return filter_set_cisco(vty, argv[idx_acl]->arg, - argv[idx_permit_deny]->arg, argv[idx_ipv4]->arg, - argv[idx_ipv4_2]->arg, argv[idx_ipv4_3]->arg, - "0.0.0.0", 1, 0); -} - -DEFUN (no_access_list_extended_host_mask, - no_access_list_extended_host_mask_cmd, - "no access-list <(100-199)|(2000-2699)> ip host A.B.C.D A.B.C.D A.B.C.D", - NO_STR - "Add an access list entry\n" - "IP extended access list\n" - "IP extended access list (expanded range)\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Any Internet Protocol\n" - "A single source host\n" - "Source address\n" - "Destination address\n" - "Destination Wildcard bits\n") -{ - int idx_acl = 2; - int idx_permit_deny = 3; - int idx_ipv4 = 6; - int idx_ipv4_2 = 7; - int idx_ipv4_3 = 8; - return filter_set_cisco(vty, argv[idx_acl]->arg, - argv[idx_permit_deny]->arg, argv[idx_ipv4]->arg, - "0.0.0.0", argv[idx_ipv4_2]->arg, - argv[idx_ipv4_3]->arg, 1, 0); -} - -DEFUN (no_access_list_extended_host_host, - no_access_list_extended_host_host_cmd, - "no access-list <(100-199)|(2000-2699)> ip host A.B.C.D host A.B.C.D", - NO_STR - "Add an access list entry\n" - "IP extended access list\n" - "IP extended access list (expanded range)\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Any Internet Protocol\n" - "A single source host\n" - "Source address\n" - "A single destination host\n" - "Destination address\n") -{ - int idx_acl = 2; - int idx_permit_deny = 3; - int idx_ipv4 = 6; - int idx_ipv4_2 = 8; - return filter_set_cisco(vty, argv[idx_acl]->arg, - argv[idx_permit_deny]->arg, argv[idx_ipv4]->arg, - "0.0.0.0", argv[idx_ipv4_2]->arg, "0.0.0.0", 1, - 0); -} - -DEFUN (no_access_list_extended_any_host, - no_access_list_extended_any_host_cmd, - "no access-list <(100-199)|(2000-2699)> ip any host A.B.C.D", - NO_STR - "Add an access list entry\n" - "IP extended access list\n" - "IP extended access list (expanded range)\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Any Internet Protocol\n" - "Any source host\n" - "A single destination host\n" - "Destination address\n") -{ - int idx_acl = 2; - int idx_permit_deny = 3; - int idx_ipv4 = 7; - return filter_set_cisco( - vty, argv[idx_acl]->arg, argv[idx_permit_deny]->arg, "0.0.0.0", - "255.255.255.255", argv[idx_ipv4]->arg, "0.0.0.0", 1, 0); -} - -DEFUN (no_access_list_extended_host_any, - no_access_list_extended_host_any_cmd, - "no access-list <(100-199)|(2000-2699)> ip host A.B.C.D any", - NO_STR - "Add an access list entry\n" - "IP extended access list\n" - "IP extended access list (expanded range)\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Any Internet Protocol\n" - "A single source host\n" - "Source address\n" - "Any destination host\n") -{ - int idx_acl = 2; - int idx_permit_deny = 3; - int idx_ipv4 = 6; - return filter_set_cisco(vty, argv[idx_acl]->arg, - argv[idx_permit_deny]->arg, argv[idx_ipv4]->arg, - "0.0.0.0", "0.0.0.0", "255.255.255.255", 1, 0); -} - -static int filter_set_zebra(struct vty *vty, const char *name_str, - const char *type_str, afi_t afi, - const char *prefix_str, int exact, int set) -{ - int ret; - enum filter_type type; - struct filter *mfilter; - struct filter_zebra *filter; - struct access_list *access; - struct prefix p; - - if (strlen(name_str) > ACL_NAMSIZ) { - vty_out(vty, - "%% ACL name %s is invalid: length exceeds " - "%d characters\n", - name_str, ACL_NAMSIZ); - return CMD_WARNING_CONFIG_FAILED; - } - - /* Check of filter type. */ - if (strncmp(type_str, "p", 1) == 0) - type = FILTER_PERMIT; - else if (strncmp(type_str, "d", 1) == 0) - type = FILTER_DENY; - else { - vty_out(vty, "filter type must be [permit|deny]\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - /* Check string format of prefix and prefixlen. */ - if (afi == AFI_IP) { - ret = str2prefix_ipv4(prefix_str, (struct prefix_ipv4 *)&p); - if (ret <= 0) { - vty_out(vty, - "IP address prefix/prefixlen is malformed\n"); - return CMD_WARNING_CONFIG_FAILED; - } - } else if (afi == AFI_IP6) { - ret = str2prefix_ipv6(prefix_str, (struct prefix_ipv6 *)&p); - if (ret <= 0) { - vty_out(vty, - "IPv6 address prefix/prefixlen is malformed\n"); - return CMD_WARNING_CONFIG_FAILED; - } - } else if (afi == AFI_L2VPN) { - ret = str2prefix_eth(prefix_str, (struct prefix_eth *)&p); - if (ret <= 0) { - vty_out(vty, "MAC address is malformed\n"); - return CMD_WARNING; - } - } else - return CMD_WARNING_CONFIG_FAILED; - - mfilter = filter_new(); - mfilter->type = type; - filter = &mfilter->u.zfilter; - prefix_copy(&filter->prefix, &p); - - /* "exact-match" */ - if (exact) - filter->exact = 1; - - /* Install new filter to the access_list. */ - access = access_list_get(afi, name_str); - - if (set) { - if (filter_lookup_zebra(access, mfilter)) - filter_free(mfilter); - else - access_list_filter_add(access, mfilter); - } else { - struct filter *delete_filter; - delete_filter = filter_lookup_zebra(access, mfilter); - if (delete_filter) - access_list_filter_delete(access, delete_filter); - - filter_free(mfilter); - } - - return CMD_SUCCESS; -} - -DEFUN (mac_access_list, - mac_access_list_cmd, - "mac access-list WORD X:X:X:X:X:X", - "Add a mac access-list\n" - "Add an access list entry\n" - "MAC zebra access-list name\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "MAC address to match. e.g. 00:01:00:01:00:01\n") -{ - return filter_set_zebra(vty, argv[2]->arg, argv[3]->arg, AFI_L2VPN, - argv[4]->arg, 0, 1); -} - -DEFUN (no_mac_access_list, - no_mac_access_list_cmd, - "no mac access-list WORD X:X:X:X:X:X", - NO_STR - "Remove a mac access-list\n" - "Remove an access list entry\n" - "MAC zebra access-list name\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "MAC address to match. e.g. 00:01:00:01:00:01\n") -{ - return filter_set_zebra(vty, argv[3]->arg, argv[4]->arg, AFI_L2VPN, - argv[5]->arg, 0, 0); -} - -DEFUN (mac_access_list_any, - mac_access_list_any_cmd, - "mac access-list WORD any", - "Add a mac access-list\n" - "Add an access list entry\n" - "MAC zebra access-list name\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "MAC address to match. e.g. 00:01:00:01:00:01\n") -{ - return filter_set_zebra(vty, argv[2]->arg, argv[3]->arg, AFI_L2VPN, - "00:00:00:00:00:00", 0, 1); -} - -DEFUN (no_mac_access_list_any, - no_mac_access_list_any_cmd, - "no mac access-list WORD any", - NO_STR - "Remove a mac access-list\n" - "Remove an access list entry\n" - "MAC zebra access-list name\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "MAC address to match. e.g. 00:01:00:01:00:01\n") -{ - return filter_set_zebra(vty, argv[3]->arg, argv[4]->arg, AFI_L2VPN, - "00:00:00:00:00:00", 0, 0); -} - -DEFUN (access_list_exact, - access_list_exact_cmd, - "access-list WORD A.B.C.D/M [exact-match]", - "Add an access list entry\n" - "IP zebra access-list name\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Prefix to match. e.g. 10.0.0.0/8\n" - "Exact match of the prefixes\n") -{ - int idx = 0; - int exact = 0; - int idx_word = 1; - int idx_permit_deny = 2; - int idx_ipv4_prefixlen = 3; - idx = idx_ipv4_prefixlen; - - if (argv_find(argv, argc, "exact-match", &idx)) - exact = 1; - - return filter_set_zebra(vty, argv[idx_word]->arg, - argv[idx_permit_deny]->arg, AFI_IP, - argv[idx_ipv4_prefixlen]->arg, exact, 1); -} - -DEFUN (access_list_any, - access_list_any_cmd, - "access-list WORD any", - "Add an access list entry\n" - "IP zebra access-list name\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Prefix to match. e.g. 10.0.0.0/8\n") -{ - int idx_word = 1; - int idx_permit_deny = 2; - return filter_set_zebra(vty, argv[idx_word]->arg, - argv[idx_permit_deny]->arg, AFI_IP, "0.0.0.0/0", - 0, 1); -} - -DEFUN (no_access_list_exact, - no_access_list_exact_cmd, - "no access-list WORD A.B.C.D/M [exact-match]", - NO_STR - "Add an access list entry\n" - "IP zebra access-list name\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Prefix to match. e.g. 10.0.0.0/8\n" - "Exact match of the prefixes\n") -{ - int idx = 0; - int exact = 0; - int idx_word = 2; - int idx_permit_deny = 3; - int idx_ipv4_prefixlen = 4; - idx = idx_ipv4_prefixlen; - - if (argv_find(argv, argc, "exact-match", &idx)) - exact = 1; - - return filter_set_zebra(vty, argv[idx_word]->arg, - argv[idx_permit_deny]->arg, AFI_IP, - argv[idx_ipv4_prefixlen]->arg, exact, 0); -} - -DEFUN (no_access_list_any, - no_access_list_any_cmd, - "no access-list WORD any", - NO_STR - "Add an access list entry\n" - "IP zebra access-list name\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Prefix to match. e.g. 10.0.0.0/8\n") -{ - int idx_word = 2; - int idx_permit_deny = 3; - return filter_set_zebra(vty, argv[idx_word]->arg, - argv[idx_permit_deny]->arg, AFI_IP, "0.0.0.0/0", - 0, 0); -} - -DEFUN (no_access_list_all, - no_access_list_all_cmd, - "no access-list <(1-99)|(100-199)|(1300-1999)|(2000-2699)|WORD>", - NO_STR - "Add an access list entry\n" - "IP standard access list\n" - "IP extended access list\n" - "IP standard access list (expanded range)\n" - "IP extended access list (expanded range)\n" - "IP zebra access-list name\n") -{ - int idx_acl = 2; - struct access_list *access; - struct access_master *master; - - /* Looking up access_list. */ - access = access_list_lookup(AFI_IP, argv[idx_acl]->arg); - if (access == NULL) { - vty_out(vty, "%% access-list %s doesn't exist\n", - argv[idx_acl]->arg); - return CMD_WARNING_CONFIG_FAILED; - } - - master = access->master; - - route_map_notify_dependencies(access->name, RMAP_EVENT_FILTER_DELETED); - /* Run hook function. */ - if (master->delete_hook) - (*master->delete_hook)(access); - - /* Delete all filter from access-list. */ - access_list_delete(access); - - return CMD_SUCCESS; -} - -DEFUN (access_list_remark, - access_list_remark_cmd, - "access-list <(1-99)|(100-199)|(1300-1999)|(2000-2699)|WORD> remark LINE...", - "Add an access list entry\n" - "IP standard access list\n" - "IP extended access list\n" - "IP standard access list (expanded range)\n" - "IP extended access list (expanded range)\n" - "IP zebra access-list\n" - "Access list entry comment\n" - "Comment up to 100 characters\n") -{ - int idx_acl = 1; - int idx_remark = 3; - struct access_list *access; - - access = access_list_get(AFI_IP, argv[idx_acl]->arg); - - if (access->remark) { - XFREE(MTYPE_TMP, access->remark); - access->remark = NULL; - } - access->remark = argv_concat(argv, argc, idx_remark); - - return CMD_SUCCESS; -} - -DEFUN (no_access_list_remark, - no_access_list_remark_cmd, - "no access-list <(1-99)|(100-199)|(1300-1999)|(2000-2699)|WORD> remark", - NO_STR - "Add an access list entry\n" - "IP standard access list\n" - "IP extended access list\n" - "IP standard access list (expanded range)\n" - "IP extended access list (expanded range)\n" - "IP zebra access-list\n" - "Access list entry comment\n") -{ - int idx_acl = 2; - return vty_access_list_remark_unset(vty, AFI_IP, argv[idx_acl]->arg); -} - -/* ALIAS_FIXME */ -DEFUN (no_access_list_remark_comment, - no_access_list_remark_comment_cmd, - "no access-list <(1-99)|(100-199)|(1300-1999)|(2000-2699)|WORD> remark LINE...", - NO_STR - "Add an access list entry\n" - "IP standard access list\n" - "IP extended access list\n" - "IP standard access list (expanded range)\n" - "IP extended access list (expanded range)\n" - "IP zebra access-list\n" - "Access list entry comment\n" - "Comment up to 100 characters\n") -{ - return no_access_list_remark(self, vty, argc, argv); -} - -DEFUN (ipv6_access_list_exact, - ipv6_access_list_exact_cmd, - "ipv6 access-list WORD X:X::X:X/M [exact-match]", - IPV6_STR - "Add an access list entry\n" - "IPv6 zebra access-list\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "IPv6 prefix\n" - "Exact match of the prefixes\n") -{ - int idx = 0; - int exact = 0; - int idx_word = 2; - int idx_allow = 3; - int idx_addr = 4; - idx = idx_addr; - - if (argv_find(argv, argc, "exact-match", &idx)) - exact = 1; - - return filter_set_zebra(vty, argv[idx_word]->arg, argv[idx_allow]->text, - AFI_IP6, argv[idx_addr]->arg, exact, 1); -} - -DEFUN (ipv6_access_list_any, - ipv6_access_list_any_cmd, - "ipv6 access-list WORD any", - IPV6_STR - "Add an access list entry\n" - "IPv6 zebra access-list\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Any prefixi to match\n") -{ - int idx_word = 2; - int idx_permit_deny = 3; - return filter_set_zebra(vty, argv[idx_word]->arg, - argv[idx_permit_deny]->arg, AFI_IP6, "::/0", 0, - 1); -} - -DEFUN (no_ipv6_access_list_exact, - no_ipv6_access_list_exact_cmd, - "no ipv6 access-list WORD X:X::X:X/M [exact-match]", - NO_STR - IPV6_STR - "Add an access list entry\n" - "IPv6 zebra access-list\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Prefix to match. e.g. 3ffe:506::/32\n" - "Exact match of the prefixes\n") -{ - int idx = 0; - int exact = 0; - int idx_word = 3; - int idx_permit_deny = 4; - int idx_ipv6_prefixlen = 5; - idx = idx_ipv6_prefixlen; - - if (argv_find(argv, argc, "exact-match", &idx)) - exact = 1; - - return filter_set_zebra(vty, argv[idx_word]->arg, - argv[idx_permit_deny]->arg, AFI_IP6, - argv[idx_ipv6_prefixlen]->arg, exact, 0); -} - -DEFUN (no_ipv6_access_list_any, - no_ipv6_access_list_any_cmd, - "no ipv6 access-list WORD any", - NO_STR - IPV6_STR - "Add an access list entry\n" - "IPv6 zebra access-list\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Any prefixi to match\n") -{ - int idx_word = 3; - int idx_permit_deny = 4; - return filter_set_zebra(vty, argv[idx_word]->arg, - argv[idx_permit_deny]->arg, AFI_IP6, "::/0", 0, - 0); -} - - -DEFUN (no_ipv6_access_list_all, - no_ipv6_access_list_all_cmd, - "no ipv6 access-list WORD", - NO_STR - IPV6_STR - "Add an access list entry\n" - "IPv6 zebra access-list\n") -{ - int idx_word = 3; - struct access_list *access; - struct access_master *master; - - /* Looking up access_list. */ - access = access_list_lookup(AFI_IP6, argv[idx_word]->arg); - if (access == NULL) { - vty_out(vty, "%% access-list %s doesn't exist\n", - argv[idx_word]->arg); - return CMD_WARNING_CONFIG_FAILED; - } - - master = access->master; - - route_map_notify_dependencies(access->name, RMAP_EVENT_FILTER_DELETED); - /* Run hook function. */ - if (master->delete_hook) - (*master->delete_hook)(access); - - /* Delete all filter from access-list. */ - access_list_delete(access); - - return CMD_SUCCESS; -} - -DEFUN (ipv6_access_list_remark, - ipv6_access_list_remark_cmd, - "ipv6 access-list WORD remark LINE...", - IPV6_STR - "Add an access list entry\n" - "IPv6 zebra access-list\n" - "Access list entry comment\n" - "Comment up to 100 characters\n") -{ - int idx_word = 2; - int idx_line = 4; - struct access_list *access; - - access = access_list_get(AFI_IP6, argv[idx_word]->arg); - - if (access->remark) { - XFREE(MTYPE_TMP, access->remark); - access->remark = NULL; - } - access->remark = argv_concat(argv, argc, idx_line); - - return CMD_SUCCESS; -} - -DEFUN (no_ipv6_access_list_remark, - no_ipv6_access_list_remark_cmd, - "no ipv6 access-list WORD remark", - NO_STR - IPV6_STR - "Add an access list entry\n" - "IPv6 zebra access-list\n" - "Access list entry comment\n") -{ - int idx_word = 3; - return vty_access_list_remark_unset(vty, AFI_IP6, argv[idx_word]->arg); -} - -/* ALIAS_FIXME */ -DEFUN (no_ipv6_access_list_remark_comment, - no_ipv6_access_list_remark_comment_cmd, - "no ipv6 access-list WORD remark LINE...", - NO_STR - IPV6_STR - "Add an access list entry\n" - "IPv6 zebra access-list\n" - "Access list entry comment\n" - "Comment up to 100 characters\n") -{ - return no_ipv6_access_list_remark(self, vty, argc, argv); -} - -void config_write_access_zebra(struct vty *, struct filter *); -void config_write_access_cisco(struct vty *, struct filter *); +static void config_write_access_zebra(struct vty *, struct filter *); +static void config_write_access_cisco(struct vty *, struct filter *); /* show access-list command. */ static int filter_show(struct vty *vty, const char *name, afi_t afi) @@ -1748,7 +569,8 @@ static int filter_show(struct vty *vty, const char *name, afi_t afi) write = 0; } - vty_out(vty, " %s%s", filter_type_str(mfilter), + vty_out(vty, " seq %" PRId64, mfilter->seq); + vty_out(vty, " %s%s", filter_type_str(mfilter), mfilter->type == FILTER_DENY ? " " : ""); if (!mfilter->cisco) @@ -1761,7 +583,8 @@ static int filter_show(struct vty *vty, const char *name, afi_t afi) else { vty_out(vty, " %s", inet_ntoa(filter->addr)); - if (filter->addr_mask.s_addr != 0) + if (filter->addr_mask.s_addr + != INADDR_ANY) vty_out(vty, ", wildcard bits %s", inet_ntoa( @@ -1795,7 +618,8 @@ static int filter_show(struct vty *vty, const char *name, afi_t afi) write = 0; } - vty_out(vty, " %s%s", filter_type_str(mfilter), + vty_out(vty, " seq %" PRId64, mfilter->seq); + vty_out(vty, " %s%s", filter_type_str(mfilter), mfilter->type == FILTER_DENY ? " " : ""); if (!mfilter->cisco) @@ -1808,7 +632,8 @@ static int filter_show(struct vty *vty, const char *name, afi_t afi) else { vty_out(vty, " %s", inet_ntoa(filter->addr)); - if (filter->addr_mask.s_addr != 0) + if (filter->addr_mask.s_addr + != INADDR_ANY) vty_out(vty, ", wildcard bits %s", inet_ntoa( @@ -1891,7 +716,7 @@ DEFUN (show_ipv6_access_list_name, return filter_show(vty, argv[idx_word]->arg, AFI_IP6); } -void config_write_access_cisco(struct vty *vty, struct filter *mfilter) +static void config_write_access_cisco(struct vty *vty, struct filter *mfilter) { struct filter_cisco *filter; @@ -1901,7 +726,7 @@ void config_write_access_cisco(struct vty *vty, struct filter *mfilter) vty_out(vty, " ip"); if (filter->addr_mask.s_addr == 0xffffffff) vty_out(vty, " any"); - else if (filter->addr_mask.s_addr == 0) + else if (filter->addr_mask.s_addr == INADDR_ANY) vty_out(vty, " host %s", inet_ntoa(filter->addr)); else { vty_out(vty, " %s", inet_ntoa(filter->addr)); @@ -1910,7 +735,7 @@ void config_write_access_cisco(struct vty *vty, struct filter *mfilter) if (filter->mask_mask.s_addr == 0xffffffff) vty_out(vty, " any"); - else if (filter->mask_mask.s_addr == 0) + else if (filter->mask_mask.s_addr == INADDR_ANY) vty_out(vty, " host %s", inet_ntoa(filter->mask)); else { vty_out(vty, " %s", inet_ntoa(filter->mask)); @@ -1922,7 +747,7 @@ void config_write_access_cisco(struct vty *vty, struct filter *mfilter) vty_out(vty, " any\n"); else { vty_out(vty, " %s", inet_ntoa(filter->addr)); - if (filter->addr_mask.s_addr != 0) + if (filter->addr_mask.s_addr != INADDR_ANY) vty_out(vty, " %s", inet_ntoa(filter->addr_mask)); vty_out(vty, "\n"); @@ -1930,7 +755,7 @@ void config_write_access_cisco(struct vty *vty, struct filter *mfilter) } } -void config_write_access_zebra(struct vty *vty, struct filter *mfilter) +static void config_write_access_zebra(struct vty *vty, struct filter *mfilter) { struct filter_zebra *filter; struct prefix *p; @@ -1956,79 +781,11 @@ void config_write_access_zebra(struct vty *vty, struct filter *mfilter) vty_out(vty, "\n"); } -static int config_write_access(struct vty *vty, afi_t afi) -{ - struct access_list *access; - struct access_master *master; - struct filter *mfilter; - int write = 0; - - master = access_master_get(afi); - if (master == NULL) - return 0; - - for (access = master->num.head; access; access = access->next) { - if (access->remark) { - vty_out(vty, "%saccess-list %s remark %s\n", - (afi == AFI_IP) ? ("") - : ((afi == AFI_IP6) ? ("ipv6 ") - : ("mac ")), - access->name, access->remark); - write++; - } - - for (mfilter = access->head; mfilter; mfilter = mfilter->next) { - vty_out(vty, "%saccess-list %s %s", - (afi == AFI_IP) ? ("") - : ((afi == AFI_IP6) ? ("ipv6 ") - : ("mac ")), - access->name, filter_type_str(mfilter)); - - if (mfilter->cisco) - config_write_access_cisco(vty, mfilter); - else - config_write_access_zebra(vty, mfilter); - - write++; - } - } - - for (access = master->str.head; access; access = access->next) { - if (access->remark) { - vty_out(vty, "%saccess-list %s remark %s\n", - (afi == AFI_IP) ? ("") - : ((afi == AFI_IP6) ? ("ipv6 ") - : ("mac ")), - access->name, access->remark); - write++; - } - - for (mfilter = access->head; mfilter; mfilter = mfilter->next) { - vty_out(vty, "%saccess-list %s %s", - (afi == AFI_IP) ? ("") - : ((afi == AFI_IP6) ? ("ipv6 ") - : ("mac ")), - access->name, filter_type_str(mfilter)); - - if (mfilter->cisco) - config_write_access_cisco(vty, mfilter); - else - config_write_access_zebra(vty, mfilter); - - write++; - } - } - return write; -} - static struct cmd_node access_mac_node = { - ACCESS_MAC_NODE, "", /* Access list has no interface. */ - 1}; - -static int config_write_access_mac(struct vty *vty) -{ - return config_write_access(vty, AFI_L2VPN); -} + .name = "MAC access list", + .node = ACCESS_MAC_NODE, + .prompt = "", +}; static void access_list_reset_mac(void) { @@ -2059,26 +816,33 @@ static void access_list_reset_mac(void) /* Install vty related command. */ static void access_list_init_mac(void) { - install_node(&access_mac_node, config_write_access_mac); + install_node(&access_mac_node); install_element(ENABLE_NODE, &show_mac_access_list_cmd); install_element(ENABLE_NODE, &show_mac_access_list_name_cmd); - - /* Zebra access-list */ - install_element(CONFIG_NODE, &mac_access_list_cmd); - install_element(CONFIG_NODE, &no_mac_access_list_cmd); - install_element(CONFIG_NODE, &mac_access_list_any_cmd); - install_element(CONFIG_NODE, &no_mac_access_list_any_cmd); } /* Access-list node. */ -static struct cmd_node access_node = {ACCESS_NODE, - "", /* Access list has no interface. */ - 1}; +static int config_write_access(struct vty *vty); +static struct cmd_node access_node = { + .name = "ipv4 access list", + .node = ACCESS_NODE, + .prompt = "", + .config_write = config_write_access, +}; -static int config_write_access_ipv4(struct vty *vty) +static int config_write_access(struct vty *vty) { - return config_write_access(vty, AFI_IP); + struct lyd_node *dnode; + int written = 0; + + dnode = yang_dnode_get(running_config->dnode, "/frr-filter:lib"); + if (dnode) { + nb_cli_show_dnode_cmds(vty, dnode, false); + written = 1; + } + + return written; } static void access_list_reset_ipv4(void) @@ -2110,59 +874,17 @@ static void access_list_reset_ipv4(void) /* Install vty related command. */ static void access_list_init_ipv4(void) { - install_node(&access_node, config_write_access_ipv4); + install_node(&access_node); install_element(ENABLE_NODE, &show_ip_access_list_cmd); install_element(ENABLE_NODE, &show_ip_access_list_name_cmd); - - /* Zebra access-list */ - install_element(CONFIG_NODE, &access_list_exact_cmd); - install_element(CONFIG_NODE, &access_list_any_cmd); - install_element(CONFIG_NODE, &no_access_list_exact_cmd); - install_element(CONFIG_NODE, &no_access_list_any_cmd); - - /* Standard access-list */ - install_element(CONFIG_NODE, &access_list_standard_cmd); - install_element(CONFIG_NODE, &access_list_standard_nomask_cmd); - install_element(CONFIG_NODE, &access_list_standard_host_cmd); - install_element(CONFIG_NODE, &access_list_standard_any_cmd); - install_element(CONFIG_NODE, &no_access_list_standard_cmd); - install_element(CONFIG_NODE, &no_access_list_standard_nomask_cmd); - install_element(CONFIG_NODE, &no_access_list_standard_host_cmd); - install_element(CONFIG_NODE, &no_access_list_standard_any_cmd); - - /* Extended access-list */ - install_element(CONFIG_NODE, &access_list_extended_cmd); - install_element(CONFIG_NODE, &access_list_extended_any_mask_cmd); - install_element(CONFIG_NODE, &access_list_extended_mask_any_cmd); - install_element(CONFIG_NODE, &access_list_extended_any_any_cmd); - install_element(CONFIG_NODE, &access_list_extended_host_mask_cmd); - install_element(CONFIG_NODE, &access_list_extended_mask_host_cmd); - install_element(CONFIG_NODE, &access_list_extended_host_host_cmd); - install_element(CONFIG_NODE, &access_list_extended_any_host_cmd); - install_element(CONFIG_NODE, &access_list_extended_host_any_cmd); - install_element(CONFIG_NODE, &no_access_list_extended_cmd); - install_element(CONFIG_NODE, &no_access_list_extended_any_mask_cmd); - install_element(CONFIG_NODE, &no_access_list_extended_mask_any_cmd); - install_element(CONFIG_NODE, &no_access_list_extended_any_any_cmd); - install_element(CONFIG_NODE, &no_access_list_extended_host_mask_cmd); - install_element(CONFIG_NODE, &no_access_list_extended_mask_host_cmd); - install_element(CONFIG_NODE, &no_access_list_extended_host_host_cmd); - install_element(CONFIG_NODE, &no_access_list_extended_any_host_cmd); - install_element(CONFIG_NODE, &no_access_list_extended_host_any_cmd); - - install_element(CONFIG_NODE, &access_list_remark_cmd); - install_element(CONFIG_NODE, &no_access_list_all_cmd); - install_element(CONFIG_NODE, &no_access_list_remark_cmd); - install_element(CONFIG_NODE, &no_access_list_remark_comment_cmd); } -static struct cmd_node access_ipv6_node = {ACCESS_IPV6_NODE, "", 1}; - -static int config_write_access_ipv6(struct vty *vty) -{ - return config_write_access(vty, AFI_IP6); -} +static struct cmd_node access_ipv6_node = { + .name = "ipv6 access list", + .node = ACCESS_IPV6_NODE, + .prompt = "", +}; static void access_list_reset_ipv6(void) { @@ -2192,20 +914,10 @@ static void access_list_reset_ipv6(void) static void access_list_init_ipv6(void) { - install_node(&access_ipv6_node, config_write_access_ipv6); + install_node(&access_ipv6_node); install_element(ENABLE_NODE, &show_ipv6_access_list_cmd); install_element(ENABLE_NODE, &show_ipv6_access_list_name_cmd); - - install_element(CONFIG_NODE, &ipv6_access_list_exact_cmd); - install_element(CONFIG_NODE, &ipv6_access_list_any_cmd); - install_element(CONFIG_NODE, &no_ipv6_access_list_exact_cmd); - install_element(CONFIG_NODE, &no_ipv6_access_list_any_cmd); - - install_element(CONFIG_NODE, &no_ipv6_access_list_all_cmd); - install_element(CONFIG_NODE, &ipv6_access_list_remark_cmd); - install_element(CONFIG_NODE, &no_ipv6_access_list_remark_cmd); - install_element(CONFIG_NODE, &no_ipv6_access_list_remark_comment_cmd); } void access_list_init(void) @@ -2213,6 +925,8 @@ void access_list_init(void) access_list_init_ipv4(); access_list_init_ipv6(); access_list_init_mac(); + + filter_cli_init(); } void access_list_reset(void) diff --git a/lib/filter.h b/lib/filter.h index 3dd2eaaf15..b1bf1d67ba 100644 --- a/lib/filter.h +++ b/lib/filter.h @@ -23,6 +23,7 @@ #define _ZEBRA_FILTER_H #include "if.h" +#include "prefix.h" #ifdef __cplusplus extern "C" { @@ -31,6 +32,16 @@ extern "C" { /* Maximum ACL name length */ #define ACL_NAMSIZ 128 +/** Cisco host wildcard mask. */ +#define CISCO_HOST_WILDCARD_MASK "0.0.0.0" +/** Cisco host wildcard binary mask. */ +#define CISCO_BIN_HOST_WILDCARD_MASK INADDR_ANY + +/** Cisco any wildcard mask. */ +#define CISCO_ANY_WILDCARD_MASK "255.255.255.255" +/** Cisco binary any wildcard mask. */ +#define CISCO_BIN_ANY_WILDCARD_MASK INADDR_NONE + /* Filter direction. */ #define FILTER_IN 0 #define FILTER_OUT 1 @@ -41,6 +52,50 @@ enum filter_type { FILTER_DENY, FILTER_PERMIT, FILTER_DYNAMIC }; enum access_type { ACCESS_TYPE_STRING, ACCESS_TYPE_NUMBER }; +struct filter_cisco { + /* Cisco access-list */ + int extended; + struct in_addr addr; + struct in_addr addr_mask; + struct in_addr mask; + struct in_addr mask_mask; +}; + +struct filter_zebra { + /* If this filter is "exact" match then this flag is set. */ + int exact; + + /* Prefix information. */ + struct prefix prefix; +}; + +/* Forward declaration of access-list struct. */ +struct access_list; + +/* Filter element of access list */ +struct filter { + /* For doubly linked list. */ + struct filter *next; + struct filter *prev; + + /* Parent access-list pointer. */ + struct access_list *acl; + + /* Filter type information. */ + enum filter_type type; + + /* Sequence number */ + int64_t seq; + + /* Cisco access-list */ + int cisco; + + union { + struct filter_cisco cfilter; + struct filter_zebra zfilter; + } u; +}; + /* Access list */ struct access_list { char *name; @@ -57,6 +112,28 @@ struct access_list { struct filter *tail; }; +/* List of access_list. */ +struct access_list_list { + struct access_list *head; + struct access_list *tail; +}; + +/* Master structure of access_list. */ +struct access_master { + /* List of access_list which name is number. */ + struct access_list_list num; + + /* List of access_list which name is string. */ + struct access_list_list str; + + /* Hook function which is executed when new access_list is added. */ + void (*add_hook)(struct access_list *); + + /* Hook function which is executed when access_list is deleted. */ + void (*delete_hook)(struct access_list *); +}; + + /* Prototypes for access-list. */ extern void access_list_init(void); extern void access_list_reset(void); @@ -66,6 +143,114 @@ extern struct access_list *access_list_lookup(afi_t, const char *); extern enum filter_type access_list_apply(struct access_list *access, const void *object); +struct access_list *access_list_get(afi_t afi, const char *name); +void access_list_delete(struct access_list *access); +struct filter *filter_new(void); +void access_list_filter_add(struct access_list *access, + struct filter *filter); +void access_list_filter_delete(struct access_list *access, + struct filter *filter); +int64_t filter_new_seq_get(struct access_list *access); +struct filter *filter_lookup_cisco(struct access_list *access, + struct filter *mnew); +struct filter *filter_lookup_zebra(struct access_list *access, + struct filter *mnew); + +extern const struct frr_yang_module_info frr_filter_info; + + +/* filter_nb.c */ +enum yang_access_list_type { + YALT_IPV4 = 0, + YALT_IPV6 = 1, + YALT_MAC = 2, +}; + +enum yang_prefix_list_type { + YPLT_IPV4 = 0, + YPLT_IPV6 = 1, +}; + +enum yang_prefix_list_action { + YPLA_DENY = 0, + YPLA_PERMIT = 1, +}; + +struct acl_dup_args { + /** Access list type ("ipv4", "ipv6" or "mac"). */ + const char *ada_type; + /** Access list name. */ + const char *ada_name; + + /** Entry action. */ + const char *ada_action; + +#define ADA_MAX_VALUES 4 + /** Entry XPath for value. */ + const char *ada_xpath[ADA_MAX_VALUES]; + /** Entry value to match. */ + const char *ada_value[ADA_MAX_VALUES]; + + /** Duplicated entry found in list? */ + bool ada_found; + + /** (Optional) Already existing `dnode`. */ + const struct lyd_node *ada_entry_dnode; +}; + +/** + * Check for duplicated entries using the candidate configuration. + * + * \param vty so we can get the candidate config. + * \param ada the arguments to check. + */ +bool acl_is_dup(const struct lyd_node *dnode, struct acl_dup_args *ada); + +struct plist_dup_args { + /** Access list type ("ipv4" or "ipv6"). */ + const char *pda_type; + /** Access list name. */ + const char *pda_name; + + /** Entry action. */ + const char *pda_action; + +#define PDA_MAX_VALUES 4 + /** Entry XPath for value. */ + const char *pda_xpath[PDA_MAX_VALUES]; + /** Entry value to match. */ + const char *pda_value[PDA_MAX_VALUES]; + + /** Duplicated entry found in list? */ + bool pda_found; + + /** (Optional) Already existing `dnode`. */ + const struct lyd_node *pda_entry_dnode; +}; + +/** + * Check for duplicated entries using the candidate configuration. + * + * \param vty so we can get the candidate config. + * \param pda the arguments to check. + */ +bool plist_is_dup(const struct lyd_node *dnode, struct plist_dup_args *pda); + +/* filter_cli.c */ +struct lyd_node; +struct vty; + +extern void access_list_show(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +extern void access_list_remark_show(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +extern void prefix_list_show(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +extern void prefix_list_remark_show(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); + +void filter_cli_init(void); + #ifdef __cplusplus } #endif diff --git a/lib/filter_cli.c b/lib/filter_cli.c new file mode 100644 index 0000000000..5f8567ce09 --- /dev/null +++ b/lib/filter_cli.c @@ -0,0 +1,1827 @@ +/* + * FRR filter CLI implementation. + * + * Copyright (C) 2019 Network Device Education Foundation, Inc. ("NetDEF") + * Rafael Zalamena + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#include "zebra.h" +#include "northbound.h" +#include "prefix.h" + +#include "lib/command.h" +#include "lib/filter.h" +#include "lib/northbound_cli.h" +#include "lib/plist.h" +#include "lib/plist_int.h" +#include "lib/printfrr.h" + +#ifndef VTYSH_EXTRACT_PL +#include "lib/filter_cli_clippy.c" +#endif /* VTYSH_EXTRACT_PL */ + +#define ACCESS_LIST_STR "Access list entry\n" +#define ACCESS_LIST_LEG_STR "IP standard access list\n" +#define ACCESS_LIST_ELEG_STR "IP extended access list\n" +#define ACCESS_LIST_ELEG_EXT_STR "IP extended access list (expanded range)\n" +#define ACCESS_LIST_ZEBRA_STR "Access list entry\n" +#define ACCESS_LIST_SEQ_STR \ + "Sequence number of an entry\n" \ + "Sequence number\n" +#define ACCESS_LIST_ACTION_STR \ + "Specify packets to reject\n" \ + "Specify packets to forward\n" +#define ACCESS_LIST_REMARK_STR "Access list entry comment\n" +#define ACCESS_LIST_REMARK_LINE_STR "Comment up to 100 characters\n" + +#define PREFIX_LIST_NAME_STR "Prefix list entry name\n" + +/* + * Helper function to locate filter data structures for Cisco-style ACLs. + */ +static int64_t acl_cisco_get_seq(struct access_list *acl, const char *action, + const char *src, const char *src_mask, + const char *dst, const char *dst_mask) +{ + struct filter_cisco *fc; + struct filter f, *fn; + + memset(&f, 0, sizeof(f)); + f.cisco = 1; + if (strcmp(action, "permit") == 0) + f.type = FILTER_PERMIT; + else + f.type = FILTER_DENY; + + fc = &f.u.cfilter; + inet_pton(AF_INET, src, &fc->addr); + inet_pton(AF_INET, src_mask, &fc->addr_mask); + fc->addr.s_addr &= ~fc->addr_mask.s_addr; + if (dst != NULL) { + fc->extended = 1; + inet_pton(AF_INET, dst, &fc->mask); + inet_pton(AF_INET, dst_mask, &fc->mask_mask); + fc->mask.s_addr &= ~fc->mask_mask.s_addr; + } + + fn = filter_lookup_cisco(acl, &f); + if (fn == NULL) + return -1; + + return fn->seq; +} + +/* + * Helper function to locate filter data structures for zebra-style ACLs. + */ +static int64_t acl_zebra_get_seq(struct access_list *acl, const char *action, + const struct prefix *p, bool exact) +{ + struct filter_zebra *fz; + struct filter f, *fn; + + memset(&f, 0, sizeof(f)); + memset(&fz, 0, sizeof(fz)); + if (strcmp(action, "permit") == 0) + f.type = FILTER_PERMIT; + else + f.type = FILTER_DENY; + + fz = &f.u.zfilter; + if (p->family) + prefix_copy(&fz->prefix, p); + fz->exact = exact; + + fn = filter_lookup_zebra(acl, &f); + if (fn == NULL) + return -1; + + return fn->seq; +} + +/* + * Helper function to generate a sequence number for legacy commands. + */ +static int acl_get_seq_cb(const struct lyd_node *dnode, void *arg) +{ + int64_t *seq = arg; + int64_t cur_seq = yang_dnode_get_uint32(dnode, "sequence"); + + if (cur_seq > *seq) + *seq = cur_seq; + + return YANG_ITER_CONTINUE; +} + +/** + * Helper function that iterates over the XPath `xpath` on the candidate + * configuration in `vty->candidate_config`. + * + * \param[in] vty shell context with the candidate configuration. + * \param[in] xpath the XPath to look for the sequence leaf. + * \returns next unused sequence number. + */ +static long acl_get_seq(struct vty *vty, const char *xpath) +{ + int64_t seq = 0; + + yang_dnode_iterate(acl_get_seq_cb, &seq, vty->candidate_config->dnode, + "%s/entry", xpath); + + return seq + 5; +} + +/* + * Cisco (legacy) access lists. + */ +DEFPY_YANG( + access_list_std, access_list_std_cmd, + "access-list WORD$name [seq (1-4294967295)$seq] $action <[host] A.B.C.D$host|A.B.C.D$host A.B.C.D$mask>", + ACCESS_LIST_STR + ACCESS_LIST_LEG_STR + ACCESS_LIST_SEQ_STR + ACCESS_LIST_ACTION_STR + "A single host address\n" + "Address to match\n" + "Address to match\n" + "Wildcard bits\n") +{ + int64_t sseq; + struct acl_dup_args ada = {}; + char xpath[XPATH_MAXLEN]; + char xpath_entry[XPATH_MAXLEN + 128]; + + /* + * Backward compatibility: don't complain about duplicated values, + * just silently accept. + */ + if (seq_str == NULL) { + ada.ada_type = "ipv4"; + ada.ada_name = name; + ada.ada_action = action; + if (host_str && mask_str == NULL) { + ada.ada_xpath[0] = "./host"; + ada.ada_value[0] = host_str; + } else if (host_str && mask_str) { + ada.ada_xpath[0] = "./network/address"; + ada.ada_value[0] = host_str; + ada.ada_xpath[1] = "./network/mask"; + ada.ada_value[1] = mask_str; + } else { + ada.ada_xpath[0] = "./source-any"; + ada.ada_value[0] = ""; + } + + /* Duplicated entry without sequence, just quit. */ + if (acl_is_dup(vty->candidate_config->dnode, &ada)) + return CMD_SUCCESS; + } + + /* + * Create the access-list first, so we can generate sequence if + * none given (backward compatibility). + */ + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='ipv4'][name='%s']", name); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + if (seq_str == NULL) { + /* Use XPath to find the next sequence number. */ + sseq = acl_get_seq(vty, xpath); + snprintfrr(xpath_entry, sizeof(xpath_entry), + "%s/entry[sequence='%" PRId64 "']", xpath, sseq); + } else + snprintfrr(xpath_entry, sizeof(xpath_entry), + "%s/entry[sequence='%s']", xpath, seq_str); + + nb_cli_enqueue_change(vty, xpath_entry, NB_OP_CREATE, NULL); + + nb_cli_enqueue_change(vty, "./action", NB_OP_MODIFY, action); + if (host_str != NULL && mask_str == NULL) { + nb_cli_enqueue_change(vty, "./host", NB_OP_MODIFY, host_str); + } else if (host_str != NULL && mask_str != NULL) { + nb_cli_enqueue_change(vty, "./network/address", NB_OP_MODIFY, + host_str); + nb_cli_enqueue_change(vty, "./network/mask", NB_OP_MODIFY, + mask_str); + } else { + nb_cli_enqueue_change(vty, "./source-any", NB_OP_CREATE, NULL); + } + + return nb_cli_apply_changes(vty, xpath_entry); +} + +DEFPY_YANG( + no_access_list_std, no_access_list_std_cmd, + "no access-list WORD$name [seq (1-4294967295)$seq] $action <[host] A.B.C.D$host|A.B.C.D$host A.B.C.D$mask>", + NO_STR + ACCESS_LIST_STR + ACCESS_LIST_LEG_STR + ACCESS_LIST_SEQ_STR + ACCESS_LIST_ACTION_STR + "A single host address\n" + "Address to match\n" + "Address to match\n" + "Wildcard bits\n") +{ + struct access_list *acl; + struct lyd_node *dnode; + int64_t sseq; + char xpath[XPATH_MAXLEN]; + char xpath_entry[XPATH_MAXLEN + 32]; + + /* If the user provided sequence number, then just go for it. */ + if (seq_str != NULL) { + snprintf( + xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='ipv4'][name='%s']/entry[sequence='%s']", + name, seq_str); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); + } + + /* Otherwise, to keep compatibility, we need to figure it out. */ + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='ipv4'][name='%s']", name); + + /* Access-list must exist before entries. */ + if (yang_dnode_exists(running_config->dnode, xpath) == false) + return CMD_WARNING_CONFIG_FAILED; + + /* Use access-list data structure to fetch sequence. */ + dnode = yang_dnode_get(running_config->dnode, xpath); + acl = nb_running_get_entry(dnode, NULL, true); + sseq = acl_cisco_get_seq(acl, action, host_str, + mask_str ? mask_str : CISCO_HOST_WILDCARD_MASK, + NULL, NULL); + if (sseq == -1) + return CMD_WARNING_CONFIG_FAILED; + + snprintfrr(xpath_entry, sizeof(xpath_entry), + "%s/entry[sequence='%" PRId64 "']", xpath, sseq); + nb_cli_enqueue_change(vty, xpath_entry, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + access_list_ext, access_list_ext_cmd, + "access-list WORD$name [seq (1-4294967295)$seq] $action ip ", + ACCESS_LIST_STR + ACCESS_LIST_ELEG_STR + ACCESS_LIST_SEQ_STR + ACCESS_LIST_ACTION_STR + "IPv4 address\n" + "Source address to match\n" + "Source address mask to apply\n" + "Single source host\n" + "Source address to match\n" + "Any source host\n" + "Destination address to match\n" + "Destination address mask to apply\n" + "Single destination host\n" + "Destination address to match\n" + "Any destination host\n") +{ + int idx = 0; + int64_t sseq; + struct acl_dup_args ada = {}; + char xpath[XPATH_MAXLEN]; + char xpath_entry[XPATH_MAXLEN + 128]; + + /* + * Backward compatibility: don't complain about duplicated values, + * just silently accept. + */ + if (seq_str == NULL) { + ada.ada_type = "ipv4"; + ada.ada_name = name; + ada.ada_action = action; + if (src_str && src_mask_str == NULL) { + ada.ada_xpath[idx] = "./host"; + ada.ada_value[idx] = src_str; + idx++; + } else if (src_str && src_mask_str) { + ada.ada_xpath[idx] = "./network/address"; + ada.ada_value[idx] = src_str; + idx++; + ada.ada_xpath[idx] = "./network/mask"; + ada.ada_value[idx] = src_mask_str; + idx++; + } else { + ada.ada_xpath[idx] = "./source-any"; + ada.ada_value[idx] = ""; + idx++; + } + + if (dst_str && dst_mask_str == NULL) { + ada.ada_xpath[idx] = "./destination-host"; + ada.ada_value[idx] = dst_str; + idx++; + } else if (dst_str && dst_mask_str) { + ada.ada_xpath[idx] = "./destination-network/address"; + ada.ada_value[idx] = dst_str; + idx++; + ada.ada_xpath[idx] = "./destination-network/mask"; + ada.ada_value[idx] = dst_mask_str; + idx++; + } else { + ada.ada_xpath[idx] = "./destination-any"; + ada.ada_value[idx] = ""; + idx++; + } + + /* Duplicated entry without sequence, just quit. */ + if (acl_is_dup(vty->candidate_config->dnode, &ada)) + return CMD_SUCCESS; + } + + /* + * Create the access-list first, so we can generate sequence if + * none given (backward compatibility). + */ + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='ipv4'][name='%s']", name); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + if (seq_str == NULL) { + /* Use XPath to find the next sequence number. */ + sseq = acl_get_seq(vty, xpath); + snprintfrr(xpath_entry, sizeof(xpath_entry), + "%s/entry[sequence='%" PRId64 "']", xpath, sseq); + } else + snprintfrr(xpath_entry, sizeof(xpath_entry), + "%s/entry[sequence='%s']", xpath, seq_str); + + nb_cli_enqueue_change(vty, xpath_entry, NB_OP_CREATE, NULL); + + nb_cli_enqueue_change(vty, "./action", NB_OP_MODIFY, action); + if (src_str != NULL && src_mask_str == NULL) { + nb_cli_enqueue_change(vty, "./host", NB_OP_MODIFY, src_str); + } else if (src_str != NULL && src_mask_str != NULL) { + nb_cli_enqueue_change(vty, "./network/address", NB_OP_MODIFY, + src_str); + nb_cli_enqueue_change(vty, "./network/mask", NB_OP_MODIFY, + src_mask_str); + } else { + nb_cli_enqueue_change(vty, "./source-any", NB_OP_CREATE, NULL); + } + + if (dst_str != NULL && dst_mask_str == NULL) { + nb_cli_enqueue_change(vty, "./destination-host", NB_OP_MODIFY, + dst_str); + } else if (dst_str != NULL && dst_mask_str != NULL) { + nb_cli_enqueue_change(vty, "./destination-network/address", + NB_OP_MODIFY, dst_str); + nb_cli_enqueue_change(vty, "./destination-network/mask", + NB_OP_MODIFY, dst_mask_str); + } else { + nb_cli_enqueue_change(vty, "./destination-any", NB_OP_CREATE, + NULL); + } + + return nb_cli_apply_changes(vty, xpath_entry); +} + +DEFPY_YANG( + no_access_list_ext, no_access_list_ext_cmd, + "no access-list WORD$name [seq (1-4294967295)$seq] $action ip ", + NO_STR + ACCESS_LIST_STR + ACCESS_LIST_ELEG_STR + ACCESS_LIST_SEQ_STR + ACCESS_LIST_ACTION_STR + "Any Internet Protocol\n" + "Source address to match\n" + "Source address mask to apply\n" + "Single source host\n" + "Source address to match\n" + "Any source host\n" + "Destination address to match\n" + "Destination address mask to apply\n" + "Single destination host\n" + "Destination address to match\n" + "Any destination host\n") +{ + struct access_list *acl; + struct lyd_node *dnode; + int64_t sseq; + char xpath[XPATH_MAXLEN]; + char xpath_entry[XPATH_MAXLEN + 32]; + + /* If the user provided sequence number, then just go for it. */ + if (seq_str != NULL) { + snprintfrr( + xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='ipv4'][name='%s']/entry[sequence='%s']", + name, seq_str); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); + } + + /* Otherwise, to keep compatibility, we need to figure it out. */ + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='ipv4'][name='%s']", name); + + /* Access-list must exist before entries. */ + if (yang_dnode_exists(running_config->dnode, xpath) == false) + return CMD_WARNING_CONFIG_FAILED; + + /* Use access-list data structure to fetch sequence. */ + dnode = yang_dnode_get(running_config->dnode, xpath); + acl = nb_running_get_entry(dnode, NULL, true); + if (src_str != NULL) { + if (dst_str != NULL) + sseq = acl_cisco_get_seq( + acl, action, src_str, + src_mask_str ? src_mask_str + : CISCO_HOST_WILDCARD_MASK, + dst_str, + dst_mask_str ? dst_mask_str + : CISCO_HOST_WILDCARD_MASK); + else + sseq = acl_cisco_get_seq( + acl, action, src_str, + src_mask_str ? src_mask_str + : CISCO_HOST_WILDCARD_MASK, + "0.0.0.0", CISCO_ANY_WILDCARD_MASK); + } else { + if (dst_str != NULL) + sseq = acl_cisco_get_seq( + acl, action, "0.0.0.0", CISCO_ANY_WILDCARD_MASK, + dst_str, + dst_mask_str ? dst_mask_str + : CISCO_HOST_WILDCARD_MASK); + else + sseq = acl_cisco_get_seq( + acl, action, "0.0.0.0", CISCO_ANY_WILDCARD_MASK, + "0.0.0.0", CISCO_ANY_WILDCARD_MASK); + } + if (sseq == -1) + return CMD_WARNING_CONFIG_FAILED; + + snprintfrr(xpath_entry, sizeof(xpath_entry), + "%s/entry[sequence='%" PRId64 "']", xpath, sseq); + nb_cli_enqueue_change(vty, xpath_entry, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +/* + * Zebra access lists. + */ +DEFPY_YANG( + access_list, access_list_cmd, + "access-list WORD$name [seq (1-4294967295)$seq] $action ", + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR + ACCESS_LIST_SEQ_STR + ACCESS_LIST_ACTION_STR + "Prefix to match. e.g. 10.0.0.0/8\n" + "Exact match of the prefixes\n" + "Match any IPv4\n") +{ + int64_t sseq; + struct acl_dup_args ada = {}; + char xpath[XPATH_MAXLEN]; + char xpath_entry[XPATH_MAXLEN + 128]; + + /* + * Backward compatibility: don't complain about duplicated values, + * just silently accept. + */ + if (seq_str == NULL) { + ada.ada_type = "ipv4"; + ada.ada_name = name; + ada.ada_action = action; + + if (prefix_str) { + ada.ada_xpath[0] = "./ipv4-prefix"; + ada.ada_value[0] = prefix_str; + if (exact) { + ada.ada_xpath[1] = "./ipv4-exact-match"; + ada.ada_value[1] = "true"; + } + } else { + ada.ada_xpath[0] = "./any"; + ada.ada_value[0] = ""; + } + + /* Duplicated entry without sequence, just quit. */ + if (acl_is_dup(vty->candidate_config->dnode, &ada)) + return CMD_SUCCESS; + } + + /* + * Create the access-list first, so we can generate sequence if + * none given (backward compatibility). + */ + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='ipv4'][name='%s']", name); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + if (seq_str == NULL) { + /* Use XPath to find the next sequence number. */ + sseq = acl_get_seq(vty, xpath); + snprintfrr(xpath_entry, sizeof(xpath_entry), + "%s/entry[sequence='%" PRId64 "']", xpath, sseq); + } else + snprintfrr(xpath_entry, sizeof(xpath_entry), + "%s/entry[sequence='%s']", xpath, seq_str); + + nb_cli_enqueue_change(vty, xpath_entry, NB_OP_CREATE, NULL); + + nb_cli_enqueue_change(vty, "./action", NB_OP_MODIFY, action); + if (prefix_str != NULL) { + nb_cli_enqueue_change(vty, "./ipv4-prefix", NB_OP_MODIFY, + prefix_str); + nb_cli_enqueue_change(vty, "./ipv4-exact-match", NB_OP_MODIFY, + exact ? "true" : "false"); + } else { + nb_cli_enqueue_change(vty, "./any", NB_OP_CREATE, NULL); + } + + return nb_cli_apply_changes(vty, xpath_entry); +} + +DEFPY_YANG( + no_access_list, no_access_list_cmd, + "no access-list WORD$name [seq (1-4294967295)$seq] $action ", + NO_STR + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR + ACCESS_LIST_SEQ_STR + ACCESS_LIST_ACTION_STR + "Prefix to match. e.g. 10.0.0.0/8\n" + "Exact match of the prefixes\n" + "Match any IPv4\n") +{ + struct access_list *acl; + struct lyd_node *dnode; + int64_t sseq; + struct prefix pany; + char xpath[XPATH_MAXLEN]; + char xpath_entry[XPATH_MAXLEN + 32]; + + /* If the user provided sequence number, then just go for it. */ + if (seq_str != NULL) { + snprintf( + xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='ipv4'][name='%s']/entry[sequence='%s']", + name, seq_str); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); + } + + /* Otherwise, to keep compatibility, we need to figure it out. */ + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='ipv4'][name='%s']", name); + + /* Access-list must exist before entries. */ + if (yang_dnode_exists(running_config->dnode, xpath) == false) + return CMD_WARNING_CONFIG_FAILED; + + /* Use access-list data structure to fetch sequence. */ + dnode = yang_dnode_get(running_config->dnode, xpath); + acl = nb_running_get_entry(dnode, NULL, true); + if (prefix_str == NULL) { + memset(&pany, 0, sizeof(pany)); + pany.family = AF_INET; + sseq = acl_zebra_get_seq(acl, action, &pany, exact); + } else + sseq = acl_zebra_get_seq(acl, action, (struct prefix *)prefix, + exact); + if (sseq == -1) + return CMD_WARNING_CONFIG_FAILED; + + snprintfrr(xpath_entry, sizeof(xpath_entry), + "%s/entry[sequence='%" PRId64 "']", xpath, sseq); + nb_cli_enqueue_change(vty, xpath_entry, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + no_access_list_all, no_access_list_all_cmd, + "no access-list WORD$name", + NO_STR + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR) +{ + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='ipv4'][name='%s']", name); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + access_list_remark, access_list_remark_cmd, + "access-list WORD$name remark LINE...", + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR + ACCESS_LIST_REMARK_STR + ACCESS_LIST_REMARK_LINE_STR) +{ + int rv; + char *remark; + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='ipv4'][name='%s']", name); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + remark = argv_concat(argv, argc, 3); + nb_cli_enqueue_change(vty, "./remark", NB_OP_CREATE, remark); + rv = nb_cli_apply_changes(vty, xpath); + XFREE(MTYPE_TMP, remark); + + return rv; +} + +DEFPY_YANG( + no_access_list_remark, no_access_list_remark_cmd, + "no access-list WORD$name remark", + NO_STR + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR + ACCESS_LIST_REMARK_STR) +{ + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='ipv4'][name='%s']/remark", + name); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +ALIAS( + no_access_list_remark, no_access_list_remark_line_cmd, + "no access-list WORD$name remark LINE...", + NO_STR + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR + ACCESS_LIST_REMARK_STR + ACCESS_LIST_REMARK_LINE_STR) + +DEFPY_YANG( + ipv6_access_list, ipv6_access_list_cmd, + "ipv6 access-list WORD$name [seq (1-4294967295)$seq] $action ", + IPV6_STR + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR + ACCESS_LIST_SEQ_STR + ACCESS_LIST_ACTION_STR + "IPv6 prefix\n" + "Exact match of the prefixes\n" + "Match any IPv6\n") +{ + int64_t sseq; + struct acl_dup_args ada = {}; + char xpath[XPATH_MAXLEN]; + char xpath_entry[XPATH_MAXLEN + 128]; + + /* + * Backward compatibility: don't complain about duplicated values, + * just silently accept. + */ + if (seq_str == NULL) { + ada.ada_type = "ipv6"; + ada.ada_name = name; + ada.ada_action = action; + + if (prefix_str) { + ada.ada_xpath[0] = "./ipv6-prefix"; + ada.ada_value[0] = prefix_str; + if (exact) { + ada.ada_xpath[1] = "./ipv6-exact-match"; + ada.ada_value[1] = "true"; + } + } else { + ada.ada_xpath[0] = "./any"; + ada.ada_value[0] = ""; + } + + /* Duplicated entry without sequence, just quit. */ + if (acl_is_dup(vty->candidate_config->dnode, &ada)) + return CMD_SUCCESS; + } + + /* + * Create the access-list first, so we can generate sequence if + * none given (backward compatibility). + */ + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='ipv6'][name='%s']", name); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + if (seq_str == NULL) { + /* Use XPath to find the next sequence number. */ + sseq = acl_get_seq(vty, xpath); + snprintfrr(xpath_entry, sizeof(xpath_entry), + "%s/entry[sequence='%" PRId64 "']", xpath, sseq); + } else + snprintfrr(xpath_entry, sizeof(xpath_entry), + "%s/entry[sequence='%s']", xpath, seq_str); + + nb_cli_enqueue_change(vty, xpath_entry, NB_OP_CREATE, NULL); + + nb_cli_enqueue_change(vty, "./action", NB_OP_MODIFY, action); + if (prefix_str != NULL) { + nb_cli_enqueue_change(vty, "./ipv6-prefix", NB_OP_MODIFY, + prefix_str); + nb_cli_enqueue_change(vty, "./ipv6-exact-match", NB_OP_MODIFY, + exact ? "true" : "false"); + } else { + nb_cli_enqueue_change(vty, "./any", NB_OP_CREATE, NULL); + } + + return nb_cli_apply_changes(vty, xpath_entry); +} + +DEFPY_YANG( + no_ipv6_access_list, no_ipv6_access_list_cmd, + "no ipv6 access-list WORD$name [seq (1-4294967295)$seq] $action ", + NO_STR + IPV6_STR + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR + ACCESS_LIST_SEQ_STR + ACCESS_LIST_ACTION_STR + "IPv6 prefix\n" + "Exact match of the prefixes\n" + "Match any IPv6\n") +{ + struct access_list *acl; + struct lyd_node *dnode; + int64_t sseq; + struct prefix pany; + char xpath[XPATH_MAXLEN]; + char xpath_entry[XPATH_MAXLEN + 32]; + + /* If the user provided sequence number, then just go for it. */ + if (seq_str != NULL) { + snprintf( + xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='ipv6'][name='%s']/entry[sequence='%s']", + name, seq_str); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); + } + + /* Otherwise, to keep compatibility, we need to figure it out. */ + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='ipv6'][name='%s']", name); + + /* Access-list must exist before entries. */ + if (yang_dnode_exists(running_config->dnode, xpath) == false) + return CMD_WARNING_CONFIG_FAILED; + + /* Use access-list data structure to fetch sequence. */ + dnode = yang_dnode_get(running_config->dnode, xpath); + acl = nb_running_get_entry(dnode, NULL, true); + if (prefix == NULL) { + memset(&pany, 0, sizeof(pany)); + pany.family = AF_INET6; + sseq = acl_zebra_get_seq(acl, action, &pany, exact); + } else + sseq = acl_zebra_get_seq(acl, action, (struct prefix *)prefix, + exact); + if (sseq == -1) + return CMD_WARNING_CONFIG_FAILED; + + snprintfrr(xpath_entry, sizeof(xpath_entry), + "%s/entry[sequence='%" PRId64 "']", xpath, sseq); + nb_cli_enqueue_change(vty, xpath_entry, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + no_ipv6_access_list_all, no_ipv6_access_list_all_cmd, + "no ipv6 access-list WORD$name", + NO_STR + IPV6_STR + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR) +{ + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='ipv6'][name='%s']", name); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + ipv6_access_list_remark, ipv6_access_list_remark_cmd, + "ipv6 access-list WORD$name remark LINE...", + IPV6_STR + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR + ACCESS_LIST_REMARK_STR + ACCESS_LIST_REMARK_LINE_STR) +{ + int rv; + char *remark; + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='ipv6'][name='%s']", name); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + remark = argv_concat(argv, argc, 4); + nb_cli_enqueue_change(vty, "./remark", NB_OP_CREATE, remark); + rv = nb_cli_apply_changes(vty, xpath); + XFREE(MTYPE_TMP, remark); + + return rv; +} + +DEFPY_YANG( + no_ipv6_access_list_remark, no_ipv6_access_list_remark_cmd, + "no ipv6 access-list WORD$name remark", + NO_STR + IPV6_STR + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR + ACCESS_LIST_REMARK_STR) +{ + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='ipv6'][name='%s']/remark", + name); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +ALIAS( + no_ipv6_access_list_remark, no_ipv6_access_list_remark_line_cmd, + "no ipv6 access-list WORD$name remark LINE...", + NO_STR + IPV6_STR + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR + ACCESS_LIST_REMARK_STR + ACCESS_LIST_REMARK_LINE_STR) + +DEFPY_YANG( + mac_access_list, mac_access_list_cmd, + "mac access-list WORD$name [seq (1-4294967295)$seq] $action ", + MAC_STR + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR + ACCESS_LIST_SEQ_STR + ACCESS_LIST_ACTION_STR + "MAC address\n" + "Match any MAC address\n") +{ + int64_t sseq; + struct acl_dup_args ada = {}; + char xpath[XPATH_MAXLEN]; + char xpath_entry[XPATH_MAXLEN + 128]; + + /* + * Backward compatibility: don't complain about duplicated values, + * just silently accept. + */ + if (seq_str == NULL) { + ada.ada_type = "mac"; + ada.ada_name = name; + ada.ada_action = action; + + if (mac_str) { + ada.ada_xpath[0] = "./mac"; + ada.ada_value[0] = mac_str; + } else { + ada.ada_xpath[0] = "./any"; + ada.ada_value[0] = ""; + } + + /* Duplicated entry without sequence, just quit. */ + if (acl_is_dup(vty->candidate_config->dnode, &ada)) + return CMD_SUCCESS; + } + + /* + * Create the access-list first, so we can generate sequence if + * none given (backward compatibility). + */ + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='mac'][name='%s']", name); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + if (seq_str == NULL) { + /* Use XPath to find the next sequence number. */ + sseq = acl_get_seq(vty, xpath); + snprintfrr(xpath_entry, sizeof(xpath_entry), + "%s/entry[sequence='%" PRId64 "']", xpath, sseq); + } else + snprintfrr(xpath_entry, sizeof(xpath_entry), + "%s/entry[sequence='%s']", xpath, seq_str); + + nb_cli_enqueue_change(vty, xpath_entry, NB_OP_CREATE, NULL); + + nb_cli_enqueue_change(vty, "./action", NB_OP_MODIFY, action); + if (mac_str != NULL) { + nb_cli_enqueue_change(vty, "./mac", NB_OP_MODIFY, mac_str); + } else { + nb_cli_enqueue_change(vty, "./any", NB_OP_CREATE, NULL); + } + + return nb_cli_apply_changes(vty, xpath_entry); +} + +DEFPY_YANG( + no_mac_access_list, no_mac_access_list_cmd, + "no mac access-list WORD$name [seq (1-4294967295)$seq] $action ", + NO_STR + MAC_STR + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR + ACCESS_LIST_SEQ_STR + ACCESS_LIST_ACTION_STR + "MAC address\n" + "Match any MAC address\n") +{ + struct access_list *acl; + struct lyd_node *dnode; + int64_t sseq; + struct prefix pany; + char xpath[XPATH_MAXLEN]; + char xpath_entry[XPATH_MAXLEN + 32]; + + /* If the user provided sequence number, then just go for it. */ + if (seq_str != NULL) { + snprintf( + xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='mac'][name='%s']/entry[sequence='%s']", + name, seq_str); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); + } + + /* Otherwise, to keep compatibility, we need to figure it out. */ + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='mac'][name='%s']", name); + + /* Access-list must exist before entries. */ + if (yang_dnode_exists(running_config->dnode, xpath) == false) + return CMD_WARNING_CONFIG_FAILED; + + /* Use access-list data structure to fetch sequence. */ + dnode = yang_dnode_get(running_config->dnode, xpath); + acl = nb_running_get_entry(dnode, NULL, true); + if (prefix == NULL) { + memset(&pany, 0, sizeof(pany)); + pany.family = AF_ETHERNET; + sseq = acl_zebra_get_seq(acl, action, &pany, false); + } else + sseq = acl_zebra_get_seq(acl, action, (struct prefix *)prefix, + false); + if (sseq == -1) + return CMD_WARNING_CONFIG_FAILED; + + snprintfrr(xpath_entry, sizeof(xpath_entry), + "%s/entry[sequence='%" PRId64 "']", xpath, sseq); + nb_cli_enqueue_change(vty, xpath_entry, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + no_mac_access_list_all, no_mac_access_list_all_cmd, + "no mac access-list WORD$name", + NO_STR + MAC_STR + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR) +{ + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='mac'][name='%s']", name); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + mac_access_list_remark, mac_access_list_remark_cmd, + "mac access-list WORD$name remark LINE...", + MAC_STR + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR + ACCESS_LIST_REMARK_STR + ACCESS_LIST_REMARK_LINE_STR) +{ + int rv; + char *remark; + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='mac'][name='%s']", name); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + remark = argv_concat(argv, argc, 4); + nb_cli_enqueue_change(vty, "./remark", NB_OP_CREATE, remark); + rv = nb_cli_apply_changes(vty, xpath); + XFREE(MTYPE_TMP, remark); + + return rv; +} + +DEFPY_YANG( + no_mac_access_list_remark, no_mac_access_list_remark_cmd, + "no mac access-list WORD$name remark", + NO_STR + MAC_STR + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR + ACCESS_LIST_REMARK_STR) +{ + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='mac'][name='%s']/remark", + name); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +ALIAS( + no_mac_access_list_remark, no_mac_access_list_remark_line_cmd, + "no mac access-list WORD$name remark LINE...", + NO_STR + MAC_STR + ACCESS_LIST_STR + ACCESS_LIST_ZEBRA_STR + ACCESS_LIST_REMARK_STR + ACCESS_LIST_REMARK_LINE_STR) + +void access_list_show(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + int type = yang_dnode_get_enum(dnode, "../type"); + struct prefix p; + bool is_any; + bool is_exact = false; + bool cisco_style = false; + bool cisco_extended = false; + struct in_addr addr, mask; + char macstr[PREFIX2STR_BUFFER]; + + is_any = yang_dnode_exists(dnode, "./any"); + switch (type) { + case YALT_IPV4: + if (is_any) + break; + + if (yang_dnode_exists(dnode, "./host") + || yang_dnode_exists(dnode, "./network/address") + || yang_dnode_exists(dnode, "./source-any")) { + cisco_style = true; + if (yang_dnode_exists(dnode, "./destination-host") + || yang_dnode_exists( + dnode, "./destination-network/address") + || yang_dnode_exists(dnode, "./destination-any")) + cisco_extended = true; + } else { + yang_dnode_get_prefix(&p, dnode, "./ipv4-prefix"); + is_exact = yang_dnode_get_bool(dnode, + "./ipv4-exact-match"); + } + break; + case YALT_IPV6: /* ipv6 */ + vty_out(vty, "ipv6 "); + if (is_any) + break; + + yang_dnode_get_prefix(&p, dnode, "./ipv6-prefix"); + is_exact = yang_dnode_get_bool(dnode, "./ipv6-exact-match"); + break; + case YALT_MAC: /* mac */ + vty_out(vty, "mac "); + if (is_any) + break; + + yang_dnode_get_prefix(&p, dnode, "./mac"); + break; + } + + vty_out(vty, "access-list %s seq %s %s", + yang_dnode_get_string(dnode, "../name"), + yang_dnode_get_string(dnode, "./sequence"), + yang_dnode_get_string(dnode, "./action")); + + /* Handle Cisco style access lists. */ + if (cisco_style) { + if (cisco_extended) + vty_out(vty, " ip"); + + if (yang_dnode_exists(dnode, "./network")) { + yang_dnode_get_ipv4(&addr, dnode, "./network/address"); + yang_dnode_get_ipv4(&mask, dnode, "./network/mask"); + vty_out(vty, " %pI4 %pI4", &addr, &mask); + } else if (yang_dnode_exists(dnode, "./host")) { + if (cisco_extended) + vty_out(vty, " host"); + + vty_out(vty, " %s", + yang_dnode_get_string(dnode, "./host")); + } else if (yang_dnode_exists(dnode, "./source-any")) + vty_out(vty, " any"); + + /* Not extended, exit earlier. */ + if (!cisco_extended) { + vty_out(vty, "\n"); + return; + } + + /* Handle destination address. */ + if (yang_dnode_exists(dnode, "./destination-network")) { + yang_dnode_get_ipv4(&addr, dnode, + "./destination-network/address"); + yang_dnode_get_ipv4(&mask, dnode, + "./destination-network/mask"); + vty_out(vty, " %pI4 %pI4", &addr, &mask); + } else if (yang_dnode_exists(dnode, "./destination-host")) + vty_out(vty, " host %s", + yang_dnode_get_string(dnode, + "./destination-host")); + else if (yang_dnode_exists(dnode, "./destination-any")) + vty_out(vty, " any"); + + vty_out(vty, "\n"); + return; + } + + /* Zebra style access list. */ + if (!is_any) { + /* If type is MAC don't show '/mask'. */ + if (type == 2 /* mac */) { + prefix_mac2str(&p.u.prefix_eth, macstr, sizeof(macstr)); + vty_out(vty, " %s", macstr); + } else + vty_out(vty, " %pFX", &p); + } else + vty_out(vty, " any"); + + if (is_exact) + vty_out(vty, " exact-match"); + + vty_out(vty, "\n"); +} + +void access_list_remark_show(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + int type = yang_dnode_get_enum(dnode, "../type"); + + switch (type) { + case YALT_IPV4: + break; + case YALT_IPV6: + vty_out(vty, "ipv6 "); + break; + case YALT_MAC: + vty_out(vty, "mac "); + break; + } + + vty_out(vty, "access-list %s remark %s\n", + yang_dnode_get_string(dnode, "../name"), + yang_dnode_get_string(dnode, NULL)); +} + +/* + * Prefix lists. + */ + +/** + * Remove main data structure prefix list if there are no more entries or + * remark. This fixes compatibility with old CLI and tests. + */ +static int plist_remove_if_empty(struct vty *vty, const char *iptype, + const char *name) +{ + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/prefix-list[type='%s'][name='%s']/remark", + iptype, name); + /* List is not empty if there is a remark, check that: */ + if (yang_dnode_exists(vty->candidate_config->dnode, xpath)) + return CMD_SUCCESS; + + /* Check if we have any entries: */ + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/prefix-list[type='%s'][name='%s']", iptype, + name); + /* + * NOTE: if the list is empty it will return the first sequence + * number: 5. + */ + if (acl_get_seq(vty, xpath) != 5) + return CMD_SUCCESS; + + /* Nobody is using this list, lets remove it. */ + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +static int plist_remove(struct vty *vty, const char *iptype, const char *name, + const char *seq, const char *action, struct prefix *p, + long ge, long le) +{ + struct prefix_list_entry *pentry; + enum prefix_list_type plt; + struct prefix_list *pl; + struct lyd_node *dnode; + char xpath[XPATH_MAXLEN]; + char xpath_entry[XPATH_MAXLEN + 32]; + int rv; + + /* If the user provided sequence number, then just go for it. */ + if (seq != NULL) { + snprintf( + xpath, sizeof(xpath), + "/frr-filter:lib/prefix-list[type='%s'][name='%s']/entry[sequence='%s']", + iptype, name, seq); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + rv = nb_cli_apply_changes(vty, NULL); + if (rv == CMD_SUCCESS) + return plist_remove_if_empty(vty, iptype, name); + + return rv; + } + + /* Otherwise, to keep compatibility, we need to figure it out. */ + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/prefix-list[type='%s'][name='%s']", iptype, + name); + + /* Access-list must exist before entries. */ + if (yang_dnode_exists(running_config->dnode, xpath) == false) + return CMD_WARNING_CONFIG_FAILED; + + /* Use access-list data structure to fetch sequence. */ + assert(action != NULL); + if (strcmp(action, "permit") == 0) + plt = PREFIX_PERMIT; + else + plt = PREFIX_DENY; + + dnode = yang_dnode_get(running_config->dnode, xpath); + pl = nb_running_get_entry(dnode, NULL, true); + pentry = prefix_list_entry_lookup(pl, p, plt, -1, le, ge); + if (pentry == NULL) + return CMD_WARNING_CONFIG_FAILED; + + snprintfrr(xpath_entry, sizeof(xpath_entry), + "%s/entry[sequence='%" PRId64 "']", xpath, pentry->seq); + nb_cli_enqueue_change(vty, xpath_entry, NB_OP_DESTROY, NULL); + + rv = nb_cli_apply_changes(vty, NULL); + if (rv == CMD_SUCCESS) + return plist_remove_if_empty(vty, iptype, name); + + return rv; +} + +DEFPY_YANG( + ip_prefix_list, ip_prefix_list_cmd, + "ip prefix-list WORD$name [seq (1-4294967295)$seq] $action ", + IP_STR + PREFIX_LIST_STR + PREFIX_LIST_NAME_STR + ACCESS_LIST_SEQ_STR + ACCESS_LIST_ACTION_STR + "Any prefix match. Same as \"0.0.0.0/0 le 32\"\n" + "IP prefix /, e.g., 35.0.0.0/8\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n") +{ + int64_t sseq; + int arg_idx = 0; + struct plist_dup_args pda = {}; + char xpath[XPATH_MAXLEN]; + char xpath_entry[XPATH_MAXLEN + 128]; + + /* + * Backward compatibility: don't complain about duplicated values, + * just silently accept. + */ + if (seq_str == NULL) { + pda.pda_type = "ipv4"; + pda.pda_name = name; + pda.pda_action = action; + if (prefix_str) { + pda.pda_xpath[arg_idx] = "./ipv4-prefix"; + pda.pda_value[arg_idx] = prefix_str; + arg_idx++; + if (ge_str) { + pda.pda_xpath[arg_idx] = + "./ipv4-prefix-length-greater-or-equal"; + pda.pda_value[arg_idx] = ge_str; + arg_idx++; + } + if (le_str) { + pda.pda_xpath[arg_idx] = + "./ipv4-prefix-length-lesser-or-equal"; + pda.pda_value[arg_idx] = le_str; + arg_idx++; + } + } else { + pda.pda_xpath[0] = "./any"; + pda.pda_value[0] = ""; + } + + /* Duplicated entry without sequence, just quit. */ + if (plist_is_dup(vty->candidate_config->dnode, &pda)) + return CMD_SUCCESS; + } + + /* + * Create the prefix-list first, so we can generate sequence if + * none given (backward compatibility). + */ + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/prefix-list[type='ipv4'][name='%s']", name); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + if (seq_str == NULL) { + /* Use XPath to find the next sequence number. */ + sseq = acl_get_seq(vty, xpath); + snprintfrr(xpath_entry, sizeof(xpath_entry), + "%s/entry[sequence='%" PRId64 "']", xpath, sseq); + } else + snprintfrr(xpath_entry, sizeof(xpath_entry), + "%s/entry[sequence='%s']", xpath, seq_str); + + nb_cli_enqueue_change(vty, xpath_entry, NB_OP_CREATE, NULL); + + nb_cli_enqueue_change(vty, "./action", NB_OP_MODIFY, action); + if (prefix_str != NULL) { + nb_cli_enqueue_change(vty, "./ipv4-prefix", NB_OP_MODIFY, + prefix_str); + + if (ge_str) + nb_cli_enqueue_change( + vty, "./ipv4-prefix-length-greater-or-equal", + NB_OP_MODIFY, ge_str); + if (le_str) + nb_cli_enqueue_change( + vty, "./ipv4-prefix-length-lesser-or-equal", + NB_OP_MODIFY, le_str); + } else { + nb_cli_enqueue_change(vty, "./any", NB_OP_CREATE, NULL); + } + + return nb_cli_apply_changes(vty, xpath_entry); +} + +DEFPY_YANG( + no_ip_prefix_list, no_ip_prefix_list_cmd, + "no ip prefix-list WORD$name [seq (1-4294967295)$seq] $action ", + NO_STR + IP_STR + PREFIX_LIST_STR + PREFIX_LIST_NAME_STR + ACCESS_LIST_SEQ_STR + ACCESS_LIST_ACTION_STR + "Any prefix match. Same as \"0.0.0.0/0 le 32\"\n" + "IP prefix /, e.g., 35.0.0.0/8\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n") +{ + return plist_remove(vty, "ipv4", name, seq_str, action, + (struct prefix *)prefix, ge, le); +} + +DEFPY_YANG( + no_ip_prefix_list_seq, no_ip_prefix_list_seq_cmd, + "no ip prefix-list WORD$name seq (1-4294967295)$seq", + NO_STR + IP_STR + PREFIX_LIST_STR + PREFIX_LIST_NAME_STR + ACCESS_LIST_SEQ_STR) +{ + return plist_remove(vty, "ipv4", name, seq_str, NULL, NULL, 0, 0); +} + +DEFPY_YANG( + no_ip_prefix_list_all, no_ip_prefix_list_all_cmd, + "no ip prefix-list WORD$name", + NO_STR + IP_STR + PREFIX_LIST_STR + PREFIX_LIST_NAME_STR) +{ + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/prefix-list[type='ipv4'][name='%s']", name); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + ip_prefix_list_remark, ip_prefix_list_remark_cmd, + "ip prefix-list WORD$name description LINE...", + IP_STR + PREFIX_LIST_STR + PREFIX_LIST_NAME_STR + ACCESS_LIST_REMARK_STR + ACCESS_LIST_REMARK_LINE_STR) +{ + int rv; + char *remark; + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/prefix-list[type='ipv4'][name='%s']", name); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + remark = argv_concat(argv, argc, 4); + nb_cli_enqueue_change(vty, "./remark", NB_OP_CREATE, remark); + rv = nb_cli_apply_changes(vty, xpath); + XFREE(MTYPE_TMP, remark); + + return rv; +} + +DEFPY_YANG( + no_ip_prefix_list_remark, no_ip_prefix_list_remark_cmd, + "no ip prefix-list WORD$name description", + NO_STR + IP_STR + PREFIX_LIST_STR + PREFIX_LIST_NAME_STR + ACCESS_LIST_REMARK_STR) +{ + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/prefix-list[type='ipv4'][name='%s']/remark", + name); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +ALIAS( + no_ip_prefix_list_remark, no_ip_prefix_list_remark_line_cmd, + "no ip prefix-list WORD$name description LINE...", + NO_STR + IP_STR + PREFIX_LIST_STR + PREFIX_LIST_NAME_STR + ACCESS_LIST_REMARK_STR + ACCESS_LIST_REMARK_LINE_STR) + +DEFPY_YANG( + ipv6_prefix_list, ipv6_prefix_list_cmd, + "ipv6 prefix-list WORD$name [seq (1-4294967295)] $action ", + IPV6_STR + PREFIX_LIST_STR + PREFIX_LIST_NAME_STR + ACCESS_LIST_SEQ_STR + ACCESS_LIST_ACTION_STR + "Any prefix match. Same as \"::0/0 le 128\"\n" + "IPv6 prefix /, e.g., 3ffe::/16\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n") +{ + int64_t sseq; + int arg_idx = 0; + struct plist_dup_args pda = {}; + char xpath[XPATH_MAXLEN]; + char xpath_entry[XPATH_MAXLEN + 128]; + + /* + * Backward compatibility: don't complain about duplicated values, + * just silently accept. + */ + if (seq_str == NULL) { + pda.pda_type = "ipv6"; + pda.pda_name = name; + pda.pda_action = action; + if (prefix_str) { + pda.pda_xpath[arg_idx] = "./ipv6-prefix"; + pda.pda_value[arg_idx] = prefix_str; + arg_idx++; + if (ge_str) { + pda.pda_xpath[arg_idx] = + "./ipv6-prefix-length-greater-or-equal"; + pda.pda_value[arg_idx] = ge_str; + arg_idx++; + } + if (le_str) { + pda.pda_xpath[arg_idx] = + "./ipv6-prefix-length-lesser-or-equal"; + pda.pda_value[arg_idx] = le_str; + arg_idx++; + } + } else { + pda.pda_xpath[0] = "./any"; + pda.pda_value[0] = ""; + } + + /* Duplicated entry without sequence, just quit. */ + if (plist_is_dup(vty->candidate_config->dnode, &pda)) + return CMD_SUCCESS; + } + + /* + * Create the prefix-list first, so we can generate sequence if + * none given (backward compatibility). + */ + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/prefix-list[type='ipv6'][name='%s']", name); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + if (seq_str == NULL) { + /* Use XPath to find the next sequence number. */ + sseq = acl_get_seq(vty, xpath); + snprintfrr(xpath_entry, sizeof(xpath_entry), + "%s/entry[sequence='%" PRId64 "']", xpath, sseq); + } else + snprintfrr(xpath_entry, sizeof(xpath_entry), + "%s/entry[sequence='%s']", xpath, seq_str); + + nb_cli_enqueue_change(vty, xpath_entry, NB_OP_CREATE, NULL); + + nb_cli_enqueue_change(vty, "./action", NB_OP_MODIFY, action); + if (prefix_str != NULL) { + nb_cli_enqueue_change(vty, "./ipv6-prefix", NB_OP_MODIFY, + prefix_str); + + if (ge_str) + nb_cli_enqueue_change( + vty, "./ipv6-prefix-length-greater-or-equal", + NB_OP_MODIFY, ge_str); + if (le_str) + nb_cli_enqueue_change( + vty, "./ipv6-prefix-length-lesser-or-equal", + NB_OP_MODIFY, le_str); + } else { + nb_cli_enqueue_change(vty, "./any", NB_OP_CREATE, NULL); + } + + return nb_cli_apply_changes(vty, xpath_entry); +} + +DEFPY_YANG( + no_ipv6_prefix_list, no_ipv6_prefix_list_cmd, + "no ipv6 prefix-list WORD$name [seq (1-4294967295)$seq] $action ", + NO_STR + IPV6_STR + PREFIX_LIST_STR + PREFIX_LIST_NAME_STR + ACCESS_LIST_SEQ_STR + ACCESS_LIST_ACTION_STR + "Any prefix match. Same as \"::0/0 le 128\"\n" + "IPv6 prefix /, e.g., 3ffe::/16\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n") +{ + return plist_remove(vty, "ipv6", name, seq_str, action, + (struct prefix *)prefix, ge, le); +} + +DEFPY_YANG( + no_ipv6_prefix_list_seq, no_ipv6_prefix_list_seq_cmd, + "no ipv6 prefix-list WORD$name seq (1-4294967295)$seq", + NO_STR + IPV6_STR + PREFIX_LIST_STR + PREFIX_LIST_NAME_STR + ACCESS_LIST_SEQ_STR) +{ + return plist_remove(vty, "ipv6", name, seq_str, NULL, NULL, 0, 0); +} + +DEFPY_YANG( + no_ipv6_prefix_list_all, no_ipv6_prefix_list_all_cmd, + "no ipv6 prefix-list WORD$name", + NO_STR + IPV6_STR + PREFIX_LIST_STR + PREFIX_LIST_NAME_STR) +{ + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/prefix-list[type='ipv6'][name='%s']", name); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + ipv6_prefix_list_remark, ipv6_prefix_list_remark_cmd, + "ipv6 prefix-list WORD$name description LINE...", + IPV6_STR + PREFIX_LIST_STR + PREFIX_LIST_NAME_STR + ACCESS_LIST_REMARK_STR + ACCESS_LIST_REMARK_LINE_STR) +{ + int rv; + char *remark; + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/prefix-list[type='ipv6'][name='%s']", name); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + remark = argv_concat(argv, argc, 4); + nb_cli_enqueue_change(vty, "./remark", NB_OP_CREATE, remark); + rv = nb_cli_apply_changes(vty, xpath); + XFREE(MTYPE_TMP, remark); + + return rv; +} + +DEFPY_YANG( + no_ipv6_prefix_list_remark, no_ipv6_prefix_list_remark_cmd, + "no ipv6 prefix-list WORD$name description", + NO_STR + IPV6_STR + PREFIX_LIST_STR + PREFIX_LIST_NAME_STR + ACCESS_LIST_REMARK_STR) +{ + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/prefix-list[type='ipv6'][name='%s']/remark", + name); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +ALIAS( + no_ipv6_prefix_list_remark, no_ipv6_prefix_list_remark_line_cmd, + "no ipv6 prefix-list WORD$name description LINE...", + NO_STR + IPV6_STR + PREFIX_LIST_STR + PREFIX_LIST_NAME_STR + ACCESS_LIST_REMARK_STR + ACCESS_LIST_REMARK_LINE_STR) + +void prefix_list_show(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + int type = yang_dnode_get_enum(dnode, "../type"); + const char *ge_str = NULL, *le_str = NULL; + bool is_any; + struct prefix p; + + is_any = yang_dnode_exists(dnode, "./any"); + switch (type) { + case YPLT_IPV4: + if (!is_any) + yang_dnode_get_prefix(&p, dnode, "./ipv4-prefix"); + if (yang_dnode_exists(dnode, + "./ipv4-prefix-length-greater-or-equal")) + ge_str = yang_dnode_get_string( + dnode, "./ipv4-prefix-length-greater-or-equal"); + if (yang_dnode_exists(dnode, + "./ipv4-prefix-length-lesser-or-equal")) + le_str = yang_dnode_get_string( + dnode, "./ipv4-prefix-length-lesser-or-equal"); + + vty_out(vty, "ip "); + break; + case YPLT_IPV6: + if (!is_any) + yang_dnode_get_prefix(&p, dnode, "ipv6-prefix"); + if (yang_dnode_exists(dnode, + "./ipv6-prefix-length-greater-or-equal")) + ge_str = yang_dnode_get_string( + dnode, "./ipv6-prefix-length-greater-or-equal"); + if (yang_dnode_exists(dnode, + "./ipv6-prefix-length-lesser-or-equal")) + le_str = yang_dnode_get_string( + dnode, "./ipv6-prefix-length-lesser-or-equal"); + + vty_out(vty, "ipv6 "); + break; + } + + vty_out(vty, "prefix-list %s seq %s %s", + yang_dnode_get_string(dnode, "../name"), + yang_dnode_get_string(dnode, "./sequence"), + yang_dnode_get_string(dnode, "./action")); + + if (is_any) { + vty_out(vty, " any\n"); + return; + } + + vty_out(vty, " %pFX", &p); + if (ge_str) + vty_out(vty, " ge %s", ge_str); + if (le_str) + vty_out(vty, " le %s", le_str); + + vty_out(vty, "\n"); +} + +void prefix_list_remark_show(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + int type = yang_dnode_get_enum(dnode, "../type"); + + switch (type) { + case YPLT_IPV4: + vty_out(vty, "ip "); + break; + case YPLT_IPV6: + vty_out(vty, "ipv6 "); + break; + } + + vty_out(vty, "prefix-list %s description %s\n", + yang_dnode_get_string(dnode, "../name"), + yang_dnode_get_string(dnode, NULL)); +} + +void filter_cli_init(void) +{ + /* access-list cisco-style (legacy). */ + install_element(CONFIG_NODE, &access_list_std_cmd); + install_element(CONFIG_NODE, &no_access_list_std_cmd); + install_element(CONFIG_NODE, &access_list_ext_cmd); + install_element(CONFIG_NODE, &no_access_list_ext_cmd); + + /* access-list zebra-style. */ + install_element(CONFIG_NODE, &access_list_cmd); + install_element(CONFIG_NODE, &no_access_list_cmd); + install_element(CONFIG_NODE, &no_access_list_all_cmd); + install_element(CONFIG_NODE, &access_list_remark_cmd); + install_element(CONFIG_NODE, &no_access_list_remark_cmd); + install_element(CONFIG_NODE, &no_access_list_remark_line_cmd); + + install_element(CONFIG_NODE, &ipv6_access_list_cmd); + install_element(CONFIG_NODE, &no_ipv6_access_list_cmd); + install_element(CONFIG_NODE, &no_ipv6_access_list_all_cmd); + install_element(CONFIG_NODE, &ipv6_access_list_remark_cmd); + install_element(CONFIG_NODE, &no_ipv6_access_list_remark_cmd); + install_element(CONFIG_NODE, &no_ipv6_access_list_remark_line_cmd); + + install_element(CONFIG_NODE, &mac_access_list_cmd); + install_element(CONFIG_NODE, &no_mac_access_list_cmd); + install_element(CONFIG_NODE, &no_mac_access_list_all_cmd); + install_element(CONFIG_NODE, &mac_access_list_remark_cmd); + install_element(CONFIG_NODE, &no_mac_access_list_remark_cmd); + install_element(CONFIG_NODE, &no_mac_access_list_remark_line_cmd); + + /* prefix lists. */ + install_element(CONFIG_NODE, &ip_prefix_list_cmd); + install_element(CONFIG_NODE, &no_ip_prefix_list_cmd); + install_element(CONFIG_NODE, &no_ip_prefix_list_seq_cmd); + install_element(CONFIG_NODE, &no_ip_prefix_list_all_cmd); + install_element(CONFIG_NODE, &ip_prefix_list_remark_cmd); + install_element(CONFIG_NODE, &no_ip_prefix_list_remark_cmd); + install_element(CONFIG_NODE, &no_ip_prefix_list_remark_line_cmd); + + install_element(CONFIG_NODE, &ipv6_prefix_list_cmd); + install_element(CONFIG_NODE, &no_ipv6_prefix_list_cmd); + install_element(CONFIG_NODE, &no_ipv6_prefix_list_seq_cmd); + install_element(CONFIG_NODE, &no_ipv6_prefix_list_all_cmd); + install_element(CONFIG_NODE, &ipv6_prefix_list_remark_cmd); + install_element(CONFIG_NODE, &no_ipv6_prefix_list_remark_cmd); + install_element(CONFIG_NODE, &no_ipv6_prefix_list_remark_line_cmd); +} diff --git a/lib/filter_nb.c b/lib/filter_nb.c new file mode 100644 index 0000000000..866ed2950e --- /dev/null +++ b/lib/filter_nb.c @@ -0,0 +1,1619 @@ +/* + * FRR filter northbound implementation. + * + * Copyright (C) 2019 Network Device Education Foundation, Inc. ("NetDEF") + * Rafael Zalamena + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#include "zebra.h" + +#include "lib/northbound.h" +#include "lib/prefix.h" +#include "lib/printfrr.h" + +#include "lib/filter.h" +#include "lib/plist.h" +#include "lib/plist_int.h" +#include "lib/routemap.h" + +/* Helper function. */ +static void acl_notify_route_map(struct access_list *acl, int route_map_event) +{ + switch (route_map_event) { + case RMAP_EVENT_FILTER_ADDED: + if (acl->master->add_hook) + (*acl->master->add_hook)(acl); + break; + case RMAP_EVENT_FILTER_DELETED: + if (acl->master->delete_hook) + (*acl->master->delete_hook)(acl); + break; + } + + route_map_notify_dependencies(acl->name, route_map_event); +} + +static enum nb_error prefix_list_length_validate(struct nb_cb_modify_args *args) +{ + int type = yang_dnode_get_enum(args->dnode, "../../type"); + const char *xpath_le = NULL, *xpath_ge = NULL; + struct prefix p; + uint8_t le, ge; + + if (type == YPLT_IPV4) { + yang_dnode_get_prefix(&p, args->dnode, "../ipv4-prefix"); + xpath_le = "../ipv4-prefix-length-lesser-or-equal"; + xpath_ge = "../ipv4-prefix-length-greater-or-equal"; + } else { + yang_dnode_get_prefix(&p, args->dnode, "../ipv6-prefix"); + xpath_le = "../ipv6-prefix-length-lesser-or-equal"; + xpath_ge = "../ipv6-prefix-length-greater-or-equal"; + } + + /* + * Check rule: + * prefix length <= le. + */ + if (yang_dnode_exists(args->dnode, xpath_le)) { + le = yang_dnode_get_uint8(args->dnode, xpath_le); + if (p.prefixlen > le) + goto log_and_fail; + } + + /* + * Check rule: + * prefix length <= ge. + */ + if (yang_dnode_exists(args->dnode, xpath_ge)) { + ge = yang_dnode_get_uint8(args->dnode, xpath_ge); + if (p.prefixlen > ge) + goto log_and_fail; + } + + /* + * Check rule: + * ge <= le. + */ + if (yang_dnode_exists(args->dnode, xpath_le) + && yang_dnode_exists(args->dnode, xpath_ge)) { + le = yang_dnode_get_uint8(args->dnode, xpath_le); + ge = yang_dnode_get_uint8(args->dnode, xpath_ge); + if (ge > le) + goto log_and_fail; + } + + return NB_OK; + +log_and_fail: + snprintfrr( + args->errmsg, args->errmsg_len, + "Invalid prefix range for %pFX: Make sure that mask length <= ge <= le", + &p); + return NB_ERR_VALIDATION; +} + +/** + * Sets prefix list entry to blank value. + * + * \param[out] ple prefix list entry to modify. + */ +static void prefix_list_entry_set_empty(struct prefix_list_entry *ple) +{ + ple->any = false; + memset(&ple->prefix, 0, sizeof(ple->prefix)); + ple->ge = 0; + ple->le = 0; +} + +/** + * Unsets the cisco style rule for addresses so it becomes disabled (the + * equivalent of setting: `0.0.0.0/32`). + * + * \param addr address part. + * \param mask mask part. + */ +static void cisco_unset_addr_mask(struct in_addr *addr, struct in_addr *mask) +{ + addr->s_addr = INADDR_ANY; + mask->s_addr = CISCO_BIN_HOST_WILDCARD_MASK; +} + +static int _acl_is_dup(const struct lyd_node *dnode, void *arg) +{ + struct acl_dup_args *ada = arg; + int idx; + + /* This entry is the caller, so skip it. */ + if (ada->ada_entry_dnode + && ada->ada_entry_dnode == dnode) + return YANG_ITER_CONTINUE; + + if (strcmp(yang_dnode_get_string(dnode, "action"), ada->ada_action)) + return YANG_ITER_CONTINUE; + + /* Check if all values match. */ + for (idx = 0; idx < ADA_MAX_VALUES; idx++) { + /* No more values. */ + if (ada->ada_xpath[idx] == NULL) + break; + + /* Not same type, just skip it. */ + if (!yang_dnode_exists(dnode, ada->ada_xpath[idx])) + return YANG_ITER_CONTINUE; + + /* Check if different value. */ + if (strcmp(yang_dnode_get_string(dnode, ada->ada_xpath[idx]), + ada->ada_value[idx])) + return YANG_ITER_CONTINUE; + } + + ada->ada_found = true; + + return YANG_ITER_STOP; +} + +bool acl_is_dup(const struct lyd_node *dnode, struct acl_dup_args *ada) +{ + ada->ada_found = false; + + yang_dnode_iterate( + _acl_is_dup, ada, dnode, + "/frr-filter:lib/access-list[type='%s'][name='%s']/entry", + ada->ada_type, ada->ada_name); + + return ada->ada_found; +} + +static bool acl_cisco_is_dup(const struct lyd_node *dnode) +{ + const struct lyd_node *entry_dnode = + yang_dnode_get_parent(dnode, "entry"); + struct acl_dup_args ada = {}; + int idx = 0, arg_idx = 0; + static const char *cisco_entries[] = { + "./host", + "./network/address", + "./network/mask", + "./source-any", + "./destination-host", + "./destination-network/address", + "./destination-network/mask", + "./destination-any", + NULL + }; + + /* Initialize. */ + ada.ada_type = "ipv4"; + ada.ada_name = yang_dnode_get_string(entry_dnode, "../name"); + ada.ada_action = yang_dnode_get_string(entry_dnode, "action"); + ada.ada_entry_dnode = entry_dnode; + + /* Load all values/XPaths. */ + while (cisco_entries[idx] != NULL) { + if (!yang_dnode_exists(entry_dnode, cisco_entries[idx])) { + idx++; + continue; + } + + ada.ada_xpath[arg_idx] = cisco_entries[idx]; + ada.ada_value[arg_idx] = + yang_dnode_get_string(entry_dnode, cisco_entries[idx]); + arg_idx++; + idx++; + } + + return acl_is_dup(entry_dnode, &ada); +} + +static bool acl_zebra_is_dup(const struct lyd_node *dnode, + enum yang_access_list_type type) +{ + const struct lyd_node *entry_dnode = + yang_dnode_get_parent(dnode, "entry"); + struct acl_dup_args ada = {}; + int idx = 0, arg_idx = 0; + static const char *zebra_entries[] = { + "./ipv4-prefix", + "./ipv4-exact-match", + "./ipv6-prefix", + "./ipv6-exact-match", + "./mac", + "./any", + NULL + }; + + /* Initialize. */ + switch (type) { + case YALT_IPV4: + ada.ada_type = "ipv4"; + break; + case YALT_IPV6: + ada.ada_type = "ipv6"; + break; + case YALT_MAC: + ada.ada_type = "mac"; + break; + } + ada.ada_name = yang_dnode_get_string(entry_dnode, "../name"); + ada.ada_action = yang_dnode_get_string(entry_dnode, "action"); + ada.ada_entry_dnode = entry_dnode; + + /* Load all values/XPaths. */ + while (zebra_entries[idx] != NULL) { + if (!yang_dnode_exists(entry_dnode, zebra_entries[idx])) { + idx++; + continue; + } + + ada.ada_xpath[arg_idx] = zebra_entries[idx]; + ada.ada_value[arg_idx] = + yang_dnode_get_string(entry_dnode, zebra_entries[idx]); + arg_idx++; + idx++; + } + + return acl_is_dup(entry_dnode, &ada); +} + +static int _plist_is_dup(const struct lyd_node *dnode, void *arg) +{ + struct plist_dup_args *pda = arg; + int idx; + + /* This entry is the caller, so skip it. */ + if (pda->pda_entry_dnode + && pda->pda_entry_dnode == dnode) + return YANG_ITER_CONTINUE; + + if (strcmp(yang_dnode_get_string(dnode, "action"), pda->pda_action)) + return YANG_ITER_CONTINUE; + + /* Check if all values match. */ + for (idx = 0; idx < PDA_MAX_VALUES; idx++) { + /* No more values. */ + if (pda->pda_xpath[idx] == NULL) + break; + + /* Not same type, just skip it. */ + if (!yang_dnode_exists(dnode, pda->pda_xpath[idx])) + return YANG_ITER_CONTINUE; + + /* Check if different value. */ + if (strcmp(yang_dnode_get_string(dnode, pda->pda_xpath[idx]), + pda->pda_value[idx])) + return YANG_ITER_CONTINUE; + } + + pda->pda_found = true; + + return YANG_ITER_STOP; +} + +bool plist_is_dup(const struct lyd_node *dnode, struct plist_dup_args *pda) +{ + pda->pda_found = false; + + yang_dnode_iterate( + _plist_is_dup, pda, dnode, + "/frr-filter:lib/prefix-list[type='%s'][name='%s']/entry", + pda->pda_type, pda->pda_name); + + return pda->pda_found; +} + +static bool plist_is_dup_nb(const struct lyd_node *dnode) +{ + const struct lyd_node *entry_dnode = + yang_dnode_get_parent(dnode, "entry"); + struct plist_dup_args pda = {}; + int idx = 0, arg_idx = 0; + static const char *entries[] = { + "./ipv4-prefix", + "./ipv4-prefix-length-greater-or-equal", + "./ipv4-prefix-length-lesser-or-equal", + "./ipv6-prefix", + "./ipv6-prefix-length-greater-or-equal", + "./ipv6-prefix-length-lesser-or-equal", + "./any", + NULL + }; + + /* Initialize. */ + pda.pda_type = yang_dnode_get_string(entry_dnode, "../type"); + pda.pda_name = yang_dnode_get_string(entry_dnode, "../name"); + pda.pda_action = yang_dnode_get_string(entry_dnode, "action"); + pda.pda_entry_dnode = entry_dnode; + + /* Load all values/XPaths. */ + while (entries[idx] != NULL) { + if (!yang_dnode_exists(entry_dnode, entries[idx])) { + idx++; + continue; + } + + pda.pda_xpath[arg_idx] = entries[idx]; + pda.pda_value[arg_idx] = + yang_dnode_get_string(entry_dnode, entries[idx]); + arg_idx++; + idx++; + } + + return plist_is_dup(entry_dnode, &pda); +} + +/* + * XPath: /frr-filter:lib/access-list + */ +static int lib_access_list_create(struct nb_cb_create_args *args) +{ + struct access_list *acl = NULL; + const char *acl_name; + int type; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + type = yang_dnode_get_enum(args->dnode, "./type"); + acl_name = yang_dnode_get_string(args->dnode, "./name"); + + switch (type) { + case YALT_IPV4: + acl = access_list_get(AFI_IP, acl_name); + break; + case YALT_IPV6: + acl = access_list_get(AFI_IP6, acl_name); + break; + case YALT_MAC: + acl = access_list_get(AFI_L2VPN, acl_name); + break; + } + + nb_running_set_entry(args->dnode, acl); + + return NB_OK; +} + +static int lib_access_list_destroy(struct nb_cb_destroy_args *args) +{ + struct access_list *acl; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + acl = nb_running_unset_entry(args->dnode); + access_list_delete(acl); + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/access-list/remark + */ +static int lib_access_list_remark_modify(struct nb_cb_modify_args *args) +{ + struct access_list *acl; + const char *remark; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + acl = nb_running_get_entry(args->dnode, NULL, true); + if (acl->remark) + XFREE(MTYPE_TMP, acl->remark); + + remark = yang_dnode_get_string(args->dnode, NULL); + acl->remark = XSTRDUP(MTYPE_TMP, remark); + + return NB_OK; +} + +static int +lib_access_list_remark_destroy(struct nb_cb_destroy_args *args) +{ + struct access_list *acl; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + acl = nb_running_get_entry(args->dnode, NULL, true); + if (acl->remark) + XFREE(MTYPE_TMP, acl->remark); + + return NB_OK; +} + + +/* + * XPath: /frr-filter:lib/access-list/entry + */ +static int lib_access_list_entry_create(struct nb_cb_create_args *args) +{ + struct access_list *acl; + struct filter *f; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + f = filter_new(); + f->seq = yang_dnode_get_uint32(args->dnode, "./sequence"); + + acl = nb_running_get_entry(args->dnode, NULL, true); + f->acl = acl; + access_list_filter_add(acl, f); + nb_running_set_entry(args->dnode, f); + + return NB_OK; +} + +static int lib_access_list_entry_destroy(struct nb_cb_destroy_args *args) +{ + struct access_list *acl; + struct filter *f; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + f = nb_running_unset_entry(args->dnode); + acl = f->acl; + access_list_filter_delete(acl, f); + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/access-list/entry/action + */ +static int +lib_access_list_entry_action_modify(struct nb_cb_modify_args *args) +{ + const char *filter_type; + struct filter *f; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + f = nb_running_get_entry(args->dnode, NULL, true); + filter_type = yang_dnode_get_string(args->dnode, NULL); + if (strcmp(filter_type, "permit") == 0) + f->type = FILTER_PERMIT; + else + f->type = FILTER_DENY; + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED); + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/access-list/entry/ipv4-prefix + */ +static int +lib_access_list_entry_ipv4_prefix_modify(struct nb_cb_modify_args *args) +{ + struct filter_zebra *fz; + struct filter *f; + + /* Don't allow duplicated values. */ + if (args->event == NB_EV_VALIDATE) { + if (acl_zebra_is_dup( + args->dnode, + yang_dnode_get_enum(args->dnode, "../../type"))) { + snprintfrr(args->errmsg, args->errmsg_len, + "duplicated access list value: %s", + yang_dnode_get_string(args->dnode, NULL)); + return NB_ERR_VALIDATION; + } + return NB_OK; + } + + if (args->event != NB_EV_APPLY) + return NB_OK; + + f = nb_running_get_entry(args->dnode, NULL, true); + f->cisco = 0; + fz = &f->u.zfilter; + yang_dnode_get_prefix(&fz->prefix, args->dnode, NULL); + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED); + + return NB_OK; +} + +static int +lib_access_list_entry_ipv4_prefix_destroy(struct nb_cb_destroy_args *args) +{ + struct filter_zebra *fz; + struct filter *f; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + f = nb_running_get_entry(args->dnode, NULL, true); + fz = &f->u.zfilter; + memset(&fz->prefix, 0, sizeof(fz->prefix)); + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_DELETED); + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/access-list/entry/ipv4-exact-match + */ +static int +lib_access_list_entry_ipv4_exact_match_modify(struct nb_cb_modify_args *args) +{ + struct filter_zebra *fz; + struct filter *f; + + /* Don't allow duplicated values. */ + if (args->event == NB_EV_VALIDATE) { + if (acl_zebra_is_dup( + args->dnode, + yang_dnode_get_enum(args->dnode, "../../type"))) { + snprintfrr(args->errmsg, args->errmsg_len, + "duplicated access list value: %s", + yang_dnode_get_string(args->dnode, NULL)); + return NB_ERR_VALIDATION; + } + return NB_OK; + } + + if (args->event != NB_EV_APPLY) + return NB_OK; + + f = nb_running_get_entry(args->dnode, NULL, true); + fz = &f->u.zfilter; + fz->exact = yang_dnode_get_bool(args->dnode, NULL); + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED); + + return NB_OK; +} + +static int +lib_access_list_entry_ipv4_exact_match_destroy(struct nb_cb_destroy_args *args) +{ + struct filter_zebra *fz; + struct filter *f; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + f = nb_running_get_entry(args->dnode, NULL, true); + fz = &f->u.zfilter; + fz->exact = 0; + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_DELETED); + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/access-list/entry/host + */ +static int +lib_access_list_entry_host_modify(struct nb_cb_modify_args *args) +{ + struct filter_cisco *fc; + struct filter *f; + + /* Don't allow duplicated values. */ + if (args->event == NB_EV_VALIDATE) { + if (acl_cisco_is_dup(args->dnode)) { + snprintfrr(args->errmsg, args->errmsg_len, + "duplicated access list value: %s", + yang_dnode_get_string(args->dnode, NULL)); + return NB_ERR_VALIDATION; + } + return NB_OK; + } + + if (args->event != NB_EV_APPLY) + return NB_OK; + + f = nb_running_get_entry(args->dnode, NULL, true); + f->cisco = 1; + fc = &f->u.cfilter; + yang_dnode_get_ipv4(&fc->addr, args->dnode, NULL); + fc->addr_mask.s_addr = CISCO_BIN_HOST_WILDCARD_MASK; + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED); + + return NB_OK; +} + +static int +lib_access_list_entry_host_destroy(struct nb_cb_destroy_args *args) +{ + struct filter_cisco *fc; + struct filter *f; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + f = nb_running_get_entry(args->dnode, NULL, true); + fc = &f->u.cfilter; + cisco_unset_addr_mask(&fc->addr, &fc->addr_mask); + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_DELETED); + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/access-list/entry/network/address + */ +static int +lib_access_list_entry_network_address_modify(struct nb_cb_modify_args *args) +{ + struct filter_cisco *fc; + struct filter *f; + + /* Don't allow duplicated values. */ + if (args->event == NB_EV_VALIDATE) { + if (acl_cisco_is_dup(args->dnode)) { + snprintfrr(args->errmsg, args->errmsg_len, + "duplicated access list value: %s", + yang_dnode_get_string(args->dnode, NULL)); + return NB_ERR_VALIDATION; + } + return NB_OK; + } + + if (args->event != NB_EV_APPLY) + return NB_OK; + + f = nb_running_get_entry(args->dnode, NULL, true); + f->cisco = 1; + fc = &f->u.cfilter; + yang_dnode_get_ipv4(&fc->addr, args->dnode, NULL); + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED); + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/access-list/entry/network/mask + */ +static int +lib_access_list_entry_network_mask_modify(struct nb_cb_modify_args *args) +{ + struct filter_cisco *fc; + struct filter *f; + + /* Don't allow duplicated values. */ + if (args->event == NB_EV_VALIDATE) { + if (acl_cisco_is_dup(args->dnode)) { + snprintfrr(args->errmsg, args->errmsg_len, + "duplicated access list value: %s", + yang_dnode_get_string(args->dnode, NULL)); + return NB_ERR_VALIDATION; + } + return NB_OK; + } + + if (args->event != NB_EV_APPLY) + return NB_OK; + + f = nb_running_get_entry(args->dnode, NULL, true); + f->cisco = 1; + fc = &f->u.cfilter; + yang_dnode_get_ipv4(&fc->addr_mask, args->dnode, NULL); + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED); + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/access-list/entry/source-any + */ +static int +lib_access_list_entry_source_any_create(struct nb_cb_create_args *args) +{ + struct filter_cisco *fc; + struct filter *f; + + /* Don't allow duplicated values. */ + if (args->event == NB_EV_VALIDATE) { + if (acl_cisco_is_dup(args->dnode)) { + snprintfrr(args->errmsg, args->errmsg_len, + "duplicated access list value: %s", + yang_dnode_get_string(args->dnode, NULL)); + return NB_ERR_VALIDATION; + } + return NB_OK; + } + + if (args->event != NB_EV_APPLY) + return NB_OK; + + f = nb_running_get_entry(args->dnode, NULL, true); + f->cisco = 1; + fc = &f->u.cfilter; + fc->addr.s_addr = INADDR_ANY; + fc->addr_mask.s_addr = CISCO_BIN_ANY_WILDCARD_MASK; + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED); + + return NB_OK; +} + +static int +lib_access_list_entry_source_any_destroy(struct nb_cb_destroy_args *args) +{ + struct filter_cisco *fc; + struct filter *f; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + f = nb_running_get_entry(args->dnode, NULL, true); + fc = &f->u.cfilter; + cisco_unset_addr_mask(&fc->addr, &fc->addr_mask); + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_DELETED); + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/access-list/entry/destination-host + */ +static int lib_access_list_entry_destination_host_modify( + struct nb_cb_modify_args *args) +{ + struct filter_cisco *fc; + struct filter *f; + + /* Don't allow duplicated values. */ + if (args->event == NB_EV_VALIDATE) { + if (acl_cisco_is_dup(args->dnode)) { + snprintfrr(args->errmsg, args->errmsg_len, + "duplicated access list value: %s", + yang_dnode_get_string(args->dnode, NULL)); + return NB_ERR_VALIDATION; + } + return NB_OK; + } + + if (args->event != NB_EV_APPLY) + return NB_OK; + + f = nb_running_get_entry(args->dnode, NULL, true); + fc = &f->u.cfilter; + fc->extended = 1; + yang_dnode_get_ipv4(&fc->mask, args->dnode, NULL); + fc->mask_mask.s_addr = CISCO_BIN_HOST_WILDCARD_MASK; + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED); + + return NB_OK; +} + +static int lib_access_list_entry_destination_host_destroy( + struct nb_cb_destroy_args *args) +{ + struct filter_cisco *fc; + struct filter *f; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + f = nb_running_get_entry(args->dnode, NULL, true); + fc = &f->u.cfilter; + fc->extended = 0; + cisco_unset_addr_mask(&fc->mask, &fc->mask_mask); + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_DELETED); + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/access-list/entry/destination-network/address + */ +static int lib_access_list_entry_destination_network_address_modify( + struct nb_cb_modify_args *args) +{ + struct filter_cisco *fc; + struct filter *f; + + /* Don't allow duplicated values. */ + if (args->event == NB_EV_VALIDATE) { + if (acl_cisco_is_dup(args->dnode)) { + snprintfrr(args->errmsg, args->errmsg_len, + "duplicated access list value: %s", + yang_dnode_get_string(args->dnode, NULL)); + return NB_ERR_VALIDATION; + } + return NB_OK; + } + + if (args->event != NB_EV_APPLY) + return NB_OK; + + f = nb_running_get_entry(args->dnode, NULL, true); + fc = &f->u.cfilter; + fc->extended = 1; + yang_dnode_get_ipv4(&fc->mask, args->dnode, NULL); + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED); + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/access-list/entry/destination-network/mask + */ +static int lib_access_list_entry_destination_network_mask_modify( + struct nb_cb_modify_args *args) +{ + struct filter_cisco *fc; + struct filter *f; + + /* Don't allow duplicated values. */ + if (args->event == NB_EV_VALIDATE) { + if (acl_cisco_is_dup(args->dnode)) { + snprintfrr(args->errmsg, args->errmsg_len, + "duplicated access list value: %s", + yang_dnode_get_string(args->dnode, NULL)); + return NB_ERR_VALIDATION; + } + return NB_OK; + } + + if (args->event != NB_EV_APPLY) + return NB_OK; + + f = nb_running_get_entry(args->dnode, NULL, true); + fc = &f->u.cfilter; + fc->extended = 1; + yang_dnode_get_ipv4(&fc->mask_mask, args->dnode, NULL); + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED); + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/access-list/entry/destination-any + */ +static int lib_access_list_entry_destination_any_create( + struct nb_cb_create_args *args) +{ + struct filter_cisco *fc; + struct filter *f; + + /* Don't allow duplicated values. */ + if (args->event == NB_EV_VALIDATE) { + if (acl_cisco_is_dup(args->dnode)) { + snprintfrr(args->errmsg, args->errmsg_len, + "duplicated access list value: %s", + yang_dnode_get_string(args->dnode, NULL)); + return NB_ERR_VALIDATION; + } + return NB_OK; + } + + if (args->event != NB_EV_APPLY) + return NB_OK; + + f = nb_running_get_entry(args->dnode, NULL, true); + fc = &f->u.cfilter; + fc->extended = 1; + fc->mask.s_addr = INADDR_ANY; + fc->mask_mask.s_addr = CISCO_BIN_ANY_WILDCARD_MASK; + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED); + + return NB_OK; +} + +static int lib_access_list_entry_destination_any_destroy( + struct nb_cb_destroy_args *args) +{ + struct filter_cisco *fc; + struct filter *f; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + f = nb_running_get_entry(args->dnode, NULL, true); + fc = &f->u.cfilter; + fc->extended = 0; + cisco_unset_addr_mask(&fc->mask, &fc->mask_mask); + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_DELETED); + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/access-list/entry/any + */ +static int lib_access_list_entry_any_create(struct nb_cb_create_args *args) +{ + struct filter_zebra *fz; + struct filter *f; + int type; + + /* Don't allow duplicated values. */ + if (args->event == NB_EV_VALIDATE) { + if (acl_zebra_is_dup( + args->dnode, + yang_dnode_get_enum(args->dnode, "../../type"))) { + snprintfrr(args->errmsg, args->errmsg_len, + "duplicated access list value: %s", + yang_dnode_get_string(args->dnode, NULL)); + return NB_ERR_VALIDATION; + } + return NB_OK; + } + + if (args->event != NB_EV_APPLY) + return NB_OK; + + f = nb_running_get_entry(args->dnode, NULL, true); + f->cisco = 0; + fz = &f->u.zfilter; + memset(&fz->prefix, 0, sizeof(fz->prefix)); + + type = yang_dnode_get_enum(args->dnode, "../../type"); + switch (type) { + case YALT_IPV4: + fz->prefix.family = AF_INET; + break; + case YALT_IPV6: + fz->prefix.family = AF_INET6; + break; + case YALT_MAC: + fz->prefix.family = AF_ETHERNET; + break; + } + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED); + + return NB_OK; +} + +static int lib_access_list_entry_any_destroy(struct nb_cb_destroy_args *args) +{ + struct filter_zebra *fz; + struct filter *f; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + f = nb_running_get_entry(args->dnode, NULL, true); + fz = &f->u.zfilter; + fz->prefix.family = 0; + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_DELETED); + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/prefix-list + */ +static int lib_prefix_list_create(struct nb_cb_create_args *args) +{ + struct prefix_list *pl = NULL; + const char *name; + int type; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + type = yang_dnode_get_enum(args->dnode, "./type"); + name = yang_dnode_get_string(args->dnode, "./name"); + switch (type) { + case 0: /* ipv4 */ + pl = prefix_list_get(AFI_IP, 0, name); + break; + case 1: /* ipv6 */ + pl = prefix_list_get(AFI_IP6, 0, name); + break; + } + + nb_running_set_entry(args->dnode, pl); + + return NB_OK; +} + +static int lib_prefix_list_destroy(struct nb_cb_destroy_args *args) +{ + struct prefix_list *pl; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + pl = nb_running_unset_entry(args->dnode); + prefix_list_delete(pl); + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/prefix-list/remark + */ +static int lib_prefix_list_remark_modify(struct nb_cb_modify_args *args) +{ + struct prefix_list *pl; + const char *remark; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + pl = nb_running_get_entry(args->dnode, NULL, true); + if (pl->desc) + XFREE(MTYPE_TMP, pl->desc); + + remark = yang_dnode_get_string(args->dnode, NULL); + pl->desc = XSTRDUP(MTYPE_TMP, remark); + + return NB_OK; +} + +static int lib_prefix_list_remark_destroy(struct nb_cb_destroy_args *args) +{ + struct prefix_list *pl; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + pl = nb_running_get_entry(args->dnode, NULL, true); + if (pl->desc) + XFREE(MTYPE_TMP, pl->desc); + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/prefix-list/entry + */ +static int lib_prefix_list_entry_create(struct nb_cb_create_args *args) +{ + struct prefix_list_entry *ple; + struct prefix_list *pl; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + pl = nb_running_get_entry(args->dnode, NULL, true); + ple = prefix_list_entry_new(); + ple->pl = pl; + ple->seq = yang_dnode_get_uint32(args->dnode, "./sequence"); + prefix_list_entry_set_empty(ple); + nb_running_set_entry(args->dnode, ple); + + return NB_OK; +} + +static int lib_prefix_list_entry_destroy(struct nb_cb_destroy_args *args) +{ + struct prefix_list_entry *ple; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ple = nb_running_unset_entry(args->dnode); + if (ple->installed) + prefix_list_entry_delete2(ple); + else + prefix_list_entry_free(ple); + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/prefix-list/entry/action + */ +static int lib_prefix_list_entry_action_modify(struct nb_cb_modify_args *args) +{ + struct prefix_list_entry *ple; + int action_type; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ple = nb_running_get_entry(args->dnode, NULL, true); + + /* Start prefix entry update procedure. */ + prefix_list_entry_update_start(ple); + + action_type = yang_dnode_get_enum(args->dnode, NULL); + if (action_type == YPLA_PERMIT) + ple->type = PREFIX_PERMIT; + else + ple->type = PREFIX_DENY; + + /* Finish prefix entry update procedure. */ + prefix_list_entry_update_finish(ple); + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/prefix-list/entry/ipv4-prefix + */ +static int +lib_prefix_list_entry_ipv4_prefix_modify(struct nb_cb_modify_args *args) +{ + struct prefix_list_entry *ple; + struct prefix p; + + if (args->event == NB_EV_VALIDATE) { + if (plist_is_dup_nb(args->dnode)) { + snprintf(args->errmsg, args->errmsg_len, + "duplicated prefix list value: %s", + yang_dnode_get_string(args->dnode, NULL)); + return NB_ERR_VALIDATION; + } + return NB_OK; + } + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ple = nb_running_get_entry(args->dnode, NULL, true); + + /* Start prefix entry update procedure. */ + prefix_list_entry_update_start(ple); + + yang_dnode_get_prefix(&ple->prefix, args->dnode, NULL); + + /* Apply mask and correct original address if necessary. */ + prefix_copy(&p, &ple->prefix); + apply_mask(&p); + if (!prefix_same(&ple->prefix, &p)) { + zlog_info("%s: bad network %pFX correcting it to %pFX", + __func__, &ple->prefix, &p); + prefix_copy(&ple->prefix, &p); + } + + + /* Finish prefix entry update procedure. */ + prefix_list_entry_update_finish(ple); + + return NB_OK; +} + +static int +lib_prefix_list_entry_ipv4_prefix_destroy(struct nb_cb_destroy_args *args) +{ + struct prefix_list_entry *ple; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ple = nb_running_get_entry(args->dnode, NULL, true); + + /* Start prefix entry update procedure. */ + prefix_list_entry_update_start(ple); + + memset(&ple->prefix, 0, sizeof(ple->prefix)); + + /* Finish prefix entry update procedure. */ + prefix_list_entry_update_finish(ple); + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/prefix-list/entry/ipv4-prefix-length-greater-or-equal + */ +static int lib_prefix_list_entry_ipv4_prefix_length_greater_or_equal_modify( + struct nb_cb_modify_args *args) +{ + struct prefix_list_entry *ple; + + if (args->event == NB_EV_VALIDATE && + prefix_list_length_validate(args) != NB_OK) + return NB_ERR_VALIDATION; + + if (args->event == NB_EV_VALIDATE) { + if (plist_is_dup_nb(args->dnode)) { + snprintf(args->errmsg, args->errmsg_len, + "duplicated prefix list value: %s", + yang_dnode_get_string(args->dnode, NULL)); + return NB_ERR_VALIDATION; + } + return NB_OK; + } + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ple = nb_running_get_entry(args->dnode, NULL, true); + + /* Start prefix entry update procedure. */ + prefix_list_entry_update_start(ple); + + ple->ge = yang_dnode_get_uint8(args->dnode, NULL); + + /* Finish prefix entry update procedure. */ + prefix_list_entry_update_finish(ple); + + return NB_OK; +} + +static int lib_prefix_list_entry_ipv4_prefix_length_greater_or_equal_destroy( + struct nb_cb_destroy_args *args) +{ + struct prefix_list_entry *ple; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ple = nb_running_get_entry(args->dnode, NULL, true); + + /* Start prefix entry update procedure. */ + prefix_list_entry_update_start(ple); + + ple->ge = 0; + + /* Finish prefix entry update procedure. */ + prefix_list_entry_update_finish(ple); + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/prefix-list/entry/ipv4-prefix-length-lesser-or-equal + */ +static int lib_prefix_list_entry_ipv4_prefix_length_lesser_or_equal_modify( + struct nb_cb_modify_args *args) +{ + struct prefix_list_entry *ple; + + if (args->event == NB_EV_VALIDATE && + prefix_list_length_validate(args) != NB_OK) + return NB_ERR_VALIDATION; + + if (args->event == NB_EV_VALIDATE) { + if (plist_is_dup_nb(args->dnode)) { + snprintf(args->errmsg, args->errmsg_len, + "duplicated prefix list value: %s", + yang_dnode_get_string(args->dnode, NULL)); + return NB_ERR_VALIDATION; + } + return NB_OK; + } + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ple = nb_running_get_entry(args->dnode, NULL, true); + + /* Start prefix entry update procedure. */ + prefix_list_entry_update_start(ple); + + ple->le = yang_dnode_get_uint8(args->dnode, NULL); + + /* Finish prefix entry update procedure. */ + prefix_list_entry_update_finish(ple); + + return NB_OK; +} + +static int lib_prefix_list_entry_ipv4_prefix_length_lesser_or_equal_destroy( + struct nb_cb_destroy_args *args) +{ + struct prefix_list_entry *ple; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ple = nb_running_get_entry(args->dnode, NULL, true); + + /* Start prefix entry update procedure. */ + prefix_list_entry_update_start(ple); + + ple->le = 0; + + /* Finish prefix entry update procedure. */ + prefix_list_entry_update_finish(ple); + + return NB_OK; +} + +/* + * XPath: /frr-filter:lib/prefix-list/entry/any + */ +static int lib_prefix_list_entry_any_create(struct nb_cb_create_args *args) +{ + struct prefix_list_entry *ple; + int type; + + if (args->event == NB_EV_VALIDATE) { + if (plist_is_dup_nb(args->dnode)) { + snprintf(args->errmsg, args->errmsg_len, + "duplicated prefix list value: %s", + yang_dnode_get_string(args->dnode, NULL)); + return NB_ERR_VALIDATION; + } + return NB_OK; + } + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ple = nb_running_get_entry(args->dnode, NULL, true); + + /* Start prefix entry update procedure. */ + prefix_list_entry_update_start(ple); + + ple->any = true; + + /* Fill prefix struct from scratch. */ + memset(&ple->prefix, 0, sizeof(ple->prefix)); + + type = yang_dnode_get_enum(args->dnode, "../../type"); + switch (type) { + case YPLT_IPV4: + ple->prefix.family = AF_INET; + ple->ge = 0; + ple->le = IPV4_MAX_BITLEN; + break; + case YPLT_IPV6: + ple->prefix.family = AF_INET6; + ple->ge = 0; + ple->le = IPV6_MAX_BITLEN; + break; + } + + /* Finish prefix entry update procedure. */ + prefix_list_entry_update_finish(ple); + + return NB_OK; +} + +static int lib_prefix_list_entry_any_destroy(struct nb_cb_destroy_args *args) +{ + struct prefix_list_entry *ple; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ple = nb_running_get_entry(args->dnode, NULL, true); + + /* Start prefix entry update procedure. */ + prefix_list_entry_update_start(ple); + + prefix_list_entry_set_empty(ple); + + /* Finish prefix entry update procedure. */ + prefix_list_entry_update_finish(ple); + + return NB_OK; +} + +/* clang-format off */ +const struct frr_yang_module_info frr_filter_info = { + .name = "frr-filter", + .nodes = { + { + .xpath = "/frr-filter:lib/access-list", + .cbs = { + .create = lib_access_list_create, + .destroy = lib_access_list_destroy, + } + }, + { + .xpath = "/frr-filter:lib/access-list/remark", + .cbs = { + .modify = lib_access_list_remark_modify, + .destroy = lib_access_list_remark_destroy, + .cli_show = access_list_remark_show, + } + }, + { + .xpath = "/frr-filter:lib/access-list/entry", + .cbs = { + .create = lib_access_list_entry_create, + .destroy = lib_access_list_entry_destroy, + .cli_show = access_list_show, + } + }, + { + .xpath = "/frr-filter:lib/access-list/entry/action", + .cbs = { + .modify = lib_access_list_entry_action_modify, + } + }, + { + .xpath = "/frr-filter:lib/access-list/entry/ipv4-prefix", + .cbs = { + .modify = lib_access_list_entry_ipv4_prefix_modify, + .destroy = lib_access_list_entry_ipv4_prefix_destroy, + } + }, + { + .xpath = "/frr-filter:lib/access-list/entry/ipv4-exact-match", + .cbs = { + .modify = lib_access_list_entry_ipv4_exact_match_modify, + .destroy = lib_access_list_entry_ipv4_exact_match_destroy, + } + }, + { + .xpath = "/frr-filter:lib/access-list/entry/host", + .cbs = { + .modify = lib_access_list_entry_host_modify, + .destroy = lib_access_list_entry_host_destroy, + } + }, + { + .xpath = "/frr-filter:lib/access-list/entry/network/address", + .cbs = { + .modify = lib_access_list_entry_network_address_modify, + } + }, + { + .xpath = "/frr-filter:lib/access-list/entry/network/mask", + .cbs = { + .modify = lib_access_list_entry_network_mask_modify, + } + }, + { + .xpath = "/frr-filter:lib/access-list/entry/source-any", + .cbs = { + .create = lib_access_list_entry_source_any_create, + .destroy = lib_access_list_entry_source_any_destroy, + } + }, + { + .xpath = "/frr-filter:lib/access-list/entry/destination-host", + .cbs = { + .modify = lib_access_list_entry_destination_host_modify, + .destroy = lib_access_list_entry_destination_host_destroy, + } + }, + { + .xpath = "/frr-filter:lib/access-list/entry/destination-network/address", + .cbs = { + .modify = lib_access_list_entry_destination_network_address_modify, + } + }, + { + .xpath = "/frr-filter:lib/access-list/entry/destination-network/mask", + .cbs = { + .modify = lib_access_list_entry_destination_network_mask_modify, + } + }, + { + .xpath = "/frr-filter:lib/access-list/entry/destination-any", + .cbs = { + .create = lib_access_list_entry_destination_any_create, + .destroy = lib_access_list_entry_destination_any_destroy, + } + }, + { + .xpath = "/frr-filter:lib/access-list/entry/ipv6-prefix", + .cbs = { + .modify = lib_access_list_entry_ipv4_prefix_modify, + .destroy = lib_access_list_entry_ipv4_prefix_destroy, + } + }, + { + .xpath = "/frr-filter:lib/access-list/entry/ipv6-exact-match", + .cbs = { + .modify = lib_access_list_entry_ipv4_exact_match_modify, + .destroy = lib_access_list_entry_ipv4_exact_match_destroy, + } + }, + { + .xpath = "/frr-filter:lib/access-list/entry/mac", + .cbs = { + .modify = lib_access_list_entry_ipv4_prefix_modify, + .destroy = lib_access_list_entry_ipv4_prefix_destroy, + } + }, + { + .xpath = "/frr-filter:lib/access-list/entry/any", + .cbs = { + .create = lib_access_list_entry_any_create, + .destroy = lib_access_list_entry_any_destroy, + } + }, + { + .xpath = "/frr-filter:lib/prefix-list", + .cbs = { + .create = lib_prefix_list_create, + .destroy = lib_prefix_list_destroy, + } + }, + { + .xpath = "/frr-filter:lib/prefix-list/remark", + .cbs = { + .modify = lib_prefix_list_remark_modify, + .destroy = lib_prefix_list_remark_destroy, + .cli_show = prefix_list_remark_show, + } + }, + { + .xpath = "/frr-filter:lib/prefix-list/entry", + .cbs = { + .create = lib_prefix_list_entry_create, + .destroy = lib_prefix_list_entry_destroy, + .cli_show = prefix_list_show, + } + }, + { + .xpath = "/frr-filter:lib/prefix-list/entry/action", + .cbs = { + .modify = lib_prefix_list_entry_action_modify, + } + }, + { + .xpath = "/frr-filter:lib/prefix-list/entry/ipv4-prefix", + .cbs = { + .modify = lib_prefix_list_entry_ipv4_prefix_modify, + .destroy = lib_prefix_list_entry_ipv4_prefix_destroy, + } + }, + { + .xpath = "/frr-filter:lib/prefix-list/entry/ipv4-prefix-length-greater-or-equal", + .cbs = { + .modify = lib_prefix_list_entry_ipv4_prefix_length_greater_or_equal_modify, + .destroy = lib_prefix_list_entry_ipv4_prefix_length_greater_or_equal_destroy, + } + }, + { + .xpath = "/frr-filter:lib/prefix-list/entry/ipv4-prefix-length-lesser-or-equal", + .cbs = { + .modify = lib_prefix_list_entry_ipv4_prefix_length_lesser_or_equal_modify, + .destroy = lib_prefix_list_entry_ipv4_prefix_length_lesser_or_equal_destroy, + } + }, + { + .xpath = "/frr-filter:lib/prefix-list/entry/ipv6-prefix", + .cbs = { + .modify = lib_prefix_list_entry_ipv4_prefix_modify, + .destroy = lib_prefix_list_entry_ipv4_prefix_destroy, + } + }, + { + .xpath = "/frr-filter:lib/prefix-list/entry/ipv6-prefix-length-greater-or-equal", + .cbs = { + .modify = lib_prefix_list_entry_ipv4_prefix_length_greater_or_equal_modify, + .destroy = lib_prefix_list_entry_ipv4_prefix_length_greater_or_equal_destroy, + } + }, + { + .xpath = "/frr-filter:lib/prefix-list/entry/ipv6-prefix-length-lesser-or-equal", + .cbs = { + .modify = lib_prefix_list_entry_ipv4_prefix_length_lesser_or_equal_modify, + .destroy = lib_prefix_list_entry_ipv4_prefix_length_lesser_or_equal_destroy, + } + }, + { + .xpath = "/frr-filter:lib/prefix-list/entry/any", + .cbs = { + .create = lib_prefix_list_entry_any_create, + .destroy = lib_prefix_list_entry_any_destroy, + } + }, + { + .xpath = NULL, + }, + } +}; diff --git a/lib/frr_pthread.c b/lib/frr_pthread.c index 2a18e5cfc6..da9594ed80 100644 --- a/lib/frr_pthread.c +++ b/lib/frr_pthread.c @@ -27,16 +27,20 @@ #include "frr_pthread.h" #include "memory.h" #include "linklist.h" +#include "zlog.h" -DEFINE_MTYPE(LIB, FRR_PTHREAD, "FRR POSIX Thread"); -DEFINE_MTYPE(LIB, PTHREAD_PRIM, "POSIX synchronization primitives"); +DEFINE_MTYPE_STATIC(LIB, FRR_PTHREAD, "FRR POSIX Thread") +DEFINE_MTYPE_STATIC(LIB, PTHREAD_PRIM, "POSIX sync primitives") /* default frr_pthread start/stop routine prototypes */ static void *fpt_run(void *arg); static int fpt_halt(struct frr_pthread *fpt, void **res); +/* misc sigs */ +static void frr_pthread_destroy_nolock(struct frr_pthread *fpt); + /* default frr_pthread attributes */ -struct frr_pthread_attr frr_pthread_attr_default = { +const struct frr_pthread_attr frr_pthread_attr_default = { .start = fpt_run, .stop = fpt_halt, }; @@ -49,24 +53,29 @@ static struct list *frr_pthread_list; void frr_pthread_init(void) { - pthread_mutex_lock(&frr_pthread_list_mtx); - { + frr_with_mutex(&frr_pthread_list_mtx) { frr_pthread_list = list_new(); - frr_pthread_list->del = (void (*)(void *))&frr_pthread_destroy; } - pthread_mutex_unlock(&frr_pthread_list_mtx); } void frr_pthread_finish(void) { - pthread_mutex_lock(&frr_pthread_list_mtx); - { + frr_pthread_stop_all(); + + frr_with_mutex(&frr_pthread_list_mtx) { + struct listnode *n, *nn; + struct frr_pthread *fpt; + + for (ALL_LIST_ELEMENTS(frr_pthread_list, n, nn, fpt)) { + listnode_delete(frr_pthread_list, fpt); + frr_pthread_destroy_nolock(fpt); + } + list_delete(&frr_pthread_list); } - pthread_mutex_unlock(&frr_pthread_list_mtx); } -struct frr_pthread *frr_pthread_new(struct frr_pthread_attr *attr, +struct frr_pthread *frr_pthread_new(const struct frr_pthread_attr *attr, const char *name, const char *os_name) { struct frr_pthread *fpt = NULL; @@ -94,19 +103,16 @@ struct frr_pthread *frr_pthread_new(struct frr_pthread_attr *attr, pthread_mutex_init(fpt->running_cond_mtx, NULL); pthread_cond_init(fpt->running_cond, NULL); - pthread_mutex_lock(&frr_pthread_list_mtx); - { + frr_with_mutex(&frr_pthread_list_mtx) { listnode_add(frr_pthread_list, fpt); } - pthread_mutex_unlock(&frr_pthread_list_mtx); return fpt; } -void frr_pthread_destroy(struct frr_pthread *fpt) +static void frr_pthread_destroy_nolock(struct frr_pthread *fpt) { thread_master_free(fpt->master); - pthread_mutex_destroy(&fpt->mtx); pthread_mutex_destroy(fpt->running_cond_mtx); pthread_cond_destroy(fpt->running_cond); @@ -116,6 +122,15 @@ void frr_pthread_destroy(struct frr_pthread *fpt) XFREE(MTYPE_FRR_PTHREAD, fpt); } +void frr_pthread_destroy(struct frr_pthread *fpt) +{ + frr_with_mutex(&frr_pthread_list_mtx) { + listnode_delete(frr_pthread_list, fpt); + } + + frr_pthread_destroy_nolock(fpt); +} + int frr_pthread_set_name(struct frr_pthread *fpt) { int ret = 0; @@ -133,41 +148,58 @@ int frr_pthread_set_name(struct frr_pthread *fpt) return ret; } +static void *frr_pthread_inner(void *arg) +{ + struct frr_pthread *fpt = arg; + + rcu_thread_start(fpt->rcu_thread); + return fpt->attr.start(fpt); +} + int frr_pthread_run(struct frr_pthread *fpt, const pthread_attr_t *attr) { int ret; + sigset_t oldsigs, blocksigs; - ret = pthread_create(&fpt->thread, attr, fpt->attr.start, fpt); + /* Ensure we never handle signals on a background thread by blocking + * everything here (new thread inherits signal mask) + */ + sigfillset(&blocksigs); + pthread_sigmask(SIG_BLOCK, &blocksigs, &oldsigs); + + fpt->rcu_thread = rcu_thread_prepare(); + ret = pthread_create(&fpt->thread, attr, frr_pthread_inner, fpt); + + /* Restore caller's signals */ + pthread_sigmask(SIG_SETMASK, &oldsigs, NULL); /* * Per pthread_create(3), the contents of fpt->thread are undefined if * pthread_create() did not succeed. Reset this value to zero. */ - if (ret < 0) + if (ret < 0) { + rcu_thread_unprepare(fpt->rcu_thread); memset(&fpt->thread, 0x00, sizeof(fpt->thread)); + } return ret; } void frr_pthread_wait_running(struct frr_pthread *fpt) { - pthread_mutex_lock(fpt->running_cond_mtx); - { + frr_with_mutex(fpt->running_cond_mtx) { while (!fpt->running) pthread_cond_wait(fpt->running_cond, fpt->running_cond_mtx); } - pthread_mutex_unlock(fpt->running_cond_mtx); } void frr_pthread_notify_running(struct frr_pthread *fpt) { - pthread_mutex_lock(fpt->running_cond_mtx); - { + frr_with_mutex(fpt->running_cond_mtx) { fpt->running = true; pthread_cond_signal(fpt->running_cond); } - pthread_mutex_unlock(fpt->running_cond_mtx); } int frr_pthread_stop(struct frr_pthread *fpt, void **result) @@ -179,14 +211,15 @@ int frr_pthread_stop(struct frr_pthread *fpt, void **result) void frr_pthread_stop_all(void) { - pthread_mutex_lock(&frr_pthread_list_mtx); - { + frr_with_mutex(&frr_pthread_list_mtx) { struct listnode *n; struct frr_pthread *fpt; - for (ALL_LIST_ELEMENTS_RO(frr_pthread_list, n, fpt)) - frr_pthread_stop(fpt, NULL); + for (ALL_LIST_ELEMENTS_RO(frr_pthread_list, n, fpt)) { + if (atomic_load_explicit(&fpt->running, + memory_order_relaxed)) + frr_pthread_stop(fpt, NULL); + } } - pthread_mutex_unlock(&frr_pthread_list_mtx); } /* @@ -251,6 +284,8 @@ static void *fpt_run(void *arg) struct frr_pthread *fpt = arg; fpt->master->owner = pthread_self(); + zlog_tls_buffer_init(); + int sleeper[2]; pipe(sleeper); thread_add_read(fpt->master, &fpt_dummy, NULL, sleeper[0], NULL); @@ -272,5 +307,7 @@ static void *fpt_run(void *arg) close(sleeper[1]); close(sleeper[0]); + zlog_tls_buffer_fini(); + return NULL; } diff --git a/lib/frr_pthread.h b/lib/frr_pthread.h index 9bc7b94033..89519abae0 100644 --- a/lib/frr_pthread.h +++ b/lib/frr_pthread.h @@ -23,15 +23,13 @@ #include #include "frratomic.h" #include "memory.h" +#include "frrcu.h" #include "thread.h" #ifdef __cplusplus extern "C" { #endif -DECLARE_MTYPE(FRR_PTHREAD); -DECLARE_MTYPE(PTHREAD_PRIM); - #define OS_THREAD_NAMELEN 16 struct frr_pthread; @@ -53,6 +51,8 @@ struct frr_pthread { /* pthread id */ pthread_t thread; + struct rcu_thread *rcu_thread; + /* thread master for this pthread's thread.c event loop */ struct thread_master *master; @@ -99,7 +99,7 @@ struct frr_pthread { char os_name[OS_THREAD_NAMELEN]; }; -extern struct frr_pthread_attr frr_pthread_attr_default; +extern const struct frr_pthread_attr frr_pthread_attr_default; /* * Initializes this module. @@ -133,7 +133,7 @@ void frr_pthread_finish(void); * @param os_name - 16 characters (including '\0') thread name to set in os, * @return the created frr_pthread upon success, or NULL upon failure */ -struct frr_pthread *frr_pthread_new(struct frr_pthread_attr *attr, +struct frr_pthread *frr_pthread_new(const struct frr_pthread_attr *attr, const char *name, const char *os_name); /* @@ -215,6 +215,54 @@ void frr_pthread_stop_all(void); #define pthread_condattr_setclock(A, B) #endif +/* mutex auto-lock/unlock */ + +/* variant 1: + * (for short blocks, multiple mutexes supported) + * break & return can be used for aborting the block + * + * frr_with_mutex(&mtx, &mtx2) { + * if (error) + * break; + * ... + * } + */ +#define _frr_with_mutex(mutex) \ + *NAMECTR(_mtx_) __attribute__(( \ + unused, cleanup(_frr_mtx_unlock))) = _frr_mtx_lock(mutex), \ + /* end */ + +#define frr_with_mutex(...) \ + for (pthread_mutex_t MACRO_REPEAT(_frr_with_mutex, ##__VA_ARGS__) \ + *_once = NULL; _once == NULL; _once = (void *)1) \ + /* end */ + +/* variant 2: + * (more suitable for long blocks, no extra indentation) + * + * frr_mutex_lock_autounlock(&mtx); + * ... + */ +#define frr_mutex_lock_autounlock(mutex) \ + pthread_mutex_t *NAMECTR(_mtx_) \ + __attribute__((unused, cleanup(_frr_mtx_unlock))) = \ + _frr_mtx_lock(mutex) \ + /* end */ + +static inline pthread_mutex_t *_frr_mtx_lock(pthread_mutex_t *mutex) +{ + pthread_mutex_lock(mutex); + return mutex; +} + +static inline void _frr_mtx_unlock(pthread_mutex_t **mutex) +{ + if (!*mutex) + return; + pthread_mutex_unlock(*mutex); + *mutex = NULL; +} + #ifdef __cplusplus } #endif diff --git a/lib/frr_zmq.c b/lib/frr_zmq.c index 7781beae5e..565936a410 100644 --- a/lib/frr_zmq.c +++ b/lib/frr_zmq.c @@ -177,7 +177,7 @@ int funcname_frrzmq_thread_add_read(struct thread_master *master, else { cb = XCALLOC(MTYPE_ZEROMQ_CB, sizeof(struct frrzmq_cb)); - cb->write.cancelled = 1; + cb->write.cancelled = true; *cbp = cb; } @@ -187,7 +187,7 @@ int funcname_frrzmq_thread_add_read(struct thread_master *master, cb->read.cb_msg = msgfunc; cb->read.cb_part = partfunc; cb->read.cb_error = errfunc; - cb->read.cancelled = 0; + cb->read.cancelled = false; if (events & ZMQ_POLLIN) { if (cb->read.thread) { @@ -285,7 +285,7 @@ int funcname_frrzmq_thread_add_write(struct thread_master *master, else { cb = XCALLOC(MTYPE_ZEROMQ_CB, sizeof(struct frrzmq_cb)); - cb->read.cancelled = 1; + cb->read.cancelled = true; *cbp = cb; } @@ -295,7 +295,7 @@ int funcname_frrzmq_thread_add_write(struct thread_master *master, cb->write.cb_msg = msgfunc; cb->write.cb_part = NULL; cb->write.cb_error = errfunc; - cb->write.cancelled = 0; + cb->write.cancelled = false; if (events & ZMQ_POLLOUT) { if (cb->write.thread) { @@ -316,7 +316,7 @@ void frrzmq_thread_cancel(struct frrzmq_cb **cb, struct cb_core *core) { if (!cb || !*cb) return; - core->cancelled = 1; + core->cancelled = true; if (core->thread) { thread_cancel(core->thread); core->thread = NULL; diff --git a/lib/frratomic.h b/lib/frratomic.h index 1e28253f2b..bafc6445e5 100644 --- a/lib/frratomic.h +++ b/lib/frratomic.h @@ -41,6 +41,7 @@ using std::memory_order_seq_cst; typedef std::atomic atomic_bool; typedef std::atomic atomic_size_t; typedef std::atomic atomic_uint_fast32_t; +typedef std::atomic atomic_uintptr_t; #elif defined(HAVE_STDATOMIC_H) #include @@ -230,6 +231,7 @@ typedef std::atomic atomic_uint_fast32_t; typedef _Atomic bool atomic_bool; typedef _Atomic size_t atomic_size_t; typedef _Atomic uint_fast32_t atomic_uint_fast32_t; +typedef _Atomic uintptr_t atomic_uintptr_t; #endif #endif /* _FRRATOMIC_H */ diff --git a/lib/frrcu.c b/lib/frrcu.c new file mode 100644 index 0000000000..7e6475b648 --- /dev/null +++ b/lib/frrcu.c @@ -0,0 +1,527 @@ +/* + * Copyright (c) 2017-19 David Lamparter, for NetDEF, Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* implementation notes: this is an epoch-based RCU implementation. rcu_seq + * (global variable) counts the current epoch. Threads hold a specific epoch + * in rcu_read_lock(). This is the oldest epoch a thread might be accessing + * data from. + * + * The rcu_seq global is only pushed forward on rcu_read_lock() and + * rcu_read_unlock() calls. This makes things a tad more efficient since + * those are the only places it matters: + * - on rcu_read_lock, we don't want to hold an old epoch pointlessly + * - on rcu_read_unlock, we want to make sure we're not stuck on an old epoch + * when heading into a long idle period where no thread holds RCU + * + * rcu_thread structures themselves are RCU-free'd. + * + * rcu_head structures are the most iffy; normally for an ATOMLIST we would + * need to make sure we use rcu_free or pthread_rwlock to deallocate old items + * to prevent ABA or use-after-free problems. However, our ATOMLIST code + * guarantees that if the list remains non-empty in all cases, we only need + * the "last" pointer to do an "add_tail()", i.e. we can't run into ABA/UAF + * issues - but we do need to keep at least 1 item on the list. + * + * (Search the atomlist code for all uses of "last") + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#ifdef HAVE_PTHREAD_NP_H +#include +#endif +#include +#include +#include + +#include "frrcu.h" +#include "seqlock.h" +#include "atomlist.h" + +DEFINE_MTYPE_STATIC(LIB, RCU_THREAD, "RCU thread") +DEFINE_MTYPE_STATIC(LIB, RCU_NEXT, "RCU sequence barrier") + +DECLARE_ATOMLIST(rcu_heads, struct rcu_head, head) + +PREDECL_ATOMLIST(rcu_threads) +struct rcu_thread { + struct rcu_threads_item head; + + struct rcu_head rcu_head; + + struct seqlock rcu; + + /* only accessed by thread itself, not atomic */ + unsigned depth; +}; +DECLARE_ATOMLIST(rcu_threads, struct rcu_thread, head) + +static const struct rcu_action rcua_next = { .type = RCUA_NEXT }; +static const struct rcu_action rcua_end = { .type = RCUA_END }; +static const struct rcu_action rcua_close = { .type = RCUA_CLOSE }; + +struct rcu_next { + struct rcu_head head_free; + struct rcu_head head_next; +}; + +#define rcu_free_internal(mtype, ptr, field) \ + do { \ + typeof(ptr) _ptr = (ptr); \ + struct rcu_head *_rcu_head = &_ptr->field; \ + static const struct rcu_action _rcu_action = { \ + .type = RCUA_FREE, \ + .u.free = { \ + .mt = mtype, \ + .offset = offsetof(typeof(*_ptr), field), \ + }, \ + }; \ + _rcu_head->action = &_rcu_action; \ + rcu_heads_add_tail(&rcu_heads, _rcu_head); \ + } while (0) + +/* primary global RCU position */ +static struct seqlock rcu_seq; +/* this is set to rcu_seq whenever something is added on the RCU queue. + * rcu_read_lock() and rcu_read_unlock() will then bump rcu_seq up one step. + */ +static _Atomic seqlock_val_t rcu_dirty; + +static struct rcu_threads_head rcu_threads; +static struct rcu_heads_head rcu_heads; + +/* main thread & RCU sweeper have pre-setup rcu_thread structures. The + * reasons are different: + * + * - rcu_thread_main is there because the main thread isn't started like + * other threads, it's implicitly created when the program is started. So + * rcu_thread_main matches up implicitly. + * + * - rcu_thread_rcu isn't actually put on the rcu_threads list (makes no + * sense really), it only exists so we can call RCU-using functions from + * the RCU thread without special handling in rcu_read_lock/unlock. + */ +static struct rcu_thread rcu_thread_main; +static struct rcu_thread rcu_thread_rcu; + +static pthread_t rcu_pthread; +static pthread_key_t rcu_thread_key; +static bool rcu_active; + +static void rcu_start(void); +static void rcu_bump(void); + +/* + * preinitialization for main thread + */ +static void rcu_thread_end(void *rcu_thread); + +static void rcu_preinit(void) __attribute__((constructor)); +static void rcu_preinit(void) +{ + struct rcu_thread *rt; + + rt = &rcu_thread_main; + rt->depth = 1; + seqlock_init(&rt->rcu); + seqlock_acquire_val(&rt->rcu, SEQLOCK_STARTVAL); + + pthread_key_create(&rcu_thread_key, rcu_thread_end); + pthread_setspecific(rcu_thread_key, rt); + + rcu_threads_add_tail(&rcu_threads, rt); + + /* RCU sweeper's rcu_thread is a dummy, NOT added to rcu_threads */ + rt = &rcu_thread_rcu; + rt->depth = 1; + + seqlock_init(&rcu_seq); + seqlock_acquire_val(&rcu_seq, SEQLOCK_STARTVAL); +} + +static struct rcu_thread *rcu_self(void) +{ + return (struct rcu_thread *)pthread_getspecific(rcu_thread_key); +} + +/* + * thread management (for the non-main thread) + */ +struct rcu_thread *rcu_thread_prepare(void) +{ + struct rcu_thread *rt, *cur; + + rcu_assert_read_locked(); + + if (!rcu_active) + rcu_start(); + + cur = rcu_self(); + assert(cur->depth); + + /* new thread always starts with rcu_read_lock held at depth 1, and + * holding the same epoch as the parent (this makes it possible to + * use RCU for things passed into the thread through its arg) + */ + rt = XCALLOC(MTYPE_RCU_THREAD, sizeof(*rt)); + rt->depth = 1; + + seqlock_init(&rt->rcu); + seqlock_acquire(&rt->rcu, &cur->rcu); + + rcu_threads_add_tail(&rcu_threads, rt); + + return rt; +} + +void rcu_thread_start(struct rcu_thread *rt) +{ + pthread_setspecific(rcu_thread_key, rt); +} + +void rcu_thread_unprepare(struct rcu_thread *rt) +{ + if (rt == &rcu_thread_rcu) + return; + + rt->depth = 1; + seqlock_acquire(&rt->rcu, &rcu_seq); + + rcu_bump(); + if (rt != &rcu_thread_main) + /* this free() happens after seqlock_release() below */ + rcu_free_internal(MTYPE_RCU_THREAD, rt, rcu_head); + + rcu_threads_del(&rcu_threads, rt); + seqlock_release(&rt->rcu); +} + +static void rcu_thread_end(void *rtvoid) +{ + struct rcu_thread *rt = rtvoid; + rcu_thread_unprepare(rt); +} + +/* + * main RCU control aspects + */ + +static void rcu_bump(void) +{ + struct rcu_next *rn; + + rn = XMALLOC(MTYPE_RCU_NEXT, sizeof(*rn)); + + /* note: each RCUA_NEXT item corresponds to exactly one seqno bump. + * This means we don't need to communicate which seqno is which + * RCUA_NEXT, since we really don't care. + */ + + /* + * Important race condition: while rcu_heads_add_tail is executing, + * there is an intermediate point where the rcu_heads "last" pointer + * already points to rn->head_next, but rn->head_next isn't added to + * the list yet. That means any other "add_tail" calls append to this + * item, which isn't fully on the list yet. Freeze this thread at + * that point and look at another thread doing a rcu_bump. It adds + * these two items and then does a seqlock_bump. But the rcu_heads + * list is still "interrupted" and there's no RCUA_NEXT on the list + * yet (from either the frozen thread or the second thread). So + * rcu_main() might actually hit the end of the list at the + * "interrupt". + * + * This situation is prevented by requiring that rcu_read_lock is held + * for any calls to rcu_bump, since if we're holding the current RCU + * epoch, that means rcu_main can't be chewing on rcu_heads and hit + * that interruption point. Only by the time the thread has continued + * to rcu_read_unlock() - and therefore completed the add_tail - the + * RCU sweeper gobbles up the epoch and can be sure to find at least + * the RCUA_NEXT and RCUA_FREE items on rcu_heads. + */ + rn->head_next.action = &rcua_next; + rcu_heads_add_tail(&rcu_heads, &rn->head_next); + + /* free rn that we allocated above. + * + * This is INTENTIONALLY not built into the RCUA_NEXT action. This + * ensures that after the action above is popped off the queue, there + * is still at least 1 item on the RCU queue. This means we never + * delete the last item, which is extremely important since it keeps + * the atomlist ->last pointer alive and well. + * + * If we were to "run dry" on the RCU queue, add_tail may run into the + * "last item is being deleted - start over" case, and then we may end + * up accessing old RCU queue items that are already free'd. + */ + rcu_free_internal(MTYPE_RCU_NEXT, rn, head_free); + + /* Only allow the RCU sweeper to run after these 2 items are queued. + * + * If another thread enqueues some RCU action in the intermediate + * window here, nothing bad happens - the queued action is associated + * with a larger seq# than strictly necessary. Thus, it might get + * executed a bit later, but that's not a problem. + * + * If another thread acquires the read lock in this window, it holds + * the previous epoch, but its RCU queue actions will be in the next + * epoch. This isn't a problem either, just a tad inefficient. + */ + seqlock_bump(&rcu_seq); +} + +static void rcu_bump_maybe(void) +{ + seqlock_val_t dirty; + + dirty = atomic_load_explicit(&rcu_dirty, memory_order_relaxed); + /* no problem if we race here and multiple threads bump rcu_seq; + * bumping too much causes no issues while not bumping enough will + * result in delayed cleanup + */ + if (dirty == seqlock_cur(&rcu_seq)) + rcu_bump(); +} + +void rcu_read_lock(void) +{ + struct rcu_thread *rt = rcu_self(); + + assert(rt); + if (rt->depth++ > 0) + return; + + seqlock_acquire(&rt->rcu, &rcu_seq); + /* need to hold RCU for bump ... */ + rcu_bump_maybe(); + /* ... but no point in holding the old epoch if we just bumped */ + seqlock_acquire(&rt->rcu, &rcu_seq); +} + +void rcu_read_unlock(void) +{ + struct rcu_thread *rt = rcu_self(); + + assert(rt && rt->depth); + if (--rt->depth > 0) + return; + rcu_bump_maybe(); + seqlock_release(&rt->rcu); +} + +void rcu_assert_read_locked(void) +{ + struct rcu_thread *rt = rcu_self(); + assert(rt && rt->depth && seqlock_held(&rt->rcu)); +} + +void rcu_assert_read_unlocked(void) +{ + struct rcu_thread *rt = rcu_self(); + assert(rt && !rt->depth && !seqlock_held(&rt->rcu)); +} + +/* + * RCU resource-release thread + */ + +static void *rcu_main(void *arg); + +static void rcu_start(void) +{ + /* ensure we never handle signals on the RCU thread by blocking + * everything here (new thread inherits signal mask) + */ + sigset_t oldsigs, blocksigs; + + sigfillset(&blocksigs); + pthread_sigmask(SIG_BLOCK, &blocksigs, &oldsigs); + + rcu_active = true; + + assert(!pthread_create(&rcu_pthread, NULL, rcu_main, NULL)); + + pthread_sigmask(SIG_SETMASK, &oldsigs, NULL); + +#ifdef HAVE_PTHREAD_SETNAME_NP +# ifdef GNU_LINUX + pthread_setname_np(rcu_pthread, "RCU sweeper"); +# elif defined(__NetBSD__) + pthread_setname_np(rcu_pthread, "RCU sweeper", NULL); +# endif +#elif defined(HAVE_PTHREAD_SET_NAME_NP) + pthread_set_name_np(rcu_pthread, "RCU sweeper"); +#endif +} + +static void rcu_do(struct rcu_head *rh) +{ + struct rcu_head_close *rhc; + void *p; + + switch (rh->action->type) { + case RCUA_FREE: + p = (char *)rh - rh->action->u.free.offset; + if (rh->action->u.free.mt) + qfree(rh->action->u.free.mt, p); + else + free(p); + break; + case RCUA_CLOSE: + rhc = container_of(rh, struct rcu_head_close, + rcu_head); + close(rhc->fd); + break; + case RCUA_CALL: + p = (char *)rh - rh->action->u.call.offset; + rh->action->u.call.fptr(p); + break; + + case RCUA_INVALID: + case RCUA_NEXT: + case RCUA_END: + default: + assert(0); + } +} + +static void rcu_watchdog(struct rcu_thread *rt) +{ +#if 0 + /* future work: print a backtrace for the thread that's holding up + * RCU. The only (good) way of doing that is to send a signal to the + * other thread, save away the backtrace in the signal handler, and + * block here until the signal is done processing. + * + * Just haven't implemented that yet. + */ + fprintf(stderr, "RCU watchdog %p\n", rt); +#endif +} + +static void *rcu_main(void *arg) +{ + struct rcu_thread *rt; + struct rcu_head *rh = NULL; + bool end = false; + struct timespec maxwait; + + seqlock_val_t rcuval = SEQLOCK_STARTVAL; + + pthread_setspecific(rcu_thread_key, &rcu_thread_rcu); + + while (!end) { + seqlock_wait(&rcu_seq, rcuval); + + /* RCU watchdog timeout, TODO: configurable value */ + clock_gettime(CLOCK_MONOTONIC, &maxwait); + maxwait.tv_nsec += 100 * 1000 * 1000; + if (maxwait.tv_nsec >= 1000000000) { + maxwait.tv_sec++; + maxwait.tv_nsec -= 1000000000; + } + + frr_each (rcu_threads, &rcu_threads, rt) + if (!seqlock_timedwait(&rt->rcu, rcuval, &maxwait)) { + rcu_watchdog(rt); + seqlock_wait(&rt->rcu, rcuval); + } + + while ((rh = rcu_heads_pop(&rcu_heads))) { + if (rh->action->type == RCUA_NEXT) + break; + else if (rh->action->type == RCUA_END) + end = true; + else + rcu_do(rh); + } + + rcuval += SEQLOCK_INCR; + } + + /* rcu_shutdown can only be called singlethreaded, and it does a + * pthread_join, so it should be impossible that anything ended up + * on the queue after RCUA_END + */ +#if 1 + assert(!rcu_heads_first(&rcu_heads)); +#else + while ((rh = rcu_heads_pop(&rcu_heads))) + if (rh->action->type >= RCUA_FREE) + rcu_do(rh); +#endif + return NULL; +} + +void rcu_shutdown(void) +{ + static struct rcu_head rcu_head_end; + struct rcu_thread *rt = rcu_self(); + void *retval; + + if (!rcu_active) + return; + + rcu_assert_read_locked(); + assert(rcu_threads_count(&rcu_threads) == 1); + + rcu_enqueue(&rcu_head_end, &rcua_end); + + rt->depth = 0; + seqlock_release(&rt->rcu); + seqlock_release(&rcu_seq); + rcu_active = false; + + /* clearing rcu_active is before pthread_join in case we hang in + * pthread_join & get a SIGTERM or something - in that case, just + * ignore the maybe-still-running RCU thread + */ + if (pthread_join(rcu_pthread, &retval) == 0) { + seqlock_acquire_val(&rcu_seq, SEQLOCK_STARTVAL); + seqlock_acquire_val(&rt->rcu, SEQLOCK_STARTVAL); + rt->depth = 1; + } +} + +/* + * RCU'd free functions + */ + +void rcu_enqueue(struct rcu_head *rh, const struct rcu_action *action) +{ + /* refer to rcu_bump() for why we need to hold RCU when adding items + * to rcu_heads + */ + rcu_assert_read_locked(); + + rh->action = action; + + if (!rcu_active) { + rcu_do(rh); + return; + } + rcu_heads_add_tail(&rcu_heads, rh); + atomic_store_explicit(&rcu_dirty, seqlock_cur(&rcu_seq), + memory_order_relaxed); +} + +void rcu_close(struct rcu_head_close *rhc, int fd) +{ + rhc->fd = fd; + rcu_enqueue(&rhc->rcu_head, &rcua_close); +} diff --git a/lib/frrcu.h b/lib/frrcu.h new file mode 100644 index 0000000000..47751ae7df --- /dev/null +++ b/lib/frrcu.h @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2017-19 David Lamparter, for NetDEF, Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _FRRCU_H +#define _FRRCU_H + +#include "memory.h" +#include "atomlist.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* quick RCU primer: + * There's a global sequence counter. Whenever a thread does a + * rcu_read_lock(), it is marked as holding the current sequence counter. + * When something is cleaned with RCU, the global sequence counter is + * increased and the item is queued for cleanup - *after* all threads are + * at a more recent sequence counter (or no sequence counter / unheld). + * + * So, by delaying resource cleanup, RCU ensures that things don't go away + * while another thread may hold a (stale) reference. + * + * Note that even if a thread is in rcu_read_lock(), it is invalid for that + * thread to access bits after rcu_free() & co on them. This is a design + * choice to allow no-op'ing out the entire RCU mechanism if we're running + * singlethreaded. (Also allows some optimization on the counter bumping.) + * + * differences from Linux Kernel RCU: + * - there's no rcu_synchronize(), if you really need to defer something + * use rcu_call() (and double check it's really necessary) + * - rcu_dereference() and rcu_assign_pointer() don't exist, use atomic_* + * instead (ATOM* list structures do the right thing) + */ + +/* opaque */ +struct rcu_thread; + +/* called before new thread creation, sets up rcu thread info for new thread + * before it actually exits. This ensures possible RCU references are held + * for thread startup. + * + * return value must be passed into the new thread's call to rcu_thread_start() + */ +extern struct rcu_thread *rcu_thread_prepare(void); + +/* cleanup in case pthread_create() fails */ +extern void rcu_thread_unprepare(struct rcu_thread *rcu_thread); + +/* called early in the new thread, with the return value from the above. + * NB: new thread is initially in RCU-held state! (at depth 1) + * + * TBD: maybe inherit RCU state from rcu_thread_prepare()? + */ +extern void rcu_thread_start(struct rcu_thread *rcu_thread); + +/* thread exit is handled through pthread_key_create's destructor function */ + +/* global RCU shutdown - must be called with only 1 active thread left. waits + * until remaining RCU actions are done & RCU thread has exited. + * + * This is mostly here to get a clean exit without memleaks. + */ +extern void rcu_shutdown(void); + +/* enter / exit RCU-held state. counter-based, so can be called nested. */ +extern void rcu_read_lock(void); +extern void rcu_read_unlock(void); + +/* for debugging / safety checks */ +extern void rcu_assert_read_locked(void); +extern void rcu_assert_read_unlocked(void); + +enum rcu_action_type { + RCUA_INVALID = 0, + /* used internally by the RCU code, shouldn't ever show up outside */ + RCUA_NEXT, + RCUA_END, + /* normal RCU actions, for outside use */ + RCUA_FREE, + RCUA_CLOSE, + RCUA_CALL, +}; + +/* since rcu_head is intended to be embedded into structs which may exist + * with lots of copies, rcu_head is shrunk down to its absolute minimum - + * the atomlist pointer + a pointer to this action struct. + */ +struct rcu_action { + enum rcu_action_type type; + + union { + struct { + struct memtype *mt; + ptrdiff_t offset; + } free; + + struct { + void (*fptr)(void *arg); + ptrdiff_t offset; + } call; + } u; +}; + +/* RCU cleanup function queue item */ +PREDECL_ATOMLIST(rcu_heads) +struct rcu_head { + struct rcu_heads_item head; + const struct rcu_action *action; +}; + +/* special RCU head for delayed fd-close */ +struct rcu_head_close { + struct rcu_head rcu_head; + int fd; +}; + +/* enqueue RCU action - use the macros below to get the rcu_action set up */ +extern void rcu_enqueue(struct rcu_head *head, const struct rcu_action *action); + +/* RCU free() and file close() operations. + * + * freed memory / closed fds become _immediately_ unavailable to the calling + * thread, but will remain available for other threads until they have passed + * into RCU-released state. + */ + +/* may be called with NULL mt to do non-MTYPE free() */ +#define rcu_free(mtype, ptr, field) \ + do { \ + typeof(ptr) _ptr = (ptr); \ + if (!_ptr) \ + break; \ + struct rcu_head *_rcu_head = &_ptr->field; \ + static const struct rcu_action _rcu_action = { \ + .type = RCUA_FREE, \ + .u.free = { \ + .mt = mtype, \ + .offset = offsetof(typeof(*_ptr), field), \ + }, \ + }; \ + rcu_enqueue(_rcu_head, &_rcu_action); \ + } while (0) + +/* use this sparingly, it runs on (and blocks) the RCU thread */ +#define rcu_call(func, ptr, field) \ + do { \ + typeof(ptr) _ptr = (ptr); \ + void (*fptype)(typeof(ptr)); \ + struct rcu_head *_rcu_head = &_ptr->field; \ + static const struct rcu_action _rcu_action = { \ + .type = RCUA_CALL, \ + .u.call = { \ + .fptr = (void *)func, \ + .offset = offsetof(typeof(*_ptr), field), \ + }, \ + }; \ + (void)(_fptype = func); \ + rcu_enqueue(_rcu_head, &_rcu_action); \ + } while (0) + +extern void rcu_close(struct rcu_head_close *head, int fd); + +#ifdef __cplusplus +} +#endif + +#endif /* _FRRCU_H */ diff --git a/lib/lua.c b/lib/frrlua.c similarity index 97% rename from lib/lua.c rename to lib/frrlua.c index 3d701a9364..9f9cf8c1f6 100644 --- a/lib/lua.c +++ b/lib/frrlua.c @@ -5,7 +5,7 @@ * Copyright (C) 2016 Cumulus Networks, Inc. * Donald Sharp * - * This file is part of FreeRangeRouting (FRR). + * This file is part of FRRouting (FRR). * * FRR is free software; you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software @@ -20,13 +20,12 @@ * with FRR; see the file COPYING. If not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#include #include #if defined(HAVE_LUA) #include "prefix.h" -#include "lua.h" +#include "frrlua.h" #include "log.h" static int lua_zlog_debug(lua_State *L) diff --git a/lib/lua.h b/lib/frrlua.h similarity index 95% rename from lib/lua.h rename to lib/frrlua.h index a864ab30e0..40c7a67b89 100644 --- a/lib/lua.h +++ b/lib/frrlua.h @@ -5,7 +5,7 @@ * Copyright (C) 2016 Cumulus Networks, Inc. * Donald Sharp * - * This file is part of FreeRangeRouting (FRR). + * This file is part of FRRouting (FRR). * * FRR is free software; you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software @@ -25,9 +25,9 @@ #if defined(HAVE_LUA) -#include -#include -#include +#include "lua.h" +#include "lualib.h" +#include "lauxlib.h" #ifdef __cplusplus extern "C" { diff --git a/lib/frrstr.c b/lib/frrstr.c index fd337073f8..7ef5fffd12 100644 --- a/lib/frrstr.c +++ b/lib/frrstr.c @@ -25,7 +25,11 @@ #include #include #include +#ifdef HAVE_LIBPCREPOSIX +#include +#else #include +#endif /* HAVE_LIBPCREPOSIX */ #include "frrstr.h" #include "memory.h" @@ -152,7 +156,33 @@ void frrstr_strvec_free(vector v) vector_free(v); } -bool begins_with(const char *str, const char *prefix) +char *frrstr_replace(const char *str, const char *find, const char *replace) +{ + char *ch; + char *nustr = XSTRDUP(MTYPE_TMP, str); + + size_t findlen = strlen(find); + size_t repllen = strlen(replace); + + while ((ch = strstr(nustr, find))) { + if (repllen > findlen) { + size_t nusz = strlen(nustr) + repllen - findlen + 1; + nustr = XREALLOC(MTYPE_TMP, nustr, nusz); + ch = strstr(nustr, find); + } + + size_t nustrlen = strlen(nustr); + size_t taillen = (nustr + nustrlen) - (ch + findlen); + + memmove(ch + findlen + (repllen - findlen), ch + findlen, + taillen + 1); + memcpy(ch, replace, repllen); + } + + return nustr; +} + +bool frrstr_startswith(const char *str, const char *prefix) { if (!str || !prefix) return false; @@ -166,10 +196,24 @@ bool begins_with(const char *str, const char *prefix) return strncmp(str, prefix, lenprefix) == 0; } +bool frrstr_endswith(const char *str, const char *suffix) +{ + if (!str || !suffix) + return false; + + size_t lenstr = strlen(str); + size_t lensuffix = strlen(suffix); + + if (lensuffix > lenstr) + return false; + + return strncmp(&str[lenstr - lensuffix], suffix, lensuffix) == 0; +} + int all_digit(const char *str) { for (; *str != '\0'; str++) - if (!isdigit((int)*str)) + if (!isdigit((unsigned char)*str)) return 0; return 1; } diff --git a/lib/frrstr.h b/lib/frrstr.h index 8b591849a3..441d7b8670 100644 --- a/lib/frrstr.h +++ b/lib/frrstr.h @@ -22,7 +22,12 @@ #define _FRRSTR_H_ #include +#include +#ifdef HAVE_LIBPCREPOSIX +#include +#else #include +#endif /* HAVE_LIBPCREPOSIX */ #include #include "vector.h" @@ -87,6 +92,29 @@ void frrstr_filter_vec(vector v, regex_t *filter); */ void frrstr_strvec_free(vector v); +/* + * Given a string, replaces all occurrences of a substring with a different + * string. The result is a new string. The original string is not modified. + * + * If 'replace' is longer than 'find', this function performs N+1 allocations, + * where N is the number of times 'find' occurs in 'str'. If 'replace' is equal + * in length or shorter than 'find', only 1 allocation is performed. + * + * str + * String to perform replacement on. + * + * find + * Substring to replace. + * + * replace + * String to replace 'find' with. + * + * Returns: + * A new string, allocated with MTYPE_TMP, that is the result of performing + * the replacement on 'str'. This must be freed by the caller. + */ +char *frrstr_replace(const char *str, const char *find, const char *replace); + /* * Prefix match for string. * @@ -97,9 +125,23 @@ void frrstr_strvec_free(vector v); * prefix to look for * * Returns: - * true str starts with prefix, false otherwise + * true if str starts with prefix, false otherwise + */ +bool frrstr_startswith(const char *str, const char *prefix); + +/* + * Suffix match for string. + * + * str + * string to check for suffix match + * + * suffix + * suffix to look for + * + * Returns: + * true if str ends with suffix, false otherwise */ -bool begins_with(const char *str, const char *prefix); +bool frrstr_endswith(const char *str, const char *suffix); /* * Check the string only contains digit characters. diff --git a/lib/getopt.h b/lib/getopt.h index 138870d199..63e12e947e 100644 --- a/lib/getopt.h +++ b/lib/getopt.h @@ -111,7 +111,7 @@ struct option { #if defined(__STDC__) && __STDC__ -#if REALLY_NEED_PLAIN_GETOPT +#ifdef REALLY_NEED_PLAIN_GETOPT /* * getopt is defined in POSIX.2. Assume that if the system defines diff --git a/lib/gitversion.pl b/lib/gitversion.pl index 2718046d0b..dd25c8976a 100644 --- a/lib/gitversion.pl +++ b/lib/gitversion.pl @@ -6,7 +6,7 @@ my $gitdesc = `git describe --always --first-parent --tags --dirty --match 'frr-*' || echo -- \"0-gUNKNOWN\"`; chomp $gitdesc; -my $gitsuffix = ($gitdesc =~ /([0-9a-fA-F]{7}(-dirty)?)$/) ? "-g$1" : "-gUNKNOWN"; +my $gitsuffix = ($gitdesc =~ /-g([0-9a-fA-F]+(-dirty)?)$/) ? "-g$1" : "-gUNKNOWN"; printf STDERR "git suffix: %s\n", $gitsuffix; printf "#define GIT_SUFFIX \"%s\"\n", $gitsuffix; diff --git a/lib/grammar_sandbox.c b/lib/grammar_sandbox.c index c6fd3c04ad..a40b815caa 100644 --- a/lib/grammar_sandbox.c +++ b/lib/grammar_sandbox.c @@ -28,7 +28,6 @@ #endif #include "command.h" -#include "memory_vty.h" #include "graph.h" #include "linklist.h" #include "command_match.h" @@ -39,9 +38,9 @@ DEFINE_MTYPE_STATIC(LIB, CMD_TOKENS, "Command desc") /** headers **/ void grammar_sandbox_init(void); -void pretty_print_graph(struct vty *vty, struct graph_node *, int, int, - struct graph_node **, size_t); -void init_cmdgraph(struct vty *, struct graph **); +static void pretty_print_graph(struct vty *vty, struct graph_node *, int, int, + struct graph_node **, size_t); +static void init_cmdgraph(struct vty *, struct graph **); /** shim interface commands **/ static struct graph *nodegraph = NULL, *nodegraph_free = NULL; @@ -400,7 +399,7 @@ DEFUN (grammar_findambig, if (!nodegraph) continue; vty_out(vty, "scanning node %d (%s)\n", scannode - 1, - node_names[scannode - 1]); + cnode->name); } commands = cmd_graph_permutations(nodegraph); @@ -492,8 +491,9 @@ void grammar_sandbox_init(void) * @param start the node to take as the root * @param level indent level for recursive calls, always pass 0 */ -void pretty_print_graph(struct vty *vty, struct graph_node *start, int level, - int desc, struct graph_node **stack, size_t stackpos) +static void pretty_print_graph(struct vty *vty, struct graph_node *start, + int level, int desc, struct graph_node **stack, + size_t stackpos) { // print this node char tokennum[32]; @@ -551,7 +551,7 @@ void pretty_print_graph(struct vty *vty, struct graph_node *start, int level, } /** stuff that should go in command.c + command.h */ -void init_cmdgraph(struct vty *vty, struct graph **graph) +static void init_cmdgraph(struct vty *vty, struct graph **graph) { // initialize graph, add start noe *graph = graph_new(); diff --git a/lib/grammar_sandbox_main.c b/lib/grammar_sandbox_main.c index c9c942f9bf..2066e4c96d 100644 --- a/lib/grammar_sandbox_main.c +++ b/lib/grammar_sandbox_main.c @@ -7,7 +7,7 @@ * Copyright (C) 2016 Cumulus Networks, Inc. * Copyright (C) 2017 David Lamparter for NetDEF, Inc. * - * This file is part of FreeRangeRouting (FRR). + * This file is part of FRRouting (FRR). * * FRR is free software; you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software @@ -28,7 +28,7 @@ #endif #include "command.h" -#include "memory_vty.h" +#include "lib_vty.h" static void vty_do_exit(int isexit) { @@ -45,19 +45,17 @@ int main(int argc, char **argv) master = thread_master_create(NULL); - openzlog("grammar_sandbox", "NONE", 0, LOG_CONS | LOG_NDELAY | LOG_PID, - LOG_DAEMON); - zlog_set_level(ZLOG_DEST_SYSLOG, ZLOG_DISABLED); - zlog_set_level(ZLOG_DEST_STDOUT, LOG_DEBUG); - zlog_set_level(ZLOG_DEST_MONITOR, ZLOG_DISABLED); + zlog_aux_init("NONE: ", LOG_DEBUG); /* Library inits. */ cmd_init(1); host.name = strdup("test"); host.domainname = strdup("testdomainname"); - vty_init(master); - memory_init(); + vty_init(master, true); + lib_cmd_init(); + yang_init(true); + nb_init(master, NULL, 0, false); vty_stdio(vty_do_exit); diff --git a/lib/hash.c b/lib/hash.c index 884c8f22af..85982774ac 100644 --- a/lib/hash.c +++ b/lib/hash.c @@ -28,16 +28,17 @@ #include "vty.h" #include "command.h" #include "libfrr.h" +#include "frr_pthread.h" -DEFINE_MTYPE(LIB, HASH, "Hash") -DEFINE_MTYPE(LIB, HASH_BACKET, "Hash Bucket") +DEFINE_MTYPE_STATIC(LIB, HASH, "Hash") +DEFINE_MTYPE_STATIC(LIB, HASH_BACKET, "Hash Bucket") DEFINE_MTYPE_STATIC(LIB, HASH_INDEX, "Hash Index") static pthread_mutex_t _hashes_mtx = PTHREAD_MUTEX_INITIALIZER; static struct list *_hashes; struct hash *hash_create_size(unsigned int size, - unsigned int (*hash_key)(void *), + unsigned int (*hash_key)(const void *), bool (*hash_cmp)(const void *, const void *), const char *name) { @@ -54,19 +55,17 @@ struct hash *hash_create_size(unsigned int size, hash->name = name ? XSTRDUP(MTYPE_HASH, name) : NULL; hash->stats.empty = hash->size; - pthread_mutex_lock(&_hashes_mtx); - { + frr_with_mutex(&_hashes_mtx) { if (!_hashes) _hashes = list_new(); listnode_add(_hashes, hash); } - pthread_mutex_unlock(&_hashes_mtx); return hash; } -struct hash *hash_create(unsigned int (*hash_key)(void *), +struct hash *hash_create(unsigned int (*hash_key)(const void *), bool (*hash_cmp)(const void *, const void *), const char *name) { @@ -78,9 +77,20 @@ void *hash_alloc_intern(void *arg) return arg; } +/* + * ssq = ssq + (new^2 - old^2) + * = ssq + ((new + old) * (new - old)) + */ #define hash_update_ssq(hz, old, new) \ - atomic_fetch_add_explicit(&hz->stats.ssq, (new + old) * (new - old), \ - memory_order_relaxed); + do { \ + int _adjust = (new + old) * (new - old); \ + if (_adjust < 0) \ + atomic_fetch_sub_explicit(&hz->stats.ssq, -_adjust, \ + memory_order_relaxed); \ + else \ + atomic_fetch_add_explicit(&hz->stats.ssq, _adjust, \ + memory_order_relaxed); \ + } while (0) /* Expand hash if the chain length exceeds the threshold. */ static void hash_expand(struct hash *hash) @@ -311,8 +321,7 @@ struct list *hash_to_list(struct hash *hash) void hash_free(struct hash *hash) { - pthread_mutex_lock(&_hashes_mtx); - { + frr_with_mutex(&_hashes_mtx) { if (_hashes) { listnode_delete(_hashes, hash); if (_hashes->count == 0) { @@ -320,7 +329,6 @@ void hash_free(struct hash *hash) } } } - pthread_mutex_unlock(&_hashes_mtx); XFREE(MTYPE_HASH, hash->name); diff --git a/lib/hash.h b/lib/hash.h index 60c412b8e0..e7ba3187f5 100644 --- a/lib/hash.h +++ b/lib/hash.h @@ -28,9 +28,6 @@ extern "C" { #endif -DECLARE_MTYPE(HASH) -DECLARE_MTYPE(HASH_BACKET) - /* Default hash table size. */ #define HASH_INITIAL_SIZE 256 /* Expansion threshold */ @@ -39,11 +36,6 @@ DECLARE_MTYPE(HASH_BACKET) #define HASHWALK_CONTINUE 0 #define HASHWALK_ABORT -1 -#if CONFDATE > 20200225 -CPP_NOTICE("hash.h: time to remove hash_backet #define") -#endif -#define hash_backet hash_bucket - struct hash_bucket { /* * if this bucket is the head of the linked listed, len denotes the @@ -79,7 +71,7 @@ struct hash { unsigned int max_size; /* Key make function. */ - unsigned int (*hash_key)(void *); + unsigned int (*hash_key)(const void *); /* Data compare function. */ bool (*hash_cmp)(const void *, const void *); @@ -123,7 +115,7 @@ struct hash { * Returns: * a new hash table */ -extern struct hash *hash_create(unsigned int (*hash_key)(void *), +extern struct hash *hash_create(unsigned int (*hash_key)(const void *), bool (*hash_cmp)(const void *, const void *), const char *name); @@ -158,7 +150,7 @@ extern struct hash *hash_create(unsigned int (*hash_key)(void *), * a new hash table */ extern struct hash * -hash_create_size(unsigned int size, unsigned int (*hash_key)(void *), +hash_create_size(unsigned int size, unsigned int (*hash_key)(const void *), bool (*hash_cmp)(const void *, const void *), const char *name); diff --git a/lib/hook.c b/lib/hook.c index 870d158aac..5a8ad00d66 100644 --- a/lib/hook.c +++ b/lib/hook.c @@ -18,16 +18,26 @@ #include "config.h" #endif +#include + #include "memory.h" #include "hook.h" DEFINE_MTYPE_STATIC(LIB, HOOK_ENTRY, "Hook entry") -void _hook_register(struct hook *hook, void *funcptr, void *arg, bool has_arg, - struct frrmod_runtime *module, const char *funcname, - int priority) +void _hook_register(struct hook *hook, struct hookent *stackent, void *funcptr, + void *arg, bool has_arg, struct frrmod_runtime *module, + const char *funcname, int priority) { - struct hookent *he = XCALLOC(MTYPE_HOOK_ENTRY, sizeof(*he)), **pos; + struct hookent *he, **pos; + + if (!stackent->ent_used) + he = stackent; + else { + he = XCALLOC(MTYPE_HOOK_ENTRY, sizeof(*he)); + he->ent_on_heap = true; + } + he->ent_used = true; he->hookfn = funcptr; he->hookarg = arg; he->has_arg = has_arg; @@ -52,7 +62,10 @@ void _hook_unregister(struct hook *hook, void *funcptr, void *arg, bool has_arg) if (he->hookfn == funcptr && he->hookarg == arg && he->has_arg == has_arg) { *prev = he->next; - XFREE(MTYPE_HOOK_ENTRY, he); + if (he->ent_on_heap) + XFREE(MTYPE_HOOK_ENTRY, he); + else + memset(he, 0, sizeof(*he)); break; } } diff --git a/lib/hook.h b/lib/hook.h index f7fb7b8a5c..bef5351e90 100644 --- a/lib/hook.h +++ b/lib/hook.h @@ -114,7 +114,9 @@ struct hookent { struct hookent *next; void *hookfn; /* actually a function pointer */ void *hookarg; - bool has_arg; + bool has_arg : 1; + bool ent_on_heap : 1; + bool ent_used : 1; int priority; struct frrmod_runtime *module; const char *fnname; @@ -133,21 +135,33 @@ struct hook { * always use hook_register(), which uses the static inline helper from * DECLARE_HOOK in order to get type safety */ -extern void _hook_register(struct hook *hook, void *funcptr, void *arg, - bool has_arg, struct frrmod_runtime *module, +extern void _hook_register(struct hook *hook, struct hookent *stackent, + void *funcptr, void *arg, bool has_arg, + struct frrmod_runtime *module, const char *funcname, int priority); + +/* most hook_register calls are not in a loop or similar and can use a + * statically allocated "struct hookent" from the data segment + */ +#define _hook_reg_svar(hook, funcptr, arg, has_arg, module, funcname, prio) \ + do { \ + static struct hookent stack_hookent = {}; \ + _hook_register(hook, &stack_hookent, funcptr, arg, has_arg, \ + module, funcname, prio); \ + } while (0) + #define hook_register(hookname, func) \ - _hook_register(&_hook_##hookname, _hook_typecheck_##hookname(func), \ + _hook_reg_svar(&_hook_##hookname, _hook_typecheck_##hookname(func), \ NULL, false, THIS_MODULE, #func, HOOK_DEFAULT_PRIORITY) #define hook_register_arg(hookname, func, arg) \ - _hook_register(&_hook_##hookname, \ + _hook_reg_svar(&_hook_##hookname, \ _hook_typecheck_arg_##hookname(func), arg, true, \ THIS_MODULE, #func, HOOK_DEFAULT_PRIORITY) #define hook_register_prio(hookname, prio, func) \ - _hook_register(&_hook_##hookname, _hook_typecheck_##hookname(func), \ + _hook_reg_svar(&_hook_##hookname, _hook_typecheck_##hookname(func), \ NULL, false, THIS_MODULE, #func, prio) #define hook_register_arg_prio(hookname, prio, func, arg) \ - _hook_register(&_hook_##hookname, \ + _hook_reg_svar(&_hook_##hookname, \ _hook_typecheck_arg_##hookname(func), arg, true, \ THIS_MODULE, #func, prio) diff --git a/lib/iana_afi.h b/lib/iana_afi.h new file mode 100644 index 0000000000..56e8a24b86 --- /dev/null +++ b/lib/iana_afi.h @@ -0,0 +1,141 @@ +/* + * iana_afi and safi definitions. + * Copyright (C) 2018-2019 Cumulus Networks, Inc. + * Donald Sharp + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef __IANA_AFI_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * The above AFI and SAFI definitions are for internal use. The protocol + * definitions (IANA values) as for example used in BGP protocol packets + * are defined below and these will get mapped to/from the internal values + * in the appropriate places. + * The rationale is that the protocol (IANA) values may be sparse and are + * not optimal for use in data-structure sizing. + * Note: Only useful (i.e., supported) values are defined below. + */ +typedef enum { + IANA_AFI_RESERVED = 0, + IANA_AFI_IPV4 = 1, + IANA_AFI_IPV6 = 2, + IANA_AFI_L2VPN = 25, +} iana_afi_t; + +typedef enum { + IANA_SAFI_RESERVED = 0, + IANA_SAFI_UNICAST = 1, + IANA_SAFI_MULTICAST = 2, + IANA_SAFI_LABELED_UNICAST = 4, + IANA_SAFI_ENCAP = 7, + IANA_SAFI_EVPN = 70, + IANA_SAFI_MPLS_VPN = 128, + IANA_SAFI_FLOWSPEC = 133 +} iana_safi_t; + +static inline afi_t afi_iana2int(iana_afi_t afi) +{ + switch (afi) { + case IANA_AFI_IPV4: + return AFI_IP; + case IANA_AFI_IPV6: + return AFI_IP6; + case IANA_AFI_L2VPN: + return AFI_L2VPN; + default: + return AFI_MAX; + } +} + +static inline iana_afi_t afi_int2iana(afi_t afi) +{ + switch (afi) { + case AFI_IP: + return IANA_AFI_IPV4; + case AFI_IP6: + return IANA_AFI_IPV6; + case AFI_L2VPN: + return IANA_AFI_L2VPN; + default: + return IANA_AFI_RESERVED; + } +} + +static inline const char *iana_afi2str(iana_afi_t afi) +{ + return afi2str(afi_iana2int(afi)); +} + +static inline safi_t safi_iana2int(iana_safi_t safi) +{ + switch (safi) { + case IANA_SAFI_UNICAST: + return SAFI_UNICAST; + case IANA_SAFI_MULTICAST: + return SAFI_MULTICAST; + case IANA_SAFI_MPLS_VPN: + return SAFI_MPLS_VPN; + case IANA_SAFI_ENCAP: + return SAFI_ENCAP; + case IANA_SAFI_EVPN: + return SAFI_EVPN; + case IANA_SAFI_LABELED_UNICAST: + return SAFI_LABELED_UNICAST; + case IANA_SAFI_FLOWSPEC: + return SAFI_FLOWSPEC; + default: + return SAFI_MAX; + } +} + +static inline iana_safi_t safi_int2iana(safi_t safi) +{ + switch (safi) { + case SAFI_UNICAST: + return IANA_SAFI_UNICAST; + case SAFI_MULTICAST: + return IANA_SAFI_MULTICAST; + case SAFI_MPLS_VPN: + return IANA_SAFI_MPLS_VPN; + case SAFI_ENCAP: + return IANA_SAFI_ENCAP; + case SAFI_EVPN: + return IANA_SAFI_EVPN; + case SAFI_LABELED_UNICAST: + return IANA_SAFI_LABELED_UNICAST; + case SAFI_FLOWSPEC: + return IANA_SAFI_FLOWSPEC; + default: + return IANA_SAFI_RESERVED; + } +} + +static inline const char *iana_safi2str(iana_safi_t safi) +{ + return safi2str(safi_iana2int(safi)); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/id_alloc.c b/lib/id_alloc.c index 222ba651b4..95096fa5f0 100644 --- a/lib/id_alloc.c +++ b/lib/id_alloc.c @@ -17,6 +17,10 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include "id_alloc.h" #include "log.h" @@ -104,7 +108,7 @@ static struct id_alloc_page *find_or_create_page(struct id_alloc *alloc, } else if (page != NULL && create) { flog_err( EC_LIB_ID_CONSISTENCY, - "ID Allocator %s attempt to re-create page at %" PRIu32, + "ID Allocator %s attempt to re-create page at %u", alloc->name, id); } @@ -127,8 +131,7 @@ void idalloc_free(struct id_alloc *alloc, uint32_t id) page = find_or_create_page(alloc, id, 0); if (!page) { flog_err(EC_LIB_ID_CONSISTENCY, - "ID Allocator %s cannot free #%" PRIu32 - ". ID Block does not exist.", + "ID Allocator %s cannot free #%u. ID Block does not exist.", alloc->name, id); return; } @@ -138,8 +141,7 @@ void idalloc_free(struct id_alloc *alloc, uint32_t id) if ((page->allocated_mask[word] & (1 << offset)) == 0) { flog_err(EC_LIB_ID_CONSISTENCY, - "ID Allocator %s cannot free #%" PRIu32 - ". ID was not allocated at the time of free.", + "ID Allocator %s cannot free #%u. ID was not allocated at the time of free.", alloc->name, id); return; } @@ -281,8 +283,7 @@ uint32_t idalloc_reserve(struct id_alloc *alloc, uint32_t id) if (page->allocated_mask[word] & (((uint32_t)1) << offset)) { flog_err(EC_LIB_ID_CONSISTENCY, - "ID Allocator %s could not reserve %" PRIu32 - " because it is already allocated.", + "ID Allocator %s could not reserve %u because it is already allocated.", alloc->name, id); return IDALLOC_INVALID; } diff --git a/lib/if.c b/lib/if.c index 86b850c059..efe38196f5 100644 --- a/lib/if.c +++ b/lib/if.c @@ -39,12 +39,14 @@ #include "lib/if_clippy.c" #endif -DEFINE_MTYPE(LIB, IF, "Interface") +DEFINE_MTYPE_STATIC(LIB, IF, "Interface") DEFINE_MTYPE_STATIC(LIB, CONNECTED, "Connected") DEFINE_MTYPE_STATIC(LIB, NBR_CONNECTED, "Neighbor Connected") DEFINE_MTYPE(LIB, CONNECTED_LABEL, "Connected interface label") DEFINE_MTYPE_STATIC(LIB, IF_LINK_PARAMS, "Informational Link Parameters") +static struct interface *if_lookup_by_ifindex(ifindex_t ifindex, + vrf_id_t vrf_id); static int if_cmp_func(const struct interface *, const struct interface *); static int if_cmp_index_func(const struct interface *ifp1, const struct interface *ifp2); @@ -56,6 +58,13 @@ DEFINE_QOBJ_TYPE(interface) DEFINE_HOOK(if_add, (struct interface * ifp), (ifp)) DEFINE_KOOH(if_del, (struct interface * ifp), (ifp)) +static struct interface_master{ + int (*create_hook)(struct interface *ifp); + int (*up_hook)(struct interface *ifp); + int (*down_hook)(struct interface *ifp); + int (*destroy_hook)(struct interface *ifp); +} ifp_master = { 0, }; + /* Compare interface names, returning an integer greater than, equal to, or * less than 0, (following the strcmp convention), according to the * relationship between ifp1 and ifp2. Interface names consist of an @@ -128,24 +137,35 @@ static int if_cmp_func(const struct interface *ifp1, static int if_cmp_index_func(const struct interface *ifp1, const struct interface *ifp2) { - return ifp1->ifindex - ifp2->ifindex; + if (ifp1->ifindex == ifp2->ifindex) + return 0; + else if (ifp1->ifindex > ifp2->ifindex) + return 1; + else + return -1; +} + +static void ifp_connected_free(void *arg) +{ + struct connected *c = arg; + + connected_free(&c); } /* Create new interface structure. */ -struct interface *if_create(const char *name, vrf_id_t vrf_id) +static struct interface *if_new(vrf_id_t vrf_id) { - struct vrf *vrf = vrf_get(vrf_id, NULL); struct interface *ifp; ifp = XCALLOC(MTYPE_IF, sizeof(struct interface)); + ifp->ifindex = IFINDEX_INTERNAL; + ifp->name[0] = '\0'; - assert(name); - strlcpy(ifp->name, name, sizeof(ifp->name)); ifp->vrf_id = vrf_id; - IFNAME_RB_INSERT(vrf, ifp); + ifp->connected = list_new(); - ifp->connected->del = (void (*)(void *))connected_free; + ifp->connected->del = ifp_connected_free; ifp->nbr_connected = list_new(); ifp->nbr_connected->del = (void (*)(void *))nbr_connected_free; @@ -154,6 +174,59 @@ struct interface *if_create(const char *name, vrf_id_t vrf_id) SET_FLAG(ifp->status, ZEBRA_INTERFACE_LINKDETECTION); QOBJ_REG(ifp, interface); + return ifp; +} + +void if_new_via_zapi(struct interface *ifp) +{ + if (ifp_master.create_hook) + (*ifp_master.create_hook)(ifp); +} + +void if_destroy_via_zapi(struct interface *ifp) +{ + if (ifp_master.destroy_hook) + (*ifp_master.destroy_hook)(ifp); + + ifp->oldifindex = ifp->ifindex; + if_set_index(ifp, IFINDEX_INTERNAL); + + if (!ifp->configured) + if_delete(&ifp); +} + +void if_up_via_zapi(struct interface *ifp) +{ + if (ifp_master.up_hook) + (*ifp_master.up_hook)(ifp); +} + +void if_down_via_zapi(struct interface *ifp) +{ + if (ifp_master.down_hook) + (*ifp_master.down_hook)(ifp); +} + +struct interface *if_create_name(const char *name, vrf_id_t vrf_id) +{ + struct interface *ifp; + + ifp = if_new(vrf_id); + + if_set_name(ifp, name); + + hook_call(if_add, ifp); + return ifp; +} + +struct interface *if_create_ifindex(ifindex_t ifindex, vrf_id_t vrf_id) +{ + struct interface *ifp; + + ifp = if_new(vrf_id); + + if_set_index(ifp, ifindex); + hook_call(if_add, ifp); return ifp; } @@ -166,7 +239,9 @@ void if_update_to_new_vrf(struct interface *ifp, vrf_id_t vrf_id) /* remove interface from old master vrf list */ old_vrf = vrf_lookup_by_id(ifp->vrf_id); if (old_vrf) { - IFNAME_RB_REMOVE(old_vrf, ifp); + if (ifp->name[0] != '\0') + IFNAME_RB_REMOVE(old_vrf, ifp); + if (ifp->ifindex != IFINDEX_INTERNAL) IFINDEX_RB_REMOVE(old_vrf, ifp); } @@ -174,7 +249,9 @@ void if_update_to_new_vrf(struct interface *ifp, vrf_id_t vrf_id) ifp->vrf_id = vrf_id; vrf = vrf_get(ifp->vrf_id, NULL); - IFNAME_RB_INSERT(vrf, ifp); + if (ifp->name[0] != '\0') + IFNAME_RB_INSERT(vrf, ifp); + if (ifp->ifindex != IFINDEX_INTERNAL) IFINDEX_RB_INSERT(vrf, ifp); @@ -186,13 +263,21 @@ void if_update_to_new_vrf(struct interface *ifp, vrf_id_t vrf_id) */ if (yang_module_find("frr-interface")) { struct lyd_node *if_dnode; + char oldpath[XPATH_MAXLEN]; + char newpath[XPATH_MAXLEN]; if_dnode = yang_dnode_get( running_config->dnode, "/frr-interface:lib/interface[name='%s'][vrf='%s']/vrf", ifp->name, old_vrf->name); + if (if_dnode) { + yang_dnode_get_path(if_dnode->parent, oldpath, + sizeof(oldpath)); yang_dnode_change_leaf(if_dnode, vrf->name); + yang_dnode_get_path(if_dnode->parent, newpath, + sizeof(newpath)); + nb_running_move_tree(oldpath, newpath); running_config->version++; } } @@ -213,31 +298,34 @@ void if_delete_retain(struct interface *ifp) } /* Delete and free interface structure. */ -void if_delete(struct interface *ifp) +void if_delete(struct interface **ifp) { + struct interface *ptr = *ifp; struct vrf *vrf; - vrf = vrf_lookup_by_id(ifp->vrf_id); + vrf = vrf_lookup_by_id(ptr->vrf_id); assert(vrf); - IFNAME_RB_REMOVE(vrf, ifp); - if (ifp->ifindex != IFINDEX_INTERNAL) - IFINDEX_RB_REMOVE(vrf, ifp); + IFNAME_RB_REMOVE(vrf, ptr); + if (ptr->ifindex != IFINDEX_INTERNAL) + IFINDEX_RB_REMOVE(vrf, ptr); - if_delete_retain(ifp); + if_delete_retain(ptr); - list_delete(&ifp->connected); - list_delete(&ifp->nbr_connected); + list_delete(&ptr->connected); + list_delete(&ptr->nbr_connected); - if_link_params_free(ifp); + if_link_params_free(ptr); - XFREE(MTYPE_TMP, ifp->desc); + XFREE(MTYPE_TMP, ptr->desc); - XFREE(MTYPE_IF, ifp); + XFREE(MTYPE_IF, ptr); + *ifp = NULL; } -/* Interface existance check by index. */ -struct interface *if_lookup_by_index(ifindex_t ifindex, vrf_id_t vrf_id) +/* Used only internally to check within VRF only */ +static struct interface *if_lookup_by_ifindex(ifindex_t ifindex, + vrf_id_t vrf_id) { struct vrf *vrf; struct interface if_tmp; @@ -250,6 +338,19 @@ struct interface *if_lookup_by_index(ifindex_t ifindex, vrf_id_t vrf_id) return RB_FIND(if_index_head, &vrf->ifaces_by_index, &if_tmp); } +/* Interface existance check by index. */ +struct interface *if_lookup_by_index(ifindex_t ifindex, vrf_id_t vrf_id) +{ + switch (vrf_get_backend()) { + case VRF_BACKEND_UNKNOWN: + case VRF_BACKEND_NETNS: + return(if_lookup_by_ifindex(ifindex, vrf_id)); + case VRF_BACKEND_VRF_LITE: + return(if_lookup_by_index_all_vrf(ifindex)); + } + return NULL; +} + const char *ifindex2ifname(ifindex_t ifindex, vrf_id_t vrf_id) { struct interface *ifp; @@ -282,6 +383,17 @@ struct interface *if_lookup_by_name(const char *name, vrf_id_t vrf_id) return RB_FIND(if_name_head, &vrf->ifaces_by_name, &if_tmp); } +struct interface *if_lookup_by_name_vrf(const char *name, struct vrf *vrf) +{ + struct interface if_tmp; + + if (!name || strnlen(name, INTERFACE_NAMSIZ) == INTERFACE_NAMSIZ) + return NULL; + + strlcpy(if_tmp.name, name, sizeof(if_tmp.name)); + return RB_FIND(if_name_head, &vrf->ifaces_by_name, &if_tmp); +} + struct interface *if_lookup_by_name_all_vrf(const char *name) { struct vrf *vrf; @@ -299,8 +411,25 @@ struct interface *if_lookup_by_name_all_vrf(const char *name) return NULL; } -/* Lookup interface by IPv4 address. */ -struct interface *if_lookup_exact_address(void *src, int family, +struct interface *if_lookup_by_index_all_vrf(ifindex_t ifindex) +{ + struct vrf *vrf; + struct interface *ifp; + + if (ifindex == IFINDEX_INTERNAL) + return NULL; + + RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) { + ifp = if_lookup_by_ifindex(ifindex, vrf->vrf_id); + if (ifp) + return ifp; + } + + return NULL; +} + +/* Lookup interface by IP address. */ +struct interface *if_lookup_exact_address(const void *src, int family, vrf_id_t vrf_id) { struct vrf *vrf = vrf_lookup_by_id(vrf_id); @@ -331,8 +460,8 @@ struct interface *if_lookup_exact_address(void *src, int family, return NULL; } -/* Lookup interface by IPv4 address. */ -struct connected *if_lookup_address(void *matchaddr, int family, +/* Lookup interface by IP address. */ +struct connected *if_lookup_address(const void *matchaddr, int family, vrf_id_t vrf_id) { struct vrf *vrf = vrf_lookup_by_id(vrf_id); @@ -369,7 +498,7 @@ struct connected *if_lookup_address(void *matchaddr, int family, } /* Lookup interface by prefix */ -struct interface *if_lookup_prefix(struct prefix *prefix, vrf_id_t vrf_id) +struct interface *if_lookup_prefix(const struct prefix *prefix, vrf_id_t vrf_id) { struct vrf *vrf = vrf_lookup_by_id(vrf_id); struct listnode *cnode; @@ -386,6 +515,34 @@ struct interface *if_lookup_prefix(struct prefix *prefix, vrf_id_t vrf_id) return NULL; } +size_t if_lookup_by_hwaddr(const uint8_t *hw_addr, size_t addrsz, + struct interface ***result, vrf_id_t vrf_id) +{ + struct vrf *vrf = vrf_lookup_by_id(vrf_id); + + struct list *rs = list_new(); + struct interface *ifp; + + FOR_ALL_INTERFACES (vrf, ifp) { + if (ifp->hw_addr_len == (int)addrsz + && !memcmp(hw_addr, ifp->hw_addr, addrsz)) + listnode_add(rs, ifp); + } + + if (rs->count) { + *result = XCALLOC(MTYPE_TMP, + sizeof(struct interface *) * rs->count); + list_to_array(rs, (void **)*result, rs->count); + } + + int count = rs->count; + + list_delete(&rs); + + return count; +} + + /* Get interface by name if given name interface doesn't exist create one. */ struct interface *if_get_by_name(const char *name, vrf_id_t vrf_id) @@ -398,7 +555,7 @@ struct interface *if_get_by_name(const char *name, vrf_id_t vrf_id) ifp = if_lookup_by_name(name, vrf_id); if (ifp) return ifp; - return if_create(name, vrf_id); + return if_create_name(name, vrf_id); case VRF_BACKEND_VRF_LITE: ifp = if_lookup_by_name_all_vrf(name); if (ifp) { @@ -410,29 +567,92 @@ struct interface *if_get_by_name(const char *name, vrf_id_t vrf_id) if_update_to_new_vrf(ifp, vrf_id); return ifp; } - return if_create(name, vrf_id); + return if_create_name(name, vrf_id); + } + + return NULL; +} + +struct interface *if_get_by_ifindex(ifindex_t ifindex, vrf_id_t vrf_id) +{ + struct interface *ifp; + + switch (vrf_get_backend()) { + case VRF_BACKEND_UNKNOWN: + case VRF_BACKEND_NETNS: + ifp = if_lookup_by_ifindex(ifindex, vrf_id); + if (ifp) + return ifp; + return if_create_ifindex(ifindex, vrf_id); + case VRF_BACKEND_VRF_LITE: + ifp = if_lookup_by_index_all_vrf(ifindex); + if (ifp) { + if (ifp->vrf_id == vrf_id) + return ifp; + /* If it came from the kernel or by way of zclient, + * believe it and update the ifp accordingly. + */ + if_update_to_new_vrf(ifp, vrf_id); + return ifp; + } + return if_create_ifindex(ifindex, vrf_id); } return NULL; } -void if_set_index(struct interface *ifp, ifindex_t ifindex) +int if_set_index(struct interface *ifp, ifindex_t ifindex) { struct vrf *vrf; - vrf = vrf_lookup_by_id(ifp->vrf_id); + if (ifp->ifindex == ifindex) + return 0; + + vrf = vrf_get(ifp->vrf_id, NULL); assert(vrf); - if (ifp->ifindex == ifindex) - return; + /* + * If there is already an interface with this ifindex, we will collide + * on insertion, so don't even try. + */ + if (if_lookup_by_ifindex(ifindex, ifp->vrf_id)) + return -1; if (ifp->ifindex != IFINDEX_INTERNAL) - IFINDEX_RB_REMOVE(vrf, ifp) + IFINDEX_RB_REMOVE(vrf, ifp); ifp->ifindex = ifindex; - if (ifp->ifindex != IFINDEX_INTERNAL) - IFINDEX_RB_INSERT(vrf, ifp) + if (ifp->ifindex != IFINDEX_INTERNAL) { + /* + * This should never happen, since we checked if there was + * already an interface with the desired ifindex at the top of + * the function. Nevertheless. + */ + if (IFINDEX_RB_INSERT(vrf, ifp)) + return -1; + } + + return 0; +} + +void if_set_name(struct interface *ifp, const char *name) +{ + struct vrf *vrf; + + vrf = vrf_get(ifp->vrf_id, NULL); + assert(vrf); + + if (if_cmp_name_func(ifp->name, name) == 0) + return; + + if (ifp->name[0] != '\0') + IFNAME_RB_REMOVE(vrf, ifp); + + strlcpy(ifp->name, name, sizeof(ifp->name)); + + if (ifp->name[0] != '\0') + IFNAME_RB_INSERT(vrf, ifp); } /* Does interface up ? */ @@ -560,12 +780,15 @@ static void if_dump(const struct interface *ifp) struct listnode *node; struct connected *c __attribute__((unused)); - for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, c)) + for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, c)) { + struct vrf *vrf = vrf_lookup_by_id(ifp->vrf_id); + zlog_info( - "Interface %s vrf %u index %d metric %d mtu %d " - "mtu6 %d %s", - ifp->name, ifp->vrf_id, ifp->ifindex, ifp->metric, - ifp->mtu, ifp->mtu6, if_flag_dump(ifp->flags)); + "Interface %s vrf %s(%u) index %d metric %d mtu %d mtu6 %d %s", + ifp->name, VRF_LOGNAME(vrf), ifp->vrf_id, ifp->ifindex, + ifp->metric, ifp->mtu, ifp->mtu6, + if_flag_dump(ifp->flags)); + } } /* Interface printing for all interface. */ @@ -626,27 +849,25 @@ DEFUN (show_address, "address\n" VRF_CMD_HELP_STR) { - int idx_vrf = 3; - struct listnode *node; - struct interface *ifp; - struct connected *ifc; - struct prefix *p; - vrf_id_t vrf_id = VRF_DEFAULT; + int idx_vrf = 3; + struct listnode *node; + struct interface *ifp; + struct connected *ifc; + struct prefix *p; + vrf_id_t vrf_id = VRF_DEFAULT; - if (argc > 2) - VRF_GET_ID (vrf_id, argv[idx_vrf]->arg); + if (argc > 2) + VRF_GET_ID (vrf_id, argv[idx_vrf]->arg); - FOR_ALL_INTERFACES (vrf, ifp) - { - for (ALL_LIST_ELEMENTS_RO (ifp->connected, node, ifc)) - { - p = ifc->address; + FOR_ALL_INTERFACES (vrf, ifp) { + for (ALL_LIST_ELEMENTS_RO (ifp->connected, node, ifc)) { + p = ifc->address; - if (p->family == AF_INET) - vty_out (vty, "%s/%d\n", inet_ntoa (p->u.prefix4), p->prefixlen); + if (p->family == AF_INET) + vty_out (vty, "%s/%d\n", inet_ntoa (p->u.prefix4), p->prefixlen); + } } - } - return CMD_SUCCESS; + return CMD_SUCCESS; } DEFUN (show_address_vrf_all, @@ -656,31 +877,30 @@ DEFUN (show_address_vrf_all, "address\n" VRF_ALL_CMD_HELP_STR) { - struct vrf *vrf; - struct listnode *node; - struct interface *ifp; - struct connected *ifc; - struct prefix *p; + struct vrf *vrf; + struct listnode *node; + struct interface *ifp; + struct connected *ifc; + struct prefix *p; - RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) - { - if (RB_EMPTY (if_name_head, &vrf->ifaces_by_name)) - continue; + RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) + { + if (RB_EMPTY (if_name_head, &vrf->ifaces_by_name)) + continue; - vty_out (vty, "\nVRF %u\n\n", vrf->vrf_id); + vty_out (vty, "\nVRF %s(%u)\n\n", + VRF_LOGNAME(vrf), vrf->vrf_id); - FOR_ALL_INTERFACES (vrf, ifp) - { - for (ALL_LIST_ELEMENTS_RO (ifp->connected, node, ifc)) - { - p = ifc->address; + FOR_ALL_INTERFACES (vrf, ifp) { + for (ALL_LIST_ELEMENTS_RO (ifp->connected, node, ifc)) { + p = ifc->address; - if (p->family == AF_INET) - vty_out (vty, "%s/%d\n", inet_ntoa (p->u.prefix4), p->prefixlen); - } - } - } - return CMD_SUCCESS; + if (p->family == AF_INET) + vty_out (vty, "%s/%d\n", inet_ntoa (p->u.prefix4), p->prefixlen); + } + } + } + return CMD_SUCCESS; } #endif @@ -697,24 +917,24 @@ struct nbr_connected *nbr_connected_new(void) } /* Free connected structure. */ -void connected_free(struct connected *connected) +void connected_free(struct connected **connected) { - if (connected->address) - prefix_free(connected->address); + struct connected *ptr = *connected; - if (connected->destination) - prefix_free(connected->destination); + prefix_free(&ptr->address); + prefix_free(&ptr->destination); - XFREE(MTYPE_CONNECTED_LABEL, connected->label); + XFREE(MTYPE_CONNECTED_LABEL, ptr->label); - XFREE(MTYPE_CONNECTED, connected); + XFREE(MTYPE_CONNECTED, ptr); + *connected = NULL; } /* Free nbr connected structure. */ void nbr_connected_free(struct nbr_connected *connected) { if (connected->address) - prefix_free(connected->address); + prefix_free(&connected->address); XFREE(MTYPE_NBR_CONNECTED, connected); } @@ -739,14 +959,17 @@ connected_log(struct connected *connected, char *str) { struct prefix *p; struct interface *ifp; + struct vrf *vrf; char logbuf[BUFSIZ]; char buf[BUFSIZ]; ifp = connected->ifp; p = connected->address; - snprintf(logbuf, BUFSIZ, "%s interface %s vrf %u %s %s/%d ", str, - ifp->name, ifp->vrf_id, prefix_family_str(p), + vrf = vrf_lookup_by_id(ifp->vrf_id); + snprintf(logbuf, sizeof(logbuf), "%s interface %s vrf %s(%u) %s %s/%d ", + str, ifp->name, VRF_LOGNAME(vrf), ifp->vrf_id, + prefix_family_str(p), inet_ntop(p->family, &p->u.prefix, buf, BUFSIZ), p->prefixlen); p = connected->destination; @@ -769,15 +992,16 @@ nbr_connected_log(struct nbr_connected *connected, char *str) ifp = connected->ifp; p = connected->address; - snprintf(logbuf, BUFSIZ, "%s interface %s %s %s/%d ", str, ifp->name, - prefix_family_str(p), + snprintf(logbuf, sizeof(logbuf), "%s interface %s %s %s/%d ", str, + ifp->name, prefix_family_str(p), inet_ntop(p->family, &p->u.prefix, buf, BUFSIZ), p->prefixlen); zlog_info("%s", logbuf); } /* If two connected address has same prefix return 1. */ -static int connected_same_prefix(struct prefix *p1, struct prefix *p2) +static int connected_same_prefix(const struct prefix *p1, + const struct prefix *p2) { if (p1->family == p2->family) { if (p1->family == AF_INET @@ -790,8 +1014,22 @@ static int connected_same_prefix(struct prefix *p1, struct prefix *p2) return 0; } +/* count the number of connected addresses that are in the given family */ +unsigned int connected_count_by_family(struct interface *ifp, int family) +{ + struct listnode *cnode; + struct connected *connected; + unsigned int cnt = 0; + + for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, connected)) + if (connected->address->family == family) + cnt++; + + return cnt; +} + struct connected *connected_lookup_prefix_exact(struct interface *ifp, - struct prefix *p) + const struct prefix *p) { struct listnode *node; struct listnode *next; @@ -830,7 +1068,7 @@ struct connected *connected_delete_by_prefix(struct interface *ifp, /* Find the address on our side that will be used when packets are sent to dst. */ struct connected *connected_lookup_prefix(struct interface *ifp, - struct prefix *addr) + const struct prefix *addr) { struct listnode *cnode; struct connected *c; @@ -873,6 +1111,19 @@ struct connected *connected_add_by_prefix(struct interface *ifp, return ifc; } +struct connected *connected_get_linklocal(struct interface *ifp) +{ + struct listnode *n; + struct connected *c = NULL; + + for (ALL_LIST_ELEMENTS_RO(ifp->connected, n, c)) { + if (c->address->family == AF_INET6 + && IN6_IS_ADDR_LINKLOCAL(&c->address->u.prefix6)) + break; + } + return c; +} + #if 0 /* this route_table of struct connected's is unused \ * however, it would be good to use a route_table rather than \ * a list.. \ @@ -955,15 +1206,30 @@ ifaddr_ipv4_lookup (struct in_addr *addr, ifindex_t ifindex) void if_terminate(struct vrf *vrf) { struct interface *ifp; + bool delete; + + /* + * If the default VRF is being terminated or has + * already been terminated it means that + * the program is shutting down and we need to + * delete all the interfaces. Otherwise, we only + * need to move VRF's interfaces to the default VRF. + */ + delete = vrf_is_backend_netns() || vrf->vrf_id == VRF_DEFAULT + || !vrf_lookup_by_id(VRF_DEFAULT); while (!RB_EMPTY(if_name_head, &vrf->ifaces_by_name)) { ifp = RB_ROOT(if_name_head, &vrf->ifaces_by_name); - if (ifp->node) { - ifp->node->info = NULL; - route_unlock_node(ifp->node); + if (delete) { + if (ifp->node) { + ifp->node->info = NULL; + route_unlock_node(ifp->node); + } + if_delete(&ifp); + } else { + if_update_to_new_vrf(ifp, VRF_DEFAULT); } - if_delete(ifp); } } @@ -1034,8 +1300,6 @@ struct if_link_params *if_link_params_get(struct interface *ifp) struct if_link_params *iflp = XCALLOC(MTYPE_IF_LINK_PARAMS, sizeof(struct if_link_params)); - if (iflp == NULL) - return NULL; /* Set TE metric equal to standard metric */ iflp->te_metric = ifp->metric; @@ -1043,7 +1307,7 @@ struct if_link_params *if_link_params_get(struct interface *ifp) /* Compute default bandwidth based on interface */ iflp->default_bw = ((ifp->bandwidth ? ifp->bandwidth : DEFAULT_BANDWIDTH) - * TE_KILO_BIT / TE_BYTE); + * TE_MEGA_BIT / TE_BYTE); /* Set Max, Reservable and Unreserved Bandwidth */ iflp->max_bw = iflp->default_bw; @@ -1063,10 +1327,7 @@ struct if_link_params *if_link_params_get(struct interface *ifp) void if_link_params_free(struct interface *ifp) { - if (ifp->link_params == NULL) - return; XFREE(MTYPE_IF_LINK_PARAMS, ifp->link_params); - ifp->link_params = NULL; } /* ----------- CLI commands ----------- */ @@ -1074,9 +1335,9 @@ void if_link_params_free(struct interface *ifp) /* * XPath: /frr-interface:lib/interface */ -DEFPY_NOSH (interface, +DEFPY_YANG_NOSH (interface, interface_cmd, - "interface IFNAME [vrf NAME$vrfname]", + "interface IFNAME [vrf NAME$vrf_name]", "Select an interface to configure\n" "Interface's name\n" VRF_CMD_HELP_STR) @@ -1086,8 +1347,8 @@ DEFPY_NOSH (interface, struct interface *ifp; int ret; - if (!vrfname) - vrfname = VRF_DEFAULT_NAME; + if (!vrf_name) + vrf_name = VRF_DEFAULT_NAME; /* * This command requires special handling to maintain backward @@ -1096,7 +1357,7 @@ DEFPY_NOSH (interface, * interface is found, then a new one should be created on the default * VRF. */ - VRF_GET_ID(vrf_id, vrfname, false); + VRF_GET_ID(vrf_id, vrf_name, false); ifp = if_lookup_by_name_all_vrf(ifname); if (ifp && ifp->vrf_id != vrf_id) { struct vrf *vrf; @@ -1107,24 +1368,24 @@ DEFPY_NOSH (interface, */ if (vrf_id != VRF_DEFAULT) { vty_out(vty, "%% interface %s not in %s vrf\n", ifname, - vrfname); + vrf_name); return CMD_WARNING_CONFIG_FAILED; } /* * Special case 2: a VRF name was *not* specified, and the found * interface is associated to a VRF other than the default one. - * Update vrf_id and vrfname to account for that. + * Update vrf_id and vrf_name to account for that. */ vrf = vrf_lookup_by_id(ifp->vrf_id); assert(vrf); vrf_id = ifp->vrf_id; - vrfname = vrf->name; + vrf_name = vrf->name; } snprintf(xpath_list, sizeof(xpath_list), "/frr-interface:lib/interface[name='%s'][vrf='%s']", ifname, - vrfname); + vrf_name); nb_cli_enqueue_change(vty, ".", NB_OP_CREATE, NULL); ret = nb_cli_apply_changes(vty, xpath_list); @@ -1137,6 +1398,7 @@ DEFPY_NOSH (interface, * all interface-level commands are converted to the new * northbound model. */ + nb_cli_pending_commit_check(vty); ifp = if_lookup_by_name(ifname, vrf_id); if (ifp) VTY_PUSH_CONTEXT(INTERFACE_NODE, ifp); @@ -1145,22 +1407,22 @@ DEFPY_NOSH (interface, return ret; } -DEFPY (no_interface, +DEFPY_YANG (no_interface, no_interface_cmd, - "no interface IFNAME [vrf NAME$vrfname]", + "no interface IFNAME [vrf NAME$vrf_name]", NO_STR "Delete a pseudo interface's configuration\n" "Interface's name\n" VRF_CMD_HELP_STR) { - if (!vrfname) - vrfname = VRF_DEFAULT_NAME; + if (!vrf_name) + vrf_name = VRF_DEFAULT_NAME; nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, NULL); return nb_cli_apply_changes( vty, "/frr-interface:lib/interface[name='%s'][vrf='%s']", - ifname, vrfname); + ifname, vrf_name); } static void cli_show_interface(struct vty *vty, struct lyd_node *dnode, @@ -1180,7 +1442,7 @@ static void cli_show_interface(struct vty *vty, struct lyd_node *dnode, /* * XPath: /frr-interface:lib/interface/description */ -DEFPY (interface_desc, +DEFPY_YANG (interface_desc, interface_desc_cmd, "description LINE...", "Interface specific description\n" @@ -1197,7 +1459,7 @@ DEFPY (interface_desc, return ret; } -DEFPY (no_interface_desc, +DEFPY_YANG (no_interface_desc, no_interface_desc_cmd, "no description", NO_STR @@ -1247,24 +1509,33 @@ void if_cmd_init(void) install_element(INTERFACE_NODE, &no_interface_desc_cmd); } +void if_zapi_callbacks(int (*create)(struct interface *ifp), + int (*up)(struct interface *ifp), + int (*down)(struct interface *ifp), + int (*destroy)(struct interface *ifp)) +{ + ifp_master.create_hook = create; + ifp_master.up_hook = up; + ifp_master.down_hook = down; + ifp_master.destroy_hook = destroy; +} + /* ------- Northbound callbacks ------- */ /* * XPath: /frr-interface:lib/interface */ -static int lib_interface_create(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) +static int lib_interface_create(struct nb_cb_create_args *args) { const char *ifname; const char *vrfname; struct vrf *vrf; struct interface *ifp; - ifname = yang_dnode_get_string(dnode, "./name"); - vrfname = yang_dnode_get_string(dnode, "./vrf"); + ifname = yang_dnode_get_string(args->dnode, "./name"); + vrfname = yang_dnode_get_string(args->dnode, "./vrf"); - switch (event) { + switch (args->event) { case NB_EV_VALIDATE: vrf = vrf_lookup_by_name(vrfname); if (!vrf) { @@ -1303,25 +1574,26 @@ static int lib_interface_create(enum nb_event event, #else ifp = if_get_by_name(ifname, vrf->vrf_id); #endif /* SUNOS_5 */ - nb_running_set_entry(dnode, ifp); + + ifp->configured = true; + nb_running_set_entry(args->dnode, ifp); break; } return NB_OK; } -static int lib_interface_destroy(enum nb_event event, - const struct lyd_node *dnode) +static int lib_interface_destroy(struct nb_cb_destroy_args *args) { struct interface *ifp; - switch (event) { + switch (args->event) { case NB_EV_VALIDATE: - ifp = nb_running_get_entry(dnode, NULL, true); + ifp = nb_running_get_entry(args->dnode, NULL, true); if (CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE)) { - zlog_warn("%s: only inactive interfaces can be deleted", - __func__); + snprintf(args->errmsg, args->errmsg_len, + "only inactive interfaces can be deleted"); return NB_ERR_VALIDATION; } break; @@ -1329,64 +1601,259 @@ static int lib_interface_destroy(enum nb_event event, case NB_EV_ABORT: break; case NB_EV_APPLY: - ifp = nb_running_unset_entry(dnode); - if_delete(ifp); + ifp = nb_running_unset_entry(args->dnode); + + ifp->configured = false; + if_delete(&ifp); break; } return NB_OK; } +/* + * XPath: /frr-interface:lib/interface + */ +static const void *lib_interface_get_next(struct nb_cb_get_next_args *args) +{ + struct vrf *vrf; + struct interface *pif = (struct interface *)args->list_entry; + + if (args->list_entry == NULL) { + vrf = RB_MIN(vrf_name_head, &vrfs_by_name); + assert(vrf); + pif = RB_MIN(if_name_head, &vrf->ifaces_by_name); + } else { + vrf = vrf_lookup_by_id(pif->vrf_id); + pif = RB_NEXT(if_name_head, pif); + /* if no more interfaces, switch to next vrf */ + while (pif == NULL) { + vrf = RB_NEXT(vrf_name_head, vrf); + if (!vrf) + return NULL; + pif = RB_MIN(if_name_head, &vrf->ifaces_by_name); + } + } + + return pif; +} + +static int lib_interface_get_keys(struct nb_cb_get_keys_args *args) +{ + const struct interface *ifp = args->list_entry; + + struct vrf *vrf = vrf_lookup_by_id(ifp->vrf_id); + + assert(vrf); + + args->keys->num = 2; + strlcpy(args->keys->key[0], ifp->name, sizeof(args->keys->key[0])); + strlcpy(args->keys->key[1], vrf->name, sizeof(args->keys->key[1])); + + return NB_OK; +} + +static const void * +lib_interface_lookup_entry(struct nb_cb_lookup_entry_args *args) +{ + const char *ifname = args->keys->key[0]; + const char *vrfname = args->keys->key[1]; + struct vrf *vrf = vrf_lookup_by_name(vrfname); + + return vrf ? if_lookup_by_name(ifname, vrf->vrf_id) : NULL; +} + /* * XPath: /frr-interface:lib/interface/description */ -static int lib_interface_description_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) +static int lib_interface_description_modify(struct nb_cb_modify_args *args) { struct interface *ifp; const char *description; - if (event != NB_EV_APPLY) + if (args->event != NB_EV_APPLY) return NB_OK; - ifp = nb_running_get_entry(dnode, NULL, true); + ifp = nb_running_get_entry(args->dnode, NULL, true); XFREE(MTYPE_TMP, ifp->desc); - description = yang_dnode_get_string(dnode, NULL); + description = yang_dnode_get_string(args->dnode, NULL); ifp->desc = XSTRDUP(MTYPE_TMP, description); return NB_OK; } -static int lib_interface_description_destroy(enum nb_event event, - const struct lyd_node *dnode) +static int lib_interface_description_destroy(struct nb_cb_destroy_args *args) { struct interface *ifp; - if (event != NB_EV_APPLY) + if (args->event != NB_EV_APPLY) return NB_OK; - ifp = nb_running_get_entry(dnode, NULL, true); + ifp = nb_running_get_entry(args->dnode, NULL, true); XFREE(MTYPE_TMP, ifp->desc); return NB_OK; } +/* + * XPath: /frr-interface:lib/interface/state/if-index + */ +static struct yang_data * +lib_interface_state_if_index_get_elem(struct nb_cb_get_elem_args *args) +{ + const struct interface *ifp = args->list_entry; + + return yang_data_new_int32(args->xpath, ifp->ifindex); +} + +/* + * XPath: /frr-interface:lib/interface/state/mtu + */ +static struct yang_data * +lib_interface_state_mtu_get_elem(struct nb_cb_get_elem_args *args) +{ + const struct interface *ifp = args->list_entry; + + return yang_data_new_uint16(args->xpath, ifp->mtu); +} + +/* + * XPath: /frr-interface:lib/interface/state/mtu6 + */ +static struct yang_data * +lib_interface_state_mtu6_get_elem(struct nb_cb_get_elem_args *args) +{ + const struct interface *ifp = args->list_entry; + + return yang_data_new_uint32(args->xpath, ifp->mtu6); +} + +/* + * XPath: /frr-interface:lib/interface/state/speed + */ +static struct yang_data * +lib_interface_state_speed_get_elem(struct nb_cb_get_elem_args *args) +{ + const struct interface *ifp = args->list_entry; + + return yang_data_new_uint32(args->xpath, ifp->speed); +} + +/* + * XPath: /frr-interface:lib/interface/state/metric + */ +static struct yang_data * +lib_interface_state_metric_get_elem(struct nb_cb_get_elem_args *args) +{ + const struct interface *ifp = args->list_entry; + + return yang_data_new_uint32(args->xpath, ifp->metric); +} + +/* + * XPath: /frr-interface:lib/interface/state/flags + */ +static struct yang_data * +lib_interface_state_flags_get_elem(struct nb_cb_get_elem_args *args) +{ + /* TODO: implement me. */ + return NULL; +} + +/* + * XPath: /frr-interface:lib/interface/state/type + */ +static struct yang_data * +lib_interface_state_type_get_elem(struct nb_cb_get_elem_args *args) +{ + /* TODO: implement me. */ + return NULL; +} + +/* + * XPath: /frr-interface:lib/interface/state/phy-address + */ +static struct yang_data * +lib_interface_state_phy_address_get_elem(struct nb_cb_get_elem_args *args) +{ + const struct interface *ifp = args->list_entry; + struct ethaddr macaddr; + + memcpy(&macaddr.octet, ifp->hw_addr, ETH_ALEN); + + return yang_data_new_mac(args->xpath, &macaddr); +} + /* clang-format off */ const struct frr_yang_module_info frr_interface_info = { .name = "frr-interface", .nodes = { { .xpath = "/frr-interface:lib/interface", - .cbs.create = lib_interface_create, - .cbs.destroy = lib_interface_destroy, - .cbs.cli_show = cli_show_interface, + .cbs = { + .create = lib_interface_create, + .destroy = lib_interface_destroy, + .cli_show = cli_show_interface, + .get_next = lib_interface_get_next, + .get_keys = lib_interface_get_keys, + .lookup_entry = lib_interface_lookup_entry, + }, }, { .xpath = "/frr-interface:lib/interface/description", - .cbs.modify = lib_interface_description_modify, - .cbs.destroy = lib_interface_description_destroy, - .cbs.cli_show = cli_show_interface_desc, + .cbs = { + .modify = lib_interface_description_modify, + .destroy = lib_interface_description_destroy, + .cli_show = cli_show_interface_desc, + }, + }, + { + .xpath = "/frr-interface:lib/interface/state/if-index", + .cbs = { + .get_elem = lib_interface_state_if_index_get_elem, + } + }, + { + .xpath = "/frr-interface:lib/interface/state/mtu", + .cbs = { + .get_elem = lib_interface_state_mtu_get_elem, + } + }, + { + .xpath = "/frr-interface:lib/interface/state/mtu6", + .cbs = { + .get_elem = lib_interface_state_mtu6_get_elem, + } + }, + { + .xpath = "/frr-interface:lib/interface/state/speed", + .cbs = { + .get_elem = lib_interface_state_speed_get_elem, + } + }, + { + .xpath = "/frr-interface:lib/interface/state/metric", + .cbs = { + .get_elem = lib_interface_state_metric_get_elem, + } + }, + { + .xpath = "/frr-interface:lib/interface/state/flags", + .cbs = { + .get_elem = lib_interface_state_flags_get_elem, + } + }, + { + .xpath = "/frr-interface:lib/interface/state/type", + .cbs = { + .get_elem = lib_interface_state_type_get_elem, + } + }, + { + .xpath = "/frr-interface:lib/interface/state/phy-address", + .cbs = { + .get_elem = lib_interface_state_phy_address_get_elem, + } }, { .xpath = NULL, diff --git a/lib/if.h b/lib/if.h index d26d4dd68b..a2a40d0957 100644 --- a/lib/if.h +++ b/lib/if.h @@ -31,7 +31,6 @@ extern "C" { #endif -DECLARE_MTYPE(IF) DECLARE_MTYPE(CONNECTED_LABEL) /* Interface link-layer type, if known. Derived from: @@ -144,7 +143,7 @@ struct if_stats { #define TE_EXT_MASK 0x0FFFFFFF #define TE_EXT_ANORMAL 0x80000000 #define LOSS_PRECISION 0.000003 -#define TE_KILO_BIT 1000 +#define TE_MEGA_BIT 1000000 #define TE_BYTE 8 #define DEFAULT_BANDWIDTH 10000 #define MAX_CLASS_TYPE 8 @@ -225,6 +224,12 @@ struct interface { not work as expected. */ ifindex_t ifindex; + ifindex_t oldifindex; + + /* + * ifindex of parent interface, if any + */ + ifindex_t link_ifindex; #define IFINDEX_INTERNAL 0 /* Zebra internal interface status */ @@ -290,42 +295,73 @@ struct interface { struct route_node *node; vrf_id_t vrf_id; + /* + * Has the end users entered `interface XXXX` from the cli in some + * fashion? + */ + bool configured; + QOBJ_FIELDS }; RB_HEAD(if_name_head, interface); RB_PROTOTYPE(if_name_head, interface, name_entry, if_cmp_func) RB_HEAD(if_index_head, interface); -RB_PROTOTYPE(if_index_head, interface, index_entry, if_cmp_func) +RB_PROTOTYPE(if_index_head, interface, index_entry, if_cmp_index_func) DECLARE_QOBJ_TYPE(interface) -#define IFNAME_RB_INSERT(vrf, ifp) \ - if (RB_INSERT(if_name_head, &vrf->ifaces_by_name, (ifp))) \ - flog_err(EC_LIB_INTERFACE, \ - "%s(%s): corruption detected -- interface with this " \ - "name exists already in VRF %u!", \ - __func__, (ifp)->name, (ifp)->vrf_id); - -#define IFNAME_RB_REMOVE(vrf, ifp) \ - if (RB_REMOVE(if_name_head, &vrf->ifaces_by_name, (ifp)) == NULL) \ - flog_err(EC_LIB_INTERFACE, \ - "%s(%s): corruption detected -- interface with this " \ - "name doesn't exist in VRF %u!", \ - __func__, (ifp)->name, (ifp)->vrf_id); - -#define IFINDEX_RB_INSERT(vrf, ifp) \ - if (RB_INSERT(if_index_head, &vrf->ifaces_by_index, (ifp))) \ - flog_err(EC_LIB_INTERFACE, \ - "%s(%u): corruption detected -- interface with this " \ - "ifindex exists already in VRF %u!", \ - __func__, (ifp)->ifindex, (ifp)->vrf_id); - -#define IFINDEX_RB_REMOVE(vrf, ifp) \ - if (RB_REMOVE(if_index_head, &vrf->ifaces_by_index, (ifp)) == NULL) \ - flog_err(EC_LIB_INTERFACE, \ - "%s(%u): corruption detected -- interface with this " \ - "ifindex doesn't exist in VRF %u!", \ - __func__, (ifp)->ifindex, (ifp)->vrf_id); +#define IFNAME_RB_INSERT(vrf, ifp) \ + ({ \ + struct interface *_iz = \ + RB_INSERT(if_name_head, &vrf->ifaces_by_name, (ifp)); \ + if (_iz) \ + flog_err( \ + EC_LIB_INTERFACE, \ + "%s(%s): corruption detected -- interface with this " \ + "name exists already in VRF %u!", \ + __func__, (ifp)->name, (ifp)->vrf_id); \ + _iz; \ + }) + +#define IFNAME_RB_REMOVE(vrf, ifp) \ + ({ \ + struct interface *_iz = \ + RB_REMOVE(if_name_head, &vrf->ifaces_by_name, (ifp)); \ + if (_iz == NULL) \ + flog_err( \ + EC_LIB_INTERFACE, \ + "%s(%s): corruption detected -- interface with this " \ + "name doesn't exist in VRF %u!", \ + __func__, (ifp)->name, (ifp)->vrf_id); \ + _iz; \ + }) + + +#define IFINDEX_RB_INSERT(vrf, ifp) \ + ({ \ + struct interface *_iz = RB_INSERT( \ + if_index_head, &vrf->ifaces_by_index, (ifp)); \ + if (_iz) \ + flog_err( \ + EC_LIB_INTERFACE, \ + "%s(%u): corruption detected -- interface with this " \ + "ifindex exists already in VRF %u!", \ + __func__, (ifp)->ifindex, (ifp)->vrf_id); \ + _iz; \ + }) + +#define IFINDEX_RB_REMOVE(vrf, ifp) \ + ({ \ + struct interface *_iz = RB_REMOVE( \ + if_index_head, &vrf->ifaces_by_index, (ifp)); \ + if (_iz == NULL) \ + flog_err( \ + EC_LIB_INTERFACE, \ + "%s(%u): corruption detected -- interface with this " \ + "ifindex doesn't exist in VRF %u!", \ + __func__, (ifp)->ifindex, (ifp)->vrf_id); \ + _iz; \ + }) #define FOR_ALL_INTERFACES(vrf, ifp) \ if (vrf) \ @@ -380,16 +416,12 @@ struct connected { /* N.B. the ZEBRA_IFA_PEER flag should be set if and only if a peer address has been configured. If this flag is set, the destination field must contain the peer address. - Otherwise, if this flag is not set, the destination address - will either contain a broadcast address or be NULL. */ /* Address of connected network. */ struct prefix *address; - /* Peer or Broadcast address, depending on whether ZEBRA_IFA_PEER is - set. - Note: destination may be NULL if ZEBRA_IFA_PEER is not set. */ + /* Peer address, if ZEBRA_IFA_PEER is set, otherwise NULL */ struct prefix *destination; /* Label for Linux 2.2.X and upper. */ @@ -474,21 +506,34 @@ extern int if_cmp_name_func(const char *p1, const char *p2); * else think before you use VRF_UNKNOWN */ extern void if_update_to_new_vrf(struct interface *, vrf_id_t vrf_id); -extern struct interface *if_create(const char *name, vrf_id_t vrf_id); + +/* Create new interface, adds to name list only */ +extern struct interface *if_create_name(const char *name, vrf_id_t vrf_id); + +/* Create new interface, adds to index list only */ +extern struct interface *if_create_ifindex(ifindex_t ifindex, vrf_id_t vrf_id); extern struct interface *if_lookup_by_index(ifindex_t, vrf_id_t vrf_id); -extern struct interface *if_lookup_exact_address(void *matchaddr, int family, - vrf_id_t vrf_id); -extern struct connected *if_lookup_address(void *matchaddr, int family, +extern struct interface *if_lookup_by_index_all_vrf(ifindex_t); +extern struct interface *if_lookup_exact_address(const void *matchaddr, + int family, vrf_id_t vrf_id); +extern struct connected *if_lookup_address(const void *matchaddr, int family, vrf_id_t vrf_id); -extern struct interface *if_lookup_prefix(struct prefix *prefix, +extern struct interface *if_lookup_prefix(const struct prefix *prefix, vrf_id_t vrf_id); +size_t if_lookup_by_hwaddr(const uint8_t *hw_addr, size_t addrsz, + struct interface ***result, vrf_id_t vrf_id); -/* These 3 functions are to be used when the ifname argument is terminated - by a '\0' character: */ +struct vrf; extern struct interface *if_lookup_by_name_all_vrf(const char *ifname); +extern struct interface *if_lookup_by_name_vrf(const char *name, struct vrf *vrf); extern struct interface *if_lookup_by_name(const char *ifname, vrf_id_t vrf_id); extern struct interface *if_get_by_name(const char *ifname, vrf_id_t vrf_id); -extern void if_set_index(struct interface *ifp, ifindex_t ifindex); +extern struct interface *if_get_by_ifindex(ifindex_t ifindex, vrf_id_t vrf_id); + +/* Sets the index and adds to index list */ +extern int if_set_index(struct interface *ifp, ifindex_t ifindex); +/* Sets the name and adds to name list */ +extern void if_set_name(struct interface *ifp, const char *name); /* Delete the interface, but do not free the structure, and leave it in the interface list. It is often advisable to leave the pseudo interface @@ -497,7 +542,7 @@ extern void if_delete_retain(struct interface *); /* Delete and free the interface structure: calls if_delete_retain and then deletes it from the interface list and frees the structure. */ -extern void if_delete(struct interface *); +extern void if_delete(struct interface **ifp); extern int if_is_up(const struct interface *ifp); extern int if_is_running(const struct interface *ifp); @@ -509,7 +554,6 @@ extern bool if_is_loopback_or_vrf(const struct interface *ifp); extern int if_is_broadcast(const struct interface *ifp); extern int if_is_pointopoint(const struct interface *ifp); extern int if_is_multicast(const struct interface *ifp); -struct vrf; extern void if_terminate(struct vrf *vrf); extern void if_dump_all(void); extern const char *if_flag_dump(unsigned long); @@ -527,19 +571,21 @@ extern ifindex_t ifname2ifindex(const char *ifname, vrf_id_t vrf_id); /* Connected address functions. */ extern struct connected *connected_new(void); -extern void connected_free(struct connected *); +extern void connected_free(struct connected **connected); extern void connected_add(struct interface *, struct connected *); extern struct connected * connected_add_by_prefix(struct interface *, struct prefix *, struct prefix *); extern struct connected *connected_delete_by_prefix(struct interface *, struct prefix *); extern struct connected *connected_lookup_prefix(struct interface *, - struct prefix *); + const struct prefix *); extern struct connected *connected_lookup_prefix_exact(struct interface *, - struct prefix *); + const struct prefix *); +extern unsigned int connected_count_by_family(struct interface *, int family); extern struct nbr_connected *nbr_connected_new(void); extern void nbr_connected_free(struct nbr_connected *); struct nbr_connected *nbr_connected_check(struct interface *, struct prefix *); +struct connected *connected_get_linklocal(struct interface *ifp); /* link parameters */ struct if_link_params *if_link_params_get(struct interface *); @@ -547,6 +593,16 @@ void if_link_params_free(struct interface *); /* Northbound. */ extern void if_cmd_init(void); +extern void if_zapi_callbacks(int (*create)(struct interface *ifp), + int (*up)(struct interface *ifp), + int (*down)(struct interface *ifp), + int (*destroy)(struct interface *ifp)); + +extern void if_new_via_zapi(struct interface *ifp); +extern void if_up_via_zapi(struct interface *ifp); +extern void if_down_via_zapi(struct interface *ifp); +extern void if_destroy_via_zapi(struct interface *ifp); + extern const struct frr_yang_module_info frr_interface_info; #ifdef __cplusplus diff --git a/lib/if_rmap.c b/lib/if_rmap.c index b0802da961..1973d40be4 100644 --- a/lib/if_rmap.c +++ b/lib/if_rmap.c @@ -107,7 +107,7 @@ static struct if_rmap *if_rmap_get(struct if_rmap_ctx *ctx, const char *ifname) return ret; } -static unsigned int if_rmap_hash_make(void *data) +static unsigned int if_rmap_hash_make(const void *data) { const struct if_rmap *if_rmap = data; @@ -164,7 +164,6 @@ static int if_rmap_unset(struct if_rmap_ctx *ctx, return 0; XFREE(MTYPE_IF_RMAP_NAME, if_rmap->routemap[IF_RMAP_IN]); - if_rmap->routemap[IF_RMAP_IN] = NULL; } if (type == IF_RMAP_OUT) { @@ -174,7 +173,6 @@ static int if_rmap_unset(struct if_rmap_ctx *ctx, return 0; XFREE(MTYPE_IF_RMAP_NAME, if_rmap->routemap[IF_RMAP_OUT]); - if_rmap->routemap[IF_RMAP_OUT] = NULL; } if (ctx->if_rmap_delete_hook) diff --git a/lib/imsg-buffer.c b/lib/imsg-buffer.c index c2f4052b8f..4d41702707 100644 --- a/lib/imsg-buffer.c +++ b/lib/imsg-buffer.c @@ -30,10 +30,10 @@ struct ibuf *ibuf_open(size_t len) struct ibuf *buf; if ((buf = calloc(1, sizeof(struct ibuf))) == NULL) - return (NULL); + return NULL; if ((buf->buf = malloc(len)) == NULL) { free(buf); - return (NULL); + return NULL; } buf->size = buf->max = len; buf->fd = -1; @@ -46,10 +46,10 @@ struct ibuf *ibuf_dynamic(size_t len, size_t max) struct ibuf *buf; if (max < len) - return (NULL); + return NULL; if ((buf = ibuf_open(len)) == NULL) - return (NULL); + return NULL; if (max > 0) buf->max = max; @@ -64,27 +64,27 @@ static int ibuf_realloc(struct ibuf *buf, size_t len) /* on static buffers max is eq size and so the following fails */ if (buf->wpos + len > buf->max) { errno = ERANGE; - return (-1); + return -1; } b = realloc(buf->buf, buf->wpos + len); if (b == NULL) - return (-1); + return -1; buf->buf = b; buf->size = buf->wpos + len; - return (0); + return 0; } int ibuf_add(struct ibuf *buf, const void *data, size_t len) { if (buf->wpos + len > buf->size) if (ibuf_realloc(buf, len) == -1) - return (-1); + return -1; memcpy(buf->buf + buf->wpos, data, len); buf->wpos += len; - return (0); + return 0; } void *ibuf_reserve(struct ibuf *buf, size_t len) @@ -93,7 +93,7 @@ void *ibuf_reserve(struct ibuf *buf, size_t len) if (buf->wpos + len > buf->size) if (ibuf_realloc(buf, len) == -1) - return (NULL); + return NULL; b = buf->buf + buf->wpos; buf->wpos += len; @@ -104,7 +104,7 @@ void *ibuf_seek(struct ibuf *buf, size_t pos, size_t len) { /* only allowed to seek in already written parts */ if (pos + len > buf->wpos) - return (NULL); + return NULL; return (buf->buf + pos); } @@ -146,17 +146,17 @@ int ibuf_write(struct msgbuf *msgbuf) goto again; if (errno == ENOBUFS) errno = EAGAIN; - return (-1); + return -1; } if (n == 0) { /* connection closed */ errno = 0; - return (0); + return 0; } msgbuf_drain(msgbuf, n); - return (1); + return 1; } void ibuf_free(struct ibuf *buf) @@ -246,12 +246,12 @@ int msgbuf_write(struct msgbuf *msgbuf) goto again; if (errno == ENOBUFS) errno = EAGAIN; - return (-1); + return -1; } if (n == 0) { /* connection closed */ errno = 0; - return (0); + return 0; } /* @@ -265,7 +265,7 @@ int msgbuf_write(struct msgbuf *msgbuf) msgbuf_drain(msgbuf, n); - return (1); + return 1; } static void ibuf_enqueue(struct msgbuf *msgbuf, struct ibuf *buf) diff --git a/lib/imsg.c b/lib/imsg.c index f07c56f0a6..b46d5cbc24 100644 --- a/lib/imsg.c +++ b/lib/imsg.c @@ -37,7 +37,7 @@ static int available_fds(unsigned int n) int ret, fds[256]; if (n > (unsigned int)array_size(fds)) - return (1); + return 1; ret = 0; for (i = 0; i < n; i++) { @@ -93,7 +93,7 @@ ssize_t imsg_read(struct imsgbuf *ibuf) msg.msg_controllen = sizeof(cmsgbuf.buf); if ((ifd = calloc(1, sizeof(struct imsg_fd))) == NULL) - return (-1); + return -1; again: #ifdef __OpenBSD__ @@ -108,7 +108,7 @@ ssize_t imsg_read(struct imsgbuf *ibuf) #endif errno = EAGAIN; free(ifd); - return (-1); + return -1; } n = recvmsg(ibuf->fd, &msg, 0); @@ -161,21 +161,21 @@ ssize_t imsg_get(struct imsgbuf *ibuf, struct imsg *imsg) av = ibuf->r.wpos; if (IMSG_HEADER_SIZE > av) - return (0); + return 0; memcpy(&imsg->hdr, ibuf->r.buf, sizeof(imsg->hdr)); if (imsg->hdr.len < IMSG_HEADER_SIZE || imsg->hdr.len > MAX_IMSGSIZE) { errno = ERANGE; - return (-1); + return -1; } if (imsg->hdr.len > av) - return (0); + return 0; datalen = imsg->hdr.len - IMSG_HEADER_SIZE; ibuf->r.rptr = ibuf->r.buf + IMSG_HEADER_SIZE; if (datalen == 0) imsg->data = NULL; else if ((imsg->data = malloc(datalen)) == NULL) - return (-1); + return -1; if (imsg->hdr.flags & IMSGF_HASFD) imsg->fd = imsg_get_fd(ibuf); @@ -201,16 +201,16 @@ int imsg_compose(struct imsgbuf *ibuf, uint32_t type, uint32_t peerid, struct ibuf *wbuf; if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL) - return (-1); + return -1; if (imsg_add(wbuf, data, datalen) == -1) - return (-1); + return -1; wbuf->fd = fd; imsg_close(ibuf, wbuf); - return (1); + return 1; } int imsg_composev(struct imsgbuf *ibuf, uint32_t type, uint32_t peerid, @@ -223,17 +223,17 @@ int imsg_composev(struct imsgbuf *ibuf, uint32_t type, uint32_t peerid, datalen += iov[i].iov_len; if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL) - return (-1); + return -1; for (i = 0; i < iovcnt; i++) if (imsg_add(wbuf, iov[i].iov_base, iov[i].iov_len) == -1) - return (-1); + return -1; wbuf->fd = fd; imsg_close(ibuf, wbuf); - return (1); + return 1; } /* ARGSUSED */ @@ -248,7 +248,7 @@ struct ibuf *imsg_create(struct imsgbuf *ibuf, uint32_t type, uint32_t peerid, datalen += IMSG_HEADER_SIZE; if (datalen > MAX_IMSGSIZE) { errno = ERANGE; - return (NULL); + return NULL; } hdr.type = type; @@ -257,10 +257,10 @@ struct ibuf *imsg_create(struct imsgbuf *ibuf, uint32_t type, uint32_t peerid, if ((hdr.pid = pid) == 0) hdr.pid = ibuf->pid; if ((wbuf = ibuf_dynamic(datalen, MAX_IMSGSIZE)) == NULL) { - return (NULL); + return NULL; } if (imsg_add(wbuf, &hdr, sizeof(hdr)) == -1) - return (NULL); + return NULL; return (wbuf); } @@ -270,7 +270,7 @@ int imsg_add(struct ibuf *msg, const void *data, uint16_t datalen) if (datalen) if (ibuf_add(msg, data, datalen) == -1) { ibuf_free(msg); - return (-1); + return -1; } return (datalen); } @@ -301,7 +301,7 @@ int imsg_get_fd(struct imsgbuf *ibuf) struct imsg_fd *ifd; if ((ifd = TAILQ_POP_FIRST(&ibuf->fds, entry)) == NULL) - return (-1); + return -1; fd = ifd->fd; free(ifd); @@ -313,8 +313,8 @@ int imsg_flush(struct imsgbuf *ibuf) { while (ibuf->w.queued) if (msgbuf_write(&ibuf->w) <= 0) - return (-1); - return (0); + return -1; + return 0; } void imsg_clear(struct imsgbuf *ibuf) diff --git a/lib/ipaddr.h b/lib/ipaddr.h index f4ddadc66e..730c7ce130 100644 --- a/lib/ipaddr.h +++ b/lib/ipaddr.h @@ -25,6 +25,8 @@ #include +#include "lib/log.h" + #ifdef __cplusplus extern "C" { #endif @@ -33,9 +35,9 @@ extern "C" { * Generic IP address - union of IPv4 and IPv6 address. */ enum ipaddr_type_t { - IPADDR_NONE = 0, - IPADDR_V4 = 1, /* IPv4 */ - IPADDR_V6 = 2, /* IPv6 */ + IPADDR_NONE = AF_UNSPEC, + IPADDR_V4 = AF_INET, + IPADDR_V6 = AF_INET6, }; struct ipaddr { @@ -56,6 +58,21 @@ struct ipaddr { #define SET_IPADDR_V4(p) (p)->ipa_type = IPADDR_V4 #define SET_IPADDR_V6(p) (p)->ipa_type = IPADDR_V6 +#define IPADDRSZ(p) \ + (IS_IPADDR_V4((p)) ? sizeof(struct in_addr) : sizeof(struct in6_addr)) + +static inline int ipaddr_family(const struct ipaddr *ip) +{ + switch (ip->ipa_type) { + case IPADDR_V4: + return AF_INET; + case IPADDR_V6: + return AF_INET6; + default: + return AF_UNSPEC; + } +} + static inline int str2ipaddr(const char *str, struct ipaddr *ip) { int ret; @@ -78,18 +95,21 @@ static inline int str2ipaddr(const char *str, struct ipaddr *ip) return -1; } -static inline char *ipaddr2str(struct ipaddr *ip, char *buf, int size) +static inline char *ipaddr2str(const struct ipaddr *ip, char *buf, int size) { buf[0] = '\0'; - if (ip) { - if (IS_IPADDR_V4(ip)) - inet_ntop(AF_INET, &ip->ip.addr, buf, size); - else if (IS_IPADDR_V6(ip)) - inet_ntop(AF_INET6, &ip->ip.addr, buf, size); - } + if (ip) + inet_ntop(ip->ipa_type, &ip->ip.addr, buf, size); return buf; } +#define IS_MAPPED_IPV6(A) \ + ((A)->s6_addr32[0] == 0x00000000 \ + ? ((A)->s6_addr32[1] == 0x00000000 \ + ? (ntohl((A)->s6_addr32[2]) == 0xFFFF ? 1 : 0) \ + : 0) \ + : 0) + /* * Convert IPv4 address to IPv4-mapped IPv6 address which is of the * form ::FFFF: (RFC 4291). This IPv6 address can then @@ -109,13 +129,51 @@ static inline void ipv4_to_ipv4_mapped_ipv6(struct in6_addr *in6, /* * convert an ipv4 mapped ipv6 address back to ipv4 address */ -static inline void ipv4_mapped_ipv6_to_ipv4(struct in6_addr *in6, +static inline void ipv4_mapped_ipv6_to_ipv4(const struct in6_addr *in6, struct in_addr *in) { memset(in, 0, sizeof(struct in_addr)); memcpy(in, (char *)in6 + 12, sizeof(struct in_addr)); } +/* + * Check if a struct ipaddr has nonzero value + */ +static inline bool ipaddr_isset(struct ipaddr *ip) +{ + static struct ipaddr a = {}; + return (0 != memcmp(&a, ip, sizeof(struct ipaddr))); +} + +/* + * generic ordering comparison between IP addresses + */ +static inline int ipaddr_cmp(const struct ipaddr *a, const struct ipaddr *b) +{ + uint32_t va, vb; + va = a->ipa_type; + vb = b->ipa_type; + if (va != vb) + return (va < vb) ? -1 : 1; + switch (a->ipa_type) { + case IPADDR_V4: + va = ntohl(a->ipaddr_v4.s_addr); + vb = ntohl(b->ipaddr_v4.s_addr); + if (va != vb) + return (va < vb) ? -1 : 1; + return 0; + case IPADDR_V6: + return memcmp((void *)&a->ipaddr_v6, (void *)&b->ipaddr_v6, + sizeof(a->ipaddr_v6)); + default: + return 0; + } +} + +#ifdef _FRR_ATTRIBUTE_PRINTFRR +#pragma FRR printfrr_ext "%pIA" (struct ipaddr *) +#endif + #ifdef __cplusplus } #endif diff --git a/lib/json.c b/lib/json.c index 4ea20ba178..6bea3982e3 100644 --- a/lib/json.c +++ b/lib/json.c @@ -47,11 +47,12 @@ void json_object_string_add(struct json_object *obj, const char *key, void json_object_int_add(struct json_object *obj, const char *key, int64_t i) { -#if defined(HAVE_JSON_C_JSON_H) json_object_object_add(obj, key, json_object_new_int64(i)); -#else - json_object_object_add(obj, key, json_object_new_int((int)i)); -#endif +} + +void json_object_double_add(struct json_object *obj, const char *key, double i) +{ + json_object_object_add(obj, key, json_object_new_double(i)); } void json_object_boolean_false_add(struct json_object *obj, const char *key) @@ -64,6 +65,11 @@ void json_object_boolean_true_add(struct json_object *obj, const char *key) json_object_object_add(obj, key, json_object_new_boolean(1)); } +void json_object_boolean_add(struct json_object *obj, const char *key, bool val) +{ + json_object_object_add(obj, key, json_object_new_boolean(val)); +} + struct json_object *json_object_lock(struct json_object *obj) { return json_object_get(obj); @@ -73,16 +79,3 @@ void json_object_free(struct json_object *obj) { json_object_put(obj); } - -#if !defined(HAVE_JSON_C_JSON_H) -int json_object_object_get_ex(struct json_object *obj, const char *key, - struct json_object **value) -{ - *value = json_object_object_get(obj, key); - - if (*value) - return 1; - - return 0; -} -#endif diff --git a/lib/json.h b/lib/json.h index a5251662be..afe0b175da 100644 --- a/lib/json.h +++ b/lib/json.h @@ -25,7 +25,7 @@ extern "C" { #endif -#if defined(HAVE_JSON_C_JSON_H) +#include "command.h" #include /* @@ -41,26 +41,16 @@ extern "C" { json_object_iter_equal(&(joi), &(join)) == 0; \ json_object_iter_next(&(joi))) -#else -#include - -/* - * json_object_to_json_string_ext is only available for json-c - * so let's just turn it back to the original usage. - */ -#define json_object_to_json_string_ext(A, B) json_object_to_json_string (A) - -extern int json_object_object_get_ex(struct json_object *obj, const char *key, - struct json_object **value); -#endif - -#include "command.h" - extern bool use_json(const int argc, struct cmd_token *argv[]); extern void json_object_string_add(struct json_object *obj, const char *key, const char *s); extern void json_object_int_add(struct json_object *obj, const char *key, int64_t i); +void json_object_boolean_add(struct json_object *obj, const char *key, + bool val); + +extern void json_object_double_add(struct json_object *obj, const char *key, + double i); extern void json_object_boolean_false_add(struct json_object *obj, const char *key); extern void json_object_boolean_true_add(struct json_object *obj, diff --git a/lib/keychain.c b/lib/keychain.c index fc9f0f9cfa..82fd6a65f2 100644 --- a/lib/keychain.c +++ b/lib/keychain.c @@ -959,20 +959,30 @@ DEFUN (no_send_lifetime, return CMD_SUCCESS; } -static struct cmd_node keychain_node = {KEYCHAIN_NODE, "%s(config-keychain)# ", - 1}; - -static struct cmd_node keychain_key_node = {KEYCHAIN_KEY_NODE, - "%s(config-keychain-key)# ", 1}; +static int keychain_config_write(struct vty *vty); +static struct cmd_node keychain_node = { + .name = "keychain", + .node = KEYCHAIN_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-keychain)# ", + .config_write = keychain_config_write, +}; + +static struct cmd_node keychain_key_node = { + .name = "keychain key", + .node = KEYCHAIN_KEY_NODE, + .parent_node = KEYCHAIN_NODE, + .prompt = "%s(config-keychain-key)# ", +}; static int keychain_strftime(char *buf, int bufsiz, time_t *time) { - struct tm *tm; + struct tm tm; size_t len; - tm = localtime(time); + localtime_r(time, &tm); - len = strftime(buf, bufsiz, "%T %b %d %Y", tm); + len = strftime(buf, bufsiz, "%T %b %d %Y", &tm); return len; } @@ -1031,6 +1041,8 @@ static int keychain_config_write(struct vty *vty) } vty_out(vty, "\n"); } + + vty_out(vty, " exit\n"); } vty_out(vty, "!\n"); } @@ -1042,8 +1054,8 @@ void keychain_init(void) { keychain_list = list_new(); - install_node(&keychain_node, keychain_config_write); - install_node(&keychain_key_node, NULL); + install_node(&keychain_node); + install_node(&keychain_key_node); install_default(KEYCHAIN_NODE); install_default(KEYCHAIN_KEY_NODE); diff --git a/lib/lib_errors.c b/lib/lib_errors.c index 5f6c25b770..6e5088142a 100644 --- a/lib/lib_errors.c +++ b/lib/lib_errors.c @@ -50,6 +50,12 @@ static struct log_ref ferr_lib_warn[] = { .description = "The Event subsystem has detected a slow process, this typically indicates that FRR is having trouble completing work in a timely manner. This can be either a misconfiguration, bug, or some combination therof.", .suggestion = "Gather log data and open an Issue", }, + { + .code = EC_LIB_NO_THREAD, + .title = "The Event subsystem has detected an internal FD problem", + .description = "The Event subsystem has detected a file descriptor read/write event without an associated handling function. This is a bug, please collect log data and open an issue.", + .suggestion = "Gather log data and open an Issue", + }, { .code = EC_LIB_RMAP_RECURSION_LIMIT, .title = "Reached the Route-Map Recursion Limit", @@ -332,6 +338,12 @@ static struct log_ref ferr_lib_err[] = { .description = "The northbound subsystem has detected that the libsysrepo library returned an error", .suggestion = "Open an Issue with all relevant log files and restart FRR" }, + { + .code = EC_LIB_GRPC_INIT, + .title = "gRPC initialization error", + .description = "Upon startup FRR failed to properly initialize and startup the gRPC northbound plugin", + .suggestion = "Check if the gRPC libraries are installed correctly in the system.", + }, { .code = EC_LIB_NB_CB_CONFIG_ABORT, .title = "A northbound configuration callback has failed in the ABORT phase", @@ -344,6 +356,12 @@ static struct log_ref ferr_lib_err[] = { .description = "A callback used to process a configuration change has returned an error while applying the changes", .suggestion = "Gather log data and open an Issue.", }, + { + .code = EC_LIB_RESOLVER, + .title = "DNS Resolution", + .description = "An error was detected while attempting to resolve a hostname", + .suggestion = "Ensure that DNS is working properly and the hostname is configured in dns. If you are still seeing this error, open an issue" + }, { .code = END_FERR, } diff --git a/lib/lib_errors.h b/lib/lib_errors.h index fc405c2098..4730b6aa33 100644 --- a/lib/lib_errors.h +++ b/lib/lib_errors.h @@ -45,6 +45,7 @@ enum lib_log_refs { EC_LIB_STREAM, EC_LIB_LINUX_NS, EC_LIB_SLOW_THREAD, + EC_LIB_NO_THREAD, EC_LIB_RMAP_RECURSION_LIMIT, EC_LIB_BACKUP_CONFIG, EC_LIB_VRF_LENGTH, @@ -80,8 +81,10 @@ enum lib_log_refs { EC_LIB_SYSREPO_INIT, EC_LIB_SYSREPO_DATA_CONVERT, EC_LIB_LIBSYSREPO, + EC_LIB_GRPC_INIT, EC_LIB_ID_CONSISTENCY, EC_LIB_ID_EXHAUST, + EC_LIB_RESOLVER, }; extern void lib_error_init(void); diff --git a/lib/memory_vty.c b/lib/lib_vty.c similarity index 75% rename from lib/memory_vty.c rename to lib/lib_vty.c index 5fd9c3b900..9c927ca4af 100644 --- a/lib/memory_vty.c +++ b/lib/lib_vty.c @@ -1,5 +1,5 @@ /* - * Memory and dynamic module VTY routine + * Assorted library VTY commands * * Copyright (C) 1998 Kunihiro Ishiguro * Copyright (C) 2016-2017 David Lamparter for NetDEF, Inc. @@ -35,7 +35,8 @@ #include "log.h" #include "memory.h" #include "module.h" -#include "memory_vty.h" +#include "defaults.h" +#include "lib_vty.h" /* Looking up memory status from vty interface. */ #include "vector.h" @@ -92,7 +93,7 @@ static int qmem_walker(void *arg, struct memgroup *mg, struct memtype *mt) #endif ); } else { - if (mt->n_alloc != 0) { + if (mt->n_max != 0) { char size[32]; snprintf(size, sizeof(size), "%6zu", mt->size); #ifdef HAVE_MALLOC_USABLE_SIZE @@ -120,11 +121,11 @@ static int qmem_walker(void *arg, struct memgroup *mg, struct memtype *mt) } -DEFUN (show_memory, - show_memory_cmd, - "show memory", - "Show running system information\n" - "Memory statistics\n") +DEFUN_NOSH (show_memory, + show_memory_cmd, + "show memory", + "Show running system information\n" + "Memory statistics\n") { #ifdef HAVE_MALLINFO show_memory_mallinfo(vty); @@ -134,11 +135,11 @@ DEFUN (show_memory, return CMD_SUCCESS; } -DEFUN (show_modules, - show_modules_cmd, - "show modules", - "Show running system information\n" - "Loaded modules\n") +DEFUN_NOSH (show_modules, + show_modules_cmd, + "show modules", + "Show running system information\n" + "Loaded modules\n") { struct frrmod_runtime *plug = frrmod_list; @@ -171,11 +172,66 @@ DEFUN (show_modules, } plug = plug->next; } + + vty_out(vty, "pid: %u\n", (uint32_t)(getpid())); + return CMD_SUCCESS; } -void memory_init(void) +DEFUN (frr_defaults, + frr_defaults_cmd, + "frr defaults PROFILE...", + "FRRouting global parameters\n" + "set of configuration defaults used\n" + "profile string\n") +{ + char *profile = argv_concat(argv, argc, 2); + int rv = CMD_SUCCESS; + + if (!frr_defaults_profile_valid(profile)) { + vty_out(vty, "%% WARNING: profile %s is not known in this version\n", + profile); + rv = CMD_WARNING; + } + frr_defaults_profile_set(profile); + XFREE(MTYPE_TMP, profile); + return rv; +} + +DEFUN (frr_version, + frr_version_cmd, + "frr version VERSION...", + "FRRouting global parameters\n" + "version configuration was written by\n" + "version string\n") { + char *version = argv_concat(argv, argc, 2); + + frr_defaults_version_set(version); + XFREE(MTYPE_TMP, version); + return CMD_SUCCESS; +} + +static void defaults_autocomplete(vector comps, struct cmd_token *token) +{ + const char **p; + + for (p = frr_defaults_profiles; *p; p++) + vector_set(comps, XSTRDUP(MTYPE_COMPLETION, *p)); +} + +static const struct cmd_variable_handler default_var_handlers[] = { + {.tokenname = "PROFILE", .completions = defaults_autocomplete}, + {.completions = NULL}, +}; + +void lib_cmd_init(void) +{ + cmd_variable_handler_register(default_var_handlers); + + install_element(CONFIG_NODE, &frr_defaults_cmd); + install_element(CONFIG_NODE, &frr_version_cmd); + install_element(VIEW_NODE, &show_memory_cmd); install_element(VIEW_NODE, &show_modules_cmd); } diff --git a/lib/memory_vty.h b/lib/lib_vty.h similarity index 89% rename from lib/memory_vty.h rename to lib/lib_vty.h index 941255be1d..48e409ec52 100644 --- a/lib/memory_vty.h +++ b/lib/lib_vty.h @@ -18,8 +18,8 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef _ZEBRA_MEMORY_VTY_H -#define _ZEBRA_MEMORY_VTY_H +#ifndef _ZEBRA_LIB_VTY_H +#define _ZEBRA_LIB_VTY_H #include "memory.h" @@ -27,7 +27,7 @@ extern "C" { #endif -extern void memory_init(void); +extern void lib_cmd_init(void); /* Human friendly string for given byte count */ #define MTYPE_MEMSTR_LEN 20 @@ -37,4 +37,4 @@ extern const char *mtype_memstr(char *, size_t, unsigned long); } #endif -#endif /* _ZEBRA_MEMORY_VTY_H */ +#endif /* _ZEBRA_LIB_VTY_H */ diff --git a/lib/libfrr.c b/lib/libfrr.c index 0d4c8d6c0f..800596c563 100644 --- a/lib/libfrr.c +++ b/lib/libfrr.c @@ -30,22 +30,27 @@ #include "vty.h" #include "command.h" #include "version.h" -#include "memory_vty.h" +#include "lib_vty.h" +#include "log_vty.h" #include "zclient.h" -#include "log_int.h" #include "module.h" #include "network.h" #include "lib_errors.h" #include "db.h" #include "northbound_cli.h" #include "northbound_db.h" +#include "debug.h" +#include "frrcu.h" +#include "frr_pthread.h" +#include "defaults.h" DEFINE_HOOK(frr_late_init, (struct thread_master * tm), (tm)) +DEFINE_HOOK(frr_very_late_init, (struct thread_master * tm), (tm)) DEFINE_KOOH(frr_early_fini, (), ()) DEFINE_KOOH(frr_fini, (), ()) const char frr_sysconfdir[] = SYSCONFDIR; -const char frr_vtydir[] = DAEMON_VTY_DIR; +char frr_vtydir[256]; #ifdef HAVE_SQLITE3 const char frr_dbdir[] = DAEMON_DB_DIR; #endif @@ -56,11 +61,11 @@ char frr_protonameinst[256] = "NONE"; char config_default[512]; char frr_zclientpath[256]; -static char pidfile_default[512]; +static char pidfile_default[1024]; #ifdef HAVE_SQLITE3 static char dbfile_default[512]; #endif -static char vtypath_default[256]; +static char vtypath_default[512]; bool debug_memstats_at_exit = false; static bool nodetach_term, nodetach_daemon; @@ -80,8 +85,8 @@ static void opt_extend(const struct optspec *os) { const struct option *lo; - strcat(comb_optstr, os->optstr); - strcat(comb_helpstr, os->helpstr); + strlcat(comb_optstr, os->optstr, sizeof(comb_optstr)); + strlcat(comb_helpstr, os->helpstr, sizeof(comb_helpstr)); for (lo = os->longopts; lo->name; lo++) memcpy(comb_next_lo++, lo, sizeof(*lo)); } @@ -93,24 +98,30 @@ static void opt_extend(const struct optspec *os) #define OPTION_LOGLEVEL 1004 #define OPTION_TCLI 1005 #define OPTION_DB_FILE 1006 +#define OPTION_LOGGING 1007 static const struct option lo_always[] = { {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'v'}, {"daemon", no_argument, NULL, 'd'}, {"module", no_argument, NULL, 'M'}, + {"profile", required_argument, NULL, 'F'}, + {"pathspace", required_argument, NULL, 'N'}, {"vty_socket", required_argument, NULL, OPTION_VTYSOCK}, {"moduledir", required_argument, NULL, OPTION_MODULEDIR}, {"log", required_argument, NULL, OPTION_LOG}, {"log-level", required_argument, NULL, OPTION_LOGLEVEL}, {"tcli", no_argument, NULL, OPTION_TCLI}, + {"command-log-always", no_argument, NULL, OPTION_LOGGING}, {NULL}}; static const struct optspec os_always = { - "hvdM:", + "hvdM:F:N:", " -h, --help Display this help and exit\n" " -v, --version Print program version\n" " -d, --daemon Runs in daemon mode\n" " -M, --module Load specified module\n" + " -F, --profile Use specified configuration profile\n" + " -N, --pathspace Insert prefix into config & socket paths\n" " --vty_socket Override vty socket path\n" " --moduledir Override modules directory\n" " --log Set Logging to stdout, syslog, or file:\n" @@ -125,18 +136,16 @@ static const struct option lo_cfg_pid_dry[] = { #ifdef HAVE_SQLITE3 {"db_file", required_argument, NULL, OPTION_DB_FILE}, #endif - {"pathspace", required_argument, NULL, 'N'}, {"dryrun", no_argument, NULL, 'C'}, {"terminal", no_argument, NULL, 't'}, {NULL}}; static const struct optspec os_cfg_pid_dry = { - "f:i:CtN:", + "f:i:Ct", " -f, --config_file Set configuration file name\n" " -i, --pid_file Set process identifier file name\n" #ifdef HAVE_SQLITE3 " --db_file Set database file name\n" #endif - " -N, --pathspace Insert prefix into config & socket paths\n" " -C, --dryrun Check configuration for validity and exit\n" " -t, --terminal Open terminal session on stdio\n" " -d -t Daemonize after terminal session ends\n", @@ -169,14 +178,13 @@ static const struct optspec os_user = {"u:g:", " -g, --group Group to run as\n", lo_user}; - bool frr_zclient_addr(struct sockaddr_storage *sa, socklen_t *sa_len, const char *path) { memset(sa, 0, sizeof(*sa)); if (!path) - path = ZEBRA_SERV_PATH; + path = frr_zclientpath; if (!strncmp(path, ZAPI_TCP_PATHNAME, strlen(ZAPI_TCP_PATHNAME))) { /* note: this functionality is disabled at bottom */ @@ -282,6 +290,11 @@ bool frr_zclient_addr(struct sockaddr_storage *sa, socklen_t *sa_len, static struct frr_daemon_info *di = NULL; +void frr_init_vtydir(void) +{ + snprintf(frr_vtydir, sizeof(frr_vtydir), DAEMON_VTY_DIR, "", ""); +} + void frr_preinit(struct frr_daemon_info *daemon, int argc, char **argv) { di = daemon; @@ -304,10 +317,13 @@ void frr_preinit(struct frr_daemon_info *daemon, int argc, char **argv) if (di->flags & FRR_DETACH_LATER) nodetach_daemon = true; + frr_init_vtydir(); snprintf(config_default, sizeof(config_default), "%s/%s.conf", frr_sysconfdir, di->name); snprintf(pidfile_default, sizeof(pidfile_default), "%s/%s.pid", frr_vtydir, di->name); + snprintf(frr_zclientpath, sizeof(frr_zclientpath), + ZEBRA_SERV_PATH, "", ""); #ifdef HAVE_SQLITE3 snprintf(dbfile_default, sizeof(dbfile_default), "%s/%s.db", frr_dbdir, di->name); @@ -316,8 +332,6 @@ void frr_preinit(struct frr_daemon_info *daemon, int argc, char **argv) strlcpy(frr_protoname, di->logname, sizeof(frr_protoname)); strlcpy(frr_protonameinst, di->logname, sizeof(frr_protonameinst)); - strlcpy(frr_zclientpath, ZEBRA_SERV_PATH, sizeof(frr_zclientpath)); - di->cli_mode = FRR_CLI_CLASSIC; } @@ -369,7 +383,7 @@ static int frr_opt(int opt) exit(0); break; case 'd': - di->daemon_mode = 1; + di->daemon_mode = true; break; case 'M': oc = XMALLOC(MTYPE_TMP, sizeof(*oc)); @@ -378,6 +392,32 @@ static int frr_opt(int opt) *modnext = oc; modnext = &oc->next; break; + case 'F': + if (!frr_defaults_profile_valid(optarg)) { + const char **p; + FILE *ofd = stderr; + + if (!strcmp(optarg, "help")) + ofd = stdout; + else + fprintf(stderr, + "The \"%s\" configuration profile is not valid for this FRR version.\n", + optarg); + + fprintf(ofd, "Available profiles are:\n"); + for (p = frr_defaults_profiles; *p; p++) + fprintf(ofd, "%s%s\n", + strcmp(*p, DFLT_NAME) ? " " : " * ", + *p); + + if (ofd == stdout) + exit(0); + fprintf(ofd, "\n"); + errors++; + break; + } + frr_defaults_profile_set(optarg); + break; case 'i': if (di->flags & FRR_NO_CFG_PID_DRY) return 1; @@ -389,14 +429,16 @@ static int frr_opt(int opt) di->config_file = optarg; break; case 'N': - if (di->flags & FRR_NO_CFG_PID_DRY) - return 1; if (di->pathspace) { fprintf(stderr, "-N/--pathspace option specified more than once!\n"); errors++; break; } + if (di->zpathspace) + fprintf(stderr, + "-N option overridden by -z for zebra named socket path\n"); + if (strchr(optarg, '/') || strchr(optarg, '.')) { fprintf(stderr, "slashes or dots are not permitted in the --pathspace option.\n"); @@ -404,6 +446,14 @@ static int frr_opt(int opt) break; } di->pathspace = optarg; + + if (!di->zpathspace) + snprintf(frr_zclientpath, sizeof(frr_zclientpath), + ZEBRA_SERV_PATH, "/", di->pathspace); + snprintf(frr_vtydir, sizeof(frr_vtydir), DAEMON_VTY_DIR, "/", + di->pathspace); + snprintf(pidfile_default, sizeof(pidfile_default), "%s/%s.pid", + frr_vtydir, di->name); break; #ifdef HAVE_SQLITE3 case OPTION_DB_FILE: @@ -415,14 +465,18 @@ static int frr_opt(int opt) case 'C': if (di->flags & FRR_NO_CFG_PID_DRY) return 1; - di->dryrun = 1; + di->dryrun = true; break; case 't': if (di->flags & FRR_NO_CFG_PID_DRY) return 1; - di->terminal = 1; + di->terminal = true; break; case 'z': + di->zpathspace = true; + if (di->pathspace) + fprintf(stderr, + "-z option overrides -N option for zebra named socket path\n"); if (di->flags & FRR_NO_ZCLIENT) return 1; strlcpy(frr_zclientpath, optarg, sizeof(frr_zclientpath)); @@ -495,6 +549,9 @@ static int frr_opt(int opt) case OPTION_LOGLEVEL: di->early_loglevel = optarg; break; + case OPTION_LOGGING: + di->log_always = true; + break; default: return 1; } @@ -571,12 +628,14 @@ struct thread_master *frr_init(void) { struct option_chain *oc; struct frrmod_runtime *module; + struct zprivs_ids_t ids; char moderr[256]; char p_instance[16] = "", p_pathspace[256] = ""; const char *dir; dir = di->module_path ? di->module_path : frr_moduledir; srandom(time(NULL)); + frr_defaults_apply(); if (di->instance) { snprintf(frr_protonameinst, sizeof(frr_protonameinst), "%s[%u]", @@ -589,17 +648,18 @@ struct thread_master *frr_init(void) snprintf(config_default, sizeof(config_default), "%s%s%s%s.conf", frr_sysconfdir, p_pathspace, di->name, p_instance); - snprintf(pidfile_default, sizeof(pidfile_default), "%s/%s%s%s.pid", - frr_vtydir, p_pathspace, di->name, p_instance); + snprintf(pidfile_default, sizeof(pidfile_default), "%s/%s%s.pid", + frr_vtydir, di->name, p_instance); #ifdef HAVE_SQLITE3 snprintf(dbfile_default, sizeof(dbfile_default), "%s/%s%s%s.db", frr_dbdir, p_pathspace, di->name, p_instance); #endif zprivs_preinit(di->privs); + zprivs_get_ids(&ids); - openzlog(di->progname, di->logname, di->instance, - LOG_CONS | LOG_NDELAY | LOG_PID, LOG_DAEMON); + zlog_init(di->progname, di->logname, di->instance, + ids.uid_normal, ids.gid_normal); command_setup_early_logging(di->early_logging, di->early_loglevel); @@ -647,14 +707,20 @@ struct thread_master *frr_init(void) else cmd_init(1); - vty_init(master); - memory_init(); + vty_init(master, di->log_always); + lib_cmd_init(); + + frr_pthread_init(); log_ref_init(); + log_ref_vty_init(); lib_error_init(); - yang_init(); - nb_init(master, di->yang_modules, di->n_yang_modules); + yang_init(true); + + debug_init_cli(); + + nb_init(master, di->yang_modules, di->n_yang_modules, true); if (nb_db_init() != NB_OK) flog_warn(EC_LIB_NB_DATABASE, "%s: failed to initialize northbound database", @@ -815,22 +881,40 @@ static void frr_daemonize(void) */ static int frr_config_read_in(struct thread *t) { - if (!vty_read_config(NULL, di->config_file, config_default) && - di->backup_config_file) { + if (!vty_read_config(vty_shared_candidate_config, di->config_file, + config_default) + && di->backup_config_file) { char *orig = XSTRDUP(MTYPE_TMP, host_config_get()); zlog_info("Attempting to read backup config file: %s specified", di->backup_config_file); - vty_read_config(NULL, di->backup_config_file, config_default); + vty_read_config(vty_shared_candidate_config, + di->backup_config_file, config_default); host_config_set(orig); XFREE(MTYPE_TMP, orig); } /* - * Update the shared candidate after reading the startup configuration. + * Automatically commit the candidate configuration after + * reading the configuration file. */ - nb_config_replace(vty_shared_candidate_config, running_config, true); + if (frr_get_cli_mode() == FRR_CLI_TRANSACTIONAL) { + struct nb_context context = {}; + char errmsg[BUFSIZ] = {0}; + int ret; + + context.client = NB_CLIENT_CLI; + ret = nb_candidate_commit(&context, vty_shared_candidate_config, + true, "Read configuration file", NULL, + errmsg, sizeof(errmsg)); + if (ret != NB_OK && ret != NB_ERR_NO_CHANGES) + zlog_err( + "%s: failed to read configuration file: %s (%s)", + __func__, nb_err_name(ret), errmsg); + } + + hook_call(frr_very_late_init, master); return 0; } @@ -856,6 +940,7 @@ void frr_config_fork(void) if (!di->pid_file) di->pid_file = pidfile_default; pid_output(di->pid_file); + zlog_tls_buffer_init(); } static void frr_vty_serv(void) @@ -866,9 +951,7 @@ static void frr_vty_serv(void) const char *dir; char defvtydir[256]; - snprintf(defvtydir, sizeof(defvtydir), "%s%s%s", frr_vtydir, - di->pathspace ? "/" : "", - di->pathspace ? di->pathspace : ""); + snprintf(defvtydir, sizeof(defvtydir), "%s", frr_vtydir); dir = di->vty_sock_path ? di->vty_sock_path : defvtydir; @@ -1009,7 +1092,7 @@ void frr_run(struct thread_master *master) } /* end fixed stderr startup logging */ - zlog_startup_stderr = false; + zlog_startup_end(); struct thread thread; while (thread_fetch(master, &thread)) @@ -1029,7 +1112,6 @@ void frr_fini(void) hook_call(frr_fini); - /* memory_init -> nothing needed */ vty_terminate(); cmd_terminate(); nb_terminate(); @@ -1038,12 +1120,15 @@ void frr_fini(void) db_close(); #endif log_ref_fini(); + frr_pthread_finish(); zprivs_terminate(di->privs); /* signal_init -> nothing needed */ thread_master_free(master); master = NULL; - closezlog(); + zlog_tls_buffer_fini(); + zlog_fini(); /* frrmod_init -> nothing needed / hooks */ + rcu_shutdown(); if (!debug_memstats_at_exit) return; diff --git a/lib/libfrr.h b/lib/libfrr.h index 891e2c1282..ab72299206 100644 --- a/lib/libfrr.h +++ b/lib/libfrr.h @@ -81,7 +81,10 @@ struct frr_daemon_info { #endif const char *vty_path; const char *module_path; + const char *pathspace; + bool zpathspace; + const char *early_logging; const char *early_loglevel; @@ -95,8 +98,10 @@ struct frr_daemon_info { struct zebra_privs_t *privs; - const struct frr_yang_module_info **yang_modules; + const struct frr_yang_module_info *const *yang_modules; size_t n_yang_modules; + + bool log_always; }; /* execname is the daemon's executable (and pidfile and configfile) name, @@ -118,17 +123,20 @@ struct frr_daemon_info { .version = FRR_VERSION, ) \ /* end */ +extern void frr_init_vtydir(void); extern void frr_preinit(struct frr_daemon_info *daemon, int argc, char **argv); extern void frr_opt_add(const char *optstr, const struct option *longopts, const char *helpstr); extern int frr_getopt(int argc, char *const argv[], int *longindex); -extern void frr_help_exit(int status); + +extern __attribute__((__noreturn__)) void frr_help_exit(int status); extern struct thread_master *frr_init(void); extern const char *frr_get_progname(void); extern enum frr_cli_mode frr_get_cli_mode(void); DECLARE_HOOK(frr_late_init, (struct thread_master * tm), (tm)) +DECLARE_HOOK(frr_very_late_init, (struct thread_master * tm), (tm)) extern void frr_config_fork(void); extern void frr_run(struct thread_master *master); @@ -148,7 +156,7 @@ extern void frr_fini(void); extern char config_default[512]; extern char frr_zclientpath[256]; extern const char frr_sysconfdir[]; -extern const char frr_vtydir[]; +extern char frr_vtydir[256]; extern const char frr_moduledir[]; extern char frr_protoname[]; diff --git a/lib/linklist.c b/lib/linklist.c index 40c4b27169..84dc6e1419 100644 --- a/lib/linklist.c +++ b/lib/linklist.c @@ -38,16 +38,30 @@ static void list_free_internal(struct list *l) XFREE(MTYPE_LINK_LIST, l); } + /* Allocate new listnode. Internal use only. */ -static struct listnode *listnode_new(void) +static struct listnode *listnode_new(struct list *list, void *val) { - return XCALLOC(MTYPE_LINK_NODE, sizeof(struct listnode)); + struct listnode *node; + + /* if listnode memory is managed by the app then the val + * passed in is the listnode + */ + if (list->flags & LINKLIST_FLAG_NODE_MEM_BY_APP) { + node = val; + node->prev = node->next = NULL; + } else { + node = XCALLOC(MTYPE_LINK_NODE, sizeof(struct listnode)); + node->data = val; + } + return node; } /* Free listnode. */ -static void listnode_free(struct listnode *node) +static void listnode_free(struct list *list, struct listnode *node) { - XFREE(MTYPE_LINK_NODE, node); + if (!(list->flags & LINKLIST_FLAG_NODE_MEM_BY_APP)) + XFREE(MTYPE_LINK_NODE, node); } struct listnode *listnode_add(struct list *list, void *val) @@ -56,10 +70,9 @@ struct listnode *listnode_add(struct list *list, void *val) assert(val != NULL); - node = listnode_new(); + node = listnode_new(list, val); node->prev = list->tail; - node->data = val; if (list->head == NULL) list->head = node; @@ -78,10 +91,9 @@ void listnode_add_head(struct list *list, void *val) assert(val != NULL); - node = listnode_new(); + node = listnode_new(list, val); node->next = list->head; - node->data = val; if (list->head == NULL) list->head = node; @@ -92,6 +104,69 @@ void listnode_add_head(struct list *list, void *val) list->count++; } +bool listnode_add_sort_nodup(struct list *list, void *val) +{ + struct listnode *n; + struct listnode *new; + int ret; + void *data; + + assert(val != NULL); + + if (list->flags & LINKLIST_FLAG_NODE_MEM_BY_APP) { + n = val; + data = n->data; + } else { + data = val; + } + + if (list->cmp) { + for (n = list->head; n; n = n->next) { + ret = (*list->cmp)(data, n->data); + if (ret < 0) { + new = listnode_new(list, val); + + new->next = n; + new->prev = n->prev; + + if (n->prev) + n->prev->next = new; + else + list->head = new; + n->prev = new; + list->count++; + return true; + } + /* found duplicate return false */ + if (ret == 0) + return false; + } + } + + new = listnode_new(list, val); + + LISTNODE_ATTACH(list, new); + + return true; +} + +struct list *list_dup(struct list *list) +{ + struct list *dup; + struct listnode *node; + void *data; + + assert(list); + + dup = list_new(); + dup->cmp = list->cmp; + dup->del = list->del; + for (ALL_LIST_ELEMENTS_RO(list, node, data)) + listnode_add(dup, data); + + return dup; +} + void listnode_add_sort(struct list *list, void *val) { struct listnode *n; @@ -99,8 +174,8 @@ void listnode_add_sort(struct list *list, void *val) assert(val != NULL); - new = listnode_new(); - new->data = val; + new = listnode_new(list, val); + val = new->data; if (list->cmp) { for (n = list->head; n; n = n->next) { @@ -137,8 +212,7 @@ struct listnode *listnode_add_after(struct list *list, struct listnode *pp, assert(val != NULL); - nn = listnode_new(); - nn->data = val; + nn = listnode_new(list, val); if (pp == NULL) { if (list->head) @@ -172,8 +246,7 @@ struct listnode *listnode_add_before(struct list *list, struct listnode *pp, assert(val != NULL); - nn = listnode_new(); - nn->data = val; + nn = listnode_new(list, val); if (pp == NULL) { if (list->tail) @@ -206,7 +279,7 @@ void listnode_move_to_tail(struct list *l, struct listnode *n) LISTNODE_ATTACH(l, n); } -void listnode_delete(struct list *list, void *val) +void listnode_delete(struct list *list, const void *val) { struct listnode *node = listnode_lookup(list, val); @@ -236,12 +309,29 @@ void list_delete_all_node(struct list *list) next = node->next; if (*list->del) (*list->del)(node->data); - listnode_free(node); + listnode_free(list, node); } list->head = list->tail = NULL; list->count = 0; } +void list_filter_out_nodes(struct list *list, bool (*cond)(void *data)) +{ + struct listnode *node; + struct listnode *next; + void *data; + + assert(list); + + for (ALL_LIST_ELEMENTS(list, node, next, data)) { + if ((cond && cond(data)) || (!cond)) { + if (*list->del) + (*list->del)(data); + list_delete_node(list, node); + } + } +} + void list_delete(struct list **list) { assert(*list); @@ -250,7 +340,7 @@ void list_delete(struct list **list) *list = NULL; } -struct listnode *listnode_lookup(struct list *list, void *data) +struct listnode *listnode_lookup(struct list *list, const void *data) { struct listnode *node; @@ -279,30 +369,7 @@ void list_delete_node(struct list *list, struct listnode *node) else list->tail = node->prev; list->count--; - listnode_free(node); -} - -void list_add_list(struct list *list, struct list *add) -{ - struct listnode *n; - - for (n = listhead(add); n; n = listnextnode(n)) - listnode_add(list, n->data); -} - -struct list *list_dup(struct list *list) -{ - struct list *new = list_new(); - struct listnode *ln; - void *data; - - new->cmp = list->cmp; - new->del = list->del; - - for (ALL_LIST_ELEMENTS_RO(list, ln, data)) - listnode_add(new, data); - - return new; + listnode_free(list, node); } void list_sort(struct list *list, int (*cmp)(const void **, const void **)) @@ -334,3 +401,18 @@ struct listnode *listnode_add_force(struct list **list, void *val) *list = list_new(); return listnode_add(*list, val); } + +void **list_to_array(struct list *list, void **arr, size_t arrlen) +{ + struct listnode *ln; + void *vp; + size_t idx = 0; + + for (ALL_LIST_ELEMENTS_RO(list, ln, vp)) { + arr[idx++] = vp; + if (idx == arrlen) + break; + } + + return arr; +} diff --git a/lib/linklist.h b/lib/linklist.h index c30d8d314a..d8820c924d 100644 --- a/lib/linklist.h +++ b/lib/linklist.h @@ -43,6 +43,12 @@ struct list { /* invariant: count is the number of listnodes in the list */ unsigned int count; + uint8_t flags; +/* Indicates that listnode memory is managed by the application and + * doesn't need to be freed by this library via listnode_delete etc. + */ +#define LINKLIST_FLAG_NODE_MEM_BY_APP (1 << 0) + /* * Returns -1 if val1 < val2, 0 if equal?, 1 if val1 > val2. * Used as definition of sorted for listnode_add_sort @@ -60,10 +66,14 @@ struct list { #define listhead(X) ((X) ? ((X)->head) : NULL) #define listhead_unchecked(X) ((X)->head) #define listtail(X) ((X) ? ((X)->tail) : NULL) +#define listtail_unchecked(X) ((X)->tail) #define listcount(X) ((X)->count) #define list_isempty(X) ((X)->head == NULL && (X)->tail == NULL) /* return X->data only if X and X->data are not NULL */ #define listgetdata(X) (assert(X), assert((X)->data != NULL), (X)->data) +/* App is going to manage listnode memory */ +#define listset_app_node_mem(X) ((X)->flags |= LINKLIST_FLAG_NODE_MEM_BY_APP) +#define listnode_init(X, val) ((X)->data = (val)) /* * Create a new linked list. @@ -95,7 +105,7 @@ extern struct listnode *listnode_add(struct list *list, void *data); * list to operate on * * data - * element to add + * If MEM_BY_APP is set this is listnode. Otherwise it is element to add. */ extern void listnode_add_head(struct list *list, void *data); @@ -112,7 +122,7 @@ extern void listnode_add_head(struct list *list, void *data); * list to operate on * * val - * element to add + * If MEM_BY_APP is set this is listnode. Otherwise it is element to add. */ extern void listnode_add_sort(struct list *list, void *val); @@ -128,7 +138,7 @@ extern void listnode_add_sort(struct list *list, void *val); * listnode to insert after * * data - * data to insert + * If MEM_BY_APP is set this is listnode. Otherwise it is element to add. * * Returns: * pointer to newly created listnode that contains the inserted data @@ -148,7 +158,7 @@ extern struct listnode *listnode_add_after(struct list *list, * listnode to insert before * * data - * data to insert + * If MEM_BY_APP is set this is listnode. Otherwise it is element to add. * * Returns: * pointer to newly created listnode that contains the inserted data @@ -180,7 +190,7 @@ extern void listnode_move_to_tail(struct list *list, struct listnode *node); * data * data to insert into list */ -extern void listnode_delete(struct list *list, void *data); +extern void listnode_delete(struct list *list, const void *data); /* * Find the listnode corresponding to an element in a list. @@ -194,7 +204,7 @@ extern void listnode_delete(struct list *list, void *data); * Returns: * pointer to listnode storing the given data if found, NULL otherwise */ -extern struct listnode *listnode_lookup(struct list *list, void *data); +extern struct listnode *listnode_lookup(struct list *list, const void *data); /* * Retrieve the element at the head of a list. @@ -207,17 +217,6 @@ extern struct listnode *listnode_lookup(struct list *list, void *data); */ extern void *listnode_head(struct list *list); -/* - * Duplicate a list. - * - * list - * list to duplicate - * - * Returns: - * copy of the list - */ -extern struct list *list_dup(struct list *l); - /* * Sort a list in place. * @@ -239,6 +238,26 @@ extern struct list *list_dup(struct list *l); extern void list_sort(struct list *list, int (*cmp)(const void **, const void **)); +/* + * Convert a list to an array of void pointers. + * + * Starts from the list head and ends either on the last node of the list or + * when the provided array cannot store any more elements. + * + * list + * list to convert + * + * arr + * Pre-allocated array of void * + * + * arrlen + * Number of elements in arr + * + * Returns: + * arr + */ +void **list_to_array(struct list *list, void **arr, size_t arrlen); + /* * Delete a list and NULL its pointer. * @@ -276,17 +295,50 @@ extern void list_delete_all_node(struct list *list); extern void list_delete_node(struct list *list, struct listnode *node); /* - * Append a list to an existing list. + * Delete all nodes which satisfy a condition from a list. + * Deletes the node if cond function returns true for the node. + * If function ptr passed is NULL, it deletes all nodes * - * Runtime is O(N) where N = listcount(add). + * list + * list to operate on + * cond + * function pointer which takes node data as input and return true or false + */ + +extern void list_filter_out_nodes(struct list *list, bool (*cond)(void *data)); + +/* + * Insert a new element into a list with insertion sort if there is no + * duplicate element present in the list. This assumes the input list is + * sorted. If unsorted, it will check for duplicate until it finds out + * the position to do insertion sort with the unsorted list. + * + * If list->cmp is set, this function is used to determine the position to + * insert the new element. If it is not set, this function is equivalent to + * listnode_add. duplicate element is determined by cmp function returning 0. + * + * Runtime is O(N). * * list - * list to append to + * list to operate on * - * add - * list to append + * val + * If MEM_BY_APP is set this is listnode. Otherwise it is element to add. + */ + +extern bool listnode_add_sort_nodup(struct list *list, void *val); + +/* + * Duplicate the specified list, creating a shallow copy of each of its + * elements. + * + * list + * list to duplicate + * + * Returns: + * the duplicated list */ -extern void list_add_list(struct list *list, struct list *add); +extern struct list *list_dup(struct list *list); /* List iteration macro. * Usage: for (ALL_LIST_ELEMENTS (...) { ... } diff --git a/lib/log.c b/lib/log.c index e64c00186b..0dace07ab7 100644 --- a/lib/log.c +++ b/lib/log.c @@ -25,19 +25,12 @@ #include "zclient.h" #include "log.h" -#include "log_int.h" #include "memory.h" #include "command.h" #include "lib_errors.h" #include "lib/hook.h" - -#ifndef SUNOS_5 -#include -#endif -/* for printstack on solaris */ -#ifdef HAVE_UCONTEXT_H -#include -#endif +#include "printfrr.h" +#include "frr_pthread.h" #ifdef HAVE_LIBUNWIND #define UNW_LOCAL_ONLY @@ -45,55 +38,6 @@ #include #endif -DEFINE_MTYPE_STATIC(LIB, ZLOG, "Logging") - -/* hook for external logging */ -DEFINE_HOOK(zebra_ext_log, (int priority, const char *format, va_list args), - (priority, format, args)); - -static int logfile_fd = -1; /* Used in signal handler. */ - -struct zlog *zlog_default = NULL; -bool zlog_startup_stderr = true; - -/* lock protecting zlog_default for mt-safe zlog */ -static pthread_mutex_t loglock = PTHREAD_MUTEX_INITIALIZER; - -const char *zlog_priority[] = { - "emergencies", "alerts", "critical", "errors", "warnings", - "notifications", "informational", "debugging", NULL, -}; - -/* - * write_wrapper - * - * glibc has declared that the return value from write *must* not be - * ignored. - * gcc see's this problem and issues a warning for the line. - * - * Why is this a big deal you say? Because both of them are right - * and if you have -Werror enabled then all calls to write - * generate a build error and the build stops. - * - * clang has helpfully allowed this construct: - * (void)write(...) - * to tell the compiler yeah I know it has a return value - * I don't care about it at this time. - * gcc doesn't have this ability. - * - * This code was written such that it didn't care about the - * return value from write. At this time do I want - * to go through and fix and test this code for correctness. - * So just wrapper the bad behavior and move on. - */ -static void write_wrapper(int fd, const void *buf, size_t count) -{ - if (write(fd, buf, count) <= 0) - return; - - return; -} - /** * Looks up a message in a message list by key. * @@ -133,11 +77,11 @@ size_t quagga_timestamp(int timestamp_precision, char *buf, size_t buflen) /* first, we update the cache if the time has changed */ if (cache.last != clock.tv_sec) { - struct tm *tm; + struct tm tm; cache.last = clock.tv_sec; - tm = localtime(&cache.last); + localtime_r(&cache.last, &tm); cache.len = strftime(cache.buf, sizeof(cache.buf), - "%Y/%m/%d %H:%M:%S", tm); + "%Y/%m/%d %H:%M:%S", &tm); } /* note: it's not worth caching the subsecond part, because chances are that back-to-back calls are not sufficiently close @@ -177,372 +121,50 @@ size_t quagga_timestamp(int timestamp_precision, char *buf, size_t buflen) return 0; } -/* Utility routine for current time printing. */ -static void time_print(FILE *fp, struct timestamp_control *ctl) -{ - if (!ctl->already_rendered) { - ctl->len = quagga_timestamp(ctl->precision, ctl->buf, - sizeof(ctl->buf)); - ctl->already_rendered = 1; - } - fprintf(fp, "%s ", ctl->buf); -} - - -static void vzlog_file(struct zlog *zl, struct timestamp_control *tsctl, - const char *proto_str, int record_priority, int priority, - FILE *fp, const char *format, va_list args) -{ - va_list ac; - - time_print(fp, tsctl); - if (record_priority) - fprintf(fp, "%s: ", zlog_priority[priority]); - - fprintf(fp, "%s", proto_str); - va_copy(ac, args); - vfprintf(fp, format, ac); - va_end(ac); - fprintf(fp, "\n"); - fflush(fp); -} - -/* va_list version of zlog. */ -void vzlog(int priority, const char *format, va_list args) -{ - pthread_mutex_lock(&loglock); - - char proto_str[32]; - int original_errno = errno; - struct timestamp_control tsctl; - tsctl.already_rendered = 0; - struct zlog *zl = zlog_default; - - /* call external hook */ - hook_call(zebra_ext_log, priority, format, args); - - /* When zlog_default is also NULL, use stderr for logging. */ - if (zl == NULL) { - tsctl.precision = 0; - time_print(stderr, &tsctl); - fprintf(stderr, "%s: ", "unknown"); - vfprintf(stderr, format, args); - fprintf(stderr, "\n"); - fflush(stderr); - - /* In this case we return at here. */ - errno = original_errno; - pthread_mutex_unlock(&loglock); - return; - } - tsctl.precision = zl->timestamp_precision; - - /* Syslog output */ - if (priority <= zl->maxlvl[ZLOG_DEST_SYSLOG]) { - va_list ac; - va_copy(ac, args); - vsyslog(priority | zlog_default->facility, format, ac); - va_end(ac); - } - - if (zl->instance) - sprintf(proto_str, "%s[%d]: ", zl->protoname, zl->instance); - else - sprintf(proto_str, "%s: ", zl->protoname); - - /* File output. */ - if ((priority <= zl->maxlvl[ZLOG_DEST_FILE]) && zl->fp) - vzlog_file(zl, &tsctl, proto_str, zl->record_priority, priority, - zl->fp, format, args); - - /* fixed-config logging to stderr while we're stating up & haven't - * daemonized / reached mainloop yet - * - * note the "else" on stdout output -- we don't want to print the same - * message to both stderr and stdout. */ - if (zlog_startup_stderr && priority <= LOG_WARNING) - vzlog_file(zl, &tsctl, proto_str, 1, priority, stderr, format, - args); - else if (priority <= zl->maxlvl[ZLOG_DEST_STDOUT]) - vzlog_file(zl, &tsctl, proto_str, zl->record_priority, priority, - stdout, format, args); - - /* Terminal monitor. */ - if (priority <= zl->maxlvl[ZLOG_DEST_MONITOR]) - vty_log((zl->record_priority ? zlog_priority[priority] : NULL), - proto_str, format, &tsctl, args); - - errno = original_errno; - pthread_mutex_unlock(&loglock); -} - -int vzlog_test(int priority) -{ - pthread_mutex_lock(&loglock); - - int ret = 0; - - struct zlog *zl = zlog_default; - - /* When zlog_default is also NULL, use stderr for logging. */ - if (zl == NULL) - ret = 1; - /* Syslog output */ - else if (priority <= zl->maxlvl[ZLOG_DEST_SYSLOG]) - ret = 1; - /* File output. */ - else if ((priority <= zl->maxlvl[ZLOG_DEST_FILE]) && zl->fp) - ret = 1; - /* stdout output. */ - else if (priority <= zl->maxlvl[ZLOG_DEST_STDOUT]) - ret = 1; - /* Terminal monitor. */ - else if (priority <= zl->maxlvl[ZLOG_DEST_MONITOR]) - ret = 1; - - pthread_mutex_unlock(&loglock); - - return ret; -} - -static char *str_append(char *dst, int len, const char *src) -{ - while ((len-- > 0) && *src) - *dst++ = *src++; - return dst; -} - -static char *num_append(char *s, int len, unsigned long x) -{ - char buf[30]; - char *t; - - if (!x) - return str_append(s, len, "0"); - *(t = &buf[sizeof(buf) - 1]) = '\0'; - while (x && (t > buf)) { - *--t = '0' + (x % 10); - x /= 10; - } - return str_append(s, len, t); -} - -#if defined(SA_SIGINFO) \ - || defined(HAVE_PRINTSTACK) \ - || defined(HAVE_GLIBC_BACKTRACE) -static char *hex_append(char *s, int len, unsigned long x) -{ - char buf[30]; - char *t; - - if (!x) - return str_append(s, len, "0"); - *(t = &buf[sizeof(buf) - 1]) = '\0'; - while (x && (t > buf)) { - unsigned int cc = (x % 16); - *--t = ((cc < 10) ? ('0' + cc) : ('a' + cc - 10)); - x /= 16; - } - return str_append(s, len, t); -} -#endif - -/* Needs to be enhanced to support Solaris. */ -static int syslog_connect(void) -{ -#ifdef SUNOS_5 - return -1; -#else - int fd; - char *s; - struct sockaddr_un addr; - - if ((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) - return -1; - addr.sun_family = AF_UNIX; -#ifdef _PATH_LOG -#define SYSLOG_SOCKET_PATH _PATH_LOG -#else -#define SYSLOG_SOCKET_PATH "/dev/log" -#endif - s = str_append(addr.sun_path, sizeof(addr.sun_path), - SYSLOG_SOCKET_PATH); -#undef SYSLOG_SOCKET_PATH - *s = '\0'; - if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { - close(fd); - return -1; - } - return fd; -#endif -} - -static void syslog_sigsafe(int priority, const char *msg, size_t msglen) -{ - static int syslog_fd = -1; - char buf[sizeof("<1234567890>ripngd[1234567890]: ") + msglen + 50]; - char *s; - - if ((syslog_fd < 0) && ((syslog_fd = syslog_connect()) < 0)) - return; - -#define LOC s,buf+sizeof(buf)-s - s = buf; - s = str_append(LOC, "<"); - s = num_append(LOC, priority); - s = str_append(LOC, ">"); - /* forget about the timestamp, too difficult in a signal handler */ - s = str_append(LOC, zlog_default->ident); - if (zlog_default->syslog_options & LOG_PID) { - s = str_append(LOC, "["); - s = num_append(LOC, getpid()); - s = str_append(LOC, "]"); - } - s = str_append(LOC, ": "); - s = str_append(LOC, msg); - write_wrapper(syslog_fd, buf, s - buf); -#undef LOC -} - -static int open_crashlog(void) -{ -#define CRASHLOG_PREFIX "/var/tmp/quagga." -#define CRASHLOG_SUFFIX "crashlog" - if (zlog_default && zlog_default->ident) { - /* Avoid strlen since it is not async-signal-safe. */ - const char *p; - size_t ilen; - - for (p = zlog_default->ident, ilen = 0; *p; p++) - ilen++; - { - char buf[sizeof(CRASHLOG_PREFIX) + ilen - + sizeof(CRASHLOG_SUFFIX) + 3]; - char *s = buf; -#define LOC s,buf+sizeof(buf)-s - s = str_append(LOC, CRASHLOG_PREFIX); - s = str_append(LOC, zlog_default->ident); - s = str_append(LOC, "."); - s = str_append(LOC, CRASHLOG_SUFFIX); -#undef LOC - *s = '\0'; - return open(buf, O_WRONLY | O_CREAT | O_EXCL, - LOGFILE_MASK); - } - } - return open(CRASHLOG_PREFIX CRASHLOG_SUFFIX, - O_WRONLY | O_CREAT | O_EXCL, LOGFILE_MASK); -#undef CRASHLOG_SUFFIX -#undef CRASHLOG_PREFIX -} +/* + * crash handling + * + * NB: only AS-Safe (async-signal) functions can be used here! + */ /* Note: the goal here is to use only async-signal-safe functions. */ -void zlog_signal(int signo, const char *action -#ifdef SA_SIGINFO - , - siginfo_t *siginfo, void *program_counter -#endif - ) +void zlog_signal(int signo, const char *action, void *siginfo_v, + void *program_counter) { + siginfo_t *siginfo = siginfo_v; time_t now; char buf[sizeof("DEFAULT: Received signal S at T (si_addr 0xP, PC 0xP); aborting...") + 100]; - char *s = buf; - char *msgstart = buf; -#define LOC s,buf+sizeof(buf)-s + struct fbuf fb = { .buf = buf, .pos = buf, .len = sizeof(buf) }; time(&now); - if (zlog_default) { - s = str_append(LOC, zlog_default->protoname); - *s++ = ':'; - *s++ = ' '; - msgstart = s; - } - s = str_append(LOC, "Received signal "); - s = num_append(LOC, signo); - s = str_append(LOC, " at "); - s = num_append(LOC, now); -#ifdef SA_SIGINFO - s = str_append(LOC, " (si_addr 0x"); - s = hex_append(LOC, (unsigned long)(siginfo->si_addr)); - if (program_counter) { - s = str_append(LOC, ", PC 0x"); - s = hex_append(LOC, (unsigned long)program_counter); - } - s = str_append(LOC, "); "); -#else /* SA_SIGINFO */ - s = str_append(LOC, "; "); -#endif /* SA_SIGINFO */ - s = str_append(LOC, action); - if (s < buf + sizeof(buf)) - *s++ = '\n'; - -/* N.B. implicit priority is most severe */ -#define PRI LOG_CRIT - -#define DUMP(FD) write_wrapper(FD, buf, s-buf); - /* If no file logging configured, try to write to fallback log file. */ - if ((logfile_fd >= 0) || ((logfile_fd = open_crashlog()) >= 0)) - DUMP(logfile_fd) - if (!zlog_default) - DUMP(STDERR_FILENO) - else { - if (PRI <= zlog_default->maxlvl[ZLOG_DEST_STDOUT]) - DUMP(STDOUT_FILENO) - /* Remove trailing '\n' for monitor and syslog */ - *--s = '\0'; - if (PRI <= zlog_default->maxlvl[ZLOG_DEST_MONITOR]) - vty_log_fixed(buf, s - buf); - if (PRI <= zlog_default->maxlvl[ZLOG_DEST_SYSLOG]) - syslog_sigsafe(PRI | zlog_default->facility, msgstart, - s - msgstart); - } -#undef DUMP - zlog_backtrace_sigsafe(PRI, -#ifdef SA_SIGINFO - program_counter -#else - NULL -#endif - ); + bprintfrr(&fb, "Received signal %d at %lld", signo, (long long)now); + if (program_counter) + bprintfrr(&fb, " (si_addr 0x%tx, PC 0x%tx)", + (ptrdiff_t)siginfo->si_addr, + (ptrdiff_t)program_counter); + else + bprintfrr(&fb, " (si_addr 0x%tx)", + (ptrdiff_t)siginfo->si_addr); + bprintfrr(&fb, "; %s\n", action); + + zlog_sigsafe(fb.buf, fb.pos - fb.buf); + + zlog_backtrace_sigsafe(LOG_CRIT, program_counter); + + fb.pos = buf; - s = buf; struct thread *tc; tc = pthread_getspecific(thread_current); - if (!tc) - s = str_append(LOC, "no thread information available\n"); - else { - s = str_append(LOC, "in thread "); - s = str_append(LOC, tc->funcname); - s = str_append(LOC, " scheduled from "); - s = str_append(LOC, tc->schedfrom); - s = str_append(LOC, ":"); - s = num_append(LOC, tc->schedfrom_line); - s = str_append(LOC, "\n"); - } -#define DUMP(FD) write_wrapper(FD, buf, s-buf); - /* If no file logging configured, try to write to fallback log file. */ - if (logfile_fd >= 0) - DUMP(logfile_fd) - if (!zlog_default) - DUMP(STDERR_FILENO) - else { - if (PRI <= zlog_default->maxlvl[ZLOG_DEST_STDOUT]) - DUMP(STDOUT_FILENO) - /* Remove trailing '\n' for monitor and syslog */ - *--s = '\0'; - if (PRI <= zlog_default->maxlvl[ZLOG_DEST_MONITOR]) - vty_log_fixed(buf, s - buf); - if (PRI <= zlog_default->maxlvl[ZLOG_DEST_SYSLOG]) - syslog_sigsafe(PRI | zlog_default->facility, msgstart, - s - msgstart); - } -#undef DUMP + if (!tc) + bprintfrr(&fb, "no thread information available\n"); + else + bprintfrr(&fb, "in thread %s scheduled from %s:%d\n", + tc->funcname, tc->schedfrom, tc->schedfrom_line); -#undef PRI -#undef LOC + zlog_sigsafe(fb.buf, fb.pos - fb.buf); } /* Log a backtrace using only async-signal-safe functions. @@ -550,12 +172,16 @@ void zlog_signal(int signo, const char *action void zlog_backtrace_sigsafe(int priority, void *program_counter) { #ifdef HAVE_LIBUNWIND - char buf[100]; + char buf[256]; + struct fbuf fb = { .buf = buf, .len = sizeof(buf) }; unw_cursor_t cursor; unw_context_t uc; unw_word_t ip, off, sp; Dl_info dlinfo; + memset(&uc, 0, sizeof(uc)); + memset(&cursor, 0, sizeof(cursor)); + unw_getcontext(&uc); unw_init_local(&cursor, &uc); while (unw_step(&cursor) > 0) { @@ -564,107 +190,47 @@ void zlog_backtrace_sigsafe(int priority, void *program_counter) unw_get_reg(&cursor, UNW_REG_IP, &ip); unw_get_reg(&cursor, UNW_REG_SP, &sp); - if (unw_is_signal_frame(&cursor)) - dprintf(2, " ---- signal ----\n"); - - if (!unw_get_proc_name(&cursor, buf, sizeof(buf), &off)) { - snprintf(name, sizeof(name), "%s+%#lx", - buf, (long)off); - } - dprintf(2, "%-30s %16lx %16lx", name, (long)ip, (long)sp); - if (dladdr((void *)ip, &dlinfo)) { - dprintf(2, " %s (mapped at %p)", - dlinfo.dli_fname, dlinfo.dli_fbase); - } - dprintf(2, "\n"); + if (!unw_get_proc_name(&cursor, buf, sizeof(buf), &off)) + snprintfrr(name, sizeof(name), "%s+%#lx", + buf, (long)off); + fb.pos = buf; + if (unw_is_signal_frame(&cursor)) + bprintfrr(&fb, " ---- signal ----\n"); + bprintfrr(&fb, "%-30s %16lx %16lx", name, (long)ip, (long)sp); + if (dladdr((void *)ip, &dlinfo)) + bprintfrr(&fb, " %s (mapped at %p)", + dlinfo.dli_fname, dlinfo.dli_fbase); + bprintfrr(&fb, "\n"); + zlog_sigsafe(fb.buf, fb.pos - fb.buf); } -#elif defined(HAVE_GLIBC_BACKTRACE) || defined(HAVE_PRINTSTACK) - static const char pclabel[] = "Program counter: "; +#elif defined(HAVE_GLIBC_BACKTRACE) void *array[64]; - int size; - char buf[100]; - char *s, **bt = NULL; -#define LOC s,buf+sizeof(buf)-s + int size, i; + char buf[128]; + struct fbuf fb = { .buf = buf, .pos = buf, .len = sizeof(buf) }; + char **bt = NULL; -#ifdef HAVE_GLIBC_BACKTRACE size = backtrace(array, array_size(array)); if (size <= 0 || (size_t)size > array_size(array)) return; -#define DUMP(FD) \ - { \ - if (program_counter) { \ - write_wrapper(FD, pclabel, sizeof(pclabel) - 1); \ - backtrace_symbols_fd(&program_counter, 1, FD); \ - } \ - write_wrapper(FD, buf, s - buf); \ - backtrace_symbols_fd(array, size, FD); \ - } -#elif defined(HAVE_PRINTSTACK) -#define DUMP(FD) \ - { \ - if (program_counter) \ - write_wrapper((FD), pclabel, sizeof(pclabel) - 1); \ - write_wrapper((FD), buf, s - buf); \ - printstack((FD)); \ - } -#endif /* HAVE_GLIBC_BACKTRACE, HAVE_PRINTSTACK */ - - s = buf; - s = str_append(LOC, "Backtrace for "); - s = num_append(LOC, size); - s = str_append(LOC, " stack frames:\n"); - - if ((logfile_fd >= 0) || ((logfile_fd = open_crashlog()) >= 0)) - DUMP(logfile_fd) - if (!zlog_default) - DUMP(STDERR_FILENO) - else { - if (priority <= zlog_default->maxlvl[ZLOG_DEST_STDOUT]) - DUMP(STDOUT_FILENO) - /* Remove trailing '\n' for monitor and syslog */ - *--s = '\0'; - if (priority <= zlog_default->maxlvl[ZLOG_DEST_MONITOR]) - vty_log_fixed(buf, s - buf); - if (priority <= zlog_default->maxlvl[ZLOG_DEST_SYSLOG]) - syslog_sigsafe(priority | zlog_default->facility, buf, - s - buf); - { - int i; -#ifdef HAVE_GLIBC_BACKTRACE - bt = backtrace_symbols(array, size); -#endif - /* Just print the function addresses. */ - for (i = 0; i < size; i++) { - s = buf; - if (bt) - s = str_append(LOC, bt[i]); - else { - s = str_append(LOC, "[bt "); - s = num_append(LOC, i); - s = str_append(LOC, "] 0x"); - s = hex_append( - LOC, (unsigned long)(array[i])); - } - *s = '\0'; - if (priority - <= zlog_default->maxlvl[ZLOG_DEST_MONITOR]) - vty_log_fixed(buf, s - buf); - if (priority - <= zlog_default->maxlvl[ZLOG_DEST_SYSLOG]) - syslog_sigsafe( - priority - | zlog_default - ->facility, - buf, s - buf); - } - if (bt) - free(bt); - } + bprintfrr(&fb, "Backtrace for %d stack frames:", size); + zlog_sigsafe(fb.pos, fb.buf - fb.pos); + + bt = backtrace_symbols(array, size); + + for (i = 0; i < size; i++) { + fb.pos = buf; + if (bt) + bprintfrr(&fb, "%s", bt[i]); + else + bprintfrr(&fb, "[bt %d] 0x%tx", i, + (ptrdiff_t)(array[i])); + zlog_sigsafe(fb.buf, fb.pos - fb.buf); } -#undef DUMP -#undef LOC + if (bt) + free(bt); #endif /* HAVE_STRACK_TRACE */ } @@ -710,8 +276,7 @@ void zlog_backtrace(int priority) if (size <= 0 || (size_t)size > array_size(array)) { flog_err_sys( EC_LIB_SYSTEM_CALL, - "Cannot get backtrace, returned invalid # of frames %d " - "(valid range is between 1 and %lu)", + "Cannot get backtrace, returned invalid # of frames %d (valid range is between 1 and %lu)", size, (unsigned long)(array_size(array))); return; } @@ -731,36 +296,6 @@ void zlog_backtrace(int priority) #endif } -void zlog(int priority, const char *format, ...) -{ - va_list args; - - va_start(args, format); - vzlog(priority, format, args); - va_end(args); -} - -#define ZLOG_FUNC(FUNCNAME, PRIORITY) \ - void FUNCNAME(const char *format, ...) \ - { \ - va_list args; \ - va_start(args, format); \ - vzlog(PRIORITY, format, args); \ - va_end(args); \ - } - -ZLOG_FUNC(zlog_err, LOG_ERR) - -ZLOG_FUNC(zlog_warn, LOG_WARNING) - -ZLOG_FUNC(zlog_info, LOG_INFO) - -ZLOG_FUNC(zlog_notice, LOG_NOTICE) - -ZLOG_FUNC(zlog_debug, LOG_DEBUG) - -#undef ZLOG_FUNC - void zlog_thread_info(int log_level) { struct thread *tc; @@ -768,8 +303,7 @@ void zlog_thread_info(int log_level) if (tc) zlog(log_level, - "Current thread function %s, scheduled from " - "file %s, line %u", + "Current thread function %s, scheduled from file %s, line %u", tc->funcname, tc->schedfrom, tc->schedfrom_line); else zlog(log_level, "Current thread not known/applicable"); @@ -778,11 +312,6 @@ void zlog_thread_info(int log_level) void _zlog_assert_failed(const char *assertion, const char *file, unsigned int line, const char *function) { - /* Force fallback file logging? */ - if (zlog_default && !zlog_default->fp - && ((logfile_fd = open_crashlog()) >= 0) - && ((zlog_default->fp = fdopen(logfile_fd, "w")) != NULL)) - zlog_default->maxlvl[ZLOG_DEST_FILE] = LOG_ERR; zlog(LOG_CRIT, "Assertion `%s' failed in file %s, line %u, function %s", assertion, file, line, (function ? function : "?")); zlog_backtrace(LOG_CRIT); @@ -793,177 +322,14 @@ void _zlog_assert_failed(const char *assertion, const char *file, void memory_oom(size_t size, const char *name) { - flog_err_sys(EC_LIB_SYSTEM_CALL, - "out of memory: failed to allocate %zu bytes for %s" - "object", - size, name); - zlog_backtrace(LOG_ERR); + zlog(LOG_CRIT, + "out of memory: failed to allocate %zu bytes for %s object", + size, name); + zlog_backtrace(LOG_CRIT); + log_memstats(stderr, "log"); abort(); } -/* Open log stream */ -void openzlog(const char *progname, const char *protoname, - unsigned short instance, int syslog_flags, int syslog_facility) -{ - struct zlog *zl; - unsigned int i; - - zl = XCALLOC(MTYPE_ZLOG, sizeof(struct zlog)); - - zl->ident = progname; - zl->protoname = protoname; - zl->instance = instance; - zl->facility = syslog_facility; - zl->syslog_options = syslog_flags; - - /* Set default logging levels. */ - for (i = 0; i < array_size(zl->maxlvl); i++) - zl->maxlvl[i] = ZLOG_DISABLED; - zl->maxlvl[ZLOG_DEST_MONITOR] = LOG_DEBUG; - zl->default_lvl = LOG_DEBUG; - - openlog(progname, syslog_flags, zl->facility); - - pthread_mutex_lock(&loglock); - zlog_default = zl; - pthread_mutex_unlock(&loglock); - -#ifdef HAVE_GLIBC_BACKTRACE - /* work around backtrace() using lazily resolved dynamically linked - * symbols, which will otherwise cause funny breakage in the SEGV - * handler. - * (particularly, the dynamic linker can call malloc(), which uses locks - * in programs linked with -pthread, thus can deadlock.) */ - void *bt[4]; - backtrace(bt, array_size(bt)); - free(backtrace_symbols(bt, 0)); - backtrace_symbols_fd(bt, 0, 0); -#endif -} - -void closezlog(void) -{ - pthread_mutex_lock(&loglock); - struct zlog *zl = zlog_default; - - closelog(); - - if (zl->fp != NULL) - fclose(zl->fp); - - XFREE(MTYPE_ZLOG, zl->filename); - - XFREE(MTYPE_ZLOG, zl); - zlog_default = NULL; - pthread_mutex_unlock(&loglock); -} - -/* Called from command.c. */ -void zlog_set_level(zlog_dest_t dest, int log_level) -{ - pthread_mutex_lock(&loglock); - zlog_default->maxlvl[dest] = log_level; - pthread_mutex_unlock(&loglock); -} - -int zlog_set_file(const char *filename, int log_level) -{ - struct zlog *zl; - FILE *fp; - mode_t oldumask; - int ret = 1; - - /* There is opend file. */ - zlog_reset_file(); - - /* Open file. */ - oldumask = umask(0777 & ~LOGFILE_MASK); - fp = fopen(filename, "a"); - umask(oldumask); - if (fp == NULL) { - ret = 0; - } else { - pthread_mutex_lock(&loglock); - zl = zlog_default; - - /* Set flags. */ - zl->filename = XSTRDUP(MTYPE_ZLOG, filename); - zl->maxlvl[ZLOG_DEST_FILE] = log_level; - zl->fp = fp; - logfile_fd = fileno(fp); - pthread_mutex_unlock(&loglock); - } - - return ret; -} - -/* Reset opend file. */ -int zlog_reset_file(void) -{ - pthread_mutex_lock(&loglock); - - struct zlog *zl = zlog_default; - - if (zl->fp) - fclose(zl->fp); - zl->fp = NULL; - logfile_fd = -1; - zl->maxlvl[ZLOG_DEST_FILE] = ZLOG_DISABLED; - - XFREE(MTYPE_ZLOG, zl->filename); - zl->filename = NULL; - - pthread_mutex_unlock(&loglock); - - return 1; -} - -/* Reopen log file. */ -int zlog_rotate(void) -{ - pthread_mutex_lock(&loglock); - - struct zlog *zl = zlog_default; - int level; - int ret = 1; - - if (zl->fp) - fclose(zl->fp); - zl->fp = NULL; - logfile_fd = -1; - level = zl->maxlvl[ZLOG_DEST_FILE]; - zl->maxlvl[ZLOG_DEST_FILE] = ZLOG_DISABLED; - - if (zl->filename) { - mode_t oldumask; - int save_errno; - - oldumask = umask(0777 & ~LOGFILE_MASK); - zl->fp = fopen(zl->filename, "a"); - save_errno = errno; - umask(oldumask); - if (zl->fp == NULL) { - - pthread_mutex_unlock(&loglock); - - flog_err_sys( - EC_LIB_SYSTEM_CALL, - "Log rotate failed: cannot open file %s for append: %s", - zl->filename, safe_strerror(save_errno)); - ret = -1; - - pthread_mutex_lock(&loglock); - } else { - logfile_fd = fileno(zl->fp); - zl->maxlvl[ZLOG_DEST_FILE] = level; - } - } - - pthread_mutex_unlock(&loglock); - - return ret; -} - /* Wrapper around strerror to handle case where it returns NULL. */ const char *safe_strerror(int errnum) { @@ -1020,6 +386,10 @@ static const struct zebra_desc_table command_types[] = { DESC_ENTRY(ZEBRA_INTERFACE_LINK_PARAMS), DESC_ENTRY(ZEBRA_MPLS_LABELS_ADD), DESC_ENTRY(ZEBRA_MPLS_LABELS_DELETE), + DESC_ENTRY(ZEBRA_MPLS_LABELS_REPLACE), + DESC_ENTRY(ZEBRA_SR_POLICY_SET), + DESC_ENTRY(ZEBRA_SR_POLICY_DELETE), + DESC_ENTRY(ZEBRA_SR_POLICY_NOTIFY_STATUS), DESC_ENTRY(ZEBRA_IPMR_ROUTE_STATS), DESC_ENTRY(ZEBRA_LABEL_MANAGER_CONNECT), DESC_ENTRY(ZEBRA_LABEL_MANAGER_CONNECT_ASYNC), @@ -1034,6 +404,10 @@ static const struct zebra_desc_table command_types[] = { DESC_ENTRY(ZEBRA_ADVERTISE_SUBNET), DESC_ENTRY(ZEBRA_LOCAL_ES_ADD), DESC_ENTRY(ZEBRA_LOCAL_ES_DEL), + DESC_ENTRY(ZEBRA_REMOTE_ES_VTEP_ADD), + DESC_ENTRY(ZEBRA_REMOTE_ES_VTEP_DEL), + DESC_ENTRY(ZEBRA_LOCAL_ES_EVI_ADD), + DESC_ENTRY(ZEBRA_LOCAL_ES_EVI_DEL), DESC_ENTRY(ZEBRA_VNI_ADD), DESC_ENTRY(ZEBRA_VNI_DEL), DESC_ENTRY(ZEBRA_L3VNI_ADD), @@ -1070,7 +444,18 @@ static const struct zebra_desc_table command_types[] = { DESC_ENTRY(ZEBRA_VXLAN_FLOOD_CONTROL), DESC_ENTRY(ZEBRA_VXLAN_SG_ADD), DESC_ENTRY(ZEBRA_VXLAN_SG_DEL), -}; + DESC_ENTRY(ZEBRA_VXLAN_SG_REPLAY), + DESC_ENTRY(ZEBRA_MLAG_PROCESS_UP), + DESC_ENTRY(ZEBRA_MLAG_PROCESS_DOWN), + DESC_ENTRY(ZEBRA_MLAG_CLIENT_REGISTER), + DESC_ENTRY(ZEBRA_MLAG_CLIENT_UNREGISTER), + DESC_ENTRY(ZEBRA_MLAG_FORWARD_MSG), + DESC_ENTRY(ZEBRA_ERROR), + DESC_ENTRY(ZEBRA_CLIENT_CAPABILITIES), + DESC_ENTRY(ZEBRA_OPAQUE_MESSAGE), + DESC_ENTRY(ZEBRA_OPAQUE_REGISTER), + DESC_ENTRY(ZEBRA_OPAQUE_UNREGISTER), + DESC_ENTRY(ZEBRA_NEIGH_DISCOVER)}; #undef DESC_ENTRY static const struct zebra_desc_table unknown = {0, "unknown", '?'}; @@ -1089,8 +474,7 @@ static const struct zebra_desc_table *zroute_lookup(unsigned int zroute) for (i = 0; i < array_size(route_types); i++) { if (zroute == route_types[i].type) { zlog_warn( - "internal error: route type table out of order " - "while searching for %u, please notify developers", + "internal error: route type table out of order while searching for %u, please notify developers", zroute); return &route_types[i]; } @@ -1200,60 +584,47 @@ int proto_redistnum(int afi, const char *s) return -1; } -void zlog_hexdump(const void *mem, unsigned int len) +void zlog_hexdump(const void *mem, size_t len) { - unsigned long i = 0; - unsigned int j = 0; - unsigned int columns = 8; - /* - * 19 bytes for 0xADDRESS: - * 24 bytes for data; 2 chars plus a space per data byte - * 1 byte for space - * 8 bytes for ASCII representation - * 1 byte for a newline - * ===================== - * 53 bytes per 8 bytes of data - * 1 byte for null term - */ - size_t bs = ((len / 8) + 1) * 53 + 1; - char buf[bs]; - char *s = buf; - - memset(buf, 0, sizeof(buf)); - - for (i = 0; i < len + ((len % columns) ? (columns - len % columns) : 0); - i++) { - /* print offset */ - if (i % columns == 0) - s += snprintf(s, bs - (s - buf), - "0x%016lx: ", (unsigned long)mem + i); - - /* print hex data */ - if (i < len) - s += snprintf(s, bs - (s - buf), "%02x ", - 0xFF & ((const char *)mem)[i]); - - /* end of block, just aligning for ASCII dump */ - else - s += snprintf(s, bs - (s - buf), " "); - - /* print ASCII dump */ - if (i % columns == (columns - 1)) { - for (j = i - (columns - 1); j <= i; j++) { - /* end of block not really printing */ - if (j >= len) - s += snprintf(s, bs - (s - buf), " "); - else if (isprint((int)((const char *)mem)[j])) - s += snprintf( - s, bs - (s - buf), "%c", - 0xFF & ((const char *)mem)[j]); - else /* other char */ - s += snprintf(s, bs - (s - buf), "."); - } - s += snprintf(s, bs - (s - buf), "\n"); + char line[64]; + const uint8_t *src = mem; + const uint8_t *end = src + len; + + if (len == 0) { + zlog_debug("%016lx: (zero length / no data)", (long)src); + return; + } + + while (src < end) { + struct fbuf fb = { + .buf = line, + .pos = line, + .len = sizeof(line), + }; + const uint8_t *lineend = src + 8; + unsigned line_bytes = 0; + + bprintfrr(&fb, "%016lx: ", (long)src); + + while (src < lineend && src < end) { + bprintfrr(&fb, "%02x ", *src++); + line_bytes++; } + if (line_bytes < 8) + bprintfrr(&fb, "%*s", (8 - line_bytes) * 3, ""); + + src -= line_bytes; + while (src < lineend && src < end && fb.pos < fb.buf + fb.len) { + uint8_t byte = *src++; + + if (isprint(byte)) + *fb.pos++ = byte; + else + *fb.pos++ = '.'; + } + + zlog_debug("%.*s", (int)(fb.pos - fb.buf), fb.buf); } - zlog_debug("\n%s", buf); } const char *zlog_sanitize(char *buf, size_t bufsz, const void *in, size_t inlen) diff --git a/lib/log.h b/lib/log.h index 9368bf9e82..3d2f0ed829 100644 --- a/lib/log.h +++ b/lib/log.h @@ -22,21 +22,22 @@ #ifndef _ZEBRA_LOG_H #define _ZEBRA_LOG_H +#include "zassert.h" + #include #include #include #include #include + #include "lib/hook.h" +#include "lib/zlog.h" +#include "lib/zlog_targets.h" #ifdef __cplusplus extern "C" { #endif -/* Hook for external logging function */ -DECLARE_HOOK(zebra_ext_log, (int priority, const char *format, va_list args), - (priority, format, args)); - /* Here is some guidance on logging levels to use: * * LOG_DEBUG - For all messages that are enabled by optional debugging @@ -53,19 +54,7 @@ DECLARE_HOOK(zebra_ext_log, (int priority, const char *format, va_list args), * please use LOG_ERR instead. */ -/* If maxlvl is set to ZLOG_DISABLED, then no messages will be sent - to that logging destination. */ -#define ZLOG_DISABLED (LOG_EMERG-1) - -typedef enum { - ZLOG_DEST_SYSLOG = 0, - ZLOG_DEST_STDOUT, - ZLOG_DEST_MONITOR, - ZLOG_DEST_FILE -} zlog_dest_t; -#define ZLOG_NUM_DESTS (ZLOG_DEST_FILE+1) - -extern bool zlog_startup_stderr; +extern void zlog_rotate(void); /* Message structure. */ struct message { @@ -73,54 +62,33 @@ struct message { const char *str; }; -/* Open zlog function */ -extern void openzlog(const char *progname, const char *protoname, - uint16_t instance, int syslog_options, - int syslog_facility); - -/* Close zlog function. */ -extern void closezlog(void); - -/* GCC have printf type attribute check. */ -#ifdef __GNUC__ -#define PRINTF_ATTRIBUTE(a,b) __attribute__ ((__format__ (__printf__, a, b))) -#else -#define PRINTF_ATTRIBUTE(a,b) -#endif /* __GNUC__ */ - -/* Handy zlog functions. */ -extern void zlog_err(const char *format, ...) PRINTF_ATTRIBUTE(1, 2); -extern void zlog_warn(const char *format, ...) PRINTF_ATTRIBUTE(1, 2); -extern void zlog_info(const char *format, ...) PRINTF_ATTRIBUTE(1, 2); -extern void zlog_notice(const char *format, ...) PRINTF_ATTRIBUTE(1, 2); -extern void zlog_debug(const char *format, ...) PRINTF_ATTRIBUTE(1, 2); -extern void zlog(int priority, const char *format, ...) PRINTF_ATTRIBUTE(2, 3); - /* For logs which have error codes associated with them */ #define flog_err(ferr_id, format, ...) \ - zlog_err("[EC %" PRIu32 "] " format, ferr_id, ##__VA_ARGS__) + zlog_err("[EC %u] " format, ferr_id, ##__VA_ARGS__) #define flog_err_sys(ferr_id, format, ...) \ flog_err(ferr_id, format, ##__VA_ARGS__) #define flog_warn(ferr_id, format, ...) \ - zlog_warn("[EC %" PRIu32 "] " format, ferr_id, ##__VA_ARGS__) + zlog_warn("[EC %u] " format, ferr_id, ##__VA_ARGS__) #define flog(priority, ferr_id, format, ...) \ - zlog(priority, "[EC %" PRIu32 "] " format, ferr_id, ##__VA_ARGS__) + zlog(priority, "[EC %u] " format, ferr_id, ##__VA_ARGS__) extern void zlog_thread_info(int log_level); -/* Set logging level for the given destination. If the log_level - argument is ZLOG_DISABLED, then the destination is disabled. - This function should not be used for file logging (use zlog_set_file - or zlog_reset_file instead). */ -extern void zlog_set_level(zlog_dest_t, int log_level); +#define ZLOG_FILTERS_MAX 100 /* Max # of filters at once */ +#define ZLOG_FILTER_LENGTH_MAX 80 /* 80 character filter limit */ + +struct zlog_cfg_filterfile { + struct zlog_cfg_file parent; +}; -/* Set logging to the given filename at the specified level. */ -extern int zlog_set_file(const char *filename, int log_level); -/* Disable file logging. */ -extern int zlog_reset_file(void); +extern void zlog_filterfile_init(struct zlog_cfg_filterfile *zcf); +extern void zlog_filterfile_fini(struct zlog_cfg_filterfile *zcf); -/* Rotate log. */ -extern int zlog_rotate(void); +/* Add/Del/Dump log filters */ +extern void zlog_filter_clear(void); +extern int zlog_filter_add(const char *filter); +extern int zlog_filter_del(const char *filter); +extern int zlog_filter_dump(char *buf, size_t max_size); const char *lookup_msg(const struct message *mz, int kz, const char *nf); @@ -128,12 +96,8 @@ const char *lookup_msg(const struct message *mz, int kz, const char *nf); extern const char *safe_strerror(int errnum); /* To be called when a fatal signal is caught. */ -extern void zlog_signal(int signo, const char *action -#ifdef SA_SIGINFO - , - siginfo_t *siginfo, void *program_counter -#endif - ); +extern void zlog_signal(int signo, const char *action, void *siginfo, + void *program_counter); /* Log a backtrace. */ extern void zlog_backtrace(int priority); @@ -154,12 +118,29 @@ extern void zlog_backtrace_sigsafe(int priority, void *program_counter); extern size_t quagga_timestamp(int timestamp_precision /* # subsecond digits */, char *buf, size_t buflen); -extern void zlog_hexdump(const void *mem, unsigned int len); +extern void zlog_hexdump(const void *mem, size_t len); extern const char *zlog_sanitize(char *buf, size_t bufsz, const void *in, size_t inlen); +/* Note: whenever a new route-type or zserv-command is added the + * corresponding {command,route}_types[] table in lib/log.c MUST be + * updated! */ + +/* Map a route type to a string. For example, ZEBRA_ROUTE_RIPNG -> "ripng". */ +extern const char *zebra_route_string(unsigned int route_type); +/* Map a route type to a char. For example, ZEBRA_ROUTE_RIPNG -> 'R'. */ +extern char zebra_route_char(unsigned int route_type); +/* Map a zserv command type to the same string, + * e.g. ZEBRA_INTERFACE_ADD -> "ZEBRA_INTERFACE_ADD" */ +/* Map a protocol name to its number. e.g. ZEBRA_ROUTE_BGP->9*/ +extern int proto_name2num(const char *s); +/* Map redistribute X argument to protocol number. + * unlike proto_name2num, this accepts shorthands and takes + * an AFI value to restrict input */ +extern int proto_redistnum(int afi, const char *s); + +extern const char *zserv_command_string(unsigned int command); -extern int vzlog_test(int priority); /* structure useful for avoiding repeated rendering of the same timestamp */ struct timestamp_control { diff --git a/lib/log_filter.c b/lib/log_filter.c new file mode 100644 index 0000000000..721e57a628 --- /dev/null +++ b/lib/log_filter.c @@ -0,0 +1,156 @@ +/* + * Logging - Filtered file log target + * Copyright (C) 2019 Cumulus Networks, Inc. + * Stephen Worley + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "frr_pthread.h" +#include "log.h" + +static pthread_mutex_t logfilterlock = PTHREAD_MUTEX_INITIALIZER; +static char zlog_filters[ZLOG_FILTERS_MAX][ZLOG_FILTER_LENGTH_MAX + 1]; +static uint8_t zlog_filter_count; + +/* + * look for a match on the filter in the current filters, + * logfilterlock must be held + */ +static int zlog_filter_lookup(const char *lookup) +{ + for (int i = 0; i < zlog_filter_count; i++) { + if (strncmp(lookup, zlog_filters[i], sizeof(zlog_filters[0])) + == 0) + return i; + } + return -1; +} + +void zlog_filter_clear(void) +{ + frr_with_mutex(&logfilterlock) { + zlog_filter_count = 0; + } +} + +int zlog_filter_add(const char *filter) +{ + frr_with_mutex(&logfilterlock) { + if (zlog_filter_count >= ZLOG_FILTERS_MAX) + return 1; + + if (zlog_filter_lookup(filter) != -1) + /* Filter already present */ + return -1; + + strlcpy(zlog_filters[zlog_filter_count], filter, + sizeof(zlog_filters[0])); + + if (zlog_filters[zlog_filter_count][0] == '\0') + /* Filter was either empty or didn't get copied + * correctly + */ + return -1; + + zlog_filter_count++; + } + return 0; +} + +int zlog_filter_del(const char *filter) +{ + frr_with_mutex(&logfilterlock) { + int found_idx = zlog_filter_lookup(filter); + int last_idx = zlog_filter_count - 1; + + if (found_idx == -1) + /* Didn't find the filter to delete */ + return -1; + + /* Adjust the filter array */ + memmove(zlog_filters[found_idx], zlog_filters[found_idx + 1], + (last_idx - found_idx) * sizeof(zlog_filters[0])); + + zlog_filter_count--; + } + return 0; +} + +/* Dump all filters to buffer, delimited by new line */ +int zlog_filter_dump(char *buf, size_t max_size) +{ + int len = 0; + + frr_with_mutex(&logfilterlock) { + for (int i = 0; i < zlog_filter_count; i++) { + int ret; + + ret = snprintf(buf + len, max_size - len, " %s\n", + zlog_filters[i]); + len += ret; + if ((ret < 0) || ((size_t)len >= max_size)) + return -1; + } + } + + return len; +} + +static int search_buf(const char *buf) +{ + char *found = NULL; + + frr_with_mutex(&logfilterlock) { + for (int i = 0; i < zlog_filter_count; i++) { + found = strstr(buf, zlog_filters[i]); + if (found != NULL) + return 0; + } + } + + return -1; +} + +static void zlog_filterfile_fd(struct zlog_target *zt, struct zlog_msg *msgs[], + size_t nmsgs) +{ + struct zlog_msg *msgfilt[nmsgs]; + size_t i, o = 0; + + for (i = 0; i < nmsgs; i++) { + if (zlog_msg_prio(msgs[i]) >= LOG_DEBUG + && search_buf(zlog_msg_text(msgs[i], NULL)) < 0) + continue; + + msgfilt[o++] = msgs[i]; + } + + if (o) + zlog_fd(zt, msgfilt, o); +} + +void zlog_filterfile_init(struct zlog_cfg_filterfile *zcf) +{ + zlog_file_init(&zcf->parent); + zcf->parent.zlog_wrap = zlog_filterfile_fd; +} + +void zlog_filterfile_fini(struct zlog_cfg_filterfile *zcf) +{ + zlog_file_fini(&zcf->parent); +} diff --git a/lib/log_int.h b/lib/log_int.h deleted file mode 100644 index 287e626eab..0000000000 --- a/lib/log_int.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Zebra logging funcions. - * Copyright (C) 1997, 1998, 1999 Kunihiro Ishiguro - * - * This file is part of GNU Zebra. - * - * GNU Zebra is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2, or (at your option) any - * later version. - * - * GNU Zebra is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; see the file COPYING; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef _ZEBRA_LOG_PRIVATE_H -#define _ZEBRA_LOG_PRIVATE_H - -#include "log.h" - -#ifdef __cplusplus -extern "C" { -#endif - -struct zlog { - const char *ident; /* daemon name (first arg to openlog) */ - const char *protoname; - unsigned short instance; - int maxlvl[ZLOG_NUM_DESTS]; /* maximum priority to send to associated - logging destination */ - int default_lvl; /* maxlvl to use if none is specified */ - FILE *fp; - char *filename; - int facility; /* as per syslog facility */ - int record_priority; /* should messages logged through stdio include the - priority of the message? */ - int syslog_options; /* 2nd arg to openlog */ - int timestamp_precision; /* # of digits of subsecond precision */ -}; - -/* Default logging strucutre. */ -extern struct zlog *zlog_default; - -extern const char *zlog_priority[]; - -/* Generic function for zlog. */ -extern void vzlog(int priority, const char *format, va_list args); - -#ifdef __cplusplus -} -#endif - -#endif /* _ZEBRA_LOG_PRIVATE_H */ diff --git a/lib/log_vty.c b/lib/log_vty.c new file mode 100644 index 0000000000..d1dcac2340 --- /dev/null +++ b/lib/log_vty.c @@ -0,0 +1,746 @@ +/* + * Logging - VTY code + * Copyright (C) 2019 Cumulus Networks, Inc. + * Stephen Worley + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "lib/log_vty.h" +#include "command.h" +#include "lib/log.h" +#include "lib/zlog_targets.h" +#include "lib/lib_errors.h" +#include "lib/printfrr.h" + +#ifndef VTYSH_EXTRACT_PL +#include "lib/log_vty_clippy.c" +#endif + +#define ZLOG_MAXLVL(a, b) MAX(a, b) + +DEFINE_HOOK(zlog_rotate, (), ()) + +static const int log_default_lvl = LOG_DEBUG; + +static int log_config_stdout_lvl = ZLOG_DISABLED; +static int log_config_syslog_lvl = ZLOG_DISABLED; +static int log_cmdline_stdout_lvl = ZLOG_DISABLED; +static int log_cmdline_syslog_lvl = ZLOG_DISABLED; + +static struct zlog_cfg_file zt_file_cmdline = { + .prio_min = ZLOG_DISABLED, +}; +static struct zlog_cfg_file zt_file = { + .prio_min = ZLOG_DISABLED, +}; +static struct zlog_cfg_file zt_stdout = { + .prio_min = ZLOG_DISABLED, +}; +static struct zlog_cfg_filterfile zt_filterfile = { + .parent = { + .prio_min = ZLOG_DISABLED, + }, +}; + +static const char *zlog_progname; +static const char *zlog_protoname; + +static const struct facility_map { + int facility; + const char *name; + size_t match; +} syslog_facilities[] = { + {LOG_KERN, "kern", 1}, + {LOG_USER, "user", 2}, + {LOG_MAIL, "mail", 1}, + {LOG_DAEMON, "daemon", 1}, + {LOG_AUTH, "auth", 1}, + {LOG_SYSLOG, "syslog", 1}, + {LOG_LPR, "lpr", 2}, + {LOG_NEWS, "news", 1}, + {LOG_UUCP, "uucp", 2}, + {LOG_CRON, "cron", 1}, +#ifdef LOG_FTP + {LOG_FTP, "ftp", 1}, +#endif + {LOG_LOCAL0, "local0", 6}, + {LOG_LOCAL1, "local1", 6}, + {LOG_LOCAL2, "local2", 6}, + {LOG_LOCAL3, "local3", 6}, + {LOG_LOCAL4, "local4", 6}, + {LOG_LOCAL5, "local5", 6}, + {LOG_LOCAL6, "local6", 6}, + {LOG_LOCAL7, "local7", 6}, + {0, NULL, 0}, +}; + +static const char * const zlog_priority[] = { + "emergencies", "alerts", "critical", "errors", "warnings", + "notifications", "informational", "debugging", NULL, +}; + +static const char *facility_name(int facility) +{ + const struct facility_map *fm; + + for (fm = syslog_facilities; fm->name; fm++) + if (fm->facility == facility) + return fm->name; + return ""; +} + +static int facility_match(const char *str) +{ + const struct facility_map *fm; + + for (fm = syslog_facilities; fm->name; fm++) + if (!strncmp(str, fm->name, fm->match)) + return fm->facility; + return -1; +} + +int log_level_match(const char *s) +{ + int level; + + for (level = 0; zlog_priority[level] != NULL; level++) + if (!strncmp(s, zlog_priority[level], 2)) + return level; + return ZLOG_DISABLED; +} + +void zlog_rotate(void) +{ + zlog_file_rotate(&zt_file); + zlog_file_rotate(&zt_filterfile.parent); + hook_call(zlog_rotate); +} + + +void log_show_syslog(struct vty *vty) +{ + int level = zlog_syslog_get_prio_min(); + + vty_out(vty, "Syslog logging: "); + if (level == ZLOG_DISABLED) + vty_out(vty, "disabled\n"); + else + vty_out(vty, "level %s, facility %s, ident %s\n", + zlog_priority[level], + facility_name(zlog_syslog_get_facility()), + zlog_progname); +} + +DEFUN (show_logging, + show_logging_cmd, + "show logging", + SHOW_STR + "Show current logging configuration\n") +{ + log_show_syslog(vty); + + vty_out(vty, "Stdout logging: "); + if (zt_stdout.prio_min == ZLOG_DISABLED) + vty_out(vty, "disabled"); + else + vty_out(vty, "level %s", + zlog_priority[zt_stdout.prio_min]); + vty_out(vty, "\n"); + + vty_out(vty, "File logging: "); + if (zt_file.prio_min == ZLOG_DISABLED || !zt_file.filename) + vty_out(vty, "disabled"); + else + vty_out(vty, "level %s, filename %s", + zlog_priority[zt_file.prio_min], zt_file.filename); + vty_out(vty, "\n"); + + if (zt_filterfile.parent.prio_min != ZLOG_DISABLED + && zt_filterfile.parent.filename) + vty_out(vty, "Filtered-file logging: level %s, filename %s\n", + zlog_priority[zt_filterfile.parent.prio_min], + zt_filterfile.parent.filename); + + if (log_cmdline_syslog_lvl != ZLOG_DISABLED) + vty_out(vty, + "From command line: \"--log syslog --log-level %s\"\n", + zlog_priority[log_cmdline_syslog_lvl]); + if (log_cmdline_stdout_lvl != ZLOG_DISABLED) + vty_out(vty, + "From command line: \"--log stdout --log-level %s\"\n", + zlog_priority[log_cmdline_stdout_lvl]); + if (zt_file_cmdline.prio_min != ZLOG_DISABLED) + vty_out(vty, + "From command line: \"--log file:%s --log-level %s\"\n", + zt_file_cmdline.filename, + zlog_priority[zt_file_cmdline.prio_min]); + + vty_out(vty, "Protocol name: %s\n", zlog_protoname); + vty_out(vty, "Record priority: %s\n", + (zt_file.record_priority ? "enabled" : "disabled")); + vty_out(vty, "Timestamp precision: %d\n", zt_file.ts_subsec); + return CMD_SUCCESS; +} + +DEFPY (config_log_stdout, + config_log_stdout_cmd, + "log stdout [$levelarg]", + "Logging control\n" + "Set stdout logging level\n" + LOG_LEVEL_DESC) +{ + int level; + + if (levelarg) { + level = log_level_match(levelarg); + if (level == ZLOG_DISABLED) + return CMD_ERR_NO_MATCH; + } else + level = log_default_lvl; + + log_config_stdout_lvl = level; + zt_stdout.prio_min = ZLOG_MAXLVL(log_config_stdout_lvl, + log_cmdline_stdout_lvl); + zlog_file_set_other(&zt_stdout); + return CMD_SUCCESS; +} + +DEFUN (no_config_log_stdout, + no_config_log_stdout_cmd, + "no log stdout []", + NO_STR + "Logging control\n" + "Cancel logging to stdout\n" + LOG_LEVEL_DESC) +{ + log_config_stdout_lvl = ZLOG_DISABLED; + zt_stdout.prio_min = ZLOG_MAXLVL(log_config_stdout_lvl, + log_cmdline_stdout_lvl); + zlog_file_set_other(&zt_stdout); + return CMD_SUCCESS; +} + +DEFUN_HIDDEN (config_log_monitor, + config_log_monitor_cmd, + "log monitor []", + "Logging control\n" + "Set terminal line (monitor) logging level\n" + LOG_LEVEL_DESC) +{ + vty_out(vty, "%% \"log monitor\" is deprecated and does nothing.\n"); + return CMD_SUCCESS; +} + +DEFUN_HIDDEN (no_config_log_monitor, + no_config_log_monitor_cmd, + "no log monitor []", + NO_STR + "Logging control\n" + "Disable terminal line (monitor) logging\n" + LOG_LEVEL_DESC) +{ + return CMD_SUCCESS; +} + +static int set_log_file(struct zlog_cfg_file *target, struct vty *vty, + const char *fname, int loglevel) +{ + char path[MAXPATHLEN + 1]; + const char *fullpath; + bool ok; + + + /* Path detection. */ + if (!IS_DIRECTORY_SEP(*fname)) { + char cwd[MAXPATHLEN + 1]; + + cwd[MAXPATHLEN] = '\0'; + + if (getcwd(cwd, MAXPATHLEN) == NULL) { + flog_err_sys(EC_LIB_SYSTEM_CALL, + "config_log_file: Unable to alloc mem!"); + return CMD_WARNING_CONFIG_FAILED; + } + + int pr = snprintf(path, sizeof(path), "%s/%s", cwd, fname); + if (pr < 0 || (unsigned int)pr >= sizeof(path)) { + flog_err_sys( + EC_LIB_SYSTEM_CALL, + "%s: Path too long ('%s/%s'); system maximum is %u", + __func__, cwd, fname, MAXPATHLEN); + return CMD_WARNING_CONFIG_FAILED; + } + + fullpath = path; + } else + fullpath = fname; + + target->prio_min = loglevel; + ok = zlog_file_set_filename(target, fullpath); + + if (!ok) { + if (vty) + vty_out(vty, "can't open logfile %s\n", fname); + return CMD_WARNING_CONFIG_FAILED; + } + return CMD_SUCCESS; +} + +void command_setup_early_logging(const char *dest, const char *level) +{ + int nlevel; + char *sep; + int len; + char type[8]; + + if (level) { + nlevel = log_level_match(level); + + if (nlevel == ZLOG_DISABLED) { + fprintf(stderr, "invalid log level \"%s\"\n", level); + exit(1); + } + } else + nlevel = log_default_lvl; + + if (!dest) + return; + + sep = strchr(dest, ':'); + len = sep ? (int)(sep - dest) : (int)strlen(dest); + + snprintfrr(type, sizeof(type), "%.*s", len, dest); + + if (strcmp(type, "stdout") == 0) { + log_cmdline_stdout_lvl = nlevel; + zt_stdout.prio_min = ZLOG_MAXLVL(log_config_stdout_lvl, + log_cmdline_stdout_lvl); + zlog_file_set_other(&zt_stdout); + return; + } + if (strcmp(type, "syslog") == 0) { + log_cmdline_syslog_lvl = nlevel; + zlog_syslog_set_prio_min(ZLOG_MAXLVL(log_config_syslog_lvl, + log_cmdline_syslog_lvl)); + return; + } + if (strcmp(type, "file") == 0 && sep) { + sep++; + set_log_file(&zt_file_cmdline, NULL, sep, nlevel); + return; + } + + fprintf(stderr, "invalid log target \"%s\" (\"%s\")\n", type, dest); + exit(1); +} + +DEFUN (clear_log_cmdline, + clear_log_cmdline_cmd, + "clear log cmdline-targets", + CLEAR_STR + "Logging control\n" + "Disable log targets specified at startup by --log option\n") +{ + zt_file_cmdline.prio_min = ZLOG_DISABLED; + zlog_file_set_other(&zt_file_cmdline); + + log_cmdline_syslog_lvl = ZLOG_DISABLED; + zlog_syslog_set_prio_min(ZLOG_MAXLVL(log_config_syslog_lvl, + log_cmdline_syslog_lvl)); + + log_cmdline_stdout_lvl = ZLOG_DISABLED; + zt_stdout.prio_min = ZLOG_MAXLVL(log_config_stdout_lvl, + log_cmdline_stdout_lvl); + zlog_file_set_other(&zt_stdout); + + return CMD_SUCCESS; +} + +DEFPY (config_log_file, + config_log_file_cmd, + "log file FILENAME [$levelarg]", + "Logging control\n" + "Logging to file\n" + "Logging filename\n" + LOG_LEVEL_DESC) +{ + int level = log_default_lvl; + + if (levelarg) { + level = log_level_match(levelarg); + if (level == ZLOG_DISABLED) + return CMD_ERR_NO_MATCH; + } + return set_log_file(&zt_file, vty, filename, level); +} + +DEFUN (no_config_log_file, + no_config_log_file_cmd, + "no log file [FILENAME [LEVEL]]", + NO_STR + "Logging control\n" + "Cancel logging to file\n" + "Logging file name\n" + "Logging level\n") +{ + zt_file.prio_min = ZLOG_DISABLED; + zlog_file_set_other(&zt_file); + return CMD_SUCCESS; +} + +DEFPY (config_log_syslog, + config_log_syslog_cmd, + "log syslog [$levelarg]", + "Logging control\n" + "Set syslog logging level\n" + LOG_LEVEL_DESC) +{ + int level; + + if (levelarg) { + level = log_level_match(levelarg); + + if (level == ZLOG_DISABLED) + return CMD_ERR_NO_MATCH; + } else + level = log_default_lvl; + + log_config_syslog_lvl = level; + zlog_syslog_set_prio_min(ZLOG_MAXLVL(log_config_syslog_lvl, + log_cmdline_syslog_lvl)); + return CMD_SUCCESS; +} + +DEFUN (no_config_log_syslog, + no_config_log_syslog_cmd, + "no log syslog [] []", + NO_STR + "Logging control\n" + "Cancel logging to syslog\n" + LOG_FACILITY_DESC + LOG_LEVEL_DESC) +{ + log_config_syslog_lvl = ZLOG_DISABLED; + zlog_syslog_set_prio_min(ZLOG_MAXLVL(log_config_syslog_lvl, + log_cmdline_syslog_lvl)); + return CMD_SUCCESS; +} + +DEFPY (config_log_facility, + config_log_facility_cmd, + "log facility $facilityarg", + "Logging control\n" + "Facility parameter for syslog messages\n" + LOG_FACILITY_DESC) +{ + int facility = facility_match(facilityarg); + + zlog_syslog_set_facility(facility); + return CMD_SUCCESS; +} + +DEFUN (no_config_log_facility, + no_config_log_facility_cmd, + "no log facility []", + NO_STR + "Logging control\n" + "Reset syslog facility to default (daemon)\n" + LOG_FACILITY_DESC) +{ + zlog_syslog_set_facility(LOG_DAEMON); + return CMD_SUCCESS; +} + +DEFUN (config_log_record_priority, + config_log_record_priority_cmd, + "log record-priority", + "Logging control\n" + "Log the priority of the message within the message\n") +{ + zt_file.record_priority = true; + zlog_file_set_other(&zt_file); + zt_stdout.record_priority = true; + zlog_file_set_other(&zt_stdout); + zt_filterfile.parent.record_priority = true; + zlog_file_set_other(&zt_filterfile.parent); + return CMD_SUCCESS; +} + +DEFUN (no_config_log_record_priority, + no_config_log_record_priority_cmd, + "no log record-priority", + NO_STR + "Logging control\n" + "Do not log the priority of the message within the message\n") +{ + zt_file.record_priority = false; + zlog_file_set_other(&zt_file); + zt_stdout.record_priority = false; + zlog_file_set_other(&zt_stdout); + zt_filterfile.parent.record_priority = false; + zlog_file_set_other(&zt_filterfile.parent); + return CMD_SUCCESS; +} + +DEFPY (config_log_timestamp_precision, + config_log_timestamp_precision_cmd, + "log timestamp precision (0-6)", + "Logging control\n" + "Timestamp configuration\n" + "Set the timestamp precision\n" + "Number of subsecond digits\n") +{ + zt_file.ts_subsec = precision; + zlog_file_set_other(&zt_file); + zt_stdout.ts_subsec = precision; + zlog_file_set_other(&zt_stdout); + zt_filterfile.parent.ts_subsec = precision; + zlog_file_set_other(&zt_filterfile.parent); + return CMD_SUCCESS; +} + +DEFUN (no_config_log_timestamp_precision, + no_config_log_timestamp_precision_cmd, + "no log timestamp precision [(0-6)]", + NO_STR + "Logging control\n" + "Timestamp configuration\n" + "Reset the timestamp precision to the default value of 0\n" + "Number of subsecond digits\n") +{ + zt_file.ts_subsec = 0; + zlog_file_set_other(&zt_file); + zt_stdout.ts_subsec = 0; + zlog_file_set_other(&zt_stdout); + zt_filterfile.parent.ts_subsec = 0; + zlog_file_set_other(&zt_filterfile.parent); + return CMD_SUCCESS; +} + +DEFPY (config_log_filterfile, + config_log_filterfile_cmd, + "log filtered-file FILENAME [$levelarg]", + "Logging control\n" + "Logging to file with string filter\n" + "Logging filename\n" + LOG_LEVEL_DESC) +{ + int level = log_default_lvl; + + if (levelarg) { + level = log_level_match(levelarg); + if (level == ZLOG_DISABLED) + return CMD_ERR_NO_MATCH; + } + return set_log_file(&zt_filterfile.parent, vty, filename, level); +} + +DEFUN (no_config_log_filterfile, + no_config_log_filterfile_cmd, + "no log filtered-file [FILENAME [LEVEL]]", + NO_STR + "Logging control\n" + "Cancel logging to file with string filter\n" + "Logging file name\n" + "Logging level\n") +{ + zt_filterfile.parent.prio_min = ZLOG_DISABLED; + zlog_file_set_other(&zt_filterfile.parent); + return CMD_SUCCESS; +} + +DEFPY (log_filter, + log_filter_cmd, + "[no] log-filter WORD$filter", + NO_STR + FILTER_LOG_STR + "String to filter by\n") +{ + int ret = 0; + + if (no) + ret = zlog_filter_del(filter); + else + ret = zlog_filter_add(filter); + + if (ret == 1) { + vty_out(vty, "%% filter table full\n"); + return CMD_WARNING; + } else if (ret != 0) { + vty_out(vty, "%% failed to %s log filter\n", + (no ? "remove" : "apply")); + return CMD_WARNING; + } + + vty_out(vty, " %s\n", filter); + return CMD_SUCCESS; +} + +/* Clear all log filters */ +DEFPY (log_filter_clear, + log_filter_clear_cmd, + "clear log-filter", + CLEAR_STR + FILTER_LOG_STR) +{ + zlog_filter_clear(); + return CMD_SUCCESS; +} + +/* Show log filter */ +DEFPY (show_log_filter, + show_log_filter_cmd, + "show log-filter", + SHOW_STR + FILTER_LOG_STR) +{ + char log_filters[ZLOG_FILTERS_MAX * (ZLOG_FILTER_LENGTH_MAX + 3)] = ""; + int len = 0; + + len = zlog_filter_dump(log_filters, sizeof(log_filters)); + + if (len == -1) { + vty_out(vty, "%% failed to get filters\n"); + return CMD_WARNING; + } + + if (len != 0) + vty_out(vty, "%s", log_filters); + + return CMD_SUCCESS; +} + +void log_config_write(struct vty *vty) +{ + bool show_cmdline_hint = false; + + if (zt_file.prio_min != ZLOG_DISABLED && zt_file.filename) { + vty_out(vty, "log file %s", zt_file.filename); + + if (zt_file.prio_min != log_default_lvl) + vty_out(vty, " %s", zlog_priority[zt_file.prio_min]); + vty_out(vty, "\n"); + } + + if (zt_filterfile.parent.prio_min != ZLOG_DISABLED + && zt_filterfile.parent.filename) { + vty_out(vty, "log filtered-file %s", + zt_filterfile.parent.filename); + + if (zt_filterfile.parent.prio_min != log_default_lvl) + vty_out(vty, " %s", + zlog_priority[zt_filterfile.parent.prio_min]); + vty_out(vty, "\n"); + } + + if (log_config_stdout_lvl != ZLOG_DISABLED) { + vty_out(vty, "log stdout"); + + if (log_config_stdout_lvl != log_default_lvl) + vty_out(vty, " %s", + zlog_priority[log_config_stdout_lvl]); + vty_out(vty, "\n"); + } + + if (log_config_syslog_lvl != ZLOG_DISABLED) { + vty_out(vty, "log syslog"); + + if (log_config_syslog_lvl != log_default_lvl) + vty_out(vty, " %s", + zlog_priority[log_config_syslog_lvl]); + vty_out(vty, "\n"); + } + + if (log_cmdline_syslog_lvl != ZLOG_DISABLED) { + vty_out(vty, + "! \"log syslog %s\" enabled by \"--log\" startup option\n", + zlog_priority[log_cmdline_syslog_lvl]); + show_cmdline_hint = true; + } + if (log_cmdline_stdout_lvl != ZLOG_DISABLED) { + vty_out(vty, + "! \"log stdout %s\" enabled by \"--log\" startup option\n", + zlog_priority[log_cmdline_stdout_lvl]); + show_cmdline_hint = true; + } + if (zt_file_cmdline.prio_min != ZLOG_DISABLED) { + vty_out(vty, + "! \"log file %s %s\" enabled by \"--log\" startup option\n", + zt_file_cmdline.filename, + zlog_priority[zt_file_cmdline.prio_min]); + show_cmdline_hint = true; + } + if (show_cmdline_hint) + vty_out(vty, + "! use \"clear log cmdline-targets\" to remove this target\n"); + + if (zlog_syslog_get_facility() != LOG_DAEMON) + vty_out(vty, "log facility %s\n", + facility_name(zlog_syslog_get_facility())); + + if (zt_file.record_priority == 1) + vty_out(vty, "log record-priority\n"); + + if (zt_file.ts_subsec > 0) + vty_out(vty, "log timestamp precision %d\n", + zt_file.ts_subsec); +} + +static int log_vty_init(const char *progname, const char *protoname, + unsigned short instance, uid_t uid, gid_t gid) +{ + zlog_progname = progname; + zlog_protoname = protoname; + + zlog_filterfile_init(&zt_filterfile); + + zlog_file_set_fd(&zt_stdout, STDOUT_FILENO); + return 0; +} + +__attribute__((_CONSTRUCTOR(475))) static void log_vty_preinit(void) +{ + hook_register(zlog_init, log_vty_init); +} + +void log_cmd_init(void) +{ + install_element(VIEW_NODE, &show_logging_cmd); + install_element(ENABLE_NODE, &clear_log_cmdline_cmd); + + install_element(CONFIG_NODE, &config_log_stdout_cmd); + install_element(CONFIG_NODE, &no_config_log_stdout_cmd); + install_element(CONFIG_NODE, &config_log_monitor_cmd); + install_element(CONFIG_NODE, &no_config_log_monitor_cmd); + install_element(CONFIG_NODE, &config_log_file_cmd); + install_element(CONFIG_NODE, &no_config_log_file_cmd); + install_element(CONFIG_NODE, &config_log_syslog_cmd); + install_element(CONFIG_NODE, &no_config_log_syslog_cmd); + install_element(CONFIG_NODE, &config_log_facility_cmd); + install_element(CONFIG_NODE, &no_config_log_facility_cmd); + install_element(CONFIG_NODE, &config_log_record_priority_cmd); + install_element(CONFIG_NODE, &no_config_log_record_priority_cmd); + install_element(CONFIG_NODE, &config_log_timestamp_precision_cmd); + install_element(CONFIG_NODE, &no_config_log_timestamp_precision_cmd); + + install_element(VIEW_NODE, &show_log_filter_cmd); + install_element(CONFIG_NODE, &log_filter_cmd); + install_element(CONFIG_NODE, &log_filter_clear_cmd); + install_element(CONFIG_NODE, &config_log_filterfile_cmd); + install_element(CONFIG_NODE, &no_config_log_filterfile_cmd); +} diff --git a/lib/log_vty.h b/lib/log_vty.h new file mode 100644 index 0000000000..16c4475467 --- /dev/null +++ b/lib/log_vty.h @@ -0,0 +1,44 @@ +/* + * Logging - VTY library + * Copyright (C) 2019 Cumulus Networks, Inc. + * Stephen Worley + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __LOG_VTY_H__ +#define __LOG_VTY_H__ + +#include "lib/hook.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct vty; + +extern void log_cmd_init(void); +extern void log_config_write(struct vty *vty); +extern int log_level_match(const char *s); +extern void log_show_syslog(struct vty *vty); + +DECLARE_HOOK(zlog_rotate, (), ()) +extern void zlog_rotate(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __LOG_VTY_H__ */ diff --git a/lib/logicalrouter.c b/lib/logicalrouter.c deleted file mode 100644 index da69ae5312..0000000000 --- a/lib/logicalrouter.c +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Logical Router functions. - * Copyright (C) 2018 6WIND S.A. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; see the file COPYING; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include - -#include "ns.h" -#include "log.h" -#include "memory.h" - -#include "command.h" -#include "vty.h" -#include "logicalrouter.h" - -/* Comment that useless define to avoid compilation error - * in order to use it, one could provide the kind of NETNS to NS backend - * so that the allocation will match the logical router - * DEFINE_MTYPE_STATIC(LIB, LOGICALROUTER, "LogicalRouter Context") - */ -DEFINE_MTYPE_STATIC(LIB, LOGICALROUTER_NAME, "Logical Router Name") - -/* Logical Router node has no interface. */ -static struct cmd_node logicalrouter_node = {LOGICALROUTER_NODE, "", 1}; - -static int logicalrouter_backend; - -/* Get a NS. If not found, create one. */ -static struct ns *logicalrouter_get(ns_id_t ns_id) -{ - struct ns *ns; - - ns = ns_lookup(ns_id); - if (ns) - return (ns); - ns = ns_get_created(ns, NULL, ns_id); - return ns; -} - -static int logicalrouter_is_backend_netns(void) -{ - return (logicalrouter_backend == LOGICALROUTER_BACKEND_NETNS); -} - - -DEFUN_NOSH (logicalrouter, - logicalrouter_cmd, - "logical-router (1-65535) ns NAME", - "Enable a logical-router\n" - "Specify the logical-router indentifier\n" - "The Name Space\n" - "The file name in " NS_RUN_DIR ", or a full pathname\n") -{ - int idx_number = 1; - int idx_name = 3; - ns_id_t ns_id; - struct ns *ns = NULL; - char *pathname = ns_netns_pathname(vty, argv[idx_name]->arg); - - if (!pathname) - return CMD_WARNING_CONFIG_FAILED; - - ns_id = strtoul(argv[idx_number]->arg, NULL, 10); - ns = logicalrouter_get(ns_id); - - if (ns->name && strcmp(ns->name, pathname) != 0) { - vty_out(vty, "NS %u is already configured with NETNS %s\n", - ns->ns_id, ns->name); - return CMD_WARNING; - } - - if (!ns->name) - ns->name = XSTRDUP(MTYPE_LOGICALROUTER_NAME, pathname); - - if (!ns_enable(ns, NULL)) { - vty_out(vty, "Can not associate NS %u with NETNS %s\n", - ns->ns_id, ns->name); - return CMD_WARNING_CONFIG_FAILED; - } - - return CMD_SUCCESS; -} - -DEFUN (no_logicalrouter, - no_logicalrouter_cmd, - "no logical-router (1-65535) ns NAME", - NO_STR - "Enable a Logical-Router\n" - "Specify the Logical-Router identifier\n" - "The Name Space\n" - "The file name in " NS_RUN_DIR ", or a full pathname\n") -{ - int idx_number = 2; - int idx_name = 4; - ns_id_t ns_id; - struct ns *ns = NULL; - char *pathname = ns_netns_pathname(vty, argv[idx_name]->arg); - - if (!pathname) - return CMD_WARNING_CONFIG_FAILED; - - ns_id = strtoul(argv[idx_number]->arg, NULL, 10); - ns = ns_lookup(ns_id); - - if (!ns) { - vty_out(vty, "NS %u is not found\n", ns_id); - return CMD_SUCCESS; - } - - if (ns->name && strcmp(ns->name, pathname) != 0) { - vty_out(vty, "Incorrect NETNS file name\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - ns_disable(ns); - - if (ns->name) { - XFREE(MTYPE_LOGICALROUTER_NAME, ns->name); - ns->name = NULL; - } - - return CMD_SUCCESS; -} - -/* Initialize NS module. */ -void logicalrouter_init(int (*writefunc)(struct vty *vty)) -{ - if (ns_have_netns() && logicalrouter_is_backend_netns()) { - /* Install LogicalRouter commands. */ - install_node(&logicalrouter_node, writefunc); - install_element(CONFIG_NODE, &logicalrouter_cmd); - install_element(CONFIG_NODE, &no_logicalrouter_cmd); - } -} - -void logicalrouter_terminate(void) -{ - ns_terminate(); -} - -void logicalrouter_configure_backend(int backend_netns) -{ - logicalrouter_backend = backend_netns; -} diff --git a/lib/md5.c b/lib/md5.c index 5a3e7c8d1b..5c93c7bc1f 100644 --- a/lib/md5.c +++ b/lib/md5.c @@ -412,8 +412,8 @@ void hmac_md5(unsigned char *text, int text_len, unsigned char *key, */ /* start out by storing key in pads */ - bzero(k_ipad, sizeof k_ipad); - bzero(k_opad, sizeof k_opad); + bzero(k_ipad, sizeof(k_ipad)); + bzero(k_opad, sizeof(k_opad)); bcopy(key, k_ipad, key_len); bcopy(key, k_opad, key_len); @@ -429,7 +429,7 @@ void hmac_md5(unsigned char *text, int text_len, unsigned char *key, * pass */ MD5Update(&context, k_ipad, 64); /* start with inner pad */ MD5Update(&context, text, text_len); /* then text of datagram */ - MD5Final((uint8_t *)digest, &context); /* finish up 1st pass */ + MD5Final(digest, &context); /* finish up 1st pass */ /* * perform outer MD5 */ @@ -438,5 +438,5 @@ void hmac_md5(unsigned char *text, int text_len, unsigned char *key, MD5Update(&context, k_opad, 64); /* start with outer pad */ MD5Update(&context, digest, 16); /* then results of 1st * hash */ - MD5Final((uint8_t *)digest, &context); /* finish up 2nd pass */ + MD5Final(digest, &context); /* finish up 2nd pass */ } diff --git a/lib/memory.c b/lib/memory.c index 149e294d50..2c902d123b 100644 --- a/lib/memory.c +++ b/lib/memory.c @@ -157,13 +157,13 @@ static int qmem_exit_walker(void *arg, struct memgroup *mg, struct memtype *mt) if (!mt) { fprintf(eda->fp, - "%s: showing active allocations in " - "memory group %s\n", + "%s: showing active allocations in memory group %s\n", eda->prefix, mg->name); } else if (mt->n_alloc) { char size[32]; - eda->error++; + if (!mg->active_at_exit) + eda->error++; snprintf(size, sizeof(size), "%10zu", mt->size); fprintf(eda->fp, "%s: memstats: %-30s: %6zu * %s\n", eda->prefix, mt->name, mt->n_alloc, diff --git a/lib/memory.h b/lib/memory.h index 0002ea3349..13f2f9b11a 100644 --- a/lib/memory.h +++ b/lib/memory.h @@ -17,6 +17,7 @@ #ifndef _QUAGGA_MEMORY_H #define _QUAGGA_MEMORY_H +#include #include #include #include @@ -48,6 +49,8 @@ struct memgroup { struct memgroup *next, **ref; struct memtype *types, **insert; const char *name; + /* ignore group on dumping memleaks at exit */ + bool active_at_exit; }; /* macro usage: @@ -76,7 +79,7 @@ struct memgroup { */ #define DECLARE_MGROUP(name) extern struct memgroup _mg_##name; -#define DEFINE_MGROUP(mname, desc) \ +#define _DEFINE_MGROUP(mname, desc, ...) \ struct memgroup _mg_##mname \ __attribute__((section(".data.mgroups"))) = { \ .name = desc, \ @@ -84,6 +87,7 @@ struct memgroup { .next = NULL, \ .insert = NULL, \ .ref = NULL, \ + __VA_ARGS__ \ }; \ static void _mginit_##mname(void) __attribute__((_CONSTRUCTOR(1000))); \ static void _mginit_##mname(void) \ @@ -99,43 +103,52 @@ struct memgroup { if (_mg_##mname.next) \ _mg_##mname.next->ref = _mg_##mname.ref; \ *_mg_##mname.ref = _mg_##mname.next; \ - } + } \ + /* end */ +#define DEFINE_MGROUP(mname, desc) \ + _DEFINE_MGROUP(mname, desc, ) +#define DEFINE_MGROUP_ACTIVEATEXIT(mname, desc) \ + _DEFINE_MGROUP(mname, desc, .active_at_exit = true) #define DECLARE_MTYPE(name) \ - extern struct memtype _mt_##name; \ - static struct memtype *const MTYPE_##name = &_mt_##name; + extern struct memtype MTYPE_##name[1]; \ + /* end */ #define DEFINE_MTYPE_ATTR(group, mname, attr, desc) \ - attr struct memtype _mt_##mname \ - __attribute__((section(".data.mtypes"))) = { \ + attr struct memtype MTYPE_##mname[1] \ + __attribute__((section(".data.mtypes"))) = { { \ .name = desc, \ .next = NULL, \ .n_alloc = 0, \ .size = 0, \ .ref = NULL, \ - }; \ + } }; \ static void _mtinit_##mname(void) __attribute__((_CONSTRUCTOR(1001))); \ static void _mtinit_##mname(void) \ { \ if (_mg_##group.insert == NULL) \ _mg_##group.insert = &_mg_##group.types; \ - _mt_##mname.ref = _mg_##group.insert; \ - *_mg_##group.insert = &_mt_##mname; \ - _mg_##group.insert = &_mt_##mname.next; \ + MTYPE_##mname->ref = _mg_##group.insert; \ + *_mg_##group.insert = MTYPE_##mname; \ + _mg_##group.insert = &MTYPE_##mname->next; \ } \ static void _mtfini_##mname(void) __attribute__((_DESTRUCTOR(1001))); \ static void _mtfini_##mname(void) \ { \ - if (_mt_##mname.next) \ - _mt_##mname.next->ref = _mt_##mname.ref; \ - *_mt_##mname.ref = _mt_##mname.next; \ - } + if (MTYPE_##mname->next) \ + MTYPE_##mname->next->ref = MTYPE_##mname->ref; \ + *MTYPE_##mname->ref = MTYPE_##mname->next; \ + } \ + /* end */ + +#define DEFINE_MTYPE(group, name, desc) \ + DEFINE_MTYPE_ATTR(group, name, , desc) \ + /* end */ -#define DEFINE_MTYPE(group, name, desc) DEFINE_MTYPE_ATTR(group, name, , desc) #define DEFINE_MTYPE_STATIC(group, name, desc) \ DEFINE_MTYPE_ATTR(group, name, static, desc) \ - static struct memtype *const MTYPE_##name = &_mt_##name; + /* end */ DECLARE_MGROUP(LIB) DECLARE_MTYPE(TMP) @@ -176,7 +189,8 @@ extern int qmem_walk(qmem_walk_fn *func, void *arg); extern int log_memstats(FILE *fp, const char *); #define log_memstats_stderr(prefix) log_memstats(stderr, prefix) -extern void memory_oom(size_t size, const char *name); +extern __attribute__((__noreturn__)) void memory_oom(size_t size, + const char *name); #ifdef __cplusplus } diff --git a/lib/mlag.c b/lib/mlag.c index acdc662924..653fbe8fe9 100644 --- a/lib/mlag.c +++ b/lib/mlag.c @@ -39,3 +39,148 @@ char *mlag_role2str(enum mlag_role role, char *buf, size_t size) return buf; } + +char *mlag_lib_msgid_to_str(enum mlag_msg_type msg_type, char *buf, size_t size) +{ + switch (msg_type) { + case MLAG_REGISTER: + snprintf(buf, size, "Register"); + break; + case MLAG_DEREGISTER: + snprintf(buf, size, "De-Register"); + break; + case MLAG_MROUTE_ADD: + snprintf(buf, size, "Mroute add"); + break; + case MLAG_MROUTE_DEL: + snprintf(buf, size, "Mroute del"); + break; + case MLAG_DUMP: + snprintf(buf, size, "Mlag Replay"); + break; + case MLAG_MROUTE_ADD_BULK: + snprintf(buf, size, "Mroute Add Batch"); + break; + case MLAG_MROUTE_DEL_BULK: + snprintf(buf, size, "Mroute Del Batch"); + break; + case MLAG_STATUS_UPDATE: + snprintf(buf, size, "Mlag Status"); + break; + case MLAG_VXLAN_UPDATE: + snprintf(buf, size, "Mlag vxlan update"); + break; + case MLAG_PEER_FRR_STATUS: + snprintf(buf, size, "Mlag Peer FRR Status"); + break; + default: + snprintf(buf, size, "Unknown %d", msg_type); + break; + } + return buf; +} + + +int mlag_lib_decode_mlag_hdr(struct stream *s, struct mlag_msg *msg, + size_t *length) +{ +#define LIB_MLAG_HDR_LENGTH 8 + if (s == NULL || msg == NULL) + return -1; + + *length = stream_get_endp(s); + + if (*length < LIB_MLAG_HDR_LENGTH) + return -1; + + *length -= LIB_MLAG_HDR_LENGTH; + + STREAM_GETL(s, msg->msg_type); + STREAM_GETW(s, msg->data_len); + STREAM_GETW(s, msg->msg_cnt); + + return 0; +stream_failure: + return -1; +} + +#define MLAG_MROUTE_ADD_LENGTH \ + (VRF_NAMSIZ + INTERFACE_NAMSIZ + 4 + 4 + 4 + 4 + 1 + 1 + 4) + +int mlag_lib_decode_mroute_add(struct stream *s, struct mlag_mroute_add *msg, + size_t *length) +{ + if (s == NULL || msg == NULL || *length < MLAG_MROUTE_ADD_LENGTH) + return -1; + + STREAM_GET(msg->vrf_name, s, VRF_NAMSIZ); + STREAM_GETL(s, msg->source_ip); + STREAM_GETL(s, msg->group_ip); + STREAM_GETL(s, msg->cost_to_rp); + STREAM_GETL(s, msg->owner_id); + STREAM_GETC(s, msg->am_i_dr); + STREAM_GETC(s, msg->am_i_dual_active); + STREAM_GETL(s, msg->vrf_id); + STREAM_GET(msg->intf_name, s, INTERFACE_NAMSIZ); + + return 0; +stream_failure: + return -1; +} + +#define MLAG_MROUTE_DEL_LENGTH (VRF_NAMSIZ + INTERFACE_NAMSIZ + 4 + 4 + 4 + 4) + +int mlag_lib_decode_mroute_del(struct stream *s, struct mlag_mroute_del *msg, + size_t *length) +{ + if (s == NULL || msg == NULL || *length < MLAG_MROUTE_DEL_LENGTH) + return -1; + + STREAM_GET(msg->vrf_name, s, VRF_NAMSIZ); + STREAM_GETL(s, msg->source_ip); + STREAM_GETL(s, msg->group_ip); + STREAM_GETL(s, msg->owner_id); + STREAM_GETL(s, msg->vrf_id); + STREAM_GET(msg->intf_name, s, INTERFACE_NAMSIZ); + + return 0; +stream_failure: + return -1; +} + +int mlag_lib_decode_mlag_status(struct stream *s, struct mlag_status *msg) +{ + if (s == NULL || msg == NULL) + return -1; + + STREAM_GET(msg->peerlink_rif, s, INTERFACE_NAMSIZ); + STREAM_GETL(s, msg->my_role); + STREAM_GETL(s, msg->peer_state); + return 0; +stream_failure: + return -1; +} + +int mlag_lib_decode_vxlan_update(struct stream *s, struct mlag_vxlan *msg) +{ + if (s == NULL || msg == NULL) + return -1; + + STREAM_GETL(s, msg->anycast_ip); + STREAM_GETL(s, msg->local_ip); + return 0; + +stream_failure: + return -1; +} + +int mlag_lib_decode_frr_status(struct stream *s, struct mlag_frr_status *msg) +{ + if (s == NULL || msg == NULL) + return -1; + + STREAM_GETL(s, msg->frr_state); + return 0; +stream_failure: + return -1; +} diff --git a/lib/mlag.h b/lib/mlag.h index 2b904d44f4..37bb3aa6db 100644 --- a/lib/mlag.h +++ b/lib/mlag.h @@ -26,14 +26,119 @@ extern "C" { #endif +#include "lib/if.h" +#include "lib/vrf.h" +#include "lib/stream.h" + +#define MLAG_MSG_NULL_PAYLOAD 0 +#define MLAG_MSG_NO_BATCH 1 +#define MLAG_BUF_LIMIT 2048 + enum mlag_role { MLAG_ROLE_NONE, MLAG_ROLE_PRIMARY, MLAG_ROLE_SECONDARY }; -extern char *mlag_role2str(enum mlag_role role, char *buf, size_t size); +enum mlag_state { + MLAG_STATE_DOWN, + MLAG_STATE_RUNNING, +}; + +enum mlag_frr_state { + MLAG_FRR_STATE_NONE, + MLAG_FRR_STATE_DOWN, + MLAG_FRR_STATE_UP, +}; + +enum mlag_owner { + MLAG_OWNER_NONE, + MLAG_OWNER_INTERFACE, + MLAG_OWNER_VXLAN, +}; + +/* + * This message definition should match mlag.proto + * Because message registration is based on this + */ +enum mlag_msg_type { + MLAG_MSG_NONE = 0, + MLAG_REGISTER = 1, + MLAG_DEREGISTER = 2, + MLAG_STATUS_UPDATE = 3, + MLAG_MROUTE_ADD = 4, + MLAG_MROUTE_DEL = 5, + MLAG_DUMP = 6, + MLAG_MROUTE_ADD_BULK = 7, + MLAG_MROUTE_DEL_BULK = 8, + MLAG_PIM_CFG_DUMP = 10, + MLAG_VXLAN_UPDATE = 11, + MLAG_PEER_FRR_STATUS = 12, +}; + +struct mlag_frr_status { + enum mlag_frr_state frr_state; +}; +struct mlag_status { + char peerlink_rif[INTERFACE_NAMSIZ]; + enum mlag_role my_role; + enum mlag_state peer_state; +}; + +#define MLAG_ROLE_STRSIZE 16 + +struct mlag_vxlan { + uint32_t anycast_ip; + uint32_t local_ip; +}; + +struct mlag_mroute_add { + char vrf_name[VRF_NAMSIZ]; + uint32_t source_ip; + uint32_t group_ip; + uint32_t cost_to_rp; + enum mlag_owner owner_id; + bool am_i_dr; + bool am_i_dual_active; + vrf_id_t vrf_id; + char intf_name[INTERFACE_NAMSIZ]; +}; + +struct mlag_mroute_del { + char vrf_name[VRF_NAMSIZ]; + uint32_t source_ip; + uint32_t group_ip; + enum mlag_owner owner_id; + vrf_id_t vrf_id; + char intf_name[INTERFACE_NAMSIZ]; +}; + +struct mlag_msg { + enum mlag_msg_type msg_type; + uint16_t data_len; + uint16_t msg_cnt; + uint8_t data[0]; +} __attribute__((packed)); + + +extern char *mlag_role2str(enum mlag_role role, char *buf, size_t size); +extern char *mlag_lib_msgid_to_str(enum mlag_msg_type msg_type, char *buf, + size_t size); +extern int mlag_lib_decode_mlag_hdr(struct stream *s, struct mlag_msg *msg, + size_t *length); +extern int mlag_lib_decode_mroute_add(struct stream *s, + struct mlag_mroute_add *msg, + size_t *length); +extern int mlag_lib_decode_mroute_del(struct stream *s, + struct mlag_mroute_del *msg, + size_t *length); +extern int mlag_lib_decode_mlag_status(struct stream *s, + struct mlag_status *msg); +extern int mlag_lib_decode_vxlan_update(struct stream *s, + struct mlag_vxlan *msg); +extern int mlag_lib_decode_frr_status(struct stream *s, + struct mlag_frr_status *msg); #ifdef __cplusplus } #endif diff --git a/lib/module.c b/lib/module.c index 098c550684..14d5cfd44f 100644 --- a/lib/module.c +++ b/lib/module.c @@ -58,7 +58,7 @@ static const char *execname = NULL; void frrmod_init(struct frrmod_runtime *modinfo) { - modinfo->finished_loading = 1; + modinfo->finished_loading = true; *frrmod_last = modinfo; frrmod_last = &modinfo->next; @@ -134,7 +134,7 @@ struct frrmod_runtime *frrmod_load(const char *spec, const char *dir, char *err, goto out_fail; } - rtinfo->finished_loading = 1; + rtinfo->finished_loading = true; *frrmod_last = rtinfo; frrmod_last = &rtinfo->next; diff --git a/lib/module.h b/lib/module.h index c5f96db85b..79cf52d75a 100644 --- a/lib/module.h +++ b/lib/module.h @@ -24,16 +24,6 @@ extern "C" { #endif -#if !defined(__GNUC__) -#error module code needs GCC visibility extensions -#elif __GNUC__ < 4 -#error module code needs GCC visibility extensions -#else -# define DSO_PUBLIC __attribute__ ((visibility ("default"))) -# define DSO_SELF __attribute__ ((visibility ("protected"))) -# define DSO_LOCAL __attribute__ ((visibility ("hidden"))) -#endif - struct frrmod_runtime; struct frrmod_info { diff --git a/lib/monotime.h b/lib/monotime.h index 6aac966ea1..dda763784f 100644 --- a/lib/monotime.h +++ b/lib/monotime.h @@ -84,7 +84,24 @@ static inline int64_t monotime_until(const struct timeval *ref, return (int64_t)tv.tv_sec * 1000000LL + tv.tv_usec; } -static inline char *time_to_string(time_t ts) +static inline time_t monotime_to_realtime(const struct timeval *mono, + struct timeval *realout) +{ + struct timeval delta, real; + + monotime_since(mono, &delta); + gettimeofday(&real, NULL); + + timersub(&real, &delta, &real); + if (realout) + *realout = real; + return real.tv_sec; +} + +/* Char buffer size for time-to-string api */ +#define MONOTIME_STRLEN 32 + +static inline char *time_to_string(time_t ts, char *buf) { struct timeval tv; time_t tbuf; @@ -92,7 +109,27 @@ static inline char *time_to_string(time_t ts) monotime(&tv); tbuf = time(NULL) - (tv.tv_sec - ts); - return ctime(&tbuf); + return ctime_r(&tbuf, buf); +} + +/* Convert interval to human-friendly string, used in cli output e.g. */ +static inline const char *frrtime_to_interval(time_t t, char *buf, + size_t buflen) +{ + struct tm tm; + + gmtime_r(&t, &tm); + + if (t < ONE_DAY_SECOND) + snprintf(buf, buflen, "%02d:%02d:%02d", tm.tm_hour, tm.tm_min, + tm.tm_sec); + else if (t < ONE_WEEK_SECOND) + snprintf(buf, buflen, "%dd%02dh%02dm", tm.tm_yday, tm.tm_hour, + tm.tm_min); + else + snprintf(buf, buflen, "%02dw%dd%02dh", tm.tm_yday / 7, + tm.tm_yday - ((tm.tm_yday / 7) * 7), tm.tm_hour); + return buf; } #ifdef __cplusplus diff --git a/lib/mpls.c b/lib/mpls.c index 759fe1206d..ac5792a686 100644 --- a/lib/mpls.c +++ b/lib/mpls.c @@ -79,7 +79,7 @@ int mpls_str2label(const char *label_str, uint8_t *num_labels, /* * Label to string conversion, labels in string separated by '/'. */ -char *mpls_label2str(uint8_t num_labels, mpls_label_t *labels, char *buf, +char *mpls_label2str(uint8_t num_labels, const mpls_label_t *labels, char *buf, int len, int pretty) { char label_buf[BUFSIZ]; diff --git a/lib/mpls.h b/lib/mpls.h index b140c8e317..74bd7aae3e 100644 --- a/lib/mpls.h +++ b/lib/mpls.h @@ -47,6 +47,7 @@ extern "C" { #define MPLS_LABEL_OAM_ALERT 14 /* [RFC3429] */ #define MPLS_LABEL_EXTENSION 15 /* [RFC7274] */ #define MPLS_LABEL_MAX 1048575 +#define MPLS_LABEL_VALUE_MASK 0x000FFFFF #define MPLS_LABEL_NONE 0xFFFFFFFF /* for internal use only */ /* Minimum and maximum label values */ @@ -54,6 +55,7 @@ extern "C" { #define MPLS_LABEL_RESERVED_MAX 15 #define MPLS_LABEL_UNRESERVED_MIN 16 #define MPLS_LABEL_UNRESERVED_MAX 1048575 +#define MPLS_LABEL_BASE_ANY 0 /* Default min and max SRGB label range */ /* Even if the SRGB allows to manage different Label space between routers, @@ -70,8 +72,7 @@ extern "C" { /* Maximum # labels that can be pushed. */ #define MPLS_MAX_LABELS 16 -#define IS_MPLS_RESERVED_LABEL(label) \ - (label >= MPLS_LABEL_RESERVED_MIN && label <= MPLS_LABEL_RESERVED_MAX) +#define IS_MPLS_RESERVED_LABEL(label) (label <= MPLS_LABEL_RESERVED_MAX) #define IS_MPLS_UNRESERVED_LABEL(label) \ (label >= MPLS_LABEL_UNRESERVED_MIN \ @@ -124,8 +125,10 @@ enum lsp_types_t { ZEBRA_LSP_STATIC = 1, /* Static LSP. */ ZEBRA_LSP_LDP = 2, /* LDP LSP. */ ZEBRA_LSP_BGP = 3, /* BGP LSP. */ - ZEBRA_LSP_SR = 4, /* Segment Routing LSP. */ - ZEBRA_LSP_SHARP = 5, /* Identifier for test protocol */ + ZEBRA_LSP_OSPF_SR = 4,/* OSPF Segment Routing LSP. */ + ZEBRA_LSP_ISIS_SR = 5,/* IS-IS Segment Routing LSP. */ + ZEBRA_LSP_SHARP = 6, /* Identifier for test protocol */ + ZEBRA_LSP_SRTE = 7, /* SR-TE LSP */ }; /* Functions for basic label operations. */ @@ -207,10 +210,13 @@ static inline char *label2str(mpls_label_t label, char *buf, size_t len) int mpls_str2label(const char *label_str, uint8_t *num_labels, mpls_label_t *labels); +/* Generic string buffer for label-stack-to-str */ +#define MPLS_LABEL_STRLEN 1024 + /* * Label to string conversion, labels in string separated by '/'. */ -char *mpls_label2str(uint8_t num_labels, mpls_label_t *labels, char *buf, +char *mpls_label2str(uint8_t num_labels, const mpls_label_t *labels, char *buf, int len, int pretty); #ifdef __cplusplus diff --git a/lib/netns_linux.c b/lib/netns_linux.c index 55c66fdc3d..c688433983 100644 --- a/lib/netns_linux.c +++ b/lib/netns_linux.c @@ -43,15 +43,12 @@ DEFINE_MTYPE_STATIC(LIB, NS, "NetNS Context") DEFINE_MTYPE_STATIC(LIB, NS_NAME, "NetNS Name") -/* default NS ID value used when VRF backend is not NETNS */ -#define NS_DEFAULT_INTERNAL 0 - static inline int ns_compare(const struct ns *ns, const struct ns *ns2); static struct ns *ns_lookup_name_internal(const char *name); RB_GENERATE(ns_head, ns, entry, ns_compare) -struct ns_head ns_tree = RB_INITIALIZER(&ns_tree); +static struct ns_head ns_tree = RB_INITIALIZER(&ns_tree); static struct ns *default_ns; static int ns_current_ns_fd; @@ -74,7 +71,8 @@ static inline int ns_map_compare(const struct ns_map_nsid *a, RB_HEAD(ns_map_nsid_head, ns_map_nsid); RB_PROTOTYPE(ns_map_nsid_head, ns_map_nsid, id_entry, ns_map_compare); RB_GENERATE(ns_map_nsid_head, ns_map_nsid, id_entry, ns_map_compare); -struct ns_map_nsid_head ns_map_nsid_list = RB_INITIALIZER(&ns_map_nsid_list); +static struct ns_map_nsid_head ns_map_nsid_list = + RB_INITIALIZER(&ns_map_nsid_list); static ns_id_t ns_id_external_numbering; @@ -100,9 +98,6 @@ static inline int setns(int fd, int nstype) static int have_netns_enabled = -1; #endif /* HAVE_NETNS */ -/* default NS ID value used when VRF backend is not NETNS */ -#define NS_DEFAULT_INTERNAL 0 - static int have_netns(void) { #ifdef HAVE_NETNS @@ -123,7 +118,7 @@ static int have_netns(void) } /* Holding NS hooks */ -struct ns_master { +static struct ns_master { int (*ns_new_hook)(struct ns *ns); int (*ns_delete_hook)(struct ns *ns); int (*ns_enable_hook)(struct ns *ns); @@ -370,7 +365,7 @@ int ns_enable(struct ns *ns, void (*func)(ns_id_t, void *)) void ns_disable(struct ns *ns) { - return ns_disable_internal(ns); + ns_disable_internal(ns); } struct ns *ns_lookup(ns_id_t ns_id) @@ -378,12 +373,20 @@ struct ns *ns_lookup(ns_id_t ns_id) return ns_lookup_internal(ns_id); } -void ns_walk_func(int (*func)(struct ns *)) +void ns_walk_func(int (*func)(struct ns *, + void *param_in, + void **param_out), + void *param_in, + void **param_out) { struct ns *ns = NULL; + int ret; - RB_FOREACH (ns, ns_head, &ns_tree) - func(ns); + RB_FOREACH (ns, ns_head, &ns_tree) { + ret = func(ns, param_in, param_out); + if (ret == NS_WALK_STOP) + return; + } } const char *ns_get_name(struct ns *ns) @@ -430,7 +433,7 @@ char *ns_netns_pathname(struct vty *vty, const char *name) /* relevant pathname */ char tmp_name[PATH_MAX]; - snprintf(tmp_name, PATH_MAX, "%s/%s", NS_RUN_DIR, name); + snprintf(tmp_name, sizeof(tmp_name), "%s/%s", NS_RUN_DIR, name); result = realpath(tmp_name, pathname); } @@ -583,9 +586,27 @@ int ns_socket(int domain, int type, int protocol, ns_id_t ns_id) return ret; } -ns_id_t ns_get_default_id(void) +/* if relative link_nsid matches default netns, + * then return default absolute netns value + * otherwise, return NS_UNKNOWN + */ +ns_id_t ns_id_get_absolute(ns_id_t ns_id_reference, ns_id_t link_nsid) +{ + struct ns *ns; + + ns = ns_lookup(ns_id_reference); + if (!ns) + return NS_UNKNOWN; + + if (ns->relative_default_ns != link_nsid) + return NS_UNKNOWN; + + ns = ns_get_default(); + assert(ns); + return ns->ns_id; +} + +struct ns *ns_get_default(void) { - if (default_ns) - return default_ns->ns_id; - return NS_DEFAULT_INTERNAL; + return default_ns; } diff --git a/lib/netns_other.c b/lib/netns_other.c index 4c7be05fab..dc6bbbe479 100644 --- a/lib/netns_other.c +++ b/lib/netns_other.c @@ -34,7 +34,7 @@ static inline int ns_compare(const struct ns *ns, const struct ns *ns2); RB_GENERATE(ns_head, ns, entry, ns_compare) -struct ns_head ns_tree = RB_INITIALIZER(&ns_tree); +static struct ns_head ns_tree = RB_INITIALIZER(&ns_tree); static inline int ns_compare(const struct ns *a, const struct ns *b) { @@ -82,7 +82,7 @@ const char *ns_get_name(struct ns *ns) } /* only called from vrf ( when removing netns from vrf) - * or at VRF or logical router termination + * or at VRF termination */ void ns_delete(struct ns *ns) { @@ -110,13 +110,6 @@ void ns_init(void) { } -/* API to retrieve default NS */ -ns_id_t ns_get_default_id(void) -{ - return NS_UNKNOWN; -} - - /* API that can be used to change from NS */ int ns_switchback_to_initial(void) { diff --git a/lib/network.c b/lib/network.c index 411661a5e1..d2482bd55e 100644 --- a/lib/network.c +++ b/lib/network.c @@ -121,3 +121,21 @@ float ntohf(float net) { return htonf(net); } + +/** + * Helper function that returns a random long value. The main purpose of + * this function is to hide a `random()` call that gets flagged by coverity + * scan and put it into one place. + * + * The main usage of this function should be for generating jitter or weak + * random values for simple purposes. + * + * See 'man 3 random' for more information. + * + * \returns random long integer. + */ +long frr_weak_random(void) +{ + /* coverity[dont_call] */ + return random(); +} diff --git a/lib/network.h b/lib/network.h index a00c5a0a65..83c9e59e76 100644 --- a/lib/network.h +++ b/lib/network.h @@ -45,6 +45,8 @@ extern int set_cloexec(int fd); extern float htonf(float); extern float ntohf(float); +extern long frr_weak_random(void); + #ifdef __cplusplus } #endif diff --git a/lib/nexthop.c b/lib/nexthop.c index 8e16e70590..3b6ad0e54c 100644 --- a/lib/nexthop.c +++ b/lib/nexthop.c @@ -23,84 +23,221 @@ #include "table.h" #include "memory.h" #include "command.h" -#include "if.h" #include "log.h" #include "sockunion.h" #include "linklist.h" -#include "thread.h" #include "prefix.h" #include "nexthop.h" #include "mpls.h" #include "jhash.h" +#include "printfrr.h" +#include "vrf.h" +#include "nexthop_group.h" DEFINE_MTYPE_STATIC(LIB, NEXTHOP, "Nexthop") DEFINE_MTYPE_STATIC(LIB, NH_LABEL, "Nexthop label") -/* check if nexthops are same, non-recursive */ -int nexthop_same_no_recurse(const struct nexthop *next1, - const struct nexthop *next2) +static int _nexthop_labels_cmp(const struct nexthop *nh1, + const struct nexthop *nh2) { - if (next1->type != next2->type) + const struct mpls_label_stack *nhl1 = NULL; + const struct mpls_label_stack *nhl2 = NULL; + + nhl1 = nh1->nh_label; + nhl2 = nh2->nh_label; + + /* No labels is a match */ + if (!nhl1 && !nhl2) return 0; - switch (next1->type) { + if (nhl1 && !nhl2) + return 1; + + if (nhl2 && !nhl1) + return -1; + + if (nhl1->num_labels > nhl2->num_labels) + return 1; + + if (nhl1->num_labels < nhl2->num_labels) + return -1; + + return memcmp(nhl1->label, nhl2->label, + (nhl1->num_labels * sizeof(mpls_label_t))); +} + +int nexthop_g_addr_cmp(enum nexthop_types_t type, const union g_addr *addr1, + const union g_addr *addr2) +{ + int ret = 0; + + switch (type) { case NEXTHOP_TYPE_IPV4: case NEXTHOP_TYPE_IPV4_IFINDEX: - if (!IPV4_ADDR_SAME(&next1->gate.ipv4, &next2->gate.ipv4)) - return 0; - if (next1->ifindex && (next1->ifindex != next2->ifindex)) - return 0; + ret = IPV4_ADDR_CMP(&addr1->ipv4, &addr2->ipv4); + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + ret = IPV6_ADDR_CMP(&addr1->ipv6, &addr2->ipv6); break; case NEXTHOP_TYPE_IFINDEX: - if (next1->ifindex != next2->ifindex) - return 0; + case NEXTHOP_TYPE_BLACKHOLE: + /* No addr here */ break; + } + + return ret; +} + +static int _nexthop_gateway_cmp(const struct nexthop *nh1, + const struct nexthop *nh2) +{ + return nexthop_g_addr_cmp(nh1->type, &nh1->gate, &nh2->gate); +} + +static int _nexthop_source_cmp(const struct nexthop *nh1, + const struct nexthop *nh2) +{ + return nexthop_g_addr_cmp(nh1->type, &nh1->src, &nh2->src); +} + +static int _nexthop_cmp_no_labels(const struct nexthop *next1, + const struct nexthop *next2) +{ + int ret = 0; + + if (next1->vrf_id < next2->vrf_id) + return -1; + + if (next1->vrf_id > next2->vrf_id) + return 1; + + if (next1->type < next2->type) + return -1; + + if (next1->type > next2->type) + return 1; + + if (next1->weight < next2->weight) + return -1; + + if (next1->weight > next2->weight) + return 1; + + switch (next1->type) { + case NEXTHOP_TYPE_IPV4: case NEXTHOP_TYPE_IPV6: - if (!IPV6_ADDR_SAME(&next1->gate.ipv6, &next2->gate.ipv6)) - return 0; + ret = _nexthop_gateway_cmp(next1, next2); + if (ret != 0) + return ret; break; + case NEXTHOP_TYPE_IPV4_IFINDEX: case NEXTHOP_TYPE_IPV6_IFINDEX: - if (!IPV6_ADDR_SAME(&next1->gate.ipv6, &next2->gate.ipv6)) - return 0; - if (next1->ifindex != next2->ifindex) - return 0; + ret = _nexthop_gateway_cmp(next1, next2); + if (ret != 0) + return ret; + /* Intentional Fall-Through */ + case NEXTHOP_TYPE_IFINDEX: + if (next1->ifindex < next2->ifindex) + return -1; + + if (next1->ifindex > next2->ifindex) + return 1; break; - default: - /* do nothing */ + case NEXTHOP_TYPE_BLACKHOLE: + if (next1->bh_type < next2->bh_type) + return -1; + + if (next1->bh_type > next2->bh_type) + return 1; break; } - return 1; + + if (next1->srte_color < next2->srte_color) + return -1; + if (next1->srte_color > next2->srte_color) + return 1; + + ret = _nexthop_source_cmp(next1, next2); + if (ret != 0) + goto done; + + if (!CHECK_FLAG(next1->flags, NEXTHOP_FLAG_HAS_BACKUP) && + !CHECK_FLAG(next2->flags, NEXTHOP_FLAG_HAS_BACKUP)) + return 0; + + if (!CHECK_FLAG(next1->flags, NEXTHOP_FLAG_HAS_BACKUP) && + CHECK_FLAG(next2->flags, NEXTHOP_FLAG_HAS_BACKUP)) + return -1; + + if (CHECK_FLAG(next1->flags, NEXTHOP_FLAG_HAS_BACKUP) && + !CHECK_FLAG(next2->flags, NEXTHOP_FLAG_HAS_BACKUP)) + return 1; + + if (next1->backup_num == 0 && next2->backup_num == 0) + goto done; + + if (next1->backup_num < next2->backup_num) + return -1; + + if (next1->backup_num > next2->backup_num) + return 1; + + ret = memcmp(next1->backup_idx, + next2->backup_idx, next1->backup_num); + +done: + return ret; +} + +int nexthop_cmp(const struct nexthop *next1, const struct nexthop *next2) +{ + int ret = 0; + + ret = _nexthop_cmp_no_labels(next1, next2); + if (ret != 0) + return ret; + + ret = _nexthop_labels_cmp(next1, next2); + + return ret; } -int nexthop_same_firsthop(struct nexthop *next1, struct nexthop *next2) +bool nexthop_same_firsthop(const struct nexthop *next1, + const struct nexthop *next2) { + /* Map the TYPE_IPx types to TYPE_IPx_IFINDEX */ int type1 = NEXTHOP_FIRSTHOPTYPE(next1->type); int type2 = NEXTHOP_FIRSTHOPTYPE(next2->type); if (type1 != type2) - return 0; + return false; + + if (next1->vrf_id != next2->vrf_id) + return false; + switch (type1) { case NEXTHOP_TYPE_IPV4_IFINDEX: if (!IPV4_ADDR_SAME(&next1->gate.ipv4, &next2->gate.ipv4)) - return 0; + return false; if (next1->ifindex != next2->ifindex) - return 0; + return false; break; case NEXTHOP_TYPE_IFINDEX: if (next1->ifindex != next2->ifindex) - return 0; + return false; break; case NEXTHOP_TYPE_IPV6_IFINDEX: if (!IPV6_ADDR_SAME(&next1->gate.ipv6, &next2->gate.ipv6)) - return 0; + return false; if (next1->ifindex != next2->ifindex) - return 0; + return false; break; default: /* do nothing */ break; } - return 1; + return true; } /* @@ -108,7 +245,7 @@ int nexthop_same_firsthop(struct nexthop *next1, struct nexthop *next2) */ const char *nexthop_type_to_str(enum nexthop_types_t nh_type) { - static const char *desc[] = { + static const char *const desc[] = { "none", "Directly connected", "IPv4 nexthop", "IPv4 nexthop with ifindex", "IPv6 nexthop", "IPv6 nexthop with ifindex", @@ -121,32 +258,33 @@ const char *nexthop_type_to_str(enum nexthop_types_t nh_type) /* * Check if the labels match for the 2 nexthops specified. */ -int nexthop_labels_match(const struct nexthop *nh1, const struct nexthop *nh2) +bool nexthop_labels_match(const struct nexthop *nh1, const struct nexthop *nh2) { - const struct mpls_label_stack *nhl1, *nhl2; - - nhl1 = nh1->nh_label; - nhl2 = nh2->nh_label; - - /* No labels is a match */ - if (!nhl1 && !nhl2) - return 1; - - if (!nhl1 || !nhl2) - return 0; - - if (nhl1->num_labels != nhl2->num_labels) - return 0; - - if (memcmp(nhl1->label, nhl2->label, nhl1->num_labels)) - return 0; + if (_nexthop_labels_cmp(nh1, nh2) != 0) + return false; - return 1; + return true; } struct nexthop *nexthop_new(void) { - return XCALLOC(MTYPE_NEXTHOP, sizeof(struct nexthop)); + struct nexthop *nh; + + nh = XCALLOC(MTYPE_NEXTHOP, sizeof(struct nexthop)); + + /* + * Default the weight to 1 here for all nexthops. + * The linux kernel does some weird stuff with adding +1 to + * all nexthop weights it gets over netlink. + * To handle this, just default everything to 1 right from + * from the beginning so we don't have to special case + * default weights in the linux netlink code. + * + * 1 should be a valid on all platforms anyway. + */ + nh->weight = 1; + + return nh; } /* Free nexthop. */ @@ -180,71 +318,147 @@ bool nexthop_same(const struct nexthop *nh1, const struct nexthop *nh2) if (nh1 == nh2) return true; - if (nh1->vrf_id != nh2->vrf_id) + if (nexthop_cmp(nh1, nh2) != 0) return false; - if (nh1->type != nh2->type) + return true; +} + +bool nexthop_same_no_labels(const struct nexthop *nh1, + const struct nexthop *nh2) +{ + if (nh1 && !nh2) return false; - switch (nh1->type) { - case NEXTHOP_TYPE_IFINDEX: - if (nh1->ifindex != nh2->ifindex) - return false; - break; - case NEXTHOP_TYPE_IPV4: - if (nh1->gate.ipv4.s_addr != nh2->gate.ipv4.s_addr) - return false; - break; - case NEXTHOP_TYPE_IPV4_IFINDEX: - if (nh1->gate.ipv4.s_addr != nh2->gate.ipv4.s_addr) - return false; - if (nh1->ifindex != nh2->ifindex) - return false; - break; - case NEXTHOP_TYPE_IPV6: - if (memcmp(&nh1->gate.ipv6, &nh2->gate.ipv6, 16)) - return false; - break; - case NEXTHOP_TYPE_IPV6_IFINDEX: - if (memcmp(&nh1->gate.ipv6, &nh2->gate.ipv6, 16)) - return false; - if (nh1->ifindex != nh2->ifindex) - return false; - break; - case NEXTHOP_TYPE_BLACKHOLE: - if (nh1->bh_type != nh2->bh_type) - return false; - break; - } + if (!nh1 && nh2) + return false; - /* Compare labels too (if present) */ - return (!!nexthop_labels_match(nh1, nh2)); + if (nh1 == nh2) + return true; + + if (_nexthop_cmp_no_labels(nh1, nh2) != 0) + return false; + + return true; +} + +/* + * Allocate a new nexthop object and initialize it from various args. + */ +struct nexthop *nexthop_from_ifindex(ifindex_t ifindex, vrf_id_t vrf_id) +{ + struct nexthop *nexthop; + + nexthop = nexthop_new(); + nexthop->type = NEXTHOP_TYPE_IFINDEX; + nexthop->ifindex = ifindex; + nexthop->vrf_id = vrf_id; + + return nexthop; +} + +struct nexthop *nexthop_from_ipv4(const struct in_addr *ipv4, + const struct in_addr *src, + vrf_id_t vrf_id) +{ + struct nexthop *nexthop; + + nexthop = nexthop_new(); + nexthop->type = NEXTHOP_TYPE_IPV4; + nexthop->vrf_id = vrf_id; + nexthop->gate.ipv4 = *ipv4; + if (src) + nexthop->src.ipv4 = *src; + + return nexthop; +} + +struct nexthop *nexthop_from_ipv4_ifindex(const struct in_addr *ipv4, + const struct in_addr *src, + ifindex_t ifindex, vrf_id_t vrf_id) +{ + struct nexthop *nexthop; + + nexthop = nexthop_new(); + nexthop->type = NEXTHOP_TYPE_IPV4_IFINDEX; + nexthop->vrf_id = vrf_id; + nexthop->gate.ipv4 = *ipv4; + if (src) + nexthop->src.ipv4 = *src; + nexthop->ifindex = ifindex; + + return nexthop; +} + +struct nexthop *nexthop_from_ipv6(const struct in6_addr *ipv6, + vrf_id_t vrf_id) +{ + struct nexthop *nexthop; + + nexthop = nexthop_new(); + nexthop->vrf_id = vrf_id; + nexthop->type = NEXTHOP_TYPE_IPV6; + nexthop->gate.ipv6 = *ipv6; + + return nexthop; +} + +struct nexthop *nexthop_from_ipv6_ifindex(const struct in6_addr *ipv6, + ifindex_t ifindex, vrf_id_t vrf_id) +{ + struct nexthop *nexthop; + + nexthop = nexthop_new(); + nexthop->vrf_id = vrf_id; + nexthop->type = NEXTHOP_TYPE_IPV6_IFINDEX; + nexthop->gate.ipv6 = *ipv6; + nexthop->ifindex = ifindex; + + return nexthop; +} + +struct nexthop *nexthop_from_blackhole(enum blackhole_type bh_type) +{ + struct nexthop *nexthop; + + nexthop = nexthop_new(); + nexthop->vrf_id = VRF_DEFAULT; + nexthop->type = NEXTHOP_TYPE_BLACKHOLE; + nexthop->bh_type = bh_type; + + return nexthop; } /* Update nexthop with label information. */ -void nexthop_add_labels(struct nexthop *nexthop, enum lsp_types_t type, - uint8_t num_labels, mpls_label_t *label) +void nexthop_add_labels(struct nexthop *nexthop, enum lsp_types_t ltype, + uint8_t num_labels, const mpls_label_t *labels) { struct mpls_label_stack *nh_label; int i; - nexthop->nh_label_type = type; + if (num_labels == 0) + return; + + /* Enforce limit on label stack size */ + if (num_labels > MPLS_MAX_LABELS) + num_labels = MPLS_MAX_LABELS; + + nexthop->nh_label_type = ltype; + nh_label = XCALLOC(MTYPE_NH_LABEL, sizeof(struct mpls_label_stack) + num_labels * sizeof(mpls_label_t)); nh_label->num_labels = num_labels; for (i = 0; i < num_labels; i++) - nh_label->label[i] = *(label + i); + nh_label->label[i] = *(labels + i); nexthop->nh_label = nh_label; } /* Free label information of nexthop, if present. */ void nexthop_del_labels(struct nexthop *nexthop) { - if (nexthop->nh_label) { - XFREE(MTYPE_NH_LABEL, nexthop->nh_label); - nexthop->nh_label_type = ZEBRA_LSP_NONE; - } + XFREE(MTYPE_NH_LABEL, nexthop->nh_label); + nexthop->nh_label_type = ZEBRA_LSP_NONE; } const char *nexthop2str(const struct nexthop *nexthop, char *str, int size) @@ -289,7 +503,7 @@ const char *nexthop2str(const struct nexthop *nexthop, char *str, int size) * left branch is 'resolved' and right branch is 'next': * https://en.wikipedia.org/wiki/Tree_traversal#/media/File:Sorted_binary_tree_preorder.svg */ -struct nexthop *nexthop_next(struct nexthop *nexthop) +struct nexthop *nexthop_next(const struct nexthop *nexthop) { if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) return nexthop->resolved; @@ -304,30 +518,43 @@ struct nexthop *nexthop_next(struct nexthop *nexthop) return NULL; } -unsigned int nexthop_level(struct nexthop *nexthop) +/* Return the next nexthop in the tree that is resolved and active */ +struct nexthop *nexthop_next_active_resolved(const struct nexthop *nexthop) +{ + struct nexthop *next = nexthop_next(nexthop); + + while (next + && (CHECK_FLAG(next->flags, NEXTHOP_FLAG_RECURSIVE) + || !CHECK_FLAG(next->flags, NEXTHOP_FLAG_ACTIVE))) + next = nexthop_next(next); + + return next; +} + +unsigned int nexthop_level(const struct nexthop *nexthop) { unsigned int rv = 0; - for (struct nexthop *par = nexthop->rparent; par; par = par->rparent) + for (const struct nexthop *par = nexthop->rparent; + par; par = par->rparent) rv++; return rv; } -uint32_t nexthop_hash(const struct nexthop *nexthop) +/* Only hash word-sized things, let cmp do the rest. */ +uint32_t nexthop_hash_quick(const struct nexthop *nexthop) { uint32_t key = 0x45afe398; + int i; key = jhash_3words(nexthop->type, nexthop->vrf_id, nexthop->nh_label_type, key); - /* gate and blackhole are together in a union */ - key = jhash(&nexthop->gate, sizeof(nexthop->gate), key); - key = jhash(&nexthop->src, sizeof(nexthop->src), key); - key = jhash(&nexthop->rmap_src, sizeof(nexthop->rmap_src), key); if (nexthop->nh_label) { int labels = nexthop->nh_label->num_labels; - int i = 0; + + i = 0; while (labels >= 3) { key = jhash_3words(nexthop->nh_label->label[i], @@ -350,16 +577,271 @@ uint32_t nexthop_hash(const struct nexthop *nexthop) key = jhash_1word(nexthop->nh_label->label[i], key); } - switch (nexthop->type) { - case NEXTHOP_TYPE_IPV4_IFINDEX: - case NEXTHOP_TYPE_IPV6_IFINDEX: - case NEXTHOP_TYPE_IFINDEX: - key = jhash_1word(nexthop->ifindex, key); - break; - case NEXTHOP_TYPE_BLACKHOLE: - case NEXTHOP_TYPE_IPV4: - case NEXTHOP_TYPE_IPV6: - break; + key = jhash_2words(nexthop->ifindex, + CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK), + key); + + /* Include backup nexthops, if present */ + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) { + int backups = nexthop->backup_num; + + i = 0; + + while (backups >= 3) { + key = jhash_3words(nexthop->backup_idx[i], + nexthop->backup_idx[i + 1], + nexthop->backup_idx[i + 2], key); + backups -= 3; + i += 3; + } + + while (backups >= 2) { + key = jhash_2words(nexthop->backup_idx[i], + nexthop->backup_idx[i + 1], key); + backups -= 2; + i += 2; + } + + if (backups >= 1) + key = jhash_1word(nexthop->backup_idx[i], key); } + + return key; +} + + +#define GATE_SIZE 4 /* Number of uint32_t words in struct g_addr */ + +/* For a more granular hash */ +uint32_t nexthop_hash(const struct nexthop *nexthop) +{ + uint32_t gate_src_rmap_raw[GATE_SIZE * 3] = {}; + /* Get all the quick stuff */ + uint32_t key = nexthop_hash_quick(nexthop); + + assert(((sizeof(nexthop->gate) + sizeof(nexthop->src) + + sizeof(nexthop->rmap_src)) + / 3) + == (GATE_SIZE * sizeof(uint32_t))); + + memcpy(gate_src_rmap_raw, &nexthop->gate, GATE_SIZE); + memcpy(gate_src_rmap_raw + GATE_SIZE, &nexthop->src, GATE_SIZE); + memcpy(gate_src_rmap_raw + (2 * GATE_SIZE), &nexthop->rmap_src, + GATE_SIZE); + + key = jhash2(gate_src_rmap_raw, (GATE_SIZE * 3), key); + return key; } + +void nexthop_copy_no_recurse(struct nexthop *copy, + const struct nexthop *nexthop, + struct nexthop *rparent) +{ + copy->vrf_id = nexthop->vrf_id; + copy->ifindex = nexthop->ifindex; + copy->type = nexthop->type; + copy->flags = nexthop->flags; + copy->weight = nexthop->weight; + + assert(nexthop->backup_num < NEXTHOP_MAX_BACKUPS); + copy->backup_num = nexthop->backup_num; + if (copy->backup_num > 0) + memcpy(copy->backup_idx, nexthop->backup_idx, copy->backup_num); + + copy->srte_color = nexthop->srte_color; + memcpy(©->gate, &nexthop->gate, sizeof(nexthop->gate)); + memcpy(©->src, &nexthop->src, sizeof(nexthop->src)); + memcpy(©->rmap_src, &nexthop->rmap_src, sizeof(nexthop->rmap_src)); + copy->rparent = rparent; + if (nexthop->nh_label) + nexthop_add_labels(copy, nexthop->nh_label_type, + nexthop->nh_label->num_labels, + &nexthop->nh_label->label[0]); +} + +void nexthop_copy(struct nexthop *copy, const struct nexthop *nexthop, + struct nexthop *rparent) +{ + nexthop_copy_no_recurse(copy, nexthop, rparent); + + /* Bit of a special case here, we need to handle the case + * of a nexthop resolving to a group. Hence, we need to + * use a nexthop_group API. + */ + if (CHECK_FLAG(copy->flags, NEXTHOP_FLAG_RECURSIVE)) + copy_nexthops(©->resolved, nexthop->resolved, copy); +} + +struct nexthop *nexthop_dup_no_recurse(const struct nexthop *nexthop, + struct nexthop *rparent) +{ + struct nexthop *new = nexthop_new(); + + nexthop_copy_no_recurse(new, nexthop, rparent); + return new; +} + +struct nexthop *nexthop_dup(const struct nexthop *nexthop, + struct nexthop *rparent) +{ + struct nexthop *new = nexthop_new(); + + nexthop_copy(new, nexthop, rparent); + return new; +} + +/* + * Parse one or more backup index values, as comma-separated numbers, + * into caller's array of uint8_ts. The array must be NEXTHOP_MAX_BACKUPS + * in size. Mails back the number of values converted, and returns 0 on + * success, <0 if an error in parsing. + */ +int nexthop_str2backups(const char *str, int *num_backups, + uint8_t *backups) +{ + char *ostr; /* copy of string (start) */ + char *lstr; /* working copy of string */ + char *nump; /* pointer to next segment */ + char *endp; /* end pointer */ + int i, ret; + uint8_t tmp[NEXTHOP_MAX_BACKUPS]; + uint32_t lval; + + /* Copy incoming string; the parse is destructive */ + lstr = ostr = XSTRDUP(MTYPE_TMP, str); + *num_backups = 0; + ret = 0; + + for (i = 0; i < NEXTHOP_MAX_BACKUPS && lstr; i++) { + nump = strsep(&lstr, ","); + lval = strtoul(nump, &endp, 10); + + /* Format check */ + if (*endp != '\0') { + ret = -1; + break; + } + + /* Empty value */ + if (endp == nump) { + ret = -1; + break; + } + + /* Limit to one octet */ + if (lval > 255) { + ret = -1; + break; + } + + tmp[i] = lval; + } + + /* Excess values */ + if (ret == 0 && i == NEXTHOP_MAX_BACKUPS && lstr) + ret = -1; + + if (ret == 0) { + *num_backups = i; + memcpy(backups, tmp, i); + } + + XFREE(MTYPE_TMP, ostr); + + return ret; +} + +/* + * nexthop printing variants: + * %pNHvv + * via 1.2.3.4 + * via 1.2.3.4, eth0 + * is directly connected, eth0 + * unreachable (blackhole) + * %pNHv + * 1.2.3.4 + * 1.2.3.4, via eth0 + * directly connected, eth0 + * unreachable (blackhole) + * %pNHs + * nexthop2str() + */ +printfrr_ext_autoreg_p("NH", printfrr_nh) +static ssize_t printfrr_nh(char *buf, size_t bsz, const char *fmt, + int prec, const void *ptr) +{ + const struct nexthop *nexthop = ptr; + struct fbuf fb = { .buf = buf, .pos = buf, .len = bsz - 1 }; + bool do_ifi = false; + const char *s, *v_is = "", *v_via = "", *v_viaif = "via "; + ssize_t ret = 3; + + /* NULL-check */ + if (nexthop == NULL) { + if (fmt[2] == 'v' && fmt[3] == 'v') + ret++; + + strlcpy(buf, "NULL", bsz); + + return ret; + } + + switch (fmt[2]) { + case 'v': + if (fmt[3] == 'v') { + v_is = "is "; + v_via = "via "; + v_viaif = ""; + ret++; + } + + switch (nexthop->type) { + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + bprintfrr(&fb, "%s%pI4", v_via, &nexthop->gate.ipv4); + do_ifi = true; + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + bprintfrr(&fb, "%s%pI6", v_via, &nexthop->gate.ipv6); + do_ifi = true; + break; + case NEXTHOP_TYPE_IFINDEX: + bprintfrr(&fb, "%sdirectly connected, %s", v_is, + ifindex2ifname(nexthop->ifindex, + nexthop->vrf_id)); + break; + case NEXTHOP_TYPE_BLACKHOLE: + switch (nexthop->bh_type) { + case BLACKHOLE_REJECT: + s = " (ICMP unreachable)"; + break; + case BLACKHOLE_ADMINPROHIB: + s = " (ICMP admin-prohibited)"; + break; + case BLACKHOLE_NULL: + s = " (blackhole)"; + break; + default: + s = ""; + break; + } + bprintfrr(&fb, "unreachable%s", s); + break; + default: + break; + } + if (do_ifi && nexthop->ifindex) + bprintfrr(&fb, ", %s%s", v_viaif, ifindex2ifname( + nexthop->ifindex, + nexthop->vrf_id)); + + *fb.pos = '\0'; + return ret; + case 's': + nexthop2str(nexthop, buf, bsz); + return 3; + } + return 0; +} diff --git a/lib/nexthop.h b/lib/nexthop.h index 663acaeb69..cadcea1f41 100644 --- a/lib/nexthop.h +++ b/lib/nexthop.h @@ -25,6 +25,7 @@ #include "prefix.h" #include "mpls.h" +#include "vxlan.h" #ifdef __cplusplus extern "C" { @@ -60,6 +61,16 @@ enum blackhole_type { ? (type) \ : ((type) | 1) +enum nh_encap_type { + NET_VXLAN = 100, /* value copied from FPM_NH_ENCAP_VXLAN. */ +}; + +/* Fixed limit on the number of backup nexthops per primary nexthop */ +#define NEXTHOP_MAX_BACKUPS 8 + +/* Backup index value is limited */ +#define NEXTHOP_BACKUP_IDX_MAX 255 + /* Nexthop structure. */ struct nexthop { struct nexthop *next; @@ -79,10 +90,16 @@ struct nexthop { #define NEXTHOP_FLAG_ACTIVE (1 << 0) /* This nexthop is alive. */ #define NEXTHOP_FLAG_FIB (1 << 1) /* FIB nexthop. */ #define NEXTHOP_FLAG_RECURSIVE (1 << 2) /* Recursive nexthop. */ -#define NEXTHOP_FLAG_ONLINK (1 << 3) /* Nexthop should be installed onlink. */ -#define NEXTHOP_FLAG_MATCHED (1 << 4) /* Already matched vs a nexthop */ -#define NEXTHOP_FLAG_DUPLICATE (1 << 5) /* nexthop duplicates another active one */ -#define NEXTHOP_FLAG_RNH_FILTERED (1 << 6) /* rmap filtered, used by rnh */ +#define NEXTHOP_FLAG_ONLINK (1 << 3) /* Nexthop should be installed + * onlink. + */ +#define NEXTHOP_FLAG_DUPLICATE (1 << 4) /* nexthop duplicates another + * active one + */ +#define NEXTHOP_FLAG_RNH_FILTERED (1 << 5) /* rmap filtered, used by rnh */ +#define NEXTHOP_FLAG_HAS_BACKUP (1 << 6) /* Backup nexthop index is set */ +#define NEXTHOP_FLAG_SRTE (1 << 7) /* SR-TE color used for BGP traffic */ + #define NEXTHOP_IS_ACTIVE(flags) \ (CHECK_FLAG(flags, NEXTHOP_FLAG_ACTIVE) \ && !CHECK_FLAG(flags, NEXTHOP_FLAG_DUPLICATE)) @@ -110,17 +127,59 @@ struct nexthop { /* Label(s) associated with this nexthop. */ struct mpls_label_stack *nh_label; + + /* Weight of the nexthop ( for unequal cost ECMP ) */ + uint8_t weight; + + /* Count and index of corresponding backup nexthop(s) in a backup list; + * only meaningful if the HAS_BACKUP flag is set. + */ + uint8_t backup_num; + uint8_t backup_idx[NEXTHOP_MAX_BACKUPS]; + + /* Encapsulation information. */ + enum nh_encap_type nh_encap_type; + union { + vni_t vni; + } nh_encap; + + /* SR-TE color used for matching SR-TE policies */ + uint32_t srte_color; }; +/* Utility to append one nexthop to another. */ +#define NEXTHOP_APPEND(to, new) \ + do { \ + (to)->next = (new); \ + (new)->prev = (to); \ + (new)->next = NULL; \ + } while (0) + struct nexthop *nexthop_new(void); void nexthop_free(struct nexthop *nexthop); void nexthops_free(struct nexthop *nexthop); -void nexthop_add_labels(struct nexthop *, enum lsp_types_t, uint8_t, - mpls_label_t *); +void nexthop_add_labels(struct nexthop *nexthop, enum lsp_types_t ltype, + uint8_t num_labels, const mpls_label_t *labels); void nexthop_del_labels(struct nexthop *); +/* + * Allocate a new nexthop object and initialize it from various args. + */ +struct nexthop *nexthop_from_ifindex(ifindex_t ifindex, vrf_id_t vrf_id); +struct nexthop *nexthop_from_ipv4(const struct in_addr *ipv4, + const struct in_addr *src, + vrf_id_t vrf_id); +struct nexthop *nexthop_from_ipv4_ifindex(const struct in_addr *ipv4, + const struct in_addr *src, + ifindex_t ifindex, vrf_id_t vrf_id); +struct nexthop *nexthop_from_ipv6(const struct in6_addr *ipv6, + vrf_id_t vrf_id); +struct nexthop *nexthop_from_ipv6_ifindex(const struct in6_addr *ipv6, + ifindex_t ifindex, vrf_id_t vrf_id); +struct nexthop *nexthop_from_blackhole(enum blackhole_type bh_type); + /* * Hash a nexthop. Suitable for use with hash tables. * @@ -137,20 +196,61 @@ void nexthop_del_labels(struct nexthop *); * 32-bit hash of nexthop */ uint32_t nexthop_hash(const struct nexthop *nexthop); +/* + * Hash a nexthop only on word-sized attributes: + * - vrf_id + * - ifindex + * - type + * - (some) flags + */ +uint32_t nexthop_hash_quick(const struct nexthop *nexthop); extern bool nexthop_same(const struct nexthop *nh1, const struct nexthop *nh2); +extern bool nexthop_same_no_labels(const struct nexthop *nh1, + const struct nexthop *nh2); +extern int nexthop_cmp(const struct nexthop *nh1, const struct nexthop *nh2); +extern int nexthop_g_addr_cmp(enum nexthop_types_t type, + const union g_addr *addr1, + const union g_addr *addr2); extern const char *nexthop_type_to_str(enum nexthop_types_t nh_type); -extern int nexthop_same_no_recurse(const struct nexthop *next1, - const struct nexthop *next2); -extern int nexthop_labels_match(const struct nexthop *nh1, - const struct nexthop *nh2); -extern int nexthop_same_firsthop(struct nexthop *next1, struct nexthop *next2); +extern bool nexthop_labels_match(const struct nexthop *nh1, + const struct nexthop *nh2); +extern bool nexthop_same_firsthop(const struct nexthop *next1, + const struct nexthop *next2); extern const char *nexthop2str(const struct nexthop *nexthop, char *str, int size); -extern struct nexthop *nexthop_next(struct nexthop *nexthop); -extern unsigned int nexthop_level(struct nexthop *nexthop); +extern struct nexthop *nexthop_next(const struct nexthop *nexthop); +extern struct nexthop * +nexthop_next_active_resolved(const struct nexthop *nexthop); +extern unsigned int nexthop_level(const struct nexthop *nexthop); +/* Copies to an already allocated nexthop struct */ +extern void nexthop_copy(struct nexthop *copy, const struct nexthop *nexthop, + struct nexthop *rparent); +/* Copies to an already allocated nexthop struct, not including recurse info */ +extern void nexthop_copy_no_recurse(struct nexthop *copy, + const struct nexthop *nexthop, + struct nexthop *rparent); +/* Duplicates a nexthop and returns the newly allocated nexthop */ +extern struct nexthop *nexthop_dup(const struct nexthop *nexthop, + struct nexthop *rparent); +/* Duplicates a nexthop and returns the newly allocated nexthop */ +extern struct nexthop *nexthop_dup_no_recurse(const struct nexthop *nexthop, + struct nexthop *rparent); + +/* + * Parse one or more backup index values, as comma-separated numbers, + * into caller's array of uint8_ts. The array must be NEXTHOP_MAX_BACKUPS + * in size. Mails back the number of values converted, and returns 0 on + * success, <0 if an error in parsing. + */ +int nexthop_str2backups(const char *str, int *num_backups, + uint8_t *backups); + +#ifdef _FRR_ATTRIBUTE_PRINTFRR +#pragma FRR printfrr_ext "%pNH" (struct nexthop *) +#endif #ifdef __cplusplus } diff --git a/lib/nexthop_group.c b/lib/nexthop_group.c index ed22f64494..687cac4062 100644 --- a/lib/nexthop_group.c +++ b/lib/nexthop_group.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -33,6 +34,18 @@ DEFINE_MTYPE_STATIC(LIB, NEXTHOP_GROUP, "Nexthop Group") +/* + * Internal struct used to hold nhg config strings + */ +struct nexthop_hold { + char *nhvrf_name; + union sockunion *addr; + char *intf; + char *labels; + uint32_t weight; + char *backup_str; +}; + struct nexthop_group_hooks { void (*new)(const char *name); void (*add_nexthop)(const struct nexthop_group_cmd *nhg, @@ -59,6 +72,16 @@ nexthop_group_cmd_compare(const struct nexthop_group_cmd *nhgc1, return strcmp(nhgc1->name, nhgc2->name); } +static struct nexthop *nexthop_group_tail(const struct nexthop_group *nhg) +{ + struct nexthop *nexthop = nhg->nexthop; + + while (nexthop && nexthop->next) + nexthop = nexthop->next; + + return nexthop; +} + uint8_t nexthop_group_nexthop_num(const struct nexthop_group *nhg) { struct nexthop *nhop; @@ -70,6 +93,17 @@ uint8_t nexthop_group_nexthop_num(const struct nexthop_group *nhg) return num; } +uint8_t nexthop_group_nexthop_num_no_recurse(const struct nexthop_group *nhg) +{ + struct nexthop *nhop; + uint8_t num = 0; + + for (nhop = nhg->nexthop; nhop; nhop = nhop->next) + num++; + + return num; +} + uint8_t nexthop_group_active_nexthop_num(const struct nexthop_group *nhg) { struct nexthop *nhop; @@ -83,7 +117,22 @@ uint8_t nexthop_group_active_nexthop_num(const struct nexthop_group *nhg) return num; } -struct nexthop *nexthop_exists(struct nexthop_group *nhg, struct nexthop *nh) +uint8_t +nexthop_group_active_nexthop_num_no_recurse(const struct nexthop_group *nhg) +{ + struct nexthop *nhop; + uint8_t num = 0; + + for (nhop = nhg->nexthop; nhop; nhop = nhop->next) { + if (CHECK_FLAG(nhop->flags, NEXTHOP_FLAG_ACTIVE)) + num++; + } + + return num; +} + +struct nexthop *nexthop_exists(const struct nexthop_group *nhg, + const struct nexthop *nh) { struct nexthop *nexthop; @@ -95,18 +144,153 @@ struct nexthop *nexthop_exists(struct nexthop_group *nhg, struct nexthop *nh) return NULL; } +/* + * Helper that locates a nexthop in an nhg config list. Note that + * this uses a specific matching / equality rule that's different from + * the complete match performed by 'nexthop_same()'. + */ +static struct nexthop *nhg_nh_find(const struct nexthop_group *nhg, + const struct nexthop *nh) +{ + struct nexthop *nexthop; + int ret; + + /* We compare: vrf, gateway, and interface */ + + for (nexthop = nhg->nexthop; nexthop; nexthop = nexthop->next) { + + /* Compare vrf and type */ + if (nexthop->vrf_id != nh->vrf_id) + continue; + if (nexthop->type != nh->type) + continue; + + /* Compare gateway */ + switch (nexthop->type) { + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV6: + ret = nexthop_g_addr_cmp(nexthop->type, + &nexthop->gate, &nh->gate); + if (ret != 0) + continue; + break; + case NEXTHOP_TYPE_IPV4_IFINDEX: + case NEXTHOP_TYPE_IPV6_IFINDEX: + ret = nexthop_g_addr_cmp(nexthop->type, + &nexthop->gate, &nh->gate); + if (ret != 0) + continue; + /* Intentional Fall-Through */ + case NEXTHOP_TYPE_IFINDEX: + if (nexthop->ifindex != nh->ifindex) + continue; + break; + case NEXTHOP_TYPE_BLACKHOLE: + if (nexthop->bh_type != nh->bh_type) + continue; + break; + } + + return nexthop; + } + + return NULL; +} + +static bool +nexthop_group_equal_common(const struct nexthop_group *nhg1, + const struct nexthop_group *nhg2, + uint8_t (*nexthop_group_nexthop_num_func)( + const struct nexthop_group *nhg)) +{ + if (nhg1 && !nhg2) + return false; + + if (!nhg1 && nhg2) + return false; + + if (nhg1 == nhg2) + return true; + + if (nexthop_group_nexthop_num_func(nhg1) + != nexthop_group_nexthop_num_func(nhg2)) + return false; + + return true; +} + +/* This assumes ordered */ +bool nexthop_group_equal_no_recurse(const struct nexthop_group *nhg1, + const struct nexthop_group *nhg2) +{ + struct nexthop *nh1 = NULL; + struct nexthop *nh2 = NULL; + + if (!nexthop_group_equal_common(nhg1, nhg2, + &nexthop_group_nexthop_num_no_recurse)) + return false; + + for (nh1 = nhg1->nexthop, nh2 = nhg2->nexthop; nh1 || nh2; + nh1 = nh1->next, nh2 = nh2->next) { + if (nh1 && !nh2) + return false; + if (!nh1 && nh2) + return false; + if (!nexthop_same(nh1, nh2)) + return false; + } + + return true; +} + +/* This assumes ordered */ +bool nexthop_group_equal(const struct nexthop_group *nhg1, + const struct nexthop_group *nhg2) +{ + struct nexthop *nh1 = NULL; + struct nexthop *nh2 = NULL; + + if (!nexthop_group_equal_common(nhg1, nhg2, &nexthop_group_nexthop_num)) + return false; + + for (nh1 = nhg1->nexthop, nh2 = nhg2->nexthop; nh1 || nh2; + nh1 = nexthop_next(nh1), nh2 = nexthop_next(nh2)) { + if (nh1 && !nh2) + return false; + if (!nh1 && nh2) + return false; + if (!nexthop_same(nh1, nh2)) + return false; + } + + return true; +} struct nexthop_group *nexthop_group_new(void) { return XCALLOC(MTYPE_NEXTHOP_GROUP, sizeof(struct nexthop_group)); } +void nexthop_group_copy(struct nexthop_group *to, + const struct nexthop_group *from) +{ + /* Copy everything, including recursive info */ + copy_nexthops(&to->nexthop, from->nexthop, NULL); +} + void nexthop_group_delete(struct nexthop_group **nhg) { + /* OK to call with NULL group */ + if ((*nhg) == NULL) + return; + + if ((*nhg)->nexthop) + nexthops_free((*nhg)->nexthop); + XFREE(MTYPE_NEXTHOP_GROUP, *nhg); } /* Add nexthop to the end of a nexthop list. */ -void nexthop_add(struct nexthop **target, struct nexthop *nexthop) +void _nexthop_add(struct nexthop **target, struct nexthop *nexthop) { struct nexthop *last; @@ -119,8 +303,61 @@ void nexthop_add(struct nexthop **target, struct nexthop *nexthop) nexthop->prev = last; } +/* Add nexthop to sorted list of nexthops */ +static void _nexthop_add_sorted(struct nexthop **head, + struct nexthop *nexthop) +{ + struct nexthop *position, *prev; + + assert(!nexthop->next); + + for (position = *head, prev = NULL; position; + prev = position, position = position->next) { + if (nexthop_cmp(position, nexthop) > 0) { + nexthop->next = position; + nexthop->prev = prev; + + if (nexthop->prev) + nexthop->prev->next = nexthop; + else + *head = nexthop; + + position->prev = nexthop; + return; + } + } + + nexthop->prev = prev; + if (prev) + prev->next = nexthop; + else + *head = nexthop; +} + +void nexthop_group_add_sorted(struct nexthop_group *nhg, + struct nexthop *nexthop) +{ + struct nexthop *tail; + + assert(!nexthop->next); + + /* Try to just append to the end first; + * trust the list is already sorted + */ + tail = nexthop_group_tail(nhg); + + if (tail && (nexthop_cmp(tail, nexthop) < 0)) { + tail->next = nexthop; + nexthop->prev = tail; + + return; + } + + _nexthop_add_sorted(&nhg->nexthop, nexthop); +} + /* Delete nexthop from a nexthop list. */ -void nexthop_del(struct nexthop_group *nhg, struct nexthop *nh) +void _nexthop_del(struct nexthop_group *nhg, struct nexthop *nh) { struct nexthop *nexthop; @@ -143,6 +380,59 @@ void nexthop_del(struct nexthop_group *nhg, struct nexthop *nh) nh->next = NULL; } +/* Unlink a nexthop from the list it's on, unconditionally */ +static void nexthop_unlink(struct nexthop_group *nhg, struct nexthop *nexthop) +{ + + if (nexthop->prev) + nexthop->prev->next = nexthop->next; + else { + assert(nhg->nexthop == nexthop); + assert(nexthop->prev == NULL); + nhg->nexthop = nexthop->next; + } + + if (nexthop->next) + nexthop->next->prev = nexthop->prev; + + nexthop->prev = NULL; + nexthop->next = NULL; +} + +/* + * Copy a list of nexthops in 'nh' to an nhg, enforcing canonical sort order + */ +void nexthop_group_copy_nh_sorted(struct nexthop_group *nhg, + const struct nexthop *nh) +{ + struct nexthop *nexthop, *tail; + const struct nexthop *nh1; + + /* We'll try to append to the end of the new list; + * if the original list in nh is already sorted, this eliminates + * lots of comparison operations. + */ + tail = nexthop_group_tail(nhg); + + for (nh1 = nh; nh1; nh1 = nh1->next) { + nexthop = nexthop_dup(nh1, NULL); + + if (tail && (nexthop_cmp(tail, nexthop) < 0)) { + tail->next = nexthop; + nexthop->prev = tail; + + tail = nexthop; + continue; + } + + _nexthop_add_sorted(&nhg->nexthop, nexthop); + + if (tail == NULL) + tail = nexthop; + } +} + +/* Copy a list of nexthops, no effort made to sort or order them. */ void copy_nexthops(struct nexthop **tnh, const struct nexthop *nh, struct nexthop *rparent) { @@ -150,29 +440,12 @@ void copy_nexthops(struct nexthop **tnh, const struct nexthop *nh, const struct nexthop *nh1; for (nh1 = nh; nh1; nh1 = nh1->next) { - nexthop = nexthop_new(); - nexthop->vrf_id = nh1->vrf_id; - nexthop->ifindex = nh1->ifindex; - nexthop->type = nh1->type; - nexthop->flags = nh1->flags; - memcpy(&nexthop->gate, &nh1->gate, sizeof(nh1->gate)); - memcpy(&nexthop->src, &nh1->src, sizeof(nh1->src)); - memcpy(&nexthop->rmap_src, &nh1->rmap_src, - sizeof(nh1->rmap_src)); - nexthop->rparent = rparent; - if (nh1->nh_label) - nexthop_add_labels(nexthop, nh1->nh_label_type, - nh1->nh_label->num_labels, - &nh1->nh_label->label[0]); - nexthop_add(tnh, nexthop); - - if (CHECK_FLAG(nh1->flags, NEXTHOP_FLAG_RECURSIVE)) - copy_nexthops(&nexthop->resolved, nh1->resolved, - nexthop); + nexthop = nexthop_dup(nh1, rparent); + _nexthop_add(tnh, nexthop); } } -uint32_t nexthop_group_hash(const struct nexthop_group *nhg) +uint32_t nexthop_group_hash_no_recurse(const struct nexthop_group *nhg) { struct nexthop *nh; uint32_t key = 0; @@ -187,6 +460,35 @@ uint32_t nexthop_group_hash(const struct nexthop_group *nhg) return key; } +uint32_t nexthop_group_hash(const struct nexthop_group *nhg) +{ + struct nexthop *nh; + uint32_t key = 0; + + for (ALL_NEXTHOPS_PTR(nhg, nh)) + key = jhash_1word(nexthop_hash(nh), key); + + return key; +} + +void nexthop_group_mark_duplicates(struct nexthop_group *nhg) +{ + struct nexthop *nexthop, *prev; + + for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { + UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE); + for (ALL_NEXTHOPS_PTR(nhg, prev)) { + if (prev == nexthop) + break; + if (nexthop_same_firsthop(nexthop, prev)) { + SET_FLAG(nexthop->flags, + NEXTHOP_FLAG_DUPLICATE); + break; + } + } + } +} + static void nhgc_delete_nexthops(struct nexthop_group_cmd *nhgc) { struct nexthop *nexthop; @@ -195,7 +497,7 @@ static void nhgc_delete_nexthops(struct nexthop_group_cmd *nhgc) while (nexthop) { struct nexthop *next = nexthop_next(nexthop); - nexthop_del(&nhgc->nhg, nexthop); + _nexthop_del(&nhgc->nhg, nexthop); if (nhg_hooks.del_nexthop) nhg_hooks.del_nexthop(nhgc, nexthop); @@ -254,7 +556,11 @@ static int nhgl_cmp(struct nexthop_hold *nh1, struct nexthop_hold *nh2) if (ret) return ret; - return nhgc_cmp_helper(nh1->nhvrf_name, nh2->nhvrf_name); + ret = nhgc_cmp_helper(nh1->nhvrf_name, nh2->nhvrf_name); + if (ret) + return ret; + + return nhgc_cmp_helper(nh1->labels, nh2->labels); } static void nhgl_delete(struct nexthop_hold *nh) @@ -266,6 +572,8 @@ static void nhgl_delete(struct nexthop_hold *nh) if (nh->addr) sockunion_free(nh->addr); + XFREE(MTYPE_TMP, nh->labels); + XFREE(MTYPE_TMP, nh); } @@ -303,12 +611,13 @@ static void nhgc_delete(struct nexthop_group_cmd *nhgc) list_delete(&nhgc->nhg_list); + QOBJ_UNREG(nhgc); XFREE(MTYPE_TMP, nhgc); } DEFINE_QOBJ_TYPE(nexthop_group_cmd) -DEFUN_NOSH(nexthop_group, nexthop_group_cmd, "nexthop-group NAME", +DEFUN_NOSH(nexthop_group, nexthop_group_cmd, "nexthop-group NHGNAME", "Enter into the nexthop-group submode\n" "Specify the NAME of the nexthop-group\n") { @@ -321,7 +630,7 @@ DEFUN_NOSH(nexthop_group, nexthop_group_cmd, "nexthop-group NAME", return CMD_SUCCESS; } -DEFUN_NOSH(no_nexthop_group, no_nexthop_group_cmd, "no nexthop-group NAME", +DEFUN_NOSH(no_nexthop_group, no_nexthop_group_cmd, "no nexthop-group NHGNAME", NO_STR "Delete the nexthop-group\n" "Specify the NAME of the nexthop-group\n") @@ -336,10 +645,37 @@ DEFUN_NOSH(no_nexthop_group, no_nexthop_group_cmd, "no nexthop-group NAME", return CMD_SUCCESS; } +DEFPY(nexthop_group_backup, nexthop_group_backup_cmd, + "backup-group WORD$name", + "Specify a group name containing backup nexthops\n" + "The name of the backup group\n") +{ + VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc); + + strlcpy(nhgc->backup_list_name, name, sizeof(nhgc->backup_list_name)); + + return CMD_SUCCESS; +} + +DEFPY(no_nexthop_group_backup, no_nexthop_group_backup_cmd, + "no backup-group [WORD$name]", + NO_STR + "Clear group name containing backup nexthops\n" + "The name of the backup group\n") +{ + VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc); + + nhgc->backup_list_name[0] = 0; + + return CMD_SUCCESS; +} + static void nexthop_group_save_nhop(struct nexthop_group_cmd *nhgc, const char *nhvrf_name, const union sockunion *addr, - const char *intf) + const char *intf, const char *labels, + const uint32_t weight, + const char *backup_str) { struct nexthop_hold *nh; @@ -351,10 +687,24 @@ static void nexthop_group_save_nhop(struct nexthop_group_cmd *nhgc, nh->intf = XSTRDUP(MTYPE_TMP, intf); if (addr) nh->addr = sockunion_dup(addr); + if (labels) + nh->labels = XSTRDUP(MTYPE_TMP, labels); + + nh->weight = weight; + + if (backup_str) + nh->backup_str = XSTRDUP(MTYPE_TMP, backup_str); listnode_add_sort(nhgc->nhg_list, nh); } +/* + * Remove config info about a nexthop from group 'nhgc'. Note that we + * use only a subset of the available attributes here to determine + * a 'match'. + * Note that this doesn't change the list of nexthops, only the config + * information. + */ static void nexthop_group_unsave_nhop(struct nexthop_group_cmd *nhgc, const char *nhvrf_name, const union sockunion *addr, @@ -364,9 +714,9 @@ static void nexthop_group_unsave_nhop(struct nexthop_group_cmd *nhgc, struct listnode *node; for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nh)) { - if (nhgc_cmp_helper(nhvrf_name, nh->nhvrf_name) == 0 && - nhgc_addr_cmp_helper(addr, nh->addr) == 0 && - nhgc_cmp_helper(intf, nh->intf) == 0) + if (nhgc_cmp_helper(nhvrf_name, nh->nhvrf_name) == 0 + && nhgc_addr_cmp_helper(addr, nh->addr) == 0 + && nhgc_cmp_helper(intf, nh->intf) == 0) break; } @@ -380,11 +730,21 @@ static void nexthop_group_unsave_nhop(struct nexthop_group_cmd *nhgc, nhgl_delete(nh); } +/* + * Parse the config strings we support for a single nexthop. This gets used + * in a couple of different ways, and we distinguish between transient + * failures - such as a still-unprocessed interface - and fatal errors + * from label-string parsing. + */ static bool nexthop_group_parse_nexthop(struct nexthop *nhop, const union sockunion *addr, - const char *intf, const char *name) + const char *intf, const char *name, + const char *labels, int *lbl_ret, + uint32_t weight, const char *backup_str) { + int ret = 0; struct vrf *vrf; + int num; memset(nhop, 0, sizeof(*nhop)); @@ -421,16 +781,62 @@ static bool nexthop_group_parse_nexthop(struct nexthop *nhop, } else nhop->type = NEXTHOP_TYPE_IFINDEX; + if (labels) { + uint8_t num = 0; + mpls_label_t larray[MPLS_MAX_LABELS]; + + ret = mpls_str2label(labels, &num, larray); + + /* Return label parse result */ + if (lbl_ret) + *lbl_ret = ret; + + if (ret < 0) + return false; + else if (num > 0) + nexthop_add_labels(nhop, ZEBRA_LSP_NONE, + num, larray); + } + + nhop->weight = weight; + + if (backup_str) { + /* Parse backup indexes */ + ret = nexthop_str2backups(backup_str, + &num, nhop->backup_idx); + if (ret == 0) { + SET_FLAG(nhop->flags, NEXTHOP_FLAG_HAS_BACKUP); + nhop->backup_num = num; + } else + return false; + } + return true; } +/* + * Wrapper to parse the strings in a 'nexthop_hold' + */ +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)); +} + DEFPY(ecmp_nexthops, ecmp_nexthops_cmd, "[no] nexthop\ <\ $addr [INTERFACE$intf]\ |INTERFACE$intf\ >\ - [nexthop-vrf NAME$name]", + [{ \ + nexthop-vrf NAME$vrf_name \ + |label WORD \ + |weight (1-255) \ + |backup-idx WORD \ + }]", NO_STR "Specify one of the nexthops in this ECMP group\n" "v4 Address\n" @@ -438,14 +844,34 @@ DEFPY(ecmp_nexthops, ecmp_nexthops_cmd, "Interface to use\n" "Interface to use\n" "If the nexthop is in a different vrf tell us\n" - "The nexthop-vrf Name\n") + "The nexthop-vrf Name\n" + "Specify label(s) for this nexthop\n" + "One or more labels in the range (16-1048575) separated by '/'\n" + "Weight to be used by the nexthop for purposes of ECMP\n" + "Weight value to be used\n" + "Specify backup nexthop indexes in another group\n" + "One or more indexes in the range (0-254) separated by ','\n") { VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc); struct nexthop nhop; struct nexthop *nh; + int lbl_ret = 0; bool legal; + int num; + uint8_t backups[NEXTHOP_MAX_BACKUPS]; + bool yes = !no; + + /* Pre-parse backup string to validate */ + if (backup_idx) { + lbl_ret = nexthop_str2backups(backup_idx, &num, backups); + if (lbl_ret < 0) { + vty_out(vty, "%% Invalid backups\n"); + return CMD_WARNING_CONFIG_FAILED; + } + } - legal = nexthop_group_parse_nexthop(&nhop, addr, intf, name); + legal = nexthop_group_parse_nexthop(&nhop, addr, intf, vrf_name, label, + &lbl_ret, weight, backup_idx); if (nhop.type == NEXTHOP_TYPE_IPV6 && IN6_IS_ADDR_LINKLOCAL(&nhop.gate.ipv6)) { @@ -454,59 +880,107 @@ DEFPY(ecmp_nexthops, ecmp_nexthops_cmd, return CMD_WARNING_CONFIG_FAILED; } - nh = nexthop_exists(&nhgc->nhg, &nhop); + /* Handle label-string errors */ + if (!legal && lbl_ret < 0) { + switch (lbl_ret) { + case -1: + vty_out(vty, "%% Malformed label(s)\n"); + break; + case -2: + vty_out(vty, + "%% Cannot use reserved label(s) (%d-%d)\n", + MPLS_LABEL_RESERVED_MIN, + MPLS_LABEL_RESERVED_MAX); + break; + case -3: + vty_out(vty, + "%% Too many labels. Enter %d or fewer\n", + MPLS_MAX_LABELS); + break; + } + return CMD_WARNING_CONFIG_FAILED; + } - if (no) { - nexthop_group_unsave_nhop(nhgc, name, addr, intf); - if (nh) { - nexthop_del(&nhgc->nhg, nh); + /* Look for an existing nexthop in the config. Note that the test + * here tests only some attributes - it's not a complete comparison. + * Note that we've got two kinds of objects to manage: 'nexthop_hold' + * that represent config that may or may not be valid (yet), and + * actual nexthops that have been validated and parsed. + */ + nh = nhg_nh_find(&nhgc->nhg, &nhop); - if (nhg_hooks.del_nexthop) - nhg_hooks.del_nexthop(nhgc, nh); + /* Always attempt to remove old config info. */ + nexthop_group_unsave_nhop(nhgc, vrf_name, addr, intf); - nexthop_free(nh); - } - } else if (!nh) { - /* must be adding new nexthop since !no and !nexthop_exists */ + /* Remove any existing nexthop, for delete and replace cases. */ + if (nh) { + nexthop_unlink(&nhgc->nhg, nh); + + if (nhg_hooks.del_nexthop) + nhg_hooks.del_nexthop(nhgc, nh); + + nexthop_free(nh); + } + if (yes) { + /* Add/replace case: capture nexthop if valid, and capture + * config info always. + */ if (legal) { nh = nexthop_new(); memcpy(nh, &nhop, sizeof(nhop)); - nexthop_add(&nhgc->nhg.nexthop, nh); + _nexthop_add(&nhgc->nhg.nexthop, nh); } - nexthop_group_save_nhop(nhgc, name, addr, intf); + /* Save config always */ + nexthop_group_save_nhop(nhgc, vrf_name, addr, intf, label, + weight, backup_idx); if (legal && nhg_hooks.add_nexthop) nhg_hooks.add_nexthop(nhgc, nh); } + if (intf) { + struct interface *ifp = if_lookup_by_name_all_vrf(intf); + + if (ifp) + ifp->configured = true; + } return CMD_SUCCESS; } -struct cmd_node nexthop_group_node = { - NH_GROUP_NODE, - "%s(config-nh-group)# ", - 1 +static int nexthop_group_write(struct vty *vty); +static struct cmd_node nexthop_group_node = { + .name = "nexthop-group", + .node = NH_GROUP_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-nh-group)# ", + .config_write = nexthop_group_write, }; -void nexthop_group_write_nexthop(struct vty *vty, struct nexthop *nh) +void nexthop_group_write_nexthop_simple(struct vty *vty, + const struct nexthop *nh, + char *altifname) { char buf[100]; - struct vrf *vrf; + char *ifname; vty_out(vty, "nexthop "); + if (altifname) + ifname = altifname; + else + ifname = (char *)ifindex2ifname(nh->ifindex, nh->vrf_id); + switch (nh->type) { case NEXTHOP_TYPE_IFINDEX: - vty_out(vty, "%s", ifindex2ifname(nh->ifindex, nh->vrf_id)); + vty_out(vty, "%s", ifname); break; case NEXTHOP_TYPE_IPV4: vty_out(vty, "%s", inet_ntoa(nh->gate.ipv4)); break; case NEXTHOP_TYPE_IPV4_IFINDEX: - vty_out(vty, "%s %s", inet_ntoa(nh->gate.ipv4), - ifindex2ifname(nh->ifindex, nh->vrf_id)); + vty_out(vty, "%s %s", inet_ntoa(nh->gate.ipv4), ifname); break; case NEXTHOP_TYPE_IPV6: vty_out(vty, "%s", @@ -515,21 +989,112 @@ void nexthop_group_write_nexthop(struct vty *vty, struct nexthop *nh) case NEXTHOP_TYPE_IPV6_IFINDEX: vty_out(vty, "%s %s", inet_ntop(AF_INET6, &nh->gate.ipv6, buf, sizeof(buf)), - ifindex2ifname(nh->ifindex, nh->vrf_id)); + ifname); break; case NEXTHOP_TYPE_BLACKHOLE: break; } +} + +void nexthop_group_write_nexthop(struct vty *vty, const struct nexthop *nh) +{ + struct vrf *vrf; + int i; + + nexthop_group_write_nexthop_simple(vty, nh, NULL); if (nh->vrf_id != VRF_DEFAULT) { vrf = vrf_lookup_by_id(nh->vrf_id); - vty_out(vty, " nexthop-vrf %s", vrf->name); + vty_out(vty, " nexthop-vrf %s", VRF_LOGNAME(vrf)); } + + if (nh->nh_label && nh->nh_label->num_labels > 0) { + char buf[200]; + + mpls_label2str(nh->nh_label->num_labels, + nh->nh_label->label, + buf, sizeof(buf), 0); + vty_out(vty, " label %s", buf); + } + + if (nh->weight) + vty_out(vty, " weight %u", nh->weight); + + if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_HAS_BACKUP)) { + vty_out(vty, " backup-idx %d", nh->backup_idx[0]); + + for (i = 1; i < nh->backup_num; i++) + vty_out(vty, ",%d", nh->backup_idx[i]); + } + vty_out(vty, "\n"); } +void nexthop_group_json_nexthop(json_object *j, const struct nexthop *nh) +{ + char buf[100]; + struct vrf *vrf; + json_object *json_backups = NULL; + int i; + + switch (nh->type) { + case NEXTHOP_TYPE_IFINDEX: + json_object_string_add(j, "nexthop", + ifindex2ifname(nh->ifindex, nh->vrf_id)); + break; + case NEXTHOP_TYPE_IPV4: + json_object_string_add(j, "nexthop", inet_ntoa(nh->gate.ipv4)); + break; + case NEXTHOP_TYPE_IPV4_IFINDEX: + json_object_string_add(j, "nexthop", inet_ntoa(nh->gate.ipv4)); + json_object_string_add(j, "vrfId", + ifindex2ifname(nh->ifindex, nh->vrf_id)); + break; + case NEXTHOP_TYPE_IPV6: + json_object_string_add( + j, "nexthop", + inet_ntop(AF_INET6, &nh->gate.ipv6, buf, sizeof(buf))); + break; + case NEXTHOP_TYPE_IPV6_IFINDEX: + json_object_string_add( + j, "nexthop", + inet_ntop(AF_INET6, &nh->gate.ipv6, buf, sizeof(buf))); + json_object_string_add(j, "vrfId", + ifindex2ifname(nh->ifindex, nh->vrf_id)); + break; + case NEXTHOP_TYPE_BLACKHOLE: + break; + } + + if (nh->vrf_id != VRF_DEFAULT) { + vrf = vrf_lookup_by_id(nh->vrf_id); + json_object_string_add(j, "targetVrf", vrf->name); + } + + if (nh->nh_label && nh->nh_label->num_labels > 0) { + char buf[200]; + + mpls_label2str(nh->nh_label->num_labels, nh->nh_label->label, + buf, sizeof(buf), 0); + json_object_string_add(j, "label", buf); + } + + if (nh->weight) + json_object_int_add(j, "weight", nh->weight); + + if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_HAS_BACKUP)) { + json_backups = json_object_new_array(); + for (i = 0; i < nh->backup_num; i++) + json_object_array_add( + json_backups, + json_object_new_int(nh->backup_idx[i])); + + json_object_object_add(j, "backupIdx", json_backups); + } +} + static void nexthop_group_write_nexthop_internal(struct vty *vty, - struct nexthop_hold *nh) + const struct nexthop_hold *nh) { char buf[100]; @@ -544,6 +1109,15 @@ static void nexthop_group_write_nexthop_internal(struct vty *vty, if (nh->nhvrf_name) vty_out(vty, " nexthop-vrf %s", nh->nhvrf_name); + if (nh->labels) + vty_out(vty, " label %s", nh->labels); + + if (nh->weight) + vty_out(vty, " weight %u", nh->weight); + + if (nh->backup_str) + vty_out(vty, " backup-idx %s", nh->backup_str); + vty_out(vty, "\n"); } @@ -557,6 +1131,10 @@ static int nexthop_group_write(struct vty *vty) vty_out(vty, "nexthop-group %s\n", nhgc->name); + if (nhgc->backup_list_name[0]) + vty_out(vty, " backup-group %s\n", + nhgc->backup_list_name); + for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nh)) { vty_out(vty, " "); nexthop_group_write_nexthop_internal(vty, nh); @@ -580,9 +1158,7 @@ void nexthop_group_enable_vrf(struct vrf *vrf) struct nexthop nhop; struct nexthop *nh; - if (!nexthop_group_parse_nexthop(&nhop, nhh->addr, - nhh->intf, - nhh->nhvrf_name)) + if (!nexthop_group_parse_nhh(&nhop, nhh)) continue; nh = nexthop_exists(&nhgc->nhg, &nhop); @@ -596,7 +1172,7 @@ void nexthop_group_enable_vrf(struct vrf *vrf) nh = nexthop_new(); memcpy(nh, &nhop, sizeof(nhop)); - nexthop_add(&nhgc->nhg.nexthop, nh); + _nexthop_add(&nhgc->nhg.nexthop, nh); if (nhg_hooks.add_nexthop) nhg_hooks.add_nexthop(nhgc, nh); @@ -616,9 +1192,7 @@ void nexthop_group_disable_vrf(struct vrf *vrf) struct nexthop nhop; struct nexthop *nh; - if (!nexthop_group_parse_nexthop(&nhop, nhh->addr, - nhh->intf, - nhh->nhvrf_name)) + if (!nexthop_group_parse_nhh(&nhop, nhh)) continue; nh = nexthop_exists(&nhgc->nhg, &nhop); @@ -629,7 +1203,7 @@ void nexthop_group_disable_vrf(struct vrf *vrf) if (nh->vrf_id != vrf->vrf_id) continue; - nexthop_del(&nhgc->nhg, nh); + _nexthop_del(&nhgc->nhg, nh); if (nhg_hooks.del_nexthop) nhg_hooks.del_nexthop(nhgc, nh); @@ -653,9 +1227,7 @@ void nexthop_group_interface_state_change(struct interface *ifp, for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) { struct nexthop nhop; - if (!nexthop_group_parse_nexthop( - &nhop, nhh->addr, nhh->intf, - nhh->nhvrf_name)) + if (!nexthop_group_parse_nhh(&nhop, nhh)) continue; switch (nhop.type) { @@ -676,10 +1248,11 @@ void nexthop_group_interface_state_change(struct interface *ifp, if (ifp->ifindex != nhop.ifindex) continue; + ifp->configured = true; nh = nexthop_new(); memcpy(nh, &nhop, sizeof(nhop)); - nexthop_add(&nhgc->nhg.nexthop, nh); + _nexthop_add(&nhgc->nhg.nexthop, nh); if (nhg_hooks.add_nexthop) nhg_hooks.add_nexthop(nhgc, nh); @@ -703,7 +1276,7 @@ void nexthop_group_interface_state_change(struct interface *ifp, if (oldifindex != nh->ifindex) continue; - nexthop_del(&nhgc->nhg, nh); + _nexthop_del(&nhgc->nhg, nh); if (nhg_hooks.del_nexthop) nhg_hooks.del_nexthop(nhgc, nh); @@ -714,6 +1287,19 @@ void nexthop_group_interface_state_change(struct interface *ifp, } } +static void nhg_name_autocomplete(vector comps, struct cmd_token *token) +{ + struct nexthop_group_cmd *nhgc; + + RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) { + vector_set(comps, XSTRDUP(MTYPE_COMPLETION, nhgc->name)); + } +} + +static const struct cmd_variable_handler nhg_name_handlers[] = { + {.tokenname = "NHGNAME", .completions = nhg_name_autocomplete}, + {.completions = NULL}}; + void nexthop_group_init(void (*new)(const char *name), void (*add_nexthop)(const struct nexthop_group_cmd *nhg, const struct nexthop *nhop), @@ -723,11 +1309,15 @@ void nexthop_group_init(void (*new)(const char *name), { RB_INIT(nhgc_entry_head, &nhgc_entries); - install_node(&nexthop_group_node, nexthop_group_write); + cmd_variable_handler_register(nhg_name_handlers); + + install_node(&nexthop_group_node); install_element(CONFIG_NODE, &nexthop_group_cmd); install_element(CONFIG_NODE, &no_nexthop_group_cmd); install_default(NH_GROUP_NODE); + install_element(NH_GROUP_NODE, &nexthop_group_backup_cmd); + install_element(NH_GROUP_NODE, &no_nexthop_group_backup_cmd); install_element(NH_GROUP_NODE, &ecmp_nexthops_cmd); memset(&nhg_hooks, 0, sizeof(nhg_hooks)); diff --git a/lib/nexthop_group.h b/lib/nexthop_group.h index 5adf2db937..5f7bde0def 100644 --- a/lib/nexthop_group.h +++ b/lib/nexthop_group.h @@ -22,6 +22,7 @@ #define __NEXTHOP_GROUP__ #include +#include "json.h" #ifdef __cplusplus extern "C" { @@ -42,12 +43,25 @@ struct nexthop_group { struct nexthop_group *nexthop_group_new(void); void nexthop_group_delete(struct nexthop_group **nhg); -void nexthop_add(struct nexthop **target, struct nexthop *nexthop); -void nexthop_del(struct nexthop_group *nhg, struct nexthop *nexthop); +void nexthop_group_copy(struct nexthop_group *to, + const struct nexthop_group *from); + +/* + * Copy a list of nexthops in 'nh' to an nhg, enforcing canonical sort order + */ +void nexthop_group_copy_nh_sorted(struct nexthop_group *nhg, + const struct nexthop *nh); + void copy_nexthops(struct nexthop **tnh, const struct nexthop *nh, struct nexthop *rparent); +uint32_t nexthop_group_hash_no_recurse(const struct nexthop_group *nhg); uint32_t nexthop_group_hash(const struct nexthop_group *nhg); +void nexthop_group_mark_duplicates(struct nexthop_group *nhg); + +/* Add a nexthop to a list, enforcing the canonical sort order. */ +void nexthop_group_add_sorted(struct nexthop_group *nhg, + struct nexthop *nexthop); /* The following for loop allows to iterate over the nexthop * structure of routes. @@ -68,17 +82,16 @@ uint32_t nexthop_group_hash(const struct nexthop_group *nhg); (nhop) = nexthop_next(nhop) -struct nexthop_hold { - char *nhvrf_name; - union sockunion *addr; - char *intf; -}; +#define NHGC_NAME_SIZE 80 struct nexthop_group_cmd { RB_ENTRY(nexthop_group_cmd) nhgc_entry; - char name[80]; + char name[NHGC_NAME_SIZE]; + + /* Name of group containing backup nexthops (if set) */ + char backup_list_name[NHGC_NAME_SIZE]; struct nexthop_group nhg; @@ -110,17 +123,35 @@ void nexthop_group_disable_vrf(struct vrf *vrf); void nexthop_group_interface_state_change(struct interface *ifp, ifindex_t oldifindex); -extern struct nexthop *nexthop_exists(struct nexthop_group *nhg, - struct nexthop *nh); +extern struct nexthop *nexthop_exists(const struct nexthop_group *nhg, + const struct nexthop *nh); +/* This assumes ordered */ +extern bool nexthop_group_equal_no_recurse(const struct nexthop_group *nhg1, + const struct nexthop_group *nhg2); + +/* This assumes ordered */ +extern bool nexthop_group_equal(const struct nexthop_group *nhg1, + const struct nexthop_group *nhg2); extern struct nexthop_group_cmd *nhgc_find(const char *name); -extern void nexthop_group_write_nexthop(struct vty *vty, struct nexthop *nh); +extern void nexthop_group_write_nexthop_simple(struct vty *vty, + const struct nexthop *nh, + char *altifname); +extern void nexthop_group_write_nexthop(struct vty *vty, + const struct nexthop *nh); + +extern void nexthop_group_json_nexthop(json_object *j, + const struct nexthop *nh); /* Return the number of nexthops in this nhg */ extern uint8_t nexthop_group_nexthop_num(const struct nexthop_group *nhg); extern uint8_t +nexthop_group_nexthop_num_no_recurse(const struct nexthop_group *nhg); +extern uint8_t nexthop_group_active_nexthop_num(const struct nexthop_group *nhg); +extern uint8_t +nexthop_group_active_nexthop_num_no_recurse(const struct nexthop_group *nhg); #ifdef __cplusplus } diff --git a/lib/logicalrouter.h b/lib/nexthop_group_private.h similarity index 51% rename from lib/logicalrouter.h rename to lib/nexthop_group_private.h index d18832ef70..4abda624ae 100644 --- a/lib/logicalrouter.h +++ b/lib/nexthop_group_private.h @@ -1,6 +1,7 @@ /* - * Logical Router related header. - * Copyright (C) 2018 6WIND S.A. + * Nexthop Group Private Functions. + * Copyright (C) 2019 Cumulus Networks, Inc. + * Stephen Worley * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -17,33 +18,26 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef _ZEBRA_LOGICAL_ROUTER_H -#define _ZEBRA_LOGICAL_ROUTER_H +/** + * These functions should only be used internally for nexthop groups + * and in certain special cases. Please use `lib/nexthop_group.h` for + * any general nexthop_group api needs. + */ + +#ifndef __NEXTHOP_GROUP_PRIVATE__ +#define __NEXTHOP_GROUP_PRIVATE__ + +#include #ifdef __cplusplus extern "C" { #endif -/* Logical Router Backend defines */ -#define LOGICALROUTER_BACKEND_OFF 0 -#define LOGICALROUTER_BACKEND_NETNS 1 - -/* - * Logical Router initializer/destructor - */ -extern void logicalrouter_init(int (*writefunc)(struct vty *vty)); -extern void logicalrouter_terminate(void); - -/* used to configure backend for logical router - * Currently, the whole NETNS feature is exclusively shared - * between logical router and VRF backend NETNS - * However, when logical router feature will be available, - * one can think of having exclusivity only per NETNS - */ -extern void logicalrouter_configure_backend(int backend_netns); +void _nexthop_add(struct nexthop **target, struct nexthop *nexthop); +void _nexthop_del(struct nexthop_group *nhg, struct nexthop *nexthop); #ifdef __cplusplus } #endif -#endif /*_ZEBRA_LOGICAL_ROUTER_H*/ +#endif /* __NEXTHOP_GROUP_PRIVATE__ */ diff --git a/lib/northbound.c b/lib/northbound.c index 5e031ac2ce..3ebbe29680 100644 --- a/lib/northbound.c +++ b/lib/northbound.c @@ -26,9 +26,11 @@ #include "command.h" #include "debug.h" #include "db.h" +#include "frr_pthread.h" #include "northbound.h" #include "northbound_cli.h" #include "northbound_db.h" +#include "frrstr.h" DEFINE_MTYPE_STATIC(LIB, NB_NODE, "Northbound Node") DEFINE_MTYPE_STATIC(LIB, NB_CONFIG, "Northbound Configuration") @@ -40,22 +42,47 @@ struct nb_config *running_config; /* Hash table of user pointers associated with configuration entries. */ static struct hash *running_config_entries; +/* Management lock for the running configuration. */ +static struct { + /* Mutex protecting this structure. */ + pthread_mutex_t mtx; + + /* Actual lock. */ + bool locked; + + /* Northbound client who owns this lock. */ + enum nb_client owner_client; + + /* Northbound user who owns this lock. */ + const void *owner_user; +} running_config_mgmt_lock; + +/* Knob to record config transaction */ +static bool nb_db_enabled; /* * Global lock used to prevent multiple configuration transactions from * happening concurrently. */ static bool transaction_in_progress; -static int nb_callback_configuration(const enum nb_event event, - struct nb_config_change *change); -static struct nb_transaction *nb_transaction_new(struct nb_config *config, - struct nb_config_cbs *changes, - enum nb_client client, - const char *comment); +static int nb_callback_pre_validate(struct nb_context *context, + const struct nb_node *nb_node, + const struct lyd_node *dnode, char *errmsg, + size_t errmsg_len); +static int nb_callback_configuration(struct nb_context *context, + const enum nb_event event, + struct nb_config_change *change, + char *errmsg, size_t errmsg_len); +static struct nb_transaction * +nb_transaction_new(struct nb_context *context, struct nb_config *config, + struct nb_config_cbs *changes, const char *comment, + char *errmsg, size_t errmsg_len); static void nb_transaction_free(struct nb_transaction *transaction); static int nb_transaction_process(enum nb_event event, - struct nb_transaction *transaction); -static void nb_transaction_apply_finish(struct nb_transaction *transaction); + struct nb_transaction *transaction, + char *errmsg, size_t errmsg_len); +static void nb_transaction_apply_finish(struct nb_transaction *transaction, + char *errmsg, size_t errmsg_len); static int nb_oper_data_iter_node(const struct lys_node *snode, const char *xpath, const void *list_entry, const struct yang_list_keys *list_keys, @@ -155,6 +182,25 @@ struct nb_node *nb_node_find(const char *xpath) return snode->priv; } +void nb_node_set_dependency_cbs(const char *dependency_xpath, + const char *dependant_xpath, + struct nb_dependency_callbacks *cbs) +{ + struct nb_node *dependency = nb_node_find(dependency_xpath); + struct nb_node *dependant = nb_node_find(dependant_xpath); + + if (!dependency || !dependant) + return; + + dependency->dep_cbs.get_dependant_xpath = cbs->get_dependant_xpath; + dependant->dep_cbs.get_dependency_xpath = cbs->get_dependency_xpath; +} + +bool nb_node_has_dependency(struct nb_node *node) +{ + return node->dep_cbs.get_dependency_xpath != NULL; +} + static int nb_node_validate_cb(const struct nb_node *nb_node, enum nb_operation operation, int callback_implemented, bool optional) @@ -163,7 +209,18 @@ static int nb_node_validate_cb(const struct nb_node *nb_node, valid = nb_operation_is_valid(operation, nb_node->snode); - if (!valid && callback_implemented) + /* + * Add an exception for operational data callbacks. A rw list usually + * doesn't need any associated operational data callbacks. But if this + * rw list is augmented by another module which adds state nodes under + * it, then this list will need to have the 'get_next()', 'get_keys()' + * and 'lookup_entry()' callbacks. As such, never log a warning when + * these callbacks are implemented when they are not needed, since this + * depends on context (e.g. some daemons might augment "frr-interface" + * while others don't). + */ + if (!valid && callback_implemented && operation != NB_OP_GET_NEXT + && operation != NB_OP_GET_KEYS && operation != NB_OP_LOOKUP_ENTRY) flog_warn(EC_LIB_NB_CB_UNNEEDED, "unneeded '%s' callback for '%s'", nb_operation_name(operation), nb_node->xpath); @@ -194,6 +251,8 @@ static unsigned int nb_node_validate_cbs(const struct nb_node *nb_node) !!nb_node->cbs.destroy, false); error += nb_node_validate_cb(nb_node, NB_OP_MOVE, !!nb_node->cbs.move, false); + error += nb_node_validate_cb(nb_node, NB_OP_PRE_VALIDATE, + !!nb_node->cbs.pre_validate, true); error += nb_node_validate_cb(nb_node, NB_OP_APPLY_FINISH, !!nb_node->cbs.apply_finish, true); error += nb_node_validate_cb(nb_node, NB_OP_GET_ELEM, @@ -315,23 +374,43 @@ static inline int nb_config_cb_compare(const struct nb_config_cb *a, return 1; /* - * Use XPath as a tie-breaker. This will naturally sort parent nodes - * before their children. + * Preserve the order of the configuration changes as told by libyang. + */ + if (a->seq < b->seq) + return -1; + if (a->seq > b->seq) + return 1; + + /* + * All 'apply_finish' callbacks have their sequence number set to zero. + * In this case, compare them using their dnode pointers (the order + * doesn't matter for callbacks that have the same priority). */ - return strcmp(a->xpath, b->xpath); + if (a->dnode < b->dnode) + return -1; + if (a->dnode > b->dnode) + return 1; + + return 0; } RB_GENERATE(nb_config_cbs, nb_config_cb, entry, nb_config_cb_compare); static void nb_config_diff_add_change(struct nb_config_cbs *changes, enum nb_operation operation, + uint32_t *seq, const struct lyd_node *dnode) { struct nb_config_change *change; + /* Ignore unimplemented nodes. */ + if (!dnode->schema->priv) + return; + change = XCALLOC(MTYPE_TMP, sizeof(*change)); change->cb.operation = operation; + change->cb.seq = *seq; + *seq = *seq + 1; change->cb.nb_node = dnode->schema->priv; - yang_dnode_get_path(dnode, change->cb.xpath, sizeof(change->cb.xpath)); change->cb.dnode = dnode; RB_INSERT(nb_config_cbs, changes, &change->cb); @@ -354,12 +433,16 @@ static void nb_config_diff_del_changes(struct nb_config_cbs *changes) * configurations. Given a new subtree, calculate all new YANG data nodes, * excluding default leafs and leaf-lists. This is a recursive function. */ -static void nb_config_diff_created(const struct lyd_node *dnode, +static void nb_config_diff_created(const struct lyd_node *dnode, uint32_t *seq, struct nb_config_cbs *changes) { enum nb_operation operation; struct lyd_node *child; + /* Ignore unimplemented nodes. */ + if (!dnode->schema->priv) + return; + switch (dnode->schema->nodetype) { case LYS_LEAF: case LYS_LEAFLIST: @@ -373,16 +456,17 @@ static void nb_config_diff_created(const struct lyd_node *dnode, else return; - nb_config_diff_add_change(changes, operation, dnode); + nb_config_diff_add_change(changes, operation, seq, dnode); break; case LYS_CONTAINER: case LYS_LIST: if (nb_operation_is_valid(NB_OP_CREATE, dnode->schema)) - nb_config_diff_add_change(changes, NB_OP_CREATE, dnode); + nb_config_diff_add_change(changes, NB_OP_CREATE, seq, + dnode); /* Process child nodes recursively. */ LY_TREE_FOR (dnode->child, child) { - nb_config_diff_created(child, changes); + nb_config_diff_created(child, seq, changes); } break; default: @@ -390,11 +474,15 @@ static void nb_config_diff_created(const struct lyd_node *dnode, } } -static void nb_config_diff_deleted(const struct lyd_node *dnode, +static void nb_config_diff_deleted(const struct lyd_node *dnode, uint32_t *seq, struct nb_config_cbs *changes) { + /* Ignore unimplemented nodes. */ + if (!dnode->schema->priv) + return; + if (nb_operation_is_valid(NB_OP_DESTROY, dnode->schema)) - nb_config_diff_add_change(changes, NB_OP_DESTROY, dnode); + nb_config_diff_add_change(changes, NB_OP_DESTROY, seq, dnode); else if (CHECK_FLAG(dnode->schema->nodetype, LYS_CONTAINER)) { struct lyd_node *child; @@ -405,7 +493,7 @@ static void nb_config_diff_deleted(const struct lyd_node *dnode, * when applicable (i.e. optional nodes). */ LY_TREE_FOR (dnode->child, child) { - nb_config_diff_deleted(child, changes); + nb_config_diff_deleted(child, seq, changes); } } } @@ -416,6 +504,7 @@ static void nb_config_diff(const struct nb_config *config1, struct nb_config_cbs *changes) { struct lyd_difflist *diff; + uint32_t seq = 0; diff = lyd_diff(config1->dnode, config2->dnode, LYD_DIFFOPT_WITHDEFAULTS); @@ -430,15 +519,16 @@ static void nb_config_diff(const struct nb_config *config1, switch (type) { case LYD_DIFF_CREATED: dnode = diff->second[i]; - nb_config_diff_created(dnode, changes); + nb_config_diff_created(dnode, &seq, changes); break; case LYD_DIFF_DELETED: dnode = diff->first[i]; - nb_config_diff_deleted(dnode, changes); + nb_config_diff_deleted(dnode, &seq, changes); break; case LYD_DIFF_CHANGED: dnode = diff->second[i]; - nb_config_diff_add_change(changes, NB_OP_MODIFY, dnode); + nb_config_diff_add_change(changes, NB_OP_MODIFY, &seq, + dnode); break; case LYD_DIFF_MOVEDAFTER1: case LYD_DIFF_MOVEDAFTER2: @@ -456,8 +546,9 @@ int nb_candidate_edit(struct nb_config *candidate, const struct yang_data *previous, const struct yang_data *data) { - struct lyd_node *dnode; + struct lyd_node *dnode, *dep_dnode; char xpath_edit[XPATH_MAXLEN]; + char dep_xpath[XPATH_MAXLEN]; /* Use special notation for leaf-lists (RFC 6020, section 9.13.5). */ if (nb_node->snode->nodetype == LYS_LEAFLIST) @@ -473,19 +564,34 @@ int nb_candidate_edit(struct nb_config *candidate, dnode = lyd_new_path(candidate->dnode, ly_native_ctx, xpath_edit, (void *)data->value, 0, LYD_PATH_OPT_UPDATE); - if (!dnode && ly_errno) { - flog_warn(EC_LIB_LIBYANG, "%s: lyd_new_path() failed", - __func__); - return NB_ERR; - } - - /* - * If a new node was created, call lyd_validate() only to create - * default child nodes. - */ if (dnode) { - lyd_schema_sort(dnode, 0); - lyd_validate(&dnode, LYD_OPT_CONFIG, ly_native_ctx); + /* + * create dependency + * + * dnode returned by the lyd_new_path may be from a + * different schema, so we need to update the nb_node + */ + nb_node = dnode->schema->priv; + if (nb_node->dep_cbs.get_dependency_xpath) { + nb_node->dep_cbs.get_dependency_xpath( + dnode, dep_xpath); + + ly_errno = 0; + dep_dnode = lyd_new_path(candidate->dnode, + ly_native_ctx, + dep_xpath, NULL, 0, + LYD_PATH_OPT_UPDATE); + if (!dep_dnode && ly_errno) { + flog_warn(EC_LIB_LIBYANG, + "%s: lyd_new_path(%s) failed", + __func__, dep_xpath); + return NB_ERR; + } + } + } else if (ly_errno) { + flog_warn(EC_LIB_LIBYANG, "%s: lyd_new_path(%s) failed", + __func__, xpath_edit); + return NB_ERR; } break; case NB_OP_DESTROY: @@ -496,6 +602,14 @@ int nb_candidate_edit(struct nb_config *candidate, * whether to ignore it or not. */ return NB_ERR_NOT_FOUND; + /* destroy dependant */ + if (nb_node->dep_cbs.get_dependant_xpath) { + nb_node->dep_cbs.get_dependant_xpath(dnode, dep_xpath); + + dep_dnode = yang_dnode_get(candidate->dnode, dep_xpath); + if (dep_dnode) + lyd_free(dep_dnode); + } lyd_free(dnode); break; case NB_OP_MOVE: @@ -538,27 +652,55 @@ int nb_candidate_update(struct nb_config *candidate) * WARNING: lyd_validate() can change the configuration as part of the * validation process. */ -static int nb_candidate_validate_yang(struct nb_config *candidate) +static int nb_candidate_validate_yang(struct nb_config *candidate, char *errmsg, + size_t errmsg_len) { - if (lyd_validate(&candidate->dnode, LYD_OPT_STRICT | LYD_OPT_CONFIG, + if (lyd_validate(&candidate->dnode, + LYD_OPT_STRICT | LYD_OPT_CONFIG | LYD_OPT_WHENAUTODEL, ly_native_ctx) - != 0) + != 0) { + yang_print_errors(ly_native_ctx, errmsg, errmsg_len); return NB_ERR_VALIDATION; + } return NB_OK; } /* Perform code-level validation using the northbound callbacks. */ -static int nb_candidate_validate_changes(struct nb_config *candidate, - struct nb_config_cbs *changes) +static int nb_candidate_validate_code(struct nb_context *context, + struct nb_config *candidate, + struct nb_config_cbs *changes, + char *errmsg, size_t errmsg_len) { struct nb_config_cb *cb; + struct lyd_node *root, *next, *child; + int ret; + + /* First validate the candidate as a whole. */ + LY_TREE_FOR (candidate->dnode, root) { + LY_TREE_DFS_BEGIN (root, next, child) { + struct nb_node *nb_node; + + nb_node = child->schema->priv; + if (!nb_node || !nb_node->cbs.pre_validate) + goto next; + ret = nb_callback_pre_validate(context, nb_node, child, + errmsg, errmsg_len); + if (ret != NB_OK) + return NB_ERR_VALIDATION; + + next: + LY_TREE_DFS_END(root, next, child); + } + } + + /* Now validate the configuration changes. */ RB_FOREACH (cb, nb_config_cbs, changes) { struct nb_config_change *change = (struct nb_config_change *)cb; - int ret; - ret = nb_callback_configuration(NB_EV_VALIDATE, change); + ret = nb_callback_configuration(context, NB_EV_VALIDATE, change, + errmsg, errmsg_len); if (ret != NB_OK) return NB_ERR_VALIDATION; } @@ -566,29 +708,36 @@ static int nb_candidate_validate_changes(struct nb_config *candidate, return NB_OK; } -int nb_candidate_validate(struct nb_config *candidate) +int nb_candidate_validate(struct nb_context *context, + struct nb_config *candidate, char *errmsg, + size_t errmsg_len) { struct nb_config_cbs changes; int ret; - if (nb_candidate_validate_yang(candidate) != NB_OK) + if (nb_candidate_validate_yang(candidate, errmsg, sizeof(errmsg_len)) + != NB_OK) return NB_ERR_VALIDATION; RB_INIT(nb_config_cbs, &changes); nb_config_diff(running_config, candidate, &changes); - ret = nb_candidate_validate_changes(candidate, &changes); + ret = nb_candidate_validate_code(context, candidate, &changes, errmsg, + errmsg_len); nb_config_diff_del_changes(&changes); return ret; } -int nb_candidate_commit_prepare(struct nb_config *candidate, - enum nb_client client, const char *comment, - struct nb_transaction **transaction) +int nb_candidate_commit_prepare(struct nb_context *context, + struct nb_config *candidate, + const char *comment, + struct nb_transaction **transaction, + char *errmsg, size_t errmsg_len) { struct nb_config_cbs changes; - if (nb_candidate_validate_yang(candidate) != NB_OK) { + if (nb_candidate_validate_yang(candidate, errmsg, errmsg_len) + != NB_OK) { flog_warn(EC_LIB_NB_CANDIDATE_INVALID, "%s: failed to validate candidate configuration", __func__); @@ -597,10 +746,16 @@ int nb_candidate_commit_prepare(struct nb_config *candidate, RB_INIT(nb_config_cbs, &changes); nb_config_diff(running_config, candidate, &changes); - if (RB_EMPTY(nb_config_cbs, &changes)) + if (RB_EMPTY(nb_config_cbs, &changes)) { + snprintf( + errmsg, errmsg_len, + "No changes to apply were found during preparation phase"); return NB_ERR_NO_CHANGES; + } - if (nb_candidate_validate_changes(candidate, &changes) != NB_OK) { + if (nb_candidate_validate_code(context, candidate, &changes, errmsg, + errmsg_len) + != NB_OK) { flog_warn(EC_LIB_NB_CANDIDATE_INVALID, "%s: failed to validate candidate configuration", __func__); @@ -608,35 +763,42 @@ int nb_candidate_commit_prepare(struct nb_config *candidate, return NB_ERR_VALIDATION; } - *transaction = nb_transaction_new(candidate, &changes, client, comment); + *transaction = nb_transaction_new(context, candidate, &changes, comment, + errmsg, errmsg_len); if (*transaction == NULL) { flog_warn(EC_LIB_NB_TRANSACTION_CREATION_FAILED, - "%s: failed to create transaction", __func__); + "%s: failed to create transaction: %s", __func__, + errmsg); nb_config_diff_del_changes(&changes); return NB_ERR_LOCKED; } - return nb_transaction_process(NB_EV_PREPARE, *transaction); + return nb_transaction_process(NB_EV_PREPARE, *transaction, errmsg, + errmsg_len); } -void nb_candidate_commit_abort(struct nb_transaction *transaction) +void nb_candidate_commit_abort(struct nb_transaction *transaction, char *errmsg, + size_t errmsg_len) { - (void)nb_transaction_process(NB_EV_ABORT, transaction); + (void)nb_transaction_process(NB_EV_ABORT, transaction, errmsg, + errmsg_len); nb_transaction_free(transaction); } void nb_candidate_commit_apply(struct nb_transaction *transaction, - bool save_transaction, uint32_t *transaction_id) + bool save_transaction, uint32_t *transaction_id, + char *errmsg, size_t errmsg_len) { - (void)nb_transaction_process(NB_EV_APPLY, transaction); - nb_transaction_apply_finish(transaction); + (void)nb_transaction_process(NB_EV_APPLY, transaction, errmsg, + errmsg_len); + nb_transaction_apply_finish(transaction, errmsg, errmsg_len); /* Replace running by candidate. */ transaction->config->version++; nb_config_replace(running_config, transaction->config, true); /* Record transaction. */ - if (save_transaction + if (save_transaction && nb_db_enabled && nb_db_transaction_save(transaction, transaction_id) != NB_OK) flog_warn(EC_LIB_NB_TRANSACTION_RECORD_FAILED, "%s: failed to record transaction", __func__); @@ -644,192 +806,518 @@ void nb_candidate_commit_apply(struct nb_transaction *transaction, nb_transaction_free(transaction); } -int nb_candidate_commit(struct nb_config *candidate, enum nb_client client, +int nb_candidate_commit(struct nb_context *context, struct nb_config *candidate, bool save_transaction, const char *comment, - uint32_t *transaction_id) + uint32_t *transaction_id, char *errmsg, + size_t errmsg_len) { struct nb_transaction *transaction = NULL; int ret; - ret = nb_candidate_commit_prepare(candidate, client, comment, - &transaction); + ret = nb_candidate_commit_prepare(context, candidate, comment, + &transaction, errmsg, errmsg_len); /* * Apply the changes if the preparation phase succeeded. Otherwise abort * the transaction. */ if (ret == NB_OK) nb_candidate_commit_apply(transaction, save_transaction, - transaction_id); + transaction_id, errmsg, errmsg_len); else if (transaction != NULL) - nb_candidate_commit_abort(transaction); + nb_candidate_commit_abort(transaction, errmsg, errmsg_len); return ret; } -static void nb_log_callback(const enum nb_event event, - enum nb_operation operation, const char *xpath, - const char *value) +int nb_running_lock(enum nb_client client, const void *user) { + int ret = -1; + + frr_with_mutex (&running_config_mgmt_lock.mtx) { + if (!running_config_mgmt_lock.locked) { + running_config_mgmt_lock.locked = true; + running_config_mgmt_lock.owner_client = client; + running_config_mgmt_lock.owner_user = user; + ret = 0; + } + } + + return ret; +} + +int nb_running_unlock(enum nb_client client, const void *user) +{ + int ret = -1; + + frr_with_mutex (&running_config_mgmt_lock.mtx) { + if (running_config_mgmt_lock.locked + && running_config_mgmt_lock.owner_client == client + && running_config_mgmt_lock.owner_user == user) { + running_config_mgmt_lock.locked = false; + running_config_mgmt_lock.owner_client = NB_CLIENT_NONE; + running_config_mgmt_lock.owner_user = NULL; + ret = 0; + } + } + + return ret; +} + +int nb_running_lock_check(enum nb_client client, const void *user) +{ + int ret = -1; + + frr_with_mutex (&running_config_mgmt_lock.mtx) { + if (!running_config_mgmt_lock.locked + || (running_config_mgmt_lock.owner_client == client + && running_config_mgmt_lock.owner_user == user)) + ret = 0; + } + + return ret; +} + +static void nb_log_config_callback(const enum nb_event event, + enum nb_operation operation, + const struct lyd_node *dnode) +{ + const char *value; + char xpath[XPATH_MAXLEN]; + + if (!DEBUG_MODE_CHECK(&nb_dbg_cbs_config, DEBUG_MODE_ALL)) + return; + + yang_dnode_get_path(dnode, xpath, sizeof(xpath)); + if (yang_snode_is_typeless_data(dnode->schema)) + value = "(none)"; + else + value = yang_dnode_get_string(dnode, NULL); + zlog_debug( "northbound callback: event [%s] op [%s] xpath [%s] value [%s]", nb_event_name(event), nb_operation_name(operation), xpath, value); } -/* - * Call the northbound configuration callback associated to a given - * configuration change. - */ -static int nb_callback_configuration(const enum nb_event event, - struct nb_config_change *change) +static int nb_callback_create(struct nb_context *context, + const struct nb_node *nb_node, + enum nb_event event, const struct lyd_node *dnode, + union nb_resource *resource, char *errmsg, + size_t errmsg_len) { - enum nb_operation operation = change->cb.operation; - const char *xpath = change->cb.xpath; - const struct nb_node *nb_node = change->cb.nb_node; - const struct lyd_node *dnode = change->cb.dnode; - union nb_resource *resource; - int ret = NB_ERR; + struct nb_cb_create_args args = {}; + bool unexpected_error = false; + int ret; - if (DEBUG_MODE_CHECK(&nb_dbg_cbs_config, DEBUG_MODE_ALL)) { - const char *value = "(none)"; + nb_log_config_callback(event, NB_OP_CREATE, dnode); - if (dnode && !yang_snode_is_typeless_data(dnode->schema)) - value = yang_dnode_get_string(dnode, NULL); + args.context = context; + args.event = event; + args.dnode = dnode; + args.resource = resource; + args.errmsg = errmsg; + args.errmsg_len = errmsg_len; + ret = nb_node->cbs.create(&args); - nb_log_callback(event, operation, xpath, value); + /* Detect and log unexpected errors. */ + switch (ret) { + case NB_OK: + case NB_ERR: + break; + case NB_ERR_VALIDATION: + if (event != NB_EV_VALIDATE) + unexpected_error = true; + break; + case NB_ERR_RESOURCE: + if (event != NB_EV_PREPARE) + unexpected_error = true; + break; + case NB_ERR_INCONSISTENCY: + if (event == NB_EV_VALIDATE) + unexpected_error = true; + break; + default: + unexpected_error = true; + break; } + if (unexpected_error) + DEBUGD(&nb_dbg_cbs_config, + "northbound callback: unexpected return value: %s", + nb_err_name(ret)); - if (event == NB_EV_VALIDATE) - resource = NULL; - else - resource = &change->resource; + return ret; +} - switch (operation) { - case NB_OP_CREATE: - ret = (*nb_node->cbs.create)(event, dnode, resource); +static int nb_callback_modify(struct nb_context *context, + const struct nb_node *nb_node, + enum nb_event event, const struct lyd_node *dnode, + union nb_resource *resource, char *errmsg, + size_t errmsg_len) +{ + struct nb_cb_modify_args args = {}; + bool unexpected_error = false; + int ret; + + nb_log_config_callback(event, NB_OP_MODIFY, dnode); + + args.context = context; + args.event = event; + args.dnode = dnode; + args.resource = resource; + args.errmsg = errmsg; + args.errmsg_len = errmsg_len; + ret = nb_node->cbs.modify(&args); + + /* Detect and log unexpected errors. */ + switch (ret) { + case NB_OK: + case NB_ERR: break; - case NB_OP_MODIFY: - ret = (*nb_node->cbs.modify)(event, dnode, resource); + case NB_ERR_VALIDATION: + if (event != NB_EV_VALIDATE) + unexpected_error = true; break; - case NB_OP_DESTROY: - ret = (*nb_node->cbs.destroy)(event, dnode); + case NB_ERR_RESOURCE: + if (event != NB_EV_PREPARE) + unexpected_error = true; break; - case NB_OP_MOVE: - ret = (*nb_node->cbs.move)(event, dnode); + case NB_ERR_INCONSISTENCY: + if (event == NB_EV_VALIDATE) + unexpected_error = true; break; default: - flog_err(EC_LIB_DEVELOPMENT, - "%s: unknown operation (%u) [xpath %s]", __func__, - operation, xpath); - exit(1); + unexpected_error = true; + break; } + if (unexpected_error) + DEBUGD(&nb_dbg_cbs_config, + "northbound callback: unexpected return value: %s", + nb_err_name(ret)); - if (ret != NB_OK) { - int priority; - enum lib_log_refs ref; + return ret; +} - switch (event) { - case NB_EV_VALIDATE: - priority = LOG_WARNING; - ref = EC_LIB_NB_CB_CONFIG_VALIDATE; - break; - case NB_EV_PREPARE: - priority = LOG_WARNING; - ref = EC_LIB_NB_CB_CONFIG_PREPARE; - break; - case NB_EV_ABORT: - priority = LOG_WARNING; - ref = EC_LIB_NB_CB_CONFIG_ABORT; - break; - case NB_EV_APPLY: - priority = LOG_ERR; - ref = EC_LIB_NB_CB_CONFIG_APPLY; - break; - default: - flog_err(EC_LIB_DEVELOPMENT, - "%s: unknown event (%u) [xpath %s]", - __func__, event, xpath); - exit(1); - } +static int nb_callback_destroy(struct nb_context *context, + const struct nb_node *nb_node, + enum nb_event event, + const struct lyd_node *dnode, char *errmsg, + size_t errmsg_len) +{ + struct nb_cb_destroy_args args = {}; + bool unexpected_error = false; + int ret; - flog(priority, ref, - "%s: error processing configuration change: error [%s] event [%s] operation [%s] xpath [%s]", - __func__, nb_err_name(ret), nb_event_name(event), - nb_operation_name(operation), xpath); + nb_log_config_callback(event, NB_OP_DESTROY, dnode); + + args.context = context; + args.event = event; + args.dnode = dnode; + args.errmsg = errmsg; + args.errmsg_len = errmsg_len; + ret = nb_node->cbs.destroy(&args); + + /* Detect and log unexpected errors. */ + switch (ret) { + case NB_OK: + case NB_ERR: + break; + case NB_ERR_VALIDATION: + if (event != NB_EV_VALIDATE) + unexpected_error = true; + break; + case NB_ERR_INCONSISTENCY: + if (event == NB_EV_VALIDATE) + unexpected_error = true; + break; + default: + unexpected_error = true; + break; } + if (unexpected_error) + DEBUGD(&nb_dbg_cbs_config, + "northbound callback: unexpected return value: %s", + nb_err_name(ret)); return ret; } +static int nb_callback_move(struct nb_context *context, + const struct nb_node *nb_node, enum nb_event event, + const struct lyd_node *dnode, char *errmsg, + size_t errmsg_len) +{ + struct nb_cb_move_args args = {}; + bool unexpected_error = false; + int ret; + + nb_log_config_callback(event, NB_OP_MOVE, dnode); + + args.context = context; + args.event = event; + args.dnode = dnode; + args.errmsg = errmsg; + args.errmsg_len = errmsg_len; + ret = nb_node->cbs.move(&args); + + /* Detect and log unexpected errors. */ + switch (ret) { + case NB_OK: + case NB_ERR: + break; + case NB_ERR_VALIDATION: + if (event != NB_EV_VALIDATE) + unexpected_error = true; + break; + case NB_ERR_INCONSISTENCY: + if (event == NB_EV_VALIDATE) + unexpected_error = true; + break; + default: + unexpected_error = true; + break; + } + if (unexpected_error) + DEBUGD(&nb_dbg_cbs_config, + "northbound callback: unexpected return value: %s", + nb_err_name(ret)); + + return ret; +} + +static int nb_callback_pre_validate(struct nb_context *context, + const struct nb_node *nb_node, + const struct lyd_node *dnode, char *errmsg, + size_t errmsg_len) +{ + struct nb_cb_pre_validate_args args = {}; + bool unexpected_error = false; + int ret; + + nb_log_config_callback(NB_EV_VALIDATE, NB_OP_PRE_VALIDATE, dnode); + + args.dnode = dnode; + args.errmsg = errmsg; + args.errmsg_len = errmsg_len; + ret = nb_node->cbs.pre_validate(&args); + + /* Detect and log unexpected errors. */ + switch (ret) { + case NB_OK: + case NB_ERR_VALIDATION: + break; + default: + unexpected_error = true; + break; + } + if (unexpected_error) + DEBUGD(&nb_dbg_cbs_config, + "northbound callback: unexpected return value: %s", + nb_err_name(ret)); + + return ret; +} + +static void nb_callback_apply_finish(struct nb_context *context, + const struct nb_node *nb_node, + const struct lyd_node *dnode, char *errmsg, + size_t errmsg_len) +{ + struct nb_cb_apply_finish_args args = {}; + + nb_log_config_callback(NB_EV_APPLY, NB_OP_APPLY_FINISH, dnode); + + args.context = context; + args.dnode = dnode; + args.errmsg = errmsg; + args.errmsg_len = errmsg_len; + nb_node->cbs.apply_finish(&args); +} + struct yang_data *nb_callback_get_elem(const struct nb_node *nb_node, const char *xpath, const void *list_entry) { + struct nb_cb_get_elem_args args = {}; + DEBUGD(&nb_dbg_cbs_state, "northbound callback (get_elem): xpath [%s] list_entry [%p]", xpath, list_entry); - return nb_node->cbs.get_elem(xpath, list_entry); + args.xpath = xpath; + args.list_entry = list_entry; + return nb_node->cbs.get_elem(&args); } const void *nb_callback_get_next(const struct nb_node *nb_node, const void *parent_list_entry, const void *list_entry) { + struct nb_cb_get_next_args args = {}; + DEBUGD(&nb_dbg_cbs_state, "northbound callback (get_next): node [%s] parent_list_entry [%p] list_entry [%p]", nb_node->xpath, parent_list_entry, list_entry); - return nb_node->cbs.get_next(parent_list_entry, list_entry); + args.parent_list_entry = parent_list_entry; + args.list_entry = list_entry; + return nb_node->cbs.get_next(&args); } int nb_callback_get_keys(const struct nb_node *nb_node, const void *list_entry, struct yang_list_keys *keys) { + struct nb_cb_get_keys_args args = {}; + DEBUGD(&nb_dbg_cbs_state, "northbound callback (get_keys): node [%s] list_entry [%p]", nb_node->xpath, list_entry); - return nb_node->cbs.get_keys(list_entry, keys); + args.list_entry = list_entry; + args.keys = keys; + return nb_node->cbs.get_keys(&args); } const void *nb_callback_lookup_entry(const struct nb_node *nb_node, const void *parent_list_entry, const struct yang_list_keys *keys) { + struct nb_cb_lookup_entry_args args = {}; + DEBUGD(&nb_dbg_cbs_state, "northbound callback (lookup_entry): node [%s] parent_list_entry [%p]", nb_node->xpath, parent_list_entry); - return nb_node->cbs.lookup_entry(parent_list_entry, keys); + args.parent_list_entry = parent_list_entry; + args.keys = keys; + return nb_node->cbs.lookup_entry(&args); } int nb_callback_rpc(const struct nb_node *nb_node, const char *xpath, const struct list *input, struct list *output) { + struct nb_cb_rpc_args args = {}; + DEBUGD(&nb_dbg_cbs_rpc, "northbound RPC: %s", xpath); - return nb_node->cbs.rpc(xpath, input, output); + args.xpath = xpath; + args.input = input; + args.output = output; + return nb_node->cbs.rpc(&args); } -static struct nb_transaction *nb_transaction_new(struct nb_config *config, - struct nb_config_cbs *changes, - enum nb_client client, - const char *comment) +/* + * Call the northbound configuration callback associated to a given + * configuration change. + */ +static int nb_callback_configuration(struct nb_context *context, + const enum nb_event event, + struct nb_config_change *change, + char *errmsg, size_t errmsg_len) +{ + enum nb_operation operation = change->cb.operation; + char xpath[XPATH_MAXLEN]; + const struct nb_node *nb_node = change->cb.nb_node; + const struct lyd_node *dnode = change->cb.dnode; + union nb_resource *resource; + int ret = NB_ERR; + + if (event == NB_EV_VALIDATE) + resource = NULL; + else + resource = &change->resource; + + switch (operation) { + case NB_OP_CREATE: + ret = nb_callback_create(context, nb_node, event, dnode, + resource, errmsg, errmsg_len); + break; + case NB_OP_MODIFY: + ret = nb_callback_modify(context, nb_node, event, dnode, + resource, errmsg, errmsg_len); + break; + case NB_OP_DESTROY: + ret = nb_callback_destroy(context, nb_node, event, dnode, + errmsg, errmsg_len); + break; + case NB_OP_MOVE: + ret = nb_callback_move(context, nb_node, event, dnode, errmsg, + errmsg_len); + break; + default: + yang_dnode_get_path(dnode, xpath, sizeof(xpath)); + flog_err(EC_LIB_DEVELOPMENT, + "%s: unknown operation (%u) [xpath %s]", __func__, + operation, xpath); + exit(1); + } + + if (ret != NB_OK) { + int priority; + enum lib_log_refs ref; + + yang_dnode_get_path(dnode, xpath, sizeof(xpath)); + + switch (event) { + case NB_EV_VALIDATE: + priority = LOG_WARNING; + ref = EC_LIB_NB_CB_CONFIG_VALIDATE; + break; + case NB_EV_PREPARE: + priority = LOG_WARNING; + ref = EC_LIB_NB_CB_CONFIG_PREPARE; + break; + case NB_EV_ABORT: + priority = LOG_WARNING; + ref = EC_LIB_NB_CB_CONFIG_ABORT; + break; + case NB_EV_APPLY: + priority = LOG_ERR; + ref = EC_LIB_NB_CB_CONFIG_APPLY; + break; + default: + flog_err(EC_LIB_DEVELOPMENT, + "%s: unknown event (%u) [xpath %s]", __func__, + event, xpath); + exit(1); + } + + flog(priority, ref, + "error processing configuration change: error [%s] event [%s] operation [%s] xpath [%s]", + nb_err_name(ret), nb_event_name(event), + nb_operation_name(operation), xpath); + if (strlen(errmsg) > 0) + flog(priority, ref, + "error processing configuration change: %s", + errmsg); + } + + return ret; +} + +static struct nb_transaction * +nb_transaction_new(struct nb_context *context, struct nb_config *config, + struct nb_config_cbs *changes, const char *comment, + char *errmsg, size_t errmsg_len) { struct nb_transaction *transaction; + if (nb_running_lock_check(context->client, context->user)) { + strlcpy(errmsg, + "running configuration is locked by another client", + errmsg_len); + return NULL; + } + if (transaction_in_progress) { - flog_warn( - EC_LIB_NB_TRANSACTION_CREATION_FAILED, - "%s: error - there's already another transaction in progress", - __func__); + strlcpy(errmsg, + "there's already another transaction in progress", + errmsg_len); return NULL; } transaction_in_progress = true; transaction = XCALLOC(MTYPE_TMP, sizeof(*transaction)); - transaction->client = client; + transaction->context = context; if (comment) strlcpy(transaction->comment, comment, sizeof(transaction->comment)); @@ -848,7 +1336,8 @@ static void nb_transaction_free(struct nb_transaction *transaction) /* Process all configuration changes associated to a transaction. */ static int nb_transaction_process(enum nb_event event, - struct nb_transaction *transaction) + struct nb_transaction *transaction, + char *errmsg, size_t errmsg_len) { struct nb_config_cb *cb; @@ -860,11 +1349,12 @@ static int nb_transaction_process(enum nb_event event, * Only try to release resources that were allocated * successfully. */ - if (event == NB_EV_ABORT && change->prepare_ok == false) + if (event == NB_EV_ABORT && !change->prepare_ok) break; /* Call the appropriate callback. */ - ret = nb_callback_configuration(event, change); + ret = nb_callback_configuration(transaction->context, event, + change, errmsg, errmsg_len); switch (event) { case NB_EV_PREPARE: if (ret != NB_OK) @@ -891,14 +1381,12 @@ static int nb_transaction_process(enum nb_event event, } static struct nb_config_cb * -nb_apply_finish_cb_new(struct nb_config_cbs *cbs, const char *xpath, - const struct nb_node *nb_node, +nb_apply_finish_cb_new(struct nb_config_cbs *cbs, const struct nb_node *nb_node, const struct lyd_node *dnode) { struct nb_config_cb *cb; cb = XCALLOC(MTYPE_TMP, sizeof(*cb)); - strlcpy(cb->xpath, xpath, sizeof(cb->xpath)); cb->nb_node = nb_node; cb->dnode = dnode; RB_INSERT(nb_config_cbs, cbs, cb); @@ -907,18 +1395,21 @@ nb_apply_finish_cb_new(struct nb_config_cbs *cbs, const char *xpath, } static struct nb_config_cb * -nb_apply_finish_cb_find(struct nb_config_cbs *cbs, const char *xpath, - const struct nb_node *nb_node) +nb_apply_finish_cb_find(struct nb_config_cbs *cbs, + const struct nb_node *nb_node, + const struct lyd_node *dnode) { struct nb_config_cb s; - strlcpy(s.xpath, xpath, sizeof(s.xpath)); + s.seq = 0; s.nb_node = nb_node; + s.dnode = dnode; return RB_FIND(nb_config_cbs, cbs, &s); } /* Call the 'apply_finish' callbacks. */ -static void nb_transaction_apply_finish(struct nb_transaction *transaction) +static void nb_transaction_apply_finish(struct nb_transaction *transaction, + char *errmsg, size_t errmsg_len) { struct nb_config_cbs cbs; struct nb_config_cb *cb; @@ -955,22 +1446,20 @@ static void nb_transaction_apply_finish(struct nb_transaction *transaction) xpath); } while (dnode) { - char xpath[XPATH_MAXLEN]; struct nb_node *nb_node; nb_node = dnode->schema->priv; - if (!nb_node->cbs.apply_finish) + if (!nb_node || !nb_node->cbs.apply_finish) goto next; /* * Don't call the callback more than once for the same * data node. */ - yang_dnode_get_path(dnode, xpath, sizeof(xpath)); - if (nb_apply_finish_cb_find(&cbs, xpath, nb_node)) + if (nb_apply_finish_cb_find(&cbs, nb_node, dnode)) goto next; - nb_apply_finish_cb_new(&cbs, xpath, nb_node, dnode); + nb_apply_finish_cb_new(&cbs, nb_node, dnode); next: dnode = dnode->parent; @@ -978,13 +1467,9 @@ static void nb_transaction_apply_finish(struct nb_transaction *transaction) } /* Call the 'apply_finish' callbacks, sorted by their priorities. */ - RB_FOREACH (cb, nb_config_cbs, &cbs) { - if (DEBUG_MODE_CHECK(&nb_dbg_cbs_config, DEBUG_MODE_ALL)) - nb_log_callback(NB_EV_APPLY, NB_OP_APPLY_FINISH, - cb->xpath, NULL); - - (*cb->nb_node->cbs.apply_finish)(cb->dnode); - } + RB_FOREACH (cb, nb_config_cbs, &cbs) + nb_callback_apply_finish(transaction->context, cb->nb_node, + cb->dnode, errmsg, errmsg_len); /* Release memory. */ while (!RB_EMPTY(nb_config_cbs, &cbs)) { @@ -1189,9 +1674,27 @@ static int nb_oper_data_iter_node(const struct lys_node *snode, /* Update XPath. */ strlcpy(xpath, xpath_parent, sizeof(xpath)); - if (!first && snode->nodetype != LYS_USES) - snprintf(xpath + strlen(xpath), sizeof(xpath) - strlen(xpath), - "/%s", snode->name); + if (!first && snode->nodetype != LYS_USES) { + struct lys_node *parent; + + /* Get the real parent. */ + parent = snode->parent; + while (parent && parent->nodetype == LYS_USES) + parent = parent->parent; + + /* + * When necessary, include the namespace of the augmenting + * module. + */ + if (parent && parent->nodetype == LYS_AUGMENT) + snprintf(xpath + strlen(xpath), + sizeof(xpath) - strlen(xpath), "/%s:%s", + snode->module->name, snode->name); + else + snprintf(xpath + strlen(xpath), + sizeof(xpath) - strlen(xpath), "/%s", + snode->name); + } nb_node = snode->priv; switch (snode->nodetype) { @@ -1260,19 +1763,12 @@ int nb_oper_data_iterate(const char *xpath, struct yang_translator *translator, */ ly_errno = 0; dnode = lyd_new_path(NULL, ly_native_ctx, xpath, NULL, 0, - LYD_PATH_OPT_UPDATE); - if (!dnode && ly_errno) { + LYD_PATH_OPT_UPDATE | LYD_PATH_OPT_NOPARENTRET); + if (!dnode) { flog_warn(EC_LIB_LIBYANG, "%s: lyd_new_path() failed", __func__); return NB_ERR; } - /* - * We can remove the following two lines once we depend on - * libyang-v0.16-r2, which has the LYD_PATH_OPT_NOPARENTRET flag for - * lyd_new_path(). - */ - dnode = yang_dnode_get(dnode, xpath); - assert(dnode); /* * Create a linked list to sort the data nodes starting from the root. @@ -1313,6 +1809,16 @@ int nb_oper_data_iterate(const char *xpath, struct yang_translator *translator, /* Find the list entry pointer. */ nn = dn->schema->priv; + if (!nn->cbs.lookup_entry) { + flog_warn( + EC_LIB_NB_OPERATIONAL_DATA, + "%s: data path doesn't support iteration over operational data: %s", + __func__, xpath); + list_delete(&list_dnodes); + yang_dnode_free(dnode); + return NB_ERR; + } + list_entry = nb_callback_lookup_entry(nn, list_entry, &list_keys); if (list_entry == NULL) { @@ -1436,6 +1942,7 @@ bool nb_operation_is_valid(enum nb_operation operation, return false; } return true; + case NB_OP_PRE_VALIDATE: case NB_OP_APPLY_FINISH: if (!CHECK_FLAG(snode->flags, LYS_CONFIG_W)) return false; @@ -1531,7 +2038,7 @@ static bool running_config_entry_cmp(const void *value1, const void *value2) return strmatch(c1->xpath, c2->xpath); } -static unsigned int running_config_entry_key_make(void *value) +static unsigned int running_config_entry_key_make(const void *value) { return string_hash_make(value); } @@ -1561,6 +2068,29 @@ void nb_running_set_entry(const struct lyd_node *dnode, void *entry) config->entry = entry; } +void nb_running_move_tree(const char *xpath_from, const char *xpath_to) +{ + struct nb_config_entry *entry; + struct list *entries = hash_to_list(running_config_entries); + struct listnode *ln; + + for (ALL_LIST_ELEMENTS_RO(entries, ln, entry)) { + if (!frrstr_startswith(entry->xpath, xpath_from)) + continue; + + hash_release(running_config_entries, entry); + + char *newpath = + frrstr_replace(entry->xpath, xpath_from, xpath_to); + strlcpy(entry->xpath, newpath, sizeof(entry->xpath)); + XFREE(MTYPE_TMP, newpath); + + hash_get(running_config_entries, entry, hash_alloc_intern); + } + + list_delete(&entries); +} + static void *nb_running_unset_entry_helper(const struct lyd_node *dnode) { struct nb_config_entry *config, s; @@ -1594,18 +2124,21 @@ void *nb_running_unset_entry(const struct lyd_node *dnode) return entry; } -void *nb_running_get_entry(const struct lyd_node *dnode, const char *xpath, - bool abort_if_not_found) +static void *nb_running_get_entry_worker(const struct lyd_node *dnode, + const char *xpath, + bool abort_if_not_found, + bool rec_search) { const struct lyd_node *orig_dnode = dnode; char xpath_buf[XPATH_MAXLEN]; + bool rec_flag = true; assert(dnode || xpath); if (!dnode) dnode = yang_dnode_get(running_config->dnode, xpath); - while (dnode) { + while (rec_flag && dnode) { struct nb_config_entry *config, s; yang_dnode_get_path(dnode, s.xpath, sizeof(s.xpath)); @@ -1613,6 +2146,8 @@ void *nb_running_get_entry(const struct lyd_node *dnode, const char *xpath, if (config) return config->entry; + rec_flag = rec_search; + dnode = dnode->parent; } @@ -1626,6 +2161,20 @@ void *nb_running_get_entry(const struct lyd_node *dnode, const char *xpath, abort(); } +void *nb_running_get_entry(const struct lyd_node *dnode, const char *xpath, + bool abort_if_not_found) +{ + return nb_running_get_entry_worker(dnode, xpath, abort_if_not_found, + true); +} + +void *nb_running_get_entry_non_rec(const struct lyd_node *dnode, + const char *xpath, bool abort_if_not_found) +{ + return nb_running_get_entry_worker(dnode, xpath, abort_if_not_found, + false); +} + /* Logging functions. */ const char *nb_event_name(enum nb_event event) { @@ -1654,6 +2203,8 @@ const char *nb_operation_name(enum nb_operation operation) return "destroy"; case NB_OP_MOVE: return "move"; + case NB_OP_PRE_VALIDATE: + return "pre_validate"; case NB_OP_APPLY_FINISH: return "apply_finish"; case NB_OP_GET_ELEM: @@ -1685,7 +2236,7 @@ const char *nb_err_name(enum nb_error error) case NB_ERR_LOCKED: return "resource is locked"; case NB_ERR_VALIDATION: - return "validation error"; + return "validation"; case NB_ERR_RESOURCE: return "failed to allocate resource"; case NB_ERR_INCONSISTENCY: @@ -1704,6 +2255,8 @@ const char *nb_client_name(enum nb_client client) return "ConfD"; case NB_CLIENT_SYSREPO: return "Sysrepo"; + case NB_CLIENT_GRPC: + return "gRPC"; default: return "unknown"; } @@ -1715,6 +2268,13 @@ static void nb_load_callbacks(const struct frr_yang_module_info *module) struct nb_node *nb_node; uint32_t priority; + if (i > YANG_MODULE_MAX_NODES) { + zlog_err( + "%s: %s.yang has more than %u nodes. Please increase YANG_MODULE_MAX_NODES to fix this problem.", + __func__, module->name, YANG_MODULE_MAX_NODES); + exit(1); + } + nb_node = nb_node_find(module->nodes[i].xpath); if (!nb_node) { flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH, @@ -1731,7 +2291,8 @@ static void nb_load_callbacks(const struct frr_yang_module_info *module) } void nb_init(struct thread_master *tm, - const struct frr_yang_module_info *modules[], size_t nmodules) + const struct frr_yang_module_info *const modules[], + size_t nmodules, bool db_enabled) { unsigned int errors = 0; @@ -1756,11 +2317,14 @@ void nb_init(struct thread_master *tm, exit(1); } + nb_db_enabled = db_enabled; + /* Create an empty running configuration. */ running_config = nb_config_new(NULL); running_config_entries = hash_create(running_config_entry_key_make, running_config_entry_cmp, "Running Configuration Entries"); + pthread_mutex_init(&running_config_mgmt_lock.mtx, NULL); /* Initialize the northbound CLI. */ nb_cli_init(tm); @@ -1778,4 +2342,5 @@ void nb_terminate(void) hash_clean(running_config_entries, running_config_entry_free); hash_free(running_config_entries); nb_config_free(running_config); + pthread_mutex_destroy(&running_config_mgmt_lock.mtx); } diff --git a/lib/northbound.h b/lib/northbound.h index 14f27c1d41..b1ae9ffa00 100644 --- a/lib/northbound.h +++ b/lib/northbound.h @@ -72,6 +72,7 @@ enum nb_operation { NB_OP_MODIFY, NB_OP_DESTROY, NB_OP_MOVE, + NB_OP_PRE_VALIDATE, NB_OP_APPLY_FINISH, NB_OP_GET_ELEM, NB_OP_GET_NEXT, @@ -85,6 +86,183 @@ union nb_resource { void *ptr; }; +/* + * Northbound callbacks parameters. + */ + +struct nb_cb_create_args { + /* Context of the configuration transaction. */ + struct nb_context *context; + + /* + * The transaction phase. Refer to the documentation comments of + * nb_event for more details. + */ + enum nb_event event; + + /* libyang data node that is being created. */ + const struct lyd_node *dnode; + + /* + * Pointer to store resource(s) allocated during the NB_EV_PREPARE + * phase. The same pointer can be used during the NB_EV_ABORT and + * NB_EV_APPLY phases to either release or make use of the allocated + * resource(s). It's set to NULL when the event is NB_EV_VALIDATE. + */ + union nb_resource *resource; + + /* Buffer to store human-readable error message in case of error. */ + char *errmsg; + + /* Size of errmsg. */ + size_t errmsg_len; +}; + +struct nb_cb_modify_args { + /* Context of the configuration transaction. */ + struct nb_context *context; + + /* + * The transaction phase. Refer to the documentation comments of + * nb_event for more details. + */ + enum nb_event event; + + /* libyang data node that is being modified. */ + const struct lyd_node *dnode; + + /* + * Pointer to store resource(s) allocated during the NB_EV_PREPARE + * phase. The same pointer can be used during the NB_EV_ABORT and + * NB_EV_APPLY phases to either release or make use of the allocated + * resource(s). It's set to NULL when the event is NB_EV_VALIDATE. + */ + union nb_resource *resource; + + /* Buffer to store human-readable error message in case of error. */ + char *errmsg; + + /* Size of errmsg. */ + size_t errmsg_len; +}; + +struct nb_cb_destroy_args { + /* Context of the configuration transaction. */ + struct nb_context *context; + + /* + * The transaction phase. Refer to the documentation comments of + * nb_event for more details. + */ + enum nb_event event; + + /* libyang data node that is being deleted. */ + const struct lyd_node *dnode; + + /* Buffer to store human-readable error message in case of error. */ + char *errmsg; + + /* Size of errmsg. */ + size_t errmsg_len; +}; + +struct nb_cb_move_args { + /* Context of the configuration transaction. */ + struct nb_context *context; + + /* + * The transaction phase. Refer to the documentation comments of + * nb_event for more details. + */ + enum nb_event event; + + /* libyang data node that is being moved. */ + const struct lyd_node *dnode; + + /* Buffer to store human-readable error message in case of error. */ + char *errmsg; + + /* Size of errmsg. */ + size_t errmsg_len; +}; + +struct nb_cb_pre_validate_args { + /* Context of the configuration transaction. */ + struct nb_context *context; + + /* libyang data node associated with the 'pre_validate' callback. */ + const struct lyd_node *dnode; + + /* Buffer to store human-readable error message in case of error. */ + char *errmsg; + + /* Size of errmsg. */ + size_t errmsg_len; +}; + +struct nb_cb_apply_finish_args { + /* Context of the configuration transaction. */ + struct nb_context *context; + + /* libyang data node associated with the 'apply_finish' callback. */ + const struct lyd_node *dnode; + + /* Buffer to store human-readable error message in case of error. */ + char *errmsg; + + /* Size of errmsg. */ + size_t errmsg_len; +}; + +struct nb_cb_get_elem_args { + /* YANG data path of the data we want to get. */ + const char *xpath; + + /* Pointer to list entry (might be NULL). */ + const void *list_entry; +}; + +struct nb_cb_get_next_args { + /* Pointer to parent list entry. */ + const void *parent_list_entry; + + /* Pointer to (leaf-)list entry. */ + const void *list_entry; +}; + +struct nb_cb_get_keys_args { + /* Pointer to list entry. */ + const void *list_entry; + + /* + * Structure to be filled based on the attributes of the provided list + * entry. + */ + struct yang_list_keys *keys; +}; + +struct nb_cb_lookup_entry_args { + /* Pointer to parent list entry. */ + const void *parent_list_entry; + + /* Structure containing the keys of the list entry. */ + const struct yang_list_keys *keys; +}; + +struct nb_cb_rpc_args { + /* XPath of the YANG RPC or action. */ + const char *xpath; + + /* Read-only list of input parameters. */ + const struct list *input; + + /* List of output parameters to be populated by the callback. */ + struct list *output; +}; + +/* + * Set of configuration callbacks that can be associated to a northbound node. + */ struct nb_callbacks { /* * Configuration callback. @@ -96,18 +274,9 @@ struct nb_callbacks { * initialize the default values of its children (if any) from the YANG * models. * - * event - * The transaction phase. Refer to the documentation comments of - * nb_event for more details. - * - * dnode - * libyang data node that is being created. - * - * resource - * Pointer to store resource(s) allocated during the NB_EV_PREPARE - * phase. The same pointer can be used during the NB_EV_ABORT and - * NB_EV_APPLY phases to either release or make use of the allocated - * resource(s). It's set to NULL when the event is NB_EV_VALIDATE. + * args + * Refer to the documentation comments of nb_cb_create_args for + * details. * * Returns: * - NB_OK on success. @@ -116,8 +285,7 @@ struct nb_callbacks { * - NB_ERR_INCONSISTENCY when an inconsistency was detected. * - NB_ERR for other errors. */ - int (*create)(enum nb_event event, const struct lyd_node *dnode, - union nb_resource *resource); + int (*create)(struct nb_cb_create_args *args); /* * Configuration callback. @@ -128,18 +296,9 @@ struct nb_callbacks { * modified, the northbound treats this as if the list was deleted and a * new one created with the updated key value. * - * event - * The transaction phase. Refer to the documentation comments of - * nb_event for more details. - * - * dnode - * libyang data node that is being modified - * - * resource - * Pointer to store resource(s) allocated during the NB_EV_PREPARE - * phase. The same pointer can be used during the NB_EV_ABORT and - * NB_EV_APPLY phases to either release or make use of the allocated - * resource(s). It's set to NULL when the event is NB_EV_VALIDATE. + * args + * Refer to the documentation comments of nb_cb_modify_args for + * details. * * Returns: * - NB_OK on success. @@ -148,8 +307,7 @@ struct nb_callbacks { * - NB_ERR_INCONSISTENCY when an inconsistency was detected. * - NB_ERR for other errors. */ - int (*modify)(enum nb_event event, const struct lyd_node *dnode, - union nb_resource *resource); + int (*modify)(struct nb_cb_modify_args *args); /* * Configuration callback. @@ -160,12 +318,9 @@ struct nb_callbacks { * The callback is supposed to delete the entire configuration object, * including its children when they exist. * - * event - * The transaction phase. Refer to the documentation comments of - * nb_event for more details. - * - * dnode - * libyang data node that is being deleted. + * args + * Refer to the documentation comments of nb_cb_destroy_args for + * details. * * Returns: * - NB_OK on success. @@ -173,7 +328,7 @@ struct nb_callbacks { * - NB_ERR_INCONSISTENCY when an inconsistency was detected. * - NB_ERR for other errors. */ - int (*destroy)(enum nb_event event, const struct lyd_node *dnode); + int (*destroy)(struct nb_cb_destroy_args *args); /* * Configuration callback. @@ -181,12 +336,9 @@ struct nb_callbacks { * A list entry or leaf-list entry has been moved. Only applicable when * the "ordered-by user" statement is present. * - * event - * The transaction phase. Refer to the documentation comments of - * nb_event for more details. - * - * dnode - * libyang data node that is being moved. + * args + * Refer to the documentation comments of nb_cb_move_args for + * details. * * Returns: * - NB_OK on success. @@ -194,7 +346,25 @@ struct nb_callbacks { * - NB_ERR_INCONSISTENCY when an inconsistency was detected. * - NB_ERR for other errors. */ - int (*move)(enum nb_event event, const struct lyd_node *dnode); + int (*move)(struct nb_cb_move_args *args); + + /* + * Optional configuration callback. + * + * This callback can be used to validate subsections of the + * configuration being committed before validating the configuration + * changes themselves. It's useful to perform more complex validations + * that depend on the relationship between multiple nodes. + * + * args + * Refer to the documentation comments of nb_cb_pre_validate_args for + * details. + * + * Returns: + * - NB_OK on success. + * - NB_ERR_VALIDATION when a validation error occurred. + */ + int (*pre_validate)(struct nb_cb_pre_validate_args *args); /* * Optional configuration callback. @@ -210,10 +380,11 @@ struct nb_callbacks { * once even if multiple changes occurred within the descendants of the * data node. * - * dnode - * libyang data node associated with the 'apply_finish' callback. + * args + * Refer to the documentation comments of nb_cb_apply_finish_args for + * details. */ - void (*apply_finish)(const struct lyd_node *dnode); + void (*apply_finish)(struct nb_cb_apply_finish_args *args); /* * Operational data callback. @@ -222,18 +393,15 @@ struct nb_callbacks { * leaf-list entry or inform if a typeless value (presence containers or * leafs of type empty) exists or not. * - * xpath - * YANG data path of the data we want to get. - * - * list_entry - * Pointer to list entry (might be NULL). + * args + * Refer to the documentation comments of nb_cb_get_elem_args for + * details. * * Returns: * Pointer to newly created yang_data structure, or NULL to indicate * the absence of data. */ - struct yang_data *(*get_elem)(const char *xpath, - const void *list_entry); + struct yang_data *(*get_elem)(struct nb_cb_get_elem_args *args); /* * Operational data callback for YANG lists and leaf-lists. @@ -242,18 +410,15 @@ struct nb_callbacks { * leaf-list. The 'list_entry' parameter will be NULL on the first * invocation. * - * parent_list_entry - * Pointer to parent list entry. - * - * list_entry - * Pointer to (leaf-)list entry. + * args + * Refer to the documentation comments of nb_cb_get_next_args for + * details. * * Returns: * Pointer to the next entry in the (leaf-)list, or NULL to signal * that the end of the (leaf-)list was reached. */ - const void *(*get_next)(const void *parent_list_entry, - const void *list_entry); + const void *(*get_next)(struct nb_cb_get_next_args *args); /* * Operational data callback for YANG lists. @@ -262,17 +427,14 @@ struct nb_callbacks { * given list_entry. Keyless lists don't need to implement this * callback. * - * list_entry - * Pointer to list entry. - * - * keys - * Structure to be filled based on the attributes of the provided - * list entry. + * args + * Refer to the documentation comments of nb_cb_get_keys_args for + * details. * * Returns: * NB_OK on success, NB_ERR otherwise. */ - int (*get_keys)(const void *list_entry, struct yang_list_keys *keys); + int (*get_keys)(struct nb_cb_get_keys_args *args); /* * Operational data callback for YANG lists. @@ -281,17 +443,14 @@ struct nb_callbacks { * keys given as a parameter. Keyless lists don't need to implement this * callback. * - * parent_list_entry - * Pointer to parent list entry. - * - * keys - * Structure containing the keys of the list entry. + * args + * Refer to the documentation comments of nb_cb_lookup_entry_args for + * details. * * Returns: * Pointer to the list entry if found, or NULL if not found. */ - const void *(*lookup_entry)(const void *parent_list_entry, - const struct yang_list_keys *keys); + const void *(*lookup_entry)(struct nb_cb_lookup_entry_args *args); /* * RPC and action callback. @@ -300,20 +459,13 @@ struct nb_callbacks { * callback should fetch all the input parameters from the 'input' list, * and add output parameters to the 'output' list if necessary. * - * xpath - * XPath of the YANG RPC or action. - * - * input - * Read-only list of input parameters. - * - * output - * List of output parameters to be populated by the callback. + * args + * Refer to the documentation comments of nb_cb_rpc_args for details. * * Returns: * NB_OK on success, NB_ERR otherwise. */ - int (*rpc)(const char *xpath, const struct list *input, - struct list *output); + int (*rpc)(struct nb_cb_rpc_args *args); /* * Optional callback to show the CLI command associated to the given @@ -337,6 +489,23 @@ struct nb_callbacks { */ void (*cli_show)(struct vty *vty, struct lyd_node *dnode, bool show_defaults); + + /* + * Optional callback to show the CLI node end for lists or containers. + * + * vty + * The vty terminal to dump the configuration to. + * + * dnode + * libyang data node that should be shown in the form of a CLI + * command. + */ + void (*cli_show_end)(struct vty *vty, struct lyd_node *dnode); +}; + +struct nb_dependency_callbacks { + void (*get_dependant_xpath)(const struct lyd_node *dnode, char *xpath); + void (*get_dependency_xpath)(const struct lyd_node *dnode, char *xpath); }; /* @@ -353,6 +522,8 @@ struct nb_node { /* Priority - lower priorities are processed first. */ uint32_t priority; + struct nb_dependency_callbacks dep_cbs; + /* Callbacks implemented for this node. */ struct nb_callbacks cbs; @@ -377,6 +548,13 @@ struct nb_node { /* The YANG list doesn't contain key leafs. */ #define F_NB_NODE_KEYLESS_LIST 0x02 +/* + * HACK: old gcc versions (< 5.x) have a bug that prevents C99 flexible arrays + * from working properly on shared libraries. For those compilers, use a fixed + * size array to work around the problem. + */ +#define YANG_MODULE_MAX_NODES 1024 + struct frr_yang_module_info { /* YANG module name. */ const char *name; @@ -391,7 +569,11 @@ struct frr_yang_module_info { /* Priority - lower priorities are processed first. */ uint32_t priority; +#if defined(__GNUC__) && ((__GNUC__ - 0) < 5) && !defined(__clang__) + } nodes[YANG_MODULE_MAX_NODES + 1]; +#else } nodes[]; +#endif }; /* Northbound error codes. */ @@ -414,9 +596,34 @@ enum nb_error { /* Northbound clients. */ enum nb_client { - NB_CLIENT_CLI = 0, + NB_CLIENT_NONE = 0, + NB_CLIENT_CLI, NB_CLIENT_CONFD, NB_CLIENT_SYSREPO, + NB_CLIENT_GRPC, +}; + +/* Northbound context. */ +struct nb_context { + /* Northbound client. */ + enum nb_client client; + + /* Northbound user (can be NULL). */ + const void *user; + + /* Client-specific data. */ +#if 0 + union { + struct { + } cli; + struct { + } confd; + struct { + } sysrepo; + struct { + } grpc; + } client_data; +#endif }; /* Northbound configuration. */ @@ -429,7 +636,7 @@ struct nb_config { struct nb_config_cb { RB_ENTRY(nb_config_cb) entry; enum nb_operation operation; - char xpath[XPATH_MAXLEN]; + uint32_t seq; const struct nb_node *nb_node; const struct lyd_node *dnode; }; @@ -445,7 +652,7 @@ struct nb_config_change { /* Northbound configuration transaction. */ struct nb_transaction { - enum nb_client client; + struct nb_context *context; char comment[80]; struct nb_config *config; struct nb_config_cbs changes; @@ -512,6 +719,12 @@ void nb_nodes_delete(void); */ extern struct nb_node *nb_node_find(const char *xpath); +extern void nb_node_set_dependency_cbs(const char *dependency_xpath, + const char *dependant_xpath, + struct nb_dependency_callbacks *cbs); + +bool nb_node_has_dependency(struct nb_node *node); + /* * Create a new northbound configuration. * @@ -643,25 +856,36 @@ extern int nb_candidate_update(struct nb_config *candidate); * WARNING: the candidate can be modified as part of the validation process * (e.g. add default nodes). * + * context + * Context of the northbound transaction. + * * candidate * Candidate configuration to validate. * + * errmsg + * Buffer to store human-readable error message in case of error. + * + * errmsg_len + * Size of errmsg. + * * Returns: * NB_OK on success, NB_ERR_VALIDATION otherwise. */ -extern int nb_candidate_validate(struct nb_config *candidate); +extern int nb_candidate_validate(struct nb_context *context, + struct nb_config *candidate, char *errmsg, + size_t errmsg_len); /* * Create a new configuration transaction but do not commit it yet. Only * validate the candidate and prepare all resources required to apply the * configuration changes. * + * context + * Context of the northbound transaction. + * * candidate * Candidate configuration to commit. * - * client - * Northbound client performing the commit. - * * comment * Optional comment describing the commit. * @@ -671,6 +895,12 @@ extern int nb_candidate_validate(struct nb_config *candidate); * nb_candidate_commit_abort() or committed using * nb_candidate_commit_apply(). * + * errmsg + * Buffer to store human-readable error message in case of error. + * + * errmsg_len + * Size of errmsg. + * * Returns: * - NB_OK on success. * - NB_ERR_NO_CHANGES when the candidate is identical to the running @@ -681,10 +911,11 @@ extern int nb_candidate_validate(struct nb_config *candidate); * the candidate configuration. * - NB_ERR for other errors. */ -extern int nb_candidate_commit_prepare(struct nb_config *candidate, - enum nb_client client, +extern int nb_candidate_commit_prepare(struct nb_context *context, + struct nb_config *candidate, const char *comment, - struct nb_transaction **transaction); + struct nb_transaction **transaction, + char *errmsg, size_t errmsg_len); /* * Abort a previously created configuration transaction, releasing all resources @@ -692,8 +923,15 @@ extern int nb_candidate_commit_prepare(struct nb_config *candidate, * * transaction * Candidate configuration to abort. It's consumed by this function. + * + * errmsg + * Buffer to store human-readable error message in case of error. + * + * errmsg_len + * Size of errmsg. */ -extern void nb_candidate_commit_abort(struct nb_transaction *transaction); +extern void nb_candidate_commit_abort(struct nb_transaction *transaction, + char *errmsg, size_t errmsg_len); /* * Commit a previously created configuration transaction. @@ -707,10 +945,17 @@ extern void nb_candidate_commit_abort(struct nb_transaction *transaction); * * transaction_id * Optional output parameter providing the ID of the committed transaction. + * + * errmsg + * Buffer to store human-readable error message in case of error. + * + * errmsg_len + * Size of errmsg. */ extern void nb_candidate_commit_apply(struct nb_transaction *transaction, bool save_transaction, - uint32_t *transaction_id); + uint32_t *transaction_id, char *errmsg, + size_t errmsg_len); /* * Create a new transaction to commit a candidate configuration. This is a @@ -720,13 +965,13 @@ extern void nb_candidate_commit_apply(struct nb_transaction *transaction, * take into account the results of the preparation phase of multiple managed * entities. * + * context + * Context of the northbound transaction. + * * candidate * Candidate configuration to commit. It's preserved regardless if the commit * operation fails or not. * - * client - * Northbound client performing the commit. - * * save_transaction * Specify whether the transaction should be recorded in the transactions log * or not. @@ -737,6 +982,12 @@ extern void nb_candidate_commit_apply(struct nb_transaction *transaction, * transaction_id * Optional output parameter providing the ID of the committed transaction. * + * errmsg + * Buffer to store human-readable error message in case of error. + * + * errmsg_len + * Size of errmsg. + * * Returns: * - NB_OK on success. * - NB_ERR_NO_CHANGES when the candidate is identical to the running @@ -747,12 +998,59 @@ extern void nb_candidate_commit_apply(struct nb_transaction *transaction, * the candidate configuration. * - NB_ERR for other errors. */ -extern int nb_candidate_commit(struct nb_config *candidate, - enum nb_client client, bool save_transaction, - const char *comment, uint32_t *transaction_id); +extern int nb_candidate_commit(struct nb_context *context, + struct nb_config *candidate, + bool save_transaction, const char *comment, + uint32_t *transaction_id, char *errmsg, + size_t errmsg_len); + +/* + * Lock the running configuration. + * + * client + * Northbound client. + * + * user + * Northbound user (can be NULL). + * + * Returns: + * 0 on success, -1 when the running configuration is already locked. + */ +extern int nb_running_lock(enum nb_client client, const void *user); + +/* + * Unlock the running configuration. + * + * client + * Northbound client. + * + * user + * Northbound user (can be NULL). + * + * Returns: + * 0 on success, -1 when the running configuration is already unlocked or + * locked by another client/user. + */ +extern int nb_running_unlock(enum nb_client client, const void *user); /* - * Iterate over operetional data. + * Check if the running configuration is locked or not for the given + * client/user. + * + * client + * Northbound client. + * + * user + * Northbound user (can be NULL). + * + * Returns: + * 0 if the running configuration is unlocked or if the client/user owns the + * lock, -1 otherwise. + */ +extern int nb_running_lock_check(enum nb_client client, const void *user); + +/* + * Iterate over operational data. * * xpath * Data path of the YANG data we want to iterate over. @@ -821,6 +1119,23 @@ extern int nb_notification_send(const char *xpath, struct list *arguments); */ extern void nb_running_set_entry(const struct lyd_node *dnode, void *entry); +/* + * Move an entire tree of user pointer nodes. + * + * Suppose we have xpath A/B/C/D, with user pointers associated to C and D. We + * need to move B to be under Z, so the new xpath is Z/B/C/D. Because user + * pointers are indexed with their absolute path, We need to move all user + * pointers at and below B to their new absolute paths; this function does + * that. + * + * xpath_from + * base xpath of tree to move (A/B) + * + * xpath_to + * base xpath of new location of tree (Z/B) + */ +extern void nb_running_move_tree(const char *xpath_from, const char *xpath_to); + /* * Unset the user pointer associated to a configuration node. * @@ -873,8 +1188,16 @@ extern void *nb_running_unset_entry(const struct lyd_node *dnode); * Returns: * User pointer if found, NULL otherwise. */ -extern void *nb_running_get_entry(const struct lyd_node *dnode, const char *xpath, - bool abort_if_not_found); +extern void *nb_running_get_entry(const struct lyd_node *dnode, + const char *xpath, bool abort_if_not_found); + +/* + * Same as 'nb_running_get_entry', but doesn't search within parent nodes + * recursively if an user point is not found. + */ +extern void *nb_running_get_entry_non_rec(const struct lyd_node *dnode, + const char *xpath, + bool abort_if_not_found); /* * Return a human-readable string representing a northbound event. @@ -929,9 +1252,13 @@ extern const char *nb_client_name(enum nb_client client); * * nmodules * Size of the modules array. + * + * db_enabled + * Set this to record the transactions in the transaction log. */ -extern void nb_init(struct thread_master *tm, const struct frr_yang_module_info *modules[], - size_t nmodules); +extern void nb_init(struct thread_master *tm, + const struct frr_yang_module_info *const modules[], + size_t nmodules, bool db_enabled); /* * Finish the northbound layer gracefully. Should be called only when the daemon diff --git a/lib/northbound_cli.c b/lib/northbound_cli.c index 48faa7595a..9e26fab28c 100644 --- a/lib/northbound_cli.c +++ b/lib/northbound_cli.c @@ -21,6 +21,7 @@ #include "libfrr.h" #include "version.h" +#include "defaults.h" #include "log.h" #include "lib_errors.h" #include "command.h" @@ -40,27 +41,124 @@ struct debug nb_dbg_cbs_state = {0, "Northbound callbacks: state"}; struct debug nb_dbg_cbs_rpc = {0, "Northbound callbacks: RPCs"}; struct debug nb_dbg_notif = {0, "Northbound notifications"}; struct debug nb_dbg_events = {0, "Northbound events"}; +struct debug nb_dbg_libyang = {0, "libyang debugging"}; struct nb_config *vty_shared_candidate_config; static struct thread_master *master; -static void vty_show_libyang_errors(struct vty *vty, struct ly_ctx *ly_ctx) +static void vty_show_nb_errors(struct vty *vty, int error, const char *errmsg) { - struct ly_err_item *ei; - const char *path; + vty_out(vty, "Error type: %s\n", nb_err_name(error)); + if (strlen(errmsg) > 0) + vty_out(vty, "Error description: %s\n", errmsg); +} - ei = ly_err_first(ly_ctx); - if (!ei) - return; +static int nb_cli_classic_commit(struct vty *vty) +{ + struct nb_context context = {}; + char errmsg[BUFSIZ] = {0}; + int ret; - for (; ei; ei = ei->next) - vty_out(vty, "%s\n", ei->msg); + context.client = NB_CLIENT_CLI; + context.user = vty; + ret = nb_candidate_commit(&context, vty->candidate_config, true, NULL, + NULL, errmsg, sizeof(errmsg)); + switch (ret) { + case NB_OK: + /* Successful commit. Print warnings (if any). */ + if (strlen(errmsg) > 0) + vty_out(vty, "%s\n", errmsg); + break; + case NB_ERR_NO_CHANGES: + break; + default: + vty_out(vty, "%% Configuration failed.\n\n"); + vty_show_nb_errors(vty, ret, errmsg); + if (vty->t_pending_commit) + vty_out(vty, + "The following commands were dynamically grouped into the same transaction and rejected:\n%s", + vty->pending_cmds_buf); - path = ly_errpath(ly_ctx); - if (path) - vty_out(vty, "YANG path: %s\n", path); + /* Regenerate candidate for consistency. */ + nb_config_replace(vty->candidate_config, running_config, true); + return CMD_WARNING_CONFIG_FAILED; + } - ly_err_clean(ly_ctx, NULL); + return CMD_SUCCESS; +} + +static void nb_cli_pending_commit_clear(struct vty *vty) +{ + THREAD_TIMER_OFF(vty->t_pending_commit); + vty->backoff_cmd_count = 0; + XFREE(MTYPE_TMP, vty->pending_cmds_buf); + vty->pending_cmds_buflen = 0; + vty->pending_cmds_bufpos = 0; +} + +static int nb_cli_pending_commit_cb(struct thread *thread) +{ + struct vty *vty = THREAD_ARG(thread); + + (void)nb_cli_classic_commit(vty); + nb_cli_pending_commit_clear(vty); + + return 0; +} + +void nb_cli_pending_commit_check(struct vty *vty) +{ + if (vty->t_pending_commit) { + (void)nb_cli_classic_commit(vty); + nb_cli_pending_commit_clear(vty); + } +} + +static bool nb_cli_backoff_start(struct vty *vty) +{ + struct timeval now, delta; + + /* + * Start the configuration backoff timer only if 100 YANG-modeled + * commands or more were entered within the last second. + */ + monotime(&now); + if (monotime_since(&vty->backoff_start, &delta) >= 1000000) { + vty->backoff_start = now; + vty->backoff_cmd_count = 1; + return false; + } + if (++vty->backoff_cmd_count < 100) + return false; + + return true; +} + +static int nb_cli_schedule_command(struct vty *vty) +{ + /* Append command to dynamically sized buffer of scheduled commands. */ + if (!vty->pending_cmds_buf) { + vty->pending_cmds_buflen = 4096; + vty->pending_cmds_buf = + XCALLOC(MTYPE_TMP, vty->pending_cmds_buflen); + } + if ((strlen(vty->buf) + 3) + > (vty->pending_cmds_buflen - vty->pending_cmds_bufpos)) { + vty->pending_cmds_buflen *= 2; + vty->pending_cmds_buf = + XREALLOC(MTYPE_TMP, vty->pending_cmds_buf, + vty->pending_cmds_buflen); + } + strlcat(vty->pending_cmds_buf, "- ", vty->pending_cmds_buflen); + vty->pending_cmds_bufpos = strlcat(vty->pending_cmds_buf, vty->buf, + vty->pending_cmds_buflen); + + /* Schedule the commit operation. */ + THREAD_TIMER_OFF(vty->t_pending_commit); + thread_add_timer_msec(master, nb_cli_pending_commit_cb, vty, 100, + &vty->t_pending_commit); + + return CMD_SUCCESS; } void nb_cli_enqueue_change(struct vty *vty, const char *xpath, @@ -84,20 +182,11 @@ void nb_cli_enqueue_change(struct vty *vty, const char *xpath, int nb_cli_apply_changes(struct vty *vty, const char *xpath_base_fmt, ...) { - struct nb_config *candidate_transitory; char xpath_base[XPATH_MAXLEN] = {}; bool error = false; - int ret; VTY_CHECK_XPATH; - /* - * Create a copy of the candidate configuration. For consistency, we - * need to ensure that either all changes made by the command are - * accepted or none are. - */ - candidate_transitory = nb_config_dup(vty->candidate_config); - /* Parse the base XPath format string. */ if (xpath_base_fmt) { va_list ap; @@ -113,6 +202,7 @@ int nb_cli_apply_changes(struct vty *vty, const char *xpath_base_fmt, ...) struct nb_node *nb_node; char xpath[XPATH_MAXLEN]; struct yang_data *data; + int ret; /* Handle relative XPaths. */ memset(xpath, 0, sizeof(xpath)); @@ -137,7 +227,7 @@ int nb_cli_apply_changes(struct vty *vty, const char *xpath_base_fmt, ...) flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH, "%s: unknown data path: %s", __func__, xpath); error = true; - break; + continue; } /* If the value is not set, get the default if it exists. */ @@ -149,7 +239,7 @@ int nb_cli_apply_changes(struct vty *vty, const char *xpath_base_fmt, ...) * Ignore "not found" errors when editing the candidate * configuration. */ - ret = nb_candidate_edit(candidate_transitory, nb_node, + ret = nb_candidate_edit(vty->candidate_config, nb_node, change->operation, xpath, NULL, data); yang_data_free(data); if (ret != NB_OK && ret != NB_ERR_NOT_FOUND) { @@ -159,44 +249,36 @@ int nb_cli_apply_changes(struct vty *vty, const char *xpath_base_fmt, ...) __func__, nb_operation_name(change->operation), xpath); error = true; - break; + continue; } } if (error) { - nb_config_free(candidate_transitory); + char buf[BUFSIZ]; - switch (frr_get_cli_mode()) { - case FRR_CLI_CLASSIC: - vty_out(vty, "%% Configuration failed.\n\n"); - break; - case FRR_CLI_TRANSACTIONAL: - vty_out(vty, - "%% Failed to edit candidate configuration.\n\n"); - break; - } - vty_show_libyang_errors(vty, ly_native_ctx); - - return CMD_WARNING_CONFIG_FAILED; + /* + * Failure to edit the candidate configuration should never + * happen in practice, unless there's a bug in the code. When + * that happens, log the error but otherwise ignore it. + */ + vty_out(vty, "%% Failed to edit configuration.\n\n"); + vty_out(vty, "%s", + yang_print_errors(ly_native_ctx, buf, sizeof(buf))); } - nb_config_replace(vty->candidate_config, candidate_transitory, false); - - /* Do an implicit "commit" when using the classic CLI mode. */ + /* + * Do an implicit commit when using the classic CLI mode. + * + * NOTE: the implicit commit might be scheduled to run later when + * too many commands are being sent at the same time. This is a + * protection mechanism where multiple commands are grouped into the + * same configuration transaction, allowing them to be processed much + * faster. + */ if (frr_get_cli_mode() == FRR_CLI_CLASSIC) { - ret = nb_candidate_commit(vty->candidate_config, NB_CLIENT_CLI, - false, NULL, NULL); - if (ret != NB_OK && ret != NB_ERR_NO_CHANGES) { - vty_out(vty, "%% Configuration failed: %s.\n\n", - nb_err_name(ret)); - vty_out(vty, - "Please check the logs for more details.\n"); - - /* Regenerate candidate for consistency. */ - nb_config_replace(vty->candidate_config, running_config, - true); - return CMD_WARNING_CONFIG_FAILED; - } + if (vty->t_pending_commit || nb_cli_backoff_start(vty)) + return nb_cli_schedule_command(vty); + return nb_cli_classic_commit(vty); } return CMD_SUCCESS; @@ -232,20 +314,30 @@ void nb_cli_confirmed_commit_clean(struct vty *vty) int nb_cli_confirmed_commit_rollback(struct vty *vty) { + struct nb_context context = {}; uint32_t transaction_id; + char errmsg[BUFSIZ] = {0}; int ret; /* Perform the rollback. */ + context.client = NB_CLIENT_CLI; + context.user = vty; ret = nb_candidate_commit( - vty->confirmed_commit_rollback, NB_CLIENT_CLI, true, + &context, vty->confirmed_commit_rollback, true, "Rollback to previous configuration - confirmed commit has timed out", - &transaction_id); - if (ret == NB_OK) + &transaction_id, errmsg, sizeof(errmsg)); + if (ret == NB_OK) { vty_out(vty, "Rollback performed successfully (Transaction ID #%u).\n", transaction_id); - else - vty_out(vty, "Failed to rollback to previous configuration.\n"); + /* Print warnings (if any). */ + if (strlen(errmsg) > 0) + vty_out(vty, "%s\n", errmsg); + } else { + vty_out(vty, + "Failed to rollback to previous configuration.\n\n"); + vty_show_nb_errors(vty, ret, errmsg); + } return ret; } @@ -267,7 +359,9 @@ static int nb_cli_confirmed_commit_timeout(struct thread *thread) static int nb_cli_commit(struct vty *vty, bool force, unsigned int confirmed_timeout, char *comment) { + struct nb_context context = {}; uint32_t transaction_id = 0; + char errmsg[BUFSIZ] = {0}; int ret; /* Check if there's a pending confirmed commit. */ @@ -291,11 +385,6 @@ static int nb_cli_commit(struct vty *vty, bool force, return CMD_SUCCESS; } - if (vty_exclusive_lock != NULL && vty_exclusive_lock != vty) { - vty_out(vty, "%% Configuration is locked by another VTY.\n\n"); - return CMD_WARNING; - } - /* "force" parameter. */ if (!force && nb_candidate_needs_update(vty->candidate_config)) { vty_out(vty, @@ -315,8 +404,11 @@ static int nb_cli_commit(struct vty *vty, bool force, &vty->t_confirmed_commit_timeout); } - ret = nb_candidate_commit(vty->candidate_config, NB_CLIENT_CLI, true, - comment, &transaction_id); + context.client = NB_CLIENT_CLI; + context.user = vty; + ret = nb_candidate_commit(&context, vty->candidate_config, true, + comment, &transaction_id, errmsg, + sizeof(errmsg)); /* Map northbound return code to CLI return code. */ switch (ret) { @@ -326,15 +418,17 @@ static int nb_cli_commit(struct vty *vty, bool force, vty_out(vty, "%% Configuration committed successfully (Transaction ID #%u).\n\n", transaction_id); + /* Print warnings (if any). */ + if (strlen(errmsg) > 0) + vty_out(vty, "%s\n", errmsg); return CMD_SUCCESS; case NB_ERR_NO_CHANGES: vty_out(vty, "%% No configuration changes to commit.\n\n"); return CMD_SUCCESS; default: vty_out(vty, - "%% Failed to commit candidate configuration: %s.\n\n", - nb_err_name(ret)); - vty_out(vty, "Please check the logs for more details.\n"); + "%% Failed to commit candidate configuration.\n\n"); + vty_show_nb_errors(vty, ret, errmsg); return CMD_WARNING; } } @@ -348,6 +442,7 @@ static int nb_cli_candidate_load_file(struct vty *vty, struct lyd_node *dnode; struct ly_ctx *ly_ctx; int ly_format; + char buf[BUFSIZ]; switch (format) { case NB_CFG_FMT_CMDS: @@ -370,7 +465,9 @@ static int nb_cli_candidate_load_file(struct vty *vty, flog_warn(EC_LIB_LIBYANG, "%s: lyd_parse_path() failed", __func__); vty_out(vty, "%% Failed to load configuration:\n\n"); - vty_show_libyang_errors(vty, ly_ctx); + vty_out(vty, "%s", + yang_print_errors(ly_native_ctx, buf, + sizeof(buf))); return CMD_WARNING; } if (translator @@ -391,7 +488,8 @@ static int nb_cli_candidate_load_file(struct vty *vty, != NB_OK) { vty_out(vty, "%% Failed to merge the loaded configuration:\n\n"); - vty_show_libyang_errors(vty, ly_native_ctx); + vty_out(vty, "%s", + yang_print_errors(ly_native_ctx, buf, sizeof(buf))); return CMD_WARNING; } @@ -403,6 +501,7 @@ static int nb_cli_candidate_load_transaction(struct vty *vty, bool replace) { struct nb_config *loaded_config; + char buf[BUFSIZ]; loaded_config = nb_db_transaction_load(transaction_id); if (!loaded_config) { @@ -417,23 +516,64 @@ static int nb_cli_candidate_load_transaction(struct vty *vty, != NB_OK) { vty_out(vty, "%% Failed to merge the loaded configuration:\n\n"); - vty_show_libyang_errors(vty, ly_native_ctx); + vty_out(vty, "%s", + yang_print_errors(ly_native_ctx, buf, sizeof(buf))); return CMD_WARNING; } return CMD_SUCCESS; } +/* + * ly_iter_next_is_up: detects when iterating up on the yang model. + * + * This function detects whether next node in the iteration is upwards, + * then return the node otherwise return NULL. + */ +static struct lyd_node *ly_iter_next_up(const struct lyd_node *elem) +{ + /* Are we going downwards? Is this still not a leaf? */ + if (!(elem->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_ANYDATA))) + return NULL; + + /* Are there still leaves in this branch? */ + if (elem->next != NULL) + return NULL; + + return elem->parent; +} + +/* Prepare the configuration for display. */ +void nb_cli_show_config_prepare(struct nb_config *config, bool with_defaults) +{ + /* Nothing to do for daemons that don't implement any YANG module. */ + if (config->dnode == NULL) + return; + + lyd_schema_sort(config->dnode, 1); + + /* + * Call lyd_validate() only to create default child nodes, ignoring + * any possible validation error. This doesn't need to be done when + * displaying the running configuration since it's always fully + * validated. + */ + if (config != running_config) + (void)lyd_validate(&config->dnode, + LYD_OPT_CONFIG | LYD_OPT_WHENAUTODEL, + ly_native_ctx); +} + void nb_cli_show_dnode_cmds(struct vty *vty, struct lyd_node *root, bool with_defaults) { - struct lyd_node *next, *child; + struct lyd_node *next, *child, *parent; LY_TREE_DFS_BEGIN (root, next, child) { struct nb_node *nb_node; nb_node = child->schema->priv; - if (!nb_node->cbs.cli_show) + if (!nb_node || !nb_node->cbs.cli_show) goto next; /* Skip default values. */ @@ -442,7 +582,32 @@ void nb_cli_show_dnode_cmds(struct vty *vty, struct lyd_node *root, (*nb_node->cbs.cli_show)(vty, child, with_defaults); next: + /* + * When transiting upwards in the yang model we should + * give the previous container/list node a chance to + * print its close vty output (e.g. "!" or "end-family" + * etc...). + */ + parent = ly_iter_next_up(child); + if (parent != NULL) { + nb_node = parent->schema->priv; + if (nb_node && nb_node->cbs.cli_show_end) + (*nb_node->cbs.cli_show_end)(vty, parent); + } + + /* + * There is a possible path in this macro that ends up + * dereferencing child->parent->parent. We just null checked + * child->parent by checking (ly_iter_next_up(child) != NULL) + * above. + * + * I am not sure whether it is possible for the other + * conditions within this macro guarding the problem + * dereference to be satisfied when child->parent == NULL. + */ +#ifndef __clang_analyzer__ LY_TREE_DFS_END(root, next, child); +#endif } } @@ -454,7 +619,7 @@ static void nb_cli_show_config_cmds(struct vty *vty, struct nb_config *config, vty_out(vty, "Configuration:\n"); vty_out(vty, "!\n"); vty_out(vty, "frr version %s\n", FRR_VER_SHORT); - vty_out(vty, "frr defaults %s\n", DFLT_NAME); + vty_out(vty, "frr defaults %s\n", frr_defaults_profile()); LY_TREE_FOR (config->dnode, root) nb_cli_show_dnode_cmds(vty, root, with_defaults); @@ -503,6 +668,8 @@ static int nb_cli_show_config(struct vty *vty, struct nb_config *config, struct yang_translator *translator, bool with_defaults) { + nb_cli_show_config_prepare(config, with_defaults); + switch (format) { case NB_CFG_FMT_CMDS: nb_cli_show_config_cmds(vty, config, with_defaults); @@ -655,13 +822,18 @@ DEFPY (config_commit_check, "Commit changes into the running configuration\n" "Check if the configuration changes are valid\n") { + struct nb_context context = {}; + char errmsg[BUFSIZ] = {0}; int ret; - ret = nb_candidate_validate(vty->candidate_config); + context.client = NB_CLIENT_CLI; + context.user = vty; + ret = nb_candidate_validate(&context, vty->candidate_config, errmsg, + sizeof(errmsg)); if (ret != NB_OK) { vty_out(vty, "%% Failed to validate candidate configuration.\n\n"); - vty_show_libyang_errors(vty, ly_native_ctx); + vty_show_nb_errors(vty, ret, errmsg); return CMD_WARNING; } @@ -710,7 +882,7 @@ DEFPY (config_load, "configuration load\ <\ file [ [translate WORD$translator_family]] FILENAME$filename\ - |transaction (1-4294967296)$tid\ + |transaction (1-4294967295)$tid\ >\ [replace$replace]", "Configuration related settings\n" @@ -872,12 +1044,12 @@ DEFPY (show_config_compare, <\ candidate$c1_candidate\ |running$c1_running\ - |transaction (1-4294967296)$c1_tid\ + |transaction (1-4294967295)$c1_tid\ >\ <\ candidate$c2_candidate\ |running$c2_running\ - |transaction (1-4294967296)$c2_tid\ + |transaction (1-4294967295)$c2_tid\ >\ [ [translate WORD$translator_family]]", SHOW_STR @@ -967,11 +1139,11 @@ ALIAS (show_config_compare, "show configuration compare\ <\ running$c1_running\ - |transaction (1-4294967296)$c1_tid\ + |transaction (1-4294967295)$c1_tid\ >\ <\ running$c2_running\ - |transaction (1-4294967296)$c2_tid\ + |transaction (1-4294967295)$c2_tid\ >\ [ [translate WORD$translator_family]]", SHOW_STR @@ -1130,7 +1302,7 @@ DEFPY (show_config_transaction, show_config_transaction_cmd, "show configuration transaction\ [\ - (1-4294967296)$transaction_id\ + (1-4294967295)$transaction_id\ [ [translate WORD$translator_family]]\ [<\ with-defaults$with_defaults\ @@ -1495,8 +1667,10 @@ DEFPY (show_yang_module_translator, static int nb_cli_rollback_configuration(struct vty *vty, uint32_t transaction_id) { + struct nb_context context = {}; struct nb_config *candidate; char comment[80]; + char errmsg[BUFSIZ] = {0}; int ret; candidate = nb_db_transaction_load(transaction_id); @@ -1509,13 +1683,18 @@ static int nb_cli_rollback_configuration(struct vty *vty, snprintf(comment, sizeof(comment), "Rollback to transaction %u", transaction_id); - ret = nb_candidate_commit(candidate, NB_CLIENT_CLI, true, comment, - NULL); + context.client = NB_CLIENT_CLI; + context.user = vty; + ret = nb_candidate_commit(&context, candidate, true, comment, NULL, + errmsg, sizeof(errmsg)); nb_config_free(candidate); switch (ret) { case NB_OK: vty_out(vty, "%% Configuration was successfully rolled back.\n\n"); + /* Print warnings (if any). */ + if (strlen(errmsg) > 0) + vty_out(vty, "%s\n", errmsg); return CMD_SUCCESS; case NB_ERR_NO_CHANGES: vty_out(vty, @@ -1523,7 +1702,7 @@ static int nb_cli_rollback_configuration(struct vty *vty, return CMD_WARNING; default: vty_out(vty, "%% Rollback failed.\n\n"); - vty_out(vty, "Please check the logs for more details.\n"); + vty_show_nb_errors(vty, ret, errmsg); return CMD_WARNING; } } @@ -1531,7 +1710,7 @@ static int nb_cli_rollback_configuration(struct vty *vty, DEFPY (rollback_config, rollback_config_cmd, - "rollback configuration (1-4294967296)$transaction_id", + "rollback configuration (1-4294967295)$transaction_id", "Rollback to a previous state\n" "Running configuration\n" "Transaction ID\n") @@ -1548,7 +1727,7 @@ DEFPY (rollback_config, /* Debug CLI commands. */ static struct debug *nb_debugs[] = { &nb_dbg_cbs_config, &nb_dbg_cbs_state, &nb_dbg_cbs_rpc, - &nb_dbg_notif, &nb_dbg_events, + &nb_dbg_notif, &nb_dbg_events, &nb_dbg_libyang, }; static const char *const nb_debugs_conflines[] = { @@ -1557,6 +1736,7 @@ static const char *const nb_debugs_conflines[] = { "debug northbound callbacks rpc", "debug northbound notifications", "debug northbound events", + "debug northbound libyang", }; DEFINE_HOOK(nb_client_debug_set_all, (uint32_t flags, bool set), (flags, set)); @@ -1581,6 +1761,7 @@ DEFPY (debug_nb, callbacks$cbs [{configuration$cbs_cfg|state$cbs_state|rpc$cbs_rpc}]\ |notifications$notifications\ |events$events\ + |libyang$libyang\ >]", NO_STR DEBUG_STR @@ -1590,7 +1771,8 @@ DEFPY (debug_nb, "State\n" "RPC\n" "Notifications\n" - "Events\n") + "Events\n" + "libyang debugging\n") { uint32_t mode = DEBUG_NODE2MODE(vty->node); @@ -1608,10 +1790,16 @@ DEFPY (debug_nb, DEBUG_MODE_SET(&nb_dbg_notif, mode, !no); if (events) DEBUG_MODE_SET(&nb_dbg_events, mode, !no); + if (libyang) { + DEBUG_MODE_SET(&nb_dbg_libyang, mode, !no); + yang_debugging_set(!no); + } /* no specific debug --> act on all of them */ - if (strmatch(argv[argc - 1]->text, "northbound")) + if (strmatch(argv[argc - 1]->text, "northbound")) { nb_debug_set_all(mode, !no); + yang_debugging_set(!no); + } return CMD_SUCCESS; } @@ -1630,7 +1818,12 @@ static int nb_debug_config_write(struct vty *vty) } static struct debug_callbacks nb_dbg_cbs = {.debug_set_all = nb_debug_set_all}; -static struct cmd_node nb_debug_node = {NORTHBOUND_DEBUG_NODE, "", 1}; +static struct cmd_node nb_debug_node = { + .name = "northbound debug", + .node = NORTHBOUND_DEBUG_NODE, + .prompt = "", + .config_write = nb_debug_config_write, +}; void nb_cli_install_default(int node) { @@ -1692,9 +1885,9 @@ void nb_cli_init(struct thread_master *tm) /* Initialize the shared candidate configuration. */ vty_shared_candidate_config = nb_config_new(NULL); - /* Install debug commands */ debug_init(&nb_dbg_cbs); - install_node(&nb_debug_node, nb_debug_config_write); + + install_node(&nb_debug_node); install_element(ENABLE_NODE, &debug_nb_cmd); install_element(CONFIG_NODE, &debug_nb_cmd); diff --git a/lib/northbound_cli.h b/lib/northbound_cli.h index 209239ca63..112d62efda 100644 --- a/lib/northbound_cli.h +++ b/lib/northbound_cli.h @@ -108,7 +108,17 @@ extern int nb_cli_rpc(const char *xpath, struct list *input, extern void nb_cli_show_dnode_cmds(struct vty *vty, struct lyd_node *dnode, bool show_defaults); +/* + * Perform pending commit, if any. + * + * vty + * The vty context. + */ +extern void nb_cli_pending_commit_check(struct vty *vty); + /* Prototypes of internal functions. */ +extern void nb_cli_show_config_prepare(struct nb_config *config, + bool with_defaults); extern void nb_cli_confirmed_commit_clean(struct vty *vty); extern int nb_cli_confirmed_commit_rollback(struct vty *vty); extern void nb_cli_install_default(int node); diff --git a/lib/northbound_confd.c b/lib/northbound_confd.c index 0df286511e..1f480f3d02 100644 --- a/lib/northbound_confd.c +++ b/lib/northbound_confd.c @@ -285,8 +285,10 @@ frr_confd_cdb_diff_iter(confd_hkeypath_t *kp, enum cdb_iter_op cdb_op, static int frr_confd_cdb_read_cb_prepare(int fd, int *subp, int reslen) { + struct nb_context context = {}; struct nb_config *candidate; struct cdb_iter_args iter_args; + char errmsg[BUFSIZ] = {0}; int ret; candidate = nb_config_dup(running_config); @@ -321,24 +323,21 @@ static int frr_confd_cdb_read_cb_prepare(int fd, int *subp, int reslen) * required to apply them. */ transaction = NULL; - ret = nb_candidate_commit_prepare(candidate, NB_CLIENT_CONFD, NULL, - &transaction); + context.client = NB_CLIENT_CONFD; + ret = nb_candidate_commit_prepare(&context, candidate, NULL, + &transaction, errmsg, sizeof(errmsg)); if (ret != NB_OK && ret != NB_ERR_NO_CHANGES) { enum confd_errcode errcode; - const char *errmsg; switch (ret) { case NB_ERR_LOCKED: errcode = CONFD_ERRCODE_IN_USE; - errmsg = "Configuration is locked by another process"; break; case NB_ERR_RESOURCE: errcode = CONFD_ERRCODE_RESOURCE_DENIED; - errmsg = "Failed do allocate resources"; break; default: - errcode = CONFD_ERRCODE_INTERNAL; - errmsg = "Internal error"; + errcode = CONFD_ERRCODE_APPLICATION; break; } @@ -376,8 +375,10 @@ static int frr_confd_cdb_read_cb_commit(int fd, int *subp, int reslen) /* Apply the transaction. */ if (transaction) { struct nb_config *candidate = transaction->config; + char errmsg[BUFSIZ] = {0}; - nb_candidate_commit_apply(transaction, true, NULL); + nb_candidate_commit_apply(transaction, true, NULL, errmsg, + sizeof(errmsg)); nb_config_free(candidate); } @@ -401,8 +402,9 @@ static int frr_confd_cdb_read_cb_abort(int fd, int *subp, int reslen) /* Abort the transaction. */ if (transaction) { struct nb_config *candidate = transaction->config; + char errmsg[BUFSIZ] = {0}; - nb_candidate_commit_abort(transaction); + nb_candidate_commit_abort(transaction, errmsg, sizeof(errmsg)); nb_config_free(candidate); } @@ -612,7 +614,7 @@ static int frr_confd_data_get_elem(struct confd_trans_ctx *tctx, confd_hkeypath_t *kp) { struct nb_node *nb_node; - char xpath[BUFSIZ]; + char xpath[XPATH_MAXLEN]; struct yang_data *data; confd_value_t v; const void *list_entry = NULL; @@ -650,7 +652,7 @@ static int frr_confd_data_get_next(struct confd_trans_ctx *tctx, confd_hkeypath_t *kp, long next) { struct nb_node *nb_node; - char xpath[BUFSIZ]; + char xpath[XPATH_MAXLEN]; struct yang_data *data; const void *parent_list_entry, *nb_next; confd_value_t v[LIST_MAXKEYS]; @@ -758,8 +760,8 @@ static int frr_confd_data_get_object(struct confd_trans_ctx *tctx, { struct nb_node *nb_node; const struct lys_node *child; - char xpath[BUFSIZ]; - char xpath_child[XPATH_MAXLEN]; + char xpath[XPATH_MAXLEN]; + char xpath_child[XPATH_MAXLEN * 2]; struct list *elements; struct yang_data *data; const void *list_entry; @@ -832,7 +834,7 @@ static int frr_confd_data_get_object(struct confd_trans_ctx *tctx, static int frr_confd_data_get_next_object(struct confd_trans_ctx *tctx, confd_hkeypath_t *kp, long next) { - char xpath[BUFSIZ]; + char xpath[XPATH_MAXLEN]; struct nb_node *nb_node; struct list *elements; const void *parent_list_entry; @@ -916,7 +918,7 @@ static int frr_confd_data_get_next_object(struct confd_trans_ctx *tctx, /* Loop through list child nodes. */ LY_TREE_FOR (nb_node->snode->child, child) { struct nb_node *nb_node_child = child->priv; - char xpath_child[XPATH_MAXLEN]; + char xpath_child[XPATH_MAXLEN * 2]; confd_value_t *v; if (nvalues > CONFD_MAX_CHILD_NODES) @@ -1059,7 +1061,7 @@ static int frr_confd_action_execute(struct confd_user_info *uinfo, struct xml_tag *name, confd_hkeypath_t *kp, confd_tag_value_t *params, int nparams) { - char xpath[BUFSIZ]; + char xpath[XPATH_MAXLEN]; struct nb_node *nb_node; struct list *input; struct list *output; @@ -1091,7 +1093,7 @@ static int frr_confd_action_execute(struct confd_user_info *uinfo, /* Process input nodes. */ for (int i = 0; i < nparams; i++) { - char xpath_input[BUFSIZ]; + char xpath_input[XPATH_MAXLEN * 2]; char value_str[YANG_VALUE_MAXLEN]; snprintf(xpath_input, sizeof(xpath_input), "%s/%s", xpath, diff --git a/lib/northbound_db.c b/lib/northbound_db.c index 598805b961..244e760b2b 100644 --- a/lib/northbound_db.c +++ b/lib/northbound_db.c @@ -86,7 +86,7 @@ int nb_db_transaction_save(const struct nb_transaction *transaction, if (!ss) goto exit; - client_name = nb_client_name(transaction->client); + client_name = nb_client_name(transaction->context->client); /* Always record configurations in the XML format. */ if (lyd_print_mem(&config_str, transaction->config->dnode, LYD_XML, LYP_FORMAT | LYP_WITHSIBLINGS) diff --git a/lib/northbound_grpc.cpp b/lib/northbound_grpc.cpp new file mode 100644 index 0000000000..f35b4bb31b --- /dev/null +++ b/lib/northbound_grpc.cpp @@ -0,0 +1,1414 @@ +// +// Copyright (C) 2019 NetDEF, Inc. +// Renato Westphal +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; see the file COPYING; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// + +#include +#include +#include "grpc/frr-northbound.grpc.pb.h" + +#include "log.h" +#include "libfrr.h" +#include "version.h" +#include "command.h" +#include "lib_errors.h" +#include "northbound.h" +#include "northbound_db.h" +#include "frr_pthread.h" + +#include +#include +#include +#include + +#define GRPC_DEFAULT_PORT 50051 + +static void *grpc_pthread_start(void *arg); + +/* + * NOTE: we can't use the FRR debugging infrastructure here since it uses + * atomics and C++ has a different atomics API. Enable gRPC debugging + * unconditionally until we figure out a way to solve this problem. + */ +static bool nb_dbg_client_grpc = 1; + +static struct frr_pthread *fpt; + +/* Default frr_pthread attributes */ +static const struct frr_pthread_attr attr = { + .start = grpc_pthread_start, + .stop = NULL, +}; + +enum CallStatus { CREATE, PROCESS, FINISH }; + +/* Thanks gooble */ +class RpcStateBase +{ + public: + virtual void doCallback() = 0; +}; + +class NorthboundImpl; + +template class RpcState : RpcStateBase +{ + public: + RpcState(NorthboundImpl *svc, + void (NorthboundImpl::*cb)(RpcState *)) + : callback(cb), responder(&ctx), async_responder(&ctx), + service(svc){}; + + void doCallback() override + { + (service->*callback)(this); + } + + grpc::ServerContext ctx; + Q request; + S response; + grpc::ServerAsyncResponseWriter responder; + grpc::ServerAsyncWriter async_responder; + + NorthboundImpl *service; + void (NorthboundImpl::*callback)(RpcState *); + + void *context; + CallStatus state = CREATE; +}; + +#define REQUEST_RPC(NAME) \ + do { \ + auto _rpcState = \ + new RpcState( \ + this, &NorthboundImpl::Handle##NAME); \ + _service->Request##NAME(&_rpcState->ctx, &_rpcState->request, \ + &_rpcState->responder, _cq, _cq, \ + _rpcState); \ + } while (0) + +#define REQUEST_RPC_STREAMING(NAME) \ + do { \ + auto _rpcState = \ + new RpcState( \ + this, &NorthboundImpl::Handle##NAME); \ + _service->Request##NAME(&_rpcState->ctx, &_rpcState->request, \ + &_rpcState->async_responder, _cq, _cq, \ + _rpcState); \ + } while (0) + +class NorthboundImpl +{ + public: + NorthboundImpl(void) + { + _nextCandidateId = 0; + _service = new frr::Northbound::AsyncService(); + } + + ~NorthboundImpl(void) + { + // Delete candidates. + for (auto it = _candidates.begin(); it != _candidates.end(); + it++) + delete_candidate(&it->second); + } + + void Run(unsigned long port) + { + grpc::ServerBuilder builder; + std::stringstream server_address; + + server_address << "0.0.0.0:" << port; + + builder.AddListeningPort(server_address.str(), + grpc::InsecureServerCredentials()); + builder.RegisterService(_service); + + auto cq = builder.AddCompletionQueue(); + _cq = cq.get(); + auto _server = builder.BuildAndStart(); + + /* Schedule all RPC handlers */ + REQUEST_RPC(GetCapabilities); + REQUEST_RPC(CreateCandidate); + REQUEST_RPC(DeleteCandidate); + REQUEST_RPC(UpdateCandidate); + REQUEST_RPC(EditCandidate); + REQUEST_RPC(LoadToCandidate); + REQUEST_RPC(Commit); + REQUEST_RPC(GetTransaction); + REQUEST_RPC(LockConfig); + REQUEST_RPC(UnlockConfig); + REQUEST_RPC(Execute); + REQUEST_RPC_STREAMING(Get); + REQUEST_RPC_STREAMING(ListTransactions); + + zlog_notice("gRPC server listening on %s", + server_address.str().c_str()); + + /* Process inbound RPCs */ + void *tag; + bool ok; + while (true) { + _cq->Next(&tag, &ok); + GPR_ASSERT(ok); + static_cast(tag)->doCallback(); + tag = nullptr; + } + } + + void HandleGetCapabilities(RpcState *tag) + { + switch (tag->state) { + case CREATE: + REQUEST_RPC(GetCapabilities); + tag->state = PROCESS; + case PROCESS: { + if (nb_dbg_client_grpc) + zlog_debug("received RPC GetCapabilities()"); + + // Response: string frr_version = 1; + tag->response.set_frr_version(FRR_VERSION); + + // Response: bool rollback_support = 2; +#ifdef HAVE_CONFIG_ROLLBACKS + tag->response.set_rollback_support(true); +#else + tag->response.set_rollback_support(false); +#endif + + // Response: repeated ModuleData supported_modules = 3; + struct yang_module *module; + RB_FOREACH (module, yang_modules, &yang_modules) { + auto m = tag->response.add_supported_modules(); + + m->set_name(module->name); + if (module->info->rev_size) + m->set_revision( + module->info->rev[0].date); + m->set_organization(module->info->org); + } + + // Response: repeated Encoding supported_encodings = 4; + tag->response.add_supported_encodings(frr::JSON); + tag->response.add_supported_encodings(frr::XML); + + tag->responder.Finish(tag->response, grpc::Status::OK, + tag); + tag->state = FINISH; + break; + } + case FINISH: + delete tag; + } + } + + void HandleGet(RpcState *tag) + { + switch (tag->state) { + case CREATE: { + auto mypaths = new std::list(); + tag->context = mypaths; + auto paths = tag->request.path(); + for (const std::string &path : paths) { + mypaths->push_back(std::string(path)); + } + REQUEST_RPC_STREAMING(Get); + tag->state = PROCESS; + } + case PROCESS: { + // Request: DataType type = 1; + int type = tag->request.type(); + // Request: Encoding encoding = 2; + frr::Encoding encoding = tag->request.encoding(); + // Request: bool with_defaults = 3; + bool with_defaults = tag->request.with_defaults(); + + if (nb_dbg_client_grpc) + zlog_debug( + "received RPC Get(type: %u, encoding: %u, with_defaults: %u)", + type, encoding, with_defaults); + + auto mypaths = static_cast *>( + tag->context); + + if (mypaths->empty()) { + tag->async_responder.Finish(grpc::Status::OK, + tag); + tag->state = FINISH; + return; + } + + + frr::GetResponse response; + grpc::Status status; + + // Response: int64 timestamp = 1; + response.set_timestamp(time(NULL)); + + // Response: DataTree data = 2; + auto *data = response.mutable_data(); + data->set_encoding(tag->request.encoding()); + status = get_path(data, mypaths->back().c_str(), type, + encoding2lyd_format(encoding), + with_defaults); + + // Something went wrong... + if (!status.ok()) { + tag->async_responder.WriteAndFinish( + response, grpc::WriteOptions(), status, + tag); + tag->state = FINISH; + return; + } + + mypaths->pop_back(); + + tag->async_responder.Write(response, tag); + + break; + } + case FINISH: + if (nb_dbg_client_grpc) + zlog_debug("received RPC Get() end"); + + delete static_cast *>( + tag->context); + delete tag; + } + } + + void HandleCreateCandidate(RpcState *tag) + { + switch (tag->state) { + case CREATE: + REQUEST_RPC(CreateCandidate); + tag->state = PROCESS; + case PROCESS: { + if (nb_dbg_client_grpc) + zlog_debug("received RPC CreateCandidate()"); + + struct candidate *candidate = create_candidate(); + if (!candidate) { + tag->responder.Finish( + tag->response, + grpc::Status( + grpc::StatusCode:: + RESOURCE_EXHAUSTED, + "Can't create candidate configuration"), + tag); + } else { + tag->response.set_candidate_id(candidate->id); + tag->responder.Finish(tag->response, + grpc::Status::OK, tag); + } + + tag->state = FINISH; + + break; + } + case FINISH: + delete tag; + } + } + + void HandleDeleteCandidate(RpcState *tag) + { + switch (tag->state) { + case CREATE: + REQUEST_RPC(DeleteCandidate); + tag->state = PROCESS; + case PROCESS: { + + // Request: uint32 candidate_id = 1; + uint32_t candidate_id = tag->request.candidate_id(); + + if (nb_dbg_client_grpc) + zlog_debug( + "received RPC DeleteCandidate(candidate_id: %u)", + candidate_id); + + struct candidate *candidate = + get_candidate(candidate_id); + if (!candidate) { + tag->responder.Finish( + tag->response, + grpc::Status( + grpc::StatusCode::NOT_FOUND, + "candidate configuration not found"), + tag); + tag->state = FINISH; + return; + } else { + delete_candidate(candidate); + tag->responder.Finish(tag->response, + grpc::Status::OK, tag); + tag->state = FINISH; + return; + } + tag->state = FINISH; + break; + } + case FINISH: + delete tag; + } + } + + void HandleUpdateCandidate(RpcState *tag) + { + switch (tag->state) { + case CREATE: + REQUEST_RPC(UpdateCandidate); + tag->state = PROCESS; + case PROCESS: { + + // Request: uint32 candidate_id = 1; + uint32_t candidate_id = tag->request.candidate_id(); + + if (nb_dbg_client_grpc) + zlog_debug( + "received RPC UpdateCandidate(candidate_id: %u)", + candidate_id); + + struct candidate *candidate = + get_candidate(candidate_id); + + if (!candidate) + tag->responder.Finish( + tag->response, + grpc::Status( + grpc::StatusCode::NOT_FOUND, + "candidate configuration not found"), + tag); + else if (candidate->transaction) + tag->responder.Finish( + tag->response, + grpc::Status( + grpc::StatusCode:: + FAILED_PRECONDITION, + "candidate is in the middle of a transaction"), + tag); + else if (nb_candidate_update(candidate->config) + != NB_OK) + tag->responder.Finish( + tag->response, + grpc::Status( + grpc::StatusCode::INTERNAL, + "failed to update candidate configuration"), + tag); + + else + tag->responder.Finish(tag->response, + grpc::Status::OK, tag); + + tag->state = FINISH; + + break; + } + case FINISH: + delete tag; + } + } + + void HandleEditCandidate(RpcState *tag) + { + switch (tag->state) { + case CREATE: + REQUEST_RPC(EditCandidate); + tag->state = PROCESS; + case PROCESS: { + + // Request: uint32 candidate_id = 1; + uint32_t candidate_id = tag->request.candidate_id(); + + if (nb_dbg_client_grpc) + zlog_debug( + "received RPC EditCandidate(candidate_id: %u)", + candidate_id); + + struct candidate *candidate = + get_candidate(candidate_id); + + if (!candidate) { + tag->responder.Finish( + tag->response, + grpc::Status( + grpc::StatusCode::NOT_FOUND, + "candidate configuration not found"), + tag); + tag->state = FINISH; + break; + } + + struct nb_config *candidate_tmp = + nb_config_dup(candidate->config); + + auto pvs = tag->request.update(); + for (const frr::PathValue &pv : pvs) { + if (yang_dnode_edit(candidate_tmp->dnode, + pv.path(), pv.value()) + != 0) { + nb_config_free(candidate_tmp); + + tag->responder.Finish( + tag->response, + grpc::Status( + grpc::StatusCode:: + INVALID_ARGUMENT, + "Failed to update \"" + + pv.path() + + "\""), + tag); + + tag->state = FINISH; + return; + } + } + + pvs = tag->request.delete_(); + for (const frr::PathValue &pv : pvs) { + if (yang_dnode_delete(candidate_tmp->dnode, + pv.path()) + != 0) { + nb_config_free(candidate_tmp); + tag->responder.Finish( + tag->response, + grpc::Status( + grpc::StatusCode:: + INVALID_ARGUMENT, + "Failed to remove \"" + + pv.path() + + "\""), + tag); + tag->state = FINISH; + return; + } + } + + // No errors, accept all changes. + nb_config_replace(candidate->config, candidate_tmp, + false); + + tag->responder.Finish(tag->response, grpc::Status::OK, + tag); + + tag->state = FINISH; + + break; + } + case FINISH: + delete tag; + } + } + + void HandleLoadToCandidate(RpcState *tag) + { + switch (tag->state) { + case CREATE: + REQUEST_RPC(LoadToCandidate); + tag->state = PROCESS; + case PROCESS: { + // Request: uint32 candidate_id = 1; + uint32_t candidate_id = tag->request.candidate_id(); + + if (nb_dbg_client_grpc) + zlog_debug( + "received RPC LoadToCandidate(candidate_id: %u)", + candidate_id); + + // Request: LoadType type = 2; + int load_type = tag->request.type(); + // Request: DataTree config = 3; + auto config = tag->request.config(); + + + struct candidate *candidate = + get_candidate(candidate_id); + + if (!candidate) { + tag->responder.Finish( + tag->response, + grpc::Status( + grpc::StatusCode::NOT_FOUND, + "candidate configuration not found"), + tag); + tag->state = FINISH; + return; + } + + struct lyd_node *dnode = + dnode_from_data_tree(&config, true); + if (!dnode) { + tag->responder.Finish( + tag->response, + grpc::Status( + grpc::StatusCode::INTERNAL, + "Failed to parse the configuration"), + tag); + tag->state = FINISH; + return; + } + + struct nb_config *loaded_config = nb_config_new(dnode); + + if (load_type == frr::LoadToCandidateRequest::REPLACE) + nb_config_replace(candidate->config, + loaded_config, false); + else if (nb_config_merge(candidate->config, + loaded_config, false) + != NB_OK) { + tag->responder.Finish( + tag->response, + grpc::Status( + grpc::StatusCode::INTERNAL, + "Failed to merge the loaded configuration"), + tag); + tag->state = FINISH; + return; + } + + tag->responder.Finish(tag->response, grpc::Status::OK, + tag); + tag->state = FINISH; + break; + } + case FINISH: + delete tag; + } + } + + void + HandleCommit(RpcState *tag) + { + switch (tag->state) { + case CREATE: + REQUEST_RPC(Commit); + tag->state = PROCESS; + case PROCESS: { + // Request: uint32 candidate_id = 1; + uint32_t candidate_id = tag->request.candidate_id(); + if (nb_dbg_client_grpc) + zlog_debug( + "received RPC Commit(candidate_id: %u)", + candidate_id); + + // Request: Phase phase = 2; + int phase = tag->request.phase(); + // Request: string comment = 3; + const std::string comment = tag->request.comment(); + + // Find candidate configuration. + struct candidate *candidate = + get_candidate(candidate_id); + if (!candidate) { + tag->responder.Finish( + tag->response, + grpc::Status( + grpc::StatusCode::NOT_FOUND, + "candidate configuration not found"), + tag); + tag->state = FINISH; + return; + } + + int ret = NB_OK; + uint32_t transaction_id = 0; + + // Check for misuse of the two-phase commit protocol. + switch (phase) { + case frr::CommitRequest::PREPARE: + case frr::CommitRequest::ALL: + if (candidate->transaction) { + tag->responder.Finish( + tag->response, + grpc::Status( + grpc::StatusCode:: + FAILED_PRECONDITION, + "candidate is in the middle of a transaction"), + tag); + tag->state = FINISH; + return; + } + break; + case frr::CommitRequest::ABORT: + case frr::CommitRequest::APPLY: + if (!candidate->transaction) { + tag->responder.Finish( + tag->response, + grpc::Status( + grpc::StatusCode:: + FAILED_PRECONDITION, + "no transaction in progress"), + tag); + tag->state = FINISH; + return; + } + break; + default: + break; + } + + + // Execute the user request. + struct nb_context context = {}; + context.client = NB_CLIENT_GRPC; + char errmsg[BUFSIZ] = {0}; + + switch (phase) { + case frr::CommitRequest::VALIDATE: + zlog_debug("`-> Performing VALIDATE"); + ret = nb_candidate_validate( + &context, candidate->config, errmsg, + sizeof(errmsg)); + break; + case frr::CommitRequest::PREPARE: + zlog_debug("`-> Performing PREPARE"); + ret = nb_candidate_commit_prepare( + &context, candidate->config, + comment.c_str(), + &candidate->transaction, errmsg, + sizeof(errmsg)); + break; + case frr::CommitRequest::ABORT: + zlog_debug("`-> Performing ABORT"); + nb_candidate_commit_abort( + candidate->transaction, errmsg, + sizeof(errmsg)); + break; + case frr::CommitRequest::APPLY: + zlog_debug("`-> Performing ABORT"); + nb_candidate_commit_apply( + candidate->transaction, true, + &transaction_id, errmsg, + sizeof(errmsg)); + break; + case frr::CommitRequest::ALL: + zlog_debug("`-> Performing ALL"); + ret = nb_candidate_commit( + &context, candidate->config, true, + comment.c_str(), &transaction_id, + errmsg, sizeof(errmsg)); + break; + } + + // Map northbound error codes to gRPC status codes. + grpc::Status status; + switch (ret) { + case NB_OK: + status = grpc::Status::OK; + break; + case NB_ERR_NO_CHANGES: + status = grpc::Status(grpc::StatusCode::ABORTED, + errmsg); + break; + case NB_ERR_LOCKED: + status = grpc::Status( + grpc::StatusCode::UNAVAILABLE, errmsg); + break; + case NB_ERR_VALIDATION: + status = grpc::Status( + grpc::StatusCode::INVALID_ARGUMENT, + errmsg); + break; + case NB_ERR_RESOURCE: + status = grpc::Status( + grpc::StatusCode::RESOURCE_EXHAUSTED, + errmsg); + break; + case NB_ERR: + default: + status = grpc::Status( + grpc::StatusCode::INTERNAL, errmsg); + break; + } + + if (nb_dbg_client_grpc) + zlog_debug("`-> Result: %s (message: '%s')", + nb_err_name((enum nb_error)ret), + errmsg); + + if (ret == NB_OK) { + // Response: uint32 transaction_id = 1; + if (transaction_id) + tag->response.set_transaction_id( + transaction_id); + } + if (strlen(errmsg) > 0) + tag->response.set_error_message(errmsg); + + tag->responder.Finish(tag->response, status, tag); + tag->state = FINISH; + break; + } + case FINISH: + delete tag; + } + } + + void + HandleListTransactions(RpcState *tag) + { + switch (tag->state) { + case CREATE: + REQUEST_RPC_STREAMING(ListTransactions); + tag->context = new std::list>(); + nb_db_transactions_iterate(list_transactions_cb, + tag->context); + tag->state = PROCESS; + case PROCESS: { + if (nb_dbg_client_grpc) + zlog_debug("received RPC ListTransactions()"); + + auto list = static_cast> *>( + tag->context); + if (list->empty()) { + tag->async_responder.Finish(grpc::Status::OK, + tag); + tag->state = FINISH; + return; + } + auto item = list->back(); + + + frr::ListTransactionsResponse response; + + // Response: uint32 id = 1; + response.set_id(std::get<0>(item)); + + // Response: string client = 2; + response.set_client(std::get<1>(item).c_str()); + + // Response: string date = 3; + response.set_date(std::get<2>(item).c_str()); + + // Response: string comment = 4; + response.set_comment(std::get<3>(item).c_str()); + + list->pop_back(); + + tag->async_responder.Write(response, tag); + break; + } + case FINISH: + delete static_cast> *>( + tag->context); + delete tag; + } + } + + void HandleGetTransaction(RpcState *tag) + { + switch (tag->state) { + case CREATE: + REQUEST_RPC(GetTransaction); + tag->state = PROCESS; + case PROCESS: { + // Request: uint32 transaction_id = 1; + uint32_t transaction_id = tag->request.transaction_id(); + // Request: Encoding encoding = 2; + frr::Encoding encoding = tag->request.encoding(); + // Request: bool with_defaults = 3; + bool with_defaults = tag->request.with_defaults(); + + if (nb_dbg_client_grpc) + zlog_debug( + "received RPC GetTransaction(transaction_id: %u, encoding: %u)", + transaction_id, encoding); + + struct nb_config *nb_config; + + // Load configuration from the transactions database. + nb_config = nb_db_transaction_load(transaction_id); + if (!nb_config) { + tag->responder.Finish( + tag->response, + grpc::Status(grpc::StatusCode:: + INVALID_ARGUMENT, + "Transaction not found"), + tag); + tag->state = FINISH; + return; + } + + // Response: DataTree config = 1; + auto config = tag->response.mutable_config(); + config->set_encoding(encoding); + + // Dump data using the requested format. + if (data_tree_from_dnode(config, nb_config->dnode, + encoding2lyd_format(encoding), + with_defaults) + != 0) { + nb_config_free(nb_config); + tag->responder.Finish( + tag->response, + grpc::Status(grpc::StatusCode::INTERNAL, + "Failed to dump data"), + tag); + tag->state = FINISH; + return; + } + + nb_config_free(nb_config); + + tag->responder.Finish(tag->response, grpc::Status::OK, + tag); + tag->state = FINISH; + break; + } + case FINISH: + delete tag; + } + } + + void HandleLockConfig( + RpcState *tag) + { + switch (tag->state) { + case CREATE: + REQUEST_RPC(LockConfig); + tag->state = PROCESS; + case PROCESS: { + if (nb_dbg_client_grpc) + zlog_debug("received RPC LockConfig()"); + + if (nb_running_lock(NB_CLIENT_GRPC, NULL)) { + tag->responder.Finish( + tag->response, + grpc::Status( + grpc::StatusCode:: + FAILED_PRECONDITION, + "running configuration is locked already"), + tag); + tag->state = FINISH; + return; + } + + tag->responder.Finish(tag->response, grpc::Status::OK, + tag); + tag->state = FINISH; + break; + } + case FINISH: + delete tag; + } + } + + void HandleUnlockConfig(RpcState *tag) + { + switch (tag->state) { + case CREATE: + REQUEST_RPC(UnlockConfig); + tag->state = PROCESS; + case PROCESS: { + if (nb_dbg_client_grpc) + zlog_debug("received RPC UnlockConfig()"); + + if (nb_running_unlock(NB_CLIENT_GRPC, NULL)) { + tag->responder.Finish( + tag->response, + grpc::Status( + grpc::StatusCode:: + FAILED_PRECONDITION, + "failed to unlock the running configuration"), + tag); + tag->state = FINISH; + return; + } + + tag->responder.Finish(tag->response, grpc::Status::OK, + tag); + tag->state = FINISH; + break; + } + case FINISH: + delete tag; + } + } + + void + HandleExecute(RpcState *tag) + { + struct nb_node *nb_node; + struct list *input_list; + struct list *output_list; + struct listnode *node; + struct yang_data *data; + const char *xpath; + + switch (tag->state) { + case CREATE: + REQUEST_RPC(Execute); + tag->state = PROCESS; + case PROCESS: { + // Request: string path = 1; + xpath = tag->request.path().c_str(); + + if (nb_dbg_client_grpc) + zlog_debug("received RPC Execute(path: \"%s\")", + xpath); + + if (tag->request.path().empty()) { + tag->responder.Finish( + tag->response, + grpc::Status(grpc::StatusCode:: + INVALID_ARGUMENT, + "Data path is empty"), + tag); + tag->state = FINISH; + return; + } + + nb_node = nb_node_find(xpath); + if (!nb_node) { + tag->responder.Finish( + tag->response, + grpc::Status(grpc::StatusCode:: + INVALID_ARGUMENT, + "Unknown data path"), + tag); + tag->state = FINISH; + return; + } + + input_list = yang_data_list_new(); + output_list = yang_data_list_new(); + + // Read input parameters. + auto input = tag->request.input(); + for (const frr::PathValue &pv : input) { + // Request: repeated PathValue input = 2; + data = yang_data_new(pv.path().c_str(), + pv.value().c_str()); + listnode_add(input_list, data); + } + + // Execute callback registered for this XPath. + if (nb_callback_rpc(nb_node, xpath, input_list, + output_list) + != NB_OK) { + flog_warn(EC_LIB_NB_CB_RPC, + "%s: rpc callback failed: %s", + __func__, xpath); + list_delete(&input_list); + list_delete(&output_list); + + tag->responder.Finish( + tag->response, + grpc::Status(grpc::StatusCode::INTERNAL, + "RPC failed"), + tag); + tag->state = FINISH; + return; + } + + // Process output parameters. + for (ALL_LIST_ELEMENTS_RO(output_list, node, data)) { + // Response: repeated PathValue output = 1; + frr::PathValue *pv = tag->response.add_output(); + pv->set_path(data->xpath); + pv->set_value(data->value); + } + + // Release memory. + list_delete(&input_list); + list_delete(&output_list); + + tag->responder.Finish(tag->response, grpc::Status::OK, + tag); + tag->state = FINISH; + break; + } + case FINISH: + delete tag; + } + } + + private: + frr::Northbound::AsyncService *_service; + grpc::ServerCompletionQueue *_cq; + + struct candidate { + uint32_t id; + struct nb_config *config; + struct nb_transaction *transaction; + }; + std::map _candidates; + uint32_t _nextCandidateId; + + static int yang_dnode_edit(struct lyd_node *dnode, + const std::string &path, + const std::string &value) + { + ly_errno = LY_SUCCESS; + dnode = lyd_new_path(dnode, ly_native_ctx, path.c_str(), + (void *)value.c_str(), + (LYD_ANYDATA_VALUETYPE)0, + LYD_PATH_OPT_UPDATE); + if (!dnode && ly_errno != LY_SUCCESS) { + flog_warn(EC_LIB_LIBYANG, "%s: lyd_new_path() failed", + __func__); + return -1; + } + + return 0; + } + + static int yang_dnode_delete(struct lyd_node *dnode, + const std::string &path) + { + dnode = yang_dnode_get(dnode, path.c_str()); + if (!dnode) + return -1; + + lyd_free(dnode); + + return 0; + } + + static LYD_FORMAT encoding2lyd_format(enum frr::Encoding encoding) + { + switch (encoding) { + case frr::JSON: + return LYD_JSON; + case frr::XML: + return LYD_XML; + default: + flog_err(EC_LIB_DEVELOPMENT, + "%s: unknown data encoding format (%u)", + __func__, encoding); + exit(1); + } + } + + static int get_oper_data_cb(const struct lys_node *snode, + struct yang_translator *translator, + struct yang_data *data, void *arg) + { + struct lyd_node *dnode = static_cast(arg); + int ret = yang_dnode_edit(dnode, data->xpath, data->value); + yang_data_free(data); + + return (ret == 0) ? NB_OK : NB_ERR; + } + + static void list_transactions_cb(void *arg, int transaction_id, + const char *client_name, + const char *date, const char *comment) + { + + auto list = static_cast> *>(arg); + list->push_back(std::make_tuple( + transaction_id, std::string(client_name), + std::string(date), std::string(comment))); + } + + static int data_tree_from_dnode(frr::DataTree *dt, + const struct lyd_node *dnode, + LYD_FORMAT lyd_format, + bool with_defaults) + { + char *strp; + int options = 0; + + SET_FLAG(options, LYP_FORMAT | LYP_WITHSIBLINGS); + if (with_defaults) + SET_FLAG(options, LYP_WD_ALL); + else + SET_FLAG(options, LYP_WD_TRIM); + + if (lyd_print_mem(&strp, dnode, lyd_format, options) == 0) { + if (strp) { + dt->set_data(strp); + free(strp); + } + return 0; + } + + return -1; + } + + static struct lyd_node *dnode_from_data_tree(const frr::DataTree *dt, + bool config_only) + { + struct lyd_node *dnode; + int options; + + if (config_only) + options = LYD_OPT_CONFIG; + else + options = LYD_OPT_DATA | LYD_OPT_DATA_NO_YANGLIB; + + dnode = lyd_parse_mem(ly_native_ctx, dt->data().c_str(), + encoding2lyd_format(dt->encoding()), + options); + + return dnode; + } + + static struct lyd_node *get_dnode_config(const std::string &path) + { + struct lyd_node *dnode; + + dnode = yang_dnode_get(running_config->dnode, + path.empty() ? NULL : path.c_str()); + if (dnode) + dnode = yang_dnode_dup(dnode); + + return dnode; + } + + static struct lyd_node *get_dnode_state(const std::string &path) + { + struct lyd_node *dnode; + + dnode = yang_dnode_new(ly_native_ctx, false); + if (nb_oper_data_iterate(path.c_str(), NULL, 0, + get_oper_data_cb, dnode) + != NB_OK) { + yang_dnode_free(dnode); + return NULL; + } + + return dnode; + } + + static grpc::Status get_path(frr::DataTree *dt, const std::string &path, + int type, LYD_FORMAT lyd_format, + bool with_defaults) + { + struct lyd_node *dnode_config = NULL; + struct lyd_node *dnode_state = NULL; + struct lyd_node *dnode_final; + + // Configuration data. + if (type == frr::GetRequest_DataType_ALL + || type == frr::GetRequest_DataType_CONFIG) { + dnode_config = get_dnode_config(path); + if (!dnode_config) + return grpc::Status( + grpc::StatusCode::INVALID_ARGUMENT, + "Data path not found"); + } + + // Operational data. + if (type == frr::GetRequest_DataType_ALL + || type == frr::GetRequest_DataType_STATE) { + dnode_state = get_dnode_state(path); + if (!dnode_state) { + if (dnode_config) + yang_dnode_free(dnode_config); + return grpc::Status( + grpc::StatusCode::INVALID_ARGUMENT, + "Failed to fetch operational data"); + } + } + + switch (type) { + case frr::GetRequest_DataType_ALL: + // + // Combine configuration and state data into a single + // dnode. + // + if (lyd_merge(dnode_state, dnode_config, + LYD_OPT_EXPLICIT) + != 0) { + yang_dnode_free(dnode_state); + yang_dnode_free(dnode_config); + return grpc::Status( + grpc::StatusCode::INTERNAL, + "Failed to merge configuration and state data"); + } + + dnode_final = dnode_state; + break; + case frr::GetRequest_DataType_CONFIG: + dnode_final = dnode_config; + break; + case frr::GetRequest_DataType_STATE: + dnode_final = dnode_state; + break; + } + + // Validate data to create implicit default nodes if necessary. + int validate_opts = 0; + if (type == frr::GetRequest_DataType_CONFIG) + validate_opts = LYD_OPT_CONFIG; + else + validate_opts = LYD_OPT_DATA | LYD_OPT_DATA_NO_YANGLIB; + lyd_validate(&dnode_final, validate_opts, ly_native_ctx); + + // Dump data using the requested format. + int ret = data_tree_from_dnode(dt, dnode_final, lyd_format, + with_defaults); + yang_dnode_free(dnode_final); + if (ret != 0) + return grpc::Status(grpc::StatusCode::INTERNAL, + "Failed to dump data"); + + return grpc::Status::OK; + } + + struct candidate *create_candidate(void) + { + uint32_t candidate_id = ++_nextCandidateId; + + // Check for overflow. + // TODO: implement an algorithm for unique reusable IDs. + if (candidate_id == 0) + return NULL; + + struct candidate *candidate = &_candidates[candidate_id]; + candidate->id = candidate_id; + candidate->config = nb_config_dup(running_config); + candidate->transaction = NULL; + + return candidate; + } + + void delete_candidate(struct candidate *candidate) + { + char errmsg[BUFSIZ] = {0}; + + _candidates.erase(candidate->id); + nb_config_free(candidate->config); + if (candidate->transaction) + nb_candidate_commit_abort(candidate->transaction, + errmsg, sizeof(errmsg)); + } + + struct candidate *get_candidate(uint32_t candidate_id) + { + struct candidate *candidate; + + if (_candidates.count(candidate_id) == 0) + return NULL; + + return &_candidates[candidate_id]; + } +}; + +static void *grpc_pthread_start(void *arg) +{ + struct frr_pthread *fpt = static_cast(arg); + unsigned long *port = static_cast(fpt->data); + + frr_pthread_set_name(fpt); + + NorthboundImpl nb; + nb.Run(*port); + + return NULL; +} + +static int frr_grpc_init(unsigned long *port) +{ + fpt = frr_pthread_new(&attr, "frr-grpc", "frr-grpc"); + fpt->data = static_cast(port); + + /* Create a pthread for gRPC since it runs its own event loop. */ + if (frr_pthread_run(fpt, NULL) < 0) { + flog_err(EC_LIB_SYSTEM_CALL, "%s: error creating pthread: %s", + __func__, safe_strerror(errno)); + return -1; + } + pthread_detach(fpt->thread); + + return 0; +} + +static int frr_grpc_finish(void) +{ + if (fpt) + frr_pthread_destroy(fpt); + // TODO: cancel the gRPC pthreads gracefully. + + return 0; +} + +/* + * This is done this way because module_init and module_late_init are both + * called during daemon pre-fork initialization. Because the GRPC library + * spawns threads internally, we need to delay initializing it until after + * fork. This is done by scheduling this init function as an event task, since + * the event loop doesn't run until after fork. + */ +static int frr_grpc_module_very_late_init(struct thread *thread) +{ + static unsigned long port = GRPC_DEFAULT_PORT; + const char *args = THIS_MODULE->load_args; + + // Parse port number. + if (args) { + try { + port = std::stoul(args); + if (port < 1024) + throw std::invalid_argument( + "can't use privileged port"); + if (port > UINT16_MAX) + throw std::invalid_argument( + "port number is too big"); + } catch (std::exception &e) { + flog_err(EC_LIB_GRPC_INIT, + "%s: failed to parse port number: %s", + __func__, e.what()); + goto error; + } + } + + if (frr_grpc_init(&port) < 0) + goto error; + + return 0; + +error: + flog_err(EC_LIB_GRPC_INIT, "failed to initialize the gRPC module"); + return -1; +} + +static int frr_grpc_module_late_init(struct thread_master *tm) +{ + thread_add_event(tm, frr_grpc_module_very_late_init, NULL, 0, NULL); + hook_register(frr_fini, frr_grpc_finish); + + return 0; +} + +static int frr_grpc_module_init(void) +{ + hook_register(frr_late_init, frr_grpc_module_late_init); + + return 0; +} + +FRR_MODULE_SETUP(.name = "frr_grpc", .version = FRR_VERSION, + .description = "FRR gRPC northbound module", + .init = frr_grpc_module_init, ) diff --git a/lib/northbound_sysrepo.c b/lib/northbound_sysrepo.c index 33b6c24782..b5ef040a3f 100644 --- a/lib/northbound_sysrepo.c +++ b/lib/northbound_sysrepo.c @@ -37,13 +37,11 @@ DEFINE_MTYPE_STATIC(LIB, SYSREPO, "Sysrepo module") static struct debug nb_dbg_client_sysrepo = {0, "Northbound client: Sysrepo"}; static struct thread_master *master; -static struct list *sysrepo_threads; static sr_session_ctx_t *session; static sr_conn_ctx_t *connection; static struct nb_transaction *transaction; static int frr_sr_read_cb(struct thread *thread); -static int frr_sr_write_cb(struct thread *thread); static int frr_sr_finish(void); /* Convert FRR YANG data value to sysrepo YANG data value. */ @@ -176,6 +174,9 @@ static int frr_sr_process_change(struct nb_config *candidate, xpath = sr_data->xpath; + DEBUGD(&nb_dbg_client_sysrepo, "sysrepo: processing change [xpath %s]", + xpath); + /* Non-presence container - nothing to do. */ if (sr_data->type == SR_CONTAINER_T) return NB_OK; @@ -225,7 +226,7 @@ static int frr_sr_process_change(struct nb_config *candidate, ret = nb_candidate_edit(candidate, nb_node, nb_op, xpath, NULL, data); yang_data_free(data); - if (ret != NB_OK) { + if (ret != NB_OK && ret != NB_ERR_NOT_FOUND) { flog_warn( EC_LIB_NB_CANDIDATE_EDIT_ERROR, "%s: failed to edit candidate configuration: operation [%s] xpath [%s]", @@ -236,23 +237,22 @@ static int frr_sr_process_change(struct nb_config *candidate, return NB_OK; } -static int frr_sr_config_change_cb_verify(sr_session_ctx_t *session, - const char *module_name, - bool startup_config) +static int frr_sr_config_change_cb_prepare(sr_session_ctx_t *session, + const char *module_name) { sr_change_iter_t *it; int ret; sr_change_oper_t sr_op; sr_val_t *sr_old_val, *sr_new_val; - char xpath[XPATH_MAXLEN]; + struct nb_context context = {}; struct nb_config *candidate; + char errmsg[BUFSIZ] = {0}; - snprintf(xpath, sizeof(xpath), "/%s:*", module_name); - ret = sr_get_changes_iter(session, xpath, &it); + ret = sr_get_changes_iter(session, "//*", &it); if (ret != SR_ERR_OK) { flog_err(EC_LIB_LIBSYSREPO, - "%s: sr_get_changes_iter() failed for xpath %s", - __func__, xpath); + "%s: sr_get_changes_iter() failed for \"%s\"", + __func__, module_name); return ret; } @@ -276,29 +276,27 @@ static int frr_sr_config_change_cb_verify(sr_session_ctx_t *session, } transaction = NULL; - if (startup_config) { - /* - * sysrepod sends the entire startup configuration using a - * single event (SR_EV_ENABLED). This means we need to perform - * the full two-phase commit protocol in one go here. - */ - ret = nb_candidate_commit(candidate, NB_CLIENT_SYSREPO, true, - NULL, NULL); - } else { - /* - * Validate the configuration changes and allocate all resources - * required to apply them. - */ - ret = nb_candidate_commit_prepare(candidate, NB_CLIENT_SYSREPO, - NULL, &transaction); - } + context.client = NB_CLIENT_SYSREPO; + /* + * Validate the configuration changes and allocate all resources + * required to apply them. + */ + ret = nb_candidate_commit_prepare(&context, candidate, NULL, + &transaction, errmsg, sizeof(errmsg)); + if (ret != NB_OK && ret != NB_ERR_NO_CHANGES) + flog_warn( + EC_LIB_LIBSYSREPO, + "%s: failed to prepare configuration transaction: %s (%s)", + __func__, nb_err_name(ret), errmsg); + + if (!transaction) + nb_config_free(candidate); /* Map northbound return code to sysrepo return code. */ switch (ret) { case NB_OK: return SR_ERR_OK; case NB_ERR_NO_CHANGES: - nb_config_free(candidate); return SR_ERR_OK; case NB_ERR_LOCKED: return SR_ERR_LOCKED; @@ -315,8 +313,10 @@ static int frr_sr_config_change_cb_apply(sr_session_ctx_t *session, /* Apply the transaction. */ if (transaction) { struct nb_config *candidate = transaction->config; + char errmsg[BUFSIZ] = {0}; - nb_candidate_commit_apply(transaction, true, NULL); + nb_candidate_commit_apply(transaction, true, NULL, errmsg, + sizeof(errmsg)); nb_config_free(candidate); } @@ -329,8 +329,9 @@ static int frr_sr_config_change_cb_abort(sr_session_ctx_t *session, /* Abort the transaction. */ if (transaction) { struct nb_config *candidate = transaction->config; + char errmsg[BUFSIZ] = {0}; - nb_candidate_commit_abort(transaction); + nb_candidate_commit_abort(transaction, errmsg, sizeof(errmsg)); nb_config_free(candidate); } @@ -339,22 +340,20 @@ static int frr_sr_config_change_cb_abort(sr_session_ctx_t *session, /* Callback for changes in the running configuration. */ static int frr_sr_config_change_cb(sr_session_ctx_t *session, - const char *module_name, - sr_notif_event_t sr_ev, void *private_ctx) + const char *module_name, const char *xpath, + sr_event_t sr_ev, uint32_t request_id, + void *private_data) { switch (sr_ev) { case SR_EV_ENABLED: - return frr_sr_config_change_cb_verify(session, module_name, - true); - case SR_EV_VERIFY: - return frr_sr_config_change_cb_verify(session, module_name, - false); - case SR_EV_APPLY: + case SR_EV_CHANGE: + return frr_sr_config_change_cb_prepare(session, module_name); + case SR_EV_DONE: return frr_sr_config_change_cb_apply(session, module_name); case SR_EV_ABORT: return frr_sr_config_change_cb_abort(session, module_name); default: - flog_err(EC_LIB_LIBSYSREPO, "%s: unknown sysrepo event: %u", + flog_err(EC_LIB_LIBSYSREPO, "%s: unexpected sysrepo event: %u", __func__, sr_ev); return SR_ERR_INTERNAL; } @@ -364,70 +363,49 @@ static int frr_sr_state_data_iter_cb(const struct lys_node *snode, struct yang_translator *translator, struct yang_data *data, void *arg) { - struct list *elements = arg; - - listnode_add(elements, data); + struct lyd_node *dnode = arg; + + ly_errno = 0; + dnode = lyd_new_path(dnode, ly_native_ctx, data->xpath, data->value, 0, + LYD_PATH_OPT_UPDATE); + if (!dnode && ly_errno) { + flog_warn(EC_LIB_LIBYANG, "%s: lyd_new_path() failed", + __func__); + yang_data_free(data); + return NB_ERR; + } + yang_data_free(data); return NB_OK; } /* Callback for state retrieval. */ -static int frr_sr_state_cb(const char *xpath, sr_val_t **values, - size_t *values_cnt, uint64_t request_id, - const char *original_xpath, void *private_ctx) +static int frr_sr_state_cb(sr_session_ctx_t *session, const char *module_name, + const char *xpath, const char *request_xpath, + uint32_t request_id, struct lyd_node **parent, + void *private_ctx) { - struct list *elements; - struct yang_data *data; - struct listnode *node; - sr_val_t *v; - int ret, count, i = 0; + struct lyd_node *dnode; - elements = yang_data_list_new(); - if (nb_oper_data_iterate(xpath, NULL, NB_OPER_DATA_ITER_NORECURSE, - frr_sr_state_data_iter_cb, elements) + dnode = *parent; + if (nb_oper_data_iterate(request_xpath, NULL, 0, + frr_sr_state_data_iter_cb, dnode) != NB_OK) { flog_warn(EC_LIB_NB_OPERATIONAL_DATA, "%s: failed to obtain operational data [xpath %s]", __func__, xpath); - goto exit; - } - - if (list_isempty(elements)) - goto exit; - - count = listcount(elements); - ret = sr_new_values(count, &v); - if (ret != SR_ERR_OK) { - flog_err(EC_LIB_LIBSYSREPO, "%s: sr_new_values(): %s", __func__, - sr_strerror(ret)); - goto exit; - } - - for (ALL_LIST_ELEMENTS_RO(elements, node, data)) { - if (yang_data_frr2sr(data, &v[i++]) != 0) { - flog_err(EC_LIB_SYSREPO_DATA_CONVERT, - "%s: failed to convert data to sysrepo format", - __func__); - } + return SR_ERR_INTERNAL; } - *values = v; - *values_cnt = count; - - list_delete(&elements); - - return SR_ERR_OK; - -exit: - list_delete(&elements); - *values = NULL; - values_cnt = 0; + *parent = dnode; return SR_ERR_OK; } -static int frr_sr_config_rpc_cb(const char *xpath, const sr_val_t *sr_input, - const size_t input_cnt, sr_val_t **sr_output, +static int frr_sr_config_rpc_cb(sr_session_ctx_t *session, const char *xpath, + const sr_val_t *sr_input, + const size_t input_cnt, sr_event_t sr_ev, + uint32_t request_id, sr_val_t **sr_output, size_t *sr_output_cnt, void *private_ctx) { struct nb_node *nb_node; @@ -534,8 +512,7 @@ static int frr_sr_notification_send(const char *xpath, struct list *arguments) } } - ret = sr_event_notif_send(session, xpath, values, values_cnt, - SR_EV_NOTIF_DEFAULT); + ret = sr_event_notif_send(session, xpath, values, values_cnt); if (ret != SR_ERR_OK) { flog_err(EC_LIB_LIBSYSREPO, "%s: sr_event_notif_send() failed for xpath %s", @@ -546,102 +523,13 @@ static int frr_sr_notification_send(const char *xpath, struct list *arguments) return NB_OK; } -/* Code to integrate the sysrepo client into FRR main event loop. */ -struct sysrepo_thread { - struct thread *thread; - sr_fd_event_t event; - int fd; -}; - -static struct sysrepo_thread *frr_sr_fd_lookup(sr_fd_event_t event, int fd) -{ - struct sysrepo_thread *sr_thread; - struct listnode *node; - - for (ALL_LIST_ELEMENTS_RO(sysrepo_threads, node, sr_thread)) { - if (sr_thread->event == event && sr_thread->fd == fd) - return sr_thread; - } - - return NULL; -} - -static void frr_sr_fd_add(int event, int fd) -{ - struct sysrepo_thread *sr_thread; - - if (frr_sr_fd_lookup(event, fd) != NULL) - return; - - sr_thread = XCALLOC(MTYPE_SYSREPO, sizeof(*sr_thread)); - sr_thread->event = event; - sr_thread->fd = fd; - listnode_add(sysrepo_threads, sr_thread); - - switch (event) { - case SR_FD_INPUT_READY: - thread_add_read(master, frr_sr_read_cb, NULL, fd, - &sr_thread->thread); - break; - case SR_FD_OUTPUT_READY: - thread_add_write(master, frr_sr_write_cb, NULL, fd, - &sr_thread->thread); - break; - default: - return; - } -} - -static void frr_sr_fd_free(struct sysrepo_thread *sr_thread) -{ - THREAD_OFF(sr_thread->thread); - XFREE(MTYPE_SYSREPO, sr_thread); -} - -static void frr_sr_fd_del(int event, int fd) -{ - struct sysrepo_thread *sr_thread; - - sr_thread = frr_sr_fd_lookup(event, fd); - if (!sr_thread) - return; - - listnode_delete(sysrepo_threads, sr_thread); - frr_sr_fd_free(sr_thread); -} - -static void frr_sr_fd_update(sr_fd_change_t *fd_change_set, - size_t fd_change_set_cnt) -{ - for (size_t i = 0; i < fd_change_set_cnt; i++) { - int fd = fd_change_set[i].fd; - int event = fd_change_set[i].events; - - if (event != SR_FD_INPUT_READY && event != SR_FD_OUTPUT_READY) - continue; - - switch (fd_change_set[i].action) { - case SR_FD_START_WATCHING: - frr_sr_fd_add(event, fd); - break; - case SR_FD_STOP_WATCHING: - frr_sr_fd_del(event, fd); - break; - default: - break; - } - } -} - static int frr_sr_read_cb(struct thread *thread) { + sr_subscription_ctx_t *sr_subscription = THREAD_ARG(thread); int fd = THREAD_FD(thread); - sr_fd_change_t *fd_change_set = NULL; - size_t fd_change_set_cnt = 0; int ret; - ret = sr_fd_event_process(fd, SR_FD_INPUT_READY, &fd_change_set, - &fd_change_set_cnt); + ret = sr_process_events(sr_subscription, session, NULL); if (ret != SR_ERR_OK) { flog_err(EC_LIB_LIBSYSREPO, "%s: sr_fd_event_process(): %s", __func__, sr_strerror(ret)); @@ -649,31 +537,7 @@ static int frr_sr_read_cb(struct thread *thread) } thread = NULL; - thread_add_read(master, frr_sr_read_cb, NULL, fd, &thread); - - frr_sr_fd_update(fd_change_set, fd_change_set_cnt); - free(fd_change_set); - - return 0; -} - -static int frr_sr_write_cb(struct thread *thread) -{ - int fd = THREAD_FD(thread); - sr_fd_change_t *fd_change_set = NULL; - size_t fd_change_set_cnt = 0; - int ret; - - ret = sr_fd_event_process(fd, SR_FD_OUTPUT_READY, &fd_change_set, - &fd_change_set_cnt); - if (ret != SR_ERR_OK) { - flog_err(EC_LIB_LIBSYSREPO, "%s: sr_fd_event_process(): %s", - __func__, sr_strerror(ret)); - return -1; - } - - frr_sr_fd_update(fd_change_set, fd_change_set_cnt); - free(fd_change_set); + thread_add_read(master, frr_sr_read_cb, sr_subscription, fd, &thread); return 0; } @@ -682,9 +546,13 @@ static void frr_sr_subscribe_config(struct yang_module *module) { int ret; + DEBUGD(&nb_dbg_client_sysrepo, + "sysrepo: subscribing for configuration changes made in the '%s' module", + module->name); + ret = sr_module_change_subscribe( - session, module->name, frr_sr_config_change_cb, NULL, 0, - SR_SUBSCR_DEFAULT | SR_SUBSCR_EV_ENABLED, + session, module->name, NULL, frr_sr_config_change_cb, NULL, 0, + SR_SUBSCR_DEFAULT | SR_SUBSCR_ENABLED | SR_SUBSCR_NO_THREAD, &module->sr_subscription); if (ret != SR_ERR_OK) flog_err(EC_LIB_LIBSYSREPO, "sr_module_change_subscribe(): %s", @@ -705,14 +573,14 @@ static int frr_sr_subscribe_state(const struct lys_node *snode, void *arg) nb_node = snode->priv; - DEBUGD(&nb_dbg_client_sysrepo, "%s: providing data to '%s'", __func__, + DEBUGD(&nb_dbg_client_sysrepo, "sysrepo: providing data to '%s'", nb_node->xpath); - ret = sr_dp_get_items_subscribe( - session, nb_node->xpath, frr_sr_state_cb, NULL, - SR_SUBSCR_CTX_REUSE, &module->sr_subscription); + ret = sr_oper_get_items_subscribe( + session, snode->module->name, nb_node->xpath, frr_sr_state_cb, + NULL, SR_SUBSCR_CTX_REUSE, &module->sr_subscription); if (ret != SR_ERR_OK) - flog_err(EC_LIB_LIBSYSREPO, "sr_dp_get_items_subscribe(): %s", + flog_err(EC_LIB_LIBSYSREPO, "sr_oper_get_items_subscribe(): %s", sr_strerror(ret)); return YANG_ITER_CONTINUE; @@ -729,11 +597,11 @@ static int frr_sr_subscribe_rpc(const struct lys_node *snode, void *arg) nb_node = snode->priv; - DEBUGD(&nb_dbg_client_sysrepo, "%s: providing RPC to '%s'", __func__, + DEBUGD(&nb_dbg_client_sysrepo, "sysrepo: providing RPC to '%s'", nb_node->xpath); ret = sr_rpc_subscribe(session, nb_node->xpath, frr_sr_config_rpc_cb, - NULL, SR_SUBSCR_CTX_REUSE, + NULL, 0, SR_SUBSCR_CTX_REUSE, &module->sr_subscription); if (ret != SR_ERR_OK) flog_err(EC_LIB_LIBSYSREPO, "sr_rpc_subscribe(): %s", @@ -742,30 +610,6 @@ static int frr_sr_subscribe_rpc(const struct lys_node *snode, void *arg) return YANG_ITER_CONTINUE; } -static int frr_sr_subscribe_action(const struct lys_node *snode, void *arg) -{ - struct yang_module *module = arg; - struct nb_node *nb_node; - int ret; - - if (snode->nodetype != LYS_ACTION) - return YANG_ITER_CONTINUE; - - nb_node = snode->priv; - - DEBUGD(&nb_dbg_client_sysrepo, "%s: providing action to '%s'", __func__, - nb_node->xpath); - - ret = sr_action_subscribe(session, nb_node->xpath, frr_sr_config_rpc_cb, - NULL, SR_SUBSCR_CTX_REUSE, - &module->sr_subscription); - if (ret != SR_ERR_OK) - flog_err(EC_LIB_LIBSYSREPO, "sr_action_subscribe(): %s", - sr_strerror(ret)); - - return YANG_ITER_CONTINUE; -} - /* CLI commands. */ DEFUN (debug_nb_sr, debug_nb_sr_cmd, @@ -813,22 +657,13 @@ static void frr_sr_cli_init(void) } /* FRR's Sysrepo initialization. */ -static int frr_sr_init(const char *program_name) +static int frr_sr_init(void) { struct yang_module *module; - int sysrepo_fd, ret; - - sysrepo_threads = list_new(); - - ret = sr_fd_watcher_init(&sysrepo_fd, NULL); - if (ret != SR_ERR_OK) { - flog_err(EC_LIB_SYSREPO_INIT, "%s: sr_fd_watcher_init(): %s", - __func__, sr_strerror(ret)); - goto cleanup; - } + int ret; /* Connect to Sysrepo. */ - ret = sr_connect(program_name, SR_CONN_DEFAULT, &connection); + ret = sr_connect(SR_CONN_DEFAULT, &connection); if (ret != SR_ERR_OK) { flog_err(EC_LIB_SYSREPO_INIT, "%s: sr_connect(): %s", __func__, sr_strerror(ret)); @@ -836,8 +671,7 @@ static int frr_sr_init(const char *program_name) } /* Start session. */ - ret = sr_session_start(connection, SR_DS_RUNNING, SR_SESS_DEFAULT, - &session); + ret = sr_session_start(connection, SR_DS_RUNNING, &session); if (ret != SR_ERR_OK) { flog_err(EC_LIB_SYSREPO_INIT, "%s: sr_session_start(): %s", __func__, sr_strerror(ret)); @@ -846,19 +680,28 @@ static int frr_sr_init(const char *program_name) /* Perform subscriptions. */ RB_FOREACH (module, yang_modules, &yang_modules) { + int event_pipe; + frr_sr_subscribe_config(module); yang_snodes_iterate_module(module->info, frr_sr_subscribe_state, 0, module); yang_snodes_iterate_module(module->info, frr_sr_subscribe_rpc, 0, module); - yang_snodes_iterate_module(module->info, - frr_sr_subscribe_action, 0, module); + + /* Watch subscriptions. */ + ret = sr_get_event_pipe(module->sr_subscription, &event_pipe); + if (ret != SR_ERR_OK) { + flog_err(EC_LIB_SYSREPO_INIT, + "%s: sr_get_event_pipe(): %s", __func__, + sr_strerror(ret)); + goto cleanup; + } + thread_add_read(master, frr_sr_read_cb, module->sr_subscription, + event_pipe, &module->sr_thread); } hook_register(nb_notification_send, frr_sr_notification_send); - frr_sr_fd_add(SR_FD_INPUT_READY, sysrepo_fd); - return 0; cleanup: @@ -874,7 +717,8 @@ static int frr_sr_finish(void) RB_FOREACH (module, yang_modules, &yang_modules) { if (!module->sr_subscription) continue; - sr_unsubscribe(session, module->sr_subscription); + sr_unsubscribe(module->sr_subscription); + THREAD_OFF(module->sr_thread); } if (session) @@ -882,24 +726,26 @@ static int frr_sr_finish(void) if (connection) sr_disconnect(connection); - sysrepo_threads->del = (void (*)(void *))frr_sr_fd_free; - list_delete(&sysrepo_threads); - sr_fd_watcher_cleanup(); - return 0; } -static int frr_sr_module_late_init(struct thread_master *tm) +static int frr_sr_module_very_late_init(struct thread_master *tm) { master = tm; - if (frr_sr_init(frr_get_progname()) < 0) { + if (frr_sr_init() < 0) { flog_err(EC_LIB_SYSREPO_INIT, "failed to initialize the Sysrepo module"); return -1; } hook_register(frr_fini, frr_sr_finish); + + return 0; +} + +static int frr_sr_module_late_init(struct thread_master *tm) +{ frr_sr_cli_init(); return 0; @@ -908,6 +754,7 @@ static int frr_sr_module_late_init(struct thread_master *tm) static int frr_sr_module_init(void) { hook_register(frr_late_init, frr_sr_module_late_init); + hook_register(frr_very_late_init, frr_sr_module_very_late_init); return 0; } diff --git a/lib/ns.h b/lib/ns.h index f3ad837889..93a3781c15 100644 --- a/lib/ns.h +++ b/lib/ns.h @@ -41,7 +41,7 @@ typedef uint32_t ns_id_t; #ifdef HAVE_NETNS #define NS_DEFAULT_NAME "/proc/self/ns/net" #else /* !HAVE_NETNS */ -#define NS_DEFAULT_NAME "Default-logical-router" +#define NS_DEFAULT_NAME "default-netns" #endif /* HAVE_NETNS */ struct ns { @@ -53,6 +53,11 @@ struct ns { /* Identifier, mapped on the NSID value */ ns_id_t internal_ns_id; + /* Identifier, value of NSID of default netns, + * relative value in that local netns + */ + ns_id_t relative_default_ns; + /* Name */ char *name; @@ -71,8 +76,6 @@ struct ns { RB_HEAD(ns_head, ns); RB_PROTOTYPE(ns_head, ns, entry, ns_compare) -extern struct ns_head ns_tree; - /* * API for managing NETNS. eg from zebra daemon * one want to manage the list of NETNS, etc... @@ -82,10 +85,10 @@ extern struct ns_head ns_tree; * NS hooks */ -#define NS_NEW_HOOK 0 /* a new logical-router is just created */ -#define NS_DELETE_HOOK 1 /* a logical-router is to be deleted */ -#define NS_ENABLE_HOOK 2 /* a logical-router is ready to use */ -#define NS_DISABLE_HOOK 3 /* a logical-router is to be unusable */ +#define NS_NEW_HOOK 0 /* a new netns is just created */ +#define NS_DELETE_HOOK 1 /* a netns is to be deleted */ +#define NS_ENABLE_HOOK 2 /* a netns is ready to use */ +#define NS_DISABLE_HOOK 3 /* a netns is to be unusable */ /* * Add a specific hook ns module. @@ -122,13 +125,20 @@ int ns_socket(int domain, int type, int protocol, ns_id_t ns_id); extern char *ns_netns_pathname(struct vty *vty, const char *name); /* Parse and execute a function on all the NETNS */ -extern void ns_walk_func(int (*func)(struct ns *)); +#define NS_WALK_CONTINUE 0 +#define NS_WALK_STOP 1 + +extern void ns_walk_func(int (*func)(struct ns *, + void *, + void **), + void *param_in, + void **param_out); /* API to get the NETNS name, from the ns pointer */ extern const char *ns_get_name(struct ns *ns); /* only called from vrf ( when removing netns from vrf) - * or at VRF or logical router termination + * or at VRF termination */ extern void ns_delete(struct ns *ns); @@ -151,10 +161,7 @@ extern ns_id_t ns_map_nsid_with_external(ns_id_t ns_id, bool map); */ extern void ns_init(void); -/* API to retrieve default NS */ -extern ns_id_t ns_get_default_id(void); - -#define NS_DEFAULT ns_get_default_id() +#define NS_DEFAULT 0 /* API that can be used to change from NS */ extern int ns_switchback_to_initial(void); @@ -176,7 +183,9 @@ extern struct ns *ns_lookup_name(const char *name); */ extern int ns_enable(struct ns *ns, void (*func)(ns_id_t, void *)); extern struct ns *ns_get_created(struct ns *ns, char *name, ns_id_t ns_id); +extern ns_id_t ns_id_get_absolute(ns_id_t ns_id_reference, ns_id_t link_nsid); extern void ns_disable(struct ns *ns); +extern struct ns *ns_get_default(void); #ifdef __cplusplus } diff --git a/lib/ntop.c b/lib/ntop.c new file mode 100644 index 0000000000..ccbf8793d3 --- /dev/null +++ b/lib/ntop.c @@ -0,0 +1,174 @@ +/* + * optimized ntop, about 10x faster than libc versions [as of 2019] + * + * Copyright (c) 2019 David Lamparter, for NetDEF, Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "compiler.h" + +#define pos (*posx) + +static inline void putbyte(uint8_t bytex, char **posx) + __attribute__((always_inline)) OPTIMIZE; + +static inline void putbyte(uint8_t bytex, char **posx) +{ + bool zero = false; + int byte = bytex, tmp, a, b; + + if ((tmp = byte - 200) >= 0) { + *pos++ = '2'; + zero = true; + byte = tmp; + } else if ((tmp = byte - 100) >= 0) { + *pos++ = '1'; + zero = true; + byte = tmp; + } + + /* make sure the compiler knows the value range of "byte" */ + assume(byte < 100 && byte >= 0); + + b = byte % 10; + a = byte / 10; + if (a || zero) { + *pos++ = '0' + a; + *pos++ = '0' + b; + } else + *pos++ = '0' + b; +} + +static inline void puthex(uint16_t word, char **posx) + __attribute__((always_inline)) OPTIMIZE; + +static inline void puthex(uint16_t word, char **posx) +{ + const char *digits = "0123456789abcdef"; + if (word >= 0x1000) + *pos++ = digits[(word >> 12) & 0xf]; + if (word >= 0x100) + *pos++ = digits[(word >> 8) & 0xf]; + if (word >= 0x10) + *pos++ = digits[(word >> 4) & 0xf]; + *pos++ = digits[word & 0xf]; +} + +#undef pos + +const char *frr_inet_ntop(int af, const void * restrict src, + char * restrict dst, socklen_t size) + __attribute__((flatten)) OPTIMIZE; + +const char *frr_inet_ntop(int af, const void * restrict src, + char * restrict dst, socklen_t size) +{ + const uint8_t *b = src; + /* 8 * "abcd:" for IPv6 + * note: the IPv4-embedded IPv6 syntax is only used for ::A.B.C.D, + * which isn't longer than 40 chars either. even with ::ffff:A.B.C.D + * it's shorter. + */ + char buf[8 * 5], *o = buf; + size_t best = 0, bestlen = 0, curlen = 0, i; + + switch (af) { + case AF_INET: +inet4: + putbyte(b[0], &o); + *o++ = '.'; + putbyte(b[1], &o); + *o++ = '.'; + putbyte(b[2], &o); + *o++ = '.'; + putbyte(b[3], &o); + *o++ = '\0'; + break; + case AF_INET6: + for (i = 0; i < 8; i++) { + if (b[i * 2] || b[i * 2 + 1]) { + if (curlen && curlen > bestlen) { + best = i - curlen; + bestlen = curlen; + } + curlen = 0; + continue; + } + curlen++; + } + if (curlen && curlen > bestlen) { + best = i - curlen; + bestlen = curlen; + } + /* do we want ::ffff:A.B.C.D? */ + if (best == 0 && bestlen == 6) { + *o++ = ':'; + *o++ = ':'; + b += 12; + goto inet4; + } + if (bestlen == 1) + bestlen = 0; + + for (i = 0; i < 8; i++) { + if (bestlen && i == best) { + if (i == 0) + *o++ = ':'; + *o++ = ':'; + continue; + } + if (i > best && i < best + bestlen) { + continue; + } + puthex((b[i * 2] << 8) | b[i * 2 + 1], &o); + + if (i < 7) + *o++ = ':'; + } + *o++ = '\0'; + break; + default: + return NULL; + } + + i = o - buf; + if (i > size) + return NULL; + /* compiler might inline memcpy if it knows the length is short, + * although neither gcc nor clang actually do this currently [2019] + */ + assume(i <= 8 * 5); + memcpy(dst, buf, i); + return dst; +} + +#if !defined(INET_NTOP_NO_OVERRIDE) && !defined(__APPLE__) +/* we want to override libc inet_ntop, but make sure it shows up in backtraces + * as frr_inet_ntop (to avoid confusion while debugging) + */ +const char *inet_ntop(int af, const void *src, char *dst, socklen_t size) + __attribute__((alias ("frr_inet_ntop"))); +#endif diff --git a/lib/openbsd-tree.c b/lib/openbsd-tree.c index eadef9902b..98d2e155e3 100644 --- a/lib/openbsd-tree.c +++ b/lib/openbsd-tree.c @@ -431,11 +431,12 @@ void *_rb_insert(const struct rb_type *t, struct rbt_tree *rbt, void *elm) rbe_insert_color(t, rbt, rbe); - return (NULL); + return NULL; } /* Finds the node with the same key as elm */ -void *_rb_find(const struct rb_type *t, struct rbt_tree *rbt, const void *key) +void *_rb_find(const struct rb_type *t, const struct rbt_tree *rbt, + const void *key) { struct rb_entry *tmp = RBH_ROOT(rbt); void *node; @@ -452,11 +453,12 @@ void *_rb_find(const struct rb_type *t, struct rbt_tree *rbt, const void *key) return (node); } - return (NULL); + return NULL; } /* Finds the first node greater than or equal to the search key */ -void *_rb_nfind(const struct rb_type *t, struct rbt_tree *rbt, const void *key) +void *_rb_nfind(const struct rb_type *t, const struct rbt_tree *rbt, + const void *key) { struct rb_entry *tmp = RBH_ROOT(rbt); void *node; @@ -522,14 +524,14 @@ void *_rb_prev(const struct rb_type *t, void *elm) return (rbe == NULL ? NULL : rb_e2n(t, rbe)); } -void *_rb_root(const struct rb_type *t, struct rbt_tree *rbt) +void *_rb_root(const struct rb_type *t, const struct rbt_tree *rbt) { struct rb_entry *rbe = RBH_ROOT(rbt); return (rbe == NULL ? rbe : rb_e2n(t, rbe)); } -void *_rb_min(const struct rb_type *t, struct rbt_tree *rbt) +void *_rb_min(const struct rb_type *t, const struct rbt_tree *rbt) { struct rb_entry *rbe = RBH_ROOT(rbt); struct rb_entry *parent = NULL; @@ -542,7 +544,7 @@ void *_rb_min(const struct rb_type *t, struct rbt_tree *rbt) return (parent == NULL ? NULL : rb_e2n(t, parent)); } -void *_rb_max(const struct rb_type *t, struct rbt_tree *rbt) +void *_rb_max(const struct rb_type *t, const struct rbt_tree *rbt) { struct rb_entry *rbe = RBH_ROOT(rbt); struct rb_entry *parent = NULL; diff --git a/lib/openbsd-tree.h b/lib/openbsd-tree.h index d2f0781333..832a10141e 100644 --- a/lib/openbsd-tree.h +++ b/lib/openbsd-tree.h @@ -364,18 +364,18 @@ static inline void _rb_init(struct rbt_tree *rbt) rbt->rbt_root = NULL; } -static inline int _rb_empty(struct rbt_tree *rbt) +static inline int _rb_empty(const struct rbt_tree *rbt) { return (rbt->rbt_root == NULL); } void *_rb_insert(const struct rb_type *, struct rbt_tree *, void *); void *_rb_remove(const struct rb_type *, struct rbt_tree *, void *); -void *_rb_find(const struct rb_type *, struct rbt_tree *, const void *); -void *_rb_nfind(const struct rb_type *, struct rbt_tree *, const void *); -void *_rb_root(const struct rb_type *, struct rbt_tree *); -void *_rb_min(const struct rb_type *, struct rbt_tree *); -void *_rb_max(const struct rb_type *, struct rbt_tree *); +void *_rb_find(const struct rb_type *, const struct rbt_tree *, const void *); +void *_rb_nfind(const struct rb_type *, const struct rbt_tree *, const void *); +void *_rb_root(const struct rb_type *, const struct rbt_tree *); +void *_rb_min(const struct rb_type *, const struct rbt_tree *); +void *_rb_max(const struct rb_type *, const struct rbt_tree *); void *_rb_next(const struct rb_type *, void *); void *_rb_prev(const struct rb_type *, void *); void *_rb_left(const struct rb_type *, void *); @@ -401,56 +401,58 @@ int _rb_check(const struct rb_type *, void *, unsigned long); __attribute__((__unused__)) static inline struct _type \ *_name##_RB_INSERT(struct _name *head, struct _type *elm) \ { \ - return (struct _type *)_rb_insert( \ - _name##_RB_TYPE, &head->rbh_root, elm); \ + return (struct _type *)_rb_insert(_name##_RB_TYPE, \ + &head->rbh_root, elm); \ } \ \ __attribute__((__unused__)) static inline struct _type \ *_name##_RB_REMOVE(struct _name *head, struct _type *elm) \ { \ - return (struct _type *)_rb_remove( \ - _name##_RB_TYPE, &head->rbh_root, elm); \ + return (struct _type *)_rb_remove(_name##_RB_TYPE, \ + &head->rbh_root, elm); \ } \ \ __attribute__((__unused__)) static inline struct _type \ - *_name##_RB_FIND(struct _name *head, const struct _type *key) \ + *_name##_RB_FIND(const struct _name *head, \ + const struct _type *key) \ { \ - return (struct _type *)_rb_find( \ - _name##_RB_TYPE, &head->rbh_root, key); \ + return (struct _type *)_rb_find(_name##_RB_TYPE, \ + &head->rbh_root, key); \ } \ \ __attribute__((__unused__)) static inline struct _type \ - *_name##_RB_NFIND(struct _name *head, const struct _type *key) \ + *_name##_RB_NFIND(const struct _name *head, \ + const struct _type *key) \ { \ - return (struct _type *)_rb_nfind( \ - _name##_RB_TYPE, &head->rbh_root, key); \ + return (struct _type *)_rb_nfind(_name##_RB_TYPE, \ + &head->rbh_root, key); \ } \ \ __attribute__((__unused__)) static inline struct _type \ - *_name##_RB_ROOT(struct _name *head) \ + *_name##_RB_ROOT(const struct _name *head) \ { \ - return (struct _type *)_rb_root( \ - _name##_RB_TYPE, &head->rbh_root); \ + return (struct _type *)_rb_root(_name##_RB_TYPE, \ + &head->rbh_root); \ } \ \ __attribute__((__unused__)) static inline int _name##_RB_EMPTY( \ - struct _name *head) \ + const struct _name *head) \ { \ return _rb_empty(&head->rbh_root); \ } \ \ __attribute__((__unused__)) static inline struct _type \ - *_name##_RB_MIN(struct _name *head) \ + *_name##_RB_MIN(const struct _name *head) \ { \ - return (struct _type *)_rb_min( \ - _name##_RB_TYPE, &head->rbh_root); \ + return (struct _type *)_rb_min(_name##_RB_TYPE, \ + &head->rbh_root); \ } \ \ __attribute__((__unused__)) static inline struct _type \ - *_name##_RB_MAX(struct _name *head) \ + *_name##_RB_MAX(const struct _name *head) \ { \ - return (struct _type *)_rb_max( \ - _name##_RB_TYPE, &head->rbh_root); \ + return (struct _type *)_rb_max(_name##_RB_TYPE, \ + &head->rbh_root); \ } \ \ __attribute__((__unused__)) static inline struct _type \ diff --git a/lib/pbr.h b/lib/pbr.h index 1425e679c5..e365888662 100644 --- a/lib/pbr.h +++ b/lib/pbr.h @@ -49,6 +49,10 @@ struct pbr_filter { #define PBR_FILTER_PROTO (1 << 5) #define PBR_FILTER_SRC_PORT_RANGE (1 << 6) #define PBR_FILTER_DST_PORT_RANGE (1 << 7) +#define PBR_FILTER_DSFIELD (1 << 8) + +#define PBR_DSFIELD_DSCP (0xfc) /* Upper 6 bits of DS field: DSCP */ +#define PBR_DSFIELD_ECN (0x03) /* Lower 2 bits of DS field: BCN */ /* Source and Destination IP address with masks. */ struct prefix src_ip; @@ -58,6 +62,9 @@ struct pbr_filter { uint16_t src_port; uint16_t dst_port; + /* Filter by Differentiated Services field */ + uint8_t dsfield; /* DSCP (6 bits) & ECN (2 bits) */ + /* Filter with fwmark */ uint32_t fwmark; }; @@ -90,7 +97,8 @@ struct pbr_rule { uint32_t unique; struct pbr_filter filter; struct pbr_action action; - uint32_t ifindex; + + char ifname[INTERFACE_NAMSIZ + 1]; }; /* TCP flags value shared @@ -121,6 +129,9 @@ struct pbr_rule { #define MATCH_PKT_LEN_INVERSE_SET (1 << 8) #define MATCH_FRAGMENT_INVERSE_SET (1 << 9) #define MATCH_ICMP_SET (1 << 10) +#define MATCH_PROTOCOL_SET (1 << 11) +#define MATCH_FLOW_LABEL_SET (1 << 12) +#define MATCH_FLOW_LABEL_INVERSE_SET (1 << 13) extern int zapi_pbr_rule_encode(uint8_t cmd, struct stream *s, struct pbr_rule *zrule); diff --git a/lib/pid_output.c b/lib/pid_output.c index f5f7b1d171..bd1d89a94c 100644 --- a/lib/pid_output.c +++ b/lib/pid_output.c @@ -65,7 +65,7 @@ pid_t pid_output(const char *path) exit(1); } - sprintf(buf, "%d\n", (int)pid); + snprintf(buf, sizeof(buf), "%d\n", (int)pid); pidsize = strlen(buf); if ((tmp = write(fd, buf, pidsize)) != (int)pidsize) flog_err_sys( diff --git a/lib/plist.c b/lib/plist.c index 2a97e1e5b2..c746d19462 100644 --- a/lib/plist.c +++ b/lib/plist.c @@ -178,7 +178,7 @@ static void prefix_list_free(struct prefix_list *plist) XFREE(MTYPE_PREFIX_LIST, plist); } -static struct prefix_list_entry *prefix_list_entry_new(void) +struct prefix_list_entry *prefix_list_entry_new(void) { struct prefix_list_entry *new; @@ -187,7 +187,7 @@ static struct prefix_list_entry *prefix_list_entry_new(void) return new; } -static void prefix_list_entry_free(struct prefix_list_entry *pentry) +void prefix_list_entry_free(struct prefix_list_entry *pentry) { XFREE(MTYPE_PREFIX_LIST_ENTRY, pentry); } @@ -218,7 +218,7 @@ static struct prefix_list *prefix_list_insert(afi_t afi, int orf, /* If name is made by all digit character. We treat it as number. */ for (number = 0, i = 0; i < strlen(name); i++) { - if (isdigit((int)name[i])) + if (isdigit((unsigned char)name[i])) number = (number * 10) + (name[i] - '0'); else break; @@ -279,7 +279,7 @@ static struct prefix_list *prefix_list_insert(afi_t afi, int orf, return plist; } -static struct prefix_list *prefix_list_get(afi_t afi, int orf, const char *name) +struct prefix_list *prefix_list_get(afi_t afi, int orf, const char *name) { struct prefix_list *plist; @@ -294,7 +294,7 @@ static void prefix_list_trie_del(struct prefix_list *plist, struct prefix_list_entry *pentry); /* Delete prefix-list from prefix_list_master and free it. */ -static void prefix_list_delete(struct prefix_list *plist) +void prefix_list_delete(struct prefix_list *plist) { struct prefix_list_list *list; struct prefix_master *master; @@ -303,6 +303,8 @@ static void prefix_list_delete(struct prefix_list *plist) /* If prefix-list contain prefix_list_entry free all of it. */ for (pentry = plist->head; pentry; pentry = next) { + route_map_notify_pentry_dependencies(plist->name, pentry, + RMAP_EVENT_PLIST_DELETED); next = pentry->next; prefix_list_trie_del(plist, pentry); prefix_list_entry_free(pentry); @@ -346,14 +348,14 @@ static void prefix_list_delete(struct prefix_list *plist) static struct prefix_list_entry * prefix_list_entry_make(struct prefix *prefix, enum prefix_list_type type, - int64_t seq, int le, int ge, int any) + int64_t seq, int le, int ge, bool any) { struct prefix_list_entry *pentry; pentry = prefix_list_entry_new(); if (any) - pentry->any = 1; + pentry->any = true; prefix_copy(&pentry->prefix, prefix); pentry->type = type; @@ -379,13 +381,13 @@ void prefix_list_delete_hook(void (*func)(struct prefix_list *plist)) } /* Calculate new sequential number. */ -static int64_t prefix_new_seq_get(struct prefix_list *plist) +int64_t prefix_new_seq_get(struct prefix_list *plist) { int64_t maxseq; int64_t newseq; struct prefix_list_entry *pentry; - maxseq = newseq = 0; + maxseq = 0; for (pentry = plist->head; pentry; pentry = pentry->next) { if (maxseq < pentry->seq) @@ -409,7 +411,7 @@ static struct prefix_list_entry *prefix_seq_check(struct prefix_list *plist, return NULL; } -static struct prefix_list_entry * +struct prefix_list_entry * prefix_list_entry_lookup(struct prefix_list *plist, struct prefix *prefix, enum prefix_list_type type, int64_t seq, int le, int ge) @@ -496,14 +498,13 @@ static void prefix_list_trie_del(struct prefix_list *plist, for (; depth > 0; depth--) if (trie_table_empty(*tables[depth])) { XFREE(MTYPE_PREFIX_LIST_TRIE, *tables[depth]); - *tables[depth] = NULL; } } -static void prefix_list_entry_delete(struct prefix_list *plist, - struct prefix_list_entry *pentry, - int update_list) +void prefix_list_entry_delete(struct prefix_list *plist, + struct prefix_list_entry *pentry, + int update_list) { if (plist == NULL || pentry == NULL) return; @@ -519,6 +520,8 @@ static void prefix_list_entry_delete(struct prefix_list *plist, else plist->tail = pentry->prev; + route_map_notify_pentry_dependencies(plist->name, pentry, + RMAP_EVENT_PLIST_DELETED); prefix_list_entry_free(pentry); plist->count--; @@ -632,6 +635,9 @@ static void prefix_list_entry_add(struct prefix_list *plist, /* Increment count. */ plist->count++; + route_map_notify_pentry_dependencies(plist->name, pentry, + RMAP_EVENT_PLIST_ADDED); + /* Run hook function. */ if (plist->master->add_hook) (*plist->master->add_hook)(plist); @@ -640,6 +646,134 @@ static void prefix_list_entry_add(struct prefix_list *plist, plist->master->recent = plist; } +/** + * Prefix list entry update start procedure: + * Remove entry from previosly installed master list, tries and notify + * observers. + * + * \param[in] ple prefix list entry. + */ +void prefix_list_entry_update_start(struct prefix_list_entry *ple) +{ + struct prefix_list *pl = ple->pl; + + /* Not installed, nothing to do. */ + if (!ple->installed) + return; + + prefix_list_trie_del(pl, ple); + + /* List manipulation: shameless copy from `prefix_list_entry_delete`. */ + if (ple->prev) + ple->prev->next = ple->next; + else + pl->head = ple->next; + if (ple->next) + ple->next->prev = ple->prev; + else + pl->tail = ple->prev; + + route_map_notify_pentry_dependencies(pl->name, ple, + RMAP_EVENT_PLIST_DELETED); + pl->count--; + + route_map_notify_dependencies(pl->name, RMAP_EVENT_PLIST_DELETED); + if (pl->master->delete_hook) + (*pl->master->delete_hook)(pl); + + if (pl->head || pl->tail || pl->desc) + pl->master->recent = pl; + + ple->next_best = NULL; + ple->installed = false; +} + +/** + * Prefix list entry update finish procedure: + * Add entry back master list, to the trie, notify observers and call master + * hook. + * + * \param[in] ple prefix list entry. + */ +void prefix_list_entry_update_finish(struct prefix_list_entry *ple) +{ + struct prefix_list *pl = ple->pl; + struct prefix_list_entry *point; + + /* Already installed, nothing to do. */ + if (ple->installed) + return; + + /* + * Check if the entry is installable: + * We can only install entry if at least the prefix is provided (IPv4 + * or IPv6). + */ + if (ple->prefix.family != AF_INET && ple->prefix.family != AF_INET6) + return; + + /* List manipulation: shameless copy from `prefix_list_entry_add`. */ + if (pl->tail && ple->seq > pl->tail->seq) + point = NULL; + else { + /* Check insert point. */ + for (point = pl->head; point; point = point->next) + if (point->seq >= ple->seq) + break; + } + + /* In case of this is the first element of the list. */ + ple->next = point; + + if (point) { + if (point->prev) + point->prev->next = ple; + else + pl->head = ple; + + ple->prev = point->prev; + point->prev = ple; + } else { + if (pl->tail) + pl->tail->next = ple; + else + pl->head = ple; + + ple->prev = pl->tail; + pl->tail = ple; + } + + prefix_list_trie_add(pl, ple); + pl->count++; + + route_map_notify_pentry_dependencies(pl->name, ple, + RMAP_EVENT_PLIST_ADDED); + + /* Run hook function. */ + if (pl->master->add_hook) + (*pl->master->add_hook)(pl); + + route_map_notify_dependencies(pl->name, RMAP_EVENT_PLIST_ADDED); + pl->master->recent = pl; + + ple->installed = true; +} + +/** + * Same as `prefix_list_entry_delete` but without `free()`ing the list if its + * empty. + * + * \param[in] ple prefix list entry. + */ +void prefix_list_entry_delete2(struct prefix_list_entry *ple) +{ + /* Does the boiler plate list removal and entry removal notification. */ + prefix_list_entry_update_start(ple); + + /* Effective `free()` memory. */ + prefix_list_entry_free(ple); +} + /* Return string of prefix_list_type. */ static const char *prefix_list_type_str(struct prefix_list_entry *pentry) { @@ -750,6 +884,7 @@ enum prefix_list_type prefix_list_apply_which_prefix( if (pbest == NULL) return PREFIX_DENY; + pbest->hitcnt++; return pbest->type; } @@ -771,7 +906,7 @@ static void __attribute__((unused)) prefix_list_print(struct prefix_list *plist) p = &pentry->prefix; - printf(" seq %" PRId64 " %s %s/%d", pentry->seq, + printf(" seq %lld %s %s/%d", (long long)pentry->seq, prefix_list_type_str(pentry), inet_ntop(p->family, p->u.val, buf, BUFSIZ), p->prefixlen); @@ -825,270 +960,6 @@ prefix_entry_dup_check(struct prefix_list *plist, struct prefix_list_entry *new) return NULL; } -static int vty_invalid_prefix_range(struct vty *vty, const char *prefix) -{ - vty_out(vty, - "%% Invalid prefix range for %s, make sure: len < ge-value <= le-value\n", - prefix); - return CMD_WARNING_CONFIG_FAILED; -} - -static int vty_prefix_list_install(struct vty *vty, afi_t afi, const char *name, - const char *seq, const char *typestr, - const char *prefix, const char *ge, - const char *le) -{ - int ret; - enum prefix_list_type type; - struct prefix_list *plist; - struct prefix_list_entry *pentry; - struct prefix_list_entry *dup; - struct prefix p, p_tmp; - int any = 0; - int64_t seqnum = -1; - int lenum = 0; - int genum = 0; - - if (name == NULL || prefix == NULL || typestr == NULL) { - vty_out(vty, "%% Missing prefix or type\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - /* Sequential number. */ - if (seq) - seqnum = (int64_t)atol(seq); - - /* ge and le number */ - if (ge) - genum = atoi(ge); - if (le) - lenum = atoi(le); - - /* Check filter type. */ - if (strncmp("permit", typestr, 1) == 0) - type = PREFIX_PERMIT; - else if (strncmp("deny", typestr, 1) == 0) - type = PREFIX_DENY; - else { - vty_out(vty, "%% prefix type must be permit or deny\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - /* "any" is special token for matching any IPv4 addresses. */ - switch (afi) { - case AFI_IP: - if (strncmp("any", prefix, strlen(prefix)) == 0) { - ret = str2prefix_ipv4("0.0.0.0/0", - (struct prefix_ipv4 *)&p); - genum = 0; - lenum = IPV4_MAX_BITLEN; - any = 1; - } else - ret = str2prefix_ipv4(prefix, (struct prefix_ipv4 *)&p); - - if (ret <= 0) { - vty_out(vty, "%% Malformed IPv4 prefix\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - /* make a copy to verify prefix matches mask length */ - prefix_copy(&p_tmp, &p); - apply_mask_ipv4((struct prefix_ipv4 *)&p_tmp); - - break; - case AFI_IP6: - if (strncmp("any", prefix, strlen(prefix)) == 0) { - ret = str2prefix_ipv6("::/0", (struct prefix_ipv6 *)&p); - genum = 0; - lenum = IPV6_MAX_BITLEN; - any = 1; - } else - ret = str2prefix_ipv6(prefix, (struct prefix_ipv6 *)&p); - - if (ret <= 0) { - vty_out(vty, "%% Malformed IPv6 prefix\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - /* make a copy to verify prefix matches mask length */ - prefix_copy(&p_tmp, &p); - apply_mask_ipv6((struct prefix_ipv6 *)&p_tmp); - - break; - case AFI_L2VPN: - default: - vty_out(vty, "%% Unrecognized AFI (%d)\n", afi); - return CMD_WARNING_CONFIG_FAILED; - break; - } - - /* If prefix has bits not under the mask, adjust it to fit */ - if (!prefix_same(&p_tmp, &p)) { - char buf[PREFIX2STR_BUFFER]; - char buf_tmp[PREFIX2STR_BUFFER]; - prefix2str(&p, buf, sizeof(buf)); - prefix2str(&p_tmp, buf_tmp, sizeof(buf_tmp)); - vty_out(vty, - "%% Prefix-list %s prefix changed from %s to %s to match length\n", - name, buf, buf_tmp); - zlog_info( - "Prefix-list %s prefix changed from %s to %s to match length", - name, buf, buf_tmp); - p = p_tmp; - } - - /* ge and le check. */ - if (genum && (genum <= p.prefixlen)) - return vty_invalid_prefix_range(vty, prefix); - - if (lenum && (lenum < p.prefixlen)) - return vty_invalid_prefix_range(vty, prefix); - - if (lenum && (genum > lenum)) - return vty_invalid_prefix_range(vty, prefix); - - if (genum && (lenum == (afi == AFI_IP ? 32 : 128))) - lenum = 0; - - /* Get prefix_list with name. */ - plist = prefix_list_get(afi, 0, name); - - /* Make prefix entry. */ - pentry = prefix_list_entry_make(&p, type, seqnum, lenum, genum, any); - - /* Check same policy. */ - dup = prefix_entry_dup_check(plist, pentry); - - if (dup) { - prefix_list_entry_free(pentry); - return CMD_SUCCESS; - } - - /* Install new filter to the access_list. */ - prefix_list_entry_add(plist, pentry); - - return CMD_SUCCESS; -} - -static int vty_prefix_list_uninstall(struct vty *vty, afi_t afi, - const char *name, const char *seq, - const char *typestr, const char *prefix, - const char *ge, const char *le) -{ - int ret; - enum prefix_list_type type; - struct prefix_list *plist; - struct prefix_list_entry *pentry; - struct prefix p; - int64_t seqnum = -1; - int lenum = 0; - int genum = 0; - - /* Check prefix list name. */ - plist = prefix_list_lookup(afi, name); - if (!plist) { - vty_out(vty, "%% Can't find specified prefix-list\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - /* Only prefix-list name specified, delete the entire prefix-list. */ - if (seq == NULL && typestr == NULL && prefix == NULL && ge == NULL - && le == NULL) { - prefix_list_delete(plist); - return CMD_SUCCESS; - } - - /* We must have, at a minimum, both the type and prefix here */ - if ((typestr == NULL) || (prefix == NULL)) { - vty_out(vty, "%% Both prefix and type required\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - /* Check sequence number. */ - if (seq) - seqnum = (int64_t)atol(seq); - - /* ge and le number */ - if (ge) - genum = atoi(ge); - if (le) - lenum = atoi(le); - - /* Check of filter type. */ - if (strncmp("permit", typestr, 1) == 0) - type = PREFIX_PERMIT; - else if (strncmp("deny", typestr, 1) == 0) - type = PREFIX_DENY; - else { - vty_out(vty, "%% prefix type must be permit or deny\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - /* "any" is special token for matching any IPv4 addresses. */ - if (afi == AFI_IP) { - if (strncmp("any", prefix, strlen(prefix)) == 0) { - ret = str2prefix_ipv4("0.0.0.0/0", - (struct prefix_ipv4 *)&p); - genum = 0; - lenum = IPV4_MAX_BITLEN; - } else - ret = str2prefix_ipv4(prefix, (struct prefix_ipv4 *)&p); - - if (ret <= 0) { - vty_out(vty, "%% Malformed IPv4 prefix\n"); - return CMD_WARNING_CONFIG_FAILED; - } - } else if (afi == AFI_IP6) { - if (strncmp("any", prefix, strlen(prefix)) == 0) { - ret = str2prefix_ipv6("::/0", (struct prefix_ipv6 *)&p); - genum = 0; - lenum = IPV6_MAX_BITLEN; - } else - ret = str2prefix_ipv6(prefix, (struct prefix_ipv6 *)&p); - - if (ret <= 0) { - vty_out(vty, "%% Malformed IPv6 prefix\n"); - return CMD_WARNING_CONFIG_FAILED; - } - } - - /* Lookup prefix entry. */ - pentry = - prefix_list_entry_lookup(plist, &p, type, seqnum, lenum, genum); - - if (pentry == NULL) { - vty_out(vty, "%% Can't find specified prefix-list\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - /* Install new filter to the access_list. */ - prefix_list_entry_delete(plist, pentry, 1); - - return CMD_SUCCESS; -} - -static int vty_prefix_list_desc_unset(struct vty *vty, afi_t afi, - const char *name) -{ - struct prefix_list *plist; - - plist = prefix_list_lookup(afi, name); - if (!plist) { - vty_out(vty, "%% Can't find specified prefix-list\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - if (plist->desc) { - XFREE(MTYPE_TMP, plist->desc); - plist->desc = NULL; - } - - if (plist->head == NULL && plist->tail == NULL && plist->desc == NULL) - prefix_list_delete(plist); - - return CMD_SUCCESS; -} - enum display_type { normal_display, summary_display, @@ -1332,61 +1203,6 @@ static int vty_clear_prefix_list(struct vty *vty, afi_t afi, const char *name, #include "lib/plist_clippy.c" #endif -DEFPY (ip_prefix_list, - ip_prefix_list_cmd, - "ip prefix-list WORD [seq (1-4294967295)] $action ", - IP_STR - PREFIX_LIST_STR - "Name of a prefix list\n" - "sequence number of an entry\n" - "Sequence number\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Any prefix match. Same as \"0.0.0.0/0 le 32\"\n" - "IP prefix /, e.g., 35.0.0.0/8\n" - "Minimum prefix length to be matched\n" - "Minimum prefix length\n" - "Maximum prefix length to be matched\n" - "Maximum prefix length\n") -{ - return vty_prefix_list_install(vty, AFI_IP, prefix_list, seq_str, - action, dest, ge_str, le_str); -} - -DEFPY (no_ip_prefix_list, - no_ip_prefix_list_cmd, - "no ip prefix-list WORD [seq (1-4294967295)] $action ", - NO_STR - IP_STR - PREFIX_LIST_STR - "Name of a prefix list\n" - "sequence number of an entry\n" - "Sequence number\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Any prefix match. Same as \"0.0.0.0/0 le 32\"\n" - "IP prefix /, e.g., 35.0.0.0/8\n" - "Minimum prefix length to be matched\n" - "Minimum prefix length\n" - "Maximum prefix length to be matched\n" - "Maximum prefix length\n") -{ - return vty_prefix_list_uninstall(vty, AFI_IP, prefix_list, seq_str, - action, dest, ge_str, le_str); -} - -DEFPY (no_ip_prefix_list_all, - no_ip_prefix_list_all_cmd, - "no ip prefix-list WORD", - NO_STR - IP_STR - PREFIX_LIST_STR - "Name of a prefix list\n") -{ - return vty_prefix_list_uninstall(vty, AFI_IP, prefix_list, NULL, NULL, - NULL, NULL, NULL); -} - DEFPY (ip_prefix_list_sequence_number, ip_prefix_list_sequence_number_cmd, "[no] ip prefix-list sequence-number", @@ -1399,56 +1215,6 @@ DEFPY (ip_prefix_list_sequence_number, return CMD_SUCCESS; } -DEFUN (ip_prefix_list_description, - ip_prefix_list_description_cmd, - "ip prefix-list WORD description LINE...", - IP_STR - PREFIX_LIST_STR - "Name of a prefix list\n" - "Prefix-list specific description\n" - "Up to 80 characters describing this prefix-list\n") -{ - int idx_word = 2; - int idx_line = 4; - struct prefix_list *plist; - - plist = prefix_list_get(AFI_IP, 0, argv[idx_word]->arg); - - if (plist->desc) { - XFREE(MTYPE_TMP, plist->desc); - plist->desc = NULL; - } - plist->desc = argv_concat(argv, argc, idx_line); - - return CMD_SUCCESS; -} - -DEFUN (no_ip_prefix_list_description, - no_ip_prefix_list_description_cmd, - "no ip prefix-list WORD description", - NO_STR - IP_STR - PREFIX_LIST_STR - "Name of a prefix list\n" - "Prefix-list specific description\n") -{ - int idx_word = 3; - return vty_prefix_list_desc_unset(vty, AFI_IP, argv[idx_word]->arg); -} - -/* ALIAS_FIXME */ -DEFUN (no_ip_prefix_list_description_comment, - no_ip_prefix_list_description_comment_cmd, - "no ip prefix-list WORD description LINE...", - NO_STR - IP_STR - PREFIX_LIST_STR - "Name of a prefix list\n" - "Prefix-list specific description\n" - "Up to 80 characters describing this prefix-list\n") -{ - return no_ip_prefix_list_description(self, vty, argc, argv); -} DEFPY (show_ip_prefix_list, show_ip_prefix_list_cmd, @@ -1526,61 +1292,6 @@ DEFPY (clear_ip_prefix_list, return vty_clear_prefix_list(vty, AFI_IP, prefix_list, prefix_str); } -DEFPY (ipv6_prefix_list, - ipv6_prefix_list_cmd, - "ipv6 prefix-list WORD [seq (1-4294967295)] $action ", - IPV6_STR - PREFIX_LIST_STR - "Name of a prefix list\n" - "sequence number of an entry\n" - "Sequence number\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Any prefix match. Same as \"::0/0 le 128\"\n" - "IPv6 prefix /, e.g., 3ffe::/16\n" - "Maximum prefix length to be matched\n" - "Maximum prefix length\n" - "Minimum prefix length to be matched\n" - "Minimum prefix length\n") -{ - return vty_prefix_list_install(vty, AFI_IP6, prefix_list, seq_str, - action, dest, ge_str, le_str); -} - -DEFPY (no_ipv6_prefix_list, - no_ipv6_prefix_list_cmd, - "no ipv6 prefix-list WORD [seq (1-4294967295)] $action ", - NO_STR - IPV6_STR - PREFIX_LIST_STR - "Name of a prefix list\n" - "sequence number of an entry\n" - "Sequence number\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Any prefix match. Same as \"::0/0 le 128\"\n" - "IPv6 prefix /, e.g., 3ffe::/16\n" - "Maximum prefix length to be matched\n" - "Maximum prefix length\n" - "Minimum prefix length to be matched\n" - "Minimum prefix length\n") -{ - return vty_prefix_list_uninstall(vty, AFI_IP6, prefix_list, seq_str, - action, dest, ge_str, le_str); -} - -DEFPY (no_ipv6_prefix_list_all, - no_ipv6_prefix_list_all_cmd, - "no ipv6 prefix-list WORD", - NO_STR - IPV6_STR - PREFIX_LIST_STR - "Name of a prefix list\n") -{ - return vty_prefix_list_uninstall(vty, AFI_IP6, prefix_list, NULL, NULL, - NULL, NULL, NULL); -} - DEFPY (ipv6_prefix_list_sequence_number, ipv6_prefix_list_sequence_number_cmd, "[no] ipv6 prefix-list sequence-number", @@ -1593,58 +1304,6 @@ DEFPY (ipv6_prefix_list_sequence_number, return CMD_SUCCESS; } -DEFUN (ipv6_prefix_list_description, - ipv6_prefix_list_description_cmd, - "ipv6 prefix-list WORD description LINE...", - IPV6_STR - PREFIX_LIST_STR - "Name of a prefix list\n" - "Prefix-list specific description\n" - "Up to 80 characters describing this prefix-list\n") -{ - int idx_word = 2; - int iddx_line = 4; - struct prefix_list *plist; - - plist = prefix_list_get(AFI_IP6, 0, argv[idx_word]->arg); - - if (plist->desc) { - XFREE(MTYPE_TMP, plist->desc); - plist->desc = NULL; - } - plist->desc = argv_concat(argv, argc, iddx_line); - - return CMD_SUCCESS; -} - -DEFUN (no_ipv6_prefix_list_description, - no_ipv6_prefix_list_description_cmd, - "no ipv6 prefix-list WORD description", - NO_STR - IPV6_STR - PREFIX_LIST_STR - "Name of a prefix list\n" - "Prefix-list specific description\n") -{ - int idx_word = 3; - return vty_prefix_list_desc_unset(vty, AFI_IP6, argv[idx_word]->arg); -} - -/* ALIAS_FIXME */ -DEFUN (no_ipv6_prefix_list_description_comment, - no_ipv6_prefix_list_description_comment_cmd, - "no ipv6 prefix-list WORD description LINE...", - NO_STR - IPV6_STR - PREFIX_LIST_STR - "Name of a prefix list\n" - "Prefix-list specific description\n" - "Up to 80 characters describing this prefix-list\n") -{ - return no_ipv6_prefix_list_description(self, vty, argc, argv); -} - - DEFPY (show_ipv6_prefix_list, show_ipv6_prefix_list_cmd, "show ipv6 prefix-list [WORD [seq$dseq (1-4294967295)$arg]]", @@ -1721,104 +1380,6 @@ DEFPY (clear_ipv6_prefix_list, return vty_clear_prefix_list(vty, AFI_IP6, prefix_list, prefix_str); } -/* Configuration write function. */ -static int config_write_prefix_afi(afi_t afi, struct vty *vty) -{ - struct prefix_list *plist; - struct prefix_list_entry *pentry; - struct prefix_master *master; - int write = 0; - - master = prefix_master_get(afi, 0); - if (master == NULL) - return 0; - - if (!master->seqnum) { - vty_out(vty, "no ip%s prefix-list sequence-number\n", - afi == AFI_IP ? "" : "v6"); - vty_out(vty, "!\n"); - } - - for (plist = master->num.head; plist; plist = plist->next) { - if (plist->desc) { - vty_out(vty, "ip%s prefix-list %s description %s\n", - afi == AFI_IP ? "" : "v6", plist->name, - plist->desc); - write++; - } - - for (pentry = plist->head; pentry; pentry = pentry->next) { - vty_out(vty, "ip%s prefix-list %s ", - afi == AFI_IP ? "" : "v6", plist->name); - - if (master->seqnum) - vty_out(vty, "seq %" PRId64 " ", pentry->seq); - - vty_out(vty, "%s ", prefix_list_type_str(pentry)); - - if (pentry->any) - vty_out(vty, "any"); - else { - struct prefix *p = &pentry->prefix; - char buf[BUFSIZ]; - - vty_out(vty, "%s/%d", - inet_ntop(p->family, p->u.val, buf, - BUFSIZ), - p->prefixlen); - - if (pentry->ge) - vty_out(vty, " ge %d", pentry->ge); - if (pentry->le) - vty_out(vty, " le %d", pentry->le); - } - vty_out(vty, "\n"); - write++; - } - /* vty_out (vty, "!\n"); */ - } - - for (plist = master->str.head; plist; plist = plist->next) { - if (plist->desc) { - vty_out(vty, "ip%s prefix-list %s description %s\n", - afi == AFI_IP ? "" : "v6", plist->name, - plist->desc); - write++; - } - - for (pentry = plist->head; pentry; pentry = pentry->next) { - vty_out(vty, "ip%s prefix-list %s ", - afi == AFI_IP ? "" : "v6", plist->name); - - if (master->seqnum) - vty_out(vty, "seq %" PRId64 " ", pentry->seq); - - vty_out(vty, "%s", prefix_list_type_str(pentry)); - - if (pentry->any) - vty_out(vty, " any"); - else { - struct prefix *p = &pentry->prefix; - char buf[BUFSIZ]; - - vty_out(vty, " %s/%d", - inet_ntop(p->family, p->u.val, buf, - BUFSIZ), - p->prefixlen); - - if (pentry->ge) - vty_out(vty, " ge %d", pentry->ge); - if (pentry->le) - vty_out(vty, " le %d", pentry->le); - } - vty_out(vty, "\n"); - write++; - } - } - - return write; -} - struct stream *prefix_bgp_orf_entry(struct stream *s, struct prefix_list *plist, uint8_t init_flag, uint8_t permit_flag, uint8_t deny_flag) @@ -1865,10 +1426,12 @@ int prefix_bgp_orf_set(char *name, afi_t afi, struct orf_prefix *orfp, if (!plist) return CMD_WARNING_CONFIG_FAILED; + apply_mask(&orfp->p); + if (set) { pentry = prefix_list_entry_make( &orfp->p, (permit ? PREFIX_PERMIT : PREFIX_DENY), - orfp->seq, orfp->le, orfp->ge, 0); + orfp->seq, orfp->le, orfp->ge, false); if (prefix_entry_dup_check(plist, pentry)) { prefix_list_entry_free(pentry); @@ -1931,10 +1494,9 @@ int prefix_bgp_show_prefix_list(struct vty *vty, afi_t afi, char *name, char buf_a[BUFSIZ]; char buf_b[BUFSIZ]; - sprintf(buf_a, "%s/%d", - inet_ntop(p->family, p->u.val, buf_b, - BUFSIZ), - p->prefixlen); + snprintf(buf_a, sizeof(buf_a), "%s/%d", + inet_ntop(p->family, p->u.val, buf_b, BUFSIZ), + p->prefixlen); json_object_int_add(json_list, "seq", pentry->seq); json_object_string_add(json_list, "seqPrefixListType", @@ -2009,20 +1571,16 @@ static void prefix_list_reset_afi(afi_t afi, int orf) assert(master->str.head == NULL); assert(master->str.tail == NULL); - master->seqnum = 1; + master->seqnum = true; master->recent = NULL; } - /* Prefix-list node. */ -static struct cmd_node prefix_node = {PREFIX_NODE, - "", /* Prefix list has no interface. */ - 1}; - -static int config_write_prefix_ipv4(struct vty *vty) -{ - return config_write_prefix_afi(AFI_IP, vty); -} +static struct cmd_node prefix_node = { + .name = "ipv4 prefix list", + .node = PREFIX_NODE, + .prompt = "", +}; static void plist_autocomplete_afi(afi_t afi, vector comps, struct cmd_token *token) @@ -2055,16 +1613,7 @@ static const struct cmd_variable_handler plist_var_handlers[] = { static void prefix_list_init_ipv4(void) { - install_node(&prefix_node, config_write_prefix_ipv4); - - install_element(CONFIG_NODE, &ip_prefix_list_cmd); - install_element(CONFIG_NODE, &no_ip_prefix_list_cmd); - install_element(CONFIG_NODE, &no_ip_prefix_list_all_cmd); - - install_element(CONFIG_NODE, &ip_prefix_list_description_cmd); - install_element(CONFIG_NODE, &no_ip_prefix_list_description_cmd); - install_element(CONFIG_NODE, - &no_ip_prefix_list_description_comment_cmd); + install_node(&prefix_node); install_element(CONFIG_NODE, &ip_prefix_list_sequence_number_cmd); @@ -2078,26 +1627,14 @@ static void prefix_list_init_ipv4(void) /* Prefix-list node. */ static struct cmd_node prefix_ipv6_node = { - PREFIX_IPV6_NODE, "", /* Prefix list has no interface. */ - 1}; - -static int config_write_prefix_ipv6(struct vty *vty) -{ - return config_write_prefix_afi(AFI_IP6, vty); -} + .name = "ipv6 prefix list", + .node = PREFIX_IPV6_NODE, + .prompt = "", +}; static void prefix_list_init_ipv6(void) { - install_node(&prefix_ipv6_node, config_write_prefix_ipv6); - - install_element(CONFIG_NODE, &ipv6_prefix_list_cmd); - install_element(CONFIG_NODE, &no_ipv6_prefix_list_cmd); - install_element(CONFIG_NODE, &no_ipv6_prefix_list_all_cmd); - - install_element(CONFIG_NODE, &ipv6_prefix_list_description_cmd); - install_element(CONFIG_NODE, &no_ipv6_prefix_list_description_cmd); - install_element(CONFIG_NODE, - &no_ipv6_prefix_list_description_comment_cmd); + install_node(&prefix_ipv6_node); install_element(CONFIG_NODE, &ipv6_prefix_list_sequence_number_cmd); diff --git a/lib/plist.h b/lib/plist.h index ba2846d74a..57eb763a68 100644 --- a/lib/plist.h +++ b/lib/plist.h @@ -79,6 +79,20 @@ extern void prefix_bgp_orf_remove_all(afi_t, char *); extern int prefix_bgp_show_prefix_list(struct vty *vty, afi_t afi, char *name, bool use_json); +extern struct prefix_list *prefix_list_get(afi_t afi, int orf, + const char *name); +extern void prefix_list_delete(struct prefix_list *plist); +extern int64_t prefix_new_seq_get(struct prefix_list *plist); + +extern struct prefix_list_entry *prefix_list_entry_new(void); +extern void prefix_list_entry_delete(struct prefix_list *plist, + struct prefix_list_entry *pentry, + int update_list); +extern struct prefix_list_entry * +prefix_list_entry_lookup(struct prefix_list *plist, struct prefix *prefix, + enum prefix_list_type type, int64_t seq, int le, + int ge); + #ifdef __cplusplus } #endif diff --git a/lib/plist_int.h b/lib/plist_int.h index 443b0c614d..5e0beabbc6 100644 --- a/lib/plist_int.h +++ b/lib/plist_int.h @@ -59,19 +59,29 @@ struct prefix_list_entry { enum prefix_list_type type; - int any; + bool any; struct prefix prefix; unsigned long refcnt; unsigned long hitcnt; + struct prefix_list *pl; + struct prefix_list_entry *next; struct prefix_list_entry *prev; /* up the chain for best match search */ struct prefix_list_entry *next_best; + + /* Flag to track trie/list installation status. */ + bool installed; }; +extern void prefix_list_entry_free(struct prefix_list_entry *pentry); +extern void prefix_list_entry_delete2(struct prefix_list_entry *ple); +extern void prefix_list_entry_update_start(struct prefix_list_entry *ple); +extern void prefix_list_entry_update_finish(struct prefix_list_entry *ple); + #ifdef __cplusplus } #endif diff --git a/lib/pqueue.c b/lib/pqueue.c deleted file mode 100644 index 87b54a681a..0000000000 --- a/lib/pqueue.c +++ /dev/null @@ -1,185 +0,0 @@ -/* Priority queue functions. - * Copyright (C) 2003 Yasuhiro Ohara - * - * This file is part of GNU Zebra. - * - * GNU Zebra is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * GNU Zebra is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; see the file COPYING; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include - -#include "memory.h" -#include "pqueue.h" - -DEFINE_MTYPE_STATIC(LIB, PQUEUE, "Priority queue") -DEFINE_MTYPE_STATIC(LIB, PQUEUE_DATA, "Priority queue data") - -/* priority queue using heap sort */ - -/* pqueue->cmp() controls the order of sorting (i.e, ascending or - descending). If you want the left node to move upper of the heap - binary tree, make cmp() to return less than 0. for example, if cmp - (10, 20) returns -1, the sorting is ascending order. if cmp (10, - 20) returns 1, the sorting is descending order. if cmp (10, 20) - returns 0, this library does not do sorting (which will not be what - you want). To be brief, if the contents of cmp_func (left, right) - is left - right, dequeue () returns the smallest node. Otherwise - (if the contents is right - left), dequeue () returns the largest - node. */ - -#define DATA_SIZE (sizeof (void *)) -#define PARENT_OF(x) ((x - 1) / 2) -#define LEFT_OF(x) (2 * x + 1) -#define RIGHT_OF(x) (2 * x + 2) -#define HAVE_CHILD(x,q) (x < (q)->size / 2) - -void trickle_up(int index, struct pqueue *queue) -{ - void *tmp; - - /* Save current node as tmp node. */ - tmp = queue->array[index]; - - /* Continue until the node reaches top or the place where the parent - node should be upper than the tmp node. */ - while (index > 0 - && (*queue->cmp)(tmp, queue->array[PARENT_OF(index)]) < 0) { - /* actually trickle up */ - queue->array[index] = queue->array[PARENT_OF(index)]; - if (queue->update != NULL) - (*queue->update)(queue->array[index], index); - index = PARENT_OF(index); - } - - /* Restore the tmp node to appropriate place. */ - queue->array[index] = tmp; - if (queue->update != NULL) - (*queue->update)(tmp, index); -} - -void trickle_down(int index, struct pqueue *queue) -{ - void *tmp; - int which; - - /* Save current node as tmp node. */ - tmp = queue->array[index]; - - /* Continue until the node have at least one (left) child. */ - while (HAVE_CHILD(index, queue)) { - /* If right child exists, and if the right child is more proper - to be moved upper. */ - if (RIGHT_OF(index) < queue->size - && (*queue->cmp)(queue->array[LEFT_OF(index)], - queue->array[RIGHT_OF(index)]) - > 0) - which = RIGHT_OF(index); - else - which = LEFT_OF(index); - - /* If the tmp node should be upper than the child, break. */ - if ((*queue->cmp)(queue->array[which], tmp) > 0) - break; - - /* Actually trickle down the tmp node. */ - queue->array[index] = queue->array[which]; - if (queue->update != NULL) - (*queue->update)(queue->array[index], index); - index = which; - } - - /* Restore the tmp node to appropriate place. */ - queue->array[index] = tmp; - if (queue->update != NULL) - (*queue->update)(tmp, index); -} - -struct pqueue *pqueue_create(void) -{ - struct pqueue *queue; - - queue = XCALLOC(MTYPE_PQUEUE, sizeof(struct pqueue)); - - queue->array = - XCALLOC(MTYPE_PQUEUE_DATA, DATA_SIZE * PQUEUE_INIT_ARRAYSIZE); - queue->array_size = PQUEUE_INIT_ARRAYSIZE; - - /* By default we want nothing to happen when a node changes. */ - queue->update = NULL; - return queue; -} - -void pqueue_delete(struct pqueue *queue) -{ - XFREE(MTYPE_PQUEUE_DATA, queue->array); - XFREE(MTYPE_PQUEUE, queue); -} - -static int pqueue_expand(struct pqueue *queue) -{ - void **newarray; - - newarray = - XCALLOC(MTYPE_PQUEUE_DATA, queue->array_size * DATA_SIZE * 2); - - memcpy(newarray, queue->array, queue->array_size * DATA_SIZE); - - XFREE(MTYPE_PQUEUE_DATA, queue->array); - queue->array = newarray; - queue->array_size *= 2; - - return 1; -} - -void pqueue_enqueue(void *data, struct pqueue *queue) -{ - if (queue->size + 2 >= queue->array_size && !pqueue_expand(queue)) - return; - - queue->array[queue->size] = data; - if (queue->update != NULL) - (*queue->update)(data, queue->size); - trickle_up(queue->size, queue); - queue->size++; -} - -void *pqueue_dequeue(struct pqueue *queue) -{ - void *data = queue->array[0]; - queue->array[0] = queue->array[--queue->size]; - trickle_down(0, queue); - return data; -} - -void pqueue_remove_at(int index, struct pqueue *queue) -{ - queue->array[index] = queue->array[--queue->size]; - - if (index > 0 - && (*queue->cmp)(queue->array[index], - queue->array[PARENT_OF(index)]) - < 0) { - trickle_up(index, queue); - } else { - trickle_down(index, queue); - } -} - -void pqueue_remove(void *data, struct pqueue *queue) -{ - for (int i = 0; i < queue->size; i++) - if (queue->array[i] == data) - pqueue_remove_at(i, queue); -} diff --git a/lib/prefix.c b/lib/prefix.c index 6b91969218..0a88f9eca6 100644 --- a/lib/prefix.c +++ b/lib/prefix.c @@ -22,12 +22,14 @@ #include #include "prefix.h" +#include "ipaddr.h" #include "vty.h" #include "sockunion.h" #include "memory.h" #include "log.h" #include "jhash.h" #include "lib_errors.h" +#include "printfrr.h" DEFINE_MTYPE_STATIC(LIB, PREFIX, "Prefix") DEFINE_MTYPE_STATIC(LIB, PREFIX_FLOWSPEC, "Prefix Flowspec") @@ -36,394 +38,6 @@ DEFINE_MTYPE_STATIC(LIB, PREFIX_FLOWSPEC, "Prefix Flowspec") static const uint8_t maskbit[] = {0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff}; -static const struct in6_addr maskbytes6[] = { - /* /0 */ {{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /1 */ - {{{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /2 */ - {{{0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /3 */ - {{{0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /4 */ - {{{0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /5 */ - {{{0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /6 */ - {{{0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /7 */ - {{{0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /8 */ - {{{0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /9 */ - {{{0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /10 */ - {{{0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /11 */ - {{{0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /12 */ - {{{0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /13 */ - {{{0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /14 */ - {{{0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /15 */ - {{{0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /16 */ - {{{0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /17 */ - {{{0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /18 */ - {{{0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /19 */ - {{{0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /20 */ - {{{0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /21 */ - {{{0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /22 */ - {{{0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /23 */ - {{{0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /24 */ - {{{0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /25 */ - {{{0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /26 */ - {{{0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /27 */ - {{{0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /28 */ - {{{0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /29 */ - {{{0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /30 */ - {{{0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /31 */ - {{{0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /32 */ - {{{0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /33 */ - {{{0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /34 */ - {{{0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /35 */ - {{{0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /36 */ - {{{0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /37 */ - {{{0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /38 */ - {{{0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /39 */ - {{{0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /40 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /41 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /42 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /43 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /44 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /45 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /46 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /47 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /48 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /49 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /50 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /51 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /52 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /53 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /54 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /55 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /56 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /57 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /58 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /59 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /60 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /61 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /62 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /63 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /64 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /65 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /66 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /67 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /68 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /69 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /70 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /71 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /72 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /73 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /74 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /75 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /76 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /77 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /78 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /79 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /80 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /81 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /82 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /83 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /84 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /85 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /86 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /87 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /88 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /89 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0x80, 0x00, 0x00, 0x00, 0x00}}}, - /* /90 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xc0, 0x00, 0x00, 0x00, 0x00}}}, - /* /91 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xe0, 0x00, 0x00, 0x00, 0x00}}}, - /* /92 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xf0, 0x00, 0x00, 0x00, 0x00}}}, - /* /93 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xf8, 0x00, 0x00, 0x00, 0x00}}}, - /* /94 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xfc, 0x00, 0x00, 0x00, 0x00}}}, - /* /95 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xfe, 0x00, 0x00, 0x00, 0x00}}}, - /* /96 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0x00, 0x00, 0x00, 0x00}}}, - /* /97 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0x80, 0x00, 0x00, 0x00}}}, - /* /98 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xc0, 0x00, 0x00, 0x00}}}, - /* /99 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xe0, 0x00, 0x00, 0x00}}}, - /* /100 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xf0, 0x00, 0x00, 0x00}}}, - /* /101 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xf8, 0x00, 0x00, 0x00}}}, - /* /102 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xfc, 0x00, 0x00, 0x00}}}, - /* /103 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xfe, 0x00, 0x00, 0x00}}}, - /* /104 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0x00, 0x00, 0x00}}}, - /* /105 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0x80, 0x00, 0x00}}}, - /* /106 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xc0, 0x00, 0x00}}}, - /* /107 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xe0, 0x00, 0x00}}}, - /* /108 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xf0, 0x00, 0x00}}}, - /* /109 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xf8, 0x00, 0x00}}}, - /* /110 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xfc, 0x00, 0x00}}}, - /* /111 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xfe, 0x00, 0x00}}}, - /* /112 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0x00, 0x00}}}, - /* /113 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0x80, 0x00}}}, - /* /114 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xc0, 0x00}}}, - /* /115 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xe0, 0x00}}}, - /* /116 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xf0, 0x00}}}, - /* /117 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xf8, 0x00}}}, - /* /118 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xfc, 0x00}}}, - /* /119 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xfe, 0x00}}}, - /* /120 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0x00}}}, - /* /121 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0x80}}}, - /* /122 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xc0}}}, - /* /123 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xe0}}}, - /* /124 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xf0}}}, - /* /125 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xf8}}}, - /* /126 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xfc}}}, - /* /127 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xfe}}}, - /* /128 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff}}}}; - /* Number of bits in prefix type. */ #ifndef PNBBY #define PNBBY 8 @@ -431,16 +45,7 @@ static const struct in6_addr maskbytes6[] = { #define MASKBIT(offset) ((0xff << (PNBBY - (offset))) & 0xff) -void prefix_hexdump(const struct prefix *p) -{ - char buf[PREFIX_STRLEN]; - - zlog_debug("prefix: %s", - prefix2str(p, buf, sizeof(buf))); - zlog_hexdump(p, sizeof(struct prefix)); -} - -int is_zero_mac(struct ethaddr *mac) +int is_zero_mac(const struct ethaddr *mac) { int i = 0; @@ -452,17 +57,31 @@ int is_zero_mac(struct ethaddr *mac) return 1; } -unsigned int prefix_bit(const uint8_t *prefix, const uint16_t prefixlen) +bool is_bcast_mac(const struct ethaddr *mac) { - unsigned int offset = prefixlen / 8; - unsigned int shift = 7 - (prefixlen % 8); + int i = 0; - return (prefix[offset] >> shift) & 1; + for (i = 0; i < ETH_ALEN; i++) + if (mac->octet[i] != 0xFF) + return false; + + return true; } -unsigned int prefix6_bit(const struct in6_addr *prefix, const uint16_t prefixlen) +bool is_mcast_mac(const struct ethaddr *mac) { - return prefix_bit((const uint8_t *)&prefix->s6_addr, prefixlen); + if ((mac->octet[0] & 0x01) == 0x01) + return true; + + return false; +} + +unsigned int prefix_bit(const uint8_t *prefix, const uint16_t bit_index) +{ + unsigned int offset = bit_index / 8; + unsigned int shift = 7 - (bit_index % 8); + + return (prefix[offset] >> shift) & 1; } int str2family(const char *string) @@ -569,6 +188,10 @@ int prefix_match(const struct prefix *n, const struct prefix *p) if (n->family == AF_FLOWSPEC) { /* prefixlen is unused. look at fs prefix len */ + if (n->u.prefix_flowspec.family != + p->u.prefix_flowspec.family) + return 0; + if (n->u.prefix_flowspec.prefixlen > p->u.prefix_flowspec.prefixlen) return 0; @@ -600,6 +223,53 @@ int prefix_match(const struct prefix *n, const struct prefix *p) if (np[offset] != pp[offset]) return 0; return 1; + +} + +/* + * n is a type5 evpn prefix. This function tries to see if there is an + * ip-prefix within n which matches prefix p + * If n includes p prefix then return 1 else return 0. + */ +int evpn_type5_prefix_match(const struct prefix *n, const struct prefix *p) +{ + int offset; + int shift; + int prefixlen; + const uint8_t *np, *pp; + struct prefix_evpn *evp; + + if (n->family != AF_EVPN) + return 0; + + evp = (struct prefix_evpn *)n; + pp = p->u.val; + + if ((evp->prefix.route_type != 5) || + (p->family == AF_INET6 && !is_evpn_prefix_ipaddr_v6(evp)) || + (p->family == AF_INET && !is_evpn_prefix_ipaddr_v4(evp)) || + (is_evpn_prefix_ipaddr_none(evp))) + return 0; + + prefixlen = evp->prefix.prefix_addr.ip_prefix_length; + np = &evp->prefix.prefix_addr.ip.ip.addr; + + /* If n's prefix is longer than p's one return 0. */ + if (prefixlen > p->prefixlen) + return 0; + + offset = prefixlen / PNBBY; + shift = prefixlen % PNBBY; + + if (shift) + if (maskbit[shift] & (np[offset] ^ pp[offset])) + return 0; + + while (offset--) + if (np[offset] != pp[offset]) + return 0; + return 1; + } /* If n includes p then return 1 else return 0. Prefix mask is not considered */ @@ -627,8 +297,15 @@ int prefix_match_network_statement(const struct prefix *n, return 1; } -void prefix_copy(struct prefix *dest, const struct prefix *src) +#ifdef __clang_analyzer__ +#undef prefix_copy /* cf. prefix.h */ +#endif + +void prefix_copy(union prefixptr udest, union prefixconstptr usrc) { + struct prefix *dest = udest.p; + const struct prefix *src = usrc.p; + dest->family = src->family; dest->prefixlen = src->prefixlen; @@ -652,6 +329,8 @@ void prefix_copy(struct prefix *dest, const struct prefix *src) len = src->u.prefix_flowspec.prefixlen; dest->u.prefix_flowspec.prefixlen = src->u.prefix_flowspec.prefixlen; + dest->u.prefix_flowspec.family = + src->u.prefix_flowspec.family; dest->family = src->family; temp = XCALLOC(MTYPE_PREFIX_FLOWSPEC, len); dest->u.prefix_flowspec.ptr = (uintptr_t)temp; @@ -673,8 +352,11 @@ void prefix_copy(struct prefix *dest, const struct prefix *src) * the same. Note that this routine has the same return value sense * as '==' (which is different from prefix_cmp). */ -int prefix_same(const struct prefix *p1, const struct prefix *p2) +int prefix_same(union prefixconstptr up1, union prefixconstptr up2) { + const struct prefix *p1 = up1.p; + const struct prefix *p2 = up2.p; + if ((p1 && !p2) || (!p1 && p2)) return 0; @@ -698,6 +380,9 @@ int prefix_same(const struct prefix *p1, const struct prefix *p2) sizeof(struct evpn_addr))) return 1; if (p1->family == AF_FLOWSPEC) { + if (p1->u.prefix_flowspec.family != + p2->u.prefix_flowspec.family) + return 0; if (p1->u.prefix_flowspec.prefixlen != p2->u.prefix_flowspec.prefixlen) return 0; @@ -711,55 +396,71 @@ int prefix_same(const struct prefix *p1, const struct prefix *p2) } /* - * Return 0 if the network prefixes represented by the struct prefix - * arguments are the same prefix, and 1 otherwise. Network prefixes - * are considered the same if the prefix lengths are equal and the - * network parts are the same. Host bits (which are considered masked + * Return -1/0/1 comparing the prefixes in a way that gives a full/linear + * order. + * + * Network prefixes are considered the same if the prefix lengths are equal + * and the network parts are the same. Host bits (which are considered masked * by the prefix length) are not significant. Thus, 10.0.0.1/8 and * 10.0.0.2/8 are considered equivalent by this routine. Note that * this routine has the same return sense as strcmp (which is different * from prefix_same). */ -int prefix_cmp(const struct prefix *p1, const struct prefix *p2) +int prefix_cmp(union prefixconstptr up1, union prefixconstptr up2) { + const struct prefix *p1 = up1.p; + const struct prefix *p2 = up2.p; int offset; int shift; + int i; /* Set both prefix's head pointer. */ const uint8_t *pp1; const uint8_t *pp2; if (p1->family != p2->family) - return 1; + return numcmp(p1->family, p2->family); if (p1->family == AF_FLOWSPEC) { pp1 = (const uint8_t *)p1->u.prefix_flowspec.ptr; pp2 = (const uint8_t *)p2->u.prefix_flowspec.ptr; + if (p1->u.prefix_flowspec.family != + p2->u.prefix_flowspec.family) + return 1; + if (p1->u.prefix_flowspec.prefixlen != p2->u.prefix_flowspec.prefixlen) - return 1; + return numcmp(p1->u.prefix_flowspec.prefixlen, + p2->u.prefix_flowspec.prefixlen); offset = p1->u.prefix_flowspec.prefixlen; while (offset--) if (pp1[offset] != pp2[offset]) - return 1; + return numcmp(pp1[offset], pp2[offset]); return 0; } pp1 = p1->u.val; pp2 = p2->u.val; if (p1->prefixlen != p2->prefixlen) - return 1; + return numcmp(p1->prefixlen, p2->prefixlen); offset = p1->prefixlen / PNBBY; shift = p1->prefixlen % PNBBY; - if (shift) - if (maskbit[shift] & (pp1[offset] ^ pp2[offset])) - return 1; + i = memcmp(pp1, pp2, offset); + if (i) + return i; - while (offset--) - if (pp1[offset] != pp2[offset]) - return 1; + /* + * At this point offset was the same, if we have shift + * that means we still have data to compare, if shift is + * 0 then we are at the end of the data structure + * and should just return, as that we will be accessing + * memory beyond the end of the party zone + */ + if (shift) + return numcmp(pp1[offset] & maskbit[shift], + pp2[offset] & maskbit[shift]); return 0; } @@ -835,12 +536,12 @@ struct prefix_ipv4 *prefix_ipv4_new(void) } /* Free prefix_ipv4 structure. */ -void prefix_ipv4_free(struct prefix_ipv4 *p) +void prefix_ipv4_free(struct prefix_ipv4 **p) { - prefix_free((struct prefix *)p); + prefix_free((struct prefix **)p); } -/* When string format is invalid return 0. */ +/* If given string is valid return 1 else return 0 */ int str2prefix_ipv4(const char *str, struct prefix_ipv4 *p) { int ret; @@ -868,8 +569,10 @@ int str2prefix_ipv4(const char *str, struct prefix_ipv4 *p) cp = XMALLOC(MTYPE_TMP, (pnt - str) + 1); memcpy(cp, str, pnt - str); *(cp + (pnt - str)) = '\0'; - ret = inet_aton(cp, &p->prefix); + ret = inet_pton(AF_INET, cp, &p->prefix); XFREE(MTYPE_TMP, cp); + if (ret == 0) + return 0; /* Get prefix length. */ plen = (uint8_t)atoi(++pnt); @@ -989,7 +692,7 @@ void apply_mask_ipv4(struct prefix_ipv4 *p) /* If prefix is 0.0.0.0/0 then return 1 else return 0. */ int prefix_ipv4_any(const struct prefix_ipv4 *p) { - return (p->prefix.s_addr == 0 && p->prefixlen == 0); + return (p->prefix.s_addr == INADDR_ANY && p->prefixlen == 0); } /* Allocate a new ip version 6 route */ @@ -1005,12 +708,12 @@ struct prefix_ipv6 *prefix_ipv6_new(void) } /* Free prefix for IPv6. */ -void prefix_ipv6_free(struct prefix_ipv6 *p) +void prefix_ipv6_free(struct prefix_ipv6 **p) { - prefix_free((struct prefix *)p); + prefix_free((struct prefix **)p); } -/* If given string is valid return pin6 else return NULL */ +/* If given string is valid return 1 else return 0 */ int str2prefix_ipv6(const char *str, struct prefix_ipv6 *p) { char *pnt; @@ -1049,31 +752,46 @@ int str2prefix_ipv6(const char *str, struct prefix_ipv6 *p) * FIXME return uint8_t as ip_maskleni() does. */ int ip6_masklen(struct in6_addr netmask) { - int len = 0; - unsigned char val; - unsigned char *pnt; - - pnt = (unsigned char *)&netmask; - - while ((*pnt == 0xff) && len < IPV6_MAX_BITLEN) { - len += 8; - pnt++; - } - - if (len < IPV6_MAX_BITLEN) { - val = *pnt; - while (val) { - len++; - val <<= 1; - } - } - return len; + if (netmask.s6_addr32[0] != 0xffffffffU) + return __builtin_clz(~ntohl(netmask.s6_addr32[0])); + if (netmask.s6_addr32[1] != 0xffffffffU) + return __builtin_clz(~ntohl(netmask.s6_addr32[1])) + 32; + if (netmask.s6_addr32[2] != 0xffffffffU) + return __builtin_clz(~ntohl(netmask.s6_addr32[2])) + 64; + if (netmask.s6_addr32[3] != 0xffffffffU) + return __builtin_clz(~ntohl(netmask.s6_addr32[3])) + 96; + /* note __builtin_clz(0) is undefined */ + return 128; } void masklen2ip6(const int masklen, struct in6_addr *netmask) { assert(masklen >= 0 && masklen <= IPV6_MAX_BITLEN); - memcpy(netmask, maskbytes6 + masklen, sizeof(struct in6_addr)); + + if (masklen == 0) { + /* note << 32 is undefined */ + memset(netmask, 0, sizeof(*netmask)); + } else if (masklen <= 32) { + netmask->s6_addr32[0] = htonl(0xffffffffU << (32 - masklen)); + netmask->s6_addr32[1] = 0; + netmask->s6_addr32[2] = 0; + netmask->s6_addr32[3] = 0; + } else if (masklen <= 64) { + netmask->s6_addr32[0] = 0xffffffffU; + netmask->s6_addr32[1] = htonl(0xffffffffU << (64 - masklen)); + netmask->s6_addr32[2] = 0; + netmask->s6_addr32[3] = 0; + } else if (masklen <= 96) { + netmask->s6_addr32[0] = 0xffffffffU; + netmask->s6_addr32[1] = 0xffffffffU; + netmask->s6_addr32[2] = htonl(0xffffffffU << (96 - masklen)); + netmask->s6_addr32[3] = 0; + } else { + netmask->s6_addr32[0] = 0xffffffffU; + netmask->s6_addr32[1] = 0xffffffffU; + netmask->s6_addr32[2] = 0xffffffffU; + netmask->s6_addr32[3] = htonl(0xffffffffU << (128 - masklen)); + } } void apply_mask_ipv6(struct prefix_ipv6 *p) @@ -1111,33 +829,6 @@ void apply_mask(struct prefix *p) return; } -/* Utility function of convert between struct prefix <=> union sockunion. - * FIXME This function isn't used anywhere. */ -struct prefix *sockunion2prefix(const union sockunion *dest, - const union sockunion *mask) -{ - if (dest->sa.sa_family == AF_INET) { - struct prefix_ipv4 *p; - - p = prefix_ipv4_new(); - p->family = AF_INET; - p->prefix = dest->sin.sin_addr; - p->prefixlen = ip_masklen(mask->sin.sin_addr); - return (struct prefix *)p; - } - if (dest->sa.sa_family == AF_INET6) { - struct prefix_ipv6 *p; - - p = prefix_ipv6_new(); - p->family = AF_INET6; - p->prefixlen = ip6_masklen(mask->sin6.sin6_addr); - memcpy(&p->prefix, &dest->sin6.sin6_addr, - sizeof(struct in6_addr)); - return (struct prefix *)p; - } - return NULL; -} - /* Utility function of convert between struct prefix <=> union sockunion. */ struct prefix *sockunion2hostprefix(const union sockunion *su, struct prefix *prefix) @@ -1181,13 +872,10 @@ int prefix_blen(const struct prefix *p) switch (p->family) { case AF_INET: return IPV4_MAX_BYTELEN; - break; case AF_INET6: return IPV6_MAX_BYTELEN; - break; case AF_ETHERNET: return ETH_ALEN; - break; } return 0; } @@ -1292,7 +980,7 @@ static const char *prefixevpn_prefix2str(const struct prefix_evpn *p, char *str, family = is_evpn_prefix_ipaddr_v4(p) ? AF_INET : AF_INET6; - snprintf(str, size, "[%d]:[%u][%s/%d]/%d", + snprintf(str, size, "[%d]:[%u]:[%s/%d]/%d", p->prefix.route_type, p->prefix.prefix_addr.eth_tag, inet_ntop(family, @@ -1328,13 +1016,29 @@ const char *prefix2str(union prefixconstptr pu, char *str, int size) { const struct prefix *p = pu.p; char buf[PREFIX2STR_BUFFER]; + int byte, tmp, a, b; + bool z = false; + size_t l; switch (p->family) { case AF_INET: case AF_INET6: - snprintf(str, size, "%s/%d", inet_ntop(p->family, &p->u.prefix, - buf, PREFIX2STR_BUFFER), - p->prefixlen); + inet_ntop(p->family, &p->u.prefix, buf, sizeof(buf)); + l = strlen(buf); + buf[l++] = '/'; + byte = p->prefixlen; + if ((tmp = p->prefixlen - 100) >= 0) { + buf[l++] = '1'; + z = true; + byte = tmp; + } + b = byte % 10; + a = byte / 10; + if (a || z) + buf[l++] = '0' + a; + buf[l++] = '0' + b; + buf[l] = '\0'; + strlcpy(str, buf, size); break; case AF_ETHERNET: @@ -1348,11 +1052,11 @@ const char *prefix2str(union prefixconstptr pu, char *str, int size) break; case AF_FLOWSPEC: - sprintf(str, "FS prefix"); + strlcpy(str, "FS prefix", size); break; default: - sprintf(str, "UNK prefix"); + strlcpy(str, "UNK prefix", size); break; } @@ -1365,7 +1069,7 @@ void prefix_mcast_inet4_dump(const char *onfail, struct in_addr addr, int save_errno = errno; if (addr.s_addr == INADDR_ANY) - strcpy(buf, "*"); + strlcpy(buf, "*", buf_size); else { if (!inet_ntop(AF_INET, &addr, buf, buf_size)) { if (onfail) @@ -1392,14 +1096,21 @@ struct prefix *prefix_new(void) { struct prefix *p; - p = XCALLOC(MTYPE_PREFIX, sizeof *p); + p = XCALLOC(MTYPE_PREFIX, sizeof(*p)); return p; } +void prefix_free_lists(void *arg) +{ + struct prefix *p = arg; + + prefix_free(&p); +} + /* Free prefix structure. */ -void prefix_free(struct prefix *p) +void prefix_free(struct prefix **p) { - XFREE(MTYPE_PREFIX, p); + XFREE(MTYPE_PREFIX, *p); } /* Utility function to convert ipv4 prefixes to Classful prefixes */ @@ -1425,25 +1136,17 @@ void apply_classful_mask_ipv4(struct prefix_ipv4 *p) } } -in_addr_t ipv4_network_addr(in_addr_t hostaddr, int masklen) -{ - struct in_addr mask; - - masklen2ip(masklen, &mask); - return hostaddr & mask.s_addr; -} - in_addr_t ipv4_broadcast_addr(in_addr_t hostaddr, int masklen) { struct in_addr mask; masklen2ip(masklen, &mask); return (masklen != IPV4_MAX_PREFIXLEN - 1) ? - /* normal case */ - (hostaddr | ~mask.s_addr) - : - /* special case for /31 */ - (hostaddr ^ ~mask.s_addr); + /* normal case */ + (hostaddr | ~mask.s_addr) + : + /* For prefix 31 return 255.255.255.255 (RFC3021) */ + htonl(0xFFFFFFFF); } /* Utility function to convert ipv4 netmask to prefixes @@ -1471,7 +1174,7 @@ int netmask_str2prefix_str(const char *net_str, const char *mask_str, } else { destination = ntohl(network.s_addr); - if (network.s_addr == 0) + if (network.s_addr == INADDR_ANY) prefixlen = 0; else if (IN_CLASSC(destination)) prefixlen = 24; @@ -1543,7 +1246,7 @@ char *prefix_mac2str(const struct ethaddr *mac, char *buf, int size) return ptr; } -unsigned prefix_hash_key(void *pp) +unsigned prefix_hash_key(const void *pp) { struct prefix copy; @@ -1626,3 +1329,68 @@ char *esi_to_str(const esi_t *esi, char *buf, int size) esi->val[9]); return ptr; } + +printfrr_ext_autoreg_p("EA", printfrr_ea) +static ssize_t printfrr_ea(char *buf, size_t bsz, const char *fmt, + int prec, const void *ptr) +{ + const struct ethaddr *mac = ptr; + + prefix_mac2str(mac, buf, bsz); + return 2; +} + +printfrr_ext_autoreg_p("IA", printfrr_ia) +static ssize_t printfrr_ia(char *buf, size_t bsz, const char *fmt, + int prec, const void *ptr) +{ + const struct ipaddr *ipa = ptr; + + ipaddr2str(ipa, buf, bsz); + return 2; +} + +printfrr_ext_autoreg_p("I4", printfrr_i4) +static ssize_t printfrr_i4(char *buf, size_t bsz, const char *fmt, + int prec, const void *ptr) +{ + inet_ntop(AF_INET, ptr, buf, bsz); + return 2; +} + +printfrr_ext_autoreg_p("I6", printfrr_i6) +static ssize_t printfrr_i6(char *buf, size_t bsz, const char *fmt, + int prec, const void *ptr) +{ + inet_ntop(AF_INET6, ptr, buf, bsz); + return 2; +} + +printfrr_ext_autoreg_p("FX", printfrr_pfx) +static ssize_t printfrr_pfx(char *buf, size_t bsz, const char *fmt, + int prec, const void *ptr) +{ + prefix2str(ptr, buf, bsz); + return 2; +} + +printfrr_ext_autoreg_p("SG4", printfrr_psg) +static ssize_t printfrr_psg(char *buf, size_t bsz, const char *fmt, + int prec, const void *ptr) +{ + const struct prefix_sg *sg = ptr; + struct fbuf fb = { .buf = buf, .pos = buf, .len = bsz - 1 }; + + if (sg->src.s_addr == INADDR_ANY) + bprintfrr(&fb, "(*,"); + else + bprintfrr(&fb, "(%pI4,", &sg->src); + + if (sg->grp.s_addr == INADDR_ANY) + bprintfrr(&fb, "*)"); + else + bprintfrr(&fb, "%pI4)", &sg->grp); + + fb.pos[0] = '\0'; + return 3; +} diff --git a/lib/prefix.h b/lib/prefix.h index d3c387e102..2a33d532c8 100644 --- a/lib/prefix.h +++ b/lib/prefix.h @@ -43,9 +43,36 @@ extern "C" { #define ETH_ALEN 6 #endif +/* EVPN route types. */ +typedef enum { + BGP_EVPN_AD_ROUTE = 1, /* Ethernet Auto-Discovery (A-D) route */ + BGP_EVPN_MAC_IP_ROUTE, /* MAC/IP Advertisement route */ + BGP_EVPN_IMET_ROUTE, /* Inclusive Multicast Ethernet Tag route */ + BGP_EVPN_ES_ROUTE, /* Ethernet Segment route */ + BGP_EVPN_IP_PREFIX_ROUTE, /* IP Prefix route */ +} bgp_evpn_route_type; + +/* value of first byte of ESI */ +#define ESI_TYPE_ARBITRARY 0 /* */ +#define ESI_TYPE_LACP 1 /* <> */ +#define ESI_TYPE_BRIDGE 2 /* ::00 */ +#define ESI_TYPE_MAC 3 /* : */ +#define ESI_TYPE_ROUTER 4 /* : */ +#define ESI_TYPE_AS 5 /* : */ + +#define MAX_ESI {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} + + +#define EVPN_ETH_TAG_BYTES 4 #define ESI_BYTES 10 #define ESI_STR_LEN (3 * ESI_BYTES) +/* Maximum number of VTEPs per-ES - + * XXX - temporary limit for allocating strings etc. + */ +#define ES_VTEP_MAX_CNT 10 +#define ES_VTEP_LIST_STR_SZ (ES_VTEP_MAX_CNT * 16) + #define ETHER_ADDR_STRLEN (3*ETH_ALEN) /* * there isn't a portable ethernet address type. We define our @@ -64,12 +91,13 @@ struct ethaddr { #define PREFIX_LEN_ROUTE_TYPE_5_IPV6 (30*8) typedef struct esi_t_ { - uint8_t val[10]; + uint8_t val[ESI_BYTES]; } esi_t; struct evpn_ead_addr { esi_t esi; uint32_t eth_tag; + struct ipaddr ip; }; struct evpn_macip_addr { @@ -148,6 +176,7 @@ struct evpn_addr { #endif struct flowspec_prefix { + uint8_t family; uint16_t prefixlen; /* length in bytes */ uintptr_t ptr; }; @@ -217,39 +246,45 @@ struct prefix_evpn { static inline int is_evpn_prefix_ipaddr_none(const struct prefix_evpn *evp) { - if (evp->prefix.route_type == 2) + if (evp->prefix.route_type == BGP_EVPN_AD_ROUTE) + return IS_IPADDR_NONE(&(evp)->prefix.ead_addr.ip); + if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) return IS_IPADDR_NONE(&(evp)->prefix.macip_addr.ip); - if (evp->prefix.route_type == 3) + if (evp->prefix.route_type == BGP_EVPN_IMET_ROUTE) return IS_IPADDR_NONE(&(evp)->prefix.imet_addr.ip); - if (evp->prefix.route_type == 4) + if (evp->prefix.route_type == BGP_EVPN_ES_ROUTE) return IS_IPADDR_NONE(&(evp)->prefix.es_addr.ip); - if (evp->prefix.route_type == 5) + if (evp->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE) return IS_IPADDR_NONE(&(evp)->prefix.prefix_addr.ip); return 0; } static inline int is_evpn_prefix_ipaddr_v4(const struct prefix_evpn *evp) { - if (evp->prefix.route_type == 2) + if (evp->prefix.route_type == BGP_EVPN_AD_ROUTE) + return IS_IPADDR_V4(&(evp)->prefix.ead_addr.ip); + if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) return IS_IPADDR_V4(&(evp)->prefix.macip_addr.ip); - if (evp->prefix.route_type == 3) + if (evp->prefix.route_type == BGP_EVPN_IMET_ROUTE) return IS_IPADDR_V4(&(evp)->prefix.imet_addr.ip); - if (evp->prefix.route_type == 4) + if (evp->prefix.route_type == BGP_EVPN_ES_ROUTE) return IS_IPADDR_V4(&(evp)->prefix.es_addr.ip); - if (evp->prefix.route_type == 5) + if (evp->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE) return IS_IPADDR_V4(&(evp)->prefix.prefix_addr.ip); return 0; } static inline int is_evpn_prefix_ipaddr_v6(const struct prefix_evpn *evp) { - if (evp->prefix.route_type == 2) + if (evp->prefix.route_type == BGP_EVPN_AD_ROUTE) + return IS_IPADDR_V6(&(evp)->prefix.ead_addr.ip); + if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) return IS_IPADDR_V6(&(evp)->prefix.macip_addr.ip); - if (evp->prefix.route_type == 3) + if (evp->prefix.route_type == BGP_EVPN_IMET_ROUTE) return IS_IPADDR_V6(&(evp)->prefix.imet_addr.ip); - if (evp->prefix.route_type == 4) + if (evp->prefix.route_type == BGP_EVPN_ES_ROUTE) return IS_IPADDR_V6(&(evp)->prefix.es_addr.ip); - if (evp->prefix.route_type == 5) + if (evp->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE) return IS_IPADDR_V6(&(evp)->prefix.prefix_addr.ip); return 0; } @@ -295,6 +330,7 @@ union prefixptr { prefixtype(prefixptr, struct prefix_ipv6, p6) prefixtype(prefixptr, struct prefix_evpn, evp) prefixtype(prefixptr, struct prefix_fs, fs) + prefixtype(prefixptr, struct prefix_rd, rd) } __attribute__((transparent_union)); union prefixconstptr { @@ -303,6 +339,7 @@ union prefixconstptr { prefixtype(prefixconstptr, const struct prefix_ipv6, p6) prefixtype(prefixconstptr, const struct prefix_evpn, evp) prefixtype(prefixconstptr, const struct prefix_fs, fs) + prefixtype(prefixconstptr, const struct prefix_rd, rd) } __attribute__((transparent_union)); #ifndef INET_ADDRSTRLEN @@ -390,13 +427,23 @@ extern const char *family2str(int family); extern const char *safi2str(safi_t safi); extern const char *afi2str(afi_t afi); -/* Check bit of the prefix. */ -extern unsigned int prefix_bit(const uint8_t *prefix, const uint16_t prefixlen); -extern unsigned int prefix6_bit(const struct in6_addr *prefix, - const uint16_t prefixlen); +/* + * Check bit of the prefix. + * + * prefix + * byte buffer + * + * bit_index + * which bit to fetch from byte buffer, 0 indexed. + */ +extern unsigned int prefix_bit(const uint8_t *prefix, const uint16_t bit_index); extern struct prefix *prefix_new(void); -extern void prefix_free(struct prefix *); +extern void prefix_free(struct prefix **p); +/* + * Function to handle prefix_free being used as a del function. + */ +extern void prefix_free_lists(void *arg); extern const char *prefix_family_str(const struct prefix *); extern int prefix_blen(const struct prefix *); extern int str2prefix(const char *, struct prefix *); @@ -407,17 +454,25 @@ extern void prefix_mcast_inet4_dump(const char *onfail, struct in_addr addr, char *buf, int buf_size); extern const char *prefix_sg2str(const struct prefix_sg *sg, char *str); extern const char *prefix2str(union prefixconstptr, char *, int); +extern int evpn_type5_prefix_match(const struct prefix *evpn_pfx, + const struct prefix *match_pfx); extern int prefix_match(const struct prefix *, const struct prefix *); extern int prefix_match_network_statement(const struct prefix *, const struct prefix *); -extern int prefix_same(const struct prefix *, const struct prefix *); -extern int prefix_cmp(const struct prefix *, const struct prefix *); +extern int prefix_same(union prefixconstptr, union prefixconstptr); +extern int prefix_cmp(union prefixconstptr, union prefixconstptr); extern int prefix_common_bits(const struct prefix *, const struct prefix *); -extern void prefix_copy(struct prefix *dest, const struct prefix *src); +extern void prefix_copy(union prefixptr, union prefixconstptr); extern void apply_mask(struct prefix *); -extern struct prefix *sockunion2prefix(const union sockunion *dest, - const union sockunion *mask); +#ifdef __clang_analyzer__ +/* clang-SA doesn't understand transparent unions, making it think that the + * target of prefix_copy is uninitialized. So just memset the target. + * cf. https://bugs.llvm.org/show_bug.cgi?id=42811 + */ +#define prefix_copy(a, b) ({ memset(a, 0, sizeof(*a)); prefix_copy(a, b); }) +#endif + extern struct prefix *sockunion2hostprefix(const union sockunion *, struct prefix *p); extern void prefix2sockunion(const struct prefix *, union sockunion *); @@ -425,7 +480,7 @@ extern void prefix2sockunion(const struct prefix *, union sockunion *); extern int str2prefix_eth(const char *, struct prefix_eth *); extern struct prefix_ipv4 *prefix_ipv4_new(void); -extern void prefix_ipv4_free(struct prefix_ipv4 *); +extern void prefix_ipv4_free(struct prefix_ipv4 **p); extern int str2prefix_ipv4(const char *, struct prefix_ipv4 *); extern void apply_mask_ipv4(struct prefix_ipv4 *); @@ -439,18 +494,15 @@ extern void apply_classful_mask_ipv4(struct prefix_ipv4 *); extern uint8_t ip_masklen(struct in_addr); extern void masklen2ip(const int, struct in_addr *); -/* returns the network portion of the host address */ -extern in_addr_t ipv4_network_addr(in_addr_t hostaddr, int masklen); /* given the address of a host on a network and the network mask length, * calculate the broadcast address for that network; - * special treatment for /31: returns the address of the other host - * on the network by flipping the host bit */ + * special treatment for /31 according to RFC3021 section 3.3 */ extern in_addr_t ipv4_broadcast_addr(in_addr_t hostaddr, int masklen); extern int netmask_str2prefix_str(const char *, const char *, char *); extern struct prefix_ipv6 *prefix_ipv6_new(void); -extern void prefix_ipv6_free(struct prefix_ipv6 *); +extern void prefix_ipv6_free(struct prefix_ipv6 **p); extern int str2prefix_ipv6(const char *, struct prefix_ipv6 *); extern void apply_mask_ipv6(struct prefix_ipv6 *); @@ -462,15 +514,16 @@ extern void masklen2ip6(const int, struct in6_addr *); extern const char *inet6_ntoa(struct in6_addr); -extern int is_zero_mac(struct ethaddr *mac); +extern int is_zero_mac(const struct ethaddr *mac); +extern bool is_mcast_mac(const struct ethaddr *mac); +extern bool is_bcast_mac(const struct ethaddr *mac); extern int prefix_str2mac(const char *str, struct ethaddr *mac); extern char *prefix_mac2str(const struct ethaddr *mac, char *buf, int size); -extern unsigned prefix_hash_key(void *pp); +extern unsigned prefix_hash_key(const void *pp); extern int str_to_esi(const char *str, esi_t *esi); extern char *esi_to_str(const esi_t *esi, char *buf, int size); -extern void prefix_hexdump(const struct prefix *p); extern void prefix_evpn_hexdump(const struct prefix_evpn *p); static inline int ipv6_martian(struct in6_addr *addr) @@ -514,7 +567,7 @@ static inline int is_default_prefix(const struct prefix *p) return 0; } -static inline int is_host_route(struct prefix *p) +static inline int is_host_route(const struct prefix *p) { if (p->family == AF_INET) return (p->prefixlen == IPV4_MAX_BITLEN); @@ -523,7 +576,7 @@ static inline int is_host_route(struct prefix *p) return 0; } -static inline int is_default_host_route(struct prefix *p) +static inline int is_default_host_route(const struct prefix *p) { if (p->family == AF_INET) { return (p->u.prefix4.s_addr == INADDR_ANY && @@ -536,6 +589,24 @@ static inline int is_default_host_route(struct prefix *p) return 0; } +#ifdef _FRR_ATTRIBUTE_PRINTFRR +#pragma FRR printfrr_ext "%pEA" (struct ethaddr *) + +#pragma FRR printfrr_ext "%pI4" (struct in_addr *) +#pragma FRR printfrr_ext "%pI4" (in_addr_t *) + +#pragma FRR printfrr_ext "%pI6" (struct in6_addr *) + +#pragma FRR printfrr_ext "%pFX" (struct prefix *) +#pragma FRR printfrr_ext "%pFX" (struct prefix_ipv4 *) +#pragma FRR printfrr_ext "%pFX" (struct prefix_ipv6 *) +#pragma FRR printfrr_ext "%pFX" (struct prefix_eth *) +#pragma FRR printfrr_ext "%pFX" (struct prefix_evpn *) +#pragma FRR printfrr_ext "%pFX" (struct prefix_fs *) + +#pragma FRR printfrr_ext "%pSG4" (struct prefix_sg *) +#endif + #ifdef __cplusplus } #endif diff --git a/lib/printf/README b/lib/printf/README new file mode 100644 index 0000000000..ac283642d7 --- /dev/null +++ b/lib/printf/README @@ -0,0 +1,9 @@ +This is the printf implementation from FreeBSD. It was imported on 2019-05-12, +from SVN revision 347514 (but the code hadn't been touched for 2 years before +that.) + +Please don't reindent or otherwise mangle the files to make importing fixes +easy (not that there are significant changes likely to happen...) + +The changes to this code are published under FreeBSD's license as listed in the +file headers. If you change license, please make that as obvious as possible. diff --git a/lib/printf/glue.c b/lib/printf/glue.c new file mode 100644 index 0000000000..29ca26ad5d --- /dev/null +++ b/lib/printf/glue.c @@ -0,0 +1,253 @@ +/* + * Copyright (c) 2019 David Lamparter, for NetDEF, Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include "printfrr.h" +#include "printflocal.h" + +ssize_t bprintfrr(struct fbuf *out, const char *fmt, ...) +{ + ssize_t ret; + va_list ap; + + va_start(ap, fmt); + ret = vbprintfrr(out, fmt, ap); + va_end(ap); + return ret; +} + +ssize_t vsnprintfrr(char *out, size_t outsz, const char *fmt, va_list ap) +{ + struct fbuf fbb = { .buf = out, .pos = out, .len = outsz - 1, }; + struct fbuf *fb = (out && outsz) ? &fbb : NULL; + ssize_t ret; + + ret = vbprintfrr(fb, fmt, ap); + if (fb) + fb->pos[0] = '\0'; + return ret; +} + +ssize_t snprintfrr(char *out, size_t outsz, const char *fmt, ...) +{ + struct fbuf fbb = { .buf = out, .pos = out, .len = outsz - 1, }; + struct fbuf *fb = (out && outsz) ? &fbb : NULL; + ssize_t ret; + va_list ap; + + va_start(ap, fmt); + ret = vbprintfrr(fb, fmt, ap); + va_end(ap); + if (fb) + fb->pos[0] = '\0'; + return ret; +} + +ssize_t vcsnprintfrr(char *out, size_t outsz, const char *fmt, va_list ap) +{ + if (!out || !outsz) + return vbprintfrr(NULL, fmt, ap); + + struct fbuf fbb = { .buf = out, .pos = out, .len = outsz - 1, }; + ssize_t ret; + size_t pos; + + pos = strnlen(out, outsz); + fbb.pos += pos; + + ret = vbprintfrr(&fbb, fmt, ap); + fbb.pos[0] = '\0'; + return ret >= 0 ? ret + (ssize_t)pos : ret; +} + +ssize_t csnprintfrr(char *out, size_t outsz, const char *fmt, ...) +{ + ssize_t ret; + va_list ap; + + va_start(ap, fmt); + ret = vcsnprintfrr(out, outsz, fmt, ap); + va_end(ap); + return ret; +} + +char *vasnprintfrr(struct memtype *mt, char *out, size_t outsz, const char *fmt, + va_list ap) +{ + struct fbuf fb = { .buf = out, .pos = out, .len = outsz - 1, }; + ssize_t len; + va_list ap2; + char *ret = out; + + va_copy(ap2, ap); + len = vbprintfrr(&fb, fmt, ap); + if (len < 0) { + va_end(ap2); + /* error = malformed format string => try something useful */ + return qstrdup(mt, fmt); + } + + if ((size_t)len >= outsz - 1) { + ret = qmalloc(mt, len + 1); + fb.buf = fb.pos = ret; + fb.len = len; + + vbprintfrr(&fb, fmt, ap2); + } + + va_end(ap2); + ret[len] = '\0'; + return ret; +} + +char *asnprintfrr(struct memtype *mt, char *out, size_t outsz, const char *fmt, + ...) +{ + va_list ap; + char *ret; + + va_start(ap, fmt); + ret = vasnprintfrr(mt, out, outsz, fmt, ap); + va_end(ap); + return ret; +} + +char *vasprintfrr(struct memtype *mt, const char *fmt, va_list ap) +{ + char buf[256]; + char *ret; + + ret = vasnprintfrr(mt, buf, sizeof(buf), fmt, ap); + + if (ret == buf) + ret = qstrdup(mt, ret); + return ret; +} + +char *asprintfrr(struct memtype *mt, const char *fmt, ...) +{ + char buf[256]; + va_list ap; + char *ret; + + va_start(ap, fmt); + ret = vasnprintfrr(mt, buf, sizeof(buf), fmt, ap); + va_end(ap); + + if (ret == buf) + ret = qstrdup(mt, ret); + return ret; +} + +/* Q: WTF? + * A: since printf should be reasonably fast (think debugging logs), the idea + * here is to keep things close by each other in a cacheline. That's why + * ext_quick just has the first 2 characters of an extension, and we do a + * nice linear continuous sweep. Only if we find something, we go do more + * expensive things. + * + * Q: doesn't this need a mutex/lock? + * A: theoretically, yes, but that's quite expensive and I rather elide that + * necessity by putting down some usage rules. Just call this at startup + * while singlethreaded and all is fine. Ideally, just use constructors + * (and make sure dlopen() doesn't mess things up...) + */ +#define MAXEXT 64 + +struct ext_quick { + char fmt[2]; +}; + +static uint8_t ext_offsets[26] __attribute__((aligned(32))); +static struct ext_quick entries[MAXEXT] __attribute__((aligned(64))); +static const struct printfrr_ext *exts[MAXEXT] __attribute__((aligned(64))); + +void printfrr_ext_reg(const struct printfrr_ext *ext) +{ + uint8_t o; + ptrdiff_t i; + + if (!printfrr_ext_char(ext->match[0])) + return; + + o = ext->match[0] - 'A'; + for (i = ext_offsets[o]; + i < MAXEXT && entries[i].fmt[0] && + memcmp(entries[i].fmt, ext->match, 2) < 0; + i++) + ; + if (i == MAXEXT) + return; + for (o++; o <= 'Z' - 'A'; o++) + ext_offsets[o]++; + + memmove(entries + i + 1, entries + i, + (MAXEXT - i - 1) * sizeof(entries[0])); + memmove(exts + i + 1, exts + i, + (MAXEXT - i - 1) * sizeof(exts[0])); + + memcpy(entries[i].fmt, ext->match, 2); + exts[i] = ext; +} + +ssize_t printfrr_extp(char *buf, size_t sz, const char *fmt, int prec, + const void *ptr) +{ + const struct printfrr_ext *ext; + size_t i; + + for (i = ext_offsets[fmt[0] - 'A']; i < MAXEXT; i++) { + if (!entries[i].fmt[0] || entries[i].fmt[0] > fmt[0]) + return 0; + if (entries[i].fmt[1] && entries[i].fmt[1] != fmt[1]) + continue; + ext = exts[i]; + if (!ext->print_ptr) + continue; + if (strncmp(ext->match, fmt, strlen(ext->match))) + continue; + return ext->print_ptr(buf, sz, fmt, prec, ptr); + } + return 0; +} + +ssize_t printfrr_exti(char *buf, size_t sz, const char *fmt, int prec, + uintmax_t num) +{ + const struct printfrr_ext *ext; + size_t i; + + for (i = ext_offsets[fmt[0] - 'A']; i < MAXEXT; i++) { + if (!entries[i].fmt[0] || entries[i].fmt[0] > fmt[0]) + return 0; + if (entries[i].fmt[1] && entries[i].fmt[1] != fmt[1]) + continue; + ext = exts[i]; + if (!ext->print_int) + continue; + if (strncmp(ext->match, fmt, strlen(ext->match))) + continue; + return ext->print_int(buf, sz, fmt, prec, num); + } + return 0; +} diff --git a/lib/printf/printf-pos.c b/lib/printf/printf-pos.c new file mode 100644 index 0000000000..cc03f7ef9a --- /dev/null +++ b/lib/printf/printf-pos.c @@ -0,0 +1,787 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_SYS_CDEFS_H +#include +#endif + +/* + * This is the code responsible for handling positional arguments + * (%m$ and %m$.n$) for vfprintf() and vfwprintf(). + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "printflocal.h" + +#ifdef NL_ARGMAX +#define MAX_POSARG NL_ARGMAX +#else +#define MAX_POSARG 65536 +#endif + +/* + * Type ids for argument type table. + */ +enum typeid { + T_UNUSED, TP_SHORT, T_INT, T_U_INT, TP_INT, + T_LONG, T_U_LONG, TP_LONG, T_LLONG, T_U_LLONG, TP_LLONG, + T_PTRDIFFT, TP_PTRDIFFT, T_SSIZET, T_SIZET, TP_SSIZET, + T_INT64T, T_UINT64T, T_INTMAXT, T_UINTMAXT, TP_INTMAXT, TP_VOID, + TP_CHAR, TP_SCHAR, T_DOUBLE, T_LONG_DOUBLE, T_WINT, TP_WCHAR +}; + +/* An expandable array of types. */ +struct typetable { + enum typeid *table; /* table of types */ + enum typeid stattable[STATIC_ARG_TBL_SIZE]; + u_int tablesize; /* current size of type table */ + u_int tablemax; /* largest used index in table */ + u_int nextarg; /* 1-based argument index */ +}; + +static int __grow_type_table(struct typetable *); +static void build_arg_table (struct typetable *, va_list, union arg **); + +/* + * Initialize a struct typetable. + */ +static inline void +inittypes(struct typetable *types) +{ + u_int n; + + types->table = types->stattable; + types->tablesize = STATIC_ARG_TBL_SIZE; + types->tablemax = 0; + types->nextarg = 1; + for (n = 0; n < STATIC_ARG_TBL_SIZE; n++) + types->table[n] = T_UNUSED; +} + +/* + * struct typetable destructor. + */ +static inline void +freetypes(struct typetable *types) +{ + + if (types->table != types->stattable) + free (types->table); +} + +/* + * Ensure that there is space to add a new argument type to the type table. + * Expand the table if necessary. Returns 0 on success. + */ +static inline int +_ensurespace(struct typetable *types) +{ + + if (types->nextarg >= types->tablesize) { + if (__grow_type_table(types)) + return -1; + } + if (types->nextarg > types->tablemax) + types->tablemax = types->nextarg; + return 0; +} + +/* + * Add an argument type to the table, expanding if necessary. + * Returns 0 on success. + */ +static inline int +addtype(struct typetable *types, enum typeid type) +{ + + if (_ensurespace(types)) + return -1; + types->table[types->nextarg++] = type; + return 0; +} + +static inline int +addsarg(struct typetable *types, int flags) +{ + + if (_ensurespace(types)) + return -1; + if (flags & LONGDBL) + types->table[types->nextarg++] = T_INT64T; + else if (flags & INTMAXT) + types->table[types->nextarg++] = T_INTMAXT; + else if (flags & SIZET) + types->table[types->nextarg++] = T_SSIZET; + else if (flags & PTRDIFFT) + types->table[types->nextarg++] = T_PTRDIFFT; + else if (flags & LLONGINT) + types->table[types->nextarg++] = T_LLONG; + else if (flags & LONGINT) + types->table[types->nextarg++] = T_LONG; + else + types->table[types->nextarg++] = T_INT; + return 0; +} + +static inline int +adduarg(struct typetable *types, int flags) +{ + + if (_ensurespace(types)) + return -1; + if (flags & LONGDBL) + types->table[types->nextarg++] = T_UINT64T; + else if (flags & INTMAXT) + types->table[types->nextarg++] = T_UINTMAXT; + else if (flags & SIZET) + types->table[types->nextarg++] = T_SIZET; + else if (flags & PTRDIFFT) + types->table[types->nextarg++] = T_SIZET; + else if (flags & LLONGINT) + types->table[types->nextarg++] = T_U_LLONG; + else if (flags & LONGINT) + types->table[types->nextarg++] = T_U_LONG; + else + types->table[types->nextarg++] = T_U_INT; + return 0; +} + +/* + * Add * arguments to the type array. + */ +static inline int +addaster(struct typetable *types, char **fmtp) +{ + char *cp; + u_int n2; + + n2 = 0; + cp = *fmtp; + while (is_digit(*cp)) { + n2 = 10 * n2 + to_digit(*cp); + cp++; + } + if (*cp == '$') { + u_int hold = types->nextarg; + types->nextarg = n2; + if (addtype(types, T_INT)) + return -1; + types->nextarg = hold; + *fmtp = ++cp; + } else { + if (addtype(types, T_INT)) + return -1; + } + return 0; +} + +#ifdef WCHAR_SUPPORT +static inline int +addwaster(struct typetable *types, wchar_t **fmtp) +{ + wchar_t *cp; + u_int n2; + + n2 = 0; + cp = *fmtp; + while (is_digit(*cp)) { + n2 = 10 * n2 + to_digit(*cp); + cp++; + } + if (*cp == '$') { + u_int hold = types->nextarg; + types->nextarg = n2; + if (addtype(types, T_INT)) + return -1; + types->nextarg = hold; + *fmtp = ++cp; + } else { + if (addtype(types, T_INT)) + return -1; + } + return 0; +} +#endif /* WCHAR_SUPPORT */ + +/* + * Find all arguments when a positional parameter is encountered. Returns a + * table, indexed by argument number, of pointers to each arguments. The + * initial argument table should be an array of STATIC_ARG_TBL_SIZE entries. + * It will be replaces with a malloc-ed one if it overflows. + * Returns 0 on success. On failure, returns nonzero and sets errno. + */ +int +_frr_find_arguments (const char *fmt0, va_list ap, union arg **argtable) +{ + char *fmt; /* format string */ + int ch; /* character from fmt */ + u_int n; /* handy integer (short term usage) */ + int error; + int flags; /* flags as above */ + struct typetable types; /* table of types */ + + fmt = (char *)fmt0; + inittypes(&types); + error = 0; + + /* + * Scan the format for conversions (`%' character). + */ + for (;;) { + while ((ch = *fmt) != '\0' && ch != '%') + fmt++; + if (ch == '\0') + goto done; + fmt++; /* skip over '%' */ + + flags = 0; + +rflag: ch = *fmt++; +reswitch: switch (ch) { + case ' ': + case '#': + goto rflag; + case '*': + if ((error = addaster(&types, &fmt))) + goto error; + goto rflag; + case '-': + case '+': + case '\'': + goto rflag; + case '.': + if ((ch = *fmt++) == '*') { + if ((error = addaster(&types, &fmt))) + goto error; + goto rflag; + } + while (is_digit(ch)) { + ch = *fmt++; + } + goto reswitch; + case '0': + goto rflag; + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + n = 0; + do { + n = 10 * n + to_digit(ch); + /* Detect overflow */ + if (n > MAX_POSARG) { + error = -1; + goto error; + } + ch = *fmt++; + } while (is_digit(ch)); + if (ch == '$') { + types.nextarg = n; + goto rflag; + } + goto reswitch; + case 'L': + flags |= LONGDBL; + goto rflag; + case 'h': + if (flags & SHORTINT) { + flags &= ~SHORTINT; + flags |= CHARINT; + } else + flags |= SHORTINT; + goto rflag; + case 'j': + flags |= INTMAXT; + goto rflag; + case 'l': + if (flags & LONGINT) { + flags &= ~LONGINT; + flags |= LLONGINT; + } else + flags |= LONGINT; + goto rflag; + case 'q': + flags |= LLONGINT; /* not necessarily */ + goto rflag; + case 't': + flags |= PTRDIFFT; + goto rflag; + case 'z': + flags |= SIZET; + goto rflag; + case 'C': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'c': + error = addtype(&types, + (flags & LONGINT) ? T_WINT : T_INT); + if (error) + goto error; + break; + case 'D': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'd': + case 'i': + if ((error = addsarg(&types, flags))) + goto error; + break; +#ifndef NO_FLOATING_POINT + case 'a': + case 'A': + case 'e': + case 'E': + case 'f': + case 'g': + case 'G': + error = addtype(&types, + (flags & LONGDBL) ? T_LONG_DOUBLE : T_DOUBLE); + if (error) + goto error; + break; +#endif /* !NO_FLOATING_POINT */ + case 'n': + if (flags & INTMAXT) + error = addtype(&types, TP_INTMAXT); + else if (flags & PTRDIFFT) + error = addtype(&types, TP_PTRDIFFT); + else if (flags & SIZET) + error = addtype(&types, TP_SSIZET); + else if (flags & LLONGINT) + error = addtype(&types, TP_LLONG); + else if (flags & LONGINT) + error = addtype(&types, TP_LONG); + else if (flags & SHORTINT) + error = addtype(&types, TP_SHORT); + else if (flags & CHARINT) + error = addtype(&types, TP_SCHAR); + else + error = addtype(&types, TP_INT); + if (error) + goto error; + continue; /* no output */ + case 'O': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'o': + if ((error = adduarg(&types, flags))) + goto error; + break; + case 'p': + if ((error = addtype(&types, TP_VOID))) + goto error; + break; + case 'S': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 's': + error = addtype(&types, + (flags & LONGINT) ? TP_WCHAR : TP_CHAR); + if (error) + goto error; + break; + case 'U': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'u': + case 'X': + case 'x': + if ((error = adduarg(&types, flags))) + goto error; + break; + default: /* "%?" prints ?, unless ? is NUL */ + if (ch == '\0') + goto done; + break; + } + } +done: + build_arg_table(&types, ap, argtable); +error: + freetypes(&types); + return (error || *argtable == NULL); +} + +#ifdef WCHAR_SUPPORT +/* wchar version of __find_arguments. */ +int +_frr_find_warguments (const wchar_t *fmt0, va_list ap, union arg **argtable) +{ + wchar_t *fmt; /* format string */ + wchar_t ch; /* character from fmt */ + u_int n; /* handy integer (short term usage) */ + int error; + int flags; /* flags as above */ + struct typetable types; /* table of types */ + + fmt = (wchar_t *)fmt0; + inittypes(&types); + error = 0; + + /* + * Scan the format for conversions (`%' character). + */ + for (;;) { + while ((ch = *fmt) != '\0' && ch != '%') + fmt++; + if (ch == '\0') + goto done; + fmt++; /* skip over '%' */ + + flags = 0; + +rflag: ch = *fmt++; +reswitch: switch (ch) { + case ' ': + case '#': + goto rflag; + case '*': + if ((error = addwaster(&types, &fmt))) + goto error; + goto rflag; + case '-': + case '+': + case '\'': + goto rflag; + case '.': + if ((ch = *fmt++) == '*') { + if ((error = addwaster(&types, &fmt))) + goto error; + goto rflag; + } + while (is_digit(ch)) { + ch = *fmt++; + } + goto reswitch; + case '0': + goto rflag; + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + n = 0; + do { + n = 10 * n + to_digit(ch); + /* Detect overflow */ + if (n > MAX_POSARG) { + error = -1; + goto error; + } + ch = *fmt++; + } while (is_digit(ch)); + if (ch == '$') { + types.nextarg = n; + goto rflag; + } + goto reswitch; + case 'L': + flags |= LONGDBL; + goto rflag; + case 'h': + if (flags & SHORTINT) { + flags &= ~SHORTINT; + flags |= CHARINT; + } else + flags |= SHORTINT; + goto rflag; + case 'j': + flags |= INTMAXT; + goto rflag; + case 'l': + if (flags & LONGINT) { + flags &= ~LONGINT; + flags |= LLONGINT; + } else + flags |= LONGINT; + goto rflag; + case 'q': + flags |= LLONGINT; /* not necessarily */ + goto rflag; + case 't': + flags |= PTRDIFFT; + goto rflag; + case 'z': + flags |= SIZET; + goto rflag; + case 'C': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'c': + error = addtype(&types, + (flags & LONGINT) ? T_WINT : T_INT); + if (error) + goto error; + break; + case 'D': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'd': + case 'i': + if ((error = addsarg(&types, flags))) + goto error; + break; +#ifndef NO_FLOATING_POINT + case 'a': + case 'A': + case 'e': + case 'E': + case 'f': + case 'g': + case 'G': + error = addtype(&types, + (flags & LONGDBL) ? T_LONG_DOUBLE : T_DOUBLE); + if (error) + goto error; + break; +#endif /* !NO_FLOATING_POINT */ + case 'n': + if (flags & INTMAXT) + error = addtype(&types, TP_INTMAXT); + else if (flags & PTRDIFFT) + error = addtype(&types, TP_PTRDIFFT); + else if (flags & SIZET) + error = addtype(&types, TP_SSIZET); + else if (flags & LLONGINT) + error = addtype(&types, TP_LLONG); + else if (flags & LONGINT) + error = addtype(&types, TP_LONG); + else if (flags & SHORTINT) + error = addtype(&types, TP_SHORT); + else if (flags & CHARINT) + error = addtype(&types, TP_SCHAR); + else + error = addtype(&types, TP_INT); + if (error) + goto error; + continue; /* no output */ + case 'O': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'o': + if ((error = adduarg(&types, flags))) + goto error; + break; + case 'p': + if ((error = addtype(&types, TP_VOID))) + goto error; + break; + case 'S': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 's': + error = addtype(&types, + (flags & LONGINT) ? TP_WCHAR : TP_CHAR); + if (error) + goto error; + break; + case 'U': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'u': + case 'X': + case 'x': + if ((error = adduarg(&types, flags))) + goto error; + break; + default: /* "%?" prints ?, unless ? is NUL */ + if (ch == '\0') + goto done; + break; + } + } +done: + build_arg_table(&types, ap, argtable); +error: + freetypes(&types); + return (error || *argtable == NULL); +} +#endif /* WCHAR_SUPPORT */ + +/* + * Increase the size of the type table. Returns 0 on success. + */ +static int +__grow_type_table(struct typetable *types) +{ + enum typeid *const oldtable = types->table; + const int oldsize = types->tablesize; + enum typeid *newtable; + u_int n, newsize; + + /* Detect overflow */ + if (types->nextarg > MAX_POSARG) + return -1; + + newsize = oldsize * 2; + if (newsize < types->nextarg + 1) + newsize = types->nextarg + 1; + if (oldsize == STATIC_ARG_TBL_SIZE) { + if ((newtable = malloc(newsize * sizeof(enum typeid))) == NULL) + return -1; + bcopy(oldtable, newtable, oldsize * sizeof(enum typeid)); + } else { + newtable = realloc(oldtable, newsize * sizeof(enum typeid)); + if (newtable == NULL) + return -1; + } + for (n = oldsize; n < newsize; n++) + newtable[n] = T_UNUSED; + + types->table = newtable; + types->tablesize = newsize; + + return 0; +} + +/* + * Build the argument table from the completed type table. + * On malloc failure, *argtable is set to NULL. + */ +static void +build_arg_table(struct typetable *types, va_list ap, union arg **argtable) +{ + u_int n; + + if (types->tablemax >= STATIC_ARG_TBL_SIZE) { + *argtable = (union arg *) + malloc (sizeof(union arg) * (types->tablemax + 1)); + if (*argtable == NULL) + return; + } + + (*argtable) [0].intarg = 0; + for (n = 1; n <= types->tablemax; n++) { + switch (types->table[n]) { + case T_UNUSED: /* whoops! */ + (*argtable) [n].intarg = va_arg (ap, int); + break; + case TP_SCHAR: + (*argtable) [n].pschararg = va_arg (ap, signed char *); + break; + case TP_SHORT: + (*argtable) [n].pshortarg = va_arg (ap, short *); + break; + case T_INT: + (*argtable) [n].intarg = va_arg (ap, int); + break; + case T_U_INT: + (*argtable) [n].uintarg = va_arg (ap, unsigned int); + break; + case TP_INT: + (*argtable) [n].pintarg = va_arg (ap, int *); + break; + case T_LONG: + (*argtable) [n].longarg = va_arg (ap, long); + break; + case T_U_LONG: + (*argtable) [n].ulongarg = va_arg (ap, unsigned long); + break; + case TP_LONG: + (*argtable) [n].plongarg = va_arg (ap, long *); + break; + case T_LLONG: + (*argtable) [n].longlongarg = va_arg (ap, long long); + break; + case T_U_LLONG: + (*argtable) [n].ulonglongarg = va_arg (ap, unsigned long long); + break; + case TP_LLONG: + (*argtable) [n].plonglongarg = va_arg (ap, long long *); + break; + case T_PTRDIFFT: + (*argtable) [n].ptrdiffarg = va_arg (ap, ptrdiff_t); + break; + case TP_PTRDIFFT: + (*argtable) [n].pptrdiffarg = va_arg (ap, ptrdiff_t *); + break; + case T_SIZET: + (*argtable) [n].sizearg = va_arg (ap, size_t); + break; + case T_SSIZET: + (*argtable) [n].sizearg = va_arg (ap, ssize_t); + break; + case TP_SSIZET: + (*argtable) [n].pssizearg = va_arg (ap, ssize_t *); + break; + case T_INTMAXT: + (*argtable) [n].intmaxarg = va_arg (ap, intmax_t); + break; + case T_UINTMAXT: + (*argtable) [n].uintmaxarg = va_arg (ap, uintmax_t); + break; + case TP_INTMAXT: + (*argtable) [n].pintmaxarg = va_arg (ap, intmax_t *); + break; + case T_INT64T: + (*argtable) [n].intmaxarg = va_arg (ap, int64_t); + break; + case T_UINT64T: + (*argtable) [n].uintmaxarg = va_arg (ap, uint64_t); + break; + case T_DOUBLE: +#ifndef NO_FLOATING_POINT + (*argtable) [n].doublearg = va_arg (ap, double); +#endif + break; + case T_LONG_DOUBLE: +#ifndef NO_FLOATING_POINT + (*argtable) [n].longdoublearg = va_arg (ap, long double); +#endif + break; + case TP_CHAR: + (*argtable) [n].pchararg = va_arg (ap, char *); + break; + case TP_VOID: + (*argtable) [n].pvoidarg = va_arg (ap, void *); + break; + case T_WINT: + (*argtable) [n].wintarg = va_arg (ap, wint_t); + break; + case TP_WCHAR: + (*argtable) [n].pwchararg = va_arg (ap, wchar_t *); + break; + } + } +} diff --git a/lib/printf/printfcommon.h b/lib/printf/printfcommon.h new file mode 100644 index 0000000000..5c45520b4c --- /dev/null +++ b/lib/printf/printfcommon.h @@ -0,0 +1,244 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * Copyright (c) 2011 The FreeBSD Foundation + * All rights reserved. + * Portions of this software were developed by David Chisnall + * under sponsorship from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * This file defines common routines used by both printf and wprintf. + * You must define CHAR to either char or wchar_t prior to including this. + */ + + +static CHAR *__ujtoa(uintmax_t, CHAR *, int, int, const char *); +static CHAR *__ultoa(u_long, CHAR *, int, int, const char *); + +#define NIOV 8 +struct io_state { + struct fbuf *cb; + size_t avail; +}; + +static inline void +io_init(struct io_state *iop, struct fbuf *cb) +{ + iop->cb = cb; + iop->avail = cb ? cb->len - (cb->pos - cb->buf) : 0; +} + +/* + * WARNING: The buffer passed to io_print() is not copied immediately; it must + * remain valid until io_flush() is called. + */ +static inline int +io_print(struct io_state *iop, const CHAR * __restrict ptr, size_t len) +{ + size_t copylen = len; + + if (!iop->cb) + return 0; + if (iop->avail < copylen) + copylen = iop->avail; + + memcpy(iop->cb->pos, ptr, copylen); + iop->avail -= copylen; + iop->cb->pos += copylen; + return 0; +} + +/* + * Choose PADSIZE to trade efficiency vs. size. If larger printf + * fields occur frequently, increase PADSIZE and make the initialisers + * below longer. + */ +#define PADSIZE 16 /* pad chunk size */ +static const CHAR blanks[PADSIZE] = +{' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}; +static const CHAR zeroes[PADSIZE] = +{'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'}; + +/* + * Pad with blanks or zeroes. 'with' should point to either the blanks array + * or the zeroes array. + */ +static inline int +io_pad(struct io_state *iop, int howmany, const CHAR * __restrict with) +{ + int n; + + while (howmany > 0) { + n = (howmany >= PADSIZE) ? PADSIZE : howmany; + if (io_print(iop, with, n)) + return (-1); + howmany -= n; + } + return (0); +} + +/* + * Print exactly len characters of the string spanning p to ep, truncating + * or padding with 'with' as necessary. + */ +static inline int +io_printandpad(struct io_state *iop, const CHAR *p, const CHAR *ep, + int len, const CHAR * __restrict with) +{ + int p_len; + + p_len = ep - p; + if (p_len > len) + p_len = len; + if (p_len > 0) { + if (io_print(iop, p, p_len)) + return (-1); + } else { + p_len = 0; + } + return (io_pad(iop, len - p_len, with)); +} + +/* + * Convert an unsigned long to ASCII for printf purposes, returning + * a pointer to the first character of the string representation. + * Octal numbers can be forced to have a leading zero; hex numbers + * use the given digits. + */ +static CHAR * +__ultoa(u_long val, CHAR *endp, int base, int octzero, const char *xdigs) +{ + CHAR *cp = endp; + long sval; + + /* + * Handle the three cases separately, in the hope of getting + * better/faster code. + */ + switch (base) { + case 10: + if (val < 10) { /* many numbers are 1 digit */ + *--cp = to_char(val); + return (cp); + } + /* + * On many machines, unsigned arithmetic is harder than + * signed arithmetic, so we do at most one unsigned mod and + * divide; this is sufficient to reduce the range of + * the incoming value to where signed arithmetic works. + */ + if (val > LONG_MAX) { + *--cp = to_char(val % 10); + sval = val / 10; + } else + sval = val; + do { + *--cp = to_char(sval % 10); + sval /= 10; + } while (sval != 0); + break; + + case 8: + do { + *--cp = to_char(val & 7); + val >>= 3; + } while (val); + if (octzero && *cp != '0') + *--cp = '0'; + break; + + case 16: + do { + *--cp = xdigs[val & 15]; + val >>= 4; + } while (val); + break; + + default: /* oops */ + abort(); + } + return (cp); +} + +/* Identical to __ultoa, but for intmax_t. */ +static CHAR * +__ujtoa(uintmax_t val, CHAR *endp, int base, int octzero, const char *xdigs) +{ + CHAR *cp = endp; + intmax_t sval; + + /* quick test for small values; __ultoa is typically much faster */ + /* (perhaps instead we should run until small, then call __ultoa?) */ + if (val <= ULONG_MAX) + return (__ultoa((u_long)val, endp, base, octzero, xdigs)); + switch (base) { + case 10: + if (val < 10) { + *--cp = to_char(val % 10); + return (cp); + } + if (val > INTMAX_MAX) { + *--cp = to_char(val % 10); + sval = val / 10; + } else + sval = val; + do { + *--cp = to_char(sval % 10); + sval /= 10; + } while (sval != 0); + break; + + case 8: + do { + *--cp = to_char(val & 7); + val >>= 3; + } while (val); + if (octzero && *cp != '0') + *--cp = '0'; + break; + + case 16: + do { + *--cp = xdigs[val & 15]; + val >>= 4; + } while (val); + break; + + default: + abort(); + } + return (cp); +} diff --git a/lib/printf/printflocal.h b/lib/printf/printflocal.h new file mode 100644 index 0000000000..335e09872e --- /dev/null +++ b/lib/printf/printflocal.h @@ -0,0 +1,105 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include "compiler.h" +#include "printfrr.h" + +/* + * Flags used during conversion. + */ +#define ALT 0x001 /* alternate form */ +#define LADJUST 0x004 /* left adjustment */ +#define LONGDBL 0x008 /* long double */ +#define LONGINT 0x010 /* long integer */ +#define LLONGINT 0x020 /* long long integer */ +#define SHORTINT 0x040 /* short integer */ +#define ZEROPAD 0x080 /* zero (as opposed to blank) pad */ +#define FPT 0x100 /* Floating point number */ +#define GROUPING 0x200 /* use grouping ("'" flag) */ + /* C99 additional size modifiers: */ +#define SIZET 0x400 /* size_t */ +#define PTRDIFFT 0x800 /* ptrdiff_t */ +#define INTMAXT 0x1000 /* intmax_t */ +#define CHARINT 0x2000 /* print char using int format */ + +/* + * Macros for converting digits to letters and vice versa + */ +#define to_digit(c) ((c) - '0') +#define is_digit(c) ((unsigned)to_digit(c) <= 9) +#define to_char(n) ((n) + '0') + +/* Size of the static argument table. */ +#define STATIC_ARG_TBL_SIZE 8 + +union arg { + int intarg; + u_int uintarg; + long longarg; + u_long ulongarg; + long long longlongarg; + unsigned long long ulonglongarg; + ptrdiff_t ptrdiffarg; + size_t sizearg; + intmax_t intmaxarg; + uintmax_t uintmaxarg; + void *pvoidarg; + char *pchararg; + signed char *pschararg; + short *pshortarg; + int *pintarg; + long *plongarg; + long long *plonglongarg; + ptrdiff_t *pptrdiffarg; + ssize_t *pssizearg; + intmax_t *pintmaxarg; +#ifndef NO_FLOATING_POINT + double doublearg; + long double longdoublearg; +#endif + wint_t wintarg; + wchar_t *pwchararg; +}; + +/* Handle positional parameters. */ +int _frr_find_arguments(const char *, va_list, union arg **) DSO_LOCAL; +#ifdef WCHAR_SUPPORT +int _frr_find_warguments(const wchar_t *, va_list, union arg **) DSO_LOCAL; +#endif + +/* returns number of bytes consumed for extended specifier */ +ssize_t printfrr_extp(char *, size_t, const char *, int, const void *) DSO_LOCAL; +ssize_t printfrr_exti(char *, size_t, const char *, int, uintmax_t) DSO_LOCAL; diff --git a/lib/printf/vfprintf.c b/lib/printf/vfprintf.c new file mode 100644 index 0000000000..6ffccb3811 --- /dev/null +++ b/lib/printf/vfprintf.c @@ -0,0 +1,730 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * Copyright (c) 2011 The FreeBSD Foundation + * All rights reserved. + * Portions of this software were developed by David Chisnall + * under sponsorship from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_SYS_CDEFS_H +#include +#endif + +/* + * Actual printf innards. + * + * This code is large and complicated... + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "printflocal.h" + +#define CHAR char +#include "printfcommon.h" + +#ifdef WCHAR_SUPPORT +/* + * Convert a wide character string argument for the %ls format to a multibyte + * string representation. If not -1, prec specifies the maximum number of + * bytes to output, and also means that we can't assume that the wide char. + * string ends is null-terminated. + */ +static char * +__wcsconv(wchar_t *wcsarg, int prec) +{ + static const mbstate_t initial; + mbstate_t mbs; + char buf[MB_LEN_MAX]; + wchar_t *p; + char *convbuf; + size_t clen, nbytes; + + /* Allocate space for the maximum number of bytes we could output. */ + if (prec < 0) { + p = wcsarg; + mbs = initial; + nbytes = wcsrtombs(NULL, (const wchar_t **)&p, 0, &mbs); + if (nbytes == (size_t)-1) + return NULL; + } else { + /* + * Optimisation: if the output precision is small enough, + * just allocate enough memory for the maximum instead of + * scanning the string. + */ + if (prec < 128) + nbytes = prec; + else { + nbytes = 0; + p = wcsarg; + mbs = initial; + for (;;) { + clen = wcrtomb(buf, *p++, &mbs); + if (clen == 0 || clen == (size_t)-1 || + nbytes + clen > (size_t)prec) + break; + nbytes += clen; + } + } + } + if ((convbuf = malloc(nbytes + 1)) == NULL) + return NULL; + + /* Fill the output buffer. */ + p = wcsarg; + mbs = initial; + if ((nbytes = wcsrtombs(convbuf, (const wchar_t **)&p, + nbytes, &mbs)) == (size_t)-1) { + free(convbuf); + return NULL; + } + convbuf[nbytes] = '\0'; + return (convbuf); +} +#endif /* WCHAR_SUPPORT */ + +/* + * The size of the buffer we use as scratch space for integer + * conversions, among other things. We need enough space to + * write a uintmax_t in octal (plus one byte). + */ +#if UINTMAX_MAX <= UINT64_MAX +#define BUF 64 +#else +#error "BUF must be large enough to format a uintmax_t" +#endif + +/* + * Non-MT-safe version + */ +ssize_t +vbprintfrr(struct fbuf *cb, const char *fmt0, va_list ap) +{ + const char *fmt; /* format string */ + int ch; /* character from fmt */ + int n, n2; /* handy integer (short term usage) */ + const char *cp; /* handy char pointer (short term usage) */ + int flags; /* flags as above */ + int ret; /* return value accumulator */ + int width; /* width from format (%8d), or 0 */ + int prec; /* precision from format; <0 for N/A */ + int saved_errno; + char sign; /* sign prefix (' ', '+', '-', or \0) */ + + u_long ulval = 0; /* integer arguments %[diouxX] */ + uintmax_t ujval = 0; /* %j, %ll, %q, %t, %z integers */ + void *ptrval; /* %p */ + int base; /* base for [diouxX] conversion */ + int dprec; /* a copy of prec if [diouxX], 0 otherwise */ + int realsz; /* field size expanded by dprec, sign, etc */ + int size; /* size of converted field or string */ + int prsize; /* max size of printed field */ + const char *xdigs; /* digits for %[xX] conversion */ + struct io_state io; /* I/O buffering state */ + char buf[BUF]; /* buffer with space for digits of uintmax_t */ + char ox[2]; /* space for 0x; ox[1] is either x, X, or \0 */ + union arg *argtable; /* args, built due to positional arg */ + union arg statargtable [STATIC_ARG_TBL_SIZE]; + int nextarg; /* 1-based argument index */ + va_list orgap; /* original argument pointer */ + char *convbuf; /* wide to multibyte conversion result */ + + static const char xdigs_lower[16] = "0123456789abcdef"; + static const char xdigs_upper[16] = "0123456789ABCDEF"; + + /* BEWARE, these `goto error' on error. */ +#define PRINT(ptr, len) { \ + if (io_print(&io, (ptr), (len))) \ + goto error; \ +} +#define PAD(howmany, with) { \ + if (io_pad(&io, (howmany), (with))) \ + goto error; \ +} +#define PRINTANDPAD(p, ep, len, with) { \ + if (io_printandpad(&io, (p), (ep), (len), (with))) \ + goto error; \ +} +#define FLUSH() do { } while (0) + + /* + * Get the argument indexed by nextarg. If the argument table is + * built, use it to get the argument. If its not, get the next + * argument (and arguments must be gotten sequentially). + */ +#define GETARG(type) \ + ((argtable != NULL) ? *((type*)(&argtable[nextarg++])) : \ + (nextarg++, va_arg(ap, type))) + + /* + * To extend shorts properly, we need both signed and unsigned + * argument extraction methods. + */ +#define SARG() \ + (flags&LONGINT ? GETARG(long) : \ + flags&SHORTINT ? (long)(short)GETARG(int) : \ + flags&CHARINT ? (long)(signed char)GETARG(int) : \ + (long)GETARG(int)) +#define UARG() \ + (flags&LONGINT ? GETARG(u_long) : \ + flags&SHORTINT ? (u_long)(u_short)GETARG(int) : \ + flags&CHARINT ? (u_long)(u_char)GETARG(int) : \ + (u_long)GETARG(u_int)) +#define INTMAX_SIZE (INTMAXT|SIZET|PTRDIFFT|LLONGINT|LONGDBL) +#define SJARG() \ + (flags&LONGDBL ? GETARG(int64_t) : \ + flags&INTMAXT ? GETARG(intmax_t) : \ + flags&SIZET ? (intmax_t)GETARG(ssize_t) : \ + flags&PTRDIFFT ? (intmax_t)GETARG(ptrdiff_t) : \ + (intmax_t)GETARG(long long)) +#define UJARG() \ + (flags&LONGDBL ? GETARG(uint64_t) : \ + flags&INTMAXT ? GETARG(uintmax_t) : \ + flags&SIZET ? (uintmax_t)GETARG(size_t) : \ + flags&PTRDIFFT ? (uintmax_t)GETARG(ptrdiff_t) : \ + (uintmax_t)GETARG(unsigned long long)) + + /* + * Get * arguments, including the form *nn$. Preserve the nextarg + * that the argument can be gotten once the type is determined. + */ +#define GETASTER(val) \ + n2 = 0; \ + cp = fmt; \ + while (is_digit(*cp)) { \ + n2 = 10 * n2 + to_digit(*cp); \ + cp++; \ + } \ + if (*cp == '$') { \ + int hold = nextarg; \ + if (argtable == NULL) { \ + argtable = statargtable; \ + if (_frr_find_arguments (fmt0, orgap, &argtable)) { \ + ret = EOF; \ + goto error; \ + } \ + } \ + nextarg = n2; \ + val = GETARG (int); \ + nextarg = hold; \ + fmt = ++cp; \ + } else { \ + val = GETARG (int); \ + } + + xdigs = xdigs_lower; + saved_errno = errno; + convbuf = NULL; + fmt = (char *)fmt0; + argtable = NULL; + nextarg = 1; + va_copy(orgap, ap); + io_init(&io, cb); + ret = 0; + + /* + * Scan the format for conversions (`%' character). + */ + for (;;) { + for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++) + /* void */; + if ((n = fmt - cp) != 0) { + if ((unsigned)ret + n > INT_MAX) { + ret = EOF; + errno = EOVERFLOW; + goto error; + } + PRINT(cp, n); + ret += n; + } + if (ch == '\0') + goto done; + fmt++; /* skip over '%' */ + + flags = 0; + dprec = 0; + width = 0; + prec = -1; + sign = '\0'; + ox[1] = '\0'; + +rflag: ch = *fmt++; +reswitch: switch (ch) { + case ' ': + /*- + * ``If the space and + flags both appear, the space + * flag will be ignored.'' + * -- ANSI X3J11 + */ + if (!sign) + sign = ' '; + goto rflag; + case '#': + flags |= ALT; + goto rflag; + case '*': + /*- + * ``A negative field width argument is taken as a + * - flag followed by a positive field width.'' + * -- ANSI X3J11 + * They don't exclude field widths read from args. + */ + GETASTER (width); + if (width >= 0) + goto rflag; + width = -width; + /* FALLTHROUGH */ + case '-': + flags |= LADJUST; + goto rflag; + case '+': + sign = '+'; + goto rflag; + case '\'': + flags |= GROUPING; + goto rflag; + case '.': + if ((ch = *fmt++) == '*') { + GETASTER (prec); + goto rflag; + } + prec = 0; + while (is_digit(ch)) { + prec = 10 * prec + to_digit(ch); + ch = *fmt++; + } + goto reswitch; + case '0': + /*- + * ``Note that 0 is taken as a flag, not as the + * beginning of a field width.'' + * -- ANSI X3J11 + */ + flags |= ZEROPAD; + goto rflag; + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + n = 0; + do { + n = 10 * n + to_digit(ch); + ch = *fmt++; + } while (is_digit(ch)); + if (ch == '$') { + nextarg = n; + if (argtable == NULL) { + argtable = statargtable; + if (_frr_find_arguments (fmt0, orgap, + &argtable)) { + ret = EOF; + goto error; + } + } + goto rflag; + } + width = n; + goto reswitch; + case 'L': + flags |= LONGDBL; + goto rflag; + case 'h': + if (flags & SHORTINT) { + flags &= ~SHORTINT; + flags |= CHARINT; + } else + flags |= SHORTINT; + goto rflag; + case 'j': + flags |= INTMAXT; + goto rflag; + case 'l': + if (flags & LONGINT) { + flags &= ~LONGINT; + flags |= LLONGINT; + } else + flags |= LONGINT; + goto rflag; + case 'q': + flags |= LLONGINT; /* not necessarily */ + goto rflag; + case 't': + flags |= PTRDIFFT; + goto rflag; + case 'z': + flags |= SIZET; + goto rflag; + case 'C': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'c': +#ifdef WCHAR_SUPPORT + if (flags & LONGINT) { + static const mbstate_t initial; + mbstate_t mbs; + size_t mbseqlen; + + mbs = initial; + mbseqlen = wcrtomb(cp = buf, + (wchar_t)GETARG(wint_t), &mbs); + if (mbseqlen == (size_t)-1) { + goto error; + } + size = (int)mbseqlen; + } else +#endif /* WCHAR_SUPPORT */ + { + buf[0] = GETARG(int); + cp = buf; + size = 1; + } + sign = '\0'; + break; + case 'D': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'd': + case 'i': + if (flags & INTMAX_SIZE) + ujval = SJARG(); + else + ulval = SARG(); + + if (printfrr_ext_char(fmt[0])) { + n2 = printfrr_exti(buf, sizeof(buf), fmt, prec, + (flags & INTMAX_SIZE) ? ujval + : (uintmax_t)ulval); + if (n2 > 0) { + fmt += n2; + cp = buf; + size = strlen(cp); + sign = '\0'; + break; + } + } + if (flags & INTMAX_SIZE) { + if ((intmax_t)ujval < 0) { + ujval = -ujval; + sign = '-'; + } + } else { + if ((long)ulval < 0) { + ulval = -ulval; + sign = '-'; + } + } + base = 10; + goto number; +#ifndef NO_FLOATING_POINT + case 'a': + case 'A': + case 'e': + case 'E': + case 'f': + case 'F': + case 'g': + case 'G': + if (flags & LONGDBL) { + long double arg = GETARG(long double); + char fmt[6] = "%.*L"; + fmt[4] = ch; + fmt[5] = '\0'; + + snprintf(buf, sizeof(buf), fmt, prec, arg); + } else { + double arg = GETARG(double); + char fmt[5] = "%.*"; + fmt[3] = ch; + fmt[4] = '\0'; + + snprintf(buf, sizeof(buf), fmt, prec, arg); + } + cp = buf; + /* for proper padding */ + if (*cp == '-') { + cp++; + sign = '-'; + } + /* "inf" */ + if (!is_digit(*cp) && *cp != '.') + flags &= ~ZEROPAD; + size = strlen(buf); + break; +#endif + case 'm': + cp = strerror(saved_errno); + size = (prec >= 0) ? strnlen(cp, prec) : strlen(cp); + sign = '\0'; + break; + case 'n': + /* + * Assignment-like behavior is specified if the + * value overflows or is otherwise unrepresentable. + * C99 says to use `signed char' for %hhn conversions. + */ + if (flags & LLONGINT) + *GETARG(long long *) = ret; + else if (flags & SIZET) + *GETARG(ssize_t *) = (ssize_t)ret; + else if (flags & PTRDIFFT) + *GETARG(ptrdiff_t *) = ret; + else if (flags & INTMAXT) + *GETARG(intmax_t *) = ret; + else if (flags & LONGINT) + *GETARG(long *) = ret; + else if (flags & SHORTINT) + *GETARG(short *) = ret; + else if (flags & CHARINT) + *GETARG(signed char *) = ret; + else + *GETARG(int *) = ret; + continue; /* no output */ + case 'O': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'o': + if (flags & INTMAX_SIZE) + ujval = UJARG(); + else + ulval = UARG(); + base = 8; + goto nosign; + case 'p': + /*- + * ``The argument shall be a pointer to void. The + * value of the pointer is converted to a sequence + * of printable characters, in an implementation- + * defined manner.'' + * -- ANSI X3J11 + */ + ptrval = GETARG(void *); + if (printfrr_ext_char(fmt[0]) && + (n2 = printfrr_extp(buf, sizeof(buf), + fmt, prec, ptrval)) > 0) { + fmt += n2; + cp = buf; + size = strlen(cp); + sign = '\0'; + break; + } + ujval = (uintmax_t)(uintptr_t)ptrval; + base = 16; + xdigs = xdigs_lower; + flags = flags | INTMAXT; + ox[1] = 'x'; + goto nosign; + case 'S': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 's': +#ifdef WCHAR_SUPPORT + if (flags & LONGINT) { + wchar_t *wcp; + + if (convbuf != NULL) + free(convbuf); + if ((wcp = GETARG(wchar_t *)) == NULL) + cp = "(null)"; + else { + convbuf = __wcsconv(wcp, prec); + if (convbuf == NULL) { + goto error; + } + cp = convbuf; + } + } else +#endif + if ((cp = GETARG(char *)) == NULL) + cp = "(null)"; + size = (prec >= 0) ? strnlen(cp, prec) : strlen(cp); + sign = '\0'; + break; + case 'U': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'u': + if (flags & INTMAX_SIZE) + ujval = UJARG(); + else + ulval = UARG(); + base = 10; + goto nosign; + case 'X': + xdigs = xdigs_upper; + goto hex; + case 'x': + xdigs = xdigs_lower; +hex: + if (flags & INTMAX_SIZE) + ujval = UJARG(); + else + ulval = UARG(); + base = 16; + /* leading 0x/X only if non-zero */ + if (flags & ALT && + (flags & INTMAX_SIZE ? ujval != 0 : ulval != 0)) + ox[1] = ch; + + flags &= ~GROUPING; + /* unsigned conversions */ +nosign: sign = '\0'; + /*- + * ``... diouXx conversions ... if a precision is + * specified, the 0 flag will be ignored.'' + * -- ANSI X3J11 + */ +number: if ((dprec = prec) >= 0) + flags &= ~ZEROPAD; + + /*- + * ``The result of converting a zero value with an + * explicit precision of zero is no characters.'' + * -- ANSI X3J11 + * + * ``The C Standard is clear enough as is. The call + * printf("%#.0o", 0) should print 0.'' + * -- Defect Report #151 + */ + cp = buf + BUF; + if (flags & INTMAX_SIZE) { + if (ujval != 0 || prec != 0 || + (flags & ALT && base == 8)) + cp = __ujtoa(ujval, buf + BUF, base, + flags & ALT, xdigs); + } else { + if (ulval != 0 || prec != 0 || + (flags & ALT && base == 8)) + cp = __ultoa(ulval, buf + BUF, base, + flags & ALT, xdigs); + } + size = buf + BUF - cp; + if (size > BUF) /* should never happen */ + abort(); + break; + default: /* "%?" prints ?, unless ? is NUL */ + if (ch == '\0') + goto done; + /* pretend it was %c with argument ch */ + buf[0] = ch; + cp = buf; + size = 1; + sign = '\0'; + break; + } + + /* + * All reasonable formats wind up here. At this point, `cp' + * points to a string which (if not flags&LADJUST) should be + * padded out to `width' places. If flags&ZEROPAD, it should + * first be prefixed by any sign or other prefix; otherwise, + * it should be blank padded before the prefix is emitted. + * After any left-hand padding and prefixing, emit zeroes + * required by a decimal [diouxX] precision, then print the + * string proper, then emit zeroes required by any leftover + * floating precision; finally, if LADJUST, pad with blanks. + * + * Compute actual size, so we know how much to pad. + * size excludes decimal prec; realsz includes it. + */ + realsz = dprec > size ? dprec : size; + if (sign) + realsz++; + if (ox[1]) + realsz += 2; + + prsize = width > realsz ? width : realsz; + if ((unsigned)ret + prsize > INT_MAX) { + ret = EOF; + errno = EOVERFLOW; + goto error; + } + + /* right-adjusting blank padding */ + if ((flags & (LADJUST|ZEROPAD)) == 0) + PAD(width - realsz, blanks); + + /* prefix */ + if (sign) + PRINT(&sign, 1); + + if (ox[1]) { /* ox[1] is either x, X, or \0 */ + ox[0] = '0'; + PRINT(ox, 2); + } + + /* right-adjusting zero padding */ + if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD) + PAD(width - realsz, zeroes); + + /* the string or number proper */ + /* leading zeroes from decimal precision */ + PAD(dprec - size, zeroes); + PRINT(cp, size); + /* left-adjusting padding (always blank) */ + if (flags & LADJUST) + PAD(width - realsz, blanks); + + /* finally, adjust ret */ + ret += prsize; + + FLUSH(); /* copy out the I/O vectors */ + } +done: + FLUSH(); +error: + va_end(orgap); + if (convbuf != NULL) + free(convbuf); + if ((argtable != NULL) && (argtable != statargtable)) + free (argtable); + return (ret); + /* NOTREACHED */ +} + diff --git a/lib/printfrr.h b/lib/printfrr.h new file mode 100644 index 0000000000..a775e1517b --- /dev/null +++ b/lib/printfrr.h @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2019 David Lamparter, for NetDEF, Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _FRR_PRINTFRR_H +#define _FRR_PRINTFRR_H + +#include +#include +#include + +#include "compiler.h" +#include "memory.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct fbuf { + char *buf; + char *pos; + size_t len; +}; + +#define at(a, b) PRINTFRR(a, b) +#define atn(a, b) \ + at(a, b) __attribute__((nonnull(1) _RET_NONNULL)) +#define atm(a, b) \ + atn(a, b) __attribute__((malloc)) + +/* return value is length needed for the full string (excluding \0) in all + * cases. The functions write as much as they can, but continue regardless, + * so the return value is independent of buffer length. Both bprintfrr and + * snprintf also accept NULL as output buffer. + */ + +/* bprintfrr does NOT null terminate! use sparingly (only provided since it's + * the most direct interface) - useful for incrementally building long text + * (call bprintfrr repeatedly with the same buffer) + */ +ssize_t vbprintfrr(struct fbuf *out, const char *fmt, va_list) at(2, 0); +ssize_t bprintfrr(struct fbuf *out, const char *fmt, ...) at(2, 3); + +/* these do null terminate like their snprintf cousins */ +ssize_t vsnprintfrr(char *out, size_t sz, const char *fmt, va_list) at(3, 0); +ssize_t snprintfrr(char *out, size_t sz, const char *fmt, ...) at(3, 4); + +/* c = continue / concatenate (append at the end of the string) + * return value is would-be string length (regardless of buffer length), + * i.e. includes already written chars */ +ssize_t vcsnprintfrr(char *out, size_t sz, const char *fmt, va_list) at(3, 0); +ssize_t csnprintfrr(char *out, size_t sz, const char *fmt, ...) at(3, 4); + +/* memory allocations don't fail in FRR, so you always get something here. + * (in case of error, returns a strdup of the format string) */ +char *vasprintfrr(struct memtype *mt, const char *fmt, va_list) atm(2, 0); +char *asprintfrr(struct memtype *mt, const char *fmt, ...) atm(2, 3); + +/* try to use provided buffer (presumably from stack), allocate if it's too + * short. Must call XFREE(mt, return value) if return value != out. + */ +char *vasnprintfrr(struct memtype *mt, char *out, size_t sz, + const char *fmt, va_list) atn(4, 0); +char *asnprintfrr(struct memtype *mt, char *out, size_t sz, + const char *fmt, ...) atn(4, 5); + +#define printfrr(fmt, ...) \ + do { \ + char buf[256], *out; \ + out = asnprintfrr(MTYPE_TMP, buf, sizeof(buf), fmt, \ + ##__VA_ARGS__); \ + fputs(out, stdout); \ + if (out != buf) \ + XFREE(MTYPE_TMP, out); \ + } while (0) + +#undef at +#undef atm +#undef atn + +/* extension specs must start with a capital letter (this is a restriction + * for both performance's and human understanding's sake.) + * + * Note that the entire thing mostly works because a letter directly following + * a %p print specifier is extremely unlikely to occur (why would you want to + * print "0x12345678HELLO"?) Normally, you'd expect spacing or punctuation + * after a placeholder. That also means that neither of those works well for + * extension purposes, e.g. "%p{foo}" is reasonable to see actually used. + * + * TODO: would be nice to support a "%pF%dF" specifier that consumes 2 + * arguments, e.g. to pass an integer + a list of known values... can be + * done, but a bit tricky. + */ +#define printfrr_ext_char(ch) ((ch) >= 'A' && (ch) <= 'Z') + +struct printfrr_ext { + /* embedded string to minimize cache line pollution */ + char match[8]; + + /* both can be given, if not the code continues searching + * (you can do %pX and %dX in 2 different entries) + * + * return value: number of bytes consumed from the format string, so + * you can consume extra flags (e.g. register for "%pX", consume + * "%pXfoo" or "%pXbar" for flags.) Convention is to make those flags + * lowercase letters or numbers. + * + * bsz is a compile-time constant in printf; it's gonna be relatively + * small. This isn't designed to print Shakespeare from a pointer. + * + * prec is the precision specifier (the 999 in "%.999p") -1 means + * none given (value in the format string cannot be negative) + */ + ssize_t (*print_ptr)(char *buf, size_t bsz, const char *fmt, int prec, + const void *); + ssize_t (*print_int)(char *buf, size_t bsz, const char *fmt, int prec, + uintmax_t); +}; + +/* no locking - must be called when single threaded (e.g. at startup.) + * this restriction hopefully won't be a huge bother considering normal usage + * scenarios... + */ +void printfrr_ext_reg(const struct printfrr_ext *); + +#define printfrr_ext_autoreg_p(matchs, print_fn) \ + static ssize_t print_fn(char *, size_t, const char *, int, \ + const void *); \ + static const struct printfrr_ext _printext_##print_fn = { \ + .match = matchs, \ + .print_ptr = print_fn, \ + }; \ + static void _printreg_##print_fn(void) __attribute__((constructor)); \ + static void _printreg_##print_fn(void) { \ + printfrr_ext_reg(&_printext_##print_fn); \ + } \ + /* end */ + +#define printfrr_ext_autoreg_i(matchs, print_fn) \ + static ssize_t print_fn(char *, size_t, const char *, int, uintmax_t); \ + static const struct printfrr_ext _printext_##print_fn = { \ + .match = matchs, \ + .print_int = print_fn, \ + }; \ + static void _printreg_##print_fn(void) __attribute__((constructor)); \ + static void _printreg_##print_fn(void) { \ + printfrr_ext_reg(&_printext_##print_fn); \ + } \ + /* end */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/privs.c b/lib/privs.c index a3314c6c3c..dc43b7279d 100644 --- a/lib/privs.c +++ b/lib/privs.c @@ -24,6 +24,7 @@ #include "log.h" #include "privs.h" #include "memory.h" +#include "frr_pthread.h" #include "lib_errors.h" #include "lib/queue.h" @@ -405,9 +406,11 @@ static void zprivs_caps_init(struct zebra_privs_t *zprivs) static void zprivs_caps_terminate(void) { - /* clear all capabilities */ + /* Clear all capabilities, if we have any. */ if (zprivs_state.caps) cap_clear(zprivs_state.caps); + else + return; /* and boom, capabilities are gone forever */ if (cap_set_proc(zprivs_state.caps)) { @@ -555,8 +558,7 @@ static void zprivs_caps_init(struct zebra_privs_t *zprivs) /* nonsensical to have gotten here but not have capabilities */ if (!zprivs_state.syscaps_p) { fprintf(stderr, - "%s: capabilities enabled, " - "but no valid capabilities supplied\n", + "%s: capabilities enabled, but no valid capabilities supplied\n", __func__); } @@ -760,8 +762,7 @@ struct zebra_privs_t *_zprivs_raise(struct zebra_privs_t *privs, * Serialize 'raise' operations; particularly important for * OSes where privs are process-wide. */ - pthread_mutex_lock(&(privs->mutex)); - { + frr_with_mutex(&(privs->mutex)) { /* Locate ref-counting object to use */ refs = get_privs_refs(privs); @@ -775,7 +776,6 @@ struct zebra_privs_t *_zprivs_raise(struct zebra_privs_t *privs, refs->raised_in_funcname = funcname; } } - pthread_mutex_unlock(&(privs->mutex)); return privs; } @@ -791,8 +791,7 @@ void _zprivs_lower(struct zebra_privs_t **privs) /* Serialize 'lower privs' operation - particularly important * when OS privs are process-wide. */ - pthread_mutex_lock(&(*privs)->mutex); - { + frr_with_mutex(&(*privs)->mutex) { refs = get_privs_refs(*privs); if (--(refs->refcount) == 0) { @@ -806,7 +805,6 @@ void _zprivs_lower(struct zebra_privs_t **privs) refs->raised_in_funcname = NULL; } } - pthread_mutex_unlock(&(*privs)->mutex); *privs = NULL; } @@ -1022,11 +1020,11 @@ void zprivs_get_ids(struct zprivs_ids_t *ids) ids->uid_priv = getuid(); (zprivs_state.zuid) ? (ids->uid_normal = zprivs_state.zuid) - : (ids->uid_normal = -1); + : (ids->uid_normal = (uid_t)-1); (zprivs_state.zgid) ? (ids->gid_normal = zprivs_state.zgid) - : (ids->gid_normal = -1); + : (ids->gid_normal = (uid_t)-1); (zprivs_state.vtygrp) ? (ids->gid_vty = zprivs_state.vtygrp) - : (ids->gid_vty = -1); + : (ids->gid_vty = (uid_t)-1); return; } diff --git a/lib/privs.h b/lib/privs.h index 2b0b44b3f2..18ba8e8888 100644 --- a/lib/privs.h +++ b/lib/privs.h @@ -24,6 +24,7 @@ #define _ZEBRA_PRIVS_H #include +#include #include "lib/queue.h" #ifdef __cplusplus @@ -109,16 +110,16 @@ extern void zprivs_get_ids(struct zprivs_ids_t *); /* * Wrapper around zprivs, to be used as: - * frr_elevate_privs(&privs) { + * frr_with_privs(&privs) { * ... code ... * if (error) * break; -- break can be used to get out of the block * ... code ... * } * - * The argument to frr_elevate_privs() can be NULL to leave privileges as-is + * The argument to frr_with_privs() can be NULL to leave privileges as-is * (mostly useful for conditional privilege-raising, i.e.:) - * frr_elevate_privs(cond ? &privs : NULL) {} + * frr_with_privs(cond ? &privs : NULL) {} * * NB: The code block is always executed, regardless of whether privileges * could be raised or not, or whether NULL was given or not. This is fully @@ -138,7 +139,7 @@ extern struct zebra_privs_t *_zprivs_raise(struct zebra_privs_t *privs, const char *funcname); extern void _zprivs_lower(struct zebra_privs_t **privs); -#define frr_elevate_privs(privs) \ +#define frr_with_privs(privs) \ for (struct zebra_privs_t *_once = NULL, \ *_privs __attribute__( \ (unused, cleanup(_zprivs_lower))) = \ diff --git a/lib/ptm_lib.c b/lib/ptm_lib.c index 7f868beda4..e00dd54290 100644 --- a/lib/ptm_lib.c +++ b/lib/ptm_lib.c @@ -65,10 +65,10 @@ static csv_record_t *_ptm_lib_encode_header(csv_t *csv, csv_record_t *rec, char client_buf[32]; csv_record_t *rec1; - sprintf(msglen_buf, "%4d", msglen); - sprintf(vers_buf, "%4d", version); - sprintf(type_buf, "%4d", type); - sprintf(cmdid_buf, "%4d", cmd_id); + snprintf(msglen_buf, sizeof(msglen_buf), "%4d", msglen); + snprintf(vers_buf, sizeof(vers_buf), "%4d", version); + snprintf(type_buf, sizeof(type_buf), "%4d", type); + snprintf(cmdid_buf, sizeof(cmdid_buf), "%4d", cmd_id); snprintf(client_buf, 17, "%16.16s", client_name); if (rec) { rec1 = csv_encode_record(csv, rec, 5, msglen_buf, vers_buf, @@ -92,47 +92,47 @@ static int _ptm_lib_decode_header(csv_t *csv, int *msglen, int *version, rec = csv_record_iter(csv); if (rec == NULL) { DLOG("malformed CSV\n"); - return (-1); + return -1; } hdr = csv_field_iter(rec, &fld); if (hdr == NULL) { DLOG("malformed CSV\n"); - return (-1); + return -1; } *msglen = atoi(hdr); hdr = csv_field_iter_next(&fld); if (hdr == NULL) { DLOG("malformed CSV\n"); - return (-1); + return -1; } *version = atoi(hdr); hdr = csv_field_iter_next(&fld); if (hdr == NULL) { DLOG("malformed CSV\n"); - return (-1); + return -1; } *type = atoi(hdr); hdr = csv_field_iter_next(&fld); if (hdr == NULL) { DLOG("malformed CSV\n"); - return (-1); + return -1; } *cmd_id = atoi(hdr); hdr = csv_field_iter_next(&fld); if (hdr == NULL) { DLOG("malformed CSV\n"); - return (-1); + return -1; } /* remove leading spaces */ for (i = j = 0; i < csv_field_len(fld); i++) { - if (!isspace((int)hdr[i])) { + if (!isspace((unsigned char)hdr[i])) { client_name[j] = hdr[i]; j++; } } client_name[j] = '\0'; - return (0); + return 0; } int ptm_lib_append_msg(ptm_lib_handle_t *hdl, void *ctxt, const char *key, @@ -143,7 +143,7 @@ int ptm_lib_append_msg(ptm_lib_handle_t *hdl, void *ctxt, const char *key, csv_record_t *mh_rec, *rec; if (!p_ctxt) { - ERRLOG("%s: no context \n", __FUNCTION__); + ERRLOG("%s: no context \n", __func__); return -1; } @@ -154,7 +154,7 @@ int ptm_lib_append_msg(ptm_lib_handle_t *hdl, void *ctxt, const char *key, /* append to the hdr record */ rec = csv_append_record(csv, rec, 1, key); if (!rec) { - ERRLOG("%s: Could not append key \n", __FUNCTION__); + ERRLOG("%s: Could not append key \n", __func__); return -1; } @@ -162,7 +162,7 @@ int ptm_lib_append_msg(ptm_lib_handle_t *hdl, void *ctxt, const char *key, /* append to the data record */ rec = csv_append_record(csv, rec, 1, val); if (!rec) { - ERRLOG("%s: Could not append val \n", __FUNCTION__); + ERRLOG("%s: Could not append val \n", __func__); return -1; } @@ -186,7 +186,7 @@ int ptm_lib_init_msg(ptm_lib_handle_t *hdl, int cmd_id, int type, void *in_ctxt, csv = csv_init(NULL, NULL, PTMLIB_MSG_SZ); if (!csv) { - ERRLOG("%s: Could not allocate csv \n", __FUNCTION__); + ERRLOG("%s: Could not allocate csv \n", __func__); return -1; } @@ -194,7 +194,7 @@ int ptm_lib_init_msg(ptm_lib_handle_t *hdl, int cmd_id, int type, void *in_ctxt, cmd_id, hdl->client_name); if (!rec) { - ERRLOG("%s: Could not allocate record \n", __FUNCTION__); + ERRLOG("%s: Could not allocate record \n", __func__); csv_clean(csv); csv_free(csv); return -1; @@ -202,7 +202,7 @@ int ptm_lib_init_msg(ptm_lib_handle_t *hdl, int cmd_id, int type, void *in_ctxt, p_ctxt = calloc(1, sizeof(*p_ctxt)); if (!p_ctxt) { - ERRLOG("%s: Could not allocate context \n", __FUNCTION__); + ERRLOG("%s: Could not allocate context \n", __func__); csv_clean(csv); csv_free(csv); return -1; @@ -234,7 +234,7 @@ int ptm_lib_cleanup_msg(ptm_lib_handle_t *hdl, void *ctxt) csv_t *csv; if (!p_ctxt) { - ERRLOG("%s: no context \n", __FUNCTION__); + ERRLOG("%s: no context \n", __func__); return -1; } @@ -254,7 +254,7 @@ int ptm_lib_complete_msg(ptm_lib_handle_t *hdl, void *ctxt, char *buf, int *len) csv_record_t *rec; if (!p_ctxt) { - ERRLOG("%s: no context \n", __FUNCTION__); + ERRLOG("%s: no context \n", __func__); return -1; } @@ -268,7 +268,7 @@ int ptm_lib_complete_msg(ptm_lib_handle_t *hdl, void *ctxt, char *buf, int *len) /* parse csv contents into string */ if (buf && len) { if (csv_serialize(csv, buf, *len)) { - ERRLOG("%s: cannot serialize\n", __FUNCTION__); + ERRLOG("%s: cannot serialize\n", __func__); return -1; } *len = csvlen(csv); @@ -364,7 +364,7 @@ int ptm_lib_process_msg(ptm_lib_handle_t *hdl, int fd, char *inbuf, int inlen, if (!csv) { DLOG("Cannot allocate csv for hdr\n"); - return (-1); + return -1; } rc = _ptm_lib_decode_header(csv, &msglen, &ver, &type, &cmd_id, @@ -390,14 +390,14 @@ int ptm_lib_process_msg(ptm_lib_handle_t *hdl, int fd, char *inbuf, int inlen, /* we only support the get-status cmd */ if (strcmp(inbuf, PTMLIB_CMD_GET_STATUS)) { DLOG("unsupported legacy cmd %s\n", inbuf); - return (-1); + return -1; } /* internally create a csv-style cmd */ ptm_lib_init_msg(hdl, 0, PTMLIB_MSG_TYPE_CMD, NULL, (void *)&p_ctxt); if (!p_ctxt) { DLOG("couldnt allocate context\n"); - return (-1); + return -1; } ptm_lib_append_msg(hdl, p_ctxt, "cmd", PTMLIB_CMD_GET_STATUS); @@ -425,8 +425,7 @@ int ptm_lib_process_msg(ptm_lib_handle_t *hdl, int fd, char *inbuf, int inlen, csv_decode(csv, inbuf); p_ctxt = calloc(1, sizeof(*p_ctxt)); if (!p_ctxt) { - ERRLOG("%s: Could not allocate context \n", - __FUNCTION__); + ERRLOG("%s: Could not allocate context \n", __func__); csv_clean(csv); csv_free(csv); return -1; diff --git a/lib/pullwr.c b/lib/pullwr.c new file mode 100644 index 0000000000..0c326f29d4 --- /dev/null +++ b/lib/pullwr.c @@ -0,0 +1,275 @@ +/* + * Pull-driven write event handler + * Copyright (C) 2019 David Lamparter + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "zebra.h" + +#include "pullwr.h" +#include "memory.h" +#include "monotime.h" + +/* defaults */ +#define PULLWR_THRESH 16384 /* size at which we start to call write() */ +#define PULLWR_MAXSPIN 2500 /* max µs to spend grabbing more data */ + +struct pullwr { + int fd; + struct thread_master *tm; + /* writer == NULL <=> we're idle */ + struct thread *writer; + + void *arg; + void (*fill)(void *, struct pullwr *); + void (*err)(void *, struct pullwr *, bool); + + /* ring buffer (although it's "un-ringed" on resizing, it WILL wrap + * around if data is trickling in while keeping it at a constant size) + */ + size_t bufsz, valid, pos; + uint64_t total_written; + char *buffer; + + size_t thresh; /* PULLWR_THRESH */ + int64_t maxspin; /* PULLWR_MAXSPIN */ +}; + +DEFINE_MTYPE_STATIC(LIB, PULLWR_HEAD, "pull-driven write controller") +DEFINE_MTYPE_STATIC(LIB, PULLWR_BUF, "pull-driven write buffer") + +static int pullwr_run(struct thread *t); + +struct pullwr *_pullwr_new(struct thread_master *tm, int fd, + void *arg, + void (*fill)(void *, struct pullwr *), + void (*err)(void *, struct pullwr *, bool)) +{ + struct pullwr *pullwr; + + pullwr = XCALLOC(MTYPE_PULLWR_HEAD, sizeof(*pullwr)); + pullwr->fd = fd; + pullwr->tm = tm; + pullwr->arg = arg; + pullwr->fill = fill; + pullwr->err = err; + + pullwr->thresh = PULLWR_THRESH; + pullwr->maxspin = PULLWR_MAXSPIN; + + return pullwr; +} + +void pullwr_del(struct pullwr *pullwr) +{ + THREAD_OFF(pullwr->writer); + + XFREE(MTYPE_PULLWR_BUF, pullwr->buffer); + XFREE(MTYPE_PULLWR_HEAD, pullwr); +} + +void pullwr_cfg(struct pullwr *pullwr, int64_t max_spin_usec, + size_t write_threshold) +{ + pullwr->maxspin = max_spin_usec ?: PULLWR_MAXSPIN; + pullwr->thresh = write_threshold ?: PULLWR_THRESH; +} + +void pullwr_bump(struct pullwr *pullwr) +{ + if (pullwr->writer) + return; + + thread_add_timer(pullwr->tm, pullwr_run, pullwr, 0, &pullwr->writer); +} + +static size_t pullwr_iov(struct pullwr *pullwr, struct iovec *iov) +{ + size_t len1; + + if (pullwr->valid == 0) + return 0; + + if (pullwr->pos + pullwr->valid <= pullwr->bufsz) { + iov[0].iov_base = pullwr->buffer + pullwr->pos; + iov[0].iov_len = pullwr->valid; + return 1; + } + + len1 = pullwr->bufsz - pullwr->pos; + + iov[0].iov_base = pullwr->buffer + pullwr->pos; + iov[0].iov_len = len1; + iov[1].iov_base = pullwr->buffer; + iov[1].iov_len = pullwr->valid - len1; + return 2; +} + +static void pullwr_resize(struct pullwr *pullwr, size_t need) +{ + struct iovec iov[2]; + size_t niov, newsize; + char *newbuf; + + /* the buffer is maintained at pullwr->thresh * 2 since we'll be + * trying to fill it as long as it's anywhere below pullwr->thresh. + * That means we frequently end up a little short of it and then write + * something that goes over the threshold. So, just use double. + */ + if (need) { + /* resize up */ + if (pullwr->bufsz - pullwr->valid >= need) + return; + + newsize = MAX((pullwr->valid + need) * 2, pullwr->thresh * 2); + newbuf = XMALLOC(MTYPE_PULLWR_BUF, newsize); + } else if (!pullwr->valid) { + /* resize down, buffer empty */ + newsize = 0; + newbuf = NULL; + } else { + /* resize down */ + if (pullwr->bufsz - pullwr->valid < pullwr->thresh) + return; + newsize = MAX(pullwr->valid, pullwr->thresh * 2); + newbuf = XMALLOC(MTYPE_PULLWR_BUF, newsize); + } + + niov = pullwr_iov(pullwr, iov); + if (niov >= 1) { + memcpy(newbuf, iov[0].iov_base, iov[0].iov_len); + if (niov >= 2) + memcpy(newbuf + iov[0].iov_len, + iov[1].iov_base, iov[1].iov_len); + } + + XFREE(MTYPE_PULLWR_BUF, pullwr->buffer); + pullwr->buffer = newbuf; + pullwr->bufsz = newsize; + pullwr->pos = 0; +} + +void pullwr_write(struct pullwr *pullwr, const void *data, size_t len) +{ + pullwr_resize(pullwr, len); + + if (pullwr->pos + pullwr->valid > pullwr->bufsz) { + size_t pos; + + pos = (pullwr->pos + pullwr->valid) % pullwr->bufsz; + memcpy(pullwr->buffer + pos, data, len); + } else { + size_t max1, len1; + max1 = pullwr->bufsz - (pullwr->pos + pullwr->valid); + max1 = MIN(max1, len); + + memcpy(pullwr->buffer + pullwr->pos + pullwr->valid, + data, max1); + len1 = len - max1; + + if (len1) + memcpy(pullwr->buffer, (char *)data + max1, len1); + + } + pullwr->valid += len; + + pullwr_bump(pullwr); +} + +static int pullwr_run(struct thread *t) +{ + struct pullwr *pullwr = THREAD_ARG(t); + struct iovec iov[2]; + size_t niov, lastvalid; + ssize_t nwr; + struct timeval t0; + bool maxspun = false; + + monotime(&t0); + + do { + lastvalid = pullwr->valid - 1; + while (pullwr->valid < pullwr->thresh + && pullwr->valid != lastvalid + && !maxspun) { + lastvalid = pullwr->valid; + pullwr->fill(pullwr->arg, pullwr); + + /* check after doing at least one fill() call so we + * don't spin without making progress on slow boxes + */ + if (!maxspun && monotime_since(&t0, NULL) + >= pullwr->maxspin) + maxspun = true; + } + + if (pullwr->valid == 0) { + /* we made a fill() call above that didn't feed any + * data in, and we have nothing more queued, so we go + * into idle, i.e. no calling thread_add_write() + */ + pullwr_resize(pullwr, 0); + return 0; + } + + niov = pullwr_iov(pullwr, iov); + assert(niov); + + nwr = writev(pullwr->fd, iov, niov); + if (nwr < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) + break; + pullwr->err(pullwr->arg, pullwr, false); + return 0; + } + + if (nwr == 0) { + pullwr->err(pullwr->arg, pullwr, true); + return 0; + } + + pullwr->total_written += nwr; + pullwr->valid -= nwr; + pullwr->pos += nwr; + pullwr->pos %= pullwr->bufsz; + } while (pullwr->valid == 0 && !maxspun); + /* pullwr->valid != 0 implies we did an incomplete write, i.e. socket + * is full and we go wait until it's available for writing again. + */ + + thread_add_write(pullwr->tm, pullwr_run, pullwr, pullwr->fd, + &pullwr->writer); + + /* if we hit the time limit, just keep the buffer, we'll probably need + * it anyway & another run is already coming up. + */ + if (!maxspun) + pullwr_resize(pullwr, 0); + return 0; +} + +void pullwr_stats(struct pullwr *pullwr, uint64_t *total_written, + size_t *pending, size_t *kernel_pending) +{ + int tmp; + + *total_written = pullwr->total_written; + *pending = pullwr->valid; + + if (ioctl(pullwr->fd, TIOCOUTQ, &tmp) != 0) + tmp = 0; + *kernel_pending = tmp; +} diff --git a/lib/pullwr.h b/lib/pullwr.h new file mode 100644 index 0000000000..a0e89e0c30 --- /dev/null +++ b/lib/pullwr.h @@ -0,0 +1,118 @@ +/* + * Pull-driven write event handler + * Copyright (C) 2019 David Lamparter + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _WRITEPOLL_H +#define _WRITEPOLL_H + +#include +#include + +#include "thread.h" +#include "stream.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct pullwr; + +/* This is a "pull-driven" write event handler. Instead of having some buffer + * or being driven by the availability of data, it triggers on the space being + * available on the socket for data to be written on and then calls fill() to + * get data to be sent. + * + * pullwr_* maintains an "idle" vs. "active" state, going into idle when a + * fill() call completes without feeing more data into it. The overall + * semantics are: + * - to put data out, call pullwr_write(). This is possible from both inside + * fill() callbacks or anywhere else. Doing so puts the pullwr into + * active state. + * - in active state, the fill() callback will be called and should feed more + * data in. It should NOT loop to push out more than one "unit" of data; + * the pullwr code handles this by calling fill() until it has enough data. + * - if there's nothing more to be sent, fill() returns without doing anything + * and pullwr goes into idle state after flushing all buffered data out. + * - when new data becomes available, pullwr_bump() should be called to put + * the pullwr back into active mode so it will collect data from fill(), + * or you can directly call pullwr_write(). + * - only calling pullwr_write() from within fill() is the cleanest way of + * doing things. + * + * When the err() callback is called, the pullwr should be considered unusable + * and released with pullwr_del(). This can be done from inside the callback, + * the pullwr code holds no more references on it when calling err(). + */ +extern struct pullwr *_pullwr_new(struct thread_master *tm, int fd, + void *arg, + void (*fill)(void *, struct pullwr *), + void (*err)(void *, struct pullwr *, bool eof)); +extern void pullwr_del(struct pullwr *pullwr); + +/* type-checking wrapper. makes sure fill() and err() take a first argument + * whose type is identical to the type of arg. + * => use "void fill(struct mystruct *arg, ...)" - no "void *arg" + */ +#define pullwr_new(tm, fd, arg, fill, err) ({ \ + void (*fill_typechk)(typeof(arg), struct pullwr *) = fill; \ + void (*err_typechk)(typeof(arg), struct pullwr *, bool) = err; \ + _pullwr_new(tm, fd, arg, (void *)fill_typechk, (void *)err_typechk); \ +}) + +/* max_spin_usec is the time after which the pullwr event handler will stop + * trying to get more data from fill() and yield control back to the + * thread_master. It does reschedule itself to continue later; this is + * only to make sure we don't freeze the entire process if we're piping a + * lot of data to a local endpoint that reads quickly (i.e. no backpressure) + * + * default: 2500 (2.5 ms) + * + * write_threshold is the amount of data buffered from fill() calls at which + * the pullwr code starts calling write(). But this is not a "limit". + * pullwr will keep poking fill() for more data until + * (a) max_spin_usec is reached; fill() will be called again later after + * returning to the thread_master to give other events a chance to run + * (b) fill() returns without pushing any data onto the pullwr with + * pullwr_write(), so fill() will NOT be called again until a call to + * pullwr_bump() or pullwr_write() comes in. + * + * default: 16384 (16 kB) + * + * passing 0 for either value (or not calling it at all) uses the default. + */ +extern void pullwr_cfg(struct pullwr *pullwr, int64_t max_spin_usec, + size_t write_threshold); + +extern void pullwr_bump(struct pullwr *pullwr); +extern void pullwr_write(struct pullwr *pullwr, + const void *data, size_t len); + +static inline void pullwr_write_stream(struct pullwr *pullwr, + struct stream *s) +{ + pullwr_write(pullwr, s->data, stream_get_endp(s)); +} + +extern void pullwr_stats(struct pullwr *pullwr, uint64_t *total_written, + size_t *pending, size_t *kernel_pending); + +#ifdef __cplusplus +} +#endif + +#endif /* _WRITEPOLL_H */ diff --git a/lib/pw.h b/lib/pw.h index 42b3ee2155..2fc4a61f96 100644 --- a/lib/pw.h +++ b/lib/pw.h @@ -35,9 +35,13 @@ extern "C" { /* Pseudowire flags. */ #define F_PSEUDOWIRE_CWORD 0x01 -/* Pseudowire status. */ -#define PW_STATUS_DOWN 0 -#define PW_STATUS_UP 1 +/* Pseudowire status TLV */ +#define PW_FORWARDING 0 +#define PW_NOT_FORWARDING (1 << 0) +#define PW_LOCAL_RX_FAULT (1 << 1) +#define PW_LOCAL_TX_FAULT (1 << 2) +#define PW_PSN_RX_FAULT (1 << 3) +#define PW_PSN_TX_FAULT (1 << 4) /* * Protocol-specific information about the pseudowire. diff --git a/lib/qobj.c b/lib/qobj.c index 3e3860a96a..cb3254cbe9 100644 --- a/lib/qobj.c +++ b/lib/qobj.c @@ -26,6 +26,7 @@ #include "log.h" #include "qobj.h" #include "jhash.h" +#include "network.h" static uint32_t qobj_hash(const struct qobj_node *node) { @@ -48,13 +49,13 @@ static pthread_rwlock_t nodes_lock; static struct qobj_nodes_head nodes = { }; -void qobj_reg(struct qobj_node *node, struct qobj_nodetype *type) +void qobj_reg(struct qobj_node *node, const struct qobj_nodetype *type) { node->type = type; pthread_rwlock_wrlock(&nodes_lock); do { - node->nid = (uint64_t)random(); - node->nid ^= (uint64_t)random() << 32; + node->nid = (uint64_t)frr_weak_random(); + node->nid ^= (uint64_t)frr_weak_random() << 32; } while (!node->nid || qobj_nodes_find(&nodes, node)); qobj_nodes_add(&nodes, node); pthread_rwlock_unlock(&nodes_lock); @@ -76,7 +77,7 @@ struct qobj_node *qobj_get(uint64_t id) return rv; } -void *qobj_get_typed(uint64_t id, struct qobj_nodetype *type) +void *qobj_get_typed(uint64_t id, const struct qobj_nodetype *type) { struct qobj_node dummy = {.nid = id}; struct qobj_node *node; diff --git a/lib/qobj.h b/lib/qobj.h index 415eae02ef..400ae0151c 100644 --- a/lib/qobj.h +++ b/lib/qobj.h @@ -89,7 +89,7 @@ PREDECL_HASH(qobj_nodes) struct qobj_node { uint64_t nid; struct qobj_nodes_item nodehash; - struct qobj_nodetype *type; + const struct qobj_nodetype *type; }; #define QOBJ_FIELDS struct qobj_node qobj_node; @@ -111,20 +111,20 @@ struct qobj_node { * * in the long this may need another touch, e.g. built-in per-object locking. */ -void qobj_reg(struct qobj_node *node, struct qobj_nodetype *type); +void qobj_reg(struct qobj_node *node, const struct qobj_nodetype *type); void qobj_unreg(struct qobj_node *node); struct qobj_node *qobj_get(uint64_t id); -void *qobj_get_typed(uint64_t id, struct qobj_nodetype *type); +void *qobj_get_typed(uint64_t id, const struct qobj_nodetype *type); /* type declarations */ #define DECLARE_QOBJ_TYPE(structname) \ - extern struct qobj_nodetype qobj_t_##structname; + extern const struct qobj_nodetype qobj_t_##structname; #define DEFINE_QOBJ_TYPE(structname) \ - struct qobj_nodetype qobj_t_##structname = { \ + const struct qobj_nodetype qobj_t_##structname = { \ .node_member_offset = \ (ptrdiff_t)offsetof(struct structname, qobj_node)}; #define DEFINE_QOBJ_TYPE_INIT(structname, ...) \ - struct qobj_nodetype qobj_t_##structname = { \ + const struct qobj_nodetype qobj_t_##structname = { \ .node_member_offset = \ (ptrdiff_t)offsetof(struct structname, qobj_node), \ __VA_ARGS__}; diff --git a/nhrpd/resolver.c b/lib/resolver.c similarity index 62% rename from nhrpd/resolver.c rename to lib/resolver.c index 64b16e7ee3..e5caadb2d0 100644 --- a/nhrpd/resolver.c +++ b/lib/resolver.c @@ -17,17 +17,18 @@ #include "vector.h" #include "thread.h" #include "lib_errors.h" - -#include "nhrpd.h" -#include "nhrp_errors.h" +#include "resolver.h" +#include "command.h" struct resolver_state { ares_channel channel; + struct thread_master *master; struct thread *timeout; vector read_threads, write_threads; }; static struct resolver_state state; +static bool resolver_debug; #define THREAD_RUNNING ((struct thread *)-1) @@ -54,7 +55,8 @@ static int resolver_cb_socket_readable(struct thread *t) ares_process_fd(r->channel, fd, ARES_SOCKET_BAD); if (vector_lookup(r->read_threads, fd) == THREAD_RUNNING) { t = NULL; - thread_add_read(master, resolver_cb_socket_readable, r, fd, &t); + thread_add_read(r->master, resolver_cb_socket_readable, r, fd, + &t); vector_set_index(r->read_threads, fd, t); } resolver_update_timeouts(r); @@ -71,7 +73,7 @@ static int resolver_cb_socket_writable(struct thread *t) ares_process_fd(r->channel, ARES_SOCKET_BAD, fd); if (vector_lookup(r->write_threads, fd) == THREAD_RUNNING) { t = NULL; - thread_add_write(master, resolver_cb_socket_writable, r, fd, + thread_add_write(r->master, resolver_cb_socket_writable, r, fd, &t); vector_set_index(r->write_threads, fd, t); } @@ -91,8 +93,8 @@ static void resolver_update_timeouts(struct resolver_state *r) tv = ares_timeout(r->channel, NULL, &tvbuf); if (tv) { unsigned int timeoutms = tv->tv_sec * 1000 + tv->tv_usec / 1000; - thread_add_timer_msec(master, resolver_cb_timeout, r, timeoutms, - &r->timeout); + thread_add_timer_msec(r->master, resolver_cb_timeout, r, + timeoutms, &r->timeout); } } @@ -105,8 +107,8 @@ static void ares_socket_cb(void *data, ares_socket_t fd, int readable, if (readable) { t = vector_lookup_ensure(r->read_threads, fd); if (!t) { - thread_add_read(master, resolver_cb_socket_readable, r, - fd, &t); + thread_add_read(r->master, resolver_cb_socket_readable, + r, fd, &t); vector_set_index(r->read_threads, fd, t); } } else { @@ -122,8 +124,8 @@ static void ares_socket_cb(void *data, ares_socket_t fd, int readable, if (writable) { t = vector_lookup_ensure(r->write_threads, fd); if (!t) { - thread_add_read(master, resolver_cb_socket_writable, r, - fd, &t); + thread_add_read(r->master, resolver_cb_socket_writable, + r, fd, &t); vector_set_index(r->write_threads, fd, t); } } else { @@ -137,37 +139,25 @@ static void ares_socket_cb(void *data, ares_socket_t fd, int readable, } } -void resolver_init(void) -{ - struct ares_options ares_opts; - - state.read_threads = vector_init(1); - state.write_threads = vector_init(1); - - ares_opts = (struct ares_options){ - .sock_state_cb = &ares_socket_cb, - .sock_state_cb_data = &state, - .timeout = 2, - .tries = 3, - }; - - ares_init_options(&state.channel, &ares_opts, - ARES_OPT_SOCK_STATE_CB | ARES_OPT_TIMEOUT - | ARES_OPT_TRIES); -} - static void ares_address_cb(void *arg, int status, int timeouts, struct hostent *he) { struct resolver_query *query = (struct resolver_query *)arg; union sockunion addr[16]; + void (*callback)(struct resolver_query *, const char *, int, + union sockunion *); size_t i; + callback = query->callback; + query->callback = NULL; + if (status != ARES_SUCCESS) { - debugf(NHRP_DEBUG_COMMON, "[%p] Resolving failed", query); - query->callback(query, -1, NULL); - query->callback = NULL; + if (resolver_debug) + zlog_debug("[%p] Resolving failed (%s)", + query, ares_strerror(status)); + + callback(query, ares_strerror(status), -1, NULL); return; } @@ -186,28 +176,111 @@ static void ares_address_cb(void *arg, int status, int timeouts, } } - debugf(NHRP_DEBUG_COMMON, "[%p] Resolved with %d results", query, - (int)i); - query->callback(query, i, &addr[0]); + if (resolver_debug) + zlog_debug("[%p] Resolved with %d results", query, (int)i); + + callback(query, NULL, i, &addr[0]); +} + +static int resolver_cb_literal(struct thread *t) +{ + struct resolver_query *query = THREAD_ARG(t); + void (*callback)(struct resolver_query *, const char *, int, + union sockunion *); + + callback = query->callback; query->callback = NULL; + + callback(query, ARES_SUCCESS, 1, &query->literal_addr); + return 0; } void resolver_resolve(struct resolver_query *query, int af, const char *hostname, - void (*callback)(struct resolver_query *, int, - union sockunion *)) + void (*callback)(struct resolver_query *, const char *, + int, union sockunion *)) { + int ret; + if (query->callback != NULL) { flog_err( - EC_NHRP_RESOLVER, + EC_LIB_RESOLVER, "Trying to resolve '%s', but previous query was not finished yet", hostname); return; } - debugf(NHRP_DEBUG_COMMON, "[%p] Resolving '%s'", query, hostname); - query->callback = callback; + query->literal_cb = NULL; + + ret = str2sockunion(hostname, &query->literal_addr); + if (ret == 0) { + if (resolver_debug) + zlog_debug("[%p] Resolving '%s' (IP literal)", + query, hostname); + + /* for consistency with proper name lookup, don't call the + * callback immediately; defer to thread loop + */ + thread_add_timer_msec(state.master, resolver_cb_literal, + query, 0, &query->literal_cb); + return; + } + + if (resolver_debug) + zlog_debug("[%p] Resolving '%s'", query, hostname); + ares_gethostbyname(state.channel, hostname, af, ares_address_cb, query); resolver_update_timeouts(&state); } + +DEFUN(debug_resolver, + debug_resolver_cmd, + "[no] debug resolver", + NO_STR + DEBUG_STR + "Debug DNS resolver actions\n") +{ + resolver_debug = (argc == 2); + return CMD_SUCCESS; +} + +static int resolver_config_write_debug(struct vty *vty); +static struct cmd_node resolver_debug_node = { + .name = "resolver debug", + .node = RESOLVER_DEBUG_NODE, + .prompt = "", + .config_write = resolver_config_write_debug, +}; + +static int resolver_config_write_debug(struct vty *vty) +{ + if (resolver_debug) + vty_out(vty, "debug resolver\n"); + return 1; +} + + +void resolver_init(struct thread_master *tm) +{ + struct ares_options ares_opts; + + state.master = tm; + state.read_threads = vector_init(1); + state.write_threads = vector_init(1); + + ares_opts = (struct ares_options){ + .sock_state_cb = &ares_socket_cb, + .sock_state_cb_data = &state, + .timeout = 2, + .tries = 3, + }; + + ares_init_options(&state.channel, &ares_opts, + ARES_OPT_SOCK_STATE_CB | ARES_OPT_TIMEOUT + | ARES_OPT_TRIES); + + install_node(&resolver_debug_node); + install_element(CONFIG_NODE, &debug_resolver_cmd); + install_element(ENABLE_NODE, &debug_resolver_cmd); +} diff --git a/lib/resolver.h b/lib/resolver.h new file mode 100644 index 0000000000..5f922dcb57 --- /dev/null +++ b/lib/resolver.h @@ -0,0 +1,39 @@ +/* C-Ares integration to Quagga mainloop + * Copyright (c) 2014-2015 Timo Teräs + * + * This file is free software: you may copy, redistribute and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef _FRR_RESOLVER_H +#define _FRR_RESOLVER_H + +#include "thread.h" +#include "sockunion.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct resolver_query { + void (*callback)(struct resolver_query *, const char *errstr, int n, + union sockunion *); + + /* used to immediate provide the result if IP literal is passed in */ + union sockunion literal_addr; + struct thread *literal_cb; +}; + +void resolver_init(struct thread_master *tm); +void resolver_resolve(struct resolver_query *query, int af, + const char *hostname, void (*cb)(struct resolver_query *, + const char *, int, + union sockunion *)); + +#ifdef __cplusplus +} +#endif + +#endif /* _FRR_RESOLVER_H */ diff --git a/lib/route_types.pl b/lib/route_types.pl index f297096633..e007de4d69 100755 --- a/lib/route_types.pl +++ b/lib/route_types.pl @@ -121,7 +121,7 @@ sub codelist { } $str =~ s/ $//; push @lines, $str . "\\n\" \\\n"; - push @lines, " \" > - selected route, * - FIB route, q - queued route, r - rejected route\\n\\n\""; + push @lines, " \" > - selected route, * - FIB route, q - queued, r - rejected, b - backup\\n\\n\""; return join("", @lines); } diff --git a/lib/route_types.txt b/lib/route_types.txt index c5eff44ca7..b549c11cfc 100644 --- a/lib/route_types.txt +++ b/lib/route_types.txt @@ -83,6 +83,9 @@ ZEBRA_ROUTE_SHARP, sharp, sharpd, 'D', 1, 1, 1, "SHARP" 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_SRTE, srte, none, '-', 0, 0, 0, "SR-TE" ZEBRA_ROUTE_ALL, wildcard, none, '-', 0, 0, 0, "-" @@ -110,4 +113,6 @@ ZEBRA_ROUTE_BABEL, "Babel routing protocol (Babel)" ZEBRA_ROUTE_SHARP, "Super Happy Advanced Routing Protocol (sharpd)" ZEBRA_ROUTE_PBR, "Policy Based Routing (PBR)" ZEBRA_ROUTE_BFD, "Bidirectional Fowarding Detection (BFD)" +ZEBRA_ROUTE_VRRP, "Virtual Router Redundancy Protocol (VRRP)" ZEBRA_ROUTE_OPENFABRIC, "OpenFabric Routing Protocol" +ZEBRA_ROUTE_NHG, "Zebra Nexthop Groups (NHG)" diff --git a/lib/routemap.c b/lib/routemap.c index 4898a8d0fa..3ba5c3204c 100644 --- a/lib/routemap.c +++ b/lib/routemap.c @@ -31,6 +31,7 @@ #include "hash.h" #include "libfrr.h" #include "lib_errors.h" +#include "table.h" DEFINE_MTYPE_STATIC(LIB, ROUTE_MAP, "Route map") DEFINE_MTYPE(LIB, ROUTE_MAP_NAME, "Route map name") @@ -39,186 +40,48 @@ DEFINE_MTYPE(LIB, ROUTE_MAP_RULE, "Route map rule") DEFINE_MTYPE_STATIC(LIB, ROUTE_MAP_RULE_STR, "Route map rule str") DEFINE_MTYPE(LIB, ROUTE_MAP_COMPILED, "Route map compiled") DEFINE_MTYPE_STATIC(LIB, ROUTE_MAP_DEP, "Route map dependency") +DEFINE_MTYPE_STATIC(LIB, ROUTE_MAP_DEP_DATA, "Route map dependency data") DEFINE_QOBJ_TYPE(route_map_index) DEFINE_QOBJ_TYPE(route_map) +#define IPv4_PREFIX_LIST "ip address prefix-list" +#define IPv6_PREFIX_LIST "ipv6 address prefix-list" + +#define IS_RULE_IPv4_PREFIX_LIST(S) \ + (strncmp(S, IPv4_PREFIX_LIST, strlen(IPv4_PREFIX_LIST)) == 0) +#define IS_RULE_IPv6_PREFIX_LIST(S) \ + (strncmp(S, IPv6_PREFIX_LIST, strlen(IPv6_PREFIX_LIST)) == 0) + +struct route_map_pentry_dep { + struct prefix_list_entry *pentry; + const char *plist_name; + route_map_event_t event; +}; + /* Vector for route match rules. */ static vector route_match_vec; /* Vector for route set rules. */ static vector route_set_vec; -struct route_map_match_set_hooks { - /* match interface */ - int (*match_interface)(struct vty *vty, struct route_map_index *index, - const char *command, const char *arg, - route_map_event_t type); - - /* no match interface */ - int (*no_match_interface)(struct vty *vty, - struct route_map_index *index, - const char *command, const char *arg, - route_map_event_t type); - - /* match ip address */ - int (*match_ip_address)(struct vty *vty, struct route_map_index *index, - const char *command, const char *arg, - route_map_event_t type); - - /* no match ip address */ - int (*no_match_ip_address)(struct vty *vty, - struct route_map_index *index, - const char *command, const char *arg, - route_map_event_t type); - - /* match ip address prefix list */ - int (*match_ip_address_prefix_list)(struct vty *vty, - struct route_map_index *index, - const char *command, - const char *arg, - route_map_event_t type); - - /* no match ip address prefix list */ - int (*no_match_ip_address_prefix_list)(struct vty *vty, - struct route_map_index *index, - const char *command, - const char *arg, - route_map_event_t type); - - /* match ip next hop */ - int (*match_ip_next_hop)(struct vty *vty, struct route_map_index *index, - const char *command, const char *arg, - route_map_event_t type); - - /* no match ip next hop */ - int (*no_match_ip_next_hop)(struct vty *vty, - struct route_map_index *index, - const char *command, const char *arg, - route_map_event_t type); - - /* match ip next hop prefix list */ - int (*match_ip_next_hop_prefix_list)(struct vty *vty, - struct route_map_index *index, - const char *command, - const char *arg, - route_map_event_t type); - - /* no match ip next hop prefix list */ - int (*no_match_ip_next_hop_prefix_list)(struct vty *vty, - struct route_map_index *index, - const char *command, - const char *arg, - route_map_event_t type); - - /* match ip next hop type */ - int (*match_ip_next_hop_type)(struct vty *vty, - struct route_map_index *index, - const char *command, - const char *arg, - route_map_event_t type); - - /* no match ip next hop type */ - int (*no_match_ip_next_hop_type)(struct vty *vty, - struct route_map_index *index, - const char *command, - const char *arg, - route_map_event_t type); - - /* match ipv6 address */ - int (*match_ipv6_address)(struct vty *vty, - struct route_map_index *index, - const char *command, const char *arg, - route_map_event_t type); - - /* no match ipv6 address */ - int (*no_match_ipv6_address)(struct vty *vty, - struct route_map_index *index, - const char *command, const char *arg, - route_map_event_t type); - - - /* match ipv6 address prefix list */ - int (*match_ipv6_address_prefix_list)(struct vty *vty, - struct route_map_index *index, - const char *command, - const char *arg, - route_map_event_t type); - - /* no match ipv6 address prefix list */ - int (*no_match_ipv6_address_prefix_list)(struct vty *vty, - struct route_map_index *index, - const char *command, - const char *arg, - route_map_event_t type); - - /* match ipv6 next-hop type */ - int (*match_ipv6_next_hop_type)(struct vty *vty, - struct route_map_index *index, - const char *command, - const char *arg, - route_map_event_t type); - - /* no match ipv6next-hop type */ - int (*no_match_ipv6_next_hop_type)(struct vty *vty, - struct route_map_index *index, - const char *command, const char *arg, - route_map_event_t type); - - /* match metric */ - int (*match_metric)(struct vty *vty, struct route_map_index *index, - const char *command, const char *arg, - route_map_event_t type); - - /* no match metric */ - int (*no_match_metric)(struct vty *vty, struct route_map_index *index, - const char *command, const char *arg, - route_map_event_t type); +static void route_map_pfx_tbl_update(route_map_event_t event, + struct route_map_index *index, afi_t afi, + const char *plist_name); +static void route_map_pfx_table_add_default(afi_t afi, + struct route_map_index *index); +static void route_map_pfx_table_del_default(afi_t afi, + struct route_map_index *index); +static void route_map_add_plist_entries(afi_t afi, + struct route_map_index *index, + const char *plist_name, + struct prefix_list_entry *entry); +static void route_map_del_plist_entries(afi_t afi, + struct route_map_index *index, + const char *plist_name, + struct prefix_list_entry *entry); - /* match tag */ - int (*match_tag)(struct vty *vty, struct route_map_index *index, - const char *command, const char *arg, - route_map_event_t type); - - /* no match tag */ - int (*no_match_tag)(struct vty *vty, struct route_map_index *index, - const char *command, const char *arg, - route_map_event_t type); - - /* set ip nexthop */ - int (*set_ip_nexthop)(struct vty *vty, struct route_map_index *index, - const char *command, const char *arg); - - /* no set ip nexthop */ - int (*no_set_ip_nexthop)(struct vty *vty, struct route_map_index *index, - const char *command, const char *arg); - - /* set ipv6 nexthop local */ - int (*set_ipv6_nexthop_local)(struct vty *vty, - struct route_map_index *index, - const char *command, const char *arg); - - /* no set ipv6 nexthop local */ - int (*no_set_ipv6_nexthop_local)(struct vty *vty, - struct route_map_index *index, - const char *command, const char *arg); - - /* set metric */ - int (*set_metric)(struct vty *vty, struct route_map_index *index, - const char *command, const char *arg); - - /* no set metric */ - int (*no_set_metric)(struct vty *vty, struct route_map_index *index, - const char *command, const char *arg); - - /* set tag */ - int (*set_tag)(struct vty *vty, struct route_map_index *index, - const char *command, const char *arg); - - /* no set tag */ - int (*no_set_tag)(struct vty *vty, struct route_map_index *index, - const char *command, const char *arg); -}; +static struct hash *route_map_get_dep_hash(route_map_event_t event); struct route_map_match_set_hooks rmap_match_set_hook; @@ -302,7 +165,7 @@ void route_map_no_match_ip_next_hop_prefix_list_hook(int (*func)( rmap_match_set_hook.no_match_ip_next_hop_prefix_list = func; } -/* match ip next hop type */ +/* match ip next-hop type */ void route_map_match_ip_next_hop_type_hook(int (*func)( struct vty *vty, struct route_map_index *index, const char *command, const char *arg, route_map_event_t type)) @@ -310,7 +173,7 @@ void route_map_match_ip_next_hop_type_hook(int (*func)( rmap_match_set_hook.match_ip_next_hop_type = func; } -/* no match ip next hop type */ +/* no match ip next-hop type */ void route_map_no_match_ip_next_hop_type_hook(int (*func)( struct vty *vty, struct route_map_index *index, const char *command, const char *arg, route_map_event_t type)) @@ -400,6 +263,24 @@ void route_map_no_match_tag_hook(int (*func)( rmap_match_set_hook.no_match_tag = func; } +/* set sr-te color */ +void route_map_set_srte_color_hook(int (*func)(struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg)) +{ + rmap_match_set_hook.set_srte_color = func; +} + +/* no set sr-te color */ +void route_map_no_set_srte_color_hook(int (*func)(struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg)) +{ + rmap_match_set_hook.no_set_srte_color = func; +} + /* set ip nexthop */ void route_map_set_ip_nexthop_hook(int (*func)(struct vty *vty, struct route_map_index *index, @@ -473,24 +354,29 @@ int generic_match_add(struct vty *vty, struct route_map_index *index, const char *command, const char *arg, route_map_event_t type) { - int ret; + enum rmap_compile_rets ret; - ret = route_map_add_match(index, command, arg); + ret = route_map_add_match(index, command, arg, type); switch (ret) { - case RMAP_COMPILE_SUCCESS: - if (type != RMAP_EVENT_MATCH_ADDED) { - route_map_upd8_dependency(type, arg, index->map->name); - } - break; case RMAP_RULE_MISSING: - vty_out(vty, "%% [%s] Can't find rule.\n", frr_protonameinst); + if (vty) + vty_out(vty, "%% [%s] Can't find rule.\n", + frr_protonameinst); + else + zlog_warn("Can't find rule: %s", command); return CMD_WARNING_CONFIG_FAILED; - break; case RMAP_COMPILE_ERROR: - vty_out(vty, - "%% [%s] Argument form is unsupported or malformed.\n", - frr_protonameinst); + if (vty) + vty_out(vty, + "%% [%s] Argument form is unsupported or malformed.\n", + frr_protonameinst); + else + zlog_warn("Argument form is unsupported or malformed: %s %s", command, arg); return CMD_WARNING_CONFIG_FAILED; + case RMAP_COMPILE_SUCCESS: + /* + * Nothing to do here move along + */ break; } @@ -501,7 +387,7 @@ int generic_match_delete(struct vty *vty, struct route_map_index *index, const char *command, const char *arg, route_map_event_t type) { - int ret; + enum rmap_compile_rets ret; int retval = CMD_SUCCESS; char *dep_name = NULL; const char *tmpstr; @@ -520,21 +406,29 @@ int generic_match_delete(struct vty *vty, struct route_map_index *index, rmap_name = XSTRDUP(MTYPE_ROUTE_MAP_NAME, index->map->name); } - ret = route_map_delete_match(index, command, dep_name); + ret = route_map_delete_match(index, command, dep_name, type); switch (ret) { case RMAP_RULE_MISSING: - vty_out(vty, "%% [%s] Can't find rule.\n", frr_protonameinst); + if (vty) + vty_out(vty, "%% [%s] Can't find rule.\n", + frr_protonameinst); + else + zlog_warn("Can't find rule: %s", command); retval = CMD_WARNING_CONFIG_FAILED; break; case RMAP_COMPILE_ERROR: - vty_out(vty, - "%% [%s] Argument form is unsupported or malformed.\n", - frr_protonameinst); + if (vty) + vty_out(vty, + "%% [%s] Argument form is unsupported or malformed.\n", + frr_protonameinst); + else + zlog_warn("Argument form is unsupported or malformed: %s %s", command, arg); retval = CMD_WARNING_CONFIG_FAILED; break; case RMAP_COMPILE_SUCCESS: - if (type != RMAP_EVENT_MATCH_DELETED && dep_name) - route_map_upd8_dependency(type, dep_name, rmap_name); + /* + * Nothing to do here + */ break; } @@ -547,20 +441,24 @@ int generic_match_delete(struct vty *vty, struct route_map_index *index, int generic_set_add(struct vty *vty, struct route_map_index *index, const char *command, const char *arg) { - int ret; + enum rmap_compile_rets ret; ret = route_map_add_set(index, command, arg); switch (ret) { case RMAP_RULE_MISSING: - vty_out(vty, "%% [%s] Can't find rule.\n", frr_protonameinst); + if (vty) + vty_out(vty, "%% [%s] Can't find rule.\n", frr_protonameinst); + else + zlog_warn("Can't find rule: %s", command); return CMD_WARNING_CONFIG_FAILED; - break; case RMAP_COMPILE_ERROR: - vty_out(vty, - "%% [%s] Argument form is unsupported or malformed.\n", - frr_protonameinst); + if (vty) + vty_out(vty, + "%% [%s] Argument form is unsupported or malformed.\n", + frr_protonameinst); + else + zlog_warn("Argument form is unsupported or malformed: %s %s", command, arg); return CMD_WARNING_CONFIG_FAILED; - break; case RMAP_COMPILE_SUCCESS: break; } @@ -571,20 +469,24 @@ int generic_set_add(struct vty *vty, struct route_map_index *index, int generic_set_delete(struct vty *vty, struct route_map_index *index, const char *command, const char *arg) { - int ret; + enum rmap_compile_rets ret; ret = route_map_delete_set(index, command, arg); switch (ret) { case RMAP_RULE_MISSING: - vty_out(vty, "%% [%s] Can't find rule.\n", frr_protonameinst); + if (vty) + vty_out(vty, "%% [%s] Can't find rule.\n", frr_protonameinst); + else + zlog_warn("Can't find rule: %s", command); return CMD_WARNING_CONFIG_FAILED; - break; case RMAP_COMPILE_ERROR: - vty_out(vty, - "%% [%s] Argument form is unsupported or malformed.\n", - frr_protonameinst); + if (vty) + vty_out(vty, + "%% [%s] Argument form is unsupported or malformed.\n", + frr_protonameinst); + else + zlog_warn("Argument form is unsupported or malformed: %s %s", command, arg); return CMD_WARNING_CONFIG_FAILED; - break; case RMAP_COMPILE_SUCCESS: break; } @@ -593,37 +495,11 @@ int generic_set_delete(struct vty *vty, struct route_map_index *index, } -/* Route map rule. This rule has both `match' rule and `set' rule. */ -struct route_map_rule { - /* Rule type. */ - struct route_map_rule_cmd *cmd; - - /* For pretty printing. */ - char *rule_str; - - /* Pre-compiled match rule. */ - void *value; - - /* Linked list. */ - struct route_map_rule *next; - struct route_map_rule *prev; -}; - -/* Making route map list. */ -struct route_map_list { - struct route_map *head; - struct route_map *tail; - - void (*add_hook)(const char *); - void (*delete_hook)(const char *); - void (*event_hook)(route_map_event_t, const char *); -}; - /* Master list of route map. */ -static struct route_map_list route_map_master = {NULL, NULL, NULL, NULL, NULL}; +struct route_map_list route_map_master = {NULL, NULL, NULL, NULL, NULL}; struct hash *route_map_master_hash = NULL; -static unsigned int route_map_hash_key_make(void *p) +static unsigned int route_map_hash_key_make(const void *p) { const struct route_map *map = p; return string_hash_make(map->name); @@ -670,16 +546,24 @@ struct route_map_dep { struct hash *this_hash; /* ptr to the hash structure this is part of */ }; +struct route_map_dep_data { + /* Route-map name. + */ + char *rname; + /* Count of number of sequences of this + * route-map that depend on the same entity. + */ + uint16_t refcnt; +}; + /* Hashes maintaining dependency between various sublists used by route maps */ -struct hash *route_map_dep_hash[ROUTE_MAP_DEP_MAX]; +static struct hash *route_map_dep_hash[ROUTE_MAP_DEP_MAX]; -static unsigned int route_map_dep_hash_make_key(void *p); +static unsigned int route_map_dep_hash_make_key(const void *p); static void route_map_clear_all_references(char *rmap_name); static void route_map_rule_delete(struct route_map_rule_list *, struct route_map_rule *); -static int rmap_debug = 0; - -static void route_map_index_delete(struct route_map_index *, int); +static bool rmap_debug; /* New route map allocation. Please note route map's name must be specified. */ @@ -728,6 +612,15 @@ static struct route_map *route_map_add(const char *name) (*route_map_master.add_hook)(name); route_map_notify_dependencies(name, RMAP_EVENT_CALL_ADDED); } + + if (!map->ipv4_prefix_table) + map->ipv4_prefix_table = route_table_init(); + + if (!map->ipv6_prefix_table) + map->ipv6_prefix_table = route_table_init(); + + if (rmap_debug) + zlog_debug("Add route-map %s", name); return map; } @@ -746,6 +639,9 @@ static void route_map_free_map(struct route_map *map) while ((index = map->head) != NULL) route_map_index_delete(index, 0); + if (rmap_debug) + zlog_debug("Deleting route-map %s", map->name); + list = &route_map_master; QOBJ_UNREG(map); @@ -766,7 +662,7 @@ static void route_map_free_map(struct route_map *map) } /* Route map delete from list. */ -static void route_map_delete(struct route_map *map) +void route_map_delete(struct route_map *map) { struct route_map_index *index; char *name; @@ -865,7 +761,7 @@ static int route_map_clear_updated(struct route_map *map) /* Lookup route map. If there isn't route map create one and return it. */ -static struct route_map *route_map_get(const char *name) +struct route_map *route_map_get(const char *name) { struct route_map *map; @@ -898,22 +794,43 @@ static const char *route_map_type_str(enum route_map_type type) switch (type) { case RMAP_PERMIT: return "permit"; - break; case RMAP_DENY: return "deny"; - break; - default: + case RMAP_ANY: return ""; - break; } + + return ""; } -static int route_map_empty(struct route_map *map) +static const char *route_map_cmd_result_str(enum route_map_cmd_result_t res) { - if (map->head == NULL && map->tail == NULL) - return 1; - else - return 0; + switch (res) { + case RMAP_MATCH: + return "match"; + case RMAP_NOMATCH: + return "no match"; + case RMAP_NOOP: + return "noop"; + case RMAP_ERROR: + return "error"; + case RMAP_OKAY: + return "okay"; + } + + return "invalid"; +} + +static const char *route_map_result_str(route_map_result_t res) +{ + switch (res) { + case RMAP_DENYMATCH: + return "deny"; + case RMAP_PERMITMATCH: + return "permit"; + } + + return "invalid"; } /* show route-map */ @@ -922,13 +839,15 @@ static void vty_show_route_map_entry(struct vty *vty, struct route_map *map) struct route_map_index *index; struct route_map_rule *rule; - vty_out(vty, "route-map: %s Invoked: %" PRIu64 "\n", - map->name, map->applied); + vty_out(vty, "route-map: %s Invoked: %" PRIu64 " Optimization: %s Processed Change: %s\n", + map->name, map->applied - map->applied_clear, + map->optimization_disabled ? "disabled" : "enabled", + map->to_be_processed ? "true" : "false"); for (index = map->head; index; index = index->next) { vty_out(vty, " %s, sequence %d Invoked %" PRIu64 "\n", route_map_type_str(index->type), index->pref, - index->applied); + index->applied - index->applied_clear); /* Description */ if (index->description) @@ -1042,20 +961,42 @@ static struct route_map_index *route_map_index_new(void) new = XCALLOC(MTYPE_ROUTE_MAP_INDEX, sizeof(struct route_map_index)); new->exitpolicy = RMAP_EXIT; /* Default to Cisco-style */ + TAILQ_INIT(&new->rhclist); QOBJ_REG(new, route_map_index); return new; } /* Free route map index. */ -static void route_map_index_delete(struct route_map_index *index, int notify) +void route_map_index_delete(struct route_map_index *index, int notify) { + struct routemap_hook_context *rhc; struct route_map_rule *rule; QOBJ_UNREG(index); + if (rmap_debug) + zlog_debug("Deleting route-map %s sequence %d", + index->map->name, index->pref); + + /* Free route map entry description. */ + XFREE(MTYPE_TMP, index->description); + + /* Free route map northbound hook contexts. */ + while ((rhc = TAILQ_FIRST(&index->rhclist)) != NULL) + routemap_hook_context_free(rhc); + /* Free route match. */ - while ((rule = index->match_list.head) != NULL) + while ((rule = index->match_list.head) != NULL) { + if (IS_RULE_IPv4_PREFIX_LIST(rule->cmd->str)) + route_map_pfx_tbl_update(RMAP_EVENT_PLIST_DELETED, + index, AFI_IP, rule->rule_str); + else if (IS_RULE_IPv6_PREFIX_LIST(rule->cmd->str)) + route_map_pfx_tbl_update(RMAP_EVENT_PLIST_DELETED, + index, AFI_IP6, + rule->rule_str); + route_map_rule_delete(&index->match_list, rule); + } /* Free route set. */ while ((rule = index->set_list.head) != NULL) @@ -1075,10 +1016,11 @@ static void route_map_index_delete(struct route_map_index *index, int notify) /* Free 'char *nextrm' if not NULL */ XFREE(MTYPE_ROUTE_MAP_NAME, index->nextrm); + route_map_pfx_tbl_update(RMAP_EVENT_INDEX_DELETED, index, 0, NULL); + /* Execute event hook. */ if (route_map_master.event_hook && notify) { - (*route_map_master.event_hook)(RMAP_EVENT_INDEX_DELETED, - index->map->name); + (*route_map_master.event_hook)(index->map->name); route_map_notify_dependencies(index->map->name, RMAP_EVENT_CALL_ADDED); } @@ -1135,17 +1077,23 @@ route_map_index_add(struct route_map *map, enum route_map_type type, int pref) point->prev = index; } + route_map_pfx_tbl_update(RMAP_EVENT_INDEX_ADDED, index, 0, NULL); + /* Execute event hook. */ if (route_map_master.event_hook) { - (*route_map_master.event_hook)(RMAP_EVENT_INDEX_ADDED, - map->name); + (*route_map_master.event_hook)(map->name); route_map_notify_dependencies(map->name, RMAP_EVENT_CALL_ADDED); } + + if (rmap_debug) + zlog_debug("Route-map %s add sequence %d, type: %s", + map->name, pref, route_map_type_str(type)); + return index; } /* Get route map index. */ -static struct route_map_index * +struct route_map_index * route_map_index_get(struct route_map *map, enum route_map_type type, int pref) { struct route_map_index *index; @@ -1171,22 +1119,22 @@ static struct route_map_rule *route_map_rule_new(void) } /* Install rule command to the match list. */ -void route_map_install_match(struct route_map_rule_cmd *cmd) +void route_map_install_match(const struct route_map_rule_cmd *cmd) { - vector_set(route_match_vec, cmd); + vector_set(route_match_vec, (void *)cmd); } /* Install rule command to the set list. */ -void route_map_install_set(struct route_map_rule_cmd *cmd) +void route_map_install_set(const struct route_map_rule_cmd *cmd) { - vector_set(route_set_vec, cmd); + vector_set(route_set_vec, (void *)cmd); } /* Lookup rule command from match list. */ -static struct route_map_rule_cmd *route_map_lookup_match(const char *name) +static const struct route_map_rule_cmd *route_map_lookup_match(const char *name) { unsigned int i; - struct route_map_rule_cmd *rule; + const struct route_map_rule_cmd *rule; for (i = 0; i < vector_active(route_match_vec); i++) if ((rule = vector_slot(route_match_vec, i)) != NULL) @@ -1196,10 +1144,10 @@ static struct route_map_rule_cmd *route_map_lookup_match(const char *name) } /* Lookup rule command from set list. */ -static struct route_map_rule_cmd *route_map_lookup_set(const char *name) +static const struct route_map_rule_cmd *route_map_lookup_set(const char *name) { unsigned int i; - struct route_map_rule_cmd *rule; + const struct route_map_rule_cmd *rule; for (i = 0; i < vector_active(route_set_vec); i++) if ((rule = vector_slot(route_set_vec, i)) != NULL) @@ -1267,7 +1215,7 @@ const char *route_map_get_match_arg(struct route_map_index *index, const char *match_name) { struct route_map_rule *rule; - struct route_map_rule_cmd *cmd; + const struct route_map_rule_cmd *cmd; /* First lookup rule for add match statement. */ cmd = route_map_lookup_match(match_name); @@ -1278,18 +1226,71 @@ const char *route_map_get_match_arg(struct route_map_index *index, if (rule->cmd == cmd && rule->rule_str != NULL) return (rule->rule_str); - return (NULL); + return NULL; +} + +static route_map_event_t get_route_map_delete_event(route_map_event_t type) +{ + switch (type) { + case RMAP_EVENT_CALL_ADDED: + return RMAP_EVENT_CALL_DELETED; + case RMAP_EVENT_PLIST_ADDED: + return RMAP_EVENT_PLIST_DELETED; + case RMAP_EVENT_CLIST_ADDED: + return RMAP_EVENT_CLIST_DELETED; + case RMAP_EVENT_ECLIST_ADDED: + return RMAP_EVENT_ECLIST_DELETED; + case RMAP_EVENT_LLIST_ADDED: + return RMAP_EVENT_LLIST_DELETED; + case RMAP_EVENT_ASLIST_ADDED: + return RMAP_EVENT_ASLIST_DELETED; + case RMAP_EVENT_FILTER_ADDED: + return RMAP_EVENT_FILTER_DELETED; + case RMAP_EVENT_SET_ADDED: + case RMAP_EVENT_SET_DELETED: + case RMAP_EVENT_SET_REPLACED: + case RMAP_EVENT_MATCH_ADDED: + case RMAP_EVENT_MATCH_DELETED: + case RMAP_EVENT_MATCH_REPLACED: + case RMAP_EVENT_INDEX_ADDED: + case RMAP_EVENT_INDEX_DELETED: + case RMAP_EVENT_CALL_DELETED: + case RMAP_EVENT_PLIST_DELETED: + case RMAP_EVENT_CLIST_DELETED: + case RMAP_EVENT_ECLIST_DELETED: + case RMAP_EVENT_LLIST_DELETED: + case RMAP_EVENT_ASLIST_DELETED: + case RMAP_EVENT_FILTER_DELETED: + /* This function returns the appropriate 'deleted' event type + * for every 'added' event type passed to this function. + * This is done only for named entities used in the + * route-map match commands. + * This function is not to be invoked for any of the other event + * types. + */ + assert(0); + } + + assert(0); + /* + * Return to make c happy but if we get here something has gone + * terribly terribly wrong, so yes this return makes no sense. + */ + return RMAP_EVENT_CALL_ADDED; } /* Add match statement to route map. */ -int route_map_add_match(struct route_map_index *index, const char *match_name, - const char *match_arg) +enum rmap_compile_rets route_map_add_match(struct route_map_index *index, + const char *match_name, + const char *match_arg, + route_map_event_t type) { struct route_map_rule *rule; struct route_map_rule *next; - struct route_map_rule_cmd *cmd; + const struct route_map_rule_cmd *cmd; void *compile; - int replaced = 0; + int8_t delete_rmap_event_type = 0; + const char *rule_key; /* First lookup rule for add match statement. */ cmd = route_map_lookup_match(match_name); @@ -1303,13 +1304,54 @@ int route_map_add_match(struct route_map_index *index, const char *match_name, return RMAP_COMPILE_ERROR; } else compile = NULL; + /* use the compiled results if applicable */ + if (compile && cmd->func_get_rmap_rule_key) + rule_key = (*cmd->func_get_rmap_rule_key) + (compile); + else + rule_key = match_arg; /* If argument is completely same ignore it. */ for (rule = index->match_list.head; rule; rule = next) { next = rule->next; if (rule->cmd == cmd) { + /* If the configured route-map match rule is exactly + * the same as the existing configuration then, + * ignore the duplicate configuration. + */ + if (strcmp(match_arg, rule->rule_str) == 0) { + if (cmd->func_free) + (*cmd->func_free)(compile); + + return RMAP_COMPILE_SUCCESS; + } + + /* If IPv4 or IPv6 prefix-list match criteria + * has been delete to the route-map index, update + * the route-map's prefix table. + */ + if (IS_RULE_IPv4_PREFIX_LIST(match_name)) + route_map_pfx_tbl_update( + RMAP_EVENT_PLIST_DELETED, index, AFI_IP, + rule->rule_str); + else if (IS_RULE_IPv6_PREFIX_LIST(match_name)) + route_map_pfx_tbl_update( + RMAP_EVENT_PLIST_DELETED, index, + AFI_IP6, rule->rule_str); + + /* Remove the dependency of the route-map on the rule + * that is being replaced. + */ + if (type >= RMAP_EVENT_CALL_ADDED) { + delete_rmap_event_type = + get_route_map_delete_event(type); + route_map_upd8_dependency( + delete_rmap_event_type, + rule->rule_str, + index->map->name); + } + route_map_rule_delete(&index->match_list, rule); - replaced = 1; } } @@ -1325,58 +1367,95 @@ int route_map_add_match(struct route_map_index *index, const char *match_name, /* Add new route match rule to linked list. */ route_map_rule_add(&index->match_list, rule); + /* If IPv4 or IPv6 prefix-list match criteria + * has been added to the route-map index, update + * the route-map's prefix table. + */ + if (IS_RULE_IPv4_PREFIX_LIST(match_name)) { + route_map_pfx_tbl_update(RMAP_EVENT_PLIST_ADDED, index, AFI_IP, + match_arg); + } else if (IS_RULE_IPv6_PREFIX_LIST(match_name)) { + route_map_pfx_tbl_update(RMAP_EVENT_PLIST_ADDED, index, AFI_IP6, + match_arg); + } + /* Execute event hook. */ if (route_map_master.event_hook) { - (*route_map_master.event_hook)( - replaced ? RMAP_EVENT_MATCH_REPLACED - : RMAP_EVENT_MATCH_ADDED, - index->map->name); + (*route_map_master.event_hook)(index->map->name); route_map_notify_dependencies(index->map->name, RMAP_EVENT_CALL_ADDED); } + if (type != RMAP_EVENT_MATCH_ADDED) + route_map_upd8_dependency(type, rule_key, index->map->name); return RMAP_COMPILE_SUCCESS; } /* Delete specified route match rule. */ -int route_map_delete_match(struct route_map_index *index, - const char *match_name, const char *match_arg) +enum rmap_compile_rets route_map_delete_match(struct route_map_index *index, + const char *match_name, + const char *match_arg, + route_map_event_t type) { struct route_map_rule *rule; - struct route_map_rule_cmd *cmd; + const struct route_map_rule_cmd *cmd; + const char *rule_key; cmd = route_map_lookup_match(match_name); if (cmd == NULL) - return 1; + return RMAP_RULE_MISSING; for (rule = index->match_list.head; rule; rule = rule->next) if (rule->cmd == cmd && (rulecmp(rule->rule_str, match_arg) == 0 || match_arg == NULL)) { - route_map_rule_delete(&index->match_list, rule); /* Execute event hook. */ if (route_map_master.event_hook) { - (*route_map_master.event_hook)( - RMAP_EVENT_MATCH_DELETED, - index->map->name); + (*route_map_master.event_hook)(index->map->name); route_map_notify_dependencies( index->map->name, RMAP_EVENT_CALL_ADDED); } - return 0; + if (cmd->func_get_rmap_rule_key) + rule_key = (*cmd->func_get_rmap_rule_key) + (rule->value); + else + rule_key = match_arg; + + if (type != RMAP_EVENT_MATCH_DELETED && rule_key) + route_map_upd8_dependency(type, rule_key, + index->map->name); + + route_map_rule_delete(&index->match_list, rule); + + /* If IPv4 or IPv6 prefix-list match criteria + * has been delete from the route-map index, update + * the route-map's prefix table. + */ + if (IS_RULE_IPv4_PREFIX_LIST(match_name)) { + route_map_pfx_tbl_update( + RMAP_EVENT_PLIST_DELETED, index, AFI_IP, + match_arg); + } else if (IS_RULE_IPv6_PREFIX_LIST(match_name)) { + route_map_pfx_tbl_update( + RMAP_EVENT_PLIST_DELETED, index, + AFI_IP6, match_arg); + } + + return RMAP_COMPILE_SUCCESS; } /* Can't find matched rule. */ - return 1; + return RMAP_RULE_MISSING; } /* Add route-map set statement to the route map. */ -int route_map_add_set(struct route_map_index *index, const char *set_name, - const char *set_arg) +enum rmap_compile_rets route_map_add_set(struct route_map_index *index, + const char *set_name, + const char *set_arg) { struct route_map_rule *rule; struct route_map_rule *next; - struct route_map_rule_cmd *cmd; + const struct route_map_rule_cmd *cmd; void *compile; - int replaced = 0; cmd = route_map_lookup_set(set_name); if (cmd == NULL) @@ -1395,10 +1474,8 @@ int route_map_add_set(struct route_map_index *index, const char *set_name, route_map_index. */ for (rule = index->set_list.head; rule; rule = next) { next = rule->next; - if (rule->cmd == cmd) { + if (rule->cmd == cmd) route_map_rule_delete(&index->set_list, rule); - replaced = 1; - } } /* Add new route map match rule. */ @@ -1415,10 +1492,7 @@ int route_map_add_set(struct route_map_index *index, const char *set_name, /* Execute event hook. */ if (route_map_master.event_hook) { - (*route_map_master.event_hook)(replaced - ? RMAP_EVENT_SET_REPLACED - : RMAP_EVENT_SET_ADDED, - index->map->name); + (*route_map_master.event_hook)(index->map->name); route_map_notify_dependencies(index->map->name, RMAP_EVENT_CALL_ADDED); } @@ -1426,15 +1500,16 @@ int route_map_add_set(struct route_map_index *index, const char *set_name, } /* Delete route map set rule. */ -int route_map_delete_set(struct route_map_index *index, const char *set_name, - const char *set_arg) +enum rmap_compile_rets route_map_delete_set(struct route_map_index *index, + const char *set_name, + const char *set_arg) { struct route_map_rule *rule; - struct route_map_rule_cmd *cmd; + const struct route_map_rule_cmd *cmd; cmd = route_map_lookup_set(set_name); if (cmd == NULL) - return 1; + return RMAP_RULE_MISSING; for (rule = index->set_list.head; rule; rule = rule->next) if ((rule->cmd == cmd) && (rulecmp(rule->rule_str, set_arg) == 0 @@ -1442,73 +1517,25 @@ int route_map_delete_set(struct route_map_index *index, const char *set_name, route_map_rule_delete(&index->set_list, rule); /* Execute event hook. */ if (route_map_master.event_hook) { - (*route_map_master.event_hook)( - RMAP_EVENT_SET_DELETED, - index->map->name); + (*route_map_master.event_hook)(index->map->name); route_map_notify_dependencies( index->map->name, RMAP_EVENT_CALL_ADDED); } - return 0; + return RMAP_COMPILE_SUCCESS; } /* Can't find matched rule. */ - return 1; + return RMAP_RULE_MISSING; } -/* Apply route map's each index to the object. - - The matrix for a route-map looks like this: - (note, this includes the description for the "NEXT" - and "GOTO" frobs now - - Match | No Match - | - permit action | cont - | - ------------------+--------------- - | - deny deny | cont - | - - action) - -Apply Set statements, accept route - -If Call statement is present jump to the specified route-map, if it - denies the route we finish. - -If NEXT is specified, goto NEXT statement - -If GOTO is specified, goto the first clause where pref > nextpref - -If nothing is specified, do as Cisco and finish - deny) - -Route is denied by route-map. - cont) - -Goto Next index - - If we get no matches after we've processed all updates, then the route - is dropped too. - - Some notes on the new "CALL", "NEXT" and "GOTO" - call WORD - If this clause is matched, then the set statements - are executed and then we jump to route-map 'WORD'. If - this route-map denies the route, we finish, in other - case we - do whatever the exit policy (EXIT, NEXT or GOTO) tells. - on-match next - If this clause is matched, then the set statements - are executed and then we drop through to the next clause - on-match goto n - If this clause is matched, then the set statments - are executed and then we goto the nth clause, or the - first clause greater than this. In order to ensure - route-maps *always* exit, you cannot jump backwards. - Sorry ;) - - We need to make sure our route-map processing matches the above -*/ - -static route_map_result_t +static enum route_map_cmd_result_t route_map_apply_match(struct route_map_rule_list *match_list, const struct prefix *prefix, route_map_object_t type, void *object) { - route_map_result_t ret = RMAP_NOMATCH; + enum route_map_cmd_result_t ret = RMAP_NOMATCH; struct route_map_rule *match; + bool is_matched = false; /* Check all match rule and if there is no match rule, go to the @@ -1517,1259 +1544,1472 @@ route_map_apply_match(struct route_map_rule_list *match_list, ret = RMAP_MATCH; else { for (match = match_list->head; match; match = match->next) { - /* Try each match statement in turn, If any do not - return - RMAP_MATCH, return, otherwise continue on to next - match - statement. All match statements must match for - end-result - to be a match. */ + /* + * Try each match statement. If any match does not + * return RMAP_MATCH or RMAP_NOOP, return. + * Otherwise continue on to next match statement. + * All match statements must MATCH for + * end-result to be a match. + * (Exception:If match stmts result in a mix of + * MATCH/NOOP, then also end-result is a match) + * If all result in NOOP, end-result is NOOP. + */ ret = (*match->cmd->func_apply)(match->value, prefix, type, object); - if (ret != RMAP_MATCH) + + /* + * If the consolidated result of func_apply is: + * ----------------------------------------------- + * | MATCH | NOMATCH | NOOP | Final Result | + * ------------------------------------------------ + * | yes | yes | yes | NOMATCH | + * | no | no | yes | NOOP | + * | yes | no | yes | MATCH | + * | no | yes | yes | NOMATCH | + * |----------------------------------------------- + * + * Traditionally, all rules within route-map + * should match for it to MATCH. + * If there are noops within the route-map rules, + * it follows the above matrix. + * + * Eg: route-map rm1 permit 10 + * match rule1 + * match rule2 + * match rule3 + * .... + * route-map rm1 permit 20 + * match ruleX + * match ruleY + * ... + */ + + switch (ret) { + case RMAP_MATCH: + is_matched = true; + break; + + case RMAP_NOMATCH: return ret; + + case RMAP_NOOP: + if (is_matched) + ret = RMAP_MATCH; + break; + + default: + break; + } + } } return ret; } -/* Apply route map to the object. */ -route_map_result_t route_map_apply(struct route_map *map, - const struct prefix *prefix, - route_map_object_t type, void *object) +static struct list *route_map_get_index_list(struct route_node **rn, + const struct prefix *prefix, + struct route_table *table) { - static int recursion = 0; - int ret = 0; - struct route_map_index *index; - struct route_map_rule *set; + struct route_node *tmp_rn = NULL; - if (recursion > RMAP_RECURSION_LIMIT) { - flog_warn( - EC_LIB_RMAP_RECURSION_LIMIT, - "route-map recursion limit (%d) reached, discarding route", - RMAP_RECURSION_LIMIT); - recursion = 0; - return RMAP_DENYMATCH; - } + if (!(*rn)) { + *rn = route_node_match(table, prefix); - if (map == NULL) - return RMAP_DENYMATCH; + if (!(*rn)) + return NULL; - map->applied++; - for (index = map->head; index; index = index->next) { - /* Apply this index. */ - index->applied++; - ret = route_map_apply_match(&index->match_list, prefix, type, - object); + if ((*rn)->info) + return (struct list *)((*rn)->info); - /* Now we apply the matrix from above */ - if (ret == RMAP_NOMATCH) - /* 'cont' from matrix - continue to next route-map - * sequence */ - continue; - else if (ret == RMAP_MATCH) { - if (index->type == RMAP_PERMIT) - /* 'action' */ - { - /* permit+match must execute sets */ - for (set = index->set_list.head; set; - set = set->next) - ret = (*set->cmd->func_apply)( - set->value, prefix, type, - object); + /* If rn->info is NULL, get the parent. + * Store the rn in tmp_rn and unlock it later. + */ + tmp_rn = *rn; + } - /* Call another route-map if available */ - if (index->nextrm) { - struct route_map *nextrm = - route_map_lookup_by_name( - index->nextrm); + do { + *rn = (*rn)->parent; + if (tmp_rn) + route_unlock_node(tmp_rn); - if (nextrm) /* Target route-map found, - jump to it */ - { - recursion++; - ret = route_map_apply( - nextrm, prefix, type, - object); - recursion--; - } + if (!(*rn)) + break; - /* If nextrm returned 'deny', finish. */ - if (ret == RMAP_DENYMATCH) - return ret; - } + if ((*rn)->info) { + route_lock_node(*rn); + return (struct list *)((*rn)->info); + } + } while (!(*rn)->info); - switch (index->exitpolicy) { - case RMAP_EXIT: - return ret; - case RMAP_NEXT: - continue; - case RMAP_GOTO: { - /* Find the next clause to jump to */ - struct route_map_index *next = - index->next; - int nextpref = index->nextpref; + return NULL; +} - while (next && next->pref < nextpref) { - index = next; - next = next->next; - } - if (next == NULL) { - /* No clauses match! */ - return ret; - } - } - } - } else if (index->type == RMAP_DENY) - /* 'deny' */ - { - return RMAP_DENYMATCH; +/* + * This function returns the route-map index that best matches the prefix. + */ +static struct route_map_index * +route_map_get_index(struct route_map *map, const struct prefix *prefix, + route_map_object_t type, void *object, uint8_t *match_ret) +{ + int ret = 0; + struct list *candidate_rmap_list = NULL; + struct route_node *rn = NULL; + struct listnode *ln = NULL, *nn = NULL; + struct route_map_index *index = NULL, *best_index = NULL; + struct route_map_index *head_index = NULL; + struct route_table *table = NULL; + unsigned char family = prefix->family; + + if (family == AF_INET) + table = map->ipv4_prefix_table; + else + table = map->ipv6_prefix_table; + + if (!table) + return NULL; + + do { + candidate_rmap_list = + route_map_get_index_list(&rn, prefix, table); + if (!rn) + break; + + /* If the index at the head of the list is of seq higher + * than that in best_index, ignore the list and get the + * parent node's list. + */ + head_index = (struct route_map_index *)(listgetdata( + listhead(candidate_rmap_list))); + if (best_index && head_index + && (best_index->pref < head_index->pref)) { + route_unlock_node(rn); + continue; + } + + for (ALL_LIST_ELEMENTS(candidate_rmap_list, ln, nn, index)) { + /* If the index is of seq higher than that in + * best_index, ignore the list and get the parent + * node's list. + */ + if (best_index && (best_index->pref < index->pref)) + break; + + ret = route_map_apply_match(&index->match_list, prefix, + type, object); + + if (ret == RMAP_MATCH) { + *match_ret = ret; + best_index = index; + break; + } else if (ret == RMAP_NOOP) { + /* + * If match_ret is denymatch, even if we see + * more noops, we retain this return value and + * return this eventually if there are no + * matches. + * If a best match route-map index already + * exists, do not reset the match_ret. + */ + if (!best_index && (*match_ret != RMAP_NOMATCH)) + *match_ret = ret; + } else { + /* + * ret is RMAP_NOMATCH. + * If a best match route-map index already + * exists, do not reset the match_ret. + */ + if (!best_index) + *match_ret = ret; } } - } - /* Finally route-map does not match at all. */ - return RMAP_DENYMATCH; -} -void route_map_add_hook(void (*func)(const char *)) -{ - route_map_master.add_hook = func; -} + route_unlock_node(rn); -void route_map_delete_hook(void (*func)(const char *)) -{ - route_map_master.delete_hook = func; -} + } while (rn); -void route_map_event_hook(void (*func)(route_map_event_t, const char *)) -{ - route_map_master.event_hook = func; + return best_index; } -/* Routines for route map dependency lists and dependency processing */ -static bool route_map_rmap_hash_cmp(const void *p1, const void *p2) +static int route_map_candidate_list_cmp(struct route_map_index *idx1, + struct route_map_index *idx2) { - return (strcmp((const char *)p1, (const char *)p2) == 0); + if (!idx1) + return -1; + if (!idx2) + return 1; + + return (idx1->pref - idx2->pref); } -static bool route_map_dep_hash_cmp(const void *p1, const void *p2) +/* + * This function adds the route-map index into the default route's + * route-node in the route-map's IPv4/IPv6 prefix-table. + */ +static void route_map_pfx_table_add_default(afi_t afi, + struct route_map_index *index) { + struct route_node *rn = NULL; + struct list *rmap_candidate_list = NULL; + struct prefix p; + bool updated_rn = false; + struct route_table *table = NULL; - return (strcmp(((const struct route_map_dep *)p1)->dep_name, - (const char *)p2) - == 0); -} + memset(&p, 0, sizeof(p)); + p.family = afi2family(afi); + p.prefixlen = 0; -static void route_map_clear_reference(struct hash_bucket *bucket, void *arg) -{ - struct route_map_dep *dep = (struct route_map_dep *)bucket->data; - char *rmap_name; + if (p.family == AF_INET) { + table = index->map->ipv4_prefix_table; + if (!table) + index->map->ipv4_prefix_table = route_table_init(); - if (arg) { - rmap_name = - (char *)hash_release(dep->dep_rmap_hash, (void *)arg); - if (rmap_name) { - XFREE(MTYPE_ROUTE_MAP_NAME, rmap_name); - } - if (!dep->dep_rmap_hash->count) { - dep = hash_release(dep->this_hash, - (void *)dep->dep_name); - hash_free(dep->dep_rmap_hash); - XFREE(MTYPE_ROUTE_MAP_NAME, dep->dep_name); - XFREE(MTYPE_ROUTE_MAP_DEP, dep); - } + table = index->map->ipv4_prefix_table; + } else { + table = index->map->ipv6_prefix_table; + if (!table) + index->map->ipv6_prefix_table = route_table_init(); + + table = index->map->ipv6_prefix_table; } -} -static void route_map_clear_all_references(char *rmap_name) -{ - int i; + /* Add default route to table */ + rn = route_node_get(table, &p); - for (i = 1; i < ROUTE_MAP_DEP_MAX; i++) { - hash_iterate(route_map_dep_hash[i], route_map_clear_reference, - (void *)rmap_name); + if (!rn) + return; + + if (!rn->info) { + rmap_candidate_list = list_new(); + rmap_candidate_list->cmp = + (int (*)(void *, void *))route_map_candidate_list_cmp; + rn->info = rmap_candidate_list; + } else { + rmap_candidate_list = (struct list *)rn->info; + updated_rn = true; } + + listnode_add_sort_nodup(rmap_candidate_list, index); + if (updated_rn) + route_unlock_node(rn); } -static void *route_map_dep_hash_alloc(void *p) +/* + * This function removes the route-map index from the default route's + * route-node in the route-map's IPv4/IPv6 prefix-table. + */ +static void route_map_pfx_table_del_default(afi_t afi, + struct route_map_index *index) { - char *dep_name = (char *)p; - struct route_map_dep *dep_entry; + struct route_node *rn = NULL; + struct list *rmap_candidate_list = NULL; + struct prefix p; + struct route_table *table = NULL; - dep_entry = XCALLOC(MTYPE_ROUTE_MAP_DEP, sizeof(struct route_map_dep)); - dep_entry->dep_name = XSTRDUP(MTYPE_ROUTE_MAP_NAME, dep_name); - dep_entry->dep_rmap_hash = - hash_create_size(8, route_map_dep_hash_make_key, - route_map_rmap_hash_cmp, "Route Map Dep Hash"); - dep_entry->this_hash = NULL; + memset(&p, 0, sizeof(p)); + p.family = afi2family(afi); + p.prefixlen = 0; + + if (p.family == AF_INET) + table = index->map->ipv4_prefix_table; + else + table = index->map->ipv6_prefix_table; + + /* Remove RMAP index from default route in table */ + rn = route_node_lookup(table, &p); + if (!rn || !rn->info) + return; + + rmap_candidate_list = (struct list *)rn->info; + + listnode_delete(rmap_candidate_list, index); - return ((void *)dep_entry); + if (listcount(rmap_candidate_list) == 0) { + list_delete(&rmap_candidate_list); + rn->info = NULL; + route_unlock_node(rn); + } + route_unlock_node(rn); } -static void *route_map_name_hash_alloc(void *p) +/* + * This function adds the route-map index to the route-node for + * the prefix-entry in the route-map's IPv4/IPv6 prefix-table. + */ +static void route_map_pfx_table_add(struct route_table *table, + struct route_map_index *index, + struct prefix_list_entry *pentry) { - return ((void *)XSTRDUP(MTYPE_ROUTE_MAP_NAME, (const char *)p)); + struct route_node *rn = NULL; + struct list *rmap_candidate_list = NULL; + bool updated_rn = false; + + rn = route_node_get(table, &pentry->prefix); + if (!rn) + return; + + if (!rn->info) { + rmap_candidate_list = list_new(); + rmap_candidate_list->cmp = + (int (*)(void *, void *))route_map_candidate_list_cmp; + rn->info = rmap_candidate_list; + } else { + rmap_candidate_list = (struct list *)rn->info; + updated_rn = true; + } + + listnode_add_sort_nodup(rmap_candidate_list, index); + if (updated_rn) + route_unlock_node(rn); } -static unsigned int route_map_dep_hash_make_key(void *p) +/* + * This function removes the route-map index from the route-node for + * the prefix-entry in the route-map's IPv4/IPv6 prefix-table. + */ +static void route_map_pfx_table_del(struct route_table *table, + struct route_map_index *index, + struct prefix_list_entry *pentry) { - return (string_hash_make((char *)p)); + struct route_node *rn = NULL; + struct list *rmap_candidate_list = NULL; + + rn = route_node_lookup(table, &pentry->prefix); + if (!rn || !rn->info) + return; + + rmap_candidate_list = (struct list *)rn->info; + + listnode_delete(rmap_candidate_list, index); + + if (listcount(rmap_candidate_list) == 0) { + list_delete(&rmap_candidate_list); + rn->info = NULL; + route_unlock_node(rn); + } + route_unlock_node(rn); } -static void route_map_print_dependency(struct hash_bucket *bucket, void *data) +/* This function checks for the presence of an IPv4 prefix-list + * match rule in the given route-map index. + */ +static bool route_map_is_ip_pfx_list_rule_present(struct route_map_index *index) { - char *rmap_name = (char *)bucket->data; - char *dep_name = (char *)data; + struct route_map_rule_list *match_list = NULL; + struct route_map_rule *rule = NULL; - zlog_debug("%s: Dependency for %s: %s", __FUNCTION__, dep_name, - rmap_name); + match_list = &index->match_list; + for (rule = match_list->head; rule; rule = rule->next) + if (IS_RULE_IPv4_PREFIX_LIST(rule->cmd->str)) + return true; + + return false; } -static int route_map_dep_update(struct hash *dephash, const char *dep_name, - const char *rmap_name, route_map_event_t type) +/* This function checks for the presence of an IPv6 prefix-list + * match rule in the given route-map index. + */ +static bool +route_map_is_ipv6_pfx_list_rule_present(struct route_map_index *index) { - struct route_map_dep *dep = NULL; - char *ret_map_name; - char *dname, *rname; - int ret = 0; + struct route_map_rule_list *match_list = NULL; + struct route_map_rule *rule = NULL; - dname = XSTRDUP(MTYPE_ROUTE_MAP_NAME, dep_name); - rname = XSTRDUP(MTYPE_ROUTE_MAP_NAME, rmap_name); + match_list = &index->match_list; + for (rule = match_list->head; rule; rule = rule->next) + if (IS_RULE_IPv6_PREFIX_LIST(rule->cmd->str)) + return true; - switch (type) { - case RMAP_EVENT_PLIST_ADDED: - case RMAP_EVENT_CLIST_ADDED: - case RMAP_EVENT_ECLIST_ADDED: - case RMAP_EVENT_ASLIST_ADDED: - case RMAP_EVENT_LLIST_ADDED: - case RMAP_EVENT_CALL_ADDED: - case RMAP_EVENT_FILTER_ADDED: - if (rmap_debug) - zlog_debug("%s: Adding dependency for %s in %s", - __FUNCTION__, dep_name, rmap_name); - dep = (struct route_map_dep *)hash_get( - dephash, dname, route_map_dep_hash_alloc); - if (!dep) { - ret = -1; - goto out; - } + return false; +} - if (!dep->this_hash) - dep->this_hash = dephash; +/* This function does the following: + * 1) If plist_name is not present, search for a IPv4 or IPv6 prefix-list + * match clause (based on the afi passed to this foo) and get the + * prefix-list name. + * 2) Look up the prefix-list using the name. + * 3) If the prefix-list is not found then, add the index to the IPv4/IPv6 + * default-route's node in the trie (based on the afi passed to this foo). + * 4) If the prefix-list is found then, remove the index from the IPv4/IPv6 + * default-route's node in the trie (based on the afi passed to this foo). + * 5) If a prefix-entry is passed then, create a route-node for this entry and + * add this index to the route-node. + * 6) If prefix-entry is not passed then, for every prefix-entry in the + * prefix-list, create a route-node for this entry and + * add this index to the route-node. + */ +static void route_map_add_plist_entries(afi_t afi, + struct route_map_index *index, + const char *plist_name, + struct prefix_list_entry *entry) +{ + struct route_map_rule_list *match_list = NULL; + struct route_map_rule *match = NULL; + struct prefix_list *plist = NULL; + struct prefix_list_entry *pentry = NULL; + bool plist_rule_is_present = false; - hash_get(dep->dep_rmap_hash, rname, route_map_name_hash_alloc); - break; - case RMAP_EVENT_PLIST_DELETED: - case RMAP_EVENT_CLIST_DELETED: - case RMAP_EVENT_ECLIST_DELETED: - case RMAP_EVENT_ASLIST_DELETED: - case RMAP_EVENT_LLIST_DELETED: - case RMAP_EVENT_CALL_DELETED: - case RMAP_EVENT_FILTER_DELETED: - if (rmap_debug) - zlog_debug("%s: Deleting dependency for %s in %s", - __FUNCTION__, dep_name, rmap_name); - dep = (struct route_map_dep *)hash_get(dephash, dname, NULL); - if (!dep) { - goto out; + if (!plist_name) { + match_list = &index->match_list; + + for (match = match_list->head; match; match = match->next) { + if (afi == AFI_IP) { + if (IS_RULE_IPv4_PREFIX_LIST(match->cmd->str)) { + plist_rule_is_present = true; + break; + } + } else { + if (IS_RULE_IPv6_PREFIX_LIST(match->cmd->str)) { + plist_rule_is_present = true; + break; + } + } } - ret_map_name = (char *)hash_release(dep->dep_rmap_hash, rname); - XFREE(MTYPE_ROUTE_MAP_NAME, ret_map_name); + if (plist_rule_is_present) + plist = prefix_list_lookup(afi, match->rule_str); + } else { + plist = prefix_list_lookup(afi, plist_name); + } - if (!dep->dep_rmap_hash->count) { - dep = hash_release(dephash, dname); - hash_free(dep->dep_rmap_hash); - XFREE(MTYPE_ROUTE_MAP_NAME, dep->dep_name); - XFREE(MTYPE_ROUTE_MAP_DEP, dep); - dep = NULL; - } - break; - default: - break; + if (!plist) { + route_map_pfx_table_add_default(afi, index); + return; } - if (dep) { - if (rmap_debug) - hash_iterate(dep->dep_rmap_hash, - route_map_print_dependency, dname); + /* Default entry should be deleted only if the first entry of the + * prefix-list is created. + */ + if (entry) { + if (plist->count == 1) + route_map_pfx_table_del_default(afi, index); + } else { + route_map_pfx_table_del_default(afi, index); } -out: - XFREE(MTYPE_ROUTE_MAP_NAME, rname); - XFREE(MTYPE_ROUTE_MAP_NAME, dname); - return ret; + if (entry) { + if (afi == AFI_IP) { + route_map_pfx_table_add(index->map->ipv4_prefix_table, + index, entry); + } else { + route_map_pfx_table_add(index->map->ipv6_prefix_table, + index, entry); + } + } else { + for (pentry = plist->head; pentry; pentry = pentry->next) { + if (afi == AFI_IP) { + route_map_pfx_table_add( + index->map->ipv4_prefix_table, index, + pentry); + } else { + route_map_pfx_table_add( + index->map->ipv6_prefix_table, index, + pentry); + } + } + } } -static struct hash *route_map_get_dep_hash(route_map_event_t event) +/* This function does the following: + * 1) If plist_name is not present, search for a IPv4 or IPv6 prefix-list + * match clause (based on the afi passed to this foo) and get the + * prefix-list name. + * 2) Look up the prefix-list using the name. + * 3) If the prefix-list is not found then, delete the index from the IPv4/IPv6 + * default-route's node in the trie (based on the afi passed to this foo). + * 4) If a prefix-entry is passed then, remove this index from the route-node + * for the prefix in this prefix-entry. + * 5) If prefix-entry is not passed then, for every prefix-entry in the + * prefix-list, remove this index from the route-node + * for the prefix in this prefix-entry. + */ +static void route_map_del_plist_entries(afi_t afi, + struct route_map_index *index, + const char *plist_name, + struct prefix_list_entry *entry) { - struct hash *upd8_hash = NULL; + struct route_map_rule_list *match_list = NULL; + struct route_map_rule *match = NULL; + struct prefix_list *plist = NULL; + struct prefix_list_entry *pentry = NULL; + bool plist_rule_is_present = false; - switch (event) { - case RMAP_EVENT_PLIST_ADDED: - case RMAP_EVENT_PLIST_DELETED: - upd8_hash = route_map_dep_hash[ROUTE_MAP_DEP_PLIST]; - break; - case RMAP_EVENT_CLIST_ADDED: - case RMAP_EVENT_CLIST_DELETED: - upd8_hash = route_map_dep_hash[ROUTE_MAP_DEP_CLIST]; - break; - case RMAP_EVENT_ECLIST_ADDED: - case RMAP_EVENT_ECLIST_DELETED: - upd8_hash = route_map_dep_hash[ROUTE_MAP_DEP_ECLIST]; - break; - case RMAP_EVENT_ASLIST_ADDED: - case RMAP_EVENT_ASLIST_DELETED: - upd8_hash = route_map_dep_hash[ROUTE_MAP_DEP_ASPATH]; - break; - case RMAP_EVENT_LLIST_ADDED: - case RMAP_EVENT_LLIST_DELETED: - upd8_hash = route_map_dep_hash[ROUTE_MAP_DEP_LCLIST]; - break; - case RMAP_EVENT_CALL_ADDED: - case RMAP_EVENT_CALL_DELETED: - upd8_hash = route_map_dep_hash[ROUTE_MAP_DEP_RMAP]; - break; - case RMAP_EVENT_FILTER_ADDED: - case RMAP_EVENT_FILTER_DELETED: - upd8_hash = route_map_dep_hash[ROUTE_MAP_DEP_FILTER]; - break; - default: - upd8_hash = NULL; - break; - } - return (upd8_hash); -} + if (!plist_name) { + match_list = &index->match_list; -static void route_map_process_dependency(struct hash_bucket *bucket, void *data) -{ - char *rmap_name = (char *)bucket->data; - route_map_event_t type = (route_map_event_t)(ptrdiff_t)data; + for (match = match_list->head; match; match = match->next) { + if (afi == AFI_IP) { + if (IS_RULE_IPv4_PREFIX_LIST(match->cmd->str)) { + plist_rule_is_present = true; + break; + } + } else { + if (IS_RULE_IPv6_PREFIX_LIST(match->cmd->str)) { + plist_rule_is_present = true; + break; + } + } + } - if (rmap_debug) - zlog_debug("%s: Notifying %s of dependency", - __FUNCTION__, rmap_name); - if (route_map_master.event_hook) - (*route_map_master.event_hook)(type, rmap_name); -} + if (plist_rule_is_present) + plist = prefix_list_lookup(afi, match->rule_str); + } else { + plist = prefix_list_lookup(afi, plist_name); + } -void route_map_upd8_dependency(route_map_event_t type, const char *arg, - const char *rmap_name) -{ - struct hash *upd8_hash = NULL; + if (!plist) { + route_map_pfx_table_del_default(afi, index); + return; + } - if ((upd8_hash = route_map_get_dep_hash(type))) { - route_map_dep_update(upd8_hash, arg, rmap_name, type); + if (entry) { + if (afi == AFI_IP) { + route_map_pfx_table_del(index->map->ipv4_prefix_table, + index, entry); + } else { + route_map_pfx_table_del(index->map->ipv6_prefix_table, + index, entry); + } + } else { + for (pentry = plist->head; pentry; pentry = pentry->next) { + if (afi == AFI_IP) { + route_map_pfx_table_del( + index->map->ipv4_prefix_table, index, + pentry); + } else { + route_map_pfx_table_del( + index->map->ipv6_prefix_table, index, + pentry); + } + } + } +} - if (type == RMAP_EVENT_CALL_ADDED) { - /* Execute hook. */ - if (route_map_master.add_hook) - (*route_map_master.add_hook)(rmap_name); - } else if (type == RMAP_EVENT_CALL_DELETED) { - /* Execute hook. */ - if (route_map_master.delete_hook) - (*route_map_master.delete_hook)(rmap_name); +/* + * This function handles the cases where a prefix-list is added/removed + * as a match command from a particular route-map index. + * It updates the prefix-table of the route-map accordingly. + */ +static void route_map_trie_update(afi_t afi, route_map_event_t event, + struct route_map_index *index, + const char *plist_name) +{ + if (event == RMAP_EVENT_PLIST_ADDED) { + if (afi == AFI_IP) { + if (!route_map_is_ipv6_pfx_list_rule_present(index)) { + route_map_pfx_table_del_default(AFI_IP6, index); + route_map_add_plist_entries(afi, index, + plist_name, NULL); + } else { + route_map_del_plist_entries(AFI_IP6, index, + NULL, NULL); + } + } else { + if (!route_map_is_ip_pfx_list_rule_present(index)) { + route_map_pfx_table_del_default(AFI_IP, index); + route_map_add_plist_entries(afi, index, + plist_name, NULL); + } else { + route_map_del_plist_entries(AFI_IP, index, NULL, + NULL); + } + } + } else if (event == RMAP_EVENT_PLIST_DELETED) { + if (afi == AFI_IP) { + route_map_del_plist_entries(afi, index, plist_name, + NULL); + + /* If IPv6 prefix-list match rule is not present, + * add this index to the IPv4 default route's trie + * node. + * Also, add this index to the trie nodes created + * for each of the prefix-entries within the IPv6 + * prefix-list, if the IPv6 prefix-list match rule + * is present. Else, add this index to the IPv6 + * default route's trie node. + */ + if (!route_map_is_ipv6_pfx_list_rule_present(index)) + route_map_pfx_table_add_default(afi, index); + + route_map_add_plist_entries(AFI_IP6, index, NULL, NULL); + } else { + route_map_del_plist_entries(afi, index, plist_name, + NULL); + + /* If IPv4 prefix-list match rule is not present, + * add this index to the IPv6 default route's trie + * node. + * Also, add this index to the trie nodes created + * for each of the prefix-entries within the IPv4 + * prefix-list, if the IPv4 prefix-list match rule + * is present. Else, add this index to the IPv4 + * default route's trie node. + */ + if (!route_map_is_ip_pfx_list_rule_present(index)) + route_map_pfx_table_add_default(afi, index); + + route_map_add_plist_entries(AFI_IP, index, NULL, NULL); } } } -void route_map_notify_dependencies(const char *affected_name, - route_map_event_t event) +/* + * This function handles the cases where a route-map index and + * prefix-list is added/removed. + * It updates the prefix-table of the route-map accordingly. + */ +static void route_map_pfx_tbl_update(route_map_event_t event, + struct route_map_index *index, afi_t afi, + const char *plist_name) { - struct route_map_dep *dep; - struct hash *upd8_hash; - char *name; + struct route_map *rmap = NULL; - if (!affected_name) + if (!index) return; - name = XSTRDUP(MTYPE_ROUTE_MAP_NAME, affected_name); - - if ((upd8_hash = route_map_get_dep_hash(event)) == NULL) { - XFREE(MTYPE_ROUTE_MAP_NAME, name); + if (event == RMAP_EVENT_INDEX_ADDED) { + route_map_pfx_table_add_default(AFI_IP, index); + route_map_pfx_table_add_default(AFI_IP6, index); return; } - dep = (struct route_map_dep *)hash_get(upd8_hash, name, NULL); - if (dep) { - if (!dep->this_hash) - dep->this_hash = upd8_hash; + if (event == RMAP_EVENT_INDEX_DELETED) { + route_map_pfx_table_del_default(AFI_IP, index); + route_map_pfx_table_del_default(AFI_IP6, index); - hash_iterate(dep->dep_rmap_hash, route_map_process_dependency, - (void *)event); + if ((index->map->head == NULL) && (index->map->tail == NULL)) { + rmap = index->map; + + if (rmap->ipv4_prefix_table) { + route_table_finish(rmap->ipv4_prefix_table); + rmap->ipv4_prefix_table = NULL; + } + + if (rmap->ipv6_prefix_table) { + route_table_finish(rmap->ipv6_prefix_table); + rmap->ipv6_prefix_table = NULL; + } + } + return; } - XFREE(MTYPE_ROUTE_MAP_NAME, name); + /* Handle prefix-list match rule addition/deletion. + */ + route_map_trie_update(afi, event, index, plist_name); } - -/* VTY related functions. */ -DEFUN (match_interface, - match_interface_cmd, - "match interface WORD", - MATCH_STR - "match first hop interface of route\n" - "Interface name\n") +/* + * This function handles the cases where a new prefix-entry is added to + * a prefix-list or, an existing prefix-entry is removed from the prefix-list. + * It updates the prefix-table of the route-map accordingly. + */ +static void route_map_pentry_update(route_map_event_t event, + const char *plist_name, + struct route_map_index *index, + struct prefix_list_entry *pentry) { - int idx_word = 2; - VTY_DECLVAR_CONTEXT(route_map_index, index); + struct prefix_list *plist = NULL; + afi_t afi; + unsigned char family = pentry->prefix.family; - if (rmap_match_set_hook.match_interface) - return rmap_match_set_hook.match_interface( - vty, index, "interface", argv[idx_word]->arg, - RMAP_EVENT_MATCH_ADDED); - return CMD_SUCCESS; + if (family == AF_INET) { + afi = AFI_IP; + plist = prefix_list_lookup(AFI_IP, plist_name); + } else { + afi = AFI_IP6; + plist = prefix_list_lookup(AFI_IP6, plist_name); + } + + if (event == RMAP_EVENT_PLIST_ADDED) { + if (afi == AFI_IP) { + if (!route_map_is_ipv6_pfx_list_rule_present(index)) + route_map_add_plist_entries(afi, index, + plist_name, pentry); + } else { + if (!route_map_is_ip_pfx_list_rule_present(index)) + route_map_add_plist_entries(afi, index, + plist_name, pentry); + } + } else if (event == RMAP_EVENT_PLIST_DELETED) { + route_map_del_plist_entries(afi, index, plist_name, pentry); + + if (plist->count == 1) { + if (afi == AFI_IP) { + if (!route_map_is_ipv6_pfx_list_rule_present( + index)) + route_map_pfx_table_add_default(afi, + index); + } else { + if (!route_map_is_ip_pfx_list_rule_present( + index)) + route_map_pfx_table_add_default(afi, + index); + } + } + } } -DEFUN (no_match_interface, - no_match_interface_cmd, - "no match interface [WORD]", - NO_STR - MATCH_STR - "Match first hop interface of route\n" - "Interface name\n") +static void route_map_pentry_process_dependency(struct hash_bucket *backet, + void *data) { - char *iface = (argc == 4) ? argv[3]->arg : NULL; - VTY_DECLVAR_CONTEXT(route_map_index, index); + char *rmap_name = NULL; + struct route_map *rmap = NULL; + struct route_map_index *index = NULL; + struct route_map_rule_list *match_list = NULL; + struct route_map_rule *match = NULL; + struct route_map_dep_data *dep_data = NULL; + struct route_map_pentry_dep *pentry_dep = + (struct route_map_pentry_dep *)data; + unsigned char family = pentry_dep->pentry->prefix.family; + + dep_data = (struct route_map_dep_data *)backet->data; + if (!dep_data) + return; - if (rmap_match_set_hook.no_match_interface) - return rmap_match_set_hook.no_match_interface( - vty, index, "interface", iface, - RMAP_EVENT_MATCH_DELETED); - return CMD_SUCCESS; -} + rmap_name = dep_data->rname; + rmap = route_map_lookup_by_name(rmap_name); + if (!rmap || !rmap->head) + return; + for (index = rmap->head; index; index = index->next) { + match_list = &index->match_list; -DEFUN (match_ip_address, - match_ip_address_cmd, - "match ip address <(1-199)|(1300-2699)|WORD>", - MATCH_STR - IP_STR - "Match address of route\n" - "IP access-list number\n" - "IP access-list number (expanded range)\n" - "IP Access-list name\n") -{ - int idx_acl = 3; - VTY_DECLVAR_CONTEXT(route_map_index, index); + if (!match_list) + continue; - if (rmap_match_set_hook.match_ip_address) - return rmap_match_set_hook.match_ip_address( - vty, index, "ip address", argv[idx_acl]->arg, - RMAP_EVENT_FILTER_ADDED); - return CMD_SUCCESS; + for (match = match_list->head; match; match = match->next) { + if (strcmp(match->rule_str, pentry_dep->plist_name) + == 0) { + if (IS_RULE_IPv4_PREFIX_LIST(match->cmd->str) + && family == AF_INET) { + route_map_pentry_update( + pentry_dep->event, + pentry_dep->plist_name, index, + pentry_dep->pentry); + } else if (IS_RULE_IPv6_PREFIX_LIST( + match->cmd->str) + && family == AF_INET6) { + route_map_pentry_update( + pentry_dep->event, + pentry_dep->plist_name, index, + pentry_dep->pentry); + } + } + } + } } +void route_map_notify_pentry_dependencies(const char *affected_name, + struct prefix_list_entry *pentry, + route_map_event_t event) +{ + struct route_map_dep *dep = NULL; + struct hash *upd8_hash = NULL; + struct route_map_pentry_dep pentry_dep; -DEFUN (no_match_ip_address, - no_match_ip_address_cmd, - "no match ip address [<(1-199)|(1300-2699)|WORD>]", - NO_STR - MATCH_STR - IP_STR - "Match address of route\n" - "IP access-list number\n" - "IP access-list number (expanded range)\n" - "IP Access-list name\n") -{ - int idx_word = 4; - VTY_DECLVAR_CONTEXT(route_map_index, index); + if (!affected_name || !pentry) + return; + + upd8_hash = route_map_get_dep_hash(event); + if (!upd8_hash) + return; - if (rmap_match_set_hook.no_match_ip_address) { - if (argc <= idx_word) - return rmap_match_set_hook.no_match_ip_address( - vty, index, "ip address", NULL, - RMAP_EVENT_FILTER_DELETED); - return rmap_match_set_hook.no_match_ip_address( - vty, index, "ip address", argv[idx_word]->arg, - RMAP_EVENT_FILTER_DELETED); + dep = (struct route_map_dep *)hash_get(upd8_hash, (void *)affected_name, + NULL); + if (dep) { + if (!dep->this_hash) + dep->this_hash = upd8_hash; + + memset(&pentry_dep, 0, sizeof(struct route_map_pentry_dep)); + pentry_dep.pentry = pentry; + pentry_dep.plist_name = affected_name; + pentry_dep.event = event; + + hash_iterate(dep->dep_rmap_hash, + route_map_pentry_process_dependency, + (void *)&pentry_dep); } - return CMD_SUCCESS; } +/* Apply route map's each index to the object. + + The matrix for a route-map looks like this: + (note, this includes the description for the "NEXT" + and "GOTO" frobs now + + | Match | No Match | No op + |-----------|--------------|------- + permit | action | cont | cont. + | | default:deny | default:permit + -------------------+----------------------- + | deny | cont | cont. + deny | | default:deny | default:permit + |-----------|--------------|-------- + + action) + -Apply Set statements, accept route + -If Call statement is present jump to the specified route-map, if it + denies the route we finish. + -If NEXT is specified, goto NEXT statement + -If GOTO is specified, goto the first clause where pref > nextpref + -If nothing is specified, do as Cisco and finish + deny) + -Route is denied by route-map. + cont) + -Goto Next index + + If we get no matches after we've processed all updates, then the route + is dropped too. + + Some notes on the new "CALL", "NEXT" and "GOTO" + call WORD - If this clause is matched, then the set statements + are executed and then we jump to route-map 'WORD'. If + this route-map denies the route, we finish, in other + case we + do whatever the exit policy (EXIT, NEXT or GOTO) tells. + on-match next - If this clause is matched, then the set statements + are executed and then we drop through to the next clause + on-match goto n - If this clause is matched, then the set statments + are executed and then we goto the nth clause, or the + first clause greater than this. In order to ensure + route-maps *always* exit, you cannot jump backwards. + Sorry ;) -DEFUN (match_ip_address_prefix_list, - match_ip_address_prefix_list_cmd, - "match ip address prefix-list WORD", - MATCH_STR - IP_STR - "Match address of route\n" - "Match entries of prefix-lists\n" - "IP prefix-list name\n") + We need to make sure our route-map processing matches the above +*/ +route_map_result_t route_map_apply(struct route_map *map, + const struct prefix *prefix, + route_map_object_t type, void *object) { - int idx_word = 4; - VTY_DECLVAR_CONTEXT(route_map_index, index); + static int recursion = 0; + enum route_map_cmd_result_t match_ret = RMAP_NOMATCH; + route_map_result_t ret = RMAP_PERMITMATCH; + struct route_map_index *index = NULL; + struct route_map_rule *set = NULL; + char buf[PREFIX_STRLEN]; + bool skip_match_clause = false; - if (rmap_match_set_hook.match_ip_address_prefix_list) - return rmap_match_set_hook.match_ip_address_prefix_list( - vty, index, "ip address prefix-list", - argv[idx_word]->arg, RMAP_EVENT_PLIST_ADDED); - return CMD_SUCCESS; -} + if (recursion > RMAP_RECURSION_LIMIT) { + flog_warn( + EC_LIB_RMAP_RECURSION_LIMIT, + "route-map recursion limit (%d) reached, discarding route", + RMAP_RECURSION_LIMIT); + recursion = 0; + return RMAP_DENYMATCH; + } + if (map == NULL || map->head == NULL) { + ret = RMAP_DENYMATCH; + goto route_map_apply_end; + } -DEFUN (no_match_ip_address_prefix_list, - no_match_ip_address_prefix_list_cmd, - "no match ip address prefix-list [WORD]", - NO_STR - MATCH_STR - IP_STR - "Match address of route\n" - "Match entries of prefix-lists\n" - "IP prefix-list name\n") -{ - int idx_word = 5; - VTY_DECLVAR_CONTEXT(route_map_index, index); + map->applied++; - if (rmap_match_set_hook.no_match_ip_address_prefix_list) { - if (argc <= idx_word) - return rmap_match_set_hook - .no_match_ip_address_prefix_list( - vty, index, "ip address prefix-list", - NULL, RMAP_EVENT_PLIST_DELETED); - return rmap_match_set_hook.no_match_ip_address_prefix_list( - vty, index, "ip address prefix-list", - argv[idx_word]->arg, RMAP_EVENT_PLIST_DELETED); + if ((!map->optimization_disabled) + && (map->ipv4_prefix_table || map->ipv6_prefix_table)) { + index = route_map_get_index(map, prefix, type, object, + (uint8_t *)&match_ret); + if (index) { + if (rmap_debug) + zlog_debug( + "Best match route-map: %s, sequence: %d for pfx: %s, result: %s", + map->name, index->pref, + prefix2str(prefix, buf, sizeof(buf)), + route_map_cmd_result_str(match_ret)); + } else { + if (rmap_debug) + zlog_debug( + "No best match sequence for pfx: %s in route-map: %s, result: %s", + prefix2str(prefix, buf, sizeof(buf)), + map->name, + route_map_cmd_result_str(match_ret)); + /* + * No index matches this prefix. Return deny unless, + * match_ret = RMAP_NOOP. + */ + if (match_ret == RMAP_NOOP) + ret = RMAP_PERMITMATCH; + else + ret = RMAP_DENYMATCH; + goto route_map_apply_end; + } + skip_match_clause = true; + } else { + index = map->head; } - return CMD_SUCCESS; -} - -DEFUN (match_ip_next_hop, - match_ip_next_hop_cmd, - "match ip next-hop <(1-199)|(1300-2699)|WORD>", - MATCH_STR - IP_STR - "Match next-hop address of route\n" - "IP access-list number\n" - "IP access-list number (expanded range)\n" - "IP Access-list name\n") -{ - int idx_acl = 3; - VTY_DECLVAR_CONTEXT(route_map_index, index); + for (; index; index = index->next) { + if (!skip_match_clause) { + index->applied++; + /* Apply this index. */ + match_ret = route_map_apply_match(&index->match_list, + prefix, type, object); + if (rmap_debug) { + zlog_debug( + "Route-map: %s, sequence: %d, prefix: %s, result: %s", + map->name, index->pref, + prefix2str(prefix, buf, sizeof(buf)), + route_map_cmd_result_str(match_ret)); + } + } else + skip_match_clause = false; - if (rmap_match_set_hook.match_ip_next_hop) - return rmap_match_set_hook.match_ip_next_hop( - vty, index, "ip next-hop", argv[idx_acl]->arg, - RMAP_EVENT_FILTER_ADDED); - return CMD_SUCCESS; -} + /* Now we apply the matrix from above */ + if (match_ret == RMAP_NOOP) + /* + * Do not change the return value. Retain the previous + * return value. Previous values can be: + * 1)permitmatch (if a nomatch was never + * seen before in this route-map.) + * 2)denymatch (if a nomatch was seen earlier in one + * of the previous sequences) + */ + + /* + * 'cont' from matrix - continue to next route-map + * sequence + */ + continue; + else if (match_ret == RMAP_NOMATCH) { + + /* + * The return value is now changed to denymatch. + * So from here on out, even if we see more noops, + * we retain this return value and return this + * eventually if there are no matches. + */ + ret = RMAP_DENYMATCH; + + /* + * 'cont' from matrix - continue to next route-map + * sequence + */ + continue; + } else if (match_ret == RMAP_MATCH) { + if (index->type == RMAP_PERMIT) + /* 'action' */ + { + /* Match succeeded, rmap is of type permit */ + ret = RMAP_PERMITMATCH; -DEFUN (no_match_ip_next_hop, - no_match_ip_next_hop_cmd, - "no match ip next-hop [<(1-199)|(1300-2699)|WORD>]", - NO_STR - MATCH_STR - IP_STR - "Match next-hop address of route\n" - "IP access-list number\n" - "IP access-list number (expanded range)\n" - "IP Access-list name\n") -{ - int idx_word = 4; - VTY_DECLVAR_CONTEXT(route_map_index, index); + /* permit+match must execute sets */ + for (set = index->set_list.head; set; + set = set->next) + /* + * set cmds return RMAP_OKAY or + * RMAP_ERROR. We do not care if + * set succeeded or not. So, ignore + * return code. + */ + (void) (*set->cmd->func_apply)( + set->value, prefix, type, + object); - if (rmap_match_set_hook.no_match_ip_next_hop) { - if (argc <= idx_word) - return rmap_match_set_hook.no_match_ip_next_hop( - vty, index, "ip next-hop", NULL, - RMAP_EVENT_FILTER_DELETED); - return rmap_match_set_hook.no_match_ip_next_hop( - vty, index, "ip next-hop", argv[idx_word]->arg, - RMAP_EVENT_FILTER_DELETED); - } - return CMD_SUCCESS; -} + /* Call another route-map if available */ + if (index->nextrm) { + struct route_map *nextrm = + route_map_lookup_by_name( + index->nextrm); + if (nextrm) /* Target route-map found, + jump to it */ + { + recursion++; + ret = route_map_apply( + nextrm, prefix, type, + object); + recursion--; + } -DEFUN (match_ip_next_hop_prefix_list, - match_ip_next_hop_prefix_list_cmd, - "match ip next-hop prefix-list WORD", - MATCH_STR - IP_STR - "Match next-hop address of route\n" - "Match entries of prefix-lists\n" - "IP prefix-list name\n") -{ - int idx_word = 4; - VTY_DECLVAR_CONTEXT(route_map_index, index); + /* If nextrm returned 'deny', finish. */ + if (ret == RMAP_DENYMATCH) + goto route_map_apply_end; + } - if (rmap_match_set_hook.match_ip_next_hop_prefix_list) - return rmap_match_set_hook.match_ip_next_hop_prefix_list( - vty, index, "ip next-hop prefix-list", - argv[idx_word]->arg, RMAP_EVENT_PLIST_ADDED); - return CMD_SUCCESS; -} + switch (index->exitpolicy) { + case RMAP_EXIT: + goto route_map_apply_end; + case RMAP_NEXT: + continue; + case RMAP_GOTO: { + /* Find the next clause to jump to */ + struct route_map_index *next = + index->next; + int nextpref = index->nextpref; -DEFUN (no_match_ip_next_hop_prefix_list, - no_match_ip_next_hop_prefix_list_cmd, - "no match ip next-hop prefix-list [WORD]", - NO_STR - MATCH_STR - IP_STR - "Match next-hop address of route\n" - "Match entries of prefix-lists\n" - "IP prefix-list name\n") -{ - int idx_word = 5; - VTY_DECLVAR_CONTEXT(route_map_index, index); + while (next && next->pref < nextpref) { + index = next; + next = next->next; + } + if (next == NULL) { + /* No clauses match! */ + goto route_map_apply_end; + } + } + } + } else if (index->type == RMAP_DENY) + /* 'deny' */ + { + ret = RMAP_DENYMATCH; + goto route_map_apply_end; + } + } + } - if (rmap_match_set_hook.no_match_ip_next_hop) { - if (argc <= idx_word) - return rmap_match_set_hook.no_match_ip_next_hop( - vty, index, "ip next-hop prefix-list", NULL, - RMAP_EVENT_PLIST_DELETED); - return rmap_match_set_hook.no_match_ip_next_hop( - vty, index, "ip next-hop prefix-list", - argv[idx_word]->arg, RMAP_EVENT_PLIST_DELETED); +route_map_apply_end: + if (rmap_debug) { + zlog_debug("Route-map: %s, prefix: %s, result: %s", + (map ? map->name : "null"), + prefix2str(prefix, buf, sizeof(buf)), + route_map_result_str(ret)); } - return CMD_SUCCESS; + + return (ret); } -DEFUN(match_ip_next_hop_type, match_ip_next_hop_type_cmd, - "match ip next-hop type ", - MATCH_STR IP_STR - "Match next-hop address of route\n" - "Match entries by type\n" - "Blackhole\n") +void route_map_add_hook(void (*func)(const char *)) { - int idx_word = 4; - VTY_DECLVAR_CONTEXT(route_map_index, index); - - if (rmap_match_set_hook.match_ip_next_hop_type) - return rmap_match_set_hook.match_ip_next_hop_type( - vty, index, "ip next-hop type", argv[idx_word]->arg, - RMAP_EVENT_MATCH_ADDED); - return CMD_SUCCESS; + route_map_master.add_hook = func; } -DEFUN(no_match_ip_next_hop_type, no_match_ip_next_hop_type_cmd, - "no match ip next-hop type []", - NO_STR MATCH_STR IP_STR - "Match next-hop address of route\n" - "Match entries by type\n" - "Blackhole\n") +void route_map_delete_hook(void (*func)(const char *)) { - int idx_word = 5; - VTY_DECLVAR_CONTEXT(route_map_index, index); - - if (rmap_match_set_hook.no_match_ip_next_hop) { - if (argc <= idx_word) - return rmap_match_set_hook.no_match_ip_next_hop( - vty, index, "ip next-hop type", NULL, - RMAP_EVENT_MATCH_DELETED); - return rmap_match_set_hook.no_match_ip_next_hop( - vty, index, "ip next-hop type", argv[idx_word]->arg, - RMAP_EVENT_MATCH_DELETED); - } - return CMD_SUCCESS; + route_map_master.delete_hook = func; } - -DEFUN (match_ipv6_address, - match_ipv6_address_cmd, - "match ipv6 address WORD", - MATCH_STR - IPV6_STR - "Match IPv6 address of route\n" - "IPv6 access-list name\n") +void route_map_event_hook(void (*func)(const char *name)) { - int idx_word = 3; - VTY_DECLVAR_CONTEXT(route_map_index, index); - - if (rmap_match_set_hook.match_ipv6_address) - return rmap_match_set_hook.match_ipv6_address( - vty, index, "ipv6 address", argv[idx_word]->arg, - RMAP_EVENT_FILTER_ADDED); - return CMD_SUCCESS; + route_map_master.event_hook = func; } -DEFUN (no_match_ipv6_address, - no_match_ipv6_address_cmd, - "no match ipv6 address WORD", - NO_STR - MATCH_STR - IPV6_STR - "Match IPv6 address of route\n" - "IPv6 access-list name\n") +/* Routines for route map dependency lists and dependency processing */ +static bool route_map_rmap_hash_cmp(const void *p1, const void *p2) { - int idx_word = 4; - VTY_DECLVAR_CONTEXT(route_map_index, index); - - if (rmap_match_set_hook.no_match_ipv6_address) - return rmap_match_set_hook.no_match_ipv6_address( - vty, index, "ipv6 address", argv[idx_word]->arg, - RMAP_EVENT_FILTER_DELETED); - return CMD_SUCCESS; + return strcmp(((const struct route_map_dep_data *)p1)->rname, + ((const struct route_map_dep_data *)p2)->rname) + == 0; } - -DEFUN (match_ipv6_address_prefix_list, - match_ipv6_address_prefix_list_cmd, - "match ipv6 address prefix-list WORD", - MATCH_STR - IPV6_STR - "Match address of route\n" - "Match entries of prefix-lists\n" - "IP prefix-list name\n") +static bool route_map_dep_hash_cmp(const void *p1, const void *p2) { - int idx_word = 4; - VTY_DECLVAR_CONTEXT(route_map_index, index); - if (rmap_match_set_hook.match_ipv6_address_prefix_list) - return rmap_match_set_hook.match_ipv6_address_prefix_list( - vty, index, "ipv6 address prefix-list", - argv[idx_word]->arg, RMAP_EVENT_PLIST_ADDED); - return CMD_SUCCESS; + return (strcmp(((const struct route_map_dep *)p1)->dep_name, + (const char *)p2) + == 0); } -DEFUN (no_match_ipv6_address_prefix_list, - no_match_ipv6_address_prefix_list_cmd, - "no match ipv6 address prefix-list WORD", - NO_STR - MATCH_STR - IPV6_STR - "Match address of route\n" - "Match entries of prefix-lists\n" - "IP prefix-list name\n") +static void route_map_clear_reference(struct hash_bucket *bucket, void *arg) { - int idx_word = 5; - VTY_DECLVAR_CONTEXT(route_map_index, index); + struct route_map_dep *dep = bucket->data; + struct route_map_dep_data *dep_data = NULL, tmp_dep_data; - if (rmap_match_set_hook.no_match_ipv6_address_prefix_list) - return rmap_match_set_hook.no_match_ipv6_address_prefix_list( - vty, index, "ipv6 address prefix-list", - argv[idx_word]->arg, RMAP_EVENT_PLIST_DELETED); - return CMD_SUCCESS; + if (arg) { + memset(&tmp_dep_data, 0, sizeof(struct route_map_dep_data)); + tmp_dep_data.rname = arg; + dep_data = hash_release(dep->dep_rmap_hash, + &tmp_dep_data); + if (dep_data) { + XFREE(MTYPE_ROUTE_MAP_NAME, dep_data->rname); + XFREE(MTYPE_ROUTE_MAP_DEP_DATA, dep_data); + } + if (!dep->dep_rmap_hash->count) { + dep = hash_release(dep->this_hash, + (void *)dep->dep_name); + hash_free(dep->dep_rmap_hash); + XFREE(MTYPE_ROUTE_MAP_NAME, dep->dep_name); + XFREE(MTYPE_ROUTE_MAP_DEP, dep); + } + } } -DEFUN(match_ipv6_next_hop_type, match_ipv6_next_hop_type_cmd, - "match ipv6 next-hop type ", - MATCH_STR IPV6_STR - "Match address of route\n" - "Match entries by type\n" - "Blackhole\n") +static void route_map_clear_all_references(char *rmap_name) { - int idx_word = 4; - VTY_DECLVAR_CONTEXT(route_map_index, index); + int i; - if (rmap_match_set_hook.match_ipv6_next_hop_type) - return rmap_match_set_hook.match_ipv6_next_hop_type( - vty, index, "ipv6 next-hop type", argv[idx_word]->arg, - RMAP_EVENT_MATCH_ADDED); - return CMD_SUCCESS; + for (i = 1; i < ROUTE_MAP_DEP_MAX; i++) { + hash_iterate(route_map_dep_hash[i], route_map_clear_reference, + (void *)rmap_name); + } } -DEFUN(no_match_ipv6_next_hop_type, no_match_ipv6_next_hop_type_cmd, - "no match ipv6 next-hop type []", - NO_STR MATCH_STR IPV6_STR - "Match address of route\n" - "Match entries by type\n" - "Blackhole\n") +static unsigned int route_map_dep_data_hash_make_key(const void *p) { - int idx_word = 5; - VTY_DECLVAR_CONTEXT(route_map_index, index); + const struct route_map_dep_data *dep_data = p; - if (rmap_match_set_hook.no_match_ipv6_next_hop_type) - return rmap_match_set_hook.no_match_ipv6_next_hop_type( - vty, index, "ipv6 next-hop type", - (argc <= idx_word) ? NULL : argv[idx_word]->arg, - RMAP_EVENT_MATCH_DELETED); - return CMD_SUCCESS; + return string_hash_make(dep_data->rname); } -DEFUN (match_metric, - match_metric_cmd, - "match metric (0-4294967295)", - MATCH_STR - "Match metric of route\n" - "Metric value\n") +DEFUN (set_srte_color, + set_srte_color_cmd, + "set sr-te color [(1-4294967295)]", + SET_STR + SRTE_STR + SRTE_COLOR_STR + "Color of the SR-TE Policies to match with\n") { - int idx_number = 2; VTY_DECLVAR_CONTEXT(route_map_index, index); + int idx = 0; + char *arg = argv_find(argv, argc, "(1-4294967295)", &idx) + ? argv[idx]->arg + : NULL; - if (rmap_match_set_hook.match_metric) - return rmap_match_set_hook.match_metric(vty, index, "metric", - argv[idx_number]->arg, - RMAP_EVENT_MATCH_ADDED); + if (rmap_match_set_hook.set_srte_color) + return rmap_match_set_hook.set_srte_color(vty, index, + "sr-te color", arg); return CMD_SUCCESS; } - -DEFUN (no_match_metric, - no_match_metric_cmd, - "no match metric [(0-4294967295)]", +DEFUN (no_set_srte_color, + no_set_srte_color_cmd, + "no set sr-te color [(1-4294967295)]", NO_STR - MATCH_STR - "Match metric of route\n" - "Metric value\n") + SET_STR + SRTE_STR + SRTE_COLOR_STR + "Color of the SR-TE Policies to match with\n") { - int idx_number = 3; VTY_DECLVAR_CONTEXT(route_map_index, index); + int idx = 0; + char *arg = argv_find(argv, argc, "(1-4294967295)", &idx) + ? argv[idx]->arg + : NULL; - if (rmap_match_set_hook.no_match_metric) { - if (argc <= idx_number) - return rmap_match_set_hook.no_match_metric( - vty, index, "metric", NULL, - RMAP_EVENT_MATCH_DELETED); - return rmap_match_set_hook.no_match_metric( - vty, index, "metric", argv[idx_number]->arg, - RMAP_EVENT_MATCH_DELETED); - } + if (rmap_match_set_hook.no_set_srte_color) + return rmap_match_set_hook.no_set_srte_color( + vty, index, "sr-te color", arg); return CMD_SUCCESS; } - -DEFUN (match_tag, - match_tag_cmd, - "match tag (1-4294967295)", - MATCH_STR - "Match tag of route\n" - "Tag value\n") +static void *route_map_dep_hash_alloc(void *p) { - int idx_number = 2; - VTY_DECLVAR_CONTEXT(route_map_index, index); + char *dep_name = (char *)p; + struct route_map_dep *dep_entry; - if (rmap_match_set_hook.match_tag) - return rmap_match_set_hook.match_tag(vty, index, "tag", - argv[idx_number]->arg, - RMAP_EVENT_MATCH_ADDED); - return CMD_SUCCESS; -} + dep_entry = XCALLOC(MTYPE_ROUTE_MAP_DEP, sizeof(struct route_map_dep)); + dep_entry->dep_name = XSTRDUP(MTYPE_ROUTE_MAP_NAME, dep_name); + dep_entry->dep_rmap_hash = + hash_create_size(8, route_map_dep_data_hash_make_key, + route_map_rmap_hash_cmp, "Route Map Dep Hash"); + dep_entry->this_hash = NULL; + return dep_entry; +} -DEFUN (no_match_tag, - no_match_tag_cmd, - "no match tag [(1-4294967295)]", - NO_STR - MATCH_STR - "Match tag of route\n" - "Tag value\n") +static void *route_map_name_hash_alloc(void *p) { - VTY_DECLVAR_CONTEXT(route_map_index, index); - - int idx = 0; - char *arg = argv_find(argv, argc, "(1-4294967295)", &idx) - ? argv[idx]->arg - : NULL; + struct route_map_dep_data *dep_data = NULL, *tmp_dep_data = NULL; - if (rmap_match_set_hook.no_match_tag) - return rmap_match_set_hook.no_match_tag( - vty, index, "tag", arg, RMAP_EVENT_MATCH_DELETED); - return CMD_SUCCESS; + dep_data = XCALLOC(MTYPE_ROUTE_MAP_DEP_DATA, + sizeof(struct route_map_dep_data)); + tmp_dep_data = p; + dep_data->rname = XSTRDUP(MTYPE_ROUTE_MAP_NAME, tmp_dep_data->rname); + return dep_data; } - -DEFUN (set_ip_nexthop, - set_ip_nexthop_cmd, - "set ip next-hop A.B.C.D", - SET_STR - IP_STR - "Next hop address\n" - "IP address of next hop\n") +static unsigned int route_map_dep_hash_make_key(const void *p) { - int idx_ipv4 = 3; - union sockunion su; - int ret; - VTY_DECLVAR_CONTEXT(route_map_index, index); + return (string_hash_make((char *)p)); +} - ret = str2sockunion(argv[idx_ipv4]->arg, &su); - if (ret < 0) { - vty_out(vty, "%% Malformed nexthop address\n"); - return CMD_WARNING_CONFIG_FAILED; - } - if (su.sin.sin_addr.s_addr == 0 - || IPV4_CLASS_DE(ntohl(su.sin.sin_addr.s_addr))) { - vty_out(vty, - "%% nexthop address cannot be 0.0.0.0, multicast or reserved\n"); - return CMD_WARNING_CONFIG_FAILED; - } +static void route_map_print_dependency(struct hash_bucket *bucket, void *data) +{ + struct route_map_dep_data *dep_data = bucket->data; + char *rmap_name = dep_data->rname; + char *dep_name = data; - if (rmap_match_set_hook.set_ip_nexthop) - return rmap_match_set_hook.set_ip_nexthop( - vty, index, "ip next-hop", argv[idx_ipv4]->arg); - return CMD_SUCCESS; + zlog_debug("%s: Dependency for %s: %s", __func__, dep_name, rmap_name); } - -DEFUN (no_set_ip_nexthop, - no_set_ip_nexthop_cmd, - "no set ip next-hop [A.B.C.D]", - NO_STR - SET_STR - IP_STR - "Next hop address\n" - "IP address of next hop\n") +static int route_map_dep_update(struct hash *dephash, const char *dep_name, + const char *rmap_name, route_map_event_t type) { - int idx = 0; - VTY_DECLVAR_CONTEXT(route_map_index, index); - const char *arg = NULL; - - if (argv_find(argv, argc, "A.B.C.D", &idx)) - arg = argv[idx]->arg; + struct route_map_dep *dep = NULL; + char *dname, *rname; + int ret = 0; + struct route_map_dep_data *dep_data = NULL, *ret_dep_data = NULL; + struct route_map_dep_data tmp_dep_data; - if (rmap_match_set_hook.no_set_ip_nexthop) - return rmap_match_set_hook.no_set_ip_nexthop( - vty, index, "ip next-hop", arg); + dname = XSTRDUP(MTYPE_ROUTE_MAP_NAME, dep_name); + rname = XSTRDUP(MTYPE_ROUTE_MAP_NAME, rmap_name); - return CMD_SUCCESS; -} + switch (type) { + case RMAP_EVENT_PLIST_ADDED: + case RMAP_EVENT_CLIST_ADDED: + case RMAP_EVENT_ECLIST_ADDED: + case RMAP_EVENT_ASLIST_ADDED: + case RMAP_EVENT_LLIST_ADDED: + case RMAP_EVENT_CALL_ADDED: + case RMAP_EVENT_FILTER_ADDED: + if (rmap_debug) + zlog_debug("Adding dependency for filter %s in route-map %s", + dep_name, rmap_name); + dep = (struct route_map_dep *)hash_get( + dephash, dname, route_map_dep_hash_alloc); + if (!dep) { + ret = -1; + goto out; + } + if (!dep->this_hash) + dep->this_hash = dephash; -DEFUN (set_ipv6_nexthop_local, - set_ipv6_nexthop_local_cmd, - "set ipv6 next-hop local X:X::X:X", - SET_STR - IPV6_STR - "IPv6 next-hop address\n" - "IPv6 local address\n" - "IPv6 address of next hop\n") -{ - int idx_ipv6 = 4; - struct in6_addr addr; - int ret; - VTY_DECLVAR_CONTEXT(route_map_index, index); + memset(&tmp_dep_data, 0, sizeof(struct route_map_dep_data)); + tmp_dep_data.rname = rname; + dep_data = hash_lookup(dep->dep_rmap_hash, &tmp_dep_data); + if (!dep_data) + dep_data = hash_get(dep->dep_rmap_hash, &tmp_dep_data, + route_map_name_hash_alloc); - ret = inet_pton(AF_INET6, argv[idx_ipv6]->arg, &addr); - if (!ret) { - vty_out(vty, "%% Malformed nexthop address\n"); - return CMD_WARNING_CONFIG_FAILED; - } - if (!IN6_IS_ADDR_LINKLOCAL(&addr)) { - vty_out(vty, "%% Invalid link-local nexthop address\n"); - return CMD_WARNING_CONFIG_FAILED; - } + dep_data->refcnt++; + break; + case RMAP_EVENT_PLIST_DELETED: + case RMAP_EVENT_CLIST_DELETED: + case RMAP_EVENT_ECLIST_DELETED: + case RMAP_EVENT_ASLIST_DELETED: + case RMAP_EVENT_LLIST_DELETED: + case RMAP_EVENT_CALL_DELETED: + case RMAP_EVENT_FILTER_DELETED: + if (rmap_debug) + zlog_debug("Deleting dependency for filter %s in route-map %s", + dep_name, rmap_name); + dep = (struct route_map_dep *)hash_get(dephash, dname, NULL); + if (!dep) { + goto out; + } - if (rmap_match_set_hook.set_ipv6_nexthop_local) - return rmap_match_set_hook.set_ipv6_nexthop_local( - vty, index, "ipv6 next-hop local", argv[idx_ipv6]->arg); - return CMD_SUCCESS; -} + memset(&tmp_dep_data, 0, sizeof(struct route_map_dep_data)); + tmp_dep_data.rname = rname; + dep_data = hash_lookup(dep->dep_rmap_hash, &tmp_dep_data); + if (!dep_data) + goto out; -DEFUN (no_set_ipv6_nexthop_local, - no_set_ipv6_nexthop_local_cmd, - "no set ipv6 next-hop local [X:X::X:X]", - NO_STR - SET_STR - IPV6_STR - "IPv6 next-hop address\n" - "IPv6 local address\n" - "IPv6 address of next hop\n") -{ - int idx_ipv6 = 5; - VTY_DECLVAR_CONTEXT(route_map_index, index); + if (dep_data->refcnt) + dep_data->refcnt--; - if (rmap_match_set_hook.no_set_ipv6_nexthop_local) { - if (argc <= idx_ipv6) - return rmap_match_set_hook.no_set_ipv6_nexthop_local( - vty, index, "ipv6 next-hop local", NULL); - return rmap_match_set_hook.no_set_ipv6_nexthop_local( - vty, index, "ipv6 next-hop local", argv[5]->arg); - } - return CMD_SUCCESS; -} + if (!dep_data->refcnt) { + ret_dep_data = hash_release(dep->dep_rmap_hash, + &tmp_dep_data); + if (ret_dep_data) { + XFREE(MTYPE_ROUTE_MAP_NAME, + ret_dep_data->rname); + XFREE(MTYPE_ROUTE_MAP_DEP_DATA, ret_dep_data); + } + } -DEFUN (set_metric, - set_metric_cmd, - "set metric <(0-4294967295)|rtt|+rtt|-rtt|+metric|-metric>", - SET_STR - "Metric value for destination routing protocol\n" - "Metric value\n" - "Assign round trip time\n" - "Add round trip time\n" - "Subtract round trip time\n" - "Add metric\n" - "Subtract metric\n") -{ - int idx_number = 2; - VTY_DECLVAR_CONTEXT(route_map_index, index); + if (!dep->dep_rmap_hash->count) { + dep = hash_release(dephash, dname); + hash_free(dep->dep_rmap_hash); + XFREE(MTYPE_ROUTE_MAP_NAME, dep->dep_name); + XFREE(MTYPE_ROUTE_MAP_DEP, dep); + } + break; + case RMAP_EVENT_SET_ADDED: + case RMAP_EVENT_SET_DELETED: + case RMAP_EVENT_SET_REPLACED: + case RMAP_EVENT_MATCH_ADDED: + case RMAP_EVENT_MATCH_DELETED: + case RMAP_EVENT_MATCH_REPLACED: + case RMAP_EVENT_INDEX_ADDED: + case RMAP_EVENT_INDEX_DELETED: + break; + } - const char *pass = (argv[idx_number]->type == RANGE_TKN) - ? argv[idx_number]->arg - : argv[idx_number]->text; + if (dep) { + if (rmap_debug) + hash_iterate(dep->dep_rmap_hash, + route_map_print_dependency, dname); + } - if (rmap_match_set_hook.set_metric) - return rmap_match_set_hook.set_metric(vty, index, "metric", - pass); - return CMD_SUCCESS; +out: + XFREE(MTYPE_ROUTE_MAP_NAME, rname); + XFREE(MTYPE_ROUTE_MAP_NAME, dname); + return ret; } - -DEFUN (no_set_metric, - no_set_metric_cmd, - "no set metric [(0-4294967295)]", - NO_STR - SET_STR - "Metric value for destination routing protocol\n" - "Metric value\n") +static struct hash *route_map_get_dep_hash(route_map_event_t event) { - int idx_number = 3; - VTY_DECLVAR_CONTEXT(route_map_index, index); + struct hash *upd8_hash = NULL; - if (rmap_match_set_hook.no_set_metric) { - if (argc <= idx_number) - return rmap_match_set_hook.no_set_metric( - vty, index, "metric", NULL); - return rmap_match_set_hook.no_set_metric(vty, index, "metric", - argv[idx_number]->arg); + switch (event) { + case RMAP_EVENT_PLIST_ADDED: + case RMAP_EVENT_PLIST_DELETED: + upd8_hash = route_map_dep_hash[ROUTE_MAP_DEP_PLIST]; + break; + case RMAP_EVENT_CLIST_ADDED: + case RMAP_EVENT_CLIST_DELETED: + upd8_hash = route_map_dep_hash[ROUTE_MAP_DEP_CLIST]; + break; + case RMAP_EVENT_ECLIST_ADDED: + case RMAP_EVENT_ECLIST_DELETED: + upd8_hash = route_map_dep_hash[ROUTE_MAP_DEP_ECLIST]; + break; + case RMAP_EVENT_ASLIST_ADDED: + case RMAP_EVENT_ASLIST_DELETED: + upd8_hash = route_map_dep_hash[ROUTE_MAP_DEP_ASPATH]; + break; + case RMAP_EVENT_LLIST_ADDED: + case RMAP_EVENT_LLIST_DELETED: + upd8_hash = route_map_dep_hash[ROUTE_MAP_DEP_LCLIST]; + break; + case RMAP_EVENT_CALL_ADDED: + case RMAP_EVENT_CALL_DELETED: + case RMAP_EVENT_MATCH_ADDED: + case RMAP_EVENT_MATCH_DELETED: + upd8_hash = route_map_dep_hash[ROUTE_MAP_DEP_RMAP]; + break; + case RMAP_EVENT_FILTER_ADDED: + case RMAP_EVENT_FILTER_DELETED: + upd8_hash = route_map_dep_hash[ROUTE_MAP_DEP_FILTER]; + break; + /* + * Should we actually be ignoring these? + * I am not sure but at this point in time, let + * us get them into this switch and we can peel + * them into the appropriate place in the future + */ + case RMAP_EVENT_SET_ADDED: + case RMAP_EVENT_SET_DELETED: + case RMAP_EVENT_SET_REPLACED: + case RMAP_EVENT_MATCH_REPLACED: + case RMAP_EVENT_INDEX_ADDED: + case RMAP_EVENT_INDEX_DELETED: + upd8_hash = NULL; + break; } - return CMD_SUCCESS; + return (upd8_hash); } - -DEFUN (set_tag, - set_tag_cmd, - "set tag (1-4294967295)", - SET_STR - "Tag value for routing protocol\n" - "Tag value\n") +static void route_map_process_dependency(struct hash_bucket *bucket, void *data) { - VTY_DECLVAR_CONTEXT(route_map_index, index); - - int idx_number = 2; - if (rmap_match_set_hook.set_tag) - return rmap_match_set_hook.set_tag(vty, index, "tag", - argv[idx_number]->arg); - return CMD_SUCCESS; -} - + struct route_map_dep_data *dep_data = NULL; + char *rmap_name = NULL; -DEFUN (no_set_tag, - no_set_tag_cmd, - "no set tag [(1-4294967295)]", - NO_STR - SET_STR - "Tag value for routing protocol\n" - "Tag value\n") -{ - VTY_DECLVAR_CONTEXT(route_map_index, index); + dep_data = bucket->data; + rmap_name = dep_data->rname; - int idx_number = 3; - if (rmap_match_set_hook.no_set_tag) { - if (argc <= idx_number) - return rmap_match_set_hook.no_set_tag(vty, index, "tag", - NULL); - return rmap_match_set_hook.no_set_tag(vty, index, "tag", - argv[idx_number]->arg); - } - return CMD_SUCCESS; + if (rmap_debug) + zlog_debug("Notifying %s of dependency", rmap_name); + if (route_map_master.event_hook) + (*route_map_master.event_hook)(rmap_name); } - -DEFUN_NOSH (route_map, - route_map_cmd, - "route-map WORD (1-65535)", - "Create route-map or enter route-map command mode\n" - "Route map tag\n" - "Route map denies set operations\n" - "Route map permits set operations\n" - "Sequence to insert to/delete from existing route-map entry\n") +void route_map_upd8_dependency(route_map_event_t type, const char *arg, + const char *rmap_name) { - int idx_word = 1; - int idx_permit_deny = 2; - int idx_number = 3; - struct route_map *map; - struct route_map_index *index; - char *endptr = NULL; - int permit = - argv[idx_permit_deny]->arg[0] == 'p' ? RMAP_PERMIT : RMAP_DENY; - unsigned long pref = strtoul(argv[idx_number]->arg, &endptr, 10); - const char *mapname = argv[idx_word]->arg; + struct hash *upd8_hash = NULL; - /* Get route map. */ - map = route_map_get(mapname); - index = route_map_index_get(map, permit, pref); + if ((upd8_hash = route_map_get_dep_hash(type))) { + route_map_dep_update(upd8_hash, arg, rmap_name, type); - VTY_PUSH_CONTEXT(RMAP_NODE, index); - return CMD_SUCCESS; + if (type == RMAP_EVENT_CALL_ADDED) { + /* Execute hook. */ + if (route_map_master.add_hook) + (*route_map_master.add_hook)(rmap_name); + } else if (type == RMAP_EVENT_CALL_DELETED) { + /* Execute hook. */ + if (route_map_master.delete_hook) + (*route_map_master.delete_hook)(rmap_name); + } + } } -DEFUN (no_route_map_all, - no_route_map_all_cmd, - "no route-map WORD", - NO_STR - "Create route-map or enter route-map command mode\n" - "Route map tag\n") +void route_map_notify_dependencies(const char *affected_name, + route_map_event_t event) { - int idx_word = 2; - const char *mapname = argv[idx_word]->arg; - struct route_map *map; - - map = route_map_lookup_by_name(mapname); - if (map == NULL) { - vty_out(vty, "%% Could not find route-map %s\n", mapname); - return CMD_WARNING_CONFIG_FAILED; - } + struct route_map_dep *dep; + struct hash *upd8_hash; + char *name; - route_map_delete(map); + if (!affected_name) + return; - return CMD_SUCCESS; -} + name = XSTRDUP(MTYPE_ROUTE_MAP_NAME, affected_name); -DEFUN (no_route_map, - no_route_map_cmd, - "no route-map WORD (1-65535)", - NO_STR - "Create route-map or enter route-map command mode\n" - "Route map tag\n" - "Route map denies set operations\n" - "Route map permits set operations\n" - "Sequence to insert to/delete from existing route-map entry\n") -{ - int idx_word = 2; - int idx_permit_deny = 3; - int idx_number = 4; - struct route_map *map; - struct route_map_index *index; - char *endptr = NULL; - int permit = strmatch(argv[idx_permit_deny]->text, "permit") - ? RMAP_PERMIT - : RMAP_DENY; - const char *prefstr = argv[idx_number]->arg; - const char *mapname = argv[idx_word]->arg; - unsigned long pref = strtoul(prefstr, &endptr, 10); - - /* Existence check. */ - map = route_map_lookup_by_name(mapname); - if (map == NULL) { - vty_out(vty, "%% Could not find route-map %s\n", mapname); - return CMD_WARNING_CONFIG_FAILED; + if ((upd8_hash = route_map_get_dep_hash(event)) == NULL) { + XFREE(MTYPE_ROUTE_MAP_NAME, name); + return; } - /* Lookup route map index. */ - index = route_map_index_lookup(map, permit, pref); - if (index == NULL) { - vty_out(vty, "%% Could not find route-map entry %s %s\n", - mapname, prefstr); - return CMD_WARNING_CONFIG_FAILED; + dep = (struct route_map_dep *)hash_get(upd8_hash, name, NULL); + if (dep) { + if (!dep->this_hash) + dep->this_hash = upd8_hash; + + if (rmap_debug) + zlog_debug("Filter %s updated", dep->dep_name); + hash_iterate(dep->dep_rmap_hash, route_map_process_dependency, + (void *)event); } - /* Delete index from route map. */ - route_map_index_delete(index, 1); + XFREE(MTYPE_ROUTE_MAP_NAME, name); +} - /* If this route rule is the last one, delete route map itself. */ - if (route_map_empty(map)) - route_map_delete(map); +/* VTY related functions. */ +DEFUN(no_routemap_optimization, no_routemap_optimization_cmd, + "no route-map optimization", + NO_STR + "route-map\n" + "optimization\n") +{ + VTY_DECLVAR_CONTEXT(route_map_index, index); + index->map->optimization_disabled = true; return CMD_SUCCESS; } -DEFUN (rmap_onmatch_next, - rmap_onmatch_next_cmd, - "on-match next", - "Exit policy on matches\n" - "Next clause\n") +DEFUN(routemap_optimization, routemap_optimization_cmd, + "route-map optimization", + "route-map\n" + "optimization\n") { - struct route_map_index *index = VTY_GET_CONTEXT(route_map_index); + VTY_DECLVAR_CONTEXT(route_map_index, index); - if (index) { - if (index->type == RMAP_DENY) { - /* Under a deny clause, match means it's finished. No - * need to set next */ - vty_out(vty, - "on-match next not supported under route-map deny\n"); - return CMD_WARNING_CONFIG_FAILED; - } - index->exitpolicy = RMAP_NEXT; - } + index->map->optimization_disabled = false; return CMD_SUCCESS; } -DEFUN (no_rmap_onmatch_next, - no_rmap_onmatch_next_cmd, - "no on-match next", - NO_STR - "Exit policy on matches\n" - "Next clause\n") +static void clear_route_map_helper(struct route_map *map) { - struct route_map_index *index = VTY_GET_CONTEXT(route_map_index); - - if (index) - index->exitpolicy = RMAP_EXIT; + struct route_map_index *index; - return CMD_SUCCESS; + map->applied_clear = map->applied; + for (index = map->head; index; index = index->next) + index->applied_clear = index->applied; } -DEFUN (rmap_onmatch_goto, - rmap_onmatch_goto_cmd, - "on-match goto (1-65535)", - "Exit policy on matches\n" - "Goto Clause number\n" - "Number\n") +DEFUN (rmap_clear_counters, + rmap_clear_counters_cmd, + "clear route-map counters [WORD]", + CLEAR_STR + "route-map information\n" + "counters associated with the specified route-map\n" + "route-map name\n") { - int idx = 0; - char *num = argv_find(argv, argc, "(1-65535)", &idx) ? argv[idx]->arg - : NULL; - - struct route_map_index *index = VTY_GET_CONTEXT(route_map_index); - int d = 0; + int idx_word = 2; + struct route_map *map; - if (index) { - if (index->type == RMAP_DENY) { - /* Under a deny clause, match means it's finished. No - * need to go anywhere */ - vty_out(vty, - "on-match goto not supported under route-map deny\n"); - return CMD_WARNING_CONFIG_FAILED; - } + const char *name = (argc == 3 ) ? argv[idx_word]->arg : NULL; - if (num) - d = strtoul(num, NULL, 10); - else - d = index->pref + 1; + if (name) { + map = route_map_lookup_by_name(name); - if (d <= index->pref) { - /* Can't allow you to do that, Dave */ - vty_out(vty, "can't jump backwards in route-maps\n"); - return CMD_WARNING_CONFIG_FAILED; - } else { - index->exitpolicy = RMAP_GOTO; - index->nextpref = d; + if (map) + clear_route_map_helper(map); + else { + vty_out(vty, "%s: 'route-map %s' not found\n", + frr_protonameinst, name); + return CMD_SUCCESS; } + } else { + for (map = route_map_master.head; map; map = map->next) + clear_route_map_helper(map); } - return CMD_SUCCESS; -} - -DEFUN (no_rmap_onmatch_goto, - no_rmap_onmatch_goto_cmd, - "no on-match goto", - NO_STR - "Exit policy on matches\n" - "Goto Clause number\n") -{ - struct route_map_index *index = VTY_GET_CONTEXT(route_map_index); - - if (index) - index->exitpolicy = RMAP_EXIT; return CMD_SUCCESS; -} -/* Cisco/GNU Zebra compatibility aliases */ -/* ALIAS_FIXME */ -DEFUN (rmap_continue, - rmap_continue_cmd, - "continue (1-65535)", - "Continue on a different entry within the route-map\n" - "Route-map entry sequence number\n") -{ - return rmap_onmatch_goto(self, vty, argc, argv); -} - -/* ALIAS_FIXME */ -DEFUN (no_rmap_continue, - no_rmap_continue_cmd, - "no continue [(1-65535)]", - NO_STR - "Continue on a different entry within the route-map\n" - "Route-map entry sequence number\n") -{ - return no_rmap_onmatch_goto(self, vty, argc, argv); } - DEFUN (rmap_show_name, rmap_show_name_cmd, "show route-map [WORD]", @@ -2791,130 +3031,49 @@ DEFUN (rmap_show_unused, return vty_show_unused_route_map(vty); } -DEFUN (rmap_call, - rmap_call_cmd, - "call WORD", - "Jump to another Route-Map after match+set\n" - "Target route-map name\n") +DEFUN (debug_rmap, + debug_rmap_cmd, + "debug route-map", + DEBUG_STR + "Debug option set for route-maps\n") { - int idx_word = 1; - struct route_map_index *index = VTY_GET_CONTEXT(route_map_index); - const char *rmap = argv[idx_word]->arg; - - assert(index); - - if (index->nextrm) { - route_map_upd8_dependency(RMAP_EVENT_CALL_DELETED, - index->nextrm, index->map->name); - XFREE(MTYPE_ROUTE_MAP_NAME, index->nextrm); - } - index->nextrm = XSTRDUP(MTYPE_ROUTE_MAP_NAME, rmap); - - /* Execute event hook. */ - route_map_upd8_dependency(RMAP_EVENT_CALL_ADDED, index->nextrm, - index->map->name); + rmap_debug = true; return CMD_SUCCESS; } -DEFUN (no_rmap_call, - no_rmap_call_cmd, - "no call", +DEFUN (no_debug_rmap, + no_debug_rmap_cmd, + "no debug route-map", NO_STR - "Jump to another Route-Map after match+set\n") -{ - struct route_map_index *index = VTY_GET_CONTEXT(route_map_index); - - if (index->nextrm) { - route_map_upd8_dependency(RMAP_EVENT_CALL_DELETED, - index->nextrm, index->map->name); - XFREE(MTYPE_ROUTE_MAP_NAME, index->nextrm); - index->nextrm = NULL; - } - - return CMD_SUCCESS; -} - -DEFUN (rmap_description, - rmap_description_cmd, - "description LINE...", - "Route-map comment\n" - "Comment describing this route-map rule\n") + DEBUG_STR + "Debug option set for route-maps\n") { - int idx_line = 1; - struct route_map_index *index = VTY_GET_CONTEXT(route_map_index); - - if (index) { - if (index->description) - XFREE(MTYPE_TMP, index->description); - index->description = argv_concat(argv, argc, idx_line); - } + rmap_debug = false; return CMD_SUCCESS; } -DEFUN (no_rmap_description, - no_rmap_description_cmd, - "no description", - NO_STR - "Route-map comment\n") -{ - struct route_map_index *index = VTY_GET_CONTEXT(route_map_index); - - if (index) { - if (index->description) - XFREE(MTYPE_TMP, index->description); - index->description = NULL; - } - return CMD_SUCCESS; -} +/* Debug node. */ +static int rmap_config_write_debug(struct vty *vty); +static struct cmd_node rmap_debug_node = { + .name = "route-map debug", + .node = RMAP_DEBUG_NODE, + .prompt = "", + .config_write = rmap_config_write_debug, +}; /* Configuration write function. */ -static int route_map_config_write(struct vty *vty) +static int rmap_config_write_debug(struct vty *vty) { - struct route_map *map; - struct route_map_index *index; - struct route_map_rule *rule; - int first = 1; int write = 0; - for (map = route_map_master.head; map; map = map->next) - for (index = map->head; index; index = index->next) { - if (!first) - vty_out(vty, "!\n"); - else - first = 0; - - vty_out(vty, "route-map %s %s %d\n", map->name, - route_map_type_str(index->type), index->pref); - - if (index->description) - vty_out(vty, " description %s\n", - index->description); - - for (rule = index->match_list.head; rule; - rule = rule->next) - vty_out(vty, " match %s %s\n", rule->cmd->str, - rule->rule_str ? rule->rule_str : ""); - - for (rule = index->set_list.head; rule; - rule = rule->next) - vty_out(vty, " set %s %s\n", rule->cmd->str, - rule->rule_str ? rule->rule_str : ""); - if (index->nextrm) - vty_out(vty, " call %s\n", index->nextrm); - if (index->exitpolicy == RMAP_GOTO) - vty_out(vty, " on-match goto %d\n", - index->nextpref); - if (index->exitpolicy == RMAP_NEXT) - vty_out(vty, " on-match next\n"); - - write++; - } + if (rmap_debug) { + vty_out(vty, "debug route-map\n"); + write++; + } + return write; } -/* Route map node structure. */ -static struct cmd_node rmap_node = {RMAP_NODE, "%s(config-route-map)# ", 1}; - /* Common route map rules */ void *route_map_rule_tag_compile(const char *arg) @@ -2973,14 +3132,6 @@ void route_map_finish(void) route_map_master_hash = NULL; } -static void rmap_autocomplete(vector comps, struct cmd_token *token) -{ - struct route_map *map; - - for (map = route_map_master.head; map; map = map->next) - vector_set(comps, XSTRDUP(MTYPE_COMPLETION, map->name)); -} - /* Increment the use_count counter while attaching the route map */ void route_map_counter_increment(struct route_map *map) { @@ -2998,13 +3149,120 @@ void route_map_counter_decrement(struct route_map *map) } } -static const struct cmd_variable_handler rmap_var_handlers[] = { - {/* "route-map WORD" */ - .varname = "route_map", - .completions = rmap_autocomplete}, - {.tokenname = "ROUTEMAP_NAME", .completions = rmap_autocomplete}, - {.tokenname = "RMAP_NAME", .completions = rmap_autocomplete}, - {.completions = NULL}}; +DEFUN_HIDDEN(show_route_map_pfx_tbl, show_route_map_pfx_tbl_cmd, + "show route-map WORD prefix-table", + SHOW_STR + "route-map\n" + "route-map name\n" + "internal prefix-table\n") +{ + const char *rmap_name = argv[2]->arg; + struct route_map *rmap = NULL; + struct route_table *rm_pfx_tbl4 = NULL; + struct route_table *rm_pfx_tbl6 = NULL; + struct route_node *rn = NULL, *prn = NULL; + struct list *rmap_index_list = NULL; + struct listnode *ln = NULL, *nln = NULL; + struct route_map_index *index = NULL; + struct prefix *p = NULL, *pp = NULL; + char buf[SU_ADDRSTRLEN], pbuf[SU_ADDRSTRLEN]; + uint8_t len = 54; + + vty_out(vty, "%s:\n", frr_protonameinst); + rmap = route_map_lookup_by_name(rmap_name); + if (rmap) { + rm_pfx_tbl4 = rmap->ipv4_prefix_table; + if (rm_pfx_tbl4) { + vty_out(vty, "\n%s%43s%s\n", "IPv4 Prefix", "", + "Route-map Index List"); + vty_out(vty, "%s%39s%s\n", "_______________", "", + "____________________"); + for (rn = route_top(rm_pfx_tbl4); rn; + rn = route_next(rn)) { + p = &rn->p; + + vty_out(vty, " %s/%d (%d)\n", + inet_ntop(p->family, &p->u.prefix, buf, + SU_ADDRSTRLEN), + p->prefixlen, rn->lock); + + vty_out(vty, "(P) "); + prn = rn->parent; + if (prn) { + pp = &prn->p; + vty_out(vty, "%s/%d\n", + inet_ntop(pp->family, + &pp->u.prefix, pbuf, + SU_ADDRSTRLEN), + pp->prefixlen); + } + + vty_out(vty, "\n"); + rmap_index_list = (struct list *)rn->info; + if (!rmap_index_list + || !listcount(rmap_index_list)) + vty_out(vty, "%*s%s\n", len, "", "-"); + else + for (ALL_LIST_ELEMENTS(rmap_index_list, + ln, nln, + index)) { + vty_out(vty, "%*s%s seq %d\n", + len, "", + index->map->name, + index->pref); + } + vty_out(vty, "\n"); + } + } + + rm_pfx_tbl6 = rmap->ipv6_prefix_table; + if (rm_pfx_tbl6) { + vty_out(vty, "\n%s%43s%s\n", "IPv6 Prefix", "", + "Route-map Index List"); + vty_out(vty, "%s%39s%s\n", "_______________", "", + "____________________"); + for (rn = route_top(rm_pfx_tbl6); rn; + rn = route_next(rn)) { + p = &rn->p; + + vty_out(vty, " %s/%d (%d)\n", + inet_ntop(p->family, &p->u.prefix, buf, + SU_ADDRSTRLEN), + p->prefixlen, rn->lock); + + vty_out(vty, "(P) "); + prn = rn->parent; + if (prn) { + pp = &prn->p; + vty_out(vty, "%s/%d\n", + inet_ntop(pp->family, + &pp->u.prefix, pbuf, + SU_ADDRSTRLEN), + pp->prefixlen); + } + + vty_out(vty, "\n"); + rmap_index_list = (struct list *)rn->info; + if (!rmap_index_list + || !listcount(rmap_index_list)) + vty_out(vty, "%*s%s\n", len, "", "-"); + else + for (ALL_LIST_ELEMENTS(rmap_index_list, + ln, nln, + index)) { + vty_out(vty, "%*s%s seq %d\n", + len, "", + index->map->name, + index->pref); + } + vty_out(vty, "\n"); + } + } + } + + vty_out(vty, "\n"); + return CMD_SUCCESS; +} /* Initialization of route map vector. */ void route_map_init(void) @@ -3023,82 +3281,31 @@ void route_map_init(void) 8, route_map_dep_hash_make_key, route_map_dep_hash_cmp, "Route Map Dep Hash"); - cmd_variable_handler_register(rmap_var_handlers); + rmap_debug = false; + + route_map_cli_init(); /* Install route map top node. */ - install_node(&rmap_node, route_map_config_write); + install_node(&rmap_debug_node); /* Install route map commands. */ - install_default(RMAP_NODE); - install_element(CONFIG_NODE, &route_map_cmd); - install_element(CONFIG_NODE, &no_route_map_cmd); - install_element(CONFIG_NODE, &no_route_map_all_cmd); - - /* Install the on-match stuff */ - install_element(RMAP_NODE, &route_map_cmd); - install_element(RMAP_NODE, &rmap_onmatch_next_cmd); - install_element(RMAP_NODE, &no_rmap_onmatch_next_cmd); - install_element(RMAP_NODE, &rmap_onmatch_goto_cmd); - install_element(RMAP_NODE, &no_rmap_onmatch_goto_cmd); - install_element(RMAP_NODE, &rmap_continue_cmd); - install_element(RMAP_NODE, &no_rmap_continue_cmd); - - /* Install the continue stuff (ALIAS of on-match). */ - - /* Install the call stuff. */ - install_element(RMAP_NODE, &rmap_call_cmd); - install_element(RMAP_NODE, &no_rmap_call_cmd); - - /* Install description commands. */ - install_element(RMAP_NODE, &rmap_description_cmd); - install_element(RMAP_NODE, &no_rmap_description_cmd); + install_element(CONFIG_NODE, &debug_rmap_cmd); + install_element(CONFIG_NODE, &no_debug_rmap_cmd); /* Install show command */ + install_element(ENABLE_NODE, &rmap_clear_counters_cmd); + install_element(ENABLE_NODE, &rmap_show_name_cmd); install_element(ENABLE_NODE, &rmap_show_unused_cmd); - install_element(RMAP_NODE, &match_interface_cmd); - install_element(RMAP_NODE, &no_match_interface_cmd); - - install_element(RMAP_NODE, &match_ip_address_cmd); - install_element(RMAP_NODE, &no_match_ip_address_cmd); - - install_element(RMAP_NODE, &match_ip_address_prefix_list_cmd); - install_element(RMAP_NODE, &no_match_ip_address_prefix_list_cmd); - - install_element(RMAP_NODE, &match_ip_next_hop_cmd); - install_element(RMAP_NODE, &no_match_ip_next_hop_cmd); - - install_element(RMAP_NODE, &match_ip_next_hop_prefix_list_cmd); - install_element(RMAP_NODE, &no_match_ip_next_hop_prefix_list_cmd); - - install_element(RMAP_NODE, &match_ip_next_hop_type_cmd); - install_element(RMAP_NODE, &no_match_ip_next_hop_type_cmd); - - install_element(RMAP_NODE, &match_ipv6_address_cmd); - install_element(RMAP_NODE, &no_match_ipv6_address_cmd); - - install_element(RMAP_NODE, &match_ipv6_address_prefix_list_cmd); - install_element(RMAP_NODE, &no_match_ipv6_address_prefix_list_cmd); - - install_element(RMAP_NODE, &match_ipv6_next_hop_type_cmd); - install_element(RMAP_NODE, &no_match_ipv6_next_hop_type_cmd); - - install_element(RMAP_NODE, &match_metric_cmd); - install_element(RMAP_NODE, &no_match_metric_cmd); - - install_element(RMAP_NODE, &match_tag_cmd); - install_element(RMAP_NODE, &no_match_tag_cmd); - - install_element(RMAP_NODE, &set_ip_nexthop_cmd); - install_element(RMAP_NODE, &no_set_ip_nexthop_cmd); + install_element(ENABLE_NODE, &debug_rmap_cmd); + install_element(ENABLE_NODE, &no_debug_rmap_cmd); - install_element(RMAP_NODE, &set_ipv6_nexthop_local_cmd); - install_element(RMAP_NODE, &no_set_ipv6_nexthop_local_cmd); + install_element(RMAP_NODE, &routemap_optimization_cmd); + install_element(RMAP_NODE, &no_routemap_optimization_cmd); - install_element(RMAP_NODE, &set_metric_cmd); - install_element(RMAP_NODE, &no_set_metric_cmd); + install_element(RMAP_NODE, &set_srte_color_cmd); + install_element(RMAP_NODE, &no_set_srte_color_cmd); - install_element(RMAP_NODE, &set_tag_cmd); - install_element(RMAP_NODE, &no_set_tag_cmd); + install_element(ENABLE_NODE, &show_route_map_pfx_tbl_cmd); } diff --git a/lib/routemap.h b/lib/routemap.h index e43e74a633..64da4b87ef 100644 --- a/lib/routemap.h +++ b/lib/routemap.h @@ -25,6 +25,8 @@ #include "memory.h" #include "qobj.h" #include "vty.h" +#include "lib/plist.h" +#include "lib/plist_int.h" #ifdef __cplusplus extern "C" { @@ -38,13 +40,35 @@ DECLARE_MTYPE(ROUTE_MAP_COMPILED) enum route_map_type { RMAP_PERMIT, RMAP_DENY, RMAP_ANY }; typedef enum { - RMAP_MATCH, RMAP_DENYMATCH, - RMAP_NOMATCH, - RMAP_ERROR, - RMAP_OKAY + RMAP_PERMITMATCH } route_map_result_t; +/* + * Route-map match or set result "Eg: match evpn vni xx" + * route-map match cmd always returns match/nomatch/noop + * match--> found a match + * nomatch--> didnt find a match + * noop--> not applicable + * route-map set retuns okay/error + * okay --> set was successful + * error --> set was not successful + */ +enum route_map_cmd_result_t { + /* + * route-map match cmd results + */ + RMAP_MATCH, + RMAP_NOMATCH, + RMAP_NOOP, + /* + * route-map set cmd results + */ + RMAP_OKAY, + RMAP_ERROR +}; + + typedef enum { RMAP_RIP, RMAP_RIPNG, @@ -91,26 +115,48 @@ struct route_map_rule_cmd { const char *str; /* Function for value set or match. */ - route_map_result_t (*func_apply)(void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object); + enum route_map_cmd_result_t (*func_apply)(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object); /* Compile argument and return result as void *. */ void *(*func_compile)(const char *); /* Free allocated value by func_compile (). */ void (*func_free)(void *); + + /** To get the rule key after Compilation **/ + void *(*func_get_rmap_rule_key)(void *val); }; /* Route map apply error. */ -enum { RMAP_COMPILE_SUCCESS, +enum rmap_compile_rets { + RMAP_COMPILE_SUCCESS, + + /* Route map rule is missing. */ + RMAP_RULE_MISSING, + + /* Route map rule can't compile */ + RMAP_COMPILE_ERROR, + +}; - /* Route map rule is missing. */ - RMAP_RULE_MISSING, +/* Route map rule. This rule has both `match' rule and `set' rule. */ +struct route_map_rule { + /* Rule type. */ + const struct route_map_rule_cmd *cmd; - /* Route map rule can't compile */ - RMAP_COMPILE_ERROR }; + /* For pretty printing. */ + char *rule_str; + + /* Pre-compiled match rule. */ + void *value; + + /* Linked list. */ + struct route_map_rule *next; + struct route_map_rule *prev; +}; /* Route map rule list. */ struct route_map_rule_list { @@ -118,6 +164,9 @@ struct route_map_rule_list { struct route_map_rule *tail; }; +/* Forward struct declaration: the complete can be found later this file. */ +struct routemap_hook_context; + /* Route map index structure. */ struct route_map_index { struct route_map *map; @@ -148,6 +197,10 @@ struct route_map_index { /* Keep track how many times we've try to apply */ uint64_t applied; + uint64_t applied_clear; + + /* List of match/sets contexts. */ + TAILQ_HEAD(, routemap_hook_context) rhclist; QOBJ_FIELDS }; @@ -169,13 +222,21 @@ struct route_map { /* Maintain update info */ bool to_be_processed; /* True if modification isn't acted on yet */ bool deleted; /* If 1, then this node will be deleted */ + bool optimization_disabled; /* How many times have we applied this route-map */ uint64_t applied; + uint64_t applied_clear; /* Counter to track active usage of this route-map */ uint16_t use_count; + /* Tables to maintain IPv4 and IPv6 prefixes from + * the prefix-list match clause. + */ + struct route_table *ipv4_prefix_table; + struct route_table *ipv6_prefix_table; + QOBJ_FIELDS }; DECLARE_QOBJ_TYPE(route_map) @@ -191,27 +252,32 @@ extern void route_map_init(void); extern void route_map_finish(void); /* Add match statement to route map. */ -extern int route_map_add_match(struct route_map_index *index, - const char *match_name, const char *match_arg); +extern enum rmap_compile_rets route_map_add_match(struct route_map_index *index, + const char *match_name, + const char *match_arg, + route_map_event_t type); /* Delete specified route match rule. */ -extern int route_map_delete_match(struct route_map_index *index, - const char *match_name, - const char *match_arg); +extern enum rmap_compile_rets +route_map_delete_match(struct route_map_index *index, + const char *match_name, const char *match_arg, + route_map_event_t type); extern const char *route_map_get_match_arg(struct route_map_index *index, const char *match_name); /* Add route-map set statement to the route map. */ -extern int route_map_add_set(struct route_map_index *index, - const char *set_name, const char *set_arg); +extern enum rmap_compile_rets route_map_add_set(struct route_map_index *index, + const char *set_name, + const char *set_arg); /* Delete route map set rule. */ -extern int route_map_delete_set(struct route_map_index *index, - const char *set_name, const char *set_arg); +extern enum rmap_compile_rets +route_map_delete_set(struct route_map_index *index, + const char *set_name, const char *set_arg); /* Install rule command to the match list. */ -extern void route_map_install_match(struct route_map_rule_cmd *cmd); +extern void route_map_install_match(const struct route_map_rule_cmd *cmd); /* * Install rule command to the set list. @@ -222,7 +288,7 @@ extern void route_map_install_match(struct route_map_rule_cmd *cmd); * in the apply command). See 'set metric' command * as it is handled in ripd/ripngd and ospfd. */ -extern void route_map_install_set(struct route_map_rule_cmd *cmd); +extern void route_map_install_set(const struct route_map_rule_cmd *cmd); /* Lookup route map by name. */ extern struct route_map *route_map_lookup_by_name(const char *name); @@ -238,14 +304,25 @@ extern route_map_result_t route_map_apply(struct route_map *map, extern void route_map_add_hook(void (*func)(const char *)); extern void route_map_delete_hook(void (*func)(const char *)); -extern void route_map_event_hook(void (*func)(route_map_event_t, const char *)); + +/* + * This is the callback for when something has changed about a + * route-map. The interested parties can register to receive + * this data. + * + * name - Is the name of the changed route-map + */ +extern void route_map_event_hook(void (*func)(const char *name)); extern int route_map_mark_updated(const char *name); extern void route_map_walk_update_list(void (*update_fn)(char *name)); extern void route_map_upd8_dependency(route_map_event_t type, const char *arg, const char *rmap_name); extern void route_map_notify_dependencies(const char *affected_name, route_map_event_t event); - +extern void +route_map_notify_pentry_dependencies(const char *affected_name, + struct prefix_list_entry *pentry, + route_map_event_t event); extern int generic_match_add(struct vty *vty, struct route_map_index *index, const char *command, const char *arg, route_map_event_t type); @@ -347,6 +424,14 @@ extern void route_map_match_tag_hook(int (*func)( extern void route_map_no_match_tag_hook(int (*func)( struct vty *vty, struct route_map_index *index, const char *command, const char *arg, route_map_event_t type)); +/* set sr-te color */ +extern void route_map_set_srte_color_hook( + int (*func)(struct vty *vty, struct route_map_index *index, + const char *command, const char *arg)); +/* no set sr-te color */ +extern void route_map_no_set_srte_color_hook( + int (*func)(struct vty *vty, struct route_map_index *index, + const char *command, const char *arg)); /* set ip nexthop */ extern void route_map_set_ip_nexthop_hook( int (*func)(struct vty *vty, struct route_map_index *index, @@ -392,6 +477,253 @@ extern void route_map_counter_increment(struct route_map *map); /* Decrement the route-map used counter */ extern void route_map_counter_decrement(struct route_map *map); +/* Route map hooks data structure. */ +struct route_map_match_set_hooks { + /* match interface */ + int (*match_interface)(struct vty *vty, struct route_map_index *index, + const char *command, const char *arg, + route_map_event_t type); + + /* no match interface */ + int (*no_match_interface)(struct vty *vty, + struct route_map_index *index, + const char *command, const char *arg, + route_map_event_t type); + + /* match ip address */ + int (*match_ip_address)(struct vty *vty, struct route_map_index *index, + const char *command, const char *arg, + route_map_event_t type); + + /* no match ip address */ + int (*no_match_ip_address)(struct vty *vty, + struct route_map_index *index, + const char *command, const char *arg, + route_map_event_t type); + + /* match ip address prefix list */ + int (*match_ip_address_prefix_list)(struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type); + + /* no match ip address prefix list */ + int (*no_match_ip_address_prefix_list)(struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type); + + /* match ip next hop */ + int (*match_ip_next_hop)(struct vty *vty, struct route_map_index *index, + const char *command, const char *arg, + route_map_event_t type); + + /* no match ip next hop */ + int (*no_match_ip_next_hop)(struct vty *vty, + struct route_map_index *index, + const char *command, const char *arg, + route_map_event_t type); + + /* match ip next hop prefix list */ + int (*match_ip_next_hop_prefix_list)(struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type); + + /* no match ip next hop prefix list */ + int (*no_match_ip_next_hop_prefix_list)(struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type); + + /* match ip next-hop type */ + int (*match_ip_next_hop_type)(struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type); + + /* no match ip next-hop type */ + int (*no_match_ip_next_hop_type)(struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type); + + /* match ipv6 address */ + int (*match_ipv6_address)(struct vty *vty, + struct route_map_index *index, + const char *command, const char *arg, + route_map_event_t type); + + /* no match ipv6 address */ + int (*no_match_ipv6_address)(struct vty *vty, + struct route_map_index *index, + const char *command, const char *arg, + route_map_event_t type); + + + /* match ipv6 address prefix list */ + int (*match_ipv6_address_prefix_list)(struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type); + + /* no match ipv6 address prefix list */ + int (*no_match_ipv6_address_prefix_list)(struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type); + + /* match ipv6 next-hop type */ + int (*match_ipv6_next_hop_type)(struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type); + + /* no match ipv6 next-hop type */ + int (*no_match_ipv6_next_hop_type)(struct vty *vty, + struct route_map_index *index, + const char *command, const char *arg, + route_map_event_t type); + + /* match metric */ + int (*match_metric)(struct vty *vty, struct route_map_index *index, + const char *command, const char *arg, + route_map_event_t type); + + /* no match metric */ + int (*no_match_metric)(struct vty *vty, struct route_map_index *index, + const char *command, const char *arg, + route_map_event_t type); + + /* match tag */ + int (*match_tag)(struct vty *vty, struct route_map_index *index, + const char *command, const char *arg, + route_map_event_t type); + + /* no match tag */ + int (*no_match_tag)(struct vty *vty, struct route_map_index *index, + const char *command, const char *arg, + route_map_event_t type); + + /* set sr-te color */ + int (*set_srte_color)(struct vty *vty, struct route_map_index *index, + const char *command, const char *arg); + + /* no set sr-te color */ + int (*no_set_srte_color)(struct vty *vty, struct route_map_index *index, + const char *command, const char *arg); + + /* set ip nexthop */ + int (*set_ip_nexthop)(struct vty *vty, struct route_map_index *index, + const char *command, const char *arg); + + /* no set ip nexthop */ + int (*no_set_ip_nexthop)(struct vty *vty, struct route_map_index *index, + const char *command, const char *arg); + + /* set ipv6 nexthop local */ + int (*set_ipv6_nexthop_local)(struct vty *vty, + struct route_map_index *index, + const char *command, const char *arg); + + /* no set ipv6 nexthop local */ + int (*no_set_ipv6_nexthop_local)(struct vty *vty, + struct route_map_index *index, + const char *command, const char *arg); + + /* set metric */ + int (*set_metric)(struct vty *vty, struct route_map_index *index, + const char *command, const char *arg); + + /* no set metric */ + int (*no_set_metric)(struct vty *vty, struct route_map_index *index, + const char *command, const char *arg); + + /* set tag */ + int (*set_tag)(struct vty *vty, struct route_map_index *index, + const char *command, const char *arg); + + /* no set tag */ + int (*no_set_tag)(struct vty *vty, struct route_map_index *index, + const char *command, const char *arg); +}; + +extern struct route_map_match_set_hooks rmap_match_set_hook; + +/* Making route map list. */ +struct route_map_list { + struct route_map *head; + struct route_map *tail; + + void (*add_hook)(const char *); + void (*delete_hook)(const char *); + void (*event_hook)(const char *); +}; + +extern struct route_map_list route_map_master; + +extern struct route_map *route_map_get(const char *name); +extern void route_map_delete(struct route_map *map); +extern struct route_map_index *route_map_index_get(struct route_map *map, + enum route_map_type type, + int pref); +extern void route_map_index_delete(struct route_map_index *index, int notify); + +/* routemap_northbound.c */ +typedef int (*routemap_match_hook_fun)(struct vty *vty, + struct route_map_index *rmi, + const char *command, const char *arg, + route_map_event_t event); + +typedef int (*routemap_set_hook_fun)(struct vty *vty, + struct route_map_index *rmi, + const char *command, const char *arg); + +struct routemap_hook_context { + struct route_map_index *rhc_rmi; + const char *rhc_rule; + route_map_event_t rhc_event; + routemap_set_hook_fun rhc_shook; + routemap_match_hook_fun rhc_mhook; + TAILQ_ENTRY(routemap_hook_context) rhc_entry; +}; + +int lib_route_map_entry_match_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_set_destroy(struct nb_cb_destroy_args *args); + +struct routemap_hook_context * +routemap_hook_context_insert(struct route_map_index *rmi); +void routemap_hook_context_free(struct routemap_hook_context *rhc); + +extern const struct frr_yang_module_info frr_route_map_info; + +/* routemap_cli.c */ +extern void route_map_instance_show(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +extern void route_map_instance_show_end(struct vty *vty, + struct lyd_node *dnode); +extern void route_map_condition_show(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +extern void route_map_action_show(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +extern void route_map_exit_policy_show(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +extern void route_map_call_show(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +extern void route_map_description_show(struct vty *vty, + struct lyd_node *dnode, + bool show_defaults); +extern void route_map_cli_init(void); + #ifdef __cplusplus } #endif diff --git a/lib/routemap_cli.c b/lib/routemap_cli.c new file mode 100644 index 0000000000..339d025124 --- /dev/null +++ b/lib/routemap_cli.c @@ -0,0 +1,1117 @@ +/* + * Route map northbound CLI implementation. + * + * Copyright (C) 2019 Network Device Education Foundation, Inc. ("NetDEF") + * Rafael Zalamena + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#include + +#include "lib/command.h" +#include "lib/northbound_cli.h" +#include "lib/routemap.h" + +#ifndef VTYSH_EXTRACT_PL +#include "lib/routemap_cli_clippy.c" +#endif /* VTYSH_EXTRACT_PL */ + +#define ROUTE_MAP_CMD_STR \ + "Create route-map or enter route-map command mode\n" \ + "Route map tag\n" +#define ROUTE_MAP_OP_CMD_STR \ + "Route map denies set operations\n" \ + "Route map permits set operations\n" +#define ROUTE_MAP_SEQUENCE_CMD_STR \ + "Sequence to insert to/delete from existing route-map entry\n" + +DEFPY_YANG_NOSH( + route_map, route_map_cmd, + "route-map WORD$name $action (1-65535)$sequence", + ROUTE_MAP_CMD_STR + ROUTE_MAP_OP_CMD_STR + ROUTE_MAP_SEQUENCE_CMD_STR) +{ + struct route_map_index *rmi; + struct route_map *rm; + int action_type; + char xpath_action[XPATH_MAXLEN + 64]; + char xpath_index[XPATH_MAXLEN + 32]; + char xpath[XPATH_MAXLEN]; + int rv; + + snprintf(xpath, sizeof(xpath), + "/frr-route-map:lib/route-map[name='%s']", name); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + snprintf(xpath_index, sizeof(xpath_index), "%s/entry[sequence='%lu']", + xpath, sequence); + nb_cli_enqueue_change(vty, xpath_index, NB_OP_CREATE, NULL); + + snprintf(xpath_action, sizeof(xpath_action), "%s/action", xpath_index); + nb_cli_enqueue_change(vty, xpath_action, NB_OP_MODIFY, action); + + rv = nb_cli_apply_changes(vty, NULL); + if (rv == CMD_SUCCESS) { + VTY_PUSH_XPATH(RMAP_NODE, xpath_index); + + /* Add support for non-migrated route map users. */ + nb_cli_pending_commit_check(vty); + rm = route_map_get(name); + action_type = (action[0] == 'p') ? RMAP_PERMIT : RMAP_DENY; + rmi = route_map_index_get(rm, action_type, sequence); + VTY_PUSH_CONTEXT(RMAP_NODE, rmi); + } + + return rv; +} + +DEFPY_YANG( + no_route_map_all, no_route_map_all_cmd, + "no route-map WORD$name", + NO_STR + ROUTE_MAP_CMD_STR) +{ + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "/frr-route-map:lib/route-map[name='%s']", name); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + no_route_map, no_route_map_cmd, + "no route-map WORD$name $action (1-65535)$sequence", + NO_STR + ROUTE_MAP_CMD_STR + ROUTE_MAP_OP_CMD_STR + ROUTE_MAP_SEQUENCE_CMD_STR) +{ + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "/frr-route-map:lib/route-map[name='%s']/entry[sequence='%lu']", + name, sequence); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +void route_map_instance_show(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + const struct route_map_rule *rmr; + const struct route_map_index *rmi; + const char *name = yang_dnode_get_string(dnode, "../name"); + const char *action = yang_dnode_get_string(dnode, "./action"); + const char *sequence = yang_dnode_get_string(dnode, "./sequence"); + + vty_out(vty, "route-map %s %s %s\n", name, action, sequence); + + rmi = nb_running_get_entry(dnode, NULL, false); + if (rmi == NULL) { + /* + * We can't have outdated rules if route map hasn't + * been created yet. + */ + return; + } + +#define SKIP_RULE(name) if (strcmp((name), rmr->cmd->str) == 0) continue + + /* Print route map `match` for old CLI users. */ + for (rmr = rmi->match_list.head; rmr; rmr = rmr->next) { + /* Skip all matches implemented by northbound. */ + SKIP_RULE("interface"); + SKIP_RULE("ip address"); + SKIP_RULE("ip address prefix-list"); + SKIP_RULE("ip next-hop"); + SKIP_RULE("ip next-hop prefix-list"); + SKIP_RULE("ip next-hop type"); + SKIP_RULE("ipv6 address"); + SKIP_RULE("ipv6 address prefix-list"); + SKIP_RULE("ipv6 next-hop type"); + SKIP_RULE("metric"); + SKIP_RULE("tag"); + /* Zebra specific match conditions. */ + SKIP_RULE("ip address prefix-len"); + SKIP_RULE("ipv6 address prefix-len"); + SKIP_RULE("ip next-hop prefix-len"); + SKIP_RULE("source-protocol"); + SKIP_RULE("source-instance"); + + vty_out(vty, " match %s %s\n", rmr->cmd->str, + rmr->rule_str ? rmr->rule_str : ""); + } + + /* Print route map `set` for old CLI users. */ + for (rmr = rmi->set_list.head; rmr; rmr = rmr->next) { + /* Skip all sets implemented by northbound. */ + SKIP_RULE("metric"); + SKIP_RULE("tag"); + /* Zebra specific set actions. */ + SKIP_RULE("src"); + + vty_out(vty, " set %s %s\n", rmr->cmd->str, + rmr->rule_str ? rmr->rule_str : ""); + } + +#undef SKIP_RULE +} + +void route_map_instance_show_end(struct vty *vty, struct lyd_node *dnode) +{ + vty_out(vty, "!\n"); +} + +DEFPY_YANG( + match_interface, match_interface_cmd, + "match interface IFNAME", + MATCH_STR + "Match first hop interface of route\n" + INTERFACE_STR) +{ + const char *xpath = "./match-condition[condition='interface']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), "%s/interface", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, ifname); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + no_match_interface, no_match_interface_cmd, + "no match interface [IFNAME]", + NO_STR + MATCH_STR + "Match first hop interface of route\n" + INTERFACE_STR) +{ + const char *xpath = "./match-condition[condition='interface']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + match_ip_address, match_ip_address_cmd, + "match ip address <(1-199)|(1300-2699)|WORD>$name", + MATCH_STR + IP_STR + "Match address of route\n" + "IP access-list number\n" + "IP access-list number (expanded range)\n" + "IP Access-list name\n") +{ + const char *xpath = "./match-condition[condition='ipv4-address-list']"; + char xpath_value[XPATH_MAXLEN + 32]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), "%s/list-name", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, name); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + no_match_ip_address, no_match_ip_address_cmd, + "no match ip address [<(1-199)|(1300-2699)|WORD>]", + NO_STR + MATCH_STR + IP_STR + "Match address of route\n" + "IP access-list number\n" + "IP access-list number (expanded range)\n" + "IP Access-list name\n") +{ + const char *xpath = "./match-condition[condition='ipv4-address-list']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + match_ip_address_prefix_list, + match_ip_address_prefix_list_cmd, + "match ip address prefix-list WORD$name", + MATCH_STR + IP_STR + "Match address of route\n" + "Match entries of prefix-lists\n" + "IP prefix-list name\n") +{ + const char *xpath = "./match-condition[condition='ipv4-prefix-list']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), "%s/list-name", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, name); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + no_match_ip_address_prefix_list, no_match_ip_address_prefix_list_cmd, + "no match ip address prefix-list [WORD]", + NO_STR + MATCH_STR + IP_STR + "Match address of route\n" + "Match entries of prefix-lists\n" + "IP prefix-list name\n") +{ + const char *xpath = "./match-condition[condition='ipv4-prefix-list']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + match_ip_next_hop, match_ip_next_hop_cmd, + "match ip next-hop <(1-199)|(1300-2699)|WORD>$name", + MATCH_STR + IP_STR + "Match next-hop address of route\n" + "IP access-list number\n" + "IP access-list number (expanded range)\n" + "IP Access-list name\n") +{ + const char *xpath = "./match-condition[condition='ipv4-next-hop-list']"; + char xpath_value[XPATH_MAXLEN + 32]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), "%s/list-name", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, name); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + no_match_ip_next_hop, no_match_ip_next_hop_cmd, + "no match ip next-hop [<(1-199)|(1300-2699)|WORD>]", + NO_STR + MATCH_STR + IP_STR + "Match address of route\n" + "IP access-list number\n" + "IP access-list number (expanded range)\n" + "IP Access-list name\n") +{ + const char *xpath = "./match-condition[condition='ipv4-next-hop-list']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + match_ip_next_hop_prefix_list, + match_ip_next_hop_prefix_list_cmd, + "match ip next-hop prefix-list WORD$name", + MATCH_STR + IP_STR + "Match next-hop address of route\n" + "Match entries of prefix-lists\n" + "IP prefix-list name\n") +{ + const char *xpath = + "./match-condition[condition='ipv4-next-hop-prefix-list']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), "%s/list-name", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, name); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + no_match_ip_next_hop_prefix_list, + no_match_ip_next_hop_prefix_list_cmd, + "no match ip next-hop prefix-list [WORD]", + NO_STR + MATCH_STR + IP_STR + "Match next-hop address of route\n" + "Match entries of prefix-lists\n" + "IP prefix-list name\n") +{ + const char *xpath = + "./match-condition[condition='ipv4-next-hop-prefix-list']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + match_ip_next_hop_type, match_ip_next_hop_type_cmd, + "match ip next-hop type $type", + MATCH_STR + IP_STR + "Match next-hop address of route\n" + "Match entries by type\n" + "Blackhole\n") +{ + const char *xpath = "./match-condition[condition='ipv4-next-hop-type']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), "%s/ipv4-next-hop-type", + xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, type); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + no_match_ip_next_hop_type, no_match_ip_next_hop_type_cmd, + "no match ip next-hop type []", + NO_STR MATCH_STR IP_STR + "Match next-hop address of route\n" + "Match entries by type\n" + "Blackhole\n") +{ + const char *xpath = "./match-condition[condition='ipv4-next-hop-type']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + match_ipv6_address, match_ipv6_address_cmd, + "match ipv6 address WORD$name", + MATCH_STR + IPV6_STR + "Match IPv6 address of route\n" + "IPv6 access-list name\n") +{ + const char *xpath = "./match-condition[condition='ipv6-address-list']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), "%s/list-name", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, name); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + no_match_ipv6_address, no_match_ipv6_address_cmd, + "no match ipv6 address [WORD]", + NO_STR + MATCH_STR + IPV6_STR + "Match IPv6 address of route\n" + "IPv6 access-list name\n") +{ + const char *xpath = "./match-condition[condition='ipv6-address-list']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + match_ipv6_address_prefix_list, match_ipv6_address_prefix_list_cmd, + "match ipv6 address prefix-list WORD$name", + MATCH_STR + IPV6_STR + "Match address of route\n" + "Match entries of prefix-lists\n" + "IP prefix-list name\n") +{ + const char *xpath = "./match-condition[condition='ipv6-prefix-list']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), "%s/list-name", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, name); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + no_match_ipv6_address_prefix_list, + no_match_ipv6_address_prefix_list_cmd, + "no match ipv6 address prefix-list [WORD]", + NO_STR + MATCH_STR + IPV6_STR + "Match address of route\n" + "Match entries of prefix-lists\n" + "IP prefix-list name\n") +{ + const char *xpath = "./match-condition[condition='ipv6-prefix-list']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + match_ipv6_next_hop_type, match_ipv6_next_hop_type_cmd, + "match ipv6 next-hop type $type", + MATCH_STR IPV6_STR + "Match next-hop address of route\n" + "Match entries by type\n" + "Blackhole\n") +{ + const char *xpath = "./match-condition[condition='ipv6-next-hop-type']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), "%s/ipv6-next-hop-type", + xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, type); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + no_match_ipv6_next_hop_type, no_match_ipv6_next_hop_type_cmd, + "no match ipv6 next-hop type []", + NO_STR MATCH_STR IPV6_STR + "Match address of route\n" + "Match entries by type\n" + "Blackhole\n") +{ + const char *xpath = "./match-condition[condition='ipv6-next-hop-type']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + match_metric, match_metric_cmd, + "match metric (0-4294967295)$metric", + MATCH_STR + "Match metric of route\n" + "Metric value\n") +{ + const char *xpath = "./match-condition[condition='metric']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), "%s/metric", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, metric_str); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + no_match_metric, no_match_metric_cmd, + "no match metric [(0-4294967295)]", + NO_STR + MATCH_STR + "Match metric of route\n" + "Metric value\n") +{ + const char *xpath = "./match-condition[condition='metric']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + match_tag, match_tag_cmd, + "match tag (1-4294967295)$tag", + MATCH_STR + "Match tag of route\n" + "Tag value\n") +{ + const char *xpath = "./match-condition[condition='tag']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), "%s/tag", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, tag_str); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + no_match_tag, no_match_tag_cmd, + "no match tag [(1-4294967295)]", + NO_STR + MATCH_STR + "Match tag of route\n" + "Tag value\n") +{ + const char *xpath = "./match-condition[condition='tag']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +void route_map_condition_show(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + int condition = yang_dnode_get_enum(dnode, "./condition"); + + switch (condition) { + case 0: /* interface */ + vty_out(vty, " match interface %s\n", + yang_dnode_get_string(dnode, "./interface")); + break; + case 1: /* ipv4-address-list */ + case 3: /* ipv4-next-hop-list */ + switch (condition) { + case 1: + vty_out(vty, " match ip address %s\n", + yang_dnode_get_string(dnode, "./list-name")); + break; + case 3: + vty_out(vty, " match ip next-hop %s\n", + yang_dnode_get_string(dnode, "./list-name")); + break; + } + break; + case 2: /* ipv4-prefix-list */ + vty_out(vty, " match ip address prefix-list %s\n", + yang_dnode_get_string(dnode, "./list-name")); + break; + case 4: /* ipv4-next-hop-prefix-list */ + vty_out(vty, " match ip next-hop prefix-list %s\n", + yang_dnode_get_string(dnode, "./list-name")); + break; + case 5: /* ipv4-next-hop-type */ + vty_out(vty, " match ip next-hop type %s\n", + yang_dnode_get_string(dnode, "./ipv4-next-hop-type")); + break; + case 6: /* ipv6-address-list */ + vty_out(vty, " match ipv6 address %s\n", + yang_dnode_get_string(dnode, "./list-name")); + break; + case 7: /* ipv6-prefix-list */ + vty_out(vty, " match ipv6 address prefix-list %s\n", + yang_dnode_get_string(dnode, "./list-name")); + break; + case 8: /* ipv6-next-hop-type */ + vty_out(vty, " match ipv6 next-hop type %s\n", + yang_dnode_get_string(dnode, "./ipv6-next-hop-type")); + break; + case 9: /* metric */ + vty_out(vty, " match metric %s\n", + yang_dnode_get_string(dnode, "./metric")); + break; + case 10: /* tag */ + vty_out(vty, " match tag %s\n", + yang_dnode_get_string(dnode, "./tag")); + break; + case 100: /* ipv4-prefix-length */ + vty_out(vty, " match ip address prefix-len %s\n", + yang_dnode_get_string(dnode,"./frr-zebra:ipv4-prefix-length")); + break; + case 101: /* ipv6-prefix-length */ + vty_out(vty, " match ipv6 address prefix-len %s\n", + yang_dnode_get_string(dnode, "./frr-zebra:ipv6-prefix-length")); + break; + case 102: /* ipv4-next-hop-prefix-length */ + vty_out(vty, " match ip next-hop prefix-len %s\n", + yang_dnode_get_string(dnode, "./frr-zebra:ipv4-prefix-length")); + break; + case 103: /* source-protocol */ + vty_out(vty, " match source-protocol %s\n", + yang_dnode_get_string(dnode, "./frr-zebra:source-protocol")); + break; + case 104: /* source-instance */ + vty_out(vty, " match source-instance %s\n", + yang_dnode_get_string(dnode, "./frr-zebra:source-instance")); + break; + } +} + +DEFPY_YANG( + set_ip_nexthop, set_ip_nexthop_cmd, + "set ip next-hop A.B.C.D$addr", + SET_STR + IP_STR + "Next hop address\n" + "IP address of next hop\n") +{ + const char *xpath = "./set-action[action='ipv4-next-hop']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), "%s/ipv4-address", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, addr_str); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + no_set_ip_nexthop, no_set_ip_nexthop_cmd, + "no set ip next-hop [A.B.C.D]", + NO_STR + SET_STR + IP_STR + "Next hop address\n" + "IP address of next hop\n") +{ + const char *xpath = "./set-action[action='ipv4-next-hop']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + set_ipv6_nexthop_local, set_ipv6_nexthop_local_cmd, + "set ipv6 next-hop local X:X::X:X$addr", + SET_STR + IPV6_STR + "IPv6 next-hop address\n" + "IPv6 local address\n" + "IPv6 address of next hop\n") +{ + const char *xpath = "./set-action[action='ipv6-next-hop']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), "%s/ipv6-address", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, addr_str); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + no_set_ipv6_nexthop_local, no_set_ipv6_nexthop_local_cmd, + "no set ipv6 next-hop local [X:X::X:X]", + NO_STR + SET_STR + IPV6_STR + "IPv6 next-hop address\n" + "IPv6 local address\n" + "IPv6 address of next hop\n") +{ + const char *xpath = "./set-action[action='ipv6-next-hop']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + set_metric, set_metric_cmd, + "set metric <(-4294967295-4294967295)$metric|rtt$rtt|+rtt$artt|-rtt$srtt>", + SET_STR + "Metric value for destination routing protocol\n" + "Metric value (use +/- for additions or subtractions)\n" + "Assign round trip time\n" + "Add round trip time\n" + "Subtract round trip time\n") +{ + const char *xpath = "./set-action[action='metric']"; + char xpath_value[XPATH_MAXLEN]; + char value[64]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + if (rtt) { + snprintf(xpath_value, sizeof(xpath_value), + "%s/use-round-trip-time", xpath); + snprintf(value, sizeof(value), "true"); + } else if (artt) { + snprintf(xpath_value, sizeof(xpath_value), + "%s/add-round-trip-time", xpath); + snprintf(value, sizeof(value), "true"); + } else if (srtt) { + snprintf(xpath_value, sizeof(xpath_value), + "%s/subtract-round-trip-time", xpath); + snprintf(value, sizeof(value), "true"); + } else if (metric_str && metric_str[0] == '+') { + snprintf(xpath_value, sizeof(xpath_value), "%s/add-metric", + xpath); + snprintf(value, sizeof(value), "%s", ++metric_str); + } else if (metric_str && metric_str[0] == '-') { + snprintf(xpath_value, sizeof(xpath_value), "%s/subtract-metric", + xpath); + snprintf(value, sizeof(value), "%s", ++metric_str); + } else { + snprintf(xpath_value, sizeof(xpath_value), "%s/value", xpath); + snprintf(value, sizeof(value), "%s", metric_str); + } + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, value); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + no_set_metric, no_set_metric_cmd, + "no set metric [OPTVAL]", + NO_STR + SET_STR + "Metric value for destination routing protocol\n" + "Metric value\n") +{ + const char *xpath = "./set-action[action='metric']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + set_tag, set_tag_cmd, + "set tag (1-4294967295)$tag", + SET_STR + "Tag value for routing protocol\n" + "Tag value\n") +{ + const char *xpath = "./set-action[action='tag']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), "%s/tag", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, tag_str); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + no_set_tag, no_set_tag_cmd, + "no set tag [(1-4294967295)]", + NO_STR + SET_STR + "Tag value for routing protocol\n" + "Tag value\n") +{ + const char *xpath = "./set-action[action='tag']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +void route_map_action_show(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + int action = yang_dnode_get_enum(dnode, "./action"); + + switch (action) { + case 0: /* ipv4-next-hop */ + vty_out(vty, " set ip next-hop %s\n", + yang_dnode_get_string(dnode, "./ipv4-address")); + break; + case 1: /* ipv6-next-hop */ + vty_out(vty, " set ipv6 next-hop local %s\n", + yang_dnode_get_string(dnode, "./ipv6-address")); + break; + case 2: /* metric */ + if (yang_dnode_get(dnode, "./use-round-trip-time")) { + vty_out(vty, " set metric rtt\n"); + } else if (yang_dnode_get(dnode, "./add-round-trip-time")) { + vty_out(vty, " set metric +rtt\n"); + } else if (yang_dnode_get(dnode, "./subtract-round-trip-time")) { + vty_out(vty, " set metric -rtt\n"); + } else if (yang_dnode_get(dnode, "./add-metric")) { + vty_out(vty, " set metric +%s\n", + yang_dnode_get_string(dnode, "./add-metric")); + } else if (yang_dnode_get(dnode, "./subtract-metric")) { + vty_out(vty, " set metric -%s\n", + yang_dnode_get_string(dnode, + "./subtract-metric")); + } else { + vty_out(vty, " set metric %s\n", + yang_dnode_get_string(dnode, "./value")); + } + break; + case 3: /* tag */ + vty_out(vty, " set tag %s\n", + yang_dnode_get_string(dnode, "./tag")); + break; + case 100: /* source */ + if (yang_dnode_exists(dnode, "./frr-zebra:source-v4")) + vty_out(vty, " set src %s\n", + yang_dnode_get_string(dnode, "./frr-zebra:source-v4")); + else + vty_out(vty, " set src %s\n", + yang_dnode_get_string(dnode, "./frr-zebra:source-v6")); + break; + } +} + +DEFPY_YANG( + rmap_onmatch_next, rmap_onmatch_next_cmd, + "on-match next", + "Exit policy on matches\n" + "Next clause\n") +{ + nb_cli_enqueue_change(vty, "./exit-policy", NB_OP_MODIFY, "next"); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + no_rmap_onmatch_next, + no_rmap_onmatch_next_cmd, + "no on-match next", + NO_STR + "Exit policy on matches\n" + "Next clause\n") +{ + nb_cli_enqueue_change(vty, "./exit-policy", NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + rmap_onmatch_goto, rmap_onmatch_goto_cmd, + "on-match goto (1-65535)$rm_num", + "Exit policy on matches\n" + "Goto Clause number\n" + "Number\n") +{ + nb_cli_enqueue_change(vty, "./exit-policy", NB_OP_MODIFY, "goto"); + nb_cli_enqueue_change(vty, "./goto-value", NB_OP_MODIFY, rm_num_str); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + no_rmap_onmatch_goto, no_rmap_onmatch_goto_cmd, + "no on-match goto", + NO_STR + "Exit policy on matches\n" + "Goto Clause number\n") +{ + nb_cli_enqueue_change(vty, "./exit-policy", NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +/* Cisco/GNU Zebra compatibility aliases */ +ALIAS_YANG( + rmap_onmatch_goto, rmap_continue_cmd, + "continue (1-65535)$rm_num", + "Continue on a different entry within the route-map\n" + "Route-map entry sequence number\n") + +ALIAS_YANG( + no_rmap_onmatch_goto, no_rmap_continue_cmd, + "no continue [(1-65535)]", + NO_STR + "Continue on a different entry within the route-map\n" + "Route-map entry sequence number\n") + +void route_map_exit_policy_show(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + int exit_policy = yang_dnode_get_enum(dnode, NULL); + + switch (exit_policy) { + case 0: /* permit-or-deny */ + /* NOTHING: default option. */ + break; + case 1: /* next */ + vty_out(vty, " on-match next\n"); + break; + case 2: /* goto */ + vty_out(vty, " on-match goto %s\n", + yang_dnode_get_string(dnode, "../goto-value")); + break; + } +} + +DEFPY_YANG( + rmap_call, rmap_call_cmd, + "call WORD$name", + "Jump to another Route-Map after match+set\n" + "Target route-map name\n") +{ + nb_cli_enqueue_change(vty, "./call", NB_OP_MODIFY, name); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + no_rmap_call, no_rmap_call_cmd, + "no call [NAME]", + NO_STR + "Jump to another Route-Map after match+set\n" + "Target route-map name\n") +{ + nb_cli_enqueue_change(vty, "./call", NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +void route_map_call_show(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + vty_out(vty, " call %s\n", yang_dnode_get_string(dnode, NULL)); +} + +DEFPY_YANG( + rmap_description, rmap_description_cmd, + "description LINE...", + "Route-map comment\n" + "Comment describing this route-map rule\n") +{ + char *desc; + int rv; + + desc = argv_concat(argv, argc, 1); + nb_cli_enqueue_change(vty, "./description", NB_OP_MODIFY, desc); + rv = nb_cli_apply_changes(vty, NULL); + XFREE(MTYPE_TMP, desc); + + return rv; +} + +DEFUN_YANG (no_rmap_description, + no_rmap_description_cmd, + "no description", + NO_STR + "Route-map comment\n") +{ + nb_cli_enqueue_change(vty, "./description", NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +void route_map_description_show(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + vty_out(vty, " description %s\n", yang_dnode_get_string(dnode, NULL)); +} + +static int route_map_config_write(struct vty *vty) +{ + struct lyd_node *dnode; + int written = 0; + + dnode = yang_dnode_get(running_config->dnode, + "/frr-route-map:lib"); + if (dnode) { + nb_cli_show_dnode_cmds(vty, dnode, false); + written = 1; + } + + return written; +} + +/* Route map node structure. */ +static int route_map_config_write(struct vty *vty); +static struct cmd_node rmap_node = { + .name = "routemap", + .node = RMAP_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-route-map)# ", + .config_write = route_map_config_write, +}; + +static void rmap_autocomplete(vector comps, struct cmd_token *token) +{ + struct route_map *map; + + for (map = route_map_master.head; map; map = map->next) + vector_set(comps, XSTRDUP(MTYPE_COMPLETION, map->name)); +} + +static const struct cmd_variable_handler rmap_var_handlers[] = { + {.varname = "route_map", .completions = rmap_autocomplete}, + {.tokenname = "ROUTEMAP_NAME", .completions = rmap_autocomplete}, + {.tokenname = "RMAP_NAME", .completions = rmap_autocomplete}, + {.completions = NULL} +}; + +void route_map_cli_init(void) +{ + /* Auto complete handler. */ + cmd_variable_handler_register(rmap_var_handlers); + + /* CLI commands. */ + install_node(&rmap_node); + install_default(RMAP_NODE); + install_element(CONFIG_NODE, &route_map_cmd); + install_element(CONFIG_NODE, &no_route_map_cmd); + install_element(CONFIG_NODE, &no_route_map_all_cmd); + + /* Install the on-match stuff */ + install_element(RMAP_NODE, &rmap_onmatch_next_cmd); + install_element(RMAP_NODE, &no_rmap_onmatch_next_cmd); + install_element(RMAP_NODE, &rmap_onmatch_goto_cmd); + install_element(RMAP_NODE, &no_rmap_onmatch_goto_cmd); + install_element(RMAP_NODE, &rmap_continue_cmd); + install_element(RMAP_NODE, &no_rmap_continue_cmd); + + /* Install the call stuff. */ + install_element(RMAP_NODE, &rmap_call_cmd); + install_element(RMAP_NODE, &no_rmap_call_cmd); + + /* Install description commands. */ + install_element(RMAP_NODE, &rmap_description_cmd); + install_element(RMAP_NODE, &no_rmap_description_cmd); + + /* Install 'match' commands. */ + install_element(RMAP_NODE, &match_interface_cmd); + install_element(RMAP_NODE, &no_match_interface_cmd); + + install_element(RMAP_NODE, &match_ip_address_cmd); + install_element(RMAP_NODE, &no_match_ip_address_cmd); + + install_element(RMAP_NODE, &match_ip_address_prefix_list_cmd); + install_element(RMAP_NODE, &no_match_ip_address_prefix_list_cmd); + + install_element(RMAP_NODE, &match_ip_next_hop_cmd); + install_element(RMAP_NODE, &no_match_ip_next_hop_cmd); + + install_element(RMAP_NODE, &match_ip_next_hop_prefix_list_cmd); + install_element(RMAP_NODE, &no_match_ip_next_hop_prefix_list_cmd); + + install_element(RMAP_NODE, &match_ip_next_hop_type_cmd); + install_element(RMAP_NODE, &no_match_ip_next_hop_type_cmd); + + install_element(RMAP_NODE, &match_ipv6_address_cmd); + install_element(RMAP_NODE, &no_match_ipv6_address_cmd); + + install_element(RMAP_NODE, &match_ipv6_address_prefix_list_cmd); + install_element(RMAP_NODE, &no_match_ipv6_address_prefix_list_cmd); + + install_element(RMAP_NODE, &match_ipv6_next_hop_type_cmd); + install_element(RMAP_NODE, &no_match_ipv6_next_hop_type_cmd); + + install_element(RMAP_NODE, &match_metric_cmd); + install_element(RMAP_NODE, &no_match_metric_cmd); + + install_element(RMAP_NODE, &match_tag_cmd); + install_element(RMAP_NODE, &no_match_tag_cmd); + + /* Install 'set' commands. */ + install_element(RMAP_NODE, &set_ip_nexthop_cmd); + install_element(RMAP_NODE, &no_set_ip_nexthop_cmd); + + install_element(RMAP_NODE, &set_ipv6_nexthop_local_cmd); + install_element(RMAP_NODE, &no_set_ipv6_nexthop_local_cmd); + + install_element(RMAP_NODE, &set_metric_cmd); + install_element(RMAP_NODE, &no_set_metric_cmd); + + install_element(RMAP_NODE, &set_tag_cmd); + install_element(RMAP_NODE, &no_set_tag_cmd); +} diff --git a/lib/routemap_northbound.c b/lib/routemap_northbound.c new file mode 100644 index 0000000000..597a6b1ecf --- /dev/null +++ b/lib/routemap_northbound.c @@ -0,0 +1,1308 @@ +/* + * Route map northbound implementation. + * + * Copyright (C) 2019 Network Device Education Foundation, Inc. ("NetDEF") + * Rafael Zalamena + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#include + +#include "lib/command.h" +#include "lib/log.h" +#include "lib/northbound.h" +#include "lib/routemap.h" + +/* + * Auxiliary functions to avoid code duplication: + * + * lib_route_map_entry_set_destroy: unset `set` commands. + * lib_route_map_entry_match_destroy: unset `match` commands. + */ +int lib_route_map_entry_match_destroy(struct nb_cb_destroy_args *args) +{ + struct routemap_hook_context *rhc; + int rv; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + rhc = nb_running_get_entry(args->dnode, NULL, true); + if (rhc->rhc_mhook == NULL) + return NB_OK; + + rv = rhc->rhc_mhook(NULL, rhc->rhc_rmi, rhc->rhc_rule, NULL, + rhc->rhc_event); + if (rv != CMD_SUCCESS) + return NB_ERR_INCONSISTENCY; + + return NB_OK; +} + +int lib_route_map_entry_set_destroy(struct nb_cb_destroy_args *args) +{ + struct routemap_hook_context *rhc; + int rv; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + rhc = nb_running_get_entry(args->dnode, NULL, true); + if (rhc->rhc_shook == NULL) + return NB_OK; + + rv = rhc->rhc_shook(NULL, rhc->rhc_rmi, rhc->rhc_rule, NULL); + if (rv != CMD_SUCCESS) + return NB_ERR_INCONSISTENCY; + + return NB_OK; +} + +/* + * Auxiliary hook context list manipulation functions. + */ +struct routemap_hook_context * +routemap_hook_context_insert(struct route_map_index *rmi) +{ + struct routemap_hook_context *rhc; + + rhc = XCALLOC(MTYPE_TMP, sizeof(*rhc)); + rhc->rhc_rmi = rmi; + TAILQ_INSERT_TAIL(&rmi->rhclist, rhc, rhc_entry); + + return rhc; +} + +void routemap_hook_context_free(struct routemap_hook_context *rhc) +{ + struct route_map_index *rmi = rhc->rhc_rmi; + + TAILQ_REMOVE(&rmi->rhclist, rhc, rhc_entry); + XFREE(MTYPE_TMP, rhc); +} + +/* + * XPath: /frr-route-map:lib/route-map + */ +static int lib_route_map_create(struct nb_cb_create_args *args) +{ + struct route_map *rm; + const char *rm_name; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + rm_name = yang_dnode_get_string(args->dnode, "./name"); + rm = route_map_get(rm_name); + nb_running_set_entry(args->dnode, rm); + break; + } + + return NB_OK; +} + +static int lib_route_map_destroy(struct nb_cb_destroy_args *args) +{ + struct route_map *rm; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + rm = nb_running_unset_entry(args->dnode); + route_map_delete(rm); + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-route-map:lib/route-map/entry + */ +static int lib_route_map_entry_create(struct nb_cb_create_args *args) +{ + struct route_map_index *rmi; + struct route_map *rm; + uint16_t sequence; + int action; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + sequence = yang_dnode_get_uint16(args->dnode, "./sequence"); + action = yang_dnode_get_enum(args->dnode, "./action") == 0 + ? RMAP_PERMIT + : RMAP_DENY; + rm = nb_running_get_entry(args->dnode, NULL, true); + rmi = route_map_index_get(rm, action, sequence); + nb_running_set_entry(args->dnode, rmi); + break; + } + + return NB_OK; +} + +static int lib_route_map_entry_destroy(struct nb_cb_destroy_args *args) +{ + struct route_map_index *rmi; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + rmi = nb_running_unset_entry(args->dnode); + route_map_index_delete(rmi, 1); + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/description + */ +static int +lib_route_map_entry_description_modify(struct nb_cb_modify_args *args) +{ + struct route_map_index *rmi; + const char *description; + + switch (args->event) { + case NB_EV_VALIDATE: + /* NOTHING */ + break; + case NB_EV_PREPARE: + description = yang_dnode_get_string(args->dnode, NULL); + args->resource->ptr = XSTRDUP(MTYPE_TMP, description); + if (args->resource->ptr == NULL) + return NB_ERR_RESOURCE; + break; + case NB_EV_ABORT: + XFREE(MTYPE_TMP, args->resource->ptr); + break; + case NB_EV_APPLY: + rmi = nb_running_get_entry(args->dnode, NULL, true); + XFREE(MTYPE_TMP, rmi->description); + rmi->description = args->resource->ptr; + break; + } + + return NB_OK; +} + +static int +lib_route_map_entry_description_destroy(struct nb_cb_destroy_args *args) +{ + struct route_map_index *rmi; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + rmi = nb_running_get_entry(args->dnode, NULL, true); + XFREE(MTYPE_TMP, rmi->description); + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/action + */ +static int lib_route_map_entry_action_modify(struct nb_cb_modify_args *args) +{ + struct route_map_index *rmi; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + rmi = nb_running_get_entry(args->dnode, NULL, true); + rmi->type = yang_dnode_get_enum(args->dnode, NULL); + /* TODO: notify? */ + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/call + */ +static int lib_route_map_entry_call_modify(struct nb_cb_modify_args *args) +{ + struct route_map_index *rmi; + const char *rm_name, *rmn_name; + + switch (args->event) { + case NB_EV_VALIDATE: + rm_name = yang_dnode_get_string(args->dnode, "../../name"); + rmn_name = yang_dnode_get_string(args->dnode, NULL); + /* Don't allow to jump to the same route map instance. */ + if (strcmp(rm_name, rmn_name) == 0) + return NB_ERR_VALIDATION; + + /* TODO: detect circular route map sequences. */ + break; + case NB_EV_PREPARE: + rmn_name = yang_dnode_get_string(args->dnode, NULL); + args->resource->ptr = XSTRDUP(MTYPE_ROUTE_MAP_NAME, rmn_name); + break; + case NB_EV_ABORT: + XFREE(MTYPE_ROUTE_MAP_NAME, args->resource->ptr); + break; + case NB_EV_APPLY: + rmi = nb_running_get_entry(args->dnode, NULL, true); + if (rmi->nextrm) { + route_map_upd8_dependency(RMAP_EVENT_CALL_DELETED, + rmi->nextrm, rmi->map->name); + XFREE(MTYPE_ROUTE_MAP_NAME, rmi->nextrm); + } + rmi->nextrm = args->resource->ptr; + route_map_upd8_dependency(RMAP_EVENT_CALL_ADDED, rmi->nextrm, + rmi->map->name); + break; + } + + return NB_OK; +} + +static int lib_route_map_entry_call_destroy(struct nb_cb_destroy_args *args) +{ + struct route_map_index *rmi; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + rmi = nb_running_get_entry(args->dnode, NULL, true); + route_map_upd8_dependency(RMAP_EVENT_CALL_DELETED, rmi->nextrm, + rmi->map->name); + XFREE(MTYPE_ROUTE_MAP_NAME, rmi->nextrm); + rmi->nextrm = NULL; + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/exit-policy + */ +static int +lib_route_map_entry_exit_policy_modify(struct nb_cb_modify_args *args) +{ + struct route_map_index *rmi; + int rm_action; + int policy; + + switch (args->event) { + case NB_EV_VALIDATE: + policy = yang_dnode_get_enum(args->dnode, NULL); + switch (policy) { + case 0: /* permit-or-deny */ + break; + case 1: /* next */ + /* FALLTHROUGH */ + case 2: /* goto */ + rm_action = + yang_dnode_get_enum(args->dnode, "../action"); + if (rm_action == 1 /* deny */) { + /* + * On deny it is not possible to 'goto' + * anywhere. + */ + return NB_ERR_VALIDATION; + } + break; + } + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + rmi = nb_running_get_entry(args->dnode, NULL, true); + policy = yang_dnode_get_enum(args->dnode, NULL); + + switch (policy) { + case 0: /* permit-or-deny */ + rmi->exitpolicy = RMAP_EXIT; + break; + case 1: /* next */ + rmi->exitpolicy = RMAP_NEXT; + break; + case 2: /* goto */ + rmi->exitpolicy = RMAP_GOTO; + break; + } + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/goto-value + */ +static int lib_route_map_entry_goto_value_modify(struct nb_cb_modify_args *args) +{ + struct route_map_index *rmi; + uint16_t rmi_index; + uint16_t rmi_next; + + switch (args->event) { + case NB_EV_VALIDATE: + rmi_index = yang_dnode_get_uint16(args->dnode, "../sequence"); + rmi_next = yang_dnode_get_uint16(args->dnode, NULL); + if (rmi_next <= rmi_index) { + /* Can't jump backwards on a route map. */ + return NB_ERR_VALIDATION; + } + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + rmi = nb_running_get_entry(args->dnode, NULL, true); + rmi->nextpref = yang_dnode_get_uint16(args->dnode, NULL); + break; + } + + return NB_OK; +} + +static int +lib_route_map_entry_goto_value_destroy(struct nb_cb_destroy_args *args) +{ + struct route_map_index *rmi; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + rmi = nb_running_get_entry(args->dnode, NULL, true); + rmi->nextpref = 0; + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/match-condition + */ +static int +lib_route_map_entry_match_condition_create(struct nb_cb_create_args *args) +{ + struct routemap_hook_context *rhc; + struct route_map_index *rmi; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + rmi = nb_running_get_entry(args->dnode, NULL, true); + rhc = routemap_hook_context_insert(rmi); + nb_running_set_entry(args->dnode, rhc); + break; + } + + return NB_OK; +} + +static int +lib_route_map_entry_match_condition_destroy(struct nb_cb_destroy_args *args) +{ + struct routemap_hook_context *rhc; + int rv; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + rv = lib_route_map_entry_match_destroy(args); + rhc = nb_running_unset_entry(args->dnode); + routemap_hook_context_free(rhc); + + return rv; +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/match-condition/interface + */ +static int lib_route_map_entry_match_condition_interface_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *ifname; + int rv; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + /* Check for hook function. */ + if (rmap_match_set_hook.match_interface == NULL) + return NB_OK; + + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + ifname = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_mhook = rmap_match_set_hook.no_match_interface; + rhc->rhc_rule = "interface"; + rhc->rhc_event = RMAP_EVENT_MATCH_DELETED; + + rv = rmap_match_set_hook.match_interface(NULL, rhc->rhc_rmi, + "interface", ifname, + RMAP_EVENT_MATCH_ADDED); + if (rv != CMD_SUCCESS) { + rhc->rhc_mhook = NULL; + return NB_ERR_INCONSISTENCY; + } + + return NB_OK; +} + +static int lib_route_map_entry_match_condition_interface_destroy( + struct nb_cb_destroy_args *args) +{ + return lib_route_map_entry_match_destroy(args); +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/match-condition/list-name + */ +static int lib_route_map_entry_match_condition_list_name_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *acl; + int condition; + int rv; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + /* Check for hook installation, otherwise we can just stop. */ + acl = yang_dnode_get_string(args->dnode, NULL); + rhc = nb_running_get_entry(args->dnode, NULL, true); + condition = yang_dnode_get_enum(args->dnode, "../condition"); + switch (condition) { + case 1: /* ipv4-address-list */ + if (rmap_match_set_hook.match_ip_address == NULL) + return NB_OK; + rhc->rhc_mhook = rmap_match_set_hook.no_match_ip_address; + rhc->rhc_rule = "ip address"; + rhc->rhc_event = RMAP_EVENT_FILTER_DELETED; + rv = rmap_match_set_hook.match_ip_address( + NULL, rhc->rhc_rmi, "ip address", acl, + RMAP_EVENT_FILTER_ADDED); + break; + case 2: /* ipv4-prefix-list */ + if (rmap_match_set_hook.match_ip_address_prefix_list == NULL) + return NB_OK; + rhc->rhc_mhook = + rmap_match_set_hook.no_match_ip_address_prefix_list; + rhc->rhc_rule = "ip address prefix-list"; + rhc->rhc_event = RMAP_EVENT_PLIST_DELETED; + rv = rmap_match_set_hook.match_ip_address_prefix_list( + NULL, rhc->rhc_rmi, "ip address prefix-list", acl, + RMAP_EVENT_PLIST_ADDED); + break; + case 3: /* ipv4-next-hop-list */ + if (rmap_match_set_hook.match_ip_next_hop == NULL) + return NB_OK; + rhc->rhc_mhook = rmap_match_set_hook.no_match_ip_next_hop; + rhc->rhc_rule = "ip next-hop"; + rhc->rhc_event = RMAP_EVENT_FILTER_DELETED; + rv = rmap_match_set_hook.match_ip_next_hop( + NULL, rhc->rhc_rmi, "ip next-hop", acl, + RMAP_EVENT_FILTER_ADDED); + break; + case 4: /* ipv4-next-hop-prefix-list */ + if (rmap_match_set_hook.match_ip_next_hop_prefix_list == NULL) + return NB_OK; + rhc->rhc_mhook = + rmap_match_set_hook.no_match_ip_next_hop_prefix_list; + rhc->rhc_rule = "ip next-hop prefix-list"; + rhc->rhc_event = RMAP_EVENT_PLIST_DELETED; + rv = rmap_match_set_hook.match_ip_next_hop_prefix_list( + NULL, rhc->rhc_rmi, "ip next-hop prefix-list", acl, + RMAP_EVENT_PLIST_ADDED); + break; + case 6: /* ipv6-address-list */ + if (rmap_match_set_hook.match_ipv6_address == NULL) + return NB_OK; + rhc->rhc_mhook = rmap_match_set_hook.no_match_ipv6_address; + rhc->rhc_rule = "ipv6 address"; + rhc->rhc_event = RMAP_EVENT_FILTER_DELETED; + rv = rmap_match_set_hook.match_ipv6_address( + NULL, rhc->rhc_rmi, "ipv6 address", acl, + RMAP_EVENT_FILTER_ADDED); + break; + case 7: /* ipv6-prefix-list */ + if (rmap_match_set_hook.match_ipv6_address_prefix_list == NULL) + return NB_OK; + rhc->rhc_mhook = + rmap_match_set_hook.no_match_ipv6_address_prefix_list; + rhc->rhc_rule = "ipv6 address prefix-list"; + rhc->rhc_event = RMAP_EVENT_PLIST_DELETED; + rv = rmap_match_set_hook.match_ipv6_address_prefix_list( + NULL, rhc->rhc_rmi, "ipv6 address prefix-list", acl, + RMAP_EVENT_PLIST_ADDED); + break; + default: + rv = CMD_ERR_NO_MATCH; + break; + } + if (rv != CMD_SUCCESS) { + rhc->rhc_mhook = NULL; + return NB_ERR_INCONSISTENCY; + } + + return NB_OK; +} + +static int lib_route_map_entry_match_condition_list_name_destroy( + struct nb_cb_destroy_args *args) +{ + return lib_route_map_entry_match_destroy(args); +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/match-condition/ipv4-next-hop-type + */ +static int lib_route_map_entry_match_condition_ipv4_next_hop_type_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *type; + int rv; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + /* Check for hook function. */ + if (rmap_match_set_hook.match_ip_next_hop_type == NULL) + return NB_OK; + + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + type = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_mhook = rmap_match_set_hook.no_match_ip_next_hop_type; + rhc->rhc_rule = "ip next-hop type"; + rhc->rhc_event = RMAP_EVENT_MATCH_DELETED; + + rv = rmap_match_set_hook.match_ip_next_hop_type( + NULL, rhc->rhc_rmi, "ip next-hop type", type, + RMAP_EVENT_MATCH_ADDED); + if (rv != CMD_SUCCESS) { + rhc->rhc_mhook = NULL; + return NB_ERR_INCONSISTENCY; + } + + return NB_OK; +} + +static int lib_route_map_entry_match_condition_ipv4_next_hop_type_destroy( + struct nb_cb_destroy_args *args) +{ + return lib_route_map_entry_match_destroy(args); +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/match-condition/ipv6-next-hop-type + */ +static int lib_route_map_entry_match_condition_ipv6_next_hop_type_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *type; + int rv; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + /* Check for hook function. */ + if (rmap_match_set_hook.match_ipv6_next_hop_type == NULL) + return NB_OK; + + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + type = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_mhook = rmap_match_set_hook.no_match_ipv6_next_hop_type; + rhc->rhc_rule = "ipv6 next-hop type"; + rhc->rhc_event = RMAP_EVENT_MATCH_DELETED; + + rv = rmap_match_set_hook.match_ipv6_next_hop_type( + NULL, rhc->rhc_rmi, "ipv6 next-hop type", type, + RMAP_EVENT_MATCH_ADDED); + if (rv != CMD_SUCCESS) { + rhc->rhc_mhook = NULL; + return NB_ERR_INCONSISTENCY; + } + + return NB_OK; +} + +static int lib_route_map_entry_match_condition_ipv6_next_hop_type_destroy( + struct nb_cb_destroy_args *args) +{ + return lib_route_map_entry_match_destroy(args); +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/match-condition/metric + */ +static int lib_route_map_entry_match_condition_metric_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *type; + int rv; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + /* Check for hook function. */ + if (rmap_match_set_hook.match_metric == NULL) + return NB_OK; + + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + type = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_mhook = rmap_match_set_hook.no_match_metric; + rhc->rhc_rule = "metric"; + rhc->rhc_event = RMAP_EVENT_MATCH_DELETED; + + rv = rmap_match_set_hook.match_metric(NULL, rhc->rhc_rmi, "metric", + type, RMAP_EVENT_MATCH_ADDED); + if (rv != CMD_SUCCESS) { + rhc->rhc_mhook = NULL; + return NB_ERR_INCONSISTENCY; + } + + return NB_OK; +} + +static int lib_route_map_entry_match_condition_metric_destroy( + struct nb_cb_destroy_args *args) +{ + return lib_route_map_entry_match_destroy(args); +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/match-condition/tag + */ +static int +lib_route_map_entry_match_condition_tag_modify(struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *tag; + int rv; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + /* Check for hook function. */ + if (rmap_match_set_hook.match_tag == NULL) + return NB_OK; + + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + tag = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_mhook = rmap_match_set_hook.no_match_tag; + rhc->rhc_rule = "tag"; + rhc->rhc_event = RMAP_EVENT_MATCH_DELETED; + + rv = rmap_match_set_hook.match_tag(NULL, rhc->rhc_rmi, "tag", tag, + RMAP_EVENT_MATCH_ADDED); + if (rv != CMD_SUCCESS) { + rhc->rhc_mhook = NULL; + return NB_ERR_INCONSISTENCY; + } + + return NB_OK; +} + +static int +lib_route_map_entry_match_condition_tag_destroy(struct nb_cb_destroy_args *args) +{ + return lib_route_map_entry_match_destroy(args); +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/set-action + */ +static int lib_route_map_entry_set_action_create(struct nb_cb_create_args *args) +{ + return lib_route_map_entry_match_condition_create(args); +} + +static int +lib_route_map_entry_set_action_destroy(struct nb_cb_destroy_args *args) +{ + struct routemap_hook_context *rhc; + int rv; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + rv = lib_route_map_entry_set_destroy(args); + rhc = nb_running_unset_entry(args->dnode); + routemap_hook_context_free(rhc); + + return rv; +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/set-action/ipv4-address + */ +static int lib_route_map_entry_set_action_ipv4_address_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *address; + struct in_addr ia; + int rv; + + switch (args->event) { + case NB_EV_VALIDATE: + /* + * NOTE: validate if 'action' is 'ipv4-next-hop', + * currently it is not necessary because this is the + * only implemented action. + */ + yang_dnode_get_ipv4(&ia, args->dnode, NULL); + if (ia.s_addr == INADDR_ANY || IPV4_CLASS_DE(ntohl(ia.s_addr))) + return NB_ERR_VALIDATION; + /* FALLTHROUGH */ + case NB_EV_PREPARE: + case NB_EV_ABORT: + return NB_OK; + case NB_EV_APPLY: + break; + } + + /* Check for hook function. */ + if (rmap_match_set_hook.set_ip_nexthop == NULL) + return NB_OK; + + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + address = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_shook = rmap_match_set_hook.no_set_ip_nexthop; + rhc->rhc_rule = "ip next-hop"; + + rv = rmap_match_set_hook.set_ip_nexthop(NULL, rhc->rhc_rmi, + "ip next-hop", address); + if (rv != CMD_SUCCESS) { + rhc->rhc_shook = NULL; + return NB_ERR_INCONSISTENCY; + } + + return NB_OK; +} + +static int lib_route_map_entry_set_action_ipv4_address_destroy( + struct nb_cb_destroy_args *args) +{ + return lib_route_map_entry_set_destroy(args); +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/set-action/ipv6-address + */ +static int lib_route_map_entry_set_action_ipv6_address_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *address; + struct in6_addr i6a; + int rv; + + switch (args->event) { + case NB_EV_VALIDATE: + /* + * NOTE: validate if 'action' is 'ipv6-next-hop', + * currently it is not necessary because this is the + * only implemented action. Other actions might have + * different validations. + */ + yang_dnode_get_ipv6(&i6a, args->dnode, NULL); + if (!IN6_IS_ADDR_LINKLOCAL(&i6a)) + return NB_ERR_VALIDATION; + /* FALLTHROUGH */ + case NB_EV_PREPARE: + case NB_EV_ABORT: + return NB_OK; + case NB_EV_APPLY: + break; + } + + /* Check for hook function. */ + if (rmap_match_set_hook.set_ipv6_nexthop_local == NULL) + return NB_OK; + + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + address = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_shook = rmap_match_set_hook.no_set_ipv6_nexthop_local; + rhc->rhc_rule = "ipv6 next-hop local"; + + rv = rmap_match_set_hook.set_ipv6_nexthop_local( + NULL, rhc->rhc_rmi, "ipv6 next-hop local", address); + if (rv != CMD_SUCCESS) { + rhc->rhc_shook = NULL; + return NB_ERR_INCONSISTENCY; + } + + return NB_OK; +} + +static int lib_route_map_entry_set_action_ipv6_address_destroy( + struct nb_cb_destroy_args *args) +{ + return lib_route_map_entry_set_destroy(args); +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/set-action/value + */ +static int set_action_modify(enum nb_event event, const struct lyd_node *dnode, + union nb_resource *resource, const char *value) +{ + struct routemap_hook_context *rhc; + int rv; + + /* + * NOTE: validate if 'action' is 'metric', currently it is not + * necessary because this is the only implemented action. Other + * actions might have different validations. + */ + if (event != NB_EV_APPLY) + return NB_OK; + + /* Check for hook function. */ + if (rmap_match_set_hook.set_metric == NULL) + return NB_OK; + + /* Add configuration. */ + rhc = nb_running_get_entry(dnode, NULL, true); + + /* Set destroy information. */ + rhc->rhc_shook = rmap_match_set_hook.no_set_metric; + rhc->rhc_rule = "metric"; + + rv = rmap_match_set_hook.set_metric(NULL, rhc->rhc_rmi, "metric", + value); + if (rv != CMD_SUCCESS) { + rhc->rhc_shook = NULL; + return NB_ERR_INCONSISTENCY; + } + + return NB_OK; +} + +static int +lib_route_map_entry_set_action_value_modify(struct nb_cb_modify_args *args) +{ + const char *metric = yang_dnode_get_string(args->dnode, NULL); + + return set_action_modify(args->event, args->dnode, args->resource, + metric); +} + +static int +lib_route_map_entry_set_action_value_destroy(struct nb_cb_destroy_args *args) +{ + return lib_route_map_entry_set_destroy(args); +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/set-action/add-metric + */ +static int +lib_route_map_entry_set_action_add_metric_modify(struct nb_cb_modify_args *args) +{ + char metric_str[16]; + + if (args->event == NB_EV_VALIDATE + && yang_dnode_get_uint32(args->dnode, NULL) == 0) { + snprintf(args->errmsg, args->errmsg_len, + "Can't add zero to metric"); + return NB_ERR_VALIDATION; + } + + snprintf(metric_str, sizeof(metric_str), "+%s", + yang_dnode_get_string(args->dnode, NULL)); + return set_action_modify(args->event, args->dnode, args->resource, + metric_str); +} + +static int lib_route_map_entry_set_action_add_metric_destroy( + struct nb_cb_destroy_args *args) +{ + return lib_route_map_entry_set_action_value_destroy(args); +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/set-action/subtract-metric + */ +static int lib_route_map_entry_set_action_subtract_metric_modify( + struct nb_cb_modify_args *args) +{ + char metric_str[16]; + + if (args->event == NB_EV_VALIDATE + && yang_dnode_get_uint32(args->dnode, NULL) == 0) { + snprintf(args->errmsg, args->errmsg_len, + "Can't subtract zero from metric"); + return NB_ERR_VALIDATION; + } + + snprintf(metric_str, sizeof(metric_str), "-%s", + yang_dnode_get_string(args->dnode, NULL)); + return set_action_modify(args->event, args->dnode, args->resource, + metric_str); +} + +static int lib_route_map_entry_set_action_subtract_metric_destroy( + struct nb_cb_destroy_args *args) +{ + return lib_route_map_entry_set_action_value_destroy(args); +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/set-action/use-round-trip-time + */ +static int lib_route_map_entry_set_action_use_round_trip_time_modify( + struct nb_cb_modify_args *args) +{ + return set_action_modify(args->event, args->dnode, args->resource, + "rtt"); +} + +static int lib_route_map_entry_set_action_use_round_trip_time_destroy( + struct nb_cb_destroy_args *args) +{ + return lib_route_map_entry_set_action_value_destroy(args); +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/set-action/add-round-trip-time + */ +static int lib_route_map_entry_set_action_add_round_trip_time_modify( + struct nb_cb_modify_args *args) +{ + return set_action_modify(args->event, args->dnode, args->resource, + "+rtt"); +} + +static int lib_route_map_entry_set_action_add_round_trip_time_destroy( + struct nb_cb_destroy_args *args) +{ + return lib_route_map_entry_set_action_value_destroy(args); +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/set-action/subtract-round-trip-time + */ +static int lib_route_map_entry_set_action_subtract_round_trip_time_modify( + struct nb_cb_modify_args *args) +{ + return set_action_modify(args->event, args->dnode, args->resource, + "-rtt"); +} + +static int lib_route_map_entry_set_action_subtract_round_trip_time_destroy( + struct nb_cb_destroy_args *args) +{ + return lib_route_map_entry_set_action_value_destroy(args); +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/set-action/tag + */ +static int +lib_route_map_entry_set_action_tag_modify(struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *tag; + int rv; + + /* + * NOTE: validate if 'action' is 'tag', currently it is not + * necessary because this is the only implemented action. Other + * actions might have different validations. + */ + if (args->event != NB_EV_APPLY) + return NB_OK; + + /* Check for hook function. */ + if (rmap_match_set_hook.set_tag == NULL) + return NB_OK; + + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + tag = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_shook = rmap_match_set_hook.no_set_tag; + rhc->rhc_rule = "tag"; + + rv = rmap_match_set_hook.set_tag(NULL, rhc->rhc_rmi, "tag", tag); + if (rv != CMD_SUCCESS) { + rhc->rhc_shook = NULL; + return NB_ERR_INCONSISTENCY; + } + + return NB_OK; +} + +static int +lib_route_map_entry_set_action_tag_destroy(struct nb_cb_destroy_args *args) +{ + return lib_route_map_entry_set_destroy(args); +} + +/* clang-format off */ +const struct frr_yang_module_info frr_route_map_info = { + .name = "frr-route-map", + .nodes = { + { + .xpath = "/frr-route-map:lib/route-map", + .cbs = { + .create = lib_route_map_create, + .destroy = lib_route_map_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry", + .cbs = { + .create = lib_route_map_entry_create, + .destroy = lib_route_map_entry_destroy, + .cli_show = route_map_instance_show, + .cli_show_end = route_map_instance_show_end, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/description", + .cbs = { + .modify = lib_route_map_entry_description_modify, + .destroy = lib_route_map_entry_description_destroy, + .cli_show = route_map_description_show, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/action", + .cbs = { + .modify = lib_route_map_entry_action_modify, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/call", + .cbs = { + .modify = lib_route_map_entry_call_modify, + .destroy = lib_route_map_entry_call_destroy, + .cli_show = route_map_call_show, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/exit-policy", + .cbs = { + .modify = lib_route_map_entry_exit_policy_modify, + .cli_show = route_map_exit_policy_show, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/goto-value", + .cbs = { + .modify = lib_route_map_entry_goto_value_modify, + .destroy = lib_route_map_entry_goto_value_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/match-condition", + .cbs = { + .create = lib_route_map_entry_match_condition_create, + .destroy = lib_route_map_entry_match_condition_destroy, + .cli_show = route_map_condition_show, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/match-condition/interface", + .cbs = { + .modify = lib_route_map_entry_match_condition_interface_modify, + .destroy = lib_route_map_entry_match_condition_interface_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/match-condition/list-name", + .cbs = { + .modify = lib_route_map_entry_match_condition_list_name_modify, + .destroy = lib_route_map_entry_match_condition_list_name_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/match-condition/ipv4-next-hop-type", + .cbs = { + .modify = lib_route_map_entry_match_condition_ipv4_next_hop_type_modify, + .destroy = lib_route_map_entry_match_condition_ipv4_next_hop_type_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/match-condition/ipv6-next-hop-type", + .cbs = { + .modify = lib_route_map_entry_match_condition_ipv6_next_hop_type_modify, + .destroy = lib_route_map_entry_match_condition_ipv6_next_hop_type_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/match-condition/metric", + .cbs = { + .modify = lib_route_map_entry_match_condition_metric_modify, + .destroy = lib_route_map_entry_match_condition_metric_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/match-condition/tag", + .cbs = { + .modify = lib_route_map_entry_match_condition_tag_modify, + .destroy = lib_route_map_entry_match_condition_tag_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action", + .cbs = { + .create = lib_route_map_entry_set_action_create, + .destroy = lib_route_map_entry_set_action_destroy, + .cli_show = route_map_action_show, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/ipv4-address", + .cbs = { + .modify = lib_route_map_entry_set_action_ipv4_address_modify, + .destroy = lib_route_map_entry_set_action_ipv4_address_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/ipv6-address", + .cbs = { + .modify = lib_route_map_entry_set_action_ipv6_address_modify, + .destroy = lib_route_map_entry_set_action_ipv6_address_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/value", + .cbs = { + .modify = lib_route_map_entry_set_action_value_modify, + .destroy = lib_route_map_entry_set_action_value_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/add-metric", + .cbs = { + .modify = lib_route_map_entry_set_action_add_metric_modify, + .destroy = lib_route_map_entry_set_action_add_metric_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/subtract-metric", + .cbs = { + .modify = lib_route_map_entry_set_action_subtract_metric_modify, + .destroy = lib_route_map_entry_set_action_subtract_metric_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/use-round-trip-time", + .cbs = { + .modify = lib_route_map_entry_set_action_use_round_trip_time_modify, + .destroy = lib_route_map_entry_set_action_use_round_trip_time_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/add-round-trip-time", + .cbs = { + .modify = lib_route_map_entry_set_action_add_round_trip_time_modify, + .destroy = lib_route_map_entry_set_action_add_round_trip_time_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/subtract-round-trip-time", + .cbs = { + .modify = lib_route_map_entry_set_action_subtract_round_trip_time_modify, + .destroy = lib_route_map_entry_set_action_subtract_round_trip_time_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/tag", + .cbs = { + .modify = lib_route_map_entry_set_action_tag_modify, + .destroy = lib_route_map_entry_set_action_tag_destroy, + } + }, + { + .xpath = NULL, + }, + } +}; diff --git a/lib/routing_nb.c b/lib/routing_nb.c new file mode 100644 index 0000000000..0160354a7e --- /dev/null +++ b/lib/routing_nb.c @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2018 Vmware + * Vishal Dhingra + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include "northbound.h" +#include "libfrr.h" +#include "routing_nb.h" + + + +/* clang-format off */ +const struct frr_yang_module_info frr_routing_info = { + .name = "frr-routing", + .nodes = { + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol", + .cbs = { + .create = routing_control_plane_protocols_control_plane_protocol_create, + .destroy = routing_control_plane_protocols_control_plane_protocol_destroy, + } + }, + { + .xpath = NULL, + }, + } +}; diff --git a/lib/routing_nb.h b/lib/routing_nb.h new file mode 100644 index 0000000000..ffba631a10 --- /dev/null +++ b/lib/routing_nb.h @@ -0,0 +1,31 @@ +#ifndef _FRR_ROUTING_NB_H_ +#define _FRR_ROUTING_NB_H_ + +extern const struct frr_yang_module_info frr_routing_info; + +/* Mandatory callbacks. */ +int routing_control_plane_protocols_control_plane_protocol_create( + struct nb_cb_create_args *args); +int routing_control_plane_protocols_control_plane_protocol_destroy( + struct nb_cb_destroy_args *args); + +#define FRR_ROUTING_XPATH \ + "/frr-routing:routing/control-plane-protocols/control-plane-protocol" + +#define FRR_ROUTING_KEY_XPATH \ + "/frr-routing:routing/control-plane-protocols/" \ + "control-plane-protocol[type='%s'][name='%s'][vrf='%s']" + +#define FRR_ROUTING_KEY_XPATH_VRF \ + "/frr-routing:routing/control-plane-protocols/" \ + "control-plane-protocol[vrf='%s']" + +/* + * callbacks for routing to handle configuration events + * based on the control plane protocol + */ +DECLARE_HOOK(routing_conf_event, (struct nb_cb_create_args *args), (args)) + +void routing_control_plane_protocols_register_vrf_dependency(void); + +#endif /* _FRR_ROUTING_NB_H_ */ diff --git a/lib/routing_nb_config.c b/lib/routing_nb_config.c new file mode 100644 index 0000000000..17698d2b87 --- /dev/null +++ b/lib/routing_nb_config.c @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2018 Vmware + * Vishal Dhingra + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "northbound.h" +#include "libfrr.h" +#include "vrf.h" +#include "lib_errors.h" +#include "routing_nb.h" + + +DEFINE_HOOK(routing_conf_event, (struct nb_cb_create_args *args), (args)) + +/* + * XPath: /frr-routing:routing/control-plane-protocols/control-plane-protocol + */ + +int routing_control_plane_protocols_control_plane_protocol_create( + struct nb_cb_create_args *args) +{ + struct vrf *vrf; + const char *vrfname; + + switch (args->event) { + case NB_EV_VALIDATE: + if (hook_call(routing_conf_event, args)) + return NB_ERR_VALIDATION; + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* + * If the daemon relies on the VRF pointer stored in this + * dnode, then it should register the dependency between this + * module and the VRF module using + * routing_control_plane_protocols_register_vrf_dependency. + * If such dependency is not registered, then nothing is + * stored in the dnode. If the dependency is registered, + * find the vrf and store the pointer. + */ + if (nb_node_has_dependency(args->dnode->schema->priv)) { + vrfname = yang_dnode_get_string(args->dnode, "./vrf"); + vrf = vrf_lookup_by_name(vrfname); + assert(vrf); + nb_running_set_entry(args->dnode, vrf); + } + break; + }; + + return NB_OK; +} + +int routing_control_plane_protocols_control_plane_protocol_destroy( + struct nb_cb_destroy_args *args) +{ + if (args->event != NB_EV_APPLY) + return NB_OK; + + /* + * If dependency on VRF module is registered, then VRF + * pointer was stored and must be cleared. + */ + if (nb_node_has_dependency(args->dnode->schema->priv)) + nb_running_unset_entry(args->dnode); + + return NB_OK; +} + +static void vrf_to_control_plane_protocol(const struct lyd_node *dnode, + char *xpath) +{ + const char *vrf; + + vrf = yang_dnode_get_string(dnode, "./name"); + + snprintf(xpath, XPATH_MAXLEN, FRR_ROUTING_KEY_XPATH_VRF, vrf); +} + +static void control_plane_protocol_to_vrf(const struct lyd_node *dnode, + char *xpath) +{ + const char *vrf; + + vrf = yang_dnode_get_string(dnode, "./vrf"); + + snprintf(xpath, XPATH_MAXLEN, FRR_VRF_KEY_XPATH, vrf); +} + +void routing_control_plane_protocols_register_vrf_dependency(void) +{ + struct nb_dependency_callbacks cbs; + + cbs.get_dependant_xpath = vrf_to_control_plane_protocol; + cbs.get_dependency_xpath = control_plane_protocol_to_vrf; + + nb_node_set_dependency_cbs(FRR_VRF_XPATH, FRR_ROUTING_XPATH, &cbs); +} diff --git a/lib/sbuf.c b/lib/sbuf.c index 03a2be3e09..c04af153b1 100644 --- a/lib/sbuf.c +++ b/lib/sbuf.c @@ -22,6 +22,7 @@ */ #include +#include "printfrr.h" #include "sbuf.h" #include "memory.h" @@ -68,7 +69,7 @@ void sbuf_push(struct sbuf *buf, int indent, const char *format, ...) written1 = indent; va_start(args, format); - written2 = vsnprintf(NULL, 0, format, args); + written2 = vsnprintfrr(NULL, 0, format, args); va_end(args); new_size = buf->size; @@ -92,8 +93,8 @@ void sbuf_push(struct sbuf *buf, int indent, const char *format, ...) buf->pos = buf->size; va_start(args, format); - written = vsnprintf(buf->buf + buf->pos, buf->size - buf->pos, format, - args); + written = vsnprintfrr(buf->buf + buf->pos, buf->size - buf->pos, + format, args); va_end(args); if (written >= 0) diff --git a/lib/sbuf.h b/lib/sbuf.h index b1518a3aa8..9f0311006d 100644 --- a/lib/sbuf.h +++ b/lib/sbuf.h @@ -78,7 +78,7 @@ const char *sbuf_buf(struct sbuf *buf); void sbuf_free(struct sbuf *buf); #include "lib/log.h" void sbuf_push(struct sbuf *buf, int indent, const char *format, ...) - PRINTF_ATTRIBUTE(3, 4); + PRINTFRR(3, 4); #ifdef __cplusplus } diff --git a/lib/seqlock.c b/lib/seqlock.c index 223d14952c..77673146ea 100644 --- a/lib/seqlock.c +++ b/lib/seqlock.c @@ -25,6 +25,7 @@ #include "config.h" #endif +#include #include #include #include @@ -35,44 +36,75 @@ #include "seqlock.h" +/**************************************** + * OS specific synchronization wrappers * + ****************************************/ + +/* + * Linux: sys_futex() + */ #ifdef HAVE_SYNC_LINUX_FUTEX -/* Linux-specific - sys_futex() */ #include #include -static long sys_futex(void *addr1, int op, int val1, struct timespec *timeout, - void *addr2, int val3) +static long sys_futex(void *addr1, int op, int val1, + const struct timespec *timeout, void *addr2, int val3) { return syscall(SYS_futex, addr1, op, val1, timeout, addr2, val3); } #define wait_once(sqlo, val) \ sys_futex((int *)&sqlo->pos, FUTEX_WAIT, (int)val, NULL, NULL, 0) +#define wait_time(sqlo, val, time, reltime) \ + sys_futex((int *)&sqlo->pos, FUTEX_WAIT_BITSET, (int)val, time, \ + NULL, ~0U) #define wait_poke(sqlo) \ sys_futex((int *)&sqlo->pos, FUTEX_WAKE, INT_MAX, NULL, NULL, 0) +/* + * OpenBSD: sys_futex(), almost the same as on Linux + */ #elif defined(HAVE_SYNC_OPENBSD_FUTEX) -/* OpenBSD variant of the above. untested, not upstream in OpenBSD. */ #include #include +#define TIME_RELATIVE 1 + #define wait_once(sqlo, val) \ futex((int *)&sqlo->pos, FUTEX_WAIT, (int)val, NULL, NULL, 0) +#define wait_time(sqlo, val, time, reltime) \ + futex((int *)&sqlo->pos, FUTEX_WAIT, (int)val, reltime, NULL, 0) #define wait_poke(sqlo) \ futex((int *)&sqlo->pos, FUTEX_WAKE, INT_MAX, NULL, NULL, 0) +/* + * FreeBSD: _umtx_op() + */ #elif defined(HAVE_SYNC_UMTX_OP) -/* FreeBSD-specific: umtx_op() */ #include #define wait_once(sqlo, val) \ _umtx_op((void *)&sqlo->pos, UMTX_OP_WAIT_UINT, val, NULL, NULL) +static int wait_time(struct seqlock *sqlo, uint32_t val, + const struct timespec *abstime, + const struct timespec *reltime) +{ + struct _umtx_time t; + t._flags = UMTX_ABSTIME; + t._clockid = CLOCK_MONOTONIC; + memcpy(&t._timeout, abstime, sizeof(t._timeout)); + return _umtx_op((void *)&sqlo->pos, UMTX_OP_WAIT_UINT, val, + (void *)(uintptr_t) sizeof(t), &t); +} #define wait_poke(sqlo) \ _umtx_op((void *)&sqlo->pos, UMTX_OP_WAKE, INT_MAX, NULL, NULL) -#else -/* generic version. used on *BSD, Solaris and OSX. +/* + * generic version. used on NetBSD, Solaris and OSX. really shitty. */ +#else + +#define TIME_ABS_REALTIME 1 #define wait_init(sqlo) do { \ pthread_mutex_init(&sqlo->lock, NULL); \ @@ -80,6 +112,9 @@ static long sys_futex(void *addr1, int op, int val1, struct timespec *timeout, } while (0) #define wait_prep(sqlo) pthread_mutex_lock(&sqlo->lock) #define wait_once(sqlo, val) pthread_cond_wait(&sqlo->wake, &sqlo->lock) +#define wait_time(sqlo, val, time, reltime) \ + pthread_cond_timedwait(&sqlo->wake, \ + &sqlo->lock, time); #define wait_done(sqlo) pthread_mutex_unlock(&sqlo->lock) #define wait_poke(sqlo) do { \ pthread_mutex_lock(&sqlo->lock); \ @@ -103,18 +138,112 @@ void seqlock_wait(struct seqlock *sqlo, seqlock_val_t val) seqlock_assert_valid(val); wait_prep(sqlo); - while (1) { - cur = atomic_load_explicit(&sqlo->pos, memory_order_acquire); - if (!(cur & 1)) + cur = atomic_load_explicit(&sqlo->pos, memory_order_relaxed); + + while (cur & SEQLOCK_HELD) { + cal = SEQLOCK_VAL(cur) - val - 1; + assert(cal < 0x40000000 || cal > 0xc0000000); + if (cal < 0x80000000) break; - cal = cur - val - 1; + + if ((cur & SEQLOCK_WAITERS) + || atomic_compare_exchange_weak_explicit( + &sqlo->pos, &cur, cur | SEQLOCK_WAITERS, + memory_order_relaxed, memory_order_relaxed)) { + wait_once(sqlo, cur | SEQLOCK_WAITERS); + cur = atomic_load_explicit(&sqlo->pos, + memory_order_relaxed); + } + /* else: we failed to swap in cur because it just changed */ + } + wait_done(sqlo); +} + +bool seqlock_timedwait(struct seqlock *sqlo, seqlock_val_t val, + const struct timespec *abs_monotime_limit) +{ +/* + * ABS_REALTIME - used on NetBSD, Solaris and OSX + */ +#ifdef TIME_ABS_REALTIME +#define time_arg1 &abs_rt +#define time_arg2 NULL +#define time_prep + struct timespec curmono, abs_rt; + + clock_gettime(CLOCK_MONOTONIC, &curmono); + clock_gettime(CLOCK_REALTIME, &abs_rt); + + abs_rt.tv_nsec += abs_monotime_limit->tv_nsec - curmono.tv_nsec; + if (abs_rt.tv_nsec < 0) { + abs_rt.tv_sec--; + abs_rt.tv_nsec += 1000000000; + } else if (abs_rt.tv_nsec >= 1000000000) { + abs_rt.tv_sec++; + abs_rt.tv_nsec -= 1000000000; + } + abs_rt.tv_sec += abs_monotime_limit->tv_sec - curmono.tv_sec; + +/* + * RELATIVE - used on OpenBSD (might get a patch to get absolute monotime) + */ +#elif defined(TIME_RELATIVE) + struct timespec reltime; + +#define time_arg1 abs_monotime_limit +#define time_arg2 &reltime +#define time_prep \ + clock_gettime(CLOCK_MONOTONIC, &reltime); \ + reltime.tv_sec = abs_monotime_limit.tv_sec - reltime.tv_sec; \ + reltime.tv_nsec = abs_monotime_limit.tv_nsec - reltime.tv_nsec; \ + if (reltime.tv_nsec < 0) { \ + reltime.tv_sec--; \ + reltime.tv_nsec += 1000000000; \ + } +/* + * FreeBSD & Linux: absolute time re. CLOCK_MONOTONIC + */ +#else +#define time_arg1 abs_monotime_limit +#define time_arg2 NULL +#define time_prep +#endif + + bool ret = true; + seqlock_val_t cur, cal; + + seqlock_assert_valid(val); + + wait_prep(sqlo); + cur = atomic_load_explicit(&sqlo->pos, memory_order_relaxed); + + while (cur & SEQLOCK_HELD) { + cal = SEQLOCK_VAL(cur) - val - 1; assert(cal < 0x40000000 || cal > 0xc0000000); if (cal < 0x80000000) break; - wait_once(sqlo, cur); + if ((cur & SEQLOCK_WAITERS) + || atomic_compare_exchange_weak_explicit( + &sqlo->pos, &cur, cur | SEQLOCK_WAITERS, + memory_order_relaxed, memory_order_relaxed)) { + int rv; + + time_prep + + rv = wait_time(sqlo, cur | SEQLOCK_WAITERS, time_arg1, + time_arg2); + if (rv) { + ret = false; + break; + } + cur = atomic_load_explicit(&sqlo->pos, + memory_order_relaxed); + } } wait_done(sqlo); + + return ret; } bool seqlock_check(struct seqlock *sqlo, seqlock_val_t val) @@ -123,26 +252,32 @@ bool seqlock_check(struct seqlock *sqlo, seqlock_val_t val) seqlock_assert_valid(val); - cur = atomic_load_explicit(&sqlo->pos, memory_order_acquire); - if (!(cur & 1)) - return 1; - cur -= val; + cur = atomic_load_explicit(&sqlo->pos, memory_order_relaxed); + if (!(cur & SEQLOCK_HELD)) + return true; + cur = SEQLOCK_VAL(cur) - val - 1; assert(cur < 0x40000000 || cur > 0xc0000000); return cur < 0x80000000; } void seqlock_acquire_val(struct seqlock *sqlo, seqlock_val_t val) { + seqlock_val_t prev; + seqlock_assert_valid(val); - atomic_store_explicit(&sqlo->pos, val, memory_order_release); - wait_poke(sqlo); + prev = atomic_exchange_explicit(&sqlo->pos, val, memory_order_relaxed); + if (prev & SEQLOCK_WAITERS) + wait_poke(sqlo); } void seqlock_release(struct seqlock *sqlo) { - atomic_store_explicit(&sqlo->pos, 0, memory_order_release); - wait_poke(sqlo); + seqlock_val_t prev; + + prev = atomic_exchange_explicit(&sqlo->pos, 0, memory_order_relaxed); + if (prev & SEQLOCK_WAITERS) + wait_poke(sqlo); } void seqlock_init(struct seqlock *sqlo) @@ -154,14 +289,23 @@ void seqlock_init(struct seqlock *sqlo) seqlock_val_t seqlock_cur(struct seqlock *sqlo) { - return atomic_load_explicit(&sqlo->pos, memory_order_acquire); + return SEQLOCK_VAL(atomic_load_explicit(&sqlo->pos, + memory_order_relaxed)); } seqlock_val_t seqlock_bump(struct seqlock *sqlo) { - seqlock_val_t val; + seqlock_val_t val, cur; + + cur = atomic_load_explicit(&sqlo->pos, memory_order_relaxed); + seqlock_assert_valid(cur); + + do { + val = SEQLOCK_VAL(cur) + SEQLOCK_INCR; + } while (!atomic_compare_exchange_weak_explicit(&sqlo->pos, &cur, val, + memory_order_relaxed, memory_order_relaxed)); - val = atomic_fetch_add_explicit(&sqlo->pos, 2, memory_order_release); - wait_poke(sqlo); + if (cur & SEQLOCK_WAITERS) + wait_poke(sqlo); return val; } diff --git a/lib/seqlock.h b/lib/seqlock.h index eef05a4307..bfbf97890e 100644 --- a/lib/seqlock.h +++ b/lib/seqlock.h @@ -27,6 +27,10 @@ #include #include "frratomic.h" +#ifdef __cplusplus +extern "C" { +#endif + /* * this locking primitive is intended to use in a 1:N setup. * @@ -54,12 +58,28 @@ */ /* use sequentially increasing "ticket numbers". lowest bit will always - * be 1 to have a 'cleared' indication (i.e., counts 1,3,5,7,etc. ) + * be 1 to have a 'cleared' indication (i.e., counts 1,5,9,13,etc. ) + * 2nd lowest bit is used to indicate we have waiters. */ typedef _Atomic uint32_t seqlock_ctr_t; typedef uint32_t seqlock_val_t; -#define seqlock_assert_valid(val) assert(val & 1) +#define seqlock_assert_valid(val) assert((val) & SEQLOCK_HELD) +/* NB: SEQLOCK_WAITERS is only allowed if SEQLOCK_HELD is also set; can't + * have waiters on an unheld seqlock + */ +#define SEQLOCK_HELD (1U << 0) +#define SEQLOCK_WAITERS (1U << 1) +#define SEQLOCK_VAL(n) ((n) & ~SEQLOCK_WAITERS) +#define SEQLOCK_STARTVAL 1U +#define SEQLOCK_INCR 4U + +/* TODO: originally, this was using "atomic_fetch_add", which is the reason + * bit 0 is used to indicate held state. With SEQLOCK_WAITERS added, there's + * no fetch_add anymore (cmpxchg loop instead), so we don't need to use bit 0 + * for this anymore & can just special-case the value 0 for it and skip it in + * counting. + */ struct seqlock { /* always used */ @@ -74,8 +94,16 @@ struct seqlock { extern void seqlock_init(struct seqlock *sqlo); -/* while (sqlo <= val) - wait until seqlock->pos > val, or seqlock unheld */ +/* basically: "while (sqlo <= val) wait();" + * returns when sqlo > val || !seqlock_held(sqlo) + */ extern void seqlock_wait(struct seqlock *sqlo, seqlock_val_t val); + +/* same, but time-limited (limit is an absolute CLOCK_MONOTONIC value) */ +extern bool seqlock_timedwait(struct seqlock *sqlo, seqlock_val_t val, + const struct timespec *abs_monotime_limit); + +/* one-shot test, returns true if seqlock_wait would return immediately */ extern bool seqlock_check(struct seqlock *sqlo, seqlock_val_t val); static inline bool seqlock_held(struct seqlock *sqlo) @@ -85,12 +113,20 @@ static inline bool seqlock_held(struct seqlock *sqlo) /* sqlo - get seqlock position -- for the "counter" seqlock */ extern seqlock_val_t seqlock_cur(struct seqlock *sqlo); -/* sqlo++ - note: like x++, returns previous value, before bumping */ + +/* ++sqlo (but atomic & wakes waiters) - returns value that we bumped to. + * + * guarantees: + * - each seqlock_bump call bumps the position by exactly one SEQLOCK_INCR. + * There are no skipped/missed or multiple increments. + * - each return value is only returned from one seqlock_bump() call + */ extern seqlock_val_t seqlock_bump(struct seqlock *sqlo); /* sqlo = val - can be used on held seqlock. */ extern void seqlock_acquire_val(struct seqlock *sqlo, seqlock_val_t val); + /* sqlo = ref - standard pattern: acquire relative to other seqlock */ static inline void seqlock_acquire(struct seqlock *sqlo, struct seqlock *ref) { @@ -103,4 +139,8 @@ extern void seqlock_release(struct seqlock *sqlo); * anything other than reading RCU items was done */ +#ifdef __cplusplus +} +#endif + #endif /* _SEQLOCK_H */ diff --git a/lib/sigevent.c b/lib/sigevent.c index f00ff4921e..135f0187b4 100644 --- a/lib/sigevent.c +++ b/lib/sigevent.c @@ -24,7 +24,6 @@ #include #include -#ifdef SA_SIGINFO #ifdef HAVE_UCONTEXT_H #ifdef GNU_LINUX /* get REG_EIP from ucontext.h */ @@ -34,7 +33,6 @@ #endif /* GNU_LINUX */ #include #endif /* HAVE_UCONTEXT_H */ -#endif /* SA_SIGINFO */ /* master signals descriptor struct */ @@ -65,6 +63,33 @@ static void quagga_signal_handler(int signo) sigmaster.caught = 1; } +/* + * Check whether any signals have been received and are pending. This is done + * with the application's key signals blocked. The complete set of signals + * is returned in 'setp', so the caller can restore them when appropriate. + * If there are pending signals, returns 'true', 'false' otherwise. + */ +bool frr_sigevent_check(sigset_t *setp) +{ + sigset_t blocked; + int i; + bool ret; + + sigemptyset(setp); + sigemptyset(&blocked); + + /* Set up mask of application's signals */ + for (i = 0; i < sigmaster.sigc; i++) + sigaddset(&blocked, sigmaster.signals[i].signal); + + pthread_sigmask(SIG_BLOCK, &blocked, setp); + + /* Now that the application's signals are blocked, test. */ + ret = (sigmaster.caught != 0); + + return ret; +} + /* check if signals have been caught and run appropriate handlers */ int quagga_sigevent_process(void) { @@ -158,8 +183,6 @@ static int signal_set(int signo) return 0; } -#ifdef SA_SIGINFO - /* XXX This function should be enhanced to support more platforms (it currently works only on Linux/x86). */ static void *program_counter(void *context) @@ -199,33 +222,20 @@ static void *program_counter(void *context) return NULL; } -#endif /* SA_SIGINFO */ - static void __attribute__((noreturn)) -exit_handler(int signo -#ifdef SA_SIGINFO - , - siginfo_t *siginfo, void *context -#endif - ) +exit_handler(int signo, siginfo_t *siginfo, void *context) { - zlog_signal(signo, "exiting..." -#ifdef SA_SIGINFO - , - siginfo, program_counter(context) -#endif - ); + void *pc = program_counter(context); + + zlog_signal(signo, "exiting...", siginfo, pc); _exit(128 + signo); } static void __attribute__((noreturn)) -core_handler(int signo -#ifdef SA_SIGINFO - , - siginfo_t *siginfo, void *context -#endif - ) +core_handler(int signo, siginfo_t *siginfo, void *context) { + void *pc = program_counter(context); + /* make sure we don't hang in here. default for SIGALRM is terminate. * - if we're in backtrace for more than a second, abort. */ struct sigaction sa_default = {.sa_handler = SIG_DFL}; @@ -238,14 +248,12 @@ core_handler(int signo alarm(1); - zlog_signal(signo, "aborting..." -#ifdef SA_SIGINFO - , - siginfo, program_counter(context) -#endif - ); + zlog_signal(signo, "aborting...", siginfo, pc); + /* dump memory stats on core */ log_memstats(stderr, "core_handler"); + + zlog_tls_buffer_fini(); abort(); } @@ -285,12 +293,7 @@ static void trap_default_signals(void) static const struct { const int *sigs; unsigned int nsigs; - void (*handler)(int signo -#ifdef SA_SIGINFO - , - siginfo_t *info, void *context -#endif - ); + void (*handler)(int signo, siginfo_t *info, void *context); } sigmap[] = { {core_signals, array_size(core_signals), core_handler}, {exit_signals, array_size(exit_signals), exit_handler}, @@ -311,15 +314,10 @@ static void trap_default_signals(void) act.sa_handler = SIG_IGN; act.sa_flags = 0; } else { -#ifdef SA_SIGINFO /* Request extra arguments to signal * handler. */ act.sa_sigaction = sigmap[i].handler; act.sa_flags = SA_SIGINFO; -#else - act.sa_handler = sigmap[i].handler; - act.sa_flags = 0; -#endif #ifdef SA_RESETHAND /* don't try to print backtraces * recursively */ diff --git a/lib/sigevent.h b/lib/sigevent.h index a0ad88fcaa..4a39b22889 100644 --- a/lib/sigevent.h +++ b/lib/sigevent.h @@ -48,6 +48,15 @@ struct quagga_signal_t { extern void signal_init(struct thread_master *m, int sigc, struct quagga_signal_t *signals); + +/* + * Check whether any signals have been received and are pending. This is done + * with the application's key signals blocked. The complete set of signals + * is returned in 'setp', so the caller can restore them when appropriate. + * If there are pending signals, returns 'true', 'false' otherwise. + */ +bool frr_sigevent_check(sigset_t *setp); + /* check whether there are signals to handle, process any found */ extern int quagga_sigevent_process(void); diff --git a/lib/skiplist.c b/lib/skiplist.c index dda442580a..b79dfa6772 100644 --- a/lib/skiplist.c +++ b/lib/skiplist.c @@ -61,6 +61,7 @@ #include "vty.h" #include "skiplist.h" #include "lib_errors.h" +#include "network.h" DEFINE_MTYPE_STATIC(LIB, SKIP_LIST, "Skip List") DEFINE_MTYPE_STATIC(LIB, SKIP_LIST_NODE, "Skip Node") @@ -73,7 +74,6 @@ DEFINE_MTYPE_STATIC(LIB, SKIP_LIST_NODE, "Skip Node") static int randomsLeft; static int randomBits; -static struct skiplist *skiplist_last_created; /* debugging hack */ #if 1 #define CHECKLAST(sl) \ @@ -95,7 +95,7 @@ static int randomLevel(void) do { if (randomsLeft <= 0) { - randomBits = random(); + randomBits = frr_weak_random(); randomsLeft = BitsInRandom / 2; } b = randomBits & 3; @@ -112,7 +112,7 @@ static int randomLevel(void) return level; } -static int default_cmp(void *key1, void *key2) +static int default_cmp(const void *key1, const void *key2) { if (key1 < key2) return -1; @@ -126,7 +126,8 @@ unsigned int skiplist_count(struct skiplist *l) return l->count; } -struct skiplist *skiplist_new(int flags, int (*cmp)(void *key1, void *key2), +struct skiplist *skiplist_new(int flags, + int (*cmp)(const void *key1, const void *key2), void (*del)(void *val)) { struct skiplist *new; @@ -148,8 +149,6 @@ struct skiplist *skiplist_new(int flags, int (*cmp)(void *key1, void *key2), if (del) new->del = del; - skiplist_last_created = new; /* debug */ - return new; } @@ -211,12 +210,12 @@ int skiplist_insert(register struct skiplist *l, register void *key, q = newNodeOfLevel(k); q->key = key; q->value = value; -#if SKIPLIST_0TIMER_DEBUG +#ifdef SKIPLIST_0TIMER_DEBUG q->flags = SKIPLIST_NODE_FLAG_INSERTED; /* debug */ #endif ++(l->stats->forward[k]); -#if SKIPLIST_DEBUG +#ifdef SKIPLIST_DEBUG zlog_debug("%s: incremented stats @%p:%d, now %ld", __func__, l, k, l->stats->forward[k] - (struct skiplistnode *)NULL); #endif @@ -281,7 +280,7 @@ int skiplist_delete(register struct skiplist *l, register void *key, /* * found node to delete */ -#if SKIPLIST_0TIMER_DEBUG +#ifdef SKIPLIST_0TIMER_DEBUG q->flags &= ~SKIPLIST_NODE_FLAG_INSERTED; #endif /* @@ -300,7 +299,7 @@ int skiplist_delete(register struct skiplist *l, register void *key, p->forward[k] = q->forward[k]; } --(l->stats->forward[k - 1]); -#if SKIPLIST_DEBUG +#ifdef SKIPLIST_DEBUG zlog_debug("%s: decremented stats @%p:%d, now %ld", __func__, l, k - 1, l->stats->forward[k - 1] @@ -329,8 +328,8 @@ int skiplist_delete(register struct skiplist *l, register void *key, * Also set a cursor for use with skiplist_next_value. */ int skiplist_first_value(register struct skiplist *l, /* in */ - register void *key, /* in */ - void **valuePointer, /* out */ + register const void *key, /* in */ + void **valuePointer, /* out */ void **cursor) /* out */ { register int k; @@ -374,11 +373,11 @@ int skiplist_search(register struct skiplist *l, register void *key, * last element with the given key, -1 is returned. */ int skiplist_next_value(register struct skiplist *l, /* in */ - register void *key, /* in */ + register const void *key, /* in */ void **valuePointer, /* in/out */ void **cursor) /* in/out */ { - register int k, m; + register int k; register struct skiplistnode *p, *q; CHECKLAST(l); @@ -389,7 +388,7 @@ int skiplist_next_value(register struct skiplist *l, /* in */ if (!cursor || !*cursor) { p = l->header; - k = m = l->level; + k = l->level; /* * Find matching key @@ -549,7 +548,7 @@ int skiplist_delete_first(register struct skiplist *l) } } -#if SKIPLIST_0TIMER_DEBUG +#ifdef SKIPLIST_0TIMER_DEBUG q->flags &= ~SKIPLIST_NODE_FLAG_INSERTED; #endif /* @@ -561,7 +560,7 @@ int skiplist_delete_first(register struct skiplist *l) } --(l->stats->forward[nodelevel]); -#if SKIPLIST_DEBUG +#ifdef SKIPLIST_DEBUG zlog_debug("%s: decremented stats @%p:%d, now %ld", __func__, l, nodelevel, l->stats->forward[nodelevel] - (struct skiplistnode *)NULL); @@ -584,7 +583,8 @@ void skiplist_debug(struct vty *vty, struct skiplist *l) int i; if (!l) - l = skiplist_last_created; + return; + vty_out(vty, "Skiplist %p has max level %d\n", l, l->level); for (i = l->level; i >= 0; --i) vty_out(vty, " @%d: %ld\n", i, @@ -608,7 +608,7 @@ void skiplist_test(struct vty *vty) struct skiplist *l; register int i, k; void *keys[sampleSize]; - void *v; + void *v = NULL; zlog_debug("%s: entry", __func__); @@ -623,7 +623,7 @@ void skiplist_test(struct vty *vty) zlog_debug("%s: (%d:%d)", __func__, i, k); } // keys[k] = (void *)random(); - keys[k] = (void *)scramble(k); + keys[k] = scramble(k); if (skiplist_insert(l, keys[k], keys[k])) zlog_debug("error in insert #%d,#%d", i, k); } @@ -648,7 +648,7 @@ void skiplist_test(struct vty *vty) zlog_debug("<%d:%d>", i, k); if (skiplist_delete(l, keys[k], keys[k])) zlog_debug("error in delete"); - keys[k] = (void *)scramble(k ^ 0xf0f0f0f0); + keys[k] = scramble(k ^ 0xf0f0f0f0); if (skiplist_insert(l, keys[k], keys[k])) zlog_debug("error in insert #%d,#%d", i, k); } diff --git a/lib/skiplist.h b/lib/skiplist.h index 2ab37331c9..a106a455d6 100644 --- a/lib/skiplist.h +++ b/lib/skiplist.h @@ -68,7 +68,7 @@ struct skiplist { * Returns -1 if val1 < val2, 0 if equal?, 1 if val1 > val2. * Used as definition of sorted for listnode_add_sort */ - int (*cmp)(void *val1, void *val2); + int (*cmp)(const void *val1, const void *val2); /* callback to free user-owned data when listnode is deleted. supplying * this callback is very much encouraged! @@ -81,8 +81,9 @@ struct skiplist { extern struct skiplist * skiplist_new(/* encouraged: set list.del callback on new lists */ int flags, - int (*cmp)(void *key1, void *key2), /* NULL => default cmp */ - void (*del)(void *val)); /* NULL => no auto val free */ + int (*cmp)(const void *key1, + const void *key2), /* NULL => default cmp */ + void (*del)(void *val)); /* NULL => no auto val free */ extern void skiplist_free(struct skiplist *); @@ -96,12 +97,12 @@ extern int skiplist_search(register struct skiplist *l, register void *key, void **valuePointer); extern int skiplist_first_value(register struct skiplist *l, /* in */ - register void *key, /* in */ - void **valuePointer, /* in/out */ + register const void *key, /* in */ + void **valuePointer, /* in/out */ void **cursor); /* out */ extern int skiplist_next_value(register struct skiplist *l, /* in */ - register void *key, /* in */ + register const void *key, /* in */ void **valuePointer, /* in/out */ void **cursor); /* in/out */ diff --git a/lib/smux.h b/lib/smux.h index 3f860db0dc..6896f02354 100644 --- a/lib/smux.h +++ b/lib/smux.h @@ -105,7 +105,7 @@ extern int smux_trap(struct variable *, size_t, const oid *, size_t, extern int oid_compare(const oid *, int, const oid *, int); extern void oid2in_addr(oid[], int, struct in_addr *); extern void *oid_copy(void *, const void *, size_t); -extern void oid_copy_addr(oid[], struct in_addr *, int); +extern void oid_copy_addr(oid[], const struct in_addr *, int); #ifdef __cplusplus } diff --git a/lib/snmp.c b/lib/snmp.c index f11d9dc8cf..736a3c62b8 100644 --- a/lib/snmp.c +++ b/lib/snmp.c @@ -64,10 +64,10 @@ void oid2in_addr(oid oid[], int len, struct in_addr *addr) *pnt++ = oid[i]; } -void oid_copy_addr(oid oid[], struct in_addr *addr, int len) +void oid_copy_addr(oid oid[], const struct in_addr *addr, int len) { int i; - uint8_t *pnt; + const uint8_t *pnt; if (len == 0) return; diff --git a/lib/sockopt.c b/lib/sockopt.c index 89f3d5b594..21fddcd01d 100644 --- a/lib/sockopt.c +++ b/lib/sockopt.c @@ -72,15 +72,29 @@ int getsockopt_so_sendbuf(const int sock) return optval; } +int getsockopt_so_recvbuf(const int sock) +{ + uint32_t optval; + socklen_t optlen = sizeof(optval); + int ret = getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char *)&optval, + &optlen); + if (ret < 0) { + flog_err_sys(EC_LIB_SYSTEM_CALL, + "fd %d: can't getsockopt SO_RCVBUF: %d (%s)", sock, + errno, safe_strerror(errno)); + return ret; + } + return optval; +} + static void *getsockopt_cmsg_data(struct msghdr *msgh, int level, int type) { struct cmsghdr *cmsg; - void *ptr = NULL; for (cmsg = CMSG_FIRSTHDR(msgh); cmsg != NULL; cmsg = CMSG_NXTHDR(msgh, cmsg)) if (cmsg->cmsg_level == level && cmsg->cmsg_type == type) - return (ptr = CMSG_DATA(cmsg)); + return CMSG_DATA(cmsg); return NULL; } @@ -106,21 +120,6 @@ int setsockopt_ipv6_pktinfo(int sock, int val) return ret; } -/* Set multicast hops val to the socket. */ -int setsockopt_ipv6_checksum(int sock, int val) -{ - int ret; - -#ifdef GNU_LINUX - ret = setsockopt(sock, IPPROTO_RAW, IPV6_CHECKSUM, &val, sizeof(val)); -#else - ret = setsockopt(sock, IPPROTO_IPV6, IPV6_CHECKSUM, &val, sizeof(val)); -#endif /* GNU_LINUX */ - if (ret < 0) - flog_err(EC_LIB_SOCKET, "can't setsockopt IPV6_CHECKSUM"); - return ret; -} - /* Set multicast hops val to the socket. */ int setsockopt_ipv6_multicast_hops(int sock, int val) { @@ -265,8 +264,7 @@ int setsockopt_ipv4_multicast(int sock, int optname, struct in_addr if_addr, * up */ char buf[1][INET_ADDRSTRLEN]; zlog_info( - "setsockopt_ipv4_multicast attempting to drop and " - "re-add (fd %d, mcast %s, ifindex %u)", + "setsockopt_ipv4_multicast attempting to drop and re-add (fd %d, mcast %s, ifindex %u)", sock, inet_ntop(AF_INET, &mreqn.imr_multiaddr, buf[0], sizeof(buf[0])), ifindex); @@ -307,8 +305,7 @@ int setsockopt_ipv4_multicast(int sock, int optname, struct in_addr if_addr, * up */ char buf[1][INET_ADDRSTRLEN]; zlog_info( - "setsockopt_ipv4_multicast attempting to drop and " - "re-add (fd %d, mcast %s, ifindex %u)", + "setsockopt_ipv4_multicast attempting to drop and re-add (fd %d, mcast %s, ifindex %u)", sock, inet_ntop(AF_INET, &mreq.imr_multiaddr, buf[0], sizeof(buf[0])), ifindex); @@ -537,10 +534,8 @@ ifindex_t getsockopt_ifindex(int af, struct msghdr *msgh) switch (af) { case AF_INET: return (getsockopt_ipv4_ifindex(msgh)); - break; case AF_INET6: return (getsockopt_ipv6_ifindex(msgh)); - break; default: flog_err(EC_LIB_DEVELOPMENT, "getsockopt_ifindex: unknown address family %d", af); @@ -686,7 +681,7 @@ int sockopt_tcp_signature_ext(int sock, union sockunion *su, uint16_t prefixlen, #endif /* GNU_LINUX */ if ((ret = setsockopt(sock, IPPROTO_TCP, optname, &md5sig, - sizeof md5sig)) + sizeof(md5sig))) < 0) { /* ENOENT is harmless. It is returned when we clear a password for which @@ -701,6 +696,12 @@ int sockopt_tcp_signature_ext(int sock, union sockunion *su, uint16_t prefixlen, } return ret; #endif /* HAVE_TCP_MD5SIG */ + + /* + * Making compiler happy. If we get to this point we probably + * have done something really really wrong. + */ + return -2; } int sockopt_tcp_signature(int sock, union sockunion *su, const char *password) diff --git a/lib/sockopt.h b/lib/sockopt.h index 732fec92aa..4081e6a45b 100644 --- a/lib/sockopt.h +++ b/lib/sockopt.h @@ -30,16 +30,16 @@ extern "C" { extern void setsockopt_so_recvbuf(int sock, int size); extern void setsockopt_so_sendbuf(const int sock, int size); extern int getsockopt_so_sendbuf(const int sock); +extern int getsockopt_so_recvbuf(const int sock); extern int setsockopt_ipv6_pktinfo(int, int); -extern int setsockopt_ipv6_checksum(int, int); extern int setsockopt_ipv6_multicast_hops(int, int); extern int setsockopt_ipv6_unicast_hops(int, int); extern int setsockopt_ipv6_hoplimit(int, int); extern int setsockopt_ipv6_multicast_loop(int, int); extern int setsockopt_ipv6_tclass(int, int); -#define SOPT_SIZE_CMSG_PKTINFO_IPV6() (sizeof (struct in6_pktinfo)); +#define SOPT_SIZE_CMSG_PKTINFO_IPV6() (sizeof(struct in6_pktinfo)); /* * Size defines for control messages used to get ifindex. We define @@ -49,7 +49,7 @@ extern int setsockopt_ipv6_tclass(int, int); */ #if defined(IP_PKTINFO) /* Linux in_pktinfo. */ -#define SOPT_SIZE_CMSG_PKTINFO_IPV4() (CMSG_SPACE(sizeof (struct in_pktinfo))) +#define SOPT_SIZE_CMSG_PKTINFO_IPV4() (CMSG_SPACE(sizeof(struct in_pktinfo))) /* XXX This should perhaps be defined even if IP_PKTINFO is not. */ #define SOPT_SIZE_CMSG_PKTINFO(af) \ ((af == AF_INET) ? SOPT_SIZE_CMSG_PKTINFO_IPV4() \ @@ -60,9 +60,9 @@ extern int setsockopt_ipv6_tclass(int, int); /* BSD/Solaris */ #if defined(SUNOS_5) -#define SOPT_SIZE_CMSG_RECVIF_IPV4() (sizeof (uint_t)) +#define SOPT_SIZE_CMSG_RECVIF_IPV4() (sizeof(uint_t)) #else -#define SOPT_SIZE_CMSG_RECVIF_IPV4() (sizeof (struct sockaddr_dl)) +#define SOPT_SIZE_CMSG_RECVIF_IPV4() (sizeof(struct sockaddr_dl)) #endif /* SUNOS_5 */ #endif /* IP_RECVIF */ @@ -72,7 +72,7 @@ extern int setsockopt_ipv6_tclass(int, int); #elif defined(SOPT_SIZE_CMSG_RECVIF_IPV4) #define SOPT_SIZE_CMSG_IFINDEX_IPV4() SOPT_SIZE_CMSG_RECVIF_IPV4() #else /* Nothing available */ -#define SOPT_SIZE_CMSG_IFINDEX_IPV4() (sizeof (char *)) +#define SOPT_SIZE_CMSG_IFINDEX_IPV4() (sizeof(char *)) #endif /* SOPT_SIZE_CMSG_IFINDEX_IPV4 */ #define SOPT_SIZE_CMSG_IFINDEX(af) \ diff --git a/lib/sockunion.c b/lib/sockunion.c index bb5426d74a..d77229797c 100644 --- a/lib/sockunion.c +++ b/lib/sockunion.c @@ -27,6 +27,7 @@ #include "log.h" #include "jhash.h" #include "lib_errors.h" +#include "printfrr.h" DEFINE_MTYPE_STATIC(LIB, SOCKUNION, "Socket union") @@ -124,7 +125,6 @@ static const char *sockunion_log(const union sockunion *su, char *buf, case AF_INET6: return inet_ntop(AF_INET6, &(su->sin6.sin6_addr), buf, len); - break; default: snprintf(buf, len, "af_unknown %d ", su->sa.sa_family); @@ -163,7 +163,7 @@ int sockunion_accept(int sock, union sockunion *su) } /* Return sizeof union sockunion. */ -static int sockunion_sizeof(const union sockunion *su) +int sockunion_sizeof(const union sockunion *su) { int ret; @@ -215,7 +215,7 @@ enum connect_result sockunion_connect(int fd, const union sockunion *peersu, if (errno != EINPROGRESS) { char str[SU_ADDRSTRLEN]; zlog_info("can't connect to %s fd %d : %s", - sockunion_log(&su, str, sizeof str), fd, + sockunion_log(&su, str, sizeof(str)), fd, safe_strerror(errno)); return connect_error; } @@ -366,21 +366,6 @@ int sockopt_cork(int sock, int onoff) return 0; } -int sockopt_mark_default(int sock, int mark, struct zebra_privs_t *cap) -{ -#ifdef SO_MARK - int ret; - - frr_elevate_privs(cap) { - ret = setsockopt(sock, SOL_SOCKET, SO_MARK, &mark, - sizeof(mark)); - } - return ret; -#else - return 0; -#endif -} - int sockopt_minttl(int family, int sock, int minttl) { #ifdef IP_MINTTL @@ -422,8 +407,7 @@ int sockopt_v6only(int family, int sock) sizeof(int)); if (ret < 0) { flog_err(EC_LIB_SOCKET, - "can't set sockopt IPV6_V6ONLY " - "to socket %d", + "can't set sockopt IPV6_V6ONLY to socket %d", sock); return -1; } @@ -534,8 +518,8 @@ union sockunion *sockunion_getsockname(int fd) } name; union sockunion *su; - memset(&name, 0, sizeof name); - len = sizeof name; + memset(&name, 0, sizeof(name)); + len = sizeof(name); ret = getsockname(fd, (struct sockaddr *)&name, &len); if (ret < 0) { @@ -572,8 +556,8 @@ union sockunion *sockunion_getpeername(int fd) } name; union sockunion *su; - memset(&name, 0, sizeof name); - len = sizeof name; + memset(&name, 0, sizeof(name)); + len = sizeof(name); ret = getpeername(fd, (struct sockaddr *)&name, &len); if (ret < 0) { flog_err(EC_LIB_SOCKET, "Can't get remote address and port: %s", @@ -682,3 +666,49 @@ void sockunion_init(union sockunion *su) { memset(su, 0, sizeof(union sockunion)); } + +printfrr_ext_autoreg_p("SU", printfrr_psu) +static ssize_t printfrr_psu(char *buf, size_t bsz, const char *fmt, + int prec, const void *ptr) +{ + const union sockunion *su = ptr; + struct fbuf fb = { .buf = buf, .pos = buf, .len = bsz - 1 }; + bool include_port = false; + bool endflags = false; + ssize_t consumed = 2; + + while (!endflags) { + switch (fmt[consumed++]) { + case 'p': + include_port = true; + break; + default: + consumed--; + endflags = true; + break; + } + }; + + switch (sockunion_family(su)) { + case AF_UNSPEC: + bprintfrr(&fb, "(unspec)"); + break; + case AF_INET: + inet_ntop(AF_INET, &su->sin.sin_addr, buf, bsz); + fb.pos += strlen(fb.buf); + if (include_port) + bprintfrr(&fb, ":%d", su->sin.sin_port); + break; + case AF_INET6: + inet_ntop(AF_INET6, &su->sin6.sin6_addr, buf, bsz); + fb.pos += strlen(fb.buf); + if (include_port) + bprintfrr(&fb, ":%d", su->sin6.sin6_port); + break; + default: + bprintfrr(&fb, "(af %d)", sockunion_family(su)); + } + + fb.pos[0] = '\0'; + return consumed; +} diff --git a/lib/sockunion.h b/lib/sockunion.h index d7d26ba85a..72f12b77ca 100644 --- a/lib/sockunion.h +++ b/lib/sockunion.h @@ -83,6 +83,7 @@ extern void sockunion_set(union sockunion *, int family, const uint8_t *addr, extern union sockunion *sockunion_str2su(const char *str); extern int sockunion_accept(int sock, union sockunion *); +extern int sockunion_sizeof(const union sockunion *su); extern int sockunion_stream_socket(union sockunion *); extern int sockopt_reuseaddr(int); extern int sockopt_reuseport(int); @@ -92,7 +93,6 @@ extern int sockunion_bind(int sock, union sockunion *, unsigned short, extern int sockopt_ttl(int family, int sock, int ttl); extern int sockopt_minttl(int family, int sock, int minttl); extern int sockopt_cork(int sock, int onoff); -extern int sockopt_mark_default(int sock, int mark, struct zebra_privs_t *); extern int sockunion_socket(const union sockunion *su); extern const char *inet_sutop(const union sockunion *su, char *str); extern enum connect_result sockunion_connect(int fd, const union sockunion *su, @@ -103,6 +103,10 @@ extern union sockunion *sockunion_dup(const union sockunion *); extern void sockunion_free(union sockunion *); extern void sockunion_init(union sockunion *); +#ifdef _FRR_ATTRIBUTE_PRINTFRR +#pragma FRR printfrr_ext "%pSU" (union sockunion *) +#endif + #ifdef __cplusplus } #endif diff --git a/lib/spf_backoff.c b/lib/spf_backoff.c index 41d4e2bb57..4e74714489 100644 --- a/lib/spf_backoff.c +++ b/lib/spf_backoff.c @@ -7,7 +7,7 @@ * Copyright (C) 2017 Orange Labs http://www.orange.com/ * Copyright (C) 2017 by Christian Franke, Open Source Routing / NetDEF Inc. * - * This file is part of FreeRangeRouting (FRR) + * This file is part of FRRouting (FRR) * * FRR is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the diff --git a/lib/spf_backoff.h b/lib/spf_backoff.h index 11b2701e3e..2617195d79 100644 --- a/lib/spf_backoff.h +++ b/lib/spf_backoff.h @@ -7,7 +7,7 @@ * Copyright (C) 2017 Orange Labs http://www.orange.com/ * Copyright (C) 2017 by Christian Franke, Open Source Routing / NetDEF Inc. * - * This file is part of FreeRangeRouting (FRR) + * This file is part of FRRouting (FRR) * * FRR is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the diff --git a/lib/srcdest_table.c b/lib/srcdest_table.c index 80004b41ac..8ffa0e9709 100644 --- a/lib/srcdest_table.c +++ b/lib/srcdest_table.c @@ -4,7 +4,7 @@ * Copyright (C) 2017 by David Lamparter & Christian Franke, * Open Source Routing / NetDEF Inc. * - * This file is part of FreeRangeRouting (FRR) + * This file is part of FRRouting (FRR) * * FRR is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -28,6 +28,7 @@ #include "memory.h" #include "prefix.h" #include "table.h" +#include "printfrr.h" DEFINE_MTYPE_STATIC(LIB, ROUTE_SRC_NODE, "Route source node") @@ -264,7 +265,8 @@ struct route_node *srcdest_rnode_lookup(struct route_table *table, return srn; } -void srcdest_rnode_prefixes(struct route_node *rn, const struct prefix **p, +void srcdest_rnode_prefixes(const struct route_node *rn, + const struct prefix **p, const struct prefix **src_p) { if (rnode_is_srcnode(rn)) { @@ -296,10 +298,32 @@ const char *srcdest2str(const struct prefix *dst_p, return str; } -const char *srcdest_rnode2str(struct route_node *rn, char *str, int size) +const char *srcdest_rnode2str(const struct route_node *rn, char *str, int size) { const struct prefix *dst_p, *src_p; srcdest_rnode_prefixes(rn, &dst_p, &src_p); return srcdest2str(dst_p, (const struct prefix_ipv6 *)src_p, str, size); } + +printfrr_ext_autoreg_p("RN", printfrr_rn) +static ssize_t printfrr_rn(char *buf, size_t bsz, const char *fmt, + int prec, const void *ptr) +{ + const struct route_node *rn = ptr; + const struct prefix *dst_p, *src_p; + + srcdest_rnode_prefixes(rn, &dst_p, &src_p); + srcdest2str(dst_p, (const struct prefix_ipv6 *)src_p, buf, bsz); + return 2; +} + +struct route_table *srcdest_srcnode_table(struct route_node *rn) +{ + if (rnode_is_dstnode(rn)) { + struct srcdest_rnode *srn = srcdest_rnode_from_rnode(rn); + + return srn->src_table; + } + return NULL; +} diff --git a/lib/srcdest_table.h b/lib/srcdest_table.h index 8845931de7..79afef9bb0 100644 --- a/lib/srcdest_table.h +++ b/lib/srcdest_table.h @@ -4,7 +4,7 @@ * Copyright (C) 2017 by David Lamparter & Christian Franke, * Open Source Routing / NetDEF Inc. * - * This file is part of FreeRangeRouting (FRR) + * This file is part of FRRouting (FRR) * * FRR is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -65,22 +65,22 @@ extern struct route_node *srcdest_rnode_get(struct route_table *table, extern struct route_node *srcdest_rnode_lookup(struct route_table *table, union prefixconstptr dst_pu, const struct prefix_ipv6 *src_p); -extern void srcdest_rnode_prefixes(struct route_node *rn, +extern void srcdest_rnode_prefixes(const struct route_node *rn, const struct prefix **p, const struct prefix **src_p); extern const char *srcdest2str(const struct prefix *dst_p, const struct prefix_ipv6 *src_p, char *str, int size); -extern const char *srcdest_rnode2str(struct route_node *rn, char *str, +extern const char *srcdest_rnode2str(const struct route_node *rn, char *str, int size); extern struct route_node *srcdest_route_next(struct route_node *rn); -static inline int rnode_is_dstnode(struct route_node *rn) +static inline int rnode_is_dstnode(const struct route_node *rn) { return rn->table->delegate == &_srcdest_dstnode_delegate; } -static inline int rnode_is_srcnode(struct route_node *rn) +static inline int rnode_is_srcnode(const struct route_node *rn) { return rn->table->delegate == &_srcdest_srcnode_delegate; } @@ -100,6 +100,8 @@ static inline void *srcdest_rnode_table_info(struct route_node *rn) return route_table_get_info(srcdest_rnode_table(rn)); } +extern struct route_table *srcdest_srcnode_table(struct route_node *rn); + #ifdef __cplusplus } #endif diff --git a/lib/pqueue.h b/lib/srte.h similarity index 52% rename from lib/pqueue.h rename to lib/srte.h index 032ee9db4c..d468c1cac9 100644 --- a/lib/pqueue.h +++ b/lib/srte.h @@ -1,5 +1,7 @@ -/* Priority queue functions. - * Copyright (C) 2003 Yasuhiro Ohara +/* + * SR-TE definitions + * Copyright 2020 NetDef Inc. + * Sascha Kattelmann * * This file is part of GNU Zebra. * @@ -18,37 +20,37 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef _ZEBRA_PQUEUE_H -#define _ZEBRA_PQUEUE_H +#ifndef _FRR_SRTE_H +#define _FRR_SRTE_H #ifdef __cplusplus extern "C" { #endif -struct pqueue { - void **array; - int array_size; - int size; +#define SRTE_POLICY_NAME_MAX_LENGTH 64 - int (*cmp)(void *, void *); - void (*update)(void *node, int actual_position); +enum zebra_sr_policy_status { + ZEBRA_SR_POLICY_UP = 0, + ZEBRA_SR_POLICY_DOWN, }; -#define PQUEUE_INIT_ARRAYSIZE 32 +static inline int sr_policy_compare(const struct ipaddr *a_endpoint, + const struct ipaddr *b_endpoint, + uint32_t a_color, uint32_t b_color) +{ + int ret; -extern struct pqueue *pqueue_create(void); -extern void pqueue_delete(struct pqueue *queue); + ret = ipaddr_cmp(a_endpoint, b_endpoint); + if (ret < 0) + return -1; + if (ret > 0) + return 1; -extern void pqueue_enqueue(void *data, struct pqueue *queue); -extern void *pqueue_dequeue(struct pqueue *queue); -extern void pqueue_remove_at(int index, struct pqueue *queue); -extern void pqueue_remove(void *data, struct pqueue *queue); - -extern void trickle_down(int index, struct pqueue *queue); -extern void trickle_up(int index, struct pqueue *queue); + return a_color - b_color; +} #ifdef __cplusplus } #endif -#endif /* _ZEBRA_PQUEUE_H */ +#endif /* _FRR_SRTE_H */ diff --git a/lib/srv6.c b/lib/srv6.c new file mode 100644 index 0000000000..287bf56089 --- /dev/null +++ b/lib/srv6.c @@ -0,0 +1,118 @@ +/* + * SRv6 definitions + * Copyright (C) 2020 Hiroki Shirokura, LINE Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "zebra.h" + +#include "srv6.h" +#include "log.h" + +const char *seg6local_action2str(uint32_t action) +{ + switch (action) { + case ZEBRA_SEG6_LOCAL_ACTION_END: + return "End"; + case ZEBRA_SEG6_LOCAL_ACTION_END_X: + return "End.X"; + case ZEBRA_SEG6_LOCAL_ACTION_END_T: + return "End.T"; + case ZEBRA_SEG6_LOCAL_ACTION_END_DX2: + return "End.DX2"; + case ZEBRA_SEG6_LOCAL_ACTION_END_DX6: + return "End.DX6"; + case ZEBRA_SEG6_LOCAL_ACTION_END_DX4: + return "End.DX4"; + case ZEBRA_SEG6_LOCAL_ACTION_END_DT6: + return "End.DT6"; + case ZEBRA_SEG6_LOCAL_ACTION_END_DT4: + return "End.DT4"; + case ZEBRA_SEG6_LOCAL_ACTION_END_B6: + return "End.B6"; + case ZEBRA_SEG6_LOCAL_ACTION_END_B6_ENCAP: + return "End.B6.Encap"; + case ZEBRA_SEG6_LOCAL_ACTION_END_BM: + return "End.BM"; + case ZEBRA_SEG6_LOCAL_ACTION_END_S: + return "End.S"; + case ZEBRA_SEG6_LOCAL_ACTION_END_AS: + return "End.AS"; + case ZEBRA_SEG6_LOCAL_ACTION_END_AM: + return "End.AM"; + case ZEBRA_SEG6_LOCAL_ACTION_UNSPEC: + return "unspec"; + default: + return "unknown"; + } +} + +int snprintf_seg6_segs(char *str, + size_t size, const struct seg6_segs *segs) +{ + str[0] = '\0'; + for (size_t i = 0; i < segs->num_segs; i++) { + char addr[INET6_ADDRSTRLEN]; + bool not_last = (i + 1) < segs->num_segs; + + inet_ntop(AF_INET6, &segs->segs[i], addr, sizeof(addr)); + strlcat(str, addr, size); + strlcat(str, not_last ? "," : "", size); + } + return strlen(str); +} + +const char *seg6local_context2str(char *str, size_t size, + struct seg6local_context *ctx, uint32_t action) +{ + char b0[128]; + + switch (action) { + + case ZEBRA_SEG6_LOCAL_ACTION_END: + snprintf(str, size, "USP"); + return str; + + case ZEBRA_SEG6_LOCAL_ACTION_END_X: + case ZEBRA_SEG6_LOCAL_ACTION_END_DX6: + inet_ntop(AF_INET6, &ctx->nh6, b0, 128); + snprintf(str, size, "nh6 %s", b0); + return str; + + case ZEBRA_SEG6_LOCAL_ACTION_END_DX4: + inet_ntop(AF_INET, &ctx->nh4, b0, 128); + snprintf(str, size, "nh4 %s", b0); + return str; + + case ZEBRA_SEG6_LOCAL_ACTION_END_T: + case ZEBRA_SEG6_LOCAL_ACTION_END_DT6: + case ZEBRA_SEG6_LOCAL_ACTION_END_DT4: + snprintf(str, size, "table %u", ctx->table); + return str; + + case ZEBRA_SEG6_LOCAL_ACTION_END_DX2: + case ZEBRA_SEG6_LOCAL_ACTION_END_B6: + case ZEBRA_SEG6_LOCAL_ACTION_END_B6_ENCAP: + case ZEBRA_SEG6_LOCAL_ACTION_END_BM: + case ZEBRA_SEG6_LOCAL_ACTION_END_S: + case ZEBRA_SEG6_LOCAL_ACTION_END_AS: + case ZEBRA_SEG6_LOCAL_ACTION_END_AM: + case ZEBRA_SEG6_LOCAL_ACTION_UNSPEC: + default: + snprintf(str, size, "unknown(%s)", __func__); + return str; + } +} diff --git a/lib/srv6.h b/lib/srv6.h new file mode 100644 index 0000000000..24c7ffc3a2 --- /dev/null +++ b/lib/srv6.h @@ -0,0 +1,133 @@ +/* + * SRv6 definitions + * Copyright (C) 2020 Hiroki Shirokura, LINE Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _FRR_SRV6_H +#define _FRR_SRV6_H + +#include +#include +#include + +#define SRV6_MAX_SIDS 16 + +#ifdef __cplusplus +extern "C" { +#endif + +#define sid2str(sid, str, size) \ + inet_ntop(AF_INET6, sid, str, size) + +enum seg6_mode_t { + INLINE, + ENCAP, + L2ENCAP, +}; + +enum seg6local_action_t { + ZEBRA_SEG6_LOCAL_ACTION_UNSPEC = 0, + ZEBRA_SEG6_LOCAL_ACTION_END = 1, + ZEBRA_SEG6_LOCAL_ACTION_END_X = 2, + ZEBRA_SEG6_LOCAL_ACTION_END_T = 3, + ZEBRA_SEG6_LOCAL_ACTION_END_DX2 = 4, + ZEBRA_SEG6_LOCAL_ACTION_END_DX6 = 5, + ZEBRA_SEG6_LOCAL_ACTION_END_DX4 = 6, + ZEBRA_SEG6_LOCAL_ACTION_END_DT6 = 7, + ZEBRA_SEG6_LOCAL_ACTION_END_DT4 = 8, + ZEBRA_SEG6_LOCAL_ACTION_END_B6 = 9, + ZEBRA_SEG6_LOCAL_ACTION_END_B6_ENCAP = 10, + ZEBRA_SEG6_LOCAL_ACTION_END_BM = 11, + ZEBRA_SEG6_LOCAL_ACTION_END_S = 12, + ZEBRA_SEG6_LOCAL_ACTION_END_AS = 13, + ZEBRA_SEG6_LOCAL_ACTION_END_AM = 14, + ZEBRA_SEG6_LOCAL_ACTION_END_BPF = 15, +}; + +struct seg6_segs { + size_t num_segs; + struct in6_addr segs[256]; +}; + +struct seg6local_context { + struct in_addr nh4; + struct in6_addr nh6; + uint32_t table; +}; + +static inline const char *seg6_mode2str(enum seg6_mode_t mode) +{ + switch (mode) { + case INLINE: + return "INLINE"; + case ENCAP: + return "ENCAP"; + case L2ENCAP: + return "L2ENCAP"; + default: + return "unknown"; + } +} + +static inline bool sid_same( + const struct in6_addr *a, + const struct in6_addr *b) +{ + if (!a && !b) + return true; + else if (!(a && b)) + return false; + else + return memcmp(a, b, sizeof(struct in6_addr)) == 0; +} + +static inline bool sid_diff( + const struct in6_addr *a, + const struct in6_addr *b) +{ + return !sid_same(a, b); +} + +static inline bool sid_zero( + const struct in6_addr *a) +{ + struct in6_addr zero = {}; + + return sid_same(a, &zero); +} + +static inline void *sid_copy(struct in6_addr *dst, + const struct in6_addr *src) +{ + return memcpy(dst, src, sizeof(struct in6_addr)); +} + +const char * +seg6local_action2str(uint32_t action); + +const char * +seg6local_context2str(char *str, size_t size, + struct seg6local_context *ctx, uint32_t action); + +int snprintf_seg6_segs(char *str, + size_t size, const struct seg6_segs *segs); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/stream.c b/lib/stream.c index 6c187bd359..dc207c16a4 100644 --- a/lib/stream.c +++ b/lib/stream.c @@ -28,6 +28,7 @@ #include "network.h" #include "prefix.h" #include "log.h" +#include "frr_pthread.h" #include "lib_errors.h" DEFINE_MTYPE_STATIC(LIB, STREAM, "Stream") @@ -54,15 +55,19 @@ DEFINE_MTYPE_STATIC(LIB, STREAM_FIFO, "Stream FIFO") * using stream_put..._at() functions. */ #define STREAM_WARN_OFFSETS(S) \ - flog_warn(EC_LIB_STREAM, \ - "&(struct stream): %p, size: %lu, getp: %lu, endp: %lu\n", \ - (void *)(S), (unsigned long)(S)->size, \ - (unsigned long)(S)->getp, (unsigned long)(S)->endp) + do { \ + flog_warn(EC_LIB_STREAM, \ + "&(struct stream): %p, size: %lu, getp: %lu, endp: %lu\n", \ + (void *)(S), (unsigned long)(S)->size, \ + (unsigned long)(S)->getp, (unsigned long)(S)->endp); \ + zlog_backtrace(LOG_WARNING); \ + } while (0) #define STREAM_VERIFY_SANE(S) \ do { \ - if (!(GETP_VALID(S, (S)->getp) && ENDP_VALID(S, (S)->endp))) \ + if (!(GETP_VALID(S, (S)->getp) && ENDP_VALID(S, (S)->endp))) { \ STREAM_WARN_OFFSETS(S); \ + } \ assert(GETP_VALID(S, (S)->getp)); \ assert(ENDP_VALID(S, (S)->endp)); \ } while (0) @@ -119,34 +124,34 @@ void stream_free(struct stream *s) XFREE(MTYPE_STREAM, s); } -struct stream *stream_copy(struct stream *new, struct stream *src) +struct stream *stream_copy(struct stream *dest, const struct stream *src) { STREAM_VERIFY_SANE(src); - assert(new != NULL); - assert(STREAM_SIZE(new) >= src->endp); + assert(dest != NULL); + assert(STREAM_SIZE(dest) >= src->endp); - new->endp = src->endp; - new->getp = src->getp; + dest->endp = src->endp; + dest->getp = src->getp; - memcpy(new->data, src->data, src->endp); + memcpy(dest->data, src->data, src->endp); - return new; + return dest; } -struct stream *stream_dup(struct stream *s) +struct stream *stream_dup(const struct stream *s) { - struct stream *new; + struct stream *snew; STREAM_VERIFY_SANE(s); - if ((new = stream_new(s->endp)) == NULL) + if ((snew = stream_new(s->endp)) == NULL) return NULL; - return (stream_copy(new, s)); + return (stream_copy(snew, s)); } -struct stream *stream_dupcat(struct stream *s1, struct stream *s2, +struct stream *stream_dupcat(const struct stream *s1, const struct stream *s2, size_t offset) { struct stream *new; @@ -186,27 +191,19 @@ size_t stream_resize_inplace(struct stream **sptr, size_t newsize) return orig->size; } -size_t __attribute__((deprecated))stream_resize_orig(struct stream *s, - size_t newsize) -{ - assert("stream_resize: Switch code to use stream_resize_inplace" == NULL); - - return stream_resize_inplace(&s, newsize); -} - -size_t stream_get_getp(struct stream *s) +size_t stream_get_getp(const struct stream *s) { STREAM_VERIFY_SANE(s); return s->getp; } -size_t stream_get_endp(struct stream *s) +size_t stream_get_endp(const struct stream *s) { STREAM_VERIFY_SANE(s); return s->endp; } -size_t stream_get_size(struct stream *s) +size_t stream_get_size(const struct stream *s) { STREAM_VERIFY_SANE(s); return s->size; @@ -259,6 +256,42 @@ void stream_forward_getp(struct stream *s, size_t size) s->getp += size; } +bool stream_forward_getp2(struct stream *s, size_t size) +{ + STREAM_VERIFY_SANE(s); + + if (!GETP_VALID(s, s->getp + size)) + return false; + + s->getp += size; + + return true; +} + +void stream_rewind_getp(struct stream *s, size_t size) +{ + STREAM_VERIFY_SANE(s); + + if (size > s->getp || !GETP_VALID(s, s->getp - size)) { + STREAM_BOUND_WARN(s, "rewind getp"); + return; + } + + s->getp -= size; +} + +bool stream_rewind_getp2(struct stream *s, size_t size) +{ + STREAM_VERIFY_SANE(s); + + if (size > s->getp || !GETP_VALID(s, s->getp - size)) + return false; + + s->getp -= size; + + return true; +} + void stream_forward_endp(struct stream *s, size_t size) { STREAM_VERIFY_SANE(s); @@ -271,6 +304,18 @@ void stream_forward_endp(struct stream *s, size_t size) s->endp += size; } +bool stream_forward_endp2(struct stream *s, size_t size) +{ + STREAM_VERIFY_SANE(s); + + if (!ENDP_VALID(s, s->endp + size)) + return false; + + s->endp += size; + + return true; +} + /* Copy from stream to destination. */ bool stream_get2(void *dst, struct stream *s, size_t size) { @@ -550,6 +595,27 @@ uint64_t stream_getq(struct stream *s) return q; } +bool stream_getq2(struct stream *s, uint64_t *q) +{ + STREAM_VERIFY_SANE(s); + + if (STREAM_READABLE(s) < sizeof(uint64_t)) { + STREAM_BOUND_WARN2(s, "get uint64"); + return false; + } + + *q = ((uint64_t)s->data[s->getp++]) << 56; + *q |= ((uint64_t)s->data[s->getp++]) << 48; + *q |= ((uint64_t)s->data[s->getp++]) << 40; + *q |= ((uint64_t)s->data[s->getp++]) << 32; + *q |= ((uint64_t)s->data[s->getp++]) << 24; + *q |= ((uint64_t)s->data[s->getp++]) << 16; + *q |= ((uint64_t)s->data[s->getp++]) << 8; + *q |= ((uint64_t)s->data[s->getp++]); + + return true; +} + /* Get next long word from the stream. */ uint32_t stream_get_ipv4(struct stream *s) { @@ -568,6 +634,43 @@ uint32_t stream_get_ipv4(struct stream *s) return l; } +bool stream_get_ipaddr(struct stream *s, struct ipaddr *ip) +{ + uint16_t ipa_len; + + STREAM_VERIFY_SANE(s); + + /* Get address type. */ + if (STREAM_READABLE(s) < sizeof(uint16_t)) { + STREAM_BOUND_WARN2(s, "get ipaddr"); + return false; + } + ip->ipa_type = stream_getw(s); + + /* Get address value. */ + switch (ip->ipa_type) { + case IPADDR_V4: + ipa_len = IPV4_MAX_BYTELEN; + break; + case IPADDR_V6: + ipa_len = IPV6_MAX_BYTELEN; + break; + default: + flog_err(EC_LIB_DEVELOPMENT, + "%s: unknown ip address-family: %u", __func__, + ip->ipa_type); + return false; + } + if (STREAM_READABLE(s) < ipa_len) { + STREAM_BOUND_WARN2(s, "get ipaddr"); + return false; + } + memcpy(&ip->ip, s->data + s->getp, ipa_len); + s->getp += ipa_len; + + return true; +} + float stream_getf(struct stream *s) { union { @@ -819,7 +922,7 @@ int stream_put_ipv4(struct stream *s, uint32_t l) } /* Put long word to the stream. */ -int stream_put_in_addr(struct stream *s, struct in_addr *addr) +int stream_put_in_addr(struct stream *s, const struct in_addr *addr) { STREAM_VERIFY_SANE(s); @@ -834,8 +937,30 @@ int stream_put_in_addr(struct stream *s, struct in_addr *addr) return sizeof(uint32_t); } +bool stream_put_ipaddr(struct stream *s, struct ipaddr *ip) +{ + stream_putw(s, ip->ipa_type); + + switch (ip->ipa_type) { + case IPADDR_V4: + stream_put_in_addr(s, &ip->ipaddr_v4); + break; + case IPADDR_V6: + stream_write(s, (uint8_t *)&ip->ipaddr_v6, 16); + break; + default: + flog_err(EC_LIB_DEVELOPMENT, + "%s: unknown ip address-family: %u", __func__, + ip->ipa_type); + return false; + } + + return true; +} + /* Put in_addr at location in the stream. */ -int stream_put_in_addr_at(struct stream *s, size_t putp, struct in_addr *addr) +int stream_put_in_addr_at(struct stream *s, size_t putp, + const struct in_addr *addr) { STREAM_VERIFY_SANE(s); @@ -849,7 +974,8 @@ int stream_put_in_addr_at(struct stream *s, size_t putp, struct in_addr *addr) } /* Put in6_addr at location in the stream. */ -int stream_put_in6_addr_at(struct stream *s, size_t putp, struct in6_addr *addr) +int stream_put_in6_addr_at(struct stream *s, size_t putp, + const struct in6_addr *addr) { STREAM_VERIFY_SANE(s); @@ -863,7 +989,7 @@ int stream_put_in6_addr_at(struct stream *s, size_t putp, struct in6_addr *addr) } /* Put prefix by nlri type format. */ -int stream_put_prefix_addpath(struct stream *s, struct prefix *p, +int stream_put_prefix_addpath(struct stream *s, const struct prefix *p, int addpath_encode, uint32_t addpath_tx_id) { size_t psize; @@ -897,27 +1023,37 @@ int stream_put_prefix_addpath(struct stream *s, struct prefix *p, return psize; } -int stream_put_prefix(struct stream *s, struct prefix *p) +int stream_put_prefix(struct stream *s, const struct prefix *p) { return stream_put_prefix_addpath(s, p, 0, 0); } /* Put NLRI with label */ -int stream_put_labeled_prefix(struct stream *s, struct prefix *p, - mpls_label_t *label) +int stream_put_labeled_prefix(struct stream *s, const struct prefix *p, + mpls_label_t *label, int addpath_encode, + uint32_t addpath_tx_id) { size_t psize; + size_t psize_with_addpath; uint8_t *label_pnt = (uint8_t *)label; STREAM_VERIFY_SANE(s); psize = PSIZE(p->prefixlen); + psize_with_addpath = psize + (addpath_encode ? 4 : 0); - if (STREAM_WRITEABLE(s) < (psize + 3)) { + if (STREAM_WRITEABLE(s) < (psize_with_addpath + 3)) { STREAM_BOUND_WARN(s, "put"); return 0; } + if (addpath_encode) { + s->data[s->endp++] = (uint8_t)(addpath_tx_id >> 24); + s->data[s->endp++] = (uint8_t)(addpath_tx_id >> 16); + s->data[s->endp++] = (uint8_t)(addpath_tx_id >> 8); + s->data[s->endp++] = (uint8_t)addpath_tx_id; + } + stream_putc(s, (p->prefixlen + 24)); stream_putc(s, label_pnt[0]); stream_putc(s, label_pnt[1]); @@ -1096,17 +1232,28 @@ int stream_flush(struct stream *s, int fd) return nbytes; } +void stream_hexdump(const struct stream *s) +{ + zlog_hexdump(s->data, s->endp); +} + /* Stream first in first out queue. */ struct stream_fifo *stream_fifo_new(void) { struct stream_fifo *new; - new = XCALLOC(MTYPE_STREAM_FIFO, sizeof(struct stream_fifo)); - pthread_mutex_init(&new->mtx, NULL); + new = XMALLOC(MTYPE_STREAM_FIFO, sizeof(struct stream_fifo)); + stream_fifo_init(new); return new; } +void stream_fifo_init(struct stream_fifo *fifo) +{ + memset(fifo, 0, sizeof(struct stream_fifo)); + pthread_mutex_init(&fifo->mtx, NULL); +} + /* Add new stream to fifo. */ void stream_fifo_push(struct stream_fifo *fifo, struct stream *s) { @@ -1134,11 +1281,9 @@ void stream_fifo_push(struct stream_fifo *fifo, struct stream *s) void stream_fifo_push_safe(struct stream_fifo *fifo, struct stream *s) { - pthread_mutex_lock(&fifo->mtx); - { + frr_with_mutex(&fifo->mtx) { stream_fifo_push(fifo, s); } - pthread_mutex_unlock(&fifo->mtx); } /* Delete first stream from fifo. */ @@ -1168,11 +1313,9 @@ struct stream *stream_fifo_pop_safe(struct stream_fifo *fifo) { struct stream *ret; - pthread_mutex_lock(&fifo->mtx); - { + frr_with_mutex(&fifo->mtx) { ret = stream_fifo_pop(fifo); } - pthread_mutex_unlock(&fifo->mtx); return ret; } @@ -1186,11 +1329,9 @@ struct stream *stream_fifo_head_safe(struct stream_fifo *fifo) { struct stream *ret; - pthread_mutex_lock(&fifo->mtx); - { + frr_with_mutex(&fifo->mtx) { ret = stream_fifo_head(fifo); } - pthread_mutex_unlock(&fifo->mtx); return ret; } @@ -1210,11 +1351,9 @@ void stream_fifo_clean(struct stream_fifo *fifo) void stream_fifo_clean_safe(struct stream_fifo *fifo) { - pthread_mutex_lock(&fifo->mtx); - { + frr_with_mutex(&fifo->mtx) { stream_fifo_clean(fifo); } - pthread_mutex_unlock(&fifo->mtx); } size_t stream_fifo_count_safe(struct stream_fifo *fifo) @@ -1222,9 +1361,14 @@ size_t stream_fifo_count_safe(struct stream_fifo *fifo) return atomic_load_explicit(&fifo->count, memory_order_acquire); } -void stream_fifo_free(struct stream_fifo *fifo) +void stream_fifo_deinit(struct stream_fifo *fifo) { stream_fifo_clean(fifo); pthread_mutex_destroy(&fifo->mtx); +} + +void stream_fifo_free(struct stream_fifo *fifo) +{ + stream_fifo_deinit(fifo); XFREE(MTYPE_STREAM_FIFO, fifo); } diff --git a/lib/stream.h b/lib/stream.h index 5341bfa40b..23f85d809b 100644 --- a/lib/stream.h +++ b/lib/stream.h @@ -110,7 +110,7 @@ struct stream { size_t getp; /* next get position */ size_t endp; /* last valid data position */ size_t size; /* size of data segment */ - unsigned char data[0]; /* data pointer */ + unsigned char data[]; /* data pointer */ }; /* First in first out queue structure. */ @@ -150,33 +150,33 @@ struct stream_fifo { */ extern struct stream *stream_new(size_t); extern void stream_free(struct stream *); -extern struct stream *stream_copy(struct stream *, struct stream *src); -extern struct stream *stream_dup(struct stream *); +/* Copy 'src' into 'dest', returns 'dest' */ +extern struct stream *stream_copy(struct stream *dest, + const struct stream *src); +extern struct stream *stream_dup(const struct stream *s); -#if CONFDATE > 20190821 -CPP_NOTICE("lib: time to remove stream_resize_orig") -#endif -extern size_t stream_resize_orig(struct stream *s, size_t newsize); -#define stream_resize stream_resize_orig extern size_t stream_resize_inplace(struct stream **sptr, size_t newsize); -extern size_t stream_get_getp(struct stream *); -extern size_t stream_get_endp(struct stream *); -extern size_t stream_get_size(struct stream *); -extern uint8_t *stream_get_data(struct stream *); +extern size_t stream_get_getp(const struct stream *s); +extern size_t stream_get_endp(const struct stream *s); +extern size_t stream_get_size(const struct stream *s); /** * Create a new stream structure; copy offset bytes from s1 to the new * stream; copy s2 data to the new stream; copy rest of s1 data to the * new stream. */ -extern struct stream *stream_dupcat(struct stream *s1, struct stream *s2, - size_t offset); +extern struct stream *stream_dupcat(const struct stream *s1, + const struct stream *s2, size_t offset); extern void stream_set_getp(struct stream *, size_t); extern void stream_set_endp(struct stream *, size_t); extern void stream_forward_getp(struct stream *, size_t); +extern bool stream_forward_getp2(struct stream *, size_t); +extern void stream_rewind_getp(struct stream *s, size_t size); +extern bool stream_rewind_getp2(struct stream *s, size_t size); extern void stream_forward_endp(struct stream *, size_t); +extern bool stream_forward_endp2(struct stream *, size_t); /* steam_put: NULL source zeroes out size_t bytes of stream */ extern void stream_put(struct stream *, const void *, size_t); @@ -191,15 +191,20 @@ extern int stream_putl_at(struct stream *, size_t, uint32_t); extern int stream_putq(struct stream *, uint64_t); extern int stream_putq_at(struct stream *, size_t, uint64_t); extern int stream_put_ipv4(struct stream *, uint32_t); -extern int stream_put_in_addr(struct stream *, struct in_addr *); -extern int stream_put_in_addr_at(struct stream *, size_t, struct in_addr *); -extern int stream_put_in6_addr_at(struct stream *, size_t, struct in6_addr *); -extern int stream_put_prefix_addpath(struct stream *, struct prefix *, +extern int stream_put_in_addr(struct stream *s, const struct in_addr *addr); +extern bool stream_put_ipaddr(struct stream *s, struct ipaddr *ip); +extern int stream_put_in_addr_at(struct stream *s, size_t putp, + const struct in_addr *addr); +extern int stream_put_in6_addr_at(struct stream *s, size_t putp, + const struct in6_addr *addr); +extern int stream_put_prefix_addpath(struct stream *s, + const struct prefix *p, int addpath_encode, uint32_t addpath_tx_id); -extern int stream_put_prefix(struct stream *, struct prefix *); -extern int stream_put_labeled_prefix(struct stream *, struct prefix *, - mpls_label_t *); +extern int stream_put_prefix(struct stream *s, const struct prefix *p); +extern int stream_put_labeled_prefix(struct stream *, const struct prefix *, + mpls_label_t *, int addpath_encode, + uint32_t addpath_tx_id); extern void stream_get(void *, struct stream *, size_t); extern bool stream_get2(void *data, struct stream *s, size_t size); extern void stream_get_from(void *, struct stream *, size_t, size_t); @@ -216,7 +221,9 @@ extern bool stream_getl2(struct stream *s, uint32_t *l); extern uint32_t stream_getl_from(struct stream *, size_t); extern uint64_t stream_getq(struct stream *); extern uint64_t stream_getq_from(struct stream *, size_t); +bool stream_getq2(struct stream *s, uint64_t *q); extern uint32_t stream_get_ipv4(struct stream *); +extern bool stream_get_ipaddr(struct stream *s, struct ipaddr *ip); /* IEEE-754 floats */ extern float stream_getf(struct stream *); @@ -252,6 +259,9 @@ extern void stream_reset(struct stream *); extern int stream_flush(struct stream *, int); extern int stream_empty(struct stream *); /* is the stream empty? */ +/* debugging */ +extern void stream_hexdump(const struct stream *s); + /* deprecated */ extern uint8_t *stream_pnt(struct stream *); @@ -279,6 +289,18 @@ extern uint8_t *stream_pnt(struct stream *); */ extern struct stream_fifo *stream_fifo_new(void); +/* + * Init or re-init an on-stack fifo. This allows use of a fifo struct without + * requiring a malloc/free cycle. + * Note well that the fifo must be de-inited with the 'fifo_deinit' api. + */ +void stream_fifo_init(struct stream_fifo *fifo); + +/* + * Deinit an on-stack fifo. + */ +void stream_fifo_deinit(struct stream_fifo *fifo); + /* * Push a stream onto a stream_fifo. * @@ -355,9 +377,10 @@ extern void stream_fifo_free(struct stream_fifo *fifo); * bit), for 64-bit values (you need to cast them anyway), and neither for * encoding (because it's downcasted.) */ -static inline uint8_t *ptr_get_be32(uint8_t *ptr, uint32_t *out) +static inline const uint8_t *ptr_get_be32(const uint8_t *ptr, uint32_t *out) { uint32_t tmp; + memcpy(&tmp, ptr, sizeof(tmp)); *out = ntohl(tmp); return ptr + 4; @@ -402,12 +425,55 @@ static inline uint8_t *ptr_get_be32(uint8_t *ptr, uint32_t *out) (P) = _pval; \ } while (0) +#define STREAM_GETF(S, P) \ + do { \ + union { \ + float r; \ + uint32_t d; \ + } _pval; \ + if (!stream_getl2((S), &_pval.d)) \ + goto stream_failure; \ + (P) = _pval.r; \ + } while (0) + +#define STREAM_GETQ(S, P) \ + do { \ + uint64_t _pval; \ + if (!stream_getq2((S), &_pval)) \ + goto stream_failure; \ + (P) = _pval; \ + } while (0) + +#define STREAM_GET_IPADDR(S, P) \ + do { \ + if (!stream_get_ipaddr((S), (P))) \ + goto stream_failure; \ + } while (0) + #define STREAM_GET(P, STR, SIZE) \ do { \ if (!stream_get2((P), (STR), (SIZE))) \ goto stream_failure; \ } while (0) +#define STREAM_FORWARD_GETP(STR, SIZE) \ + do { \ + if (!stream_forward_getp2((STR), (SIZE))) \ + goto stream_failure; \ + } while (0) + +#define STREAM_REWIND_GETP(STR, SIZE) \ + do { \ + if (!stream_rewind_getp2((STR), (SIZE))) \ + goto stream_failure; \ + } while (0) + +#define STREAM_FORWARD_ENDP(STR, SIZE) \ + do { \ + if (!stream_forward_endp2((STR), (SIZE))) \ + goto stream_failure; \ + } while (0) + #ifdef __cplusplus } #endif diff --git a/lib/subdir.am b/lib/subdir.am index 7027f3f0da..1feaa56d13 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -3,7 +3,7 @@ # lib_LTLIBRARIES += lib/libfrr.la lib_libfrr_la_LDFLAGS = -version-info 0:0:0 -Xlinker -e_libfrr_version -lib_libfrr_la_LIBADD = $(LIBCAP) $(UNWIND_LIBS) $(LIBYANG_LIBS) +lib_libfrr_la_LIBADD = $(LIBCAP) $(UNWIND_LIBS) $(LIBYANG_LIBS) $(LUA_LIB) $(LIBM) lib_libfrr_la_SOURCES = \ lib/agg_table.c \ @@ -18,9 +18,14 @@ lib_libfrr_la_SOURCES = \ lib/command_parse.y \ lib/csv.c \ lib/debug.c \ + lib/defaults.c \ lib/distribute.c \ lib/ferr.c \ lib/filter.c \ + lib/filter_cli.c \ + lib/filter_nb.c \ + lib/frrcu.c \ + lib/frrlua.c \ lib/frr_pthread.c \ lib/frrstr.c \ lib/getopt.c \ @@ -38,15 +43,18 @@ lib_libfrr_la_SOURCES = \ lib/json.c \ lib/keychain.c \ lib/lib_errors.c \ + lib/lib_vty.c \ lib/libfrr.c \ lib/linklist.c \ lib/log.c \ + lib/log_filter.c \ + lib/log_vty.c \ lib/md5.c \ lib/memory.c \ - lib/memory_vty.c \ lib/mlag.c \ lib/module.c \ lib/mpls.c \ + lib/srv6.c \ lib/network.c \ lib/nexthop.c \ lib/netns_linux.c \ @@ -55,16 +63,19 @@ lib_libfrr_la_SOURCES = \ lib/northbound.c \ lib/northbound_cli.c \ lib/northbound_db.c \ + lib/ntop.c \ lib/openbsd-tree.c \ lib/pid_output.c \ lib/plist.c \ - lib/pqueue.c \ lib/prefix.c \ lib/privs.c \ lib/ptm_lib.c \ + lib/pullwr.c \ lib/qobj.c \ lib/ringbuf.c \ lib/routemap.c \ + lib/routemap_cli.c \ + lib/routemap_northbound.c \ lib/sbuf.c \ lib/seqlock.c \ lib/sha256.c \ @@ -92,45 +103,62 @@ lib_libfrr_la_SOURCES = \ lib/yang_translator.c \ lib/yang_wrappers.c \ lib/zclient.c \ - lib/logicalrouter.c \ - lib/lua.c \ + lib/zlog.c \ + lib/zlog_targets.c \ + lib/printf/printf-pos.c \ + lib/printf/vfprintf.c \ + lib/printf/glue.c \ + lib/routing_nb.c \ + lib/routing_nb_config.c \ # end nodist_lib_libfrr_la_SOURCES = \ + yang/frr-filter.yang.c \ yang/frr-interface.yang.c \ + yang/frr-route-map.yang.c \ yang/frr-route-types.yang.c \ + yang/frr-vrf.yang.c \ + yang/frr-routing.yang.c \ + yang/frr-nexthop.yang.c \ + yang/ietf/ietf-routing-types.yang.c \ + yang/ietf/ietf-interfaces.yang.c \ yang/frr-module-translator.yang.c \ + yang/frr-nexthop.yang.c \ # end vtysh_scan += \ - $(top_srcdir)/lib/distribute.c \ - $(top_srcdir)/lib/filter.c \ - $(top_srcdir)/lib/if.c \ - $(top_srcdir)/lib/if_rmap.c \ - $(top_srcdir)/lib/keychain.c \ - $(top_srcdir)/lib/logicalrouter.c \ - $(top_srcdir)/lib/nexthop_group.c \ - $(top_srcdir)/lib/plist.c \ - $(top_srcdir)/lib/routemap.c \ - $(top_srcdir)/lib/vrf.c \ - $(top_srcdir)/lib/vty.c \ + lib/distribute.c \ + lib/filter.c \ + lib/filter_cli.c \ + lib/if.c \ + lib/if_rmap.c \ + lib/keychain.c \ + lib/lib_vty.c \ + lib/nexthop_group.c \ + lib/plist.c \ + lib/routemap.c \ + lib/routemap_cli.c \ + lib/vrf.c \ + lib/vty.c \ # end # can be loaded as DSO - always include for vtysh -vtysh_scan += $(top_srcdir)/lib/agentx.c +vtysh_scan += lib/agentx.c if SQLITE3 lib_libfrr_la_LIBADD += $(SQLITE3_LIBS) lib_libfrr_la_SOURCES += lib/db.c endif -lib/if_clippy.c: $(CLIPPY_DEPS) -lib/if.lo: lib/if_clippy.c -lib/plist_clippy.c: $(CLIPPY_DEPS) -lib/plist.lo: lib/plist_clippy.c -lib/nexthop_group_clippy.c: $(CLIPPY_DEPS) -lib/nexthop_group.lo: lib/nexthop_group_clippy.c -lib/northbound_cli_clippy.c: $(CLIPPY_DEPS) -lib/northbound_cli.lo: lib/northbound_cli_clippy.c +clippy_scan += \ + lib/if.c \ + lib/filter_cli.c \ + lib/log_vty.c \ + lib/nexthop_group.c \ + lib/northbound_cli.c \ + lib/plist.c \ + lib/routemap_cli.c \ + lib/vty.c \ + # end pkginclude_HEADERS += \ lib/agg_table.h \ @@ -147,17 +175,21 @@ pkginclude_HEADERS += \ lib/csv.h \ lib/db.h \ lib/debug.h \ + lib/defaults.h \ lib/distribute.h \ lib/ferr.h \ lib/filter.h \ lib/freebsd-queue.h \ + lib/frrlua.h \ lib/frr_pthread.h \ lib/frratomic.h \ + lib/frrcu.h \ lib/frrstr.h \ lib/getopt.h \ lib/graph.h \ lib/hash.h \ lib/hook.h \ + lib/iana_afi.h \ lib/id_alloc.h \ lib/if.h \ lib/if_rmap.h \ @@ -167,19 +199,22 @@ pkginclude_HEADERS += \ lib/json.h \ lib/keychain.h \ lib/lib_errors.h \ + lib/lib_vty.h \ lib/libfrr.h \ lib/libospf.h \ lib/linklist.h \ lib/log.h \ + lib/log_vty.h \ lib/md5.h \ lib/memory.h \ - lib/memory_vty.h \ lib/module.h \ lib/monotime.h \ lib/mpls.h \ + lib/srv6.h \ lib/network.h \ lib/nexthop.h \ lib/nexthop_group.h \ + lib/nexthop_group_private.h \ lib/northbound.h \ lib/northbound_cli.h \ lib/northbound_db.h \ @@ -187,10 +222,11 @@ pkginclude_HEADERS += \ lib/openbsd-queue.h \ lib/openbsd-tree.h \ lib/plist.h \ - lib/pqueue.h \ lib/prefix.h \ + lib/printfrr.h \ lib/privs.h \ lib/ptm_lib.h \ + lib/pullwr.h \ lib/pw.h \ lib/qobj.h \ lib/queue.h \ @@ -206,6 +242,7 @@ pkginclude_HEADERS += \ lib/sockunion.h \ lib/spf_backoff.h \ lib/srcdest_table.h \ + lib/srte.h \ lib/stream.h \ lib/systemd.h \ lib/table.h \ @@ -227,9 +264,10 @@ pkginclude_HEADERS += \ lib/zassert.h \ lib/zclient.h \ lib/zebra.h \ - lib/logicalrouter.h \ - lib/lua.h \ + lib/zlog.h \ + lib/zlog_targets.h \ lib/pbr.h \ + lib/routing_nb.h \ # end @@ -240,8 +278,9 @@ nodist_pkginclude_HEADERS += \ noinst_HEADERS += \ lib/clippy.h \ - lib/log_int.h \ lib/plist_int.h \ + lib/printf/printfcommon.h \ + lib/printf/printflocal.h \ #end # General note about module and module helper library (libfrrsnmp, libfrrzmq) @@ -270,6 +309,21 @@ lib_libfrrsnmp_la_SOURCES = \ lib/snmp.c \ # end +# +# c-ares support +# +if CARES +lib_LTLIBRARIES += lib/libfrrcares.la +pkginclude_HEADERS += lib/resolver.h +endif + +lib_libfrrcares_la_CFLAGS = $(WERROR) $(CARES_CFLAGS) +lib_libfrrcares_la_LDFLAGS = -version-info 0:0:0 +lib_libfrrcares_la_LIBADD = $(CARES_LIBS) +lib_libfrrcares_la_SOURCES = \ + lib/resolver.c \ + #end + # # ZeroMQ support # @@ -309,6 +363,18 @@ lib_sysrepo_la_LDFLAGS = -avoid-version -module -shared -export-dynamic lib_sysrepo_la_LIBADD = lib/libfrr.la $(SYSREPO_LIBS) lib_sysrepo_la_SOURCES = lib/northbound_sysrepo.c +# +# gRPC northbound plugin +# +if GRPC +module_LTLIBRARIES += lib/grpc.la +endif + +lib_grpc_la_CXXFLAGS = $(WERROR) $(GRPC_CFLAGS) +lib_grpc_la_LDFLAGS = -avoid-version -module -shared -export-dynamic +lib_grpc_la_LIBADD = lib/libfrr.la grpc/libfrrgrpc_pb.la $(GRPC_LIBS) +lib_grpc_la_SOURCES = lib/northbound_grpc.cpp + # # CLI utilities # @@ -319,8 +385,10 @@ noinst_PROGRAMS += \ if BUILD_CLIPPY noinst_PROGRAMS += lib/clippy else -$(HOSTTOOLS)lib/clippy: - @$(MAKE) -C $(top_builddir)/$(HOSTTOOLS) lib/route_types.h lib/clippy +if HOSTTOOLS_CLIPPY +$(CLIPPY): + @$(MAKE) -C $(top_builddir)/hosttools lib/route_types.h lib/clippy +endif endif lib_grammar_sandbox_SOURCES = \ @@ -351,13 +419,11 @@ am__v_CLIPPY_ = $(am__v_CLIPPY_$(AM_DEFAULT_VERBOSITY)) am__v_CLIPPY_0 = @echo " CLIPPY " $@; am__v_CLIPPY_1 = -CLIPPY_DEPS = $(HOSTTOOLS)lib/clippy $(top_srcdir)/python/clidef.py +CLIPPY_DEPS = $(CLIPPY) $(top_srcdir)/python/clidef.py -SUFFIXES = _clippy.c .proto .pb-c.c .pb-c.h .pb.h +SUFFIXES += _clippy.c .c_clippy.c: - @{ test -x $(top_builddir)/$(HOSTTOOLS)lib/clippy || \ - $(MAKE) -C $(top_builddir)/$(HOSTTOOLS) lib/clippy; } - $(AM_V_CLIPPY) $(top_builddir)/$(HOSTTOOLS)lib/clippy $(top_srcdir)/python/clidef.py -o $@ $< + $(AM_V_CLIPPY) $(CLIPPY) $(top_srcdir)/python/clidef.py -o $@ $< ## automake's "ylwrap" is a great piece of GNU software... not. .l.c: diff --git a/lib/systemd.c b/lib/systemd.c index 8a2a5eeac3..c5cc3aa447 100644 --- a/lib/systemd.c +++ b/lib/systemd.c @@ -32,7 +32,7 @@ * Wrapper this silliness if we * don't have systemd */ -void systemd_send_information(const char *info) +static void systemd_send_information(const char *info) { #if defined HAVE_SYSTEMD sd_notify(0, info); @@ -86,14 +86,15 @@ static int systemd_get_watchdog_time(int the_process) void systemd_send_stopping(void) { + systemd_send_information("STATUS="); systemd_send_information("STOPPING=1"); } /* * How many seconds should we wait between watchdog sends */ -int wsecs = 0; -struct thread_master *systemd_master = NULL; +static int wsecs = 0; +static struct thread_master *systemd_master = NULL; static int systemd_send_watchdog(struct thread *t) { @@ -113,6 +114,16 @@ void systemd_send_started(struct thread_master *m, int the_process) systemd_master = m; systemd_send_information("READY=1"); - if (wsecs != 0) + if (wsecs != 0) { + systemd_send_information("WATCHDOG=1"); thread_add_timer(m, systemd_send_watchdog, m, wsecs, NULL); + } +} + +void systemd_send_status(const char *status) +{ + char buffer[1024]; + + snprintf(buffer, sizeof(buffer), "STATUS=%s", status); + systemd_send_information(buffer); } diff --git a/lib/systemd.h b/lib/systemd.h index 6e43df527d..d9885c5d9c 100644 --- a/lib/systemd.h +++ b/lib/systemd.h @@ -32,7 +32,6 @@ extern "C" { * To turn on systemd compilation, use --enable-systemd on * configure run. */ -void systemd_send_information(const char *info); void systemd_send_stopping(void); /* @@ -42,6 +41,11 @@ void systemd_send_stopping(void); */ void systemd_send_started(struct thread_master *master, int the_process); +/* + * status - A status string to send to systemd + */ +void systemd_send_status(const char *status); + #ifdef __cplusplus } #endif diff --git a/lib/table.c b/lib/table.c index edba7f1932..86347cbacd 100644 --- a/lib/table.c +++ b/lib/table.c @@ -28,17 +28,19 @@ #include "memory.h" #include "sockunion.h" -DEFINE_MTYPE(LIB, ROUTE_TABLE, "Route table") +DEFINE_MTYPE_STATIC(LIB, ROUTE_TABLE, "Route table") DEFINE_MTYPE(LIB, ROUTE_NODE, "Route node") static void route_table_free(struct route_table *); -static bool route_table_hash_cmp(const void *a, const void *b) +static int route_table_hash_cmp(const struct route_node *a, + const struct route_node *b) { - const struct prefix *pa = a, *pb = b; - return prefix_cmp(pa, pb) == 0; + return prefix_cmp(&a->p, &b->p); } +DECLARE_HASH(rn_hash_node, struct route_node, nodehash, route_table_hash_cmp, + prefix_hash_key) /* * route_table_init_with_delegate */ @@ -49,8 +51,7 @@ route_table_init_with_delegate(route_table_delegate_t *delegate) rt = XCALLOC(MTYPE_ROUTE_TABLE, sizeof(struct route_table)); rt->delegate = delegate; - rt->hash = hash_create(prefix_hash_key, route_table_hash_cmp, - "route table hash"); + rn_hash_node_init(&rt->hash); return rt; } @@ -69,15 +70,14 @@ static struct route_node *route_node_new(struct route_table *table) static struct route_node *route_node_set(struct route_table *table, const struct prefix *prefix) { - struct route_node *node, *inserted; + struct route_node *node; node = route_node_new(table); prefix_copy(&node->p, prefix); node->table = table; - inserted = hash_get(node->table->hash, node, hash_alloc_intern); - assert(inserted == node); + rn_hash_node_add(&node->table->hash, node); return node; } @@ -99,9 +99,6 @@ static void route_table_free(struct route_table *rt) if (rt == NULL) return; - hash_clean(rt->hash, NULL); - hash_free(rt->hash); - node = rt->top; /* Bulk deletion of nodes remaining in this table. This function is not @@ -123,6 +120,7 @@ static void route_table_free(struct route_table *rt) tmp_node->table->count--; tmp_node->lock = 0; /* to cause assert if unlocked after this */ + rn_hash_node_del(&rt->hash, tmp_node); route_node_free(rt, tmp_node); if (node != NULL) { @@ -137,6 +135,7 @@ static void route_table_free(struct route_table *rt) assert(rt->count == 0); + rn_hash_node_fini(&rt->hash); XFREE(MTYPE_ROUTE_TABLE, rt); return; } @@ -161,7 +160,7 @@ static void route_common(const struct prefix *n, const struct prefix *p, np = (const uint8_t *)&n->u.prefix; pp = (const uint8_t *)&p->u.prefix; - newp = (uint8_t *)&new->u.prefix; + newp = &new->u.prefix; for (i = 0; i < p->prefixlen / 8; i++) { if (np[i] == pp[i]) @@ -192,7 +191,7 @@ static void set_link(struct route_node *node, struct route_node *new) } /* Find matched prefix. */ -struct route_node *route_node_match(const struct route_table *table, +struct route_node *route_node_match(struct route_table *table, union prefixconstptr pu) { const struct prefix *p = pu.p; @@ -222,7 +221,7 @@ struct route_node *route_node_match(const struct route_table *table, return NULL; } -struct route_node *route_node_match_ipv4(const struct route_table *table, +struct route_node *route_node_match_ipv4(struct route_table *table, const struct in_addr *addr) { struct prefix_ipv4 p; @@ -235,7 +234,7 @@ struct route_node *route_node_match_ipv4(const struct route_table *table, return route_node_match(table, (struct prefix *)&p); } -struct route_node *route_node_match_ipv6(const struct route_table *table, +struct route_node *route_node_match_ipv6(struct route_table *table, const struct in6_addr *addr) { struct prefix_ipv6 p; @@ -245,49 +244,50 @@ struct route_node *route_node_match_ipv6(const struct route_table *table, p.prefixlen = IPV6_MAX_PREFIXLEN; p.prefix = *addr; - return route_node_match(table, (struct prefix *)&p); + return route_node_match(table, &p); } /* Lookup same prefix node. Return NULL when we can't find route. */ -struct route_node *route_node_lookup(const struct route_table *table, +struct route_node *route_node_lookup(struct route_table *table, union prefixconstptr pu) { - struct prefix p; - struct route_node *node; - prefix_copy(&p, pu.p); - apply_mask(&p); + struct route_node rn, *node; + prefix_copy(&rn.p, pu.p); + apply_mask(&rn.p); - node = hash_get(table->hash, (void *)&p, NULL); + node = rn_hash_node_find(&table->hash, &rn); return (node && node->info) ? route_lock_node(node) : NULL; } /* Lookup same prefix node. Return NULL when we can't find route. */ -struct route_node *route_node_lookup_maynull(const struct route_table *table, +struct route_node *route_node_lookup_maynull(struct route_table *table, union prefixconstptr pu) { - struct prefix p; - struct route_node *node; - prefix_copy(&p, pu.p); - apply_mask(&p); + struct route_node rn, *node; + prefix_copy(&rn.p, pu.p); + apply_mask(&rn.p); - node = hash_get(table->hash, (void *)&p, NULL); + node = rn_hash_node_find(&table->hash, &rn); return node ? route_lock_node(node) : NULL; } /* Add node to routing table. */ -struct route_node *route_node_get(struct route_table *const table, +struct route_node *route_node_get(struct route_table *table, union prefixconstptr pu) { - const struct prefix *p = pu.p; + struct route_node search; + struct prefix *p = &search.p; + + prefix_copy(p, pu.p); + apply_mask(p); + struct route_node *new; struct route_node *node; struct route_node *match; - struct route_node *inserted; uint16_t prefixlen = p->prefixlen; const uint8_t *prefix = &p->u.prefix; - apply_mask((struct prefix *)p); - node = hash_get(table->hash, (void *)p, NULL); + node = rn_hash_node_find(&table->hash, &search); if (node && node->info) return route_lock_node(node); @@ -314,8 +314,7 @@ struct route_node *route_node_get(struct route_table *const table, new->p.family = p->family; new->table = table; set_link(new, node); - inserted = hash_get(node->table->hash, new, hash_alloc_intern); - assert(inserted == new); + rn_hash_node_add(&table->hash, new); if (match) set_link(match, new); @@ -367,7 +366,7 @@ void route_node_delete(struct route_node *node) node->table->count--; - hash_release(node->table->hash, node); + rn_hash_node_del(&node->table->hash, node); /* WARNING: FRAGILE CODE! * route_node_free may have the side effect of free'ing the entire @@ -472,7 +471,7 @@ struct route_node *route_next_until(struct route_node *node, return NULL; } -unsigned long route_table_count(const struct route_table *table) +unsigned long route_table_count(struct route_table *table) { return table->count; } @@ -607,7 +606,7 @@ static struct route_node *route_get_subtree_next(struct route_node *node) * @see route_table_get_next */ static struct route_node * -route_table_get_next_internal(const struct route_table *table, +route_table_get_next_internal(struct route_table *table, const struct prefix *p) { struct route_node *node, *tmp_node; @@ -708,7 +707,7 @@ route_table_get_next_internal(const struct route_table *table, * Find the node that occurs after the given prefix in order of * iteration. */ -struct route_node *route_table_get_next(const struct route_table *table, +struct route_node *route_table_get_next(struct route_table *table, union prefixconstptr pu) { const struct prefix *p = pu.p; diff --git a/lib/table.h b/lib/table.h index ce578e795c..2858784341 100644 --- a/lib/table.h +++ b/lib/table.h @@ -25,12 +25,12 @@ #include "memory.h" #include "hash.h" #include "prefix.h" +#include "typesafe.h" #ifdef __cplusplus extern "C" { #endif -DECLARE_MTYPE(ROUTE_TABLE) DECLARE_MTYPE(ROUTE_NODE) /* @@ -45,7 +45,7 @@ struct route_table; * Function vector that can be used by a client to customize the * behavior of one or more route tables. */ -typedef struct route_table_delegate_t_ route_table_delegate_t; +typedef const struct route_table_delegate_t_ route_table_delegate_t; typedef struct route_node *(*route_table_create_node_func_t)( route_table_delegate_t *, struct route_table *); @@ -59,10 +59,12 @@ struct route_table_delegate_t_ { route_table_destroy_node_func_t destroy_node; }; +PREDECL_HASH(rn_hash_node) + /* Routing table top structure. */ struct route_table { struct route_node *top; - struct hash *hash; + struct rn_hash_node_head hash; /* * Delegate that performs certain functions for this table. @@ -129,6 +131,7 @@ struct route_table { /* Lock of this radix */ \ unsigned int table_rdonly(lock); \ \ + struct rn_hash_node_item nodehash; \ /* Each node of route. */ \ void *info; \ @@ -199,21 +202,20 @@ extern struct route_node *route_top(struct route_table *table); extern struct route_node *route_next(struct route_node *node); extern struct route_node *route_next_until(struct route_node *node, const struct route_node *limit); -extern struct route_node *route_node_get(struct route_table *const table, +extern struct route_node *route_node_get(struct route_table *table, union prefixconstptr pu); -extern struct route_node *route_node_lookup(const struct route_table *table, +extern struct route_node *route_node_lookup(struct route_table *table, union prefixconstptr pu); -extern struct route_node * -route_node_lookup_maynull(const struct route_table *table, - union prefixconstptr pu); -extern struct route_node *route_node_match(const struct route_table *table, +extern struct route_node *route_node_lookup_maynull(struct route_table *table, + union prefixconstptr pu); +extern struct route_node *route_node_match(struct route_table *table, union prefixconstptr pu); -extern struct route_node *route_node_match_ipv4(const struct route_table *table, +extern struct route_node *route_node_match_ipv4(struct route_table *table, const struct in_addr *addr); -extern struct route_node *route_node_match_ipv6(const struct route_table *table, +extern struct route_node *route_node_match_ipv6(struct route_table *table, const struct in6_addr *addr); -extern unsigned long route_table_count(const struct route_table *table); +extern unsigned long route_table_count(struct route_table *table); extern struct route_node *route_node_create(route_table_delegate_t *delegate, struct route_table *table); @@ -222,7 +224,7 @@ extern void route_node_destroy(route_table_delegate_t *delegate, struct route_table *table, struct route_node *node); -extern struct route_node *route_table_get_next(const struct route_table *table, +extern struct route_node *route_table_get_next(struct route_table *table, union prefixconstptr pu); extern int route_table_prefix_iter_cmp(const struct prefix *p1, const struct prefix *p2); @@ -291,6 +293,8 @@ static inline struct route_node *route_table_iter_next(route_table_iter_t *iter) return NULL; default: + /* Suppress uninitialized variable warning */ + node = NULL; assert(0); } @@ -306,7 +310,7 @@ static inline struct route_node *route_table_iter_next(route_table_iter_t *iter) /* * route_table_iter_is_done * - * Returns TRUE if the iteration is complete. + * Returns true if the iteration is complete. */ static inline int route_table_iter_is_done(route_table_iter_t *iter) { @@ -316,13 +320,17 @@ static inline int route_table_iter_is_done(route_table_iter_t *iter) /* * route_table_iter_started * - * Returns TRUE if this iterator has started iterating over the tree. + * Returns true if this iterator has started iterating over the tree. */ static inline int route_table_iter_started(route_table_iter_t *iter) { return iter->state != RT_ITER_STATE_INIT; } +#ifdef _FRR_ATTRIBUTE_PRINTFRR +#pragma FRR printfrr_ext "%pRN" (struct route_node *) +#endif + #ifdef __cplusplus } #endif diff --git a/lib/termtable.c b/lib/termtable.c index 01468b8203..b22a1ad387 100644 --- a/lib/termtable.c +++ b/lib/termtable.c @@ -20,13 +20,14 @@ #include #include +#include "printfrr.h" #include "memory.h" #include "termtable.h" DEFINE_MTYPE_STATIC(LIB, TTABLE, "ASCII table") /* clang-format off */ -struct ttable_style ttable_styles[] = { +const struct ttable_style ttable_styles[] = { { // default ascii .corner = '+', .rownums_on = false, @@ -98,7 +99,7 @@ void ttable_del(struct ttable *tt) XFREE(MTYPE_TTABLE, tt); } -struct ttable *ttable_new(struct ttable_style *style) +struct ttable *ttable_new(const struct ttable_style *style) { struct ttable *tt; @@ -134,6 +135,7 @@ static struct ttable_cell *ttable_insert_row_va(struct ttable *tt, int i, { assert(i >= -1 && i < tt->nrows); + char shortbuf[256]; char *res, *orig, *section; struct ttable_cell *row; int col = 0; @@ -158,9 +160,7 @@ static struct ttable_cell *ttable_insert_row_va(struct ttable *tt, int i, /* CALLOC a block of cells */ row = XCALLOC(MTYPE_TTABLE, tt->ncols * sizeof(struct ttable_cell)); - res = NULL; - vasprintf(&res, format, ap); - + res = vasnprintfrr(MTYPE_TMP, shortbuf, sizeof(shortbuf), format, ap); orig = res; while (res && col < tt->ncols) { @@ -170,7 +170,8 @@ static struct ttable_cell *ttable_insert_row_va(struct ttable *tt, int i, col++; } - free(orig); + if (orig != shortbuf) + XFREE(MTYPE_TMP, orig); /* insert row */ if (i == -1 || i == tt->nrows) diff --git a/lib/termtable.h b/lib/termtable.h index 491010a856..698cc73465 100644 --- a/lib/termtable.h +++ b/lib/termtable.h @@ -80,7 +80,7 @@ struct ttable { #define TTSTYLE_ASCII 0 #define TTSTYLE_BLANK 1 -extern struct ttable_style ttable_styles[2]; +extern const struct ttable_style ttable_styles[2]; /** * Creates a new table with the default style, which looks like this: @@ -95,7 +95,7 @@ extern struct ttable_style ttable_styles[2]; * * @return the created table */ -struct ttable *ttable_new(struct ttable_style *tts); +struct ttable *ttable_new(const struct ttable_style *tts); /** * Deletes a table and releases all associated resources. @@ -137,8 +137,7 @@ void ttable_cell_del(struct ttable_cell *cell); * columns were specified */ struct ttable_cell *ttable_insert_row(struct ttable *tt, unsigned int row, - const char *format, ...) - PRINTF_ATTRIBUTE(3, 4); + const char *format, ...) PRINTFRR(3, 4); /** * Inserts a new row at the end of the table. * @@ -164,7 +163,7 @@ struct ttable_cell *ttable_insert_row(struct ttable *tt, unsigned int row, * columns were specified */ struct ttable_cell *ttable_add_row(struct ttable *tt, const char *format, ...) - PRINTF_ATTRIBUTE(2, 3); + PRINTFRR(2, 3); /** * Removes a row from the table. diff --git a/lib/thread.c b/lib/thread.c index 5ca859a74d..8d840359e4 100644 --- a/lib/thread.c +++ b/lib/thread.c @@ -25,14 +25,15 @@ #include "thread.h" #include "memory.h" +#include "frrcu.h" #include "log.h" #include "hash.h" -#include "pqueue.h" #include "command.h" #include "sigevent.h" #include "network.h" #include "jhash.h" #include "frratomic.h" +#include "frr_pthread.h" #include "lib_errors.h" DEFINE_MTYPE_STATIC(LIB, THREAD, "Thread") @@ -42,6 +43,22 @@ DEFINE_MTYPE_STATIC(LIB, THREAD_STATS, "Thread stats") DECLARE_LIST(thread_list, struct thread, threaditem) +static int thread_timer_cmp(const struct thread *a, const struct thread *b) +{ + if (a->u.sands.tv_sec < b->u.sands.tv_sec) + return -1; + if (a->u.sands.tv_sec > b->u.sands.tv_sec) + return 1; + if (a->u.sands.tv_usec < b->u.sands.tv_usec) + return -1; + if (a->u.sands.tv_usec > b->u.sands.tv_usec) + return 1; + return 0; +} + +DECLARE_HEAP(thread_timer_list, struct thread, timeritem, + thread_timer_cmp) + #if defined(__APPLE__) #include #include @@ -49,7 +66,7 @@ DECLARE_LIST(thread_list, struct thread, threaditem) #define AWAKEN(m) \ do { \ - static unsigned char wakebyte = 0x01; \ + const unsigned char wakebyte = 0x01; \ write(m->io_pipe[1], &wakebyte, 1); \ } while (0); @@ -63,7 +80,7 @@ static struct list *masters; static void thread_free(struct thread_master *master, struct thread *thread); /* CLI start ---------------------------------------------------------------- */ -static unsigned int cpu_record_hash_key(struct cpu_thread_history *a) +static unsigned int cpu_record_hash_key(const struct cpu_thread_history *a) { int size = sizeof(a->func); @@ -92,13 +109,15 @@ static void cpu_record_hash_free(void *a) XFREE(MTYPE_THREAD_STATS, hist); } +#ifndef EXCLUDE_CPU_TIME static void vty_out_cpu_thread_history(struct vty *vty, struct cpu_thread_history *a) { - vty_out(vty, "%5zu %10zu.%03lu %9zu %8zu %9zu %8lu %9lu", - a->total_active, a->cpu.total / 1000, a->cpu.total % 1000, - a->total_calls, a->cpu.total / a->total_calls, a->cpu.max, - a->real.total / a->total_calls, a->real.max); + vty_out(vty, "%5zu %10zu.%03zu %9zu %8zu %9zu %8zu %9zu", + (size_t)a->total_active, a->cpu.total / 1000, + a->cpu.total % 1000, (size_t)a->total_calls, + (size_t)(a->cpu.total / a->total_calls), a->cpu.max, + (size_t)(a->real.total / a->total_calls), a->real.max); vty_out(vty, " %c%c%c%c%c %s\n", a->types & (1 << THREAD_READ) ? 'R' : ' ', a->types & (1 << THREAD_WRITE) ? 'W' : ' ', @@ -151,12 +170,11 @@ static void cpu_record_print(struct vty *vty, uint8_t filter) struct thread_master *m; struct listnode *ln; - memset(&tmp, 0, sizeof tmp); + memset(&tmp, 0, sizeof(tmp)); tmp.funcname = "TOTAL"; tmp.types = filter; - pthread_mutex_lock(&masters_mtx); - { + frr_with_mutex(&masters_mtx) { for (ALL_LIST_ELEMENTS_RO(masters, ln, m)) { const char *name = m->name ? m->name : "main"; @@ -188,7 +206,6 @@ static void cpu_record_print(struct vty *vty, uint8_t filter) vty_out(vty, "\n"); } } - pthread_mutex_unlock(&masters_mtx); vty_out(vty, "\n"); vty_out(vty, "Total thread statistics\n"); @@ -202,6 +219,7 @@ static void cpu_record_print(struct vty *vty, uint8_t filter) if (tmp.total_calls > 0) vty_out_cpu_thread_history(vty, &tmp); } +#endif static void cpu_record_hash_clear(struct hash_bucket *bucket, void *args[]) { @@ -222,11 +240,9 @@ static void cpu_record_clear(uint8_t filter) struct thread_master *m; struct listnode *ln; - pthread_mutex_lock(&masters_mtx); - { + frr_with_mutex(&masters_mtx) { for (ALL_LIST_ELEMENTS_RO(masters, ln, m)) { - pthread_mutex_lock(&m->mtx); - { + frr_with_mutex(&m->mtx) { void *args[2] = {tmp, m->cpu_record}; hash_iterate( m->cpu_record, @@ -234,10 +250,8 @@ static void cpu_record_clear(uint8_t filter) void *))cpu_record_hash_clear, args); } - pthread_mutex_unlock(&m->mtx); } } - pthread_mutex_unlock(&masters_mtx); } static uint8_t parse_filter(const char *filterstr) @@ -275,13 +289,14 @@ static uint8_t parse_filter(const char *filterstr) return filter; } +#ifndef EXCLUDE_CPU_TIME DEFUN (show_thread_cpu, show_thread_cpu_cmd, "show thread cpu [FILTER]", SHOW_STR "Thread information\n" "Thread CPU usage\n" - "Display filter (rwtexb)\n") + "Display filter (rwtex)\n") { uint8_t filter = (uint8_t)-1U; int idx = 0; @@ -290,8 +305,7 @@ DEFUN (show_thread_cpu, filter = parse_filter(argv[idx]->arg); if (!filter) { vty_out(vty, - "Invalid filter \"%s\" specified; must contain at least" - "one of 'RWTEXB'\n", + "Invalid filter \"%s\" specified; must contain at leastone of 'RWTEXB'\n", argv[idx]->arg); return CMD_WARNING; } @@ -300,11 +314,13 @@ DEFUN (show_thread_cpu, cpu_record_print(vty, filter); return CMD_SUCCESS; } +#endif static void show_thread_poll_helper(struct vty *vty, struct thread_master *m) { const char *name = m->name ? m->name : "main"; char underline[strlen(name) + 1]; + struct thread *thread; uint32_t i; memset(underline, '-', sizeof(underline)); @@ -312,12 +328,33 @@ static void show_thread_poll_helper(struct vty *vty, struct thread_master *m) vty_out(vty, "\nShowing poll FD's for %s\n", name); vty_out(vty, "----------------------%s\n", underline); - vty_out(vty, "Count: %u\n", (uint32_t)m->handler.pfdcount); - for (i = 0; i < m->handler.pfdcount; i++) - vty_out(vty, "\t%6d fd:%6d events:%2d revents:%2d\n", i, - m->handler.pfds[i].fd, - m->handler.pfds[i].events, + vty_out(vty, "Count: %u/%d\n", (uint32_t)m->handler.pfdcount, + m->fd_limit); + for (i = 0; i < m->handler.pfdcount; i++) { + vty_out(vty, "\t%6d fd:%6d events:%2d revents:%2d\t\t", i, + m->handler.pfds[i].fd, m->handler.pfds[i].events, m->handler.pfds[i].revents); + + if (m->handler.pfds[i].events & POLLIN) { + thread = m->read[m->handler.pfds[i].fd]; + + if (!thread) + vty_out(vty, "ERROR "); + else + vty_out(vty, "%s ", thread->funcname); + } else + vty_out(vty, " "); + + if (m->handler.pfds[i].events & POLLOUT) { + thread = m->write[m->handler.pfds[i].fd]; + + if (!thread) + vty_out(vty, "ERROR\n"); + else + vty_out(vty, "%s\n", thread->funcname); + } else + vty_out(vty, "\n"); + } } DEFUN (show_thread_poll, @@ -330,13 +367,11 @@ DEFUN (show_thread_poll, struct listnode *node; struct thread_master *m; - pthread_mutex_lock(&masters_mtx); - { + frr_with_mutex(&masters_mtx) { for (ALL_LIST_ELEMENTS_RO(masters, node, m)) { show_thread_poll_helper(vty, m); } } - pthread_mutex_unlock(&masters_mtx); return CMD_SUCCESS; } @@ -357,8 +392,7 @@ DEFUN (clear_thread_cpu, filter = parse_filter(argv[idx]->arg); if (!filter) { vty_out(vty, - "Invalid filter \"%s\" specified; must contain at least" - "one of 'RWTEXB'\n", + "Invalid filter \"%s\" specified; must contain at leastone of 'RWTEXB'\n", argv[idx]->arg); return CMD_WARNING; } @@ -370,32 +404,15 @@ DEFUN (clear_thread_cpu, void thread_cmd_init(void) { +#ifndef EXCLUDE_CPU_TIME install_element(VIEW_NODE, &show_thread_cpu_cmd); +#endif install_element(VIEW_NODE, &show_thread_poll_cmd); install_element(ENABLE_NODE, &clear_thread_cpu_cmd); } /* CLI end ------------------------------------------------------------------ */ -static int thread_timer_cmp(void *a, void *b) -{ - struct thread *thread_a = a; - struct thread *thread_b = b; - - if (timercmp(&thread_a->u.sands, &thread_b->u.sands, <)) - return -1; - if (timercmp(&thread_a->u.sands, &thread_b->u.sands, >)) - return 1; - return 0; -} - -static void thread_timer_update(void *node, int actual_position) -{ - struct thread *thread = node; - - thread->index = actual_position; -} - static void cancelreq_del(void *cr) { XFREE(MTYPE_TMP, cr); @@ -433,18 +450,14 @@ struct thread_master *thread_master_create(const char *name) sizeof(struct thread *) * rv->fd_limit); rv->cpu_record = hash_create_size( - 8, (unsigned int (*)(void *))cpu_record_hash_key, + 8, (unsigned int (*)(const void *))cpu_record_hash_key, (bool (*)(const void *, const void *))cpu_record_hash_cmp, "Thread Hash"); thread_list_init(&rv->event); thread_list_init(&rv->ready); thread_list_init(&rv->unuse); - - /* Initialize the timer queues */ - rv->timer = pqueue_create(); - rv->timer->cmp = thread_timer_cmp; - rv->timer->update = thread_timer_update; + thread_timer_list_init(&rv->timer); /* Initialize thread_fetch() settings */ rv->spin = true; @@ -470,26 +483,22 @@ struct thread_master *thread_master_create(const char *name) sizeof(struct pollfd) * rv->handler.pfdsize); /* add to list of threadmasters */ - pthread_mutex_lock(&masters_mtx); - { + frr_with_mutex(&masters_mtx) { if (!masters) masters = list_new(); listnode_add(masters, rv); } - pthread_mutex_unlock(&masters_mtx); return rv; } void thread_master_set_name(struct thread_master *master, const char *name) { - pthread_mutex_lock(&master->mtx); - { + frr_with_mutex(&master->mtx) { XFREE(MTYPE_THREAD_MASTER, master->name); master->name = XSTRDUP(MTYPE_THREAD_MASTER, name); } - pthread_mutex_unlock(&master->mtx); } #define THREAD_UNUSED_DEPTH 10 @@ -542,16 +551,6 @@ static void thread_array_free(struct thread_master *m, XFREE(MTYPE_THREAD_POLL, thread_array); } -static void thread_queue_free(struct thread_master *m, struct pqueue *queue) -{ - int i; - - for (i = 0; i < queue->size; i++) - thread_free(m, queue->array[i]); - - pqueue_delete(queue); -} - /* * thread_master_free_unused * @@ -562,30 +561,29 @@ static void thread_queue_free(struct thread_master *m, struct pqueue *queue) */ void thread_master_free_unused(struct thread_master *m) { - pthread_mutex_lock(&m->mtx); - { + frr_with_mutex(&m->mtx) { struct thread *t; while ((t = thread_list_pop(&m->unuse))) thread_free(m, t); } - pthread_mutex_unlock(&m->mtx); } /* Stop thread scheduler. */ void thread_master_free(struct thread_master *m) { - pthread_mutex_lock(&masters_mtx); - { + struct thread *t; + + frr_with_mutex(&masters_mtx) { listnode_delete(masters, m); if (masters->count == 0) { list_delete(&masters); } } - pthread_mutex_unlock(&masters_mtx); thread_array_free(m, m->read); thread_array_free(m, m->write); - thread_queue_free(m, m->timer); + while ((t = thread_timer_list_pop(&m->timer))) + thread_free(m, t); thread_list_free(m, &m->event); thread_list_free(m, &m->ready); thread_list_free(m, &m->unuse); @@ -611,11 +609,9 @@ unsigned long thread_timer_remain_msec(struct thread *thread) { int64_t remain; - pthread_mutex_lock(&thread->mtx); - { + frr_with_mutex(&thread->mtx) { remain = monotime_until(&thread->u.sands, NULL) / 1000LL; } - pthread_mutex_unlock(&thread->mtx); return remain < 0 ? 0 : remain; } @@ -632,14 +628,42 @@ unsigned long thread_timer_remain_second(struct thread *thread) struct timeval thread_timer_remain(struct thread *thread) { struct timeval remain; - pthread_mutex_lock(&thread->mtx); - { + frr_with_mutex(&thread->mtx) { monotime_until(&thread->u.sands, &remain); } - pthread_mutex_unlock(&thread->mtx); return remain; } +static int time_hhmmss(char *buf, int buf_size, long sec) +{ + long hh; + long mm; + int wr; + + zassert(buf_size >= 8); + + hh = sec / 3600; + sec %= 3600; + mm = sec / 60; + sec %= 60; + + wr = snprintf(buf, buf_size, "%02ld:%02ld:%02ld", hh, mm, sec); + + return wr != 8; +} + +char *thread_timer_to_hhmmss(char *buf, int buf_size, + struct thread *t_timer) +{ + if (t_timer) { + time_hhmmss(buf, buf_size, + thread_timer_remain_second(t_timer)); + } else { + snprintf(buf, buf_size, "--:--:--"); + } + return buf; +} + /* Get new thread. */ static struct thread *thread_get(struct thread_master *m, uint8_t type, int (*func)(struct thread *), void *arg, @@ -659,7 +683,6 @@ static struct thread *thread_get(struct thread_master *m, uint8_t type, thread->add_type = type; thread->master = m; thread->arg = arg; - thread->index = -1; thread->yield = THREAD_YIELD_TIME_SLOT; /* default */ thread->ref = NULL; @@ -700,20 +723,23 @@ static void thread_free(struct thread_master *master, struct thread *thread) XFREE(MTYPE_THREAD, thread); } -static int fd_poll(struct thread_master *m, struct pollfd *pfds, nfds_t pfdsize, - nfds_t count, const struct timeval *timer_wait) +static int fd_poll(struct thread_master *m, const struct timeval *timer_wait, + bool *eintr_p) { - /* If timer_wait is null here, that means poll() should block - * indefinitely, - * unless the thread_master has overriden it by setting + sigset_t origsigs; + unsigned char trash[64]; + nfds_t count = m->handler.copycount; + + /* + * If timer_wait is null here, that means poll() should block + * indefinitely, unless the thread_master has overridden it by setting * ->selectpoll_timeout. + * * If the value is positive, it specifies the maximum number of - * milliseconds - * to wait. If the timeout is -1, it specifies that we should never wait - * and - * always return immediately even if no event is detected. If the value - * is - * zero, the behavior is default. */ + * milliseconds to wait. If the timeout is -1, it specifies that + * we should never wait and always return immediately even if no + * event is detected. If the value is zero, the behavior is default. + */ int timeout = -1; /* number of file descriptors with events */ @@ -729,19 +755,68 @@ static int fd_poll(struct thread_master *m, struct pollfd *pfds, nfds_t pfdsize, < 0) // effect a poll (return immediately) timeout = 0; + zlog_tls_buffer_flush(); + rcu_read_unlock(); + rcu_assert_read_unlocked(); + /* add poll pipe poker */ - assert(count + 1 < pfdsize); - pfds[count].fd = m->io_pipe[0]; - pfds[count].events = POLLIN; - pfds[count].revents = 0x00; + assert(count + 1 < m->handler.pfdsize); + m->handler.copy[count].fd = m->io_pipe[0]; + m->handler.copy[count].events = POLLIN; + m->handler.copy[count].revents = 0x00; + + /* We need to deal with a signal-handling race here: we + * don't want to miss a crucial signal, such as SIGTERM or SIGINT, + * that may arrive just before we enter poll(). We will block the + * key signals, then check whether any have arrived - if so, we return + * before calling poll(). If not, we'll re-enable the signals + * in the ppoll() call. + */ + + sigemptyset(&origsigs); + if (m->handle_signals) { + /* Main pthread that handles the app signals */ + if (frr_sigevent_check(&origsigs)) { + /* Signal to process - restore signal mask and return */ + pthread_sigmask(SIG_SETMASK, &origsigs, NULL); + num = -1; + *eintr_p = true; + goto done; + } + } else { + /* Don't make any changes for the non-main pthreads */ + pthread_sigmask(SIG_SETMASK, NULL, &origsigs); + } - num = poll(pfds, count + 1, timeout); +#if defined(HAVE_PPOLL) + struct timespec ts, *tsp; - unsigned char trash[64]; - if (num > 0 && pfds[count].revents != 0 && num--) + if (timeout >= 0) { + ts.tv_sec = timeout / 1000; + ts.tv_nsec = (timeout % 1000) * 1000000; + tsp = &ts; + } else + tsp = NULL; + + num = ppoll(m->handler.copy, count + 1, tsp, &origsigs); + pthread_sigmask(SIG_SETMASK, &origsigs, NULL); +#else + /* Not ideal - there is a race after we restore the signal mask */ + pthread_sigmask(SIG_SETMASK, &origsigs, NULL); + num = poll(m->handler.copy, count + 1, timeout); +#endif + +done: + + if (num < 0 && errno == EINTR) + *eintr_p = true; + + if (num > 0 && m->handler.copy[count].revents != 0 && num--) while (read(m->io_pipe[0], &trash, sizeof(trash)) > 0) ; + rcu_read_lock(); + return num; } @@ -753,25 +828,36 @@ struct thread *funcname_thread_add_read_write(int dir, struct thread_master *m, debugargdef) { struct thread *thread = NULL; + struct thread **thread_array; assert(fd >= 0 && fd < m->fd_limit); - pthread_mutex_lock(&m->mtx); - { - if (t_ptr - && *t_ptr) // thread is already scheduled; don't reschedule - { - pthread_mutex_unlock(&m->mtx); - return NULL; - } + frr_with_mutex(&m->mtx) { + if (t_ptr && *t_ptr) + // thread is already scheduled; don't reschedule + break; /* default to a new pollfd */ nfds_t queuepos = m->handler.pfdcount; + if (dir == THREAD_READ) + thread_array = m->read; + else + thread_array = m->write; + /* if we already have a pollfd for our file descriptor, find and * use it */ for (nfds_t i = 0; i < m->handler.pfdcount; i++) if (m->handler.pfds[i].fd == fd) { queuepos = i; + +#ifdef DEV_BUILD + /* + * What happens if we have a thread already + * created for this event? + */ + if (thread_array[fd]) + assert(!"Thread already scheduled for file descriptor"); +#endif break; } @@ -788,15 +874,10 @@ struct thread *funcname_thread_add_read_write(int dir, struct thread_master *m, m->handler.pfdcount++; if (thread) { - pthread_mutex_lock(&thread->mtx); - { + frr_with_mutex(&thread->mtx) { thread->u.fd = fd; - if (dir == THREAD_READ) - m->read[thread->u.fd] = thread; - else - m->write[thread->u.fd] = thread; + thread_array[thread->u.fd] = thread; } - pthread_mutex_unlock(&thread->mtx); if (t_ptr) { *t_ptr = thread; @@ -806,7 +887,6 @@ struct thread *funcname_thread_add_read_write(int dir, struct thread_master *m, AWAKEN(m); } - pthread_mutex_unlock(&m->mtx); return thread; } @@ -818,41 +898,32 @@ funcname_thread_add_timer_timeval(struct thread_master *m, struct thread **t_ptr, debugargdef) { struct thread *thread; - struct pqueue *queue; assert(m != NULL); assert(type == THREAD_TIMER); assert(time_relative); - pthread_mutex_lock(&m->mtx); - { - if (t_ptr - && *t_ptr) // thread is already scheduled; don't reschedule - { - pthread_mutex_unlock(&m->mtx); + frr_with_mutex(&m->mtx) { + if (t_ptr && *t_ptr) + /* thread is already scheduled; don't reschedule */ return NULL; - } - queue = m->timer; thread = thread_get(m, type, func, arg, debugargpass); - pthread_mutex_lock(&thread->mtx); - { + frr_with_mutex(&thread->mtx) { monotime(&thread->u.sands); timeradd(&thread->u.sands, time_relative, &thread->u.sands); - pqueue_enqueue(thread, queue); + thread_timer_list_add(&m->timer, thread); if (t_ptr) { *t_ptr = thread; thread->ref = t_ptr; } } - pthread_mutex_unlock(&thread->mtx); AWAKEN(m); } - pthread_mutex_unlock(&m->mtx); return thread; } @@ -909,26 +980,20 @@ struct thread *funcname_thread_add_event(struct thread_master *m, void *arg, int val, struct thread **t_ptr, debugargdef) { - struct thread *thread; + struct thread *thread = NULL; assert(m != NULL); - pthread_mutex_lock(&m->mtx); - { - if (t_ptr - && *t_ptr) // thread is already scheduled; don't reschedule - { - pthread_mutex_unlock(&m->mtx); - return NULL; - } + frr_with_mutex(&m->mtx) { + if (t_ptr && *t_ptr) + /* thread is already scheduled; don't reschedule */ + break; thread = thread_get(m, THREAD_EVENT, func, arg, debugargpass); - pthread_mutex_lock(&thread->mtx); - { + frr_with_mutex(&thread->mtx) { thread->u.val = val; thread_list_add_tail(&m->event, thread); } - pthread_mutex_unlock(&thread->mtx); if (t_ptr) { *t_ptr = thread; @@ -937,7 +1002,6 @@ struct thread *funcname_thread_add_event(struct thread_master *m, AWAKEN(m); } - pthread_mutex_unlock(&m->mtx); return thread; } @@ -991,6 +1055,8 @@ static void thread_cancel_rw(struct thread_master *master, int fd, short state) (master->handler.pfdcount - i - 1) * sizeof(struct pollfd)); master->handler.pfdcount--; + master->handler.pfds[master->handler.pfdcount].fd = 0; + master->handler.pfds[master->handler.pfdcount].events = 0; } /* If we have the same pollfd in the copy, perform the same operations, @@ -1005,6 +1071,8 @@ static void thread_cancel_rw(struct thread_master *master, int fd, short state) (master->handler.copycount - i - 1) * sizeof(struct pollfd)); master->handler.copycount--; + master->handler.copy[master->handler.copycount].fd = 0; + master->handler.copy[master->handler.copycount].events = 0; } } @@ -1019,22 +1087,22 @@ static void thread_cancel_rw(struct thread_master *master, int fd, short state) static void do_thread_cancel(struct thread_master *master) { struct thread_list_head *list = NULL; - struct pqueue *queue = NULL; struct thread **thread_array = NULL; struct thread *thread; struct cancel_req *cr; struct listnode *ln; for (ALL_LIST_ELEMENTS_RO(master->cancel_req, ln, cr)) { - /* If this is an event object cancellation, linear search - * through event - * list deleting any events which have the specified argument. - * We also - * need to check every thread in the ready queue. */ + /* + * If this is an event object cancellation, linear search + * through event list deleting any events which have the + * specified argument. We also need to check every thread + * in the ready queue. + */ if (cr->eventobj) { struct thread *t; - for_each_safe(thread_list, &master->event, t) { + frr_each_safe(thread_list, &master->event, t) { if (t->arg != cr->eventobj) continue; thread_list_del(&master->event, t); @@ -1043,7 +1111,7 @@ static void do_thread_cancel(struct thread_master *master) thread_add_unuse(master, t); } - for_each_safe(thread_list, &master->ready, t) { + frr_each_safe(thread_list, &master->ready, t) { if (t->arg != cr->eventobj) continue; thread_list_del(&master->ready, t); @@ -1054,11 +1122,12 @@ static void do_thread_cancel(struct thread_master *master) continue; } - /* The pointer varies depending on whether the cancellation - * request was - * made asynchronously or not. If it was, we need to check - * whether the - * thread even exists anymore before cancelling it. */ + /* + * The pointer varies depending on whether the cancellation + * request was made asynchronously or not. If it was, we + * need to check whether the thread even exists anymore + * before cancelling it. + */ thread = (cr->thread) ? cr->thread : *cr->threadref; if (!thread) @@ -1075,7 +1144,7 @@ static void do_thread_cancel(struct thread_master *master) thread_array = master->write; break; case THREAD_TIMER: - queue = master->timer; + thread_timer_list_del(&master->timer, thread); break; case THREAD_EVENT: list = &master->event; @@ -1088,16 +1157,10 @@ static void do_thread_cancel(struct thread_master *master) break; } - if (queue) { - assert(thread->index >= 0); - assert(thread == queue->array[thread->index]); - pqueue_remove_at(thread->index, queue); - } else if (list) { + if (list) { thread_list_del(list, thread); } else if (thread_array) { thread_array[thread->u.fd] = NULL; - } else { - assert(!"Thread should be either in queue or list or array!"); } if (thread->ref) @@ -1107,7 +1170,8 @@ static void do_thread_cancel(struct thread_master *master) } /* Delete and free all cancellation requests */ - list_delete_all_node(master->cancel_req); + if (master->cancel_req) + list_delete_all_node(master->cancel_req); /* Wake up any threads which may be blocked in thread_cancel_async() */ master->canceled = true; @@ -1126,15 +1190,13 @@ void thread_cancel_event(struct thread_master *master, void *arg) { assert(master->owner == pthread_self()); - pthread_mutex_lock(&master->mtx); - { + frr_with_mutex(&master->mtx) { struct cancel_req *cr = XCALLOC(MTYPE_TMP, sizeof(struct cancel_req)); cr->eventobj = arg; listnode_add(master->cancel_req, cr); do_thread_cancel(master); } - pthread_mutex_unlock(&master->mtx); } /** @@ -1150,15 +1212,13 @@ void thread_cancel(struct thread *thread) assert(master->owner == pthread_self()); - pthread_mutex_lock(&master->mtx); - { + frr_with_mutex(&master->mtx) { struct cancel_req *cr = XCALLOC(MTYPE_TMP, sizeof(struct cancel_req)); cr->thread = thread; listnode_add(master->cancel_req, cr); do_thread_cancel(master); } - pthread_mutex_unlock(&master->mtx); } /** @@ -1191,8 +1251,7 @@ void thread_cancel_async(struct thread_master *master, struct thread **thread, assert(!(thread && eventobj) && (thread || eventobj)); assert(master->owner != pthread_self()); - pthread_mutex_lock(&master->mtx); - { + frr_with_mutex(&master->mtx) { master->canceled = false; if (thread) { @@ -1211,19 +1270,18 @@ void thread_cancel_async(struct thread_master *master, struct thread **thread, while (!master->canceled) pthread_cond_wait(&master->cancel_cond, &master->mtx); } - pthread_mutex_unlock(&master->mtx); } /* ------------------------------------------------------------------------- */ -static struct timeval *thread_timer_wait(struct pqueue *queue, +static struct timeval *thread_timer_wait(struct thread_timer_list_head *timers, struct timeval *timer_val) { - if (queue->size) { - struct thread *next_timer = queue->array[0]; - monotime_until(&next_timer->u.sands, timer_val); - return timer_val; - } - return NULL; + if (!thread_timer_list_count(timers)) + return NULL; + + struct thread *next_timer = thread_timer_list_first(timers); + monotime_until(&next_timer->u.sands, timer_val); + return timer_val; } static struct thread *thread_run(struct thread_master *m, struct thread *thread, @@ -1235,12 +1293,31 @@ static struct thread *thread_run(struct thread_master *m, struct thread *thread, } static int thread_process_io_helper(struct thread_master *m, - struct thread *thread, short state, int pos) + struct thread *thread, short state, + short actual_state, int pos) { struct thread **thread_array; - if (!thread) + /* + * poll() clears the .events field, but the pollfd array we + * pass to poll() is a copy of the one used to schedule threads. + * We need to synchronize state between the two here by applying + * the same changes poll() made on the copy of the "real" pollfd + * array. + * + * This cleans up a possible infinite loop where we refuse + * to respond to a poll event but poll is insistent that + * we should. + */ + m->handler.pfds[pos].events &= ~(state); + + if (!thread) { + if ((actual_state & (POLLHUP|POLLIN)) != POLLHUP) + flog_err(EC_LIB_NO_THREAD, + "Attempting to process an I/O event but for fd: %d(%d) no thread to handle this!\n", + m->handler.pfds[pos].fd, actual_state); return 0; + } if (thread->type == THREAD_READ) thread_array = m->read; @@ -1250,9 +1327,7 @@ static int thread_process_io_helper(struct thread_master *m, thread_array[thread->u.fd] = NULL; thread_list_add_tail(&m->ready, thread); thread->type = THREAD_READY; - /* if another pthread scheduled this file descriptor for the event we're - * responding to, no problem; we're getting to it now */ - thread->master->handler.pfds[pos].events &= ~(state); + return 1; } @@ -1277,23 +1352,27 @@ static void thread_process_io(struct thread_master *m, unsigned int num) ready++; - /* Unless someone has called thread_cancel from another pthread, - * the only - * thing that could have changed in m->handler.pfds while we - * were - * asleep is the .events field in a given pollfd. Barring - * thread_cancel() - * that value should be a superset of the values we have in our - * copy, so - * there's no need to update it. Similarily, barring deletion, - * the fd - * should still be a valid index into the master's pfds. */ - if (pfds[i].revents & (POLLIN | POLLHUP)) + /* + * Unless someone has called thread_cancel from another + * pthread, the only thing that could have changed in + * m->handler.pfds while we were asleep is the .events + * field in a given pollfd. Barring thread_cancel() that + * value should be a superset of the values we have in our + * copy, so there's no need to update it. Similarily, + * barring deletion, the fd should still be a valid index + * into the master's pfds. + * + * We are including POLLERR here to do a READ event + * this is because the read should fail and the + * read function should handle it appropriately + */ + if (pfds[i].revents & (POLLIN | POLLHUP | POLLERR)) { thread_process_io_helper(m, m->read[pfds[i].fd], POLLIN, - i); + pfds[i].revents, i); + } if (pfds[i].revents & POLLOUT) thread_process_io_helper(m, m->write[pfds[i].fd], - POLLOUT, i); + POLLOUT, pfds[i].revents, i); /* if one of our file descriptors is garbage, remove the same * from @@ -1303,11 +1382,15 @@ static void thread_process_io(struct thread_master *m, unsigned int num) (m->handler.pfdcount - i - 1) * sizeof(struct pollfd)); m->handler.pfdcount--; + m->handler.pfds[m->handler.pfdcount].fd = 0; + m->handler.pfds[m->handler.pfdcount].events = 0; memmove(pfds + i, pfds + i + 1, (m->handler.copycount - i - 1) * sizeof(struct pollfd)); m->handler.copycount--; + m->handler.copy[m->handler.copycount].fd = 0; + m->handler.copy[m->handler.copycount].events = 0; i--; } @@ -1315,17 +1398,16 @@ static void thread_process_io(struct thread_master *m, unsigned int num) } /* Add all timers that have popped to the ready list. */ -static unsigned int thread_process_timers(struct pqueue *queue, +static unsigned int thread_process_timers(struct thread_timer_list_head *timers, struct timeval *timenow) { struct thread *thread; unsigned int ready = 0; - while (queue->size) { - thread = queue->array[0]; + while ((thread = thread_timer_list_first(timers))) { if (timercmp(timenow, &thread->u.sands, <)) return ready; - pqueue_dequeue(queue); + thread_timer_list_pop(timers); thread->type = THREAD_READY; thread_list_add_tail(&thread->master->ready, thread); ready++; @@ -1356,7 +1438,7 @@ struct thread *thread_fetch(struct thread_master *m, struct thread *fetch) struct timeval zerotime = {0, 0}; struct timeval tv; struct timeval *tw = NULL; - + bool eintr_p = false; int num = 0; do { @@ -1396,18 +1478,17 @@ struct thread *thread_fetch(struct thread_master *m, struct thread *fetch) * * - If there are events pending, set the poll() timeout to zero * - If there are no events pending, but there are timers - * pending, set the - * timeout to the smallest remaining time on any timer + * pending, set the timeout to the smallest remaining time on + * any timer. * - If there are neither timers nor events pending, but there - * are file - * descriptors pending, block indefinitely in poll() + * are file descriptors pending, block indefinitely in poll() * - If nothing is pending, it's time for the application to die * * In every case except the last, we need to hit poll() at least * once per loop to avoid starvation by events */ if (!thread_list_count(&m->ready)) - tw = thread_timer_wait(m->timer, &tv); + tw = thread_timer_wait(&m->timer, &tv); if (thread_list_count(&m->ready) || (tw && !timercmp(tw, &zerotime, >))) @@ -1429,14 +1510,14 @@ struct thread *thread_fetch(struct thread_master *m, struct thread *fetch) pthread_mutex_unlock(&m->mtx); { - num = fd_poll(m, m->handler.copy, m->handler.pfdsize, - m->handler.copycount, tw); + eintr_p = false; + num = fd_poll(m, tw, &eintr_p); } pthread_mutex_lock(&m->mtx); /* Handle any errors received in poll() */ if (num < 0) { - if (errno == EINTR) { + if (eintr_p) { pthread_mutex_unlock(&m->mtx); /* loop around to signal handler */ continue; @@ -1452,7 +1533,7 @@ struct thread *thread_fetch(struct thread_master *m, struct thread *fetch) /* Post timers to ready queue. */ monotime(&now); - thread_process_timers(m->timer, &now); + thread_process_timers(&m->timer, &now); /* Post I/O to ready queue. */ if (num > 0) @@ -1493,22 +1574,18 @@ unsigned long thread_consumed_time(RUSAGE_T *now, RUSAGE_T *start, int thread_should_yield(struct thread *thread) { int result; - pthread_mutex_lock(&thread->mtx); - { + frr_with_mutex(&thread->mtx) { result = monotime_since(&thread->real, NULL) > (int64_t)thread->yield; } - pthread_mutex_unlock(&thread->mtx); return result; } void thread_set_yield_time(struct thread *thread, unsigned long yield_time) { - pthread_mutex_lock(&thread->mtx); - { + frr_with_mutex(&thread->mtx) { thread->yield = yield_time; } - pthread_mutex_unlock(&thread->mtx); } void thread_getrusage(RUSAGE_T *r) @@ -1519,7 +1596,9 @@ void thread_getrusage(RUSAGE_T *r) #define FRR_RUSAGE RUSAGE_SELF #endif monotime(&r->real); +#ifndef EXCLUDE_CPU_TIME getrusage(FRR_RUSAGE, &(r->cpu)); +#endif } /* @@ -1535,9 +1614,11 @@ void thread_getrusage(RUSAGE_T *r) */ void thread_call(struct thread *thread) { +#ifndef EXCLUDE_CPU_TIME _Atomic unsigned long realtime, cputime; unsigned long exp; unsigned long helper; +#endif RUSAGE_T before, after; GETRUSAGE(&before); @@ -1549,6 +1630,7 @@ void thread_call(struct thread *thread) GETRUSAGE(&after); +#ifndef EXCLUDE_CPU_TIME realtime = thread_consumed_time(&after, &before, &helper); cputime = helper; @@ -1593,6 +1675,7 @@ void thread_call(struct thread *thread) realtime / 1000, cputime / 1000); } #endif /* CONSUMED_TIME_CHECK */ +#endif /* Exclude CPU Time */ } /* Execute thread */ @@ -1603,20 +1686,16 @@ void funcname_thread_execute(struct thread_master *m, struct thread *thread; /* Get or allocate new thread to execute. */ - pthread_mutex_lock(&m->mtx); - { + frr_with_mutex(&m->mtx) { thread = thread_get(m, THREAD_EVENT, func, arg, debugargpass); /* Set its event value. */ - pthread_mutex_lock(&thread->mtx); - { + frr_with_mutex(&thread->mtx) { thread->add_type = THREAD_EXECUTE; thread->u.val = val; thread->ref = &thread; } - pthread_mutex_unlock(&thread->mtx); } - pthread_mutex_unlock(&m->mtx); /* Execute thread doing all accounting. */ thread_call(thread); @@ -1624,3 +1703,49 @@ void funcname_thread_execute(struct thread_master *m, /* Give back or free thread. */ thread_add_unuse(m, thread); } + +/* Debug signal mask - if 'sigs' is NULL, use current effective mask. */ +void debug_signals(const sigset_t *sigs) +{ + int i, found; + sigset_t tmpsigs; + char buf[300]; + + /* + * We're only looking at the non-realtime signals here, so we need + * some limit value. Platform differences mean at some point we just + * need to pick a reasonable value. + */ +#if defined SIGRTMIN +# define LAST_SIGNAL SIGRTMIN +#else +# define LAST_SIGNAL 32 +#endif + + + if (sigs == NULL) { + sigemptyset(&tmpsigs); + pthread_sigmask(SIG_BLOCK, NULL, &tmpsigs); + sigs = &tmpsigs; + } + + found = 0; + buf[0] = '\0'; + + for (i = 0; i < LAST_SIGNAL; i++) { + char tmp[20]; + + if (sigismember(sigs, i) > 0) { + if (found > 0) + strlcat(buf, ",", sizeof(buf)); + snprintf(tmp, sizeof(tmp), "%d", i); + strlcat(buf, tmp, sizeof(buf)); + found++; + } + } + + if (found == 0) + snprintf(buf, sizeof(buf), ""); + + zlog_debug("%s: %s", __func__, buf); +} diff --git a/lib/thread.h b/lib/thread.h index 7897265120..55ddac4059 100644 --- a/lib/thread.h +++ b/lib/thread.h @@ -41,8 +41,7 @@ struct rusage_t { #define GETRUSAGE(X) thread_getrusage(X) PREDECL_LIST(thread_list) - -struct pqueue; +PREDECL_HEAP(thread_timer_list) struct fd_handler { /* number of pfd that fit in the allocated space of pfds. This is a @@ -73,7 +72,7 @@ struct thread_master { struct thread **read; struct thread **write; - struct pqueue *timer; + struct thread_timer_list_head timer; struct thread_list_head event, ready, unuse; struct list *cancel_req; bool canceled; @@ -95,6 +94,7 @@ struct thread { uint8_t type; /* thread type */ uint8_t add_type; /* thread type */ struct thread_list_item threaditem; + struct thread_timer_list_item timeritem; struct thread **ref; /* external reference (if given) */ struct thread_master *master; /* pointer to the struct thread_master */ int (*func)(struct thread *); /* event function */ @@ -104,7 +104,6 @@ struct thread { int fd; /* file descriptor in case of r/w */ struct timeval sands; /* rest of time sands value. */ } u; - int index; /* queue position for timers */ struct timeval real; struct cpu_thread_history *hist; /* cache pointer to cpu_history */ unsigned long yield; /* yield time in microseconds */ @@ -141,6 +140,8 @@ struct cpu_thread_history { /* Thread yield time. */ #define THREAD_YIELD_TIME_SLOT 10 * 1000L /* 10ms */ +#define THREAD_TIMER_STRLEN 12 + /* Macros. */ #define THREAD_ARG(X) ((X)->arg) #define THREAD_FD(X) ((X)->u.fd) @@ -229,6 +230,11 @@ extern unsigned long thread_consumed_time(RUSAGE_T *after, RUSAGE_T *before, /* only for use in logging functions! */ extern pthread_key_t thread_current; +extern char *thread_timer_to_hhmmss(char *buf, int buf_size, + struct thread *t_timer); + +/* Debug signal mask */ +void debug_signals(const sigset_t *sigs); #ifdef __cplusplus } diff --git a/lib/typerb.c b/lib/typerb.c index d361e7651e..092faa4cc9 100644 --- a/lib/typerb.c +++ b/lib/typerb.c @@ -41,6 +41,10 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include "typerb.h" #define RB_BLACK 0 @@ -329,9 +333,10 @@ rbe_remove(struct rbt_tree *rbt, struct rb_entry *rbe) return (old); } -void typed_rb_remove(struct rbt_tree *rbt, struct rb_entry *rbe) +struct typed_rb_entry *typed_rb_remove(struct rbt_tree *rbt, + struct rb_entry *rbe) { - rbe_remove(rbt, rbe); + return rbe_remove(rbt, rbe); } struct typed_rb_entry *typed_rb_insert(struct rbt_tree *rbt, @@ -372,12 +377,13 @@ struct typed_rb_entry *typed_rb_insert(struct rbt_tree *rbt, } /* Finds the node with the same key as elm */ -struct rb_entry *typed_rb_find(struct rbt_tree *rbt, const struct rb_entry *key, +const struct rb_entry *typed_rb_find(const struct rbt_tree *rbt, + const struct rb_entry *key, int (*cmpfn)( const struct typed_rb_entry *a, const struct typed_rb_entry *b)) { - struct rb_entry *tmp = RBH_ROOT(rbt); + const struct rb_entry *tmp = RBH_ROOT(rbt); int comp; while (tmp != NULL) { @@ -390,16 +396,16 @@ struct rb_entry *typed_rb_find(struct rbt_tree *rbt, const struct rb_entry *key, return tmp; } - return (NULL); + return NULL; } -struct rb_entry *typed_rb_find_gteq(struct rbt_tree *rbt, +const struct rb_entry *typed_rb_find_gteq(const struct rbt_tree *rbt, const struct rb_entry *key, int (*cmpfn)( const struct typed_rb_entry *a, const struct typed_rb_entry *b)) { - struct rb_entry *tmp = RBH_ROOT(rbt), *best = NULL; + const struct rb_entry *tmp = RBH_ROOT(rbt), *best = NULL; int comp; while (tmp != NULL) { @@ -416,13 +422,13 @@ struct rb_entry *typed_rb_find_gteq(struct rbt_tree *rbt, return best; } -struct rb_entry *typed_rb_find_lt(struct rbt_tree *rbt, +const struct rb_entry *typed_rb_find_lt(const struct rbt_tree *rbt, const struct rb_entry *key, int (*cmpfn)( const struct typed_rb_entry *a, const struct typed_rb_entry *b)) { - struct rb_entry *tmp = RBH_ROOT(rbt), *best = NULL; + const struct rb_entry *tmp = RBH_ROOT(rbt), *best = NULL; int comp; while (tmp != NULL) { @@ -438,8 +444,10 @@ struct rb_entry *typed_rb_find_lt(struct rbt_tree *rbt, return best; } -struct rb_entry *typed_rb_next(struct rb_entry *rbe) +struct rb_entry *typed_rb_next(const struct rb_entry *rbe_const) { + struct rb_entry *rbe = (struct rb_entry *)rbe_const; + if (RBE_RIGHT(rbe) != NULL) { rbe = RBE_RIGHT(rbe); while (RBE_LEFT(rbe) != NULL) @@ -458,7 +466,7 @@ struct rb_entry *typed_rb_next(struct rb_entry *rbe) return rbe; } -struct rb_entry *typed_rb_min(struct rbt_tree *rbt) +struct rb_entry *typed_rb_min(const struct rbt_tree *rbt) { struct rb_entry *rbe = RBH_ROOT(rbt); struct rb_entry *parent = NULL; diff --git a/lib/typerb.h b/lib/typerb.h index 3d8db06fe0..fca45e20d1 100644 --- a/lib/typerb.h +++ b/lib/typerb.h @@ -22,6 +22,10 @@ #include "typesafe.h" +#ifdef __cplusplus +extern "C" { +#endif + struct typed_rb_entry { struct typed_rb_entry *rbt_parent; struct typed_rb_entry *rbt_left; @@ -34,29 +38,30 @@ struct typed_rb_root { size_t count; }; -struct typed_rb_entry *typed_rb_insert(struct typed_rb_root *, +struct typed_rb_entry *typed_rb_insert(struct typed_rb_root *rbt, struct typed_rb_entry *rbe, int (*cmpfn)( const struct typed_rb_entry *a, const struct typed_rb_entry *b)); -void typed_rb_remove(struct typed_rb_root *, struct typed_rb_entry *rbe); -struct typed_rb_entry *typed_rb_find(struct typed_rb_root *, +struct typed_rb_entry *typed_rb_remove(struct typed_rb_root *rbt, + struct typed_rb_entry *rbe); +const struct typed_rb_entry *typed_rb_find(const struct typed_rb_root *rbt, const struct typed_rb_entry *rbe, int (*cmpfn)( const struct typed_rb_entry *a, const struct typed_rb_entry *b)); -struct typed_rb_entry *typed_rb_find_gteq(struct typed_rb_root *, +const struct typed_rb_entry *typed_rb_find_gteq(const struct typed_rb_root *rbt, const struct typed_rb_entry *rbe, int (*cmpfn)( const struct typed_rb_entry *a, const struct typed_rb_entry *b)); -struct typed_rb_entry *typed_rb_find_lt(struct typed_rb_root *, +const struct typed_rb_entry *typed_rb_find_lt(const struct typed_rb_root *rbt, const struct typed_rb_entry *rbe, int (*cmpfn)( const struct typed_rb_entry *a, const struct typed_rb_entry *b)); -struct typed_rb_entry *typed_rb_min(struct typed_rb_root *); -struct typed_rb_entry *typed_rb_next(struct typed_rb_entry *); +struct typed_rb_entry *typed_rb_min(const struct typed_rb_root *rbt); +struct typed_rb_entry *typed_rb_next(const struct typed_rb_entry *rbe); #define _PREDECL_RBTREE(prefix) \ struct prefix ## _head { struct typed_rb_root rr; }; \ @@ -81,23 +86,26 @@ macro_inline type *prefix ## _add(struct prefix##_head *h, type *item) \ re = typed_rb_insert(&h->rr, &item->field.re, cmpfn_uq); \ return container_of_null(re, type, field.re); \ } \ -macro_inline type *prefix ## _find_gteq(struct prefix##_head *h, \ - const type *item) \ +macro_inline const type *prefix ## _const_find_gteq( \ + const struct prefix##_head *h, const type *item) \ { \ - struct typed_rb_entry *re; \ + const struct typed_rb_entry *re; \ re = typed_rb_find_gteq(&h->rr, &item->field.re, cmpfn_nuq); \ return container_of_null(re, type, field.re); \ } \ -macro_inline type *prefix ## _find_lt(struct prefix##_head *h, \ - const type *item) \ +macro_inline const type *prefix ## _const_find_lt( \ + const struct prefix##_head *h, const type *item) \ { \ - struct typed_rb_entry *re; \ + const struct typed_rb_entry *re; \ re = typed_rb_find_lt(&h->rr, &item->field.re, cmpfn_nuq); \ return container_of_null(re, type, field.re); \ } \ -macro_inline void prefix ## _del(struct prefix##_head *h, type *item) \ +TYPESAFE_FIND_CMP(prefix, type) \ +macro_inline type *prefix ## _del(struct prefix##_head *h, type *item) \ { \ - typed_rb_remove(&h->rr, &item->field.re); \ + struct typed_rb_entry *re; \ + re = typed_rb_remove(&h->rr, &item->field.re); \ + return container_of_null(re, type, field.re); \ } \ macro_inline type *prefix ## _pop(struct prefix##_head *h) \ { \ @@ -108,25 +116,27 @@ macro_inline type *prefix ## _pop(struct prefix##_head *h) \ typed_rb_remove(&h->rr, re); \ return container_of(re, type, field.re); \ } \ -macro_pure type *prefix ## _first(struct prefix##_head *h) \ +macro_pure const type *prefix ## _const_first(const struct prefix##_head *h) \ { \ - struct typed_rb_entry *re; \ + const struct typed_rb_entry *re; \ re = typed_rb_min(&h->rr); \ return container_of_null(re, type, field.re); \ } \ -macro_pure type *prefix ## _next(struct prefix##_head *h, type *item) \ +macro_pure const type *prefix ## _const_next(const struct prefix##_head *h, \ + const type *item) \ { \ - struct typed_rb_entry *re; \ + const struct typed_rb_entry *re; \ re = typed_rb_next(&item->field.re); \ return container_of_null(re, type, field.re); \ } \ +TYPESAFE_FIRST_NEXT(prefix, type) \ macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \ { \ struct typed_rb_entry *re; \ re = item ? typed_rb_next(&item->field.re) : NULL; \ return container_of_null(re, type, field.re); \ } \ -macro_pure size_t prefix ## _count(struct prefix##_head *h) \ +macro_pure size_t prefix ## _count(const struct prefix##_head *h) \ { \ return h->rr.count; \ } \ @@ -142,12 +152,14 @@ macro_inline int prefix ## __cmp(const struct typed_rb_entry *a, \ return cmpfn(container_of(a, type, field.re), \ container_of(b, type, field.re)); \ } \ -macro_inline type *prefix ## _find(struct prefix##_head *h, const type *item) \ +macro_inline const type *prefix ## _const_find(const struct prefix##_head *h, \ + const type *item) \ { \ - struct typed_rb_entry *re; \ + const struct typed_rb_entry *re; \ re = typed_rb_find(&h->rr, &item->field.re, &prefix ## __cmp); \ return container_of_null(re, type, field.re); \ } \ +TYPESAFE_FIND(prefix, type) \ \ _DECLARE_RBTREE(prefix, type, field, prefix ## __cmp, prefix ## __cmp) \ /* ... */ @@ -179,4 +191,8 @@ macro_inline int prefix ## __cmp_uq(const struct typed_rb_entry *a, \ _DECLARE_RBTREE(prefix, type, field, prefix ## __cmp, prefix ## __cmp_uq) \ /* ... */ +#ifdef __cplusplus +} +#endif + #endif /* _FRR_TYPERB_H */ diff --git a/lib/typesafe.c b/lib/typesafe.c index bd269e9b54..69796e2d81 100644 --- a/lib/typesafe.c +++ b/lib/typesafe.c @@ -14,14 +14,20 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include #include #include "typesafe.h" #include "memory.h" +#include "network.h" DEFINE_MTYPE_STATIC(LIB, TYPEDHASH_BUCKET, "Typed-hash bucket") DEFINE_MTYPE_STATIC(LIB, SKIPLIST_OFLOW, "Skiplist overflow") +DEFINE_MTYPE_STATIC(LIB, HEAP_ARRAY, "Typed-heap array") #if 0 static void hash_consistency_check(struct thash_head *head) @@ -152,7 +158,7 @@ void typesafe_hash_shrink(struct thash_head *head) /* skiplist */ -static inline struct sskip_item *sl_level_get(struct sskip_item *item, +static inline struct sskip_item *sl_level_get(const struct sskip_item *item, size_t level) { if (level < SKIPLIST_OVERFLOW) @@ -191,7 +197,7 @@ struct sskip_item *typesafe_skiplist_add(struct sskip_head *head, int cmpval; /* level / newlevel are 1-counted here */ - newlevel = __builtin_ctz(random()) + 1; + newlevel = __builtin_ctz(frr_weak_random()) + 1; if (newlevel > SKIPLIST_MAXDEPTH) newlevel = SKIPLIST_MAXDEPTH; @@ -257,13 +263,14 @@ struct sskip_item *typesafe_skiplist_add(struct sskip_head *head, /* NOTE: level counting below is 1-based since that makes the code simpler! */ -struct sskip_item *typesafe_skiplist_find(struct sskip_head *head, +const struct sskip_item *typesafe_skiplist_find( + const struct sskip_head *head, const struct sskip_item *item, int (*cmpfn)( const struct sskip_item *a, const struct sskip_item *b)) { size_t level = SKIPLIST_MAXDEPTH; - struct sskip_item *prev = &head->hitem, *next; + const struct sskip_item *prev = &head->hitem, *next; int cmpval; while (level) { @@ -284,13 +291,14 @@ struct sskip_item *typesafe_skiplist_find(struct sskip_head *head, return NULL; } -struct sskip_item *typesafe_skiplist_find_gteq(struct sskip_head *head, +const struct sskip_item *typesafe_skiplist_find_gteq( + const struct sskip_head *head, const struct sskip_item *item, int (*cmpfn)( const struct sskip_item *a, const struct sskip_item *b)) { size_t level = SKIPLIST_MAXDEPTH; - struct sskip_item *prev = &head->hitem, *next; + const struct sskip_item *prev = &head->hitem, *next; int cmpval; while (level) { @@ -311,13 +319,14 @@ struct sskip_item *typesafe_skiplist_find_gteq(struct sskip_head *head, return next; } -struct sskip_item *typesafe_skiplist_find_lt(struct sskip_head *head, +const struct sskip_item *typesafe_skiplist_find_lt( + const struct sskip_head *head, const struct sskip_item *item, int (*cmpfn)( const struct sskip_item *a, const struct sskip_item *b)) { size_t level = SKIPLIST_MAXDEPTH; - struct sskip_item *prev = &head->hitem, *next, *best = NULL; + const struct sskip_item *prev = &head->hitem, *next, *best = NULL; int cmpval; while (level) { @@ -336,13 +345,14 @@ struct sskip_item *typesafe_skiplist_find_lt(struct sskip_head *head, return best; } -void typesafe_skiplist_del(struct sskip_head *head, struct sskip_item *item, - int (*cmpfn)(const struct sskip_item *a, - const struct sskip_item *b)) +struct sskip_item *typesafe_skiplist_del( + struct sskip_head *head, struct sskip_item *item, + int (*cmpfn)(const struct sskip_item *a, const struct sskip_item *b)) { size_t level = SKIPLIST_MAXDEPTH; struct sskip_item *prev = &head->hitem, *next; int cmpval; + bool found = false; while (level) { next = sl_level_get(prev, level - 1); @@ -354,6 +364,7 @@ void typesafe_skiplist_del(struct sskip_head *head, struct sskip_item *item, sl_level_set(prev, level - 1, sl_level_get(item, level - 1)); level--; + found = true; continue; } cmpval = cmpfn(next, item); @@ -364,6 +375,9 @@ void typesafe_skiplist_del(struct sskip_head *head, struct sskip_item *item, level--; } + if (!found) + return NULL; + /* TBD: assert when trying to remove non-existing item? */ head->count--; @@ -374,4 +388,183 @@ void typesafe_skiplist_del(struct sskip_head *head, struct sskip_item *item, XFREE(MTYPE_SKIPLIST_OFLOW, oflow); } memset(item, 0, sizeof(*item)); + + return item; +} + +struct sskip_item *typesafe_skiplist_pop(struct sskip_head *head) +{ + size_t level = SKIPLIST_MAXDEPTH; + struct sskip_item *prev = &head->hitem, *next, *item; + + item = sl_level_get(prev, 0); + if (!item) + return NULL; + + do { + level--; + + next = sl_level_get(prev, level); + if (next != item) + continue; + + sl_level_set(prev, level, sl_level_get(item, level)); + } while (level); + + head->count--; + + if ((uintptr_t)item->next[SKIPLIST_OVERFLOW] & 1) { + uintptr_t ptrval = (uintptr_t)item->next[SKIPLIST_OVERFLOW]; + ptrval &= UINTPTR_MAX - 3; + struct sskip_overflow *oflow = (struct sskip_overflow *)ptrval; + XFREE(MTYPE_SKIPLIST_OFLOW, oflow); + } + memset(item, 0, sizeof(*item)); + + return item; +} + +/* heap */ + +#if 0 +static void heap_consistency_check(struct heap_head *head, + int (*cmpfn)(const struct heap_item *a, + const struct heap_item *b), + uint32_t pos) +{ + uint32_t rghtpos = pos + 1; + uint32_t downpos = HEAP_NARY * (pos + 1); + + if (pos + 1 > ~0U / HEAP_NARY) + downpos = ~0U; + + if ((pos & (HEAP_NARY - 1)) != HEAP_NARY - 1 && rghtpos < head->count) { + assert(cmpfn(head->array[rghtpos], head->array[pos]) >= 0); + heap_consistency_check(head, cmpfn, rghtpos); + } + if (downpos < head->count) { + assert(cmpfn(head->array[downpos], head->array[pos]) >= 0); + heap_consistency_check(head, cmpfn, downpos); + } +} +#else +#define heap_consistency_check(head, cmpfn, pos) +#endif + +void typesafe_heap_resize(struct heap_head *head, bool grow) +{ + uint32_t newsize; + + if (grow) { + newsize = head->arraysz; + if (newsize <= 36) + newsize = 72; + else if (newsize < 262144) + newsize += newsize / 2; + else if (newsize < 0xaaaa0000) + newsize += newsize / 3; + else + assert(!newsize); + } else if (head->count > 0) { + newsize = head->count; + } else { + XFREE(MTYPE_HEAP_ARRAY, head->array); + head->arraysz = 0; + return; + } + + newsize += HEAP_NARY - 1; + newsize &= ~(HEAP_NARY - 1); + if (newsize == head->arraysz) + return; + + head->array = XREALLOC(MTYPE_HEAP_ARRAY, head->array, + newsize * sizeof(struct heap_item *)); + head->arraysz = newsize; +} + +void typesafe_heap_pushdown(struct heap_head *head, uint32_t pos, + struct heap_item *item, + int (*cmpfn)(const struct heap_item *a, + const struct heap_item *b)) +{ + uint32_t rghtpos, downpos, moveto; + + while (1) { + /* rghtpos: neighbor to the "right", inside block of NARY. + * may be invalid if last in block, check nary_last() + * downpos: first neighbor in the "downwards" block further + * away from the root + */ + rghtpos = pos + 1; + + /* make sure we can use the full 4G items */ + downpos = HEAP_NARY * (pos + 1); + if (pos + 1 > ~0U / HEAP_NARY) + /* multiplication overflowed. ~0U is guaranteed + * to be an invalid index; size limit is enforced in + * resize() + */ + downpos = ~0U; + + /* only used on break */ + moveto = pos; + +#define nary_last(x) (((x) & (HEAP_NARY - 1)) == HEAP_NARY - 1) + if (downpos >= head->count + || cmpfn(head->array[downpos], item) >= 0) { + /* not moving down; either at end or down is >= item */ + if (nary_last(pos) || rghtpos >= head->count + || cmpfn(head->array[rghtpos], item) >= 0) + /* not moving right either - got our spot */ + break; + + moveto = rghtpos; + + /* else: downpos is valid and < item. choose between down + * or right (if the latter is an option) */ + } else if (nary_last(pos) || cmpfn(head->array[rghtpos], + head->array[downpos]) >= 0) + moveto = downpos; + else + moveto = rghtpos; +#undef nary_last + + head->array[pos] = head->array[moveto]; + head->array[pos]->index = pos; + pos = moveto; + } + + head->array[moveto] = item; + item->index = moveto; + + heap_consistency_check(head, cmpfn, 0); +} + +void typesafe_heap_pullup(struct heap_head *head, uint32_t pos, + struct heap_item *item, + int (*cmpfn)(const struct heap_item *a, + const struct heap_item *b)) +{ + uint32_t moveto; + + while (pos != 0) { + if ((pos & (HEAP_NARY - 1)) == 0) + moveto = pos / HEAP_NARY - 1; + else + moveto = pos - 1; + + if (cmpfn(head->array[moveto], item) <= 0) + break; + + head->array[pos] = head->array[moveto]; + head->array[pos]->index = pos; + + pos = moveto; + } + + head->array[pos] = item; + item->index = pos; + + heap_consistency_check(head, cmpfn, 0); } diff --git a/lib/typesafe.h b/lib/typesafe.h index bbf3ce8f1c..e134316dd9 100644 --- a/lib/typesafe.h +++ b/lib/typesafe.h @@ -23,23 +23,62 @@ #include #include "compiler.h" +#ifdef __cplusplus +extern "C" { +#endif + /* generic macros for all list-like types */ -#define for_each(prefix, head, item) \ +#define frr_each(prefix, head, item) \ for (item = prefix##_first(head); item; \ item = prefix##_next(head, item)) -#define for_each_safe(prefix, head, item) \ +#define frr_each_safe(prefix, head, item) \ for (typeof(prefix##_next_safe(head, NULL)) prefix##_safe = \ prefix##_next_safe(head, \ (item = prefix##_first(head))); \ item; \ item = prefix##_safe, \ prefix##_safe = prefix##_next_safe(head, prefix##_safe)) -#define for_each_from(prefix, head, item, from) \ +#define frr_each_from(prefix, head, item, from) \ for (item = from, from = prefix##_next_safe(head, item); \ item; \ item = from, from = prefix##_next_safe(head, from)) + +/* non-const variants. these wrappers are the same for all the types, so + * bundle them together here. + */ +#define TYPESAFE_FIRST_NEXT(prefix, type) \ +macro_pure type *prefix ## _first(struct prefix##_head *h) \ +{ \ + return (type *)prefix ## _const_first(h); \ +} \ +macro_pure type *prefix ## _next(struct prefix##_head *h, type *item) \ +{ \ + return (type *)prefix ## _const_next(h, item); \ +} \ +/* ... */ +#define TYPESAFE_FIND(prefix, type) \ +macro_inline type *prefix ## _find(struct prefix##_head *h, \ + const type *item) \ +{ \ + return (type *)prefix ## _const_find(h, item); \ +} \ +/* ... */ +#define TYPESAFE_FIND_CMP(prefix, type) \ +macro_inline type *prefix ## _find_lt(struct prefix##_head *h, \ + const type *item) \ +{ \ + return (type *)prefix ## _const_find_lt(h, item); \ +} \ +macro_inline type *prefix ## _find_gteq(struct prefix##_head *h, \ + const type *item) \ +{ \ + return (type *)prefix ## _const_find_gteq(h, item); \ +} \ +/* ... */ + + /* single-linked list, unsorted/arbitrary. * can be used as queue with add_tail / pop */ @@ -105,17 +144,18 @@ macro_inline void prefix ## _add_after(struct prefix##_head *h, \ typesafe_list_add(&h->sh, nextp, &item->field.si); \ } \ /* TODO: del_hint */ \ -macro_inline void prefix ## _del(struct prefix##_head *h, type *item) \ +macro_inline type *prefix ## _del(struct prefix##_head *h, type *item) \ { \ struct slist_item **iter = &h->sh.first; \ while (*iter && *iter != &item->field.si) \ iter = &(*iter)->next; \ if (!*iter) \ - return; \ + return NULL; \ h->sh.count--; \ *iter = item->field.si.next; \ if (!item->field.si.next) \ h->sh.last_next = iter; \ + return item; \ } \ macro_inline type *prefix ## _pop(struct prefix##_head *h) \ { \ @@ -128,15 +168,17 @@ macro_inline type *prefix ## _pop(struct prefix##_head *h) \ h->sh.last_next = &h->sh.first; \ return container_of(sitem, type, field.si); \ } \ -macro_pure type *prefix ## _first(struct prefix##_head *h) \ +macro_pure const type *prefix ## _const_first(const struct prefix##_head *h) \ { \ return container_of_null(h->sh.first, type, field.si); \ } \ -macro_pure type *prefix ## _next(struct prefix##_head * h, type *item) \ +macro_pure const type *prefix ## _const_next(const struct prefix##_head *h, \ + const type *item) \ { \ - struct slist_item *sitem = &item->field.si; \ + const struct slist_item *sitem = &item->field.si; \ return container_of_null(sitem->next, type, field.si); \ } \ +TYPESAFE_FIRST_NEXT(prefix, type) \ macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \ { \ struct slist_item *sitem; \ @@ -145,12 +187,233 @@ macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \ sitem = &item->field.si; \ return container_of_null(sitem->next, type, field.si); \ } \ -macro_pure size_t prefix ## _count(struct prefix##_head *h) \ +macro_pure size_t prefix ## _count(const struct prefix##_head *h) \ { \ return h->sh.count; \ } \ /* ... */ +/* don't use these structs directly */ +struct dlist_item { + struct dlist_item *next; + struct dlist_item *prev; +}; + +struct dlist_head { + struct dlist_item hitem; + size_t count; +}; + +static inline void typesafe_dlist_add(struct dlist_head *head, + struct dlist_item *prev, struct dlist_item *item) +{ + item->next = prev->next; + item->next->prev = item; + item->prev = prev; + prev->next = item; + head->count++; +} + +/* double-linked list, for fast item deletion + */ +#define PREDECL_DLIST(prefix) \ +struct prefix ## _head { struct dlist_head dh; }; \ +struct prefix ## _item { struct dlist_item di; }; + +#define INIT_DLIST(var) { .dh = { \ + .hitem = { &var.dh.hitem, &var.dh.hitem }, }, } + +#define DECLARE_DLIST(prefix, type, field) \ + \ +macro_inline void prefix ## _init(struct prefix##_head *h) \ +{ \ + memset(h, 0, sizeof(*h)); \ + h->dh.hitem.prev = &h->dh.hitem; \ + h->dh.hitem.next = &h->dh.hitem; \ +} \ +macro_inline void prefix ## _fini(struct prefix##_head *h) \ +{ \ + memset(h, 0, sizeof(*h)); \ +} \ +macro_inline void prefix ## _add_head(struct prefix##_head *h, type *item) \ +{ \ + typesafe_dlist_add(&h->dh, &h->dh.hitem, &item->field.di); \ +} \ +macro_inline void prefix ## _add_tail(struct prefix##_head *h, type *item) \ +{ \ + typesafe_dlist_add(&h->dh, h->dh.hitem.prev, &item->field.di); \ +} \ +macro_inline void prefix ## _add_after(struct prefix##_head *h, \ + type *after, type *item) \ +{ \ + struct dlist_item *prev; \ + prev = after ? &after->field.di : &h->dh.hitem; \ + typesafe_dlist_add(&h->dh, prev, &item->field.di); \ +} \ +macro_inline type *prefix ## _del(struct prefix##_head *h, type *item) \ +{ \ + struct dlist_item *ditem = &item->field.di; \ + ditem->prev->next = ditem->next; \ + ditem->next->prev = ditem->prev; \ + h->dh.count--; \ + ditem->prev = ditem->next = NULL; \ + return item; \ +} \ +macro_inline type *prefix ## _pop(struct prefix##_head *h) \ +{ \ + struct dlist_item *ditem = h->dh.hitem.next; \ + if (ditem == &h->dh.hitem) \ + return NULL; \ + ditem->prev->next = ditem->next; \ + ditem->next->prev = ditem->prev; \ + h->dh.count--; \ + return container_of(ditem, type, field.di); \ +} \ +macro_pure const type *prefix ## _const_first(const struct prefix##_head *h) \ +{ \ + const struct dlist_item *ditem = h->dh.hitem.next; \ + if (ditem == &h->dh.hitem) \ + return NULL; \ + return container_of(ditem, type, field.di); \ +} \ +macro_pure const type *prefix ## _const_next(const struct prefix##_head *h, \ + const type *item) \ +{ \ + const struct dlist_item *ditem = &item->field.di; \ + if (ditem->next == &h->dh.hitem) \ + return NULL; \ + return container_of(ditem->next, type, field.di); \ +} \ +TYPESAFE_FIRST_NEXT(prefix, type) \ +macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \ +{ \ + if (!item) \ + return NULL; \ + return prefix ## _next(h, item); \ +} \ +macro_pure size_t prefix ## _count(const struct prefix##_head *h) \ +{ \ + return h->dh.count; \ +} \ +/* ... */ + +/* note: heap currently caps out at 4G items */ + +#define HEAP_NARY 8U +typedef uint32_t heap_index_i; + +struct heap_item { + uint32_t index; +}; + +struct heap_head { + struct heap_item **array; + uint32_t arraysz, count; +}; + +#define HEAP_RESIZE_TRESH_UP(h) \ + (h->hh.count + 1 >= h->hh.arraysz) +#define HEAP_RESIZE_TRESH_DN(h) \ + (h->hh.count == 0 || \ + h->hh.arraysz - h->hh.count > (h->hh.count + 1024) / 2) + +#define PREDECL_HEAP(prefix) \ +struct prefix ## _head { struct heap_head hh; }; \ +struct prefix ## _item { struct heap_item hi; }; + +#define INIT_HEAP(var) { } + +#define DECLARE_HEAP(prefix, type, field, cmpfn) \ + \ +macro_inline void prefix ## _init(struct prefix##_head *h) \ +{ \ + memset(h, 0, sizeof(*h)); \ +} \ +macro_inline void prefix ## _fini(struct prefix##_head *h) \ +{ \ + assert(h->hh.count == 0); \ + memset(h, 0, sizeof(*h)); \ +} \ +macro_inline int prefix ## __cmp(const struct heap_item *a, \ + const struct heap_item *b) \ +{ \ + return cmpfn(container_of(a, type, field.hi), \ + container_of(b, type, field.hi)); \ +} \ +macro_inline type *prefix ## _add(struct prefix##_head *h, type *item) \ +{ \ + if (HEAP_RESIZE_TRESH_UP(h)) \ + typesafe_heap_resize(&h->hh, true); \ + typesafe_heap_pullup(&h->hh, h->hh.count, &item->field.hi, \ + prefix ## __cmp); \ + h->hh.count++; \ + return NULL; \ +} \ +macro_inline type *prefix ## _del(struct prefix##_head *h, type *item) \ +{ \ + struct heap_item *other; \ + uint32_t index = item->field.hi.index; \ + assert(h->hh.array[index] == &item->field.hi); \ + h->hh.count--; \ + other = h->hh.array[h->hh.count]; \ + if (cmpfn(container_of(other, type, field.hi), item) < 0) \ + typesafe_heap_pullup(&h->hh, index, other, prefix ## __cmp); \ + else \ + typesafe_heap_pushdown(&h->hh, index, other, prefix ## __cmp); \ + if (HEAP_RESIZE_TRESH_DN(h)) \ + typesafe_heap_resize(&h->hh, false); \ + return item; \ +} \ +macro_inline type *prefix ## _pop(struct prefix##_head *h) \ +{ \ + struct heap_item *hitem, *other; \ + if (h->hh.count == 0) \ + return NULL; \ + hitem = h->hh.array[0]; \ + h->hh.count--; \ + other = h->hh.array[h->hh.count]; \ + typesafe_heap_pushdown(&h->hh, 0, other, prefix ## __cmp); \ + if (HEAP_RESIZE_TRESH_DN(h)) \ + typesafe_heap_resize(&h->hh, false); \ + return container_of(hitem, type, field.hi); \ +} \ +macro_pure const type *prefix ## _const_first(const struct prefix##_head *h) \ +{ \ + if (h->hh.count == 0) \ + return NULL; \ + return container_of(h->hh.array[0], type, field.hi); \ +} \ +macro_pure const type *prefix ## _const_next(const struct prefix##_head *h, \ + const type *item) \ +{ \ + uint32_t idx = item->field.hi.index + 1; \ + if (idx >= h->hh.count) \ + return NULL; \ + return container_of(h->hh.array[idx], type, field.hi); \ +} \ +TYPESAFE_FIRST_NEXT(prefix, type) \ +macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \ +{ \ + if (!item) \ + return NULL; \ + return prefix ## _next(h, item); \ +} \ +macro_pure size_t prefix ## _count(const struct prefix##_head *h) \ +{ \ + return h->hh.count; \ +} \ +/* ... */ + +extern void typesafe_heap_resize(struct heap_head *head, bool grow); +extern void typesafe_heap_pushdown(struct heap_head *head, uint32_t index, + struct heap_item *item, + int (*cmpfn)(const struct heap_item *a, + const struct heap_item *b)); +extern void typesafe_heap_pullup(struct heap_head *head, uint32_t index, + struct heap_item *item, + int (*cmpfn)(const struct heap_item *a, + const struct heap_item *b)); + /* single-linked list, sorted. * can be used as priority queue with add / pop */ @@ -209,36 +472,38 @@ macro_inline type *prefix ## _add(struct prefix##_head *h, type *item) \ h->sh.count++; \ return NULL; \ } \ -macro_inline type *prefix ## _find_gteq(struct prefix##_head *h, \ - const type *item) \ +macro_inline const type *prefix ## _const_find_gteq( \ + const struct prefix##_head *h, const type *item) \ { \ - struct ssort_item *sitem = h->sh.first; \ + const struct ssort_item *sitem = h->sh.first; \ int cmpval = 0; \ while (sitem && (cmpval = cmpfn_nuq( \ - container_of(sitem, type, field.si), item) < 0)) \ + container_of(sitem, type, field.si), item)) < 0) \ sitem = sitem->next; \ return container_of_null(sitem, type, field.si); \ } \ -macro_inline type *prefix ## _find_lt(struct prefix##_head *h, \ - const type *item) \ +macro_inline const type *prefix ## _const_find_lt( \ + const struct prefix##_head *h, const type *item) \ { \ - struct ssort_item *prev = NULL, *sitem = h->sh.first; \ + const struct ssort_item *prev = NULL, *sitem = h->sh.first; \ int cmpval = 0; \ while (sitem && (cmpval = cmpfn_nuq( \ - container_of(sitem, type, field.si), item) < 0)) \ + container_of(sitem, type, field.si), item)) < 0) \ sitem = (prev = sitem)->next; \ return container_of_null(prev, type, field.si); \ } \ +TYPESAFE_FIND_CMP(prefix, type) \ /* TODO: del_hint */ \ -macro_inline void prefix ## _del(struct prefix##_head *h, type *item) \ +macro_inline type *prefix ## _del(struct prefix##_head *h, type *item) \ { \ struct ssort_item **iter = &h->sh.first; \ while (*iter && *iter != &item->field.si) \ iter = &(*iter)->next; \ if (!*iter) \ - return; \ + return NULL; \ h->sh.count--; \ *iter = item->field.si.next; \ + return item; \ } \ macro_inline type *prefix ## _pop(struct prefix##_head *h) \ { \ @@ -249,15 +514,17 @@ macro_inline type *prefix ## _pop(struct prefix##_head *h) \ h->sh.first = sitem->next; \ return container_of(sitem, type, field.si); \ } \ -macro_pure type *prefix ## _first(struct prefix##_head *h) \ +macro_pure const type *prefix ## _const_first(const struct prefix##_head *h) \ { \ return container_of_null(h->sh.first, type, field.si); \ } \ -macro_pure type *prefix ## _next(struct prefix##_head *h, type *item) \ +macro_pure const type *prefix ## _const_next(const struct prefix##_head *h, \ + const type *item) \ { \ - struct ssort_item *sitem = &item->field.si; \ + const struct ssort_item *sitem = &item->field.si; \ return container_of_null(sitem->next, type, field.si); \ } \ +TYPESAFE_FIRST_NEXT(prefix, type) \ macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \ { \ struct ssort_item *sitem; \ @@ -266,7 +533,7 @@ macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \ sitem = &item->field.si; \ return container_of_null(sitem->next, type, field.si); \ } \ -macro_pure size_t prefix ## _count(struct prefix##_head *h) \ +macro_pure size_t prefix ## _count(const struct prefix##_head *h) \ { \ return h->sh.count; \ } \ @@ -274,18 +541,20 @@ macro_pure size_t prefix ## _count(struct prefix##_head *h) \ #define DECLARE_SORTLIST_UNIQ(prefix, type, field, cmpfn) \ _DECLARE_SORTLIST(prefix, type, field, cmpfn, cmpfn) \ - \ -macro_inline type *prefix ## _find(struct prefix##_head *h, const type *item) \ + \ +macro_inline const type *prefix ## _const_find(const struct prefix##_head *h, \ + const type *item) \ { \ - struct ssort_item *sitem = h->sh.first; \ + const struct ssort_item *sitem = h->sh.first; \ int cmpval = 0; \ while (sitem && (cmpval = cmpfn( \ - container_of(sitem, type, field.si), item) < 0)) \ + container_of(sitem, type, field.si), item)) < 0) \ sitem = sitem->next; \ if (!sitem || cmpval > 0) \ return NULL; \ return container_of(sitem, type, field.si); \ } \ +TYPESAFE_FIND(prefix, type) \ /* ... */ #define DECLARE_SORTLIST_NONUNIQ(prefix, type, field, cmpfn) \ @@ -383,12 +652,13 @@ macro_inline type *prefix ## _add(struct prefix##_head *h, type *item) \ *np = &item->field.hi; \ return NULL; \ } \ -macro_inline type *prefix ## _find(struct prefix##_head *h, const type *item) \ +macro_inline const type *prefix ## _const_find(const struct prefix##_head *h, \ + const type *item) \ { \ if (!h->hh.tabshift) \ return NULL; \ uint32_t hval = hashfn(item), hbits = HASH_KEY(h->hh, hval); \ - struct thash_item *hitem = h->hh.entries[hbits]; \ + const struct thash_item *hitem = h->hh.entries[hbits]; \ while (hitem && hitem->hashval < hval) \ hitem = hitem->next; \ while (hitem && hitem->hashval == hval) { \ @@ -398,10 +668,11 @@ macro_inline type *prefix ## _find(struct prefix##_head *h, const type *item) \ } \ return NULL; \ } \ -macro_inline void prefix ## _del(struct prefix##_head *h, type *item) \ +TYPESAFE_FIND(prefix, type) \ +macro_inline type *prefix ## _del(struct prefix##_head *h, type *item) \ { \ if (!h->hh.tabshift) \ - return; \ + return NULL; \ uint32_t hval = item->field.hi.hashval, hbits = HASH_KEY(h->hh, hval); \ struct thash_item **np = &h->hh.entries[hbits]; \ while (*np && (*np)->hashval < hval) \ @@ -409,12 +680,13 @@ macro_inline void prefix ## _del(struct prefix##_head *h, type *item) \ while (*np && *np != &item->field.hi && (*np)->hashval == hval) \ np = &(*np)->next; \ if (*np != &item->field.hi) \ - return; \ + return NULL; \ *np = item->field.hi.next; \ item->field.hi.next = NULL; \ h->hh.count--; \ if (HASH_SHRINK_THRESHOLD(h->hh)) \ typesafe_hash_shrink(&h->hh); \ + return item; \ } \ macro_inline type *prefix ## _pop(struct prefix##_head *h) \ { \ @@ -431,7 +703,7 @@ macro_inline type *prefix ## _pop(struct prefix##_head *h) \ } \ return NULL; \ } \ -macro_pure type *prefix ## _first(struct prefix##_head *h) \ +macro_pure const type *prefix ## _const_first(const struct prefix##_head *h) \ { \ uint32_t i; \ for (i = 0; i < HASH_SIZE(h->hh); i++) \ @@ -439,24 +711,26 @@ macro_pure type *prefix ## _first(struct prefix##_head *h) \ return container_of(h->hh.entries[i], type, field.hi); \ return NULL; \ } \ -macro_pure type *prefix ## _next(struct prefix##_head *h, type *item) \ +macro_pure const type *prefix ## _const_next(const struct prefix##_head *h, \ + const type *item) \ { \ - struct thash_item *hitem = &item->field.hi; \ + const struct thash_item *hitem = &item->field.hi; \ if (hitem->next) \ return container_of(hitem->next, type, field.hi); \ uint32_t i = HASH_KEY(h->hh, hitem->hashval) + 1; \ - for (; i < HASH_SIZE(h->hh); i++) \ + for (; i < HASH_SIZE(h->hh); i++) \ if (h->hh.entries[i]) \ return container_of(h->hh.entries[i], type, field.hi); \ return NULL; \ } \ +TYPESAFE_FIRST_NEXT(prefix, type) \ macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \ { \ if (!item) \ return NULL; \ return prefix ## _next(h, item); \ } \ -macro_pure size_t prefix ## _count(struct prefix##_head *h) \ +macro_pure size_t prefix ## _count(const struct prefix##_head *h) \ { \ return h->hh.count; \ } \ @@ -518,49 +792,51 @@ macro_inline type *prefix ## _add(struct prefix##_head *h, type *item) \ si = typesafe_skiplist_add(&h->sh, &item->field.si, cmpfn_uq); \ return container_of_null(si, type, field.si); \ } \ -macro_inline type *prefix ## _find_gteq(struct prefix##_head *h, \ - const type *item) \ +macro_inline const type *prefix ## _const_find_gteq( \ + const struct prefix##_head *h, const type *item) \ { \ - struct sskip_item *sitem = typesafe_skiplist_find_gteq(&h->sh, \ + const struct sskip_item *sitem = typesafe_skiplist_find_gteq(&h->sh, \ &item->field.si, cmpfn_nuq); \ return container_of_null(sitem, type, field.si); \ } \ -macro_inline type *prefix ## _find_lt(struct prefix##_head *h, \ - const type *item) \ +macro_inline const type *prefix ## _const_find_lt( \ + const struct prefix##_head *h, const type *item) \ { \ - struct sskip_item *sitem = typesafe_skiplist_find_lt(&h->sh, \ + const struct sskip_item *sitem = typesafe_skiplist_find_lt(&h->sh, \ &item->field.si, cmpfn_nuq); \ return container_of_null(sitem, type, field.si); \ } \ -macro_inline void prefix ## _del(struct prefix##_head *h, type *item) \ +TYPESAFE_FIND_CMP(prefix, type) \ +macro_inline type *prefix ## _del(struct prefix##_head *h, type *item) \ { \ - typesafe_skiplist_del(&h->sh, &item->field.si, cmpfn_uq); \ + struct sskip_item *sitem = typesafe_skiplist_del(&h->sh, \ + &item->field.si, cmpfn_uq); \ + return container_of_null(sitem, type, field.si); \ } \ macro_inline type *prefix ## _pop(struct prefix##_head *h) \ { \ - struct sskip_item *sitem = h->sh.hitem.next[0]; \ - if (!sitem) \ - return NULL; \ - typesafe_skiplist_del(&h->sh, sitem, cmpfn_uq); \ - return container_of(sitem, type, field.si); \ + struct sskip_item *sitem = typesafe_skiplist_pop(&h->sh); \ + return container_of_null(sitem, type, field.si); \ } \ -macro_pure type *prefix ## _first(struct prefix##_head *h) \ +macro_pure const type *prefix ## _const_first(const struct prefix##_head *h) \ { \ - struct sskip_item *first = h->sh.hitem.next[0]; \ + const struct sskip_item *first = h->sh.hitem.next[0]; \ return container_of_null(first, type, field.si); \ } \ -macro_pure type *prefix ## _next(struct prefix##_head *h, type *item) \ +macro_pure const type *prefix ## _const_next(const struct prefix##_head *h, \ + const type *item) \ { \ - struct sskip_item *next = item->field.si.next[0]; \ + const struct sskip_item *next = item->field.si.next[0]; \ return container_of_null(next, type, field.si); \ } \ +TYPESAFE_FIRST_NEXT(prefix, type) \ macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \ { \ struct sskip_item *next; \ next = item ? item->field.si.next[0] : NULL; \ return container_of_null(next, type, field.si); \ } \ -macro_pure size_t prefix ## _count(struct prefix##_head *h) \ +macro_pure size_t prefix ## _count(const struct prefix##_head *h) \ { \ return h->sh.count; \ } \ @@ -569,19 +845,21 @@ macro_pure size_t prefix ## _count(struct prefix##_head *h) \ #define PREDECL_SKIPLIST_UNIQ(prefix) \ _PREDECL_SKIPLIST(prefix) #define DECLARE_SKIPLIST_UNIQ(prefix, type, field, cmpfn) \ - \ + \ macro_inline int prefix ## __cmp(const struct sskip_item *a, \ const struct sskip_item *b) \ { \ return cmpfn(container_of(a, type, field.si), \ container_of(b, type, field.si)); \ } \ -macro_inline type *prefix ## _find(struct prefix##_head *h, const type *item) \ +macro_inline const type *prefix ## _const_find(const struct prefix##_head *h, \ + const type *item) \ { \ - struct sskip_item *sitem = typesafe_skiplist_find(&h->sh, \ + const struct sskip_item *sitem = typesafe_skiplist_find(&h->sh, \ &item->field.si, &prefix ## __cmp); \ return container_of_null(sitem, type, field.si); \ } \ +TYPESAFE_FIND(prefix, type) \ \ _DECLARE_SKIPLIST(prefix, type, field, \ prefix ## __cmp, prefix ## __cmp) \ @@ -620,22 +898,30 @@ extern struct sskip_item *typesafe_skiplist_add(struct sskip_head *head, struct sskip_item *item, int (*cmpfn)( const struct sskip_item *a, const struct sskip_item *b)); -extern struct sskip_item *typesafe_skiplist_find(struct sskip_head *head, +extern const struct sskip_item *typesafe_skiplist_find( + const struct sskip_head *head, const struct sskip_item *item, int (*cmpfn)( const struct sskip_item *a, const struct sskip_item *b)); -extern struct sskip_item *typesafe_skiplist_find_gteq(struct sskip_head *head, +extern const struct sskip_item *typesafe_skiplist_find_gteq( + const struct sskip_head *head, const struct sskip_item *item, int (*cmpfn)( const struct sskip_item *a, const struct sskip_item *b)); -extern struct sskip_item *typesafe_skiplist_find_lt(struct sskip_head *head, +extern const struct sskip_item *typesafe_skiplist_find_lt( + const struct sskip_head *head, const struct sskip_item *item, int (*cmpfn)( const struct sskip_item *a, const struct sskip_item *b)); -extern void typesafe_skiplist_del(struct sskip_head *head, - struct sskip_item *item, int (*cmpfn)( +extern struct sskip_item *typesafe_skiplist_del( + struct sskip_head *head, struct sskip_item *item, int (*cmpfn)( const struct sskip_item *a, const struct sskip_item *b)); +extern struct sskip_item *typesafe_skiplist_pop(struct sskip_head *head); + +#ifdef __cplusplus +} +#endif /* this needs to stay at the end because both files include each other. * the resolved order is typesafe.h before typerb.h diff --git a/lib/version.h.in b/lib/version.h.in index 52c10f7d68..d535d131c8 100644 --- a/lib/version.h.in +++ b/lib/version.h.in @@ -28,6 +28,10 @@ #include "gitversion.h" #endif +#ifdef __cplusplus +extern "C" { +#endif + #ifndef GIT_SUFFIX #define GIT_SUFFIX "" #endif @@ -54,4 +58,8 @@ pid_t pid_output (const char *); +#ifdef __cplusplus +} +#endif + #endif /* _ZEBRA_VERSION_H */ diff --git a/lib/vrf.c b/lib/vrf.c index de50e6a517..d35b288caf 100644 --- a/lib/vrf.c +++ b/lib/vrf.c @@ -36,9 +36,10 @@ #include "privs.h" #include "nexthop_group.h" #include "lib_errors.h" +#include "northbound.h" +#include "northbound_cli.h" -/* default VRF ID value used when VRF backend is not NETNS */ -#define VRF_DEFAULT_INTERNAL 0 +/* default VRF name value used when VRF backend is not NETNS */ #define VRF_DEFAULT_NAME_INTERNAL "default" DEFINE_MTYPE_STATIC(LIB, VRF, "VRF") @@ -67,7 +68,7 @@ static char vrf_default_name[VRF_NAMSIZ] = VRF_DEFAULT_NAME_INTERNAL; static int debug_vrf = 0; /* Holding VRF hooks */ -struct vrf_master { +static struct vrf_master { int (*vrf_new_hook)(struct vrf *); int (*vrf_delete_hook)(struct vrf *); int (*vrf_enable_hook)(struct vrf *); @@ -116,7 +117,7 @@ static void vrf_update_vrf_id(ns_id_t ns_id, void *opaqueptr) vrf->vrf_id = vrf_id; RB_INSERT(vrf_id_head, &vrfs_by_id, vrf); if (old_vrf_id == VRF_UNKNOWN) - vrf_enable((struct vrf *)vrf); + vrf_enable(vrf); } int vrf_switch_to_netns(vrf_id_t vrf_id) @@ -157,10 +158,6 @@ struct vrf *vrf_get(vrf_id_t vrf_id, const char *name) struct vrf *vrf = NULL; int new = 0; - if (debug_vrf) - zlog_debug("VRF_GET: %s(%u)", name == NULL ? "(NULL)" : name, - vrf_id); - /* Nothing to see, move along here */ if (!name && vrf_id == VRF_UNKNOWN) return NULL; @@ -199,9 +196,14 @@ struct vrf *vrf_get(vrf_id_t vrf_id, const char *name) /* Set name */ if (name && vrf->name[0] != '\0' && strcmp(name, vrf->name)) { + /* update the vrf name */ RB_REMOVE(vrf_name_head, &vrfs_by_name, vrf); + strlcpy(vrf->data.l.netns_name, + name, NS_NAMSIZ); strlcpy(vrf->name, name, sizeof(vrf->name)); RB_INSERT(vrf_name_head, &vrfs_by_name, vrf); + if (vrf->vrf_id == VRF_DEFAULT) + vrf_set_default_name(vrf->name, false); } else if (name && vrf->name[0] == '\0') { strlcpy(vrf->name, name, sizeof(vrf->name)); RB_INSERT(vrf_name_head, &vrfs_by_name, vrf); @@ -218,7 +220,8 @@ struct vrf *vrf_get(vrf_id_t vrf_id, const char *name) void vrf_delete(struct vrf *vrf) { if (debug_vrf) - zlog_debug("VRF %u is to be deleted.", vrf->vrf_id); + zlog_debug("VRF %s(%u) is to be deleted.", vrf->name, + vrf->vrf_id); if (vrf_is_enabled(vrf)) vrf_disable(vrf); @@ -275,7 +278,7 @@ int vrf_enable(struct vrf *vrf) return 1; if (debug_vrf) - zlog_debug("VRF %u is enabled.", vrf->vrf_id); + zlog_debug("VRF %s(%u) is enabled.", vrf->name, vrf->vrf_id); SET_FLAG(vrf->status, VRF_ACTIVE); @@ -305,11 +308,20 @@ void vrf_disable(struct vrf *vrf) UNSET_FLAG(vrf->status, VRF_ACTIVE); if (debug_vrf) - zlog_debug("VRF %u is to be disabled.", vrf->vrf_id); + zlog_debug("VRF %s(%u) is to be disabled.", vrf->name, + vrf->vrf_id); /* Till now, nothing to be done for the default VRF. */ // Pending: see why this statement. + + /* + * When the vrf is disabled let's + * handle all nexthop-groups associated + * with this vrf + */ + nexthop_group_disable_vrf(vrf); + if (vrf_master.vrf_disable_hook) (*vrf_master.vrf_disable_hook)(vrf); } @@ -318,11 +330,11 @@ const char *vrf_id_to_name(vrf_id_t vrf_id) { struct vrf *vrf; - vrf = vrf_lookup_by_id(vrf_id); - if (vrf) - return vrf->name; + if (vrf_id == VRF_DEFAULT) + return VRF_DEFAULT_NAME; - return "n/a"; + vrf = vrf_lookup_by_id(vrf_id); + return VRF_LOGNAME(vrf); } vrf_id_t vrf_name_to_id(const char *name) @@ -362,9 +374,9 @@ struct vrf_bit_set { bool set; }; -static unsigned int vrf_hash_bitmap_key(void *data) +static unsigned int vrf_hash_bitmap_key(const void *data) { - struct vrf_bit_set *bit = data; + const struct vrf_bit_set *bit = data; return bit->vrf_id; } @@ -467,6 +479,14 @@ static const struct cmd_variable_handler vrf_var_handlers[] = { .varname = "vrf", .completions = vrf_autocomplete, }, + { + .varname = "vrf_name", + .completions = vrf_autocomplete, + }, + { + .varname = "nexthop_vrf", + .completions = vrf_autocomplete, + }, {.completions = NULL}, }; @@ -480,8 +500,7 @@ void vrf_init(int (*create)(struct vrf *), int (*enable)(struct vrf *), /* initialise NS, in case VRF backend if NETNS */ ns_init(); if (debug_vrf) - zlog_debug("%s: Initializing VRF subsystem", - __PRETTY_FUNCTION__); + zlog_debug("%s: Initializing VRF subsystem", __func__); vrf_master.vrf_new_hook = create; vrf_master.vrf_enable_hook = enable; @@ -501,7 +520,7 @@ void vrf_init(int (*create)(struct vrf *), int (*enable)(struct vrf *), strlcpy(default_vrf->data.l.netns_name, VRF_DEFAULT_NAME, NS_NAMSIZ); - ns = ns_lookup(ns_get_default_id()); + ns = ns_lookup(NS_DEFAULT); ns->vrf_ctxt = default_vrf; default_vrf->ns_ctxt = ns; } @@ -522,8 +541,7 @@ void vrf_terminate(void) struct vrf *vrf; if (debug_vrf) - zlog_debug("%s: Shutting down vrf subsystem", - __PRETTY_FUNCTION__); + zlog_debug("%s: Shutting down vrf subsystem", __func__); while (!RB_EMPTY(vrf_id_head, &vrfs_by_id)) { vrf = RB_ROOT(vrf_id_head, &vrfs_by_id); @@ -542,7 +560,6 @@ void vrf_terminate(void) } } -/* Create a socket for the VRF. */ int vrf_socket(int domain, int type, int protocol, vrf_id_t vrf_id, const char *interfacename) { @@ -583,16 +600,30 @@ int vrf_get_backend(void) return vrf_backend; } -void vrf_configure_backend(int vrf_backend_netns) +int vrf_configure_backend(enum vrf_backend_type backend) { - vrf_backend = vrf_backend_netns; + /* Work around issue in old gcc */ + switch (backend) { + case VRF_BACKEND_UNKNOWN: + case VRF_BACKEND_NETNS: + case VRF_BACKEND_VRF_LITE: + break; + default: + return -1; + } + + vrf_backend = backend; vrf_backend_configured = 1; + + return 0; } int vrf_handler_create(struct vty *vty, const char *vrfname, struct vrf **vrf) { struct vrf *vrfp; + char xpath_list[XPATH_MAXLEN]; + int ret; if (strlen(vrfname) > VRF_NAMSIZ) { if (vty) @@ -607,18 +638,31 @@ int vrf_handler_create(struct vty *vty, const char *vrfname, return CMD_WARNING_CONFIG_FAILED; } - vrfp = vrf_get(VRF_UNKNOWN, vrfname); - - if (vty) - VTY_PUSH_CONTEXT(VRF_NODE, vrfp); + if (vty) { + snprintf(xpath_list, sizeof(xpath_list), FRR_VRF_KEY_XPATH, + vrfname); + + nb_cli_enqueue_change(vty, xpath_list, NB_OP_CREATE, NULL); + ret = nb_cli_apply_changes(vty, xpath_list); + if (ret == CMD_SUCCESS) { + VTY_PUSH_XPATH(VRF_NODE, xpath_list); + nb_cli_pending_commit_check(vty); + vrfp = vrf_lookup_by_name(vrfname); + if (vrfp) + VTY_PUSH_CONTEXT(VRF_NODE, vrfp); + } + } else { + vrfp = vrf_get(VRF_UNKNOWN, vrfname); - if (vrf) - *vrf = vrfp; + if (vrf) + *vrf = vrfp; + } return CMD_SUCCESS; } int vrf_netns_handler_create(struct vty *vty, struct vrf *vrf, char *pathname, - ns_id_t ns_id, ns_id_t internal_ns_id) + ns_id_t ns_id, ns_id_t internal_ns_id, + ns_id_t rel_def_ns_id) { struct ns *ns = NULL; @@ -656,8 +700,7 @@ int vrf_netns_handler_create(struct vty *vty, struct vrf *vrf, char *pathname, return CMD_SUCCESS; if (vty) vty_out(vty, - "NS %s is already configured" - " with VRF %u(%s)\n", + "NS %s is already configured with VRF %u(%s)\n", ns->name, vrf2->vrf_id, vrf2->name); else zlog_info("NS %s is already configured with VRF %u(%s)", @@ -666,6 +709,7 @@ int vrf_netns_handler_create(struct vty *vty, struct vrf *vrf, char *pathname, } ns = ns_get_created(ns, pathname, ns_id); ns->internal_ns_id = internal_ns_id; + ns->relative_default_ns = rel_def_ns_id; ns->vrf_ctxt = (void *)vrf; vrf->ns_ctxt = (void *)ns; /* update VRF netns NAME */ @@ -692,11 +736,11 @@ DEFUN_NOSH(vrf_exit, { /* We have to set vrf context to default vrf */ VTY_PUSH_CONTEXT(VRF_NODE, vrf_get(VRF_DEFAULT, VRF_DEFAULT_NAME)); - vty->node = CONFIG_NODE; + cmd_exit(vty); return CMD_SUCCESS; } -DEFUN_NOSH (vrf, +DEFUN_YANG_NOSH (vrf, vrf_cmd, "vrf NAME", "Select a VRF to configure\n" @@ -708,7 +752,7 @@ DEFUN_NOSH (vrf, return vrf_handler_create(vty, vrfname, NULL); } -DEFUN (no_vrf, +DEFUN_YANG (no_vrf, no_vrf_cmd, "no vrf NAME", NO_STR @@ -716,30 +760,33 @@ DEFUN (no_vrf, "VRF's name\n") { const char *vrfname = argv[2]->arg; + char xpath_list[XPATH_MAXLEN]; struct vrf *vrfp; vrfp = vrf_lookup_by_name(vrfname); - if (vrfp == NULL) { - vty_out(vty, "%% VRF %s does not exist\n", vrfname); - return CMD_WARNING_CONFIG_FAILED; - } + if (vrfp == NULL) + return CMD_SUCCESS; if (CHECK_FLAG(vrfp->status, VRF_ACTIVE)) { vty_out(vty, "%% Only inactive VRFs can be deleted\n"); return CMD_WARNING_CONFIG_FAILED; } - /* Clear configured flag and invoke delete. */ - UNSET_FLAG(vrfp->status, VRF_CONFIGURED); - vrf_delete(vrfp); + snprintf(xpath_list, sizeof(xpath_list), FRR_VRF_KEY_XPATH, vrfname); - return CMD_SUCCESS; + nb_cli_enqueue_change(vty, xpath_list, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, xpath_list); } -struct cmd_node vrf_node = {VRF_NODE, "%s(config-vrf)# ", 1}; +static struct cmd_node vrf_node = { + .name = "vrf", + .node = VRF_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-vrf)# ", +}; DEFUN_NOSH (vrf_netns, vrf_netns_cmd, @@ -755,9 +802,11 @@ DEFUN_NOSH (vrf_netns, if (!pathname) return CMD_WARNING_CONFIG_FAILED; - frr_elevate_privs(vrf_daemon_privs) { + frr_with_privs(vrf_daemon_privs) { ret = vrf_netns_handler_create(vty, vrf, pathname, - NS_UNKNOWN, NS_UNKNOWN); + NS_UNKNOWN, + NS_UNKNOWN, + NS_UNKNOWN); } return ret; } @@ -829,11 +878,17 @@ static int vrf_write_host(struct vty *vty) return 1; } -static struct cmd_node vrf_debug_node = {VRF_DEBUG_NODE, "", 1}; +static int vrf_write_host(struct vty *vty); +static struct cmd_node vrf_debug_node = { + .name = "vrf debug", + .node = VRF_DEBUG_NODE, + .prompt = "", + .config_write = vrf_write_host, +}; void vrf_install_commands(void) { - install_node(&vrf_debug_node, vrf_write_host); + install_node(&vrf_debug_node); install_element(CONFIG_NODE, &vrf_debug_cmd); install_element(ENABLE_NODE, &vrf_debug_cmd); @@ -846,7 +901,8 @@ void vrf_cmd_init(int (*writefunc)(struct vty *vty), { install_element(CONFIG_NODE, &vrf_cmd); install_element(CONFIG_NODE, &no_vrf_cmd); - install_node(&vrf_node, writefunc); + vrf_node.config_write = writefunc; + install_node(&vrf_node); install_default(VRF_NODE); install_element(VRF_NODE, &vrf_exit_cmd); if (vrf_is_backend_netns() && ns_have_netns()) { @@ -860,7 +916,6 @@ void vrf_cmd_init(int (*writefunc)(struct vty *vty), void vrf_set_default_name(const char *default_name, bool force) { struct vrf *def_vrf; - struct vrf *vrf_with_default_name = NULL; static bool def_vrf_forced; def_vrf = vrf_lookup_by_id(VRF_DEFAULT); @@ -871,13 +926,8 @@ void vrf_set_default_name(const char *default_name, bool force) def_vrf->vrf_id); return; } - if (vrf_with_default_name && vrf_with_default_name != def_vrf) { - /* vrf name already used by an other VRF */ - zlog_debug("VRF: %s, avoid changing name to %s, same name exists (%u)", - vrf_with_default_name->name, default_name, - vrf_with_default_name->vrf_id); + if (strmatch(vrf_default_name, default_name)) return; - } snprintf(vrf_default_name, VRF_NAMSIZ, "%s", default_name); if (def_vrf) { if (force) @@ -897,24 +947,19 @@ const char *vrf_get_default_name(void) return vrf_default_name; } -vrf_id_t vrf_get_default_id(void) -{ - /* backend netns is only known by zebra - * for other daemons, we return VRF_DEFAULT_INTERNAL - */ - if (vrf_is_backend_netns()) - return ns_get_default_id(); - else - return VRF_DEFAULT_INTERNAL; -} - int vrf_bind(vrf_id_t vrf_id, int fd, const char *name) { int ret = 0; + struct interface *ifp; if (fd < 0 || name == NULL) return fd; - if (vrf_is_backend_netns()) + /* the device should exist + * otherwise we should return + * case ifname = vrf in netns mode => return + */ + ifp = if_lookup_by_name(name, vrf_id); + if (!ifp) return fd; #ifdef SO_BINDTODEVICE ret = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, name, strlen(name)+1); @@ -1000,3 +1045,145 @@ vrf_id_t vrf_generate_id(void) return ++vrf_id_local; } + +/* ------- Northbound callbacks ------- */ + +/* + * XPath: /frr-vrf:lib/vrf + */ +static int lib_vrf_create(struct nb_cb_create_args *args) +{ + const char *vrfname; + struct vrf *vrfp; + + vrfname = yang_dnode_get_string(args->dnode, "./name"); + + if (args->event != NB_EV_APPLY) + return NB_OK; + + vrfp = vrf_get(VRF_UNKNOWN, vrfname); + + SET_FLAG(vrfp->status, VRF_CONFIGURED); + nb_running_set_entry(args->dnode, vrfp); + + return NB_OK; +} + +static int lib_vrf_destroy(struct nb_cb_destroy_args *args) +{ + struct vrf *vrfp; + + switch (args->event) { + case NB_EV_VALIDATE: + vrfp = nb_running_get_entry(args->dnode, NULL, true); + if (CHECK_FLAG(vrfp->status, VRF_ACTIVE)) { + snprintf(args->errmsg, args->errmsg_len, + "Only inactive VRFs can be deleted"); + return NB_ERR_VALIDATION; + } + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + vrfp = nb_running_unset_entry(args->dnode); + + /* Clear configured flag and invoke delete. */ + UNSET_FLAG(vrfp->status, VRF_CONFIGURED); + vrf_delete(vrfp); + break; + } + + return NB_OK; +} + +static const void *lib_vrf_get_next(struct nb_cb_get_next_args *args) +{ + struct vrf *vrfp = (struct vrf *)args->list_entry; + + if (args->list_entry == NULL) { + vrfp = RB_MIN(vrf_name_head, &vrfs_by_name); + } else { + vrfp = RB_NEXT(vrf_name_head, vrfp); + } + + return vrfp; +} + +static int lib_vrf_get_keys(struct nb_cb_get_keys_args *args) +{ + struct vrf *vrfp = (struct vrf *)args->list_entry; + + args->keys->num = 1; + strlcpy(args->keys->key[0], vrfp->name, sizeof(args->keys->key[0])); + + return NB_OK; +} + +static const void *lib_vrf_lookup_entry(struct nb_cb_lookup_entry_args *args) +{ + const char *vrfname = args->keys->key[0]; + + struct vrf *vrf = vrf_lookup_by_name(vrfname); + + return vrf; +} + +/* + * XPath: /frr-vrf:lib/vrf/id + */ +static struct yang_data * +lib_vrf_state_id_get_elem(struct nb_cb_get_elem_args *args) +{ + struct vrf *vrfp = (struct vrf *)args->list_entry; + + return yang_data_new_uint32(args->xpath, vrfp->vrf_id); +} + +/* + * XPath: /frr-vrf:lib/vrf/active + */ +static struct yang_data * +lib_vrf_state_active_get_elem(struct nb_cb_get_elem_args *args) +{ + struct vrf *vrfp = (struct vrf *)args->list_entry; + + if (vrfp->status == VRF_ACTIVE) + return yang_data_new_bool( + args->xpath, vrfp->status == VRF_ACTIVE ? true : false); + + return NULL; +} + +/* clang-format off */ +const struct frr_yang_module_info frr_vrf_info = { + .name = "frr-vrf", + .nodes = { + { + .xpath = "/frr-vrf:lib/vrf", + .cbs = { + .create = lib_vrf_create, + .destroy = lib_vrf_destroy, + .get_next = lib_vrf_get_next, + .get_keys = lib_vrf_get_keys, + .lookup_entry = lib_vrf_lookup_entry, + } + }, + { + .xpath = "/frr-vrf:lib/vrf/state/id", + .cbs = { + .get_elem = lib_vrf_state_id_get_elem, + } + }, + { + .xpath = "/frr-vrf:lib/vrf/state/active", + .cbs = { + .get_elem = lib_vrf_state_active_get_elem, + } + }, + { + .xpath = NULL, + }, + } +}; + diff --git a/lib/vrf.h b/lib/vrf.h index ca253e58a3..12232f6a2e 100644 --- a/lib/vrf.h +++ b/lib/vrf.h @@ -52,6 +52,9 @@ enum { IFLA_VRF_UNSPEC, IFLA_VRF_TABLE, __IFLA_VRF_MAX }; #define VRF_ALL_CMD_HELP_STR "Specify the VRF\nAll VRFs\n" #define VRF_FULL_CMD_HELP_STR "Specify the VRF\nThe VRF name\nAll VRFs\n" +#define FRR_VRF_XPATH "/frr-vrf:lib/vrf" +#define FRR_VRF_KEY_XPATH "/frr-vrf:lib/vrf[name='%s']" + /* * Pass some OS specific data up through * to the daemons @@ -101,9 +104,12 @@ RB_PROTOTYPE(vrf_name_head, vrf, name_entry, vrf_name_compare) DECLARE_QOBJ_TYPE(vrf) /* Allow VRF with netns as backend */ -#define VRF_BACKEND_VRF_LITE 0 -#define VRF_BACKEND_NETNS 1 -#define VRF_BACKEND_UNKNOWN 2 +enum vrf_backend_type { + VRF_BACKEND_VRF_LITE, + VRF_BACKEND_NETNS, + VRF_BACKEND_UNKNOWN, + VRF_BACKEND_MAX, +}; extern struct vrf_id_head vrfs_by_id; extern struct vrf_name_head vrfs_by_name; @@ -114,6 +120,8 @@ extern struct vrf *vrf_get(vrf_id_t, const char *); extern const char *vrf_id_to_name(vrf_id_t vrf_id); extern vrf_id_t vrf_name_to_id(const char *); +#define VRF_LOGNAME(V) V ? V->name : "Unknown" + #define VRF_GET_ID(V, NAME, USE_JSON) \ do { \ struct vrf *_vrf; \ @@ -150,18 +158,6 @@ static inline int vrf_is_user_cfged(struct vrf *vrf) return vrf && CHECK_FLAG(vrf->status, VRF_CONFIGURED); } -/* Mark that VRF has user configuration */ -static inline void vrf_set_user_cfged(struct vrf *vrf) -{ - SET_FLAG(vrf->status, VRF_CONFIGURED); -} - -/* Mark that VRF no longer has any user configuration */ -static inline void vrf_reset_user_cfged(struct vrf *vrf) -{ - UNSET_FLAG(vrf->status, VRF_CONFIGURED); -} - /* * Utilities to obtain the user data */ @@ -218,13 +214,36 @@ extern void vrf_terminate(void); * or call network operations */ -/* Create a socket serving for the given VRF */ +/* + * Create a new socket associated with a VRF. + * + * This is a wrapper that ensures correct behavior when using namespace VRFs. + * In the namespace case, the socket is created within the namespace. In the + * non-namespace case, this is equivalent to socket(). + * + * If name is provided, this is provided to vrf_bind() to bind the socket to + * the VRF. This is only relevant when using VRF-lite. + * + * Summary: + * - Namespace: pass vrf_id but not name + * - VRF-lite: pass vrf_id and name of VRF device to bind to + * - VRF-lite, no binding: pass vrf_id but not name, or just use socket() + */ extern int vrf_socket(int domain, int type, int protocol, vrf_id_t vrf_id, const char *name); extern int vrf_sockunion_socket(const union sockunion *su, vrf_id_t vrf_id, const char *name); +/* + * Binds a socket to a VRF device. + * + * If name is null, the socket is not bound, irrespective of any other + * arguments. + * + * name should be the name of the VRF device. vrf_id should be the + * corresponding vrf_id (the ifindex of the device). + */ extern int vrf_bind(vrf_id_t vrf_id, int fd, const char *name); /* VRF ioctl operations */ @@ -234,12 +253,8 @@ extern int vrf_getaddrinfo(const char *node, const char *service, extern int vrf_ioctl(vrf_id_t vrf_id, int d, unsigned long request, char *args); -/* function called by macro VRF_DEFAULT - * to get the default VRF_ID - */ -extern vrf_id_t vrf_get_default_id(void); /* The default VRF ID */ -#define VRF_DEFAULT vrf_get_default_id() +#define VRF_DEFAULT 0 extern void vrf_set_default_name(const char *default_name, bool force); extern const char *vrf_get_default_name(void); @@ -267,10 +282,10 @@ extern void vrf_install_commands(void); * VRF utilities */ -/* API for configuring VRF backend - * should be called from zebra only +/* + * API for configuring VRF backend */ -extern void vrf_configure_backend(int vrf_backend_netns); +extern int vrf_configure_backend(enum vrf_backend_type backend); extern int vrf_get_backend(void); extern int vrf_is_backend_netns(void); @@ -287,7 +302,7 @@ extern int vrf_handler_create(struct vty *vty, const char *name, */ extern int vrf_netns_handler_create(struct vty *vty, struct vrf *vrf, char *pathname, ns_id_t ext_ns_id, - ns_id_t ns_id); + ns_id_t ns_id, ns_id_t rel_def_ns_id); /* used internally to enable or disable VRF. * Notify a change in the VRF ID of the VRF @@ -297,6 +312,8 @@ extern int vrf_enable(struct vrf *vrf); extern void vrf_delete(struct vrf *vrf); extern vrf_id_t vrf_generate_id(void); +extern const struct frr_yang_module_info frr_vrf_info; + #ifdef __cplusplus } #endif diff --git a/lib/vty.c b/lib/vty.c index dae8e82599..077c6f6211 100644 --- a/lib/vty.c +++ b/lib/vty.c @@ -23,7 +23,12 @@ #include #include +#include +#ifdef HAVE_LIBPCREPOSIX +#include +#else #include +#endif /* HAVE_LIBPCREPOSIX */ #include #include "linklist.h" @@ -42,10 +47,15 @@ #include "frrstr.h" #include "lib_errors.h" #include "northbound_cli.h" +#include "printfrr.h" #include #include +#ifndef VTYSH_EXTRACT_PL +#include "lib/vty_clippy.c" +#endif + DEFINE_MTYPE_STATIC(LIB, VTY, "VTY") DEFINE_MTYPE_STATIC(LIB, VTY_OUT_BUF, "VTY output buffer") DEFINE_MTYPE_STATIC(LIB, VTY_HIST, "VTY history") @@ -84,10 +94,7 @@ static char *vty_ipv6_accesslist_name = NULL; static vector Vvty_serv_thread; /* Current directory. */ -char *vty_cwd = NULL; - -/* Exclusive configuration lock. */ -struct vty *vty_exclusive_lock; +static char vty_cwd[MAXPATHLEN]; /* Login password check. */ static int no_password_check = 0; @@ -95,15 +102,16 @@ static int no_password_check = 0; /* Integrated configuration file path */ static char integrate_default[] = SYSCONFDIR INTEGRATE_DEFAULT_CONFIG; -static int do_log_commands = 0; +static bool do_log_commands; +static bool do_log_commands_perm; void vty_frame(struct vty *vty, const char *format, ...) { va_list args; va_start(args, format); - vsnprintf(vty->frame + vty->frame_pos, - sizeof(vty->frame) - vty->frame_pos, format, args); + vsnprintfrr(vty->frame + vty->frame_pos, + sizeof(vty->frame) - vty->frame_pos, format, args); vty->frame_pos = strlen(vty->frame); va_end(args); } @@ -133,8 +141,8 @@ bool vty_set_include(struct vty *vty, const char *regexp) REG_EXTENDED | REG_NEWLINE | REG_NOSUB); if (errcode) { ret = false; - regerror(ret, &vty->include, errbuf, sizeof(errbuf)); - vty_out(vty, "%% Regex compilation error: %s", errbuf); + regerror(errcode, &vty->include, errbuf, sizeof(errbuf)); + vty_out(vty, "%% Regex compilation error: %s\n", errbuf); } else { vty->filter = true; } @@ -146,8 +154,7 @@ bool vty_set_include(struct vty *vty, const char *regexp) int vty_out(struct vty *vty, const char *format, ...) { va_list args; - int len = 0; - int size = 1024; + ssize_t len; char buf[1024]; char *p = NULL; char *filtered; @@ -157,35 +164,11 @@ int vty_out(struct vty *vty, const char *format, ...) vty_out(vty, "%s", vty->frame); } - /* Try to write to initial buffer. */ va_start(args, format); - len = vsnprintf(buf, sizeof(buf), format, args); + p = vasnprintfrr(MTYPE_VTY_OUT_BUF, buf, sizeof(buf), format, args); va_end(args); - /* Initial buffer is not enough. */ - if (len < 0 || len >= size) { - while (1) { - if (len > -1) - size = len + 1; - else - size = size * 2; - - p = XREALLOC(MTYPE_VTY_OUT_BUF, p, size); - if (!p) - return -1; - - va_start(args, format); - len = vsnprintf(p, size, format, args); - va_end(args); - - if (len > -1 && len < size) - break; - } - } - - /* When initial buffer is enough to store all output. */ - if (!p) - p = buf; + len = strlen(p); /* filter buffer */ if (vty->filter) { @@ -248,8 +231,13 @@ int vty_out(struct vty *vty, const char *format, ...) strlen(filtered)); break; case VTY_SHELL: - fprintf(vty->of, "%s", filtered); - fflush(vty->of); + if (vty->of) { + fprintf(vty->of, "%s", filtered); + fflush(vty->of); + } else if (vty->of_saved) { + fprintf(vty->of_saved, "%s", filtered); + fflush(vty->of_saved); + } break; case VTY_SHELL_SERV: case VTY_FILE: @@ -272,8 +260,8 @@ int vty_out(struct vty *vty, const char *format, ...) } static int vty_log_out(struct vty *vty, const char *level, - const char *proto_str, const char *format, - struct timestamp_control *ctl, va_list va) + const char *proto_str, const char *msg, + struct timestamp_control *ctl) { int ret; int len; @@ -298,7 +286,7 @@ static int vty_log_out(struct vty *vty, const char *level, if ((ret < 0) || ((size_t)(len += ret) >= sizeof(buf))) return -1; - if (((ret = vsnprintf(buf + len, sizeof(buf) - len, format, va)) < 0) + if (((ret = snprintf(buf + len, sizeof(buf) - len, "%s", msg)) < 0) || ((size_t)((len += ret) + 2) > sizeof(buf))) return -1; @@ -359,7 +347,8 @@ void vty_hello(struct vty *vty) /* work backwards to ignore trailling isspace() */ for (s = buf + strlen(buf); - (s > buf) && isspace((int)*(s - 1)); s--) + (s > buf) && isspace((unsigned char)s[-1]); + s--) ; *s = '\0'; vty_out(vty, "%s\n", buf); @@ -369,6 +358,15 @@ void vty_hello(struct vty *vty) vty_out(vty, "MOTD file not found\n"); } else if (host.motd) vty_out(vty, "%s", host.motd); + +#if CONFDATE > 20200901 + CPP_NOTICE("Please remove solaris code from system as it is deprecated"); +#endif +#ifdef SUNOS_5 + zlog_warn("If you are using FRR on Solaris, the FRR developers would love to hear from you\n"); + zlog_warn("Please send email to dev@lists.frrouting.org about this message\n"); + zlog_warn("We are considering deprecating Solaris and want to find users of Solaris systems\n"); +#endif } /* Put out prompt and wait input from user. */ @@ -490,7 +488,7 @@ static int vty_command(struct vty *vty, char *buf) cp = buf; if (cp != NULL) { /* Skip white spaces. */ - while (isspace((int)*cp) && *cp != '\0') + while (isspace((unsigned char)*cp) && *cp != '\0') cp++; } if (cp != NULL && *cp != '\0') { @@ -914,7 +912,7 @@ static void vty_complete_command(struct vty *vty) return; /* In case of 'help \t'. */ - if (isspace((int)vty->buf[vty->length - 1])) + if (isspace((unsigned char)vty->buf[vty->length - 1])) vector_set(vline, NULL); matched = cmd_complete_command(vline, vty, &ret); @@ -1001,7 +999,7 @@ static void vty_describe_fold(struct vty *vty, int cmd_width, if (pos == 0) break; - strncpy(buf, p, pos); + memcpy(buf, p, pos); buf[pos] = '\0'; vty_out(vty, " %-*s %s\n", cmd_width, cmd, buf); @@ -1028,7 +1026,7 @@ static void vty_describe_command(struct vty *vty) if (vline == NULL) { vline = vector_init(1); vector_set(vline, NULL); - } else if (isspace((int)vty->buf[vty->length - 1])) + } else if (isspace((unsigned char)vty->buf[vty->length - 1])) vector_set(vline, NULL); describe = cmd_describe_command(vline, vty, &ret); @@ -1233,7 +1231,6 @@ static int vty_telnet_option(struct vty *vty, unsigned char *buf, int nbytes) vty->sb_len = 0; vty->iac_sb_in_progress = 1; return 0; - break; case SE: { if (!vty->iac_sb_in_progress) return 0; @@ -1247,8 +1244,7 @@ static int vty_telnet_option(struct vty *vty, unsigned char *buf, int nbytes) if (vty->sb_len != TELNET_NAWS_SB_LEN) flog_err( EC_LIB_SYSTEM_CALL, - "RFC 1073 violation detected: telnet NAWS option " - "should send %d characters, but we received %lu", + "RFC 1073 violation detected: telnet NAWS option should send %d characters, but we received %lu", TELNET_NAWS_SB_LEN, (unsigned long)vty->sb_len); else if (sizeof(vty->sb_buf) < TELNET_NAWS_SB_LEN) @@ -1264,8 +1260,7 @@ static int vty_telnet_option(struct vty *vty, unsigned char *buf, int nbytes) | vty->sb_buf[4]); #ifdef TELNET_OPTION_DEBUG vty_out(vty, - "TELNET NAWS window size negotiation completed: " - "width %d, height %d\n", + "TELNET NAWS window size negotiation completed: width %d, height %d\n", vty->width, vty->height); #endif } @@ -1273,7 +1268,6 @@ static int vty_telnet_option(struct vty *vty, unsigned char *buf, int nbytes) } vty->iac_sb_in_progress = 0; return 0; - break; } default: break; @@ -1357,7 +1351,6 @@ static int vty_read(struct thread *thread) int vty_sock = THREAD_FD(thread); struct vty *vty = THREAD_ARG(thread); - vty->t_read = NULL; /* Read raw data from socket */ if ((nbytes = read(vty->fd, buf, VTY_READ_BUFSIZ)) <= 0) { @@ -1551,13 +1544,9 @@ static int vty_flush(struct thread *thread) int vty_sock = THREAD_FD(thread); struct vty *vty = THREAD_ARG(thread); - vty->t_write = NULL; - /* Tempolary disable read thread. */ - if ((vty->lines == 0) && vty->t_read) { - thread_cancel(vty->t_read); - vty->t_read = NULL; - } + if (vty->lines == 0) + THREAD_OFF(vty->t_read); /* Function execution continue. */ erase = ((vty->status == VTY_MORE || vty->status == VTY_MORELINE)); @@ -1662,7 +1651,7 @@ static struct vty *vty_create(int vty_sock, union sockunion *su) /* configurable parameters not part of basic init */ vty->v_timeout = vty_timeout_val; - strcpy(vty->address, buf); + strlcpy(vty->address, buf, sizeof(vty->address)); if (no_password_check) { if (host.advanced) vty->node = ENABLE_NODE; @@ -1735,12 +1724,9 @@ void vty_stdio_suspend(void) if (!stdio_vty) return; - if (stdio_vty->t_write) - thread_cancel(stdio_vty->t_write); - if (stdio_vty->t_read) - thread_cancel(stdio_vty->t_read); - if (stdio_vty->t_timeout) - thread_cancel(stdio_vty->t_timeout); + THREAD_OFF(stdio_vty->t_write); + THREAD_OFF(stdio_vty->t_read); + THREAD_OFF(stdio_vty->t_timeout); if (stdio_termios) tcsetattr(0, TCSANOW, &stdio_orig_termios); @@ -1798,7 +1784,7 @@ struct vty *vty_stdio(void (*atclose)(int isexit)) */ vty->node = ENABLE_NODE; vty->v_timeout = 0; - strcpy(vty->address, "console"); + strlcpy(vty->address, "console", sizeof(vty->address)); vty_stdio_resume(); return vty; @@ -1894,7 +1880,7 @@ static void vty_serv_sock_addrinfo(const char *hostname, unsigned short port) req.ai_flags = AI_PASSIVE; req.ai_family = AF_UNSPEC; req.ai_socktype = SOCK_STREAM; - sprintf(port_str, "%d", port); + snprintf(port_str, sizeof(port_str), "%d", port); port_str[sizeof(port_str) - 1] = '\0'; ret = getaddrinfo(hostname, port_str, &req, &ainfo); @@ -2080,7 +2066,6 @@ static int vtysh_flush(struct vty *vty) buffer_reset(vty->obuf); vty_close(vty); return -1; - break; case BUFFER_EMPTY: break; } @@ -2099,7 +2084,6 @@ static int vtysh_read(struct thread *thread) sock = THREAD_FD(thread); vty = THREAD_ARG(thread); - vty->t_read = NULL; if ((nbytes = read(sock, buf, VTY_READ_BUFSIZ)) <= 0) { if (nbytes < 0) { @@ -2179,7 +2163,6 @@ static int vtysh_write(struct thread *thread) { struct vty *vty = THREAD_ARG(thread); - vty->t_write = NULL; vtysh_flush(vty); return 0; } @@ -2214,13 +2197,13 @@ void vty_close(struct vty *vty) int i; bool was_stdio = false; + /* Drop out of configure / transaction if needed. */ + vty_config_exit(vty); + /* Cancel threads.*/ - if (vty->t_read) - thread_cancel(vty->t_read); - if (vty->t_write) - thread_cancel(vty->t_write); - if (vty->t_timeout) - thread_cancel(vty->t_timeout); + THREAD_OFF(vty->t_read); + THREAD_OFF(vty->t_write); + THREAD_OFF(vty->t_timeout); /* Flush buffer. */ buffer_flush_all(vty->obuf, vty->wfd); @@ -2260,9 +2243,6 @@ void vty_close(struct vty *vty) list_delete(&vty->error); } - /* Check configure. */ - vty_config_exit(vty); - /* OK free vty. */ XFREE(MTYPE_VTY, vty); @@ -2276,7 +2256,6 @@ static int vty_timeout(struct thread *thread) struct vty *vty; vty = THREAD_ARG(thread); - vty->t_timeout = NULL; vty->v_timeout = 0; /* Clear buffer*/ @@ -2311,6 +2290,7 @@ static void vty_read_file(struct nb_config *config, FILE *confp) vty->wfd = STDERR_FILENO; vty->type = VTY_FILE; vty->node = CONFIG_NODE; + vty->config = true; if (config) vty->candidate_config = config; else { @@ -2366,14 +2346,19 @@ static void vty_read_file(struct nb_config *config, FILE *confp) * Automatically commit the candidate configuration after * reading the configuration file. */ - if (config == NULL && vty->candidate_config - && frr_get_cli_mode() == FRR_CLI_TRANSACTIONAL) { - ret = nb_candidate_commit(vty->candidate_config, NB_CLIENT_CLI, - true, "Read configuration file", - NULL); + if (config == NULL) { + struct nb_context context = {}; + char errmsg[BUFSIZ] = {0}; + + context.client = NB_CLIENT_CLI; + context.user = vty; + ret = nb_candidate_commit(&context, vty->candidate_config, true, + "Read configuration file", NULL, + errmsg, sizeof(errmsg)); if (ret != NB_OK && ret != NB_ERR_NO_CHANGES) - zlog_err("%s: failed to read configuration file.", - __func__); + zlog_err( + "%s: failed to read configuration file: %s (%s)", + __func__, nb_err_name(ret), errmsg); } vty_close(vty); @@ -2387,9 +2372,10 @@ static FILE *vty_use_backup_config(const char *fullpath) int c; char buffer[512]; - fullpath_sav = malloc(strlen(fullpath) + strlen(CONF_BACKUP_EXT) + 1); - strcpy(fullpath_sav, fullpath); - strcat(fullpath_sav, CONF_BACKUP_EXT); + size_t fullpath_sav_sz = strlen(fullpath) + strlen(CONF_BACKUP_EXT) + 1; + fullpath_sav = malloc(fullpath_sav_sz); + strlcpy(fullpath_sav, fullpath, fullpath_sav_sz); + strlcat(fullpath_sav, CONF_BACKUP_EXT, fullpath_sav_sz); sav = open(fullpath_sav, O_RDONLY); if (sav < 0) { @@ -2398,7 +2384,7 @@ static FILE *vty_use_backup_config(const char *fullpath) } fullpath_tmp = malloc(strlen(fullpath) + 8); - sprintf(fullpath_tmp, "%s.XXXXXX", fullpath); + snprintf(fullpath_tmp, strlen(fullpath) + 8, "%s.XXXXXX", fullpath); /* Open file to configuration write. */ tmp = mkstemp(fullpath_tmp); @@ -2549,8 +2535,8 @@ bool vty_read_config(struct nb_config *config, const char *config_file, } /* Small utility function which output log to the VTY. */ -void vty_log(const char *level, const char *proto_str, const char *format, - struct timestamp_control *ctl, va_list va) +void vty_log(const char *level, const char *proto_str, const char *msg, + struct timestamp_control *ctl) { unsigned int i; struct vty *vty; @@ -2560,13 +2546,8 @@ void vty_log(const char *level, const char *proto_str, const char *format, for (i = 0; i < vector_active(vtyvec); i++) if ((vty = vector_slot(vtyvec, i)) != NULL) - if (vty->monitor) { - va_list ac; - va_copy(ac, va); - vty_log_out(vty, level, proto_str, format, ctl, - ac); - va_end(ac); - } + if (vty->monitor) + vty_log_out(vty, level, proto_str, msg, ctl); } /* Async-signal-safe version of vty_log for fixed strings. */ @@ -2601,8 +2582,8 @@ void vty_log_fixed(char *buf, size_t len) int vty_config_enter(struct vty *vty, bool private_config, bool exclusive) { - if (exclusive && !vty_config_exclusive_lock(vty)) { - vty_out(vty, "VTY configuration is locked by other VTY\n"); + if (exclusive && nb_running_lock(NB_CLIENT_CLI, vty)) { + vty_out(vty, "%% Configuration is locked by other client\n"); return CMD_WARNING; } @@ -2628,6 +2609,31 @@ int vty_config_enter(struct vty *vty, bool private_config, bool exclusive) void vty_config_exit(struct vty *vty) { + enum node_type node = vty->node; + struct cmd_node *cnode; + + /* unlock and jump up to ENABLE_NODE if -and only if- we're + * somewhere below CONFIG_NODE */ + while (node && node != CONFIG_NODE) { + cnode = vector_lookup(cmdvec, node); + node = cnode->parent_node; + } + if (node != CONFIG_NODE) + /* called outside config, e.g. vty_close() in ENABLE_NODE */ + return; + + while (vty->node != ENABLE_NODE) + /* will call vty_config_node_exit() below */ + cmd_exit(vty); +} + +int vty_config_node_exit(struct vty *vty) +{ + vty->xpath_index = 0; + + /* Perform pending commit if any. */ + nb_cli_pending_commit_check(vty); + /* Check if there's a pending confirmed commit. */ if (vty->t_confirmed_commit_timeout) { vty_out(vty, @@ -2636,7 +2642,7 @@ void vty_config_exit(struct vty *vty) nb_cli_confirmed_commit_clean(vty); } - vty_config_exclusive_unlock(vty); + (void)nb_running_unlock(NB_CLIENT_CLI, vty); if (vty->candidate_config) { if (vty->private_config) @@ -2649,21 +2655,7 @@ void vty_config_exit(struct vty *vty) } vty->config = false; -} - -int vty_config_exclusive_lock(struct vty *vty) -{ - if (vty_exclusive_lock == NULL) { - vty_exclusive_lock = vty; - return 1; - } - return 0; -} - -void vty_config_exclusive_unlock(struct vty *vty) -{ - if (vty_exclusive_lock == vty) - vty_exclusive_lock = NULL; + return 1; } /* Master of the threads. */ @@ -2686,25 +2678,20 @@ static void vty_event(enum event event, int sock, struct vty *vty) vector_set_index(Vvty_serv_thread, sock, vty_serv_thread); break; case VTYSH_READ: - vty->t_read = NULL; thread_add_read(vty_master, vtysh_read, vty, sock, &vty->t_read); break; case VTYSH_WRITE: - vty->t_write = NULL; thread_add_write(vty_master, vtysh_write, vty, sock, &vty->t_write); break; #endif /* VTYSH */ case VTY_READ: - vty->t_read = NULL; thread_add_read(vty_master, vty_read, vty, sock, &vty->t_read); /* Time out treatment. */ if (vty->v_timeout) { - if (vty->t_timeout) - thread_cancel(vty->t_timeout); - vty->t_timeout = NULL; + THREAD_OFF(vty->t_timeout); thread_add_timer(vty_master, vty_timeout, vty, vty->v_timeout, &vty->t_timeout); } @@ -2714,15 +2701,10 @@ static void vty_event(enum event event, int sock, struct vty *vty) &vty->t_write); break; case VTY_TIMEOUT_RESET: - if (vty->t_timeout) { - thread_cancel(vty->t_timeout); - vty->t_timeout = NULL; - } - if (vty->v_timeout) { - vty->t_timeout = NULL; + THREAD_OFF(vty->t_timeout); + if (vty->v_timeout) thread_add_timer(vty_master, vty_timeout, vty, vty->v_timeout, &vty->t_timeout); - } break; } } @@ -2987,13 +2969,24 @@ DEFUN_NOSH (show_history, } /* vty login. */ -DEFUN (log_commands, +DEFPY (log_commands, log_commands_cmd, - "log commands", + "[no] log commands", + NO_STR "Logging control\n" - "Log all commands (can't be unset without restart)\n") + "Log all commands\n") { - do_log_commands = 1; + if (no) { + if (do_log_commands_perm) { + vty_out(vty, + "Daemon started with permanent logging turned on for commands, ignoring\n"); + return CMD_WARNING; + } + + do_log_commands = false; + } else + do_log_commands = true; + return CMD_SUCCESS; } @@ -3026,8 +3019,13 @@ static int vty_config_write(struct vty *vty) return CMD_SUCCESS; } +static int vty_config_write(struct vty *vty); struct cmd_node vty_node = { - VTY_NODE, "%s(config-line)# ", 1, + .name = "vty", + .node = VTY_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-line)# ", + .config_write = vty_config_write, }; /* Reset all VTY status. */ @@ -3048,30 +3046,22 @@ void vty_reset(void) for (i = 0; i < vector_active(Vvty_serv_thread); i++) if ((vty_serv_thread = vector_slot(Vvty_serv_thread, i)) != NULL) { - thread_cancel(vty_serv_thread); + THREAD_OFF(vty_serv_thread); vector_slot(Vvty_serv_thread, i) = NULL; close(i); } vty_timeout_val = VTY_TIMEOUT_DEFAULT; - if (vty_accesslist_name) { - XFREE(MTYPE_VTY, vty_accesslist_name); - vty_accesslist_name = NULL; - } - - if (vty_ipv6_accesslist_name) { - XFREE(MTYPE_VTY, vty_ipv6_accesslist_name); - vty_ipv6_accesslist_name = NULL; - } + XFREE(MTYPE_VTY, vty_accesslist_name); + XFREE(MTYPE_VTY, vty_ipv6_accesslist_name); } static void vty_save_cwd(void) { - char cwd[MAXPATHLEN]; char *c; - c = getcwd(cwd, MAXPATHLEN); + c = getcwd(vty_cwd, sizeof(vty_cwd)); if (!c) { /* @@ -3085,15 +3075,12 @@ static void vty_save_cwd(void) SYSCONFDIR, errno); exit(-1); } - if (getcwd(cwd, MAXPATHLEN) == NULL) { + if (getcwd(vty_cwd, sizeof(vty_cwd)) == NULL) { flog_err_sys(EC_LIB_SYSTEM_CALL, "Failure to getcwd, errno: %d", errno); exit(-1); } } - - vty_cwd = XMALLOC(MTYPE_TMP, strlen(cwd) + 1); - strcpy(vty_cwd, cwd); } char *vty_get_cwd(void) @@ -3117,7 +3104,7 @@ void vty_init_vtysh(void) } /* Install vty's own commands like `who' command. */ -void vty_init(struct thread_master *master_thread) +void vty_init(struct thread_master *master_thread, bool do_command_logging) { /* For further configuration read, preserve current directory. */ vty_save_cwd(); @@ -3132,7 +3119,7 @@ void vty_init(struct thread_master *master_thread) Vvty_serv_thread = vector_init(VECTOR_MIN_SIZE); /* Install bgp top node. */ - install_node(&vty_node, vty_config_write); + install_node(&vty_node); install_element(VIEW_NODE, &config_who_cmd); install_element(VIEW_NODE, &show_history_cmd); @@ -3141,6 +3128,12 @@ void vty_init(struct thread_master *master_thread) install_element(CONFIG_NODE, &no_service_advanced_vty_cmd); install_element(CONFIG_NODE, &show_history_cmd); install_element(CONFIG_NODE, &log_commands_cmd); + + if (do_command_logging) { + do_log_commands = true; + do_log_commands_perm = true; + } + install_element(ENABLE_NODE, &terminal_monitor_cmd); install_element(ENABLE_NODE, &terminal_no_monitor_cmd); install_element(ENABLE_NODE, &no_terminal_monitor_cmd); @@ -3159,7 +3152,7 @@ void vty_init(struct thread_master *master_thread) void vty_terminate(void) { - XFREE(MTYPE_TMP, vty_cwd); + memset(vty_cwd, 0x00, sizeof(vty_cwd)); if (vtyvec && Vvty_serv_thread) { vty_reset(); diff --git a/lib/vty.h b/lib/vty.h index 9c2653e1ac..623f97ab03 100644 --- a/lib/vty.h +++ b/lib/vty.h @@ -22,7 +22,11 @@ #define _ZEBRA_VTY_H #include +#ifdef HAVE_LIBPCREPOSIX +#include +#else #include +#endif /* HAVE_LIBPCREPOSIX */ #include "thread.h" #include "log.h" @@ -39,7 +43,7 @@ extern "C" { #define VTY_MAXHIST 20 #define VTY_MAXDEPTH 8 -#define VTY_MAXCFGCHANGES 8 +#define VTY_MAXCFGCHANGES 16 struct vty_error { char error_buf[VTY_BUFSIZ]; @@ -130,6 +134,14 @@ struct vty { /* Base candidate configuration. */ struct nb_config *candidate_config_base; + /* Dynamic transaction information. */ + struct timeval backoff_start; + size_t backoff_cmd_count; + struct thread *t_pending_commit; + char *pending_cmds_buf; + size_t pending_cmds_buflen; + size_t pending_cmds_bufpos; + /* Confirmed-commit timeout and rollback configuration. */ struct thread *t_confirmed_commit_timeout; struct nb_config *confirmed_commit_rollback; @@ -254,7 +266,8 @@ static inline void vty_push_context(struct vty *vty, int node, uint64_t id) #define VTY_CHECK_XPATH \ do { \ - if (vty->xpath_index > 0 \ + if (vty->type != VTY_FILE && !vty->private_config \ + && vty->xpath_index > 0 \ && !yang_dnode_exists(vty->candidate_config->dnode, \ VTY_CURR_XPATH)) { \ vty_out(vty, \ @@ -289,11 +302,8 @@ struct vty_arg { #define IS_DIRECTORY_SEP(c) ((c) == DIRECTORY_SEP) #endif -/* Exported variables */ -extern struct vty *vty_exclusive_lock; - /* Prototypes. */ -extern void vty_init(struct thread_master *); +extern void vty_init(struct thread_master *, bool do_command_logging); extern void vty_init_vtysh(void); extern void vty_terminate(void); extern void vty_reset(void); @@ -305,8 +315,8 @@ extern struct vty *vty_stdio(void (*atclose)(int isexit)); * - vty_endframe() clears the buffer without printing it, and prints an * extra string if the buffer was empty before (for context-end markers) */ -extern int vty_out(struct vty *, const char *, ...) PRINTF_ATTRIBUTE(2, 3); -extern void vty_frame(struct vty *, const char *, ...) PRINTF_ATTRIBUTE(2, 3); +extern int vty_out(struct vty *, const char *, ...) PRINTFRR(2, 3); +extern void vty_frame(struct vty *, const char *, ...) PRINTFRR(2, 3); extern void vty_endframe(struct vty *, const char *); bool vty_set_include(struct vty *vty, const char *regexp); @@ -316,13 +326,12 @@ extern void vty_time_print(struct vty *, int); extern void vty_serv_sock(const char *, unsigned short, const char *); extern void vty_close(struct vty *); extern char *vty_get_cwd(void); -extern void vty_log(const char *level, const char *proto, const char *fmt, - struct timestamp_control *, va_list); +extern void vty_log(const char *level, const char *proto, const char *msg, + struct timestamp_control *); extern int vty_config_enter(struct vty *vty, bool private_config, bool exclusive); extern void vty_config_exit(struct vty *); -extern int vty_config_exclusive_lock(struct vty *vty); -extern void vty_config_exclusive_unlock(struct vty *vty); +extern int vty_config_node_exit(struct vty *); extern int vty_shell(struct vty *); extern int vty_shell_serv(struct vty *); extern void vty_hello(struct vty *); diff --git a/lib/wheel.c b/lib/wheel.c index 69d2fa48dc..f5e5cc52c3 100644 --- a/lib/wheel.c +++ b/lib/wheel.c @@ -47,8 +47,8 @@ static int wheel_timer_thread_helper(struct thread *t) curr_slot = wheel->curr_slot % wheel->slots; if (debug_timer_wheel) - zlog_debug("%s: Wheel Slot: %lld(%lld) count: %d", - __PRETTY_FUNCTION__, wheel->curr_slot, curr_slot, + zlog_debug("%s: Wheel Slot: %lld(%lld) count: %d", __func__, + wheel->curr_slot, curr_slot, listcount(wheel->wheel_slot_lists[curr_slot])); for (ALL_LIST_ELEMENTS(wheel->wheel_slot_lists[curr_slot], node, @@ -80,7 +80,7 @@ static int wheel_timer_thread(struct thread *t) } struct timer_wheel *wheel_init(struct thread_master *master, int period, - size_t slots, unsigned int (*slot_key)(void *), + size_t slots, unsigned int (*slot_key)(const void *), void (*slot_run)(void *), const char *run_name) { @@ -146,8 +146,8 @@ int wheel_add_item(struct timer_wheel *wheel, void *item) slot = (*wheel->slot_key)(item); if (debug_timer_wheel) - zlog_debug("%s: Inserting %p: %lld %lld", __PRETTY_FUNCTION__, - item, slot, slot % wheel->slots); + zlog_debug("%s: Inserting %p: %lld %lld", __func__, item, slot, + slot % wheel->slots); listnode_add(wheel->wheel_slot_lists[slot % wheel->slots], item); return 0; @@ -160,8 +160,8 @@ int wheel_remove_item(struct timer_wheel *wheel, void *item) slot = (*wheel->slot_key)(item); if (debug_timer_wheel) - zlog_debug("%s: Removing %p: %lld %lld", __PRETTY_FUNCTION__, - item, slot, slot % wheel->slots); + zlog_debug("%s: Removing %p: %lld %lld", __func__, item, slot, + slot % wheel->slots); listnode_delete(wheel->wheel_slot_lists[slot % wheel->slots], item); return 0; diff --git a/lib/wheel.h b/lib/wheel.h index e66751c1d0..401789e1a0 100644 --- a/lib/wheel.h +++ b/lib/wheel.h @@ -38,7 +38,7 @@ struct timer_wheel { /* * Key to determine what slot the item belongs in */ - unsigned int (*slot_key)(void *); + unsigned int (*slot_key)(const void *); void (*slot_run)(void *); }; @@ -80,9 +80,9 @@ struct timer_wheel { * of running your code. */ struct timer_wheel *wheel_init(struct thread_master *master, int period, - size_t slots, unsigned int (*slot_key)(void *), - void (*slot_run)(void *), - const char *run_name); + size_t slots, + unsigned int (*slot_key)(const void *), + void (*slot_run)(void *), const char *run_name); /* * Delete the specified timer wheel created diff --git a/lib/yang.c b/lib/yang.c index 2a2c155dee..910409f2cc 100644 --- a/lib/yang.c +++ b/lib/yang.c @@ -27,8 +27,8 @@ #include -DEFINE_MTYPE(LIB, YANG_MODULE, "YANG module") -DEFINE_MTYPE(LIB, YANG_DATA, "YANG data structure") +DEFINE_MTYPE_STATIC(LIB, YANG_MODULE, "YANG module") +DEFINE_MTYPE_STATIC(LIB, YANG_DATA, "YANG data structure") /* libyang container. */ struct ly_ctx *ly_native_ctx; @@ -53,31 +53,47 @@ static const char *yang_module_imp_clb(const char *mod_name, { struct yang_module_embed *e; - if (submod_name || submod_rev) - return NULL; - for (e = embeds; e; e = e->next) { - if (strcmp(e->mod_name, mod_name)) - continue; - if (mod_rev && strcmp(e->mod_rev, mod_rev)) - continue; + if (e->sub_mod_name && submod_name) { + if (strcmp(e->sub_mod_name, submod_name)) + continue; + + if (submod_rev && strcmp(e->sub_mod_rev, submod_rev)) + continue; + } else { + if (strcmp(e->mod_name, mod_name)) + continue; + + if (mod_rev && strcmp(e->mod_rev, mod_rev)) + continue; + } *format = e->format; return e->data; } - flog_warn(EC_LIB_YANG_MODULE_LOAD, - "YANG model \"%s@%s\" not embedded, trying external file", - mod_name, mod_rev ? mod_rev : "*"); + flog_warn( + EC_LIB_YANG_MODULE_LOAD, + "YANG model \"%s@%s\" \"%s@%s\"not embedded, trying external file", + mod_name, mod_rev ? mod_rev : "*", + submod_name ? submod_name : "*", submod_rev ? submod_rev : "*"); return NULL; } +/* clang-format off */ static const char *const frr_native_modules[] = { "frr-interface", + "frr-vrf", + "frr-routing", + "frr-route-map", + "frr-nexthop", "frr-ripd", "frr-ripngd", "frr-isisd", + "frr-vrrpd", + "frr-zebra", }; +/* clang-format on */ /* Generate the yang_modules tree. */ static inline int yang_module_compare(const struct yang_module *a, @@ -443,6 +459,32 @@ bool yang_dnode_exists(const struct lyd_node *dnode, const char *xpath_fmt, ...) return found; } +void yang_dnode_iterate(yang_dnode_iter_cb cb, void *arg, + const struct lyd_node *dnode, const char *xpath_fmt, + ...) +{ + va_list ap; + char xpath[XPATH_MAXLEN]; + struct ly_set *set; + + va_start(ap, xpath_fmt); + vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap); + va_end(ap); + + set = lyd_find_path(dnode, xpath); + assert(set); + for (unsigned int i = 0; i < set->number; i++) { + int ret; + + dnode = set->set.d[i]; + ret = (*cb)(dnode, arg); + if (ret == YANG_ITER_STOP) + break; + } + + ly_set_free(set); +} + bool yang_dnode_is_default(const struct lyd_node *dnode, const char *xpath_fmt, ...) { @@ -595,7 +637,7 @@ struct yang_data *yang_data_list_find(const struct list *list, /* Make libyang log its errors using FRR logging infrastructure. */ static void ly_log_cb(LY_LOG_LEVEL level, const char *msg, const char *path) { - int priority; + int priority = LOG_ERR; switch (level) { case LY_LLERR: @@ -605,10 +647,9 @@ static void ly_log_cb(LY_LOG_LEVEL level, const char *msg, const char *path) priority = LOG_WARNING; break; case LY_LLVRB: + case LY_LLDBG: priority = LOG_DEBUG; break; - default: - return; } if (path) @@ -617,15 +658,46 @@ static void ly_log_cb(LY_LOG_LEVEL level, const char *msg, const char *path) zlog(priority, "libyang: %s", msg); } -#if CONFDATE > 20190401 -CPP_NOTICE("lib/yang: time to remove non-LIBYANG_EXT_BUILTIN support") -#endif +const char *yang_print_errors(struct ly_ctx *ly_ctx, char *buf, size_t buf_len) +{ + struct ly_err_item *ei; + const char *path; + + ei = ly_err_first(ly_ctx); + if (!ei) + return ""; + + strlcpy(buf, "YANG error(s):\n", buf_len); + for (; ei; ei = ei->next) { + strlcat(buf, " ", buf_len); + strlcat(buf, ei->msg, buf_len); + strlcat(buf, "\n", buf_len); + } -#ifdef LIBYANG_EXT_BUILTIN -extern struct lytype_plugin_list frr_user_types[]; -#endif + path = ly_errpath(ly_ctx); + if (path) { + strlcat(buf, " YANG path: ", buf_len); + strlcat(buf, path, buf_len); + strlcat(buf, "\n", buf_len); + } + + ly_err_clean(ly_ctx, NULL); + + return buf; +} -struct ly_ctx *yang_ctx_new_setup(void) +void yang_debugging_set(bool enable) +{ + if (enable) { + ly_verb(LY_LLDBG); + ly_verb_dbg(0xFF); + } else { + ly_verb(LY_LLERR); + ly_verb_dbg(0); + } +} + +struct ly_ctx *yang_ctx_new_setup(bool embedded_modules) { struct ly_ctx *ctx; const char *yang_models_path = YANG_MODELS_PATH; @@ -644,60 +716,26 @@ struct ly_ctx *yang_ctx_new_setup(void) ctx = ly_ctx_new(yang_models_path, LY_CTX_DISABLE_SEARCHDIR_CWD); if (!ctx) return NULL; - ly_ctx_set_module_imp_clb(ctx, yang_module_imp_clb, NULL); + + if (embedded_modules) + ly_ctx_set_module_imp_clb(ctx, yang_module_imp_clb, NULL); + return ctx; } -void yang_init(void) +void yang_init(bool embedded_modules) { -#ifndef LIBYANG_EXT_BUILTIN -CPP_NOTICE("lib/yang: deprecated libyang <0.16.74 extension loading in use!") - static char ly_plugin_dir[PATH_MAX]; - const char *const *ly_loaded_plugins; - const char *ly_plugin; - bool found_ly_frr_types = false; - - /* Tell libyang where to find its plugins. */ - snprintf(ly_plugin_dir, sizeof(ly_plugin_dir), "%s=%s", - "LIBYANG_USER_TYPES_PLUGINS_DIR", LIBYANG_PLUGINS_PATH); - putenv(ly_plugin_dir); -#endif - /* Initialize libyang global parameters that affect all containers. */ ly_set_log_clb(ly_log_cb, 1); ly_log_options(LY_LOLOG | LY_LOSTORE); -#ifdef LIBYANG_EXT_BUILTIN - if (ly_register_types(frr_user_types, "frr_user_types")) { - flog_err(EC_LIB_LIBYANG_PLUGIN_LOAD, - "ly_register_types() failed"); - exit(1); - } -#endif - /* Initialize libyang container for native models. */ - ly_native_ctx = yang_ctx_new_setup(); + ly_native_ctx = yang_ctx_new_setup(embedded_modules); if (!ly_native_ctx) { flog_err(EC_LIB_LIBYANG, "%s: ly_ctx_new() failed", __func__); exit(1); } -#ifndef LIBYANG_EXT_BUILTIN - /* Detect if the required libyang plugin(s) were loaded successfully. */ - ly_loaded_plugins = ly_get_loaded_plugins(); - for (size_t i = 0; (ly_plugin = ly_loaded_plugins[i]); i++) { - if (strmatch(ly_plugin, "frr_user_types")) { - found_ly_frr_types = true; - break; - } - } - if (!found_ly_frr_types) { - flog_err(EC_LIB_LIBYANG_PLUGIN_LOAD, - "%s: failed to load frr_user_types.so", __func__); - exit(1); - } -#endif - yang_translator_init(); } @@ -722,3 +760,147 @@ void yang_terminate(void) ly_ctx_destroy(ly_native_ctx, NULL); } + +const struct lyd_node *yang_dnode_get_parent(const struct lyd_node *dnode, + const char *name) +{ + const struct lyd_node *orig_dnode = dnode; + + while (orig_dnode) { + switch (orig_dnode->schema->nodetype) { + case LYS_LIST: + case LYS_CONTAINER: + if (!strcmp(orig_dnode->schema->name, name)) + return orig_dnode; + break; + default: + break; + } + + orig_dnode = orig_dnode->parent; + } + + return NULL; +} + +/* API to check if the given node is last node in the list */ +static bool yang_is_last_list_dnode(const struct lyd_node *dnode) +{ + return (((dnode->next == NULL) + || (dnode->next + && (strcmp(dnode->next->schema->name, dnode->schema->name) + != 0))) + && dnode->prev + && ((dnode->prev == dnode) + || (strcmp(dnode->prev->schema->name, dnode->schema->name) + != 0))); +} + +/* API to check if the given node is last node in the data tree level */ +static bool yang_is_last_level_dnode(const struct lyd_node *dnode) +{ + const struct lyd_node *parent; + const struct lys_node_list *snode; + const struct lyd_node *key_leaf; + uint8_t keys_size; + + switch (dnode->schema->nodetype) { + case LYS_LIST: + assert(dnode->parent); + parent = dnode->parent; + snode = (struct lys_node_list *)parent->schema; + key_leaf = dnode->prev; + for (keys_size = 1; keys_size < snode->keys_size; keys_size++) + key_leaf = key_leaf->prev; + if (key_leaf->prev == dnode) + return true; + break; + case LYS_CONTAINER: + return true; + default: + break; + } + + return false; +} + + +const struct lyd_node * +yang_get_subtree_with_no_sibling(const struct lyd_node *dnode) +{ + bool parent = true; + const struct lyd_node *node; + const struct lys_node_container *snode; + + node = dnode; + if (node->schema->nodetype != LYS_LIST) + return node; + + while (parent) { + switch (node->schema->nodetype) { + case LYS_CONTAINER: + snode = (struct lys_node_container *)node->schema; + if ((!snode->presence) + && yang_is_last_level_dnode(node)) { + if (node->parent + && (node->parent->schema->module + == dnode->schema->module)) + node = node->parent; + else + parent = false; + } else + parent = false; + break; + case LYS_LIST: + if (yang_is_last_list_dnode(node) + && yang_is_last_level_dnode(node)) { + if (node->parent + && (node->parent->schema->module + == dnode->schema->module)) + node = node->parent; + else + parent = false; + } else + parent = false; + break; + default: + parent = false; + break; + } + } + return node; +} + +uint32_t yang_get_list_pos(const struct lyd_node *node) +{ + return lyd_list_pos(node); +} + +uint32_t yang_get_list_elements_count(const struct lyd_node *node) +{ + unsigned int count; + struct lys_node *schema; + + if (!node + || ((node->schema->nodetype != LYS_LIST) + && (node->schema->nodetype != LYS_LEAFLIST))) { + return 0; + } + + schema = node->schema; + count = 0; + do { + if (node->schema == schema) + ++count; + node = node->next; + } while (node); + return count; +} + + +const struct lyd_node *yang_dnode_get_child(const struct lyd_node *dnode) +{ + if (dnode) + return dnode->child; + return NULL; +} diff --git a/lib/yang.h b/lib/yang.h index 6f8c84ab64..94bbed233d 100644 --- a/lib/yang.h +++ b/lib/yang.h @@ -33,11 +33,8 @@ extern "C" { #endif -DECLARE_MTYPE(YANG_MODULE) -DECLARE_MTYPE(YANG_DATA) - /* Maximum XPath length. */ -#define XPATH_MAXLEN 256 +#define XPATH_MAXLEN 1024 /* Maximum list key length. */ #define LIST_MAXKEYS 8 @@ -51,6 +48,8 @@ DECLARE_MTYPE(YANG_DATA) struct yang_module_embed { struct yang_module_embed *next; const char *mod_name, *mod_rev; + const char *sub_mod_name; + const char *sub_mod_rev; const char *data; LYS_INFORMAT format; }; @@ -64,6 +63,7 @@ struct yang_module { #endif #ifdef HAVE_SYSREPO sr_subscription_ctx_t *sr_subscription; + struct thread *sr_thread; #endif }; RB_HEAD(yang_modules, yang_module); @@ -110,6 +110,9 @@ enum yang_iter_flags { /* Callback used by the yang_snodes_iterate_*() family of functions. */ typedef int (*yang_iterate_cb)(const struct lys_node *snode, void *arg); +/* Callback used by the yang_dnode_iterate() function. */ +typedef int (*yang_dnode_iter_cb)(const struct lyd_node *dnode, void *arg); + /* Return values of the 'yang_iterate_cb' callback. */ #define YANG_ITER_CONTINUE 0 #define YANG_ITER_STOP -1 @@ -360,6 +363,25 @@ extern struct lyd_node *yang_dnode_get(const struct lyd_node *dnode, extern bool yang_dnode_exists(const struct lyd_node *dnode, const char *xpath_fmt, ...); +/* + * Iterate over all libyang data nodes that satisfy an XPath query. + * + * cb + * Function to call with each data node. + * + * arg + * Arbitrary argument passed as the second parameter in each call to 'cb'. + * + * dnode + * Base libyang data node to operate on. + * + * xpath_fmt + * XPath expression (absolute or relative). + */ +void yang_dnode_iterate(yang_dnode_iter_cb cb, void *arg, + const struct lyd_node *dnode, const char *xpath_fmt, + ...); + /* * Check if the libyang data node contains a default value. Non-presence * containers are assumed to always contain a default value. @@ -485,14 +507,46 @@ extern struct yang_data *yang_data_list_find(const struct list *list, /* * Create and set up a libyang context (for use by the translator) + * + * embedded_modules + * Specify whether libyang should attempt to look for embedded YANG modules. + */ +extern struct ly_ctx *yang_ctx_new_setup(bool embedded_modules); + +/* + * Enable or disable libyang verbose debugging. + * + * enable + * When set to true, enable libyang verbose debugging, otherwise disable it. */ -extern struct ly_ctx *yang_ctx_new_setup(void); +extern void yang_debugging_set(bool enable); + +/* + * Print libyang error messages into the provided buffer. + * + * ly_ctx + * libyang context to operate on. + * + * buf + * Buffer to store the libyang error messages. + * + * buf_len + * Size of buf. + * + * Returns: + * The provided buffer. + */ +extern const char *yang_print_errors(struct ly_ctx *ly_ctx, char *buf, + size_t buf_len); /* * Initialize the YANG subsystem. Should be called only once during the * daemon initialization process. + * + * embedded_modules + * Specify whether libyang should attempt to look for embedded YANG modules. */ -extern void yang_init(void); +extern void yang_init(bool embedded_modules); /* * Finish the YANG subsystem gracefully. Should be called only when the daemon @@ -500,6 +554,57 @@ extern void yang_init(void); */ extern void yang_terminate(void); +/* + * API to return the parent dnode having a given schema-node name + * Use case: One has to access the parent dnode's private pointer + * for a given child node. + * For that there is a need to find parent dnode first. + * + * dnode The starting node to work on + * + * name The name of container/list schema-node + * + * Returns The dnode matched with the given name + */ +extern const struct lyd_node * +yang_dnode_get_parent(const struct lyd_node *dnode, const char *name); + + +/* + * In some cases there is a need to auto delete the parent nodes + * if the given node is last in the list. + * It tries to delete all the parents in a given tree in a given module. + * The use case is with static routes and route maps + * example : ip route 1.1.1.1/32 ens33 + * ip route 1.1.1.1/32 ens34 + * After this no ip route 1.1.1.1/32 ens34 came, now staticd + * has to find out upto which level it has to delete the dnodes. + * For this case it has to send delete nexthop + * After this no ip route 1.1.1.1/32 ens33 came, now staticd has to + * clear nexthop, path and route nodes. + * The same scheme is required for routemaps also + * dnode The starting node to work on + * + * Returns The final parent node selected for deletion + */ +extern const struct lyd_node * +yang_get_subtree_with_no_sibling(const struct lyd_node *dnode); + +/* To get the relative position of a node in list */ +extern uint32_t yang_get_list_pos(const struct lyd_node *node); + +/* To get the number of elements in a list + * + * dnode : The head of list + * Returns : The number of dnodes present in the list + */ +extern uint32_t yang_get_list_elements_count(const struct lyd_node *node); + + +/* To get the immediate child of a dnode */ +const struct lyd_node *yang_dnode_get_child(const struct lyd_node *dnode); + + #ifdef __cplusplus } #endif diff --git a/lib/yang_translator.c b/lib/yang_translator.c index 76a6cc5fd1..7dbb1f3f1a 100644 --- a/lib/yang_translator.c +++ b/lib/yang_translator.c @@ -24,6 +24,7 @@ #include "hash.h" #include "yang.h" #include "yang_translator.h" +#include "frrstr.h" DEFINE_MTYPE_STATIC(LIB, YANG_TRANSLATOR, "YANG Translator") DEFINE_MTYPE_STATIC(LIB, YANG_TRANSLATOR_MODULE, "YANG Translator Module") @@ -45,8 +46,6 @@ static struct ly_ctx *ly_translator_ctx; static unsigned int yang_translator_validate(struct yang_translator *translator); static unsigned int yang_module_nodes_count(const struct lys_module *module); -static void str_replace(char *o_string, const char *s_string, - const char *r_string); struct yang_mapping_node { char xpath_from_canonical[XPATH_MAXLEN]; @@ -62,7 +61,7 @@ static bool yang_mapping_hash_cmp(const void *value1, const void *value2) return strmatch(c1->xpath_from_canonical, c2->xpath_from_canonical); } -static unsigned int yang_mapping_hash_key(void *value) +static unsigned int yang_mapping_hash_key(const void *value) { return string_hash_make(value); } @@ -108,14 +107,24 @@ static void yang_mapping_add(struct yang_translator *translator, int dir, sizeof(mapping->xpath_from_fmt)); strlcpy(mapping->xpath_to_fmt, xpath_to_fmt, sizeof(mapping->xpath_to_fmt)); - str_replace(mapping->xpath_from_fmt, "KEY1", "%[^']"); - str_replace(mapping->xpath_from_fmt, "KEY2", "%[^']"); - str_replace(mapping->xpath_from_fmt, "KEY3", "%[^']"); - str_replace(mapping->xpath_from_fmt, "KEY4", "%[^']"); - str_replace(mapping->xpath_to_fmt, "KEY1", "%s"); - str_replace(mapping->xpath_to_fmt, "KEY2", "%s"); - str_replace(mapping->xpath_to_fmt, "KEY3", "%s"); - str_replace(mapping->xpath_to_fmt, "KEY4", "%s"); + + const char *keys[] = {"KEY1", "KEY2", "KEY3", "KEY4"}; + char *xpfmt; + + for (unsigned int i = 0; i < array_size(keys); i++) { + xpfmt = frrstr_replace(mapping->xpath_from_fmt, keys[i], + "%[^']"); + strlcpy(mapping->xpath_from_fmt, xpfmt, + sizeof(mapping->xpath_from_fmt)); + XFREE(MTYPE_TMP, xpfmt); + } + + for (unsigned int i = 0; i < array_size(keys); i++) { + xpfmt = frrstr_replace(mapping->xpath_to_fmt, keys[i], "%s"); + strlcpy(mapping->xpath_to_fmt, xpfmt, + sizeof(mapping->xpath_to_fmt)); + XFREE(MTYPE_TMP, xpfmt); + } } struct yang_translator *yang_translator_load(const char *path) @@ -162,7 +171,7 @@ struct yang_translator *yang_translator_load(const char *path) RB_INSERT(yang_translators, &yang_translators, translator); /* Initialize the translator libyang context. */ - translator->ly_ctx = yang_ctx_new_setup(); + translator->ly_ctx = yang_ctx_new_setup(false); if (!translator->ly_ctx) { flog_warn(EC_LIB_LIBYANG, "%s: ly_ctx_new() failed", __func__); goto error; @@ -500,31 +509,9 @@ static unsigned int yang_module_nodes_count(const struct lys_module *module) return total; } -/* TODO: rewrite this function. */ -static void str_replace(char *o_string, const char *s_string, - const char *r_string) -{ - char buffer[BUFSIZ]; - char *ch; - - ch = strstr(o_string, s_string); - if (!ch) - return; - - memcpy(buffer, o_string, ch - o_string); - buffer[ch - o_string] = 0; - - sprintf(buffer + (ch - o_string), "%s%s", r_string, - ch + strlen(s_string)); - - o_string[0] = 0; - strcpy(o_string, buffer); - return str_replace(o_string, s_string, r_string); -} - void yang_translator_init(void) { - ly_translator_ctx = yang_ctx_new_setup(); + ly_translator_ctx = yang_ctx_new_setup(true); if (!ly_translator_ctx) { flog_err(EC_LIB_LIBYANG, "%s: ly_ctx_new() failed", __func__); exit(1); diff --git a/lib/yang_wrappers.c b/lib/yang_wrappers.c index 7ecea5f445..4c658c1bfb 100644 --- a/lib/yang_wrappers.c +++ b/lib/yang_wrappers.c @@ -22,6 +22,9 @@ #include "log.h" #include "lib_errors.h" #include "northbound.h" +#include "printfrr.h" +#include "nexthop.h" +#include "printfrr.h" static const char *yang_get_default_value(const char *xpath) { @@ -443,7 +446,7 @@ struct yang_data *yang_data_new_int64(const char *xpath, int64_t value) { char value_str[BUFSIZ]; - snprintf(value_str, sizeof(value_str), "%" PRId64, value); + snprintfrr(value_str, sizeof(value_str), "%" PRId64, value); return yang_data_new(xpath, value_str); } @@ -651,7 +654,7 @@ struct yang_data *yang_data_new_uint64(const char *xpath, uint64_t value) { char value_str[BUFSIZ]; - snprintf(value_str, sizeof(value_str), "%" PRIu64, value); + snprintfrr(value_str, sizeof(value_str), "%" PRIu64, value); return yang_data_new(xpath, value_str); } @@ -781,6 +784,97 @@ void yang_get_default_string_buf(char *buf, size_t size, const char *xpath_fmt, xpath); } +/* + * Primitive type: empty. + */ +struct yang_data *yang_data_new_empty(const char *xpath) +{ + return yang_data_new(xpath, NULL); +} + +bool yang_dnode_get_empty(const struct lyd_node *dnode, const char *xpath_fmt, + ...) +{ + va_list ap; + char xpath[XPATH_MAXLEN]; + const struct lyd_node_leaf_list *dleaf; + + assert(dnode); + + va_start(ap, xpath_fmt); + vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap); + va_end(ap); + + dnode = yang_dnode_get(dnode, xpath); + if (dnode) { + dleaf = (const struct lyd_node_leaf_list *)dnode; + if (dleaf->value_type == LY_TYPE_EMPTY) + return true; + } + + return false; +} + +/* + * Derived type: IP prefix. + */ +void yang_str2prefix(const char *value, union prefixptr prefix) +{ + (void)str2prefix(value, prefix.p); + apply_mask(prefix.p); +} + +struct yang_data *yang_data_new_prefix(const char *xpath, + union prefixconstptr prefix) +{ + char value_str[PREFIX2STR_BUFFER]; + + (void)prefix2str(prefix.p, value_str, sizeof(value_str)); + return yang_data_new(xpath, value_str); +} + +void yang_dnode_get_prefix(struct prefix *prefix, const struct lyd_node *dnode, + const char *xpath_fmt, ...) +{ + const struct lyd_node_leaf_list *dleaf; + + assert(dnode); + if (xpath_fmt) { + va_list ap; + char xpath[XPATH_MAXLEN]; + + va_start(ap, xpath_fmt); + vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap); + va_end(ap); + dnode = yang_dnode_get(dnode, xpath); + YANG_DNODE_GET_ASSERT(dnode, xpath); + } + + /* + * Initialize prefix to avoid static analyzer complaints about + * uninitialized memory. + */ + memset(prefix, 0, sizeof(*prefix)); + + dleaf = (const struct lyd_node_leaf_list *)dnode; + assert(dleaf->value_type == LY_TYPE_STRING); + (void)str2prefix(dleaf->value_str, prefix); +} + +void yang_get_default_prefix(union prefixptr var, const char *xpath_fmt, ...) +{ + char xpath[XPATH_MAXLEN]; + const char *value; + va_list ap; + + va_start(ap, xpath_fmt); + vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap); + va_end(ap); + + value = yang_get_default_value(xpath); + yang_str2prefix(value, var); +} + /* * Derived type: ipv4. */ @@ -817,7 +911,7 @@ void yang_dnode_get_ipv4(struct in_addr *addr, const struct lyd_node *dnode, dleaf = (const struct lyd_node_leaf_list *)dnode; assert(dleaf->value_type == LY_TYPE_STRING); - memcpy(addr, dleaf->value.ptr, sizeof(*addr)); + (void)inet_pton(AF_INET, dleaf->value_str, addr); } void yang_get_default_ipv4(struct in_addr *var, const char *xpath_fmt, ...) @@ -874,7 +968,7 @@ void yang_dnode_get_ipv4p(union prefixptr prefix, const struct lyd_node *dnode, dleaf = (const struct lyd_node_leaf_list *)dnode; assert(dleaf->value_type == LY_TYPE_STRING); - memcpy(prefix4, dleaf->value.ptr, sizeof(*prefix4)); + (void)str2prefix_ipv4(dleaf->value_str, prefix4); } void yang_get_default_ipv4p(union prefixptr var, const char *xpath_fmt, ...) @@ -927,7 +1021,7 @@ void yang_dnode_get_ipv6(struct in6_addr *addr, const struct lyd_node *dnode, dleaf = (const struct lyd_node_leaf_list *)dnode; assert(dleaf->value_type == LY_TYPE_STRING); - memcpy(addr, dleaf->value.ptr, sizeof(*addr)); + (void)inet_pton(AF_INET6, dleaf->value_str, addr); } void yang_get_default_ipv6(struct in6_addr *var, const char *xpath_fmt, ...) @@ -984,7 +1078,7 @@ void yang_dnode_get_ipv6p(union prefixptr prefix, const struct lyd_node *dnode, dleaf = (const struct lyd_node_leaf_list *)dnode; assert(dleaf->value_type == LY_TYPE_STRING); - memcpy(prefix6, dleaf->value.ptr, sizeof(*prefix6)); + (void)str2prefix_ipv6(dleaf->value_str, prefix6); } void yang_get_default_ipv6p(union prefixptr var, const char *xpath_fmt, ...) @@ -1000,3 +1094,183 @@ void yang_get_default_ipv6p(union prefixptr var, const char *xpath_fmt, ...) value = yang_get_default_value(xpath); yang_str2ipv6p(value, var); } + +/* + * Derived type: ip. + */ +void yang_str2ip(const char *value, struct ipaddr *ip) +{ + (void)str2ipaddr(value, ip); +} + +struct yang_data *yang_data_new_ip(const char *xpath, const struct ipaddr *addr) +{ + size_t sz = IS_IPADDR_V4(addr) ? INET_ADDRSTRLEN : INET6_ADDRSTRLEN; + char value_str[sz]; + + ipaddr2str(addr, value_str, sizeof(value_str)); + return yang_data_new(xpath, value_str); +} + +void yang_dnode_get_ip(struct ipaddr *addr, const struct lyd_node *dnode, + const char *xpath_fmt, ...) +{ + const struct lyd_node_leaf_list *dleaf; + + assert(dnode); + if (xpath_fmt) { + va_list ap; + char xpath[XPATH_MAXLEN]; + + va_start(ap, xpath_fmt); + vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap); + va_end(ap); + dnode = yang_dnode_get(dnode, xpath); + YANG_DNODE_GET_ASSERT(dnode, xpath); + } + + dleaf = (const struct lyd_node_leaf_list *)dnode; + assert(dleaf->value_type == LY_TYPE_STRING); + (void)str2ipaddr(dleaf->value_str, addr); +} + +void yang_get_default_ip(struct ipaddr *var, const char *xpath_fmt, ...) +{ + char xpath[XPATH_MAXLEN]; + const char *value; + va_list ap; + + va_start(ap, xpath_fmt); + vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap); + va_end(ap); + + value = yang_get_default_value(xpath); + yang_str2ip(value, var); +} + +struct yang_data *yang_data_new_mac(const char *xpath, + const struct ethaddr *mac) +{ + char value_str[ETHER_ADDR_STRLEN]; + + prefix_mac2str(mac, value_str, sizeof(value_str)); + return yang_data_new(xpath, value_str); +} + +void yang_str2mac(const char *value, struct ethaddr *mac) +{ + (void)prefix_str2mac(value, mac); +} + +struct yang_data *yang_data_new_date_and_time(const char *xpath, time_t time) +{ + struct tm tm; + char timebuf[MONOTIME_STRLEN]; + struct timeval _time, time_real; + char *ts_dot; + uint16_t buflen; + + _time.tv_sec = time; + _time.tv_usec = 0; + monotime_to_realtime(&_time, &time_real); + + gmtime_r(&time_real.tv_sec, &tm); + + /* rfc-3339 format */ + strftime(timebuf, sizeof(timebuf), "%Y-%m-%dT%H:%M:%S", &tm); + buflen = strlen(timebuf); + ts_dot = timebuf + buflen; + + /* microseconds and appends Z */ + snprintfrr(ts_dot, sizeof(timebuf) - buflen, ".%06luZ", + (unsigned long)time_real.tv_usec); + + return yang_data_new(xpath, timebuf); +} + +const char *yang_nexthop_type2str(uint32_t ntype) +{ + switch (ntype) { + case NEXTHOP_TYPE_IFINDEX: + return "ifindex"; + break; + case NEXTHOP_TYPE_IPV4: + return "ip4"; + break; + case NEXTHOP_TYPE_IPV4_IFINDEX: + return "ip4-ifindex"; + break; + case NEXTHOP_TYPE_IPV6: + return "ip6"; + break; + case NEXTHOP_TYPE_IPV6_IFINDEX: + return "ip6-ifindex"; + break; + case NEXTHOP_TYPE_BLACKHOLE: + return "blackhole"; + break; + default: + return "unknown"; + break; + } +} + + +const char *yang_afi_safi_value2identity(afi_t afi, safi_t safi) +{ + if (afi == AFI_IP && safi == SAFI_UNICAST) + return "frr-routing:ipv4-unicast"; + if (afi == AFI_IP6 && safi == SAFI_UNICAST) + return "frr-routing:ipv6-unicast"; + if (afi == AFI_IP && safi == SAFI_MULTICAST) + return "frr-routing:ipv4-multicast"; + if (afi == AFI_IP6 && safi == SAFI_MULTICAST) + return "frr-routing:ipv6-multicast"; + if (afi == AFI_IP && safi == SAFI_MPLS_VPN) + return "frr-routing:l3vpn-ipv4-unicast"; + if (afi == AFI_IP6 && safi == SAFI_MPLS_VPN) + return "frr-routing:l3vpn-ipv6-unicast"; + if (afi == AFI_L2VPN && safi == SAFI_EVPN) + return "frr-routing:l2vpn-evpn"; + if (afi == AFI_IP && safi == SAFI_LABELED_UNICAST) + return "frr-routing:ipv4-labeled-unicast"; + if (afi == AFI_IP6 && safi == SAFI_LABELED_UNICAST) + return "frr-routing:ipv6-labeled-unicast"; + + return NULL; +} + +void yang_afi_safi_identity2value(const char *key, afi_t *afi, safi_t *safi) +{ + if (strmatch(key, "frr-routing:ipv4-unicast")) { + *afi = AFI_IP; + *safi = SAFI_UNICAST; + } else if (strmatch(key, "frr-routing:ipv6-unicast")) { + *afi = AFI_IP6; + *safi = SAFI_UNICAST; + } else if (strmatch(key, "frr-routing:ipv4-multicast")) { + *afi = AFI_IP; + *safi = SAFI_MULTICAST; + } else if (strmatch(key, "frr-routing:ipv6-multicast")) { + *afi = AFI_IP6; + *safi = SAFI_MULTICAST; + } else if (strmatch(key, "frr-routing:l3vpn-ipv4-unicast")) { + *afi = AFI_IP; + *safi = SAFI_MPLS_VPN; + } else if (strmatch(key, "frr-routing:l3vpn-ipv6-unicast")) { + *afi = AFI_IP6; + *safi = SAFI_MPLS_VPN; + } else if (strmatch(key, "frr-routing:ipv4-labeled-unicast")) { + *afi = AFI_IP; + *safi = SAFI_LABELED_UNICAST; + } else if (strmatch(key, "frr-routing:ipv6-labeled-unicast")) { + *afi = AFI_IP6; + *safi = SAFI_LABELED_UNICAST; + } else if (strmatch(key, "frr-routing:l2vpn-evpn")) { + *afi = AFI_L2VPN; + *safi = SAFI_EVPN; + } else { + *afi = AFI_UNSPEC; + *safi = SAFI_UNSPEC; + } +} diff --git a/lib/yang_wrappers.h b/lib/yang_wrappers.h index 5203a033ad..d781dfb1e4 100644 --- a/lib/yang_wrappers.h +++ b/lib/yang_wrappers.h @@ -22,6 +22,10 @@ #include "prefix.h" +#ifdef __cplusplus +extern "C" { +#endif + /* bool */ extern bool yang_str2bool(const char *value); extern struct yang_data *yang_data_new_bool(const char *xpath, bool value); @@ -114,6 +118,21 @@ extern const char *yang_get_default_string(const char *xpath_fmt, ...); extern void yang_get_default_string_buf(char *buf, size_t size, const char *xpath_fmt, ...); +/* empty */ +extern struct yang_data *yang_data_new_empty(const char *xpath); +extern bool yang_dnode_get_empty(const struct lyd_node *dnode, + const char *xpath_fmt, ...); + +/* ip prefix */ +extern void yang_str2prefix(const char *value, union prefixptr prefix); +extern struct yang_data *yang_data_new_prefix(const char *xpath, + union prefixconstptr prefix); +extern void yang_dnode_get_prefix(struct prefix *prefix, + const struct lyd_node *dnode, + const char *xpath_fmt, ...); +extern void yang_get_default_prefix(union prefixptr var, const char *xpath_fmt, + ...); + /* ipv4 */ extern void yang_str2ipv4(const char *value, struct in_addr *addr); extern struct yang_data *yang_data_new_ipv4(const char *xpath, @@ -154,4 +173,31 @@ extern void yang_dnode_get_ipv6p(union prefixptr prefix, extern void yang_get_default_ipv6p(union prefixptr var, const char *xpath_fmt, ...); +/* ip */ +extern void yang_str2ip(const char *value, struct ipaddr *addr); +extern struct yang_data *yang_data_new_ip(const char *xpath, + const struct ipaddr *addr); +extern void yang_dnode_get_ip(struct ipaddr *addr, const struct lyd_node *dnode, + const char *xpath_fmt, ...); +extern void yang_get_default_ip(struct ipaddr *var, const char *xpath_fmt, ...); + +/* mac */ +extern struct yang_data *yang_data_new_mac(const char *xpath, + const struct ethaddr *mac); +extern void yang_str2mac(const char *value, struct ethaddr *mac); + +/*data-and-time */ +extern struct yang_data *yang_data_new_date_and_time(const char *xpath, + time_t time); + +/* nexthop enum2str */ +extern const char *yang_nexthop_type2str(uint32_t ntype); + +const char *yang_afi_safi_value2identity(afi_t afi, safi_t safi); +void yang_afi_safi_identity2value(const char *key, afi_t *afi, safi_t *safi); + +#ifdef __cplusplus +} +#endif + #endif /* _FRR_NORTHBOUND_WRAPPERS_H_ */ diff --git a/lib/zassert.h b/lib/zassert.h index d45e1be5f8..e50a88f407 100644 --- a/lib/zassert.h +++ b/lib/zassert.h @@ -19,6 +19,10 @@ #ifndef _QUAGGA_ASSERT_H #define _QUAGGA_ASSERT_H +#ifdef __cplusplus +extern "C" { +#endif + extern void _zlog_assert_failed(const char *assertion, const char *file, unsigned int line, const char *function) __attribute__((noreturn)); @@ -39,4 +43,8 @@ extern void _zlog_assert_failed(const char *assertion, const char *file, #undef assert #define assert(EX) zassert(EX) +#ifdef __cplusplus +} +#endif + #endif /* _QUAGGA_ASSERT_H */ diff --git a/lib/zclient.c b/lib/zclient.c index 4901c92743..fb48d4a12c 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -39,6 +39,7 @@ #include "pbr.h" #include "nexthop_group.h" #include "lib_errors.h" +#include "srte.h" DEFINE_MTYPE_STATIC(LIB, ZCLIENT, "Zclient") DEFINE_MTYPE_STATIC(LIB, REDIST_INST, "Redistribution instance IDs") @@ -49,7 +50,11 @@ enum event { ZCLIENT_SCHEDULE, ZCLIENT_READ, ZCLIENT_CONNECT }; /* Prototype for event manager. */ static void zclient_event(enum event, struct zclient *); -struct zclient_options zclient_options_default = {.receive_notify = false}; +static void zebra_interface_if_set_value(struct stream *s, + struct interface *ifp); + +struct zclient_options zclient_options_default = {.receive_notify = false, + .synchronous = false}; struct sockaddr_storage zclient_addr; socklen_t zclient_addr_len; @@ -62,14 +67,18 @@ struct zclient *zclient_new(struct thread_master *master, struct zclient_options *opt) { struct zclient *zclient; + size_t stream_size = + MAX(ZEBRA_MAX_PACKET_SIZ, sizeof(struct zapi_route)); + zclient = XCALLOC(MTYPE_ZCLIENT, sizeof(struct zclient)); - zclient->ibuf = stream_new(ZEBRA_MAX_PACKET_SIZ); - zclient->obuf = stream_new(ZEBRA_MAX_PACKET_SIZ); + zclient->ibuf = stream_new(stream_size); + zclient->obuf = stream_new(stream_size); zclient->wb = buffer_new(0); zclient->master = master; zclient->receive_notify = opt->receive_notify; + zclient->synchronous = opt->synchronous; return zclient; } @@ -137,6 +146,18 @@ void redist_del_instance(struct redist_proto *red, unsigned short instance) } } +void redist_del_all_instances(struct redist_proto *red) +{ + struct listnode *ln, *nn; + unsigned short *id; + + if (!red->instances) + return; + + for (ALL_LIST_ELEMENTS(red->instances, ln, nn, id)) + redist_del_instance(red, *id); +} + /* Stop zebra client services. */ void zclient_stop(struct zclient *zclient) { @@ -144,7 +165,7 @@ void zclient_stop(struct zclient *zclient) int i; if (zclient_debug) - zlog_debug("zclient stopped"); + zlog_debug("zclient %p stopped", zclient); /* Stop threads. */ THREAD_OFF(zclient->t_read); @@ -218,9 +239,8 @@ int zclient_socket_connect(struct zclient *zclient) ret = connect(sock, (struct sockaddr *)&zclient_addr, zclient_addr_len); if (ret < 0) { if (zclient_debug) - zlog_debug("%s connect failure: %d(%s)", - __PRETTY_FUNCTION__, errno, - safe_strerror(errno)); + zlog_debug("%s connect failure: %d(%s)", __func__, + errno, safe_strerror(errno)); close(sock); return -1; } @@ -251,7 +271,6 @@ static int zclient_flush_data(struct thread *thread) "%s: buffer_flush_available failed on zclient fd %d, closing", __func__, zclient->sock); return zclient_failed(zclient); - break; case BUFFER_PENDING: zclient->t_write = NULL; thread_add_write(zclient->master, zclient_flush_data, zclient, @@ -275,7 +294,6 @@ int zclient_send_message(struct zclient *zclient) "%s: buffer_write failed to zclient fd %d, closing", __func__, zclient->sock); return zclient_failed(zclient); - break; case BUFFER_EMPTY: THREAD_OFF(zclient->t_write); break; @@ -287,6 +305,10 @@ int zclient_send_message(struct zclient *zclient) return 0; } +/* + * If we add more data to this structure please ensure that + * struct zmsghdr in lib/zclient.h is updated as appropriate. + */ void zclient_create_header(struct stream *s, uint16_t command, vrf_id_t vrf_id) { /* length placeholder, caller can update */ @@ -322,8 +344,9 @@ int zclient_read_header(struct stream *s, int sock, uint16_t *size, if (*size && stream_read(s, sock, *size) != *size) return -1; -stream_failure: return 0; +stream_failure: + return -1; } bool zapi_parse_header(struct stream *zmsg, struct zmsghdr *hdr) @@ -354,11 +377,11 @@ static int zebra_message_send(struct zclient *zclient, int command, return zclient_send_message(zclient); } -static int zebra_hello_send(struct zclient *zclient) +int zclient_send_hello(struct zclient *zclient) { struct stream *s; - if (zclient->redist_default) { + if (zclient->redist_default || zclient->synchronous) { s = zclient->obuf; stream_reset(s); @@ -366,10 +389,15 @@ static int zebra_hello_send(struct zclient *zclient) zclient_create_header(s, ZEBRA_HELLO, VRF_DEFAULT); stream_putc(s, zclient->redist_default); stream_putw(s, zclient->instance); + stream_putl(s, zclient->session_id); if (zclient->receive_notify) stream_putc(s, 1); else stream_putc(s, 0); + if (zclient->synchronous) + stream_putc(s, 1); + else + stream_putc(s, 0); stream_putw_at(s, 0, stream_get_endp(s)); return zclient_send_message(zclient); @@ -409,7 +437,8 @@ void zclient_send_reg_requests(struct zclient *zclient, vrf_id_t vrf_id) vrf_id); /* We need router-id information. */ - zebra_message_send(zclient, ZEBRA_ROUTER_ID_ADD, vrf_id); + zclient_send_router_id_update(zclient, ZEBRA_ROUTER_ID_ADD, AFI_IP, + vrf_id); /* We need interface information. */ zebra_message_send(zclient, ZEBRA_INTERFACE_ADD, vrf_id); @@ -476,7 +505,8 @@ void zclient_send_dereg_requests(struct zclient *zclient, vrf_id_t vrf_id) vrf_id); /* We need router-id information. */ - zebra_message_send(zclient, ZEBRA_ROUTER_ID_DELETE, vrf_id); + zclient_send_router_id_update(zclient, ZEBRA_ROUTER_ID_DELETE, AFI_IP, + vrf_id); zebra_message_send(zclient, ZEBRA_INTERFACE_DELETE, vrf_id); @@ -527,6 +557,18 @@ void zclient_send_dereg_requests(struct zclient *zclient, vrf_id_t vrf_id) } } +int zclient_send_router_id_update(struct zclient *zclient, + zebra_message_types_t type, afi_t afi, + vrf_id_t vrf_id) +{ + struct stream *s = zclient->obuf; + stream_reset(s); + zclient_create_header(s, type, vrf_id); + stream_putw(s, afi); + stream_putw_at(s, 0, stream_get_endp(s)); + return zclient_send_message(zclient); +} + /* Send request to zebra daemon to start or stop RA. */ void zclient_send_interface_radv_req(struct zclient *zclient, vrf_id_t vrf_id, struct interface *ifp, int enable, @@ -555,6 +597,25 @@ void zclient_send_interface_radv_req(struct zclient *zclient, vrf_id_t vrf_id, zclient_send_message(zclient); } +int zclient_send_interface_protodown(struct zclient *zclient, vrf_id_t vrf_id, + struct interface *ifp, bool down) +{ + struct stream *s; + + if (zclient->sock < 0) + return -1; + + s = zclient->obuf; + stream_reset(s); + zclient_create_header(s, ZEBRA_INTERFACE_SET_PROTODOWN, vrf_id); + stream_putl(s, ifp->ifindex); + stream_putc(s, !!down); + stream_putw_at(s, 0, stream_get_endp(s)); + zclient_send_message(zclient); + + return 0; +} + /* Make connection to zebra daemon. */ int zclient_start(struct zclient *zclient) { @@ -590,7 +651,7 @@ int zclient_start(struct zclient *zclient) /* Create read thread. */ zclient_event(ZCLIENT_READ, zclient); - zebra_hello_send(zclient); + zclient_send_hello(zclient); zebra_message_send(zclient, ZEBRA_INTERFACE_ADD, VRF_DEFAULT); @@ -651,8 +712,9 @@ static int zclient_connect(struct thread *t) return zclient_start(zclient); } -int zclient_send_rnh(struct zclient *zclient, int command, struct prefix *p, - bool exact_match, vrf_id_t vrf_id) +int zclient_send_rnh(struct zclient *zclient, int command, + const struct prefix *p, bool exact_match, + vrf_id_t vrf_id) { struct stream *s; @@ -743,6 +805,218 @@ int zclient_route_send(uint8_t cmd, struct zclient *zclient, return zclient_send_message(zclient); } +static int zapi_nexthop_labels_cmp(const struct zapi_nexthop *next1, + const struct zapi_nexthop *next2) +{ + if (next1->label_num > next2->label_num) + return 1; + + if (next1->label_num < next2->label_num) + return -1; + + return memcmp(next1->labels, next2->labels, next1->label_num); +} + +static int zapi_nexthop_cmp_no_labels(const struct zapi_nexthop *next1, + const struct zapi_nexthop *next2) +{ + int ret = 0; + + if (next1->vrf_id < next2->vrf_id) + return -1; + + if (next1->vrf_id > next2->vrf_id) + return 1; + + if (next1->type < next2->type) + return -1; + + if (next1->type > next2->type) + return 1; + + if (next1->weight < next2->weight) + return -1; + + if (next1->weight > next2->weight) + return 1; + + switch (next1->type) { + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV6: + ret = nexthop_g_addr_cmp(next1->type, &next1->gate, + &next2->gate); + if (ret != 0) + return ret; + break; + case NEXTHOP_TYPE_IPV4_IFINDEX: + case NEXTHOP_TYPE_IPV6_IFINDEX: + ret = nexthop_g_addr_cmp(next1->type, &next1->gate, + &next2->gate); + if (ret != 0) + return ret; + /* Intentional Fall-Through */ + case NEXTHOP_TYPE_IFINDEX: + if (next1->ifindex < next2->ifindex) + return -1; + + if (next1->ifindex > next2->ifindex) + return 1; + break; + case NEXTHOP_TYPE_BLACKHOLE: + if (next1->bh_type < next2->bh_type) + return -1; + + if (next1->bh_type > next2->bh_type) + return 1; + break; + } + + if (next1->srte_color < next2->srte_color) + return -1; + if (next1->srte_color > next2->srte_color) + return 1; + + if (CHECK_FLAG(next1->flags, NEXTHOP_FLAG_HAS_BACKUP) || + CHECK_FLAG(next2->flags, NEXTHOP_FLAG_HAS_BACKUP)) { + + if (!CHECK_FLAG(next1->flags, NEXTHOP_FLAG_HAS_BACKUP) && + CHECK_FLAG(next2->flags, NEXTHOP_FLAG_HAS_BACKUP)) + return -1; + + if (CHECK_FLAG(next1->flags, NEXTHOP_FLAG_HAS_BACKUP) && + !CHECK_FLAG(next2->flags, NEXTHOP_FLAG_HAS_BACKUP)) + return 1; + + if (next1->backup_num > 0 || next2->backup_num > 0) { + + if (next1->backup_num < next2->backup_num) + return -1; + + if (next1->backup_num > next2->backup_num) + return 1; + + ret = memcmp(next1->backup_idx, + next2->backup_idx, next1->backup_num); + if (ret != 0) + return ret; + } + } + + return 0; +} + +static int zapi_nexthop_cmp(const void *item1, const void *item2) +{ + int ret = 0; + + const struct zapi_nexthop *next1 = item1; + const struct zapi_nexthop *next2 = item2; + + ret = zapi_nexthop_cmp_no_labels(next1, next2); + if (ret != 0) + return ret; + + ret = zapi_nexthop_labels_cmp(next1, next2); + + return ret; +} + +static void zapi_nexthop_group_sort(struct zapi_nexthop *nh_grp, + uint16_t nexthop_num) +{ + qsort(nh_grp, nexthop_num, sizeof(struct zapi_nexthop), + &zapi_nexthop_cmp); +} + +/* + * Encode a single zapi nexthop + */ +int zapi_nexthop_encode(struct stream *s, const struct zapi_nexthop *api_nh, + uint32_t api_flags, uint32_t api_message) +{ + int i, ret = 0; + int nh_flags = api_nh->flags; + + stream_putl(s, api_nh->vrf_id); + stream_putc(s, api_nh->type); + + /* If needed, set 'labelled nexthop' flag */ + if (api_nh->label_num > 0) { + SET_FLAG(nh_flags, ZAPI_NEXTHOP_FLAG_LABEL); + + /* Validate label count */ + if (api_nh->label_num > MPLS_MAX_LABELS) { + ret = -1; + goto done; + } + } + + /* If present, set 'weight' flag before encoding flags */ + if (api_nh->weight) + SET_FLAG(nh_flags, ZAPI_NEXTHOP_FLAG_WEIGHT); + + /* Note that we're only encoding a single octet */ + stream_putc(s, nh_flags); + + switch (api_nh->type) { + case NEXTHOP_TYPE_BLACKHOLE: + stream_putc(s, api_nh->bh_type); + break; + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + stream_put_in_addr(s, &api_nh->gate.ipv4); + stream_putl(s, api_nh->ifindex); + break; + case NEXTHOP_TYPE_IFINDEX: + stream_putl(s, api_nh->ifindex); + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + stream_write(s, (uint8_t *)&api_nh->gate.ipv6, + 16); + stream_putl(s, api_nh->ifindex); + break; + } + + /* We only encode labels if we have >0 - we use + * the per-nexthop flag above to signal that the count + * is present in the payload. + */ + if (api_nh->label_num > 0) { + stream_putc(s, api_nh->label_num); + stream_put(s, &api_nh->labels[0], + api_nh->label_num * sizeof(mpls_label_t)); + } + + if (api_nh->weight) + stream_putl(s, api_nh->weight); + + /* Router MAC for EVPN routes. */ + if (CHECK_FLAG(api_flags, ZEBRA_FLAG_EVPN_ROUTE)) + stream_put(s, &(api_nh->rmac), + sizeof(struct ethaddr)); + + /* Color for Segment Routing TE. */ + if (CHECK_FLAG(api_message, ZAPI_MESSAGE_SRTE)) + stream_putl(s, api_nh->srte_color); + + /* Index of backup nexthop */ + if (CHECK_FLAG(nh_flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP)) { + /* Validate backup count */ + if (api_nh->backup_num > NEXTHOP_MAX_BACKUPS) { + ret = -1; + goto done; + } + + stream_putc(s, api_nh->backup_num); + for (i = 0; i < api_nh->backup_num; i++) + stream_putc(s, api_nh->backup_idx[i]); + } + +done: + return ret; +} + int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api) { struct zapi_nexthop *api_nh; @@ -755,19 +1029,19 @@ int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api) if (api->type >= ZEBRA_ROUTE_MAX) { flog_err(EC_LIB_ZAPI_ENCODE, "%s: Specified route type (%u) is not a legal value\n", - __PRETTY_FUNCTION__, api->type); + __func__, api->type); return -1; } stream_putc(s, api->type); stream_putw(s, api->instance); stream_putl(s, api->flags); - stream_putc(s, api->message); + stream_putl(s, api->message); if (api->safi < SAFI_UNICAST || api->safi >= SAFI_MAX) { flog_err(EC_LIB_ZAPI_ENCODE, "%s: Specified route SAFI (%u) is not a legal value\n", - __PRETTY_FUNCTION__, api->safi); + __func__, api->safi); return -1; } stream_putc(s, api->safi); @@ -776,7 +1050,7 @@ int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api) stream_putc(s, api->prefix.family); psize = PSIZE(api->prefix.prefixlen); stream_putc(s, api->prefix.prefixlen); - stream_write(s, (uint8_t *)&api->prefix.u.prefix, psize); + stream_write(s, &api->prefix.u.prefix, psize); if (CHECK_FLAG(api->message, ZAPI_MESSAGE_SRCPFX)) { psize = PSIZE(api->src_prefix.prefixlen); @@ -798,64 +1072,81 @@ int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api) return -1; } + /* We canonicalize the nexthops by sorting them; this allows + * zebra to resolve the list of nexthops to a nexthop-group + * more efficiently. + */ + zapi_nexthop_group_sort(api->nexthops, api->nexthop_num); + stream_putw(s, api->nexthop_num); for (i = 0; i < api->nexthop_num; i++) { api_nh = &api->nexthops[i]; - stream_putl(s, api_nh->vrf_id); - stream_putc(s, api_nh->type); - stream_putc(s, api_nh->onlink); - switch (api_nh->type) { - case NEXTHOP_TYPE_BLACKHOLE: - stream_putc(s, api_nh->bh_type); - break; - case NEXTHOP_TYPE_IPV4: - stream_put_in_addr(s, &api_nh->gate.ipv4); - break; - case NEXTHOP_TYPE_IPV4_IFINDEX: - stream_put_in_addr(s, &api_nh->gate.ipv4); - stream_putl(s, api_nh->ifindex); - break; - case NEXTHOP_TYPE_IFINDEX: - stream_putl(s, api_nh->ifindex); - break; - case NEXTHOP_TYPE_IPV6: - stream_write(s, (uint8_t *)&api_nh->gate.ipv6, - 16); - break; - case NEXTHOP_TYPE_IPV6_IFINDEX: - stream_write(s, (uint8_t *)&api_nh->gate.ipv6, - 16); - stream_putl(s, api_nh->ifindex); - break; + /* MPLS labels for BGP-LU or Segment Routing */ + if (api_nh->label_num > MPLS_MAX_LABELS) { + char buf[PREFIX2STR_BUFFER]; + + prefix2str(&api->prefix, buf, sizeof(buf)); + + flog_err(EC_LIB_ZAPI_ENCODE, + "%s: prefix %s: can't encode %u labels (maximum is %u)", + __func__, buf, + api_nh->label_num, + MPLS_MAX_LABELS); + return -1; } + if (zapi_nexthop_encode(s, api_nh, api->flags, + api->message) + != 0) + return -1; + } + } + + /* Backup nexthops */ + if (CHECK_FLAG(api->message, ZAPI_MESSAGE_BACKUP_NEXTHOPS)) { + /* limit the number of nexthops if necessary */ + if (api->backup_nexthop_num > MULTIPATH_NUM) { + char buf[PREFIX2STR_BUFFER]; + + prefix2str(&api->prefix, buf, sizeof(buf)); + flog_err( + EC_LIB_ZAPI_ENCODE, + "%s: prefix %s: can't encode %u backup nexthops (maximum is %u)", + __func__, buf, api->backup_nexthop_num, + MULTIPATH_NUM); + return -1; + } + + /* Note that we do not sort the list of backup nexthops - + * this list is treated as an array and indexed by each + * primary nexthop that is associated with a backup. + */ + + stream_putw(s, api->backup_nexthop_num); + + for (i = 0; i < api->backup_nexthop_num; i++) { + api_nh = &api->backup_nexthops[i]; + /* MPLS labels for BGP-LU or Segment Routing */ - if (CHECK_FLAG(api->message, ZAPI_MESSAGE_LABEL)) { - if (api_nh->label_num > MPLS_MAX_LABELS) { - char buf[PREFIX2STR_BUFFER]; - prefix2str(&api->prefix, buf, - sizeof(buf)); - flog_err(EC_LIB_ZAPI_ENCODE, - "%s: prefix %s: can't encode " - "%u labels (maximum is %u)", - __func__, buf, - api_nh->label_num, - MPLS_MAX_LABELS); - return -1; - } - - stream_putc(s, api_nh->label_num); - stream_put(s, &api_nh->labels[0], - api_nh->label_num - * sizeof(mpls_label_t)); + if (api_nh->label_num > MPLS_MAX_LABELS) { + char buf[PREFIX2STR_BUFFER]; + + prefix2str(&api->prefix, buf, sizeof(buf)); + + flog_err(EC_LIB_ZAPI_ENCODE, + "%s: prefix %s: backup: can't encode %u labels (maximum is %u)", + __func__, buf, + api_nh->label_num, + MPLS_MAX_LABELS); + return -1; } - /* Router MAC for EVPN routes. */ - if (CHECK_FLAG(api->flags, ZEBRA_FLAG_EVPN_ROUTE)) - stream_put(s, &(api_nh->rmac), - sizeof(struct ethaddr)); + if (zapi_nexthop_encode(s, api_nh, api->flags, + api->message) + != 0) + return -1; } } @@ -877,6 +1168,86 @@ int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api) return 0; } +/* + * 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 i, ret = -1; + + STREAM_GETL(s, api_nh->vrf_id); + STREAM_GETC(s, api_nh->type); + + /* Note that we're only using a single octet of flags */ + STREAM_GETC(s, api_nh->flags); + + switch (api_nh->type) { + case NEXTHOP_TYPE_BLACKHOLE: + STREAM_GETC(s, api_nh->bh_type); + break; + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + STREAM_GET(&api_nh->gate.ipv4.s_addr, s, + IPV4_MAX_BYTELEN); + STREAM_GETL(s, api_nh->ifindex); + break; + case NEXTHOP_TYPE_IFINDEX: + STREAM_GETL(s, api_nh->ifindex); + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + STREAM_GET(&api_nh->gate.ipv6, s, 16); + STREAM_GETL(s, api_nh->ifindex); + break; + } + + /* MPLS labels for BGP-LU or Segment Routing */ + if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_LABEL)) { + STREAM_GETC(s, api_nh->label_num); + if (api_nh->label_num > MPLS_MAX_LABELS) { + flog_err( + EC_LIB_ZAPI_ENCODE, + "%s: invalid number of MPLS labels (%u)", + __func__, api_nh->label_num); + return -1; + } + + STREAM_GET(&api_nh->labels[0], s, + api_nh->label_num * sizeof(mpls_label_t)); + } + + if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_WEIGHT)) + STREAM_GETL(s, api_nh->weight); + + /* Router MAC for EVPN routes. */ + if (CHECK_FLAG(api_flags, ZEBRA_FLAG_EVPN_ROUTE)) + STREAM_GET(&(api_nh->rmac), s, + sizeof(struct ethaddr)); + + /* Color for Segment Routing TE. */ + if (CHECK_FLAG(api_message, ZAPI_MESSAGE_SRTE)) + STREAM_GETL(s, api_nh->srte_color); + + /* Backup nexthop index */ + if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP)) { + STREAM_GETC(s, api_nh->backup_num); + + if (api_nh->backup_num > NEXTHOP_MAX_BACKUPS) + return -1; + + for (i = 0; i < api_nh->backup_num; i++) + STREAM_GETC(s, api_nh->backup_idx[i]); + } + + /* Success */ + ret = 0; + +stream_failure: + + return ret; +} + int zapi_route_decode(struct stream *s, struct zapi_route *api) { struct zapi_nexthop *api_nh; @@ -889,18 +1260,18 @@ int zapi_route_decode(struct stream *s, struct zapi_route *api) if (api->type >= ZEBRA_ROUTE_MAX) { flog_err(EC_LIB_ZAPI_ENCODE, "%s: Specified route type: %d is not a legal value\n", - __PRETTY_FUNCTION__, api->type); + __func__, api->type); return -1; } STREAM_GETW(s, api->instance); STREAM_GETL(s, api->flags); - STREAM_GETC(s, api->message); + STREAM_GETL(s, api->message); STREAM_GETC(s, api->safi); if (api->safi < SAFI_UNICAST || api->safi >= SAFI_MAX) { flog_err(EC_LIB_ZAPI_ENCODE, "%s: Specified route SAFI (%u) is not a legal value\n", - __PRETTY_FUNCTION__, api->safi); + __func__, api->safi); return -1; } @@ -913,7 +1284,7 @@ int zapi_route_decode(struct stream *s, struct zapi_route *api) flog_err( EC_LIB_ZAPI_ENCODE, "%s: V4 prefixlen is %d which should not be more than 32", - __PRETTY_FUNCTION__, api->prefix.prefixlen); + __func__, api->prefix.prefixlen); return -1; } break; @@ -922,14 +1293,14 @@ int zapi_route_decode(struct stream *s, struct zapi_route *api) flog_err( EC_LIB_ZAPI_ENCODE, "%s: v6 prefixlen is %d which should not be more than 128", - __PRETTY_FUNCTION__, api->prefix.prefixlen); + __func__, api->prefix.prefixlen); return -1; } break; default: flog_err(EC_LIB_ZAPI_ENCODE, - "%s: Specified family %d is not v4 or v6", - __PRETTY_FUNCTION__, api->prefix.family); + "%s: Specified family %d is not v4 or v6", __func__, + api->prefix.family); return -1; } STREAM_GET(&api->prefix.u.prefix, s, PSIZE(api->prefix.prefixlen)); @@ -941,7 +1312,7 @@ int zapi_route_decode(struct stream *s, struct zapi_route *api) flog_err( EC_LIB_ZAPI_ENCODE, "%s: SRC Prefix prefixlen received: %d is too large", - __PRETTY_FUNCTION__, api->src_prefix.prefixlen); + __func__, api->src_prefix.prefixlen); return -1; } STREAM_GET(&api->src_prefix.prefix, s, @@ -952,7 +1323,7 @@ int zapi_route_decode(struct stream *s, struct zapi_route *api) flog_err( EC_LIB_ZAPI_ENCODE, "%s: SRC prefix specified in some manner that makes no sense", - __PRETTY_FUNCTION__); + __func__); return -1; } } @@ -970,55 +1341,30 @@ int zapi_route_decode(struct stream *s, struct zapi_route *api) for (i = 0; i < api->nexthop_num; i++) { api_nh = &api->nexthops[i]; - STREAM_GETL(s, api_nh->vrf_id); - STREAM_GETC(s, api_nh->type); - STREAM_GETC(s, api_nh->onlink); - switch (api_nh->type) { - case NEXTHOP_TYPE_BLACKHOLE: - STREAM_GETC(s, api_nh->bh_type); - break; - case NEXTHOP_TYPE_IPV4: - STREAM_GET(&api_nh->gate.ipv4.s_addr, s, - IPV4_MAX_BYTELEN); - break; - case NEXTHOP_TYPE_IPV4_IFINDEX: - STREAM_GET(&api_nh->gate.ipv4.s_addr, s, - IPV4_MAX_BYTELEN); - STREAM_GETL(s, api_nh->ifindex); - break; - case NEXTHOP_TYPE_IFINDEX: - STREAM_GETL(s, api_nh->ifindex); - break; - case NEXTHOP_TYPE_IPV6: - STREAM_GET(&api_nh->gate.ipv6, s, 16); - break; - case NEXTHOP_TYPE_IPV6_IFINDEX: - STREAM_GET(&api_nh->gate.ipv6, s, 16); - STREAM_GETL(s, api_nh->ifindex); - break; - } + if (zapi_nexthop_decode(s, api_nh, api->flags, + api->message) + != 0) + return -1; + } + } - /* MPLS labels for BGP-LU or Segment Routing */ - if (CHECK_FLAG(api->message, ZAPI_MESSAGE_LABEL)) { - STREAM_GETC(s, api_nh->label_num); - - if (api_nh->label_num > MPLS_MAX_LABELS) { - flog_err( - EC_LIB_ZAPI_ENCODE, - "%s: invalid number of MPLS labels (%u)", - __func__, api_nh->label_num); - return -1; - } - - STREAM_GET(&api_nh->labels[0], s, - api_nh->label_num - * sizeof(mpls_label_t)); - } + /* Backup nexthops. */ + if (CHECK_FLAG(api->message, ZAPI_MESSAGE_BACKUP_NEXTHOPS)) { + STREAM_GETW(s, api->backup_nexthop_num); + if (api->backup_nexthop_num > MULTIPATH_NUM) { + flog_err(EC_LIB_ZAPI_ENCODE, + "%s: invalid number of backup nexthops (%u)", + __func__, api->backup_nexthop_num); + return -1; + } + + for (i = 0; i < api->backup_nexthop_num; i++) { + api_nh = &api->backup_nexthops[i]; - /* Router MAC for EVPN routes. */ - if (CHECK_FLAG(api->flags, ZEBRA_FLAG_EVPN_ROUTE)) - stream_get(&(api_nh->rmac), s, - sizeof(struct ethaddr)); + if (zapi_nexthop_decode(s, api_nh, api->flags, + api->message) + != 0) + return -1; } } @@ -1034,8 +1380,9 @@ int zapi_route_decode(struct stream *s, struct zapi_route *api) if (CHECK_FLAG(api->message, ZAPI_MESSAGE_TABLEID)) STREAM_GETL(s, api->tableid); -stream_failure: return 0; +stream_failure: + return -1; } static void zapi_encode_prefix(struct stream *s, struct prefix *p, @@ -1077,7 +1424,7 @@ int zapi_pbr_rule_encode(uint8_t cmd, struct stream *s, struct pbr_rule *zrule) stream_putw(s, zrule->filter.fwmark); /* fwmark */ stream_putl(s, zrule->action.table); - stream_putl(s, zrule->ifindex); + stream_put(s, zrule->ifname, INTERFACE_NAMSIZ); /* Put length at the first point of the stream. */ stream_putw_at(s, 0, stream_get_endp(s)); @@ -1107,27 +1454,23 @@ bool zapi_route_notify_decode(struct stream *s, struct prefix *p, } bool zapi_rule_notify_decode(struct stream *s, uint32_t *seqno, - uint32_t *priority, uint32_t *unique, - ifindex_t *ifindex, + uint32_t *priority, uint32_t *unique, char *ifname, enum zapi_rule_notify_owner *note) { uint32_t prio, seq, uni; - ifindex_t ifi; STREAM_GET(note, s, sizeof(*note)); STREAM_GETL(s, seq); STREAM_GETL(s, prio); STREAM_GETL(s, uni); - STREAM_GETL(s, ifi); + STREAM_GET(ifname, s, INTERFACE_NAMSIZ); if (zclient_debug) - zlog_debug("%s: %u %u %u %u", __PRETTY_FUNCTION__, seq, prio, - uni, ifi); + zlog_debug("%s: %u %u %u %s", __func__, seq, prio, uni, ifname); *seqno = seq; *priority = prio; *unique = uni; - *ifindex = ifi; return true; @@ -1145,7 +1488,7 @@ bool zapi_ipset_notify_decode(struct stream *s, uint32_t *unique, STREAM_GETL(s, uni); if (zclient_debug) - zlog_debug("%s: %u", __PRETTY_FUNCTION__, uni); + zlog_debug("%s: %u", __func__, uni); *unique = uni; return true; @@ -1167,7 +1510,7 @@ bool zapi_ipset_entry_notify_decode(struct stream *s, uint32_t *unique, STREAM_GET(ipset_name, s, ZEBRA_IPSET_NAME_SIZE); if (zclient_debug) - zlog_debug("%s: %u", __PRETTY_FUNCTION__, uni); + zlog_debug("%s: %u", __func__, uni); *unique = uni; return true; @@ -1187,7 +1530,7 @@ bool zapi_iptable_notify_decode(struct stream *s, STREAM_GETL(s, uni); if (zclient_debug) - zlog_debug("%s: %u", __PRETTY_FUNCTION__, uni); + zlog_debug("%s: %u", __func__, uni); *unique = uni; return true; @@ -1196,7 +1539,7 @@ bool zapi_iptable_notify_decode(struct stream *s, return false; } -struct nexthop *nexthop_from_zapi_nexthop(struct zapi_nexthop *znh) +struct nexthop *nexthop_from_zapi_nexthop(const struct zapi_nexthop *znh) { struct nexthop *n = nexthop_new(); @@ -1204,6 +1547,7 @@ struct nexthop *nexthop_from_zapi_nexthop(struct zapi_nexthop *znh) n->vrf_id = znh->vrf_id; n->ifindex = znh->ifindex; n->gate = znh->gate; + n->srte_color = znh->srte_color; /* * This function currently handles labels @@ -1213,15 +1557,115 @@ struct nexthop *nexthop_from_zapi_nexthop(struct zapi_nexthop *znh) znh->labels); } + if (CHECK_FLAG(znh->flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP)) { + SET_FLAG(n->flags, NEXTHOP_FLAG_HAS_BACKUP); + n->backup_num = znh->backup_num; + memcpy(n->backup_idx, znh->backup_idx, n->backup_num); + } + return n; } +/* + * Convert nexthop to zapi nexthop + */ +int zapi_nexthop_from_nexthop(struct zapi_nexthop *znh, + const struct nexthop *nh) +{ + int i; + + memset(znh, 0, sizeof(*znh)); + + znh->type = nh->type; + znh->vrf_id = nh->vrf_id; + znh->weight = nh->weight; + znh->ifindex = nh->ifindex; + znh->gate = nh->gate; + + if (nh->nh_label && (nh->nh_label->num_labels > 0)) { + + /* Validate */ + if (nh->nh_label->num_labels > MPLS_MAX_LABELS) + return -1; + + for (i = 0; i < nh->nh_label->num_labels; i++) + znh->labels[i] = nh->nh_label->label[i]; + + znh->label_num = i; + SET_FLAG(znh->flags, ZAPI_NEXTHOP_FLAG_LABEL); + } + + if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_HAS_BACKUP)) { + if (nh->backup_num > NEXTHOP_MAX_BACKUPS) + return -1; + + SET_FLAG(znh->flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP); + znh->backup_num = nh->backup_num; + memcpy(znh->backup_idx, nh->backup_idx, znh->backup_num); + } + + return 0; +} + +/* + * Wrapper that converts backup nexthop + */ +int zapi_backup_nexthop_from_nexthop(struct zapi_nexthop *znh, + const struct nexthop *nh) +{ + int ret; + + /* Ensure that zapi flags are correct: backups don't have backups */ + ret = zapi_nexthop_from_nexthop(znh, nh); + if (ret == 0) + UNSET_FLAG(znh->flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP); + + return ret; +} + +/* + * Format some info about a zapi nexthop, for debug or logging. + */ +const char *zapi_nexthop2str(const struct zapi_nexthop *znh, char *buf, + int bufsize) +{ + char tmp[INET6_ADDRSTRLEN]; + + switch (znh->type) { + case NEXTHOP_TYPE_IFINDEX: + snprintf(buf, bufsize, "if %u", znh->ifindex); + break; + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + inet_ntop(AF_INET, &znh->gate.ipv4, tmp, sizeof(tmp)); + snprintf(buf, bufsize, "%s if %u", tmp, znh->ifindex); + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + inet_ntop(AF_INET6, &znh->gate.ipv6, tmp, sizeof(tmp)); + snprintf(buf, bufsize, "%s if %u", tmp, znh->ifindex); + break; + case NEXTHOP_TYPE_BLACKHOLE: + snprintf(buf, bufsize, "blackhole"); + break; + default: + snprintf(buf, bufsize, "unknown"); + break; + } + + return buf; +} + +/* + * Decode the nexthop-tracking update message + */ bool zapi_nexthop_update_decode(struct stream *s, struct zapi_route *nhr) { uint32_t i; memset(nhr, 0, sizeof(*nhr)); + STREAM_GETL(s, nhr->message); STREAM_GETW(s, nhr->prefix.family); STREAM_GETC(s, nhr->prefix.prefixlen); switch (nhr->prefix.family) { @@ -1234,6 +1678,8 @@ bool zapi_nexthop_update_decode(struct stream *s, struct zapi_route *nhr) default: break; } + if (CHECK_FLAG(nhr->message, ZAPI_MESSAGE_SRTE)) + STREAM_GETL(s, nhr->srte_color); STREAM_GETC(s, nhr->type); STREAM_GETW(s, nhr->instance); @@ -1242,38 +1688,8 @@ bool zapi_nexthop_update_decode(struct stream *s, struct zapi_route *nhr) STREAM_GETC(s, nhr->nexthop_num); for (i = 0; i < nhr->nexthop_num; i++) { - STREAM_GETL(s, nhr->nexthops[i].vrf_id); - STREAM_GETC(s, nhr->nexthops[i].type); - switch (nhr->nexthops[i].type) { - case NEXTHOP_TYPE_IPV4: - case NEXTHOP_TYPE_IPV4_IFINDEX: - STREAM_GET(&nhr->nexthops[i].gate.ipv4.s_addr, s, - IPV4_MAX_BYTELEN); - STREAM_GETL(s, nhr->nexthops[i].ifindex); - break; - case NEXTHOP_TYPE_IFINDEX: - STREAM_GETL(s, nhr->nexthops[i].ifindex); - break; - case NEXTHOP_TYPE_IPV6: - case NEXTHOP_TYPE_IPV6_IFINDEX: - STREAM_GET(&nhr->nexthops[i].gate.ipv6, s, - IPV6_MAX_BYTELEN); - STREAM_GETL(s, nhr->nexthops[i].ifindex); - break; - case NEXTHOP_TYPE_BLACKHOLE: - break; - } - STREAM_GETC(s, nhr->nexthops[i].label_num); - if (nhr->nexthops[i].label_num > MPLS_MAX_LABELS) { - flog_err(EC_LIB_ZAPI_ENCODE, - "%s: invalid number of MPLS labels (%u)", - __func__, nhr->nexthops[i].label_num); + if (zapi_nexthop_decode(s, &(nhr->nexthops[i]), 0, 0) != 0) return false; - } - if (nhr->nexthops[i].label_num) - STREAM_GET(&nhr->nexthops[i].labels[0], s, - nhr->nexthops[i].label_num - * sizeof(mpls_label_t)); } return true; @@ -1281,6 +1697,21 @@ bool zapi_nexthop_update_decode(struct stream *s, struct zapi_route *nhr) return false; } +bool zapi_error_decode(struct stream *s, enum zebra_error_types *error) +{ + memset(error, 0, sizeof(*error)); + + STREAM_GET(error, s, sizeof(*error)); + + if (zclient_debug) + zlog_debug("%s: type: %s", __func__, + zebra_error_type2str(*error)); + + return true; +stream_failure: + return false; +} + /* * send a ZEBRA_REDISTRIBUTE_ADD or ZEBRA_REDISTRIBUTE_DELETE * for the route type (ZEBRA_ROUTE_KERNEL etc.). The zebra server will @@ -1322,33 +1753,34 @@ int zebra_redistribute_default_send(int command, struct zclient *zclient, } /* Get prefix in ZServ format; family should be filled in on prefix */ -static void zclient_stream_get_prefix(struct stream *s, struct prefix *p) +static int zclient_stream_get_prefix(struct stream *s, struct prefix *p) { size_t plen = prefix_blen(p); uint8_t c; p->prefixlen = 0; if (plen == 0) - return; + return -1; - stream_get(&p->u.prefix, s, plen); + STREAM_GET(&p->u.prefix, s, plen); STREAM_GETC(s, c); p->prefixlen = MIN(plen * 8, c); + return 0; stream_failure: - return; + return -1; } /* Router-id update from zebra daemon. */ -void zebra_router_id_update_read(struct stream *s, struct prefix *rid) +int zebra_router_id_update_read(struct stream *s, struct prefix *rid) { /* Fetch interface address. */ STREAM_GETC(s, rid->family); - zclient_stream_get_prefix(s, rid); + return zclient_stream_get_prefix(s, rid); stream_failure: - return; + return -1; } /* Interface addition from zebra daemon. */ @@ -1381,6 +1813,8 @@ void zebra_router_id_update_read(struct stream *s, struct prefix *rid) * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | bandwidth | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | parent ifindex | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Link Layer Type | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Harware Address Length | @@ -1395,24 +1829,36 @@ void zebra_router_id_update_read(struct stream *s, struct prefix *rid) * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ -static void zclient_vrf_add(struct zclient *zclient, vrf_id_t vrf_id) +static int zclient_vrf_add(struct zclient *zclient, vrf_id_t vrf_id) { struct vrf *vrf; - char vrfname_tmp[VRF_NAMSIZ]; + char vrfname_tmp[VRF_NAMSIZ + 1] = {}; struct vrf_data data; - stream_get(&data, zclient->ibuf, sizeof(struct vrf_data)); + STREAM_GET(&data, zclient->ibuf, sizeof(struct vrf_data)); /* Read interface name. */ - stream_get(vrfname_tmp, zclient->ibuf, VRF_NAMSIZ); + STREAM_GET(vrfname_tmp, zclient->ibuf, VRF_NAMSIZ); - /* Lookup/create vrf by vrf_id. */ + if (strlen(vrfname_tmp) == 0) + goto stream_failure; + + /* Lookup/create vrf by name, then vrf_id. */ vrf = vrf_get(vrf_id, vrfname_tmp); + + /* If there's already a VRF with this name, don't create vrf */ + if (!vrf) + return 0; + vrf->data.l.table_id = data.l.table_id; memcpy(vrf->data.l.netns_name, data.l.netns_name, NS_NAMSIZ); /* overwrite default vrf */ if (vrf_id == VRF_DEFAULT) vrf_set_default_name(vrfname_tmp, false); vrf_enable(vrf); + + return 0; +stream_failure: + return -1; } static void zclient_vrf_delete(struct zclient *zclient, vrf_id_t vrf_id) @@ -1433,36 +1879,47 @@ static void zclient_vrf_delete(struct zclient *zclient, vrf_id_t vrf_id) vrf_delete(vrf); } -struct interface *zebra_interface_add_read(struct stream *s, vrf_id_t vrf_id) +static int zclient_interface_add(struct zclient *zclient, vrf_id_t vrf_id) { struct interface *ifp; - char ifname_tmp[INTERFACE_NAMSIZ]; + char ifname_tmp[INTERFACE_NAMSIZ + 1] = {}; + struct stream *s = zclient->ibuf; /* Read interface name. */ - stream_get(ifname_tmp, s, INTERFACE_NAMSIZ); + STREAM_GET(ifname_tmp, s, INTERFACE_NAMSIZ); /* Lookup/create interface by name. */ + if (!vrf_get(vrf_id, NULL)) { + zlog_debug( + "Rx'd interface add from Zebra, but VRF %u does not exist", + vrf_id); + return -1; + } + ifp = if_get_by_name(ifname_tmp, vrf_id); zebra_interface_if_set_value(s, ifp); - return ifp; + if_new_via_zapi(ifp); + + return 0; +stream_failure: + return -1; } /* * Read interface up/down msg (ZEBRA_INTERFACE_UP/ZEBRA_INTERFACE_DOWN) * from zebra server. The format of this message is the same as - * that sent for ZEBRA_INTERFACE_ADD/ZEBRA_INTERFACE_DELETE (see - * comments for zebra_interface_add_read), except that no sockaddr_dl - * is sent at the tail of the message. + * that sent for ZEBRA_INTERFACE_ADD/ZEBRA_INTERFACE_DELETE, + * except that no sockaddr_dl is sent at the tail of the message. */ struct interface *zebra_interface_state_read(struct stream *s, vrf_id_t vrf_id) { struct interface *ifp; - char ifname_tmp[INTERFACE_NAMSIZ]; + char ifname_tmp[INTERFACE_NAMSIZ + 1] = {}; /* Read interface name. */ - stream_get(ifname_tmp, s, INTERFACE_NAMSIZ); + STREAM_GET(ifname_tmp, s, INTERFACE_NAMSIZ); /* Lookup this by interface index. */ ifp = if_lookup_by_name(ifname_tmp, vrf_id); @@ -1476,43 +1933,101 @@ struct interface *zebra_interface_state_read(struct stream *s, vrf_id_t vrf_id) zebra_interface_if_set_value(s, ifp); return ifp; +stream_failure: + return NULL; } -static void link_params_set_value(struct stream *s, struct if_link_params *iflp) +static void zclient_interface_delete(struct zclient *zclient, vrf_id_t vrf_id) { + struct interface *ifp; + struct stream *s = zclient->ibuf; - if (iflp == NULL) + ifp = zebra_interface_state_read(s, vrf_id); + + if (ifp == NULL) + return; + + if_destroy_via_zapi(ifp); + return; +} + +static void zclient_interface_up(struct zclient *zclient, vrf_id_t vrf_id) +{ + struct interface *ifp; + struct stream *s = zclient->ibuf; + + ifp = zebra_interface_state_read(s, vrf_id); + + if (!ifp) + return; + + if_up_via_zapi(ifp); +} + +static void zclient_interface_down(struct zclient *zclient, vrf_id_t vrf_id) +{ + struct interface *ifp; + struct stream *s = zclient->ibuf; + + ifp = zebra_interface_state_read(s, vrf_id); + + if (!ifp) return; - iflp->lp_status = stream_getl(s); - iflp->te_metric = stream_getl(s); - iflp->max_bw = stream_getf(s); - iflp->max_rsv_bw = stream_getf(s); - uint32_t bwclassnum = stream_getl(s); + if_down_via_zapi(ifp); +} + +static void zclient_handle_error(ZAPI_CALLBACK_ARGS) +{ + enum zebra_error_types error; + struct stream *s = zclient->ibuf; + + zapi_error_decode(s, &error); + + if (zclient->handle_error) + (*zclient->handle_error)(error); +} + +static int link_params_set_value(struct stream *s, struct if_link_params *iflp) +{ + + if (iflp == NULL) + return -1; + + uint32_t bwclassnum; + + STREAM_GETL(s, iflp->lp_status); + STREAM_GETL(s, iflp->te_metric); + STREAM_GETF(s, iflp->max_bw); + STREAM_GETF(s, iflp->max_rsv_bw); + STREAM_GETL(s, bwclassnum); { unsigned int i; for (i = 0; i < bwclassnum && i < MAX_CLASS_TYPE; i++) - iflp->unrsv_bw[i] = stream_getf(s); + STREAM_GETF(s, iflp->unrsv_bw[i]); if (i < bwclassnum) flog_err( EC_LIB_ZAPI_MISSMATCH, - "%s: received %d > %d (MAX_CLASS_TYPE) bw entries" - " - outdated library?", + "%s: received %d > %d (MAX_CLASS_TYPE) bw entries - outdated library?", __func__, bwclassnum, MAX_CLASS_TYPE); } - iflp->admin_grp = stream_getl(s); - iflp->rmt_as = stream_getl(s); + STREAM_GETL(s, iflp->admin_grp); + STREAM_GETL(s, iflp->rmt_as); iflp->rmt_ip.s_addr = stream_get_ipv4(s); - iflp->av_delay = stream_getl(s); - iflp->min_delay = stream_getl(s); - iflp->max_delay = stream_getl(s); - iflp->delay_var = stream_getl(s); + STREAM_GETL(s, iflp->av_delay); + STREAM_GETL(s, iflp->min_delay); + STREAM_GETL(s, iflp->max_delay); + STREAM_GETL(s, iflp->delay_var); - iflp->pkt_loss = stream_getf(s); - iflp->res_bw = stream_getf(s); - iflp->ava_bw = stream_getf(s); - iflp->use_bw = stream_getf(s); + STREAM_GETF(s, iflp->pkt_loss); + STREAM_GETF(s, iflp->res_bw); + STREAM_GETF(s, iflp->ava_bw); + STREAM_GETF(s, iflp->use_bw); + + return 0; +stream_failure: + return -1; } struct interface *zebra_interface_link_params_read(struct stream *s, @@ -1521,9 +2036,7 @@ struct interface *zebra_interface_link_params_read(struct stream *s, struct if_link_params *iflp; ifindex_t ifindex; - assert(s); - - ifindex = stream_getl(s); + STREAM_GETL(s, ifindex); struct interface *ifp = if_lookup_by_index(ifindex, vrf_id); @@ -1537,34 +2050,41 @@ struct interface *zebra_interface_link_params_read(struct stream *s, if ((iflp = if_link_params_get(ifp)) == NULL) return NULL; - link_params_set_value(s, iflp); + if (link_params_set_value(s, iflp) != 0) + goto stream_failure; return ifp; + +stream_failure: + return NULL; } -void zebra_interface_if_set_value(struct stream *s, struct interface *ifp) +static void zebra_interface_if_set_value(struct stream *s, + struct interface *ifp) { uint8_t link_params_status = 0; - ifindex_t old_ifindex; + ifindex_t old_ifindex, new_ifindex; - old_ifindex = ifp->ifindex; + old_ifindex = ifp->oldifindex; /* Read interface's index. */ - if_set_index(ifp, stream_getl(s)); - ifp->status = stream_getc(s); + STREAM_GETL(s, new_ifindex); + if_set_index(ifp, new_ifindex); + STREAM_GETC(s, ifp->status); /* Read interface's value. */ - ifp->flags = stream_getq(s); - ifp->ptm_enable = stream_getc(s); - ifp->ptm_status = stream_getc(s); - ifp->metric = stream_getl(s); - ifp->speed = stream_getl(s); - ifp->mtu = stream_getl(s); - ifp->mtu6 = stream_getl(s); - ifp->bandwidth = stream_getl(s); - ifp->ll_type = stream_getl(s); - ifp->hw_addr_len = stream_getl(s); + STREAM_GETQ(s, ifp->flags); + STREAM_GETC(s, ifp->ptm_enable); + STREAM_GETC(s, ifp->ptm_status); + STREAM_GETL(s, ifp->metric); + STREAM_GETL(s, ifp->speed); + STREAM_GETL(s, ifp->mtu); + STREAM_GETL(s, ifp->mtu6); + STREAM_GETL(s, ifp->bandwidth); + STREAM_GETL(s, ifp->link_ifindex); + STREAM_GETL(s, ifp->ll_type); + STREAM_GETL(s, ifp->hw_addr_len); if (ifp->hw_addr_len) - stream_get(ifp->hw_addr, s, + STREAM_GET(ifp->hw_addr, s, MIN(ifp->hw_addr_len, INTERFACE_HWADDR_MAX)); /* Read Traffic Engineering status */ @@ -1576,6 +2096,11 @@ void zebra_interface_if_set_value(struct stream *s, struct interface *ifp) } nexthop_group_interface_state_change(ifp, old_ifindex); + + return; +stream_failure: + zlog_err("Could not parse interface values; aborting"); + assert(!"Failed to parse interface values"); } size_t zebra_interface_link_params_write(struct stream *s, @@ -1674,7 +2199,7 @@ struct connected *zebra_interface_address_read(int type, struct stream *s, memset(&d, 0, sizeof(d)); /* Get interface index. */ - ifindex = stream_getl(s); + STREAM_GETL(s, ifindex); /* Lookup index. */ ifp = if_lookup_by_index(ifindex, vrf_id); @@ -1687,16 +2212,18 @@ struct connected *zebra_interface_address_read(int type, struct stream *s, } /* Fetch flag. */ - ifc_flags = stream_getc(s); + STREAM_GETC(s, ifc_flags); /* Fetch interface address. */ - d.family = p.family = stream_getc(s); + STREAM_GETC(s, d.family); + p.family = d.family; plen = prefix_blen(&d); - zclient_stream_get_prefix(s, &p); + if (zclient_stream_get_prefix(s, &p) != 0) + goto stream_failure; /* Fetch destination address. */ - stream_get(&d.u.prefix, s, plen); + STREAM_GET(&d.u.prefix, s, plen); /* N.B. NULL destination pointers are encoded as all zeroes */ dp = memconstant(&d.u.prefix, 0, plen) ? NULL : &d; @@ -1722,7 +2249,7 @@ struct connected *zebra_interface_address_read(int type, struct stream *s, "warning: interface %s address %s with peer flag set, but no peer address!", ifp->name, prefix2str(ifc->address, buf, - sizeof buf)); + sizeof(buf))); UNSET_FLAG(ifc->flags, ZEBRA_IFA_PEER); } } @@ -1732,6 +2259,9 @@ struct connected *zebra_interface_address_read(int type, struct stream *s, } return ifc; + +stream_failure: + return NULL; } /* @@ -1767,7 +2297,7 @@ zebra_interface_nbr_address_read(int type, struct stream *s, vrf_id_t vrf_id) struct nbr_connected *ifc; /* Get interface index. */ - ifindex = stream_getl(s); + STREAM_GETL(s, ifindex); /* Lookup index. */ ifp = if_lookup_by_index(ifindex, vrf_id); @@ -1780,9 +2310,9 @@ zebra_interface_nbr_address_read(int type, struct stream *s, vrf_id_t vrf_id) return NULL; } - p.family = stream_getc(s); - stream_get(&p.u.prefix, s, prefix_blen(&p)); - p.prefixlen = stream_getc(s); + STREAM_GETC(s, p.family); + STREAM_GET(&p.u.prefix, s, prefix_blen(&p)); + STREAM_GETC(s, p.prefixlen); if (type == ZEBRA_INTERFACE_NBR_ADDRESS_ADD) { /* Currently only supporting P2P links, so any new RA source @@ -1806,18 +2336,21 @@ zebra_interface_nbr_address_read(int type, struct stream *s, vrf_id_t vrf_id) } return ifc; + +stream_failure: + return NULL; } struct interface *zebra_interface_vrf_update_read(struct stream *s, vrf_id_t vrf_id, vrf_id_t *new_vrf_id) { - char ifname[INTERFACE_NAMSIZ]; + char ifname[INTERFACE_NAMSIZ + 1] = {}; struct interface *ifp; vrf_id_t new_id; /* Read interface name. */ - stream_get(ifname, s, INTERFACE_NAMSIZ); + STREAM_GET(ifname, s, INTERFACE_NAMSIZ); /* Lookup interface. */ ifp = if_lookup_by_name(ifname, vrf_id); @@ -1829,10 +2362,13 @@ struct interface *zebra_interface_vrf_update_read(struct stream *s, } /* Fetch new VRF Id. */ - new_id = stream_getw(s); + STREAM_GETL(s, new_id); *new_vrf_id = new_id; return ifp; + +stream_failure: + return NULL; } /* filter unwanted messages until the expected one arrives */ @@ -1941,8 +2477,11 @@ int lm_label_manager_connect(struct zclient *zclient, int async) s = zclient->ibuf; /* read instance and proto */ - uint8_t proto = stream_getc(s); - uint16_t instance = stream_getw(s); + uint8_t proto; + uint16_t instance; + + STREAM_GETC(s, proto); + STREAM_GETW(s, instance); /* sanity */ if (proto != zclient->redist_default) @@ -1957,11 +2496,14 @@ int lm_label_manager_connect(struct zclient *zclient, int async) instance, zclient->instance); /* result code */ - result = stream_getc(s); + STREAM_GETC(s, result); if (zclient_debug) zlog_debug("LM connect-response received, result %u", result); return (int)result; + +stream_failure: + return -1; } /* @@ -1970,10 +2512,11 @@ int lm_label_manager_connect(struct zclient *zclient, int async) * @param zclient Zclient used to connect to label manager (zebra) * @param keep Avoid garbage collection * @param chunk_size Amount of labels requested + * @param base Base for the label chunk. if MPLS_LABEL_BASE_ANY we do not care * @result 0 on success, -1 otherwise */ int zclient_send_get_label_chunk(struct zclient *zclient, uint8_t keep, - uint32_t chunk_size) + uint32_t chunk_size, uint32_t base) { struct stream *s; @@ -1993,6 +2536,7 @@ int zclient_send_get_label_chunk(struct zclient *zclient, uint8_t keep, stream_putw(s, zclient->instance); stream_putc(s, keep); stream_putl(s, chunk_size); + stream_putl(s, base); /* Put length at the first point of the stream. */ stream_putw_at(s, 0, stream_get_endp(s)); @@ -2013,7 +2557,7 @@ int zclient_send_get_label_chunk(struct zclient *zclient, uint8_t keep, * @param end To write last assigned chunk label to * @result 0 on success, -1 otherwise */ -int lm_get_label_chunk(struct zclient *zclient, uint8_t keep, +int lm_get_label_chunk(struct zclient *zclient, uint8_t keep, uint32_t base, uint32_t chunk_size, uint32_t *start, uint32_t *end) { int ret; @@ -2038,6 +2582,8 @@ int lm_get_label_chunk(struct zclient *zclient, uint8_t keep, stream_putc(s, keep); /* chunk size */ stream_putl(s, chunk_size); + /* requested chunk base */ + stream_putl(s, base); /* Put length at the first point of the stream. */ stream_putw_at(s, 0, stream_get_endp(s)); @@ -2065,8 +2611,11 @@ int lm_get_label_chunk(struct zclient *zclient, uint8_t keep, s = zclient->ibuf; /* read proto and instance */ - uint8_t proto = stream_getc(s); - uint16_t instance = stream_getw(s); + uint8_t proto; + uint8_t instance; + + STREAM_GETC(s, proto); + STREAM_GETW(s, instance); /* sanities */ if (proto != zclient->redist_default) @@ -2078,11 +2627,20 @@ int lm_get_label_chunk(struct zclient *zclient, uint8_t keep, "Wrong instId (%u) in get chunk response Should be %u", instance, zclient->instance); + /* if we requested a specific chunk and it could not be allocated, the + * response message will end here + */ + if (!STREAM_READABLE(s)) { + zlog_info("Unable to assign Label Chunk to %s instance %u", + zebra_route_string(proto), instance); + return -1; + } + /* keep */ - response_keep = stream_getc(s); + STREAM_GETC(s, response_keep); /* start and end labels */ - *start = stream_getl(s); - *end = stream_getl(s); + STREAM_GETL(s, *start); + STREAM_GETL(s, *end); /* not owning this response */ if (keep != response_keep) { @@ -2104,6 +2662,9 @@ int lm_get_label_chunk(struct zclient *zclient, uint8_t keep, response_keep); return 0; + +stream_failure: + return -1; } /** @@ -2216,7 +2777,7 @@ int tm_table_manager_connect(struct zclient *zclient) return (int)result; stream_failure: - return 0; + return -1; } /** @@ -2283,8 +2844,9 @@ int tm_get_table_chunk(struct zclient *zclient, uint32_t chunk_size, if (zclient_debug) zlog_debug("Table Chunk assign: %u - %u ", *start, *end); -stream_failure: return 0; +stream_failure: + return -1; } /** @@ -2322,6 +2884,256 @@ int tm_release_table_chunk(struct zclient *zclient, uint32_t start, return zclient_send_message(zclient); } +int zebra_send_sr_policy(struct zclient *zclient, int cmd, + struct zapi_sr_policy *zp) +{ + if (zapi_sr_policy_encode(zclient->obuf, cmd, zp) < 0) + return -1; + return zclient_send_message(zclient); +} + +int zapi_sr_policy_encode(struct stream *s, int cmd, struct zapi_sr_policy *zp) +{ + struct zapi_srte_tunnel *zt = &zp->segment_list; + + stream_reset(s); + + zclient_create_header(s, cmd, VRF_DEFAULT); + stream_putl(s, zp->color); + stream_put_ipaddr(s, &zp->endpoint); + stream_write(s, &zp->name, SRTE_POLICY_NAME_MAX_LENGTH); + + stream_putc(s, zt->type); + stream_putl(s, zt->local_label); + + if (zt->label_num > MPLS_MAX_LABELS) { + flog_err(EC_LIB_ZAPI_ENCODE, + "%s: label %u: can't encode %u labels (maximum is %u)", + __func__, zt->local_label, zt->label_num, + MPLS_MAX_LABELS); + return -1; + } + stream_putw(s, zt->label_num); + + for (int i = 0; i < zt->label_num; i++) + stream_putl(s, zt->labels[i]); + + /* Put length at the first point of the stream. */ + stream_putw_at(s, 0, stream_get_endp(s)); + + return 0; +} + +int zapi_sr_policy_decode(struct stream *s, struct zapi_sr_policy *zp) +{ + memset(zp, 0, sizeof(*zp)); + + struct zapi_srte_tunnel *zt = &zp->segment_list; + + STREAM_GETL(s, zp->color); + STREAM_GET_IPADDR(s, &zp->endpoint); + STREAM_GET(&zp->name, s, SRTE_POLICY_NAME_MAX_LENGTH); + + /* segment list of active candidate path */ + STREAM_GETC(s, zt->type); + STREAM_GETL(s, zt->local_label); + STREAM_GETW(s, zt->label_num); + if (zt->label_num > MPLS_MAX_LABELS) { + flog_err(EC_LIB_ZAPI_ENCODE, + "%s: label %u: can't decode %u labels (maximum is %u)", + __func__, zt->local_label, zt->label_num, + MPLS_MAX_LABELS); + return -1; + } + for (int i = 0; i < zt->label_num; i++) + STREAM_GETL(s, zt->labels[i]); + + return 0; + +stream_failure: + return -1; +} + +int zapi_sr_policy_notify_status_decode(struct stream *s, + struct zapi_sr_policy *zp) +{ + memset(zp, 0, sizeof(*zp)); + + STREAM_GETL(s, zp->color); + STREAM_GET_IPADDR(s, &zp->endpoint); + STREAM_GET(&zp->name, s, SRTE_POLICY_NAME_MAX_LENGTH); + STREAM_GETL(s, zp->status); + + return 0; + +stream_failure: + return -1; +} + +int zebra_send_mpls_labels(struct zclient *zclient, int cmd, + struct zapi_labels *zl) +{ + if (zapi_labels_encode(zclient->obuf, cmd, zl) < 0) + return -1; + return zclient_send_message(zclient); +} + +int zapi_labels_encode(struct stream *s, int cmd, struct zapi_labels *zl) +{ + struct zapi_nexthop *znh; + + stream_reset(s); + + zclient_create_header(s, cmd, VRF_DEFAULT); + stream_putc(s, zl->message); + stream_putc(s, zl->type); + stream_putl(s, zl->local_label); + + if (CHECK_FLAG(zl->message, ZAPI_LABELS_FTN)) { + stream_putw(s, zl->route.prefix.family); + stream_put_prefix(s, &zl->route.prefix); + stream_putc(s, zl->route.type); + stream_putw(s, zl->route.instance); + } + + if (zl->nexthop_num > MULTIPATH_NUM) { + flog_err( + EC_LIB_ZAPI_ENCODE, + "%s: label %u: can't encode %u nexthops (maximum is %u)", + __func__, zl->local_label, zl->nexthop_num, + MULTIPATH_NUM); + return -1; + } + stream_putw(s, zl->nexthop_num); + + for (int i = 0; i < zl->nexthop_num; i++) { + znh = &zl->nexthops[i]; + + if (zapi_nexthop_encode(s, znh, 0, 0) < 0) + return -1; + } + + if (CHECK_FLAG(zl->message, ZAPI_LABELS_HAS_BACKUPS)) { + + if (zl->backup_nexthop_num > MULTIPATH_NUM) { + flog_err( + EC_LIB_ZAPI_ENCODE, + "%s: label %u: can't encode %u nexthops (maximum is %u)", + __func__, zl->local_label, zl->nexthop_num, + MULTIPATH_NUM); + return -1; + } + stream_putw(s, zl->backup_nexthop_num); + + for (int i = 0; i < zl->backup_nexthop_num; i++) { + znh = &zl->backup_nexthops[i]; + + if (zapi_nexthop_encode(s, znh, 0, 0) < 0) + return -1; + } + + } + + /* Put length at the first point of the stream. */ + stream_putw_at(s, 0, stream_get_endp(s)); + + return 0; +} + +int zapi_labels_decode(struct stream *s, struct zapi_labels *zl) +{ + struct zapi_nexthop *znh; + + memset(zl, 0, sizeof(*zl)); + + /* Get data. */ + STREAM_GETC(s, zl->message); + STREAM_GETC(s, zl->type); + STREAM_GETL(s, zl->local_label); + + if (CHECK_FLAG(zl->message, ZAPI_LABELS_FTN)) { + size_t psize; + + STREAM_GETW(s, zl->route.prefix.family); + STREAM_GETC(s, zl->route.prefix.prefixlen); + + psize = PSIZE(zl->route.prefix.prefixlen); + switch (zl->route.prefix.family) { + case AF_INET: + if (zl->route.prefix.prefixlen > IPV4_MAX_BITLEN) { + zlog_debug( + "%s: Specified prefix length %d is greater than a v4 address can support", + __func__, zl->route.prefix.prefixlen); + return -1; + } + STREAM_GET(&zl->route.prefix.u.prefix4.s_addr, s, + psize); + break; + case AF_INET6: + if (zl->route.prefix.prefixlen > IPV6_MAX_BITLEN) { + zlog_debug( + "%s: Specified prefix length %d is greater than a v6 address can support", + __func__, zl->route.prefix.prefixlen); + return -1; + } + STREAM_GET(&zl->route.prefix.u.prefix6, s, psize); + break; + default: + flog_err(EC_LIB_ZAPI_ENCODE, + "%s: Specified family %u is not v4 or v6", + __func__, zl->route.prefix.family); + return -1; + } + + STREAM_GETC(s, zl->route.type); + STREAM_GETW(s, zl->route.instance); + } + + STREAM_GETW(s, zl->nexthop_num); + + if (zl->nexthop_num > MULTIPATH_NUM) { + flog_warn( + EC_LIB_ZAPI_ENCODE, + "%s: Prefix %pFX has %d nexthops, but we can only use the first %d", + __func__, &zl->route.prefix, zl->nexthop_num, + MULTIPATH_NUM); + } + + zl->nexthop_num = MIN(MULTIPATH_NUM, zl->nexthop_num); + + for (int i = 0; i < zl->nexthop_num; i++) { + znh = &zl->nexthops[i]; + + if (zapi_nexthop_decode(s, znh, 0, 0) < 0) + return -1; + } + + if (CHECK_FLAG(zl->message, ZAPI_LABELS_HAS_BACKUPS)) { + STREAM_GETW(s, zl->backup_nexthop_num); + + if (zl->backup_nexthop_num > MULTIPATH_NUM) { + flog_warn( + EC_LIB_ZAPI_ENCODE, + "%s: Prefix %pFX has %d backup nexthops, but we can only use the first %d", + __func__, &zl->route.prefix, + zl->backup_nexthop_num, MULTIPATH_NUM); + } + + zl->backup_nexthop_num = MIN(MULTIPATH_NUM, + zl->backup_nexthop_num); + + for (int i = 0; i < zl->backup_nexthop_num; i++) { + znh = &zl->backup_nexthops[i]; + + if (zapi_nexthop_decode(s, znh, 0, 0) < 0) + return -1; + } + } + + return 0; +stream_failure: + return -1; +} int zebra_send_pw(struct zclient *zclient, int command, struct zapi_pw *pw) { @@ -2371,9 +3183,7 @@ int zebra_send_pw(struct zclient *zclient, int command, struct zapi_pw *pw) /* * Receive PW status update from Zebra and send it to LDE process. */ -void zebra_read_pw_status_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id, - struct zapi_pw_status *pw) +int zebra_read_pw_status_update(ZAPI_CALLBACK_ARGS, struct zapi_pw_status *pw) { struct stream *s; @@ -2382,12 +3192,15 @@ void zebra_read_pw_status_update(int command, struct zclient *zclient, /* Get data. */ stream_get(pw->ifname, s, IF_NAMESIZE); - pw->ifindex = stream_getl(s); - pw->status = stream_getl(s); + STREAM_GETL(s, pw->ifindex); + STREAM_GETL(s, pw->status); + + return 0; +stream_failure: + return -1; } -static void zclient_capability_decode(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static void zclient_capability_decode(ZAPI_CALLBACK_ARGS) { struct zclient_capabilities cap; struct stream *s = zclient->ibuf; @@ -2395,7 +3208,14 @@ static void zclient_capability_decode(int command, struct zclient *zclient, uint8_t mpls_enabled; STREAM_GETL(s, vrf_backend); - vrf_configure_backend(vrf_backend); + + if (vrf_backend < 0 || vrf_configure_backend(vrf_backend)) { + flog_err(EC_LIB_ZAPI_ENCODE, + "%s: Garbage VRF backend type: %d\n", __func__, + vrf_backend); + goto stream_failure; + } + memset(&cap, 0, sizeof(cap)); STREAM_GETC(s, mpls_enabled); @@ -2410,6 +3230,237 @@ static void zclient_capability_decode(int command, struct zclient *zclient, return; } +void zclient_send_mlag_register(struct zclient *client, uint32_t bit_map) +{ + struct stream *s; + + s = client->obuf; + stream_reset(s); + + zclient_create_header(s, ZEBRA_MLAG_CLIENT_REGISTER, VRF_DEFAULT); + stream_putl(s, bit_map); + + stream_putw_at(s, 0, stream_get_endp(s)); + zclient_send_message(client); +} + +void zclient_send_mlag_deregister(struct zclient *client) +{ + zebra_message_send(client, ZEBRA_MLAG_CLIENT_UNREGISTER, VRF_DEFAULT); +} + +void zclient_send_mlag_data(struct zclient *client, struct stream *client_s) +{ + struct stream *s; + + s = client->obuf; + stream_reset(s); + + zclient_create_header(s, ZEBRA_MLAG_FORWARD_MSG, VRF_DEFAULT); + stream_put(s, client_s->data, client_s->endp); + + stream_putw_at(s, 0, stream_get_endp(s)); + zclient_send_message(client); +} + +static void zclient_mlag_process_up(ZAPI_CALLBACK_ARGS) +{ + if (zclient->mlag_process_up) + (*zclient->mlag_process_up)(); +} + +static void zclient_mlag_process_down(ZAPI_CALLBACK_ARGS) +{ + if (zclient->mlag_process_down) + (*zclient->mlag_process_down)(); +} + +static void zclient_mlag_handle_msg(ZAPI_CALLBACK_ARGS) +{ + if (zclient->mlag_handle_msg) + (*zclient->mlag_handle_msg)(zclient->ibuf, length); +} + +/* + * Send an OPAQUE message, contents opaque to zebra. The message header + * is a message subtype. + */ +int zclient_send_opaque(struct zclient *zclient, uint32_t type, + const uint8_t *data, size_t datasize) +{ + int ret; + struct stream *s; + uint16_t flags = 0; + + /* Check buffer size */ + if (STREAM_SIZE(zclient->obuf) < + (ZEBRA_HEADER_SIZE + sizeof(type) + datasize)) + return -1; + + s = zclient->obuf; + stream_reset(s); + + zclient_create_header(s, ZEBRA_OPAQUE_MESSAGE, VRF_DEFAULT); + + /* Send sub-type and flags */ + stream_putl(s, type); + stream_putw(s, flags); + + /* Send opaque data */ + stream_write(s, data, datasize); + + /* Put length into the header at the start of the stream. */ + stream_putw_at(s, 0, stream_get_endp(s)); + + ret = zclient_send_message(zclient); + + return ret; +} + +/* + * Send an OPAQUE message to a specific zclient. The contents are opaque + * to zebra. + */ +int zclient_send_opaque_unicast(struct zclient *zclient, uint32_t type, + uint8_t proto, uint16_t instance, + uint32_t session_id, const uint8_t *data, + size_t datasize) +{ + int ret; + struct stream *s; + uint16_t flags = 0; + + /* Check buffer size */ + if (STREAM_SIZE(zclient->obuf) < + (ZEBRA_HEADER_SIZE + sizeof(struct zapi_opaque_msg) + datasize)) + return -1; + + s = zclient->obuf; + stream_reset(s); + + zclient_create_header(s, ZEBRA_OPAQUE_MESSAGE, VRF_DEFAULT); + + /* Send sub-type and flags */ + SET_FLAG(flags, ZAPI_OPAQUE_FLAG_UNICAST); + stream_putl(s, type); + stream_putw(s, flags); + + /* Send destination client info */ + stream_putc(s, proto); + stream_putw(s, instance); + stream_putl(s, session_id); + + /* Send opaque data */ + stream_write(s, data, datasize); + + /* Put length into the header at the start of the stream. */ + stream_putw_at(s, 0, stream_get_endp(s)); + + ret = zclient_send_message(zclient); + + return ret; +} + +/* + * Decode incoming opaque message into info struct + */ +int zclient_opaque_decode(struct stream *s, struct zapi_opaque_msg *info) +{ + memset(info, 0, sizeof(*info)); + + /* Decode subtype and flags */ + STREAM_GETL(s, info->type); + STREAM_GETW(s, info->flags); + + /* Decode unicast client info if present */ + if (CHECK_FLAG(info->flags, ZAPI_OPAQUE_FLAG_UNICAST)) { + STREAM_GETC(s, info->proto); + STREAM_GETW(s, info->instance); + STREAM_GETL(s, info->session_id); + } + + info->len = STREAM_READABLE(s); + + return 0; + +stream_failure: + + return -1; +} + +/* + * Send a registration request for opaque messages with a specified subtype. + */ +int zclient_register_opaque(struct zclient *zclient, uint32_t type) +{ + int ret; + struct stream *s; + + s = zclient->obuf; + stream_reset(s); + + zclient_create_header(s, ZEBRA_OPAQUE_REGISTER, VRF_DEFAULT); + + /* Send sub-type */ + stream_putl(s, type); + + /* Add zclient info */ + stream_putc(s, zclient->redist_default); + stream_putw(s, zclient->instance); + stream_putl(s, zclient->session_id); + + /* Put length at the first point of the stream. */ + stream_putw_at(s, 0, stream_get_endp(s)); + + ret = zclient_send_message(zclient); + + return ret; +} + +/* + * Send an un-registration request for a specified opaque subtype. + */ +int zclient_unregister_opaque(struct zclient *zclient, uint32_t type) +{ + int ret; + struct stream *s; + + s = zclient->obuf; + stream_reset(s); + + zclient_create_header(s, ZEBRA_OPAQUE_UNREGISTER, VRF_DEFAULT); + + /* Send sub-type */ + stream_putl(s, type); + + /* Add zclient info */ + stream_putc(s, zclient->redist_default); + stream_putw(s, zclient->instance); + stream_putl(s, zclient->session_id); + + /* Put length at the first point of the stream. */ + stream_putw_at(s, 0, stream_get_endp(s)); + + ret = zclient_send_message(zclient); + + return ret; +} + +/* Utility to decode opaque registration info */ +int zapi_opaque_reg_decode(struct stream *s, struct zapi_opaque_reg_info *info) +{ + STREAM_GETL(s, info->type); + STREAM_GETC(s, info->proto); + STREAM_GETW(s, info->instance); + STREAM_GETL(s, info->session_id); + + return 0; + +stream_failure: + + return -1; +} + /* Zebra client message read function. */ static int zclient_read(struct thread *thread) { @@ -2526,14 +3577,10 @@ static int zclient_read(struct thread *thread) zclient_vrf_delete(zclient, vrf_id); break; case ZEBRA_INTERFACE_ADD: - if (zclient->interface_add) - (*zclient->interface_add)(command, zclient, length, - vrf_id); + zclient_interface_add(zclient, vrf_id); break; case ZEBRA_INTERFACE_DELETE: - if (zclient->interface_delete) - (*zclient->interface_delete)(command, zclient, length, - vrf_id); + zclient_interface_delete(zclient, vrf_id); break; case ZEBRA_INTERFACE_ADDRESS_ADD: if (zclient->interface_address_add) @@ -2561,14 +3608,10 @@ static int zclient_read(struct thread *thread) command, zclient, length, vrf_id); break; case ZEBRA_INTERFACE_UP: - if (zclient->interface_up) - (*zclient->interface_up)(command, zclient, length, - vrf_id); + zclient_interface_up(zclient, vrf_id); break; case ZEBRA_INTERFACE_DOWN: - if (zclient->interface_down) - (*zclient->interface_down)(command, zclient, length, - vrf_id); + zclient_interface_down(zclient, vrf_id); break; case ZEBRA_INTERFACE_VRF_UPDATE: if (zclient->interface_vrf_update) @@ -2625,6 +3668,16 @@ static int zclient_read(struct thread *thread) (*zclient->local_es_del)(command, zclient, length, vrf_id); break; + case ZEBRA_LOCAL_ES_EVI_ADD: + if (zclient->local_es_evi_add) + (*zclient->local_es_evi_add)(command, zclient, length, + vrf_id); + break; + case ZEBRA_LOCAL_ES_EVI_DEL: + if (zclient->local_es_evi_del) + (*zclient->local_es_evi_del)(command, zclient, length, + vrf_id); + break; case ZEBRA_VNI_ADD: if (zclient->local_vni_add) (*zclient->local_vni_add)(command, zclient, length, @@ -2712,6 +3765,37 @@ static int zclient_read(struct thread *thread) (*zclient->vxlan_sg_del)(command, zclient, length, vrf_id); break; + case ZEBRA_MLAG_PROCESS_UP: + zclient_mlag_process_up(command, zclient, length, vrf_id); + break; + case ZEBRA_MLAG_PROCESS_DOWN: + zclient_mlag_process_down(command, zclient, length, vrf_id); + break; + case ZEBRA_MLAG_FORWARD_MSG: + zclient_mlag_handle_msg(command, zclient, length, vrf_id); + break; + case ZEBRA_ERROR: + zclient_handle_error(command, zclient, length, vrf_id); + break; + case ZEBRA_OPAQUE_MESSAGE: + if (zclient->opaque_msg_handler) + (*zclient->opaque_msg_handler)(command, zclient, length, + vrf_id); + break; + case ZEBRA_OPAQUE_REGISTER: + if (zclient->opaque_register_handler) + (*zclient->opaque_register_handler)(command, zclient, + length, vrf_id); + break; + case ZEBRA_OPAQUE_UNREGISTER: + if (zclient->opaque_unregister_handler) + (*zclient->opaque_unregister_handler)(command, zclient, + length, vrf_id); + break; + case ZEBRA_SR_POLICY_NOTIFY_STATUS: + if (zclient->sr_policy_notify_status) + (*zclient->sr_policy_notify_status)(command, zclient, + length, vrf_id); default: break; } @@ -2828,3 +3912,92 @@ void zclient_interface_set_master(struct zclient *client, stream_putw_at(s, 0, stream_get_endp(s)); zclient_send_message(client); } + +/* + * Send capabilities message to zebra + */ +int32_t zclient_capabilities_send(uint32_t cmd, struct zclient *zclient, + struct zapi_cap *api) +{ + + struct stream *s; + + if (zclient == NULL) + return -1; + + s = zclient->obuf; + stream_reset(s); + zclient_create_header(s, cmd, 0); + stream_putl(s, api->cap); + + switch (api->cap) { + case ZEBRA_CLIENT_GR_CAPABILITIES: + case ZEBRA_CLIENT_RIB_STALE_TIME: + stream_putl(s, api->stale_removal_time); + stream_putl(s, api->vrf_id); + break; + case ZEBRA_CLIENT_ROUTE_UPDATE_COMPLETE: + case ZEBRA_CLIENT_ROUTE_UPDATE_PENDING: + stream_putl(s, api->afi); + stream_putl(s, api->safi); + stream_putl(s, api->vrf_id); + break; + case ZEBRA_CLIENT_GR_DISABLE: + stream_putl(s, api->vrf_id); + break; + } + + /* Put length at the first point of the stream */ + stream_putw_at(s, 0, stream_get_endp(s)); + + return zclient_send_message(zclient); +} + +/* + * Process capabilities message from zebra + */ +int32_t zapi_capabilities_decode(struct stream *s, struct zapi_cap *api) +{ + + memset(api, 0, sizeof(*api)); + + STREAM_GETL(s, api->cap); + switch (api->cap) { + case ZEBRA_CLIENT_GR_CAPABILITIES: + case ZEBRA_CLIENT_RIB_STALE_TIME: + STREAM_GETL(s, api->stale_removal_time); + STREAM_GETL(s, api->vrf_id); + break; + case ZEBRA_CLIENT_ROUTE_UPDATE_COMPLETE: + case ZEBRA_CLIENT_ROUTE_UPDATE_PENDING: + STREAM_GETL(s, api->afi); + STREAM_GETL(s, api->safi); + STREAM_GETL(s, api->vrf_id); + break; + case ZEBRA_CLIENT_GR_DISABLE: + STREAM_GETL(s, api->vrf_id); + break; + } +stream_failure: + return 0; +} + +int zclient_send_neigh_discovery_req(struct zclient *zclient, + const struct interface *ifp, + const struct prefix *p) +{ + struct stream *s; + + s = zclient->obuf; + stream_reset(s); + + zclient_create_header(s, ZEBRA_NEIGH_DISCOVER, ifp->vrf_id); + stream_putl(s, ifp->ifindex); + + stream_putc(s, p->family); + stream_putc(s, p->prefixlen); + stream_put(s, &p->u.prefix, prefix_blen(p)); + + stream_putw_at(s, 0, stream_get_endp(s)); + return zclient_send_message(zclient); +} diff --git a/lib/zclient.h b/lib/zclient.h index 0926281f2e..37b8e38713 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -37,9 +37,23 @@ #include "pw.h" #include "mlag.h" +#include "srte.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Zebra types. Used in Zserv message header. */ +typedef uint16_t zebra_size_t; + +/* Marker value used in new Zserv, in the byte location corresponding + * the command value in the old zserv header. To allow old and new + * Zserv headers to be distinguished from each other. + */ +#define ZEBRA_HEADER_MARKER 254 /* For input/output buffer to zebra. */ -#define ZEBRA_MAX_PACKET_SIZ 16384 +#define ZEBRA_MAX_PACKET_SIZ 16384U /* Zebra header size. */ #define ZEBRA_HEADER_SIZE 10 @@ -64,6 +78,20 @@ #define ZEBRA_FEC_REGISTER_LABEL 0x1 #define ZEBRA_FEC_REGISTER_LABEL_INDEX 0x2 +/* Client capabilities */ +enum zserv_client_capabilities { + ZEBRA_CLIENT_GR_CAPABILITIES = 1, + ZEBRA_CLIENT_ROUTE_UPDATE_COMPLETE = 2, + ZEBRA_CLIENT_ROUTE_UPDATE_PENDING = 3, + ZEBRA_CLIENT_GR_DISABLE = 4, + ZEBRA_CLIENT_RIB_STALE_TIME +}; + +/* Macro to check if there GR enabled. */ +#define ZEBRA_CLIENT_GR_ENABLED(X) (X == ZEBRA_CLIENT_GR_CAPABILITIES) + +#define ZEBRA_SR_POLICY_NAME_MAX_LENGTH 100 + extern struct sockaddr_storage zclient_addr; extern socklen_t zclient_addr_len; @@ -76,6 +104,7 @@ typedef enum { ZEBRA_INTERFACE_UP, ZEBRA_INTERFACE_DOWN, ZEBRA_INTERFACE_SET_MASTER, + ZEBRA_INTERFACE_SET_PROTODOWN, ZEBRA_ROUTE_ADD, ZEBRA_ROUTE_DELETE, ZEBRA_ROUTE_NOTIFY_OWNER, @@ -116,6 +145,10 @@ typedef enum { ZEBRA_INTERFACE_LINK_PARAMS, ZEBRA_MPLS_LABELS_ADD, ZEBRA_MPLS_LABELS_DELETE, + ZEBRA_MPLS_LABELS_REPLACE, + ZEBRA_SR_POLICY_SET, + ZEBRA_SR_POLICY_DELETE, + ZEBRA_SR_POLICY_NOTIFY_STATUS, ZEBRA_IPMR_ROUTE_STATS, ZEBRA_LABEL_MANAGER_CONNECT, ZEBRA_LABEL_MANAGER_CONNECT_ASYNC, @@ -130,6 +163,10 @@ typedef enum { ZEBRA_ADVERTISE_ALL_VNI, ZEBRA_LOCAL_ES_ADD, ZEBRA_LOCAL_ES_DEL, + ZEBRA_REMOTE_ES_VTEP_ADD, + ZEBRA_REMOTE_ES_VTEP_DEL, + ZEBRA_LOCAL_ES_EVI_ADD, + ZEBRA_LOCAL_ES_EVI_DEL, ZEBRA_VNI_ADD, ZEBRA_VNI_DEL, ZEBRA_L3VNI_ADD, @@ -166,8 +203,45 @@ typedef enum { ZEBRA_VXLAN_FLOOD_CONTROL, ZEBRA_VXLAN_SG_ADD, ZEBRA_VXLAN_SG_DEL, + ZEBRA_VXLAN_SG_REPLAY, + ZEBRA_MLAG_PROCESS_UP, + ZEBRA_MLAG_PROCESS_DOWN, + ZEBRA_MLAG_CLIENT_REGISTER, + ZEBRA_MLAG_CLIENT_UNREGISTER, + ZEBRA_MLAG_FORWARD_MSG, + ZEBRA_ERROR, + ZEBRA_CLIENT_CAPABILITIES, + ZEBRA_OPAQUE_MESSAGE, + ZEBRA_OPAQUE_REGISTER, + ZEBRA_OPAQUE_UNREGISTER, + ZEBRA_NEIGH_DISCOVER, } zebra_message_types_t; +enum zebra_error_types { + ZEBRA_UNKNOWN_ERROR, /* Error of unknown type */ + ZEBRA_NO_VRF, /* Vrf in header was not found */ + ZEBRA_INVALID_MSG_TYPE, /* No handler found for msg type */ +}; + +static inline const char *zebra_error_type2str(enum zebra_error_types type) +{ + const char *ret = "UNKNOWN"; + + switch (type) { + case ZEBRA_UNKNOWN_ERROR: + ret = "ZEBRA_UNKNOWN_ERROR"; + break; + case ZEBRA_NO_VRF: + ret = "ZEBRA_NO_VRF"; + break; + case ZEBRA_INVALID_MSG_TYPE: + ret = "ZEBRA_INVALID_MSG_TYPE"; + break; + } + + return ret; +} + struct redist_proto { uint8_t enabled; struct list *instances; @@ -179,6 +253,15 @@ struct zclient_capabilities { enum mlag_role role; }; +/* Graceful Restart Capabilities message */ +struct zapi_cap { + enum zserv_client_capabilities cap; + uint32_t stale_removal_time; + afi_t afi; + safi_t safi; + vrf_id_t vrf_id; +}; + /* Structure for the zebra client. */ struct zclient { /* The thread master we schedule ourselves on */ @@ -190,6 +273,12 @@ struct zclient { /* Do we care about failure events for route install? */ bool receive_notify; + /* Is this a synchronous client? */ + bool synchronous; + + /* Session id (optional) to support clients with multiple sessions */ + uint32_t session_id; + /* Socket to zebra daemon. */ int sock; @@ -221,66 +310,55 @@ struct zclient { /* Redistribute defauilt. */ vrf_bitmap_t default_information[AFI_MAX]; +#define ZAPI_CALLBACK_ARGS \ + int cmd, struct zclient *zclient, uint16_t length, vrf_id_t vrf_id + /* Pointer to the callback functions. */ void (*zebra_connected)(struct zclient *); void (*zebra_capabilities)(struct zclient_capabilities *cap); - int (*router_id_update)(int, struct zclient *, uint16_t, vrf_id_t); - int (*interface_add)(int, struct zclient *, uint16_t, vrf_id_t); - int (*interface_delete)(int, struct zclient *, uint16_t, vrf_id_t); - int (*interface_up)(int, struct zclient *, uint16_t, vrf_id_t); - int (*interface_down)(int, struct zclient *, uint16_t, vrf_id_t); - int (*interface_address_add)(int, struct zclient *, uint16_t, vrf_id_t); - int (*interface_address_delete)(int, struct zclient *, uint16_t, - vrf_id_t); - int (*interface_link_params)(int, struct zclient *, uint16_t, vrf_id_t); - int (*interface_bfd_dest_update)(int, struct zclient *, uint16_t, - vrf_id_t); - int (*interface_nbr_address_add)(int, struct zclient *, uint16_t, - vrf_id_t); - int (*interface_nbr_address_delete)(int, struct zclient *, uint16_t, - vrf_id_t); - int (*interface_vrf_update)(int, struct zclient *, uint16_t, vrf_id_t); - int (*nexthop_update)(int, struct zclient *, uint16_t, vrf_id_t); - int (*import_check_update)(int, struct zclient *, uint16_t, vrf_id_t); - int (*bfd_dest_replay)(int, struct zclient *, uint16_t, vrf_id_t); - int (*redistribute_route_add)(int, struct zclient *, uint16_t, - vrf_id_t); - int (*redistribute_route_del)(int, struct zclient *, uint16_t, - vrf_id_t); + int (*router_id_update)(ZAPI_CALLBACK_ARGS); + int (*interface_address_add)(ZAPI_CALLBACK_ARGS); + int (*interface_address_delete)(ZAPI_CALLBACK_ARGS); + int (*interface_link_params)(ZAPI_CALLBACK_ARGS); + int (*interface_bfd_dest_update)(ZAPI_CALLBACK_ARGS); + int (*interface_nbr_address_add)(ZAPI_CALLBACK_ARGS); + int (*interface_nbr_address_delete)(ZAPI_CALLBACK_ARGS); + int (*interface_vrf_update)(ZAPI_CALLBACK_ARGS); + int (*nexthop_update)(ZAPI_CALLBACK_ARGS); + int (*import_check_update)(ZAPI_CALLBACK_ARGS); + int (*bfd_dest_replay)(ZAPI_CALLBACK_ARGS); + int (*redistribute_route_add)(ZAPI_CALLBACK_ARGS); + int (*redistribute_route_del)(ZAPI_CALLBACK_ARGS); int (*fec_update)(int, struct zclient *, uint16_t); - int (*local_es_add)(int command, struct zclient *zclient, - uint16_t length, vrf_id_t vrf_id); - int (*local_es_del)(int command, struct zclient *zclient, - uint16_t length, vrf_id_t vrf_id); - int (*local_vni_add)(int, struct zclient *, uint16_t, vrf_id_t); - int (*local_vni_del)(int, struct zclient *, uint16_t, vrf_id_t); - int (*local_l3vni_add)(int, struct zclient *, uint16_t, vrf_id_t); - int (*local_l3vni_del)(int, struct zclient *, uint16_t, vrf_id_t); - void (*local_ip_prefix_add)(int, struct zclient *, uint16_t, vrf_id_t); - void (*local_ip_prefix_del)(int, struct zclient *, uint16_t, vrf_id_t); - int (*local_macip_add)(int, struct zclient *, uint16_t, vrf_id_t); - int (*local_macip_del)(int, struct zclient *, uint16_t, vrf_id_t); - int (*pw_status_update)(int, struct zclient *, uint16_t, vrf_id_t); - int (*route_notify_owner)(int command, struct zclient *zclient, - uint16_t length, vrf_id_t vrf_id); - int (*rule_notify_owner)(int command, struct zclient *zclient, - uint16_t length, vrf_id_t vrf_id); - void (*label_chunk)(int command, struct zclient *zclient, - uint16_t length, vrf_id_t vrf_id); - int (*ipset_notify_owner)(int command, struct zclient *zclient, - uint16_t length, vrf_id_t vrf_id); - int (*ipset_entry_notify_owner)(int command, - struct zclient *zclient, - uint16_t length, - vrf_id_t vrf_id); - int (*iptable_notify_owner)(int command, - struct zclient *zclient, - uint16_t length, - vrf_id_t vrf_id); - int (*vxlan_sg_add)(int command, struct zclient *client, - uint16_t length, vrf_id_t vrf_id); - int (*vxlan_sg_del)(int command, struct zclient *client, - uint16_t length, vrf_id_t vrf_id_t); + int (*local_es_add)(ZAPI_CALLBACK_ARGS); + int (*local_es_del)(ZAPI_CALLBACK_ARGS); + int (*local_es_evi_add)(ZAPI_CALLBACK_ARGS); + int (*local_es_evi_del)(ZAPI_CALLBACK_ARGS); + int (*local_vni_add)(ZAPI_CALLBACK_ARGS); + int (*local_vni_del)(ZAPI_CALLBACK_ARGS); + int (*local_l3vni_add)(ZAPI_CALLBACK_ARGS); + int (*local_l3vni_del)(ZAPI_CALLBACK_ARGS); + void (*local_ip_prefix_add)(ZAPI_CALLBACK_ARGS); + void (*local_ip_prefix_del)(ZAPI_CALLBACK_ARGS); + int (*local_macip_add)(ZAPI_CALLBACK_ARGS); + int (*local_macip_del)(ZAPI_CALLBACK_ARGS); + int (*pw_status_update)(ZAPI_CALLBACK_ARGS); + int (*route_notify_owner)(ZAPI_CALLBACK_ARGS); + int (*rule_notify_owner)(ZAPI_CALLBACK_ARGS); + void (*label_chunk)(ZAPI_CALLBACK_ARGS); + int (*ipset_notify_owner)(ZAPI_CALLBACK_ARGS); + int (*ipset_entry_notify_owner)(ZAPI_CALLBACK_ARGS); + int (*iptable_notify_owner)(ZAPI_CALLBACK_ARGS); + int (*vxlan_sg_add)(ZAPI_CALLBACK_ARGS); + int (*vxlan_sg_del)(ZAPI_CALLBACK_ARGS); + int (*mlag_process_up)(void); + int (*mlag_process_down)(void); + int (*mlag_handle_msg)(struct stream *msg, int len); + int (*handle_error)(enum zebra_error_types error); + int (*opaque_msg_handler)(ZAPI_CALLBACK_ARGS); + int (*opaque_register_handler)(ZAPI_CALLBACK_ARGS); + int (*opaque_unregister_handler)(ZAPI_CALLBACK_ARGS); + int (*sr_policy_notify_status)(ZAPI_CALLBACK_ARGS); }; /* Zebra API message flag. */ @@ -290,13 +368,16 @@ struct zclient { #define ZAPI_MESSAGE_TAG 0x08 #define ZAPI_MESSAGE_MTU 0x10 #define ZAPI_MESSAGE_SRCPFX 0x20 -#define ZAPI_MESSAGE_LABEL 0x40 +/* Backup nexthops are present */ +#define ZAPI_MESSAGE_BACKUP_NEXTHOPS 0x40 + /* * This should only be used by a DAEMON that needs to communicate * the table being used is not in the VRF. You must pass the * default vrf, else this will be ignored. */ -#define ZAPI_MESSAGE_TABLEID 0x80 +#define ZAPI_MESSAGE_TABLEID 0x0080 +#define ZAPI_MESSAGE_SRTE 0x0100 #define ZSERV_VERSION 6 /* Zserv protocol message header */ @@ -307,13 +388,19 @@ struct zmsghdr { uint8_t version; vrf_id_t vrf_id; uint16_t command; -}; +} __attribute__((packed)); +#define ZAPI_HEADER_CMD_LOCATION offsetof(struct zmsghdr, command) +/* + * ZAPI nexthop. Note that these are sorted when associated with ZAPI routes, + * and that sorting must be aligned with the sorting of nexthops in + * lib/nexthop.c. Any new fields must be accounted for in zapi_nexthop_cmp(). + */ struct zapi_nexthop { enum nexthop_types_t type; vrf_id_t vrf_id; ifindex_t ifindex; - bool onlink; + uint8_t flags; union { union g_addr gate; enum blackhole_type bh_type; @@ -324,8 +411,28 @@ struct zapi_nexthop { mpls_label_t labels[MPLS_MAX_LABELS]; struct ethaddr rmac; + + uint32_t weight; + + /* Backup nexthops, for IP-FRR, TI-LFA, etc */ + uint8_t backup_num; + uint8_t backup_idx[NEXTHOP_MAX_BACKUPS]; + + /* SR-TE color. */ + uint32_t srte_color; }; +/* + * ZAPI nexthop flags values - we're encoding a single octet + * initially, so ensure that the on-the-wire encoding continues + * to match the number of valid flags. + */ + +#define ZAPI_NEXTHOP_FLAG_ONLINK 0x01 +#define ZAPI_NEXTHOP_FLAG_LABEL 0x02 +#define ZAPI_NEXTHOP_FLAG_WEIGHT 0x04 +#define ZAPI_NEXTHOP_FLAG_HAS_BACKUP 0x08 /* Nexthop has a backup */ + /* * Some of these data structures do not map easily to * a actual data structure size giving different compilers @@ -338,8 +445,44 @@ struct zapi_route { unsigned short instance; uint32_t flags; +/* + * Cause Zebra to consider this routes nexthops recursively + */ +#define ZEBRA_FLAG_ALLOW_RECURSION 0x01 +/* + * This is a route that is read in on startup that was left around + * from a previous run of FRR + */ +#define ZEBRA_FLAG_SELFROUTE 0x02 +/* + * This flag is used to tell Zebra that the BGP route being passed + * down is a IBGP route + */ +#define ZEBRA_FLAG_IBGP 0x04 +/* + * This is a route that has been selected for FIB installation. + * This flag is set in zebra and can be passed up to routing daemons + */ +#define ZEBRA_FLAG_SELECTED 0x08 +/* + * This is a route that we are telling Zebra that this route *must* + * win and will be installed even over ZEBRA_FLAG_SELECTED + */ +#define ZEBRA_FLAG_FIB_OVERRIDE 0x10 +/* + * This flag tells Zebra that the route is a EVPN route and should + * be treated specially + */ +#define ZEBRA_FLAG_EVPN_ROUTE 0x20 +/* + * This flag tells Zebra that it should treat the distance passed + * down as an additional discriminator for route selection of the + * route entry. This mainly is used for backup static routes. + */ +#define ZEBRA_FLAG_RR_USE_DISTANCE 0x40 - uint8_t message; + /* The older XXX_MESSAGE flags live here */ + uint32_t message; /* * This is an enum but we are going to treat it as a uint8_t @@ -353,6 +496,10 @@ struct zapi_route { uint16_t nexthop_num; struct zapi_nexthop nexthops[MULTIPATH_NUM]; + /* Support backup routes for IP FRR, TI-LFA, traffic engineering */ + uint16_t backup_nexthop_num; + struct zapi_nexthop backup_nexthops[MULTIPATH_NUM]; + uint8_t distance; uint32_t metric; @@ -364,6 +511,44 @@ struct zapi_route { vrf_id_t vrf_id; uint32_t tableid; + + /* SR-TE color (used for nexthop updates only). */ + uint32_t srte_color; +}; + +struct zapi_labels { + uint8_t message; +#define ZAPI_LABELS_FTN 0x01 +#define ZAPI_LABELS_HAS_BACKUPS 0x02 + enum lsp_types_t type; + mpls_label_t local_label; + struct { + struct prefix prefix; + uint8_t type; + unsigned short instance; + } route; + + uint16_t nexthop_num; + struct zapi_nexthop nexthops[MULTIPATH_NUM]; + + /* Backup nexthops, if present */ + uint16_t backup_nexthop_num; + struct zapi_nexthop backup_nexthops[MULTIPATH_NUM]; +}; + +struct zapi_srte_tunnel { + enum lsp_types_t type; + mpls_label_t local_label; + uint8_t label_num; + mpls_label_t labels[MPLS_MAX_LABELS]; +}; + +struct zapi_sr_policy { + uint32_t color; + struct ipaddr endpoint; + char name[SRTE_POLICY_NAME_MAX_LENGTH]; + struct zapi_srte_tunnel segment_list; + int status; }; struct zapi_pw { @@ -428,16 +613,46 @@ enum zapi_iptable_notify_owner { ZAPI_IPTABLE_FAIL_REMOVE, }; +static inline const char * +zapi_rule_notify_owner2str(enum zapi_rule_notify_owner note) +{ + const char *ret = "UNKNOWN"; + + switch (note) { + case ZAPI_RULE_FAIL_INSTALL: + ret = "ZAPI_RULE_FAIL_INSTALL"; + break; + case ZAPI_RULE_INSTALLED: + ret = "ZAPI_RULE_INSTALLED"; + break; + case ZAPI_RULE_FAIL_REMOVE: + ret = "ZAPI_RULE_FAIL_REMOVE"; + break; + case ZAPI_RULE_REMOVED: + ret = "ZAPI_RULE_REMOVED"; + break; + } + + return ret; +} + /* Zebra MAC types */ #define ZEBRA_MACIP_TYPE_STICKY 0x01 /* Sticky MAC*/ #define ZEBRA_MACIP_TYPE_GW 0x02 /* gateway (SVI) mac*/ #define ZEBRA_MACIP_TYPE_ROUTER_FLAG 0x04 /* Router Flag - proxy NA */ #define ZEBRA_MACIP_TYPE_OVERRIDE_FLAG 0x08 /* Override Flag */ +#define ZEBRA_MACIP_TYPE_SVI_IP 0x10 /* SVI MAC-IP */ +#define ZEBRA_MACIP_TYPE_PROXY_ADVERT 0x20 /* Not locally active */ +#define ZEBRA_MACIP_TYPE_SYNC_PATH 0x40 /* sync path */ +/* XXX - flags is an u8; that needs to be changed to u32 if you need + * to allocate past 0x80 + */ enum zebra_neigh_state { ZEBRA_NEIGH_INACTIVE = 0, ZEBRA_NEIGH_ACTIVE = 1 }; struct zclient_options { bool receive_notify; + bool synchronous; }; extern struct zclient_options zclient_options_default; @@ -458,6 +673,7 @@ extern unsigned short *redist_check_instance(struct redist_proto *, unsigned short); extern void redist_add_instance(struct redist_proto *, unsigned short); extern void redist_del_instance(struct redist_proto *, unsigned short); +extern void redist_del_all_instances(struct redist_proto *red); /* * Send to zebra that the specified vrf is using label to resolve @@ -478,11 +694,17 @@ extern void zclient_send_vrf_label(struct zclient *zclient, vrf_id_t vrf_id, extern void zclient_send_reg_requests(struct zclient *, vrf_id_t); extern void zclient_send_dereg_requests(struct zclient *, vrf_id_t); +extern int zclient_send_router_id_update(struct zclient *zclient, + zebra_message_types_t type, afi_t afi, + vrf_id_t vrf_id); extern void zclient_send_interface_radv_req(struct zclient *zclient, vrf_id_t vrf_id, struct interface *ifp, int enable, int ra_interval); +extern int zclient_send_interface_protodown(struct zclient *zclient, + vrf_id_t vrf_id, + struct interface *ifp, bool down); /* Send redistribute command to zebra daemon. Do not update zclient state. */ extern int zebra_redistribute_send(int command, struct zclient *, afi_t, @@ -562,7 +784,6 @@ extern bool zapi_parse_header(struct stream *zmsg, struct zmsghdr *hdr); extern void zclient_interface_set_master(struct zclient *client, struct interface *master, struct interface *slave); -extern struct interface *zebra_interface_add_read(struct stream *, vrf_id_t); extern struct interface *zebra_interface_state_read(struct stream *s, vrf_id_t); extern struct connected *zebra_interface_address_read(int, struct stream *, vrf_id_t); @@ -571,22 +792,19 @@ zebra_interface_nbr_address_read(int, struct stream *, vrf_id_t); extern struct interface *zebra_interface_vrf_update_read(struct stream *s, vrf_id_t vrf_id, vrf_id_t *new_vrf_id); -extern void zebra_interface_if_set_value(struct stream *, struct interface *); -extern void zebra_router_id_update_read(struct stream *s, struct prefix *rid); +extern int zebra_router_id_update_read(struct stream *s, struct prefix *rid); extern struct interface *zebra_interface_link_params_read(struct stream *s, vrf_id_t vrf_id); extern size_t zebra_interface_link_params_write(struct stream *, struct interface *); -extern int zclient_send_get_label_chunk( - struct zclient *zclient, - uint8_t keep, - uint32_t chunk_size); +extern int zclient_send_get_label_chunk(struct zclient *zclient, uint8_t keep, + uint32_t chunk_size, uint32_t base); extern int lm_label_manager_connect(struct zclient *zclient, int async); extern int lm_get_label_chunk(struct zclient *zclient, uint8_t keep, - uint32_t chunk_size, uint32_t *start, - uint32_t *end); + uint32_t base, uint32_t chunk_size, + uint32_t *start, uint32_t *end); extern int lm_release_label_chunk(struct zclient *zclient, uint32_t start, uint32_t end); extern int tm_table_manager_connect(struct zclient *zclient); @@ -595,24 +813,38 @@ extern int tm_get_table_chunk(struct zclient *zclient, uint32_t chunk_size, extern int tm_release_table_chunk(struct zclient *zclient, uint32_t start, uint32_t end); +extern int zebra_send_sr_policy(struct zclient *zclient, int cmd, + struct zapi_sr_policy *zp); +extern int zapi_sr_policy_encode(struct stream *s, int cmd, + struct zapi_sr_policy *zp); +extern int zapi_sr_policy_decode(struct stream *s, struct zapi_sr_policy *zp); +extern int zapi_sr_policy_notify_status_decode(struct stream *s, + struct zapi_sr_policy *zp); + +extern int zebra_send_mpls_labels(struct zclient *zclient, int cmd, + struct zapi_labels *zl); +extern int zapi_labels_encode(struct stream *s, int cmd, + struct zapi_labels *zl); +extern int zapi_labels_decode(struct stream *s, struct zapi_labels *zl); + extern int zebra_send_pw(struct zclient *zclient, int command, struct zapi_pw *pw); -extern void zebra_read_pw_status_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id, - struct zapi_pw_status *pw); +extern int zebra_read_pw_status_update(ZAPI_CALLBACK_ARGS, + struct zapi_pw_status *pw); extern int zclient_route_send(uint8_t, struct zclient *, struct zapi_route *); extern int zclient_send_rnh(struct zclient *zclient, int command, - struct prefix *p, bool exact_match, + const struct prefix *p, bool exact_match, vrf_id_t vrf_id); +int zapi_nexthop_encode(struct stream *s, const struct zapi_nexthop *api_nh, + uint32_t api_flags, uint32_t api_message); extern int zapi_route_encode(uint8_t, struct stream *, struct zapi_route *); extern int zapi_route_decode(struct stream *, struct zapi_route *); bool zapi_route_notify_decode(struct stream *s, struct prefix *p, uint32_t *tableid, enum zapi_route_notify_owner *note); bool zapi_rule_notify_decode(struct stream *s, uint32_t *seqno, - uint32_t *priority, uint32_t *unique, - ifindex_t *ifindex, + uint32_t *priority, uint32_t *unique, char *ifname, enum zapi_rule_notify_owner *note); bool zapi_ipset_notify_decode(struct stream *s, uint32_t *unique, @@ -628,9 +860,24 @@ bool zapi_iptable_notify_decode(struct stream *s, uint32_t *unique, enum zapi_iptable_notify_owner *note); -extern struct nexthop *nexthop_from_zapi_nexthop(struct zapi_nexthop *znh); +extern struct nexthop * +nexthop_from_zapi_nexthop(const struct zapi_nexthop *znh); +int zapi_nexthop_from_nexthop(struct zapi_nexthop *znh, + const struct nexthop *nh); +int zapi_backup_nexthop_from_nexthop(struct zapi_nexthop *znh, + const struct nexthop *nh); extern bool zapi_nexthop_update_decode(struct stream *s, struct zapi_route *nhr); +const char *zapi_nexthop2str(const struct zapi_nexthop *znh, char *buf, + int bufsize); + +/* Decode the zebra error message */ +extern bool zapi_error_decode(struct stream *s, enum zebra_error_types *error); + +/* Encode and decode restart capabilities */ +extern int32_t zclient_capabilities_send(uint32_t cmd, struct zclient *zclient, + struct zapi_cap *api); +extern int32_t zapi_capabilities_decode(struct stream *s, struct zapi_cap *api); static inline void zapi_route_set_blackhole(struct zapi_route *api, enum blackhole_type bh_type) @@ -642,5 +889,84 @@ static inline void zapi_route_set_blackhole(struct zapi_route *api, SET_FLAG(api->message, ZAPI_MESSAGE_NEXTHOP); }; +extern void zclient_send_mlag_register(struct zclient *client, + uint32_t bit_map); +extern void zclient_send_mlag_deregister(struct zclient *client); + +extern void zclient_send_mlag_data(struct zclient *client, + struct stream *client_s); + +/* + * Send an OPAQUE message, contents opaque to zebra - but note that + * the length of the payload is restricted by the zclient's + * outgoing message buffer. + * The message header is a message subtype; please use the registry + * below to avoid sub-type collisions. Clients use the registration + * apis to manage the specific opaque subtypes they want to receive. + */ +int zclient_send_opaque(struct zclient *zclient, uint32_t type, + const uint8_t *data, size_t datasize); + +int zclient_send_opaque_unicast(struct zclient *zclient, uint32_t type, + uint8_t proto, uint16_t instance, + uint32_t session_id, const uint8_t *data, + size_t datasize); + +/* Struct representing the decoded opaque header info */ +struct zapi_opaque_msg { + uint32_t type; /* Subtype */ + uint16_t len; /* len after zapi header and this info */ + uint16_t flags; + + /* Client-specific info - *if* UNICAST flag is set */ + uint8_t proto; + uint16_t instance; + uint32_t session_id; +}; + +#define ZAPI_OPAQUE_FLAG_UNICAST 0x01 + +/* Simple struct to convey registration/unreg requests */ +struct zapi_opaque_reg_info { + /* Message subtype */ + uint32_t type; + + /* Client session tuple */ + uint8_t proto; + uint16_t instance; + uint32_t session_id; +}; + +/* Decode incoming opaque */ +int zclient_opaque_decode(struct stream *msg, struct zapi_opaque_msg *info); + +int zclient_register_opaque(struct zclient *zclient, uint32_t type); +int zclient_unregister_opaque(struct zclient *zclient, uint32_t type); +int zapi_opaque_reg_decode(struct stream *msg, + struct zapi_opaque_reg_info *info); + +/* + * Registry of opaque message types. Please do not reuse an in-use + * type code; some daemons are likely relying on it. + */ +enum zapi_opaque_registry { + /* Request link-state database dump, at restart for example */ + LINK_STATE_REQUEST = 1, + /* Update containing link-state db info */ + LINK_STATE_UPDATE = 2, +}; + +/* Send the hello message. + * Returns 0 for success or -1 on an I/O error. + */ +extern int zclient_send_hello(struct zclient *client); + +extern int zclient_send_neigh_discovery_req(struct zclient *zclient, + const struct interface *ifp, + const struct prefix *p); + +#ifdef __cplusplus +} +#endif #endif /* _ZEBRA_ZCLIENT_H */ diff --git a/lib/zebra.h b/lib/zebra.h index b1ea43c747..b2f0202c53 100644 --- a/lib/zebra.h +++ b/lib/zebra.h @@ -54,8 +54,9 @@ typedef unsigned char uint8_t; #ifdef HAVE_SYS_SYSCTL_H #ifdef GNU_LINUX #include -#endif +#else #include +#endif #endif /* HAVE_SYS_SYSCTL_H */ #include #ifdef HAVE_SYS_CONF_H @@ -134,6 +135,11 @@ typedef unsigned char uint8_t; #endif #endif +#ifdef CRYPTO_OPENSSL +#include +#include +#endif + #include "openbsd-tree.h" #include @@ -202,18 +208,12 @@ typedef unsigned char uint8_t; /* Some systems do not define UINT32_MAX, etc.. from inttypes.h * e.g. this makes life easier for FBSD 4.11 users. */ -#ifndef INT8_MAX -#define INT8_MAX (127) -#endif #ifndef INT16_MAX #define INT16_MAX (32767) #endif #ifndef INT32_MAX #define INT32_MAX (2147483647) #endif -#ifndef UINT8_MAX -#define UINT8_MAX (255U) -#endif #ifndef UINT16_MAX #define UINT16_MAX (65535U) #endif @@ -250,13 +250,6 @@ size_t strlcpy(char *__restrict dest, const char *__restrict src, size_t destsize); #endif -/* GCC have printf type attribute check. */ -#ifdef __GNUC__ -#define PRINTF_ATTRIBUTE(a,b) __attribute__ ((__format__ (__printf__, a, b))) -#else -#define PRINTF_ATTRIBUTE(a,b) -#endif /* __GNUC__ */ - /* * RFC 3542 defines several macros for using struct cmsghdr. * Here, we define those that are not present @@ -343,12 +336,6 @@ struct in_pktinfo { /* default zebra TCP port for zclient */ #define ZEBRA_PORT 2600 -/* Marker value used in new Zserv, in the byte location corresponding - * the command value in the old zserv header. To allow old and new - * Zserv headers to be distinguished from each other. - */ -#define ZEBRA_HEADER_MARKER 254 - /* * The compiler.h header is used for anyone using the CPP_NOTICE * since this is universally needed, let's add it to zebra.h @@ -358,74 +345,26 @@ struct in_pktinfo { /* Zebra route's types are defined in route_types.h */ #include "route_types.h" -/* Note: whenever a new route-type or zserv-command is added the - * corresponding {command,route}_types[] table in lib/log.c MUST be - * updated! */ - -/* Map a route type to a string. For example, ZEBRA_ROUTE_RIPNG -> "ripng". */ -extern const char *zebra_route_string(unsigned int route_type); -/* Map a route type to a char. For example, ZEBRA_ROUTE_RIPNG -> 'R'. */ -extern char zebra_route_char(unsigned int route_type); -/* Map a zserv command type to the same string, - * e.g. ZEBRA_INTERFACE_ADD -> "ZEBRA_INTERFACE_ADD" */ -/* Map a protocol name to its number. e.g. ZEBRA_ROUTE_BGP->9*/ -extern int proto_name2num(const char *s); -/* Map redistribute X argument to protocol number. - * unlike proto_name2num, this accepts shorthands and takes - * an AFI value to restrict input */ -extern int proto_redistnum(int afi, const char *s); - -extern const char *zserv_command_string(unsigned int command); - #define strmatch(a,b) (!strcmp((a), (b))) -/* Zebra message flags */ - -/* - * Cause Zebra to consider this routes nexthops recursively - */ -#define ZEBRA_FLAG_ALLOW_RECURSION 0x01 -/* - * This is a route that is read in on startup that was left around - * from a previous run of FRR - */ -#define ZEBRA_FLAG_SELFROUTE 0x02 -/* - * This flag is used to tell Zebra that the BGP route being passed - * down is a IBGP route - */ -#define ZEBRA_FLAG_IBGP 0x04 -/* - * This is a route that has been selected for FIB installation. - * This flag is set in zebra and can be passed up to routing daemons - */ -#define ZEBRA_FLAG_SELECTED 0x08 -/* - * This is a route that we are telling Zebra that this route *must* - * win and will be installed even over ZEBRA_FLAG_SELECTED - */ -#define ZEBRA_FLAG_FIB_OVERRIDE 0x10 -/* - * This flag tells Zebra that the route is a EVPN route and should - * be treated specially - */ -#define ZEBRA_FLAG_EVPN_ROUTE 0x20 -/* - * This flag tells Zebra that it should treat the distance passed - * down as an additional discriminator for route selection of the - * route entry. This mainly is used for backup static routes. - */ -#define ZEBRA_FLAG_RR_USE_DISTANCE 0x40 - #ifndef INADDR_LOOPBACK #define INADDR_LOOPBACK 0x7f000001 /* Internet address 127.0.0.1. */ #endif /* Address family numbers from RFC1700. */ -typedef enum { AFI_IP = 1, AFI_IP6 = 2, AFI_L2VPN = 3, AFI_MAX = 4 } afi_t; +typedef enum { + AFI_UNSPEC = 0, + AFI_IP = 1, + AFI_IP6 = 2, + AFI_L2VPN = 3, + AFI_MAX = 4 +} afi_t; + +#define IS_VALID_AFI(a) ((a) > AFI_UNSPEC && (a) < AFI_MAX) /* Subsequent Address Family Identifier. */ typedef enum { + SAFI_UNSPEC = 0, SAFI_UNICAST = 1, SAFI_MULTICAST = 2, SAFI_MPLS_VPN = 3, @@ -436,35 +375,6 @@ typedef enum { SAFI_MAX = 8 } safi_t; -/* - * The above AFI and SAFI definitions are for internal use. The protocol - * definitions (IANA values) as for example used in BGP protocol packets - * are defined below and these will get mapped to/from the internal values - * in the appropriate places. - * The rationale is that the protocol (IANA) values may be sparse and are - * not optimal for use in data-structure sizing. - * Note: Only useful (i.e., supported) values are defined below. - */ -typedef enum { - IANA_AFI_RESERVED = 0, - IANA_AFI_IPV4 = 1, - IANA_AFI_IPV6 = 2, - IANA_AFI_L2VPN = 25, - IANA_AFI_IPMR = 128, - IANA_AFI_IP6MR = 129 -} iana_afi_t; - -typedef enum { - IANA_SAFI_RESERVED = 0, - IANA_SAFI_UNICAST = 1, - IANA_SAFI_MULTICAST = 2, - IANA_SAFI_LABELED_UNICAST = 4, - IANA_SAFI_ENCAP = 7, - IANA_SAFI_EVPN = 70, - IANA_SAFI_MPLS_VPN = 128, - IANA_SAFI_FLOWSPEC = 133 -} iana_safi_t; - /* Default Administrative Distance of each protocol. */ #define ZEBRA_KERNEL_DISTANCE_DEFAULT 0 #define ZEBRA_CONNECT_DISTANCE_DEFAULT 0 @@ -495,10 +405,6 @@ typedef enum { #define RESET_FLAG_ATOMIC(PV) \ ((atomic_store_explicit(PV, 0, memory_order_seq_cst))) -/* Zebra types. Used in Zserv message header. */ -typedef uint16_t zebra_size_t; -typedef uint16_t zebra_command_t; - /* VRF ID type. */ typedef uint32_t vrf_id_t; @@ -506,76 +412,4 @@ typedef uint32_t route_tag_t; #define ROUTE_TAG_MAX UINT32_MAX #define ROUTE_TAG_PRI PRIu32 -static inline afi_t afi_iana2int(iana_afi_t afi) -{ - switch (afi) { - case IANA_AFI_IPV4: - return AFI_IP; - case IANA_AFI_IPV6: - return AFI_IP6; - case IANA_AFI_L2VPN: - return AFI_L2VPN; - default: - return AFI_MAX; - } -} - -static inline iana_afi_t afi_int2iana(afi_t afi) -{ - switch (afi) { - case AFI_IP: - return IANA_AFI_IPV4; - case AFI_IP6: - return IANA_AFI_IPV6; - case AFI_L2VPN: - return IANA_AFI_L2VPN; - default: - return IANA_AFI_RESERVED; - } -} - -static inline safi_t safi_iana2int(iana_safi_t safi) -{ - switch (safi) { - case IANA_SAFI_UNICAST: - return SAFI_UNICAST; - case IANA_SAFI_MULTICAST: - return SAFI_MULTICAST; - case IANA_SAFI_MPLS_VPN: - return SAFI_MPLS_VPN; - case IANA_SAFI_ENCAP: - return SAFI_ENCAP; - case IANA_SAFI_EVPN: - return SAFI_EVPN; - case IANA_SAFI_LABELED_UNICAST: - return SAFI_LABELED_UNICAST; - case IANA_SAFI_FLOWSPEC: - return SAFI_FLOWSPEC; - default: - return SAFI_MAX; - } -} - -static inline iana_safi_t safi_int2iana(safi_t safi) -{ - switch (safi) { - case SAFI_UNICAST: - return IANA_SAFI_UNICAST; - case SAFI_MULTICAST: - return IANA_SAFI_MULTICAST; - case SAFI_MPLS_VPN: - return IANA_SAFI_MPLS_VPN; - case SAFI_ENCAP: - return IANA_SAFI_ENCAP; - case SAFI_EVPN: - return IANA_SAFI_EVPN; - case SAFI_LABELED_UNICAST: - return IANA_SAFI_LABELED_UNICAST; - case SAFI_FLOWSPEC: - return IANA_SAFI_FLOWSPEC; - default: - return IANA_SAFI_RESERVED; - } -} - #endif /* _ZEBRA_H */ diff --git a/lib/zlog.c b/lib/zlog.c new file mode 100644 index 0000000000..8dfd20371b --- /dev/null +++ b/lib/zlog.c @@ -0,0 +1,701 @@ +/* + * Copyright (c) 2015-19 David Lamparter, for NetDEF, Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "zebra.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* gettid() & co. */ +#ifdef HAVE_PTHREAD_NP_H +#include +#endif +#ifdef linux +#include +#endif +#ifdef __FreeBSD__ +#include +#endif +#ifdef __NetBSD__ +#include +#endif +#ifdef __DragonFly__ +#include +#endif +#ifdef __APPLE__ +#include +#endif + +#include "memory.h" +#include "atomlist.h" +#include "printfrr.h" +#include "frrcu.h" +#include "zlog.h" + +DEFINE_MTYPE_STATIC(LIB, LOG_MESSAGE, "log message") +DEFINE_MTYPE_STATIC(LIB, LOG_TLSBUF, "log thread-local buffer") + +DEFINE_HOOK(zlog_init, (const char *progname, const char *protoname, + unsigned short instance, uid_t uid, gid_t gid), + (progname, protoname, instance, uid, gid)) +DEFINE_KOOH(zlog_fini, (), ()) +DEFINE_HOOK(zlog_aux_init, (const char *prefix, int prio_min), + (prefix, prio_min)) + +char zlog_prefix[128]; +size_t zlog_prefixsz; +int zlog_tmpdirfd = -1; + +/* these are kept around because logging is initialized (and directories + * & files created) before zprivs code switches to the FRR user; therefore + * we need to chown() things so we don't get permission errors later when + * trying to delete things on shutdown + */ +static uid_t zlog_uid = -1; +static gid_t zlog_gid = -1; + +DECLARE_ATOMLIST(zlog_targets, struct zlog_target, head); +static struct zlog_targets_head zlog_targets; + +/* cf. zlog.h for additional comments on this struct. + * + * Note: you MUST NOT pass the format string + va_list to non-FRR format + * string functions (e.g. vsyslog, sd_journal_printv, ...) since FRR uses an + * extended prinf() with additional formats (%pI4 and the like). + * + * Also remember to use va_copy() on args. + */ + +struct zlog_msg { + struct timespec ts; + int prio; + + const char *fmt; + va_list args; + + char *stackbuf; + size_t stackbufsz; + char *text; + size_t textlen; + + /* This is always ISO8601 with sub-second precision 9 here, it's + * converted for callers as needed. ts_dot points to the "." + * separating sub-seconds. ts_zonetail is "Z" or "+00:00" for the + * local time offset. + * + * Valid if ZLOG_TS_ISO8601 is set. + * (0 if timestamp has not been formatted yet) + */ + uint32_t ts_flags; + char ts_str[32], *ts_dot, ts_zonetail[8]; +}; + +/* thread-local log message buffering + * + * This is strictly optional and set up by calling zlog_tls_buffer_init() + * on a particular thread. + * + * If in use, this will create a temporary file in /var/tmp which is used as + * memory-mapped MAP_SHARED log message buffer. The idea there is that buffer + * access doesn't require any syscalls, but in case of a crash the kernel + * knows to sync the memory back to disk. This way the user can still get the + * last log messages if there were any left unwritten in the buffer. + * + * Sizing this dynamically isn't particularly useful, so here's an 8k buffer + * with a message limit of 64 messages. Message metadata (e.g. priority, + * timestamp) aren't in the mmap region, so they're lost on crash, but we can + * live with that. + */ + +#if defined(HAVE_OPENAT) && defined(HAVE_UNLINKAT) +#define CAN_DO_TLS 1 +#endif + +#define TLS_LOG_BUF_SIZE 8192 +#define TLS_LOG_MAXMSG 64 + +struct zlog_tls { + char *mmbuf; + size_t bufpos; + + size_t nmsgs; + struct zlog_msg msgs[TLS_LOG_MAXMSG]; + struct zlog_msg *msgp[TLS_LOG_MAXMSG]; +}; + +static inline void zlog_tls_free(void *arg); + +/* proper ELF TLS is a bit faster than pthread_[gs]etspecific, so if it's + * available we'll use it here + */ + +#ifdef __OpenBSD__ +static pthread_key_t zlog_tls_key; + +static void zlog_tls_key_init(void) __attribute__((_CONSTRUCTOR(500))); +static void zlog_tls_key_init(void) +{ + pthread_key_create(&zlog_tls_key, zlog_tls_free); +} + +static void zlog_tls_key_fini(void) __attribute__((_DESTRUCTOR(500))); +static void zlog_tls_key_fini(void) +{ + pthread_key_delete(zlog_tls_key); +} + +static inline struct zlog_tls *zlog_tls_get(void) +{ + return pthread_getspecific(zlog_tls_key); +} + +static inline void zlog_tls_set(struct zlog_tls *val) +{ + pthread_setspecific(zlog_tls_key, val); +} +#else +# ifndef thread_local +# define thread_local __thread +# endif + +static thread_local struct zlog_tls *zlog_tls_var + __attribute__((tls_model("initial-exec"))); + +static inline struct zlog_tls *zlog_tls_get(void) +{ + return zlog_tls_var; +} + +static inline void zlog_tls_set(struct zlog_tls *val) +{ + zlog_tls_var = val; +} +#endif + +#ifdef CAN_DO_TLS +static long zlog_gettid(void) +{ + long rv = -1; +#ifdef HAVE_PTHREAD_GETTHREADID_NP + rv = pthread_getthreadid_np(); +#elif defined(linux) + rv = syscall(__NR_gettid); +#elif defined(__NetBSD__) + rv = _lwp_self(); +#elif defined(__FreeBSD__) + thr_self(&rv); +#elif defined(__DragonFly__) + rv = lwp_gettid(); +#elif defined(__OpenBSD__) + rv = getthrid(); +#elif defined(__sun) + rv = pthread_self(); +#elif defined(__APPLE__) + rv = mach_thread_self(); + mach_port_deallocate(mach_task_self(), rv); +#endif + return rv; +} + +void zlog_tls_buffer_init(void) +{ + struct zlog_tls *zlog_tls; + char mmpath[MAXPATHLEN]; + int mmfd; + size_t i; + + zlog_tls = zlog_tls_get(); + + if (zlog_tls || zlog_tmpdirfd < 0) + return; + + zlog_tls = XCALLOC(MTYPE_LOG_TLSBUF, sizeof(*zlog_tls)); + for (i = 0; i < array_size(zlog_tls->msgp); i++) + zlog_tls->msgp[i] = &zlog_tls->msgs[i]; + + snprintfrr(mmpath, sizeof(mmpath), "logbuf.%ld", zlog_gettid()); + + mmfd = openat(zlog_tmpdirfd, mmpath, + O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC, 0600); + if (mmfd < 0) { + zlog_err("failed to open thread log buffer \"%s\": %s", + mmpath, strerror(errno)); + goto out_anon; + } + fchown(mmfd, zlog_uid, zlog_gid); + +#ifdef HAVE_POSIX_FALLOCATE + if (posix_fallocate(mmfd, 0, TLS_LOG_BUF_SIZE) != 0) + /* note next statement is under above if() */ +#endif + if (ftruncate(mmfd, TLS_LOG_BUF_SIZE) < 0) { + zlog_err("failed to allocate thread log buffer \"%s\": %s", + mmpath, strerror(errno)); + goto out_anon_unlink; + } + + zlog_tls->mmbuf = mmap(NULL, TLS_LOG_BUF_SIZE, PROT_READ | PROT_WRITE, + MAP_SHARED, mmfd, 0); + if (zlog_tls->mmbuf == MAP_FAILED) { + zlog_err("failed to mmap thread log buffer \"%s\": %s", + mmpath, strerror(errno)); + goto out_anon_unlink; + } + + close(mmfd); + zlog_tls_set(zlog_tls); + return; + +out_anon_unlink: + unlink(mmpath); + close(mmfd); +out_anon: + +#ifndef MAP_ANONYMOUS +#define MAP_ANONYMOUS MAP_ANON +#endif + zlog_tls->mmbuf = mmap(NULL, TLS_LOG_BUF_SIZE, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + + if (!zlog_tls->mmbuf) { + zlog_err("failed to anonymous-mmap thread log buffer: %s", + strerror(errno)); + XFREE(MTYPE_LOG_TLSBUF, zlog_tls); + zlog_tls_set(NULL); + return; + } + + zlog_tls_set(zlog_tls); +} + +void zlog_tls_buffer_fini(void) +{ + char mmpath[MAXPATHLEN]; + + zlog_tls_buffer_flush(); + + zlog_tls_free(zlog_tls_get()); + zlog_tls_set(NULL); + + snprintfrr(mmpath, sizeof(mmpath), "logbuf.%ld", zlog_gettid()); + if (unlinkat(zlog_tmpdirfd, mmpath, 0)) + zlog_err("unlink logbuf: %s (%d)", strerror(errno), errno); +} + +#else /* !CAN_DO_TLS */ +void zlog_tls_buffer_init(void) +{ +} + +void zlog_tls_buffer_fini(void) +{ +} +#endif + +static inline void zlog_tls_free(void *arg) +{ + struct zlog_tls *zlog_tls = arg; + + if (!zlog_tls) + return; + + munmap(zlog_tls->mmbuf, TLS_LOG_BUF_SIZE); + XFREE(MTYPE_LOG_TLSBUF, zlog_tls); +} + +void zlog_tls_buffer_flush(void) +{ + struct zlog_target *zt; + struct zlog_tls *zlog_tls = zlog_tls_get(); + + if (!zlog_tls) + return; + if (!zlog_tls->nmsgs) + return; + + rcu_read_lock(); + frr_each (zlog_targets, &zlog_targets, zt) { + if (!zt->logfn) + continue; + + zt->logfn(zt, zlog_tls->msgp, zlog_tls->nmsgs); + } + rcu_read_unlock(); + + zlog_tls->bufpos = 0; + zlog_tls->nmsgs = 0; +} + + +static void vzlog_notls(int prio, const char *fmt, va_list ap) +{ + struct zlog_target *zt; + struct zlog_msg stackmsg = { + .prio = prio & LOG_PRIMASK, + .fmt = fmt, + }, *msg = &stackmsg; + char stackbuf[512]; + + clock_gettime(CLOCK_REALTIME, &msg->ts); + va_copy(msg->args, ap); + msg->stackbuf = stackbuf; + msg->stackbufsz = sizeof(stackbuf); + + rcu_read_lock(); + frr_each (zlog_targets, &zlog_targets, zt) { + if (prio > zt->prio_min) + continue; + if (!zt->logfn) + continue; + + zt->logfn(zt, &msg, 1); + } + rcu_read_unlock(); + + va_end(msg->args); + if (msg->text && msg->text != stackbuf) + XFREE(MTYPE_LOG_MESSAGE, msg->text); +} + +static void vzlog_tls(struct zlog_tls *zlog_tls, int prio, + const char *fmt, va_list ap) +{ + struct zlog_target *zt; + struct zlog_msg *msg; + char *buf; + bool ignoremsg = true; + bool immediate = false; + + /* avoid further processing cost if no target wants this message */ + rcu_read_lock(); + frr_each (zlog_targets, &zlog_targets, zt) { + if (prio > zt->prio_min) + continue; + ignoremsg = false; + break; + } + rcu_read_unlock(); + + if (ignoremsg) + return; + + msg = &zlog_tls->msgs[zlog_tls->nmsgs]; + zlog_tls->nmsgs++; + if (zlog_tls->nmsgs == array_size(zlog_tls->msgs)) + immediate = true; + + memset(msg, 0, sizeof(*msg)); + clock_gettime(CLOCK_REALTIME, &msg->ts); + va_copy(msg->args, ap); + msg->stackbuf = buf = zlog_tls->mmbuf + zlog_tls->bufpos; + msg->stackbufsz = TLS_LOG_BUF_SIZE - zlog_tls->bufpos - 1; + msg->fmt = fmt; + msg->prio = prio & LOG_PRIMASK; + if (msg->prio < LOG_INFO) + immediate = true; + + if (!immediate) { + /* messages written later need to take the formatting cost + * immediately since we can't hold a reference on varargs + */ + zlog_msg_text(msg, NULL); + + if (msg->text != buf) + /* zlog_msg_text called malloc() on us :( */ + immediate = true; + else { + zlog_tls->bufpos += msg->textlen + 1; + /* write a second \0 to mark current end position + * (in case of crash this signals end of unwritten log + * messages in mmap'd logbuf file) + */ + zlog_tls->mmbuf[zlog_tls->bufpos] = '\0'; + + /* avoid malloc() for next message */ + if (TLS_LOG_BUF_SIZE - zlog_tls->bufpos < 256) + immediate = true; + } + } + + if (immediate) + zlog_tls_buffer_flush(); + + va_end(msg->args); + if (msg->text && msg->text != buf) + XFREE(MTYPE_LOG_MESSAGE, msg->text); +} + +void vzlog(int prio, const char *fmt, va_list ap) +{ + struct zlog_tls *zlog_tls = zlog_tls_get(); + + if (zlog_tls) + vzlog_tls(zlog_tls, prio, fmt, ap); + else + vzlog_notls(prio, fmt, ap); +} + +void zlog_sigsafe(const char *text, size_t len) +{ + struct zlog_target *zt; + const char *end = text + len, *nlpos; + + while (text < end) { + nlpos = memchr(text, '\n', end - text); + if (!nlpos) + nlpos = end; + + frr_each (zlog_targets, &zlog_targets, zt) { + if (LOG_CRIT > zt->prio_min) + continue; + if (!zt->logfn_sigsafe) + continue; + + zt->logfn_sigsafe(zt, text, nlpos - text); + } + + if (nlpos == end) + break; + text = nlpos + 1; + } +} + + +int zlog_msg_prio(struct zlog_msg *msg) +{ + return msg->prio; +} + +const char *zlog_msg_text(struct zlog_msg *msg, size_t *textlen) +{ + if (!msg->text) { + va_list args; + + va_copy(args, msg->args); + msg->text = vasnprintfrr(MTYPE_LOG_MESSAGE, msg->stackbuf, + msg->stackbufsz, msg->fmt, args); + msg->textlen = strlen(msg->text); + va_end(args); + } + if (textlen) + *textlen = msg->textlen; + return msg->text; +} + +#define ZLOG_TS_FORMAT (ZLOG_TS_ISO8601 | ZLOG_TS_LEGACY) +#define ZLOG_TS_FLAGS ~ZLOG_TS_PREC + +size_t zlog_msg_ts(struct zlog_msg *msg, char *out, size_t outsz, + uint32_t flags) +{ + size_t len1; + + if (!(flags & ZLOG_TS_FORMAT)) + return 0; + + if (!(msg->ts_flags & ZLOG_TS_FORMAT) || + ((msg->ts_flags ^ flags) & ZLOG_TS_UTC)) { + struct tm tm; + + if (flags & ZLOG_TS_UTC) + gmtime_r(&msg->ts.tv_sec, &tm); + else + localtime_r(&msg->ts.tv_sec, &tm); + + strftime(msg->ts_str, sizeof(msg->ts_str), + "%Y-%m-%dT%H:%M:%S", &tm); + + if (flags & ZLOG_TS_UTC) { + msg->ts_zonetail[0] = 'Z'; + msg->ts_zonetail[1] = '\0'; + } else + snprintfrr(msg->ts_zonetail, sizeof(msg->ts_zonetail), + "%+03d:%02d", + (int)(tm.tm_gmtoff / 3600), + (int)(labs(tm.tm_gmtoff) / 60) % 60); + + msg->ts_dot = msg->ts_str + strlen(msg->ts_str); + snprintfrr(msg->ts_dot, + msg->ts_str + sizeof(msg->ts_str) - msg->ts_dot, + ".%09lu", (unsigned long)msg->ts.tv_nsec); + + msg->ts_flags = ZLOG_TS_ISO8601 | (flags & ZLOG_TS_UTC); + } + + len1 = flags & ZLOG_TS_PREC; + len1 = (msg->ts_dot - msg->ts_str) + (len1 ? len1 + 1 : 0); + + if (len1 > strlen(msg->ts_str)) + len1 = strlen(msg->ts_str); + + if (flags & ZLOG_TS_LEGACY) { + if (len1 + 1 > outsz) + return 0; + + /* just swap out the formatting, faster than redoing it */ + for (char *p = msg->ts_str; p < msg->ts_str + len1; p++) { + switch (*p) { + case '-': + *out++ = '/'; + break; + case 'T': + *out++ = ' '; + break; + default: + *out++ = *p; + } + } + *out = '\0'; + return len1; + } else { + size_t len2 = strlen(msg->ts_zonetail); + + if (len1 + len2 + 1 > outsz) + return 0; + memcpy(out, msg->ts_str, len1); + memcpy(out + len1, msg->ts_zonetail, len2); + out[len1 + len2] = '\0'; + return len1 + len2; + } +} + +/* setup functions */ + +struct zlog_target *zlog_target_clone(struct memtype *mt, + struct zlog_target *oldzt, size_t size) +{ + struct zlog_target *newzt; + + newzt = XCALLOC(mt, size); + if (oldzt) { + newzt->prio_min = oldzt->prio_min; + newzt->logfn = oldzt->logfn; + newzt->logfn_sigsafe = oldzt->logfn_sigsafe; + } + + return newzt; +} + +struct zlog_target *zlog_target_replace(struct zlog_target *oldzt, + struct zlog_target *newzt) +{ + if (newzt) + zlog_targets_add_tail(&zlog_targets, newzt); + if (oldzt) + zlog_targets_del(&zlog_targets, oldzt); + return oldzt; +} + + +/* common init */ + +#define TMPBASEDIR "/var/tmp/frr" + +static char zlog_tmpdir[MAXPATHLEN]; + +void zlog_aux_init(const char *prefix, int prio_min) +{ + if (prefix) + strlcpy(zlog_prefix, prefix, sizeof(zlog_prefix)); + + hook_call(zlog_aux_init, prefix, prio_min); +} + +void zlog_init(const char *progname, const char *protoname, + unsigned short instance, uid_t uid, gid_t gid) +{ + zlog_uid = uid; + zlog_gid = gid; + + if (instance) { + snprintfrr(zlog_tmpdir, sizeof(zlog_tmpdir), + "/var/tmp/frr/%s-%d.%ld", + progname, instance, (long)getpid()); + + zlog_prefixsz = snprintfrr(zlog_prefix, sizeof(zlog_prefix), + "%s[%d]: ", protoname, instance); + } else { + snprintfrr(zlog_tmpdir, sizeof(zlog_tmpdir), + "/var/tmp/frr/%s.%ld", + progname, (long)getpid()); + + zlog_prefixsz = snprintfrr(zlog_prefix, sizeof(zlog_prefix), + "%s: ", protoname); + } + + if (mkdir(TMPBASEDIR, 0700) != 0) { + if (errno != EEXIST) { + zlog_err("failed to mkdir \"%s\": %s", + TMPBASEDIR, strerror(errno)); + goto out_warn; + } + } + chown(TMPBASEDIR, zlog_uid, zlog_gid); + + if (mkdir(zlog_tmpdir, 0700) != 0) { + zlog_err("failed to mkdir \"%s\": %s", + zlog_tmpdir, strerror(errno)); + goto out_warn; + } + +#ifdef O_PATH + zlog_tmpdirfd = open(zlog_tmpdir, + O_PATH | O_RDONLY | O_CLOEXEC); +#else + zlog_tmpdirfd = open(zlog_tmpdir, + O_DIRECTORY | O_RDONLY | O_CLOEXEC); +#endif + if (zlog_tmpdirfd < 0) { + zlog_err("failed to open \"%s\": %s", + zlog_tmpdir, strerror(errno)); + goto out_warn; + } + +#ifdef AT_EMPTY_PATH + fchownat(zlog_tmpdirfd, "", zlog_uid, zlog_gid, AT_EMPTY_PATH); +#else + chown(zlog_tmpdir, zlog_uid, zlog_gid); +#endif + + hook_call(zlog_init, progname, protoname, instance, uid, gid); + return; + +out_warn: + zlog_err("crashlog and per-thread log buffering unavailable!"); + hook_call(zlog_init, progname, protoname, instance, uid, gid); +} + +void zlog_fini(void) +{ + hook_call(zlog_fini); + + if (zlog_tmpdirfd >= 0) { + close(zlog_tmpdirfd); + zlog_tmpdirfd = -1; + + if (rmdir(zlog_tmpdir)) + zlog_err("failed to rmdir \"%s\": %s", + zlog_tmpdir, strerror(errno)); + } +} diff --git a/lib/zlog.h b/lib/zlog.h new file mode 100644 index 0000000000..1c5013746b --- /dev/null +++ b/lib/zlog.h @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2015-19 David Lamparter, for NetDEF, Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _FRR_ZLOG_H +#define _FRR_ZLOG_H + +#include +#include +#include +#include +#include +#include +#include + +#include "atomlist.h" +#include "frrcu.h" +#include "memory.h" +#include "hook.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern char zlog_prefix[]; +extern size_t zlog_prefixsz; +extern int zlog_tmpdirfd; + +/* These functions are set up to write to stdout/stderr without explicit + * initialization and/or before config load. There is no need to call e.g. + * fprintf(stderr, ...) just because it's "too early" at startup. Depending + * on context, it may still be the right thing to use fprintf though -- try to + * determine wether something is a log message or something else. + */ + +extern void vzlog(int prio, const char *fmt, va_list ap); + +PRINTFRR(2, 3) +static inline void zlog(int prio, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vzlog(prio, fmt, ap); + va_end(ap); +} + +#define zlog_err(...) zlog(LOG_ERR, __VA_ARGS__) +#define zlog_warn(...) zlog(LOG_WARNING, __VA_ARGS__) +#define zlog_info(...) zlog(LOG_INFO, __VA_ARGS__) +#define zlog_notice(...) zlog(LOG_NOTICE, __VA_ARGS__) +#define zlog_debug(...) zlog(LOG_DEBUG, __VA_ARGS__) + +extern void zlog_sigsafe(const char *text, size_t len); + +/* extra priority value to disable a target without deleting it */ +#define ZLOG_DISABLED (LOG_EMERG-1) + +/* zlog_msg encapsulates a particular logging call from somewhere in the code. + * The same struct is passed around to all zlog_targets. + * + * This is used to defer formatting the log message until it is actually + * requested by one of the targets. If none of the targets needs the message + * formatted, the formatting call is avoided entirely. + * + * This struct is opaque / private to the core zlog code. Logging targets + * should use zlog_msg_* functions to get text / timestamps / ... for a + * message. + */ + +struct zlog_msg; + +extern int zlog_msg_prio(struct zlog_msg *msg); + +/* pass NULL as textlen if you don't need it. */ +extern const char *zlog_msg_text(struct zlog_msg *msg, size_t *textlen); + +/* timestamp formatting control flags */ + +/* sub-second digit count */ +#define ZLOG_TS_PREC 0xfU + +/* 8601: 0000-00-00T00:00:00Z (if used with ZLOG_TS_UTC) + * 0000-00-00T00:00:00+00:00 (otherwise) + * Legacy: 0000/00/00 00:00:00 (no TZ indicated!) + */ +#define ZLOG_TS_ISO8601 (1 << 8) +#define ZLOG_TS_LEGACY (1 << 9) + +/* default is local time zone */ +#define ZLOG_TS_UTC (1 << 10) + +extern size_t zlog_msg_ts(struct zlog_msg *msg, char *out, size_t outsz, + uint32_t flags); + +/* This list & struct implements the actual logging targets. It is accessed + * lock-free from all threads, and thus MUST only be changed atomically, i.e. + * RCU. + * + * Since there's no atomic replace, the replacement action is an add followed + * by a delete. This means that during logging config changes, log messages + * may be duplicated in the log target that is being changed. The old entry + * being changed MUST also at the very least not crash or do other stupid + * things. + * + * This list and struct are NOT related to config. Logging config is kept + * separately, and results in creating appropriate zlog_target(s) to realize + * the config. Log targets may also be created from varying sources, e.g. + * command line options, or VTY commands ("log monitor"). + * + * struct zlog_target is intended to be embedded into a larger structure that + * contains additional field for the specific logging target, e.g. an fd or + * additional options. It MUST be the first field in that larger struct. + */ + +PREDECL_ATOMLIST(zlog_targets) +struct zlog_target { + struct zlog_targets_item head; + + int prio_min; + + void (*logfn)(struct zlog_target *zt, struct zlog_msg *msg[], + size_t nmsgs); + + /* for crash handlers, set to NULL if log target can't write crash logs + * without possibly deadlocking (AS-Safe) + * + * text is not \0 terminated & split up into lines (e.g. no \n) + */ + void (*logfn_sigsafe)(struct zlog_target *zt, const char *text, + size_t len); + + struct rcu_head rcu_head; +}; + +/* make a copy for RCUpdating. oldzt may be NULL to allocate a fresh one. */ +extern struct zlog_target *zlog_target_clone(struct memtype *mt, + struct zlog_target *oldzt, + size_t size); + +/* update the zlog_targets list; both oldzt and newzt may be NULL. You + * still need to zlog_target_free() the old target afterwards if it wasn't + * NULL. + * + * Returns oldzt so you can zlog_target_free(zlog_target_replace(old, new)); + * (Some log targets may need extra cleanup inbetween, but remember the old + * target MUST remain functional until the end of the current RCU cycle.) + */ +extern struct zlog_target *zlog_target_replace(struct zlog_target *oldzt, + struct zlog_target *newzt); + +/* Mostly for symmetry for zlog_target_clone(), just rcu_free() internally. */ +#define zlog_target_free(mt, zt) \ + rcu_free(mt, zt, rcu_head) + +extern void zlog_init(const char *progname, const char *protoname, + unsigned short instance, uid_t uid, gid_t gid); +DECLARE_HOOK(zlog_init, (const char *progname, const char *protoname, + unsigned short instance, uid_t uid, gid_t gid), + (progname, protoname, instance, uid, gid)) + +extern void zlog_fini(void); +DECLARE_KOOH(zlog_fini, (), ()) + +/* for tools & test programs, i.e. anything not a daemon. + * (no cleanup needed at exit) + */ +extern void zlog_aux_init(const char *prefix, int prio_min); +DECLARE_HOOK(zlog_aux_init, (const char *prefix, int prio_min), + (prefix, prio_min)) + +extern void zlog_startup_end(void); + +extern void zlog_tls_buffer_init(void); +extern void zlog_tls_buffer_flush(void); +extern void zlog_tls_buffer_fini(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _FRR_ZLOG_H */ diff --git a/lib/zlog_targets.c b/lib/zlog_targets.c new file mode 100644 index 0000000000..5e12c309c4 --- /dev/null +++ b/lib/zlog_targets.c @@ -0,0 +1,583 @@ +/* + * Copyright (c) 2015-19 David Lamparter, for NetDEF, Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "zebra.h" + +#include +#include + +#include "memory.h" +#include "frrcu.h" +#include "frr_pthread.h" +#include "printfrr.h" +#include "zlog.h" +#include "zlog_targets.h" + +/* these allocations are intentionally left active even when doing full exit + * cleanup, in order to keep the logging subsystem fully functional until the + * absolute end. + */ + +DECLARE_MGROUP(LOG) +DEFINE_MGROUP_ACTIVEATEXIT(LOG, "logging subsystem") + +DEFINE_MTYPE_STATIC(LOG, LOG_FD, "log file target") +DEFINE_MTYPE_STATIC(LOG, LOG_FD_NAME, "log file name") +DEFINE_MTYPE_STATIC(LOG, LOG_FD_ROTATE, "log file rotate helper") +DEFINE_MTYPE_STATIC(LOG, LOG_SYSL, "syslog target") + +struct zlt_fd { + struct zlog_target zt; + + atomic_uint_fast32_t fd; + + char ts_subsec; + bool record_priority; + + struct rcu_head_close head_close; +}; + +static const char * const prionames[] = { + [LOG_EMERG] = "emergencies: ", + [LOG_ALERT] = "alerts: ", + [LOG_CRIT] = "critical: ", + [LOG_ERR] = "errors: ", + [LOG_WARNING] = "warnings: ", + [LOG_NOTICE] = "notifications: ", + [LOG_INFO] = "informational: ", + [LOG_DEBUG] = "debugging: ", +}; + +void zlog_fd(struct zlog_target *zt, struct zlog_msg *msgs[], size_t nmsgs) +{ + struct zlt_fd *zte = container_of(zt, struct zlt_fd, zt); + int fd; + size_t i, textlen, iovpos = 0; + size_t niov = MIN(4 * nmsgs + 1, IOV_MAX); + struct iovec iov[niov]; + /* "\nYYYY-MM-DD HH:MM:SS.NNNNNNNNN+ZZ:ZZ " = 37 chars */ +#define TS_LEN 40 + char ts_buf[TS_LEN * nmsgs], *ts_pos = ts_buf; + + fd = atomic_load_explicit(&zte->fd, memory_order_relaxed); + + for (i = 0; i < nmsgs; i++) { + struct zlog_msg *msg = msgs[i]; + int prio = zlog_msg_prio(msg); + + if (prio <= zt->prio_min) { + iov[iovpos].iov_base = ts_pos; + if (iovpos > 0) + *ts_pos++ = '\n'; + ts_pos += zlog_msg_ts(msg, ts_pos, + sizeof(ts_buf) - 1 + - (ts_pos - ts_buf), + ZLOG_TS_LEGACY | zte->ts_subsec); + *ts_pos++ = ' '; + iov[iovpos].iov_len = + ts_pos - (char *)iov[iovpos].iov_base; + + iovpos++; + + if (zte->record_priority) { + iov[iovpos].iov_base = (char *)prionames[prio]; + iov[iovpos].iov_len = + strlen(iov[iovpos].iov_base); + + iovpos++; + } + + iov[iovpos].iov_base = zlog_prefix; + iov[iovpos].iov_len = zlog_prefixsz; + + iovpos++; + + iov[iovpos].iov_base = + (char *)zlog_msg_text(msg, &textlen); + iov[iovpos].iov_len = textlen; + + iovpos++; + } + + /* conditions that trigger writing: + * - out of space for more timestamps/headers + * - this being the last message in the batch + * - not enough remaining iov entries + */ + if (iovpos > 0 && (ts_buf + sizeof(ts_buf) - ts_pos < TS_LEN + || i + 1 == nmsgs + || array_size(iov) - iovpos < 5)) { + iov[iovpos].iov_base = (char *)"\n"; + iov[iovpos].iov_len = 1; + + iovpos++; + + writev(fd, iov, iovpos); + + iovpos = 0; + ts_pos = ts_buf; + } + } + + assert(iovpos == 0); +} + +static void zlog_fd_sigsafe(struct zlog_target *zt, const char *text, + size_t len) +{ + struct zlt_fd *zte = container_of(zt, struct zlt_fd, zt); + struct iovec iov[4]; + int fd; + + iov[0].iov_base = (char *)prionames[LOG_CRIT]; + iov[0].iov_len = zte->record_priority ? strlen(iov[0].iov_base) : 0; + + iov[1].iov_base = zlog_prefix; + iov[1].iov_len = zlog_prefixsz; + + iov[2].iov_base = (char *)text; + iov[2].iov_len = len; + + iov[3].iov_base = (char *)"\n"; + iov[3].iov_len = 1; + + fd = atomic_load_explicit(&zte->fd, memory_order_relaxed); + + writev(fd, iov, array_size(iov)); +} + +/* + * (re-)configuration + */ + +void zlog_file_init(struct zlog_cfg_file *zcf) +{ + memset(zcf, 0, sizeof(*zcf)); + zcf->prio_min = ZLOG_DISABLED; + zcf->fd = -1; + pthread_mutex_init(&zcf->cfg_mtx, NULL); +} + +static void zlog_file_target_free(struct zlt_fd *zlt) +{ + if (!zlt) + return; + + rcu_close(&zlt->head_close, zlt->fd); + rcu_free(MTYPE_LOG_FD, zlt, zt.rcu_head); +} + +void zlog_file_fini(struct zlog_cfg_file *zcf) +{ + if (zcf->active) { + struct zlt_fd *ztf; + struct zlog_target *zt; + + zt = zlog_target_replace(&zcf->active->zt, NULL); + ztf = container_of(zt, struct zlt_fd, zt); + zlog_file_target_free(ztf); + } + XFREE(MTYPE_LOG_FD_NAME, zcf->filename); + pthread_mutex_destroy(&zcf->cfg_mtx); +} + +static bool zlog_file_cycle(struct zlog_cfg_file *zcf) +{ + struct zlog_target *zt, *old; + struct zlt_fd *zlt = NULL; + int fd; + bool rv = true; + + do { + if (zcf->prio_min == ZLOG_DISABLED) + break; + + if (zcf->fd != -1) + fd = dup(zcf->fd); + else if (zcf->filename) + fd = open(zcf->filename, + O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC + | O_NOCTTY, + LOGFILE_MASK); + else + fd = -1; + + if (fd < 0) { + rv = false; + break; + } + + zt = zlog_target_clone(MTYPE_LOG_FD, &zcf->active->zt, + sizeof(*zlt)); + zlt = container_of(zt, struct zlt_fd, zt); + + zlt->fd = fd; + zlt->record_priority = zcf->record_priority; + zlt->ts_subsec = zcf->ts_subsec; + + zlt->zt.prio_min = zcf->prio_min; + zlt->zt.logfn = zcf->zlog_wrap ? zcf->zlog_wrap : zlog_fd; + zlt->zt.logfn_sigsafe = zlog_fd_sigsafe; + } while (0); + + old = zlog_target_replace(zcf->active ? &zcf->active->zt : NULL, + zlt ? &zlt->zt : NULL); + zcf->active = zlt; + + zlog_file_target_free(container_of_null(old, struct zlt_fd, zt)); + + return rv; +} + +void zlog_file_set_other(struct zlog_cfg_file *zcf) +{ + frr_with_mutex(&zcf->cfg_mtx) { + zlog_file_cycle(zcf); + } +} + +bool zlog_file_set_filename(struct zlog_cfg_file *zcf, const char *filename) +{ + frr_with_mutex(&zcf->cfg_mtx) { + XFREE(MTYPE_LOG_FD_NAME, zcf->filename); + zcf->filename = XSTRDUP(MTYPE_LOG_FD_NAME, filename); + zcf->fd = -1; + + return zlog_file_cycle(zcf); + } + assert(0); +} + +bool zlog_file_set_fd(struct zlog_cfg_file *zcf, int fd) +{ + frr_with_mutex(&zcf->cfg_mtx) { + if (zcf->fd == fd) + return true; + + XFREE(MTYPE_LOG_FD_NAME, zcf->filename); + zcf->fd = fd; + + return zlog_file_cycle(zcf); + } + assert(0); +} + +struct rcu_close_rotate { + struct rcu_head_close head_close; + struct rcu_head head_self; +}; + +bool zlog_file_rotate(struct zlog_cfg_file *zcf) +{ + struct rcu_close_rotate *rcr; + int fd; + + frr_with_mutex(&zcf->cfg_mtx) { + if (!zcf->active || !zcf->filename) + return true; + + fd = open(zcf->filename, + O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC | O_NOCTTY, + LOGFILE_MASK); + if (fd < 0) + return false; + + fd = atomic_exchange_explicit(&zcf->active->fd, + (uint_fast32_t)fd, + memory_order_relaxed); + } + + rcr = XCALLOC(MTYPE_LOG_FD_ROTATE, sizeof(*rcr)); + rcu_close(&rcr->head_close, fd); + rcu_free(MTYPE_LOG_FD_ROTATE, rcr, head_self); + + return true; +} + +/* fixed crash logging */ + +static struct zlt_fd zlog_crashlog; + +static void zlog_crashlog_sigsafe(struct zlog_target *zt, const char *text, + size_t len) +{ + static int crashlog_fd = -1; + + if (crashlog_fd == -1) { +#ifdef HAVE_OPENAT + crashlog_fd = openat(zlog_tmpdirfd, "crashlog", + O_WRONLY | O_APPEND | O_CREAT, + LOGFILE_MASK); +#endif + if (crashlog_fd < 0) + crashlog_fd = -2; + } + + if (crashlog_fd == -2) + return; + + zlog_crashlog.fd = crashlog_fd; + zlog_fd_sigsafe(&zlog_crashlog.zt, text, len); +} + +/* this is used for assert failures (they don't need AS-Safe logging) */ +static void zlog_crashlog_plain(struct zlog_target *zt, struct zlog_msg *msgs[], + size_t nmsgs) +{ + size_t i, len; + const char *text; + + for (i = 0; i < nmsgs; i++) { + if (zlog_msg_prio(msgs[i]) > zt->prio_min) + continue; + + text = zlog_msg_text(msgs[i], &len); + zlog_crashlog_sigsafe(zt, text, len); + } +} + +static void zlog_crashlog_init(void) +{ + zlog_crashlog.zt.prio_min = LOG_CRIT; + zlog_crashlog.zt.logfn = zlog_crashlog_plain; + zlog_crashlog.zt.logfn_sigsafe = zlog_crashlog_sigsafe; + zlog_crashlog.fd = -1; + + zlog_target_replace(NULL, &zlog_crashlog.zt); +} + +/* fixed logging for test/auxiliary programs */ + +static struct zlt_fd zlog_aux_stdout; +static bool zlog_is_aux; + +static int zlt_aux_init(const char *prefix, int prio_min) +{ + zlog_is_aux = true; + + zlog_aux_stdout.zt.prio_min = prio_min; + zlog_aux_stdout.zt.logfn = zlog_fd; + zlog_aux_stdout.zt.logfn_sigsafe = zlog_fd_sigsafe; + zlog_aux_stdout.fd = STDOUT_FILENO; + + zlog_target_replace(NULL, &zlog_aux_stdout.zt); + zlog_startup_end(); + return 0; +} + +static int zlt_init(const char *progname, const char *protoname, + unsigned short instance, uid_t uid, gid_t gid) +{ + openlog(progname, LOG_CONS | LOG_NDELAY | LOG_PID, LOG_DAEMON); + return 0; +} + +static int zlt_fini(void) +{ + closelog(); + return 0; +} + +/* fixed startup logging to stderr */ + +static struct zlt_fd zlog_startup_stderr; + +__attribute__((_CONSTRUCTOR(450))) static void zlog_startup_init(void) +{ + zlog_startup_stderr.zt.prio_min = LOG_WARNING; + zlog_startup_stderr.zt.logfn = zlog_fd; + zlog_startup_stderr.zt.logfn_sigsafe = zlog_fd_sigsafe; + zlog_startup_stderr.fd = STDERR_FILENO; + + zlog_target_replace(NULL, &zlog_startup_stderr.zt); + + hook_register(zlog_aux_init, zlt_aux_init); + hook_register(zlog_init, zlt_init); + hook_register(zlog_fini, zlt_fini); +} + +void zlog_startup_end(void) +{ + static bool startup_ended = false; + + if (startup_ended) + return; + startup_ended = true; + + zlog_target_replace(&zlog_startup_stderr.zt, NULL); + + if (zlog_is_aux) + return; + + /* until here, crashlogs go to stderr */ + zlog_crashlog_init(); +} + +/* syslog */ + +struct zlt_syslog { + struct zlog_target zt; + + int syslog_facility; +}; + +static void zlog_syslog(struct zlog_target *zt, struct zlog_msg *msgs[], + size_t nmsgs) +{ + size_t i; + struct zlt_syslog *zte = container_of(zt, struct zlt_syslog, zt); + + for (i = 0; i < nmsgs; i++) { + if (zlog_msg_prio(msgs[i]) > zt->prio_min) + continue; + + syslog(zlog_msg_prio(msgs[i]) | zte->syslog_facility, "%s", + zlog_msg_text(msgs[i], NULL)); + } +} + +#ifndef _PATH_LOG +#define _PATH_LOG "/dev/log" +#endif + +static void zlog_syslog_sigsafe(struct zlog_target *zt, const char *text, + size_t len) +{ + static int syslog_fd = -1; + + char hdr[192]; + size_t hdrlen; + struct iovec iov[2]; + + if (syslog_fd == -1) { + syslog_fd = socket(AF_UNIX, SOCK_DGRAM, 0); + if (syslog_fd >= 0) { + struct sockaddr_un sa; + socklen_t salen = sizeof(sa); + + sa.sun_family = AF_UNIX; + strlcpy(sa.sun_path, _PATH_LOG, sizeof(sa.sun_path)); +#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN + salen = sa.sun_len = SUN_LEN(&sa); +#endif + if (connect(syslog_fd, (struct sockaddr *)&sa, salen)) { + close(syslog_fd); + syslog_fd = -1; + } + } + + /* /dev/log could be a fifo instead of a socket */ + if (syslog_fd == -1) { + syslog_fd = open(_PATH_LOG, O_WRONLY | O_NOCTTY); + if (syslog_fd < 0) + /* give up ... */ + syslog_fd = -2; + } + } + + if (syslog_fd == -2) + return; + + /* note zlog_prefix includes trailing ": ", need to cut off 2 chars */ + hdrlen = snprintfrr(hdr, sizeof(hdr), "<%d>%.*s[%ld]: ", LOG_CRIT, + zlog_prefixsz > 2 ? (int)(zlog_prefixsz - 2) : 0, + zlog_prefix, (long)getpid()); + + iov[0].iov_base = hdr; + iov[0].iov_len = hdrlen; + + iov[1].iov_base = (char *)text; + iov[1].iov_len = len; + + writev(syslog_fd, iov, array_size(iov)); +} + + +static pthread_mutex_t syslog_cfg_mutex = PTHREAD_MUTEX_INITIALIZER; +static struct zlt_syslog *zlt_syslog; +static int syslog_facility = LOG_DAEMON; +static int syslog_prio_min = ZLOG_DISABLED; + +void zlog_syslog_set_facility(int facility) +{ + struct zlog_target *newztc; + struct zlt_syslog *newzt; + + frr_with_mutex(&syslog_cfg_mutex) { + if (facility == syslog_facility) + return; + syslog_facility = facility; + + if (syslog_prio_min == ZLOG_DISABLED) + return; + + newztc = zlog_target_clone(MTYPE_LOG_SYSL, &zlt_syslog->zt, + sizeof(*newzt)); + newzt = container_of(newztc, struct zlt_syslog, zt); + newzt->syslog_facility = syslog_facility; + + zlog_target_free(MTYPE_LOG_SYSL, + zlog_target_replace(&zlt_syslog->zt, + &newzt->zt)); + + zlt_syslog = newzt; + } +} + +int zlog_syslog_get_facility(void) +{ + frr_with_mutex(&syslog_cfg_mutex) { + return syslog_facility; + } + assert(0); +} + +void zlog_syslog_set_prio_min(int prio_min) +{ + struct zlog_target *newztc; + struct zlt_syslog *newzt = NULL; + + frr_with_mutex(&syslog_cfg_mutex) { + if (prio_min == syslog_prio_min) + return; + syslog_prio_min = prio_min; + + if (syslog_prio_min != ZLOG_DISABLED) { + newztc = zlog_target_clone(MTYPE_LOG_SYSL, + &zlt_syslog->zt, + sizeof(*newzt)); + newzt = container_of(newztc, struct zlt_syslog, zt); + newzt->zt.prio_min = prio_min; + newzt->zt.logfn = zlog_syslog; + newzt->zt.logfn_sigsafe = zlog_syslog_sigsafe; + newzt->syslog_facility = syslog_facility; + } + + zlog_target_free(MTYPE_LOG_SYSL, + zlog_target_replace(&zlt_syslog->zt, + &newzt->zt)); + + zlt_syslog = newzt; + } +} + +int zlog_syslog_get_prio_min(void) +{ + frr_with_mutex(&syslog_cfg_mutex) { + return syslog_prio_min; + } + assert(0); +} diff --git a/lib/zlog_targets.h b/lib/zlog_targets.h new file mode 100644 index 0000000000..6faf722ffd --- /dev/null +++ b/lib/zlog_targets.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2015-19 David Lamparter, for NetDEF, Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _FRR_ZLOG_TARGETS_H +#define _FRR_ZLOG_TARGETS_H + +#include + +#include "zlog.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* multiple file log targets can be active */ + +struct zlt_fd; + +struct zlog_cfg_file { + struct zlt_fd *active; + + pthread_mutex_t cfg_mtx; + + /* call zlog_file_set_other() to apply these */ + int prio_min; + char ts_subsec; + bool record_priority; + + /* call zlog_file_set_filename/fd() to change this */ + char *filename; + int fd; + + void (*zlog_wrap)(struct zlog_target *zt, struct zlog_msg *msgs[], + size_t nmsgs); +}; + +extern void zlog_file_init(struct zlog_cfg_file *zcf); +extern void zlog_file_fini(struct zlog_cfg_file *zcf); + +extern void zlog_file_set_other(struct zlog_cfg_file *zcf); +extern bool zlog_file_set_filename(struct zlog_cfg_file *zcf, const char *name); +extern bool zlog_file_set_fd(struct zlog_cfg_file *zcf, int fd); +extern bool zlog_file_rotate(struct zlog_cfg_file *zcf); + +extern void zlog_fd(struct zlog_target *zt, struct zlog_msg *msgs[], + size_t nmsgs); + +/* syslog is always limited to one target */ + +extern void zlog_syslog_set_facility(int facility); +extern int zlog_syslog_get_facility(void); + +/* use ZLOG_DISABLED to disable */ +extern void zlog_syslog_set_prio_min(int prio_min); +extern int zlog_syslog_get_prio_min(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _FRR_ZLOG_TARGETS_H */ diff --git a/m4/.gitignore b/m4/.gitignore index 2c04163659..cc778b9e99 100644 --- a/m4/.gitignore +++ b/m4/.gitignore @@ -4,6 +4,7 @@ !ax_compare_version.m4 !ax_prog_perl_modules.m4 !ax_pthread.m4 +!ax_python.m4 !ax_sys_weak_alias.m4 !ax_sys_weak_alias.m4 !pkg.m4 diff --git a/m4/ax_lua.m4 b/m4/ax_lua.m4 new file mode 100644 index 0000000000..9feb352255 --- /dev/null +++ b/m4/ax_lua.m4 @@ -0,0 +1,664 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_lua.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_PROG_LUA[([MINIMUM-VERSION], [TOO-BIG-VERSION], [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])] +# AX_LUA_HEADERS[([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])] +# AX_LUA_LIBS[([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])] +# AX_LUA_READLINE[([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])] +# +# DESCRIPTION +# +# Detect a Lua interpreter, optionally specifying a minimum and maximum +# version number. Set up important Lua paths, such as the directories in +# which to install scripts and modules (shared libraries). +# +# Also detect Lua headers and libraries. The Lua version contained in the +# header is checked to match the Lua interpreter version exactly. When +# searching for Lua libraries, the version number is used as a suffix. +# This is done with the goal of supporting multiple Lua installs (5.1, +# 5.2, and 5.3 side-by-side). +# +# A note on compatibility with previous versions: This file has been +# mostly rewritten for serial 18. Most developers should be able to use +# these macros without needing to modify configure.ac. Care has been taken +# to preserve each macro's behavior, but there are some differences: +# +# 1) AX_WITH_LUA is deprecated; it now expands to the exact same thing as +# AX_PROG_LUA with no arguments. +# +# 2) AX_LUA_HEADERS now checks that the version number defined in lua.h +# matches the interpreter version. AX_LUA_HEADERS_VERSION is therefore +# unnecessary, so it is deprecated and does not expand to anything. +# +# 3) The configure flag --with-lua-suffix no longer exists; the user +# should instead specify the LUA precious variable on the command line. +# See the AX_PROG_LUA description for details. +# +# Please read the macro descriptions below for more information. +# +# This file was inspired by Andrew Dalke's and James Henstridge's +# python.m4 and Tom Payne's, Matthieu Moy's, and Reuben Thomas's ax_lua.m4 +# (serial 17). Basically, this file is a mash-up of those two files. I +# like to think it combines the best of the two! +# +# AX_PROG_LUA: Search for the Lua interpreter, and set up important Lua +# paths. Adds precious variable LUA, which may contain the path of the Lua +# interpreter. If LUA is blank, the user's path is searched for an +# suitable interpreter. +# +# If MINIMUM-VERSION is supplied, then only Lua interpreters with a +# version number greater or equal to MINIMUM-VERSION will be accepted. If +# TOO-BIG-VERSION is also supplied, then only Lua interpreters with a +# version number greater or equal to MINIMUM-VERSION and less than +# TOO-BIG-VERSION will be accepted. +# +# The Lua version number, LUA_VERSION, is found from the interpreter, and +# substituted. LUA_PLATFORM is also found, but not currently supported (no +# standard representation). +# +# Finally, the macro finds four paths: +# +# luadir Directory to install Lua scripts. +# pkgluadir $luadir/$PACKAGE +# luaexecdir Directory to install Lua modules. +# pkgluaexecdir $luaexecdir/$PACKAGE +# +# These paths are found based on $prefix, $exec_prefix, Lua's +# package.path, and package.cpath. The first path of package.path +# beginning with $prefix is selected as luadir. The first path of +# package.cpath beginning with $exec_prefix is used as luaexecdir. This +# should work on all reasonable Lua installations. If a path cannot be +# determined, a default path is used. Of course, the user can override +# these later when invoking make. +# +# luadir Default: $prefix/share/lua/$LUA_VERSION +# luaexecdir Default: $exec_prefix/lib/lua/$LUA_VERSION +# +# These directories can be used by Automake as install destinations. The +# variable name minus 'dir' needs to be used as a prefix to the +# appropriate Automake primary, e.g. lua_SCRIPS or luaexec_LIBRARIES. +# +# If an acceptable Lua interpreter is found, then ACTION-IF-FOUND is +# performed, otherwise ACTION-IF-NOT-FOUND is preformed. If ACTION-IF-NOT- +# FOUND is blank, then it will default to printing an error. To prevent +# the default behavior, give ':' as an action. +# +# AX_LUA_HEADERS: Search for Lua headers. Requires that AX_PROG_LUA be +# expanded before this macro. Adds precious variable LUA_INCLUDE, which +# may contain Lua specific include flags, e.g. -I/usr/include/lua5.1. If +# LUA_INCLUDE is blank, then this macro will attempt to find suitable +# flags. +# +# LUA_INCLUDE can be used by Automake to compile Lua modules or +# executables with embedded interpreters. The *_CPPFLAGS variables should +# be used for this purpose, e.g. myprog_CPPFLAGS = $(LUA_INCLUDE). +# +# This macro searches for the header lua.h (and others). The search is +# performed with a combination of CPPFLAGS, CPATH, etc, and LUA_INCLUDE. +# If the search is unsuccessful, then some common directories are tried. +# If the headers are then found, then LUA_INCLUDE is set accordingly. +# +# The paths automatically searched are: +# +# * /usr/include/luaX.Y +# * /usr/include/lua/X.Y +# * /usr/include/luaXY +# * /usr/local/include/luaX.Y +# * /usr/local/include/lua-X.Y +# * /usr/local/include/lua/X.Y +# * /usr/local/include/luaXY +# +# (Where X.Y is the Lua version number, e.g. 5.1.) +# +# The Lua version number found in the headers is always checked to match +# the Lua interpreter's version number. Lua headers with mismatched +# version numbers are not accepted. +# +# If headers are found, then ACTION-IF-FOUND is performed, otherwise +# ACTION-IF-NOT-FOUND is performed. If ACTION-IF-NOT-FOUND is blank, then +# it will default to printing an error. To prevent the default behavior, +# set the action to ':'. +# +# AX_LUA_LIBS: Search for Lua libraries. Requires that AX_PROG_LUA be +# expanded before this macro. Adds precious variable LUA_LIB, which may +# contain Lua specific linker flags, e.g. -llua5.1. If LUA_LIB is blank, +# then this macro will attempt to find suitable flags. +# +# LUA_LIB can be used by Automake to link Lua modules or executables with +# embedded interpreters. The *_LIBADD and *_LDADD variables should be used +# for this purpose, e.g. mymod_LIBADD = $(LUA_LIB). +# +# This macro searches for the Lua library. More technically, it searches +# for a library containing the function lua_load. The search is performed +# with a combination of LIBS, LIBRARY_PATH, and LUA_LIB. +# +# If the search determines that some linker flags are missing, then those +# flags will be added to LUA_LIB. +# +# If libraries are found, then ACTION-IF-FOUND is performed, otherwise +# ACTION-IF-NOT-FOUND is performed. If ACTION-IF-NOT-FOUND is blank, then +# it will default to printing an error. To prevent the default behavior, +# set the action to ':'. +# +# AX_LUA_READLINE: Search for readline headers and libraries. Requires the +# AX_LIB_READLINE macro, which is provided by ax_lib_readline.m4 from the +# Autoconf Archive. +# +# If a readline compatible library is found, then ACTION-IF-FOUND is +# performed, otherwise ACTION-IF-NOT-FOUND is performed. +# +# LICENSE +# +# Copyright (c) 2015 Reuben Thomas +# Copyright (c) 2014 Tim Perkins +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 39 + +dnl ========================================================================= +dnl AX_PROG_LUA([MINIMUM-VERSION], [TOO-BIG-VERSION], +dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +dnl ========================================================================= +AC_DEFUN([AX_PROG_LUA], +[ + dnl Check for required tools. + AC_REQUIRE([AC_PROG_GREP]) + AC_REQUIRE([AC_PROG_SED]) + + dnl Make LUA a precious variable. + AC_ARG_VAR([LUA], [The Lua interpreter, e.g. /usr/bin/lua5.1]) + + dnl Find a Lua interpreter. + m4_define_default([_AX_LUA_INTERPRETER_LIST], + [lua lua5.3 lua53 lua5.2 lua52 lua5.1 lua51 lua50]) + + m4_if([$1], [], + [ dnl No version check is needed. Find any Lua interpreter. + AS_IF([test "x$LUA" = 'x'], + [AC_PATH_PROGS([LUA], [_AX_LUA_INTERPRETER_LIST], [:])]) + ax_display_LUA='lua' + + AS_IF([test "x$LUA" != 'x:'], + [ dnl At least check if this is a Lua interpreter. + AC_MSG_CHECKING([if $LUA is a Lua interpreter]) + _AX_LUA_CHK_IS_INTRP([$LUA], + [AC_MSG_RESULT([yes])], + [ AC_MSG_RESULT([no]) + AC_MSG_ERROR([not a Lua interpreter]) + ]) + ]) + ], + [ dnl A version check is needed. + AS_IF([test "x$LUA" != 'x'], + [ dnl Check if this is a Lua interpreter. + AC_MSG_CHECKING([if $LUA is a Lua interpreter]) + _AX_LUA_CHK_IS_INTRP([$LUA], + [AC_MSG_RESULT([yes])], + [ AC_MSG_RESULT([no]) + AC_MSG_ERROR([not a Lua interpreter]) + ]) + dnl Check the version. + m4_if([$2], [], + [_ax_check_text="whether $LUA version >= $1"], + [_ax_check_text="whether $LUA version >= $1, < $2"]) + AC_MSG_CHECKING([$_ax_check_text]) + _AX_LUA_CHK_VER([$LUA], [$1], [$2], + [AC_MSG_RESULT([yes])], + [ AC_MSG_RESULT([no]) + AC_MSG_ERROR([version is out of range for specified LUA])]) + ax_display_LUA=$LUA + ], + [ dnl Try each interpreter until we find one that satisfies VERSION. + m4_if([$2], [], + [_ax_check_text="for a Lua interpreter with version >= $1"], + [_ax_check_text="for a Lua interpreter with version >= $1, < $2"]) + AC_CACHE_CHECK([$_ax_check_text], + [ax_cv_pathless_LUA], + [ for ax_cv_pathless_LUA in _AX_LUA_INTERPRETER_LIST none; do + test "x$ax_cv_pathless_LUA" = 'xnone' && break + _AX_LUA_CHK_IS_INTRP([$ax_cv_pathless_LUA], [], [continue]) + _AX_LUA_CHK_VER([$ax_cv_pathless_LUA], [$1], [$2], [break]) + done + ]) + dnl Set $LUA to the absolute path of $ax_cv_pathless_LUA. + AS_IF([test "x$ax_cv_pathless_LUA" = 'xnone'], + [LUA=':'], + [AC_PATH_PROG([LUA], [$ax_cv_pathless_LUA])]) + ax_display_LUA=$ax_cv_pathless_LUA + ]) + ]) + + AS_IF([test "x$LUA" = 'x:'], + [ dnl Run any user-specified action, or abort. + m4_default([$4], [AC_MSG_ERROR([cannot find suitable Lua interpreter])]) + ], + [ dnl Query Lua for its version number. + AC_CACHE_CHECK([for $ax_display_LUA version], + [ax_cv_lua_version], + [ dnl Get the interpreter version in X.Y format. This should work for + dnl interpreters version 5.0 and beyond. + ax_cv_lua_version=[`$LUA -e ' + -- return a version number in X.Y format + local _, _, ver = string.find(_VERSION, "^Lua (%d+%.%d+)") + print(ver)'`] + ]) + AS_IF([test "x$ax_cv_lua_version" = 'x'], + [AC_MSG_ERROR([invalid Lua version number])]) + AC_SUBST([LUA_VERSION], [$ax_cv_lua_version]) + AC_SUBST([LUA_SHORT_VERSION], [`echo "$LUA_VERSION" | $SED 's|\.||'`]) + + dnl The following check is not supported: + dnl At times (like when building shared libraries) you may want to know + dnl which OS platform Lua thinks this is. + AC_CACHE_CHECK([for $ax_display_LUA platform], + [ax_cv_lua_platform], + [ax_cv_lua_platform=[`$LUA -e 'print("unknown")'`]]) + AC_SUBST([LUA_PLATFORM], [$ax_cv_lua_platform]) + + dnl Use the values of $prefix and $exec_prefix for the corresponding + dnl values of LUA_PREFIX and LUA_EXEC_PREFIX. These are made distinct + dnl variables so they can be overridden if need be. However, the general + dnl consensus is that you shouldn't need this ability. + AC_SUBST([LUA_PREFIX], ['${prefix}']) + AC_SUBST([LUA_EXEC_PREFIX], ['${exec_prefix}']) + + dnl Lua provides no way to query the script directory, and instead + dnl provides LUA_PATH. However, we should be able to make a safe educated + dnl guess. If the built-in search path contains a directory which is + dnl prefixed by $prefix, then we can store scripts there. The first + dnl matching path will be used. + AC_CACHE_CHECK([for $ax_display_LUA script directory], + [ax_cv_lua_luadir], + [ AS_IF([test "x$prefix" = 'xNONE'], + [ax_lua_prefix=$ac_default_prefix], + [ax_lua_prefix=$prefix]) + + dnl Initialize to the default path. + ax_cv_lua_luadir="$LUA_PREFIX/share/lua/$LUA_VERSION" + + dnl Try to find a path with the prefix. + _AX_LUA_FND_PRFX_PTH([$LUA], [$ax_lua_prefix], [script]) + AS_IF([test "x$ax_lua_prefixed_path" != 'x'], + [ dnl Fix the prefix. + _ax_strip_prefix=`echo "$ax_lua_prefix" | $SED 's|.|.|g'` + ax_cv_lua_luadir=`echo "$ax_lua_prefixed_path" | \ + $SED "s|^$_ax_strip_prefix|$LUA_PREFIX|"` + ]) + ]) + AC_SUBST([luadir], [$ax_cv_lua_luadir]) + AC_SUBST([pkgluadir], [\${luadir}/$PACKAGE]) + + dnl Lua provides no way to query the module directory, and instead + dnl provides LUA_PATH. However, we should be able to make a safe educated + dnl guess. If the built-in search path contains a directory which is + dnl prefixed by $exec_prefix, then we can store modules there. The first + dnl matching path will be used. + AC_CACHE_CHECK([for $ax_display_LUA module directory], + [ax_cv_lua_luaexecdir], + [ AS_IF([test "x$exec_prefix" = 'xNONE'], + [ax_lua_exec_prefix=$ax_lua_prefix], + [ax_lua_exec_prefix=$exec_prefix]) + + dnl Initialize to the default path. + ax_cv_lua_luaexecdir="$LUA_EXEC_PREFIX/lib/lua/$LUA_VERSION" + + dnl Try to find a path with the prefix. + _AX_LUA_FND_PRFX_PTH([$LUA], + [$ax_lua_exec_prefix], [module]) + AS_IF([test "x$ax_lua_prefixed_path" != 'x'], + [ dnl Fix the prefix. + _ax_strip_prefix=`echo "$ax_lua_exec_prefix" | $SED 's|.|.|g'` + ax_cv_lua_luaexecdir=`echo "$ax_lua_prefixed_path" | \ + $SED "s|^$_ax_strip_prefix|$LUA_EXEC_PREFIX|"` + ]) + ]) + AC_SUBST([luaexecdir], [$ax_cv_lua_luaexecdir]) + AC_SUBST([pkgluaexecdir], [\${luaexecdir}/$PACKAGE]) + + dnl Run any user specified action. + $3 + ]) +]) + +dnl AX_WITH_LUA is now the same thing as AX_PROG_LUA. +AC_DEFUN([AX_WITH_LUA], +[ + AC_MSG_WARN([[$0 is deprecated, please use AX_PROG_LUA instead]]) + AX_PROG_LUA +]) + + +dnl ========================================================================= +dnl _AX_LUA_CHK_IS_INTRP(PROG, [ACTION-IF-TRUE], [ACTION-IF-FALSE]) +dnl ========================================================================= +AC_DEFUN([_AX_LUA_CHK_IS_INTRP], +[ + dnl A minimal Lua factorial to prove this is an interpreter. This should work + dnl for Lua interpreters version 5.0 and beyond. + _ax_lua_factorial=[`$1 2>/dev/null -e ' + -- a simple factorial + function fact (n) + if n == 0 then + return 1 + else + return n * fact(n-1) + end + end + print("fact(5) is " .. fact(5))'`] + AS_IF([test "$_ax_lua_factorial" = 'fact(5) is 120'], + [$2], [$3]) +]) + + +dnl ========================================================================= +dnl _AX_LUA_CHK_VER(PROG, MINIMUM-VERSION, [TOO-BIG-VERSION], +dnl [ACTION-IF-TRUE], [ACTION-IF-FALSE]) +dnl ========================================================================= +AC_DEFUN([_AX_LUA_CHK_VER], +[ + dnl Check that the Lua version is within the bounds. Only the major and minor + dnl version numbers are considered. This should work for Lua interpreters + dnl version 5.0 and beyond. + _ax_lua_good_version=[`$1 -e ' + -- a script to compare versions + function verstr2num(verstr) + local _, _, majorver, minorver = string.find(verstr, "^(%d+)%.(%d+)") + if majorver and minorver then + return tonumber(majorver) * 100 + tonumber(minorver) + end + end + local minver = verstr2num("$2") + local _, _, trimver = string.find(_VERSION, "^Lua (.*)") + local ver = verstr2num(trimver) + local maxver = verstr2num("$3") or 1e9 + if minver <= ver and ver < maxver then + print("yes") + else + print("no") + end'`] + AS_IF([test "x$_ax_lua_good_version" = "xyes"], + [$4], [$5]) +]) + + +dnl ========================================================================= +dnl _AX_LUA_FND_PRFX_PTH(PROG, PREFIX, SCRIPT-OR-MODULE-DIR) +dnl ========================================================================= +AC_DEFUN([_AX_LUA_FND_PRFX_PTH], +[ + dnl Get the script or module directory by querying the Lua interpreter, + dnl filtering on the given prefix, and selecting the shallowest path. If no + dnl path is found matching the prefix, the result will be an empty string. + dnl The third argument determines the type of search, it can be 'script' or + dnl 'module'. Supplying 'script' will perform the search with package.path + dnl and LUA_PATH, and supplying 'module' will search with package.cpath and + dnl LUA_CPATH. This is done for compatibility with Lua 5.0. + + ax_lua_prefixed_path=[`$1 -e ' + -- get the path based on search type + local searchtype = "$3" + local paths = "" + if searchtype == "script" then + paths = (package and package.path) or LUA_PATH + elseif searchtype == "module" then + paths = (package and package.cpath) or LUA_CPATH + end + -- search for the prefix + local prefix = "'$2'" + local minpath = "" + local mindepth = 1e9 + string.gsub(paths, "(@<:@^;@:>@+)", + function (path) + path = string.gsub(path, "%?.*$", "") + path = string.gsub(path, "/@<:@^/@:>@*$", "") + if string.find(path, prefix) then + local depth = string.len(string.gsub(path, "@<:@^/@:>@", "")) + if depth < mindepth then + minpath = path + mindepth = depth + end + end + end) + print(minpath)'`] +]) + + +dnl ========================================================================= +dnl AX_LUA_HEADERS([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +dnl ========================================================================= +AC_DEFUN([AX_LUA_HEADERS], +[ + dnl Check for LUA_VERSION. + AC_MSG_CHECKING([if LUA_VERSION is defined]) + AS_IF([test "x$LUA_VERSION" != 'x'], + [AC_MSG_RESULT([yes])], + [ AC_MSG_RESULT([no]) + AC_MSG_ERROR([cannot check Lua headers without knowing LUA_VERSION]) + ]) + + dnl Make LUA_INCLUDE a precious variable. + AC_ARG_VAR([LUA_INCLUDE], [The Lua includes, e.g. -I/usr/include/lua5.1]) + + dnl Some default directories to search. + LUA_SHORT_VERSION=`echo "$LUA_VERSION" | $SED 's|\.||'` + m4_define_default([_AX_LUA_INCLUDE_LIST], + [ /usr/include/lua$LUA_VERSION \ + /usr/include/lua-$LUA_VERSION \ + /usr/include/lua/$LUA_VERSION \ + /usr/include/lua$LUA_SHORT_VERSION \ + /usr/local/include/lua$LUA_VERSION \ + /usr/local/include/lua-$LUA_VERSION \ + /usr/local/include/lua/$LUA_VERSION \ + /usr/local/include/lua$LUA_SHORT_VERSION \ + ]) + + dnl Try to find the headers. + _ax_lua_saved_cppflags=$CPPFLAGS + CPPFLAGS="$CPPFLAGS $LUA_INCLUDE" + AC_CHECK_HEADERS([lua.h lualib.h lauxlib.h luaconf.h]) + CPPFLAGS=$_ax_lua_saved_cppflags + + dnl Try some other directories if LUA_INCLUDE was not set. + AS_IF([test "x$LUA_INCLUDE" = 'x' && + test "x$ac_cv_header_lua_h" != 'xyes'], + [ dnl Try some common include paths. + for _ax_include_path in _AX_LUA_INCLUDE_LIST; do + test ! -d "$_ax_include_path" && continue + + AC_MSG_CHECKING([for Lua headers in]) + AC_MSG_RESULT([$_ax_include_path]) + + AS_UNSET([ac_cv_header_lua_h]) + AS_UNSET([ac_cv_header_lualib_h]) + AS_UNSET([ac_cv_header_lauxlib_h]) + AS_UNSET([ac_cv_header_luaconf_h]) + + _ax_lua_saved_cppflags=$CPPFLAGS + CPPFLAGS="$CPPFLAGS -I$_ax_include_path" + AC_CHECK_HEADERS([lua.h lualib.h lauxlib.h luaconf.h]) + CPPFLAGS=$_ax_lua_saved_cppflags + + AS_IF([test "x$ac_cv_header_lua_h" = 'xyes'], + [ LUA_INCLUDE="-I$_ax_include_path" + break + ]) + done + ]) + + AS_IF([test "x$ac_cv_header_lua_h" = 'xyes'], + [ dnl Make a program to print LUA_VERSION defined in the header. + dnl TODO It would be really nice if we could do this without compiling a + dnl program, then it would work when cross compiling. But I'm not sure how + dnl to do this reliably. For now, assume versions match when cross compiling. + + AS_IF([test "x$cross_compiling" != 'xyes'], + [ AC_CACHE_CHECK([for Lua header version], + [ax_cv_lua_header_version], + [ _ax_lua_saved_cppflags=$CPPFLAGS + CPPFLAGS="$CPPFLAGS $LUA_INCLUDE" + AC_RUN_IFELSE( + [ AC_LANG_SOURCE([[ +#include +#include +#include +int main(int argc, char ** argv) +{ + if(argc > 1) printf("%s", LUA_VERSION); + exit(EXIT_SUCCESS); +} +]]) + ], + [ ax_cv_lua_header_version=`./conftest$EXEEXT p | \ + $SED -n "s|^Lua \(@<:@0-9@:>@\{1,\}\.@<:@0-9@:>@\{1,\}\).\{0,\}|\1|p"` + ], + [ax_cv_lua_header_version='unknown']) + CPPFLAGS=$_ax_lua_saved_cppflags + ]) + + dnl Compare this to the previously found LUA_VERSION. + AC_MSG_CHECKING([if Lua header version matches $LUA_VERSION]) + AS_IF([test "x$ax_cv_lua_header_version" = "x$LUA_VERSION"], + [ AC_MSG_RESULT([yes]) + ax_header_version_match='yes' + ], + [ AC_MSG_RESULT([no]) + ax_header_version_match='no' + ]) + ], + [ AC_MSG_WARN([cross compiling so assuming header version number matches]) + ax_header_version_match='yes' + ]) + ]) + + dnl Was LUA_INCLUDE specified? + AS_IF([test "x$ax_header_version_match" != 'xyes' && + test "x$LUA_INCLUDE" != 'x'], + [AC_MSG_ERROR([cannot find headers for specified LUA_INCLUDE])]) + + dnl Test the final result and run user code. + AS_IF([test "x$ax_header_version_match" = 'xyes'], [$1], + [m4_default([$2], [AC_MSG_ERROR([cannot find Lua includes])])]) +]) + +dnl AX_LUA_HEADERS_VERSION no longer exists, use AX_LUA_HEADERS. +AC_DEFUN([AX_LUA_HEADERS_VERSION], +[ + AC_MSG_WARN([[$0 is deprecated, please use AX_LUA_HEADERS instead]]) +]) + + +dnl ========================================================================= +dnl AX_LUA_LIBS([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +dnl ========================================================================= +AC_DEFUN([AX_LUA_LIBS], +[ + dnl TODO Should this macro also check various -L flags? + + dnl Check for LUA_VERSION. + AC_MSG_CHECKING([if LUA_VERSION is defined]) + AS_IF([test "x$LUA_VERSION" != 'x'], + [AC_MSG_RESULT([yes])], + [ AC_MSG_RESULT([no]) + AC_MSG_ERROR([cannot check Lua libs without knowing LUA_VERSION]) + ]) + + dnl Make LUA_LIB a precious variable. + AC_ARG_VAR([LUA_LIB], [The Lua library, e.g. -llua5.1]) + + AS_IF([test "x$LUA_LIB" != 'x'], + [ dnl Check that LUA_LIBS works. + _ax_lua_saved_libs=$LIBS + LIBS="$LIBS $LUA_LIB" + AC_SEARCH_LIBS([lua_load], [], + [_ax_found_lua_libs='yes'], + [_ax_found_lua_libs='no']) + LIBS=$_ax_lua_saved_libs + + dnl Check the result. + AS_IF([test "x$_ax_found_lua_libs" != 'xyes'], + [AC_MSG_ERROR([cannot find libs for specified LUA_LIB])]) + ], + [ dnl First search for extra libs. + _ax_lua_extra_libs='' + + _ax_lua_saved_libs=$LIBS + LIBS="$LIBS $LUA_LIB" + AC_SEARCH_LIBS([exp], [m]) + AC_SEARCH_LIBS([dlopen], [dl]) + LIBS=$_ax_lua_saved_libs + + AS_IF([test "x$ac_cv_search_exp" != 'xno' && + test "x$ac_cv_search_exp" != 'xnone required'], + [_ax_lua_extra_libs="$_ax_lua_extra_libs $ac_cv_search_exp"]) + + AS_IF([test "x$ac_cv_search_dlopen" != 'xno' && + test "x$ac_cv_search_dlopen" != 'xnone required'], + [_ax_lua_extra_libs="$_ax_lua_extra_libs $ac_cv_search_dlopen"]) + + dnl Try to find the Lua libs. + _ax_lua_saved_libs=$LIBS + LIBS="$LIBS $LUA_LIB" + AC_SEARCH_LIBS([lua_load], + [ lua$LUA_VERSION \ + lua$LUA_SHORT_VERSION \ + lua-$LUA_VERSION \ + lua-$LUA_SHORT_VERSION \ + lua \ + ], + [_ax_found_lua_libs='yes'], + [_ax_found_lua_libs='no'], + [$_ax_lua_extra_libs]) + LIBS=$_ax_lua_saved_libs + + AS_IF([test "x$ac_cv_search_lua_load" != 'xno' && + test "x$ac_cv_search_lua_load" != 'xnone required'], + [LUA_LIB="$ac_cv_search_lua_load $_ax_lua_extra_libs"]) + ]) + + dnl Test the result and run user code. + AS_IF([test "x$_ax_found_lua_libs" = 'xyes'], [$1], + [m4_default([$2], [AC_MSG_ERROR([cannot find Lua libs])])]) +]) + + +dnl ========================================================================= +dnl AX_LUA_READLINE([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +dnl ========================================================================= +AC_DEFUN([AX_LUA_READLINE], +[ + AX_LIB_READLINE + AS_IF([test "x$ac_cv_header_readline_readline_h" != 'x' && + test "x$ac_cv_header_readline_history_h" != 'x'], + [ LUA_LIBS_CFLAGS="-DLUA_USE_READLINE $LUA_LIBS_CFLAGS" + $1 + ], + [$2]) +]) diff --git a/m4/ax_python.m4 b/m4/ax_python.m4 new file mode 100644 index 0000000000..91d12b99b4 --- /dev/null +++ b/m4/ax_python.m4 @@ -0,0 +1,289 @@ +dnl FRR Python autoconf magic +dnl 2019 David Lamparter for NetDEF, Inc. +dnl SPDX-License-Identifier: GPL-2.0-or-later + +dnl the _ at the beginning will be cut off (to support the empty version string) +m4_define_default([_FRR_PY_VERS], [_3 _3.10 _3.9 _3.8 _3.7 _3.6 _3.5 _3.4 _3.3 _3.2 _ _2 _2.7]) + +dnl check basic interpreter properties (py2/py3) +dnl doubles as simple check whether the interpreter actually works +dnl also swaps in the full path to the interpreter +dnl arg1: if-true, arg2: if-false +AC_DEFUN([_FRR_PYTHON_INTERP], [dnl +AC_ARG_VAR([PYTHON], [Python interpreter to use])dnl + AC_MSG_CHECKING([python interpreter $PYTHON]) + AC_RUN_LOG(["$PYTHON" -c 'import sys; open("conftest.pyver", "w").write(sys.executable or ""); sys.exit(not (sys.version_info.major == 2 and sys.version_info.minor >= 7))']) + py2=$ac_status + _py2_full="`cat conftest.pyver 2>/dev/null`" + rm -f "conftest.pyver" >/dev/null 2>/dev/null + + AC_RUN_LOG(["$PYTHON" -c 'import sys; open("conftest.pyver", "w").write(sys.executable or ""); sys.exit(not ((sys.version_info.major == 3 and sys.version_info.minor >= 2) or sys.version_info.major > 3))']) + py3=$ac_status + _py3_full="`cat conftest.pyver 2>/dev/null`" + rm -f "conftest.pyver" >/dev/null 2>/dev/null + + case "p${py2}p${py3}" in + p0p1) frr_cv_python=python2 + _python_full="$_py2_full" ;; + p1p0) frr_cv_python=python3 + _python_full="$_py3_full" ;; + *) frr_cv_python=none ;; + esac + + if test "$frr_cv_python" = none; then + AC_MSG_RESULT([not working]) + $2 + else + test -n "$_python_full" -a -x "$_python_full" && PYTHON="$_python_full" + AC_MSG_RESULT([$PYTHON ($frr_cv_python)]) + $1 + fi + + dnl return value + test "$frr_cv_python" != none +]) + +dnl check whether $PYTHON has modules available +dnl arg1: list of modules (space separated) +dnl arg2: if all true, arg3: if any missing +dnl also sets frr_py_mod_ to "true" or "false" +AC_DEFUN([FRR_PYTHON_MODULES], [ + result=true + for pymod in $1; do + AC_MSG_CHECKING([whether $PYTHON module $pymod is available]) + AC_RUN_LOG(["$PYTHON" -c "import $pymod"]) + sane="`echo \"$pymod\" | tr -c '[a-zA-Z0-9\n]' '_'`" + if test "$ac_status" -eq 0; then + AC_MSG_RESULT([yes]) + eval frr_py_mod_$sane=true + else + AC_MSG_RESULT([no]) + eval frr_py_mod_$sane=false + result=false + fi + done + if $result; then + m4_default([$2], [:]) + else + m4_default([$3], [:]) + fi + $result +]) + +dnl check whether $PYTHON has modules available +dnl arg1: list of modules (space separated) +dnl arg2: command line parameters for executing +dnl arg3: if all true, arg4: if any missing +dnl also sets frr_py_modexec_ to "true" or "false" +AC_DEFUN([FRR_PYTHON_MOD_EXEC], [ + result=true + for pymod in $1; do + AC_MSG_CHECKING([whether $PYTHON module $pymod is executable]) + AC_RUN_LOG(["$PYTHON" -m "$pymod" $2 > /dev/null]) + sane="`echo \"$pymod\" | tr -c '[a-zA-Z0-9\n]' '_'`" + if test "$ac_status" -eq 0; then + AC_MSG_RESULT([yes]) + eval frr_py_modexec_$sane=true + else + AC_MSG_RESULT([no]) + eval frr_py_modexec_$sane=false + result=false + fi + done + if $result; then + m4_default([$3], [:]) + else + m4_default([$4], [:]) + fi + $result +]) + +dnl check whether we can build & link python bits +dnl input: PYTHON_CFLAGS and PYTHON_LIBS +AC_DEFUN([_FRR_PYTHON_DEVENV], [ + result=true + AC_LINK_IFELSE_FLAGS([$PYTHON_CFLAGS], [$PYTHON_LIBS], [AC_LANG_PROGRAM([ +#include +#if PY_VERSION_HEX < 0x02070000 +#error python too old +#endif +int main(void); +], +[ +{ + Py_Initialize(); + return 0; +} +])], [ + # some python installs are missing the zlib dependency... + PYTHON_LIBS="${PYTHON_LIBS} -lz" + AC_LINK_IFELSE_FLAGS([$PYTHON_CFLAGS], [$PYTHON_LIBS], [AC_LANG_PROGRAM([ +#include +#if PY_VERSION_HEX < 0x02070000 +#error python too old +#endif +int main(void); +], +[ +{ + Py_Initialize(); + return 0; +} +])], [ + result=false + AC_MSG_RESULT([no]) + ], [:]) + ], [:]) + + if $result; then + AC_LINK_IFELSE_FLAGS([$PYTHON_CFLAGS], [$PYTHON_LIBS], [AC_LANG_PROGRAM([ +#include +#if PY_VERSION_HEX != $1 +#error python version mismatch +#endif +int main(void); +], +[ +{ + Py_Initialize(); + return 0; +} +])], [ + result=false + AC_MSG_RESULT([version mismatch]) + ], [ + AC_MSG_RESULT([yes]) + ]) + fi + + if $result; then + m4_default([$2], [:]) + else + m4_default([$3], [ + unset PYTHON_LIBS + unset PYTHON_CFLAGS + ]) + fi +]) + +AC_DEFUN([_FRR_PYTHON_GETDEV], [dnl +AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl + + py_abi="` \"$1\" -c \"import sys; print(getattr(sys, 'abiflags', ''))\"`" + py_hex="` \"$1\" -c \"import sys; print(hex(sys.hexversion))\"`" + py_ldver="` \"$1\" -c \"import sysconfig; print(sysconfig.get_config_var('LDVERSION') or '')\"`" + py_ver="` \"$1\" -c \"import sysconfig; print(sysconfig.get_config_var('VERSION') or '')\"`" + py_bindir="`\"$1\" -c \"import sysconfig; print(sysconfig.get_config_var('BINDIR') or '')\"`" + test -z "$py_bindir" || py_bindir="$py_bindir/" + echo "py_abi=${py_abi} py_ldver=${py_ldver} py_ver=${py_ver} py_bindir=${py_bindir}" >&AS_MESSAGE_LOG_FD + + py_found=false + + for tryver in "${py_ldver}" "${py_ver}"; do + pycfg="${py_bindir}python${tryver}-config" + AC_MSG_CHECKING([whether ${pycfg} is available]) + if "$pycfg" --configdir >/dev/null 2>/dev/null; then + AC_MSG_RESULT([yes]) + + PYTHON_CFLAGS="`\"$pycfg\" --includes`" + minor_ver=${py_ver#*\.} + if test $((minor_ver)) -gt 7; then + PYTHON_LIBS="`\"$pycfg\" --ldflags --embed`" + else + PYTHON_LIBS="`\"$pycfg\" --ldflags`" + fi + + AC_MSG_CHECKING([whether ${pycfg} provides a working build environment]) + _FRR_PYTHON_DEVENV([$py_hex], [ + py_found=true + break + ]) + else + AC_MSG_RESULT([no]) + fi + + pkg_failed=no + AC_MSG_CHECKING([whether pkg-config python-${tryver} is available]) + unset PYTHON_CFLAGS + unset PYTHON_LIBS + pkg="python-${tryver}" + pkg="${pkg%-}" + _PKG_CONFIG([PYTHON_CFLAGS], [cflags], [${pkg}]) + _PKG_CONFIG([PYTHON_LIBS], [libs], [${pkg}]) + if test $pkg_failed = no; then + AC_MSG_RESULT([yes]) + + PYTHON_CFLAGS=$pkg_cv_PYTHON_CFLAGS + PYTHON_LIBS=$pkg_cv_PYTHON_LIBS + + AC_MSG_CHECKING([whether pkg-config python-${tryver} provides a working build environment]) + _FRR_PYTHON_DEVENV([$py_hex], [ + py_found=true + break + ]) + else + AC_MSG_RESULT([no]) + fi + done + + if $py_found; then + m4_default([$2], [:]) + else + unset PYTHON_CFLAGS + unset PYTHON_LIBS + m4_default([$3], [:]) + fi +]) + +dnl just find python without checking headers/libs +AC_DEFUN([FRR_PYTHON], [ + dnl user override + if test "x$PYTHON" != "x"; then + _FRR_PYTHON_INTERP([], [ + AC_MSG_ERROR([PYTHON ($PYTHON) explicitly specified but not working]) + ]) + else + for frr_pyver in _FRR_PY_VERS; do + PYTHON="python${frr_pyver#_}" + _FRR_PYTHON_INTERP([break]) + PYTHON=":" + done + if test "$PYTHON" = ":"; then + AC_MSG_ERROR([no working python version found]) + fi + fi + AC_SUBST([PYTHON]) +]) + +dnl find python with checking headers/libs +AC_DEFUN([FRR_PYTHON_DEV], [dnl +AC_ARG_VAR([PYTHON_CFLAGS], [C compiler flags for Python])dnl +AC_ARG_VAR([PYTHON_LIBS], [linker flags for Python])dnl + + dnl user override + if test "x$PYTHON" != "x"; then + _FRR_PYTHON_INTERP([], [ + AC_MSG_ERROR([PYTHON ($PYTHON) explicitly specified but not working]) + ]) + _FRR_PYTHON_GETDEV([$PYTHON], [], [ + AC_MSG_ERROR([PYTHON ($PYTHON) explicitly specified but development environment not working]) + ]) + else + for frr_pyver in _FRR_PY_VERS; do + PYTHON="python${frr_pyver#_}" + _FRR_PYTHON_INTERP([ + _FRR_PYTHON_GETDEV([$PYTHON], [ + break + ]) + ]) + PYTHON=":" + done + if test "$PYTHON" = ":"; then + AC_MSG_ERROR([no working python version found]) + fi + fi + + AC_SUBST([PYTHON_CFLAGS]) + AC_SUBST([PYTHON_LIBS]) + AC_SUBST([PYTHON]) +]) diff --git a/mlag/mlag.proto b/mlag/mlag.proto new file mode 100644 index 0000000000..1e302151f8 --- /dev/null +++ b/mlag/mlag.proto @@ -0,0 +1,186 @@ +// See README.txt for information and build instructions. +// +// Note: START and END tags are used in comments to define sections used in +// tutorials. They are not part of the syntax for Protocol Buffers. +// +// To get an in-depth walkthrough of this file and the related examples, see: +// https://developers.google.com/protocol-buffers/docs/tutorials + +// [START declaration] +syntax = "proto3"; +//package tutorial; + +/* + * This Contains the Message structures used for PIM MLAG Active-Active support. + * Mainly there were two types of messages + * + * 1. Messages sent from PIM (Node-1) to PIM (Node-2) + * 2. Messages sent from CLAG to PIM (status Messages) + * + * ProtoBuf supports maximum 32 fields, so to make it more generic message + * encoding is like below. + * __________________________________________ + * | | | + * | Header | bytes | + * ___________________________________________ + * + * + * Header carries Information about + * 1) what Message it is carrying + * 2) Bytes carries the actual payload encoded with protobuf + * + * + * Limitations + *============= + * Since message-type is 32-bit, there were no real limitations on number of + * messages Infra can support, but each message can carry only 32 fields. + * + */ + + +// [START messages] +message ZebraMlag_Header { + enum MessageType { + ZEBRA_MLAG_NONE = 0; //Invalid message-type + ZEBRA_MLAG_REGISTER = 1; + ZEBRA_MLAG_DEREGISTER = 2; + ZEBRA_MLAG_STATUS_UPDATE = 3; + ZEBRA_MLAG_MROUTE_ADD = 4; + ZEBRA_MLAG_MROUTE_DEL = 5; + ZEBRA_MLAG_DUMP = 6; + ZEBRA_MLAG_MROUTE_ADD_BULK = 7; + ZEBRA_MLAG_MROUTE_DEL_BULK = 8; + ZEBRA_MLAG_PIM_CFG_DUMP = 10; + ZEBRA_MLAG_VXLAN_UPDATE = 11; + ZEBRA_MLAG_ZEBRA_STATUS_UPDATE = 12; + } + + /* + * tells what type of message this payload carries + */ + MessageType type = 1; + + /* + * Length of payload + */ + uint32 len = 2; + + /* + * Actual Encoded payload + */ + bytes data = 3; +} + + +/* + * ZEBRA_MLAG_REGISTER & ZEBRA_MLAG_DEREGISTER + * + * After the MLAGD is up, First Zebra has to register to send any data, + * otherwise MLAGD will not accept any data from the client. + * De-register will be used for the Data cleanup at MLAGD + * These are NULL payload message currently + */ + +/* + * ZEBRA_MLAG_STATUS_UPDATE + * + * This message will be posted by CLAGD(an external control plane manager + * which monitors CLAG failures) to inform peerlink/CLAG Failure + * to zebra, after the failure Notification Node with primary role will + * forward the Traffic and Node with standby will drop the traffic + */ + +message ZebraMlagStatusUpdate { + enum ClagState { + CLAG_STATE_DOWN = 0; + CLAG_STATE_RUNNING = 1; + } + + enum ClagRole { + CLAG_ROLE_NONE = 0; + CLAG_ROLE_PRIMAY = 1; + CLAG_ROLE_SECONDARY = 2; + } + + string peerlink = 1; + ClagRole my_role = 2; + ClagState peer_state = 3; +} + +/* + * ZEBRA_MLAG_VXLAN_UPDATE + * + * This message will be posted by CLAGD(an external control plane Manager + * which is responsible for MCLAG) to inform zebra obout anycast/local + * ip updates. + */ +message ZebraMlagVxlanUpdate { + uint32 anycast_ip = 1; + uint32 local_ip = 2; +} + +/* + * ZebraMlagZebraStatusUpdate + * + * This message will be posted by CLAGD to advertise FRR state + * Change Information to peer + */ + +message ZebraMlagZebraStatusUpdate{ + enum FrrState { + FRR_STATE_NONE = 0; + FRR_STATE_DOWN = 1; + FRR_STATE_UP = 2; + } + + FrrState peer_frrstate = 1; +} + +/* + * ZEBRA_MLAG_MROUTE_ADD & ZEBRA_MLAG_MROUTE_DEL + * + * These messages will be sent from PIM (Node-1) to PIM (Node-2) to perform + * DF Election for each Mcast flow. Elected DF will forward the traffic + * towards the host and loser will keep the OIL as empty, so that only single + * copy will be sent to host + * This message will be posted with any change in the params. + * + * ZEBRA_MLAG_MROUTE_DEL is mainly to delete the record at MLAGD when the + * mcast flow is deleted. + * key for the MLAGD lookup is (vrf_id, source_ip & group_ip) + */ + +message ZebraMlagMrouteAdd { + string vrf_name = 1; + uint32 source_ip = 2; + uint32 group_ip = 3; + /* + * This is the IGP Cost to reach Configured RP in case of (*,G) or + * Cost to the source in case of (S,G) entry + */ + uint32 cost_to_rp = 4; + uint32 owner_id = 5; + bool am_i_DR = 6; + bool am_i_Dual_active = 7; + uint32 vrf_id = 8; + string intf_name = 9; +} + +message ZebraMlagMrouteDel { + string vrf_name = 1; + uint32 source_ip = 2; + uint32 group_ip = 3; + uint32 owner_id = 4; + uint32 vrf_id = 5; + string intf_name = 6; +} + +message ZebraMlagMrouteAddBulk { + repeated ZebraMlagMrouteAdd mroute_add = 1; +} + +message ZebraMlagMrouteDelBulk { + repeated ZebraMlagMrouteDel mroute_del = 1; +} + +// [END messages] diff --git a/mlag/subdir.am b/mlag/subdir.am new file mode 100644 index 0000000000..49d1761505 --- /dev/null +++ b/mlag/subdir.am @@ -0,0 +1,19 @@ +if HAVE_PROTOBUF3 +lib_LTLIBRARIES += mlag/libmlag_pb.la +endif + +mlag_libmlag_pb_la_LDFLAGS = -version-info 0:0:0 +mlag_libmlag_pb_la_CPPFLAGS = $(AM_CPPFLAGS) $(PROTOBUF_C_CFLAGS) +mlag_libmlag_pb_la_SOURCES = \ + # end + +nodist_mlag_libmlag_pb_la_SOURCES = \ + mlag/mlag.pb-c.c \ + # end + +CLEANFILES += \ + mlag/mlag.pb-c.c \ + mlag/mlag.pb-c.h \ + # end + +EXTRA_DIST += mlag/mlag.proto diff --git a/nhrpd/linux.c b/nhrpd/linux.c index 85e941e7ba..59c82b1c55 100644 --- a/nhrpd/linux.c +++ b/nhrpd/linux.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "nhrp_protocol.h" #include "os.h" @@ -127,10 +128,11 @@ static int linux_configure_arp(const char *iface, int on) static int linux_icmp_redirect_off(const char *iface) { - char fname[256]; + char fname[PATH_MAX]; int fd, ret = -1; - sprintf(fname, "/proc/sys/net/ipv4/conf/%s/send_redirects", iface); + snprintf(fname, sizeof(fname), + "/proc/sys/net/ipv4/conf/%s/send_redirects", iface); fd = open(fname, O_WRONLY); if (fd < 0) return -1; diff --git a/nhrpd/list.h b/nhrpd/list.h index ee7f1c4403..a43687ac08 100644 --- a/nhrpd/list.h +++ b/nhrpd/list.h @@ -198,9 +198,12 @@ static inline int list_empty(const struct list_head *n) pos = list_entry(pos->member.next, typeof(*pos), member)) #define list_for_each_entry_safe(pos, n, head, member) \ - for (pos = list_entry((head)->next, typeof(*pos), member), \ - n = list_entry(pos->member.next, typeof(*pos), member); \ - &pos->member != (head); \ + for (pos = ((head)->next != head ? \ + list_entry((head)->next, typeof(*pos), member) : \ + NULL), \ + n = (pos ? \ + list_entry(pos->member.next, typeof(*pos), member) : NULL); \ + pos && (&pos->member != (head)); \ pos = n, n = list_entry(n->member.next, typeof(*n), member)) #endif diff --git a/nhrpd/netlink_arp.c b/nhrpd/netlink_arp.c index 6666c6e96b..cf338a0876 100644 --- a/nhrpd/netlink_arp.c +++ b/nhrpd/netlink_arp.c @@ -65,11 +65,12 @@ static void netlink_neigh_msg(struct nlmsghdr *msg, struct zbuf *zb) struct nhrp_cache *c; struct interface *ifp; struct zbuf payload; - union sockunion addr; + union sockunion addr, lladdr; size_t len; - char buf[SU_ADDRSTRLEN]; + char buf[4][SU_ADDRSTRLEN]; int state; + memset(&lladdr, 0, sizeof(lladdr)); ndm = znl_pull(zb, sizeof(*ndm)); if (!ndm) return; @@ -82,6 +83,10 @@ static void netlink_neigh_msg(struct nlmsghdr *msg, struct zbuf *zb) sockunion_set(&addr, ndm->ndm_family, zbuf_pulln(&payload, len), len); break; + case NDA_LLADDR: + sockunion_set(&lladdr, ndm->ndm_family, + zbuf_pulln(&payload, len), len); + break; } } @@ -93,20 +98,34 @@ static void netlink_neigh_msg(struct nlmsghdr *msg, struct zbuf *zb) if (!c) return; - if (msg->nlmsg_type == RTM_GETNEIGH) { - debugf(NHRP_DEBUG_KERNEL, "Netlink: who-has %s dev %s", - sockunion2str(&addr, buf, sizeof buf), ifp->name); + debugf(NHRP_DEBUG_KERNEL, + "Netlink: %s %s dev %s lladdr %s nud 0x%x cache used %u type %u", + (msg->nlmsg_type == RTM_GETNEIGH) + ? "who-has" + : (msg->nlmsg_type == RTM_NEWNEIGH) ? "new-neigh" + : "del-neigh", + sockunion2str(&addr, buf[0], sizeof(buf[0])), ifp->name, + sockunion2str(&lladdr, buf[1], sizeof(buf[1])), ndm->ndm_state, + c->used, c->cur.type); + if (msg->nlmsg_type == RTM_GETNEIGH) { if (c->cur.type >= NHRP_CACHE_CACHED) { nhrp_cache_set_used(c, 1); - netlink_update_binding(ifp, &addr, - &c->cur.peer->vc->remote.nbma); + debugf(NHRP_DEBUG_KERNEL, + "Netlink: update binding for %s dev %s from c %s peer.vc.nbma %s to lladdr %s", + sockunion2str(&addr, buf[0], sizeof(buf[0])), + ifp->name, + sockunion2str(&c->cur.remote_nbma_natoa, buf[1], + sizeof(buf[1])), + sockunion2str(&c->cur.peer->vc->remote.nbma, + buf[2], sizeof(buf[2])), + sockunion2str(&lladdr, buf[3], sizeof(buf[3]))); + /* In case of shortcuts, nbma is given by lladdr, not + * vc->remote.nbma. + */ + netlink_update_binding(ifp, &addr, &lladdr); } } else { - debugf(NHRP_DEBUG_KERNEL, "Netlink: update %s dev %s nud %x", - sockunion2str(&addr, buf, sizeof buf), ifp->name, - ndm->ndm_state); - state = (msg->nlmsg_type == RTM_NEWNEIGH) ? ndm->ndm_state : NUD_FAILED; nhrp_cache_set_used(c, state == NUD_REACHABLE); diff --git a/nhrpd/nhrp_cache.c b/nhrpd/nhrp_cache.c index 5508778243..42f6a88f95 100644 --- a/nhrpd/nhrp_cache.c +++ b/nhrpd/nhrp_cache.c @@ -30,9 +30,9 @@ const char *const nhrp_cache_type_str[] = { [NHRP_CACHE_LOCAL] = "local", }; -static unsigned int nhrp_cache_protocol_key(void *peer_data) +static unsigned int nhrp_cache_protocol_key(const void *peer_data) { - struct nhrp_cache *p = peer_data; + const struct nhrp_cache *p = peer_data; return sockunion_hash(&p->remote_addr); } @@ -119,12 +119,43 @@ static void nhrp_cache_update_route(struct nhrp_cache *c) { struct prefix pfx; struct nhrp_peer *p = c->cur.peer; + char buf[3][SU_ADDRSTRLEN]; + struct nhrp_interface *nifp; sockunion2hostprefix(&c->remote_addr, &pfx); if (p && nhrp_peer_check(p, 1)) { - netlink_update_binding(p->ifp, &c->remote_addr, - &p->vc->remote.nbma); + if (sockunion_family(&c->cur.remote_nbma_natoa) != AF_UNSPEC) { + /* remote_nbma_natoa is already set. Therefore, binding + * should be updated to this value and not vc's remote + * nbma. + */ + debugf(NHRP_DEBUG_COMMON, + "cache (remote_nbma_natoa set): Update binding for %s dev %s from (deleted) peer.vc.nbma %s to %s", + sockunion2str(&c->remote_addr, buf[0], + sizeof(buf[0])), + p->ifp->name, + sockunion2str(&p->vc->remote.nbma, buf[1], + sizeof(buf[1])), + sockunion2str(&c->cur.remote_nbma_natoa, buf[2], + sizeof(buf[2]))); + + netlink_update_binding(p->ifp, &c->remote_addr, + &c->cur.remote_nbma_natoa); + } else { + /* update binding to peer->vc->remote->nbma */ + debugf(NHRP_DEBUG_COMMON, + "cache (remote_nbma_natoa unspec): Update binding for %s dev %s from (deleted) to peer.vc.nbma %s", + sockunion2str(&c->remote_addr, buf[0], + sizeof(buf[0])), + p->ifp->name, + sockunion2str(&p->vc->remote.nbma, buf[1], + sizeof(buf[1]))); + + netlink_update_binding(p->ifp, &c->remote_addr, + &p->vc->remote.nbma); + } + nhrp_route_announce(1, c->cur.type, &pfx, c->ifp, NULL, c->cur.mtu); if (c->cur.type >= NHRP_CACHE_DYNAMIC) { @@ -139,6 +170,17 @@ static void nhrp_cache_update_route(struct nhrp_cache *c) c->route_installed = 1; } } else { + /* debug the reason for peer check fail */ + if (p) { + nifp = p->ifp->info; + debugf(NHRP_DEBUG_COMMON, + "cache (peer check failed: online?%d requested?%d ipsec?%d)", + p->online, p->requested, + nifp->ipsec_profile ? 1 : 0); + } else + debugf(NHRP_DEBUG_COMMON, + "cache (peer check failed: no p)"); + if (c->nhrp_route_installed) { nhrp_route_update_nhrp(&pfx, NULL); c->nhrp_route_installed = 0; @@ -207,10 +249,10 @@ static void nhrp_cache_update_timers(struct nhrp_cache *c) static void nhrp_cache_authorize_binding(struct nhrp_reqid *r, void *arg) { struct nhrp_cache *c = container_of(r, struct nhrp_cache, eventid); - char buf[SU_ADDRSTRLEN]; + char buf[3][SU_ADDRSTRLEN]; debugf(NHRP_DEBUG_COMMON, "cache: %s %s: %s", c->ifp->name, - sockunion2str(&c->remote_addr, buf, sizeof buf), + sockunion2str(&c->remote_addr, buf[0], sizeof(buf[0])), (const char *)arg); nhrp_reqid_free(&nhrp_event_reqid, r); @@ -230,6 +272,26 @@ static void nhrp_cache_authorize_binding(struct nhrp_reqid *r, void *arg) if (c->cur.peer) nhrp_peer_notify_add(c->cur.peer, &c->peer_notifier, nhrp_cache_peer_notifier); + + if (sockunion_family(&c->cur.remote_nbma_natoa) != AF_UNSPEC) { + debugf(NHRP_DEBUG_COMMON, + "cache: update binding for %s dev %s from (deleted) peer.vc.nbma %s to %s", + sockunion2str(&c->remote_addr, buf[0], + sizeof(buf[0])), + c->ifp->name, + (c->cur.peer ? sockunion2str( + &c->cur.peer->vc->remote.nbma, buf[1], + sizeof(buf[1])) + : "(no peer)"), + sockunion2str(&c->cur.remote_nbma_natoa, buf[2], + sizeof(buf[2]))); + + if (c->cur.peer) + netlink_update_binding( + c->cur.peer->ifp, &c->remote_addr, + &c->cur.remote_nbma_natoa); + } + nhrp_cache_update_route(c); notifier_call(&c->notifier_list, NOTIFY_CACHE_BINDING_CHANGE); } else { @@ -273,6 +335,8 @@ int nhrp_cache_update_binding(struct nhrp_cache *c, enum nhrp_cache_type type, int holding_time, struct nhrp_peer *p, uint32_t mtu, union sockunion *nbma_oa) { + char buf[2][SU_ADDRSTRLEN]; + if (c->cur.type > type || c->new.type > type) { nhrp_peer_unref(p); return 0; @@ -293,17 +357,31 @@ int nhrp_cache_update_binding(struct nhrp_cache *c, enum nhrp_cache_type type, break; } + sockunion2str(&c->cur.remote_nbma_natoa, buf[0], sizeof(buf[0])); + if (nbma_oa) + sockunion2str(nbma_oa, buf[1], sizeof(buf[1])); + nhrp_cache_reset_new(c); if (c->cur.type == type && c->cur.peer == p && c->cur.mtu == mtu) { + debugf(NHRP_DEBUG_COMMON, + "cache: same type %u, updating expiry and changing nbma addr from %s to %s", + type, buf[0], nbma_oa ? buf[1] : "(NULL)"); if (holding_time > 0) c->cur.expires = monotime(NULL) + holding_time; + if (nbma_oa) c->cur.remote_nbma_natoa = *nbma_oa; else memset(&c->cur.remote_nbma_natoa, 0, - sizeof c->cur.remote_nbma_natoa); + sizeof(c->cur.remote_nbma_natoa)); + nhrp_peer_unref(p); } else { + debugf(NHRP_DEBUG_COMMON, + "cache: new type %u/%u, or peer %s, or mtu %u/%u, nbma %s --> %s (map %d)", + c->cur.type, type, (c->cur.peer == p) ? "same" : "diff", + c->cur.mtu, mtu, buf[0], nbma_oa ? buf[1] : "(NULL)", + c->map); c->new.type = type; c->new.peer = p; c->new.mtu = mtu; diff --git a/nhrpd/nhrp_errors.c b/nhrpd/nhrp_errors.c index 4c4f55be9e..741e64d8b3 100644 --- a/nhrpd/nhrp_errors.c +++ b/nhrpd/nhrp_errors.c @@ -31,12 +31,6 @@ static struct log_ref ferr_nhrp_err[] = { .description = "NHRP has detected a error with the Strongswan code", .suggestion = "Ensure that StrongSwan is configured correctly. Restart StrongSwan and FRR" }, - { - .code = EC_NHRP_RESOLVER, - .title = "NHRP DNS Resolution", - .description = "NHRP has detected an error in an attempt to resolve a hostname", - .suggestion = "Ensure that DNS is working properly and the hostname is configured in dns. If you are still seeing this error, open an issue" - }, { .code = END_FERR, } diff --git a/nhrpd/nhrp_errors.h b/nhrpd/nhrp_errors.h index 593714786a..d4958358fe 100644 --- a/nhrpd/nhrp_errors.h +++ b/nhrpd/nhrp_errors.h @@ -25,7 +25,6 @@ enum nhrp_log_refs { EC_NHRP_SWAN = NHRP_FERR_START, - EC_NHRP_RESOLVER, }; extern void nhrp_error_init(void); diff --git a/nhrpd/nhrp_event.c b/nhrpd/nhrp_event.c index 9301c2d515..40efeb5795 100644 --- a/nhrpd/nhrp_event.c +++ b/nhrpd/nhrp_event.c @@ -200,7 +200,7 @@ static int evmgr_reconnect(struct thread *t) fd = sock_open_unix(nhrp_event_socket_path); if (fd < 0) { zlog_warn("%s: failure connecting nhrp-event socket: %s", - __PRETTY_FUNCTION__, strerror(errno)); + __func__, strerror(errno)); zbufq_reset(&evmgr->obuf); thread_add_timer(master, evmgr_reconnect, evmgr, 10, &evmgr->t_reconnect); diff --git a/nhrpd/nhrp_interface.c b/nhrpd/nhrp_interface.c index b33eaa0478..1e576fc5ac 100644 --- a/nhrpd/nhrp_interface.c +++ b/nhrpd/nhrp_interface.c @@ -112,7 +112,7 @@ static void nhrp_interface_interface_notifier(struct notifier_block *n, NOTIFY_INTERFACE_NBMA_CHANGED); debugf(NHRP_DEBUG_IF, "%s: NBMA change: address %s", nifp->ifp->name, - sockunion2str(&nifp->nbma, buf, sizeof buf)); + sockunion2str(&nifp->nbma, buf, sizeof(buf))); break; } } @@ -135,7 +135,7 @@ static void nhrp_interface_update_nbma(struct interface *ifp) &nifp->linkidx, &saddr); debugf(NHRP_DEBUG_IF, "%s: GRE: %x %x %x", ifp->name, nifp->grekey, nifp->linkidx, saddr.s_addr); - if (saddr.s_addr) + if (saddr.s_addr != INADDR_ANY) sockunion_set(&nbma, AF_INET, (uint8_t *)&saddr.s_addr, sizeof(saddr.s_addr)); else if (!nbmaifp && nifp->linkidx != IFINDEX_INTERNAL) @@ -221,7 +221,7 @@ static void nhrp_interface_update_address(struct interface *ifp, afi_t afi, if (best && if_ad->configured && best->address->prefixlen != 8 * prefix_blen(best->address)) { zlog_notice("%s: %s is not a host prefix", ifp->name, - prefix2str(best->address, buf, sizeof buf)); + prefix2str(best->address, buf, sizeof(buf))); best = NULL; } @@ -243,7 +243,7 @@ static void nhrp_interface_update_address(struct interface *ifp, afi_t afi, debugf(NHRP_DEBUG_KERNEL, "%s: IPv%d address changed to %s", ifp->name, afi == AFI_IP ? 4 : 6, - best ? prefix2str(best->address, buf, sizeof buf) : "(none)"); + best ? prefix2str(best->address, buf, sizeof(buf)) : "(none)"); if_ad->addr = addr; if (if_ad->configured && sockunion_family(&if_ad->addr) != AF_UNSPEC) { @@ -296,16 +296,8 @@ void nhrp_interface_update(struct interface *ifp) } } -int nhrp_interface_add(int cmd, struct zclient *client, zebra_size_t length, - vrf_id_t vrf_id) +int nhrp_ifp_create(struct interface *ifp) { - struct interface *ifp; - - /* read and add the interface in the iflist. */ - ifp = zebra_interface_add_read(client->ibuf, vrf_id); - if (ifp == NULL) - return 0; - debugf(NHRP_DEBUG_IF, "if-add: %s, ifindex: %u, hw_type: %d %s", ifp->name, ifp->ifindex, ifp->ll_type, if_link_type_str(ifp->ll_type)); @@ -315,67 +307,42 @@ int nhrp_interface_add(int cmd, struct zclient *client, zebra_size_t length, return 0; } -int nhrp_interface_delete(int cmd, struct zclient *client, zebra_size_t length, - vrf_id_t vrf_id) +int nhrp_ifp_destroy(struct interface *ifp) { - struct interface *ifp; - struct stream *s; - - s = client->ibuf; - ifp = zebra_interface_state_read(s, vrf_id); - if (ifp == NULL) - return 0; - debugf(NHRP_DEBUG_IF, "if-delete: %s", ifp->name); nhrp_interface_update(ifp); - if_set_index(ifp, IFINDEX_INTERNAL); - return 0; } -int nhrp_interface_up(int cmd, struct zclient *client, zebra_size_t length, - vrf_id_t vrf_id) +int nhrp_ifp_up(struct interface *ifp) { - struct interface *ifp; - - ifp = zebra_interface_state_read(client->ibuf, vrf_id); - if (ifp == NULL) - return 0; - debugf(NHRP_DEBUG_IF, "if-up: %s", ifp->name); nhrp_interface_update_nbma(ifp); return 0; } -int nhrp_interface_down(int cmd, struct zclient *client, zebra_size_t length, - vrf_id_t vrf_id) +int nhrp_ifp_down(struct interface *ifp) { - struct interface *ifp; - - ifp = zebra_interface_state_read(client->ibuf, vrf_id); - if (ifp == NULL) - return 0; - debugf(NHRP_DEBUG_IF, "if-down: %s", ifp->name); nhrp_interface_update(ifp); + return 0; } -int nhrp_interface_address_add(int cmd, struct zclient *client, - zebra_size_t length, vrf_id_t vrf_id) +int nhrp_interface_address_add(ZAPI_CALLBACK_ARGS) { struct connected *ifc; char buf[PREFIX_STRLEN]; - ifc = zebra_interface_address_read(cmd, client->ibuf, vrf_id); + ifc = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); if (ifc == NULL) return 0; debugf(NHRP_DEBUG_IF, "if-addr-add: %s: %s", ifc->ifp->name, - prefix2str(ifc->address, buf, sizeof buf)); + prefix2str(ifc->address, buf, sizeof(buf))); nhrp_interface_update_address( ifc->ifp, family2afi(PREFIX_FAMILY(ifc->address)), 0); @@ -383,22 +350,21 @@ int nhrp_interface_address_add(int cmd, struct zclient *client, return 0; } -int nhrp_interface_address_delete(int cmd, struct zclient *client, - zebra_size_t length, vrf_id_t vrf_id) +int nhrp_interface_address_delete(ZAPI_CALLBACK_ARGS) { struct connected *ifc; char buf[PREFIX_STRLEN]; - ifc = zebra_interface_address_read(cmd, client->ibuf, vrf_id); + ifc = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); if (ifc == NULL) return 0; debugf(NHRP_DEBUG_IF, "if-addr-del: %s: %s", ifc->ifp->name, - prefix2str(ifc->address, buf, sizeof buf)); + prefix2str(ifc->address, buf, sizeof(buf))); nhrp_interface_update_address( ifc->ifp, family2afi(PREFIX_FAMILY(ifc->address)), 0); - connected_free(ifc); + connected_free(&ifc); return 0; } diff --git a/nhrpd/nhrp_main.c b/nhrpd/nhrp_main.c index d7c485f0a0..ba4bab00b7 100644 --- a/nhrpd/nhrp_main.c +++ b/nhrpd/nhrp_main.c @@ -21,9 +21,9 @@ #include "version.h" #include "log.h" #include "memory.h" -#include "memory_vty.h" #include "command.h" #include "libfrr.h" +#include "filter.h" #include "nhrpd.h" #include "netlink.h" @@ -116,7 +116,8 @@ static struct quagga_signal_t sighandlers[] = { }, }; -static const struct frr_yang_module_info *nhrpd_yang_modules[] = { +static const struct frr_yang_module_info *const nhrpd_yang_modules[] = { + &frr_filter_info, &frr_interface_info, }; @@ -141,10 +142,11 @@ int main(int argc, char **argv) nhrp_error_init(); vrf_init(NULL, NULL, NULL, NULL, NULL); nhrp_interface_init(); - resolver_init(); + resolver_init(master); /* Run with elevated capabilities, as for all netlink activity * we need privileges anyway. */ + assert(nhrpd_privs.change); nhrpd_privs.change(ZPRIVS_RAISE); netlink_init(); @@ -152,6 +154,8 @@ int main(int argc, char **argv) nhrp_vc_init(); nhrp_packet_init(); vici_init(); + if_zapi_callbacks(nhrp_ifp_create, nhrp_ifp_up, + nhrp_ifp_down, nhrp_ifp_destroy); nhrp_zebra_init(); nhrp_shortcut_init(); diff --git a/nhrpd/nhrp_nhs.c b/nhrpd/nhrp_nhs.c index 360972c327..8509cedcee 100644 --- a/nhrpd/nhrp_nhs.c +++ b/nhrpd/nhrp_nhs.c @@ -136,7 +136,7 @@ static void nhrp_reg_peer_notify(struct notifier_block *n, unsigned long cmd) case NOTIFY_PEER_MTU_CHANGED: debugf(NHRP_DEBUG_COMMON, "NHS: Flush timer for %s", sockunion2str(&r->peer->vc->remote.nbma, buf, - sizeof buf)); + sizeof(buf))); THREAD_TIMER_OFF(r->t_register); thread_add_timer_msec(master, nhrp_reg_send_req, r, 10, &r->t_register); @@ -162,7 +162,7 @@ static int nhrp_reg_send_req(struct thread *t) if (!nhrp_peer_check(r->peer, 2)) { debugf(NHRP_DEBUG_COMMON, "NHS: Waiting link for %s", sockunion2str(&r->peer->vc->remote.nbma, buf1, - sizeof buf1)); + sizeof(buf1))); thread_add_timer(master, nhrp_reg_send_req, r, 120, &r->t_register); return 0; @@ -238,8 +238,8 @@ nhrp_reg_by_nbma(struct nhrp_nhs *nhs, const union sockunion *nbma_addr) return NULL; } -static void nhrp_nhs_resolve_cb(struct resolver_query *q, int n, - union sockunion *addrs) +static void nhrp_nhs_resolve_cb(struct resolver_query *q, const char *errstr, + int n, union sockunion *addrs) { struct nhrp_nhs *nhs = container_of(q, struct nhrp_nhs, dns_resolve); struct nhrp_interface *nifp = nhs->ifp->info; diff --git a/nhrpd/nhrp_peer.c b/nhrpd/nhrp_peer.c index db2f72ac22..2dc019ce65 100644 --- a/nhrpd/nhrp_peer.c +++ b/nhrpd/nhrp_peer.c @@ -151,9 +151,9 @@ static void nhrp_peer_ifp_notify(struct notifier_block *n, unsigned long cmd) nhrp_peer_unref(p); } -static unsigned int nhrp_peer_key(void *peer_data) +static unsigned int nhrp_peer_key(const void *peer_data) { - struct nhrp_peer *p = peer_data; + const struct nhrp_peer *p = peer_data; return sockunion_hash(&p->vc->remote.nbma); } @@ -305,8 +305,8 @@ void nhrp_peer_send(struct nhrp_peer *p, struct zbuf *zb) return; debugf(NHRP_DEBUG_KERNEL, "PACKET: Send %s -> %s", - sockunion2str(&p->vc->local.nbma, buf[0], sizeof buf[0]), - sockunion2str(&p->vc->remote.nbma, buf[1], sizeof buf[1])); + sockunion2str(&p->vc->local.nbma, buf[0], sizeof(buf[0])), + sockunion2str(&p->vc->remote.nbma, buf[1], sizeof(buf[1]))); os_sendmsg(zb->head, zbuf_used(zb), p->ifp->ifindex, sockunion_get_addr(&p->vc->remote.nbma), @@ -314,23 +314,29 @@ void nhrp_peer_send(struct nhrp_peer *p, struct zbuf *zb) zbuf_reset(zb); } -static void nhrp_handle_resolution_req(struct nhrp_packet_parser *p) +static void nhrp_handle_resolution_req(struct nhrp_packet_parser *pp) { + struct interface *ifp = pp->ifp; struct zbuf *zb, payload; struct nhrp_packet_header *hdr; struct nhrp_cie_header *cie; struct nhrp_extension_header *ext; - struct nhrp_interface *nifp; + struct nhrp_cache *c; + union sockunion cie_nbma, cie_proto, *proto_addr, *nbma_addr; + int holdtime, prefix_len, hostprefix_len; + struct nhrp_interface *nifp = ifp->info; struct nhrp_peer *peer; + size_t paylen; + char buf[SU_ADDRSTRLEN]; - if (!(p->if_ad->flags & NHRP_IFF_SHORTCUT)) { + if (!(pp->if_ad->flags & NHRP_IFF_SHORTCUT)) { debugf(NHRP_DEBUG_COMMON, "Shortcuts disabled"); /* FIXME: Send error indication? */ return; } - if (p->if_ad->network_id && p->route_type == NHRP_ROUTE_OFF_NBMA - && p->route_prefix.prefixlen < 8) { + if (pp->if_ad->network_id && pp->route_type == NHRP_ROUTE_OFF_NBMA + && pp->route_prefix.prefixlen < 8) { debugf(NHRP_DEBUG_COMMON, "Shortcut to more generic than /8 dropped"); return; @@ -338,45 +344,101 @@ static void nhrp_handle_resolution_req(struct nhrp_packet_parser *p) debugf(NHRP_DEBUG_COMMON, "Parsing and replying to Resolution Req"); - if (nhrp_route_address(p->ifp, &p->src_proto, NULL, &peer) + if (nhrp_route_address(ifp, &pp->src_proto, NULL, &peer) != NHRP_ROUTE_NBMA_NEXTHOP) return; -#if 0 - /* FIXME: Update requestors binding if CIE specifies holding time */ - nhrp_cache_update_binding( - NHRP_CACHE_CACHED, &p->src_proto, - nhrp_peer_get(p->ifp, &p->src_nbma), - htons(cie->holding_time)); -#endif + /* Copy payload CIE */ + hostprefix_len = 8 * sockunion_get_addrlen(&pp->if_ad->addr); + paylen = zbuf_used(&pp->payload); + debugf(NHRP_DEBUG_COMMON, "shortcut res_rep: paylen %zu", paylen); + + while ((cie = nhrp_cie_pull(&pp->payload, pp->hdr, &cie_nbma, + &cie_proto)) + != NULL) { + prefix_len = cie->prefix_length; + debugf(NHRP_DEBUG_COMMON, + "shortcut res_rep: parsing CIE with prefixlen=%u", + prefix_len); + if (prefix_len == 0 || prefix_len >= hostprefix_len) + prefix_len = hostprefix_len; + + if (prefix_len != hostprefix_len + && !(pp->hdr->flags + & htons(NHRP_FLAG_REGISTRATION_UNIQUE))) { + cie->code = NHRP_CODE_BINDING_NON_UNIQUE; + continue; + } + + /* We currently support only unique prefix registrations */ + if (prefix_len != hostprefix_len) { + cie->code = NHRP_CODE_ADMINISTRATIVELY_PROHIBITED; + continue; + } + + proto_addr = (sockunion_family(&cie_proto) == AF_UNSPEC) + ? &pp->src_proto + : &cie_proto; + nbma_addr = (sockunion_family(&cie_nbma) == AF_UNSPEC) + ? &pp->src_nbma + : &cie_nbma; + + holdtime = htons(cie->holding_time); + debugf(NHRP_DEBUG_COMMON, + "shortcut res_rep: holdtime is %u (if 0, using %u)", + holdtime, pp->if_ad->holdtime); + if (!holdtime) + holdtime = pp->if_ad->holdtime; + + c = nhrp_cache_get(ifp, proto_addr, 1); + if (!c) { + debugf(NHRP_DEBUG_COMMON, + "shortcut res_rep: no cache found"); + cie->code = NHRP_CODE_INSUFFICIENT_RESOURCES; + continue; + } + if (nbma_addr) + sockunion2str(nbma_addr, buf, sizeof(buf)); + + debugf(NHRP_DEBUG_COMMON, + "shortcut res_rep: updating binding for nmba addr %s", + nbma_addr ? buf : "(NULL)"); + if (!nhrp_cache_update_binding(c, NHRP_CACHE_DYNAMIC, holdtime, + nhrp_peer_ref(pp->peer), + htons(cie->mtu), nbma_addr)) { + cie->code = NHRP_CODE_ADMINISTRATIVELY_PROHIBITED; + continue; + } - nifp = peer->ifp->info; + cie->code = NHRP_CODE_SUCCESS; + } /* Create reply */ zb = zbuf_alloc(1500); - hdr = nhrp_packet_push(zb, NHRP_PACKET_RESOLUTION_REPLY, &p->src_nbma, - &p->src_proto, &p->dst_proto); + hdr = nhrp_packet_push(zb, NHRP_PACKET_RESOLUTION_REPLY, &pp->src_nbma, + &pp->src_proto, &pp->dst_proto); /* Copied information from request */ - hdr->flags = - p->hdr->flags & htons(NHRP_FLAG_RESOLUTION_SOURCE_IS_ROUTER - | NHRP_FLAG_RESOLUTION_SOURCE_STABLE); + hdr->flags = pp->hdr->flags + & htons(NHRP_FLAG_RESOLUTION_SOURCE_IS_ROUTER + | NHRP_FLAG_RESOLUTION_SOURCE_STABLE); hdr->flags |= htons(NHRP_FLAG_RESOLUTION_DESTINATION_STABLE | NHRP_FLAG_RESOLUTION_AUTHORATIVE); - hdr->u.request_id = p->hdr->u.request_id; + hdr->u.request_id = pp->hdr->u.request_id; - /* CIE payload */ + /* CIE payload for the reply packet */ cie = nhrp_cie_push(zb, NHRP_CODE_SUCCESS, &nifp->nbma, - &p->if_ad->addr); - cie->holding_time = htons(p->if_ad->holdtime); - cie->mtu = htons(p->if_ad->mtu); - if (p->if_ad->network_id && p->route_type == NHRP_ROUTE_OFF_NBMA) - cie->prefix_length = p->route_prefix.prefixlen; + &pp->if_ad->addr); + cie->holding_time = htons(pp->if_ad->holdtime); + cie->mtu = htons(pp->if_ad->mtu); + if (pp->if_ad->network_id && pp->route_type == NHRP_ROUTE_OFF_NBMA) + cie->prefix_length = pp->route_prefix.prefixlen; else - cie->prefix_length = 8 * sockunion_get_addrlen(&p->if_ad->addr); + cie->prefix_length = + 8 * sockunion_get_addrlen(&pp->if_ad->addr); /* Handle extensions */ - while ((ext = nhrp_ext_pull(&p->extensions, &payload)) != NULL) { + while ((ext = nhrp_ext_pull(&pp->extensions, &payload)) != NULL) { switch (htons(ext->type) & ~NHRP_EXTENSION_FLAG_COMPULSORY) { case NHRP_EXTENSION_NAT_ADDRESS: if (sockunion_family(&nifp->nat_nbma) == AF_UNSPEC) @@ -386,13 +448,13 @@ static void nhrp_handle_resolution_req(struct nhrp_packet_parser *p) if (!ext) goto err; cie = nhrp_cie_push(zb, NHRP_CODE_SUCCESS, - &nifp->nat_nbma, &p->if_ad->addr); + &nifp->nat_nbma, &pp->if_ad->addr); if (!cie) goto err; nhrp_ext_complete(zb, ext); break; default: - if (nhrp_ext_reply(zb, hdr, p->ifp, ext, &payload) < 0) + if (nhrp_ext_reply(zb, hdr, ifp, ext, &payload) < 0) goto err; break; } @@ -584,15 +646,15 @@ void nhrp_peer_send_indication(struct interface *ifp, uint16_t protocol_type, debugf(NHRP_DEBUG_COMMON, "Send Traffic Indication to %s about packet to %s ignored", sockunion2str(&p->vc->remote.nbma, buf[0], - sizeof buf[0]), - sockunion2str(&dst, buf[1], sizeof buf[1])); + sizeof(buf[0])), + sockunion2str(&dst, buf[1], sizeof(buf[1]))); return; } debugf(NHRP_DEBUG_COMMON, "Send Traffic Indication to %s (online=%d) about packet to %s", - sockunion2str(&p->vc->remote.nbma, buf[0], sizeof buf[0]), - p->online, sockunion2str(&dst, buf[1], sizeof buf[1])); + sockunion2str(&p->vc->remote.nbma, buf[0], sizeof(buf[0])), + p->online, sockunion2str(&dst, buf[1], sizeof(buf[1]))); /* Create reply */ zb = zbuf_alloc(1500); @@ -622,8 +684,8 @@ static void nhrp_handle_error_ind(struct nhrp_packet_parser *pp) debugf(NHRP_DEBUG_COMMON, "Error Indication from %s about packet to %s ignored", - sockunion2str(&pp->src_proto, buf[0], sizeof buf[0]), - sockunion2str(&dst_proto, buf[1], sizeof buf[1])); + sockunion2str(&pp->src_proto, buf[0], sizeof(buf[0])), + sockunion2str(&dst_proto, buf[1], sizeof(buf[1]))); reqid = nhrp_reqid_lookup(&nhrp_packet_reqid, htonl(hdr->u.request_id)); if (reqid) @@ -641,8 +703,8 @@ static void nhrp_handle_traffic_ind(struct nhrp_packet_parser *p) debugf(NHRP_DEBUG_COMMON, "Traffic Indication from %s about packet to %s: %s", - sockunion2str(&p->src_proto, buf[0], sizeof buf[0]), - sockunion2str(&dst, buf[1], sizeof buf[1]), + sockunion2str(&p->src_proto, buf[0], sizeof(buf[0])), + sockunion2str(&dst, buf[1], sizeof(buf[1])), (p->if_ad->flags & NHRP_IFF_SHORTCUT) ? "trying shortcut" : "ignored"); @@ -755,10 +817,9 @@ static void nhrp_peer_forward(struct nhrp_peer *p, if ((type == NHRP_EXTENSION_REVERSE_TRANSIT_NHS) == (packet_types[hdr->type].type == PACKET_REPLY)) { /* Check NHS list for forwarding loop */ - while ((cie = nhrp_cie_pull(&extpl, pp->hdr, - &cie_nbma, - &cie_protocol)) - != NULL) { + while (nhrp_cie_pull(&extpl, pp->hdr, + &cie_nbma, + &cie_protocol) != NULL) { if (sockunion_same(&p->vc->remote.nbma, &cie_nbma)) goto err; @@ -810,8 +871,8 @@ static void nhrp_packet_debug(struct zbuf *zb, const char *dir) zbuf_init(&zhdr, zb->buf, zb->tail - zb->buf, zb->tail - zb->buf); hdr = nhrp_packet_pull(&zhdr, &src_nbma, &src_proto, &dst_proto); - sockunion2str(&src_proto, buf[0], sizeof buf[0]); - sockunion2str(&dst_proto, buf[1], sizeof buf[1]); + sockunion2str(&src_proto, buf[0], sizeof(buf[0])); + sockunion2str(&dst_proto, buf[1], sizeof(buf[1])); reply = packet_types[hdr->type].type == PACKET_REPLY; debugf(NHRP_DEBUG_COMMON, "%s %s(%d) %s -> %s", dir, @@ -853,8 +914,8 @@ void nhrp_peer_recv(struct nhrp_peer *p, struct zbuf *zb) afi_t nbma_afi, proto_afi; debugf(NHRP_DEBUG_KERNEL, "PACKET: Recv %s -> %s", - sockunion2str(&vc->remote.nbma, buf[0], sizeof buf[0]), - sockunion2str(&vc->local.nbma, buf[1], sizeof buf[1])); + sockunion2str(&vc->remote.nbma, buf[0], sizeof(buf[0])), + sockunion2str(&vc->local.nbma, buf[1], sizeof(buf[1]))); if (!p->online) { info = "peer not online"; @@ -886,7 +947,7 @@ void nhrp_peer_recv(struct nhrp_peer *p, struct zbuf *zb) || htons(hdr->packet_size) > realsize) { zlog_info( "From %s: error: packet type %d, version %d, AFI %d, proto %x, size %d (real size %d)", - sockunion2str(&vc->remote.nbma, buf[0], sizeof buf[0]), + sockunion2str(&vc->remote.nbma, buf[0], sizeof(buf[0])), (int)hdr->type, (int)hdr->version, (int)nbma_afi, (int)htons(hdr->protocol_type), (int)htons(hdr->packet_size), (int)realsize); @@ -896,11 +957,17 @@ void nhrp_peer_recv(struct nhrp_peer *p, struct zbuf *zb) extoff = htons(hdr->extension_offset); if (extoff) { + assert(zb->head > zb->buf); + uint32_t header_offset = zb->head - zb->buf; if (extoff >= realsize) { info = "extoff larger than packet"; goto drop; } - paylen = extoff - (zb->head - zb->buf); + if (extoff < header_offset) { + info = "extoff smaller than header offset"; + goto drop; + } + paylen = extoff - header_offset; } else { paylen = zbuf_used(zb); } @@ -961,7 +1028,7 @@ void nhrp_peer_recv(struct nhrp_peer *p, struct zbuf *zb) if (info) { zlog_info( "From %s: error: %s", - sockunion2str(&vc->remote.nbma, buf[0], sizeof buf[0]), + sockunion2str(&vc->remote.nbma, buf[0], sizeof(buf[0])), info); } if (peer) diff --git a/nhrpd/nhrp_route.c b/nhrpd/nhrp_route.c index dae00bbcea..3ecc441124 100644 --- a/nhrpd/nhrp_route.c +++ b/nhrpd/nhrp_route.c @@ -58,7 +58,6 @@ static void nhrp_route_update_put(struct route_node *rn) if (!ri->ifp && !ri->nhrp_ifp && sockunion_family(&ri->via) == AF_UNSPEC) { XFREE(MTYPE_NHRP_ROUTE, rn->info); - rn->info = NULL; route_unlock_node(rn); } route_unlock_node(rn); @@ -100,6 +99,7 @@ void nhrp_route_announce(int add, enum nhrp_cache_type type, { struct zapi_route api; struct zapi_nexthop *api_nh; + union sockunion *nexthop_ref = (union sockunion *)nexthop; if (zclient->sock < 0) return; @@ -135,8 +135,14 @@ void nhrp_route_announce(int add, enum nhrp_cache_type type, switch (api.prefix.family) { case AF_INET: - if (nexthop) { - api_nh->gate.ipv4 = nexthop->sin.sin_addr; + if (api.prefix.prefixlen == IPV4_MAX_BITLEN && + nexthop_ref && + memcmp(&nexthop_ref->sin.sin_addr, &api.prefix.u.prefix4, + sizeof(struct in_addr)) == 0) { + nexthop_ref = NULL; + } + if (nexthop_ref) { + api_nh->gate.ipv4 = nexthop_ref->sin.sin_addr; api_nh->type = NEXTHOP_TYPE_IPV4; } if (ifp) { @@ -148,8 +154,14 @@ void nhrp_route_announce(int add, enum nhrp_cache_type type, } break; case AF_INET6: - if (nexthop) { - api_nh->gate.ipv6 = nexthop->sin6.sin6_addr; + if (api.prefix.prefixlen == IPV6_MAX_BITLEN && + nexthop_ref && + memcmp(&nexthop_ref->sin6.sin6_addr, &api.prefix.u.prefix6, + sizeof(struct in6_addr)) == 0) { + nexthop_ref = NULL; + } + if (nexthop_ref) { + api_nh->gate.ipv6 = nexthop_ref->sin6.sin6_addr; api_nh->type = NEXTHOP_TYPE_IPV6; } if (ifp) { @@ -171,12 +183,12 @@ void nhrp_route_announce(int add, enum nhrp_cache_type type, prefix2str(&api.prefix, buf[0], sizeof(buf[0])); zlog_debug( - "Zebra send: route %s %s nexthop %s metric %u" - " count %d dev %s", + "Zebra send: route %s %s nexthop %s metric %u count %d dev %s", add ? "add" : "del", buf[0], - nexthop ? inet_ntop(api.prefix.family, &api_nh->gate, - buf[1], sizeof(buf[1])) - : "", + nexthop_ref ? inet_ntop(api.prefix.family, + &api_nh->gate, + buf[1], sizeof(buf[1])) + : "", api.metric, api.nexthop_num, ifp ? ifp->name : "none"); } @@ -184,8 +196,7 @@ void nhrp_route_announce(int add, enum nhrp_cache_type type, &api); } -int nhrp_route_read(int cmd, struct zclient *zclient, zebra_size_t length, - vrf_id_t vrf_id) +int nhrp_route_read(ZAPI_CALLBACK_ARGS) { struct zapi_route api; struct zapi_nexthop *api_nh; @@ -201,6 +212,10 @@ int nhrp_route_read(int cmd, struct zclient *zclient, zebra_size_t length, if (CHECK_FLAG(api.message, ZAPI_MESSAGE_SRCPFX)) return 0; + /* ignore our routes */ + if (api.type == ZEBRA_ROUTE_NHRP) + return 0; + sockunion_family(&nexthop_addr) = AF_UNSPEC; if (CHECK_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP)) { api_nh = &api.nexthops[0]; @@ -222,8 +237,8 @@ int nhrp_route_read(int cmd, struct zclient *zclient, zebra_size_t length, added = (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD); debugf(NHRP_DEBUG_ROUTE, "if-route-%s: %s via %s dev %s", added ? "add" : "del", - prefix2str(&api.prefix, buf[0], sizeof buf[0]), - sockunion2str(&nexthop_addr, buf[1], sizeof buf[1]), + prefix2str(&api.prefix, buf[0], sizeof(buf[0])), + sockunion2str(&nexthop_addr, buf[1], sizeof(buf[1])), ifp ? ifp->name : "(none)"); nhrp_route_update_zebra(&api.prefix, &nexthop_addr, ifp); @@ -250,7 +265,7 @@ int nhrp_route_get_nexthop(const union sockunion *addr, struct prefix *p, ri = rn->info; if (ri->nhrp_ifp) { debugf(NHRP_DEBUG_ROUTE, "lookup %s: nhrp_if=%s", - prefix2str(&lookup, buf, sizeof buf), + prefix2str(&lookup, buf, sizeof(buf)), ri->nhrp_ifp->name); if (via) @@ -259,7 +274,7 @@ int nhrp_route_get_nexthop(const union sockunion *addr, struct prefix *p, *ifp = ri->nhrp_ifp; } else { debugf(NHRP_DEBUG_ROUTE, "lookup %s: zebra route dev %s", - prefix2str(&lookup, buf, sizeof buf), + prefix2str(&lookup, buf, sizeof(buf)), ri->ifp ? ri->ifp->name : "(none)"); if (via) @@ -346,10 +361,6 @@ void nhrp_zebra_init(void) zclient = zclient_new(master, &zclient_options_default); zclient->zebra_connected = nhrp_zebra_connected; - zclient->interface_add = nhrp_interface_add; - zclient->interface_delete = nhrp_interface_delete; - zclient->interface_up = nhrp_interface_up; - zclient->interface_down = nhrp_interface_down; zclient->interface_address_add = nhrp_interface_address_add; zclient->interface_address_delete = nhrp_interface_address_delete; zclient->redistribute_route_add = nhrp_route_read; @@ -358,10 +369,22 @@ void nhrp_zebra_init(void) zclient_init(zclient, ZEBRA_ROUTE_NHRP, 0, &nhrpd_privs); } +static void nhrp_table_node_cleanup(struct route_table *table, + struct route_node *node) +{ + if (!node->info) + return; + + XFREE(MTYPE_NHRP_ROUTE, node->info); +} + void nhrp_zebra_terminate(void) { zclient_stop(zclient); zclient_free(zclient); + + zebra_rib[AFI_IP]->cleanup = nhrp_table_node_cleanup; + zebra_rib[AFI_IP6]->cleanup = nhrp_table_node_cleanup; route_table_finish(zebra_rib[AFI_IP]); route_table_finish(zebra_rib[AFI_IP6]); } diff --git a/nhrpd/nhrp_shortcut.c b/nhrpd/nhrp_shortcut.c index 84053b4b5d..b4abecb4e0 100644 --- a/nhrpd/nhrp_shortcut.c +++ b/nhrpd/nhrp_shortcut.c @@ -32,7 +32,7 @@ static void nhrp_shortcut_check_use(struct nhrp_shortcut *s) if (s->expiring && s->cache && s->cache->used) { debugf(NHRP_DEBUG_ROUTE, "Shortcut %s used and expiring", - prefix2str(s->p, buf, sizeof buf)); + prefix2str(s->p, buf, sizeof(buf))); nhrp_shortcut_send_resolution_req(s); } } @@ -53,14 +53,27 @@ static int nhrp_shortcut_do_expire(struct thread *t) static void nhrp_shortcut_cache_notify(struct notifier_block *n, unsigned long cmd) { + char buf[PREFIX_STRLEN]; + char buf2[PREFIX_STRLEN]; + struct nhrp_shortcut *s = container_of(n, struct nhrp_shortcut, cache_notifier); + struct nhrp_cache *c = s->cache; + if (c) + sockunion2str(&c->remote_addr, buf2, sizeof(buf2)); + else + snprintf(buf2, sizeof(buf2), "(unspec)"); switch (cmd) { case NOTIFY_CACHE_UP: if (!s->route_installed) { - nhrp_route_announce(1, s->type, s->p, NULL, - &s->cache->remote_addr, 0); + debugf(NHRP_DEBUG_ROUTE, + "Shortcut: route install %s nh %s dev %s", + prefix2str(s->p, buf, sizeof(buf)), buf2, + c && c->ifp ? c->ifp->name : ""); + + nhrp_route_announce(1, s->type, s->p, c ? c->ifp : NULL, + c ? &c->remote_addr : NULL, 0); s->route_installed = 1; } break; @@ -84,6 +97,8 @@ static void nhrp_shortcut_update_binding(struct nhrp_shortcut *s, enum nhrp_cache_type type, struct nhrp_cache *c, int holding_time) { + char buf[2][PREFIX_STRLEN]; + s->type = type; if (c != s->cache) { if (s->cache) { @@ -98,13 +113,29 @@ static void nhrp_shortcut_update_binding(struct nhrp_shortcut *s, /* Force renewal of Zebra announce on prefix * change */ s->route_installed = 0; + debugf(NHRP_DEBUG_ROUTE, + "Shortcut: forcing renewal of zebra announce on prefix change peer %s ht %u cur nbma %s dev %s", + sockunion2str(&s->cache->remote_addr, + buf[0], sizeof(buf[0])), + holding_time, + sockunion2str( + &s->cache->cur.remote_nbma_natoa, + buf[1], sizeof(buf[1])), + s->cache->ifp->name); nhrp_shortcut_cache_notify(&s->cache_notifier, NOTIFY_CACHE_UP); } } - if (!s->cache || !s->cache->route_installed) + if (!s->cache || !s->cache->route_installed) { + debugf(NHRP_DEBUG_ROUTE, + "Shortcut: notify cache down because cache?%s or ri?%s", + s->cache ? "yes" : "no", + s->cache ? (s->cache->route_installed ? "yes" + : "no") + : "n/a"); nhrp_shortcut_cache_notify(&s->cache_notifier, NOTIFY_CACHE_DOWN); + } } if (s->type == NHRP_CACHE_NEGATIVE && !s->route_installed) { nhrp_route_announce(1, s->type, s->p, NULL, NULL, 0); @@ -133,7 +164,7 @@ static void nhrp_shortcut_delete(struct nhrp_shortcut *s) nhrp_reqid_free(&nhrp_packet_reqid, &s->reqid); debugf(NHRP_DEBUG_ROUTE, "Shortcut %s purged", - prefix2str(s->p, buf, sizeof buf)); + prefix2str(s->p, buf, sizeof(buf))); nhrp_shortcut_update_binding(s, NHRP_CACHE_INVALID, NULL, 0); @@ -173,7 +204,7 @@ static struct nhrp_shortcut *nhrp_shortcut_get(struct prefix *p) s->p = &rn->p; debugf(NHRP_DEBUG_ROUTE, "Shortcut %s created", - prefix2str(s->p, buf, sizeof buf)); + prefix2str(s->p, buf, sizeof(buf))); } else { s = rn->info; route_unlock_node(rn); @@ -191,11 +222,10 @@ static void nhrp_shortcut_recv_resolution_rep(struct nhrp_reqid *reqid, struct nhrp_extension_header *ext; struct nhrp_cie_header *cie; struct nhrp_cache *c = NULL; - union sockunion *proto, cie_proto, *nbma, *nbma_natoa, cie_nbma, - nat_nbma; + union sockunion *proto, cie_proto, *nbma, cie_nbma, nat_nbma; struct prefix prefix, route_prefix; struct zbuf extpl; - char bufp[PREFIX_STRLEN], buf[3][SU_ADDRSTRLEN]; + char bufp[PREFIX_STRLEN], buf[4][SU_ADDRSTRLEN]; int holding_time = pp->if_ad->holdtime; nhrp_reqid_free(&nhrp_packet_reqid, &s->reqid); @@ -218,7 +248,7 @@ static void nhrp_shortcut_recv_resolution_rep(struct nhrp_reqid *reqid, } /* Parse extensions */ - memset(&nat_nbma, 0, sizeof nat_nbma); + memset(&nat_nbma, 0, sizeof(nat_nbma)); while ((ext = nhrp_ext_pull(&pp->extensions, &extpl)) != NULL) { switch (htons(ext->type) & ~NHRP_EXTENSION_FLAG_COMPULSORY) { case NHRP_EXTENSION_NAT_ADDRESS: @@ -232,8 +262,8 @@ static void nhrp_shortcut_recv_resolution_rep(struct nhrp_reqid *reqid, if (!sockunion_same(&cie_proto, &pp->dst_proto)) { debugf(NHRP_DEBUG_COMMON, "Shortcut: Warning dst_proto altered from %s to %s", - sockunion2str(&cie_proto, buf[0], sizeof buf[0]), - sockunion2str(&pp->dst_proto, buf[1], sizeof buf[1])); + sockunion2str(&cie_proto, buf[0], sizeof(buf[0])), + sockunion2str(&pp->dst_proto, buf[1], sizeof(buf[1]))); } /* One or more CIEs should be given as reply, we support only one */ @@ -263,39 +293,55 @@ static void nhrp_shortcut_recv_resolution_rep(struct nhrp_reqid *reqid, } debugf(NHRP_DEBUG_COMMON, - "Shortcut: %s is at proto %s cie-nbma %s nat-nbma %s cie-holdtime %d", - prefix2str(&prefix, bufp, sizeof bufp), - sockunion2str(proto, buf[0], sizeof buf[0]), - sockunion2str(&cie_nbma, buf[1], sizeof buf[1]), - sockunion2str(&nat_nbma, buf[2], sizeof buf[2]), + "Shortcut: %s is at proto %s dst_proto %s cie-nbma %s nat-nbma %s cie-holdtime %d", + prefix2str(&prefix, bufp, sizeof(bufp)), + sockunion2str(proto, buf[0], sizeof(buf[0])), + sockunion2str(&pp->dst_proto, buf[1], sizeof(buf[1])), + sockunion2str(&cie_nbma, buf[2], sizeof(buf[2])), + sockunion2str(&nat_nbma, buf[3], sizeof(buf[3])), htons(cie->holding_time)); /* Update cache entry for the protocol to nbma binding */ - if (sockunion_family(&nat_nbma) != AF_UNSPEC) { + if (sockunion_family(&nat_nbma) != AF_UNSPEC) nbma = &nat_nbma; - nbma_natoa = &cie_nbma; - } else { + else nbma = &cie_nbma; - nbma_natoa = NULL; - } + if (sockunion_family(nbma)) { c = nhrp_cache_get(pp->ifp, proto, 1); if (c) { - nhrp_cache_update_binding(c, NHRP_CACHE_CACHED, + debugf(NHRP_DEBUG_COMMON, + "Shortcut: cache found, update binding"); + nhrp_cache_update_binding(c, NHRP_CACHE_DYNAMIC, holding_time, nhrp_peer_get(pp->ifp, nbma), - htons(cie->mtu), nbma_natoa); + htons(cie->mtu), nbma); + } else { + debugf(NHRP_DEBUG_COMMON, + "Shortcut: no cache for nbma %s", buf[2]); } } /* Update shortcut entry for subnet to protocol gw binding */ - if (c && !sockunion_same(proto, &pp->dst_proto)) { + if (c) { ps = nhrp_shortcut_get(&prefix); if (ps) { ps->addr = s->addr; - nhrp_shortcut_update_binding(ps, NHRP_CACHE_CACHED, c, + debugf(NHRP_DEBUG_COMMON, + "Shortcut: calling update_binding"); + nhrp_shortcut_update_binding(ps, NHRP_CACHE_DYNAMIC, c, holding_time); + } else { + debugf(NHRP_DEBUG_COMMON, + "Shortcut: proto diff but no ps"); } + } else { + debugf(NHRP_DEBUG_COMMON, + "NO Shortcut because c NULL?%s or same proto?%s", + c ? "no" : "yes", + proto && pp && sockunion_same(proto, &pp->dst_proto) + ? "yes" + : "no"); } debugf(NHRP_DEBUG_COMMON, "Shortcut: Resolution reply handled"); @@ -307,7 +353,9 @@ static void nhrp_shortcut_send_resolution_req(struct nhrp_shortcut *s) struct nhrp_packet_header *hdr; struct interface *ifp; struct nhrp_interface *nifp; + struct nhrp_afi_data *if_ad; struct nhrp_peer *peer; + struct nhrp_cie_header *cie; if (nhrp_route_address(NULL, &s->addr, NULL, &peer) != NHRP_ROUTE_NBMA_NEXTHOP) @@ -337,7 +385,15 @@ static void nhrp_shortcut_send_resolution_req(struct nhrp_shortcut *s) * - MTU: MTU of the source station * - Holding Time: Max time to cache the source information * */ - /* FIXME: Send holding time, and MTU */ + /* FIXME: push CIE for each local protocol address */ + cie = nhrp_cie_push(zb, NHRP_CODE_SUCCESS, NULL, NULL); + cie->prefix_length = 0xff; + if_ad = &nifp->afi[family2afi(sockunion_family(&s->addr))]; + cie->holding_time = htons(if_ad->holdtime); + cie->mtu = htons(if_ad->mtu); + debugf(NHRP_DEBUG_COMMON, + "Shortcut res_req: set cie ht to %u and mtu to %u. shortcut ht is %u", + ntohs(cie->holding_time), ntohs(cie->mtu), s->holding_time); nhrp_ext_request(zb, hdr, ifp); diff --git a/nhrpd/nhrp_vc.c b/nhrpd/nhrp_vc.c index fa3549f5ed..6567b231a9 100644 --- a/nhrpd/nhrp_vc.c +++ b/nhrpd/nhrp_vc.c @@ -28,9 +28,9 @@ struct child_sa { static struct hash *nhrp_vc_hash; static struct list_head childlist_head[512]; -static unsigned int nhrp_vc_key(void *peer_data) +static unsigned int nhrp_vc_key(const void *peer_data) { - struct nhrp_vc *vc = peer_data; + const struct nhrp_vc *vc = peer_data; return jhash_2words(sockunion_hash(&vc->local.nbma), sockunion_hash(&vc->remote.nbma), 0); } @@ -142,8 +142,8 @@ int nhrp_vc_ipsec_updown(uint32_t child_id, struct nhrp_vc *vc) sa->vc->abort_migration = 0; debugf(NHRP_DEBUG_COMMON, "IPsec NBMA change of %s to %s", sockunion2str(&sa->vc->remote.nbma, buf[0], - sizeof buf[0]), - sockunion2str(&vc->remote.nbma, buf[1], sizeof buf[1])); + sizeof(buf[0])), + sockunion2str(&vc->remote.nbma, buf[1], sizeof(buf[1]))); nhrp_vc_update(sa->vc, NOTIFY_VC_IPSEC_UPDATE_NBMA); abort_migration = sa->vc->abort_migration; } diff --git a/nhrpd/nhrp_vty.c b/nhrpd/nhrp_vty.c index cfedc1c6b9..27b7bece48 100644 --- a/nhrpd/nhrp_vty.c +++ b/nhrpd/nhrp_vty.c @@ -12,20 +12,27 @@ #include "zclient.h" #include "stream.h" #include "filter.h" +#include "json.h" #include "nhrpd.h" #include "netlink.h" +static int nhrp_config_write(struct vty *vty); static struct cmd_node zebra_node = { + .name = "zebra", .node = ZEBRA_NODE, + .parent_node = CONFIG_NODE, .prompt = "%s(config-router)# ", - .vtysh = 1, + .config_write = nhrp_config_write, }; +static int interface_config_write(struct vty *vty); static struct cmd_node nhrp_interface_node = { + .name = "interface", .node = INTERFACE_NODE, + .parent_node = CONFIG_NODE, .prompt = "%s(config-if)# ", - .vtysh = 1, + .config_write = interface_config_write, }; #define NHRP_DEBUG_FLAGS_CMD "" @@ -513,17 +520,19 @@ DEFUN(if_nhrp_map, if_nhrp_map_cmd, } DEFUN(if_no_nhrp_map, if_no_nhrp_map_cmd, - "no " AFI_CMD " nhrp map ", + "no " AFI_CMD " nhrp map []", NO_STR AFI_STR NHRP_STR "Nexthop Server configuration\n" "IPv4 protocol address\n" - "IPv6 protocol address\n") + "IPv6 protocol address\n" + "IPv4 NBMA address\n" + "Handle protocol address locally\n") { VTY_DECLVAR_CONTEXT(interface, ifp); afi_t afi = cmd_to_afi(argv[1]); - union sockunion proto_addr; + union sockunion proto_addr, nbma_addr; struct nhrp_cache *c; if (str2sockunion(argv[4]->arg, &proto_addr) < 0 @@ -534,7 +543,8 @@ DEFUN(if_no_nhrp_map, if_no_nhrp_map_cmd, if (!c || !c->map) return nhrp_vty_return(vty, NHRP_ERR_ENTRY_NOT_FOUND); - nhrp_cache_update_binding(c, c->cur.type, -1, NULL, 0, NULL); + nhrp_cache_update_binding(c, c->cur.type, -1, + nhrp_peer_get(ifp, &nbma_addr), 0, NULL); return CMD_SUCCESS; } @@ -591,6 +601,7 @@ struct info_ctx { struct vty *vty; afi_t afi; int count; + struct json_object *json; }; static void show_ip_nhrp_cache(struct nhrp_cache *c, void *pctx) @@ -598,22 +609,60 @@ static void show_ip_nhrp_cache(struct nhrp_cache *c, void *pctx) struct info_ctx *ctx = pctx; struct vty *vty = ctx->vty; char buf[2][SU_ADDRSTRLEN]; + struct json_object *json = NULL; if (ctx->afi != family2afi(sockunion_family(&c->remote_addr))) return; - if (!ctx->count) { + + if (!ctx->count && !ctx->json) { vty_out(vty, "%-8s %-8s %-24s %-24s %-6s %s\n", "Iface", "Type", "Protocol", "NBMA", "Flags", "Identity"); } ctx->count++; + sockunion2str(&c->remote_addr, buf[0], sizeof(buf[0])); + if (c->cur.peer) + sockunion2str(&c->cur.peer->vc->remote.nbma, + buf[1], sizeof(buf[1])); + else + snprintf(buf[1], sizeof(buf[1]), "-"); + + if (ctx->json) { + json = json_object_new_object(); + json_object_string_add(json, "interface", c->ifp->name); + json_object_string_add(json, "type", + nhrp_cache_type_str[c->cur.type]); + json_object_string_add(json, "protocol", buf[0]); + json_object_string_add(json, "nbma", buf[1]); + + if (c->used) + json_object_boolean_true_add(json, "used"); + else + json_object_boolean_false_add(json, "used"); + + if (c->t_timeout) + json_object_boolean_true_add(json, "timeout"); + else + json_object_boolean_false_add(json, "timeout"); + + if (c->t_auth) + json_object_boolean_true_add(json, "auth"); + else + json_object_boolean_false_add(json, "auth"); + + if (c->cur.peer) + json_object_string_add(json, "identity", + c->cur.peer->vc->remote.id); + else + json_object_string_add(json, "identity", "-"); + + json_object_array_add(ctx->json, json); + return; + } vty_out(ctx->vty, "%-8s %-8s %-24s %-24s %c%c%c %s\n", c->ifp->name, nhrp_cache_type_str[c->cur.type], - sockunion2str(&c->remote_addr, buf[0], sizeof buf[0]), - c->cur.peer ? sockunion2str(&c->cur.peer->vc->remote.nbma, - buf[1], sizeof buf[1]) - : "-", + buf[0], buf[1], c->used ? 'U' : ' ', c->t_timeout ? 'T' : ' ', c->t_auth ? 'A' : ' ', c->cur.peer ? c->cur.peer->vc->remote.id : "-"); @@ -625,19 +674,35 @@ static void show_ip_nhrp_nhs(struct nhrp_nhs *n, struct nhrp_registration *reg, struct info_ctx *ctx = pctx; struct vty *vty = ctx->vty; char buf[2][SU_ADDRSTRLEN]; + struct json_object *json = NULL; - if (!ctx->count) { + if (!ctx->count && !ctx->json) { vty_out(vty, "%-8s %-24s %-16s %-16s\n", "Iface", "FQDN", "NBMA", "Protocol"); } ctx->count++; + if (reg && reg->peer) + sockunion2str(®->peer->vc->remote.nbma, + buf[0], sizeof(buf[0])); + else + snprintf(buf[0], sizeof(buf[0]), "-"); + sockunion2str(reg ? ®->proto_addr : &n->proto_addr, buf[1], + sizeof(buf[1])); + + if (ctx->json) { + json = json_object_new_object(); + json_object_string_add(json, "interface", n->ifp->name); + json_object_string_add(json, "fqdn", n->nbma_fqdn); + json_object_string_add(json, "nbma", buf[0]); + json_object_string_add(json, "protocol", buf[1]); + + json_object_array_add(ctx->json, json); + return; + } + vty_out(vty, "%-8s %-24s %-16s %-16s\n", n->ifp->name, n->nbma_fqdn, - (reg && reg->peer) ? sockunion2str(®->peer->vc->remote.nbma, - buf[0], sizeof buf[0]) - : "-", - sockunion2str(reg ? ®->proto_addr : &n->proto_addr, buf[1], - sizeof buf[1])); + buf[0], buf[1]); } static void show_ip_nhrp_shortcut(struct nhrp_shortcut *s, void *pctx) @@ -646,6 +711,7 @@ static void show_ip_nhrp_shortcut(struct nhrp_shortcut *s, void *pctx) struct nhrp_cache *c; struct vty *vty = ctx->vty; char buf1[PREFIX_STRLEN], buf2[SU_ADDRSTRLEN]; + struct json_object *json = NULL; if (!ctx->count) { vty_out(vty, "%-8s %-24s %-24s %s\n", "Type", "Prefix", "Via", @@ -654,20 +720,82 @@ static void show_ip_nhrp_shortcut(struct nhrp_shortcut *s, void *pctx) ctx->count++; c = s->cache; - vty_out(ctx->vty, "%-8s %-24s %-24s %s\n", nhrp_cache_type_str[s->type], - prefix2str(s->p, buf1, sizeof buf1), - c ? sockunion2str(&c->remote_addr, buf2, sizeof buf2) : "", + if (c) + sockunion2str(&c->remote_addr, buf2, sizeof(buf2)); + prefix2str(s->p, buf1, sizeof(buf1)); + + if (ctx->json) { + json = json_object_new_object(); + json_object_string_add(json, "type", + nhrp_cache_type_str[s->type]); + json_object_string_add(json, "prefix", buf1); + + if (c) + json_object_string_add(json, "via", buf2); + + if (c && c->cur.peer) + json_object_string_add(json, "identity", + c->cur.peer->vc->remote.id); + else + json_object_string_add(json, "identity", ""); + + json_object_array_add(ctx->json, json); + return; + } + + vty_out(ctx->vty, "%-8s %-24s %-24s %s\n", + nhrp_cache_type_str[s->type], + buf1, buf2, (c && c->cur.peer) ? c->cur.peer->vc->remote.id : ""); } static void show_ip_opennhrp_cache(struct nhrp_cache *c, void *pctx) { struct info_ctx *ctx = pctx; - char buf[SU_ADDRSTRLEN]; + char buf[3][SU_ADDRSTRLEN]; + struct json_object *json = NULL; + if (ctx->afi != family2afi(sockunion_family(&c->remote_addr))) return; + sockunion2str(&c->remote_addr, buf[0], sizeof(buf[0])); + if (c->cur.peer) + sockunion2str(&c->cur.peer->vc->remote.nbma, buf[1], + sizeof(buf[1])); + if (sockunion_family(&c->cur.remote_nbma_natoa) != AF_UNSPEC) + sockunion2str(&c->cur.remote_nbma_natoa, buf[2], + sizeof(buf[2])); + if (ctx->json) { + json = json_object_new_object(); + json_object_string_add(json, "type", + nhrp_cache_type_str[c->cur.type]); + + if (c->cur.peer && c->cur.peer->online) + json_object_boolean_true_add(json, "up"); + else + json_object_boolean_false_add(json, "up"); + + if (c->used) + json_object_boolean_true_add(json, "used"); + else + json_object_boolean_false_add(json, "used"); + + json_object_string_add(json, "protocolAddress", buf[0]); + json_object_int_add(json, "protocolAddressSize", + 8 * family2addrsize(sockunion_family + (&c->remote_addr))); + + if (c->cur.peer) + json_object_string_add(json, "nbmaAddress", buf[1]); + + if (sockunion_family(&c->cur.remote_nbma_natoa) != AF_UNSPEC) + json_object_string_add(json, "nbmaNatOaAddress", + buf[2]); + + json_object_array_add(ctx->json, json); + return; + } vty_out(ctx->vty, "Type: %s\n" "Flags:%s%s\n" @@ -675,40 +803,45 @@ static void show_ip_opennhrp_cache(struct nhrp_cache *c, void *pctx) nhrp_cache_type_str[c->cur.type], (c->cur.peer && c->cur.peer->online) ? " up" : "", c->used ? " used" : "", - sockunion2str(&c->remote_addr, buf, sizeof buf), + buf[0], 8 * family2addrsize(sockunion_family(&c->remote_addr))); - if (c->cur.peer) { - vty_out(ctx->vty, "NBMA-Address: %s\n", - sockunion2str(&c->cur.peer->vc->remote.nbma, buf, - sizeof buf)); - } + if (c->cur.peer) + vty_out(ctx->vty, "NBMA-Address: %s\n", buf[1]); - if (sockunion_family(&c->cur.remote_nbma_natoa) != AF_UNSPEC) { - vty_out(ctx->vty, "NBMA-NAT-OA-Address: %s\n", - sockunion2str(&c->cur.remote_nbma_natoa, buf, - sizeof buf)); - } + if (sockunion_family(&c->cur.remote_nbma_natoa) != AF_UNSPEC) + vty_out(ctx->vty, "NBMA-NAT-OA-Address: %s\n", buf[2]); vty_out(ctx->vty, "\n\n"); } DEFUN(show_ip_nhrp, show_ip_nhrp_cmd, - "show " AFI_CMD " nhrp [cache|nhs|shortcut|opennhrp]", + "show " AFI_CMD " nhrp [cache|nhs|shortcut|opennhrp] [json]", SHOW_STR AFI_STR "NHRP information\n" "Forwarding cache information\n" "Next hop server information\n" "Shortcut information\n" - "opennhrpctl style cache dump\n") + "opennhrpctl style cache dump\n" + JSON_STR) { struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT); struct interface *ifp; struct info_ctx ctx = { - .vty = vty, .afi = cmd_to_afi(argv[1]), + .vty = vty, .afi = cmd_to_afi(argv[1]), .json = NULL }; - + bool uj = use_json(argc, argv); + struct json_object *json_path = NULL; + struct json_object *json_vrf = NULL, *json_vrf_path = NULL; + int ret = CMD_SUCCESS; + + if (uj) { + json_vrf = json_object_new_object(); + json_vrf_path = json_object_new_object(); + json_path = json_object_new_array(); + ctx.json = json_path; + } if (argc <= 3 || argv[3]->text[0] == 'c') { FOR_ALL_INTERFACES (vrf, ifp) nhrp_cache_foreach(ifp, show_ip_nhrp_cache, &ctx); @@ -718,49 +851,104 @@ DEFUN(show_ip_nhrp, show_ip_nhrp_cmd, } else if (argv[3]->text[0] == 's') { nhrp_shortcut_foreach(ctx.afi, show_ip_nhrp_shortcut, &ctx); } else { - vty_out(vty, "Status: ok\n\n"); + if (!ctx.json) + vty_out(vty, "Status: ok\n\n"); + else + json_object_string_add(json_vrf, "status", "ok"); + ctx.count++; FOR_ALL_INTERFACES (vrf, ifp) nhrp_cache_foreach(ifp, show_ip_opennhrp_cache, &ctx); } + if (uj) + json_object_int_add(json_vrf, "entriesCount", ctx.count); if (!ctx.count) { - vty_out(vty, "%% No entries\n"); - return CMD_WARNING; + if (!ctx.json) + vty_out(vty, "%% No entries\n"); + ret = CMD_WARNING; } - - return CMD_SUCCESS; + if (uj) { + json_object_object_add(json_vrf_path, "attr", json_vrf); + json_object_object_add(json_vrf_path, "table", ctx.json); + vty_out(vty, "%s", + json_object_to_json_string_ext( + json_vrf_path, JSON_C_TO_STRING_PRETTY)); + json_object_free(json_vrf_path); + } + return ret; } +struct dmvpn_cfg { + struct vty *vty; + struct json_object *json; +}; + static void show_dmvpn_entry(struct nhrp_vc *vc, void *ctx) { - struct vty *vty = ctx; + struct dmvpn_cfg *ctxt = ctx; + struct vty *vty; char buf[2][SU_ADDRSTRLEN]; + struct json_object *json = NULL; - vty_out(vty, "%-24s %-24s %c %-4d %-24s\n", - sockunion2str(&vc->local.nbma, buf[0], sizeof buf[0]), - sockunion2str(&vc->remote.nbma, buf[1], sizeof buf[1]), - notifier_active(&vc->notifier_list) ? 'n' : ' ', vc->ipsec, - vc->remote.id); + if (!ctxt || !ctxt->vty) + return; + vty = ctxt->vty; + sockunion2str(&vc->local.nbma, buf[0], sizeof(buf[0])); + sockunion2str(&vc->remote.nbma, buf[1], sizeof(buf[1])); + if (ctxt->json) { + json = json_object_new_object(); + json_object_string_add(json, "src", buf[0]); + json_object_string_add(json, "dst", buf[1]); + + if (notifier_active(&vc->notifier_list)) + json_object_boolean_true_add(json, "notifierActive"); + else + json_object_boolean_false_add(json, "notifierActive"); + + json_object_int_add(json, "sas", vc->ipsec); + json_object_string_add(json, "identity", vc->remote.id); + json_object_array_add(ctxt->json, json); + } else { + vty_out(vty, "%-24s %-24s %c %-4d %-24s\n", + buf[0], buf[1], notifier_active(&vc->notifier_list) ? + 'n' : ' ', vc->ipsec, vc->remote.id); + } } DEFUN(show_dmvpn, show_dmvpn_cmd, - "show dmvpn", + "show dmvpn [json]", SHOW_STR - "DMVPN information\n") + "DMVPN information\n" + JSON_STR) { - vty_out(vty, "%-24s %-24s %-6s %-4s %-24s\n", "Src", "Dst", "Flags", - "SAs", "Identity"); - - nhrp_vc_foreach(show_dmvpn_entry, vty); - + bool uj = use_json(argc, argv); + struct dmvpn_cfg ctxt; + struct json_object *json_path = NULL; + + ctxt.vty = vty; + if (!uj) { + ctxt.json = NULL; + vty_out(vty, "%-24s %-24s %-6s %-4s %-24s\n", + "Src", "Dst", "Flags", "SAs", "Identity"); + } else { + json_path = json_object_new_array(); + ctxt.json = json_path; + } + nhrp_vc_foreach(show_dmvpn_entry, &ctxt); + if (uj) { + vty_out(vty, "%s", + json_object_to_json_string_ext( + json_path, JSON_C_TO_STRING_PRETTY)); + json_object_free(json_path); + } return CMD_SUCCESS; } static void clear_nhrp_cache(struct nhrp_cache *c, void *data) { struct info_ctx *ctx = data; - if (c->cur.type <= NHRP_CACHE_CACHED) { + if (c->cur.type <= NHRP_CACHE_DYNAMIC) { nhrp_cache_update_binding(c, c->cur.type, -1, NULL, 0, NULL); ctx->count++; } @@ -821,11 +1009,11 @@ static void interface_config_write_nhrp_map(struct nhrp_cache *c, void *data) return; vty_out(vty, " %s nhrp map %s %s\n", ctx->aficmd, - sockunion2str(&c->remote_addr, buf[0], sizeof buf[0]), + sockunion2str(&c->remote_addr, buf[0], sizeof(buf[0])), c->cur.type == NHRP_CACHE_LOCAL ? "local" : sockunion2str(&c->cur.peer->vc->remote.nbma, buf[1], - sizeof buf[1])); + sizeof(buf[1]))); } static int interface_config_write(struct vty *vty) @@ -901,7 +1089,7 @@ static int interface_config_write(struct vty *vty) ? "dynamic" : sockunion2str( &nhs->proto_addr, buf, - sizeof buf), + sizeof(buf)), nhs->nbma_fqdn); } } @@ -914,18 +1102,19 @@ static int interface_config_write(struct vty *vty) void nhrp_config_init(void) { - install_node(&zebra_node, nhrp_config_write); + install_node(&zebra_node); install_default(ZEBRA_NODE); /* access-list commands */ access_list_init(); /* global commands */ - install_element(VIEW_NODE, &show_debugging_nhrp_cmd); install_element(VIEW_NODE, &show_ip_nhrp_cmd); install_element(VIEW_NODE, &show_dmvpn_cmd); install_element(ENABLE_NODE, &clear_nhrp_cmd); + install_element(ENABLE_NODE, &show_debugging_nhrp_cmd); + install_element(ENABLE_NODE, &debug_nhrp_cmd); install_element(ENABLE_NODE, &no_debug_nhrp_cmd); @@ -938,7 +1127,7 @@ void nhrp_config_init(void) install_element(CONFIG_NODE, &no_nhrp_nflog_group_cmd); /* interface specific commands */ - install_node(&nhrp_interface_node, interface_config_write); + install_node(&nhrp_interface_node); if_cmd_init(); install_element(INTERFACE_NODE, &tunnel_protection_cmd); diff --git a/nhrpd/nhrpd.h b/nhrpd/nhrpd.h index 8f1c63457a..cbee5951f3 100644 --- a/nhrpd/nhrpd.h +++ b/nhrpd/nhrpd.h @@ -16,6 +16,7 @@ #include "zclient.h" #include "debug.h" #include "memory.h" +#include "resolver.h" DECLARE_MGROUP(NHRPD) @@ -75,8 +76,9 @@ static inline void notifier_del(struct notifier_block *n) static inline void notifier_call(struct notifier_list *l, int cmd) { struct notifier_block *n, *nn; - list_for_each_entry_safe(n, nn, &l->notifier_head, notifier_entry) + list_for_each_entry_safe(n, nn, &l->notifier_head, notifier_entry) { n->action(n, cmd); + } } static inline int notifier_active(struct notifier_list *l) @@ -84,15 +86,6 @@ static inline int notifier_active(struct notifier_list *l) return !list_empty(&l->notifier_head); } -struct resolver_query { - void (*callback)(struct resolver_query *, int n, union sockunion *); -}; - -void resolver_init(void); -void resolver_resolve(struct resolver_query *query, int af, - const char *hostname, void (*cb)(struct resolver_query *, - int, union sockunion *)); - void nhrp_zebra_init(void); void nhrp_zebra_terminate(void); @@ -314,18 +307,12 @@ void nhrp_interface_init(void); void nhrp_interface_update(struct interface *ifp); void nhrp_interface_update_mtu(struct interface *ifp, afi_t afi); -int nhrp_interface_add(int cmd, struct zclient *client, zebra_size_t length, - vrf_id_t vrf_id); -int nhrp_interface_delete(int cmd, struct zclient *client, zebra_size_t length, - vrf_id_t vrf_id); -int nhrp_interface_up(int cmd, struct zclient *client, zebra_size_t length, - vrf_id_t vrf_id); -int nhrp_interface_down(int cmd, struct zclient *client, zebra_size_t length, - vrf_id_t vrf_id); -int nhrp_interface_address_add(int cmd, struct zclient *client, - zebra_size_t length, vrf_id_t vrf_id); -int nhrp_interface_address_delete(int cmd, struct zclient *client, - zebra_size_t length, vrf_id_t vrf_id); +int nhrp_interface_add(ZAPI_CALLBACK_ARGS); +int nhrp_interface_delete(ZAPI_CALLBACK_ARGS); +int nhrp_interface_up(ZAPI_CALLBACK_ARGS); +int nhrp_interface_down(ZAPI_CALLBACK_ARGS); +int nhrp_interface_address_add(ZAPI_CALLBACK_ARGS); +int nhrp_interface_address_delete(ZAPI_CALLBACK_ARGS); void nhrp_interface_notify_add(struct interface *ifp, struct notifier_block *n, notifier_fn_t fn); @@ -333,6 +320,10 @@ void nhrp_interface_notify_del(struct interface *ifp, struct notifier_block *n); void nhrp_interface_set_protection(struct interface *ifp, const char *profile, const char *fallback_profile); void nhrp_interface_set_source(struct interface *ifp, const char *ifname); +extern int nhrp_ifp_create(struct interface *ifp); +extern int nhrp_ifp_up(struct interface *ifp); +extern int nhrp_ifp_down(struct interface *ifp); +extern int nhrp_ifp_destroy(struct interface *ifp); int nhrp_nhs_add(struct interface *ifp, afi_t afi, union sockunion *proto_addr, const char *nbma_fqdn); @@ -349,8 +340,7 @@ void nhrp_route_update_nhrp(const struct prefix *p, struct interface *ifp); void nhrp_route_announce(int add, enum nhrp_cache_type type, const struct prefix *p, struct interface *ifp, const union sockunion *nexthop, uint32_t mtu); -int nhrp_route_read(int command, struct zclient *zclient, zebra_size_t length, - vrf_id_t vrf_id); +int nhrp_route_read(ZAPI_CALLBACK_ARGS); int nhrp_route_get_nexthop(const union sockunion *addr, struct prefix *p, union sockunion *via, struct interface **ifp); enum nhrp_route_type nhrp_route_address(struct interface *in_ifp, diff --git a/nhrpd/reqid.c b/nhrpd/reqid.c index 08a007bdf9..e56bbe3bf7 100644 --- a/nhrpd/reqid.c +++ b/nhrpd/reqid.c @@ -2,9 +2,9 @@ #include "hash.h" #include "nhrpd.h" -static unsigned int nhrp_reqid_key(void *data) +static unsigned int nhrp_reqid_key(const void *data) { - struct nhrp_reqid *r = data; + const struct nhrp_reqid *r = data; return r->request_id; } diff --git a/nhrpd/subdir.am b/nhrpd/subdir.am index 6e2b91780f..8cfc25b7bd 100644 --- a/nhrpd/subdir.am +++ b/nhrpd/subdir.am @@ -4,12 +4,11 @@ if NHRPD sbin_PROGRAMS += nhrpd/nhrpd -vtysh_scan += $(top_srcdir)/nhrpd/nhrp_vty.c -man8 += $(MANBUILD)/nhrpd.8 +vtysh_scan += nhrpd/nhrp_vty.c +man8 += $(MANBUILD)/frr-nhrpd.8 endif -nhrpd_nhrpd_LDADD = lib/libfrr.la $(LIBCAP) $(CARES_LIBS) -nhrpd_nhrpd_CFLAGS = $(AM_CFLAGS) $(CARES_CFLAGS) +nhrpd_nhrpd_LDADD = lib/libfrr.la lib/libfrrcares.la $(LIBCAP) nhrpd_nhrpd_SOURCES = \ nhrpd/linux.c \ nhrpd/netlink_arp.c \ @@ -27,7 +26,6 @@ nhrpd_nhrpd_SOURCES = \ nhrpd/nhrp_vc.c \ nhrpd/nhrp_vty.c \ nhrpd/reqid.c \ - nhrpd/resolver.c \ nhrpd/vici.c \ nhrpd/zbuf.c \ nhrpd/znl.c \ diff --git a/nhrpd/vici.c b/nhrpd/vici.c index 3de4609a2b..2dc05a4aa7 100644 --- a/nhrpd/vici.c +++ b/nhrpd/vici.c @@ -207,7 +207,7 @@ static void parse_sa_message(struct vici_message_ctx *ctx, } break; default: - if (!key) + if (!key || !key->ptr) break; switch (key->ptr[0]) { @@ -303,7 +303,7 @@ static void vici_recv_sa(struct vici_conn *vici, struct zbuf *msg, int event) if (ctx.kill_ikesa && ctx.ike_uniqueid) { debugf(NHRP_DEBUG_COMMON, "VICI: Deleting IKE_SA %u", ctx.ike_uniqueid); - snprintf(buf, sizeof buf, "%u", ctx.ike_uniqueid); + snprintf(buf, sizeof(buf), "%u", ctx.ike_uniqueid); vici_submit_request(vici, "terminate", VICI_KEY_VALUE, "ike-id", strlen(buf), buf, VICI_END); } @@ -481,8 +481,8 @@ static int vici_reconnect(struct thread *t) fd = sock_open_unix("/var/run/charon.vici"); if (fd < 0) { debugf(NHRP_DEBUG_VICI, - "%s: failure connecting VICI socket: %s", - __PRETTY_FUNCTION__, strerror(errno)); + "%s: failure connecting VICI socket: %s", __func__, + strerror(errno)); thread_add_timer(master, vici_reconnect, vici, 2, &vici->t_reconnect); return 0; @@ -527,8 +527,8 @@ void vici_request_vc(const char *profile, union sockunion *src, struct vici_conn *vici = &vici_connection; char buf[2][SU_ADDRSTRLEN]; - sockunion2str(src, buf[0], sizeof buf[0]); - sockunion2str(dst, buf[1], sizeof buf[1]); + sockunion2str(src, buf[0], sizeof(buf[0])); + sockunion2str(dst, buf[1], sizeof(buf[1])); vici_submit_request(vici, "initiate", VICI_KEY_VALUE, "child", strlen(profile), profile, VICI_KEY_VALUE, "timeout", @@ -550,7 +550,7 @@ int sock_open_unix(const char *path) memset(&addr, 0, sizeof(struct sockaddr_un)); addr.sun_family = AF_UNIX; - strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1); + strlcpy(addr.sun_path, path, sizeof(addr.sun_path)); ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr.sun_family) + strlen(addr.sun_path)); diff --git a/nhrpd/zbuf.h b/nhrpd/zbuf.h index d03f4ca3a2..e6f7101d63 100644 --- a/nhrpd/zbuf.h +++ b/nhrpd/zbuf.h @@ -86,9 +86,9 @@ static inline void *__zbuf_pull(struct zbuf *zb, size_t size, int error) } #define zbuf_pull(zb, type) ((type *)__zbuf_pull(zb, sizeof(type), 1)) -#define zbuf_pulln(zb, sz) ((void *)__zbuf_pull(zb, sz, 1)) +#define zbuf_pulln(zb, sz) (__zbuf_pull(zb, sz, 1)) #define zbuf_may_pull(zb, type) ((type *)__zbuf_pull(zb, sizeof(type), 0)) -#define zbuf_may_pulln(zb, sz) ((void *)__zbuf_pull(zb, sz, 0)) +#define zbuf_may_pulln(zb, sz) (__zbuf_pull(zb, sz, 0)) void *zbuf_may_pull_until(struct zbuf *zb, const char *sep, struct zbuf *msg); @@ -149,9 +149,9 @@ static inline void *__zbuf_push(struct zbuf *zb, size_t size, int error) } #define zbuf_push(zb, type) ((type *)__zbuf_push(zb, sizeof(type), 1)) -#define zbuf_pushn(zb, sz) ((void *)__zbuf_push(zb, sz, 1)) +#define zbuf_pushn(zb, sz) (__zbuf_push(zb, sz, 1)) #define zbuf_may_push(zb, type) ((type *)__zbuf_may_push(zb, sizeof(type), 0)) -#define zbuf_may_pushn(zb, sz) ((void *)__zbuf_push(zb, sz, 0)) +#define zbuf_may_pushn(zb, sz) (__zbuf_push(zb, sz, 0)) static inline void zbuf_put(struct zbuf *zb, const void *src, size_t len) { diff --git a/ospf6d/ospf6_abr.c b/ospf6d/ospf6_abr.c index 0ce53143b8..2ecfd892cd 100644 --- a/ospf6d/ospf6_abr.c +++ b/ospf6d/ospf6_abr.c @@ -72,7 +72,7 @@ static int ospf6_abr_nexthops_belong_to_area(struct ospf6_route *route, struct ospf6_interface *oi; oi = ospf6_interface_lookup_by_ifindex( - ospf6_route_get_first_nh_index(route)); + ospf6_route_get_first_nh_index(route), area->ospf6->vrf_id); if (oi && oi->area && oi->area == area) return 1; else @@ -232,8 +232,8 @@ int ospf6_abr_originate_summary_to_area(struct ospf6_route *route, &route->prefix)), buf, sizeof(buf)); zlog_debug( - "%s: route %s with cost %u is not best, ignore." - , __PRETTY_FUNCTION__, buf, + "%s: route %s with cost %u is not best, ignore.", + __func__, buf, route->path.cost); } return 0; @@ -246,9 +246,10 @@ int ospf6_abr_originate_summary_to_area(struct ospf6_route *route, if (is_debug) { prefix2str(&route->prefix, buf, sizeof(buf)); - zlog_debug("%s: intra-prefix route %s with cost %u is not best, ignore." - , __PRETTY_FUNCTION__, buf, - route->path.cost); + zlog_debug( + "%s: intra-prefix route %s with cost %u is not best, ignore.", + __func__, buf, + route->path.cost); } return 0; } @@ -762,11 +763,13 @@ void ospf6_abr_old_path_update(struct ospf6_route *old_route, } if (IS_OSPF6_DEBUG_ABR || IS_OSPF6_DEBUG_EXAMIN(INTER_PREFIX)) - zlog_debug("%s: paths %u nh %u", __PRETTY_FUNCTION__, - old_route->paths ? - listcount(old_route->paths) : 0, - old_route->nh_list ? - listcount(old_route->nh_list) : 0); + zlog_debug("%s: paths %u nh %u", __func__, + old_route->paths + ? listcount(old_route->paths) + : 0, + old_route->nh_list + ? listcount(old_route->nh_list) + : 0); if (table->hook_add) (*table->hook_add)(old_route); @@ -790,6 +793,10 @@ void ospf6_abr_old_route_remove(struct ospf6_lsa *lsa, struct ospf6_route *old, struct ospf6_route_table *table) { + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("%s: route %pFX, paths %d", __func__, &old->prefix, + listcount(old->paths)); + if (listcount(old->paths) > 1) { struct listnode *anode, *anext, *nnode, *rnode, *rnext; struct ospf6_path *o_path; @@ -799,13 +806,15 @@ void ospf6_abr_old_route_remove(struct ospf6_lsa *lsa, for (ALL_LIST_ELEMENTS(old->paths, anode, anext, o_path)) { if (o_path->origin.adv_router != lsa->header->adv_router - && o_path->origin.id != lsa->header->id) + || o_path->origin.id != lsa->header->id) continue; for (ALL_LIST_ELEMENTS_RO(o_path->nh_list, nnode, nh)) { for (ALL_LIST_ELEMENTS(old->nh_list, rnode, rnext, rnh)) { if (!ospf6_nexthop_is_same(rnh, nh)) continue; + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("deleted nexthop"); listnode_delete(old->nh_list, rnh); ospf6_nexthop_delete(rnh); } @@ -822,9 +831,10 @@ void ospf6_abr_old_route_remove(struct ospf6_lsa *lsa, prefix2str(&old->prefix, buf, sizeof(buf)); zlog_debug("%s: old %s updated nh %u", - __PRETTY_FUNCTION__, buf, - old->nh_list ? - listcount(old->nh_list) : 0); + __func__, buf, + old->nh_list ? listcount( + old->nh_list) + : 0); } if (table->hook_add) @@ -871,15 +881,16 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa) bool old_entry_updated = false; struct ospf6_path *path, *o_path, *ecmp_path; struct listnode *anode; - char adv_router[16]; + bool add_route = false; memset(&prefix, 0, sizeof(prefix)); if (lsa->header->type == htons(OSPF6_LSTYPE_INTER_PREFIX)) { if (IS_OSPF6_DEBUG_EXAMIN(INTER_PREFIX)) { is_debug++; - zlog_debug("%s: Examin %s in area %s", - __PRETTY_FUNCTION__, lsa->name, oa->name); + zlog_debug("%s: LSA %s age %d in area %s", __func__, + lsa->name, ospf6_lsa_age_current(lsa), + oa->name); } prefix_lsa = @@ -898,8 +909,9 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa) } else if (lsa->header->type == htons(OSPF6_LSTYPE_INTER_ROUTER)) { if (IS_OSPF6_DEBUG_EXAMIN(INTER_ROUTER)) { is_debug++; - zlog_debug("%s: Examin %s in area %s", - __PRETTY_FUNCTION__, lsa->name, oa->name); + zlog_debug("%s: LSA %s age %d in area %s", __func__, + lsa->name, ospf6_lsa_age_current(lsa), + oa->name); } router_lsa = @@ -923,8 +935,12 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa) /* Find existing route */ route = ospf6_route_lookup(&prefix, table); - if (route) + if (route) { ospf6_route_lock(route); + if (is_debug) + zlog_debug("%s: route %pFX, paths %d", __func__, + &prefix, listcount(route->paths)); + } while (route && ospf6_route_is_prefix(&prefix, route)) { if (route->path.area_id == oa->area_id && route->path.origin.type == lsa->header->type @@ -936,19 +952,16 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa) if (listcount(route->paths) > 1) { for (ALL_LIST_ELEMENTS_RO(route->paths, anode, o_path)) { - inet_ntop(AF_INET, - &o_path->origin.adv_router, - adv_router, - sizeof(adv_router)); if (o_path->origin.id == lsa->header->id && o_path->origin.adv_router == lsa->header->adv_router) { old = route; if (is_debug) - zlog_debug("%s: old entry found in paths, adv_router %s", - __PRETTY_FUNCTION__, - adv_router); + zlog_debug( + "%s: old entry found in paths, adv_router %pI4", + __func__, + &o_path->origin.adv_router); break; } @@ -973,13 +986,14 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa) } if (OSPF6_LSA_IS_MAXAGE(lsa)) { if (is_debug) - zlog_debug("%s: LSA %s is MaxAge, ignore", - __PRETTY_FUNCTION__, lsa->name); + zlog_debug("%s: LSA %s is MaxAge, ignore", __func__, + lsa->name); if (old) ospf6_abr_old_route_remove(lsa, old, table); return; } + /* (2) if the LSA is self-originated, ignore */ if (lsa->header->adv_router == oa->ospf6->router_id) { if (is_debug) @@ -1053,22 +1067,22 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa) || CHECK_FLAG(abr_entry->flag, OSPF6_ROUTE_REMOVE) || !CHECK_FLAG(abr_entry->path.router_bits, OSPF6_ROUTER_BIT_B)) { if (is_debug) - zlog_debug("%s: ABR router entry does not exist, ignore", - __PRETTY_FUNCTION__); + zlog_debug( + "%s: ABR router entry %pFX does not exist, ignore", + __func__, &abr_prefix); if (old) { if (old->type == OSPF6_DEST_TYPE_ROUTER && oa->intra_brouter_calc) { if (is_debug) zlog_debug( - "%s: intra_brouter_calc is on, skip brouter remove: %s (%p)", - __PRETTY_FUNCTION__, buf, - (void *)old); + "%s: intra_brouter_calc is on, skip brouter remove: %s (%p)", + __func__, buf, (void *)old); } else { if (is_debug) - zlog_debug("%s: remove old entry: %s %p ", - __PRETTY_FUNCTION__, buf, - (void *)old); - ospf6_route_remove(old, table); + zlog_debug( + "%s: remove old entry: %s %p ", + __func__, buf, (void *)old); + ospf6_abr_old_route_remove(lsa, old, table); } } return; @@ -1132,7 +1146,11 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa) are identical. */ old = ospf6_route_lookup(&prefix, table); - + if (old) { + if (is_debug) + zlog_debug("%s: found old route %pFX, paths %d", + __func__, &prefix, listcount(old->paths)); + } for (old_route = old; old_route; old_route = old_route->next) { if (!ospf6_route_is_same(old_route, route) || (old_route->type != route->type) || @@ -1142,11 +1160,10 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa) if ((ospf6_route_cmp(route, old_route) != 0)) { if (is_debug) { prefix2str(&prefix, buf, sizeof(buf)); - zlog_debug("%s: old %p %s cost %u new route cost %u are not same", - __PRETTY_FUNCTION__, - (void *)old_route, buf, - old_route->path.cost, - route->path.cost); + zlog_debug( + "%s: old %p %s cost %u new route cost %u are not same", + __func__, (void *)old_route, buf, + old_route->path.cost, route->path.cost); } /* Check new route's adv. router is same in one of @@ -1181,30 +1198,43 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa) listnode_add_sort(old_route->paths, ecmp_path); if (is_debug) { - prefix2str(&route->prefix, buf, sizeof(buf)); - inet_ntop(AF_INET, - &ecmp_path->origin.adv_router, - adv_router, sizeof(adv_router)); - zlog_debug("%s: route %s cost %u another path %s added with nh %u, effective paths %u nh %u", - __PRETTY_FUNCTION__, buf, - old_route->path.cost, - adv_router, - listcount(ecmp_path->nh_list), - old_route->paths ? - listcount(old_route->paths) : 0, - listcount(old_route->nh_list)); + zlog_debug( + "%s: route %pFX cost %u another path %pI4 added with nh %u, effective paths %u nh %u", + __func__, &route->prefix, + old_route->path.cost, + &ecmp_path->origin.adv_router, + listcount(ecmp_path->nh_list), + old_route->paths + ? listcount(old_route->paths) + : 0, + listcount(old_route->nh_list)); } } else { - /* adv. router exists in the list, update the nhs */ - list_delete_all_node(o_path->nh_list); - ospf6_copy_nexthops(o_path->nh_list, route->nh_list); + struct ospf6_route *tmp_route = ospf6_route_create(); + + ospf6_copy_nexthops(tmp_route->nh_list, + o_path->nh_list); + + if (ospf6_route_cmp_nexthops(tmp_route, route) != 0) { + /* adv. router exists in the list, update nhs */ + list_delete_all_node(o_path->nh_list); + ospf6_copy_nexthops(o_path->nh_list, + route->nh_list); + ospf6_route_delete(tmp_route); + } else { + /* adv. router has no change in nhs */ + old_entry_updated = false; + ospf6_route_delete(tmp_route); + continue; + } } if (is_debug) - zlog_debug("%s: Update route: %s %p old cost %u new cost %u nh %u", - __PRETTY_FUNCTION__, buf, (void *)old_route, - old_route->path.cost, route->path.cost, - listcount(route->nh_list)); + zlog_debug( + "%s: Update route: %s %p old cost %u new cost %u nh %u", + __func__, buf, (void *)old_route, + old_route->path.cost, route->path.cost, + listcount(old_route->nh_list)); /* For Inter-Prefix route: Update RIB/FIB, * For Inter-Router trigger summary update @@ -1217,13 +1247,22 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa) break; } + /* If the old entry is not updated and old entry not found or old entry + * does not match with the new entry then add the new route + */ if (old_entry_updated == false) { + if ((old == NULL) || (old->type != route->type) + || (old->path.type != route->path.type)) + add_route = true; + } + + if (add_route) { if (is_debug) { - inet_ntop(AF_INET, &route->path.origin.adv_router, - adv_router, sizeof(adv_router)); - zlog_debug("%s: Install route: %s cost %u nh %u adv_router %s ", - __PRETTY_FUNCTION__, buf, route->path.cost, - listcount(route->nh_list), adv_router); + zlog_debug( + "%s: Install new route: %s cost %u nh %u adv_router %pI4", + __func__, buf, route->path.cost, + listcount(route->nh_list), + &route->path.origin.adv_router); } path = ospf6_path_dup(&route->path); @@ -1420,7 +1459,7 @@ void install_element_ospf6_debug_abr(void) install_element(CONFIG_NODE, &no_debug_ospf6_abr_cmd); } -struct ospf6_lsa_handler inter_prefix_handler = { +static struct ospf6_lsa_handler inter_prefix_handler = { .lh_type = OSPF6_LSTYPE_INTER_PREFIX, .lh_name = "Inter-Prefix", .lh_short_name = "IAP", @@ -1428,7 +1467,7 @@ struct ospf6_lsa_handler inter_prefix_handler = { .lh_get_prefix_str = ospf6_inter_area_prefix_lsa_get_prefix_str, .lh_debug = 0}; -struct ospf6_lsa_handler inter_router_handler = { +static struct ospf6_lsa_handler inter_router_handler = { .lh_type = OSPF6_LSTYPE_INTER_ROUTER, .lh_name = "Inter-Router", .lh_short_name = "IAR", diff --git a/ospf6d/ospf6_area.c b/ospf6d/ospf6_area.c index 484e5adae6..713ce26ecb 100644 --- a/ospf6d/ospf6_area.c +++ b/ospf6d/ospf6_area.c @@ -61,8 +61,7 @@ static void ospf6_area_lsdb_hook_add(struct ospf6_lsa *lsa) case OSPF6_LSTYPE_ROUTER: case OSPF6_LSTYPE_NETWORK: if (IS_OSPF6_DEBUG_EXAMIN_TYPE(lsa->header->type)) { - zlog_debug("%s Examin LSA %s", __PRETTY_FUNCTION__, - lsa->name); + zlog_debug("%s Examin LSA %s", __func__, lsa->name); zlog_debug(" Schedule SPF Calculation for %s", OSPF6_AREA(lsa->lsdb->data)->name); } @@ -157,7 +156,7 @@ static int ospf6_area_stub_set(struct ospf6 *ospf6, struct ospf6_area *area) ospf6_area_stub_update(area); } - return (1); + return 1; } static void ospf6_area_stub_unset(struct ospf6 *ospf6, struct ospf6_area *area) @@ -380,22 +379,6 @@ void ospf6_area_show(struct vty *vty, struct ospf6_area *oa) vty_out(vty, "SPF has not been run\n"); } - -#define OSPF6_CMD_AREA_GET(str, oa) \ - { \ - char *ep; \ - uint32_t area_id = htonl(strtoul(str, &ep, 10)); \ - if (*ep && inet_pton(AF_INET, str, &area_id) != 1) { \ - vty_out(vty, "Malformed Area-ID: %s\n", str); \ - return CMD_SUCCESS; \ - } \ - int format = !*ep ? OSPF6_AREA_FMT_DECIMAL \ - : OSPF6_AREA_FMT_DOTTEDQUAD; \ - oa = ospf6_area_lookup(area_id, ospf6); \ - if (oa == NULL) \ - oa = ospf6_area_create(area_id, ospf6, format); \ - } - DEFUN (area_range, area_range_cmd, "area range X:X::X:X/M []", diff --git a/ospf6d/ospf6_area.h b/ospf6d/ospf6_area.h index 5648b1dfec..f6287660d6 100644 --- a/ospf6d/ospf6_area.h +++ b/ospf6d/ospf6_area.h @@ -28,7 +28,7 @@ struct ospf6_area { struct ospf6 *ospf6; /* Area-ID */ - uint32_t area_id; + in_addr_t area_id; #define OSPF6_AREA_FMT_DOTTEDQUAD 1 #define OSPF6_AREA_FMT_DECIMAL 2 @@ -117,6 +117,21 @@ struct ospf6_area { #define IS_AREA_TRANSIT(oa) (CHECK_FLAG ((oa)->flag, OSPF6_AREA_TRANSIT)) #define IS_AREA_STUB(oa) (CHECK_FLAG ((oa)->flag, OSPF6_AREA_STUB)) +#define OSPF6_CMD_AREA_GET(str, oa) \ + { \ + char *ep; \ + uint32_t area_id = htonl(strtoul(str, &ep, 10)); \ + if (*ep && inet_pton(AF_INET, str, &area_id) != 1) { \ + vty_out(vty, "Malformed Area-ID: %s\n", str); \ + return CMD_SUCCESS; \ + } \ + int format = !*ep ? OSPF6_AREA_FMT_DECIMAL \ + : OSPF6_AREA_FMT_DOTTEDQUAD; \ + oa = ospf6_area_lookup(area_id, ospf6); \ + if (oa == NULL) \ + oa = ospf6_area_create(area_id, ospf6, format); \ + } + /* prototypes */ extern int ospf6_area_cmp(void *va, void *vb); diff --git a/ospf6d/ospf6_asbr.c b/ospf6d/ospf6_asbr.c index 2795bb9abd..8fcedc8198 100644 --- a/ospf6d/ospf6_asbr.c +++ b/ospf6d/ospf6_asbr.c @@ -47,6 +47,9 @@ #include "ospf6_flood.h" #include "ospf6d.h" +static void ospf6_asbr_redistribute_set(int type, vrf_id_t vrf_id); +static void ospf6_asbr_redistribute_unset(int type, vrf_id_t vrf_id); + unsigned char conf_debug_ospf6_asbr = 0; #define ZROUTE_NAME(x) zebra_route_string(x) @@ -166,7 +169,7 @@ int ospf6_orig_as_external_lsa(struct thread *thread) if (IS_OSPF6_DEBUG_ASBR) zlog_debug( "%s: Send update of AS-External LSA %s seq 0x%x", - __PRETTY_FUNCTION__, lsa->name, + __func__, lsa->name, ntohl(lsa->header->seqnum)); ospf6_flood_interface(NULL, lsa, oi); @@ -203,7 +206,7 @@ static route_tag_t ospf6_as_external_lsa_get_tag(struct ospf6_lsa *lsa) void ospf6_asbr_update_route_ecmp_path(struct ospf6_route *old, struct ospf6_route *route) { - struct ospf6_route *old_route; + struct ospf6_route *old_route, *next_route; struct ospf6_path *ecmp_path, *o_path = NULL; struct listnode *anode, *anext; struct listnode *nnode, *rnode, *rnext; @@ -214,9 +217,11 @@ void ospf6_asbr_update_route_ecmp_path(struct ospf6_route *old, /* check for old entry match with new route origin, * delete old entry. */ - for (old_route = old; old_route; old_route = old_route->next) { + for (old_route = old; old_route; old_route = next_route) { bool route_updated = false; + next_route = old_route->next; + if (!ospf6_route_is_same(old_route, route) || (old_route->path.type != route->path.type)) continue; @@ -241,11 +246,9 @@ void ospf6_asbr_update_route_ecmp_path(struct ospf6_route *old, continue; if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) { - prefix2str(&old_route->prefix, buf, - sizeof(buf)); zlog_debug( - "%s: route %s cost old %u new %u is not same, replace route", - __PRETTY_FUNCTION__, buf, o_path->cost, + "%s: route %pFX cost old %u new %u is not same, replace route", + __func__, &old_route->prefix, o_path->cost, route->path.cost); } @@ -305,14 +308,14 @@ void ospf6_asbr_update_route_ecmp_path(struct ospf6_route *old, } } else { if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) { - prefix2str(&old_route->prefix, buf, - sizeof(buf)); zlog_debug( - "%s: route %s old cost %u new cost %u, delete old entry.", - __PRETTY_FUNCTION__, buf, + "%s: route %pFX old cost %u new cost %u, delete old entry.", + __func__, &old_route->prefix, old_route->path.cost, route->path.cost); } + if (old == old_route) + old = next_route; ospf6_route_remove(old_route, ospf6->route_table); } @@ -336,11 +339,9 @@ void ospf6_asbr_update_route_ecmp_path(struct ospf6_route *old, && (old_route->path.u.cost_e2 == route->path.u.cost_e2)) { if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) { - prefix2str(&old_route->prefix, buf, - sizeof(buf)); zlog_debug( - "%s: old route %s path cost %u e2 %u", - __PRETTY_FUNCTION__, buf, + "%s: old route %pFX path cost %u e2 %u", + __func__, &old_route->prefix, old_route->path.cost, old_route->path.u.cost_e2); } @@ -378,13 +379,11 @@ void ospf6_asbr_update_route_ecmp_path(struct ospf6_route *old, sizeof(buf)); zlog_debug( "%s: route %s another path added with nh %u, effective paths %u nh %u", - __PRETTY_FUNCTION__, buf, + __func__, buf, listcount(ecmp_path->nh_list), - old_route->paths - ? listcount( - old_route - ->paths) - : 0, + old_route->paths ? listcount( + old_route->paths) + : 0, listcount(old_route->nh_list)); } } else { @@ -410,9 +409,9 @@ void ospf6_asbr_update_route_ecmp_path(struct ospf6_route *old, AS_EXTERNAL)) { prefix2str(&old_route->prefix, buf, sizeof(buf)); - zlog_debug("%s: ls_prfix %s asbr_entry not found.", - __PRETTY_FUNCTION__, - buf); + zlog_debug( + "%s: ls_prfix %s asbr_entry not found.", + __func__, buf); } continue; } @@ -422,12 +421,15 @@ void ospf6_asbr_update_route_ecmp_path(struct ospf6_route *old, if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) { prefix2str(&route->prefix, buf, sizeof(buf)); - zlog_debug("%s: route %s with effective paths %u nh %u", - __PRETTY_FUNCTION__, buf, - old_route->paths ? - listcount(old_route->paths) : 0, - old_route->nh_list ? - listcount(old_route->nh_list) : 0); + zlog_debug( + "%s: route %s with effective paths %u nh %u", + __func__, buf, + old_route->paths + ? listcount(old_route->paths) + : 0, + old_route->nh_list + ? listcount(old_route->nh_list) + : 0); } /* Update RIB/FIB */ @@ -532,7 +534,7 @@ void ospf6_asbr_lsa_add(struct ospf6_lsa *lsa) if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) { prefix2str(&route->prefix, buf, sizeof(buf)); zlog_debug("%s: AS-External %u route add %s cost %u(%u) nh %u", - __PRETTY_FUNCTION__, + __func__, (route->path.type == OSPF6_PATH_TYPE_EXTERNAL1) ? 1 : 2, buf, route->path.cost, route->path.u.cost_e2, @@ -559,7 +561,6 @@ void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa, struct ospf6_as_external_lsa *external; struct prefix prefix; struct ospf6_route *route, *nroute, *route_to_del; - char buf[PREFIX2STR_BUFFER]; external = (struct ospf6_as_external_lsa *)OSPF6_LSA_HEADER_END( lsa->header); @@ -609,8 +610,7 @@ void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa, route = ospf6_route_lookup(&prefix, ospf6->route_table); if (route == NULL) { if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) { - prefix2str(&prefix, buf, sizeof(buf)); - zlog_debug("AS-External route %s not found", buf); + zlog_debug("AS-External route %pFX not found", &prefix); } ospf6_route_delete(route_to_del); @@ -618,12 +618,10 @@ void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa, } if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) { - prefix2str(&prefix, buf, sizeof(buf)); zlog_debug( - "%s: Current route %s cost %u e2 %u, route to del cost %u e2 %u", - __PRETTY_FUNCTION__, buf, route->path.cost, - route->path.u.cost_e2, route_to_del->path.cost, - route_to_del->path.u.cost_e2); + "%s: Current route %pFX cost %u e2 %u, route to del cost %u e2 %u", + __func__, &prefix, route->path.cost, route->path.u.cost_e2, + route_to_del->path.cost, route_to_del->path.u.cost_e2); } for (ospf6_route_lock(route); @@ -666,12 +664,10 @@ void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa, .cost_e2)) { if (IS_OSPF6_DEBUG_EXAMIN( AS_EXTERNAL)) { - prefix2str(&prefix, buf, - sizeof(buf)); zlog_debug( - "%s: route %s to delete is not same, cost %u del cost %u. skip", - __PRETTY_FUNCTION__, - buf, route->path.cost, + "%s: route %pFX to delete is not same, cost %u del cost %u. skip", + __func__, &prefix, + route->path.cost, route_to_del->path .cost); } @@ -679,11 +675,9 @@ void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa, } if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) { - prefix2str(&prefix, buf, sizeof(buf)); zlog_debug( - "%s: route %s path found with cost %u nh %u to remove.", - __PRETTY_FUNCTION__, buf, - route->path.cost, + "%s: route %pFX path found with cost %u nh %u to remove.", + __func__, &prefix, route->path.cost, listcount(o_path->nh_list)); } @@ -722,18 +716,17 @@ void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa, } if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) { - prefix2str(&route->prefix, buf, - sizeof(buf)); zlog_debug( - "%s: AS-External %u route %s update paths %u nh %u", - __PRETTY_FUNCTION__, + "%s: AS-External %u route %pFX update paths %u nh %u", + __func__, (route->path.type == OSPF6_PATH_TYPE_EXTERNAL1) ? 1 : 2, - buf, listcount(route->paths), - route->nh_list ? - listcount(route->nh_list) : 0); + &route->prefix, listcount(route->paths), + route->nh_list ? listcount( + route->nh_list) + : 0); } if (listcount(route->paths)) { @@ -782,11 +775,9 @@ void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa, || (route->path.u.cost_e2 != route_to_del->path.u.cost_e2))) { if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) { - prefix2str(&prefix, buf, sizeof(buf)); zlog_debug( - "%s: route %s to delete is not same, cost %u del cost %u. skip", - __PRETTY_FUNCTION__, buf, - route->path.cost, + "%s: route %pFX to delete is not same, cost %u del cost %u. skip", + __func__, &prefix, route->path.cost, route_to_del->path.cost); } continue; @@ -799,14 +790,13 @@ void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa, continue; } if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) { - prefix2str(&route->prefix, buf, sizeof(buf)); zlog_debug( - "%s: AS-External %u route remove %s cost %u(%u) nh %u", - __PRETTY_FUNCTION__, + "%s: AS-External %u route remove %pFX cost %u(%u) nh %u", + __func__, route->path.type == OSPF6_PATH_TYPE_EXTERNAL1 ? 1 : 2, - buf, route->path.cost, route->path.u.cost_e2, + &route->prefix, route->path.cost, route->path.u.cost_e2, listcount(route->nh_list)); } ospf6_route_remove(route, ospf6->route_table); @@ -892,12 +882,11 @@ static int ospf6_asbr_routemap_update_timer(struct thread *thread) if (ospf6->rmap[arg_type].map) { if (IS_OSPF6_DEBUG_ASBR) zlog_debug("%s: route-map %s update, reset redist %s", - __PRETTY_FUNCTION__, - ospf6->rmap[arg_type].name, + __func__, ospf6->rmap[arg_type].name, ZROUTE_NAME(arg_type)); - ospf6_zebra_no_redistribute(arg_type); - ospf6_zebra_redistribute(arg_type); + ospf6_zebra_no_redistribute(arg_type, ospf6->vrf_id); + ospf6_zebra_redistribute(arg_type, ospf6->vrf_id); } XFREE(MTYPE_OSPF6_DIST_ARGS, arg); @@ -917,12 +906,12 @@ void ospf6_asbr_distribute_list_update(int type) args[1] = (void *)((ptrdiff_t)type); if (IS_OSPF6_DEBUG_ASBR) - zlog_debug("%s: trigger redistribute %s reset thread", - __PRETTY_FUNCTION__, ZROUTE_NAME(type)); + zlog_debug("%s: trigger redistribute %s reset thread", __func__, + ZROUTE_NAME(type)); ospf6->t_distribute_update = NULL; - thread_add_timer_msec(master, ospf6_asbr_routemap_update_timer, - (void **)args, OSPF_MIN_LS_INTERVAL, + thread_add_timer_msec(master, ospf6_asbr_routemap_update_timer, args, + OSPF_MIN_LS_INTERVAL, &ospf6->t_distribute_update); } @@ -938,25 +927,44 @@ static void ospf6_asbr_routemap_update(const char *mapname) ospf6->rmap[type].map = route_map_lookup_by_name( ospf6->rmap[type].name); - if (mapname && ospf6->rmap[type].map + if (mapname && (strcmp(ospf6->rmap[type].name, mapname) == 0)) { - if (IS_OSPF6_DEBUG_ASBR) - zlog_debug( - "%s: route-map %s update, reset redist %s", - __PRETTY_FUNCTION__, mapname, - ZROUTE_NAME(type)); + if (ospf6->rmap[type].map) { + if (IS_OSPF6_DEBUG_ASBR) + zlog_debug( + "%s: route-map %s update, reset redist %s", + __func__, mapname, + ZROUTE_NAME(type)); - route_map_counter_increment( - ospf6->rmap[type].map); + route_map_counter_increment( + ospf6->rmap[type].map); - ospf6_asbr_distribute_list_update(type); + ospf6_asbr_distribute_list_update(type); + } else { + /* + * if the mapname matches a route-map on + * ospf6 but the map doesn't exist, it + * is being deleted. flush and then + * readvertise + */ + if (IS_OSPF6_DEBUG_ASBR) + zlog_debug( + "%s: route-map %s deleted, reset redist %s", + __func__, mapname, + ZROUTE_NAME(type)); + ospf6_asbr_redistribute_unset( + type, ospf6->vrf_id); + ospf6_asbr_routemap_set(type, mapname); + ospf6_asbr_redistribute_set( + type, ospf6->vrf_id); + } } } else ospf6->rmap[type].map = NULL; } } -static void ospf6_asbr_routemap_event(route_map_event_t event, const char *name) +static void ospf6_asbr_routemap_event(const char *name) { int type; @@ -975,17 +983,17 @@ int ospf6_asbr_is_asbr(struct ospf6 *o) return o->external_table->count; } -static void ospf6_asbr_redistribute_set(int type) +static void ospf6_asbr_redistribute_set(int type, vrf_id_t vrf_id) { - ospf6_zebra_redistribute(type); + ospf6_zebra_redistribute(type, vrf_id); } -static void ospf6_asbr_redistribute_unset(int type) +static void ospf6_asbr_redistribute_unset(int type, vrf_id_t vrf_id) { struct ospf6_route *route; struct ospf6_external_info *info; - ospf6_zebra_no_redistribute(type); + ospf6_zebra_no_redistribute(type, vrf_id); for (route = ospf6_route_head(ospf6->external_table); route; route = ospf6_route_next(route)) { @@ -1018,7 +1026,7 @@ void ospf6_asbr_redistribute_add(int type, ifindex_t ifindex, unsigned int nexthop_num, struct in6_addr *nexthop, route_tag_t tag) { - int ret; + route_map_result_t ret; struct ospf6_route troute; struct ospf6_external_info tinfo; struct ospf6_route *route, *match; @@ -1029,7 +1037,7 @@ void ospf6_asbr_redistribute_add(int type, ifindex_t ifindex, struct listnode *lnode, *lnnode; struct ospf6_area *oa; - if (!ospf6_zebra_is_redistribute(type)) + if (!ospf6_zebra_is_redistribute(type, ospf6->vrf_id)) return; memset(&troute, 0, sizeof(troute)); @@ -1064,6 +1072,7 @@ void ospf6_asbr_redistribute_add(int type, ifindex_t ifindex, if (IS_OSPF6_DEBUG_ASBR) zlog_debug("Denied by route-map \"%s\"", ospf6->rmap[type].name); + ospf6_asbr_redistribute_remove(type, ifindex, prefix); return; } } @@ -1240,13 +1249,15 @@ DEFUN (ospf6_redistribute, { int type; + OSPF6_CMD_CHECK_RUNNING(); + char *proto = argv[argc - 1]->text; type = proto_redistnum(AFI_IP6, proto); if (type < 0) return CMD_WARNING_CONFIG_FAILED; - ospf6_asbr_redistribute_unset(type); - ospf6_asbr_redistribute_set(type); + ospf6_asbr_redistribute_unset(type, ospf6->vrf_id); + ospf6_asbr_redistribute_set(type, ospf6->vrf_id); return CMD_SUCCESS; } @@ -1262,14 +1273,16 @@ DEFUN (ospf6_redistribute_routemap, int idx_word = 3; int type; + OSPF6_CMD_CHECK_RUNNING(); + char *proto = argv[idx_protocol]->text; type = proto_redistnum(AFI_IP6, proto); if (type < 0) return CMD_WARNING_CONFIG_FAILED; - ospf6_asbr_redistribute_unset(type); + ospf6_asbr_redistribute_unset(type, ospf6->vrf_id); ospf6_asbr_routemap_set(type, argv[idx_word]->arg); - ospf6_asbr_redistribute_set(type); + ospf6_asbr_redistribute_set(type, ospf6->vrf_id); return CMD_SUCCESS; } @@ -1285,12 +1298,14 @@ DEFUN (no_ospf6_redistribute, int idx_protocol = 2; int type; + OSPF6_CMD_CHECK_RUNNING(); + char *proto = argv[idx_protocol]->text; type = proto_redistnum(AFI_IP6, proto); if (type < 0) return CMD_WARNING_CONFIG_FAILED; - ospf6_asbr_redistribute_unset(type); + ospf6_asbr_redistribute_unset(type, ospf6->vrf_id); return CMD_SUCCESS; } @@ -1302,7 +1317,7 @@ int ospf6_redistribute_config_write(struct vty *vty) for (type = 0; type < ZEBRA_ROUTE_MAX; type++) { if (type == ZEBRA_ROUTE_OSPF6) continue; - if (!ospf6_zebra_is_redistribute(type)) + if (!ospf6_zebra_is_redistribute(type, ospf6->vrf_id)) continue; if (ospf6->rmap[type].name) @@ -1337,7 +1352,7 @@ static void ospf6_redistribute_show_config(struct vty *vty) for (type = 0; type < ZEBRA_ROUTE_MAX; type++) { if (type == ZEBRA_ROUTE_OSPF6) continue; - if (!ospf6_zebra_is_redistribute(type)) + if (!ospf6_zebra_is_redistribute(type, ospf6->vrf_id)) continue; if (ospf6->rmap[type].name) @@ -1355,7 +1370,7 @@ static void ospf6_redistribute_show_config(struct vty *vty) /* Routemap Functions */ -static route_map_result_t +static enum route_map_cmd_result_t ospf6_routemap_rule_match_address_prefixlist(void *rule, const struct prefix *prefix, route_map_object_t type, @@ -1385,7 +1400,8 @@ static void ospf6_routemap_rule_match_address_prefixlist_free(void *rule) XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); } -struct route_map_rule_cmd ospf6_routemap_rule_match_address_prefixlist_cmd = { +static const struct route_map_rule_cmd + ospf6_routemap_rule_match_address_prefixlist_cmd = { "ipv6 address prefix-list", ospf6_routemap_rule_match_address_prefixlist, ospf6_routemap_rule_match_address_prefixlist_compile, @@ -1395,7 +1411,7 @@ struct route_map_rule_cmd ospf6_routemap_rule_match_address_prefixlist_cmd = { /* `match interface IFNAME' */ /* Match function should return 1 if match is success else return zero. */ -static route_map_result_t +static enum route_map_cmd_result_t ospf6_routemap_rule_match_interface(void *rule, const struct prefix *prefix, route_map_object_t type, void *object) { @@ -1404,7 +1420,7 @@ ospf6_routemap_rule_match_interface(void *rule, const struct prefix *prefix, if (type == RMAP_OSPF6) { ei = ((struct ospf6_route *)object)->route_option; - ifp = if_lookup_by_name((char *)rule, VRF_DEFAULT); + ifp = if_lookup_by_name_all_vrf((char *)rule); if (ifp != NULL && ei->ifindex == ifp->ifindex) return RMAP_MATCH; @@ -1427,16 +1443,18 @@ static void ospf6_routemap_rule_match_interface_free(void *rule) } /* Route map commands for interface matching. */ -struct route_map_rule_cmd ospf6_routemap_rule_match_interface_cmd = { - "interface", ospf6_routemap_rule_match_interface, +static const struct route_map_rule_cmd + ospf6_routemap_rule_match_interface_cmd = { + "interface", + ospf6_routemap_rule_match_interface, ospf6_routemap_rule_match_interface_compile, - ospf6_routemap_rule_match_interface_free}; + ospf6_routemap_rule_match_interface_free +}; /* Match function for matching route tags */ -static route_map_result_t ospf6_routemap_rule_match_tag(void *rule, - const struct prefix *p, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +ospf6_routemap_rule_match_tag(void *rule, const struct prefix *p, + route_map_object_t type, void *object) { route_tag_t *tag = rule; struct ospf6_route *route = object; @@ -1448,12 +1466,15 @@ static route_map_result_t ospf6_routemap_rule_match_tag(void *rule, return RMAP_NOMATCH; } -static struct route_map_rule_cmd ospf6_routemap_rule_match_tag_cmd = { - "tag", ospf6_routemap_rule_match_tag, route_map_rule_tag_compile, +static const struct route_map_rule_cmd + ospf6_routemap_rule_match_tag_cmd = { + "tag", + ospf6_routemap_rule_match_tag, + route_map_rule_tag_compile, route_map_rule_tag_free, }; -static route_map_result_t +static enum route_map_cmd_result_t ospf6_routemap_rule_set_metric_type(void *rule, const struct prefix *prefix, route_map_object_t type, void *object) { @@ -1483,13 +1504,15 @@ static void ospf6_routemap_rule_set_metric_type_free(void *rule) XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); } -struct route_map_rule_cmd ospf6_routemap_rule_set_metric_type_cmd = { - "metric-type", ospf6_routemap_rule_set_metric_type, +static const struct route_map_rule_cmd + ospf6_routemap_rule_set_metric_type_cmd = { + "metric-type", + ospf6_routemap_rule_set_metric_type, ospf6_routemap_rule_set_metric_type_compile, ospf6_routemap_rule_set_metric_type_free, }; -static route_map_result_t +static enum route_map_cmd_result_t ospf6_routemap_rule_set_metric(void *rule, const struct prefix *prefix, route_map_object_t type, void *object) { @@ -1518,13 +1541,15 @@ static void ospf6_routemap_rule_set_metric_free(void *rule) XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); } -struct route_map_rule_cmd ospf6_routemap_rule_set_metric_cmd = { - "metric", ospf6_routemap_rule_set_metric, +static const struct route_map_rule_cmd + ospf6_routemap_rule_set_metric_cmd = { + "metric", + ospf6_routemap_rule_set_metric, ospf6_routemap_rule_set_metric_compile, ospf6_routemap_rule_set_metric_free, }; -static route_map_result_t +static enum route_map_cmd_result_t ospf6_routemap_rule_set_forwarding(void *rule, const struct prefix *prefix, route_map_object_t type, void *object) { @@ -1556,16 +1581,17 @@ static void ospf6_routemap_rule_set_forwarding_free(void *rule) XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); } -struct route_map_rule_cmd ospf6_routemap_rule_set_forwarding_cmd = { - "forwarding-address", ospf6_routemap_rule_set_forwarding, +static const struct route_map_rule_cmd + ospf6_routemap_rule_set_forwarding_cmd = { + "forwarding-address", + ospf6_routemap_rule_set_forwarding, ospf6_routemap_rule_set_forwarding_compile, ospf6_routemap_rule_set_forwarding_free, }; -static route_map_result_t ospf6_routemap_rule_set_tag(void *rule, - const struct prefix *p, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +ospf6_routemap_rule_set_tag(void *rule, const struct prefix *p, + route_map_object_t type, void *object) { route_tag_t *tag = rule; struct ospf6_route *route = object; @@ -1578,22 +1604,23 @@ static route_map_result_t ospf6_routemap_rule_set_tag(void *rule, return RMAP_OKAY; } -static struct route_map_rule_cmd ospf6_routemap_rule_set_tag_cmd = { - "tag", ospf6_routemap_rule_set_tag, route_map_rule_tag_compile, +static const struct route_map_rule_cmd + ospf6_routemap_rule_set_tag_cmd = { + "tag", + ospf6_routemap_rule_set_tag, + route_map_rule_tag_compile, route_map_rule_tag_free, }; -static int route_map_command_status(struct vty *vty, int ret) +static int route_map_command_status(struct vty *vty, enum rmap_compile_rets ret) { switch (ret) { case RMAP_RULE_MISSING: vty_out(vty, "OSPF6 Can't find rule.\n"); return CMD_WARNING_CONFIG_FAILED; - break; case RMAP_COMPILE_ERROR: vty_out(vty, "OSPF6 Argument is malformed.\n"); return CMD_WARNING_CONFIG_FAILED; - break; case RMAP_COMPILE_SUCCESS: break; } @@ -1612,8 +1639,10 @@ DEFUN (ospf6_routemap_set_metric_type, { VTY_DECLVAR_CONTEXT(route_map_index, route_map_index); int idx_external = 2; - int ret = route_map_add_set(route_map_index, "metric-type", - argv[idx_external]->arg); + enum rmap_compile_rets ret = route_map_add_set(route_map_index, + "metric-type", + argv[idx_external]->arg); + return route_map_command_status(vty, ret); } @@ -1629,7 +1658,9 @@ DEFUN (ospf6_routemap_no_set_metric_type, { VTY_DECLVAR_CONTEXT(route_map_index, route_map_index); char *ext = (argc == 4) ? argv[3]->text : NULL; - int ret = route_map_delete_set(route_map_index, "metric-type", ext); + enum rmap_compile_rets ret = route_map_delete_set(route_map_index, + "metric-type", ext); + return route_map_command_status(vty, ret); } @@ -1643,8 +1674,10 @@ DEFUN (ospf6_routemap_set_forwarding, { VTY_DECLVAR_CONTEXT(route_map_index, route_map_index); int idx_ipv6 = 2; - int ret = route_map_add_set(route_map_index, "forwarding-address", - argv[idx_ipv6]->arg); + enum rmap_compile_rets ret = route_map_add_set(route_map_index, + "forwarding-address", + argv[idx_ipv6]->arg); + return route_map_command_status(vty, ret); } @@ -1659,8 +1692,10 @@ DEFUN (ospf6_routemap_no_set_forwarding, { VTY_DECLVAR_CONTEXT(route_map_index, route_map_index); int idx_ipv6 = 3; - int ret = route_map_delete_set(route_map_index, "forwarding-address", - argv[idx_ipv6]->arg); + enum rmap_compile_rets ret = route_map_delete_set(route_map_index, + "forwarding-address", + argv[idx_ipv6]->arg); + return route_map_command_status(vty, ret); } @@ -1836,7 +1871,7 @@ DEFUN (show_ipv6_ospf6_redistribute, return CMD_SUCCESS; } -struct ospf6_lsa_handler as_external_handler = { +static struct ospf6_lsa_handler as_external_handler = { .lh_type = OSPF6_LSTYPE_AS_EXTERNAL, .lh_name = "AS-External", .lh_short_name = "ASE", @@ -1857,15 +1892,15 @@ void ospf6_asbr_init(void) install_element(OSPF6_NODE, &no_ospf6_redistribute_cmd); } -void ospf6_asbr_redistribute_reset(void) +void ospf6_asbr_redistribute_reset(vrf_id_t vrf_id) { int type; for (type = 0; type < ZEBRA_ROUTE_MAX; type++) { if (type == ZEBRA_ROUTE_OSPF6) continue; - if (ospf6_zebra_is_redistribute(type)) - ospf6_asbr_redistribute_unset(type); + if (ospf6_zebra_is_redistribute(type, vrf_id)) + ospf6_asbr_redistribute_unset(type, vrf_id); } } diff --git a/ospf6d/ospf6_asbr.h b/ospf6d/ospf6_asbr.h index 9890ef0619..41b1ac70e9 100644 --- a/ospf6d/ospf6_asbr.h +++ b/ospf6d/ospf6_asbr.h @@ -88,7 +88,7 @@ extern void ospf6_asbr_redistribute_remove(int type, ifindex_t ifindex, extern int ospf6_redistribute_config_write(struct vty *vty); extern void ospf6_asbr_init(void); -extern void ospf6_asbr_redistribute_reset(void); +extern void ospf6_asbr_redistribute_reset(vrf_id_t vrf_id); extern void ospf6_asbr_terminate(void); extern void ospf6_asbr_send_externals_to_area(struct ospf6_area *); diff --git a/ospf6d/ospf6_bfd.c b/ospf6d/ospf6_bfd.c index e7284a6659..1b58cd14f6 100644 --- a/ospf6d/ospf6_bfd.c +++ b/ospf6d/ospf6_bfd.c @@ -74,6 +74,7 @@ void ospf6_bfd_reg_dereg_nbr(struct ospf6_neighbor *on, int command) struct interface *ifp = oi->interface; struct bfd_info *bfd_info; char src[64]; + int cbit; if (!oi->bfd_info || !on->bfd_info) return; @@ -85,9 +86,11 @@ void ospf6_bfd_reg_dereg_nbr(struct ospf6_neighbor *on, int command) bfd_get_command_dbg_str(command), src); } + cbit = CHECK_FLAG(bfd_info->flags, BFD_FLAG_BFD_CBIT_ON); + bfd_peer_sendmsg(zclient, bfd_info, AF_INET6, &on->linklocal_addr, - on->ospf6_if->linklocal_addr, ifp->name, 0, 0, command, - 0, VRF_DEFAULT); + on->ospf6_if->linklocal_addr, ifp->name, 0, 0, cbit, + command, 0, ifp->vrf_id); if (command == ZEBRA_BFD_DEST_DEREGISTER) bfd_info_free((struct bfd_info **)&on->bfd_info); @@ -138,10 +141,9 @@ static void ospf6_bfd_reg_dereg_all_nbr(struct ospf6_interface *oi, int command) * ospf6_bfd_nbr_replay - Replay all the neighbors that have BFD enabled * to zebra */ -static int ospf6_bfd_nbr_replay(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ospf6_bfd_nbr_replay(ZAPI_CALLBACK_ARGS) { - struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT); + struct vrf *vrf = vrf_lookup_by_id(vrf_id); struct listnode *node; struct interface *ifp; struct ospf6_interface *oi; @@ -152,7 +154,7 @@ static int ospf6_bfd_nbr_replay(int command, struct zclient *zclient, zlog_debug("Zebra: BFD Dest replay request"); /* Send the client registration */ - bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER); + bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, vrf_id); /* Replay the neighbor, if BFD is enabled on the interface*/ FOR_ALL_INTERFACES (vrf, ifp) { @@ -182,8 +184,7 @@ static int ospf6_bfd_nbr_replay(int command, struct zclient *zclient, * has changed and bring down the neighbor * connectivity if BFD down is received. */ -static int ospf6_bfd_interface_dest_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ospf6_bfd_interface_dest_update(ZAPI_CALLBACK_ARGS) { struct interface *ifp; struct ospf6_interface *oi; @@ -197,7 +198,8 @@ static int ospf6_bfd_interface_dest_update(int command, struct zclient *zclient, struct bfd_info *bfd_info; struct timeval tv; - ifp = bfd_get_peer_info(zclient->ibuf, &dp, &sp, &status, vrf_id); + ifp = bfd_get_peer_info(zclient->ibuf, &dp, &sp, &status, + NULL, vrf_id); if ((ifp == NULL) || (dp.family != AF_INET6)) return 0; @@ -234,7 +236,7 @@ static int ospf6_bfd_interface_dest_update(int command, struct zclient *zclient, continue; old_status = bfd_info->status; - bfd_info->status = status; + BFD_SET_CLIENT_STATUS(bfd_info->status, status); monotime(&tv); bfd_info->last_update = tv.tv_sec; @@ -306,7 +308,7 @@ static void ospf6_bfd_if_param_set(struct ospf6_interface *oi, uint32_t min_rx, int command = 0; bfd_set_param((struct bfd_info **)&(oi->bfd_info), min_rx, min_tx, - detect_mult, defaults, &command); + detect_mult, NULL, defaults, &command); if (command) ospf6_bfd_reg_dereg_all_nbr(oi, command); } diff --git a/ospf6d/ospf6_flood.c b/ospf6d/ospf6_flood.c index 0828c2beb6..56aac0f21f 100644 --- a/ospf6d/ospf6_flood.c +++ b/ospf6d/ospf6_flood.c @@ -258,8 +258,8 @@ void ospf6_install_lsa(struct ospf6_lsa *lsa) if (IS_OSPF6_DEBUG_LSA_TYPE(lsa->header->type) || IS_OSPF6_DEBUG_EXAMIN_TYPE(lsa->header->type)) zlog_debug("%s Install LSA: %s age %d seqnum %x in LSDB.", - __PRETTY_FUNCTION__, lsa->name, - ntohs(lsa->header->age), ntohl(lsa->header->seqnum)); + __func__, lsa->name, ntohs(lsa->header->age), + ntohl(lsa->header->seqnum)); /* actually install */ lsa->installed = now; @@ -332,11 +332,12 @@ void ospf6_flood_interface(struct ospf6_neighbor *from, struct ospf6_lsa *lsa, if (req == on->last_ls_req) { /* sanity check refcount */ assert(req->lock >= 2); - ospf6_lsa_unlock(req); + req = ospf6_lsa_unlock(req); on->last_ls_req = NULL; } - ospf6_lsdb_remove(req, - on->request_list); + if (req) + ospf6_lsdb_remove( + req, on->request_list); ospf6_check_nbr_loading(on); continue; } @@ -348,7 +349,7 @@ void ospf6_flood_interface(struct ospf6_neighbor *from, struct ospf6_lsa *lsa, zlog_debug( "Received is newer, remove requesting"); if (req == on->last_ls_req) { - ospf6_lsa_unlock(req); + req = ospf6_lsa_unlock(req); on->last_ls_req = NULL; } if (req) @@ -373,7 +374,7 @@ void ospf6_flood_interface(struct ospf6_neighbor *from, struct ospf6_lsa *lsa, if (is_debug) zlog_debug( "%s: Send LSA %s (age %d) update now", - __PRETTY_FUNCTION__, lsa->name, + __func__, lsa->name, ntohs(lsa->header->age)); ospf6_lsupdate_send_neighbor_now(on, lsa); continue; @@ -842,18 +843,6 @@ void ospf6_receive_lsa(struct ospf6_neighbor *from, zlog_debug("Received is duplicated LSA"); SET_FLAG(new->flag, OSPF6_LSA_DUPLICATE); } - if (old->header->adv_router - == from->ospf6_if->area->ospf6->router_id - && OSPF6_LSA_IS_MAXAGE(new)) { - ospf6_acknowledge_lsa(new, ismore_recent, from); - ospf6_lsa_delete(new); - if (is_debug) - zlog_debug( - "%s: Received is self orig MAXAGE LSA %s, discard (ismore_recent %d)", - __PRETTY_FUNCTION__, old->name, - ismore_recent); - return; - } } /* if no database copy or received is more recent */ @@ -1012,18 +1001,22 @@ void ospf6_receive_lsa(struct ospf6_neighbor *from, * MAXAGEd and not removed.*/ if (OSPF6_LSA_IS_MAXAGE(old) && !OSPF6_LSA_IS_MAXAGE(new)) { - - if (is_debug) - zlog_debug( - "%s: Current copy of LSA %s is MAXAGE, but new has recent Age.", - old->name, __PRETTY_FUNCTION__); - - ospf6_lsa_purge(old); if (new->header->adv_router - != from->ospf6_if->area->ospf6->router_id) + != from->ospf6_if->area->ospf6->router_id) { + if (is_debug) + zlog_debug( + "%s: Current copy of LSA %s is MAXAGE, but new has recent age, flooding/installing.", + old->name, __PRETTY_FUNCTION__); + ospf6_lsa_purge(old); ospf6_flood(from, new); - - ospf6_install_lsa(new); + ospf6_install_lsa(new); + } else { + if (is_debug) + zlog_debug( + "%s: Current copy of self-originated LSA %s is MAXAGE, but new has recent age, ignoring new.", + old->name, __PRETTY_FUNCTION__); + ospf6_lsa_delete(new); + } return; } diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c index 692c84ad08..e8be12c9b6 100644 --- a/ospf6d/ospf6_interface.c +++ b/ospf6d/ospf6_interface.c @@ -42,6 +42,7 @@ #include "ospf6_spf.h" #include "ospf6d.h" #include "ospf6_bfd.h" +#include "ospf6_zebra.h" DEFINE_MTYPE_STATIC(OSPF6D, CFG_PLIST_NAME, "configured prefix list names") DEFINE_QOBJ_TYPE(ospf6_interface) @@ -51,16 +52,17 @@ DEFINE_HOOK(ospf6_interface_change, unsigned char conf_debug_ospf6_interface = 0; -const char *ospf6_interface_state_str[] = { +const char *const ospf6_interface_state_str[] = { "None", "Down", "Loopback", "Waiting", "PointToPoint", "DROther", "BDR", "DR", NULL}; -struct ospf6_interface *ospf6_interface_lookup_by_ifindex(ifindex_t ifindex) +struct ospf6_interface *ospf6_interface_lookup_by_ifindex(ifindex_t ifindex, + vrf_id_t vrf_id) { struct ospf6_interface *oi; struct interface *ifp; - ifp = if_lookup_by_index(ifindex, VRF_DEFAULT); + ifp = if_lookup_by_index(ifindex, vrf_id); if (ifp == NULL) return (struct ospf6_interface *)NULL; @@ -246,6 +248,7 @@ void ospf6_interface_delete(struct ospf6_interface *oi) THREAD_OFF(oi->thread_send_lsupdate); THREAD_OFF(oi->thread_send_lsack); THREAD_OFF(oi->thread_sso); + THREAD_OFF(oi->thread_wait_timer); ospf6_lsdb_remove_all(oi->lsdb); ospf6_lsdb_remove_all(oi->lsupdate_list); @@ -300,6 +303,7 @@ void ospf6_interface_disable(struct ospf6_interface *oi) THREAD_OFF(oi->thread_link_lsa); THREAD_OFF(oi->thread_intra_prefix_lsa); THREAD_OFF(oi->thread_as_extern_lsa); + THREAD_OFF(oi->thread_wait_timer); } static struct in6_addr * @@ -355,8 +359,6 @@ void ospf6_interface_state_update(struct interface *ifp) oi = (struct ospf6_interface *)ifp->info; if (oi == NULL) return; - if (oi->area == NULL) - return; if (CHECK_FLAG(oi->flag, OSPF6_INTERFACE_DISABLE)) return; @@ -505,6 +507,7 @@ static void ospf6_interface_state_change(uint8_t next_state, IPV6_JOIN_GROUP); OSPF6_ROUTER_LSA_SCHEDULE(oi->area); + OSPF6_LINK_LSA_SCHEDULE(oi); if (next_state == OSPF6_INTERFACE_DOWN) { OSPF6_NETWORK_LSA_EXECUTE(oi); OSPF6_INTRA_PREFIX_LSA_EXECUTE_TRANSIT(oi); @@ -683,6 +686,9 @@ int interface_up(struct thread *thread) oi = (struct ospf6_interface *)THREAD_ARG(thread); assert(oi && oi->interface); + if (!oi->type_cfg) + oi->type = ospf6_default_iftype(oi->interface); + /* * Remove old pointer. If this thread wasn't a timer this * operation won't make a difference, because it is already NULL. @@ -774,15 +780,14 @@ int interface_up(struct thread *thread) } /* decide next interface state */ - if ((if_is_pointopoint(oi->interface)) - || (oi->type == OSPF_IFTYPE_POINTOPOINT)) { + if (oi->type == OSPF_IFTYPE_POINTOPOINT) { ospf6_interface_state_change(OSPF6_INTERFACE_POINTTOPOINT, oi); } else if (oi->priority == 0) ospf6_interface_state_change(OSPF6_INTERFACE_DROTHER, oi); else { ospf6_interface_state_change(OSPF6_INTERFACE_WAITING, oi); thread_add_timer(master, wait_timer, oi, oi->dead_interval, - NULL); + &oi->thread_wait_timer); } return 0; @@ -880,6 +885,19 @@ int interface_down(struct thread *thread) } +static const char *ospf6_iftype_str(uint8_t iftype) +{ + switch (iftype) { + case OSPF_IFTYPE_LOOPBACK: + return "LOOPBACK"; + case OSPF_IFTYPE_BROADCAST: + return "BROADCAST"; + case OSPF_IFTYPE_POINTOPOINT: + return "POINTOPOINT"; + } + return "UNKNOWN"; +} + /* show specified interface structure */ static int ospf6_interface_show(struct vty *vty, struct interface *ifp) { @@ -888,23 +906,16 @@ static int ospf6_interface_show(struct vty *vty, struct interface *ifp) struct prefix *p; struct listnode *i; char strbuf[PREFIX2STR_BUFFER], drouter[32], bdrouter[32]; - const char *type; + uint8_t default_iftype; struct timeval res, now; char duration[32]; struct ospf6_lsa *lsa; - /* check physical interface type */ - if (if_is_loopback(ifp)) - type = "LOOPBACK"; - else if (if_is_broadcast(ifp)) - type = "BROADCAST"; - else if (if_is_pointopoint(ifp)) - type = "POINTOPOINT"; - else - type = "UNKNOWN"; + default_iftype = ospf6_default_iftype(ifp); vty_out(vty, "%s is %s, type %s\n", ifp->name, - (if_is_operative(ifp) ? "up" : "down"), type); + (if_is_operative(ifp) ? "up" : "down"), + ospf6_iftype_str(default_iftype)); vty_out(vty, " Interface ID: %d\n", ifp->ifindex); if (ifp->info == NULL) { @@ -913,6 +924,10 @@ static int ospf6_interface_show(struct vty *vty, struct interface *ifp) } else oi = (struct ospf6_interface *)ifp->info; + if (if_is_operative(ifp) && oi->type != default_iftype) + vty_out(vty, " Operating as type %s\n", + ospf6_iftype_str(oi->type)); + vty_out(vty, " Internet Address:\n"); for (ALL_LIST_ELEMENTS_RO(ifp->connected, i, c)) { @@ -1011,7 +1026,7 @@ DEFUN (show_ipv6_ospf6_interface, return CMD_SUCCESS; } -static int ospf6_interface_show_traffic(struct vty *vty, uint32_t vrf_id, +static int ospf6_interface_show_traffic(struct vty *vty, struct interface *intf_ifp, int display_once) { @@ -1019,7 +1034,10 @@ static int ospf6_interface_show_traffic(struct vty *vty, uint32_t vrf_id, struct vrf *vrf = NULL; struct ospf6_interface *oi = NULL; - vrf = vrf_lookup_by_id(vrf_id); + if (intf_ifp) + vrf = vrf_lookup_by_id(intf_ifp->vrf_id); + else + vrf = vrf_lookup_by_id(VRF_DEFAULT); if (!display_once) { vty_out(vty, "\n"); @@ -1094,7 +1112,7 @@ DEFUN (show_ipv6_ospf6_interface_traffic, } } - ospf6_interface_show_traffic(vty, VRF_DEFAULT, ifp, display_once); + ospf6_interface_show_traffic(vty, ifp, display_once); return CMD_SUCCESS; @@ -1138,6 +1156,12 @@ DEFUN (show_ipv6_ospf6_interface_ifname_prefix, return CMD_WARNING; } + if (CHECK_FLAG(oi->flag, OSPF6_INTERFACE_DISABLE)) { + vty_out(vty, "Interface %s not attached to area\n", + argv[idx_ifname]->arg); + return CMD_WARNING; + } + ospf6_route_table_show(vty, idx_prefix, argc, argv, oi->route_connected); @@ -1169,7 +1193,7 @@ DEFUN (show_ipv6_ospf6_interface_prefix, FOR_ALL_INTERFACES (vrf, ifp) { oi = (struct ospf6_interface *)ifp->info; - if (oi == NULL) + if (oi == NULL || CHECK_FLAG(oi->flag, OSPF6_INTERFACE_DISABLE)) continue; ospf6_route_table_show(vty, idx_prefix, argc, argv, @@ -1309,12 +1333,11 @@ DEFUN (ipv6_ospf6_cost, return CMD_WARNING_CONFIG_FAILED; } + SET_FLAG(oi->flag, OSPF6_INTERFACE_NOAUTOCOST); if (oi->cost == lcost) return CMD_SUCCESS; oi->cost = lcost; - SET_FLAG(oi->flag, OSPF6_INTERFACE_NOAUTOCOST); - ospf6_interface_force_recalculate_cost(oi); return CMD_SUCCESS; @@ -1663,8 +1686,11 @@ DEFUN (no_ipv6_ospf6_passive, UNSET_FLAG(oi->flag, OSPF6_INTERFACE_PASSIVE); THREAD_OFF(oi->thread_send_hello); THREAD_OFF(oi->thread_sso); - thread_add_event(master, ospf6_hello_send, oi, 0, - &oi->thread_send_hello); + + /* don't send hellos over loopback interface */ + if (!if_is_loopback(oi->interface)) + thread_add_event(master, ospf6_hello_send, oi, 0, + &oi->thread_send_hello); return CMD_SUCCESS; } @@ -1809,6 +1835,8 @@ DEFUN (ipv6_ospf6_network, } assert(oi); + oi->type_cfg = true; + if (strncmp(argv[idx_network]->arg, "b", 1) == 0) { if (oi->type == OSPF_IFTYPE_BROADCAST) return CMD_SUCCESS; @@ -1849,6 +1877,8 @@ DEFUN (no_ipv6_ospf6_network, return CMD_SUCCESS; } + oi->type_cfg = false; + type = ospf6_default_iftype(ifp); if (oi->type == type) { return CMD_SUCCESS; @@ -1916,13 +1946,10 @@ static int config_write_ospf6_interface(struct vty *vty) if (oi->mtu_ignore) vty_out(vty, " ipv6 ospf6 mtu-ignore\n"); - if (oi->type != ospf6_default_iftype(ifp)) { - if (oi->type == OSPF_IFTYPE_POINTOPOINT) - vty_out(vty, - " ipv6 ospf6 network point-to-point\n"); - else if (oi->type == OSPF_IFTYPE_BROADCAST) - vty_out(vty, " ipv6 ospf6 network broadcast\n"); - } + if (oi->type_cfg && oi->type == OSPF_IFTYPE_POINTOPOINT) + vty_out(vty, " ipv6 ospf6 network point-to-point\n"); + else if (oi->type_cfg && oi->type == OSPF_IFTYPE_BROADCAST) + vty_out(vty, " ipv6 ospf6 network broadcast\n"); ospf6_bfd_write_config(vty, oi); @@ -1931,15 +1958,71 @@ static int config_write_ospf6_interface(struct vty *vty) return 0; } +static int config_write_ospf6_interface(struct vty *vty); static struct cmd_node interface_node = { - INTERFACE_NODE, "%s(config-if)# ", 1 /* VTYSH */ + .name = "interface", + .node = INTERFACE_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-if)# ", + .config_write = config_write_ospf6_interface, }; +static int ospf6_ifp_create(struct interface *ifp) +{ + if (IS_OSPF6_DEBUG_ZEBRA(RECV)) + zlog_debug("Zebra Interface add: %s index %d mtu %d", ifp->name, + ifp->ifindex, ifp->mtu6); + ospf6_interface_if_add(ifp); + + return 0; +} + +static int ospf6_ifp_up(struct interface *ifp) +{ + if (IS_OSPF6_DEBUG_ZEBRA(RECV)) + zlog_debug( + "Zebra Interface state change: %s index %d flags %llx metric %d mtu %d bandwidth %d", + ifp->name, ifp->ifindex, (unsigned long long)ifp->flags, + ifp->metric, ifp->mtu6, ifp->bandwidth); + + ospf6_interface_state_update(ifp); + + return 0; +} + +static int ospf6_ifp_down(struct interface *ifp) +{ + if (IS_OSPF6_DEBUG_ZEBRA(RECV)) + zlog_debug( + "Zebra Interface state change: %s index %d flags %llx metric %d mtu %d bandwidth %d", + ifp->name, ifp->ifindex, (unsigned long long)ifp->flags, + ifp->metric, ifp->mtu6, ifp->bandwidth); + + ospf6_interface_state_update(ifp); + + return 0; +} + +static int ospf6_ifp_destroy(struct interface *ifp) +{ + if (if_is_up(ifp)) + zlog_warn("Zebra: got delete of %s, but interface is still up", + ifp->name); + + if (IS_OSPF6_DEBUG_ZEBRA(RECV)) + zlog_debug("Zebra Interface delete: %s index %d mtu %d", + ifp->name, ifp->ifindex, ifp->mtu6); + + return 0; +} + void ospf6_interface_init(void) { /* Install interface node. */ - install_node(&interface_node, config_write_ospf6_interface); + install_node(&interface_node); if_cmd_init(); + if_zapi_callbacks(ospf6_ifp_create, ospf6_ifp_up, + ospf6_ifp_down, ospf6_ifp_destroy); install_element(VIEW_NODE, &show_ipv6_ospf6_interface_prefix_cmd); install_element(VIEW_NODE, &show_ipv6_ospf6_interface_ifname_cmd); diff --git a/ospf6d/ospf6_interface.h b/ospf6d/ospf6_interface.h index e0c39a29b4..6e4692920c 100644 --- a/ospf6d/ospf6_interface.h +++ b/ospf6d/ospf6_interface.h @@ -55,6 +55,7 @@ struct ospf6_interface { /* Network Type */ uint8_t type; + bool type_cfg; /* Router Priority */ uint8_t priority; @@ -89,10 +90,10 @@ struct ospf6_interface { uint8_t mtu_ignore; /* Decision of DR Election */ - uint32_t drouter; - uint32_t bdrouter; - uint32_t prev_drouter; - uint32_t prev_bdrouter; + in_addr_t drouter; + in_addr_t bdrouter; + in_addr_t prev_drouter; + in_addr_t prev_bdrouter; /* Linklocal LSA Database: includes Link-LSA */ struct ospf6_lsdb *lsdb; @@ -110,6 +111,7 @@ struct ospf6_interface { struct thread *thread_link_lsa; struct thread *thread_intra_prefix_lsa; struct thread *thread_as_extern_lsa; + struct thread *thread_wait_timer; struct ospf6_route_table *route_connected; @@ -147,7 +149,7 @@ DECLARE_QOBJ_TYPE(ospf6_interface) #define OSPF6_INTERFACE_DR 7 #define OSPF6_INTERFACE_MAX 8 -extern const char *ospf6_interface_state_str[]; +extern const char *const ospf6_interface_state_str[]; /* flags */ #define OSPF6_INTERFACE_DISABLE 0x01 @@ -169,7 +171,8 @@ extern const char *ospf6_interface_state_str[]; /* Function Prototypes */ -extern struct ospf6_interface *ospf6_interface_lookup_by_ifindex(ifindex_t); +extern struct ospf6_interface * +ospf6_interface_lookup_by_ifindex(ifindex_t, vrf_id_t vrf_id); extern struct ospf6_interface *ospf6_interface_create(struct interface *); extern void ospf6_interface_delete(struct ospf6_interface *); diff --git a/ospf6d/ospf6_intra.c b/ospf6d/ospf6_intra.c index 7ae7d682bd..51d34773d7 100644 --- a/ospf6d/ospf6_intra.c +++ b/ospf6d/ospf6_intra.c @@ -76,7 +76,8 @@ static char *ospf6_router_lsa_get_nbr_id(struct ospf6_lsa *lsa, char *buf, *)(start + pos * (sizeof(struct ospf6_router_lsdesc))); - if ((char *)lsdesc < end) { + if ((char *)lsdesc + sizeof(struct ospf6_router_lsdesc) + <= end) { if (buf && (buflen > INET_ADDRSTRLEN * 2)) { inet_ntop(AF_INET, &lsdesc->neighbor_interface_id, buf1, @@ -84,12 +85,13 @@ static char *ospf6_router_lsa_get_nbr_id(struct ospf6_lsa *lsa, char *buf, inet_ntop(AF_INET, &lsdesc->neighbor_router_id, buf2, sizeof(buf2)); sprintf(buf, "%s/%s", buf2, buf1); + + return buf; } - } else - return NULL; + } } - return buf; + return NULL; } static int ospf6_router_lsa_show(struct vty *vty, struct ospf6_lsa *lsa) @@ -172,13 +174,13 @@ int ospf6_router_is_stub_router(struct ospf6_lsa *lsa) + sizeof(struct ospf6_lsa_header)); if (!OSPF6_OPT_ISSET(rtr_lsa->options, OSPF6_OPT_R)) { - return (OSPF6_IS_STUB_ROUTER); + return OSPF6_IS_STUB_ROUTER; } else if (!OSPF6_OPT_ISSET(rtr_lsa->options, OSPF6_OPT_V6)) { - return (OSPF6_IS_STUB_ROUTER_V6); + return OSPF6_IS_STUB_ROUTER_V6; } } - return (OSPF6_NOT_STUB_ROUTER); + return OSPF6_NOT_STUB_ROUTER; } int ospf6_router_lsa_originate(struct thread *thread) @@ -411,14 +413,15 @@ static char *ospf6_network_lsa_get_ar_id(struct ospf6_lsa *lsa, char *buf, if ((current + sizeof(struct ospf6_network_lsdesc)) <= end) { lsdesc = (struct ospf6_network_lsdesc *)current; - if (buf) + if (buf) { inet_ntop(AF_INET, &lsdesc->router_id, buf, buflen); - } else - return NULL; + return buf; + } + } } - return (buf); + return NULL; } static int ospf6_network_lsa_show(struct vty *vty, struct ospf6_lsa *lsa) @@ -596,22 +599,21 @@ static char *ospf6_link_lsa_get_prefix_str(struct ospf6_lsa *lsa, char *buf, prefixnum = ntohl(link_lsa->prefix_num); if (pos > prefixnum) - return (NULL); + return NULL; start = (char *)link_lsa + sizeof(struct ospf6_link_lsa); end = (char *)lsa->header + ntohs(lsa->header->length); current = start; - do { + while (current + sizeof(struct ospf6_prefix) <= end) { prefix = (struct ospf6_prefix *)current; if (prefix->prefix_length == 0 || current + OSPF6_PREFIX_SIZE(prefix) > end) { - return (NULL); + return NULL; } - if (cnt < pos) { - current = - start + pos * OSPF6_PREFIX_SIZE(prefix); + if (cnt < (pos - 1)) { + current += OSPF6_PREFIX_SIZE(prefix); cnt++; } else { memset(&in6, 0, sizeof(in6)); @@ -621,9 +623,9 @@ static char *ospf6_link_lsa_get_prefix_str(struct ospf6_lsa *lsa, char *buf, inet_ntop(AF_INET6, &in6, buf, buflen); return (buf); } - } while (current <= end); + } } - return (NULL); + return NULL; } static int ospf6_link_lsa_show(struct vty *vty, struct ospf6_lsa *lsa) @@ -796,15 +798,15 @@ static char *ospf6_intra_prefix_lsa_get_prefix_str(struct ospf6_lsa *lsa, + sizeof(struct ospf6_lsa_header)); prefixnum = ntohs(intra_prefix_lsa->prefix_num); - if (pos > prefixnum) - return (NULL); + if ((pos + 1) > prefixnum) + return NULL; start = (char *)intra_prefix_lsa + sizeof(struct ospf6_intra_prefix_lsa); end = (char *)lsa->header + ntohs(lsa->header->length); current = start; - do { + while (current + sizeof(struct ospf6_prefix) <= end) { prefix = (struct ospf6_prefix *)current; if (prefix->prefix_length == 0 || current + OSPF6_PREFIX_SIZE(prefix) > end) { @@ -812,8 +814,7 @@ static char *ospf6_intra_prefix_lsa_get_prefix_str(struct ospf6_lsa *lsa, } if (cnt < pos) { - current = - start + pos * OSPF6_PREFIX_SIZE(prefix); + current += OSPF6_PREFIX_SIZE(prefix); cnt++; } else { memset(&in6, 0, sizeof(in6)); @@ -825,9 +826,9 @@ static char *ospf6_intra_prefix_lsa_get_prefix_str(struct ospf6_lsa *lsa, prefix->prefix_length); return (buf); } - } while (current <= end); + } } - return (buf); + return NULL; } static int ospf6_intra_prefix_lsa_show(struct vty *vty, struct ospf6_lsa *lsa) @@ -1022,8 +1023,9 @@ int ospf6_intra_prefix_lsa_originate_stub(struct thread *thread) */ if (oa->intra_prefix_originate) { if (IS_OSPF6_DEBUG_ORIGINATE(INTRA_PREFIX)) - zlog_debug("%s: Re-originate intra prefix LSA, Current full nbrs %u", - __PRETTY_FUNCTION__, oa->full_nbrs); + zlog_debug( + "%s: Re-originate intra prefix LSA, Current full nbrs %u", + __func__, oa->full_nbrs); if (old) ospf6_lsa_purge_multi_ls_id(oa, old); oa->intra_prefix_originate = 0; @@ -1345,6 +1347,8 @@ static void ospf6_intra_prefix_update_route_origin(struct ospf6_route *oa_route) g_route->path.origin.id = h_path->origin.id; g_route->path.origin.adv_router = h_path->origin.adv_router; + if (nroute) + ospf6_route_unlock(nroute); break; } } @@ -1367,7 +1371,7 @@ void ospf6_intra_prefix_route_ecmp_path(struct ospf6_area *oa, struct ospf6_nexthop *nh, *rnh; char buf[PREFIX2STR_BUFFER]; bool route_found = false; - struct interface *ifp; + struct interface *ifp = NULL; struct ospf6_lsa *lsa; struct ospf6_intra_prefix_lsa *intra_prefix_lsa; @@ -1399,11 +1403,10 @@ void ospf6_intra_prefix_route_ecmp_path(struct ospf6_area *oa, continue; if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) { - prefix2str(&old_route->prefix, buf, - sizeof(buf)); - zlog_debug("%s: route %s cost old %u new %u is not same, replace route", - __PRETTY_FUNCTION__, buf, - o_path->cost, route->path.cost); + zlog_debug( + "%s: route %pFX cost old %u new %u is not same, replace route", + __func__, &old_route->prefix, o_path->cost, + route->path.cost); } /* Remove selected current path's nh from @@ -1456,12 +1459,11 @@ void ospf6_intra_prefix_route_ecmp_path(struct ospf6_area *oa, } } else { if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) { - prefix2str(&old_route->prefix, buf, - sizeof(buf)); - zlog_debug("%s: route %s old cost %u new cost %u, delete old entry.", - __PRETTY_FUNCTION__, buf, - old_route->path.cost, - route->path.cost); + zlog_debug( + "%s: route %pFX old cost %u new cost %u, delete old entry.", + __func__, &old_route->prefix, + old_route->path.cost, + route->path.cost); } if (oa->route_table->hook_remove) ospf6_route_remove(old_route, @@ -1512,17 +1514,15 @@ void ospf6_intra_prefix_route_ecmp_path(struct ospf6_area *oa, listnode_add_sort(old_route->paths, ecmp_path); if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) { - prefix2str(&route->prefix, buf, - sizeof(buf)); zlog_debug( - "%s: route %s %p another path added with nh %u, effective paths %u nh %u", - __PRETTY_FUNCTION__, buf, + "%s: route %pFX %p another path added with nh %u, effective paths %u nh %u", + __func__, &route->prefix, (void *)old_route, listcount(ecmp_path->nh_list), - old_route->paths ? - listcount(old_route->paths) : 0, + old_route->paths ? listcount( + old_route->paths) + : 0, listcount(old_route->nh_list)); - } } else { list_delete_all_node(o_path->nh_list); @@ -1540,9 +1540,9 @@ void ospf6_intra_prefix_route_ecmp_path(struct ospf6_area *oa, oa->spf_table); if (ls_entry == NULL) { if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) - zlog_debug("%s: ls_prfix %s ls_entry not found.", - __PRETTY_FUNCTION__, - buf); + zlog_debug( + "%s: ls_prfix %s ls_entry not found.", + __func__, buf); continue; } lsa = ospf6_lsdb_lookup(o_path->origin.type, @@ -1559,9 +1559,9 @@ void ospf6_intra_prefix_route_ecmp_path(struct ospf6_area *oa, o_path->origin.id, &adv_prefix); prefix2str(&adv_prefix, buf, sizeof(buf)); - zlog_debug("%s: adv_router %s lsa not found", - __PRETTY_FUNCTION__, - buf); + zlog_debug( + "%s: adv_router %s lsa not found", + __func__, buf); } continue; } @@ -1572,14 +1572,22 @@ void ospf6_intra_prefix_route_ecmp_path(struct ospf6_area *oa, if (intra_prefix_lsa->ref_adv_router == oa->ospf6->router_id) { ifp = if_lookup_prefix( - &old_route->prefix, - VRF_DEFAULT); - if (ifp) - ospf6_route_add_nexthop( - old_route, + &old_route->prefix, + oa->ospf6->vrf_id); + } + + if (ifp) { + /* Nexthop interface found */ + ospf6_route_add_nexthop(old_route, ifp->ifindex, NULL); } else { + /* The connected interfaces between + * routers can be in different networks. + * In this case the matching interface + * is not found. Copy nexthops from the + * link state entry + */ ospf6_route_merge_nexthops(old_route, ls_entry); } @@ -1587,12 +1595,13 @@ void ospf6_intra_prefix_route_ecmp_path(struct ospf6_area *oa, if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) { prefix2str(&route->prefix, buf, sizeof(buf)); - zlog_debug("%s: route %s %p with final effective paths %u nh%u", - __PRETTY_FUNCTION__, buf, - (void *)old_route, - old_route->paths ? - listcount(old_route->paths) : 0, - listcount(old_route->nh_list)); + zlog_debug( + "%s: route %s %p with final effective paths %u nh%u", + __func__, buf, (void *)old_route, + old_route->paths + ? listcount(old_route->paths) + : 0, + listcount(old_route->nh_list)); } /* used in intra_route_calculation() to add to @@ -1628,7 +1637,7 @@ void ospf6_intra_prefix_lsa_add(struct ospf6_lsa *lsa) struct ospf6_prefix *op; char *start, *current, *end; char buf[PREFIX2STR_BUFFER]; - struct interface *ifp; + struct interface *ifp = NULL; int direct_connect = 0; struct ospf6_path *path; @@ -1636,7 +1645,7 @@ void ospf6_intra_prefix_lsa_add(struct ospf6_lsa *lsa) return; if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) - zlog_debug("%s: LSA %s found", __PRETTY_FUNCTION__, lsa->name); + zlog_debug("%s: LSA %s found", __func__, lsa->name); oa = OSPF6_AREA(lsa->lsdb->data); @@ -1716,11 +1725,19 @@ void ospf6_intra_prefix_lsa_add(struct ospf6_lsa *lsa) memcpy(&route->path.ls_prefix, &ls_prefix, sizeof(struct prefix)); if (direct_connect) { - ifp = if_lookup_prefix(&route->prefix, VRF_DEFAULT); - if (ifp) - ospf6_route_add_nexthop(route, ifp->ifindex, - NULL); + ifp = if_lookup_prefix(&route->prefix, + oa->ospf6->vrf_id); + } + + if (ifp) { + /* Nexthop interface found */ + ospf6_route_add_nexthop(route, ifp->ifindex, NULL); } else { + /* The connected interfaces between routers can be in + * different networks. In this case the matching + * interface is not found. Copy nexthops from the + * link state entry + */ ospf6_route_copy_nexthops(route, ls_entry); } @@ -1732,21 +1749,22 @@ void ospf6_intra_prefix_lsa_add(struct ospf6_lsa *lsa) if (old) { if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) { prefix2str(&route->prefix, buf, sizeof(buf)); - zlog_debug("%s Update route: %s old cost %u new cost %u paths %u nh %u", - __PRETTY_FUNCTION__, buf, - old->path.cost, route->path.cost, - listcount(route->paths), - listcount(route->nh_list)); + zlog_debug( + "%s Update route: %s old cost %u new cost %u paths %u nh %u", + __func__, buf, old->path.cost, + route->path.cost, + listcount(route->paths), + listcount(route->nh_list)); } ospf6_intra_prefix_route_ecmp_path(oa, old, route); } else { if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) { prefix2str(&route->prefix, buf, sizeof(buf)); - zlog_debug("%s route %s add with cost %u paths %u nh %u", - __PRETTY_FUNCTION__, buf, - route->path.cost, - listcount(route->paths), - listcount(route->nh_list)); + zlog_debug( + "%s route %s add with cost %u paths %u nh %u", + __func__, buf, route->path.cost, + listcount(route->paths), + listcount(route->nh_list)); } ospf6_route_add(route, oa->route_table); } @@ -1783,7 +1801,7 @@ static void ospf6_intra_prefix_lsa_remove_update_route(struct ospf6_lsa *lsa, prefix2str(&route->prefix, buf, sizeof(buf)); zlog_debug( "%s: route %s path found with cost %u nh %u to remove.", - __PRETTY_FUNCTION__, buf, o_path->cost, + __func__, buf, o_path->cost, listcount(o_path->nh_list)); } @@ -1819,11 +1837,10 @@ static void ospf6_intra_prefix_lsa_remove_update_route(struct ospf6_lsa *lsa, if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) { prefix2str(&route->prefix, buf, sizeof(buf)); - zlog_debug("%s: route %s update paths %u nh %u", - __PRETTY_FUNCTION__, buf, - route->paths ? listcount(route->paths) : 0, - route->nh_list ? listcount(route->nh_list) - : 0); + zlog_debug( + "%s: route %s update paths %u nh %u", __func__, + buf, route->paths ? listcount(route->paths) : 0, + route->nh_list ? listcount(route->nh_list) : 0); } /* Update Global Route table and @@ -1859,8 +1876,7 @@ void ospf6_intra_prefix_lsa_remove(struct ospf6_lsa *lsa) char buf[PREFIX2STR_BUFFER]; if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) - zlog_debug("%s: %s disappearing", __PRETTY_FUNCTION__, - lsa->name); + zlog_debug("%s: %s disappearing", __func__, lsa->name); oa = OSPF6_AREA(lsa->lsdb->data); @@ -1917,12 +1933,12 @@ void ospf6_intra_prefix_lsa_remove(struct ospf6_lsa *lsa) if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) { prefix2str(&route->prefix, buf, sizeof(buf)); - zlog_debug("%s: route remove %s with path type %u cost %u paths %u nh %u", - __PRETTY_FUNCTION__, buf, - route->path.type, - route->path.cost, - listcount(route->paths), - listcount(route->nh_list)); + zlog_debug( + "%s: route remove %s with path type %u cost %u paths %u nh %u", + __func__, buf, route->path.type, + route->path.cost, + listcount(route->paths), + listcount(route->nh_list)); } ospf6_route_remove(route, oa->route_table); } @@ -2054,8 +2070,8 @@ void ospf6_intra_brouter_calculation(struct ospf6_area *oa) if (IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ID(oa->area_id) || IS_OSPF6_DEBUG_ROUTE(MEMORY)) - zlog_info("%s: border-router calculation for area %s", - __PRETTY_FUNCTION__, oa->name); + zlog_info("%s: border-router calculation for area %s", __func__, + oa->name); hook_add = oa->ospf6->brouter_table->hook_add; hook_remove = oa->ospf6->brouter_table->hook_remove; @@ -2161,10 +2177,11 @@ void ospf6_intra_brouter_calculation(struct ospf6_area *oa) if (ospf6_route_lookup(&adv_prefix, oa->spf_table)) { if (IS_OSPF6_DEBUG_BROUTER) { - zlog_debug("%s: keep inter brouter %s as adv router 0x%x found in spf", - __PRETTY_FUNCTION__, - brouter_name, - brouter->path.origin.adv_router); + zlog_debug( + "%s: keep inter brouter %s as adv router 0x%x found in spf", + __func__, brouter_name, + brouter->path.origin + .adv_router); ospf6_brouter_debug_print(brouter); } UNSET_FLAG(brouter->flag, OSPF6_ROUTE_REMOVE); @@ -2183,9 +2200,9 @@ void ospf6_intra_brouter_calculation(struct ospf6_area *oa) brouter_id) || IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ID( oa->area_id)) - zlog_info("%s: brouter %s disappears via area %s", - __PRETTY_FUNCTION__, brouter_name, - oa->name); + zlog_info( + "%s: brouter %s disappears via area %s", + __func__, brouter_name, oa->name); /* This is used to protect nbrouter from removed from * the table. For an example, ospf6_abr_examin_summary, * removes brouters which are marked for remove. @@ -2201,8 +2218,7 @@ void ospf6_intra_brouter_calculation(struct ospf6_area *oa) || IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ID( oa->area_id)) zlog_info("%s: brouter %s appears via area %s", - __PRETTY_FUNCTION__, brouter_name, - oa->name); + __func__, brouter_name, oa->name); /* newly added */ if (hook_add) @@ -2229,34 +2245,34 @@ void ospf6_intra_brouter_calculation(struct ospf6_area *oa) if (IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ID(oa->area_id) || IS_OSPF6_DEBUG_ROUTE(MEMORY)) zlog_info("%s: border-router calculation for area %s: done", - __PRETTY_FUNCTION__, oa->name); + __func__, oa->name); } -struct ospf6_lsa_handler router_handler = {.lh_type = OSPF6_LSTYPE_ROUTER, - .lh_name = "Router", - .lh_short_name = "Rtr", - .lh_show = ospf6_router_lsa_show, - .lh_get_prefix_str = - ospf6_router_lsa_get_nbr_id, - .lh_debug = 0}; - -struct ospf6_lsa_handler network_handler = {.lh_type = OSPF6_LSTYPE_NETWORK, - .lh_name = "Network", - .lh_short_name = "Net", - .lh_show = ospf6_network_lsa_show, - .lh_get_prefix_str = - ospf6_network_lsa_get_ar_id, - .lh_debug = 0}; - -struct ospf6_lsa_handler link_handler = {.lh_type = OSPF6_LSTYPE_LINK, - .lh_name = "Link", - .lh_short_name = "Lnk", - .lh_show = ospf6_link_lsa_show, - .lh_get_prefix_str = - ospf6_link_lsa_get_prefix_str, - .lh_debug = 0}; - -struct ospf6_lsa_handler intra_prefix_handler = { +static struct ospf6_lsa_handler router_handler = { + .lh_type = OSPF6_LSTYPE_ROUTER, + .lh_name = "Router", + .lh_short_name = "Rtr", + .lh_show = ospf6_router_lsa_show, + .lh_get_prefix_str = ospf6_router_lsa_get_nbr_id, + .lh_debug = 0}; + +static struct ospf6_lsa_handler network_handler = { + .lh_type = OSPF6_LSTYPE_NETWORK, + .lh_name = "Network", + .lh_short_name = "Net", + .lh_show = ospf6_network_lsa_show, + .lh_get_prefix_str = ospf6_network_lsa_get_ar_id, + .lh_debug = 0}; + +static struct ospf6_lsa_handler link_handler = { + .lh_type = OSPF6_LSTYPE_LINK, + .lh_name = "Link", + .lh_short_name = "Lnk", + .lh_show = ospf6_link_lsa_show, + .lh_get_prefix_str = ospf6_link_lsa_get_prefix_str, + .lh_debug = 0}; + +static struct ospf6_lsa_handler intra_prefix_handler = { .lh_type = OSPF6_LSTYPE_INTRA_PREFIX, .lh_name = "Intra-Prefix", .lh_short_name = "INP", diff --git a/ospf6d/ospf6_intra.h b/ospf6d/ospf6_intra.h index 672e288bf3..9c29681dee 100644 --- a/ospf6d/ospf6_intra.h +++ b/ospf6d/ospf6_intra.h @@ -23,8 +23,8 @@ /* Debug option */ extern unsigned char conf_debug_ospf6_brouter; -extern uint32_t conf_debug_ospf6_brouter_specific_router_id; -extern uint32_t conf_debug_ospf6_brouter_specific_area_id; +extern in_addr_t conf_debug_ospf6_brouter_specific_router_id; +extern in_addr_t conf_debug_ospf6_brouter_specific_area_id; #define OSPF6_DEBUG_BROUTER_SUMMARY 0x01 #define OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER 0x02 #define OSPF6_DEBUG_BROUTER_SPECIFIC_AREA 0x04 @@ -86,7 +86,7 @@ struct ospf6_router_lsdesc { uint16_t metric; /* output cost */ uint32_t interface_id; uint32_t neighbor_interface_id; - uint32_t neighbor_router_id; + in_addr_t neighbor_router_id; }; #define OSPF6_ROUTER_LSDESC_POINTTOPOINT 1 @@ -125,7 +125,7 @@ struct ospf6_network_lsa { /* Link State Description in Router-LSA */ #define OSPF6_NETWORK_LSDESC_FIX_SIZE 4U struct ospf6_network_lsdesc { - uint32_t router_id; + in_addr_t router_id; }; #define NETWORK_LSDESC_GET_NBR_ROUTERID(x) \ (((struct ospf6_network_lsdesc *)(x))->router_id) @@ -146,7 +146,7 @@ struct ospf6_intra_prefix_lsa { uint16_t prefix_num; uint16_t ref_type; uint32_t ref_id; - uint32_t ref_adv_router; + in_addr_t ref_adv_router; /* followed by ospf6 prefix(es) */ }; diff --git a/ospf6d/ospf6_lsa.c b/ospf6d/ospf6_lsa.c index 9acbd09b1a..9e7479c797 100644 --- a/ospf6d/ospf6_lsa.c +++ b/ospf6d/ospf6_lsa.c @@ -77,16 +77,16 @@ static struct ospf6_lsa_handler unknown_handler = { .lh_debug = 0 /* No default debug */ }; -void ospf6_install_lsa_handler(const struct ospf6_lsa_handler *handler) +void ospf6_install_lsa_handler(struct ospf6_lsa_handler *handler) { /* type in handler is host byte order */ int index = handler->lh_type & OSPF6_LSTYPE_FCODE_MASK; vector_set_index(ospf6_lsa_handler_vector, index, (void *)handler); } -const struct ospf6_lsa_handler *ospf6_get_lsa_handler(uint16_t type) +struct ospf6_lsa_handler *ospf6_get_lsa_handler(uint16_t type) { - const struct ospf6_lsa_handler *handler = NULL; + struct ospf6_lsa_handler *handler = NULL; unsigned int index = ntohs(type) & OSPF6_LSTYPE_FCODE_MASK; if (index >= vector_active(ospf6_lsa_handler_vector)) @@ -397,10 +397,10 @@ void ospf6_lsa_show_summary(struct vty *vty, struct ospf6_lsa *lsa) (unsigned long)ntohl(lsa->header->seqnum), handler->lh_get_prefix_str(lsa, buf, sizeof(buf), 0)); } else if (type != OSPF6_LSTYPE_UNKNOWN) { - sprintf(tmpbuf, "%-4s %-15s%-15s%4hu %8lx", - ospf6_lstype_short_name(lsa->header->type), id, - adv_router, ospf6_lsa_age_current(lsa), - (unsigned long)ntohl(lsa->header->seqnum)); + snprintf(tmpbuf, sizeof(tmpbuf), "%-4s %-15s%-15s%4hu %8lx", + ospf6_lstype_short_name(lsa->header->type), id, + adv_router, ospf6_lsa_age_current(lsa), + (unsigned long)ntohl(lsa->header->seqnum)); while (handler->lh_get_prefix_str(lsa, buf, sizeof(buf), cnt) != NULL) { @@ -527,7 +527,7 @@ struct ospf6_lsa *ospf6_lsa_create(struct ospf6_lsa_header *header) /* allocate memory */ lsa = XCALLOC(MTYPE_OSPF6_LSA, sizeof(struct ospf6_lsa)); - lsa->header = (struct ospf6_lsa_header *)new_header; + lsa->header = new_header; /* dump string */ ospf6_lsa_printbuf(lsa, lsa->name, sizeof(lsa->name)); @@ -554,7 +554,7 @@ struct ospf6_lsa *ospf6_lsa_create_headeronly(struct ospf6_lsa_header *header) /* allocate memory */ lsa = XCALLOC(MTYPE_OSPF6_LSA, sizeof(struct ospf6_lsa)); - lsa->header = (struct ospf6_lsa_header *)new_header; + lsa->header = new_header; SET_FLAG(lsa->flag, OSPF6_LSA_HEADERONLY); /* dump string */ @@ -608,16 +608,17 @@ void ospf6_lsa_lock(struct ospf6_lsa *lsa) } /* decrement reference counter of struct ospf6_lsa */ -void ospf6_lsa_unlock(struct ospf6_lsa *lsa) +struct ospf6_lsa *ospf6_lsa_unlock(struct ospf6_lsa *lsa) { /* decrement reference counter */ assert(lsa->lock > 0); lsa->lock--; if (lsa->lock != 0) - return; + return lsa; ospf6_lsa_delete(lsa); + return NULL; } diff --git a/ospf6d/ospf6_lsa.h b/ospf6d/ospf6_lsa.h index d871a8842e..a85d7b0603 100644 --- a/ospf6d/ospf6_lsa.h +++ b/ospf6d/ospf6_lsa.h @@ -80,8 +80,8 @@ struct ospf6_lsa_header { uint16_t age; /* LS age */ uint16_t type; /* LS type */ - uint32_t id; /* Link State ID */ - uint32_t adv_router; /* Advertising Router */ + in_addr_t id; /* Link State ID */ + in_addr_t adv_router; /* Advertising Router */ uint32_t seqnum; /* LS sequence number */ uint16_t checksum; /* LS checksum */ uint16_t length; /* LSA length */ @@ -227,7 +227,7 @@ extern void ospf6_lsa_delete(struct ospf6_lsa *lsa); extern struct ospf6_lsa *ospf6_lsa_copy(struct ospf6_lsa *); extern void ospf6_lsa_lock(struct ospf6_lsa *); -extern void ospf6_lsa_unlock(struct ospf6_lsa *); +extern struct ospf6_lsa *ospf6_lsa_unlock(struct ospf6_lsa *); extern int ospf6_lsa_expire(struct thread *); extern int ospf6_lsa_refresh(struct thread *); @@ -237,8 +237,8 @@ extern int ospf6_lsa_checksum_valid(struct ospf6_lsa_header *); extern int ospf6_lsa_prohibited_duration(uint16_t type, uint32_t id, uint32_t adv_router, void *scope); -extern void ospf6_install_lsa_handler(const struct ospf6_lsa_handler *handler); -extern const struct ospf6_lsa_handler *ospf6_get_lsa_handler(uint16_t type); +extern void ospf6_install_lsa_handler(struct ospf6_lsa_handler *handler); +extern struct ospf6_lsa_handler *ospf6_get_lsa_handler(uint16_t type); extern void ospf6_lsa_init(void); extern void ospf6_lsa_terminate(void); diff --git a/ospf6d/ospf6_main.c b/ospf6d/ospf6_main.c index 61094c7cdb..81306d9162 100644 --- a/ospf6d/ospf6_main.c +++ b/ospf6d/ospf6_main.c @@ -28,7 +28,6 @@ #include "command.h" #include "vty.h" #include "memory.h" -#include "memory_vty.h" #include "if.h" #include "filter.h" #include "prefix.h" @@ -80,15 +79,17 @@ struct thread_master *master; static void __attribute__((noreturn)) ospf6_exit(int status) { - struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT); + struct vrf *vrf; struct interface *ifp; frr_early_fini(); if (ospf6) { + vrf = vrf_lookup_by_id(ospf6->vrf_id); ospf6_delete(ospf6); ospf6 = NULL; - } + } else + vrf = vrf_lookup_by_id(VRF_DEFAULT); bfd_gbl_exit(); @@ -100,7 +101,6 @@ static void __attribute__((noreturn)) ospf6_exit(int status) ospf6_asbr_terminate(); ospf6_lsa_terminate(); - ospf6_serv_close(); /* reverse access_list_init */ access_list_reset(); @@ -166,8 +166,11 @@ struct quagga_signal_t ospf6_signals[] = { }, }; -static const struct frr_yang_module_info *ospf6d_yang_modules[] = { +static const struct frr_yang_module_info *const ospf6d_yang_modules[] = { + &frr_filter_info, &frr_interface_info, + &frr_route_map_info, + &frr_vrf_info, }; FRR_DAEMON_INFO(ospf6d, OSPF6, .vty_port = OSPF6_VTY_PORT, diff --git a/ospf6d/ospf6_message.c b/ospf6d/ospf6_message.c index 4acb5e3b2e..0311ff4c2c 100644 --- a/ospf6d/ospf6_message.c +++ b/ospf6d/ospf6_message.c @@ -1516,15 +1516,8 @@ int ospf6_iobuf_size(unsigned int size) void ospf6_message_terminate(void) { - if (recvbuf) { - XFREE(MTYPE_OSPF6_MESSAGE, recvbuf); - recvbuf = NULL; - } - - if (sendbuf) { - XFREE(MTYPE_OSPF6_MESSAGE, sendbuf); - sendbuf = NULL; - } + XFREE(MTYPE_OSPF6_MESSAGE, recvbuf); + XFREE(MTYPE_OSPF6_MESSAGE, sendbuf); iobuflen = 0; } @@ -1542,7 +1535,7 @@ int ospf6_receive(struct thread *thread) /* add next read thread */ sockfd = THREAD_FD(thread); - thread_add_read(master, ospf6_receive, NULL, sockfd, NULL); + thread_add_read(master, ospf6_receive, NULL, sockfd, &ospf6->t_ospf6_receive); /* initialize */ memset(&src, 0, sizeof(src)); @@ -1561,7 +1554,7 @@ int ospf6_receive(struct thread *thread) return 0; } - oi = ospf6_interface_lookup_by_ifindex(ifindex); + oi = ospf6_interface_lookup_by_ifindex(ifindex, ospf6->vrf_id); if (oi == NULL || oi->area == NULL || CHECK_FLAG(oi->flag, OSPF6_INTERFACE_DISABLE)) { if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) @@ -1951,9 +1944,9 @@ int ospf6_lsreq_send(struct thread *thread) } if (last_req != NULL) { - if (on->last_ls_req != NULL) { - ospf6_lsa_unlock(on->last_ls_req); - } + if (on->last_ls_req != NULL) + on->last_ls_req = ospf6_lsa_unlock(on->last_ls_req); + ospf6_lsa_lock(last_req); on->last_ls_req = last_req; } @@ -2186,9 +2179,8 @@ int ospf6_lsupdate_send_neighbor_now(struct ospf6_neighbor *on, if (IS_OSPF6_DEBUG_FLOODING || IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_LSUPDATE, SEND)) - zlog_debug("%s: Send lsupdate with lsa %s (age %u)", - __PRETTY_FUNCTION__, lsa->name, - ntohs(lsa->header->age)); + zlog_debug("%s: Send lsupdate with lsa %s (age %u)", __func__, + lsa->name, ntohs(lsa->header->age)); ospf6_send_lsupdate(on, NULL, oh); @@ -2241,8 +2233,7 @@ int ospf6_lsupdate_send_interface(struct thread *thread) if (IS_OSPF6_DEBUG_MESSAGE( OSPF6_MESSAGE_TYPE_LSUPDATE, SEND)) zlog_debug("%s: LSUpdate length %d", - __PRETTY_FUNCTION__, - ntohs(oh->length)); + __func__, ntohs(oh->length)); memset(sendbuf, 0, iobuflen); oh = (struct ospf6_header *)sendbuf; diff --git a/ospf6d/ospf6_message.h b/ospf6d/ospf6_message.h index d24b7f8942..7ec8cb785f 100644 --- a/ospf6d/ospf6_message.h +++ b/ospf6d/ospf6_message.h @@ -49,8 +49,8 @@ struct ospf6_header { uint8_t version; uint8_t type; uint16_t length; - uint32_t router_id; - uint32_t area_id; + in_addr_t router_id; + in_addr_t area_id; uint16_t checksum; uint8_t instance_id; uint8_t reserved; @@ -66,8 +66,8 @@ struct ospf6_hello { uint8_t options[3]; uint16_t hello_interval; uint16_t dead_interval; - uint32_t drouter; - uint32_t bdrouter; + in_addr_t drouter; + in_addr_t bdrouter; /* Followed by Router-IDs */ }; @@ -94,8 +94,8 @@ struct ospf6_dbdesc { struct ospf6_lsreq_entry { uint16_t reserved; /* Must Be Zero */ uint16_t type; /* LS type */ - uint32_t id; /* Link State ID */ - uint32_t adv_router; /* Advertising Router */ + in_addr_t id; /* Link State ID */ + in_addr_t adv_router; /* Advertising Router */ }; /* Link State Update */ diff --git a/ospf6d/ospf6_neighbor.c b/ospf6d/ospf6_neighbor.c index 46dc621ae7..92a3c9e1ad 100644 --- a/ospf6d/ospf6_neighbor.c +++ b/ospf6d/ospf6_neighbor.c @@ -51,10 +51,16 @@ DEFINE_HOOK(ospf6_neighbor_change, unsigned char conf_debug_ospf6_neighbor = 0; -const char *ospf6_neighbor_state_str[] = { +const char *const ospf6_neighbor_state_str[] = { "None", "Down", "Attempt", "Init", "Twoway", "ExStart", "ExChange", "Loading", "Full", NULL}; +const char *const ospf6_neighbor_event_str[] = { + "NoEvent", "HelloReceived", "2-WayReceived", "NegotiationDone", + "ExchangeDone", "LoadingDone", "AdjOK?", "SeqNumberMismatch", + "BadLSReq", "1-WayReceived", "InactivityTimer", +}; + int ospf6_neighbor_cmp(void *va, void *vb) { struct ospf6_neighbor *ona = (struct ospf6_neighbor *)va; @@ -618,7 +624,7 @@ static void ospf6_neighbor_show(struct vty *vty, struct ospf6_neighbor *on) snprintf(deadtime, sizeof(deadtime), "%02ld:%02ld:%02ld", h, m, s); /* Neighbor State */ - if (if_is_pointopoint(on->ospf6_if->interface)) + if (on->ospf6_if->type == OSPF_IFTYPE_POINTOPOINT) snprintf(nstate, sizeof(nstate), "PointToPoint"); else { if (on->router_id == on->drouter) diff --git a/ospf6d/ospf6_neighbor.h b/ospf6d/ospf6_neighbor.h index 840683cc2f..1a45a1966a 100644 --- a/ospf6d/ospf6_neighbor.h +++ b/ospf6d/ospf6_neighbor.h @@ -48,7 +48,7 @@ struct ospf6_neighbor { struct timeval last_changed; /* Neighbor Router ID */ - uint32_t router_id; + in_addr_t router_id; /* Neighbor Interface ID */ ifindex_t ifindex; @@ -56,10 +56,10 @@ struct ospf6_neighbor { /* Router Priority of this neighbor */ uint8_t priority; - uint32_t drouter; - uint32_t bdrouter; - uint32_t prev_drouter; - uint32_t prev_bdrouter; + in_addr_t drouter; + in_addr_t bdrouter; + in_addr_t prev_drouter; + in_addr_t prev_bdrouter; /* Options field (Capability) */ char options[3]; @@ -123,11 +123,7 @@ struct ospf6_neighbor { #define OSPF6_NEIGHBOR_EVENT_INACTIVITY_TIMER 10 #define OSPF6_NEIGHBOR_EVENT_MAX_EVENT 11 -static const char *ospf6_neighbor_event_str[] = { - "NoEvent", "HelloReceived", "2-WayReceived", "NegotiationDone", - "ExchangeDone", "LoadingDone", "AdjOK?", "SeqNumberMismatch", - "BadLSReq", "1-WayReceived", "InactivityTimer", -}; +extern const char *const ospf6_neighbor_event_str[]; static inline const char *ospf6_neighbor_event_string(int event) { @@ -138,7 +134,7 @@ static inline const char *ospf6_neighbor_event_string(int event) return OSPF6_NEIGHBOR_UNKNOWN_EVENT_STRING; } -extern const char *ospf6_neighbor_state_str[]; +extern const char *const ospf6_neighbor_state_str[]; /* Function Prototypes */ diff --git a/ospf6d/ospf6_network.c b/ospf6d/ospf6_network.c index 625ad884f2..9a18680b8b 100644 --- a/ospf6d/ospf6_network.c +++ b/ospf6d/ospf6_network.c @@ -85,7 +85,7 @@ void ospf6_serv_close(void) /* Make ospf6d's server socket. */ int ospf6_serv_sock(void) { - frr_elevate_privs(&ospf6d_privs) { + frr_with_privs(&ospf6d_privs) { ospf6_sock = socket(AF_INET6, SOCK_RAW, IPPROTO_OSPFIGP); if (ospf6_sock < 0) { diff --git a/ospf6d/ospf6_proto.h b/ospf6d/ospf6_proto.h index c9e7b549db..3876a98c50 100644 --- a/ospf6d/ospf6_proto.h +++ b/ospf6d/ospf6_proto.h @@ -71,7 +71,7 @@ struct ospf6_prefix { #define OSPF6_PREFIX_OPTION_P (1 << 3) /* Propagate (NSSA) */ /* caddr_t OSPF6_PREFIX_BODY (struct ospf6_prefix *); */ -#define OSPF6_PREFIX_BODY(x) ((caddr_t)(x) + sizeof (struct ospf6_prefix)) +#define OSPF6_PREFIX_BODY(x) ((caddr_t)(x) + sizeof(struct ospf6_prefix)) /* size_t OSPF6_PREFIX_SPACE (int prefixlength); */ #define OSPF6_PREFIX_SPACE(x) ((((x) + 31) / 32) * 4) diff --git a/ospf6d/ospf6_route.c b/ospf6d/ospf6_route.c index 441a6f3677..a443e4c3ba 100644 --- a/ospf6d/ospf6_route.c +++ b/ospf6d/ospf6_route.c @@ -147,19 +147,19 @@ void ospf6_linkstate_prefix2str(struct prefix *prefix, char *buf, int size) } /* Global strings for logging */ -const char *ospf6_dest_type_str[OSPF6_DEST_TYPE_MAX] = { +const char *const ospf6_dest_type_str[OSPF6_DEST_TYPE_MAX] = { "Unknown", "Router", "Network", "Discard", "Linkstate", "AddressRange", }; -const char *ospf6_dest_type_substr[OSPF6_DEST_TYPE_MAX] = { +const char *const ospf6_dest_type_substr[OSPF6_DEST_TYPE_MAX] = { "?", "R", "N", "D", "L", "A", }; -const char *ospf6_path_type_str[OSPF6_PATH_TYPE_MAX] = { +const char *const ospf6_path_type_str[OSPF6_PATH_TYPE_MAX] = { "Unknown", "Intra-Area", "Inter-Area", "External-1", "External-2", }; -const char *ospf6_path_type_substr[OSPF6_PATH_TYPE_MAX] = { +const char *const ospf6_path_type_substr[OSPF6_PATH_TYPE_MAX] = { "??", "IA", "IE", "E1", "E2", }; @@ -307,14 +307,14 @@ void ospf6_route_zebra_copy_nexthops(struct ospf6_route *route, inet_ntop(AF_INET6, &nh->address, buf, sizeof(buf)); ifname = ifindex2ifname(nh->ifindex, - VRF_DEFAULT); + ospf6->vrf_id); zlog_debug(" nexthop: %s%%%.*s(%d)", buf, IFNAMSIZ, ifname, nh->ifindex); } if (i >= entries) return; - nexthops[i].vrf_id = VRF_DEFAULT; + nexthops[i].vrf_id = ospf6->vrf_id; nexthops[i].ifindex = nh->ifindex; if (!IN6_IS_ADDR_UNSPECIFIED(&nh->address)) { nexthops[i].gate.ipv6 = nh->address; @@ -336,7 +336,7 @@ int ospf6_route_get_first_nh_index(struct ospf6_route *route) return nh->ifindex; } - return (-1); + return -1; } int ospf6_nexthop_cmp(struct ospf6_nexthop *a, struct ospf6_nexthop *b) @@ -1042,6 +1042,11 @@ void ospf6_route_show(struct vty *vty, struct ospf6_route *route) struct listnode *node; struct ospf6_nexthop *nh; + if (ospf6 == NULL) { + vty_out(vty, "OSPFv3 is not running\n"); + return; + } + monotime(&now); timersub(&now, &route->changed, &res); timerstring(&res, duration, sizeof(duration)); @@ -1060,7 +1065,7 @@ void ospf6_route_show(struct vty *vty, struct ospf6_route *route) for (ALL_LIST_ELEMENTS_RO(route->nh_list, node, nh)) { /* nexthop */ inet_ntop(AF_INET6, &nh->address, nexthop, sizeof(nexthop)); - ifname = ifindex2ifname(nh->ifindex, VRF_DEFAULT); + ifname = ifindex2ifname(nh->ifindex, ospf6->vrf_id); if (!i) { vty_out(vty, "%c%1s %2s %-30s %-25s %6.*s %s\n", @@ -1086,6 +1091,11 @@ void ospf6_route_show_detail(struct vty *vty, struct ospf6_route *route) struct listnode *node; struct ospf6_nexthop *nh; + if (ospf6 == NULL) { + vty_out(vty, "OSPFv3 is not running\n"); + return; + } + monotime(&now); /* destination */ @@ -1160,7 +1170,7 @@ void ospf6_route_show_detail(struct vty *vty, struct ospf6_route *route) for (ALL_LIST_ELEMENTS_RO(route->nh_list, node, nh)) { /* nexthop */ inet_ntop(AF_INET6, &nh->address, nexthop, sizeof(nexthop)); - ifname = ifindex2ifname(nh->ifindex, VRF_DEFAULT); + ifname = ifindex2ifname(nh->ifindex, ospf6->vrf_id); vty_out(vty, " %s %.*s\n", nexthop, IFNAMSIZ, ifname); } vty_out(vty, "\n"); diff --git a/ospf6d/ospf6_route.h b/ospf6d/ospf6_route.h index 02002533e6..95ba983e6b 100644 --- a/ospf6d/ospf6_route.h +++ b/ospf6d/ospf6_route.h @@ -64,8 +64,8 @@ struct ospf6_nexthop { /* Path */ struct ospf6_ls_origin { uint16_t type; - uint32_t id; - uint32_t adv_router; + in_addr_t id; + in_addr_t adv_router; }; struct ospf6_path { @@ -82,7 +82,7 @@ struct ospf6_path { uint8_t prefix_options; /* Associated Area */ - uint32_t area_id; + in_addr_t area_id; /* Path-type */ uint8_t type; @@ -215,8 +215,8 @@ struct ospf6_route_table { #define OSPF6_ROUTE_TABLE_CREATE(s, t) \ ospf6_route_table_create(OSPF6_SCOPE_TYPE_##s, OSPF6_TABLE_TYPE_##t) -extern const char *ospf6_dest_type_str[OSPF6_DEST_TYPE_MAX]; -extern const char *ospf6_dest_type_substr[OSPF6_DEST_TYPE_MAX]; +extern const char *const ospf6_dest_type_str[OSPF6_DEST_TYPE_MAX]; +extern const char *const ospf6_dest_type_substr[OSPF6_DEST_TYPE_MAX]; #define OSPF6_DEST_TYPE_NAME(x) \ (0 < (x) && (x) < OSPF6_DEST_TYPE_MAX ? ospf6_dest_type_str[(x)] \ : ospf6_dest_type_str[0]) @@ -224,8 +224,8 @@ extern const char *ospf6_dest_type_substr[OSPF6_DEST_TYPE_MAX]; (0 < (x) && (x) < OSPF6_DEST_TYPE_MAX ? ospf6_dest_type_substr[(x)] \ : ospf6_dest_type_substr[0]) -extern const char *ospf6_path_type_str[OSPF6_PATH_TYPE_MAX]; -extern const char *ospf6_path_type_substr[OSPF6_PATH_TYPE_MAX]; +extern const char *const ospf6_path_type_str[OSPF6_PATH_TYPE_MAX]; +extern const char *const ospf6_path_type_substr[OSPF6_PATH_TYPE_MAX]; #define OSPF6_PATH_TYPE_NAME(x) \ (0 < (x) && (x) < OSPF6_PATH_TYPE_MAX ? ospf6_path_type_str[(x)] \ : ospf6_path_type_str[0]) diff --git a/ospf6d/ospf6_snmp.c b/ospf6d/ospf6_snmp.c index fc7c6177d7..bb5264a5a4 100644 --- a/ospf6d/ospf6_snmp.c +++ b/ospf6d/ospf6_snmp.c @@ -837,7 +837,7 @@ static uint8_t *ospfv3WwLsdbEntry(struct variable *v, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { - struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT); + struct vrf *vrf; struct ospf6_lsa *lsa = NULL; ifindex_t ifindex; uint32_t area_id, id, instid, adv_router; @@ -861,6 +861,7 @@ static uint8_t *ospfv3WwLsdbEntry(struct variable *v, oid *name, size_t *length, if (ospf6 == NULL) return NULL; + vrf = vrf_lookup_by_id(ospf6->vrf_id); /* Get variable length. */ offset = name + v->namelen; offsetlen = *length - v->namelen; @@ -926,7 +927,8 @@ static uint8_t *ospfv3WwLsdbEntry(struct variable *v, oid *name, size_t *length, return NULL; lsa = ospf6_lsdb_lookup(type, id, adv_router, oa->lsdb); } else if (v->magic & OSPFv3WWLINKTABLE) { - oi = ospf6_interface_lookup_by_ifindex(ifindex); + oi = ospf6_interface_lookup_by_ifindex(ifindex, + ospf6->vrf_id); if (!oi || oi->instance_id != instid) return NULL; lsa = ospf6_lsdb_lookup(type, id, adv_router, oi->lsdb); @@ -953,8 +955,6 @@ static uint8_t *ospfv3WwLsdbEntry(struct variable *v, oid *name, size_t *length, else if (v->magic & OSPFv3WWLINKTABLE) { /* We build a sorted list of interfaces */ ifslist = list_new(); - if (!ifslist) - return NULL; ifslist->cmp = (int (*)(void *, void *))if_icmp_func; FOR_ALL_INTERFACES (vrf, iif) listnode_add_sort(ifslist, iif); @@ -963,7 +963,7 @@ static uint8_t *ospfv3WwLsdbEntry(struct variable *v, oid *name, size_t *length, if (!iif->ifindex) continue; oi = ospf6_interface_lookup_by_ifindex( - iif->ifindex); + iif->ifindex, iif->vrf_id); if (!oi) continue; if (iif->ifindex < ifindex) @@ -983,6 +983,7 @@ static uint8_t *ospfv3WwLsdbEntry(struct variable *v, oid *name, size_t *length, } list_delete_all_node(ifslist); + list_delete(&ifslist); } } @@ -1017,18 +1018,14 @@ static uint8_t *ospfv3WwLsdbEntry(struct variable *v, oid *name, size_t *length, switch (v->magic & OSPFv3WWCOLUMN) { case OSPFv3WWLSDBSEQUENCE: return SNMP_INTEGER(ntohl(lsa->header->seqnum)); - break; case OSPFv3WWLSDBAGE: ospf6_lsa_age_current(lsa); return SNMP_INTEGER(ntohs(lsa->header->age)); - break; case OSPFv3WWLSDBCHECKSUM: return SNMP_INTEGER(ntohs(lsa->header->checksum)); - break; case OSPFv3WWLSDBADVERTISEMENT: *var_len = ntohs(lsa->header->length); return (uint8_t *)lsa->header; - break; case OSPFv3WWLSDBTYPEKNOWN: return SNMP_INTEGER(OSPF6_LSA_IS_KNOWN(lsa->header->type) ? SNMP_TRUE @@ -1042,7 +1039,7 @@ static uint8_t *ospfv3IfEntry(struct variable *v, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { - struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT); + struct vrf *vrf; ifindex_t ifindex = 0; unsigned int instid = 0; struct ospf6_interface *oi = NULL; @@ -1062,6 +1059,7 @@ static uint8_t *ospfv3IfEntry(struct variable *v, oid *name, size_t *length, if (ospf6 == NULL) return NULL; + vrf = vrf_lookup_by_id(ospf6->vrf_id); /* Get variable length. */ offset = name + v->namelen; offsetlen = *length - v->namelen; @@ -1084,14 +1082,12 @@ static uint8_t *ospfv3IfEntry(struct variable *v, oid *name, size_t *length, // offsetlen -= len; if (exact) { - oi = ospf6_interface_lookup_by_ifindex(ifindex); + oi = ospf6_interface_lookup_by_ifindex(ifindex, ospf6->vrf_id); if (!oi || oi->instance_id != instid) return NULL; } else { /* We build a sorted list of interfaces */ ifslist = list_new(); - if (!ifslist) - return NULL; ifslist->cmp = (int (*)(void *, void *))if_icmp_func; FOR_ALL_INTERFACES (vrf, iif) listnode_add_sort(ifslist, iif); @@ -1099,7 +1095,8 @@ static uint8_t *ospfv3IfEntry(struct variable *v, oid *name, size_t *length, for (ALL_LIST_ELEMENTS_RO(ifslist, i, iif)) { if (!iif->ifindex) continue; - oi = ospf6_interface_lookup_by_ifindex(iif->ifindex); + oi = ospf6_interface_lookup_by_ifindex(iif->ifindex, + iif->vrf_id); if (!oi) continue; if (iif->ifindex > ifindex @@ -1110,6 +1107,7 @@ static uint8_t *ospfv3IfEntry(struct variable *v, oid *name, size_t *length, } list_delete_all_node(ifslist); + list_delete(&ifslist); } if (!oi) @@ -1130,9 +1128,9 @@ static uint8_t *ospfv3IfEntry(struct variable *v, oid *name, size_t *length, return SNMP_INTEGER(ntohl(oi->area->area_id)); break; case OSPFv3IFTYPE: - if (if_is_broadcast(oi->interface)) + if (oi->type == OSPF_IFTYPE_BROADCAST) return SNMP_INTEGER(1); - else if (if_is_pointopoint(oi->interface)) + else if (oi->type == OSPF_IFTYPE_POINTOPOINT) return SNMP_INTEGER(3); else break; /* Unknown, don't put anything */ @@ -1195,7 +1193,7 @@ static uint8_t *ospfv3NbrEntry(struct variable *v, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { - struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT); + struct vrf *vrf; ifindex_t ifindex = 0; unsigned int instid, rtrid; struct ospf6_interface *oi = NULL; @@ -1216,6 +1214,7 @@ static uint8_t *ospfv3NbrEntry(struct variable *v, oid *name, size_t *length, if (ospf6 == NULL) return NULL; + vrf = vrf_lookup_by_id(ospf6->vrf_id); /* Get variable length. */ offset = name + v->namelen; offsetlen = *length - v->namelen; @@ -1245,15 +1244,13 @@ static uint8_t *ospfv3NbrEntry(struct variable *v, oid *name, size_t *length, // offsetlen -= len; if (exact) { - oi = ospf6_interface_lookup_by_ifindex(ifindex); + oi = ospf6_interface_lookup_by_ifindex(ifindex, ospf6->vrf_id); if (!oi || oi->instance_id != instid) return NULL; on = ospf6_neighbor_lookup(rtrid, oi); } else { /* We build a sorted list of interfaces */ ifslist = list_new(); - if (!ifslist) - return NULL; ifslist->cmp = (int (*)(void *, void *))if_icmp_func; FOR_ALL_INTERFACES (vrf, iif) listnode_add_sort(ifslist, iif); @@ -1261,7 +1258,8 @@ static uint8_t *ospfv3NbrEntry(struct variable *v, oid *name, size_t *length, for (ALL_LIST_ELEMENTS_RO(ifslist, i, iif)) { if (!iif->ifindex) continue; - oi = ospf6_interface_lookup_by_ifindex(iif->ifindex); + oi = ospf6_interface_lookup_by_ifindex(iif->ifindex, + iif->vrf_id); if (!oi) continue; for (ALL_LIST_ELEMENTS_RO(oi->neighbor_list, j, on)) { @@ -1280,6 +1278,7 @@ static uint8_t *ospfv3NbrEntry(struct variable *v, oid *name, size_t *length, } list_delete_all_node(ifslist); + list_delete(&ifslist); } if (!oi || !on) @@ -1360,7 +1359,7 @@ static int ospf6TrapNbrStateChange(struct ospf6_neighbor *on, int next_state, smux_trap(ospfv3_variables, array_size(ospfv3_variables), ospfv3_trap_oid, array_size(ospfv3_trap_oid), ospfv3_oid, - sizeof ospfv3_oid / sizeof(oid), index, 3, ospf6NbrTrapList, + sizeof(ospfv3_oid) / sizeof(oid), index, 3, ospf6NbrTrapList, array_size(ospf6NbrTrapList), NBRSTATECHANGE); return 0; } @@ -1382,7 +1381,7 @@ static int ospf6TrapIfStateChange(struct ospf6_interface *oi, int next_state, smux_trap(ospfv3_variables, array_size(ospfv3_variables), ospfv3_trap_oid, array_size(ospfv3_trap_oid), ospfv3_oid, - sizeof ospfv3_oid / sizeof(oid), index, 2, ospf6IfTrapList, + sizeof(ospfv3_oid) / sizeof(oid), index, 2, ospf6IfTrapList, array_size(ospf6IfTrapList), IFSTATECHANGE); return 0; } diff --git a/ospf6d/ospf6_spf.c b/ospf6d/ospf6_spf.c index aa4a995173..e5eb8d74eb 100644 --- a/ospf6d/ospf6_spf.c +++ b/ospf6d/ospf6_spf.c @@ -278,7 +278,7 @@ static void ospf6_nexthop_calc(struct ospf6_vertex *w, struct ospf6_vertex *v, return; } - oi = ospf6_interface_lookup_by_ifindex(ifindex); + oi = ospf6_interface_lookup_by_ifindex(ifindex, ospf6->vrf_id); if (oi == NULL) { if (IS_OSPF6_DEBUG_SPF(PROCESS)) zlog_debug("Can't find interface in SPF: ifindex %d", @@ -351,7 +351,7 @@ static int ospf6_spf_install(struct ospf6_vertex *v, if (IS_OSPF6_DEBUG_SPF(PROCESS)) { zlog_debug( "%s: V lsa %s id %u, route id %u are different", - __PRETTY_FUNCTION__, v->lsa->name, + __func__, v->lsa->name, ntohl(v->lsa->header->id), ntohl(route->path.origin.id)); } @@ -435,7 +435,7 @@ void ospf6_spf_table_finish(struct ospf6_route_table *result_table) } } -static const char *ospf6_spf_reason_str[] = { +static const char *const ospf6_spf_reason_str[] = { "R+", "R-", "N+", "N-", "L+", "L-", "R*", "N*", }; @@ -658,8 +658,7 @@ static int ospf6_spf_calculation_thread(struct thread *t) (long long)runtime.tv_usec); zlog_info( - "SPF processing: # Areas: %d, SPF runtime: %lld sec %lld usec, " - "Reason: %s\n", + "SPF processing: # Areas: %d, SPF runtime: %lld sec %lld usec, Reason: %s\n", areas_processed, (long long)runtime.tv_sec, (long long)runtime.tv_usec, rbuf); @@ -989,21 +988,21 @@ struct ospf6_lsa *ospf6_create_single_router_lsa(struct ospf6_area *area, rtr_lsa = ospf6_lsdb_next(end, rtr_lsa); continue; } - lsa_header = (struct ospf6_lsa_header *)rtr_lsa->header; + lsa_header = rtr_lsa->header; total_lsa_length += (ntohs(lsa_header->length) - lsa_length); num_lsa++; rtr_lsa = ospf6_lsdb_next(end, rtr_lsa); } if (IS_OSPF6_DEBUG_SPF(PROCESS)) - zlog_debug("%s: adv_router %s num_lsa %u to convert.", - __PRETTY_FUNCTION__, ifbuf, num_lsa); + zlog_debug("%s: adv_router %s num_lsa %u to convert.", __func__, + ifbuf, num_lsa); if (num_lsa == 1) return lsa; if (num_lsa == 0) { if (IS_OSPF6_DEBUG_SPF(PROCESS)) zlog_debug("%s: adv_router %s not found in LSDB.", - __PRETTY_FUNCTION__, ifbuf); + __func__, ifbuf); return NULL; } @@ -1027,7 +1026,7 @@ struct ospf6_lsa *ospf6_create_single_router_lsa(struct ospf6_area *area, assert(rtr_lsa); if (!OSPF6_LSA_IS_MAXAGE(rtr_lsa)) { /* Append first Link State ID LSA */ - lsa_header = (struct ospf6_lsa_header *)rtr_lsa->header; + lsa_header = rtr_lsa->header; memcpy(new_header, lsa_header, ntohs(lsa_header->length)); /* Assign new lsa length as aggregated length. */ ((struct ospf6_lsa_header *)new_header)->length = @@ -1052,12 +1051,12 @@ struct ospf6_lsa *ospf6_create_single_router_lsa(struct ospf6_area *area, inet_ntop(AF_INET, &interface_id, ifbuf, sizeof(ifbuf)); zlog_debug( "%s: Next Router LSA %s to aggreat with len %u interface_id %s", - __PRETTY_FUNCTION__, rtr_lsa->name, + __func__, rtr_lsa->name, ntohs(lsa_header->length), ifbuf); } /* Append Next Link State ID LSA */ - lsa_header = (struct ospf6_lsa_header *)rtr_lsa->header; + lsa_header = rtr_lsa->header; memcpy(new_header, (OSPF6_LSA_HEADER_END(rtr_lsa->header) + 4), (ntohs(lsa_header->length) - lsa_length)); new_header += (ntohs(lsa_header->length) - lsa_length); @@ -1074,9 +1073,9 @@ struct ospf6_lsa *ospf6_create_single_router_lsa(struct ospf6_area *area, if (IS_OSPF6_DEBUG_SPF(PROCESS)) zlog_debug("%s: LSA %s id %u type 0%x len %u num_lsa %u", - __PRETTY_FUNCTION__, lsa->name, - ntohl(lsa->header->id), ntohs(lsa->header->type), - ntohs(lsa->header->length), num_lsa); + __func__, lsa->name, ntohl(lsa->header->id), + ntohs(lsa->header->type), ntohs(lsa->header->length), + num_lsa); return lsa; } @@ -1089,7 +1088,7 @@ void ospf6_remove_temp_router_lsa(struct ospf6_area *area) if (IS_OSPF6_DEBUG_SPF(PROCESS)) zlog_debug( "%s Remove LSA %s lsa->lock %u lsdb count %u", - __PRETTY_FUNCTION__, lsa->name, lsa->lock, + __func__, lsa->name, lsa->lock, area->temp_router_lsa_lsdb->count); ospf6_lsdb_remove(lsa, area->temp_router_lsa_lsdb); } diff --git a/ospf6d/ospf6_top.c b/ospf6d/ospf6_top.c index 40c6123810..960cd3ee01 100644 --- a/ospf6d/ospf6_top.c +++ b/ospf6d/ospf6_top.c @@ -41,6 +41,7 @@ #include "ospf6_area.h" #include "ospf6_interface.h" #include "ospf6_neighbor.h" +#include "ospf6_network.h" #include "ospf6_flood.h" #include "ospf6_asbr.h" @@ -51,6 +52,11 @@ DEFINE_QOBJ_TYPE(ospf6) +FRR_CFG_DEFAULT_BOOL(OSPF6_LOG_ADJACENCY_CHANGES, + { .val_bool = true, .match_profile = "datacenter", }, + { .val_bool = false }, +) + /* global ospf6d variable */ struct ospf6 *ospf6; static struct ospf6_master ospf6_master; @@ -106,7 +112,7 @@ static void ospf6_top_brouter_hook_add(struct ospf6_route *route) inet_ntop(AF_INET, &brouter_id, brouter_name, sizeof(brouter_name)); zlog_debug("%s: brouter %s add with adv router %x nh count %u", - __PRETTY_FUNCTION__, brouter_name, + __func__, brouter_name, route->path.origin.adv_router, listcount(route->nh_list)); } @@ -126,7 +132,7 @@ static void ospf6_top_brouter_hook_remove(struct ospf6_route *route) inet_ntop(AF_INET, &brouter_id, brouter_name, sizeof(brouter_name)); zlog_debug("%s: brouter %p %s del with adv router %x nh %u", - __PRETTY_FUNCTION__, (void *)route, brouter_name, + __func__, (void *)route, brouter_name, route->path.origin.adv_router, listcount(route->nh_list)); } @@ -136,7 +142,7 @@ static void ospf6_top_brouter_hook_remove(struct ospf6_route *route) ospf6_abr_originate_summary(route); } -static struct ospf6 *ospf6_create(void) +static struct ospf6 *ospf6_create(vrf_id_t vrf_id) { struct ospf6 *o; @@ -144,6 +150,7 @@ static struct ospf6 *ospf6_create(void) /* initialize */ monotime(&o->starttime); + o->vrf_id = vrf_id; o->area_list = list_new(); o->area_list->cmp = ospf6_area_cmp; o->lsdb = ospf6_lsdb_create(o); @@ -178,13 +185,12 @@ static struct ospf6 *ospf6_create(void) o->distance_table = route_table_init(); -/* Enable "log-adjacency-changes" */ -#if DFLT_OSPF6_LOG_ADJACENCY_CHANGES - SET_FLAG(o->config_flags, OSPF6_LOG_ADJACENCY_CHANGES); -#endif - QOBJ_REG(o, ospf6); + ospf6_serv_sock(); + + thread_add_read(master, ospf6_receive, NULL, ospf6_sock, &o->t_ospf6_receive); + return o; } @@ -198,6 +204,8 @@ void ospf6_delete(struct ospf6 *o) ospf6_flush_self_originated_lsas_now(); ospf6_disable(ospf6); + ospf6_serv_close(); + for (ALL_LIST_ELEMENTS(o->area_list, node, nnode, oa)) ospf6_area_delete(oa); @@ -231,7 +239,7 @@ static void ospf6_disable(struct ospf6 *o) ospf6_area_disable(oa); /* XXX: This also changes persistent settings */ - ospf6_asbr_redistribute_reset(); + ospf6_asbr_redistribute_reset(o->vrf_id); ospf6_lsdb_remove_all(o->lsdb); ospf6_route_remove_all(o->route_table); @@ -241,6 +249,7 @@ static void ospf6_disable(struct ospf6 *o) THREAD_OFF(o->t_spf_calc); THREAD_OFF(o->t_ase_calc); THREAD_OFF(o->t_distribute_update); + THREAD_OFF(o->t_ospf6_receive); } } @@ -325,7 +334,10 @@ DEFUN_NOSH (router_ospf6, OSPF6_STR) { if (ospf6 == NULL) { - ospf6 = ospf6_create(); + ospf6 = ospf6_create(VRF_DEFAULT); + if (DFLT_OSPF6_LOG_ADJACENCY_CHANGES) + SET_FLAG(ospf6->config_flags, + OSPF6_LOG_ADJACENCY_CHANGES); if (ospf6->router_id == 0) ospf6_router_id_update(); } @@ -386,8 +398,7 @@ DEFUN(ospf6_router_id, for (ALL_LIST_ELEMENTS_RO(o->area_list, node, oa)) { if (oa->full_nbrs) { vty_out(vty, - "For this router-id change to take effect," - " save config and restart ospf6d\n"); + "For this router-id change to take effect, save config and restart ospf6d\n"); return CMD_SUCCESS; } } @@ -413,8 +424,7 @@ DEFUN(no_ospf6_router_id, for (ALL_LIST_ELEMENTS_RO(o->area_list, node, oa)) { if (oa->full_nbrs) { vty_out(vty, - "For this router-id change to take effect," - " save config and restart ospf6d\n"); + "For this router-id change to take effect, save config and restart ospf6d\n"); return CMD_SUCCESS; } } @@ -640,11 +650,12 @@ DEFUN (no_ospf6_distance_source, DEFUN (ospf6_interface_area, ospf6_interface_area_cmd, - "interface IFNAME area A.B.C.D", + "interface IFNAME area ", "Enable routing on an IPv6 interface\n" IFNAME_STR "Specify the OSPF6 area ID\n" "OSPF6 area ID in IPv4 address notation\n" + "OSPF6 area ID in decimal notation\n" ) { VTY_DECLVAR_CONTEXT(ospf6, o); @@ -653,7 +664,6 @@ DEFUN (ospf6_interface_area, struct ospf6_area *oa; struct ospf6_interface *oi; struct interface *ifp; - uint32_t area_id; /* find/create ospf6 interface */ ifp = if_get_by_name(argv[idx_ifname]->arg, VRF_DEFAULT); @@ -667,15 +677,7 @@ DEFUN (ospf6_interface_area, } /* parse Area-ID */ - if (inet_pton(AF_INET, argv[idx_ipv4]->arg, &area_id) != 1) { - vty_out(vty, "Invalid Area-ID: %s\n", argv[idx_ipv4]->arg); - return CMD_SUCCESS; - } - - /* find/create ospf6 area */ - oa = ospf6_area_lookup(area_id, o); - if (oa == NULL) - oa = ospf6_area_create(area_id, o, OSPF6_AREA_FMT_DOTTEDQUAD); + OSPF6_CMD_AREA_GET(argv[idx_ipv4]->arg, oa); /* attach interface to area */ listnode_add(oa->if_list, oi); /* sort ?? */ @@ -699,12 +701,13 @@ DEFUN (ospf6_interface_area, DEFUN (no_ospf6_interface_area, no_ospf6_interface_area_cmd, - "no interface IFNAME area A.B.C.D", + "no interface IFNAME area ", NO_STR "Disable routing on an IPv6 interface\n" IFNAME_STR "Specify the OSPF6 area ID\n" "OSPF6 area ID in IPv4 address notation\n" + "OSPF6 area ID in decimal notation\n" ) { int idx_ifname = 2; @@ -727,14 +730,13 @@ DEFUN (no_ospf6_interface_area, } /* parse Area-ID */ - if (inet_pton(AF_INET, argv[idx_ipv4]->arg, &area_id) != 1) { - vty_out(vty, "Invalid Area-ID: %s\n", argv[idx_ipv4]->arg); - return CMD_SUCCESS; - } + if (inet_pton(AF_INET, argv[idx_ipv4]->arg, &area_id) != 1) + area_id = htonl(strtoul(argv[idx_ipv4]->arg, NULL, 10)); /* Verify Area */ if (oi->area == NULL) { - vty_out(vty, "No such Area-ID: %s\n", argv[idx_ipv4]->arg); + vty_out(vty, "%s not attached to area %s\n", + oi->interface->name, oi->area->name); return CMD_SUCCESS; } @@ -744,7 +746,7 @@ DEFUN (no_ospf6_interface_area, return CMD_SUCCESS; } - thread_execute(master, interface_down, oi, 0); + ospf6_interface_disable(oi); oa = oi->area; listnode_delete(oi->area->if_list, oi); @@ -1078,9 +1080,9 @@ static int config_write_ospf6(struct vty *vty) if (CHECK_FLAG(ospf6->config_flags, OSPF6_LOG_ADJACENCY_CHANGES)) { if (CHECK_FLAG(ospf6->config_flags, OSPF6_LOG_ADJACENCY_DETAIL)) vty_out(vty, " log-adjacency-changes detail\n"); - else if (!DFLT_OSPF6_LOG_ADJACENCY_CHANGES) + else if (!SAVE_OSPF6_LOG_ADJACENCY_CHANGES) vty_out(vty, " log-adjacency-changes\n"); - } else if (DFLT_OSPF6_LOG_ADJACENCY_CHANGES) { + } else if (SAVE_OSPF6_LOG_ADJACENCY_CHANGES) { vty_out(vty, " no log-adjacency-changes\n"); } @@ -1108,16 +1110,21 @@ static int config_write_ospf6(struct vty *vty) return 0; } +static int config_write_ospf6(struct vty *vty); /* OSPF6 node structure. */ static struct cmd_node ospf6_node = { - OSPF6_NODE, "%s(config-ospf6)# ", 1 /* VTYSH */ + .name = "ospf6", + .node = OSPF6_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-ospf6)# ", + .config_write = config_write_ospf6, }; /* Install ospf related commands. */ void ospf6_top_init(void) { /* Install ospf6 top node. */ - install_node(&ospf6_node, config_write_ospf6); + install_node(&ospf6_node); install_element(VIEW_NODE, &show_ipv6_ospf6_cmd); install_element(CONFIG_NODE, &router_ospf6_cmd); diff --git a/ospf6d/ospf6_top.h b/ospf6d/ospf6_top.h index 381027dcff..a3b61ba651 100644 --- a/ospf6d/ospf6_top.h +++ b/ospf6d/ospf6_top.h @@ -26,16 +26,25 @@ struct ospf6_master { - uint32_t zebra_router_id; + in_addr_t zebra_router_id; +}; + +/* ospf6->config_flags */ +enum { + OSPF6_LOG_ADJACENCY_CHANGES = (1 << 0), + OSPF6_LOG_ADJACENCY_DETAIL = (1 << 1), }; /* OSPFv3 top level data structure */ struct ospf6 { + /* The relevant vrf_id */ + vrf_id_t vrf_id; + /* my router id */ - uint32_t router_id; + in_addr_t router_id; /* static router id */ - uint32_t router_id_static; + in_addr_t router_id_static; struct in_addr router_id_zebra; @@ -65,10 +74,8 @@ struct ospf6 { uint8_t flag; - /* Configured flags */ + /* Configuration bitmask, refer to enum above */ uint8_t config_flags; -#define OSPF6_LOG_ADJACENCY_CHANGES (1 << 0) -#define OSPF6_LOG_ADJACENCY_DETAIL (1 << 1) /* LSA timer parameters */ unsigned int lsa_minarrival; /* LSA minimum arrival in milliseconds. */ @@ -90,6 +97,7 @@ struct ospf6 { struct thread *t_ase_calc; /* ASE calculation timer. */ struct thread *maxage_remover; struct thread *t_distribute_update; /* Distirbute update timer. */ + struct thread *t_ospf6_receive; /* OSPF6 receive timer */ uint32_t ref_bandwidth; diff --git a/ospf6d/ospf6_zebra.c b/ospf6d/ospf6_zebra.c index abdc82a738..62e0e149b8 100644 --- a/ospf6d/ospf6_zebra.c +++ b/ospf6d/ospf6_zebra.c @@ -48,8 +48,7 @@ unsigned char conf_debug_ospf6_zebra = 0; struct zclient *zclient = NULL; /* Router-id update message from zebra. */ -static int ospf6_router_id_update_zebra(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ospf6_router_id_update_zebra(ZAPI_CALLBACK_ARGS) { struct prefix router_id; struct ospf6 *o = ospf6; @@ -65,10 +64,9 @@ static int ospf6_router_id_update_zebra(int command, struct zclient *zclient, if (IS_OSPF6_DEBUG_ZEBRA(RECV)) { char buf[INET_ADDRSTRLEN]; - zlog_debug("%s: zebra router-id %s update", - __PRETTY_FUNCTION__, - inet_ntop(AF_INET, &router_id.u.prefix4, - buf, INET_ADDRSTRLEN)); + zlog_debug("%s: zebra router-id %s update", __func__, + inet_ntop(AF_INET, &router_id.u.prefix4, buf, + INET_ADDRSTRLEN)); } ospf6_router_id_update(); @@ -77,85 +75,28 @@ static int ospf6_router_id_update_zebra(int command, struct zclient *zclient, } /* redistribute function */ -void ospf6_zebra_redistribute(int type) +void ospf6_zebra_redistribute(int type, vrf_id_t vrf_id) { - if (vrf_bitmap_check(zclient->redist[AFI_IP6][type], VRF_DEFAULT)) + if (vrf_bitmap_check(zclient->redist[AFI_IP6][type], vrf_id)) return; - vrf_bitmap_set(zclient->redist[AFI_IP6][type], VRF_DEFAULT); + vrf_bitmap_set(zclient->redist[AFI_IP6][type], vrf_id); if (zclient->sock > 0) zebra_redistribute_send(ZEBRA_REDISTRIBUTE_ADD, zclient, - AFI_IP6, type, 0, VRF_DEFAULT); + AFI_IP6, type, 0, vrf_id); } -void ospf6_zebra_no_redistribute(int type) +void ospf6_zebra_no_redistribute(int type, vrf_id_t vrf_id) { - if (!vrf_bitmap_check(zclient->redist[AFI_IP6][type], VRF_DEFAULT)) + if (!vrf_bitmap_check(zclient->redist[AFI_IP6][type], vrf_id)) return; - vrf_bitmap_unset(zclient->redist[AFI_IP6][type], VRF_DEFAULT); + vrf_bitmap_unset(zclient->redist[AFI_IP6][type], vrf_id); if (zclient->sock > 0) zebra_redistribute_send(ZEBRA_REDISTRIBUTE_DELETE, zclient, - AFI_IP6, type, 0, VRF_DEFAULT); + AFI_IP6, type, 0, vrf_id); } -/* Inteface addition message from zebra. */ -static int ospf6_zebra_if_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) -{ - struct interface *ifp; - - ifp = zebra_interface_add_read(zclient->ibuf, vrf_id); - if (IS_OSPF6_DEBUG_ZEBRA(RECV)) - zlog_debug("Zebra Interface add: %s index %d mtu %d", ifp->name, - ifp->ifindex, ifp->mtu6); - ospf6_interface_if_add(ifp); - return 0; -} - -static int ospf6_zebra_if_del(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) -{ - struct interface *ifp; - - if (!(ifp = zebra_interface_state_read(zclient->ibuf, vrf_id))) - return 0; - - if (if_is_up(ifp)) - zlog_warn("Zebra: got delete of %s, but interface is still up", - ifp->name); - - if (IS_OSPF6_DEBUG_ZEBRA(RECV)) - zlog_debug("Zebra Interface delete: %s index %d mtu %d", - ifp->name, ifp->ifindex, ifp->mtu6); - - if_set_index(ifp, IFINDEX_INTERNAL); - return 0; -} - -static int ospf6_zebra_if_state_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) -{ - struct interface *ifp; - - ifp = zebra_interface_state_read(zclient->ibuf, vrf_id); - if (ifp == NULL) - return 0; - - if (IS_OSPF6_DEBUG_ZEBRA(RECV)) - zlog_debug( - "Zebra Interface state change: " - "%s index %d flags %llx metric %d mtu %d bandwidth %d", - ifp->name, ifp->ifindex, (unsigned long long)ifp->flags, - ifp->metric, ifp->mtu6, ifp->bandwidth); - - ospf6_interface_state_update(ifp); - return 0; -} - -static int ospf6_zebra_if_address_update_add(int command, - struct zclient *zclient, - zebra_size_t length, - vrf_id_t vrf_id) +static int ospf6_zebra_if_address_update_add(ZAPI_CALLBACK_ARGS) { struct connected *c; char buf[128]; @@ -179,10 +120,7 @@ static int ospf6_zebra_if_address_update_add(int command, return 0; } -static int ospf6_zebra_if_address_update_delete(int command, - struct zclient *zclient, - zebra_size_t length, - vrf_id_t vrf_id) +static int ospf6_zebra_if_address_update_delete(ZAPI_CALLBACK_ARGS) { struct connected *c; char buf[128]; @@ -204,13 +142,12 @@ static int ospf6_zebra_if_address_update_delete(int command, ospf6_interface_state_update(c->ifp); } - connected_free(c); + connected_free(&c); return 0; } -static int ospf6_zebra_read_route(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ospf6_zebra_read_route(ZAPI_CALLBACK_ARGS) { struct zapi_route api; unsigned long ifindex; @@ -234,19 +171,19 @@ static int ospf6_zebra_read_route(int command, struct zclient *zclient, if (IS_OSPF6_DEBUG_ZEBRA(RECV)) { char prefixstr[PREFIX2STR_BUFFER], nexthopstr[128]; - prefix2str((struct prefix *)&api.prefix, prefixstr, - sizeof(prefixstr)); + + prefix2str(&api.prefix, prefixstr, sizeof(prefixstr)); inet_ntop(AF_INET6, nexthop, nexthopstr, sizeof(nexthopstr)); zlog_debug( "Zebra Receive route %s: %s %s nexthop %s ifindex %ld tag %" ROUTE_TAG_PRI, - (command == ZEBRA_REDISTRIBUTE_ROUTE_ADD ? "add" - : "delete"), + (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD ? "add" + : "delete"), zebra_route_string(api.type), prefixstr, nexthopstr, ifindex, api.tag); } - if (command == ZEBRA_REDISTRIBUTE_ROUTE_ADD) + if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD) ospf6_asbr_redistribute_add(api.type, ifindex, &api.prefix, api.nexthop_num, nexthop, api.tag); else @@ -269,7 +206,7 @@ DEFUN (show_zebra, return CMD_SUCCESS; } - vty_out(vty, "Zebra Infomation\n"); + vty_out(vty, "Zebra Information\n"); vty_out(vty, " fail: %d\n", zclient->fail); vty_out(vty, " redistribute default: %d\n", vrf_bitmap_check(zclient->default_information[AFI_IP6], @@ -319,7 +256,7 @@ static void ospf6_zebra_route_update(int type, struct ospf6_route *request) && ospf6_route_is_same(request, request->next)) { if (IS_OSPF6_DEBUG_ZEBRA(SEND)) zlog_debug( - " Best-path removal resulted Sencondary addition"); + " Best-path removal resulted Secondary addition"); type = ADD; request = request->next; } @@ -342,7 +279,7 @@ static void ospf6_zebra_route_update(int type, struct ospf6_route *request) dest = &request->prefix; memset(&api, 0, sizeof(api)); - api.vrf_id = VRF_DEFAULT; + api.vrf_id = ospf6->vrf_id; api.type = ZEBRA_ROUTE_OSPF6; api.safi = SAFI_UNICAST; api.prefix = *dest; @@ -393,7 +330,7 @@ void ospf6_zebra_add_discard(struct ospf6_route *request) if (!CHECK_FLAG(request->flag, OSPF6_ROUTE_BLACKHOLE_ADDED)) { memset(&api, 0, sizeof(api)); - api.vrf_id = VRF_DEFAULT; + api.vrf_id = ospf6->vrf_id; api.type = ZEBRA_ROUTE_OSPF6; api.safi = SAFI_UNICAST; api.prefix = *dest; @@ -426,7 +363,7 @@ void ospf6_zebra_delete_discard(struct ospf6_route *request) if (CHECK_FLAG(request->flag, OSPF6_ROUTE_BLACKHOLE_ADDED)) { memset(&api, 0, sizeof(api)); - api.vrf_id = VRF_DEFAULT; + api.vrf_id = ospf6->vrf_id; api.type = ZEBRA_ROUTE_OSPF6; api.safi = SAFI_UNICAST; api.prefix = *dest; @@ -582,7 +519,7 @@ uint8_t ospf6_distance_apply(struct prefix_ipv6 *p, struct ospf6_route * or) static void ospf6_zebra_connected(struct zclient *zclient) { /* Send the client registration */ - bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER); + bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, VRF_DEFAULT); zclient_send_reg_requests(zclient, VRF_DEFAULT); } @@ -594,10 +531,6 @@ void ospf6_zebra_init(struct thread_master *master) zclient_init(zclient, ZEBRA_ROUTE_OSPF6, 0, &ospf6d_privs); zclient->zebra_connected = ospf6_zebra_connected; zclient->router_id_update = ospf6_router_id_update_zebra; - zclient->interface_add = ospf6_zebra_if_add; - zclient->interface_delete = ospf6_zebra_if_del; - zclient->interface_up = ospf6_zebra_if_state_update; - zclient->interface_down = ospf6_zebra_if_state_update; zclient->interface_address_add = ospf6_zebra_if_address_update_add; zclient->interface_address_delete = ospf6_zebra_if_address_update_delete; diff --git a/ospf6d/ospf6_zebra.h b/ospf6d/ospf6_zebra.h index e2f778fa72..d23268303a 100644 --- a/ospf6d/ospf6_zebra.h +++ b/ospf6d/ospf6_zebra.h @@ -45,10 +45,10 @@ extern struct zclient *zclient; extern void ospf6_zebra_route_update_add(struct ospf6_route *request); extern void ospf6_zebra_route_update_remove(struct ospf6_route *request); -extern void ospf6_zebra_redistribute(int); -extern void ospf6_zebra_no_redistribute(int); -#define ospf6_zebra_is_redistribute(type) \ - vrf_bitmap_check(zclient->redist[AFI_IP6][type], VRF_DEFAULT) +extern void ospf6_zebra_redistribute(int, vrf_id_t vrf_id); +extern void ospf6_zebra_no_redistribute(int, vrf_id_t vrf_id); +#define ospf6_zebra_is_redistribute(type, vrf_id) \ + vrf_bitmap_check(zclient->redist[AFI_IP6][type], vrf_id) extern void ospf6_zebra_init(struct thread_master *); extern void ospf6_zebra_add_discard(struct ospf6_route *request); extern void ospf6_zebra_delete_discard(struct ospf6_route *request); diff --git a/ospf6d/ospf6d.c b/ospf6d/ospf6d.c index db61fe087b..3d53608853 100644 --- a/ospf6d/ospf6d.c +++ b/ospf6d/ospf6d.c @@ -69,8 +69,12 @@ struct route_node *route_prev(struct route_node *node) return prev; } +static int config_write_ospf6_debug(struct vty *vty); static struct cmd_node debug_node = { - DEBUG_NODE, "", 1 /* VTYSH */ + .name = "debug", + .node = DEBUG_NODE, + .prompt = "", + .config_write = config_write_ospf6_debug, }; static int config_write_ospf6_debug(struct vty *vty) @@ -1216,7 +1220,7 @@ void ospf6_init(void) prefix_list_delete_hook(ospf6_plist_del); ospf6_bfd_init(); - install_node(&debug_node, config_write_ospf6_debug); + install_node(&debug_node); install_element_ospf6_debug_message(); install_element_ospf6_debug_lsa(); @@ -1232,7 +1236,7 @@ void ospf6_init(void) install_element_ospf6_clear_interface(); - install_element(VIEW_NODE, &show_debugging_ospf6_cmd); + install_element(ENABLE_NODE, &show_debugging_ospf6_cmd); install_element(VIEW_NODE, &show_ipv6_ospf6_border_routers_cmd); @@ -1263,10 +1267,6 @@ void ospf6_init(void) VIEW_NODE, &show_ipv6_ospf6_database_type_self_originated_linkstate_id_cmd); install_element(VIEW_NODE, &show_ipv6_ospf6_database_aggr_router_cmd); - - /* Make ospf protocol socket. */ - ospf6_serv_sock(); - thread_add_read(master, ospf6_receive, NULL, ospf6_sock, NULL); } void ospf6_clean(void) diff --git a/ospf6d/subdir.am b/ospf6d/subdir.am index eac0eee45f..9bb6838310 100644 --- a/ospf6d/subdir.am +++ b/ospf6d/subdir.am @@ -7,26 +7,26 @@ noinst_LIBRARIES += ospf6d/libospf6.a sbin_PROGRAMS += ospf6d/ospf6d dist_examples_DATA += ospf6d/ospf6d.conf.sample vtysh_scan += \ - $(top_srcdir)/ospf6d/ospf6_abr.c \ - $(top_srcdir)/ospf6d/ospf6_asbr.c \ - $(top_srcdir)/ospf6d/ospf6_area.c \ - $(top_srcdir)/ospf6d/ospf6_bfd.c \ - $(top_srcdir)/ospf6d/ospf6_flood.c \ - $(top_srcdir)/ospf6d/ospf6_interface.c \ - $(top_srcdir)/ospf6d/ospf6_intra.c \ - $(top_srcdir)/ospf6d/ospf6_lsa.c \ - $(top_srcdir)/ospf6d/ospf6_message.c \ - $(top_srcdir)/ospf6d/ospf6_neighbor.c \ - $(top_srcdir)/ospf6d/ospf6_route.c \ - $(top_srcdir)/ospf6d/ospf6_spf.c \ - $(top_srcdir)/ospf6d/ospf6_top.c \ - $(top_srcdir)/ospf6d/ospf6_zebra.c \ - $(top_srcdir)/ospf6d/ospf6d.c \ + ospf6d/ospf6_abr.c \ + ospf6d/ospf6_asbr.c \ + ospf6d/ospf6_area.c \ + ospf6d/ospf6_bfd.c \ + ospf6d/ospf6_flood.c \ + ospf6d/ospf6_interface.c \ + ospf6d/ospf6_intra.c \ + ospf6d/ospf6_lsa.c \ + ospf6d/ospf6_message.c \ + ospf6d/ospf6_neighbor.c \ + ospf6d/ospf6_route.c \ + ospf6d/ospf6_spf.c \ + ospf6d/ospf6_top.c \ + ospf6d/ospf6_zebra.c \ + ospf6d/ospf6d.c \ # end if SNMP module_LTLIBRARIES += ospf6d/ospf6d_snmp.la endif -man8 += $(MANBUILD)/ospf6d.8 +man8 += $(MANBUILD)/frr-ospf6d.8 endif ospf6d_libospf6_a_SOURCES = \ diff --git a/ospfclient/ospf_apiclient.c b/ospfclient/ospf_apiclient.c index 50485cc7e2..da390e3c70 100644 --- a/ospfclient/ospf_apiclient.c +++ b/ospfclient/ospf_apiclient.c @@ -451,7 +451,7 @@ int ospf_apiclient_lsa_originate(struct ospf_apiclient *oclient, tmp = SET_OPAQUE_LSID(opaque_type, opaque_id); lsah->id.s_addr = htonl(tmp); - lsah->adv_router.s_addr = 0; + lsah->adv_router.s_addr = INADDR_ANY; lsah->ls_seqnum = 0; lsah->checksum = 0; lsah->length = htons(sizeof(struct lsa_header) + opaquelen); diff --git a/ospfclient/subdir.am b/ospfclient/subdir.am index 94d489358c..756ad88f15 100644 --- a/ospfclient/subdir.am +++ b/ospfclient/subdir.am @@ -5,7 +5,7 @@ if OSPFCLIENT lib_LTLIBRARIES += ospfclient/libfrrospfapiclient.la noinst_PROGRAMS += ospfclient/ospfclient -man8 += $(MANBUILD)/ospfclient.8 +#man8 += $(MANBUILD)/frr-ospfclient.8 endif ospfclient_libfrrospfapiclient_la_LDFLAGS = -version-info 0:0:0 diff --git a/ospfd/ospf_abr.c b/ospfd/ospf_abr.c index c8b8b611ef..3f89ab0382 100644 --- a/ospfd/ospf_abr.c +++ b/ospfd/ospf_abr.c @@ -310,7 +310,7 @@ int ospf_area_range_substitute_unset(struct ospf *ospf, struct in_addr area_id, ospf_schedule_abr_task(ospf); UNSET_FLAG(range->flags, OSPF_AREA_RANGE_SUBSTITUTE); - range->subst_addr.s_addr = 0; + range->subst_addr.s_addr = INADDR_ANY; range->subst_masklen = 0; return 1; @@ -348,8 +348,7 @@ static int ospf_abr_nssa_am_elected(struct ospf_area *area) if (IS_ROUTER_LSA_NT(rlsa)) { if (IS_DEBUG_OSPF_NSSA) zlog_debug( - "ospf_abr_nssa_am_elected: " - "router %s asserts Nt", + "ospf_abr_nssa_am_elected: router %s asserts Nt", inet_ntoa(lsa->data->id)); return 0; } @@ -378,7 +377,7 @@ static int ospf_abr_nssa_am_elected(struct ospf_area *area) /* Check NSSA ABR status * assumes there are nssa areas */ -static void ospf_abr_nssa_check_status(struct ospf *ospf) +void ospf_abr_nssa_check_status(struct ospf *ospf) { struct ospf_area *area; struct listnode *lnode, *nnode; @@ -391,15 +390,13 @@ static void ospf_abr_nssa_check_status(struct ospf *ospf) if (IS_DEBUG_OSPF(nssa, NSSA)) zlog_debug( - "ospf_abr_nssa_check_status: " - "checking area %s", + "ospf_abr_nssa_check_status: checking area %s", inet_ntoa(area->area_id)); if (!IS_OSPF_ABR(area->ospf)) { if (IS_DEBUG_OSPF(nssa, NSSA)) zlog_debug( - "ospf_abr_nssa_check_status: " - "not ABR"); + "ospf_abr_nssa_check_status: not ABR"); area->NSSATranslatorState = OSPF_NSSA_TRANSLATE_DISABLED; } else { @@ -409,8 +406,7 @@ static void ospf_abr_nssa_check_status(struct ospf *ospf) /* TODO: check previous state and flush? */ if (IS_DEBUG_OSPF(nssa, NSSA)) zlog_debug( - "ospf_abr_nssa_check_status: " - "never translate"); + "ospf_abr_nssa_check_status: never translate"); area->NSSATranslatorState = OSPF_NSSA_TRANSLATE_DISABLED; break; @@ -422,8 +418,7 @@ static void ospf_abr_nssa_check_status(struct ospf *ospf) */ if (IS_DEBUG_OSPF(nssa, NSSA)) zlog_debug( - "ospf_abr_nssa_check_status: " - "translate always"); + "ospf_abr_nssa_check_status: translate always"); area->NSSATranslatorState = OSPF_NSSA_TRANSLATE_ENABLED; break; @@ -435,15 +430,13 @@ static void ospf_abr_nssa_check_status(struct ospf *ospf) OSPF_NSSA_TRANSLATE_ENABLED; if (IS_DEBUG_OSPF(nssa, NSSA)) zlog_debug( - "ospf_abr_nssa_check_status: " - "elected translator"); + "ospf_abr_nssa_check_status: elected translator"); } else { area->NSSATranslatorState = OSPF_NSSA_TRANSLATE_DISABLED; if (IS_DEBUG_OSPF(nssa, NSSA)) zlog_debug( - "ospf_abr_nssa_check_status: " - "not elected"); + "ospf_abr_nssa_check_status: not elected"); } break; } @@ -639,21 +632,32 @@ static int ospf_abr_translate_nssa(struct ospf_area *area, struct ospf_lsa *lsa) if (ext7->e[0].fwd_addr.s_addr == OSPF_DEFAULT_DESTINATION) { if (IS_DEBUG_OSPF_NSSA) zlog_debug( - "ospf_abr_translate_nssa(): LSA Id %s, " - "Forward address is 0, NO Translation", + "ospf_abr_translate_nssa(): LSA Id %s, Forward address is 0, NO Translation", inet_ntoa(lsa->data->id)); return 1; } /* try find existing AS-External LSA for this prefix */ - old = ospf_external_info_find_lsa(area->ospf, &p); - if (old) { + if (CHECK_FLAG(lsa->flags, OSPF_LSA_IN_MAXAGE)) { + /* if type-7 is removed, remove old translated type-5 lsa */ + if (old) { + UNSET_FLAG(old->flags, OSPF_LSA_APPROVED); + if (IS_DEBUG_OSPF_NSSA) + zlog_debug( + "ospf_abr_translate_nssa(): remove old translated LSA id %s", + inet_ntoa(old->data->id)); + } + /* if type-7 is removed and type-5 does not exist, do not + * originate */ + return 1; + } + + if (old && CHECK_FLAG(old->flags, OSPF_LSA_APPROVED)) { if (IS_DEBUG_OSPF_NSSA) zlog_debug( - "ospf_abr_translate_nssa(): " - "found old translated LSA Id %s, refreshing", + "ospf_abr_translate_nssa(): found old translated LSA Id %s, refreshing", inet_ntoa(old->data->id)); /* refresh */ @@ -661,8 +665,7 @@ static int ospf_abr_translate_nssa(struct ospf_area *area, struct ospf_lsa *lsa) if (!new) { if (IS_DEBUG_OSPF_NSSA) zlog_debug( - "ospf_abr_translate_nssa(): " - "could not refresh translated LSA Id %s", + "ospf_abr_translate_nssa(): could not refresh translated LSA Id %s", inet_ntoa(old->data->id)); } } else { @@ -670,12 +673,10 @@ static int ospf_abr_translate_nssa(struct ospf_area *area, struct ospf_lsa *lsa) * originate translated LSA */ - if ((new = ospf_translated_nssa_originate(area->ospf, lsa)) - == NULL) { + if (ospf_translated_nssa_originate(area->ospf, lsa) == NULL) { if (IS_DEBUG_OSPF_NSSA) zlog_debug( - "ospf_abr_translate_nssa(): Could not translate " - "Type-7 for %s to Type-5", + "ospf_abr_translate_nssa(): Could not translate Type-7 for %s to Type-5", inet_ntoa(lsa->data->id)); return 1; } @@ -709,8 +710,7 @@ void ospf_abr_announce_network_to_area(struct prefix_ipv4 *p, uint32_t cost, else full_cost = cost; - old = ospf_lsa_lookup_by_prefix(area->lsdb, OSPF_SUMMARY_LSA, - (struct prefix_ipv4 *)p, + old = ospf_lsa_lookup_by_prefix(area->lsdb, OSPF_SUMMARY_LSA, p, area->ospf->router_id); if (old) { if (IS_DEBUG_OSPF_EVENT) @@ -721,8 +721,7 @@ void ospf_abr_announce_network_to_area(struct prefix_ipv4 *p, uint32_t cost, if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_abr_announce_network_to_area(): " - "old metric: %d, new metric: %d", + "ospf_abr_announce_network_to_area(): old metric: %d, new metric: %d", GET_METRIC(sl->metric), cost); if ((GET_METRIC(sl->metric) == full_cost) @@ -730,15 +729,13 @@ void ospf_abr_announce_network_to_area(struct prefix_ipv4 *p, uint32_t cost, /* unchanged. simply reapprove it */ if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_abr_announce_network_to_area(): " - "old summary approved"); + "ospf_abr_announce_network_to_area(): old summary approved"); SET_FLAG(old->flags, OSPF_LSA_APPROVED); } else { /* LSA is changed, refresh it */ if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_abr_announce_network_to_area(): " - "refreshing summary"); + "ospf_abr_announce_network_to_area(): refreshing summary"); set_metric(old, full_cost); lsa = ospf_lsa_refresh(area->ospf, old); @@ -760,10 +757,8 @@ void ospf_abr_announce_network_to_area(struct prefix_ipv4 *p, uint32_t cost, } else { if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_abr_announce_network_to_area(): " - "creating new summary"); - lsa = ospf_summary_lsa_originate((struct prefix_ipv4 *)p, - full_cost, area); + "ospf_abr_announce_network_to_area(): creating new summary"); + lsa = ospf_summary_lsa_originate(p, full_cost, area); /* This will flood through area. */ if (!lsa) { @@ -779,8 +774,7 @@ void ospf_abr_announce_network_to_area(struct prefix_ipv4 *p, uint32_t cost, SET_FLAG(lsa->flags, OSPF_LSA_APPROVED); if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_abr_announce_network_to_area(): " - "flooding new version of summary"); + "ospf_abr_announce_network_to_area(): flooding new version of summary"); } if (IS_DEBUG_OSPF_EVENT) @@ -878,8 +872,7 @@ static void ospf_abr_announce_network(struct ospf *ospf, struct prefix_ipv4 *p, if (!ospf_abr_should_accept(p, area)) { if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_abr_announce_network(): " - "prefix %s/%d was denied by import-list", + "ospf_abr_announce_network(): prefix %s/%d was denied by import-list", inet_ntoa(p->prefix), p->prefixlen); continue; } @@ -887,8 +880,7 @@ static void ospf_abr_announce_network(struct ospf *ospf, struct prefix_ipv4 *p, if (!ospf_abr_plist_in_check(area, or, p)) { if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_abr_announce_network(): " - "prefix %s/%d was denied by prefix-list", + "ospf_abr_announce_network(): prefix %s/%d was denied by prefix-list", inet_ntoa(p->prefix), p->prefixlen); continue; } @@ -897,8 +889,7 @@ static void ospf_abr_announce_network(struct ospf *ospf, struct prefix_ipv4 *p, && area->no_summary) { if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_abr_announce_network(): " - "area %s is stub and no_summary", + "ospf_abr_announce_network(): area %s is stub and no_summary", inet_ntoa(area->area_id)); continue; } @@ -906,8 +897,7 @@ static void ospf_abr_announce_network(struct ospf *ospf, struct prefix_ipv4 *p, if (or->path_type == OSPF_PATH_INTER_AREA) { if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_abr_announce_network(): this is " - "inter-area route to %s/%d", + "ospf_abr_announce_network(): this is inter-area route to %s/%d", inet_ntoa(p->prefix), p->prefixlen); if (!OSPF_IS_AREA_BACKBONE(area)) @@ -918,8 +908,7 @@ static void ospf_abr_announce_network(struct ospf *ospf, struct prefix_ipv4 *p, if (or->path_type == OSPF_PATH_INTRA_AREA) { if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_abr_announce_network(): " - "this is intra-area route to %s/%d", + "ospf_abr_announce_network(): this is intra-area route to %s/%d", inet_ntoa(p->prefix), p->prefixlen); if ((range = ospf_area_range_match(or_area, p)) && !ospf_area_is_transit(area)) @@ -979,8 +968,7 @@ static void ospf_abr_process_nssa_translates(struct ospf *ospf) if (IS_DEBUG_OSPF_NSSA) zlog_debug( - "ospf_abr_process_nssa_translates(): " - "looking at area %s", + "ospf_abr_process_nssa_translates(): looking at area %s", inet_ntoa(area->area_id)); LSDB_LOOP (NSSA_LSDB(area), rn, lsa) @@ -1021,24 +1009,21 @@ static void ospf_abr_process_network_rt(struct ospf *ospf, if (or->path_type >= OSPF_PATH_TYPE1_EXTERNAL) { if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_abr_process_network_rt(): " - "this is an External router, skipping"); + "ospf_abr_process_network_rt(): this is an External router, skipping"); continue; } if (or->cost >= OSPF_LS_INFINITY) { if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_abr_process_network_rt():" - " this route's cost is infinity, skipping"); + "ospf_abr_process_network_rt(): this route's cost is infinity, skipping"); continue; } if (or->type == OSPF_DESTINATION_DISCARD) { if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_abr_process_network_rt():" - " this is a discard entry, skipping"); + "ospf_abr_process_network_rt(): this is a discard entry, skipping"); continue; } @@ -1068,8 +1053,7 @@ static void ospf_abr_process_network_rt(struct ospf *ospf, && !OSPF_IS_AREA_ID_BACKBONE(or->u.std.area_id)) { if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_abr_process_network_rt():" - " this is route is not backbone one, skipping"); + "ospf_abr_process_network_rt(): this is route is not backbone one, skipping"); continue; } @@ -1081,8 +1065,7 @@ static void ospf_abr_process_network_rt(struct ospf *ospf, or->path_type != OSPF_PATH_INTRA_AREA) { if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_abr_process_network_rt(): ALT ABR: " - "No BB connection, skip not intra-area routes"); + "ospf_abr_process_network_rt(): ALT ABR: No BB connection, skip not intra-area routes"); continue; } @@ -1115,8 +1098,7 @@ static void ospf_abr_announce_rtr_to_area(struct prefix_ipv4 *p, uint32_t cost, if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_abr_announce_network_to_area(): " - "old metric: %d, new metric: %d", + "ospf_abr_announce_network_to_area(): old metric: %d, new metric: %d", GET_METRIC(slsa->metric), cost); } @@ -1147,8 +1129,7 @@ static void ospf_abr_announce_rtr_to_area(struct prefix_ipv4 *p, uint32_t cost, if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_abr_announce_rtr_to_area(): " - "flooding new version of summary"); + "ospf_abr_announce_rtr_to_area(): flooding new version of summary"); /* zlog_info ("ospf_abr_announce_rtr_to_area(): creating new @@ -1185,11 +1166,19 @@ static void ospf_abr_announce_rtr(struct ospf *ospf, struct prefix_ipv4 *p, if (ospf_abr_nexthops_belong_to_area(or, area)) continue; + /* RFC3101: Do not generate ASBR type 4 LSA if NSSA ABR */ + if (or->u.std.external_routing == OSPF_AREA_NSSA) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "ospf_abr_announce_rtr(): do not generate LSA Type-4 %s from NSSA", + inet_ntoa(p->prefix)); + continue; + } + if (area->external_routing != OSPF_AREA_DEFAULT) { if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_abr_announce_rtr(): " - "area %s doesn't support external routing", + "ospf_abr_announce_rtr(): area %s doesn't support external routing", inet_ntoa(area->area_id)); continue; } @@ -1197,8 +1186,7 @@ static void ospf_abr_announce_rtr(struct ospf *ospf, struct prefix_ipv4 *p, if (or->path_type == OSPF_PATH_INTER_AREA) { if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_abr_announce_rtr(): " - "this is inter-area route to %s", + "ospf_abr_announce_rtr(): this is inter-area route to %s", inet_ntoa(p->prefix)); if (!OSPF_IS_AREA_BACKBONE(area)) ospf_abr_announce_rtr_to_area(p, or->cost, @@ -1208,8 +1196,7 @@ static void ospf_abr_announce_rtr(struct ospf *ospf, struct prefix_ipv4 *p, if (or->path_type == OSPF_PATH_INTRA_AREA) { if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_abr_announce_rtr(): " - "this is intra-area route to %s", + "ospf_abr_announce_rtr(): this is intra-area route to %s", inet_ntoa(p->prefix)); ospf_abr_announce_rtr_to_area(p, or->cost, area); } @@ -1258,8 +1245,7 @@ static void ospf_abr_process_router_rt(struct ospf *ospf, if (!CHECK_FLAG(or->u.std.flags, ROUTER_LSA_EXTERNAL)) { if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_abr_process_router_rt(): " - "This is not an ASBR, skipping"); + "ospf_abr_process_router_rt(): This is not an ASBR, skipping"); continue; } @@ -1275,8 +1261,7 @@ static void ospf_abr_process_router_rt(struct ospf *ospf, if (or != best) { if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_abr_process_router_rt(): " - "This route is not the best among possible, skipping"); + "ospf_abr_process_router_rt(): This route is not the best among possible, skipping"); continue; } @@ -1286,16 +1271,14 @@ static void ospf_abr_process_router_rt(struct ospf *ospf, or->u.std.area_id)) { if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_abr_process_router_rt(): " - "This route is not a backbone one, skipping"); + "ospf_abr_process_router_rt(): This route is not a backbone one, skipping"); continue; } if (or->cost >= OSPF_LS_INFINITY) { if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_abr_process_router_rt(): " - "This route has LS_INFINITY metric, skipping"); + "ospf_abr_process_router_rt(): This route has LS_INFINITY metric, skipping"); continue; } @@ -1305,8 +1288,7 @@ static void ospf_abr_process_router_rt(struct ospf *ospf, or->path_type != OSPF_PATH_INTRA_AREA) { if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_abr_process_network_rt(): ALT ABR: " - "No BB connection, skip not intra-area routes"); + "ospf_abr_process_network_rt(): ALT ABR: No BB connection, skip not intra-area routes"); continue; } @@ -1336,8 +1318,7 @@ ospf_abr_unapprove_translates(struct ospf *ospf) /* For NSSA Translations */ UNSET_FLAG(lsa->flags, OSPF_LSA_APPROVED); if (IS_DEBUG_OSPF_NSSA) zlog_debug( - "ospf_abr_unapprove_translates(): " - "approved unset on link id %s", + "ospf_abr_unapprove_translates(): approved unset on link id %s", inet_ntoa(lsa->data->id)); } @@ -1358,15 +1339,13 @@ static void ospf_abr_unapprove_summaries(struct ospf *ospf) for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) { if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_abr_unapprove_summaries(): " - "considering area %s", + "ospf_abr_unapprove_summaries(): considering area %s", inet_ntoa(area->area_id)); LSDB_LOOP (SUMMARY_LSDB(area), rn, lsa) if (ospf_lsa_is_self_originated(ospf, lsa)) { if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_abr_unapprove_summaries(): " - "approved unset on summary link id %s", + "ospf_abr_unapprove_summaries(): approved unset on summary link id %s", inet_ntoa(lsa->data->id)); UNSET_FLAG(lsa->flags, OSPF_LSA_APPROVED); } @@ -1375,8 +1354,7 @@ static void ospf_abr_unapprove_summaries(struct ospf *ospf) if (ospf_lsa_is_self_originated(ospf, lsa)) { if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_abr_unapprove_summaries(): " - "approved unset on asbr-summary link id %s", + "ospf_abr_unapprove_summaries(): approved unset on asbr-summary link id %s", inet_ntoa(lsa->data->id)); UNSET_FLAG(lsa->flags, OSPF_LSA_APPROVED); } @@ -1431,8 +1409,7 @@ static void ospf_abr_announce_aggregates(struct ospf *ospf) OSPF_AREA_RANGE_ADVERTISE)) { if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_abr_announce_aggregates():" - " discarding suppress-ranges"); + "ospf_abr_announce_aggregates(): discarding suppress-ranges"); continue; } @@ -1442,8 +1419,7 @@ static void ospf_abr_announce_aggregates(struct ospf *ospf) if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_abr_announce_aggregates():" - " this is range: %s/%d", + "ospf_abr_announce_aggregates(): this is range: %s/%d", inet_ntoa(p.u.prefix4), p.prefixlen); @@ -1480,9 +1456,7 @@ static void ospf_abr_announce_aggregates(struct ospf *ospf) area)) { if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_abr_announce_aggregates(): Skipping " - "announcement of BB aggregate into" - " a transit area"); + "ospf_abr_announce_aggregates(): Skipping announcement of BB aggregate into a transit area"); continue; } ospf_abr_announce_network_to_area( @@ -1529,8 +1503,7 @@ ospf_abr_send_nssa_aggregates(struct ospf *ospf) /* temporarily turned off */ OSPF_AREA_RANGE_ADVERTISE)) { if (IS_DEBUG_OSPF_NSSA) zlog_debug( - "ospf_abr_send_nssa_aggregates():" - " discarding suppress-ranges"); + "ospf_abr_send_nssa_aggregates(): discarding suppress-ranges"); continue; } @@ -1540,8 +1513,7 @@ ospf_abr_send_nssa_aggregates(struct ospf *ospf) /* temporarily turned off */ if (IS_DEBUG_OSPF_NSSA) zlog_debug( - "ospf_abr_send_nssa_aggregates():" - " this is range: %s/%d", + "ospf_abr_send_nssa_aggregates(): this is range: %s/%d", inet_ntoa(p.prefix), p.prefixlen); if (CHECK_FLAG(range->flags, @@ -1601,8 +1573,7 @@ static void ospf_abr_announce_stub_defaults(struct ospf *ospf) if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_abr_announce_stub_defaults(): " - "announcing 0.0.0.0/0 to area %s", + "ospf_abr_announce_stub_defaults(): announcing 0.0.0.0/0 to area %s", inet_ntoa(area->area_id)); ospf_abr_announce_network_to_area(&p, area->default_cost, area); } @@ -1617,8 +1588,7 @@ static int ospf_abr_remove_unapproved_translates_apply(struct ospf *ospf, if (CHECK_FLAG(lsa->flags, OSPF_LSA_LOCAL_XLT) && !CHECK_FLAG(lsa->flags, OSPF_LSA_APPROVED)) { zlog_info( - "ospf_abr_remove_unapproved_translates(): " - "removing unapproved translates, ID: %s", + "ospf_abr_remove_unapproved_translates(): removing unapproved translates, ID: %s", inet_ntoa(lsa->data->id)); /* FLUSH THROUGHOUT AS */ @@ -1659,8 +1629,7 @@ static void ospf_abr_remove_unapproved_summaries(struct ospf *ospf) for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) { if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_abr_remove_unapproved_summaries(): " - "looking at area %s", + "ospf_abr_remove_unapproved_summaries(): looking at area %s", inet_ntoa(area->area_id)); LSDB_LOOP (SUMMARY_LSDB(area), rn, lsa) @@ -1855,6 +1824,7 @@ static int ospf_abr_task_timer(struct thread *thread) ospf_abr_task(ospf); ospf_abr_nssa_task(ospf); /* if nssa-abr, then scan Type-7 LSDB */ + ospf_asbr_nssa_redist_task(ospf); return 0; } diff --git a/ospfd/ospf_abr.h b/ospfd/ospf_abr.h index b3007622c4..e15f4a6bf7 100644 --- a/ospfd/ospf_abr.h +++ b/ospfd/ospf_abr.h @@ -83,4 +83,5 @@ extern void ospf_schedule_abr_task(struct ospf *); extern void ospf_abr_announce_network_to_area(struct prefix_ipv4 *, uint32_t, struct ospf_area *); +extern void ospf_abr_nssa_check_status(struct ospf *ospf); #endif /* _ZEBRA_OSPF_ABR_H */ diff --git a/ospfd/ospf_api.c b/ospfd/ospf_api.c index f06e45392e..7e7236a3b6 100644 --- a/ospfd/ospf_api.c +++ b/ospfd/ospf_api.c @@ -246,13 +246,6 @@ void msg_print(struct msg *msg) return; } -#ifdef ORIGINAL_CODING - zlog_debug( - "msg=%p msgtype=%d msglen=%d msgseq=%d streamdata=%p streamsize=%lu\n", - msg, msg->hdr.msgtype, ntohs(msg->hdr.msglen), - ntohl(msg->hdr.msgseq), STREAM_DATA(msg->s), - STREAM_SIZE(msg->s)); -#else /* ORIGINAL_CODING */ /* API message common header part. */ zlog_debug("API-msg [%s]: type(%d),len(%d),seq(%lu),data(%p),size(%zd)", ospf_api_typename(msg->hdr.msgtype), msg->hdr.msgtype, @@ -260,16 +253,7 @@ void msg_print(struct msg *msg) (unsigned long)ntohl(msg->hdr.msgseq), STREAM_DATA(msg->s), STREAM_SIZE(msg->s)); -/* API message body part. */ -#ifdef ndef - /* Generic Hex/Ascii dump */ - DumpBuf(STREAM_DATA(msg->s), STREAM_SIZE(msg->s)); /* Sorry, deleted! */ -#else /* ndef */ -/* Message-type dependent dump function. */ -#endif /* ndef */ - return; -#endif /* ORIGINAL_CODING */ } void msg_free(struct msg *msg) @@ -369,8 +353,8 @@ struct msg *msg_read(int fd) struct msg *msg; struct apimsghdr hdr; uint8_t buf[OSPF_API_MAX_MSG_SIZE]; - int bodylen; - int rlen; + ssize_t bodylen; + ssize_t rlen; /* Read message header */ rlen = readn(fd, (uint8_t *)&hdr, sizeof(struct apimsghdr)); @@ -394,8 +378,13 @@ struct msg *msg_read(int fd) /* Determine body length. */ bodylen = ntohs(hdr.msglen); - if (bodylen > 0) { + if (bodylen > (ssize_t)sizeof(buf)) { + zlog_warn("%s: Body Length of message greater than what we can read", + __func__); + return NULL; + } + if (bodylen > 0) { /* Read message body */ rlen = readn(fd, buf, bodylen); if (rlen < 0) { diff --git a/ospfd/ospf_api.h b/ospfd/ospf_api.h index c99923e7b8..0fc683a5db 100644 --- a/ospfd/ospf_api.h +++ b/ospfd/ospf_api.h @@ -140,16 +140,10 @@ struct msg_unregister_opaque_type { * Power2[0] is not used. */ -#ifdef ORIGINAL_CODING -static const uint16_t Power2[] = {0x0, 0x1, 0x2, 0x4, 0x8, 0x10, - 0x20, 0x40, 0x80, 0x100, 0x200, 0x400, - 0x800, 0x1000, 0x2000, 0x4000, 0x8000}; -#else static const uint16_t Power2[] = { 0, (1 << 0), (1 << 1), (1 << 2), (1 << 3), (1 << 4), (1 << 5), (1 << 6), (1 << 7), (1 << 8), (1 << 9), (1 << 10), (1 << 11), (1 << 12), (1 << 13), (1 << 14), (1 << 15)}; -#endif /* ORIGINAL_CODING */ struct lsa_filter_type { uint16_t typemask; /* bitmask for selecting LSA types (1..16) */ diff --git a/ospfd/ospf_apiserver.c b/ospfd/ospf_apiserver.c index d6f1fba28b..73ce606504 100644 --- a/ospfd/ospf_apiserver.c +++ b/ospfd/ospf_apiserver.c @@ -889,8 +889,7 @@ int ospf_apiserver_register_opaque_type(struct ospf_apiserver *apiserv, if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "API: Add LSA-type(%d)/Opaque-type(%d) into" - " apiserv(%p), total#(%d)", + "API: Add LSA-type(%d)/Opaque-type(%d) into apiserv(%p), total#(%d)", lsa_type, opaque_type, (void *)apiserv, listcount(apiserv->opaque_types)); @@ -920,8 +919,7 @@ int ospf_apiserver_unregister_opaque_type(struct ospf_apiserver *apiserv, if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "API: Del LSA-type(%d)/Opaque-type(%d)" - " from apiserv(%p), total#(%d)", + "API: Del LSA-type(%d)/Opaque-type(%d) from apiserv(%p), total#(%d)", lsa_type, opaque_type, (void *)apiserv, listcount(apiserv->opaque_types)); @@ -2325,14 +2323,12 @@ void ospf_apiserver_clients_notify_ism_change(struct ospf_interface *oi) void ospf_apiserver_clients_notify_nsm_change(struct ospf_neighbor *nbr) { struct msg *msg; - struct in_addr ifaddr = {.s_addr = 0L}; + struct in_addr ifaddr; struct in_addr nbraddr; assert(nbr); - if (nbr->oi) { - ifaddr = nbr->oi->address->u.prefix4; - } + ifaddr = nbr->oi->address->u.prefix4; nbraddr = nbr->address.u.prefix4; diff --git a/ospfd/ospf_asbr.c b/ospfd/ospf_asbr.c index ea919017d3..83519cf32d 100644 --- a/ospfd/ospf_asbr.c +++ b/ospfd/ospf_asbr.c @@ -141,7 +141,6 @@ ospf_external_info_add(struct ospf *ospf, uint8_t type, unsigned short instance, ospf->vrf_id, inet_ntoa(p.prefix), p.prefixlen, inetbuf); XFREE(MTYPE_OSPF_EXTERNAL_INFO, rn->info); - rn->info = NULL; } /* Create new External info instance. */ @@ -150,6 +149,7 @@ ospf_external_info_add(struct ospf *ospf, uint8_t type, unsigned short instance, new->ifindex = ifindex; new->nexthop = nexthop; new->tag = tag; + new->orig_tag = tag; /* we don't unlock rn from the get() because we're attaching the info */ if (rn) @@ -213,45 +213,58 @@ struct ospf_lsa *ospf_external_info_find_lsa(struct ospf *ospf, struct as_external_lsa *al; struct in_addr mask, id; - lsa = ospf_lsdb_lookup_by_id(ospf->lsdb, OSPF_AS_EXTERNAL_LSA, - p->prefix, ospf->router_id); - - if (!lsa) - return NULL; - - al = (struct as_external_lsa *)lsa->data; + /* Fisrt search the lsdb with address specifc LSID + * where all the host bits are set, if there a matched + * LSA, return. + * Ex: For route 10.0.0.0/16, LSID is 10.0.255.255 + * If no lsa with above LSID, use received address as + * LSID and check if any LSA in LSDB. + * If LSA found, check if the mask is same b/w the matched + * LSA and received prefix, if same then it is the LSA for + * this prefix. + * Ex: For route 10.0.0.0/16, LSID is 10.0.0.0 + */ masklen2ip(p->prefixlen, &mask); + id.s_addr = p->prefix.s_addr | (~mask.s_addr); + lsa = ospf_lsdb_lookup_by_id(ospf->lsdb, OSPF_AS_EXTERNAL_LSA, id, + ospf->router_id); + if (lsa) + return lsa; - if (mask.s_addr != al->mask.s_addr) { - id.s_addr = p->prefix.s_addr | (~mask.s_addr); - lsa = ospf_lsdb_lookup_by_id(ospf->lsdb, OSPF_AS_EXTERNAL_LSA, - id, ospf->router_id); - if (!lsa) - return NULL; + lsa = ospf_lsdb_lookup_by_id(ospf->lsdb, OSPF_AS_EXTERNAL_LSA, + p->prefix, ospf->router_id); + + if (lsa) { + al = (struct as_external_lsa *)lsa->data; + if (mask.s_addr == al->mask.s_addr) + return lsa; } - return lsa; + return NULL; } /* Update ASBR status. */ void ospf_asbr_status_update(struct ospf *ospf, uint8_t status) { - zlog_info("ASBR[Status:%d]: Update", status); + zlog_info("ASBR[%s:Status:%d]: Update", + ospf_get_name(ospf), status); /* ASBR on. */ if (status) { /* Already ASBR. */ if (IS_OSPF_ASBR(ospf)) { - zlog_info("ASBR[Status:%d]: Already ASBR", status); + zlog_info("ASBR[%s:Status:%d]: Already ASBR", + ospf_get_name(ospf), status); return; } SET_FLAG(ospf->flags, OSPF_FLAG_ASBR); } else { /* Already non ASBR. */ if (!IS_OSPF_ASBR(ospf)) { - zlog_info("ASBR[Status:%d]: Already non ASBR", status); + zlog_info("ASBR[%s:Status:%d]: Already non ASBR", + ospf_get_name(ospf), status); return; } UNSET_FLAG(ospf->flags, OSPF_FLAG_ASBR); @@ -262,6 +275,31 @@ void ospf_asbr_status_update(struct ospf *ospf, uint8_t status) ospf_router_lsa_update(ospf); } +/* If there's redistribution configured, we need to refresh external + * LSAs in order to install Type-7 and flood to all NSSA Areas + */ +void ospf_asbr_nssa_redist_task(struct ospf *ospf) +{ + int type; + + for (type = 0; type < ZEBRA_ROUTE_MAX; type++) { + struct list *red_list; + struct listnode *node; + struct ospf_redist *red; + + red_list = ospf->redist[type]; + if (!red_list) + continue; + + for (ALL_LIST_ELEMENTS_RO(red_list, node, red)) + ospf_external_lsa_refresh_type(ospf, type, + red->instance, + LSA_REFRESH_IF_CHANGED); + } + + ospf_external_lsa_refresh_default(ospf); +} + void ospf_redistribute_withdraw(struct ospf *ospf, uint8_t type, unsigned short instance) { diff --git a/ospfd/ospf_asbr.h b/ospfd/ospf_asbr.h index ac7bd68b5f..ede6c47906 100644 --- a/ospfd/ospf_asbr.h +++ b/ospfd/ospf_asbr.h @@ -46,6 +46,9 @@ struct external_info { /* Additional Route tag. */ route_tag_t tag; + /* Actual tag received from zebra*/ + route_tag_t orig_tag; + struct route_map_set_values route_map_set; #define ROUTEMAP_METRIC(E) (E)->route_map_set.metric #define ROUTEMAP_METRIC_TYPE(E) (E)->route_map_set.metric_type @@ -69,6 +72,7 @@ extern struct external_info *ospf_external_info_lookup(struct ospf *, uint8_t, unsigned short, struct prefix_ipv4 *); extern void ospf_asbr_status_update(struct ospf *, uint8_t); +extern void ospf_asbr_nssa_redist_task(struct ospf *ospf); extern void ospf_redistribute_withdraw(struct ospf *, uint8_t, unsigned short); extern void ospf_asbr_check(void); diff --git a/ospfd/ospf_ase.c b/ospfd/ospf_ase.c index 9492de544f..d2a30477b0 100644 --- a/ospfd/ospf_ase.c +++ b/ospfd/ospf_ase.c @@ -190,24 +190,21 @@ ospf_ase_calculate_asbr_route (struct ospf *ospf, zlog_debug ("ospf_ase_calculate(): Originating router is not an ASBR"); return NULL; } - + if (al->e[0].fwd_addr.s_addr != 0) { if (IS_DEBUG_OSPF (lsa, LSA)) - zlog_debug ("ospf_ase_calculate(): " - "Forwarding address is not 0.0.0.0."); + zlog_debug ("ospf_ase_calculate(): Forwarding address is not 0.0.0.0."); if (! ospf_ase_forward_address_check (ospf, al->e[0].fwd_addr)) { if (IS_DEBUG_OSPF (lsa, LSA)) - zlog_debug ("ospf_ase_calculate(): " - "Forwarding address is one of our addresses, Ignore."); + zlog_debug ("ospf_ase_calculate(): Forwarding address is one of our addresses, Ignore."); return NULL; } if (IS_DEBUG_OSPF (lsa, LSA)) - zlog_debug ("ospf_ase_calculate(): " - "Looking up in the Network Routing Table."); + zlog_debug ("ospf_ase_calculate(): Looking up in the Network Routing Table."); /* Looking up the path to the fwd_addr from Network route. */ asbr.family = AF_INET; @@ -215,12 +212,11 @@ ospf_ase_calculate_asbr_route (struct ospf *ospf, asbr.prefixlen = IPV4_MAX_BITLEN; rn = route_node_match (rt_network, (struct prefix *) &asbr); - + if (rn == NULL) { if (IS_DEBUG_OSPF (lsa, LSA)) - zlog_debug ("ospf_ase_calculate(): " - "Couldn't find a route to the forwarding address."); + zlog_debug ("ospf_ase_calculate(): Couldn't find a route to the forwarding address."); return NULL; } @@ -229,8 +225,7 @@ ospf_ase_calculate_asbr_route (struct ospf *ospf, if ((asbr_route = rn->info) == NULL) { if (IS_DEBUG_OSPF (lsa, LSA)) - zlog_debug ("ospf_ase_calculate(): " - "Somehow OSPF route to ASBR is lost"); + zlog_debug ("ospf_ase_calculate(): Somehow OSPF route to ASBR is lost"); return NULL; } } @@ -306,7 +301,7 @@ int ospf_ase_calculate_route(struct ospf *ospf, struct ospf_lsa *lsa) } if (IS_DEBUG_OSPF(lsa, LSA)) { - snprintf(buf1, INET_ADDRSTRLEN, "%s", + snprintf(buf1, sizeof(buf1), "%s", inet_ntoa(al->header.adv_router)); zlog_debug( "Route[External]: Calculate AS-external-LSA to %s/%d adv_router %s", @@ -370,7 +365,7 @@ int ospf_ase_calculate_route(struct ospf *ospf, struct ospf_lsa *lsa) external-LSA. This indicates the IP address to which packets for the destination should be forwarded. */ - if (al->e[0].fwd_addr.s_addr == 0) { + if (al->e[0].fwd_addr.s_addr == INADDR_ANY) { /* If the forwarding address is set to 0.0.0.0, packets should be sent to the ASBR itself. Among the multiple routing table entries for the ASBR, select the preferred entry as follows. @@ -393,8 +388,7 @@ int ospf_ase_calculate_route(struct ospf *ospf, struct ospf_lsa *lsa) if (!ospf_ase_forward_address_check(ospf, al->e[0].fwd_addr)) { if (IS_DEBUG_OSPF(lsa, LSA)) zlog_debug( - "Route[External]: Forwarding address is our router " - "address"); + "Route[External]: Forwarding address is our router address"); return 0; } @@ -407,8 +401,7 @@ int ospf_ase_calculate_route(struct ospf *ospf, struct ospf_lsa *lsa) if (rn == NULL || (asbr_route = rn->info) == NULL) { if (IS_DEBUG_OSPF(lsa, LSA)) zlog_debug( - "Route[External]: Can't find route to forwarding " - "address"); + "Route[External]: Can't find route to forwarding address"); if (rn) route_unlock_node(rn); return 0; @@ -470,7 +463,7 @@ int ospf_ase_calculate_route(struct ospf *ospf, struct ospf_lsa *lsa) ospf_route_add(ospf->new_external_route, &p, new, asbr_route); - if (al->e[0].fwd_addr.s_addr) + if (al->e[0].fwd_addr.s_addr != INADDR_ANY) ospf_ase_complete_direct_routes(new, al->e[0].fwd_addr); return 0; } else { @@ -512,7 +505,7 @@ int ospf_ase_calculate_route(struct ospf *ospf, struct ospf_lsa *lsa) zlog_debug( "Route[External]: New route is better"); ospf_route_subst(rn, new, asbr_route); - if (al->e[0].fwd_addr.s_addr) + if (al->e[0].fwd_addr.s_addr != INADDR_ANY) ospf_ase_complete_direct_routes( new, al->e[0].fwd_addr); or = new; @@ -530,7 +523,7 @@ int ospf_ase_calculate_route(struct ospf *ospf, struct ospf_lsa *lsa) if (IS_DEBUG_OSPF(lsa, LSA)) zlog_debug("Route[External]: Routes are equal"); ospf_route_copy_nexthops(or, asbr_route->paths); - if (al->e[0].fwd_addr.s_addr) + if (al->e[0].fwd_addr.s_addr != INADDR_ANY) ospf_ase_complete_direct_routes( or, al->e[0].fwd_addr); } @@ -691,7 +684,7 @@ static int ospf_ase_calculate_timer(struct thread *t) if (IS_DEBUG_OSPF_EVENT) zlog_info( - "SPF Processing Time(usecs): External Routes: %lld\n", + "SPF Processing Time(usecs): External Routes: %lld", (stop_time.tv_sec - start_time.tv_sec) * 1000000LL + (stop_time.tv_usec diff --git a/ospfd/ospf_bfd.c b/ospfd/ospf_bfd.c index 594735a08f..cdc1d14feb 100644 --- a/ospfd/ospf_bfd.c +++ b/ospfd/ospf_bfd.c @@ -65,6 +65,7 @@ static void ospf_bfd_reg_dereg_nbr(struct ospf_neighbor *nbr, int command) struct interface *ifp = oi->ifp; struct ospf_if_params *params; struct bfd_info *bfd_info; + int cbit; /* Check if BFD is enabled */ params = IF_DEF_PARAMS(ifp); @@ -80,8 +81,10 @@ static void ospf_bfd_reg_dereg_nbr(struct ospf_neighbor *nbr, int command) inet_ntoa(nbr->src), ospf_vrf_id_to_name(oi->ospf->vrf_id)); + cbit = CHECK_FLAG(bfd_info->flags, BFD_FLAG_BFD_CBIT_ON); + bfd_peer_sendmsg(zclient, bfd_info, AF_INET, &nbr->src, NULL, ifp->name, - 0, 0, command, 0, oi->ospf->vrf_id); + 0, 0, cbit, command, 0, oi->ospf->vrf_id); } /* @@ -141,8 +144,7 @@ static int ospf_bfd_reg_dereg_all_nbr(struct interface *ifp, int command) * ospf_bfd_nbr_replay - Replay all the neighbors that have BFD enabled * to zebra */ -static int ospf_bfd_nbr_replay(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ospf_bfd_nbr_replay(ZAPI_CALLBACK_ARGS) { struct listnode *inode, *node, *onode; struct ospf *ospf; @@ -157,7 +159,7 @@ static int ospf_bfd_nbr_replay(int command, struct zclient *zclient, } /* Send the client registration */ - bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER); + bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, vrf_id); /* Replay the neighbor, if BFD is enabled in OSPF */ for (ALL_LIST_ELEMENTS(om->ospf, node, onode, ospf)) { @@ -195,21 +197,22 @@ static int ospf_bfd_nbr_replay(int command, struct zclient *zclient, * connectivity if the BFD status changed to * down. */ -static int ospf_bfd_interface_dest_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ospf_bfd_interface_dest_update(ZAPI_CALLBACK_ARGS) { struct interface *ifp; struct ospf_interface *oi; struct ospf_if_params *params; - struct ospf_neighbor *nbr; + struct ospf_neighbor *nbr = NULL; struct route_node *node; - struct prefix p; + struct route_node *n_node; + struct prefix p, src_p; int status; int old_status; struct bfd_info *bfd_info; struct timeval tv; - ifp = bfd_get_peer_info(zclient->ibuf, &p, NULL, &status, vrf_id); + ifp = bfd_get_peer_info(zclient->ibuf, &p, &src_p, &status, NULL, + vrf_id); if ((ifp == NULL) || (p.family != AF_INET)) return 0; @@ -229,7 +232,28 @@ static int ospf_bfd_interface_dest_update(int command, struct zclient *zclient, if ((oi = node->info) == NULL) continue; - nbr = ospf_nbr_lookup_by_addr(oi->nbrs, &p.u.prefix4); + /* walk the neighbor list for point-to-point network */ + if (oi->type == OSPF_IFTYPE_POINTOPOINT) { + for (n_node = route_top(oi->nbrs); n_node; + n_node = route_next(n_node)) { + nbr = n_node->info; + if (nbr) { + /* skip myself */ + if (nbr == oi->nbr_self) { + nbr = NULL; + continue; + } + + /* Found the matching neighbor */ + if (nbr->src.s_addr == + p.u.prefix4.s_addr) + break; + } + } + } else { + nbr = ospf_nbr_lookup_by_addr(oi->nbrs, &p.u.prefix4); + } + if (!nbr || !nbr->bfd_info) continue; @@ -238,7 +262,7 @@ static int ospf_bfd_interface_dest_update(int command, struct zclient *zclient, continue; old_status = bfd_info->status; - bfd_info->status = status; + BFD_SET_CLIENT_STATUS(bfd_info->status, status); monotime(&tv); bfd_info->last_update = tv.tv_sec; @@ -251,6 +275,13 @@ static int ospf_bfd_interface_dest_update(int command, struct zclient *zclient, OSPF_NSM_EVENT_SCHEDULE(nbr, NSM_InactivityTimer); } + if ((status == BFD_STATUS_UP) + && (old_status == BFD_STATUS_DOWN)) { + if (IS_DEBUG_OSPF(nsm, NSM_EVENTS)) + zlog_debug("NSM[%s:%s]: BFD Up", + IF_NAME(nbr->oi), + inet_ntoa(nbr->address.u.prefix4)); + } } return 0; @@ -350,7 +381,7 @@ static void ospf_bfd_if_param_set(struct interface *ifp, uint32_t min_rx, params = IF_DEF_PARAMS(ifp); bfd_set_param((struct bfd_info **)&(params->bfd_info), min_rx, min_tx, - detect_mult, defaults, &command); + detect_mult, NULL, defaults, &command); if (command) ospf_bfd_reg_dereg_all_nbr(ifp, command); } diff --git a/ospfd/ospf_dump.c b/ospfd/ospf_dump.c index f74d9733ee..dcc479def6 100644 --- a/ospfd/ospf_dump.c +++ b/ospfd/ospf_dump.c @@ -53,10 +53,11 @@ unsigned long conf_debug_ospf_nssa = 0; unsigned long conf_debug_ospf_te = 0; unsigned long conf_debug_ospf_ext = 0; unsigned long conf_debug_ospf_sr = 0; +unsigned long conf_debug_ospf_defaultinfo = 0; /* Enable debug option variables -- valid only session. */ unsigned long term_debug_ospf_packet[5] = {0, 0, 0, 0, 0}; -unsigned long term_debug_ospf_event = 0; +unsigned long term_debug_ospf_event; unsigned long term_debug_ospf_ism = 0; unsigned long term_debug_ospf_nsm = 0; unsigned long term_debug_ospf_lsa = 0; @@ -65,6 +66,7 @@ unsigned long term_debug_ospf_nssa = 0; unsigned long term_debug_ospf_te = 0; unsigned long term_debug_ospf_ext = 0; unsigned long term_debug_ospf_sr = 0; +unsigned long term_debug_ospf_defaultinfo; const char *ospf_redist_string(unsigned int route_type) { @@ -82,9 +84,8 @@ const char *ospf_area_name_string(struct ospf_area *area) return "-"; area_id = ntohl(area->area_id.s_addr); - snprintf(buf, OSPF_AREA_STRING_MAXLEN, "%d.%d.%d.%d", - (area_id >> 24) & 0xff, (area_id >> 16) & 0xff, - (area_id >> 8) & 0xff, area_id & 0xff); + snprintf(buf, sizeof(buf), "%d.%d.%d.%d", (area_id >> 24) & 0xff, + (area_id >> 16) & 0xff, (area_id >> 8) & 0xff, area_id & 0xff); return buf; } @@ -100,11 +101,11 @@ const char *ospf_area_desc_string(struct ospf_area *area) type = area->external_routing; switch (type) { case OSPF_AREA_NSSA: - snprintf(buf, OSPF_AREA_DESC_STRING_MAXLEN, "%s [NSSA]", + snprintf(buf, sizeof(buf), "%s [NSSA]", ospf_area_name_string(area)); break; case OSPF_AREA_STUB: - snprintf(buf, OSPF_AREA_DESC_STRING_MAXLEN, "%s [Stub]", + snprintf(buf, sizeof(buf), "%s [Stub]", ospf_area_name_string(area)); break; default: @@ -127,7 +128,7 @@ const char *ospf_if_name_string(struct ospf_interface *oi) return oi->ifp->name; ifaddr = ntohl(oi->address->u.prefix4.s_addr); - snprintf(buf, OSPF_IF_STRING_MAXLEN, "%s:%d.%d.%d.%d", oi->ifp->name, + snprintf(buf, sizeof(buf), "%s:%d.%d.%d.%d", oi->ifp->name, (ifaddr >> 24) & 0xff, (ifaddr >> 16) & 0xff, (ifaddr >> 8) & 0xff, ifaddr & 0xff); return buf; @@ -158,12 +159,12 @@ const char *ospf_timeval_dump(struct timeval *t, char *buf, size_t size) #define HOUR_IN_SECONDS (60*MINUTE_IN_SECONDS) #define DAY_IN_SECONDS (24*HOUR_IN_SECONDS) #define WEEK_IN_SECONDS (7*DAY_IN_SECONDS) - unsigned long w, d, h, m, s, ms, us; + unsigned long w, d, h, m, ms, us; if (!t) return "inactive"; - w = d = h = m = s = ms = us = 0; + w = d = h = m = ms = 0; memset(buf, 0, size); us = t->tv_usec; @@ -386,7 +387,7 @@ static void ospf_packet_db_desc_dump(struct stream *s, uint16_t length) zlog_debug(" Options %d (%s)", dd->options, ospf_options_dump(dd->options)); zlog_debug(" Flags %d (%s)", dd->flags, - ospf_dd_flags_dump(dd->flags, dd_flags, sizeof dd_flags)); + ospf_dd_flags_dump(dd->flags, dd_flags, sizeof(dd_flags))); zlog_debug(" Sequence Number 0x%08lx", (unsigned long)ntohl(dd->dd_seqnum)); @@ -501,23 +502,6 @@ static void ospf_packet_ls_ack_dump(struct stream *s, uint16_t length) stream_set_getp(s, sp); } -/* Expects header to be in host order */ -void ospf_ip_header_dump(struct ip *iph) -{ - /* IP Header dump. */ - zlog_debug("ip_v %d", iph->ip_v); - zlog_debug("ip_hl %d", iph->ip_hl); - zlog_debug("ip_tos %d", iph->ip_tos); - zlog_debug("ip_len %d", iph->ip_len); - zlog_debug("ip_id %u", (uint32_t)iph->ip_id); - zlog_debug("ip_off %u", (uint32_t)iph->ip_off); - zlog_debug("ip_ttl %d", iph->ip_ttl); - zlog_debug("ip_p %d", iph->ip_p); - zlog_debug("ip_sum 0x%x", (uint32_t)iph->ip_sum); - zlog_debug("ip_src %s", inet_ntoa(iph->ip_src)); - zlog_debug("ip_dst %s", inet_ntoa(iph->ip_dst)); -} - static void ospf_header_dump(struct ospf_header *ospfh) { char buf[9]; @@ -1465,6 +1449,33 @@ DEFUN (no_debug_ospf_sr, return CMD_SUCCESS; } +DEFUN (debug_ospf_default_info, + debug_ospf_default_info_cmd, + "debug ospf default-information", + DEBUG_STR + OSPF_STR + "OSPF default information\n") +{ + if (vty->node == CONFIG_NODE) + CONF_DEBUG_ON(defaultinfo, DEFAULTINFO); + TERM_DEBUG_ON(defaultinfo, DEFAULTINFO); + return CMD_SUCCESS; +} + +DEFUN (no_debug_ospf_default_info, + no_debug_ospf_default_info_cmd, + "no debug ospf default-information", + NO_STR + DEBUG_STR + OSPF_STR + "OSPF default information\n") +{ + if (vty->node == CONFIG_NODE) + CONF_DEBUG_OFF(defaultinfo, DEFAULTINFO); + TERM_DEBUG_OFF(defaultinfo, DEFAULTINFO); + return CMD_SUCCESS; +} + DEFUN (no_debug_ospf, no_debug_ospf_cmd, "no debug ospf", @@ -1493,6 +1504,7 @@ DEFUN (no_debug_ospf, DEBUG_OFF(zebra, ZEBRA); DEBUG_OFF(zebra, ZEBRA_INTERFACE); DEBUG_OFF(zebra, ZEBRA_REDISTRIBUTE); + DEBUG_OFF(defaultinfo, DEFAULTINFO); for (i = 0; i < 5; i++) DEBUG_PACKET_OFF(i, flag); @@ -1519,6 +1531,7 @@ DEFUN (no_debug_ospf, TERM_DEBUG_OFF(zebra, ZEBRA); TERM_DEBUG_OFF(zebra, ZEBRA_INTERFACE); TERM_DEBUG_OFF(zebra, ZEBRA_REDISTRIBUTE); + TERM_DEBUG_OFF(defaultinfo, DEFAULTINFO); return CMD_SUCCESS; } @@ -1613,6 +1626,9 @@ static int show_debugging_ospf_common(struct vty *vty, struct ospf *ospf) " OSPF Zebra redistribute debugging is on\n"); } + if (IS_DEBUG_OSPF(defaultinfo, DEFAULTINFO) == OSPF_DEBUG_DEFAULTINFO) + vty_out(vty, "OSPF default information is on\n"); + /* Show debug status for NSSA. */ if (IS_DEBUG_OSPF(nssa, NSSA) == OSPF_DEBUG_NSSA) vty_out(vty, " OSPF NSSA debugging is on\n"); @@ -1657,9 +1673,13 @@ DEFUN_NOSH (show_debugging_ospf_instance, return show_debugging_ospf_common(vty, ospf); } +static int config_write_debug(struct vty *vty); /* Debug node. */ static struct cmd_node debug_node = { - DEBUG_NODE, "", 1 /* VTYSH */ + .name = "debug", + .node = DEBUG_NODE, + .prompt = "", + .config_write = config_write_debug, }; static int config_write_debug(struct vty *vty) @@ -1682,7 +1702,7 @@ static int config_write_debug(struct vty *vty) return CMD_SUCCESS; if (ospf->instance) - sprintf(str, " %u", ospf->instance); + snprintf(str, sizeof(str), " %u", ospf->instance); /* debug ospf ism (status|events|timers). */ if (IS_CONF_DEBUG_OSPF(ism, ISM) == OSPF_DEBUG_ISM) @@ -1800,7 +1820,7 @@ static int config_write_debug(struct vty *vty) /* Initialize debug commands. */ void ospf_debug_init(void) { - install_node(&debug_node, config_write_debug); + install_node(&debug_node); install_element(ENABLE_NODE, &show_debugging_ospf_cmd); install_element(ENABLE_NODE, &debug_ospf_ism_cmd); @@ -1811,6 +1831,7 @@ void ospf_debug_init(void) install_element(ENABLE_NODE, &debug_ospf_nssa_cmd); install_element(ENABLE_NODE, &debug_ospf_te_cmd); install_element(ENABLE_NODE, &debug_ospf_sr_cmd); + install_element(ENABLE_NODE, &debug_ospf_default_info_cmd); install_element(ENABLE_NODE, &no_debug_ospf_ism_cmd); install_element(ENABLE_NODE, &no_debug_ospf_nsm_cmd); install_element(ENABLE_NODE, &no_debug_ospf_lsa_cmd); @@ -1819,6 +1840,7 @@ void ospf_debug_init(void) install_element(ENABLE_NODE, &no_debug_ospf_nssa_cmd); install_element(ENABLE_NODE, &no_debug_ospf_te_cmd); install_element(ENABLE_NODE, &no_debug_ospf_sr_cmd); + install_element(ENABLE_NODE, &no_debug_ospf_default_info_cmd); install_element(ENABLE_NODE, &show_debugging_ospf_instance_cmd); install_element(ENABLE_NODE, &debug_ospf_packet_cmd); @@ -1848,6 +1870,7 @@ void ospf_debug_init(void) install_element(CONFIG_NODE, &debug_ospf_nssa_cmd); install_element(CONFIG_NODE, &debug_ospf_te_cmd); install_element(CONFIG_NODE, &debug_ospf_sr_cmd); + install_element(CONFIG_NODE, &debug_ospf_default_info_cmd); install_element(CONFIG_NODE, &no_debug_ospf_nsm_cmd); install_element(CONFIG_NODE, &no_debug_ospf_lsa_cmd); install_element(CONFIG_NODE, &no_debug_ospf_zebra_cmd); @@ -1855,6 +1878,7 @@ void ospf_debug_init(void) install_element(CONFIG_NODE, &no_debug_ospf_nssa_cmd); install_element(CONFIG_NODE, &no_debug_ospf_te_cmd); install_element(CONFIG_NODE, &no_debug_ospf_sr_cmd); + install_element(CONFIG_NODE, &no_debug_ospf_default_info_cmd); install_element(CONFIG_NODE, &debug_ospf_instance_nsm_cmd); install_element(CONFIG_NODE, &debug_ospf_instance_lsa_cmd); diff --git a/ospfd/ospf_dump.h b/ospfd/ospf_dump.h index 397f666f69..8c01977ff8 100644 --- a/ospfd/ospf_dump.h +++ b/ospfd/ospf_dump.h @@ -59,6 +59,7 @@ #define OSPF_DEBUG_TE 0x04 #define OSPF_DEBUG_EXT 0x08 #define OSPF_DEBUG_SR 0x10 +#define OSPF_DEBUG_DEFAULTINFO 0x20 /* Macro for setting debug option. */ #define CONF_DEBUG_PACKET_ON(a, b) conf_debug_ospf_packet[a] |= (b) @@ -104,15 +105,12 @@ #define IS_DEBUG_OSPF_SR IS_DEBUG_OSPF(sr, SR) +#define IS_DEBUG_OSPF_DEFAULT_INFO IS_DEBUG_OSPF(defaultinfo, DEFAULTINFO) + #define IS_CONF_DEBUG_OSPF_PACKET(a, b) \ (conf_debug_ospf_packet[a] & OSPF_DEBUG_##b) #define IS_CONF_DEBUG_OSPF(a, b) (conf_debug_ospf_##a & OSPF_DEBUG_##b) -#ifdef ORIGINAL_CODING -#else /* ORIGINAL_CODING */ -struct stream; -#endif /* ORIGINAL_CODING */ - #define AREA_NAME(A) ospf_area_name_string ((A)) #define IF_NAME(I) ospf_if_name_string ((I)) @@ -127,6 +125,7 @@ extern unsigned long term_debug_ospf_nssa; extern unsigned long term_debug_ospf_te; extern unsigned long term_debug_ospf_ext; extern unsigned long term_debug_ospf_sr; +extern unsigned long term_debug_ospf_defaultinfo; /* Message Strings. */ extern char *ospf_lsa_type_str[]; @@ -138,7 +137,6 @@ extern const char *ospf_if_name_string(struct ospf_interface *); extern void ospf_nbr_state_message(struct ospf_neighbor *, char *, size_t); extern const char *ospf_timer_dump(struct thread *, char *, size_t); extern const char *ospf_timeval_dump(struct timeval *, char *, size_t); -extern void ospf_ip_header_dump(struct ip *); extern void ospf_packet_dump(struct stream *); extern void ospf_debug_init(void); diff --git a/ospfd/ospf_dump_api.c b/ospfd/ospf_dump_api.c index 1196339c34..e24936a473 100644 --- a/ospfd/ospf_dump_api.c +++ b/ospfd/ospf_dump_api.c @@ -105,7 +105,7 @@ char *ospf_options_dump(uint8_t options) { static char buf[OSPF_OPTION_STR_MAXLEN]; - snprintf(buf, OSPF_OPTION_STR_MAXLEN, "*|%s|%s|%s|%s|%s|%s|%s", + snprintf(buf, sizeof(buf), "*|%s|%s|%s|%s|%s|%s|%s", (options & OSPF_OPTION_O) ? "O" : "-", (options & OSPF_OPTION_DC) ? "DC" : "-", (options & OSPF_OPTION_EA) ? "EA" : "-", diff --git a/ospfd/ospf_errors.c b/ospfd/ospf_errors.c index dd02160195..2de77a43f6 100644 --- a/ospfd/ospf_errors.c +++ b/ospfd/ospf_errors.c @@ -39,7 +39,7 @@ static struct log_ref ferr_ospf_warn[] = { }, { .code = EC_OSPF_PACKET, - .title = "OSPF has detected packet information missmatch", + .title = "OSPF has detected packet information mismatch", .description = "OSPF has detected that packet information received is incorrect", .suggestion = "Ensure interface configuration is correct, gather log files from here and the peer and open an Issue", }, @@ -157,6 +157,12 @@ static struct log_ref ferr_ospf_err[] = { .description = "OSPF Segment Routing invalid lsa id", .suggestion = "Restart OSPF instance, If the problem persists, report the problem for troubleshooting" }, + { + .code = EC_OSPF_SR_SID_OVERFLOW, + .title = "OSPF SR Segment-ID overflow", + .description = "OSPF Segment Routing ID index or label exceed Global or Local Block Range", + .suggestion = "Restart OSPF instance, If the problem persists, report the problem for troubleshooting" + }, { .code = EC_OSPF_INVALID_ALGORITHM, .title = "OSPF SR Invalid Algorithm", diff --git a/ospfd/ospf_errors.h b/ospfd/ospf_errors.h index 726f7d9c8b..df5bdaa491 100644 --- a/ospfd/ospf_errors.h +++ b/ospfd/ospf_errors.h @@ -31,6 +31,7 @@ enum ospf_log_refs { EC_OSPF_SR_INVALID_DB, EC_OSPF_SR_NODE_CREATE, EC_OSPF_SR_INVALID_LSA_ID, + EC_OSPF_SR_SID_OVERFLOW, EC_OSPF_INVALID_ALGORITHM, EC_OSPF_FSM_INVALID_STATE, EC_OSPF_SET_METRIC_PLUS, diff --git a/ospfd/ospf_ext.c b/ospfd/ospf_ext.c index df64fca883..90dc108c0e 100644 --- a/ospfd/ospf_ext.c +++ b/ospfd/ospf_ext.c @@ -80,7 +80,6 @@ static struct ospf_ext_lp OspfEXT; */ /* Extended Prefix Opaque LSA related callback functions */ -static void ospf_ext_pref_ism_change(struct ospf_interface *oi, int old_status); static void ospf_ext_pref_show_info(struct vty *vty, struct ospf_lsa *lsa); static int ospf_ext_pref_lsa_originate(void *arg); static struct ospf_lsa *ospf_ext_pref_lsa_refresh(struct ospf_lsa *lsa); @@ -89,7 +88,7 @@ static void ospf_ext_pref_lsa_schedule(struct ext_itf *exti, /* Extended Link Opaque LSA related callback functions */ static int ospf_ext_link_new_if(struct interface *ifp); static int ospf_ext_link_del_if(struct interface *ifp); -static void ospf_ext_link_ism_change(struct ospf_interface *oi, int old_status); +static void ospf_ext_ism_change(struct ospf_interface *oi, int old_status); static void ospf_ext_link_nsm_change(struct ospf_neighbor *nbr, int old_status); static void ospf_ext_link_show_info(struct vty *vty, struct ospf_lsa *lsa); static int ospf_ext_link_lsa_originate(void *arg); @@ -99,6 +98,7 @@ static void ospf_ext_link_lsa_schedule(struct ext_itf *exti, static void ospf_ext_lsa_schedule(struct ext_itf *exti, enum lsa_opcode op); static int ospf_ext_link_lsa_update(struct ospf_lsa *lsa); static int ospf_ext_pref_lsa_update(struct ospf_lsa *lsa); +static void ospf_ext_link_delete_adj_sid(struct ext_itf *exti); static void del_ext_info(void *val); /* @@ -125,7 +125,7 @@ int ospf_ext_init(void) OSPF_OPAQUE_AREA_LSA, OPAQUE_TYPE_EXTENDED_LINK_LSA, ospf_ext_link_new_if, /* new if */ ospf_ext_link_del_if, /* del if */ - ospf_ext_link_ism_change, /* ism change */ + ospf_ext_ism_change, /* ism change */ ospf_ext_link_nsm_change, /* nsm change */ NULL, /* Write router config. */ NULL, /* Write interface conf. */ @@ -148,7 +148,7 @@ int ospf_ext_init(void) OspfEXT.scope, OPAQUE_TYPE_EXTENDED_PREFIX_LSA, NULL, /* new if handle by link */ NULL, /* del if handle by link */ - ospf_ext_pref_ism_change, /* ism change */ + NULL, /* ism change */ NULL, /* nsm change */ ospf_sr_config_write_router, /* Write router config. */ NULL, /* Write interface conf. */ @@ -200,7 +200,15 @@ void ospf_ext_term(void) */ void ospf_ext_finish(void) { - // list_delete_all_node(OspfEXT.iflist); + + struct listnode *node; + struct ext_itf *exti; + + /* Flush Router Info LSA */ + for (ALL_LIST_ELEMENTS_RO(OspfEXT.iflist, node, exti)) + if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED)) + ospf_ext_lsa_schedule(exti, FLUSH_THIS_LSA); + OspfEXT.enabled = false; } @@ -427,6 +435,20 @@ static void set_lan_adj_sid(struct ext_itf *exti, bool backup, uint32_t value, exti->lan_sid[index].neighbor_id = neighbor_id; } +static void unset_adjacency_sid(struct ext_itf *exti) +{ + /* Reset Adjacency TLV */ + if (exti->type == ADJ_SID) { + TLV_TYPE(exti->adj_sid[0]) = 0; + TLV_TYPE(exti->adj_sid[1]) = 0; + } + /* or Lan-Adjacency TLV */ + if (exti->type == LAN_ADJ_SID) { + TLV_TYPE(exti->lan_sid[0]) = 0; + TLV_TYPE(exti->lan_sid[1]) = 0; + } +} + /* Experimental SubTLV from Cisco */ static void set_rmt_itf_addr(struct ext_itf *exti, struct in_addr rmtif) { @@ -436,6 +458,34 @@ static void set_rmt_itf_addr(struct ext_itf *exti, struct in_addr rmtif) exti->rmt_itf_addr.value = rmtif; } +/* Delete Extended LSA */ +static void ospf_extended_lsa_delete(struct ext_itf *exti) +{ + + /* Process only Active Extended Prefix/Link LSA */ + if (!CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE)) + return; + + osr_debug("EXT (%s): Disable %s%s%s-SID on interface %s", __func__, + exti->stype == LOCAL_SID ? "Prefix" : "", + exti->stype == ADJ_SID ? "Adjacency" : "", + exti->stype == LAN_ADJ_SID ? "LAN-Adjacency" : "", + exti->ifp->name); + + /* Flush LSA if already engaged */ + if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED)) { + ospf_ext_lsa_schedule(exti, FLUSH_THIS_LSA); + UNSET_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED); + } + + /* De-activate this Extended Prefix/Link and remove corresponding + * Segment-Routing Prefix-SID or (LAN)-ADJ-SID */ + if (exti->stype == ADJ_SID || exti->stype == LAN_ADJ_SID) + ospf_ext_link_delete_adj_sid(exti); + else + ospf_sr_ext_itf_delete(exti); +} + /* * Update Extended prefix SID index for Loopback interface type * @@ -458,12 +508,7 @@ uint32_t ospf_ext_schedule_prefix_index(struct interface *ifp, uint32_t index, return rc; if (p != NULL) { - if (IS_DEBUG_OSPF_SR) - zlog_debug( - "EXT (%s): Schedule new prefix %s/%u with " - "index %u on interface %s", - __func__, inet_ntoa(p->prefix), p->prefixlen, - index, ifp->name); + osr_debug("EXT (%s): Schedule new prefix %pFX with index %u on interface %s", __func__, p, index, ifp->name); /* Set first Extended Prefix then the Prefix SID information */ set_ext_prefix(exti, OSPF_PATH_INTRA_AREA, EXT_TLV_PREF_NFLG, @@ -471,26 +516,116 @@ uint32_t ospf_ext_schedule_prefix_index(struct interface *ifp, uint32_t index, set_prefix_sid(exti, SR_ALGORITHM_SPF, index, SID_INDEX, flags); /* Try to Schedule LSA */ - SET_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE); - if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED)) - ospf_ext_pref_lsa_schedule(exti, REFRESH_THIS_LSA); - else - ospf_ext_pref_lsa_schedule(exti, REORIGINATE_THIS_LSA); + if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE)) { + if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED)) + ospf_ext_pref_lsa_schedule(exti, + REFRESH_THIS_LSA); + else + ospf_ext_pref_lsa_schedule( + exti, REORIGINATE_THIS_LSA); + } } else { - if (IS_DEBUG_OSPF_SR) - zlog_debug("EXT (%s): Remove prefix for interface %s", - __func__, ifp->name); + osr_debug("EXT (%s): Remove prefix for interface %s", __func__, + ifp->name); - if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED)) { + if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED)) ospf_ext_pref_lsa_schedule(exti, FLUSH_THIS_LSA); - UNSET_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED); - UNSET_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE); - } } return SET_OPAQUE_LSID(exti->type, exti->instance); } +/** + * Update Adjacecny-SID for Extended Link LSA + * + * @param exti Extended Link information + */ +static void ospf_ext_link_update_adj_sid(struct ext_itf *exti) +{ + mpls_label_t label; + mpls_label_t bck_label; + + /* Process only (LAN)Adjacency-SID Type */ + if (exti->stype != ADJ_SID && exti->stype != LAN_ADJ_SID) + return; + + /* Request Primary & Backup Labels from Label Manager */ + bck_label = ospf_sr_local_block_request_label(); + label = ospf_sr_local_block_request_label(); + if (bck_label == MPLS_INVALID_LABEL || label == MPLS_INVALID_LABEL) { + if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED)) + ospf_ext_lsa_schedule(exti, FLUSH_THIS_LSA); + return; + } + + /* Set Adjacency-SID, backup first */ + if (exti->stype == ADJ_SID) { + set_adj_sid(exti, true, bck_label, SID_LABEL); + set_adj_sid(exti, false, label, SID_LABEL); + } else { + set_lan_adj_sid(exti, true, bck_label, SID_LABEL, + exti->lan_sid[0].neighbor_id); + set_lan_adj_sid(exti, false, label, SID_LABEL, + exti->lan_sid[1].neighbor_id); + } + + /* Finally, add corresponding SR Link in SRDB & MPLS LFIB */ + SET_FLAG(exti->flags, EXT_LPFLG_FIB_ENTRY_SET); + ospf_sr_ext_itf_add(exti); +} + +/** + * Delete Adjacecny-SID for Extended Link LSA + * + * @param exti Extended Link information + */ +static void ospf_ext_link_delete_adj_sid(struct ext_itf *exti) +{ + /* Process only (LAN)Adjacency-SID Type */ + if (exti->stype != ADJ_SID && exti->stype != LAN_ADJ_SID) + return; + + /* Release Primary & Backup Labels from Label Manager */ + if (exti->stype == ADJ_SID) { + ospf_sr_local_block_release_label(exti->adj_sid[0].value); + ospf_sr_local_block_release_label(exti->adj_sid[1].value); + } else { + ospf_sr_local_block_release_label(exti->lan_sid[0].value); + ospf_sr_local_block_release_label(exti->lan_sid[1].value); + } + /* And reset corresponding TLV */ + unset_adjacency_sid(exti); + + /* Finally, remove corresponding SR Link in SRDB & MPLS LFIB */ + UNSET_FLAG(exti->flags, EXT_LPFLG_FIB_ENTRY_SET); + ospf_sr_ext_itf_delete(exti); +} + +/** + * Update Extended Link LSA once Segment Routing Label Block has been changed. + */ +void ospf_ext_link_srlb_update(void) +{ + struct listnode *node; + struct ext_itf *exti; + + + osr_debug("EXT (%s): Update Extended Links with new SRLB", __func__); + + /* Update all Extended Link Adjaceny-SID */ + for (ALL_LIST_ELEMENTS_RO(OspfEXT.iflist, node, exti)) { + /* Skip Extended Prefix */ + if (exti->stype == PREF_SID || exti->stype == LOCAL_SID) + continue; + + /* Skip inactive Extended Link */ + if (!CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE)) + continue; + + ospf_ext_link_update_adj_sid(exti); + } +} + /* * Used by Segment Routing to activate/deactivate Extended Link/Prefix flooding * @@ -503,28 +638,39 @@ void ospf_ext_update_sr(bool enable) struct listnode *node; struct ext_itf *exti; - if (IS_DEBUG_OSPF_SR) - zlog_debug("EXT (%s): %s Extended LSAs for Segment Routing ", - __func__, enable ? "Enable" : "Disable"); + osr_debug("EXT (%s): %s Extended LSAs for Segment Routing ", __func__, + enable ? "Enable" : "Disable"); if (enable) { OspfEXT.enabled = true; + /* Refresh LSAs if already engaged or originate */ - for (ALL_LIST_ELEMENTS_RO(OspfEXT.iflist, node, exti)) + for (ALL_LIST_ELEMENTS_RO(OspfEXT.iflist, node, exti)) { + /* Skip Inactive Extended Link */ + if (!CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE)) + continue; + + /* Update Extended Link (LAN)Adj-SID if not set */ + if (!CHECK_FLAG(exti->flags, EXT_LPFLG_FIB_ENTRY_SET)) + ospf_ext_link_update_adj_sid(exti); + + /* Finally, flood the extended Link */ if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED)) ospf_ext_lsa_schedule(exti, REFRESH_THIS_LSA); else ospf_ext_lsa_schedule(exti, REORIGINATE_THIS_LSA); + } } else { - /* Start by Flushing engaged LSAs */ + /* Start by Removing Extended LSA */ for (ALL_LIST_ELEMENTS_RO(OspfEXT.iflist, node, exti)) - if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED)) - ospf_ext_lsa_schedule(exti, FLUSH_THIS_LSA); + ospf_extended_lsa_delete(exti); + /* And then disable Extended Link/Prefix */ OspfEXT.enabled = false; } } + /* * ----------------------------------------------------------------------- * Followings are callback functions against generic Opaque-LSAs handling @@ -562,10 +708,11 @@ static int ospf_ext_link_del_if(struct interface *ifp) exti = lookup_ext_by_ifp(ifp); if (exti != NULL) { - struct list *iflist = OspfEXT.iflist; + /* Flush LSA and remove Adjacency SID */ + ospf_extended_lsa_delete(exti); /* Dequeue listnode entry from the list. */ - listnode_delete(iflist, exti); + listnode_delete(OspfEXT.iflist, exti); XFREE(MTYPE_OSPF_EXT_PARAMS, exti); @@ -580,39 +727,10 @@ static int ospf_ext_link_del_if(struct interface *ifp) } /* - * Determine if an Interface belongs to an Extended Link Adjacency or LAN Adj. - * type and allocate new instance value accordingly - */ -static void ospf_ext_link_ism_change(struct ospf_interface *oi, int old_status) -{ - struct ext_itf *exti; - - /* Get interface information for Segment Routing */ - exti = lookup_ext_by_ifp(oi->ifp); - if (exti == NULL) - return; - - /* Determine if interface is related to Adjacency or LAN Adj. SID */ - if (oi->type != OSPF_IFTYPE_LOOPBACK) { - if (oi->state == ISM_DR) - exti->stype = LAN_ADJ_SID; - else - exti->stype = ADJ_SID; - - exti->instance = get_ext_link_instance_value(); - exti->type = OPAQUE_TYPE_EXTENDED_LINK_LSA; - - zlog_debug("EXT (%s): Set %s SID to interface %s ", __func__, - exti->stype == ADJ_SID ? "Adj." : "LAN Adj.", - oi->ifp->name); - } -} - -/* - * Determine if an Interface belongs to an Extended Prefix and - * allocate new instance value accordingly + * Determine if an Interface belongs to an Extended Link Adjacency or + * Extended Prefix SID type and allocate new instance value accordingly */ -static void ospf_ext_pref_ism_change(struct ospf_interface *oi, int old_status) +static void ospf_ext_ism_change(struct ospf_interface *oi, int old_status) { struct ext_itf *exti; @@ -625,18 +743,46 @@ static void ospf_ext_pref_ism_change(struct ospf_interface *oi, int old_status) return; } - /* Determine if interface is related to a Node SID */ + /* Reset Extended information if ospf interface goes Down */ + if (oi->state == ISM_Down) { + ospf_extended_lsa_delete(exti); + exti->area = NULL; + exti->flags = EXT_LPFLG_LSA_INACTIVE; + return; + } + + /* Determine if interface is related to a Prefix or an Adjacency SID */ if (oi->type == OSPF_IFTYPE_LOOPBACK) { exti->stype = PREF_SID; - exti->instance = get_ext_pref_instance_value(); exti->type = OPAQUE_TYPE_EXTENDED_PREFIX_LSA; + exti->flags = EXT_LPFLG_LSA_ACTIVE; + exti->instance = get_ext_pref_instance_value(); + exti->area = oi->area; - zlog_debug("EXT (%s): Set Node SID to interface %s ", __func__, - oi->ifp->name); + osr_debug("EXT (%s): Set Prefix SID to interface %s ", + __func__, oi->ifp->name); /* Complete SRDB if the interface belongs to a Prefix */ if (OspfEXT.enabled) - ospf_sr_update_prefix(oi->ifp, oi->address); + ospf_sr_update_local_prefix(oi->ifp, oi->address); + } else { + /* Determine if interface is related to Adj. or LAN Adj. SID */ + if (oi->state == ISM_DR) + exti->stype = LAN_ADJ_SID; + else + exti->stype = ADJ_SID; + + exti->type = OPAQUE_TYPE_EXTENDED_LINK_LSA; + exti->instance = get_ext_link_instance_value(); + exti->area = oi->area; + + /* + * Note: Adjacency SID information are completed when ospf + * adjacency become up see ospf_ext_link_nsm_change() + */ + osr_debug("EXT (%s): Set %sAdjacency SID for interface %s ", + __func__, exti->stype == ADJ_SID ? "" : "LAN-", + oi->ifp->name); } } @@ -648,10 +794,9 @@ static void ospf_ext_link_nsm_change(struct ospf_neighbor *nbr, int old_status) { struct ospf_interface *oi = nbr->oi; struct ext_itf *exti; - uint32_t label; - /* Process Neighbor only when its state is NSM Full */ - if (nbr->state != NSM_Full) + /* Process Link only when neighbor old or new state is NSM Full */ + if (nbr->state != NSM_Full && old_status != NSM_Full) return; /* Get interface information for Segment Routing */ @@ -663,6 +808,7 @@ static void ospf_ext_link_nsm_change(struct ospf_neighbor *nbr, int old_status) return; } + /* Check that we have a valid area and ospf context */ if (oi->area == NULL || oi->area->ospf == NULL) { flog_warn(EC_OSPF_EXT_LSA_UNEXPECTED, "EXT (%s): Cannot refer to OSPF from OI(%s)", @@ -670,9 +816,17 @@ static void ospf_ext_link_nsm_change(struct ospf_neighbor *nbr, int old_status) return; } + /* Remove Extended Link if Neighbor State goes Down or Deleted */ + if (nbr->state == NSM_Down || nbr->state == NSM_Deleted) { + ospf_ext_link_delete_adj_sid(exti); + if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED)) + ospf_ext_link_lsa_schedule(exti, FLUSH_THIS_LSA); + exti->flags = EXT_LPFLG_LSA_INACTIVE; + return; + } + /* Keep Area information in combination with SR info. */ exti->area = oi->area; - OspfEXT.area = oi->area; /* Process only Adjacency/LAN SID */ if (exti->stype == PREF_SID) @@ -687,11 +841,6 @@ static void ospf_ext_link_nsm_change(struct ospf_neighbor *nbr, int old_status) set_ext_link(exti, OSPF_IFTYPE_POINTOPOINT, nbr->router_id, oi->address->u.prefix4); - /* Set Extended Link Adjacency SubTLVs, backup first */ - label = get_ext_link_label_value(); - set_adj_sid(exti, true, label, SID_LABEL); - label = get_ext_link_label_value(); - set_adj_sid(exti, false, label, SID_LABEL); /* And Remote Interface address */ set_rmt_itf_addr(exti, nbr->address.u.prefix4); @@ -705,11 +854,9 @@ static void ospf_ext_link_nsm_change(struct ospf_neighbor *nbr, int old_status) set_ext_link(exti, OSPF_IFTYPE_BROADCAST, DR(oi), oi->address->u.prefix4); - /* Set Extended Link Adjacency SubTLVs, backup first */ - label = get_ext_link_label_value(); - set_lan_adj_sid(exti, true, label, SID_LABEL, nbr->router_id); - label = get_ext_link_label_value(); - set_lan_adj_sid(exti, false, label, SID_LABEL, nbr->router_id); + /* Set Neighbor ID */ + exti->lan_sid[0].neighbor_id = nbr->router_id; + exti->lan_sid[1].neighbor_id = nbr->router_id; break; @@ -722,32 +869,28 @@ static void ospf_ext_link_nsm_change(struct ospf_neighbor *nbr, int old_status) set_ext_link(exti, OSPF_IFTYPE_BROADCAST, DR(oi), oi->address->u.prefix4); - /* Set Extended Link Adjacency SubTLVs, backup first */ - label = get_ext_link_label_value(); - set_adj_sid(exti, true, label, SID_LABEL); - label = get_ext_link_label_value(); - set_adj_sid(exti, false, label, SID_LABEL); - break; default: - if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED)) { + if (CHECK_FLAG(exti->flags, EXT_LPFLG_FIB_ENTRY_SET)) + ospf_ext_link_delete_adj_sid(exti); + if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED)) ospf_ext_link_lsa_schedule(exti, FLUSH_THIS_LSA); - UNSET_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED); - UNSET_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE); - } + exti->flags = EXT_LPFLG_LSA_INACTIVE; return; } - if (IS_DEBUG_OSPF_SR) - zlog_debug("EXT (%s): Complete %s SID to interface %s ", - __func__, - exti->stype == ADJ_SID ? "Adj." : "LAN Adj.", - oi->ifp->name); - - /* flood this links params if everything is ok */ SET_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE); + if (OspfEXT.enabled) { + osr_debug("EXT (%s): Set %sAdjacency SID for interface %s ", + __func__, exti->stype == ADJ_SID ? "" : "LAN-", + oi->ifp->name); + + /* Update (LAN)Adjacency SID */ + ospf_ext_link_update_adj_sid(exti); + + /* flood this links params if everything is ok */ if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED)) ospf_ext_link_lsa_schedule(exti, REFRESH_THIS_LSA); else @@ -775,6 +918,10 @@ static int ospf_ext_link_lsa_update(struct ospf_lsa *lsa) != OPAQUE_TYPE_EXTENDED_LINK_LSA) return 0; + /* Check if it is not my LSA */ + if (IS_LSA_SELF(lsa)) + return 0; + /* Check if Extended is enable */ if (!OspfEXT.enabled) return 0; @@ -959,12 +1106,9 @@ static struct ospf_lsa *ospf_ext_pref_lsa_new(struct ospf_area *area, /* Set opaque-LSA header fields. */ lsa_header_set(s, options, lsa_type, lsa_id, router_id); - if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) - zlog_debug( - "EXT (%s): LSA[Type%u:%s]: Create an Opaque-LSA " - "Extended Prefix Opaque LSA instance", - __func__, lsa_type, inet_ntoa(lsa_id)); - + osr_debug( + "EXT (%s): LSA[Type%u:%pI4]: Create an Opaque-LSA Extended Prefix Opaque LSA instance", + __func__, lsa_type, &lsa_id); /* Set opaque-LSA body fields. */ ospf_ext_pref_lsa_body_set(s, exti); @@ -1019,11 +1163,9 @@ static struct ospf_lsa *ospf_ext_link_lsa_new(struct ospf_area *area, tmp = SET_OPAQUE_LSID(OPAQUE_TYPE_EXTENDED_LINK_LSA, exti->instance); lsa_id.s_addr = htonl(tmp); - if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) - zlog_debug( - "EXT (%s) LSA[Type%u:%s]: Create an Opaque-LSA " - "Extended Link Opaque LSA instance", - __func__, lsa_type, inet_ntoa(lsa_id)); + osr_debug( + "EXT (%s) LSA[Type%u:%pI4]: Create an Opaque-LSA Extended Link Opaque LSA instance", + __func__, lsa_type, &lsa_id); /* Set opaque-LSA header fields. */ lsa_header_set(s, options, lsa_type, lsa_id, area->ospf->router_id); @@ -1086,17 +1228,12 @@ static int ospf_ext_pref_lsa_originate1(struct ospf_area *area, /* Flood new LSA through area. */ ospf_flood_through_area(area, NULL /*nbr */, new); - if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) { - char area_id[INET_ADDRSTRLEN]; - - inet_ntop(AF_INET, &area->area_id, area_id, sizeof(area_id)); - zlog_debug( - "EXT (%s): LSA[Type%u:%s]: Originate Opaque-LSA " - "Extended Prefix Opaque LSA: Area(%s), Link(%s)", - __func__, new->data->type, inet_ntoa(new->data->id), - area_id, exti->ifp->name); + osr_debug( + "EXT (%s): LSA[Type%u:%pI4]: Originate Opaque-LSAExtended Prefix Opaque LSA: Area(%pI4), Link(%s)", + __func__, new->data->type, &new->data->id, + &area->area_id, exti->ifp->name); + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) ospf_lsa_header_dump(new->data); - } rc = 0; @@ -1138,17 +1275,12 @@ static int ospf_ext_link_lsa_originate1(struct ospf_area *area, /* Flood new LSA through area. */ ospf_flood_through_area(area, NULL /*nbr */, new); - if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) { - char area_id[INET_ADDRSTRLEN]; - - inet_ntop(AF_INET, &area->area_id, area_id, sizeof(area_id)); - zlog_debug( - "EXT (%s): LSA[Type%u:%s]: Originate Opaque-LSA " - "Extended Link Opaque LSA: Area(%s), Link(%s)", - __func__, new->data->type, inet_ntoa(new->data->id), - area_id, exti->ifp->name); + osr_debug( + "EXT (%s): LSA[Type%u:%pI4]: Originate Opaque-LSA Extended Link Opaque LSA: Area(%pI4), Link(%s)", + __func__, new->data->type, &new->data->id, + &area->area_id, exti->ifp->name); + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) ospf_lsa_header_dump(new->data); - } rc = 0; @@ -1165,15 +1297,13 @@ static int ospf_ext_pref_lsa_originate(void *arg) if (!OspfEXT.enabled) { zlog_info( - "EXT (%s): Segment Routing " - "functionality is Disabled now", + "EXT (%s): Segment Routing functionality is Disabled now", __func__); rc = 0; /* This is not an error case. */ return rc; } - if (IS_DEBUG_OSPF_SR) - zlog_debug("EXT (%s): Start Originate Prefix LSA for area %s", - __func__, inet_ntoa(area->area_id)); + osr_debug("EXT (%s): Start Originate Prefix LSA for area %pI4", + __func__, &area->area_id); /* Check if Extended Prefix Opaque LSA is already engaged */ for (ALL_LIST_ELEMENTS_RO(OspfEXT.iflist, node, exti)) { @@ -1203,12 +1333,9 @@ static int ospf_ext_pref_lsa_originate(void *arg) } /* Ok, let's try to originate an LSA */ - if (IS_DEBUG_OSPF_SR) - zlog_debug( - "EXT (%s): Let's finally reoriginate the " - "LSA 7.0.0.%u for Itf %s", - __func__, exti->instance, - exti->ifp ? exti->ifp->name : ""); + osr_debug( + "EXT (%s): Let's finally re-originate the LSA 7.0.0.%u for Itf %s", __func__, exti->instance, + exti->ifp ? exti->ifp->name : ""); ospf_ext_pref_lsa_originate1(area, exti); } @@ -1226,8 +1353,7 @@ static int ospf_ext_link_lsa_originate(void *arg) if (!OspfEXT.enabled) { zlog_info( - "EXT (%s): Segment Routing " - "functionality is Disabled now", + "EXT (%s): Segment Routing functionality is Disabled now", __func__); rc = 0; /* This is not an error case. */ return rc; @@ -1239,6 +1365,10 @@ static int ospf_ext_link_lsa_originate(void *arg) if (exti->stype == PREF_SID) continue; + /* Skip Inactive Extended Link */ + if (!CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE)) + continue; + /* Process only Extended Link with valid Area ID */ if ((exti->area == NULL) || (!IPV4_ADDR_SAME(&exti->area->area_id, &area->area_id))) @@ -1261,13 +1391,10 @@ static int ospf_ext_link_lsa_originate(void *arg) } /* Ok, let's try to originate an LSA */ - if (IS_DEBUG_OSPF_SR) - zlog_debug( - "EXT (%s): Let's finally reoriginate the " - "LSA 8.0.0.%u for Itf %s through the Area %s", - __func__, exti->instance, - exti->ifp ? exti->ifp->name : "-", - inet_ntoa(area->area_id)); + osr_debug( + "EXT (%s): Let's finally reoriginate the LSA 8.0.0.%u for Itf %s through the Area %pI4", __func__, + exti->instance, exti->ifp ? exti->ifp->name : "-", + &area->area_id); ospf_ext_link_lsa_originate1(area, exti); } @@ -1290,8 +1417,7 @@ static struct ospf_lsa *ospf_ext_pref_lsa_refresh(struct ospf_lsa *lsa) * It seems a slip among routers in the routing domain. */ zlog_info( - "EXT (%s): Segment Routing functionality is " - "Disabled", + "EXT (%s): Segment Routing functionality is Disabled", __func__); /* Flush it anyway. */ lsa->data->ls_age = htons(OSPF_LSA_MAXAGE); @@ -1355,12 +1481,11 @@ static struct ospf_lsa *ospf_ext_pref_lsa_refresh(struct ospf_lsa *lsa) ospf_flood_through_area(area, NULL /*nbr */, new); /* Debug logging. */ - if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) { - zlog_debug( - "EXT (%s): LSA[Type%u:%s] Refresh Extended Prefix LSA", - __func__, new->data->type, inet_ntoa(new->data->id)); + osr_debug("EXT (%s): LSA[Type%u:%pI4] Refresh Extended Prefix LSA", + __func__, new->data->type, &new->data->id); + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) ospf_lsa_header_dump(new->data); - } + return new; } @@ -1431,12 +1556,10 @@ static struct ospf_lsa *ospf_ext_link_lsa_refresh(struct ospf_lsa *lsa) ospf_flood_through_area(area, NULL /*nbr */, new); /* Debug logging. */ - if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) { - zlog_debug( - "EXT (%s): LSA[Type%u:%s]: Refresh Extended Link LSA", - __func__, new->data->type, inet_ntoa(new->data->id)); + osr_debug("EXT (%s): LSA[Type%u:%pI4]: Refresh Extended Link LSA", + __func__, new->data->type, &new->data->id); + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) ospf_lsa_header_dump(new->data); - } return new; } @@ -1461,25 +1584,28 @@ static void ospf_ext_pref_lsa_schedule(struct ext_itf *exti, if (!(CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE))) return; - zlog_debug("EXT (%s): Schedule %s%s%s LSA for interface %s", __func__, - opcode == REORIGINATE_THIS_LSA ? "Re-Originate" : "", - opcode == REFRESH_THIS_LSA ? "Refresh" : "", - opcode == FLUSH_THIS_LSA ? "Flush" : "", - exti->ifp ? exti->ifp->name : "-"); + osr_debug("EXT (%s): Schedule %s%s%s LSA for interface %s", __func__, + opcode == REORIGINATE_THIS_LSA ? "Re-Originate" : "", + opcode == REFRESH_THIS_LSA ? "Refresh" : "", + opcode == FLUSH_THIS_LSA ? "Flush" : "", + exti->ifp ? exti->ifp->name : "-"); - /* Set LSA header information */ + /* Verify Area */ if (exti->area == NULL) { - flog_warn( - EC_OSPF_EXT_LSA_UNEXPECTED, - "EXT (%s): Flooding is Area scope but area is not yet set", + osr_debug( + "EXT (%s): Area is not yet set. Try to use Backbone Area", __func__); - if (OspfEXT.area == NULL) { - top = ospf_lookup_by_vrf_id(VRF_DEFAULT); - OspfEXT.area = ospf_area_lookup_by_area_id( - top, OspfEXT.area_id); + + top = ospf_lookup_by_vrf_id(VRF_DEFAULT); + struct in_addr backbone = {.s_addr = INADDR_ANY}; + exti->area = ospf_area_lookup_by_area_id(top, backbone); + if (exti->area == NULL) { + flog_warn(EC_OSPF_EXT_LSA_UNEXPECTED, + "EXT (%s): Unable to set Area", __func__); + return; } - exti->area = OspfEXT.area; } + /* Set LSA header information */ lsa.area = exti->area; lsa.data = &lsah; lsah.type = OSPF_OPAQUE_AREA_LSA; @@ -1522,25 +1648,28 @@ static void ospf_ext_link_lsa_schedule(struct ext_itf *exti, if (!(CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE))) return; - zlog_debug("EXT (%s): Schedule %s%s%s LSA for interface %s", __func__, - opcode == REORIGINATE_THIS_LSA ? "Re-Originate" : "", - opcode == REFRESH_THIS_LSA ? "Refresh" : "", - opcode == FLUSH_THIS_LSA ? "Flush" : "", - exti->ifp ? exti->ifp->name : "-"); + osr_debug("EXT (%s): Schedule %s%s%s LSA for interface %s", __func__, + opcode == REORIGINATE_THIS_LSA ? "Re-Originate" : "", + opcode == REFRESH_THIS_LSA ? "Refresh" : "", + opcode == FLUSH_THIS_LSA ? "Flush" : "", + exti->ifp ? exti->ifp->name : "-"); - /* Set LSA header information */ + /* Verify Area */ if (exti->area == NULL) { - flog_warn( - EC_OSPF_EXT_LSA_UNEXPECTED, - "EXT (%s): Flooding is Area scope but area is not yet set", + osr_debug( + "EXT (%s): Area is not yet set. Try to use Backbone Area", __func__); - if (OspfEXT.area == NULL) { - top = ospf_lookup_by_vrf_id(VRF_DEFAULT); - OspfEXT.area = ospf_area_lookup_by_area_id( - top, OspfEXT.area_id); + + top = ospf_lookup_by_vrf_id(VRF_DEFAULT); + struct in_addr backbone = {.s_addr = INADDR_ANY}; + exti->area = ospf_area_lookup_by_area_id(top, backbone); + if (exti->area == NULL) { + flog_warn(EC_OSPF_EXT_LSA_UNEXPECTED, + "EXT (%s): Unable to set Area", __func__); + return; } - exti->area = OspfEXT.area; } + /* Set LSA header information */ lsa.area = exti->area; lsa.data = &lsah; lsah.type = OSPF_OPAQUE_AREA_LSA; @@ -1557,7 +1686,6 @@ static void ospf_ext_link_lsa_schedule(struct ext_itf *exti, ospf_opaque_lsa_refresh_schedule(&lsa); break; case FLUSH_THIS_LSA: - UNSET_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED); ospf_opaque_lsa_flush_schedule(&lsa); break; } @@ -1588,8 +1716,7 @@ static uint16_t show_vty_ext_link_rmt_itf_addr(struct vty *vty, top = (struct ext_subtlv_rmt_itf_addr *)tlvh; vty_out(vty, - " Remote Interface Address Sub-TLV: Length %u\n " - "Address: %s\n", + " Remote Interface Address Sub-TLV: Length %u\n Address: %s\n", ntohs(top->header.length), inet_ntoa(top->value)); return TLV_SIZE(tlvh); @@ -1602,8 +1729,7 @@ static uint16_t show_vty_ext_link_adj_sid(struct vty *vty, struct ext_subtlv_adj_sid *top = (struct ext_subtlv_adj_sid *)tlvh; vty_out(vty, - " Adj-SID Sub-TLV: Length %u\n\tFlags: " - "0x%x\n\tMT-ID:0x%x\n\tWeight: 0x%x\n\t%s: %u\n", + " Adj-SID Sub-TLV: Length %u\n\tFlags: 0x%x\n\tMT-ID:0x%x\n\tWeight: 0x%x\n\t%s: %u\n", ntohs(top->header.length), top->flags, top->mtid, top->weight, CHECK_FLAG(top->flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG) ? "Label" : "Index", @@ -1622,9 +1748,7 @@ static uint16_t show_vty_ext_link_lan_adj_sid(struct vty *vty, (struct ext_subtlv_lan_adj_sid *)tlvh; vty_out(vty, - " LAN-Adj-SID Sub-TLV: Length %u\n\tFlags: " - "0x%x\n\tMT-ID:0x%x\n\tWeight: 0x%x\n\tNeighbor ID: " - "%s\n\t%s: %u\n", + " LAN-Adj-SID Sub-TLV: Length %u\n\tFlags: 0x%x\n\tMT-ID:0x%x\n\tWeight: 0x%x\n\tNeighbor ID: %s\n\t%s: %u\n", ntohs(top->header.length), top->flags, top->mtid, top->weight, inet_ntoa(top->neighbor_id), CHECK_FLAG(top->flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG) ? "Label" @@ -1684,7 +1808,7 @@ static uint16_t show_vty_link_info(struct vty *vty, struct tlv_header *ext) /* Extended Link TLVs */ static void ospf_ext_link_show_info(struct vty *vty, struct ospf_lsa *lsa) { - struct lsa_header *lsah = (struct lsa_header *)lsa->data; + struct lsa_header *lsah = lsa->data; struct tlv_header *tlvh; uint16_t length = 0, sum = 0; @@ -1712,8 +1836,7 @@ static uint16_t show_vty_ext_pref_pref_sid(struct vty *vty, (struct ext_subtlv_prefix_sid *)tlvh; vty_out(vty, - " Prefix SID Sub-TLV: Length %u\n\tAlgorithm: " - "%u\n\tFlags: 0x%x\n\tMT-ID:0x%x\n\t%s: %u\n", + " Prefix SID Sub-TLV: Length %u\n\tAlgorithm: %u\n\tFlags: 0x%x\n\tMT-ID:0x%x\n\t%s: %u\n", ntohs(top->header.length), top->algorithm, top->flags, top->mtid, CHECK_FLAG(top->flags, EXT_SUBTLV_PREFIX_SID_VFLG) ? "Label" @@ -1758,7 +1881,7 @@ static uint16_t show_vty_pref_info(struct vty *vty, struct tlv_header *ext) /* Extended Prefix TLVs */ static void ospf_ext_pref_show_info(struct vty *vty, struct ospf_lsa *lsa) { - struct lsa_header *lsah = (struct lsa_header *)lsa->data; + struct lsa_header *lsah = lsa->data; struct tlv_header *tlvh; uint16_t length = 0, sum = 0; diff --git a/ospfd/ospf_ext.h b/ospfd/ospf_ext.h index c3f9ae94dc..b1e4feb6df 100644 --- a/ospfd/ospf_ext.h +++ b/ospfd/ospf_ext.h @@ -151,10 +151,6 @@ struct ospf_ext_lp { */ uint8_t scope; - /* area pointer if flooding is Type 10 Null if flooding is AS scope */ - struct ospf_area *area; - struct in_addr area_id; - /* List of interface with Segment Routing enable */ struct list *iflist; }; @@ -193,6 +189,7 @@ extern int ospf_ext_init(void); extern void ospf_ext_term(void); extern void ospf_ext_finish(void); extern void ospf_ext_update_sr(bool enable); +extern void ospf_ext_link_srlb_update(void); extern uint32_t ospf_ext_schedule_prefix_index(struct interface *ifp, uint32_t index, struct prefix_ipv4 *p, diff --git a/ospfd/ospf_flood.c b/ospfd/ospf_flood.c index 6d1e44996e..58afb2b392 100644 --- a/ospfd/ospf_flood.c +++ b/ospfd/ospf_flood.c @@ -83,6 +83,9 @@ struct external_info *ospf_external_info_check(struct ospf *ospf, struct as_external_lsa *al; struct prefix_ipv4 p; struct route_node *rn; + struct list *ext_list; + struct listnode *node; + struct ospf_external *ext; int type; al = (struct as_external_lsa *)lsa->data; @@ -91,7 +94,7 @@ struct external_info *ospf_external_info_check(struct ospf *ospf, p.prefix = lsa->data->id; p.prefixlen = ip_masklen(al->mask); - for (type = 0; type <= ZEBRA_ROUTE_MAX; type++) { + for (type = 0; type < ZEBRA_ROUTE_MAX; type++) { int redist_on = 0; redist_on = @@ -105,10 +108,6 @@ struct external_info *ospf_external_info_check(struct ospf *ospf, ospf->vrf_id)); // Pending: check for MI above. if (redist_on) { - struct list *ext_list; - struct listnode *node; - struct ospf_external *ext; - ext_list = ospf->external[type]; if (!ext_list) continue; @@ -129,6 +128,22 @@ struct external_info *ospf_external_info_check(struct ospf *ospf, } } + if (is_prefix_default(&p) && ospf->external[DEFAULT_ROUTE]) { + ext_list = ospf->external[DEFAULT_ROUTE]; + + for (ALL_LIST_ELEMENTS_RO(ext_list, node, ext)) { + if (!ext->external_info) + continue; + + rn = route_node_lookup(ext->external_info, + (struct prefix *)&p); + if (!rn) + continue; + route_unlock_node(rn); + if (rn->info != NULL) + return (struct external_info *)rn->info; + } + } return NULL; } @@ -142,9 +157,9 @@ static void ospf_process_self_originated_lsa(struct ospf *ospf, if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "LSA[Type%d:%s]: Process self-originated LSA seq 0x%x", - new->data->type, inet_ntoa(new->data->id), - ntohl(new->data->ls_seqnum)); + "%s:LSA[Type%d:%s]: Process self-originated LSA seq 0x%x", + ospf_get_name(ospf), new->data->type, + inet_ntoa(new->data->id), ntohl(new->data->ls_seqnum)); /* If we're here, we installed a self-originated LSA that we received from a neighbor, i.e. it's more recent. We must see whether we want @@ -261,8 +276,8 @@ int ospf_flood(struct ospf *ospf, struct ospf_neighbor *nbr, if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "LSA[Flooding]: start, NBR %s (%s), cur(%p), New-LSA[%s]", - inet_ntoa(nbr->router_id), + "%s:LSA[Flooding]: start, NBR %s (%s), cur(%p), New-LSA[%s]", + ospf_get_name(ospf), inet_ntoa(nbr->router_id), lookup_msg(ospf_nsm_state_msg, nbr->state, NULL), (void *)current, dump_lsa_key(new)); @@ -280,15 +295,16 @@ int ospf_flood(struct ospf *ospf, struct ospf_neighbor *nbr, == OSPF_INITIAL_SEQUENCE_NUMBER)) { if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "LSA[Flooding]: Got a self-originated LSA, " - "while local one is initial instance."); + "%s:LSA[Flooding]: Got a self-originated LSA, while local one is initial instance.", + ospf_get_name(ospf)); ; /* Accept this LSA for quick LSDB resynchronization. */ } else if (monotime_since(¤t->tv_recv, NULL) < ospf->min_ls_arrival * 1000LL) { if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "LSA[Flooding]: LSA is received recently."); + "%s:LSA[Flooding]: LSA is received recently.", + ospf_get_name(ospf)); return -1; } } @@ -312,8 +328,7 @@ int ospf_flood(struct ospf *ospf, struct ospf_neighbor *nbr, ospf_ls_retransmit_delete_nbr_as(ospf, current); break; default: - ospf_ls_retransmit_delete_nbr_area(nbr->oi->area, - current); + ospf_ls_retransmit_delete_nbr_area(oi->area, current); break; } } @@ -329,7 +344,7 @@ int ospf_flood(struct ospf *ospf, struct ospf_neighbor *nbr, procedure cannot overwrite the newly installed LSA until MinLSArrival seconds have elapsed. */ - if (!(new = ospf_lsa_install(ospf, nbr->oi, new))) + if (!(new = ospf_lsa_install(ospf, oi, new))) return -1; /* unknown LSA type or any other error condition */ /* Acknowledge the receipt of the LSA by sending a Link State @@ -361,9 +376,8 @@ static int ospf_flood_through_interface(struct ospf_interface *oi, if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_flood_through_interface(): " - "considering int %s, INBR(%s), LSA[%s] AGE %u", - IF_NAME(oi), inbr ? inet_ntoa(inbr->router_id) : "NULL", + "%s:ospf_flood_through_interface(): considering int %s, INBR(%s), LSA[%s] AGE %u", + ospf_get_name(oi->ospf), IF_NAME(oi), inbr ? inet_ntoa(inbr->router_id) : "NULL", dump_lsa_key(lsa), ntohs(lsa->data->ls_age)); if (!ospf_if_is_enable(oi)) @@ -384,8 +398,9 @@ static int ospf_flood_through_interface(struct ospf_interface *oi, onbr = rn->info; if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_flood_through_interface(): considering nbr %s (%s)", + "ospf_flood_through_interface(): considering nbr %s(%s) (%s)", inet_ntoa(onbr->router_id), + ospf_get_name(oi->ospf), lookup_msg(ospf_nsm_state_msg, onbr->state, NULL)); @@ -439,13 +454,8 @@ static int ospf_flood_through_interface(struct ospf_interface *oi, } } -/* If the new LSA was received from this neighbor, - examine the next neighbor. */ -#ifdef ORIGINAL_CODING - if (inbr) - if (IPV4_ADDR_SAME(&inbr->router_id, &onbr->router_id)) - continue; -#else /* ORIGINAL_CODING */ + /* If the new LSA was received from this neighbor, + examine the next neighbor. */ if (inbr) { /* * Triggered by LSUpd message parser "ospf_ls_upd ()". @@ -471,7 +481,6 @@ static int ospf_flood_through_interface(struct ospf_interface *oi, continue; } } -#endif /* ORIGINAL_CODING */ /* Add the new LSA to the Link state retransmission list for the adjacency. The LSA will be retransmitted @@ -498,8 +507,7 @@ static int ospf_flood_through_interface(struct ospf_interface *oi, if (NBR_IS_DR(inbr) || NBR_IS_BDR(inbr)) { if (IS_DEBUG_OSPF_NSSA) zlog_debug( - "ospf_flood_through_interface(): " - "DR/BDR NOT SEND to int %s", + "ospf_flood_through_interface(): DR/BDR NOT SEND to int %s", IF_NAME(oi)); return 1; } @@ -513,8 +521,7 @@ static int ospf_flood_through_interface(struct ospf_interface *oi, if (oi->state == ISM_Backup) { if (IS_DEBUG_OSPF_NSSA) zlog_debug( - "ospf_flood_through_interface(): " - "ISM_Backup NOT SEND to int %s", + "ospf_flood_through_interface(): ISM_Backup NOT SEND to int %s", IF_NAME(oi)); return 1; } @@ -529,8 +536,7 @@ static int ospf_flood_through_interface(struct ospf_interface *oi, /* XXX HASSO: Is this IS_DEBUG_OSPF_NSSA really correct? */ if (IS_DEBUG_OSPF_NSSA) zlog_debug( - "ospf_flood_through_interface(): " - "DR/BDR sending upd to int %s", + "ospf_flood_through_interface(): DR/BDR sending upd to int %s", IF_NAME(oi)); /* RFC2328 Section 13.3 @@ -676,43 +682,14 @@ int ospf_flood_through(struct ospf *ospf, struct ospf_neighbor *inbr, { int lsa_ack_flag = 0; -/* Type-7 LSA's for NSSA are flooded throughout the AS here, and - upon return are updated in the LSDB for Type-7's. Later, - re-fresh will re-send them (and also, if ABR, packet code will - translate to Type-5's) - - As usual, Type-5 LSA's (if not DISCARDED because we are STUB or - NSSA) are flooded throughout the AS, and are updated in the - global table. */ -#ifdef ORIGINAL_CODING - switch (lsa->data->type) { - case OSPF_ROUTER_LSA: - case OSPF_NETWORK_LSA: - case OSPF_SUMMARY_LSA: - case OSPF_ASBR_SUMMARY_LSA: - case OSPF_OPAQUE_LINK_LSA: /* ospf_flood_through_interface ? */ - case OSPF_OPAQUE_AREA_LSA: - lsa_ack_flag = - ospf_flood_through_area(inbr->oi->area, inbr, lsa); - break; - case OSPF_AS_EXTERNAL_LSA: /* Type-5 */ - case OSPF_OPAQUE_AS_LSA: - lsa_ack_flag = ospf_flood_through_as(ospf, inbr, lsa); - break; - /* Type-7 Only received within NSSA, then flooded */ - case OSPF_AS_NSSA_LSA: - /* Any P-bit was installed with the Type-7. */ - lsa_ack_flag = - ospf_flood_through_area(inbr->oi->area, inbr, lsa); + /* Type-7 LSA's for NSSA are flooded throughout the AS here, and + upon return are updated in the LSDB for Type-7's. Later, + re-fresh will re-send them (and also, if ABR, packet code will + translate to Type-5's) - if (IS_DEBUG_OSPF_NSSA) - zlog_debug( - "ospf_flood_through: LOCAL NSSA FLOOD of Type-7."); - break; - default: - break; - } -#else /* ORIGINAL_CODING */ + As usual, Type-5 LSA's (if not DISCARDED because we are STUB or + NSSA) are flooded throughout the AS, and are updated in the + global table. */ /* * At the common sub-sub-function "ospf_flood_through_interface()", * a parameter "inbr" will be used to distinguish the called context @@ -742,7 +719,6 @@ int ospf_flood_through(struct ospf *ospf, struct ospf_neighbor *inbr, lsa_ack_flag = ospf_flood_through_area(lsa->area, inbr, lsa); break; } -#endif /* ORIGINAL_CODING */ return (lsa_ack_flag); } @@ -758,9 +734,10 @@ void ospf_ls_request_add(struct ospf_neighbor *nbr, struct ospf_lsa *lsa) * the common function "ospf_lsdb_add()" -- endo. */ if (IS_DEBUG_OSPF(lsa, LSA_FLOODING)) - zlog_debug("RqstL(%lu)++, NBR(%s), LSA[%s]", + zlog_debug("RqstL(%lu)++, NBR(%s(%s)), LSA[%s]", ospf_ls_request_count(nbr), - inet_ntoa(nbr->router_id), dump_lsa_key(lsa)); + inet_ntoa(nbr->router_id), + ospf_get_name(nbr->oi->ospf), dump_lsa_key(lsa)); ospf_lsdb_add(&nbr->ls_req, lsa); } @@ -784,9 +761,10 @@ void ospf_ls_request_delete(struct ospf_neighbor *nbr, struct ospf_lsa *lsa) } if (IS_DEBUG_OSPF(lsa, LSA_FLOODING)) /* -- endo. */ - zlog_debug("RqstL(%lu)--, NBR(%s), LSA[%s]", + zlog_debug("RqstL(%lu)--, NBR(%s(%s)), LSA[%s]", ospf_ls_request_count(nbr), - inet_ntoa(nbr->router_id), dump_lsa_key(lsa)); + inet_ntoa(nbr->router_id), + ospf_get_name(nbr->oi->ospf), dump_lsa_key(lsa)); ospf_lsdb_delete(&nbr->ls_req, lsa); } @@ -844,6 +822,12 @@ void ospf_ls_retransmit_add(struct ospf_neighbor *nbr, struct ospf_lsa *lsa) if (ospf_lsa_more_recent(old, lsa) < 0) { if (old) { old->retransmit_counter--; + if (IS_DEBUG_OSPF(lsa, LSA_FLOODING)) + zlog_debug("RXmtL(%lu)--, NBR(%s(%s)), LSA[%s]", + ospf_ls_retransmit_count(nbr), + inet_ntoa(nbr->router_id), + ospf_get_name(nbr->oi->ospf), + dump_lsa_key(old)); ospf_lsdb_delete(&nbr->ls_rxmt, old); } lsa->retransmit_counter++; @@ -856,9 +840,10 @@ void ospf_ls_retransmit_add(struct ospf_neighbor *nbr, struct ospf_lsa *lsa) * the common function "ospf_lsdb_add()" -- endo. */ if (IS_DEBUG_OSPF(lsa, LSA_FLOODING)) - zlog_debug("RXmtL(%lu)++, NBR(%s), LSA[%s]", + zlog_debug("RXmtL(%lu)++, NBR(%s(%s)), LSA[%s]", ospf_ls_retransmit_count(nbr), inet_ntoa(nbr->router_id), + ospf_get_name(nbr->oi->ospf), dump_lsa_key(lsa)); ospf_lsdb_add(&nbr->ls_rxmt, lsa); } @@ -870,9 +855,10 @@ void ospf_ls_retransmit_delete(struct ospf_neighbor *nbr, struct ospf_lsa *lsa) if (ospf_ls_retransmit_lookup(nbr, lsa)) { lsa->retransmit_counter--; if (IS_DEBUG_OSPF(lsa, LSA_FLOODING)) /* -- endo. */ - zlog_debug("RXmtL(%lu)--, NBR(%s), LSA[%s]", + zlog_debug("RXmtL(%lu)--, NBR(%s(%s)), LSA[%s]", ospf_ls_retransmit_count(nbr), inet_ntoa(nbr->router_id), + ospf_get_name(nbr->oi->ospf), dump_lsa_key(lsa)); ospf_lsdb_delete(&nbr->ls_rxmt, lsa); } @@ -959,7 +945,7 @@ void ospf_lsa_flush_area(struct ospf_lsa *lsa, struct ospf_area *area) retransmissions */ lsa->data->ls_age = htons(OSPF_LSA_MAXAGE); if (IS_DEBUG_OSPF_EVENT) - zlog_debug("%s: MAXAGE set to LSA %s", __PRETTY_FUNCTION__, + zlog_debug("%s: MAXAGE set to LSA %s", __func__, inet_ntoa(lsa->data->id)); monotime(&lsa->tv_recv); lsa->tv_orig = lsa->tv_recv; diff --git a/ospfd/ospf_ia.c b/ospfd/ospf_ia.c index f1ba8a31e8..87929e4369 100644 --- a/ospfd/ospf_ia.c +++ b/ospfd/ospf_ia.c @@ -88,8 +88,7 @@ static void ospf_ia_network_route(struct ospf *ospf, struct route_table *rt, if ((or = rn1->info)) { if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_ia_network_route(): " - "Found a route to the same network"); + "ospf_ia_network_route(): Found a route to the same network"); /* Check the existing route. */ if ((res = ospf_route_cmp(ospf, new_or, or)) < 0) { /* New route is better, so replace old one. */ @@ -151,8 +150,7 @@ static void ospf_ia_router_route(struct ospf *ospf, struct route_table *rtrs, if (or) { if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_ia_router_route(): " - "a route to the same ABR through the same area exists"); + "ospf_ia_router_route(): a route to the same ABR through the same area exists"); /* New route is better */ if ((ret = ospf_route_cmp(ospf, new_or, or)) < 0) { listnode_delete(rn->info, or); @@ -329,8 +327,7 @@ static void ospf_update_network_route(struct ospf *ospf, struct route_table *rt, backbone paths */ if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_update_network_route(): " - "Allowing Shortcut ABR to add new route"); + "ospf_update_network_route(): Allowing Shortcut ABR to add new route"); new_or = ospf_route_new(); new_or->type = OSPF_DESTINATION_NETWORK; new_or->id = lsa->header.id; @@ -367,8 +364,7 @@ static void ospf_update_network_route(struct ospf *ospf, struct route_table *rt, or->u.std.area_id)) { if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_update_network_route(): Shortcut: " - "this intra-area path is not backbone"); + "ospf_update_network_route(): Shortcut: this intra-area path is not backbone"); return; } } else /* Not Shortcut ABR */ @@ -376,8 +372,7 @@ static void ospf_update_network_route(struct ospf *ospf, struct route_table *rt, if (!OSPF_IS_AREA_ID_BACKBONE(or->u.std.area_id)) { if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_update_network_route(): " - "route is not BB-associated"); + "ospf_update_network_route(): route is not BB-associated"); return; /* We can update only BB routes */ } } @@ -392,16 +387,14 @@ static void ospf_update_network_route(struct ospf *ospf, struct route_table *rt, if (or->cost == cost) { if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_update_network_route(): " - "new route is same distance, adding nexthops"); + "ospf_update_network_route(): new route is same distance, adding nexthops"); ospf_route_copy_nexthops(or, abr_or->paths); } if (or->cost > cost) { if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_update_network_route(): " - "new route is better, overriding nexthops"); + "ospf_update_network_route(): new route is better, overriding nexthops"); ospf_route_subst_nexthops(or, abr_or->paths); or->cost = cost; @@ -649,8 +642,7 @@ void ospf_ia_routing(struct ospf *ospf, struct route_table *rt, */ if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_ia_routing(): " - "Active BB connection not found"); + "ospf_ia_routing(): Active BB connection not found"); for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) OSPF_EXAMINE_SUMMARIES_ALL(area, rt, diff --git a/ospfd/ospf_interface.c b/ospfd/ospf_interface.c index ce1604a5b1..190f05fe3e 100644 --- a/ospfd/ospf_interface.c +++ b/ospfd/ospf_interface.c @@ -50,6 +50,8 @@ DEFINE_QOBJ_TYPE(ospf_interface) DEFINE_HOOK(ospf_vl_add, (struct ospf_vl_data * vd), (vd)) DEFINE_HOOK(ospf_vl_delete, (struct ospf_vl_data * vd), (vd)) +DEFINE_HOOK(ospf_if_update, (struct interface * ifp), (ifp)) +DEFINE_HOOK(ospf_if_delete, (struct interface * ifp), (ifp)) int ospf_interface_neighbor_count(struct ospf_interface *oi) { @@ -225,12 +227,14 @@ struct ospf_interface *ospf_if_new(struct ospf *ospf, struct interface *ifp, { struct ospf_interface *oi; - if ((oi = ospf_if_table_lookup(ifp, p)) == NULL) { - oi = XCALLOC(MTYPE_OSPF_IF, sizeof(struct ospf_interface)); - memset(oi, 0, sizeof(struct ospf_interface)); - } else + oi = ospf_if_table_lookup(ifp, p); + if (oi) return oi; + oi = XCALLOC(MTYPE_OSPF_IF, sizeof(struct ospf_interface)); + + oi->obuf = ospf_fifo_new(); + /* Set zebra interface pointer. */ oi->ifp = ifp; oi->address = p; @@ -264,14 +268,12 @@ struct ospf_interface *ospf_if_new(struct ospf *ospf, struct interface *ifp, oi->ospf = ospf; - ospf_if_stream_set(oi); - QOBJ_REG(oi, ospf_interface); if (IS_DEBUG_OSPF_EVENT) zlog_debug("%s: ospf interface %s vrf %s id %u created", - __PRETTY_FUNCTION__, ifp->name, - ospf_vrf_id_to_name(ospf->vrf_id), ospf->vrf_id); + __func__, ifp->name, ospf_get_name(ospf), + ospf->vrf_id); return oi; } @@ -325,8 +327,7 @@ void ospf_if_free(struct ospf_interface *oi) { ospf_if_down(oi); - if (oi->obuf) - ospf_fifo_free(oi->obuf); + ospf_fifo_free(oi->obuf); assert(oi->state == ISM_Down); @@ -348,7 +349,7 @@ void ospf_if_free(struct ospf_interface *oi) if (IS_DEBUG_OSPF_EVENT) zlog_debug("%s: ospf interface %s vrf %s id %u deleted", - __PRETTY_FUNCTION__, oi->ifp->name, + __func__, oi->ifp->name, ospf_vrf_id_to_name(oi->ifp->vrf_id), oi->ifp->vrf_id); @@ -490,29 +491,20 @@ static void ospf_if_reset_stats(struct ospf_interface *oi) oi->ls_ack_in = oi->ls_ack_out = 0; } -void ospf_if_stream_set(struct ospf_interface *oi) -{ - /* set output fifo queue. */ - if (oi->obuf == NULL) - oi->obuf = ospf_fifo_new(); -} - void ospf_if_stream_unset(struct ospf_interface *oi) { struct ospf *ospf = oi->ospf; - if (oi->obuf) { - /* flush the interface packet queue */ - ospf_fifo_flush(oi->obuf); - /*reset protocol stats */ - ospf_if_reset_stats(oi); - - if (oi->on_write_q) { - listnode_delete(ospf->oi_write_q, oi); - if (list_isempty(ospf->oi_write_q)) - OSPF_TIMER_OFF(ospf->t_write); - oi->on_write_q = 0; - } + /* flush the interface packet queue */ + ospf_fifo_flush(oi->obuf); + /*reset protocol stats */ + ospf_if_reset_stats(oi); + + if (oi->on_write_q) { + listnode_delete(ospf->oi_write_q, oi); + if (list_isempty(ospf->oi_write_q)) + OSPF_TIMER_OFF(ospf->t_write); + oi->on_write_q = 0; } } @@ -535,10 +527,12 @@ static struct ospf_if_params *ospf_new_if_params(void) UNSET_IF_PARAM(oip, auth_simple); UNSET_IF_PARAM(oip, auth_crypt); UNSET_IF_PARAM(oip, auth_type); + UNSET_IF_PARAM(oip, if_area); oip->auth_crypt = list_new(); oip->network_lsa_seqnum = htonl(OSPF_INITIAL_SEQUENCE_NUMBER); + oip->is_v_wait_set = false; return oip; } @@ -577,8 +571,8 @@ void ospf_free_if_params(struct interface *ifp, struct in_addr addr) && !OSPF_IF_PARAM_CONFIGURED(oip, type) && !OSPF_IF_PARAM_CONFIGURED(oip, auth_simple) && !OSPF_IF_PARAM_CONFIGURED(oip, auth_type) - && listcount(oip->auth_crypt) == 0 - && ntohl(oip->network_lsa_seqnum) != OSPF_INITIAL_SEQUENCE_NUMBER) { + && !OSPF_IF_PARAM_CONFIGURED(oip, if_area) + && listcount(oip->auth_crypt) == 0) { ospf_del_if_params(oip); rn->info = NULL; route_unlock_node(rn); @@ -698,7 +692,6 @@ static int ospf_if_delete_hook(struct interface *ifp) ospf_del_if_params((struct ospf_if_params *)IF_DEF_PARAMS(ifp)); XFREE(MTYPE_OSPF_IF_INFO, ifp->info); - ifp->info = NULL; return rc; } @@ -840,12 +833,11 @@ struct ospf_interface *ospf_vl_new(struct ospf *ospf, struct prefix_ipv4 *p; if (IS_DEBUG_OSPF_EVENT) - zlog_debug("ospf_vl_new(): Start"); + zlog_debug("ospf_vl_new()(%s): Start", ospf_get_name(ospf)); if (vlink_count == OSPF_VL_MAX_COUNT) { if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_vl_new(): Alarm: " - "cannot create more than OSPF_MAX_VL_COUNT virtual links"); + "ospf_vl_new(): Alarm: cannot create more than OSPF_MAX_VL_COUNT virtual links"); return NULL; } @@ -855,9 +847,9 @@ struct ospf_interface *ospf_vl_new(struct ospf *ospf, ospf->vrf_id); snprintf(ifname, sizeof(ifname), "VLINK%u", vlink_count); - vi = if_create(ifname, ospf->vrf_id); + vi = if_create_name(ifname, ospf->vrf_id); /* - * if_create sets ZEBRA_INTERFACE_LINKDETECTION + * if_create_name sets ZEBRA_INTERFACE_LINKDETECTION * virtual links don't need this. */ UNSET_FLAG(vi->status, ZEBRA_INTERFACE_LINKDETECTION); @@ -867,7 +859,7 @@ struct ospf_interface *ospf_vl_new(struct ospf *ospf, p = prefix_ipv4_new(); p->family = AF_INET; - p->prefix.s_addr = 0; + p->prefix.s_addr = INADDR_ANY; p->prefixlen = 0; co->address = (struct prefix *)p; @@ -890,7 +882,7 @@ struct ospf_interface *ospf_vl_new(struct ospf *ospf, if (IS_DEBUG_OSPF_EVENT) zlog_debug("ospf_vl_new(): set if->name to %s", vi->name); - area_id.s_addr = 0; + area_id.s_addr = INADDR_ANY; area = ospf_area_get(ospf, area_id); voi->area = area; @@ -903,8 +895,6 @@ struct ospf_interface *ospf_vl_new(struct ospf *ospf, ospf_area_add_if(voi->area, voi); - ospf_if_stream_set(voi); - if (IS_DEBUG_OSPF_EVENT) zlog_debug("ospf_vl_new(): Stop"); return voi; @@ -913,10 +903,11 @@ struct ospf_interface *ospf_vl_new(struct ospf *ospf, static void ospf_vl_if_delete(struct ospf_vl_data *vl_data) { struct interface *ifp = vl_data->vl_oi->ifp; - vl_data->vl_oi->address->u.prefix4.s_addr = 0; + + vl_data->vl_oi->address->u.prefix4.s_addr = INADDR_ANY; vl_data->vl_oi->address->prefixlen = 0; ospf_if_free(vl_data->vl_oi); - if_delete(ifp); + if_delete(&ifp); vlink_count--; } @@ -977,7 +968,7 @@ static void ospf_vl_shutdown(struct ospf_vl_data *vl_data) if ((oi = vl_data->vl_oi) == NULL) return; - oi->address->u.prefix4.s_addr = 0; + oi->address->u.prefix4.s_addr = INADDR_ANY; oi->address->prefixlen = 0; UNSET_FLAG(oi->ifp->flags, IFF_UP); @@ -1002,7 +993,8 @@ void ospf_vl_delete(struct ospf *ospf, struct ospf_vl_data *vl_data) ospf_vl_data_free(vl_data); } -static int ospf_vl_set_params(struct ospf_vl_data *vl_data, struct vertex *v) +static int ospf_vl_set_params(struct ospf_area *area, + struct ospf_vl_data *vl_data, struct vertex *v) { int changed = 0; struct ospf_interface *voi; @@ -1010,6 +1002,7 @@ static int ospf_vl_set_params(struct ospf_vl_data *vl_data, struct vertex *v) struct vertex_parent *vp = NULL; unsigned int i; struct router_lsa *rl; + struct ospf_interface *oi; voi = vl_data->vl_oi; @@ -1020,17 +1013,24 @@ static int ospf_vl_set_params(struct ospf_vl_data *vl_data, struct vertex *v) } for (ALL_LIST_ELEMENTS_RO(v->parents, node, vp)) { - vl_data->nexthop.oi = vp->nexthop->oi; + vl_data->nexthop.lsa_pos = vp->nexthop->lsa_pos; vl_data->nexthop.router = vp->nexthop->router; - if (!IPV4_ADDR_SAME(&voi->address->u.prefix4, - &vl_data->nexthop.oi->address->u.prefix4)) - changed = 1; + /* + * Only deal with interface data when the local + * (calculating) node is the SPF root node + */ + if (!area->spf_dry_run) { + oi = ospf_if_lookup_by_lsa_pos( + area, vl_data->nexthop.lsa_pos); + + if (!IPV4_ADDR_SAME(&voi->address->u.prefix4, + &oi->address->u.prefix4)) + changed = 1; - voi->address->u.prefix4 = - vl_data->nexthop.oi->address->u.prefix4; - voi->address->prefixlen = - vl_data->nexthop.oi->address->prefixlen; + voi->address->u.prefix4 = oi->address->u.prefix4; + voi->address->prefixlen = oi->address->prefixlen; + } break; /* We take the first interface. */ } @@ -1121,11 +1121,10 @@ void ospf_vl_up_check(struct ospf_area *area, struct in_addr rid, OSPF_ISM_EVENT_EXECUTE(oi, ISM_InterfaceUp); } - if (ospf_vl_set_params(vl_data, v)) { + if (ospf_vl_set_params(area, vl_data, v)) { if (IS_DEBUG_OSPF(ism, ISM_EVENTS)) zlog_debug( - "ospf_vl_up_check: VL cost change," - " scheduling router lsa refresh"); + "ospf_vl_up_check: VL cost change, scheduling router lsa refresh"); if (ospf->backbone) ospf_router_lsa_update_area( ospf->backbone); @@ -1230,8 +1229,155 @@ uint8_t ospf_default_iftype(struct interface *ifp) return OSPF_IFTYPE_BROADCAST; } +void ospf_if_interface(struct interface *ifp) +{ + hook_call(ospf_if_update, ifp); +} + +uint32_t ospf_if_count_area_params(struct interface *ifp) +{ + struct ospf_if_params *params; + struct route_node *rn; + uint32_t count = 0; + + params = IF_DEF_PARAMS(ifp); + if (OSPF_IF_PARAM_CONFIGURED(params, if_area)) + count++; + + for (rn = route_top(IF_OIFS_PARAMS(ifp)); rn; rn = route_next(rn)) + if ((params = rn->info) + && OSPF_IF_PARAM_CONFIGURED(params, if_area)) + count++; + + return count; +} + +static int ospf_ifp_create(struct interface *ifp) +{ + struct ospf *ospf = NULL; + struct ospf_if_info *oii; + + if (IS_DEBUG_OSPF(zebra, ZEBRA_INTERFACE)) + zlog_debug( + "Zebra: interface add %s vrf %s[%u] index %d flags %llx metric %d mtu %d speed %u", + ifp->name, ospf_vrf_id_to_name(ifp->vrf_id), + ifp->vrf_id, ifp->ifindex, + (unsigned long long)ifp->flags, ifp->metric, ifp->mtu, + ifp->speed); + + assert(ifp->info); + + oii = ifp->info; + oii->curr_mtu = ifp->mtu; + + if (IF_DEF_PARAMS(ifp) + && !OSPF_IF_PARAM_CONFIGURED(IF_DEF_PARAMS(ifp), type)) { + SET_IF_PARAM(IF_DEF_PARAMS(ifp), type); + IF_DEF_PARAMS(ifp)->type = ospf_default_iftype(ifp); + } + + ospf = ospf_lookup_by_vrf_id(ifp->vrf_id); + if (!ospf) + return 0; + + if (ospf_if_count_area_params(ifp) > 0) + ospf_interface_area_set(ospf, ifp); + + ospf_if_recalculate_output_cost(ifp); + + ospf_if_update(ospf, ifp); + + hook_call(ospf_if_update, ifp); + + return 0; +} + +static int ospf_ifp_up(struct interface *ifp) +{ + struct ospf_interface *oi; + struct route_node *rn; + struct ospf_if_info *oii = ifp->info; + + ospf_if_recalculate_output_cost(ifp); + + if (oii && oii->curr_mtu != ifp->mtu) { + if (IS_DEBUG_OSPF(zebra, ZEBRA_INTERFACE)) + zlog_debug( + "Zebra: Interface[%s] MTU change %u -> %u.", + ifp->name, oii->curr_mtu, ifp->mtu); + + oii->curr_mtu = ifp->mtu; + /* Must reset the interface (simulate down/up) when MTU + * changes. */ + ospf_if_reset(ifp); + + return 0; + } + + if (IS_DEBUG_OSPF(zebra, ZEBRA_INTERFACE)) + zlog_debug("Zebra: Interface[%s] state change to up.", + ifp->name); + + for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next(rn)) { + if ((oi = rn->info) == NULL) + continue; + + ospf_if_up(oi); + } + + return 0; +} + +static int ospf_ifp_down(struct interface *ifp) +{ + struct ospf_interface *oi; + struct route_node *node; + + if (IS_DEBUG_OSPF(zebra, ZEBRA_INTERFACE)) + zlog_debug("Zebra: Interface[%s] state change to down.", + ifp->name); + + for (node = route_top(IF_OIFS(ifp)); node; node = route_next(node)) { + if ((oi = node->info) == NULL) + continue; + ospf_if_down(oi); + } + + return 0; +} + +static int ospf_ifp_destroy(struct interface *ifp) +{ + struct ospf *ospf; + struct route_node *rn; + + if (IS_DEBUG_OSPF(zebra, ZEBRA_INTERFACE)) + zlog_debug( + "Zebra: interface delete %s vrf %s[%u] index %d flags %llx metric %d mtu %d", + ifp->name, ospf_vrf_id_to_name(ifp->vrf_id), + ifp->vrf_id, ifp->ifindex, + (unsigned long long)ifp->flags, ifp->metric, ifp->mtu); + + hook_call(ospf_if_delete, ifp); + + ospf = ospf_lookup_by_vrf_id(ifp->vrf_id); + if (ospf) { + if (ospf_if_count_area_params(ifp) > 0) + ospf_interface_area_unset(ospf, ifp); + } + + for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next(rn)) + if (rn->info) + ospf_if_free((struct ospf_interface *)rn->info); + + return 0; +} + void ospf_if_init(void) { + if_zapi_callbacks(ospf_ifp_create, ospf_ifp_up, + ospf_ifp_down, ospf_ifp_destroy); + /* Initialize Zebra interface data structure. */ hook_register_prio(if_add, 0, ospf_if_new_hook); hook_register_prio(if_del, 0, ospf_if_delete_hook); diff --git a/ospfd/ospf_interface.h b/ospfd/ospf_interface.h index b88d405875..c8adb5f125 100644 --- a/ospfd/ospf_interface.h +++ b/ospfd/ospf_interface.h @@ -84,6 +84,7 @@ struct ospf_if_params { DECLARE_IF_PARAM(uint32_t, v_hello); /* Hello Interval */ DECLARE_IF_PARAM(uint32_t, v_wait); /* Router Dead Interval */ + bool is_v_wait_set; /* Check for Dead Interval set */ /* MTU mismatch check (see RFC2328, chap 10.6) */ DECLARE_IF_PARAM(uint8_t, mtu_ignore); @@ -117,6 +118,8 @@ struct ospf_if_info { struct route_table *oifs; unsigned int membership_counts[MEMBER_MAX]; /* multicast group refcnts */ + + uint32_t curr_mtu; }; struct ospf_interface; @@ -285,7 +288,6 @@ extern void ospf_if_update_params(struct interface *, struct in_addr); extern int ospf_if_new_hook(struct interface *); extern void ospf_if_init(void); -extern void ospf_if_stream_set(struct ospf_interface *); extern void ospf_if_stream_unset(struct ospf_interface *); extern void ospf_if_reset_variables(struct ospf_interface *); extern int ospf_if_is_enable(struct ospf_interface *); @@ -322,7 +324,14 @@ extern int ospf_interface_neighbor_count(struct ospf_interface *oi); state of the interface. */ extern void ospf_if_set_multicast(struct ospf_interface *); +extern void ospf_if_interface(struct interface *ifp); + +extern uint32_t ospf_if_count_area_params(struct interface *ifp); + DECLARE_HOOK(ospf_vl_add, (struct ospf_vl_data * vd), (vd)) DECLARE_HOOK(ospf_vl_delete, (struct ospf_vl_data * vd), (vd)) +DECLARE_HOOK(ospf_if_update, (struct interface * ifp), (ifp)) +DECLARE_HOOK(ospf_if_delete, (struct interface * ifp), (ifp)) + #endif /* _ZEBRA_OSPF_INTERFACE_H */ diff --git a/ospfd/ospf_ism.c b/ospfd/ospf_ism.c index 419081fe59..86712c6198 100644 --- a/ospfd/ospf_ism.c +++ b/ospfd/ospf_ism.c @@ -169,7 +169,7 @@ static void ospf_dr_eligible_routers(struct route_table *nbrs, for (rn = route_top(nbrs); rn; rn = route_next(rn)) if ((nbr = rn->info) != NULL) /* Ignore 0.0.0.0 node*/ - if (nbr->router_id.s_addr != 0) + if (nbr->router_id.s_addr != INADDR_ANY) /* Is neighbor eligible? */ if (nbr->priority > 0) /* Is neighbor upper 2-Way? */ @@ -183,17 +183,22 @@ static void ospf_dr_change(struct ospf *ospf, struct route_table *nbrs) struct route_node *rn; struct ospf_neighbor *nbr; - for (rn = route_top(nbrs); rn; rn = route_next(rn)) - if ((nbr = rn->info) != NULL) - /* Ignore 0.0.0.0 node*/ - if (nbr->router_id.s_addr != 0) - /* Is neighbor upper 2-Way? */ - if (nbr->state >= NSM_TwoWay) - /* Ignore myself. */ - if (!IPV4_ADDR_SAME(&nbr->router_id, - &ospf->router_id)) - OSPF_NSM_EVENT_SCHEDULE( - nbr, NSM_AdjOK); + for (rn = route_top(nbrs); rn; rn = route_next(rn)) { + nbr = rn->info; + + if (!nbr) + continue; + + /* + * Ignore 0.0.0.0 node + * Is neighbor 2-Way? + * Ignore myself + */ + if (nbr->router_id.s_addr != INADDR_ANY + && nbr->state >= NSM_TwoWay + && !IPV4_ADDR_SAME(&nbr->router_id, &ospf->router_id)) + OSPF_NSM_EVENT_SCHEDULE(nbr, NSM_AdjOK); + } } static int ospf_dr_election(struct ospf_interface *oi) @@ -418,7 +423,7 @@ static int ism_ignore(struct ospf_interface *oi) } /* Interface State Machine */ -struct { +const struct { int (*func)(struct ospf_interface *); int next_state; } ISM[OSPF_ISM_STATE_MAX][OSPF_ISM_EVENT_MAX] = { @@ -512,7 +517,7 @@ struct { }, }; -static const char *ospf_ism_event_str[] = { +static const char *const ospf_ism_event_str[] = { "NoEvent", "InterfaceUp", "WaitTimer", "BackupSeen", "NeighborChange", "LoopInd", "UnLoopInd", "InterfaceDown", }; diff --git a/ospfd/ospf_ism.h b/ospfd/ospf_ism.h index 5ae99ab320..8d21403695 100644 --- a/ospfd/ospf_ism.h +++ b/ospfd/ospf_ism.h @@ -53,8 +53,9 @@ listnode_add((O)->oi_write_q, oi); \ oi->on_write_q = 1; \ } \ - thread_add_write(master, ospf_write, (O), (O)->fd, \ - &(O)->t_write); \ + if (!list_isempty((O)->oi_write_q)) \ + thread_add_write(master, ospf_write, (O), (O)->fd, \ + &(O)->t_write); \ } while (0) /* Macro for OSPF ISM timer turn on. */ diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c index bf46d22031..6c9a36ad5a 100644 --- a/ospfd/ospf_lsa.c +++ b/ospfd/ospf_lsa.c @@ -33,6 +33,7 @@ #include "hash.h" #include "sockunion.h" /* for inet_aton() */ #include "checksum.h" +#include "network.h" #include "ospfd/ospfd.h" #include "ospfd/ospf_interface.h" @@ -123,7 +124,7 @@ int get_age(struct ospf_lsa *lsa) one-based. */ uint16_t ospf_lsa_checksum(struct lsa_header *lsa) { - uint8_t *buffer = (uint8_t *)&lsa->options; + uint8_t *buffer = &lsa->options; int options_offset = buffer - (uint8_t *)&lsa->ls_age; /* should be 2 */ /* Skip the AGE field */ @@ -138,7 +139,7 @@ uint16_t ospf_lsa_checksum(struct lsa_header *lsa) int ospf_lsa_checksum_valid(struct lsa_header *lsa) { - uint8_t *buffer = (uint8_t *)&lsa->options; + uint8_t *buffer = &lsa->options; int options_offset = buffer - (uint8_t *)&lsa->ls_age; /* should be 2 */ /* Skip the AGE field */ @@ -290,7 +291,7 @@ void ospf_lsa_data_free(struct lsa_header *lsah) const char *dump_lsa_key(struct ospf_lsa *lsa) { - static char buf[] = {"Type255,id(255.255.255.255),ar(255.255.255.255)"}; + static char buf[sizeof("Type255,id(255.255.255.255),ar(255.255.255.255)")+1]; struct lsa_header *lsah; if (lsa != NULL && (lsah = lsa->data) != NULL) { @@ -298,7 +299,8 @@ const char *dump_lsa_key(struct ospf_lsa *lsa) strlcpy(id, inet_ntoa(lsah->id), sizeof(id)); strlcpy(ar, inet_ntoa(lsah->adv_router), sizeof(ar)); - sprintf(buf, "Type%d,id(%s),ar(%s)", lsah->type, id, ar); + snprintf(buf, sizeof(buf), "Type%d,id(%s),ar(%s)", lsah->type, + id, ar); } else strlcpy(buf, "NULL", sizeof(buf)); @@ -434,7 +436,7 @@ static char link_info_set(struct stream **s, struct in_addr id, /* we futz the size here for simplicity, really we need * to account * for just: - * IP Header - (sizeof (struct ip)) + * IP Header - (sizeof(struct ip)) * OSPF Header - OSPF_HEADER_SIZE * LSA Header - OSPF_LSA_HEADER_SIZE * MD5 auth data, if MD5 is configured - @@ -523,8 +525,7 @@ static int lsa_link_broadcast_set(struct stream **s, struct ospf_interface *oi) if (oi->state == ISM_Waiting) { if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) zlog_debug( - "LSA[Type1]: Interface %s is in state Waiting. " - "Adding stub interface", + "LSA[Type1]: Interface %s is in state Waiting. Adding stub interface", oi->ifp->name); masklen2ip(oi->address->prefixlen, &mask); id.s_addr = oi->address->u.prefix4.s_addr & mask.s_addr; @@ -539,8 +540,7 @@ static int lsa_link_broadcast_set(struct stream **s, struct ospf_interface *oi) && ospf_nbr_count(oi, NSM_Full) > 0) { if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) zlog_debug( - "LSA[Type1]: Interface %s has a DR. " - "Adding transit interface", + "LSA[Type1]: Interface %s has a DR. Adding transit interface", oi->ifp->name); return link_info_set(s, DR(oi), oi->address->u.prefix4, LSA_LINK_TYPE_TRANSIT, 0, cost); @@ -549,8 +549,7 @@ static int lsa_link_broadcast_set(struct stream **s, struct ospf_interface *oi) else { if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) zlog_debug( - "LSA[Type1]: Interface %s has no DR. " - "Adding stub interface", + "LSA[Type1]: Interface %s has no DR. Adding stub interface", oi->ifp->name); masklen2ip(oi->address->prefixlen, &mask); id.s_addr = oi->address->u.prefix4.s_addr & mask.s_addr; @@ -820,7 +819,7 @@ static struct ospf_lsa *ospf_router_lsa_originate(struct ospf_area *area) } /* Sanity check. */ - if (new->data->adv_router.s_addr == 0) { + if (new->data->adv_router.s_addr == INADDR_ANY) { if (IS_DEBUG_OSPF_EVENT) zlog_debug("LSA[Type1]: AdvRouter is 0, discard"); ospf_lsa_discard(new); @@ -1082,8 +1081,7 @@ static struct ospf_lsa *ospf_network_lsa_refresh(struct ospf_lsa *lsa) if (oi == NULL) { if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) { zlog_debug( - "LSA[Type%d:%s]: network-LSA refresh: " - "no oi found, ick, ignoring.", + "LSA[Type%d:%s]: network-LSA refresh: no oi found, ick, ignoring.", lsa->data->type, inet_ntoa(lsa->data->id)); ospf_lsa_header_dump(lsa->data); } @@ -1459,7 +1457,7 @@ struct in_addr ospf_get_ip_from_ifp(struct ospf_interface *oi) { struct in_addr fwd; - fwd.s_addr = 0; + fwd.s_addr = INADDR_ANY; if (if_is_operative(oi->ifp)) return oi->address->u.prefix4; @@ -1759,8 +1757,7 @@ static struct ospf_lsa *ospf_lsa_translated_nssa_new(struct ospf *ospf, == NULL) { if (IS_DEBUG_OSPF_NSSA) zlog_debug( - "ospf_nssa_translate_originate(): Could not originate " - "Translated Type-5 for %s", + "ospf_nssa_translate_originate(): Could not originate Translated Type-5 for %s", inet_ntoa(ei.p.prefix)); return NULL; } @@ -1794,33 +1791,30 @@ struct ospf_lsa *ospf_translated_nssa_originate(struct ospf *ospf, if ((new = ospf_lsa_translated_nssa_new(ospf, type7)) == NULL) { if (IS_DEBUG_OSPF_NSSA) zlog_debug( - "ospf_translated_nssa_originate(): Could not translate " - "Type-7, Id %s, to Type-5", + "ospf_translated_nssa_originate(): Could not translate Type-7, Id %s, to Type-5", inet_ntoa(type7->data->id)); return NULL; } - extnew = (struct as_external_lsa *)new; + extnew = (struct as_external_lsa *)new->data; + + if ((new = ospf_lsa_install(ospf, NULL, new)) == NULL) { + flog_warn( + EC_OSPF_LSA_INSTALL_FAILURE, + "ospf_lsa_translated_nssa_originate(): Could not install LSA id %s", + inet_ntoa(type7->data->id)); + return NULL; + } if (IS_DEBUG_OSPF_NSSA) { zlog_debug( - "ospf_translated_nssa_originate(): " - "translated Type 7, installed:"); + "ospf_translated_nssa_originate(): translated Type 7, installed:"); ospf_lsa_header_dump(new->data); zlog_debug(" Network mask: %d", ip_masklen(extnew->mask)); zlog_debug(" Forward addr: %s", inet_ntoa(extnew->e[0].fwd_addr)); } - if ((new = ospf_lsa_install(ospf, NULL, new)) == NULL) { - flog_warn(EC_OSPF_LSA_INSTALL_FAILURE, - "ospf_lsa_translated_nssa_originate(): " - "Could not install LSA " - "id %s", - inet_ntoa(type7->data->id)); - return NULL; - } - ospf->lsa_originate_count++; ospf_flood_through_as(ospf, NULL, new); @@ -1883,8 +1877,7 @@ struct ospf_lsa *ospf_translated_nssa_refresh(struct ospf *ospf, if (!type7) { if (IS_DEBUG_OSPF_NSSA) zlog_debug( - "ospf_translated_nssa_refresh(): no Type-7 found for " - "Type-5 LSA Id %s", + "ospf_translated_nssa_refresh(): no Type-7 found for Type-5 LSA Id %s", inet_ntoa(type5->data->id)); return NULL; } @@ -1893,8 +1886,7 @@ struct ospf_lsa *ospf_translated_nssa_refresh(struct ospf *ospf, if (type5 == NULL || !CHECK_FLAG(type5->flags, OSPF_LSA_LOCAL_XLT)) { if (IS_DEBUG_OSPF_NSSA) zlog_debug( - "ospf_translated_nssa_refresh(): No translated Type-5 " - "found for Type-7 with Id %s", + "ospf_translated_nssa_refresh(): No translated Type-5 found for Type-7 with Id %s", inet_ntoa(type7->data->id)); return NULL; } @@ -1906,8 +1898,7 @@ struct ospf_lsa *ospf_translated_nssa_refresh(struct ospf *ospf, if ((new = ospf_lsa_translated_nssa_new(ospf, type7)) == NULL) { if (IS_DEBUG_OSPF_NSSA) zlog_debug( - "ospf_translated_nssa_refresh(): Could not translate " - "Type-7 for %s to Type-5", + "ospf_translated_nssa_refresh(): Could not translate Type-7 for %s to Type-5", inet_ntoa(type7->data->id)); return NULL; } @@ -1931,7 +1922,7 @@ int is_prefix_default(struct prefix_ipv4 *p) struct prefix_ipv4 q; q.family = AF_INET; - q.prefix.s_addr = 0; + q.prefix.s_addr = INADDR_ANY; q.prefixlen = 0; return prefix_same((struct prefix *)p, (struct prefix *)&q); @@ -1979,6 +1970,14 @@ struct ospf_lsa *ospf_external_lsa_originate(struct ospf *ospf, */ + if (ospf->router_id.s_addr == INADDR_ANY) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "LSA[Type5:%pI4]: deferring AS-external-LSA origination, router ID is zero", + &ei->p.prefix); + return NULL; + } + /* Check the AS-external-LSA should be originated. */ if (!ospf_redistribute_check(ospf, ei, NULL)) return NULL; @@ -2019,65 +2018,25 @@ struct ospf_lsa *ospf_external_lsa_originate(struct ospf *ospf, return new; } -/* Originate AS-external-LSA from external info with initial flag. */ -int ospf_external_lsa_originate_timer(struct thread *thread) -{ - struct ospf *ospf = THREAD_ARG(thread); - struct route_node *rn; - struct external_info *ei; - struct route_table *rt; - int type = THREAD_VAL(thread); - struct list *ext_list; - struct listnode *node; - struct ospf_external *ext; - - ospf->t_external_lsa = NULL; - - ext_list = ospf->external[type]; - if (!ext_list) - return 0; - - for (ALL_LIST_ELEMENTS_RO(ext_list, node, ext)) { - /* Originate As-external-LSA from all type of distribute source. - */ - rt = ext->external_info; - if (!rt) - continue; - - for (rn = route_top(rt); rn; rn = route_next(rn)) { - ei = rn->info; - - if (!ei) - continue; - - if (is_prefix_default((struct prefix_ipv4 *)&ei->p)) - continue; - - if (!ospf_external_lsa_originate(ospf, ei)) - flog_warn( - EC_OSPF_LSA_INSTALL_FAILURE, - "LSA: AS-external-LSA was not originated."); - } - } - - return 0; -} - static struct external_info *ospf_default_external_info(struct ospf *ospf) { int type; - struct route_node *rn; struct prefix_ipv4 p; + struct external_info *default_ei; + int ret = 0; p.family = AF_INET; p.prefix.s_addr = 0; p.prefixlen = 0; + default_ei = ospf_external_info_lookup(ospf, DEFAULT_ROUTE, + ospf->instance, &p); + if (!default_ei) + return NULL; + /* First, lookup redistributed default route. */ for (type = 0; type <= ZEBRA_ROUTE_MAX; type++) { struct list *ext_list; - struct listnode *node; - struct ospf_external *ext; if (type == ZEBRA_ROUTE_OSPF) continue; @@ -2086,47 +2045,61 @@ static struct external_info *ospf_default_external_info(struct ospf *ospf) if (!ext_list) continue; - for (ALL_LIST_ELEMENTS_RO(ext_list, node, ext)) { - rn = route_node_lookup(ext->external_info, - (struct prefix *)&p); - if (rn != NULL) { - route_unlock_node(rn); - assert(rn->info); - if (ospf_redistribute_check(ospf, rn->info, - NULL)) - return rn->info; - } - } + ret = ospf_external_default_routemap_apply_walk(ospf, ext_list, + default_ei); + if (ret) + return default_ei; } return NULL; } -int ospf_default_originate_timer(struct thread *thread) +void ospf_external_lsa_rid_change(struct ospf *ospf) { - struct prefix_ipv4 p; - struct in_addr nexthop; struct external_info *ei; - struct ospf *ospf; + int type; - ospf = THREAD_ARG(thread); + for (type = 0; type < ZEBRA_ROUTE_MAX; type++) { + struct route_node *rn; + struct route_table *rt; + struct list *ext_list; + struct listnode *node; + struct ospf_external *ext; - p.family = AF_INET; - p.prefix.s_addr = 0; - p.prefixlen = 0; + ext_list = ospf->external[type]; + if (!ext_list) + continue; - if (ospf->default_originate == DEFAULT_ORIGINATE_ALWAYS) { - /* If there is no default route via redistribute, - then originate AS-external-LSA with nexthop 0 (self). */ - nexthop.s_addr = 0; - ospf_external_info_add(ospf, DEFAULT_ROUTE, 0, p, 0, nexthop, - 0); - } + for (ALL_LIST_ELEMENTS_RO(ext_list, node, ext)) { + /* Originate As-external-LSA from all type of + * distribute source. + */ + rt = ext->external_info; + if (!rt) + continue; - if ((ei = ospf_default_external_info(ospf))) - ospf_external_lsa_originate(ospf, ei); + for (rn = route_top(rt); rn; rn = route_next(rn)) { + ei = rn->info; - return 0; + if (!ei) + continue; + + if (is_prefix_default( + (struct prefix_ipv4 *)&ei->p)) + continue; + + if (!ospf_external_lsa_originate(ospf, ei)) + flog_warn(EC_OSPF_LSA_INSTALL_FAILURE, + "LSA: AS-external-LSA was not originated."); + } + } + } + + ei = ospf_default_external_info(ospf); + if (ei && !ospf_external_lsa_originate(ospf, ei)) { + flog_warn(EC_OSPF_LSA_INSTALL_FAILURE, + "LSA: AS-external-LSA for default route was not originated."); + } } /* Flush any NSSA LSAs for given prefix */ @@ -2213,49 +2186,25 @@ void ospf_external_lsa_refresh_default(struct ospf *ospf) p.family = AF_INET; p.prefixlen = 0; - p.prefix.s_addr = 0; + p.prefix.s_addr = INADDR_ANY; ei = ospf_default_external_info(ospf); lsa = ospf_external_info_find_lsa(ospf, &p); - if (ei) { - if (lsa) { - if (IS_DEBUG_OSPF_EVENT) - zlog_debug( - "LSA[Type5:0.0.0.0]: Refresh AS-external-LSA %p", - (void *)lsa); - ospf_external_lsa_refresh(ospf, lsa, ei, - LSA_REFRESH_FORCE); - } else { - if (IS_DEBUG_OSPF_EVENT) - zlog_debug( - "LSA[Type5:0.0.0.0]: Originate AS-external-LSA"); - ospf_external_lsa_originate(ospf, ei); - } - } else { - if (lsa) { - if (IS_DEBUG_OSPF_EVENT) - zlog_debug( - "LSA[Type5:0.0.0.0]: Flush AS-external-LSA"); - ospf_refresher_unregister_lsa(ospf, lsa); - ospf_lsa_flush_as(ospf, lsa); - } - } -} - -void ospf_default_originate_lsa_update(struct ospf *ospf) -{ - struct prefix_ipv4 p; - struct ospf_lsa *lsa; - - p.family = AF_INET; - p.prefixlen = 0; - p.prefix.s_addr = 0; - - lsa = ospf_external_info_find_lsa(ospf, &p); - if (lsa && IS_LSA_MAXAGE(lsa)) { - ospf_discard_from_db(ospf, lsa->lsdb, lsa); - ospf_lsdb_delete(lsa->lsdb, lsa); + if (ei && lsa) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("LSA[Type5:0.0.0.0]: Refresh AS-external-LSA %p", + (void *)lsa); + ospf_external_lsa_refresh(ospf, lsa, ei, LSA_REFRESH_FORCE); + } else if (ei && !lsa) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "LSA[Type5:0.0.0.0]: Originate AS-external-LSA"); + ospf_external_lsa_originate(ospf, ei); + } else if (lsa) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("LSA[Type5:0.0.0.0]: Flush AS-external-LSA"); + ospf_external_lsa_flush(ospf, DEFAULT_ROUTE, &p, 0); } } @@ -2306,8 +2255,7 @@ struct ospf_lsa *ospf_external_lsa_refresh(struct ospf *ospf, if (!ospf_redistribute_check(ospf, ei, &changed)) { if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) zlog_debug( - "LSA[Type%d:%s]: Could not be refreshed, " - "redist check fail", + "LSA[Type%d:%s]: Could not be refreshed, redist check fail", lsa->data->type, inet_ntoa(lsa->data->id)); ospf_external_lsa_flush(ospf, ei->type, &ei->p, ei->ifindex /*, ei->nexthop */); @@ -2477,7 +2425,7 @@ static struct ospf_lsa *ospf_summary_asbr_lsa_install(struct ospf *ospf, #if 0 /* These don't exist yet... */ ospf_summary_incremental_update(new); - /* Isn't this done by the above call? + /* Isn't this done by the above call? - RFC 2328 Section 16.5 implies it should be */ /* ospf_ase_calculate_schedule(); */ #else /* #if 0 */ @@ -2653,8 +2601,7 @@ struct ospf_lsa *ospf_lsa_install(struct ospf *ospf, struct ospf_interface *oi, if (IS_DEBUG_OSPF(lsa, LSA_REFRESH)) { zlog_debug( - "ospf_lsa_install() Premature Aging " - "lsa 0x%p, seqnum 0x%x", + "ospf_lsa_install() Premature Aging lsa 0x%p, seqnum 0x%x", (void *)lsa, ntohl(lsa->data->ls_seqnum)); ospf_lsa_header_dump(lsa->data); @@ -2662,8 +2609,7 @@ struct ospf_lsa *ospf_lsa_install(struct ospf *ospf, struct ospf_interface *oi, } else { if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) { zlog_debug( - "ospf_lsa_install() got an lsa with seq 0x80000000 " - "that was not self originated. Ignoring\n"); + "ospf_lsa_install() got an lsa with seq 0x80000000 that was not self originated. Ignoring\n"); ospf_lsa_header_dump(lsa->data); } return old; @@ -2844,7 +2790,7 @@ static int ospf_maxage_lsa_remover(struct thread *thread) if (CHECK_FLAG(lsa->flags, OSPF_LSA_PREMATURE_AGE)) { if (IS_DEBUG_OSPF(lsa, LSA_FLOODING)) zlog_debug( - "originating new lsa for lsa 0x%p\n", + "originating new lsa for lsa 0x%p", (void *)lsa); ospf_lsa_refresh(ospf, lsa); } @@ -2884,8 +2830,7 @@ void ospf_lsa_maxage_delete(struct ospf *ospf, struct ospf_lsa *lsa) lsa_prefix.prefixlen = sizeof(lsa_prefix.u.ptr) * CHAR_BIT; lsa_prefix.u.ptr = (uintptr_t)lsa; - if ((rn = route_node_lookup(ospf->maxage_lsa, - (struct prefix *)&lsa_prefix))) { + if ((rn = route_node_lookup(ospf->maxage_lsa, &lsa_prefix))) { if (rn->info == lsa) { UNSET_FLAG(lsa->flags, OSPF_LSA_IN_MAXAGE); ospf_lsa_unlock(&lsa); /* maxage_lsa */ @@ -2897,7 +2842,7 @@ void ospf_lsa_maxage_delete(struct ospf *ospf, struct ospf_lsa *lsa) } else { if (IS_DEBUG_OSPF_EVENT) zlog_debug("%s: lsa %s is not found in maxage db.", - __PRETTY_FUNCTION__, dump_lsa_key(lsa)); + __func__, dump_lsa_key(lsa)); } } @@ -2927,7 +2872,7 @@ void ospf_lsa_maxage(struct ospf *ospf, struct ospf_lsa *lsa) lsa_prefix.prefixlen = sizeof(lsa_prefix.u.ptr) * CHAR_BIT; lsa_prefix.u.ptr = (uintptr_t)lsa; - rn = route_node_get(ospf->maxage_lsa, (struct prefix *)&lsa_prefix); + rn = route_node_get(ospf->maxage_lsa, &lsa_prefix); if (rn->info != NULL) { if (IS_DEBUG_OSPF(lsa, LSA_FLOODING)) zlog_debug( @@ -3242,45 +3187,6 @@ int ospf_lsa_different(struct ospf_lsa *l1, struct ospf_lsa *l2) return 0; } -#ifdef ORIGINAL_CODING -void ospf_lsa_flush_self_originated(struct ospf_neighbor *nbr, - struct ospf_lsa *self, struct ospf_lsa *new) -{ - uint32_t seqnum; - - /* Adjust LS Sequence Number. */ - seqnum = ntohl(new->data->ls_seqnum) + 1; - self->data->ls_seqnum = htonl(seqnum); - - /* Recalculate LSA checksum. */ - ospf_lsa_checksum(self->data); - - /* Reflooding LSA. */ - /* RFC2328 Section 13.3 - On non-broadcast networks, separate Link State Update - packets must be sent, as unicasts, to each adjacent neighbor - (i.e., those in state Exchange or greater). The destination - IP addresses for these packets are the neighbors' IP - addresses. */ - if (nbr->oi->type == OSPF_IFTYPE_NBMA) { - struct route_node *rn; - struct ospf_neighbor *onbr; - - for (rn = route_top(nbr->oi->nbrs); rn; rn = route_next(rn)) - if ((onbr = rn->info) != NULL) - if (onbr != nbr->oi->nbr_self - && onbr->status >= NSM_Exchange) - ospf_ls_upd_send_lsa( - onbr, self, - OSPF_SEND_PACKET_DIRECT); - } else - ospf_ls_upd_send_lsa(nbr, self, OSPF_SEND_PACKET_INDIRECT); - - if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) - zlog_debug("LSA[Type%d:%s]: Flush self-originated LSA", - self->data->type, inet_ntoa(self->data->id)); -} -#else /* ORIGINAL_CODING */ int ospf_lsa_flush_schedule(struct ospf *ospf, struct ospf_lsa *lsa) { if (lsa == NULL || !IS_LSA_SELF(lsa)) @@ -3385,7 +3291,6 @@ void ospf_flush_self_originated_lsas_now(struct ospf *ospf) return; } -#endif /* ORIGINAL_CODING */ /* If there is self-originated LSA, then return 1, otherwise return 0. */ /* An interface-independent version of ospf_lsa_is_self_originated */ @@ -3446,8 +3351,7 @@ struct in_addr ospf_lsa_unique_id(struct ospf *ospf, struct ospf_lsdb *lsdb, if (ip_masklen(al->mask) == p->prefixlen) { if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) zlog_debug( - "ospf_lsa_unique_id(): " - "Can't get Link State ID for %s/%d", + "ospf_lsa_unique_id(): Can't get Link State ID for %s/%d", inet_ntoa(p->prefix), p->prefixlen); /* id.s_addr = 0; */ id.s_addr = 0xffffffff; @@ -3464,8 +3368,7 @@ struct in_addr ospf_lsa_unique_id(struct ospf *ospf, struct ospf_lsdb *lsdb, if (lsa) { if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) zlog_debug( - "ospf_lsa_unique_id(): " - "Can't get Link State ID for %s/%d", + "ospf_lsa_unique_id(): Can't get Link State ID for %s/%d", inet_ntoa(p->prefix), p->prefixlen); /* id.s_addr = 0; */ @@ -3603,7 +3506,8 @@ void ospf_refresher_register_lsa(struct ospf *ospf, struct ospf_lsa *lsa) * 1680s * and 1740s. */ - delay = (random() % (max_delay - min_delay)) + min_delay; + delay = (frr_weak_random() % (max_delay - min_delay)) + + min_delay; current_index = ospf->lsa_refresh_queue.index + (monotime(NULL) - ospf->lsa_refresher_started) @@ -3627,8 +3531,7 @@ void ospf_refresher_register_lsa(struct ospf *ospf, struct ospf_lsa *lsa) if (IS_DEBUG_OSPF(lsa, LSA_REFRESH)) zlog_debug( - "LSA[Refresh:Type%d:%s]: ospf_refresher_register_lsa(): " - "setting refresh_list on lsa %p (slod %d)", + "LSA[Refresh:Type%d:%s]: ospf_refresher_register_lsa(): setting refresh_list on lsa %p (slod %d)", lsa->data->type, inet_ntoa(lsa->data->id), (void *)lsa, index); } @@ -3686,8 +3589,7 @@ int ospf_lsa_refresh_walker(struct thread *t) i = (i + 1) % OSPF_LSA_REFRESHER_SLOTS) { if (IS_DEBUG_OSPF(lsa, LSA_REFRESH)) zlog_debug( - "LSA[Refresh]: ospf_lsa_refresh_walker(): " - "refresh index %d", + "LSA[Refresh]: ospf_lsa_refresh_walker(): refresh index %d", i); refresh_list = ospf->lsa_refresh_queue.qs[i]; @@ -3701,8 +3603,7 @@ int ospf_lsa_refresh_walker(struct thread *t) lsa)) { if (IS_DEBUG_OSPF(lsa, LSA_REFRESH)) zlog_debug( - "LSA[Refresh:Type%d:%s]: ospf_lsa_refresh_walker(): " - "refresh lsa %p (slot %d)", + "LSA[Refresh:Type%d:%s]: ospf_lsa_refresh_walker(): refresh lsa %p (slot %d)", lsa->data->type, inet_ntoa(lsa->data->id), (void *)lsa, i); @@ -3735,3 +3636,34 @@ int ospf_lsa_refresh_walker(struct thread *t) return 0; } + +/* Flush the LSAs for the specific area */ +void ospf_flush_lsa_from_area(struct ospf *ospf, struct in_addr area_id, + int type) +{ + struct ospf_area *area; + struct route_node *rn; + struct ospf_lsa *lsa; + + area = ospf_area_get(ospf, area_id); + + switch (type) { + case OSPF_AS_EXTERNAL_LSA: + if ((area->external_routing == OSPF_AREA_NSSA) || + (area->external_routing == OSPF_AREA_STUB)) { + LSDB_LOOP (EXTERNAL_LSDB(ospf), rn, lsa) + if (IS_LSA_SELF(lsa) && + !(CHECK_FLAG(lsa->flags, + OSPF_LSA_LOCAL_XLT))) + ospf_lsa_flush_area(lsa, area); + } + break; + case OSPF_AS_NSSA_LSA: + LSDB_LOOP (NSSA_LSDB(area), rn, lsa) + if (IS_LSA_SELF(lsa)) + ospf_lsa_flush_area(lsa, area); + break; + default: + break; + } +} diff --git a/ospfd/ospf_lsa.h b/ospfd/ospf_lsa.h index 5e3dabc27a..64d6617c37 100644 --- a/ospfd/ospf_lsa.h +++ b/ospfd/ospf_lsa.h @@ -275,8 +275,7 @@ extern struct in_addr ospf_get_ip_from_ifp(struct ospf_interface *); extern struct ospf_lsa *ospf_external_lsa_originate(struct ospf *, struct external_info *); -extern int ospf_external_lsa_originate_timer(struct thread *); -extern int ospf_default_originate_timer(struct thread *); +extern void ospf_external_lsa_rid_change(struct ospf *ospf); extern struct ospf_lsa *ospf_lsa_lookup(struct ospf *ospf, struct ospf_area *, uint32_t, struct in_addr, struct in_addr); @@ -301,7 +300,6 @@ extern int ospf_lsa_maxage_walker(struct thread *); extern struct ospf_lsa *ospf_lsa_refresh(struct ospf *, struct ospf_lsa *); extern void ospf_external_lsa_refresh_default(struct ospf *); -extern void ospf_default_originate_lsa_update(struct ospf *ospf); extern void ospf_external_lsa_refresh_type(struct ospf *, uint8_t, unsigned short, int); @@ -333,5 +331,6 @@ extern struct ospf_lsa *ospf_translated_nssa_refresh(struct ospf *, struct ospf_lsa *); extern struct ospf_lsa *ospf_translated_nssa_originate(struct ospf *, struct ospf_lsa *); - +extern void ospf_flush_lsa_from_area(struct ospf *ospf, struct in_addr area_id, + int type); #endif /* _ZEBRA_OSPF_LSA_H */ diff --git a/ospfd/ospf_main.c b/ospfd/ospf_main.c index 36bb8d49b5..45382e48d3 100644 --- a/ospfd/ospf_main.c +++ b/ospfd/ospf_main.c @@ -35,12 +35,12 @@ #include "stream.h" #include "log.h" #include "memory.h" -#include "memory_vty.h" #include "privs.h" #include "sigevent.h" #include "zclient.h" #include "vrf.h" #include "libfrr.h" +#include "routemap.h" #include "ospfd/ospfd.h" #include "ospfd/ospf_interface.h" @@ -72,9 +72,11 @@ struct zebra_privs_t ospfd_privs = { .cap_num_i = 0}; /* OSPFd options. */ -struct option longopts[] = {{"instance", required_argument, NULL, 'n'}, - {"apiserver", no_argument, NULL, 'a'}, - {0}}; +const struct option longopts[] = { + {"instance", required_argument, NULL, 'n'}, + {"apiserver", no_argument, NULL, 'a'}, + {0} +}; /* OSPFd program name */ @@ -96,6 +98,7 @@ static void sigint(void) { zlog_notice("Terminating on signal"); ospf_terminate(); + exit(0); } /* SIGUSR1 handler. */ @@ -123,8 +126,11 @@ struct quagga_signal_t ospf_signals[] = { }, }; -static const struct frr_yang_module_info *ospfd_yang_modules[] = { +static const struct frr_yang_module_info *const ospfd_yang_modules[] = { + &frr_filter_info, &frr_interface_info, + &frr_route_map_info, + &frr_vrf_info, }; FRR_DAEMON_INFO(ospfd, OSPF, .vty_port = OSPF_VTY_PORT, @@ -140,6 +146,7 @@ FRR_DAEMON_INFO(ospfd, OSPF, .vty_port = OSPF_VTY_PORT, int main(int argc, char **argv) { unsigned short instance = 0; + bool created = false; #ifdef SUPPORT_OSPF_API /* OSPF apiserver is disabled by default. */ @@ -216,11 +223,12 @@ int main(int argc, char **argv) /* OSPF errors init */ ospf_error_init(); - /* Need to initialize the default ospf structure, so the interface mode - commands can be duly processed if they are received before 'router - ospf', - when quagga(ospfd) is restarted */ - if (!ospf_get_instance(instance)) { + /* + * Need to initialize the default ospf structure, so the interface mode + * commands can be duly processed if they are received before 'router + * ospf', when ospfd is restarted + */ + if (instance && !ospf_get_instance(instance, &created)) { flog_err(EC_OSPF_INIT_FAIL, "OSPF instance init failed: %s", strerror(errno)); exit(1); @@ -230,5 +238,5 @@ int main(int argc, char **argv) frr_run(master); /* Not reached. */ - return (0); + return 0; } diff --git a/ospfd/ospf_neighbor.c b/ospfd/ospf_neighbor.c index a58bd93b6e..46dfc505ef 100644 --- a/ospfd/ospf_neighbor.c +++ b/ospfd/ospf_neighbor.c @@ -141,6 +141,8 @@ void ospf_nbr_free(struct ospf_neighbor *nbr) thread_cancel_event(master, nbr); ospf_bfd_info_free(&nbr->bfd_info); + + nbr->oi = NULL; XFREE(MTYPE_OSPF_NEIGHBOR, nbr); } @@ -402,12 +404,14 @@ void ospf_renegotiate_optional_capabilities(struct ospf *top) struct ospf_neighbor *ospf_nbr_lookup(struct ospf_interface *oi, struct ip *iph, struct ospf_header *ospfh) { + struct in_addr srcaddr = iph->ip_src; + if (oi->type == OSPF_IFTYPE_VIRTUALLINK || oi->type == OSPF_IFTYPE_POINTOPOINT) return (ospf_nbr_lookup_by_routerid(oi->nbrs, &ospfh->router_id)); else - return (ospf_nbr_lookup_by_addr(oi->nbrs, &iph->ip_src)); + return (ospf_nbr_lookup_by_addr(oi->nbrs, &srcaddr)); } static struct ospf_neighbor *ospf_nbr_add(struct ospf_interface *oi, @@ -444,7 +448,7 @@ static struct ospf_neighbor *ospf_nbr_add(struct ospf_interface *oi, nbr->crypt_seqnum = ospfh->u.crypt.crypt_seqnum; if (IS_DEBUG_OSPF_EVENT) - zlog_debug("NSM[%s:%s]: start", IF_NAME(nbr->oi), + zlog_debug("NSM[%s:%s]: start", IF_NAME(oi), inet_ntoa(nbr->router_id)); return nbr; diff --git a/ospfd/ospf_network.c b/ospfd/ospf_network.c index 1415a6e8b7..3a1547978a 100644 --- a/ospfd/ospf_network.c +++ b/ospfd/ospf_network.c @@ -53,9 +53,7 @@ int ospf_if_add_allspfrouters(struct ospf *top, struct prefix *p, if (ret < 0) flog_err( EC_LIB_SOCKET, - "can't setsockopt IP_ADD_MEMBERSHIP (fd %d, addr %s, " - "ifindex %u, AllSPFRouters): %s; perhaps a kernel limit " - "on # of multicast group memberships has been exceeded?", + "can't setsockopt IP_ADD_MEMBERSHIP (fd %d, addr %s, ifindex %u, AllSPFRouters): %s; perhaps a kernel limit on # of multicast group memberships has been exceeded?", top->fd, inet_ntoa(p->u.prefix4), ifindex, safe_strerror(errno)); else { @@ -78,8 +76,7 @@ int ospf_if_drop_allspfrouters(struct ospf *top, struct prefix *p, ifindex); if (ret < 0) flog_err(EC_LIB_SOCKET, - "can't setsockopt IP_DROP_MEMBERSHIP (fd %d, addr %s, " - "ifindex %u, AllSPFRouters): %s", + "can't setsockopt IP_DROP_MEMBERSHIP (fd %d, addr %s, ifindex %u, AllSPFRouters): %s", top->fd, inet_ntoa(p->u.prefix4), ifindex, safe_strerror(errno)); else { @@ -104,9 +101,7 @@ int ospf_if_add_alldrouters(struct ospf *top, struct prefix *p, if (ret < 0) flog_err( EC_LIB_SOCKET, - "can't setsockopt IP_ADD_MEMBERSHIP (fd %d, addr %s, " - "ifindex %u, AllDRouters): %s; perhaps a kernel limit " - "on # of multicast group memberships has been exceeded?", + "can't setsockopt IP_ADD_MEMBERSHIP (fd %d, addr %s, ifindex %u, AllDRouters): %s; perhaps a kernel limit on # of multicast group memberships has been exceeded?", top->fd, inet_ntoa(p->u.prefix4), ifindex, safe_strerror(errno)); else @@ -127,8 +122,7 @@ int ospf_if_drop_alldrouters(struct ospf *top, struct prefix *p, ifindex); if (ret < 0) flog_err(EC_LIB_SOCKET, - "can't setsockopt IP_DROP_MEMBERSHIP (fd %d, addr %s, " - "ifindex %u, AllDRouters): %s", + "can't setsockopt IP_DROP_MEMBERSHIP (fd %d, addr %s, ifindex %u, AllDRouters): %s", top->fd, inet_ntoa(p->u.prefix4), ifindex, safe_strerror(errno)); else @@ -167,8 +161,7 @@ int ospf_if_ipmulticast(struct ospf *top, struct prefix *p, ifindex_t ifindex) ret = setsockopt_ipv4_multicast_if(top->fd, p->u.prefix4, ifindex); if (ret < 0) flog_err(EC_LIB_SOCKET, - "can't setsockopt IP_MULTICAST_IF(fd %d, addr %s, " - "ifindex %u): %s", + "can't setsockopt IP_MULTICAST_IF(fd %d, addr %s, ifindex %u): %s", top->fd, inet_ntoa(p->u.prefix4), ifindex, safe_strerror(errno)); #endif @@ -190,7 +183,7 @@ int ospf_sock_init(struct ospf *ospf) /* silently return since VRF is not ready */ return -1; } - frr_elevate_privs(&ospfd_privs) { + frr_with_privs(&ospfd_privs) { ospf_sock = vrf_socket(AF_INET, SOCK_RAW, IPPROTO_OSPFIGP, ospf->vrf_id, ospf->name); if (ospf_sock < 0) { diff --git a/ospfd/ospf_nsm.c b/ospfd/ospf_nsm.c index f7c73fee33..dffbfb7d17 100644 --- a/ospfd/ospf_nsm.c +++ b/ospfd/ospf_nsm.c @@ -33,6 +33,7 @@ #include "table.h" #include "log.h" #include "command.h" +#include "network.h" #include "ospfd/ospfd.h" #include "ospfd/ospf_interface.h" @@ -65,8 +66,9 @@ static int ospf_inactivity_timer(struct thread *thread) nbr->t_inactivity = NULL; if (IS_DEBUG_OSPF(nsm, NSM_TIMERS)) - zlog_debug("NSM[%s:%s]: Timer (Inactivity timer expire)", - IF_NAME(nbr->oi), inet_ntoa(nbr->router_id)); + zlog_debug("NSM[%s:%s:%s]: Timer (Inactivity timer expire)", + IF_NAME(nbr->oi), inet_ntoa(nbr->router_id), + ospf_get_name(nbr->oi->ospf)); OSPF_NSM_EVENT_SCHEDULE(nbr, NSM_InactivityTimer); @@ -81,8 +83,9 @@ static int ospf_db_desc_timer(struct thread *thread) nbr->t_db_desc = NULL; if (IS_DEBUG_OSPF(nsm, NSM_TIMERS)) - zlog_debug("NSM[%s:%s]: Timer (DD Retransmit timer expire)", - IF_NAME(nbr->oi), inet_ntoa(nbr->src)); + zlog_debug("NSM[%s:%s:%s]: Timer (DD Retransmit timer expire)", + IF_NAME(nbr->oi), inet_ntoa(nbr->src), + ospf_get_name(nbr->oi->ospf)); /* resent last send DD packet. */ assert(nbr->last_send); @@ -222,7 +225,7 @@ static int ospf_db_summary_add(struct ospf_neighbor *nbr, struct ospf_lsa *lsa) case OSPF_OPAQUE_LINK_LSA: /* Exclude type-9 LSAs that does not have the same "oi" with * "nbr". */ - if (nbr->oi && ospf_if_exists(lsa->oi) != nbr->oi) + if (ospf_if_exists(lsa->oi) != nbr->oi) return 0; break; case OSPF_OPAQUE_AREA_LSA: @@ -387,16 +390,17 @@ static int nsm_kill_nbr(struct ospf_neighbor *nbr) if (IS_DEBUG_OSPF(nsm, NSM_EVENTS)) zlog_debug( - "NSM[%s:%s]: Down (PollIntervalTimer scheduled)", + "NSM[%s:%s:%s]: Down (PollIntervalTimer scheduled)", IF_NAME(nbr->oi), - inet_ntoa(nbr->address.u.prefix4)); + inet_ntoa(nbr->address.u.prefix4), + ospf_get_name(nbr->oi->ospf)); } return 0; } /* Neighbor State Machine */ -struct { +const struct { int (*func)(struct ospf_neighbor *); int next_state; } NSM[OSPF_NSM_STATE_MAX][OSPF_NSM_EVENT_MAX] = { @@ -572,7 +576,7 @@ struct { }, }; -static const char *ospf_nsm_event_str[] = { +static const char *const ospf_nsm_event_str[] = { "NoEvent", "PacketReceived", "Start", "2-WayReceived", "NegotiationDone", "ExchangeDone", "BadLSReq", "LoadingDone", "AdjOK?", @@ -585,8 +589,9 @@ static void nsm_notice_state_change(struct ospf_neighbor *nbr, int next_state, { /* Logging change of status. */ if (IS_DEBUG_OSPF(nsm, NSM_STATUS)) - zlog_debug("NSM[%s:%s]: State change %s -> %s (%s)", + zlog_debug("NSM[%s:%s:%s]: State change %s -> %s (%s)", IF_NAME(nbr->oi), inet_ntoa(nbr->router_id), + ospf_get_name(nbr->oi->ospf), lookup_msg(ospf_nsm_state_msg, nbr->state, NULL), lookup_msg(ospf_nsm_state_msg, next_state, NULL), ospf_nsm_event_str[event]); @@ -595,8 +600,9 @@ static void nsm_notice_state_change(struct ospf_neighbor *nbr, int next_state, if (CHECK_FLAG(nbr->oi->ospf->config, OSPF_LOG_ADJACENCY_CHANGES) && (CHECK_FLAG(nbr->oi->ospf->config, OSPF_LOG_ADJACENCY_DETAIL) || (next_state == NSM_Full) || (next_state < nbr->state))) - zlog_notice("AdjChg: Nbr %s on %s: %s -> %s (%s)", - inet_ntoa(nbr->router_id), IF_NAME(nbr->oi), + zlog_notice("AdjChg: Nbr %s(%s) on %s: %s -> %s (%s)", + inet_ntoa(nbr->router_id), + ospf_get_name(nbr->oi->ospf), IF_NAME(nbr->oi), lookup_msg(ospf_nsm_state_msg, nbr->state, NULL), lookup_msg(ospf_nsm_state_msg, next_state, NULL), ospf_nsm_event_str[event]); @@ -616,8 +622,6 @@ static void nsm_change_state(struct ospf_neighbor *nbr, int state) struct ospf_interface *oi = nbr->oi; struct ospf_area *vl_area = NULL; uint8_t old_state; - int x; - int force = 1; /* Preserve old status. */ old_state = nbr->state; @@ -664,32 +668,6 @@ static void nsm_change_state(struct ospf_neighbor *nbr, int state) if (oi->type == OSPF_IFTYPE_VIRTUALLINK && vl_area) if (++vl_area->full_vls == 1) ospf_schedule_abr_task(oi->ospf); - - /* kevinm: refresh any redistributions */ - for (x = ZEBRA_ROUTE_SYSTEM; x < ZEBRA_ROUTE_MAX; x++) { - struct list *red_list; - struct listnode *node; - struct ospf_redist *red; - - if (x == ZEBRA_ROUTE_OSPF6) - continue; - - red_list = oi->ospf->redist[x]; - if (!red_list) - continue; - - for (ALL_LIST_ELEMENTS_RO(red_list, node, red)) - ospf_external_lsa_refresh_type( - oi->ospf, x, red->instance, - force); - } - /* XXX: Clearly some thing is wrong with refresh of - * external LSAs - * this added to hack around defaults not refreshing - * after a timer - * jump. - */ - ospf_external_lsa_refresh_default(oi->ospf); } else { oi->full_nbrs--; oi->area->full_nbrs--; @@ -705,9 +683,9 @@ static void nsm_change_state(struct ospf_neighbor *nbr, int state) if (CHECK_FLAG(oi->ospf->config, OSPF_LOG_ADJACENCY_DETAIL)) zlog_info( - "%s:(%s, %s -> %s): " - "scheduling new router-LSA origination", - __PRETTY_FUNCTION__, inet_ntoa(nbr->router_id), + "%s:[%s:%s], %s -> %s): scheduling new router-LSA origination", + __func__, inet_ntoa(nbr->router_id), + ospf_get_name(oi->ospf), lookup_msg(ospf_nsm_state_msg, old_state, NULL), lookup_msg(ospf_nsm_state_msg, state, NULL)); @@ -745,12 +723,19 @@ static void nsm_change_state(struct ospf_neighbor *nbr, int state) /* Start DD exchange protocol */ if (state == NSM_ExStart) { if (nbr->dd_seqnum == 0) - nbr->dd_seqnum = (uint32_t)random(); + nbr->dd_seqnum = (uint32_t)frr_weak_random(); else nbr->dd_seqnum++; nbr->dd_flags = OSPF_DD_FLAG_I | OSPF_DD_FLAG_M | OSPF_DD_FLAG_MS; + if (CHECK_FLAG(oi->ospf->config, OSPF_LOG_ADJACENCY_DETAIL)) + zlog_info( + "%s: Initializing [DD]: %s with seqnum:%x , flags:%x", + (oi->ospf->name) ? oi->ospf->name + : VRF_DEFAULT_NAME, + inet_ntoa(nbr->router_id), nbr->dd_seqnum, + nbr->dd_flags); ospf_db_desc_send(nbr); } @@ -774,8 +759,9 @@ int ospf_nsm_event(struct thread *thread) event = THREAD_VAL(thread); if (IS_DEBUG_OSPF(nsm, NSM_EVENTS)) - zlog_debug("NSM[%s:%s]: %s (%s)", IF_NAME(nbr->oi), + zlog_debug("NSM[%s:%s:%s]: %s (%s)", IF_NAME(nbr->oi), inet_ntoa(nbr->router_id), + ospf_get_name(nbr->oi->ospf), lookup_msg(ospf_nsm_state_msg, nbr->state, NULL), ospf_nsm_event_str[event]); @@ -798,9 +784,9 @@ int ospf_nsm_event(struct thread *thread) */ flog_err( EC_OSPF_FSM_INVALID_STATE, - "NSM[%s:%s]: %s (%s): " - "Warning: action tried to change next_state to %s", + "NSM[%s:%s:%s]: %s (%s): Warning: action tried to change next_state to %s", IF_NAME(nbr->oi), inet_ntoa(nbr->router_id), + ospf_get_name(nbr->oi->ospf), lookup_msg(ospf_nsm_state_msg, nbr->state, NULL), ospf_nsm_event_str[event], diff --git a/ospfd/ospf_opaque.c b/ospfd/ospf_opaque.c index 147773ce23..97cfa48c3d 100644 --- a/ospfd/ospf_opaque.c +++ b/ospfd/ospf_opaque.c @@ -126,6 +126,8 @@ void ospf_opaque_term(void) void ospf_opaque_finish(void) { + ospf_mpls_te_finish(); + ospf_router_info_finish(); ospf_ext_finish(); @@ -430,9 +432,9 @@ void ospf_delete_opaque_functab(uint8_t lsa_type, uint8_t opaque_type) /* Cleanup internal control information, if it * still remains. */ if (functab->oipt != NULL) { + free_opaque_info_owner(functab->oipt); free_opaque_info_per_type( functab->oipt); - free_opaque_info_owner(functab->oipt); } /* Dequeue listnode entry from the list. */ @@ -554,8 +556,8 @@ register_opaque_info_per_type(struct ospf_opaque_functab *functab, case OSPF_OPAQUE_AS_LSA: top = ospf_lookup_by_vrf_id(new->vrf_id); if (new->area != NULL && (top = new->area->ospf) == NULL) { - free_opaque_info_per_type((void *)oipt); free_opaque_info_owner(oipt); + free_opaque_info_per_type(oipt); oipt = NULL; goto out; /* This case may not exist. */ } @@ -567,8 +569,8 @@ register_opaque_info_per_type(struct ospf_opaque_functab *functab, EC_OSPF_LSA_UNEXPECTED, "register_opaque_info_per_type: Unexpected LSA-type(%u)", new->data->type); - free_opaque_info_per_type((void *)oipt); free_opaque_info_owner(oipt); + free_opaque_info_per_type(oipt); oipt = NULL; goto out; /* This case may not exist. */ } @@ -1161,7 +1163,7 @@ void ospf_opaque_config_write_debug(struct vty *vty) void show_opaque_info_detail(struct vty *vty, struct ospf_lsa *lsa) { - struct lsa_header *lsah = (struct lsa_header *)lsa->data; + struct lsa_header *lsah = lsa->data; uint32_t lsid = ntohl(lsah->id.s_addr); uint8_t opaque_type = GET_OPAQUE_TYPE(lsid); uint32_t opaque_id = GET_OPAQUE_ID(lsid); @@ -1557,8 +1559,8 @@ struct ospf_lsa *ospf_opaque_lsa_install(struct ospf_lsa *lsa, int rt_recalc) ospf_lsa_unlock(&oipi->lsa); oipi->lsa = ospf_lsa_lock(lsa); } - /* Register the new lsa entry and get its control info. */ - else if ((oipi = register_opaque_lsa(lsa)) == NULL) { + /* Register the new lsa entry */ + else if (register_opaque_lsa(lsa) == NULL) { flog_warn(EC_OSPF_LSA, "ospf_opaque_lsa_install: register_opaque_lsa() ?"); goto out; @@ -1781,8 +1783,7 @@ void ospf_opaque_lsa_reoriginate_schedule(void *lsa_type_dependent, if (oipt->t_opaque_lsa_self != NULL) { if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "Type-%u Opaque-LSA has already scheduled to" - " RE-ORIGINATE: [opaque-type=%u]", + "Type-%u Opaque-LSA has already scheduled to RE-ORIGINATE: [opaque-type=%u]", lsa_type, GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr))); goto out; @@ -1799,8 +1800,7 @@ void ospf_opaque_lsa_reoriginate_schedule(void *lsa_type_dependent, if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "Schedule Type-%u Opaque-LSA to RE-ORIGINATE in %d" - " ms later: [opaque-type=%u]", + "Schedule Type-%u Opaque-LSA to RE-ORIGINATE in %d ms later: [opaque-type=%u]", lsa_type, delay, GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr))); @@ -1919,8 +1919,7 @@ static int ospf_opaque_type10_lsa_reoriginate_timer(struct thread *t) if (n == 0 || !CHECK_FLAG(top->config, OSPF_OPAQUE_CAPABLE)) { if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "Suspend re-origination of Type-10 Opaque-LSAs" - " (opaque-type=%u) for a while...", + "Suspend re-origination of Type-10 Opaque-LSAs (opaque-type=%u) for a while...", oipt->opaque_type); oipt->status = PROC_SUSPEND; @@ -1930,8 +1929,7 @@ static int ospf_opaque_type10_lsa_reoriginate_timer(struct thread *t) if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "Timer[Type10-LSA]: Re-originate Opaque-LSAs" - " (opaque-type=%u) for Area %s", + "Timer[Type10-LSA]: Re-originate Opaque-LSAs (opaque-type=%u) for Area %s", oipt->opaque_type, inet_ntoa(area->area_id)); rc = (*functab->lsa_originator)(area); diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c index 43c5e338b0..12e6ab123a 100644 --- a/ospfd/ospf_packet.c +++ b/ospfd/ospf_packet.c @@ -33,7 +33,9 @@ #include "log.h" #include "sockopt.h" #include "checksum.h" +#ifdef CRYPTO_INTERNAL #include "md5.h" +#endif #include "vrf.h" #include "lib_errors.h" @@ -51,6 +53,7 @@ #include "ospfd/ospf_flood.h" #include "ospfd/ospf_dump.h" #include "ospfd/ospf_errors.h" +#include "ospfd/ospf_zebra.h" /* * OSPF Fragmentation / fragmented writes @@ -130,7 +133,7 @@ static int ospf_auth_type(struct ospf_interface *oi) return auth_type; } -struct ospf_packet *ospf_packet_new(size_t size) +static struct ospf_packet *ospf_packet_new(size_t size) { struct ospf_packet *new; @@ -229,22 +232,8 @@ void ospf_fifo_free(struct ospf_fifo *fifo) XFREE(MTYPE_OSPF_FIFO, fifo); } -void ospf_packet_add(struct ospf_interface *oi, struct ospf_packet *op) +static void ospf_packet_add(struct ospf_interface *oi, struct ospf_packet *op) { - if (!oi->obuf) { - flog_err( - EC_OSPF_PKT_PROCESS, - "ospf_packet_add(interface %s in state %d [%s], packet type %s, " - "destination %s) called with NULL obuf, ignoring " - "(please report this bug)!\n", - IF_NAME(oi), oi->state, - lookup_msg(ospf_ism_state_msg, oi->state, NULL), - lookup_msg(ospf_packet_type_str, - stream_getc_from(op->s, 1), NULL), - inet_ntoa(op->dst)); - return; - } - /* Add packet to end of queue. */ ospf_fifo_push(oi->obuf, op); @@ -255,20 +244,6 @@ void ospf_packet_add(struct ospf_interface *oi, struct ospf_packet *op) static void ospf_packet_add_top(struct ospf_interface *oi, struct ospf_packet *op) { - if (!oi->obuf) { - flog_err( - EC_OSPF_PKT_PROCESS, - "ospf_packet_add(interface %s in state %d [%s], packet type %s, " - "destination %s) called with NULL obuf, ignoring " - "(please report this bug)!\n", - IF_NAME(oi), oi->state, - lookup_msg(ospf_ism_state_msg, oi->state, NULL), - lookup_msg(ospf_packet_type_str, - stream_getc_from(op->s, 1), NULL), - inet_ntoa(op->dst)); - return; - } - /* Add packet to head of queue. */ ospf_fifo_push_head(oi->obuf, op); @@ -276,7 +251,7 @@ static void ospf_packet_add_top(struct ospf_interface *oi, /* ospf_fifo_debug (oi->obuf); */ } -void ospf_packet_delete(struct ospf_interface *oi) +static void ospf_packet_delete(struct ospf_interface *oi) { struct ospf_packet *op; @@ -286,7 +261,7 @@ void ospf_packet_delete(struct ospf_interface *oi) ospf_packet_free(op); } -struct ospf_packet *ospf_packet_dup(struct ospf_packet *op) +static struct ospf_packet *ospf_packet_dup(struct ospf_packet *op) { struct ospf_packet *new; @@ -332,7 +307,11 @@ static unsigned int ospf_packet_max(struct ospf_interface *oi) static int ospf_check_md5_digest(struct ospf_interface *oi, struct ospf_header *ospfh) { +#ifdef CRYPTO_OPENSSL + EVP_MD_CTX *ctx; +#elif CRYPTO_INTERNAL MD5_CTX ctx; +#endif unsigned char digest[OSPF_AUTH_MD5_SIZE]; struct crypt_key *ck; struct ospf_neighbor *nbr; @@ -361,11 +340,21 @@ static int ospf_check_md5_digest(struct ospf_interface *oi, } /* Generate a digest for the ospf packet - their digest + our digest. */ +#ifdef CRYPTO_OPENSSL + unsigned int md5_size = OSPF_AUTH_MD5_SIZE; + ctx = EVP_MD_CTX_new(); + EVP_DigestInit(ctx, EVP_md5()); + EVP_DigestUpdate(ctx, ospfh, length); + EVP_DigestUpdate(ctx, ck->auth_key, OSPF_AUTH_MD5_SIZE); + EVP_DigestFinal(ctx, digest, &md5_size); + EVP_MD_CTX_free(ctx); +#elif CRYPTO_INTERNAL memset(&ctx, 0, sizeof(ctx)); MD5Init(&ctx); MD5Update(&ctx, ospfh, length); MD5Update(&ctx, ck->auth_key, OSPF_AUTH_MD5_SIZE); MD5Final(digest, &ctx); +#endif /* compare the two */ if (memcmp((caddr_t)ospfh + length, digest, OSPF_AUTH_MD5_SIZE)) { @@ -389,7 +378,11 @@ static int ospf_make_md5_digest(struct ospf_interface *oi, { struct ospf_header *ospfh; unsigned char digest[OSPF_AUTH_MD5_SIZE] = {0}; +#ifdef CRYPTO_OPENSSL + EVP_MD_CTX *ctx; +#elif CRYPTO_INTERNAL MD5_CTX ctx; +#endif void *ibuf; uint32_t t; struct crypt_key *ck; @@ -422,11 +415,21 @@ static int ospf_make_md5_digest(struct ospf_interface *oi, } /* Generate a digest for the entire packet + our secret key. */ +#ifdef CRYPTO_OPENSSL + unsigned int md5_size = OSPF_AUTH_MD5_SIZE; + ctx = EVP_MD_CTX_new(); + EVP_DigestInit(ctx, EVP_md5()); + EVP_DigestUpdate(ctx, ibuf, ntohs(ospfh->length)); + EVP_DigestUpdate(ctx, auth_key, OSPF_AUTH_MD5_SIZE); + EVP_DigestFinal(ctx, digest, &md5_size); + EVP_MD_CTX_free(ctx); +#elif CRYPTO_INTERNAL memset(&ctx, 0, sizeof(ctx)); MD5Init(&ctx); MD5Update(&ctx, ibuf, ntohs(ospfh->length)); MD5Update(&ctx, auth_key, OSPF_AUTH_MD5_SIZE); MD5Final(digest, &ctx); +#endif /* Append md5 digest to the end of the stream. */ stream_put(op->s, digest, OSPF_AUTH_MD5_SIZE); @@ -602,23 +605,15 @@ static void ospf_write_frags(int fd, struct ospf_packet *op, struct ip *iph, if (ret < 0) flog_err( EC_LIB_SOCKET, - "*** ospf_write_frags: sendmsg failed to %s," - " id %d, off %d, len %d, mtu %u failed with %s", + "*** ospf_write_frags: sendmsg failed to %s, id %d, off %d, len %d, mtu %u failed with %s", inet_ntoa(iph->ip_dst), iph->ip_id, iph->ip_off, iph->ip_len, mtu, safe_strerror(errno)); if (IS_DEBUG_OSPF_PACKET(type - 1, SEND)) { zlog_debug( - "ospf_write_frags: sent id %d, off %d, len %d to %s\n", + "ospf_write_frags: sent id %d, off %d, len %d to %s", iph->ip_id, iph->ip_off, iph->ip_len, inet_ntoa(iph->ip_dst)); - if (IS_DEBUG_OSPF_PACKET(type - 1, DETAIL)) { - zlog_debug( - "-----------------IP Header Dump----------------------"); - ospf_ip_header_dump(iph); - zlog_debug( - "-----------------------------------------------------"); - } } iph->ip_off += offset; @@ -660,12 +655,17 @@ static int ospf_write(struct thread *thread) struct in_pktinfo *pi; #endif - ospf->t_write = NULL; + if (ospf->fd < 0 || ospf->oi_running == 0) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "ospf_write failed to send, fd %d, instance %u" + ,ospf->fd, ospf->oi_running); + return -1; + } node = listhead(ospf->oi_write_q); assert(node); oi = listgetdata(node); - assert(oi); #ifdef WANT_OSPF_WRITE_FRAGMENT /* seed ipid static with low order bits of time */ @@ -725,7 +725,7 @@ static int ospf_write(struct thread *thread) * but.. */ if (sizeof(struct ip) > (unsigned int)(iph.ip_hl << OSPF_WRITE_IPHL_SHIFT)) - iph.ip_hl++; /* we presume sizeof struct ip cant + iph.ip_hl++; /* we presume sizeof(struct ip) cant overflow ip_hl.. */ iph.ip_v = IPVERSION; @@ -798,16 +798,14 @@ static int ospf_write(struct thread *thread) sockopt_iphdrincl_swab_systoh(&iph); if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_write to %s, " - "id %d, off %d, len %d, interface %s, mtu %u:", + "ospf_write to %s, id %d, off %d, len %d, interface %s, mtu %u:", inet_ntoa(iph.ip_dst), iph.ip_id, iph.ip_off, iph.ip_len, oi->ifp->name, oi->ifp->mtu); if (ret < 0) flog_err( EC_LIB_SOCKET, - "*** sendmsg in ospf_write failed to %s, " - "id %d, off %d, len %d, interface %s, mtu %u: %s", + "*** sendmsg in ospf_write failed to %s, id %d, off %d, len %d, interface %s, mtu %u: %s", inet_ntoa(iph.ip_dst), iph.ip_id, iph.ip_off, iph.ip_len, oi->ifp->name, oi->ifp->mtu, safe_strerror(errno)); @@ -817,7 +815,6 @@ static int ospf_write(struct thread *thread) if (IS_DEBUG_OSPF_PACKET(type - 1, DETAIL)) { zlog_debug( "-----------------------------------------------------"); - ospf_ip_header_dump(&iph); stream_set_getp(op->s, 0); ospf_packet_dump(op->s); } @@ -868,9 +865,7 @@ static int ospf_write(struct thread *thread) /* Setup to service from the head of the queue again */ if (!list_isempty(ospf->oi_write_q)) { node = listhead(ospf->oi_write_q); - assert(node); oi = listgetdata(node); - assert(oi); } } @@ -900,8 +895,7 @@ static void ospf_hello(struct ip *iph, struct ospf_header *ospfh, if (IPV4_ADDR_SAME(&ospfh->router_id, &oi->ospf->router_id)) { if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV)) { zlog_debug( - "ospf_header[%s/%s]: selforiginated, " - "dropping.", + "ospf_header[%s/%s]: selforiginated, dropping.", lookup_msg(ospf_packet_type_str, ospfh->type, NULL), inet_ntoa(iph->ip_src)); @@ -930,8 +924,7 @@ static void ospf_hello(struct ip *iph, struct ospf_header *ospfh, /* Compare Router Dead Interval. */ if (OSPF_IF_PARAM(oi, v_wait) != ntohl(hello->dead_interval)) { flog_warn(EC_OSPF_PACKET, - "Packet %s [Hello:RECV]: RouterDeadInterval mismatch " - "(expected %u, but received %u).", + "Packet %s [Hello:RECV]: RouterDeadInterval mismatch (expected %u, but received %u).", inet_ntoa(ospfh->router_id), OSPF_IF_PARAM(oi, v_wait), ntohl(hello->dead_interval)); @@ -944,8 +937,7 @@ static void ospf_hello(struct ip *iph, struct ospf_header *ospfh, != ntohs(hello->hello_interval)) { flog_warn( EC_OSPF_PACKET, - "Packet %s [Hello:RECV]: HelloInterval mismatch " - "(expected %u, but received %u).", + "Packet %s [Hello:RECV]: HelloInterval mismatch (expected %u, but received %u).", inet_ntoa(ospfh->router_id), OSPF_IF_PARAM(oi, v_hello), ntohs(hello->hello_interval)); @@ -1077,7 +1069,8 @@ static void ospf_hello(struct ip *iph, struct ospf_header *ospfh, /* If neighbor itself declares DR and no BDR exists, cause event BackupSeen */ if (IPV4_ADDR_SAME(&nbr->address.u.prefix4, &hello->d_router)) - if (hello->bd_router.s_addr == 0 && oi->state == ISM_Waiting) + if (hello->bd_router.s_addr == INADDR_ANY + && oi->state == ISM_Waiting) OSPF_ISM_EVENT_SCHEDULE(oi, ISM_BackupSeen); /* neighbor itself declares BDR. */ @@ -1215,8 +1208,7 @@ static void ospf_db_desc_proc(struct stream *s, struct ospf_interface *oi, */ if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "Packet [DD:RECV]: LSA received Type %d, " - "ID %s is not recent.", + "Packet [DD:RECV]: LSA received Type %d, ID %s is not recent.", lsah->type, inet_ntoa(lsah->id)); ospf_lsa_discard(new); } @@ -1352,16 +1344,25 @@ static void ospf_db_desc(struct ip *iph, struct ospf_header *ospfh, /* Add event to thread. */ OSPF_NSM_EVENT_SCHEDULE(nbr, NSM_PacketReceived); + if (CHECK_FLAG(oi->ospf->config, OSPF_LOG_ADJACENCY_DETAIL)) + zlog_info( + "%s:Packet[DD]: Neighbor %s state is %s, seq_num:0x%x, local:0x%x", + (oi->ospf->name) ? oi->ospf->name : VRF_DEFAULT_NAME, + inet_ntoa(nbr->router_id), + lookup_msg(ospf_nsm_state_msg, nbr->state, NULL), + ntohl(dd->dd_seqnum), nbr->dd_seqnum); + /* Process DD packet by neighbor status. */ switch (nbr->state) { case NSM_Down: case NSM_Attempt: case NSM_TwoWay: - flog_warn( - EC_OSPF_PACKET, - "Packet[DD]: Neighbor %s state is %s, packet discarded.", - inet_ntoa(nbr->router_id), - lookup_msg(ospf_nsm_state_msg, nbr->state, NULL)); + if (CHECK_FLAG(oi->ospf->config, OSPF_LOG_ADJACENCY_DETAIL)) + zlog_info( + "Packet[DD]: Neighbor %s state is %s, packet discarded.", + inet_ntoa(nbr->router_id), + lookup_msg(ospf_nsm_state_msg, nbr->state, + NULL)); break; case NSM_Init: OSPF_NSM_EVENT_EXECUTE(nbr, NSM_TwoWayReceived); @@ -1395,8 +1396,7 @@ static void ospf_db_desc(struct ip *iph, struct ospf_header *ospfh, if (CHECK_FLAG(oi->ospf->config, OSPF_LOG_ADJACENCY_DETAIL)) zlog_info( - "Packet[DD]: Neighbor %s: Initial DBD from Slave, " - "ignoring.", + "Packet[DD]: Neighbor %s: Initial DBD from Slave, ignoring.", inet_ntoa(nbr->router_id)); break; } @@ -1491,10 +1491,6 @@ static void ospf_db_desc(struct ip *iph, struct ospf_header *ospfh, /* Check DD Options. */ if (dd->options != nbr->options) { -#ifdef ORIGINAL_CODING - /* Save the new options for debugging */ - nbr->options = dd->options; -#endif /* ORIGINAL_CODING */ flog_warn(EC_OSPF_PACKET, "Packet[DD]: Neighbor %s options mismatch.", inet_ntoa(nbr->router_id)); @@ -1524,8 +1520,7 @@ static void ospf_db_desc(struct ip *iph, struct ospf_header *ospfh, if (IS_SET_DD_MS(nbr->dd_flags)) { /* Master should discard duplicate DD packet. */ zlog_info( - "Packet[DD]: Neighbor %s duplicated, " - "packet discarded.", + "Packet[DD]: Neighbor %s duplicated, packet discarded.", inet_ntoa(nbr->router_id)); break; } else { @@ -1845,8 +1840,7 @@ static void ospf_ls_upd(struct ospf *ospf, struct ip *iph, if (nbr->state < NSM_Exchange) { if (IS_DEBUG_OSPF(nsm, NSM_EVENTS)) zlog_debug( - "Link State Update: " - "Neighbor[%s] state %s is less than Exchange", + "Link State Update: Neighbor[%s] state %s is less than Exchange", inet_ntoa(ospfh->router_id), lookup_msg(ospf_nsm_state_msg, nbr->state, NULL)); @@ -1945,8 +1939,7 @@ static void ospf_ls_upd(struct ospf *ospf, struct ip *iph, char buf3[INET_ADDRSTRLEN]; flog_err(EC_OSPF_ROUTER_LSA_MISMATCH, - "Incoming Router-LSA from %s with " - "Adv-ID[%s] != LS-ID[%s]", + "Incoming Router-LSA from %s with Adv-ID[%s] != LS-ID[%s]", inet_ntop(AF_INET, &ospfh->router_id, buf1, INET_ADDRSTRLEN), inet_ntop(AF_INET, &lsa->data->id, @@ -1956,8 +1949,7 @@ static void ospf_ls_upd(struct ospf *ospf, struct ip *iph, INET_ADDRSTRLEN)); flog_err( EC_OSPF_DOMAIN_CORRUPT, - "OSPF domain compromised by attack or corruption. " - "Verify correct operation of -ALL- OSPF routers."); + "OSPF domain compromised by attack or corruption. Verify correct operation of -ALL- OSPF routers."); DISCARD_LSA(lsa, 0); } @@ -2029,16 +2021,15 @@ static void ospf_ls_upd(struct ospf *ospf, struct ip *iph, if (current == NULL) { if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "LSA[%s]: Previously originated Opaque-LSA," - "not found in the LSDB.", + "LSA[%s]: Previously originated Opaque-LSA,not found in the LSDB.", dump_lsa_key(lsa)); SET_FLAG(lsa->flags, OSPF_LSA_SELF); - ospf_opaque_self_originated_lsa_received(nbr, - lsa); ospf_ls_ack_send(nbr, lsa); + ospf_opaque_self_originated_lsa_received(nbr, + lsa); continue; } } @@ -2107,7 +2098,6 @@ static void ospf_ls_upd(struct ospf *ospf, struct ip *iph, dump_lsa_key(lsa)); DISCARD_LSA(lsa, 4); - continue; } /* Actual flooding procedure. */ @@ -2252,8 +2242,7 @@ static void ospf_ls_ack(struct ip *iph, struct ospf_header *ospfh, if (nbr->state < NSM_Exchange) { if (IS_DEBUG_OSPF(nsm, NSM_EVENTS)) zlog_debug( - "Link State Acknowledgment: " - "Neighbor[%s] state %s is less than Exchange", + "Link State Acknowledgment: Neighbor[%s] state %s is less than Exchange", inet_ntoa(ospfh->router_id), lookup_msg(ospf_nsm_state_msg, nbr->state, NULL)); @@ -2309,18 +2298,18 @@ static struct stream *ospf_recv_packet(struct ospf *ospf, int fd, msgh.msg_control = (caddr_t)buff; msgh.msg_controllen = sizeof(buff); - ret = stream_recvmsg(ibuf, fd, &msgh, 0, OSPF_MAX_PACKET_SIZE + 1); + ret = stream_recvmsg(ibuf, fd, &msgh, MSG_DONTWAIT, + OSPF_MAX_PACKET_SIZE + 1); if (ret < 0) { - flog_warn(EC_OSPF_PACKET, "stream_recvmsg failed: %s", - safe_strerror(errno)); + if (errno != EAGAIN && errno != EWOULDBLOCK) + flog_warn(EC_OSPF_PACKET, "stream_recvmsg failed: %s", + safe_strerror(errno)); return NULL; } - if ((unsigned int)ret < sizeof(iph)) /* ret must be > 0 now */ - { + if ((unsigned int)ret < sizeof(struct ip)) { flog_warn( EC_OSPF_PACKET, - "ospf_recv_packet: discarding runt packet of length %d " - "(ip header size is %u)", + "ospf_recv_packet: discarding runt packet of length %d (ip header size is %u)", ret, (unsigned int)sizeof(iph)); return NULL; } @@ -2332,7 +2321,7 @@ static struct stream *ospf_recv_packet(struct ospf *ospf, int fd, ip_len = iph->ip_len; -#if !defined(GNU_LINUX) && (OpenBSD < 200311) && (__FreeBSD_version < 1000000) +#if defined(__FreeBSD__) && (__FreeBSD_version < 1000000) /* * Kernel network code touches incoming IP header parameters, * before protocol specific processing. @@ -2366,12 +2355,15 @@ static struct stream *ospf_recv_packet(struct ospf *ospf, int fd, if (ret != ip_len) { flog_warn( EC_OSPF_PACKET, - "ospf_recv_packet read length mismatch: ip_len is %d, " - "but recvmsg returned %d", + "ospf_recv_packet read length mismatch: ip_len is %d, but recvmsg returned %d", ip_len, ret); return NULL; } + if (IS_DEBUG_OSPF_PACKET(0, RECV)) + zlog_debug("%s: fd %d(%s) on interface %d(%s)", __func__, fd, + ospf_get_name(ospf), ifindex, + *ifp ? (*ifp)->name : "Unknown"); return ibuf; } @@ -2597,7 +2589,7 @@ static unsigned ospf_router_lsa_links_examin(struct router_lsa_link *link, { unsigned counted_links = 0, thislinklen; - while (linkbytes) { + while (linkbytes >= OSPF_ROUTER_LSA_LINK_SIZE) { thislinklen = OSPF_ROUTER_LSA_LINK_SIZE + 4 * link->m[0].tos_count; if (thislinklen > linkbytes) { @@ -2635,26 +2627,32 @@ static unsigned ospf_lsa_examin(struct lsa_header *lsah, const uint16_t lsalen, return MSG_NG; } switch (lsah->type) { - case OSPF_ROUTER_LSA: - /* RFC2328 A.4.2, LSA header + 4 bytes followed by N>=1 - * (12+)-byte link blocks */ - if (headeronly) { - ret = (lsalen - OSPF_LSA_HEADER_SIZE - - OSPF_ROUTER_LSA_MIN_SIZE) - % 4 - ? MSG_NG - : MSG_OK; - break; - } + case OSPF_ROUTER_LSA: { + /* + * RFC2328 A.4.2, LSA header + 4 bytes followed by N>=0 + * (12+)-byte link blocks + */ + size_t linkbytes_len = lsalen - OSPF_LSA_HEADER_SIZE + - OSPF_ROUTER_LSA_MIN_SIZE; + + /* + * LSA link blocks are variable length but always multiples of + * 4; basic sanity check + */ + if (linkbytes_len % 4 != 0) + return MSG_NG; + + if (headeronly) + return MSG_OK; + rlsa = (struct router_lsa *)lsah; + ret = ospf_router_lsa_links_examin( (struct router_lsa_link *)rlsa->link, - lsalen - OSPF_LSA_HEADER_SIZE - 4, /* skip: basic - header, "flags", - 0, "# links" */ - ntohs(rlsa->links) /* 16 bits */ - ); + linkbytes_len, + ntohs(rlsa->links)); break; + } case OSPF_AS_EXTERNAL_LSA: /* RFC2328 A.4.5, LSA header + 4 bytes followed by N>=1 12-bytes long * blocks */ @@ -2930,55 +2928,68 @@ static int ospf_verify_header(struct stream *ibuf, struct ospf_interface *oi, return 0; } -/* Starting point of packet process function. */ -int ospf_read(struct thread *thread) +enum ospf_read_return_enum { + OSPF_READ_ERROR, + OSPF_READ_CONTINUE, +}; + +static enum ospf_read_return_enum ospf_read_helper(struct ospf *ospf) { int ret; struct stream *ibuf; - struct ospf *ospf; struct ospf_interface *oi; struct ip *iph; struct ospf_header *ospfh; uint16_t length; - struct interface *ifp = NULL; struct connected *c; - - /* first of all get interface pointer. */ - ospf = THREAD_ARG(thread); - - /* prepare for next packet. */ - ospf->t_read = NULL; - thread_add_read(master, ospf_read, ospf, ospf->fd, &ospf->t_read); + struct interface *ifp = NULL; stream_reset(ospf->ibuf); ibuf = ospf_recv_packet(ospf, ospf->fd, &ifp, ospf->ibuf); if (ibuf == NULL) - return -1; - /* This raw packet is known to be at least as big as its IP header. */ + return OSPF_READ_ERROR; - /* Note that there should not be alignment problems with this assignment - because this is at the beginning of the stream data buffer. */ + /* + * This raw packet is known to be at least as big as its + * IP header. Note that there should not be alignment problems with + * this assignment because this is at the beginning of the + * stream data buffer. + */ iph = (struct ip *)STREAM_DATA(ibuf); - /* Note that sockopt_iphdrincl_swab_systoh was called in - * ospf_recv_packet. */ - + /* + * Note that sockopt_iphdrincl_swab_systoh was called in + * ospf_recv_packet. + */ if (ifp == NULL) { - /* Handle cases where the platform does not support retrieving - the ifindex, - and also platforms (such as Solaris 8) that claim to support - ifindex - retrieval but do not. */ + /* + * Handle cases where the platform does not support + * retrieving the ifindex, and also platforms (such as + * Solaris 8) that claim to support ifindex retrieval but do + * not. + */ c = if_lookup_address((void *)&iph->ip_src, AF_INET, ospf->vrf_id); if (c) ifp = c->ifp; - if (ifp == NULL) - return 0; + if (ifp == NULL) { + if (IS_DEBUG_OSPF_PACKET(0, RECV)) + zlog_debug( + "%s: Unable to determine incoming interface from: %s(%s)", + __func__, inet_ntoa(iph->ip_src), + ospf_get_name(ospf)); + return OSPF_READ_CONTINUE; + } } - /* IP Header dump. */ - if (IS_DEBUG_OSPF_PACKET(0, RECV)) - ospf_ip_header_dump(iph); + if (ospf->vrf_id == VRF_DEFAULT && ospf->vrf_id != ifp->vrf_id) { + /* + * We may have a situation where l3mdev_accept == 1 + * let's just kindly drop the packet and move on. + * ospf really really really does not like when + * we receive the same packet multiple times. + */ + return OSPF_READ_CONTINUE; + } /* Self-originated packet should be discarded silently. */ if (ospf_if_lookup_by_local_addr(ospf, NULL, iph->ip_src)) { @@ -2987,30 +2998,44 @@ int ospf_read(struct thread *thread) "ospf_read[%s]: Dropping self-originated packet", inet_ntoa(iph->ip_src)); } - return 0; + return OSPF_READ_CONTINUE; } - /* Advance from IP header to OSPF header (iph->ip_hl has been verified - by ospf_recv_packet() to be correct). */ - stream_forward_getp(ibuf, iph->ip_hl * 4); + /* Check that we have enough for an IP header */ + if ((unsigned int)(iph->ip_hl << 2) >= STREAM_READABLE(ibuf)) { + if ((unsigned int)(iph->ip_hl << 2) == STREAM_READABLE(ibuf)) { + flog_warn( + EC_OSPF_PACKET, + "Rx'd IP packet with OSPF protocol number but no payload"); + } else { + flog_warn( + EC_OSPF_PACKET, + "IP header length field claims header is %u bytes, but we only have %zu", + (unsigned int)(iph->ip_hl << 2), + STREAM_READABLE(ibuf)); + } + + return OSPF_READ_ERROR; + } + stream_forward_getp(ibuf, iph->ip_hl << 2); ospfh = (struct ospf_header *)stream_pnt(ibuf); if (MSG_OK - != ospf_packet_examin( - ospfh, stream_get_endp(ibuf) - stream_get_getp(ibuf))) - return -1; + != ospf_packet_examin(ospfh, stream_get_endp(ibuf) + - stream_get_getp(ibuf))) + return OSPF_READ_CONTINUE; /* Now it is safe to access all fields of OSPF packet header. */ /* associate packet with ospf interface */ oi = ospf_if_lookup_recv_if(ospf, iph->ip_src, ifp); - /* ospf_verify_header() relies on a valid "oi" and thus can be called - only - after the passive/backbone/other checks below are passed. These - checks - in turn access the fields of unverified "ospfh" structure for their - own - purposes and must remain very accurate in doing this. */ + /* + * ospf_verify_header() relies on a valid "oi" and thus can be called + * only after the passive/backbone/other checks below are passed. + * These checks in turn access the fields of unverified "ospfh" + * structure for their own purposes and must remain very accurate + * in doing this. + */ /* If incoming interface is passive one, ignore it. */ if (oi && OSPF_IF_PASSIVE_STATUS(oi) == OSPF_IF_PASSIVE) { @@ -3018,8 +3043,7 @@ int ospf_read(struct thread *thread) if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ignoring packet from router %s sent to %s, " - "received on a passive interface, %s", + "ignoring packet from router %s sent to %s, received on a passive interface, %s", inet_ntop(AF_INET, &ospfh->router_id, buf[0], sizeof(buf[0])), inet_ntop(AF_INET, &iph->ip_dst, buf[1], @@ -3035,7 +3059,7 @@ int ospf_read(struct thread *thread) OI_MEMBER_JOINED(oi, MEMBER_ALLROUTERS); ospf_if_set_multicast(oi); } - return 0; + return OSPF_READ_CONTINUE; } @@ -3043,34 +3067,35 @@ int ospf_read(struct thread *thread) * or header area is backbone but ospf_interface is not * check for VLINK interface */ - if ((oi == NULL) || (OSPF_IS_AREA_ID_BACKBONE(ospfh->area_id) - && !OSPF_IS_AREA_ID_BACKBONE(oi->area->area_id))) { + if ((oi == NULL) + || (OSPF_IS_AREA_ID_BACKBONE(ospfh->area_id) + && !OSPF_IS_AREA_ID_BACKBONE(oi->area->area_id))) { if ((oi = ospf_associate_packet_vl(ospf, ifp, iph, ospfh)) == NULL) { if (!ospf->instance && IS_DEBUG_OSPF_EVENT) zlog_debug( - "Packet from [%s] received on link %s" - " but no ospf_interface", + "Packet from [%s] received on link %s but no ospf_interface", inet_ntoa(iph->ip_src), ifp->name); - return 0; + return OSPF_READ_CONTINUE; } } - /* else it must be a local ospf interface, check it was received on - * correct link + /* + * else it must be a local ospf interface, check it was + * received on correct link */ else if (oi->ifp != ifp) { if (IS_DEBUG_OSPF_EVENT) flog_warn(EC_OSPF_PACKET, "Packet from [%s] received on wrong link %s", inet_ntoa(iph->ip_src), ifp->name); - return 0; + return OSPF_READ_CONTINUE; } else if (oi->state == ISM_Down) { char buf[2][INET_ADDRSTRLEN]; + flog_warn( EC_OSPF_PACKET, - "Ignoring packet from %s to %s received on interface that is " - "down [%s]; interface flags are %s", + "Ignoring packet from %s to %s received on interface that is down [%s]; interface flags are %s", inet_ntop(AF_INET, &iph->ip_src, buf[0], sizeof(buf[0])), inet_ntop(AF_INET, &iph->ip_dst, buf[1], @@ -3083,13 +3108,15 @@ int ospf_read(struct thread *thread) OI_MEMBER_JOINED(oi, MEMBER_DROUTERS); if (oi->multicast_memberships) ospf_if_set_multicast(oi); - return 0; + return OSPF_READ_CONTINUE; } /* - * If the received packet is destined for AllDRouters, the packet - * should be accepted only if the received ospf interface state is - * either DR or Backup -- endo. + * If the received packet is destined for AllDRouters, the + * packet should be accepted only if the received ospf + * interface state is either DR or Backup -- endo. + * + * I wonder who endo is? */ if (iph->ip_dst.s_addr == htonl(OSPF_ALLDROUTERS) && (oi->state != ISM_DR && oi->state != ISM_Backup)) { @@ -3101,7 +3128,7 @@ int ospf_read(struct thread *thread) /* Try to fix multicast membership. */ SET_FLAG(oi->multicast_memberships, MEMBER_DROUTERS); ospf_if_set_multicast(oi); - return 0; + return OSPF_READ_CONTINUE; } /* Verify more OSPF header fields. */ @@ -3109,10 +3136,9 @@ int ospf_read(struct thread *thread) if (ret < 0) { if (IS_DEBUG_OSPF_PACKET(0, RECV)) zlog_debug( - "ospf_read[%s]: Header check failed, " - "dropping.", + "ospf_read[%s]: Header check failed, dropping.", inet_ntoa(iph->ip_src)); - return ret; + return OSPF_READ_CONTINUE; } /* Show debug receiving packet. */ @@ -3139,7 +3165,8 @@ int ospf_read(struct thread *thread) /* Adjust size to message length. */ length = ntohs(ospfh->length) - OSPF_HEADER_SIZE; - /* Read rest of the packet and call each sort of packet routine. */ + /* Read rest of the packet and call each sort of packet routine. + */ switch (ospfh->type) { case OSPF_MSG_HELLO: ospf_hello(iph, ospfh, ibuf, oi, length); @@ -3157,12 +3184,40 @@ int ospf_read(struct thread *thread) ospf_ls_ack(iph, ospfh, ibuf, oi, length); break; default: - flog_warn(EC_OSPF_PACKET, - "interface %s: OSPF packet header type %d is illegal", - IF_NAME(oi), ospfh->type); + flog_warn( + EC_OSPF_PACKET, + "interface %s(%s): OSPF packet header type %d is illegal", + IF_NAME(oi), ospf_get_name(ospf), ospfh->type); break; } + return OSPF_READ_CONTINUE; +} + +/* Starting point of packet process function. */ +int ospf_read(struct thread *thread) +{ + struct ospf *ospf; + int32_t count = 0; + enum ospf_read_return_enum ret; + + /* first of all get interface pointer. */ + ospf = THREAD_ARG(thread); + + /* prepare for next packet. */ + thread_add_read(master, ospf_read, ospf, ospf->fd, &ospf->t_read); + + while (count < ospf->write_oi_count) { + count++; + ret = ospf_read_helper(ospf); + switch (ret) { + case OSPF_READ_ERROR: + return -1; + case OSPF_READ_CONTINUE: + break; + } + } + return 0; } @@ -3195,7 +3250,7 @@ static int ospf_make_auth(struct ospf_interface *oi, struct ospf_header *ospfh) switch (ospf_auth_type(oi)) { case OSPF_AUTH_NULL: - /* memset (ospfh->u.auth_data, 0, sizeof (ospfh->u.auth_data)); + /* memset (ospfh->u.auth_data, 0, sizeof(ospfh->u.auth_data)); */ break; case OSPF_AUTH_SIMPLE: @@ -3218,7 +3273,7 @@ static int ospf_make_auth(struct ospf_interface *oi, struct ospf_header *ospfh) /* note: the seq is done in ospf_make_md5_digest() */ break; default: - /* memset (ospfh->u.auth_data, 0, sizeof (ospfh->u.auth_data)); + /* memset (ospfh->u.auth_data, 0, sizeof(ospfh->u.auth_data)); */ break; } @@ -3296,7 +3351,7 @@ static int ospf_make_hello(struct ospf_interface *oi, struct stream *s) for (rn = route_top(oi->nbrs); rn; rn = route_next(rn)) if ((nbr = rn->info)) if (nbr->router_id.s_addr - != 0) /* Ignore 0.0.0.0 node. */ + != INADDR_ANY) /* Ignore 0.0.0.0 node. */ if (nbr->state != NSM_Attempt) /* Ignore Down neighbor. */ if (nbr->state @@ -3308,17 +3363,17 @@ static int ospf_make_hello(struct ospf_interface *oi, struct stream *s) /* Check neighbor is * sane? */ if (nbr->d_router.s_addr - != 0 + != INADDR_ANY && IPV4_ADDR_SAME( - &nbr->d_router, - &oi->address - ->u - .prefix4) + &nbr->d_router, + &oi->address + ->u + .prefix4) && IPV4_ADDR_SAME( - &nbr->bd_router, - &oi->address - ->u - .prefix4)) + &nbr->bd_router, + &oi->address + ->u + .prefix4)) flag = 1; /* Hello packet overflows interface MTU. */ @@ -3633,7 +3688,7 @@ static void ospf_hello_send_sub(struct ospf_interface *oi, in_addr_t addr) if (oi->ospf->vrf_id) zlog_debug( "%s: Hello Tx interface %s ospf vrf %s id %u", - __PRETTY_FUNCTION__, oi->ifp->name, + __func__, oi->ifp->name, ospf_vrf_id_to_name(oi->ospf->vrf_id), oi->ospf->vrf_id); } @@ -3701,8 +3756,6 @@ int ospf_hello_reply_timer(struct thread *thread) nbr = THREAD_ARG(thread); nbr->t_hello_reply = NULL; - assert(nbr->oi); - if (IS_DEBUG_OSPF(nsm, NSM_TIMERS)) zlog_debug("NSM[%s:%s]: Timer (hello-reply timer expire)", IF_NAME(nbr->oi), inet_ntoa(nbr->router_id)); @@ -3820,6 +3873,12 @@ void ospf_db_desc_send(struct ospf_neighbor *nbr) ospf_packet_free(nbr->last_send); nbr->last_send = ospf_packet_dup(op); monotime(&nbr->last_send_ts); + if (CHECK_FLAG(oi->ospf->config, OSPF_LOG_ADJACENCY_DETAIL)) + zlog_info( + "%s:Packet[DD]: %s DB Desc send with seqnum:%x , flags:%x", + (oi->ospf->name) ? oi->ospf->name : VRF_DEFAULT_NAME, + inet_ntoa(nbr->router_id), nbr->dd_seqnum, + nbr->dd_flags); } /* Re-send Database Description. */ @@ -3834,6 +3893,12 @@ void ospf_db_desc_resend(struct ospf_neighbor *nbr) /* Hook thread to write packet. */ OSPF_ISM_WRITE_ON(oi->ospf); + if (CHECK_FLAG(oi->ospf->config, OSPF_LOG_ADJACENCY_DETAIL)) + zlog_info( + "%s:Packet[DD]: %s DB Desc resend with seqnum:%x , flags:%x", + (oi->ospf->name) ? oi->ospf->name : VRF_DEFAULT_NAME, + inet_ntoa(nbr->router_id), nbr->dd_seqnum, + nbr->dd_flags); } /* Send Link State Request. */ @@ -3921,17 +3986,13 @@ static struct ospf_packet *ospf_ls_upd_packet_new(struct list *update, if (!warned) { flog_warn( EC_OSPF_LARGE_LSA, - "ospf_ls_upd_packet_new: oversized LSA encountered!" - "will need to fragment. Not optimal. Try divide up" - " your network with areas. Use 'debug ospf packet send'" - " to see details, or look at 'show ip ospf database ..'"); + "ospf_ls_upd_packet_new: oversized LSA encountered!will need to fragment. Not optimal. Try divide up your network with areas. Use 'debug ospf packet send' to see details, or look at 'show ip ospf database ..'"); warned = 1; } if (IS_DEBUG_OSPF_PACKET(0, SEND)) zlog_debug( - "ospf_ls_upd_packet_new: oversized LSA id:%s," - " %d bytes originated by %s, will be fragmented!", + "ospf_ls_upd_packet_new: oversized LSA id:%s, %d bytes originated by %s, will be fragmented!", inet_ntoa(lsa->data->id), ntohs(lsa->data->length), inet_ntoa(lsa->data->adv_router)); @@ -3949,9 +4010,7 @@ static struct ospf_packet *ospf_ls_upd_packet_new(struct list *update, if (size > OSPF_MAX_PACKET_SIZE) { flog_warn(EC_OSPF_LARGE_LSA, - "ospf_ls_upd_packet_new: oversized LSA id:%s too big," - " %d bytes, packet size %ld, dropping it completely." - " OSPF routing is broken!", + "ospf_ls_upd_packet_new: oversized LSA id:%s too big, %d bytes, packet size %ld, dropping it completely. OSPF routing is broken!", inet_ntoa(lsa->data->id), ntohs(lsa->data->length), (long int)size); list_delete_node(update, ln); @@ -4025,6 +4084,23 @@ static void ospf_ls_upd_queue_send(struct ospf_interface *oi, oi->on_write_q = 1; } ospf_write(&os_packet_thd); + /* + * We are fake calling ospf_write with a fake + * thread. Imagine that we have oi_a already + * enqueued and we have turned on the write + * thread(t_write). + * Now this function calls this for oi_b + * so the on_write_q has oi_a and oi_b on + * it, ospf_write runs and clears the packets + * for both oi_a and oi_b. Removing them from + * the on_write_q. After this thread of execution + * finishes we will execute the t_write thread + * with nothing in the on_write_q causing an + * assert. So just make sure that the t_write + * is actually turned off. + */ + if (list_isempty(oi->ospf->oi_write_q)) + OSPF_TIMER_OFF(oi->ospf->t_write); } else { /* Hook thread to write packet. */ OSPF_ISM_WRITE_ON(oi->ospf); @@ -4065,8 +4141,7 @@ static int ospf_ls_upd_send_queue_event(struct thread *thread) if (again != 0) { if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_ls_upd_send_queue: update lists not cleared," - " %d nodes to try again, raising new event", + "ospf_ls_upd_send_queue: update lists not cleared, %d nodes to try again, raising new event", again); oi->t_ls_upd_event = NULL; thread_add_event(master, ospf_ls_upd_send_queue_event, oi, 0, @@ -4255,21 +4330,10 @@ void ospf_ls_ack_send_delayed(struct ospf_interface *oi) * punt-to-CPU set on them. This may overload the CPU control path that * can be avoided if the MAC was known apriori. */ -#define OSPF_PING_NBR_STR_MAX (BUFSIZ) void ospf_proactively_arp(struct ospf_neighbor *nbr) { - char ping_nbr[OSPF_PING_NBR_STR_MAX]; - int ret; - - if (!nbr || !nbr->oi || !nbr->oi->ifp) + if (!nbr || !nbr->oi->ospf->proactive_arp) return; - snprintf(ping_nbr, sizeof(ping_nbr), - "ping -c 1 -I %s %s > /dev/null 2>&1 &", nbr->oi->ifp->name, - inet_ntoa(nbr->address.u.prefix4)); - - ret = system(ping_nbr); - if (IS_DEBUG_OSPF_EVENT) - zlog_debug("Executed %s %s", ping_nbr, - ((ret == 0) ? "successfully" : "but failed")); + ospf_zebra_send_arp(nbr->oi->ifp, &nbr->address); } diff --git a/ospfd/ospf_packet.h b/ospfd/ospf_packet.h index a508727968..5a3e029f2e 100644 --- a/ospfd/ospf_packet.h +++ b/ospfd/ospf_packet.h @@ -131,8 +131,6 @@ struct ospf_ls_update { #define IS_SET_DD_ALL(X) ((X) & OSPF_DD_FLAG_ALL) /* Prototypes. */ -extern void ospf_output_forward(struct stream *, int); -extern struct ospf_packet *ospf_packet_new(size_t); extern void ospf_packet_free(struct ospf_packet *); extern struct ospf_fifo *ospf_fifo_new(void); extern void ospf_fifo_push(struct ospf_fifo *, struct ospf_packet *); @@ -140,10 +138,6 @@ extern struct ospf_packet *ospf_fifo_pop(struct ospf_fifo *); extern struct ospf_packet *ospf_fifo_head(struct ospf_fifo *); extern void ospf_fifo_flush(struct ospf_fifo *); extern void ospf_fifo_free(struct ospf_fifo *); -extern void ospf_packet_add(struct ospf_interface *, struct ospf_packet *); -extern void ospf_packet_delete(struct ospf_interface *); -extern struct stream *ospf_stream_dup(struct stream *); -extern struct ospf_packet *ospf_packet_dup(struct ospf_packet *); extern int ospf_read(struct thread *); extern void ospf_hello_send(struct ospf_interface *); diff --git a/ospfd/ospf_ri.c b/ospfd/ospf_ri.c index 5f01edfbdf..fc9c8f6be6 100644 --- a/ospfd/ospf_ri.c +++ b/ospfd/ospf_ri.c @@ -178,6 +178,14 @@ void ospf_router_info_term(void) void ospf_router_info_finish(void) { + struct listnode *node, *nnode; + struct ospf_ri_area_info *ai; + + /* Flush Router Info LSA */ + for (ALL_LIST_ELEMENTS(OspfRI.area_info, node, nnode, ai)) + if (CHECK_FLAG(ai->flags, RIFLG_LSA_ENGAGED)) + ospf_router_info_lsa_schedule(ai, FLUSH_THIS_LSA); + list_delete_all_node(OspfRI.pce_info.pce_domain); list_delete_all_node(OspfRI.pce_info.pce_neighbor); @@ -434,29 +442,52 @@ static void unset_sr_algorithm(uint8_t algo) TLV_LEN(OspfRI.sr_info.algo) = htons(0); } -/* Segment Routing Global Block SubTLV - section 3.2 */ -static void set_sr_sid_label_range(struct sr_srgb srgb) +/* Set Segment Routing Global Block SubTLV - section 3.2 */ +static void set_sr_global_label_range(struct sr_block srgb) { /* Set Header */ - TLV_TYPE(OspfRI.sr_info.range) = htons(RI_SR_TLV_SID_LABEL_RANGE); - TLV_LEN(OspfRI.sr_info.range) = + TLV_TYPE(OspfRI.sr_info.srgb) = htons(RI_SR_TLV_SRGB_LABEL_RANGE); + TLV_LEN(OspfRI.sr_info.srgb) = htons(SUBTLV_SID_LABEL_SIZE + sizeof(uint32_t)); /* Set Range Size */ - OspfRI.sr_info.range.size = htonl(SET_RANGE_SIZE(srgb.range_size)); + OspfRI.sr_info.srgb.size = htonl(SET_RANGE_SIZE(srgb.range_size)); /* Set Lower bound label SubTLV */ - TLV_TYPE(OspfRI.sr_info.range.lower) = htons(SUBTLV_SID_LABEL); - TLV_LEN(OspfRI.sr_info.range.lower) = htons(SID_RANGE_LABEL_LENGTH); - OspfRI.sr_info.range.lower.value = htonl(SET_LABEL(srgb.lower_bound)); + TLV_TYPE(OspfRI.sr_info.srgb.lower) = htons(SUBTLV_SID_LABEL); + TLV_LEN(OspfRI.sr_info.srgb.lower) = htons(SID_RANGE_LABEL_LENGTH); + OspfRI.sr_info.srgb.lower.value = htonl(SET_LABEL(srgb.lower_bound)); } -/* Unset this SRGB SubTLV */ -static void unset_sr_sid_label_range(void) +/* Unset Segment Routing Global Block SubTLV */ +static void unset_sr_global_label_range(void) { + TLV_TYPE(OspfRI.sr_info.srgb) = htons(0); + TLV_LEN(OspfRI.sr_info.srgb) = htons(0); + TLV_TYPE(OspfRI.sr_info.srgb.lower) = htons(0); + TLV_LEN(OspfRI.sr_info.srgb.lower) = htons(0); +} - TLV_TYPE(OspfRI.sr_info.range) = htons(0); - TLV_LEN(OspfRI.sr_info.range) = htons(0); - TLV_TYPE(OspfRI.sr_info.range.lower) = htons(0); - TLV_LEN(OspfRI.sr_info.range.lower) = htons(0); +/* Set Segment Routing Local Block SubTLV - section 3.2 */ +static void set_sr_local_label_range(struct sr_block srlb) +{ + /* Set Header */ + TLV_TYPE(OspfRI.sr_info.srlb) = htons(RI_SR_TLV_SRLB_LABEL_RANGE); + TLV_LEN(OspfRI.sr_info.srlb) = + htons(SUBTLV_SID_LABEL_SIZE + sizeof(uint32_t)); + /* Set Range Size */ + OspfRI.sr_info.srlb.size = htonl(SET_RANGE_SIZE(srlb.range_size)); + /* Set Lower bound label SubTLV */ + TLV_TYPE(OspfRI.sr_info.srlb.lower) = htons(SUBTLV_SID_LABEL); + TLV_LEN(OspfRI.sr_info.srlb.lower) = htons(SID_RANGE_LABEL_LENGTH); + OspfRI.sr_info.srlb.lower.value = htonl(SET_LABEL(srlb.lower_bound)); +} + +/* Unset Segment Routing Local Block SubTLV */ +static void unset_sr_local_label_range(void) +{ + TLV_TYPE(OspfRI.sr_info.srlb) = htons(0); + TLV_LEN(OspfRI.sr_info.srlb) = htons(0); + TLV_TYPE(OspfRI.sr_info.srlb.lower) = htons(0); + TLV_LEN(OspfRI.sr_info.srlb.lower) = htons(0); } /* Set Maximum Stack Depth for this router */ @@ -510,6 +541,8 @@ static void initialize_params(struct ospf_router_info *ori) /* Try to get available Area's context from ospf at this step. * Do it latter if not available */ if (OspfRI.scope == OSPF_OPAQUE_AREA_LSA) { + if (!list_isempty(OspfRI.area_info)) + list_delete_all_node(OspfRI.area_info); for (ALL_LIST_ELEMENTS(top->areas, node, nnode, area)) { zlog_debug("RI (%s): Add area %s to Router Information", __func__, inet_ntoa(area->area_id)); @@ -540,20 +573,23 @@ static void initialize_params(struct ospf_router_info *ori) return; } -static int is_mandated_params_set(struct ospf_router_info ori) +static int is_mandated_params_set(struct ospf_router_info *ori) { int rc = 0; - if (ntohs(ori.router_cap.header.type) == 0) + if (ori == NULL) return rc; - if ((ntohs(ori.pce_info.pce_header.header.type) == RI_TLV_PCE) - && (ntohs(ori.pce_info.pce_address.header.type) == 0) - && (ntohs(ori.pce_info.pce_cap_flag.header.type) == 0)) + if (ntohs(ori->router_cap.header.type) == 0) return rc; - if ((ori.sr_info.enabled) && (ntohs(TLV_TYPE(ori.sr_info.algo)) == 0) - && (ntohs(TLV_TYPE(ori.sr_info.range)) == 0)) + if ((ntohs(ori->pce_info.pce_header.header.type) == RI_TLV_PCE) + && (ntohs(ori->pce_info.pce_address.header.type) == 0) + && (ntohs(ori->pce_info.pce_cap_flag.header.type) == 0)) + return rc; + + if ((ori->sr_info.enabled) && (ntohs(TLV_TYPE(ori->sr_info.algo)) == 0) + && (ntohs(TLV_TYPE(ori->sr_info.srgb)) == 0)) return rc; rc = 1; @@ -565,13 +601,11 @@ static int is_mandated_params_set(struct ospf_router_info ori) * Used by Segment Routing to set new TLVs and Sub-TLVs values * * @param enable To activate or not Segment Routing router Information flooding - * @param size Size of Label Range i.e. SRGB size - * @param lower Lower bound of the Label Range i.e. SRGB first label - * @param msd Maximum label Stack Depth supported by the router + * @param srn Self Segment Routing node * * @return none */ -void ospf_router_info_update_sr(bool enable, struct sr_srgb srgb, uint8_t msd) +void ospf_router_info_update_sr(bool enable, struct sr_node *srn) { struct listnode *node, *nnode; struct ospf_ri_area_info *ai; @@ -595,6 +629,10 @@ void ospf_router_info_update_sr(bool enable, struct sr_srgb srgb, uint8_t msd) initialize_params(&OspfRI); } + /* Check that SR node is valid */ + if (srn == NULL) + return; + if (IS_DEBUG_OSPF_SR) zlog_debug("RI (%s): %s Routing Information for Segment Routing", __func__, enable ? "Enable" : "Disable"); @@ -602,15 +640,17 @@ void ospf_router_info_update_sr(bool enable, struct sr_srgb srgb, uint8_t msd) /* Unset or Set SR parameters */ if (!enable) { unset_sr_algorithm(SR_ALGORITHM_SPF); - unset_sr_sid_label_range(); + unset_sr_global_label_range(); + unset_sr_local_label_range(); unset_sr_node_msd(); OspfRI.sr_info.enabled = false; } else { // Only SR_ALGORITHM_SPF is supported set_sr_algorithm(SR_ALGORITHM_SPF); - set_sr_sid_label_range(srgb); - if (msd != 0) - set_sr_node_msd(msd); + set_sr_global_label_range(srn->srgb); + set_sr_local_label_range(srn->srlb); + if (srn->msd != 0) + set_sr_node_msd(srn->msd); else unset_sr_node_msd(); OspfRI.sr_info.enabled = true; @@ -688,7 +728,9 @@ static void ospf_router_info_lsa_body_set(struct stream *s) /* Build Algorithm TLV */ build_tlv(s, &TLV_HDR(OspfRI.sr_info.algo)); /* Build SRGB TLV */ - build_tlv(s, &TLV_HDR(OspfRI.sr_info.range)); + build_tlv(s, &TLV_HDR(OspfRI.sr_info.srgb)); + /* Build SRLB TLV */ + build_tlv(s, &TLV_HDR(OspfRI.sr_info.srlb)); /* Build MSD TLV */ build_tlv(s, &TLV_HDR(OspfRI.sr_info.msd)); } @@ -951,7 +993,7 @@ static int ospf_router_info_lsa_originate(void *arg) } /* Router Information is not yet Engaged, check parameters */ - if (!is_mandated_params_set(OspfRI)) + if (!is_mandated_params_set(&OspfRI)) flog_warn( EC_OSPF_LSA, "RI (%s): lacks mandated ROUTER INFORMATION parameters", @@ -1382,9 +1424,8 @@ static uint16_t show_vty_sr_algorithm(struct vty *vty, struct tlv_header *tlvh) zlog_debug(" Algorithm %d: Strict SPF", i); break; default: - zlog_debug( - " Algorithm %d: Unknown value %d\n", - i, algo->value[i]); + zlog_debug(" Algorithm %d: Unknown value %d", + i, algo->value[i]); break; } } @@ -1400,16 +1441,22 @@ static uint16_t show_vty_sr_range(struct vty *vty, struct tlv_header *tlvh) if (vty != NULL) { vty_out(vty, - " Segment Routing Range TLV:\n" + " Segment Routing %s Range TLV:\n" " Range Size = %d\n" " SID Label = %d\n\n", + ntohs(range->header.type) == RI_SR_TLV_SRGB_LABEL_RANGE + ? "Global" + : "Local", GET_RANGE_SIZE(ntohl(range->size)), GET_LABEL(ntohl(range->lower.value))); } else { zlog_debug( - " Segment Routing Range TLV:\n" + " Segment Routing %s Range TLV:\n" " Range Size = %d\n" " SID Label = %d\n\n", + ntohs(range->header.type) == RI_SR_TLV_SRGB_LABEL_RANGE + ? "Global" + : "Local", GET_RANGE_SIZE(ntohl(range->size)), GET_LABEL(ntohl(range->lower.value))); } @@ -1439,7 +1486,7 @@ static uint16_t show_vty_sr_msd(struct vty *vty, struct tlv_header *tlvh) static void ospf_router_info_show_info(struct vty *vty, struct ospf_lsa *lsa) { - struct lsa_header *lsah = (struct lsa_header *)lsa->data; + struct lsa_header *lsah = lsa->data; struct tlv_header *tlvh; uint16_t length = 0, sum = 0; @@ -1460,7 +1507,8 @@ static void ospf_router_info_show_info(struct vty *vty, struct ospf_lsa *lsa) case RI_SR_TLV_SR_ALGORITHM: sum += show_vty_sr_algorithm(vty, tlvh); break; - case RI_SR_TLV_SID_LABEL_RANGE: + case RI_SR_TLV_SRGB_LABEL_RANGE: + case RI_SR_TLV_SRLB_LABEL_RANGE: sum += show_vty_sr_range(vty, tlvh); break; case RI_SR_TLV_NODE_MSD: diff --git a/ospfd/ospf_ri.h b/ospfd/ospf_ri.h index 84511ac5e7..4729677bca 100644 --- a/ospfd/ospf_ri.h +++ b/ospfd/ospf_ri.h @@ -201,7 +201,12 @@ struct ospf_ri_sr_info { * Segment Routing Global Block i.e. label range * Only one range supported in this code */ - struct ri_sr_tlv_sid_label_range range; + struct ri_sr_tlv_sid_label_range srgb; + /* + * Segment Routing Local Block. + * Only one block is authorized - see section 3.3 + */ + struct ri_sr_tlv_sid_label_range srlb; /* Maximum SID Depth supported by the node */ struct ri_sr_tlv_node_msd msd; }; @@ -242,7 +247,6 @@ extern int ospf_router_info_init(void); extern void ospf_router_info_term(void); extern void ospf_router_info_finish(void); extern int ospf_router_info_enable(void); -extern void ospf_router_info_update_sr(bool enable, struct sr_srgb srgb, - uint8_t msd); +extern void ospf_router_info_update_sr(bool enable, struct sr_node *self); extern struct scope_info ospf_router_info_get_flooding_scope(void); #endif /* _ZEBRA_OSPF_ROUTER_INFO_H */ diff --git a/ospfd/ospf_route.c b/ospfd/ospf_route.c index da83c1ddaf..3b049555ba 100644 --- a/ospfd/ospf_route.c +++ b/ospfd/ospf_route.c @@ -322,8 +322,7 @@ void ospf_intra_add_router(struct route_table *rt, struct vertex *v, if (!IS_ROUTER_LSA_BORDER(lsa) && !IS_ROUTER_LSA_EXTERNAL(lsa)) { if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_intra_add_router: " - "this router is neither ASBR nor ABR, skipping it"); + "ospf_intra_add_router: this router is neither ASBR nor ABR, skipping it"); return; } @@ -376,7 +375,7 @@ void ospf_intra_add_router(struct route_table *rt, struct vertex *v, else route_unlock_node(rn); - ospf_route_copy_nexthops_from_vertex(or, v); + ospf_route_copy_nexthops_from_vertex(area, or, v); listnode_add(rn->info, or); @@ -439,7 +438,7 @@ void ospf_intra_add_transit(struct route_table *rt, struct vertex *v, or->type = OSPF_DESTINATION_NETWORK; or->u.std.origin = (struct lsa_header *)lsa; - ospf_route_copy_nexthops_from_vertex(or, v); + ospf_route_copy_nexthops_from_vertex(area, or, v); rn->info = or ; } @@ -454,7 +453,7 @@ void ospf_intra_add_stub(struct route_table *rt, struct router_lsa_link *link, struct ospf_route * or ; struct prefix_ipv4 p; struct router_lsa *lsa; - struct ospf_interface *oi; + struct ospf_interface *oi = NULL; struct ospf_path *path; if (IS_DEBUG_OSPF_EVENT) @@ -506,8 +505,7 @@ void ospf_intra_add_stub(struct route_table *rt, struct router_lsa_link *link, if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_intra_add_stub(): " - "another route to the same prefix found with cost %u", + "ospf_intra_add_stub(): another route to the same prefix found with cost %u", cur_or->cost); /* Compare this distance to the current best cost to the stub @@ -540,7 +538,7 @@ void ospf_intra_add_stub(struct route_table *rt, struct router_lsa_link *link, zlog_debug( "ospf_intra_add_stub(): routes are equal, merge"); - ospf_route_copy_nexthops_from_vertex(cur_or, v); + ospf_route_copy_nexthops_from_vertex(area, cur_or, v); if (IPV4_ADDR_CMP(&cur_or->u.std.origin->id, &lsa->header.id) @@ -565,7 +563,7 @@ void ospf_intra_add_stub(struct route_table *rt, struct router_lsa_link *link, list_delete_all_node(cur_or->paths); - ospf_route_copy_nexthops_from_vertex(cur_or, v); + ospf_route_copy_nexthops_from_vertex(area, cur_or, v); cur_or->u.std.origin = (struct lsa_header *)lsa; return; @@ -590,24 +588,35 @@ void ospf_intra_add_stub(struct route_table *rt, struct router_lsa_link *link, if (IS_DEBUG_OSPF_EVENT) zlog_debug( "ospf_intra_add_stub(): this network is on remote router"); - ospf_route_copy_nexthops_from_vertex(or, v); + ospf_route_copy_nexthops_from_vertex(area, or, v); } else { if (IS_DEBUG_OSPF_EVENT) zlog_debug( "ospf_intra_add_stub(): this network is on this router"); - if ((oi = ospf_if_lookup_by_lsa_pos(area, lsa_pos))) { + /* + * Only deal with interface data when we + * don't do a dry run + */ + if (!area->spf_dry_run) + oi = ospf_if_lookup_by_lsa_pos(area, lsa_pos); + + if (oi || area->spf_dry_run) { if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_intra_add_stub(): the interface is %s", - IF_NAME(oi)); + "ospf_intra_add_stub(): the lsa pos is %d", + lsa_pos); path = ospf_path_new(); - path->nexthop.s_addr = 0; - path->ifindex = oi->ifp->ifindex; - if (CHECK_FLAG(oi->connected->flags, - ZEBRA_IFA_UNNUMBERED)) - path->unnumbered = 1; + path->nexthop.s_addr = INADDR_ANY; + + if (oi) { + path->ifindex = oi->ifp->ifindex; + if (CHECK_FLAG(oi->connected->flags, + ZEBRA_IFA_UNNUMBERED)) + path->unnumbered = 1; + } + listnode_add(or->paths, path); } else { if (IS_DEBUG_OSPF_EVENT) @@ -622,34 +631,24 @@ void ospf_intra_add_stub(struct route_table *rt, struct router_lsa_link *link, zlog_debug("ospf_intra_add_stub(): Stop"); } -const char *ospf_path_type_str[] = {"unknown-type", "intra-area", "inter-area", - "type1-external", "type2-external"}; +static const char *const ospf_path_type_str[] = { + "unknown-type", "intra-area", "inter-area", "type1-external", + "type2-external" +}; void ospf_route_table_dump(struct route_table *rt) { struct route_node *rn; struct ospf_route * or ; - char buf1[BUFSIZ]; - char buf2[BUFSIZ]; struct listnode *pnode; struct ospf_path *path; -#if 0 - zlog_debug ("Type Dest Area Path Type Cost Next Adv."); - zlog_debug (" Hop(s) Router(s)"); -#endif /* 0 */ - zlog_debug("========== OSPF routing table =========="); for (rn = route_top(rt); rn; rn = route_next(rn)) if ((or = rn->info) != NULL) { if (or->type == OSPF_DESTINATION_NETWORK) { - zlog_debug("N %s/%d\t%s\t%s\t%d", - inet_ntop(AF_INET, &rn->p.u.prefix4, - buf1, BUFSIZ), - rn->p.prefixlen, - inet_ntop(AF_INET, - & or->u.std.area_id, buf2, - BUFSIZ), + zlog_debug("N %-18pFX %-15pI4 %s %d", &rn->p, + &or->u.std.area_id, ospf_path_type_str[or->path_type], or->cost); for (ALL_LIST_ELEMENTS_RO(or->paths, pnode, @@ -657,18 +656,46 @@ void ospf_route_table_dump(struct route_table *rt) zlog_debug(" -> %s", inet_ntoa(path->nexthop)); } else - zlog_debug("R %s\t%s\t%s\t%d", - inet_ntop(AF_INET, &rn->p.u.prefix4, - buf1, BUFSIZ), - inet_ntop(AF_INET, - & or->u.std.area_id, buf2, - BUFSIZ), + zlog_debug("R %-18pI4 %-15pI4 %s %d", + &rn->p.u.prefix4, + &or->u.std.area_id, ospf_path_type_str[or->path_type], or->cost); } zlog_debug("========================================"); } +void ospf_route_table_print(struct vty *vty, struct route_table *rt) +{ + struct route_node *rn; + struct ospf_route * or ; + struct listnode *pnode; + struct ospf_path *path; + + vty_out(vty, "========== OSPF routing table ==========\n"); + for (rn = route_top(rt); rn; rn = route_next(rn)) + if ((or = rn->info) != NULL) { + if (or->type == OSPF_DESTINATION_NETWORK) { + vty_out(vty, "N %-18pFX %-15pI4 %s %d\n", + &rn->p, & or->u.std.area_id, + ospf_path_type_str[or->path_type], + or->cost); + for (ALL_LIST_ELEMENTS_RO(or->paths, pnode, + path)) + vty_out(vty, " -> %s\n", + path->nexthop.s_addr != 0 + ? inet_ntoa( + path->nexthop) + : "directly connected"); + } else + vty_out(vty, "R %-18pI4 %-15pI4 %s %d\n", + &rn->p.u.prefix4, & or->u.std.area_id, + ospf_path_type_str[or->path_type], + or->cost); + } + vty_out(vty, "========================================\n"); +} + /* This is 16.4.1 implementation. o Intra-area paths using non-backbone areas are always the most preferred. o The other paths, intra-area backbone paths and inter-area paths, @@ -749,30 +776,41 @@ static int ospf_path_exist(struct list *plist, struct in_addr nexthop, return 0; } -void ospf_route_copy_nexthops_from_vertex(struct ospf_route *to, +void ospf_route_copy_nexthops_from_vertex(struct ospf_area *area, + struct ospf_route *to, struct vertex *v) { struct listnode *node; struct ospf_path *path; struct vertex_nexthop *nexthop; struct vertex_parent *vp; + struct ospf_interface *oi = NULL; assert(to->paths); for (ALL_LIST_ELEMENTS_RO(v->parents, node, vp)) { nexthop = vp->nexthop; - if (nexthop->oi != NULL) { - if (!ospf_path_exist(to->paths, nexthop->router, - nexthop->oi)) { - path = ospf_path_new(); - path->nexthop = nexthop->router; - path->ifindex = nexthop->oi->ifp->ifindex; - if (CHECK_FLAG(nexthop->oi->connected->flags, + /* + * Only deal with interface data when we + * don't do a dry run + */ + if (!area->spf_dry_run) + oi = ospf_if_lookup_by_lsa_pos(area, nexthop->lsa_pos); + + if ((oi && !ospf_path_exist(to->paths, nexthop->router, oi)) + || area->spf_dry_run) { + path = ospf_path_new(); + path->nexthop = nexthop->router; + + if (oi) { + path->ifindex = oi->ifp->ifindex; + if (CHECK_FLAG(oi->connected->flags, ZEBRA_IFA_UNNUMBERED)) path->unnumbered = 1; - listnode_add(to->paths, path); } + + listnode_add(to->paths, path); } } } @@ -936,16 +974,14 @@ int ospf_add_discard_route(struct ospf *ospf, struct route_table *rt, if (or->path_type == OSPF_PATH_INTRA_AREA) { if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_add_discard_route(): " - "an intra-area route exists"); + "ospf_add_discard_route(): an intra-area route exists"); return 0; } if (or->type == OSPF_DESTINATION_DISCARD) { if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_add_discard_route(): " - "discard entry already installed"); + "ospf_add_discard_route(): discard entry already installed"); return 0; } @@ -954,13 +990,12 @@ int ospf_add_discard_route(struct ospf *ospf, struct route_table *rt, if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_add_discard_route(): " - "adding %s/%d", + "ospf_add_discard_route(): adding %s/%d", inet_ntoa(p->prefix), p->prefixlen); new_or = ospf_route_new(); new_or->type = OSPF_DESTINATION_DISCARD; - new_or->id.s_addr = 0; + new_or->id.s_addr = INADDR_ANY; new_or->cost = 0; new_or->u.std.area_id = area->area_id; new_or->u.std.external_routing = area->external_routing; @@ -980,8 +1015,7 @@ void ospf_delete_discard_route(struct ospf *ospf, struct route_table *rt, if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_delete_discard_route(): " - "deleting %s/%d", + "ospf_delete_discard_route(): deleting %s/%d", inet_ntoa(p->prefix), p->prefixlen); rn = route_node_lookup(rt, (struct prefix *)p); @@ -998,16 +1032,14 @@ void ospf_delete_discard_route(struct ospf *ospf, struct route_table *rt, if (or->path_type == OSPF_PATH_INTRA_AREA) { if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_delete_discard_route(): " - "an intra-area route exists"); + "ospf_delete_discard_route(): an intra-area route exists"); return; } if (or->type != OSPF_DESTINATION_DISCARD) { if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_delete_discard_route(): " - "not a discard entry"); + "ospf_delete_discard_route(): not a discard entry"); return; } diff --git a/ospfd/ospf_route.h b/ospfd/ospf_route.h index 8cb5d32a8a..c3fa5954d5 100644 --- a/ospfd/ospf_route.h +++ b/ospfd/ospf_route.h @@ -33,12 +33,24 @@ #define OSPF_PATH_TYPE2_EXTERNAL 4 #define OSPF_PATH_MAX 5 +/* Segment Routing information to complement ospf_path structure */ +struct sr_nexthop_info { + /* Output label associated to this route */ + mpls_label_t label_out; + /* + * Pointer to SR Node which is the next hop for this route + * or NULL if next hop is the destination of the prefix + */ + struct sr_node *nexthop; +}; + /* OSPF Path. */ struct ospf_path { struct in_addr nexthop; struct in_addr adv_router; ifindex_t ifindex; unsigned char unnumbered; + struct sr_nexthop_info srni; }; /* Below is the structure linked to every @@ -120,6 +132,7 @@ extern void ospf_route_table_free(struct route_table *); extern void ospf_route_install(struct ospf *, struct route_table *); extern void ospf_route_table_dump(struct route_table *); +extern void ospf_route_table_print(struct vty *vty, struct route_table *rt); extern void ospf_intra_add_router(struct route_table *, struct vertex *, struct ospf_area *); @@ -134,7 +147,8 @@ extern void ospf_intra_add_stub(struct route_table *, struct router_lsa_link *, extern int ospf_route_cmp(struct ospf *, struct ospf_route *, struct ospf_route *); extern void ospf_route_copy_nexthops(struct ospf_route *, struct list *); -extern void ospf_route_copy_nexthops_from_vertex(struct ospf_route *, +extern void ospf_route_copy_nexthops_from_vertex(struct ospf_area *area, + struct ospf_route *, struct vertex *); extern void ospf_route_subst(struct route_node *, struct ospf_route *, diff --git a/ospfd/ospf_routemap.c b/ospfd/ospf_routemap.c index 30b2a50bb3..0928100f24 100644 --- a/ospfd/ospf_routemap.c +++ b/ospfd/ospf_routemap.c @@ -70,19 +70,14 @@ static void ospf_route_map_update(const char *name) /* Keep old route-map. */ struct route_map *old = ROUTEMAP(red); - if (!old) { - /* Route-map creation */ - /* Update route-map. */ - ROUTEMAP(red) = - route_map_lookup_by_name( - ROUTEMAP_NAME(red)); - - route_map_counter_increment( - ROUTEMAP(red)); - } else { - /* Route-map deletion */ - ROUTEMAP(red) = NULL; - } + ROUTEMAP(red) = + route_map_lookup_by_name( + ROUTEMAP_NAME(red)); + + if (!old) + route_map_counter_increment( + ROUTEMAP(red)); + /* No update for this distribute type. */ if (old == NULL @@ -97,7 +92,7 @@ static void ospf_route_map_update(const char *name) } } -static void ospf_route_map_event(route_map_event_t event, const char *name) +static void ospf_route_map_event(const char *name) { struct ospf *ospf; int type; @@ -126,10 +121,9 @@ static void ospf_route_map_event(route_map_event_t event, const char *name) /* `match ip netxthop ' */ /* Match function return 1 if match is success else return zero. */ -static route_map_result_t route_match_ip_nexthop(void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_match_ip_nexthop(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { struct access_list *alist; struct external_info *ei = object; @@ -165,13 +159,16 @@ static void route_match_ip_nexthop_free(void *rule) } /* Route map commands for metric matching. */ -struct route_map_rule_cmd route_match_ip_nexthop_cmd = { - "ip next-hop", route_match_ip_nexthop, route_match_ip_nexthop_compile, - route_match_ip_nexthop_free}; +static const struct route_map_rule_cmd route_match_ip_nexthop_cmd = { + "ip next-hop", + route_match_ip_nexthop, + route_match_ip_nexthop_compile, + route_match_ip_nexthop_free +}; /* `match ip next-hop prefix-list PREFIX_LIST' */ -static route_map_result_t +static enum route_map_cmd_result_t route_match_ip_next_hop_prefix_list(void *rule, const struct prefix *prefix, route_map_object_t type, void *object) { @@ -205,18 +202,57 @@ static void route_match_ip_next_hop_prefix_list_free(void *rule) XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); } -struct route_map_rule_cmd route_match_ip_next_hop_prefix_list_cmd = { - "ip next-hop prefix-list", route_match_ip_next_hop_prefix_list, +static const struct route_map_rule_cmd + route_match_ip_next_hop_prefix_list_cmd = { + "ip next-hop prefix-list", + route_match_ip_next_hop_prefix_list, route_match_ip_next_hop_prefix_list_compile, - route_match_ip_next_hop_prefix_list_free}; + route_match_ip_next_hop_prefix_list_free +}; + +/* `match ip next-hop type ' */ + +static enum route_map_cmd_result_t +route_match_ip_next_hop_type(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) +{ + struct external_info *ei = object; + + if (type == RMAP_OSPF && prefix->family == AF_INET) { + ei = (struct external_info *)object; + if (!ei) + return RMAP_NOMATCH; + + if (ei->nexthop.s_addr == INADDR_ANY && !ei->ifindex) + return RMAP_MATCH; + } + return RMAP_NOMATCH; +} + +static void *route_match_ip_next_hop_type_compile(const char *arg) +{ + return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg); +} + +static void route_match_ip_next_hop_type_free(void *rule) +{ + XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); +} + +static const struct route_map_rule_cmd + route_match_ip_next_hop_type_cmd = { + "ip next-hop type", + route_match_ip_next_hop_type, + route_match_ip_next_hop_type_compile, + route_match_ip_next_hop_type_free +}; /* `match ip address IP_ACCESS_LIST' */ /* Match function should return 1 if match is success else return zero. */ -static route_map_result_t route_match_ip_address(void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_match_ip_address(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { struct access_list *alist; /* struct prefix_ipv4 match; */ @@ -247,12 +283,15 @@ static void route_match_ip_address_free(void *rule) } /* Route map commands for ip address matching. */ -struct route_map_rule_cmd route_match_ip_address_cmd = { - "ip address", route_match_ip_address, route_match_ip_address_compile, - route_match_ip_address_free}; +static const struct route_map_rule_cmd route_match_ip_address_cmd = { + "ip address", + route_match_ip_address, + route_match_ip_address_compile, + route_match_ip_address_free +}; /* `match ip address prefix-list PREFIX_LIST' */ -static route_map_result_t +static enum route_map_cmd_result_t route_match_ip_address_prefix_list(void *rule, const struct prefix *prefix, route_map_object_t type, void *object) { @@ -280,18 +319,20 @@ static void route_match_ip_address_prefix_list_free(void *rule) XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); } -struct route_map_rule_cmd route_match_ip_address_prefix_list_cmd = { - "ip address prefix-list", route_match_ip_address_prefix_list, +static const struct route_map_rule_cmd + route_match_ip_address_prefix_list_cmd = { + "ip address prefix-list", + route_match_ip_address_prefix_list, route_match_ip_address_prefix_list_compile, - route_match_ip_address_prefix_list_free}; + route_match_ip_address_prefix_list_free +}; /* `match interface IFNAME' */ /* Match function should return 1 if match is success else return zero. */ -static route_map_result_t route_match_interface(void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_match_interface(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { struct interface *ifp; struct external_info *ei; @@ -322,14 +363,17 @@ static void route_match_interface_free(void *rule) } /* Route map commands for ip address matching. */ -struct route_map_rule_cmd route_match_interface_cmd = { - "interface", route_match_interface, route_match_interface_compile, - route_match_interface_free}; +static const struct route_map_rule_cmd route_match_interface_cmd = { + "interface", + route_match_interface, + route_match_interface_compile, + route_match_interface_free +}; /* Match function return 1 if match is success else return zero. */ -static route_map_result_t route_match_tag(void *rule, - const struct prefix *prefix, - route_map_object_t type, void *object) +static enum route_map_cmd_result_t +route_match_tag(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { route_tag_t *tag; struct external_info *ei; @@ -345,8 +389,10 @@ static route_map_result_t route_match_tag(void *rule, } /* Route map commands for tag matching. */ -static struct route_map_rule_cmd route_match_tag_cmd = { - "tag", route_match_tag, route_map_rule_tag_compile, +static const struct route_map_rule_cmd route_match_tag_cmd = { + "tag", + route_match_tag, + route_map_rule_tag_compile, route_map_rule_tag_free, }; @@ -358,10 +404,9 @@ struct ospf_metric { /* `set metric METRIC' */ /* Set metric to attribute. */ -static route_map_result_t route_set_metric(void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_set_metric(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { struct ospf_metric *metric; struct external_info *ei; @@ -395,7 +440,7 @@ static void *route_set_metric_compile(const char *arg) { struct ospf_metric *metric; - metric = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(uint32_t)); + metric = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(*metric)); metric->used = false; if (all_digit(arg)) @@ -432,17 +477,18 @@ static void route_set_metric_free(void *rule) } /* Set metric rule structure. */ -struct route_map_rule_cmd route_set_metric_cmd = { - "metric", route_set_metric, route_set_metric_compile, +static const struct route_map_rule_cmd route_set_metric_cmd = { + "metric", + route_set_metric, + route_set_metric_compile, route_set_metric_free, }; /* `set metric-type TYPE' */ /* Set metric-type to attribute. */ -static route_map_result_t route_set_metric_type(void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_set_metric_type(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { uint32_t *metric_type; struct external_info *ei; @@ -484,13 +530,16 @@ static void route_set_metric_type_free(void *rule) } /* Set metric rule structure. */ -struct route_map_rule_cmd route_set_metric_type_cmd = { - "metric-type", route_set_metric_type, route_set_metric_type_compile, +static const struct route_map_rule_cmd route_set_metric_type_cmd = { + "metric-type", + route_set_metric_type, + route_set_metric_type_compile, route_set_metric_type_free, }; -static route_map_result_t route_set_tag(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static enum route_map_cmd_result_t +route_set_tag(void *rule, const struct prefix *prefix, route_map_object_t type, + void *object) { route_tag_t *tag; struct external_info *ei; @@ -507,8 +556,10 @@ static route_map_result_t route_set_tag(void *rule, const struct prefix *prefix, } /* Route map commands for tag set. */ -static struct route_map_rule_cmd route_set_tag_cmd = { - "tag", route_set_tag, route_map_rule_tag_compile, +static const struct route_map_rule_cmd route_set_tag_cmd = { + "tag", + route_set_tag, + route_map_rule_tag_compile, route_map_rule_tag_free, }; @@ -566,6 +617,9 @@ void ospf_route_map_init(void) route_map_match_ip_next_hop_prefix_list_hook(generic_match_add); route_map_no_match_ip_next_hop_prefix_list_hook(generic_match_delete); + route_map_match_ip_next_hop_type_hook(generic_match_add); + route_map_no_match_ip_next_hop_type_hook(generic_match_delete); + route_map_match_tag_hook(generic_match_add); route_map_no_match_tag_hook(generic_match_delete); @@ -579,6 +633,7 @@ void ospf_route_map_init(void) route_map_install_match(&route_match_ip_next_hop_prefix_list_cmd); route_map_install_match(&route_match_ip_address_cmd); route_map_install_match(&route_match_ip_address_prefix_list_cmd); + route_map_install_match(&route_match_ip_next_hop_type_cmd); route_map_install_match(&route_match_interface_cmd); route_map_install_match(&route_match_tag_cmd); diff --git a/ospfd/ospf_snmp.c b/ospfd/ospf_snmp.c index c26545344a..63191d5cb5 100644 --- a/ospfd/ospf_snmp.c +++ b/ospfd/ospf_snmp.c @@ -548,54 +548,45 @@ static uint8_t *ospfGeneralGroup(struct variable *v, oid *name, size_t *length, return SNMP_IPADDRESS(ospf->router_id); else return SNMP_IPADDRESS(ospf_empty_addr); - break; case OSPFADMINSTAT: /* 2 */ /* The administrative status of OSPF in the router. */ if (ospf_admin_stat(ospf)) return SNMP_INTEGER(OSPF_STATUS_ENABLED); else return SNMP_INTEGER(OSPF_STATUS_DISABLED); - break; case OSPFVERSIONNUMBER: /* 3 */ /* OSPF version 2. */ return SNMP_INTEGER(OSPF_VERSION); - break; case OSPFAREABDRRTRSTATUS: /* 4 */ /* Area Border router status. */ if (ospf && CHECK_FLAG(ospf->flags, OSPF_FLAG_ABR)) return SNMP_INTEGER(SNMP_TRUE); else return SNMP_INTEGER(SNMP_FALSE); - break; case OSPFASBDRRTRSTATUS: /* 5 */ /* AS Border router status. */ if (ospf && CHECK_FLAG(ospf->flags, OSPF_FLAG_ASBR)) return SNMP_INTEGER(SNMP_TRUE); else return SNMP_INTEGER(SNMP_FALSE); - break; case OSPFEXTERNLSACOUNT: /* 6 */ /* External LSA counts. */ if (ospf) return SNMP_INTEGER(ospf_lsdb_count_all(ospf->lsdb)); else return SNMP_INTEGER(0); - break; case OSPFEXTERNLSACKSUMSUM: /* 7 */ /* External LSA checksum. */ return SNMP_INTEGER(0); - break; case OSPFTOSSUPPORT: /* 8 */ /* TOS is not supported. */ return SNMP_INTEGER(SNMP_FALSE); - break; case OSPFORIGINATENEWLSAS: /* 9 */ /* The number of new link-state advertisements. */ if (ospf) return SNMP_INTEGER(ospf->lsa_originate_count); else return SNMP_INTEGER(0); - break; case OSPFRXNEWLSAS: /* 10 */ /* The number of link-state advertisements received determined to be new instantiations. */ @@ -603,24 +594,19 @@ static uint8_t *ospfGeneralGroup(struct variable *v, oid *name, size_t *length, return SNMP_INTEGER(ospf->rx_lsa_count); else return SNMP_INTEGER(0); - break; case OSPFEXTLSDBLIMIT: /* 11 */ /* There is no limit for the number of non-default AS-external-LSAs. */ return SNMP_INTEGER(-1); - break; case OSPFMULTICASTEXTENSIONS: /* 12 */ /* Multicast Extensions to OSPF is not supported. */ return SNMP_INTEGER(0); - break; case OSPFEXITOVERFLOWINTERVAL: /* 13 */ /* Overflow is not supported. */ return SNMP_INTEGER(0); - break; case OSPFDEMANDEXTENSIONS: /* 14 */ /* Demand routing is not supported. */ return SNMP_INTEGER(SNMP_FALSE); - break; default: return NULL; } @@ -717,28 +703,20 @@ static uint8_t *ospfAreaEntry(struct variable *v, oid *name, size_t *length, switch (v->magic) { case OSPFAREAID: /* 1 */ return SNMP_IPADDRESS(area->area_id); - break; case OSPFAUTHTYPE: /* 2 */ return SNMP_INTEGER(area->auth_type); - break; case OSPFIMPORTASEXTERN: /* 3 */ return SNMP_INTEGER(area->external_routing + 1); - break; case OSPFSPFRUNS: /* 4 */ return SNMP_INTEGER(area->spf_calculation); - break; case OSPFAREABDRRTRCOUNT: /* 5 */ return SNMP_INTEGER(area->abr_count); - break; case OSPFASBDRRTRCOUNT: /* 6 */ return SNMP_INTEGER(area->asbr_count); - break; case OSPFAREALSACOUNT: /* 7 */ return SNMP_INTEGER(area->lsdb->total); - break; case OSPFAREALSACKSUMSUM: /* 8 */ return SNMP_INTEGER(0); - break; case OSPFAREASUMMARY: /* 9 */ #define OSPF_noAreaSummary 1 #define OSPF_sendAreaSummary 2 @@ -746,13 +724,10 @@ static uint8_t *ospfAreaEntry(struct variable *v, oid *name, size_t *length, return SNMP_INTEGER(OSPF_noAreaSummary); else return SNMP_INTEGER(OSPF_sendAreaSummary); - break; case OSPFAREASTATUS: /* 10 */ return SNMP_INTEGER(SNMP_VALID); - break; default: return NULL; - break; } return NULL; } @@ -809,7 +784,7 @@ static struct ospf_area *ospfStubAreaLookup(struct variable *v, oid name[], area = ospf_area_lookup_by_area_id(ospf, *addr); - if (area->external_routing == OSPF_AREA_STUB) + if (area && area->external_routing == OSPF_AREA_STUB) return area; else return NULL; @@ -857,29 +832,23 @@ static uint8_t *ospfStubAreaEntry(struct variable *v, oid *name, size_t *length, case OSPFSTUBAREAID: /* 1 */ /* OSPF stub area id. */ return SNMP_IPADDRESS(area->area_id); - break; case OSPFSTUBTOS: /* 2 */ /* TOS value is not supported. */ return SNMP_INTEGER(0); - break; case OSPFSTUBMETRIC: /* 3 */ /* Default cost to stub area. */ return SNMP_INTEGER(area->default_cost); - break; case OSPFSTUBSTATUS: /* 4 */ /* Status of the stub area. */ return SNMP_INTEGER(SNMP_VALID); - break; case OSPFSTUBMETRICTYPE: /* 5 */ /* OSPF Metric type. */ #define OSPF_ospfMetric 1 #define OSPF_comparableCost 2 #define OSPF_nonComparable 3 return SNMP_INTEGER(OSPF_ospfMetric); - break; default: return NULL; - break; } return NULL; } @@ -1097,32 +1066,23 @@ static uint8_t *ospfLsdbEntry(struct variable *v, oid *name, size_t *length, switch (v->magic) { case OSPFLSDBAREAID: /* 1 */ return SNMP_IPADDRESS(lsa->area->area_id); - break; case OSPFLSDBTYPE: /* 2 */ return SNMP_INTEGER(lsah->type); - break; case OSPFLSDBLSID: /* 3 */ return SNMP_IPADDRESS(lsah->id); - break; case OSPFLSDBROUTERID: /* 4 */ return SNMP_IPADDRESS(lsah->adv_router); - break; case OSPFLSDBSEQUENCE: /* 5 */ return SNMP_INTEGER(lsah->ls_seqnum); - break; case OSPFLSDBAGE: /* 6 */ return SNMP_INTEGER(lsah->ls_age); - break; case OSPFLSDBCHECKSUM: /* 7 */ return SNMP_INTEGER(lsah->checksum); - break; case OSPFLSDBADVERTISEMENT: /* 8 */ *var_len = ntohs(lsah->length); return (uint8_t *)lsah; - break; default: return NULL; - break; } return NULL; } @@ -1256,24 +1216,18 @@ static uint8_t *ospfAreaRangeEntry(struct variable *v, oid *name, switch (v->magic) { case OSPFAREARANGEAREAID: /* 1 */ return SNMP_IPADDRESS(area_id); - break; case OSPFAREARANGENET: /* 2 */ return SNMP_IPADDRESS(range_net); - break; case OSPFAREARANGEMASK: /* 3 */ return SNMP_IPADDRESS(mask); - break; case OSPFAREARANGESTATUS: /* 4 */ return SNMP_INTEGER(SNMP_VALID); - break; case OSPFAREARANGEEFFECT: /* 5 */ #define OSPF_advertiseMatching 1 #define OSPF_doNotAdvertiseMatching 2 return SNMP_INTEGER(OSPF_advertiseMatching); - break; default: return NULL; - break; } return NULL; } @@ -1359,28 +1313,22 @@ static uint8_t *ospfHostEntry(struct variable *v, oid *name, size_t *length, switch (v->magic) { case OSPFHOSTIPADDRESS: /* 1 */ return SNMP_IPADDRESS(nbr_nbma->addr); - break; case OSPFHOSTTOS: /* 2 */ return SNMP_INTEGER(0); - break; case OSPFHOSTMETRIC: /* 3 */ if (oi) return SNMP_INTEGER(oi->output_cost); else return SNMP_INTEGER(1); - break; case OSPFHOSTSTATUS: /* 4 */ return SNMP_INTEGER(SNMP_VALID); - break; case OSPFHOSTAREAID: /* 5 */ if (oi && oi->area) return SNMP_IPADDRESS(oi->area->area_id); else return SNMP_IPADDRESS(ospf_empty_addr); - break; default: return NULL; - break; } return NULL; } @@ -1683,80 +1631,59 @@ static uint8_t *ospfIfEntry(struct variable *v, oid *name, size_t *length, switch (v->magic) { case OSPFIFIPADDRESS: /* 1 */ return SNMP_IPADDRESS(ifaddr); - break; case OSPFADDRESSLESSIF: /* 2 */ return SNMP_INTEGER(ifindex); - break; case OSPFIFAREAID: /* 3 */ if (oi->area) return SNMP_IPADDRESS(oi->area->area_id); else return SNMP_IPADDRESS(ospf_empty_addr); - break; case OSPFIFTYPE: /* 4 */ return SNMP_INTEGER(ospf_snmp_iftype(oi->ifp)); - break; case OSPFIFADMINSTAT: /* 5 */ if (oi) return SNMP_INTEGER(OSPF_STATUS_ENABLED); else return SNMP_INTEGER(OSPF_STATUS_DISABLED); - break; case OSPFIFRTRPRIORITY: /* 6 */ return SNMP_INTEGER(PRIORITY(oi)); - break; case OSPFIFTRANSITDELAY: /* 7 */ return SNMP_INTEGER(OSPF_IF_PARAM(oi, transmit_delay)); - break; case OSPFIFRETRANSINTERVAL: /* 8 */ return SNMP_INTEGER(OSPF_IF_PARAM(oi, retransmit_interval)); - break; case OSPFIFHELLOINTERVAL: /* 9 */ return SNMP_INTEGER(OSPF_IF_PARAM(oi, v_hello)); - break; case OSPFIFRTRDEADINTERVAL: /* 10 */ return SNMP_INTEGER(OSPF_IF_PARAM(oi, v_wait)); - break; case OSPFIFPOLLINTERVAL: /* 11 */ return SNMP_INTEGER(OSPF_POLL_INTERVAL_DEFAULT); - break; case OSPFIFSTATE: /* 12 */ return SNMP_INTEGER(ISM_SNMP(oi->state)); - break; case OSPFIFDESIGNATEDROUTER: /* 13 */ return SNMP_IPADDRESS(DR(oi)); - break; case OSPFIFBACKUPDESIGNATEDROUTER: /* 14 */ return SNMP_IPADDRESS(BDR(oi)); - break; case OSPFIFEVENTS: /* 15 */ return SNMP_INTEGER(oi->state_change); - break; case OSPFIFAUTHKEY: /* 16 */ *var_len = 0; return (uint8_t *)OSPF_IF_PARAM(oi, auth_simple); - break; case OSPFIFSTATUS: /* 17 */ return SNMP_INTEGER(SNMP_VALID); - break; case OSPFIFMULTICASTFORWARDING: /* 18 */ #define ospf_snmp_multiforward_blocked 1 #define ospf_snmp_multiforward_multicast 2 #define ospf_snmp_multiforward_unicast 3 return SNMP_INTEGER(ospf_snmp_multiforward_blocked); - break; case OSPFIFDEMAND: /* 19 */ return SNMP_INTEGER(SNMP_FALSE); - break; case OSPFIFAUTHTYPE: /* 20 */ if (oi->area) return SNMP_INTEGER(oi->area->auth_type); else return SNMP_INTEGER(0); - break; default: return NULL; - break; } return NULL; } @@ -1851,22 +1778,16 @@ static uint8_t *ospfIfMetricEntry(struct variable *v, oid *name, size_t *length, switch (v->magic) { case OSPFIFMETRICIPADDRESS: return SNMP_IPADDRESS(ifaddr); - break; case OSPFIFMETRICADDRESSLESSIF: return SNMP_INTEGER(ifindex); - break; case OSPFIFMETRICTOS: return SNMP_INTEGER(0); - break; case OSPFIFMETRICVALUE: return SNMP_INTEGER(OSPF_SNMP_METRIC_VALUE); - break; case OSPFIFMETRICSTATUS: return SNMP_INTEGER(1); - break; default: return NULL; - break; } return NULL; } @@ -2041,44 +1962,32 @@ static uint8_t *ospfVirtIfEntry(struct variable *v, oid *name, size_t *length, switch (v->magic) { case OSPFVIRTIFAREAID: return SNMP_IPADDRESS(area_id); - break; case OSPFVIRTIFNEIGHBOR: return SNMP_IPADDRESS(neighbor); - break; case OSPFVIRTIFTRANSITDELAY: return SNMP_INTEGER(OSPF_IF_PARAM(oi, transmit_delay)); - break; case OSPFVIRTIFRETRANSINTERVAL: return SNMP_INTEGER(OSPF_IF_PARAM(oi, retransmit_interval)); - break; case OSPFVIRTIFHELLOINTERVAL: return SNMP_INTEGER(OSPF_IF_PARAM(oi, v_hello)); - break; case OSPFVIRTIFRTRDEADINTERVAL: return SNMP_INTEGER(OSPF_IF_PARAM(oi, v_wait)); - break; case OSPFVIRTIFSTATE: return SNMP_INTEGER(oi->state); - break; case OSPFVIRTIFEVENTS: return SNMP_INTEGER(oi->state_change); - break; case OSPFVIRTIFAUTHKEY: *var_len = 0; return (uint8_t *)OSPF_IF_PARAM(oi, auth_simple); - break; case OSPFVIRTIFSTATUS: return SNMP_INTEGER(SNMP_VALID); - break; case OSPFVIRTIFAUTHTYPE: if (oi->area) return SNMP_INTEGER(oi->area->auth_type); else return SNMP_INTEGER(0); - break; default: return NULL; - break; } return NULL; } @@ -2257,47 +2166,33 @@ static uint8_t *ospfNbrEntry(struct variable *v, oid *name, size_t *length, if (!nbr) return NULL; oi = nbr->oi; - if (!oi) - return NULL; /* Return the current value of the variable */ switch (v->magic) { case OSPFNBRIPADDR: return SNMP_IPADDRESS(nbr_addr); - break; case OSPFNBRADDRESSLESSINDEX: return SNMP_INTEGER(ifindex); - break; case OSPFNBRRTRID: return SNMP_IPADDRESS(nbr->router_id); - break; case OSPFNBROPTIONS: return SNMP_INTEGER(oi->nbr_self->options); - break; case OSPFNBRPRIORITY: return SNMP_INTEGER(nbr->priority); - break; case OSPFNBRSTATE: return SNMP_INTEGER(ospf_snmp_neighbor_state(nbr->state)); - break; case OSPFNBREVENTS: return SNMP_INTEGER(nbr->state_change); - break; case OSPFNBRLSRETRANSQLEN: return SNMP_INTEGER(ospf_ls_retransmit_count(nbr)); - break; case OSPFNBMANBRSTATUS: return SNMP_INTEGER(SNMP_VALID); - break; case OSPFNBMANBRPERMANENCE: return SNMP_INTEGER(2); - break; case OSPFNBRHELLOSUPPRESSED: return SNMP_INTEGER(SNMP_FALSE); - break; default: return NULL; - break; } return NULL; } @@ -2331,31 +2226,22 @@ static uint8_t *ospfVirtNbrEntry(struct variable *v, oid *name, size_t *length, switch (v->magic) { case OSPFVIRTNBRAREA: return (uint8_t *)NULL; - break; case OSPFVIRTNBRRTRID: return (uint8_t *)NULL; - break; case OSPFVIRTNBRIPADDR: return (uint8_t *)NULL; - break; case OSPFVIRTNBROPTIONS: return (uint8_t *)NULL; - break; case OSPFVIRTNBRSTATE: return (uint8_t *)NULL; - break; case OSPFVIRTNBREVENTS: return (uint8_t *)NULL; - break; case OSPFVIRTNBRLSRETRANSQLEN: return (uint8_t *)NULL; - break; case OSPFVIRTNBRHELLOSUPPRESSED: return (uint8_t *)NULL; - break; default: return NULL; - break; } return NULL; } @@ -2485,29 +2371,21 @@ static uint8_t *ospfExtLsdbEntry(struct variable *v, oid *name, size_t *length, switch (v->magic) { case OSPFEXTLSDBTYPE: return SNMP_INTEGER(OSPF_AS_EXTERNAL_LSA); - break; case OSPFEXTLSDBLSID: return SNMP_IPADDRESS(lsah->id); - break; case OSPFEXTLSDBROUTERID: return SNMP_IPADDRESS(lsah->adv_router); - break; case OSPFEXTLSDBSEQUENCE: return SNMP_INTEGER(lsah->ls_seqnum); - break; case OSPFEXTLSDBAGE: return SNMP_INTEGER(lsah->ls_age); - break; case OSPFEXTLSDBCHECKSUM: return SNMP_INTEGER(lsah->checksum); - break; case OSPFEXTLSDBADVERTISEMENT: *var_len = ntohs(lsah->length); return (uint8_t *)lsah; - break; default: return NULL; - break; } return NULL; } @@ -2525,25 +2403,18 @@ static uint8_t *ospfAreaAggregateEntry(struct variable *v, oid *name, switch (v->magic) { case OSPFAREAAGGREGATEAREAID: return (uint8_t *)NULL; - break; case OSPFAREAAGGREGATELSDBTYPE: return (uint8_t *)NULL; - break; case OSPFAREAAGGREGATENET: return (uint8_t *)NULL; - break; case OSPFAREAAGGREGATEMASK: return (uint8_t *)NULL; - break; case OSPFAREAAGGREGATESTATUS: return (uint8_t *)NULL; - break; case OSPFAREAAGGREGATEEFFECT: return (uint8_t *)NULL; - break; default: return NULL; - break; } return NULL; } @@ -2584,7 +2455,7 @@ static void ospfTrapNbrStateChange(struct ospf_neighbor *on) ospf_nbr_state_message(on, msgbuf, sizeof(msgbuf)); if (IS_DEBUG_OSPF_EVENT) - zlog_info("%s: trap sent: %s now %s", __PRETTY_FUNCTION__, + zlog_info("%s: trap sent: %s now %s", __func__, inet_ntoa(on->address.u.prefix4), msgbuf); oid_copy_addr(index, &(on->address.u.prefix4), IN_ADDR_SIZE); @@ -2592,7 +2463,7 @@ static void ospfTrapNbrStateChange(struct ospf_neighbor *on) smux_trap(ospf_variables, array_size(ospf_variables), ospf_trap_oid, array_size(ospf_trap_oid), ospf_oid, - sizeof ospf_oid / sizeof(oid), index, IN_ADDR_SIZE + 1, + sizeof(ospf_oid) / sizeof(oid), index, IN_ADDR_SIZE + 1, ospfNbrTrapList, array_size(ospfNbrTrapList), NBRSTATECHANGE); } @@ -2607,7 +2478,7 @@ static void ospfTrapVirtNbrStateChange(struct ospf_neighbor *on) smux_trap(ospf_variables, array_size(ospf_variables), ospf_trap_oid, array_size(ospf_trap_oid), ospf_oid, - sizeof ospf_oid / sizeof(oid), index, IN_ADDR_SIZE + 1, + sizeof(ospf_oid) / sizeof(oid), index, IN_ADDR_SIZE + 1, ospfVirtNbrTrapList, array_size(ospfVirtNbrTrapList), VIRTNBRSTATECHANGE); } @@ -2637,7 +2508,7 @@ static void ospfTrapIfStateChange(struct ospf_interface *oi) oid index[sizeof(oid) * (IN_ADDR_SIZE + 1)]; if (IS_DEBUG_OSPF_EVENT) - zlog_info("%s: trap sent: %s now %s", __PRETTY_FUNCTION__, + zlog_info("%s: trap sent: %s now %s", __func__, inet_ntoa(oi->address->u.prefix4), lookup_msg(ospf_ism_state_msg, oi->state, NULL)); @@ -2646,7 +2517,7 @@ static void ospfTrapIfStateChange(struct ospf_interface *oi) smux_trap(ospf_variables, array_size(ospf_variables), ospf_trap_oid, array_size(ospf_trap_oid), ospf_oid, - sizeof ospf_oid / sizeof(oid), index, IN_ADDR_SIZE + 1, + sizeof(ospf_oid) / sizeof(oid), index, IN_ADDR_SIZE + 1, ospfIfTrapList, array_size(ospfIfTrapList), IFSTATECHANGE); } @@ -2661,7 +2532,7 @@ static void ospfTrapVirtIfStateChange(struct ospf_interface *oi) smux_trap(ospf_variables, array_size(ospf_variables), ospf_trap_oid, array_size(ospf_trap_oid), ospf_oid, - sizeof ospf_oid / sizeof(oid), index, IN_ADDR_SIZE + 1, + sizeof(ospf_oid) / sizeof(oid), index, IN_ADDR_SIZE + 1, ospfVirtIfTrapList, array_size(ospfVirtIfTrapList), VIRTIFSTATECHANGE); } diff --git a/ospfd/ospf_spf.c b/ospfd/ospf_spf.c index 296a05bdf1..a83261d445 100644 --- a/ospfd/ospf_spf.c +++ b/ospfd/ospf_spf.c @@ -68,14 +68,11 @@ static void ospf_spf_set_reason(ospf_spf_reason_t reason) } static void ospf_vertex_free(void *); -/* List of allocated vertices, to simplify cleanup of SPF. - * Not thread-safe obviously. If it ever needs to be, it'd have to be - * dynamically allocated at begin of ospf_spf_calculate - */ -static struct list vertex_list = {.del = ospf_vertex_free}; -/* Heap related functions, for the managment of the candidates, to - * be used with pqueue. */ +/* + * Heap related functions, for the managment of the candidates, to + * be used with pqueue. + */ static int vertex_cmp(const struct vertex *v1, const struct vertex *v2) { if (v1->distance != v2->distance) @@ -118,7 +115,8 @@ static void vertex_nexthop_free(struct vertex_nexthop *nh) XFREE(MTYPE_OSPF_NEXTHOP, nh); } -/* Free the canonical nexthop objects for an area, ie the nexthop objects +/* + * Free the canonical nexthop objects for an area, ie the nexthop objects * attached to the first-hop router vertices, and any intervening network * vertices. */ @@ -131,7 +129,8 @@ static void ospf_canonical_nexthops_free(struct vertex *root) struct listnode *n2, *nn2; struct vertex_parent *vp; - /* router vertices through an attached network each + /* + * router vertices through an attached network each * have a distinct (canonical / not inherited) nexthop * which must be freed. * @@ -150,7 +149,8 @@ static void ospf_canonical_nexthops_free(struct vertex *root) } } -/* TODO: Parent list should be excised, in favour of maintaining only +/* + * TODO: Parent list should be excised, in favour of maintaining only * vertex_nexthop, with refcounts. */ static struct vertex_parent *vertex_parent_new(struct vertex *v, int backlink, @@ -163,6 +163,7 @@ static struct vertex_parent *vertex_parent_new(struct vertex *v, int backlink, new->parent = v; new->backlink = backlink; new->nexthop = hop; + return new; } @@ -177,7 +178,8 @@ static int vertex_parent_cmp(void *aa, void *bb) return IPV4_ADDR_CMP(&a->nexthop->router, &b->nexthop->router); } -static struct vertex *ospf_vertex_new(struct ospf_lsa *lsa) +static struct vertex *ospf_vertex_new(struct ospf_area *area, + struct ospf_lsa *lsa) { struct vertex *new; @@ -195,13 +197,14 @@ static struct vertex *ospf_vertex_new(struct ospf_lsa *lsa) lsa->stat = new; - listnode_add(&vertex_list, new); + listnode_add(area->spf_vertex_list, new); if (IS_DEBUG_OSPF_EVENT) zlog_debug("%s: Created %s vertex %s", __func__, new->type == OSPF_VERTEX_ROUTER ? "Router" : "Network", inet_ntoa(new->lsa->id)); + return new; } @@ -214,14 +217,6 @@ static void ospf_vertex_free(void *data) v->type == OSPF_VERTEX_ROUTER ? "Router" : "Network", inet_ntoa(v->lsa->id)); - /* There should be no parents potentially holding references to this - * vertex - * Children however may still be there, but presumably referenced by - * other - * vertices - */ - // assert (listcount (v->parents) == 0); - if (v->children) list_delete(&v->children); @@ -252,14 +247,12 @@ static void ospf_vertex_dump(const char *msg, struct vertex *v, if (vp) { zlog_debug( - "parent %s backlink %d nexthop %s interface %s", + "parent %s backlink %d nexthop %s lsa pos %d", inet_ntoa(vp->parent->lsa->id), vp->backlink, inet_ntop(AF_INET, &vp->nexthop->router, buf1, BUFSIZ), - vp->nexthop->oi - ? IF_NAME(vp->nexthop->oi) - : "NULL"); + vp->nexthop->lsa_pos); } } } @@ -291,15 +284,24 @@ static void ospf_vertex_add_parent(struct vertex *v) } } -static void ospf_spf_init(struct ospf_area *area) +static void ospf_spf_init(struct ospf_area *area, struct ospf_lsa *root_lsa, + bool is_dry_run, bool is_root_node) { + struct list *vertex_list; struct vertex *v; - /* Create root node. */ - v = ospf_vertex_new(area->router_lsa_self); + /* Create vertex list */ + vertex_list = list_new(); + vertex_list->del = ospf_vertex_free; + area->spf_vertex_list = vertex_list; + /* Create root node. */ + v = ospf_vertex_new(area, root_lsa); area->spf = v; + area->spf_dry_run = is_dry_run; + area->spf_root_node = is_root_node; + /* Reset ABR and ASBR router counts. */ area->abr_count = 0; area->asbr_count = 0; @@ -364,7 +366,8 @@ static int ospf_lsa_has_link(struct lsa_header *w, struct lsa_header *v) return -1; } -/* Find the next link after prev_link from v to w. If prev_link is +/* + * Find the next link after prev_link from v to w. If prev_link is * NULL, return the first link from v to w. Ignore stub and virtual links; * these link types will never be returned. */ @@ -433,10 +436,11 @@ static void ospf_spf_add_parent(struct vertex *v, struct vertex *w, assert(v && w && newhop); assert(distance); - /* IFF w has already been assigned a distance, then we shouldn't get - * here - * unless callers have determined V(l)->W is shortest / equal-shortest - * path (0 is a special case distance (no distance yet assigned)). + /* + * IFF w has already been assigned a distance, then we shouldn't get + * here unless callers have determined V(l)->W is shortest / + * equal-shortest path (0 is a special case distance (no distance yet + * assigned)). */ if (w->distance) assert(distance <= w->distance); @@ -452,7 +456,8 @@ static void ospf_spf_add_parent(struct vertex *v, struct vertex *w, sizeof(buf[1]))); } - /* Adding parent for a new, better path: flush existing parents from W. + /* + * Adding parent for a new, better path: flush existing parents from W. */ if (distance < w->distance) { if (IS_DEBUG_OSPF_EVENT) @@ -463,7 +468,8 @@ static void ospf_spf_add_parent(struct vertex *v, struct vertex *w, w->distance = distance; } - /* new parent is <= existing parents, add it to parent list (if nexthop + /* + * new parent is <= existing parents, add it to parent list (if nexthop * not on parent list) */ for (ALL_LIST_ELEMENTS_RO(w->parents, node, wp)) { @@ -482,7 +488,43 @@ static void ospf_spf_add_parent(struct vertex *v, struct vertex *w, return; } -/* 16.1.1. Calculate nexthop from root through V (parent) to +static int match_stub_prefix(struct lsa_header *lsa, struct in_addr v_link_addr, + struct in_addr w_link_addr) +{ + uint8_t *p, *lim; + struct router_lsa_link *l = NULL; + struct in_addr masked_lsa_addr; + + if (lsa->type != OSPF_ROUTER_LSA) + return 0; + + p = ((uint8_t *)lsa) + OSPF_LSA_HEADER_SIZE + 4; + lim = ((uint8_t *)lsa) + ntohs(lsa->length); + + while (p < lim) { + l = (struct router_lsa_link *)p; + p += (OSPF_ROUTER_LSA_LINK_SIZE + + (l->m[0].tos_count * OSPF_ROUTER_LSA_TOS_SIZE)); + + if (l->m[0].type != LSA_LINK_TYPE_STUB) + continue; + + masked_lsa_addr.s_addr = + (l->link_id.s_addr & l->link_data.s_addr); + + /* check that both links belong to the same stub subnet */ + if ((masked_lsa_addr.s_addr + == (v_link_addr.s_addr & l->link_data.s_addr)) + && (masked_lsa_addr.s_addr + == (w_link_addr.s_addr & l->link_data.s_addr))) + return 1; + } + + return 0; +} + +/* + * 16.1.1. Calculate nexthop from root through V (parent) to * vertex W (destination), with given distance from root->W. * * The link must be supplied if V is the root vertex. In all other cases @@ -501,7 +543,6 @@ static unsigned int ospf_nexthop_calculation(struct ospf_area *area, struct listnode *node, *nnode; struct vertex_nexthop *nh; struct vertex_parent *vp; - struct ospf_interface *oi = NULL; unsigned int added = 0; char buf1[BUFSIZ]; char buf2[BUFSIZ]; @@ -514,100 +555,123 @@ static unsigned int ospf_nexthop_calculation(struct ospf_area *area, } if (v == area->spf) { - /* 16.1.1 para 4. In the first case, the parent vertex (V) is - the - root (the calculating router itself). This means that the - destination is either a directly connected network or - directly - connected router. The outgoing interface in this case is - simply - the OSPF interface connecting to the destination - network/router. - */ + /* + * 16.1.1 para 4. In the first case, the parent vertex (V) is + * the root (the calculating router itself). This means that + * the destination is either a directly connected network or + * directly connected router. The outgoing interface in this + * case is simply the OSPF interface connecting to the + * destination network/router. + */ /* we *must* be supplied with the link data */ assert(l != NULL); - oi = ospf_if_lookup_by_lsa_pos(area, lsa_pos); - if (!oi) { - zlog_debug( - "%s: OI not found in LSA: lsa_pos:%d link_id:%s link_data:%s", - __func__, lsa_pos, - inet_ntop(AF_INET, &l->link_id, buf1, BUFSIZ), - inet_ntop(AF_INET, &l->link_data, buf2, - BUFSIZ)); - return 0; - } if (IS_DEBUG_OSPF_EVENT) { zlog_debug( - "%s: considering link:%s " - "type:%d link_id:%s link_data:%s", - __func__, oi->ifp->name, l->m[0].type, + "%s: considering link type:%d link_id:%s link_data:%s", + __func__, l->m[0].type, inet_ntop(AF_INET, &l->link_id, buf1, BUFSIZ), inet_ntop(AF_INET, &l->link_data, buf2, BUFSIZ)); } if (w->type == OSPF_VERTEX_ROUTER) { - /* l is a link from v to w - * l2 will be link from w to v + /* + * l is a link from v to w l2 will be link from w to v */ struct router_lsa_link *l2 = NULL; if (l->m[0].type == LSA_LINK_TYPE_POINTOPOINT) { + struct ospf_interface *oi = NULL; struct in_addr nexthop = {.s_addr = 0}; - /* If the destination is a router which connects - to - the calculating router via a - Point-to-MultiPoint - network, the destination's next hop IP - address(es) - can be determined by examining the - destination's - router-LSA: each link pointing back to the - calculating router and having a Link Data - field - belonging to the Point-to-MultiPoint network - provides an IP address of the next hop - router. - - At this point l is a link from V to W, and V - is the - root ("us"). If it is a point-to-multipoint - interface, - then look through the links in the opposite - direction (W to V). - If any of them have an address that lands - within the - subnet declared by the PtMP link, then that - link - is a constituent of the PtMP link, and its - address is - a nexthop address for V. - */ + oi = ospf_if_lookup_by_lsa_pos(area, lsa_pos); + if (!oi) { + zlog_debug( + "%s: OI not found in LSA: lsa_pos: %d link_id:%s link_data:%s", + __func__, lsa_pos, + inet_ntop(AF_INET, &l->link_id, + buf1, BUFSIZ), + inet_ntop(AF_INET, + &l->link_data, buf2, + BUFSIZ)); + return 0; + } + + /* + * If the destination is a router which connects + * to the calculating router via a + * Point-to-MultiPoint network, the + * destination's next hop IP address(es) can be + * determined by examining the destination's + * router-LSA: each link pointing back to the + * calculating router and having a Link Data + * field belonging to the Point-to-MultiPoint + * network provides an IP address of the next + * hop router. + * + * At this point l is a link from V to W, and V + * is the root ("us"). If it is a point-to- + * multipoint interface, then look through the + * links in the opposite direction (W to V). + * If any of them have an address that lands + * within the subnet declared by the PtMP link, + * then that link is a constituent of the PtMP + * link, and its address is a nexthop address + * for V. + * + * Note for point-to-point interfaces: + * + * Having nexthop = 0 (as proposed in the RFC) + * is tempting, but NOT acceptable. It breaks + * AS-External routes with a forwarding address, + * since ospf_ase_complete_direct_routes() will + * mistakenly assume we've reached the last hop + * and should place the forwarding address as + * nexthop. Also, users may configure multi- + * access links in p2p mode, so we need the IP + * to ARP the nexthop. + * + * If the calculating router is the SPF root + * node and the link is P2P then access the + * interface information directly. This can be + * crucial when e.g. IP unnumbered is used + * where 'correct' nexthop information are not + * available via Router LSAs. + * + * Otherwise handle P2P and P2MP the same way + * as described above using a reverse lookup to + * figure out the nexthop. + */ if (oi->type == OSPF_IFTYPE_POINTOPOINT) { - /* Having nexthop = 0 is tempting, but - NOT acceptable. - It breaks AS-External routes with a - forwarding address, - since - ospf_ase_complete_direct_routes() - will mistakenly - assume we've reached the last hop and - should place the - forwarding address as nexthop. - Also, users may configure - multi-access links in p2p mode, - so we need the IP to ARP the nexthop. - */ - struct ospf_neighbor *nbr_w; - - nbr_w = ospf_nbr_lookup_by_routerid( - oi->nbrs, &l->link_id); - if (nbr_w != NULL) { - added = 1; - nexthop = nbr_w->src; + struct ospf_neighbor *nbr_w = NULL; + + /* Calculating node is root node, link + * is P2P */ + if (area->spf_root_node) { + nbr_w = ospf_nbr_lookup_by_routerid( + oi->nbrs, &l->link_id); + if (nbr_w) { + added = 1; + nexthop = nbr_w->src; + } + } + + /* Reverse lookup */ + if (!added) { + while ((l2 = ospf_get_next_link( + w, v, l2))) { + if (match_stub_prefix( + v->lsa, + l->link_data, + l2->link_data)) { + added = 1; + nexthop = + l2->link_data; + break; + } + } } } else if (oi->type == OSPF_IFTYPE_POINTOMULTIPOINT) { @@ -616,8 +680,10 @@ static unsigned int ospf_nexthop_calculation(struct ospf_area *area, la.family = AF_INET; la.prefixlen = oi->address->prefixlen; - /* V links to W on PtMP interface - - find the interface address on W */ + /* + * V links to W on PtMP interface; + * find the interface address on W + */ while ((l2 = ospf_get_next_link(w, v, l2))) { la.prefix = l2->link_data; @@ -627,8 +693,6 @@ static unsigned int ospf_nexthop_calculation(struct ospf_area *area, oi->address) != 0) continue; - /* link_data is on our PtMP - * network */ added = 1; nexthop = l2->link_data; break; @@ -636,11 +700,9 @@ static unsigned int ospf_nexthop_calculation(struct ospf_area *area, } if (added) { - /* found all necessary info to build - * nexthop */ nh = vertex_nexthop_new(); - nh->oi = oi; nh->router = nexthop; + nh->lsa_pos = lsa_pos; ospf_spf_add_parent(v, w, nh, distance); return 1; } else @@ -649,20 +711,19 @@ static unsigned int ospf_nexthop_calculation(struct ospf_area *area, __func__, oi->ifp->name); } /* end point-to-point link from V to W */ else if (l->m[0].type == LSA_LINK_TYPE_VIRTUALLINK) { - struct ospf_vl_data *vl_data; - - /* VLink implementation limitations: - * a) vl_data can only reference one nexthop, so - * no ECMP - * to backbone through VLinks. Though - * transit-area - * summaries may be considered, and those can - * be ECMP. + /* + * VLink implementation limitations: + * a) vl_data can only reference one nexthop, + * so no ECMP to backbone through VLinks. + * Though transit-area summaries may be + * considered, and those can be ECMP. * b) We can only use /one/ VLink, even if - * multiple ones - * exist this router through multiple - * transit-areas. + * multiple ones exist this router through + * multiple transit-areas. */ + + struct ospf_vl_data *vl_data; + vl_data = ospf_vl_lookup(area->ospf, NULL, l->link_id); @@ -670,14 +731,13 @@ static unsigned int ospf_nexthop_calculation(struct ospf_area *area, && CHECK_FLAG(vl_data->flags, OSPF_VL_FLAG_APPROVED)) { nh = vertex_nexthop_new(); - nh->oi = vl_data->nexthop.oi; nh->router = vl_data->nexthop.router; + nh->lsa_pos = vl_data->nexthop.lsa_pos; ospf_spf_add_parent(v, w, nh, distance); return 1; } else zlog_info( - "ospf_nexthop_calculation(): " - "vl_data for VL link not found"); + "ospf_nexthop_calculation(): vl_data for VL link not found"); } /* end virtual-link from V to W */ return 0; } /* end W is a Router vertex */ @@ -685,8 +745,8 @@ static unsigned int ospf_nexthop_calculation(struct ospf_area *area, assert(w->type == OSPF_VERTEX_NETWORK); nh = vertex_nexthop_new(); - nh->oi = oi; nh->router.s_addr = 0; /* Nexthop not required */ + nh->lsa_pos = lsa_pos; ospf_spf_add_parent(v, w, nh, distance); return 1; } @@ -695,82 +755,70 @@ static unsigned int ospf_nexthop_calculation(struct ospf_area *area, else if (v->type == OSPF_VERTEX_NETWORK) { /* See if any of V's parents are the root. */ for (ALL_LIST_ELEMENTS(v->parents, node, nnode, vp)) { - if (vp->parent == area->spf) /* connects to root? */ - { - /* 16.1.1 para 5. ...the parent vertex is a - * network that - * directly connects the calculating router to - * the destination - * router. The list of next hops is then - * determined by - * examining the destination's router-LSA... + if (vp->parent == area->spf) { + /* + * 16.1.1 para 5. ...the parent vertex is a + * network that directly connects the + * calculating router to the destination + * router. The list of next hops is then + * determined by examining the destination's + * router-LSA ... */ assert(w->type == OSPF_VERTEX_ROUTER); while ((l = ospf_get_next_link(w, v, l))) { - /* ...For each link in the router-LSA - * that points back to the - * parent network, the link's Link Data - * field provides the IP - * address of a next hop router. The - * outgoing interface to - * use can then be derived from the next - * hop IP address (or - * it can be inherited from the parent - * network). + /* + * ... For each link in the router-LSA + * that points back to the parent + * network, the link's Link Data field + * provides the IP address of a next hop + * router. The outgoing interface to use + * can then be derived from the next + * hop IP address (or it can be + * inherited from the parent network). */ nh = vertex_nexthop_new(); - nh->oi = vp->nexthop->oi; nh->router = l->link_data; + nh->lsa_pos = vp->nexthop->lsa_pos; added = 1; ospf_spf_add_parent(v, w, nh, distance); } - /* Note lack of return is deliberate. See next - * comment. */ + /* + * Note lack of return is deliberate. See next + * comment. + */ } } - /* NB: This code is non-trivial. + /* + * NB: This code is non-trivial. * * E.g. it is not enough to know that V connects to the root. It - * is - * also important that the while above, looping through all - * links from - * W->V found at least one link, so that we know there is - * bi-directional connectivity between V and W (which need not - * be the - * case, e.g. when OSPF has not yet converged fully). - * Otherwise, if - * we /always/ return here, without having checked that - * root->V->-W - * actually resulted in a valid nexthop being created, then we - * we will - * prevent SPF from finding/using higher cost paths. + * is also important that the while above, looping through all + * links from W->V found at least one link, so that we know + * there is bi-directional connectivity between V and W (which + * need not be the case, e.g. when OSPF has not yet converged + * fully). Otherwise, if we /always/ return here, without having + * checked that root->V->-W actually resulted in a valid nexthop + * being created, then we we will prevent SPF from finding/using + * higher cost paths. * * It is important, if root->V->W has not been added, that we - * continue - * through to the intervening-router nexthop code below. So as - * to - * ensure other paths to V may be used. This avoids unnecessary - * blackholes while OSPF is convergening. + * continue through to the intervening-router nexthop code + * below. So as to ensure other paths to V may be used. This + * avoids unnecessary blackholes while OSPF is converging. * * I.e. we may have arrived at this function, examining V -> W, - * via - * workable paths other than root -> V, and it's important to - * avoid - * getting "confused" by non-working root->V->W path - it's - * important - * to *not* lose the working non-root paths, just because of a - * non-viable root->V->W. - * - * See also bug #330 (required reading!), and: - * - * http://blogs.oracle.com/paulj/entry/the_difference_a_line_makes + * via workable paths other than root -> V, and it's important + * to avoid getting "confused" by non-working root->V->W path + * - it's important to *not* lose the working non-root paths, + * just because of a non-viable root->V->W. */ if (added) return added; } - /* 16.1.1 para 4. If there is at least one intervening router in the + /* + * 16.1.1 para 4. If there is at least one intervening router in the * current shortest path between the destination and the root, the * destination simply inherits the set of next hops from the * parent. @@ -787,13 +835,13 @@ static unsigned int ospf_nexthop_calculation(struct ospf_area *area, return added; } -/* RFC2328 Section 16.1 (2). - * v is on the SPF tree. Examine the links in v's LSA. Update the list - * of candidates with any vertices not already on the list. If a lower-cost - * path is found to a vertex already on the candidate list, store the new cost. +/* + * RFC2328 16.1 (2). + * v is on the SPF tree. Examine the links in v's LSA. Update the list of + * candidates with any vertices not already on the list. If a lower-cost path + * is found to a vertex already on the candidate list, store the new cost. */ -static void ospf_spf_next(struct vertex *v, struct ospf *ospf, - struct ospf_area *area, +static void ospf_spf_next(struct vertex *v, struct ospf_area *area, struct vertex_pqueue_head *candidate) { struct ospf_lsa *w_lsa = NULL; @@ -803,8 +851,10 @@ static void ospf_spf_next(struct vertex *v, struct ospf *ospf, struct in_addr *r; int type = 0, lsa_pos = -1, lsa_pos_next = 0; - /* If this is a router-LSA, and bit V of the router-LSA (see Section - A.4.2:RFC2328) is set, set Area A's TransitCapability to TRUE. */ + /* + * If this is a router-LSA, and bit V of the router-LSA (see Section + * A.4.2:RFC2328) is set, set Area A's TransitCapability to true. + */ if (v->type == OSPF_VERTEX_ROUTER) { if (IS_ROUTER_LSA_VIRTUAL((struct router_lsa *)v->lsa)) area->transit = OSPF_TRANSIT_TRUE; @@ -828,40 +878,39 @@ static void ospf_spf_next(struct vertex *v, struct ospf *ospf, lsa_pos = lsa_pos_next; /* LSA link position */ lsa_pos_next++; + p += (OSPF_ROUTER_LSA_LINK_SIZE + (l->m[0].tos_count * OSPF_ROUTER_LSA_TOS_SIZE)); - /* (a) If this is a link to a stub network, examine the - next - link in V's LSA. Links to stub networks will be - considered in the second stage of the shortest path - calculation. */ + /* + * (a) If this is a link to a stub network, examine the + * next link in V's LSA. Links to stub networks will + * be considered in the second stage of the shortest + * path calculation. + */ if ((type = l->m[0].type) == LSA_LINK_TYPE_STUB) continue; - /* (b) Otherwise, W is a transit vertex (router or - transit - network). Look up the vertex W's LSA (router-LSA or - network-LSA) in Area A's link state database. */ + /* + * (b) Otherwise, W is a transit vertex (router or + * transit network). Look up the vertex W's LSA + * (router-LSA or network-LSA) in Area A's link state + * database. + */ switch (type) { case LSA_LINK_TYPE_POINTOPOINT: case LSA_LINK_TYPE_VIRTUALLINK: - if (type == LSA_LINK_TYPE_VIRTUALLINK) { - if (IS_DEBUG_OSPF_EVENT) - zlog_debug( - "looking up LSA through VL: %s", - inet_ntoa(l->link_id)); - } - - w_lsa = ospf_lsa_lookup(ospf, area, + if (type == LSA_LINK_TYPE_VIRTUALLINK + && IS_DEBUG_OSPF_EVENT) + zlog_debug( + "looking up LSA through VL: %s", + inet_ntoa(l->link_id)); + w_lsa = ospf_lsa_lookup(area->ospf, area, OSPF_ROUTER_LSA, l->link_id, l->link_id); - if (w_lsa) { - if (IS_DEBUG_OSPF_EVENT) - zlog_debug( - "found Router LSA %s", - inet_ntoa(l->link_id)); - } + if (w_lsa && IS_DEBUG_OSPF_EVENT) + zlog_debug("found Router LSA %s", + inet_ntoa(l->link_id)); break; case LSA_LINK_TYPE_TRANSIT: if (IS_DEBUG_OSPF_EVENT) @@ -870,15 +919,17 @@ static void ospf_spf_next(struct vertex *v, struct ospf *ospf, inet_ntoa(l->link_id)); w_lsa = ospf_lsa_lookup_by_id( area, OSPF_NETWORK_LSA, l->link_id); - if (w_lsa) - if (IS_DEBUG_OSPF_EVENT) - zlog_debug("found the LSA"); + if (w_lsa && IS_DEBUG_OSPF_EVENT) + zlog_debug("found the LSA"); break; default: flog_warn(EC_OSPF_LSA, "Invalid LSA link type %d", type); continue; } + + /* step (d) below */ + distance = v->distance + ntohs(l->m[0].metric); } else { /* In case of V is Network-LSA. */ r = (struct in_addr *)p; @@ -887,16 +938,19 @@ static void ospf_spf_next(struct vertex *v, struct ospf *ospf, /* Lookup the vertex W's LSA. */ w_lsa = ospf_lsa_lookup_by_id(area, OSPF_ROUTER_LSA, *r); - if (w_lsa) { - if (IS_DEBUG_OSPF_EVENT) - zlog_debug("found Router LSA %s", - inet_ntoa(w_lsa->data->id)); - } + if (w_lsa && IS_DEBUG_OSPF_EVENT) + zlog_debug("found Router LSA %s", + inet_ntoa(w_lsa->data->id)); + + /* step (d) below */ + distance = v->distance; } - /* (b cont.) If the LSA does not exist, or its LS age is equal - to MaxAge, or it does not have a link back to vertex V, - examine the next link in V's LSA.[23] */ + /* + * (b cont.) If the LSA does not exist, or its LS age is equal + * to MaxAge, or it does not have a link back to vertex V, + * examine the next link in V's LSA.[23] + */ if (w_lsa == NULL) { if (IS_DEBUG_OSPF_EVENT) zlog_debug("No LSA found"); @@ -915,30 +969,30 @@ static void ospf_spf_next(struct vertex *v, struct ospf *ospf, continue; } - /* (c) If vertex W is already on the shortest-path tree, examine - the next link in the LSA. */ + /* + * (c) If vertex W is already on the shortest-path tree, examine + * the next link in the LSA. + */ if (w_lsa->stat == LSA_SPF_IN_SPFTREE) { if (IS_DEBUG_OSPF_EVENT) zlog_debug("The LSA is already in SPF"); continue; } - /* (d) Calculate the link state cost D of the resulting path - from the root to vertex W. D is equal to the sum of the link - state cost of the (already calculated) shortest path to - vertex V and the advertised cost of the link between vertices - V and W. If D is: */ + /* + * (d) Calculate the link state cost D of the resulting path + * from the root to vertex W. D is equal to the sum of the link + * state cost of the (already calculated) shortest path to + * vertex V and the advertised cost of the link between vertices + * V and W. If D is: + */ - /* calculate link cost D. */ - if (v->lsa->type == OSPF_ROUTER_LSA) - distance = v->distance + ntohs(l->m[0].metric); - else /* v is not a Router-LSA */ - distance = v->distance; + /* calculate link cost D -- moved above */ /* Is there already vertex W in candidate list? */ if (w_lsa->stat == LSA_SPF_NOT_EXPLORED) { /* prepare vertex W. */ - w = ospf_vertex_new(w_lsa); + w = ospf_vertex_new(area, w_lsa); /* Calculate nexthop to W. */ if (ospf_nexthop_calculation(area, v, w, l, distance, @@ -948,29 +1002,28 @@ static void ospf_spf_next(struct vertex *v, struct ospf *ospf, zlog_debug("Nexthop Calc failed"); } else if (w_lsa->stat != LSA_SPF_IN_SPFTREE) { w = w_lsa->stat; - /* if D is greater than. */ if (w->distance < distance) { continue; } - /* equal to. */ else if (w->distance == distance) { - /* Found an equal-cost path to W. - * Calculate nexthop of to W from V. */ + /* + * Found an equal-cost path to W. + * Calculate nexthop of to W from V. + */ ospf_nexthop_calculation(area, v, w, l, distance, lsa_pos); } - /* less than. */ else { - /* Found a lower-cost path to W. + /* + * Found a lower-cost path to W. * nexthop_calculation is conditional, if it - * finds - * valid nexthop it will call spf_add_parents, - * which - * will flush the old parents + * finds valid nexthop it will call + * spf_add_parents, which will flush the old + * parents. */ vertex_pqueue_del(candidate, w); ospf_nexthop_calculation(area, v, w, l, - distance, lsa_pos); + distance, lsa_pos); vertex_pqueue_add(candidate, w); } } /* end W is already on the candidate list */ @@ -997,11 +1050,9 @@ static void ospf_spf_dump(struct vertex *v, int i) if (IS_DEBUG_OSPF_EVENT) for (ALL_LIST_ELEMENTS_RO(v->parents, nnode, parent)) { - zlog_debug(" nexthop %p %s %s", (void *)parent->nexthop, + zlog_debug(" nexthop %p %s %d", (void *)parent->nexthop, inet_ntoa(parent->nexthop->router), - parent->nexthop->oi - ? IF_NAME(parent->nexthop->oi) - : "NULL"); + parent->nexthop->lsa_pos); } i++; @@ -1010,6 +1061,33 @@ static void ospf_spf_dump(struct vertex *v, int i) ospf_spf_dump(v, i); } +void ospf_spf_print(struct vty *vty, struct vertex *v, int i) +{ + struct listnode *cnode; + struct listnode *nnode; + struct vertex_parent *parent; + + if (v->type == OSPF_VERTEX_ROUTER) { + vty_out(vty, "SPF Result: depth %d [R] %s\n", i, + inet_ntoa(v->lsa->id)); + } else { + struct network_lsa *lsa = (struct network_lsa *)v->lsa; + vty_out(vty, "SPF Result: depth %d [N] %s/%d\n", i, + inet_ntoa(v->lsa->id), ip_masklen(lsa->mask)); + } + + for (ALL_LIST_ELEMENTS_RO(v->parents, nnode, parent)) { + vty_out(vty, " nexthop %s lsa pos %d\n", + inet_ntoa(parent->nexthop->router), + parent->nexthop->lsa_pos); + } + + i++; + + for (ALL_LIST_ELEMENTS_RO(v->children, cnode, v)) + ospf_spf_print(vty, v, i); +} + /* Second stage of SPF calculation. */ static void ospf_spf_process_stubs(struct ospf_area *area, struct vertex *v, struct route_table *rt, int parent_is_root) @@ -1020,24 +1098,26 @@ static void ospf_spf_process_stubs(struct ospf_area *area, struct vertex *v, if (IS_DEBUG_OSPF_EVENT) zlog_debug("ospf_process_stub():processing stubs for area %s", inet_ntoa(area->area_id)); + if (v->type == OSPF_VERTEX_ROUTER) { uint8_t *p; uint8_t *lim; struct router_lsa_link *l; - struct router_lsa *rlsa; + struct router_lsa *router_lsa; int lsa_pos = 0; if (IS_DEBUG_OSPF_EVENT) zlog_debug( "ospf_process_stubs():processing router LSA, id: %s", inet_ntoa(v->lsa->id)); - rlsa = (struct router_lsa *)v->lsa; + router_lsa = (struct router_lsa *)v->lsa; if (IS_DEBUG_OSPF_EVENT) zlog_debug( "ospf_process_stubs(): we have %d links to process", - ntohs(rlsa->links)); + ntohs(router_lsa->links)); + p = ((uint8_t *)v->lsa) + OSPF_LSA_HEADER_SIZE + 4; lim = ((uint8_t *)v->lsa) + ntohs(v->lsa->length); @@ -1061,7 +1141,8 @@ static void ospf_spf_process_stubs(struct ospf_area *area, struct vertex *v, if (CHECK_FLAG(child->flags, OSPF_VERTEX_PROCESSED)) continue; - /* the first level of routers connected to the root + /* + * The first level of routers connected to the root * should have 'parent_is_root' set, including those * connected via a network vertex. */ @@ -1097,9 +1178,23 @@ void ospf_rtrs_free(struct route_table *rtrs) rn->info = NULL; route_unlock_node(rn); } + route_table_finish(rtrs); } +void ospf_spf_cleanup(struct vertex *spf, struct list *vertex_list) +{ + /* + * Free nexthop information, canonical versions of which are + * attached the first level of router vertices attached to the + * root vertex, see ospf_nexthop_calculation. + */ + ospf_canonical_nexthops_free(spf); + + /* Free SPF vertices list with deconstructor ospf_vertex_free. */ + list_delete(&vertex_list); +} + #if 0 static void ospf_rtrs_print (struct route_table *rtrs) @@ -1162,10 +1257,11 @@ ospf_rtrs_print (struct route_table *rtrs) } #endif -/* Calculating the shortest-path tree for an area. */ -static void ospf_spf_calculate(struct ospf *ospf, struct ospf_area *area, - struct route_table *new_table, - struct route_table *new_rtrs) +/* Calculating the shortest-path tree for an area, see RFC2328 16.1. */ +void ospf_spf_calculate(struct ospf_area *area, struct ospf_lsa *root_lsa, + struct route_table *new_table, + struct route_table *new_rtrs, bool is_dry_run, + bool is_root_node) { struct vertex_pqueue_head candidate; struct vertex *v; @@ -1176,56 +1272,57 @@ static void ospf_spf_calculate(struct ospf *ospf, struct ospf_area *area, inet_ntoa(area->area_id)); } - /* Check router-lsa-self. If self-router-lsa is not yet allocated, - return this area's calculation. */ - if (!area->router_lsa_self) { + /* + * If the router LSA of the root is not yet allocated, return this + * area's calculation. In the 'usual' case the root_lsa is the + * self-originated router LSA of the node itself. + */ + if (!root_lsa) { if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_spf_calculate: " - "Skip area %s's calculation due to empty router_lsa_self", + "ospf_spf_calculate: Skip area %s's calculation due to empty root LSA", inet_ntoa(area->area_id)); return; } - /* RFC2328 16.1. (1). */ - /* Initialize the algorithm's data structures. */ + /* Initialize the algorithm's data structures, see RFC2328 16.1. (1). */ - /* This function scans all the LSA database and set the stat field to - * LSA_SPF_NOT_EXPLORED. */ + /* + * This function scans all the LSA database and set the stat field to + * LSA_SPF_NOT_EXPLORED. + */ lsdb_clean_stat(area->lsdb); + /* Create a new heap for the candidates. */ vertex_pqueue_init(&candidate); - /* Initialize the shortest-path tree to only the root (which is the - router doing the calculation). */ - ospf_spf_init(area); - v = area->spf; - /* Set LSA position to LSA_SPF_IN_SPFTREE. This vertex is the root of - * the - * spanning tree. */ - v->lsa_p->stat = LSA_SPF_IN_SPFTREE; + /* + * Initialize the shortest-path tree to only the root (which is usually + * the router doing the calculation). + */ + ospf_spf_init(area, root_lsa, is_dry_run, is_root_node); - /* Set Area A's TransitCapability to FALSE. */ + /* Set Area A's TransitCapability to false. */ area->transit = OSPF_TRANSIT_FALSE; area->shortcut_capability = 1; + /* + * Use the root vertex for the start of the SPF algorithm and make it + * part of the tree. + */ + v = area->spf; + v->lsa_p->stat = LSA_SPF_IN_SPFTREE; + for (;;) { /* RFC2328 16.1. (2). */ - ospf_spf_next(v, ospf, area, &candidate); + ospf_spf_next(v, area, &candidate); /* RFC2328 16.1. (3). */ - /* If at this step the candidate list is empty, the shortest- - path tree (of transit vertices) has been completely built and - this stage of the procedure terminates. */ - /* Otherwise, choose the vertex belonging to the candidate list - that is closest to the root, and add it to the shortest-path - tree (removing it from the candidate list in the - process). */ - /* Extract from the candidates the node with the lower key. */ v = vertex_pqueue_pop(&candidate); if (!v) + /* No more vertices left. */ break; - /* Update stat field in vertex. */ + v->lsa_p->stat = LSA_SPF_IN_SPFTREE; ospf_vertex_add_parent(v); @@ -1236,28 +1333,21 @@ static void ospf_spf_calculate(struct ospf *ospf, struct ospf_area *area, else ospf_intra_add_transit(new_table, v, area); - /* RFC2328 16.1. (5). */ - /* Iterate the algorithm by returning to Step 2. */ - - } /* end loop until no more candidate vertices */ + /* Iterate back to (2), see RFC2328 16.1. (5). */ + } if (IS_DEBUG_OSPF_EVENT) { ospf_spf_dump(area->spf, 0); ospf_route_table_dump(new_table); } - /* Second stage of SPF calculation procedure's */ + /* + * Second stage of SPF calculation procedure's, add leaves to the tree + * for stub networks. + */ ospf_spf_process_stubs(area, area->spf, new_table, 0); - /* Free candidate queue. */ - //vertex_pqueue_fini(&candidate); - ospf_vertex_dump(__func__, area->spf, 0, 1); - /* Free nexthop information, canonical versions of which are attached - * the first level of router vertices attached to the root vertex, see - * ospf_nexthop_calculation. - */ - ospf_canonical_nexthops_free(area->spf); /* Increment SPF Calculation Counter. */ area->spf_calculation++; @@ -1269,105 +1359,122 @@ static void ospf_spf_calculate(struct ospf *ospf, struct ospf_area *area, zlog_debug("ospf_spf_calculate: Stop. %zd vertices", mtype_stats_alloc(MTYPE_OSPF_VERTEX)); - /* Free SPF vertices, but not the list. List has ospf_vertex_free - * as deconstructor. - */ - list_delete_all_node(&vertex_list); + /* If this is a dry run then keep the SPF data in place */ + if (!area->spf_dry_run) + ospf_spf_cleanup(area->spf, area->spf_vertex_list); } -/* Timer for SPF calculation. */ -static int ospf_spf_calculate_timer(struct thread *thread) +int ospf_spf_calculate_areas(struct ospf *ospf, struct route_table *new_table, + struct route_table *new_rtrs, bool is_dry_run, + bool is_root_node) { - struct ospf *ospf = THREAD_ARG(thread); - struct route_table *new_table, *new_rtrs; struct ospf_area *area; struct listnode *node, *nnode; - struct timeval start_time, spf_start_time; int areas_processed = 0; - unsigned long ia_time, prune_time, rt_time; - unsigned long abr_time, total_spf_time, spf_time; - char rbuf[32]; /* reason_buf */ - - if (IS_DEBUG_OSPF_EVENT) - zlog_debug("SPF: Timer (SPF calculation expire)"); - - ospf->t_spf_calc = NULL; - - monotime(&spf_start_time); - /* Allocate new table tree. */ - new_table = route_table_init(); - new_rtrs = route_table_init(); - - ospf_vl_unapprove(ospf); /* Calculate SPF for each area. */ for (ALL_LIST_ELEMENTS(ospf->areas, node, nnode, area)) { /* Do backbone last, so as to first discover intra-area paths - * for any back-bone virtual-links - */ + * for any back-bone virtual-links */ if (ospf->backbone && ospf->backbone == area) continue; - ospf_spf_calculate(ospf, area, new_table, new_rtrs); + ospf_spf_calculate(area, area->router_lsa_self, new_table, + new_rtrs, is_dry_run, is_root_node); areas_processed++; } /* SPF for backbone, if required */ if (ospf->backbone) { - ospf_spf_calculate(ospf, ospf->backbone, new_table, new_rtrs); + area = ospf->backbone; + ospf_spf_calculate(area, area->router_lsa_self, new_table, + new_rtrs, is_dry_run, is_root_node); areas_processed++; } + return areas_processed; +} + +/* Worker for SPF calculation scheduler. */ +static int ospf_spf_calculate_schedule_worker(struct thread *thread) +{ + struct ospf *ospf = THREAD_ARG(thread); + struct route_table *new_table, *new_rtrs; + struct timeval start_time, spf_start_time; + int areas_processed; + unsigned long ia_time, prune_time, rt_time; + unsigned long abr_time, total_spf_time, spf_time; + char rbuf[32]; /* reason_buf */ + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("SPF: Timer (SPF calculation expire)"); + + ospf->t_spf_calc = NULL; + + ospf_vl_unapprove(ospf); + + /* Execute SPF for each area including backbone, see RFC 2328 16.1. */ + monotime(&spf_start_time); + new_table = route_table_init(); /* routing table */ + new_rtrs = route_table_init(); /* ABR/ASBR routing table */ + areas_processed = ospf_spf_calculate_areas(ospf, new_table, new_rtrs, + false, true); spf_time = monotime_since(&spf_start_time, NULL); ospf_vl_shut_unapproved(ospf); + /* Calculate inter-area routes, see RFC 2328 16.2. */ monotime(&start_time); ospf_ia_routing(ospf, new_table, new_rtrs); ia_time = monotime_since(&start_time, NULL); + /* Get rid of transit networks and routers we cannot reach anyway. */ monotime(&start_time); ospf_prune_unreachable_networks(new_table); ospf_prune_unreachable_routers(new_rtrs); prune_time = monotime_since(&start_time, NULL); - /* AS-external-LSA calculation should not be performed here. */ - - /* If new Router Route is installed, - then schedule re-calculate External routes. */ - if (1) - ospf_ase_calculate_schedule(ospf); + /* Note: RFC 2328 16.3. is apparently missing. */ + /* + * Calculate AS external routes, see RFC 2328 16.4. + * There is a dedicated routing table for external routes which is not + * handled here directly + */ + ospf_ase_calculate_schedule(ospf); ospf_ase_calculate_timer_add(ospf); if (IS_DEBUG_OSPF_EVENT) zlog_debug( "%s: ospf install new route, vrf %s id %u new_table count %lu", - __PRETTY_FUNCTION__, ospf_vrf_id_to_name(ospf->vrf_id), + __func__, ospf_vrf_id_to_name(ospf->vrf_id), ospf->vrf_id, new_table->count); + /* Update routing table. */ monotime(&start_time); ospf_route_install(ospf, new_table); rt_time = monotime_since(&start_time, NULL); - /* Update ABR/ASBR routing table */ - if (ospf->old_rtrs) { - /* old_rtrs's node holds linked list of ospf_route. --kunihiro. - */ + /* Free old ABR/ASBR routing table */ + if (ospf->old_rtrs) /* ospf_route_delete (ospf->old_rtrs); */ ospf_rtrs_free(ospf->old_rtrs); - } + /* Update ABR/ASBR routing table */ ospf->old_rtrs = ospf->new_rtrs; ospf->new_rtrs = new_rtrs; + /* ABRs may require additional changes, see RFC 2328 16.7. */ monotime(&start_time); - if (IS_OSPF_ABR(ospf)) + if (IS_OSPF_ABR(ospf)) { + if (ospf->anyNSSA) + ospf_abr_nssa_check_status(ospf); ospf_abr_task(ospf); + } abr_time = monotime_since(&start_time, NULL); /* Schedule Segment Routing update */ - ospf_sr_update_timer_add(ospf); + ospf_sr_update_task(ospf); total_spf_time = monotime_since(&spf_start_time, &ospf->ts_spf_duration); @@ -1399,13 +1506,13 @@ static int ospf_spf_calculate_timer(struct thread *thread) if (IS_DEBUG_OSPF_EVENT) { zlog_info("SPF Processing Time(usecs): %ld", total_spf_time); - zlog_info("\t SPF Time: %ld", spf_time); - zlog_info("\t InterArea: %ld", ia_time); - zlog_info("\t Prune: %ld", prune_time); - zlog_info("\tRouteInstall: %ld", rt_time); + zlog_info(" SPF Time: %ld", spf_time); + zlog_info(" InterArea: %ld", ia_time); + zlog_info(" Prune: %ld", prune_time); + zlog_info(" RouteInstall: %ld", rt_time); if (IS_OSPF_ABR(ospf)) - zlog_info("\t ABR: %ld (%d areas)", abr_time, - areas_processed); + zlog_info(" ABR: %ld (%d areas)", + abr_time, areas_processed); zlog_info("Reason(s) for SPF: %s", rbuf); } @@ -1414,8 +1521,10 @@ static int ospf_spf_calculate_timer(struct thread *thread) return 0; } -/* Add schedule for SPF calculation. To avoid frequenst SPF calc, we - set timer for SPF calc. */ +/* + * Add schedule for SPF calculation. To avoid frequenst SPF calc, we set timer + * for SPF calc. + */ void ospf_spf_calculate_schedule(struct ospf *ospf, ospf_spf_reason_t reason) { unsigned long delay, elapsed, ht; @@ -1447,9 +1556,10 @@ void ospf_spf_calculate_schedule(struct ospf *ospf, ospf_spf_reason_t reason) /* Get SPF calculation delay time. */ if (elapsed < ht) { - /* Got an event within the hold time of last SPF. We need to + /* + * Got an event within the hold time of last SPF. We need to * increase the hold_multiplier, if it's not already at/past - * maximum value, and wasn't already increased.. + * maximum value, and wasn't already increased. */ if (ht < ospf->spf_max_holdtime) ospf->spf_hold_multiplier++; @@ -1469,6 +1579,6 @@ void ospf_spf_calculate_schedule(struct ospf *ospf, ospf_spf_reason_t reason) zlog_debug("SPF: calculation timer delay = %ld msec", delay); ospf->t_spf_calc = NULL; - thread_add_timer_msec(master, ospf_spf_calculate_timer, ospf, delay, - &ospf->t_spf_calc); + thread_add_timer_msec(master, ospf_spf_calculate_schedule_worker, ospf, + delay, &ospf->t_spf_calc); } diff --git a/ospfd/ospf_spf.h b/ospfd/ospf_spf.h index 09a0b6f1b7..2dc0f8b886 100644 --- a/ospfd/ospf_spf.h +++ b/ospfd/ospf_spf.h @@ -49,15 +49,14 @@ struct vertex { /* A nexthop taken on the root node to get to this (parent) vertex */ struct vertex_nexthop { - struct ospf_interface *oi; /* output intf on root node */ struct in_addr router; /* router address to send to */ + int lsa_pos; /* LSA position for resolving the interface */ }; struct vertex_parent { - struct vertex_nexthop - *nexthop; /* link to nexthop info for this parent */ - struct vertex *parent; /* parent vertex */ - int backlink; /* index back to parent for router-lsa's */ + struct vertex_nexthop *nexthop; /* nexthop address for this parent */ + struct vertex *parent; /* parent vertex */ + int backlink; /* index back to parent for router-lsa's */ }; /* What triggered the SPF ? */ @@ -73,7 +72,19 @@ typedef enum { } ospf_spf_reason_t; extern void ospf_spf_calculate_schedule(struct ospf *, ospf_spf_reason_t); +extern void ospf_spf_calculate(struct ospf_area *area, + struct ospf_lsa *root_lsa, + struct route_table *new_table, + struct route_table *new_rtrs, bool is_dry_run, + bool is_root_node); +extern int ospf_spf_calculate_areas(struct ospf *ospf, + struct route_table *new_table, + struct route_table *new_rtrs, + bool is_dry_run, bool is_root_node); extern void ospf_rtrs_free(struct route_table *); +extern void ospf_spf_cleanup(struct vertex *spf, struct list *vertex_list); + +extern void ospf_spf_print(struct vty *vty, struct vertex *v, int i); /* void ospf_spf_calculate_timer_add (); */ #endif /* _QUAGGA_OSPF_SPF_H */ diff --git a/ospfd/ospf_sr.c b/ospfd/ospf_sr.c index a493520868..eb882c5d0e 100644 --- a/ospfd/ospf_sr.c +++ b/ospfd/ospf_sr.c @@ -1,13 +1,14 @@ /* * This is an implementation of Segment Routing - * as per draft draft-ietf-ospf-segment-routing-extensions-24 + * as per RFC 8665 - OSPF Extensions for Segment Routing + * and RFC 8476 - Signaling Maximum SID Depth (MSD) Using OSPF * * Module name: Segment Routing * * Author: Olivier Dugeon * Author: Anselme Sawadogo * - * Copyright (C) 2016 - 2018 Orange Labs http://www.orange.com + * Copyright (C) 2016 - 2020 Orange Labs http://www.orange.com * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -33,6 +34,7 @@ #include #include +#include "printfrr.h" #include "command.h" #include "hash.h" #include "if.h" @@ -51,6 +53,7 @@ #include "thread.h" #include "vty.h" #include "zclient.h" +#include "sbuf.h" #include #include "ospf_errors.h" @@ -79,14 +82,15 @@ */ static struct ospf_sr_db OspfSR; static void ospf_sr_register_vty(void); -static inline void del_sid_nhlfe(struct sr_nhlfe nhlfe); +static inline void del_adj_sid(struct sr_nhlfe nhlfe); +static int ospf_sr_start(struct ospf *ospf); /* * Segment Routing Data Base functions */ /* Hash function for Segment Routing entry */ -static unsigned int sr_hash(void *p) +static unsigned int sr_hash(const void *p) { const struct in_addr *rid = p; @@ -107,8 +111,8 @@ static void del_sr_link(void *val) { struct sr_link *srl = (struct sr_link *)val; - del_sid_nhlfe(srl->nhlfe[0]); - del_sid_nhlfe(srl->nhlfe[1]); + del_adj_sid(srl->nhlfe[0]); + del_adj_sid(srl->nhlfe[1]); XFREE(MTYPE_OSPF_SR_PARAMS, val); } @@ -117,7 +121,7 @@ static void del_sr_pref(void *val) { struct sr_prefix *srp = (struct sr_prefix *)val; - del_sid_nhlfe(srp->nhlfe); + ospf_zebra_delete_prefix_sid(srp); XFREE(MTYPE_OSPF_SR_PARAMS, val); } @@ -151,9 +155,7 @@ static struct sr_node *sr_node_new(struct in_addr *rid) new->neighbor = NULL; new->instance = 0; - if (IS_DEBUG_OSPF_SR) - zlog_debug(" |- Created new SR node for %s", - inet_ntoa(new->adv_router)); + osr_debug(" |- Created new SR node for %pI4", &new->adv_router); return new; } @@ -164,6 +166,8 @@ static void sr_node_del(struct sr_node *srn) if (srn == NULL) return; + osr_debug(" |- Delete SR node for %pI4", &srn->adv_router); + /* Clean Extended Link */ list_delete(&srn->ext_link); @@ -188,9 +192,7 @@ static struct sr_node *get_sr_node_by_nexthop(struct ospf *ospf, if (OspfSR.neighbors == NULL) return NULL; - if (IS_DEBUG_OSPF_SR) - zlog_debug(" |- Search SR-Node for nexthop %s", - inet_ntoa(nexthop)); + osr_debug(" |- Search SR-Node for nexthop %pI4", &nexthop); /* First, search neighbor Router ID for this nexthop */ found = false; @@ -209,19 +211,214 @@ static struct sr_node *get_sr_node_by_nexthop(struct ospf *ospf, if (!found) return NULL; - if (IS_DEBUG_OSPF_SR) - zlog_debug(" |- Found nexthop Router ID %s", - inet_ntoa(nbr->router_id)); + osr_debug(" |- Found nexthop Router ID %pI4", &nbr->router_id); + /* Then, search SR Node */ srn = (struct sr_node *)hash_lookup(OspfSR.neighbors, &nbr->router_id); return srn; } +/* + * Segment Routing Local Block management functions + */ + +/** + * It is necessary to known which label is already allocated to manage the range + * of SRLB. This is particular useful when an interface flap (goes up / down + * frequently). Here, SR will release and then allocate label for the Adjacency + * for each concerned interface. If we don't care, there is a risk to run out of + * label. + * + * For that purpose, a similar principle as already provided to manage chunk of + * label is proposed. But, here, the label chunk has not a fix range of 64 + * labels that could be easily manage with a single variable of 64 bits size. + * So, used_mark is used as a bit wise to mark label reserved (bit set) or not + * (bit unset). Its size is equal to the number of label of the SRLB range round + * up to 64 bits. + * + * - sr__local_block_init() computes the number of 64 bits variables that are + * needed to manage the SRLB range and allocates this number. + * - ospf_sr_local_block_request_label() pick up the first available label and + * set corresponding bit + * - ospf_sr_local_block_release_label() release label by reseting the + * corresponding bit and set the next label to the first free position + */ + +/** + * Initialize Segment Routing Local Block from SRDB configuration and reserve + * block of bits to manage label allocation. + * + * @param lower_bound The lower bound of the SRLB range + * @param upper_bound The upper bound of the SRLB range + * + * @return 0 on success, -1 otherwise + */ +static int sr_local_block_init(uint32_t lower_bound, uint32_t upper_bound) +{ + struct sr_local_block *srlb = &OspfSR.srlb; + uint32_t size; + + /* Check if SRLB is not already configured */ + if (srlb->reserved) + return 0; + + /* + * Request SRLB to the label manager. If the allocation fails, return + * an error to disable SR until a new SRLB is successfully allocated. + */ + size = upper_bound - lower_bound + 1; + if (ospf_zebra_request_label_range(lower_bound, size)) { + srlb->reserved = false; + return -1; + } + + osr_debug("SR (%s): Got new SRLB [%u/%u]", __func__, lower_bound, + upper_bound); + + /* Initialize the SRLB */ + srlb->start = lower_bound; + srlb->end = upper_bound; + srlb->current = 0; + /* Compute the needed Used Mark number and allocate them */ + srlb->max_block = size / SRLB_BLOCK_SIZE; + if ((size % SRLB_BLOCK_SIZE) != 0) + srlb->max_block++; + srlb->used_mark = XCALLOC(MTYPE_OSPF_SR_PARAMS, + srlb->max_block * SRLB_BLOCK_SIZE); + srlb->reserved = true; + + return 0; +} + +/** + * Remove Segment Routing Local Block. + * + */ +static void sr_local_block_delete() +{ + struct sr_local_block *srlb = &OspfSR.srlb; + + /* Check if SRLB is not already delete */ + if (!srlb->reserved) + return; + + osr_debug("SR (%s): Remove SRLB [%u/%u]", __func__, srlb->start, + srlb->end); + + /* First release the label block */ + ospf_zebra_release_label_range(srlb->start, srlb->end); + + /* Then reset SRLB structure */ + if (srlb->used_mark != NULL) + XFREE(MTYPE_OSPF_SR_PARAMS, srlb->used_mark); + srlb->reserved = false; +} + +/** + * Request a label from the Segment Routing Local Block. + * + * @return First available label on success or MPLS_INVALID_LABEL if the + * block of labels is full + */ +mpls_label_t ospf_sr_local_block_request_label(void) +{ + struct sr_local_block *srlb = &OspfSR.srlb; + mpls_label_t label; + uint32_t index; + uint32_t pos; + + /* Check if we ran out of available labels */ + if (srlb->current >= srlb->end) + return MPLS_INVALID_LABEL; + + /* Get first available label and mark it used */ + label = srlb->current + srlb->start; + index = srlb->current / SRLB_BLOCK_SIZE; + pos = 1ULL << (srlb->current % SRLB_BLOCK_SIZE); + srlb->used_mark[index] |= pos; + + /* Jump to the next free position */ + srlb->current++; + pos = srlb->current % SRLB_BLOCK_SIZE; + while (srlb->current < srlb->end) { + if (pos == 0) + index++; + if (!((1ULL << pos) & srlb->used_mark[index])) + break; + else { + srlb->current++; + pos = srlb->current % SRLB_BLOCK_SIZE; + } + } + + return label; +} + +/** + * Release label in the Segment Routing Local Block. + * + * @param label Label to be release + * + * @return 0 on success or -1 if label falls outside SRLB + */ +int ospf_sr_local_block_release_label(mpls_label_t label) +{ + struct sr_local_block *srlb = &OspfSR.srlb; + uint32_t index; + uint32_t pos; + + /* Check that label falls inside the SRLB */ + if ((label < srlb->start) || (label > srlb->end)) { + flog_warn(EC_OSPF_SR_SID_OVERFLOW, + "%s: Returning label %u is outside SRLB [%u/%u]", + __func__, label, srlb->start, srlb->end); + return -1; + } + + index = (label - srlb->start) / SRLB_BLOCK_SIZE; + pos = 1ULL << ((label - srlb->start) % SRLB_BLOCK_SIZE); + srlb->used_mark[index] &= ~pos; + /* Reset current to the first available position */ + for (index = 0; index < srlb->max_block; index++) { + if (srlb->used_mark[index] != 0xFFFFFFFFFFFFFFFF) { + for (pos = 0; pos < SRLB_BLOCK_SIZE; pos++) + if (!((1ULL << pos) & srlb->used_mark[index])) { + srlb->current = + index * SRLB_BLOCK_SIZE + pos; + break; + } + break; + } + } + + return 0; +} + /* * Segment Routing Initialization functions */ +/** + * Thread function to re-attempt connection to the Label Manager and thus be + * able to start Segment Routing. + * + * @param start Thread structure that contains area as argument + * + * @return 1 on success + */ +static int sr_start_label_manager(struct thread *start) +{ + struct ospf *ospf; + + ospf = THREAD_ARG(start); + + /* re-attempt to start SR & Label Manager connection */ + ospf_sr_start(ospf); + + return 1; +} + /* Segment Routing starter function */ static int ospf_sr_start(struct ospf *ospf) { @@ -230,22 +427,63 @@ static int ospf_sr_start(struct ospf *ospf) struct sr_node *srn; int rc = 0; - if (IS_DEBUG_OSPF_SR) - zlog_debug("SR (%s): Start Segment Routing", __func__); + osr_debug("SR (%s): Start Segment Routing", __func__); + + /* Initialize self SR Node if not already done */ + if (OspfSR.self == NULL) { + srn = hash_get(OspfSR.neighbors, (void *)&(ospf->router_id), + (void *)sr_node_new); + + /* Complete & Store self SR Node */ + srn->srgb.range_size = OspfSR.srgb.size; + srn->srgb.lower_bound = OspfSR.srgb.start; + srn->srlb.lower_bound = OspfSR.srlb.start; + srn->srlb.range_size = OspfSR.srlb.end - OspfSR.srlb.start + 1; + srn->algo[0] = OspfSR.algo[0]; + srn->msd = OspfSR.msd; + OspfSR.self = srn; + } + + /* Then, start Label Manager if not ready */ + if (!ospf_zebra_label_manager_ready()) + if (ospf_zebra_label_manager_connect() < 0) { + /* Re-attempt to connect to Label Manager in 1 sec. */ + thread_add_timer(master, sr_start_label_manager, ospf, + 1, &OspfSR.t_start_lm); + osr_debug(" |- Failed to start the Label Manager"); + return -1; + } - /* Initialize self SR Node */ - srn = hash_get(OspfSR.neighbors, (void *)&(ospf->router_id), - (void *)sr_node_new); + /* + * Request SRLB & SGRB to the label manager if not already reserved. + * If the allocation fails, return an error to disable SR until a new + * SRLB and/or SRGB are successfully allocated. + */ + sr_local_block_init(OspfSR.srlb.start, OspfSR.srlb.end); + if (!OspfSR.srgb.reserved) { + if (ospf_zebra_request_label_range(OspfSR.srgb.start, + OspfSR.srgb.size) + < 0) { + OspfSR.srgb.reserved = false; + return -1; + } else + OspfSR.srgb.reserved = true; + } - /* Complete & Store self SR Node */ - srn->srgb.range_size = OspfSR.srgb.range_size; - srn->srgb.lower_bound = OspfSR.srgb.lower_bound; - srn->algo[0] = OspfSR.algo[0]; - srn->msd = OspfSR.msd; - OspfSR.self = srn; + /* SR is UP and ready to flood LSA */ + OspfSR.status = SR_UP; + + /* Set Router Information SR parameters */ + osr_debug("SR: Activate SR for Router Information LSA"); + + ospf_router_info_update_sr(true, OspfSR.self); + + /* Update Ext LSA */ + osr_debug("SR: Activate SR for Extended Link/Prefix LSA"); + + ospf_ext_update_sr(true); - if (IS_DEBUG_OSPF_EVENT) - zlog_debug("SR (%s): Update SR-DB from LSDB", __func__); + osr_debug("SR (%s): Update SR-DB from LSDB", __func__); /* Start by looking to Router Info & Extended LSA in lsdb */ if ((ospf != NULL) && (ospf->backbone != NULL)) { @@ -278,14 +516,35 @@ static int ospf_sr_start(struct ospf *ospf) static void ospf_sr_stop(void) { - if (IS_DEBUG_OSPF_SR) - zlog_debug("SR (%s): Stop Segment Routing", __func__); + osr_debug("SR (%s): Stop Segment Routing", __func__); + + /* Disable any re-attempt to connect to Label Manager */ + THREAD_TIMER_OFF(OspfSR.t_start_lm); + + /* Release SRGB & SRLB if active. */ + if (OspfSR.srgb.reserved) + ospf_zebra_release_label_range( + OspfSR.srgb.start, + OspfSR.srgb.start + OspfSR.srgb.size - 1); + sr_local_block_delete(); + + /* Revert SRGB, SRLB and MSD to default values */ + OspfSR.srgb.size = DEFAULT_SRGB_SIZE; + OspfSR.srgb.start = DEFAULT_SRGB_LABEL; + OspfSR.srgb.reserved = false; + + OspfSR.srlb.start = DEFAULT_SRLB_LABEL; + OspfSR.srlb.end = DEFAULT_SRLB_LABEL + DEFAULT_SRLB_SIZE - 1; + OspfSR.srlb.reserved = false; + OspfSR.msd = 0; /* * Remove all SR Nodes from the Hash table. Prefix and Link SID will * be remove though list_delete() call. See sr_node_del() */ hash_clean(OspfSR.neighbors, (void *)sr_node_del); + OspfSR.self = NULL; + OspfSR.status = SR_OFF; } /* @@ -299,22 +558,26 @@ int ospf_sr_init(void) { int rc = -1; - if (IS_DEBUG_OSPF_SR) - zlog_info("SR (%s): Initialize SR Data Base", __func__); + osr_debug("SR (%s): Initialize SR Data Base", __func__); memset(&OspfSR, 0, sizeof(struct ospf_sr_db)); - OspfSR.enabled = false; + OspfSR.status = SR_OFF; /* Only AREA flooding is supported in this release */ OspfSR.scope = OSPF_OPAQUE_AREA_LSA; - /* Initialize SRGB, Algorithms and MSD TLVs */ + /* Initialize Algorithms, SRGB, SRLB and MSD TLVs */ /* Only Algorithm SPF is supported */ OspfSR.algo[0] = SR_ALGORITHM_SPF; for (int i = 1; i < ALGORITHM_COUNT; i++) OspfSR.algo[i] = SR_ALGORITHM_UNSET; - OspfSR.srgb.range_size = MPLS_DEFAULT_MAX_SRGB_SIZE; - OspfSR.srgb.lower_bound = MPLS_DEFAULT_MIN_SRGB_LABEL; + OspfSR.srgb.size = DEFAULT_SRGB_SIZE; + OspfSR.srgb.start = DEFAULT_SRGB_LABEL; + OspfSR.srgb.reserved = false; + + OspfSR.srlb.start = DEFAULT_SRLB_LABEL; + OspfSR.srlb.end = DEFAULT_SRLB_LABEL + DEFAULT_SRLB_SIZE - 1; + OspfSR.srlb.reserved = false; OspfSR.msd = 0; /* Initialize Hash table for neighbor SR nodes */ @@ -353,9 +616,6 @@ void ospf_sr_term(void) /* Clear Prefix Table */ if (OspfSR.prefix) route_table_finish(OspfSR.prefix); - - OspfSR.enabled = false; - OspfSR.self = NULL; } /* @@ -368,8 +628,6 @@ void ospf_sr_finish(void) { /* Stop Segment Routing */ ospf_sr_stop(); - - OspfSR.enabled = false; } /* @@ -378,14 +636,17 @@ void ospf_sr_finish(void) */ /* Compute label from index */ -static mpls_label_t index2label(uint32_t index, struct sr_srgb srgb) +static mpls_label_t index2label(uint32_t index, struct sr_block srgb) { mpls_label_t label; label = srgb.lower_bound + index; - if (label > (srgb.lower_bound + srgb.range_size)) + if (label > (srgb.lower_bound + srgb.range_size)) { + flog_warn(EC_OSPF_SR_SID_OVERFLOW, + "%s: SID index %u falls outside SRGB range", + __func__, index); return MPLS_INVALID_LABEL; - else + } else return label; } @@ -417,21 +678,17 @@ static struct ospf_neighbor *get_neighbor_by_addr(struct ospf *top, } /* Get OSPF Path from address */ -static struct ospf_path *get_nexthop_by_addr(struct ospf *top, - struct prefix_ipv4 p) +static struct ospf_route *get_nexthop_by_addr(struct ospf *top, + struct prefix_ipv4 p) { - struct ospf_route * or ; - struct ospf_path *path; - struct listnode *node; struct route_node *rn; /* Sanity Check */ if (top == NULL) return NULL; - if (IS_DEBUG_OSPF_SR) - zlog_debug(" |- Search Nexthop for prefix %s/%u", - inet_ntoa(p.prefix), p.prefixlen); + osr_debug(" |- Search Nexthop for prefix %pFX", + (struct prefix *)&p); rn = route_node_lookup(top->new_table, (struct prefix *)&p); @@ -443,16 +700,7 @@ static struct ospf_path *get_nexthop_by_addr(struct ospf *top, return NULL; route_unlock_node(rn); - or = rn->info; - if (or == NULL) - return NULL; - - /* Then search path from this route */ - for (ALL_LIST_ELEMENTS_RO(or->paths, node, path)) - if (path->nexthop.s_addr != INADDR_ANY || path->ifindex != 0) - return path; - - return NULL; + return rn->info; } /* Compute NHLFE entry for Extended Link */ @@ -462,10 +710,7 @@ static int compute_link_nhlfe(struct sr_link *srl) struct ospf_neighbor *nh; int rc = 0; - if (IS_DEBUG_OSPF_SR) - zlog_debug(" |- Compute NHLFE for link %s/%u", - inet_ntoa(srl->nhlfe[0].prefv4.prefix), - srl->nhlfe[0].prefv4.prefixlen); + osr_debug(" |- Compute NHLFE for link %pI4", &srl->itf_addr); /* First determine the OSPF Neighbor */ nh = get_neighbor_by_addr(top, srl->nhlfe[0].nexthop); @@ -477,9 +722,7 @@ static int compute_link_nhlfe(struct sr_link *srl) if (nh == NULL) return rc; - if (IS_DEBUG_OSPF_SR) - zlog_debug(" |- Found nexthop NHLFE %s", - inet_ntoa(nh->router_id)); + osr_debug(" |- Found nexthop %pI4", &nh->router_id); /* Set ifindex for this neighbor */ srl->nhlfe[0].ifindex = nh->oi->ifp->ifindex; @@ -510,204 +753,139 @@ static int compute_link_nhlfe(struct sr_link *srl) return rc; } +/** + * Compute output label for the given Prefix-SID. + * + * @param srp Segment Routing Prefix + * @param srnext Segment Routing nexthop node + * + * @return MPLS label or MPLS_INVALID_LABEL in case of error + */ +static mpls_label_t sr_prefix_out_label(const struct sr_prefix *srp, + const struct sr_node *srnext) +{ + /* Check if the nexthop SR Node is the last hop? */ + if (srnext == srp->srn) { + /* SR-Node doesn't request NO-PHP. Return Implicit NULL label */ + if (!CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_NPFLG)) + return MPLS_LABEL_IMPLICIT_NULL; + + /* SR-Node requests Explicit NULL Label */ + if (CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_EFLG)) + return MPLS_LABEL_IPV4_EXPLICIT_NULL; + /* Fallthrough */ + } + + /* Return SID value as MPLS label if it is an Absolute SID */ + if (CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_VFLG + | EXT_SUBTLV_PREFIX_SID_LFLG)) { + /* + * V/L SIDs have local significance, so only adjacent routers + * can use them (RFC8665 section #5) + */ + if (srp->srn != srnext) + return MPLS_INVALID_LABEL; + return srp->sid; + } + + /* Return MPLS label as SRGB lower bound + SID index as per RFC 8665 */ + return (index2label(srp->sid, srnext->srgb)); +} + /* * Compute NHLFE entry for Extended Prefix * * @param srp - Segment Routing Prefix * - * @return -1 if next hop is not found, 0 if nexthop has not changed - * and 1 if success + * @return -1 if no route is found, 0 if there is no SR route ready + * and 1 if success or update */ static int compute_prefix_nhlfe(struct sr_prefix *srp) { struct ospf *top = ospf_lookup_by_vrf_id(VRF_DEFAULT); - struct ospf_path *nh = NULL; + struct ospf_path *path; + struct listnode *node; struct sr_node *srnext; int rc = -1; - if (IS_DEBUG_OSPF_SR) - zlog_debug(" |- Compute NHLFE for prefix %s/%u", - inet_ntoa(srp->nhlfe.prefv4.prefix), - srp->nhlfe.prefv4.prefixlen); + osr_debug(" |- Compute NHLFE for prefix %pFX", + (struct prefix *)&srp->prefv4); + /* First determine the nexthop */ - nh = get_nexthop_by_addr(top, srp->nhlfe.prefv4); + srp->route = get_nexthop_by_addr(top, srp->prefv4); /* Nexthop could be not found when OSPF Adjacency just fire up * because SPF don't yet populate routing table. This NHLFE will * be fixed later when SR SPF schedule will be called. */ - if (nh == NULL) - return rc; - - /* Check if NextHop has changed when call after running a new SPF */ - if (IPV4_ADDR_SAME(&nh->nexthop, &srp->nhlfe.nexthop) - && (nh->ifindex == srp->nhlfe.ifindex)) - return 0; - - if (IS_DEBUG_OSPF_SR) - zlog_debug(" |- Found new next hop for this NHLFE: %s", - inet_ntoa(nh->nexthop)); - - /* - * Get SR-Node for this nexthop. Could be not yet available - * as Extende Link / Prefix and Router Information are flooded - * after LSA Type 1 & 2 which populate the OSPF Route Table - */ - srnext = get_sr_node_by_nexthop(top, nh->nexthop); - if (srnext == NULL) + if (srp->route == NULL) return rc; - /* And store this information for later update if SR Node is found */ - srnext->neighbor = OspfSR.self; - if (IPV4_ADDR_SAME(&srnext->adv_router, &srp->adv_router)) - srp->nexthop = NULL; - else - srp->nexthop = srnext; - - /* - * SR Node could be known, but SRGB could be not initialize - * This is due to the fact that Extended Link / Prefix could - * be received before corresponding Router Information LSA - */ - if ((srnext == NULL) || (srnext->srgb.lower_bound == 0) - || (srnext->srgb.range_size == 0)) - return rc; - - if (IS_DEBUG_OSPF_SR) - zlog_debug(" |- Found SRGB %u/%u for next hop SR-Node %s", - srnext->srgb.range_size, srnext->srgb.lower_bound, - inet_ntoa(srnext->adv_router)); - - /* Set ip addr & ifindex for this neighbor */ - IPV4_ADDR_COPY(&srp->nhlfe.nexthop, &nh->nexthop); - srp->nhlfe.ifindex = nh->ifindex; - /* Compute Input Label with self SRGB */ - srp->nhlfe.label_in = index2label(srp->sid, OspfSR.srgb); - /* - * and Output Label with Next hop SR Node SRGB or Implicit Null label - * if next hop is the destination and request PHP - */ - if ((srp->nexthop == NULL) - && (!CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_NPFLG))) - srp->nhlfe.label_out = MPLS_LABEL_IMPLICIT_NULL; - else if (CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_VFLG)) - srp->nhlfe.label_out = srp->sid; - else - srp->nhlfe.label_out = index2label(srp->sid, srnext->srgb); + srp->label_in = index2label(srp->sid, OspfSR.self->srgb); - if (IS_DEBUG_OSPF_SR) - zlog_debug(" |- Computed new labels in: %u out: %u", - srp->nhlfe.label_in, srp->nhlfe.label_out); - - rc = 1; - return rc; -} - -/* Send MPLS Label entry to Zebra for installation or deletion */ -static int ospf_zebra_send_mpls_labels(int cmd, struct sr_nhlfe nhlfe) -{ - struct stream *s; - - /* Reset stream. */ - s = zclient->obuf; - stream_reset(s); - - zclient_create_header(s, cmd, VRF_DEFAULT); - stream_putc(s, ZEBRA_LSP_SR); - /* OSPF Segment Routing currently support only IPv4 */ - stream_putl(s, nhlfe.prefv4.family); - stream_put_in_addr(s, &nhlfe.prefv4.prefix); - stream_putc(s, nhlfe.prefv4.prefixlen); - stream_put_in_addr(s, &nhlfe.nexthop); - stream_putl(s, nhlfe.ifindex); - stream_putc(s, OSPF_SR_PRIORITY_DEFAULT); - stream_putl(s, nhlfe.label_in); - stream_putl(s, nhlfe.label_out); + rc = 0; + for (ALL_LIST_ELEMENTS_RO(srp->route->paths, node, path)) { - /* Put length at the first point of the stream. */ - stream_putw_at(s, 0, stream_get_endp(s)); + osr_debug(" |- Process new route via %pI4 for this prefix", + &path->nexthop); - if (IS_DEBUG_OSPF_SR) - zlog_debug(" |- %s LSP %u/%u for %s/%u via %u", - cmd == ZEBRA_MPLS_LABELS_ADD ? "Add" : "Delete", - nhlfe.label_in, nhlfe.label_out, - inet_ntoa(nhlfe.prefv4.prefix), - nhlfe.prefv4.prefixlen, nhlfe.ifindex); + /* + * Get SR-Node for this nexthop. Could be not yet available + * as Extended Link / Prefix and Router Information are flooded + * after LSA Type 1 & 2 which populate the OSPF Route Table + */ + srnext = get_sr_node_by_nexthop(top, path->nexthop); + if (srnext == NULL) + continue; - return zclient_send_message(zclient); -} + /* And store this information for later update */ + srnext->neighbor = OspfSR.self; + path->srni.nexthop = srnext; -/* Request zebra to install/remove FEC in FIB */ -static int ospf_zebra_send_mpls_ftn(int cmd, struct sr_nhlfe nhlfe) -{ - struct zapi_route api; - struct zapi_nexthop *api_nh; + /* + * SR Node could be known, but SRGB could be not initialize + * This is due to the fact that Extended Link / Prefix could + * be received before corresponding Router Information LSA + */ + if (srnext == NULL || srnext->srgb.lower_bound == 0 + || srnext->srgb.range_size == 0) + continue; - /* Support only IPv4 */ - if (nhlfe.prefv4.family != AF_INET) - return -1; + osr_debug(" |- Found SRGB %u/%u for next hop SR-Node %pI4", + srnext->srgb.range_size, srnext->srgb.lower_bound, + &srnext->adv_router); - memset(&api, 0, sizeof(api)); - api.vrf_id = VRF_DEFAULT; - api.type = ZEBRA_ROUTE_OSPF; - api.safi = SAFI_UNICAST; - memcpy(&api.prefix, &nhlfe.prefv4, sizeof(struct prefix_ipv4)); + /* Compute Output Label with Nexthop SR Node SRGB */ + path->srni.label_out = sr_prefix_out_label(srp, srnext); - if (cmd == ZEBRA_ROUTE_ADD) { - /* Metric value. */ - SET_FLAG(api.message, ZAPI_MESSAGE_METRIC); - api.metric = OSPF_SR_DEFAULT_METRIC; - /* Nexthop */ - SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP); - api_nh = &api.nexthops[0]; - IPV4_ADDR_COPY(&api_nh->gate.ipv4, &nhlfe.nexthop); - api_nh->type = NEXTHOP_TYPE_IPV4_IFINDEX; - api_nh->ifindex = nhlfe.ifindex; - /* MPLS labels */ - SET_FLAG(api.message, ZAPI_MESSAGE_LABEL); - api_nh->labels[0] = nhlfe.label_out; - api_nh->label_num = 1; - api_nh->vrf_id = VRF_DEFAULT; - api.nexthop_num = 1; + osr_debug(" |- Computed new labels in: %u out: %u", + srp->label_in, path->srni.label_out); + rc = 1; } - - if (IS_DEBUG_OSPF_SR) - zlog_debug(" |- %s FEC %u for %s/%u via %u", - cmd == ZEBRA_ROUTE_ADD ? "Add" : "Delete", - nhlfe.label_out, inet_ntoa(nhlfe.prefv4.prefix), - nhlfe.prefv4.prefixlen, nhlfe.ifindex); - - return zclient_route_send(cmd, zclient, &api); + return rc; } -/* Add new NHLFE entry for SID */ -static inline void add_sid_nhlfe(struct sr_nhlfe nhlfe) +/* Add new NHLFE entry for Adjacency SID */ +static inline void add_adj_sid(struct sr_nhlfe nhlfe) { - if ((nhlfe.label_in != 0) && (nhlfe.label_out != 0)) { - ospf_zebra_send_mpls_labels(ZEBRA_MPLS_LABELS_ADD, nhlfe); - if (nhlfe.label_out != MPLS_LABEL_IMPLICIT_NULL) - ospf_zebra_send_mpls_ftn(ZEBRA_ROUTE_ADD, nhlfe); - } + if (nhlfe.label_in != 0) + ospf_zebra_send_adjacency_sid(ZEBRA_MPLS_LABELS_ADD, nhlfe); } -/* Remove NHLFE entry for SID */ -static inline void del_sid_nhlfe(struct sr_nhlfe nhlfe) +/* Remove NHLFE entry for Adjacency SID */ +static inline void del_adj_sid(struct sr_nhlfe nhlfe) { - if ((nhlfe.label_in != 0) && (nhlfe.label_out != 0)) { - ospf_zebra_send_mpls_labels(ZEBRA_MPLS_LABELS_DELETE, nhlfe); - if (nhlfe.label_out != MPLS_LABEL_IMPLICIT_NULL) - ospf_zebra_send_mpls_ftn(ZEBRA_ROUTE_DELETE, nhlfe); - } + if (nhlfe.label_in != 0) + ospf_zebra_send_adjacency_sid(ZEBRA_MPLS_LABELS_DELETE, nhlfe); } -/* Update NHLFE entry for SID */ -static inline void update_sid_nhlfe(struct sr_nhlfe n1, struct sr_nhlfe n2) +/* Update NHLFE entry for Adjacency SID */ +static inline void update_adj_sid(struct sr_nhlfe n1, struct sr_nhlfe n2) { - - del_sid_nhlfe(n1); - add_sid_nhlfe(n2); + del_adj_sid(n1); + add_adj_sid(n2); } /* @@ -778,23 +956,10 @@ static struct sr_link *get_ext_link_sid(struct tlv_header *tlvh) sum += TLV_SIZE(sub_tlvh); } - IPV4_ADDR_COPY(&srl->nhlfe[0].prefv4.prefix, &link->link_data); - srl->nhlfe[0].prefv4.prefixlen = IPV4_MAX_PREFIXLEN; - srl->nhlfe[0].prefv4.family = AF_INET; - apply_mask_ipv4(&srl->nhlfe[0].prefv4); - IPV4_ADDR_COPY(&srl->nhlfe[1].prefv4.prefix, &link->link_data); - srl->nhlfe[1].prefv4.prefixlen = IPV4_MAX_PREFIXLEN; - srl->nhlfe[1].prefv4.family = AF_INET; - apply_mask_ipv4(&srl->nhlfe[1].prefv4); + IPV4_ADDR_COPY(&srl->itf_addr, &link->link_data); - if (IS_DEBUG_OSPF_SR) { - zlog_debug(" |- Found primary Adj/Lan Sid %u for %s/%u", - srl->sid[0], inet_ntoa(srl->nhlfe[0].prefv4.prefix), - srl->nhlfe[0].prefv4.prefixlen); - zlog_debug(" |- Found backup Adj/Lan Sid %u for %s/%u", - srl->sid[1], inet_ntoa(srl->nhlfe[1].prefv4.prefix), - srl->nhlfe[1].prefv4.prefixlen); - } + osr_debug(" |- Found primary %u and backup %u Adj/Lan Sid for %pI4", + srl->sid[0], srl->sid[1], &srl->itf_addr); return srl; } @@ -833,11 +998,10 @@ static struct sr_prefix *get_ext_prefix_sid(struct tlv_header *tlvh) srp->sid = GET_LABEL(ntohl(psid->value)); else srp->sid = ntohl(psid->value); - IPV4_ADDR_COPY(&srp->nhlfe.prefv4.prefix, - &pref->address); - srp->nhlfe.prefv4.prefixlen = pref->pref_length; - srp->nhlfe.prefv4.family = AF_INET; - apply_mask_ipv4(&srp->nhlfe.prefv4); + IPV4_ADDR_COPY(&srp->prefv4.prefix, &pref->address); + srp->prefv4.prefixlen = pref->pref_length; + srp->prefv4.family = AF_INET; + apply_mask_ipv4(&srp->prefv4); break; default: break; @@ -845,10 +1009,9 @@ static struct sr_prefix *get_ext_prefix_sid(struct tlv_header *tlvh) sum += TLV_SIZE(sub_tlvh); } - if (IS_DEBUG_OSPF_SR) - zlog_debug(" |- Found SID %u for prefix %s/%u", srp->sid, - inet_ntoa(srp->nhlfe.prefv4.prefix), - srp->nhlfe.prefv4.prefixlen); + osr_debug(" |- Found SID %u for prefix %pFX", srp->sid, + (struct prefix *)&srp->prefv4); + return srp; } @@ -888,13 +1051,12 @@ static void update_ext_link_sid(struct sr_node *srn, struct sr_link *srl, if ((srn == NULL) || (srl == NULL)) return; - if (IS_DEBUG_OSPF_SR) - zlog_debug(" |- Process Extended Link Adj/Lan-SID"); + osr_debug(" |- Process Extended Link Adj/Lan-SID"); - /* Process only Local Adj/Lan_Adj SID coming from LSA SELF */ - if (!CHECK_FLAG(srl->flags[0], EXT_SUBTLV_LINK_ADJ_SID_LFLG) - || !CHECK_FLAG(srl->flags[1], EXT_SUBTLV_LINK_ADJ_SID_LFLG) - || !CHECK_FLAG(lsa_flags, OSPF_LSA_SELF)) + /* Skip Local Adj/Lan_Adj SID coming from neighbors */ + if (!CHECK_FLAG(lsa_flags, OSPF_LSA_SELF) + && (CHECK_FLAG(srl->flags[0], EXT_SUBTLV_LINK_ADJ_SID_LFLG) + || CHECK_FLAG(srl->flags[1], EXT_SUBTLV_LINK_ADJ_SID_LFLG))) return; /* Search for existing Segment Link */ @@ -904,11 +1066,9 @@ static void update_ext_link_sid(struct sr_node *srn, struct sr_link *srl, break; } - if (IS_DEBUG_OSPF_SR) - zlog_debug(" |- %s SR Link 8.0.0.%u for SR node %s", - found ? "Update" : "Add", - GET_OPAQUE_ID(srl->instance), - inet_ntoa(srn->adv_router)); + osr_debug(" |- %s SR Link 8.0.0.%u for SR node %pI4", + found ? "Update" : "Add", GET_OPAQUE_ID(srl->instance), + &srn->adv_router); /* if not found, add new Segment Link and install NHLFE */ if (!found) { @@ -918,14 +1078,14 @@ static void update_ext_link_sid(struct sr_node *srn, struct sr_link *srl, listnode_add(srn->ext_link, srl); /* Try to set MPLS table */ if (compute_link_nhlfe(srl)) { - add_sid_nhlfe(srl->nhlfe[0]); - add_sid_nhlfe(srl->nhlfe[1]); + add_adj_sid(srl->nhlfe[0]); + add_adj_sid(srl->nhlfe[1]); } } else { if (sr_link_cmp(lk, srl)) { if (compute_link_nhlfe(srl)) { - update_sid_nhlfe(lk->nhlfe[0], srl->nhlfe[0]); - update_sid_nhlfe(lk->nhlfe[1], srl->nhlfe[1]); + update_adj_sid(lk->nhlfe[0], srl->nhlfe[0]); + update_adj_sid(lk->nhlfe[1], srl->nhlfe[1]); /* Replace Segment List */ listnode_delete(srn->ext_link, lk); XFREE(MTYPE_OSPF_SR_PARAMS, lk); @@ -961,8 +1121,7 @@ static void update_ext_prefix_sid(struct sr_node *srn, struct sr_prefix *srp) if (srn == NULL || srp == NULL) return; - if (IS_DEBUG_OSPF_SR) - zlog_debug(" |- Process Extended Prefix SID %u", srp->sid); + osr_debug(" |- Process Extended Prefix SID %u", srp->sid); /* Process only Global Prefix SID */ if (CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_LFLG)) @@ -975,11 +1134,9 @@ static void update_ext_prefix_sid(struct sr_node *srn, struct sr_prefix *srp) break; } - if (IS_DEBUG_OSPF_SR) - zlog_debug(" |- %s SR LSA ID 7.0.0.%u for SR node %s", - found ? "Update" : "Add", - GET_OPAQUE_ID(srp->instance), - inet_ntoa(srn->adv_router)); + osr_debug(" |- %s SR LSA ID 7.0.0.%u for SR node %pI4", + found ? "Update" : "Add", GET_OPAQUE_ID(srp->instance), + &srn->adv_router); /* if not found, add new Segment Prefix and install NHLFE */ if (!found) { @@ -989,11 +1146,11 @@ static void update_ext_prefix_sid(struct sr_node *srn, struct sr_prefix *srp) listnode_add(srn->ext_prefix, srp); /* Try to set MPLS table */ if (compute_prefix_nhlfe(srp) == 1) - add_sid_nhlfe(srp->nhlfe); + ospf_zebra_update_prefix_sid(srp); } else { if (sr_prefix_cmp(pref, srp)) { if (compute_prefix_nhlfe(srp) == 1) { - update_sid_nhlfe(pref->nhlfe, srp->nhlfe); + ospf_zebra_delete_prefix_sid(pref); /* Replace Segment Prefix */ listnode_delete(srn->ext_prefix, pref); XFREE(MTYPE_OSPF_SR_PARAMS, pref); @@ -1001,6 +1158,7 @@ static void update_ext_prefix_sid(struct sr_node *srn, struct sr_prefix *srp) IPV4_ADDR_COPY(&srp->adv_router, &srn->adv_router); listnode_add(srn->ext_prefix, srp); + ospf_zebra_update_prefix_sid(srp); } else { /* New NHLFE was not found. * Just free the SR Prefix @@ -1025,7 +1183,6 @@ static void update_in_nhlfe(struct hash_bucket *bucket, void *args) struct listnode *node; struct sr_node *srn = (struct sr_node *)bucket->data; struct sr_prefix *srp; - struct sr_nhlfe new; /* Process Every Extended Prefix for this SR-Node */ for (ALL_LIST_ELEMENTS_RO(srn->ext_prefix, node, srp)) { @@ -1038,13 +1195,12 @@ static void update_in_nhlfe(struct hash_bucket *bucket, void *args) if (CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_VFLG)) continue; - /* OK. Compute new NHLFE */ - memcpy(&new, &srp->nhlfe, sizeof(struct sr_nhlfe)); - new.label_in = index2label(srp->sid, OspfSR.srgb); - /* Update MPLS LFIB */ - update_sid_nhlfe(srp->nhlfe, new); - /* Finally update Input Label */ - srp->nhlfe.label_in = new.label_in; + /* First, remove old MPLS table entries ... */ + ospf_zebra_delete_prefix_sid(srp); + /* ... then compute new input label ... */ + srp->label_in = index2label(srp->sid, OspfSR.self->srgb); + /* ... and install new MPLS LFIB */ + ospf_zebra_update_prefix_sid(srp); } } @@ -1054,21 +1210,27 @@ static void update_in_nhlfe(struct hash_bucket *bucket, void *args) */ static void update_out_nhlfe(struct hash_bucket *bucket, void *args) { - struct listnode *node; + struct listnode *node, *pnode; struct sr_node *srn = (struct sr_node *)bucket->data; struct sr_node *srnext = (struct sr_node *)args; struct sr_prefix *srp; - struct sr_nhlfe new; + struct ospf_path *path; for (ALL_LIST_ELEMENTS_RO(srn->ext_prefix, node, srp)) { - /* Process only SID Index for next hop without PHP */ - if ((srp->nexthop == NULL) - && (!CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_NPFLG))) + /* Process only SID Index with valid route */ + if (srp->route == NULL) continue; - memcpy(&new, &srp->nhlfe, sizeof(struct sr_nhlfe)); - new.label_out = index2label(srp->sid, srnext->srgb); - update_sid_nhlfe(srp->nhlfe, new); - srp->nhlfe.label_out = new.label_out; + + for (ALL_LIST_ELEMENTS_RO(srp->route->paths, pnode, path)) { + /* Process only SID Index for next hop without PHP */ + if ((path->srni.nexthop == srp->srn) + && (!CHECK_FLAG(srp->flags, + EXT_SUBTLV_PREFIX_SID_NPFLG))) + continue; + path->srni.label_out = + index2label(srp->sid, srnext->srgb); + ospf_zebra_update_prefix_sid(srp); + } } } @@ -1084,18 +1246,17 @@ void ospf_sr_ri_lsa_update(struct ospf_lsa *lsa) { struct sr_node *srn; struct tlv_header *tlvh; - struct lsa_header *lsah = (struct lsa_header *)lsa->data; - struct ri_sr_tlv_sid_label_range *ri_srgb; - struct ri_sr_tlv_sr_algorithm *algo; - struct sr_srgb srgb; + struct lsa_header *lsah = lsa->data; + struct ri_sr_tlv_sid_label_range *ri_srgb = NULL; + struct ri_sr_tlv_sid_label_range *ri_srlb = NULL; + struct ri_sr_tlv_sr_algorithm *algo = NULL; + struct sr_block srgb; uint16_t length = 0, sum = 0; + uint8_t msd = 0; - if (IS_DEBUG_OSPF_SR) - zlog_debug( - "SR (%s): Process Router " - "Information LSA 4.0.0.%u from %s", - __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)), - inet_ntoa(lsah->adv_router)); + osr_debug("SR (%s): Process Router Information LSA 4.0.0.%u from %pI4", + __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)), + &lsah->adv_router); /* Sanity check */ if (IS_LSA_SELF(lsa)) @@ -1107,26 +1268,10 @@ void ospf_sr_ri_lsa_update(struct ospf_lsa *lsa) return; } - /* Get SR Node in hash table from Router ID */ - srn = hash_get(OspfSR.neighbors, (void *)&(lsah->adv_router), - (void *)sr_node_new); - - /* Sanity check */ - if (srn == NULL) { - flog_err(EC_OSPF_SR_NODE_CREATE, - "SR (%s): Abort! can't create SR node in hash table", - __func__); - return; - } + /* Search SR Node in hash table from Router ID */ + srn = (struct sr_node *)hash_lookup(OspfSR.neighbors, + &lsah->adv_router); - if ((srn->instance != 0) && (srn->instance != ntohl(lsah->id.s_addr))) { - flog_err(EC_OSPF_SR_INVALID_LSA_ID, - "SR (%s): Abort! Wrong " - "LSA ID 4.0.0.%u for SR node %s/%u", - __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)), - inet_ntoa(lsah->adv_router), srn->instance); - return; - } /* Collect Router Information Sub TLVs */ /* Initialize TLV browsing */ @@ -1139,23 +1284,18 @@ void ospf_sr_ri_lsa_update(struct ospf_lsa *lsa) switch (ntohs(tlvh->type)) { case RI_SR_TLV_SR_ALGORITHM: algo = (struct ri_sr_tlv_sr_algorithm *)tlvh; - int i; - - for (i = 0; i < ntohs(algo->header.length); i++) - srn->algo[i] = algo->value[0]; - for (; i < ALGORITHM_COUNT; i++) - srn->algo[i] = SR_ALGORITHM_UNSET; sum += TLV_SIZE(tlvh); break; - case RI_SR_TLV_SID_LABEL_RANGE: + case RI_SR_TLV_SRGB_LABEL_RANGE: ri_srgb = (struct ri_sr_tlv_sid_label_range *)tlvh; - srgb.range_size = GET_RANGE_SIZE(ntohl(ri_srgb->size)); - srgb.lower_bound = - GET_LABEL(ntohl(ri_srgb->lower.value)); + sum += TLV_SIZE(tlvh); + break; + case RI_SR_TLV_SRLB_LABEL_RANGE: + ri_srlb = (struct ri_sr_tlv_sid_label_range *)tlvh; sum += TLV_SIZE(tlvh); break; case RI_SR_TLV_NODE_MSD: - srn->msd = ((struct ri_sr_tlv_node_msd *)(tlvh))->value; + msd = ((struct ri_sr_tlv_node_msd *)(tlvh))->value; sum += TLV_SIZE(tlvh); break; default: @@ -1164,19 +1304,50 @@ void ospf_sr_ri_lsa_update(struct ospf_lsa *lsa) } } - /* Check that we collect mandatory parameters */ - if (srn->algo[0] == SR_ALGORITHM_UNSET || srgb.range_size == 0 - || srgb.lower_bound == 0) { - flog_err(EC_OSPF_SR_NODE_CREATE, - "SR (%s): Missing mandatory parameters. Abort!", - __func__); - hash_release(OspfSR.neighbors, &(srn->adv_router)); - XFREE(MTYPE_OSPF_SR_PARAMS, srn); + /* Check if Segment Routing Capabilities has been found */ + if (ri_srgb == NULL) { + /* Skip Router Information without SR capabilities + * advertise by a non SR Node */ + if (srn == NULL) { + return; + } else { + /* Remove SR Node that advertise Router Information + * without SR capabilities. This could correspond to a + * Node stopping Segment Routing */ + hash_release(OspfSR.neighbors, &(srn->adv_router)); + sr_node_del(srn); + return; + } + } + + /* Check that RI LSA belongs to the correct SR Node */ + if ((srn != NULL) && (srn->instance != 0) + && (srn->instance != ntohl(lsah->id.s_addr))) { + flog_err(EC_OSPF_SR_INVALID_LSA_ID, + "SR (%s): Abort! Wrong LSA ID 4.0.0.%u for SR node %pI4/%u", + __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)), + &lsah->adv_router, srn->instance); return; } + /* OK. All things look good. Get SRGB */ + srgb.range_size = GET_RANGE_SIZE(ntohl(ri_srgb->size)); + srgb.lower_bound = GET_LABEL(ntohl(ri_srgb->lower.value)); + /* Check if it is a new SR Node or not */ - if (srn->instance == 0) { + if (srn == NULL) { + /* Get a new SR Node in hash table from Router ID */ + srn = (struct sr_node *)hash_get(OspfSR.neighbors, + &lsah->adv_router, + (void *)sr_node_new); + /* Sanity check */ + if (srn == NULL) { + flog_err( + EC_OSPF_SR_NODE_CREATE, + "SR (%s): Abort! can't create SR node in hash table", + __func__); + return; + } /* update LSA ID */ srn->instance = ntohl(lsah->id.s_addr); /* Copy SRGB */ @@ -1184,18 +1355,39 @@ void ospf_sr_ri_lsa_update(struct ospf_lsa *lsa) srn->srgb.lower_bound = srgb.lower_bound; } - /* Check if SRGB has changed */ - if ((srn->srgb.range_size != srgb.range_size) - || (srn->srgb.lower_bound != srgb.lower_bound)) { - srn->srgb.range_size = srgb.range_size; - srn->srgb.lower_bound = srgb.lower_bound; - /* Update NHLFE if it is a neighbor SR node */ - if (srn->neighbor == OspfSR.self) - hash_iterate(OspfSR.neighbors, - (void (*)(struct hash_bucket *, - void *))update_out_nhlfe, - (void *)srn); + /* Update Algorithm, SRLB and MSD if present */ + if (algo != NULL) { + int i; + for (i = 0; i < ntohs(algo->header.length); i++) + srn->algo[i] = algo->value[0]; + for (; i < ALGORITHM_COUNT; i++) + srn->algo[i] = SR_ALGORITHM_UNSET; + } else { + srn->algo[0] = SR_ALGORITHM_SPF; } + srn->msd = msd; + if (ri_srlb != NULL) { + srn->srlb.range_size = GET_RANGE_SIZE(ntohl(ri_srlb->size)); + srn->srlb.lower_bound = GET_LABEL(ntohl(ri_srlb->lower.value)); + } + + osr_debug(" |- Update SR-Node[%pI4], SRGB[%u/%u], SRLB[%u/%u], Algo[%u], MSD[%u]", + &srn->adv_router, srn->srgb.lower_bound, srn->srgb.range_size, + srn->srlb.lower_bound, srn->srlb.range_size, srn->algo[0], + srn->msd); + + /* Check if SRGB has changed */ + if ((srn->srgb.range_size == srgb.range_size) + && (srn->srgb.lower_bound == srgb.lower_bound)) + return; + + /* Copy SRGB */ + srn->srgb.range_size = srgb.range_size; + srn->srgb.lower_bound = srgb.lower_bound; + + /* ... and NHLFE if it is a neighbor SR node */ + if (srn->neighbor == OspfSR.self) + hash_iterate(OspfSR.neighbors, update_out_nhlfe, srn); } /* @@ -1205,12 +1397,10 @@ void ospf_sr_ri_lsa_update(struct ospf_lsa *lsa) void ospf_sr_ri_lsa_delete(struct ospf_lsa *lsa) { struct sr_node *srn; - struct lsa_header *lsah = (struct lsa_header *)lsa->data; + struct lsa_header *lsah = lsa->data; - if (IS_DEBUG_OSPF_SR) - zlog_debug("SR (%s): Remove SR node %s from lsa_id 4.0.0.%u", - __func__, inet_ntoa(lsah->adv_router), - GET_OPAQUE_ID(ntohl(lsah->id.s_addr))); + osr_debug("SR (%s): Remove SR node %pI4 from lsa_id 4.0.0.%u", __func__, + &lsah->adv_router, GET_OPAQUE_ID(ntohl(lsah->id.s_addr))); /* Sanity check */ if (OspfSR.neighbors == NULL) { @@ -1225,16 +1415,17 @@ void ospf_sr_ri_lsa_delete(struct ospf_lsa *lsa) /* Sanity check */ if (srn == NULL) { flog_err(EC_OSPF_SR_NODE_CREATE, - "SR (%s): Abort! no entry in SRDB for SR Node %s", - __func__, inet_ntoa(lsah->adv_router)); + "SR (%s): Abort! no entry in SRDB for SR Node %pI4", + __func__, &lsah->adv_router); return; } if ((srn->instance != 0) && (srn->instance != ntohl(lsah->id.s_addr))) { - flog_err(EC_OSPF_SR_INVALID_LSA_ID, - "SR (%s): Abort! Wrong LSA ID 4.0.0.%u for SR node %s", - __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)), - inet_ntoa(lsah->adv_router)); + flog_err( + EC_OSPF_SR_INVALID_LSA_ID, + "SR (%s): Abort! Wrong LSA ID 4.0.0.%u for SR node %pI4", + __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)), + &lsah->adv_router); return; } @@ -1247,16 +1438,14 @@ void ospf_sr_ext_link_lsa_update(struct ospf_lsa *lsa) { struct sr_node *srn; struct tlv_header *tlvh; - struct lsa_header *lsah = (struct lsa_header *)lsa->data; + struct lsa_header *lsah = lsa->data; struct sr_link *srl; uint16_t length, sum; - if (IS_DEBUG_OSPF_SR) - zlog_debug( - "SR (%s): Process Extended Link LSA 8.0.0.%u from %s", - __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)), - inet_ntoa(lsah->adv_router)); + osr_debug("SR (%s): Process Extended Link LSA 8.0.0.%u from %pI4", + __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)), + &lsah->adv_router); /* Sanity check */ if (OspfSR.neighbors == NULL) { @@ -1302,13 +1491,12 @@ void ospf_sr_ext_link_lsa_delete(struct ospf_lsa *lsa) struct listnode *node; struct sr_link *srl; struct sr_node *srn; - struct lsa_header *lsah = (struct lsa_header *)lsa->data; + struct lsa_header *lsah = lsa->data; uint32_t instance = ntohl(lsah->id.s_addr); - if (IS_DEBUG_OSPF_SR) - zlog_debug("SR (%s): Remove Extended Link LSA 8.0.0.%u from %s", - __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)), - inet_ntoa(lsah->adv_router)); + osr_debug("SR (%s): Remove Extended Link LSA 8.0.0.%u from %pI4", + __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)), + &lsah->adv_router); /* Sanity check */ if (OspfSR.neighbors == NULL) { @@ -1327,8 +1515,8 @@ void ospf_sr_ext_link_lsa_delete(struct ospf_lsa *lsa) */ if (srn == NULL) { flog_err(EC_OSPF_SR_INVALID_DB, - "SR (%s): Stop! no entry in SRDB for SR Node %s", - __func__, inet_ntoa(lsah->adv_router)); + "SR (%s): Stop! no entry in SRDB for SR Node %pI4", + __func__, &lsah->adv_router); return; } @@ -1337,18 +1525,143 @@ void ospf_sr_ext_link_lsa_delete(struct ospf_lsa *lsa) if (srl->instance == instance) break; - /* Remove Segment Link if found */ + /* Remove Segment Link if found. Note that for Neighbors, only Global + * Adj/Lan-Adj SID are stored in the SR-DB */ if ((srl != NULL) && (srl->instance == instance)) { - del_sid_nhlfe(srl->nhlfe[0]); - del_sid_nhlfe(srl->nhlfe[1]); + del_adj_sid(srl->nhlfe[0]); + del_adj_sid(srl->nhlfe[1]); listnode_delete(srn->ext_link, srl); XFREE(MTYPE_OSPF_SR_PARAMS, srl); + } +} + +/* Add (LAN)Adjacency-SID from Extended Link Information */ +void ospf_sr_ext_itf_add(struct ext_itf *exti) +{ + struct sr_node *srn = OspfSR.self; + struct sr_link *srl; + + osr_debug("SR (%s): Add Extended Link LSA 8.0.0.%u from self", __func__, + exti->instance); + + /* Sanity check */ + if (srn == NULL) + return; + + /* Initialize new Segment Routing Link */ + srl = XCALLOC(MTYPE_OSPF_SR_PARAMS, sizeof(struct sr_link)); + srl->srn = srn; + srl->adv_router = srn->adv_router; + srl->itf_addr = exti->link.link_data; + srl->instance = + SET_OPAQUE_LSID(OPAQUE_TYPE_EXTENDED_LINK_LSA, exti->instance); + switch (exti->stype) { + case ADJ_SID: + srl->type = ADJ_SID; + /* Primary information */ + srl->flags[0] = exti->adj_sid[0].flags; + if (CHECK_FLAG(exti->adj_sid[0].flags, + EXT_SUBTLV_LINK_ADJ_SID_VFLG)) + srl->sid[0] = GET_LABEL(ntohl(exti->adj_sid[0].value)); + else + srl->sid[0] = ntohl(exti->adj_sid[0].value); + if (exti->rmt_itf_addr.header.type == 0) + srl->nhlfe[0].nexthop = exti->link.link_id; + else + srl->nhlfe[0].nexthop = exti->rmt_itf_addr.value; + /* Backup Information if set */ + if (exti->adj_sid[1].header.type == 0) + break; + srl->flags[1] = exti->adj_sid[1].flags; + if (CHECK_FLAG(exti->adj_sid[1].flags, + EXT_SUBTLV_LINK_ADJ_SID_VFLG)) + srl->sid[1] = GET_LABEL(ntohl(exti->adj_sid[1].value)); + else + srl->sid[1] = ntohl(exti->adj_sid[1].value); + if (exti->rmt_itf_addr.header.type == 0) + srl->nhlfe[1].nexthop = exti->link.link_id; + else + srl->nhlfe[1].nexthop = exti->rmt_itf_addr.value; + break; + case LAN_ADJ_SID: + srl->type = LAN_ADJ_SID; + /* Primary information */ + srl->flags[0] = exti->lan_sid[0].flags; + if (CHECK_FLAG(exti->lan_sid[0].flags, + EXT_SUBTLV_LINK_ADJ_SID_VFLG)) + srl->sid[0] = GET_LABEL(ntohl(exti->lan_sid[0].value)); + else + srl->sid[0] = ntohl(exti->lan_sid[0].value); + if (exti->rmt_itf_addr.header.type == 0) + srl->nhlfe[0].nexthop = exti->lan_sid[0].neighbor_id; + else + srl->nhlfe[0].nexthop = exti->rmt_itf_addr.value; + /* Backup Information if set */ + if (exti->lan_sid[1].header.type == 0) + break; + srl->flags[1] = exti->lan_sid[1].flags; + if (CHECK_FLAG(exti->lan_sid[1].flags, + EXT_SUBTLV_LINK_ADJ_SID_VFLG)) + srl->sid[1] = GET_LABEL(ntohl(exti->lan_sid[1].value)); + else + srl->sid[1] = ntohl(exti->lan_sid[1].value); + if (exti->rmt_itf_addr.header.type == 0) + srl->nhlfe[1].nexthop = exti->lan_sid[1].neighbor_id; + else + srl->nhlfe[1].nexthop = exti->rmt_itf_addr.value; + break; + default: + /* Wrong SID Type. Abort! */ + XFREE(MTYPE_OSPF_SR_PARAMS, srl); + return; + } + + /* Segment Routing Link is ready, update it */ + update_ext_link_sid(srn, srl, OSPF_LSA_SELF); +} + +/* Delete Prefix or (LAN)Adjacency-SID from Extended Link Information */ +void ospf_sr_ext_itf_delete(struct ext_itf *exti) +{ + struct listnode *node; + struct sr_node *srn = OspfSR.self; + struct sr_prefix *srp = NULL; + struct sr_link *srl = NULL; + uint32_t instance; + + osr_debug("SR (%s): Remove Extended LSA %u.0.0.%u from self", + __func__, exti->stype == PREF_SID ? 7 : 8, exti->instance); + + /* Sanity check: SR-Node and Extended Prefix/Link list may have been + * removed earlier when stopping OSPF or OSPF-SR */ + if (srn == NULL || srn->ext_prefix == NULL || srn->ext_link == NULL) + return; + + if (exti->stype == PREF_SID) { + instance = SET_OPAQUE_LSID(OPAQUE_TYPE_EXTENDED_PREFIX_LSA, + exti->instance); + for (ALL_LIST_ELEMENTS_RO(srn->ext_prefix, node, srp)) + if (srp->instance == instance) + break; + + /* Uninstall Segment Prefix SID if found */ + if ((srp != NULL) && (srp->instance == instance)) + ospf_zebra_delete_prefix_sid(srp); } else { - flog_err(EC_OSPF_SR_INVALID_DB, - "SR (%s): Didn't found corresponding SR Link 8.0.0.%u " - "for SR Node %s", - __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)), - inet_ntoa(lsah->adv_router)); + /* Search for corresponding Segment Link for self SR-Node */ + instance = SET_OPAQUE_LSID(OPAQUE_TYPE_EXTENDED_LINK_LSA, + exti->instance); + for (ALL_LIST_ELEMENTS_RO(srn->ext_link, node, srl)) + if (srl->instance == instance) + break; + + /* Remove Segment Link if found */ + if ((srl != NULL) && (srl->instance == instance)) { + del_adj_sid(srl->nhlfe[0]); + del_adj_sid(srl->nhlfe[1]); + listnode_delete(srn->ext_link, srl); + XFREE(MTYPE_OSPF_SR_PARAMS, srl); + } } } @@ -1362,12 +1675,9 @@ void ospf_sr_ext_prefix_lsa_update(struct ospf_lsa *lsa) uint16_t length, sum; - if (IS_DEBUG_OSPF_SR) - zlog_debug( - "SR (%s): Process Extended Prefix LSA " - "7.0.0.%u from %s", - __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)), - inet_ntoa(lsah->adv_router)); + osr_debug("SR (%s): Process Extended Prefix LSA 7.0.0.%u from %pI4", + __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)), + &lsah->adv_router); /* Sanity check */ if (OspfSR.neighbors == NULL) { @@ -1416,11 +1726,9 @@ void ospf_sr_ext_prefix_lsa_delete(struct ospf_lsa *lsa) struct lsa_header *lsah = (struct lsa_header *)lsa->data; uint32_t instance = ntohl(lsah->id.s_addr); - if (IS_DEBUG_OSPF_SR) - zlog_debug( - "SR (%s): Remove Extended Prefix LSA 7.0.0.%u from %s", - __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)), - inet_ntoa(lsah->adv_router)); + osr_debug("SR (%s): Remove Extended Prefix LSA 7.0.0.%u from %pI4", + __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)), + &lsah->adv_router); /* Sanity check */ if (OspfSR.neighbors == NULL) { @@ -1439,52 +1747,40 @@ void ospf_sr_ext_prefix_lsa_delete(struct ospf_lsa *lsa) */ if (srn == NULL) { flog_err(EC_OSPF_SR_INVALID_DB, - "SR (%s): Stop! no entry in SRDB for SR Node %s", - __func__, inet_ntoa(lsah->adv_router)); + "SR (%s): Stop! no entry in SRDB for SR Node %pI4", + __func__, &lsah->adv_router); return; } - /* Search for corresponding Segment Link */ + /* Search for corresponding Segment Prefix */ for (ALL_LIST_ELEMENTS_RO(srn->ext_prefix, node, srp)) if (srp->instance == instance) break; - /* Remove Segment Link if found */ + /* Remove Prefix if found */ if ((srp != NULL) && (srp->instance == instance)) { - del_sid_nhlfe(srp->nhlfe); - listnode_delete(srn->ext_link, srp); + ospf_zebra_delete_prefix_sid(srp); + listnode_delete(srn->ext_prefix, srp); XFREE(MTYPE_OSPF_SR_PARAMS, srp); } else { flog_err( EC_OSPF_SR_INVALID_DB, - "SR (%s): Didn't found corresponding SR Prefix 7.0.0.%u for SR Node %s", + "SR (%s): Didn't found corresponding SR Prefix 7.0.0.%u for SR Node %pI4", __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)), - inet_ntoa(lsah->adv_router)); + &lsah->adv_router); } } -/* Get Label for Extended Link SID */ -/* TODO: To be replace by Zebra Label Manager */ -uint32_t get_ext_link_label_value(void) -{ - static uint32_t label = ADJ_SID_MIN - 1; - - if (label < ADJ_SID_MAX) - label += 1; - - return label; -} - /* * Update Prefix SID. Call by ospf_ext_pref_ism_change to - * complete initial CLI command at startutp. + * complete initial CLI command at startup. * * @param ifp - Loopback interface * @param pref - Prefix address of this interface * * @return - void */ -void ospf_sr_update_prefix(struct interface *ifp, struct prefix *p) +void ospf_sr_update_local_prefix(struct interface *ifp, struct prefix *p) { struct listnode *node; struct sr_prefix *srp; @@ -1499,29 +1795,32 @@ void ospf_sr_update_prefix(struct interface *ifp, struct prefix *p) */ for (ALL_LIST_ELEMENTS_RO(OspfSR.self->ext_prefix, node, srp)) { if ((srp->nhlfe.ifindex == ifp->ifindex) - || ((IPV4_ADDR_SAME(&srp->nhlfe.prefv4.prefix, - &p->u.prefix4)) - && (srp->nhlfe.prefv4.prefixlen == p->prefixlen))) { + || ((IPV4_ADDR_SAME(&srp->prefv4.prefix, &p->u.prefix4)) + && (srp->prefv4.prefixlen == p->prefixlen))) { /* Update Interface & Prefix info */ srp->nhlfe.ifindex = ifp->ifindex; - IPV4_ADDR_COPY(&srp->nhlfe.prefv4.prefix, - &p->u.prefix4); - srp->nhlfe.prefv4.prefixlen = p->prefixlen; - srp->nhlfe.prefv4.family = p->family; + IPV4_ADDR_COPY(&srp->prefv4.prefix, &p->u.prefix4); + srp->prefv4.prefixlen = p->prefixlen; + srp->prefv4.family = p->family; IPV4_ADDR_COPY(&srp->nhlfe.nexthop, &p->u.prefix4); /* OK. Let's Schedule Extended Prefix LSA */ srp->instance = ospf_ext_schedule_prefix_index( - ifp, srp->sid, &srp->nhlfe.prefv4, srp->flags); - - /* Install NHLFE if NO-PHP is requested */ - if (CHECK_FLAG(srp->flags, - EXT_SUBTLV_PREFIX_SID_NPFLG)) { - srp->nhlfe.label_in = index2label( - srp->sid, OspfSR.self->srgb); + ifp, srp->sid, &srp->prefv4, srp->flags); + + osr_debug( + " |- Update Node SID %pFX - %u for self SR Node", + (struct prefix *)&srp->prefv4, srp->sid); + + /* Install SID if NO-PHP is set and not EXPLICIT-NULL */ + if (CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_NPFLG) + && !CHECK_FLAG(srp->flags, + EXT_SUBTLV_PREFIX_SID_EFLG)) { + srp->label_in = index2label(srp->sid, + OspfSR.self->srgb); srp->nhlfe.label_out = MPLS_LABEL_IMPLICIT_NULL; - add_sid_nhlfe(srp->nhlfe); + ospf_zebra_update_prefix_sid(srp); } } } @@ -1537,12 +1836,10 @@ static void ospf_sr_nhlfe_update(struct hash_bucket *bucket, void *args) struct sr_node *srn = (struct sr_node *)bucket->data; struct listnode *node; struct sr_prefix *srp; - struct sr_nhlfe old; + bool old; int rc; - if (IS_DEBUG_OSPF_SR) - zlog_debug(" |- Update Prefix for SR Node %s", - inet_ntoa(srn->adv_router)); + osr_debug(" |- Update Prefix for SR Node %pI4", &srn->adv_router); /* Skip Self SR Node */ if (srn == OspfSR.self) @@ -1551,24 +1848,25 @@ static void ospf_sr_nhlfe_update(struct hash_bucket *bucket, void *args) /* Update Extended Prefix */ for (ALL_LIST_ELEMENTS_RO(srn->ext_prefix, node, srp)) { - /* Backup current NHLFE */ - memcpy(&old, &srp->nhlfe, sizeof(struct sr_nhlfe)); + /* Keep track of valid route */ + old = srp->route != NULL; /* Compute the new NHLFE */ rc = compute_prefix_nhlfe(srp); /* Check computation result */ switch (rc) { - /* next hop is not know, remove old NHLFE to avoid loop */ + /* Routes are not know, remove old NHLFE if any to avoid loop */ case -1: - del_sid_nhlfe(srp->nhlfe); + if (old) + ospf_zebra_delete_prefix_sid(srp); break; - /* next hop has not changed, skip it */ + /* Routes exist but are not ready, skip it */ case 0: break; - /* there is a new next hop, update NHLFE */ + /* There is at least one route, update NHLFE */ case 1: - update_sid_nhlfe(old, srp->nhlfe); + ospf_zebra_update_prefix_sid(srp); break; default: break; @@ -1576,22 +1874,17 @@ static void ospf_sr_nhlfe_update(struct hash_bucket *bucket, void *args) } } -static int ospf_sr_update_schedule(struct thread *t) +void ospf_sr_update_task(struct ospf *ospf) { - struct ospf *ospf; struct timeval start_time, stop_time; - ospf = THREAD_ARG(t); - ospf->t_sr_update = NULL; - - if (!OspfSR.update) - return 0; + if (ospf == NULL) + return; monotime(&start_time); - if (IS_DEBUG_OSPF_SR) - zlog_debug("SR (%s): Start SPF update", __func__); + osr_debug("SR (%s): Start SPF update", __func__); hash_iterate(OspfSR.neighbors, (void (*)(struct hash_bucket *, void *))ospf_sr_nhlfe_update, @@ -1599,32 +1892,9 @@ static int ospf_sr_update_schedule(struct thread *t) monotime(&stop_time); - if (IS_DEBUG_OSPF_SR) - zlog_debug("SR (%s): SPF Processing Time(usecs): %lld", - __func__, - (stop_time.tv_sec - start_time.tv_sec) * 1000000LL - + (stop_time.tv_usec - start_time.tv_usec)); - - OspfSR.update = false; - return 1; -} - -#define OSPF_SR_UPDATE_INTERVAL 1 - -void ospf_sr_update_timer_add(struct ospf *ospf) -{ - - if (ospf == NULL) - return; - - /* Check if an update is not alreday engage */ - if (OspfSR.update) - return; - - OspfSR.update = true; - - thread_add_timer(master, ospf_sr_update_schedule, ospf, - OSPF_SR_UPDATE_INTERVAL, &ospf->t_sr_update); + osr_debug("SR (%s): SPF Processing Time(usecs): %lld", __func__, + (stop_time.tv_sec - start_time.tv_sec) * 1000000LL + + (stop_time.tv_usec - start_time.tv_usec)); } /* @@ -1648,17 +1918,23 @@ void ospf_sr_config_write_router(struct vty *vty) { struct listnode *node; struct sr_prefix *srp; + uint32_t upper; - if (OspfSR.enabled) { + if (OspfSR.status == SR_UP) { vty_out(vty, " segment-routing on\n"); - if ((OspfSR.srgb.lower_bound != MPLS_DEFAULT_MIN_SRGB_LABEL) - || (OspfSR.srgb.range_size != MPLS_DEFAULT_MAX_SRGB_SIZE)) { + upper = OspfSR.srgb.start + OspfSR.srgb.size - 1; + if ((OspfSR.srgb.start != DEFAULT_SRGB_LABEL) + || (OspfSR.srgb.size != DEFAULT_SRGB_SIZE)) vty_out(vty, " segment-routing global-block %u %u\n", - OspfSR.srgb.lower_bound, - OspfSR.srgb.lower_bound + OspfSR.srgb.range_size - - 1); - } + OspfSR.srgb.start, upper); + + upper = DEFAULT_SRLB_LABEL + DEFAULT_SRLB_SIZE - 1; + if ((OspfSR.srlb.start != DEFAULT_SRLB_LABEL) + || (OspfSR.srlb.end != upper)) + vty_out(vty, " segment-routing local-block %u %u\n", + OspfSR.srlb.start, OspfSR.srlb.end); + if (OspfSR.msd != 0) vty_out(vty, " segment-routing node-msd %u\n", OspfSR.msd); @@ -1668,13 +1944,18 @@ void ospf_sr_config_write_router(struct vty *vty) srp)) { vty_out(vty, " segment-routing prefix %s/%u " - "index %u%s\n", - inet_ntoa(srp->nhlfe.prefv4.prefix), - srp->nhlfe.prefv4.prefixlen, srp->sid, - CHECK_FLAG(srp->flags, - EXT_SUBTLV_PREFIX_SID_NPFLG) - ? " no-php-flag" - : ""); + "index %u", + inet_ntoa(srp->prefv4.prefix), + srp->prefv4.prefixlen, srp->sid); + if (CHECK_FLAG(srp->flags, + EXT_SUBTLV_PREFIX_SID_EFLG)) + vty_out(vty, " explicit-null\n"); + else if (CHECK_FLAG( + srp->flags, + EXT_SUBTLV_PREFIX_SID_NPFLG)) + vty_out(vty, " no-php-flag\n"); + else + vty_out(vty, "\n"); } } } @@ -1689,35 +1970,21 @@ DEFUN(ospf_sr_enable, VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); - if (OspfSR.enabled) + if (OspfSR.status != SR_OFF) return CMD_SUCCESS; if (ospf->vrf_id != VRF_DEFAULT) { vty_out(vty, - "Segment Routing is only supported in default " - "VRF\n"); + "Segment Routing is only supported in default VRF\n"); return CMD_WARNING_CONFIG_FAILED; } - if (IS_DEBUG_OSPF_EVENT) - zlog_debug("SR: Segment Routing: OFF -> ON"); + osr_debug("SR: Segment Routing: OFF -> ON"); /* Start Segment Routing */ - OspfSR.enabled = true; + OspfSR.status = SR_ON; ospf_sr_start(ospf); - /* Set Router Information SR parameters */ - if (IS_DEBUG_OSPF_EVENT) - zlog_debug("SR: Activate SR for Router Information LSA"); - - ospf_router_info_update_sr(true, OspfSR.srgb, OspfSR.msd); - - /* Update Ext LSA */ - if (IS_DEBUG_OSPF_EVENT) - zlog_debug("SR: Activate SR for Extended Link/Prefix LSA"); - - ospf_ext_update_sr(true); - return CMD_SUCCESS; } @@ -1729,28 +1996,26 @@ DEFUN (no_ospf_sr_enable, "Disable Segment Routing\n") { - if (!OspfSR.enabled) + if (OspfSR.status == SR_OFF) return CMD_SUCCESS; - if (IS_DEBUG_OSPF_EVENT) - zlog_debug("SR: Segment Routing: ON -> OFF"); + osr_debug("SR: Segment Routing: ON -> OFF"); /* Start by Disabling Extended Link & Prefix LSA */ ospf_ext_update_sr(false); /* then, disable Router Information SR parameters */ - ospf_router_info_update_sr(false, OspfSR.srgb, OspfSR.msd); + ospf_router_info_update_sr(false, OspfSR.self); /* Finally, stop Segment Routing */ ospf_sr_stop(); - OspfSR.enabled = false; return CMD_SUCCESS; } static int ospf_sr_enabled(struct vty *vty) { - if (OspfSR.enabled) + if (OspfSR.status != SR_OFF) return 1; if (vty) @@ -1759,13 +2024,78 @@ static int ospf_sr_enabled(struct vty *vty) return 0; } -DEFUN (sr_sid_label_range, - sr_sid_label_range_cmd, - "segment-routing global-block (0-1048575) (0-1048575)", +/** + * Update SRGB following new CLI value. + * + * @param lower Lower bound of the SRGB + * @param size Size of the SRGB + * + * @return 0 on success, -1 otherwise + */ +static int update_srgb(uint32_t lower, uint32_t size) +{ + + /* Check if values have changed */ + if ((OspfSR.srgb.size == size) && (OspfSR.srgb.start == lower)) + return 0; + + /* Release old SRGB if active. */ + if (OspfSR.srgb.reserved) { + ospf_zebra_release_label_range( + OspfSR.srgb.start, + OspfSR.srgb.start + OspfSR.srgb.size - 1); + OspfSR.srgb.reserved = false; + } + + /* Set new SRGB values */ + OspfSR.srgb.size = size; + OspfSR.srgb.start = lower; + if (OspfSR.self != NULL) { + OspfSR.self->srgb.range_size = size; + OspfSR.self->srgb.lower_bound = lower; + } + + /* Check if SR is correctly started i.e. Label Manager connected */ + if (OspfSR.status != SR_UP) + return 0; + + /* + * Try to reserve the new block from the Label Manger. If the allocation + * fails, disable SR until a new SRGB is successfully allocated. + */ + if (ospf_zebra_request_label_range(OspfSR.srgb.start, + OspfSR.srgb.size) < 0) { + OspfSR.srgb.reserved = false; + ospf_sr_stop(); + return -1; + } else + OspfSR.srgb.reserved = true; + + osr_debug("SR(%s): Got new SRGB [%u/%u]", __func__, OspfSR.srgb.start, + OspfSR.srgb.start + OspfSR.srgb.size - 1); + + /* Update Self SR-Node */ + if (OspfSR.self != NULL) { + /* SRGB is reserved, set Router Information parameters */ + ospf_router_info_update_sr(true, OspfSR.self); + + /* and update NHLFE entries */ + hash_iterate( + OspfSR.neighbors, + (void (*)(struct hash_bucket *, void *))update_in_nhlfe, + NULL); + } + + return 0; +} + +DEFUN (sr_global_label_range, + sr_global_label_range_cmd, + "segment-routing global-block (16-1048575) (16-1048575)", SR_STR "Segment Routing Global Block label range\n" - "Lower-bound range in decimal (0-1048575)\n" - "Upper-bound range in decimal (0-1048575)\n") + "Lower-bound range in decimal (16-1048575)\n" + "Upper-bound range in decimal (16-1048575)\n") { uint32_t upper; uint32_t lower; @@ -1781,77 +2111,156 @@ DEFUN (sr_sid_label_range, upper = strtoul(argv[idx_up]->arg, NULL, 10); size = upper - lower + 1; - if (size > MPLS_DEFAULT_MAX_SRGB_SIZE || size <= 0) { + /* Validate SRGB against SRLB */ + if (!((upper < OspfSR.srlb.start) || (lower > OspfSR.srlb.end))) { vty_out(vty, - "Range size cannot be less than 0 or more than %u\n", - MPLS_DEFAULT_MAX_SRGB_SIZE); + "New SR Global Block (%u/%u) conflict with Local Block (%u/%u)\n", + lower, upper, OspfSR.srlb.end, OspfSR.srlb.start); return CMD_WARNING_CONFIG_FAILED; } - if (upper > MPLS_DEFAULT_MAX_SRGB_LABEL) { - vty_out(vty, "Upper-bound cannot exceed %u\n", - MPLS_DEFAULT_MAX_SRGB_LABEL); + if (update_srgb(lower, size) < 0) return CMD_WARNING_CONFIG_FAILED; - } + else + return CMD_SUCCESS; +} - if (upper < MPLS_DEFAULT_MIN_SRGB_LABEL) { - vty_out(vty, "Upper-bound cannot be lower than %u\n", - MPLS_DEFAULT_MIN_SRGB_LABEL); +DEFUN (no_sr_global_label_range, + no_sr_global_label_range_cmd, + "no segment-routing global-block [(16-1048575) (16-1048575)]", + NO_STR + SR_STR + "Segment Routing Global Block label range\n" + "Lower-bound range in decimal (16-1048575)\n" + "Upper-bound range in decimal (16-1048575)\n") +{ + + if (!ospf_sr_enabled(vty)) + return CMD_WARNING_CONFIG_FAILED; + + /* Validate SRGB against SRLB */ + uint32_t upper = DEFAULT_SRGB_LABEL + DEFAULT_SRGB_SIZE - 1; + if (!((upper < OspfSR.srlb.start) + || (DEFAULT_SRGB_LABEL > OspfSR.srlb.end))) { + vty_out(vty, + "New SR Global Block (%u/%u) conflict with Local Block (%u/%u)\n", + DEFAULT_SRGB_LABEL, upper, OspfSR.srlb.end, + OspfSR.srlb.start); return CMD_WARNING_CONFIG_FAILED; } + if (update_srgb(DEFAULT_SRGB_LABEL, DEFAULT_SRGB_SIZE) < 0) + return CMD_WARNING_CONFIG_FAILED; + else + return CMD_SUCCESS; +} + +DEFUN (sr_local_label_range, + sr_local_label_range_cmd, + "segment-routing local-block (16-1048575) (16-1048575)", + SR_STR + "Segment Routing Local Block label range\n" + "Lower-bound range in decimal (16-1048575)\n" + "Upper-bound range in decimal (16-1048575)\n") +{ + uint32_t upper; + uint32_t lower; + uint32_t srgb_upper; + int idx_low = 2; + int idx_up = 3; + + if (!ospf_sr_enabled(vty)) + return CMD_WARNING_CONFIG_FAILED; + + /* Get lower and upper bound */ + lower = strtoul(argv[idx_low]->arg, NULL, 10); + upper = strtoul(argv[idx_up]->arg, NULL, 10); + /* Check if values have changed */ - if ((OspfSR.srgb.range_size == size) - && (OspfSR.srgb.lower_bound == lower)) + if ((OspfSR.srlb.start == lower) + && (OspfSR.srlb.end == upper)) return CMD_SUCCESS; - /* Set SID/Label range SRGB */ - OspfSR.srgb.range_size = size; - OspfSR.srgb.lower_bound = lower; - if (OspfSR.self != NULL) { - OspfSR.self->srgb.range_size = size; - OspfSR.self->srgb.lower_bound = lower; + /* Validate SRLB against SRGB */ + srgb_upper = OspfSR.srgb.start + OspfSR.srgb.size - 1; + if (!((upper < OspfSR.srgb.start) || (lower > srgb_upper))) { + vty_out(vty, + "New SR Local Block (%u/%u) conflict with Global Block (%u/%u)\n", + lower, upper, OspfSR.srgb.start, srgb_upper); + return CMD_WARNING_CONFIG_FAILED; } - /* Set Router Information SR parameters */ - ospf_router_info_update_sr(true, OspfSR.srgb, OspfSR.msd); + /* Remove old SRLB */ + sr_local_block_delete(); - /* Update NHLFE entries */ - hash_iterate(OspfSR.neighbors, - (void (*)(struct hash_bucket *, void *))update_in_nhlfe, - NULL); + /* Try to reserve the new block from the Label Manger. If the allocation + * fails, disable SR until a new SRLB is successfully allocated. + */ + if (sr_local_block_init(lower, upper) != 0) { + ospf_sr_stop(); + return CMD_WARNING_CONFIG_FAILED; + } + + /* SRLB is reserved, Update Self SR-Node and Router Information LSA */ + if (OspfSR.self != NULL) { + OspfSR.self->srlb.lower_bound = lower; + OspfSR.self->srlb.range_size = upper - lower + 1; + ospf_router_info_update_sr(true, OspfSR.self); + } + + /* and update (LAN)-Adjacency SID */ + ospf_ext_link_srlb_update(); return CMD_SUCCESS; } -DEFUN (no_sr_sid_label_range, - no_sr_sid_label_range_cmd, - "no segment-routing global-block [(0-1048575) (0-1048575)]", +DEFUN (no_sr_local_label_range, + no_sr_local_label_range_cmd, + "no segment-routing local-block [(16-1048575) (16-1048575)]", NO_STR SR_STR - "Segment Routing Global Block label range\n" - "Lower-bound range in decimal (0-1048575)\n" - "Upper-bound range in decimal (0-1048575)\n") + "Segment Routing Local Block label range\n" + "Lower-bound range in decimal (16-1048575)\n" + "Upper-bound range in decimal (16-1048575)\n") { + uint32_t upper; + uint32_t srgb_end; + if (!ospf_sr_enabled(vty)) return CMD_WARNING_CONFIG_FAILED; - /* Revert to default SRGB value */ - OspfSR.srgb.range_size = MPLS_DEFAULT_MIN_SRGB_SIZE; - OspfSR.srgb.lower_bound = MPLS_DEFAULT_MIN_SRGB_LABEL; - if (OspfSR.self != NULL) { - OspfSR.self->srgb.range_size = OspfSR.srgb.range_size; - OspfSR.self->srgb.lower_bound = OspfSR.srgb.lower_bound; + /* First, remove old SRLB */ + sr_local_block_delete(); + + /* Validate SRLB against SRGB */ + srgb_end = OspfSR.srgb.start + OspfSR.srgb.size - 1; + upper = DEFAULT_SRLB_LABEL + DEFAULT_SRLB_SIZE - 1; + if (!((upper < OspfSR.srgb.start) || (DEFAULT_SRLB_LABEL > srgb_end))) { + vty_out(vty, + "New SR Local Block (%u/%u) conflict with Global Block (%u/%u)\n", + DEFAULT_SRLB_LABEL, upper, OspfSR.srgb.start, srgb_end); + return CMD_WARNING_CONFIG_FAILED; } - /* Set Router Information SR parameters */ - ospf_router_info_update_sr(true, OspfSR.srgb, OspfSR.msd); + /* Then, initialize SRLB with default value and try to reserve the new + * block from the Label Manger. If the allocation fails, disable SR + * until a new SRLB is successfully allocated. + */ + if (sr_local_block_init(DEFAULT_SRLB_LABEL, upper) != 0) { + ospf_sr_stop(); + return CMD_WARNING_CONFIG_FAILED; + } - /* Update NHLFE entries */ - hash_iterate(OspfSR.neighbors, - (void (*)(struct hash_bucket *, void *))update_in_nhlfe, - NULL); + /* SRLB is reserved, Update Self SR-Node and Router Information LSA */ + if (OspfSR.self != NULL) { + OspfSR.self->srlb.lower_bound = DEFAULT_SRLB_LABEL; + OspfSR.self->srlb.range_size = DEFAULT_SRLB_SIZE; + ospf_router_info_update_sr(true, OspfSR.self); + } + + /* and update (LAN)-Adjacency SID */ + ospf_ext_link_srlb_update(); return CMD_SUCCESS; } @@ -1884,11 +2293,13 @@ DEFUN (sr_node_msd, /* Set this router MSD */ OspfSR.msd = msd; - if (OspfSR.self != NULL) + if (OspfSR.self != NULL) { OspfSR.self->msd = msd; - /* Set Router Information SR parameters */ - ospf_router_info_update_sr(true, OspfSR.srgb, OspfSR.msd); + /* Set Router Information parameters if SR is UP */ + if (OspfSR.status == SR_UP) + ospf_router_info_update_sr(true, OspfSR.self); + } return CMD_SUCCESS; } @@ -1907,30 +2318,33 @@ DEFUN (no_sr_node_msd, /* unset this router MSD */ OspfSR.msd = 0; - if (OspfSR.self != NULL) + if (OspfSR.self != NULL) { OspfSR.self->msd = 0; - /* Set Router Information SR parameters */ - ospf_router_info_update_sr(true, OspfSR.srgb, 0); + /* Set Router Information parameters if SR is UP */ + if (OspfSR.status == SR_UP) + ospf_router_info_update_sr(true, OspfSR.self); + } return CMD_SUCCESS; } DEFUN (sr_prefix_sid, sr_prefix_sid_cmd, - "segment-routing prefix A.B.C.D/M index (0-65535) [no-php-flag]", + "segment-routing prefix A.B.C.D/M index (0-65535) [no-php-flag|explicit-null]", SR_STR "Prefix SID\n" "IPv4 Prefix as A.B.C.D/M\n" "SID index for this prefix in decimal (0-65535)\n" "Index value inside SRGB (lower_bound < index < upper_bound)\n" - "Don't request Penultimate Hop Popping (PHP)\n") + "Don't request Penultimate Hop Popping (PHP)\n" + "Upstream neighbor must replace prefix-sid with explicit null label\n") { int idx = 0; struct prefix p; uint32_t index; struct listnode *node; - struct sr_prefix *srp, *new; + struct sr_prefix *srp, *new = NULL; struct interface *ifp; if (!ospf_sr_enabled(vty)) @@ -1946,38 +2360,51 @@ DEFUN (sr_prefix_sid, /* Get & verify index value */ argv_find(argv, argc, "(0-65535)", &idx); index = strtoul(argv[idx]->arg, NULL, 10); - if (index > OspfSR.srgb.range_size - 1) { + if (index > OspfSR.srgb.size - 1) { vty_out(vty, "Index %u must be lower than range size %u\n", - index, OspfSR.srgb.range_size); + index, OspfSR.srgb.size); return CMD_WARNING_CONFIG_FAILED; } - /* check that the index is not already used */ + /* Search for an existing Prefix-SID */ for (ALL_LIST_ELEMENTS_RO(OspfSR.self->ext_prefix, node, srp)) { if (srp->sid == index) { - vty_out(vty, "Index %u is already used\n", index); - return CMD_WARNING_CONFIG_FAILED; + if (prefix_same((struct prefix *)&srp->prefv4, &p)) { + new = srp; + break; + } else { + vty_out(vty, "Index %u is already used\n", + index); + return CMD_WARNING_CONFIG_FAILED; + } } } /* Create new Extended Prefix to SRDB if not found */ - new = XCALLOC(MTYPE_OSPF_SR_PARAMS, sizeof(struct sr_prefix)); - IPV4_ADDR_COPY(&new->nhlfe.prefv4.prefix, &p.u.prefix4); - IPV4_ADDR_COPY(&new->nhlfe.nexthop, &p.u.prefix4); - new->nhlfe.prefv4.prefixlen = p.prefixlen; - new->nhlfe.prefv4.family = p.family; - new->sid = index; + if (new == NULL) { + new = XCALLOC(MTYPE_OSPF_SR_PARAMS, sizeof(struct sr_prefix)); + IPV4_ADDR_COPY(&new->prefv4.prefix, &p.u.prefix4); + new->prefv4.prefixlen = p.prefixlen; + new->prefv4.family = p.family; + new->sid = index; + new->type = LOCAL_SID; + } + /* Set NO PHP flag if present and compute NHLFE */ if (argv_find(argv, argc, "no-php-flag", &idx)) { SET_FLAG(new->flags, EXT_SUBTLV_PREFIX_SID_NPFLG); - new->nhlfe.label_in = index2label(new->sid, OspfSR.self->srgb); + UNSET_FLAG(new->flags, EXT_SUBTLV_PREFIX_SID_EFLG); + new->label_in = index2label(new->sid, OspfSR.self->srgb); new->nhlfe.label_out = MPLS_LABEL_IMPLICIT_NULL; } + /* Set EXPLICIT NULL flag is present */ + if (argv_find(argv, argc, "explicit-null", &idx)) { + SET_FLAG(new->flags, EXT_SUBTLV_PREFIX_SID_NPFLG); + SET_FLAG(new->flags, EXT_SUBTLV_PREFIX_SID_EFLG); + } - if (IS_DEBUG_OSPF_SR) - zlog_debug("SR (%s): Add new index %u to Prefix %s/%u", - __func__, index, inet_ntoa(new->nhlfe.prefv4.prefix), - new->nhlfe.prefv4.prefixlen); + osr_debug("SR (%s): Add new index %u to Prefix %pFX", __func__, index, + (struct prefix *)&new->prefv4); /* Get Interface and check if it is a Loopback */ ifp = if_lookup_prefix(&p, VRF_DEFAULT); @@ -1990,9 +2417,8 @@ DEFUN (sr_prefix_sid, */ listnode_add(OspfSR.self->ext_prefix, new); zlog_info( - "Interface for prefix %s/%u not found. Deferred LSA " - "flooding", - inet_ntoa(p.u.prefix4), p.prefixlen); + "Interface for prefix %pFX not found. Deferred LSA flooding", + &p); return CMD_SUCCESS; } @@ -2003,28 +2429,21 @@ DEFUN (sr_prefix_sid, } new->nhlfe.ifindex = ifp->ifindex; - /* Search if this prefix already exist */ - for (ALL_LIST_ELEMENTS_RO(OspfSR.self->ext_prefix, node, srp)) { - if ((IPV4_ADDR_SAME(&srp->nhlfe.prefv4.prefix, &p.u.prefix4) - && srp->nhlfe.prefv4.prefixlen == p.prefixlen)) - break; - else - srp = NULL; - } - - /* Update or Add this new SR Prefix */ - if (srp) { - update_sid_nhlfe(srp->nhlfe, new->nhlfe); - listnode_delete(OspfSR.self->ext_prefix, srp); - listnode_add(OspfSR.self->ext_prefix, new); - } else { + /* Add this new SR Prefix if not already found */ + if (srp != new) listnode_add(OspfSR.self->ext_prefix, new); - add_sid_nhlfe(new->nhlfe); - } - /* Finally, update Extended Prefix LSA */ + /* Install Prefix SID if SR is UP and a valid input label set */ + if (OspfSR.status == SR_UP) { + if (CHECK_FLAG(new->flags, EXT_SUBTLV_PREFIX_SID_NPFLG) + && !CHECK_FLAG(new->flags, EXT_SUBTLV_PREFIX_SID_EFLG)) + ospf_zebra_update_prefix_sid(new); + } else + return CMD_SUCCESS; + + /* Finally, update Extended Prefix LSA id SR is UP */ new->instance = ospf_ext_schedule_prefix_index( - ifp, new->sid, &new->nhlfe.prefv4, new->flags); + ifp, new->sid, &new->prefv4, new->flags); if (new->instance == 0) { vty_out(vty, "Unable to set index %u for prefix %s/%u\n", index, inet_ntoa(p.u.prefix4), p.prefixlen); @@ -2036,14 +2455,15 @@ DEFUN (sr_prefix_sid, DEFUN (no_sr_prefix_sid, no_sr_prefix_sid_cmd, - "no segment-routing prefix A.B.C.D/M [index (0-65535) no-php-flag]", + "no segment-routing prefix A.B.C.D/M [index (0-65535)|no-php-flag|explicit-null]", NO_STR SR_STR "Prefix SID\n" "IPv4 Prefix as A.B.C.D/M\n" "SID index for this prefix in decimal (0-65535)\n" "Index value inside SRGB (lower_bound < index < upper_bound)\n" - "Don't request Penultimate Hop Popping (PHP)\n") + "Don't request Penultimate Hop Popping (PHP)\n" + "Upstream neighbor must replace prefix-sid with explicit null label\n") { int idx = 0; struct prefix p; @@ -2056,6 +2476,9 @@ DEFUN (no_sr_prefix_sid, if (!ospf_sr_enabled(vty)) return CMD_WARNING_CONFIG_FAILED; + if (OspfSR.status != SR_UP) + return CMD_SUCCESS; + /* Get network prefix */ argv_find(argv, argc, "A.B.C.D/M", &idx); rc = str2prefix(argv[idx]->arg, &p); @@ -2066,8 +2489,8 @@ DEFUN (no_sr_prefix_sid, /* check that the prefix is already set */ for (ALL_LIST_ELEMENTS_RO(OspfSR.self->ext_prefix, node, srp)) - if (IPV4_ADDR_SAME(&srp->nhlfe.prefv4.prefix, &p.u.prefix4) - && (srp->nhlfe.prefv4.prefixlen == p.prefixlen)) { + if (IPV4_ADDR_SAME(&srp->prefv4.prefix, &p.u.prefix4) + && (srp->prefv4.prefixlen == p.prefixlen)) { found = true; break; } @@ -2092,14 +2515,13 @@ DEFUN (no_sr_prefix_sid, return CMD_WARNING; } - if (IS_DEBUG_OSPF_SR) - zlog_debug("SR (%s): Remove Prefix %s/%u with index %u", - __func__, inet_ntoa(srp->nhlfe.prefv4.prefix), - srp->nhlfe.prefv4.prefixlen, srp->sid); + osr_debug("SR (%s): Remove Prefix %pFX with index %u", __func__, + (struct prefix *)&srp->prefv4, srp->sid); - /* Delete NHLFE is NO-PHP is set */ - if (CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_NPFLG)) - del_sid_nhlfe(srp->nhlfe); + /* Delete NHLFE if NO-PHP is set and EXPLICIT NULL not set */ + if (CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_NPFLG) + && !CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_EFLG)) + ospf_zebra_delete_prefix_sid(srp); /* OK, all is clean, remove SRP from SRDB */ listnode_delete(OspfSR.self->ext_prefix, srp); @@ -2109,6 +2531,119 @@ DEFUN (no_sr_prefix_sid, } +static char *sr_op2str(char *buf, size_t size, mpls_label_t label_in, + mpls_label_t label_out) +{ + if (size < 24) + return NULL; + + switch (label_out) { + case MPLS_LABEL_IMPLICIT_NULL: + snprintf(buf, size, "Pop(%u)", label_in); + break; + case MPLS_LABEL_IPV4_EXPLICIT_NULL: + if (label_in == MPLS_LABEL_IPV4_EXPLICIT_NULL) + snprintf(buf, size, "no-op."); + else + snprintf(buf, size, "Swap(%u, null)", label_in); + break; + case MPLS_INVALID_LABEL: + snprintf(buf, size, "no-op."); + break; + default: + snprintf(buf, size, "Swap(%u, %u)", label_in, label_out); + break; + } + return buf; +} + +static void show_sr_prefix(struct sbuf *sbuf, struct json_object *json, + struct sr_prefix *srp) +{ + + struct listnode *node; + struct ospf_path *path; + struct interface *itf; + json_object *json_route = NULL, *json_obj; + char pref[19]; + char sid[22]; + char op[32]; + int indent = 0; + + snprintfrr(pref, 19, "%pFX", (struct prefix *)&srp->prefv4); + snprintf(sid, 22, "SR Pfx (idx %u)", srp->sid); + if (json) { + json_object_string_add(json, "prefix", pref); + json_object_int_add(json, "sid", srp->sid); + json_object_int_add(json, "inputLabel", srp->label_in); + } else { + sbuf_push(sbuf, 0, "%18s %21s ", pref, sid); + } + + /* Check if it is a Local Node SID */ + if (srp->type == LOCAL_SID) { + itf = if_lookup_by_index(srp->nhlfe.ifindex, VRF_DEFAULT); + if (json) { + if (!json_route) { + json_route = json_object_new_array(); + json_object_object_add(json, "prefixRoute", + json_route); + } + json_obj = json_object_new_object(); + json_object_int_add(json_obj, "outputLabel", + srp->nhlfe.label_out); + json_object_string_add(json_obj, "interface", + itf ? itf->name : "-"); + json_object_string_add(json_obj, "nexthop", + inet_ntoa(srp->nhlfe.nexthop)); + json_object_array_add(json_route, json_obj); + } else { + sbuf_push(sbuf, 0, "%20s %9s %15s\n", + sr_op2str(op, 32, srp->label_in, + srp->nhlfe.label_out), + itf ? itf->name : "-", + inet_ntoa(srp->nhlfe.nexthop)); + } + return; + } + + /* Check if we have a valid path for this prefix */ + if (srp->route == NULL) { + if (!json) { + sbuf_push(sbuf, 0, "\n"); + } + return; + } + + /* Process list of OSPF paths */ + for (ALL_LIST_ELEMENTS_RO(srp->route->paths, node, path)) { + itf = if_lookup_by_index(path->ifindex, VRF_DEFAULT); + if (json) { + if (!json_route) { + json_route = json_object_new_array(); + json_object_object_add(json, "prefixRoute", + json_route); + } + json_obj = json_object_new_object(); + json_object_int_add(json_obj, "outputLabel", + path->srni.label_out); + json_object_string_add(json_obj, "interface", + itf ? itf->name : "-"); + json_object_string_add(json_obj, "nexthop", + inet_ntoa(path->nexthop)); + json_object_array_add(json_route, json_obj); + } else { + sbuf_push(sbuf, indent, "%20s %9s %15s\n", + sr_op2str(op, 32, srp->label_in, + path->srni.label_out), + itf ? itf->name : "-", + inet_ntoa(path->nexthop)); + /* Offset to align information for ECMP */ + indent = 43; + } + } +} + static void show_sr_node(struct vty *vty, struct json_object *json, struct sr_node *srn) { @@ -2117,9 +2652,11 @@ static void show_sr_node(struct vty *vty, struct json_object *json, struct sr_link *srl; struct sr_prefix *srp; struct interface *itf; + struct sbuf sbuf; char pref[19]; char sid[22]; - char label[8]; + char op[32]; + uint32_t upper; json_object *json_node = NULL, *json_algo, *json_obj; json_object *json_prefix = NULL, *json_link = NULL; @@ -2127,6 +2664,8 @@ static void show_sr_node(struct vty *vty, struct json_object *json, if (srn == NULL) return; + sbuf_init(&sbuf, NULL, 0); + if (json) { json_node = json_object_new_object(); json_object_string_add(json_node, "routerID", @@ -2135,6 +2674,10 @@ static void show_sr_node(struct vty *vty, struct json_object *json, srn->srgb.range_size); json_object_int_add(json_node, "srgbLabel", srn->srgb.lower_bound); + json_object_int_add(json_node, "srlbSize", + srn->srlb.range_size); + json_object_int_add(json_node, "srlbLabel", + srn->srlb.lower_bound); json_algo = json_object_new_array(); json_object_object_add(json_node, "algorithms", json_algo); for (int i = 0; i < ALGORITHM_COUNT; i++) { @@ -2143,7 +2686,7 @@ static void show_sr_node(struct vty *vty, struct json_object *json, json_obj = json_object_new_object(); char tmp[2]; - snprintf(tmp, 2, "%u", i); + snprintf(tmp, sizeof(tmp), "%u", i); json_object_string_add(json_obj, tmp, srn->algo[i] == SR_ALGORITHM_SPF ? "SPF" @@ -2153,39 +2696,33 @@ static void show_sr_node(struct vty *vty, struct json_object *json, if (srn->msd != 0) json_object_int_add(json_node, "nodeMsd", srn->msd); } else { - vty_out(vty, "SR-Node: %s", inet_ntoa(srn->adv_router)); - vty_out(vty, "\tSRGB (Size/Label): %u/%u", srn->srgb.range_size, - srn->srgb.lower_bound); - vty_out(vty, "\tAlgorithm(s): %s", - srn->algo[0] == SR_ALGORITHM_SPF ? "SPF" : "S-SPF"); + sbuf_push(&sbuf, 0, "SR-Node: %s", inet_ntoa(srn->adv_router)); + upper = srn->srgb.lower_bound + srn->srgb.range_size - 1; + sbuf_push(&sbuf, 0, "\tSRGB: [%u/%u]", + srn->srgb.lower_bound, upper); + upper = srn->srlb.lower_bound + srn->srlb.range_size - 1; + sbuf_push(&sbuf, 0, "\tSRLB: [%u/%u]", + srn->srlb.lower_bound, upper); + sbuf_push(&sbuf, 0, "\tAlgo.(s): %s", + srn->algo[0] == SR_ALGORITHM_SPF ? "SPF" : "S-SPF"); for (int i = 1; i < ALGORITHM_COUNT; i++) { if (srn->algo[i] == SR_ALGORITHM_UNSET) continue; - vty_out(vty, "/%s", - srn->algo[i] == SR_ALGORITHM_SPF ? "SPF" - : "S-SPF"); + sbuf_push(&sbuf, 0, "/%s", + srn->algo[i] == SR_ALGORITHM_SPF ? "SPF" + : "S-SPF"); } if (srn->msd != 0) - vty_out(vty, "\tMSD: %u", srn->msd); + sbuf_push(&sbuf, 0, "\tMSD: %u", srn->msd); } if (!json) { - vty_out(vty, - "\n\n Prefix or Link Label In Label Out " - "Node or Adj. SID Interface Nexthop\n"); - vty_out(vty, - "------------------ -------- --------- " - "--------------------- --------- ---------------\n"); + sbuf_push(&sbuf, 0, + "\n\n Prefix or Link Node or Adj. SID Label Operation Interface Nexthop\n"); + sbuf_push(&sbuf, 0, + "------------------ --------------------- -------------------- --------- ---------------\n"); } for (ALL_LIST_ELEMENTS_RO(srn->ext_prefix, node, srp)) { - snprintf(pref, 19, "%s/%u", inet_ntoa(srp->nhlfe.prefv4.prefix), - srp->nhlfe.prefv4.prefixlen); - snprintf(sid, 22, "SR Pfx (idx %u)", srp->sid); - if (srp->nhlfe.label_out == MPLS_LABEL_IMPLICIT_NULL) - sprintf(label, "pop"); - else - sprintf(label, "%u", srp->nhlfe.label_out); - itf = if_lookup_by_index(srp->nhlfe.ifindex, VRF_DEFAULT); if (json) { if (!json_prefix) { json_prefix = json_object_new_array(); @@ -2194,33 +2731,16 @@ static void show_sr_node(struct vty *vty, struct json_object *json, json_prefix); } json_obj = json_object_new_object(); - json_object_string_add(json_obj, "prefix", pref); - json_object_int_add(json_obj, "sid", srp->sid); - json_object_int_add(json_obj, "inputLabel", - srp->nhlfe.label_in); - json_object_string_add(json_obj, "outputLabel", label); - json_object_string_add(json_obj, "interface", - itf ? itf->name : "-"); - json_object_string_add(json_obj, "nexthop", - inet_ntoa(srp->nhlfe.nexthop)); + show_sr_prefix(NULL, json_obj, srp); json_object_array_add(json_prefix, json_obj); } else { - vty_out(vty, "%18s %8u %9s %21s %9s %15s\n", pref, - srp->nhlfe.label_in, label, sid, - itf ? itf->name : "-", - inet_ntoa(srp->nhlfe.nexthop)); + show_sr_prefix(&sbuf, NULL, srp); } } for (ALL_LIST_ELEMENTS_RO(srn->ext_link, node, srl)) { - snprintf(pref, 19, "%s/%u", - inet_ntoa(srl->nhlfe[0].prefv4.prefix), - srl->nhlfe[0].prefv4.prefixlen); + snprintfrr(pref, 19, "%pI4/32", &srl->itf_addr); snprintf(sid, 22, "SR Adj. (lbl %u)", srl->sid[0]); - if (srl->nhlfe[0].label_out == MPLS_LABEL_IMPLICIT_NULL) - sprintf(label, "pop"); - else - sprintf(label, "%u", srl->nhlfe[0].label_out); itf = if_lookup_by_index(srl->nhlfe[0].ifindex, VRF_DEFAULT); if (json) { if (!json_link) { @@ -2234,7 +2754,8 @@ static void show_sr_node(struct vty *vty, struct json_object *json, json_object_int_add(json_obj, "sid", srl->sid[0]); json_object_int_add(json_obj, "inputLabel", srl->nhlfe[0].label_in); - json_object_string_add(json_obj, "outputLabel", label); + json_object_int_add(json_obj, "outputLabel", + srl->nhlfe[0].label_out); json_object_string_add(json_obj, "interface", itf ? itf->name : "-"); json_object_string_add( @@ -2244,15 +2765,12 @@ static void show_sr_node(struct vty *vty, struct json_object *json, /* Backup Link */ json_obj = json_object_new_object(); snprintf(sid, 22, "SR Adj. (lbl %u)", srl->sid[1]); - if (srl->nhlfe[1].label_out == MPLS_LABEL_IMPLICIT_NULL) - sprintf(label, "pop"); - else - sprintf(label, "%u", srl->nhlfe[0].label_out); json_object_string_add(json_obj, "prefix", pref); json_object_int_add(json_obj, "sid", srl->sid[1]); json_object_int_add(json_obj, "inputLabel", srl->nhlfe[1].label_in); - json_object_string_add(json_obj, "outputLabel", label); + json_object_int_add(json_obj, "outputLabel", + srl->nhlfe[1].label_out); json_object_string_add(json_obj, "interface", itf ? itf->name : "-"); json_object_string_add( @@ -2260,25 +2778,27 @@ static void show_sr_node(struct vty *vty, struct json_object *json, inet_ntoa(srl->nhlfe[1].nexthop)); json_object_array_add(json_link, json_obj); } else { - vty_out(vty, "%18s %8u %9s %21s %9s %15s\n", pref, - srl->nhlfe[0].label_in, label, sid, - itf ? itf->name : "-", - inet_ntoa(srl->nhlfe[0].nexthop)); + sbuf_push(&sbuf, 0, "%18s %21s %20s %9s %15s\n", + pref, sid, + sr_op2str(op, 32, srl->nhlfe[0].label_in, + srl->nhlfe[0].label_out), + itf ? itf->name : "-", + inet_ntoa(srl->nhlfe[0].nexthop)); snprintf(sid, 22, "SR Adj. (lbl %u)", srl->sid[1]); - if (srl->nhlfe[1].label_out == MPLS_LABEL_IMPLICIT_NULL) - sprintf(label, "pop"); - else - sprintf(label, "%u", srl->nhlfe[1].label_out); - vty_out(vty, "%18s %8u %9s %21s %9s %15s\n", pref, - srl->nhlfe[1].label_in, label, sid, - itf ? itf->name : "-", - inet_ntoa(srl->nhlfe[1].nexthop)); + sbuf_push(&sbuf, 0, "%18s %21s %20s %9s %15s\n", + pref, sid, + sr_op2str(op, 32, srl->nhlfe[1].label_in, + srl->nhlfe[1].label_out), + itf ? itf->name : "-", + inet_ntoa(srl->nhlfe[1].nexthop)); } } if (json) json_object_array_add(json, json_node); else - vty_out(vty, "\n"); + vty_out(vty, "%s\n", sbuf_buf(&sbuf)); + + sbuf_free(&sbuf); } static void show_vty_srdb(struct hash_bucket *bucket, void *args) @@ -2316,7 +2836,7 @@ DEFUN (show_ip_opsf_srdb, bool uj = use_json(argc, argv); json_object *json = NULL, *json_node_array = NULL; - if (!OspfSR.enabled) { + if (OspfSR.status == SR_OFF) { vty_out(vty, "Segment Routing is disabled on this router\n"); return CMD_WARNING; } @@ -2387,8 +2907,10 @@ void ospf_sr_register_vty(void) install_element(OSPF_NODE, &ospf_sr_enable_cmd); install_element(OSPF_NODE, &no_ospf_sr_enable_cmd); - install_element(OSPF_NODE, &sr_sid_label_range_cmd); - install_element(OSPF_NODE, &no_sr_sid_label_range_cmd); + install_element(OSPF_NODE, &sr_global_label_range_cmd); + install_element(OSPF_NODE, &no_sr_global_label_range_cmd); + install_element(OSPF_NODE, &sr_local_label_range_cmd); + install_element(OSPF_NODE, &no_sr_local_label_range_cmd); install_element(OSPF_NODE, &sr_node_msd_cmd); install_element(OSPF_NODE, &no_sr_node_msd_cmd); install_element(OSPF_NODE, &sr_prefix_sid_cmd); diff --git a/ospfd/ospf_sr.h b/ospfd/ospf_sr.h index 4d3f5f441a..c54d2dcc3c 100644 --- a/ospfd/ospf_sr.h +++ b/ospfd/ospf_sr.h @@ -1,13 +1,14 @@ /* * This is an implementation of Segment Routing - * as per draft draft-ietf-ospf-segment-routing-extensions-24 + * as per RFC 8665 - OSPF Extensions for Segment Routing + * and RFC 8476 - Signaling Maximum SID Depth (MSD) Using OSPF * * Module name: Segment Routing header definitions * * Author: Olivier Dugeon * Author: Anselme Sawadogo * - * Copyright (C) 2016 - 2018 Orange Labs http://www.orange.com + * Copyright (C) 2016 - 2020 Orange Labs http://www.orange.com * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -27,9 +28,6 @@ #ifndef _FRR_OSPF_SR_H #define _FRR_OSPF_SR_H -/* Default Route priority for OSPF Segment Routing */ -#define OSPF_SR_PRIORITY_DEFAULT 10 - /* macros and constants for segment routing */ #define SET_RANGE_SIZE_MASK 0xffffff00 #define GET_RANGE_SIZE_MASK 0x00ffffff @@ -40,13 +38,9 @@ #define SET_LABEL(label) ((label << 8) & SET_LABEL_MASK) #define GET_LABEL(label) ((label >> 8) & GET_LABEL_MASK) -/* Label range for Adj-SID attribution purpose. Start just right after SRGB */ -#define ADJ_SID_MIN MPLS_DEFAULT_MAX_SRGB_LABEL -#define ADJ_SID_MAX (MPLS_DEFAULT_MAX_SRGB_LABEL + 1000) - #define OSPF_SR_DEFAULT_METRIC 1 -/* Segment Routing TLVs as per draft-ietf-ospf-segment-routing-extensions-19 */ +/* Segment Routing TLVs as per RFC 8665 */ /* Segment ID could be a Label (3 bytes) or an Index (4 bytes) */ #define SID_LABEL 3 @@ -54,6 +48,17 @@ #define SID_INDEX 4 #define SID_INDEX_SIZE(U) (U) +/* Macro to log debug message */ +#define osr_debug(...) \ + do { \ + if (IS_DEBUG_OSPF_SR) \ + zlog_debug(__VA_ARGS__); \ + } while (0) + +/* Macro to check if SR Prefix has no valid route */ +#define IS_NO_ROUTE(srp) ((srp->route == NULL) || (srp->route->paths == NULL) \ + || list_isempty(srp->route->paths)) + /* SID/Label Sub TLV - section 2.1 */ #define SUBTLV_SID_LABEL 1 #define SUBTLV_SID_LABEL_SIZE 8 @@ -80,8 +85,9 @@ struct ri_sr_tlv_sr_algorithm { uint8_t value[ALGORITHM_COUNT]; }; -/* RI SID/Label Range TLV - section 3.2 */ -#define RI_SR_TLV_SID_LABEL_RANGE 9 +/* RI SID/Label Range TLV used for SRGB & SRLB - section 3.2 & 3.3 */ +#define RI_SR_TLV_SRGB_LABEL_RANGE 9 +#define RI_SR_TLV_SRLB_LABEL_RANGE 14 struct ri_sr_tlv_sid_label_range { struct tlv_header header; /* Only 24 upper most bits are significant */ @@ -91,7 +97,7 @@ struct ri_sr_tlv_sid_label_range { struct subtlv_sid_label lower; }; -/* RI Node/MSD TLV as per draft-ietf-ospf-segment-routing-msd-05 */ +/* RI Node/MSD TLV as per RFC 8476 */ #define RI_SR_TLV_NODE_MSD 12 struct ri_sr_tlv_node_msd { struct tlv_header header; @@ -175,23 +181,48 @@ struct ext_subtlv_lan_adj_sid { * Following section define structure used to manage Segment Routing * information and TLVs / SubTLVs */ +/* Default min and size of SR Global Block label range */ +#define DEFAULT_SRGB_LABEL 16000 +#define DEFAULT_SRGB_SIZE 8000 -/* Structure aggregating SRGB info retrieved from an lsa */ -struct sr_srgb { +/* Default min and size of SR Local Block label range */ +#define DEFAULT_SRLB_LABEL 15000 +#define DEFAULT_SRLB_SIZE 1000 + +/* Structure aggregating SR Range Block info retrieved from an lsa */ +struct sr_block { uint32_t range_size; uint32_t lower_bound; }; +/* Segment Routing Global Block allocation */ +struct sr_global_block { + bool reserved; + uint32_t start; + uint32_t size; +}; + +/* Segment Routing Local Block allocation */ +struct sr_local_block { + bool reserved; + uint32_t start; + uint32_t end; + uint32_t current; + uint32_t max_block; + uint64_t *used_mark; +}; +#define SRLB_BLOCK_SIZE 64 + /* SID type to make difference between loopback interfaces and others */ -enum sid_type { PREF_SID, ADJ_SID, LAN_ADJ_SID }; +enum sid_type { PREF_SID, LOCAL_SID, ADJ_SID, LAN_ADJ_SID }; + +/* Status of Segment Routing: Off (Disable), On (Enable), (Up) Started */ +enum sr_status { SR_OFF, SR_ON, SR_UP, SR_DOWN }; /* Structure aggregating all OSPF Segment Routing information for the node */ struct ospf_sr_db { - /* Status of Segment Routing: enable or disable */ - bool enabled; - - /* Ongoing Update following an OSPF SPF */ - bool update; + /* Status of Segment Routing */ + enum sr_status status; /* Flooding Scope: Area = 10 or AS = 11 */ uint8_t scope; @@ -213,9 +244,16 @@ struct ospf_sr_db { * Segment Routing Global Block i.e. label range * Only one range supported in this code */ - struct sr_srgb srgb; + struct sr_global_block srgb; + + /* Segment Routing Local Block */ + struct sr_local_block srlb; + /* Maximum SID Depth supported by the node */ uint8_t msd; + + /* Thread timer to start Label Manager */ + struct thread *t_start_lm; }; /* Structure aggregating all received SR info from LSAs by node */ @@ -225,9 +263,9 @@ struct sr_node { uint32_t instance; uint8_t algo[ALGORITHM_COUNT]; /* Algorithms supported by the node */ - /* Segment Routing Global Block i.e. label range */ - struct sr_srgb srgb; - uint8_t msd; /* Maximum SID Depth */ + struct sr_block srgb; /* Segment Routing Global Block */ + struct sr_block srlb; /* Segment Routing Local Block */ + uint8_t msd; /* Maximum SID Depth */ /* List of Prefix & Link advertise by this node */ struct list *ext_prefix; /* For Node SID */ @@ -237,10 +275,8 @@ struct sr_node { struct sr_node *neighbor; }; - /* Segment Routing - NHLFE info: support IPv4 Only */ struct sr_nhlfe { - struct prefix_ipv4 prefv4; struct in_addr nexthop; ifindex_t ifindex; mpls_label_t label_in; @@ -254,6 +290,9 @@ struct sr_link { /* 24-bit Opaque-ID field value according to RFC 7684 specification */ uint32_t instance; + /* Interface address */ + struct in_addr itf_addr; + /* Flags to manage this link parameters. */ uint8_t flags[2]; @@ -261,7 +300,7 @@ struct sr_link { uint32_t sid[2]; enum sid_type type; - /* SR NHLFE for this link */ + /* SR NHLFE (Primary + Backup) for this link */ struct sr_nhlfe nhlfe[2]; /* Back pointer to SR Node which advertise this Link */ @@ -274,6 +313,9 @@ struct sr_prefix { /* 24-bit Opaque-ID field value according to RFC 7684 specification */ uint32_t instance; + /* Prefix itself */ + struct prefix_ipv4 prefv4; + /* Flags to manage this prefix parameters. */ uint8_t flags; @@ -281,17 +323,17 @@ struct sr_prefix { uint32_t sid; enum sid_type type; - /* SR NHLFE for this prefix */ + /* Incoming label for this prefix */ + mpls_label_t label_in; + + /* Back pointer to OSPF Route for remote prefix */ + struct ospf_route *route; + + /* NHLFE for local prefix */ struct sr_nhlfe nhlfe; /* Back pointer to SR Node which advertise this Prefix */ struct sr_node *srn; - - /* - * Pointer to SR Node which is the next hop for this Prefix - * or NULL if next hop is the destination of the prefix - */ - struct sr_node *nexthop; }; /* Prototypes definition */ @@ -299,6 +341,9 @@ struct sr_prefix { extern int ospf_sr_init(void); extern void ospf_sr_term(void); extern void ospf_sr_finish(void); +/* Segment Routing label allocation functions */ +extern mpls_label_t ospf_sr_local_block_request_label(void); +extern int ospf_sr_local_block_release_label(mpls_label_t label); /* Segment Routing LSA update & delete functions */ extern void ospf_sr_ri_lsa_update(struct ospf_lsa *lsa); extern void ospf_sr_ri_lsa_delete(struct ospf_lsa *lsa); @@ -306,10 +351,14 @@ extern void ospf_sr_ext_link_lsa_update(struct ospf_lsa *lsa); extern void ospf_sr_ext_link_lsa_delete(struct ospf_lsa *lsa); extern void ospf_sr_ext_prefix_lsa_update(struct ospf_lsa *lsa); extern void ospf_sr_ext_prefix_lsa_delete(struct ospf_lsa *lsa); +/* Segment Routing Extending Link management */ +struct ext_itf; +extern void ospf_sr_ext_itf_add(struct ext_itf *exti); +extern void ospf_sr_ext_itf_delete(struct ext_itf *exti); /* Segment Routing configuration functions */ -extern uint32_t get_ext_link_label_value(void); extern void ospf_sr_config_write_router(struct vty *vty); -extern void ospf_sr_update_prefix(struct interface *ifp, struct prefix *p); +extern void ospf_sr_update_local_prefix(struct interface *ifp, + struct prefix *p); /* Segment Routing re-routing function */ -extern void ospf_sr_update_timer_add(struct ospf *ospf); +extern void ospf_sr_update_task(struct ospf *ospf); #endif /* _FRR_OSPF_SR_H */ diff --git a/ospfd/ospf_te.c b/ospfd/ospf_te.c index bd8cbee11a..85f06c2b4c 100644 --- a/ospfd/ospf_te.c +++ b/ospfd/ospf_te.c @@ -69,7 +69,7 @@ */ struct ospf_mpls_te OspfMplsTE; -const char *mode2text[] = {"Off", "AS", "Area"}; +static const char *const mode2text[] = {"Off", "AS", "Area"}; /*------------------------------------------------------------------------* * Followings are initialize/terminate functions for MPLS-TE handling. @@ -187,6 +187,7 @@ void ospf_mpls_te_finish(void) // list_delete_all_node(OspfMplsTE.iflist); OspfMplsTE.enabled = false; + ospf_mpls_te_unregister(); OspfMplsTE.inter_as = Off; } @@ -397,53 +398,13 @@ static void set_linkparams_link_type(struct ospf_interface *oi, return; } -static void set_linkparams_link_id(struct ospf_interface *oi, - struct mpls_te_link *lp) +static void set_linkparams_link_id(struct mpls_te_link *lp, + struct in_addr link_id) { - struct ospf_neighbor *nbr; - int done = 0; lp->link_id.header.type = htons(TE_LINK_SUBTLV_LINK_ID); lp->link_id.header.length = htons(TE_LINK_SUBTLV_DEF_SIZE); - - /* - * The Link ID is identical to the contents of the Link ID field - * in the Router LSA for these link types. - */ - switch (oi->type) { - case OSPF_IFTYPE_POINTOPOINT: - /* Take the router ID of the neighbor. */ - if ((nbr = ospf_nbr_lookup_ptop(oi)) - && nbr->state == NSM_Full) { - lp->link_id.value = nbr->router_id; - done = 1; - } - break; - case OSPF_IFTYPE_BROADCAST: - case OSPF_IFTYPE_NBMA: - /* Take the interface address of the designated router. */ - if ((nbr = ospf_nbr_lookup_by_addr(oi->nbrs, &DR(oi))) == NULL) - break; - - if (nbr->state == NSM_Full - || (IPV4_ADDR_SAME(&oi->address->u.prefix4, &DR(oi)) - && ospf_nbr_count(oi, NSM_Full) > 0)) { - lp->link_id.value = DR(oi); - done = 1; - } - break; - default: - /* Not supported yet. */ /* XXX */ - lp->link_id.header.type = htons(0); - break; - } - - if (!done) { - struct in_addr mask; - masklen2ip(oi->address->prefixlen, &mask); - lp->link_id.value.s_addr = - oi->address->u.prefix4.s_addr & mask.s_addr; - } + lp->link_id.value = link_id; return; } @@ -958,40 +919,33 @@ void ospf_mpls_te_update_if(struct interface *ifp) return; } +/* + * Just add interface and set available information. Other information + * and flooding of LSA will be done later when adjacency will be up + * See ospf_mpls_te_nsm_change() after + */ static void ospf_mpls_te_ism_change(struct ospf_interface *oi, int old_state) { - struct te_link_subtlv_link_type old_type; - struct te_link_subtlv_link_id old_id; + struct mpls_te_link *lp; - if ((lp = lookup_linkparams_by_ifp(oi->ifp)) == NULL) { + lp = lookup_linkparams_by_ifp(oi->ifp); + if (lp == NULL) { flog_warn( EC_OSPF_TE_UNEXPECTED, - "ospf_mpls_te_ism_change: Cannot get linkparams from OI(%s)?", - IF_NAME(oi)); + "MPLS-TE (%s): Cannot get linkparams from OI(%s)?", + __func__, IF_NAME(oi)); return; } if (oi->area == NULL || oi->area->ospf == NULL) { flog_warn( EC_OSPF_TE_UNEXPECTED, - "ospf_mpls_te_ism_change: Cannot refer to OSPF from OI(%s)?", - IF_NAME(oi)); + "MPLS-TE (%s): Cannot refer to OSPF from OI(%s)?", + __func__, IF_NAME(oi)); return; } -#ifdef notyet - if ((lp->area != NULL - && !IPV4_ADDR_SAME(&lp->area->area_id, &oi->area->area_id)) - || (lp->area != NULL && oi->area == NULL)) { - /* How should we consider this case? */ - flog_warn( - EC_OSPF_TE_UNEXPECTED, - "MPLS-TE: Area for OI(%s) has changed to [%s], flush previous LSAs", - IF_NAME(oi), - oi->area ? inet_ntoa(oi->area->area_id) : "N/A"); - ospf_mpls_te_lsa_schedule(lp, FLUSH_THIS_LSA); - } -#endif + /* Keep Area information in combination with linkparams. */ lp->area = oi->area; @@ -1003,55 +957,103 @@ static void ospf_mpls_te_ism_change(struct ospf_interface *oi, int old_state) case ISM_DROther: case ISM_Backup: case ISM_DR: - old_type = lp->link_type; - old_id = lp->link_id; - - /* Set Link type, Link ID, Local and Remote IP addr */ + /* Set Link type and Local IP addr */ set_linkparams_link_type(oi, lp); - set_linkparams_link_id(oi, lp); set_linkparams_lclif_ipaddr(lp, oi->address->u.prefix4); - if (oi->type == LINK_TYPE_SUBTLV_VALUE_PTP) { - struct prefix *pref = CONNECTED_PREFIX(oi->connected); - if (pref != NULL) - set_linkparams_rmtif_ipaddr(lp, - pref->u.prefix4); - } - - /* Update TE parameters */ - update_linkparams(lp); - - /* Try to Schedule LSA */ - if ((ntohs(old_type.header.type) - != ntohs(lp->link_type.header.type) - || old_type.link_type.value - != lp->link_type.link_type.value) - || (ntohs(old_id.header.type) - != ntohs(lp->link_id.header.type) - || ntohl(old_id.value.s_addr) - != ntohl(lp->link_id.value.s_addr))) { - if (CHECK_FLAG(lp->flags, LPFLG_LSA_ENGAGED)) - ospf_mpls_te_lsa_schedule(lp, REFRESH_THIS_LSA); - else - ospf_mpls_te_lsa_schedule(lp, - REORIGINATE_THIS_LSA); - } break; default: - lp->link_type.header.type = htons(0); - lp->link_id.header.type = htons(0); - + /* State is undefined: Flush LSA if engaged */ if (CHECK_FLAG(lp->flags, LPFLG_LSA_ENGAGED)) ospf_mpls_te_lsa_schedule(lp, FLUSH_THIS_LSA); break; } + if (IS_DEBUG_OSPF_TE) + zlog_debug( + "MPLS-TE(%s): Update Link parameters for interface %s", + __func__, IF_NAME(oi)); + return; } +/* + * Complete TE info and schedule LSA flooding + * Link-ID and Remote IP address must be set with neighbor info + * which are only valid once NSM state is FULL + */ static void ospf_mpls_te_nsm_change(struct ospf_neighbor *nbr, int old_state) { - /* Nothing to do here */ + struct ospf_interface *oi = nbr->oi; + struct mpls_te_link *lp; + + /* Process Neighbor only when its state is NSM Full */ + if (nbr->state != NSM_Full) + return; + + /* Get interface information for Traffic Engineering */ + lp = lookup_linkparams_by_ifp(oi->ifp); + if (lp == NULL) { + flog_warn( + EC_OSPF_TE_UNEXPECTED, + "MPLS-TE (%s): Cannot get linkparams from OI(%s)?", + __func__, IF_NAME(oi)); + return; + } + + if (oi->area == NULL || oi->area->ospf == NULL) { + flog_warn( + EC_OSPF_TE_UNEXPECTED, + "MPLS-TE (%s): Cannot refer to OSPF from OI(%s)?", + __func__, IF_NAME(oi)); + return; + } + + /* Keep Area information in combination with SR info. */ + lp->area = oi->area; + + /* Keep interface MPLS-TE status */ + lp->flags = HAS_LINK_PARAMS(oi->ifp); + + /* + * The Link ID is identical to the contents of the Link ID field + * in the Router LSA for these link types. + */ + switch (oi->state) { + case ISM_PointToPoint: + /* Set Link ID with neighbor Router ID */ + set_linkparams_link_id(lp, nbr->router_id); + /* Set Remote IP address */ + set_linkparams_rmtif_ipaddr(lp, nbr->address.u.prefix4); + break; + + case ISM_DR: + case ISM_DROther: + case ISM_Backup: + /* Set Link ID with the Designated Router ID */ + set_linkparams_link_id(lp, DR(oi)); + break; + + default: + /* State is undefined: Flush LSA if engaged */ + if (OspfMplsTE.enabled && + CHECK_FLAG(lp->flags, LPFLG_LSA_ENGAGED)) + ospf_mpls_te_lsa_schedule(lp, FLUSH_THIS_LSA); + return; + } + + if (IS_DEBUG_OSPF_TE) + zlog_debug( + "MPLS-TE (%s): Add Link-ID %s for interface %s ", + __func__, inet_ntoa(lp->link_id.value), oi->ifp->name); + + /* Try to Schedule LSA */ + if (OspfMplsTE.enabled) { + if (CHECK_FLAG(lp->flags, LPFLG_LSA_ENGAGED)) + ospf_mpls_te_lsa_schedule(lp, REFRESH_THIS_LSA); + else + ospf_mpls_te_lsa_schedule(lp, REORIGINATE_THIS_LSA); + } return; } @@ -1779,7 +1781,7 @@ static uint16_t show_vty_link_subtlv_unrsv_bw(struct vty *vty, i, fval1, i + 1, fval2); else zlog_debug( - " [%d]: %g (Bytes/sec),\t[%d]: %g (Bytes/sec)", + " [%d]: %g (Bytes/sec), [%d]: %g (Bytes/sec)", i, fval1, i + 1, fval2); } @@ -2118,7 +2120,7 @@ static uint16_t ospf_mpls_te_show_link_subtlv(struct vty *vty, static void ospf_mpls_te_show_info(struct vty *vty, struct ospf_lsa *lsa) { - struct lsa_header *lsah = (struct lsa_header *)lsa->data; + struct lsa_header *lsah = lsa->data; struct tlv_header *tlvh, *next; uint16_t sum, total; uint16_t (*subfunc)(struct vty * vty, struct tlv_header * tlvh, @@ -2234,6 +2236,17 @@ DEFUN (no_ospf_mpls_te, if (CHECK_FLAG(lp->flags, LPFLG_LSA_ENGAGED)) ospf_mpls_te_lsa_schedule(lp, FLUSH_THIS_LSA); + /* + * This resets the OspfMplsTE.inter_as to its initial state. + * This is to avoid having an inter-as value different from + * Off when mpls-te gets restarted (after being removed) + */ + if (OspfMplsTE.inter_as != Off) { + /* Deregister the Callbacks for Inter-AS support */ + ospf_mpls_te_unregister(); + OspfMplsTE.inter_as = Off; + } + return CMD_SUCCESS; } @@ -2534,7 +2547,7 @@ DEFUN (show_ip_ospf_mpls_te_link, struct interface *ifp = NULL; struct listnode *node; char *vrf_name = NULL; - bool all_vrf; + bool all_vrf = false; int inst = 0; int idx_vrf = 0; struct ospf *ospf = NULL; diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c index 631465fb20..755a19aa13 100644 --- a/ospfd/ospf_vty.c +++ b/ospfd/ospf_vty.c @@ -53,7 +53,12 @@ #include "ospfd/ospf_dump.h" #include "ospfd/ospf_bfd.h" -static const char *ospf_network_type_str[] = { +FRR_CFG_DEFAULT_BOOL(OSPF_LOG_ADJACENCY_CHANGES, + { .val_bool = true, .match_profile = "datacenter", }, + { .val_bool = false }, +) + +static const char *const ospf_network_type_str[] = { "Null", "POINTOPOINT", "BROADCAST", "NBMA", "POINTOMULTIPOINT", "VIRTUALLINK", "LOOPBACK"}; @@ -78,7 +83,8 @@ static void area_id2str(char *buf, int length, struct in_addr *area_id, if (area_id_fmt == OSPF_AREA_ID_FMT_DOTTEDQUAD) inet_ntop(AF_INET, area_id, buf, length); else - sprintf(buf, "%lu", (unsigned long)ntohl(area_id->s_addr)); + snprintf(buf, length, "%lu", + (unsigned long)ntohl(area_id->s_addr)); } static int str2metric(const char *str, int *metric) @@ -88,7 +94,7 @@ static int str2metric(const char *str, int *metric) return 0; *metric = strtol(str, NULL, 10); - if (*metric < 0 && *metric > 16777214) { + if (*metric < 0 || *metric > 16777214) { /* vty_out (vty, "OSPF metric value is invalid\n"); */ return 0; } @@ -138,6 +144,7 @@ static struct ospf *ospf_cmd_lookup_ospf(struct vty *vty, struct ospf *ospf = NULL; int idx_vrf = 0, idx_inst = 0; const char *vrf_name = NULL; + bool created = false; *instance = 0; if (argv_find(argv, argc, "(1-65535)", &idx_inst)) @@ -149,18 +156,23 @@ static struct ospf *ospf_cmd_lookup_ospf(struct vty *vty, vrf_name = NULL; if (enable) { /* Allocate VRF aware instance */ - ospf = ospf_get(*instance, vrf_name); + ospf = ospf_get(*instance, vrf_name, &created); } else { ospf = ospf_lookup_by_inst_name(*instance, vrf_name); } } else { if (enable) { - ospf = ospf_get(*instance, NULL); + ospf = ospf_get(*instance, NULL, &created); } else { ospf = ospf_lookup_instance(*instance); } } + if (created) { + if (DFLT_OSPF_LOG_ADJACENCY_CHANGES) + SET_FLAG(ospf->config, OSPF_LOG_ADJACENCY_CHANGES); + } + return ospf; } @@ -200,9 +212,6 @@ DEFUN_NOSH (router_ospf, struct ospf *ospf = NULL; int ret = CMD_SUCCESS; unsigned short instance = 0; - struct vrf *vrf = NULL; - struct route_node *rn; - struct interface *ifp; ospf = ospf_cmd_lookup_ospf(vty, argv, argc, 1, &instance); if (!ospf) @@ -214,46 +223,12 @@ DEFUN_NOSH (router_ospf, VTY_PUSH_CONTEXT_NULL(OSPF_NODE); ret = CMD_NOT_MY_INSTANCE; } else { - if (ospf->vrf_id != VRF_UNKNOWN) - ospf->oi_running = 1; if (IS_DEBUG_OSPF_EVENT) zlog_debug( "Config command 'router ospf %d' received, vrf %s id %u oi_running %u", instance, ospf->name ? ospf->name : "NIL", ospf->vrf_id, ospf->oi_running); VTY_PUSH_CONTEXT(OSPF_NODE, ospf); - - /* Activate 'ip ospf area x' configured interfaces for given - * vrf. Activate area on vrf x aware interfaces. - * vrf_enable callback calls router_id_update which - * internally will call ospf_if_update to trigger - * network_run_state - */ - vrf = vrf_lookup_by_id(ospf->vrf_id); - - FOR_ALL_INTERFACES (vrf, ifp) { - struct ospf_if_params *params; - - params = IF_DEF_PARAMS(ifp); - if (OSPF_IF_PARAM_CONFIGURED(params, if_area)) { - for (rn = route_top(ospf->networks); rn; - rn = route_next(rn)) { - if (rn->info != NULL) { - vty_out(vty, - "Interface %s has area config but please remove all network commands first.\n", - ifp->name); - return ret; - } - } - if (!ospf_interface_area_is_already_set(ospf, - ifp)) { - ospf_interface_area_set(ospf, ifp); - ospf->if_ospf_cli_count++; - } - } - } - - ospf_router_id_update(ospf); } return ret; @@ -301,8 +276,7 @@ DEFPY (ospf_router_id, for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) if (area->full_nbrs) { vty_out(vty, - "For this router-id change to take effect," - " save config and restart ospfd\n"); + "For this router-id change to take effect, save config and restart ospfd\n"); return CMD_SUCCESS; } @@ -335,8 +309,7 @@ DEFUN_HIDDEN (ospf_router_id_old, for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) if (area->full_nbrs) { vty_out(vty, - "For this router-id change to take effect," - " save config and restart ospfd\n"); + "For this router-id change to take effect, save config and restart ospfd\n"); return CMD_SUCCESS; } @@ -369,8 +342,7 @@ DEFPY (no_ospf_router_id, for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) if (area->full_nbrs) { vty_out(vty, - "For this router-id change to take effect," - " save config and restart ospfd\n"); + "For this router-id change to take effect, save config and restart ospfd\n"); return CMD_SUCCESS; } @@ -595,6 +567,7 @@ DEFUN (ospf_network_area, struct prefix_ipv4 p; struct in_addr area_id; int ret, format; + uint32_t count; if (ospf->instance) { vty_out(vty, @@ -602,15 +575,15 @@ DEFUN (ospf_network_area, return CMD_WARNING_CONFIG_FAILED; } - if (ospf->if_ospf_cli_count > 0) { + count = ospf_count_area_params(ospf); + if (count > 0) { vty_out(vty, "Please remove all ip ospf area x.x.x.x commands first.\n"); if (IS_DEBUG_OSPF_EVENT) zlog_debug( "%s ospf vrf %s num of %u ip osp area x config", - __PRETTY_FUNCTION__, - ospf->name ? ospf->name : "NIL", - ospf->if_ospf_cli_count); + __func__, ospf->name ? ospf->name : "NIL", + count); return CMD_WARNING_CONFIG_FAILED; } @@ -689,6 +662,8 @@ DEFUN (ospf_area_range, str2prefix_ipv4(argv[idx_ipv4_prefixlen]->arg, &p); ospf_area_range_set(ospf, area_id, &p, OSPF_AREA_RANGE_ADVERTISE); + ospf_area_display_format_set(ospf, ospf_area_get(ospf, area_id), + format); if (argc > 5) { cost = strtoul(argv[idx_cost]->arg, NULL, 10); ospf_area_range_cost_set(ospf, area_id, &p, cost); @@ -1167,9 +1142,9 @@ DEFUN (no_ospf_area_vlink, "no area virtual-link A.B.C.D [authentication []] []", NO_STR VLINK_HELPSTR_IPADDR - "Enable authentication on this virtual link\n" \ - "Use message-digest authentication\n" \ - "Use null authentication\n" \ + "Enable authentication on this virtual link\n" + "Use message-digest authentication\n" + "Use null authentication\n" VLINK_HELPSTR_AUTH_MD5 VLINK_HELPSTR_AUTH_SIMPLE) { @@ -1374,8 +1349,7 @@ DEFUN (ospf_area_shortcut, if (ospf->abr_type != OSPF_ABR_SHORTCUT) vty_out(vty, - "Shortcut area setting will take effect " - "only when the router is configured as Shortcut ABR\n"); + "Shortcut area setting will take effect only when the router is configured as Shortcut ABR\n"); return CMD_SUCCESS; } @@ -1435,6 +1409,8 @@ DEFUN (ospf_area_stub, return CMD_WARNING_CONFIG_FAILED; } + /* Flush the external LSAs from the specified area */ + ospf_flush_lsa_from_area(ospf, area_id, OSPF_AS_EXTERNAL_LSA); ospf_area_no_summary_unset(ospf, area_id); return CMD_SUCCESS; @@ -1557,6 +1533,8 @@ static int ospf_area_nssa_cmd_handler(struct vty *vty, int argc, ospf_area_no_summary_unset(ospf, area_id); } + /* Flush the external LSA for the specified area */ + ospf_flush_lsa_from_area(ospf, area_id, OSPF_AS_EXTERNAL_LSA); ospf_schedule_abr_task(ospf); return CMD_SUCCESS; @@ -1662,6 +1640,8 @@ DEFUN (no_ospf_area_nssa, VTY_GET_OSPF_AREA_ID_NO_BB("NSSA", area_id, format, argv[idx_ipv4_number]->arg); + /* Flush the NSSA LSA for the specified area */ + ospf_flush_lsa_from_area(ospf, area_id, OSPF_AS_NSSA_LSA); ospf_area_nssa_unset(ospf, area_id, argc); ospf_schedule_abr_task(ospf); @@ -1707,8 +1687,7 @@ DEFUN (ospf_area_default_cost, p.prefixlen = 0; if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_abr_announce_stub_defaults(): " - "announcing 0.0.0.0/0 to area %s", + "ospf_abr_announce_stub_defaults(): announcing 0.0.0.0/0 to area %s", inet_ntoa(area->area_id)); ospf_abr_announce_network_to_area(&p, area->default_cost, area); @@ -1751,8 +1730,7 @@ DEFUN (no_ospf_area_default_cost, p.prefixlen = 0; if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_abr_announce_stub_defaults(): " - "announcing 0.0.0.0/0 to area %s", + "ospf_abr_announce_stub_defaults(): announcing 0.0.0.0/0 to area %s", inet_ntoa(area->area_id)); ospf_abr_announce_network_to_area(&p, area->default_cost, area); @@ -2622,11 +2600,14 @@ ALIAS(no_ospf_write_multiplier, no_write_multiplier_cmd, "Write multiplier\n" "Maximum number of interface serviced per write\n") -const char *ospf_abr_type_descr_str[] = {"Unknown", "Standard (RFC2328)", - "Alternative IBM", "Alternative Cisco", - "Alternative Shortcut"}; +static const char *const ospf_abr_type_descr_str[] = { + "Unknown", "Standard (RFC2328)", "Alternative IBM", + "Alternative Cisco", "Alternative Shortcut" +}; -const char *ospf_shortcut_mode_descr_str[] = {"Default", "Enabled", "Disabled"}; +static const char *const ospf_shortcut_mode_descr_str[] = { + "Default", "Enabled", "Disabled" +}; static void show_ip_ospf_area(struct vty *vty, struct ospf_area *area, json_object *json_areas, bool use_json) @@ -2698,8 +2679,7 @@ static void show_ip_ospf_area(struct vty *vty, struct ospf_area *area, area->act_ints); } else vty_out(vty, - " Number of interfaces in this area: Total: %d, " - "Active: %d\n", + " Number of interfaces in this area: Total: %d, Active: %d\n", listcount(area->oiflist), area->act_ints); if (area->external_routing == OSPF_AREA_NSSA) { @@ -2857,8 +2837,7 @@ static void show_ip_ospf_area(struct vty *vty, struct ospf_area *area, } else { /* Show number of fully adjacent neighbors. */ vty_out(vty, - " Number of fully adjacent neighbors in this area:" - " %d\n", + " Number of fully adjacent neighbors in this area: %d\n", area->full_nbrs); /* Show authentication type. */ @@ -2872,8 +2851,7 @@ static void show_ip_ospf_area(struct vty *vty, struct ospf_area *area, if (!OSPF_IS_AREA_BACKBONE(area)) vty_out(vty, - " Number of full virtual adjacencies going through" - " this area: %d\n", + " Number of full virtual adjacencies going through this area: %d\n", area->full_vls); /* Show SPF calculation times. */ @@ -3159,8 +3137,7 @@ static int show_ip_ospf_common(struct vty *vty, struct ospf *ospf, "injectingExternalRoutingInformation"); else vty_out(vty, - " This router is an ASBR " - "(injecting external routing information)\n"); + " This router is an ASBR (injecting external routing information)\n"); } /* Show Number of AS-external-LSAs. */ @@ -3252,7 +3229,7 @@ DEFUN (show_ip_ospf, bool uj = use_json(argc, argv); struct listnode *node = NULL; char *vrf_name = NULL; - bool all_vrf = FALSE; + bool all_vrf = false; int ret = CMD_SUCCESS; int inst = 0; int idx_vrf = 0; @@ -3269,7 +3246,7 @@ DEFUN (show_ip_ospf, /* vrf input is provided could be all or specific vrf*/ if (vrf_name) { - bool ospf_output = FALSE; + bool ospf_output = false; use_vrf = 1; @@ -3277,7 +3254,7 @@ DEFUN (show_ip_ospf, for (ALL_LIST_ELEMENTS_RO(om->ospf, node, ospf)) { if (!ospf->oi_running) continue; - ospf_output = TRUE; + ospf_output = true; ret = show_ip_ospf_common(vty, ospf, json, use_vrf); } @@ -3442,6 +3419,9 @@ static void show_ip_ospf_interface_sub(struct vty *vty, struct ospf *ospf, else vty_out(vty, " This interface is UNNUMBERED,"); } else { + struct in_addr dest; + const char *dstr; + /* Show OSPF interface information. */ if (use_json) { json_object_string_add( @@ -3455,46 +3435,40 @@ static void show_ip_ospf_interface_sub(struct vty *vty, struct ospf *ospf, inet_ntoa(oi->address->u.prefix4), oi->address->prefixlen); - if (oi->connected->destination - || oi->type == OSPF_IFTYPE_VIRTUALLINK) { - struct in_addr *dest; - const char *dstr; - - if (CONNECTED_PEER(oi->connected) - || oi->type == OSPF_IFTYPE_VIRTUALLINK) - dstr = "Peer"; - else - dstr = "Broadcast"; + /* For Vlinks, showing the peer address is + * probably more informative than the local + * interface that is being used */ + if (oi->type == OSPF_IFTYPE_VIRTUALLINK) { + dstr = "Peer"; + dest = oi->vl_data->peer_addr; + } else if (CONNECTED_PEER(oi->connected) + && oi->connected->destination) { + dstr = "Peer"; + dest = oi->connected->destination->u.prefix4; + } else { + dstr = "Broadcast"; + dest.s_addr = ipv4_broadcast_addr( + oi->connected->address->u.prefix4.s_addr, + oi->connected->address->prefixlen); + } - /* For Vlinks, showing the peer address is - * probably more - * * * * * informative than the local - * interface that is being used - * * * * */ + if (use_json) { + json_object_string_add( + json_interface_sub, + "ospfIfType", dstr); if (oi->type == OSPF_IFTYPE_VIRTUALLINK) - dest = &oi->vl_data->peer_addr; + json_object_string_add( + json_interface_sub, + "vlinkPeer", + inet_ntoa(dest)); else - dest = &oi->connected->destination->u - .prefix4; - - if (use_json) { json_object_string_add( json_interface_sub, - "ospfIfType", dstr); - if (oi->type == OSPF_IFTYPE_VIRTUALLINK) - json_object_string_add( - json_interface_sub, - "vlinkPeer", - inet_ntoa(*dest)); - else - json_object_string_add( - json_interface_sub, - "localIfUsed", - inet_ntoa(*dest)); - } else - vty_out(vty, " %s %s,", dstr, - inet_ntoa(*dest)); - } + "localIfUsed", + inet_ntoa(dest)); + } else + vty_out(vty, " %s %s,", dstr, + inet_ntoa(dest)); } if (use_json) { json_object_string_add(json_interface_sub, "area", @@ -3540,7 +3514,7 @@ static void show_ip_ospf_interface_sub(struct vty *vty, struct ospf *ospf, } /* Show DR information. */ - if (DR(oi).s_addr == 0) { + if (DR(oi).s_addr == INADDR_ANY) { if (!use_json) vty_out(vty, " No backup designated router on this network\n"); @@ -3942,7 +3916,7 @@ DEFUN (show_ip_ospf_interface, bool uj = use_json(argc, argv); struct listnode *node = NULL; char *vrf_name = NULL, *intf_name = NULL; - bool all_vrf = FALSE; + bool all_vrf = false; int ret = CMD_SUCCESS; int inst = 0; int idx_vrf = 0, idx_intf = 0; @@ -4082,7 +4056,7 @@ DEFUN (show_ip_ospf_interface_traffic, struct ospf *ospf = NULL; struct listnode *node = NULL; char *vrf_name = NULL, *intf_name = NULL; - bool all_vrf = FALSE; + bool all_vrf = false; int inst = 0; int idx_vrf = 0, idx_intf = 0; bool uj = use_json(argc, argv); @@ -4154,7 +4128,7 @@ DEFUN (show_ip_ospf_interface_traffic, static void show_ip_ospf_neighbour_header(struct vty *vty) { - vty_out(vty, "\n%-15s %3s %-15s %9s %-15s %-20s %5s %5s %5s\n", + vty_out(vty, "\n%-15s %3s %-15s %9s %-15s %-32s %5s %5s %5s\n", "Neighbor ID", "Pri", "State", "Dead Time", "Address", "Interface", "RXmtL", "RqstL", "DBsmL"); } @@ -4188,7 +4162,7 @@ static void show_ip_ospf_neighbor_sub(struct vty *vty, } if (nbr->state == NSM_Attempt - && nbr->router_id.s_addr == 0) + && nbr->router_id.s_addr == INADDR_ANY) strlcpy(neigh_str, "neighbor", sizeof(neigh_str)); else @@ -4247,7 +4221,7 @@ static void show_ip_ospf_neighbor_sub(struct vty *vty, ospf_nbr_state_message(nbr, msgbuf, 16); if (nbr->state == NSM_Attempt - && nbr->router_id.s_addr == 0) + && nbr->router_id.s_addr == INADDR_ANY) vty_out(vty, "%-15s %3d %-15s ", "-", nbr->priority, msgbuf); else @@ -4260,7 +4234,7 @@ static void show_ip_ospf_neighbor_sub(struct vty *vty, timebuf, sizeof(timebuf))); vty_out(vty, "%-15s ", inet_ntoa(nbr->src)); - vty_out(vty, "%-20s %5ld %5ld %5d\n", + vty_out(vty, "%-32s %5ld %5ld %5d\n", IF_NAME(oi), ospf_ls_retransmit_count(nbr), ospf_ls_request_count(nbr), @@ -4337,7 +4311,7 @@ DEFUN (show_ip_ospf_neighbor, bool uj = use_json(argc, argv); struct listnode *node = NULL; char *vrf_name = NULL; - bool all_vrf = FALSE; + bool all_vrf = false; int ret = CMD_SUCCESS; int inst = 0; int idx_vrf = 0; @@ -4524,7 +4498,7 @@ static int show_ip_ospf_neighbor_all_common(struct vty *vty, struct ospf *ospf, "-", nbr_nbma->priority, "Down", "-"); vty_out(vty, - "%-15s %-20s %5d %5d %5d\n", + "%-32s %-20s %5d %5d %5d\n", inet_ntoa(nbr_nbma->addr), IF_NAME(oi), 0, 0, 0); } @@ -4563,7 +4537,7 @@ DEFUN (show_ip_ospf_neighbor_all, bool uj = use_json(argc, argv); struct listnode *node = NULL; char *vrf_name = NULL; - bool all_vrf = FALSE; + bool all_vrf = false; int ret = CMD_SUCCESS; int inst = 0; int idx_vrf = 0; @@ -4884,16 +4858,40 @@ static void show_ip_ospf_nbr_nbma_detail_sub(struct vty *vty, static void show_ip_ospf_neighbor_detail_sub(struct vty *vty, struct ospf_interface *oi, struct ospf_neighbor *nbr, + struct ospf_neighbor *prev_nbr, json_object *json, bool use_json) { char timebuf[OSPF_TIME_DUMP_SIZE]; - json_object *json_sub = NULL; + json_object *json_neigh = NULL, *json_neigh_array = NULL; + char neigh_str[INET_ADDRSTRLEN] = {0}; - if (use_json) - json_sub = json_object_new_object(); - else { + if (use_json) { + if (prev_nbr && + !IPV4_ADDR_SAME(&prev_nbr->src, &nbr->src)) { + json_neigh_array = NULL; + } + + if (nbr->state == NSM_Attempt + && nbr->router_id.s_addr == INADDR_ANY) + strlcpy(neigh_str, "noNbrId", sizeof(neigh_str)); + else + strlcpy(neigh_str, inet_ntoa(nbr->router_id), + sizeof(neigh_str)); + + json_object_object_get_ex(json, neigh_str, &json_neigh_array); + + if (!json_neigh_array) { + json_neigh_array = json_object_new_array(); + json_object_object_add(json, neigh_str, + json_neigh_array); + } + + json_neigh = json_object_new_object(); + + } else { /* Show neighbor ID. */ - if (nbr->state == NSM_Attempt && nbr->router_id.s_addr == 0) + if (nbr->state == NSM_Attempt + && nbr->router_id.s_addr == INADDR_ANY) vty_out(vty, " Neighbor %s,", "-"); else vty_out(vty, " Neighbor %s,", @@ -4902,7 +4900,7 @@ static void show_ip_ospf_neighbor_detail_sub(struct vty *vty, /* Show interface address. */ if (use_json) - json_object_string_add(json_sub, "ifaceAddress", + json_object_string_add(json_neigh, "ifaceAddress", inet_ntoa(nbr->address.u.prefix4)); else vty_out(vty, " interface address %s\n", @@ -4910,18 +4908,18 @@ static void show_ip_ospf_neighbor_detail_sub(struct vty *vty, /* Show Area ID. */ if (use_json) { - json_object_string_add(json_sub, "areaId", + json_object_string_add(json_neigh, "areaId", ospf_area_desc_string(oi->area)); - json_object_string_add(json_sub, "ifaceName", oi->ifp->name); + json_object_string_add(json_neigh, "ifaceName", oi->ifp->name); } else vty_out(vty, " In the area %s via interface %s\n", ospf_area_desc_string(oi->area), oi->ifp->name); /* Show neighbor priority and state. */ if (use_json) { - json_object_int_add(json_sub, "nbrPriority", nbr->priority); + json_object_int_add(json_neigh, "nbrPriority", nbr->priority); json_object_string_add( - json_sub, "nbrState", + json_neigh, "nbrState", lookup_msg(ospf_nsm_state_msg, nbr->state, NULL)); } else vty_out(vty, " Neighbor priority is %d, State is %s,", @@ -4930,7 +4928,7 @@ static void show_ip_ospf_neighbor_detail_sub(struct vty *vty, /* Show state changes. */ if (use_json) - json_object_int_add(json_sub, "stateChangeCounter", + json_object_int_add(json_neigh, "stateChangeCounter", nbr->state_change); else vty_out(vty, " %d state changes\n", nbr->state_change); @@ -4942,7 +4940,7 @@ static void show_ip_ospf_neighbor_detail_sub(struct vty *vty, time_store = monotime_since(&nbr->ts_last_progress, &res) / 1000LL; if (use_json) { - json_object_int_add(json_sub, "lastPrgrsvChangeMsec", + json_object_int_add(json_neigh, "lastPrgrsvChangeMsec", time_store); } else { vty_out(vty, @@ -4960,12 +4958,13 @@ static void show_ip_ospf_neighbor_detail_sub(struct vty *vty, time_store = monotime_since(&nbr->ts_last_regress, &res) / 1000LL; if (use_json) { - json_object_int_add(json_sub, + json_object_int_add(json_neigh, "lastRegressiveChangeMsec", time_store); if (nbr->last_regress_str) json_object_string_add( - json_sub, "lastRegressiveChangeReason", + json_neigh, + "lastRegressiveChangeReason", nbr->last_regress_str); } else { vty_out(vty, @@ -4979,22 +4978,22 @@ static void show_ip_ospf_neighbor_detail_sub(struct vty *vty, /* Show Designated Rotuer ID. */ if (use_json) - json_object_string_add(json_sub, "routerDesignatedId", + json_object_string_add(json_neigh, "routerDesignatedId", inet_ntoa(nbr->d_router)); else vty_out(vty, " DR is %s,", inet_ntoa(nbr->d_router)); /* Show Backup Designated Rotuer ID. */ if (use_json) - json_object_string_add(json_sub, "routerDesignatedBackupId", + json_object_string_add(json_neigh, "routerDesignatedBackupId", inet_ntoa(nbr->bd_router)); else vty_out(vty, " BDR is %s\n", inet_ntoa(nbr->bd_router)); /* Show options. */ if (use_json) { - json_object_int_add(json_sub, "optionsCounter", nbr->options); - json_object_string_add(json_sub, "optionsList", + json_object_int_add(json_neigh, "optionsCounter", nbr->options); + json_object_string_add(json_neigh, "optionsList", ospf_options_dump(nbr->options)); } else vty_out(vty, " Options %d %s\n", nbr->options, @@ -5007,12 +5006,13 @@ static void show_ip_ospf_neighbor_detail_sub(struct vty *vty, time_store = monotime_until(&nbr->t_inactivity->u.sands, NULL) / 1000LL; - json_object_int_add(json_sub, + json_object_int_add(json_neigh, "routerDeadIntervalTimerDueMsec", time_store); } else json_object_int_add( - json_sub, "routerDeadIntervalTimerDueMsec", -1); + json_neigh, + "routerDeadIntervalTimerDueMsec", -1); } else vty_out(vty, " Dead timer due in %s\n", ospf_timer_dump(nbr->t_inactivity, timebuf, @@ -5020,7 +5020,7 @@ static void show_ip_ospf_neighbor_detail_sub(struct vty *vty, /* Show Database Summary list. */ if (use_json) - json_object_int_add(json_sub, "databaseSummaryListCounter", + json_object_int_add(json_neigh, "databaseSummaryListCounter", ospf_db_summary_count(nbr)); else vty_out(vty, " Database Summary List %d\n", @@ -5028,7 +5028,7 @@ static void show_ip_ospf_neighbor_detail_sub(struct vty *vty, /* Show Link State Request list. */ if (use_json) - json_object_int_add(json_sub, "linkStateRequestListCounter", + json_object_int_add(json_neigh, "linkStateRequestListCounter", ospf_ls_request_count(nbr)); else vty_out(vty, " Link State Request List %ld\n", @@ -5036,7 +5036,7 @@ static void show_ip_ospf_neighbor_detail_sub(struct vty *vty, /* Show Link State Retransmission list. */ if (use_json) - json_object_int_add(json_sub, + json_object_int_add(json_neigh, "linkStateRetransmissionListCounter", ospf_ls_retransmit_count(nbr)); else @@ -5046,7 +5046,7 @@ static void show_ip_ospf_neighbor_detail_sub(struct vty *vty, /* Show inactivity timer thread. */ if (use_json) { if (nbr->t_inactivity != NULL) - json_object_string_add(json_sub, + json_object_string_add(json_neigh, "threadInactivityTimer", "on"); } else vty_out(vty, " Thread Inactivity Timer %s\n", @@ -5056,7 +5056,7 @@ static void show_ip_ospf_neighbor_detail_sub(struct vty *vty, if (use_json) { if (nbr->t_db_desc != NULL) json_object_string_add( - json_sub, + json_neigh, "threadDatabaseDescriptionRetransmission", "on"); } else @@ -5068,7 +5068,7 @@ static void show_ip_ospf_neighbor_detail_sub(struct vty *vty, if (use_json) { if (nbr->t_ls_req != NULL) json_object_string_add( - json_sub, + json_neigh, "threadLinkStateRequestRetransmission", "on"); } else vty_out(vty, @@ -5079,22 +5079,19 @@ static void show_ip_ospf_neighbor_detail_sub(struct vty *vty, if (use_json) { if (nbr->t_ls_upd != NULL) json_object_string_add( - json_sub, "threadLinkStateUpdateRetransmission", + json_neigh, + "threadLinkStateUpdateRetransmission", "on"); } else vty_out(vty, " Thread Link State Update Retransmission %s\n\n", nbr->t_ls_upd != NULL ? "on" : "off"); - if (use_json) { - if (nbr->state == NSM_Attempt && nbr->router_id.s_addr == 0) - json_object_object_add(json, "noNbrId", json_sub); - else - json_object_object_add(json, inet_ntoa(nbr->router_id), - json_sub); - } + ospf_bfd_show_info(vty, nbr->bfd_info, json_neigh, use_json, 0); + + if (use_json) + json_object_array_add(json_neigh_array, json_neigh); - ospf_bfd_show_info(vty, nbr->bfd_info, json, use_json, 0); } static int show_ip_ospf_neighbor_id_common(struct vty *vty, struct ospf *ospf, @@ -5121,8 +5118,8 @@ static int show_ip_ospf_neighbor_id_common(struct vty *vty, struct ospf *ospf, for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi)) { if ((nbr = ospf_nbr_lookup_by_routerid(oi->nbrs, router_id))) { - show_ip_ospf_neighbor_detail_sub(vty, oi, nbr, json, - use_json); + show_ip_ospf_neighbor_detail_sub(vty, oi, nbr, NULL, + json, use_json); } } @@ -5192,16 +5189,20 @@ static int show_ip_ospf_neighbor_detail_common(struct vty *vty, struct ospf_interface *oi; struct listnode *node; json_object *json_vrf = NULL; + json_object *json_nbr_sub = NULL; if (use_json) { if (use_vrf) json_vrf = json_object_new_object(); else json_vrf = json; + + json_nbr_sub = json_object_new_object(); } + if (ospf->instance) { if (use_json) - json_object_int_add(json_vrf, "ospfInstance", + json_object_int_add(json, "ospfInstance", ospf->instance); else vty_out(vty, "\nOSPF Instance: %d\n\n", ospf->instance); @@ -5211,22 +5212,25 @@ static int show_ip_ospf_neighbor_detail_common(struct vty *vty, for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi)) { struct route_node *rn; - struct ospf_neighbor *nbr; + struct ospf_neighbor *nbr, *prev_nbr = NULL; for (rn = route_top(oi->nbrs); rn; rn = route_next(rn)) { if ((nbr = rn->info)) { if (nbr != oi->nbr_self) { if (nbr->state != NSM_Down) { show_ip_ospf_neighbor_detail_sub( - vty, oi, nbr, json_vrf, - use_json); + vty, oi, nbr, prev_nbr, + json_nbr_sub, use_json); } } + prev_nbr = nbr; } } } if (use_json) { + json_object_object_add(json_vrf, "neighbors", + json_nbr_sub); if (use_vrf) { if (ospf->vrf_id == VRF_DEFAULT) json_object_object_add(json, "default", @@ -5257,7 +5261,7 @@ DEFUN (show_ip_ospf_neighbor_detail, bool uj = use_json(argc, argv); struct listnode *node = NULL; char *vrf_name = NULL; - bool all_vrf = FALSE; + bool all_vrf = false; int ret = CMD_SUCCESS; int inst = 0; int idx_vrf = 0; @@ -5389,16 +5393,20 @@ static int show_ip_ospf_neighbor_detail_all_common(struct vty *vty, for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi)) { struct route_node *rn; - struct ospf_neighbor *nbr; + struct ospf_neighbor *nbr, *prev_nbr = NULL; struct ospf_nbr_nbma *nbr_nbma; - for (rn = route_top(oi->nbrs); rn; rn = route_next(rn)) - if ((nbr = rn->info)) + for (rn = route_top(oi->nbrs); rn; rn = route_next(rn)) { + if ((nbr = rn->info)) { if (nbr != oi->nbr_self) if (nbr->state != NSM_Down) show_ip_ospf_neighbor_detail_sub( vty, oi, rn->info, + prev_nbr, json_vrf, use_json); + prev_nbr = nbr; + } + } if (oi->type == OSPF_IFTYPE_NBMA) { struct listnode *nd; @@ -5446,7 +5454,7 @@ DEFUN (show_ip_ospf_neighbor_detail_all, bool uj = use_json(argc, argv); struct listnode *node = NULL; char *vrf_name = NULL; - bool all_vrf = FALSE; + bool all_vrf = false; int ret = CMD_SUCCESS; int inst = 0; int idx_vrf = 0; @@ -5594,6 +5602,7 @@ static int show_ip_ospf_neighbor_int_detail_common(struct vty *vty, if (nbr->state != NSM_Down) show_ip_ospf_neighbor_detail_sub( vty, oi, nbr, + NULL, json, use_json); } } @@ -5626,13 +5635,13 @@ DEFUN (show_ip_ospf_neighbor_int_detail, bool uj = use_json(argc, argv); struct listnode *node = NULL; int ret = CMD_SUCCESS; - bool ospf_output = FALSE; + bool ospf_output = false; for (ALL_LIST_ELEMENTS_RO(om->ospf, node, ospf)) { if (!ospf->oi_running) continue; - ospf_output = TRUE; - ret = show_ip_ospf_neighbor_int_detail_common(vty, ospf, 0, + ospf_output = true; + ret = show_ip_ospf_neighbor_int_detail_common(vty, ospf, 4, argv, uj); } @@ -5737,7 +5746,7 @@ static int show_lsa_summary(struct vty *vty, struct ospf_lsa *lsa, int self) return 0; } -static const char *show_database_desc[] = { +static const char *const show_database_desc[] = { "unknown", "Router Link States", "Net Link States", @@ -5752,7 +5761,7 @@ static const char *show_database_desc[] = { "AS-external Opaque-LSA", }; -static const char *show_database_header[] = { +static const char *const show_database_header[] = { "", "Link ID ADV Router Age Seq# CkSum Link count", "Link ID ADV Router Age Seq# CkSum", @@ -5804,7 +5813,7 @@ static void show_ip_ospf_database_header(struct vty *vty, struct ospf_lsa *lsa) vty_out(vty, " Length: %d\n\n", ntohs(lsa->data->length)); } -const char *link_type_desc[] = { +static const char *const link_type_desc[] = { "(null)", "another Router (point-to-point)", "a Transit Network", @@ -5812,12 +5821,12 @@ const char *link_type_desc[] = { "a Virtual Link", }; -const char *link_id_desc[] = { +static const char *const link_id_desc[] = { "(null)", "Neighboring Router ID", "Designated Router address", "Net", "Neighboring Router ID", }; -const char *link_data_desc[] = { +static const char *const link_data_desc[] = { "(null)", "Router Interface address", "Router Interface address", "Network Mask", "Router Interface address", }; @@ -6017,7 +6026,7 @@ static int show_opaque_lsa_detail(struct vty *vty, struct ospf_lsa *lsa) return 0; } -int (*show_function[])(struct vty *, struct ospf_lsa *) = { +int (*const show_function[])(struct vty *, struct ospf_lsa *) = { NULL, show_router_lsa_detail, show_network_lsa_detail, @@ -6334,7 +6343,7 @@ DEFUN (show_ip_ospf_database_max, struct ospf *ospf = NULL; struct listnode *node = NULL; char *vrf_name = NULL; - bool all_vrf = FALSE; + bool all_vrf = false; int ret = CMD_SUCCESS; int inst = 0; int idx_vrf = 0; @@ -6343,7 +6352,7 @@ DEFUN (show_ip_ospf_database_max, OSPF_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (vrf_name) { - bool ospf_output = FALSE; + bool ospf_output = false; use_vrf = 1; @@ -6351,7 +6360,7 @@ DEFUN (show_ip_ospf_database_max, for (ALL_LIST_ELEMENTS_RO(om->ospf, node, ospf)) { if (!ospf->oi_running) continue; - ospf_output = TRUE; + ospf_output = true; ret = show_ip_ospf_database_common( vty, ospf, idx_vrf ? 2 : 0, argc, argv, use_vrf); @@ -6403,7 +6412,7 @@ DEFUN (show_ip_ospf_instance_database, unsigned short instance = 0; struct listnode *node = NULL; char *vrf_name = NULL; - bool all_vrf = FALSE; + bool all_vrf = false; int ret = CMD_SUCCESS; int inst = 0; int idx = 0; @@ -6561,7 +6570,7 @@ DEFUN (show_ip_ospf_instance_database_type_adv_router, unsigned short instance = 0; struct listnode *node = NULL; char *vrf_name = NULL; - bool all_vrf = FALSE; + bool all_vrf = false; int ret = CMD_SUCCESS; int inst = 0; int idx = 0, idx_vrf = 0; @@ -6584,7 +6593,7 @@ DEFUN (show_ip_ospf_instance_database_type_adv_router, OSPF_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (vrf_name) { - bool ospf_output = FALSE; + bool ospf_output = false; use_vrf = 1; @@ -6592,10 +6601,9 @@ DEFUN (show_ip_ospf_instance_database_type_adv_router, for (ALL_LIST_ELEMENTS_RO(om->ospf, node, ospf)) { if (!ospf->oi_running) continue; - ospf_output = TRUE; + ospf_output = true; ret = show_ip_ospf_database_type_adv_router_common( - vty, ospf, idx ? 1 : 0, argc, argv, - use_vrf); + vty, ospf, 2, argc, argv, use_vrf); } if (!ospf_output) vty_out(vty, "%% OSPF instance not found\n"); @@ -6607,7 +6615,7 @@ DEFUN (show_ip_ospf_instance_database_type_adv_router, } ret = show_ip_ospf_database_type_adv_router_common( - vty, ospf, idx ? 1 : 0, argc, argv, use_vrf); + vty, ospf, 2, argc, argv, use_vrf); } } else { /* Display default ospf (instance 0) info */ @@ -7267,6 +7275,7 @@ static int ospf_vty_dead_interval_set(struct vty *vty, const char *interval_str, SET_IF_PARAM(params, v_wait); params->v_wait = seconds; + params->is_v_wait_set = true; /* Update timer values in neighbor structure. */ if (nbr_str) { @@ -7376,6 +7385,7 @@ DEFUN (no_ip_ospf_dead_interval, UNSET_IF_PARAM(params, v_wait); params->v_wait = OSPF_ROUTER_DEAD_INTERVAL_DEFAULT; + params->is_v_wait_set = false; UNSET_IF_PARAM(params, fast_hello); params->fast_hello = OSPF_FAST_HELLO_DEFAULT; @@ -7452,6 +7462,17 @@ DEFUN (ip_ospf_hello_interval, SET_IF_PARAM(params, v_hello); params->v_hello = seconds; + if (!params->is_v_wait_set) { + SET_IF_PARAM(params, v_wait); + /* As per RFC 4062 + * The router dead interval should + * be some multiple of the HelloInterval (perhaps 4 times the + * hello interval) and must be the same for all routers + * attached to a common network. + */ + params->v_wait = 4 * seconds; + } + return CMD_SUCCESS; } @@ -7480,6 +7501,7 @@ DEFUN (no_ip_ospf_hello_interval, int idx = 0; struct in_addr addr; struct ospf_if_params *params; + struct route_node *rn; params = IF_DEF_PARAMS(ifp); @@ -7498,6 +7520,25 @@ DEFUN (no_ip_ospf_hello_interval, UNSET_IF_PARAM(params, v_hello); params->v_hello = OSPF_HELLO_INTERVAL_DEFAULT; + if (!params->is_v_wait_set) { + UNSET_IF_PARAM(params, v_wait); + params->v_wait = OSPF_ROUTER_DEAD_INTERVAL_DEFAULT; + } + + for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next(rn)) { + struct ospf_interface *oi = rn->info; + + if (!oi) + continue; + + oi->type = IF_DEF_PARAMS(ifp)->type; + + if (oi->state > ISM_Down) { + OSPF_ISM_EVENT_EXECUTE(oi, ISM_InterfaceDown); + OSPF_ISM_EVENT_EXECUTE(oi, ISM_InterfaceUp); + } + } + if (params != IF_DEF_PARAMS(ifp)) { ospf_free_if_params(ifp, addr); ospf_if_update_params(ifp, addr); @@ -7763,7 +7804,7 @@ DEFUN_HIDDEN (no_ospf_priority, DEFUN (ip_ospf_retransmit_interval, ip_ospf_retransmit_interval_addr_cmd, - "ip ospf retransmit-interval (3-65535) [A.B.C.D]", + "ip ospf retransmit-interval (1-65535) [A.B.C.D]", "IP Information\n" "OSPF interface commands\n" "Time between retransmitting lost link state advertisements\n" @@ -7777,7 +7818,7 @@ DEFUN (ip_ospf_retransmit_interval, struct ospf_if_params *params; params = IF_DEF_PARAMS(ifp); - argv_find(argv, argc, "(3-65535)", &idx); + argv_find(argv, argc, "(1-65535)", &idx); seconds = strtol(argv[idx]->arg, NULL, 10); if (argv_find(argv, argc, "A.B.C.D", &idx)) { @@ -7799,7 +7840,7 @@ DEFUN (ip_ospf_retransmit_interval, DEFUN_HIDDEN (ospf_retransmit_interval, ospf_retransmit_interval_cmd, - "ospf retransmit-interval (3-65535) [A.B.C.D]", + "ospf retransmit-interval (1-65535) [A.B.C.D]", "OSPF interface commands\n" "Time between retransmitting lost link state advertisements\n" "Seconds\n" @@ -7810,7 +7851,7 @@ DEFUN_HIDDEN (ospf_retransmit_interval, DEFUN (no_ip_ospf_retransmit_interval, no_ip_ospf_retransmit_interval_addr_cmd, - "no ip ospf retransmit-interval [(3-65535)] [A.B.C.D]", + "no ip ospf retransmit-interval [(1-65535)] [A.B.C.D]", NO_STR "IP Information\n" "OSPF interface commands\n" @@ -7850,7 +7891,7 @@ DEFUN (no_ip_ospf_retransmit_interval, DEFUN_HIDDEN (no_ospf_retransmit_interval, no_ospf_retransmit_interval_cmd, - "no ospf retransmit-interval [(3-65535)] [A.B.C.D]", + "no ospf retransmit-interval [(1-65535)] [A.B.C.D]", NO_STR "OSPF interface commands\n" "Time between retransmitting lost link state advertisements\n" @@ -7981,6 +8022,7 @@ DEFUN (ip_ospf_area, struct ospf *ospf = NULL; unsigned short instance = 0; char *areaid; + uint32_t count = 0; if (argv_find(argv, argc, "(1-65535)", &idx)) instance = strtol(argv[idx]->arg, NULL, 10); @@ -7994,13 +8036,37 @@ DEFUN (ip_ospf_area, ospf = ospf_lookup_instance(instance); if (instance && ospf == NULL) { + /* + * At this point we know we have received + * an instance and there is no ospf instance + * associated with it. This means we are + * in a situation where we have an + * ospf command that is setup for a different + * process(instance). We need to safely + * remove the command from ourselves and + * allow the other instance(process) handle + * the configuration command. + */ + count = 0; + params = IF_DEF_PARAMS(ifp); if (OSPF_IF_PARAM_CONFIGURED(params, if_area)) { UNSET_IF_PARAM(params, if_area); - ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); - ospf_interface_area_unset(ospf, ifp); - ospf->if_ospf_cli_count--; + count++; + } + + for (rn = route_top(IF_OIFS_PARAMS(ifp)); rn; rn = route_next(rn)) + if ((params = rn->info) && OSPF_IF_PARAM_CONFIGURED(params, if_area)) { + UNSET_IF_PARAM(params, if_area); + count++; + } + + if (count > 0) { + ospf = ospf_lookup_by_vrf_id(ifp->vrf_id); + if (ospf) + ospf_interface_area_unset(ospf, ifp); } + return CMD_NOT_MY_INSTANCE; } @@ -8014,6 +8080,16 @@ DEFUN (ip_ospf_area, return CMD_WARNING_CONFIG_FAILED; } + if (ospf) { + for (rn = route_top(ospf->networks); rn; rn = route_next(rn)) { + if (rn->info != NULL) { + vty_out(vty, + "Please remove all network commands first.\n"); + return CMD_WARNING_CONFIG_FAILED; + } + } + } + params = IF_DEF_PARAMS(ifp); if (OSPF_IF_PARAM_CONFIGURED(params, if_area) && !IPV4_ADDR_SAME(¶ms->if_area, &area_id)) { @@ -8032,21 +8108,14 @@ DEFUN (ip_ospf_area, // update/create address-level params params = ospf_get_if_params((ifp), (addr)); if (OSPF_IF_PARAM_CONFIGURED(params, if_area)) { - vty_out(vty, - "Must remove previous area/address config before changing ospf area"); - return CMD_WARNING_CONFIG_FAILED; - } - ospf_if_update_params((ifp), (addr)); - } - - if (ospf) { - for (rn = route_top(ospf->networks); rn; rn = route_next(rn)) { - if (rn->info != NULL) { + if (!IPV4_ADDR_SAME(¶ms->if_area, &area_id)) { vty_out(vty, - "Please remove all network commands first.\n"); + "Must remove previous area/address config before changing ospf area\n"); return CMD_WARNING_CONFIG_FAILED; - } + } else + return CMD_SUCCESS; } + ospf_if_update_params((ifp), (addr)); } /* enable ospf on this interface with area_id */ @@ -8056,10 +8125,8 @@ DEFUN (ip_ospf_area, params->if_area_id_fmt = format; } - if (ospf) { + if (ospf) ospf_interface_area_set(ospf, ifp); - ospf->if_ospf_cli_count++; - } return CMD_SUCCESS; } @@ -8082,6 +8149,7 @@ DEFUN (no_ip_ospf_area, struct ospf_if_params *params; unsigned short instance = 0; struct in_addr addr; + struct in_addr area_id; if (argv_find(argv, argc, "(1-65535)", &idx)) instance = strtol(argv[idx]->arg, NULL, 10); @@ -8091,7 +8159,7 @@ DEFUN (no_ip_ospf_area, else ospf = ospf_lookup_instance(instance); - if (ospf == NULL) + if (instance && ospf == NULL) return CMD_NOT_MY_INSTANCE; argv_find(argv, argc, "area", &idx); @@ -8109,6 +8177,7 @@ DEFUN (no_ip_ospf_area, } else params = IF_DEF_PARAMS(ifp); + area_id = params->if_area; if (!OSPF_IF_PARAM_CONFIGURED(params, if_area)) { vty_out(vty, "Can't find specified interface area configuration.\n"); @@ -8121,8 +8190,11 @@ DEFUN (no_ip_ospf_area, ospf_if_update_params((ifp), (addr)); } - ospf_interface_area_unset(ospf, ifp); - ospf->if_ospf_cli_count--; + if (ospf) { + ospf_interface_area_unset(ospf, ifp); + ospf_area_check_free(ospf, area_id); + } + return CMD_SUCCESS; } @@ -8454,22 +8526,8 @@ DEFUN (no_ospf_default_information_originate, "Pointer to route-map entries\n") { VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); - struct prefix_ipv4 p; - struct ospf_external *ext; struct ospf_redist *red; - p.family = AF_INET; - p.prefix.s_addr = 0; - p.prefixlen = 0; - - ospf_external_lsa_flush(ospf, DEFAULT_ROUTE, &p, 0); - - ext = ospf_external_lookup(ospf, DEFAULT_ROUTE, 0); - if (ext && EXTERNAL_INFO(ext)) { - ospf_external_info_delete(ospf, DEFAULT_ROUTE, 0, p); - ospf_external_del(ospf, DEFAULT_ROUTE, 0); - } - red = ospf_redist_lookup(ospf, DEFAULT_ROUTE, 0); if (!red) return CMD_SUCCESS; @@ -8477,7 +8535,8 @@ DEFUN (no_ospf_default_information_originate, ospf_routemap_unset(red); ospf_redist_del(ospf, DEFAULT_ROUTE, 0); - return ospf_redistribute_default_unset(ospf); + return ospf_redistribute_default_set(ospf, DEFAULT_ORIGINATE_NONE, + 0, 0); } DEFUN (ospf_default_metric, @@ -8900,6 +8959,31 @@ DEFUN (no_ospf_max_metric_router_lsa_shutdown, return CMD_SUCCESS; } +DEFUN (ospf_proactive_arp, + ospf_proactive_arp_cmd, + "proactive-arp", + "Allow sending ARP requests proactively\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + + ospf->proactive_arp = true; + + return CMD_SUCCESS; +} + +DEFUN (no_ospf_proactive_arp, + no_ospf_proactive_arp_cmd, + "no proactive-arp", + NO_STR + "Disallow sending ARP requests proactively\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + + ospf->proactive_arp = false; + + return CMD_SUCCESS; +} + static void config_write_stub_router(struct vty *vty, struct ospf *ospf) { struct listnode *ln; @@ -9231,8 +9315,8 @@ static void show_ip_ospf_route_external(struct vty *vty, struct ospf *ospf, char buf1[19]; - snprintf(buf1, 19, "%s/%d", inet_ntoa(rn->p.u.prefix4), - rn->p.prefixlen); + snprintf(buf1, sizeof(buf1), "%s/%d", + inet_ntoa(rn->p.u.prefix4), rn->p.prefixlen); json_route = json_object_new_object(); if (json) { json_object_object_add(json, buf1, json_route); @@ -9372,7 +9456,7 @@ DEFUN (show_ip_ospf_border_routers, struct ospf *ospf = NULL; struct listnode *node = NULL; char *vrf_name = NULL; - bool all_vrf = FALSE; + bool all_vrf = false; int ret = CMD_SUCCESS; int inst = 0; int idx_vrf = 0; @@ -9381,7 +9465,7 @@ DEFUN (show_ip_ospf_border_routers, OSPF_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (vrf_name) { - bool ospf_output = FALSE; + bool ospf_output = false; use_vrf = 1; @@ -9390,7 +9474,7 @@ DEFUN (show_ip_ospf_border_routers, if (!ospf->oi_running) continue; - ospf_output = TRUE; + ospf_output = true; ret = show_ip_ospf_border_routers_common( vty, ospf, use_vrf); } @@ -9510,7 +9594,7 @@ DEFUN (show_ip_ospf_route, struct ospf *ospf = NULL; struct listnode *node = NULL; char *vrf_name = NULL; - bool all_vrf = FALSE; + bool all_vrf = false; int ret = CMD_SUCCESS; int inst = 0; int idx_vrf = 0; @@ -9525,7 +9609,7 @@ DEFUN (show_ip_ospf_route, /* vrf input is provided could be all or specific vrf*/ if (vrf_name) { - bool ospf_output = FALSE; + bool ospf_output = false; use_vrf = 1; @@ -9533,7 +9617,7 @@ DEFUN (show_ip_ospf_route, for (ALL_LIST_ELEMENTS_RO(om->ospf, node, ospf)) { if (!ospf->oi_running) continue; - ospf_output = TRUE; + ospf_output = true; ret = show_ip_ospf_route_common(vty, ospf, json, use_vrf); } @@ -9629,7 +9713,7 @@ DEFUN (show_ip_ospf_vrfs, struct ospf *ospf = NULL; struct listnode *node = NULL; int count = 0; - static char header[] = "Name Id RouterId "; + static const char header[] = "Name Id RouterId "; if (uj) { json = json_object_new_object(); @@ -9648,7 +9732,7 @@ DEFUN (show_ip_ospf_vrfs, if (uj) json_vrf = json_object_new_object(); - if (ospf->vrf_id == 0) + if (ospf->vrf_id == VRF_DEFAULT) name = VRF_DEFAULT_NAME; else name = ospf->name; @@ -9686,16 +9770,23 @@ DEFUN (show_ip_ospf_vrfs, return CMD_SUCCESS; } -const char *ospf_abr_type_str[] = {"unknown", "standard", "ibm", "cisco", - "shortcut"}; +static const char *const ospf_abr_type_str[] = { + "unknown", "standard", "ibm", "cisco", "shortcut" +}; -const char *ospf_shortcut_mode_str[] = {"default", "enable", "disable"}; +static const char *const ospf_shortcut_mode_str[] = { + "default", "enable", "disable" +}; -const char *ospf_int_type_str[] = {"unknown", /* should never be used. */ - "point-to-point", "broadcast", - "non-broadcast", "point-to-multipoint", - "virtual-link", /* should never be used. */ - "loopback"}; +static const char *const ospf_int_type_str[] = { + "unknown", /* should never be used. */ + "point-to-point", + "broadcast", + "non-broadcast", + "point-to-multipoint", + "virtual-link", /* should never be used. */ + "loopback" +}; static int config_write_interface_one(struct vty *vty, struct vrf *vrf) { @@ -9958,7 +10049,7 @@ static int config_write_interface(struct vty *vty) static int config_write_network_area(struct vty *vty, struct ospf *ospf) { struct route_node *rn; - uint8_t buf[INET_ADDRSTRLEN]; + char buf[INET_ADDRSTRLEN]; /* `network area' print. */ for (rn = route_top(ospf->networks); rn; rn = route_next(rn)) @@ -9967,12 +10058,12 @@ static int config_write_network_area(struct vty *vty, struct ospf *ospf) /* Create Area ID string by specified Area ID format. */ if (n->area_id_fmt == OSPF_AREA_ID_FMT_DOTTEDQUAD) - inet_ntop(AF_INET, &n->area_id, (char *)buf, + inet_ntop(AF_INET, &n->area_id, buf, sizeof(buf)); else - sprintf((char *)buf, "%lu", - (unsigned long int)ntohl( - n->area_id.s_addr)); + snprintf(buf, sizeof(buf), "%lu", + (unsigned long int)ntohl( + n->area_id.s_addr)); /* Network print. */ vty_out(vty, " network %s/%d area %s\n", @@ -9987,13 +10078,13 @@ static int config_write_ospf_area(struct vty *vty, struct ospf *ospf) { struct listnode *node; struct ospf_area *area; - uint8_t buf[INET_ADDRSTRLEN]; + char buf[INET_ADDRSTRLEN]; /* Area configuration print. */ for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) { struct route_node *rn1; - area_id2str((char *)buf, sizeof(buf), &area->area_id, + area_id2str(buf, sizeof(buf), &area->area_id, area->area_id_fmt); if (area->auth_type != OSPF_AUTH_NULL) { @@ -10165,8 +10256,7 @@ static int config_write_virtual_link(struct vty *vty, struct ospf *ospf) ->auth_crypt, n2, ck)) vty_out(vty, - " area %s virtual-link %s" - " message-digest-key %d md5 %s\n", + " area %s virtual-link %s message-digest-key %d md5 %s\n", buf, inet_ntoa(vl_data->vl_peer), ck->key_id, ck->auth_key); } @@ -10331,9 +10421,9 @@ static int ospf_config_write_one(struct vty *vty, struct ospf *ospf) if (CHECK_FLAG(ospf->config, OSPF_LOG_ADJACENCY_CHANGES)) { if (CHECK_FLAG(ospf->config, OSPF_LOG_ADJACENCY_DETAIL)) vty_out(vty, " log-adjacency-changes detail\n"); - else if (!DFLT_OSPF_LOG_ADJACENCY_CHANGES) + else if (!SAVE_OSPF_LOG_ADJACENCY_CHANGES) vty_out(vty, " log-adjacency-changes\n"); - } else if (DFLT_OSPF_LOG_ADJACENCY_CHANGES) { + } else if (SAVE_OSPF_LOG_ADJACENCY_CHANGES) { vty_out(vty, " no log-adjacency-changes\n"); } @@ -10345,8 +10435,7 @@ static int ospf_config_write_one(struct vty *vty, struct ospf *ospf) /* auto-cost reference-bandwidth configuration. */ if (ospf->ref_bandwidth != OSPF_DEFAULT_REF_BANDWIDTH) { vty_out(vty, - "! Important: ensure reference bandwidth " - "is consistent across all routers\n"); + "! Important: ensure reference bandwidth is consistent across all routers\n"); vty_out(vty, " auto-cost reference-bandwidth %d\n", ospf->ref_bandwidth); } @@ -10385,6 +10474,14 @@ static int ospf_config_write_one(struct vty *vty, struct ospf *ospf) if (ospf->passive_interface_default == OSPF_IF_PASSIVE) vty_out(vty, " passive-interface default\n"); + /* proactive-arp print. */ + if (ospf->proactive_arp != OSPF_PROACTIVE_ARP_DEFAULT) { + if (ospf->proactive_arp) + vty_out(vty, " proactive-arp\n"); + else + vty_out(vty, " no proactive-arp\n"); + } + FOR_ALL_INTERFACES (vrf, ifp) if (OSPF_IF_PARAM_CONFIGURED(IF_DEF_PARAMS(ifp), passive_interface) @@ -10516,14 +10613,21 @@ void ospf_vty_show_init(void) } +static int config_write_interface(struct vty *vty); /* ospfd's interface node. */ -static struct cmd_node interface_node = {INTERFACE_NODE, "%s(config-if)# ", 1}; +static struct cmd_node interface_node = { + .name = "interface", + .node = INTERFACE_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-if)# ", + .config_write = config_write_interface, +}; /* Initialization of OSPF interface. */ static void ospf_vty_if_init(void) { /* Install interface node. */ - install_node(&interface_node, config_write_interface); + install_node(&interface_node); if_cmd_init(); /* "ip ospf authentication" commands. */ @@ -10629,7 +10733,14 @@ static void ospf_vty_zebra_init(void) #endif /* 0 */ } -static struct cmd_node ospf_node = {OSPF_NODE, "%s(config-router)# ", 1}; +static int ospf_config_write(struct vty *vty); +static struct cmd_node ospf_node = { + .name = "ospf", + .node = OSPF_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-router)# ", + .config_write = ospf_config_write, +}; static void ospf_interface_clear(struct interface *ifp) { @@ -10644,7 +10755,7 @@ static void ospf_interface_clear(struct interface *ifp) DEFUN (clear_ip_ospf_interface, clear_ip_ospf_interface_cmd, - "clear ip ospf [vrf ] interface [IFNAME]", + "clear ip ospf [vrf NAME] interface [IFNAME]", CLEAR_STR IP_STR "OSPF information\n" @@ -10702,7 +10813,7 @@ void ospf_vty_clear_init(void) void ospf_vty_init(void) { /* Install ospf top node. */ - install_node(&ospf_node, ospf_config_write); + install_node(&ospf_node); /* "router ospf" commands. */ install_element(CONFIG_NODE, &router_ospf_cmd); @@ -10827,6 +10938,10 @@ void ospf_vty_init(void) install_element(OSPF_NODE, &no_ospf_write_multiplier_cmd); install_element(OSPF_NODE, &no_write_multiplier_cmd); + /* "proactive-arp" commands. */ + install_element(OSPF_NODE, &ospf_proactive_arp_cmd); + install_element(OSPF_NODE, &no_ospf_proactive_arp_cmd); + /* Init interface related vty commands. */ ospf_vty_if_init(); diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c index 4cbd817ad8..5dab899898 100644 --- a/ospfd/ospf_zebra.c +++ b/ospfd/ospf_zebra.c @@ -50,23 +50,23 @@ #include "ospfd/ospf_nsm.h" #include "ospfd/ospf_zebra.h" #include "ospfd/ospf_te.h" +#include "ospfd/ospf_sr.h" DEFINE_MTYPE_STATIC(OSPFD, OSPF_EXTERNAL, "OSPF External route table") DEFINE_MTYPE_STATIC(OSPFD, OSPF_REDISTRIBUTE, "OSPF Redistriute") DEFINE_MTYPE_STATIC(OSPFD, OSPF_DIST_ARGS, "OSPF Distribute arguments") -DEFINE_HOOK(ospf_if_update, (struct interface * ifp), (ifp)) -DEFINE_HOOK(ospf_if_delete, (struct interface * ifp), (ifp)) /* Zebra structure to hold current status. */ struct zclient *zclient = NULL; +/* and for the Synchronous connection to the Label Manager */ +static struct zclient *zclient_sync; /* For registering threads. */ extern struct thread_master *master; /* Router-id update message from zebra. */ -static int ospf_router_id_update_zebra(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ospf_router_id_update_zebra(ZAPI_CALLBACK_ARGS) { struct ospf *ospf = NULL; struct prefix router_id; @@ -91,186 +91,20 @@ static int ospf_router_id_update_zebra(int command, struct zclient *zclient, prefix2str(&router_id, buf, sizeof(buf)); zlog_debug( "%s: ospf instance not found for vrf %s id %u router_id %s", - __PRETTY_FUNCTION__, - ospf_vrf_id_to_name(vrf_id), vrf_id, buf); + __func__, ospf_vrf_id_to_name(vrf_id), vrf_id, + buf); } } return 0; } -/* Inteface addition message from zebra. */ -static int ospf_interface_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) -{ - struct interface *ifp = NULL; - struct ospf *ospf = NULL; - - ifp = zebra_interface_add_read(zclient->ibuf, vrf_id); - if (ifp == NULL) - return 0; - - if (IS_DEBUG_OSPF(zebra, ZEBRA_INTERFACE)) - zlog_debug( - "Zebra: interface add %s vrf %s[%u] index %d flags %llx metric %d mtu %d speed %u", - ifp->name, ospf_vrf_id_to_name(ifp->vrf_id), - ifp->vrf_id, ifp->ifindex, - (unsigned long long)ifp->flags, ifp->metric, ifp->mtu, - ifp->speed); - - assert(ifp->info); - - if (IF_DEF_PARAMS(ifp) - && !OSPF_IF_PARAM_CONFIGURED(IF_DEF_PARAMS(ifp), type)) { - SET_IF_PARAM(IF_DEF_PARAMS(ifp), type); - IF_DEF_PARAMS(ifp)->type = ospf_default_iftype(ifp); - } - - ospf = ospf_lookup_by_vrf_id(vrf_id); - if (!ospf) - return 0; - - ospf_if_recalculate_output_cost(ifp); - - ospf_if_update(ospf, ifp); - - hook_call(ospf_if_update, ifp); - - return 0; -} - -static int ospf_interface_delete(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) -{ - struct interface *ifp; - struct stream *s; - struct route_node *rn; - - s = zclient->ibuf; - /* zebra_interface_state_read() updates interface structure in iflist */ - ifp = zebra_interface_state_read(s, vrf_id); - - if (ifp == NULL) - return 0; - - if (IS_DEBUG_OSPF(zebra, ZEBRA_INTERFACE)) - zlog_debug( - "Zebra: interface delete %s vrf %s[%u] index %d flags %llx metric %d mtu %d", - ifp->name, ospf_vrf_id_to_name(ifp->vrf_id), - ifp->vrf_id, ifp->ifindex, - (unsigned long long)ifp->flags, ifp->metric, ifp->mtu); - - hook_call(ospf_if_delete, ifp); - - for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next(rn)) - if (rn->info) - ospf_if_free((struct ospf_interface *)rn->info); - - if_set_index(ifp, IFINDEX_INTERNAL); - return 0; -} - -static struct interface *zebra_interface_if_lookup(struct stream *s, - vrf_id_t vrf_id) -{ - char ifname_tmp[INTERFACE_NAMSIZ]; - - /* Read interface name. */ - stream_get(ifname_tmp, s, INTERFACE_NAMSIZ); - - /* And look it up. */ - return if_lookup_by_name(ifname_tmp, vrf_id); -} - -static int ospf_interface_state_up(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) -{ - struct interface *ifp; - struct ospf_interface *oi; - struct route_node *rn; - - ifp = zebra_interface_if_lookup(zclient->ibuf, vrf_id); - - if (ifp == NULL) - return 0; - - /* Interface is already up. */ - if (if_is_operative(ifp)) { - /* Temporarily keep ifp values. */ - struct interface if_tmp; - memcpy(&if_tmp, ifp, sizeof(struct interface)); - - zebra_interface_if_set_value(zclient->ibuf, ifp); - - if (IS_DEBUG_OSPF(zebra, ZEBRA_INTERFACE)) - zlog_debug( - "Zebra: Interface[%s] state update speed %u -> %u, bw %d -> %d", - ifp->name, if_tmp.speed, ifp->speed, - if_tmp.bandwidth, ifp->bandwidth); - - ospf_if_recalculate_output_cost(ifp); - - if (if_tmp.mtu != ifp->mtu) { - if (IS_DEBUG_OSPF(zebra, ZEBRA_INTERFACE)) - zlog_debug( - "Zebra: Interface[%s] MTU change %u -> %u.", - ifp->name, if_tmp.mtu, ifp->mtu); - - /* Must reset the interface (simulate down/up) when MTU - * changes. */ - ospf_if_reset(ifp); - } - return 0; - } - - zebra_interface_if_set_value(zclient->ibuf, ifp); - - if (IS_DEBUG_OSPF(zebra, ZEBRA_INTERFACE)) - zlog_debug("Zebra: Interface[%s] state change to up.", - ifp->name); - - for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next(rn)) { - if ((oi = rn->info) == NULL) - continue; - - ospf_if_up(oi); - } - - return 0; -} - -static int ospf_interface_state_down(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) -{ - struct interface *ifp; - struct ospf_interface *oi; - struct route_node *node; - - ifp = zebra_interface_state_read(zclient->ibuf, vrf_id); - - if (ifp == NULL) - return 0; - - if (IS_DEBUG_OSPF(zebra, ZEBRA_INTERFACE)) - zlog_debug("Zebra: Interface[%s] state change to down.", - ifp->name); - - for (node = route_top(IF_OIFS(ifp)); node; node = route_next(node)) { - if ((oi = node->info) == NULL) - continue; - ospf_if_down(oi); - } - - return 0; -} - -static int ospf_interface_address_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ospf_interface_address_add(ZAPI_CALLBACK_ARGS) { struct connected *c; struct ospf *ospf = NULL; - c = zebra_interface_address_read(command, zclient->ibuf, vrf_id); + c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); if (c == NULL) return 0; @@ -289,13 +123,12 @@ static int ospf_interface_address_add(int command, struct zclient *zclient, ospf_if_update(ospf, c->ifp); - hook_call(ospf_if_update, c->ifp); + ospf_if_interface(c->ifp); return 0; } -static int ospf_interface_address_delete(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ospf_interface_address_delete(ZAPI_CALLBACK_ARGS) { struct connected *c; struct interface *ifp; @@ -303,7 +136,7 @@ static int ospf_interface_address_delete(int command, struct zclient *zclient, struct route_node *rn; struct prefix p; - c = zebra_interface_address_read(command, zclient->ibuf, vrf_id); + c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); if (c == NULL) return 0; @@ -321,7 +154,7 @@ static int ospf_interface_address_delete(int command, struct zclient *zclient, rn = route_node_lookup(IF_OIFS(ifp), &p); if (!rn) { - connected_free(c); + connected_free(&c); return 0; } @@ -332,15 +165,14 @@ static int ospf_interface_address_delete(int command, struct zclient *zclient, /* Call interface hook functions to clean up */ ospf_if_free(oi); - hook_call(ospf_if_update, c->ifp); + ospf_if_interface(c->ifp); - connected_free(c); + connected_free(&c); return 0; } -static int ospf_interface_link_params(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ospf_interface_link_params(ZAPI_CALLBACK_ARGS) { struct interface *ifp; @@ -356,8 +188,7 @@ static int ospf_interface_link_params(int command, struct zclient *zclient, } /* VRF update for an interface. */ -static int ospf_interface_vrf_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ospf_interface_vrf_update(ZAPI_CALLBACK_ARGS) { struct interface *ifp = NULL; vrf_id_t new_vrf_id; @@ -370,7 +201,7 @@ static int ospf_interface_vrf_update(int command, struct zclient *zclient, if (IS_DEBUG_OSPF_EVENT) zlog_debug( "%s: Rx Interface %s VRF change vrf_id %u New vrf %s id %u", - __PRETTY_FUNCTION__, ifp->name, vrf_id, + __func__, ifp->name, vrf_id, ospf_vrf_id_to_name(new_vrf_id), new_vrf_id); /*if_update(ifp, ifp->name, strlen(ifp->name), new_vrf_id);*/ @@ -447,7 +278,7 @@ void ospf_zebra_add(struct ospf *ospf, struct prefix_ipv4 *p, count++; if (IS_DEBUG_OSPF(zebra, ZEBRA_REDISTRIBUTE)) { - char buf[2][INET_ADDRSTRLEN]; + char buf[2][PREFIX2STR_BUFFER]; struct interface *ifp; ifp = if_lookup_by_index(path->ifindex, ospf->vrf_id); @@ -569,6 +400,101 @@ struct ospf_external *ospf_external_add(struct ospf *ospf, uint8_t type, return ext; } +/* + * Walk all the ei received from zebra for a route type and apply + * default route-map. + */ +bool ospf_external_default_routemap_apply_walk(struct ospf *ospf, + struct list *ext_list, + struct external_info *default_ei) +{ + struct listnode *node; + struct ospf_external *ext; + struct route_node *rn; + struct external_info *ei = NULL; + int ret = 0; + + for (ALL_LIST_ELEMENTS_RO(ext_list, node, ext)) { + if (!ext->external_info) + continue; + + for (rn = route_top(ext->external_info); rn; + rn = route_next(rn)) { + ei = rn->info; + if (!ei) + continue; + ret = ospf_external_info_apply_default_routemap( + ospf, ei, default_ei); + if (ret) + break; + } + } + + if (ret && ei) { + if (IS_DEBUG_OSPF_DEFAULT_INFO) + zlog_debug("Default originate routemap permit ei: %s", + inet_ntoa(ei->p.prefix)); + return true; + } + + return false; +} + +/* + * Function to originate or flush default after applying + * route-map on all ei. + */ +static int ospf_external_lsa_default_routemap_timer(struct thread *thread) +{ + struct list *ext_list; + struct ospf *ospf = THREAD_ARG(thread); + struct prefix_ipv4 p; + int type; + int ret = 0; + struct ospf_lsa *lsa; + struct external_info *default_ei; + + p.family = AF_INET; + p.prefixlen = 0; + p.prefix.s_addr = INADDR_ANY; + + /* Get the default extenal info. */ + default_ei = ospf_external_info_lookup(ospf, DEFAULT_ROUTE, + ospf->instance, &p); + if (!default_ei) { + /* Nothing to be done here. */ + if (IS_DEBUG_OSPF_DEFAULT_INFO) + zlog_debug("Default originate info not present"); + return 0; + } + + /* For all the ei apply route-map */ + for (type = 0; type <= ZEBRA_ROUTE_MAX; type++) { + ext_list = ospf->external[type]; + if (!ext_list || type == ZEBRA_ROUTE_OSPF) + continue; + + ret = ospf_external_default_routemap_apply_walk(ospf, ext_list, + default_ei); + if (ret) + break; + } + + /* Get the default LSA. */ + lsa = ospf_external_info_find_lsa(ospf, &p); + + /* If permit then originate default. */ + if (ret && !lsa) + ospf_external_lsa_originate(ospf, default_ei); + else if (ret && lsa && IS_LSA_MAXAGE(lsa)) + ospf_external_lsa_refresh(ospf, lsa, default_ei, true); + else if (!ret && lsa) + ospf_external_lsa_flush(ospf, DEFAULT_ROUTE, &default_ei->p, 0); + + return 1; +} + + void ospf_external_del(struct ospf *ospf, uint8_t type, unsigned short instance) { struct ospf_external *ext; @@ -586,6 +512,125 @@ void ospf_external_del(struct ospf *ospf, uint8_t type, unsigned short instance) XFREE(MTYPE_OSPF_EXTERNAL, ext); } + + /* + * Check if default needs to be flushed too. + */ + thread_add_event(master, ospf_external_lsa_default_routemap_timer, ospf, + 0, &ospf->t_default_routemap_timer); +} + +/* Update NHLFE for Prefix SID */ +void ospf_zebra_update_prefix_sid(const struct sr_prefix *srp) +{ + struct zapi_labels zl; + struct zapi_nexthop *znh; + struct listnode *node; + struct ospf_path *path; + + osr_debug("SR (%s): Update Labels %u for Prefix %pFX", __func__, + srp->label_in, (struct prefix *)&srp->prefv4); + + /* Prepare message. */ + memset(&zl, 0, sizeof(zl)); + zl.type = ZEBRA_LSP_OSPF_SR; + zl.local_label = srp->label_in; + + switch (srp->type) { + case LOCAL_SID: + /* Set Label for local Prefix */ + znh = &zl.nexthops[zl.nexthop_num++]; + znh->type = NEXTHOP_TYPE_IFINDEX; + znh->ifindex = srp->nhlfe.ifindex; + znh->label_num = 1; + znh->labels[0] = srp->nhlfe.label_out; + break; + + case PREF_SID: + /* Update route in the RIB too. */ + SET_FLAG(zl.message, ZAPI_LABELS_FTN); + zl.route.prefix.u.prefix4 = srp->prefv4.prefix; + zl.route.prefix.prefixlen = srp->prefv4.prefixlen; + zl.route.prefix.family = srp->prefv4.family; + zl.route.type = ZEBRA_ROUTE_OSPF; + zl.route.instance = 0; + + /* Check that SRP contains at least one valid path */ + if (srp->route == NULL) { + return; + } + for (ALL_LIST_ELEMENTS_RO(srp->route->paths, node, path)) { + if (path->srni.label_out == MPLS_INVALID_LABEL) + continue; + + if (zl.nexthop_num >= MULTIPATH_NUM) + break; + + znh = &zl.nexthops[zl.nexthop_num++]; + znh->type = NEXTHOP_TYPE_IPV4_IFINDEX; + znh->gate.ipv4 = path->nexthop; + znh->ifindex = path->ifindex; + znh->label_num = 1; + znh->labels[0] = path->srni.label_out; + } + break; + default: + return; + } + + /* Finally, send message to zebra. */ + (void)zebra_send_mpls_labels(zclient, ZEBRA_MPLS_LABELS_REPLACE, &zl); +} + +/* Remove NHLFE for Prefix-SID */ +void ospf_zebra_delete_prefix_sid(const struct sr_prefix *srp) +{ + struct zapi_labels zl; + + osr_debug("SR (%s): Delete Labels %u for Prefix %pFX", __func__, + srp->label_in, (struct prefix *)&srp->prefv4); + + /* Prepare message. */ + memset(&zl, 0, sizeof(zl)); + zl.type = ZEBRA_LSP_OSPF_SR; + zl.local_label = srp->label_in; + + if (srp->type == PREF_SID) { + /* Update route in the RIB too */ + SET_FLAG(zl.message, ZAPI_LABELS_FTN); + zl.route.prefix.u.prefix4 = srp->prefv4.prefix; + zl.route.prefix.prefixlen = srp->prefv4.prefixlen; + zl.route.prefix.family = srp->prefv4.family; + zl.route.type = ZEBRA_ROUTE_OSPF; + zl.route.instance = 0; + } + + /* Send message to zebra. */ + (void)zebra_send_mpls_labels(zclient, ZEBRA_MPLS_LABELS_DELETE, &zl); +} + +/* Send MPLS Label entry to Zebra for installation or deletion */ +void ospf_zebra_send_adjacency_sid(int cmd, struct sr_nhlfe nhlfe) +{ + struct zapi_labels zl; + struct zapi_nexthop *znh; + + osr_debug("SR (%s): %s Labels %u/%u for Adjacency via %u", __func__, + cmd == ZEBRA_MPLS_LABELS_ADD ? "Add" : "Delete", + nhlfe.label_in, nhlfe.label_out, nhlfe.ifindex); + + memset(&zl, 0, sizeof(zl)); + zl.type = ZEBRA_LSP_OSPF_SR; + zl.local_label = nhlfe.label_in; + zl.nexthop_num = 1; + znh = &zl.nexthops[0]; + znh->type = NEXTHOP_TYPE_IPV4_IFINDEX; + znh->gate.ipv4 = nhlfe.nexthop; + znh->ifindex = nhlfe.ifindex; + znh->label_num = 1; + znh->labels[0] = nhlfe.label_out; + + (void)zebra_send_mpls_labels(zclient, cmd, &zl); } struct ospf_redist *ospf_redist_lookup(struct ospf *ospf, uint8_t type, @@ -753,53 +798,19 @@ int ospf_redistribute_unset(struct ospf *ospf, int type, int ospf_redistribute_default_set(struct ospf *ospf, int originate, int mtype, int mvalue) { - struct ospf_external *ext; struct prefix_ipv4 p; struct in_addr nexthop; int cur_originate = ospf->default_originate; + const char *type_str = NULL; - nexthop.s_addr = 0; + nexthop.s_addr = INADDR_ANY; p.family = AF_INET; - p.prefix.s_addr = 0; + p.prefix.s_addr = INADDR_ANY; p.prefixlen = 0; ospf->default_originate = originate; - ospf_external_add(ospf, DEFAULT_ROUTE, 0); - - if (cur_originate == DEFAULT_ORIGINATE_NONE) { - /* First time configuration */ - if (IS_DEBUG_OSPF(zebra, ZEBRA_REDISTRIBUTE)) - zlog_debug("Redistribute[DEFAULT]: Start Type[%d], Metric[%d]", - metric_type(ospf, DEFAULT_ROUTE, 0), - metric_value(ospf, DEFAULT_ROUTE, 0)); - - if (ospf->router_id.s_addr == 0) - ospf->external_origin |= (1 << DEFAULT_ROUTE); - if ((originate == DEFAULT_ORIGINATE_ALWAYS) - && (ospf->router_id.s_addr)) { - - /* always , so originate lsa even it doesn't - * exist in RIB. - */ - ospf_external_info_add(ospf, DEFAULT_ROUTE, 0, - p, 0, nexthop, 0); - ospf_external_lsa_refresh_default(ospf); - - } else if (originate == DEFAULT_ORIGINATE_ZEBRA) { - /* Send msg to Zebra to validate default route - * existance. - */ - zclient_redistribute_default( - ZEBRA_REDISTRIBUTE_DEFAULT_ADD, zclient, AFI_IP, - ospf->vrf_id); - } - - ospf_asbr_status_update(ospf, ++ospf->redistribute); - return CMD_SUCCESS; - - - } else if (originate == cur_originate) { + if (cur_originate == originate) { /* Refresh the lsa since metric might different */ if (IS_DEBUG_OSPF(zebra, ZEBRA_REDISTRIBUTE)) zlog_debug( @@ -809,67 +820,51 @@ int ospf_redistribute_default_set(struct ospf *ospf, int originate, int mtype, metric_value(ospf, DEFAULT_ROUTE, 0)); ospf_external_lsa_refresh_default(ospf); - - } else { - /* "default-info originate always" configured now, - * where "default-info originate" configured previoulsly. - */ - if (originate == DEFAULT_ORIGINATE_ALWAYS) { - - zclient_redistribute_default( - ZEBRA_REDISTRIBUTE_DEFAULT_DELETE, - zclient, AFI_IP, ospf->vrf_id); - /* here , ex-info should be added since ex-info might - * have not updated earlier if def route is not exist. - * If ex-iinfo ex-info already exist , it will return - * smoothly. - */ - ospf_external_info_add(ospf, DEFAULT_ROUTE, 0, - p, 0, nexthop, 0); - ospf_external_lsa_refresh_default(ospf); - - } else { - /* "default-info originate" configured now,where - * "default-info originate always" configured - * previoulsy. - */ - - ospf_external_lsa_flush(ospf, DEFAULT_ROUTE, &p, 0); - - ext = ospf_external_lookup(ospf, DEFAULT_ROUTE, 0); - if (ext && EXTERNAL_INFO(ext)) - ospf_external_info_delete(ospf, - DEFAULT_ROUTE, 0, p); - - zclient_redistribute_default( - ZEBRA_REDISTRIBUTE_DEFAULT_ADD, - zclient, AFI_IP, ospf->vrf_id); - } + return CMD_SUCCESS; } - return CMD_SUCCESS; -} -int ospf_redistribute_default_unset(struct ospf *ospf) -{ - if (ospf->default_originate == DEFAULT_ORIGINATE_ZEBRA) { - if (!ospf_is_type_redistributed(ospf, DEFAULT_ROUTE, 0)) - return CMD_SUCCESS; + switch (cur_originate) { + case DEFAULT_ORIGINATE_NONE: + break; + case DEFAULT_ORIGINATE_ZEBRA: zclient_redistribute_default(ZEBRA_REDISTRIBUTE_DEFAULT_DELETE, zclient, AFI_IP, ospf->vrf_id); + ospf->redistribute--; + break; + case DEFAULT_ORIGINATE_ALWAYS: + ospf_external_info_delete(ospf, DEFAULT_ROUTE, 0, p); + ospf_external_del(ospf, DEFAULT_ROUTE, 0); + ospf->redistribute--; + break; } - ospf->default_originate = DEFAULT_ORIGINATE_NONE; + switch (originate) { + case DEFAULT_ORIGINATE_NONE: + type_str = "none"; + break; + case DEFAULT_ORIGINATE_ZEBRA: + type_str = "normal"; + ospf->redistribute++; + zclient_redistribute_default(ZEBRA_REDISTRIBUTE_DEFAULT_ADD, + zclient, AFI_IP, ospf->vrf_id); + break; + case DEFAULT_ORIGINATE_ALWAYS: + type_str = "always"; + ospf->redistribute++; + ospf_external_add(ospf, DEFAULT_ROUTE, 0); + ospf_external_info_add(ospf, DEFAULT_ROUTE, 0, p, 0, nexthop, + 0); + break; + } if (IS_DEBUG_OSPF(zebra, ZEBRA_REDISTRIBUTE)) - zlog_debug("Redistribute[DEFAULT]: Stop"); - - // Pending: how does the external_info cleanup work in this case? - - ospf_asbr_status_update(ospf, --ospf->redistribute); - - /* clean up maxage default originate external lsa */ - ospf_default_originate_lsa_update(ospf); + zlog_debug("Redistribute[DEFAULT]: %s Type[%d], Metric[%d]", + type_str, + metric_type(ospf, DEFAULT_ROUTE, 0), + metric_value(ospf, DEFAULT_ROUTE, 0)); + ospf_external_lsa_refresh_default(ospf); + ospf_asbr_status_update(ospf, ospf->redistribute); return CMD_SUCCESS; } @@ -879,8 +874,7 @@ static int ospf_external_lsa_originate_check(struct ospf *ospf, /* If prefix is multicast, then do not originate LSA. */ if (IN_MULTICAST(htonl(ei->p.prefix.s_addr))) { zlog_info( - "LSA[Type5:%s]: Not originate AS-external-LSA, " - "Prefix belongs multicast", + "LSA[Type5:%s]: Not originate AS-external-LSA, Prefix belongs multicast", inet_ntoa(ei->p.prefix)); return 0; } @@ -889,8 +883,7 @@ static int ospf_external_lsa_originate_check(struct ospf *ospf, if (is_prefix_default(&ei->p)) if (ospf->default_originate == DEFAULT_ORIGINATE_NONE) { zlog_info( - "LSA[Type5:0.0.0.0]: Not originate AS-external-LSA " - "for default"); + "LSA[Type5:0.0.0.0]: Not originate AS-external-LSA for default"); return 0; } @@ -910,6 +903,132 @@ int ospf_distribute_check_connected(struct ospf *ospf, struct external_info *ei) return 1; } + +/* Apply default route-map on ei received. */ +int ospf_external_info_apply_default_routemap(struct ospf *ospf, + struct external_info *ei, + struct external_info *default_ei) +{ + struct ospf_redist *red; + int type = default_ei->type; + struct prefix_ipv4 *p = &ei->p; + struct route_map_set_values save_values; + + + if (!ospf_external_lsa_originate_check(ospf, default_ei)) + return 0; + + save_values = default_ei->route_map_set; + ospf_reset_route_map_set_values(&default_ei->route_map_set); + + /* apply route-map if needed */ + red = ospf_redist_lookup(ospf, type, ospf->instance); + if (red && ROUTEMAP_NAME(red)) { + route_map_result_t ret; + + ret = route_map_apply(ROUTEMAP(red), (struct prefix *)p, + RMAP_OSPF, ei); + + if (ret == RMAP_DENYMATCH) { + ei->route_map_set = save_values; + return 0; + } + } + + return 1; +} + + +/* + * Default originated is based on route-map condition then + * apply route-map on received external info. Originate or + * flush based on route-map condition. + */ +static bool ospf_external_lsa_default_routemap_apply(struct ospf *ospf, + struct external_info *ei, + int cmd) +{ + struct external_info *default_ei; + struct prefix_ipv4 p; + struct ospf_lsa *lsa; + int ret; + + p.family = AF_INET; + p.prefixlen = 0; + p.prefix.s_addr = INADDR_ANY; + + + /* Get the default extenal info. */ + default_ei = ospf_external_info_lookup(ospf, DEFAULT_ROUTE, + ospf->instance, &p); + if (!default_ei) { + /* Nothing to be done here. */ + return false; + } + + if (IS_DEBUG_OSPF_DEFAULT_INFO) + zlog_debug("Apply default originate routemap on ei: %s cmd: %d", + inet_ntoa(ei->p.prefix), cmd); + + ret = ospf_external_info_apply_default_routemap(ospf, ei, default_ei); + + /* If deny then nothing to be done both in add and del case. */ + if (!ret) { + if (IS_DEBUG_OSPF_DEFAULT_INFO) + zlog_debug("Default originte routemap deny for ei: %s", + inet_ntoa(ei->p.prefix)); + return false; + } + + /* Get the default LSA. */ + lsa = ospf_external_info_find_lsa(ospf, &p); + + /* If this is add route and permit then ooriginate default. */ + if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD) { + /* If permit and default already advertise then return. */ + if (lsa && !IS_LSA_MAXAGE(lsa)) { + if (IS_DEBUG_OSPF_DEFAULT_INFO) + zlog_debug("Default lsa already originated"); + return true; + } + + if (IS_DEBUG_OSPF_DEFAULT_INFO) + zlog_debug("Originating/Refreshing default lsa"); + + if (lsa && IS_LSA_MAXAGE(lsa)) + /* Refresh lsa.*/ + ospf_external_lsa_refresh(ospf, lsa, default_ei, true); + else + /* If permit and default not advertised then advertise. + */ + ospf_external_lsa_originate(ospf, default_ei); + + } else if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_DEL) { + /* If deny and lsa is not originated then nothing to be done.*/ + if (!lsa) { + if (IS_DEBUG_OSPF_DEFAULT_INFO) + zlog_debug( + "Default lsa not originated, not flushing"); + return true; + } + + if (IS_DEBUG_OSPF_DEFAULT_INFO) + zlog_debug( + "Running default route-map again as ei: %s deleted", + inet_ntoa(ei->p.prefix)); + /* + * if this route delete was permitted then we need to check + * there are any other external info which can still trigger + * default route origination else flush it. + */ + thread_add_event(master, + ospf_external_lsa_default_routemap_timer, ospf, + 0, &ospf->t_default_routemap_timer); + } + + return true; +} + /* return 1 if external LSA must be originated, 0 otherwise */ int ospf_redistribute_check(struct ospf *ospf, struct external_info *ei, int *changed) @@ -919,6 +1038,11 @@ int ospf_redistribute_check(struct ospf *ospf, struct external_info *ei, struct ospf_redist *red; uint8_t type = is_prefix_default(&ei->p) ? DEFAULT_ROUTE : ei->type; unsigned short instance = is_prefix_default(&ei->p) ? 0 : ei->instance; + route_tag_t saved_tag = 0; + + /* Default is handled differently. */ + if (type == DEFAULT_ROUTE) + return 1; if (changed) *changed = 0; @@ -949,10 +1073,14 @@ int ospf_redistribute_check(struct ospf *ospf, struct external_info *ei, save_values = ei->route_map_set; ospf_reset_route_map_set_values(&ei->route_map_set); + saved_tag = ei->tag; + /* Resetting with original route tag */ + ei->tag = ei->orig_tag; + /* apply route-map if needed */ red = ospf_redist_lookup(ospf, type, instance); if (red && ROUTEMAP_NAME(red)) { - int ret; + route_map_result_t ret; ret = route_map_apply(ROUTEMAP(red), (struct prefix *)p, RMAP_OSPF, ei); @@ -970,9 +1098,13 @@ int ospf_redistribute_check(struct ospf *ospf, struct external_info *ei, } /* check if 'route-map set' changed something */ - if (changed) + if (changed) { *changed = !ospf_route_map_set_compare( &ei->route_map_set, &save_values); + + /* check if tag is modified */ + *changed |= (saved_tag != ei->tag); + } } return 1; @@ -1003,8 +1135,7 @@ void ospf_routemap_unset(struct ospf_redist *red) } /* Zebra route add and delete treatment. */ -static int ospf_zebra_read_route(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ospf_zebra_read_route(ZAPI_CALLBACK_ARGS) { struct zapi_route api; struct prefix_ipv4 p; @@ -1043,11 +1174,12 @@ static int ospf_zebra_read_route(int command, struct zclient *zclient, char buf_prefix[PREFIX_STRLEN]; prefix2str(&api.prefix, buf_prefix, sizeof(buf_prefix)); - zlog_debug("%s: from client %s: vrf_id %d, p %s", __func__, + zlog_debug("%s: cmd %s from client %s: vrf_id %d, p %s", + __func__, zserv_command_string(cmd), zebra_route_string(api.type), vrf_id, buf_prefix); } - if (command == ZEBRA_REDISTRIBUTE_ROUTE_ADD) { + if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD) { /* XXX|HACK|TODO|FIXME: * Maybe we should ignore reject/blackhole routes? Testing * shows that there is no problems though and this is only way @@ -1077,11 +1209,7 @@ static int ospf_zebra_read_route(int command, struct zclient *zclient, /* Nothing has changed, so nothing to do; return */ return 0; } - if (ospf->router_id.s_addr == 0) - /* Set flags to generate AS-external-LSA originate event - for each redistributed protocols later. */ - ospf->external_origin |= (1 << rt_type); - else { + if (ospf->router_id.s_addr != INADDR_ANY) { if (ei) { if (is_prefix_default(&p)) ospf_external_lsa_refresh_default(ospf); @@ -1108,8 +1236,24 @@ static int ospf_zebra_read_route(int command, struct zclient *zclient, } } } - } else /* if (command == ZEBRA_REDISTRIBUTE_ROUTE_DEL) */ + + /* + * Check if default-information originate is + * with some routemap prefix/access list match. + */ + ospf_external_lsa_default_routemap_apply(ospf, ei, cmd); + + } else /* if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_DEL) */ { + /* + * Check if default-information originate is + * with some routemap prefix/access list match. + * Apply before ei is deleted. + */ + ei = ospf_external_info_lookup(ospf, rt_type, api.instance, &p); + if (ei) + ospf_external_lsa_default_routemap_apply(ospf, ei, cmd); + ospf_external_info_delete(ospf, rt_type, api.instance, p); if (is_prefix_default(&p)) ospf_external_lsa_refresh_default(ospf); @@ -1118,6 +1262,7 @@ static int ospf_zebra_read_route(int command, struct zclient *zclient, ifindex /*, nexthop */); } + return 0; } @@ -1184,8 +1329,8 @@ static int ospf_distribute_list_update_timer(struct thread *thread) if (IS_DEBUG_OSPF_EVENT) { zlog_debug( "%s: ospf distribute-list update arg_type %d vrf %s id %d", - __PRETTY_FUNCTION__, arg_type, - ospf_vrf_id_to_name(ospf->vrf_id), ospf->vrf_id); + __func__, arg_type, ospf_vrf_id_to_name(ospf->vrf_id), + ospf->vrf_id); } /* foreach all external info. */ @@ -1208,11 +1353,28 @@ static int ospf_distribute_list_update_timer(struct thread *thread) default_refresh = 1; else if ( (lsa = ospf_external_info_find_lsa( - ospf, &ei->p))) + ospf, &ei->p))) { + int force = + LSA_REFRESH_IF_CHANGED; + /* If this is a MaxAge LSA, we + * need to force refresh it + * because distribute settings + * might have changed and now, + * this LSA needs to be + * originated, not be removed. + * If we don't force refresh it, + * it will remain a MaxAge LSA + * because it will look like it + * hasn't changed. Neighbors + * will not receive updates for + * this LSA. + */ + if (IS_LSA_MAXAGE(lsa)) + force = LSA_REFRESH_FORCE; + ospf_external_lsa_refresh( - ospf, lsa, ei, - LSA_REFRESH_IF_CHANGED); - else + ospf, lsa, ei, force); + } else ospf_external_lsa_originate( ospf, ei); } @@ -1229,7 +1391,6 @@ static int ospf_distribute_list_update_timer(struct thread *thread) void ospf_distribute_list_update(struct ospf *ospf, int type, unsigned short instance) { - struct route_table *rt; struct ospf_external *ext; void **args = XCALLOC(MTYPE_OSPF_DIST_ARGS, sizeof(void *) * 2); @@ -1238,7 +1399,7 @@ void ospf_distribute_list_update(struct ospf *ospf, int type, /* External info does not exist. */ ext = ospf_external_lookup(ospf, type, instance); - if (!ext || !(rt = EXTERNAL_INFO(ext))) { + if (!ext || !EXTERNAL_INFO(ext)) { XFREE(MTYPE_OSPF_DIST_ARGS, args); return; } @@ -1251,8 +1412,8 @@ void ospf_distribute_list_update(struct ospf *ospf, int type, /* Set timer. */ ospf->t_distribute_update = NULL; - thread_add_timer_msec(master, ospf_distribute_list_update_timer, - (void **)args, ospf->min_ls_interval, + thread_add_timer_msec(master, ospf_distribute_list_update_timer, args, + ospf->min_ls_interval, &ospf->t_distribute_update); } @@ -1548,8 +1709,7 @@ void ospf_zebra_vrf_register(struct ospf *ospf) if (ospf->vrf_id != VRF_UNKNOWN) { if (IS_DEBUG_OSPF_EVENT) - zlog_debug("%s: Register VRF %s id %u", - __PRETTY_FUNCTION__, + zlog_debug("%s: Register VRF %s id %u", __func__, ospf_vrf_id_to_name(ospf->vrf_id), ospf->vrf_id); zclient_send_reg_requests(zclient, ospf->vrf_id); @@ -1564,18 +1724,121 @@ void ospf_zebra_vrf_deregister(struct ospf *ospf) if (ospf->vrf_id != VRF_DEFAULT && ospf->vrf_id != VRF_UNKNOWN) { if (IS_DEBUG_OSPF_EVENT) zlog_debug("%s: De-Register VRF %s id %u to Zebra.", - __PRETTY_FUNCTION__, - ospf_vrf_id_to_name(ospf->vrf_id), + __func__, ospf_vrf_id_to_name(ospf->vrf_id), ospf->vrf_id); /* Deregister for router-id, interfaces, * redistributed routes. */ zclient_send_dereg_requests(zclient, ospf->vrf_id); } } + +/* Label Manager Functions */ + +/** + * Check if Label Manager is Ready or not. + * + * @return True if Label Manager is ready, False otherwise + */ +bool ospf_zebra_label_manager_ready(void) +{ + return (zclient_sync->sock > 0); +} + +/** + * Request Label Range to the Label Manager. + * + * @param base base label of the label range to request + * @param chunk_size size of the label range to request + * + * @return 0 on success, -1 on failure + */ +int ospf_zebra_request_label_range(uint32_t base, uint32_t chunk_size) +{ + int ret; + uint32_t start, end; + + if (zclient_sync->sock < 0) + return -1; + + ret = lm_get_label_chunk(zclient_sync, 0, base, chunk_size, &start, + &end); + if (ret < 0) { + zlog_warn("%s: error getting label range!", __func__); + return -1; + } + + return 0; +} + +/** + * Release Label Range to the Label Manager. + * + * @param start start of label range to release + * @param end end of label range to release + * + * @return 0 on success, -1 otherwise + */ +int ospf_zebra_release_label_range(uint32_t start, uint32_t end) +{ + int ret; + + if (zclient_sync->sock < 0) + return -1; + + ret = lm_release_label_chunk(zclient_sync, start, end); + if (ret < 0) { + zlog_warn("%s: error releasing label range!", __func__); + return -1; + } + + return 0; +} + +/** + * Connect to the Label Manager. + * + * @return 0 on success, -1 otherwise + */ +int ospf_zebra_label_manager_connect(void) +{ + /* Connect to label manager. */ + if (zclient_socket_connect(zclient_sync) < 0) { + zlog_warn("%s: failed connecting synchronous zclient!", + __func__); + return -1; + } + /* make socket non-blocking */ + set_nonblocking(zclient_sync->sock); + + /* Send hello to notify zebra this is a synchronous client */ + if (zclient_send_hello(zclient_sync) < 0) { + zlog_warn("%s: failed sending hello for synchronous zclient!", + __func__); + close(zclient_sync->sock); + zclient_sync->sock = -1; + return -1; + } + + /* Connect to label manager */ + if (lm_label_manager_connect(zclient_sync, 0) != 0) { + zlog_warn("%s: failed connecting to label manager!", __func__); + if (zclient_sync->sock > 0) { + close(zclient_sync->sock); + zclient_sync->sock = -1; + } + return -1; + } + + osr_debug("SR (%s): Successfully connected to the Label Manager", + __func__); + + return 0; +} + static void ospf_zebra_connected(struct zclient *zclient) { /* Send the client registration */ - bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER); + bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, VRF_DEFAULT); zclient_send_reg_requests(zclient, VRF_DEFAULT); } @@ -1587,10 +1850,6 @@ void ospf_zebra_init(struct thread_master *master, unsigned short instance) zclient_init(zclient, ZEBRA_ROUTE_OSPF, instance, &ospfd_privs); zclient->zebra_connected = ospf_zebra_connected; zclient->router_id_update = ospf_router_id_update_zebra; - zclient->interface_add = ospf_interface_add; - zclient->interface_delete = ospf_interface_delete; - zclient->interface_up = ospf_interface_state_up; - zclient->interface_down = ospf_interface_state_down; zclient->interface_address_add = ospf_interface_address_add; zclient->interface_address_delete = ospf_interface_address_delete; zclient->interface_link_params = ospf_interface_link_params; @@ -1599,8 +1858,27 @@ void ospf_zebra_init(struct thread_master *master, unsigned short instance) zclient->redistribute_route_add = ospf_zebra_read_route; zclient->redistribute_route_del = ospf_zebra_read_route; + /* Initialize special zclient for synchronous message exchanges. */ + struct zclient_options options = zclient_options_default; + options.synchronous = true; + zclient_sync = zclient_new(master, &options); + zclient_sync->sock = -1; + zclient_sync->redist_default = ZEBRA_ROUTE_OSPF; + zclient_sync->instance = instance; + /* + * session_id must be different from default value (0) to distinguish + * the asynchronous socket from the synchronous one + */ + zclient_sync->session_id = 1; + zclient_sync->privs = &ospfd_privs; + access_list_add_hook(ospf_filter_update); access_list_delete_hook(ospf_filter_update); prefix_list_add_hook(ospf_prefix_list_update); prefix_list_delete_hook(ospf_prefix_list_update); } + +void ospf_zebra_send_arp(const struct interface *ifp, const struct prefix *p) +{ + zclient_send_neigh_discovery_req(zclient, ifp, p); +} diff --git a/ospfd/ospf_zebra.h b/ospfd/ospf_zebra.h index 6737306532..bdc8af0402 100644 --- a/ospfd/ospf_zebra.h +++ b/ospfd/ospf_zebra.h @@ -41,6 +41,7 @@ struct ospf_distance { }; /* Prototypes */ +struct ospf_route; extern void ospf_zebra_add(struct ospf *ospf, struct prefix_ipv4 *, struct ospf_route *); extern void ospf_zebra_delete(struct ospf *ospf, struct prefix_ipv4 *, @@ -63,6 +64,13 @@ extern struct ospf_external *ospf_external_lookup(struct ospf *, uint8_t, unsigned short); extern struct ospf_external *ospf_external_add(struct ospf *, uint8_t, unsigned short); + +struct sr_prefix; +struct sr_nhlfe; +extern void ospf_zebra_update_prefix_sid(const struct sr_prefix *srp); +extern void ospf_zebra_delete_prefix_sid(const struct sr_prefix *srp); +extern void ospf_zebra_send_adjacency_sid(int cmd, struct sr_nhlfe nhlfe); + extern void ospf_external_del(struct ospf *, uint8_t, unsigned short); extern struct ospf_redist *ospf_redist_lookup(struct ospf *, uint8_t, unsigned short); @@ -70,7 +78,6 @@ extern struct ospf_redist *ospf_redist_add(struct ospf *, uint8_t, unsigned short); extern void ospf_redist_del(struct ospf *, uint8_t, unsigned short); - extern int ospf_redistribute_set(struct ospf *, int, unsigned short, int, int); extern int ospf_redistribute_unset(struct ospf *, int, unsigned short); extern int ospf_redistribute_default_set(struct ospf *, int, int, int); @@ -86,8 +93,17 @@ extern int ospf_distance_unset(struct vty *, struct ospf *, const char *, extern void ospf_zebra_init(struct thread_master *, unsigned short); extern void ospf_zebra_vrf_register(struct ospf *ospf); extern void ospf_zebra_vrf_deregister(struct ospf *ospf); +bool ospf_external_default_routemap_apply_walk( + struct ospf *ospf, struct list *ext_list, + struct external_info *default_ei); +int ospf_external_info_apply_default_routemap(struct ospf *ospf, + struct external_info *ei, + struct external_info *default_ei); -DECLARE_HOOK(ospf_if_update, (struct interface * ifp), (ifp)) -DECLARE_HOOK(ospf_if_delete, (struct interface * ifp), (ifp)) - +extern void ospf_zebra_send_arp(const struct interface *ifp, + const struct prefix *p); +bool ospf_zebra_label_manager_ready(void); +int ospf_zebra_label_manager_connect(void); +int ospf_zebra_request_label_range(uint32_t base, uint32_t chunk_size); +int ospf_zebra_release_label_range(uint32_t start, uint32_t end); #endif /* _ZEBRA_OSPF_ZEBRA_H */ diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c index 073a51561b..c56687b0a0 100644 --- a/ospfd/ospfd.c +++ b/ospfd/ospfd.c @@ -91,7 +91,6 @@ void ospf_router_id_update(struct ospf *ospf) struct ospf_interface *oi; struct interface *ifp; struct listnode *node; - int type; if (!ospf->oi_running) { if (IS_DEBUG_OSPF_EVENT) @@ -133,24 +132,18 @@ void ospf_router_id_update(struct ospf *ospf) * !(virtual | ptop) links */ ospf_nbr_self_reset(oi, router_id); - } - /* If AS-external-LSA is queued, then flush those LSAs. */ - if (router_id_old.s_addr == 0 && ospf->external_origin) { - /* Originate each redistributed external route. */ - for (type = 0; type < ZEBRA_ROUTE_MAX; type++) - if (ospf->external_origin & (1 << type)) - thread_add_event( - master, - ospf_external_lsa_originate_timer, - ospf, type, NULL); - /* Originate Deafult. */ - if (ospf->external_origin & (1 << ZEBRA_ROUTE_MAX)) - thread_add_event(master, - ospf_default_originate_timer, - ospf, 0, NULL); - - ospf->external_origin = 0; + /* + * If the old router id was not set, but now it + * is and the interface is operative and the + * state is ISM_Down we should kick the state + * machine as that we processed the interfaces + * based upon the network statement( or intf config ) + * but could not start it at that time. + */ + if (if_is_operative(oi->ifp) && oi->state == ISM_Down + && router_id_old.s_addr == INADDR_ANY) + ospf_if_up(oi); } /* Flush (inline) all external LSAs based on the OSPF_LSA_SELF @@ -196,20 +189,14 @@ void ospf_router_id_update(struct ospf *ospf) } } - /* Originate each redistributed external route. */ - for (type = 0; type < ZEBRA_ROUTE_MAX; type++) - thread_add_event(master, - ospf_external_lsa_originate_timer, - ospf, type, NULL); - thread_add_event(master, ospf_default_originate_timer, ospf, 0, - NULL); - /* update router-lsa's for each area */ ospf_router_lsa_update(ospf); /* update ospf_interface's */ FOR_ALL_INTERFACES (vrf, ifp) ospf_if_update(ospf, ifp); + + ospf_external_lsa_rid_change(ospf); } } @@ -245,7 +232,7 @@ static struct ospf *ospf_new(unsigned short instance, const char *name) if (IS_DEBUG_OSPF_EVENT) zlog_debug( "%s: Create new ospf instance with vrf_name %s vrf_id %u", - __PRETTY_FUNCTION__, name, new->vrf_id); + __func__, name, new->vrf_id); } else { new->vrf_id = VRF_DEFAULT; vrf = vrf_lookup_by_id(VRF_DEFAULT); @@ -318,10 +305,7 @@ static struct ospf *ospf_new(unsigned short instance, const char *name) new->oi_write_q = list_new(); new->write_oi_count = OSPF_WRITE_INTERFACE_COUNT_DEFAULT; -/* Enable "log-adjacency-changes" */ -#if DFLT_OSPF_LOG_ADJACENCY_CHANGES - SET_FLAG(new->config, OSPF_LOG_ADJACENCY_CHANGES); -#endif + new->proactive_arp = OSPF_PROACTIVE_ARP_DEFAULT; QOBJ_REG(new, ospf); @@ -359,7 +343,7 @@ struct ospf *ospf_lookup_instance(unsigned short instance) static int ospf_is_ready(struct ospf *ospf) { /* OSPF must be on and Router-ID must be configured. */ - if (!ospf || ospf->router_id.s_addr == 0) + if (!ospf || ospf->router_id.s_addr == INADDR_ANY) return 0; return 1; @@ -393,7 +377,17 @@ struct ospf *ospf_lookup_by_inst_name(unsigned short instance, const char *name) return NULL; } -struct ospf *ospf_get(unsigned short instance, const char *name) +static void ospf_init(struct ospf *ospf) +{ + ospf_opaque_type11_lsa_init(ospf); + + if (ospf->vrf_id != VRF_UNKNOWN) + ospf->oi_running = 1; + + ospf_router_id_update(ospf); +} + +struct ospf *ospf_get(unsigned short instance, const char *name, bool *created) { struct ospf *ospf; @@ -404,42 +398,28 @@ struct ospf *ospf_get(unsigned short instance, const char *name) else ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + *created = (ospf == NULL); if (ospf == NULL) { ospf = ospf_new(instance, name); ospf_add(ospf); - if (ospf->router_id_static.s_addr == 0) - ospf_router_id_update(ospf); - - ospf_opaque_type11_lsa_init(ospf); + ospf_init(ospf); } return ospf; } -struct ospf *ospf_get_instance(unsigned short instance) +struct ospf *ospf_get_instance(unsigned short instance, bool *created) { struct ospf *ospf; ospf = ospf_lookup_instance(instance); + *created = (ospf == NULL); if (ospf == NULL) { ospf = ospf_new(instance, NULL /* VRF_DEFAULT*/); ospf_add(ospf); - if (ospf->router_id_static.s_addr == 0) { - if (vrf_lookup_by_id(ospf->vrf_id)) - ospf_router_id_update(ospf); - else { - if (IS_DEBUG_OSPF_EVENT) - zlog_debug( - "%s: ospf VRF (id %d) is not active yet, skip router id update", - __PRETTY_FUNCTION__, - ospf->vrf_id); - } - ospf_router_id_update(ospf); - } - - ospf_opaque_type11_lsa_init(ospf); + ospf_init(ospf); } return ospf; @@ -455,6 +435,23 @@ struct ospf *ospf_lookup_by_vrf_id(vrf_id_t vrf_id) return (vrf->info) ? (struct ospf *)vrf->info : NULL; } +uint32_t ospf_count_area_params(struct ospf *ospf) +{ + struct vrf *vrf; + struct interface *ifp; + uint32_t count = 0; + + if (ospf->vrf_id != VRF_UNKNOWN) { + vrf = vrf_lookup_by_id(ospf->vrf_id); + + FOR_ALL_INTERFACES (vrf, ifp) { + count += ospf_if_count_area_params(ifp); + } + } + + return count; +} + /* It should only be used when processing incoming info update from zebra. * Other situations, it is not sufficient to lookup the ospf instance by * vrf_name only without using the instance number. @@ -555,9 +552,9 @@ void ospf_terminate(void) SET_FLAG(om->options, OSPF_MASTER_SHUTDOWN); - /* exit immediately if OSPF not actually running */ + /* Skip some steps if OSPF not actually running */ if (listcount(om->ospf) == 0) - exit(0); + goto done; bfd_gbl_exit(); for (ALL_LIST_ELEMENTS(om->ospf, node, nnode, ospf)) @@ -581,6 +578,7 @@ void ospf_terminate(void) zclient_stop(zclient); zclient_free(zclient); +done: frr_fini(); } @@ -599,17 +597,15 @@ void ospf_finish(struct ospf *ospf) /* Final cleanup of ospf instance */ static void ospf_finish_final(struct ospf *ospf) { - struct vrf *vrf = vrf_lookup_by_id(ospf->vrf_id); + struct vrf *vrf; struct route_node *rn; struct ospf_nbr_nbma *nbr_nbma; struct ospf_lsa *lsa; - struct interface *ifp; struct ospf_interface *oi; struct ospf_area *area; struct ospf_vl_data *vl_data; struct listnode *node, *nnode; int i; - unsigned short instance = 0; QOBJ_UNREG(ospf); @@ -628,10 +624,12 @@ static void ospf_finish_final(struct ospf *ospf) if (!red_list) continue; - for (ALL_LIST_ELEMENTS(red_list, node, nnode, red)) + for (ALL_LIST_ELEMENTS(red_list, node, nnode, red)) { ospf_redistribute_unset(ospf, i, red->instance); + ospf_redist_del(ospf, i, red->instance); + } } - ospf_redistribute_default_unset(ospf); + ospf_redistribute_default_set(ospf, DEFAULT_ORIGINATE_NONE, 0, 0); for (ALL_LIST_ELEMENTS(ospf->areas, node, nnode, area)) ospf_remove_vls_through_area(ospf, area); @@ -641,19 +639,11 @@ static void ospf_finish_final(struct ospf *ospf) list_delete(&ospf->vlinks); - /* Remove any ospf interface config params */ - FOR_ALL_INTERFACES (vrf, ifp) { - struct ospf_if_params *params; - - params = IF_DEF_PARAMS(ifp); - if (OSPF_IF_PARAM_CONFIGURED(params, if_area)) - UNSET_IF_PARAM(params, if_area); - } - /* Reset interface. */ for (ALL_LIST_ELEMENTS(ospf->oiflist, node, nnode, oi)) ospf_if_free(oi); list_delete(&ospf->oiflist); + ospf->oi_running = 0; /* De-Register VRF */ ospf_zebra_vrf_deregister(ospf); @@ -697,7 +687,8 @@ static void ospf_finish_final(struct ospf *ospf) } /* Cancel all timers. */ - OSPF_TIMER_OFF(ospf->t_external_lsa); + OSPF_TIMER_OFF(ospf->t_read); + OSPF_TIMER_OFF(ospf->t_write); OSPF_TIMER_OFF(ospf->t_spf_calc); OSPF_TIMER_OFF(ospf->t_ase_calc); OSPF_TIMER_OFF(ospf->t_maxage); @@ -706,13 +697,9 @@ static void ospf_finish_final(struct ospf *ospf) OSPF_TIMER_OFF(ospf->t_asbr_check); OSPF_TIMER_OFF(ospf->t_distribute_update); OSPF_TIMER_OFF(ospf->t_lsa_refresher); - OSPF_TIMER_OFF(ospf->t_read); - OSPF_TIMER_OFF(ospf->t_write); OSPF_TIMER_OFF(ospf->t_opaque_lsa_self); OSPF_TIMER_OFF(ospf->t_sr_update); - - close(ospf->fd); - stream_free(ospf->ibuf); + OSPF_TIMER_OFF(ospf->t_default_routemap_timer); LSDB_LOOP (OPAQUE_AS_LSDB(ospf), rn, lsa) ospf_discard_from_db(ospf, ospf->lsdb, lsa); @@ -753,9 +740,6 @@ static void ospf_finish_final(struct ospf *ospf) ospf_ase_external_lsas_finish(ospf->external_lsas); } - list_delete(&ospf->areas); - list_delete(&ospf->oi_write_q); - for (i = ZEBRA_ROUTE_SYSTEM; i <= ZEBRA_ROUTE_MAX; i++) { struct list *ext_list; struct ospf_external *ext; @@ -764,7 +748,7 @@ static void ospf_finish_final(struct ospf *ospf) if (!ext_list) continue; - for (ALL_LIST_ELEMENTS_RO(ext_list, node, ext)) { + for (ALL_LIST_ELEMENTS(ext_list, node, nnode, ext)) { if (ext->external_info) for (rn = route_top(ext->external_info); rn; rn = route_next(rn)) { @@ -776,15 +760,20 @@ static void ospf_finish_final(struct ospf *ospf) rn->info = NULL; route_unlock_node(rn); } + + ospf_external_del(ospf, i, ext->instance); } } ospf_distance_reset(ospf); route_table_finish(ospf->distance_table); - if (!CHECK_FLAG(om->options, OSPF_MASTER_SHUTDOWN)) - instance = ospf->instance; + list_delete(&ospf->areas); + list_delete(&ospf->oi_write_q); + close(ospf->fd); + stream_free(ospf->ibuf); + ospf->fd = -1; ospf_delete(ospf); if (ospf->name) { @@ -799,9 +788,6 @@ static void ospf_finish_final(struct ospf *ospf) } XFREE(MTYPE_OSPF_TOP, ospf); - - if (!CHECK_FLAG(om->options, OSPF_MASTER_SHUTDOWN)) - ospf_get_instance(instance); } @@ -979,7 +965,8 @@ static void add_ospf_interface(struct connected *co, struct ospf_area *area) * ospf_router_id_update() will call ospf_if_update * whenever r-id is configured instead. */ - if ((area->ospf->router_id.s_addr != 0) && if_is_operative(co->ifp)) + if ((area->ospf->router_id.s_addr != INADDR_ANY) + && if_is_operative(co->ifp)) ospf_if_up(oi); } @@ -1153,32 +1140,6 @@ void ospf_interface_area_unset(struct ospf *ospf, struct interface *ifp) update_redistributed(ospf, 0); /* interfaces possibly removed */ } -bool ospf_interface_area_is_already_set(struct ospf *ospf, - struct interface *ifp) -{ - struct route_node *rn_oi; - - if (!ospf) - return false; /* Ospf not ready yet */ - - /* Find interfaces that may need to be removed. */ - for (rn_oi = route_top(IF_OIFS(ifp)); rn_oi; - rn_oi = route_next(rn_oi)) { - struct ospf_interface *oi = rn_oi->info; - - if (oi == NULL) - continue; - - if (oi->type == OSPF_IFTYPE_VIRTUALLINK) - continue; - /* at least one route covered by interface - * that implies already done - */ - return true; - } - return false; -} - /* Check whether interface matches given network * returns: 1, true. 0, false */ @@ -1308,7 +1269,7 @@ static void ospf_network_run(struct prefix *p, struct ospf_area *area) struct interface *ifp; /* Schedule Router ID Update. */ - if (area->ospf->router_id.s_addr == 0) + if (area->ospf->router_id.s_addr == INADDR_ANY) ospf_router_id_update(area->ospf); /* Get target interface. */ @@ -1348,7 +1309,7 @@ void ospf_if_update(struct ospf *ospf, struct interface *ifp) if (IS_DEBUG_OSPF_EVENT) zlog_debug( "%s: interface %s ifp->vrf_id %u ospf vrf %s vrf_id %u router_id %s", - __PRETTY_FUNCTION__, ifp->name, ifp->vrf_id, + __func__, ifp->name, ifp->vrf_id, ospf_vrf_id_to_name(ospf->vrf_id), ospf->vrf_id, inet_ntoa(ospf->router_id)); @@ -1360,6 +1321,7 @@ void ospf_if_update(struct ospf *ospf, struct interface *ifp) /* Update connected redistribute. */ update_redistributed(ospf, 1); + } void ospf_remove_vls_through_area(struct ospf *ospf, struct ospf_area *area) @@ -1603,7 +1565,8 @@ int ospf_area_nssa_unset(struct ospf *ospf, struct in_addr area_id, int argc) OSPF_NSSA_TRANS_STABLE_DEFAULT; ospf_area_type_set(area, OSPF_AREA_DEFAULT); } else { - area->NSSATranslatorRole = OSPF_NSSA_ROLE_CANDIDATE; + ospf_area_nssa_translator_role_set(ospf, area_id, + OSPF_NSSA_ROLE_CANDIDATE); } ospf_area_check_free(ospf, area_id); @@ -1620,7 +1583,19 @@ int ospf_area_nssa_translator_role_set(struct ospf *ospf, if (area == NULL) return 0; - area->NSSATranslatorRole = role; + if (role != area->NSSATranslatorRole) { + if ((area->NSSATranslatorRole == OSPF_NSSA_ROLE_ALWAYS) + || (role == OSPF_NSSA_ROLE_ALWAYS)) { + /* RFC 3101 3.1 + * if new role is OSPF_NSSA_ROLE_ALWAYS we need to set + * Nt bit, if the role was OSPF_NSSA_ROLE_ALWAYS we need + * to clear Nt bit + */ + area->NSSATranslatorRole = role; + ospf_router_lsa_update_area(area); + } else + area->NSSATranslatorRole = role; + } return 1; } @@ -1826,7 +1801,7 @@ static void ospf_nbr_nbma_add(struct ospf_nbr_nbma *nbr_nbma, p.prefixlen = IPV4_MAX_BITLEN; p.u.prefix4 = nbr_nbma->addr; - rn = route_node_get(oi->nbrs, (struct prefix *)&p); + rn = route_node_get(oi->nbrs, &p); if (rn->info) { nbr = rn->info; nbr->nbr_nbma = nbr_nbma; @@ -2058,8 +2033,8 @@ void ospf_vrf_unlink(struct ospf *ospf, struct vrf *vrf) static int ospf_vrf_new(struct vrf *vrf) { if (IS_DEBUG_OSPF_EVENT) - zlog_debug("%s: VRF Created: %s(%u)", __PRETTY_FUNCTION__, - vrf->name, vrf->vrf_id); + zlog_debug("%s: VRF Created: %s(%u)", __func__, vrf->name, + vrf->vrf_id); return 0; } @@ -2068,8 +2043,8 @@ static int ospf_vrf_new(struct vrf *vrf) static int ospf_vrf_delete(struct vrf *vrf) { if (IS_DEBUG_OSPF_EVENT) - zlog_debug("%s: VRF Deletion: %s(%u)", __PRETTY_FUNCTION__, - vrf->name, vrf->vrf_id); + zlog_debug("%s: VRF Deletion: %s(%u)", __func__, vrf->name, + vrf->vrf_id); return 0; } @@ -2099,8 +2074,8 @@ static int ospf_vrf_enable(struct vrf *vrf) int ret = 0; if (IS_DEBUG_OSPF_EVENT) - zlog_debug("%s: VRF %s id %u enabled", __PRETTY_FUNCTION__, - vrf->name, vrf->vrf_id); + zlog_debug("%s: VRF %s id %u enabled", __func__, vrf->name, + vrf->vrf_id); ospf = ospf_lookup_by_name(vrf->name); if (ospf) { @@ -2114,11 +2089,10 @@ static int ospf_vrf_enable(struct vrf *vrf) if (IS_DEBUG_OSPF_EVENT) zlog_debug( "%s: ospf linked to vrf %s vrf_id %u (old id %u)", - __PRETTY_FUNCTION__, vrf->name, ospf->vrf_id, - old_vrf_id); + __func__, vrf->name, ospf->vrf_id, old_vrf_id); if (old_vrf_id != ospf->vrf_id) { - frr_elevate_privs(&ospfd_privs) { + frr_with_privs(&ospfd_privs) { /* stop zebra redist to us for old vrf */ zclient_send_dereg_requests(zclient, old_vrf_id); @@ -2152,8 +2126,8 @@ static int ospf_vrf_disable(struct vrf *vrf) return 0; if (IS_DEBUG_OSPF_EVENT) - zlog_debug("%s: VRF %s id %d disabled.", __PRETTY_FUNCTION__, - vrf->name, vrf->vrf_id); + zlog_debug("%s: VRF %s id %d disabled.", __func__, vrf->name, + vrf->vrf_id); ospf = ospf_lookup_by_name(vrf->name); if (ospf) { @@ -2165,8 +2139,8 @@ static int ospf_vrf_disable(struct vrf *vrf) ospf_vrf_unlink(ospf, vrf); ospf->oi_running = 0; if (IS_DEBUG_OSPF_EVENT) - zlog_debug("%s: ospf old_vrf_id %d unlinked", - __PRETTY_FUNCTION__, old_vrf_id); + zlog_debug("%s: ospf old_vrf_id %d unlinked", __func__, + old_vrf_id); thread_cancel(ospf->t_read); close(ospf->fd); ospf->fd = -1; @@ -2179,7 +2153,7 @@ static int ospf_vrf_disable(struct vrf *vrf) void ospf_vrf_init(void) { vrf_init(ospf_vrf_new, ospf_vrf_enable, ospf_vrf_disable, - ospf_vrf_delete, NULL); + ospf_vrf_delete, ospf_vrf_enable); } void ospf_vrf_terminate(void) @@ -2193,3 +2167,11 @@ const char *ospf_vrf_id_to_name(vrf_id_t vrf_id) return vrf ? vrf->name : "NIL"; } + +const char *ospf_get_name(const struct ospf *ospf) +{ + if (ospf->name) + return ospf->name; + else + return VRF_DEFAULT_NAME; +} diff --git a/ospfd/ospfd.h b/ospfd/ospfd.h index cbea033b73..4095051f24 100644 --- a/ospfd/ospfd.h +++ b/ospfd/ospfd.h @@ -117,6 +117,14 @@ struct ospf_redist { #define ROUTEMAP(R) (R->route_map.map) }; +/* ospf->config */ +enum { + OSPF_RFC1583_COMPATIBLE = (1 << 0), + OSPF_OPAQUE_CAPABLE = (1 << 2), + OSPF_LOG_ADJACENCY_CHANGES = (1 << 3), + OSPF_LOG_ADJACENCY_DETAIL = (1 << 4), +}; + /* OSPF instance structure. */ struct ospf { /* OSPF's running state based on the '[no] router ospf []' @@ -151,12 +159,8 @@ struct ospf { /* NSSA ABR */ uint8_t anyNSSA; /* Bump for every NSSA attached. */ - /* Configured variables. */ + /* Configuration bitmask, refer to enum above */ uint8_t config; -#define OSPF_RFC1583_COMPATIBLE (1 << 0) -#define OSPF_OPAQUE_CAPABLE (1 << 2) -#define OSPF_LOG_ADJACENCY_CHANGES (1 << 3) -#define OSPF_LOG_ADJACENCY_DETAIL (1 << 4) /* Opaque-LSA administrative flags. */ uint8_t opaque; @@ -202,7 +206,6 @@ struct ospf { struct ospf_lsdb *lsdb; /* Flags. */ - int external_origin; /* AS-external-LSA origin flag. */ int ase_calc; /* ASE calculation flag. */ struct list *opaque_lsa_self; /* Type-11 Opaque-LSAs */ @@ -233,7 +236,6 @@ struct ospf { struct thread *t_distribute_update; /* Distirbute list update timer. */ struct thread *t_spf_calc; /* SPF calculation timer. */ struct thread *t_ase_calc; /* ASE calculation timer. */ - struct thread *t_external_lsa; /* AS-external-LSA origin timer. */ struct thread *t_opaque_lsa_self; /* Type-11 Opaque-LSAs origin event. */ struct thread *t_sr_update; /* Segment Routing update timer */ @@ -247,6 +249,8 @@ struct ospf { struct thread *t_write; #define OSPF_WRITE_INTERFACE_COUNT_DEFAULT 20 + struct thread *t_default_routemap_timer; + int write_oi_count; /* Num of packets sent per thread invocation */ struct thread *t_read; int fd; @@ -297,17 +301,16 @@ struct ospf { /* Statistics for LSA used for new instantiation. */ uint32_t rx_lsa_count; - /* Counter of "ip ospf area x.x.x.x" used - * for multual exclusion of network command under - * router ospf or ip ospf area x under interface. */ - uint32_t if_ospf_cli_count; - struct route_table *distance_table; /* Used during ospf instance going down send LSDB * update to neighbors immediatly */ uint8_t inst_shutdown; + /* Enable or disable sending proactive ARP requests. */ + bool proactive_arp; +#define OSPF_PROACTIVE_ARP_DEFAULT true + /* Redistributed external information. */ struct list *external[ZEBRA_ROUTE_MAX + 1]; #define EXTERNAL_INFO(E) (E->external_info) @@ -406,6 +409,12 @@ struct ospf_area { /* Shortest Path Tree. */ struct vertex *spf; + struct list *spf_vertex_list; + + bool spf_dry_run; /* flag for checking if the SPF calculation is + intended for the local RIB */ + bool spf_root_node; /* flag for checking if the calculating node is the + root node of the SPF tree */ /* Threads. */ struct thread *t_stub_router; /* Stub-router timer */ @@ -502,11 +511,13 @@ extern struct zebra_privs_t ospfd_privs; /* Prototypes. */ extern const char *ospf_redist_string(unsigned int route_type); extern struct ospf *ospf_lookup_instance(unsigned short); -extern struct ospf *ospf_get(unsigned short instance, const char *name); -extern struct ospf *ospf_get_instance(unsigned short); +extern struct ospf *ospf_get(unsigned short instance, const char *name, + bool *created); +extern struct ospf *ospf_get_instance(unsigned short, bool *created); extern struct ospf *ospf_lookup_by_inst_name(unsigned short instance, const char *name); extern struct ospf *ospf_lookup_by_vrf_id(vrf_id_t vrf_id); +extern uint32_t ospf_count_area_params(struct ospf *ospf); extern void ospf_finish(struct ospf *); extern void ospf_router_id_update(struct ospf *ospf); extern int ospf_network_set(struct ospf *, struct prefix_ipv4 *, struct in_addr, @@ -541,7 +552,6 @@ extern int ospf_nbr_nbma_poll_interval_set(struct ospf *, struct in_addr, unsigned int); extern int ospf_nbr_nbma_poll_interval_unset(struct ospf *, struct in_addr); extern void ospf_prefix_list_update(struct prefix_list *); -extern void ospf_init(void); extern void ospf_if_update(struct ospf *, struct interface *); extern void ospf_ls_upd_queue_empty(struct ospf_interface *); extern void ospf_terminate(void); @@ -561,8 +571,6 @@ extern void ospf_area_del_if(struct ospf_area *, struct ospf_interface *); extern void ospf_interface_area_set(struct ospf *, struct interface *); extern void ospf_interface_area_unset(struct ospf *, struct interface *); -extern bool ospf_interface_area_is_already_set(struct ospf *ospf, - struct interface *ifp); extern void ospf_route_map_init(void); @@ -574,4 +582,5 @@ extern void ospf_vrf_unlink(struct ospf *ospf, struct vrf *vrf); const char *ospf_vrf_id_to_name(vrf_id_t vrf_id); int ospf_area_nssa_no_summary_set(struct ospf *, struct in_addr); +const char *ospf_get_name(const struct ospf *ospf); #endif /* _ZEBRA_OSPFD_H */ diff --git a/ospfd/subdir.am b/ospfd/subdir.am index 48dd741b24..447ddf9cbb 100644 --- a/ospfd/subdir.am +++ b/ospfd/subdir.am @@ -7,19 +7,19 @@ noinst_LIBRARIES += ospfd/libfrrospf.a sbin_PROGRAMS += ospfd/ospfd dist_examples_DATA += ospfd/ospfd.conf.sample vtysh_scan += \ - $(top_srcdir)/ospfd/ospf_bfd.c \ - $(top_srcdir)/ospfd/ospf_dump.c \ - $(top_srcdir)/ospfd/ospf_opaque.c \ - $(top_srcdir)/ospfd/ospf_ri.c \ - $(top_srcdir)/ospfd/ospf_routemap.c \ - $(top_srcdir)/ospfd/ospf_te.c \ - $(top_srcdir)/ospfd/ospf_sr.c \ - $(top_srcdir)/ospfd/ospf_vty.c \ + ospfd/ospf_bfd.c \ + ospfd/ospf_dump.c \ + ospfd/ospf_opaque.c \ + ospfd/ospf_ri.c \ + ospfd/ospf_routemap.c \ + ospfd/ospf_te.c \ + ospfd/ospf_sr.c \ + ospfd/ospf_vty.c \ # end if SNMP module_LTLIBRARIES += ospfd/ospfd_snmp.la endif -man8 += $(MANBUILD)/ospfd.8 +man8 += $(MANBUILD)/frr-ospfd.8 endif ospfd_libfrrospf_a_SOURCES = \ @@ -72,8 +72,9 @@ ospfdheader_HEADERS = \ # end endif -ospfd/ospf_vty_clippy.c: $(CLIPPY_DEPS) -ospfd/ospf_vty.$(OBJEXT): ospfd/ospf_vty_clippy.c +clippy_scan += \ + ospfd/ospf_vty.c \ + # end noinst_HEADERS += \ ospfd/ospf_abr.h \ diff --git a/pbrd/pbr_main.c b/pbrd/pbr_main.c index 246d836acf..9a9edd79c6 100644 --- a/pbrd/pbr_main.c +++ b/pbrd/pbr_main.c @@ -48,6 +48,7 @@ #include "pbr_zebra.h" #include "pbr_vty.h" #include "pbr_debug.h" +#include "pbr_vrf.h" zebra_capabilities_t _caps_p[] = { ZCAP_NET_RAW, ZCAP_BIND, ZCAP_NET_ADMIN, @@ -81,6 +82,8 @@ static void sigint(void) { zlog_notice("Terminating on signal"); + frr_fini(); + exit(0); } @@ -111,7 +114,8 @@ struct quagga_signal_t pbr_signals[] = { #define PBR_VTY_PORT 2615 -static const struct frr_yang_module_info *pbrd_yang_modules[] = { +static const struct frr_yang_module_info *const pbrd_yang_modules[] = { + &frr_filter_info, &frr_interface_info, }; @@ -153,7 +157,6 @@ int main(int argc, char **argv, char **envp) pbr_debug_init(); - vrf_init(NULL, NULL, NULL, NULL, NULL); nexthop_group_init(pbr_nhgroup_add_cb, pbr_nhgroup_add_nexthop_cb, pbr_nhgroup_del_nexthop_cb, @@ -166,7 +169,10 @@ int main(int argc, char **argv, char **envp) access_list_init(); pbr_nht_init(); pbr_map_init(); + if_zapi_callbacks(pbr_ifp_create, pbr_ifp_up, + pbr_ifp_down, pbr_ifp_destroy); pbr_zebra_init(); + pbr_vrf_init(); pbr_vty_init(); frr_config_fork(); diff --git a/pbrd/pbr_map.c b/pbrd/pbr_map.c index 5e67990d5e..01caff5b52 100644 --- a/pbrd/pbr_map.c +++ b/pbrd/pbr_map.c @@ -35,6 +35,7 @@ #include "pbr_zebra.h" #include "pbr_memory.h" #include "pbr_debug.h" +#include "pbr_vrf.h" DEFINE_MTYPE_STATIC(PBRD, PBR_MAP, "PBR Map") DEFINE_MTYPE_STATIC(PBRD, PBR_MAP_SEQNO, "PBR Map Sequence") @@ -42,6 +43,7 @@ DEFINE_MTYPE_STATIC(PBRD, PBR_MAP_INTERFACE, "PBR Map Interface") static uint32_t pbr_map_sequence_unique; +static bool pbr_map_check_valid_internal(struct pbr_map *pbrm); static inline int pbr_map_compare(const struct pbr_map *pbrmap1, const struct pbr_map *pbrmap2); @@ -73,6 +75,7 @@ static void pbr_map_sequence_delete(struct pbr_map_sequence *pbrms) { XFREE(MTYPE_TMP, pbrms->internal_nhg_name); + QOBJ_UNREG(pbrms); XFREE(MTYPE_PBR_MAP_SEQNO, pbrms); } @@ -98,9 +101,86 @@ static void pbr_map_interface_list_delete(struct pbr_map_interface *pmi) } } -static const char *pbr_map_reason_str[] = { +static bool pbrms_is_installed(const struct pbr_map_sequence *pbrms, + const struct pbr_map_interface *pmi) +{ + uint64_t is_installed = (uint64_t)1 << pmi->install_bit; + + is_installed &= pbrms->installed; + + if (is_installed) + return true; + + return false; +} + +/* If any sequence is installed on the interface, assume installed */ +static bool +pbr_map_interface_is_installed(const struct pbr_map *pbrm, + const struct pbr_map_interface *check_pmi) +{ + + struct pbr_map_sequence *pbrms; + struct pbr_map_interface *pmi; + struct listnode *node, *inode; + + for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) + for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, inode, pmi)) + if (pmi == check_pmi && pbrms_is_installed(pbrms, pmi)) + return true; + + return false; +} + +static bool pbr_map_interface_is_valid(const struct pbr_map_interface *pmi) +{ + /* Don't install rules without a real ifindex on the incoming interface. + * + * This can happen when we have config for an interface that does not + * exist or when an interface is changing vrfs. + */ + if (pmi->ifp && pmi->ifp->ifindex != IFINDEX_INTERNAL) + return true; + + return false; +} + +static void pbr_map_pbrms_update_common(struct pbr_map_sequence *pbrms, + bool install, bool changed) +{ + struct pbr_map *pbrm; + struct listnode *node; + struct pbr_map_interface *pmi; + + pbrm = pbrms->parent; + + if (pbrms->nhs_installed && pbrm->incoming->count) { + for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, node, pmi)) { + if (!pmi->ifp) + continue; + + if (install && !pbr_map_interface_is_valid(pmi)) + continue; + + pbr_send_pbr_map(pbrms, pmi, install, changed); + } + } +} + +static void pbr_map_pbrms_install(struct pbr_map_sequence *pbrms, bool changed) +{ + pbr_map_pbrms_update_common(pbrms, true, changed); +} + +static void pbr_map_pbrms_uninstall(struct pbr_map_sequence *pbrms) +{ + pbr_map_pbrms_update_common(pbrms, false, false); +} + +static const char *const pbr_map_reason_str[] = { "Invalid NH-group", "Invalid NH", "No Nexthops", - "Both NH and NH-Group", "Invalid Src or Dst", "Deleting Sequence", + "Both NH and NH-Group", "Invalid Src or Dst", "Invalid VRF", + "Deleting Sequence", }; void pbr_map_reason_string(unsigned int reason, char *buf, int size) @@ -123,7 +203,7 @@ void pbr_map_reason_string(unsigned int reason, char *buf, int size) void pbr_map_final_interface_deletion(struct pbr_map *pbrm, struct pbr_map_interface *pmi) { - if (pmi->delete == true) { + if (pmi->delete && !pbr_map_interface_is_installed(pbrm, pmi)) { listnode_delete(pbrm->incoming, pmi); pmi->pbrm = NULL; @@ -168,6 +248,92 @@ void pbr_map_add_interface(struct pbr_map *pbrm, struct interface *ifp_add) pbr_map_install(pbrm); } +static int +pbr_map_policy_interface_update_common(const struct interface *ifp, + struct pbr_interface **pbr_ifp, + struct pbr_map **pbrm) +{ + if (!ifp->info) { + DEBUGD(&pbr_dbg_map, "%s: %s has no pbr_interface info", + __func__, ifp->name); + return -1; + } + + *pbr_ifp = ifp->info; + + *pbrm = pbrm_find((*pbr_ifp)->mapname); + + if (!*pbrm) { + DEBUGD(&pbr_dbg_map, "%s: applied PBR-MAP(%s) does not exist?", + __func__, (*pbr_ifp)->mapname); + return -1; + } + + return 0; +} + +void pbr_map_policy_interface_update(const struct interface *ifp, bool state_up) +{ + struct pbr_interface *pbr_ifp; + struct pbr_map_sequence *pbrms; + struct pbr_map *pbrm; + struct listnode *node, *inode; + struct pbr_map_interface *pmi; + + if (pbr_map_policy_interface_update_common(ifp, &pbr_ifp, &pbrm)) + return; + + DEBUGD(&pbr_dbg_map, "%s: %s %s rules on interface %s", __func__, + pbr_ifp->mapname, (state_up ? "installing" : "removing"), + ifp->name); + + /* + * Walk the list and install/remove maps on the interface. + */ + for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) + for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, inode, pmi)) + if (pmi->ifp == ifp && pbr_map_interface_is_valid(pmi)) + pbr_send_pbr_map(pbrms, pmi, state_up, true); +} + +static void pbrms_vrf_update(struct pbr_map_sequence *pbrms, + const struct pbr_vrf *pbr_vrf) +{ + const char *vrf_name = pbr_vrf_name(pbr_vrf); + + if (pbrms->vrf_lookup + && (strncmp(vrf_name, pbrms->vrf_name, sizeof(pbrms->vrf_name)) + == 0)) { + DEBUGD(&pbr_dbg_map, "\tSeq %u uses vrf %s (%u), updating map", + pbrms->seqno, vrf_name, pbr_vrf_id(pbr_vrf)); + + pbr_map_check(pbrms, false); + } +} + +/* Vrf enabled/disabled */ +void pbr_map_vrf_update(const struct pbr_vrf *pbr_vrf) +{ + struct pbr_map *pbrm; + struct pbr_map_sequence *pbrms; + struct listnode *node; + + if (!pbr_vrf) + return; + + bool enabled = pbr_vrf_is_enabled(pbr_vrf); + + DEBUGD(&pbr_dbg_map, "%s: %s (%u) %s, updating pbr maps", __func__, + pbr_vrf_name(pbr_vrf), pbr_vrf_id(pbr_vrf), + enabled ? "enabled" : "disabled"); + + RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps) { + DEBUGD(&pbr_dbg_map, "%s: Looking at %s", __func__, pbrm->name); + for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) + pbrms_vrf_update(pbrms, pbr_vrf); + } +} + void pbr_map_write_interfaces(struct vty *vty, struct interface *ifp) { struct pbr_interface *pbr_ifp = ifp->info; @@ -195,7 +361,7 @@ extern void pbr_map_delete(struct pbr_map_sequence *pbrms) pbrm = pbrms->parent; for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, inode, pmi)) - pbr_send_pbr_map(pbrms, pmi, false); + pbr_send_pbr_map(pbrms, pmi, false, false); if (pbrms->nhg) pbr_nht_delete_individual_nexthop(pbrms); @@ -210,24 +376,29 @@ extern void pbr_map_delete(struct pbr_map_sequence *pbrms) } } -void pbr_map_delete_nexthop_group(struct pbr_map_sequence *pbrms) +static void pbr_map_delete_common(struct pbr_map_sequence *pbrms) { struct pbr_map *pbrm = pbrms->parent; - struct listnode *node; - struct pbr_map_interface *pmi; - if (pbrm->valid && pbrms->nhs_installed && pbrm->incoming->count) { - for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, node, pmi)) - pbr_send_pbr_map(pbrms, pmi, false); - } + pbr_map_pbrms_uninstall(pbrms); pbrm->valid = false; pbrms->nhs_installed = false; pbrms->reason |= PBR_MAP_INVALID_NO_NEXTHOPS; - pbrms->nhgrp_name = NULL; + XFREE(MTYPE_TMP, pbrms->nhgrp_name); } -struct pbr_map_sequence *pbrms_lookup_unique(uint32_t unique, ifindex_t ifindex, +void pbr_map_delete_nexthops(struct pbr_map_sequence *pbrms) +{ + pbr_map_delete_common(pbrms); +} + +void pbr_map_delete_vrf(struct pbr_map_sequence *pbrms) +{ + pbr_map_delete_common(pbrms); +} + +struct pbr_map_sequence *pbrms_lookup_unique(uint32_t unique, char *ifname, struct pbr_map_interface **ppmi) { struct pbr_map_sequence *pbrms; @@ -237,7 +408,8 @@ struct pbr_map_sequence *pbrms_lookup_unique(uint32_t unique, ifindex_t ifindex, RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps) { for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, inode, pmi)) { - if (pmi->ifp->ifindex != ifindex) + if (strncmp(pmi->ifp->name, ifname, INTERFACE_NAMSIZ) + != 0) continue; if (ppmi) @@ -246,8 +418,7 @@ struct pbr_map_sequence *pbrms_lookup_unique(uint32_t unique, ifindex_t ifindex, for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, snode, pbrms)) { DEBUGD(&pbr_dbg_map, "%s: Comparing %u to %u", - __PRETTY_FUNCTION__, pbrms->unique, - unique); + __func__, pbrms->unique, unique); if (pbrms->unique == unique) return pbrms; } @@ -274,6 +445,59 @@ static void pbr_map_add_interfaces(struct pbr_map *pbrm) } } +/* Decodes a standardized DSCP into its representative value */ +uint8_t pbr_map_decode_dscp_enum(const char *name) +{ + /* Standard Differentiated Services Field Codepoints */ + if (!strcmp(name, "cs0")) + return 0; + if (!strcmp(name, "cs1")) + return 8; + if (!strcmp(name, "cs2")) + return 16; + if (!strcmp(name, "cs3")) + return 24; + if (!strcmp(name, "cs4")) + return 32; + if (!strcmp(name, "cs5")) + return 40; + if (!strcmp(name, "cs6")) + return 48; + if (!strcmp(name, "cs7")) + return 56; + if (!strcmp(name, "af11")) + return 10; + if (!strcmp(name, "af12")) + return 12; + if (!strcmp(name, "af13")) + return 14; + if (!strcmp(name, "af21")) + return 18; + if (!strcmp(name, "af22")) + return 20; + if (!strcmp(name, "af23")) + return 22; + if (!strcmp(name, "af31")) + return 26; + if (!strcmp(name, "af32")) + return 28; + if (!strcmp(name, "af33")) + return 30; + if (!strcmp(name, "af41")) + return 34; + if (!strcmp(name, "af42")) + return 36; + if (!strcmp(name, "af43")) + return 38; + if (!strcmp(name, "ef")) + return 46; + if (!strcmp(name, "voice-admit")) + return 44; + + /* No match? Error out */ + return -1; +} + struct pbr_map_sequence *pbrms_get(const char *name, uint32_t seqno) { struct pbr_map *pbrm; @@ -316,8 +540,9 @@ struct pbr_map_sequence *pbrms_get(const char *name, uint32_t seqno) pbrms->ruleno = pbr_nht_get_next_rule(seqno); pbrms->parent = pbrm; pbrms->reason = - PBR_MAP_INVALID_SRCDST | + PBR_MAP_INVALID_EMPTY | PBR_MAP_INVALID_NO_NEXTHOPS; + pbrms->vrf_name[0] = '\0'; QOBJ_REG(pbrms, pbr_map_sequence); listnode_add_sort(pbrm->seqnumbers, pbrms); @@ -329,12 +554,36 @@ struct pbr_map_sequence *pbrms_get(const char *name, uint32_t seqno) static void pbr_map_sequence_check_nexthops_valid(struct pbr_map_sequence *pbrms) { + /* Check if any are present first */ + if (!pbrms->vrf_unchanged && !pbrms->vrf_lookup && !pbrms->nhg + && !pbrms->nhgrp_name) { + pbrms->reason |= PBR_MAP_INVALID_NO_NEXTHOPS; + return; + } + + /* + * Check validness of vrf. + */ + + /* This one can be considered always valid */ + if (pbrms->vrf_unchanged) + pbrms->nhs_installed = true; + + if (pbrms->vrf_lookup) { + struct pbr_vrf *pbr_vrf = + pbr_vrf_lookup_by_name(pbrms->vrf_name); + + if (pbr_vrf && pbr_vrf_is_valid(pbr_vrf)) + pbrms->nhs_installed = true; + else + pbrms->reason |= PBR_MAP_INVALID_VRF; + } + /* * Check validness of the nexthop or nexthop-group */ - if (!pbrms->nhg && !pbrms->nhgrp_name) - pbrms->reason |= PBR_MAP_INVALID_NO_NEXTHOPS; + /* Only nexthop or nexthop group allowed */ if (pbrms->nhg && pbrms->nhgrp_name) pbrms->reason |= PBR_MAP_INVALID_BOTH_NHANDGRP; @@ -350,10 +599,10 @@ pbr_map_sequence_check_nexthops_valid(struct pbr_map_sequence *pbrms) } } -static void pbr_map_sequence_check_src_dst_valid(struct pbr_map_sequence *pbrms) +static void pbr_map_sequence_check_not_empty(struct pbr_map_sequence *pbrms) { - if (!pbrms->src && !pbrms->dst) - pbrms->reason |= PBR_MAP_INVALID_SRCDST; + if (!pbrms->src && !pbrms->dst && !pbrms->mark && !pbrms->dsfield) + pbrms->reason |= PBR_MAP_INVALID_EMPTY; } /* @@ -364,7 +613,7 @@ static void pbr_map_sequence_check_valid(struct pbr_map_sequence *pbrms) { pbr_map_sequence_check_nexthops_valid(pbrms); - pbr_map_sequence_check_src_dst_valid(pbrms); + pbr_map_sequence_check_not_empty(pbrms); } static bool pbr_map_check_valid_internal(struct pbr_map *pbrm) @@ -399,8 +648,8 @@ bool pbr_map_check_valid(const char *name) pbrm = pbrm_find(name); if (!pbrm) { DEBUGD(&pbr_dbg_map, - "%s: Specified PBR-MAP(%s) does not exist?", - __PRETTY_FUNCTION__, name); + "%s: Specified PBR-MAP(%s) does not exist?", __func__, + name); return false; } @@ -408,15 +657,14 @@ bool pbr_map_check_valid(const char *name) return pbrm->valid; } -void pbr_map_schedule_policy_from_nhg(const char *nh_group) +void pbr_map_schedule_policy_from_nhg(const char *nh_group, bool installed) { struct pbr_map_sequence *pbrms; struct pbr_map *pbrm; struct listnode *node; RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps) { - DEBUGD(&pbr_dbg_map, "%s: Looking at %s", __PRETTY_FUNCTION__, - pbrm->name); + DEBUGD(&pbr_dbg_map, "%s: Looking at %s", __func__, pbrm->name); for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) { DEBUGD(&pbr_dbg_map, "\tNH Grp name: %s", pbrms->nhgrp_name ? @@ -424,17 +672,17 @@ void pbr_map_schedule_policy_from_nhg(const char *nh_group) if (pbrms->nhgrp_name && (strcmp(nh_group, pbrms->nhgrp_name) == 0)) { - pbrms->nhs_installed = true; + pbrms->nhs_installed = installed; - pbr_map_check(pbrms); + pbr_map_check(pbrms, false); } if (pbrms->nhg && (strcmp(nh_group, pbrms->internal_nhg_name) == 0)) { - pbrms->nhs_installed = true; + pbrms->nhs_installed = installed; - pbr_map_check(pbrms); + pbr_map_check(pbrms, false); } } } @@ -447,22 +695,24 @@ void pbr_map_policy_install(const char *name) struct listnode *node, *inode; struct pbr_map_interface *pmi; - DEBUGD(&pbr_dbg_map, "%s: for %s", __PRETTY_FUNCTION__, name); + DEBUGD(&pbr_dbg_map, "%s: for %s", __func__, name); pbrm = pbrm_find(name); if (!pbrm) return; for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) { DEBUGD(&pbr_dbg_map, - "%s: Looking at what to install %s(%u) %d %d", - __PRETTY_FUNCTION__, name, pbrms->seqno, pbrm->valid, - pbrms->nhs_installed); + "%s: Looking at what to install %s(%u) %d %d", __func__, + name, pbrms->seqno, pbrm->valid, pbrms->nhs_installed); - if (pbrm->valid && pbrms->nhs_installed && pbrm->incoming->count) { - DEBUGD(&pbr_dbg_map, "\tInstalling %s %u", - pbrm->name, pbrms->seqno); + if (pbrm->valid && pbrms->nhs_installed + && pbrm->incoming->count) { + DEBUGD(&pbr_dbg_map, "\tInstalling %s %u", pbrm->name, + pbrms->seqno); for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, inode, pmi)) - pbr_send_pbr_map(pbrms, pmi, true); + if (pbr_map_interface_is_valid(pmi)) + pbr_send_pbr_map(pbrms, pmi, true, + false); } } } @@ -471,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); + 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); } /* @@ -516,22 +777,81 @@ void pbr_map_check_nh_group_change(const char *nh_group) pbrm->incoming, inode, pmi)) pbr_send_pbr_map(pbrms, pmi, - false); + false, false); } } } } -void pbr_map_check(struct pbr_map_sequence *pbrms) +void pbr_map_check_vrf_nh_group_change(const char *nh_group, + struct pbr_vrf *pbr_vrf, + uint32_t old_vrf_id) +{ + struct pbr_map *pbrm; + struct pbr_map_sequence *pbrms; + struct listnode *node; + + + RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps) { + for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) { + if (pbrms->nhgrp_name) + continue; + + if (pbrms->nhg == NULL) + continue; + + if (strcmp(nh_group, pbrms->internal_nhg_name)) + continue; + + if (pbrms->nhg->nexthop == NULL) + continue; + + if (pbrms->nhg->nexthop->vrf_id != old_vrf_id) + continue; + + pbrms->nhg->nexthop->vrf_id = pbr_vrf_id(pbr_vrf); + } + } +} + +void pbr_map_check_interface_nh_group_change(const char *nh_group, + struct interface *ifp, + ifindex_t oldifindex) +{ + struct pbr_map *pbrm; + struct pbr_map_sequence *pbrms; + struct listnode *node; + + RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps) { + for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) { + if (pbrms->nhgrp_name) + continue; + + if (pbrms->nhg == NULL) + continue; + + if (strcmp(nh_group, pbrms->internal_nhg_name)) + continue; + + if (pbrms->nhg->nexthop == NULL) + continue; + + if (pbrms->nhg->nexthop->ifindex != oldifindex) + continue; + + pbrms->nhg->nexthop->ifindex = ifp->ifindex; + } + } +} + +void pbr_map_check(struct pbr_map_sequence *pbrms, bool changed) { struct pbr_map *pbrm; - struct listnode *inode; - struct pbr_map_interface *pmi; bool install; pbrm = pbrms->parent; - DEBUGD(&pbr_dbg_map, "%s: for %s(%u)", __PRETTY_FUNCTION__, - pbrm->name, pbrms->seqno); + DEBUGD(&pbr_dbg_map, "%s: for %s(%u)", __func__, pbrm->name, + pbrms->seqno); if (pbr_map_check_valid(pbrm->name)) DEBUGD(&pbr_dbg_map, "We are totally valid %s", pbrm->name); @@ -539,35 +859,31 @@ void pbr_map_check(struct pbr_map_sequence *pbrms) if (pbrms->reason == PBR_MAP_VALID_SEQUENCE_NUMBER) { install = true; DEBUGD(&pbr_dbg_map, "%s: Installing %s(%u) reason: %" PRIu64, - __PRETTY_FUNCTION__, pbrm->name, pbrms->seqno, - pbrms->reason); + __func__, pbrm->name, pbrms->seqno, pbrms->reason); DEBUGD(&pbr_dbg_map, "\tSending PBR_MAP_POLICY_INSTALL event"); } else { install = false; - DEBUGD(&pbr_dbg_map, - "%s: Removing %s(%u) reason: %" PRIu64, - __PRETTY_FUNCTION__, pbrm->name, - pbrms->seqno, pbrms->reason); + DEBUGD(&pbr_dbg_map, "%s: Removing %s(%u) reason: %" PRIu64, + __func__, pbrm->name, pbrms->seqno, pbrms->reason); } - for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, inode, pmi)) { - pbr_send_pbr_map(pbrms, pmi, install); - } + if (install) + pbr_map_pbrms_install(pbrms, changed); + else + pbr_map_pbrms_uninstall(pbrms); } void pbr_map_install(struct pbr_map *pbrm) { - struct listnode *node, *inode; struct pbr_map_sequence *pbrms; - struct pbr_map_interface *pmi; + struct listnode *node; if (!pbrm->incoming->count) return; for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) - for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, inode, pmi)) - pbr_send_pbr_map(pbrms, pmi, true); + pbr_map_pbrms_install(pbrms, false); } void pbr_map_init(void) diff --git a/pbrd/pbr_map.h b/pbrd/pbr_map.h index 945f76bb2b..ad2db146b7 100644 --- a/pbrd/pbr_map.h +++ b/pbrd/pbr_map.h @@ -22,6 +22,8 @@ #include +#include "pbr_vrf.h" + struct pbr_map { /* * RB Tree of the pbr_maps @@ -87,12 +89,29 @@ struct pbr_map_sequence { */ struct prefix *src; struct prefix *dst; + uint8_t dsfield; + uint32_t mark; /* * Family of the src/dst. Needed when deleting since we clear them */ unsigned char family; + /* + * Use interface's vrf. + */ + bool vrf_unchanged; + + /* + * The vrf to lookup in was directly configured. + */ + bool vrf_lookup; + + /* + * VRF to lookup. + */ + char vrf_name[VRF_NAMSIZ + 1]; + /* * The nexthop group we auto create * for when the user specifies a individual @@ -121,12 +140,13 @@ struct pbr_map_sequence { * A reason of 0 means we think the pbr_map_sequence is good to go * We can accumuluate multiple failure states */ -#define PBR_MAP_VALID_SEQUENCE_NUMBER 0 -#define PBR_MAP_INVALID_NEXTHOP_GROUP (1 << 0) -#define PBR_MAP_INVALID_NEXTHOP (1 << 1) -#define PBR_MAP_INVALID_NO_NEXTHOPS (1 << 2) -#define PBR_MAP_INVALID_BOTH_NHANDGRP (1 << 3) -#define PBR_MAP_INVALID_SRCDST (1 << 4) +#define PBR_MAP_VALID_SEQUENCE_NUMBER 0 +#define PBR_MAP_INVALID_NEXTHOP_GROUP (1 << 0) +#define PBR_MAP_INVALID_NEXTHOP (1 << 1) +#define PBR_MAP_INVALID_NO_NEXTHOPS (1 << 2) +#define PBR_MAP_INVALID_BOTH_NHANDGRP (1 << 3) +#define PBR_MAP_INVALID_EMPTY (1 << 4) +#define PBR_MAP_INVALID_VRF (1 << 5) uint64_t reason; QOBJ_FIELDS @@ -138,31 +158,58 @@ extern struct pbr_map_entry_head pbr_maps; extern struct pbr_map_sequence *pbrms_get(const char *name, uint32_t seqno); extern struct pbr_map_sequence * -pbrms_lookup_unique(uint32_t unique, ifindex_t ifindex, +pbrms_lookup_unique(uint32_t unique, char *ifname, struct pbr_map_interface **ppmi); extern struct pbr_map *pbrm_find(const char *name); extern void pbr_map_delete(struct pbr_map_sequence *pbrms); -extern void pbr_map_delete_nexthop_group(struct pbr_map_sequence *pbrms); +extern void pbr_map_delete_nexthops(struct pbr_map_sequence *pbrms); +extern void pbr_map_delete_vrf(struct pbr_map_sequence *pbrms); extern void pbr_map_add_interface(struct pbr_map *pbrm, struct interface *ifp); extern void pbr_map_interface_delete(struct pbr_map *pbrm, struct interface *ifp); + +extern uint8_t pbr_map_decode_dscp_enum(const char *name); + +/* Update maps installed on interface */ +extern void pbr_map_policy_interface_update(const struct interface *ifp, + bool state_up); + extern void pbr_map_final_interface_deletion(struct pbr_map *pbrm, struct pbr_map_interface *pmi); + +extern void pbr_map_vrf_update(const struct pbr_vrf *pbr_vrf); + extern void pbr_map_write_interfaces(struct vty *vty, struct interface *ifp); extern void pbr_map_init(void); extern bool pbr_map_check_valid(const char *name); -extern void pbr_map_check(struct pbr_map_sequence *pbrms); +/** + * Re-check the pbr map for validity. + * + * Install if valid, remove if not. + * + * If changed is set, the config on the on the map has changed somewhere + * and the rules need to be replaced if valid. + */ +extern void pbr_map_check(struct pbr_map_sequence *pbrms, bool changed); extern void pbr_map_check_nh_group_change(const char *nh_group); extern void pbr_map_reason_string(unsigned int reason, char *buf, int size); -extern void pbr_map_schedule_policy_from_nhg(const char *nh_group); +extern void pbr_map_schedule_policy_from_nhg(const char *nh_group, + bool installed); extern void pbr_map_install(struct pbr_map *pbrm); extern void pbr_map_policy_install(const char *name); extern void pbr_map_policy_delete(struct pbr_map *pbrm, struct pbr_map_interface *pmi); + +extern void pbr_map_check_vrf_nh_group_change(const char *nh_group, + struct pbr_vrf *pbr_vrf, + uint32_t old_vrf_id); +extern void pbr_map_check_interface_nh_group_change(const char *nh_group, + struct interface *ifp, + ifindex_t oldifindex); #endif diff --git a/pbrd/pbr_nht.c b/pbrd/pbr_nht.c index 7504752725..3fb3759049 100644 --- a/pbrd/pbr_nht.c +++ b/pbrd/pbr_nht.c @@ -21,7 +21,8 @@ #include #include -#include +#include "nexthop_group.h" +#include "nexthop_group_private.h" #include #include #include @@ -36,7 +37,7 @@ DEFINE_MTYPE_STATIC(PBRD, PBR_NHG, "PBR Nexthop Groups") -static struct hash *pbr_nhg_hash; +struct hash *pbr_nhg_hash; static struct hash *pbr_nhrc_hash; static uint32_t pbr_nhg_low_table; @@ -66,6 +67,8 @@ static void *pbr_nhrc_hash_alloc(void *p) { struct nhrc *nhrc = XCALLOC(MTYPE_PBR_NHG, sizeof(struct nhrc)); nhrc->nexthop = *(struct nexthop *)p; + nhrc->nexthop.next = NULL; + nhrc->nexthop.prev = NULL; return nhrc; } @@ -88,16 +91,15 @@ static void *pbr_nh_alloc(void *p) struct nhrc *nhrc; new = XCALLOC(MTYPE_PBR_NHG, sizeof(*new)); - nhrc = hash_get(pbr_nhrc_hash, pnhc->nexthop, pbr_nhrc_hash_alloc); - new->nexthop = &nhrc->nexthop; + nhrc = hash_get(pbr_nhrc_hash, &pnhc->nexthop, pbr_nhrc_hash_alloc); + new->nexthop = nhrc->nexthop; /* Decremented again in pbr_nh_delete */ ++nhrc->refcount; - DEBUGD(&pbr_dbg_nht, "%s: Sending nexthop to Zebra", - __PRETTY_FUNCTION__); + DEBUGD(&pbr_dbg_nht, "%s: Sending nexthop to Zebra", __func__); - pbr_send_rnh(new->nexthop, true); + pbr_send_rnh(&new->nexthop, true); new->valid = false; return new; @@ -107,14 +109,14 @@ static void pbr_nh_delete(struct pbr_nexthop_cache **pnhc) { struct nhrc *nhrc; - nhrc = hash_lookup(pbr_nhrc_hash, (*pnhc)->nexthop); + nhrc = hash_lookup(pbr_nhrc_hash, &((*pnhc)->nexthop)); if (nhrc) --nhrc->refcount; if (!nhrc || nhrc->refcount == 0) { DEBUGD(&pbr_dbg_nht, "%s: Removing nexthop from Zebra", - __PRETTY_FUNCTION__); - pbr_send_rnh((*pnhc)->nexthop, false); + __func__); + pbr_send_rnh(&((*pnhc)->nexthop), false); } if (nhrc && nhrc->refcount == 0) { hash_release(pbr_nhrc_hash, nhrc); @@ -129,12 +131,12 @@ static void pbr_nh_delete_iterate(struct hash_bucket *b, void *p) pbr_nh_delete((struct pbr_nexthop_cache **)&b->data); } -static uint32_t pbr_nh_hash_key(void *arg) +static uint32_t pbr_nh_hash_key(const void *arg) { uint32_t key; - struct pbr_nexthop_cache *pbrnc = (struct pbr_nexthop_cache *)arg; + const struct pbr_nexthop_cache *pbrnc = arg; - key = nexthop_hash(pbrnc->nexthop); + key = nexthop_hash(&pbrnc->nexthop); return key; } @@ -146,28 +148,28 @@ static bool pbr_nh_hash_equal(const void *arg1, const void *arg2) const struct pbr_nexthop_cache *pbrnc2 = (const struct pbr_nexthop_cache *)arg2; - if (pbrnc1->nexthop->vrf_id != pbrnc2->nexthop->vrf_id) + if (pbrnc1->nexthop.vrf_id != pbrnc2->nexthop.vrf_id) return false; - if (pbrnc1->nexthop->ifindex != pbrnc2->nexthop->ifindex) + if (pbrnc1->nexthop.ifindex != pbrnc2->nexthop.ifindex) return false; - if (pbrnc1->nexthop->type != pbrnc2->nexthop->type) + if (pbrnc1->nexthop.type != pbrnc2->nexthop.type) return false; - switch (pbrnc1->nexthop->type) { + switch (pbrnc1->nexthop.type) { case NEXTHOP_TYPE_IFINDEX: - return pbrnc1->nexthop->ifindex == pbrnc2->nexthop->ifindex; + return pbrnc1->nexthop.ifindex == pbrnc2->nexthop.ifindex; case NEXTHOP_TYPE_IPV4_IFINDEX: case NEXTHOP_TYPE_IPV4: - return pbrnc1->nexthop->gate.ipv4.s_addr - == pbrnc2->nexthop->gate.ipv4.s_addr; + return pbrnc1->nexthop.gate.ipv4.s_addr + == pbrnc2->nexthop.gate.ipv4.s_addr; case NEXTHOP_TYPE_IPV6_IFINDEX: case NEXTHOP_TYPE_IPV6: - return !memcmp(&pbrnc1->nexthop->gate.ipv6, - &pbrnc2->nexthop->gate.ipv6, 16); + return !memcmp(&pbrnc1->nexthop.gate.ipv6, + &pbrnc2->nexthop.gate.ipv6, 16); case NEXTHOP_TYPE_BLACKHOLE: - return pbrnc1->nexthop->bh_type == pbrnc2->nexthop->bh_type; + return pbrnc1->nexthop.bh_type == pbrnc2->nexthop.bh_type; } /* @@ -191,11 +193,11 @@ static void *pbr_nhgc_alloc(void *p) new = XCALLOC(MTYPE_PBR_NHG, sizeof(*new)); - strcpy(new->name, pnhgc->name); + strlcpy(new->name, pnhgc->name, sizeof(pnhgc->name)); new->table_id = pbr_nht_get_next_tableid(false); - DEBUGD(&pbr_dbg_nht, "%s: NHT: %s assigned Table ID: %u", - __PRETTY_FUNCTION__, new->name, new->table_id); + DEBUGD(&pbr_dbg_nht, "%s: NHT: %s assigned Table ID: %u", __func__, + new->name, new->table_id); new->nhh = hash_create_size(8, pbr_nh_hash_key, pbr_nh_hash_equal, "PBR NH Cache Hash"); @@ -212,7 +214,7 @@ void pbr_nhgroup_add_cb(const char *name) if (!nhgc) { DEBUGD(&pbr_dbg_nht, "%s: Could not find nhgc with name: %s\n", - __PRETTY_FUNCTION__, name); + __func__, name); return; } @@ -221,10 +223,8 @@ void pbr_nhgroup_add_cb(const char *name) if (!pnhgc) return; - DEBUGD(&pbr_dbg_nht, "%s: Added nexthop-group %s", __PRETTY_FUNCTION__, - name); + DEBUGD(&pbr_dbg_nht, "%s: Added nexthop-group %s", __func__, name); - pbr_nht_install_nexthop_group(pnhgc, nhgc->nhg); pbr_map_check_nh_group_change(name); } @@ -240,7 +240,7 @@ void pbr_nhgroup_add_nexthop_cb(const struct nexthop_group_cmd *nhgc, if (!pbr_nht_get_next_tableid(true)) { zlog_warn( "%s: Exhausted all table identifiers; cannot create nexthop-group cache for nexthop-group '%s'", - __PRETTY_FUNCTION__, nhgc->name); + __func__, nhgc->name); return; } @@ -249,9 +249,8 @@ void pbr_nhgroup_add_nexthop_cb(const struct nexthop_group_cmd *nhgc, pnhgc = hash_get(pbr_nhg_hash, &pnhgc_find, pbr_nhgc_alloc); /* create & insert new pnhc into pnhgc->nhh */ - pnhc_find.nexthop = (struct nexthop *)nhop; + pnhc_find.nexthop = *nhop; pnhc = hash_get(pnhgc->nhh, &pnhc_find, pbr_nh_alloc); - pnhc_find.nexthop = NULL; /* set parent pnhgc */ pnhc->parent = pnhgc; @@ -259,13 +258,15 @@ void pbr_nhgroup_add_nexthop_cb(const struct nexthop_group_cmd *nhgc, if (DEBUG_MODE_CHECK(&pbr_dbg_nht, DEBUG_MODE_ALL)) { nexthop2str(nhop, debugstr, sizeof(debugstr)); DEBUGD(&pbr_dbg_nht, "%s: Added %s to nexthop-group %s", - __PRETTY_FUNCTION__, debugstr, nhgc->name); + __func__, debugstr, nhgc->name); } pbr_nht_install_nexthop_group(pnhgc, nhgc->nhg); pbr_map_check_nh_group_change(nhgc->name); - if (nhop->type == NEXTHOP_TYPE_IFINDEX) { + if (nhop->type == NEXTHOP_TYPE_IFINDEX + || (nhop->type == NEXTHOP_TYPE_IPV6_IFINDEX + && IN6_IS_ADDR_LINKLOCAL(&nhop->gate.ipv6))) { struct interface *ifp; ifp = if_lookup_by_index(nhop->ifindex, nhop->vrf_id); @@ -289,7 +290,7 @@ void pbr_nhgroup_del_nexthop_cb(const struct nexthop_group_cmd *nhgc, pnhgc = hash_lookup(pbr_nhg_hash, &pnhgc_find); /* delete pnhc from pnhgc->nhh */ - pnhc_find.nexthop = (struct nexthop *)nhop; + pnhc_find.nexthop = *nhop; pnhc = hash_release(pnhgc->nhh, &pnhc_find); /* delete pnhc */ @@ -298,7 +299,7 @@ void pbr_nhgroup_del_nexthop_cb(const struct nexthop_group_cmd *nhgc, if (DEBUG_MODE_CHECK(&pbr_dbg_nht, DEBUG_MODE_ALL)) { nexthop2str(nhop, debugstr, sizeof(debugstr)); DEBUGD(&pbr_dbg_nht, "%s: Removed %s from nexthop-group %s", - __PRETTY_FUNCTION__, debugstr, nhgc->name); + __func__, debugstr, nhgc->name); } if (pnhgc->nhh->count) @@ -311,8 +312,7 @@ void pbr_nhgroup_del_nexthop_cb(const struct nexthop_group_cmd *nhgc, void pbr_nhgroup_delete_cb(const char *name) { - DEBUGD(&pbr_dbg_nht, "%s: Removed nexthop-group %s", - __PRETTY_FUNCTION__, name); + DEBUGD(&pbr_dbg_nht, "%s: Removed nexthop-group %s", __func__, name); /* delete group from all pbrms's */ pbr_nht_delete_group(name); @@ -327,27 +327,29 @@ static struct pbr_nexthop_cache *pbr_nht_lookup_nexthop(struct nexthop *nexthop) } #endif +static void +pbr_nht_find_nhg_from_table_update(struct pbr_nexthop_group_cache *pnhgc, + uint32_t table_id, bool installed) +{ + if (pnhgc->table_id == table_id) { + DEBUGD(&pbr_dbg_nht, "%s: %s: Table ID (%u) matches %s", + __func__, (installed ? "install" : "remove"), table_id, + pnhgc->name); + + pnhgc->installed = installed; + pnhgc->valid = installed; + pbr_map_schedule_policy_from_nhg(pnhgc->name, pnhgc->installed); + } +} + static void pbr_nht_find_nhg_from_table_install(struct hash_bucket *b, void *data) { struct pbr_nexthop_group_cache *pnhgc = (struct pbr_nexthop_group_cache *)b->data; - uint32_t *table_id = (uint32_t *)data; - - if (pnhgc->table_id == *table_id) { - DEBUGD(&pbr_dbg_nht, "%s: Table ID (%u) matches %s", - __PRETTY_FUNCTION__, *table_id, pnhgc->name); + uint32_t table_id = *(uint32_t *)data; - /* - * If the table has been re-handled by zebra - * and we are already installed no need to do - * anything here. - */ - if (!pnhgc->installed) { - pnhgc->installed = true; - pbr_map_schedule_policy_from_nhg(pnhgc->name); - } - } + pbr_nht_find_nhg_from_table_update(pnhgc, table_id, true); } void pbr_nht_route_installed_for_table(uint32_t table_id) @@ -359,7 +361,11 @@ void pbr_nht_route_installed_for_table(uint32_t table_id) static void pbr_nht_find_nhg_from_table_remove(struct hash_bucket *b, void *data) { - ; + struct pbr_nexthop_group_cache *pnhgc = + (struct pbr_nexthop_group_cache *)b->data; + uint32_t table_id = *(uint32_t *)data; + + pbr_nht_find_nhg_from_table_update(pnhgc, table_id, false); } void pbr_nht_route_removed_for_table(uint32_t table_id) @@ -430,13 +436,13 @@ static afi_t pbr_nht_which_afi(struct nexthop_group nhg, if (!bh && v6 && v4) DEBUGD(&pbr_dbg_nht, - "%s: Saw both V6 and V4 nexthops...using %s", - __PRETTY_FUNCTION__, afi2str(install_afi)); + "%s: Saw both V6 and V4 nexthops...using %s", __func__, + afi2str(install_afi)); if (bh && (v6 || v4)) DEBUGD(&pbr_dbg_nht, "%s: Saw blackhole nexthop(s) with %s%s%s nexthop(s), using AFI_MAX.", - __PRETTY_FUNCTION__, v4 ? "v4" : "", - (v4 && v6) ? " and " : "", v6 ? "v6" : ""); + __func__, v4 ? "v4" : "", (v4 && v6) ? " and " : "", + v6 ? "v6" : ""); return install_afi; } @@ -484,7 +490,7 @@ void pbr_nht_change_group(const char *name) if (!pnhgc) { DEBUGD(&pbr_dbg_nht, "%s: Could not find nexthop-group cache w/ name '%s'", - __PRETTY_FUNCTION__, name); + __func__, name); return; } @@ -492,7 +498,7 @@ void pbr_nht_change_group(const char *name) struct pbr_nexthop_cache lookup; struct pbr_nexthop_cache *pnhc; - lookup.nexthop = nhop; + lookup.nexthop = *nhop; pnhc = hash_lookup(pnhgc->nhh, &lookup); if (!pnhc) { pnhc = hash_get(pnhgc->nhh, &lookup, pbr_nh_alloc); @@ -509,12 +515,26 @@ char *pbr_nht_nexthop_make_name(char *name, size_t l, return buffer; } -void pbr_nht_add_individual_nexthop(struct pbr_map_sequence *pbrms) +void pbr_nht_add_individual_nexthop(struct pbr_map_sequence *pbrms, + const struct nexthop *nhop) { struct pbr_nexthop_group_cache *pnhgc; struct pbr_nexthop_group_cache find; struct pbr_nexthop_cache *pnhc; struct pbr_nexthop_cache lookup; + struct nexthop *nh; + char buf[PBR_NHC_NAMELEN]; + + pbrms->nhg = nexthop_group_new(); + pbrms->internal_nhg_name = XSTRDUP( + MTYPE_TMP, + pbr_nht_nexthop_make_name(pbrms->parent->name, PBR_NHC_NAMELEN, + pbrms->seqno, buf)); + + nh = nexthop_new(); + memcpy(nh, nhop, sizeof(*nh)); + + nexthop_group_add_sorted(pbrms->nhg, nh); memset(&find, 0, sizeof(find)); pbr_nht_nexthop_make_name(pbrms->parent->name, PBR_NHC_NAMELEN, @@ -523,7 +543,7 @@ void pbr_nht_add_individual_nexthop(struct pbr_map_sequence *pbrms) if (!pbr_nht_get_next_tableid(true)) { zlog_warn( "%s: Exhausted all table identifiers; cannot create nexthop-group cache for nexthop-group '%s'", - __PRETTY_FUNCTION__, find.name); + __func__, find.name); return; } @@ -532,40 +552,44 @@ void pbr_nht_add_individual_nexthop(struct pbr_map_sequence *pbrms) pnhgc = hash_get(pbr_nhg_hash, &find, pbr_nhgc_alloc); - lookup.nexthop = pbrms->nhg->nexthop; + lookup.nexthop = *pbrms->nhg->nexthop; pnhc = hash_get(pnhgc->nhh, &lookup, pbr_nh_alloc); pnhc->parent = pnhgc; + if (nhop->vrf_id != VRF_DEFAULT) { + struct vrf *vrf = vrf_lookup_by_id(nhop->vrf_id); + + if (vrf) + strlcpy(pnhc->vrf_name, vrf->name, + sizeof(pnhc->vrf_name)); + } + + if (nhop->ifindex != 0) { + struct interface *ifp = + if_lookup_by_index(nhop->ifindex, nhop->vrf_id); + + if (ifp) + strlcpy(pnhc->intf_name, ifp->name, + sizeof(pnhc->intf_name)); + } pbr_nht_install_nexthop_group(pnhgc, *pbrms->nhg); } -void pbr_nht_delete_individual_nexthop(struct pbr_map_sequence *pbrms) +static void pbr_nht_release_individual_nexthop(struct pbr_map_sequence *pbrms) { struct pbr_nexthop_group_cache *pnhgc; struct pbr_nexthop_group_cache find; struct pbr_nexthop_cache *pnhc; struct pbr_nexthop_cache lup; - struct pbr_map *pbrm = pbrms->parent; - struct listnode *node; - struct pbr_map_interface *pmi; struct nexthop *nh; enum nexthop_types_t nh_type = 0; - if (pbrm->valid && pbrms->nhs_installed && pbrm->incoming->count) { - for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, node, pmi)) - pbr_send_pbr_map(pbrms, pmi, false); - } - - pbrm->valid = false; - pbrms->nhs_installed = false; - pbrms->reason |= PBR_MAP_INVALID_NO_NEXTHOPS; - memset(&find, 0, sizeof(find)); snprintf(find.name, sizeof(find.name), "%s", pbrms->internal_nhg_name); pnhgc = hash_lookup(pbr_nhg_hash, &find); nh = pbrms->nhg->nexthop; nh_type = nh->type; - lup.nexthop = nh; + lup.nexthop = *nh; pnhc = hash_lookup(pnhgc->nhh, &lup); pnhc->parent = NULL; hash_release(pnhgc->nhh, pnhc); @@ -573,13 +597,19 @@ void pbr_nht_delete_individual_nexthop(struct pbr_map_sequence *pbrms) pbr_nht_uninstall_nexthop_group(pnhgc, *pbrms->nhg, nh_type); hash_release(pbr_nhg_hash, pnhgc); + pbr_nhgc_delete(pnhgc); - nexthop_del(pbrms->nhg, nh); - nexthop_free(nh); nexthop_group_delete(&pbrms->nhg); XFREE(MTYPE_TMP, pbrms->internal_nhg_name); } +void pbr_nht_delete_individual_nexthop(struct pbr_map_sequence *pbrms) +{ + pbr_map_delete_nexthops(pbrms); + + pbr_nht_release_individual_nexthop(pbrms); +} + struct pbr_nexthop_group_cache *pbr_nht_add_group(const char *name) { struct nexthop *nhop; @@ -590,7 +620,7 @@ struct pbr_nexthop_group_cache *pbr_nht_add_group(const char *name) if (!pbr_nht_get_next_tableid(true)) { zlog_warn( "%s: Exhausted all table identifiers; cannot create nexthop-group cache for nexthop-group '%s'", - __PRETTY_FUNCTION__, name); + __func__, name); return NULL; } @@ -598,20 +628,19 @@ struct pbr_nexthop_group_cache *pbr_nht_add_group(const char *name) if (!nhgc) { DEBUGD(&pbr_dbg_nht, "%s: Could not find nhgc with name: %s\n", - __PRETTY_FUNCTION__, name); + __func__, name); return NULL; } snprintf(lookup.name, sizeof(lookup.name), "%s", name); pnhgc = hash_get(pbr_nhg_hash, &lookup, pbr_nhgc_alloc); - DEBUGD(&pbr_dbg_nht, "%s: Retrieved NHGC @ %p", __PRETTY_FUNCTION__, - pnhgc); + DEBUGD(&pbr_dbg_nht, "%s: Retrieved NHGC @ %p", __func__, pnhgc); for (ALL_NEXTHOPS(nhgc->nhg, nhop)) { struct pbr_nexthop_cache lookupc; struct pbr_nexthop_cache *pnhc; - lookupc.nexthop = nhop; + lookupc.nexthop = *nhop; pnhc = hash_lookup(pnhgc->nhh, &lookupc); if (!pnhc) { pnhc = hash_get(pnhgc->nhh, &lookupc, pbr_nh_alloc); @@ -635,7 +664,6 @@ void pbr_nht_delete_group(const char *name) if (pbrms->nhgrp_name && strmatch(pbrms->nhgrp_name, name)) { pbrms->reason |= PBR_MAP_INVALID_NO_NEXTHOPS; - nexthop_group_delete(&pbrms->nhg); pbrms->nhg = NULL; pbrms->internal_nhg_name = NULL; pbrm->valid = false; @@ -650,7 +678,7 @@ void pbr_nht_delete_group(const char *name) bool pbr_nht_nexthop_valid(struct nexthop_group *nhg) { - DEBUGD(&pbr_dbg_nht, "%s: %p", __PRETTY_FUNCTION__, nhg); + DEBUGD(&pbr_dbg_nht, "%s: %p", __func__, nhg); return true; } @@ -659,13 +687,13 @@ bool pbr_nht_nexthop_group_valid(const char *name) struct pbr_nexthop_group_cache *pnhgc; struct pbr_nexthop_group_cache lookup; - DEBUGD(&pbr_dbg_nht, "%s: %s", __PRETTY_FUNCTION__, name); + DEBUGD(&pbr_dbg_nht, "%s: %s", __func__, name); snprintf(lookup.name, sizeof(lookup.name), "%s", name); pnhgc = hash_get(pbr_nhg_hash, &lookup, NULL); if (!pnhgc) return false; - DEBUGD(&pbr_dbg_nht, "%s: \t%d %d", __PRETTY_FUNCTION__, pnhgc->valid, + DEBUGD(&pbr_dbg_nht, "%s: \t%d %d", __func__, pnhgc->valid, pnhgc->installed); if (pnhgc->valid && pnhgc->installed) return true; @@ -676,60 +704,196 @@ bool pbr_nht_nexthop_group_valid(const char *name) struct pbr_nht_individual { struct zapi_route *nhr; struct interface *ifp; + struct pbr_vrf *pbr_vrf; + struct pbr_nexthop_cache *pnhc; + vrf_id_t old_vrf_id; - uint32_t valid; + bool valid; + + bool nhr_matched; }; -static void pbr_nht_individual_nexthop_update_lookup(struct hash_bucket *b, - void *data) +static bool +pbr_nht_individual_nexthop_gw_update(struct pbr_nexthop_cache *pnhc, + struct pbr_nht_individual *pnhi) { - struct pbr_nexthop_cache *pnhc = b->data; - struct pbr_nht_individual *pnhi = data; - char buf[PREFIX_STRLEN]; - bool old_valid; + bool is_valid = pnhc->valid; - old_valid = pnhc->valid; + if (!pnhi->nhr) /* It doesn't care about non-nexthop updates */ + goto done; switch (pnhi->nhr->prefix.family) { case AF_INET: - if (pnhc->nexthop->gate.ipv4.s_addr - == pnhi->nhr->prefix.u.prefix4.s_addr) - pnhc->valid = !!pnhi->nhr->nexthop_num; + if (pnhc->nexthop.gate.ipv4.s_addr + != pnhi->nhr->prefix.u.prefix4.s_addr) + goto done; /* Unrelated change */ break; case AF_INET6: - if (memcmp(&pnhc->nexthop->gate.ipv6, + if (memcmp(&pnhc->nexthop.gate.ipv6, &pnhi->nhr->prefix.u.prefix6, 16) - == 0) - pnhc->valid = !!pnhi->nhr->nexthop_num; + != 0) + goto done; /* Unrelated change */ + break; + } + + pnhi->nhr_matched = true; + if (!pnhi->nhr->nexthop_num) { + is_valid = false; + goto done; + } + + if (pnhc->nexthop.type == NEXTHOP_TYPE_IPV4_IFINDEX + || pnhc->nexthop.type == NEXTHOP_TYPE_IPV6_IFINDEX) { + + /* GATEWAY_IFINDEX type shouldn't resolve to group */ + if (pnhi->nhr->nexthop_num > 1) { + is_valid = false; + goto done; + } + + /* If whatever we resolved to wasn't on the interface we + * specified. (i.e. not a connected route), its invalid. + */ + if (pnhi->nhr->nexthops[0].ifindex != pnhc->nexthop.ifindex) { + is_valid = false; + goto done; + } + } + + is_valid = true; + +done: + pnhc->valid = is_valid; + + return pnhc->valid; +} + +static bool +pbr_nht_individual_nexthop_interface_update(struct pbr_nexthop_cache *pnhc, + struct pbr_nht_individual *pnhi) +{ + bool is_valid = pnhc->valid; + + if (!pnhi->ifp) /* It doesn't care about non-interface updates */ + goto done; + + if (pnhc->nexthop.ifindex + != pnhi->ifp->ifindex) /* Un-related interface */ + goto done; + + pnhi->nhr_matched = true; + is_valid = !!if_is_up(pnhi->ifp); + +done: + pnhc->valid = is_valid; + + return pnhc->valid; +} + +/* Given this update either from interface or nexthop tracking, re-validate this + * nexthop. + * + * If the update is un-related, the subroutines shoud just return their cached + * valid state. + */ +static void pbr_nht_individual_nexthop_update(struct pbr_nexthop_cache *pnhc, + struct pbr_nht_individual *pnhi) +{ + assert(pnhi->nhr || pnhi->ifp); /* Either nexthop or interface update */ + + switch (pnhc->nexthop.type) { + case NEXTHOP_TYPE_IFINDEX: + pbr_nht_individual_nexthop_interface_update(pnhc, pnhi); + break; + case NEXTHOP_TYPE_IPV6_IFINDEX: + if (IN6_IS_ADDR_LINKLOCAL(&pnhc->nexthop.gate.ipv6)) { + pbr_nht_individual_nexthop_interface_update(pnhc, pnhi); + break; + } + /* Intentional fall thru */ + case NEXTHOP_TYPE_IPV4_IFINDEX: + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV6: + pbr_nht_individual_nexthop_gw_update(pnhc, pnhi); + break; + case NEXTHOP_TYPE_BLACKHOLE: + pnhc->valid = true; break; } +} + +static void pbr_nht_individual_nexthop_update_lookup(struct hash_bucket *b, + void *data) +{ + struct pbr_nexthop_cache *pnhc = b->data; + struct pbr_nht_individual *pnhi = data; + char buf[PREFIX_STRLEN]; + bool old_valid; + + old_valid = pnhc->valid; + + pbr_nht_individual_nexthop_update(pnhc, pnhi); DEBUGD(&pbr_dbg_nht, "\tFound %s: old: %d new: %d", prefix2str(&pnhi->nhr->prefix, buf, sizeof(buf)), old_valid, pnhc->valid); if (pnhc->valid) - pnhi->valid += 1; + pnhi->valid = true; +} + +static void pbr_nexthop_group_cache_iterate_to_group(struct hash_bucket *b, + void *data) +{ + struct pbr_nexthop_cache *pnhc = b->data; + struct nexthop_group *nhg = data; + struct nexthop *nh = NULL; + + copy_nexthops(&nh, &pnhc->nexthop, NULL); + + _nexthop_add(&nhg->nexthop, nh); +} + +static void +pbr_nexthop_group_cache_to_nexthop_group(struct nexthop_group *nhg, + struct pbr_nexthop_group_cache *pnhgc) +{ + hash_iterate(pnhgc->nhh, pbr_nexthop_group_cache_iterate_to_group, nhg); } static void pbr_nht_nexthop_update_lookup(struct hash_bucket *b, void *data) { struct pbr_nexthop_group_cache *pnhgc = b->data; - struct pbr_nht_individual pnhi; + struct pbr_nht_individual pnhi = {}; + struct nexthop_group nhg = {}; bool old_valid; old_valid = pnhgc->valid; pnhi.nhr = (struct zapi_route *)data; - pnhi.valid = 0; + pnhi.valid = false; + pnhi.nhr_matched = false; hash_iterate(pnhgc->nhh, pbr_nht_individual_nexthop_update_lookup, &pnhi); + if (!pnhi.nhr_matched) + return; + /* * If any of the specified nexthops are valid we are valid */ pnhgc->valid = !!pnhi.valid; + pbr_nexthop_group_cache_to_nexthop_group(&nhg, pnhgc); + + if (pnhgc->valid) + pbr_nht_install_nexthop_group(pnhgc, nhg); + else + pbr_nht_uninstall_nexthop_group(pnhgc, nhg, 0); + + /* Don't need copied nexthops anymore */ + nexthops_free(nhg.nexthop); + if (old_valid != pnhgc->valid) pbr_map_check_nh_group_change(pnhgc->name); } @@ -739,8 +903,180 @@ void pbr_nht_nexthop_update(struct zapi_route *nhr) hash_iterate(pbr_nhg_hash, pbr_nht_nexthop_update_lookup, nhr); } +struct nhrc_vrf_info { + struct pbr_vrf *pbr_vrf; + uint32_t old_vrf_id; + struct nhrc *nhrc; +}; + +static int pbr_nht_nhrc_vrf_change(struct hash_bucket *b, void *data) +{ + struct nhrc *nhrc = b->data; + struct nhrc_vrf_info *nhrcvi = data; + + if (nhrc->nexthop.vrf_id == nhrcvi->old_vrf_id) { + nhrcvi->nhrc = nhrc; + return HASHWALK_ABORT; + } + + return HASHWALK_CONTINUE; +} + +static int pbr_nht_individual_nexthop_vrf_handle(struct hash_bucket *b, + void *data) +{ + struct pbr_nexthop_cache *pnhc = b->data; + struct pbr_nht_individual *pnhi = data; + + if (pnhc->looked_at == true) + return HASHWALK_CONTINUE; + + if (pnhc->nexthop.vrf_id == VRF_DEFAULT) + return HASHWALK_CONTINUE; + + if (strncmp(pnhc->vrf_name, pbr_vrf_name(pnhi->pbr_vrf), + sizeof(pnhc->vrf_name)) + == 0) { + pnhi->pnhc = pnhc; + + if (pnhc->nexthop.vrf_id != pbr_vrf_id(pnhi->pbr_vrf)) { + struct nhrc_vrf_info nhrcvi; + + memset(&nhrcvi, 0, sizeof(nhrcvi)); + nhrcvi.pbr_vrf = pnhi->pbr_vrf; + nhrcvi.old_vrf_id = pnhc->nexthop.vrf_id; + + pnhi->nhr_matched = true; + pnhi->old_vrf_id = pnhc->nexthop.vrf_id; + + do { + nhrcvi.nhrc = NULL; + hash_walk(pbr_nhrc_hash, + pbr_nht_nhrc_vrf_change, &nhrcvi); + if (nhrcvi.nhrc) { + hash_release(pbr_nhrc_hash, + nhrcvi.nhrc); + nhrcvi.nhrc->nexthop.vrf_id = + pbr_vrf_id(pnhi->pbr_vrf); + hash_get(pbr_nhrc_hash, nhrcvi.nhrc, + hash_alloc_intern); + pbr_send_rnh(&nhrcvi.nhrc->nexthop, true); + } + } while (nhrcvi.nhrc); + } + + pnhc->looked_at = true; + return HASHWALK_ABORT; + } + + return HASHWALK_CONTINUE; +} + +static void pbr_nht_clear_looked_at(struct hash_bucket *b, void *data) +{ + struct pbr_nexthop_cache *pnhc = b->data; + + pnhc->looked_at = false; +} + +static void pbr_nht_nexthop_vrf_handle(struct hash_bucket *b, void *data) +{ + struct pbr_nexthop_group_cache *pnhgc = b->data; + struct pbr_vrf *pbr_vrf = data; + struct pbr_nht_individual pnhi = {}; + + zlog_debug("pnhgc iterating"); + hash_iterate(pnhgc->nhh, pbr_nht_clear_looked_at, NULL); + memset(&pnhi, 0, sizeof(pnhi)); + pnhi.pbr_vrf = pbr_vrf; + do { + struct pbr_nexthop_cache *pnhc; + + pnhi.pnhc = NULL; + hash_walk(pnhgc->nhh, pbr_nht_individual_nexthop_vrf_handle, + &pnhi); + + if (!pnhi.pnhc) + continue; + + pnhc = pnhi.pnhc; + pnhc->nexthop.vrf_id = pnhi.old_vrf_id; + pnhi.pnhc = hash_release(pnhgc->nhh, pnhi.pnhc); + if (pnhi.pnhc) { + pnhi.pnhc->nexthop.vrf_id = pbr_vrf_id(pbr_vrf); + + hash_get(pnhgc->nhh, pnhi.pnhc, hash_alloc_intern); + } else + pnhc->nexthop.vrf_id = pbr_vrf_id(pbr_vrf); + + pbr_map_check_vrf_nh_group_change(pnhgc->name, pbr_vrf, + pnhi.old_vrf_id); + } while (pnhi.pnhc); +} + +void pbr_nht_vrf_update(struct pbr_vrf *pbr_vrf) +{ + hash_iterate(pbr_nhg_hash, pbr_nht_nexthop_vrf_handle, pbr_vrf); +} + +static void pbr_nht_individual_nexthop_interface_handle(struct hash_bucket *b, + void *data) +{ + struct pbr_nexthop_cache *pnhc = b->data; + struct pbr_nht_individual *pnhi = data; + + if (pnhc->nexthop.ifindex == 0) + return; + + if ((strncmp(pnhc->intf_name, pnhi->ifp->name, sizeof(pnhc->intf_name)) + == 0) + && pnhc->nexthop.ifindex != pnhi->ifp->ifindex) + pnhi->pnhc = pnhc; +} + +static void pbr_nht_nexthop_interface_handle(struct hash_bucket *b, void *data) +{ + struct pbr_nexthop_group_cache *pnhgc = b->data; + struct interface *ifp = data; + struct pbr_nht_individual pnhi = {}; + struct nhrc *nhrc; + uint32_t old_ifindex; + + do { + memset(&pnhi, 0, sizeof(pnhi)); + pnhi.ifp = ifp; + hash_iterate(pnhgc->nhh, + pbr_nht_individual_nexthop_interface_handle, + &pnhi); + + if (!pnhi.pnhc) + continue; + + pnhi.pnhc = hash_release(pnhgc->nhh, pnhi.pnhc); + old_ifindex = pnhi.pnhc->nexthop.ifindex; + + nhrc = hash_lookup(pbr_nhrc_hash, &pnhi.pnhc->nexthop); + if (nhrc) { + hash_release(pbr_nhrc_hash, nhrc); + nhrc->nexthop.ifindex = ifp->ifindex; + hash_get(pbr_nhrc_hash, nhrc, hash_alloc_intern); + } + pnhi.pnhc->nexthop.ifindex = ifp->ifindex; + + hash_get(pnhgc->nhh, pnhi.pnhc, hash_alloc_intern); + + pbr_map_check_interface_nh_group_change(pnhgc->name, ifp, + old_ifindex); + } while (pnhi.pnhc); +} + +void pbr_nht_interface_update(struct interface *ifp) +{ + hash_iterate(pbr_nhg_hash, pbr_nht_nexthop_interface_handle, ifp); +} + static void -pbr_nht_individual_nexthop_interface_update_lookup(struct hash_backet *b, +pbr_nht_individual_nexthop_interface_update_lookup(struct hash_bucket *b, void *data) { struct pbr_nexthop_cache *pnhc = b->data; @@ -749,35 +1085,33 @@ pbr_nht_individual_nexthop_interface_update_lookup(struct hash_backet *b, old_valid = pnhc->valid; - if (pnhc->nexthop->type == NEXTHOP_TYPE_IFINDEX - && pnhc->nexthop->ifindex == pnhi->ifp->ifindex) - pnhc->valid = !!if_is_up(pnhi->ifp); + pbr_nht_individual_nexthop_update(pnhc, pnhi); DEBUGD(&pbr_dbg_nht, "\tFound %s: old: %d new: %d", pnhi->ifp->name, old_valid, pnhc->valid); if (pnhc->valid) - pnhi->valid += 1; + pnhi->valid = true; } -static void pbr_nht_nexthop_interface_update_lookup(struct hash_backet *b, +static void pbr_nht_nexthop_interface_update_lookup(struct hash_bucket *b, void *data) { struct pbr_nexthop_group_cache *pnhgc = b->data; - struct pbr_nht_individual pnhi; + struct pbr_nht_individual pnhi = {}; bool old_valid; old_valid = pnhgc->valid; pnhi.ifp = data; - pnhi.valid = 0; + pnhi.valid = false; hash_iterate(pnhgc->nhh, pbr_nht_individual_nexthop_interface_update_lookup, &pnhi); /* * If any of the specified nexthops are valid we are valid */ - pnhgc->valid = !!pnhi.valid; + pnhgc->valid = pnhi.valid; if (old_valid != pnhgc->valid) pbr_map_check_nh_group_change(pnhgc->name); @@ -789,10 +1123,9 @@ void pbr_nht_nexthop_interface_update(struct interface *ifp) ifp); } -static uint32_t pbr_nhg_hash_key(void *arg) +static uint32_t pbr_nhg_hash_key(const void *arg) { - struct pbr_nexthop_group_cache *nhgc = - (struct pbr_nexthop_group_cache *)arg; + const struct pbr_nexthop_group_cache *nhgc = arg; return jhash(&nhgc->name, strlen(nhgc->name), 0x52c34a96); } @@ -872,7 +1205,7 @@ uint32_t pbr_nht_get_table(const char *name) if (!pnhgc) { DEBUGD(&pbr_dbg_nht, "%s: Could not find nexthop-group cache w/ name '%s'", - __PRETTY_FUNCTION__, name); + __func__, name); return 5000; } @@ -901,11 +1234,25 @@ static void pbr_nht_show_nhg_nexthops(struct hash_bucket *b, void *data) struct vty *vty = data; vty_out(vty, "\tValid: %d ", pnhc->valid); - nexthop_group_write_nexthop(vty, pnhc->nexthop); + nexthop_group_write_nexthop(vty, &pnhc->nexthop); +} + +static void pbr_nht_json_nhg_nexthops(struct hash_bucket *b, void *data) +{ + struct pbr_nexthop_cache *pnhc = b->data; + json_object *all_hops = data; + json_object *this_hop; + + this_hop = json_object_new_object(); + nexthop_group_json_nexthop(this_hop, &pnhc->nexthop); + json_object_boolean_add(this_hop, "valid", pnhc->valid); + + json_object_array_add(all_hops, this_hop); } struct pbr_nht_show { struct vty *vty; + json_object *json; const char *name; }; @@ -925,6 +1272,36 @@ static void pbr_nht_show_nhg(struct hash_bucket *b, void *data) hash_iterate(pnhgc->nhh, pbr_nht_show_nhg_nexthops, vty); } +static void pbr_nht_json_nhg(struct hash_bucket *b, void *data) +{ + struct pbr_nexthop_group_cache *pnhgc = b->data; + struct pbr_nht_show *pns = data; + json_object *j, *this_group, *group_hops; + + if (pns->name && strcmp(pns->name, pnhgc->name) != 0) + return; + + j = pns->json; + this_group = json_object_new_object(); + + if (!j || !this_group) + return; + + json_object_int_add(this_group, "id", pnhgc->table_id); + json_object_string_add(this_group, "name", pnhgc->name); + json_object_boolean_add(this_group, "valid", pnhgc->valid); + json_object_boolean_add(this_group, "installed", pnhgc->installed); + + group_hops = json_object_new_array(); + + if (group_hops) { + hash_iterate(pnhgc->nhh, pbr_nht_json_nhg_nexthops, group_hops); + json_object_object_add(this_group, "nexthops", group_hops); + } + + json_object_array_add(j, this_group); +} + void pbr_nht_show_nexthop_group(struct vty *vty, const char *name) { struct pbr_nht_show pns; @@ -935,12 +1312,22 @@ void pbr_nht_show_nexthop_group(struct vty *vty, const char *name) hash_iterate(pbr_nhg_hash, pbr_nht_show_nhg, &pns); } +void pbr_nht_json_nexthop_group(json_object *j, const char *name) +{ + struct pbr_nht_show pns; + + pns.name = name; + pns.json = j; + + hash_iterate(pbr_nhg_hash, pbr_nht_json_nhg, &pns); +} + void pbr_nht_init(void) { pbr_nhg_hash = hash_create_size( 16, pbr_nhg_hash_key, pbr_nhg_hash_equal, "PBR NHG Cache Hash"); pbr_nhrc_hash = - hash_create_size(16, (unsigned int (*)(void *))nexthop_hash, + hash_create_size(16, (unsigned int (*)(const void *))nexthop_hash, pbr_nhrc_hash_equal, "PBR NH Hash"); pbr_nhg_low_table = PBR_NHT_DEFAULT_LOW_TABLEID; diff --git a/pbrd/pbr_nht.h b/pbrd/pbr_nht.h index 4ef41cede7..6346795215 100644 --- a/pbrd/pbr_nht.h +++ b/pbrd/pbr_nht.h @@ -24,9 +24,12 @@ #include #include "pbr_map.h" +#include "json.h" #define PBR_NHC_NAMELEN PBR_MAP_NAMELEN + 10 +extern struct hash *pbr_nhg_hash; + struct pbr_nexthop_group_cache { char name[PBR_NHC_NAMELEN]; @@ -45,9 +48,14 @@ struct pbr_nexthop_group_cache { struct pbr_nexthop_cache { struct pbr_nexthop_group_cache *parent; - struct nexthop *nexthop; + char vrf_name[VRF_NAMSIZ + 1]; + char intf_name[INTERFACE_NAMSIZ + 1]; + + struct nexthop nexthop; + bool looked_at; bool valid; + bool nhr_matched; }; extern void pbr_nht_write_table_range(struct vty *vty); @@ -88,7 +96,8 @@ extern struct pbr_nexthop_group_cache *pbr_nht_add_group(const char *name); extern void pbr_nht_change_group(const char *name); extern void pbr_nht_delete_group(const char *name); -extern void pbr_nht_add_individual_nexthop(struct pbr_map_sequence *pbrms); +extern void pbr_nht_add_individual_nexthop(struct pbr_map_sequence *pbrms, + const struct nexthop *nhop); extern void pbr_nht_delete_individual_nexthop(struct pbr_map_sequence *pbrms); /* * Given the tableid of the installed default @@ -111,6 +120,7 @@ extern char *pbr_nht_nexthop_make_name(char *name, size_t l, uint32_t seqno, char *buffer); extern void pbr_nht_show_nexthop_group(struct vty *vty, const char *name); +extern void pbr_nht_json_nexthop_group(json_object *j, const char *name); /* * When we get a callback from zebra about a nexthop changing @@ -123,4 +133,7 @@ extern void pbr_nht_nexthop_update(struct zapi_route *nhr); extern void pbr_nht_nexthop_interface_update(struct interface *ifp); extern void pbr_nht_init(void); + +extern void pbr_nht_vrf_update(struct pbr_vrf *pbr_vrf); +extern void pbr_nht_interface_update(struct interface *ifp); #endif diff --git a/pbrd/pbr_vrf.c b/pbrd/pbr_vrf.c new file mode 100644 index 0000000000..389e5e8be0 --- /dev/null +++ b/pbrd/pbr_vrf.c @@ -0,0 +1,139 @@ +/* + * PBR - vrf code + * Copyright (C) 2019 Cumulus Networks, Inc. + * Stephen Worley + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include + +#include "vrf.h" + +#include "pbr_vrf.h" +#include "pbr_memory.h" +#include "pbr_map.h" +#include "pbr_debug.h" +#include "pbr_nht.h" + +DEFINE_MTYPE_STATIC(PBRD, PBR_MAP_VRF, "PBR Map VRF") + +static struct pbr_vrf *pbr_vrf_alloc(void) +{ + struct pbr_vrf *pbr_vrf; + + pbr_vrf = XCALLOC(MTYPE_PBR_MAP_VRF, sizeof(struct pbr_vrf)); + + return pbr_vrf; +} + +static void pbr_vrf_free(struct pbr_vrf *pbr_vrf) +{ + XFREE(MTYPE_PBR_MAP_VRF, pbr_vrf); +} + +static int pbr_vrf_new(struct vrf *vrf) +{ + struct pbr_vrf *pbr_vrf; + + DEBUGD(&pbr_dbg_event, "%s: %u (%s)", __func__, vrf->vrf_id, vrf->name); + + pbr_vrf = pbr_vrf_alloc(); + vrf->info = pbr_vrf; + pbr_vrf->vrf = vrf; + + return 0; +} + +static int pbr_vrf_enable(struct vrf *vrf) +{ + DEBUGD(&pbr_dbg_event, "%s: %u (%s)", __func__, vrf->vrf_id, vrf->name); + + pbr_nht_vrf_update(vrf->info); + pbr_map_vrf_update(vrf->info); + + return 0; +} + +static int pbr_vrf_disable(struct vrf *vrf) +{ + DEBUGD(&pbr_dbg_event, "%s: %u (%s)", __func__, vrf->vrf_id, vrf->name); + + pbr_map_vrf_update(vrf->info); + + return 0; +} + +static int pbr_vrf_delete(struct vrf *vrf) +{ + DEBUGD(&pbr_dbg_event, "%s: %u (%s)", __func__, vrf->vrf_id, vrf->name); + + /* + * Make sure vrf is always marked disabled first so we handle + * pbr rules using it. + */ + assert(!vrf_is_enabled(vrf)); + + pbr_vrf_free(vrf->info); + vrf->info = NULL; + + return 0; +} + +struct pbr_vrf *pbr_vrf_lookup_by_id(vrf_id_t vrf_id) +{ + struct vrf *vrf; + + vrf = vrf_lookup_by_id(vrf_id); + if (vrf) + return ((struct pbr_vrf *)vrf->info); + + return NULL; +} + +struct pbr_vrf *pbr_vrf_lookup_by_name(const char *name) +{ + struct vrf *vrf; + + if (!name) + name = VRF_DEFAULT_NAME; + + vrf = vrf_lookup_by_name(name); + if (vrf) + return ((struct pbr_vrf *)vrf->info); + + return NULL; +} + +bool pbr_vrf_is_enabled(const struct pbr_vrf *pbr_vrf) +{ + return vrf_is_enabled(pbr_vrf->vrf) ? true : false; +} + +bool pbr_vrf_is_valid(const struct pbr_vrf *pbr_vrf) +{ + if (vrf_is_backend_netns()) + return false; + + if (!pbr_vrf->vrf) + return false; + + return pbr_vrf_is_enabled(pbr_vrf); +} + +void pbr_vrf_init(void) +{ + vrf_init(pbr_vrf_new, pbr_vrf_enable, pbr_vrf_disable, pbr_vrf_delete, + NULL); +} diff --git a/pbrd/pbr_vrf.h b/pbrd/pbr_vrf.h new file mode 100644 index 0000000000..c9448762eb --- /dev/null +++ b/pbrd/pbr_vrf.h @@ -0,0 +1,43 @@ +/* + * VRF library for PBR + * Copyright (C) 2019 Cumulus Networks, Inc. + * Stephen Worley + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef __PBR_VRF_H__ +#define __PBR_VRF_H__ + +struct pbr_vrf { + struct vrf *vrf; +}; + +static inline const char *pbr_vrf_name(const struct pbr_vrf *pbr_vrf) +{ + return pbr_vrf->vrf->name; +} + +static inline vrf_id_t pbr_vrf_id(const struct pbr_vrf *pbr_vrf) +{ + return pbr_vrf->vrf->vrf_id; +} + +extern struct pbr_vrf *pbr_vrf_lookup_by_id(vrf_id_t vrf_id); +extern struct pbr_vrf *pbr_vrf_lookup_by_name(const char *name); +extern bool pbr_vrf_is_valid(const struct pbr_vrf *pbr_vrf); +extern bool pbr_vrf_is_enabled(const struct pbr_vrf *pbr_vrf); + +extern void pbr_vrf_init(void); +#endif diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c index 067d5c01fd..f96c6b5ee7 100644 --- a/pbrd/pbr_vty.c +++ b/pbrd/pbr_vty.c @@ -25,7 +25,9 @@ #include "vrf.h" #include "nexthop.h" #include "nexthop_group.h" +#include "nexthop_group_private.h" #include "log.h" +#include "json.h" #include "debug.h" #include "pbr.h" @@ -38,7 +40,7 @@ #include "pbrd/pbr_vty_clippy.c" #endif -DEFUN_NOSH(pbr_map, pbr_map_cmd, "pbr-map WORD seq (1-700)", +DEFUN_NOSH(pbr_map, pbr_map_cmd, "pbr-map PBRMAP seq (1-700)", "Create pbr-map or enter pbr-map command mode\n" "The name of the PBR MAP\n" "Sequence to insert in existing pbr-map entry\n" @@ -54,7 +56,7 @@ DEFUN_NOSH(pbr_map, pbr_map_cmd, "pbr-map WORD seq (1-700)", return CMD_SUCCESS; } -DEFUN_NOSH(no_pbr_map, no_pbr_map_cmd, "no pbr-map WORD [seq (1-700)]", +DEFUN_NOSH(no_pbr_map, no_pbr_map_cmd, "no pbr-map PBRMAP [seq (1-700)]", NO_STR "Delete pbr-map\n" "The name of the PBR MAP\n" @@ -87,8 +89,7 @@ DEFUN_NOSH(no_pbr_map, no_pbr_map_cmd, "no pbr-map WORD [seq (1-700)]", DEFPY(pbr_set_table_range, pbr_set_table_range_cmd, - "[no] pbr table range (10000-4294966272)$lb (10000-4294966272)$ub", - NO_STR + "pbr table range (10000-4294966272)$lb (10000-4294966272)$ub", PBR_STR "Set table ID range\n" "Set table ID range\n" @@ -112,6 +113,19 @@ DEFPY(pbr_set_table_range, return ret; } +DEFPY(no_pbr_set_table_range, no_pbr_set_table_range_cmd, + "no pbr table range [(10000-4294966272)$lb (10000-4294966272)$ub]", + NO_STR + PBR_STR + "Set table ID range\n" + "Set table ID range\n" + "Lower bound for table ID range\n" + "Upper bound for table ID range\n") +{ + pbr_nht_set_tableid_range(PBR_NHT_DEFAULT_LOW_TABLEID, + PBR_NHT_DEFAULT_HIGH_TABLEID); + return CMD_SUCCESS; +} DEFPY(pbr_map_match_src, pbr_map_match_src_cmd, "[no] match src-ip $prefix", @@ -126,18 +140,17 @@ DEFPY(pbr_map_match_src, pbr_map_match_src_cmd, pbrms->family = prefix->family; if (!no) { - if (prefix_same(pbrms->src, prefix)) - return CMD_SUCCESS; - - if (!pbrms->src) + if (pbrms->src) { + if (prefix_same(pbrms->src, prefix)) + return CMD_SUCCESS; + } else pbrms->src = prefix_new(); + prefix_copy(pbrms->src, prefix); - } else { - prefix_free(pbrms->src); - pbrms->src = 0; - } + } else + prefix_free(&pbrms->src); - pbr_map_check(pbrms); + pbr_map_check(pbrms, true); return CMD_SUCCESS; } @@ -146,7 +159,7 @@ DEFPY(pbr_map_match_dst, pbr_map_match_dst_cmd, "[no] match dst-ip $prefix", NO_STR "Match the rest of the command\n" - "Choose the src ip or ipv6 prefix to use\n" + "Choose the dst ip or ipv6 prefix to use\n" "v4 Prefix\n" "v6 Prefix\n") { @@ -155,79 +168,215 @@ DEFPY(pbr_map_match_dst, pbr_map_match_dst_cmd, pbrms->family = prefix->family; if (!no) { - if (prefix_same(pbrms->dst, prefix)) - return CMD_SUCCESS; - - if (!pbrms->dst) + if (pbrms->dst) { + if (prefix_same(pbrms->dst, prefix)) + return CMD_SUCCESS; + } else pbrms->dst = prefix_new(); + prefix_copy(pbrms->dst, prefix); + } else + prefix_free(&pbrms->dst); + + pbr_map_check(pbrms, true); + + return CMD_SUCCESS; +} + +DEFPY(pbr_map_match_dscp, pbr_map_match_dscp_cmd, + "[no] match dscp DSCP$dscp", + NO_STR + "Match the rest of the command\n" + "Match based on IP DSCP field\n" + "DSCP value (below 64) or standard codepoint name\n") +{ + struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); + char dscpname[100]; + uint8_t rawDscp; + + /* Discriminate dscp enums (cs0, cs1 etc.) and numbers */ + bool isANumber = true; + for (int i = 0; i < (int)strlen(dscp); i++) { + /* Letters are not numbers */ + if (!isdigit(dscp[i])) + isANumber = false; + + /* Lowercase the dscp enum (if needed) */ + if (isupper(dscp[i])) + dscpname[i] = tolower(dscp[i]); + else + dscpname[i] = dscp[i]; + } + dscpname[strlen(dscp)] = '\0'; + + if (isANumber) { + /* dscp passed is a regular number */ + long dscpAsNum = strtol(dscp, NULL, 0); + + if (dscpAsNum > PBR_DSFIELD_DSCP >> 2) { + /* Refuse to install on overflow */ + vty_out(vty, "dscp (%s) must be less than 64\n", dscp); + return CMD_WARNING_CONFIG_FAILED; + } + rawDscp = dscpAsNum; + } else { + /* check dscp if it is an enum like cs0 */ + rawDscp = pbr_map_decode_dscp_enum(dscpname); + if (rawDscp > PBR_DSFIELD_DSCP) { + vty_out(vty, "Invalid dscp value: %s\n", dscpname); + return CMD_WARNING_CONFIG_FAILED; + } + } + + if (!no) { + if (((pbrms->dsfield & PBR_DSFIELD_DSCP) >> 2) == rawDscp) + return CMD_SUCCESS; + + /* Set the DSCP bits of the DSField */ + pbrms->dsfield = + (pbrms->dsfield & ~PBR_DSFIELD_DSCP) | (rawDscp << 2); } else { - prefix_free(pbrms->dst); - pbrms->dst = NULL; + pbrms->dsfield &= ~PBR_DSFIELD_DSCP; } - pbr_map_check(pbrms); + pbr_map_check(pbrms, true); return CMD_SUCCESS; } -DEFPY(pbr_map_nexthop_group, pbr_map_nexthop_group_cmd, - "[no] set nexthop-group NAME$name", +DEFPY(pbr_map_match_ecn, pbr_map_match_ecn_cmd, + "[no] match ecn (0-3)$ecn", + NO_STR + "Match the rest of the command\n" + "Match based on IP ECN field\n" + "Explicit Congestion Notification\n") +{ + struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); + + if (!no) { + if ((pbrms->dsfield & PBR_DSFIELD_ECN) == ecn) + return CMD_SUCCESS; + + /* Set the ECN bits of the DSField */ + pbrms->dsfield = (pbrms->dsfield & ~PBR_DSFIELD_ECN) | ecn; + } else { + pbrms->dsfield &= ~PBR_DSFIELD_ECN; + } + + pbr_map_check(pbrms, true); + + return CMD_SUCCESS; +} + +DEFPY(pbr_map_match_mark, pbr_map_match_mark_cmd, + "[no] match mark (1-4294967295)$mark", NO_STR - "Set for the PBR-MAP\n" - "nexthop-group to use\n" - "The name of the nexthop-group\n") + "Match the rest of the command\n" + "Choose the mark value to use\n" + "mark\n") { struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); - struct nexthop_group_cmd *nhgc; - if (pbrms->nhg) { - vty_out(vty, - "A `set nexthop XX` command already exists, please remove that first\n"); - return CMD_WARNING_CONFIG_FAILED; +#ifndef GNU_LINUX + vty_out(vty, "pbr marks are not supported on this platform"); + return CMD_WARNING_CONFIG_FAILED; +#endif + + if (!no) { + if (pbrms->mark) + if (pbrms->mark == (uint32_t)mark) + return CMD_SUCCESS; + + pbrms->mark = (uint32_t)mark; + } else + pbrms->mark = 0; + + pbr_map_check(pbrms, true); + + return CMD_SUCCESS; +} + +static void pbrms_clear_set_vrf_config(struct pbr_map_sequence *pbrms) +{ + if (pbrms->vrf_lookup || pbrms->vrf_unchanged) { + pbr_map_delete_vrf(pbrms); + pbrms->vrf_name[0] = '\0'; + pbrms->vrf_lookup = false; + pbrms->vrf_unchanged = false; } +} + +static void pbrms_clear_set_nhg_config(struct pbr_map_sequence *pbrms) +{ + if (pbrms->nhgrp_name) + pbr_map_delete_nexthops(pbrms); +} + +static void pbrms_clear_set_nexthop_config(struct pbr_map_sequence *pbrms) +{ + if (pbrms->nhg) + pbr_nht_delete_individual_nexthop(pbrms); +} + +static void pbrms_clear_set_config(struct pbr_map_sequence *pbrms) +{ + pbrms_clear_set_vrf_config(pbrms); + pbrms_clear_set_nhg_config(pbrms); + pbrms_clear_set_nexthop_config(pbrms); + + pbrms->nhs_installed = false; +} + +DEFPY(pbr_map_nexthop_group, pbr_map_nexthop_group_cmd, + "set nexthop-group NHGNAME$name", + "Set for the PBR-MAP\n" + "nexthop-group to use\n" + "The name of the nexthop-group\n") +{ + struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); + struct nexthop_group_cmd *nhgc; nhgc = nhgc_find(name); if (!nhgc) { vty_out(vty, "Specified nexthop-group %s does not exist\n", name); - vty_out(vty, "PBR-MAP will not be applied until it is created\n"); + vty_out(vty, + "PBR-MAP will not be applied until it is created\n"); } - if (no) { - if (pbrms->nhgrp_name && strcmp(name, pbrms->nhgrp_name) == 0) - pbr_map_delete_nexthop_group(pbrms); - else { - vty_out(vty, - "Nexthop Group specified: %s does not exist to remove", - name); - return CMD_WARNING_CONFIG_FAILED; - } - } else { - if (pbrms->nhgrp_name) { - if (strcmp(name, pbrms->nhgrp_name) != 0) { - vty_out(vty, - "Please delete current nexthop group before modifying current one"); - return CMD_WARNING_CONFIG_FAILED; - } + if (pbrms->nhgrp_name && strcmp(name, pbrms->nhgrp_name) == 0) + return CMD_SUCCESS; - return CMD_SUCCESS; - } - pbrms->nhgrp_name = XSTRDUP(MTYPE_TMP, name); - pbr_map_check(pbrms); - } + /* This is new/replacement config */ + pbrms_clear_set_config(pbrms); + + pbrms->nhgrp_name = XSTRDUP(MTYPE_TMP, name); + pbr_map_check(pbrms, true); + + return CMD_SUCCESS; +} + +DEFPY(no_pbr_map_nexthop_group, no_pbr_map_nexthop_group_cmd, + "no set nexthop-group [NHGNAME$name]", + NO_STR + "Set for the PBR-MAP\n" + "nexthop-group to use\n" + "The name of the nexthop-group\n") +{ + struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); + + pbrms_clear_set_config(pbrms); return CMD_SUCCESS; } DEFPY(pbr_map_nexthop, pbr_map_nexthop_cmd, - "[no] set nexthop\ + "set nexthop\ <\ $addr [INTERFACE$intf]\ |INTERFACE$intf\ >\ - [nexthop-vrf NAME$name]", - NO_STR + [nexthop-vrf NAME$vrf_name]", "Set for the PBR-MAP\n" "Specify one of the nexthops in this map\n" "v4 Address\n" @@ -240,21 +389,15 @@ DEFPY(pbr_map_nexthop, pbr_map_nexthop_cmd, struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); struct vrf *vrf; struct nexthop nhop; - struct nexthop *nh; - - if (pbrms->nhgrp_name) { - vty_out(vty, - "Please unconfigure the nexthop group before adding an individual nexthop"); - return CMD_WARNING_CONFIG_FAILED; - } + struct nexthop *nh = NULL; - if (name) - vrf = vrf_lookup_by_name(name); + if (vrf_name) + vrf = vrf_lookup_by_name(vrf_name); else vrf = vrf_lookup_by_id(VRF_DEFAULT); if (!vrf) { - vty_out(vty, "Specified: %s is non-existent\n", name); + vty_out(vty, "Specified VRF: %s is non-existent\n", vrf_name); return CMD_WARNING_CONFIG_FAILED; } @@ -262,13 +405,24 @@ DEFPY(pbr_map_nexthop, pbr_map_nexthop_cmd, nhop.vrf_id = vrf->vrf_id; if (intf) { - nhop.ifindex = ifname2ifindex(intf, vrf->vrf_id); - if (nhop.ifindex == IFINDEX_INTERNAL) { - vty_out(vty, - "Specified Intf %s does not exist in vrf: %s\n", - intf, vrf->name); + struct interface *ifp; + + ifp = if_lookup_by_name_all_vrf(intf); + if (!ifp) { + vty_out(vty, "Specified Intf %s does not exist\n", + intf); return CMD_WARNING_CONFIG_FAILED; } + if (ifp->vrf_id != vrf->vrf_id) { + struct vrf *actual; + + actual = vrf_lookup_by_id(ifp->vrf_id); + vty_out(vty, + "Specified Intf %s is not in vrf %s but is in vrf %s, using actual vrf\n", + ifp->name, vrf->name, actual->name); + } + nhop.ifindex = ifp->ifindex; + nhop.vrf_id = ifp->vrf_id; } if (addr) { @@ -296,46 +450,21 @@ DEFPY(pbr_map_nexthop, pbr_map_nexthop_cmd, if (pbrms->nhg) nh = nexthop_exists(pbrms->nhg, &nhop); - else { - char buf[PBR_NHC_NAMELEN]; - if (no) { - vty_out(vty, "No nexthops to delete\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - pbrms->nhg = nexthop_group_new(); - pbrms->internal_nhg_name = - XSTRDUP(MTYPE_TMP, - pbr_nht_nexthop_make_name(pbrms->parent->name, - PBR_NHC_NAMELEN, - pbrms->seqno, - buf)); - nh = NULL; - } - - if (no) { - if (nh) - pbr_nht_delete_individual_nexthop(pbrms); - } else if (!nh) { - - if (pbrms->nhg->nexthop) { - vty_out(vty, - "If you would like more than one nexthop please use nexthop-groups"); - return CMD_WARNING_CONFIG_FAILED; - } + if (nh) /* Same config re-entered */ + goto done; - /* must be adding new nexthop since !no and !nexthop_exists */ - nh = nexthop_new(); + /* This is new/replacement config */ + pbrms_clear_set_config(pbrms); - memcpy(nh, &nhop, sizeof(nhop)); - nexthop_add(&pbrms->nhg->nexthop, nh); + pbr_nht_add_individual_nexthop(pbrms, &nhop); - pbr_nht_add_individual_nexthop(pbrms); - pbr_map_check(pbrms); - } + pbr_map_check(pbrms, true); - if (nhop.type == NEXTHOP_TYPE_IFINDEX) { +done: + if (nhop.type == NEXTHOP_TYPE_IFINDEX + || (nhop.type == NEXTHOP_TYPE_IPV6_IFINDEX + && IN6_IS_ADDR_LINKLOCAL(&nhop.gate.ipv6))) { struct interface *ifp; ifp = if_lookup_by_index(nhop.ifindex, nhop.vrf_id); @@ -346,9 +475,85 @@ DEFPY(pbr_map_nexthop, pbr_map_nexthop_cmd, return CMD_SUCCESS; } +DEFPY(no_pbr_map_nexthop, no_pbr_map_nexthop_cmd, + "no set nexthop\ + [<\ + $addr [INTERFACE$intf]\ + |INTERFACE$intf\ + >\ + [nexthop-vrf NAME$vrf_name]]", + NO_STR + "Set for the PBR-MAP\n" + "Specify one of the nexthops in this map\n" + "v4 Address\n" + "v6 Address\n" + "Interface to use\n" + "Interface to use\n" + "If the nexthop is in a different vrf tell us\n" + "The nexthop-vrf Name\n") +{ + struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); + + pbrms_clear_set_config(pbrms); + + return CMD_SUCCESS; +} + +DEFPY(pbr_map_vrf, pbr_map_vrf_cmd, + "set vrf ", + "Set for the PBR-MAP\n" + "Specify the VRF for this map\n" + "The VRF Name\n" + "Use the interface's VRF for lookup\n") +{ + struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); + + /* + * If an equivalent set vrf * exists, just return success. + */ + if (vrf_name && pbrms->vrf_lookup + && strncmp(pbrms->vrf_name, vrf_name, sizeof(pbrms->vrf_name)) == 0) + return CMD_SUCCESS; + else if (!vrf_name && pbrms->vrf_unchanged) /* Unchanged already set */ + return CMD_SUCCESS; + + if (vrf_name && !pbr_vrf_lookup_by_name(vrf_name)) { + vty_out(vty, "Specified: %s is non-existent\n", vrf_name); + return CMD_WARNING_CONFIG_FAILED; + } + + /* This is new/replacement config */ + pbrms_clear_set_config(pbrms); + + if (vrf_name) { + pbrms->vrf_lookup = true; + strlcpy(pbrms->vrf_name, vrf_name, sizeof(pbrms->vrf_name)); + } else + pbrms->vrf_unchanged = true; + + pbr_map_check(pbrms, true); + + return CMD_SUCCESS; +} + +DEFPY(no_pbr_map_vrf, no_pbr_map_vrf_cmd, + "no set vrf []", + NO_STR + "Set for the PBR-MAP\n" + "Specify the VRF for this map\n" + "The VRF Name\n" + "Use the interface's VRF for lookup\n") +{ + struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); + + pbrms_clear_set_config(pbrms); + + return CMD_SUCCESS; +} + DEFPY (pbr_policy, pbr_policy_cmd, - "[no] pbr-policy NAME$mapname", + "[no] pbr-policy PBRMAP$mapname", NO_STR "Policy to use\n" "Name of the pbr-map to apply\n") @@ -413,115 +618,331 @@ DEFPY (show_pbr, return CMD_SUCCESS; } +static void +pbrms_nexthop_group_write_individual_nexthop( + struct vty *vty, const struct pbr_map_sequence *pbrms) +{ + struct pbr_nexthop_group_cache find; + struct pbr_nexthop_group_cache *pnhgc; + struct pbr_nexthop_cache lookup; + struct pbr_nexthop_cache *pnhc; + + memset(&find, 0, sizeof(find)); + strlcpy(find.name, pbrms->internal_nhg_name, sizeof(find.name)); + + pnhgc = hash_lookup(pbr_nhg_hash, &find); + assert(pnhgc); + + lookup.nexthop = *pbrms->nhg->nexthop; + pnhc = hash_lookup(pnhgc->nhh, &lookup); + + nexthop_group_write_nexthop_simple( + vty, pbrms->nhg->nexthop, + pnhc->nexthop.ifindex != 0 ? pnhc->intf_name : NULL); + if (pnhc->nexthop.vrf_id != VRF_DEFAULT) + vty_out(vty, " nexthop-vrf %s", pnhc->vrf_name); + + vty_out(vty, "\n"); +} + +static void vty_show_pbrms(struct vty *vty, + const struct pbr_map_sequence *pbrms, bool detail) +{ + char buf[PREFIX_STRLEN]; + char rbuf[64]; + + if (pbrms->reason) + pbr_map_reason_string(pbrms->reason, rbuf, sizeof(rbuf)); + + vty_out(vty, " Seq: %u rule: %u\n", pbrms->seqno, pbrms->ruleno); + + if (detail) + vty_out(vty, " Installed: %" PRIu64 "(%u) Reason: %s\n", + pbrms->installed, pbrms->unique, + pbrms->reason ? rbuf : "Valid"); + else + vty_out(vty, " Installed: %s Reason: %s\n", + pbrms->installed ? "yes" : "no", + pbrms->reason ? rbuf : "Valid"); + + if (pbrms->src) + vty_out(vty, " SRC Match: %s\n", + prefix2str(pbrms->src, buf, sizeof(buf))); + if (pbrms->dst) + vty_out(vty, " DST Match: %s\n", + prefix2str(pbrms->dst, buf, sizeof(buf))); + if (pbrms->dsfield & PBR_DSFIELD_DSCP) + vty_out(vty, " DSCP Match: %u\n", + (pbrms->dsfield & PBR_DSFIELD_DSCP) >> 2); + if (pbrms->dsfield & PBR_DSFIELD_ECN) + vty_out(vty, " ECN Match: %u\n", + pbrms->dsfield & PBR_DSFIELD_ECN); + if (pbrms->mark) + vty_out(vty, " MARK Match: %u\n", pbrms->mark); + + if (pbrms->nhgrp_name) { + vty_out(vty, " Nexthop-Group: %s\n", pbrms->nhgrp_name); + + if (detail) + vty_out(vty, + " Installed: %u(%d) Tableid: %d\n", + pbrms->nhs_installed, + pbr_nht_get_installed(pbrms->nhgrp_name), + pbr_nht_get_table(pbrms->nhgrp_name)); + else + vty_out(vty, " Installed: %s Tableid: %d\n", + pbr_nht_get_installed(pbrms->nhgrp_name) ? "yes" + : "no", + pbr_nht_get_table(pbrms->nhgrp_name)); + + } else if (pbrms->nhg) { + vty_out(vty, " "); + pbrms_nexthop_group_write_individual_nexthop(vty, pbrms); + if (detail) + vty_out(vty, + " Installed: %u(%d) Tableid: %d\n", + pbrms->nhs_installed, + pbr_nht_get_installed(pbrms->internal_nhg_name), + pbr_nht_get_table(pbrms->internal_nhg_name)); + else + vty_out(vty, " Installed: %s Tableid: %d\n", + pbr_nht_get_installed(pbrms->internal_nhg_name) + ? "yes" + : "no", + pbr_nht_get_table(pbrms->internal_nhg_name)); + + } else if (pbrms->vrf_unchanged) { + vty_out(vty, " VRF Unchanged (use interface vrf)\n"); + } else if (pbrms->vrf_lookup) { + vty_out(vty, " VRF Lookup: %s\n", pbrms->vrf_name); + } else { + vty_out(vty, " Nexthop-Group: Unknown Installed: no\n"); + } +} + +static void vty_json_pbrms(json_object *j, struct vty *vty, + const struct pbr_map_sequence *pbrms) +{ + json_object *jpbrm, *nexthop_group; + char *nhg_name = pbrms->nhgrp_name ? pbrms->nhgrp_name + : pbrms->internal_nhg_name; + char buf[PREFIX_STRLEN]; + char rbuf[64]; + + jpbrm = json_object_new_object(); + + json_object_int_add(jpbrm, "id", pbrms->unique); + + if (pbrms->reason) + pbr_map_reason_string(pbrms->reason, rbuf, sizeof(rbuf)); + + json_object_int_add(jpbrm, "sequenceNumber", pbrms->seqno); + json_object_int_add(jpbrm, "ruleNumber", pbrms->ruleno); + json_object_boolean_add(jpbrm, "vrfUnchanged", pbrms->vrf_unchanged); + json_object_boolean_add(jpbrm, "installed", + pbr_nht_get_installed(nhg_name)); + json_object_string_add(jpbrm, "installedReason", + pbrms->reason ? rbuf : "Valid"); + + if (nhg_name) { + nexthop_group = json_object_new_object(); + + json_object_int_add(nexthop_group, "tableId", + pbr_nht_get_table(nhg_name)); + json_object_string_add(nexthop_group, "name", nhg_name); + json_object_boolean_add(nexthop_group, "installed", + pbr_nht_get_installed(nhg_name)); + json_object_int_add(nexthop_group, "installedInternally", + pbrms->nhs_installed); + + json_object_object_add(jpbrm, "nexthopGroup", nexthop_group); + } + + if (pbrms->vrf_lookup) + json_object_string_add(jpbrm, "vrfName", pbrms->vrf_name); + + if (pbrms->src) + json_object_string_add( + jpbrm, "matchSrc", + prefix2str(pbrms->src, buf, sizeof(buf))); + if (pbrms->dst) + json_object_string_add( + jpbrm, "matchDst", + prefix2str(pbrms->dst, buf, sizeof(buf))); + if (pbrms->mark) + json_object_int_add(jpbrm, "matchMark", pbrms->mark); + if (pbrms->dsfield & PBR_DSFIELD_DSCP) + json_object_int_add(jpbrm, "matchDscp", + (pbrms->dsfield & PBR_DSFIELD_DSCP) >> 2); + if (pbrms->dsfield & PBR_DSFIELD_ECN) + json_object_int_add(jpbrm, "matchEcn", + pbrms->dsfield & PBR_DSFIELD_ECN); + + json_object_array_add(j, jpbrm); +} + +static void vty_show_pbr_map(struct vty *vty, const struct pbr_map *pbrm, + bool detail) +{ + struct pbr_map_sequence *pbrms; + struct listnode *node; + + vty_out(vty, " pbr-map %s valid: %s\n", pbrm->name, + pbrm->valid ? "yes" : "no"); + + for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) + vty_show_pbrms(vty, pbrms, detail); +} + +static void vty_json_pbr_map(json_object *j, struct vty *vty, + const struct pbr_map *pbrm) +{ + struct pbr_map_sequence *pbrms; + struct listnode *node; + json_object *jpbrms; + + json_object_string_add(j, "name", pbrm->name); + json_object_boolean_add(j, "valid", pbrm->valid); + + jpbrms = json_object_new_array(); + + for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) + vty_json_pbrms(jpbrms, vty, pbrms); + + json_object_object_add(j, "policies", jpbrms); +} + DEFPY (show_pbr_map, show_pbr_map_cmd, - "show pbr map [NAME$name] [detail$detail]", + "show pbr map [NAME$name] [detail$detail|json$json]", SHOW_STR PBR_STR "PBR Map\n" "PBR Map Name\n" - "Detailed information\n") + "Detailed information\n" + JSON_STR) { - struct pbr_map_sequence *pbrms; struct pbr_map *pbrm; - struct listnode *node; - char buf[PREFIX_STRLEN]; - char rbuf[64]; + json_object *j = NULL; + + if (json) + j = json_object_new_array(); RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps) { + json_object *this_map = NULL; if (name && strcmp(name, pbrm->name) != 0) continue; - vty_out(vty, " pbr-map %s valid: %d\n", pbrm->name, - pbrm->valid); + if (j) + this_map = json_object_new_object(); - for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) { - if (pbrms->reason) - pbr_map_reason_string(pbrms->reason, rbuf, - sizeof(rbuf)); - vty_out(vty, - " Seq: %u rule: %u Installed: %" PRIu64 "(%u) Reason: %s\n", - pbrms->seqno, pbrms->ruleno, pbrms->installed, - pbrms->unique, pbrms->reason ? rbuf : "Valid"); - - if (pbrms->src) - vty_out(vty, "\tSRC Match: %s\n", - prefix2str(pbrms->src, buf, - sizeof(buf))); - if (pbrms->dst) - vty_out(vty, "\tDST Match: %s\n", - prefix2str(pbrms->dst, buf, - sizeof(buf))); - - if (pbrms->nhgrp_name) { - vty_out(vty, - "\tNexthop-Group: %s(%u) Installed: %u(%d)\n", - pbrms->nhgrp_name, - pbr_nht_get_table(pbrms->nhgrp_name), - pbrms->nhs_installed, - pbr_nht_get_installed( - pbrms->nhgrp_name)); - } else if (pbrms->nhg) { - vty_out(vty, " "); - nexthop_group_write_nexthop( - vty, pbrms->nhg->nexthop); - vty_out(vty, - "\tInstalled: %u(%d) Tableid: %d\n", - pbrms->nhs_installed, - pbr_nht_get_installed( - pbrms->internal_nhg_name), - pbr_nht_get_table( - pbrms->internal_nhg_name)); - } else { - vty_out(vty, - "\tNexthop-Group: Unknown Installed: 0(0)\n"); - } + if (this_map) { + vty_json_pbr_map(this_map, vty, pbrm); + + json_object_array_add(j, this_map); + continue; } + + vty_show_pbr_map(vty, pbrm, detail); } + + if (j) { + vty_out(vty, "%s\n", + json_object_to_json_string_ext( + j, JSON_C_TO_STRING_PRETTY)); + json_object_free(j); + } + return CMD_SUCCESS; } DEFPY(show_pbr_nexthop_group, show_pbr_nexthop_group_cmd, - "show pbr nexthop-groups [WORD$word]", + "show pbr nexthop-groups [WORD$word] [json$json]", SHOW_STR PBR_STR "Nexthop Groups\n" - "Optional Name of the nexthop group\n") + "Optional Name of the nexthop group\n" + JSON_STR) { - pbr_nht_show_nexthop_group(vty, word); + json_object *j = NULL; + + if (json) + j = json_object_new_array(); + + if (j) { + pbr_nht_json_nexthop_group(j, word); + + vty_out(vty, "%s\n", + json_object_to_json_string_ext( + j, JSON_C_TO_STRING_PRETTY)); + + json_object_free(j); + } else + pbr_nht_show_nexthop_group(vty, word); + return CMD_SUCCESS; } DEFPY (show_pbr_interface, show_pbr_interface_cmd, - "show pbr interface [NAME$name]", + "show pbr interface [NAME$name] [json$json]", SHOW_STR PBR_STR "PBR Interface\n" - "PBR Interface Name\n") + "PBR Interface Name\n" + JSON_STR) { struct interface *ifp; struct vrf *vrf; struct pbr_interface *pbr_ifp; + json_object *j = NULL; + + if (json) + j = json_object_new_array(); RB_FOREACH(vrf, vrf_name_head, &vrfs_by_name) { FOR_ALL_INTERFACES(vrf, ifp) { struct pbr_map *pbrm; + json_object *this_iface = NULL; + + if (j) + this_iface = json_object_new_object(); - if (!ifp->info) + if (!ifp->info) { + json_object_free(this_iface); continue; + } - if (name && strcmp(ifp->name, name) != 0) + if (name && strcmp(ifp->name, name) != 0) { + json_object_free(this_iface); continue; + } pbr_ifp = ifp->info; - if (strcmp(pbr_ifp->mapname, "") == 0) + if (strcmp(pbr_ifp->mapname, "") == 0) { + json_object_free(this_iface); continue; + } pbrm = pbrm_find(pbr_ifp->mapname); + + if (this_iface) { + json_object_string_add(this_iface, "name", + ifp->name); + json_object_int_add(this_iface, "index", + ifp->ifindex); + json_object_string_add(this_iface, "policy", + pbr_ifp->mapname); + json_object_boolean_add(this_iface, "valid", + pbrm); + + json_object_array_add(j, this_iface); + continue; + } + vty_out(vty, " %s(%d) with pbr-policy %s", ifp->name, ifp->ifindex, pbr_ifp->mapname); if (!pbrm) @@ -530,12 +951,24 @@ DEFPY (show_pbr_interface, } } + if (j) { + vty_out(vty, "%s\n", + json_object_to_json_string_ext( + j, JSON_C_TO_STRING_PRETTY)); + json_object_free(j); + } + return CMD_SUCCESS; } /* PBR debugging CLI ------------------------------------------------------- */ -static struct cmd_node debug_node = {DEBUG_NODE, "", 1}; +static struct cmd_node debug_node = { + .name = "debug", + .node = DEBUG_NODE, + .prompt = "", + .config_write = pbr_debug_config_write, +}; DEFPY(debug_pbr, debug_pbr_cmd, @@ -583,8 +1016,13 @@ DEFUN_NOSH(show_debugging_pbr, /* ------------------------------------------------------------------------- */ +static int pbr_interface_config_write(struct vty *vty); static struct cmd_node interface_node = { - INTERFACE_NODE, "%s(config-if)# ", 1 /* vtysh ? yes */ + .name = "interface", + .node = INTERFACE_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-if)# ", + .config_write = pbr_interface_config_write, }; static int pbr_interface_config_write(struct vty *vty) @@ -612,8 +1050,15 @@ static int pbr_interface_config_write(struct vty *vty) return 1; } +static int pbr_vty_map_config_write(struct vty *vty); /* PBR map node structure. */ -static struct cmd_node pbr_map_node = {PBRMAP_NODE, "%s(config-pbr-map)# ", 1}; +static struct cmd_node pbr_map_node = { + .name = "pbr-map", + .node = PBRMAP_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-pbr-map)# ", + .config_write = pbr_vty_map_config_write, +}; static int pbr_vty_map_config_write_sequence(struct vty *vty, struct pbr_map *pbrm, @@ -631,12 +1076,29 @@ static int pbr_vty_map_config_write_sequence(struct vty *vty, vty_out(vty, " match dst-ip %s\n", prefix2str(pbrms->dst, buff, sizeof(buff))); + if (pbrms->dsfield & PBR_DSFIELD_DSCP) + vty_out(vty, " match dscp %u\n", + (pbrms->dsfield & PBR_DSFIELD_DSCP) >> 2); + + if (pbrms->dsfield & PBR_DSFIELD_ECN) + vty_out(vty, " match ecn %u\n", + pbrms->dsfield & PBR_DSFIELD_ECN); + + if (pbrms->mark) + vty_out(vty, " match mark %u\n", pbrms->mark); + + if (pbrms->vrf_unchanged) + vty_out(vty, " set vrf unchanged\n"); + + if (pbrms->vrf_lookup) + vty_out(vty, " set vrf %s\n", pbrms->vrf_name); + if (pbrms->nhgrp_name) vty_out(vty, " set nexthop-group %s\n", pbrms->nhgrp_name); if (pbrms->nhg) { vty_out(vty, " set "); - nexthop_group_write_nexthop(vty, pbrms->nhg->nexthop); + pbrms_nexthop_group_write_individual_nexthop(vty, pbrms); } vty_out(vty, "!\n"); @@ -661,31 +1123,56 @@ static int pbr_vty_map_config_write(struct vty *vty) return 1; } +static void pbr_map_completer(vector comps, struct cmd_token *token) +{ + struct pbr_map *pbrm; + + RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps) + vector_set(comps, XSTRDUP(MTYPE_COMPLETION, pbrm->name)); +} + +static const struct cmd_variable_handler pbr_map_name[] = { + { + .tokenname = "PBRMAP", .completions = pbr_map_completer, + }, + { + .completions = NULL + } +}; + void pbr_vty_init(void) { - install_node(&interface_node, - pbr_interface_config_write); + cmd_variable_handler_register(pbr_map_name); + + install_node(&interface_node); if_cmd_init(); - install_node(&pbr_map_node, - pbr_vty_map_config_write); + install_node(&pbr_map_node); /* debug */ - install_node(&debug_node, pbr_debug_config_write); - install_element(VIEW_NODE, &debug_pbr_cmd); + install_node(&debug_node); + install_element(ENABLE_NODE, &debug_pbr_cmd); install_element(CONFIG_NODE, &debug_pbr_cmd); - install_element(VIEW_NODE, &show_debugging_pbr_cmd); + install_element(ENABLE_NODE, &show_debugging_pbr_cmd); install_default(PBRMAP_NODE); install_element(CONFIG_NODE, &pbr_map_cmd); install_element(CONFIG_NODE, &no_pbr_map_cmd); install_element(CONFIG_NODE, &pbr_set_table_range_cmd); + install_element(CONFIG_NODE, &no_pbr_set_table_range_cmd); install_element(INTERFACE_NODE, &pbr_policy_cmd); install_element(PBRMAP_NODE, &pbr_map_match_src_cmd); install_element(PBRMAP_NODE, &pbr_map_match_dst_cmd); + install_element(PBRMAP_NODE, &pbr_map_match_dscp_cmd); + install_element(PBRMAP_NODE, &pbr_map_match_ecn_cmd); + install_element(PBRMAP_NODE, &pbr_map_match_mark_cmd); install_element(PBRMAP_NODE, &pbr_map_nexthop_group_cmd); + install_element(PBRMAP_NODE, &no_pbr_map_nexthop_group_cmd); install_element(PBRMAP_NODE, &pbr_map_nexthop_cmd); + install_element(PBRMAP_NODE, &no_pbr_map_nexthop_cmd); + install_element(PBRMAP_NODE, &pbr_map_vrf_cmd); + install_element(PBRMAP_NODE, &no_pbr_map_vrf_cmd); install_element(VIEW_NODE, &show_pbr_cmd); install_element(VIEW_NODE, &show_pbr_map_cmd); install_element(VIEW_NODE, &show_pbr_interface_cmd); diff --git a/pbrd/pbr_zebra.c b/pbrd/pbr_zebra.c index 4f8f50556b..866f27136e 100644 --- a/pbrd/pbr_zebra.c +++ b/pbrd/pbr_zebra.c @@ -39,6 +39,7 @@ #include "pbr_memory.h" #include "pbr_zebra.h" #include "pbr_debug.h" +#include "pbr_vrf.h" DEFINE_MTYPE_STATIC(PBRD, PBR_INTERFACE, "PBR Interface") @@ -59,115 +60,104 @@ struct pbr_interface *pbr_if_new(struct interface *ifp) } /* Inteface addition message from zebra. */ -static int interface_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +int pbr_ifp_create(struct interface *ifp) { - struct interface *ifp; - - ifp = zebra_interface_add_read(zclient->ibuf, vrf_id); - - if (!ifp) - return 0; - - DEBUGD(&pbr_dbg_zebra, - "%s: %s", __PRETTY_FUNCTION__, ifp->name); + DEBUGD(&pbr_dbg_zebra, "%s: %s", __func__, ifp->name); if (!ifp->info) pbr_if_new(ifp); + pbr_nht_interface_update(ifp); + /* Update nexthops tracked from a `set nexthop` command */ pbr_nht_nexthop_interface_update(ifp); + pbr_map_policy_interface_update(ifp, true); + return 0; } -static int interface_delete(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +int pbr_ifp_destroy(struct interface *ifp) { - struct interface *ifp; - struct stream *s; - - s = zclient->ibuf; - /* zebra_interface_state_read () updates interface structure in iflist - */ - ifp = zebra_interface_state_read(s, vrf_id); - - if (ifp == NULL) - return 0; - - DEBUGD(&pbr_dbg_zebra, - "%s: %s", __PRETTY_FUNCTION__, ifp->name); + DEBUGD(&pbr_dbg_zebra, "%s: %s", __func__, ifp->name); - if_set_index(ifp, IFINDEX_INTERNAL); + pbr_map_policy_interface_update(ifp, false); return 0; } -static int interface_address_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int interface_address_add(ZAPI_CALLBACK_ARGS) { struct connected *c; char buf[PREFIX_STRLEN]; - c = zebra_interface_address_read(command, zclient->ibuf, vrf_id); + c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); - DEBUGD(&pbr_dbg_zebra, - "%s: %s added %s", __PRETTY_FUNCTION__, c->ifp->name, - prefix2str(c->address, buf, sizeof(buf))); + DEBUGD(&pbr_dbg_zebra, "%s: %s added %s", __func__, + c ? c->ifp->name : "Unknown", + c ? prefix2str(c->address, buf, sizeof(buf)) : "Unknown"); return 0; } -static int interface_address_delete(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int interface_address_delete(ZAPI_CALLBACK_ARGS) { struct connected *c; char buf[PREFIX_STRLEN]; - c = zebra_interface_address_read(command, zclient->ibuf, vrf_id); + c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); if (!c) return 0; - DEBUGD(&pbr_dbg_zebra, - "%s: %s deleted %s", __PRETTY_FUNCTION__, c->ifp->name, + DEBUGD(&pbr_dbg_zebra, "%s: %s deleted %s", __func__, c->ifp->name, prefix2str(c->address, buf, sizeof(buf))); - connected_free(c); + connected_free(&c); return 0; } -static int interface_state_up(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +int pbr_ifp_up(struct interface *ifp) { - struct interface *ifp; + DEBUGD(&pbr_dbg_zebra, "%s: %s is up", __func__, ifp->name); - ifp = zebra_interface_state_read(zclient->ibuf, vrf_id); + pbr_nht_nexthop_interface_update(ifp); - DEBUGD(&pbr_dbg_zebra, - "%s: %s is up", __PRETTY_FUNCTION__, ifp->name); + return 0; +} + +int pbr_ifp_down(struct interface *ifp) +{ + DEBUGD(&pbr_dbg_zebra, "%s: %s is down", __func__, ifp->name); pbr_nht_nexthop_interface_update(ifp); return 0; } -static int interface_state_down(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int interface_vrf_update(ZAPI_CALLBACK_ARGS) { struct interface *ifp; + vrf_id_t new_vrf_id; - ifp = zebra_interface_state_read(zclient->ibuf, vrf_id); + ifp = zebra_interface_vrf_update_read(zclient->ibuf, vrf_id, + &new_vrf_id); - DEBUGD(&pbr_dbg_zebra, - "%s: %s is down", __PRETTY_FUNCTION__, ifp->name); + if (!ifp) { + DEBUGD(&pbr_dbg_zebra, "%s: VRF change interface not found", + __func__); - pbr_nht_nexthop_interface_update(ifp); + return 0; + } + + DEBUGD(&pbr_dbg_zebra, "%s: %s VRF change %u -> %u", __func__, + ifp->name, vrf_id, new_vrf_id); + + if_update_to_new_vrf(ifp, new_vrf_id); return 0; } -static int route_notify_owner(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int route_notify_owner(ZAPI_CALLBACK_ARGS) { struct prefix p; enum zapi_route_notify_owner note; @@ -182,56 +172,55 @@ static int route_notify_owner(int command, struct zclient *zclient, switch (note) { case ZAPI_ROUTE_FAIL_INSTALL: DEBUGD(&pbr_dbg_zebra, - "%s: [%s] Route install failure for table: %u", - __PRETTY_FUNCTION__, buf, table_id); + "%s: [%s] Route install failure for table: %u", __func__, + buf, table_id); break; case ZAPI_ROUTE_BETTER_ADMIN_WON: DEBUGD(&pbr_dbg_zebra, "%s: [%s] Route better admin distance won for table: %u", - __PRETTY_FUNCTION__, buf, table_id); + __func__, buf, table_id); break; case ZAPI_ROUTE_INSTALLED: DEBUGD(&pbr_dbg_zebra, "%s: [%s] Route installed succeeded for table: %u", - __PRETTY_FUNCTION__, buf, table_id); + __func__, buf, table_id); pbr_nht_route_installed_for_table(table_id); break; case ZAPI_ROUTE_REMOVED: DEBUGD(&pbr_dbg_zebra, "%s: [%s] Route Removed succeeded for table: %u", - __PRETTY_FUNCTION__, buf, table_id); + __func__, buf, table_id); pbr_nht_route_removed_for_table(table_id); break; case ZAPI_ROUTE_REMOVE_FAIL: DEBUGD(&pbr_dbg_zebra, - "%s: [%s] Route remove fail for table: %u", - __PRETTY_FUNCTION__, buf, table_id); + "%s: [%s] Route remove fail for table: %u", __func__, + buf, table_id); break; } return 0; } -static int rule_notify_owner(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int rule_notify_owner(ZAPI_CALLBACK_ARGS) { uint32_t seqno, priority, unique; enum zapi_rule_notify_owner note; struct pbr_map_sequence *pbrms; struct pbr_map_interface *pmi; - ifindex_t ifi; + char ifname[INTERFACE_NAMSIZ + 1]; uint64_t installed; if (!zapi_rule_notify_decode(zclient->ibuf, &seqno, &priority, &unique, - &ifi, ¬e)) + ifname, ¬e)) return -1; pmi = NULL; - pbrms = pbrms_lookup_unique(unique, ifi, &pmi); + pbrms = pbrms_lookup_unique(unique, ifname, &pmi); if (!pbrms) { DEBUGD(&pbr_dbg_zebra, - "%s: Failure to lookup pbrms based upon %u", - __PRETTY_FUNCTION__, unique); + "%s: Failure to lookup pbrms based upon %u", __func__, + unique); return 0; } @@ -240,23 +229,21 @@ static int rule_notify_owner(int command, struct zclient *zclient, switch (note) { case ZAPI_RULE_FAIL_INSTALL: pbrms->installed &= ~installed; - DEBUGD(&pbr_dbg_zebra, - "%s: Received RULE_FAIL_INSTALL: %" PRIu64, - __PRETTY_FUNCTION__, pbrms->installed); break; case ZAPI_RULE_INSTALLED: pbrms->installed |= installed; - DEBUGD(&pbr_dbg_zebra, "%s: Received RULE_INSTALLED: %" PRIu64, - __PRETTY_FUNCTION__, pbrms->installed); break; case ZAPI_RULE_FAIL_REMOVE: + /* Don't change state on rule removal failure */ + break; case ZAPI_RULE_REMOVED: pbrms->installed &= ~installed; - DEBUGD(&pbr_dbg_zebra, "%s: Received RULE REMOVED: %" PRIu64, - __PRETTY_FUNCTION__, pbrms->installed); break; } + DEBUGD(&pbr_dbg_zebra, "%s: Received %s: %" PRIu64, __func__, + zapi_rule_notify_owner2str(note), pbrms->installed); + pbr_map_final_interface_deletion(pbrms->parent, pmi); return 0; @@ -264,8 +251,7 @@ static int rule_notify_owner(int command, struct zclient *zclient, static void zebra_connected(struct zclient *zclient) { - DEBUGD(&pbr_dbg_zebra, "%s: Registering for fun and profit", - __PRETTY_FUNCTION__); + DEBUGD(&pbr_dbg_zebra, "%s: Registering for fun and profit", __func__); zclient_send_reg_requests(zclient, VRF_DEFAULT); } @@ -287,6 +273,7 @@ static void route_add_helper(struct zapi_route *api, struct nexthop_group nhg, api_nh = &api->nexthops[i]; api_nh->vrf_id = nhop->vrf_id; api_nh->type = nhop->type; + api_nh->weight = nhop->weight; switch (nhop->type) { case NEXTHOP_TYPE_IPV4: api_nh->gate.ipv4 = nhop->gate.ipv4; @@ -325,8 +312,7 @@ void route_add(struct pbr_nexthop_group_cache *pnhgc, struct nexthop_group nhg, { struct zapi_route api; - DEBUGD(&pbr_dbg_zebra, "%s for Table: %d", __PRETTY_FUNCTION__, - pnhgc->table_id); + DEBUGD(&pbr_dbg_zebra, "%s for Table: %d", __func__, pnhgc->table_id); memset(&api, 0, sizeof(api)); @@ -354,7 +340,11 @@ void route_add(struct pbr_nexthop_group_cache *pnhgc, struct nexthop_group nhg, case AFI_L2VPN: DEBUGD(&pbr_dbg_zebra, "%s: Asked to install unsupported route type: L2VPN", - __PRETTY_FUNCTION__); + __func__); + break; + case AFI_UNSPEC: + DEBUGD(&pbr_dbg_zebra, + "%s: Asked to install unspecified route type", __func__); break; } } @@ -367,8 +357,7 @@ void route_delete(struct pbr_nexthop_group_cache *pnhgc, afi_t afi) { struct zapi_route api; - DEBUGD(&pbr_dbg_zebra, "%s for Table: %d", __PRETTY_FUNCTION__, - pnhgc->table_id); + DEBUGD(&pbr_dbg_zebra, "%s for Table: %d", __func__, pnhgc->table_id); memset(&api, 0, sizeof(api)); api.vrf_id = VRF_DEFAULT; @@ -396,36 +385,38 @@ void route_delete(struct pbr_nexthop_group_cache *pnhgc, afi_t afi) case AFI_L2VPN: DEBUGD(&pbr_dbg_zebra, "%s: Asked to delete unsupported route type: L2VPN", - __PRETTY_FUNCTION__); + __func__); + break; + case AFI_UNSPEC: + DEBUGD(&pbr_dbg_zebra, + "%s: Asked to delete unspecified route type", __func__); break; } } -static int pbr_zebra_nexthop_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int pbr_zebra_nexthop_update(ZAPI_CALLBACK_ARGS) { struct zapi_route nhr; char buf[PREFIX2STR_BUFFER]; uint32_t i; if (!zapi_nexthop_update_decode(zclient->ibuf, &nhr)) { - zlog_warn("Failure to decode Nexthop update message"); + zlog_err("Failure to decode Nexthop update message"); return 0; } if (DEBUG_MODE_CHECK(&pbr_dbg_zebra, DEBUG_MODE_ALL)) { DEBUGD(&pbr_dbg_zebra, "%s: Received Nexthop update: %s", - __PRETTY_FUNCTION__, - prefix2str(&nhr.prefix, buf, sizeof(buf))); + __func__, prefix2str(&nhr.prefix, buf, sizeof(buf))); - DEBUGD(&pbr_dbg_zebra, "%s: (\tNexthops(%u)", - __PRETTY_FUNCTION__, nhr.nexthop_num); + DEBUGD(&pbr_dbg_zebra, "%s: (\tNexthops(%u)", __func__, + nhr.nexthop_num); for (i = 0; i < nhr.nexthop_num; i++) { DEBUGD(&pbr_dbg_zebra, "%s: \tType: %d: vrf: %d, ifindex: %d gate: %s", - __PRETTY_FUNCTION__, nhr.nexthops[i].type, + __func__, nhr.nexthops[i].type, nhr.nexthops[i].vrf_id, nhr.nexthops[i].ifindex, inet_ntoa(nhr.nexthops[i].gate.ipv4)); } @@ -445,12 +436,9 @@ void pbr_zebra_init(void) zclient_init(zclient, ZEBRA_ROUTE_PBR, 0, &pbr_privs); zclient->zebra_connected = zebra_connected; - zclient->interface_add = interface_add; - zclient->interface_delete = interface_delete; - zclient->interface_up = interface_state_up; - zclient->interface_down = interface_state_down; zclient->interface_address_add = interface_address_add; zclient->interface_address_delete = interface_address_delete; + zclient->interface_vrf_update = interface_vrf_update; zclient->route_notify_owner = route_notify_owner; zclient->rule_notify_owner = rule_notify_owner; zclient->nexthop_update = pbr_zebra_nexthop_update; @@ -480,13 +468,18 @@ void pbr_send_rnh(struct nexthop *nhop, bool reg) p.family = AF_INET6; memcpy(&p.u.prefix6, &nhop->gate.ipv6, 16); p.prefixlen = 128; + if (IN6_IS_ADDR_LINKLOCAL(&nhop->gate.ipv6)) + /* + * Don't bother tracking link locals, just track their + * interface state. + */ + return; break; } if (zclient_send_rnh(zclient, command, &p, false, nhop->vrf_id) < 0) { - zlog_warn("%s: Failure to send nexthop to zebra", - __PRETTY_FUNCTION__); + zlog_warn("%s: Failure to send nexthop to zebra", __func__); } } @@ -507,6 +500,26 @@ static void pbr_encode_pbr_map_sequence_prefix(struct stream *s, stream_put(s, &p->u.prefix, prefix_blen(p)); } +static void +pbr_encode_pbr_map_sequence_vrf(struct stream *s, + const struct pbr_map_sequence *pbrms, + const struct interface *ifp) +{ + struct pbr_vrf *pbr_vrf; + + if (pbrms->vrf_unchanged) + pbr_vrf = pbr_vrf_lookup_by_id(ifp->vrf_id); + else + pbr_vrf = pbr_vrf_lookup_by_name(pbrms->vrf_name); + + if (!pbr_vrf) { + DEBUGD(&pbr_dbg_zebra, "%s: VRF not found", __func__); + return; + } + + stream_putl(s, pbr_vrf->vrf->data.l.table_id); +} + static void pbr_encode_pbr_map_sequence(struct stream *s, struct pbr_map_sequence *pbrms, struct interface *ifp) @@ -524,16 +537,20 @@ static void pbr_encode_pbr_map_sequence(struct stream *s, stream_putw(s, 0); /* src port */ pbr_encode_pbr_map_sequence_prefix(s, pbrms->dst, family); stream_putw(s, 0); /* dst port */ - stream_putl(s, 0); /* fwmark */ - if (pbrms->nhgrp_name) + stream_putc(s, pbrms->dsfield); + stream_putl(s, pbrms->mark); + + if (pbrms->vrf_unchanged || pbrms->vrf_lookup) + pbr_encode_pbr_map_sequence_vrf(s, pbrms, ifp); + else if (pbrms->nhgrp_name) stream_putl(s, pbr_nht_get_table(pbrms->nhgrp_name)); else if (pbrms->nhg) stream_putl(s, pbr_nht_get_table(pbrms->internal_nhg_name)); - stream_putl(s, ifp->ifindex); + stream_put(s, ifp->name, INTERFACE_NAMSIZ); } -void pbr_send_pbr_map(struct pbr_map_sequence *pbrms, - struct pbr_map_interface *pmi, bool install) +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; struct stream *s; @@ -541,19 +558,21 @@ void pbr_send_pbr_map(struct pbr_map_sequence *pbrms, is_installed &= pbrms->installed; - DEBUGD(&pbr_dbg_zebra, "%s: for %s %d(%" PRIu64 ")", - __PRETTY_FUNCTION__, pbrm->name, install, is_installed); + DEBUGD(&pbr_dbg_zebra, "%s: for %s %d(%" PRIu64 ")", __func__, + pbrm->name, install, is_installed); /* - * If we are installed and asked to do so again - * just return. If we are not installed and asked - * and asked to delete just return; + * If we are installed and asked to do so again and the config + * has not changed, just return. + * + * If we are not installed and asked + * to delete just return. */ - if (install && is_installed) - return; + if (install && is_installed && !changed) + return false; if (!install && !is_installed) - return; + return false; s = zclient->obuf; stream_reset(s); @@ -567,13 +586,15 @@ void pbr_send_pbr_map(struct pbr_map_sequence *pbrms, */ stream_putl(s, 1); - DEBUGD(&pbr_dbg_zebra, "%s: \t%s %s %d %s %u", - __PRETTY_FUNCTION__, install ? "Installing" : "Deleting", - pbrm->name, install, pmi->ifp->name, pmi->delete); + DEBUGD(&pbr_dbg_zebra, "%s: \t%s %s seq %u %d %s %u", __func__, + install ? "Installing" : "Deleting", pbrm->name, pbrms->seqno, + install, pmi->ifp->name, pmi->delete); pbr_encode_pbr_map_sequence(s, pbrms, pmi->ifp); 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 4cbefe2636..e8f9bff5d9 100644 --- a/pbrd/pbr_zebra.h +++ b/pbrd/pbr_zebra.h @@ -35,8 +35,15 @@ 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, - struct pbr_map_interface *pmi, bool install); +extern bool pbr_send_pbr_map(struct pbr_map_sequence *pbrms, + struct pbr_map_interface *pmi, bool install, + bool changed); extern struct pbr_interface *pbr_if_new(struct interface *ifp); + +extern int pbr_ifp_create(struct interface *ifp); +extern int pbr_ifp_up(struct interface *ifp); +extern int pbr_ifp_down(struct interface *ifp); +extern int pbr_ifp_destroy(struct interface *ifp); + #endif diff --git a/pbrd/pbrd.conf.sample b/pbrd/pbrd.conf.sample index bb1c2edca8..c9e7dce01f 100644 --- a/pbrd/pbrd.conf.sample +++ b/pbrd/pbrd.conf.sample @@ -1,3 +1,19 @@ +! Sample pbrd configuration file +! +! A quick example of what a pbr configuration might look like ! ! log stdout +! +! nexthop-group TEST +! nexthop 4.5.6.7 +! nexthop 5.6.7.8 +! ! +! pbr-map BLUE seq 100 +! match dst-ip 9.9.9.0/24 +! match src-ip 10.10.10.0/24 +! set nexthop-group TEST +! ! +! int swp1 +! pbr-policy BLUE +! diff --git a/pbrd/subdir.am b/pbrd/subdir.am index 0f2e7ad8bd..42f279988b 100644 --- a/pbrd/subdir.am +++ b/pbrd/subdir.am @@ -7,10 +7,10 @@ noinst_LIBRARIES += pbrd/libpbr.a sbin_PROGRAMS += pbrd/pbrd dist_examples_DATA += pbrd/pbrd.conf.sample vtysh_scan += \ - $(top_srcdir)/pbrd/pbr_vty.c \ - $(top_srcdir)/pbrd/pbr_debug.c \ + pbrd/pbr_vty.c \ + pbrd/pbr_debug.c \ # end -man8 += $(MANBUILD)/pbrd.8 +man8 += $(MANBUILD)/frr-pbrd.8 endif pbrd_libpbr_a_SOURCES = \ @@ -20,6 +20,7 @@ pbrd_libpbr_a_SOURCES = \ pbrd/pbr_memory.c \ pbrd/pbr_nht.c \ pbrd/pbr_debug.c \ + pbrd/pbr_vrf.c \ # end noinst_HEADERS += \ @@ -29,13 +30,13 @@ noinst_HEADERS += \ pbrd/pbr_vty.h \ pbrd/pbr_zebra.h \ pbrd/pbr_debug.h \ + pbrd/pbr_vrf.h \ # end -pbrd/pbr_vty_clippy.c: $(CLIPPY_DEPS) -pbrd/pbr_vty.$(OBJEXT): pbrd/pbr_vty_clippy.c - -pbrd/pbr_debug_clippy.c: $(CLIPPY_DEPS) -pbrd/pbr_debug.$(OBJEXT): pbrd/pbr_debug_clippy.c +clippy_scan += \ + pbrd/pbr_debug.c \ + pbrd/pbr_vty.c \ + # end pbrd_pbrd_SOURCES = pbrd/pbr_main.c pbrd_pbrd_LDADD = pbrd/libpbr.a lib/libfrr.la $(LIBCAP) diff --git a/pimd/README b/pimd/README index 3d03979a9a..1db0aad83c 100644 --- a/pimd/README +++ b/pimd/README @@ -33,7 +33,7 @@ HOME SITE qpimd lives at: - https://github.com/freerangerouting/frr + https://github.com/frrouting/frr PLATFORMS @@ -57,7 +57,7 @@ SUPPORT Please post comments, questions, patches, bug reports at the support site: - https://freerangerouting/frr + https://frrouting.org/frr RELATED WORK diff --git a/pimd/mtracebis.c b/pimd/mtracebis.c index 65c495eff0..1b812de92c 100644 --- a/pimd/mtracebis.c +++ b/pimd/mtracebis.c @@ -17,9 +17,7 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif +#include #ifdef __linux__ @@ -110,7 +108,7 @@ static const char *rtg_proto_str(enum mtrace_rtg_proto proto) case MTRACE_RTG_PROTO_PIM_ASSERT: return "PIM assert"; default: - sprintf(buf, "unknown protocol (%d)", proto); + snprintf(buf, sizeof(buf), "unknown protocol (%d)", proto); return buf; } } @@ -163,7 +161,7 @@ static const char *fwd_code_str(enum mtrace_fwd_code code) case MTRACE_FWD_CODE_ADMIN_PROHIB: return "admin. prohib."; default: - sprintf(buf, "unknown fwd. code (%d)", code); + snprintf(buf, sizeof(buf), "unknown fwd. code (%d)", code); return buf; } } @@ -450,7 +448,7 @@ int main(int argc, char *const argv[]) exit(EXIT_FAILURE); } - mc_group.s_addr = 0; + mc_group.s_addr = INADDR_ANY; not_group = false; if (argc == 3) { diff --git a/pimd/pim_assert.c b/pimd/pim_assert.c index 0a450834e3..957f904714 100644 --- a/pimd/pim_assert.c +++ b/pimd/pim_assert.c @@ -56,7 +56,7 @@ void pim_ifassert_winner_set(struct pim_ifchannel *ch, if (ch->ifassert_state != new_state) { zlog_debug( "%s: (S,G)=%s assert state changed from %s to %s on interface %s", - __PRETTY_FUNCTION__, ch->sg_str, + __func__, ch->sg_str, pim_ifchannel_ifassert_name(ch->ifassert_state), pim_ifchannel_ifassert_name(new_state), ch->interface->name); @@ -71,8 +71,8 @@ void pim_ifassert_winner_set(struct pim_ifchannel *ch, sizeof(winner_str)); zlog_debug( "%s: (S,G)=%s assert winner changed from %s to %s on interface %s", - __PRETTY_FUNCTION__, ch->sg_str, was_str, - winner_str, ch->interface->name); + __func__, ch->sg_str, was_str, winner_str, + ch->interface->name); } } /* PIM_DEBUG_PIM_EVENTS */ @@ -130,7 +130,7 @@ static void if_could_assert_do_a1(const char *caller, struct pim_ifchannel *ch) if (assert_action_a1(ch)) { zlog_warn( "%s: %s: (S,G)=%s assert_action_a1 failure on interface %s", - __PRETTY_FUNCTION__, caller, ch->sg_str, + __func__, caller, ch->sg_str, ch->interface->name); /* log warning only */ } @@ -148,23 +148,17 @@ static int dispatch_assert(struct interface *ifp, struct in_addr source_addr, sg.src = source_addr; sg.grp = group_addr; ch = pim_ifchannel_add(ifp, &sg, 0, 0); - if (!ch) { - zlog_warn( - "%s: (S,G)=%s failure creating channel on interface %s", - __PRETTY_FUNCTION__, pim_str_sg_dump(&sg), ifp->name); - return -1; - } switch (ch->ifassert_state) { case PIM_IFASSERT_NOINFO: if (recv_metric.rpt_bit_flag) { /* RPT bit set */ - if_could_assert_do_a1(__PRETTY_FUNCTION__, ch); + if_could_assert_do_a1(__func__, ch); } else { /* RPT bit clear */ if (inferior_assert(&ch->ifassert_my_metric, &recv_metric)) { - if_could_assert_do_a1(__PRETTY_FUNCTION__, ch); + if_could_assert_do_a1(__func__, ch); } else if (acceptable_assert(&ch->ifassert_my_metric, &recv_metric)) { if (PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED( @@ -211,8 +205,7 @@ static int dispatch_assert(struct interface *ifp, struct in_addr source_addr, default: { zlog_warn( "%s: (S,G)=%s invalid assert state %d on interface %s", - __PRETTY_FUNCTION__, ch->sg_str, ch->ifassert_state, - ifp->name); + __func__, ch->sg_str, ch->ifassert_state, ifp->name); } return -2; } @@ -231,7 +224,7 @@ int pim_assert_recv(struct interface *ifp, struct pim_neighbor *neigh, int curr_size; struct pim_interface *pim_ifp = NULL; - on_trace(__PRETTY_FUNCTION__, ifp, src_addr); + on_trace(__func__, ifp, src_addr); curr = buf; curr_size = buf_size; @@ -245,7 +238,7 @@ int pim_assert_recv(struct interface *ifp, struct pim_neighbor *neigh, char src_str[INET_ADDRSTRLEN]; pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); zlog_warn("%s: pim_parse_addr_group() failure: from %s on %s", - __PRETTY_FUNCTION__, src_str, ifp->name); + __func__, src_str, ifp->name); return -1; } curr += offset; @@ -259,18 +252,18 @@ int pim_assert_recv(struct interface *ifp, struct pim_neighbor *neigh, char src_str[INET_ADDRSTRLEN]; pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); zlog_warn("%s: pim_parse_addr_ucast() failure: from %s on %s", - __PRETTY_FUNCTION__, src_str, ifp->name); + __func__, src_str, ifp->name); return -2; } curr += offset; curr_size -= offset; - if (curr_size != 8) { + if (curr_size < 8) { char src_str[INET_ADDRSTRLEN]; pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); zlog_warn( - "%s: preference/metric size is not 8: size=%d from %s on interface %s", - __PRETTY_FUNCTION__, curr_size, src_str, ifp->name); + "%s: preference/metric size is less than 8 bytes: size=%d from %s on interface %s", + __func__, curr_size, src_str, ifp->name); return -3; } @@ -303,9 +296,8 @@ int pim_assert_recv(struct interface *ifp, struct pim_neighbor *neigh, pim_inet4_dump("", sg.grp, group_str, sizeof(group_str)); zlog_debug( "%s: from %s on %s: (S,G)=(%s,%s) pref=%u metric=%u rpt_bit=%u", - __PRETTY_FUNCTION__, neigh_str, ifp->name, source_str, - group_str, msg_metric.metric_preference, - msg_metric.route_metric, + __func__, neigh_str, ifp->name, source_str, group_str, + msg_metric.metric_preference, msg_metric.route_metric, PIM_FORCE_BOOLEAN(msg_metric.rpt_bit_flag)); } @@ -386,7 +378,7 @@ int pim_assert_build_msg(uint8_t *pim_msg, int buf_size, struct interface *ifp, sizeof(group_str)); zlog_warn( "%s: failure encoding group address %s: space left=%d", - __PRETTY_FUNCTION__, group_str, remain); + __func__, group_str, remain); return -1; } @@ -400,7 +392,7 @@ int pim_assert_build_msg(uint8_t *pim_msg, int buf_size, struct interface *ifp, sizeof(source_str)); zlog_warn( "%s: failure encoding source address %s: space left=%d", - __PRETTY_FUNCTION__, source_str, remain); + __func__, source_str, remain); return -2; } @@ -418,7 +410,7 @@ int pim_assert_build_msg(uint8_t *pim_msg, int buf_size, struct interface *ifp, Add PIM header */ pim_msg_size = pim_msg_curr - pim_msg; - pim_msg_build_header(pim_msg, pim_msg_size, PIM_MSG_TYPE_ASSERT); + pim_msg_build_header(pim_msg, pim_msg_size, PIM_MSG_TYPE_ASSERT, false); return pim_msg_size; } @@ -435,7 +427,7 @@ static int pim_assert_do(struct pim_ifchannel *ch, if (!ifp) { if (PIM_DEBUG_PIM_TRACE) zlog_debug("%s: channel%s has no associated interface!", - __PRETTY_FUNCTION__, ch->sg_str); + __func__, ch->sg_str); return -1; } pim_ifp = ifp->info; @@ -443,7 +435,7 @@ static int pim_assert_do(struct pim_ifchannel *ch, if (PIM_DEBUG_PIM_TRACE) zlog_debug( "%s: channel %s pim not enabled on interface: %s", - __PRETTY_FUNCTION__, ch->sg_str, ifp->name); + __func__, ch->sg_str, ifp->name); return -1; } @@ -454,7 +446,7 @@ static int pim_assert_do(struct pim_ifchannel *ch, if (pim_msg_size < 1) { zlog_warn( "%s: failure building PIM assert message: msg_size=%d", - __PRETTY_FUNCTION__, pim_msg_size); + __func__, pim_msg_size); return -2; } @@ -471,7 +463,7 @@ static int pim_assert_do(struct pim_ifchannel *ch, if (PIM_DEBUG_PIM_TRACE) { zlog_debug("%s: to %s: (S,G)=%s pref=%u metric=%u rpt_bit=%u", - __PRETTY_FUNCTION__, ifp->name, ch->sg_str, + __func__, ifp->name, ch->sg_str, metric.metric_preference, metric.route_metric, PIM_FORCE_BOOLEAN(metric.rpt_bit_flag)); } @@ -481,7 +473,7 @@ static int pim_assert_do(struct pim_ifchannel *ch, qpim_all_pim_routers_addr, pim_msg, pim_msg_size, ifp->name)) { zlog_warn("%s: could not send PIM message on interface %s", - __PRETTY_FUNCTION__, ifp->name); + __func__, ifp->name); return -3; } @@ -522,7 +514,7 @@ static int on_assert_timer(struct thread *t) if (PIM_DEBUG_PIM_TRACE) { zlog_debug("%s: (S,G)=%s timer expired on interface %s", - __PRETTY_FUNCTION__, ch->sg_str, ifp->name); + __func__, ch->sg_str, ifp->name); } ch->t_ifassert_timer = NULL; @@ -538,8 +530,8 @@ static int on_assert_timer(struct thread *t) if (PIM_DEBUG_PIM_EVENTS) zlog_warn( "%s: (S,G)=%s invalid assert state %d on interface %s", - __PRETTY_FUNCTION__, ch->sg_str, - ch->ifassert_state, ifp->name); + __func__, ch->sg_str, ch->ifassert_state, + ifp->name); } } @@ -552,8 +544,7 @@ static void assert_timer_off(struct pim_ifchannel *ch) if (ch->t_ifassert_timer) { zlog_debug( "%s: (S,G)=%s cancelling timer on interface %s", - __PRETTY_FUNCTION__, ch->sg_str, - ch->interface->name); + __func__, ch->sg_str, ch->interface->name); } } THREAD_OFF(ch->t_ifassert_timer); @@ -565,8 +556,7 @@ static void pim_assert_timer_set(struct pim_ifchannel *ch, int interval) if (PIM_DEBUG_PIM_TRACE) { zlog_debug("%s: (S,G)=%s starting %u sec timer on interface %s", - __PRETTY_FUNCTION__, ch->sg_str, interval, - ch->interface->name); + __func__, ch->sg_str, interval, ch->interface->name); } thread_add_timer(router->master, on_assert_timer, ch, interval, @@ -597,7 +587,7 @@ int assert_action_a1(struct pim_ifchannel *ch) pim_ifp = ifp->info; if (!pim_ifp) { zlog_warn("%s: (S,G)=%s multicast not enabled on interface %s", - __PRETTY_FUNCTION__, ch->sg_str, ifp->name); + __func__, ch->sg_str, ifp->name); return -1; /* must return since pim_ifp is used below */ } @@ -610,7 +600,7 @@ int assert_action_a1(struct pim_ifchannel *ch) if (assert_action_a3(ch)) { zlog_warn( "%s: (S,G)=%s assert_action_a3 failure on interface %s", - __PRETTY_FUNCTION__, ch->sg_str, ifp->name); + __func__, ch->sg_str, ifp->name); /* warning only */ } @@ -618,7 +608,7 @@ int assert_action_a1(struct pim_ifchannel *ch) if (PIM_DEBUG_PIM_EVENTS) zlog_warn( "%s: channel%s not in expected PIM_IFASSERT_I_AM_WINNER state", - __PRETTY_FUNCTION__, ch->sg_str); + __func__, ch->sg_str); } return 0; @@ -645,7 +635,7 @@ static void assert_action_a2(struct pim_ifchannel *ch, if (PIM_DEBUG_PIM_EVENTS) zlog_warn( "%s: channel%s not in expected PIM_IFASSERT_I_AM_LOSER state", - __PRETTY_FUNCTION__, ch->sg_str); + __func__, ch->sg_str); } } @@ -663,7 +653,7 @@ static int assert_action_a3(struct pim_ifchannel *ch) if (PIM_DEBUG_PIM_EVENTS) zlog_warn( "%s: channel%s expected to be in PIM_IFASSERT_I_AM_WINNER state", - __PRETTY_FUNCTION__, ch->sg_str); + __func__, ch->sg_str); return -1; } @@ -671,7 +661,7 @@ static int assert_action_a3(struct pim_ifchannel *ch) if (pim_assert_send(ch)) { zlog_warn("%s: (S,G)=%s failure sending assert on interface %s", - __PRETTY_FUNCTION__, ch->sg_str, ch->interface->name); + __func__, ch->sg_str, ch->interface->name); return -1; } @@ -692,7 +682,7 @@ void assert_action_a4(struct pim_ifchannel *ch) { if (pim_assert_cancel(ch)) { zlog_warn("%s: failure sending AssertCancel%s on interface %s", - __PRETTY_FUNCTION__, ch->sg_str, ch->interface->name); + __func__, ch->sg_str, ch->interface->name); /* log warning only */ } @@ -702,7 +692,7 @@ void assert_action_a4(struct pim_ifchannel *ch) if (PIM_DEBUG_PIM_EVENTS) zlog_warn( "%s: channel%s not in PIM_IFASSERT_NOINFO state as expected", - __PRETTY_FUNCTION__, ch->sg_str); + __func__, ch->sg_str); } } @@ -721,7 +711,7 @@ void assert_action_a5(struct pim_ifchannel *ch) if (PIM_DEBUG_PIM_EVENTS) zlog_warn( "%s: channel%s not in PIM_IFSSERT_NOINFO state as expected", - __PRETTY_FUNCTION__, ch->sg_str); + __func__, ch->sg_str); } } @@ -734,7 +724,7 @@ void assert_action_a5(struct pim_ifchannel *ch) winner metric as AssertWinnerMetric(S,G,I). Set Assert Timer to Assert_Time. If (I is RPF_interface(S)) AND (UpstreamJPState(S,G) == true) - set SPTbit(S,G) to TRUE. + set SPTbit(S,G) to true. */ static void assert_action_a6(struct pim_ifchannel *ch, struct pim_assert_metric winner_metric) @@ -743,7 +733,7 @@ static void assert_action_a6(struct pim_ifchannel *ch, /* If (I is RPF_interface(S)) AND (UpstreamJPState(S,G) == true) set - SPTbit(S,G) to TRUE. + SPTbit(S,G) to true. */ if (ch->upstream->rpf.source_nexthop.interface == ch->interface) if (ch->upstream->join_state == PIM_UPSTREAM_JOINED) @@ -753,6 +743,6 @@ static void assert_action_a6(struct pim_ifchannel *ch, if (PIM_DEBUG_PIM_EVENTS) zlog_warn( "%s: channel%s not in PIM_IFASSERT_I_AM_LOSER state as expected", - __PRETTY_FUNCTION__, ch->sg_str); + __func__, ch->sg_str); } } diff --git a/pimd/pim_bfd.c b/pimd/pim_bfd.c index 466cc60643..a103fc5f8d 100644 --- a/pimd/pim_bfd.c +++ b/pimd/pim_bfd.c @@ -47,7 +47,7 @@ void pim_bfd_write_config(struct vty *vty, struct interface *ifp) if (!pim_ifp) return; - bfd_info = (struct bfd_info *)pim_ifp->bfd_info; + bfd_info = pim_ifp->bfd_info; if (!bfd_info) return; @@ -92,7 +92,7 @@ void pim_bfd_info_nbr_create(struct pim_interface *pim_ifp, if (!neigh->bfd_info) return; - nbr_bfd_info = (struct bfd_info *)neigh->bfd_info; + nbr_bfd_info = neigh->bfd_info; nbr_bfd_info->detect_mult = pim_ifp->bfd_info->detect_mult; nbr_bfd_info->desired_min_tx = pim_ifp->bfd_info->desired_min_tx; nbr_bfd_info->required_min_rx = pim_ifp->bfd_info->required_min_rx; @@ -111,24 +111,29 @@ static void pim_bfd_reg_dereg_nbr(struct pim_neighbor *nbr, int command) struct pim_interface *pim_ifp = NULL; struct bfd_info *bfd_info = NULL; struct zclient *zclient = NULL; + int cbit; zclient = pim_zebra_zclient_get(); if (!nbr) return; pim_ifp = nbr->interface->info; - bfd_info = (struct bfd_info *)pim_ifp->bfd_info; + bfd_info = pim_ifp->bfd_info; if (!bfd_info) return; if (PIM_DEBUG_PIM_TRACE) { char str[INET_ADDRSTRLEN]; pim_inet4_dump("", nbr->source_addr, str, sizeof(str)); - zlog_debug("%s Nbr %s %s with BFD", __PRETTY_FUNCTION__, str, + zlog_debug("%s Nbr %s %s with BFD", __func__, str, bfd_get_command_dbg_str(command)); } + + cbit = CHECK_FLAG(bfd_info->flags, BFD_FLAG_BFD_CBIT_ON); + bfd_peer_sendmsg(zclient, bfd_info, AF_INET, &nbr->source_addr, NULL, - nbr->interface->name, 0, 0, command, 0, VRF_DEFAULT); + nbr->interface->name, 0, 0, cbit, + command, 0, VRF_DEFAULT); } /* @@ -189,13 +194,13 @@ void pim_bfd_if_param_set(struct interface *ifp, uint32_t min_rx, if (!pim_ifp) return; - bfd_set_param((struct bfd_info **)&(pim_ifp->bfd_info), min_rx, min_tx, - detect_mult, defaults, &command); + bfd_set_param(&(pim_ifp->bfd_info), min_rx, min_tx, detect_mult, NULL, + defaults, &command); if (pim_ifp->bfd_info) { if (PIM_DEBUG_PIM_TRACE) - zlog_debug("%s: interface %s has bfd_info", - __PRETTY_FUNCTION__, ifp->name); + zlog_debug("%s: interface %s has bfd_info", __func__, + ifp->name); } if (command) pim_bfd_reg_dereg_all_nbr(ifp, command); @@ -208,12 +213,11 @@ void pim_bfd_if_param_set(struct interface *ifp, uint32_t min_rx, * connectivity if the BFD status changed to * down. */ -static int pim_bfd_interface_dest_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int pim_bfd_interface_dest_update(ZAPI_CALLBACK_ARGS) { struct interface *ifp = NULL; struct pim_interface *pim_ifp = NULL; - struct prefix p; + struct prefix p, src_p; int status; char msg[100]; int old_status; @@ -223,7 +227,8 @@ static int pim_bfd_interface_dest_update(int command, struct zclient *zclient, struct listnode *neigh_nextnode = NULL; struct pim_neighbor *neigh = NULL; - ifp = bfd_get_peer_info(zclient->ibuf, &p, NULL, &status, vrf_id); + ifp = bfd_get_peer_info(zclient->ibuf, &p, &src_p, &status, NULL, + vrf_id); if ((ifp == NULL) || (p.family != AF_INET)) return 0; @@ -235,16 +240,15 @@ static int pim_bfd_interface_dest_update(int command, struct zclient *zclient, if (!pim_ifp->bfd_info) { if (PIM_DEBUG_PIM_TRACE) zlog_debug("%s: pim interface %s BFD is disabled ", - __PRETTY_FUNCTION__, ifp->name); + __func__, ifp->name); return 0; } if (PIM_DEBUG_PIM_TRACE) { char buf[PREFIX2STR_BUFFER]; prefix2str(&p, buf, sizeof(buf)); - zlog_debug("%s: interface %s bfd destination %s %s", - __PRETTY_FUNCTION__, ifp->name, buf, - bfd_get_status_str(status)); + zlog_debug("%s: interface %s bfd destination %s %s", __func__, + ifp->name, buf, bfd_get_status_str(status)); } for (ALL_LIST_ELEMENTS(pim_ifp->pim_neighbor_list, neigh_node, @@ -260,18 +264,17 @@ static int pim_bfd_interface_dest_update(int command, struct zclient *zclient, pim_inet4_dump("", neigh->source_addr, str, sizeof(str)); zlog_debug("%s: bfd status is same for nbr %s", - __PRETTY_FUNCTION__, str); + __func__, str); } continue; } old_status = bfd_info->status; - bfd_info->status = status; + BFD_SET_CLIENT_STATUS(bfd_info->status, status); monotime(&tv); bfd_info->last_update = tv.tv_sec; if (PIM_DEBUG_PIM_TRACE) { - zlog_debug("%s: status %s old_status %s", - __PRETTY_FUNCTION__, + zlog_debug("%s: status %s old_status %s", __func__, bfd_get_status_str(status), bfd_get_status_str(old_status)); } @@ -288,8 +291,7 @@ static int pim_bfd_interface_dest_update(int command, struct zclient *zclient, * pim_bfd_nbr_replay - Replay all the neighbors that have BFD enabled * to zebra */ -static int pim_bfd_nbr_replay(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int pim_bfd_nbr_replay(ZAPI_CALLBACK_ARGS) { struct interface *ifp = NULL; struct pim_interface *pim_ifp = NULL; @@ -299,7 +301,7 @@ static int pim_bfd_nbr_replay(int command, struct zclient *zclient, struct vrf *vrf = NULL; /* Send the client registration */ - bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER); + bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, vrf_id); RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { FOR_ALL_INTERFACES (vrf, ifp) { @@ -324,8 +326,7 @@ static int pim_bfd_nbr_replay(int command, struct zclient *zclient, sizeof(str)); zlog_debug( "%s: Replaying Pim Neigh %s to BFD vrf_id %u", - __PRETTY_FUNCTION__, str, - vrf->vrf_id); + __func__, str, vrf->vrf_id); } pim_bfd_reg_dereg_nbr(neigh, ZEBRA_BFD_DEST_UPDATE); diff --git a/pimd/pim_bsm.c b/pimd/pim_bsm.c new file mode 100644 index 0000000000..1c9005588f --- /dev/null +++ b/pimd/pim_bsm.c @@ -0,0 +1,1421 @@ +/* + * pim_bsm.c: PIM BSM handling routines + * + * Copyright (C) 2018-19 Vmware, Inc. + * Saravanan K + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "if.h" +#include "pimd.h" +#include "pim_iface.h" +#include "pim_instance.h" +#include "pim_rpf.h" +#include "pim_hello.h" +#include "pim_pim.h" +#include "pim_nht.h" +#include "pim_bsm.h" +#include "pim_time.h" + +/* Functions forward declaration */ +static void pim_bs_timer_start(struct bsm_scope *scope, int bs_timeout); +static void pim_g2rp_timer_start(struct bsm_rpinfo *bsrp, int hold_time); +static inline void pim_g2rp_timer_restart(struct bsm_rpinfo *bsrp, + int hold_time); + +/* Memory Types */ +DEFINE_MTYPE_STATIC(PIMD, PIM_BSGRP_NODE, "PIM BSR advertised grp info") +DEFINE_MTYPE_STATIC(PIMD, PIM_BSRP_NODE, "PIM BSR advertised RP info") +DEFINE_MTYPE_STATIC(PIMD, PIM_BSM_INFO, "PIM BSM Info") +DEFINE_MTYPE_STATIC(PIMD, PIM_BSM_PKT_VAR_MEM, "PIM BSM Packet") + +/* All bsm packets forwarded shall be fit within ip mtu less iphdr(max) */ +#define MAX_IP_HDR_LEN 24 + +/* pim_bsm_write_config - Write the interface pim bsm configuration.*/ +void pim_bsm_write_config(struct vty *vty, struct interface *ifp) +{ + struct pim_interface *pim_ifp = ifp->info; + + if (pim_ifp) { + if (!pim_ifp->bsm_enable) + vty_out(vty, " no ip pim bsm\n"); + if (!pim_ifp->ucast_bsm_accept) + vty_out(vty, " no ip pim unicast-bsm\n"); + } +} + +static void pim_free_bsgrp_data(struct bsgrp_node *bsgrp_node) +{ + if (bsgrp_node->bsrp_list) + list_delete(&bsgrp_node->bsrp_list); + if (bsgrp_node->partial_bsrp_list) + list_delete(&bsgrp_node->partial_bsrp_list); + XFREE(MTYPE_PIM_BSGRP_NODE, bsgrp_node); +} + +static void pim_free_bsgrp_node(struct route_table *rt, struct prefix *grp) +{ + struct route_node *rn; + + rn = route_node_lookup(rt, grp); + if (rn) { + rn->info = NULL; + route_unlock_node(rn); + route_unlock_node(rn); + } +} + +static void pim_bsm_node_free(struct bsm_info *bsm) +{ + XFREE(MTYPE_PIM_BSM_PKT_VAR_MEM, bsm->bsm); + XFREE(MTYPE_PIM_BSM_INFO, bsm); +} + +static int pim_g2rp_list_compare(struct bsm_rpinfo *node1, + struct bsm_rpinfo *node2) +{ + /* RP election Algo : + * Step-1 : Loweset Rp priority will have higher precedance. + * Step-2 : If priority same then higher hash val will have + * higher precedance. + * Step-3 : If Hash val is same then highest rp address will + * become elected RP. + */ + if (node1->rp_prio < node2->rp_prio) + return -1; + if (node1->rp_prio > node2->rp_prio) + return 1; + if (node1->hash < node2->hash) + return 1; + if (node1->hash > node2->hash) + return -1; + if (node1->rp_address.s_addr < node2->rp_address.s_addr) + return 1; + if (node1->rp_address.s_addr > node2->rp_address.s_addr) + return -1; + return 0; +} + +static void pim_free_bsrp_node(struct bsm_rpinfo *bsrp_info) +{ + if (bsrp_info->g2rp_timer) + THREAD_OFF(bsrp_info->g2rp_timer); + XFREE(MTYPE_PIM_BSRP_NODE, bsrp_info); +} + +static struct list *pim_alloc_bsrp_list(void) +{ + struct list *new_list = NULL; + + new_list = list_new(); + + if (!new_list) + return NULL; + + new_list->cmp = (int (*)(void *, void *))pim_g2rp_list_compare; + new_list->del = (void (*)(void *))pim_free_bsrp_node; + + return new_list; +} + +static struct bsgrp_node *pim_bsm_new_bsgrp_node(struct route_table *rt, + struct prefix *grp) +{ + struct route_node *rn; + struct bsgrp_node *bsgrp; + + rn = route_node_get(rt, grp); + if (!rn) { + zlog_warn("%s: route node creation failed", __func__); + return NULL; + } + bsgrp = XCALLOC(MTYPE_PIM_BSGRP_NODE, sizeof(struct bsgrp_node)); + + rn->info = bsgrp; + bsgrp->bsrp_list = pim_alloc_bsrp_list(); + bsgrp->partial_bsrp_list = pim_alloc_bsrp_list(); + + if ((!bsgrp->bsrp_list) || (!bsgrp->partial_bsrp_list)) { + route_unlock_node(rn); + pim_free_bsgrp_data(bsgrp); + return NULL; + } + + prefix_copy(&bsgrp->group, grp); + return bsgrp; +} + +static int pim_on_bs_timer(struct thread *t) +{ + struct route_node *rn; + struct bsm_scope *scope; + struct bsgrp_node *bsgrp_node; + struct bsm_rpinfo *bsrp; + struct prefix nht_p; + char buf[PREFIX2STR_BUFFER]; + bool is_bsr_tracking = true; + + scope = THREAD_ARG(t); + THREAD_OFF(scope->bs_timer); + + if (PIM_DEBUG_BSM) + zlog_debug("%s: Bootstrap Timer expired for scope: %d", + __func__, scope->sz_id); + + /* Remove next hop tracking for the bsr */ + nht_p.family = AF_INET; + nht_p.prefixlen = IPV4_MAX_BITLEN; + nht_p.u.prefix4 = scope->current_bsr; + if (PIM_DEBUG_BSM) { + prefix2str(&nht_p, buf, sizeof(buf)); + zlog_debug("%s: Deregister BSR addr %s with Zebra NHT", + __func__, buf); + } + pim_delete_tracked_nexthop(scope->pim, &nht_p, NULL, NULL, + is_bsr_tracking); + + /* Reset scope zone data */ + scope->accept_nofwd_bsm = false; + scope->state = ACCEPT_ANY; + scope->current_bsr.s_addr = INADDR_ANY; + scope->current_bsr_prio = 0; + scope->current_bsr_first_ts = 0; + scope->current_bsr_last_ts = 0; + scope->bsm_frag_tag = 0; + list_delete_all_node(scope->bsm_list); + + for (rn = route_top(scope->bsrp_table); rn; rn = route_next(rn)) { + + bsgrp_node = (struct bsgrp_node *)rn->info; + if (!bsgrp_node) { + if (PIM_DEBUG_BSM) + zlog_debug("%s: bsgrp_node is null", __func__); + continue; + } + /* Give grace time for rp to continue for another hold time */ + if ((bsgrp_node->bsrp_list) && (bsgrp_node->bsrp_list->count)) { + bsrp = listnode_head(bsgrp_node->bsrp_list); + pim_g2rp_timer_restart(bsrp, bsrp->rp_holdtime); + } + /* clear pending list */ + if ((bsgrp_node->partial_bsrp_list) + && (bsgrp_node->partial_bsrp_list->count)) { + list_delete_all_node(bsgrp_node->partial_bsrp_list); + bsgrp_node->pend_rp_cnt = 0; + } + } + return 0; +} + +static void pim_bs_timer_stop(struct bsm_scope *scope) +{ + if (PIM_DEBUG_BSM) + zlog_debug("%s : BS timer being stopped of sz: %d", __func__, + scope->sz_id); + THREAD_OFF(scope->bs_timer); +} + +static void pim_bs_timer_start(struct bsm_scope *scope, int bs_timeout) +{ + if (!scope) { + if (PIM_DEBUG_BSM) + zlog_debug("%s : Invalid scope(NULL).", __func__); + return; + } + THREAD_OFF(scope->bs_timer); + if (PIM_DEBUG_BSM) + zlog_debug( + "%s : starting bs timer for scope %d with timeout %d secs", + __func__, scope->sz_id, bs_timeout); + thread_add_timer(router->master, pim_on_bs_timer, scope, bs_timeout, + &scope->bs_timer); +} + +static inline void pim_bs_timer_restart(struct bsm_scope *scope, int bs_timeout) +{ + pim_bs_timer_start(scope, bs_timeout); +} + +void pim_bsm_proc_init(struct pim_instance *pim) +{ + memset(&pim->global_scope, 0, sizeof(struct bsm_scope)); + + pim->global_scope.sz_id = PIM_GBL_SZ_ID; + pim->global_scope.bsrp_table = route_table_init(); + pim->global_scope.accept_nofwd_bsm = true; + pim->global_scope.state = NO_INFO; + pim->global_scope.pim = pim; + pim->global_scope.bsm_list = list_new(); + pim->global_scope.bsm_list->del = (void (*)(void *))pim_bsm_node_free; + pim_bs_timer_start(&pim->global_scope, PIM_BS_TIME); +} + +void pim_bsm_proc_free(struct pim_instance *pim) +{ + struct route_node *rn; + struct bsgrp_node *bsgrp; + + pim_bs_timer_stop(&pim->global_scope); + + if (pim->global_scope.bsm_list) + list_delete(&pim->global_scope.bsm_list); + + for (rn = route_top(pim->global_scope.bsrp_table); rn; + rn = route_next(rn)) { + bsgrp = rn->info; + if (!bsgrp) + continue; + pim_free_bsgrp_data(bsgrp); + } + + route_table_finish(pim->global_scope.bsrp_table); +} + +static bool is_hold_time_elapsed(void *data) +{ + struct bsm_rpinfo *bsrp; + + bsrp = data; + + if (bsrp->elapse_time < bsrp->rp_holdtime) + return false; + else + return true; +} + +static int pim_on_g2rp_timer(struct thread *t) +{ + struct bsm_rpinfo *bsrp; + struct bsm_rpinfo *bsrp_node; + struct bsgrp_node *bsgrp_node; + struct listnode *bsrp_ln; + struct pim_instance *pim; + struct rp_info *rp_info; + struct route_node *rn; + uint16_t elapse; + struct in_addr bsrp_addr; + + bsrp = THREAD_ARG(t); + THREAD_OFF(bsrp->g2rp_timer); + bsgrp_node = bsrp->bsgrp_node; + + /* elapse time is the hold time of expired node */ + elapse = bsrp->rp_holdtime; + bsrp_addr = bsrp->rp_address; + + /* update elapse for all bsrp nodes */ + for (ALL_LIST_ELEMENTS_RO(bsgrp_node->bsrp_list, bsrp_ln, bsrp_node)) + bsrp_node->elapse_time += elapse; + + /* remove the expired nodes from the list */ + list_filter_out_nodes(bsgrp_node->bsrp_list, is_hold_time_elapsed); + + /* Get the next elected rp node */ + bsrp = listnode_head(bsgrp_node->bsrp_list); + pim = bsgrp_node->scope->pim; + rn = route_node_lookup(pim->rp_table, &bsgrp_node->group); + + if (!rn) { + zlog_warn("%s: Route node doesn't exist", __func__); + return 0; + } + + rp_info = (struct rp_info *)rn->info; + + if (!rp_info) { + route_unlock_node(rn); + return 0; + } + + if (rp_info->rp_src != RP_SRC_STATIC) { + /* If new rp available, change it else delete the existing */ + if (bsrp) { + bsrp_addr = bsrp->rp_address; + pim_g2rp_timer_start( + bsrp, (bsrp->rp_holdtime - bsrp->elapse_time)); + pim_rp_change(pim, bsrp_addr, bsgrp_node->group, + RP_SRC_BSR); + } else { + pim_rp_del(pim, bsrp_addr, bsgrp_node->group, NULL, + RP_SRC_BSR); + } + } + + if ((!bsgrp_node->bsrp_list->count) + && (!bsgrp_node->partial_bsrp_list->count)) { + pim_free_bsgrp_node(pim->global_scope.bsrp_table, + &bsgrp_node->group); + pim_free_bsgrp_data(bsgrp_node); + } + + return 0; +} + +static void pim_g2rp_timer_start(struct bsm_rpinfo *bsrp, int hold_time) +{ + if (!bsrp) { + if (PIM_DEBUG_BSM) + zlog_debug("%s : Invalid brsp(NULL).", __func__); + return; + } + THREAD_OFF(bsrp->g2rp_timer); + if (PIM_DEBUG_BSM) { + char buf[48]; + + zlog_debug( + "%s : starting g2rp timer for grp: %s - rp: %s with timeout %d secs(Actual Hold time : %d secs)", + __func__, prefix2str(&bsrp->bsgrp_node->group, buf, 48), + inet_ntoa(bsrp->rp_address), hold_time, + bsrp->rp_holdtime); + } + + thread_add_timer(router->master, pim_on_g2rp_timer, bsrp, hold_time, + &bsrp->g2rp_timer); +} + +static inline void pim_g2rp_timer_restart(struct bsm_rpinfo *bsrp, + int hold_time) +{ + pim_g2rp_timer_start(bsrp, hold_time); +} + +static void pim_g2rp_timer_stop(struct bsm_rpinfo *bsrp) +{ + if (!bsrp) + return; + + if (PIM_DEBUG_BSM) { + char buf[48]; + + zlog_debug("%s : stopping g2rp timer for grp: %s - rp: %s", + __func__, + prefix2str(&bsrp->bsgrp_node->group, buf, 48), + inet_ntoa(bsrp->rp_address)); + } + + THREAD_OFF(bsrp->g2rp_timer); +} + +static bool is_hold_time_zero(void *data) +{ + struct bsm_rpinfo *bsrp; + + bsrp = data; + + if (bsrp->rp_holdtime) + return false; + else + return true; +} + +static void pim_instate_pend_list(struct bsgrp_node *bsgrp_node) +{ + struct bsm_rpinfo *active; + struct bsm_rpinfo *pend; + struct list *temp; + struct rp_info *rp_info; + struct route_node *rn; + struct pim_instance *pim; + struct rp_info *rp_all; + struct prefix group_all; + bool had_rp_node = true; + + pim = bsgrp_node->scope->pim; + active = listnode_head(bsgrp_node->bsrp_list); + + /* Remove nodes with hold time 0 & check if list still has a head */ + list_filter_out_nodes(bsgrp_node->partial_bsrp_list, is_hold_time_zero); + pend = listnode_head(bsgrp_node->partial_bsrp_list); + + if (!str2prefix("224.0.0.0/4", &group_all)) + return; + + rp_all = pim_rp_find_match_group(pim, &group_all); + rn = route_node_lookup(pim->rp_table, &bsgrp_node->group); + + if (pend) + pim_g2rp_timer_start(pend, pend->rp_holdtime); + + /* if rp node doesn't exist or exist but not configured(rp_all), + * install the rp from head(if exists) of partial list. List is + * is sorted such that head is the elected RP for the group. + */ + if (!rn || (prefix_same(&rp_all->group, &bsgrp_node->group) + && pim_rpf_addr_is_inaddr_none(&rp_all->rp))) { + if (PIM_DEBUG_BSM) + zlog_debug("%s: Route node doesn't exist", __func__); + if (pend) + pim_rp_new(pim, pend->rp_address, bsgrp_node->group, + NULL, RP_SRC_BSR); + had_rp_node = false; + } else { + rp_info = (struct rp_info *)rn->info; + if (!rp_info) { + route_unlock_node(rn); + if (pend) + pim_rp_new(pim, pend->rp_address, + bsgrp_node->group, NULL, RP_SRC_BSR); + had_rp_node = false; + } + } + + /* We didn't have rp node and pending list is empty(unlikely), cleanup*/ + if ((!had_rp_node) && (!pend)) { + pim_free_bsgrp_node(bsgrp_node->scope->bsrp_table, + &bsgrp_node->group); + pim_free_bsgrp_data(bsgrp_node); + return; + } + + if ((had_rp_node) && (rp_info->rp_src != RP_SRC_STATIC)) { + /* This means we searched and got rp node, needs unlock */ + route_unlock_node(rn); + + if (active && pend) { + if ((active->rp_address.s_addr + != pend->rp_address.s_addr)) + pim_rp_change(pim, pend->rp_address, + bsgrp_node->group, RP_SRC_BSR); + } + + /* Possible when the first BSM has group with 0 rp count */ + if ((!active) && (!pend)) { + if (PIM_DEBUG_BSM) { + zlog_debug( + "%s: Both bsrp and partial list are empty", + __func__); + } + pim_free_bsgrp_node(bsgrp_node->scope->bsrp_table, + &bsgrp_node->group); + pim_free_bsgrp_data(bsgrp_node); + return; + } + + /* Possible when a group with 0 rp count received in BSM */ + if ((active) && (!pend)) { + pim_rp_del(pim, active->rp_address, bsgrp_node->group, + NULL, RP_SRC_BSR); + pim_free_bsgrp_node(bsgrp_node->scope->bsrp_table, + &bsgrp_node->group); + if (PIM_DEBUG_BSM) { + zlog_debug("%s:Pend List is null,del grp node", + __func__); + } + pim_free_bsgrp_data(bsgrp_node); + return; + } + } + + if ((had_rp_node) && (rp_info->rp_src == RP_SRC_STATIC)) { + /* We need to unlock rn this case */ + route_unlock_node(rn); + /* there is a chance that static rp exist and bsrp cleaned + * so clean bsgrp node if pending list empty + */ + if (!pend) { + if (PIM_DEBUG_BSM) + zlog_debug( + "%s: Partial list is empty, static rp exists", + __func__); + pim_free_bsgrp_node(bsgrp_node->scope->bsrp_table, + &bsgrp_node->group); + pim_free_bsgrp_data(bsgrp_node); + return; + } + } + + /* swap the list & delete all nodes in partial list (old bsrp_list) + * before swap + * active is head of bsrp list + * pend is head of partial list + * After swap + * active is head of partial list + * pend is head of bsrp list + * So check appriate head after swap and clean the new partial list + */ + temp = bsgrp_node->bsrp_list; + bsgrp_node->bsrp_list = bsgrp_node->partial_bsrp_list; + bsgrp_node->partial_bsrp_list = temp; + + if (active) { + pim_g2rp_timer_stop(active); + list_delete_all_node(bsgrp_node->partial_bsrp_list); + } +} + +static bool pim_bsr_rpf_check(struct pim_instance *pim, struct in_addr bsr, + struct in_addr ip_src_addr) +{ + struct pim_nexthop nexthop; + int result; + + memset(&nexthop, 0, sizeof(nexthop)); + + /* New BSR recived */ + if (bsr.s_addr != pim->global_scope.current_bsr.s_addr) { + result = pim_nexthop_match(pim, bsr, ip_src_addr); + + /* Nexthop lookup pass for the new BSR address */ + if (result) + return true; + + if (PIM_DEBUG_BSM) { + char bsr_str[INET_ADDRSTRLEN]; + + pim_inet4_dump("", bsr, bsr_str, sizeof(bsr_str)); + zlog_debug("%s : No route to BSR address %s", __func__, + bsr_str); + } + return false; + } + + return pim_nexthop_match_nht_cache(pim, bsr, ip_src_addr); +} + +static bool is_preferred_bsr(struct pim_instance *pim, struct in_addr bsr, + uint32_t bsr_prio) +{ + if (bsr.s_addr == pim->global_scope.current_bsr.s_addr) + return true; + + if (bsr_prio > pim->global_scope.current_bsr_prio) + return true; + + else if (bsr_prio == pim->global_scope.current_bsr_prio) { + if (ntohl(bsr.s_addr) + >= ntohl(pim->global_scope.current_bsr.s_addr)) + return true; + else + return false; + } else + return false; +} + +static void pim_bsm_update(struct pim_instance *pim, struct in_addr bsr, + uint32_t bsr_prio) +{ + struct pim_nexthop_cache pnc; + + if (bsr.s_addr != pim->global_scope.current_bsr.s_addr) { + struct prefix nht_p; + char buf[PREFIX2STR_BUFFER]; + bool is_bsr_tracking = true; + + /* De-register old BSR and register new BSR with Zebra NHT */ + nht_p.family = AF_INET; + nht_p.prefixlen = IPV4_MAX_BITLEN; + + if (pim->global_scope.current_bsr.s_addr != INADDR_ANY) { + nht_p.u.prefix4 = pim->global_scope.current_bsr; + if (PIM_DEBUG_BSM) { + prefix2str(&nht_p, buf, sizeof(buf)); + zlog_debug( + "%s: Deregister BSR addr %s with Zebra NHT", + __func__, buf); + } + pim_delete_tracked_nexthop(pim, &nht_p, NULL, NULL, + is_bsr_tracking); + } + + nht_p.u.prefix4 = bsr; + if (PIM_DEBUG_BSM) { + prefix2str(&nht_p, buf, sizeof(buf)); + zlog_debug( + "%s: NHT Register BSR addr %s with Zebra NHT", + __func__, buf); + } + + memset(&pnc, 0, sizeof(struct pim_nexthop_cache)); + pim_find_or_track_nexthop(pim, &nht_p, NULL, NULL, + is_bsr_tracking, &pnc); + pim->global_scope.current_bsr = bsr; + pim->global_scope.current_bsr_first_ts = + pim_time_monotonic_sec(); + pim->global_scope.state = ACCEPT_PREFERRED; + } + pim->global_scope.current_bsr_prio = bsr_prio; + pim->global_scope.current_bsr_last_ts = pim_time_monotonic_sec(); +} + +static bool pim_bsm_send_intf(uint8_t *buf, int len, struct interface *ifp, + struct in_addr dst_addr) +{ + struct pim_interface *pim_ifp; + + pim_ifp = ifp->info; + + if (!pim_ifp) { + if (PIM_DEBUG_BSM) + zlog_debug("%s: Pim interface not available for %s", + __func__, ifp->name); + return false; + } + + if (pim_ifp->pim_sock_fd == -1) { + if (PIM_DEBUG_BSM) + zlog_debug("%s: Pim sock not available for %s", + __func__, ifp->name); + return false; + } + + if (pim_msg_send(pim_ifp->pim_sock_fd, pim_ifp->primary_address, + dst_addr, buf, len, ifp->name)) { + zlog_warn("%s: Could not send BSM message on interface: %s", + __func__, ifp->name); + return false; + } + + pim_ifp->pim_ifstat_bsm_tx++; + pim_ifp->pim->bsm_sent++; + return true; +} + +static bool pim_bsm_frag_send(uint8_t *buf, uint32_t len, struct interface *ifp, + uint32_t pim_mtu, struct in_addr dst_addr, + bool no_fwd) +{ + struct bsmmsg_grpinfo *grpinfo, *curgrp; + uint8_t *firstgrp_ptr; + uint8_t *pkt; + uint8_t *pak_start; + uint32_t parsed_len = 0; + uint32_t this_pkt_rem; + uint32_t copy_byte_count; + uint32_t this_pkt_len; + uint8_t total_rp_cnt; + uint8_t this_rp_cnt; + uint8_t frag_rp_cnt; + uint8_t rp_fit_cnt; + bool pak_pending = false; + + /* MTU passed here is PIM MTU (IP MTU less IP Hdr) */ + if (pim_mtu < (PIM_MIN_BSM_LEN)) { + zlog_warn( + "%s: mtu(pim mtu: %d) size less than minimum bootstrap len", + __func__, pim_mtu); + if (PIM_DEBUG_BSM) + zlog_debug( + "%s: mtu (pim mtu:%d) less than minimum bootstrap len", + __func__, pim_mtu); + return false; + } + + pak_start = XCALLOC(MTYPE_PIM_BSM_PKT_VAR_MEM, pim_mtu); + + pkt = pak_start; + + /* Fill PIM header later before sending packet to calc checksum */ + pkt += PIM_MSG_HEADER_LEN; + buf += PIM_MSG_HEADER_LEN; + + /* copy bsm header to new packet at offset of pim hdr */ + memcpy(pkt, buf, PIM_BSM_HDR_LEN); + pkt += PIM_BSM_HDR_LEN; + buf += PIM_BSM_HDR_LEN; + parsed_len += (PIM_MSG_HEADER_LEN + PIM_BSM_HDR_LEN); + + /* Store the position of first grp ptr, which can be reused for + * next packet to start filling group. old bsm header and pim hdr + * remains. So need not be filled again for next packet onwards. + */ + firstgrp_ptr = pkt; + + /* we received mtu excluding IP hdr len as param + * now this_pkt_rem is mtu excluding + * PIM_BSM_HDR_LEN + PIM_MSG_HEADER_LEN + */ + this_pkt_rem = pim_mtu - (PIM_BSM_HDR_LEN + PIM_MSG_HEADER_LEN); + + /* For each group till the packet length parsed */ + while (parsed_len < len) { + /* pkt ---> fragment's current pointer + * buf ---> input buffer's current pointer + * mtu ---> size of the pim packet - PIM header + * curgrp ---> current group on the fragment + * grpinfo ---> current group on the input buffer + * this_pkt_rem ---> bytes remaing on the current fragment + * rp_fit_cnt ---> num of rp for current grp that + * fits this frag + * total_rp_cnt ---> total rp present for the group in the buf + * frag_rp_cnt ---> no of rp for the group to be fit in + * the frag + * this_rp_cnt ---> how many rp have we parsed + */ + grpinfo = (struct bsmmsg_grpinfo *)buf; + memcpy(pkt, buf, PIM_BSM_GRP_LEN); + curgrp = (struct bsmmsg_grpinfo *)pkt; + parsed_len += PIM_BSM_GRP_LEN; + pkt += PIM_BSM_GRP_LEN; + buf += PIM_BSM_GRP_LEN; + this_pkt_rem -= PIM_BSM_GRP_LEN; + + /* initialize rp count and total_rp_cnt before the rp loop */ + this_rp_cnt = 0; + total_rp_cnt = grpinfo->frag_rp_count; + + /* Loop till all RPs for the group parsed */ + while (this_rp_cnt < total_rp_cnt) { + /* All RP from a group processed here. + * group is pointed by grpinfo. + * At this point make sure buf pointing to a RP + * within a group + */ + rp_fit_cnt = this_pkt_rem / PIM_BSM_RP_LEN; + + /* calculate how many rp am i going to copy in + * this frag + */ + if (rp_fit_cnt > (total_rp_cnt - this_rp_cnt)) + frag_rp_cnt = total_rp_cnt - this_rp_cnt; + else + frag_rp_cnt = rp_fit_cnt; + + /* populate the frag rp count for the current grp */ + curgrp->frag_rp_count = frag_rp_cnt; + copy_byte_count = frag_rp_cnt * PIM_BSM_RP_LEN; + + /* copy all the rp that we are fitting in this + * frag for the grp + */ + memcpy(pkt, buf, copy_byte_count); + this_rp_cnt += frag_rp_cnt; + buf += copy_byte_count; + pkt += copy_byte_count; + parsed_len += copy_byte_count; + this_pkt_rem -= copy_byte_count; + + /* Either we couldn't fit all rp for the group or the + * mtu reached + */ + if ((this_rp_cnt < total_rp_cnt) + || (this_pkt_rem + < (PIM_BSM_GRP_LEN + PIM_BSM_RP_LEN))) { + /* No space to fit in more rp, send this pkt */ + this_pkt_len = pim_mtu - this_pkt_rem; + pim_msg_build_header(pak_start, this_pkt_len, + PIM_MSG_TYPE_BOOTSTRAP, + no_fwd); + pim_bsm_send_intf(pak_start, this_pkt_len, ifp, + dst_addr); + + /* Construct next fragment. Reuse old packet */ + pkt = firstgrp_ptr; + this_pkt_rem = pim_mtu - (PIM_BSM_HDR_LEN + + PIM_MSG_HEADER_LEN); + + /* If pkt can't accomodate next group + atleast + * one rp, we must break out of this inner loop + * and process next RP + */ + if (total_rp_cnt == this_rp_cnt) + break; + + /* If some more RPs for the same group pending, + * fill grp hdr + */ + memcpy(pkt, (uint8_t *)grpinfo, + PIM_BSM_GRP_LEN); + curgrp = (struct bsmmsg_grpinfo *)pkt; + pkt += PIM_BSM_GRP_LEN; + this_pkt_rem -= PIM_BSM_GRP_LEN; + pak_pending = false; + } else { + /* We filled something but not yet sent out */ + pak_pending = true; + } + } /* while RP count */ + } /*while parsed len */ + + /* Send if we have any unsent packet */ + if (pak_pending) { + this_pkt_len = pim_mtu - this_pkt_rem; + pim_msg_build_header(pak_start, this_pkt_len, + PIM_MSG_TYPE_BOOTSTRAP, no_fwd); + pim_bsm_send_intf(pak_start, (pim_mtu - this_pkt_rem), ifp, + dst_addr); + } + XFREE(MTYPE_PIM_BSM_PKT_VAR_MEM, pak_start); + return true; +} + +static void pim_bsm_fwd_whole_sz(struct pim_instance *pim, uint8_t *buf, + uint32_t len, int sz) +{ + struct interface *ifp; + struct pim_interface *pim_ifp; + struct in_addr dst_addr; + uint32_t pim_mtu; + bool no_fwd = false; + bool ret = false; + + /* For now only global scope zone is supported, so send on all + * pim interfaces in the vrf + */ + dst_addr = qpim_all_pim_routers_addr; + FOR_ALL_INTERFACES (pim->vrf, ifp) { + pim_ifp = ifp->info; + if ((!pim_ifp) || (!pim_ifp->bsm_enable)) + continue; + + /* + * RFC 5059 Sec 3.4: + * When a Bootstrap message is forwarded, it is forwarded out + * of every multicast-capable interface that has PIM neighbors. + * + * So skipping pim interfaces with no neighbors. + */ + if (listcount(pim_ifp->pim_neighbor_list) == 0) + continue; + + pim_hello_require(ifp); + pim_mtu = ifp->mtu - MAX_IP_HDR_LEN; + if (pim_mtu < len) { + ret = pim_bsm_frag_send(buf, len, ifp, pim_mtu, + dst_addr, no_fwd); + if (PIM_DEBUG_BSM) + zlog_debug("%s: pim_bsm_frag_send returned %s", + __func__, ret ? "TRUE" : "FALSE"); + } else { + pim_msg_build_header(buf, len, PIM_MSG_TYPE_BOOTSTRAP, + no_fwd); + if (!pim_bsm_send_intf(buf, len, ifp, dst_addr)) { + if (PIM_DEBUG_BSM) + zlog_debug( + "%s: pim_bsm_send_intf returned false", + __func__); + } + } + } +} + +bool pim_bsm_new_nbr_fwd(struct pim_neighbor *neigh, struct interface *ifp) +{ + struct in_addr dst_addr; + struct pim_interface *pim_ifp; + struct bsm_scope *scope; + struct listnode *bsm_ln; + struct bsm_info *bsminfo; + char neigh_src_str[INET_ADDRSTRLEN]; + uint32_t pim_mtu; + bool no_fwd = true; + bool ret = false; + + if (PIM_DEBUG_BSM) { + pim_inet4_dump("", neigh->source_addr, neigh_src_str, + sizeof(neigh_src_str)); + zlog_debug("%s: New neighbor %s seen on %s", __func__, + neigh_src_str, ifp->name); + } + + pim_ifp = ifp->info; + + /* DR only forwards BSM packet */ + if (pim_ifp->pim_dr_addr.s_addr == pim_ifp->primary_address.s_addr) { + if (PIM_DEBUG_BSM) + zlog_debug( + "%s: It is not DR, so don't forward BSM packet", + __func__); + } + + if (!pim_ifp->bsm_enable) { + if (PIM_DEBUG_BSM) + zlog_debug("%s: BSM proc not enabled on %s", __func__, + ifp->name); + return ret; + } + + scope = &pim_ifp->pim->global_scope; + + if (!scope->bsm_list->count) { + if (PIM_DEBUG_BSM) + zlog_debug("%s: BSM list for the scope is empty", + __func__); + return ret; + } + + if (!pim_ifp->ucast_bsm_accept) { + dst_addr = qpim_all_pim_routers_addr; + if (PIM_DEBUG_BSM) + zlog_debug("%s: Sending BSM mcast to %s", __func__, + neigh_src_str); + } else { + dst_addr = neigh->source_addr; + if (PIM_DEBUG_BSM) + zlog_debug("%s: Sending BSM ucast to %s", __func__, + neigh_src_str); + } + pim_mtu = ifp->mtu - MAX_IP_HDR_LEN; + pim_hello_require(ifp); + + for (ALL_LIST_ELEMENTS_RO(scope->bsm_list, bsm_ln, bsminfo)) { + if (pim_mtu < bsminfo->size) { + ret = pim_bsm_frag_send(bsminfo->bsm, bsminfo->size, + ifp, pim_mtu, dst_addr, no_fwd); + if (!ret) { + if (PIM_DEBUG_BSM) + zlog_debug( + "%s: pim_bsm_frag_send failed", + __func__); + } + } else { + /* Pim header needs to be constructed */ + pim_msg_build_header(bsminfo->bsm, bsminfo->size, + PIM_MSG_TYPE_BOOTSTRAP, no_fwd); + ret = pim_bsm_send_intf(bsminfo->bsm, bsminfo->size, + ifp, dst_addr); + if (!ret) { + if (PIM_DEBUG_BSM) + zlog_debug( + "%s: pim_bsm_frag_send failed", + __func__); + } + } + } + return ret; +} + +struct bsgrp_node *pim_bsm_get_bsgrp_node(struct bsm_scope *scope, + struct prefix *grp) +{ + struct route_node *rn; + struct bsgrp_node *bsgrp; + + rn = route_node_lookup(scope->bsrp_table, grp); + if (!rn) { + if (PIM_DEBUG_BSM) + zlog_debug("%s: Route node doesn't exist for the group", + __func__); + return NULL; + } + bsgrp = rn->info; + route_unlock_node(rn); + + return bsgrp; +} + +static uint32_t hash_calc_on_grp_rp(struct prefix group, struct in_addr rp, + uint8_t hashmasklen) +{ + uint64_t temp; + uint32_t hash; + uint32_t grpaddr; + uint32_t rp_add; + uint32_t mask = 0xffffffff; + + /* mask to be made zero if hashmasklen is 0 because mask << 32 + * may not give 0. hashmasklen can be 0 to 32. + */ + if (hashmasklen == 0) + mask = 0; + + /* in_addr stores ip in big endian, hence network byte order + * convert to uint32 before processing hash + */ + grpaddr = ntohl(group.u.prefix4.s_addr); + /* Avoid shifting by 32 bit on a 32 bit register */ + if (hashmasklen) + grpaddr = grpaddr & ((mask << (32 - hashmasklen))); + else + grpaddr = grpaddr & mask; + rp_add = ntohl(rp.s_addr); + temp = 1103515245 * ((1103515245 * (uint64_t)grpaddr + 12345) ^ rp_add) + + 12345; + hash = temp & (0x7fffffff); + return hash; +} + +static bool pim_install_bsm_grp_rp(struct pim_instance *pim, + struct bsgrp_node *grpnode, + struct bsmmsg_rpinfo *rp) +{ + struct bsm_rpinfo *bsm_rpinfo; + uint8_t hashMask_len = pim->global_scope.hashMasklen; + + /*memory allocation for bsm_rpinfo */ + bsm_rpinfo = XCALLOC(MTYPE_PIM_BSRP_NODE, sizeof(*bsm_rpinfo)); + + bsm_rpinfo->rp_prio = rp->rp_pri; + bsm_rpinfo->rp_holdtime = rp->rp_holdtime; + memcpy(&bsm_rpinfo->rp_address, &rp->rpaddr.addr, + sizeof(struct in_addr)); + bsm_rpinfo->elapse_time = 0; + + /* Back pointer to the group node. */ + bsm_rpinfo->bsgrp_node = grpnode; + + /* update hash for this rp node */ + bsm_rpinfo->hash = hash_calc_on_grp_rp(grpnode->group, rp->rpaddr.addr, + hashMask_len); + if (listnode_add_sort_nodup(grpnode->partial_bsrp_list, bsm_rpinfo)) { + if (PIM_DEBUG_BSM) + zlog_debug( + "%s, bs_rpinfo node added to the partial bs_rplist.", + __func__); + return true; + } + + if (PIM_DEBUG_BSM) + zlog_debug("%s: list node not added", __func__); + + XFREE(MTYPE_PIM_BSRP_NODE, bsm_rpinfo); + return false; +} + +static void pim_update_pending_rp_cnt(struct bsm_scope *sz, + struct bsgrp_node *bsgrp, + uint16_t bsm_frag_tag, + uint32_t total_rp_count) +{ + if (bsgrp->pend_rp_cnt) { + /* received bsm is different packet , + * it is not same fragment. + */ + if (bsm_frag_tag != bsgrp->frag_tag) { + if (PIM_DEBUG_BSM) + zlog_debug( + "%s,Received a new BSM ,so clear the pending bs_rpinfo list.", + __func__); + list_delete_all_node(bsgrp->partial_bsrp_list); + bsgrp->pend_rp_cnt = total_rp_count; + } + } else + bsgrp->pend_rp_cnt = total_rp_count; + + bsgrp->frag_tag = bsm_frag_tag; +} + +/* Parsing BSR packet and adding to partial list of corresponding bsgrp node */ +static bool pim_bsm_parse_install_g2rp(struct bsm_scope *scope, uint8_t *buf, + int buflen, uint16_t bsm_frag_tag) +{ + struct bsmmsg_grpinfo grpinfo; + struct bsmmsg_rpinfo rpinfo; + struct prefix group; + struct bsgrp_node *bsgrp = NULL; + int frag_rp_cnt = 0; + int offset = 0; + int ins_count = 0; + + while (buflen > offset) { + if (offset + (int)sizeof(struct bsmmsg_grpinfo) > buflen) { + if (PIM_DEBUG_BSM) + zlog_debug( + "%s: buflen received %d is less than the internal data structure of the packet would suggest", + __func__, buflen); + return false; + } + /* Extract Group tlv from BSM */ + memcpy(&grpinfo, buf, sizeof(struct bsmmsg_grpinfo)); + + if (PIM_DEBUG_BSM) { + char grp_str[INET_ADDRSTRLEN]; + + pim_inet4_dump("", grpinfo.group.addr, grp_str, + sizeof(grp_str)); + zlog_debug( + "%s, Group %s Rpcount:%d Fragment-Rp-count:%d", + __func__, grp_str, grpinfo.rp_count, + grpinfo.frag_rp_count); + } + + buf += sizeof(struct bsmmsg_grpinfo); + offset += sizeof(struct bsmmsg_grpinfo); + + if (grpinfo.rp_count == 0) { + if (PIM_DEBUG_BSM) { + char grp_str[INET_ADDRSTRLEN]; + + pim_inet4_dump("", grpinfo.group.addr, + grp_str, sizeof(grp_str)); + zlog_debug("%s, Rp count is zero for group: %s", + __func__, grp_str); + } + return false; + } + + group.family = AF_INET; + if (grpinfo.group.mask > IPV4_MAX_BITLEN) { + if (PIM_DEBUG_BSM) + zlog_debug( + "%s, v4 prefix length specified: %d is too long", + __func__, grpinfo.group.mask); + return false; + } + group.prefixlen = grpinfo.group.mask; + group.u.prefix4.s_addr = grpinfo.group.addr.s_addr; + + /* Get the Group node for the BSM rp table */ + bsgrp = pim_bsm_get_bsgrp_node(scope, &group); + + if (!bsgrp) { + if (PIM_DEBUG_BSM) + zlog_debug("%s, Create new BSM Group node.", + __func__); + + /* create a new node to be added to the tree. */ + bsgrp = pim_bsm_new_bsgrp_node(scope->bsrp_table, + &group); + + if (!bsgrp) { + zlog_debug( + "%s, Failed to get the BSM group node.", + __func__); + continue; + } + + bsgrp->scope = scope; + } + + pim_update_pending_rp_cnt(scope, bsgrp, bsm_frag_tag, + grpinfo.rp_count); + frag_rp_cnt = grpinfo.frag_rp_count; + ins_count = 0; + + while (frag_rp_cnt--) { + if (offset + (int)sizeof(struct bsmmsg_rpinfo) + > buflen) { + if (PIM_DEBUG_BSM) + zlog_debug( + "%s, buflen received: %u is less than the internal data structure of the packet would suggest", + __func__, buflen); + return false; + } + + /* Extract RP address tlv from BSM */ + memcpy(&rpinfo, buf, sizeof(struct bsmmsg_rpinfo)); + rpinfo.rp_holdtime = ntohs(rpinfo.rp_holdtime); + buf += sizeof(struct bsmmsg_rpinfo); + offset += sizeof(struct bsmmsg_rpinfo); + + if (PIM_DEBUG_BSM) { + char rp_str[INET_ADDRSTRLEN]; + + pim_inet4_dump("", rpinfo.rpaddr.addr, + rp_str, sizeof(rp_str)); + zlog_debug( + "%s, Rp address - %s; pri:%d hold:%d", + __func__, rp_str, rpinfo.rp_pri, + rpinfo.rp_holdtime); + } + + /* Call Install api to update grp-rp mappings */ + if (pim_install_bsm_grp_rp(scope->pim, bsgrp, &rpinfo)) + ins_count++; + } + + bsgrp->pend_rp_cnt -= ins_count; + + if (!bsgrp->pend_rp_cnt) { + if (PIM_DEBUG_BSM) + zlog_debug( + "%s, Recvd all the rps for this group, so bsrp list with penidng rp list.", + __func__); + /* replace the bsrp_list with pending list */ + pim_instate_pend_list(bsgrp); + } + } + return true; +} + +int pim_bsm_process(struct interface *ifp, struct ip *ip_hdr, uint8_t *buf, + uint32_t buf_size, bool no_fwd) +{ + struct bsm_hdr *bshdr; + int sz = PIM_GBL_SZ_ID; + struct bsmmsg_grpinfo *msg_grp; + struct pim_interface *pim_ifp = NULL; + struct bsm_info *bsminfo; + struct pim_instance *pim; + char bsr_str[INET_ADDRSTRLEN]; + uint16_t frag_tag; + bool empty_bsm = false; + + /* BSM Packet acceptance validation */ + pim_ifp = ifp->info; + if (!pim_ifp) { + if (PIM_DEBUG_BSM) + zlog_debug("%s: multicast not enabled on interface %s", + __func__, ifp->name); + return -1; + } + + pim_ifp->pim_ifstat_bsm_rx++; + pim = pim_ifp->pim; + pim->bsm_rcvd++; + + /* Drop if bsm processing is disabled on interface */ + if (!pim_ifp->bsm_enable) { + zlog_warn("%s: BSM not enabled on interface %s", __func__, + ifp->name); + pim_ifp->pim_ifstat_bsm_cfg_miss++; + pim->bsm_dropped++; + return -1; + } + + if (buf_size < (PIM_MSG_HEADER_LEN + sizeof(struct bsm_hdr))) { + if (PIM_DEBUG_BSM) + zlog_debug( + "%s: received buffer length of %d which is too small to properly decode", + __func__, buf_size); + return -1; + } + + bshdr = (struct bsm_hdr *)(buf + PIM_MSG_HEADER_LEN); + pim_inet4_dump("", bshdr->bsr_addr.addr, bsr_str, + sizeof(bsr_str)); + if (bshdr->hm_len > 32) { + zlog_warn("Bad hashmask length for IPv4; got %hhu, expected value in range 0-32", + bshdr->hm_len); + pim->bsm_dropped++; + return -1; + } + pim->global_scope.hashMasklen = bshdr->hm_len; + frag_tag = ntohs(bshdr->frag_tag); + + /* Identify empty BSM */ + if ((buf_size - PIM_BSM_HDR_LEN - PIM_MSG_HEADER_LEN) < PIM_BSM_GRP_LEN) + empty_bsm = true; + + if (!empty_bsm) { + msg_grp = (struct bsmmsg_grpinfo *)(buf + PIM_MSG_HEADER_LEN + + PIM_BSM_HDR_LEN); + /* Currently we don't support scope zoned BSM */ + if (msg_grp->group.sz) { + if (PIM_DEBUG_BSM) + zlog_debug( + "%s : Administratively scoped range BSM received", + __func__); + pim_ifp->pim_ifstat_bsm_invalid_sz++; + pim->bsm_dropped++; + return -1; + } + } + + /* Drop if bsr is not preferred bsr */ + if (!is_preferred_bsr(pim, bshdr->bsr_addr.addr, bshdr->bsr_prio)) { + if (PIM_DEBUG_BSM) + zlog_debug("%s : Received a non-preferred BSM", + __func__); + pim->bsm_dropped++; + return -1; + } + + if (no_fwd) { + /* only accept no-forward BSM if quick refresh on startup */ + if ((pim->global_scope.accept_nofwd_bsm) + || (frag_tag == pim->global_scope.bsm_frag_tag)) { + pim->global_scope.accept_nofwd_bsm = false; + } else { + if (PIM_DEBUG_BSM) + zlog_debug( + "%s : nofwd_bsm received on %s when accpt_nofwd_bsm false", + __func__, bsr_str); + pim->bsm_dropped++; + pim_ifp->pim_ifstat_ucast_bsm_cfg_miss++; + return -1; + } + } + + /* Mulicast BSM received */ + if (ip_hdr->ip_dst.s_addr == qpim_all_pim_routers_addr.s_addr) { + if (!no_fwd) { + if (!pim_bsr_rpf_check(pim, bshdr->bsr_addr.addr, + ip_hdr->ip_src)) { + if (PIM_DEBUG_BSM) + zlog_debug( + "%s : RPF check fail for BSR address %s", + __func__, bsr_str); + pim->bsm_dropped++; + return -1; + } + } + } else if (if_lookup_exact_address(&ip_hdr->ip_dst, AF_INET, + pim->vrf_id)) { + /* Unicast BSM received - if ucast bsm not enabled on + * the interface, drop it + */ + if (!pim_ifp->ucast_bsm_accept) { + if (PIM_DEBUG_BSM) + zlog_debug( + "%s : Unicast BSM not enabled on interface %s", + __func__, ifp->name); + pim_ifp->pim_ifstat_ucast_bsm_cfg_miss++; + pim->bsm_dropped++; + return -1; + } + + } else { + if (PIM_DEBUG_BSM) + zlog_debug("%s : Invalid destination address", + __func__); + pim->bsm_dropped++; + return -1; + } + + if (empty_bsm) { + if (PIM_DEBUG_BSM) + zlog_debug("%s : Empty Pref BSM received", __func__); + } + /* Parse Update bsm rp table and install/uninstall rp if required */ + if (!pim_bsm_parse_install_g2rp( + &pim_ifp->pim->global_scope, + (buf + PIM_BSM_HDR_LEN + PIM_MSG_HEADER_LEN), + (buf_size - PIM_BSM_HDR_LEN - PIM_MSG_HEADER_LEN), + frag_tag)) { + if (PIM_DEBUG_BSM) { + zlog_debug("%s, Parsing BSM failed.", __func__); + } + pim->bsm_dropped++; + return -1; + } + /* Restart the bootstrap timer */ + pim_bs_timer_restart(&pim_ifp->pim->global_scope, + PIM_BSR_DEFAULT_TIMEOUT); + + /* If new BSM received, clear the old bsm database */ + if (pim_ifp->pim->global_scope.bsm_frag_tag != frag_tag) { + if (PIM_DEBUG_BSM) { + zlog_debug("%s: Current frag tag: %d Frag teg rcvd: %d", + __func__, + pim_ifp->pim->global_scope.bsm_frag_tag, + frag_tag); + } + list_delete_all_node(pim_ifp->pim->global_scope.bsm_list); + pim_ifp->pim->global_scope.bsm_frag_tag = frag_tag; + } + + /* update the scope information from bsm */ + pim_bsm_update(pim, bshdr->bsr_addr.addr, bshdr->bsr_prio); + + if (!no_fwd) { + pim_bsm_fwd_whole_sz(pim_ifp->pim, buf, buf_size, sz); + bsminfo = XCALLOC(MTYPE_PIM_BSM_INFO, sizeof(struct bsm_info)); + + bsminfo->bsm = XCALLOC(MTYPE_PIM_BSM_PKT_VAR_MEM, buf_size); + + bsminfo->size = buf_size; + memcpy(bsminfo->bsm, buf, buf_size); + listnode_add(pim_ifp->pim->global_scope.bsm_list, bsminfo); + } + + return 0; +} diff --git a/pimd/pim_bsm.h b/pimd/pim_bsm.h new file mode 100644 index 0000000000..0758c94f19 --- /dev/null +++ b/pimd/pim_bsm.h @@ -0,0 +1,198 @@ +/* + * pim_bsm.h: PIM BSM handling related + * + * Copyright (C) 2018-19 Vmware, Inc. + * Saravanan K + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#ifndef __PIM_BSM_H__ +#define __PIM_BSM_H__ + +#include "if.h" +#include "vty.h" +#include "linklist.h" +#include "table.h" +#include "pim_rp.h" +#include "pim_msg.h" + +/* Defines */ +#define PIM_GBL_SZ_ID 0 /* global scope zone id set to 0 */ +#define PIM_BS_TIME 60 /* RFC 5059 - Sec 5 */ +#define PIM_BSR_DEFAULT_TIMEOUT 130 /* RFC 5059 - Sec 5 */ + +/* These structures are only encoded IPv4 specific */ +#define PIM_BSM_HDR_LEN sizeof(struct bsm_hdr) +#define PIM_BSM_GRP_LEN sizeof(struct bsmmsg_grpinfo) +#define PIM_BSM_RP_LEN sizeof(struct bsmmsg_rpinfo) + +#define PIM_MIN_BSM_LEN \ + (PIM_HDR_LEN + PIM_BSM_HDR_LEN + PIM_BSM_GRP_LEN + PIM_BSM_RP_LEN) + +/* Datastructures + * ============== + */ + +/* Non candidate BSR states */ +enum ncbsr_state { + NO_INFO = 0, + ACCEPT_ANY, + ACCEPT_PREFERRED +}; + +/* BSM scope - bsm processing is per scope */ +struct bsm_scope { + int sz_id; /* scope zone id */ + enum ncbsr_state state; /* non candidate BSR state */ + bool accept_nofwd_bsm; /* no fwd bsm accepted for scope */ + struct in_addr current_bsr; /* current elected BSR for the sz */ + uint32_t current_bsr_prio; /* current BSR priority */ + int64_t current_bsr_first_ts; /* current BSR elected time */ + int64_t current_bsr_last_ts; /* Last BSM received from E-BSR */ + uint16_t bsm_frag_tag; /* Last received frag tag from E-BSR */ + uint8_t hashMasklen; /* Mask in hash calc RFC 7761 4.7.2 */ + struct pim_instance *pim; /* Back pointer to pim instance */ + struct list *bsm_list; /* list of bsm frag for frowarding */ + struct route_table *bsrp_table; /* group2rp mapping rcvd from BSR */ + struct thread *bs_timer; /* Boot strap timer */ + struct thread *sz_timer; +}; + +/* BSM packet - this is stored as list in bsm_list inside scope + * This is used for forwarding to new neighbors or restarting mcast routers + */ +struct bsm_info { + uint32_t size; /* size of the packet */ + unsigned char *bsm; /* Actual packet */ +}; + +/* This is the group node of the bsrp table in scope. + * this node maintains the list of rp for the group. + */ +struct bsgrp_node { + struct prefix group; /* Group range */ + struct bsm_scope *scope; /* Back ptr to scope */ + struct list *bsrp_list; /* list of RPs adv by BSR */ + struct list *partial_bsrp_list; /* maintained until all RPs received */ + int pend_rp_cnt; /* Total RP - Received RP */ + uint16_t frag_tag; /* frag tag to identify the fragment */ +}; + +/* This is the list node of bsrp_list and partial bsrp list in + * bsgrp_node. Hold info of each RP received for the group + */ +struct bsm_rpinfo { + uint32_t hash; /* Hash Value as per RFC 7761 4.7.2 */ + uint32_t elapse_time; /* upd at expiry of elected RP node */ + uint16_t rp_prio; /* RP priority */ + uint16_t rp_holdtime; /* RP holdtime - g2rp timer value */ + struct in_addr rp_address; /* RP Address */ + struct bsgrp_node *bsgrp_node; /* Back ptr to bsgrp_node */ + struct thread *g2rp_timer; /* Run only for elected RP node */ +}; + +/* Structures to extract Bootstrap Message header and Grp to RP Mappings + * ===================================================================== + * BSM Format: + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |PIM Ver| Type |N| Reserved | Checksum | PIM HDR + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Fragment Tag | Hash Mask Len | BSR Priority | BS HDR(1) + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | BSR Address (Encoded-Unicast format) | BS HDR(2) + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Group Address 1 (Encoded-Group format) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | RP Count 1 | Frag RP Cnt 1 | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | RP Address 1 (Encoded-Unicast format) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | RP1 Holdtime | RP1 Priority | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | RP Address 2 (Encoded-Unicast format) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | RP2 Holdtime | RP2 Priority | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | . | + * | . | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | RP Address m (Encoded-Unicast format) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | RPm Holdtime | RPm Priority | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Group Address 2 (Encoded-Group format) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | . | + * | . | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Group Address n (Encoded-Group format) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | RP Count n | Frag RP Cnt n | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | RP Address 1 (Encoded-Unicast format) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | RP1 Holdtime | RP1 Priority | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | RP Address 2 (Encoded-Unicast format) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | RP2 Holdtime | RP2 Priority | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | . | + * | . | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | RP Address m (Encoded-Unicast format) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | RPm Holdtime | RPm Priority | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +struct bsm_hdr { + uint16_t frag_tag; + uint8_t hm_len; + uint8_t bsr_prio; + struct pim_encoded_ipv4_unicast bsr_addr; +} __attribute__((packed)); + +struct bsmmsg_grpinfo { + struct pim_encoded_group_ipv4 group; + uint8_t rp_count; + uint8_t frag_rp_count; + uint16_t reserved; +} __attribute__((packed)); + +struct bsmmsg_rpinfo { + struct pim_encoded_ipv4_unicast rpaddr; + uint16_t rp_holdtime; + uint8_t rp_pri; + uint8_t reserved; +} __attribute__((packed)); + +/* API */ +void pim_bsm_proc_init(struct pim_instance *pim); +void pim_bsm_proc_free(struct pim_instance *pim); +void pim_bsm_write_config(struct vty *vty, struct interface *ifp); +int pim_bsm_process(struct interface *ifp, + struct ip *ip_hdr, + uint8_t *buf, + uint32_t buf_size, + bool no_fwd); +bool pim_bsm_new_nbr_fwd(struct pim_neighbor *neigh, struct interface *ifp); +struct bsgrp_node *pim_bsm_get_bsgrp_node(struct bsm_scope *scope, + struct prefix *grp); +#endif diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index 9c1cb38012..f006bd43ab 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -61,17 +61,28 @@ #include "pim_nht.h" #include "pim_bfd.h" #include "pim_vxlan.h" +#include "pim_mlag.h" #include "bfd.h" +#include "pim_bsm.h" #ifndef VTYSH_EXTRACT_PL #include "pimd/pim_cmd_clippy.c" #endif static struct cmd_node interface_node = { - INTERFACE_NODE, "%s(config-if)# ", 1 /* vtysh ? yes */ + .name = "interface", + .node = INTERFACE_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-if)# ", + .config_write = pim_interface_config_write, }; -static struct cmd_node debug_node = {DEBUG_NODE, "", 1}; +static struct cmd_node debug_node = { + .name = "debug", + .node = DEBUG_NODE, + .prompt = "", + .config_write = pim_debug_config_write, +}; static struct vrf *pim_cmd_lookup_vrf(struct vty *vty, struct cmd_token *argv[], const int argc, int *idx) @@ -164,7 +175,7 @@ static void pim_if_membership_refresh(struct interface *ifp) sg.src = src->source_addr; sg.grp = grp->group_addr; pim_ifchannel_local_membership_add(ifp, - &sg); + &sg, false /*is_vxlan*/); } } /* scan group sources */ @@ -657,6 +668,7 @@ static void igmp_show_interfaces_single(struct pim_instance *pim, long oqpi_msec; /* Other Querier Present Interval */ long qri_msec; time_t now; + int lmqc; json_object *json = NULL; json_object *json_row = NULL; @@ -701,8 +713,8 @@ static void igmp_show_interfaces_single(struct pim_instance *pim, pim_ifp->igmp_query_max_response_time_dsec); lmqt_msec = PIM_IGMP_LMQT_MSEC( - pim_ifp->igmp_query_max_response_time_dsec, - igmp->querier_robustness_variable); + pim_ifp->igmp_specific_query_max_response_time_dsec, + pim_ifp->igmp_last_member_query_count); ohpi_msec = PIM_IGMP_OHPI_DSEC( @@ -718,6 +730,7 @@ static void igmp_show_interfaces_single(struct pim_instance *pim, pim_ifp->pim_sock_fd); else mloop = 0; + lmqc = pim_ifp->igmp_last_member_query_count; if (uj) { json_row = json_object_new_object(); @@ -742,6 +755,9 @@ static void igmp_show_interfaces_single(struct pim_instance *pim, json_row, "timerGroupMembershipIntervalMsec", gmi_msec); + json_object_int_add(json_row, + "lastMemberQueryCount", + lmqc); json_object_int_add(json_row, "timerLastMemberQueryMsec", lmqt_msec); @@ -808,6 +824,9 @@ static void igmp_show_interfaces_single(struct pim_instance *pim, vty_out(vty, "Group Membership Interval : %lis\n", gmi_msec / 1000); + vty_out(vty, + "Last Member Query Count : %d\n", + lmqc); vty_out(vty, "Last Member Query Time : %lis\n", lmqt_msec / 1000); @@ -899,12 +918,11 @@ static void igmp_show_interface_join(struct pim_instance *pim, struct vty *vty) static void pim_show_interfaces_single(struct pim_instance *pim, struct vty *vty, const char *ifname, - bool uj) + bool mlag, bool uj) { struct in_addr ifaddr; struct interface *ifp; struct listnode *neighnode; - struct listnode *upnode; struct pim_interface *pim_ifp; struct pim_neighbor *neigh; struct pim_upstream *up; @@ -943,6 +961,9 @@ static void pim_show_interfaces_single(struct pim_instance *pim, if (!pim_ifp) continue; + if (mlag == true && pim_ifp->activeactive == false) + continue; + if (strcmp(ifname, "detail") && strcmp(ifname, ifp->name)) continue; @@ -1042,8 +1063,7 @@ static void pim_show_interfaces_single(struct pim_instance *pim, pim_ifp->pim_dr_election_changes); // FHR - for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, upnode, - up)) { + frr_each (rb_pim_upstream, &pim->upstream_head, up) { if (ifp != up->rpf.source_nexthop.interface) continue; @@ -1205,8 +1225,9 @@ static void pim_show_interfaces_single(struct pim_instance *pim, // FHR print_header = 1; - for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, upnode, - up)) { + frr_each (rb_pim_upstream, &pim->upstream_head, up) { + if (!up->rpf.source_nexthop.interface) + continue; if (strcmp(ifp->name, up->rpf.source_nexthop @@ -1371,10 +1392,9 @@ static void igmp_show_statistics(struct pim_instance *pim, struct vty *vty, } static void pim_show_interfaces(struct pim_instance *pim, struct vty *vty, - bool uj) + bool mlag, bool uj) { struct interface *ifp; - struct listnode *upnode; struct pim_interface *pim_ifp; struct pim_upstream *up; int fhr = 0; @@ -1392,11 +1412,14 @@ static void pim_show_interfaces(struct pim_instance *pim, struct vty *vty, if (!pim_ifp) continue; + if (mlag == true && pim_ifp->activeactive == false) + continue; + pim_nbrs = pim_ifp->pim_neighbor_list->count; pim_ifchannels = pim_if_ifchannel_count(pim_ifp); fhr = 0; - for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, upnode, up)) + frr_each (rb_pim_upstream, &pim->upstream_head, up) if (ifp == up->rpf.source_nexthop.interface) if (up->flags & PIM_UPSTREAM_FLAG_MASK_FHR) fhr++; @@ -1475,13 +1498,14 @@ static void pim_show_interface_traffic(struct pim_instance *pim, json = json_object_new_object(); else { vty_out(vty, "\n"); - vty_out(vty, "%-16s%-17s%-17s%-17s%-17s%-17s%-17s\n", + vty_out(vty, "%-16s%-17s%-17s%-17s%-17s%-17s%-17s%-17s\n", "Interface", " HELLO", " JOIN", " PRUNE", " REGISTER", "REGISTER-STOP", - " ASSERT"); - vty_out(vty, "%-16s%-17s%-17s%-17s%-17s%-17s%-17s\n", "", + " ASSERT", " BSM"); + vty_out(vty, "%-16s%-17s%-17s%-17s%-17s%-17s%-17s%-17s\n", "", " Rx/Tx", " Rx/Tx", " Rx/Tx", - " Rx/Tx", " Rx/Tx", " Rx/Tx"); + " Rx/Tx", " Rx/Tx", " Rx/Tx", + " Rx/Tx"); vty_out(vty, "---------------------------------------------------------------------------------------------------------------\n"); } @@ -1505,6 +1529,10 @@ static void pim_show_interface_traffic(struct pim_instance *pim, pim_ifp->pim_ifstat_join_recv); json_object_int_add(json_row, "joinTx", pim_ifp->pim_ifstat_join_send); + json_object_int_add(json_row, "pruneTx", + pim_ifp->pim_ifstat_prune_send); + json_object_int_add(json_row, "pruneRx", + pim_ifp->pim_ifstat_prune_recv); json_object_int_add(json_row, "registerRx", pim_ifp->pim_ifstat_reg_recv); json_object_int_add(json_row, "registerTx", @@ -1516,12 +1544,15 @@ static void pim_show_interface_traffic(struct pim_instance *pim, json_object_int_add(json_row, "assertRx", pim_ifp->pim_ifstat_assert_recv); json_object_int_add(json_row, "assertTx", - pim_ifp->pim_ifstat_assert_send); - + pim_ifp->pim_ifstat_assert_send); + json_object_int_add(json_row, "bsmRx", + pim_ifp->pim_ifstat_bsm_rx); + json_object_int_add(json_row, "bsmTx", + pim_ifp->pim_ifstat_bsm_tx); json_object_object_add(json, ifp->name, json_row); } else { vty_out(vty, - "%-16s %8u/%-8u %7u/%-7u %7u/%-7u %7u/%-7u %7u/%-7u %7u/%-7u \n", + "%-16s %8u/%-8u %7u/%-7u %7u/%-7u %7u/%-7u %7u/%-7u %7u/%-7u %7" PRIu64 "/%-7" PRIu64 "\n", ifp->name, pim_ifp->pim_ifstat_hello_recv, pim_ifp->pim_ifstat_hello_sent, pim_ifp->pim_ifstat_join_recv, @@ -1533,7 +1564,9 @@ static void pim_show_interface_traffic(struct pim_instance *pim, pim_ifp->pim_ifstat_reg_stop_recv, pim_ifp->pim_ifstat_reg_stop_send, pim_ifp->pim_ifstat_assert_recv, - pim_ifp->pim_ifstat_assert_send); + pim_ifp->pim_ifstat_assert_send, + pim_ifp->pim_ifstat_bsm_rx, + pim_ifp->pim_ifstat_bsm_tx); } } if (uj) { @@ -1557,14 +1590,15 @@ static void pim_show_interface_traffic_single(struct pim_instance *pim, json = json_object_new_object(); else { vty_out(vty, "\n"); - vty_out(vty, "%-16s%-17s%-17s%-17s%-17s%-17s%-17s\n", + vty_out(vty, "%-16s%-17s%-17s%-17s%-17s%-17s%-17s%-17s\n", "Interface", " HELLO", " JOIN", " PRUNE", - " REGISTER", " REGISTER-STOP", " ASSERT"); - vty_out(vty, "%-14s%-18s%-17s%-17s%-17s%-17s%-17s\n", "", + " REGISTER", " REGISTER-STOP", " ASSERT", + " BSM"); + vty_out(vty, "%-14s%-18s%-17s%-17s%-17s%-17s%-17s%-17s\n", "", " Rx/Tx", " Rx/Tx", " Rx/Tx", " Rx/Tx", - " Rx/Tx", " Rx/Tx"); + " Rx/Tx", " Rx/Tx", " Rx/Tx"); vty_out(vty, - "---------------------------------------------------------------------------------------------------------------------\n"); + "-------------------------------------------------------------------------------------------------------------------------------\n"); } FOR_ALL_INTERFACES (pim->vrf, ifp) { @@ -1603,11 +1637,15 @@ static void pim_show_interface_traffic_single(struct pim_instance *pim, pim_ifp->pim_ifstat_assert_recv); json_object_int_add(json_row, "assertTx", pim_ifp->pim_ifstat_assert_send); + json_object_int_add(json_row, "bsmRx", + pim_ifp->pim_ifstat_bsm_rx); + json_object_int_add(json_row, "bsmTx", + pim_ifp->pim_ifstat_bsm_tx); json_object_object_add(json, ifp->name, json_row); } else { vty_out(vty, - "%-16s %8u/%-8u %7u/%-7u %7u/%-7u %7u/%-7u %7u/%-7u %7u/%-7u \n", + "%-16s %8u/%-8u %7u/%-7u %7u/%-7u %7u/%-7u %7u/%-7u %7u/%-7u %7" PRIu64 "/%-7" PRIu64 "\n", ifp->name, pim_ifp->pim_ifstat_hello_recv, pim_ifp->pim_ifstat_hello_sent, pim_ifp->pim_ifstat_join_recv, @@ -1619,7 +1657,9 @@ static void pim_show_interface_traffic_single(struct pim_instance *pim, pim_ifp->pim_ifstat_reg_stop_recv, pim_ifp->pim_ifstat_reg_stop_send, pim_ifp->pim_ifstat_assert_recv, - pim_ifp->pim_ifstat_assert_send); + pim_ifp->pim_ifstat_assert_send, + pim_ifp->pim_ifstat_bsm_rx, + pim_ifp->pim_ifstat_bsm_tx); } } if (uj) { @@ -1679,7 +1719,10 @@ static void pim_show_join_helper(struct vty *vty, struct pim_interface *pim_ifp, pim_ifchannel_ifjoin_name(ch->ifjoin_state, ch->flags)); if (PIM_IF_FLAG_TEST_S_G_RPT(ch->flags)) json_object_int_add(json_row, "SGRpt", 1); - + if (PIM_IF_FLAG_TEST_PROTO_PIM(ch->flags)) + json_object_int_add(json_row, "protocolPim", 1); + if (PIM_IF_FLAG_TEST_PROTO_IGMP(ch->flags)) + json_object_int_add(json_row, "protocolIgmp", 1); json_object_object_get_ex(json_iface, ch_grp_str, &json_grp); if (!json_grp) { json_grp = json_object_new_object(); @@ -1720,12 +1763,12 @@ static void pim_show_join(struct pim_instance *pim, struct vty *vty, continue; RB_FOREACH (ch, pim_ifchannel_rb, &pim_ifp->ifchannel_rb) { - if (sg->grp.s_addr != 0 + if (sg->grp.s_addr != INADDR_ANY && sg->grp.s_addr != ch->sg.grp.s_addr) continue; - if (sg->src.s_addr != 0 + if (sg->src.s_addr != INADDR_ANY && sg->src.s_addr != ch->sg.src.s_addr) - continue; + continue; pim_show_join_helper(vty, pim_ifp, ch, json, now, uj); } /* scan interface channels */ } @@ -1950,7 +1993,6 @@ static void pim_show_state(struct pim_instance *pim, struct vty *vty, const char *src_or_group, const char *group, bool uj) { struct channel_oil *c_oil; - struct listnode *node; json_object *json = NULL; json_object *json_group = NULL; json_object *json_ifp_in = NULL; @@ -1964,20 +2006,28 @@ static void pim_show_state(struct pim_instance *pim, struct vty *vty, json = json_object_new_object(); } else { vty_out(vty, - "Codes: J -> Pim Join, I -> IGMP Report, S -> Source, * -> Inherited from (*,G), V -> VxLAN"); + "Codes: J -> Pim Join, I -> IGMP Report, S -> Source, * -> Inherited from (*,G), V -> VxLAN, M -> Muted"); vty_out(vty, - "\nInstalled Source Group IIF OIL\n"); + "\nActive Source Group RPT IIF OIL\n"); } - for (ALL_LIST_ELEMENTS_RO(pim->channel_oil_list, node, c_oil)) { + frr_each (rb_pim_oil, &pim->channel_oil_head, c_oil) { char grp_str[INET_ADDRSTRLEN]; char src_str[INET_ADDRSTRLEN]; char in_ifname[INTERFACE_NAMSIZ + 1]; char out_ifname[INTERFACE_NAMSIZ + 1]; int oif_vif_index; struct interface *ifp_in; + bool isRpt; first_oif = 1; + if ((c_oil->up && + PIM_UPSTREAM_FLAG_TEST_USE_RPT(c_oil->up->flags)) || + c_oil->oil.mfcc_origin.s_addr == INADDR_ANY) + isRpt = true; + else + isRpt = false; + pim_inet4_dump("", c_oil->oil.mfcc_mcastgrp, grp_str, sizeof(grp_str)); pim_inet4_dump("", c_oil->oil.mfcc_origin, src_str, @@ -1985,9 +2035,9 @@ static void pim_show_state(struct pim_instance *pim, struct vty *vty, ifp_in = pim_if_find_by_vif_index(pim, c_oil->oil.mfcc_parent); if (ifp_in) - strcpy(in_ifname, ifp_in->name); + strlcpy(in_ifname, ifp_in->name, sizeof(in_ifname)); else - strcpy(in_ifname, ""); + strlcpy(in_ifname, "", sizeof(in_ifname)); if (src_or_group) { if (strcmp(src_or_group, src_str) @@ -2031,6 +2081,12 @@ static void pim_show_state(struct pim_instance *pim, struct vty *vty, json_ifp_in); json_object_int_add(json_source, "Installed", c_oil->installed); + if (isRpt) + json_object_boolean_true_add( + json_source, "isRpt"); + else + json_object_boolean_false_add( + json_source, "isRpt"); json_object_int_add(json_source, "RefCount", c_oil->oil_ref_count); json_object_int_add(json_source, "OilListSize", @@ -2049,8 +2105,9 @@ static void pim_show_state(struct pim_instance *pim, struct vty *vty, c_oil->cc.wrong_if); } } else { - vty_out(vty, "%-9d %-15s %-15s %-16s ", - c_oil->installed, src_str, grp_str, in_ifname); + vty_out(vty, "%-6d %-15s %-15s %-3s %-16s ", + c_oil->installed, src_str, grp_str, + isRpt ? "y" : "n", in_ifname); } for (oif_vif_index = 0; oif_vif_index < MAXVIFS; @@ -2069,9 +2126,9 @@ static void pim_show_state(struct pim_instance *pim, struct vty *vty, now - c_oil->oif_creation[oif_vif_index]); if (ifp_out) - strcpy(out_ifname, ifp_out->name); + strlcpy(out_ifname, ifp_out->name, sizeof(out_ifname)); else - strcpy(out_ifname, ""); + strlcpy(out_ifname, "", sizeof(out_ifname)); if (uj) { json_ifp_out = json_object_new_object(); @@ -2093,7 +2150,8 @@ static void pim_show_state(struct pim_instance *pim, struct vty *vty, } else { if (first_oif) { first_oif = 0; - vty_out(vty, "%s(%c%c%c%c%c)", out_ifname, + vty_out(vty, "%s(%c%c%c%c%c)", + out_ifname, (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_IGMP) ? 'I' @@ -2106,13 +2164,13 @@ static void pim_show_state(struct pim_instance *pim, struct vty *vty, & PIM_OIF_FLAG_PROTO_VXLAN) ? 'V' : ' ', - (c_oil->oif_flags[oif_vif_index] - & PIM_OIF_FLAG_PROTO_SOURCE) - ? 'S' - : ' ', (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_STAR) ? '*' + : ' ', + (c_oil->oif_flags[oif_vif_index] + & PIM_OIF_FLAG_MUTE) + ? 'M' : ' '); } else vty_out(vty, ", %s(%c%c%c%c%c)", @@ -2129,13 +2187,13 @@ static void pim_show_state(struct pim_instance *pim, struct vty *vty, & PIM_OIF_FLAG_PROTO_VXLAN) ? 'V' : ' ', - (c_oil->oif_flags[oif_vif_index] - & PIM_OIF_FLAG_PROTO_SOURCE) - ? 'S' - : ' ', (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_STAR) ? '*' + : ' ', + (c_oil->oif_flags[oif_vif_index] + & PIM_OIF_FLAG_MUTE) + ? 'M' : ' '); } } @@ -2350,37 +2408,37 @@ static void json_object_pim_upstream_add(json_object *json, static const char * pim_upstream_state2brief_str(enum pim_upstream_state join_state, - char *state_str) + char *state_str, size_t state_str_len) { switch (join_state) { case PIM_UPSTREAM_NOTJOINED: - strcpy(state_str, "NotJ"); + strlcpy(state_str, "NotJ", state_str_len); break; case PIM_UPSTREAM_JOINED: - strcpy(state_str, "J"); + strlcpy(state_str, "J", state_str_len); break; default: - strcpy(state_str, "Unk"); + strlcpy(state_str, "Unk", state_str_len); } return state_str; } static const char *pim_reg_state2brief_str(enum pim_reg_state reg_state, - char *state_str) + char *state_str, size_t state_str_len) { switch (reg_state) { case PIM_REG_NOINFO: - strcpy(state_str, "RegNI"); + strlcpy(state_str, "RegNI", state_str_len); break; case PIM_REG_JOIN: - strcpy(state_str, "RegJ"); + strlcpy(state_str, "RegJ", state_str_len); break; case PIM_REG_JOIN_PENDING: case PIM_REG_PRUNE: - strcpy(state_str, "RegP"); + strlcpy(state_str, "RegP", state_str_len); break; default: - strcpy(state_str, "Unk"); + strlcpy(state_str, "Unk", state_str_len); } return state_str; } @@ -2388,7 +2446,6 @@ static const char *pim_reg_state2brief_str(enum pim_reg_state reg_state, static void pim_show_upstream(struct pim_instance *pim, struct vty *vty, struct prefix_sg *sg, bool uj) { - struct listnode *upnode; struct pim_upstream *up; time_t now; json_object *json = NULL; @@ -2403,7 +2460,7 @@ static void pim_show_upstream(struct pim_instance *pim, struct vty *vty, vty_out(vty, "Iif Source Group State Uptime JoinTimer RSTimer KATimer RefCnt\n"); - for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, upnode, up)) { + frr_each (rb_pim_upstream, &pim->upstream_head, up) { char src_str[INET_ADDRSTRLEN]; char grp_str[INET_ADDRSTRLEN]; char uptime[10]; @@ -2413,9 +2470,11 @@ static void pim_show_upstream(struct pim_instance *pim, struct vty *vty, char msdp_reg_timer[10]; char state_str[PIM_REG_STATE_STR_LEN]; - if (sg->grp.s_addr != 0 && sg->grp.s_addr != up->sg.grp.s_addr) + if (sg->grp.s_addr != INADDR_ANY + && sg->grp.s_addr != up->sg.grp.s_addr) continue; - if (sg->src.s_addr != 0 && sg->src.s_addr != up->sg.src.s_addr) + if (sg->src.s_addr != INADDR_ANY + && sg->src.s_addr != up->sg.src.s_addr) continue; pim_inet4_dump("", up->sg.src, src_str, sizeof(src_str)); @@ -2448,13 +2507,15 @@ static void pim_show_upstream(struct pim_instance *pim, struct vty *vty, pim_time_timer_to_hhmmss(msdp_reg_timer, sizeof(msdp_reg_timer), up->t_msdp_reg_timer); - pim_upstream_state2brief_str(up->join_state, state_str); + pim_upstream_state2brief_str(up->join_state, state_str, sizeof(state_str)); if (up->reg_state != PIM_REG_NOINFO) { char tmp_str[PIM_REG_STATE_STR_LEN]; + char tmp[sizeof(state_str) + 1]; - sprintf(state_str + strlen(state_str), ",%s", - pim_reg_state2brief_str(up->reg_state, - tmp_str)); + snprintf(tmp, sizeof(tmp), ",%s", + pim_reg_state2brief_str(up->reg_state, tmp_str, + sizeof(tmp_str))); + strlcat(state_str, tmp, sizeof(state_str)); } if (uj) { @@ -2505,7 +2566,7 @@ static void pim_show_upstream(struct pim_instance *pim, struct vty *vty, pim_upstream_state2str(up->join_state)); json_object_string_add( json_row, "regState", - pim_reg_state2str(up->reg_state, state_str)); + pim_reg_state2str(up->reg_state, state_str, sizeof(state_str))); json_object_string_add(json_row, "upTime", uptime); json_object_string_add(json_row, "joinTimer", join_timer); @@ -2537,7 +2598,7 @@ static void pim_show_upstream(struct pim_instance *pim, struct vty *vty, } } -static void pim_show_join_desired_helper(struct pim_instance *pim, +static void pim_show_channel_helper(struct pim_instance *pim, struct vty *vty, struct pim_interface *pim_ifp, struct pim_ifchannel *ch, @@ -2596,7 +2657,7 @@ static void pim_show_join_desired_helper(struct pim_instance *pim, } } -static void pim_show_join_desired(struct pim_instance *pim, struct vty *vty, +static void pim_show_channel(struct pim_instance *pim, struct vty *vty, bool uj) { struct pim_interface *pim_ifp; @@ -2620,7 +2681,7 @@ static void pim_show_join_desired(struct pim_instance *pim, struct vty *vty, RB_FOREACH (ch, pim_ifchannel_rb, &pim_ifp->ifchannel_rb) { /* scan all interfaces */ - pim_show_join_desired_helper(pim, vty, pim_ifp, ch, + pim_show_channel_helper(pim, vty, pim_ifp, ch, json, uj); } } @@ -2632,10 +2693,75 @@ static void pim_show_join_desired(struct pim_instance *pim, struct vty *vty, } } +static void pim_show_join_desired_helper(struct pim_instance *pim, + struct vty *vty, + struct pim_upstream *up, + json_object *json, bool uj) +{ + json_object *json_group = NULL; + char src_str[INET_ADDRSTRLEN]; + char grp_str[INET_ADDRSTRLEN]; + json_object *json_row = NULL; + + pim_inet4_dump("", up->sg.src, src_str, sizeof(src_str)); + pim_inet4_dump("", up->sg.grp, grp_str, sizeof(grp_str)); + + if (uj) { + json_object_object_get_ex(json, grp_str, &json_group); + + if (!json_group) { + json_group = json_object_new_object(); + json_object_object_add(json, grp_str, json_group); + } + + json_row = json_object_new_object(); + json_object_pim_upstream_add(json_row, up); + json_object_string_add(json_row, "source", src_str); + json_object_string_add(json_row, "group", grp_str); + + if (pim_upstream_evaluate_join_desired(pim, up)) + json_object_boolean_true_add(json_row, + "evaluateJoinDesired"); + + json_object_object_add(json_group, src_str, json_row); + + } else { + vty_out(vty, "%-15s %-15s %-6s\n", + src_str, grp_str, + pim_upstream_evaluate_join_desired(pim, up) ? "yes" + : "no"); + } +} + +static void pim_show_join_desired(struct pim_instance *pim, struct vty *vty, + bool uj) +{ + struct pim_upstream *up; + + json_object *json = NULL; + + if (uj) + json = json_object_new_object(); + else + vty_out(vty, + "Source Group EvalJD\n"); + + frr_each (rb_pim_upstream, &pim->upstream_head, up) { + /* scan all interfaces */ + pim_show_join_desired_helper(pim, vty, up, + json, uj); + } + + if (uj) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } +} + static void pim_show_upstream_rpf(struct pim_instance *pim, struct vty *vty, bool uj) { - struct listnode *upnode; struct pim_upstream *up; json_object *json = NULL; json_object *json_group = NULL; @@ -2647,7 +2773,7 @@ static void pim_show_upstream_rpf(struct pim_instance *pim, struct vty *vty, vty_out(vty, "Source Group RpfIface RibNextHop RpfAddress \n"); - for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, upnode, up)) { + frr_each (rb_pim_upstream, &pim->upstream_head, up) { char src_str[INET_ADDRSTRLEN]; char grp_str[INET_ADDRSTRLEN]; char rpf_nexthop_str[PREFIX_STRLEN]; @@ -2768,7 +2894,6 @@ static void show_scan_oil_stats(struct pim_instance *pim, struct vty *vty, static void pim_show_rpf(struct pim_instance *pim, struct vty *vty, bool uj) { - struct listnode *up_node; struct pim_upstream *up; time_t now = pim_time_monotonic_sec(); json_object *json = NULL; @@ -2785,7 +2910,7 @@ static void pim_show_rpf(struct pim_instance *pim, struct vty *vty, bool uj) "Source Group RpfIface RpfAddress RibNextHop Metric Pref\n"); } - for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, up_node, up)) { + frr_each (rb_pim_upstream, &pim->upstream_head, up) { char src_str[INET_ADDRSTRLEN]; char grp_str[INET_ADDRSTRLEN]; char rpf_addr_str[PREFIX_STRLEN]; @@ -2886,118 +3011,156 @@ static void pim_show_nexthop(struct pim_instance *pim, struct vty *vty) hash_walk(pim->rpf_hash, pim_print_pnc_cache_walkcb, &cwd); } -static void igmp_show_groups(struct pim_instance *pim, struct vty *vty, bool uj) +/* Display the bsm database details */ +static void pim_show_bsm_db(struct pim_instance *pim, struct vty *vty, bool uj) { - struct interface *ifp; - time_t now; + struct listnode *bsmnode; + int count = 0; + int fragment = 1; + struct bsm_info *bsm; json_object *json = NULL; - json_object *json_iface = NULL; + json_object *json_group = NULL; json_object *json_row = NULL; - now = pim_time_monotonic_sec(); + count = pim->global_scope.bsm_list->count; - if (uj) + if (uj) { json = json_object_new_object(); - else - vty_out(vty, - "Interface Address Group Mode Timer Srcs V Uptime \n"); + json_object_int_add(json, "Number of the fragments", count); + } else { + vty_out(vty, "Scope Zone: Global\n"); + vty_out(vty, "Number of the fragments: %d\n", count); + vty_out(vty, "\n"); + } - /* scan interfaces */ - FOR_ALL_INTERFACES (pim->vrf, ifp) { - struct pim_interface *pim_ifp = ifp->info; - struct listnode *sock_node; - struct igmp_sock *igmp; + for (ALL_LIST_ELEMENTS_RO(pim->global_scope.bsm_list, bsmnode, bsm)) { + char grp_str[PREFIX_STRLEN]; + char rp_str[INET_ADDRSTRLEN]; + char bsr_str[INET_ADDRSTRLEN]; + struct bsmmsg_grpinfo *group; + struct bsmmsg_rpinfo *rpaddr; + struct prefix grp; + struct bsm_hdr *hdr; + uint32_t offset = 0; + uint8_t *buf; + uint32_t len = 0; + uint32_t frag_rp_cnt = 0; - if (!pim_ifp) - continue; + buf = bsm->bsm; + len = bsm->size; - /* scan igmp sockets */ - for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, - igmp)) { - char ifaddr_str[INET_ADDRSTRLEN]; - struct listnode *grpnode; - struct igmp_group *grp; + /* skip pim header */ + buf += PIM_MSG_HEADER_LEN; + len -= PIM_MSG_HEADER_LEN; - pim_inet4_dump("", igmp->ifaddr, ifaddr_str, - sizeof(ifaddr_str)); + hdr = (struct bsm_hdr *)buf; - /* scan igmp groups */ - for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, - grpnode, grp)) { - char group_str[INET_ADDRSTRLEN]; - char hhmmss[10]; - char uptime[10]; + /* BSM starts with bsr header */ + buf += sizeof(struct bsm_hdr); + len -= sizeof(struct bsm_hdr); - pim_inet4_dump("", grp->group_addr, - group_str, sizeof(group_str)); - pim_time_timer_to_hhmmss(hhmmss, sizeof(hhmmss), - grp->t_group_timer); - pim_time_uptime(uptime, sizeof(uptime), - now - grp->group_creation); + pim_inet4_dump("", hdr->bsr_addr.addr, bsr_str, + sizeof(bsr_str)); - if (uj) { - json_object_object_get_ex( - json, ifp->name, &json_iface); - if (!json_iface) { - json_iface = - json_object_new_object(); - json_object_pim_ifp_add( - json_iface, ifp); - json_object_object_add( - json, ifp->name, - json_iface); - } + if (uj) { + json_object_string_add(json, "BSR address", bsr_str); + json_object_int_add(json, "BSR priority", + hdr->bsr_prio); + json_object_int_add(json, "Hashmask Length", + hdr->hm_len); + json_object_int_add(json, "Fragment Tag", + ntohs(hdr->frag_tag)); + } else { + vty_out(vty, "BSM Fragment : %d\n", fragment); + vty_out(vty, "------------------\n"); + vty_out(vty, "%-15s %-15s %-15s %-15s\n", "BSR-Address", + "BSR-Priority", "Hashmask-len", "Fragment-Tag"); + vty_out(vty, "%-15s %-15d %-15d %-15d\n", bsr_str, + hdr->bsr_prio, hdr->hm_len, + ntohs(hdr->frag_tag)); + } - json_row = json_object_new_object(); - json_object_string_add( - json_row, "source", ifaddr_str); - json_object_string_add( - json_row, "group", group_str); + vty_out(vty, "\n"); - if (grp->igmp_version == 3) - json_object_string_add( - json_row, "mode", - grp->group_filtermode_isexcl - ? "EXCLUDE" - : "INCLUDE"); + while (offset < len) { + group = (struct bsmmsg_grpinfo *)buf; - json_object_string_add(json_row, - "timer", hhmmss); + if (group->group.family == PIM_MSG_ADDRESS_FAMILY_IPV4) + grp.family = AF_INET; + + grp.prefixlen = group->group.mask; + grp.u.prefix4.s_addr = group->group.addr.s_addr; + + prefix2str(&grp, grp_str, sizeof(grp_str)); + + buf += sizeof(struct bsmmsg_grpinfo); + offset += sizeof(struct bsmmsg_grpinfo); + + if (uj) { + json_object_object_get_ex(json, grp_str, + &json_group); + if (!json_group) { + json_group = json_object_new_object(); + json_object_int_add(json_group, + "Rp Count", + group->rp_count); json_object_int_add( - json_row, "sourcesCount", - grp->group_source_list - ? listcount( - grp->group_source_list) - : 0); - json_object_int_add(json_row, "version", - grp->igmp_version); - json_object_string_add( - json_row, "uptime", uptime); - json_object_object_add(json_iface, - group_str, - json_row); + json_group, "Fragment Rp count", + group->frag_rp_count); + json_object_object_add(json, grp_str, + json_group); + } + } else { + vty_out(vty, "Group : %s\n", grp_str); + vty_out(vty, "-------------------\n"); + vty_out(vty, "Rp Count:%d\n", group->rp_count); + vty_out(vty, "Fragment Rp Count : %d\n", + group->frag_rp_count); + } + + frag_rp_cnt = group->frag_rp_count; + + if (!frag_rp_cnt) + continue; + + if (!uj) + vty_out(vty, + "RpAddress HoldTime Priority\n"); + + while (frag_rp_cnt--) { + rpaddr = (struct bsmmsg_rpinfo *)buf; + buf += sizeof(struct bsmmsg_rpinfo); + offset += sizeof(struct bsmmsg_rpinfo); + + pim_inet4_dump("", + rpaddr->rpaddr.addr, rp_str, + sizeof(rp_str)); + + if (uj) { + json_row = json_object_new_object(); + json_object_string_add( + json_row, "Rp Address", rp_str); + json_object_int_add( + json_row, "Rp HoldTime", + ntohs(rpaddr->rp_holdtime)); + json_object_int_add(json_row, + "Rp Priority", + rpaddr->rp_pri); + json_object_object_add( + json_group, rp_str, json_row); } else { - vty_out(vty, - "%-16s %-15s %-15s %4s %8s %4d %d %8s\n", - ifp->name, ifaddr_str, - group_str, - grp->igmp_version == 3 - ? (grp->group_filtermode_isexcl - ? "EXCL" - : "INCL") - : "----", - hhmmss, - grp->group_source_list - ? listcount( - grp->group_source_list) - : 0, - grp->igmp_version, uptime); + vty_out(vty, "%-15s %-12d %d\n", rp_str, + ntohs(rpaddr->rp_holdtime), + rpaddr->rp_pri); } - } /* scan igmp groups */ - } /* scan igmp sockets */ - } /* scan interfaces */ + } + vty_out(vty, "\n"); + } + + fragment++; + } if (uj) { vty_out(vty, "%s\n", json_object_to_json_string_ext( @@ -3006,47 +3169,429 @@ static void igmp_show_groups(struct pim_instance *pim, struct vty *vty, bool uj) } } -static void igmp_show_group_retransmission(struct pim_instance *pim, - struct vty *vty) +/*Display the group-rp mappings */ +static void pim_show_group_rp_mappings_info(struct pim_instance *pim, + struct vty *vty, bool uj) { - struct interface *ifp; + struct bsgrp_node *bsgrp; + struct listnode *rpnode; + struct bsm_rpinfo *bsm_rp; + struct route_node *rn; + char bsr_str[INET_ADDRSTRLEN]; + json_object *json = NULL; + json_object *json_group = NULL; + json_object *json_row = NULL; - vty_out(vty, - "Interface Address Group RetTimer Counter RetSrcs\n"); + if (pim->global_scope.current_bsr.s_addr == INADDR_ANY) + strlcpy(bsr_str, "0.0.0.0", sizeof(bsr_str)); - /* scan interfaces */ - FOR_ALL_INTERFACES (pim->vrf, ifp) { - struct pim_interface *pim_ifp = ifp->info; - struct listnode *sock_node; - struct igmp_sock *igmp; + else + pim_inet4_dump("", pim->global_scope.current_bsr, bsr_str, + sizeof(bsr_str)); - if (!pim_ifp) - continue; + if (uj) { + json = json_object_new_object(); + json_object_string_add(json, "BSR Address", bsr_str); + } else { + vty_out(vty, "BSR Address %s\n", bsr_str); + } - /* scan igmp sockets */ - for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, - igmp)) { - char ifaddr_str[INET_ADDRSTRLEN]; - struct listnode *grpnode; - struct igmp_group *grp; + for (rn = route_top(pim->global_scope.bsrp_table); rn; + rn = route_next(rn)) { + bsgrp = (struct bsgrp_node *)rn->info; - pim_inet4_dump("", igmp->ifaddr, ifaddr_str, - sizeof(ifaddr_str)); + if (!bsgrp) + continue; - /* scan igmp groups */ - for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, - grpnode, grp)) { - char group_str[INET_ADDRSTRLEN]; - char grp_retr_mmss[10]; - struct listnode *src_node; - struct igmp_source *src; - int grp_retr_sources = 0; + char grp_str[PREFIX_STRLEN]; - pim_inet4_dump("", grp->group_addr, - group_str, sizeof(group_str)); - pim_time_timer_to_mmss( - grp_retr_mmss, sizeof(grp_retr_mmss), - grp->t_group_query_retransmit_timer); + prefix2str(&bsgrp->group, grp_str, sizeof(grp_str)); + + if (uj) { + json_object_object_get_ex(json, grp_str, &json_group); + if (!json_group) { + json_group = json_object_new_object(); + json_object_object_add(json, grp_str, + json_group); + } + } else { + vty_out(vty, "Group Address %s\n", grp_str); + vty_out(vty, "--------------------------\n"); + vty_out(vty, "%-15s %-15s %-15s %-15s\n", "Rp Address", + "priority", "Holdtime", "Hash"); + + vty_out(vty, "(ACTIVE)\n"); + } + + if (bsgrp->bsrp_list) { + for (ALL_LIST_ELEMENTS_RO(bsgrp->bsrp_list, rpnode, + bsm_rp)) { + char rp_str[INET_ADDRSTRLEN]; + + pim_inet4_dump("", + bsm_rp->rp_address, rp_str, + sizeof(rp_str)); + + if (uj) { + json_row = json_object_new_object(); + json_object_string_add( + json_row, "Rp Address", rp_str); + json_object_int_add( + json_row, "Rp HoldTime", + bsm_rp->rp_holdtime); + json_object_int_add(json_row, + "Rp Priority", + bsm_rp->rp_prio); + json_object_int_add(json_row, + "Hash Val", + bsm_rp->hash); + json_object_object_add( + json_group, rp_str, json_row); + + } else { + vty_out(vty, + "%-15s %-15u %-15u %-15u\n", + rp_str, bsm_rp->rp_prio, + bsm_rp->rp_holdtime, + bsm_rp->hash); + } + } + if (!bsgrp->bsrp_list->count && !uj) + vty_out(vty, "Active List is empty.\n"); + } + + if (uj) { + json_object_int_add(json_group, "Pending RP count", + bsgrp->pend_rp_cnt); + } else { + vty_out(vty, "(PENDING)\n"); + vty_out(vty, "Pending RP count :%d\n", + bsgrp->pend_rp_cnt); + if (bsgrp->pend_rp_cnt) + vty_out(vty, "%-15s %-15s %-15s %-15s\n", + "Rp Address", "priority", "Holdtime", + "Hash"); + } + + if (bsgrp->partial_bsrp_list) { + for (ALL_LIST_ELEMENTS_RO(bsgrp->partial_bsrp_list, + rpnode, bsm_rp)) { + char rp_str[INET_ADDRSTRLEN]; + + pim_inet4_dump("", bsm_rp->rp_address, + rp_str, sizeof(rp_str)); + + if (uj) { + json_row = json_object_new_object(); + json_object_string_add( + json_row, "Rp Address", rp_str); + json_object_int_add( + json_row, "Rp HoldTime", + bsm_rp->rp_holdtime); + json_object_int_add(json_row, + "Rp Priority", + bsm_rp->rp_prio); + json_object_int_add(json_row, + "Hash Val", + bsm_rp->hash); + json_object_object_add( + json_group, rp_str, json_row); + } else { + vty_out(vty, + "%-15s %-15u %-15u %-15u\n", + rp_str, bsm_rp->rp_prio, + bsm_rp->rp_holdtime, + bsm_rp->hash); + } + } + if (!bsgrp->partial_bsrp_list->count && !uj) + vty_out(vty, "Partial List is empty\n"); + } + + if (!uj) + vty_out(vty, "\n"); + } + + if (uj) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } +} + +/* pim statistics - just adding only bsm related now. + * We can continue to add all pim related stats here. + */ +static void pim_show_statistics(struct pim_instance *pim, struct vty *vty, + const char *ifname, bool uj) +{ + json_object *json = NULL; + struct interface *ifp; + + if (uj) { + json = json_object_new_object(); + json_object_int_add(json, "bsmRx", pim->bsm_rcvd); + json_object_int_add(json, "bsmTx", pim->bsm_sent); + json_object_int_add(json, "bsmDropped", pim->bsm_dropped); + } else { + vty_out(vty, "BSM Statistics :\n"); + vty_out(vty, "----------------\n"); + vty_out(vty, "Number of Received BSMs : %" PRIu64 "\n", + pim->bsm_rcvd); + vty_out(vty, "Number of Forwared BSMs : %" PRIu64 "\n", + pim->bsm_sent); + vty_out(vty, "Number of Dropped BSMs : %" PRIu64 "\n", + pim->bsm_dropped); + } + + vty_out(vty, "\n"); + + /* scan interfaces */ + FOR_ALL_INTERFACES (pim->vrf, ifp) { + struct pim_interface *pim_ifp = ifp->info; + + if (ifname && strcmp(ifname, ifp->name)) + continue; + + if (!pim_ifp) + continue; + + if (!uj) { + vty_out(vty, "Interface : %s\n", ifp->name); + vty_out(vty, "-------------------\n"); + vty_out(vty, + "Number of BSMs dropped due to config miss : %u\n", + pim_ifp->pim_ifstat_bsm_cfg_miss); + vty_out(vty, "Number of unicast BSMs dropped : %u\n", + pim_ifp->pim_ifstat_ucast_bsm_cfg_miss); + vty_out(vty, + "Number of BSMs dropped due to invalid scope zone : %u\n", + pim_ifp->pim_ifstat_bsm_invalid_sz); + } else { + + json_object *json_row = NULL; + + json_row = json_object_new_object(); + + json_object_string_add(json_row, "If Name", ifp->name); + json_object_int_add(json_row, "bsmDroppedConfig", + pim_ifp->pim_ifstat_bsm_cfg_miss); + json_object_int_add( + json_row, "bsmDroppedUnicast", + pim_ifp->pim_ifstat_ucast_bsm_cfg_miss); + json_object_int_add(json_row, + "bsmDroppedInvalidScopeZone", + pim_ifp->pim_ifstat_bsm_invalid_sz); + json_object_object_add(json, ifp->name, json_row); + } + vty_out(vty, "\n"); + } + + if (uj) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } +} + +static void clear_pim_statistics(struct pim_instance *pim) +{ + struct interface *ifp; + + pim->bsm_rcvd = 0; + pim->bsm_sent = 0; + pim->bsm_dropped = 0; + + /* scan interfaces */ + FOR_ALL_INTERFACES (pim->vrf, ifp) { + struct pim_interface *pim_ifp = ifp->info; + + if (!pim_ifp) + continue; + + pim_ifp->pim_ifstat_bsm_cfg_miss = 0; + pim_ifp->pim_ifstat_ucast_bsm_cfg_miss = 0; + pim_ifp->pim_ifstat_bsm_invalid_sz = 0; + } +} + +static void igmp_show_groups(struct pim_instance *pim, struct vty *vty, bool uj) +{ + struct interface *ifp; + time_t now; + json_object *json = NULL; + json_object *json_iface = NULL; + json_object *json_group = NULL; + json_object *json_groups = NULL; + + now = pim_time_monotonic_sec(); + + if (uj) { + json = json_object_new_object(); + json_object_int_add(json, "totalGroups", pim->igmp_group_count); + json_object_int_add(json, "watermarkLimit", + pim->igmp_watermark_limit); + } else { + vty_out(vty, "Total IGMP groups: %u\n", pim->igmp_group_count); + vty_out(vty, "Watermark warn limit(%s): %u\n", + pim->igmp_watermark_limit ? "Set" : "Not Set", + pim->igmp_watermark_limit); + vty_out(vty, + "Interface Address Group Mode Timer Srcs V Uptime \n"); + } + + /* scan interfaces */ + FOR_ALL_INTERFACES (pim->vrf, ifp) { + struct pim_interface *pim_ifp = ifp->info; + struct listnode *sock_node; + struct igmp_sock *igmp; + + if (!pim_ifp) + continue; + + /* scan igmp sockets */ + for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, + igmp)) { + char ifaddr_str[INET_ADDRSTRLEN]; + struct listnode *grpnode; + struct igmp_group *grp; + + pim_inet4_dump("", igmp->ifaddr, ifaddr_str, + sizeof(ifaddr_str)); + + /* scan igmp groups */ + for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, + grpnode, grp)) { + char group_str[INET_ADDRSTRLEN]; + char hhmmss[10]; + char uptime[10]; + + pim_inet4_dump("", grp->group_addr, + group_str, sizeof(group_str)); + pim_time_timer_to_hhmmss(hhmmss, sizeof(hhmmss), + grp->t_group_timer); + pim_time_uptime(uptime, sizeof(uptime), + now - grp->group_creation); + + if (uj) { + json_object_object_get_ex( + json, ifp->name, &json_iface); + + if (!json_iface) { + json_iface = + json_object_new_object(); + json_object_pim_ifp_add( + json_iface, ifp); + json_object_object_add( + json, ifp->name, + json_iface); + json_groups = + json_object_new_array(); + json_object_object_add( + json_iface, + "groups", + json_groups); + } + + json_group = json_object_new_object(); + json_object_string_add(json_group, + "source", + ifaddr_str); + json_object_string_add(json_group, + "group", + group_str); + + if (grp->igmp_version == 3) + json_object_string_add( + json_group, "mode", + grp->group_filtermode_isexcl + ? "EXCLUDE" + : "INCLUDE"); + + json_object_string_add(json_group, + "timer", hhmmss); + json_object_int_add( + json_group, "sourcesCount", + grp->group_source_list + ? listcount( + grp->group_source_list) + : 0); + json_object_int_add( + json_group, "version", + grp->igmp_version); + json_object_string_add( + json_group, "uptime", uptime); + json_object_array_add(json_groups, + json_group); + } else { + vty_out(vty, + "%-16s %-15s %-15s %4s %8s %4d %d %8s\n", + ifp->name, ifaddr_str, + group_str, + grp->igmp_version == 3 + ? (grp->group_filtermode_isexcl + ? "EXCL" + : "INCL") + : "----", + hhmmss, + grp->group_source_list + ? listcount( + grp->group_source_list) + : 0, + grp->igmp_version, uptime); + } + } /* scan igmp groups */ + } /* scan igmp sockets */ + } /* scan interfaces */ + + if (uj) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } +} + +static void igmp_show_group_retransmission(struct pim_instance *pim, + struct vty *vty) +{ + struct interface *ifp; + + vty_out(vty, + "Interface Address Group RetTimer Counter RetSrcs\n"); + + /* scan interfaces */ + FOR_ALL_INTERFACES (pim->vrf, ifp) { + struct pim_interface *pim_ifp = ifp->info; + struct listnode *sock_node; + struct igmp_sock *igmp; + + if (!pim_ifp) + continue; + + /* scan igmp sockets */ + for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, + igmp)) { + char ifaddr_str[INET_ADDRSTRLEN]; + struct listnode *grpnode; + struct igmp_group *grp; + + pim_inet4_dump("", igmp->ifaddr, ifaddr_str, + sizeof(ifaddr_str)); + + /* scan igmp groups */ + for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, + grpnode, grp)) { + char group_str[INET_ADDRSTRLEN]; + char grp_retr_mmss[10]; + struct listnode *src_node; + struct igmp_source *src; + int grp_retr_sources = 0; + + pim_inet4_dump("", grp->group_addr, + group_str, sizeof(group_str)); + pim_time_timer_to_mmss( + grp_retr_mmss, sizeof(grp_retr_mmss), + grp->t_group_query_retransmit_timer); /* count group sources with retransmission state @@ -3205,6 +3750,81 @@ static void igmp_show_source_retransmission(struct pim_instance *pim, } /* scan interfaces */ } +static void pim_show_bsr(struct pim_instance *pim, + struct vty *vty, + bool uj) +{ + char uptime[10]; + char last_bsm_seen[10]; + time_t now; + char bsr_state[20]; + char bsr_str[PREFIX_STRLEN]; + json_object *json = NULL; + + if (pim->global_scope.current_bsr.s_addr == INADDR_ANY) { + strlcpy(bsr_str, "0.0.0.0", sizeof(bsr_str)); + pim_time_uptime(uptime, sizeof(uptime), + pim->global_scope.current_bsr_first_ts); + pim_time_uptime(last_bsm_seen, sizeof(last_bsm_seen), + pim->global_scope.current_bsr_last_ts); + } + + else { + pim_inet4_dump("", pim->global_scope.current_bsr, + bsr_str, sizeof(bsr_str)); + now = pim_time_monotonic_sec(); + pim_time_uptime(uptime, sizeof(uptime), + (now - pim->global_scope.current_bsr_first_ts)); + pim_time_uptime(last_bsm_seen, sizeof(last_bsm_seen), + now - pim->global_scope.current_bsr_last_ts); + } + + switch (pim->global_scope.state) { + case NO_INFO: + strlcpy(bsr_state, "NO_INFO", sizeof(bsr_state)); + break; + case ACCEPT_ANY: + strlcpy(bsr_state, "ACCEPT_ANY", sizeof(bsr_state)); + break; + case ACCEPT_PREFERRED: + strlcpy(bsr_state, "ACCEPT_PREFERRED", sizeof(bsr_state)); + break; + default: + strlcpy(bsr_state, "", sizeof(bsr_state)); + } + + if (uj) { + json = json_object_new_object(); + json_object_string_add(json, "bsr", bsr_str); + json_object_int_add(json, "priority", + pim->global_scope.current_bsr_prio); + json_object_int_add(json, "fragmentTag", + pim->global_scope.bsm_frag_tag); + json_object_string_add(json, "state", bsr_state); + json_object_string_add(json, "upTime", uptime); + json_object_string_add(json, "lastBsmSeen", last_bsm_seen); + } + + else { + vty_out(vty, "PIMv2 Bootstrap information\n"); + vty_out(vty, "Current preferred BSR address: %s\n", bsr_str); + vty_out(vty, + "Priority Fragment-Tag State UpTime\n"); + vty_out(vty, " %-12d %-12d %-13s %7s\n", + pim->global_scope.current_bsr_prio, + pim->global_scope.bsm_frag_tag, + bsr_state, + uptime); + vty_out(vty, "Last BSM seen: %s\n", last_bsm_seen); + } + + if (uj) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } +} + static void clear_igmp_interfaces(struct pim_instance *pim) { struct interface *ifp; @@ -3280,46 +3900,69 @@ DEFUN (clear_ip_igmp_interfaces, return CMD_SUCCESS; } -static void mroute_add_all(struct pim_instance *pim) +DEFUN (clear_ip_pim_statistics, + clear_ip_pim_statistics_cmd, + "clear ip pim statistics [vrf NAME]", + CLEAR_STR + IP_STR + CLEAR_IP_PIM_STR + VRF_CMD_HELP_STR + "Reset PIM statistics\n") { - struct listnode *node; - struct channel_oil *c_oil; + int idx = 2; + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); - for (ALL_LIST_ELEMENTS_RO(pim->channel_oil_list, node, c_oil)) { - if (pim_mroute_add(c_oil, __PRETTY_FUNCTION__)) { - /* just log warning */ - char source_str[INET_ADDRSTRLEN]; - char group_str[INET_ADDRSTRLEN]; - pim_inet4_dump("", c_oil->oil.mfcc_origin, - source_str, sizeof(source_str)); - pim_inet4_dump("", c_oil->oil.mfcc_mcastgrp, - group_str, sizeof(group_str)); - zlog_warn("%s %s: (S,G)=(%s,%s) failure writing MFC", - __FILE__, __PRETTY_FUNCTION__, source_str, - group_str); - } - } + if (!vrf) + return CMD_WARNING; + + clear_pim_statistics(vrf->info); + return CMD_SUCCESS; } -static void mroute_del_all(struct pim_instance *pim) +static void clear_mroute(struct pim_instance *pim) { - struct listnode *node; - struct channel_oil *c_oil; + struct pim_upstream *up; + struct interface *ifp; + + /* scan interfaces */ + FOR_ALL_INTERFACES (pim->vrf, ifp) { + struct pim_interface *pim_ifp = ifp->info; + struct listnode *sock_node; + struct igmp_sock *igmp; + struct pim_ifchannel *ch; + + if (!pim_ifp) + continue; + + /* deleting all ifchannels */ + while (!RB_EMPTY(pim_ifchannel_rb, &pim_ifp->ifchannel_rb)) { + ch = RB_ROOT(pim_ifchannel_rb, &pim_ifp->ifchannel_rb); + + pim_ifchannel_delete(ch); + } + + /* clean up all igmp groups */ + /* scan igmp sockets */ + for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, + igmp)) { + + struct igmp_group *grp; + + if (igmp->igmp_group_list) { + while (igmp->igmp_group_list->count) { + grp = listnode_head( + igmp->igmp_group_list); + igmp_group_delete(grp); + } + } - for (ALL_LIST_ELEMENTS_RO(pim->channel_oil_list, node, c_oil)) { - if (pim_mroute_del(c_oil, __PRETTY_FUNCTION__)) { - /* just log warning */ - char source_str[INET_ADDRSTRLEN]; - char group_str[INET_ADDRSTRLEN]; - pim_inet4_dump("", c_oil->oil.mfcc_origin, - source_str, sizeof(source_str)); - pim_inet4_dump("", c_oil->oil.mfcc_mcastgrp, - group_str, sizeof(group_str)); - zlog_warn("%s %s: (S,G)=(%s,%s) failure clearing MFC", - __FILE__, __PRETTY_FUNCTION__, source_str, - group_str); } } + + /* clean up all upstreams*/ + while ((up = rb_pim_upstream_first(&pim->upstream_head))) { + pim_upstream_del(pim, up, __func__); + } } DEFUN (clear_ip_mroute, @@ -3336,8 +3979,7 @@ DEFUN (clear_ip_mroute, if (!vrf) return CMD_WARNING; - mroute_del_all(vrf->info); - mroute_add_all(vrf->info); + clear_mroute(vrf->info); return CMD_SUCCESS; } @@ -3398,6 +4040,8 @@ DEFUN (clear_ip_pim_interface_traffic, pim_ifp->pim_ifstat_reg_stop_send = 0; pim_ifp->pim_ifstat_assert_recv = 0; pim_ifp->pim_ifstat_assert_send = 0; + pim_ifp->pim_ifstat_bsm_rx = 0; + pim_ifp->pim_ifstat_bsm_tx = 0; } return CMD_SUCCESS; @@ -3685,6 +4329,113 @@ DEFUN (show_ip_igmp_statistics, return CMD_SUCCESS; } +DEFUN (show_ip_pim_mlag_summary, + show_ip_pim_mlag_summary_cmd, + "show ip pim mlag summary [json]", + SHOW_STR + IP_STR + PIM_STR + "MLAG\n" + "status and stats\n" + JSON_STR) +{ + bool uj = use_json(argc, argv); + char role_buf[MLAG_ROLE_STRSIZE]; + char addr_buf[INET_ADDRSTRLEN]; + + if (uj) { + json_object *json = NULL; + json_object *json_stat = NULL; + + json = json_object_new_object(); + if (router->mlag_flags & PIM_MLAGF_LOCAL_CONN_UP) + json_object_boolean_true_add(json, "mlagConnUp"); + if (router->mlag_flags & PIM_MLAGF_PEER_CONN_UP) + json_object_boolean_true_add(json, "mlagPeerConnUp"); + if (router->mlag_flags & PIM_MLAGF_PEER_ZEBRA_UP) + json_object_boolean_true_add(json, "mlagPeerZebraUp"); + json_object_string_add(json, "mlagRole", + mlag_role2str(router->mlag_role, + role_buf, sizeof(role_buf))); + inet_ntop(AF_INET, &router->local_vtep_ip, + addr_buf, INET_ADDRSTRLEN); + json_object_string_add(json, "localVtepIp", addr_buf); + inet_ntop(AF_INET, &router->anycast_vtep_ip, + addr_buf, INET_ADDRSTRLEN); + json_object_string_add(json, "anycastVtepIp", addr_buf); + json_object_string_add(json, "peerlinkRif", + router->peerlink_rif); + + json_stat = json_object_new_object(); + json_object_int_add(json_stat, "mlagConnFlaps", + router->mlag_stats.mlagd_session_downs); + json_object_int_add(json_stat, "mlagPeerConnFlaps", + router->mlag_stats.peer_session_downs); + json_object_int_add(json_stat, "mlagPeerZebraFlaps", + router->mlag_stats.peer_zebra_downs); + json_object_int_add(json_stat, "mrouteAddRx", + router->mlag_stats.msg.mroute_add_rx); + json_object_int_add(json_stat, "mrouteAddTx", + router->mlag_stats.msg.mroute_add_tx); + json_object_int_add(json_stat, "mrouteDelRx", + router->mlag_stats.msg.mroute_del_rx); + json_object_int_add(json_stat, "mrouteDelTx", + router->mlag_stats.msg.mroute_del_tx); + json_object_int_add(json_stat, "mlagStatusUpdates", + router->mlag_stats.msg.mlag_status_updates); + json_object_int_add(json_stat, "peerZebraStatusUpdates", + router->mlag_stats.msg.peer_zebra_status_updates); + json_object_int_add(json_stat, "pimStatusUpdates", + router->mlag_stats.msg.pim_status_updates); + json_object_int_add(json_stat, "vxlanUpdates", + router->mlag_stats.msg.vxlan_updates); + json_object_object_add(json, "connStats", json_stat); + + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + return CMD_SUCCESS; + } + + vty_out(vty, "MLAG daemon connection: %s\n", + (router->mlag_flags & PIM_MLAGF_LOCAL_CONN_UP) + ? "up" : "down"); + vty_out(vty, "MLAG peer state: %s\n", + (router->mlag_flags & PIM_MLAGF_PEER_CONN_UP) + ? "up" : "down"); + vty_out(vty, "Zebra peer state: %s\n", + (router->mlag_flags & PIM_MLAGF_PEER_ZEBRA_UP) + ? "up" : "down"); + vty_out(vty, "MLAG role: %s\n", + mlag_role2str(router->mlag_role, role_buf, sizeof(role_buf))); + inet_ntop(AF_INET, &router->local_vtep_ip, + addr_buf, INET_ADDRSTRLEN); + vty_out(vty, "Local VTEP IP: %s\n", addr_buf); + inet_ntop(AF_INET, &router->anycast_vtep_ip, + addr_buf, INET_ADDRSTRLEN); + vty_out(vty, "Anycast VTEP IP: %s\n", addr_buf); + vty_out(vty, "Peerlink: %s\n", router->peerlink_rif); + vty_out(vty, "Session flaps: mlagd: %d mlag-peer: %d zebra-peer: %d\n", + router->mlag_stats.mlagd_session_downs, + router->mlag_stats.peer_session_downs, + router->mlag_stats.peer_zebra_downs); + vty_out(vty, "Message Statistics:\n"); + vty_out(vty, " mroute adds: rx: %d, tx: %d\n", + router->mlag_stats.msg.mroute_add_rx, + router->mlag_stats.msg.mroute_add_tx); + vty_out(vty, " mroute dels: rx: %d, tx: %d\n", + router->mlag_stats.msg.mroute_del_rx, + router->mlag_stats.msg.mroute_del_tx); + vty_out(vty, " peer zebra status updates: %d\n", + router->mlag_stats.msg.peer_zebra_status_updates); + vty_out(vty, " PIM status updates: %d\n", + router->mlag_stats.msg.pim_status_updates); + vty_out(vty, " VxLAN updates: %d\n", + router->mlag_stats.msg.vxlan_updates); + + return CMD_SUCCESS; +} + DEFUN (show_ip_pim_assert, show_ip_pim_assert_cmd, "show ip pim [vrf NAME] assert", @@ -3767,10 +4518,11 @@ DEFUN (show_ip_pim_assert_winner_metric, DEFUN (show_ip_pim_interface, show_ip_pim_interface_cmd, - "show ip pim [vrf NAME] interface [detail|WORD] [json]", + "show ip pim [mlag] [vrf NAME] interface [detail|WORD] [json]", SHOW_STR IP_STR PIM_STR + "MLAG\n" VRF_CMD_HELP_STR "PIM interface information\n" "Detailed output\n" @@ -3780,36 +4532,47 @@ DEFUN (show_ip_pim_interface, int idx = 2; struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); bool uj = use_json(argc, argv); + bool mlag = false; if (!vrf) return CMD_WARNING; + if (argv_find(argv, argc, "mlag", &idx)) + mlag = true; + if (argv_find(argv, argc, "WORD", &idx) || argv_find(argv, argc, "detail", &idx)) - pim_show_interfaces_single(vrf->info, vty, argv[idx]->arg, uj); + pim_show_interfaces_single(vrf->info, vty, argv[idx]->arg, mlag, + uj); else - pim_show_interfaces(vrf->info, vty, uj); + pim_show_interfaces(vrf->info, vty, mlag, uj); return CMD_SUCCESS; } DEFUN (show_ip_pim_interface_vrf_all, show_ip_pim_interface_vrf_all_cmd, - "show ip pim vrf all interface [detail|WORD] [json]", + "show ip pim [mlag] vrf all interface [detail|WORD] [json]", SHOW_STR IP_STR PIM_STR + "MLAG\n" VRF_CMD_HELP_STR "PIM interface information\n" "Detailed output\n" "interface name\n" JSON_STR) { - int idx = 6; + int idx = 2; bool uj = use_json(argc, argv); struct vrf *vrf; bool first = true; + bool mlag = false; + + if (argv_find(argv, argc, "mlag", &idx)) + mlag = true; + idx = 6; if (uj) vty_out(vty, "{ "); RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { @@ -3823,9 +4586,9 @@ DEFUN (show_ip_pim_interface_vrf_all, if (argv_find(argv, argc, "WORD", &idx) || argv_find(argv, argc, "detail", &idx)) pim_show_interfaces_single(vrf->info, vty, - argv[idx]->arg, uj); + argv[idx]->arg, mlag, uj); else - pim_show_interfaces(vrf->info, vty, uj); + pim_show_interfaces(vrf->info, vty, mlag, uj); } if (uj) vty_out(vty, "}\n"); @@ -3840,14 +4603,139 @@ DEFPY (show_ip_pim_join, IP_STR PIM_STR VRF_CMD_HELP_STR - "PIM interface join information\n" - "The Source or Group\n" - "The Group\n" - JSON_STR) + "PIM interface join information\n" + "The Source or Group\n" + "The Group\n" + JSON_STR) +{ + struct prefix_sg sg = {0}; + struct vrf *v; + bool uj = !!json; + struct pim_instance *pim; + + v = vrf_lookup_by_name(vrf ? vrf : VRF_DEFAULT_NAME); + + if (!v) { + vty_out(vty, "%% Vrf specified: %s does not exist\n", vrf); + return CMD_WARNING; + } + pim = pim_get_pim_instance(v->vrf_id); + + if (!pim) { + vty_out(vty, "%% Unable to find pim instance\n"); + return CMD_WARNING; + } + + if (s_or_g.s_addr != 0) { + if (g.s_addr != 0) { + sg.src = s_or_g; + sg.grp = g; + } else + sg.grp = s_or_g; + } + + pim_show_join(pim, vty, &sg, uj); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_pim_join_vrf_all, + show_ip_pim_join_vrf_all_cmd, + "show ip pim vrf all join [json]", + SHOW_STR + IP_STR + PIM_STR + VRF_CMD_HELP_STR + "PIM interface join information\n" + JSON_STR) +{ + struct prefix_sg sg = {0}; + bool uj = use_json(argc, argv); + struct vrf *vrf; + bool first = true; + + if (uj) + vty_out(vty, "{ "); + RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { + if (uj) { + if (!first) + vty_out(vty, ", "); + vty_out(vty, " \"%s\": ", vrf->name); + first = false; + } else + vty_out(vty, "VRF: %s\n", vrf->name); + pim_show_join(vrf->info, vty, &sg, uj); + } + if (uj) + vty_out(vty, "}\n"); + + return CMD_WARNING; +} + +static void pim_show_jp_agg_helper(struct vty *vty, + struct interface *ifp, + struct pim_neighbor *neigh, + struct pim_upstream *up, + int is_join) +{ + char src_str[INET_ADDRSTRLEN]; + char grp_str[INET_ADDRSTRLEN]; + char rpf_str[INET_ADDRSTRLEN]; + + pim_inet4_dump("", up->sg.src, src_str, sizeof(src_str)); + pim_inet4_dump("", up->sg.grp, grp_str, sizeof(grp_str)); + /* pius->address.s_addr */ + pim_inet4_dump("", neigh->source_addr, rpf_str, sizeof(rpf_str)); + + vty_out(vty, "%-16s %-15s %-15s %-15s %5s\n", + ifp->name, rpf_str, src_str, + grp_str, is_join?"J":"P"); +} + +static void pim_show_jp_agg_list(struct pim_instance *pim, struct vty *vty) +{ + struct interface *ifp; + struct pim_interface *pim_ifp; + struct listnode *n_node; + struct pim_neighbor *neigh; + struct listnode *jag_node; + struct pim_jp_agg_group *jag; + struct listnode *js_node; + struct pim_jp_sources *js; + + vty_out(vty, + "Interface RPF Nbr Source Group State\n"); + + FOR_ALL_INTERFACES (pim->vrf, ifp) { + pim_ifp = ifp->info; + if (!pim_ifp) + continue; + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, + n_node, neigh)) { + for (ALL_LIST_ELEMENTS_RO(neigh->upstream_jp_agg, + jag_node, jag)) { + for (ALL_LIST_ELEMENTS_RO(jag->sources, + js_node, js)) { + pim_show_jp_agg_helper(vty, + ifp, neigh, js->up, + js->is_join); + } + } + } + } +} + +DEFPY (show_ip_pim_jp_agg, + show_ip_pim_jp_agg_cmd, + "show ip pim [vrf NAME] jp-agg", + SHOW_STR + IP_STR + PIM_STR + VRF_CMD_HELP_STR + "join prune aggregation list\n") { - struct prefix_sg sg = {0}; struct vrf *v; - bool uj = !!json; struct pim_instance *pim; v = vrf_lookup_by_name(vrf ? vrf : VRF_DEFAULT_NAME); @@ -3863,70 +4751,294 @@ DEFPY (show_ip_pim_join, return CMD_WARNING; } - if (s_or_g.s_addr != 0) { - if (g.s_addr != 0) { - sg.src = s_or_g; - sg.grp = g; - } else - sg.grp = s_or_g; - } - - pim_show_join(pim, vty, &sg, uj); + pim_show_jp_agg_list(pim, vty); return CMD_SUCCESS; } -DEFUN (show_ip_pim_join_vrf_all, - show_ip_pim_join_vrf_all_cmd, - "show ip pim vrf all join [json]", +DEFUN (show_ip_pim_local_membership, + show_ip_pim_local_membership_cmd, + "show ip pim [vrf NAME] local-membership [json]", SHOW_STR IP_STR PIM_STR VRF_CMD_HELP_STR - "PIM interface join information\n" + "PIM interface local-membership\n" JSON_STR) { - struct prefix_sg sg = {0}; + int idx = 2; + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); bool uj = use_json(argc, argv); - struct vrf *vrf; - bool first = true; + + if (!vrf) + return CMD_WARNING; + + pim_show_membership(vrf->info, vty, uj); + + return CMD_SUCCESS; +} + +static void pim_show_mlag_up_entry_detail(struct vrf *vrf, + struct vty *vty, struct pim_upstream *up, + char *src_str, char *grp_str, json_object *json) +{ + if (json) { + json_object *json_row = NULL; + json_object *own_list = NULL; + json_object *json_group = NULL; + + + json_object_object_get_ex(json, grp_str, &json_group); + if (!json_group) { + json_group = json_object_new_object(); + json_object_object_add(json, grp_str, + json_group); + } + + json_row = json_object_new_object(); + json_object_string_add(json_row, "source", src_str); + json_object_string_add(json_row, "group", grp_str); + + own_list = json_object_new_array(); + if (pim_up_mlag_is_local(up)) + json_object_array_add(own_list, + json_object_new_string("local")); + if (up->flags & (PIM_UPSTREAM_FLAG_MASK_MLAG_PEER)) + json_object_array_add(own_list, + json_object_new_string("peer")); + if (up->flags & (PIM_UPSTREAM_FLAG_MASK_MLAG_INTERFACE)) + json_object_array_add( + own_list, json_object_new_string("Interface")); + json_object_object_add(json_row, "owners", own_list); + + json_object_int_add(json_row, "localCost", + pim_up_mlag_local_cost(up)); + json_object_int_add(json_row, "peerCost", + pim_up_mlag_peer_cost(up)); + if (PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(up->flags)) + json_object_boolean_false_add(json_row, "df"); + else + json_object_boolean_true_add(json_row, "df"); + json_object_object_add(json_group, src_str, json_row); + } else { + char own_str[6]; + + own_str[0] = '\0'; + if (pim_up_mlag_is_local(up)) + strlcat(own_str, "L", sizeof(own_str)); + if (up->flags & (PIM_UPSTREAM_FLAG_MASK_MLAG_PEER)) + strlcat(own_str, "P", sizeof(own_str)); + if (up->flags & (PIM_UPSTREAM_FLAG_MASK_MLAG_INTERFACE)) + strlcat(own_str, "I", sizeof(own_str)); + /* XXX - fixup, print paragraph output */ + vty_out(vty, + "%-15s %-15s %-6s %-11u %-10d %2s\n", + src_str, grp_str, own_str, + pim_up_mlag_local_cost(up), + pim_up_mlag_peer_cost(up), + PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(up->flags) + ? "n" : "y"); + } +} + +static void pim_show_mlag_up_detail(struct vrf *vrf, + struct vty *vty, const char *src_or_group, + const char *group, bool uj) +{ + char src_str[INET_ADDRSTRLEN]; + char grp_str[INET_ADDRSTRLEN]; + struct pim_upstream *up; + struct pim_instance *pim = vrf->info; + json_object *json = NULL; if (uj) - vty_out(vty, "{ "); - RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { + json = json_object_new_object(); + else + vty_out(vty, + "Source Group Owner Local-cost Peer-cost DF\n"); + + frr_each (rb_pim_upstream, &pim->upstream_head, up) { + if (!(up->flags & PIM_UPSTREAM_FLAG_MASK_MLAG_PEER) + && !(up->flags & PIM_UPSTREAM_FLAG_MASK_MLAG_INTERFACE) + && !pim_up_mlag_is_local(up)) + continue; + + pim_inet4_dump("", up->sg.src, src_str, sizeof(src_str)); + pim_inet4_dump("", up->sg.grp, grp_str, sizeof(grp_str)); + /* XXX: strcmps are clearly inefficient. we should do uint comps + * here instead. + */ + if (group) { + if (strcmp(src_str, src_or_group) || + strcmp(grp_str, group)) + continue; + } else { + if (strcmp(src_str, src_or_group) && + strcmp(grp_str, src_or_group)) + continue; + } + pim_show_mlag_up_entry_detail(vrf, vty, up, + src_str, grp_str, json); + } + + if (uj) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } +} + +static void pim_show_mlag_up_vrf(struct vrf *vrf, struct vty *vty, bool uj) +{ + json_object *json = NULL; + json_object *json_row; + struct pim_upstream *up; + char src_str[INET_ADDRSTRLEN]; + char grp_str[INET_ADDRSTRLEN]; + struct pim_instance *pim = vrf->info; + json_object *json_group = NULL; + + if (uj) { + json = json_object_new_object(); + } else { + vty_out(vty, + "Source Group Owner Local-cost Peer-cost DF\n"); + } + + frr_each (rb_pim_upstream, &pim->upstream_head, up) { + if (!(up->flags & PIM_UPSTREAM_FLAG_MASK_MLAG_PEER) + && !(up->flags & PIM_UPSTREAM_FLAG_MASK_MLAG_INTERFACE) + && !pim_up_mlag_is_local(up)) + continue; + pim_inet4_dump("", up->sg.src, src_str, sizeof(src_str)); + pim_inet4_dump("", up->sg.grp, grp_str, sizeof(grp_str)); if (uj) { - if (!first) - vty_out(vty, ", "); - vty_out(vty, " \"%s\": ", vrf->name); - first = false; - } else - vty_out(vty, "VRF: %s\n", vrf->name); - pim_show_join(vrf->info, vty, &sg, uj); + json_object *own_list = NULL; + + json_object_object_get_ex(json, grp_str, &json_group); + if (!json_group) { + json_group = json_object_new_object(); + json_object_object_add(json, grp_str, + json_group); + } + + json_row = json_object_new_object(); + json_object_string_add(json_row, "vrf", vrf->name); + json_object_string_add(json_row, "source", src_str); + json_object_string_add(json_row, "group", grp_str); + + own_list = json_object_new_array(); + if (pim_up_mlag_is_local(up)) { + + json_object_array_add(own_list, + json_object_new_string("local")); + } + if (up->flags & (PIM_UPSTREAM_FLAG_MASK_MLAG_PEER)) { + json_object_array_add(own_list, + json_object_new_string("peer")); + } + json_object_object_add(json_row, "owners", own_list); + + json_object_int_add(json_row, "localCost", + pim_up_mlag_local_cost(up)); + json_object_int_add(json_row, "peerCost", + pim_up_mlag_peer_cost(up)); + if (PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(up->flags)) + json_object_boolean_false_add(json_row, "df"); + else + json_object_boolean_true_add(json_row, "df"); + json_object_object_add(json_group, src_str, json_row); + } else { + char own_str[6]; + + own_str[0] = '\0'; + if (pim_up_mlag_is_local(up)) + strlcat(own_str, "L", sizeof(own_str)); + if (up->flags & (PIM_UPSTREAM_FLAG_MASK_MLAG_PEER)) + strlcat(own_str, "P", sizeof(own_str)); + if (up->flags & (PIM_UPSTREAM_FLAG_MASK_MLAG_INTERFACE)) + strlcat(own_str, "I", sizeof(own_str)); + vty_out(vty, + "%-15s %-15s %-6s %-11u %-10u %2s\n", + src_str, grp_str, own_str, + pim_up_mlag_local_cost(up), + pim_up_mlag_peer_cost(up), + PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(up->flags) + ? "n" : "y"); + } } - if (uj) - vty_out(vty, "}\n"); + if (uj) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } +} - return CMD_WARNING; +static void pim_show_mlag_help_string(struct vty *vty, bool uj) +{ + if (!uj) { + vty_out(vty, "Owner codes:\n"); + vty_out(vty, + "L: EVPN-MLAG Entry, I:PIM-MLAG Entry, P: Peer Entry\n"); + } } -DEFUN (show_ip_pim_local_membership, - show_ip_pim_local_membership_cmd, - "show ip pim [vrf NAME] local-membership [json]", - SHOW_STR - IP_STR - PIM_STR - VRF_CMD_HELP_STR - "PIM interface local-membership\n" - JSON_STR) + +DEFUN(show_ip_pim_mlag_up, show_ip_pim_mlag_up_cmd, + "show ip pim [vrf NAME] mlag upstream [A.B.C.D [A.B.C.D]] [json]", + SHOW_STR + IP_STR + PIM_STR + VRF_CMD_HELP_STR + "MLAG\n" + "upstream\n" + "Unicast or Multicast address\n" + "Multicast address\n" JSON_STR) { + const char *src_or_group = NULL; + const char *group = NULL; int idx = 2; struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); bool uj = use_json(argc, argv); - if (!vrf) + if (!vrf || !vrf->info) { + vty_out(vty, "%s: VRF or Info missing\n", __func__); return CMD_WARNING; + } - pim_show_membership(vrf->info, vty, uj); + if (uj) + argc--; + + if (argv_find(argv, argc, "A.B.C.D", &idx)) { + src_or_group = argv[idx]->arg; + if (idx + 1 < argc) + group = argv[idx + 1]->arg; + } + + pim_show_mlag_help_string(vty, uj); + + if (src_or_group) + pim_show_mlag_up_detail(vrf, vty, src_or_group, group, uj); + else + pim_show_mlag_up_vrf(vrf, vty, uj); + + return CMD_SUCCESS; +} + + +DEFUN(show_ip_pim_mlag_up_vrf_all, show_ip_pim_mlag_up_vrf_all_cmd, + "show ip pim vrf all mlag upstream [json]", + SHOW_STR IP_STR PIM_STR VRF_CMD_HELP_STR + "MLAG\n" + "upstream\n" JSON_STR) +{ + struct vrf *vrf; + bool uj = use_json(argc, argv); + + pim_show_mlag_help_string(vty, uj); + RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { + pim_show_mlag_up_vrf(vrf, vty, uj); + } return CMD_SUCCESS; } @@ -4173,6 +5285,28 @@ DEFUN (show_ip_pim_upstream_vrf_all, return CMD_SUCCESS; } +DEFUN (show_ip_pim_channel, + show_ip_pim_channel_cmd, + "show ip pim [vrf NAME] channel [json]", + SHOW_STR + IP_STR + PIM_STR + VRF_CMD_HELP_STR + "PIM downstream channel info\n" + JSON_STR) +{ + int idx = 2; + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + bool uj = use_json(argc, argv); + + if (!vrf) + return CMD_WARNING; + + pim_show_channel(vrf->info, vty, uj); + + return CMD_SUCCESS; +} + DEFUN (show_ip_pim_upstream_join_desired, show_ip_pim_upstream_join_desired_cmd, "show ip pim [vrf NAME] upstream-join-desired [json]", @@ -4412,33 +5546,104 @@ DEFUN (show_ip_pim_nexthop_lookup, grp.u.prefix4 = grp_addr; memset(&nexthop, 0, sizeof(nexthop)); - result = pim_ecmp_nexthop_lookup(vrf->info, &nexthop, &nht_p, &grp, 0); + result = pim_ecmp_nexthop_lookup(vrf->info, &nexthop, &nht_p, &grp, 0); + + if (!result) { + vty_out(vty, + "Nexthop Lookup failed, no usable routes returned.\n"); + return CMD_SUCCESS; + } + + pim_addr_dump("", &grp, grp_str, sizeof(grp_str)); + pim_addr_dump("", &nexthop.mrib_nexthop_addr, + nexthop_addr_str, sizeof(nexthop_addr_str)); + vty_out(vty, "Group %s --- Nexthop %s Interface %s \n", grp_str, + nexthop_addr_str, nexthop.interface->name); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_pim_interface_traffic, + show_ip_pim_interface_traffic_cmd, + "show ip pim [vrf NAME] interface traffic [WORD] [json]", + SHOW_STR + IP_STR + PIM_STR + VRF_CMD_HELP_STR + "PIM interface information\n" + "Protocol Packet counters\n" + "Interface name\n" + JSON_STR) +{ + int idx = 2; + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + bool uj = use_json(argc, argv); + + if (!vrf) + return CMD_WARNING; + + if (argv_find(argv, argc, "WORD", &idx)) + pim_show_interface_traffic_single(vrf->info, vty, + argv[idx]->arg, uj); + else + pim_show_interface_traffic(vrf->info, vty, uj); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_pim_bsm_db, + show_ip_pim_bsm_db_cmd, + "show ip pim bsm-database [vrf NAME] [json]", + SHOW_STR + IP_STR + PIM_STR + "PIM cached bsm packets information\n" + VRF_CMD_HELP_STR + JSON_STR) +{ + int idx = 2; + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + bool uj = use_json(argc, argv); + + if (!vrf) + return CMD_WARNING; + + pim_show_bsm_db(vrf->info, vty, uj); + return CMD_SUCCESS; +} - if (!result) { - vty_out(vty, - "Nexthop Lookup failed, no usable routes returned.\n"); - return CMD_SUCCESS; - } +DEFUN (show_ip_pim_bsrp, + show_ip_pim_bsrp_cmd, + "show ip pim bsrp-info [vrf NAME] [json]", + SHOW_STR + IP_STR + PIM_STR + "PIM cached group-rp mappings information\n" + VRF_CMD_HELP_STR + JSON_STR) +{ + int idx = 2; + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + bool uj = use_json(argc, argv); - pim_addr_dump("", &grp, grp_str, sizeof(grp_str)); - pim_addr_dump("", &nexthop.mrib_nexthop_addr, - nexthop_addr_str, sizeof(nexthop_addr_str)); - vty_out(vty, "Group %s --- Nexthop %s Interface %s \n", grp_str, - nexthop_addr_str, nexthop.interface->name); + if (!vrf) + return CMD_WARNING; + + pim_show_group_rp_mappings_info(vrf->info, vty, uj); return CMD_SUCCESS; } -DEFUN (show_ip_pim_interface_traffic, - show_ip_pim_interface_traffic_cmd, - "show ip pim [vrf NAME] interface traffic [WORD] [json]", +DEFUN (show_ip_pim_statistics, + show_ip_pim_statistics_cmd, + "show ip pim [vrf NAME] statistics [interface WORD] [json]", SHOW_STR IP_STR PIM_STR VRF_CMD_HELP_STR - "PIM interface information\n" - "Protocol Packet counters\n" - "Interface name\n" + "PIM statistics\n" + INTERFACE_STR + "PIM interface\n" JSON_STR) { int idx = 2; @@ -4449,22 +5654,27 @@ DEFUN (show_ip_pim_interface_traffic, return CMD_WARNING; if (argv_find(argv, argc, "WORD", &idx)) - pim_show_interface_traffic_single(vrf->info, vty, - argv[idx]->arg, uj); + pim_show_statistics(vrf->info, vty, argv[idx]->arg, uj); else - pim_show_interface_traffic(vrf->info, vty, uj); + pim_show_statistics(vrf->info, vty, NULL, uj); return CMD_SUCCESS; } -static void show_multicast_interfaces(struct pim_instance *pim, struct vty *vty) +static void show_multicast_interfaces(struct pim_instance *pim, struct vty *vty, + bool uj) { struct interface *ifp; + json_object *json = NULL; + json_object *json_row = NULL; vty_out(vty, "\n"); - vty_out(vty, - "Interface Address ifi Vif PktsIn PktsOut BytesIn BytesOut\n"); + if (uj) + json = json_object_new_object(); + else + vty_out(vty, + "Interface Address ifi Vif PktsIn PktsOut BytesIn BytesOut\n"); FOR_ALL_INTERFACES (pim->vrf, ifp) { struct pim_interface *pim_ifp; @@ -4488,12 +5698,43 @@ static void show_multicast_interfaces(struct pim_instance *pim, struct vty *vty) } ifaddr = pim_ifp->primary_address; + if (uj) { + json_row = json_object_new_object(); + json_object_string_add(json_row, "name", ifp->name); + json_object_string_add(json_row, "state", + if_is_up(ifp) ? "up" : "down"); + json_object_string_add( + json_row, "address", + inet_ntoa(pim_ifp->primary_address)); + json_object_int_add(json_row, "ifIndex", ifp->ifindex); + json_object_int_add(json_row, "vif", + pim_ifp->mroute_vif_index); + json_object_int_add(json_row, "pktsIn", + (unsigned long)vreq.icount); + json_object_int_add(json_row, "pktsOut", + (unsigned long)vreq.ocount); + json_object_int_add(json_row, "bytesIn", + (unsigned long)vreq.ibytes); + json_object_int_add(json_row, "bytesOut", + (unsigned long)vreq.obytes); + json_object_object_add(json, ifp->name, json_row); + } else { + vty_out(vty, + "%-16s %-15s %3d %3d %7lu %7lu %10lu %10lu\n", + ifp->name, inet_ntoa(ifaddr), ifp->ifindex, + pim_ifp->mroute_vif_index, + (unsigned long)vreq.icount, + (unsigned long)vreq.ocount, + (unsigned long)vreq.ibytes, + (unsigned long)vreq.obytes); + } + } - vty_out(vty, "%-16s %-15s %3d %3d %7lu %7lu %10lu %10lu\n", - ifp->name, inet_ntoa(ifaddr), ifp->ifindex, - pim_ifp->mroute_vif_index, (unsigned long)vreq.icount, - (unsigned long)vreq.ocount, (unsigned long)vreq.ibytes, - (unsigned long)vreq.obytes); + if (uj) { + vty_out(vty, "%s\n", + json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); } } @@ -4508,7 +5749,7 @@ static void pim_cmd_show_ip_multicast_helper(struct pim_instance *pim, pim = vrf->info; vty_out(vty, "Router MLAG Role: %s\n", - mlag_role2str(router->role, mlag_role, sizeof(mlag_role))); + mlag_role2str(router->mlag_role, mlag_role, sizeof(mlag_role))); vty_out(vty, "Mroute socket descriptor:"); vty_out(vty, " %d(%s)\n", pim->mroute_socket, vrf->name); @@ -4540,7 +5781,7 @@ static void pim_cmd_show_ip_multicast_helper(struct pim_instance *pim, show_scan_oil_stats(pim, vty, now); - show_multicast_interfaces(pim, vty); + show_multicast_interfaces(pim, vty, false); } DEFUN (show_ip_multicast, @@ -4592,6 +5833,60 @@ DEFUN (show_ip_multicast_vrf_all, return CMD_SUCCESS; } +DEFUN(show_ip_multicast_count, + show_ip_multicast_count_cmd, + "show ip multicast count [vrf NAME] [json]", + SHOW_STR IP_STR + "Multicast global information\n" + "Data packet count\n" + VRF_CMD_HELP_STR JSON_STR) +{ + int idx = 3; + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + bool uj = use_json(argc, argv); + + if (!vrf) + return CMD_WARNING; + + show_multicast_interfaces(vrf->info, vty, uj); + + return CMD_SUCCESS; +} + +DEFUN(show_ip_multicast_count_vrf_all, + show_ip_multicast_count_vrf_all_cmd, + "show ip multicast count vrf all [json]", + SHOW_STR IP_STR + "Multicast global information\n" + "Data packet count\n" + VRF_CMD_HELP_STR JSON_STR) +{ + bool uj = use_json(argc, argv); + struct vrf *vrf; + bool first = true; + + if (uj) + vty_out(vty, "{ "); + + RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { + if (uj) { + if (!first) + vty_out(vty, ", "); + + vty_out(vty, " \"%s\": ", vrf->name); + first = false; + } else + vty_out(vty, "VRF: %s\n", vrf->name); + + show_multicast_interfaces(vrf->info, vty, uj); + } + + if (uj) + vty_out(vty, "}\n"); + + return CMD_SUCCESS; +} + static void show_mroute(struct pim_instance *pim, struct vty *vty, struct prefix_sg *sg, bool fill, bool uj) { @@ -4613,21 +5908,27 @@ static void show_mroute(struct pim_instance *pim, struct vty *vty, int oif_vif_index; struct interface *ifp_in; char proto[100]; + char state_str[PIM_REG_STATE_STR_LEN]; + char mroute_uptime[10]; if (uj) { json = json_object_new_object(); } else { + vty_out(vty, "IP Multicast Routing Table\n"); + vty_out(vty, "Flags: S - Sparse, C - Connected, P - Pruned\n"); vty_out(vty, - "Source Group Proto Input Output TTL Uptime\n"); + " R - RP-bit set, F - Register flag, T - SPT-bit set\n"); + vty_out(vty, + "\nSource Group Flags Proto Input Output TTL Uptime\n"); } now = pim_time_monotonic_sec(); /* print list of PIM and IGMP routes */ - for (ALL_LIST_ELEMENTS_RO(pim->channel_oil_list, node, c_oil)) { + frr_each (rb_pim_oil, &pim->channel_oil_head, c_oil) { found_oif = 0; first = 1; - if (!c_oil->installed && !uj) + if (!c_oil->installed) continue; if (sg->grp.s_addr != 0 && @@ -4641,12 +5942,33 @@ static void show_mroute(struct pim_instance *pim, struct vty *vty, sizeof(grp_str)); pim_inet4_dump("", c_oil->oil.mfcc_origin, src_str, sizeof(src_str)); + + strlcpy(state_str, "S", sizeof(state_str)); + /* When a non DR receives a igmp join, it creates a (*,G) + * channel_oil without any upstream creation */ + if (c_oil->up) { + if (PIM_UPSTREAM_FLAG_TEST_SRC_IGMP(c_oil->up->flags)) + strlcat(state_str, "C", sizeof(state_str)); + if (pim_upstream_is_sg_rpt(c_oil->up)) + strlcat(state_str, "R", sizeof(state_str)); + if (PIM_UPSTREAM_FLAG_TEST_FHR(c_oil->up->flags)) + strlcat(state_str, "F", sizeof(state_str)); + if (c_oil->up->sptbit == PIM_UPSTREAM_SPTBIT_TRUE) + strlcat(state_str, "T", sizeof(state_str)); + } + if (pim_channel_oil_empty(c_oil)) + strlcat(state_str, "P", sizeof(state_str)); + ifp_in = pim_if_find_by_vif_index(pim, c_oil->oil.mfcc_parent); if (ifp_in) - strcpy(in_ifname, ifp_in->name); + strlcpy(in_ifname, ifp_in->name, sizeof(in_ifname)); else - strcpy(in_ifname, ""); + strlcpy(in_ifname, "", sizeof(in_ifname)); + + + pim_time_uptime(mroute_uptime, sizeof(mroute_uptime), + now - c_oil->mroute_creation); if (uj) { @@ -4660,7 +5982,8 @@ static void show_mroute(struct pim_instance *pim, struct vty *vty, } /* Find the source nested under the group, create it if - * it doesn't exist */ + * it doesn't exist + */ json_object_object_get_ex(json_group, src_str, &json_source); @@ -4681,29 +6004,37 @@ static void show_mroute(struct pim_instance *pim, struct vty *vty, json_object_int_add(json_source, "OilInheritedRescan", c_oil->oil_inherited_rescan); json_object_string_add(json_source, "iif", in_ifname); + json_object_string_add(json_source, "upTime", + mroute_uptime); json_oil = NULL; } for (oif_vif_index = 0; oif_vif_index < MAXVIFS; ++oif_vif_index) { struct interface *ifp_out; - char mroute_uptime[10]; int ttl; ttl = c_oil->oil.mfcc_ttls[oif_vif_index]; if (ttl < 1) continue; + /* do not display muted OIFs */ + if (c_oil->oif_flags[oif_vif_index] + & PIM_OIF_FLAG_MUTE) + continue; + + if (c_oil->oil.mfcc_parent == oif_vif_index && + !pim_mroute_allow_iif_in_oil(c_oil, + oif_vif_index)) + continue; + ifp_out = pim_if_find_by_vif_index(pim, oif_vif_index); - pim_time_uptime( - mroute_uptime, sizeof(mroute_uptime), - now - c_oil->mroute_creation); found_oif = 1; if (ifp_out) - strcpy(out_ifname, ifp_out->name); + strlcpy(out_ifname, ifp_out->name, sizeof(out_ifname)); else - strcpy(out_ifname, ""); + strlcpy(out_ifname, "", sizeof(out_ifname)); if (uj) { json_ifp_out = json_object_new_object(); @@ -4727,11 +6058,6 @@ static void show_mroute(struct pim_instance *pim, struct vty *vty, json_object_boolean_true_add( json_ifp_out, "protocolVxlan"); - if (c_oil->oif_flags[oif_vif_index] - & PIM_OIF_FLAG_PROTO_SOURCE) - json_object_boolean_true_add( - json_ifp_out, "protocolSource"); - if (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_STAR) json_object_boolean_true_add( @@ -4751,6 +6077,8 @@ static void show_mroute(struct pim_instance *pim, struct vty *vty, json_object_int_add(json_ifp_out, "ttl", ttl); json_object_string_add(json_ifp_out, "upTime", mroute_uptime); + json_object_string_add(json_source, "flags", + state_str); if (!json_oil) { json_oil = json_object_new_object(); json_object_object_add(json_source, @@ -4759,49 +6087,49 @@ static void show_mroute(struct pim_instance *pim, struct vty *vty, json_object_object_add(json_oil, out_ifname, json_ifp_out); } else { + proto[0] = '\0'; if (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_PIM) { - strcpy(proto, "PIM"); + strlcpy(proto, "PIM", sizeof(proto)); } if (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_IGMP) { - strcpy(proto, "IGMP"); + strlcpy(proto, "IGMP", sizeof(proto)); } if (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_VXLAN) { - strcpy(proto, "VxLAN"); - } - - if (c_oil->oif_flags[oif_vif_index] - & PIM_OIF_FLAG_PROTO_SOURCE) { - strcpy(proto, "SRC"); + strlcpy(proto, "VxLAN", sizeof(proto)); } if (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_STAR) { - strcpy(proto, "STAR"); + strlcpy(proto, "STAR", sizeof(proto)); } vty_out(vty, - "%-15s %-15s %-6s %-16s %-16s %-3d %8s\n", - src_str, grp_str, proto, in_ifname, - out_ifname, ttl, mroute_uptime); + "%-15s %-15s %-15s %-6s %-16s %-16s %-3d %8s\n", + src_str, grp_str, state_str, proto, + in_ifname, out_ifname, ttl, + mroute_uptime); if (first) { src_str[0] = '\0'; grp_str[0] = '\0'; in_ifname[0] = '\0'; + state_str[0] = '\0'; + mroute_uptime[0] = '\0'; first = 0; } } } if (!uj && !found_oif) { - vty_out(vty, "%-15s %-15s %-6s %-16s %-16s %-3d %8s\n", - src_str, grp_str, "none", in_ifname, "none", 0, - "--:--:--"); + vty_out(vty, + "%-15s %-15s %-15s %-6s %-16s %-16s %-3d %8s\n", + src_str, grp_str, state_str, "none", in_ifname, + "none", 0, "--:--:--"); } } @@ -4820,9 +6148,9 @@ static void show_mroute(struct pim_instance *pim, struct vty *vty, found_oif = 0; if (ifp_in) - strcpy(in_ifname, ifp_in->name); + strlcpy(in_ifname, ifp_in->name, sizeof(in_ifname)); else - strcpy(in_ifname, ""); + strlcpy(in_ifname, "", sizeof(in_ifname)); if (uj) { @@ -4849,7 +6177,7 @@ static void show_mroute(struct pim_instance *pim, struct vty *vty, json_object_string_add(json_source, "iif", in_ifname); json_oil = NULL; } else { - strcpy(proto, "STATIC"); + strlcpy(proto, "STATIC", sizeof(proto)); } for (oif_vif_index = 0; oif_vif_index < MAXVIFS; @@ -4871,9 +6199,9 @@ static void show_mroute(struct pim_instance *pim, struct vty *vty, found_oif = 1; if (ifp_out) - strcpy(out_ifname, ifp_out->name); + strlcpy(out_ifname, ifp_out->name, sizeof(out_ifname)); else - strcpy(out_ifname, ""); + strlcpy(out_ifname, "", sizeof(out_ifname)); if (uj) { json_ifp_out = json_object_new_object(); @@ -4946,53 +6274,225 @@ DEFPY (show_ip_mroute, "Fill in Assumed data\n" JSON_STR) { - struct prefix_sg sg = {0}; - struct pim_instance *pim; - struct vrf *v; - - v = vrf_lookup_by_name(vrf ? vrf : VRF_DEFAULT_NAME); - - if (!v) { - vty_out(vty, "%% Vrf specified: %s does not exist\n", vrf); - return CMD_WARNING; - } - pim = pim_get_pim_instance(v->vrf_id); + struct prefix_sg sg = {0}; + struct pim_instance *pim; + struct vrf *v; + + v = vrf_lookup_by_name(vrf ? vrf : VRF_DEFAULT_NAME); + + if (!v) { + vty_out(vty, "%% Vrf specified: %s does not exist\n", vrf); + return CMD_WARNING; + } + pim = pim_get_pim_instance(v->vrf_id); + + if (!pim) { + vty_out(vty, "%% Unable to find pim instance\n"); + return CMD_WARNING; + } + + if (s_or_g.s_addr != 0) { + if (g.s_addr != 0) { + sg.src = s_or_g; + sg.grp = g; + } else + sg.grp = s_or_g; + } + show_mroute(pim, vty, &sg, !!fill, !!json); + return CMD_SUCCESS; +} + +DEFUN (show_ip_mroute_vrf_all, + show_ip_mroute_vrf_all_cmd, + "show ip mroute vrf all [fill] [json]", + SHOW_STR + IP_STR + MROUTE_STR + VRF_CMD_HELP_STR + "Fill in Assumed data\n" + JSON_STR) +{ + struct prefix_sg sg = {0}; + bool uj = use_json(argc, argv); + int idx = 4; + struct vrf *vrf; + bool first = true; + bool fill = false; + + if (argv_find(argv, argc, "fill", &idx)) + fill = true; + + if (uj) + vty_out(vty, "{ "); + RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { + if (uj) { + if (!first) + vty_out(vty, ", "); + vty_out(vty, " \"%s\": ", vrf->name); + first = false; + } else + vty_out(vty, "VRF: %s\n", vrf->name); + show_mroute(vrf->info, vty, &sg, fill, uj); + } + if (uj) + vty_out(vty, "}\n"); + + return CMD_SUCCESS; +} + +DEFUN (clear_ip_mroute_count, + clear_ip_mroute_count_cmd, + "clear ip mroute [vrf NAME] count", + CLEAR_STR + IP_STR + MROUTE_STR + VRF_CMD_HELP_STR + "Route and packet count data\n") +{ + int idx = 2; + struct listnode *node; + struct channel_oil *c_oil; + struct static_route *sr; + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + struct pim_instance *pim; + + if (!vrf) + return CMD_WARNING; + + pim = vrf->info; + frr_each(rb_pim_oil, &pim->channel_oil_head, c_oil) { + if (!c_oil->installed) + continue; + + pim_mroute_update_counters(c_oil); + c_oil->cc.origpktcnt = c_oil->cc.pktcnt; + c_oil->cc.origbytecnt = c_oil->cc.bytecnt; + c_oil->cc.origwrong_if = c_oil->cc.wrong_if; + } + + for (ALL_LIST_ELEMENTS_RO(pim->static_routes, node, sr)) { + if (!sr->c_oil.installed) + continue; + + pim_mroute_update_counters(&sr->c_oil); + + sr->c_oil.cc.origpktcnt = sr->c_oil.cc.pktcnt; + sr->c_oil.cc.origbytecnt = sr->c_oil.cc.bytecnt; + sr->c_oil.cc.origwrong_if = sr->c_oil.cc.wrong_if; + } + return CMD_SUCCESS; +} + +static void show_mroute_count_per_channel_oil(struct channel_oil *c_oil, + json_object *json, + struct vty *vty) +{ + char group_str[INET_ADDRSTRLEN]; + char source_str[INET_ADDRSTRLEN]; + json_object *json_group = NULL; + json_object *json_source = NULL; + + if (!c_oil->installed) + return; + + pim_mroute_update_counters(c_oil); + + pim_inet4_dump("", c_oil->oil.mfcc_mcastgrp, group_str, + sizeof(group_str)); + pim_inet4_dump("", c_oil->oil.mfcc_origin, source_str, + sizeof(source_str)); + + if (json) { + json_object_object_get_ex(json, group_str, &json_group); + + if (!json_group) { + json_group = json_object_new_object(); + json_object_object_add(json, group_str, json_group); + } + + json_source = json_object_new_object(); + json_object_object_add(json_group, source_str, json_source); + json_object_int_add(json_source, "lastUsed", + c_oil->cc.lastused / 100); + json_object_int_add(json_source, "packets", c_oil->cc.pktcnt); + json_object_int_add(json_source, "bytes", c_oil->cc.bytecnt); + json_object_int_add(json_source, "wrongIf", c_oil->cc.wrong_if); + + } else { + vty_out(vty, "%-15s %-15s %-8llu %-7ld %-10ld %-7ld\n", + source_str, group_str, c_oil->cc.lastused / 100, + c_oil->cc.pktcnt - c_oil->cc.origpktcnt, + c_oil->cc.bytecnt - c_oil->cc.origbytecnt, + c_oil->cc.wrong_if - c_oil->cc.origwrong_if); + } +} + +static void show_mroute_count(struct pim_instance *pim, struct vty *vty, + bool uj) +{ + struct listnode *node; + struct channel_oil *c_oil; + struct static_route *sr; + json_object *json = NULL; + + if (uj) + json = json_object_new_object(); + else { + vty_out(vty, "\n"); + + vty_out(vty, + "Source Group LastUsed Packets Bytes WrongIf \n"); + } + + /* Print PIM and IGMP route counts */ + frr_each (rb_pim_oil, &pim->channel_oil_head, c_oil) + show_mroute_count_per_channel_oil(c_oil, json, vty); + + for (ALL_LIST_ELEMENTS_RO(pim->static_routes, node, sr)) + show_mroute_count_per_channel_oil(&sr->c_oil, json, vty); + + if (uj) { + vty_out(vty, "%s\n", + json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } +} + +DEFUN (show_ip_mroute_count, + show_ip_mroute_count_cmd, + "show ip mroute [vrf NAME] count [json]", + SHOW_STR + IP_STR + MROUTE_STR + VRF_CMD_HELP_STR + "Route and packet count data\n" + JSON_STR) +{ + int idx = 2; + bool uj = use_json(argc, argv); + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); - if (!pim) { - vty_out(vty, "%% Unable to find pim instance\n"); + if (!vrf) return CMD_WARNING; - } - if (s_or_g.s_addr != 0) { - if (g.s_addr != 0) { - sg.src = s_or_g; - sg.grp = g; - } else - sg.grp = s_or_g; - } - show_mroute(pim, vty, &sg, !!fill, !!json); + show_mroute_count(vrf->info, vty, uj); return CMD_SUCCESS; } -DEFUN (show_ip_mroute_vrf_all, - show_ip_mroute_vrf_all_cmd, - "show ip mroute vrf all [fill] [json]", +DEFUN (show_ip_mroute_count_vrf_all, + show_ip_mroute_count_vrf_all_cmd, + "show ip mroute vrf all count [json]", SHOW_STR IP_STR MROUTE_STR VRF_CMD_HELP_STR - "Fill in Assumed data\n" + "Route and packet count data\n" JSON_STR) { - struct prefix_sg sg = {0}; bool uj = use_json(argc, argv); - int idx = 4; struct vrf *vrf; bool first = true; - bool fill = false; - - if (argv_find(argv, argc, "fill", &idx)) - fill = true; if (uj) vty_out(vty, "{ "); @@ -5004,7 +6504,7 @@ DEFUN (show_ip_mroute_vrf_all, first = false; } else vty_out(vty, "VRF: %s\n", vrf->name); - show_mroute(vrf->info, vty, &sg, fill, uj); + show_mroute_count(vrf->info, vty, uj); } if (uj) vty_out(vty, "}\n"); @@ -5012,105 +6512,155 @@ DEFUN (show_ip_mroute_vrf_all, return CMD_SUCCESS; } -static void show_mroute_count(struct pim_instance *pim, struct vty *vty) +static void show_mroute_summary(struct pim_instance *pim, struct vty *vty, + json_object *json) { struct listnode *node; struct channel_oil *c_oil; struct static_route *s_route; - - vty_out(vty, "\n"); - - vty_out(vty, - "Source Group LastUsed Packets Bytes WrongIf \n"); - - /* Print PIM and IGMP route counts */ - for (ALL_LIST_ELEMENTS_RO(pim->channel_oil_list, node, c_oil)) { - char group_str[INET_ADDRSTRLEN]; - char source_str[INET_ADDRSTRLEN]; - - if (!c_oil->installed) - continue; - - pim_mroute_update_counters(c_oil); - - pim_inet4_dump("", c_oil->oil.mfcc_mcastgrp, group_str, - sizeof(group_str)); - pim_inet4_dump("", c_oil->oil.mfcc_origin, source_str, - sizeof(source_str)); - - vty_out(vty, "%-15s %-15s %-8llu %-7ld %-10ld %-7ld\n", - source_str, group_str, c_oil->cc.lastused / 100, - c_oil->cc.pktcnt, c_oil->cc.bytecnt, - c_oil->cc.wrong_if); + uint32_t starg_sw_mroute_cnt = 0; + uint32_t sg_sw_mroute_cnt = 0; + uint32_t starg_hw_mroute_cnt = 0; + uint32_t sg_hw_mroute_cnt = 0; + json_object *json_starg = NULL; + json_object *json_sg = NULL; + + if (!json) + vty_out(vty, "Mroute Type Installed/Total\n"); + + frr_each (rb_pim_oil, &pim->channel_oil_head, c_oil) { + if (!c_oil->installed) { + if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY) + starg_sw_mroute_cnt++; + else + sg_sw_mroute_cnt++; + } else { + if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY) + starg_hw_mroute_cnt++; + else + sg_hw_mroute_cnt++; + } } for (ALL_LIST_ELEMENTS_RO(pim->static_routes, node, s_route)) { - char group_str[INET_ADDRSTRLEN]; - char source_str[INET_ADDRSTRLEN]; + if (!s_route->c_oil.installed) { + if (s_route->c_oil.oil.mfcc_origin.s_addr == INADDR_ANY) + starg_sw_mroute_cnt++; + else + sg_sw_mroute_cnt++; + } else { + if (s_route->c_oil.oil.mfcc_origin.s_addr == INADDR_ANY) + starg_hw_mroute_cnt++; + else + sg_hw_mroute_cnt++; + } + } - if (!s_route->c_oil.installed) - continue; + if (!json) { + vty_out(vty, "%-20s %u/%u\n", "(*, G)", starg_hw_mroute_cnt, + starg_sw_mroute_cnt + starg_hw_mroute_cnt); + vty_out(vty, "%-20s %u/%u\n", "(S, G)", sg_hw_mroute_cnt, + sg_sw_mroute_cnt + sg_hw_mroute_cnt); + vty_out(vty, "------\n"); + vty_out(vty, "%-20s %u/%u\n", "Total", + (starg_hw_mroute_cnt + sg_hw_mroute_cnt), + (starg_sw_mroute_cnt + starg_hw_mroute_cnt + + sg_sw_mroute_cnt + sg_hw_mroute_cnt)); + } else { + /* (*,G) route details */ + json_starg = json_object_new_object(); + json_object_object_add(json, "wildcardGroup", json_starg); - pim_mroute_update_counters(&s_route->c_oil); + json_object_int_add(json_starg, "installed", + starg_hw_mroute_cnt); + json_object_int_add(json_starg, "total", + starg_sw_mroute_cnt + starg_hw_mroute_cnt); - pim_inet4_dump("", s_route->c_oil.oil.mfcc_mcastgrp, - group_str, sizeof(group_str)); - pim_inet4_dump("", s_route->c_oil.oil.mfcc_origin, - source_str, sizeof(source_str)); + /* (S, G) route details */ + json_sg = json_object_new_object(); + json_object_object_add(json, "sourceGroup", json_sg); - vty_out(vty, "%-15s %-15s %-8llu %-7ld %-10ld %-7ld\n", - source_str, group_str, s_route->c_oil.cc.lastused, - s_route->c_oil.cc.pktcnt, s_route->c_oil.cc.bytecnt, - s_route->c_oil.cc.wrong_if); + json_object_int_add(json_sg, "installed", sg_hw_mroute_cnt); + json_object_int_add(json_sg, "total", + sg_sw_mroute_cnt + sg_hw_mroute_cnt); + + json_object_int_add(json, "totalNumOfInstalledMroutes", + starg_hw_mroute_cnt + sg_hw_mroute_cnt); + json_object_int_add(json, "totalNumOfMroutes", + starg_sw_mroute_cnt + starg_hw_mroute_cnt + + sg_sw_mroute_cnt + + sg_hw_mroute_cnt); } } -DEFUN (show_ip_mroute_count, - show_ip_mroute_count_cmd, - "show ip mroute [vrf NAME] count", +DEFUN (show_ip_mroute_summary, + show_ip_mroute_summary_cmd, + "show ip mroute [vrf NAME] summary [json]", SHOW_STR IP_STR MROUTE_STR VRF_CMD_HELP_STR - "Route and packet count data\n") + "Summary of all mroutes\n" + JSON_STR) { int idx = 2; + bool uj = use_json(argc, argv); struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + json_object *json = NULL; + + if (uj) + json = json_object_new_object(); if (!vrf) return CMD_WARNING; - show_mroute_count(vrf->info, vty); + show_mroute_summary(vrf->info, vty, json); + + if (uj) { + vty_out(vty, "%s\n", + json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } return CMD_SUCCESS; } -DEFUN (show_ip_mroute_count_vrf_all, - show_ip_mroute_count_vrf_all_cmd, - "show ip mroute vrf all count", +DEFUN (show_ip_mroute_summary_vrf_all, + show_ip_mroute_summary_vrf_all_cmd, + "show ip mroute vrf all summary [json]", SHOW_STR IP_STR MROUTE_STR VRF_CMD_HELP_STR - "Route and packet count data\n") + "Summary of all mroutes\n" + JSON_STR) { - bool uj = use_json(argc, argv); struct vrf *vrf; - bool first = true; + bool uj = use_json(argc, argv); + json_object *json = NULL; + json_object *json_vrf = NULL; if (uj) - vty_out(vty, "{ "); + json = json_object_new_object(); + RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { - if (uj) { - if (!first) - vty_out(vty, ", "); - vty_out(vty, " \"%s\": ", vrf->name); - first = false; - } else + if (uj) + json_vrf = json_object_new_object(); + else vty_out(vty, "VRF: %s\n", vrf->name); - show_mroute_count(vrf->info, vty); + + show_mroute_summary(vrf->info, vty, json_vrf); + + if (uj) + json_object_object_add(json, vrf->name, json_vrf); + } + + if (uj) { + vty_out(vty, "%s\n", + json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); } - if (uj) - vty_out(vty, "}\n"); return CMD_SUCCESS; } @@ -5231,10 +6781,17 @@ static int pim_rp_cmd_worker(struct pim_instance *pim, struct vty *vty, { int result; - result = pim_rp_new(pim, rp, group, plist); + result = pim_rp_new_config(pim, rp, group, plist); + + if (result == PIM_GROUP_BAD_ADDR_MASK_COMBO) { + vty_out(vty, "%% Inconsistent address and mask: %s\n", + group ? group : "No Group Address"); + return CMD_WARNING_CONFIG_FAILED; + } if (result == PIM_GROUP_BAD_ADDRESS) { - vty_out(vty, "%% Bad group address specified: %s\n", group); + vty_out(vty, "%% Bad group address specified: %s\n", + group ? group : "No Group Address"); return CMD_WARNING_CONFIG_FAILED; } @@ -5266,12 +6823,6 @@ static int pim_rp_cmd_worker(struct pim_instance *pim, struct vty *vty, return CMD_WARNING_CONFIG_FAILED; } - if (result == PIM_GROUP_BAD_ADDR_MASK_COMBO) { - vty_out(vty, "%% Inconsistent address and mask: %s\n", - group); - return CMD_WARNING_CONFIG_FAILED; - } - return CMD_SUCCESS; } @@ -5283,18 +6834,18 @@ static int pim_cmd_spt_switchover(struct pim_instance *pim, switch (pim->spt.switchover) { case PIM_SPT_IMMEDIATE: - XFREE(MTYPE_PIM_SPT_PLIST_NAME, pim->spt.plist); + XFREE(MTYPE_PIM_PLIST_NAME, pim->spt.plist); pim_upstream_add_lhr_star_pimreg(pim); break; case PIM_SPT_INFINITY: pim_upstream_remove_lhr_star_pimreg(pim, plist); - XFREE(MTYPE_PIM_SPT_PLIST_NAME, pim->spt.plist); + XFREE(MTYPE_PIM_PLIST_NAME, pim->spt.plist); if (plist) pim->spt.plist = - XSTRDUP(MTYPE_PIM_SPT_PLIST_NAME, plist); + XSTRDUP(MTYPE_PIM_PLIST_NAME, plist); break; } @@ -5355,6 +6906,26 @@ DEFUN (no_ip_pim_spt_switchover_infinity_plist, return pim_cmd_spt_switchover(pim, PIM_SPT_IMMEDIATE, NULL); } +DEFPY (pim_register_accept_list, + pim_register_accept_list_cmd, + "[no] ip pim register-accept-list WORD$word", + NO_STR + IP_STR + PIM_STR + "Only accept registers from a specific source prefix list\n" + "Prefix-List name\n") +{ + PIM_DECLVAR_CONTEXT(vrf, pim); + + if (no) + XFREE(MTYPE_PIM_PLIST_NAME, pim->register_plist); + else { + XFREE(MTYPE_PIM_PLIST_NAME, pim->register_plist); + pim->register_plist = XSTRDUP(MTYPE_PIM_PLIST_NAME, word); + } + return CMD_SUCCESS; +} + DEFUN (ip_pim_joinprune_time, ip_pim_joinprune_time_cmd, "ip pim join-prune-interval (60-600)", @@ -5492,6 +7063,35 @@ DEFUN (no_ip_pim_packets, return CMD_SUCCESS; } +DEFPY (igmp_group_watermark, + igmp_group_watermark_cmd, + "ip igmp watermark-warn (10-60000)$limit", + IP_STR + IGMP_STR + "Configure group limit for watermark warning\n" + "Group count to generate watermark warning\n") +{ + PIM_DECLVAR_CONTEXT(vrf, pim); + pim->igmp_watermark_limit = limit; + + return CMD_SUCCESS; +} + +DEFPY (no_igmp_group_watermark, + no_igmp_group_watermark_cmd, + "no ip igmp watermark-warn [(10-60000)$limit]", + NO_STR + IP_STR + IGMP_STR + "Unconfigure group limit for watermark warning\n" + "Group count to generate watermark warning\n") +{ + PIM_DECLVAR_CONTEXT(vrf, pim); + pim->igmp_watermark_limit = 0; + + return CMD_SUCCESS; +} + DEFUN (ip_pim_v6_secondary, ip_pim_v6_secondary_cmd, "ip pim send-v6-secondary", @@ -5557,10 +7157,11 @@ static int pim_no_rp_cmd_worker(struct pim_instance *pim, struct vty *vty, const char *rp, const char *group, const char *plist) { - int result = pim_rp_del(pim, rp, group, plist); + int result = pim_rp_del_config(pim, rp, group, plist); if (result == PIM_GROUP_BAD_ADDRESS) { - vty_out(vty, "%% Bad group address specified: %s\n", group); + vty_out(vty, "%% Bad group address specified: %s\n", + group ? group : "No Group Address"); return CMD_WARNING_CONFIG_FAILED; } @@ -5617,6 +7218,7 @@ static int pim_ssm_cmd_worker(struct pim_instance *pim, struct vty *vty, const char *plist) { int result = pim_ssm_range_set(pim, pim->vrf_id, plist); + int ret = CMD_WARNING_CONFIG_FAILED; if (result == PIM_SSM_ERR_NONE) return CMD_SUCCESS; @@ -5627,12 +7229,13 @@ static int pim_ssm_cmd_worker(struct pim_instance *pim, struct vty *vty, break; case PIM_SSM_ERR_DUP: vty_out(vty, "%% duplicate config\n"); + ret = CMD_WARNING; break; default: vty_out(vty, "%% ssm range config failed\n"); } - return CMD_WARNING_CONFIG_FAILED; + return ret; } DEFUN (ip_pim_ssm_prefix_list, @@ -5776,6 +7379,27 @@ DEFUN (show_ip_pim_group_type, return CMD_SUCCESS; } +DEFUN (show_ip_pim_bsr, + show_ip_pim_bsr_cmd, + "show ip pim bsr [json]", + SHOW_STR + IP_STR + PIM_STR + "boot-strap router information\n" + JSON_STR) +{ + int idx = 2; + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + bool uj = use_json(argc, argv); + + if (!vrf) + return CMD_WARNING; + + pim_show_bsr(vrf->info, vty, uj); + + return CMD_SUCCESS; +} + DEFUN (ip_ssmpingd, ip_ssmpingd_cmd, "ip ssmpingd [A.B.C.D]", @@ -5897,18 +7521,21 @@ DEFUN (no_ip_pim_ecmp_rebalance, static int pim_cmd_igmp_start(struct vty *vty, struct interface *ifp) { struct pim_interface *pim_ifp; + struct pim_instance *pim; uint8_t need_startup = 0; pim_ifp = ifp->info; if (!pim_ifp) { - pim_ifp = pim_if_new(ifp, true, false, false, - false /*vxlan_term*/); - if (!pim_ifp) { - vty_out(vty, "Could not enable IGMP on interface %s\n", - ifp->name); + pim = pim_get_pim_instance(ifp->vrf_id); + /* Limit mcast interfaces to number of vifs available */ + if (pim->mcast_if_count == MAXVIFS) { + vty_out(vty, + "Max multicast interfaces(%d) Reached. Could not enable IGMP on interface %s\n", + MAXVIFS, ifp->name); return CMD_WARNING_CONFIG_FAILED; } + (void)pim_if_new(ifp, true, false, false, false); need_startup = 1; } else { if (!PIM_IF_TEST_IGMP(pim_ifp->options)) { @@ -5966,7 +7593,7 @@ DEFUN (interface_no_ip_igmp, DEFUN (interface_ip_igmp_join, interface_ip_igmp_join_cmd, - "ip igmp join A.B.C.D A.B.C.D", + "ip igmp join A.B.C.D [A.B.C.D]", IP_STR IFACE_IGMP_STR "IGMP join multicast group\n" @@ -5992,12 +7619,21 @@ DEFUN (interface_ip_igmp_join, } /* Source address */ - source_str = argv[idx_ipv4_2]->arg; - result = inet_pton(AF_INET, source_str, &source_addr); - if (result <= 0) { - vty_out(vty, "Bad source address %s: errno=%d: %s\n", - source_str, errno, safe_strerror(errno)); - return CMD_WARNING_CONFIG_FAILED; + if (argc == (idx_ipv4_2 + 1)) { + source_str = argv[idx_ipv4_2]->arg; + result = inet_pton(AF_INET, source_str, &source_addr); + if (result <= 0) { + vty_out(vty, "Bad source address %s: errno=%d: %s\n", + source_str, errno, safe_strerror(errno)); + return CMD_WARNING_CONFIG_FAILED; + } + /* Reject 0.0.0.0. Reserved for any source. */ + if (source_addr.s_addr == INADDR_ANY) { + vty_out(vty, "Bad source address %s\n", source_str); + return CMD_WARNING_CONFIG_FAILED; + } + } else { + source_addr.s_addr = INADDR_ANY; } CMD_FERR_RETURN(pim_if_igmp_join_add(ifp, group_addr, source_addr), @@ -6008,7 +7644,7 @@ DEFUN (interface_ip_igmp_join, DEFUN (interface_no_ip_igmp_join, interface_no_ip_igmp_join_cmd, - "no ip igmp join A.B.C.D A.B.C.D", + "no ip igmp join A.B.C.D [A.B.C.D]", NO_STR IP_STR IFACE_IGMP_STR @@ -6035,12 +7671,22 @@ DEFUN (interface_no_ip_igmp_join, } /* Source address */ - source_str = argv[idx_ipv4_2]->arg; - result = inet_pton(AF_INET, source_str, &source_addr); - if (result <= 0) { - vty_out(vty, "Bad source address %s: errno=%d: %s\n", - source_str, errno, safe_strerror(errno)); - return CMD_WARNING_CONFIG_FAILED; + if (argc == (idx_ipv4_2 + 1)) { + source_str = argv[idx_ipv4_2]->arg; + result = inet_pton(AF_INET, source_str, &source_addr); + if (result <= 0) { + vty_out(vty, "Bad source address %s: errno=%d: %s\n", + source_str, errno, safe_strerror(errno)); + return CMD_WARNING_CONFIG_FAILED; + } + /* Reject 0.0.0.0. Reserved for any source. */ + if (source_addr.s_addr == INADDR_ANY) { + vty_out(vty, "Bad source address %s\n", source_str); + return CMD_WARNING_CONFIG_FAILED; + } + } else { + source_str = "*"; + source_addr.s_addr = INADDR_ANY; } result = pim_if_igmp_join_del(ifp, group_addr, source_addr); @@ -6084,7 +7730,7 @@ static void igmp_sock_query_interval_reconfig(struct igmp_sock *igmp) pim_inet4_dump("", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str)); zlog_debug("%s: Querier %s on %s reconfig query_interval=%d", - __PRETTY_FUNCTION__, ifaddr_str, ifp->name, + __func__, ifaddr_str, ifp->name, pim_ifp->igmp_default_query_interval); } @@ -6098,6 +7744,9 @@ static void igmp_sock_query_interval_reconfig(struct igmp_sock *igmp) static void igmp_sock_query_reschedule(struct igmp_sock *igmp) { + if (igmp->mtrace_only) + return; + if (igmp->t_igmp_query_timer) { /* other querier present */ zassert(igmp->t_igmp_query_timer); @@ -6447,8 +8096,108 @@ DEFUN_HIDDEN (interface_no_ip_igmp_query_max_response_time_dsec, if (!pim_ifp) return CMD_SUCCESS; - change_query_max_response_time(pim_ifp, - IGMP_QUERY_MAX_RESPONSE_TIME_DSEC); + change_query_max_response_time(pim_ifp, + IGMP_QUERY_MAX_RESPONSE_TIME_DSEC); + + return CMD_SUCCESS; +} + +#define IGMP_LAST_MEMBER_QUERY_COUNT_MIN (1) +#define IGMP_LAST_MEMBER_QUERY_COUNT_MAX (7) + +DEFUN (interface_ip_igmp_last_member_query_count, + interface_ip_igmp_last_member_query_count_cmd, + "ip igmp last-member-query-count (1-7)", + IP_STR + IFACE_IGMP_STR + IFACE_IGMP_LAST_MEMBER_QUERY_COUNT_STR + "Last member query count\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct pim_interface *pim_ifp = ifp->info; + int last_member_query_count; + int ret; + + if (!pim_ifp) { + ret = pim_cmd_igmp_start(vty, ifp); + if (ret != CMD_SUCCESS) + return ret; + pim_ifp = ifp->info; + } + + last_member_query_count = atoi(argv[3]->arg); + + pim_ifp->igmp_last_member_query_count = last_member_query_count; + + return CMD_SUCCESS; +} + +DEFUN (interface_no_ip_igmp_last_member_query_count, + interface_no_ip_igmp_last_member_query_count_cmd, + "no ip igmp last-member-query-count", + NO_STR + IP_STR + IFACE_IGMP_STR + IFACE_IGMP_LAST_MEMBER_QUERY_COUNT_STR) +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct pim_interface *pim_ifp = ifp->info; + + if (!pim_ifp) + return CMD_SUCCESS; + + pim_ifp->igmp_last_member_query_count = + IGMP_DEFAULT_ROBUSTNESS_VARIABLE; + + return CMD_SUCCESS; +} + +#define IGMP_LAST_MEMBER_QUERY_INTERVAL_MIN (1) +#define IGMP_LAST_MEMBER_QUERY_INTERVAL_MAX (255) + +DEFUN (interface_ip_igmp_last_member_query_interval, + interface_ip_igmp_last_member_query_interval_cmd, + "ip igmp last-member-query-interval (1-255)", + IP_STR + IFACE_IGMP_STR + IFACE_IGMP_LAST_MEMBER_QUERY_INTERVAL_STR + "Last member query interval in deciseconds\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct pim_interface *pim_ifp = ifp->info; + int last_member_query_interval; + int ret; + + if (!pim_ifp) { + ret = pim_cmd_igmp_start(vty, ifp); + if (ret != CMD_SUCCESS) + return ret; + pim_ifp = ifp->info; + } + + last_member_query_interval = atoi(argv[3]->arg); + pim_ifp->igmp_specific_query_max_response_time_dsec + = last_member_query_interval; + + return CMD_SUCCESS; +} + +DEFUN (interface_no_ip_igmp_last_member_query_interval, + interface_no_ip_igmp_last_member_query_interval_cmd, + "no ip igmp last-member-query-interval", + NO_STR + IP_STR + IFACE_IGMP_STR + IFACE_IGMP_LAST_MEMBER_QUERY_INTERVAL_STR) +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct pim_interface *pim_ifp = ifp->info; + + if (!pim_ifp) + return CMD_SUCCESS; + + pim_ifp->igmp_specific_query_max_response_time_dsec = + IGMP_SPECIFIC_QUERY_MAX_RESPONSE_TIME_DSEC; return CMD_SUCCESS; } @@ -6509,22 +8258,53 @@ DEFUN (interface_no_ip_pim_drprio, return CMD_SUCCESS; } -static int pim_cmd_interface_add(struct interface *ifp) +DEFPY_HIDDEN (interface_ip_igmp_query_generate, + interface_ip_igmp_query_generate_cmd, + "ip igmp generate-query-once [version (2-3)]", + IP_STR + IFACE_IGMP_STR + "Generate igmp general query once\n" + "IGMP version\n" + "IGMP version number\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + int igmp_version = 2; + + if (!ifp->info) { + vty_out(vty, "IGMP/PIM is not enabled on the interface %s\n", + ifp->name); + return CMD_WARNING_CONFIG_FAILED; + } + + if (argc > 3) + igmp_version = atoi(argv[4]->arg); + + igmp_send_query_on_intf(ifp, igmp_version); + + return CMD_SUCCESS; +} + +static int pim_cmd_interface_add(struct vty *vty, struct interface *ifp) { struct pim_interface *pim_ifp = ifp->info; + struct pim_instance *pim; if (!pim_ifp) { - pim_ifp = pim_if_new(ifp, false, true, false, - false /*vxlan_term*/); - if (!pim_ifp) { + pim = pim_get_pim_instance(ifp->vrf_id); + /* Limiting mcast interfaces to number of VIFs */ + if (pim->mcast_if_count == MAXVIFS) { + vty_out(vty, "Max multicast interfaces(%d) reached.", + MAXVIFS); return 0; } - } else { + pim_ifp = pim_if_new(ifp, false, true, false, false); + } else PIM_IF_DO_PIM(pim_ifp->options); - } pim_if_addr_add_all(ifp); pim_if_membership_refresh(ifp); + + pim_if_create_pimreg(pim_ifp->pim); return 1; } @@ -6578,27 +8358,34 @@ DEFPY_HIDDEN (pim_test_sg_keepalive, return CMD_SUCCESS; } -DEFPY_HIDDEN (interface_ip_pim_activeactive, - interface_ip_pim_activeactive_cmd, - "[no$no] ip pim active-active", - NO_STR - IP_STR - PIM_STR - "Mark interface as Active-Active for MLAG operations, Hidden because not finished yet\n") +DEFPY (interface_ip_pim_activeactive, + interface_ip_pim_activeactive_cmd, + "[no$no] ip pim active-active", + NO_STR + IP_STR + PIM_STR + "Mark interface as Active-Active for MLAG operations, Hidden because not finished yet\n") { VTY_DECLVAR_CONTEXT(interface, ifp); struct pim_interface *pim_ifp; - if (!no && !pim_cmd_interface_add(ifp)) { - vty_out(vty, "Could not enable PIM SM active-active on interface\n"); + if (!no && !pim_cmd_interface_add(vty, ifp)) { + vty_out(vty, + "Could not enable PIM SM active-active on interface %s\n", + ifp->name); return CMD_WARNING_CONFIG_FAILED; } + + if (PIM_DEBUG_MLAG) + zlog_debug("%sConfiguring PIM active-active on Interface: %s", + no ? "Un-" : " ", ifp->name); + pim_ifp = ifp->info; if (no) - pim_ifp->activeactive = false; + pim_if_unconfigure_mlag_dualactive(pim_ifp); else - pim_ifp->activeactive = true; + pim_if_configure_mlag_dualactive(pim_ifp); return CMD_SUCCESS; } @@ -6612,14 +8399,14 @@ DEFUN_HIDDEN (interface_ip_pim_ssm, { VTY_DECLVAR_CONTEXT(interface, ifp); - if (!pim_cmd_interface_add(ifp)) { - vty_out(vty, "Could not enable PIM SM on interface\n"); + if (!pim_cmd_interface_add(vty, ifp)) { + vty_out(vty, "Could not enable PIM SM on interface %s\n", + ifp->name); return CMD_WARNING_CONFIG_FAILED; } vty_out(vty, - "WARN: Enabled PIM SM on interface; configure PIM SSM " - "range if needed\n"); + "WARN: Enabled PIM SM on interface; configure PIM SSM range if needed\n"); return CMD_SUCCESS; } @@ -6629,8 +8416,9 @@ static int interface_ip_pim_helper(struct vty *vty) VTY_DECLVAR_CONTEXT(interface, ifp); - if (!pim_cmd_interface_add(ifp)) { - vty_out(vty, "Could not enable PIM SM on interface\n"); + if (!pim_cmd_interface_add(vty, ifp)) { + vty_out(vty, "Could not enable PIM SM on interface %s\n", + ifp->name); return CMD_WARNING_CONFIG_FAILED; } @@ -6782,55 +8570,7 @@ DEFUN(interface_no_ip_pim_boundary_oil, DEFUN (interface_ip_mroute, interface_ip_mroute_cmd, - "ip mroute INTERFACE A.B.C.D", - IP_STR - "Add multicast route\n" - "Outgoing interface name\n" - "Group address\n") -{ - VTY_DECLVAR_CONTEXT(interface, iif); - struct pim_interface *pim_ifp; - struct pim_instance *pim; - int idx_interface = 2; - int idx_ipv4 = 3; - struct interface *oif; - const char *oifname; - const char *grp_str; - struct in_addr grp_addr; - struct in_addr src_addr; - int result; - - PIM_GET_PIM_INTERFACE(pim_ifp, iif); - pim = pim_ifp->pim; - - oifname = argv[idx_interface]->arg; - oif = if_lookup_by_name(oifname, pim->vrf_id); - if (!oif) { - vty_out(vty, "No such interface name %s\n", oifname); - return CMD_WARNING; - } - - grp_str = argv[idx_ipv4]->arg; - result = inet_pton(AF_INET, grp_str, &grp_addr); - if (result <= 0) { - vty_out(vty, "Bad group address %s: errno=%d: %s\n", grp_str, - errno, safe_strerror(errno)); - return CMD_WARNING; - } - - src_addr.s_addr = INADDR_ANY; - - if (pim_static_add(pim, iif, oif, grp_addr, src_addr)) { - vty_out(vty, "Failed to add route\n"); - return CMD_WARNING; - } - - return CMD_SUCCESS; -} - -DEFUN (interface_ip_mroute_source, - interface_ip_mroute_source_cmd, - "ip mroute INTERFACE A.B.C.D A.B.C.D", + "ip mroute INTERFACE A.B.C.D [A.B.C.D]", IP_STR "Add multicast route\n" "Outgoing interface name\n" @@ -6842,7 +8582,6 @@ DEFUN (interface_ip_mroute_source, struct pim_instance *pim; int idx_interface = 2; int idx_ipv4 = 3; - int idx_ipv4_2 = 4; struct interface *oif; const char *oifname; const char *grp_str; @@ -6869,16 +8608,21 @@ DEFUN (interface_ip_mroute_source, return CMD_WARNING; } - src_str = argv[idx_ipv4_2]->arg; - result = inet_pton(AF_INET, src_str, &src_addr); - if (result <= 0) { - vty_out(vty, "Bad source address %s: errno=%d: %s\n", src_str, - errno, safe_strerror(errno)); - return CMD_WARNING; - } + if (argc == (idx_ipv4 + 1)) { + src_addr.s_addr = INADDR_ANY; + } + else { + src_str = argv[idx_ipv4 + 1]->arg; + result = inet_pton(AF_INET, src_str, &src_addr); + if (result <= 0) { + vty_out(vty, "Bad source address %s: errno=%d: %s\n", src_str, + errno, safe_strerror(errno)); + return CMD_WARNING; + } + } if (pim_static_add(pim, iif, oif, grp_addr, src_addr)) { - vty_out(vty, "Failed to add route\n"); + vty_out(vty, "Failed to add static mroute\n"); return CMD_WARNING; } @@ -6887,56 +8631,7 @@ DEFUN (interface_ip_mroute_source, DEFUN (interface_no_ip_mroute, interface_no_ip_mroute_cmd, - "no ip mroute INTERFACE A.B.C.D", - NO_STR - IP_STR - "Add multicast route\n" - "Outgoing interface name\n" - "Group Address\n") -{ - VTY_DECLVAR_CONTEXT(interface, iif); - struct pim_interface *pim_ifp; - struct pim_instance *pim; - int idx_interface = 3; - int idx_ipv4 = 4; - struct interface *oif; - const char *oifname; - const char *grp_str; - struct in_addr grp_addr; - struct in_addr src_addr; - int result; - - PIM_GET_PIM_INTERFACE(pim_ifp, iif); - pim = pim_ifp->pim; - - oifname = argv[idx_interface]->arg; - oif = if_lookup_by_name(oifname, pim->vrf_id); - if (!oif) { - vty_out(vty, "No such interface name %s\n", oifname); - return CMD_WARNING; - } - - grp_str = argv[idx_ipv4]->arg; - result = inet_pton(AF_INET, grp_str, &grp_addr); - if (result <= 0) { - vty_out(vty, "Bad group address %s: errno=%d: %s\n", grp_str, - errno, safe_strerror(errno)); - return CMD_WARNING; - } - - src_addr.s_addr = INADDR_ANY; - - if (pim_static_del(pim, iif, oif, grp_addr, src_addr)) { - vty_out(vty, "Failed to remove route\n"); - return CMD_WARNING; - } - - return CMD_SUCCESS; -} - -DEFUN (interface_no_ip_mroute_source, - interface_no_ip_mroute_source_cmd, - "no ip mroute INTERFACE A.B.C.D A.B.C.D", + "no ip mroute INTERFACE A.B.C.D [A.B.C.D]", NO_STR IP_STR "Add multicast route\n" @@ -6949,7 +8644,6 @@ DEFUN (interface_no_ip_mroute_source, struct pim_instance *pim; int idx_interface = 3; int idx_ipv4 = 4; - int idx_ipv4_2 = 5; struct interface *oif; const char *oifname; const char *grp_str; @@ -6976,16 +8670,21 @@ DEFUN (interface_no_ip_mroute_source, return CMD_WARNING; } - src_str = argv[idx_ipv4_2]->arg; - result = inet_pton(AF_INET, src_str, &src_addr); - if (result <= 0) { - vty_out(vty, "Bad source address %s: errno=%d: %s\n", src_str, - errno, safe_strerror(errno)); - return CMD_WARNING; - } + if (argc == (idx_ipv4 + 1)) { + src_addr.s_addr = INADDR_ANY; + } + else { + src_str = argv[idx_ipv4 + 1]->arg; + result = inet_pton(AF_INET, src_str, &src_addr); + if (result <= 0) { + vty_out(vty, "Bad source address %s: errno=%d: %s\n", src_str, + errno, safe_strerror(errno)); + return CMD_WARNING; + } + } if (pim_static_del(pim, iif, oif, grp_addr, src_addr)) { - vty_out(vty, "Failed to remove route\n"); + vty_out(vty, "Failed to remove static mroute\n"); return CMD_WARNING; } @@ -7007,8 +8706,10 @@ DEFUN (interface_ip_pim_hello, struct pim_interface *pim_ifp = ifp->info; if (!pim_ifp) { - if (!pim_cmd_interface_add(ifp)) { - vty_out(vty, "Could not enable PIM SM on interface\n"); + if (!pim_cmd_interface_add(vty, ifp)) { + vty_out(vty, + "Could not enable PIM SM on interface %s\n", + ifp->name); return CMD_WARNING_CONFIG_FAILED; } } @@ -7224,6 +8925,7 @@ DEFUN (debug_pim, PIM_DO_DEBUG_PIM_TRACE; PIM_DO_DEBUG_MSDP_EVENTS; PIM_DO_DEBUG_MSDP_PACKETS; + PIM_DO_DEBUG_BSM; return CMD_SUCCESS; } @@ -7242,6 +8944,7 @@ DEFUN (no_debug_pim, PIM_DONT_DEBUG_PIM_PACKETDUMP_SEND; PIM_DONT_DEBUG_PIM_PACKETDUMP_RECV; + PIM_DONT_DEBUG_BSM; return CMD_SUCCESS; } @@ -7514,6 +9217,20 @@ DEFUN (no_debug_pim_zebra, return CMD_SUCCESS; } +DEFUN(debug_pim_mlag, debug_pim_mlag_cmd, "debug pim mlag", + DEBUG_STR DEBUG_PIM_STR DEBUG_PIM_MLAG_STR) +{ + PIM_DO_DEBUG_MLAG; + return CMD_SUCCESS; +} + +DEFUN(no_debug_pim_mlag, no_debug_pim_mlag_cmd, "no debug pim mlag", + NO_STR DEBUG_STR DEBUG_PIM_STR DEBUG_PIM_MLAG_STR) +{ + PIM_DONT_DEBUG_MLAG; + return CMD_SUCCESS; +} + DEFUN (debug_pim_vxlan, debug_pim_vxlan_cmd, "debug pim vxlan", @@ -7627,6 +9344,30 @@ DEFUN (no_debug_mtrace, return CMD_SUCCESS; } +DEFUN (debug_bsm, + debug_bsm_cmd, + "debug pim bsm", + DEBUG_STR + DEBUG_PIM_STR + DEBUG_PIM_BSM_STR) +{ + PIM_DO_DEBUG_BSM; + return CMD_SUCCESS; +} + +DEFUN (no_debug_bsm, + no_debug_bsm_cmd, + "no debug pim bsm", + NO_STR + DEBUG_STR + DEBUG_PIM_STR + DEBUG_PIM_BSM_STR) +{ + PIM_DONT_DEBUG_BSM; + return CMD_SUCCESS; +} + + DEFUN_NOSH (show_debugging_pim, show_debugging_pim_cmd, "show debugging [pim]", @@ -7710,8 +9451,10 @@ DEFUN (ip_pim_bfd, struct bfd_info *bfd_info = NULL; if (!pim_ifp) { - if (!pim_cmd_interface_add(ifp)) { - vty_out(vty, "Could not enable PIM SM on interface\n"); + if (!pim_cmd_interface_add(vty, ifp)) { + vty_out(vty, + "Could not enable PIM SM on interface %s\n", + ifp->name); return CMD_WARNING; } } @@ -7750,11 +9493,111 @@ DEFUN (no_ip_pim_bfd, return CMD_SUCCESS; } +DEFUN (ip_pim_bsm, + ip_pim_bsm_cmd, + "ip pim bsm", + IP_STR + PIM_STR + "Enables BSM support on the interface\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct pim_interface *pim_ifp = ifp->info; + + if (!pim_ifp) { + if (!pim_cmd_interface_add(vty, ifp)) { + vty_out(vty, + "Could not enable PIM SM on interface %s\n", + ifp->name); + return CMD_WARNING; + } + } + + pim_ifp = ifp->info; + pim_ifp->bsm_enable = true; + + return CMD_SUCCESS; +} + +DEFUN (no_ip_pim_bsm, + no_ip_pim_bsm_cmd, + "no ip pim bsm", + NO_STR + IP_STR + PIM_STR + "Disables BSM support\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct pim_interface *pim_ifp = ifp->info; + + if (!pim_ifp) { + vty_out(vty, "Pim not enabled on this interface\n"); + return CMD_WARNING; + } + + pim_ifp->bsm_enable = false; + + return CMD_SUCCESS; +} + +DEFUN (ip_pim_ucast_bsm, + ip_pim_ucast_bsm_cmd, + "ip pim unicast-bsm", + IP_STR + PIM_STR + "Accept/Send unicast BSM on the interface\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct pim_interface *pim_ifp = ifp->info; + + if (!pim_ifp) { + if (!pim_cmd_interface_add(vty, ifp)) { + vty_out(vty, + "Could not enable PIM SM on interface %s\n", + ifp->name); + return CMD_WARNING; + } + } + + pim_ifp = ifp->info; + pim_ifp->ucast_bsm_accept = true; + + return CMD_SUCCESS; +} + +DEFUN (no_ip_pim_ucast_bsm, + no_ip_pim_ucast_bsm_cmd, + "no ip pim unicast-bsm", + NO_STR + IP_STR + PIM_STR + "Block send/receive unicast BSM on this interface\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct pim_interface *pim_ifp = ifp->info; + + if (!pim_ifp) { + vty_out(vty, "Pim not enabled on this interface\n"); + return CMD_WARNING; + } + + pim_ifp->ucast_bsm_accept = false; + + return CMD_SUCCESS; +} + #if HAVE_BFDD > 0 DEFUN_HIDDEN( + ip_pim_bfd_param, + ip_pim_bfd_param_cmd, + "ip pim bfd (2-255) (50-60000) (50-60000)", + IP_STR + PIM_STR + "Enables BFD support\n" + "Detect Multiplier\n" + "Required min receive interval\n" + "Desired min transmit interval\n") #else DEFUN( -#endif /* HAVE_BFDD */ ip_pim_bfd_param, ip_pim_bfd_param_cmd, "ip pim bfd (2-255) (50-60000) (50-60000)", @@ -7764,6 +9607,7 @@ DEFUN( "Detect Multiplier\n" "Required min receive interval\n" "Desired min transmit interval\n") +#endif /* HAVE_BFDD */ { VTY_DECLVAR_CONTEXT(interface, ifp); int idx_number = 3; @@ -7776,8 +9620,10 @@ DEFUN( struct pim_interface *pim_ifp = ifp->info; if (!pim_ifp) { - if (!pim_cmd_interface_add(ifp)) { - vty_out(vty, "Could not enable PIM SM on interface\n"); + if (!pim_cmd_interface_add(vty, ifp)) { + vty_out(vty, + "Could not enable PIM SM on interface %s\n", + ifp->name); return CMD_WARNING; } } @@ -8102,11 +9948,25 @@ DEFUN (no_ip_msdp_mesh_group_source, "mesh group local address\n") { PIM_DECLVAR_CONTEXT(vrf, pim); - if (argc == 7) - return ip_no_msdp_mesh_group_cmd_worker(pim, vty, argv[6]->arg); + + return ip_no_msdp_mesh_group_source_cmd_worker(pim, vty, argv[4]->arg); +} + +DEFUN (no_ip_msdp_mesh_group, + no_ip_msdp_mesh_group_cmd, + "no ip msdp mesh-group [WORD]", + NO_STR + IP_STR + CFG_MSDP_STR + "Delete MSDP mesh-group\n" + "mesh group name") +{ + PIM_DECLVAR_CONTEXT(vrf, pim); + + if (argc == 5) + return ip_no_msdp_mesh_group_cmd_worker(pim, vty, argv[4]->arg); else - return ip_no_msdp_mesh_group_source_cmd_worker(pim, vty, - argv[4]->arg); + return ip_no_msdp_mesh_group_cmd_worker(pim, vty, NULL); } static void print_empty_json_obj(struct vty *vty) @@ -8267,7 +10127,7 @@ static void ip_msdp_show_peers(struct pim_instance *pim, struct vty *vty, pim_time_uptime(timebuf, sizeof(timebuf), now - mp->uptime); } else { - strcpy(timebuf, "-"); + strlcpy(timebuf, "-", sizeof(timebuf)); } pim_inet4_dump("", mp->peer, peer_str, sizeof(peer_str)); pim_inet4_dump("", mp->local, local_str, @@ -8324,7 +10184,7 @@ static void ip_msdp_show_peers_detail(struct pim_instance *pim, struct vty *vty, pim_time_uptime(timebuf, sizeof(timebuf), now - mp->uptime); } else { - strcpy(timebuf, "-"); + strlcpy(timebuf, "-", sizeof(timebuf)); } pim_inet4_dump("", mp->local, local_str, sizeof(local_str)); @@ -8503,18 +10363,18 @@ static void ip_msdp_show_sa(struct pim_instance *pim, struct vty *vty, bool uj) if (sa->flags & PIM_MSDP_SAF_PEER) { pim_inet4_dump("", sa->rp, rp_str, sizeof(rp_str)); if (sa->up) { - strcpy(spt_str, "yes"); + strlcpy(spt_str, "yes", sizeof(spt_str)); } else { - strcpy(spt_str, "no"); + strlcpy(spt_str, "no", sizeof(spt_str)); } } else { - strcpy(rp_str, "-"); - strcpy(spt_str, "-"); + strlcpy(rp_str, "-", sizeof(rp_str)); + strlcpy(spt_str, "-", sizeof(spt_str)); } if (sa->flags & PIM_MSDP_SAF_LOCAL) { - strcpy(local_str, "yes"); + strlcpy(local_str, "yes", sizeof(local_str)); } else { - strcpy(local_str, "no"); + strlcpy(local_str, "no", sizeof(local_str)); } if (uj) { json_object_object_get_ex(json, grp_str, &json_group); @@ -8568,19 +10428,19 @@ static void ip_msdp_show_sa_entry_detail(struct pim_msdp_sa *sa, pim_inet4_dump("", sa->rp, rp_str, sizeof(rp_str)); pim_inet4_dump("", sa->peer, peer_str, sizeof(peer_str)); if (sa->up) { - strcpy(spt_str, "yes"); + strlcpy(spt_str, "yes", sizeof(spt_str)); } else { - strcpy(spt_str, "no"); + strlcpy(spt_str, "no", sizeof(spt_str)); } } else { - strcpy(rp_str, "-"); - strcpy(peer_str, "-"); - strcpy(spt_str, "-"); + strlcpy(rp_str, "-", sizeof(rp_str)); + strlcpy(peer_str, "-", sizeof(peer_str)); + strlcpy(spt_str, "-", sizeof(spt_str)); } if (sa->flags & PIM_MSDP_SAF_LOCAL) { - strcpy(local_str, "yes"); + strlcpy(local_str, "yes", sizeof(local_str)); } else { - strcpy(local_str, "no"); + strlcpy(local_str, "no", sizeof(local_str)); } pim_time_timer_to_hhmmss(statetimer, sizeof(statetimer), sa->sa_state_timer); @@ -8854,7 +10714,7 @@ static void pim_show_vxlan_sg_entry(struct pim_vxlan_sg *vxlan_sg, char src_str[INET_ADDRSTRLEN]; char grp_str[INET_ADDRSTRLEN]; json_object *json_row; - bool installed = (vxlan_sg->up)?TRUE:FALSE; + bool installed = (vxlan_sg->up) ? true : false; const char *iif_name = vxlan_sg->iif?vxlan_sg->iif->name:"-"; const char *oif_name; @@ -8895,7 +10755,7 @@ static void pim_show_vxlan_sg_entry(struct pim_vxlan_sg *vxlan_sg, } } -static void pim_show_vxlan_sg_hash_entry(struct hash_backet *backet, void *arg) +static void pim_show_vxlan_sg_hash_entry(struct hash_bucket *backet, void *arg) { pim_show_vxlan_sg_entry((struct pim_vxlan_sg *)backet->data, (struct pim_sg_cache_walk_data *)arg); @@ -8952,7 +10812,7 @@ static void pim_show_vxlan_sg_match_addr(struct pim_instance *pim, cwd.vty = vty; cwd.json = json; - cwd.addr_match = TRUE; + cwd.addr_match = true; hash_iterate(pim->vxlan.sg_hash, pim_show_vxlan_sg_hash_entry, &cwd); if (uj) { @@ -8993,7 +10853,7 @@ static void pim_show_vxlan_sg_one(struct pim_instance *pim, vxlan_sg = pim_vxlan_sg_find(pim, &sg); if (vxlan_sg) { - installed = (vxlan_sg->up)?TRUE:FALSE; + installed = (vxlan_sg->up) ? true : false; iif_name = vxlan_sg->iif?vxlan_sg->iif->name:"-"; if (pim_vxlan_is_orig_mroute(vxlan_sg)) @@ -9129,8 +10989,8 @@ DEFUN_HIDDEN (no_ip_pim_mlag, struct in_addr addr; addr.s_addr = 0; - pim_vxlan_mlag_update(TRUE /*mlag_enable*/, - FALSE /*peer_state*/, PIM_VXLAN_MLAG_ROLE_SECONDARY, + pim_vxlan_mlag_update(true/*mlag_enable*/, + false/*peer_state*/, MLAG_ROLE_NONE, NULL/*peerlink*/, &addr); return CMD_SUCCESS; @@ -9170,9 +11030,9 @@ DEFUN_HIDDEN (ip_pim_mlag, idx += 2; if (!strcmp(argv[idx]->arg, "primary")) { - role = PIM_VXLAN_MLAG_ROLE_PRIMARY; + role = MLAG_ROLE_PRIMARY; } else if (!strcmp(argv[idx]->arg, "secondary")) { - role = PIM_VXLAN_MLAG_ROLE_SECONDARY; + role = MLAG_ROLE_SECONDARY; } else { vty_out(vty, "unknown MLAG role %s\n", argv[idx]->arg); return CMD_WARNING; @@ -9180,9 +11040,9 @@ DEFUN_HIDDEN (ip_pim_mlag, idx += 2; if (!strcmp(argv[idx]->arg, "up")) { - peer_state = TRUE; + peer_state = true; } else if (strcmp(argv[idx]->arg, "down")) { - peer_state = FALSE; + peer_state = false; } else { vty_out(vty, "unknown MLAG state %s\n", argv[idx]->arg); return CMD_WARNING; @@ -9196,18 +11056,17 @@ DEFUN_HIDDEN (ip_pim_mlag, errno, safe_strerror(errno)); return CMD_WARNING_CONFIG_FAILED; } - pim_vxlan_mlag_update(TRUE, peer_state, role, ifp, ®_addr); + pim_vxlan_mlag_update(true, peer_state, role, ifp, ®_addr); return CMD_SUCCESS; } void pim_cmd_init(void) { - install_node(&interface_node, - pim_interface_config_write); /* INTERFACE_NODE */ + install_node(&interface_node); /* INTERFACE_NODE */ if_cmd_init(); - install_node(&debug_node, pim_debug_config_write); + install_node(&debug_node); install_element(ENABLE_NODE, &pim_test_sg_keepalive_cmd); @@ -9238,6 +11097,8 @@ void pim_cmd_init(void) install_element(CONFIG_NODE, &no_ip_pim_spt_switchover_infinity_plist_cmd); install_element(VRF_NODE, &no_ip_pim_spt_switchover_infinity_plist_cmd); + install_element(CONFIG_NODE, &pim_register_accept_list_cmd); + install_element(VRF_NODE, &pim_register_accept_list_cmd); install_element(CONFIG_NODE, &ip_pim_joinprune_time_cmd); install_element(VRF_NODE, &ip_pim_joinprune_time_cmd); install_element(CONFIG_NODE, &no_ip_pim_joinprune_time_cmd); @@ -9276,6 +11137,10 @@ void pim_cmd_init(void) install_element(VRF_NODE, &no_ip_pim_ecmp_rebalance_cmd); install_element(CONFIG_NODE, &ip_pim_mlag_cmd); install_element(CONFIG_NODE, &no_ip_pim_mlag_cmd); + install_element(CONFIG_NODE, &igmp_group_watermark_cmd); + install_element(VRF_NODE, &igmp_group_watermark_cmd); + install_element(CONFIG_NODE, &no_igmp_group_watermark_cmd); + install_element(VRF_NODE, &no_igmp_group_watermark_cmd); install_element(INTERFACE_NODE, &interface_ip_igmp_cmd); install_element(INTERFACE_NODE, &interface_no_ip_igmp_cmd); @@ -9294,6 +11159,14 @@ void pim_cmd_init(void) &interface_ip_igmp_query_max_response_time_dsec_cmd); install_element(INTERFACE_NODE, &interface_no_ip_igmp_query_max_response_time_dsec_cmd); + install_element(INTERFACE_NODE, + &interface_ip_igmp_last_member_query_count_cmd); + install_element(INTERFACE_NODE, + &interface_no_ip_igmp_last_member_query_count_cmd); + install_element(INTERFACE_NODE, + &interface_ip_igmp_last_member_query_interval_cmd); + install_element(INTERFACE_NODE, + &interface_no_ip_igmp_last_member_query_interval_cmd); install_element(INTERFACE_NODE, &interface_ip_pim_activeactive_cmd); install_element(INTERFACE_NODE, &interface_ip_pim_ssm_cmd); install_element(INTERFACE_NODE, &interface_no_ip_pim_ssm_cmd); @@ -9307,12 +11180,11 @@ void pim_cmd_init(void) install_element(INTERFACE_NODE, &interface_no_ip_pim_hello_cmd); install_element(INTERFACE_NODE, &interface_ip_pim_boundary_oil_cmd); install_element(INTERFACE_NODE, &interface_no_ip_pim_boundary_oil_cmd); + install_element(INTERFACE_NODE, &interface_ip_igmp_query_generate_cmd); // Static mroutes NEB install_element(INTERFACE_NODE, &interface_ip_mroute_cmd); - install_element(INTERFACE_NODE, &interface_ip_mroute_source_cmd); install_element(INTERFACE_NODE, &interface_no_ip_mroute_cmd); - install_element(INTERFACE_NODE, &interface_no_ip_mroute_source_cmd); install_element(VIEW_NODE, &show_ip_igmp_interface_cmd); install_element(VIEW_NODE, &show_ip_igmp_interface_vrf_all_cmd); @@ -9333,7 +11205,11 @@ void pim_cmd_init(void) install_element(VIEW_NODE, &show_ip_pim_interface_vrf_all_cmd); install_element(VIEW_NODE, &show_ip_pim_join_cmd); install_element(VIEW_NODE, &show_ip_pim_join_vrf_all_cmd); + install_element(VIEW_NODE, &show_ip_pim_jp_agg_cmd); install_element(VIEW_NODE, &show_ip_pim_local_membership_cmd); + install_element(VIEW_NODE, &show_ip_pim_mlag_summary_cmd); + install_element(VIEW_NODE, &show_ip_pim_mlag_up_cmd); + install_element(VIEW_NODE, &show_ip_pim_mlag_up_vrf_all_cmd); install_element(VIEW_NODE, &show_ip_pim_neighbor_cmd); install_element(VIEW_NODE, &show_ip_pim_neighbor_vrf_all_cmd); install_element(VIEW_NODE, &show_ip_pim_rpf_cmd); @@ -9343,28 +11219,40 @@ void pim_cmd_init(void) install_element(VIEW_NODE, &show_ip_pim_state_vrf_all_cmd); install_element(VIEW_NODE, &show_ip_pim_upstream_cmd); install_element(VIEW_NODE, &show_ip_pim_upstream_vrf_all_cmd); + install_element(VIEW_NODE, &show_ip_pim_channel_cmd); install_element(VIEW_NODE, &show_ip_pim_upstream_join_desired_cmd); install_element(VIEW_NODE, &show_ip_pim_upstream_rpf_cmd); install_element(VIEW_NODE, &show_ip_pim_rp_cmd); install_element(VIEW_NODE, &show_ip_pim_rp_vrf_all_cmd); + install_element(VIEW_NODE, &show_ip_pim_bsr_cmd); install_element(VIEW_NODE, &show_ip_multicast_cmd); install_element(VIEW_NODE, &show_ip_multicast_vrf_all_cmd); + install_element(VIEW_NODE, &show_ip_multicast_count_cmd); + install_element(VIEW_NODE, &show_ip_multicast_count_vrf_all_cmd); install_element(VIEW_NODE, &show_ip_mroute_cmd); install_element(VIEW_NODE, &show_ip_mroute_vrf_all_cmd); install_element(VIEW_NODE, &show_ip_mroute_count_cmd); install_element(VIEW_NODE, &show_ip_mroute_count_vrf_all_cmd); + install_element(VIEW_NODE, &show_ip_mroute_summary_cmd); + install_element(VIEW_NODE, &show_ip_mroute_summary_vrf_all_cmd); install_element(VIEW_NODE, &show_ip_rib_cmd); install_element(VIEW_NODE, &show_ip_ssmpingd_cmd); - install_element(VIEW_NODE, &show_debugging_pim_cmd); install_element(VIEW_NODE, &show_ip_pim_nexthop_cmd); install_element(VIEW_NODE, &show_ip_pim_nexthop_lookup_cmd); + install_element(VIEW_NODE, &show_ip_pim_bsrp_cmd); + install_element(VIEW_NODE, &show_ip_pim_bsm_db_cmd); + install_element(VIEW_NODE, &show_ip_pim_statistics_cmd); + install_element(ENABLE_NODE, &clear_ip_mroute_count_cmd); install_element(ENABLE_NODE, &clear_ip_interfaces_cmd); install_element(ENABLE_NODE, &clear_ip_igmp_interfaces_cmd); install_element(ENABLE_NODE, &clear_ip_mroute_cmd); install_element(ENABLE_NODE, &clear_ip_pim_interfaces_cmd); install_element(ENABLE_NODE, &clear_ip_pim_interface_traffic_cmd); install_element(ENABLE_NODE, &clear_ip_pim_oil_cmd); + install_element(ENABLE_NODE, &clear_ip_pim_statistics_cmd); + + install_element(ENABLE_NODE, &show_debugging_pim_cmd); install_element(ENABLE_NODE, &debug_igmp_cmd); install_element(ENABLE_NODE, &no_debug_igmp_cmd); @@ -9402,6 +11290,8 @@ void pim_cmd_init(void) install_element(ENABLE_NODE, &no_debug_ssmpingd_cmd); install_element(ENABLE_NODE, &debug_pim_zebra_cmd); install_element(ENABLE_NODE, &no_debug_pim_zebra_cmd); + install_element(ENABLE_NODE, &debug_pim_mlag_cmd); + install_element(ENABLE_NODE, &no_debug_pim_mlag_cmd); install_element(ENABLE_NODE, &debug_pim_vxlan_cmd); install_element(ENABLE_NODE, &no_debug_pim_vxlan_cmd); install_element(ENABLE_NODE, &debug_msdp_cmd); @@ -9412,6 +11302,8 @@ void pim_cmd_init(void) install_element(ENABLE_NODE, &no_debug_msdp_packets_cmd); install_element(ENABLE_NODE, &debug_mtrace_cmd); install_element(ENABLE_NODE, &no_debug_mtrace_cmd); + install_element(ENABLE_NODE, &debug_bsm_cmd); + install_element(ENABLE_NODE, &no_debug_bsm_cmd); install_element(CONFIG_NODE, &debug_igmp_cmd); install_element(CONFIG_NODE, &no_debug_igmp_cmd); @@ -9445,6 +11337,8 @@ void pim_cmd_init(void) install_element(CONFIG_NODE, &no_debug_ssmpingd_cmd); install_element(CONFIG_NODE, &debug_pim_zebra_cmd); install_element(CONFIG_NODE, &no_debug_pim_zebra_cmd); + install_element(CONFIG_NODE, &debug_pim_mlag_cmd); + install_element(CONFIG_NODE, &no_debug_pim_mlag_cmd); install_element(CONFIG_NODE, &debug_pim_vxlan_cmd); install_element(CONFIG_NODE, &no_debug_pim_vxlan_cmd); install_element(CONFIG_NODE, &debug_msdp_cmd); @@ -9455,6 +11349,8 @@ void pim_cmd_init(void) install_element(CONFIG_NODE, &no_debug_msdp_packets_cmd); install_element(CONFIG_NODE, &debug_mtrace_cmd); install_element(CONFIG_NODE, &no_debug_mtrace_cmd); + install_element(CONFIG_NODE, &debug_bsm_cmd); + install_element(CONFIG_NODE, &no_debug_bsm_cmd); install_element(CONFIG_NODE, &ip_msdp_mesh_group_member_cmd); install_element(VRF_NODE, &ip_msdp_mesh_group_member_cmd); @@ -9464,6 +11360,8 @@ void pim_cmd_init(void) install_element(VRF_NODE, &ip_msdp_mesh_group_source_cmd); install_element(CONFIG_NODE, &no_ip_msdp_mesh_group_source_cmd); install_element(VRF_NODE, &no_ip_msdp_mesh_group_source_cmd); + install_element(CONFIG_NODE, &no_ip_msdp_mesh_group_cmd); + install_element(VRF_NODE, &no_ip_msdp_mesh_group_cmd); install_element(VIEW_NODE, &show_ip_msdp_peer_detail_cmd); install_element(VIEW_NODE, &show_ip_msdp_peer_detail_vrf_all_cmd); install_element(VIEW_NODE, &show_ip_msdp_sa_detail_cmd); @@ -9478,6 +11376,11 @@ void pim_cmd_init(void) install_element(VIEW_NODE, &show_ip_pim_vxlan_sg_work_cmd); install_element(INTERFACE_NODE, &interface_pim_use_source_cmd); install_element(INTERFACE_NODE, &interface_no_pim_use_source_cmd); + /* Install BSM command */ + install_element(INTERFACE_NODE, &ip_pim_bsm_cmd); + install_element(INTERFACE_NODE, &no_ip_pim_bsm_cmd); + install_element(INTERFACE_NODE, &ip_pim_ucast_bsm_cmd); + install_element(INTERFACE_NODE, &no_ip_pim_ucast_bsm_cmd); /* Install BFD command */ install_element(INTERFACE_NODE, &ip_pim_bfd_cmd); install_element(INTERFACE_NODE, &ip_pim_bfd_param_cmd); diff --git a/pimd/pim_cmd.h b/pimd/pim_cmd.h index 67d6e43c34..89a4e6e699 100644 --- a/pimd/pim_cmd.h +++ b/pimd/pim_cmd.h @@ -35,6 +35,8 @@ #define IFACE_IGMP_QUERY_INTERVAL_STR "IGMP host query interval\n" #define IFACE_IGMP_QUERY_MAX_RESPONSE_TIME_STR "IGMP max query response value (seconds)\n" #define IFACE_IGMP_QUERY_MAX_RESPONSE_TIME_DSEC_STR "IGMP max query response value (deciseconds)\n" +#define IFACE_IGMP_LAST_MEMBER_QUERY_INTERVAL_STR "IGMP last member query interval\n" +#define IFACE_IGMP_LAST_MEMBER_QUERY_COUNT_STR "IGMP last member query count\n" #define DEBUG_IGMP_STR "IGMP protocol activity\n" #define DEBUG_IGMP_EVENTS_STR "IGMP protocol events\n" #define DEBUG_IGMP_PACKETS_STR "IGMP protocol packets\n" @@ -52,6 +54,7 @@ #define DEBUG_PIM_PACKETDUMP_RECV_STR "Dump received packets\n" #define DEBUG_PIM_TRACE_STR "PIM internal daemon activity\n" #define DEBUG_PIM_ZEBRA_STR "ZEBRA protocol activity\n" +#define DEBUG_PIM_MLAG_STR "PIM Mlag activity\n" #define DEBUG_PIM_VXLAN_STR "PIM VxLAN events\n" #define DEBUG_SSMPINGD_STR "ssmpingd activity\n" #define CLEAR_IP_IGMP_STR "IGMP clear commands\n" @@ -65,6 +68,8 @@ #define DEBUG_MSDP_INTERNAL_STR "MSDP protocol internal\n" #define DEBUG_MSDP_PACKETS_STR "MSDP protocol packets\n" #define DEBUG_MTRACE_STR "Mtrace protocol activity\n" +#define DEBUG_PIM_BSM_STR "BSR message processing activity\n" + void pim_cmd_init(void); diff --git a/pimd/pim_hello.c b/pimd/pim_hello.c index e482d321a4..e50504ec10 100644 --- a/pimd/pim_hello.c +++ b/pimd/pim_hello.c @@ -31,6 +31,7 @@ #include "pim_iface.h" #include "pim_neighbor.h" #include "pim_upstream.h" +#include "pim_bsm.h" static void on_trace(const char *label, struct interface *ifp, struct in_addr src) @@ -103,7 +104,7 @@ static void tlv_trace(const char *label, const char *tlv_name, char src_str[INET_ADDRSTRLEN]; pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); zlog_debug("%s: PIM hello option from %s on interface %s: %s", - label, + label, src_str, ifname, tlv_name); } @@ -153,7 +154,7 @@ int pim_hello_recv(struct interface *ifp, struct in_addr src_addr, struct list *hello_option_addr_list = 0; if (PIM_DEBUG_PIM_HELLO) - on_trace(__PRETTY_FUNCTION__, ifp, src_addr); + on_trace(__func__, ifp, src_addr); pim_ifp = ifp->info; zassert(pim_ifp); @@ -179,8 +180,8 @@ int pim_hello_recv(struct interface *ifp, struct in_addr src_addr, sizeof(src_str)); zlog_debug( "%s: short PIM hello TLV size=%d < min=%d from %s on interface %s", - __PRETTY_FUNCTION__, remain, - PIM_TLV_MIN_SIZE, src_str, ifp->name); + __func__, remain, PIM_TLV_MIN_SIZE, + src_str, ifp->name); } FREE_ADDR_LIST_THEN_RETURN(-1); } @@ -197,9 +198,9 @@ int pim_hello_recv(struct interface *ifp, struct in_addr src_addr, sizeof(src_str)); zlog_debug( "%s: long PIM hello TLV type=%d length=%d > left=%td from %s on interface %s", - __PRETTY_FUNCTION__, option_type, - option_len, tlv_pastend - tlv_curr, - src_str, ifp->name); + __func__, option_type, option_len, + tlv_pastend - tlv_curr, src_str, + ifp->name); } FREE_ADDR_LIST_THEN_RETURN(-2); } @@ -210,8 +211,8 @@ int pim_hello_recv(struct interface *ifp, struct in_addr src_addr, sizeof(src_str)); zlog_debug( "%s: parse left_size=%d: PIM hello TLV type=%d length=%d from %s on %s", - __PRETTY_FUNCTION__, remain, option_type, - option_len, src_str, ifp->name); + __func__, remain, option_type, option_len, + src_str, ifp->name); } switch (option_type) { @@ -263,8 +264,8 @@ int pim_hello_recv(struct interface *ifp, struct in_addr src_addr, sizeof(src_str)); zlog_debug( "%s: ignoring PIM hello dense-mode state refresh TLV option type=%d length=%d from %s on interface %s", - __PRETTY_FUNCTION__, option_type, - option_len, src_str, ifp->name); + __func__, option_type, option_len, + src_str, ifp->name); } break; default: @@ -274,8 +275,8 @@ int pim_hello_recv(struct interface *ifp, struct in_addr src_addr, sizeof(src_str)); zlog_debug( "%s: ignoring unknown PIM hello TLV type=%d length=%d from %s on interface %s", - __PRETTY_FUNCTION__, option_type, - option_len, src_str, ifp->name); + __func__, option_type, option_len, + src_str, ifp->name); } } @@ -287,44 +288,38 @@ int pim_hello_recv(struct interface *ifp, struct in_addr src_addr, */ if (PIM_DEBUG_PIM_HELLO) { - tlv_trace_uint16(__PRETTY_FUNCTION__, "holdtime", ifp->name, - src_addr, + tlv_trace_uint16(__func__, "holdtime", ifp->name, src_addr, PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_HOLDTIME), hello_option_holdtime); tlv_trace_uint16( - __PRETTY_FUNCTION__, "propagation_delay", ifp->name, - src_addr, + __func__, "propagation_delay", ifp->name, src_addr, PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY), hello_option_propagation_delay); tlv_trace_uint16( - __PRETTY_FUNCTION__, "override_interval", ifp->name, - src_addr, + __func__, "override_interval", ifp->name, src_addr, PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY), hello_option_override_interval); tlv_trace_bool( - __PRETTY_FUNCTION__, "can_disable_join_suppression", - ifp->name, src_addr, + __func__, "can_disable_join_suppression", ifp->name, + src_addr, PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY), PIM_OPTION_IS_SET( hello_options, PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION)); - tlv_trace_uint32(__PRETTY_FUNCTION__, "dr_priority", ifp->name, - src_addr, + tlv_trace_uint32(__func__, "dr_priority", ifp->name, src_addr, PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_DR_PRIORITY), hello_option_dr_priority); tlv_trace_uint32_hex( - __PRETTY_FUNCTION__, "generation_id", ifp->name, - src_addr, + __func__, "generation_id", ifp->name, src_addr, PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_GENERATION_ID), hello_option_generation_id); - tlv_trace_list(__PRETTY_FUNCTION__, "address_list", ifp->name, - src_addr, + tlv_trace_list(__func__, "address_list", ifp->name, src_addr, PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_ADDRESS_LIST), hello_option_addr_list); @@ -337,7 +332,7 @@ int pim_hello_recv(struct interface *ifp, struct in_addr src_addr, sizeof(src_str)); zlog_debug( "%s: PIM hello missing holdtime from %s on interface %s", - __PRETTY_FUNCTION__, src_str, ifp->name); + __func__, src_str, ifp->name); } } @@ -362,11 +357,17 @@ int pim_hello_recv(struct interface *ifp, struct in_addr src_addr, sizeof(src_str)); zlog_warn( "%s: failure creating PIM neighbor %s on interface %s", - __PRETTY_FUNCTION__, src_str, - ifp->name); + __func__, src_str, ifp->name); } FREE_ADDR_LIST_THEN_RETURN(-8); } + /* Forward BSM if required */ + if (!pim_bsm_new_nbr_fwd(neigh, ifp)) { + if (PIM_DEBUG_PIM_HELLO) + zlog_debug( + "%s: forwarding bsm to new nbr failed", + __func__); + } /* actual addr list has been saved under neighbor */ return 0; @@ -389,8 +390,7 @@ int pim_hello_recv(struct interface *ifp, struct in_addr src_addr, sizeof(src_str)); zlog_debug( "%s: GenId mismatch new=%08x old=%08x: replacing neighbor %s on %s", - __PRETTY_FUNCTION__, - hello_option_generation_id, + __func__, hello_option_generation_id, neigh->generation_id, src_str, ifp->name); } @@ -415,11 +415,17 @@ int pim_hello_recv(struct interface *ifp, struct in_addr src_addr, sizeof(src_str)); zlog_debug( "%s: failure re-creating PIM neighbor %s on interface %s", - __PRETTY_FUNCTION__, src_str, - ifp->name); + __func__, src_str, ifp->name); } FREE_ADDR_LIST_THEN_RETURN(-9); } + /* Forward BSM if required */ + if (!pim_bsm_new_nbr_fwd(neigh, ifp)) { + if (PIM_DEBUG_PIM_HELLO) + zlog_debug( + "%s: forwarding bsm to new nbr failed", + __func__); + } /* actual addr list is saved under neighbor */ return 0; @@ -460,7 +466,7 @@ int pim_hello_build_tlv(struct interface *ifp, uint8_t *tlv_buf, if (PIM_DEBUG_PIM_HELLO) { zlog_debug( "%s: could not set PIM hello Holdtime option for interface %s", - __PRETTY_FUNCTION__, ifp->name); + __func__, ifp->name); } return -1; } @@ -473,12 +479,12 @@ int pim_hello_build_tlv(struct interface *ifp, uint8_t *tlv_buf, if (PIM_DEBUG_PIM_HELLO) { zlog_debug( "%s: could not set PIM LAN Prune Delay option for interface %s", - __PRETTY_FUNCTION__, ifp->name); + __func__, ifp->name); } return -1; } if (can_disable_join_suppression) { - *((uint8_t *)(curr) + 4) |= 0x80; /* enable T bit */ + *(curr + 4) |= 0x80; /* enable T bit */ } curr = tmp; @@ -489,7 +495,7 @@ int pim_hello_build_tlv(struct interface *ifp, uint8_t *tlv_buf, if (PIM_DEBUG_PIM_HELLO) { zlog_debug( "%s: could not set PIM hello DR Priority option for interface %s", - __PRETTY_FUNCTION__, ifp->name); + __func__, ifp->name); } return -2; } @@ -502,7 +508,7 @@ int pim_hello_build_tlv(struct interface *ifp, uint8_t *tlv_buf, if (PIM_DEBUG_PIM_HELLO) { zlog_debug( "%s: could not set PIM hello Generation ID option for interface %s", - __PRETTY_FUNCTION__, ifp->name); + __func__, ifp->name); } return -3; } @@ -515,7 +521,7 @@ int pim_hello_build_tlv(struct interface *ifp, uint8_t *tlv_buf, if (PIM_DEBUG_PIM_HELLO) { zlog_debug( "%s: could not set PIM hello v4 Secondary Address List option for interface %s", - __PRETTY_FUNCTION__, ifp->name); + __func__, ifp->name); } return -4; } @@ -526,7 +532,7 @@ int pim_hello_build_tlv(struct interface *ifp, uint8_t *tlv_buf, if (PIM_DEBUG_PIM_HELLO) { zlog_debug( "%s: could not sent PIM hello v6 secondary Address List option for interface %s", - __PRETTY_FUNCTION__, ifp->name); + __func__, ifp->name); } return -4; } diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c index 0fb7f176ce..0caa360df9 100644 --- a/pimd/pim_iface.c +++ b/pimd/pim_iface.c @@ -28,6 +28,7 @@ #include "plist.h" #include "hash.h" #include "ferr.h" +#include "network.h" #include "pimd.h" #include "pim_instance.h" @@ -131,6 +132,13 @@ struct pim_interface *pim_if_new(struct interface *ifp, bool igmp, bool pim, IGMP_QUERY_MAX_RESPONSE_TIME_DSEC; pim_ifp->igmp_specific_query_max_response_time_dsec = IGMP_SPECIFIC_QUERY_MAX_RESPONSE_TIME_DSEC; + pim_ifp->igmp_last_member_query_count = + IGMP_DEFAULT_ROBUSTNESS_VARIABLE; + + /* BSM config on interface: true by default */ + pim_ifp->bsm_enable = true; + pim_ifp->ucast_bsm_accept = true; + pim_ifp->am_i_dr = false; /* RFC 3376: 8.3. Query Response Interval @@ -180,6 +188,7 @@ struct pim_interface *pim_if_new(struct interface *ifp, bool igmp, bool pim, pim_sock_reset(ifp); pim_if_add_vif(ifp, ispimreg, is_vxlan_term); + pim_ifp->pim->mcast_if_count++; return pim_ifp; } @@ -203,6 +212,7 @@ void pim_if_delete(struct interface *ifp) pim_neighbor_delete_all(ifp, "Interface removed from configuration"); pim_if_del_vif(ifp); + pim_ifp->pim->mcast_if_count--; list_delete(&pim_ifp->igmp_socket_list); list_delete(&pim_ifp->pim_neighbor_list); @@ -269,7 +279,7 @@ static void pim_addr_change(struct interface *ifp) 1) Before an interface goes down or changes primary IP address, a Hello message with a zero HoldTime should be sent immediately (with the old IP address if the IP address changed). - -- FIXME See CAVEAT C13 + -- Done at the caller of the function as new ip already updated here 2) After an interface has changed its IP address, it MUST send a Hello message with its new IP address. @@ -308,12 +318,16 @@ static int detect_primary_address_change(struct interface *ifp, sizeof(new_prim_str)); pim_inet4_dump("", pim_ifp->primary_address, old_prim_str, sizeof(old_prim_str)); - zlog_debug("%s: old=%s new=%s on interface %s: %s", - __PRETTY_FUNCTION__, old_prim_str, new_prim_str, - ifp->name, changed ? "changed" : "unchanged"); + zlog_debug("%s: old=%s new=%s on interface %s: %s", __func__, + old_prim_str, new_prim_str, ifp->name, + changed ? "changed" : "unchanged"); } if (changed) { + /* Before updating pim_ifp send Hello time with 0 hold time */ + if (PIM_IF_TEST_PIM(pim_ifp->options)) { + pim_hello_send(ifp, 0 /* zero-sec holdtime */); + } pim_ifp->primary_address = new_prim_addr; } @@ -327,7 +341,7 @@ pim_sec_addr_find(struct pim_interface *pim_ifp, struct prefix *addr) struct listnode *node; for (ALL_LIST_ELEMENTS_RO(pim_ifp->sec_addr_list, node, sec_addr)) { - if (prefix_cmp(&sec_addr->addr, addr)) { + if (prefix_cmp(&sec_addr->addr, addr) == 0) { return sec_addr; } } @@ -481,8 +495,7 @@ int pim_update_source_set(struct interface *ifp, struct in_addr source) } pim_ifp->update_source = source; - detect_address_change(ifp, 0 /* force_prim_as_any */, - __PRETTY_FUNCTION__); + detect_address_change(ifp, 0 /* force_prim_as_any */, __func__); return PIM_SUCCESS; } @@ -492,6 +505,7 @@ void pim_if_addr_add(struct connected *ifc) struct pim_interface *pim_ifp; struct interface *ifp; struct in_addr ifaddr; + bool vxlan_term; zassert(ifc); @@ -508,7 +522,7 @@ void pim_if_addr_add(struct connected *ifc) char buf[BUFSIZ]; prefix2str(ifc->address, buf, BUFSIZ); zlog_debug("%s: %s ifindex=%d connected IP address %s %s", - __PRETTY_FUNCTION__, ifp->name, ifp->ifindex, buf, + __func__, ifp->name, ifp->ifindex, buf, CHECK_FLAG(ifc->flags, ZEBRA_IFA_SECONDARY) ? "secondary" : "primary"); @@ -516,7 +530,7 @@ void pim_if_addr_add(struct connected *ifc) ifaddr = ifc->address->u.prefix4; - detect_address_change(ifp, 0, __PRETTY_FUNCTION__); + detect_address_change(ifp, 0, __func__); // if (ifc->address->family != AF_INET) // return; @@ -564,8 +578,8 @@ void pim_if_addr_add(struct connected *ifc) source_str, sizeof(source_str)); zlog_warn( "%s: igmp_join_sock() failure for IGMP group %s source %s on interface %s", - __PRETTY_FUNCTION__, group_str, - source_str, ifp->name); + __func__, group_str, source_str, + ifp->name); /* warning only */ } else ij->sock_fd = join_fd; @@ -629,7 +643,8 @@ void pim_if_addr_add(struct connected *ifc) address assigned, then try to create a vif_index. */ if (pim_ifp->mroute_vif_index < 0) { - pim_if_add_vif(ifp, false, false /*vxlan_term*/); + vxlan_term = pim_vxlan_is_term_dev_cfg(pim_ifp->pim, ifp); + pim_if_add_vif(ifp, false, vxlan_term); } pim_ifchannel_scan_forward_start(ifp); } @@ -704,13 +719,13 @@ void pim_if_addr_del(struct connected *ifc, int force_prim_as_any) char buf[BUFSIZ]; prefix2str(ifc->address, buf, BUFSIZ); zlog_debug("%s: %s ifindex=%d disconnected IP address %s %s", - __PRETTY_FUNCTION__, ifp->name, ifp->ifindex, buf, + __func__, ifp->name, ifp->ifindex, buf, CHECK_FLAG(ifc->flags, ZEBRA_IFA_SECONDARY) ? "secondary" : "primary"); } - detect_address_change(ifp, force_prim_as_any, __PRETTY_FUNCTION__); + detect_address_change(ifp, force_prim_as_any, __func__); pim_if_addr_del_igmp(ifc); pim_if_addr_del_pim(ifc); @@ -724,6 +739,7 @@ void pim_if_addr_add_all(struct interface *ifp) int v4_addrs = 0; int v6_addrs = 0; struct pim_interface *pim_ifp = ifp->info; + bool vxlan_term; /* PIM/IGMP enabled ? */ @@ -762,7 +778,8 @@ void pim_if_addr_add_all(struct interface *ifp) * address assigned, then try to create a vif_index. */ if (pim_ifp->mroute_vif_index < 0) { - pim_if_add_vif(ifp, false, false /*vxlan_term*/); + vxlan_term = pim_vxlan_is_term_dev_cfg(pim_ifp->pim, ifp); + pim_if_add_vif(ifp, false, vxlan_term); } pim_ifchannel_scan_forward_start(ifp); @@ -867,7 +884,7 @@ struct in_addr pim_find_primary_addr(struct interface *ifp) if (PIM_INADDR_IS_ANY(p->u.prefix4)) { zlog_warn( "%s: null IPv4 address connected to interface %s", - __PRETTY_FUNCTION__, ifp->name); + __func__, ifp->name); continue; } @@ -885,15 +902,16 @@ struct in_addr pim_find_primary_addr(struct interface *ifp) * So let's grab the loopbacks v4 address * and use that as the primary address */ - if (!v4_addrs && v6_addrs && !if_is_loopback(ifp)) { + if (!v4_addrs && v6_addrs) { struct interface *lo_ifp; + // DBS - Come back and check here if (ifp->vrf_id == VRF_DEFAULT) lo_ifp = if_lookup_by_name("lo", vrf->vrf_id); else lo_ifp = if_lookup_by_name(vrf->name, vrf->vrf_id); - if (lo_ifp) + if (lo_ifp && (lo_ifp != ifp)) return pim_find_primary_addr(lo_ifp); } @@ -937,14 +955,14 @@ int pim_if_add_vif(struct interface *ifp, bool ispimreg, bool is_vxlan_term) if (pim_ifp->mroute_vif_index > 0) { zlog_warn("%s: vif_index=%d > 0 on interface %s ifindex=%d", - __PRETTY_FUNCTION__, pim_ifp->mroute_vif_index, - ifp->name, ifp->ifindex); + __func__, pim_ifp->mroute_vif_index, ifp->name, + ifp->ifindex); return -1; } if (ifp->ifindex < 0) { - zlog_warn("%s: ifindex=%d < 1 on interface %s", - __PRETTY_FUNCTION__, ifp->ifindex, ifp->name); + zlog_warn("%s: ifindex=%d < 1 on interface %s", __func__, + ifp->ifindex, ifp->name); return -2; } @@ -952,7 +970,7 @@ int pim_if_add_vif(struct interface *ifp, bool ispimreg, bool is_vxlan_term) if (!ispimreg && !is_vxlan_term && PIM_INADDR_IS_ANY(ifaddr)) { zlog_warn( "%s: could not get address for interface %s ifindex=%d", - __PRETTY_FUNCTION__, ifp->name, ifp->ifindex); + __func__, ifp->name, ifp->ifindex); return -4; } @@ -961,7 +979,7 @@ int pim_if_add_vif(struct interface *ifp, bool ispimreg, bool is_vxlan_term) if (pim_ifp->mroute_vif_index >= MAXVIFS) { zlog_warn( "%s: Attempting to configure more than MAXVIFS=%d on pim enabled interface %s", - __PRETTY_FUNCTION__, MAXVIFS, ifp->name); + __func__, MAXVIFS, ifp->name); return -3; } @@ -991,8 +1009,8 @@ int pim_if_del_vif(struct interface *ifp) if (pim_ifp->mroute_vif_index < 1) { zlog_warn("%s: vif_index=%d < 1 on interface %s ifindex=%d", - __PRETTY_FUNCTION__, pim_ifp->mroute_vif_index, - ifp->name, ifp->ifindex); + __func__, pim_ifp->mroute_vif_index, ifp->name, + ifp->ifindex); return -1; } @@ -1087,7 +1105,8 @@ int pim_if_t_override_msec(struct interface *ifp) effective_override_interval_msec = pim_if_effective_override_interval_msec(ifp); - t_override_msec = random() % (effective_override_interval_msec + 1); + t_override_msec = + frr_weak_random() % (effective_override_interval_msec + 1); return t_override_msec; } @@ -1119,8 +1138,8 @@ struct pim_neighbor *pim_if_find_neighbor(struct interface *ifp, pim_ifp = ifp->info; if (!pim_ifp) { - zlog_warn("%s: multicast not enabled on interface %s", - __PRETTY_FUNCTION__, ifp->name); + zlog_warn("%s: multicast not enabled on interface %s", __func__, + ifp->name); return 0; } @@ -1145,7 +1164,7 @@ struct pim_neighbor *pim_if_find_neighbor(struct interface *ifp, pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); zlog_debug( "%s: neighbor not found for address %s on interface %s", - __PRETTY_FUNCTION__, addr_str, ifp->name); + __func__, addr_str, ifp->name); } return NULL; @@ -1165,7 +1184,7 @@ long pim_if_t_suppressed_msec(struct interface *ifp) return 0; /* t_suppressed = t_periodic * rand(1.1, 1.4) */ - ramount = 1100 + (random() % (1400 - 1100 + 1)); + ramount = 1100 + (frr_weak_random() % (1400 - 1100 + 1)); t_suppressed_msec = router->t_periodic * ramount; return t_suppressed_msec; @@ -1213,8 +1232,8 @@ static int igmp_join_sock(const char *ifname, ifindex_t ifindex, sizeof(source_str)); zlog_warn( "%s: setsockopt(fd=%d) failure for IGMP group %s source %s ifindex %d on interface %s: errno=%d: %s", - __PRETTY_FUNCTION__, join_fd, group_str, source_str, - ifindex, ifname, errno, safe_strerror(errno)); + __func__, join_fd, group_str, source_str, ifindex, + ifname, errno, safe_strerror(errno)); close(join_fd); return -2; @@ -1246,7 +1265,7 @@ static struct igmp_join *igmp_join_new(struct interface *ifp, sizeof(source_str)); zlog_warn( "%s: igmp_join_sock() failure for IGMP group %s source %s on interface %s", - __PRETTY_FUNCTION__, group_str, source_str, ifp->name); + __func__, group_str, source_str, ifp->name); return 0; } @@ -1287,11 +1306,7 @@ ferr_r pim_if_igmp_join_add(struct interface *ifp, struct in_addr group_addr, return ferr_ok(); } - ij = igmp_join_new(ifp, group_addr, source_addr); - if (!ij) { - return ferr_cfg_invalid( - "Failure to create new join data structure, see log file for more information"); - } + (void)igmp_join_new(ifp, group_addr, source_addr); if (PIM_DEBUG_IGMP_EVENTS) { char group_str[INET_ADDRSTRLEN]; @@ -1302,7 +1317,7 @@ ferr_r pim_if_igmp_join_add(struct interface *ifp, struct in_addr group_addr, sizeof(source_str)); zlog_debug( "%s: issued static igmp join for channel (S,G)=(%s,%s) on interface %s", - __PRETTY_FUNCTION__, source_str, group_str, ifp->name); + __func__, source_str, group_str, ifp->name); } return ferr_ok(); @@ -1317,14 +1332,14 @@ int pim_if_igmp_join_del(struct interface *ifp, struct in_addr group_addr, pim_ifp = ifp->info; if (!pim_ifp) { - zlog_warn("%s: multicast not enabled on interface %s", - __PRETTY_FUNCTION__, ifp->name); + zlog_warn("%s: multicast not enabled on interface %s", __func__, + ifp->name); return -1; } if (!pim_ifp->igmp_join_list) { - zlog_warn("%s: no IGMP join on interface %s", - __PRETTY_FUNCTION__, ifp->name); + zlog_warn("%s: no IGMP join on interface %s", __func__, + ifp->name); return -2; } @@ -1338,7 +1353,7 @@ int pim_if_igmp_join_del(struct interface *ifp, struct in_addr group_addr, sizeof(source_str)); zlog_warn( "%s: could not find IGMP group %s source %s on interface %s", - __PRETTY_FUNCTION__, group_str, source_str, ifp->name); + __func__, group_str, source_str, ifp->name); return -3; } @@ -1351,8 +1366,8 @@ int pim_if_igmp_join_del(struct interface *ifp, struct in_addr group_addr, sizeof(source_str)); zlog_warn( "%s: failure closing sock_fd=%d for IGMP group %s source %s on interface %s: errno=%d: %s", - __PRETTY_FUNCTION__, ij->sock_fd, group_str, source_str, - ifp->name, errno, safe_strerror(errno)); + __func__, ij->sock_fd, group_str, source_str, ifp->name, + errno, safe_strerror(errno)); /* warning only */ } listnode_delete(pim_ifp->igmp_join_list, ij); @@ -1374,8 +1389,8 @@ static void pim_if_igmp_join_del_all(struct interface *ifp) pim_ifp = ifp->info; if (!pim_ifp) { - zlog_warn("%s: multicast not enabled on interface %s", - __PRETTY_FUNCTION__, ifp->name); + zlog_warn("%s: multicast not enabled on interface %s", __func__, + ifp->name); return; } @@ -1474,22 +1489,29 @@ void pim_if_create_pimreg(struct pim_instance *pim) snprintf(pimreg_name, sizeof(pimreg_name), "pimreg%u", pim->vrf->data.l.table_id); - pim->regiface = if_create(pimreg_name, pim->vrf_id); + pim->regiface = if_create_name(pimreg_name, pim->vrf_id); pim->regiface->ifindex = PIM_OIF_PIM_REGISTER_VIF; pim_if_new(pim->regiface, false, false, true, false /*vxlan_term*/); + /* + * On vrf moves we delete the interface if there + * is nothing going on with it. We cannot have + * the pimregiface deleted. + */ + pim->regiface->configured = true; + } } -int pim_if_connected_to_source(struct interface *ifp, struct in_addr src) +struct prefix *pim_if_connected_to_source(struct interface *ifp, struct in_addr src) { struct listnode *cnode; struct connected *c; struct prefix p; if (!ifp) - return 0; + return NULL; p.family = AF_INET; p.u.prefix4 = src; @@ -1498,11 +1520,11 @@ int pim_if_connected_to_source(struct interface *ifp, struct in_addr src) for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, c)) { if ((c->address->family == AF_INET) && prefix_match(CONNECTED_PREFIX(c), &p)) { - return 1; + return CONNECTED_PREFIX(c); } } - return 0; + return NULL; } bool pim_if_is_vrf_device(struct interface *ifp) @@ -1524,3 +1546,173 @@ int pim_if_ifchannel_count(struct pim_interface *pim_ifp) return count; } + +int pim_ifp_create(struct interface *ifp) +{ + struct pim_instance *pim; + + pim = pim_get_pim_instance(ifp->vrf_id); + if (PIM_DEBUG_ZEBRA) { + zlog_debug( + "%s: %s index %d(%u) flags %ld metric %d mtu %d operative %d", + __func__, ifp->name, ifp->ifindex, ifp->vrf_id, + (long)ifp->flags, ifp->metric, ifp->mtu, + if_is_operative(ifp)); + } + + if (if_is_operative(ifp)) { + struct pim_interface *pim_ifp; + + pim_ifp = ifp->info; + /* + * If we have a pim_ifp already and this is an if_add + * that means that we probably have a vrf move event + * If that is the case, set the proper vrfness. + */ + if (pim_ifp) + pim_ifp->pim = pim; + pim_if_addr_add_all(ifp); + } + + /* + * If we are a vrf device that is up, open up the pim_socket for + * listening + * to incoming pim messages irrelevant if the user has configured us + * for pim or not. + */ + if (pim_if_is_vrf_device(ifp)) { + struct pim_interface *pim_ifp; + + if (!ifp->info) { + pim_ifp = pim_if_new(ifp, false, false, false, + false /*vxlan_term*/); + ifp->info = pim_ifp; + } + + pim_sock_add(ifp); + } + + if (!strncmp(ifp->name, PIM_VXLAN_TERM_DEV_NAME, + sizeof(PIM_VXLAN_TERM_DEV_NAME))) { + if (pim->mcast_if_count < MAXVIFS) + pim_vxlan_add_term_dev(pim, ifp); + else + zlog_warn( + "%s: Cannot enable pim on %s. MAXVIFS(%d) reached. Deleting and readding the vxlan termimation device after unconfiguring pim from other interfaces may succeed.", + __func__, ifp->name, MAXVIFS); + } + + return 0; +} + +int pim_ifp_up(struct interface *ifp) +{ + struct pim_interface *pim_ifp; + struct pim_instance *pim; + uint32_t table_id; + + if (PIM_DEBUG_ZEBRA) { + zlog_debug( + "%s: %s index %d(%u) flags %ld metric %d mtu %d operative %d", + __func__, ifp->name, ifp->ifindex, ifp->vrf_id, + (long)ifp->flags, ifp->metric, ifp->mtu, + if_is_operative(ifp)); + } + + pim = pim_get_pim_instance(ifp->vrf_id); + + pim_ifp = ifp->info; + /* + * If we have a pim_ifp already and this is an if_add + * that means that we probably have a vrf move event + * If that is the case, set the proper vrfness. + */ + if (pim_ifp) + pim_ifp->pim = pim; + + /* + pim_if_addr_add_all() suffices for bringing up both IGMP and + PIM + */ + pim_if_addr_add_all(ifp); + + /* + * If we have a pimreg device callback and it's for a specific + * table set the master appropriately + */ + if (sscanf(ifp->name, "pimreg%" SCNu32, &table_id) == 1) { + struct vrf *vrf; + RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { + if ((table_id == vrf->data.l.table_id) + && (ifp->vrf_id != vrf->vrf_id)) { + struct interface *master = if_lookup_by_name( + vrf->name, vrf->vrf_id); + + if (!master) { + zlog_debug( + "%s: Unable to find Master interface for %s", + __func__, vrf->name); + return 0; + } + pim_zebra_interface_set_master(master, ifp); + } + } + } + return 0; +} + +int pim_ifp_down(struct interface *ifp) +{ + if (PIM_DEBUG_ZEBRA) { + zlog_debug( + "%s: %s index %d(%u) flags %ld metric %d mtu %d operative %d", + __func__, ifp->name, ifp->ifindex, ifp->vrf_id, + (long)ifp->flags, ifp->metric, ifp->mtu, + if_is_operative(ifp)); + } + + if (!if_is_operative(ifp)) { + pim_ifchannel_delete_all(ifp); + /* + pim_if_addr_del_all() suffices for shutting down IGMP, + but not for shutting down PIM + */ + pim_if_addr_del_all(ifp); + + /* + pim_sock_delete() closes the socket, stops read and timer + threads, + and kills all neighbors. + */ + if (ifp->info) { + pim_sock_delete(ifp, "link down"); + } + } + + if (ifp->info) + pim_if_del_vif(ifp); + + return 0; +} + +int pim_ifp_destroy(struct interface *ifp) +{ + struct pim_instance *pim; + + if (PIM_DEBUG_ZEBRA) { + zlog_debug( + "%s: %s index %d(%u) flags %ld metric %d mtu %d operative %d", + __func__, ifp->name, ifp->ifindex, ifp->vrf_id, + (long)ifp->flags, ifp->metric, ifp->mtu, + if_is_operative(ifp)); + } + + if (!if_is_operative(ifp)) + pim_if_addr_del_all(ifp); + + pim = pim_get_pim_instance(ifp->vrf_id); + if (pim && pim->vxlan.term_if == ifp) + pim_vxlan_del_term_dev(pim); + + return 0; +} diff --git a/pimd/pim_iface.h b/pimd/pim_iface.h index fe96c07758..8decfef74d 100644 --- a/pimd/pim_iface.h +++ b/pimd/pim_iface.h @@ -55,6 +55,7 @@ #define PIM_IF_DONT_PIM_CAN_DISABLE_JOIN_SUPRESSION(options) ((options) &= ~PIM_IF_MASK_PIM_CAN_DISABLE_JOIN_SUPRESSION) #define PIM_I_am_DR(pim_ifp) (pim_ifp)->pim_dr_addr.s_addr == (pim_ifp)->primary_address.s_addr +#define PIM_I_am_DualActive(pim_ifp) (pim_ifp)->activeactive == true struct pim_iface_upstream_switch { struct in_addr address; @@ -88,8 +89,14 @@ struct pim_interface { int igmp_query_max_response_time_dsec; /* IGMPv3 Max Response Time in dsecs for general queries */ int igmp_specific_query_max_response_time_dsec; /* IGMPv3 Max Response - Time in dsecs for - specific queries */ + Time in dsecs called + as last member query + interval, defines the + maximum response time + advertised in IGMP + group-specific + queries */ + int igmp_last_member_query_count; /* IGMP last member query count */ struct list *igmp_socket_list; /* list of struct igmp_sock */ struct list *igmp_join_list; /* list of struct igmp_join */ @@ -126,8 +133,11 @@ struct pim_interface { /* Turn on Active-Active for this interface */ bool activeactive; + bool am_i_dr; int64_t pim_ifstat_start; /* start timestamp for stats */ + uint64_t pim_ifstat_bsm_rx; + uint64_t pim_ifstat_bsm_tx; uint32_t pim_ifstat_hello_sent; uint32_t pim_ifstat_hello_sendfail; uint32_t pim_ifstat_hello_recv; @@ -142,7 +152,12 @@ struct pim_interface { uint32_t pim_ifstat_reg_stop_send; uint32_t pim_ifstat_assert_recv; uint32_t pim_ifstat_assert_send; + uint32_t pim_ifstat_bsm_cfg_miss; + uint32_t pim_ifstat_ucast_bsm_cfg_miss; + uint32_t pim_ifstat_bsm_invalid_sz; struct bfd_info *bfd_info; + bool bsm_enable; /* bsm processing enable */ + bool ucast_bsm_accept; /* ucast bsm processing */ }; /* @@ -208,10 +223,16 @@ void pim_if_update_assert_tracking_desired(struct interface *ifp); void pim_if_create_pimreg(struct pim_instance *pim); -int pim_if_connected_to_source(struct interface *ifp, struct in_addr src); +struct prefix *pim_if_connected_to_source(struct interface *ifp, struct in_addr src); int pim_update_source_set(struct interface *ifp, struct in_addr source); bool pim_if_is_vrf_device(struct interface *ifp); int pim_if_ifchannel_count(struct pim_interface *pim_ifp); + +extern int pim_ifp_create(struct interface *ifp); +extern int pim_ifp_up(struct interface *ifp); +extern int pim_ifp_down(struct interface *ifp); +extern int pim_ifp_destroy(struct interface *ifp); + #endif /* PIM_IFACE_H */ diff --git a/pimd/pim_ifchannel.c b/pimd/pim_ifchannel.c index cbc3c6a640..7957b0799b 100644 --- a/pimd/pim_ifchannel.c +++ b/pimd/pim_ifchannel.c @@ -43,6 +43,7 @@ #include "pim_upstream.h" #include "pim_ssm.h" #include "pim_rp.h" +#include "pim_mlag.h" RB_GENERATE(pim_ifchannel_rb, pim_ifchannel, pim_ifp_rb, pim_ifchannel_compare); @@ -127,17 +128,50 @@ static void pim_ifchannel_find_new_children(struct pim_ifchannel *ch) void pim_ifchannel_delete(struct pim_ifchannel *ch) { struct pim_interface *pim_ifp; + struct pim_upstream *up; pim_ifp = ch->interface->info; + if (PIM_DEBUG_PIM_TRACE) + zlog_debug("%s: ifchannel entry %s(%s) del start", __func__, + ch->sg_str, ch->interface->name); + + if (PIM_I_am_DualActive(pim_ifp)) { + if (PIM_DEBUG_MLAG) + zlog_debug( + "%s: if-chnanel-%s is deleted from a Dual active Interface", + __func__, ch->sg_str); + /* Post Delete only if it is the last Dual-active Interface */ + if (ch->upstream->dualactive_ifchannel_count == 1) { + pim_mlag_up_local_del(pim_ifp->pim, ch->upstream); + PIM_UPSTREAM_FLAG_UNSET_MLAG_INTERFACE( + ch->upstream->flags); + } + ch->upstream->dualactive_ifchannel_count--; + } + if (ch->upstream->channel_oil) { uint32_t mask = PIM_OIF_FLAG_PROTO_PIM; if (ch->upstream->flags & PIM_UPSTREAM_FLAG_MASK_SRC_IGMP) - mask = PIM_OIF_FLAG_PROTO_IGMP; + mask |= PIM_OIF_FLAG_PROTO_IGMP; - /* SGRpt entry could have empty oil */ - pim_channel_del_oif(ch->upstream->channel_oil, ch->interface, - mask); + /* + * A S,G RPT channel can have an empty oil, we also + * need to take into account the fact that a ifchannel + * might have been suppressing a *,G ifchannel from + * being inherited. So let's figure out what + * needs to be done here + */ + if ((ch->sg.src.s_addr != INADDR_ANY) && + pim_upstream_evaluate_join_desired_interface( + ch->upstream, ch, ch->parent)) + pim_channel_add_oif(ch->upstream->channel_oil, + ch->interface, + PIM_OIF_FLAG_PROTO_STAR, + __func__); + + pim_channel_del_oif(ch->upstream->channel_oil, + ch->interface, mask, __func__); /* * Do we have any S,G's that are inheriting? * Nuke from on high too. @@ -148,9 +182,10 @@ void pim_ifchannel_delete(struct pim_ifchannel *ch) for (ALL_LIST_ELEMENTS_RO(ch->upstream->sources, up_node, child)) - pim_channel_del_oif(child->channel_oil, - ch->interface, - PIM_OIF_FLAG_PROTO_STAR); + pim_channel_del_inherited_oif( + child->channel_oil, + ch->interface, + __func__); } } @@ -166,23 +201,22 @@ void pim_ifchannel_delete(struct pim_ifchannel *ch) listnode_delete(ch->upstream->ifchannels, ch); - if (ch->ifjoin_state != PIM_IFJOIN_NOINFO) { - pim_upstream_update_join_desired(pim_ifp->pim, ch->upstream); - } + up = ch->upstream; /* upstream is common across ifchannels, check if upstream's ifchannel list is empty before deleting upstream_del ref count will take care of it. */ if (ch->upstream->ref_count > 0) - pim_upstream_del(pim_ifp->pim, ch->upstream, - __PRETTY_FUNCTION__); + up = pim_upstream_del(pim_ifp->pim, ch->upstream, __func__); - else - zlog_warn("%s: Avoiding deletion of upstream with ref_count %d " - "from ifchannel(%s): %s", __PRETTY_FUNCTION__, - ch->upstream->ref_count, ch->interface->name, - ch->sg_str); + else { + if (PIM_DEBUG_PIM_TRACE) + zlog_debug( + "%s: Avoiding deletion of upstream with ref_count %d from ifchannel(%s): %s", + __func__, ch->upstream->ref_count, + ch->interface->name, ch->sg_str); + } ch->upstream = NULL; @@ -198,10 +232,13 @@ void pim_ifchannel_delete(struct pim_ifchannel *ch) RB_REMOVE(pim_ifchannel_rb, &pim_ifp->ifchannel_rb, ch); if (PIM_DEBUG_PIM_TRACE) - zlog_debug("%s: ifchannel entry %s is deleted ", - __PRETTY_FUNCTION__, ch->sg_str); + zlog_debug("%s: ifchannel entry %s(%s) is deleted ", __func__, + ch->sg_str, ch->interface->name); XFREE(MTYPE_PIM_IFCHANNEL, ch); + + if (up) + pim_upstream_update_join_desired(pim_ifp->pim, up); } void pim_ifchannel_delete_all(struct interface *ifp) @@ -216,6 +253,7 @@ void pim_ifchannel_delete_all(struct interface *ifp) while (!RB_EMPTY(pim_ifchannel_rb, &pim_ifp->ifchannel_rb)) { ch = RB_ROOT(pim_ifchannel_rb, &pim_ifp->ifchannel_rb); + pim_ifchannel_ifjoin_switch(__func__, ch, PIM_IFJOIN_NOINFO); pim_ifchannel_delete(ch); } } @@ -233,6 +271,7 @@ void pim_ifchannel_ifjoin_switch(const char *caller, struct pim_ifchannel *ch, { enum pim_ifjoin_state old_state = ch->ifjoin_state; struct pim_interface *pim_ifp = ch->interface->info; + struct pim_ifchannel *child_ch; if (PIM_DEBUG_PIM_EVENTS) zlog_debug( @@ -246,7 +285,7 @@ void pim_ifchannel_ifjoin_switch(const char *caller, struct pim_ifchannel *ch, if (PIM_DEBUG_PIM_EVENTS) { zlog_debug( "%s calledby %s: non-transition on state %d (%s)", - __PRETTY_FUNCTION__, caller, new_state, + __func__, caller, new_state, pim_ifchannel_ifjoin_name(new_state, 0)); } return; @@ -269,35 +308,24 @@ void pim_ifchannel_ifjoin_switch(const char *caller, struct pim_ifchannel *ch, if (PIM_DEBUG_PIM_TRACE) zlog_debug( "%s %s: Prune(S,G)=%s from %s", - __FILE__, - __PRETTY_FUNCTION__, + __FILE__, __func__, child->sg_str, up->sg_str); if (!c_oil) continue; - if (!pim_upstream_evaluate_join_desired( - pim_ifp->pim, child)) { - pim_channel_del_oif( - c_oil, ch->interface, - PIM_OIF_FLAG_PROTO_STAR); - pim_upstream_update_join_desired( - pim_ifp->pim, child); - } - /* * If the S,G has no if channel and the * c_oil still * has output here then the *,G was * supplying the implied * if channel. So remove it. - * I think this is dead code now. is it? */ if (c_oil->oil.mfcc_ttls [pim_ifp->mroute_vif_index]) - pim_channel_del_oif( + pim_channel_del_inherited_oif( c_oil, ch->interface, - PIM_OIF_FLAG_PROTO_STAR); + __func__); } } if (ch->ifjoin_state == PIM_IFJOIN_JOIN) { @@ -306,17 +334,23 @@ void pim_ifchannel_ifjoin_switch(const char *caller, struct pim_ifchannel *ch, if (PIM_DEBUG_PIM_TRACE) zlog_debug( "%s %s: Join(S,G)=%s from %s", - __FILE__, - __PRETTY_FUNCTION__, + __FILE__, __func__, child->sg_str, up->sg_str); - if (pim_upstream_evaluate_join_desired( - pim_ifp->pim, child)) { + /* check if the channel can be + * inherited into the SG's OIL + */ + child_ch = pim_ifchannel_find( + ch->interface, + &child->sg); + if (pim_upstream_eval_inherit_if( + child, child_ch, ch)) { pim_channel_add_oif( child->channel_oil, ch->interface, - PIM_OIF_FLAG_PROTO_STAR); + PIM_OIF_FLAG_PROTO_STAR, + __func__); pim_upstream_update_join_desired( pim_ifp->pim, child); } @@ -355,34 +389,28 @@ const char *pim_ifchannel_ifjoin_name(enum pim_ifjoin_state ifjoin_state, return "SGRpt(NI)"; else return "NOINFO"; - break; case PIM_IFJOIN_JOIN: return "JOIN"; - break; case PIM_IFJOIN_PRUNE: if (PIM_IF_FLAG_TEST_S_G_RPT(flags)) return "SGRpt(P)"; else return "PRUNE"; - break; case PIM_IFJOIN_PRUNE_PENDING: if (PIM_IF_FLAG_TEST_S_G_RPT(flags)) return "SGRpt(PP)"; else return "PRUNEP"; - break; case PIM_IFJOIN_PRUNE_TMP: if (PIM_IF_FLAG_TEST_S_G_RPT(flags)) return "SGRpt(P')"; else return "PRUNET"; - break; case PIM_IFJOIN_PRUNE_PENDING_TMP: if (PIM_IF_FLAG_TEST_S_G_RPT(flags)) return "SGRpt(PP')"; else return "PRUNEPT"; - break; } return "ifjoin_bad_state"; @@ -429,7 +457,7 @@ struct pim_ifchannel *pim_ifchannel_find(struct interface *ifp, if (!pim_ifp) { zlog_warn("%s: (S,G)=%s: multicast not enabled on interface %s", - __PRETTY_FUNCTION__, pim_str_sg_dump(sg), ifp->name); + __func__, pim_str_sg_dump(sg), ifp->name); return NULL; } @@ -450,7 +478,7 @@ static void ifmembership_set(struct pim_ifchannel *ch, if (PIM_DEBUG_PIM_EVENTS) { zlog_debug("%s: (S,G)=%s membership now is %s on interface %s", - __PRETTY_FUNCTION__, ch->sg_str, + __func__, ch->sg_str, membership == PIM_IFMEMBERSHIP_INCLUDE ? "INCLUDE" : "NOINFO", ch->interface->name); @@ -555,8 +583,7 @@ struct pim_ifchannel *pim_ifchannel_add(struct interface *ifp, RB_INSERT(pim_ifchannel_rb, &pim_ifp->ifchannel_rb, ch); - up = pim_upstream_add(pim_ifp->pim, sg, NULL, up_flags, - __PRETTY_FUNCTION__, ch); + up = pim_upstream_add(pim_ifp->pim, sg, NULL, up_flags, __func__, ch); ch->upstream = up; @@ -565,7 +592,7 @@ struct pim_ifchannel *pim_ifchannel_add(struct interface *ifp, ch->ifassert_my_metric = pim_macro_ch_my_assert_metric_eval(ch); ch->ifassert_winner_metric = pim_macro_ch_my_assert_metric_eval(ch); - ch->ifassert_winner.s_addr = 0; + ch->ifassert_winner.s_addr = INADDR_ANY; /* Assert state */ ch->t_ifassert_timer = NULL; @@ -581,9 +608,32 @@ struct pim_ifchannel *pim_ifchannel_add(struct interface *ifp, else PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch->flags); + /* + * advertise MLAG Data to MLAG peer + */ + if (PIM_I_am_DualActive(pim_ifp)) { + up->dualactive_ifchannel_count++; + /* Sync once for upstream */ + if (up->dualactive_ifchannel_count == 1) { + PIM_UPSTREAM_FLAG_SET_MLAG_INTERFACE(up->flags); + pim_mlag_up_local_add(pim_ifp->pim, up); + } + if (PIM_DEBUG_MLAG) + zlog_debug( + "%s: New Dual active if-chnanel is added to upstream:%s count:%d, flags:0x%x", + __func__, up->sg_str, + up->dualactive_ifchannel_count, up->flags); + } + + if (up_flags == PIM_UPSTREAM_FLAG_MASK_SRC_PIM) + PIM_IF_FLAG_SET_PROTO_PIM(ch->flags); + + if (up_flags == PIM_UPSTREAM_FLAG_MASK_SRC_IGMP) + PIM_IF_FLAG_SET_PROTO_IGMP(ch->flags); + if (PIM_DEBUG_PIM_TRACE) - zlog_debug("%s: ifchannel %s is created ", __PRETTY_FUNCTION__, - ch->sg_str); + zlog_debug("%s: ifchannel %s(%s) is created ", __func__, + ch->sg_str, ch->interface->name); return ch; } @@ -591,7 +641,7 @@ struct pim_ifchannel *pim_ifchannel_add(struct interface *ifp, static void ifjoin_to_noinfo(struct pim_ifchannel *ch, bool ch_del) { pim_forward_stop(ch, !ch_del); - pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_NOINFO); + pim_ifchannel_ifjoin_switch(__func__, ch, PIM_IFJOIN_NOINFO); if (ch_del) delete_on_noinfo(ch); } @@ -602,6 +652,10 @@ static int on_ifjoin_expiry_timer(struct thread *t) ch = THREAD_ARG(t); + if (PIM_DEBUG_PIM_TRACE) + zlog_debug("%s: ifchannel %s expiry timer", __func__, + ch->sg_str); + ifjoin_to_noinfo(ch, true); /* ch may have been deleted */ @@ -617,10 +671,10 @@ static int on_ifjoin_prune_pending_timer(struct thread *t) ch = THREAD_ARG(t); - if (PIM_DEBUG_TRACE) + if (PIM_DEBUG_PIM_TRACE) zlog_debug( "%s: IFCHANNEL%s %s Prune Pending Timer Popped", - __PRETTY_FUNCTION__, pim_str_sg_dump(&ch->sg), + __func__, pim_str_sg_dump(&ch->sg), pim_ifchannel_ifjoin_name(ch->ifjoin_state, ch->flags)); if (ch->ifjoin_state == PIM_IFJOIN_PRUNE_PENDING) { @@ -656,6 +710,21 @@ static int on_ifjoin_prune_pending_timer(struct thread *t) pim_jp_agg_single_upstream_send(&parent->rpf, parent, true); + /* + * SGRpt prune pending expiry has to install + * SG entry with empty olist to drop the SG + * traffic incase no other intf exists. + * On that scenario, SG entry wouldn't have + * got installed until Prune pending timer + * expired. So install now. + */ + pim_channel_del_oif( + ch->upstream->channel_oil, ifp, + PIM_OIF_FLAG_PROTO_STAR, __func__); + if (!ch->upstream->channel_oil->installed) + pim_upstream_mroute_add( + ch->upstream->channel_oil, + __PRETTY_FUNCTION__); } } /* from here ch may have been deleted */ @@ -682,8 +751,8 @@ static void check_recv_upstream(int is_join, struct interface *recv_ifp, if (pim_rpf_addr_is_inaddr_any(&up->rpf)) { /* RPF'(S,G) not found */ - zlog_warn("%s %s: RPF'%s not found", __FILE__, - __PRETTY_FUNCTION__, up->sg_str); + zlog_warn("%s %s: RPF'%s not found", __FILE__, __func__, + up->sg_str); return; } @@ -696,8 +765,8 @@ static void check_recv_upstream(int is_join, struct interface *recv_ifp, sizeof(rpf_str)); zlog_warn( "%s %s: (S,G)=%s upstream=%s not directed to RPF'(S,G)=%s on interface %s", - __FILE__, __PRETTY_FUNCTION__, up->sg_str, up_str, - rpf_str, recv_ifp->name); + __FILE__, __func__, up->sg_str, up_str, rpf_str, + recv_ifp->name); return; } /* upstream directed to RPF'(S,G) */ @@ -748,7 +817,7 @@ static int nonlocal_upstream(int is_join, struct interface *recv_ifp, char up_str[INET_ADDRSTRLEN]; pim_inet4_dump("", upstream, up_str, sizeof(up_str)); zlog_warn("%s: recv %s (S,G)=%s to non-local upstream=%s on %s", - __PRETTY_FUNCTION__, is_join ? "join" : "prune", + __func__, is_join ? "join" : "prune", pim_str_sg_dump(sg), up_str, recv_ifp->name); } @@ -762,6 +831,27 @@ static int nonlocal_upstream(int is_join, struct interface *recv_ifp, return 1; /* non-local */ } +static void pim_ifchannel_ifjoin_handler(struct pim_ifchannel *ch, + struct pim_interface *pim_ifp) +{ + pim_ifchannel_ifjoin_switch(__func__, ch, PIM_IFJOIN_JOIN); + PIM_IF_FLAG_UNSET_S_G_RPT(ch->flags); + /* check if the interface qualifies as an immediate + * OIF + */ + if (pim_upstream_evaluate_join_desired_interface( + ch->upstream, ch, + NULL /*starch*/)) { + pim_channel_add_oif(ch->upstream->channel_oil, + ch->interface, + PIM_OIF_FLAG_PROTO_PIM, + __func__); + pim_upstream_update_join_desired(pim_ifp->pim, + ch->upstream); + } +} + + void pim_ifchannel_join_add(struct interface *ifp, struct in_addr neigh_addr, struct in_addr upstream, struct prefix_sg *sg, uint8_t source_flags, uint16_t holdtime) @@ -776,8 +866,6 @@ void pim_ifchannel_join_add(struct interface *ifp, struct in_addr neigh_addr, ch = pim_ifchannel_add(ifp, sg, source_flags, PIM_UPSTREAM_FLAG_MASK_SRC_PIM); - if (!ch) - return; /* RFC 4601: 4.6.1. (S,G) Assert Message State Machine @@ -800,8 +888,7 @@ void pim_ifchannel_join_add(struct interface *ifp, struct in_addr neigh_addr, pim_inet4_dump("", neigh_addr, neigh_str, sizeof(neigh_str)); zlog_warn("%s: Assert Loser recv Join%s from %s on %s", - __PRETTY_FUNCTION__, ch->sg_str, neigh_str, - ifp->name); + __func__, ch->sg_str, neigh_str, ifp->name); assert_action_a5(ch); } @@ -811,8 +898,7 @@ void pim_ifchannel_join_add(struct interface *ifp, struct in_addr neigh_addr, switch (ch->ifjoin_state) { case PIM_IFJOIN_NOINFO: - pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, - PIM_IFJOIN_JOIN); + pim_ifchannel_ifjoin_switch(__func__, ch, PIM_IFJOIN_JOIN); if (pim_macro_chisin_oiflist(ch)) { pim_upstream_inherited_olist(pim_ifp->pim, ch->upstream); @@ -821,13 +907,14 @@ void pim_ifchannel_join_add(struct interface *ifp, struct in_addr neigh_addr, /* * If we are going to be a LHR, we need to note it */ - if (ch->upstream->parent && (ch->upstream->parent->flags - & PIM_UPSTREAM_FLAG_MASK_SRC_IGMP) + if (ch->upstream->parent && + (PIM_UPSTREAM_FLAG_TEST_CAN_BE_LHR( + ch->upstream->parent->flags)) && !(ch->upstream->flags & PIM_UPSTREAM_FLAG_MASK_SRC_LHR)) { pim_upstream_ref(ch->upstream, PIM_UPSTREAM_FLAG_MASK_SRC_LHR, - __PRETTY_FUNCTION__); + __func__); pim_upstream_keep_alive_timer_start( ch->upstream, pim_ifp->pim->keep_alive_time); } @@ -868,38 +955,24 @@ void pim_ifchannel_join_add(struct interface *ifp, struct in_addr neigh_addr, THREAD_OFF(ch->t_ifjoin_expiry_timer); break; case PIM_IFJOIN_PRUNE: - if (source_flags & PIM_ENCODE_RPT_BIT) - pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, + if (source_flags & PIM_ENCODE_RPT_BIT) { + pim_ifchannel_ifjoin_switch(__func__, ch, PIM_IFJOIN_NOINFO); - else { - /* - * We have received a S,G join and we are in - * S,G RPT Prune state. Which means we need - * to transition to Join state and setup - * state as appropriate. - */ - pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, - PIM_IFJOIN_JOIN); - PIM_IF_FLAG_UNSET_S_G_RPT(ch->flags); - if (pim_upstream_evaluate_join_desired(pim_ifp->pim, - ch->upstream)) { - pim_channel_add_oif(ch->upstream->channel_oil, - ch->interface, - PIM_OIF_FLAG_PROTO_PIM); - pim_upstream_update_join_desired(pim_ifp->pim, - ch->upstream); - } - } + THREAD_OFF(ch->t_ifjoin_expiry_timer); + delete_on_noinfo(ch); + return; + } else + pim_ifchannel_ifjoin_handler(ch, pim_ifp); break; case PIM_IFJOIN_PRUNE_PENDING: THREAD_OFF(ch->t_ifjoin_prune_pending_timer); if (source_flags & PIM_ENCODE_RPT_BIT) { THREAD_OFF(ch->t_ifjoin_expiry_timer); - pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, + pim_ifchannel_ifjoin_switch(__func__, ch, PIM_IFJOIN_NOINFO); - } else - pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, - PIM_IFJOIN_JOIN); + } else { + pim_ifchannel_ifjoin_handler(ch, pim_ifp); + } break; case PIM_IFJOIN_PRUNE_TMP: break; @@ -928,18 +1001,16 @@ void pim_ifchannel_prune(struct interface *ifp, struct in_addr upstream, ch = pim_ifchannel_find(ifp, sg); if (!ch && !(source_flags & PIM_ENCODE_RPT_BIT)) { - if (PIM_DEBUG_TRACE) + if (PIM_DEBUG_PIM_TRACE) zlog_debug( "%s: Received prune with no relevant ifchannel %s%s state: %d", - __PRETTY_FUNCTION__, ifp->name, - pim_str_sg_dump(sg), source_flags); + __func__, ifp->name, pim_str_sg_dump(sg), + source_flags); return; } ch = pim_ifchannel_add(ifp, sg, source_flags, PIM_UPSTREAM_FLAG_MASK_SRC_PIM); - if (!ch) - return; pim_ifp = ifp->info; @@ -980,7 +1051,7 @@ void pim_ifchannel_prune(struct interface *ifp, struct in_addr upstream, case PIM_IFJOIN_JOIN: THREAD_OFF(ch->t_ifjoin_expiry_timer); - pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, + pim_ifchannel_ifjoin_switch(__func__, ch, PIM_IFJOIN_PRUNE_PENDING); if (listcount(pim_ifp->pim_neighbor_list) > 1) @@ -1001,6 +1072,24 @@ void pim_ifchannel_prune(struct interface *ifp, struct in_addr upstream, case PIM_IFJOIN_PRUNE: if (source_flags & PIM_ENCODE_RPT_BIT) { THREAD_OFF(ch->t_ifjoin_prune_pending_timer); + /* + * While in Prune State, Receive SGRpt Prune. + * RFC 7761 Sec 4.5.3: + * The (S,G,rpt) downstream state machine on interface I + * remains in Prune state. The Expiry Timer (ET) is + * restarted and is then set to the maximum of its + * current value and the HoldTime from the triggering + * Join/Prune message. + */ + if (ch->t_ifjoin_expiry_timer) { + unsigned long rem = thread_timer_remain_second( + ch->t_ifjoin_expiry_timer); + + if (rem > holdtime) + return; + THREAD_OFF(ch->t_ifjoin_expiry_timer); + } + thread_add_timer(router->master, on_ifjoin_expiry_timer, ch, holdtime, &ch->t_ifjoin_expiry_timer); @@ -1028,27 +1117,27 @@ void pim_ifchannel_prune(struct interface *ifp, struct in_addr upstream, } int pim_ifchannel_local_membership_add(struct interface *ifp, - struct prefix_sg *sg) + struct prefix_sg *sg, bool is_vxlan) { struct pim_ifchannel *ch, *starch; struct pim_interface *pim_ifp; struct pim_instance *pim; + int up_flags; /* PIM enabled on interface? */ pim_ifp = ifp->info; if (!pim_ifp) { if (PIM_DEBUG_EVENTS) zlog_debug("%s:%s Expected pim interface setup for %s", - __PRETTY_FUNCTION__, - pim_str_sg_dump(sg), ifp->name); + __func__, pim_str_sg_dump(sg), ifp->name); return 0; } if (!PIM_IF_TEST_PIM(pim_ifp->options)) { if (PIM_DEBUG_EVENTS) - zlog_debug("%s:%s PIM is not configured on this interface %s", - __PRETTY_FUNCTION__, - pim_str_sg_dump(sg), ifp->name); + zlog_debug( + "%s:%s PIM is not configured on this interface %s", + __func__, pim_str_sg_dump(sg), ifp->name); return 0; } @@ -1060,20 +1149,17 @@ int pim_ifchannel_local_membership_add(struct interface *ifp, if (PIM_DEBUG_PIM_EVENTS) zlog_debug( "%s: local membership (S,G)=%s ignored as group is SSM", - __PRETTY_FUNCTION__, - pim_str_sg_dump(sg)); + __func__, pim_str_sg_dump(sg)); return 1; } } - ch = pim_ifchannel_add(ifp, sg, 0, PIM_UPSTREAM_FLAG_MASK_SRC_IGMP); - if (!ch) { - if (PIM_DEBUG_EVENTS) - zlog_debug("%s:%s Unable to add ifchannel", - __PRETTY_FUNCTION__, - pim_str_sg_dump(sg)); - return 0; - } + /* vxlan term mroutes use ipmr-lo as local member to + * pull down multicast vxlan tunnel traffic + */ + up_flags = is_vxlan ? PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_TERM : + PIM_UPSTREAM_FLAG_MASK_SRC_IGMP; + ch = pim_ifchannel_add(ifp, sg, 0, up_flags); ifmembership_set(ch, PIM_IFMEMBERSHIP_INCLUDE); @@ -1087,17 +1173,16 @@ int pim_ifchannel_local_membership_add(struct interface *ifp, for (ALL_LIST_ELEMENTS_RO(up->sources, up_node, child)) { if (PIM_DEBUG_EVENTS) zlog_debug("%s %s: IGMP (S,G)=%s(%s) from %s", - __FILE__, __PRETTY_FUNCTION__, - child->sg_str, ifp->name, - up->sg_str); + __FILE__, __func__, child->sg_str, + ifp->name, up->sg_str); ch = pim_ifchannel_find(ifp, &child->sg); if (pim_upstream_evaluate_join_desired_interface( child, ch, starch)) { pim_channel_add_oif(child->channel_oil, ifp, - PIM_OIF_FLAG_PROTO_STAR); - pim_upstream_switch(pim, child, - PIM_UPSTREAM_JOINED); + PIM_OIF_FLAG_PROTO_STAR, + __func__); + pim_upstream_update_join_desired(pim, child); } } @@ -1114,12 +1199,14 @@ int pim_ifchannel_local_membership_add(struct interface *ifp, == PREFIX_DENY) { pim_channel_add_oif( up->channel_oil, pim->regiface, - PIM_OIF_FLAG_PROTO_IGMP); + PIM_OIF_FLAG_PROTO_IGMP, + __func__); } } } else pim_channel_add_oif(up->channel_oil, pim->regiface, - PIM_OIF_FLAG_PROTO_IGMP); + PIM_OIF_FLAG_PROTO_IGMP, + __func__); } return 1; @@ -1159,27 +1246,23 @@ void pim_ifchannel_local_membership_del(struct interface *ifp, if (PIM_DEBUG_EVENTS) zlog_debug("%s %s: Prune(S,G)=%s(%s) from %s", - __FILE__, __PRETTY_FUNCTION__, - up->sg_str, ifp->name, - child->sg_str); + __FILE__, __func__, up->sg_str, + ifp->name, child->sg_str); ch = pim_ifchannel_find(ifp, &child->sg); - if (c_oil - && !pim_upstream_evaluate_join_desired_interface( - child, ch, starch)) - pim_channel_del_oif(c_oil, ifp, - PIM_OIF_FLAG_PROTO_STAR); - /* * If the S,G has no if channel and the c_oil still * has output here then the *,G was supplying the * implied * if channel. So remove it. */ - if (!chchannel && c_oil - && c_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index]) - pim_channel_del_oif(c_oil, ifp, - PIM_OIF_FLAG_PROTO_STAR); + if (!pim_upstream_evaluate_join_desired_interface( + child, ch, starch) || + (!chchannel && + c_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index])) { + pim_channel_del_inherited_oif(c_oil, ifp, + __func__); + } /* Child node removal/ref count-- will happen as part of * parent' delete_no_info */ @@ -1204,16 +1287,15 @@ void pim_ifchannel_update_could_assert(struct pim_ifchannel *ch) pim_inet4_dump("", ch->sg.src, src_str, sizeof(src_str)); pim_inet4_dump("", ch->sg.grp, grp_str, sizeof(grp_str)); zlog_debug("%s: CouldAssert(%s,%s,%s) changed from %d to %d", - __PRETTY_FUNCTION__, src_str, grp_str, - ch->interface->name, old_couldassert, - new_couldassert); + __func__, src_str, grp_str, ch->interface->name, + old_couldassert, new_couldassert); } if (new_couldassert) { - /* CouldAssert(S,G,I) switched from FALSE to TRUE */ + /* CouldAssert(S,G,I) switched from false to true */ PIM_IF_FLAG_SET_COULD_ASSERT(ch->flags); } else { - /* CouldAssert(S,G,I) switched from TRUE to FALSE */ + /* CouldAssert(S,G,I) switched from true to false */ PIM_IF_FLAG_UNSET_COULD_ASSERT(ch->flags); if (ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER) { @@ -1253,8 +1335,7 @@ void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel *ch) new_addr_str, sizeof(new_addr_str)); zlog_debug( "%s: my_assert_metric(%s,%s,%s) changed from %u,%u,%u,%s to %u,%u,%u,%s", - __PRETTY_FUNCTION__, src_str, grp_str, - ch->interface->name, + __func__, src_str, grp_str, ch->interface->name, ch->ifassert_my_metric.rpt_bit_flag, ch->ifassert_my_metric.metric_preference, ch->ifassert_my_metric.route_metric, old_addr_str, @@ -1288,15 +1369,15 @@ void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel *ch) pim_inet4_dump("", ch->sg.grp, grp_str, sizeof(grp_str)); zlog_debug( "%s: AssertTrackingDesired(%s,%s,%s) changed from %d to %d", - __PRETTY_FUNCTION__, src_str, grp_str, - ch->interface->name, old_atd, new_atd); + __func__, src_str, grp_str, ch->interface->name, + old_atd, new_atd); } if (new_atd) { - /* AssertTrackingDesired(S,G,I) switched from FALSE to TRUE */ + /* AssertTrackingDesired(S,G,I) switched from false to true */ PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch->flags); } else { - /* AssertTrackingDesired(S,G,I) switched from TRUE to FALSE */ + /* AssertTrackingDesired(S,G,I) switched from true to false */ PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch->flags); if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) { @@ -1358,7 +1439,7 @@ void pim_ifchannel_set_star_g_join_state(struct pim_ifchannel *ch, int eom, if (PIM_DEBUG_PIM_TRACE) zlog_debug( - "%s: %s %s eom: %d join %u", __PRETTY_FUNCTION__, + "%s: %s %s eom: %d join %u", __func__, pim_ifchannel_ifjoin_name(ch->ifjoin_state, ch->flags), ch->sg_str, eom, join); if (!ch->sources) @@ -1393,15 +1474,15 @@ void pim_ifchannel_set_star_g_join_state(struct pim_ifchannel *ch, int eom, PIM_IF_FLAG_UNSET_S_G_RPT(child->flags); child->ifjoin_state = PIM_IFJOIN_NOINFO; - if (I_am_RP(pim, child->sg.grp)) { + if ((I_am_RP(pim, child->sg.grp)) && + (!pim_upstream_empty_inherited_olist( + child->upstream))) { pim_channel_add_oif( child->upstream->channel_oil, - ch->interface, PIM_OIF_FLAG_PROTO_STAR); - pim_upstream_switch(pim, child->upstream, - PIM_UPSTREAM_JOINED); - pim_jp_agg_single_upstream_send( - &child->upstream->rpf, child->upstream, - true); + ch->interface, PIM_OIF_FLAG_PROTO_STAR, + __func__); + pim_upstream_update_join_desired(pim, + child->upstream); } send_upstream_starg = true; @@ -1414,9 +1495,9 @@ void pim_ifchannel_set_star_g_join_state(struct pim_ifchannel *ch, int eom, pim_jp_agg_single_upstream_send(&starup->rpf, starup, true); } -unsigned int pim_ifchannel_hash_key(void *arg) +unsigned int pim_ifchannel_hash_key(const void *arg) { - struct pim_ifchannel *ch = (struct pim_ifchannel *)arg; + const struct pim_ifchannel *ch = arg; return jhash_2words(ch->sg.src.s_addr, ch->sg.grp.s_addr, 0); } diff --git a/pimd/pim_ifchannel.h b/pimd/pim_ifchannel.h index b9d4d291d8..425622b79e 100644 --- a/pimd/pim_ifchannel.h +++ b/pimd/pim_ifchannel.h @@ -69,13 +69,30 @@ struct pim_assert_metric { #define PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(flags) ((flags) &= ~PIM_IF_FLAG_MASK_ASSERT_TRACKING_DESIRED) /* - * Flat to tell us if the ifchannel is (S,G,rpt) + * Flag to tell us if the ifchannel is (S,G,rpt) */ #define PIM_IF_FLAG_MASK_S_G_RPT (1 << 2) #define PIM_IF_FLAG_TEST_S_G_RPT(flags) ((flags) & PIM_IF_FLAG_MASK_S_G_RPT) #define PIM_IF_FLAG_SET_S_G_RPT(flags) ((flags) |= PIM_IF_FLAG_MASK_S_G_RPT) #define PIM_IF_FLAG_UNSET_S_G_RPT(flags) ((flags) &= ~PIM_IF_FLAG_MASK_S_G_RPT) +/* + * Flag to tell us if the ifchannel is proto PIM + */ +#define PIM_IF_FLAG_MASK_PROTO_PIM (1 << 3) +#define PIM_IF_FLAG_TEST_PROTO_PIM(flags) ((flags)&PIM_IF_FLAG_MASK_PROTO_PIM) +#define PIM_IF_FLAG_SET_PROTO_PIM(flags) ((flags) |= PIM_IF_FLAG_MASK_PROTO_PIM) +#define PIM_IF_FLAG_UNSET_PROTO_PIM(flags) \ + ((flags) &= ~PIM_IF_FLAG_MASK_PROTO_PIM) +/* + * Flag to tell us if the ifchannel is proto IGMP + */ +#define PIM_IF_FLAG_MASK_PROTO_IGMP (1 << 4) +#define PIM_IF_FLAG_TEST_PROTO_IGMP(flags) ((flags)&PIM_IF_FLAG_MASK_PROTO_IGMP) +#define PIM_IF_FLAG_SET_PROTO_IGMP(flags) \ + ((flags) |= PIM_IF_FLAG_MASK_PROTO_IGMP) +#define PIM_IF_FLAG_UNSET_PROTO_IGMP(flags) \ + ((flags) &= ~PIM_IF_FLAG_MASK_PROTO_IGMP) /* Per-interface (S,G) state */ @@ -130,7 +147,7 @@ void pim_ifchannel_prune(struct interface *ifp, struct in_addr upstream, struct prefix_sg *sg, uint8_t source_flags, uint16_t holdtime); int pim_ifchannel_local_membership_add(struct interface *ifp, - struct prefix_sg *sg); + struct prefix_sg *sg, bool is_vxlan); void pim_ifchannel_local_membership_del(struct interface *ifp, struct prefix_sg *sg); @@ -155,5 +172,5 @@ void pim_ifchannel_set_star_g_join_state(struct pim_ifchannel *ch, int eom, int pim_ifchannel_compare(const struct pim_ifchannel *ch1, const struct pim_ifchannel *ch2); -unsigned int pim_ifchannel_hash_key(void *arg); +unsigned int pim_ifchannel_hash_key(const void *arg); #endif /* PIM_IFCHANNEL_H */ diff --git a/pimd/pim_igmp.c b/pimd/pim_igmp.c index cdd156b96f..04ece6dbb0 100644 --- a/pimd/pim_igmp.c +++ b/pimd/pim_igmp.c @@ -64,9 +64,8 @@ static int igmp_sock_open(struct in_addr ifaddr, struct interface *ifp, } else { zlog_warn( "%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s", - __FILE__, __PRETTY_FUNCTION__, fd, - inet_ntoa(ifaddr), PIM_ALL_ROUTERS, errno, - safe_strerror(errno)); + __FILE__, __func__, fd, inet_ntoa(ifaddr), + PIM_ALL_ROUTERS, errno, safe_strerror(errno)); } } @@ -81,7 +80,7 @@ static int igmp_sock_open(struct in_addr ifaddr, struct interface *ifp, } else { zlog_warn( "%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s", - __FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr), + __FILE__, __func__, fd, inet_ntoa(ifaddr), PIM_ALL_SYSTEMS, errno, safe_strerror(errno)); } @@ -92,7 +91,7 @@ static int igmp_sock_open(struct in_addr ifaddr, struct interface *ifp, } else { zlog_warn( "%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s", - __FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr), + __FILE__, __func__, fd, inet_ntoa(ifaddr), PIM_ALL_IGMP_ROUTERS, errno, safe_strerror(errno)); } @@ -119,8 +118,8 @@ static void igmp_sock_dump(array_t *igmp_sock_array) struct igmp_sock *igmp = array_get(igmp_sock_array, i); zlog_debug("%s %s: [%d/%d] igmp_addr=%s fd=%d", __FILE__, - __PRETTY_FUNCTION__, i, size, - inet_ntoa(igmp->ifaddr), igmp->fd); + __func__, i, size, inet_ntoa(igmp->ifaddr), + igmp->fd); } } #endif @@ -139,7 +138,7 @@ struct igmp_sock *pim_igmp_sock_lookup_ifaddr(struct list *igmp_sock_list, if (ifaddr.s_addr == igmp->ifaddr.s_addr) return igmp; - return 0; + return NULL; } struct igmp_sock *igmp_sock_lookup_by_fd(struct list *igmp_sock_list, int fd) @@ -151,7 +150,7 @@ struct igmp_sock *igmp_sock_lookup_by_fd(struct list *igmp_sock_list, int fd) if (fd == igmp->fd) return igmp; - return 0; + return NULL; } static int pim_igmp_other_querier_expire(struct thread *t) @@ -166,8 +165,7 @@ static int pim_igmp_other_querier_expire(struct thread *t) char ifaddr_str[INET_ADDRSTRLEN]; pim_inet4_dump("", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str)); - zlog_debug("%s: Querier %s resuming", __PRETTY_FUNCTION__, - ifaddr_str); + zlog_debug("%s: Querier %s resuming", __func__, ifaddr_str); } /* @@ -305,6 +303,20 @@ static int igmp_recv_query(struct igmp_sock *igmp, int query_version, return -1; } + if (!pim_if_connected_to_source(ifp, from)) { + if (PIM_DEBUG_IGMP_PACKETS) + zlog_debug("Recv IGMP query on interface: %s from a non-connected source: %s", + ifp->name, from_str); + return 0; + } + + if (if_lookup_exact_address(&from, AF_INET, ifp->vrf_id)) { + if (PIM_DEBUG_IGMP_PACKETS) + zlog_debug("Recv IGMP query on interface: %s from ourself %s", + ifp->name, from_str); + return 0; + } + /* Collecting IGMP Rx stats */ switch (query_version) { case 1: @@ -339,9 +351,7 @@ static int igmp_recv_query(struct igmp_sock *igmp, int query_version, */ if (query_version != pim_ifp->igmp_version) { zlog_warn( - "Recv IGMP query v%d from %s on %s but we are using v%d, please " - "configure all PIM routers on this subnet to use the same " - "IGMP version", + "Recv IGMP query v%d from %s on %s but we are using v%d, please configure all PIM routers on this subnet to use the same IGMP version", query_version, from_str, ifp->name, pim_ifp->igmp_version); return 0; @@ -405,7 +415,7 @@ static int igmp_v1_recv_report(struct igmp_sock *igmp, struct in_addr from, struct igmp_group *group; struct in_addr group_addr; - on_trace(__PRETTY_FUNCTION__, igmp->interface, from); + on_trace(__func__, igmp->interface, from); if (igmp->mtrace_only) return 0; @@ -421,8 +431,7 @@ static int igmp_v1_recv_report(struct igmp_sock *igmp, struct in_addr from, igmp->rx_stats.report_v1++; if (PIM_DEBUG_IGMP_TRACE) { - zlog_warn("%s %s: FIXME WRITEME", __FILE__, - __PRETTY_FUNCTION__); + zlog_warn("%s %s: FIXME WRITEME", __FILE__, __func__); } memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr)); @@ -464,30 +473,31 @@ int pim_igmp_packet(struct igmp_sock *igmp, char *buf, size_t len) ip_hlen = ip_hdr->ip_hl << 2; /* ip_hl gives length in 4-byte words */ - if (PIM_DEBUG_IGMP_PACKETS) { - zlog_debug( - "Recv IP packet from %s to %s on %s: size=%zu ip_header_size=%zu ip_proto=%d", - from_str, to_str, igmp->interface->name, len, ip_hlen, - ip_hdr->ip_p); + if (ip_hlen > len) { + zlog_warn( + "IGMP packet header claims size %zu, but we only have %zu bytes", + ip_hlen, len); + return -1; } igmp_msg = buf + ip_hlen; - msg_type = *igmp_msg; igmp_msg_len = len - ip_hlen; - if (PIM_DEBUG_IGMP_PACKETS) { - zlog_debug( - "Recv IGMP packet from %s to %s on %s: ttl=%d msg_type=%d msg_size=%d", - from_str, to_str, igmp->interface->name, ip_hdr->ip_ttl, - msg_type, igmp_msg_len); - } - if (igmp_msg_len < PIM_IGMP_MIN_LEN) { zlog_warn("IGMP message size=%d shorter than minimum=%d", igmp_msg_len, PIM_IGMP_MIN_LEN); return -1; } + msg_type = *igmp_msg; + + if (PIM_DEBUG_IGMP_PACKETS) { + zlog_debug( + "Recv IGMP packet from %s to %s on %s: size=%zu ttl=%d msg_type=%d msg_size=%d", + from_str, to_str, igmp->interface->name, len, ip_hdr->ip_ttl, + msg_type, igmp_msg_len); + } + switch (msg_type) { case PIM_IGMP_MEMBERSHIP_QUERY: { int max_resp_code = igmp_msg[1]; @@ -741,7 +751,40 @@ static void igmp_group_free(struct igmp_group *group) XFREE(MTYPE_PIM_IGMP_GROUP, group); } -static void igmp_group_delete(struct igmp_group *group) +static void igmp_group_count_incr(struct igmp_sock *igmp) +{ + struct pim_interface *pim_ifp = igmp->interface->info; + + if (!pim_ifp) + return; + + ++pim_ifp->pim->igmp_group_count; + if (pim_ifp->pim->igmp_group_count + == pim_ifp->pim->igmp_watermark_limit) { + zlog_warn( + "IGMP group count reached watermark limit: %u(vrf: %s)", + pim_ifp->pim->igmp_group_count, + VRF_LOGNAME(pim_ifp->pim->vrf)); + } +} + +static void igmp_group_count_decr(struct igmp_sock *igmp) +{ + struct pim_interface *pim_ifp = igmp->interface->info; + + if (!pim_ifp) + return; + + if (pim_ifp->pim->igmp_group_count == 0) { + zlog_warn("Cannot decrement igmp group count below 0(vrf: %s)", + VRF_LOGNAME(pim_ifp->pim->vrf)); + return; + } + + --pim_ifp->pim->igmp_group_count; +} + +void igmp_group_delete(struct igmp_group *group) { struct listnode *src_node; struct listnode *src_nextnode; @@ -766,6 +809,7 @@ static void igmp_group_delete(struct igmp_group *group) } group_timer_off(group); + igmp_group_count_decr(group->group_igmp_sock); listnode_delete(group->group_igmp_sock->igmp_group_list, group); hash_release(group->group_igmp_sock->igmp_group_hash, group); @@ -829,9 +873,9 @@ void igmp_sock_delete_all(struct interface *ifp) } } -static unsigned int igmp_group_hash_key(void *arg) +static unsigned int igmp_group_hash_key(const void *arg) { - struct igmp_group *group = (struct igmp_group *)arg; + const struct igmp_group *group = arg; return jhash_1word(group->group_addr.s_addr, 0); } @@ -867,7 +911,7 @@ static struct igmp_sock *igmp_sock_new(int fd, struct in_addr ifaddr, igmp->igmp_group_list = list_new(); igmp->igmp_group_list->del = (void (*)(void *))igmp_group_free; - snprintf(hash_name, 64, "IGMP %s hash", ifp->name); + snprintf(hash_name, sizeof(hash_name), "IGMP %s hash", ifp->name); igmp->igmp_group_hash = hash_create(igmp_group_hash_key, igmp_group_hash_equal, hash_name); @@ -951,6 +995,7 @@ struct igmp_sock *pim_igmp_sock_add(struct list *igmp_sock_list, { struct pim_interface *pim_ifp; struct igmp_sock *igmp; + struct sockaddr_in sin; int fd; pim_ifp = ifp->info; @@ -959,7 +1004,18 @@ struct igmp_sock *pim_igmp_sock_add(struct list *igmp_sock_list, if (fd < 0) { zlog_warn("Could not open IGMP socket for %s on %s", inet_ntoa(ifaddr), ifp->name); - return 0; + return NULL; + } + + sin.sin_family = AF_INET; + sin.sin_addr = ifaddr; + sin.sin_port = 0; + if (bind(fd, (struct sockaddr *) &sin, sizeof(sin)) != 0) { + zlog_warn("Could not bind IGMP socket for %s on %s", + inet_ntoa(ifaddr), ifp->name); + close(fd); + + return NULL; } igmp = igmp_sock_new(fd, ifaddr, ifp, mtrace_only); @@ -999,9 +1055,8 @@ static int igmp_group_timer(struct thread *t) char group_str[INET_ADDRSTRLEN]; pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); - zlog_debug("%s: Timer for group %s on interface %s", - __PRETTY_FUNCTION__, group_str, - group->group_igmp_sock->interface->name); + zlog_debug("%s: Timer for group %s on interface %s", __func__, + group_str, group->group_igmp_sock->interface->name); } zassert(group->group_filtermode_isexcl); @@ -1093,13 +1148,15 @@ struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp, if (!pim_is_group_224_4(group_addr)) { zlog_warn("%s: Group Specified is not part of 224.0.0.0/4", - __PRETTY_FUNCTION__); + __func__); return NULL; } if (pim_is_group_224_0_0_0_24(group_addr)) { - zlog_warn("%s: Group specified is part of 224.0.0.0/24", - __PRETTY_FUNCTION__); + if (PIM_DEBUG_IGMP_TRACE) + zlog_debug( + "%s: Group specified %s is part of 224.0.0.0/24", + __func__, inet_ntoa(group_addr)); return NULL; } /* @@ -1145,6 +1202,8 @@ struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp, group_str, igmp->fd, igmp->interface->name); } + igmp_group_count_incr(igmp); + /* RFC 3376: 6.2.2. Definition of Group Timers @@ -1180,3 +1239,42 @@ void igmp_send_query(int igmp_version, struct igmp_group *group, int fd, group_addr, query_max_response_time_dsec); } } + +void igmp_send_query_on_intf(struct interface *ifp, int igmp_ver) +{ + struct pim_interface *pim_ifp = ifp->info; + struct listnode *sock_node = NULL; + struct igmp_sock *igmp = NULL; + struct in_addr dst_addr; + struct in_addr group_addr; + int query_buf_size; + + if (!igmp_ver) + igmp_ver = 2; + + if (igmp_ver == 3) + query_buf_size = PIM_IGMP_BUFSIZE_WRITE; + else + query_buf_size = IGMP_V12_MSG_SIZE; + + dst_addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP); + group_addr.s_addr = PIM_NET_INADDR_ANY; + + if (PIM_DEBUG_IGMP_TRACE) + zlog_debug("Issuing general query on request on %s", + ifp->name); + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) { + + char query_buf[query_buf_size]; + + igmp_send_query(igmp_ver, 0 /* igmp_group */, igmp->fd, + igmp->interface->name, query_buf, + sizeof(query_buf), 0 /* num_sources */, + dst_addr, group_addr, + pim_ifp->igmp_query_max_response_time_dsec, + 1 /* s_flag: always set for general queries */, + igmp->querier_robustness_variable, + igmp->querier_query_interval); + } +} diff --git a/pimd/pim_igmp.h b/pimd/pim_igmp.h index c8b880ddd7..9231b0b41f 100644 --- a/pimd/pim_igmp.h +++ b/pimd/pim_igmp.h @@ -197,4 +197,7 @@ void igmp_send_query(int igmp_version, struct igmp_group *group, int fd, int query_max_response_time_dsec, uint8_t s_flag, uint8_t querier_robustness_variable, uint16_t querier_query_interval); +void igmp_group_delete(struct igmp_group *group); + +void igmp_send_query_on_intf(struct interface *ifp, int igmp_ver); #endif /* PIM_IGMP_H */ diff --git a/pimd/pim_igmp_join.h b/pimd/pim_igmp_join.h index 88385bffba..c323902764 100644 --- a/pimd/pim_igmp_join.h +++ b/pimd/pim_igmp_join.h @@ -26,6 +26,10 @@ #define SOL_IP IPPROTO_IP #endif +#ifndef MCAST_JOIN_GROUP +#define MCAST_JOIN_GROUP 42 +#endif + #ifndef MCAST_JOIN_SOURCE_GROUP #define MCAST_JOIN_SOURCE_GROUP 46 struct group_source_req { @@ -58,8 +62,12 @@ static int pim_igmp_join_source(int fd, ifindex_t ifindex, req.gsr_interface = ifindex; - return setsockopt(fd, SOL_IP, MCAST_JOIN_SOURCE_GROUP, &req, - sizeof(req)); + if (source_addr.s_addr == INADDR_ANY) + return setsockopt(fd, SOL_IP, MCAST_JOIN_GROUP, &req, + sizeof(req)); + else + return setsockopt(fd, SOL_IP, MCAST_JOIN_SOURCE_GROUP, &req, + sizeof(req)); } #endif /* PIM_IGMP_JOIN_H */ diff --git a/pimd/pim_igmp_mtrace.c b/pimd/pim_igmp_mtrace.c index 0758e2f784..9e78b76008 100644 --- a/pimd/pim_igmp_mtrace.c +++ b/pimd/pim_igmp_mtrace.c @@ -68,7 +68,7 @@ static bool mtrace_fwd_info_weak(struct pim_instance *pim, struct in_addr nh_addr; char nexthop_str[INET_ADDRSTRLEN]; - nh_addr.s_addr = 0; + nh_addr.s_addr = INADDR_ANY; memset(&nexthop, 0, sizeof(nexthop)); @@ -123,7 +123,7 @@ static bool mtrace_fwd_info(struct pim_instance *pim, up = pim_upstream_find(pim, &sg); if (!up) { - sg.src.s_addr = 0; + sg.src.s_addr = INADDR_ANY; up = pim_upstream_find(pim, &sg); } @@ -132,8 +132,8 @@ static bool mtrace_fwd_info(struct pim_instance *pim, if (!up->rpf.source_nexthop.interface) { if (PIM_DEBUG_TRACE) - zlog_debug("%s: up %s RPF is not present", - __PRETTY_FUNCTION__, up->sg_str); + zlog_debug("%s: up %s RPF is not present", __func__, + up->sg_str); return false; } @@ -160,7 +160,7 @@ static bool mtrace_fwd_info(struct pim_instance *pim, rspp->rtg_proto = MTRACE_RTG_PROTO_PIM; /* 6.2.2. 4. Fill in ... S, and Src Mask */ - if (sg.src.s_addr) { + if (sg.src.s_addr != INADDR_ANY) { rspp->s = 1; rspp->src_mask = MTRACE_SRC_MASK_SOURCE; } else { @@ -181,9 +181,9 @@ static void mtrace_rsp_set_fwd_code(struct igmp_mtrace_rsp *mtrace_rspp, static void mtrace_rsp_init(struct igmp_mtrace_rsp *mtrace_rspp) { mtrace_rspp->arrival = 0; - mtrace_rspp->incoming.s_addr = 0; - mtrace_rspp->outgoing.s_addr = 0; - mtrace_rspp->prev_hop.s_addr = 0; + mtrace_rspp->incoming.s_addr = INADDR_ANY; + mtrace_rspp->outgoing.s_addr = INADDR_ANY; + mtrace_rspp->prev_hop.s_addr = INADDR_ANY; mtrace_rspp->in_count = htonl(MTRACE_UNKNOWN_COUNT); mtrace_rspp->out_count = htonl(MTRACE_UNKNOWN_COUNT); mtrace_rspp->total = htonl(MTRACE_UNKNOWN_COUNT); @@ -231,9 +231,7 @@ static void mtrace_debug(struct pim_interface *pim_ifp, ra = mtracep->rsp_addr; zlog_debug( - "Rx mtrace packet incoming on %s: " - "hops=%d type=%d size=%d, grp=%s, src=%s," - " dst=%s rsp=%s ttl=%d qid=%ud", + "Rx mtrace packet incoming on %s: hops=%d type=%d size=%d, grp=%s, src=%s, dst=%s rsp=%s ttl=%d qid=%ud", inet_ntop(AF_INET, &(pim_ifp->primary_address), inc_str, sizeof(inc_str)), mtracep->hops, mtracep->type, mtrace_len, @@ -255,8 +253,7 @@ static void mtrace_debug(struct pim_interface *pim_ifp, if ((responses % sizeof(struct igmp_mtrace_rsp)) != 0) if (PIM_DEBUG_MTRACE) zlog_debug( - "Mtrace response block of wrong" - " length"); + "Mtrace response block of wrong length"); responses = responses / sizeof(struct igmp_mtrace_rsp); @@ -271,11 +268,10 @@ static uint32_t query_arrival_time(void) struct timeval tv; uint32_t qat; - char m_qat[] = "Query arrival time lookup failed: errno=%d: %s"; - if (gettimeofday(&tv, NULL) < 0) { if (PIM_DEBUG_MTRACE) - zlog_warn(m_qat, errno, safe_strerror(errno)); + zlog_warn("Query arrival time lookup failed: errno=%d: %s", + errno, safe_strerror(errno)); return 0; } /* not sure second offset correct, as I get different value */ @@ -359,17 +355,14 @@ static int mtrace_send_packet(struct interface *ifp, if (sent < 0) { if (PIM_DEBUG_MTRACE) zlog_warn( - "Send mtrace request failed for %s on" - "%s: group=%s msg_size=%zd: errno=%d: " - " %s", + "Send mtrace request failed for %s on%s: group=%s msg_size=%zd: errno=%d: %s", dst_str, ifp->name, group_str, mtrace_buf_len, errno, safe_strerror(errno)); } else { if (PIM_DEBUG_MTRACE) zlog_warn( - "Send mtrace request failed for %s on" - " %s: group=%s msg_size=%zd: sent=%zd", + "Send mtrace request failed for %s on %s: group=%s msg_size=%zd: sent=%zd", dst_str, ifp->name, group_str, mtrace_buf_len, sent); } @@ -419,8 +412,7 @@ static int mtrace_un_forward_packet(struct pim_instance *pim, struct ip *ip_hdr, close(fd); if (PIM_DEBUG_MTRACE) zlog_warn( - "Dropping mtrace packet, " - "no route to destination"); + "Dropping mtrace packet, no route to destination"); return -1; } @@ -449,8 +441,7 @@ static int mtrace_un_forward_packet(struct pim_instance *pim, struct ip *ip_hdr, if (sent < 0) { if (PIM_DEBUG_MTRACE) zlog_warn( - "Failed to forward mtrace packet:" - " sendto errno=%d, %s", + "Failed to forward mtrace packet: sendto errno=%d, %s", errno, safe_strerror(errno)); return -1; } @@ -481,8 +472,7 @@ static int mtrace_mc_forward_packet(struct pim_instance *pim, struct ip *ip_hdr) if (c_oil == NULL) { if (PIM_DEBUG_MTRACE) { zlog_debug( - "Dropping mtrace multicast packet " - "len=%u to %s ttl=%u", + "Dropping mtrace multicast packet len=%u to %s ttl=%u", ntohs(ip_hdr->ip_len), inet_ntoa(ip_hdr->ip_dst), ip_hdr->ip_ttl); } @@ -533,8 +523,7 @@ static int mtrace_send_mc_response(struct pim_instance *pim, if (c_oil == NULL) { if (PIM_DEBUG_MTRACE) { zlog_debug( - "Dropping mtrace multicast response packet " - "len=%u to %s", + "Dropping mtrace multicast response packet len=%u to %s", (unsigned int)mtrace_len, inet_ntoa(mtracep->rsp_addr)); } @@ -596,8 +585,7 @@ static int mtrace_send_response(struct pim_instance *pim, if (!pim_nexthop_lookup(pim, &nexthop, mtracep->rsp_addr, 1)) { if (PIM_DEBUG_MTRACE) zlog_warn( - "Dropped response qid=%ud, no route to " - "response address", + "Dropped response qid=%ud, no route to response address", mtracep->qry_id); return -1; } @@ -646,8 +634,7 @@ int igmp_mtrace_recv_qry_req(struct igmp_sock *igmp, struct ip *ip_hdr, if (igmp_msg_len < (int)sizeof(struct igmp_mtrace)) { if (PIM_DEBUG_MTRACE) zlog_warn( - "Recv mtrace packet from %s on %s: too short," - " len=%d, min=%zu", + "Recv mtrace packet from %s on %s: too short, len=%d, min=%zu", from_str, ifp->name, igmp_msg_len, sizeof(struct igmp_mtrace)); return -1; @@ -664,8 +651,7 @@ int igmp_mtrace_recv_qry_req(struct igmp_sock *igmp, struct ip *ip_hdr, if (recv_checksum != checksum) { if (PIM_DEBUG_MTRACE) zlog_warn( - "Recv mtrace packet from %s on %s: checksum" - " mismatch: received=%x computed=%x", + "Recv mtrace packet from %s on %s: checksum mismatch: received=%x computed=%x", from_str, ifp->name, recv_checksum, checksum); return -1; } @@ -689,8 +675,7 @@ int igmp_mtrace_recv_qry_req(struct igmp_sock *igmp, struct ip *ip_hdr, if (IPV4_CLASS_DE(ntohl(ip_hdr->ip_dst.s_addr))) { if (PIM_DEBUG_MTRACE) zlog_debug( - "Dropping multicast query " - "on wrong interface"); + "Dropping multicast query on wrong interface"); return -1; } /* Unicast query on wrong interface */ @@ -701,8 +686,7 @@ int igmp_mtrace_recv_qry_req(struct igmp_sock *igmp, struct ip *ip_hdr, if (qry_id == mtracep->qry_id && qry_src == from.s_addr) { if (PIM_DEBUG_MTRACE) zlog_debug( - "Dropping multicast query with " - "duplicate source and id"); + "Dropping multicast query with duplicate source and id"); return -1; } qry_id = mtracep->qry_id; @@ -722,8 +706,7 @@ int igmp_mtrace_recv_qry_req(struct igmp_sock *igmp, struct ip *ip_hdr, } else { if (PIM_DEBUG_MTRACE) zlog_warn( - "Recv mtrace packet from %s on %s: " - "invalid length %d", + "Recv mtrace packet from %s on %s: invalid length %d", from_str, ifp->name, igmp_msg_len); return -1; } @@ -733,8 +716,7 @@ int igmp_mtrace_recv_qry_req(struct igmp_sock *igmp, struct ip *ip_hdr, && !IPV4_MC_LINKLOCAL(ntohl(ip_hdr->ip_dst.s_addr))) { if (PIM_DEBUG_MTRACE) zlog_warn( - "Recv mtrace packet from %s on %s:" - " not link-local multicast %s", + "Recv mtrace packet from %s on %s: not link-local multicast %s", from_str, ifp->name, inet_ntoa(ip_hdr->ip_dst)); return -1; } @@ -779,7 +761,7 @@ int igmp_mtrace_recv_qry_req(struct igmp_sock *igmp, struct ip *ip_hdr, /* 6.2.2. 2. Attempt to determine the forwarding information... */ - if (mtracep->grp_addr.s_addr) + if (mtracep->grp_addr.s_addr != INADDR_ANY) fwd_info = mtrace_fwd_info(pim, mtracep, rspp, &out_ifp); else fwd_info = mtrace_fwd_info_weak(pim, mtracep, rspp, &out_ifp); @@ -797,7 +779,7 @@ int igmp_mtrace_recv_qry_req(struct igmp_sock *igmp, struct ip *ip_hdr, reached_source = false; - if (nh_addr.s_addr == 0) { + if (nh_addr.s_addr == INADDR_ANY) { /* no pim? i.e. 7.5.3. No Previous Hop */ if (!out_ifp->info) { if (PIM_DEBUG_MTRACE) @@ -864,6 +846,15 @@ int igmp_mtrace_recv_response(struct igmp_sock *igmp, struct ip *ip_hdr, pim_ifp = ifp->info; pim = pim_ifp->pim; + if (igmp_msg_len < (int)sizeof(struct igmp_mtrace)) { + if (PIM_DEBUG_MTRACE) + zlog_warn( + "Recv mtrace packet from %s on %s: too short, len=%d, min=%zu", + from_str, ifp->name, igmp_msg_len, + sizeof(struct igmp_mtrace)); + return -1; + } + mtracep = (struct igmp_mtrace *)igmp_msg; recv_checksum = mtracep->checksum; @@ -875,8 +866,7 @@ int igmp_mtrace_recv_response(struct igmp_sock *igmp, struct ip *ip_hdr, if (recv_checksum != checksum) { if (PIM_DEBUG_MTRACE) zlog_warn( - "Recv mtrace response from %s on %s: checksum" - " mismatch: received=%x computed=%x", + "Recv mtrace response from %s on %s: checksum mismatch: received=%x computed=%x", from_str, ifp->name, recv_checksum, checksum); return -1; } diff --git a/pimd/pim_igmpv2.c b/pimd/pim_igmpv2.c index 19c3768813..af598d040d 100644 --- a/pimd/pim_igmpv2.c +++ b/pimd/pim_igmpv2.c @@ -109,7 +109,7 @@ int igmp_v2_recv_report(struct igmp_sock *igmp, struct in_addr from, struct in_addr group_addr; char group_str[INET_ADDRSTRLEN]; - on_trace(__PRETTY_FUNCTION__, igmp->interface, from); + on_trace(__func__, igmp->interface, from); if (igmp->mtrace_only) return 0; @@ -158,7 +158,7 @@ int igmp_v2_recv_leave(struct igmp_sock *igmp, struct in_addr from, struct in_addr group_addr; char group_str[INET_ADDRSTRLEN]; - on_trace(__PRETTY_FUNCTION__, igmp->interface, from); + on_trace(__func__, igmp->interface, from); if (igmp->mtrace_only) return 0; diff --git a/pimd/pim_igmpv3.c b/pimd/pim_igmpv3.c index b845f54f06..8eaca75821 100644 --- a/pimd/pim_igmpv3.c +++ b/pimd/pim_igmpv3.c @@ -126,7 +126,7 @@ static int igmp_source_timer(struct thread *t) sizeof(source_str)); zlog_debug( "%s: Source timer expired for group %s source %s on %s", - __PRETTY_FUNCTION__, group_str, source_str, + __func__, group_str, source_str, group->group_igmp_sock->interface->name); } @@ -332,7 +332,7 @@ void igmp_source_free(struct igmp_source *source) static void source_channel_oil_detach(struct igmp_source *source) { if (source->source_channel_oil) { - pim_channel_oil_del(source->source_channel_oil); + pim_channel_oil_del(source->source_channel_oil, __func__); source->source_channel_oil = NULL; } } @@ -377,7 +377,7 @@ void igmp_source_delete(struct igmp_source *source) sizeof(source_str)); zlog_warn( "%s: forwarding=ON(!) IGMP source %s for group %s from socket %d interface %s", - __PRETTY_FUNCTION__, source_str, group_str, + __func__, source_str, group_str, group->group_igmp_sock->fd, group->group_igmp_sock->interface->name); /* warning only */ @@ -544,8 +544,8 @@ void igmpv3_report_isin(struct igmp_sock *igmp, struct in_addr from, struct in_addr group_addr, int num_sources, struct in_addr *sources) { - on_trace(__PRETTY_FUNCTION__, igmp->interface, from, group_addr, - num_sources, sources); + on_trace(__func__, igmp->interface, from, group_addr, num_sources, + sources); allow(igmp, from, group_addr, num_sources, sources); } @@ -653,8 +653,7 @@ void igmpv3_report_isex(struct igmp_sock *igmp, struct in_addr from, struct interface *ifp = igmp->interface; struct igmp_group *group; - on_trace(__PRETTY_FUNCTION__, ifp, from, group_addr, num_sources, - sources); + on_trace(__func__, ifp, from, group_addr, num_sources, sources); if (pim_is_group_filtered(ifp->info, &group_addr)) return; @@ -773,8 +772,7 @@ void igmpv3_report_toin(struct igmp_sock *igmp, struct in_addr from, struct interface *ifp = igmp->interface; struct igmp_group *group; - on_trace(__PRETTY_FUNCTION__, ifp, from, group_addr, num_sources, - sources); + on_trace(__func__, ifp, from, group_addr, num_sources, sources); /* * If the requested filter mode is INCLUDE *and* the requested source @@ -936,8 +934,7 @@ void igmpv3_report_toex(struct igmp_sock *igmp, struct in_addr from, struct interface *ifp = igmp->interface; struct igmp_group *group; - on_trace(__PRETTY_FUNCTION__, ifp, from, group_addr, num_sources, - sources); + on_trace(__func__, ifp, from, group_addr, num_sources, sources); /* non-existant group is created as INCLUDE {empty} */ group = igmp_add_group_by_addr(igmp, group_addr); @@ -963,8 +960,8 @@ void igmpv3_report_allow(struct igmp_sock *igmp, struct in_addr from, struct in_addr group_addr, int num_sources, struct in_addr *sources) { - on_trace(__PRETTY_FUNCTION__, igmp->interface, from, group_addr, - num_sources, sources); + on_trace(__func__, igmp->interface, from, group_addr, num_sources, + sources); allow(igmp, from, group_addr, num_sources, sources); } @@ -997,7 +994,7 @@ static void group_retransmit_group(struct igmp_group *group) char query_buf[query_buf_size]; - lmqc = igmp->querier_robustness_variable; + lmqc = pim_ifp->igmp_last_member_query_count; lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec; lmqt_msec = lmqc * lmqi_msec; @@ -1076,7 +1073,7 @@ static int group_retransmit_sources(struct igmp_group *group, igmp = group->group_igmp_sock; pim_ifp = igmp->interface->info; - lmqc = igmp->querier_robustness_variable; + lmqc = pim_ifp->igmp_last_member_query_count; lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec; lmqt_msec = lmqc * lmqi_msec; @@ -1136,7 +1133,7 @@ static int group_retransmit_sources(struct igmp_group *group, group_str, sizeof(group_str)); zlog_warn( "%s: group %s on %s: s_flag=1 unable to fit %d sources into buf_size=%zu (max_sources=%d)", - __PRETTY_FUNCTION__, group_str, + __func__, group_str, igmp->interface->name, num_sources_tosend1, sizeof(query_buf1), query_buf1_max_sources); @@ -1180,9 +1177,9 @@ static int group_retransmit_sources(struct igmp_group *group, sizeof(group_str)); zlog_warn( "%s: group %s on %s: s_flag=0 unable to fit %d sources into buf_size=%zu (max_sources=%d)", - __PRETTY_FUNCTION__, group_str, - igmp->interface->name, num_sources_tosend2, - sizeof(query_buf2), query_buf2_max_sources); + __func__, group_str, igmp->interface->name, + num_sources_tosend2, sizeof(query_buf2), + query_buf2_max_sources); } else { /* RFC3376: 4.1.12. IP Destination Addresses for Queries @@ -1314,9 +1311,13 @@ static long igmp_source_timer_remain_msec(struct igmp_source *source) */ static void group_query_send(struct igmp_group *group) { + struct pim_interface *pim_ifp; + struct igmp_sock *igmp; long lmqc; /* Last Member Query Count */ - lmqc = group->group_igmp_sock->querier_robustness_variable; + igmp = group->group_igmp_sock; + pim_ifp = igmp->interface->info; + lmqc = pim_ifp->igmp_last_member_query_count; /* lower group timer to lmqt */ igmp_group_timer_lower_to_lmqt(group); @@ -1351,7 +1352,7 @@ static void source_query_send_by_flag(struct igmp_group *group, igmp = group->group_igmp_sock; pim_ifp = igmp->interface->info; - lmqc = igmp->querier_robustness_variable; + lmqc = pim_ifp->igmp_last_member_query_count; lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec; lmqt_msec = lmqc * lmqi_msec; @@ -1464,8 +1465,7 @@ void igmpv3_report_block(struct igmp_sock *igmp, struct in_addr from, struct interface *ifp = igmp->interface; struct igmp_group *group; - on_trace(__PRETTY_FUNCTION__, ifp, from, group_addr, num_sources, - sources); + on_trace(__func__, ifp, from, group_addr, num_sources, sources); /* non-existant group is created as INCLUDE {empty} */ group = igmp_add_group_by_addr(igmp, group_addr); @@ -1509,7 +1509,7 @@ void igmp_group_timer_lower_to_lmqt(struct igmp_group *group) ifname = ifp->name; lmqi_dsec = pim_ifp->igmp_specific_query_max_response_time_dsec; - lmqc = igmp->querier_robustness_variable; + lmqc = pim_ifp->igmp_last_member_query_count; lmqt_msec = PIM_IGMP_LMQT_MSEC( lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */ @@ -1519,7 +1519,7 @@ void igmp_group_timer_lower_to_lmqt(struct igmp_group *group) sizeof(group_str)); zlog_debug( "%s: group %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec", - __PRETTY_FUNCTION__, group_str, ifname, lmqc, lmqi_dsec, + __func__, group_str, ifname, lmqc, lmqi_dsec, lmqt_msec); } @@ -1546,7 +1546,7 @@ void igmp_source_timer_lower_to_lmqt(struct igmp_source *source) ifname = ifp->name; lmqi_dsec = pim_ifp->igmp_specific_query_max_response_time_dsec; - lmqc = igmp->querier_robustness_variable; + lmqc = pim_ifp->igmp_last_member_query_count; lmqt_msec = PIM_IGMP_LMQT_MSEC( lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */ @@ -1559,8 +1559,8 @@ void igmp_source_timer_lower_to_lmqt(struct igmp_source *source) sizeof(source_str)); zlog_debug( "%s: group %s source %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec", - __PRETTY_FUNCTION__, group_str, source_str, ifname, - lmqc, lmqi_dsec, lmqt_msec); + __func__, group_str, source_str, ifname, lmqc, + lmqi_dsec, lmqt_msec); } igmp_source_timer_on(group, source, lmqt_msec); @@ -1588,8 +1588,7 @@ void igmp_v3_send_query(struct igmp_group *group, int fd, const char *ifname, flog_err( EC_LIB_DEVELOPMENT, "%s %s: unable to send: msg_size=%zd larger than query_buf_size=%d", - __FILE__, __PRETTY_FUNCTION__, msg_size, - query_buf_size); + __FILE__, __func__, msg_size, query_buf_size); return; } @@ -1686,7 +1685,7 @@ void igmp_v3_send_query(struct igmp_group *group, int fd, const char *ifname, sizeof(group_str)); zlog_warn( "%s: to %s on %s: group=%s sources=%d: s_flag is clear for general query!", - __PRETTY_FUNCTION__, dst_str, ifname, group_str, + __func__, dst_str, ifname, group_str, num_sources); } } @@ -1922,7 +1921,7 @@ int igmp_v3_recv_report(struct igmp_sock *igmp, struct in_addr from, if (PIM_DEBUG_IGMP_PACKETS) { zlog_debug( - "Recv IGMP report v3 from %s on %s: record=%d type=%d auxdatalen=%d sources=%d group=%s", + " Recv IGMP report v3 from %s on %s: record=%d type=%d auxdatalen=%d sources=%d group=%s", from_str, ifp->name, i, rec_type, rec_auxdatalen, rec_num_sources, inet_ntoa(rec_group)); @@ -1946,10 +1945,11 @@ int igmp_v3_recv_report(struct igmp_sock *igmp, struct in_addr from, if (!inet_ntop(AF_INET, src, src_str, sizeof(src_str))) - sprintf(src_str, ""); + snprintf(src_str, sizeof(src_str), + ""); zlog_debug( - "Recv IGMP report v3 from %s on %s: record=%d group=%s source=%s", + " Recv IGMP report v3 from %s on %s: record=%d group=%s source=%s", from_str, ifp->name, i, inet_ntoa(rec_group), src_str); } diff --git a/pimd/pim_instance.c b/pimd/pim_instance.c index a2bf3d2783..019048abf1 100644 --- a/pimd/pim_instance.c +++ b/pimd/pim_instance.c @@ -33,6 +33,8 @@ #include "pim_static.h" #include "pim_ssmpingd.h" #include "pim_vty.h" +#include "pim_bsm.h" +#include "pim_mlag.h" static void pim_instance_terminate(struct pim_instance *pim) { @@ -46,10 +48,14 @@ static void pim_instance_terminate(struct pim_instance *pim) if (pim->static_routes) list_delete(&pim->static_routes); + pim_instance_mlag_terminate(pim); + pim_upstream_terminate(pim); pim_rp_free(pim); + pim_bsm_proc_free(pim); + /* Traverse and cleanup rpf_hash */ if (pim->rpf_hash) { hash_clean(pim->rpf_hash, (void *)pim_rp_list_hash_clean); @@ -63,6 +69,10 @@ static void pim_instance_terminate(struct pim_instance *pim) pim_msdp_exit(pim); + XFREE(MTYPE_PIM_PLIST_NAME, pim->spt.plist); + XFREE(MTYPE_PIM_PLIST_NAME, pim->register_plist); + + pim->vrf = NULL; XFREE(MTYPE_PIM_PIM_INSTANCE, pim); } @@ -75,6 +85,7 @@ static struct pim_instance *pim_instance_init(struct vrf *vrf) pim_if_init(pim); + pim->mcast_if_count = 0; pim->keep_alive_time = PIM_KEEPALIVE_PERIOD; pim->rp_keep_alive_time = PIM_RP_KEEPALIVE_PERIOD; @@ -90,12 +101,12 @@ static struct pim_instance *pim_instance_init(struct vrf *vrf) pim_msdp_init(pim, router->master); pim_vxlan_init(pim); - snprintf(hash_name, 64, "PIM %s RPF Hash", vrf->name); + snprintf(hash_name, sizeof(hash_name), "PIM %s RPF Hash", vrf->name); pim->rpf_hash = hash_create_size(256, pim_rpf_hash_key, pim_rpf_equal, hash_name); if (PIM_DEBUG_ZEBRA) - zlog_debug("%s: NHT rpf hash init ", __PRETTY_FUNCTION__); + zlog_debug("%s: NHT rpf hash init ", __func__); pim->ssm_info = pim_ssm_init(); @@ -106,10 +117,14 @@ static struct pim_instance *pim_instance_init(struct vrf *vrf) pim_rp_init(pim); + pim_bsm_proc_init(pim); + pim_oil_init(pim); pim_upstream_init(pim); + pim_instance_mlag_init(pim); + pim->last_route_change_time = -1; return pim; } @@ -140,10 +155,16 @@ static int pim_vrf_delete(struct vrf *vrf) { struct pim_instance *pim = vrf->info; + if (!pim) + return 0; + zlog_debug("VRF Deletion: %s(%u)", vrf->name, vrf->vrf_id); pim_ssmpingd_destroy(pim); pim_instance_terminate(pim); + + vrf->info = NULL; + return 0; } @@ -155,7 +176,7 @@ static int pim_vrf_enable(struct vrf *vrf) { struct pim_instance *pim = (struct pim_instance *)vrf->info; - zlog_debug("%s: for %s", __PRETTY_FUNCTION__, vrf->name); + zlog_debug("%s: for %s", __func__, vrf->name); pim_mroute_socket_enable(pim); diff --git a/pimd/pim_instance.h b/pimd/pim_instance.h index 1740bcc790..2b76da21b2 100644 --- a/pimd/pim_instance.h +++ b/pimd/pim_instance.h @@ -26,7 +26,10 @@ #include "pim_str.h" #include "pim_msdp.h" #include "pim_assert.h" +#include "pim_bsm.h" #include "pim_vxlan_instance.h" +#include "pim_oil.h" +#include "pim_upstream.h" #if defined(HAVE_LINUX_MROUTE_H) #include @@ -45,6 +48,46 @@ enum pim_spt_switchover { PIM_SPT_INFINITY, }; +/* stats for updates rxed from the MLAG component during the life of a + * session + */ +struct pim_mlag_msg_stats { + uint32_t mroute_add_rx; + uint32_t mroute_add_tx; + uint32_t mroute_del_rx; + uint32_t mroute_del_tx; + uint32_t mlag_status_updates; + uint32_t pim_status_updates; + uint32_t vxlan_updates; + uint32_t peer_zebra_status_updates; +}; + +struct pim_mlag_stats { + /* message stats are reset when the connection to mlagd flaps */ + struct pim_mlag_msg_stats msg; + uint32_t mlagd_session_downs; + uint32_t peer_session_downs; + uint32_t peer_zebra_downs; +}; + +enum pim_mlag_flags { + PIM_MLAGF_NONE = 0, + /* connection to the local MLAG daemon is up */ + PIM_MLAGF_LOCAL_CONN_UP = (1 << 0), + /* connection to the MLAG daemon on the peer switch is up. note + * that there is no direct connection between FRR and the peer MLAG + * daemon. this is just a peer-session status provided by the local + * MLAG daemon. + */ + PIM_MLAGF_PEER_CONN_UP = (1 << 1), + /* status update rxed from the local daemon */ + PIM_MLAGF_STATUS_RXED = (1 << 2), + /* initial dump of data done post peerlink flap */ + PIM_MLAGF_PEER_REPLAY_DONE = (1 << 3), + /* zebra is up on the peer */ + PIM_MLAGF_PEER_ZEBRA_UP = (1 << 4) +}; + struct pim_router { struct thread_master *master; @@ -62,7 +105,24 @@ struct pim_router { */ vrf_id_t vrf_id; - enum mlag_role role; + enum mlag_role mlag_role; + uint32_t pim_mlag_intf_cnt; + /* if true we have registered with MLAG */ + bool mlag_process_register; + /* if true local MLAG process reported that it is connected + * with the peer MLAG process + */ + bool connected_to_mlag; + /* Holds the client data(unencoded) that need to be pushed to MCLAGD*/ + struct stream_fifo *mlag_fifo; + struct stream *mlag_stream; + struct thread *zpthread_mlag_write; + struct in_addr anycast_vtep_ip; + struct in_addr local_vtep_ip; + struct pim_mlag_stats mlag_stats; + enum pim_mlag_flags mlag_flags; + char peerlink_rif[INTERFACE_NAMSIZ]; + struct interface *peerlink_rif_p; }; /* Per VRF PIM DB */ @@ -75,6 +135,9 @@ struct pim_instance { char *plist; } spt; + /* The name of the register-accept prefix-list */ + char *register_plist; + struct hash *rpf_hash; void *ssm_info; /* per-vrf SSM configuration */ @@ -95,8 +158,7 @@ struct pim_instance { struct list *static_routes; // Upstream vrf specific information - struct list *upstream_list; - struct hash *upstream_hash; + struct rb_pim_upstream_head upstream_head; struct timer_wheel *upstream_sg_wheel; /* @@ -106,9 +168,9 @@ struct pim_instance { struct route_table *rp_table; int iface_vif_index[MAXVIFS]; + int mcast_if_count; - struct list *channel_oil_list; - struct hash *channel_oil_hash; + struct rb_pim_oil_head channel_oil_head; struct pim_msdp msdp; struct pim_vxlan_instance vxlan; @@ -116,11 +178,21 @@ struct pim_instance { struct list *ssmpingd_list; struct in_addr ssmpingd_group_addr; + unsigned int igmp_group_count; + unsigned int igmp_watermark_limit; unsigned int keep_alive_time; unsigned int rp_keep_alive_time; bool ecmp_enable; bool ecmp_rebalance_enable; + /* No. of Dual active I/fs in pim_instance */ + uint32_t inst_mlag_intf_cnt; + + /* Bsm related */ + struct bsm_scope global_scope; + uint64_t bsm_rcvd; + uint64_t bsm_sent; + uint64_t bsm_dropped; /* If we need to rescan all our upstreams */ struct thread *rpf_cache_refresher; diff --git a/pimd/pim_join.c b/pimd/pim_join.c index cbacaf3ea8..3a88de2070 100644 --- a/pimd/pim_join.c +++ b/pimd/pim_join.c @@ -62,9 +62,9 @@ static void recv_join(struct interface *ifp, struct pim_neighbor *neigh, pim_inet4_dump("", upstream, up_str, sizeof(up_str)); pim_inet4_dump("", neigh->source_addr, neigh_str, sizeof(neigh_str)); - zlog_warn( + zlog_debug( "%s: join (S,G)=%s rpt=%d wc=%d upstream=%s holdtime=%d from %s on %s", - __PRETTY_FUNCTION__, pim_str_sg_dump(sg), + __func__, pim_str_sg_dump(sg), !!(source_flags & PIM_RPT_BIT_MASK), !!(source_flags & PIM_WILDCARD_BIT_MASK), up_str, holdtime, neigh_str, ifp->name); @@ -83,6 +83,11 @@ static void recv_join(struct interface *ifp, struct pim_neighbor *neigh, && (source_flags & PIM_WILDCARD_BIT_MASK)) { struct pim_rpf *rp = RP(pim_ifp->pim, sg->grp); + if (!rp) { + zlog_warn("%s: Lookup of RP failed for %pSG4", __func__, + sg); + return; + } /* * If the RP sent in the message is not * our RP for the group, drop the message @@ -94,11 +99,9 @@ static void recv_join(struct interface *ifp, struct pim_neighbor *neigh, sizeof(received_rp)); pim_inet4_dump("", rp->rpf_addr.u.prefix4, local_rp, sizeof(local_rp)); - if (PIM_DEBUG_PIM_TRACE) - zlog_warn( - "%s: Specified RP(%s) in join is different than our configured RP(%s)", - __PRETTY_FUNCTION__, received_rp, - local_rp); + zlog_warn( + "%s: Specified RP(%s) in join is different than our configured RP(%s)", + __func__, received_rp, local_rp); return; } @@ -122,9 +125,9 @@ static void recv_prune(struct interface *ifp, struct pim_neighbor *neigh, pim_inet4_dump("", upstream, up_str, sizeof(up_str)); pim_inet4_dump("", neigh->source_addr, neigh_str, sizeof(neigh_str)); - zlog_warn( + zlog_debug( "%s: prune (S,G)=%s rpt=%d wc=%d upstream=%s holdtime=%d from %s on %s", - __PRETTY_FUNCTION__, pim_str_sg_dump(sg), + __func__, pim_str_sg_dump(sg), source_flags & PIM_RPT_BIT_MASK, source_flags & PIM_WILDCARD_BIT_MASK, up_str, holdtime, neigh_str, ifp->name); @@ -137,11 +140,19 @@ static void recv_prune(struct interface *ifp, struct pim_neighbor *neigh, if ((source_flags & PIM_RPT_BIT_MASK) && (source_flags & PIM_WILDCARD_BIT_MASK)) { - struct pim_rpf *rp = RP(pim_ifp->pim, sg->grp); + /* + * RFC 4601 Section 4.5.2: + * Received Prune(*,G) messages are processed even if the + * RP in the message does not match RP(G). + */ + if (PIM_DEBUG_PIM_TRACE) { + char received_rp[INET_ADDRSTRLEN]; - // Ignoring Prune *,G's at the moment. - if (sg->src.s_addr != rp->rpf_addr.u.prefix4.s_addr) - return; + pim_inet4_dump("", sg->src, received_rp, + sizeof(received_rp)); + zlog_debug("%s: Prune received with RP(%s) for %pSG4", + __func__, received_rp, sg); + } sg->src.s_addr = INADDR_ANY; } @@ -176,7 +187,7 @@ int pim_joinprune_recv(struct interface *ifp, struct pim_neighbor *neigh, char src_str[INET_ADDRSTRLEN]; pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); zlog_warn("%s: pim_parse_addr_ucast() failure: from %s on %s", - __PRETTY_FUNCTION__, src_str, ifp->name); + __func__, src_str, ifp->name); return -1; } buf += addr_offset; @@ -185,15 +196,11 @@ int pim_joinprune_recv(struct interface *ifp, struct pim_neighbor *neigh, Check upstream address family */ if (msg_upstream_addr.family != AF_INET) { - if (PIM_DEBUG_PIM_J_P) { - char src_str[INET_ADDRSTRLEN]; - pim_inet4_dump("", src_addr, src_str, - sizeof(src_str)); - zlog_warn( - "%s: ignoring join/prune directed to unexpected addr family=%d from %s on %s", - __PRETTY_FUNCTION__, msg_upstream_addr.family, - src_str, ifp->name); - } + char src_str[INET_ADDRSTRLEN]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn( + "%s: ignoring join/prune directed to unexpected addr family=%d from %s on %s", + __func__, msg_upstream_addr.family, src_str, ifp->name); return -2; } @@ -203,7 +210,7 @@ int pim_joinprune_recv(struct interface *ifp, struct pim_neighbor *neigh, pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); zlog_warn( "%s: short join/prune message buffer for group list: size=%d minimum=%d from %s on %s", - __PRETTY_FUNCTION__, remain, 4, src_str, ifp->name); + __func__, remain, 4, src_str, ifp->name); return -4; } @@ -222,8 +229,8 @@ int pim_joinprune_recv(struct interface *ifp, struct pim_neighbor *neigh, upstream_str, sizeof(upstream_str)); zlog_debug( "%s: join/prune upstream=%s groups=%d holdtime=%d from %s on %s", - __PRETTY_FUNCTION__, upstream_str, msg_num_groups, - msg_holdtime, src_str, ifp->name); + __func__, upstream_str, msg_num_groups, msg_holdtime, + src_str, ifp->name); } /* Scan groups */ @@ -250,8 +257,7 @@ int pim_joinprune_recv(struct interface *ifp, struct pim_neighbor *neigh, sizeof(src_str)); zlog_warn( "%s: short join/prune buffer for source list: size=%d minimum=%d from %s on %s", - __PRETTY_FUNCTION__, remain, 4, src_str, - ifp->name); + __func__, remain, 4, src_str, ifp->name); return -6; } @@ -270,9 +276,9 @@ int pim_joinprune_recv(struct interface *ifp, struct pim_neighbor *neigh, upstream_str, sizeof(upstream_str)); pim_inet4_dump("", sg.grp, group_str, sizeof(group_str)); - zlog_warn( + zlog_debug( "%s: join/prune upstream=%s group=%s/32 join_src=%d prune_src=%d from %s on %s", - __PRETTY_FUNCTION__, upstream_str, group_str, + __func__, upstream_str, group_str, msg_num_joined_sources, msg_num_pruned_sources, src_str, ifp->name); } @@ -323,7 +329,6 @@ int pim_joinprune_recv(struct interface *ifp, struct pim_neighbor *neigh, recv_prune(ifp, neigh, msg_holdtime, msg_upstream_addr.u.prefix4, &sg, msg_source_flags); - /* * So if we are receiving a S,G,RPT prune * before we have any data for that S,G @@ -339,15 +344,14 @@ int pim_joinprune_recv(struct interface *ifp, struct pim_neighbor *neigh, struct pim_upstream *up = sg_ch->upstream; PIM_IF_FLAG_SET_S_G_RPT(sg_ch->flags); if (up) { - if (PIM_DEBUG_TRACE) + if (PIM_DEBUG_PIM_TRACE) zlog_debug( "%s: SGRpt flag is set, del inherit oif from up %s", - __PRETTY_FUNCTION__, - up->sg_str); - pim_channel_del_oif( + __func__, up->sg_str); + pim_channel_del_inherited_oif( up->channel_oil, starg_ch->interface, - PIM_OIF_FLAG_PROTO_STAR); + __func__); } } } @@ -442,17 +446,15 @@ int pim_joinprune_send(struct pim_rpf *rpf, struct list *groups) if (rpf->source_nexthop.interface) pim_ifp = rpf->source_nexthop.interface->info; else { - zlog_warn("%s: RPF interface is not present", - __PRETTY_FUNCTION__); + zlog_warn("%s: RPF interface is not present", __func__); return -1; } - on_trace(__PRETTY_FUNCTION__, rpf->source_nexthop.interface, - rpf->rpf_addr.u.prefix4); + on_trace(__func__, rpf->source_nexthop.interface, + rpf->rpf_addr.u.prefix4); if (!pim_ifp) { - zlog_warn("%s: multicast not enabled on interface %s", - __PRETTY_FUNCTION__, + zlog_warn("%s: multicast not enabled on interface %s", __func__, rpf->source_nexthop.interface->name); return -1; } @@ -463,7 +465,7 @@ int pim_joinprune_send(struct pim_rpf *rpf, struct list *groups) pim_inet4_dump("", rpf->rpf_addr.u.prefix4, dst_str, sizeof(dst_str)); zlog_debug("%s: upstream=%s is myself on interface %s", - __PRETTY_FUNCTION__, dst_str, + __func__, dst_str, rpf->source_nexthop.interface->name); } return 0; @@ -512,14 +514,14 @@ int pim_joinprune_send(struct pim_rpf *rpf, struct list *groups) sizeof(grp_str)); zlog_debug( "%s: sending (G)=%s to upstream=%s on interface %s", - __PRETTY_FUNCTION__, grp_str, dst_str, + __func__, grp_str, dst_str, rpf->source_nexthop.interface->name); } group_size = pim_msg_get_jp_group_size(group->sources); if (group_size > packet_left) { pim_msg_build_header(pim_msg, packet_size, - PIM_MSG_TYPE_JOIN_PRUNE); + PIM_MSG_TYPE_JOIN_PRUNE, false); if (pim_msg_send(pim_ifp->pim_sock_fd, pim_ifp->primary_address, qpim_all_pim_routers_addr, pim_msg, @@ -527,7 +529,7 @@ int pim_joinprune_send(struct pim_rpf *rpf, struct list *groups) rpf->source_nexthop.interface->name)) { zlog_warn( "%s: could not send PIM message on interface %s", - __PRETTY_FUNCTION__, + __func__, rpf->source_nexthop.interface->name); } @@ -568,15 +570,14 @@ int pim_joinprune_send(struct pim_rpf *rpf, struct list *groups) if (PIM_DEBUG_PIM_TRACE) zlog_debug( "%s: interface %s num_joins %u num_prunes %u", - __PRETTY_FUNCTION__, - rpf->source_nexthop.interface->name, + __func__, rpf->source_nexthop.interface->name, ntohs(grp->joins), ntohs(grp->prunes)); grp = (struct pim_jp_groups *)curr_ptr; if (packet_left < sizeof(struct pim_jp_groups) || msg->num_groups == 255) { pim_msg_build_header(pim_msg, packet_size, - PIM_MSG_TYPE_JOIN_PRUNE); + PIM_MSG_TYPE_JOIN_PRUNE, false); if (pim_msg_send(pim_ifp->pim_sock_fd, pim_ifp->primary_address, qpim_all_pim_routers_addr, pim_msg, @@ -584,7 +585,7 @@ int pim_joinprune_send(struct pim_rpf *rpf, struct list *groups) rpf->source_nexthop.interface->name)) { zlog_warn( "%s: could not send PIM message on interface %s", - __PRETTY_FUNCTION__, + __func__, rpf->source_nexthop.interface->name); } @@ -596,15 +597,14 @@ int pim_joinprune_send(struct pim_rpf *rpf, struct list *groups) if (!new_packet) { // msg->num_groups = htons (msg->num_groups); pim_msg_build_header(pim_msg, packet_size, - PIM_MSG_TYPE_JOIN_PRUNE); + PIM_MSG_TYPE_JOIN_PRUNE, false); if (pim_msg_send(pim_ifp->pim_sock_fd, pim_ifp->primary_address, qpim_all_pim_routers_addr, pim_msg, packet_size, rpf->source_nexthop.interface->name)) { zlog_warn( "%s: could not send PIM message on interface %s", - __PRETTY_FUNCTION__, - rpf->source_nexthop.interface->name); + __func__, rpf->source_nexthop.interface->name); } } return 0; diff --git a/pimd/pim_jp_agg.c b/pimd/pim_jp_agg.c index 06a9e6d0d6..5279a00855 100644 --- a/pimd/pim_jp_agg.c +++ b/pimd/pim_jp_agg.c @@ -117,10 +117,16 @@ void pim_jp_agg_clear_group(struct list *group) static struct pim_iface_upstream_switch * pim_jp_agg_get_interface_upstream_switch_list(struct pim_rpf *rpf) { - struct pim_interface *pim_ifp = rpf->source_nexthop.interface->info; + struct interface *ifp = rpf->source_nexthop.interface; + struct pim_interface *pim_ifp; struct pim_iface_upstream_switch *pius; struct listnode *node, *nnode; + if (!ifp) + return NULL; + + pim_ifp = ifp->info; + /* Old interface is pim disabled */ if (!pim_ifp) return NULL; @@ -142,7 +148,8 @@ pim_jp_agg_get_interface_upstream_switch_list(struct pim_rpf *rpf) return pius; } -void pim_jp_agg_remove_group(struct list *group, struct pim_upstream *up) +void pim_jp_agg_remove_group(struct list *group, struct pim_upstream *up, + struct pim_neighbor *nbr) { struct listnode *node, *nnode; struct pim_jp_agg_group *jag = NULL; @@ -161,6 +168,20 @@ void pim_jp_agg_remove_group(struct list *group, struct pim_upstream *up) break; } + if (nbr) { + if (PIM_DEBUG_TRACE) { + char src_str[INET_ADDRSTRLEN]; + + pim_inet4_dump("", nbr->source_addr, src_str, + sizeof(src_str)); + zlog_debug( + "up %s remove from nbr %s/%s jp-agg-list", + up->sg_str, + nbr->interface->name, + src_str); + } + } + if (js) { js->up = NULL; listnode_delete(jag->sources, js); @@ -217,9 +238,9 @@ void pim_jp_agg_upstream_verification(struct pim_upstream *up, bool ignore) struct pim_instance *pim; if (!up->rpf.source_nexthop.interface) { - if (PIM_DEBUG_TRACE) - zlog_debug("%s: up %s RPF is not present", - __PRETTY_FUNCTION__, up->sg_str); + if (PIM_DEBUG_PIM_TRACE) + zlog_debug("%s: up %s RPF is not present", __func__, + up->sg_str); return; } @@ -248,7 +269,7 @@ void pim_jp_agg_upstream_verification(struct pim_upstream *up, bool ignore) } void pim_jp_agg_add_group(struct list *group, struct pim_upstream *up, - bool is_join) + bool is_join, struct pim_neighbor *nbr) { struct listnode *node, *nnode; struct pim_jp_agg_group *jag = NULL; @@ -274,6 +295,20 @@ void pim_jp_agg_add_group(struct list *group, struct pim_upstream *up, break; } + if (nbr) { + if (PIM_DEBUG_TRACE) { + char src_str[INET_ADDRSTRLEN]; + + pim_inet4_dump("", nbr->source_addr, src_str, + sizeof(src_str)); + zlog_debug( + "up %s add to nbr %s/%s jp-agg-list", + up->sg_str, + up->rpf.source_nexthop.interface->name, + src_str); + } + } + if (!js) { js = XCALLOC(MTYPE_PIM_JP_AGG_SOURCE, sizeof(struct pim_jp_sources)); @@ -314,10 +349,11 @@ void pim_jp_agg_switch_interface(struct pim_rpf *orpf, struct pim_rpf *nrpf, /* send Prune(S,G) to the old upstream neighbor */ if (opius) - pim_jp_agg_add_group(opius->us, up, false); + pim_jp_agg_add_group(opius->us, up, false, NULL); /* send Join(S,G) to the current upstream neighbor */ - pim_jp_agg_add_group(npius->us, up, true); + if (npius) + pim_jp_agg_add_group(npius->us, up, true, NULL); } @@ -331,10 +367,10 @@ void pim_jp_agg_single_upstream_send(struct pim_rpf *rpf, static bool first = true; /* skip JP upstream messages if source is directly connected */ - if (!up || !rpf->source_nexthop.interface || pim_if_connected_to_source( - rpf->source_nexthop - .interface, - up->sg.src)) + if (!up || !rpf->source_nexthop.interface || + pim_if_connected_to_source(rpf->source_nexthop.interface, + up->sg.src) || + if_is_loopback_or_vrf(rpf->source_nexthop.interface)) return; if (first) { diff --git a/pimd/pim_jp_agg.h b/pimd/pim_jp_agg.h index aa21aa9816..d88ff8892b 100644 --- a/pimd/pim_jp_agg.h +++ b/pimd/pim_jp_agg.h @@ -37,10 +37,11 @@ void pim_jp_agg_group_list_free(struct pim_jp_agg_group *jag); int pim_jp_agg_group_list_cmp(void *arg1, void *arg2); void pim_jp_agg_clear_group(struct list *group); -void pim_jp_agg_remove_group(struct list *group, struct pim_upstream *up); +void pim_jp_agg_remove_group(struct list *group, struct pim_upstream *up, + struct pim_neighbor *nbr); void pim_jp_agg_add_group(struct list *group, struct pim_upstream *up, - bool is_join); + bool is_join, struct pim_neighbor *nbr); void pim_jp_agg_switch_interface(struct pim_rpf *orpf, struct pim_rpf *nrpf, struct pim_upstream *up); diff --git a/pimd/pim_macro.c b/pimd/pim_macro.c index 908026ab14..c6961d30c2 100644 --- a/pimd/pim_macro.c +++ b/pimd/pim_macro.c @@ -45,11 +45,9 @@ static int downstream_jpstate_isjoined(const struct pim_ifchannel *ch) case PIM_IFJOIN_PRUNE_TMP: case PIM_IFJOIN_PRUNE_PENDING_TMP: return 0; - break; case PIM_IFJOIN_JOIN: case PIM_IFJOIN_PRUNE_PENDING: return 1; - break; } return 0; } @@ -91,11 +89,11 @@ int pim_macro_chisin_joins(const struct pim_ifchannel *ch) lost_assert(S,G) = { all interfaces I such that - lost_assert(S,G,I) == TRUE } + lost_assert(S,G,I) == true } bool lost_assert(S,G,I) { if ( RPF_interface(S) == I ) { - return FALSE + return false } else { return ( AssertWinner(S,G,I) != NULL AND AssertWinner(S,G,I) != me AND @@ -115,8 +113,7 @@ int pim_macro_ch_lost_assert(const struct pim_ifchannel *ch) ifp = ch->interface; if (!ifp) { - zlog_warn("%s: (S,G)=%s: null interface", __PRETTY_FUNCTION__, - ch->sg_str); + zlog_warn("%s: (S,G)=%s: null interface", __func__, ch->sg_str); return 0; /* false */ } @@ -127,7 +124,7 @@ int pim_macro_ch_lost_assert(const struct pim_ifchannel *ch) pim_ifp = ifp->info; if (!pim_ifp) { zlog_warn("%s: (S,G)=%s: multicast not enabled on interface %s", - __PRETTY_FUNCTION__, ch->sg_str, ifp->name); + __func__, ch->sg_str, ifp->name); return 0; /* false */ } @@ -150,7 +147,7 @@ int pim_macro_ch_lost_assert(const struct pim_ifchannel *ch) pim_include(S,G) = { all interfaces I such that: - ( (I_am_DR( I ) AND lost_assert(S,G,I) == FALSE ) + ( (I_am_DR( I ) AND lost_assert(S,G,I) == false ) OR AssertWinner(S,G,I) == me ) AND local_receiver_include(S,G,I) } @@ -160,10 +157,11 @@ int pim_macro_ch_lost_assert(const struct pim_ifchannel *ch) int pim_macro_chisin_pim_include(const struct pim_ifchannel *ch) { struct pim_interface *pim_ifp = ch->interface->info; + bool mlag_active = false; if (!pim_ifp) { zlog_warn("%s: (S,G)=%s: multicast not enabled on interface %s", - __PRETTY_FUNCTION__, ch->sg_str, ch->interface->name); + __func__, ch->sg_str, ch->interface->name); return 0; /* false */ } @@ -175,10 +173,22 @@ int pim_macro_chisin_pim_include(const struct pim_ifchannel *ch) if (ch->ifassert_winner.s_addr == pim_ifp->primary_address.s_addr) return 1; /* true */ + /* + * When we have a activeactive interface we need to signal + * that this interface is interesting to the upstream + * decision to JOIN *if* we are syncing over the interface + */ + if (pim_ifp->activeactive) { + struct pim_upstream *up = ch->upstream; + + if (PIM_UPSTREAM_FLAG_TEST_MLAG_INTERFACE(up->flags)) + mlag_active = true; + } + return ( /* I_am_DR( I ) ? */ - PIM_I_am_DR(pim_ifp) && - /* lost_assert(S,G,I) == FALSE ? */ + (PIM_I_am_DR(pim_ifp) || mlag_active) && + /* lost_assert(S,G,I) == false ? */ (!pim_macro_ch_lost_assert(ch))); } @@ -223,12 +233,11 @@ int pim_macro_ch_could_assert_eval(const struct pim_ifchannel *ch) ifp = ch->interface; if (!ifp) { - zlog_warn("%s: (S,G)=%s: null interface", __PRETTY_FUNCTION__, - ch->sg_str); + zlog_warn("%s: (S,G)=%s: null interface", __func__, ch->sg_str); return 0; /* false */ } - /* SPTbit(S,G) == TRUE */ + /* SPTbit(S,G) == true */ if (ch->upstream->sptbit == PIM_UPSTREAM_SPTBIT_FALSE) return 0; /* false */ @@ -272,9 +281,9 @@ struct pim_assert_metric pim_macro_spt_assert_metric(const struct pim_rpf *rpf, following pseudocode: assert_metric my_assert_metric(S,G,I) { - if( CouldAssert(S,G,I) == TRUE ) { + if( CouldAssert(S,G,I) == true ) { return spt_assert_metric(S,I) - } else if( CouldAssert(*,G,I) == TRUE ) { + } else if( CouldAssert(*,G,I) == true ) { return rpt_assert_metric(G,I) } else { return infinite_assert_metric() @@ -365,11 +374,11 @@ int pim_macro_chisin_oiflist(const struct pim_ifchannel *ch) (+) ( pim_include(*,G) (-) pim_exclude(S,G) ) (-) lost_assert(*,G) (+) joins(S,G) ) ) - OR (local_receiver_include(S,G,I) == TRUE + OR (local_receiver_include(S,G,I) == true AND (I_am_DR(I) OR (AssertWinner(S,G,I) == me))) - OR ((RPF_interface(S) == I) AND (JoinDesired(S,G) == TRUE)) - OR ((RPF_interface(RP(G)) == I) AND (JoinDesired(*,G) == TRUE) - AND (SPTbit(S,G) == FALSE)) + OR ((RPF_interface(S) == I) AND (JoinDesired(S,G) == true)) + OR ((RPF_interface(RP(G)) == I) AND (JoinDesired(*,G) == true) + AND (SPTbit(S,G) == false)) AssertTrackingDesired(S,G,I) is true on any interface in which an (S,G) assert might affect our behavior. @@ -381,15 +390,14 @@ int pim_macro_assert_tracking_desired_eval(const struct pim_ifchannel *ch) ifp = ch->interface; if (!ifp) { - zlog_warn("%s: (S,G)=%s: null interface", __PRETTY_FUNCTION__, - ch->sg_str); + zlog_warn("%s: (S,G)=%s: null interface", __func__, ch->sg_str); return 0; /* false */ } pim_ifp = ifp->info; if (!pim_ifp) { zlog_warn("%s: (S,G)=%s: multicast not enabled on interface %s", - __PRETTY_FUNCTION__, ch->sg_str, ch->interface->name); + __func__, ch->sg_str, ch->interface->name); return 0; /* false */ } diff --git a/pimd/pim_main.c b/pimd/pim_main.c index 5a8991c4c0..132d913f68 100644 --- a/pimd/pim_main.c +++ b/pimd/pim_main.c @@ -29,7 +29,6 @@ #include "memory.h" #include "vrf.h" -#include "memory_vty.h" #include "filter.h" #include "vty.h" #include "sigevent.h" @@ -38,6 +37,7 @@ #include "plist.h" #include "vrf.h" #include "libfrr.h" +#include "routemap.h" #include "pimd.h" #include "pim_instance.h" @@ -47,6 +47,7 @@ #include "pim_msdp.h" #include "pim_iface.h" #include "pim_bfd.h" +#include "pim_mlag.h" #include "pim_errors.h" extern struct host host; @@ -71,8 +72,11 @@ struct zebra_privs_t pimd_privs = { .cap_num_p = array_size(_caps_p), .cap_num_i = 0}; -static const struct frr_yang_module_info *pimd_yang_modules[] = { +static const struct frr_yang_module_info *const pimd_yang_modules[] = { + &frr_filter_info, &frr_interface_info, + &frr_route_map_info, + &frr_vrf_info, }; FRR_DAEMON_INFO(pimd, PIM, .vty_port = PIMD_VTY_PORT, @@ -127,8 +131,11 @@ int main(int argc, char **argv, char **envp) /* * Initialize zclient "update" and "lookup" sockets */ + if_zapi_callbacks(pim_ifp_create, pim_ifp_up, + pim_ifp_down, pim_ifp_destroy); pim_zebra_init(); pim_bfd_init(); + pim_mlag_init(); frr_config_fork(); diff --git a/pimd/pim_memory.c b/pimd/pim_memory.c index 2bbab67e45..6bc8062c4b 100644 --- a/pimd/pim_memory.c +++ b/pimd/pim_memory.c @@ -51,5 +51,5 @@ DEFINE_MTYPE(PIMD, PIM_JP_AGG_SOURCE, "PIM JP AGG Source") DEFINE_MTYPE(PIMD, PIM_PIM_INSTANCE, "PIM global state") DEFINE_MTYPE(PIMD, PIM_NEXTHOP_CACHE, "PIM nexthop cache state") DEFINE_MTYPE(PIMD, PIM_SSM_INFO, "PIM SSM configuration") -DEFINE_MTYPE(PIMD, PIM_SPT_PLIST_NAME, "PIM SPT Prefix List Name") +DEFINE_MTYPE(PIMD, PIM_PLIST_NAME, "PIM Prefix List Names") DEFINE_MTYPE(PIMD, PIM_VXLAN_SG, "PIM VxLAN mroute cache") diff --git a/pimd/pim_memory.h b/pimd/pim_memory.h index e5ca57a15d..6beeb60075 100644 --- a/pimd/pim_memory.h +++ b/pimd/pim_memory.h @@ -50,7 +50,7 @@ DECLARE_MTYPE(PIM_JP_AGG_SOURCE) DECLARE_MTYPE(PIM_PIM_INSTANCE) DECLARE_MTYPE(PIM_NEXTHOP_CACHE) DECLARE_MTYPE(PIM_SSM_INFO) -DECLARE_MTYPE(PIM_SPT_PLIST_NAME); +DECLARE_MTYPE(PIM_PLIST_NAME); DECLARE_MTYPE(PIM_VXLAN_SG) #endif /* _QUAGGA_PIM_MEMORY_H */ diff --git a/pimd/pim_mlag.c b/pimd/pim_mlag.c new file mode 100644 index 0000000000..a06c0a6f4e --- /dev/null +++ b/pimd/pim_mlag.c @@ -0,0 +1,1101 @@ +/* + * This is an implementation of PIM MLAG Functionality + * + * Module name: PIM MLAG + * + * Author: sathesh Kumar karra + * + * Copyright (C) 2019 Cumulus Networks http://www.cumulusnetworks.com + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include + +#include "pimd.h" +#include "pim_mlag.h" +#include "pim_upstream.h" +#include "pim_vxlan.h" + +extern struct zclient *zclient; + +#define PIM_MLAG_METADATA_LEN 4 + +/*********************ACtual Data processing *****************************/ +/* TBD: There can be duplicate updates to FIB***/ +#define PIM_MLAG_ADD_OIF_TO_OIL(ch, ch_oil) \ + do { \ + if (PIM_DEBUG_MLAG) \ + zlog_debug( \ + "%s: add Dual-active Interface to %s " \ + "to oil:%s", \ + __func__, ch->interface->name, ch->sg_str); \ + pim_channel_update_oif_mute(ch_oil, ch->interface->info); \ + } while (0) + +#define PIM_MLAG_DEL_OIF_TO_OIL(ch, ch_oil) \ + do { \ + if (PIM_DEBUG_MLAG) \ + zlog_debug( \ + "%s: del Dual-active Interface to %s " \ + "to oil:%s", \ + __func__, ch->interface->name, ch->sg_str); \ + pim_channel_update_oif_mute(ch_oil, ch->interface->info); \ + } while (0) + + +static void pim_mlag_calculate_df_for_ifchannels(struct pim_upstream *up, + bool is_df) +{ + struct listnode *chnode; + struct listnode *chnextnode; + struct pim_ifchannel *ch; + struct pim_interface *pim_ifp = NULL; + struct channel_oil *ch_oil = NULL; + + ch_oil = (up) ? up->channel_oil : NULL; + + if (!ch_oil) + return; + + if (PIM_DEBUG_MLAG) + zlog_debug("%s: Calculating DF for Dual active if-channel%s", + __func__, up->sg_str); + + for (ALL_LIST_ELEMENTS(up->ifchannels, chnode, chnextnode, ch)) { + pim_ifp = (ch->interface) ? ch->interface->info : NULL; + if (!pim_ifp || !PIM_I_am_DualActive(pim_ifp)) + continue; + + if (is_df) + PIM_MLAG_ADD_OIF_TO_OIL(ch, ch_oil); + else + PIM_MLAG_DEL_OIF_TO_OIL(ch, ch_oil); + } +} + +static void pim_mlag_inherit_mlag_flags(struct pim_upstream *up, bool is_df) +{ + struct listnode *listnode; + struct pim_upstream *child; + struct listnode *chnode; + struct listnode *chnextnode; + struct pim_ifchannel *ch; + struct pim_interface *pim_ifp = NULL; + struct channel_oil *ch_oil = NULL; + + if (PIM_DEBUG_MLAG) + zlog_debug("%s: Updating DF for uptream:%s children", __func__, + up->sg_str); + + + for (ALL_LIST_ELEMENTS(up->ifchannels, chnode, chnextnode, ch)) { + pim_ifp = (ch->interface) ? ch->interface->info : NULL; + if (!pim_ifp || !PIM_I_am_DualActive(pim_ifp)) + continue; + + for (ALL_LIST_ELEMENTS_RO(up->sources, listnode, child)) { + if (PIM_DEBUG_MLAG) + zlog_debug("%s: Updating DF for child:%s", + __func__, child->sg_str); + ch_oil = (child) ? child->channel_oil : NULL; + + if (!ch_oil) + continue; + + if (is_df) + PIM_MLAG_ADD_OIF_TO_OIL(ch, ch_oil); + else + PIM_MLAG_DEL_OIF_TO_OIL(ch, ch_oil); + } + } +} + +/******************************* pim upstream sync **************************/ +/* Update DF role for the upstream entry and return true on role change */ +bool pim_mlag_up_df_role_update(struct pim_instance *pim, + struct pim_upstream *up, bool is_df, const char *reason) +{ + struct channel_oil *c_oil = up->channel_oil; + bool old_is_df = !PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(up->flags); + struct pim_interface *vxlan_ifp; + + if (is_df == old_is_df) { + if (PIM_DEBUG_MLAG) + zlog_debug( + "%s: Ignoring Role update for %s, since no change", + __func__, up->sg_str); + return false; + } + + if (PIM_DEBUG_MLAG) + zlog_debug("local MLAG mroute %s role changed to %s based on %s", + up->sg_str, is_df ? "df" : "non-df", reason); + + if (is_df) + PIM_UPSTREAM_FLAG_UNSET_MLAG_NON_DF(up->flags); + else + PIM_UPSTREAM_FLAG_SET_MLAG_NON_DF(up->flags); + + + /* + * This Upstream entry synced to peer Because of Dual-active + * Interface configuration + */ + if (PIM_UPSTREAM_FLAG_TEST_MLAG_INTERFACE(up->flags)) { + pim_mlag_inherit_mlag_flags(up, is_df); + pim_mlag_calculate_df_for_ifchannels(up, is_df); + } + + /* If the DF role has changed check if ipmr-lo needs to be + * muted/un-muted. Active-Active devices and vxlan termination + * devices (ipmr-lo) are suppressed on the non-DF. + * This may leave the mroute with the empty OIL in which case the + * the forwarding entry's sole purpose is to just blackhole the flow + * headed to the switch. + */ + if (c_oil) { + vxlan_ifp = pim_vxlan_get_term_ifp(pim); + if (vxlan_ifp) + pim_channel_update_oif_mute(c_oil, vxlan_ifp); + } + + /* If DF role changed on a (*,G) termination mroute update the + * associated DF role on the inherited (S,G) entries + */ + if ((up->sg.src.s_addr == INADDR_ANY) && + PIM_UPSTREAM_FLAG_TEST_MLAG_VXLAN(up->flags)) + pim_vxlan_inherit_mlag_flags(pim, up, true /* inherit */); + + return true; +} + +/* Run per-upstream entry DF election and return true on role change */ +static bool pim_mlag_up_df_role_elect(struct pim_instance *pim, + struct pim_upstream *up) +{ + bool is_df; + uint32_t peer_cost; + uint32_t local_cost; + bool rv; + + if (!pim_up_mlag_is_local(up)) + return false; + + /* We are yet to rx a status update from the local MLAG daemon so + * we will assume DF status. + */ + if (!(router->mlag_flags & PIM_MLAGF_STATUS_RXED)) + return pim_mlag_up_df_role_update(pim, up, + true /*is_df*/, "mlagd-down"); + + /* If not connected to peer assume DF role on the MLAG primary + * switch (and non-DF on the secondary switch. + */ + if (!(router->mlag_flags & PIM_MLAGF_PEER_CONN_UP)) { + is_df = (router->mlag_role == MLAG_ROLE_PRIMARY) ? true : false; + return pim_mlag_up_df_role_update(pim, up, + is_df, "peer-down"); + } + + /* If MLAG peer session is up but zebra is down on the peer + * assume DF role. + */ + if (!(router->mlag_flags & PIM_MLAGF_PEER_ZEBRA_UP)) + return pim_mlag_up_df_role_update(pim, up, + true /*is_df*/, "zebra-down"); + + /* If we are connected to peer switch but don't have a mroute + * from it we have to assume non-DF role to avoid duplicates. + * Note: When the peer connection comes up we wait for initial + * replay to complete before moving "strays" i.e. local-mlag-mroutes + * without a peer reference to non-df role. + */ + if (!PIM_UPSTREAM_FLAG_TEST_MLAG_PEER(up->flags)) + return pim_mlag_up_df_role_update(pim, up, + false /*is_df*/, "no-peer-mroute"); + + /* switch with the lowest RPF cost wins. if both switches have the same + * cost MLAG role is used as a tie breaker (MLAG primary wins). + */ + peer_cost = up->mlag.peer_mrib_metric; + local_cost = pim_up_mlag_local_cost(up); + if (local_cost == peer_cost) { + is_df = (router->mlag_role == MLAG_ROLE_PRIMARY) ? true : false; + rv = pim_mlag_up_df_role_update(pim, up, is_df, "equal-cost"); + } else { + is_df = (local_cost < peer_cost) ? true : false; + rv = pim_mlag_up_df_role_update(pim, up, is_df, "cost"); + } + + return rv; +} + +/* Handle upstream entry add from the peer MLAG switch - + * - if a local entry doesn't exist one is created with reference + * _MLAG_PEER + * - if a local entry exists and has a MLAG OIF DF election is run. + * the non-DF switch stop forwarding traffic to MLAG devices. + */ +static void pim_mlag_up_peer_add(struct mlag_mroute_add *msg) +{ + struct pim_upstream *up; + struct pim_instance *pim; + int flags = 0; + struct prefix_sg sg; + struct vrf *vrf; + char sg_str[PIM_SG_LEN]; + + memset(&sg, 0, sizeof(struct prefix_sg)); + sg.src.s_addr = htonl(msg->source_ip); + sg.grp.s_addr = htonl(msg->group_ip); + if (PIM_DEBUG_MLAG) + pim_str_sg_set(&sg, sg_str); + + if (PIM_DEBUG_MLAG) + zlog_debug("peer MLAG mroute add %s:%s cost %d", + msg->vrf_name, sg_str, msg->cost_to_rp); + + /* XXX - this is not correct. we MUST cache updates to avoid losing + * an entry because of race conditions with the peer switch. + */ + vrf = vrf_lookup_by_name(msg->vrf_name); + if (!vrf) { + if (PIM_DEBUG_MLAG) + zlog_debug("peer MLAG mroute add failed %s:%s; no vrf", + msg->vrf_name, sg_str); + return; + } + pim = vrf->info; + + up = pim_upstream_find(pim, &sg); + if (up) { + /* upstream already exists; create peer reference if it + * doesn't already exist. + */ + if (!PIM_UPSTREAM_FLAG_TEST_MLAG_PEER(up->flags)) + pim_upstream_ref(up, PIM_UPSTREAM_FLAG_MASK_MLAG_PEER, + __func__); + } else { + PIM_UPSTREAM_FLAG_SET_MLAG_PEER(flags); + up = pim_upstream_add(pim, &sg, NULL /*iif*/, flags, __func__, + NULL /*if_ch*/); + + if (!up) { + if (PIM_DEBUG_MLAG) + zlog_debug("peer MLAG mroute add failed %s:%s", + vrf->name, sg_str); + return; + } + } + up->mlag.peer_mrib_metric = msg->cost_to_rp; + pim_mlag_up_df_role_elect(pim, up); +} + +/* Handle upstream entry del from the peer MLAG switch - + * - peer reference is removed. this can result in the upstream + * being deleted altogether. + * - if a local entry continues to exisy and has a MLAG OIF DF election + * is re-run (at the end of which the local entry will be the DF). + */ +static struct pim_upstream *pim_mlag_up_peer_deref(struct pim_instance *pim, + struct pim_upstream *up) +{ + if (!PIM_UPSTREAM_FLAG_TEST_MLAG_PEER(up->flags)) + return up; + + PIM_UPSTREAM_FLAG_UNSET_MLAG_PEER(up->flags); + up = pim_upstream_del(pim, up, __func__); + if (up) + pim_mlag_up_df_role_elect(pim, up); + + return up; +} + +static void pim_mlag_up_peer_del(struct mlag_mroute_del *msg) +{ + struct pim_upstream *up; + struct pim_instance *pim; + struct prefix_sg sg; + struct vrf *vrf; + char sg_str[PIM_SG_LEN]; + + memset(&sg, 0, sizeof(struct prefix_sg)); + sg.src.s_addr = htonl(msg->source_ip); + sg.grp.s_addr = htonl(msg->group_ip); + if (PIM_DEBUG_MLAG) + pim_str_sg_set(&sg, sg_str); + + if (PIM_DEBUG_MLAG) + zlog_debug("peer MLAG mroute del %s:%s", msg->vrf_name, + sg_str); + + vrf = vrf_lookup_by_name(msg->vrf_name); + if (!vrf) { + if (PIM_DEBUG_MLAG) + zlog_debug("peer MLAG mroute del skipped %s:%s; no vrf", + msg->vrf_name, sg_str); + return; + } + pim = vrf->info; + + up = pim_upstream_find(pim, &sg); + if (!up) { + if (PIM_DEBUG_MLAG) + zlog_debug("peer MLAG mroute del skipped %s:%s; no up", + vrf->name, sg_str); + return; + } + + (void)pim_mlag_up_peer_deref(pim, up); +} + +/* When we lose connection to the local MLAG daemon we can drop all peer + * references. + */ +static void pim_mlag_up_peer_del_all(void) +{ + struct list *temp = list_new(); + struct pim_upstream *up; + struct vrf *vrf; + struct pim_instance *pim; + + /* + * So why these gyrations? + * pim->upstream_head has the list of *,G and S,G + * that are in the system. The problem of course + * is that it is an ordered list: + * (*,G1) -> (S1,G1) -> (S2,G2) -> (S3, G2) -> (*,G2) -> (S1,G2) + * And the *,G1 has pointers to S1,G1 and S2,G1 + * if we delete *,G1 then we have a situation where + * S1,G1 and S2,G2 can be deleted as well. Then a + * simple ALL_LIST_ELEMENTS will have the next listnode + * pointer become invalid and we crash. + * So let's grab the list of MLAG_PEER upstreams + * add a refcount put on another list and delete safely + */ + RB_FOREACH(vrf, vrf_name_head, &vrfs_by_name) { + pim = vrf->info; + frr_each (rb_pim_upstream, &pim->upstream_head, up) { + if (!PIM_UPSTREAM_FLAG_TEST_MLAG_PEER(up->flags)) + continue; + listnode_add(temp, up); + /* + * Add a reference since we are adding to this + * list for deletion + */ + up->ref_count++; + } + + while (temp->count) { + up = listnode_head(temp); + listnode_delete(temp, up); + + up = pim_mlag_up_peer_deref(pim, up); + /* + * This is the deletion of the reference added + * above + */ + if (up) + pim_upstream_del(pim, up, __func__); + } + } + + list_delete(&temp); +} + +/* Send upstream entry to the local MLAG daemon (which will subsequently + * send it to the peer MLAG switch). + */ +static void pim_mlag_up_local_add_send(struct pim_instance *pim, + struct pim_upstream *up) +{ + struct stream *s = NULL; + struct vrf *vrf = pim->vrf; + + if (!(router->mlag_flags & PIM_MLAGF_LOCAL_CONN_UP)) + return; + + s = stream_new(sizeof(struct mlag_mroute_add) + PIM_MLAG_METADATA_LEN); + if (!s) + return; + + if (PIM_DEBUG_MLAG) + zlog_debug("local MLAG mroute add %s:%s", + vrf->name, up->sg_str); + + ++router->mlag_stats.msg.mroute_add_tx; + + stream_putl(s, MLAG_MROUTE_ADD); + stream_put(s, vrf->name, VRF_NAMSIZ); + stream_putl(s, ntohl(up->sg.src.s_addr)); + stream_putl(s, ntohl(up->sg.grp.s_addr)); + + stream_putl(s, pim_up_mlag_local_cost(up)); + /* XXX - who is addding*/ + stream_putl(s, MLAG_OWNER_VXLAN); + /* XXX - am_i_DR field should be removed */ + stream_putc(s, false); + stream_putc(s, !(PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(up->flags))); + stream_putl(s, vrf->vrf_id); + /* XXX - this field is a No-op for VXLAN*/ + stream_put(s, NULL, INTERFACE_NAMSIZ); + + stream_fifo_push_safe(router->mlag_fifo, s); + pim_mlag_signal_zpthread(); +} + +static void pim_mlag_up_local_del_send(struct pim_instance *pim, + struct pim_upstream *up) +{ + struct stream *s = NULL; + struct vrf *vrf = pim->vrf; + + if (!(router->mlag_flags & PIM_MLAGF_LOCAL_CONN_UP)) + return; + + s = stream_new(sizeof(struct mlag_mroute_del) + PIM_MLAG_METADATA_LEN); + if (!s) + return; + + if (PIM_DEBUG_MLAG) + zlog_debug("local MLAG mroute del %s:%s", + vrf->name, up->sg_str); + + ++router->mlag_stats.msg.mroute_del_tx; + + stream_putl(s, MLAG_MROUTE_DEL); + stream_put(s, vrf->name, VRF_NAMSIZ); + stream_putl(s, ntohl(up->sg.src.s_addr)); + stream_putl(s, ntohl(up->sg.grp.s_addr)); + /* XXX - who is adding */ + stream_putl(s, MLAG_OWNER_VXLAN); + stream_putl(s, vrf->vrf_id); + /* XXX - this field is a No-op for VXLAN */ + stream_put(s, NULL, INTERFACE_NAMSIZ); + + /* XXX - is this the the most optimal way to do things */ + stream_fifo_push_safe(router->mlag_fifo, s); + pim_mlag_signal_zpthread(); +} + + +/* Called when a local upstream entry is created or if it's cost changes */ +void pim_mlag_up_local_add(struct pim_instance *pim, + struct pim_upstream *up) +{ + pim_mlag_up_df_role_elect(pim, up); + /* XXX - need to add some dup checks here */ + pim_mlag_up_local_add_send(pim, up); +} + +/* Called when local MLAG reference is removed from an upstream entry */ +void pim_mlag_up_local_del(struct pim_instance *pim, + struct pim_upstream *up) +{ + pim_mlag_up_df_role_elect(pim, up); + pim_mlag_up_local_del_send(pim, up); +} + +/* When connection to local MLAG daemon is established all the local + * MLAG upstream entries are replayed to it. + */ +static void pim_mlag_up_local_replay(void) +{ + struct pim_upstream *up; + struct vrf *vrf; + struct pim_instance *pim; + + RB_FOREACH(vrf, vrf_name_head, &vrfs_by_name) { + pim = vrf->info; + frr_each (rb_pim_upstream, &pim->upstream_head, up) { + if (pim_up_mlag_is_local(up)) + pim_mlag_up_local_add_send(pim, up); + } + } +} + +/* on local/peer mlag connection and role changes the DF status needs + * to be re-evaluated + */ +static void pim_mlag_up_local_reeval(bool mlagd_send, const char *reason_code) +{ + struct pim_upstream *up; + struct vrf *vrf; + struct pim_instance *pim; + + if (PIM_DEBUG_MLAG) + zlog_debug("%s re-run DF election because of %s", + __func__, reason_code); + RB_FOREACH(vrf, vrf_name_head, &vrfs_by_name) { + pim = vrf->info; + frr_each (rb_pim_upstream, &pim->upstream_head, up) { + if (!pim_up_mlag_is_local(up)) + continue; + /* if role changes re-send to peer */ + if (pim_mlag_up_df_role_elect(pim, up) && + mlagd_send) + pim_mlag_up_local_add_send(pim, up); + } + } +} + +/*****************PIM Actions for MLAG state changes**********************/ + +/* notify the anycast VTEP component about state changes */ +static inline void pim_mlag_vxlan_state_update(void) +{ + bool enable = !!(router->mlag_flags & PIM_MLAGF_STATUS_RXED); + bool peer_state = !!(router->mlag_flags & PIM_MLAGF_PEER_CONN_UP); + + pim_vxlan_mlag_update(enable, peer_state, router->mlag_role, + router->peerlink_rif_p, &router->local_vtep_ip); + +} + +/**************End of PIM Actions for MLAG State changes******************/ + + +/********************API to process PIM MLAG Data ************************/ + +static void pim_mlag_process_mlagd_state_change(struct mlag_status msg) +{ + bool role_chg = false; + bool state_chg = false; + bool notify_vxlan = false; + struct interface *peerlink_rif_p; + char buf[MLAG_ROLE_STRSIZE]; + + if (PIM_DEBUG_MLAG) + zlog_debug("%s: msg dump: my_role: %s, peer_state: %s", + __func__, + mlag_role2str(msg.my_role, buf, sizeof(buf)), + (msg.peer_state == MLAG_STATE_RUNNING ? "RUNNING" + : "DOWN")); + + if (!(router->mlag_flags & PIM_MLAGF_LOCAL_CONN_UP)) { + if (PIM_DEBUG_MLAG) + zlog_debug("%s: msg ignored mlagd process state down", + __func__); + return; + } + ++router->mlag_stats.msg.mlag_status_updates; + + /* evaluate the changes first */ + if (router->mlag_role != msg.my_role) { + role_chg = true; + notify_vxlan = true; + router->mlag_role = msg.my_role; + } + + strlcpy(router->peerlink_rif, msg.peerlink_rif, + sizeof(router->peerlink_rif)); + + /* XXX - handle the case where we may rx the interface name from the + * MLAG daemon before we get the interface from zebra. + */ + peerlink_rif_p = if_lookup_by_name(router->peerlink_rif, VRF_DEFAULT); + if (router->peerlink_rif_p != peerlink_rif_p) { + router->peerlink_rif_p = peerlink_rif_p; + notify_vxlan = true; + } + + if (msg.peer_state == MLAG_STATE_RUNNING) { + if (!(router->mlag_flags & PIM_MLAGF_PEER_CONN_UP)) { + state_chg = true; + notify_vxlan = true; + router->mlag_flags |= PIM_MLAGF_PEER_CONN_UP; + } + router->connected_to_mlag = true; + } else { + if (router->mlag_flags & PIM_MLAGF_PEER_CONN_UP) { + ++router->mlag_stats.peer_session_downs; + state_chg = true; + notify_vxlan = true; + router->mlag_flags &= ~PIM_MLAGF_PEER_CONN_UP; + } + router->connected_to_mlag = false; + } + + /* apply the changes */ + /* when connection to mlagd comes up we hold send mroutes till we have + * rxed the status and had a chance to re-valuate DF state + */ + if (!(router->mlag_flags & PIM_MLAGF_STATUS_RXED)) { + router->mlag_flags |= PIM_MLAGF_STATUS_RXED; + pim_mlag_vxlan_state_update(); + /* on session up re-eval DF status */ + pim_mlag_up_local_reeval(false /*mlagd_send*/, "mlagd_up"); + /* replay all the upstream entries to the local MLAG daemon */ + pim_mlag_up_local_replay(); + return; + } + + if (notify_vxlan) + pim_mlag_vxlan_state_update(); + + if (state_chg) { + if (!(router->mlag_flags & PIM_MLAGF_PEER_CONN_UP)) + /* when a connection goes down the primary takes over + * DF role for all entries + */ + pim_mlag_up_local_reeval(true /*mlagd_send*/, + "peer_down"); + else + /* XXX - when session comes up we need to wait for + * PEER_REPLAY_DONE before running re-election on + * local-mlag entries that are missing peer reference + */ + pim_mlag_up_local_reeval(true /*mlagd_send*/, + "peer_up"); + } else if (role_chg) { + /* MLAG role changed without a state change */ + pim_mlag_up_local_reeval(true /*mlagd_send*/, "role_chg"); + } +} + +static void pim_mlag_process_peer_frr_state_change(struct mlag_frr_status msg) +{ + if (PIM_DEBUG_MLAG) + zlog_debug( + "%s: msg dump: peer_frr_state: %s", __func__, + (msg.frr_state == MLAG_FRR_STATE_UP ? "UP" : "DOWN")); + + if (!(router->mlag_flags & PIM_MLAGF_LOCAL_CONN_UP)) { + if (PIM_DEBUG_MLAG) + zlog_debug("%s: msg ignored mlagd process state down", + __func__); + return; + } + ++router->mlag_stats.msg.peer_zebra_status_updates; + + /* evaluate the changes first */ + if (msg.frr_state == MLAG_FRR_STATE_UP) { + if (!(router->mlag_flags & PIM_MLAGF_PEER_ZEBRA_UP)) { + router->mlag_flags |= PIM_MLAGF_PEER_ZEBRA_UP; + /* XXX - when peer zebra comes up we need to wait for + * for some time to let the peer setup MDTs before + * before relinquishing DF status + */ + pim_mlag_up_local_reeval(true /*mlagd_send*/, + "zebra_up"); + } + } else { + if (router->mlag_flags & PIM_MLAGF_PEER_ZEBRA_UP) { + ++router->mlag_stats.peer_zebra_downs; + router->mlag_flags &= ~PIM_MLAGF_PEER_ZEBRA_UP; + /* when a peer zebra goes down we assume DF role */ + pim_mlag_up_local_reeval(true /*mlagd_send*/, + "zebra_down"); + } + } +} + +static void pim_mlag_process_vxlan_update(struct mlag_vxlan *msg) +{ + char addr_buf1[INET_ADDRSTRLEN]; + char addr_buf2[INET_ADDRSTRLEN]; + uint32_t local_ip; + + if (!(router->mlag_flags & PIM_MLAGF_LOCAL_CONN_UP)) { + if (PIM_DEBUG_MLAG) + zlog_debug("%s: msg ignored mlagd process state down", + __func__); + return; + } + + ++router->mlag_stats.msg.vxlan_updates; + router->anycast_vtep_ip.s_addr = htonl(msg->anycast_ip); + local_ip = htonl(msg->local_ip); + if (router->local_vtep_ip.s_addr != local_ip) { + router->local_vtep_ip.s_addr = local_ip; + pim_mlag_vxlan_state_update(); + } + + if (PIM_DEBUG_MLAG) { + inet_ntop(AF_INET, &router->local_vtep_ip, + addr_buf1, INET_ADDRSTRLEN); + inet_ntop(AF_INET, &router->anycast_vtep_ip, + addr_buf2, INET_ADDRSTRLEN); + + zlog_debug("%s: msg dump: local-ip:%s, anycast-ip:%s", + __func__, addr_buf1, addr_buf2); + } +} + +static void pim_mlag_process_mroute_add(struct mlag_mroute_add msg) +{ + if (PIM_DEBUG_MLAG) { + struct prefix_sg sg; + + sg.grp.s_addr = ntohl(msg.group_ip); + sg.src.s_addr = ntohl(msg.source_ip); + + zlog_debug( + "%s: msg dump: vrf_name: %s, s.ip: 0x%x, g.ip: 0x%x (%pSG4) cost: %u", + __func__, msg.vrf_name, msg.source_ip, msg.group_ip, + &sg, msg.cost_to_rp); + zlog_debug( + "(%pSG4)owner_id: %d, DR: %d, Dual active: %d, vrf_id: 0x%x intf_name: %s", + &sg, msg.owner_id, msg.am_i_dr, msg.am_i_dual_active, + msg.vrf_id, msg.intf_name); + } + + if (!(router->mlag_flags & PIM_MLAGF_LOCAL_CONN_UP)) { + if (PIM_DEBUG_MLAG) + zlog_debug("%s: msg ignored mlagd process state down", + __func__); + return; + } + + ++router->mlag_stats.msg.mroute_add_rx; + + pim_mlag_up_peer_add(&msg); +} + +static void pim_mlag_process_mroute_del(struct mlag_mroute_del msg) +{ + if (PIM_DEBUG_MLAG) { + struct prefix_sg sg; + + sg.grp.s_addr = ntohl(msg.group_ip); + sg.src.s_addr = ntohl(msg.source_ip); + zlog_debug( + "%s: msg dump: vrf_name: %s, s.ip: 0x%x, g.ip: 0x%x(%pSG4)", + __func__, msg.vrf_name, msg.source_ip, msg.group_ip, + &sg); + zlog_debug("(%pSG4)owner_id: %d, vrf_id: 0x%x intf_name: %s", + &sg, msg.owner_id, msg.vrf_id, msg.intf_name); + } + + if (!(router->mlag_flags & PIM_MLAGF_LOCAL_CONN_UP)) { + if (PIM_DEBUG_MLAG) + zlog_debug("%s: msg ignored mlagd process state down", + __func__); + return; + } + + ++router->mlag_stats.msg.mroute_del_rx; + + pim_mlag_up_peer_del(&msg); +} + +int pim_zebra_mlag_handle_msg(struct stream *s, int len) +{ + struct mlag_msg mlag_msg; + char buf[80]; + int rc = 0; + size_t length; + + rc = mlag_lib_decode_mlag_hdr(s, &mlag_msg, &length); + if (rc) + return (rc); + + if (PIM_DEBUG_MLAG) + zlog_debug("%s: Received msg type: %s length: %d, bulk_cnt: %d", + __func__, + mlag_lib_msgid_to_str(mlag_msg.msg_type, buf, + sizeof(buf)), + mlag_msg.data_len, mlag_msg.msg_cnt); + + switch (mlag_msg.msg_type) { + case MLAG_STATUS_UPDATE: { + struct mlag_status msg; + + rc = mlag_lib_decode_mlag_status(s, &msg); + if (rc) + return (rc); + pim_mlag_process_mlagd_state_change(msg); + } break; + case MLAG_PEER_FRR_STATUS: { + struct mlag_frr_status msg; + + rc = mlag_lib_decode_frr_status(s, &msg); + if (rc) + return (rc); + pim_mlag_process_peer_frr_state_change(msg); + } break; + case MLAG_VXLAN_UPDATE: { + struct mlag_vxlan msg; + + rc = mlag_lib_decode_vxlan_update(s, &msg); + if (rc) + return rc; + pim_mlag_process_vxlan_update(&msg); + } break; + case MLAG_MROUTE_ADD: { + struct mlag_mroute_add msg; + + rc = mlag_lib_decode_mroute_add(s, &msg, &length); + if (rc) + return (rc); + pim_mlag_process_mroute_add(msg); + } break; + case MLAG_MROUTE_DEL: { + struct mlag_mroute_del msg; + + rc = mlag_lib_decode_mroute_del(s, &msg, &length); + if (rc) + return (rc); + pim_mlag_process_mroute_del(msg); + } break; + case MLAG_MROUTE_ADD_BULK: { + struct mlag_mroute_add msg; + int i; + + for (i = 0; i < mlag_msg.msg_cnt; i++) { + rc = mlag_lib_decode_mroute_add(s, &msg, &length); + if (rc) + return (rc); + pim_mlag_process_mroute_add(msg); + } + } break; + case MLAG_MROUTE_DEL_BULK: { + struct mlag_mroute_del msg; + int i; + + for (i = 0; i < mlag_msg.msg_cnt; i++) { + rc = mlag_lib_decode_mroute_del(s, &msg, &length); + if (rc) + return (rc); + pim_mlag_process_mroute_del(msg); + } + } break; + default: + break; + } + return 0; +} + +/****************End of PIM Mesasge processing handler********************/ + +int pim_zebra_mlag_process_up(void) +{ + if (PIM_DEBUG_MLAG) + zlog_debug("%s: Received Process-Up from Mlag", __func__); + + /* + * Incase of local MLAG restart, PIM needs to replay all the data + * since MLAG is empty. + */ + router->connected_to_mlag = true; + router->mlag_flags |= PIM_MLAGF_LOCAL_CONN_UP; + return 0; +} + +static void pim_mlag_param_reset(void) +{ + /* reset the cached params and stats */ + router->mlag_flags &= ~(PIM_MLAGF_STATUS_RXED | + PIM_MLAGF_LOCAL_CONN_UP | + PIM_MLAGF_PEER_CONN_UP | + PIM_MLAGF_PEER_ZEBRA_UP); + router->local_vtep_ip.s_addr = INADDR_ANY; + router->anycast_vtep_ip.s_addr = INADDR_ANY; + router->mlag_role = MLAG_ROLE_NONE; + memset(&router->mlag_stats.msg, 0, sizeof(router->mlag_stats.msg)); + router->peerlink_rif[0] = '\0'; +} + +int pim_zebra_mlag_process_down(void) +{ + if (PIM_DEBUG_MLAG) + zlog_debug("%s: Received Process-Down from Mlag", __func__); + + /* Local CLAG is down, reset peer data and forward the traffic if + * we are DR + */ + if (router->mlag_flags & PIM_MLAGF_PEER_CONN_UP) + ++router->mlag_stats.peer_session_downs; + if (router->mlag_flags & PIM_MLAGF_PEER_ZEBRA_UP) + ++router->mlag_stats.peer_zebra_downs; + router->connected_to_mlag = false; + pim_mlag_param_reset(); + /* on mlagd session down re-eval DF status */ + pim_mlag_up_local_reeval(false /*mlagd_send*/, "mlagd_down"); + /* flush all peer references */ + pim_mlag_up_peer_del_all(); + /* notify the vxlan component */ + pim_mlag_vxlan_state_update(); + return 0; +} + +static int pim_mlag_register_handler(struct thread *thread) +{ + uint32_t bit_mask = 0; + + if (!zclient) + return -1; + + SET_FLAG(bit_mask, (1 << MLAG_STATUS_UPDATE)); + SET_FLAG(bit_mask, (1 << MLAG_MROUTE_ADD)); + SET_FLAG(bit_mask, (1 << MLAG_MROUTE_DEL)); + SET_FLAG(bit_mask, (1 << MLAG_DUMP)); + SET_FLAG(bit_mask, (1 << MLAG_MROUTE_ADD_BULK)); + SET_FLAG(bit_mask, (1 << MLAG_MROUTE_DEL_BULK)); + SET_FLAG(bit_mask, (1 << MLAG_PIM_CFG_DUMP)); + SET_FLAG(bit_mask, (1 << MLAG_VXLAN_UPDATE)); + SET_FLAG(bit_mask, (1 << MLAG_PEER_FRR_STATUS)); + + if (PIM_DEBUG_MLAG) + zlog_debug("%s: Posting Client Register to MLAG mask: 0x%x", + __func__, bit_mask); + + zclient_send_mlag_register(zclient, bit_mask); + return 0; +} + +void pim_mlag_register(void) +{ + if (router->mlag_process_register) + return; + + router->mlag_process_register = true; + + thread_add_event(router->master, pim_mlag_register_handler, NULL, 0, + NULL); +} + +static int pim_mlag_deregister_handler(struct thread *thread) +{ + if (!zclient) + return -1; + + if (PIM_DEBUG_MLAG) + zlog_debug("%s: Posting Client De-Register to MLAG from PIM", + __func__); + router->connected_to_mlag = false; + zclient_send_mlag_deregister(zclient); + return 0; +} + +void pim_mlag_deregister(void) +{ + /* if somebody still interested in the MLAG channel skip de-reg */ + if (router->pim_mlag_intf_cnt || pim_vxlan_do_mlag_reg()) + return; + + /* not registered; nothing do */ + if (!router->mlag_process_register) + return; + + router->mlag_process_register = false; + + thread_add_event(router->master, pim_mlag_deregister_handler, NULL, 0, + NULL); +} + +void pim_if_configure_mlag_dualactive(struct pim_interface *pim_ifp) +{ + if (!pim_ifp || !pim_ifp->pim || pim_ifp->activeactive == true) + return; + + if (PIM_DEBUG_MLAG) + zlog_debug("%s: Configuring active-active on Interface: %s", + __func__, "NULL"); + + pim_ifp->activeactive = true; + if (pim_ifp->pim) + pim_ifp->pim->inst_mlag_intf_cnt++; + + router->pim_mlag_intf_cnt++; + if (PIM_DEBUG_MLAG) + zlog_debug( + "%s: Total MLAG configured Interfaces on router: %d, Inst: %d", + __func__, router->pim_mlag_intf_cnt, + pim_ifp->pim->inst_mlag_intf_cnt); + + if (router->pim_mlag_intf_cnt == 1) { + /* + * atleast one Interface is configured for MLAG, send register + * to Zebra for receiving MLAG Updates + */ + pim_mlag_register(); + } +} + +void pim_if_unconfigure_mlag_dualactive(struct pim_interface *pim_ifp) +{ + if (!pim_ifp || !pim_ifp->pim || pim_ifp->activeactive == false) + return; + + if (PIM_DEBUG_MLAG) + zlog_debug("%s: UnConfiguring active-active on Interface: %s", + __func__, "NULL"); + + pim_ifp->activeactive = false; + pim_ifp->pim->inst_mlag_intf_cnt--; + + router->pim_mlag_intf_cnt--; + if (PIM_DEBUG_MLAG) + zlog_debug( + "%s: Total MLAG configured Interfaces on router: %d, Inst: %d", + __func__, router->pim_mlag_intf_cnt, + pim_ifp->pim->inst_mlag_intf_cnt); + + if (router->pim_mlag_intf_cnt == 0) { + /* + * all the Interfaces are MLAG un-configured, post MLAG + * De-register to Zebra + */ + pim_mlag_deregister(); + pim_mlag_param_reset(); + } +} + + +void pim_instance_mlag_init(struct pim_instance *pim) +{ + if (!pim) + return; + + pim->inst_mlag_intf_cnt = 0; +} + + +void pim_instance_mlag_terminate(struct pim_instance *pim) +{ + struct interface *ifp; + + if (!pim) + return; + + FOR_ALL_INTERFACES (pim->vrf, ifp) { + struct pim_interface *pim_ifp = ifp->info; + + if (!pim_ifp || pim_ifp->activeactive == false) + continue; + + pim_if_unconfigure_mlag_dualactive(pim_ifp); + } + pim->inst_mlag_intf_cnt = 0; +} + +void pim_mlag_terminate(void) +{ + stream_free(router->mlag_stream); + router->mlag_stream = NULL; + stream_fifo_free(router->mlag_fifo); + router->mlag_fifo = NULL; +} + +void pim_mlag_init(void) +{ + pim_mlag_param_reset(); + router->pim_mlag_intf_cnt = 0; + router->connected_to_mlag = false; + router->mlag_fifo = stream_fifo_new(); + router->zpthread_mlag_write = NULL; + router->mlag_stream = stream_new(MLAG_BUF_LIMIT); +} diff --git a/pimd/pim_mlag.h b/pimd/pim_mlag.h new file mode 100644 index 0000000000..b044c31c44 --- /dev/null +++ b/pimd/pim_mlag.h @@ -0,0 +1,54 @@ +/* + * This is an implementation of PIM MLAG Functionality + * + * Module name: PIM MLAG + * + * Author: sathesh Kumar karra + * + * Copyright (C) 2019 Cumulus Networks http://www.cumulusnetworks.com + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef __PIM_MLAG_H__ +#define __PIM_MLAG_H__ + +#include "mlag.h" +#include "pim_iface.h" + +extern void pim_mlag_init(void); +extern void pim_mlag_terminate(void); +extern void pim_instance_mlag_init(struct pim_instance *pim); +extern void pim_instance_mlag_terminate(struct pim_instance *pim); +extern void pim_if_configure_mlag_dualactive(struct pim_interface *pim_ifp); +extern void pim_if_unconfigure_mlag_dualactive(struct pim_interface *pim_ifp); +extern int pim_zebra_mlag_process_up(void); +extern int pim_zebra_mlag_process_down(void); +extern int pim_zebra_mlag_handle_msg(struct stream *msg, int len); + +/* pm_zpthread.c */ +extern int pim_mlag_signal_zpthread(void); +extern void pim_zpthread_init(void); +extern void pim_zpthread_terminate(void); + +extern void pim_mlag_register(void); +extern void pim_mlag_deregister(void); +extern void pim_mlag_up_local_add(struct pim_instance *pim, + struct pim_upstream *upstream); +extern void pim_mlag_up_local_del(struct pim_instance *pim, + struct pim_upstream *upstream); +extern bool pim_mlag_up_df_role_update(struct pim_instance *pim, + struct pim_upstream *up, bool is_df, + const char *reason); +#endif diff --git a/pimd/pim_mroute.c b/pimd/pim_mroute.c index 866a19fc98..0bccba397b 100644 --- a/pimd/pim_mroute.c +++ b/pimd/pim_mroute.c @@ -57,7 +57,7 @@ static int pim_mroute_set(struct pim_instance *pim, int enable) * We need to create the VRF table for the pim mroute_socket */ if (pim->vrf_id != VRF_DEFAULT) { - frr_elevate_privs(&pimd_privs) { + frr_with_privs(&pimd_privs) { data = pim->vrf->data.l.table_id; err = setsockopt(pim->mroute_socket, IPPROTO_IP, @@ -65,17 +65,16 @@ static int pim_mroute_set(struct pim_instance *pim, int enable) &data, data_len); if (err) { zlog_warn( - "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP, MRT_TABLE=%d): errno=%d: %s", - __FILE__, __PRETTY_FUNCTION__, - pim->mroute_socket, data, errno, - safe_strerror(errno)); + "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP, MRT_TABLE=%d): errno=%d: %s", + __FILE__, __func__, pim->mroute_socket, + data, errno, safe_strerror(errno)); return -1; } } } - frr_elevate_privs(&pimd_privs) { + frr_with_privs(&pimd_privs) { opt = enable ? MRT_INIT : MRT_DONE; /* * *BSD *cares* about what value we pass down @@ -86,11 +85,10 @@ static int pim_mroute_set(struct pim_instance *pim, int enable) opt, &data, data_len); if (err) { zlog_warn( - "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,%s=%d): errno=%d: %s", - __FILE__, __PRETTY_FUNCTION__, - pim->mroute_socket, - enable ? "MRT_INIT" : "MRT_DONE", data, errno, - safe_strerror(errno)); + "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,%s=%d): errno=%d: %s", + __FILE__, __func__, pim->mroute_socket, + enable ? "MRT_INIT" : "MRT_DONE", data, errno, + safe_strerror(errno)); return -1; } } @@ -147,7 +145,7 @@ static int pim_mroute_set(struct pim_instance *pim, int enable) return 0; } -static const char *igmpmsgtype2str[IGMPMSG_WRVIFWHOLE + 1] = { +static const char *const igmpmsgtype2str[IGMPMSG_WRVIFWHOLE + 1] = { "", "NOCACHE", "WRONGVIF", "WHOLEPKT", "WRVIFWHOLE"}; static int pim_mroute_msg_nocache(int fd, struct interface *ifp, @@ -168,7 +166,7 @@ static int pim_mroute_msg_nocache(int fd, struct interface *ifp, if (PIM_DEBUG_MROUTE_DETAIL) zlog_debug( "%s: Interface is not configured correctly to handle incoming packet: Could be !pim_ifp, !SM, !RP", - __PRETTY_FUNCTION__); + __func__); return 0; } @@ -181,7 +179,7 @@ static int pim_mroute_msg_nocache(int fd, struct interface *ifp, if (PIM_DEBUG_MROUTE_DETAIL) zlog_debug( "%s: Received incoming packet that doesn't originate on our seg", - __PRETTY_FUNCTION__); + __func__); return 0; } @@ -190,35 +188,30 @@ static int pim_mroute_msg_nocache(int fd, struct interface *ifp, sg.grp = msg->im_dst; if (!(PIM_I_am_DR(pim_ifp))) { - struct channel_oil *c_oil; - if (PIM_DEBUG_MROUTE_DETAIL) - zlog_debug("%s: Interface is not the DR blackholing incoming traffic for %s", - __PRETTY_FUNCTION__, pim_str_sg_dump(&sg)); + zlog_debug( + "%s: Interface is not the DR blackholing incoming traffic for %s", + __func__, pim_str_sg_dump(&sg)); /* * We are not the DR, but we are still receiving packets * Let's blackhole those packets for the moment * As that they will be coming up to the cpu * and causing us to consider them. + * + * This *will* create a dangling channel_oil + * that I see no way to get rid of. Just noting + * this for future reference. */ - c_oil = pim_channel_oil_add(pim_ifp->pim, &sg, - pim_ifp->mroute_vif_index); - pim_mroute_add(c_oil, __PRETTY_FUNCTION__); + up = pim_upstream_find_or_add( + &sg, ifp, PIM_UPSTREAM_FLAG_MASK_SRC_NOCACHE, __func__); + pim_upstream_mroute_add(up->channel_oil, __func__); return 0; } up = pim_upstream_find_or_add(&sg, ifp, PIM_UPSTREAM_FLAG_MASK_FHR, - __PRETTY_FUNCTION__); - if (!up) { - if (PIM_DEBUG_MROUTE) { - zlog_debug( - "%s: Failure to add upstream information for %s", - __PRETTY_FUNCTION__, pim_str_sg_dump(&sg)); - } - return 0; - } + __func__); /* * I moved this debug till after the actual add because @@ -226,24 +219,21 @@ static int pim_mroute_msg_nocache(int fd, struct interface *ifp, */ if (PIM_DEBUG_MROUTE) { zlog_debug("%s: Adding a Route %s for WHOLEPKT consumption", - __PRETTY_FUNCTION__, up->sg_str); + __func__, up->sg_str); } PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up->flags); pim_upstream_keep_alive_timer_start(up, pim_ifp->pim->keep_alive_time); up->channel_oil->cc.pktcnt++; - PIM_UPSTREAM_FLAG_SET_FHR(up->flags); // resolve mfcc_parent prior to mroute_add in channel_add_oif if (up->rpf.source_nexthop.interface && up->channel_oil->oil.mfcc_parent >= MAXVIFS) { - int vif_index = 0; - vif_index = pim_if_find_vifindex_by_ifindex( - pim_ifp->pim, - up->rpf.source_nexthop.interface->ifindex); - up->channel_oil->oil.mfcc_parent = vif_index; + pim_upstream_mroute_iif_update(up->channel_oil, __func__); } pim_register_join(up); + /* if we have receiver, inherit from parent */ + pim_upstream_inherited_olist_decide(pim_ifp->pim, up); return 0; } @@ -272,41 +262,39 @@ static int pim_mroute_msg_wholepkt(int fd, struct interface *ifp, up = pim_upstream_find(pim_ifp->pim, &star); - if (up && PIM_UPSTREAM_FLAG_TEST_SRC_IGMP(up->flags)) { + if (up && PIM_UPSTREAM_FLAG_TEST_CAN_BE_LHR(up->flags)) { up = pim_upstream_add(pim_ifp->pim, &sg, ifp, PIM_UPSTREAM_FLAG_MASK_SRC_LHR, - __PRETTY_FUNCTION__, NULL); + __func__, NULL); if (!up) { if (PIM_DEBUG_MROUTE) zlog_debug( "%s: Unable to create upstream information for %s", - __PRETTY_FUNCTION__, - pim_str_sg_dump(&sg)); + __func__, pim_str_sg_dump(&sg)); return 0; } pim_upstream_keep_alive_timer_start( up, pim_ifp->pim->keep_alive_time); pim_upstream_inherited_olist(pim_ifp->pim, up); - pim_upstream_switch(pim_ifp->pim, up, - PIM_UPSTREAM_JOINED); + pim_upstream_update_join_desired(pim_ifp->pim, up); if (PIM_DEBUG_MROUTE) zlog_debug("%s: Creating %s upstream on LHR", - __PRETTY_FUNCTION__, up->sg_str); + __func__, up->sg_str); return 0; } if (PIM_DEBUG_MROUTE_DETAIL) { zlog_debug( "%s: Unable to find upstream channel WHOLEPKT%s", - __PRETTY_FUNCTION__, pim_str_sg_dump(&sg)); + __func__, pim_str_sg_dump(&sg)); } return 0; } if (!up->rpf.source_nexthop.interface) { - if (PIM_DEBUG_TRACE) - zlog_debug("%s: up %s RPF is not present", - __PRETTY_FUNCTION__, up->sg_str); + if (PIM_DEBUG_PIM_TRACE) + zlog_debug("%s: up %s RPF is not present", __func__, + up->sg_str); return 0; } @@ -317,8 +305,7 @@ static int pim_mroute_msg_wholepkt(int fd, struct interface *ifp, if ((pim_rpf_addr_is_inaddr_none(rpg)) || (!pim_ifp) || (!(PIM_I_am_DR(pim_ifp)))) { if (PIM_DEBUG_MROUTE) { - zlog_debug("%s: Failed Check send packet", - __PRETTY_FUNCTION__); + zlog_debug("%s: Failed Check send packet", __func__); } return 0; } @@ -334,6 +321,15 @@ static int pim_mroute_msg_wholepkt(int fd, struct interface *ifp, pim_str_sg_dump(&sg)); return 0; } + + if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags)) { + if (PIM_DEBUG_PIM_REG) + zlog_debug( + "%s register forward skipped, not FHR", + up->sg_str); + return 0; + } + pim_register_send((uint8_t *)buf + sizeof(struct ip), ntohs(ip_hdr->ip_len) - sizeof(struct ip), pim_ifp->primary_address, rpg, 0, up); @@ -367,8 +363,7 @@ static int pim_mroute_msg_wrongvif(int fd, struct interface *ifp, if (PIM_DEBUG_MROUTE) zlog_debug( "%s: WRONGVIF (S,G)=%s could not find input interface for input_vif_index=%d", - __PRETTY_FUNCTION__, pim_str_sg_dump(&sg), - msg->im_vif); + __func__, pim_str_sg_dump(&sg), msg->im_vif); return -1; } @@ -377,8 +372,7 @@ static int pim_mroute_msg_wrongvif(int fd, struct interface *ifp, if (PIM_DEBUG_MROUTE) zlog_debug( "%s: WRONGVIF (S,G)=%s multicast not enabled on interface %s", - __PRETTY_FUNCTION__, pim_str_sg_dump(&sg), - ifp->name); + __func__, pim_str_sg_dump(&sg), ifp->name); return -2; } @@ -388,8 +382,7 @@ static int pim_mroute_msg_wrongvif(int fd, struct interface *ifp, if (PIM_DEBUG_MROUTE) zlog_debug( "%s: WRONGVIF (S,G)=%s could not find channel on interface %s", - __PRETTY_FUNCTION__, pim_str_sg_dump(&sg), - ifp->name); + __func__, pim_str_sg_dump(&sg), ifp->name); star_g.src.s_addr = INADDR_ANY; ch = pim_ifchannel_find(ifp, &star_g); @@ -397,8 +390,8 @@ static int pim_mroute_msg_wrongvif(int fd, struct interface *ifp, if (PIM_DEBUG_MROUTE) zlog_debug( "%s: WRONGVIF (*,G)=%s could not find channel on interface %s", - __PRETTY_FUNCTION__, - pim_str_sg_dump(&star_g), ifp->name); + __func__, pim_str_sg_dump(&star_g), + ifp->name); return -3; } } @@ -421,7 +414,7 @@ static int pim_mroute_msg_wrongvif(int fd, struct interface *ifp, if (PIM_DEBUG_MROUTE) { zlog_debug( "%s: WRONGVIF (S,G)=%s channel is not on Assert NoInfo state for interface %s", - __PRETTY_FUNCTION__, ch->sg_str, ifp->name); + __func__, ch->sg_str, ifp->name); } return -4; } @@ -430,7 +423,7 @@ static int pim_mroute_msg_wrongvif(int fd, struct interface *ifp, if (PIM_DEBUG_MROUTE) { zlog_debug( "%s: WRONGVIF (S,G)=%s interface %s is not downstream for channel", - __PRETTY_FUNCTION__, ch->sg_str, ifp->name); + __func__, ch->sg_str, ifp->name); } return -5; } @@ -439,7 +432,7 @@ static int pim_mroute_msg_wrongvif(int fd, struct interface *ifp, if (PIM_DEBUG_MROUTE) { zlog_debug( "%s: WRONGVIF (S,G)=%s assert_action_a1 failure on interface %s", - __PRETTY_FUNCTION__, ch->sg_str, ifp->name); + __func__, ch->sg_str, ifp->name); } return -6; } @@ -452,11 +445,11 @@ static int pim_mroute_msg_wrvifwhole(int fd, struct interface *ifp, { const struct ip *ip_hdr = (const struct ip *)buf; struct pim_interface *pim_ifp; + struct pim_instance *pim; struct pim_ifchannel *ch; struct pim_upstream *up; struct prefix_sg star_g; struct prefix_sg sg; - struct channel_oil *oil; pim_ifp = ifp->info; @@ -475,23 +468,28 @@ static int pim_mroute_msg_wrvifwhole(int fd, struct interface *ifp, star_g = sg; star_g.src.s_addr = INADDR_ANY; -#if 0 - ch = pim_ifchannel_find(ifp, &star_g); - if (ch) - { - if (PIM_DEBUG_MROUTE) - zlog_debug ("WRVIFWHOLE (*,G)=%s found ifchannel on interface %s", - pim_str_sg_dump (&star_g), ifp->name); - return -1; - } -#endif + + pim = pim_ifp->pim; + /* + * If the incoming interface is the pimreg, then + * we know the callback is associated with a pim register + * packet and there is nothing to do here as that + * normal pim processing will see the packet and allow + * us to do the right thing. + */ + if (ifp == pim->regiface) { + return 0; + } up = pim_upstream_find(pim_ifp->pim, &sg); if (up) { struct pim_upstream *parent; struct pim_nexthop source; struct pim_rpf *rpf = RP(pim_ifp->pim, sg.grp); - if (!rpf || !rpf->source_nexthop.interface) + + /* No RPF or No RPF interface or No mcast on RPF interface */ + if (!rpf || !rpf->source_nexthop.interface + || !rpf->source_nexthop.interface->info) return 0; /* @@ -512,22 +510,28 @@ static int pim_mroute_msg_wrvifwhole(int fd, struct interface *ifp, * the pimreg period, so I believe we can ignore this packet */ if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags)) { - // No if channel, but upstream we are at the RP. - if (pim_nexthop_lookup(pim_ifp->pim, &source, + /* + * No if channel, but upstream we are at the RP. + * + * This could be a anycast RP too and we may + * not have received a register packet from + * the source here at all. So gracefully + * bow out of doing a nexthop lookup and + * setting the SPTBIT to true + */ + if (up->upstream_register.s_addr != INADDR_ANY && + pim_nexthop_lookup(pim_ifp->pim, &source, up->upstream_register, 0)) { pim_register_stop_send(source.interface, &sg, pim_ifp->primary_address, up->upstream_register); up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE; } - if (!up->channel_oil) - up->channel_oil = pim_channel_oil_add( - pim_ifp->pim, &sg, - pim_ifp->mroute_vif_index); + pim_upstream_inherited_olist(pim_ifp->pim, up); if (!up->channel_oil->installed) - pim_mroute_add(up->channel_oil, - __PRETTY_FUNCTION__); + pim_upstream_mroute_add(up->channel_oil, + __func__); } else { if (I_am_RP(pim_ifp->pim, up->sg.grp)) { if (pim_nexthop_lookup(pim_ifp->pim, &source, @@ -548,13 +552,10 @@ static int pim_mroute_msg_wrvifwhole(int fd, struct interface *ifp, } pim_ifp = ifp->info; - oil = pim_channel_oil_add(pim_ifp->pim, &sg, pim_ifp->mroute_vif_index); - if (!oil->installed) - pim_mroute_add(oil, __PRETTY_FUNCTION__); if (pim_if_connected_to_source(ifp, sg.src)) { up = pim_upstream_add(pim_ifp->pim, &sg, ifp, - PIM_UPSTREAM_FLAG_MASK_FHR, - __PRETTY_FUNCTION__, NULL); + PIM_UPSTREAM_FLAG_MASK_FHR, __func__, + NULL); if (!up) { if (PIM_DEBUG_MROUTE) zlog_debug( @@ -565,13 +566,20 @@ static int pim_mroute_msg_wrvifwhole(int fd, struct interface *ifp, PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up->flags); pim_upstream_keep_alive_timer_start( up, pim_ifp->pim->keep_alive_time); - up->channel_oil = oil; up->channel_oil->cc.pktcnt++; pim_register_join(up); pim_upstream_inherited_olist(pim_ifp->pim, up); + if (!up->channel_oil->installed) + pim_upstream_mroute_add(up->channel_oil, __func__); // Send the packet to the RP pim_mroute_msg_wholepkt(fd, ifp, buf); + } else { + up = pim_upstream_add(pim_ifp->pim, &sg, ifp, + PIM_UPSTREAM_FLAG_MASK_SRC_NOCACHE, + __func__, NULL); + if (!up->channel_oil->installed) + pim_upstream_mroute_add(up->channel_oil, __func__); } return 0; @@ -584,12 +592,12 @@ static int pim_mroute_msg(struct pim_instance *pim, const char *buf, struct pim_interface *pim_ifp; const struct ip *ip_hdr; const struct igmpmsg *msg; - char ip_src_str[INET_ADDRSTRLEN] = ""; - char ip_dst_str[INET_ADDRSTRLEN] = ""; - char src_str[INET_ADDRSTRLEN] = ""; - char grp_str[INET_ADDRSTRLEN] = ""; struct in_addr ifaddr; struct igmp_sock *igmp; + const struct prefix *connected_src; + + if (buf_size < (int)sizeof(struct ip)) + return 0; ip_hdr = (const struct ip *)buf; @@ -606,35 +614,38 @@ static int pim_mroute_msg(struct pim_instance *pim, const char *buf, if (!ifp || !ifp->info) return 0; + connected_src = pim_if_connected_to_source(ifp, ip_hdr->ip_src); + + if (!connected_src) { + if (PIM_DEBUG_IGMP_PACKETS) { + zlog_debug("Recv IGMP packet on interface: %s from a non-connected source: %pI4", + ifp->name, &ip_hdr->ip_src); + } + return 0; + } + pim_ifp = ifp->info; - ifaddr = pim_find_primary_addr(ifp); - igmp = pim_igmp_sock_lookup_ifaddr(pim_ifp->igmp_socket_list, - ifaddr); + ifaddr = connected_src->u.prefix4; + igmp = pim_igmp_sock_lookup_ifaddr(pim_ifp->igmp_socket_list, ifaddr); if (PIM_DEBUG_MROUTE) { - pim_inet4_dump("", ip_hdr->ip_src, ip_src_str, - sizeof(ip_src_str)); - pim_inet4_dump("", ip_hdr->ip_dst, ip_dst_str, - sizeof(ip_dst_str)); - - zlog_warn( - "%s(%s): igmp kernel upcall on %s(%p) for %s -> %s", - __PRETTY_FUNCTION__, pim->vrf->name, ifp->name, - igmp, ip_src_str, ip_dst_str); + zlog_debug( + "%s(%s): igmp kernel upcall on %s(%p) for %pI4 -> %pI4", + __func__, pim->vrf->name, ifp->name, igmp, + &ip_hdr->ip_src, &ip_hdr->ip_dst); } if (igmp) pim_igmp_packet(igmp, (char *)buf, buf_size); - + else if (PIM_DEBUG_IGMP_PACKETS) { + zlog_debug("No IGMP socket on interface: %s with connected source: %pFX", + ifp->name, connected_src); + } } else if (ip_hdr->ip_p) { if (PIM_DEBUG_MROUTE_DETAIL) { - pim_inet4_dump("", ip_hdr->ip_src, src_str, - sizeof(src_str)); - pim_inet4_dump("", ip_hdr->ip_dst, grp_str, - sizeof(grp_str)); zlog_debug( - "%s: no kernel upcall proto=%d src: %s dst: %s msg_size=%d", - __PRETTY_FUNCTION__, ip_hdr->ip_p, src_str, - grp_str, buf_size); + "%s: no kernel upcall proto=%d src: %pI4 dst: %pI4 msg_size=%d", + __func__, ip_hdr->ip_p, &ip_hdr->ip_src, &ip_hdr->ip_dst, + buf_size); } } else { @@ -645,16 +656,11 @@ static int pim_mroute_msg(struct pim_instance *pim, const char *buf, if (!ifp) return 0; if (PIM_DEBUG_MROUTE) { - pim_inet4_dump("", msg->im_src, src_str, - sizeof(src_str)); - pim_inet4_dump("", msg->im_dst, grp_str, - sizeof(grp_str)); - zlog_warn( - "%s: pim kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%s,%s) on %s vifi=%d size=%d", - __PRETTY_FUNCTION__, - igmpmsgtype2str[msg->im_msgtype], + zlog_debug( + "%s: pim kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%pI4,%pI4) on %s vifi=%d size=%d", + __func__, igmpmsgtype2str[msg->im_msgtype], msg->im_msgtype, ip_hdr->ip_p, - pim->mroute_socket, src_str, grp_str, ifp->name, + pim->mroute_socket, &msg->im_src, &msg->im_dst, ifp->name, msg->im_vif, buf_size); } @@ -704,12 +710,10 @@ static int mroute_read(struct thread *t) if (errno == EWOULDBLOCK || errno == EAGAIN) break; - if (PIM_DEBUG_MROUTE) - zlog_warn( - "%s: failure reading rd=%d: fd=%d: errno=%d: %s", - __PRETTY_FUNCTION__, rd, - pim->mroute_socket, errno, - safe_strerror(errno)); + zlog_warn( + "%s: failure reading rd=%d: fd=%d: errno=%d: %s", + __func__, rd, pim->mroute_socket, errno, + safe_strerror(errno)); goto done; } @@ -741,7 +745,7 @@ int pim_mroute_socket_enable(struct pim_instance *pim) { int fd; - frr_elevate_privs(&pimd_privs) { + frr_with_privs(&pimd_privs) { fd = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP); @@ -816,7 +820,7 @@ int pim_mroute_add_vif(struct interface *ifp, struct in_addr ifaddr, int err; if (PIM_DEBUG_MROUTE) - zlog_debug("%s: Add Vif %d (%s[%s])", __PRETTY_FUNCTION__, + zlog_debug("%s: Add Vif %d (%s[%s])", __func__, pim_ifp->mroute_vif_index, ifp->name, pim_ifp->pim->vrf->name); @@ -828,7 +832,7 @@ int pim_mroute_add_vif(struct interface *ifp, struct in_addr ifaddr, if (ifaddr.s_addr == INADDR_ANY) { zlog_warn( "%s: unnumbered interfaces are not supported on this platform", - __PRETTY_FUNCTION__); + __func__); return -1; } memcpy(&vc.vifc_lcl_addr, &ifaddr, sizeof(vc.vifc_lcl_addr)); @@ -854,9 +858,8 @@ int pim_mroute_add_vif(struct interface *ifp, struct in_addr ifaddr, zlog_warn( "%s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_VIF,vif_index=%d,ifaddr=%s,flag=%d): errno=%d: %s", - __PRETTY_FUNCTION__, pim_ifp->pim->mroute_socket, - ifp->ifindex, ifaddr_str, flags, errno, - safe_strerror(errno)); + __func__, pim_ifp->pim->mroute_socket, ifp->ifindex, + ifaddr_str, flags, errno, safe_strerror(errno)); return -2; } @@ -870,7 +873,7 @@ int pim_mroute_del_vif(struct interface *ifp) int err; if (PIM_DEBUG_MROUTE) - zlog_debug("%s: Del Vif %d (%s[%s])", __PRETTY_FUNCTION__, + zlog_debug("%s: Del Vif %d (%s[%s])", __func__, pim_ifp->mroute_vif_index, ifp->name, pim_ifp->pim->vrf->name); @@ -882,81 +885,100 @@ int pim_mroute_del_vif(struct interface *ifp) if (err) { zlog_warn( "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_VIF,vif_index=%d): errno=%d: %s", - __FILE__, __PRETTY_FUNCTION__, - pim_ifp->pim->mroute_socket, pim_ifp->mroute_vif_index, - errno, safe_strerror(errno)); + __FILE__, __func__, pim_ifp->pim->mroute_socket, + pim_ifp->mroute_vif_index, errno, safe_strerror(errno)); return -2; } return 0; } -int pim_mroute_add(struct channel_oil *c_oil, const char *name) +/* + * Prevent creating MFC entry with OIF=IIF. + * + * This is a protection against implementation mistakes. + * + * PIM protocol implicitely ensures loopfree multicast topology. + * + * IGMP must be protected against adding looped MFC entries created + * by both source and receiver attached to the same interface. See + * TODO T22. + * We shall allow igmp to create upstream when it is DR for the intf. + * Assume RP reachable via non DR. + */ +bool pim_mroute_allow_iif_in_oil(struct channel_oil *c_oil, + int oif_index) +{ +#ifdef PIM_ENFORCE_LOOPFREE_MFC + struct interface *ifp_out; + struct pim_interface *pim_ifp; + + if (c_oil->up && + PIM_UPSTREAM_FLAG_TEST_ALLOW_IIF_IN_OIL(c_oil->up->flags)) + return true; + + ifp_out = pim_if_find_by_vif_index(c_oil->pim, oif_index); + if (!ifp_out) + return false; + pim_ifp = ifp_out->info; + if (!pim_ifp) + return false; + if ((c_oil->oif_flags[oif_index] & PIM_OIF_FLAG_PROTO_IGMP) && + PIM_I_am_DR(pim_ifp)) + return true; + + return false; +#else + return true; +#endif +} + +static inline void pim_mroute_copy(struct mfcctl *oil, + struct channel_oil *c_oil) +{ + int i; + + oil->mfcc_origin = c_oil->oil.mfcc_origin; + oil->mfcc_mcastgrp = c_oil->oil.mfcc_mcastgrp; + oil->mfcc_parent = c_oil->oil.mfcc_parent; + + for (i = 0; i < MAXVIFS; ++i) { + if ((oil->mfcc_parent == i) && + !pim_mroute_allow_iif_in_oil(c_oil, i)) { + oil->mfcc_ttls[i] = 0; + continue; + } + + if (c_oil->oif_flags[i] & PIM_OIF_FLAG_MUTE) + oil->mfcc_ttls[i] = 0; + else + oil->mfcc_ttls[i] = c_oil->oil.mfcc_ttls[i]; + } +} + +/* This function must not be called directly 0 + * use pim_upstream_mroute_add or pim_static_mroute_add instead + */ +static int pim_mroute_add(struct channel_oil *c_oil, const char *name) { struct pim_instance *pim = c_oil->pim; + struct mfcctl tmp_oil = { {0} }; int err; - int orig = 0; - int orig_iif_vif = 0; - struct pim_interface *pim_reg_ifp; - int orig_pimreg_ttl; - bool pimreg_ttl_reset = false; - struct pim_interface *vxlan_ifp; - int orig_term_ttl; - bool orig_term_ttl_reset = false; pim->mroute_add_last = pim_time_monotonic_sec(); ++pim->mroute_add_events; - /* Do not install route if incoming interface is undefined. */ - if (c_oil->oil.mfcc_parent >= MAXVIFS) { - if (PIM_DEBUG_MROUTE) { - char buf[1000]; - zlog_debug( - "%s(%s) %s Attempting to add vifi that is invalid to mroute table", - __PRETTY_FUNCTION__, name, - pim_channel_oil_dump(c_oil, buf, sizeof(buf))); - } - return -2; - } + /* Copy the oil to a temporary structure to fixup (without need to + * later restore) before sending the mroute add to the dataplane + */ + pim_mroute_copy(&tmp_oil, c_oil); /* The linux kernel *expects* the incoming * vif to be part of the outgoing list * in the case of a (*,G). */ if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY) { - orig = c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent]; - c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent] = 1; - } - - if (c_oil->up) { - /* suppress pimreg in the OIL if the mroute is not supposed to - * trigger register encapsulated data - */ - if (PIM_UPSTREAM_FLAG_TEST_NO_PIMREG_DATA(c_oil->up->flags)) { - pim_reg_ifp = pim->regiface->info; - orig_pimreg_ttl = - c_oil->oil.mfcc_ttls[pim_reg_ifp->mroute_vif_index]; - c_oil->oil.mfcc_ttls[pim_reg_ifp->mroute_vif_index] = 0; - /* remember to flip it back after MFC programming */ - pimreg_ttl_reset = true; - } - - vxlan_ifp = pim_vxlan_get_term_ifp(pim); - /* 1. vxlan termination device must never be added to the - * origination mroute (and that can actually happen because - * of XG inheritance from the termination mroute) otherwise - * traffic will end up looping. - * 2. vxlan termination device should be removed from the non-DF - * to prevent duplicates to the overlay rxer - */ - if (vxlan_ifp && - (PIM_UPSTREAM_FLAG_TEST_SRC_VXLAN_ORIG(c_oil->up->flags) || - PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(c_oil->up->flags))) { - orig_term_ttl_reset = true; - orig_term_ttl = - c_oil->oil.mfcc_ttls[vxlan_ifp->mroute_vif_index]; - c_oil->oil.mfcc_ttls[vxlan_ifp->mroute_vif_index] = 0; - } + tmp_oil.mfcc_ttls[c_oil->oil.mfcc_parent] = 1; } /* @@ -968,52 +990,178 @@ int pim_mroute_add(struct channel_oil *c_oil, const char *name) */ if (!c_oil->installed && c_oil->oil.mfcc_origin.s_addr != INADDR_ANY && c_oil->oil.mfcc_parent != 0) { - orig_iif_vif = c_oil->oil.mfcc_parent; - c_oil->oil.mfcc_parent = 0; + tmp_oil.mfcc_parent = 0; } err = setsockopt(pim->mroute_socket, IPPROTO_IP, MRT_ADD_MFC, - &c_oil->oil, sizeof(c_oil->oil)); + &tmp_oil, sizeof(tmp_oil)); if (!err && !c_oil->installed && c_oil->oil.mfcc_origin.s_addr != INADDR_ANY - && orig_iif_vif != 0) { - c_oil->oil.mfcc_parent = orig_iif_vif; + && c_oil->oil.mfcc_parent != 0) { + tmp_oil.mfcc_parent = c_oil->oil.mfcc_parent; err = setsockopt(pim->mroute_socket, IPPROTO_IP, MRT_ADD_MFC, - &c_oil->oil, sizeof(c_oil->oil)); + &tmp_oil, sizeof(tmp_oil)); } - if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY) - c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent] = orig; - - if (pimreg_ttl_reset) - c_oil->oil.mfcc_ttls[pim_reg_ifp->mroute_vif_index] = - orig_pimreg_ttl; - - if (orig_term_ttl_reset) - c_oil->oil.mfcc_ttls[vxlan_ifp->mroute_vif_index] = - orig_term_ttl; - if (err) { zlog_warn( "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_MFC): errno=%d: %s", - __FILE__, __PRETTY_FUNCTION__, pim->mroute_socket, - errno, safe_strerror(errno)); + __FILE__, __func__, pim->mroute_socket, errno, + safe_strerror(errno)); return -2; } if (PIM_DEBUG_MROUTE) { char buf[1000]; - zlog_debug("%s(%s), vrf %s Added Route: %s", - __PRETTY_FUNCTION__, name, pim->vrf->name, + zlog_debug("%s(%s), vrf %s Added Route: %s", __func__, name, + pim->vrf->name, pim_channel_oil_dump(c_oil, buf, sizeof(buf))); } - c_oil->installed = 1; - c_oil->mroute_creation = pim_time_monotonic_sec(); + if (!c_oil->installed) { + c_oil->installed = 1; + c_oil->mroute_creation = pim_time_monotonic_sec(); + } return 0; } +static int pim_upstream_get_mroute_iif(struct channel_oil *c_oil, + const char *name) +{ + vifi_t iif = MAXVIFS; + struct interface *ifp = NULL; + struct pim_interface *pim_ifp; + struct pim_upstream *up = c_oil->up; + + if (up) { + if (PIM_UPSTREAM_FLAG_TEST_USE_RPT(up->flags)) { + if (up->parent) + ifp = up->parent->rpf.source_nexthop.interface; + } else { + ifp = up->rpf.source_nexthop.interface; + } + if (ifp) { + pim_ifp = (struct pim_interface *)ifp->info; + if (pim_ifp) + iif = pim_ifp->mroute_vif_index; + } + } + return iif; +} + +static int pim_upstream_mroute_update(struct channel_oil *c_oil, + const char *name) +{ + char buf[1000]; + + if (c_oil->oil.mfcc_parent >= MAXVIFS) { + /* the c_oil cannot be installed as a mroute yet */ + if (PIM_DEBUG_MROUTE) + zlog_debug( + "%s(%s) %s mroute not ready to be installed; %s", + __func__, name, + pim_channel_oil_dump(c_oil, buf, + sizeof(buf)), + c_oil->installed ? + "uninstall" : "skip"); + /* if already installed flush it out as we are going to stop + * updates to it leaving it in a stale state + */ + if (c_oil->installed) + pim_mroute_del(c_oil, name); + /* return success (skipped) */ + return 0; + } + + return pim_mroute_add(c_oil, name); +} + +/* IIF associated with SGrpt entries are re-evaluated when the parent + * (*,G) entries IIF changes + */ +static void pim_upstream_all_sources_iif_update(struct pim_upstream *up) +{ + struct listnode *listnode; + struct pim_upstream *child; + + for (ALL_LIST_ELEMENTS_RO(up->sources, listnode, + child)) { + if (PIM_UPSTREAM_FLAG_TEST_USE_RPT(child->flags)) + pim_upstream_mroute_iif_update(child->channel_oil, + __func__); + } +} + +/* In the case of "PIM state machine" added mroutes an upstream entry + * must be present to decide on the SPT-forwarding vs. RPT-forwarding. + */ +int pim_upstream_mroute_add(struct channel_oil *c_oil, const char *name) +{ + vifi_t iif; + + iif = pim_upstream_get_mroute_iif(c_oil, name); + + if (c_oil->oil.mfcc_parent != iif) { + c_oil->oil.mfcc_parent = iif; + if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY && + c_oil->up) + pim_upstream_all_sources_iif_update(c_oil->up); + } else { + c_oil->oil.mfcc_parent = iif; + } + + return pim_upstream_mroute_update(c_oil, name); +} + +/* Look for IIF changes and update the dateplane entry only if the IIF + * has changed. + */ +int pim_upstream_mroute_iif_update(struct channel_oil *c_oil, const char *name) +{ + vifi_t iif; + char buf[1000]; + + iif = pim_upstream_get_mroute_iif(c_oil, name); + if (c_oil->oil.mfcc_parent == iif) { + /* no change */ + return 0; + } + c_oil->oil.mfcc_parent = iif; + + if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY && + c_oil->up) + pim_upstream_all_sources_iif_update(c_oil->up); + + if (PIM_DEBUG_MROUTE_DETAIL) + zlog_debug("%s(%s) %s mroute iif update %d", + __func__, name, + pim_channel_oil_dump(c_oil, buf, + sizeof(buf)), iif); + /* XXX: is this hack needed? */ + c_oil->oil_inherited_rescan = 1; + return pim_upstream_mroute_update(c_oil, name); +} + +int pim_static_mroute_add(struct channel_oil *c_oil, const char *name) +{ + return pim_mroute_add(c_oil, name); +} + +void pim_static_mroute_iif_update(struct channel_oil *c_oil, + int input_vif_index, + const char *name) +{ + if (c_oil->oil.mfcc_parent == input_vif_index) + return; + + c_oil->oil.mfcc_parent = input_vif_index; + if (input_vif_index == MAXVIFS) + pim_mroute_del(c_oil, name); + else + pim_static_mroute_add(c_oil, name); +} + int pim_mroute_del(struct channel_oil *c_oil, const char *name) { struct pim_instance *pim = c_oil->pim; @@ -1027,8 +1175,7 @@ int pim_mroute_del(struct channel_oil *c_oil, const char *name) char buf[1000]; zlog_debug( "%s %s: vifi %d for route is %s not installed, do not need to send del req. ", - __FILE__, __PRETTY_FUNCTION__, - c_oil->oil.mfcc_parent, + __FILE__, __func__, c_oil->oil.mfcc_parent, pim_channel_oil_dump(c_oil, buf, sizeof(buf))); } return -2; @@ -1040,16 +1187,15 @@ int pim_mroute_del(struct channel_oil *c_oil, const char *name) if (PIM_DEBUG_MROUTE) zlog_warn( "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_MFC): errno=%d: %s", - __FILE__, __PRETTY_FUNCTION__, - pim->mroute_socket, errno, + __FILE__, __func__, pim->mroute_socket, errno, safe_strerror(errno)); return -2; } if (PIM_DEBUG_MROUTE) { char buf[1000]; - zlog_debug("%s(%s), vrf %s Deleted Route: %s", - __PRETTY_FUNCTION__, name, pim->vrf->name, + zlog_debug("%s(%s), vrf %s Deleted Route: %s", __func__, name, + pim->vrf->name, pim_channel_oil_dump(c_oil, buf, sizeof(buf))); } @@ -1089,18 +1235,14 @@ void pim_mroute_update_counters(struct channel_oil *c_oil) pim_zlookup_sg_statistics(c_oil); if (ioctl(pim->mroute_socket, SIOCGETSGCNT, &sgreq)) { - if (PIM_DEBUG_MROUTE) { - struct prefix_sg sg; + struct prefix_sg sg; - sg.src = c_oil->oil.mfcc_origin; - sg.grp = c_oil->oil.mfcc_mcastgrp; + sg.src = c_oil->oil.mfcc_origin; + sg.grp = c_oil->oil.mfcc_mcastgrp; - zlog_warn( - "ioctl(SIOCGETSGCNT=%lu) failure for (S,G)=%s: errno=%d: %s", - (unsigned long)SIOCGETSGCNT, - pim_str_sg_dump(&sg), errno, - safe_strerror(errno)); - } + zlog_warn("ioctl(SIOCGETSGCNT=%lu) failure for (S,G)=%s: errno=%d: %s", + (unsigned long)SIOCGETSGCNT, pim_str_sg_dump(&sg), + errno, safe_strerror(errno)); return; } diff --git a/pimd/pim_mroute.h b/pimd/pim_mroute.h index bd71acbf82..2d8e1b01fb 100644 --- a/pimd/pim_mroute.h +++ b/pimd/pim_mroute.h @@ -174,8 +174,15 @@ int pim_mroute_add_vif(struct interface *ifp, struct in_addr ifaddr, unsigned char flags); int pim_mroute_del_vif(struct interface *ifp); -int pim_mroute_add(struct channel_oil *c_oil, const char *name); +int pim_upstream_mroute_add(struct channel_oil *c_oil, const char *name); +int pim_upstream_mroute_iif_update(struct channel_oil *c_oil, const char *name); +int pim_static_mroute_add(struct channel_oil *c_oil, const char *name); +void pim_static_mroute_iif_update(struct channel_oil *c_oil, + int input_vif_index, + const char *name); int pim_mroute_del(struct channel_oil *c_oil, const char *name); void pim_mroute_update_counters(struct channel_oil *c_oil); +bool pim_mroute_allow_iif_in_oil(struct channel_oil *c_oil, + int oif_index); #endif /* PIM_MROUTE_H */ diff --git a/pimd/pim_msdp.c b/pimd/pim_msdp.c index 395c4af35f..b42092a464 100644 --- a/pimd/pim_msdp.c +++ b/pimd/pim_msdp.c @@ -126,7 +126,12 @@ static void pim_msdp_sa_upstream_del(struct pim_msdp_sa *sa) if (PIM_UPSTREAM_FLAG_TEST_SRC_MSDP(up->flags)) { PIM_UPSTREAM_FLAG_UNSET_SRC_MSDP(up->flags); sa->flags |= PIM_MSDP_SAF_UP_DEL_IN_PROG; - pim_upstream_del(sa->pim, up, __PRETTY_FUNCTION__); + up = pim_upstream_del(sa->pim, up, __func__); + /* re-eval joinDesired; clearing peer-msdp-sa flag can + * cause JD to change + */ + if (up) + pim_upstream_update_join_desired(sa->pim, up); sa->flags &= ~PIM_MSDP_SAF_UP_DEL_IN_PROG; } @@ -205,8 +210,7 @@ static void pim_msdp_sa_upstream_update(struct pim_msdp_sa *sa, /* RFC3618: "RP triggers a (S, G) join event towards the data source * as if a JP message was rxed addressed to the RP itself." */ up = pim_upstream_add(sa->pim, &sa->sg, NULL /* iif */, - PIM_UPSTREAM_FLAG_MASK_SRC_MSDP, - __PRETTY_FUNCTION__, NULL); + PIM_UPSTREAM_FLAG_MASK_SRC_MSDP, __func__, NULL); sa->up = up; if (up) { @@ -445,10 +449,9 @@ static bool pim_msdp_sa_local_add_ok(struct pim_upstream *up) return false; } - if (!up->t_ka_timer) { + if (!pim_upstream_is_kat_running(up)) /* stream is not active */ return false; - } if (!I_am_RP(pim, up->sg.grp)) { /* we are not RP for the group */ @@ -469,7 +472,7 @@ static void pim_msdp_sa_local_add(struct pim_instance *pim, struct prefix_sg *sg) { struct in_addr rp; - rp.s_addr = 0; + rp.s_addr = INADDR_ANY; pim_msdp_sa_ref(pim, NULL /* mp */, sg, rp); } @@ -561,11 +564,9 @@ void pim_msdp_sa_local_update(struct pim_upstream *up) static void pim_msdp_sa_local_setup(struct pim_instance *pim) { struct pim_upstream *up; - struct listnode *up_node; - for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, up_node, up)) { + frr_each (rb_pim_upstream, &pim->upstream_head, up) pim_msdp_sa_local_update(up); - } } /* whenever the RP changes we need to re-evaluate the "local" SA-cache */ @@ -680,9 +681,9 @@ void pim_msdp_up_del(struct pim_instance *pim, struct prefix_sg *sg) } /* sa hash and peer list helpers */ -static unsigned int pim_msdp_sa_hash_key_make(void *p) +static unsigned int pim_msdp_sa_hash_key_make(const void *p) { - struct pim_msdp_sa *sa = p; + const struct pim_msdp_sa *sa = p; return (jhash_2words(sa->sg.src.s_addr, sa->sg.grp.s_addr, 0)); } @@ -1078,7 +1079,7 @@ static enum pim_msdp_err pim_msdp_peer_new(struct pim_instance *pim, mp->mesh_group_name = XSTRDUP(MTYPE_PIM_MSDP_MG_NAME, mesh_group_name); mp->state = PIM_MSDP_INACTIVE; mp->fd = -1; - strcpy(mp->last_reset, "-"); + strlcpy(mp->last_reset, "-", sizeof(mp->last_reset)); /* higher IP address is listener */ if (ntohl(mp->local.s_addr) > ntohl(mp->peer.s_addr)) { mp->flags |= PIM_MSDP_PEERF_LISTENER; @@ -1173,9 +1174,7 @@ static void pim_msdp_peer_free(struct pim_msdp_peer *mp) stream_fifo_free(mp->obuf); } - if (mp->mesh_group_name) { - XFREE(MTYPE_PIM_MSDP_MG_NAME, mp->mesh_group_name); - } + XFREE(MTYPE_PIM_MSDP_MG_NAME, mp->mesh_group_name); mp->pim = NULL; XFREE(MTYPE_PIM_MSDP_PEER, mp); @@ -1215,9 +1214,9 @@ enum pim_msdp_err pim_msdp_peer_del(struct pim_instance *pim, } /* peer hash and peer list helpers */ -static unsigned int pim_msdp_peer_hash_key_make(void *p) +static unsigned int pim_msdp_peer_hash_key_make(const void *p) { - struct pim_msdp_peer *mp = p; + const struct pim_msdp_peer *mp = p; return (jhash_1word(mp->peer.s_addr, 0)); } @@ -1287,7 +1286,9 @@ enum pim_msdp_err pim_msdp_mg_del(struct pim_instance *pim, struct pim_msdp_mg *mg = pim->msdp.mg; struct pim_msdp_mg_mbr *mbr; - if (!mg || strcmp(mg->mesh_group_name, mesh_group_name)) { + if (!mg + || (mesh_group_name + && strcmp(mg->mesh_group_name, mesh_group_name))) { return PIM_MSDP_ERR_NO_MG; } @@ -1573,14 +1574,16 @@ void pim_msdp_init(struct pim_instance *pim, struct thread_master *master) pim->msdp.master = master; char hash_name[64]; - snprintf(hash_name, 64, "PIM %s MSDP Peer Hash", pim->vrf->name); + snprintf(hash_name, sizeof(hash_name), "PIM %s MSDP Peer Hash", + pim->vrf->name); pim->msdp.peer_hash = hash_create(pim_msdp_peer_hash_key_make, pim_msdp_peer_hash_eq, hash_name); pim->msdp.peer_list = list_new(); pim->msdp.peer_list->del = (void (*)(void *))pim_msdp_peer_free; pim->msdp.peer_list->cmp = (int (*)(void *, void *))pim_msdp_peer_comp; - snprintf(hash_name, 64, "PIM %s MSDP SA Hash", pim->vrf->name); + snprintf(hash_name, sizeof(hash_name), "PIM %s MSDP SA Hash", + pim->vrf->name); pim->msdp.sa_hash = hash_create(pim_msdp_sa_hash_key_make, pim_msdp_sa_hash_eq, hash_name); pim->msdp.sa_list = list_new(); diff --git a/pimd/pim_msdp_socket.c b/pimd/pim_msdp_socket.c index b1f7cfd2c6..7620cd5792 100644 --- a/pimd/pim_msdp_socket.c +++ b/pimd/pim_msdp_socket.c @@ -162,20 +162,20 @@ int pim_msdp_sock_listen(struct pim_instance *pim) if (!ifp) { flog_err(EC_LIB_INTERFACE, "%s: Unable to lookup vrf interface: %s", - __PRETTY_FUNCTION__, pim->vrf->name); + __func__, pim->vrf->name); close(sock); return -1; } if (pim_socket_bind(sock, ifp)) { flog_err_sys(EC_LIB_SOCKET, "%s: Unable to bind to socket: %s", - __PRETTY_FUNCTION__, safe_strerror(errno)); + __func__, safe_strerror(errno)); close(sock); return -1; } } - frr_elevate_privs(&pimd_privs) { + frr_with_privs(&pimd_privs) { /* bind to well known TCP port */ rc = bind(sock, (struct sockaddr *)&sin, socklen); } @@ -243,13 +243,13 @@ int pim_msdp_sock_connect(struct pim_msdp_peer *mp) if (!ifp) { flog_err(EC_LIB_INTERFACE, "%s: Unable to lookup vrf interface: %s", - __PRETTY_FUNCTION__, mp->pim->vrf->name); + __func__, mp->pim->vrf->name); return -1; } if (pim_socket_bind(mp->fd, ifp)) { flog_err_sys(EC_LIB_SOCKET, "%s: Unable to bind to socket: %s", - __PRETTY_FUNCTION__, safe_strerror(errno)); + __func__, safe_strerror(errno)); close(mp->fd); mp->fd = -1; return -1; diff --git a/pimd/pim_msg.c b/pimd/pim_msg.c index 63688f87e0..65b6405c81 100644 --- a/pimd/pim_msg.c +++ b/pimd/pim_msg.c @@ -39,7 +39,7 @@ #include "pim_oil.h" void pim_msg_build_header(uint8_t *pim_msg, size_t pim_msg_size, - uint8_t pim_msg_type) + uint8_t pim_msg_type, bool no_fwd) { struct pim_msg_header *header = (struct pim_msg_header *)pim_msg; @@ -48,6 +48,7 @@ void pim_msg_build_header(uint8_t *pim_msg, size_t pim_msg_size, */ header->ver = PIM_PROTO_VERSION; header->type = pim_msg_type; + header->Nbit = no_fwd; header->reserved = 0; @@ -122,11 +123,20 @@ size_t pim_msg_get_jp_group_size(struct list *sources) if (PIM_DEBUG_PIM_PACKETS) zlog_debug( "%s: Considering (%s) children for (S,G,rpt) prune", - __PRETTY_FUNCTION__, up->sg_str); + __func__, up->sg_str); for (ALL_LIST_ELEMENTS_RO(up->sources, up_node, child)) { - if (child->sptbit == PIM_UPSTREAM_SPTBIT_TRUE) { - if (!pim_rpf_is_same(&up->rpf, &child->rpf)) { + if (!PIM_UPSTREAM_FLAG_TEST_USE_RPT(child->flags)) { + /* If we are using SPT and the SPT and RPT IIFs + * are different we can prune the source off + * of the RPT. + * If RPF_interface(S) is not resolved hold + * decision to prune as SPT may end up on the + * same IIF as RPF_interface(RP). + */ + if (child->rpf.source_nexthop.interface && + !pim_rpf_is_same(&up->rpf, + &child->rpf)) { size += sizeof( struct pim_encoded_source_ipv4); PIM_UPSTREAM_FLAG_SET_SEND_SG_RPT_PRUNE( @@ -134,45 +144,30 @@ size_t pim_msg_get_jp_group_size(struct list *sources) if (PIM_DEBUG_PIM_PACKETS) zlog_debug( "%s: SPT Bit and RPF'(%s) != RPF'(S,G): Add Prune (%s,rpt) to compound message", - __PRETTY_FUNCTION__, - up->sg_str, + __func__, up->sg_str, child->sg_str); } else if (PIM_DEBUG_PIM_PACKETS) zlog_debug( "%s: SPT Bit and RPF'(%s) == RPF'(S,G): Not adding Prune for (%s,rpt)", - __PRETTY_FUNCTION__, up->sg_str, + __func__, up->sg_str, child->sg_str); - } else if (pim_upstream_is_sg_rpt(child)) { - if (pim_upstream_empty_inherited_olist(child)) { - size += sizeof( - struct pim_encoded_source_ipv4); - PIM_UPSTREAM_FLAG_SET_SEND_SG_RPT_PRUNE( - child->flags); - if (PIM_DEBUG_PIM_PACKETS) - zlog_debug( - "%s: inherited_olist(%s,rpt) is NULL, Add Prune to compound message", - __PRETTY_FUNCTION__, - child->sg_str); - } else if (!pim_rpf_is_same(&up->rpf, - &child->rpf)) { - size += sizeof( + } else if (pim_upstream_empty_inherited_olist(child)) { + /* S is supposed to be forwarded along the RPT + * but it's inherited OIL is empty. So just + * prune it off. + */ + size += sizeof( struct pim_encoded_source_ipv4); - PIM_UPSTREAM_FLAG_SET_SEND_SG_RPT_PRUNE( + PIM_UPSTREAM_FLAG_SET_SEND_SG_RPT_PRUNE( child->flags); - if (PIM_DEBUG_PIM_PACKETS) - zlog_debug( - "%s: RPF'(%s) != RPF'(%s,rpt), Add Prune to compound message", - __PRETTY_FUNCTION__, - up->sg_str, - child->sg_str); - } else if (PIM_DEBUG_PIM_PACKETS) + if (PIM_DEBUG_PIM_PACKETS) zlog_debug( - "%s: RPF'(%s) == RPF'(%s,rpt), Do not add Prune to compound message", - __PRETTY_FUNCTION__, up->sg_str, - child->sg_str); + "%s: inherited_olist(%s,rpt) is NULL, Add Prune to compound message", + __func__, child->sg_str); } else if (PIM_DEBUG_PIM_PACKETS) - zlog_debug("%s: SPT bit is not set for (%s)", - __PRETTY_FUNCTION__, child->sg_str); + zlog_debug( + "%s: Do not add Prune %s to compound message %s", + __func__, child->sg_str, up->sg_str); } } return size; diff --git a/pimd/pim_msg.h b/pimd/pim_msg.h index ad9b5d9c01..2d69a4b03a 100644 --- a/pimd/pim_msg.h +++ b/pimd/pim_msg.h @@ -23,6 +23,8 @@ #include #include "pim_jp_agg.h" + +#define PIM_HDR_LEN sizeof(struct pim_msg_header) /* Number Description ---------- ------------------ @@ -40,12 +42,30 @@ enum pim_msg_address_family { }; /* - * Network Order pim_msg_hdr + * pim_msg_hdr + * ========================= + * PIM Header definition as per RFC 5059. N bit introduced to indicate + * do-not-forward option in PIM Boot strap Message. + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |PIM Ver| Type |N| Reserved | Checksum | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ struct pim_msg_header { +#if (BYTE_ORDER == LITTLE_ENDIAN) uint8_t type : 4; uint8_t ver : 4; - uint8_t reserved; + uint8_t reserved : 7; + uint8_t Nbit : 1; /* No Fwd Bit */ +#elif (BYTE_ORDER == BIG_ENDIAN) + uint8_t ver : 4; + uint8_t type : 4; + uint8_t Nbit : 1; /* No Fwd Bit */ + uint8_t reserved : 7; +#else +#error"Please set byte order" +#endif uint16_t checksum; } __attribute__((packed)); @@ -55,17 +75,48 @@ struct pim_encoded_ipv4_unicast { struct in_addr addr; } __attribute__((packed)); +/* + * Encoded Group format. RFC 4601 Sec 4.9.1 + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Addr Family | Encoding Type |B| Reserved |Z| Mask Len | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Group multicast Address + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+... + */ struct pim_encoded_group_ipv4 { - uint8_t ne; uint8_t family; - uint8_t reserved; + uint8_t ne; +#if (BYTE_ORDER == LITTLE_ENDIAN) + uint8_t sz : 1; /* scope zone bit */ + uint8_t reserved : 6; /* Reserved */ + uint8_t bidir : 1; /* Bidir bit */ +#elif (BYTE_ORDER == BIG_ENDIAN) + uint8_t bidir : 1; /* Bidir bit */ + uint8_t reserved : 6; /* Reserved */ + uint8_t sz : 1; /* scope zone bit */ +#else +#error"Please set byte order" +#endif uint8_t mask; struct in_addr addr; } __attribute__((packed)); + +/* + * Encoded Source format. RFC 4601 Sec 4.9.1 + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Addr Family | Encoding Type | Rsrvd |S|W|R| Mask Len | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Source Address + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-... + */ struct pim_encoded_source_ipv4 { - uint8_t ne; uint8_t family; + uint8_t ne; uint8_t bits; uint8_t mask; struct in_addr addr; @@ -88,7 +139,7 @@ struct pim_jp { } __attribute__((packed)); void pim_msg_build_header(uint8_t *pim_msg, size_t pim_msg_size, - uint8_t pim_msg_type); + uint8_t pim_msg_type, bool no_fwd); uint8_t *pim_msg_addr_encode_ipv4_ucast(uint8_t *buf, struct in_addr addr); uint8_t *pim_msg_addr_encode_ipv4_group(uint8_t *buf, struct in_addr addr); diff --git a/pimd/pim_neighbor.c b/pimd/pim_neighbor.c index a63b09fc1f..4d6625bf6f 100644 --- a/pimd/pim_neighbor.c +++ b/pimd/pim_neighbor.c @@ -40,6 +40,7 @@ #include "pim_join.h" #include "pim_jp_agg.h" #include "pim_bfd.h" +#include "pim_register.h" static void dr_election_by_addr(struct interface *ifp) { @@ -53,8 +54,7 @@ static void dr_election_by_addr(struct interface *ifp) pim_ifp->pim_dr_addr = pim_ifp->primary_address; if (PIM_DEBUG_PIM_TRACE) { - zlog_debug("%s: on interface %s", __PRETTY_FUNCTION__, - ifp->name); + zlog_debug("%s: on interface %s", __func__, ifp->name); } for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, node, neigh)) { @@ -79,14 +79,14 @@ static void dr_election_by_pri(struct interface *ifp) dr_pri = pim_ifp->pim_dr_priority; if (PIM_DEBUG_PIM_TRACE) { - zlog_debug("%s: dr pri %u on interface %s", __PRETTY_FUNCTION__, - dr_pri, ifp->name); + zlog_debug("%s: dr pri %u on interface %s", __func__, dr_pri, + ifp->name); } for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, node, neigh)) { if (PIM_DEBUG_PIM_TRACE) { zlog_info("%s: neigh pri %u addr %x if dr addr %x", - __PRETTY_FUNCTION__, neigh->dr_priority, + __func__, neigh->dr_priority, ntohl(neigh->source_addr.s_addr), ntohl(pim_ifp->pim_dr_addr.s_addr)); } @@ -133,8 +133,7 @@ int pim_if_dr_election(struct interface *ifp) pim_inet4_dump("", pim_ifp->pim_dr_addr, dr_new_str, sizeof(dr_new_str)); zlog_debug("%s: DR was %s now is %s on interface %s", - __PRETTY_FUNCTION__, dr_old_str, dr_new_str, - ifp->name); + __func__, dr_old_str, dr_new_str, ifp->name); } pim_ifp->pim_dr_election_last = @@ -143,6 +142,16 @@ int pim_if_dr_election(struct interface *ifp) pim_if_update_join_desired(pim_ifp); pim_if_update_could_assert(ifp); pim_if_update_assert_tracking_desired(ifp); + + if (PIM_I_am_DR(pim_ifp)) + pim_ifp->am_i_dr = true; + else { + if (pim_ifp->am_i_dr == true) { + pim_reg_del_on_couldreg_fail(ifp); + pim_ifp->am_i_dr = false; + } + } + return 1; } @@ -251,7 +260,7 @@ void pim_neighbor_timer_reset(struct pim_neighbor *neigh, uint16_t holdtime) pim_inet4_dump("", neigh->source_addr, src_str, sizeof(src_str)); zlog_debug("%s: starting %u sec timer for neighbor %s on %s", - __PRETTY_FUNCTION__, neigh->holdtime, src_str, + __func__, neigh->holdtime, src_str, neigh->interface->name); } @@ -269,7 +278,7 @@ static int on_neighbor_jp_timer(struct thread *t) pim_inet4_dump("", neigh->source_addr, src_str, sizeof(src_str)); zlog_debug("%s:Sending JP Agg to %s on %s with %d groups", - __PRETTY_FUNCTION__, src_str, neigh->interface->name, + __func__, src_str, neigh->interface->name, neigh->upstream_jp_agg->count); } @@ -338,7 +347,7 @@ pim_neighbor_new(struct interface *ifp, struct in_addr source_addr, if (PIM_DEBUG_PIM_EVENTS) { zlog_debug("%s: creating PIM neighbor %s on interface %s", - __PRETTY_FUNCTION__, src_str, ifp->name); + __func__, src_str, ifp->name); } zlog_info("PIM NEIGHBOR UP: neighbor %s on interface %s", src_str, @@ -390,7 +399,7 @@ static void delete_prefix_list(struct pim_neighbor *neigh) sizeof(addr_str)); zlog_debug( "%s: DUMP_PREFIX_LIST neigh=%x prefix_list=%x prefix=%x addr=%s [%d/%d]", - __PRETTY_FUNCTION__, (unsigned)neigh, + __func__, (unsigned)neigh, (unsigned)neigh->prefix_list, (unsigned)p, addr_str, i, list_size); ++i; @@ -424,10 +433,11 @@ struct pim_neighbor *pim_neighbor_find_by_secondary(struct interface *ifp, struct pim_neighbor *neigh; struct prefix *p; - pim_ifp = ifp->info; - if (!pim_ifp) + if (!ifp || !ifp->info) return NULL; + pim_ifp = ifp->info; + for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, node, neigh)) { for (ALL_LIST_ELEMENTS_RO(neigh->prefix_list, pnode, p)) { if (prefix_same(p, src)) @@ -501,7 +511,7 @@ pim_neighbor_add(struct interface *ifp, struct in_addr source_addr, if (PIM_DEBUG_PIM_TRACE_DETAIL) { char str[INET_ADDRSTRLEN]; pim_inet4_dump("", source_addr, str, sizeof(str)); - zlog_debug("%s: neighbor %s added ", __PRETTY_FUNCTION__, str); + zlog_debug("%s: neighbor %s added ", __func__, str); } /* RFC 4601: 4.3.2. DR Election @@ -656,7 +666,7 @@ void pim_neighbor_delete(struct interface *ifp, struct pim_neighbor *neigh, if (PIM_DEBUG_PIM_TRACE) { zlog_debug("%s: deleting PIM neighbor %s on interface %s", - __PRETTY_FUNCTION__, src_str, ifp->name); + __func__, src_str, ifp->name); } // De-Register PIM Neighbor with BFD @@ -766,7 +776,7 @@ static void delete_from_neigh_addr(struct interface *ifp, other_neigh_str, ifp->name); listnode_delete(neigh->prefix_list, p); - prefix_free(p); + prefix_free(&p); } } @@ -780,6 +790,7 @@ void pim_neighbor_update(struct pim_neighbor *neigh, uint32_t dr_priority, struct list *addr_list) { struct pim_interface *pim_ifp = neigh->interface->info; + uint32_t old, new; /* Received holdtime ? */ if (PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_HOLDTIME)) { @@ -792,7 +803,7 @@ void pim_neighbor_update(struct pim_neighbor *neigh, #ifdef DUMP_PREFIX_LIST zlog_debug( "%s: DUMP_PREFIX_LIST old_prefix_list=%x old_size=%d new_prefix_list=%x new_size=%d", - __PRETTY_FUNCTION__, (unsigned)neigh->prefix_list, + __func__, (unsigned)neigh->prefix_list, neigh->prefix_list ? (int)listcount(neigh->prefix_list) : -1, (unsigned)addr_list, addr_list ? (int)listcount(addr_list) : -1); @@ -803,7 +814,7 @@ void pim_neighbor_update(struct pim_neighbor *neigh, flog_err( EC_LIB_DEVELOPMENT, "%s: internal error: trying to replace same prefix list=%p", - __PRETTY_FUNCTION__, (void *)addr_list); + __func__, (void *)addr_list); } } else { /* Delete existing secondary address list */ @@ -819,6 +830,16 @@ void pim_neighbor_update(struct pim_neighbor *neigh, neigh->prefix_list = addr_list; update_dr_priority(neigh, hello_options, dr_priority); + new = PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY); + old = PIM_OPTION_IS_SET(neigh->hello_options, + PIM_OPTION_MASK_LAN_PRUNE_DELAY); + + if (old != new) { + if (old) + ++pim_ifp->pim_number_of_nonlandelay_neighbors; + else + --pim_ifp->pim_number_of_nonlandelay_neighbors; + } /* Copy flags */ diff --git a/pimd/pim_nht.c b/pimd/pim_nht.c index 8a459fe86e..f06d4ae605 100644 --- a/pimd/pim_nht.c +++ b/pimd/pim_nht.c @@ -61,7 +61,7 @@ void pim_sendmsg_zebra_rnh(struct pim_instance *pim, struct zclient *zclient, prefix2str(p, buf, sizeof(buf)); zlog_debug( "%s: NHT %sregistered addr %s(%s) with Zebra ret:%d ", - __PRETTY_FUNCTION__, + __func__, (command == ZEBRA_NEXTHOP_REGISTER) ? " " : "de", buf, pim->vrf->name, ret); } @@ -103,7 +103,7 @@ static struct pim_nexthop_cache *pim_nexthop_cache_add(struct pim_instance *pim, pnc->rp_list = list_new(); pnc->rp_list->cmp = pim_rp_list_cmp; - snprintf(hash_name, 64, "PNC %s(%s) Upstream Hash", + snprintf(hash_name, sizeof(hash_name), "PNC %s(%s) Upstream Hash", prefix2str(&pnc->rpf.rpf_addr, buf1, 64), pim->vrf->name); pnc->upstream_hash = hash_create_size(8192, pim_upstream_hash_key, pim_upstream_equal, hash_name); @@ -121,6 +121,7 @@ static struct pim_nexthop_cache *pim_nexthop_cache_add(struct pim_instance *pim, */ int pim_find_or_track_nexthop(struct pim_instance *pim, struct prefix *addr, struct pim_upstream *up, struct rp_info *rp, + bool bsr_track_needed, struct pim_nexthop_cache *out_pnc) { struct pim_nexthop_cache *pnc = NULL; @@ -144,7 +145,7 @@ int pim_find_or_track_nexthop(struct pim_instance *pim, struct prefix *addr, prefix2str(addr, buf, sizeof(buf)); zlog_debug( "%s: NHT cache and zebra notification added for %s(%s)", - __PRETTY_FUNCTION__, buf, pim->vrf->name); + __func__, buf, pim->vrf->name); } } @@ -157,6 +158,9 @@ int pim_find_or_track_nexthop(struct pim_instance *pim, struct prefix *addr, if (up != NULL) hash_get(pnc->upstream_hash, up, hash_alloc_intern); + if (bsr_track_needed) + pnc->bsr_tracking = true; + if (CHECK_FLAG(pnc->flags, PIM_NEXTHOP_VALID)) { if (out_pnc) memcpy(out_pnc, pnc, sizeof(struct pim_nexthop_cache)); @@ -167,12 +171,12 @@ int pim_find_or_track_nexthop(struct pim_instance *pim, struct prefix *addr, } void pim_delete_tracked_nexthop(struct pim_instance *pim, struct prefix *addr, - struct pim_upstream *up, struct rp_info *rp) + struct pim_upstream *up, struct rp_info *rp, + bool del_bsr_tracking) { struct pim_nexthop_cache *pnc = NULL; struct pim_nexthop_cache lookup; struct zclient *zclient = NULL; - struct listnode *upnode = NULL; struct pim_upstream *upstream = NULL; zclient = pim_zebra_zclient_get(); @@ -185,8 +189,8 @@ void pim_delete_tracked_nexthop(struct pim_instance *pim, struct prefix *addr, /* Release the (*, G)upstream from pnc->upstream_hash, * whose Group belongs to the RP getting deleted */ - for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, upnode, - upstream)) { + frr_each (rb_pim_upstream, &pim->upstream_head, + upstream) { struct prefix grp; struct rp_info *trp_info; @@ -208,17 +212,21 @@ void pim_delete_tracked_nexthop(struct pim_instance *pim, struct prefix *addr, if (up) hash_release(pnc->upstream_hash, up); + if (del_bsr_tracking) + pnc->bsr_tracking = false; + if (PIM_DEBUG_PIM_NHT) { char buf[PREFIX_STRLEN]; - prefix2str(addr, buf, sizeof buf); + prefix2str(addr, buf, sizeof(buf)); zlog_debug( "%s: NHT %s(%s) rp_list count:%d upstream count:%ld", - __PRETTY_FUNCTION__, buf, pim->vrf->name, + __func__, buf, pim->vrf->name, pnc->rp_list->count, pnc->upstream_hash->count); } if (pnc->rp_list->count == 0 - && pnc->upstream_hash->count == 0) { + && pnc->upstream_hash->count == 0 + && pnc->bsr_tracking == false) { pim_sendmsg_zebra_rnh(pim, zclient, pnc, ZEBRA_NEXTHOP_UNREGISTER); @@ -233,6 +241,167 @@ void pim_delete_tracked_nexthop(struct pim_instance *pim, struct prefix *addr, } } +/* Given a source address and a neighbor address, check if the neighbor is one + * of the next hop to reach the source. search from zebra route database + */ +bool pim_nexthop_match(struct pim_instance *pim, struct in_addr addr, + struct in_addr ip_src) +{ + struct pim_zlookup_nexthop nexthop_tab[MULTIPATH_NUM]; + int i = 0; + ifindex_t first_ifindex = 0; + struct interface *ifp = NULL; + struct pim_neighbor *nbr = NULL; + int num_ifindex; + + if (addr.s_addr == INADDR_NONE) + return false; + + memset(nexthop_tab, 0, + sizeof(struct pim_zlookup_nexthop) * MULTIPATH_NUM); + num_ifindex = zclient_lookup_nexthop(pim, nexthop_tab, MULTIPATH_NUM, + addr, PIM_NEXTHOP_LOOKUP_MAX); + if (num_ifindex < 1) { + char addr_str[INET_ADDRSTRLEN]; + + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + zlog_warn( + "%s %s: could not find nexthop ifindex for address %s", + __FILE__, __func__, addr_str); + return false; + } + + while (i < num_ifindex) { + first_ifindex = nexthop_tab[i].ifindex; + + ifp = if_lookup_by_index(first_ifindex, pim->vrf_id); + if (!ifp) { + if (PIM_DEBUG_ZEBRA) { + char addr_str[INET_ADDRSTRLEN]; + + pim_inet4_dump("", addr, addr_str, + sizeof(addr_str)); + zlog_debug( + "%s %s: could not find interface for ifindex %d (address %s)", + __FILE__, __func__, first_ifindex, + addr_str); + } + i++; + continue; + } + + if (!ifp->info) { + if (PIM_DEBUG_ZEBRA) { + char addr_str[INET_ADDRSTRLEN]; + + pim_inet4_dump("", addr, addr_str, + sizeof(addr_str)); + zlog_debug( + "%s: multicast not enabled on input interface %s (ifindex=%d, RPF for source %s)", + __func__, ifp->name, first_ifindex, + addr_str); + } + i++; + continue; + } + + if (!pim_if_connected_to_source(ifp, addr)) { + nbr = pim_neighbor_find( + ifp, nexthop_tab[i].nexthop_addr.u.prefix4); + if (PIM_DEBUG_PIM_TRACE_DETAIL) + zlog_debug("ifp name: %s, pim nbr: %p", + ifp->name, nbr); + if (!nbr && !if_is_loopback(ifp)) { + i++; + continue; + } + } + + if (nexthop_tab[i].nexthop_addr.u.prefix4.s_addr + == ip_src.s_addr) + return true; + + i++; + } + + return false; +} + +/* Given a source address and a neighbor address, check if the neighbor is one + * of the next hop to reach the source. search from pim next hop cache + */ +bool pim_nexthop_match_nht_cache(struct pim_instance *pim, struct in_addr addr, + struct in_addr ip_src) +{ + struct pim_rpf rpf; + ifindex_t first_ifindex; + struct interface *ifp = NULL; + uint8_t nh_iter = 0; + struct pim_neighbor *nbr = NULL; + struct nexthop *nh_node = NULL; + struct pim_nexthop_cache *pnc = NULL; + + memset(&rpf, 0, sizeof(struct pim_rpf)); + rpf.rpf_addr.family = AF_INET; + rpf.rpf_addr.prefixlen = IPV4_MAX_BITLEN; + rpf.rpf_addr.u.prefix4 = addr; + + pnc = pim_nexthop_cache_find(pim, &rpf); + if (!pnc || !pnc->nexthop_num) + return false; + + for (nh_node = pnc->nexthop; nh_node; nh_node = nh_node->next) { + first_ifindex = nh_node->ifindex; + ifp = if_lookup_by_index(first_ifindex, pim->vrf_id); + if (!ifp) { + if (PIM_DEBUG_PIM_NHT) { + char addr_str[INET_ADDRSTRLEN]; + + pim_inet4_dump("", addr, addr_str, + sizeof(addr_str)); + zlog_debug( + "%s %s: could not find interface for ifindex %d (address %s(%s))", + __FILE__, __func__, first_ifindex, + addr_str, pim->vrf->name); + } + nh_iter++; + continue; + } + if (!ifp->info) { + if (PIM_DEBUG_PIM_NHT) { + char addr_str[INET_ADDRSTRLEN]; + + pim_inet4_dump("", addr, addr_str, + sizeof(addr_str)); + zlog_debug( + "%s: multicast not enabled on input interface %s(%s) (ifindex=%d, RPF for source %s)", + __func__, ifp->name, pim->vrf->name, + first_ifindex, addr_str); + } + nh_iter++; + continue; + } + + if (!pim_if_connected_to_source(ifp, addr)) { + nbr = pim_neighbor_find(ifp, nh_node->gate.ipv4); + if (!nbr && !if_is_loopback(ifp)) { + if (PIM_DEBUG_PIM_NHT) + zlog_debug( + "%s: pim nbr not found on input interface %s(%s)", + __func__, ifp->name, + pim->vrf->name); + nh_iter++; + continue; + } + } + + if (nh_node->gate.ipv4.s_addr == ip_src.s_addr) + return true; + } + + return false; +} + void pim_rp_nexthop_del(struct rp_info *rp_info) { rp_info->rp.source_nexthop.interface = NULL; @@ -269,46 +438,35 @@ static int pim_update_upstream_nh_helper(struct hash_bucket *bucket, void *arg) { struct pim_instance *pim = (struct pim_instance *)arg; struct pim_upstream *up = (struct pim_upstream *)bucket->data; - int vif_index = 0; enum pim_rpf_result rpf_result; struct pim_rpf old; old.source_nexthop.interface = up->rpf.source_nexthop.interface; - rpf_result = pim_rpf_update(pim, up, &old, 0); - if (rpf_result == PIM_RPF_FAILURE) { - pim_upstream_rpf_clear(pim, up); - return HASHWALK_CONTINUE; - } + rpf_result = pim_rpf_update(pim, up, &old, __func__); - /* update kernel multicast forwarding cache (MFC) */ - if (up->rpf.source_nexthop.interface) { - ifindex_t ifindex = up->rpf.source_nexthop.interface->ifindex; - - vif_index = pim_if_find_vifindex_by_ifindex(pim, ifindex); - /* Pass Current selected NH vif index to mroute download - */ - if (vif_index) - pim_scan_individual_oil(up->channel_oil, vif_index); - else { - if (PIM_DEBUG_PIM_NHT) - zlog_debug( - "%s: NHT upstream %s channel_oil IIF %s vif_index is not valid", - __PRETTY_FUNCTION__, up->sg_str, - up->rpf.source_nexthop.interface->name); - } - } + /* update kernel multicast forwarding cache (MFC); if the + * RPF nbr is now unreachable the MFC has already been updated + * by pim_rpf_clear + */ + if (rpf_result != PIM_RPF_FAILURE) + pim_upstream_mroute_iif_update(up->channel_oil, __func__); - if (rpf_result == PIM_RPF_CHANGED) + if (rpf_result == PIM_RPF_CHANGED || + (rpf_result == PIM_RPF_FAILURE && old.source_nexthop.interface)) pim_zebra_upstream_rpf_changed(pim, up, &old); if (PIM_DEBUG_PIM_NHT) { - zlog_debug("%s: NHT upstream %s(%s) old ifp %s new ifp %s", - __PRETTY_FUNCTION__, up->sg_str, pim->vrf->name, - old.source_nexthop.interface - ? old.source_nexthop.interface->name : "Unknwon", - up->rpf.source_nexthop.interface->name); + zlog_debug( + "%s: NHT upstream %s(%s) old ifp %s new ifp %s", + __func__, up->sg_str, pim->vrf->name, + old.source_nexthop.interface ? old.source_nexthop + .interface->name + : "Unknown", + up->rpf.source_nexthop.interface ? up->rpf.source_nexthop + .interface->name + : "Unknown"); } return HASHWALK_CONTINUE; @@ -402,8 +560,15 @@ static int pim_ecmp_nexthop_search(struct pim_instance *pim, if (PIM_DEBUG_PIM_NHT) zlog_debug( "%s: current nexthop does not have nbr ", - __PRETTY_FUNCTION__); + __func__); } else { + /* update metric even if the upstream + * neighbor stays unchanged + */ + nexthop->mrib_metric_preference = + pnc->distance; + nexthop->mrib_route_metric = + pnc->metric; if (PIM_DEBUG_PIM_NHT) { char src_str[INET_ADDRSTRLEN]; pim_inet4_dump("", @@ -417,9 +582,8 @@ static int pim_ecmp_nexthop_search(struct pim_instance *pim, sizeof(grp_str)); zlog_debug( "%s: (%s,%s)(%s) current nexthop %s is valid, skipping new path selection", - __PRETTY_FUNCTION__, - src_str, grp_str, - pim->vrf->name, + __func__, src_str, + grp_str, pim->vrf->name, nexthop->interface->name); } return 1; @@ -469,9 +633,8 @@ static int pim_ecmp_nexthop_search(struct pim_instance *pim, addr_str, sizeof(addr_str)); zlog_debug( "%s %s: could not find interface for ifindex %d (address %s(%s))", - __FILE__, __PRETTY_FUNCTION__, - first_ifindex, addr_str, - pim->vrf->name); + __FILE__, __func__, first_ifindex, + addr_str, pim->vrf->name); } if (nh_iter == mod_val) mod_val++; // Select nexthpath @@ -485,9 +648,8 @@ static int pim_ecmp_nexthop_search(struct pim_instance *pim, addr_str, sizeof(addr_str)); zlog_debug( "%s: multicast not enabled on input interface %s(%s) (ifindex=%d, RPF for source %s)", - __PRETTY_FUNCTION__, ifp->name, - pim->vrf->name, first_ifindex, - addr_str); + __func__, ifp->name, pim->vrf->name, + first_ifindex, addr_str); } if (nh_iter == mod_val) mod_val++; // Select nexthpath @@ -502,7 +664,7 @@ static int pim_ecmp_nexthop_search(struct pim_instance *pim, if (PIM_DEBUG_PIM_NHT) zlog_debug( "%s: pim nbr not found on input interface %s(%s)", - __PRETTY_FUNCTION__, ifp->name, + __func__, ifp->name, pim->vrf->name); if (nh_iter == mod_val) mod_val++; // Select nexthpath @@ -537,9 +699,9 @@ static int pim_ecmp_nexthop_search(struct pim_instance *pim, buf, sizeof(buf)); zlog_debug( "%s: (%s,%s)(%s) selected nhop interface %s addr %s mod_val %u iter %d ecmp %d", - __PRETTY_FUNCTION__, buf2, buf3, - pim->vrf->name, ifp->name, buf, mod_val, - nh_iter, pim->ecmp_enable); + __func__, buf2, buf3, pim->vrf->name, + ifp->name, buf, mod_val, nh_iter, + pim->ecmp_enable); } } nh_iter++; @@ -553,8 +715,7 @@ static int pim_ecmp_nexthop_search(struct pim_instance *pim, /* This API is used to parse Registered address nexthop update coming from Zebra */ -int pim_parse_nexthop_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +int pim_parse_nexthop_update(ZAPI_CALLBACK_ARGS) { struct nexthop *nexthop; struct nexthop *nhlist_head = NULL; @@ -574,14 +735,12 @@ int pim_parse_nexthop_update(int command, struct zclient *zclient, pim = vrf->info; if (!zapi_nexthop_update_decode(zclient->ibuf, &nhr)) { - if (PIM_DEBUG_PIM_NHT) - zlog_debug( - "%s: Decode of nexthop update from zebra failed", - __PRETTY_FUNCTION__); + zlog_err("%s: Decode of nexthop update from zebra failed", + __func__); return 0; } - if (command == ZEBRA_NEXTHOP_UPDATE) { + if (cmd == ZEBRA_NEXTHOP_UPDATE) { prefix_copy(&rpf.rpf_addr, &nhr.prefix); pnc = pim_nexthop_cache_find(pim, &rpf); if (!pnc) { @@ -590,7 +749,7 @@ int pim_parse_nexthop_update(int command, struct zclient *zclient, prefix2str(&rpf.rpf_addr, buf, sizeof(buf)); zlog_debug( "%s: Skipping NHT update, addr %s is not in local cached DB.", - __PRETTY_FUNCTION__, buf); + __func__, buf); } return 0; } @@ -627,7 +786,11 @@ int pim_parse_nexthop_update(int command, struct zclient *zclient, case NEXTHOP_TYPE_IPV6_IFINDEX: ifp1 = if_lookup_by_index(nexthop->ifindex, pim->vrf_id); - nbr = pim_neighbor_find_if(ifp1); + + if (!ifp1) + nbr = NULL; + else + nbr = pim_neighbor_find_if(ifp1); /* Overwrite with Nbr address as NH addr */ if (nbr) nexthop->gate.ipv4 = nbr->source_addr; @@ -647,8 +810,7 @@ int pim_parse_nexthop_update(int command, struct zclient *zclient, char buf[NEXTHOP_STRLEN]; zlog_debug( "%s: could not find interface for ifindex %d(%s) (addr %s)", - __PRETTY_FUNCTION__, - nexthop->ifindex, + __func__, nexthop->ifindex, pim->vrf->name, nexthop2str(nexthop, buf, sizeof(buf))); @@ -663,27 +825,32 @@ int pim_parse_nexthop_update(int command, struct zclient *zclient, prefix2str(&nhr.prefix, p_str, sizeof(p_str)); zlog_debug( "%s: NHT addr %s(%s) %d-nhop via %s(%s) type %d distance:%u metric:%u ", - __PRETTY_FUNCTION__, p_str, - pim->vrf->name, i + 1, + __func__, p_str, pim->vrf->name, i + 1, inet_ntoa(nexthop->gate.ipv4), ifp->name, nexthop->type, nhr.distance, nhr.metric); } if (!ifp->info) { + /* + * Though Multicast is not enabled on this + * Interface store it in database otheriwse we + * may miss this update and this will not cause + * any issue, because while choosing the path we + * are ommitting the Interfaces which are not + * multicast enabled + */ if (PIM_DEBUG_PIM_NHT) { char buf[NEXTHOP_STRLEN]; zlog_debug( "%s: multicast not enabled on input interface %s(%s) (ifindex=%d, addr %s)", - __PRETTY_FUNCTION__, ifp->name, + __func__, ifp->name, pim->vrf->name, nexthop->ifindex, nexthop2str(nexthop, buf, sizeof(buf))); } - nexthop_free(nexthop); - continue; } if (nhlist_tail) { @@ -717,9 +884,9 @@ int pim_parse_nexthop_update(int command, struct zclient *zclient, prefix2str(&nhr.prefix, buf, sizeof(buf)); zlog_debug( "%s: NHT Update for %s(%s) num_nh %d num_pim_nh %d vrf:%u up %ld rp %d", - __PRETTY_FUNCTION__, buf, pim->vrf->name, - nhr.nexthop_num, pnc->nexthop_num, vrf_id, - pnc->upstream_hash->count, listcount(pnc->rp_list)); + __func__, buf, pim->vrf->name, nhr.nexthop_num, + pnc->nexthop_num, vrf_id, pnc->upstream_hash->count, + listcount(pnc->rp_list)); } pim_rpf_set_refresh_time(pim); @@ -753,7 +920,7 @@ int pim_ecmp_nexthop_lookup(struct pim_instance *pim, pim_inet4_dump("", src->u.prefix4, addr_str, sizeof(addr_str)); zlog_debug("%s: Looking up: %s(%s), last lookup time: %lld", - __PRETTY_FUNCTION__, addr_str, pim->vrf->name, + __func__, addr_str, pim->vrf->name, nexthop->last_lookup_time); } @@ -778,7 +945,7 @@ int pim_ecmp_nexthop_lookup(struct pim_instance *pim, if (PIM_DEBUG_PIM_NHT) zlog_warn( "%s: could not find nexthop ifindex for address %s(%s)", - __PRETTY_FUNCTION__, addr_str, pim->vrf->name); + __func__, addr_str, pim->vrf->name); return 0; } @@ -815,8 +982,8 @@ int pim_ecmp_nexthop_lookup(struct pim_instance *pim, hash_val = pim_compute_ecmp_hash(src, grp); mod_val = hash_val % consider; if (PIM_DEBUG_PIM_NHT_DETAIL) - zlog_debug("%s: hash_val %u mod_val %u", - __PRETTY_FUNCTION__, hash_val, mod_val); + zlog_debug("%s: hash_val %u mod_val %u", __func__, + hash_val, mod_val); } i = 0; @@ -828,9 +995,8 @@ int pim_ecmp_nexthop_lookup(struct pim_instance *pim, if (PIM_DEBUG_PIM_NHT) zlog_debug( "%s %s: could not find interface for ifindex %d (address %s(%s))", - __FILE__, __PRETTY_FUNCTION__, - first_ifindex, addr_str, - pim->vrf->name); + __FILE__, __func__, first_ifindex, + addr_str, pim->vrf->name); if (i == mod_val) mod_val++; i++; @@ -841,9 +1007,8 @@ int pim_ecmp_nexthop_lookup(struct pim_instance *pim, if (PIM_DEBUG_PIM_NHT) zlog_debug( "%s: multicast not enabled on input interface %s(%s) (ifindex=%d, RPF for source %s)", - __PRETTY_FUNCTION__, ifp->name, - pim->vrf->name, first_ifindex, - addr_str); + __func__, ifp->name, pim->vrf->name, + first_ifindex, addr_str); if (i == mod_val) mod_val++; i++; @@ -862,7 +1027,7 @@ int pim_ecmp_nexthop_lookup(struct pim_instance *pim, if (PIM_DEBUG_PIM_NHT) zlog_debug( "%s: NBR not found on input interface %s(%s) (RPF for source %s)", - __PRETTY_FUNCTION__, ifp->name, + __func__, ifp->name, pim->vrf->name, addr_str); continue; } @@ -877,8 +1042,8 @@ int pim_ecmp_nexthop_lookup(struct pim_instance *pim, nexthop_str, sizeof(nexthop_str)); zlog_debug( "%s: found nhop %s for addr %s interface %s(%s) metric %d dist %d", - __PRETTY_FUNCTION__, nexthop_str, - addr_str, ifp->name, pim->vrf->name, + __func__, nexthop_str, addr_str, + ifp->name, pim->vrf->name, nexthop_tab[i].route_metric, nexthop_tab[i].protocol_distance); } @@ -917,11 +1082,11 @@ int pim_ecmp_fib_lookup_if_vif_index(struct pim_instance *pim, sizeof(addr_str)); memset(&nhop, 0, sizeof(nhop)); - if (!pim_ecmp_nexthop_lookup(pim, &nhop, src, grp, 0)) { + if (!pim_ecmp_nexthop_lookup(pim, &nhop, src, grp, 1)) { if (PIM_DEBUG_PIM_NHT) zlog_debug( "%s: could not find nexthop ifindex for address %s(%s)", - __PRETTY_FUNCTION__, addr_str, pim->vrf->name); + __func__, addr_str, pim->vrf->name); return -1; } @@ -929,8 +1094,7 @@ int pim_ecmp_fib_lookup_if_vif_index(struct pim_instance *pim, if (PIM_DEBUG_PIM_NHT) zlog_debug( "%s: found nexthop ifindex=%d (interface %s(%s)) for address %s", - __PRETTY_FUNCTION__, ifindex, - ifindex2ifname(ifindex, pim->vrf_id), + __func__, ifindex, ifindex2ifname(ifindex, pim->vrf_id), pim->vrf->name, addr_str); vif_index = pim_if_find_vifindex_by_ifindex(pim, ifindex); @@ -939,8 +1103,7 @@ int pim_ecmp_fib_lookup_if_vif_index(struct pim_instance *pim, if (PIM_DEBUG_PIM_NHT) { zlog_debug( "%s: low vif_index=%d(%s) < 1 nexthop for address %s", - __PRETTY_FUNCTION__, vif_index, pim->vrf->name, - addr_str); + __func__, vif_index, pim->vrf->name, addr_str); } return -2; } diff --git a/pimd/pim_nht.h b/pimd/pim_nht.h index 13bb0fcb55..12dbf167d1 100644 --- a/pimd/pim_nht.h +++ b/pimd/pim_nht.h @@ -45,15 +45,22 @@ struct pim_nexthop_cache { struct list *rp_list; struct hash *upstream_hash; + /* Ideally this has to be list of scope zone. But for now we can just + * have as a bool variable to say bsr_tracking. + * Later this variable can be changed as a list of scope zones for + * tracking same bsr for multiple scope zones. + */ + bool bsr_tracking; }; -int pim_parse_nexthop_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id); +int pim_parse_nexthop_update(ZAPI_CALLBACK_ARGS); int pim_find_or_track_nexthop(struct pim_instance *pim, struct prefix *addr, struct pim_upstream *up, struct rp_info *rp, + bool bsr_track_needed, struct pim_nexthop_cache *out_pnc); void pim_delete_tracked_nexthop(struct pim_instance *pim, struct prefix *addr, - struct pim_upstream *up, struct rp_info *rp); + struct pim_upstream *up, struct rp_info *rp, + bool del_bsr_tracking); struct pim_nexthop_cache *pim_nexthop_cache_find(struct pim_instance *pim, struct pim_rpf *rpf); uint32_t pim_compute_ecmp_hash(struct prefix *src, struct prefix *grp); @@ -65,4 +72,9 @@ void pim_sendmsg_zebra_rnh(struct pim_instance *pim, struct zclient *zclient, int pim_ecmp_fib_lookup_if_vif_index(struct pim_instance *pim, struct prefix *src, struct prefix *grp); void pim_rp_nexthop_del(struct rp_info *rp_info); +bool pim_nexthop_match(struct pim_instance *pim, struct in_addr addr, + struct in_addr ip_src); +bool pim_nexthop_match_nht_cache(struct pim_instance *pim, struct in_addr addr, + struct in_addr ip_src); + #endif diff --git a/pimd/pim_oil.c b/pimd/pim_oil.c index 5945bc55fd..3ec0720fc4 100644 --- a/pimd/pim_oil.c +++ b/pimd/pim_oil.c @@ -31,25 +31,29 @@ #include "pim_str.h" #include "pim_iface.h" #include "pim_time.h" +#include "pim_vxlan.h" -// struct list *pim_channel_oil_list = NULL; -// struct hash *pim_channel_oil_hash = NULL; +static void pim_channel_update_mute(struct channel_oil *c_oil); char *pim_channel_oil_dump(struct channel_oil *c_oil, char *buf, size_t size) { char *out; + struct interface *ifp; struct prefix_sg sg; int i; sg.src = c_oil->oil.mfcc_origin; sg.grp = c_oil->oil.mfcc_mcastgrp; - snprintf(buf, size, "%s IIF: %d, OIFS: ", pim_str_sg_dump(&sg), - c_oil->oil.mfcc_parent); + ifp = pim_if_find_by_vif_index(c_oil->pim, c_oil->oil.mfcc_parent); + snprintf(buf, size, "%s IIF: %s, OIFS: ", pim_str_sg_dump(&sg), + ifp ? ifp->name : "(?)"); out = buf + strlen(buf); for (i = 0; i < MAXVIFS; i++) { if (c_oil->oil.mfcc_ttls[i] != 0) { - snprintf(out, buf + size - out, "%d ", i); + ifp = pim_if_find_by_vif_index(c_oil->pim, i); + snprintf(out, buf + size - out, "%s ", + ifp ? ifp->name : "(?)"); out += strlen(out); } } @@ -57,8 +61,8 @@ char *pim_channel_oil_dump(struct channel_oil *c_oil, char *buf, size_t size) return buf; } -static int pim_channel_oil_compare(struct channel_oil *c1, - struct channel_oil *c2) +int pim_channel_oil_compare(const struct channel_oil *c1, + const struct channel_oil *c2) { if (ntohl(c1->oil.mfcc_mcastgrp.s_addr) < ntohl(c2->oil.mfcc_mcastgrp.s_addr)) @@ -79,48 +83,19 @@ static int pim_channel_oil_compare(struct channel_oil *c1, return 0; } -static bool pim_oil_equal(const void *arg1, const void *arg2) -{ - const struct channel_oil *c1 = (const struct channel_oil *)arg1; - const struct channel_oil *c2 = (const struct channel_oil *)arg2; - - if ((c1->oil.mfcc_mcastgrp.s_addr == c2->oil.mfcc_mcastgrp.s_addr) - && (c1->oil.mfcc_origin.s_addr == c2->oil.mfcc_origin.s_addr)) - return true; - - return false; -} - -static unsigned int pim_oil_hash_key(void *arg) -{ - struct channel_oil *oil = (struct channel_oil *)arg; - - return jhash_2words(oil->oil.mfcc_mcastgrp.s_addr, - oil->oil.mfcc_origin.s_addr, 0); -} - void pim_oil_init(struct pim_instance *pim) { - char hash_name[64]; - - snprintf(hash_name, 64, "PIM %s Oil Hash", pim->vrf->name); - pim->channel_oil_hash = hash_create_size(8192, pim_oil_hash_key, - pim_oil_equal, hash_name); - - pim->channel_oil_list = list_new(); - pim->channel_oil_list->del = (void (*)(void *))pim_channel_oil_free; - pim->channel_oil_list->cmp = - (int (*)(void *, void *))pim_channel_oil_compare; + rb_pim_oil_init(&pim->channel_oil_head); } void pim_oil_terminate(struct pim_instance *pim) { - if (pim->channel_oil_list) - list_delete(&pim->channel_oil_list); + struct channel_oil *c_oil; - if (pim->channel_oil_hash) - hash_free(pim->channel_oil_hash); - pim->channel_oil_hash = NULL; + while ((c_oil = rb_pim_oil_pop(&pim->channel_oil_head))) + pim_channel_oil_free(c_oil); + + rb_pim_oil_fini(&pim->channel_oil_head); } void pim_channel_oil_free(struct channel_oil *c_oil) @@ -137,67 +112,75 @@ struct channel_oil *pim_find_channel_oil(struct pim_instance *pim, lookup.oil.mfcc_mcastgrp = sg->grp; lookup.oil.mfcc_origin = sg->src; - c_oil = hash_lookup(pim->channel_oil_hash, &lookup); + c_oil = rb_pim_oil_find(&pim->channel_oil_head, &lookup); return c_oil; } struct channel_oil *pim_channel_oil_add(struct pim_instance *pim, struct prefix_sg *sg, - int input_vif_index) + const char *name) { struct channel_oil *c_oil; - struct interface *ifp; c_oil = pim_find_channel_oil(pim, sg); if (c_oil) { - if (c_oil->oil.mfcc_parent != input_vif_index) { - c_oil->oil_inherited_rescan = 1; - if (PIM_DEBUG_MROUTE) - zlog_debug( - "%s: Existing channel oil %s points to %d, modifying to point at %d", - __PRETTY_FUNCTION__, - pim_str_sg_dump(sg), - c_oil->oil.mfcc_parent, - input_vif_index); - } - c_oil->oil.mfcc_parent = input_vif_index; ++c_oil->oil_ref_count; - c_oil->up = pim_upstream_find( - pim, sg); // channel might be present prior to upstream - return c_oil; - } - if (input_vif_index != MAXVIFS) { - ifp = pim_if_find_by_vif_index(pim, input_vif_index); - if (!ifp) { - /* warning only */ - zlog_warn( - "%s: (S,G)=%s could not find input interface for input_vif_index=%d", - __PRETTY_FUNCTION__, pim_str_sg_dump(sg), - input_vif_index); + if (!c_oil->up) { + /* channel might be present prior to upstream */ + c_oil->up = pim_upstream_find( + pim, sg); + /* if the upstream entry is being anchored to an + * already existing channel OIL we need to re-evaluate + * the "Mute" state on AA OIFs + */ + pim_channel_update_mute(c_oil); } + + /* check if the IIF has changed + * XXX - is this really needed + */ + pim_upstream_mroute_iif_update(c_oil, __func__); + + if (PIM_DEBUG_MROUTE) + zlog_debug( + "%s(%s): Existing oil for %pSG4 Ref Count: %d (Post Increment)", + __func__, name, sg, c_oil->oil_ref_count); + return c_oil; } c_oil = XCALLOC(MTYPE_PIM_CHANNEL_OIL, sizeof(*c_oil)); c_oil->oil.mfcc_mcastgrp = sg->grp; c_oil->oil.mfcc_origin = sg->src; - c_oil = hash_get(pim->channel_oil_hash, c_oil, hash_alloc_intern); - c_oil->oil.mfcc_parent = input_vif_index; + c_oil->oil.mfcc_parent = MAXVIFS; c_oil->oil_ref_count = 1; c_oil->installed = 0; c_oil->up = pim_upstream_find(pim, sg); c_oil->pim = pim; - listnode_add_sort(pim->channel_oil_list, c_oil); + rb_pim_oil_add(&pim->channel_oil_head, c_oil); + + if (PIM_DEBUG_MROUTE) + zlog_debug("%s(%s): c_oil %s add", + __func__, name, pim_str_sg_dump(sg)); return c_oil; } -void pim_channel_oil_del(struct channel_oil *c_oil) +struct channel_oil *pim_channel_oil_del(struct channel_oil *c_oil, + const char *name) { + if (PIM_DEBUG_MROUTE) { + struct prefix_sg sg = {.src = c_oil->oil.mfcc_mcastgrp, + .grp = c_oil->oil.mfcc_origin}; + + zlog_debug( + "%s(%s): Del oil for %pSG4, Ref Count: %d (Predecrement)", + __func__, name, &sg, c_oil->oil_ref_count); + } --c_oil->oil_ref_count; if (c_oil->oil_ref_count < 1) { @@ -207,15 +190,32 @@ void pim_channel_oil_del(struct channel_oil *c_oil) * called by list_delete_all_node() */ c_oil->up = NULL; - listnode_delete(c_oil->pim->channel_oil_list, c_oil); - hash_release(c_oil->pim->channel_oil_hash, c_oil); + rb_pim_oil_del(&c_oil->pim->channel_oil_head, c_oil); pim_channel_oil_free(c_oil); + return NULL; + } + + return c_oil; +} + +void pim_channel_oil_upstream_deref(struct channel_oil *c_oil) +{ + /* The upstream entry associated with a channel_oil is abt to be + * deleted. If the channel_oil is kept around because of other + * references we need to remove upstream based states out of it. + */ + c_oil = pim_channel_oil_del(c_oil, __func__); + if (c_oil) { + /* note: here we assume that c_oil->up has already been + * cleared + */ + pim_channel_update_mute(c_oil); } } int pim_channel_del_oif(struct channel_oil *channel_oil, struct interface *oif, - uint32_t proto_mask) + uint32_t proto_mask, const char *caller) { struct pim_interface *pim_ifp; @@ -240,7 +240,7 @@ int pim_channel_del_oif(struct channel_oil *channel_oil, struct interface *oif, sizeof(source_str)); zlog_debug( "%s %s: no existing protocol mask %u(%u) for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)", - __FILE__, __PRETTY_FUNCTION__, proto_mask, + __FILE__, __func__, proto_mask, channel_oil ->oif_flags[pim_ifp->mroute_vif_index], oif->name, pim_ifp->mroute_vif_index, @@ -253,7 +253,8 @@ int pim_channel_del_oif(struct channel_oil *channel_oil, struct interface *oif, channel_oil->oif_flags[pim_ifp->mroute_vif_index] &= ~proto_mask; - if (channel_oil->oif_flags[pim_ifp->mroute_vif_index]) { + if (channel_oil->oif_flags[pim_ifp->mroute_vif_index] & + PIM_OIF_FLAG_PROTO_ANY) { if (PIM_DEBUG_MROUTE) { char group_str[INET_ADDRSTRLEN]; char source_str[INET_ADDRSTRLEN]; @@ -265,7 +266,7 @@ int pim_channel_del_oif(struct channel_oil *channel_oil, struct interface *oif, sizeof(source_str)); zlog_debug( "%s %s: other protocol masks remain for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)", - __FILE__, __PRETTY_FUNCTION__, oif->name, + __FILE__, __func__, oif->name, pim_ifp->mroute_vif_index, channel_oil->oil .mfcc_ttls[pim_ifp->mroute_vif_index], @@ -275,8 +276,10 @@ int pim_channel_del_oif(struct channel_oil *channel_oil, struct interface *oif, } channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = 0; + /* clear mute; will be re-evaluated when the OIF becomes valid again */ + channel_oil->oif_flags[pim_ifp->mroute_vif_index] &= ~PIM_OIF_FLAG_MUTE; - if (pim_mroute_add(channel_oil, __PRETTY_FUNCTION__)) { + if (pim_upstream_mroute_add(channel_oil, __func__)) { if (PIM_DEBUG_MROUTE) { char group_str[INET_ADDRSTRLEN]; char source_str[INET_ADDRSTRLEN]; @@ -288,7 +291,7 @@ int pim_channel_del_oif(struct channel_oil *channel_oil, struct interface *oif, sizeof(source_str)); zlog_debug( "%s %s: could not remove output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)", - __FILE__, __PRETTY_FUNCTION__, oif->name, + __FILE__, __func__, oif->name, pim_ifp->mroute_vif_index, source_str, group_str); } @@ -305,22 +308,137 @@ int pim_channel_del_oif(struct channel_oil *channel_oil, struct interface *oif, pim_inet4_dump("", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str)); zlog_debug( - "%s %s: (S,G)=(%s,%s): proto_mask=%u IIF:%d OIF=%s vif_index=%d", - __FILE__, __PRETTY_FUNCTION__, source_str, group_str, - proto_mask, channel_oil->oil.mfcc_parent, oif->name, + "%s(%s): (S,G)=(%s,%s): proto_mask=%u IIF:%d OIF=%s vif_index=%d", + __func__, caller, source_str, group_str, proto_mask, + channel_oil->oil.mfcc_parent, oif->name, pim_ifp->mroute_vif_index); } return 0; } +void pim_channel_del_inherited_oif(struct channel_oil *c_oil, + struct interface *oif, const char *caller) +{ + struct pim_upstream *up = c_oil->up; + + pim_channel_del_oif(c_oil, oif, PIM_OIF_FLAG_PROTO_STAR, + caller); + + /* if an inherited OIF is being removed join-desired can change + * if the inherited OIL is now empty and KAT is running + */ + if (up && up->sg.src.s_addr != INADDR_ANY && + pim_upstream_empty_inherited_olist(up)) + pim_upstream_update_join_desired(up->pim, up); +} + +static bool pim_channel_eval_oif_mute(struct channel_oil *c_oil, + struct pim_interface *pim_ifp) +{ + struct pim_interface *pim_reg_ifp; + struct pim_interface *vxlan_ifp; + bool do_mute = false; + struct pim_instance *pim = c_oil->pim; + + if (!c_oil->up) + return do_mute; + + pim_reg_ifp = pim->regiface->info; + if (pim_ifp == pim_reg_ifp) { + /* suppress pimreg in the OIL if the mroute is not supposed to + * trigger register encapsulated data + */ + if (PIM_UPSTREAM_FLAG_TEST_NO_PIMREG_DATA(c_oil->up->flags)) + do_mute = true; + + return do_mute; + } + + vxlan_ifp = pim_vxlan_get_term_ifp(pim); + if (pim_ifp == vxlan_ifp) { + /* 1. vxlan termination device must never be added to the + * origination mroute (and that can actually happen because + * of XG inheritance from the termination mroute) otherwise + * traffic will end up looping. + * PS: This check has also been extended to non-orig mroutes + * that have a local SIP as such mroutes can move back and + * forth between orig<=>non-orig type. + * 2. vxlan termination device should be removed from the non-DF + * to prevent duplicates to the overlay rxer + */ + if (PIM_UPSTREAM_FLAG_TEST_SRC_VXLAN_ORIG(c_oil->up->flags) || + PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(c_oil->up->flags) || + pim_vxlan_is_local_sip(c_oil->up)) + do_mute = true; + + return do_mute; + } + + if (PIM_I_am_DualActive(pim_ifp)) { + struct pim_upstream *starup = c_oil->up->parent; + if (PIM_UPSTREAM_FLAG_TEST_MLAG_INTERFACE(c_oil->up->flags) + && (PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(c_oil->up->flags))) + do_mute = true; + + /* In case entry is (S,G), Negotiation happens at (*.G) */ + if (starup + + && PIM_UPSTREAM_FLAG_TEST_MLAG_INTERFACE(starup->flags) + && (PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(starup->flags))) + do_mute = true; + return do_mute; + } + return do_mute; +} + +void pim_channel_update_oif_mute(struct channel_oil *c_oil, + struct pim_interface *pim_ifp) +{ + bool old_mute; + bool new_mute; + + /* If pim_ifp is not a part of the OIL there is nothing to do */ + if (!c_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index]) + return; + + old_mute = !!(c_oil->oif_flags[pim_ifp->mroute_vif_index] & + PIM_OIF_FLAG_MUTE); + new_mute = pim_channel_eval_oif_mute(c_oil, pim_ifp); + if (old_mute == new_mute) + return; + + if (new_mute) + c_oil->oif_flags[pim_ifp->mroute_vif_index] |= + PIM_OIF_FLAG_MUTE; + else + c_oil->oif_flags[pim_ifp->mroute_vif_index] &= + ~PIM_OIF_FLAG_MUTE; + + pim_upstream_mroute_add(c_oil, __func__); +} + +/* pim_upstream has been set or cleared on the c_oil. re-eval mute state + * on all existing OIFs + */ +static void pim_channel_update_mute(struct channel_oil *c_oil) +{ + struct pim_interface *pim_reg_ifp; + struct pim_interface *vxlan_ifp; + + pim_reg_ifp = c_oil->pim->regiface->info; + if (pim_reg_ifp) + pim_channel_update_oif_mute(c_oil, pim_reg_ifp); + vxlan_ifp = pim_vxlan_get_term_ifp(c_oil->pim); + if (vxlan_ifp) + pim_channel_update_oif_mute(c_oil, vxlan_ifp); +} int pim_channel_add_oif(struct channel_oil *channel_oil, struct interface *oif, - uint32_t proto_mask) + uint32_t proto_mask, const char *caller) { struct pim_interface *pim_ifp; int old_ttl; - bool allow_iif_in_oil = false; /* * If we've gotten here we've gone bad, but let's @@ -333,46 +451,6 @@ int pim_channel_add_oif(struct channel_oil *channel_oil, struct interface *oif, pim_ifp = oif->info; -#ifdef PIM_ENFORCE_LOOPFREE_MFC - /* - Prevent creating MFC entry with OIF=IIF. - - This is a protection against implementation mistakes. - - PIM protocol implicitely ensures loopfree multicast topology. - - IGMP must be protected against adding looped MFC entries created - by both source and receiver attached to the same interface. See - TODO T22. - */ - if (channel_oil->up && - PIM_UPSTREAM_FLAG_TEST_ALLOW_IIF_IN_OIL( - channel_oil->up->flags)) { - allow_iif_in_oil = true; - } - - if (!allow_iif_in_oil && - pim_ifp->mroute_vif_index == channel_oil->oil.mfcc_parent) { - channel_oil->oil_inherited_rescan = 1; - if (PIM_DEBUG_MROUTE) { - char group_str[INET_ADDRSTRLEN]; - char source_str[INET_ADDRSTRLEN]; - pim_inet4_dump("", - channel_oil->oil.mfcc_mcastgrp, - group_str, sizeof(group_str)); - pim_inet4_dump("", - channel_oil->oil.mfcc_origin, source_str, - sizeof(source_str)); - zlog_debug( - "%s %s: refusing protocol mask %u request for IIF=OIF=%s (vif_index=%d) for channel (S,G)=(%s,%s)", - __FILE__, __PRETTY_FUNCTION__, proto_mask, - oif->name, pim_ifp->mroute_vif_index, - source_str, group_str); - } - return -2; - } -#endif - /* Prevent single protocol from subscribing same interface to channel (S,G) multiple times */ if (channel_oil->oif_flags[pim_ifp->mroute_vif_index] & proto_mask) { @@ -387,8 +465,8 @@ int pim_channel_add_oif(struct channel_oil *channel_oil, struct interface *oif, sizeof(source_str)); zlog_debug( "%s %s: existing protocol mask %u requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)", - __FILE__, __PRETTY_FUNCTION__, proto_mask, - oif->name, pim_ifp->mroute_vif_index, + __FILE__, __func__, proto_mask, oif->name, + pim_ifp->mroute_vif_index, channel_oil->oil .mfcc_ttls[pim_ifp->mroute_vif_index], source_str, group_str); @@ -421,15 +499,31 @@ int pim_channel_add_oif(struct channel_oil *channel_oil, struct interface *oif, source_str, sizeof(source_str)); zlog_warn( "%s %s: new protocol mask %u requested nonexistent OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)", - __FILE__, __PRETTY_FUNCTION__, - proto_mask, oif->name, - pim_ifp->mroute_vif_index, + __FILE__, __func__, proto_mask, + oif->name, pim_ifp->mroute_vif_index, channel_oil->oil.mfcc_ttls [pim_ifp->mroute_vif_index], source_str, group_str); } } + if (PIM_DEBUG_MROUTE) { + char group_str[INET_ADDRSTRLEN]; + char source_str[INET_ADDRSTRLEN]; + pim_inet4_dump("", + channel_oil->oil.mfcc_mcastgrp, + group_str, sizeof(group_str)); + pim_inet4_dump("", + channel_oil->oil.mfcc_origin, source_str, + sizeof(source_str)); + zlog_debug( + "%s(%s): (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d added to 0x%x", + __func__, caller, source_str, group_str, + proto_mask, oif->name, + pim_ifp->mroute_vif_index, + channel_oil + ->oif_flags[pim_ifp->mroute_vif_index]); + } return 0; } @@ -447,7 +541,7 @@ int pim_channel_add_oif(struct channel_oil *channel_oil, struct interface *oif, sizeof(source_str)); zlog_debug( "%s %s: interface %s (vif_index=%d) is existing output for channel (S,G)=(%s,%s)", - __FILE__, __PRETTY_FUNCTION__, oif->name, + __FILE__, __func__, oif->name, pim_ifp->mroute_vif_index, source_str, group_str); } @@ -457,11 +551,23 @@ int pim_channel_add_oif(struct channel_oil *channel_oil, struct interface *oif, channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = PIM_MROUTE_MIN_TTL; + /* Some OIFs are held in a muted state i.e. the PIM state machine + * decided to include the OIF but additional status check such as + * MLAG DF role prevent it from being activated for traffic + * forwarding. + */ + if (pim_channel_eval_oif_mute(channel_oil, pim_ifp)) + channel_oil->oif_flags[pim_ifp->mroute_vif_index] |= + PIM_OIF_FLAG_MUTE; + else + channel_oil->oif_flags[pim_ifp->mroute_vif_index] &= + ~PIM_OIF_FLAG_MUTE; + /* channel_oil->oil.mfcc_parent != MAXVIFS indicate this entry is not * valid to get installed in kernel. */ if (channel_oil->oil.mfcc_parent != MAXVIFS) { - if (pim_mroute_add(channel_oil, __PRETTY_FUNCTION__)) { + if (pim_upstream_mroute_add(channel_oil, __func__)) { if (PIM_DEBUG_MROUTE) { char group_str[INET_ADDRSTRLEN]; char source_str[INET_ADDRSTRLEN]; @@ -472,10 +578,10 @@ int pim_channel_add_oif(struct channel_oil *channel_oil, struct interface *oif, channel_oil->oil.mfcc_origin, source_str, sizeof(source_str)); zlog_debug( - "%s %s: could not add output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)", - __FILE__, __PRETTY_FUNCTION__, oif->name, - pim_ifp->mroute_vif_index, source_str, - group_str); + "%s %s: could not add output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)", + __FILE__, __func__, oif->name, + pim_ifp->mroute_vif_index, source_str, + group_str); } channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] @@ -497,9 +603,9 @@ int pim_channel_add_oif(struct channel_oil *channel_oil, struct interface *oif, pim_inet4_dump("", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str)); zlog_debug( - "%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d: DONE", - __FILE__, __PRETTY_FUNCTION__, source_str, group_str, - proto_mask, oif->name, pim_ifp->mroute_vif_index); + "%s(%s): (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d: DONE", + __func__, caller, source_str, group_str, proto_mask, + oif->name, pim_ifp->mroute_vif_index); } return 0; @@ -507,19 +613,15 @@ int pim_channel_add_oif(struct channel_oil *channel_oil, struct interface *oif, int pim_channel_oil_empty(struct channel_oil *c_oil) { - static uint32_t zero[MAXVIFS]; - static int inited = 0; + static struct mfcctl null_oil; if (!c_oil) return 1; - /* - * Not sure that this is necessary, but I would rather ensure - * that this works. - */ - if (!inited) { - memset(&zero, 0, sizeof(uint32_t) * MAXVIFS); - inited = 1; - } - return !memcmp(c_oil->oif_flags, zero, MAXVIFS * sizeof(uint32_t)); + /* exclude pimreg from the OIL when checking if the inherited_oil is + * non-NULL. + * pimreg device (in all vrfs) uses a vifi of + * 0 (PIM_OIF_PIM_REGISTER_VIF) so we simply mfcc_ttls[0] */ + return !memcmp(&c_oil->oil.mfcc_ttls[1], &null_oil.mfcc_ttls[1], + sizeof(null_oil.mfcc_ttls) - sizeof(null_oil.mfcc_ttls[0])); } diff --git a/pimd/pim_oil.h b/pimd/pim_oil.h index c5106d01cb..8a808afa73 100644 --- a/pimd/pim_oil.h +++ b/pimd/pim_oil.h @@ -21,6 +21,7 @@ #define PIM_OIL_H #include "pim_mroute.h" +#include "pim_iface.h" /* * Where did we get this (S,G) from? @@ -32,14 +33,14 @@ */ #define PIM_OIF_FLAG_PROTO_IGMP (1 << 0) #define PIM_OIF_FLAG_PROTO_PIM (1 << 1) -#define PIM_OIF_FLAG_PROTO_SOURCE (1 << 2) -#define PIM_OIF_FLAG_PROTO_STAR (1 << 3) -#define PIM_OIF_FLAG_PROTO_VXLAN (1 << 4) +#define PIM_OIF_FLAG_PROTO_STAR (1 << 2) +#define PIM_OIF_FLAG_PROTO_VXLAN (1 << 3) #define PIM_OIF_FLAG_PROTO_ANY \ (PIM_OIF_FLAG_PROTO_IGMP | PIM_OIF_FLAG_PROTO_PIM \ - | PIM_OIF_FLAG_PROTO_SOURCE | PIM_OIF_FLAG_PROTO_STAR \ - | PIM_OIF_FLAG_PROTO_VXLAN) + | PIM_OIF_FLAG_PROTO_STAR | PIM_OIF_FLAG_PROTO_VXLAN) +/* OIF is present in the OIL but must not be used for forwarding traffic */ +#define PIM_OIF_FLAG_MUTE (1 << 4) /* * We need a pimreg vif id from the kernel. * Since ifindex == vif id for most cases and the number @@ -53,10 +54,13 @@ struct channel_counts { unsigned long long lastused; + unsigned long origpktcnt; unsigned long pktcnt; unsigned long oldpktcnt; + unsigned long origbytecnt; unsigned long bytecnt; unsigned long oldbytecnt; + unsigned long origwrong_if; unsigned long wrong_if; unsigned long oldwrong_if; }; @@ -86,10 +90,13 @@ struct channel_counts { installed: indicate if this entry is installed in the kernel. */ +PREDECL_RBTREE_UNIQ(rb_pim_oil) struct channel_oil { struct pim_instance *pim; + struct rb_pim_oil_item oil_rb; + struct mfcctl oil; int installed; int oil_inherited_rescan; @@ -102,6 +109,12 @@ struct channel_oil { time_t mroute_creation; }; +extern int pim_channel_oil_compare(const struct channel_oil *c1, + const struct channel_oil *c2); +DECLARE_RBTREE_UNIQ(rb_pim_oil, struct channel_oil, oil_rb, + pim_channel_oil_compare) + + extern struct list *pim_channel_oil_list; void pim_oil_init(struct pim_instance *pim); @@ -112,15 +125,27 @@ struct channel_oil *pim_find_channel_oil(struct pim_instance *pim, struct prefix_sg *sg); struct channel_oil *pim_channel_oil_add(struct pim_instance *pim, struct prefix_sg *sg, - int input_vif_index); -void pim_channel_oil_del(struct channel_oil *c_oil); + const char *name); +void pim_channel_oil_change_iif(struct pim_instance *pim, + struct channel_oil *c_oil, int input_vif_index, + const char *name); +struct channel_oil *pim_channel_oil_del(struct channel_oil *c_oil, + const char *name); int pim_channel_add_oif(struct channel_oil *c_oil, struct interface *oif, - uint32_t proto_mask); + uint32_t proto_mask, const char *caller); int pim_channel_del_oif(struct channel_oil *c_oil, struct interface *oif, - uint32_t proto_mask); + uint32_t proto_mask, const char *caller); int pim_channel_oil_empty(struct channel_oil *c_oil); char *pim_channel_oil_dump(struct channel_oil *c_oil, char *buf, size_t size); + +void pim_channel_update_oif_mute(struct channel_oil *c_oil, + struct pim_interface *pim_ifp); + +void pim_channel_oil_upstream_deref(struct channel_oil *c_oil); +void pim_channel_del_inherited_oif(struct channel_oil *c_oil, + struct interface *oif, const char *caller); + #endif /* PIM_OIL_H */ diff --git a/pimd/pim_pim.c b/pimd/pim_pim.c index 71b0d47928..bf31d4e450 100644 --- a/pimd/pim_pim.c +++ b/pimd/pim_pim.c @@ -23,6 +23,7 @@ #include "thread.h" #include "memory.h" #include "if.h" +#include "network.h" #include "pimd.h" #include "pim_pim.h" @@ -39,9 +40,9 @@ #include "pim_msg.h" #include "pim_register.h" #include "pim_errors.h" +#include "pim_bsm.h" static int on_pim_hello_send(struct thread *t); -static int pim_hello_send(struct interface *ifp, uint16_t holdtime); static const char *pim_pim_msgtype2str(enum pim_msg_type type) { @@ -118,7 +119,7 @@ void pim_sock_delete(struct interface *ifp, const char *delete_message) if (!ifp->info) { flog_err(EC_PIM_CONFIG, "%s: %s: but PIM not enabled on interface %s (!)", - __PRETTY_FUNCTION__, delete_message, ifp->name); + __func__, delete_message, ifp->name); return; } @@ -136,6 +137,18 @@ void pim_sock_delete(struct interface *ifp, const char *delete_message) sock_close(ifp); } +/* For now check dst address for hello, assrt and join/prune is all pim rtr */ +static bool pim_pkt_dst_addr_ok(enum pim_msg_type type, in_addr_t addr) +{ + if ((type == PIM_MSG_TYPE_HELLO) || (type == PIM_MSG_TYPE_ASSERT) + || (type == PIM_MSG_TYPE_JOIN_PRUNE)) { + if (addr != qpim_all_pim_routers_addr.s_addr) + return false; + } + + return true; +} + int pim_pim_packet(struct interface *ifp, uint8_t *buf, size_t len) { struct ip *ip_hdr; @@ -148,6 +161,7 @@ int pim_pim_packet(struct interface *ifp, uint8_t *buf, size_t len) uint16_t checksum; /* computed checksum */ struct pim_neighbor *neigh; struct pim_msg_header *header; + bool no_fwd; if (len < sizeof(*ip_hdr)) { if (PIM_DEBUG_PIM_PACKETS) @@ -185,8 +199,15 @@ int pim_pim_packet(struct interface *ifp, uint8_t *buf, size_t len) /* for computing checksum */ header->checksum = 0; + no_fwd = header->Nbit; if (header->type == PIM_MSG_TYPE_REGISTER) { + if (pim_msg_len < PIM_MSG_REGISTER_LEN) { + if (PIM_DEBUG_PIM_PACKETS) + zlog_debug("PIM Register Message size=%d shorther than min length %d", + pim_msg_len, PIM_MSG_REGISTER_LEN); + return -1; + } /* First 8 byte header checksum */ checksum = in_cksum(pim_msg, PIM_MSG_REGISTER_LEN); if (checksum != pim_checksum) { @@ -224,10 +245,25 @@ int pim_pim_packet(struct interface *ifp, uint8_t *buf, size_t len) ifp->name, ip_hdr->ip_ttl, header->ver, pim_msg_len, checksum); if (PIM_DEBUG_PIM_PACKETDUMP_RECV) { - pim_pkt_dump(__PRETTY_FUNCTION__, pim_msg, pim_msg_len); + pim_pkt_dump(__func__, pim_msg, pim_msg_len); } } + if (!pim_pkt_dst_addr_ok(header->type, ip_hdr->ip_dst.s_addr)) { + char dst_str[INET_ADDRSTRLEN]; + char src_str[INET_ADDRSTRLEN]; + + pim_inet4_dump("", ip_hdr->ip_dst, dst_str, + sizeof(dst_str)); + pim_inet4_dump("", ip_hdr->ip_src, src_str, + sizeof(src_str)); + zlog_warn( + "%s: Ignoring Pkt. Unexpected IP destination %s for %s (Expected: all_pim_routers_addr) from %s", + __func__, dst_str, pim_pim_msgtype2str(header->type), + src_str); + return -1; + } + switch (header->type) { case PIM_MSG_TYPE_HELLO: return pim_hello_recv(ifp, ip_hdr->ip_src, @@ -249,8 +285,8 @@ int pim_pim_packet(struct interface *ifp, uint8_t *buf, size_t len) if (PIM_DEBUG_PIM_PACKETS) zlog_debug( "%s %s: non-hello PIM message type=%d from non-neighbor %s on %s", - __FILE__, __PRETTY_FUNCTION__, - header->type, src_str, ifp->name); + __FILE__, __func__, header->type, + src_str, ifp->name); return -1; } pim_neighbor_timer_reset(neigh, neigh->holdtime); @@ -264,8 +300,8 @@ int pim_pim_packet(struct interface *ifp, uint8_t *buf, size_t len) if (PIM_DEBUG_PIM_PACKETS) zlog_debug( "%s %s: non-hello PIM message type=%d from non-neighbor %s on %s", - __FILE__, __PRETTY_FUNCTION__, - header->type, src_str, ifp->name); + __FILE__, __func__, header->type, + src_str, ifp->name); return -1; } pim_neighbor_timer_reset(neigh, neigh->holdtime); @@ -273,6 +309,11 @@ int pim_pim_packet(struct interface *ifp, uint8_t *buf, size_t len) pim_msg + PIM_MSG_HEADER_LEN, pim_msg_len - PIM_MSG_HEADER_LEN); break; + case PIM_MSG_TYPE_BOOTSTRAP: + return pim_bsm_process(ifp, ip_hdr, pim_msg, pim_msg_len, + no_fwd); + break; + default: if (PIM_DEBUG_PIM_PACKETS) { zlog_debug( @@ -333,15 +374,15 @@ static int pim_sock_read(struct thread *t) if (PIM_DEBUG_PIM_PACKETS) zlog_debug( "%s: Received incoming pim packet on interface(%s:%d) not yet configured for pim", - __PRETTY_FUNCTION__, - ifp ? ifp->name : "Unknown", ifindex); + __func__, ifp ? ifp->name : "Unknown", + ifindex); goto done; } int fail = pim_pim_packet(ifp, buf, len); if (fail) { if (PIM_DEBUG_PIM_PACKETS) zlog_debug("%s: pim_pim_packet() return=%d", - __PRETTY_FUNCTION__, fail); + __func__, fail); goto done; } @@ -460,6 +501,7 @@ void pim_sock_reset(struct interface *ifp) pim_ifp->pim_dr_num_nondrpri_neighbors = 0; /* neighbors without dr_pri */ pim_ifp->pim_dr_addr = pim_ifp->primary_address; + pim_ifp->am_i_dr = true; pim_ifstat_reset(ifp); } @@ -499,18 +541,16 @@ static int pim_msg_send_frame(int fd, char *buf, size_t len, } return -1; - break; default: if (PIM_DEBUG_PIM_PACKETS) { pim_inet4_dump("", ip->ip_dst, dst_str, sizeof(dst_str)); zlog_warn( "%s: sendto() failure to %s: fd=%d msg_size=%zd: errno=%d: %s", - __PRETTY_FUNCTION__, dst_str, fd, len, - errno, safe_strerror(errno)); + __func__, dst_str, fd, len, errno, + safe_strerror(errno)); } return -1; - break; } } @@ -566,6 +606,7 @@ int pim_msg_send(int fd, struct in_addr src, struct in_addr dst, ip->ip_id = htons(++ip_id); ip->ip_hl = 5; ip->ip_v = 4; + ip->ip_tos = IPTOS_PREC_INTERNETCONTROL; ip->ip_p = PIM_IP_PROTO_PIM; ip->ip_src = src; ip->ip_dst = dst; @@ -575,9 +616,8 @@ int pim_msg_send(int fd, struct in_addr src, struct in_addr dst, if (PIM_DEBUG_PIM_PACKETS) { char dst_str[INET_ADDRSTRLEN]; pim_inet4_dump("", dst, dst_str, sizeof(dst_str)); - zlog_debug("%s: to %s on %s: msg_size=%d checksum=%x", - __PRETTY_FUNCTION__, dst_str, ifname, pim_msg_size, - header->checksum); + zlog_debug("%s: to %s on %s: msg_size=%d checksum=%x", __func__, + dst_str, ifname, pim_msg_size, header->checksum); } memset(&to, 0, sizeof(to)); @@ -586,7 +626,7 @@ int pim_msg_send(int fd, struct in_addr src, struct in_addr dst, tolen = sizeof(to); if (PIM_DEBUG_PIM_PACKETDUMP_SEND) { - pim_pkt_dump(__PRETTY_FUNCTION__, pim_msg, pim_msg_size); + pim_pkt_dump(__func__, pim_msg, pim_msg_size); } pim_msg_send_frame(fd, (char *)buffer, sendlen, (struct sockaddr *)&to, @@ -609,7 +649,7 @@ static int hello_send(struct interface *ifp, uint16_t holdtime) sizeof(dst_str)); zlog_debug( "%s: to %s on %s: holdt=%u prop_d=%u overr_i=%u dis_join_supp=%d dr_prio=%u gen_id=%08x addrs=%d", - __PRETTY_FUNCTION__, dst_str, ifp->name, holdtime, + __func__, dst_str, ifp->name, holdtime, pim_ifp->pim_propagation_delay_msec, pim_ifp->pim_override_interval_msec, PIM_IF_TEST_PIM_CAN_DISABLE_JOIN_SUPRESSION( @@ -634,7 +674,7 @@ static int hello_send(struct interface *ifp, uint16_t holdtime) zassert(pim_msg_size >= PIM_PIM_MIN_LEN); zassert(pim_msg_size <= PIM_PIM_BUFSIZE_WRITE); - pim_msg_build_header(pim_msg, pim_msg_size, PIM_MSG_TYPE_HELLO); + pim_msg_build_header(pim_msg, pim_msg_size, PIM_MSG_TYPE_HELLO, false); if (pim_msg_send(pim_ifp->pim_sock_fd, pim_ifp->primary_address, qpim_all_pim_routers_addr, pim_msg, pim_msg_size, @@ -642,7 +682,7 @@ static int hello_send(struct interface *ifp, uint16_t holdtime) if (PIM_DEBUG_PIM_HELLO) { zlog_debug( "%s: could not send PIM message on interface %s", - __PRETTY_FUNCTION__, ifp->name); + __func__, ifp->name); } return -2; } @@ -650,7 +690,7 @@ static int hello_send(struct interface *ifp, uint16_t holdtime) return 0; } -static int pim_hello_send(struct interface *ifp, uint16_t holdtime) +int pim_hello_send(struct interface *ifp, uint16_t holdtime) { struct pim_interface *pim_ifp = ifp->info; @@ -840,7 +880,7 @@ int pim_sock_add(struct interface *ifp) old_genid = pim_ifp->pim_generation_id; while (old_genid == pim_ifp->pim_generation_id) - pim_ifp->pim_generation_id = random(); + pim_ifp->pim_generation_id = frr_weak_random(); zlog_info("PIM INTERFACE UP: on interface %s ifindex=%d", ifp->name, ifp->ifindex); diff --git a/pimd/pim_pim.h b/pimd/pim_pim.h index e930ab7c2d..b9fdb14dc0 100644 --- a/pimd/pim_pim.h +++ b/pimd/pim_pim.h @@ -59,4 +59,5 @@ int pim_pim_packet(struct interface *ifp, uint8_t *buf, size_t len); int pim_msg_send(int fd, struct in_addr src, struct in_addr dst, uint8_t *pim_msg, int pim_msg_size, const char *ifname); +int pim_hello_send(struct interface *ifp, uint16_t holdtime); #endif /* PIM_PIM_H */ diff --git a/pimd/pim_register.c b/pimd/pim_register.c index 431236eebe..19e15f3ede 100644 --- a/pimd/pim_register.c +++ b/pimd/pim_register.c @@ -59,9 +59,9 @@ void pim_register_join(struct pim_upstream *up) } pim_channel_add_oif(up->channel_oil, pim->regiface, - PIM_OIF_FLAG_PROTO_PIM); + PIM_OIF_FLAG_PROTO_PIM, __func__); up->reg_state = PIM_REG_JOIN; - pim_vxlan_update_sg_reg_state(pim, up, TRUE /*reg_join*/); + pim_vxlan_update_sg_reg_state(pim, up, true /*reg_join*/); } void pim_register_stop_send(struct interface *ifp, struct prefix_sg *sg, @@ -94,12 +94,12 @@ void pim_register_stop_send(struct interface *ifp, struct prefix_sg *sg, b1length += length; pim_msg_build_header(buffer, b1length + PIM_MSG_REGISTER_STOP_LEN, - PIM_MSG_TYPE_REG_STOP); + PIM_MSG_TYPE_REG_STOP, false); pinfo = (struct pim_interface *)ifp->info; if (!pinfo) { if (PIM_DEBUG_PIM_TRACE) - zlog_debug("%s: No pinfo!", __PRETTY_FUNCTION__); + zlog_debug("%s: No pinfo!", __func__); return; } if (pim_msg_send(pinfo->pim_sock_fd, src, originator, buffer, @@ -107,7 +107,7 @@ void pim_register_stop_send(struct interface *ifp, struct prefix_sg *sg, if (PIM_DEBUG_PIM_TRACE) { zlog_debug( "%s: could not send PIM register stop message on interface %s", - __PRETTY_FUNCTION__, ifp->name); + __func__, ifp->name); } } ++pinfo->pim_ifstat_reg_stop_send; @@ -122,6 +122,8 @@ int pim_register_stop_recv(struct interface *ifp, uint8_t *buf, int buf_size) struct prefix_sg sg; int l; + ++pim_ifp->pim_ifstat_reg_stop_recv; + memset(&sg, 0, sizeof(struct prefix_sg)); l = pim_parse_addr_group(&sg, buf, buf_size); buf += l; @@ -141,20 +143,18 @@ int pim_register_stop_recv(struct interface *ifp, uint8_t *buf, int buf_size) case PIM_REG_NOINFO: case PIM_REG_PRUNE: return 0; - break; case PIM_REG_JOIN: upstream->reg_state = PIM_REG_PRUNE; pim_channel_del_oif(upstream->channel_oil, pim->regiface, - PIM_OIF_FLAG_PROTO_PIM); + PIM_OIF_FLAG_PROTO_PIM, __func__); pim_upstream_start_register_stop_timer(upstream, 0); pim_vxlan_update_sg_reg_state(pim, upstream, - FALSE /*reg_join*/); + false/*reg_join*/); break; case PIM_REG_JOIN_PENDING: upstream->reg_state = PIM_REG_PRUNE; pim_upstream_start_register_stop_timer(upstream, 0); return 0; - break; } return 0; @@ -179,15 +179,15 @@ void pim_register_send(const uint8_t *buf, int buf_size, struct in_addr src, if (!ifp) { if (PIM_DEBUG_PIM_REG) zlog_debug("%s: No interface to transmit register on", - __PRETTY_FUNCTION__); + __func__); return; } pinfo = (struct pim_interface *)ifp->info; if (!pinfo) { if (PIM_DEBUG_PIM_REG) zlog_debug( - "%s: Interface: %s not configured for pim to trasmit on!\n", - __PRETTY_FUNCTION__, ifp->name); + "%s: Interface: %s not configured for pim to transmit on!", + __func__, ifp->name); return; } @@ -196,8 +196,8 @@ void pim_register_send(const uint8_t *buf, int buf_size, struct in_addr src, strlcpy(rp_str, inet_ntoa(rpg->rpf_addr.u.prefix4), sizeof(rp_str)); zlog_debug("%s: Sending %s %sRegister Packet to %s on %s", - __PRETTY_FUNCTION__, up->sg_str, - null_register ? "NULL " : "", rp_str, ifp->name); + __func__, up->sg_str, null_register ? "NULL " : "", + rp_str, ifp->name); } memset(buffer, 0, 10000); @@ -208,7 +208,7 @@ void pim_register_send(const uint8_t *buf, int buf_size, struct in_addr src, memcpy(b1, (const unsigned char *)buf, buf_size); pim_msg_build_header(buffer, buf_size + PIM_MSG_REGISTER_LEN, - PIM_MSG_TYPE_REGISTER); + PIM_MSG_TYPE_REGISTER, false); ++pinfo->pim_ifstat_reg_send; @@ -217,7 +217,7 @@ void pim_register_send(const uint8_t *buf, int buf_size, struct in_addr src, if (PIM_DEBUG_PIM_TRACE) { zlog_debug( "%s: could not send PIM register message on interface %s", - __PRETTY_FUNCTION__, ifp->name); + __func__, ifp->name); } return; } @@ -232,19 +232,19 @@ void pim_null_register_send(struct pim_upstream *up) pim_ifp = up->rpf.source_nexthop.interface->info; if (!pim_ifp) { - if (PIM_DEBUG_TRACE) + if (PIM_DEBUG_PIM_TRACE) zlog_debug( "%s: Cannot send null-register for %s no valid iif", - __PRETTY_FUNCTION__, up->sg_str); + __func__, up->sg_str); return; } rpg = RP(pim_ifp->pim, up->sg.grp); if (!rpg) { - if (PIM_DEBUG_TRACE) + if (PIM_DEBUG_PIM_TRACE) zlog_debug( "%s: Cannot send null-register for %s no RPF to the RP", - __PRETTY_FUNCTION__, up->sg_str); + __func__, up->sg_str); return; } @@ -260,10 +260,10 @@ void pim_null_register_send(struct pim_upstream *up) src = pim_ifp->primary_address; if (PIM_UPSTREAM_FLAG_TEST_SRC_VXLAN_ORIG(up->flags)) { if (!pim_vxlan_get_register_src(pim_ifp->pim, up, &src)) { - if (PIM_DEBUG_TRACE) + if (PIM_DEBUG_PIM_TRACE) zlog_debug( "%s: Cannot send null-register for %s vxlan-aa PIP unavailable", - __PRETTY_FUNCTION__, up->sg_str); + __func__, up->sg_str); return; } } @@ -283,8 +283,8 @@ void pim_null_register_send(struct pim_upstream *up) * # Note: this may be a spoofing attempt * } * if( I_am_RP(G) AND outer.dst == RP(G) ) { - * sentRegisterStop = FALSE; - * if ( register.borderbit == TRUE ) { + * sentRegisterStop = false; + * if ( register.borderbit == true ) { * if ( PMBR(S,G) == unknown ) { * PMBR(S,G) = outer.src * } else if ( outer.src != PMBR(S,G) ) { @@ -296,10 +296,10 @@ void pim_null_register_send(struct pim_upstream *up) * ( SwitchToSptDesired(S,G) AND * ( inherited_olist(S,G) == NULL ))) { * send Register-Stop(S,G) to outer.src - * sentRegisterStop = TRUE; + * sentRegisterStop = true; * } * if ( SPTbit(S,G) OR SwitchToSptDesired(S,G) ) { - * if ( sentRegisterStop == TRUE ) { + * if ( sentRegisterStop == true ) { * set KeepaliveTimer(S,G) to RP_Keepalive_Period; * } else { * set KeepaliveTimer(S,G) to Keepalive_Period; @@ -324,14 +324,13 @@ int pim_register_recv(struct interface *ifp, struct in_addr dest_addr, struct prefix_sg sg; uint32_t *bits; int i_am_rp = 0; - struct pim_interface *pim_ifp = NULL; - - pim_ifp = ifp->info; + struct pim_interface *pim_ifp = ifp->info; + struct pim_instance *pim = pim_ifp->pim; #define PIM_MSG_REGISTER_BIT_RESERVED_LEN 4 ip_hdr = (struct ip *)(tlv_buf + PIM_MSG_REGISTER_BIT_RESERVED_LEN); - if (!pim_rp_check_is_my_ip_address(pim_ifp->pim, dest_addr)) { + if (!pim_rp_check_is_my_ip_address(pim, dest_addr)) { if (PIM_DEBUG_PIM_REG) { char dest[INET_ADDRSTRLEN]; @@ -375,7 +374,7 @@ int pim_register_recv(struct interface *ifp, struct in_addr dest_addr, sg.src = ip_hdr->ip_src; sg.grp = ip_hdr->ip_dst; - i_am_rp = I_am_RP(pim_ifp->pim, sg.grp); + i_am_rp = I_am_RP(pim, sg.grp); if (PIM_DEBUG_PIM_REG) { char src_str[INET_ADDRSTRLEN]; @@ -387,9 +386,36 @@ int pim_register_recv(struct interface *ifp, struct in_addr dest_addr, if (i_am_rp && (dest_addr.s_addr - == ((RP(pim_ifp->pim, sg.grp))->rpf_addr.u.prefix4.s_addr))) { + == ((RP(pim, sg.grp))->rpf_addr.u.prefix4.s_addr))) { sentRegisterStop = 0; + if (pim->register_plist) { + struct prefix_list *plist; + struct prefix src; + + plist = prefix_list_lookup(AFI_IP, pim->register_plist); + + src.family = AF_INET; + src.prefixlen = IPV4_MAX_PREFIXLEN; + src.u.prefix4 = sg.src; + + if (prefix_list_apply(plist, &src) == PREFIX_DENY) { + pim_register_stop_send(ifp, &sg, dest_addr, + src_addr); + if (PIM_DEBUG_PIM_PACKETS) { + char src_str[INET_ADDRSTRLEN]; + + pim_inet4_dump("", src_addr, + src_str, + sizeof(src_str)); + zlog_debug("%s: Sending register-stop to %s for %pSG4 due to prefix-list denial, dropping packet", + __func__, src_str, &sg); + } + + return 0; + } + } + if (*bits & PIM_REGISTER_BORDER_BIT) { struct in_addr pimbr = pim_br_get_pmbr(&sg); if (PIM_DEBUG_PIM_PACKETS) @@ -411,16 +437,15 @@ int pim_register_recv(struct interface *ifp, struct in_addr dest_addr, } } - struct pim_upstream *upstream = - pim_upstream_find(pim_ifp->pim, &sg); + struct pim_upstream *upstream = pim_upstream_find(pim, &sg); /* * If we don't have a place to send ignore the packet */ if (!upstream) { upstream = pim_upstream_add( - pim_ifp->pim, &sg, ifp, - PIM_UPSTREAM_FLAG_MASK_SRC_STREAM, - __PRETTY_FUNCTION__, NULL); + pim, &sg, ifp, + PIM_UPSTREAM_FLAG_MASK_SRC_STREAM, __func__, + NULL); if (!upstream) { zlog_warn("Failure to create upstream state"); return 1; @@ -452,10 +477,8 @@ int pim_register_recv(struct interface *ifp, struct in_addr dest_addr, } if ((upstream->sptbit == PIM_UPSTREAM_SPTBIT_TRUE) - || ((SwitchToSptDesired(pim_ifp->pim, &sg)) - && pim_upstream_inherited_olist(pim_ifp->pim, upstream) - == 0)) { - // pim_scan_individual_oil (upstream->channel_oil); + || ((SwitchToSptDesiredOnRp(pim, &sg)) + && pim_upstream_inherited_olist(pim, upstream) == 0)) { pim_register_stop_send(ifp, &sg, dest_addr, src_addr); sentRegisterStop = 1; } else { @@ -464,15 +487,13 @@ int pim_register_recv(struct interface *ifp, struct in_addr dest_addr, upstream->sptbit); } if ((upstream->sptbit == PIM_UPSTREAM_SPTBIT_TRUE) - || (SwitchToSptDesired(pim_ifp->pim, &sg))) { + || (SwitchToSptDesiredOnRp(pim, &sg))) { if (sentRegisterStop) { pim_upstream_keep_alive_timer_start( - upstream, - pim_ifp->pim->rp_keep_alive_time); + upstream, pim->rp_keep_alive_time); } else { pim_upstream_keep_alive_timer_start( - upstream, - pim_ifp->pim->keep_alive_time); + upstream, pim->keep_alive_time); } } @@ -499,3 +520,32 @@ int pim_register_recv(struct interface *ifp, struct in_addr dest_addr, return 0; } + +/* + * This routine scan all upstream and update register state and remove pimreg + * when couldreg becomes false. + */ +void pim_reg_del_on_couldreg_fail(struct interface *ifp) +{ + struct pim_interface *pim_ifp = ifp->info; + struct pim_instance *pim; + struct pim_upstream *up; + + if (!pim_ifp) + return; + + pim = pim_ifp->pim; + + frr_each (rb_pim_upstream, &pim->upstream_head, up) { + if (ifp != up->rpf.source_nexthop.interface) + continue; + + if (!pim_upstream_could_register(up) + && (up->reg_state != PIM_REG_NOINFO)) { + pim_channel_del_oif(up->channel_oil, pim->regiface, + PIM_OIF_FLAG_PROTO_PIM, __func__); + THREAD_OFF(up->t_rs_timer); + up->reg_state = PIM_REG_NOINFO; + } + } +} diff --git a/pimd/pim_register.h b/pimd/pim_register.h index c5a28fee41..caaacd9d54 100644 --- a/pimd/pim_register.h +++ b/pimd/pim_register.h @@ -43,5 +43,6 @@ void pim_register_stop_send(struct interface *ifp, struct prefix_sg *sg, struct in_addr src, struct in_addr originator); void pim_register_join(struct pim_upstream *up); void pim_null_register_send(struct pim_upstream *up); +void pim_reg_del_on_couldreg_fail(struct interface *ifp); #endif diff --git a/pimd/pim_routemap.c b/pimd/pim_routemap.c index 4230c127ad..2de94e9031 100644 --- a/pimd/pim_routemap.c +++ b/pimd/pim_routemap.c @@ -36,7 +36,7 @@ static void pim_route_map_delete(const char *rmap_name) route_map_notify_dependencies(rmap_name, RMAP_EVENT_MATCH_DELETED); } -static void pim_route_map_event(route_map_event_t event, const char *rmap_name) +static void pim_route_map_event(const char *rmap_name) { route_map_notify_dependencies(rmap_name, RMAP_EVENT_MATCH_ADDED); } diff --git a/pimd/pim_rp.c b/pimd/pim_rp.c index 14643743ad..93fe787a93 100644 --- a/pimd/pim_rp.c +++ b/pimd/pim_rp.c @@ -48,6 +48,7 @@ #include "pim_mroute.h" #include "pim_oil.h" #include "pim_zebra.h" +#include "pim_bsm.h" /* Cleanup pim->rpf_hash each node data */ void pim_rp_list_hash_clean(void *data) @@ -130,7 +131,7 @@ void pim_rp_init(struct pim_instance *pim) rn = route_node_get(pim->rp_table, &rp_info->group); rn->info = rp_info; - if (PIM_DEBUG_TRACE) + if (PIM_DEBUG_PIM_TRACE) zlog_debug( "Allocated: %p for rp_info: %p(224.0.0.0/4) Lock: %d", rn, rp_info, rn->lock); @@ -140,6 +141,9 @@ void pim_rp_free(struct pim_instance *pim) { if (pim->rp_list) list_delete(&pim->rp_list); + if (pim->rp_table) + route_table_finish(pim->rp_table); + pim->rp_table = NULL; } /* @@ -239,21 +243,22 @@ struct rp_info *pim_rp_find_match_group(struct pim_instance *pim, flog_err( EC_LIB_DEVELOPMENT, "%s: BUG We should have found default group information\n", - __PRETTY_FUNCTION__); + __func__); return best; } rp_info = rn->info; - if (PIM_DEBUG_TRACE) { + if (PIM_DEBUG_PIM_TRACE) { char buf[PREFIX_STRLEN]; - route_unlock_node(rn); zlog_debug("Lookedup: %p for rp_info: %p(%s) Lock: %d", rn, rp_info, prefix2str(&rp_info->group, buf, sizeof(buf)), rn->lock); } + route_unlock_node(rn); + if (!best) return rp_info; @@ -273,6 +278,7 @@ struct rp_info *pim_rp_find_match_group(struct pim_instance *pim, static void pim_rp_refresh_group_to_rp_mapping(struct pim_instance *pim) { pim_msdp_i_am_rp_changed(pim); + pim_upstream_reeval_use_rpt(pim); } void pim_rp_prefix_list_update(struct pim_instance *pim, @@ -347,10 +353,9 @@ void pim_upstream_update(struct pim_instance *pim, struct pim_upstream *up) pim_rp_set_upstream_addr(pim, &new_upstream_addr, up->sg.src, up->sg.grp); - if (PIM_DEBUG_TRACE) + if (PIM_DEBUG_PIM_TRACE) zlog_debug("%s: pim upstream update for old upstream %s", - __PRETTY_FUNCTION__, - inet_ntoa(old_upstream_addr)); + __func__, inet_ntoa(old_upstream_addr)); if (old_upstream_addr.s_addr == new_upstream_addr.s_addr) return; @@ -365,14 +370,15 @@ void pim_upstream_update(struct pim_instance *pim, struct pim_upstream *up) nht_p.family = AF_INET; nht_p.prefixlen = IPV4_MAX_BITLEN; nht_p.u.prefix4 = old_upstream_addr; - if (PIM_DEBUG_TRACE) { + if (PIM_DEBUG_PIM_TRACE) { char buf[PREFIX2STR_BUFFER]; prefix2str(&nht_p, buf, sizeof(buf)); - zlog_debug("%s: Deregister upstream %s addr %s with Zebra NHT", - __PRETTY_FUNCTION__, up->sg_str, buf); + zlog_debug( + "%s: Deregister upstream %s addr %s with Zebra NHT", + __func__, up->sg_str, buf); } - pim_delete_tracked_nexthop(pim, &nht_p, up, NULL); + pim_delete_tracked_nexthop(pim, &nht_p, up, NULL, false); } /* Update the upstream address */ @@ -380,36 +386,61 @@ void pim_upstream_update(struct pim_instance *pim, struct pim_upstream *up) old_rpf.source_nexthop.interface = up->rpf.source_nexthop.interface; - rpf_result = pim_rpf_update(pim, up, &old_rpf, 1); + rpf_result = pim_rpf_update(pim, up, &old_rpf, __func__); if (rpf_result == PIM_RPF_FAILURE) - pim_mroute_del(up->channel_oil, __PRETTY_FUNCTION__); + pim_mroute_del(up->channel_oil, __func__); /* update kernel multicast forwarding cache (MFC) */ - if (up->rpf.source_nexthop.interface && up->channel_oil) { - ifindex_t ifindex = up->rpf.source_nexthop.interface->ifindex; - int vif_index = pim_if_find_vifindex_by_ifindex(pim, ifindex); - /* Pass Current selected NH vif index to mroute download */ - if (vif_index) - pim_scan_individual_oil(up->channel_oil, vif_index); - else { - if (PIM_DEBUG_PIM_NHT) - zlog_debug( - "%s: NHT upstream %s channel_oil IIF %s vif_index is not valid", - __PRETTY_FUNCTION__, up->sg_str, - up->rpf.source_nexthop.interface->name); - } - } + if (up->rpf.source_nexthop.interface && up->channel_oil) + pim_upstream_mroute_iif_update(up->channel_oil, __func__); - if (rpf_result == PIM_RPF_CHANGED) + if (rpf_result == PIM_RPF_CHANGED || + (rpf_result == PIM_RPF_FAILURE && + old_rpf.source_nexthop.interface)) pim_zebra_upstream_rpf_changed(pim, up, &old_rpf); pim_zebra_update_all_interfaces(pim); } -int pim_rp_new(struct pim_instance *pim, const char *rp, - const char *group_range, const char *plist) +int pim_rp_new_config(struct pim_instance *pim, const char *rp, + const char *group_range, const char *plist) +{ + int result = 0; + struct prefix group; + struct in_addr rp_addr; + + if (group_range == NULL) + result = str2prefix("224.0.0.0/4", &group); + else { + result = str2prefix(group_range, &group); + if (result) { + struct prefix temp; + + prefix_copy(&temp, &group); + apply_mask(&temp); + if (!prefix_same(&group, &temp)) + return PIM_GROUP_BAD_ADDR_MASK_COMBO; + } + } + + if (!result) + return PIM_GROUP_BAD_ADDRESS; + + result = inet_pton(AF_INET, rp, &rp_addr); + + if (result <= 0) + return PIM_RP_BAD_ADDRESS; + + result = pim_rp_new(pim, rp_addr, group, plist, RP_SRC_STATIC); + return result; +} + +int pim_rp_new(struct pim_instance *pim, struct in_addr rp_addr, + struct prefix group, const char *plist, + enum rp_source rp_src_flag) { int result = 0; + char rp[INET_ADDRSTRLEN]; struct rp_info *rp_info; struct rp_info *rp_all; struct prefix group_all; @@ -417,41 +448,22 @@ int pim_rp_new(struct pim_instance *pim, const char *rp, struct rp_info *tmp_rp_info; char buffer[BUFSIZ]; struct prefix nht_p; - struct prefix temp; struct route_node *rn; struct pim_upstream *up; - struct listnode *upnode; - rp_info = XCALLOC(MTYPE_PIM_RP, sizeof(*rp_info)); - - if (group_range == NULL) - result = str2prefix("224.0.0.0/4", &rp_info->group); - else { - result = str2prefix(group_range, &rp_info->group); - if (result) { - prefix_copy(&temp, &rp_info->group); - apply_mask(&temp); - if (!prefix_same(&rp_info->group, &temp)) { - XFREE(MTYPE_PIM_RP, rp_info); - return PIM_GROUP_BAD_ADDR_MASK_COMBO; - } - } - } + if (rp_addr.s_addr == INADDR_ANY || + rp_addr.s_addr == INADDR_NONE) + return PIM_RP_BAD_ADDRESS; - if (!result) { - XFREE(MTYPE_PIM_RP, rp_info); - return PIM_GROUP_BAD_ADDRESS; - } + rp_info = XCALLOC(MTYPE_PIM_RP, sizeof(*rp_info)); rp_info->rp.rpf_addr.family = AF_INET; rp_info->rp.rpf_addr.prefixlen = IPV4_MAX_PREFIXLEN; - result = inet_pton(rp_info->rp.rpf_addr.family, rp, - &rp_info->rp.rpf_addr.u.prefix4); + rp_info->rp.rpf_addr.u.prefix4 = rp_addr; + prefix_copy(&rp_info->group, &group); + rp_info->rp_src = rp_src_flag; - if (result <= 0) { - XFREE(MTYPE_PIM_RP, rp_info); - return PIM_RP_BAD_ADDRESS; - } + inet_ntop(AF_INET, &rp_info->rp.rpf_addr.u.prefix4, rp, sizeof(rp)); if (plist) { /* @@ -479,10 +491,10 @@ int pim_rp_new(struct pim_instance *pim, const char *rp, if (rp_info->rp.rpf_addr.u.prefix4.s_addr == tmp_rp_info->rp.rpf_addr.u.prefix4.s_addr) { if (tmp_rp_info->plist) - pim_rp_del(pim, rp, NULL, - tmp_rp_info->plist); + pim_rp_del_config(pim, rp, NULL, + tmp_rp_info->plist); else - pim_rp_del( + pim_rp_del_config( pim, rp, prefix2str(&tmp_rp_info->group, buffer, BUFSIZ), @@ -516,7 +528,8 @@ int pim_rp_new(struct pim_instance *pim, const char *rp, && rp_info->rp.rpf_addr.u.prefix4.s_addr == tmp_rp_info->rp.rpf_addr.u.prefix4 .s_addr) { - pim_rp_del(pim, rp, NULL, tmp_rp_info->plist); + pim_rp_del_config(pim, rp, NULL, + tmp_rp_info->plist); } } @@ -526,6 +539,7 @@ int pim_rp_new(struct pim_instance *pim, const char *rp, if (prefix_same(&rp_all->group, &rp_info->group) && pim_rpf_addr_is_inaddr_none(&rp_all->rp)) { rp_all->rp.rpf_addr = rp_info->rp.rpf_addr; + rp_all->rp_src = rp_src_flag; XFREE(MTYPE_PIM_RP, rp_info); /* Register addr with Zebra NHT */ @@ -540,11 +554,10 @@ int pim_rp_new(struct pim_instance *pim, const char *rp, prefix2str(&rp_all->group, buf1, sizeof(buf1)); zlog_debug( "%s: NHT Register rp_all addr %s grp %s ", - __PRETTY_FUNCTION__, buf, buf1); + __func__, buf, buf1); } - for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, upnode, - up)) { + frr_each (rb_pim_upstream, &pim->upstream_head, up) { /* Find (*, G) upstream whose RP is not * configured yet */ @@ -556,8 +569,8 @@ int pim_rp_new(struct pim_instance *pim, const char *rp, grp.family = AF_INET; grp.prefixlen = IPV4_MAX_BITLEN; grp.u.prefix4 = up->sg.grp; - trp_info = pim_rp_find_match_group(pim, - &grp); + trp_info = pim_rp_find_match_group( + pim, &grp); if (trp_info == rp_all) pim_upstream_update(pim, up); } @@ -565,24 +578,27 @@ int pim_rp_new(struct pim_instance *pim, const char *rp, pim_rp_check_interfaces(pim, rp_all); pim_rp_refresh_group_to_rp_mapping(pim); - pim_find_or_track_nexthop(pim, &nht_p, NULL, rp_all, - NULL); + false, NULL); + if (!pim_ecmp_nexthop_lookup(pim, &rp_all->rp.source_nexthop, &nht_p, &rp_all->group, 1)) return PIM_RP_NO_PATH; - return PIM_SUCCESS; } /* * Return if the group is already configured for this RP */ - if (pim_rp_find_exact(pim, rp_info->rp.rpf_addr.u.prefix4, - &rp_info->group)) { + tmp_rp_info = pim_rp_find_exact( + pim, rp_info->rp.rpf_addr.u.prefix4, &rp_info->group); + if (tmp_rp_info) { + if ((tmp_rp_info->rp_src != rp_src_flag) + && (rp_src_flag == RP_SRC_STATIC)) + tmp_rp_info->rp_src = rp_src_flag; XFREE(MTYPE_PIM_RP, rp_info); - return PIM_SUCCESS; + return result; } /* @@ -604,8 +620,20 @@ int pim_rp_new(struct pim_instance *pim, const char *rp, */ if (prefix_same(&rp_info->group, &tmp_rp_info->group)) { + if ((rp_src_flag == RP_SRC_STATIC) + && (tmp_rp_info->rp_src + == RP_SRC_STATIC)) { + XFREE(MTYPE_PIM_RP, rp_info); + return PIM_GROUP_OVERLAP; + } + + result = pim_rp_change( + pim, + rp_info->rp.rpf_addr.u.prefix4, + tmp_rp_info->group, + rp_src_flag); XFREE(MTYPE_PIM_RP, rp_info); - return PIM_GROUP_OVERLAP; + return result; } } } @@ -615,7 +643,7 @@ int pim_rp_new(struct pim_instance *pim, const char *rp, rn = route_node_get(pim->rp_table, &rp_info->group); rn->info = rp_info; - if (PIM_DEBUG_TRACE) { + if (PIM_DEBUG_PIM_TRACE) { char buf[PREFIX_STRLEN]; zlog_debug("Allocated: %p for rp_info: %p(%s) Lock: %d", rn, @@ -624,7 +652,7 @@ int pim_rp_new(struct pim_instance *pim, const char *rp, rn->lock); } - for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, upnode, up)) { + frr_each (rb_pim_upstream, &pim->upstream_head, up) { if (up->sg.src.s_addr == INADDR_ANY) { struct prefix grp; struct rp_info *trp_info; @@ -652,10 +680,9 @@ int pim_rp_new(struct pim_instance *pim, const char *rp, prefix2str(&nht_p, buf, sizeof(buf)); prefix2str(&rp_info->group, buf1, sizeof(buf1)); zlog_debug("%s: NHT Register RP addr %s grp %s with Zebra ", - __PRETTY_FUNCTION__, buf, buf1); + __func__, buf, buf1); } - - pim_find_or_track_nexthop(pim, &nht_p, NULL, rp_info, NULL); + pim_find_or_track_nexthop(pim, &nht_p, NULL, rp_info, false, NULL); if (!pim_ecmp_nexthop_lookup(pim, &rp_info->rp.source_nexthop, &nht_p, &rp_info->group, 1)) return PIM_RP_NO_PATH; @@ -663,21 +690,12 @@ int pim_rp_new(struct pim_instance *pim, const char *rp, return PIM_SUCCESS; } -int pim_rp_del(struct pim_instance *pim, const char *rp, - const char *group_range, const char *plist) +int pim_rp_del_config(struct pim_instance *pim, const char *rp, + const char *group_range, const char *plist) { struct prefix group; struct in_addr rp_addr; - struct prefix g_all; - struct rp_info *rp_info; - struct rp_info *rp_all; int result; - struct prefix nht_p; - struct route_node *rn; - bool was_plist = false; - struct rp_info *trp_info; - struct pim_upstream *up; - struct listnode *upnode; if (group_range == NULL) result = str2prefix("224.0.0.0/4", &group); @@ -691,6 +709,31 @@ int pim_rp_del(struct pim_instance *pim, const char *rp, if (result <= 0) return PIM_RP_BAD_ADDRESS; + result = pim_rp_del(pim, rp_addr, group, plist, RP_SRC_STATIC); + return result; +} + +int pim_rp_del(struct pim_instance *pim, struct in_addr rp_addr, + struct prefix group, const char *plist, + enum rp_source rp_src_flag) +{ + struct prefix g_all; + struct rp_info *rp_info; + struct rp_info *rp_all; + struct prefix nht_p; + struct route_node *rn; + bool was_plist = false; + struct rp_info *trp_info; + struct pim_upstream *up; + struct bsgrp_node *bsgrp = NULL; + struct bsm_rpinfo *bsrp = NULL; + char grp_str[PREFIX2STR_BUFFER]; + char rp_str[INET_ADDRSTRLEN]; + + if (!inet_ntop(AF_INET, &rp_addr, rp_str, sizeof(rp_str))) + snprintf(rp_str, sizeof(rp_str), ""); + prefix2str(&group, grp_str, sizeof(grp_str)); + if (plist) rp_info = pim_rp_find_prefix_list(pim, rp_addr, plist); else @@ -704,6 +747,44 @@ int pim_rp_del(struct pim_instance *pim, const char *rp, was_plist = true; } + if (PIM_DEBUG_PIM_TRACE) + zlog_debug("%s: Delete RP %s for the group %s", __func__, + rp_str, grp_str); + + /* While static RP is getting deleted, we need to check if dynamic RP + * present for the same group in BSM RP table, then install the dynamic + * RP for the group node into the main rp table + */ + if (rp_src_flag == RP_SRC_STATIC) { + bsgrp = pim_bsm_get_bsgrp_node(&pim->global_scope, &group); + + if (bsgrp) { + bsrp = listnode_head(bsgrp->bsrp_list); + if (bsrp) { + if (PIM_DEBUG_PIM_TRACE) { + char bsrp_str[INET_ADDRSTRLEN]; + + if (!inet_ntop(AF_INET, bsrp, bsrp_str, + sizeof(bsrp_str))) + snprintf(bsrp_str, + sizeof(bsrp_str), + ""); + + zlog_debug( + "%s: BSM RP %s found for the group %s", + __func__, bsrp_str, grp_str); + } + return pim_rp_change(pim, bsrp->rp_address, + group, RP_SRC_BSR); + } + } else { + if (PIM_DEBUG_PIM_TRACE) + zlog_debug( + "%s: BSM RP not found for the group %s", + __func__, grp_str); + } + } + /* Deregister addr with Zebra NHT */ nht_p.family = AF_INET; nht_p.prefixlen = IPV4_MAX_BITLEN; @@ -711,10 +792,10 @@ int pim_rp_del(struct pim_instance *pim, const char *rp, if (PIM_DEBUG_PIM_NHT_RP) { char buf[PREFIX2STR_BUFFER]; prefix2str(&nht_p, buf, sizeof(buf)); - zlog_debug("%s: Deregister RP addr %s with Zebra ", - __PRETTY_FUNCTION__, buf); + zlog_debug("%s: Deregister RP addr %s with Zebra ", __func__, + buf); } - pim_delete_tracked_nexthop(pim, &nht_p, NULL, rp_info); + pim_delete_tracked_nexthop(pim, &nht_p, NULL, rp_info, false); if (!str2prefix("224.0.0.0/4", &g_all)) return PIM_RP_BAD_ADDRESS; @@ -722,12 +803,13 @@ int pim_rp_del(struct pim_instance *pim, const char *rp, rp_all = pim_rp_find_match_group(pim, &g_all); if (rp_all == rp_info) { - for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, upnode, up)) { + frr_each (rb_pim_upstream, &pim->upstream_head, up) { /* Find the upstream (*, G) whose upstream address is * same as the deleted RP */ - if ((up->upstream_addr.s_addr == rp_addr.s_addr) && - (up->sg.src.s_addr == INADDR_ANY)) { + if ((up->upstream_addr.s_addr + == rp_info->rp.rpf_addr.u.prefix4.s_addr) + && (up->sg.src.s_addr == INADDR_ANY)) { struct prefix grp; grp.family = AF_INET; grp.prefixlen = IPV4_MAX_BITLEN; @@ -755,12 +837,12 @@ int pim_rp_del(struct pim_instance *pim, const char *rp, EC_LIB_DEVELOPMENT, "Expected rn->info to be equal to rp_info"); - if (PIM_DEBUG_TRACE) { + if (PIM_DEBUG_PIM_TRACE) { char buf[PREFIX_STRLEN]; zlog_debug( "%s:Found for Freeing: %p for rp_info: %p(%s) Lock: %d", - __PRETTY_FUNCTION__, rn, rp_info, + __func__, rn, rp_info, prefix2str(&rp_info->group, buf, sizeof(buf)), rn->lock); @@ -773,12 +855,13 @@ int pim_rp_del(struct pim_instance *pim, const char *rp, pim_rp_refresh_group_to_rp_mapping(pim); - for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, upnode, up)) { + frr_each (rb_pim_upstream, &pim->upstream_head, up) { /* Find the upstream (*, G) whose upstream address is same as * the deleted RP */ - if ((up->upstream_addr.s_addr == rp_addr.s_addr) && - (up->sg.src.s_addr == INADDR_ANY)) { + if ((up->upstream_addr.s_addr + == rp_info->rp.rpf_addr.u.prefix4.s_addr) + && (up->sg.src.s_addr == INADDR_ANY)) { struct prefix grp; grp.family = AF_INET; @@ -790,9 +873,9 @@ int pim_rp_del(struct pim_instance *pim, const char *rp, /* RP not found for the group grp */ if (pim_rpf_addr_is_inaddr_none(&trp_info->rp)) { pim_upstream_rpf_clear(pim, up); - pim_rp_set_upstream_addr(pim, - &up->upstream_addr, - up->sg.src, up->sg.grp); + pim_rp_set_upstream_addr( + pim, &up->upstream_addr, up->sg.src, + up->sg.grp); } /* RP found for the group grp */ @@ -805,6 +888,105 @@ int pim_rp_del(struct pim_instance *pim, const char *rp, return PIM_SUCCESS; } +int pim_rp_change(struct pim_instance *pim, struct in_addr new_rp_addr, + struct prefix group, enum rp_source rp_src_flag) +{ + struct prefix nht_p; + struct route_node *rn; + int result = 0; + struct rp_info *rp_info = NULL; + struct pim_upstream *up; + + rn = route_node_lookup(pim->rp_table, &group); + if (!rn) { + result = pim_rp_new(pim, new_rp_addr, group, NULL, rp_src_flag); + return result; + } + + rp_info = rn->info; + + if (!rp_info) { + route_unlock_node(rn); + result = pim_rp_new(pim, new_rp_addr, group, NULL, rp_src_flag); + return result; + } + + if (rp_info->rp.rpf_addr.u.prefix4.s_addr == new_rp_addr.s_addr) { + if (rp_info->rp_src != rp_src_flag) { + rp_info->rp_src = rp_src_flag; + route_unlock_node(rn); + return PIM_SUCCESS; + } + } + + nht_p.family = AF_INET; + nht_p.prefixlen = IPV4_MAX_BITLEN; + + /* Deregister old RP addr with Zebra NHT */ + if (rp_info->rp.rpf_addr.u.prefix4.s_addr != INADDR_ANY) { + nht_p.u.prefix4 = rp_info->rp.rpf_addr.u.prefix4; + if (PIM_DEBUG_PIM_NHT_RP) { + char buf[PREFIX2STR_BUFFER]; + + prefix2str(&nht_p, buf, sizeof(buf)); + zlog_debug("%s: Deregister RP addr %s with Zebra ", + __func__, buf); + } + pim_delete_tracked_nexthop(pim, &nht_p, NULL, rp_info, false); + } + + pim_rp_nexthop_del(rp_info); + listnode_delete(pim->rp_list, rp_info); + /* Update the new RP address*/ + rp_info->rp.rpf_addr.u.prefix4 = new_rp_addr; + rp_info->rp_src = rp_src_flag; + rp_info->i_am_rp = 0; + + listnode_add_sort(pim->rp_list, rp_info); + + frr_each (rb_pim_upstream, &pim->upstream_head, up) { + if (up->sg.src.s_addr == INADDR_ANY) { + struct prefix grp; + struct rp_info *trp_info; + + grp.family = AF_INET; + grp.prefixlen = IPV4_MAX_BITLEN; + grp.u.prefix4 = up->sg.grp; + trp_info = pim_rp_find_match_group(pim, &grp); + + if (trp_info == rp_info) + pim_upstream_update(pim, up); + } + } + + /* Register new RP addr with Zebra NHT */ + nht_p.u.prefix4 = rp_info->rp.rpf_addr.u.prefix4; + if (PIM_DEBUG_PIM_NHT_RP) { + char buf[PREFIX2STR_BUFFER]; + char buf1[PREFIX2STR_BUFFER]; + + prefix2str(&nht_p, buf, sizeof(buf)); + prefix2str(&rp_info->group, buf1, sizeof(buf1)); + zlog_debug("%s: NHT Register RP addr %s grp %s with Zebra ", + __func__, buf, buf1); + } + + pim_find_or_track_nexthop(pim, &nht_p, NULL, rp_info, false, NULL); + if (!pim_ecmp_nexthop_lookup(pim, &rp_info->rp.source_nexthop, &nht_p, + &rp_info->group, 1)) { + route_unlock_node(rn); + return PIM_RP_NO_PATH; + } + + pim_rp_check_interfaces(pim, rp_info); + + route_unlock_node(rn); + + pim_rp_refresh_group_to_rp_mapping(pim); + + return result; +} + void pim_rp_setup(struct pim_instance *pim) { struct listnode *node; @@ -819,7 +1001,8 @@ void pim_rp_setup(struct pim_instance *pim) nht_p.prefixlen = IPV4_MAX_BITLEN; nht_p.u.prefix4 = rp_info->rp.rpf_addr.u.prefix4; - pim_find_or_track_nexthop(pim, &nht_p, NULL, rp_info, NULL); + pim_find_or_track_nexthop(pim, &nht_p, NULL, rp_info, false, + NULL); if (!pim_ecmp_nexthop_lookup(pim, &rp_info->rp.source_nexthop, &nht_p, &rp_info->group, 1)) if (PIM_DEBUG_PIM_NHT_RP) @@ -867,6 +1050,7 @@ void pim_rp_check_on_if_add(struct pim_interface *pim_ifp) if (i_am_rp_changed) { pim_msdp_i_am_rp_changed(pim); + pim_upstream_reeval_use_rpt(pim); } } @@ -909,6 +1093,7 @@ void pim_i_am_rp_re_evaluate(struct pim_instance *pim) if (i_am_rp_changed) { pim_msdp_i_am_rp_changed(pim); + pim_upstream_reeval_use_rpt(pim); } } @@ -967,10 +1152,10 @@ struct pim_rpf *pim_rp_g(struct pim_instance *pim, struct in_addr group) prefix2str(&rp_info->group, buf1, sizeof(buf1)); zlog_debug( "%s: NHT Register RP addr %s grp %s with Zebra", - __PRETTY_FUNCTION__, buf, buf1); + __func__, buf, buf1); } - - pim_find_or_track_nexthop(pim, &nht_p, NULL, rp_info, NULL); + pim_find_or_track_nexthop(pim, &nht_p, NULL, rp_info, false, + NULL); pim_rpf_set_refresh_time(pim); (void)pim_ecmp_nexthop_lookup(pim, &rp_info->rp.source_nexthop, &nht_p, &rp_info->group, 1); @@ -1002,11 +1187,11 @@ int pim_rp_set_upstream_addr(struct pim_instance *pim, struct in_addr *up, rp_info = pim_rp_find_match_group(pim, &g); - if ((pim_rpf_addr_is_inaddr_none(&rp_info->rp)) - && (source.s_addr == INADDR_ANY)) { + if (!rp_info || ((pim_rpf_addr_is_inaddr_none(&rp_info->rp)) + && (source.s_addr == INADDR_ANY))) { if (PIM_DEBUG_PIM_NHT_RP) zlog_debug("%s: Received a (*,G) with no RP configured", - __PRETTY_FUNCTION__); + __func__); up->s_addr = INADDR_ANY; return 0; } @@ -1030,6 +1215,9 @@ int pim_rp_config_write(struct pim_instance *pim, struct vty *vty, if (pim_rpf_addr_is_inaddr_none(&rp_info->rp)) continue; + if (rp_info->rp_src == RP_SRC_BSR) + continue; + if (rp_info->plist) vty_out(vty, "%sip pim rp %s prefix-list %s\n", spaces, inet_ntop(AF_INET, @@ -1062,6 +1250,7 @@ void pim_rp_show_information(struct pim_instance *pim, struct vty *vty, bool uj) struct rp_info *rp_info; struct rp_info *prev_rp_info = NULL; struct listnode *node; + char source[7]; json_object *json = NULL; json_object *json_rp_rows = NULL; @@ -1071,12 +1260,17 @@ void pim_rp_show_information(struct pim_instance *pim, struct vty *vty, bool uj) json = json_object_new_object(); else vty_out(vty, - "RP address group/prefix-list OIF I am RP\n"); - + "RP address group/prefix-list OIF I am RP Source\n"); for (ALL_LIST_ELEMENTS_RO(pim->rp_list, node, rp_info)) { if (!pim_rpf_addr_is_inaddr_none(&rp_info->rp)) { char buf[48]; + if (rp_info->rp_src == RP_SRC_STATIC) + strlcpy(source, "Static", sizeof(source)); + else if (rp_info->rp_src == RP_SRC_BSR) + strlcpy(source, "BSR", sizeof(source)); + else + strlcpy(source, "None", sizeof(source)); if (uj) { /* * If we have moved on to a new RP then add the @@ -1100,15 +1294,25 @@ void pim_rp_show_information(struct pim_instance *pim, struct vty *vty, bool uj) json_rp_rows = json_object_new_array(); json_row = json_object_new_object(); + json_object_string_add( + json_row, "rpAddress", + inet_ntoa(rp_info->rp.rpf_addr.u + .prefix4)); if (rp_info->rp.source_nexthop.interface) json_object_string_add( json_row, "outboundInterface", rp_info->rp.source_nexthop .interface->name); - + else + json_object_string_add( + json_row, "outboundInterface", + "Unknown"); if (rp_info->i_am_rp) json_object_boolean_true_add(json_row, "iAmRP"); + else + json_object_boolean_false_add(json_row, + "iAmRP"); if (rp_info->plist) json_object_string_add(json_row, @@ -1119,6 +1323,8 @@ void pim_rp_show_information(struct pim_instance *pim, struct vty *vty, bool uj) json_row, "group", prefix2str(&rp_info->group, buf, 48)); + json_object_string_add(json_row, "source", + source); json_object_array_add(json_rp_rows, json_row); } else { @@ -1141,11 +1347,12 @@ void pim_rp_show_information(struct pim_instance *pim, struct vty *vty, bool uj) vty_out(vty, "%-16s ", "(Unknown)"); if (rp_info->i_am_rp) - vty_out(vty, "yes\n"); + vty_out(vty, "yes"); else - vty_out(vty, "no\n"); - } + vty_out(vty, "no"); + vty_out(vty, "%14s\n", source); + } prev_rp_info = rp_info; } } @@ -1180,7 +1387,7 @@ void pim_resolve_rp_nh(struct pim_instance *pim, struct pim_neighbor *nbr) nht_p.u.prefix4 = rp_info->rp.rpf_addr.u.prefix4; memset(&pnc, 0, sizeof(struct pim_nexthop_cache)); if (!pim_find_or_track_nexthop(pim, &nht_p, NULL, rp_info, - &pnc)) + false, &pnc)) continue; for (nh_node = pnc.nexthop; nh_node; nh_node = nh_node->next) { @@ -1203,8 +1410,7 @@ void pim_resolve_rp_nh(struct pim_instance *pim, struct pim_neighbor *nbr) sizeof(str)); zlog_debug( "%s: addr %s new nexthop addr %s interface %s", - __PRETTY_FUNCTION__, str, str1, - ifp1->name); + __func__, str, str1, ifp1->name); } } } diff --git a/pimd/pim_rp.h b/pimd/pim_rp.h index 402ec30aba..6dc26c07a9 100644 --- a/pimd/pim_rp.h +++ b/pimd/pim_rp.h @@ -27,9 +27,16 @@ #include "pim_iface.h" #include "pim_rpf.h" +enum rp_source { + RP_SRC_NONE = 0, + RP_SRC_STATIC, + RP_SRC_BSR +}; + struct rp_info { struct prefix group; struct pim_rpf rp; + enum rp_source rp_src; int i_am_rp; char *plist; }; @@ -39,10 +46,18 @@ void pim_rp_free(struct pim_instance *pim); void pim_rp_list_hash_clean(void *data); -int pim_rp_new(struct pim_instance *pim, const char *rp, const char *group, - const char *plist); -int pim_rp_del(struct pim_instance *pim, const char *rp, const char *group, - const char *plist); +int pim_rp_new_config(struct pim_instance *pim, const char *rp, + const char *group, const char *plist); +int pim_rp_new(struct pim_instance *pim, struct in_addr rp_addr, + struct prefix group, const char *plist, + enum rp_source rp_src_flag); +int pim_rp_del_config(struct pim_instance *pim, const char *rp, + const char *group, const char *plist); +int pim_rp_del(struct pim_instance *pim, struct in_addr rp_addr, + struct prefix group, const char *plist, + enum rp_source rp_src_flag); +int pim_rp_change(struct pim_instance *pim, struct in_addr new_rp_addr, + struct prefix group, enum rp_source rp_src_flag); void pim_rp_prefix_list_update(struct pim_instance *pim, struct prefix_list *plist); diff --git a/pimd/pim_rpf.c b/pimd/pim_rpf.c index afe3886aa5..043ccdb848 100644 --- a/pimd/pim_rpf.c +++ b/pimd/pim_rpf.c @@ -36,15 +36,16 @@ #include "pim_time.h" #include "pim_nht.h" #include "pim_oil.h" +#include "pim_mlag.h" static struct in_addr pim_rpf_find_rpf_addr(struct pim_upstream *up); void pim_rpf_set_refresh_time(struct pim_instance *pim) { pim->last_route_change_time = pim_time_monotonic_usec(); - if (PIM_DEBUG_TRACE) + if (PIM_DEBUG_PIM_TRACE) zlog_debug("%s: vrf(%s) New last route change time: %" PRId64, - __PRETTY_FUNCTION__, pim->vrf->name, + __func__, pim->vrf->name, pim->last_route_change_time); } @@ -69,7 +70,7 @@ bool pim_nexthop_lookup(struct pim_instance *pim, struct pim_nexthop *nexthop, if ((nexthop->last_lookup.s_addr == addr.s_addr) && (nexthop->last_lookup_time > pim->last_route_change_time)) { - if (PIM_DEBUG_TRACE) { + if (PIM_DEBUG_PIM_NHT) { char addr_str[INET_ADDRSTRLEN]; pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); @@ -77,22 +78,20 @@ bool pim_nexthop_lookup(struct pim_instance *pim, struct pim_nexthop *nexthop, pim_addr_dump("", &nexthop->mrib_nexthop_addr, nexthop_str, sizeof(nexthop_str)); zlog_debug( - "%s: Using last lookup for %s at %lld, %" PRId64 " addr %s", - __PRETTY_FUNCTION__, addr_str, - nexthop->last_lookup_time, + "%s: Using last lookup for %s at %lld, %" PRId64" addr %s", + __func__, addr_str, nexthop->last_lookup_time, pim->last_route_change_time, nexthop_str); } pim->nexthop_lookups_avoided++; return true; } else { - if (PIM_DEBUG_TRACE) { + if (PIM_DEBUG_PIM_NHT) { char addr_str[INET_ADDRSTRLEN]; pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); zlog_debug( "%s: Looking up: %s, last lookup time: %lld, %" PRId64, - __PRETTY_FUNCTION__, addr_str, - nexthop->last_lookup_time, + __func__, addr_str, nexthop->last_lookup_time, pim->last_route_change_time); } } @@ -106,7 +105,7 @@ bool pim_nexthop_lookup(struct pim_instance *pim, struct pim_nexthop *nexthop, pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); zlog_warn( "%s %s: could not find nexthop ifindex for address %s", - __FILE__, __PRETTY_FUNCTION__, addr_str); + __FILE__, __func__, addr_str); return false; } @@ -121,8 +120,8 @@ bool pim_nexthop_lookup(struct pim_instance *pim, struct pim_nexthop *nexthop, sizeof(addr_str)); zlog_debug( "%s %s: could not find interface for ifindex %d (address %s)", - __FILE__, __PRETTY_FUNCTION__, - first_ifindex, addr_str); + __FILE__, __func__, first_ifindex, + addr_str); } i++; continue; @@ -135,8 +134,8 @@ bool pim_nexthop_lookup(struct pim_instance *pim, struct pim_nexthop *nexthop, sizeof(addr_str)); zlog_debug( "%s: multicast not enabled on input interface %s (ifindex=%d, RPF for source %s)", - __PRETTY_FUNCTION__, ifp->name, - first_ifindex, addr_str); + __func__, ifp->name, first_ifindex, + addr_str); } i++; } else if (neighbor_needed @@ -165,8 +164,8 @@ bool pim_nexthop_lookup(struct pim_instance *pim, struct pim_nexthop *nexthop, sizeof(addr_str)); zlog_debug( "%s %s: found nexthop %s for address %s: interface %s ifindex=%d metric=%d pref=%d", - __FILE__, __PRETTY_FUNCTION__, nexthop_str, - addr_str, ifp->name, first_ifindex, + __FILE__, __func__, nexthop_str, addr_str, + ifp->name, first_ifindex, nexthop_tab[i].route_metric, nexthop_tab[i].protocol_distance); } @@ -194,36 +193,60 @@ static int nexthop_mismatch(const struct pim_nexthop *nh1, || (nh1->mrib_route_metric != nh2->mrib_route_metric); } +static void pim_rpf_cost_change(struct pim_instance *pim, + struct pim_upstream *up, uint32_t old_cost) +{ + struct pim_rpf *rpf = &up->rpf; + uint32_t new_cost; + + new_cost = pim_up_mlag_local_cost(up); + if (PIM_DEBUG_MLAG) + zlog_debug( + "%s: Cost_to_rp of upstream-%s changed to:%u, from:%u", + __func__, up->sg_str, new_cost, old_cost); + + if (old_cost == new_cost) + return; + + /* Cost changed, it might Impact MLAG DF election, update */ + if (PIM_DEBUG_MLAG) + zlog_debug( + "%s: Cost_to_rp of upstream-%s changed to:%u", + __func__, up->sg_str, + rpf->source_nexthop.mrib_route_metric); + + if (pim_up_mlag_is_local(up)) + pim_mlag_up_local_add(pim, up); +} + enum pim_rpf_result pim_rpf_update(struct pim_instance *pim, - struct pim_upstream *up, struct pim_rpf *old, - uint8_t is_new) + struct pim_upstream *up, struct pim_rpf *old, + const char *caller) { struct pim_rpf *rpf = &up->rpf; struct pim_rpf saved; struct prefix nht_p; struct prefix src, grp; bool neigh_needed = true; + uint32_t saved_mrib_route_metric; if (PIM_UPSTREAM_FLAG_TEST_STATIC_IIF(up->flags)) return PIM_RPF_OK; if (up->upstream_addr.s_addr == INADDR_ANY) { - zlog_debug("%s: RP is not configured yet for %s", - __PRETTY_FUNCTION__, up->sg_str); + zlog_debug("%s(%s): RP is not configured yet for %s", + __func__, caller, up->sg_str); return PIM_RPF_OK; } saved.source_nexthop = rpf->source_nexthop; saved.rpf_addr = rpf->rpf_addr; - - if (is_new && PIM_DEBUG_ZEBRA) { - char source_str[INET_ADDRSTRLEN]; - pim_inet4_dump("", up->upstream_addr, source_str, - sizeof(source_str)); - zlog_debug("%s: NHT Register upstream %s addr %s with Zebra.", - __PRETTY_FUNCTION__, up->sg_str, source_str); + saved_mrib_route_metric = pim_up_mlag_local_cost(up); + if (old) { + old->source_nexthop = saved.source_nexthop; + old->rpf_addr = saved.rpf_addr; } - /* Register addr with Zebra NHT */ + nht_p.family = AF_INET; nht_p.prefixlen = IPV4_MAX_BITLEN; nht_p.u.prefix4.s_addr = up->upstream_addr.s_addr; @@ -237,18 +260,22 @@ enum pim_rpf_result pim_rpf_update(struct pim_instance *pim, if ((up->sg.src.s_addr == INADDR_ANY && I_am_RP(pim, up->sg.grp)) || PIM_UPSTREAM_FLAG_TEST_FHR(up->flags)) - neigh_needed = FALSE; - pim_find_or_track_nexthop(pim, &nht_p, up, NULL, NULL); + neigh_needed = false; + pim_find_or_track_nexthop(pim, &nht_p, up, NULL, false, NULL); if (!pim_ecmp_nexthop_lookup(pim, &rpf->source_nexthop, &src, &grp, - neigh_needed)) + neigh_needed)) { + /* Route is Deleted in Zebra, reset the stored NH data */ + pim_upstream_rpf_clear(pim, up); + pim_rpf_cost_change(pim, up, saved_mrib_route_metric); return PIM_RPF_FAILURE; + } rpf->rpf_addr.family = AF_INET; rpf->rpf_addr.u.prefix4 = pim_rpf_find_rpf_addr(up); if (pim_rpf_addr_is_inaddr_any(rpf) && PIM_DEBUG_ZEBRA) { /* RPF'(S,G) not found */ - zlog_debug("%s %s: RPF'%s not found: won't send join upstream", - __FILE__, __PRETTY_FUNCTION__, up->sg_str); + zlog_debug("%s(%s): RPF'%s not found: won't send join upstream", + __func__, caller, up->sg_str); /* warning only */ } @@ -260,8 +287,8 @@ enum pim_rpf_result pim_rpf_update(struct pim_instance *pim, pim_addr_dump("", &rpf->source_nexthop.mrib_nexthop_addr, nhaddr_str, sizeof(nhaddr_str)); - zlog_debug("%s %s: (S,G)=%s source nexthop now is: interface=%s address=%s pref=%d metric=%d", - __FILE__, __PRETTY_FUNCTION__, + zlog_debug("%s(%s): (S,G)=%s source nexthop now is: interface=%s address=%s pref=%d metric=%d", + __func__, caller, up->sg_str, rpf->source_nexthop.interface ? rpf->source_nexthop.interface->name : "", nhaddr_str, @@ -278,8 +305,8 @@ enum pim_rpf_result pim_rpf_update(struct pim_instance *pim, if (saved.source_nexthop.interface != rpf->source_nexthop.interface) { if (PIM_DEBUG_ZEBRA) { - zlog_debug("%s %s: (S,G)=%s RPF_interface(S) changed from %s to %s", - __FILE__, __PRETTY_FUNCTION__, + zlog_debug("%s(%s): (S,G)=%s RPF_interface(S) changed from %s to %s", + __func__, caller, up->sg_str, saved.source_nexthop.interface ? saved.source_nexthop.interface->name : "", rpf->source_nexthop.interface ? rpf->source_nexthop.interface->name : ""); @@ -294,15 +321,18 @@ enum pim_rpf_result pim_rpf_update(struct pim_instance *pim, if (saved.rpf_addr.u.prefix4.s_addr != rpf->rpf_addr.u.prefix4.s_addr || saved.source_nexthop .interface != rpf->source_nexthop.interface) { - - /* return old rpf to caller ? */ - if (old) { - old->source_nexthop = saved.source_nexthop; - old->rpf_addr = saved.rpf_addr; - } + pim_rpf_cost_change(pim, up, saved_mrib_route_metric); return PIM_RPF_CHANGED; } + if (PIM_DEBUG_MLAG) + zlog_debug( + "%s(%s): Cost_to_rp of upstream-%s changed to:%u", + __func__, caller, up->sg_str, + rpf->source_nexthop.mrib_route_metric); + + pim_rpf_cost_change(pim, up, saved_mrib_route_metric); + return PIM_RPF_OK; } @@ -316,11 +346,6 @@ void pim_upstream_rpf_clear(struct pim_instance *pim, struct pim_upstream *up) { if (up->rpf.source_nexthop.interface) { - if (up->channel_oil) { - up->channel_oil->oil.mfcc_parent = MAXVIFS; - pim_mroute_del(up->channel_oil, __PRETTY_FUNCTION__); - - } pim_upstream_switch(pim, up, PIM_UPSTREAM_NOTJOINED); up->rpf.source_nexthop.interface = NULL; up->rpf.source_nexthop.mrib_nexthop_addr.u.prefix4.s_addr = @@ -330,6 +355,7 @@ void pim_upstream_rpf_clear(struct pim_instance *pim, up->rpf.source_nexthop.mrib_route_metric = router->infinite_assert_metric.route_metric; up->rpf.rpf_addr.u.prefix4.s_addr = PIM_NET_INADDR_ANY; + pim_upstream_mroute_iif_update(up->channel_oil, __func__); } } @@ -356,7 +382,7 @@ static struct in_addr pim_rpf_find_rpf_addr(struct pim_upstream *up) if (!up->rpf.source_nexthop.interface) { zlog_warn("%s: missing RPF interface for upstream (S,G)=%s", - __PRETTY_FUNCTION__, up->sg_str); + __func__, up->sg_str); rpf_addr.s_addr = PIM_NET_INADDR_ANY; return rpf_addr; @@ -387,14 +413,11 @@ int pim_rpf_addr_is_inaddr_none(struct pim_rpf *rpf) switch (rpf->rpf_addr.family) { case AF_INET: return rpf->rpf_addr.u.prefix4.s_addr == INADDR_NONE; - break; case AF_INET6: - zlog_warn("%s: v6 Unimplmeneted", __PRETTY_FUNCTION__); + zlog_warn("%s: v6 Unimplmeneted", __func__); return 1; - break; default: return 0; - break; } return 0; @@ -405,14 +428,11 @@ int pim_rpf_addr_is_inaddr_any(struct pim_rpf *rpf) switch (rpf->rpf_addr.family) { case AF_INET: return rpf->rpf_addr.u.prefix4.s_addr == INADDR_ANY; - break; case AF_INET6: - zlog_warn("%s: v6 Unimplmented", __PRETTY_FUNCTION__); + zlog_warn("%s: v6 Unimplmented", __func__); return 1; - break; default: return 0; - break; } return 0; @@ -426,9 +446,9 @@ int pim_rpf_is_same(struct pim_rpf *rpf1, struct pim_rpf *rpf2) return 0; } -unsigned int pim_rpf_hash_key(void *arg) +unsigned int pim_rpf_hash_key(const void *arg) { - struct pim_nexthop_cache *r = (struct pim_nexthop_cache *)arg; + const struct pim_nexthop_cache *r = arg; return jhash_1word(r->rpf.rpf_addr.u.prefix4.s_addr, 0); } diff --git a/pimd/pim_rpf.h b/pimd/pim_rpf.h index 57bb22674f..f006519b71 100644 --- a/pimd/pim_rpf.h +++ b/pimd/pim_rpf.h @@ -56,14 +56,14 @@ enum pim_rpf_result { PIM_RPF_OK = 0, PIM_RPF_CHANGED, PIM_RPF_FAILURE }; struct pim_upstream; -unsigned int pim_rpf_hash_key(void *arg); +unsigned int pim_rpf_hash_key(const void *arg); bool pim_rpf_equal(const void *arg1, const void *arg2); bool pim_nexthop_lookup(struct pim_instance *pim, struct pim_nexthop *nexthop, struct in_addr addr, int neighbor_needed); enum pim_rpf_result pim_rpf_update(struct pim_instance *pim, - struct pim_upstream *up, struct pim_rpf *old, - uint8_t is_new); + struct pim_upstream *up, + struct pim_rpf *old, const char *caller); void pim_upstream_rpf_clear(struct pim_instance *pim, struct pim_upstream *up); int pim_rpf_addr_is_inaddr_none(struct pim_rpf *rpf); diff --git a/pimd/pim_sock.c b/pimd/pim_sock.c index c4538a4ac5..504519c8a4 100644 --- a/pimd/pim_sock.c +++ b/pimd/pim_sock.c @@ -46,7 +46,7 @@ int pim_socket_raw(int protocol) { int fd; - frr_elevate_privs(&pimd_privs) { + frr_with_privs(&pimd_privs) { fd = socket(AF_INET, SOCK_RAW, protocol); @@ -65,12 +65,11 @@ void pim_socket_ip_hdr(int fd) { const int on = 1; - frr_elevate_privs(&pimd_privs) { + frr_with_privs(&pimd_privs) { if (setsockopt(fd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on))) zlog_err("%s: Could not turn on IP_HDRINCL option: %s", - __PRETTY_FUNCTION__, safe_strerror(errno)); - + __func__, safe_strerror(errno)); } } @@ -83,7 +82,7 @@ int pim_socket_bind(int fd, struct interface *ifp) int ret = 0; #ifdef SO_BINDTODEVICE - frr_elevate_privs(&pimd_privs) { + frr_with_privs(&pimd_privs) { ret = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifp->name, strlen(ifp->name)); @@ -153,7 +152,7 @@ int pim_socket_mcast(int protocol, struct in_addr ifaddr, struct interface *ifp, flog_err( EC_LIB_DEVELOPMENT, "%s %s: Missing IP_PKTINFO and IP_RECVDSTADDR: unable to get dst addr from recvmsg()", - __FILE__, __PRETTY_FUNCTION__); + __FILE__, __func__); close(fd); return PIM_SOCK_ERR_DSTADDR; #endif @@ -231,8 +230,8 @@ int pim_socket_mcast(int protocol, struct in_addr ifaddr, struct interface *ifp, } if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf))) - zlog_warn("%s: Failure to set buffer size to %d", - __PRETTY_FUNCTION__, rcvbuf); + zlog_warn("%s: Failure to set buffer size to %d", __func__, + rcvbuf); { long flags; @@ -255,6 +254,12 @@ int pim_socket_mcast(int protocol, struct in_addr ifaddr, struct interface *ifp, } } + /* Set Tx socket DSCP byte */ + if (setsockopt_ipv4_tos(fd, IPTOS_PREC_INTERNETCONTROL)) { + zlog_warn("can't set sockopt IP_TOS to PIM/IGMP socket %d: %s", + fd, safe_strerror(errno)); + } + return fd; } @@ -283,10 +288,10 @@ int pim_socket_join(int fd, struct in_addr group, struct in_addr ifaddr, char group_str[INET_ADDRSTRLEN]; char ifaddr_str[INET_ADDRSTRLEN]; if (!inet_ntop(AF_INET, &group, group_str, sizeof(group_str))) - sprintf(group_str, ""); + snprintf(group_str, sizeof(group_str), ""); if (!inet_ntop(AF_INET, &ifaddr, ifaddr_str, sizeof(ifaddr_str))) - sprintf(ifaddr_str, ""); + snprintf(ifaddr_str, sizeof(ifaddr_str), ""); flog_err( EC_LIB_SOCKET, @@ -299,10 +304,10 @@ int pim_socket_join(int fd, struct in_addr group, struct in_addr ifaddr, char group_str[INET_ADDRSTRLEN]; char ifaddr_str[INET_ADDRSTRLEN]; if (!inet_ntop(AF_INET, &group, group_str, sizeof(group_str))) - sprintf(group_str, ""); + snprintf(group_str, sizeof(group_str), ""); if (!inet_ntop(AF_INET, &ifaddr, ifaddr_str, sizeof(ifaddr_str))) - sprintf(ifaddr_str, ""); + snprintf(ifaddr_str, sizeof(ifaddr_str), ""); zlog_debug( "Socket fd=%d joined group %s on interface address %s", @@ -370,8 +375,7 @@ int pim_socket_recvfromto(int fd, uint8_t *buf, size_t len, struct in_pktinfo *i = (struct in_pktinfo *)CMSG_DATA(cmsg); if (to) - ((struct sockaddr_in *)to)->sin_addr = - i->ipi_addr; + to->sin_addr = i->ipi_addr; if (tolen) *tolen = sizeof(struct sockaddr_in); if (ifindex) @@ -386,7 +390,7 @@ int pim_socket_recvfromto(int fd, uint8_t *buf, size_t len, && (cmsg->cmsg_type == IP_RECVDSTADDR)) { struct in_addr *i = (struct in_addr *)CMSG_DATA(cmsg); if (to) - ((struct sockaddr_in *)to)->sin_addr = *i; + to->sin_addr = *i; if (tolen) *tolen = sizeof(struct sockaddr_in); diff --git a/pimd/pim_ssm.c b/pimd/pim_ssm.c index 6a70a73b45..8d3e04f5da 100644 --- a/pimd/pim_ssm.c +++ b/pimd/pim_ssm.c @@ -75,7 +75,7 @@ static int pim_is_grp_standard_ssm(struct prefix *group) if (!str2prefix(PIM_SSM_STANDARD_RANGE, &group_ssm)) flog_err(EC_LIB_DEVELOPMENT, "%s: Failure to Read Group Address: %s", - __PRETTY_FUNCTION__, PIM_SSM_STANDARD_RANGE); + __func__, PIM_SSM_STANDARD_RANGE); first = 0; } diff --git a/pimd/pim_ssmpingd.c b/pimd/pim_ssmpingd.c index 17bc375c12..f4d3547b3f 100644 --- a/pimd/pim_ssmpingd.c +++ b/pimd/pim_ssmpingd.c @@ -85,7 +85,7 @@ static int ssmpingd_socket(struct in_addr addr, int port, int mttl) if (fd < 0) { flog_err_sys(EC_LIB_SOCKET, "%s: could not create socket: errno=%d: %s", - __PRETTY_FUNCTION__, errno, safe_strerror(errno)); + __func__, errno, safe_strerror(errno)); return -1; } @@ -98,8 +98,8 @@ static int ssmpingd_socket(struct in_addr addr, int port, int mttl) pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); zlog_warn( "%s: bind(fd=%d,addr=%s,port=%d,len=%zu) failure: errno=%d: %s", - __PRETTY_FUNCTION__, fd, addr_str, port, - sizeof(sockaddr), errno, safe_strerror(errno)); + __func__, fd, addr_str, port, sizeof(sockaddr), errno, + safe_strerror(errno)); close(fd); return -1; } @@ -112,8 +112,7 @@ static int ssmpingd_socket(struct in_addr addr, int port, int mttl) if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &opt, sizeof(opt))) { zlog_warn( "%s: could not set IP_PKTINFO on socket fd=%d: errno=%d: %s", - __PRETTY_FUNCTION__, fd, errno, - safe_strerror(errno)); + __func__, fd, errno, safe_strerror(errno)); } #elif defined(HAVE_IP_RECVDSTADDR) /* BSD IP_RECVDSTADDR */ @@ -122,14 +121,13 @@ static int ssmpingd_socket(struct in_addr addr, int port, int mttl) sizeof(opt))) { zlog_warn( "%s: could not set IP_RECVDSTADDR on socket fd=%d: errno=%d: %s", - __PRETTY_FUNCTION__, fd, errno, - safe_strerror(errno)); + __func__, fd, errno, safe_strerror(errno)); } #else flog_err( EC_LIB_DEVELOPMENT, "%s %s: missing IP_PKTINFO and IP_RECVDSTADDR: unable to get dst addr from recvmsg()", - __FILE__, __PRETTY_FUNCTION__); + __FILE__, __func__); close(fd); return -1; #endif @@ -141,8 +139,7 @@ static int ssmpingd_socket(struct in_addr addr, int port, int mttl) sizeof(reuse))) { zlog_warn( "%s: could not set Reuse Address Option on socket fd=%d: errno=%d: %s", - __PRETTY_FUNCTION__, fd, errno, - safe_strerror(errno)); + __func__, fd, errno, safe_strerror(errno)); close(fd); return -1; } @@ -152,8 +149,7 @@ static int ssmpingd_socket(struct in_addr addr, int port, int mttl) sizeof(mttl))) { zlog_warn( "%s: could not set multicast TTL=%d on socket fd=%d: errno=%d: %s", - __PRETTY_FUNCTION__, mttl, fd, errno, - safe_strerror(errno)); + __func__, mttl, fd, errno, safe_strerror(errno)); close(fd); return -1; } @@ -161,7 +157,7 @@ static int ssmpingd_socket(struct in_addr addr, int port, int mttl) if (setsockopt_ipv4_multicast_loop(fd, 0)) { zlog_warn( "%s: could not disable Multicast Loopback Option on socket fd=%d: errno=%d: %s", - __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno)); + __func__, fd, errno, safe_strerror(errno)); close(fd); return PIM_SOCK_ERR_LOOP; } @@ -170,7 +166,7 @@ static int ssmpingd_socket(struct in_addr addr, int port, int mttl) sizeof(addr))) { zlog_warn( "%s: could not set Outgoing Interface Option on socket fd=%d: errno=%d: %s", - __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno)); + __func__, fd, errno, safe_strerror(errno)); close(fd); return -1; } @@ -182,8 +178,7 @@ static int ssmpingd_socket(struct in_addr addr, int port, int mttl) if (flags < 0) { zlog_warn( "%s: could not get fcntl(F_GETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s", - __PRETTY_FUNCTION__, fd, errno, - safe_strerror(errno)); + __func__, fd, errno, safe_strerror(errno)); close(fd); return -1; } @@ -191,8 +186,7 @@ static int ssmpingd_socket(struct in_addr addr, int port, int mttl) if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) { zlog_warn( "%s: could not set fcntl(F_SETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s", - __PRETTY_FUNCTION__, fd, errno, - safe_strerror(errno)); + __func__, fd, errno, safe_strerror(errno)); close(fd); return -1; } @@ -213,7 +207,7 @@ static void ssmpingd_delete(struct ssmpingd_sock *ss) sizeof(source_str)); zlog_warn( "%s: failure closing ssmpingd sock_fd=%d for source %s: errno=%d: %s", - __PRETTY_FUNCTION__, ss->sock_fd, source_str, errno, + __func__, ss->sock_fd, source_str, errno, safe_strerror(errno)); /* warning only */ } @@ -236,12 +230,12 @@ static void ssmpingd_sendto(struct ssmpingd_sock *ss, const uint8_t *buf, if (sent < 0) { zlog_warn( "%s: sendto() failure to %s,%d: fd=%d len=%d: errno=%d: %s", - __PRETTY_FUNCTION__, to_str, ntohs(to.sin_port), + __func__, to_str, ntohs(to.sin_port), ss->sock_fd, len, errno, safe_strerror(errno)); } else { zlog_warn( "%s: sendto() partial to %s,%d: fd=%d len=%d: sent=%d", - __PRETTY_FUNCTION__, to_str, ntohs(to.sin_port), + __func__, to_str, ntohs(to.sin_port), ss->sock_fd, len, sent); } } @@ -268,7 +262,7 @@ static int ssmpingd_read_msg(struct ssmpingd_sock *ss) sizeof(source_str)); zlog_warn( "%s: failure receiving ssmping for source %s on fd=%d: errno=%d: %s", - __PRETTY_FUNCTION__, source_str, ss->sock_fd, errno, + __func__, source_str, ss->sock_fd, errno, safe_strerror(errno)); return -1; } @@ -286,8 +280,8 @@ static int ssmpingd_read_msg(struct ssmpingd_sock *ss) pim_inet4_dump("", to.sin_addr, to_str, sizeof(to_str)); zlog_warn( "%s: bad ssmping type=%d from %s,%d to %s,%d on interface %s ifindex=%d fd=%d src=%s", - __PRETTY_FUNCTION__, buf[0], from_str, - ntohs(from.sin_port), to_str, ntohs(to.sin_port), + __func__, buf[0], from_str, ntohs(from.sin_port), + to_str, ntohs(to.sin_port), ifp ? ifp->name : "", ifindex, ss->sock_fd, source_str); return 0; @@ -304,10 +298,9 @@ static int ssmpingd_read_msg(struct ssmpingd_sock *ss) pim_inet4_dump("", to.sin_addr, to_str, sizeof(to_str)); zlog_debug( "%s: recv ssmping from %s,%d to %s,%d on interface %s ifindex=%d fd=%d src=%s", - __PRETTY_FUNCTION__, from_str, ntohs(from.sin_port), - to_str, ntohs(to.sin_port), - ifp ? ifp->name : "", ifindex, ss->sock_fd, - source_str); + __func__, from_str, ntohs(from.sin_port), to_str, + ntohs(to.sin_port), ifp ? ifp->name : "", + ifindex, ss->sock_fd, source_str); } buf[0] = PIM_SSMPINGD_REPLY; @@ -361,7 +354,7 @@ static struct ssmpingd_sock *ssmpingd_new(struct pim_instance *pim, pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); zlog_warn("%s: ssmpingd_socket() failure for source %s", - __PRETTY_FUNCTION__, source_str); + __func__, source_str); return 0; } @@ -395,8 +388,8 @@ int pim_ssmpingd_start(struct pim_instance *pim, struct in_addr source_addr) char source_str[INET_ADDRSTRLEN]; pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); - zlog_info("%s: starting ssmpingd for source %s", - __PRETTY_FUNCTION__, source_str); + zlog_info("%s: starting ssmpingd for source %s", __func__, + source_str); } ss = ssmpingd_new(pim, source_addr); @@ -404,8 +397,8 @@ int pim_ssmpingd_start(struct pim_instance *pim, struct in_addr source_addr) char source_str[INET_ADDRSTRLEN]; pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); - zlog_warn("%s: ssmpingd_new() failure for source %s", - __PRETTY_FUNCTION__, source_str); + zlog_warn("%s: ssmpingd_new() failure for source %s", __func__, + source_str); return -1; } @@ -421,8 +414,8 @@ int pim_ssmpingd_stop(struct pim_instance *pim, struct in_addr source_addr) char source_str[INET_ADDRSTRLEN]; pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); - zlog_warn("%s: could not find ssmpingd for source %s", - __PRETTY_FUNCTION__, source_str); + zlog_warn("%s: could not find ssmpingd for source %s", __func__, + source_str); return -1; } @@ -430,8 +423,8 @@ int pim_ssmpingd_stop(struct pim_instance *pim, struct in_addr source_addr) char source_str[INET_ADDRSTRLEN]; pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); - zlog_info("%s: stopping ssmpingd for source %s", - __PRETTY_FUNCTION__, source_str); + zlog_info("%s: stopping ssmpingd for source %s", __func__, + source_str); } ssmpingd_delete(ss); diff --git a/pimd/pim_static.c b/pimd/pim_static.c index 442b22e06f..91c9b5b933 100644 --- a/pimd/pim_static.c +++ b/pimd/pim_static.c @@ -79,7 +79,7 @@ int pim_static_add(struct pim_instance *pim, struct interface *iif, if (!iif_index || !oif_index || iif_index == -1 || oif_index == -1) { zlog_warn( "%s %s: Unable to add static route: Invalid interface index(iif=%d,oif=%d)", - __FILE__, __PRETTY_FUNCTION__, iif_index, oif_index); + __FILE__, __func__, iif_index, oif_index); return -2; } @@ -88,7 +88,7 @@ int pim_static_add(struct pim_instance *pim, struct interface *iif, /* looped MFC entry */ zlog_warn( "%s %s: Unable to add static route: Looped MFC entry(iif=%d,oif=%d)", - __FILE__, __PRETTY_FUNCTION__, iif_index, oif_index); + __FILE__, __func__, iif_index, oif_index); return -4; } #endif @@ -109,9 +109,8 @@ int pim_static_add(struct pim_instance *pim, struct interface *iif, sizeof(sifaddr_str)); zlog_warn( "%s %s: Unable to add static route: Route already exists (iif=%d,oif=%d,group=%s,source=%s)", - __FILE__, __PRETTY_FUNCTION__, - iif_index, oif_index, gifaddr_str, - sifaddr_str); + __FILE__, __func__, iif_index, + oif_index, gifaddr_str, sifaddr_str); return -3; } @@ -124,9 +123,6 @@ int pim_static_add(struct pim_instance *pim, struct interface *iif, * back if it fails. */ original_s_route = static_route_alloc(); - if (!original_s_route) { - return -5; - } memcpy(original_s_route, s_route, sizeof(struct static_route)); @@ -141,7 +137,8 @@ int pim_static_add(struct pim_instance *pim, struct interface *iif, } else { /* input interface changed */ s_route->iif = iif_index; - s_route->c_oil.oil.mfcc_parent = iif_index; + pim_static_mroute_iif_update( + &s_route->c_oil, iif_index, __func__); #ifdef PIM_ENFORCE_LOOPFREE_MFC /* check to make sure the new input was not an @@ -180,7 +177,7 @@ int pim_static_add(struct pim_instance *pim, struct interface *iif, s_route->c_oil.pim = pim; - if (pim_mroute_add(&s_route->c_oil, __PRETTY_FUNCTION__)) { + if (pim_static_mroute_add(&s_route->c_oil, __func__)) { char gifaddr_str[INET_ADDRSTRLEN]; char sifaddr_str[INET_ADDRSTRLEN]; pim_inet4_dump("", group, gifaddr_str, @@ -189,8 +186,8 @@ int pim_static_add(struct pim_instance *pim, struct interface *iif, sizeof(sifaddr_str)); zlog_warn( "%s %s: Unable to add static route(iif=%d,oif=%d,group=%s,source=%s)", - __FILE__, __PRETTY_FUNCTION__, iif_index, oif_index, - gifaddr_str, sifaddr_str); + __FILE__, __func__, iif_index, oif_index, gifaddr_str, + sifaddr_str); /* Need to put s_route back to the way it was */ if (original_s_route) { @@ -224,7 +221,7 @@ int pim_static_add(struct pim_instance *pim, struct interface *iif, sizeof(sifaddr_str)); zlog_debug( "%s: Static route added(iif=%d,oif=%d,group=%s,source=%s)", - __PRETTY_FUNCTION__, iif_index, oif_index, gifaddr_str, + __func__, iif_index, oif_index, gifaddr_str, sifaddr_str); } @@ -246,7 +243,7 @@ int pim_static_del(struct pim_instance *pim, struct interface *iif, if (!iif_index || !oif_index) { zlog_warn( "%s %s: Unable to remove static route: Invalid interface index(iif=%d,oif=%d)", - __FILE__, __PRETTY_FUNCTION__, iif_index, oif_index); + __FILE__, __func__, iif_index, oif_index); return -2; } @@ -263,10 +260,9 @@ int pim_static_del(struct pim_instance *pim, struct interface *iif, * route, otherwise set the route with the new outputs */ if (s_route->c_oil.oil_ref_count <= 0 - ? pim_mroute_del(&s_route->c_oil, - __PRETTY_FUNCTION__) - : pim_mroute_add(&s_route->c_oil, - __PRETTY_FUNCTION__)) { + ? pim_mroute_del(&s_route->c_oil, __func__) + : pim_static_mroute_add(&s_route->c_oil, + __func__)) { char gifaddr_str[INET_ADDRSTRLEN]; char sifaddr_str[INET_ADDRSTRLEN]; pim_inet4_dump("", group, gifaddr_str, @@ -275,9 +271,8 @@ int pim_static_del(struct pim_instance *pim, struct interface *iif, sizeof(sifaddr_str)); zlog_warn( "%s %s: Unable to remove static route(iif=%d,oif=%d,group=%s,source=%s)", - __FILE__, __PRETTY_FUNCTION__, - iif_index, oif_index, gifaddr_str, - sifaddr_str); + __FILE__, __func__, iif_index, + oif_index, gifaddr_str, sifaddr_str); s_route->oif_ttls[oif_index] = 1; s_route->c_oil.oil.mfcc_ttls[oif_index] = 1; @@ -302,8 +297,8 @@ int pim_static_del(struct pim_instance *pim, struct interface *iif, sizeof(sifaddr_str)); zlog_debug( "%s: Static route removed(iif=%d,oif=%d,group=%s,source=%s)", - __PRETTY_FUNCTION__, iif_index, - oif_index, gifaddr_str, sifaddr_str); + __func__, iif_index, oif_index, + gifaddr_str, sifaddr_str); } break; @@ -319,8 +314,8 @@ int pim_static_del(struct pim_instance *pim, struct interface *iif, sizeof(sifaddr_str)); zlog_warn( "%s %s: Unable to remove static route: Route does not exist(iif=%d,oif=%d,group=%s,source=%s)", - __FILE__, __PRETTY_FUNCTION__, iif_index, oif_index, - gifaddr_str, sifaddr_str); + __FILE__, __func__, iif_index, oif_index, gifaddr_str, + sifaddr_str); return -3; } diff --git a/pimd/pim_time.c b/pimd/pim_time.c index f12f767537..9878fcf6b4 100644 --- a/pimd/pim_time.c +++ b/pimd/pim_time.c @@ -37,7 +37,7 @@ static int gettime_monotonic(struct timeval *tv) if (result) { flog_err_sys(EC_LIB_SYSTEM_CALL, "%s: gettimeofday() failure: errno=%d: %s", - __PRETTY_FUNCTION__, errno, safe_strerror(errno)); + __func__, errno, safe_strerror(errno)); } return result; @@ -54,7 +54,7 @@ int64_t pim_time_monotonic_sec(void) if (gettime_monotonic(&now_tv)) { flog_err_sys(EC_LIB_SYSTEM_CALL, "%s: gettime_monotonic() failure: errno=%d: %s", - __PRETTY_FUNCTION__, errno, safe_strerror(errno)); + __func__, errno, safe_strerror(errno)); return -1; } @@ -73,7 +73,7 @@ int64_t pim_time_monotonic_dsec(void) if (gettime_monotonic(&now_tv)) { flog_err_sys(EC_LIB_SYSTEM_CALL, "%s: gettime_monotonic() failure: errno=%d: %s", - __PRETTY_FUNCTION__, errno, safe_strerror(errno)); + __func__, errno, safe_strerror(errno)); return -1; } @@ -91,7 +91,7 @@ int64_t pim_time_monotonic_usec(void) if (gettime_monotonic(&now_tv)) { flog_err_sys(EC_LIB_SYSTEM_CALL, "%s: gettime_monotonic() failure: errno=%d: %s", - __PRETTY_FUNCTION__, errno, safe_strerror(errno)); + __func__, errno, safe_strerror(errno)); return -1; } diff --git a/pimd/pim_tlv.c b/pimd/pim_tlv.c index d93a360448..633bb207bd 100644 --- a/pimd/pim_tlv.c +++ b/pimd/pim_tlv.c @@ -121,32 +121,28 @@ int pim_encode_addr_ucast(uint8_t *buf, struct prefix *p) { switch (p->family) { case AF_INET: - *(uint8_t *)buf = - PIM_MSG_ADDRESS_FAMILY_IPV4; /* notice: AF_INET != - PIM_MSG_ADDRESS_FAMILY_IPV4 - */ + *buf = PIM_MSG_ADDRESS_FAMILY_IPV4; /* notice: AF_INET != + PIM_MSG_ADDRESS_FAMILY_IPV4 + */ ++buf; - *(uint8_t *)buf = 0; /* ucast IPv4 native encoding type (RFC + *buf = 0; /* ucast IPv4 native encoding type (RFC 4601: 4.9.1) */ ++buf; memcpy(buf, &p->u.prefix4, sizeof(struct in_addr)); return ucast_ipv4_encoding_len; - break; case AF_INET6: - *(uint8_t *)buf = PIM_MSG_ADDRESS_FAMILY_IPV6; + *buf = PIM_MSG_ADDRESS_FAMILY_IPV6; ++buf; - *(uint8_t *)buf = 0; + *buf = 0; ++buf; memcpy(buf, &p->u.prefix6, sizeof(struct in6_addr)); return ucast_ipv6_encoding_len; - break; default: return 0; - break; } } -#define group_ipv4_encoding_len (4 + sizeof (struct in_addr)) +#define group_ipv4_encoding_len (4 + sizeof(struct in_addr)) /* * Encoded-Group addresses take the following format: @@ -201,20 +197,18 @@ int pim_encode_addr_group(uint8_t *buf, afi_t afi, int bidir, int scope, switch (afi) { case AFI_IP: - *(uint8_t *)buf = PIM_MSG_ADDRESS_FAMILY_IPV4; + *buf = PIM_MSG_ADDRESS_FAMILY_IPV4; ++buf; - *(uint8_t *)buf = 0; + *buf = 0; ++buf; - *(uint8_t *)buf = flags; + *buf = flags; ++buf; - *(uint8_t *)buf = 32; + *buf = 32; ++buf; memcpy(buf, &group, sizeof(struct in_addr)); return group_ipv4_encoding_len; - break; default: return 0; - break; } } @@ -262,7 +256,7 @@ uint8_t *pim_tlv_append_addrlist_ucast(uint8_t *buf, const uint8_t *buf_pastend, if (PIM_DEBUG_PIM_TRACE_DETAIL) { zlog_debug( "%s: number of encoded secondary unicast IPv4 addresses: %zu", - __PRETTY_FUNCTION__, option_len / uel); + __func__, option_len / uel); } if (option_len < 1) { @@ -345,15 +339,15 @@ int pim_tlv_parse_holdtime(const char *ifname, struct in_addr src_addr, { const char *label = "holdtime"; - if (check_tlv_length(__PRETTY_FUNCTION__, label, ifname, src_addr, + if (check_tlv_length(__func__, label, ifname, src_addr, sizeof(uint16_t), option_len)) { return -1; } - check_tlv_redefinition_uint16( - __PRETTY_FUNCTION__, label, ifname, src_addr, *hello_options, - PIM_OPTION_MASK_HOLDTIME, PIM_TLV_GET_HOLDTIME(tlv_curr), - *hello_option_holdtime); + check_tlv_redefinition_uint16(__func__, label, ifname, src_addr, + *hello_options, PIM_OPTION_MASK_HOLDTIME, + PIM_TLV_GET_HOLDTIME(tlv_curr), + *hello_option_holdtime); PIM_OPTION_SET(*hello_options, PIM_OPTION_MASK_HOLDTIME); @@ -368,13 +362,13 @@ int pim_tlv_parse_lan_prune_delay(const char *ifname, struct in_addr src_addr, uint16_t *hello_option_override_interval, uint16_t option_len, const uint8_t *tlv_curr) { - if (check_tlv_length(__PRETTY_FUNCTION__, "lan_prune_delay", ifname, - src_addr, sizeof(uint32_t), option_len)) { + if (check_tlv_length(__func__, "lan_prune_delay", ifname, src_addr, + sizeof(uint32_t), option_len)) { return -1; } - check_tlv_redefinition_uint16(__PRETTY_FUNCTION__, "propagation_delay", - ifname, src_addr, *hello_options, + check_tlv_redefinition_uint16(__func__, "propagation_delay", ifname, + src_addr, *hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY, PIM_TLV_GET_PROPAGATION_DELAY(tlv_curr), *hello_option_propagation_delay); @@ -405,13 +399,13 @@ int pim_tlv_parse_dr_priority(const char *ifname, struct in_addr src_addr, { const char *label = "dr_priority"; - if (check_tlv_length(__PRETTY_FUNCTION__, label, ifname, src_addr, + if (check_tlv_length(__func__, label, ifname, src_addr, sizeof(uint32_t), option_len)) { return -1; } check_tlv_redefinition_uint32( - __PRETTY_FUNCTION__, label, ifname, src_addr, *hello_options, + __func__, label, ifname, src_addr, *hello_options, PIM_OPTION_MASK_DR_PRIORITY, PIM_TLV_GET_DR_PRIORITY(tlv_curr), *hello_option_dr_priority); @@ -429,13 +423,13 @@ int pim_tlv_parse_generation_id(const char *ifname, struct in_addr src_addr, { const char *label = "generation_id"; - if (check_tlv_length(__PRETTY_FUNCTION__, label, ifname, src_addr, + if (check_tlv_length(__func__, label, ifname, src_addr, sizeof(uint32_t), option_len)) { return -1; } - check_tlv_redefinition_uint32_hex(__PRETTY_FUNCTION__, label, ifname, - src_addr, *hello_options, + check_tlv_redefinition_uint32_hex(__func__, label, ifname, src_addr, + *hello_options, PIM_OPTION_MASK_GENERATION_ID, PIM_TLV_GET_GENERATION_ID(tlv_curr), *hello_option_generation_id); @@ -458,7 +452,7 @@ int pim_parse_addr_ucast(struct prefix *p, const uint8_t *buf, int buf_size) if (buf_size < ucast_encoding_min_len) { zlog_warn( "%s: unicast address encoding overflow: left=%d needed=%d", - __PRETTY_FUNCTION__, buf_size, ucast_encoding_min_len); + __func__, buf_size, ucast_encoding_min_len); return -1; } @@ -470,7 +464,7 @@ int pim_parse_addr_ucast(struct prefix *p, const uint8_t *buf, int buf_size) if (type) { zlog_warn("%s: unknown unicast address encoding type=%d", - __PRETTY_FUNCTION__, type); + __func__, type); return -2; } @@ -478,8 +472,8 @@ int pim_parse_addr_ucast(struct prefix *p, const uint8_t *buf, int buf_size) case PIM_MSG_ADDRESS_FAMILY_IPV4: if ((addr + sizeof(struct in_addr)) > pastend) { zlog_warn( - "%s: IPv4 unicast address overflow: left=%zd needed=%zu", - __PRETTY_FUNCTION__, pastend - addr, + "%s: IPv4 unicast address overflow: left=%td needed=%zu", + __func__, pastend - addr, sizeof(struct in_addr)); return -3; } @@ -494,8 +488,8 @@ int pim_parse_addr_ucast(struct prefix *p, const uint8_t *buf, int buf_size) case PIM_MSG_ADDRESS_FAMILY_IPV6: if ((addr + sizeof(struct in6_addr)) > pastend) { zlog_warn( - "%s: IPv6 unicast address overflow: left=%zd needed %zu", - __PRETTY_FUNCTION__, pastend - addr, + "%s: IPv6 unicast address overflow: left=%td needed %zu", + __func__, pastend - addr, sizeof(struct in6_addr)); return -3; } @@ -508,7 +502,7 @@ int pim_parse_addr_ucast(struct prefix *p, const uint8_t *buf, int buf_size) break; default: { zlog_warn("%s: unknown unicast address encoding family=%d from", - __PRETTY_FUNCTION__, family); + __func__, family); return -4; } } @@ -529,7 +523,7 @@ int pim_parse_addr_group(struct prefix_sg *sg, const uint8_t *buf, int buf_size) if (buf_size < grp_encoding_min_len) { zlog_warn( "%s: group address encoding overflow: left=%d needed=%d", - __PRETTY_FUNCTION__, buf_size, grp_encoding_min_len); + __func__, buf_size, grp_encoding_min_len); return -1; } @@ -547,14 +541,14 @@ int pim_parse_addr_group(struct prefix_sg *sg, const uint8_t *buf, int buf_size) if (type) { zlog_warn( "%s: unknown group address encoding type=%d from", - __PRETTY_FUNCTION__, type); + __func__, type); return -2; } if ((addr + sizeof(struct in_addr)) > pastend) { zlog_warn( - "%s: IPv4 group address overflow: left=%zd needed=%zu from", - __PRETTY_FUNCTION__, pastend - addr, + "%s: IPv4 group address overflow: left=%td needed=%zu from", + __func__, pastend - addr, sizeof(struct in_addr)); return -3; } @@ -567,7 +561,7 @@ int pim_parse_addr_group(struct prefix_sg *sg, const uint8_t *buf, int buf_size) default: { zlog_warn( "%s: unknown group address encoding family=%d mask_len=%d from", - __PRETTY_FUNCTION__, family, mask_len); + __func__, family, mask_len); return -4; } } @@ -589,7 +583,7 @@ int pim_parse_addr_source(struct prefix_sg *sg, uint8_t *flags, if (buf_size < src_encoding_min_len) { zlog_warn( "%s: source address encoding overflow: left=%d needed=%d", - __PRETTY_FUNCTION__, buf_size, src_encoding_min_len); + __func__, buf_size, src_encoding_min_len); return -1; } @@ -603,9 +597,8 @@ int pim_parse_addr_source(struct prefix_sg *sg, uint8_t *flags, if (type) { zlog_warn( - "%s: unknown source address encoding type=%d: %02x%02x%02x%02x%02x%02x%02x%02x", - __PRETTY_FUNCTION__, type, buf[0], buf[1], buf[2], - buf[3], buf[4], buf[5], buf[6], buf[7]); + "%s: unknown source address encoding type=%d: %02x%02x%02x%02x", + __func__, type, buf[0], buf[1], buf[2], buf[3]); return -2; } @@ -613,8 +606,8 @@ int pim_parse_addr_source(struct prefix_sg *sg, uint8_t *flags, case PIM_MSG_ADDRESS_FAMILY_IPV4: if ((addr + sizeof(struct in_addr)) > pastend) { zlog_warn( - "%s: IPv4 source address overflow: left=%zd needed=%zu", - __PRETTY_FUNCTION__, pastend - addr, + "%s: IPv4 source address overflow: left=%td needed=%zu", + __func__, pastend - addr, sizeof(struct in_addr)); return -3; } @@ -635,7 +628,7 @@ int pim_parse_addr_source(struct prefix_sg *sg, uint8_t *flags, */ if (mask_len != 32) { zlog_warn("%s: IPv4 bad source address mask: %d", - __PRETTY_FUNCTION__, mask_len); + __func__, mask_len); return -4; } @@ -644,9 +637,8 @@ int pim_parse_addr_source(struct prefix_sg *sg, uint8_t *flags, break; default: { zlog_warn( - "%s: unknown source address encoding family=%d: %02x%02x%02x%02x%02x%02x%02x%02x", - __PRETTY_FUNCTION__, family, buf[0], buf[1], buf[2], - buf[3], buf[4], buf[5], buf[6], buf[7]); + "%s: unknown source address encoding family=%d: %02x%02x%02x%02x", + __func__, family, buf[0], buf[1], buf[2], buf[3]); return -5; } } @@ -691,7 +683,7 @@ int pim_tlv_parse_addr_list(const char *ifname, struct in_addr src_addr, sizeof(src_str)); zlog_warn( "%s: pim_parse_addr_ucast() failure: from %s on %s", - __PRETTY_FUNCTION__, src_str, ifname); + __func__, src_str, ifname); FREE_ADDR_LIST(*hello_option_addr_list); return -1; } @@ -711,10 +703,10 @@ int pim_tlv_parse_addr_list(const char *ifname, struct in_addr src_addr, sizeof(src_str)); zlog_debug( "%s: PIM hello TLV option: list_old_size=%d IPv4 address %s from %s on %s", - __PRETTY_FUNCTION__, + __func__, *hello_option_addr_list ? ((int)listcount( - *hello_option_addr_list)) + *hello_option_addr_list)) : -1, addr_str, src_str, ifname); } break; @@ -726,10 +718,10 @@ int pim_tlv_parse_addr_list(const char *ifname, struct in_addr src_addr, sizeof(src_str)); zlog_debug( "%s: PIM hello TLV option: list_old_size=%d UNKNOWN address family from %s on %s", - __PRETTY_FUNCTION__, + __func__, *hello_option_addr_list ? ((int)listcount( - *hello_option_addr_list)) + *hello_option_addr_list)) : -1, src_str, ifname); } @@ -747,7 +739,7 @@ int pim_tlv_parse_addr_list(const char *ifname, struct in_addr src_addr, sizeof(src_str)); zlog_warn( "%s: ignoring primary address in secondary list from %s on %s", - __PRETTY_FUNCTION__, src_str, ifname); + __func__, src_str, ifname); continue; } } @@ -757,8 +749,7 @@ int pim_tlv_parse_addr_list(const char *ifname, struct in_addr src_addr, */ if (!*hello_option_addr_list) { *hello_option_addr_list = list_new(); - (*hello_option_addr_list)->del = - (void (*)(void *))prefix_free; + (*hello_option_addr_list)->del = prefix_free_lists; } /* diff --git a/pimd/pim_tlv.h b/pimd/pim_tlv.h index 657675b312..ef764656d3 100644 --- a/pimd/pim_tlv.h +++ b/pimd/pim_tlv.h @@ -48,8 +48,18 @@ typedef uint32_t pim_hello_options; #define PIM_OPTION_UNSET(options, option_mask) ((options) &= ~(option_mask)) #define PIM_OPTION_IS_SET(options, option_mask) ((options) & (option_mask)) -#define PIM_TLV_GET_UINT16(buf) ntohs(*(const uint16_t *)(buf)) -#define PIM_TLV_GET_UINT32(buf) ntohl(*(const uint32_t *)(buf)) +#define PIM_TLV_GET_UINT16(buf) \ + ({ \ + uint16_t _tmp; \ + memcpy(&_tmp, (buf), sizeof(uint16_t)); \ + ntohs(_tmp); \ + }) +#define PIM_TLV_GET_UINT32(buf) \ + ({ \ + uint32_t _tmp; \ + memcpy(&_tmp, (buf), sizeof(uint32_t)); \ + ntohl(_tmp); \ + }) #define PIM_TLV_GET_TYPE(buf) PIM_TLV_GET_UINT16(buf) #define PIM_TLV_GET_LENGTH(buf) PIM_TLV_GET_UINT16(buf) #define PIM_TLV_GET_HOLDTIME(buf) PIM_TLV_GET_UINT16(buf) diff --git a/pimd/pim_upstream.c b/pimd/pim_upstream.c index b708e86a20..d3fb0d46de 100644 --- a/pimd/pim_upstream.c +++ b/pimd/pim_upstream.c @@ -29,6 +29,7 @@ #include "hash.h" #include "jhash.h" #include "wheel.h" +#include "network.h" #include "pimd.h" #include "pim_pim.h" @@ -52,10 +53,12 @@ #include "pim_nht.h" #include "pim_ssm.h" #include "pim_vxlan.h" +#include "pim_mlag.h" static void join_timer_stop(struct pim_upstream *up); static void pim_upstream_update_assert_tracking_desired(struct pim_upstream *up); +static bool pim_upstream_sg_running_proc(struct pim_upstream *up); /* * A (*,G) or a (*,*) is going away @@ -75,11 +78,15 @@ static void pim_upstream_remove_children(struct pim_instance *pim, listnode_delete(up->sources, child); if (PIM_UPSTREAM_FLAG_TEST_SRC_LHR(child->flags)) { PIM_UPSTREAM_FLAG_UNSET_SRC_LHR(child->flags); - child = pim_upstream_del(pim, child, - __PRETTY_FUNCTION__); + child = pim_upstream_del(pim, child, __func__); } - if (child) + if (child) { child->parent = NULL; + if (PIM_UPSTREAM_FLAG_TEST_USE_RPT(child->flags)) + pim_upstream_mroute_iif_update( + child->channel_oil, + __func__); + } } list_delete(&up->sources); } @@ -93,7 +100,6 @@ static void pim_upstream_find_new_children(struct pim_instance *pim, struct pim_upstream *up) { struct pim_upstream *child; - struct listnode *ch_node; if ((up->sg.src.s_addr != INADDR_ANY) && (up->sg.grp.s_addr != INADDR_ANY)) @@ -103,12 +109,16 @@ static void pim_upstream_find_new_children(struct pim_instance *pim, && (up->sg.grp.s_addr == INADDR_ANY)) return; - for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, ch_node, child)) { + frr_each (rb_pim_upstream, &pim->upstream_head, child) { if ((up->sg.grp.s_addr != INADDR_ANY) && (child->sg.grp.s_addr == up->sg.grp.s_addr) && (child != up)) { child->parent = up; listnode_add_sort(up->sources, child); + if (PIM_UPSTREAM_FLAG_TEST_USE_RPT(child->flags)) + pim_upstream_mroute_iif_update( + child->channel_oil, + __func__); } } } @@ -133,6 +143,18 @@ static struct pim_upstream *pim_upstream_find_parent(struct pim_instance *pim, if (up) listnode_add(up->sources, child); + /* + * In case parent is MLAG entry copy the data to child + */ + if (up && PIM_UPSTREAM_FLAG_TEST_MLAG_INTERFACE(up->flags)) { + PIM_UPSTREAM_FLAG_SET_MLAG_INTERFACE(child->flags); + if (PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(up->flags)) + PIM_UPSTREAM_FLAG_SET_MLAG_NON_DF(child->flags); + else + PIM_UPSTREAM_FLAG_UNSET_MLAG_NON_DF( + child->flags); + } + return up; } @@ -141,14 +163,22 @@ static struct pim_upstream *pim_upstream_find_parent(struct pim_instance *pim, static void upstream_channel_oil_detach(struct pim_upstream *up) { - if (up->channel_oil) { + struct channel_oil *channel_oil = up->channel_oil; + + if (channel_oil) { /* Detaching from channel_oil, channel_oil may exist post del, but upstream would not keep reference of it */ - up->channel_oil->up = NULL; - pim_channel_oil_del(up->channel_oil); + channel_oil->up = NULL; up->channel_oil = NULL; + + /* attempt to delete channel_oil; if channel_oil is being held + * because of other references cleanup info such as "Mute" + * inferred from the parent upstream + */ + pim_channel_oil_upstream_deref(channel_oil); } + } struct pim_upstream *pim_upstream_del(struct pim_instance *pim, @@ -159,10 +189,10 @@ struct pim_upstream *pim_upstream_del(struct pim_instance *pim, bool notify_msdp = false; struct prefix nht_p; - if (PIM_DEBUG_TRACE) + if (PIM_DEBUG_PIM_TRACE) zlog_debug( "%s(%s): Delete %s[%s] ref count: %d , flags: %d c_oil ref count %d (Pre decrement)", - __PRETTY_FUNCTION__, name, up->sg_str, pim->vrf->name, + __func__, name, up->sg_str, pim->vrf->name, up->ref_count, up->flags, up->channel_oil->oil_ref_count); @@ -173,6 +203,13 @@ struct pim_upstream *pim_upstream_del(struct pim_instance *pim, if (up->ref_count >= 1) return up; + if (PIM_DEBUG_TRACE) + zlog_debug("pim_upstream free vrf:%s %s flags 0x%x", + pim->vrf->name, up->sg_str, up->flags); + + if (pim_up_mlag_is_local(up)) + pim_mlag_up_local_del(pim, up); + THREAD_OFF(up->t_ka_timer); THREAD_OFF(up->t_rs_timer); THREAD_OFF(up->t_msdp_reg_timer); @@ -198,7 +235,7 @@ struct pim_upstream *pim_upstream_del(struct pim_instance *pim, notify_msdp = true; } - pim_mroute_del(up->channel_oil, __PRETTY_FUNCTION__); + pim_mroute_del(up->channel_oil, __func__); upstream_channel_oil_detach(up); for (ALL_LIST_ELEMENTS(up->ifchannels, node, nnode, ch)) @@ -213,8 +250,7 @@ struct pim_upstream *pim_upstream_del(struct pim_instance *pim, listnode_delete(up->parent->sources, up); up->parent = NULL; - listnode_delete(pim->upstream_list, up); - hash_release(pim->upstream_hash, up); + rb_pim_upstream_del(&pim->upstream_head, up); if (notify_msdp) { pim_msdp_up_del(pim, &up->sg); @@ -231,13 +267,14 @@ struct pim_upstream *pim_upstream_del(struct pim_instance *pim, nht_p.family = AF_INET; nht_p.prefixlen = IPV4_MAX_BITLEN; nht_p.u.prefix4 = up->upstream_addr; - if (PIM_DEBUG_TRACE) { + if (PIM_DEBUG_PIM_TRACE) { char buf[PREFIX2STR_BUFFER]; prefix2str(&nht_p, buf, sizeof(buf)); - zlog_debug("%s: Deregister upstream %s addr %s with Zebra NHT", - __PRETTY_FUNCTION__, up->sg_str, buf); + zlog_debug( + "%s: Deregister upstream %s addr %s with Zebra NHT", + __func__, up->sg_str, buf); } - pim_delete_tracked_nexthop(pim, &nht_p, up, NULL); + pim_delete_tracked_nexthop(pim, &nht_p, up, NULL, false); } XFREE(MTYPE_PIM_UPSTREAM, up); @@ -248,23 +285,23 @@ struct pim_upstream *pim_upstream_del(struct pim_instance *pim, void pim_upstream_send_join(struct pim_upstream *up) { if (!up->rpf.source_nexthop.interface) { - if (PIM_DEBUG_TRACE) - zlog_debug("%s: up %s RPF is not present", - __PRETTY_FUNCTION__, up->sg_str); + if (PIM_DEBUG_PIM_TRACE) + zlog_debug("%s: up %s RPF is not present", __func__, + up->sg_str); return; } - if (PIM_DEBUG_TRACE) { + if (PIM_DEBUG_PIM_TRACE) { char rpf_str[PREFIX_STRLEN]; pim_addr_dump("", &up->rpf.rpf_addr, rpf_str, sizeof(rpf_str)); - zlog_debug("%s: RPF'%s=%s(%s) for Interface %s", - __PRETTY_FUNCTION__, up->sg_str, rpf_str, + zlog_debug("%s: RPF'%s=%s(%s) for Interface %s", __func__, + up->sg_str, rpf_str, pim_upstream_state2str(up->join_state), up->rpf.source_nexthop.interface->name); if (pim_rpf_addr_is_inaddr_any(&up->rpf)) { zlog_debug("%s: can't send join upstream: RPF'%s=%s", - __PRETTY_FUNCTION__, up->sg_str, rpf_str); + __func__, up->sg_str, rpf_str); /* warning only */ } } @@ -280,9 +317,9 @@ static int on_join_timer(struct thread *t) up = THREAD_ARG(t); if (!up->rpf.source_nexthop.interface) { - if (PIM_DEBUG_TRACE) - zlog_debug("%s: up %s RPF is not present", - __PRETTY_FUNCTION__, up->sg_str); + if (PIM_DEBUG_PIM_TRACE) + zlog_debug("%s: up %s RPF is not present", __func__, + up->sg_str); return 0; } @@ -316,7 +353,7 @@ static void join_timer_stop(struct pim_upstream *up) up->rpf.rpf_addr.u.prefix4); if (nbr) - pim_jp_agg_remove_group(nbr->upstream_jp_agg, up); + pim_jp_agg_remove_group(nbr->upstream_jp_agg, up, nbr); pim_jp_agg_upstream_verification(up, false); } @@ -332,13 +369,12 @@ void join_timer_start(struct pim_upstream *up) if (PIM_DEBUG_PIM_EVENTS) { zlog_debug( "%s: starting %d sec timer for upstream (S,G)=%s", - __PRETTY_FUNCTION__, router->t_periodic, - up->sg_str); + __func__, router->t_periodic, up->sg_str); } } if (nbr) - pim_jp_agg_add_group(nbr->upstream_jp_agg, up, 1); + pim_jp_agg_add_group(nbr->upstream_jp_agg, up, 1, nbr); else { THREAD_OFF(up->t_join_timer); thread_add_timer(router->master, on_join_timer, up, @@ -366,7 +402,7 @@ static void pim_upstream_join_timer_restart_msec(struct pim_upstream *up, { if (PIM_DEBUG_PIM_EVENTS) { zlog_debug("%s: restarting %d msec timer for upstream (S,G)=%s", - __PRETTY_FUNCTION__, interval_msec, up->sg_str); + __func__, interval_msec, up->sg_str); } THREAD_OFF(up->t_join_timer); @@ -378,12 +414,13 @@ void pim_upstream_join_suppress(struct pim_upstream *up, struct in_addr rpf_addr, int holdtime) { long t_joinsuppress_msec; - long join_timer_remain_msec; + long join_timer_remain_msec = 0; + struct pim_neighbor *nbr = NULL; if (!up->rpf.source_nexthop.interface) { - if (PIM_DEBUG_TRACE) - zlog_debug("%s: up %s RPF is not present", - __PRETTY_FUNCTION__, up->sg_str); + if (PIM_DEBUG_PIM_TRACE) + zlog_debug("%s: up %s RPF is not present", __func__, + up->sg_str); return; } @@ -391,25 +428,39 @@ void pim_upstream_join_suppress(struct pim_upstream *up, MIN(pim_if_t_suppressed_msec(up->rpf.source_nexthop.interface), 1000 * holdtime); - join_timer_remain_msec = pim_time_timer_remain_msec(up->t_join_timer); + if (up->t_join_timer) + join_timer_remain_msec = + pim_time_timer_remain_msec(up->t_join_timer); + else { + /* Remove it from jp agg from the nbr for suppression */ + nbr = pim_neighbor_find(up->rpf.source_nexthop.interface, + up->rpf.rpf_addr.u.prefix4); + if (nbr) { + join_timer_remain_msec = + pim_time_timer_remain_msec(nbr->jp_timer); + } + } - if (PIM_DEBUG_TRACE) { + if (PIM_DEBUG_PIM_TRACE) { char rpf_str[INET_ADDRSTRLEN]; pim_inet4_dump("", rpf_addr, rpf_str, sizeof(rpf_str)); zlog_debug( "%s %s: detected Join%s to RPF'(S,G)=%s: join_timer=%ld msec t_joinsuppress=%ld msec", - __FILE__, __PRETTY_FUNCTION__, up->sg_str, rpf_str, + __FILE__, __func__, up->sg_str, rpf_str, join_timer_remain_msec, t_joinsuppress_msec); } if (join_timer_remain_msec < t_joinsuppress_msec) { - if (PIM_DEBUG_TRACE) { + if (PIM_DEBUG_PIM_TRACE) { zlog_debug( "%s %s: suppressing Join(S,G)=%s for %ld msec", - __FILE__, __PRETTY_FUNCTION__, up->sg_str, + __FILE__, __func__, up->sg_str, t_joinsuppress_msec); } + if (nbr) + pim_jp_agg_remove_group(nbr->upstream_jp_agg, up, nbr); + pim_upstream_join_timer_restart_msec(up, t_joinsuppress_msec); } } @@ -421,17 +472,33 @@ void pim_upstream_join_timer_decrease_to_t_override(const char *debug_label, int t_override_msec; if (!up->rpf.source_nexthop.interface) { - if (PIM_DEBUG_TRACE) - zlog_debug("%s: up %s RPF is not present", - __PRETTY_FUNCTION__, up->sg_str); + if (PIM_DEBUG_PIM_TRACE) + zlog_debug("%s: up %s RPF is not present", __func__, + up->sg_str); return; } - join_timer_remain_msec = pim_time_timer_remain_msec(up->t_join_timer); t_override_msec = pim_if_t_override_msec(up->rpf.source_nexthop.interface); - if (PIM_DEBUG_TRACE) { + if (up->t_join_timer) { + join_timer_remain_msec = + pim_time_timer_remain_msec(up->t_join_timer); + } else { + /* upstream join tracked with neighbor jp timer */ + struct pim_neighbor *nbr; + + nbr = pim_neighbor_find(up->rpf.source_nexthop.interface, + up->rpf.rpf_addr.u.prefix4); + if (nbr) + join_timer_remain_msec = + pim_time_timer_remain_msec(nbr->jp_timer); + else + /* Manipulate such that override takes place */ + join_timer_remain_msec = t_override_msec + 1; + } + + if (PIM_DEBUG_PIM_TRACE) { char rpf_str[INET_ADDRSTRLEN]; pim_inet4_dump("", up->rpf.rpf_addr.u.prefix4, rpf_str, sizeof(rpf_str)); @@ -442,7 +509,7 @@ void pim_upstream_join_timer_decrease_to_t_override(const char *debug_label, } if (join_timer_remain_msec > t_override_msec) { - if (PIM_DEBUG_TRACE) { + if (PIM_DEBUG_PIM_TRACE) { zlog_debug( "%s: decreasing (S,G)=%s join timer to t_override=%d msec", debug_label, up->sg_str, t_override_msec); @@ -480,7 +547,7 @@ static void forward_off(struct pim_upstream *up) } /* scan iface channel list */ } -static int pim_upstream_could_register(struct pim_upstream *up) +int pim_upstream_could_register(struct pim_upstream *up) { struct pim_interface *pim_ifp = NULL; @@ -494,9 +561,9 @@ static int pim_upstream_could_register(struct pim_upstream *up) if (up->rpf.source_nexthop.interface) pim_ifp = up->rpf.source_nexthop.interface->info; else { - if (PIM_DEBUG_TRACE) - zlog_debug("%s: up %s RPF is not present", - __PRETTY_FUNCTION__, up->sg_str); + if (PIM_DEBUG_PIM_TRACE) + zlog_debug("%s: up %s RPF is not present", __func__, + up->sg_str); } if (pim_ifp && PIM_I_am_DR(pim_ifp) @@ -511,15 +578,15 @@ static int pim_upstream_could_register(struct pim_upstream *up) * we re-revaluate register setup for existing upstream entries */ void pim_upstream_register_reevaluate(struct pim_instance *pim) { - struct listnode *upnode; struct pim_upstream *up; - for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, upnode, up)) { + frr_each (rb_pim_upstream, &pim->upstream_head, up) { /* If FHR is set CouldRegister is True. Also check if the flow * is actually active; if it is not kat setup will trigger * source * registration whenever the flow becomes active. */ - if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags) || !up->t_ka_timer) + if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags) || + !pim_upstream_is_kat_running(up)) continue; if (pim_is_grp_ssm(pim, up->sg.grp)) { @@ -532,7 +599,8 @@ void pim_upstream_register_reevaluate(struct pim_instance *pim) /* remove regiface from the OIL if it is there*/ pim_channel_del_oif(up->channel_oil, pim->regiface, - PIM_OIF_FLAG_PROTO_PIM); + PIM_OIF_FLAG_PROTO_PIM, + __func__); up->reg_state = PIM_REG_NOINFO; } } else { @@ -544,13 +612,86 @@ void pim_upstream_register_reevaluate(struct pim_instance *pim) up->sg_str); pim_channel_add_oif(up->channel_oil, pim->regiface, - PIM_OIF_FLAG_PROTO_PIM); + PIM_OIF_FLAG_PROTO_PIM, + __func__); up->reg_state = PIM_REG_JOIN; } } } } +/* RFC7761, Section 4.2 “Data Packet Forwarding Rules” says we should + * forward a S - + * 1. along the SPT if SPTbit is set + * 2. and along the RPT if SPTbit is not set + * If forwarding is hw accelerated i.e. control and dataplane components + * are separate you may not be able to reliably set SPT bit on intermediate + * routers while still fowarding on the (S,G,rpt). + * + * This macro is a slight deviation on the RFC and uses "traffic-agnostic" + * criteria to decide between using the RPT vs. SPT for forwarding. + */ +void pim_upstream_update_use_rpt(struct pim_upstream *up, + bool update_mroute) +{ + bool old_use_rpt; + bool new_use_rpt; + + if (up->sg.src.s_addr == INADDR_ANY) + return; + + old_use_rpt = !!PIM_UPSTREAM_FLAG_TEST_USE_RPT(up->flags); + + /* We will use the SPT (IIF=RPF_interface(S) if - + * 1. We have decided to join the SPT + * 2. We are FHR + * 3. Source is directly connected + * 4. We are RP (parent's IIF is lo or vrf-device) + * In all other cases the source will stay along the RPT and + * IIF=RPF_interface(RP). + */ + if (up->join_state == PIM_UPSTREAM_JOINED || + PIM_UPSTREAM_FLAG_TEST_FHR(up->flags) || + pim_if_connected_to_source( + up->rpf.source_nexthop.interface, + up->sg.src) || + /* XXX - need to switch this to a more efficient + * lookup API + */ + I_am_RP(up->pim, up->sg.grp)) + /* use SPT */ + PIM_UPSTREAM_FLAG_UNSET_USE_RPT(up->flags); + else + /* use RPT */ + PIM_UPSTREAM_FLAG_SET_USE_RPT(up->flags); + + new_use_rpt = !!PIM_UPSTREAM_FLAG_TEST_USE_RPT(up->flags); + if (old_use_rpt != new_use_rpt) { + if (PIM_DEBUG_PIM_EVENTS) + zlog_debug("%s switched from %s to %s", + up->sg_str, + old_use_rpt?"RPT":"SPT", + new_use_rpt?"RPT":"SPT"); + if (update_mroute) + pim_upstream_mroute_add(up->channel_oil, __func__); + } +} + +/* some events like RP change require re-evaluation of SGrpt across + * all groups + */ +void pim_upstream_reeval_use_rpt(struct pim_instance *pim) +{ + struct pim_upstream *up; + + frr_each (rb_pim_upstream, &pim->upstream_head, up) { + if (up->sg.src.s_addr == INADDR_ANY) + continue; + + pim_upstream_update_use_rpt(up, true /*update_mroute*/); + } +} + void pim_upstream_switch(struct pim_instance *pim, struct pim_upstream *up, enum pim_upstream_state new_state) { @@ -558,21 +699,21 @@ void pim_upstream_switch(struct pim_instance *pim, struct pim_upstream *up, if (up->upstream_addr.s_addr == INADDR_ANY) { if (PIM_DEBUG_PIM_EVENTS) - zlog_debug("%s: RPF not configured for %s", - __PRETTY_FUNCTION__, up->sg_str); + zlog_debug("%s: RPF not configured for %s", __func__, + up->sg_str); return; } if (!up->rpf.source_nexthop.interface) { if (PIM_DEBUG_PIM_EVENTS) - zlog_debug("%s: RP not reachable for %s", - __PRETTY_FUNCTION__, up->sg_str); + zlog_debug("%s: RP not reachable for %s", __func__, + up->sg_str); return; } if (PIM_DEBUG_PIM_EVENTS) { zlog_debug("%s: PIM_UPSTREAM_%s: (S,G) old: %s new: %s", - __PRETTY_FUNCTION__, up->sg_str, + __func__, up->sg_str, pim_upstream_state2str(up->join_state), pim_upstream_state2str(new_state)); } @@ -603,21 +744,47 @@ void pim_upstream_switch(struct pim_instance *pim, struct pim_upstream *up, join_timer_start(up); } } + if (old_state != new_state) + pim_upstream_update_use_rpt(up, true /*update_mroute*/); } else { + bool old_use_rpt; + bool new_use_rpt; + bool send_xg_jp = false; forward_off(up); if (old_state == PIM_UPSTREAM_JOINED) pim_msdp_up_join_state_changed(pim, up); + if (old_state != new_state) { + old_use_rpt = + !!PIM_UPSTREAM_FLAG_TEST_USE_RPT(up->flags); + pim_upstream_update_use_rpt(up, true /*update_mroute*/); + new_use_rpt = + !!PIM_UPSTREAM_FLAG_TEST_USE_RPT(up->flags); + if (new_use_rpt && + (new_use_rpt != old_use_rpt) && + up->parent) + /* we have decided to switch from the SPT back + * to the RPT which means we need to cancel + * any previously sent SGrpt prunes immediately + */ + send_xg_jp = true; + } + /* IHR, Trigger SGRpt on *,G IIF to prune S,G from RPT towards RP. If I am RP for G then send S,G prune to its IIF. */ - if (pim_upstream_is_sg_rpt(up) && up->parent - && !I_am_RP(pim, up->sg.grp)) { + if (pim_upstream_is_sg_rpt(up) && up->parent && + !I_am_RP(pim, up->sg.grp)) + send_xg_jp = true; + else + pim_jp_agg_single_upstream_send(&up->rpf, up, + 0 /* prune */); + + if (send_xg_jp) { if (PIM_DEBUG_PIM_TRACE_DETAIL) zlog_debug( - "%s: *,G IIF %s S,G IIF %s ", - __PRETTY_FUNCTION__, + "re-join RPT; *,G IIF %s S,G IIF %s ", up->parent->rpf.source_nexthop.interface ? up->parent->rpf.source_nexthop.interface->name : "Unknown", @@ -627,18 +794,14 @@ void pim_upstream_switch(struct pim_instance *pim, struct pim_upstream *up, pim_jp_agg_single_upstream_send(&up->parent->rpf, up->parent, 1 /* (W,G) Join */); - } else - pim_jp_agg_single_upstream_send(&up->rpf, up, - 0 /* prune */); + } join_timer_stop(up); } } -int pim_upstream_compare(void *arg1, void *arg2) +int pim_upstream_compare(const struct pim_upstream *up1, + const struct pim_upstream *up2) { - const struct pim_upstream *up1 = (const struct pim_upstream *)arg1; - const struct pim_upstream *up2 = (const struct pim_upstream *)arg2; - if (ntohl(up1->sg.grp.s_addr) < ntohl(up2->sg.grp.s_addr)) return -1; @@ -683,26 +846,28 @@ static struct pim_upstream *pim_upstream_new(struct pim_instance *pim, up = XCALLOC(MTYPE_PIM_UPSTREAM, sizeof(*up)); + up->pim = pim; up->sg = *sg; pim_str_sg_set(sg, up->sg_str); if (ch) ch->upstream = up; - up = hash_get(pim->upstream_hash, up, hash_alloc_intern); + rb_pim_upstream_add(&pim->upstream_head, up); /* Set up->upstream_addr as INADDR_ANY, if RP is not * configured and retain the upstream data structure */ if (!pim_rp_set_upstream_addr(pim, &up->upstream_addr, sg->src, sg->grp)) { - if (PIM_DEBUG_TRACE) + if (PIM_DEBUG_PIM_TRACE) zlog_debug("%s: Received a (*,G) with no RP configured", - __PRETTY_FUNCTION__); + __func__); } up->parent = pim_upstream_find_parent(pim, up); if (up->sg.src.s_addr == INADDR_ANY) { up->sources = list_new(); - up->sources->cmp = pim_upstream_compare; + up->sources->cmp = + (int (*)(void *, void *))pim_upstream_compare; } else up->sources = NULL; @@ -716,7 +881,7 @@ static struct pim_upstream *pim_upstream_new(struct pim_instance *pim, up->join_state = PIM_UPSTREAM_NOTJOINED; up->reg_state = PIM_REG_NOINFO; up->state_transition = pim_time_monotonic_sec(); - up->channel_oil = NULL; + up->channel_oil = pim_channel_oil_add(pim, &up->sg, __func__); up->sptbit = PIM_UPSTREAM_SPTBIT_FALSE; up->rpf.source_nexthop.interface = NULL; @@ -733,55 +898,95 @@ static struct pim_upstream *pim_upstream_new(struct pim_instance *pim, up->ifchannels = list_new(); up->ifchannels->cmp = (int (*)(void *, void *))pim_ifchannel_compare; - if (up->sg.src.s_addr != INADDR_ANY) + if (up->sg.src.s_addr != INADDR_ANY) { wheel_add_item(pim->upstream_sg_wheel, up); - if (PIM_UPSTREAM_FLAG_TEST_STATIC_IIF(up->flags)) { + /* Inherit the DF role from the parent (*, G) entry for + * VxLAN BUM groups + */ + if (up->parent + && PIM_UPSTREAM_FLAG_TEST_MLAG_VXLAN(up->parent->flags) + && PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(up->parent->flags)) { + PIM_UPSTREAM_FLAG_SET_MLAG_NON_DF(up->flags); + if (PIM_DEBUG_VXLAN) + zlog_debug( + "upstream %s inherited mlag non-df flag from parent", + up->sg_str); + } + } + + if (PIM_UPSTREAM_FLAG_TEST_STATIC_IIF(up->flags) + || PIM_UPSTREAM_FLAG_TEST_SRC_NOCACHE(up->flags)) { pim_upstream_fill_static_iif(up, incoming); pim_ifp = up->rpf.source_nexthop.interface->info; assert(pim_ifp); - up->channel_oil = pim_channel_oil_add(pim, - &up->sg, pim_ifp->mroute_vif_index); - } else if (up->upstream_addr.s_addr == INADDR_ANY) { - /* Create a dummmy channel oil with incoming ineterface MAXVIFS, - * since RP is not configured - */ - up->channel_oil = pim_channel_oil_add(pim, &up->sg, MAXVIFS); - - } else { - rpf_result = pim_rpf_update(pim, up, NULL, 1); + pim_upstream_update_use_rpt(up, + false /*update_mroute*/); + pim_upstream_mroute_iif_update(up->channel_oil, __func__); + + if (PIM_UPSTREAM_FLAG_TEST_SRC_NOCACHE(up->flags)) + pim_upstream_keep_alive_timer_start( + up, pim->keep_alive_time); + } else if (up->upstream_addr.s_addr != INADDR_ANY) { + pim_upstream_update_use_rpt(up, + false /*update_mroute*/); + rpf_result = pim_rpf_update(pim, up, NULL, __func__); if (rpf_result == PIM_RPF_FAILURE) { - if (PIM_DEBUG_TRACE) + if (PIM_DEBUG_PIM_TRACE) zlog_debug( "%s: Attempting to create upstream(%s), Unable to RPF for source", - __PRETTY_FUNCTION__, up->sg_str); - /* Create a dummmy channel oil with incoming ineterface - * MAXVIFS, since RP is not reachable - */ - up->channel_oil = pim_channel_oil_add( - pim, &up->sg, MAXVIFS); + __func__, up->sg_str); } if (up->rpf.source_nexthop.interface) { - pim_ifp = up->rpf.source_nexthop.interface->info; - if (pim_ifp) - up->channel_oil = pim_channel_oil_add(pim, - &up->sg, pim_ifp->mroute_vif_index); + pim_upstream_mroute_iif_update(up->channel_oil, + __func__); } } - listnode_add_sort(pim->upstream_list, up); + /* send the entry to the MLAG peer */ + /* XXX - duplicate send is possible here if pim_rpf_update + * successfully resolved the nexthop + */ + if (pim_up_mlag_is_local(up) + || PIM_UPSTREAM_FLAG_TEST_MLAG_INTERFACE(up->flags)) + pim_mlag_up_local_add(pim, up); - if (PIM_DEBUG_TRACE) { + if (PIM_DEBUG_PIM_TRACE) { zlog_debug( "%s: Created Upstream %s upstream_addr %s ref count %d increment", - __PRETTY_FUNCTION__, up->sg_str, - inet_ntoa(up->upstream_addr), up->ref_count); + __func__, up->sg_str, inet_ntoa(up->upstream_addr), + up->ref_count); } return up; } +uint32_t pim_up_mlag_local_cost(struct pim_upstream *up) +{ + if (!(pim_up_mlag_is_local(up)) + && !(up->flags & PIM_UPSTREAM_FLAG_MASK_MLAG_INTERFACE)) + return router->infinite_assert_metric.route_metric; + + if ((up->rpf.source_nexthop.interface == + up->pim->vxlan.peerlink_rif) && + (up->rpf.source_nexthop.mrib_route_metric < + (router->infinite_assert_metric.route_metric - + PIM_UPSTREAM_MLAG_PEERLINK_PLUS_METRIC))) + return up->rpf.source_nexthop.mrib_route_metric + + PIM_UPSTREAM_MLAG_PEERLINK_PLUS_METRIC; + + return up->rpf.source_nexthop.mrib_route_metric; +} + +uint32_t pim_up_mlag_peer_cost(struct pim_upstream *up) +{ + if (!(up->flags & PIM_UPSTREAM_FLAG_MASK_MLAG_PEER)) + return router->infinite_assert_metric.route_metric; + + return up->mlag.peer_mrib_metric; +} + struct pim_upstream *pim_upstream_find(struct pim_instance *pim, struct prefix_sg *sg) { @@ -789,46 +994,54 @@ struct pim_upstream *pim_upstream_find(struct pim_instance *pim, struct pim_upstream *up = NULL; lookup.sg = *sg; - up = hash_lookup(pim->upstream_hash, &lookup); + up = rb_pim_upstream_find(&pim->upstream_head, &lookup); return up; } struct pim_upstream *pim_upstream_find_or_add(struct prefix_sg *sg, - struct interface *incoming, - int flags, const char *name) + struct interface *incoming, + int flags, const char *name) { - struct pim_upstream *up; - struct pim_interface *pim_ifp; - - pim_ifp = incoming->info; - - up = pim_upstream_find(pim_ifp->pim, sg); - - if (up) { - if (!(up->flags & flags)) { - up->flags |= flags; - up->ref_count++; - if (PIM_DEBUG_TRACE) - zlog_debug( - "%s(%s): upstream %s ref count %d increment", - __PRETTY_FUNCTION__, name, up->sg_str, - up->ref_count); - } - } else - up = pim_upstream_add(pim_ifp->pim, sg, incoming, flags, name, - NULL); + struct pim_interface *pim_ifp = incoming->info; - return up; + return (pim_upstream_add(pim_ifp->pim, sg, incoming, flags, name, + NULL)); } void pim_upstream_ref(struct pim_upstream *up, int flags, const char *name) { + /* if a local MLAG reference is being created we need to send the mroute + * to the peer + */ + if (!PIM_UPSTREAM_FLAG_TEST_MLAG_VXLAN(up->flags) && + PIM_UPSTREAM_FLAG_TEST_MLAG_VXLAN(flags)) { + PIM_UPSTREAM_FLAG_SET_MLAG_VXLAN(up->flags); + pim_mlag_up_local_add(up->pim, up); + } + + /* when we go from non-FHR to FHR we need to re-eval traffic + * forwarding path + */ + if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags) && + PIM_UPSTREAM_FLAG_TEST_FHR(flags)) { + PIM_UPSTREAM_FLAG_SET_FHR(up->flags); + pim_upstream_update_use_rpt(up, true /*update_mroute*/); + } + + /* re-eval joinDesired; clearing peer-msdp-sa flag can + * cause JD to change + */ + if (!PIM_UPSTREAM_FLAG_TEST_SRC_MSDP(up->flags) && + PIM_UPSTREAM_FLAG_TEST_SRC_MSDP(flags)) { + PIM_UPSTREAM_FLAG_SET_SRC_MSDP(up->flags); + pim_upstream_update_join_desired(up->pim, up); + } + up->flags |= flags; ++up->ref_count; - if (PIM_DEBUG_TRACE) + if (PIM_DEBUG_PIM_TRACE) zlog_debug("%s(%s): upstream %s ref count %d increment", - __PRETTY_FUNCTION__, name, up->sg_str, - up->ref_count); + __func__, name, up->sg_str, up->ref_count); } struct pim_upstream *pim_upstream_add(struct pim_instance *pim, @@ -848,24 +1061,53 @@ struct pim_upstream *pim_upstream_add(struct pim_instance *pim, up = pim_upstream_new(pim, sg, incoming, flags, ch); } - if (PIM_DEBUG_TRACE) { + if (PIM_DEBUG_PIM_TRACE) { if (up) { char buf[PREFIX2STR_BUFFER]; prefix2str(&up->rpf.rpf_addr, buf, sizeof(buf)); zlog_debug("%s(%s): %s, iif %s (%s) found: %d: ref_count: %d", - __PRETTY_FUNCTION__, name, + __func__, name, up->sg_str, buf, up->rpf.source_nexthop.interface ? up->rpf.source_nexthop.interface->name : "Unknown" , found, up->ref_count); } else - zlog_debug("%s(%s): (%s) failure to create", - __PRETTY_FUNCTION__, name, - pim_str_sg_dump(sg)); + zlog_debug("%s(%s): (%s) failure to create", __func__, + name, pim_str_sg_dump(sg)); } return up; } +/* + * Passed in up must be the upstream for ch. starch is NULL if no + * information + * This function is copied over from + * pim_upstream_evaluate_join_desired_interface but limited to + * parent (*,G)'s includes/joins. + */ +int pim_upstream_eval_inherit_if(struct pim_upstream *up, + struct pim_ifchannel *ch, + struct pim_ifchannel *starch) +{ + /* if there is an explicit prune for this interface we cannot + * add it to the OIL + */ + if (ch) { + if (PIM_IF_FLAG_TEST_S_G_RPT(ch->flags)) + return 0; + } + + /* Check if the OIF can be inherited fron the (*,G) entry + */ + if (starch) { + if (!pim_macro_ch_lost_assert(starch) + && pim_macro_chisin_joins_or_include(starch)) + return 1; + } + + return 0; +} + /* * Passed in up must be the upstream for ch. starch is NULL if no * information @@ -887,8 +1129,14 @@ int pim_upstream_evaluate_join_desired_interface(struct pim_upstream *up, * joins (*,G) */ if (starch) { + /* XXX: check on this with donald + * we are looking for PIM_IF_FLAG_MASK_S_G_RPT in + * upstream flags? + */ +#if 0 if (PIM_IF_FLAG_TEST_S_G_RPT(starch->upstream->flags)) return 0; +#endif if (!pim_macro_ch_lost_assert(starch) && pim_macro_chisin_joins_or_include(starch)) @@ -898,56 +1146,77 @@ int pim_upstream_evaluate_join_desired_interface(struct pim_upstream *up, return 0; } -/* - Evaluate JoinDesired(S,G): - - JoinDesired(S,G) is true if there is a downstream (S,G) interface I - in the set: - - inherited_olist(S,G) = - joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G) - - JoinDesired(S,G) may be affected by changes in the following: - - pim_ifp->primary_address - pim_ifp->pim_dr_addr - ch->ifassert_winner_metric - ch->ifassert_winner - ch->local_ifmembership - ch->ifjoin_state - ch->upstream->rpf.source_nexthop.mrib_metric_preference - ch->upstream->rpf.source_nexthop.mrib_route_metric - ch->upstream->rpf.source_nexthop.interface - - See also pim_upstream_update_join_desired() below. +/* Returns true if immediate OIL is empty and is used to evaluate + * JoinDesired. See pim_upstream_evaluate_join_desired. */ -int pim_upstream_evaluate_join_desired(struct pim_instance *pim, +static bool pim_upstream_empty_immediate_olist(struct pim_instance *pim, struct pim_upstream *up) { struct interface *ifp; - struct pim_ifchannel *ch, *starch; - struct pim_upstream *starup = up->parent; - int ret = 0; + struct pim_ifchannel *ch; FOR_ALL_INTERFACES (pim->vrf, ifp) { if (!ifp->info) continue; ch = pim_ifchannel_find(ifp, &up->sg); - - if (starup) - starch = pim_ifchannel_find(ifp, &starup->sg); - else - starch = NULL; - - if (!ch && !starch) + if (!ch) continue; - ret += pim_upstream_evaluate_join_desired_interface(up, ch, - starch); + /* If we have even one immediate OIF we can return with + * not-empty + */ + if (pim_upstream_evaluate_join_desired_interface(up, ch, + NULL /* starch */)) + return false; } /* scan iface channel list */ - return ret; /* false */ + /* immediate_oil is empty */ + return true; +} + + +static inline bool pim_upstream_is_msdp_peer_sa(struct pim_upstream *up) +{ + return PIM_UPSTREAM_FLAG_TEST_SRC_MSDP(up->flags); +} + +/* + * bool JoinDesired(*,G) { + * if (immediate_olist(*,G) != NULL) + * return TRUE + * else + * return FALSE + * } + * + * bool JoinDesired(S,G) { + * return( immediate_olist(S,G) != NULL + * OR ( KeepaliveTimer(S,G) is running + * AND inherited_olist(S,G) != NULL ) ) + * } + */ +bool pim_upstream_evaluate_join_desired(struct pim_instance *pim, + struct pim_upstream *up) +{ + bool empty_imm_oil; + bool empty_inh_oil; + + empty_imm_oil = pim_upstream_empty_immediate_olist(pim, up); + + /* (*,G) */ + if (up->sg.src.s_addr == INADDR_ANY) + return !empty_imm_oil; + + /* (S,G) */ + if (!empty_imm_oil) + return true; + empty_inh_oil = pim_upstream_empty_inherited_olist(up); + if (!empty_inh_oil && + (pim_upstream_is_kat_running(up) || + pim_upstream_is_msdp_peer_sa(up))) + return true; + + return false; } /* @@ -992,16 +1261,13 @@ void pim_upstream_update_join_desired(struct pim_instance *pim, void pim_upstream_rpf_genid_changed(struct pim_instance *pim, struct in_addr neigh_addr) { - struct listnode *up_node; - struct listnode *up_nextnode; struct pim_upstream *up; /* * Scan all (S,G) upstreams searching for RPF'(S,G)=neigh_addr */ - for (ALL_LIST_ELEMENTS(pim->upstream_list, up_node, up_nextnode, up)) { - - if (PIM_DEBUG_TRACE) { + frr_each (rb_pim_upstream, &pim->upstream_head, up) { + if (PIM_DEBUG_PIM_TRACE) { char neigh_str[INET_ADDRSTRLEN]; char rpf_addr_str[PREFIX_STRLEN]; pim_inet4_dump("", neigh_addr, neigh_str, @@ -1010,8 +1276,7 @@ void pim_upstream_rpf_genid_changed(struct pim_instance *pim, sizeof(rpf_addr_str)); zlog_debug( "%s: matching neigh=%s against upstream (S,G)=%s[%s] joined=%d rpf_addr=%s", - __PRETTY_FUNCTION__, neigh_str, up->sg_str, - pim->vrf->name, + __func__, neigh_str, up->sg_str, pim->vrf->name, up->join_state == PIM_UPSTREAM_JOINED, rpf_addr_str); } @@ -1110,7 +1375,7 @@ static void pim_upstream_fhr_kat_expiry(struct pim_instance *pim, if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags)) return; - if (PIM_DEBUG_TRACE) + if (PIM_DEBUG_PIM_TRACE) zlog_debug("kat expired on %s; clear fhr reg state", up->sg_str); @@ -1118,7 +1383,7 @@ static void pim_upstream_fhr_kat_expiry(struct pim_instance *pim, THREAD_OFF(up->t_rs_timer); /* remove regiface from the OIL if it is there*/ pim_channel_del_oif(up->channel_oil, pim->regiface, - PIM_OIF_FLAG_PROTO_PIM); + PIM_OIF_FLAG_PROTO_PIM, __func__); /* clear the register state */ up->reg_state = PIM_REG_NOINFO; PIM_UPSTREAM_FLAG_UNSET_FHR(up->flags); @@ -1130,7 +1395,7 @@ static void pim_upstream_fhr_kat_expiry(struct pim_instance *pim, static void pim_upstream_fhr_kat_start(struct pim_upstream *up) { if (pim_upstream_could_register(up)) { - if (PIM_DEBUG_TRACE) + if (PIM_DEBUG_PIM_TRACE) zlog_debug( "kat started on %s; set fhr reg state to joined", up->sg_str); @@ -1138,6 +1403,7 @@ static void pim_upstream_fhr_kat_start(struct pim_upstream *up) PIM_UPSTREAM_FLAG_SET_FHR(up->flags); if (up->reg_state == PIM_REG_NOINFO) pim_register_join(up); + pim_upstream_update_use_rpt(up, true /*update_mroute*/); } } @@ -1173,20 +1439,38 @@ struct pim_upstream *pim_upstream_keep_alive_timer_proc( /* source is no longer active - pull the SA from MSDP's cache */ pim_msdp_sa_local_del(pim, &up->sg); + /* JoinDesired can change when KAT is started or stopped */ + pim_upstream_update_join_desired(pim, up); + /* if entry was created because of activity we need to deref it */ if (PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up->flags)) { pim_upstream_fhr_kat_expiry(pim, up); - if (PIM_DEBUG_TRACE) + if (PIM_DEBUG_PIM_TRACE) zlog_debug( "kat expired on %s[%s]; remove stream reference", up->sg_str, pim->vrf->name); PIM_UPSTREAM_FLAG_UNSET_SRC_STREAM(up->flags); - up = pim_upstream_del(pim, up, __PRETTY_FUNCTION__); - } else if (PIM_UPSTREAM_FLAG_TEST_SRC_LHR(up->flags)) { + + /* Return if upstream entry got deleted.*/ + if (!pim_upstream_del(pim, up, __func__)) + return NULL; + } + if (PIM_UPSTREAM_FLAG_TEST_SRC_NOCACHE(up->flags)) { + PIM_UPSTREAM_FLAG_UNSET_SRC_NOCACHE(up->flags); + + if (!pim_upstream_del(pim, up, __func__)) + return NULL; + } + + /* upstream reference would have been added to track the local + * membership if it is LHR. We have to clear it when KAT expires. + * Otherwise would result in stale entry with uncleared ref count. + */ + if (PIM_UPSTREAM_FLAG_TEST_SRC_LHR(up->flags)) { struct pim_upstream *parent = up->parent; PIM_UPSTREAM_FLAG_UNSET_SRC_LHR(up->flags); - up = pim_upstream_del(pim, up, __PRETTY_FUNCTION__); + up = pim_upstream_del(pim, up, __func__); if (parent) { pim_jp_agg_single_upstream_send(&parent->rpf, parent, @@ -1202,6 +1486,11 @@ static int pim_upstream_keep_alive_timer(struct thread *t) up = THREAD_ARG(t); + /* pull the stats and re-check */ + if (pim_upstream_sg_running_proc(up)) + /* kat was restarted because of new activity */ + return 0; + pim_upstream_keep_alive_timer_proc(up); return 0; } @@ -1209,7 +1498,7 @@ static int pim_upstream_keep_alive_timer(struct thread *t) void pim_upstream_keep_alive_timer_start(struct pim_upstream *up, uint32_t time) { if (!PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up->flags)) { - if (PIM_DEBUG_TRACE) + if (PIM_DEBUG_PIM_TRACE) zlog_debug("kat start on %s with no stream reference", up->sg_str); } @@ -1220,6 +1509,8 @@ void pim_upstream_keep_alive_timer_start(struct pim_upstream *up, uint32_t time) /* any time keepalive is started against a SG we will have to * re-evaluate our active source database */ pim_msdp_sa_local_update(up); + /* JoinDesired can change when KAT is started or stopped */ + pim_upstream_update_join_desired(up->pim, up); } /* MSDP on RP needs to know if a source is registerable to this RP */ @@ -1269,7 +1560,7 @@ void pim_upstream_msdp_reg_timer_start(struct pim_upstream *up) * SwitchToSptDesired(S,G) return true once a single packet has been * received for the source and group. */ -int pim_upstream_switch_to_spt_desired(struct pim_instance *pim, +int pim_upstream_switch_to_spt_desired_on_rp(struct pim_instance *pim, struct prefix_sg *sg) { if (I_am_RP(pim, sg->grp)) @@ -1295,14 +1586,14 @@ int pim_upstream_is_sg_rpt(struct pim_upstream *up) * void * Update_SPTbit(S,G,iif) { * if ( iif == RPF_interface(S) - * AND JoinDesired(S,G) == TRUE - * AND ( DirectlyConnected(S) == TRUE + * AND JoinDesired(S,G) == true + * AND ( DirectlyConnected(S) == true * OR RPF_interface(S) != RPF_interface(RP(G)) * OR inherited_olist(S,G,rpt) == NULL * OR ( ( RPF'(S,G) == RPF'(*,G) ) AND * ( RPF'(S,G) != NULL ) ) * OR ( I_Am_Assert_Loser(S,G,iif) ) { - * Set SPTbit(S,G) to TRUE + * Set SPTbit(S,G) to true * } * } */ @@ -1313,28 +1604,28 @@ void pim_upstream_set_sptbit(struct pim_upstream *up, // iif == RPF_interfvace(S) if (up->rpf.source_nexthop.interface != incoming) { - if (PIM_DEBUG_TRACE) + if (PIM_DEBUG_PIM_TRACE) zlog_debug( "%s: Incoming Interface: %s is different than RPF_interface(S) %s", - __PRETTY_FUNCTION__, incoming->name, + __func__, incoming->name, up->rpf.source_nexthop.interface->name); return; } - // AND JoinDesired(S,G) == TRUE + // AND JoinDesired(S,G) == true if (!pim_upstream_evaluate_join_desired(up->channel_oil->pim, up)) { - if (PIM_DEBUG_TRACE) - zlog_debug("%s: %s Join is not Desired", - __PRETTY_FUNCTION__, up->sg_str); + if (PIM_DEBUG_PIM_TRACE) + zlog_debug("%s: %s Join is not Desired", __func__, + up->sg_str); return; } - // DirectlyConnected(S) == TRUE + // DirectlyConnected(S) == true if (pim_if_connected_to_source(up->rpf.source_nexthop.interface, up->sg.src)) { - if (PIM_DEBUG_TRACE) + if (PIM_DEBUG_PIM_TRACE) zlog_debug("%s: %s is directly connected to the source", - __PRETTY_FUNCTION__, up->sg_str); + __func__, up->sg_str); up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE; return; } @@ -1345,10 +1636,10 @@ void pim_upstream_set_sptbit(struct pim_upstream *up, .interface != starup->rpf.source_nexthop.interface) { struct pim_upstream *starup = up->parent; - if (PIM_DEBUG_TRACE) + if (PIM_DEBUG_PIM_TRACE) zlog_debug( "%s: %s RPF_interface(S) != RPF_interface(RP(G))", - __PRETTY_FUNCTION__, up->sg_str); + __func__, up->sg_str); up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE; pim_jp_agg_single_upstream_send(&starup->rpf, starup, true); @@ -1358,9 +1649,9 @@ void pim_upstream_set_sptbit(struct pim_upstream *up, // OR inherited_olist(S,G,rpt) == NULL if (pim_upstream_is_sg_rpt(up) && pim_upstream_empty_inherited_olist(up)) { - if (PIM_DEBUG_TRACE) + if (PIM_DEBUG_PIM_TRACE) zlog_debug("%s: %s OR inherited_olist(S,G,rpt) == NULL", - __PRETTY_FUNCTION__, up->sg_str); + __func__, up->sg_str); up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE; return; } @@ -1368,9 +1659,9 @@ void pim_upstream_set_sptbit(struct pim_upstream *up, // OR ( ( RPF'(S,G) == RPF'(*,G) ) AND // ( RPF'(S,G) != NULL ) ) if (up->parent && pim_rpf_is_same(&up->rpf, &up->parent->rpf)) { - if (PIM_DEBUG_TRACE) + if (PIM_DEBUG_PIM_TRACE) zlog_debug("%s: %s RPF'(S,G) is the same as RPF'(*,G)", - __PRETTY_FUNCTION__, up->sg_str); + __func__, up->sg_str); up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE; return; } @@ -1383,31 +1674,30 @@ const char *pim_upstream_state2str(enum pim_upstream_state join_state) switch (join_state) { case PIM_UPSTREAM_NOTJOINED: return "NotJoined"; - break; case PIM_UPSTREAM_JOINED: return "Joined"; - break; } return "Unknown"; } -const char *pim_reg_state2str(enum pim_reg_state reg_state, char *state_str) +const char *pim_reg_state2str(enum pim_reg_state reg_state, char *state_str, + size_t state_str_len) { switch (reg_state) { case PIM_REG_NOINFO: - strcpy(state_str, "RegNoInfo"); + strlcpy(state_str, "RegNoInfo", state_str_len); break; case PIM_REG_JOIN: - strcpy(state_str, "RegJoined"); + strlcpy(state_str, "RegJoined", state_str_len); break; case PIM_REG_JOIN_PENDING: - strcpy(state_str, "RegJoinPend"); + strlcpy(state_str, "RegJoinPend", state_str_len); break; case PIM_REG_PRUNE: - strcpy(state_str, "RegPrune"); + strlcpy(state_str, "RegPrune", state_str_len); break; default: - strcpy(state_str, "RegUnknown"); + strlcpy(state_str, "RegUnknown", state_str_len); } return state_str; } @@ -1420,36 +1710,40 @@ static int pim_upstream_register_stop_timer(struct thread *t) up = THREAD_ARG(t); pim = up->channel_oil->pim; - if (PIM_DEBUG_TRACE) { + if (PIM_DEBUG_PIM_TRACE) { char state_str[PIM_REG_STATE_STR_LEN]; zlog_debug("%s: (S,G)=%s[%s] upstream register stop timer %s", - __PRETTY_FUNCTION__, up->sg_str, pim->vrf->name, - pim_reg_state2str(up->reg_state, state_str)); + __func__, up->sg_str, pim->vrf->name, + pim_reg_state2str(up->reg_state, state_str, + sizeof(state_str))); } switch (up->reg_state) { case PIM_REG_JOIN_PENDING: up->reg_state = PIM_REG_JOIN; pim_channel_add_oif(up->channel_oil, pim->regiface, - PIM_OIF_FLAG_PROTO_PIM); - pim_vxlan_update_sg_reg_state(pim, up, TRUE /*reg_join*/); + PIM_OIF_FLAG_PROTO_PIM, + __func__); + pim_vxlan_update_sg_reg_state(pim, up, true /*reg_join*/); break; case PIM_REG_JOIN: break; case PIM_REG_PRUNE: + /* This is equalent to Couldreg -> False */ if (!up->rpf.source_nexthop.interface) { - if (PIM_DEBUG_TRACE) + if (PIM_DEBUG_PIM_TRACE) zlog_debug("%s: up %s RPF is not present", - __PRETTY_FUNCTION__, up->sg_str); + __func__, up->sg_str); + up->reg_state = PIM_REG_NOINFO; return 0; } pim_ifp = up->rpf.source_nexthop.interface->info; if (!pim_ifp) { - if (PIM_DEBUG_TRACE) + if (PIM_DEBUG_PIM_TRACE) zlog_debug( "%s: Interface: %s is not configured for pim", - __PRETTY_FUNCTION__, + __func__, up->rpf.source_nexthop.interface->name); return 0; } @@ -1459,10 +1753,10 @@ static int pim_upstream_register_stop_timer(struct thread *t) if (((up->channel_oil->cc.lastused / 100) > pim->keep_alive_time) && (I_am_RP(pim_ifp->pim, up->sg.grp))) { - if (PIM_DEBUG_TRACE) + if (PIM_DEBUG_PIM_TRACE) zlog_debug( "%s: Stop sending the register, because I am the RP and we haven't seen a packet in a while", - __PRETTY_FUNCTION__); + __func__); return 0; } pim_null_register_send(up); @@ -1484,15 +1778,15 @@ void pim_upstream_start_register_stop_timer(struct pim_upstream *up, if (!null_register) { uint32_t lower = (0.5 * PIM_REGISTER_SUPPRESSION_PERIOD); uint32_t upper = (1.5 * PIM_REGISTER_SUPPRESSION_PERIOD); - time = lower + (random() % (upper - lower + 1)) + time = lower + (frr_weak_random() % (upper - lower + 1)) - PIM_REGISTER_PROBE_PERIOD; } else time = PIM_REGISTER_PROBE_PERIOD; - if (PIM_DEBUG_TRACE) { + if (PIM_DEBUG_PIM_TRACE) { zlog_debug( "%s: (S,G)=%s Starting upstream register stop timer %d", - __PRETTY_FUNCTION__, up->sg_str, time); + __func__, up->sg_str, time); } thread_add_timer(router->master, pim_upstream_register_stop_timer, up, time, &up->t_rs_timer); @@ -1502,23 +1796,17 @@ int pim_upstream_inherited_olist_decide(struct pim_instance *pim, struct pim_upstream *up) { struct interface *ifp; - struct pim_interface *pim_ifp = NULL; struct pim_ifchannel *ch, *starch; struct pim_upstream *starup = up->parent; int output_intf = 0; - if (up->rpf.source_nexthop.interface) - pim_ifp = up->rpf.source_nexthop.interface->info; - else { - if (PIM_DEBUG_TRACE) - zlog_debug("%s: up %s RPF is not present", - __PRETTY_FUNCTION__, up->sg_str); - } - if (pim_ifp && !up->channel_oil) - up->channel_oil = pim_channel_oil_add( - pim, &up->sg, pim_ifp->mroute_vif_index); + if (!up->rpf.source_nexthop.interface) + if (PIM_DEBUG_PIM_TRACE) + zlog_debug("%s: up %s RPF is not present", __func__, + up->sg_str); FOR_ALL_INTERFACES (pim->vrf, ifp) { + struct pim_interface *pim_ifp; if (!ifp->info) continue; @@ -1532,14 +1820,27 @@ int pim_upstream_inherited_olist_decide(struct pim_instance *pim, if (!ch && !starch) continue; + pim_ifp = ifp->info; + if (PIM_I_am_DualActive(pim_ifp) + && PIM_UPSTREAM_FLAG_TEST_MLAG_INTERFACE(up->flags) + && (PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(up->flags) + || !PIM_UPSTREAM_FLAG_TEST_MLAG_PEER(up->flags))) + continue; if (pim_upstream_evaluate_join_desired_interface(up, ch, starch)) { - int flag = PIM_OIF_FLAG_PROTO_PIM; + int flag = 0; if (!ch) flag = PIM_OIF_FLAG_PROTO_STAR; + else { + if (PIM_IF_FLAG_TEST_PROTO_IGMP(ch->flags)) + flag = PIM_OIF_FLAG_PROTO_IGMP; + if (PIM_IF_FLAG_TEST_PROTO_PIM(ch->flags)) + flag |= PIM_OIF_FLAG_PROTO_PIM; + } - pim_channel_add_oif(up->channel_oil, ifp, flag); + pim_channel_add_oif(up->channel_oil, ifp, flag, + __func__); output_intf++; } } @@ -1574,9 +1875,9 @@ int pim_upstream_inherited_olist(struct pim_instance *pim, * switch on a stick so turn on forwarding to just accept the * incoming packets so we don't bother the other stuff! */ - if (output_intf) - pim_upstream_switch(pim, up, PIM_UPSTREAM_JOINED); - else + pim_upstream_update_join_desired(pim, up); + + if (!output_intf) forward_on(up); return output_intf; @@ -1595,35 +1896,45 @@ int pim_upstream_empty_inherited_olist(struct pim_upstream *up) */ void pim_upstream_find_new_rpf(struct pim_instance *pim) { - struct listnode *up_node; - struct listnode *up_nextnode; struct pim_upstream *up; + struct pim_rpf old; + enum pim_rpf_result rpf_result; /* * Scan all (S,G) upstreams searching for RPF'(S,G)=neigh_addr */ - for (ALL_LIST_ELEMENTS(pim->upstream_list, up_node, up_nextnode, up)) { + frr_each (rb_pim_upstream, &pim->upstream_head, up) { if (up->upstream_addr.s_addr == INADDR_ANY) { - if (PIM_DEBUG_TRACE) + if (PIM_DEBUG_PIM_TRACE) zlog_debug( - "%s: RP not configured for Upstream %s", - __PRETTY_FUNCTION__, up->sg_str); + "%s: RP not configured for Upstream %s", + __func__, up->sg_str); continue; } if (pim_rpf_addr_is_inaddr_any(&up->rpf)) { - if (PIM_DEBUG_TRACE) + if (PIM_DEBUG_PIM_TRACE) zlog_debug( "%s: Upstream %s without a path to send join, checking", - __PRETTY_FUNCTION__, up->sg_str); - pim_rpf_update(pim, up, NULL, 1); + __func__, up->sg_str); + old.source_nexthop.interface = + up->rpf.source_nexthop.interface; + rpf_result = pim_rpf_update(pim, up, &old, __func__); + if (rpf_result == PIM_RPF_CHANGED || + (rpf_result == PIM_RPF_FAILURE && + old.source_nexthop.interface)) + pim_zebra_upstream_rpf_changed(pim, up, &old); + /* update kernel multicast forwarding cache (MFC) */ + pim_upstream_mroute_iif_update(up->channel_oil, + __func__); } } + pim_zebra_update_all_interfaces(pim); } -unsigned int pim_upstream_hash_key(void *arg) +unsigned int pim_upstream_hash_key(const void *arg) { - struct pim_upstream *up = (struct pim_upstream *)arg; + const struct pim_upstream *up = arg; return jhash_2words(up->sg.src.s_addr, up->sg.grp.s_addr, 0); } @@ -1632,18 +1943,11 @@ void pim_upstream_terminate(struct pim_instance *pim) { struct pim_upstream *up; - if (pim->upstream_list) { - while (pim->upstream_list->count) { - up = listnode_head(pim->upstream_list); - pim_upstream_del(pim, up, __PRETTY_FUNCTION__); - } - - list_delete(&pim->upstream_list); + while ((up = rb_pim_upstream_first(&pim->upstream_head))) { + pim_upstream_del(pim, up, __func__); } - if (pim->upstream_hash) - hash_free(pim->upstream_hash); - pim->upstream_hash = NULL; + rb_pim_upstream_fini(&pim->upstream_head); if (pim->upstream_sg_wheel) wheel_delete(pim->upstream_sg_wheel); @@ -1665,7 +1969,7 @@ bool pim_upstream_equal(const void *arg1, const void *arg2) /* rfc4601:section-4.2:"Data Packet Forwarding Rules" defines * the cases where kat has to be restarted on rxing traffic - * - * if( DirectlyConnected(S) == TRUE AND iif == RPF_interface(S) ) { + * if( DirectlyConnected(S) == true AND iif == RPF_interface(S) ) { * set KeepaliveTimer(S,G) to Keepalive_Period * # Note: a register state transition or UpstreamJPState(S,G) * # transition may happen as a result of restarting @@ -1678,125 +1982,138 @@ bool pim_upstream_equal(const void *arg1, const void *arg2) */ static bool pim_upstream_kat_start_ok(struct pim_upstream *up) { - struct pim_instance *pim = up->channel_oil->pim; + struct channel_oil *c_oil = up->channel_oil; + struct interface *ifp = up->rpf.source_nexthop.interface; + struct pim_interface *pim_ifp; - /* "iif == RPF_interface(S)" check has to be done by the kernel or hw - * so we will skip that here */ - if (up->rpf.source_nexthop.interface && - pim_if_connected_to_source(up->rpf.source_nexthop.interface, + /* "iif == RPF_interface(S)" check is not easy to do as the info + * we get from the kernel/ASIC is really a "lookup/key hit". + * So we will do an approximate check here to avoid starting KAT + * because of (S,G,rpt) forwarding on a non-LHR. + */ + if (!ifp) + return false; + + pim_ifp = ifp->info; + if (pim_ifp->mroute_vif_index != c_oil->oil.mfcc_parent) + return false; + + if (pim_if_connected_to_source(up->rpf.source_nexthop.interface, up->sg.src)) { return true; } if ((up->join_state == PIM_UPSTREAM_JOINED) - && !pim_upstream_empty_inherited_olist(up)) { - /* XXX: I have added this RP check just for 3.2 and it's a - * digression from - * what rfc-4601 says. Till now we were only running KAT on FHR - * and RP and - * there is some angst around making the change to run it all - * routers that - * maintain the (S, G) state. This is tracked via CM-13601 and - * MUST be - * removed to handle spt turn-arounds correctly in a 3-tier clos - */ - if (I_am_RP(pim, up->sg.grp)) - return true; + && !pim_upstream_empty_inherited_olist(up)) { + return true; } return false; } -/* - * Code to check and see if we've received packets on a S,G mroute - * and if so to set the SPT bit appropriately - */ -static void pim_upstream_sg_running(void *arg) +static bool pim_upstream_sg_running_proc(struct pim_upstream *up) { - struct pim_upstream *up = (struct pim_upstream *)arg; - struct pim_instance *pim = up->channel_oil->pim; + bool rv = false; + struct pim_instance *pim = up->pim; - // No packet can have arrived here if this is the case - if (!up->channel_oil->installed) { - if (PIM_DEBUG_TRACE) - zlog_debug("%s: %s%s is not installed in mroute", - __PRETTY_FUNCTION__, up->sg_str, - pim->vrf->name); - return; - } + if (!up->channel_oil->installed) + return rv; - /* - * This is a bit of a hack - * We've noted that we should rescan but - * we've missed the window for doing so in - * pim_zebra.c for some reason. I am - * only doing this at this point in time - * to get us up and working for the moment - */ - if (up->channel_oil->oil_inherited_rescan) { - if (PIM_DEBUG_TRACE) - zlog_debug( - "%s: Handling unscanned inherited_olist for %s[%s]", - __PRETTY_FUNCTION__, up->sg_str, - pim->vrf->name); - pim_upstream_inherited_olist_decide(pim, up); - up->channel_oil->oil_inherited_rescan = 0; - } pim_mroute_update_counters(up->channel_oil); // Have we seen packets? if ((up->channel_oil->cc.oldpktcnt >= up->channel_oil->cc.pktcnt) && (up->channel_oil->cc.lastused / 100 > 30)) { - if (PIM_DEBUG_TRACE) { + if (PIM_DEBUG_PIM_TRACE) { zlog_debug( "%s[%s]: %s old packet count is equal or lastused is greater than 30, (%ld,%ld,%lld)", - __PRETTY_FUNCTION__, up->sg_str, pim->vrf->name, + __func__, up->sg_str, pim->vrf->name, up->channel_oil->cc.oldpktcnt, up->channel_oil->cc.pktcnt, up->channel_oil->cc.lastused / 100); } - return; + return rv; } if (pim_upstream_kat_start_ok(up)) { /* Add a source reference to the stream if * one doesn't already exist */ if (!PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up->flags)) { - if (PIM_DEBUG_TRACE) + if (PIM_DEBUG_PIM_TRACE) zlog_debug( "source reference created on kat restart %s[%s]", up->sg_str, pim->vrf->name); pim_upstream_ref(up, PIM_UPSTREAM_FLAG_MASK_SRC_STREAM, - __PRETTY_FUNCTION__); + __func__); PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up->flags); pim_upstream_fhr_kat_start(up); } pim_upstream_keep_alive_timer_start(up, pim->keep_alive_time); - } else if (PIM_UPSTREAM_FLAG_TEST_SRC_LHR(up->flags)) + rv = true; + } else if (PIM_UPSTREAM_FLAG_TEST_SRC_LHR(up->flags)) { pim_upstream_keep_alive_timer_start(up, pim->keep_alive_time); + rv = true; + } if ((up->sptbit != PIM_UPSTREAM_SPTBIT_TRUE) && (up->rpf.source_nexthop.interface)) { pim_upstream_set_sptbit(up, up->rpf.source_nexthop.interface); } - return; + + return rv; +} + +/* + * Code to check and see if we've received packets on a S,G mroute + * and if so to set the SPT bit appropriately + */ +static void pim_upstream_sg_running(void *arg) +{ + struct pim_upstream *up = (struct pim_upstream *)arg; + struct pim_instance *pim = up->channel_oil->pim; + + // No packet can have arrived here if this is the case + if (!up->channel_oil->installed) { + if (PIM_DEBUG_TRACE) + zlog_debug("%s: %s%s is not installed in mroute", + __func__, up->sg_str, pim->vrf->name); + return; + } + + /* + * This is a bit of a hack + * We've noted that we should rescan but + * we've missed the window for doing so in + * pim_zebra.c for some reason. I am + * only doing this at this point in time + * to get us up and working for the moment + */ + if (up->channel_oil->oil_inherited_rescan) { + if (PIM_DEBUG_TRACE) + zlog_debug( + "%s: Handling unscanned inherited_olist for %s[%s]", + __func__, up->sg_str, pim->vrf->name); + pim_upstream_inherited_olist_decide(pim, up); + up->channel_oil->oil_inherited_rescan = 0; + } + + pim_upstream_sg_running_proc(up); } void pim_upstream_add_lhr_star_pimreg(struct pim_instance *pim) { struct pim_upstream *up; - struct listnode *node; - for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, node, up)) { + frr_each (rb_pim_upstream, &pim->upstream_head, up) { if (up->sg.src.s_addr != INADDR_ANY) continue; - if (!PIM_UPSTREAM_FLAG_TEST_SRC_IGMP(up->flags)) + if (!PIM_UPSTREAM_FLAG_TEST_CAN_BE_LHR(up->flags)) continue; pim_channel_add_oif(up->channel_oil, pim->regiface, - PIM_OIF_FLAG_PROTO_IGMP); + PIM_OIF_FLAG_PROTO_IGMP, __func__); } } @@ -1826,7 +2143,6 @@ void pim_upstream_remove_lhr_star_pimreg(struct pim_instance *pim, const char *nlist) { struct pim_upstream *up; - struct listnode *node; struct prefix_list *np; struct prefix g; enum prefix_list_type apply_new; @@ -1836,26 +2152,27 @@ void pim_upstream_remove_lhr_star_pimreg(struct pim_instance *pim, g.family = AF_INET; g.prefixlen = IPV4_MAX_PREFIXLEN; - for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, node, up)) { + frr_each (rb_pim_upstream, &pim->upstream_head, up) { if (up->sg.src.s_addr != INADDR_ANY) continue; - if (!PIM_UPSTREAM_FLAG_TEST_SRC_IGMP(up->flags)) + if (!PIM_UPSTREAM_FLAG_TEST_CAN_BE_LHR(up->flags)) continue; if (!nlist) { pim_channel_del_oif(up->channel_oil, pim->regiface, - PIM_OIF_FLAG_PROTO_IGMP); + PIM_OIF_FLAG_PROTO_IGMP, __func__); continue; } g.u.prefix4 = up->sg.grp; apply_new = prefix_list_apply(np, &g); if (apply_new == PREFIX_DENY) pim_channel_add_oif(up->channel_oil, pim->regiface, - PIM_OIF_FLAG_PROTO_IGMP); + PIM_OIF_FLAG_PROTO_IGMP, + __func__); else pim_channel_del_oif(up->channel_oil, pim->regiface, - PIM_OIF_FLAG_PROTO_IGMP); + PIM_OIF_FLAG_PROTO_IGMP, __func__); } } @@ -1863,17 +2180,10 @@ void pim_upstream_init(struct pim_instance *pim) { char name[64]; - snprintf(name, 64, "PIM %s Timer Wheel", - pim->vrf->name); + snprintf(name, sizeof(name), "PIM %s Timer Wheel", pim->vrf->name); pim->upstream_sg_wheel = wheel_init(router->master, 31000, 100, pim_upstream_hash_key, pim_upstream_sg_running, name); - snprintf(name, 64, "PIM %s Upstream Hash", - pim->vrf->name); - pim->upstream_hash = hash_create_size(8192, pim_upstream_hash_key, - pim_upstream_equal, name); - - pim->upstream_list = list_new(); - pim->upstream_list->cmp = pim_upstream_compare; + rb_pim_upstream_init(&pim->upstream_head); } diff --git a/pimd/pim_upstream.h b/pimd/pim_upstream.h index 13a3dcdf8c..8030835fb2 100644 --- a/pimd/pim_upstream.h +++ b/pimd/pim_upstream.h @@ -74,6 +74,27 @@ * blackholing the traffic pulled down to the LHR. */ #define PIM_UPSTREAM_FLAG_MASK_MLAG_NON_DF (1 << 17) +/* MLAG mroute rxed from the peer MLAG switch */ +#define PIM_UPSTREAM_FLAG_MASK_MLAG_PEER (1 << 18) +/* + * We are creating a non-joined upstream data structure + * for this S,G as that we want to have a channel oil + * associated with an upstream + */ +#define PIM_UPSTREAM_FLAG_MASK_SRC_NOCACHE (1 << 19) +/* By default as SG entry will use the SPT for forwarding traffic + * unless it was setup as a result of a Prune(S,G,rpt) from a + * downstream router and has JoinDesired(S,G) as False. + * This flag is only relevant for (S,G) entries. + */ +#define PIM_UPSTREAM_FLAG_MASK_USE_RPT (1 << 20) +/* PIM Syncs upstream entries to peer Nodes via MLAG in 2 cases. + * one is to support plain PIM Redundancy and another one is to support + * PIM REdundancy. + */ +#define PIM_UPSTREAM_FLAG_MASK_MLAG_INTERFACE (1 << 21) + + #define PIM_UPSTREAM_FLAG_ALL 0xFFFFFFFF #define PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED) @@ -95,6 +116,11 @@ #define PIM_UPSTREAM_FLAG_TEST_SRC_VXLAN(flags) ((flags) & (PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_ORIG | PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_TERM)) #define PIM_UPSTREAM_FLAG_TEST_MLAG_VXLAN(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_MLAG_VXLAN) #define PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_MLAG_NON_DF) +#define PIM_UPSTREAM_FLAG_TEST_MLAG_PEER(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_MLAG_PEER) +#define PIM_UPSTREAM_FLAG_TEST_SRC_NOCACHE(flags) ((flags) &PIM_UPSTREAM_FLAG_MASK_SRC_NOCACHE) +#define PIM_UPSTREAM_FLAG_TEST_USE_RPT(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_USE_RPT) +#define PIM_UPSTREAM_FLAG_TEST_CAN_BE_LHR(flags) ((flags) & (PIM_UPSTREAM_FLAG_MASK_SRC_IGMP | PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_TERM)) +#define PIM_UPSTREAM_FLAG_TEST_MLAG_INTERFACE(flags) ((flags)&PIM_UPSTREAM_FLAG_MASK_MLAG_INTERFACE) #define PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED) #define PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED_UPDATED(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED) @@ -114,6 +140,9 @@ #define PIM_UPSTREAM_FLAG_SET_SRC_VXLAN_TERM(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_TERM) #define PIM_UPSTREAM_FLAG_SET_MLAG_VXLAN(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_MLAG_VXLAN) #define PIM_UPSTREAM_FLAG_SET_MLAG_NON_DF(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_MLAG_NON_DF) +#define PIM_UPSTREAM_FLAG_SET_MLAG_PEER(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_MLAG_PEER) +#define PIM_UPSTREAM_FLAG_SET_USE_RPT(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_USE_RPT) +#define PIM_UPSTREAM_FLAG_SET_MLAG_INTERFACE(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_MLAG_INTERFACE) #define PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED) #define PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED_UPDATED(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED) @@ -133,6 +162,16 @@ #define PIM_UPSTREAM_FLAG_UNSET_SRC_VXLAN_TERM(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_TERM) #define PIM_UPSTREAM_FLAG_UNSET_MLAG_VXLAN(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_MLAG_VXLAN) #define PIM_UPSTREAM_FLAG_UNSET_MLAG_NON_DF(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_MLAG_NON_DF) +#define PIM_UPSTREAM_FLAG_UNSET_MLAG_PEER(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_MLAG_PEER) +#define PIM_UPSTREAM_FLAG_UNSET_SRC_NOCACHE(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_SRC_NOCACHE) +#define PIM_UPSTREAM_FLAG_UNSET_USE_RPT(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_USE_RPT) +#define PIM_UPSTREAM_FLAG_UNSET_MLAG_INTERFACE(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_MLAG_INTERFACE) + +/* The RPF cost is incremented by 10 if the RPF interface is the peerlink-rif. + * This is used to force the MLAG switch with the lowest cost to the RPF + * to become the MLAG DF. + */ +#define PIM_UPSTREAM_MLAG_PEERLINK_PLUS_METRIC 10 enum pim_upstream_state { PIM_UPSTREAM_NOTJOINED, @@ -151,6 +190,14 @@ enum pim_upstream_sptbit { PIM_UPSTREAM_SPTBIT_TRUE }; +struct pim_up_mlag { + /* MRIB.metric(S) from the peer switch. This is used for DF election + * and switch with the lowest cost wins. + */ + uint32_t peer_mrib_metric; +}; + +PREDECL_RBTREE_UNIQ(rb_pim_upstream); /* Upstream (S,G) channel in Joined state (S,G) in the "Not Joined" state is not represented @@ -179,6 +226,8 @@ enum pim_upstream_sptbit { */ struct pim_upstream { + struct pim_instance *pim; + struct rb_pim_upstream_item upstream_rb; struct pim_upstream *parent; struct in_addr upstream_addr; /* Who we are talking to */ struct in_addr upstream_register; /*Who we received a register from*/ @@ -188,6 +237,8 @@ struct pim_upstream { struct channel_oil *channel_oil; struct list *sources; struct list *ifchannels; + /* Counter for Dual active ifchannels*/ + uint32_t dualactive_ifchannel_count; enum pim_upstream_state join_state; enum pim_reg_state reg_state; @@ -197,6 +248,8 @@ struct pim_upstream { struct pim_rpf rpf; + struct pim_up_mlag mlag; + struct thread *t_join_timer; /* @@ -223,6 +276,20 @@ struct pim_upstream { int64_t state_transition; /* Record current state uptime */ }; +static inline bool pim_upstream_is_kat_running(struct pim_upstream *up) +{ + return (up->t_ka_timer != NULL); +} + +static inline bool pim_up_mlag_is_local(struct pim_upstream *up) +{ + /* XXX: extend this to also return true if the channel-oil has + * any AA devices + */ + return (up->flags & (PIM_UPSTREAM_FLAG_MASK_MLAG_VXLAN + | PIM_UPSTREAM_FLAG_MASK_MLAG_INTERFACE)); +} + struct pim_upstream *pim_upstream_find(struct pim_instance *pim, struct prefix_sg *sg); struct pim_upstream *pim_upstream_find_or_add(struct prefix_sg *sg, @@ -233,16 +300,20 @@ struct pim_upstream *pim_upstream_add(struct pim_instance *pim, struct interface *ifp, int flags, const char *name, struct pim_ifchannel *ch); -void pim_upstream_ref(struct pim_upstream *up, int flags, const char *name); +void pim_upstream_ref(struct pim_upstream *up, + int flags, const char *name); struct pim_upstream *pim_upstream_del(struct pim_instance *pim, struct pim_upstream *up, const char *name); -int pim_upstream_evaluate_join_desired(struct pim_instance *pim, - struct pim_upstream *up); +bool pim_upstream_evaluate_join_desired(struct pim_instance *pim, + struct pim_upstream *up); int pim_upstream_evaluate_join_desired_interface(struct pim_upstream *up, struct pim_ifchannel *ch, struct pim_ifchannel *starch); +int pim_upstream_eval_inherit_if(struct pim_upstream *up, + struct pim_ifchannel *ch, + struct pim_ifchannel *starch); void pim_upstream_update_join_desired(struct pim_instance *pim, struct pim_upstream *up); @@ -265,9 +336,9 @@ void pim_upstream_update_my_assert_metric(struct pim_upstream *up); void pim_upstream_keep_alive_timer_start(struct pim_upstream *up, uint32_t time); -int pim_upstream_switch_to_spt_desired(struct pim_instance *pim, +int pim_upstream_switch_to_spt_desired_on_rp(struct pim_instance *pim, struct prefix_sg *sg); -#define SwitchToSptDesired(pim, sg) pim_upstream_switch_to_spt_desired (pim, sg) +#define SwitchToSptDesiredOnRp(pim, sg) pim_upstream_switch_to_spt_desired_on_rp (pim, sg) int pim_upstream_is_sg_rpt(struct pim_upstream *up); void pim_upstream_set_sptbit(struct pim_upstream *up, @@ -283,7 +354,8 @@ void pim_upstream_switch(struct pim_instance *pim, struct pim_upstream *up, const char *pim_upstream_state2str(enum pim_upstream_state join_state); #define PIM_REG_STATE_STR_LEN 12 -const char *pim_reg_state2str(enum pim_reg_state state, char *state_str); +const char *pim_reg_state2str(enum pim_reg_state state, char *state_str, + size_t state_str_len); int pim_upstream_inherited_olist_decide(struct pim_instance *pim, struct pim_upstream *up); @@ -298,7 +370,11 @@ void pim_upstream_init(struct pim_instance *pim); void pim_upstream_terminate(struct pim_instance *pim); void join_timer_start(struct pim_upstream *up); -int pim_upstream_compare(void *arg1, void *arg2); +int pim_upstream_compare(const struct pim_upstream *up1, + const struct pim_upstream *up2); +DECLARE_RBTREE_UNIQ(rb_pim_upstream, struct pim_upstream, upstream_rb, + pim_upstream_compare) + void pim_upstream_register_reevaluate(struct pim_instance *pim); void pim_upstream_add_lhr_star_pimreg(struct pim_instance *pim); @@ -308,10 +384,16 @@ void pim_upstream_remove_lhr_star_pimreg(struct pim_instance *pim, void pim_upstream_spt_prefix_list_update(struct pim_instance *pim, struct prefix_list *pl); -unsigned int pim_upstream_hash_key(void *arg); +unsigned int pim_upstream_hash_key(const void *arg); bool pim_upstream_equal(const void *arg1, const void *arg2); struct pim_upstream *pim_upstream_keep_alive_timer_proc( struct pim_upstream *up); void pim_upstream_fill_static_iif(struct pim_upstream *up, struct interface *incoming); +void pim_upstream_update_use_rpt(struct pim_upstream *up, + bool update_mroute); +uint32_t pim_up_mlag_local_cost(struct pim_upstream *up); +uint32_t pim_up_mlag_peer_cost(struct pim_upstream *up); +void pim_upstream_reeval_use_rpt(struct pim_instance *pim); +int pim_upstream_could_register(struct pim_upstream *up); #endif /* PIM_UPSTREAM_H */ diff --git a/pimd/pim_vty.c b/pimd/pim_vty.c index 2654ebc588..1f2ca11db3 100644 --- a/pimd/pim_vty.c +++ b/pimd/pim_vty.c @@ -39,6 +39,7 @@ #include "pim_msdp.h" #include "pim_ssm.h" #include "pim_bfd.h" +#include "pim_bsm.h" #include "pim_vxlan.h" int pim_debug_config_write(struct vty *vty) @@ -69,10 +70,6 @@ int pim_debug_config_write(struct vty *vty) vty_out(vty, "debug igmp trace\n"); ++writes; } - if (PIM_DEBUG_IGMP_TRACE_DETAIL) { - vty_out(vty, "debug igmp trace detail\n"); - ++writes; - } if (PIM_DEBUG_MROUTE) { vty_out(vty, "debug mroute\n"); @@ -84,7 +81,7 @@ int pim_debug_config_write(struct vty *vty) ++writes; } - if (PIM_DEBUG_MROUTE_DETAIL) { + if (PIM_DEBUG_MROUTE_DETAIL_ONLY) { vty_out(vty, "debug mroute detail\n"); ++writes; } @@ -110,7 +107,7 @@ int pim_debug_config_write(struct vty *vty) vty_out(vty, "debug pim trace\n"); ++writes; } - if (PIM_DEBUG_PIM_TRACE_DETAIL) { + if (PIM_DEBUG_PIM_TRACE_DETAIL_ONLY) { vty_out(vty, "debug pim trace detail\n"); ++writes; } @@ -120,6 +117,16 @@ int pim_debug_config_write(struct vty *vty) ++writes; } + if (PIM_DEBUG_MLAG) { + vty_out(vty, "debug pim mlag\n"); + ++writes; + } + + if (PIM_DEBUG_BSM) { + vty_out(vty, "debug pim bsm\n"); + ++writes; + } + if (PIM_DEBUG_VXLAN) { vty_out(vty, "debug pim vxlan\n"); ++writes; @@ -165,9 +172,9 @@ int pim_global_config_write_worker(struct pim_instance *pim, struct vty *vty) char spaces[10]; if (pim->vrf_id == VRF_DEFAULT) - sprintf(spaces, "%s", ""); + snprintf(spaces, sizeof(spaces), "%s", ""); else - sprintf(spaces, "%s", " "); + snprintf(spaces, sizeof(spaces), "%s", " "); writes += pim_msdp_config_write(pim, vty, spaces); @@ -209,6 +216,11 @@ int pim_global_config_write_worker(struct pim_instance *pim, struct vty *vty) ssm->plist_name); ++writes; } + if (pim->register_plist) { + vty_out(vty, "%sip pim register-accept-list %s\n", spaces, + pim->register_plist); + ++writes; + } if (pim->spt.switchover == PIM_SPT_INFINITY) { if (pim->spt.plist) vty_out(vty, @@ -227,6 +239,13 @@ int pim_global_config_write_worker(struct pim_instance *pim, struct vty *vty) vty_out(vty, "%sip pim ecmp\n", spaces); ++writes; } + + if (pim->igmp_watermark_limit != 0) { + vty_out(vty, "%sip igmp watermark-warn %u\n", spaces, + pim->igmp_watermark_limit); + ++writes; + } + if (pim->ssmpingd_list) { struct listnode *node; struct ssmpingd_sock *ss; @@ -240,8 +259,6 @@ int pim_global_config_write_worker(struct pim_instance *pim, struct vty *vty) } } - pim_vxlan_config_write(vty, spaces, &writes); - return writes; } @@ -344,6 +361,24 @@ int pim_interface_config_write(struct vty *vty) ++writes; } + /* IF ip igmp last-member_query-count */ + if (pim_ifp->igmp_last_member_query_count + != IGMP_DEFAULT_ROBUSTNESS_VARIABLE) { + vty_out(vty, + " ip igmp last-member-query-count %d\n", + pim_ifp->igmp_last_member_query_count); + ++writes; + } + + /* IF ip igmp last-member_query-interval */ + if (pim_ifp->igmp_specific_query_max_response_time_dsec + != IGMP_SPECIFIC_QUERY_MAX_RESPONSE_TIME_DSEC) { + vty_out(vty, + " ip igmp last-member-query-interval %d\n", + pim_ifp->igmp_specific_query_max_response_time_dsec); + ++writes; + } + /* IF ip igmp join */ if (pim_ifp->igmp_join_list) { struct listnode *node; @@ -359,13 +394,19 @@ int pim_interface_config_write(struct vty *vty) ij->group_addr, group_str, sizeof(group_str)); - inet_ntop(AF_INET, - &ij->source_addr, - source_str, - sizeof(source_str)); - vty_out(vty, - " ip igmp join %s %s\n", - group_str, source_str); + if (ij->source_addr.s_addr == INADDR_ANY) { + vty_out(vty, + " ip igmp join %s\n", + group_str); + } else { + inet_ntop(AF_INET, + &ij->source_addr, + source_str, + sizeof(source_str)); + vty_out(vty, + " ip igmp join %s %s\n", + group_str, source_str); + } ++writes; } } @@ -383,7 +424,10 @@ int pim_interface_config_write(struct vty *vty) writes += pim_static_write_mroute(pim, vty, ifp); + pim_bsm_write_config(vty, ifp); + ++writes; pim_bfd_write_config(vty, ifp); + ++writes; } vty_endframe(vty, "!\n"); ++writes; diff --git a/pimd/pim_vxlan.c b/pimd/pim_vxlan.c index af76c6d732..380c97a97c 100644 --- a/pimd/pim_vxlan.c +++ b/pimd/pim_vxlan.c @@ -38,6 +38,7 @@ #include "pim_nht.h" #include "pim_zebra.h" #include "pim_vxlan.h" +#include "pim_mlag.h" /* pim-vxlan global info */ struct pim_vxlan vxlan_info, *pim_vxlan_p = &vxlan_info; @@ -84,8 +85,16 @@ static void pim_vxlan_do_reg_work(void) if (PIM_DEBUG_VXLAN) zlog_debug("vxlan SG %s periodic NULL register", vxlan_sg->sg_str); - pim_null_register_send(vxlan_sg->up); - ++work_cnt; + + /* + * If we are on the work queue *and* the rpf + * has been lost on the vxlan_sg->up let's + * make sure that we don't send it. + */ + if (vxlan_sg->up->rpf.source_nexthop.interface) { + pim_null_register_send(vxlan_sg->up); + ++work_cnt; + } } if (work_cnt > vxlan_info.max_work_cnt) { @@ -115,7 +124,7 @@ static void pim_vxlan_init_work(void) vxlan_info.max_work_cnt = PIM_VXLAN_WORK_MAX; vxlan_info.flags |= PIM_VXLANF_WORK_INITED; vxlan_info.work_list = list_new(); - pim_vxlan_work_timer_setup(TRUE /* start */); + pim_vxlan_work_timer_setup(true/* start */); } static void pim_vxlan_add_work(struct pim_vxlan_sg *vxlan_sg) @@ -216,6 +225,7 @@ static void pim_vxlan_orig_mr_up_del(struct pim_vxlan_sg *vxlan_sg) vxlan_sg->sg_str); vxlan_sg->up = NULL; + if (up->flags & PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_ORIG) { /* clear out all the vxlan properties */ up->flags &= ~(PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_ORIG | @@ -238,33 +248,35 @@ static void pim_vxlan_orig_mr_up_del(struct pim_vxlan_sg *vxlan_sg) * origination mroutes active sources but just in * case */ - up = pim_upstream_del(vxlan_sg->pim, up, - __PRETTY_FUNCTION__); + up = pim_upstream_del(vxlan_sg->pim, up, __func__); } /* if there are other references register the source * for nht */ - if (up) - pim_rpf_update(vxlan_sg->pim, up, NULL, 1 /* is_new */); + if (up) { + enum pim_rpf_result r; + + r = pim_rpf_update(vxlan_sg->pim, up, NULL, __func__); + if (r == PIM_RPF_FAILURE) { + if (PIM_DEBUG_VXLAN) + zlog_debug( + "vxlan SG %s rpf_update failure", + vxlan_sg->sg_str); + } + } } } static void pim_vxlan_orig_mr_up_iif_update(struct pim_vxlan_sg *vxlan_sg) { - int vif_index; - /* update MFC with the new IIF */ pim_upstream_fill_static_iif(vxlan_sg->up, vxlan_sg->iif); - vif_index = pim_if_find_vifindex_by_ifindex(vxlan_sg->pim, - vxlan_sg->iif->ifindex); - if (vif_index > 0) - pim_scan_individual_oil(vxlan_sg->up->channel_oil, - vif_index); + pim_upstream_mroute_iif_update(vxlan_sg->up->channel_oil, __func__); if (PIM_DEBUG_VXLAN) - zlog_debug("vxlan SG %s orig mroute-up updated with iif %s vifi %d", + zlog_debug("vxlan SG %s orig mroute-up updated with iif %s", vxlan_sg->sg_str, - vxlan_sg->iif?vxlan_sg->iif->name:"-", vif_index); + vxlan_sg->iif?vxlan_sg->iif->name:"-"); } @@ -290,8 +302,10 @@ static void pim_vxlan_orig_mr_up_iif_update(struct pim_vxlan_sg *vxlan_sg) static void pim_vxlan_orig_mr_up_add(struct pim_vxlan_sg *vxlan_sg) { struct pim_upstream *up; + struct pim_interface *term_ifp; int flags = 0; struct prefix nht_p; + struct pim_instance *pim = vxlan_sg->pim; if (vxlan_sg->up) { /* nothing to do */ @@ -344,15 +358,25 @@ static void pim_vxlan_orig_mr_up_add(struct pim_vxlan_sg *vxlan_sg) nht_p.prefixlen = IPV4_MAX_BITLEN; nht_p.u.prefix4 = up->upstream_addr; pim_delete_tracked_nexthop(vxlan_sg->pim, - &nht_p, up, NULL); + &nht_p, up, NULL, false); } - pim_upstream_ref(up, flags, __PRETTY_FUNCTION__); + /* We are acting FHR; clear out use_rpt setting if any */ + pim_upstream_update_use_rpt(up, false /*update_mroute*/); + pim_upstream_ref(up, flags, __func__); vxlan_sg->up = up; + term_ifp = pim_vxlan_get_term_ifp(pim); + /* mute termination device on origination mroutes */ + if (term_ifp) + pim_channel_update_oif_mute(up->channel_oil, + term_ifp); pim_vxlan_orig_mr_up_iif_update(vxlan_sg); + /* mute pimreg on origination mroutes */ + if (pim->regiface) + pim_channel_update_oif_mute(up->channel_oil, + pim->regiface->info); } else { up = pim_upstream_add(vxlan_sg->pim, &vxlan_sg->sg, - vxlan_sg->iif, flags, - __PRETTY_FUNCTION__, NULL); + vxlan_sg->iif, flags, __func__, NULL); vxlan_sg->up = up; } @@ -373,6 +397,8 @@ static void pim_vxlan_orig_mr_up_add(struct pim_vxlan_sg *vxlan_sg) /* update the inherited OIL */ pim_upstream_inherited_olist(vxlan_sg->pim, up); + if (!up->channel_oil->installed) + pim_upstream_mroute_add(up->channel_oil, __func__); } static void pim_vxlan_orig_mr_oif_add(struct pim_vxlan_sg *vxlan_sg) @@ -386,7 +412,8 @@ static void pim_vxlan_orig_mr_oif_add(struct pim_vxlan_sg *vxlan_sg) vxlan_sg->flags |= PIM_VXLAN_SGF_OIF_INSTALLED; pim_channel_add_oif(vxlan_sg->up->channel_oil, - vxlan_sg->orig_oif, PIM_OIF_FLAG_PROTO_VXLAN); + vxlan_sg->orig_oif, PIM_OIF_FLAG_PROTO_VXLAN, + __func__); } static void pim_vxlan_orig_mr_oif_del(struct pim_vxlan_sg *vxlan_sg) @@ -405,7 +432,7 @@ static void pim_vxlan_orig_mr_oif_del(struct pim_vxlan_sg *vxlan_sg) vxlan_sg->flags &= ~PIM_VXLAN_SGF_OIF_INSTALLED; pim_channel_del_oif(vxlan_sg->up->channel_oil, - orig_oif, PIM_OIF_FLAG_PROTO_VXLAN); + orig_oif, PIM_OIF_FLAG_PROTO_VXLAN, __func__); } static inline struct interface *pim_vxlan_orig_mr_oif_get( @@ -470,15 +497,16 @@ static void pim_vxlan_orig_mr_del(struct pim_vxlan_sg *vxlan_sg) pim_vxlan_orig_mr_up_del(vxlan_sg); } -static void pim_vxlan_orig_mr_iif_update(struct hash_backet *backet, void *arg) +static void pim_vxlan_orig_mr_iif_update(struct hash_bucket *backet, void *arg) { - struct interface *ifp = (struct interface *)arg; + struct interface *ifp; struct pim_vxlan_sg *vxlan_sg = (struct pim_vxlan_sg *)backet->data; struct interface *old_iif = vxlan_sg->iif; if (!pim_vxlan_is_orig_mroute(vxlan_sg)) return; + ifp = pim_vxlan_orig_mr_iif_get(vxlan_sg->pim); if (PIM_DEBUG_VXLAN) zlog_debug("vxlan SG %s iif changed from %s to %s", vxlan_sg->sg_str, @@ -525,8 +553,15 @@ static void pim_vxlan_term_mr_oif_add(struct pim_vxlan_sg *vxlan_sg) vxlan_sg->sg_str, vxlan_sg->term_oif->name); if (pim_ifchannel_local_membership_add(vxlan_sg->term_oif, - &vxlan_sg->sg)) { + &vxlan_sg->sg, true /*is_vxlan */)) { vxlan_sg->flags |= PIM_VXLAN_SGF_OIF_INSTALLED; + /* update the inherited OIL */ + /* XXX - I don't see the inherited OIL updated when a local + * member is added. And that probably needs to be fixed. Till + * that happens we do a force update on the inherited OIL + * here. + */ + pim_upstream_inherited_olist(vxlan_sg->pim, vxlan_sg->up); } else { zlog_warn("vxlan SG %s term-oif %s add failed", vxlan_sg->sg_str, vxlan_sg->term_oif->name); @@ -544,6 +579,43 @@ static void pim_vxlan_term_mr_oif_del(struct pim_vxlan_sg *vxlan_sg) vxlan_sg->flags &= ~PIM_VXLAN_SGF_OIF_INSTALLED; pim_ifchannel_local_membership_del(vxlan_sg->term_oif, &vxlan_sg->sg); + /* update the inherited OIL */ + /* XXX - I don't see the inherited OIL updated when a local member + * is deleted. And that probably needs to be fixed. Till that happens + * we do a force update on the inherited OIL here. + */ + pim_upstream_inherited_olist(vxlan_sg->pim, vxlan_sg->up); +} + +static void pim_vxlan_update_sg_entry_mlag(struct pim_instance *pim, + struct pim_upstream *up, bool inherit) +{ + bool is_df = true; + + if (inherit && up->parent && + PIM_UPSTREAM_FLAG_TEST_MLAG_VXLAN(up->parent->flags) && + PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(up->parent->flags)) + is_df = false; + + pim_mlag_up_df_role_update(pim, up, is_df, "inherit_xg_df"); +} + +/* We run MLAG DF election only on mroutes that have the termination + * device ipmr-lo in the immediate OIL. This is only (*, G) entries at the + * moment. For (S, G) entries that (with ipmr-lo in the inherited OIL) we + * inherit the DF role from the (*, G) entry. + */ +void pim_vxlan_inherit_mlag_flags(struct pim_instance *pim, + struct pim_upstream *up, bool inherit) +{ + struct listnode *listnode; + struct pim_upstream *child; + + for (ALL_LIST_ELEMENTS_RO(up->sources, listnode, + child)) { + pim_vxlan_update_sg_entry_mlag(pim, + child, true /* inherit */); + } } static void pim_vxlan_term_mr_up_add(struct pim_vxlan_sg *vxlan_sg) @@ -564,15 +636,18 @@ static void pim_vxlan_term_mr_up_add(struct pim_vxlan_sg *vxlan_sg) /* enable MLAG designated-forwarder election on termination mroutes */ PIM_UPSTREAM_FLAG_SET_MLAG_VXLAN(flags); - up = pim_upstream_add(vxlan_sg->pim, &vxlan_sg->sg, - NULL /* iif */, flags, - __PRETTY_FUNCTION__, NULL); + up = pim_upstream_add(vxlan_sg->pim, &vxlan_sg->sg, NULL /* iif */, + flags, __func__, NULL); vxlan_sg->up = up; if (!up) { zlog_warn("vxlan SG %s term mroute-up add failed", vxlan_sg->sg_str); + return; } + + /* update existing SG entries with the parent's MLAG flag */ + pim_vxlan_inherit_mlag_flags(vxlan_sg->pim, up, true /*enable*/); } static void pim_vxlan_term_mr_up_del(struct pim_vxlan_sg *vxlan_sg) @@ -587,12 +662,14 @@ static void pim_vxlan_term_mr_up_del(struct pim_vxlan_sg *vxlan_sg) vxlan_sg->sg_str); vxlan_sg->up = NULL; if (up->flags & PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_TERM) { + /* update SG entries that are inheriting from this XG entry */ + pim_vxlan_inherit_mlag_flags(vxlan_sg->pim, up, + false /*enable*/); /* clear out all the vxlan related flags */ up->flags &= ~(PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_TERM | PIM_UPSTREAM_FLAG_MASK_MLAG_VXLAN); - - pim_upstream_del(vxlan_sg->pim, up, - __PRETTY_FUNCTION__); + pim_mlag_up_local_del(vxlan_sg->pim, up); + pim_upstream_del(vxlan_sg->pim, up, __func__); } } @@ -623,9 +700,9 @@ static void pim_vxlan_term_mr_del(struct pim_vxlan_sg *vxlan_sg) } /************************** vxlan SG cache management ************************/ -static unsigned int pim_vxlan_sg_hash_key_make(void *p) +static unsigned int pim_vxlan_sg_hash_key_make(const void *p) { - struct pim_vxlan_sg *vxlan_sg = p; + const struct pim_vxlan_sg *vxlan_sg = p; return (jhash_2words(vxlan_sg->sg.src.s_addr, vxlan_sg->sg.grp.s_addr, 0)); @@ -656,6 +733,14 @@ static struct pim_vxlan_sg *pim_vxlan_sg_new(struct pim_instance *pim, vxlan_sg = hash_get(pim->vxlan.sg_hash, vxlan_sg, hash_alloc_intern); + /* we register with the MLAG daemon in the first VxLAN SG and never + * de-register during that life of the pimd + */ + if (pim->vxlan.sg_hash->count == 1) { + vxlan_mlag.flags |= PIM_VXLAN_MLAGF_DO_REG; + pim_mlag_register(); + } + return vxlan_sg; } @@ -687,14 +772,8 @@ struct pim_vxlan_sg *pim_vxlan_sg_add(struct pim_instance *pim, return vxlan_sg; } -void pim_vxlan_sg_del(struct pim_instance *pim, struct prefix_sg *sg) +static void pim_vxlan_sg_del_item(struct pim_vxlan_sg *vxlan_sg) { - struct pim_vxlan_sg *vxlan_sg; - - vxlan_sg = pim_vxlan_sg_find(pim, sg); - if (!vxlan_sg) - return; - vxlan_sg->flags |= PIM_VXLAN_SGF_DEL_IN_PROG; pim_vxlan_del_work(vxlan_sg); @@ -704,21 +783,37 @@ void pim_vxlan_sg_del(struct pim_instance *pim, struct prefix_sg *sg) else pim_vxlan_term_mr_del(vxlan_sg); - hash_release(vxlan_sg->pim->vxlan.sg_hash, vxlan_sg); - if (PIM_DEBUG_VXLAN) zlog_debug("vxlan SG %s free", vxlan_sg->sg_str); XFREE(MTYPE_PIM_VXLAN_SG, vxlan_sg); } +void pim_vxlan_sg_del(struct pim_instance *pim, struct prefix_sg *sg) +{ + struct pim_vxlan_sg *vxlan_sg; + + vxlan_sg = pim_vxlan_sg_find(pim, sg); + if (!vxlan_sg) + return; + + hash_release(pim->vxlan.sg_hash, vxlan_sg); + pim_vxlan_sg_del_item(vxlan_sg); +} + /******************************* MLAG handling *******************************/ +bool pim_vxlan_do_mlag_reg(void) +{ + return (vxlan_mlag.flags & PIM_VXLAN_MLAGF_DO_REG); +} + /* The peerlink sub-interface is added as an OIF to the origination-mroute. * This is done to send a copy of the multicast-vxlan encapsulated traffic * to the MLAG peer which may mroute it over the underlay if there are any * interested receivers. */ -static void pim_vxlan_sg_peerlink_update(struct hash_backet *backet, void *arg) +static void pim_vxlan_sg_peerlink_oif_update(struct hash_bucket *backet, + void *arg) { struct interface *new_oif = (struct interface *)arg; struct pim_vxlan_sg *vxlan_sg = (struct pim_vxlan_sg *)backet->data; @@ -757,8 +852,6 @@ void pim_vxlan_mlag_update(bool enable, bool peer_state, uint32_t role, struct in_addr *reg_addr) { struct pim_instance *pim; - struct interface *old_oif; - struct interface *new_oif; char addr_buf[INET_ADDRSTRLEN]; struct pim_interface *pim_ifp = NULL; @@ -778,8 +871,6 @@ void pim_vxlan_mlag_update(bool enable, bool peer_state, uint32_t role, */ pim = pim_get_pim_instance(VRF_DEFAULT); - old_oif = pim_vxlan_orig_mr_oif_get(pim); - if (enable) vxlan_mlag.flags |= PIM_VXLAN_MLAGF_ENABLED; else @@ -800,35 +891,9 @@ void pim_vxlan_mlag_update(bool enable, bool peer_state, uint32_t role, pim_vxlan_set_peerlink_rif(pim, peerlink_rif); else pim_vxlan_set_peerlink_rif(pim, NULL); - - new_oif = pim_vxlan_orig_mr_oif_get(pim); - if (old_oif != new_oif) - hash_iterate(pim->vxlan.sg_hash, pim_vxlan_sg_peerlink_update, - new_oif); } /****************************** misc callbacks *******************************/ -void pim_vxlan_config_write(struct vty *vty, char *spaces, int *writes) -{ - char addr_buf[INET_ADDRSTRLEN]; - - if ((vxlan_mlag.flags & PIM_VXLAN_MLAGF_ENABLED) && - vxlan_mlag.peerlink_rif) { - - inet_ntop(AF_INET, &vxlan_mlag.reg_addr, - addr_buf, sizeof(addr_buf)); - vty_out(vty, - "%sip pim mlag %s role %s state %s addr %s\n", - spaces, - vxlan_mlag.peerlink_rif->name, - (vxlan_mlag.role == PIM_VXLAN_MLAG_ROLE_PRIMARY) ? - "primary":"secondary", - vxlan_mlag.peer_state ? "up" : "down", - addr_buf); - *writes += 1; - } -} - static void pim_vxlan_set_default_iif(struct pim_instance *pim, struct interface *ifp) { @@ -840,9 +905,8 @@ static void pim_vxlan_set_default_iif(struct pim_instance *pim, old_iif = pim->vxlan.default_iif; if (PIM_DEBUG_VXLAN) zlog_debug("%s: vxlan default iif changed from %s to %s", - __PRETTY_FUNCTION__, - old_iif ? old_iif->name : "-", - ifp ? ifp->name : "-"); + __func__, old_iif ? old_iif->name : "-", + ifp ? ifp->name : "-"); old_iif = pim_vxlan_orig_mr_iif_get(pim); pim->vxlan.default_iif = ifp; @@ -851,22 +915,80 @@ static void pim_vxlan_set_default_iif(struct pim_instance *pim, return; if (PIM_DEBUG_VXLAN) - zlog_debug("%s: vxlan orig iif changed from %s to %s", - __PRETTY_FUNCTION__, old_iif ? old_iif->name : "-", - ifp ? ifp->name : "-"); + zlog_debug("%s: vxlan orig iif changed from %s to %s", __func__, + old_iif ? old_iif->name : "-", + ifp ? ifp->name : "-"); /* add/del upstream entries for the existing vxlan SG when the * interface becomes available */ if (pim->vxlan.sg_hash) hash_iterate(pim->vxlan.sg_hash, - pim_vxlan_orig_mr_iif_update, ifp); + pim_vxlan_orig_mr_iif_update, NULL); +} + +static void pim_vxlan_up_cost_update(struct pim_instance *pim, + struct pim_upstream *up, + struct interface *old_peerlink_rif) +{ + if (!PIM_UPSTREAM_FLAG_TEST_MLAG_VXLAN(up->flags)) + return; + + if (up->rpf.source_nexthop.interface && + ((up->rpf.source_nexthop.interface == + pim->vxlan.peerlink_rif) || + (up->rpf.source_nexthop.interface == + old_peerlink_rif))) { + if (PIM_DEBUG_VXLAN) + zlog_debug("RPF cost adjust for %s on peerlink-rif (old: %s, new: %s) change", + up->sg_str, + old_peerlink_rif ? + old_peerlink_rif->name : "-", + pim->vxlan.peerlink_rif ? + pim->vxlan.peerlink_rif->name : "-"); + pim_mlag_up_local_add(pim, up); + } +} + +static void pim_vxlan_term_mr_cost_update(struct hash_bucket *backet, void *arg) +{ + struct interface *old_peerlink_rif = (struct interface *)arg; + struct pim_vxlan_sg *vxlan_sg = (struct pim_vxlan_sg *)backet->data; + struct pim_upstream *up; + struct listnode *listnode; + struct pim_upstream *child; + + if (pim_vxlan_is_orig_mroute(vxlan_sg)) + return; + + /* Lookup all XG and SG entries with RPF-interface peerlink_rif */ + up = vxlan_sg->up; + if (!up) + return; + + pim_vxlan_up_cost_update(vxlan_sg->pim, up, + old_peerlink_rif); + + for (ALL_LIST_ELEMENTS_RO(up->sources, listnode, + child)) + pim_vxlan_up_cost_update(vxlan_sg->pim, child, + old_peerlink_rif); +} + +static void pim_vxlan_sg_peerlink_rif_update(struct hash_bucket *backet, + void *arg) +{ + pim_vxlan_orig_mr_iif_update(backet, NULL); + pim_vxlan_term_mr_cost_update(backet, arg); } static void pim_vxlan_set_peerlink_rif(struct pim_instance *pim, struct interface *ifp) { struct interface *old_iif; + struct interface *new_iif; + struct interface *old_oif; + struct interface *new_oif; if (pim->vxlan.peerlink_rif == ifp) return; @@ -874,26 +996,79 @@ static void pim_vxlan_set_peerlink_rif(struct pim_instance *pim, old_iif = pim->vxlan.peerlink_rif; if (PIM_DEBUG_VXLAN) zlog_debug("%s: vxlan peerlink_rif changed from %s to %s", - __PRETTY_FUNCTION__, old_iif ? old_iif->name : "-", - ifp ? ifp->name : "-"); + __func__, old_iif ? old_iif->name : "-", + ifp ? ifp->name : "-"); old_iif = pim_vxlan_orig_mr_iif_get(pim); + old_oif = pim_vxlan_orig_mr_oif_get(pim); pim->vxlan.peerlink_rif = ifp; - ifp = pim_vxlan_orig_mr_iif_get(pim); - if (old_iif == ifp) + + new_iif = pim_vxlan_orig_mr_iif_get(pim); + if (old_iif != new_iif) { + if (PIM_DEBUG_VXLAN) + zlog_debug("%s: vxlan orig iif changed from %s to %s", + __func__, old_iif ? old_iif->name : "-", + new_iif ? new_iif->name : "-"); + + /* add/del upstream entries for the existing vxlan SG when the + * interface becomes available + */ + if (pim->vxlan.sg_hash) + hash_iterate(pim->vxlan.sg_hash, + pim_vxlan_sg_peerlink_rif_update, + old_iif); + } + + new_oif = pim_vxlan_orig_mr_oif_get(pim); + if (old_oif != new_oif) { + if (PIM_DEBUG_VXLAN) + zlog_debug("%s: vxlan orig oif changed from %s to %s", + __func__, old_oif ? old_oif->name : "-", + new_oif ? new_oif->name : "-"); + if (pim->vxlan.sg_hash) + hash_iterate(pim->vxlan.sg_hash, + pim_vxlan_sg_peerlink_oif_update, + new_oif); + } +} + +static void pim_vxlan_term_mr_oif_update(struct hash_bucket *backet, void *arg) +{ + struct interface *ifp = (struct interface *)arg; + struct pim_vxlan_sg *vxlan_sg = (struct pim_vxlan_sg *)backet->data; + + if (pim_vxlan_is_orig_mroute(vxlan_sg)) + return; + + if (vxlan_sg->term_oif == ifp) return; if (PIM_DEBUG_VXLAN) - zlog_debug("%s: vxlan orig iif changed from %s to %s", - __PRETTY_FUNCTION__, old_iif ? old_iif->name : "-", + zlog_debug("vxlan SG %s term oif changed from %s to %s", + vxlan_sg->sg_str, + vxlan_sg->term_oif ? vxlan_sg->term_oif->name : "-", ifp ? ifp->name : "-"); - /* add/del upstream entries for the existing vxlan SG when the - * interface becomes available - */ + pim_vxlan_term_mr_del(vxlan_sg); + vxlan_sg->term_oif = ifp; + pim_vxlan_term_mr_add(vxlan_sg); +} + +static void pim_vxlan_term_oif_update(struct pim_instance *pim, + struct interface *ifp) +{ + if (pim->vxlan.term_if == ifp) + return; + + if (PIM_DEBUG_VXLAN) + zlog_debug("vxlan term oif changed from %s to %s", + pim->vxlan.term_if ? pim->vxlan.term_if->name : "-", + ifp ? ifp->name : "-"); + + pim->vxlan.term_if = ifp; if (pim->vxlan.sg_hash) hash_iterate(pim->vxlan.sg_hash, - pim_vxlan_orig_mr_iif_update, ifp); + pim_vxlan_term_mr_oif_update, ifp); } void pim_vxlan_add_vif(struct interface *ifp) @@ -910,6 +1085,9 @@ void pim_vxlan_add_vif(struct interface *ifp) if (vxlan_mlag.flags & PIM_VXLAN_MLAGF_ENABLED && (ifp == vxlan_mlag.peerlink_rif)) pim_vxlan_set_peerlink_rif(pim, ifp); + + if (pim->vxlan.term_if_cfg == ifp) + pim_vxlan_term_oif_update(pim, ifp); } void pim_vxlan_del_vif(struct interface *ifp) @@ -925,76 +1103,56 @@ void pim_vxlan_del_vif(struct interface *ifp) if (pim->vxlan.peerlink_rif == ifp) pim_vxlan_set_peerlink_rif(pim, NULL); -} - -static void pim_vxlan_term_mr_oif_update(struct hash_backet *backet, void *arg) -{ - struct interface *ifp = (struct interface *)arg; - struct pim_vxlan_sg *vxlan_sg = (struct pim_vxlan_sg *)backet->data; - if (pim_vxlan_is_orig_mroute(vxlan_sg)) - return; - - if (vxlan_sg->term_oif == ifp) - return; - - if (PIM_DEBUG_VXLAN) - zlog_debug("vxlan SG %s term oif changed from %s to %s", - vxlan_sg->sg_str, - vxlan_sg->term_oif ? vxlan_sg->term_oif->name : "-", - ifp ? ifp->name : "-"); - - pim_vxlan_term_mr_del(vxlan_sg); - vxlan_sg->term_oif = ifp; - pim_vxlan_term_mr_add(vxlan_sg); + if (pim->vxlan.term_if == ifp) + pim_vxlan_term_oif_update(pim, NULL); } +/* enable pim implicitly on the termination device add */ void pim_vxlan_add_term_dev(struct pim_instance *pim, struct interface *ifp) { struct pim_interface *pim_ifp; - if (pim->vxlan.term_if == ifp) + if (pim->vxlan.term_if_cfg == ifp) return; if (PIM_DEBUG_VXLAN) - zlog_debug("vxlan term oif changed from %s to %s", - pim->vxlan.term_if ? pim->vxlan.term_if->name : "-", - ifp->name); + zlog_debug("vxlan term oif cfg changed from %s to %s", + pim->vxlan.term_if_cfg ? + pim->vxlan.term_if_cfg->name : "-", + ifp->name); + + pim->vxlan.term_if_cfg = ifp; /* enable pim on the term ifp */ pim_ifp = (struct pim_interface *)ifp->info; if (pim_ifp) { PIM_IF_DO_PIM(pim_ifp->options); + /* ifp is already oper up; activate it as a term dev */ + if (pim_ifp->mroute_vif_index >= 0) + pim_vxlan_term_oif_update(pim, ifp); } else { - pim_ifp = pim_if_new(ifp, false /*igmp*/, true /*pim*/, - false /*pimreg*/, true /*vxlan_term*/); - /* ensure that pimreg existss before using the newly created + /* ensure that pimreg exists before using the newly created * vxlan termination device */ pim_if_create_pimreg(pim); + (void)pim_if_new(ifp, false /*igmp*/, true /*pim*/, + false /*pimreg*/, true /*vxlan_term*/); } - - pim->vxlan.term_if = ifp; - - if (pim->vxlan.sg_hash) - hash_iterate(pim_ifp->pim->vxlan.sg_hash, - pim_vxlan_term_mr_oif_update, ifp); } +/* disable pim implicitly, if needed, on the termination device deletion */ void pim_vxlan_del_term_dev(struct pim_instance *pim) { - struct interface *ifp = pim->vxlan.term_if; + struct interface *ifp = pim->vxlan.term_if_cfg; struct pim_interface *pim_ifp; if (PIM_DEBUG_VXLAN) - zlog_debug("vxlan term oif changed from %s to -", ifp->name); + zlog_debug("vxlan term oif cfg changed from %s to -", + ifp->name); - pim->vxlan.term_if = NULL; - - if (pim->vxlan.sg_hash) - hash_iterate(pim->vxlan.sg_hash, - pim_vxlan_term_mr_oif_update, NULL); + pim->vxlan.term_if_cfg = NULL; pim_ifp = (struct pim_interface *)ifp->info; if (pim_ifp) { @@ -1002,7 +1160,6 @@ void pim_vxlan_del_term_dev(struct pim_instance *pim) if (!PIM_IF_TEST_IGMP(pim_ifp->options)) pim_if_delete(ifp); } - } void pim_vxlan_init(struct pim_instance *pim) @@ -1018,8 +1175,14 @@ void pim_vxlan_init(struct pim_instance *pim) void pim_vxlan_exit(struct pim_instance *pim) { if (pim->vxlan.sg_hash) { - hash_clean(pim->vxlan.sg_hash, NULL); + hash_clean(pim->vxlan.sg_hash, + (void (*)(void *))pim_vxlan_sg_del_item); hash_free(pim->vxlan.sg_hash); pim->vxlan.sg_hash = NULL; } } + +void pim_vxlan_terminate(void) +{ + pim_vxlan_work_timer_setup(false); +} diff --git a/pimd/pim_vxlan.h b/pimd/pim_vxlan.h index f0a66e6b77..18f1b74175 100644 --- a/pimd/pim_vxlan.h +++ b/pimd/pim_vxlan.h @@ -66,17 +66,14 @@ struct pim_vxlan_sg { enum pim_vxlan_mlag_flags { PIM_VXLAN_MLAGF_NONE = 0, - PIM_VXLAN_MLAGF_ENABLED = (1 << 0) -}; - -enum pim_vxlan_mlag_role { - PIM_VXLAN_MLAG_ROLE_SECONDARY = 0, - PIM_VXLAN_MLAG_ROLE_PRIMARY + PIM_VXLAN_MLAGF_ENABLED = (1 << 0), + PIM_VXLAN_MLAGF_DO_REG = (1 << 1) }; struct pim_vxlan_mlag { enum pim_vxlan_mlag_flags flags; - enum pim_vxlan_mlag_role role; + /* XXX - remove this variable from here */ + int role; bool peer_state; /* routed interface setup on top of MLAG peerlink */ struct interface *peerlink_rif; @@ -115,6 +112,19 @@ static inline bool pim_vxlan_is_orig_mroute(struct pim_vxlan_sg *vxlan_sg) return (vxlan_sg->sg.src.s_addr != 0); } +static inline bool pim_vxlan_is_local_sip(struct pim_upstream *up) +{ + return (up->sg.src.s_addr != INADDR_ANY) && + up->rpf.source_nexthop.interface && + if_is_loopback_or_vrf(up->rpf.source_nexthop.interface); +} + +static inline bool pim_vxlan_is_term_dev_cfg(struct pim_instance *pim, + struct interface *ifp) +{ + return pim->vxlan.term_if_cfg == ifp; +} + extern struct pim_vxlan *pim_vxlan_p; extern struct pim_vxlan_sg *pim_vxlan_sg_find(struct pim_instance *pim, struct prefix_sg *sg); @@ -134,6 +144,10 @@ extern bool pim_vxlan_get_register_src(struct pim_instance *pim, extern void pim_vxlan_mlag_update(bool enable, bool peer_state, uint32_t role, struct interface *peerlink_rif, struct in_addr *reg_addr); -extern void pim_vxlan_config_write(struct vty *vty, char *spaces, int *writes); +extern bool pim_vxlan_do_mlag_reg(void); +extern void pim_vxlan_inherit_mlag_flags(struct pim_instance *pim, + struct pim_upstream *up, bool inherit); +/* Shutdown of PIM stop the thread */ +extern void pim_vxlan_terminate(void); #endif /* PIM_VXLAN_H */ diff --git a/pimd/pim_vxlan_instance.h b/pimd/pim_vxlan_instance.h index 3f99483fbe..5b35bcbeaa 100644 --- a/pimd/pim_vxlan_instance.h +++ b/pimd/pim_vxlan_instance.h @@ -36,6 +36,7 @@ struct pim_vxlan_instance { /* device used by the dataplane to terminate multicast encapsulated * vxlan traffic */ + struct interface *term_if_cfg; struct interface *term_if; }; diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c index aeaea7d69f..d089dfda51 100644 --- a/pimd/pim_zebra.c +++ b/pimd/pim_zebra.c @@ -46,16 +46,16 @@ #include "pim_nht.h" #include "pim_ssm.h" #include "pim_vxlan.h" +#include "pim_mlag.h" #undef PIM_DEBUG_IFADDR_DUMP #define PIM_DEBUG_IFADDR_DUMP -static struct zclient *zclient = NULL; +struct zclient *zclient; /* Router-id update message from zebra. */ -static int pim_router_id_update_zebra(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int pim_router_id_update_zebra(ZAPI_CALLBACK_ARGS) { struct prefix router_id; @@ -64,224 +64,7 @@ static int pim_router_id_update_zebra(int command, struct zclient *zclient, return 0; } -static int pim_zebra_if_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) -{ - struct interface *ifp; - struct pim_instance *pim; - - /* - zebra api adds/dels interfaces using the same call - interface_add_read below, see comments in lib/zclient.c - */ - ifp = zebra_interface_add_read(zclient->ibuf, vrf_id); - if (!ifp) - return 0; - - pim = pim_get_pim_instance(vrf_id); - if (PIM_DEBUG_ZEBRA) { - zlog_debug( - "%s: %s index %d(%u) flags %ld metric %d mtu %d operative %d", - __PRETTY_FUNCTION__, ifp->name, ifp->ifindex, vrf_id, - (long)ifp->flags, ifp->metric, ifp->mtu, - if_is_operative(ifp)); - } - - if (if_is_operative(ifp)) { - struct pim_interface *pim_ifp; - - pim_ifp = ifp->info; - /* - * If we have a pim_ifp already and this is an if_add - * that means that we probably have a vrf move event - * If that is the case, set the proper vrfness. - */ - if (pim_ifp) - pim_ifp->pim = pim; - pim_if_addr_add_all(ifp); - } - - /* - * If we are a vrf device that is up, open up the pim_socket for - * listening - * to incoming pim messages irrelevant if the user has configured us - * for pim or not. - */ - if (pim_if_is_vrf_device(ifp)) { - struct pim_interface *pim_ifp; - - if (!ifp->info) { - pim_ifp = pim_if_new(ifp, false, false, false, - false /*vxlan_term*/); - ifp->info = pim_ifp; - } - - pim_sock_add(ifp); - } - - if (!strncmp(ifp->name, PIM_VXLAN_TERM_DEV_NAME, - sizeof(PIM_VXLAN_TERM_DEV_NAME))) - pim_vxlan_add_term_dev(pim, ifp); - - return 0; -} - -static int pim_zebra_if_del(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) -{ - struct interface *ifp; - struct pim_instance *pim; - - /* - zebra api adds/dels interfaces using the same call - interface_add_read below, see comments in lib/zclient.c - - comments in lib/zclient.c seem to indicate that calling - zebra_interface_add_read is the correct call, but that - results in an attemted out of bounds read which causes - pimd to assert. Other clients use zebra_interface_state_read - and it appears to work just fine. - */ - ifp = zebra_interface_state_read(zclient->ibuf, vrf_id); - if (!ifp) - return 0; - - if (PIM_DEBUG_ZEBRA) { - zlog_debug( - "%s: %s index %d(%u) flags %ld metric %d mtu %d operative %d", - __PRETTY_FUNCTION__, ifp->name, ifp->ifindex, vrf_id, - (long)ifp->flags, ifp->metric, ifp->mtu, - if_is_operative(ifp)); - } - - if (!if_is_operative(ifp)) - pim_if_addr_del_all(ifp); - - if_set_index(ifp, IFINDEX_INTERNAL); - - pim = pim_get_pim_instance(vrf_id); - if (pim && pim->vxlan.term_if == ifp) - pim_vxlan_del_term_dev(pim); - - return 0; -} - -static int pim_zebra_if_state_up(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) -{ - struct pim_instance *pim; - struct interface *ifp; - uint32_t table_id; - - /* - zebra api notifies interface up/down events by using the same call - zebra_interface_state_read below, see comments in lib/zclient.c - */ - ifp = zebra_interface_state_read(zclient->ibuf, vrf_id); - if (!ifp) - return 0; - - if (PIM_DEBUG_ZEBRA) { - zlog_debug( - "%s: %s index %d(%u) flags %ld metric %d mtu %d operative %d", - __PRETTY_FUNCTION__, ifp->name, ifp->ifindex, vrf_id, - (long)ifp->flags, ifp->metric, ifp->mtu, - if_is_operative(ifp)); - } - - pim = pim_get_pim_instance(vrf_id); - if (if_is_operative(ifp)) { - struct pim_interface *pim_ifp; - - pim_ifp = ifp->info; - /* - * If we have a pim_ifp already and this is an if_add - * that means that we probably have a vrf move event - * If that is the case, set the proper vrfness. - */ - if (pim_ifp) - pim_ifp->pim = pim; - - /* - pim_if_addr_add_all() suffices for bringing up both IGMP and - PIM - */ - pim_if_addr_add_all(ifp); - } - - /* - * If we have a pimreg device callback and it's for a specific - * table set the master appropriately - */ - if (sscanf(ifp->name, "pimreg%" SCNu32, &table_id) == 1) { - struct vrf *vrf; - RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { - if ((table_id == vrf->data.l.table_id) - && (ifp->vrf_id != vrf->vrf_id)) { - struct interface *master = if_lookup_by_name( - vrf->name, vrf->vrf_id); - - if (!master) { - zlog_debug( - "%s: Unable to find Master interface for %s", - __PRETTY_FUNCTION__, vrf->name); - return 0; - } - zclient_interface_set_master(zclient, master, - ifp); - } - } - } - return 0; -} - -static int pim_zebra_if_state_down(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) -{ - struct interface *ifp; - - /* - zebra api notifies interface up/down events by using the same call - zebra_interface_state_read below, see comments in lib/zclient.c - */ - ifp = zebra_interface_state_read(zclient->ibuf, vrf_id); - if (!ifp) - return 0; - - if (PIM_DEBUG_ZEBRA) { - zlog_debug( - "%s: %s index %d(%u) flags %ld metric %d mtu %d operative %d", - __PRETTY_FUNCTION__, ifp->name, ifp->ifindex, vrf_id, - (long)ifp->flags, ifp->metric, ifp->mtu, - if_is_operative(ifp)); - } - - if (!if_is_operative(ifp)) { - pim_ifchannel_delete_all(ifp); - /* - pim_if_addr_del_all() suffices for shutting down IGMP, - but not for shutting down PIM - */ - pim_if_addr_del_all(ifp); - - /* - pim_sock_delete() closes the socket, stops read and timer - threads, - and kills all neighbors. - */ - if (ifp->info) { - pim_sock_delete(ifp, "link down"); - } - } - - if (ifp->info) - pim_if_del_vif(ifp); - - return 0; -} - -static int pim_zebra_interface_vrf_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int pim_zebra_interface_vrf_update(ZAPI_CALLBACK_ARGS) { struct interface *ifp; vrf_id_t new_vrf_id; @@ -292,9 +75,8 @@ static int pim_zebra_interface_vrf_update(int command, struct zclient *zclient, return 0; if (PIM_DEBUG_ZEBRA) - zlog_debug("%s: %s updating from %u to %u", - __PRETTY_FUNCTION__, - ifp->name, vrf_id, new_vrf_id); + zlog_debug("%s: %s updating from %u to %u", __func__, ifp->name, + vrf_id, new_vrf_id); if_update_to_new_vrf(ifp, new_vrf_id); @@ -307,8 +89,8 @@ static void dump_if_address(struct interface *ifp) struct connected *ifc; struct listnode *node; - zlog_debug("%s %s: interface %s addresses:", __FILE__, - __PRETTY_FUNCTION__, ifp->name); + zlog_debug("%s %s: interface %s addresses:", __FILE__, __func__, + ifp->name); for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) { struct prefix *p = ifc->address; @@ -317,8 +99,7 @@ static void dump_if_address(struct interface *ifp) continue; zlog_debug("%s %s: interface %s address %s %s", __FILE__, - __PRETTY_FUNCTION__, ifp->name, - inet_ntoa(p->u.prefix4), + __func__, ifp->name, inet_ntoa(p->u.prefix4), CHECK_FLAG(ifc->flags, ZEBRA_IFA_SECONDARY) ? "secondary" : "primary"); @@ -326,8 +107,7 @@ static void dump_if_address(struct interface *ifp) } #endif -static int pim_zebra_if_address_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int pim_zebra_if_address_add(ZAPI_CALLBACK_ARGS) { struct connected *c; struct prefix *p; @@ -342,7 +122,7 @@ static int pim_zebra_if_address_add(int command, struct zclient *zclient, will add address to interface list by calling connected_add_by_prefix() */ - c = zebra_interface_address_read(command, zclient->ibuf, vrf_id); + c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); if (!c) return 0; @@ -353,8 +133,7 @@ static int pim_zebra_if_address_add(int command, struct zclient *zclient, char buf[BUFSIZ]; prefix2str(p, buf, BUFSIZ); zlog_debug("%s: %s(%u) connected IP address %s flags %u %s", - __PRETTY_FUNCTION__, c->ifp->name, vrf_id, buf, - c->flags, + __func__, c->ifp->name, vrf_id, buf, c->flags, CHECK_FLAG(c->flags, ZEBRA_IFA_SECONDARY) ? "secondary" : "primary"); @@ -379,7 +158,7 @@ static int pim_zebra_if_address_add(int command, struct zclient *zclient, zlog_warn( "%s: %s : forcing secondary flag on %s", - __PRETTY_FUNCTION__, c->ifp->name, buf); + __func__, c->ifp->name, buf); } SET_FLAG(c->flags, ZEBRA_IFA_SECONDARY); } @@ -406,8 +185,7 @@ static int pim_zebra_if_address_add(int command, struct zclient *zclient, return 0; } -static int pim_zebra_if_address_del(int command, struct zclient *client, - zebra_size_t length, vrf_id_t vrf_id) +static int pim_zebra_if_address_del(ZAPI_CALLBACK_ARGS) { struct connected *c; struct prefix *p; @@ -426,7 +204,7 @@ static int pim_zebra_if_address_del(int command, struct zclient *client, will remove address from interface list by calling connected_delete_by_prefix() */ - c = zebra_interface_address_read(command, client->ibuf, vrf_id); + c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); if (!c) return 0; @@ -437,8 +215,7 @@ static int pim_zebra_if_address_del(int command, struct zclient *client, prefix2str(p, buf, BUFSIZ); zlog_debug( "%s: %s(%u) disconnected IP address %s flags %u %s", - __PRETTY_FUNCTION__, c->ifp->name, vrf_id, buf, - c->flags, + __func__, c->ifp->name, vrf_id, buf, c->flags, CHECK_FLAG(c->flags, ZEBRA_IFA_SECONDARY) ? "secondary" : "primary"); @@ -453,7 +230,7 @@ static int pim_zebra_if_address_del(int command, struct zclient *client, pim_i_am_rp_re_evaluate(pim); } - connected_free(c); + connected_free(&c); return 0; } @@ -491,7 +268,7 @@ void pim_zebra_upstream_rpf_changed(struct pim_instance *pim, nbr = pim_neighbor_find(old->source_nexthop.interface, old->rpf_addr.u.prefix4); if (nbr) - pim_jp_agg_remove_group(nbr->upstream_jp_agg, up); + pim_jp_agg_remove_group(nbr->upstream_jp_agg, up, nbr); /* * We have detected a case where we might need @@ -509,8 +286,8 @@ void pim_zebra_upstream_rpf_changed(struct pim_instance *pim, * so install it. */ if (!up->channel_oil->installed) - pim_mroute_add(up->channel_oil, - __PRETTY_FUNCTION__); + pim_upstream_mroute_add(up->channel_oil, + __func__); /* * RFC 4601: 4.5.7. Sending (S,G) @@ -544,8 +321,11 @@ void pim_zebra_upstream_rpf_changed(struct pim_instance *pim, up->channel_oil->oil_inherited_rescan = 0; } + if (up->join_state == PIM_UPSTREAM_JOINED) + pim_jp_agg_switch_interface(old, &up->rpf, up); + if (!up->channel_oil->installed) - pim_mroute_add(up->channel_oil, __PRETTY_FUNCTION__); + pim_upstream_mroute_add(up->channel_oil, __func__); } /* FIXME can join_desired actually be changed by pim_rpf_update() @@ -554,8 +334,7 @@ void pim_zebra_upstream_rpf_changed(struct pim_instance *pim, pim_upstream_update_join_desired(pim, up); } -static int pim_zebra_vxlan_sg_proc(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int pim_zebra_vxlan_sg_proc(ZAPI_CALLBACK_ARGS) { struct stream *s; struct pim_instance *pim; @@ -577,11 +356,11 @@ static int pim_zebra_vxlan_sg_proc(int command, struct zclient *zclient, pim_str_sg_set(&sg, sg_str); zlog_debug("%u:recv SG %s %s", vrf_id, - (command == ZEBRA_VXLAN_SG_ADD)?"add":"del", + (cmd == ZEBRA_VXLAN_SG_ADD)?"add":"del", sg_str); } - if (command == ZEBRA_VXLAN_SG_ADD) + if (cmd == ZEBRA_VXLAN_SG_ADD) pim_vxlan_sg_add(pim, &sg); else pim_vxlan_sg_del(pim, &sg); @@ -589,163 +368,32 @@ static int pim_zebra_vxlan_sg_proc(int command, struct zclient *zclient, return 0; } -void pim_scan_individual_oil(struct channel_oil *c_oil, int in_vif_index) +static void pim_zebra_vxlan_replay(void) { - struct in_addr vif_source; - int input_iface_vif_index; - int old_vif_index; + struct stream *s = NULL; - pim_rp_set_upstream_addr(c_oil->pim, &vif_source, - c_oil->oil.mfcc_origin, - c_oil->oil.mfcc_mcastgrp); - - if (in_vif_index) - input_iface_vif_index = in_vif_index; - else { - struct prefix src, grp; - - src.family = AF_INET; - src.prefixlen = IPV4_MAX_BITLEN; - src.u.prefix4 = vif_source; - grp.family = AF_INET; - grp.prefixlen = IPV4_MAX_BITLEN; - grp.u.prefix4 = c_oil->oil.mfcc_mcastgrp; - - if (PIM_DEBUG_ZEBRA) { - char source_str[INET_ADDRSTRLEN]; - char group_str[INET_ADDRSTRLEN]; - pim_inet4_dump("", c_oil->oil.mfcc_origin, - source_str, sizeof(source_str)); - pim_inet4_dump("", c_oil->oil.mfcc_mcastgrp, - group_str, sizeof(group_str)); - zlog_debug( - "%s: channel_oil (%s,%s) upstream info is not present.", - __PRETTY_FUNCTION__, source_str, group_str); - } - input_iface_vif_index = pim_ecmp_fib_lookup_if_vif_index( - c_oil->pim, &src, &grp); - } - - if (input_iface_vif_index < 1) { - if (PIM_DEBUG_ZEBRA) { - char source_str[INET_ADDRSTRLEN]; - char group_str[INET_ADDRSTRLEN]; - pim_inet4_dump("", c_oil->oil.mfcc_origin, - source_str, sizeof(source_str)); - pim_inet4_dump("", c_oil->oil.mfcc_mcastgrp, - group_str, sizeof(group_str)); - zlog_debug( - "%s %s: could not find input interface(%d) for (S,G)=(%s,%s)", - __FILE__, __PRETTY_FUNCTION__, - c_oil->oil.mfcc_parent, source_str, group_str); - } - pim_mroute_del(c_oil, __PRETTY_FUNCTION__); - return; - } - - if (input_iface_vif_index == c_oil->oil.mfcc_parent) { - if (!c_oil->installed) - pim_mroute_add(c_oil, __PRETTY_FUNCTION__); - - /* RPF unchanged */ + /* Check socket. */ + if (!zclient || zclient->sock < 0) return; - } - if (PIM_DEBUG_ZEBRA) { - struct interface *old_iif = pim_if_find_by_vif_index( - c_oil->pim, c_oil->oil.mfcc_parent); - struct interface *new_iif = pim_if_find_by_vif_index( - c_oil->pim, input_iface_vif_index); - char source_str[INET_ADDRSTRLEN]; - char group_str[INET_ADDRSTRLEN]; - pim_inet4_dump("", c_oil->oil.mfcc_origin, source_str, - sizeof(source_str)); - pim_inet4_dump("", c_oil->oil.mfcc_mcastgrp, group_str, - sizeof(group_str)); - zlog_debug( - "%s %s: (S,G)=(%s,%s) input interface changed from %s vif_index=%d to %s vif_index=%d", - __FILE__, __PRETTY_FUNCTION__, source_str, group_str, - (old_iif) ? old_iif->name : "", - c_oil->oil.mfcc_parent, - (new_iif) ? new_iif->name : "", - input_iface_vif_index); - } - - /* new iif loops to existing oif ? */ - if (c_oil->oil.mfcc_ttls[input_iface_vif_index]) { - struct interface *new_iif = pim_if_find_by_vif_index( - c_oil->pim, input_iface_vif_index); - - if (PIM_DEBUG_ZEBRA) { - char source_str[INET_ADDRSTRLEN]; - char group_str[INET_ADDRSTRLEN]; - pim_inet4_dump("", c_oil->oil.mfcc_origin, - source_str, sizeof(source_str)); - pim_inet4_dump("", c_oil->oil.mfcc_mcastgrp, - group_str, sizeof(group_str)); - zlog_debug( - "%s %s: (S,G)=(%s,%s) new iif loops to existing oif: %s vif_index=%d", - __FILE__, __PRETTY_FUNCTION__, source_str, - group_str, - (new_iif) ? new_iif->name : "", - input_iface_vif_index); - } - } + s = zclient->obuf; + stream_reset(s); - /* update iif vif_index */ - old_vif_index = c_oil->oil.mfcc_parent; - c_oil->oil.mfcc_parent = input_iface_vif_index; + zclient_create_header(s, ZEBRA_VXLAN_SG_REPLAY, VRF_DEFAULT); + stream_putw_at(s, 0, stream_get_endp(s)); - /* update kernel multicast forwarding cache (MFC) */ - if (pim_mroute_add(c_oil, __PRETTY_FUNCTION__)) { - if (PIM_DEBUG_MROUTE) { - /* just log warning */ - struct interface *old_iif = pim_if_find_by_vif_index( - c_oil->pim, old_vif_index); - struct interface *new_iif = pim_if_find_by_vif_index( - c_oil->pim, input_iface_vif_index); - char source_str[INET_ADDRSTRLEN]; - char group_str[INET_ADDRSTRLEN]; - pim_inet4_dump("", c_oil->oil.mfcc_origin, - source_str, sizeof(source_str)); - pim_inet4_dump("", c_oil->oil.mfcc_mcastgrp, - group_str, sizeof(group_str)); - zlog_debug( - "%s %s: (S,G)=(%s,%s) failure updating input interface from %s vif_index=%d to %s vif_index=%d", - __FILE__, __PRETTY_FUNCTION__, source_str, - group_str, - old_iif ? old_iif->name : "", - c_oil->oil.mfcc_parent, - new_iif ? new_iif->name : "", - input_iface_vif_index); - } - } + zclient_send_message(zclient); } void pim_scan_oil(struct pim_instance *pim) { - struct listnode *node; - struct listnode *nextnode; struct channel_oil *c_oil; - ifindex_t ifindex; - int vif_index = 0; pim->scan_oil_last = pim_time_monotonic_sec(); ++pim->scan_oil_events; - for (ALL_LIST_ELEMENTS(pim->channel_oil_list, node, nextnode, c_oil)) { - if (c_oil->up && c_oil->up->rpf.source_nexthop.interface) { - ifindex = c_oil->up->rpf.source_nexthop - .interface->ifindex; - vif_index = - pim_if_find_vifindex_by_ifindex(pim, ifindex); - /* Pass Current selected NH vif index to mroute - * download */ - if (vif_index) - pim_scan_individual_oil(c_oil, vif_index); - } else - pim_scan_individual_oil(c_oil, 0); - } + frr_each (rb_pim_oil, &pim->channel_oil_head, c_oil) + pim_upstream_mroute_iif_update(c_oil, __func__); } static int on_rpf_cache_refresh(struct thread *t) @@ -777,7 +425,7 @@ void sched_rpf_cache_refresh(struct pim_instance *pim) /* Start refresh timer */ if (PIM_DEBUG_ZEBRA) { - zlog_debug("%s: triggering %ld msec timer", __PRETTY_FUNCTION__, + zlog_debug("%s: triggering %ld msec timer", __func__, router->rpf_cache_refresh_delay_msec); } @@ -789,14 +437,17 @@ void sched_rpf_cache_refresh(struct pim_instance *pim) static void pim_zebra_connected(struct zclient *zclient) { /* Send the client registration */ - bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER); + bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, router->vrf_id); zclient_send_reg_requests(zclient, router->vrf_id); + + /* request for VxLAN BUM group addresses */ + pim_zebra_vxlan_replay(); } static void pim_zebra_capabilities(struct zclient_capabilities *cap) { - router->role = cap->role; + router->mlag_role = cap->role; } void pim_zebra_init(void) @@ -807,21 +458,19 @@ void pim_zebra_init(void) zclient->zebra_capabilities = pim_zebra_capabilities; zclient->zebra_connected = pim_zebra_connected; zclient->router_id_update = pim_router_id_update_zebra; - zclient->interface_add = pim_zebra_if_add; - zclient->interface_delete = pim_zebra_if_del; - zclient->interface_up = pim_zebra_if_state_up; - zclient->interface_down = pim_zebra_if_state_down; zclient->interface_address_add = pim_zebra_if_address_add; zclient->interface_address_delete = pim_zebra_if_address_del; zclient->interface_vrf_update = pim_zebra_interface_vrf_update; zclient->nexthop_update = pim_parse_nexthop_update; zclient->vxlan_sg_add = pim_zebra_vxlan_sg_proc; zclient->vxlan_sg_del = pim_zebra_vxlan_sg_proc; + zclient->mlag_process_up = pim_zebra_mlag_process_up; + zclient->mlag_process_down = pim_zebra_mlag_process_down; + zclient->mlag_handle_msg = pim_zebra_mlag_handle_msg; zclient_init(zclient, ZEBRA_ROUTE_PIM, 0, &pimd_privs); if (PIM_DEBUG_PIM_TRACE) { - zlog_notice("%s: zclient socket initialized", - __PRETTY_FUNCTION__); + zlog_notice("%s: zclient socket initialized", __func__); } zclient_lookup_new(); @@ -838,8 +487,7 @@ void igmp_anysource_forward_start(struct pim_instance *pim, source = source_new(group, src_addr); if (!source) { - zlog_warn("%s: Failure to create * source", - __PRETTY_FUNCTION__); + zlog_warn("%s: Failure to create * source", __func__); return; } @@ -892,7 +540,8 @@ static void igmp_source_forward_reevaluate_one(struct pim_instance *pim, "local membership add for %s as G is now ASM", pim_str_sg_dump(&sg)); pim_ifchannel_local_membership_add( - group->group_igmp_sock->interface, &sg); + group->group_igmp_sock->interface, &sg, + false /*is_vxlan*/); } } } @@ -948,8 +597,8 @@ void igmp_source_forward_start(struct pim_instance *pim, if (PIM_DEBUG_IGMP_TRACE) { zlog_debug( - "%s: (S,G)=%s igmp_sock=%d oif=%s fwd=%d", - __PRETTY_FUNCTION__, pim_str_sg_dump(&sg), + "%s: (S,G)=%s igmp_sock=%d oif=%s fwd=%d", __func__, + pim_str_sg_dump(&sg), source->source_group->group_igmp_sock->fd, source->source_group->group_igmp_sock->interface->name, IGMP_SOURCE_TEST_FORWARDING(source->source_flags)); @@ -965,11 +614,10 @@ void igmp_source_forward_start(struct pim_instance *pim, pim_oif = group->group_igmp_sock->interface->info; if (!pim_oif) { if (PIM_DEBUG_IGMP_TRACE) { - zlog_debug( - "%s: multicast not enabled on oif=%s ?", - __PRETTY_FUNCTION__, + zlog_debug("%s: multicast not enabled on oif=%s ?", + __func__, source->source_group->group_igmp_sock - ->interface->name); + ->interface->name); } return; } @@ -984,17 +632,7 @@ void igmp_source_forward_start(struct pim_instance *pim, source->source_addr, sg.grp)) { /*Create a dummy channel oil */ source->source_channel_oil = - pim_channel_oil_add(pim, &sg, MAXVIFS); - - if (!source->source_channel_oil) { - if (PIM_DEBUG_IGMP_TRACE) { - zlog_debug( - "%s %s: could not create OIL for channel (S,G)=%s", - __FILE__, __PRETTY_FUNCTION__, - pim_str_sg_dump(&sg)); - } - return; - } + pim_channel_oil_add(pim, &sg, __func__); } else { @@ -1026,10 +664,10 @@ void igmp_source_forward_start(struct pim_instance *pim, pim_inet4_dump("", vif_source, buf2, sizeof(buf2)); - zlog_debug("%s: NHT %s vif_source %s vif_index:%d ", - __PRETTY_FUNCTION__, - pim_str_sg_dump(&sg), - buf2, input_iface_vif_index); + zlog_debug( + "%s: NHT %s vif_source %s vif_index:%d ", + __func__, pim_str_sg_dump(&sg), buf2, + input_iface_vif_index); } if (input_iface_vif_index < 1) { @@ -1039,12 +677,11 @@ void igmp_source_forward_start(struct pim_instance *pim, source->source_addr, source_str, sizeof(source_str)); zlog_debug( - "%s %s: could not find input interface for source %s", - __FILE__, __PRETTY_FUNCTION__, - source_str); + "%s %s: could not find input interface for source %s", + __FILE__, __func__, source_str); } source->source_channel_oil = - pim_channel_oil_add(pim, &sg, MAXVIFS); + pim_channel_oil_add(pim, &sg, __func__); } else { @@ -1052,37 +689,38 @@ void igmp_source_forward_start(struct pim_instance *pim, * Protect IGMP against adding looped MFC * entries created by both source and receiver * attached to the same interface. See TODO - * T22. + * T22. Block only when the intf is non DR + * DR must create upstream. */ - if (input_iface_vif_index == - pim_oif->mroute_vif_index) { + if ((input_iface_vif_index == + pim_oif->mroute_vif_index) && + !(PIM_I_am_DR(pim_oif))) { /* ignore request for looped MFC entry */ if (PIM_DEBUG_IGMP_TRACE) { zlog_debug( - "%s: ignoring request for looped MFC entry (S,G)=%s: igmp_sock=%d oif=%s vif_index=%d", - __PRETTY_FUNCTION__, - pim_str_sg_dump(&sg), - source->source_group - ->group_igmp_sock->fd, - source->source_group - ->group_igmp_sock - ->interface->name, - input_iface_vif_index); + "%s: ignoring request for looped MFC entry (S,G)=%s: igmp_sock=%d oif=%s vif_index=%d", + __func__, + pim_str_sg_dump(&sg), + source->source_group + ->group_igmp_sock + ->fd, + source->source_group + ->group_igmp_sock + ->interface->name, + input_iface_vif_index); } return; } source->source_channel_oil = - pim_channel_oil_add(pim, &sg, - input_iface_vif_index); + pim_channel_oil_add(pim, &sg, __func__); if (!source->source_channel_oil) { if (PIM_DEBUG_IGMP_TRACE) { zlog_debug( - "%s %s: could not create OIL for channel (S,G)=%s", - __FILE__, - __PRETTY_FUNCTION__, - pim_str_sg_dump(&sg)); + "%s %s: could not create OIL for channel (S,G)=%s", + __FILE__, __func__, + pim_str_sg_dump(&sg)); } return; } @@ -1090,27 +728,24 @@ void igmp_source_forward_start(struct pim_instance *pim, } } - result = pim_channel_add_oif(source->source_channel_oil, - group->group_igmp_sock->interface, - PIM_OIF_FLAG_PROTO_IGMP); - if (result) { - if (PIM_DEBUG_MROUTE) { - zlog_warn("%s: add_oif() failed with return=%d", - __func__, result); + if (PIM_I_am_DR(pim_oif) || PIM_I_am_DualActive(pim_oif)) { + result = pim_channel_add_oif(source->source_channel_oil, + group->group_igmp_sock->interface, + PIM_OIF_FLAG_PROTO_IGMP, __func__); + if (result) { + if (PIM_DEBUG_MROUTE) { + zlog_warn("%s: add_oif() failed with return=%d", + __func__, result); + } + return; } - return; - } - - if (!(PIM_I_am_DR(pim_oif))) { + } else { if (PIM_DEBUG_IGMP_TRACE) - zlog_debug("%s: %s was received on %s interface but we are not DR for that interface", - __PRETTY_FUNCTION__, - pim_str_sg_dump(&sg), - group->group_igmp_sock->interface->name); + zlog_debug( + "%s: %s was received on %s interface but we are not DR for that interface", + __func__, pim_str_sg_dump(&sg), + group->group_igmp_sock->interface->name); - pim_channel_del_oif(source->source_channel_oil, - group->group_igmp_sock->interface, - PIM_OIF_FLAG_PROTO_IGMP); return; } /* @@ -1118,14 +753,15 @@ void igmp_source_forward_start(struct pim_instance *pim, per-interface (S,G) state. */ if (!pim_ifchannel_local_membership_add( - group->group_igmp_sock->interface, &sg)) { + group->group_igmp_sock->interface, &sg, + false /*is_vxlan*/)) { if (PIM_DEBUG_MROUTE) zlog_warn("%s: Failure to add local membership for %s", - __PRETTY_FUNCTION__, pim_str_sg_dump(&sg)); + __func__, pim_str_sg_dump(&sg)); pim_channel_del_oif(source->source_channel_oil, group->group_igmp_sock->interface, - PIM_OIF_FLAG_PROTO_IGMP); + PIM_OIF_FLAG_PROTO_IGMP, __func__); return; } @@ -1148,8 +784,8 @@ void igmp_source_forward_stop(struct igmp_source *source) if (PIM_DEBUG_IGMP_TRACE) { zlog_debug( - "%s: (S,G)=%s igmp_sock=%d oif=%s fwd=%d", - __PRETTY_FUNCTION__, pim_str_sg_dump(&sg), + "%s: (S,G)=%s igmp_sock=%d oif=%s fwd=%d", __func__, + pim_str_sg_dump(&sg), source->source_group->group_igmp_sock->fd, source->source_group->group_igmp_sock->interface->name, IGMP_SOURCE_TEST_FORWARDING(source->source_flags)); @@ -1176,7 +812,8 @@ void igmp_source_forward_stop(struct igmp_source *source) */ result = pim_channel_del_oif(source->source_channel_oil, group->group_igmp_sock->interface, - PIM_OIF_FLAG_PROTO_IGMP); + PIM_OIF_FLAG_PROTO_IGMP, + __func__); if (result) { if (PIM_DEBUG_IGMP_TRACE) zlog_debug( @@ -1198,13 +835,7 @@ void igmp_source_forward_stop(struct igmp_source *source) void pim_forward_start(struct pim_ifchannel *ch) { struct pim_upstream *up = ch->upstream; - uint32_t mask = PIM_OIF_FLAG_PROTO_PIM; - int input_iface_vif_index = 0; - struct pim_instance *pim; - struct pim_interface *pim_ifp; - - pim_ifp = ch->interface->info; - pim = pim_ifp->pim; + uint32_t mask = 0; if (PIM_DEBUG_PIM_TRACE) { char source_str[INET_ADDRSTRLEN]; @@ -1217,82 +848,19 @@ void pim_forward_start(struct pim_ifchannel *ch) sizeof(group_str)); pim_inet4_dump("", up->upstream_addr, upstream_str, sizeof(upstream_str)); - zlog_debug("%s: (S,G)=(%s,%s) oif=%s (%s)", __PRETTY_FUNCTION__, + zlog_debug("%s: (S,G)=(%s,%s) oif=%s (%s)", __func__, source_str, group_str, ch->interface->name, inet_ntoa(up->upstream_addr)); } - /* Resolve IIF for upstream as mroute_del sets mfcc_parent to MAXVIFS, - as part of mroute_del called by pim_forward_stop. - */ - if ((up->upstream_addr.s_addr != INADDR_ANY) && (!up->channel_oil)) { - struct prefix src, grp; - - grp.family = AF_INET; - grp.prefixlen = IPV4_MAX_BITLEN; - grp.u.prefix4 = up->sg.grp; - src.family = AF_INET; - src.prefixlen = IPV4_MAX_BITLEN; - src.u.prefix4 = up->sg.src; - - if (pim_ecmp_nexthop_lookup(pim, &up->rpf.source_nexthop, &src, - &grp, 0)) - input_iface_vif_index = pim_if_find_vifindex_by_ifindex( - pim, up->rpf.source_nexthop.interface->ifindex); - - if (input_iface_vif_index < 1) { - if (PIM_DEBUG_PIM_TRACE) { - char source_str[INET_ADDRSTRLEN]; - pim_inet4_dump("", up->sg.src, - source_str, sizeof(source_str)); - zlog_debug( - "%s %s: could not find input interface for source %s", - __FILE__, __PRETTY_FUNCTION__, - source_str); - } - up->channel_oil = pim_channel_oil_add(pim, &up->sg, - MAXVIFS); - } - - else { - up->channel_oil = pim_channel_oil_add(pim, &up->sg, - input_iface_vif_index); - if (!up->channel_oil) { - if (PIM_DEBUG_PIM_TRACE) - zlog_debug( - "%s %s: could not create OIL for channel (S,G)=%s", - __FILE__, __PRETTY_FUNCTION__, - up->sg_str); - return; - } - } - - if (PIM_DEBUG_TRACE) { - struct interface *in_intf = pim_if_find_by_vif_index( - pim, input_iface_vif_index); - zlog_debug( - "%s: Update channel_oil IIF %s VIFI %d entry %s ", - __PRETTY_FUNCTION__, - in_intf ? in_intf->name : "Unknown", - input_iface_vif_index, up->sg_str); - } - - up->channel_oil = pim_channel_oil_add(pim, &up->sg, - input_iface_vif_index); - if (!up->channel_oil) { - if (PIM_DEBUG_PIM_TRACE) - zlog_debug( - "%s %s: could not create OIL for channel (S,G)=%s", - __FILE__, __PRETTY_FUNCTION__, - up->sg_str); - return; - } - } - - if (up->flags & PIM_UPSTREAM_FLAG_MASK_SRC_IGMP) + if (PIM_IF_FLAG_TEST_PROTO_IGMP(ch->flags)) mask = PIM_OIF_FLAG_PROTO_IGMP; - pim_channel_add_oif(up->channel_oil, ch->interface, mask); + if (PIM_IF_FLAG_TEST_PROTO_PIM(ch->flags)) + mask |= PIM_OIF_FLAG_PROTO_PIM; + + pim_channel_add_oif(up->channel_oil, ch->interface, + mask, __func__); } void pim_forward_stop(struct pim_ifchannel *ch, bool install_it) @@ -1301,15 +869,23 @@ void pim_forward_stop(struct pim_ifchannel *ch, bool install_it) if (PIM_DEBUG_PIM_TRACE) { zlog_debug("%s: (S,G)=%s oif=%s install_it: %d installed: %d", - __PRETTY_FUNCTION__, ch->sg_str, ch->interface->name, + __func__, ch->sg_str, ch->interface->name, install_it, up->channel_oil->installed); } - pim_channel_del_oif(up->channel_oil, ch->interface, - PIM_OIF_FLAG_PROTO_PIM); + /* + * If a channel is being removed, check to see if we still need + * to inherit the interface. If so make sure it is added in + */ + if (pim_upstream_evaluate_join_desired_interface(up, ch, ch->parent)) + pim_channel_add_oif(up->channel_oil, ch->interface, + PIM_OIF_FLAG_PROTO_PIM, __func__); + else + pim_channel_del_oif(up->channel_oil, ch->interface, + PIM_OIF_FLAG_PROTO_PIM, __func__); if (install_it && !up->channel_oil->installed) - pim_mroute_add(up->channel_oil, __PRETTY_FUNCTION__); + pim_upstream_mroute_add(up->channel_oil, __func__); } void pim_zebra_zclient_update(struct vty *vty) @@ -1330,3 +906,9 @@ struct zclient *pim_zebra_zclient_get(void) else return NULL; } + +void pim_zebra_interface_set_master(struct interface *vrf, + struct interface *ifp) +{ + zclient_interface_set_master(zclient, vrf, ifp); +} diff --git a/pimd/pim_zebra.h b/pimd/pim_zebra.h index c9ed89863c..0f216cf5c9 100644 --- a/pimd/pim_zebra.h +++ b/pimd/pim_zebra.h @@ -51,4 +51,7 @@ void pim_zebra_update_all_interfaces(struct pim_instance *pim); void pim_zebra_upstream_rpf_changed(struct pim_instance *pim, struct pim_upstream *up, struct pim_rpf *old); + +void pim_zebra_interface_set_master(struct interface *vrf, + struct interface *ifp); #endif /* PIM_ZEBRA_H */ diff --git a/pimd/pim_zlookup.c b/pimd/pim_zlookup.c index 6d93a40b91..dc4c621e9c 100644 --- a/pimd/pim_zlookup.c +++ b/pimd/pim_zlookup.c @@ -37,8 +37,10 @@ #include "pim_zlookup.h" static struct zclient *zlookup = NULL; +struct thread *zlookup_read; static void zclient_lookup_sched(struct zclient *zlookup, int delay); +static int zclient_lookup_read_pipe(struct thread *thread); /* Connect to zebra for nexthop lookup. */ static int zclient_lookup_connect(struct thread *t) @@ -54,17 +56,27 @@ static int zclient_lookup_connect(struct thread *t) if (zclient_socket_connect(zlookup) < 0) { ++zlookup->fail; zlog_warn("%s: failure connecting zclient socket: failures=%d", - __PRETTY_FUNCTION__, zlookup->fail); + __func__, zlookup->fail); } else { zlookup->fail = 0; /* reset counter on connection */ } + if (zclient_send_hello(zlookup) < 0) { + if (close(zlookup->sock)) { + zlog_warn("%s: closing fd=%d: errno=%d %s", __func__, + zlookup->sock, errno, safe_strerror(errno)); + } + zlookup->sock = -1; + } + if (zlookup->sock < 0) { /* Since last connect failed, retry within 10 secs */ zclient_lookup_sched(zlookup, 10); return -1; } + thread_add_timer(router->master, zclient_lookup_read_pipe, zlookup, 60, + &zlookup_read); return 0; } @@ -75,7 +87,7 @@ static void zclient_lookup_sched(struct zclient *zlookup, int delay) &zlookup->t_connect); zlog_notice("%s: zclient lookup connection scheduled for %d seconds", - __PRETTY_FUNCTION__, delay); + __func__, delay); } /* Schedule connection for now. */ @@ -85,7 +97,7 @@ static void zclient_lookup_sched_now(struct zclient *zlookup) &zlookup->t_connect); zlog_notice("%s: zclient lookup immediate connection scheduled", - __PRETTY_FUNCTION__); + __func__); } /* Schedule reconnection, if needed. */ @@ -113,6 +125,7 @@ static void zclient_lookup_failed(struct zclient *zlookup) void zclient_lookup_free(void) { + THREAD_OFF(zlookup_read); zclient_stop(zlookup); zclient_free(zlookup); zlookup = NULL; @@ -120,10 +133,13 @@ void zclient_lookup_free(void) void zclient_lookup_new(void) { - zlookup = zclient_new(router->master, &zclient_options_default); + struct zclient_options options = zclient_options_default; + options.synchronous = true; + + zlookup = zclient_new(router->master, &options); if (!zlookup) { flog_err(EC_LIB_ZAPI_SOCKET, "%s: zclient_new() failure", - __PRETTY_FUNCTION__); + __func__); return; } @@ -133,8 +149,7 @@ void zclient_lookup_new(void) zclient_lookup_sched_now(zlookup); - zlog_notice("%s: zclient lookup socket initialized", - __PRETTY_FUNCTION__); + zlog_notice("%s: zclient lookup socket initialized", __func__); } static int zclient_read_nexthop(struct pim_instance *pim, @@ -157,8 +172,9 @@ static int zclient_read_nexthop(struct pim_instance *pim, if (PIM_DEBUG_PIM_NHT_DETAIL) { char addr_str[INET_ADDRSTRLEN]; + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); - zlog_debug("%s: addr=%s(%s)", __PRETTY_FUNCTION__, addr_str, + zlog_debug("%s: addr=%s(%s)", __func__, addr_str, pim->vrf->name); } @@ -170,11 +186,18 @@ static int zclient_read_nexthop(struct pim_instance *pim, &version, &vrf_id, &command); if (err < 0) { flog_err(EC_LIB_ZAPI_MISSMATCH, - "%s: zclient_read_header() failed", - __PRETTY_FUNCTION__); + "%s: zclient_read_header() failed", __func__); zclient_lookup_failed(zlookup); return -1; } + + if (command == ZEBRA_ERROR) { + enum zebra_error_types error; + + zapi_error_decode(s, &error); + /* Do nothing with it for now */ + return -1; + } } raddr.s_addr = stream_get_ipv4(s); @@ -185,8 +208,7 @@ static int zclient_read_nexthop(struct pim_instance *pim, pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); pim_inet4_dump("", raddr, raddr_str, sizeof(raddr_str)); zlog_warn("%s: address mismatch: addr=%s(%s) raddr=%s", - __PRETTY_FUNCTION__, addr_str, pim->vrf->name, - raddr_str); + __func__, addr_str, pim->vrf->name, raddr_str); /* warning only */ } @@ -215,8 +237,8 @@ static int zclient_read_nexthop(struct pim_instance *pim, sizeof(addr_str)); zlog_warn( "%s: found too many nexthop ifindexes (%d > %d) for address %s(%s)", - __PRETTY_FUNCTION__, (num_ifindex + 1), - tab_size, addr_str, pim->vrf->name); + __func__, (num_ifindex + 1), tab_size, addr_str, + pim->vrf->name); return num_ifindex; } nexthop_tab[num_ifindex].protocol_distance = distance; @@ -263,17 +285,17 @@ static int zclient_read_nexthop(struct pim_instance *pim, * If we are sending v6 secondary assume we receive v6 * secondary */ - if (pim->send_v6_secondary) - nbr = pim_neighbor_find_by_secondary( - if_lookup_by_index( - nexthop_tab[num_ifindex] - .ifindex, - nexthop_vrf_id), - &p); + struct interface *ifp = if_lookup_by_index( + nexthop_tab[num_ifindex].ifindex, + nexthop_vrf_id); + + if (!ifp) + nbr = NULL; + else if (pim->send_v6_secondary) + nbr = pim_neighbor_find_by_secondary(ifp, &p); else - nbr = pim_neighbor_find_if(if_lookup_by_index( - nexthop_tab[num_ifindex].ifindex, - nexthop_vrf_id)); + nbr = pim_neighbor_find_if(ifp); + if (nbr) { nexthop_tab[num_ifindex].nexthop_addr.family = AF_INET; @@ -291,8 +313,8 @@ static int zclient_read_nexthop(struct pim_instance *pim, sizeof(addr_str)); zlog_warn( "%s: found non-ifindex nexthop type=%d for address %s(%s)", - __PRETTY_FUNCTION__, nexthop_type, - addr_str, pim->vrf->name); + __func__, nexthop_type, addr_str, + pim->vrf->name); } break; } @@ -311,7 +333,7 @@ static int zclient_lookup_nexthop_once(struct pim_instance *pim, if (PIM_DEBUG_PIM_NHT_DETAIL) { char addr_str[INET_ADDRSTRLEN]; pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); - zlog_debug("%s: addr=%s(%s)", __PRETTY_FUNCTION__, addr_str, + zlog_debug("%s: addr=%s(%s)", __func__, addr_str, pim->vrf->name); } @@ -319,7 +341,7 @@ static int zclient_lookup_nexthop_once(struct pim_instance *pim, if (zlookup->sock < 0) { flog_err(EC_LIB_ZAPI_SOCKET, "%s: zclient lookup socket is not connected", - __PRETTY_FUNCTION__); + __func__); zclient_lookup_failed(zlookup); return -1; } @@ -327,13 +349,14 @@ static int zclient_lookup_nexthop_once(struct pim_instance *pim, if (pim->vrf->vrf_id == VRF_UNKNOWN) { zlog_notice( "%s: VRF: %s does not fully exist yet, delaying lookup", - __PRETTY_FUNCTION__, pim->vrf->name); + __func__, pim->vrf->name); return -1; } s = zlookup->obuf; stream_reset(s); - zclient_create_header(s, ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB, pim->vrf_id); + zclient_create_header(s, ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB, + pim->vrf->vrf_id); stream_put_in_addr(s, &addr); stream_putw_at(s, 0, stream_get_endp(s)); @@ -342,14 +365,14 @@ static int zclient_lookup_nexthop_once(struct pim_instance *pim, flog_err( EC_LIB_SOCKET, "%s: writen() failure: %d writing to zclient lookup socket", - __PRETTY_FUNCTION__, errno); + __func__, errno); zclient_lookup_failed(zlookup); return -2; } if (ret == 0) { flog_err_sys(EC_LIB_SOCKET, "%s: connection closed on zclient lookup socket", - __PRETTY_FUNCTION__); + __func__); zclient_lookup_failed(zlookup); return -3; } @@ -357,6 +380,20 @@ static int zclient_lookup_nexthop_once(struct pim_instance *pim, return zclient_read_nexthop(pim, zlookup, nexthop_tab, tab_size, addr); } +int zclient_lookup_read_pipe(struct thread *thread) +{ + struct zclient *zlookup = THREAD_ARG(thread); + struct pim_instance *pim = pim_get_pim_instance(VRF_DEFAULT); + struct pim_zlookup_nexthop nexthop_tab[10]; + struct in_addr l = {.s_addr = INADDR_ANY}; + + zclient_lookup_nexthop_once(pim, nexthop_tab, 10, l); + thread_add_timer(router->master, zclient_lookup_read_pipe, zlookup, 60, + &zlookup_read); + + return 1; +} + int zclient_lookup_nexthop(struct pim_instance *pim, struct pim_zlookup_nexthop nexthop_tab[], const int tab_size, struct in_addr addr, @@ -382,8 +419,8 @@ int zclient_lookup_nexthop(struct pim_instance *pim, sizeof(addr_str)); zlog_debug( "%s: lookup=%d/%d: could not find nexthop ifindex for address %s(%s)", - __PRETTY_FUNCTION__, lookup, max_lookup, - addr_str, pim->vrf->name); + __func__, lookup, max_lookup, addr_str, + pim->vrf->name); } return -1; } @@ -420,9 +457,9 @@ int zclient_lookup_nexthop(struct pim_instance *pim, sizeof(addr_str)); zlog_debug( "%s: lookup=%d/%d: found non-recursive ifindex=%d for address %s(%s) dist=%d met=%d", - __PRETTY_FUNCTION__, lookup, - max_lookup, first_ifindex, - addr_str, pim->vrf->name, + __func__, lookup, max_lookup, + first_ifindex, addr_str, + pim->vrf->name, nexthop_tab[0] .protocol_distance, nexthop_tab[0].route_metric); @@ -449,8 +486,8 @@ int zclient_lookup_nexthop(struct pim_instance *pim, sizeof(nexthop_str)); zlog_debug( "%s: lookup=%d/%d: zebra returned recursive nexthop %s for address %s(%s) dist=%d met=%d", - __PRETTY_FUNCTION__, lookup, max_lookup, - nexthop_str, addr_str, pim->vrf->name, + __func__, lookup, max_lookup, nexthop_str, + addr_str, pim->vrf->name, nexthop_tab[0].protocol_distance, nexthop_tab[0].route_metric); } @@ -465,8 +502,7 @@ int zclient_lookup_nexthop(struct pim_instance *pim, pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); zlog_warn( "%s: lookup=%d/%d: failure searching recursive nexthop ifindex for address %s(%s)", - __PRETTY_FUNCTION__, lookup, max_lookup, addr_str, - pim->vrf->name); + __func__, lookup, max_lookup, addr_str, pim->vrf->name); } return -2; @@ -520,7 +556,7 @@ int pim_zlookup_sg_statistics(struct channel_oil *c_oil) flog_err( EC_LIB_SOCKET, "%s: writen() failure: %d writing to zclient lookup socket", - __PRETTY_FUNCTION__, errno); + __func__, errno); return -1; } @@ -538,8 +574,7 @@ int pim_zlookup_sg_statistics(struct channel_oil *c_oil) &version, &vrf_id, &command); if (err < 0) { flog_err(EC_LIB_ZAPI_MISSMATCH, - "%s: zclient_read_header() failed", - __PRETTY_FUNCTION__); + "%s: zclient_read_header() failed", __func__); zclient_lookup_failed(zlookup); return -1; } @@ -557,7 +592,7 @@ int pim_zlookup_sg_statistics(struct channel_oil *c_oil) flog_err( EC_LIB_ZAPI_MISSMATCH, "%s: Received wrong %s(%s) information requested", - __PRETTY_FUNCTION__, pim_str_sg_dump(&more), + __func__, pim_str_sg_dump(&more), c_oil->pim->vrf->name); } zclient_lookup_failed(zlookup); diff --git a/pimd/pim_zpthread.c b/pimd/pim_zpthread.c new file mode 100644 index 0000000000..518b024749 --- /dev/null +++ b/pimd/pim_zpthread.c @@ -0,0 +1,225 @@ +/* + * PIM for Quagga + * Copyright (C) 2008 Everton da Silva Marques + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include + +#include "pimd.h" +#include "pim_mlag.h" +#include "pim_zebra.h" + +extern struct zclient *zclient; + +#define PIM_MLAG_POST_LIMIT 100 + +int32_t mlag_bulk_cnt; + +static void pim_mlag_zebra_fill_header(enum mlag_msg_type msg_type) +{ + uint32_t fill_msg_type = msg_type; + uint16_t data_len; + uint16_t msg_cnt = 1; + + if (msg_type == MLAG_MSG_NONE) + return; + + switch (msg_type) { + case MLAG_REGISTER: + case MLAG_DEREGISTER: + data_len = sizeof(struct mlag_msg); + break; + case MLAG_MROUTE_ADD: + data_len = sizeof(struct mlag_mroute_add); + fill_msg_type = MLAG_MROUTE_ADD_BULK; + break; + case MLAG_MROUTE_DEL: + data_len = sizeof(struct mlag_mroute_del); + fill_msg_type = MLAG_MROUTE_DEL_BULK; + break; + default: + data_len = 0; + break; + } + + stream_reset(router->mlag_stream); + /* ADD Hedaer */ + stream_putl(router->mlag_stream, fill_msg_type); + /* + * In case of Bulk actual size & msg_cnt will be updated + * just before writing onto zebra + */ + stream_putw(router->mlag_stream, data_len); + stream_putw(router->mlag_stream, msg_cnt); + + if (PIM_DEBUG_MLAG) + zlog_debug(":%s: msg_type: %d/%d len %d", + __func__, msg_type, fill_msg_type, data_len); +} + +static void pim_mlag_zebra_flush_buffer(void) +{ + uint32_t msg_type; + + /* Stream had bulk messages update the Hedaer */ + if (mlag_bulk_cnt > 1) { + /* + * No need to reset the pointer, below api reads from data[0] + */ + STREAM_GETL(router->mlag_stream, msg_type); + if (msg_type == MLAG_MROUTE_ADD_BULK) { + stream_putw_at( + router->mlag_stream, 4, + (mlag_bulk_cnt * sizeof(struct mlag_mroute_add))); + stream_putw_at(router->mlag_stream, 6, mlag_bulk_cnt); + } else if (msg_type == MLAG_MROUTE_DEL_BULK) { + stream_putw_at( + router->mlag_stream, 4, + (mlag_bulk_cnt * sizeof(struct mlag_mroute_del))); + stream_putw_at(router->mlag_stream, 6, mlag_bulk_cnt); + } else { + flog_err(EC_LIB_ZAPI_ENCODE, + "unknown bulk message type %d bulk_count %d", + msg_type, mlag_bulk_cnt); + stream_reset(router->mlag_stream); + mlag_bulk_cnt = 0; + return; + } + } + + zclient_send_mlag_data(zclient, router->mlag_stream); +stream_failure: + stream_reset(router->mlag_stream); + mlag_bulk_cnt = 0; +} + +/* + * Only ROUTE add & Delete will be bulked. + * Buffer will be flushed, when + * 1) there were no messages in the queue + * 2) Curr_msg_type != prev_msg_type + */ + +static void pim_mlag_zebra_check_for_buffer_flush(uint32_t curr_msg_type, + uint32_t prev_msg_type) +{ + /* First Message, keep bulking */ + if (prev_msg_type == MLAG_MSG_NONE) { + mlag_bulk_cnt = 1; + return; + } + + /*msg type is route add & delete, keep bulking */ + if (curr_msg_type == prev_msg_type + && (curr_msg_type == MLAG_MROUTE_ADD + || curr_msg_type == MLAG_MROUTE_DEL)) { + mlag_bulk_cnt++; + return; + } + + pim_mlag_zebra_flush_buffer(); +} + +/* + * Thsi thread reads the clients data from the Gloabl queue and encodes with + * protobuf and pass on to the MLAG socket. + */ +static int pim_mlag_zthread_handler(struct thread *event) +{ + struct stream *read_s; + uint32_t wr_count = 0; + uint32_t prev_msg_type = MLAG_MSG_NONE; + uint32_t curr_msg_type = MLAG_MSG_NONE; + + router->zpthread_mlag_write = NULL; + wr_count = stream_fifo_count_safe(router->mlag_fifo); + + if (PIM_DEBUG_MLAG) + zlog_debug(":%s: Processing MLAG write, %d messages in queue", + __func__, wr_count); + + if (wr_count == 0) + return 0; + + for (wr_count = 0; wr_count < PIM_MLAG_POST_LIMIT; wr_count++) { + /* FIFO is empty,wait for teh message to be add */ + if (stream_fifo_count_safe(router->mlag_fifo) == 0) + break; + + read_s = stream_fifo_pop_safe(router->mlag_fifo); + if (!read_s) { + zlog_debug(":%s: Got a NULL Messages, some thing wrong", + __func__); + break; + } + STREAM_GETL(read_s, curr_msg_type); + /* + * Check for Buffer Overflow, + * MLAG Can't process more than 'PIM_MLAG_BUF_LIMIT' bytes + */ + if (router->mlag_stream->endp + read_s->endp + ZEBRA_HEADER_SIZE + > MLAG_BUF_LIMIT) + pim_mlag_zebra_flush_buffer(); + + pim_mlag_zebra_check_for_buffer_flush(curr_msg_type, + prev_msg_type); + + /* + * First message to Buffer, fill the Header + */ + if (router->mlag_stream->endp == 0) + pim_mlag_zebra_fill_header(curr_msg_type); + + /* + * add the data now + */ + stream_put(router->mlag_stream, read_s->data + read_s->getp, + read_s->endp - read_s->getp); + + stream_free(read_s); + prev_msg_type = curr_msg_type; + } + +stream_failure: + /* + * we are here , because + * 1. Queue might be empty + * 2. we crossed the max Q Read limit + * In any acse flush the buffer towards zebra + */ + pim_mlag_zebra_flush_buffer(); + + if (wr_count >= PIM_MLAG_POST_LIMIT) + pim_mlag_signal_zpthread(); + + return 0; +} + + +int pim_mlag_signal_zpthread(void) +{ + if (router->master) { + if (PIM_DEBUG_MLAG) + zlog_debug(":%s: Scheduling PIM MLAG write Thread", + __func__); + thread_add_event(router->master, pim_mlag_zthread_handler, NULL, + 0, &router->zpthread_mlag_write); + } + return (0); +} diff --git a/pimd/pimd.c b/pimd/pimd.c index 889a83a136..811dc96b56 100644 --- a/pimd/pimd.c +++ b/pimd/pimd.c @@ -39,8 +39,14 @@ #include "pim_static.h" #include "pim_rp.h" #include "pim_ssm.h" +#include "pim_vxlan.h" #include "pim_zlookup.h" #include "pim_zebra.h" +#include "pim_mlag.h" + +#if MAXVIFS > 256 +CPP_NOTICE("Work needs to be done to make this work properly via the pim mroute socket\n"); +#endif /* MAXVIFS > 256 */ const char *const PIM_ALL_SYSTEMS = MCAST_ALL_SYSTEMS; const char *const PIM_ALL_ROUTERS = MCAST_ALL_ROUTERS; @@ -50,6 +56,7 @@ const char *const PIM_ALL_IGMP_ROUTERS = MCAST_ALL_IGMP_ROUTERS; DEFINE_MTYPE_STATIC(PIMD, ROUTER, "PIM Router information"); struct pim_router *router = NULL; +struct in_addr qpim_all_pim_routers_addr; void pim_prefix_list_update(struct prefix_list *plist) { @@ -101,10 +108,13 @@ void pim_router_init(void) router->packet_process = PIM_DEFAULT_PACKET_PROCESS; router->register_probe_time = PIM_REGISTER_PROBE_TIME_DEFAULT; router->vrf_id = VRF_DEFAULT; + router->pim_mlag_intf_cnt = 0; + router->connected_to_mlag = false; } void pim_router_terminate(void) { + pim_mlag_terminate(); XFREE(MTYPE_ROUTER, router); } @@ -114,8 +124,8 @@ void pim_init(void) flog_err( EC_LIB_SOCKET, "%s %s: could not solve %s to group address: errno=%d: %s", - __FILE__, __PRETTY_FUNCTION__, PIM_ALL_PIM_ROUTERS, - errno, safe_strerror(errno)); + __FILE__, __func__, PIM_ALL_PIM_ROUTERS, errno, + safe_strerror(errno)); zassert(0); return; } @@ -132,6 +142,7 @@ void pim_terminate(void) prefix_list_delete_hook(NULL); prefix_list_reset(); + pim_vxlan_terminate(); pim_vrf_terminate(); zclient = pim_zebra_zclient_get(); diff --git a/pimd/pimd.h b/pimd/pimd.h index 2f2a870371..88e692b50d 100644 --- a/pimd/pimd.h +++ b/pimd/pimd.h @@ -114,6 +114,8 @@ #define PIM_MASK_PIM_NHT_RP (1 << 24) #define PIM_MASK_MTRACE (1 << 25) #define PIM_MASK_VXLAN (1 << 26) +#define PIM_MASK_BSM_PROC (1 << 27) +#define PIM_MASK_MLAG (1 << 28) /* Remember 32 bits!!! */ /* PIM error codes */ @@ -129,14 +131,14 @@ #define PIM_UPDATE_SOURCE_DUP -10 #define PIM_GROUP_BAD_ADDR_MASK_COMBO -11 -const char *const PIM_ALL_SYSTEMS; -const char *const PIM_ALL_ROUTERS; -const char *const PIM_ALL_PIM_ROUTERS; -const char *const PIM_ALL_IGMP_ROUTERS; +extern const char *const PIM_ALL_SYSTEMS; +extern const char *const PIM_ALL_ROUTERS; +extern const char *const PIM_ALL_PIM_ROUTERS; +extern const char *const PIM_ALL_IGMP_ROUTERS; extern struct pim_router *router; extern struct zebra_privs_t pimd_privs; -struct in_addr qpim_all_pim_routers_addr; +extern struct in_addr qpim_all_pim_routers_addr; extern uint8_t qpim_ecmp_enable; extern uint8_t qpim_ecmp_rebalance_enable; @@ -153,23 +155,29 @@ extern uint8_t qpim_ecmp_rebalance_enable; #define PIM_DEBUG_PIM_EVENTS (router->debugs & PIM_MASK_PIM_EVENTS) #define PIM_DEBUG_PIM_EVENTS_DETAIL \ - (router->debugs & PIM_MASK_PIM_EVENTS_DETAIL) + (router->debugs & (PIM_MASK_PIM_EVENTS_DETAIL | PIM_MASK_PIM_EVENTS)) #define PIM_DEBUG_PIM_PACKETS (router->debugs & PIM_MASK_PIM_PACKETS) #define PIM_DEBUG_PIM_PACKETDUMP_SEND \ (router->debugs & PIM_MASK_PIM_PACKETDUMP_SEND) #define PIM_DEBUG_PIM_PACKETDUMP_RECV \ (router->debugs & PIM_MASK_PIM_PACKETDUMP_RECV) #define PIM_DEBUG_PIM_TRACE (router->debugs & PIM_MASK_PIM_TRACE) -#define PIM_DEBUG_PIM_TRACE_DETAIL (router->debugs & PIM_MASK_PIM_TRACE_DETAIL) +#define PIM_DEBUG_PIM_TRACE_DETAIL \ + (router->debugs & (PIM_MASK_PIM_TRACE_DETAIL | PIM_MASK_PIM_TRACE)) +#define PIM_DEBUG_PIM_TRACE_DETAIL_ONLY \ + (router->debugs & PIM_MASK_PIM_TRACE_DETAIL) #define PIM_DEBUG_IGMP_EVENTS (router->debugs & PIM_MASK_IGMP_EVENTS) #define PIM_DEBUG_IGMP_PACKETS (router->debugs & PIM_MASK_IGMP_PACKETS) #define PIM_DEBUG_IGMP_TRACE (router->debugs & PIM_MASK_IGMP_TRACE) #define PIM_DEBUG_IGMP_TRACE_DETAIL \ - (router->debugs & PIM_MASK_IGMP_TRACE_DETAIL) + (router->debugs & (PIM_MASK_IGMP_TRACE_DETAIL | PIM_MASK_IGMP_TRACE)) #define PIM_DEBUG_ZEBRA (router->debugs & PIM_MASK_ZEBRA) +#define PIM_DEBUG_MLAG (router->debugs & PIM_MASK_MLAG) #define PIM_DEBUG_SSMPINGD (router->debugs & PIM_MASK_SSMPINGD) #define PIM_DEBUG_MROUTE (router->debugs & PIM_MASK_MROUTE) -#define PIM_DEBUG_MROUTE_DETAIL (router->debugs & PIM_MASK_MROUTE_DETAIL) +#define PIM_DEBUG_MROUTE_DETAIL \ + (router->debugs & (PIM_MASK_MROUTE_DETAIL | PIM_MASK_MROUTE)) +#define PIM_DEBUG_MROUTE_DETAIL_ONLY (router->debugs & PIM_MASK_MROUTE_DETAIL) #define PIM_DEBUG_PIM_HELLO (router->debugs & PIM_MASK_PIM_HELLO) #define PIM_DEBUG_PIM_J_P (router->debugs & PIM_MASK_PIM_J_P) #define PIM_DEBUG_PIM_REG (router->debugs & PIM_MASK_PIM_REG) @@ -178,15 +186,17 @@ extern uint8_t qpim_ecmp_rebalance_enable; #define PIM_DEBUG_MSDP_PACKETS (router->debugs & PIM_MASK_MSDP_PACKETS) #define PIM_DEBUG_MSDP_INTERNAL (router->debugs & PIM_MASK_MSDP_INTERNAL) #define PIM_DEBUG_PIM_NHT (router->debugs & PIM_MASK_PIM_NHT) -#define PIM_DEBUG_PIM_NHT_DETAIL (router->debugs & PIM_MASK_PIM_NHT_DETAIL) +#define PIM_DEBUG_PIM_NHT_DETAIL \ + (router->debugs & (PIM_MASK_PIM_NHT_DETAIL | PIM_MASK_PIM_NHT)) #define PIM_DEBUG_PIM_NHT_RP (router->debugs & PIM_MASK_PIM_NHT_RP) #define PIM_DEBUG_MTRACE (router->debugs & PIM_MASK_MTRACE) #define PIM_DEBUG_VXLAN (router->debugs & PIM_MASK_VXLAN) +#define PIM_DEBUG_BSM (router->debugs & PIM_MASK_BSM_PROC) #define PIM_DEBUG_EVENTS \ (router->debugs \ & (PIM_MASK_PIM_EVENTS | PIM_MASK_IGMP_EVENTS \ - | PIM_MASK_MSDP_EVENTS)) + | PIM_MASK_MSDP_EVENTS | PIM_MASK_BSM_PROC)) #define PIM_DEBUG_PACKETS \ (router->debugs \ & (PIM_MASK_PIM_PACKETS | PIM_MASK_IGMP_PACKETS \ @@ -209,9 +219,11 @@ extern uint8_t qpim_ecmp_rebalance_enable; #define PIM_DO_DEBUG_IGMP_TRACE_DETAIL \ (router->debugs |= PIM_MASK_IGMP_TRACE_DETAIL) #define PIM_DO_DEBUG_ZEBRA (router->debugs |= PIM_MASK_ZEBRA) +#define PIM_DO_DEBUG_MLAG (router->debugs |= PIM_MASK_MLAG) #define PIM_DO_DEBUG_SSMPINGD (router->debugs |= PIM_MASK_SSMPINGD) #define PIM_DO_DEBUG_MROUTE (router->debugs |= PIM_MASK_MROUTE) #define PIM_DO_DEBUG_MROUTE_DETAIL (router->debugs |= PIM_MASK_MROUTE_DETAIL) +#define PIM_DO_DEBUG_BSM (router->debugs |= PIM_MASK_BSM_PROC) #define PIM_DO_DEBUG_PIM_HELLO (router->debugs |= PIM_MASK_PIM_HELLO) #define PIM_DO_DEBUG_PIM_J_P (router->debugs |= PIM_MASK_PIM_J_P) #define PIM_DO_DEBUG_PIM_REG (router->debugs |= PIM_MASK_PIM_REG) @@ -239,6 +251,7 @@ extern uint8_t qpim_ecmp_rebalance_enable; #define PIM_DONT_DEBUG_IGMP_TRACE_DETAIL \ (router->debugs &= ~PIM_MASK_IGMP_TRACE_DETAIL) #define PIM_DONT_DEBUG_ZEBRA (router->debugs &= ~PIM_MASK_ZEBRA) +#define PIM_DONT_DEBUG_MLAG (router->debugs &= ~PIM_MASK_MLAG) #define PIM_DONT_DEBUG_SSMPINGD (router->debugs &= ~PIM_MASK_SSMPINGD) #define PIM_DONT_DEBUG_MROUTE (router->debugs &= ~PIM_MASK_MROUTE) #define PIM_DONT_DEBUG_MROUTE_DETAIL (router->debugs &= ~PIM_MASK_MROUTE_DETAIL) @@ -253,6 +266,7 @@ extern uint8_t qpim_ecmp_rebalance_enable; #define PIM_DONT_DEBUG_PIM_NHT_RP (router->debugs &= ~PIM_MASK_PIM_NHT_RP) #define PIM_DONT_DEBUG_MTRACE (router->debugs &= ~PIM_MASK_MTRACE) #define PIM_DONT_DEBUG_VXLAN (router->debugs &= ~PIM_MASK_VXLAN) +#define PIM_DONT_DEBUG_BSM (router->debugs &= ~PIM_MASK_BSM_PROC) void pim_router_init(void); void pim_router_terminate(void); diff --git a/pimd/subdir.am b/pimd/subdir.am index 7f4810722b..8540651544 100644 --- a/pimd/subdir.am +++ b/pimd/subdir.am @@ -8,8 +8,8 @@ sbin_PROGRAMS += pimd/pimd bin_PROGRAMS += pimd/mtracebis noinst_PROGRAMS += pimd/test_igmpv3_join dist_examples_DATA += pimd/pimd.conf.sample -vtysh_scan += $(top_srcdir)/pimd/pim_cmd.c -man8 += $(MANBUILD)/pimd.8 +vtysh_scan += pimd/pim_cmd.c +man8 += $(MANBUILD)/frr-pimd.8 man8 += $(MANBUILD)/mtracebis.8 endif @@ -17,6 +17,7 @@ pimd_libpim_a_SOURCES = \ pimd/pim_assert.c \ pimd/pim_bfd.c \ pimd/pim_br.c \ + pimd/pim_bsm.c \ pimd/pim_cmd.c \ pimd/pim_errors.c \ pimd/pim_hello.c \ @@ -33,6 +34,7 @@ pimd_libpim_a_SOURCES = \ pimd/pim_jp_agg.c \ pimd/pim_macro.c \ pimd/pim_memory.c \ + pimd/pim_mlag.c \ pimd/pim_mroute.c \ pimd/pim_msdp.c \ pimd/pim_msdp_packet.c \ @@ -61,6 +63,7 @@ pimd_libpim_a_SOURCES = \ pimd/pim_zebra.c \ pimd/pim_zlookup.c \ pimd/pim_vxlan.c \ + pimd/pim_zpthread.c \ pimd/pimd.c \ # end @@ -68,6 +71,7 @@ noinst_HEADERS += \ pimd/pim_assert.h \ pimd/pim_bfd.h \ pimd/pim_br.h \ + pimd/pim_bsm.h \ pimd/pim_cmd.h \ pimd/pim_errors.h \ pimd/pim_hello.h \ @@ -85,6 +89,7 @@ noinst_HEADERS += \ pimd/pim_jp_agg.h \ pimd/pim_macro.h \ pimd/pim_memory.h \ + pimd/pim_mlag.h \ pimd/pim_mroute.h \ pimd/pim_msdp.h \ pimd/pim_msdp_packet.h \ @@ -118,8 +123,15 @@ noinst_HEADERS += \ pimd/mtracebis_routeget.h \ # end -pimd/pim_cmd_clippy.c: $(CLIPPY_DEPS) -pimd/pim_cmd.$(OBJEXT): pimd/pim_cmd_clippy.c +clippy_scan += \ + pimd/pim_cmd.c \ + # end + +nodist_pimd_pimd_SOURCES = \ + yang/frr-igmp.yang.c \ + yang/frr-pim.yang.c \ + yang/frr-pim-rp.yang.c \ + # end pimd_pimd_LDADD = pimd/libpim.a lib/libfrr.la $(LIBCAP) pimd_pimd_SOURCES = pimd/pim_main.c diff --git a/python/callgraph-dot.py b/python/callgraph-dot.py new file mode 100644 index 0000000000..4faf1dae16 --- /dev/null +++ b/python/callgraph-dot.py @@ -0,0 +1,476 @@ +# callgraph json to graphviz generator for FRR +# +# Copyright (C) 2020 David Lamparter for NetDEF, Inc. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the Free +# Software Foundation; either version 2 of the License, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; see the file COPYING; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +import re +import sys +import json + +class FunctionNode(object): + funcs = {} + + def __init__(self, name): + super().__init__() + FunctionNode.funcs[name] = self + + self.name = name + self.out = [] + self.inb = [] + self.rank = None + self.defined = False + self.defs = [] + + def __repr__(self): + return '<"%s()" rank=%r>' % (self.name, self.rank) + + def define(self, attrs): + self.defined = True + self.defs.append((attrs['filename'], attrs['line'])) + return self + + def add_call(self, called, attrs): + return CallEdge(self, called, attrs) + + def calls(self): + for e in self.out: + yield e.o + + def calld(self): + for e in self.inb: + yield e.i + + def unlink(self, other): + self.out = list([edge for edge in self.out if edge.o != other]) + other.inb = list([edge for edge in other.inb if edge.i != other]) + + @classmethod + def get(cls, name): + if name in cls.funcs: + return cls.funcs[name] + return FunctionNode(name) + +class CallEdge(object): + def __init__(self, i, o, attrs): + self.i = i + self.o = o + self.is_external = attrs['is_external'] + self.attrs = attrs + + i.out.append(self) + o.inb.append(self) + + def __repr__(self): + return '<"%s()" -> "%s()">' % (self.i.name, self.o.name) + +def nameclean(n): + if '.' in n: + return n.split('.', 1)[0] + return n + +def calc_rank(queue, direction): + nextq = queue + + if direction == 1: + aggr = max + elem = lambda x: x.calls() + else: + aggr = min + elem = lambda x: x.calld() + + currank = direction + cont = True + + while len(nextq) > 0 and cont: + queue = nextq + nextq = [] + + #sys.stderr.write('rank %d\n' % currank) + + cont = False + + for node in queue: + if not node.defined: + node.rank = 0 + continue + + rank = direction + for other in elem(node): + if other is node: + continue + if other.rank is None: + nextq.append(node) + break + rank = aggr(rank, other.rank + direction) + else: + cont = True + node.rank = rank + + currank += direction + + return nextq + +class Graph(dict): + class Subgraph(set): + def __init__(self): + super().__init__() + + class NodeGroup(set): + def __init__(self, members): + super().__init__(members) + + class Node(object): + def __init__(self, graph, fn): + super().__init__() + self._fn = fn + self._fns = [fn] + self._graph = graph + self._calls = set() + self._calld = set() + self._group = None + + def __repr__(self): + return '' % (self._fn.name, len(self._fns)) + + def __hash__(self): + return hash(self._fn.name) + + def _finalize(self): + for called in self._fn.calls(): + if called.name == self._fn.name: + continue + if called.name in self._graph: + self._calls.add(self._graph[called.name]) + self._graph[called.name]._calld.add(self) + + def unlink(self, other): + self._calls.remove(other) + other._calld.remove(self) + + @property + def name(self): + return self._fn.name + + def calls(self): + return self._calls + def calld(self): + return self._calld + + def group(self, members): + assert self in members + + pregroups = [] + for g in [m._group for m in members]: + if g is None: + continue + if g in pregroups: + continue + + assert g <= members + pregroups.append(g) + + if len(pregroups) == 0: + group = self._graph.NodeGroup(members) + self._graph._groups.append(group) + elif len(pregroups) == 1: + group = pregroups[0] + group |= members + else: + for g in pregroups: + self._graph._groups.remove(g) + group = self._graph.NodeGroup(members) + self._graph._groups.append(group) + + for m in members: + m._group = group + return group + + def merge(self, other): + self._fns.extend(other._fns) + self._calls = (self._calls | other._calls) - {self, other} + self._calld = (self._calld | other._calld) - {self, other} + for c in other._calls: + if c == self: + continue + c._calld.remove(other) + c._calld.add(self) + for c in other._calld: + if c == self: + continue + c._calls.remove(other) + c._calls.add(self) + del self._graph[other._fn.name] + + def __init__(self, funcs): + super().__init__() + self._funcs = funcs + for fn in funcs: + self[fn.name] = self.Node(self, fn) + for node in self.values(): + node._finalize() + self._groups = [] + + def automerge(self): + nodes = list(self.values()) + + while len(nodes): + node = nodes.pop(0) + + candidates = {node} + evalset = set(node.calls()) + prevevalset = None + + while prevevalset != evalset: + prevevalset = evalset + evalset = set() + + for evnode in prevevalset: + inbound = set(evnode.calld()) + if inbound <= candidates: + candidates.add(evnode) + evalset |= set(evnode.calls()) - candidates + else: + evalset.add(evnode) + + #if len(candidates) > 1: + # for candidate in candidates: + # if candidate != node: + # #node.merge(candidate) + # if candidate in nodes: + # nodes.remove(candidate) + node.group(candidates) + + for candidate in candidates: + if candidate in nodes: + nodes.remove(candidate) + + def calc_subgraphs(self): + nodes = list(self.values()) + self._subgraphs = [] + up = {} + down = {} + + self._linear_nodes = [] + + while len(nodes): + sys.stderr.write('%d\n' % len(nodes)) + node = nodes.pop(0) + + down[node] = set() + queue = [node] + while len(queue): + now = queue.pop() + down[node].add(now) + for calls in now.calls(): + if calls in down[node]: + continue + queue.append(calls) + + up[node] = set() + queue = [node] + while len(queue): + now = queue.pop() + up[node].add(now) + for calld in now.calld(): + if calld in up[node]: + continue + queue.append(calld) + + common = up[node] & down[node] + + if len(common) == 1: + self._linear_nodes.append(node) + else: + sg = self.Subgraph() + sg |= common + self._subgraphs.append(sg) + for n in common: + if n != node: + nodes.remove(n) + + return self._subgraphs, self._linear_nodes + + +with open(sys.argv[1], 'r') as fd: + data = json.load(fd) + +extra_info = { + # zebra - LSP WQ + ('lsp_processq_add', 'work_queue_add'): [ + 'lsp_process', + 'lsp_processq_del', + 'lsp_processq_complete', + ], + # zebra - main WQ + ('mq_add_handler', 'work_queue_add'): [ + 'meta_queue_process', + ], + ('meta_queue_process', 'work_queue_add'): [ + 'meta_queue_process', + ], + # bgpd - label pool WQ + ('bgp_lp_get', 'work_queue_add'): [ + 'lp_cbq_docallback', + ], + ('bgp_lp_event_chunk', 'work_queue_add'): [ + 'lp_cbq_docallback', + ], + ('bgp_lp_event_zebra_up', 'work_queue_add'): [ + 'lp_cbq_docallback', + ], + # bgpd - main WQ + ('bgp_process', 'work_queue_add'): [ + 'bgp_process_wq', + 'bgp_processq_del', + ], + ('bgp_add_eoiu_mark', 'work_queue_add'): [ + 'bgp_process_wq', + 'bgp_processq_del', + ], + # clear node WQ + ('bgp_clear_route_table', 'work_queue_add'): [ + 'bgp_clear_route_node', + 'bgp_clear_node_queue_del', + 'bgp_clear_node_complete', + ], + # rfapi WQs + ('rfapi_close', 'work_queue_add'): [ + 'rfapi_deferred_close_workfunc', + ], + ('rfapiRibUpdatePendingNode', 'work_queue_add'): [ + 'rfapiRibDoQueuedCallback', + 'rfapiRibQueueItemDelete', + ], +} + + +for func, fdata in data['functions'].items(): + func = nameclean(func) + fnode = FunctionNode.get(func).define(fdata) + + for call in fdata['calls']: + if call.get('type') in [None, 'unnamed', 'thread_sched']: + if call.get('target') is None: + continue + tgt = nameclean(call['target']) + fnode.add_call(FunctionNode.get(tgt), call) + for fptr in call.get('funcptrs', []): + fnode.add_call(FunctionNode.get(nameclean(fptr)), call) + if tgt == 'work_queue_add': + if (func, tgt) not in extra_info: + sys.stderr.write('%s:%d:%s(): work_queue_add() not handled\n' % ( + call['filename'], call['line'], func)) + else: + attrs = dict(call) + attrs.update({'is_external': False, 'type': 'workqueue'}) + for dst in extra_info[func, tgt]: + fnode.add_call(FunctionNode.get(dst), call) + elif call['type'] == 'install_element': + vty_node = FunctionNode.get('VTY_NODE_%d' % call['vty_node']) + vty_node.add_call(FunctionNode.get(nameclean(call['target'])), call) + elif call['type'] == 'hook': + # TODO: edges for hooks from data['hooks'] + pass + +n = FunctionNode.funcs + +# fix some very low end functions cycling back very far to the top +if 'peer_free' in n: + n['peer_free'].unlink(n['bgp_timer_set']) + n['peer_free'].unlink(n['bgp_addpath_set_peer_type']) +if 'bgp_path_info_extra_free' in n: + n['bgp_path_info_extra_free'].rank = 0 + +if 'zlog_ref' in n: + n['zlog_ref'].rank = 0 +if 'mt_checkalloc' in n: + n['mt_checkalloc'].rank = 0 + +queue = list(FunctionNode.funcs.values()) +queue = calc_rank(queue, 1) +queue = calc_rank(queue, -1) + +sys.stderr.write('%d functions in cyclic set\n' % len(queue)) + +graph = Graph(queue) +graph.automerge() + +gv_nodes = [] +gv_edges = [] + +sys.stderr.write('%d groups after automerge\n' % len(graph._groups)) + +def is_vnc(n): + return n.startswith('rfapi') or n.startswith('vnc') or ('_vnc_' in n) + +_vncstyle = ',fillcolor="#ffffcc",style=filled' +cyclic_set_names = set([fn.name for fn in graph.values()]) + +for i, group in enumerate(graph._groups): + if len(group) > 1: + group.num = i + gv_nodes.append('\tsubgraph cluster_%d {' % i) + gv_nodes.append('\t\tcolor=blue;') + for gn in group: + has_cycle_callers = set(gn.calld()) - group + has_ext_callers = set([edge.i.name for edge in gn._fn.inb]) - cyclic_set_names + + style = '' + etext = '' + if is_vnc(gn.name): + style += _vncstyle + if has_cycle_callers: + style += ',color=blue,penwidth=3' + if has_ext_callers: + style += ',fillcolor="#ffeebb",style=filled' + etext += '
(%d other callers)' % (len(has_ext_callers)) + + gv_nodes.append('\t\t"%s" [shape=box,label=<%s%s>%s];' % (gn.name, '
'.join([fn.name for fn in gn._fns]), etext, style)) + gv_nodes.append('\t}') + else: + for gn in group: + has_ext_callers = set([edge.i.name for edge in gn._fn.inb]) - cyclic_set_names + + style = '' + etext = '' + if is_vnc(gn.name): + style += _vncstyle + if has_ext_callers: + style += ',fillcolor="#ffeebb",style=filled' + etext += '
(%d other callers)' % (len(has_ext_callers)) + gv_nodes.append('\t"%s" [shape=box,label=<%s%s>%s];' % (gn.name, '
'.join([fn.name for fn in gn._fns]), etext, style)) + +edges = set() +for gn in graph.values(): + for calls in gn.calls(): + if gn._group == calls._group: + gv_edges.append('\t"%s" -> "%s" [color="#55aa55",style=dashed];' % (gn.name, calls.name)) + else: + def xname(nn): + if len(nn._group) > 1: + return 'cluster_%d' % nn._group.num + else: + return nn.name + tup = xname(gn), calls.name + if tup[0] != tup[1] and tup not in edges: + gv_edges.append('\t"%s" -> "%s" [weight=0.0,w=0.0,color=blue];' % tup) + edges.add(tup) + +with open(sys.argv[2], 'w') as fd: + fd.write('''digraph { + node [fontsize=13,fontname="Fira Sans"]; +%s +}''' % '\n'.join(gv_nodes + [''] + gv_edges)) diff --git a/python/clidef.py b/python/clidef.py index f8d96115bd..baa6ed52b2 100644 --- a/python/clidef.py +++ b/python/clidef.py @@ -37,6 +37,7 @@ def combine(self, other): deref = '' drop_str = False canfail = True + canassert = False class StringHandler(RenderHandler): argtype = 'const char *' @@ -44,6 +45,7 @@ class StringHandler(RenderHandler): code = Template('$varname = (argv[_i]->type == WORD_TKN) ? argv[_i]->text : argv[_i]->arg;') drop_str = True canfail = False + canassert = True class LongHandler(RenderHandler): argtype = 'long' @@ -111,6 +113,7 @@ class IPGenHandler(IPBase): _fail = !inet_aton(argv[_i]->arg, &s__$varname.sin.sin_addr); $varname = &s__$varname; }''') + canassert = True def mix_handlers(handlers): def combine(a, b): @@ -171,6 +174,7 @@ def combine(a, b): return CMD_WARNING; #endif #endif +$argassert return ${fnname}_magic(self, vty, argc, argv$arglist); } @@ -182,16 +186,72 @@ def combine(a, b): $code }''') -def process_file(fn, ofd, dumpfd, all_defun): +def get_always_args(token, always_args, args = [], stack = []): + if token in stack: + return + if token.type == 'END_TKN': + for arg in list(always_args): + if arg not in args: + always_args.remove(arg) + return + + stack = stack + [token] + if token.type in handlers and token.varname is not None: + args = args + [token.varname] + for nexttkn in token.next(): + get_always_args(nexttkn, always_args, args, stack) + +class Macros(dict): + def load(self, filename): + filedata = clippy.parse(filename) + for entry in filedata['data']: + if entry['type'] != 'PREPROC': + continue + ppdir = entry['line'].lstrip().split(None, 1) + if ppdir[0] != 'define' or len(ppdir) != 2: + continue + ppdef = ppdir[1].split(None, 1) + name = ppdef[0] + if '(' in name: + continue + val = ppdef[1] if len(ppdef) == 2 else '' + + val = val.strip(' \t\n\\') + if name in self: + sys.stderr.write('warning: macro %s redefined!\n' % (name)) + self[name] = val + +def process_file(fn, ofd, dumpfd, all_defun, macros): + errors = 0 filedata = clippy.parse(fn) for entry in filedata['data']: if entry['type'].startswith('DEFPY') or (all_defun and entry['type'].startswith('DEFUN')): + if len(entry['args'][0]) != 1: + sys.stderr.write('%s:%d: DEFPY function name not parseable (%r)\n' % (fn, entry['lineno'], entry['args'][0])) + errors += 1 + continue + cmddef = entry['args'][2] - cmddef = ''.join([i[1:-1] for i in cmddef]) + cmddefx = [] + for i in cmddef: + while i in macros: + i = macros[i] + if i.startswith('"') and i.endswith('"'): + cmddefx.append(i[1:-1]) + continue + + sys.stderr.write('%s:%d: DEFPY command string not parseable (%r)\n' % (fn, entry['lineno'], cmddef)) + errors += 1 + cmddefx = None + break + if cmddefx is None: + continue + cmddef = ''.join([i for i in cmddefx]) graph = clippy.Graph(cmddef) args = OrderedDict() + always_args = set() for token, depth in clippy.graph_iterate(graph): if token.type not in handlers: continue @@ -199,6 +259,9 @@ def process_file(fn, ofd, dumpfd, all_defun): continue arg = args.setdefault(token.varname, []) arg.append(handlers[token.type](token)) + always_args.add(token.varname) + + get_always_args(graph.first(), always_args) #print('-' * 76) #pprint(entry) @@ -210,30 +273,36 @@ def process_file(fn, ofd, dumpfd, all_defun): argdecls = [] arglist = [] argblocks = [] + argassert = [] doc = [] canfail = 0 - def do_add(handler, varname, attr = ''): + def do_add(handler, basename, varname, attr = ''): argdefs.append(',\\\n\t%s %s%s' % (handler.argtype, varname, attr)) argdecls.append('\t%s\n' % (handler.decl.substitute({'varname': varname}).replace('\n', '\n\t'))) arglist.append(', %s%s' % (handler.deref, varname)) + if basename in always_args and handler.canassert: + argassert.append('''\tif (!%s) { +\t\tvty_out(vty, "Internal CLI error [%%s]\\n", "%s"); +\t\treturn CMD_WARNING; +\t}\n''' % (varname, varname)) if attr == '': at = handler.argtype if not at.startswith('const '): at = '. . . ' + at - doc.append('\t%-26s %s' % (at, varname)) + doc.append('\t%-26s %s %s' % (at, 'alw' if basename in always_args else 'opt', varname)) for varname in args.keys(): handler = mix_handlers(args[varname]) #print(varname, handler) if handler is None: continue - do_add(handler, varname) + do_add(handler, varname, varname) code = handler.code.substitute({'varname': varname}).replace('\n', '\n\t\t\t') if handler.canfail: canfail = 1 strblock = '' if not handler.drop_str: - do_add(StringHandler(None), '%s_str' % (varname), ' __attribute__ ((unused))') + do_add(StringHandler(None), varname, '%s_str' % (varname), ' __attribute__ ((unused))') strblock = '\n\t\t\t%s_str = argv[_i]->arg;' % (varname) argblocks.append(argblock.substitute({'varname': varname, 'strblock': strblock, 'code': code})) @@ -249,8 +318,11 @@ def do_add(handler, varname, attr = ''): params['argblocks'] = ''.join(argblocks) params['canfail'] = canfail params['nonempty'] = len(argblocks) + params['argassert'] = ''.join(argassert) ofd.write(templ.substitute(params)) + return errors + if __name__ == '__main__': import argparse @@ -274,7 +346,18 @@ def do_add(handler, varname, attr = ''): if args.show: dumpfd = sys.stderr - process_file(args.cfile, ofd, dumpfd, args.all_defun) + basepath = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + + macros = Macros() + macros.load('lib/route_types.h') + macros.load(os.path.join(basepath, 'lib/command.h')) + macros.load(os.path.join(basepath, 'bgpd/bgp_vty.h')) + # sigh :( + macros['PROTO_REDIST_STR'] = 'FRR_REDIST_STR_ISISD' + + errors = process_file(args.cfile, ofd, dumpfd, args.all_defun, macros) + if errors != 0: + sys.exit(1) if args.o is not None: clippy.wrdiff(args.o, ofd, [args.cfile, os.path.realpath(__file__), sys.executable]) diff --git a/python/firstheader.py b/python/firstheader.py new file mode 100644 index 0000000000..19a85b63e5 --- /dev/null +++ b/python/firstheader.py @@ -0,0 +1,30 @@ +# +# check that the first header included in C files is either +# zebra.h or config.h +# + +import sys, os, re, subprocess + +include_re = re.compile('^#\s*include\s+["<]([^ ">]+)[">]', re.M) + +errors = 0 + +files = subprocess.check_output(['git', 'ls-files']).decode('ASCII') +for fn in files.splitlines(): + if not fn.endswith('.c'): + continue + if fn.startswith('tools/'): + continue + with open(fn, 'r') as fd: + data = fd.read() + m = include_re.search(data) + if m is None: + #sys.stderr.write('no #include in %s?\n' % (fn)) + continue + if m.group(1) in ['config.h', 'zebra.h', 'lib/zebra.h']: + continue + sys.stderr.write('%s: %s\n' % (fn, m.group(0))) + errors += 1 + +if errors: + sys.exit(1) diff --git a/python/makefile.py b/python/makefile.py new file mode 100644 index 0000000000..fe20945ccc --- /dev/null +++ b/python/makefile.py @@ -0,0 +1,129 @@ +#!/usr/bin/python3 +# +# FRR extended automake/Makefile functionality helper +# +# This script is executed on/after generating Makefile to add some pieces for +# clippy. + +import sys +import os +import subprocess +import re +import argparse +from string import Template +from makevars import MakeReVars + +argp = argparse.ArgumentParser(description = 'FRR Makefile extensions') +argp.add_argument('--dev-build', action = 'store_const', const = True, + help = 'run additional developer checks') +args = argp.parse_args() + +with open('Makefile', 'r') as fd: + before = fd.read() + +mv = MakeReVars(before) + +clippy_scan = mv['clippy_scan'].strip().split() +for clippy_file in clippy_scan: + assert clippy_file.endswith('.c') + +# check for files using clippy but not listed in clippy_scan +if args.dev_build: + basepath = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + if os.path.exists(os.path.join(basepath, '.git')): + clippy_ref = subprocess.check_output([ + 'git', '-C', basepath, 'grep', '-l', '-P', '^#\s*include.*_clippy.c', '--', '**.c']).decode('US-ASCII') + + clippy_ref = set(clippy_ref.splitlines()) + missing = clippy_ref - set(clippy_scan) + + if len(missing) > 0: + sys.stderr.write('error: files seem to be using clippy, but not listed in "clippy_scan" in subdir.am:\n\t%s\n' % ('\n\t'.join(sorted(missing)))) + sys.exit(1) + +clippydep = Template(''' +${clippybase}.$$(OBJEXT): ${clippybase}_clippy.c +${clippybase}.lo: ${clippybase}_clippy.c +${clippybase}_clippy.c: $$(CLIPPY_DEPS)''') + +clippyauxdep = Template('''# clippy{ +# auxiliary clippy target +${target}: ${clippybase}_clippy.c +# }clippy''') + +lines = before.splitlines() +autoderp = '#AUTODERP# ' +out_lines = [] +bcdeps = [] +make_rule_re = re.compile('^([^:\s]+):\s*([^:\s]+)\s*($|\n)') + +while lines: + line = lines.pop(0) + if line.startswith(autoderp): + line = line[len(autoderp):] + + if line == '# clippy{': + while lines: + line = lines.pop(0) + if line == '# }clippy': + break + continue + + if line.startswith('#'): + out_lines.append(line) + continue + + full_line = line + full_lines = lines[:] + while full_line.endswith('\\'): + full_line = full_line[:-1] + full_lines.pop(0) + + m = make_rule_re.match(full_line) + if m is None: + out_lines.append(line) + continue + + line, lines = full_line, full_lines + + target, dep = m.group(1), m.group(2) + + if target.endswith('.lo') or target.endswith('.o'): + if not dep.endswith('.h'): + bcdeps.append('%s.bc: %s' % (target, target)) + bcdeps.append('\t$(AM_V_LLVM_BC)$(COMPILE) -emit-llvm -c -o $@ %s' % (dep)) + if m.group(2) in clippy_scan: + out_lines.append(clippyauxdep.substitute(target=m.group(1), clippybase=m.group(2)[:-2])) + + out_lines.append(line) + +out_lines.append('# clippy{\n# main clippy targets') +for clippy_file in clippy_scan: + out_lines.append(clippydep.substitute(clippybase = clippy_file[:-2])) + +out_lines.append('') +out_lines.extend(bcdeps) +out_lines.append('') +bc_targets = [] +for varname in ['bin_PROGRAMS', 'sbin_PROGRAMS', 'lib_LTLIBRARIES', 'module_LTLIBRARIES', 'noinst_LIBRARIES']: + bc_targets.extend(mv[varname].strip().split()) +for target in bc_targets: + amtgt = target.replace('/', '_').replace('.', '_').replace('-', '_') + objs = mv[amtgt + '_OBJECTS'].strip().split() + objs = [obj + '.bc' for obj in objs] + deps = mv.get(amtgt + '_DEPENDENCIES', '').strip().split() + deps = [d + '.bc' for d in deps if d.endswith('.a')] + objs.extend(deps) + out_lines.append('%s.bc: %s' % (target, ' '.join(objs))) + out_lines.append('\t$(AM_V_LLVM_LD)$(LLVM_LINK) -o $@ $^') + out_lines.append('') + +out_lines.append('# }clippy') +out_lines.append('') + +after = '\n'.join(out_lines) +if after == before: + sys.exit(0) + +with open('Makefile.pyout', 'w') as fd: + fd.write(after) +os.rename('Makefile.pyout', 'Makefile') diff --git a/python/makevars.py b/python/makevars.py new file mode 100644 index 0000000000..63bf8c5eeb --- /dev/null +++ b/python/makevars.py @@ -0,0 +1,90 @@ +# +# helper class to grab variables from FRR's Makefile +# + +import os +import subprocess +import re + +class MakeVarsBase(object): + ''' + common code between MakeVars and MakeReVars + ''' + def __init__(self): + self._data = dict() + + def __getitem__(self, k): + if k not in self._data: + self.getvars([k]) + return self._data[k] + + def get(self, k, defval = None): + if k not in self._data: + self.getvars([k]) + return self._data.get(k) or defval + +class MakeVars(MakeVarsBase): + ''' + makevars['FOO_CFLAGS'] gets you "FOO_CFLAGS" from Makefile + + This variant works by invoking make as a subprocess, i.e. Makefile must + be valid and working. (This is sometimes a problem if depfiles have not + been generated.) + ''' + def getvars(self, varlist): + ''' + get a batch list of variables from make. faster than individual calls. + ''' + rdfd, wrfd = os.pipe() + + shvars = ['shvar-%s' % s for s in varlist] + make = subprocess.Popen(['make', '-s', 'VARFD=%d' % wrfd] + shvars, pass_fds = [wrfd]) + os.close(wrfd) + data = b'' + + rdf = os.fdopen(rdfd, 'rb') + while True: + rdata = rdf.read() + if len(rdata) == 0: + break + data += rdata + + del rdf + make.wait() + + data = data.decode('US-ASCII').strip().split('\n') + for row in data: + k, v = row.split('=', 1) + v = v[1:-1] + self._data[k] = v + +class MakeReVars(MakeVarsBase): + ''' + makevars['FOO_CFLAGS'] gets you "FOO_CFLAGS" from Makefile + + This variant works by regexing through Makefile. This means the Makefile + does not need to be fully working, but on the other hand it doesn't support + fancy complicated make expressions. + ''' + var_re = re.compile(r'^([^=#\n\s]+)[ \t]*=[ \t]*([^#\n]*)(?:#.*)?$', flags=re.MULTILINE) + repl_re = re.compile(r'\$(?:([A-Za-z])|\(([^\)]+)\))') + + def __init__(self, maketext): + super(MakeReVars, self).__init__() + self._vars = dict(self.var_re.findall(maketext.replace('\\\n', ''))) + + def replacevar(self, match): + varname = match.group(1) or match.group(2) + return self._vars.get(varname, '') + + def getvars(self, varlist): + for varname in varlist: + if varname not in self._vars: + continue + + val, prevval = self._vars[varname], None + while val != prevval: + prevval = val + val = self.repl_re.sub(self.replacevar, val) + + self._data[varname] = val diff --git a/qpb/subdir.am b/qpb/subdir.am index 75a733f8fc..80f8f3aca9 100644 --- a/qpb/subdir.am +++ b/qpb/subdir.am @@ -10,9 +10,12 @@ qpb_libfrr_pb_la_SOURCES = \ qpb/qpb.c \ qpb/qpb_allocator.c \ # end + +if HAVE_PROTOBUF nodist_qpb_libfrr_pb_la_SOURCES = \ qpb/qpb.pb-c.c \ # end +endif noinst_HEADERS += \ qpb/linear_allocator.h \ @@ -26,6 +29,7 @@ CLEANFILES += \ # end EXTRA_DIST += qpb/qpb.proto +SUFFIXES += .proto .pb-c.c .pb-c.h if HAVE_PROTOBUF diff --git a/redhat/frr.logrotate b/redhat/frr.logrotate index df7c5da54e..b488ad08dc 100644 --- a/redhat/frr.logrotate +++ b/redhat/frr.logrotate @@ -14,6 +14,14 @@ endscript } +/var/log/frr/babeld.log { + notifempty + missingok + postrotate + /bin/kill -USR1 `cat /var/run/frr/babeld.pid 2> /dev/null` 2> /dev/null || true + endscript +} + /var/log/frr/bgpd.log { notifempty missingok @@ -101,3 +109,44 @@ /bin/kill -USR1 `cat /var/run/frr/fabricd.pid 2> /dev/null` 2> /dev/null || true endscript } + +/var/log/frr/pbrd.log { + notifempty + missingok + postrotate + /bin/kill -USR1 `cat /var/run/frr/pbrd.pid 2> /dev/null` 2> /dev/null || true + endscript +} + +/var/log/frr/pimd.log { + notifempty + missingok + postrotate + /bin/kill -USR1 `cat /var/run/frr/pimd.pid 2> /dev/null` 2> /dev/null || true + endscript +} + +/var/log/frr/sharpd.log { + notifempty + missingok + postrotate + /bin/kill -USR1 `cat /var/run/frr/sharpd.pid 2> /dev/null` 2> /dev/null || true + endscript +} + +/var/log/frr/staticd.log { + notifempty + missingok + postrotate + /bin/kill -USR1 `cat /var/run/frr/static.pid 2> /dev/null` 2> /dev/null || true + endscript +} + +/var/log/frr/vrrpd.log { + notifempty + missingok + postrotate + /bin/kill -USR1 `cat /var/run/frr/vrrpd.pid 2> /dev/null` 2> /dev/null || true + endscript +} + diff --git a/redhat/frr.spec.in b/redhat/frr.spec.in index 36f9259865..6ab82ff002 100644 --- a/redhat/frr.spec.in +++ b/redhat/frr.spec.in @@ -1,6 +1,6 @@ # configure options # -# Some can be overriden on rpmbuild commandline with: +# Some can be overridden on rpmbuild commandline with: # rpmbuild --define 'variable value' # (use any value, ie 1 for flag "with_XXXX" definitions) # @@ -24,7 +24,7 @@ %{!?with_pam: %global with_pam 0 } %{!?with_pbrd: %global with_pbrd 1 } %{!?with_pimd: %global with_pimd 1 } -%{!?with_rpki: %global with_rpki 0 } +%{!?with_vrrpd: %global with_vrrpd 1 } %{!?with_rtadv: %global with_rtadv 1 } %{!?with_watchfrr: %global with_watchfrr 1 } @@ -43,12 +43,6 @@ # defines for configure %define rundir %{_localstatedir}/run/%{name} -# define for sphinx-build binary -%if 0%{?rhel} && 0%{?rhel} < 7 - %define sphinx sphinx-build2.7 -%else - %define sphinx sphinx-build -%endif ############################################################################ #### Version String tweak @@ -68,6 +62,13 @@ %endif %endif +# Check for python version - use python2.7 on CentOS 6, otherwise python3 +%if 0%{?rhel} && 0%{?rhel} < 7 + %global use_python2 1 +%else + %global use_python2 0 +%endif + # If init system is systemd, then always enable watchfrr %if "%{initsystem}" == "systemd" %global with_watchfrr 1 @@ -124,6 +125,12 @@ %define daemon_babeld "" %endif +%if %{with_vrrpd} + %define daemon_vrrpd vrrpd +%else + %define daemon_vrrpd "" +%endif + %if %{with_watchfrr} %define daemon_watchfrr watchfrr %else @@ -136,7 +143,7 @@ %define daemon_bfdd "" %endif -%define all_daemons %{daemon_list} %{daemon_ldpd} %{daemon_pimd} %{daemon_nhrpd} %{daemon_eigrpd} %{daemon_babeld} %{daemon_watchfrr} %{daemon_pbrd} %{daemon_bfdd} +%define all_daemons %{daemon_list} %{daemon_ldpd} %{daemon_pimd} %{daemon_nhrpd} %{daemon_eigrpd} %{daemon_babeld} %{daemon_watchfrr} %{daemon_pbrd} %{daemon_bfdd} %{daemon_vrrpd} #release sub-revision (the two digits after the CONFDATE) %{!?release_rev: %global release_rev 01 } @@ -162,22 +169,28 @@ BuildRequires: make BuildRequires: ncurses-devel BuildRequires: readline-devel BuildRequires: texinfo -BuildRequires: libyang-devel >= 0.16.74 +BuildRequires: libyang-devel >= 1.0.184 %if 0%{?rhel} && 0%{?rhel} < 7 #python27-devel is available from ius community repo for RedHat/CentOS 6 BuildRequires: python27-devel BuildRequires: python27-sphinx %else +%if %{use_python2} BuildRequires: python-devel >= 2.7 BuildRequires: python-sphinx +%else +BuildRequires: python3-devel +BuildRequires: python3-sphinx +%endif +%endif +%if 0%{?rhel} > 7 +#platform-python-devel is needed for /usr/bin/pathfix.py +BuildRequires: platform-python-devel %endif Requires: initscripts %if %{with_pam} BuildRequires: pam-devel %endif -%if %{with_rpki} -BuildRequires: librtr-devel >= 0.5 -%endif %if "%{initsystem}" == "systemd" BuildRequires: systemd BuildRequires: systemd-devel @@ -216,8 +229,18 @@ Contributed/3rd party tools which may be of use with frr. %package pythontools Summary: python tools for frr -BuildRequires: python -Requires: python-ipaddress +%if 0%{?rhel} && 0%{?rhel} < 7 +#python27 is available from ius community repo for RedHat/CentOS 6 +BuildRequires: python27 +Requires: python27-ipaddress +%else +%if %{use_python2} +BuildRequires: python2 +Requires: python2-ipaddress +%else +BuildRequires: python3 +%endif +%endif Group: System Environment/Daemons %description pythontools @@ -234,6 +257,32 @@ The frr-devel package contains the header and object files neccessary for developing OSPF-API and frr applications. +%package rpki-rtrlib +Summary: BGP RPKI support (rtrlib) +Group: System Environment/Daemons +BuildRequires: librtr-devel >= 0.5 +Requires: %{name} = %{version}-%{release} + +%description rpki-rtrlib +Adds RPKI support to FRR's bgpd, allowing validation of BGP routes +against cryptographic information stored in WHOIS databases. This is +used to prevent hijacking of networks on the wider internet. It is only +relevant to internet service providers using their own autonomous system +number. + + +%package snmp +Summary: SNMP support +Group: System Environment/Daemons +BuildRequires: net-snmp-devel +Requires: %{name} = %{version}-%{release} + +%description snmp +Adds SNMP support to FRR's daemons by attaching to net-snmp's snmpd +through the AgentX protocol. Provides read-only access to current +routing state through standard SNMP MIBs. + + %prep %setup -q -n frr-%{frrversion} @@ -306,6 +355,11 @@ developing OSPF-API and frr applications. %else --disable-babeld \ %endif +%if %{with_vrrpd} + --enable-vrrpd \ +%else + --disable-vrrpd \ +%endif %if %{with_pam} --with-libpam \ %endif @@ -338,20 +392,27 @@ developing OSPF-API and frr applications. %if "%{initsystem}" == "systemd" --enable-systemd \ %endif -%if %{with_rpki} --enable-rpki \ -%else - --disable-rpki \ -%endif %if %{with_bfdd} --enable-bfdd \ %else --disable-bfdd \ %endif - SPHINXBUILD=%{sphinx} + --enable-snmp + # end make %{?_smp_mflags} MAKEINFO="makeinfo --no-split" +%if %{use_python2} +# Change frr-reload.py to use python2.7 +sed -e '1c #!/usr/bin/python2.7' -i %{zeb_src}/tools/frr-reload.py +sed -e '1c #!/usr/bin/python2.7' -i %{zeb_src}/tools/generate_support_bundle.py +%else +# Change frr-reload.py to use python3 +sed -e '1c #!/usr/bin/python3' -i %{zeb_src}/tools/frr-reload.py +sed -e '1c #!/usr/bin/python3' -i %{zeb_src}/tools/generate_support_bundle.py +%endif + pushd doc make info popd @@ -383,14 +444,15 @@ ln -s %{_sbindir}/frrinit.sh %{buildroot}%{_initddir}/frr %endif install %{zeb_src}/tools/etc/frr/daemons %{buildroot}%{_sysconfdir}/frr -# add rpki module to daemon -%if %{with_rpki} - sed -i -e 's/^\(bgpd_options=\)\(.*\)\(".*\)/\1\2 -M rpki\3/' %{buildroot}%{_sysconfdir}/frr/daemons -%endif install -m644 %{zeb_rh_src}/frr.pam %{buildroot}%{_sysconfdir}/pam.d/frr install -m644 %{zeb_rh_src}/frr.logrotate %{buildroot}%{_sysconfdir}/logrotate.d/frr install -d -m750 %{buildroot}%{rundir} +%if 0%{?rhel} > 7 || 0%{?fedora} > 29 +# avoid `ERROR: ambiguous python shebang in` errors +pathfix.py -pni "%{__python3} %{py3_shbang_opts}" %{buildroot}/usr/lib/frr/*.py +%py_byte_compile %{__python3} %{buildroot}/usr/lib/frr/*.py +%endif %pre # add vty_group @@ -422,7 +484,7 @@ zebra_spec_add_service () { # Add port /etc/services entry if it isn't already there if [ -f %{_sysconfdir}/services ] && \ - ! %__sed -e 's/#.*$//' %{_sysconfdir}/services | %__grep -wq $1 ; then + ! %__sed -e 's/#.*$//' %{_sysconfdir}/services 2>/dev/null | %__grep -wq $1 ; then echo "$1 $2 # $3" >> %{_sysconfdir}/services fi } @@ -461,6 +523,9 @@ zebra_spec_add_service isisd 2608/tcp "ISISd vty" zebra_spec_add_service bfdd 2617/tcp "BFDd vty" %endif zebra_spec_add_service fabricd 2618/tcp "Fabricd vty" +%if %{with_vrrpd} + zebra_spec_add_service vrrpd 2619/tcp "VRRPd vty" +%endif %if "%{initsystem}" == "systemd" for daemon in %all_daemons ; do @@ -596,6 +661,9 @@ fi %if %{with_pbrd} %{_sbindir}/pbrd %endif +%if %{with_vrrpd} + %{_sbindir}/vrrpd +%endif %{_sbindir}/isisd %{_sbindir}/fabricd %if %{with_ldpd} @@ -613,15 +681,16 @@ fi %if %{with_bfdd} %{_sbindir}/bfdd %endif -%{_libdir}/lib*.so.0 -%{_libdir}/lib*.so.0.* +%{_libdir}/libfrr.so* +%{_libdir}/libfrrcares* +%{_libdir}/libfrrospf* %if %{with_fpm} %{_libdir}/frr/modules/zebra_fpm.so %endif -%if %{with_rpki} - %{_libdir}/frr/modules/bgpd_rpki.so -%endif +%{_libdir}/frr/modules/zebra_cumulus_mlag.so +%{_libdir}/frr/modules/dplane_fpm_nl.so %{_libdir}/frr/modules/zebra_irdp.so +%{_libdir}/frr/modules/bgpd_bmp.so %{_bindir}/* %config(noreplace) %{configdir}/[!v]*.conf* %config(noreplace) %attr(750,%{frr_user},%{frr_user}) %{configdir}/daemons @@ -643,9 +712,33 @@ fi %files pythontools +%{_sbindir}/generate_support_bundle.py %{_sbindir}/frr-reload.py +%if 0%{?rhel} > 7 || 0%{?fedora} > 29 +%{_sbindir}/__pycache__/* +%else +%{_sbindir}/generate_support_bundle.pyc +%{_sbindir}/generate_support_bundle.pyo %{_sbindir}/frr-reload.pyc %{_sbindir}/frr-reload.pyo +%endif + + +%post rpki-rtrlib +# add rpki module to daemons +sed -i -e 's/^\(bgpd_options=\)\(.*\)\(".*\)/\1\2 -M rpki\3/' %{_sysconfdir}/frr/daemons + +%postun rpki-rtrlib +# remove rpki module from daemons +sed -i 's/ -M rpki//' %{_sysconfdir}/frr/daemons + +%files rpki-rtrlib +%{_libdir}/frr/modules/bgpd_rpki.so + + +%files snmp +%{_libdir}/libfrrsnmp.so* +%{_libdir}/frr/modules/*snmp.so %files devel @@ -665,7 +758,433 @@ fi %changelog -* Sun May 28 2018 Rafael Zalamena - %{version} +* Wed Mar 3 2021 Martin Winter - 7.5.1 +- BABEL: +- Fix connected route leak on change +- BFD: +- Session lookup was sometimes wrong +- Memory leak and handling cleanups +- In some situations handle vrf appropriately when receiving packets +- BGP: +- Peer Group Inheritance Fixes +- Dissallow attempt to peer peers reachable via blackholes +- Send BMP down message when reachability fails +- Cleanup handling of aggregator data when the AGG AS is 0 +- Handle `neighbor - 7.5 +- BFD +- Profile support +- Minimum ttl support +- BGP +- rpki VRF support +- GR fixes +- Add wide option to display of routes +- Add `maximum-prefix force` +- Add `bestpath-routes` to neighbor command +- Add `bgp shutdown message MSG...` command +- Add v6 Flowspec support +- Add `neighbor shutdown rtt` command +- Allow update-delay to be applied globaly +- EVPN +- Beginning of MultiHoming Support +- ISIS +- Segment Routing Support +- VRF Support +- Guard against adj timer display overflow +- Add support for Anycast-SIDs +- Add support for Topology Independent LFA (TI-LFA) +- Add `lsp-gen-interval 2` to isis configuration +- OSPF +- Segment Routing support for ECMP +- Various LSA fixes +- Prevent crash if transferring config amongst instances +- PBR +- Adding json support to commands +- DSCP/ECN based PBR Matching +- PIM +- Add more json support to commands +- Fix missing mesh-group commands +- MSDP SA forwarding +- Clear (s,g,rpt) ifchannel on (*, G) prune received +- Fix igmp querier election and IP address mapping +- Crash fix when RP is removed +- STATIC +- Northbound Support +- YANG +- Filter and route-map Support +- OSPF model definition +- BGP model definition +- VTYSH +- Speed up output across daemons +- Fix build-time errors for some --enable flags +- Speed up output of configuration across daemons +- ZEBRA +- nexthop group support for FPM +- northbound support for rib model +- Backup nexthop support +- netlink batching support +- Allow upper level protocols to request ARP +- Add json output for zebra ES, ES-EVI and access vlan dumps +- +- Upgrade to using libyang1.0.184 +- +- RPM +- Moved RPKI to subpackage +- Added SNMP subpackage +- +- As always there are too many bugfixes to list individually. This release +- compromises just over 1k of commits by the community, with contributors from +- 70 people. + +* Tue Jun 30 2020 Martin Winter - 7.4 +- BGPd +- Use sequence numbers for community lists +- Fixes to nexthop groups +- Add feature to limit outgoing number of routes +- Per Neighbor Graceful Restart +- Multiple Graceful Restart fixes +- Support sub-Type-4 and sub-Type-5 for the VPNv4 SRv6 backend +- rfc7606 support: treat certain malformed routes as withdraw +- allow origin override for route aggregates +- rfc6608 support: Subcodes for BGP Finite State Machine Error +- rfc7607 support: Codification of AS 0 Processing +- rfc6286 support: Autonomous-System-Wide Unique BGP Identifier for BGP-4 +- Unequal cost multipath (a.ka. weighted ECMP) with BGP link-bandwidth +- Enable rfc8212 by default except datacenter profile +- staticd +- Add debug support +- vtysh +- Add copy command to copy config from file into running config +- LDPd +- adding support for LDP ordered label distribution control +- ISISd +- IS-IS Segment Routing support +- SHARPd +- add initial support to add/remove lsps +- Zebra +- fix broadcast address in IPv4 networks with /31 mask +- Add Graceful Restart support for Protocol Daemon restarts +- lib +- migrate route-maps to use northbound interface +- plus countless bug fixes and other improvements + +* Wed May 06 2020 David Lamparter - 7.3.1 +- upstream 7.3.1 + +* Fri Feb 14 2020 Martin Winter - 7.3 +- BGPd +- EVPN PIP Support +- Route Aggregation code speed ups +- BGP Vector I/O speed ups +- New CLI: `set distance XXX` +- New CLI: `aggregate-address A.B.C.D/M route-map WORD` +- New CLI: `bgp reject-as-sets` +- New CLI: `advertise pip ...` +- New CLI: `match evpn rd ASN:NN_OR_IP-ADDRESS:NN` +- New CLI: `show bgp l2vpn evpn community|large-community X` +- New CLI: `show bgp l2vpn evpn A.B.C.D` +- Auto-completion for clear bgp command +- Add ability to set tcp socket buffer size +- OSPFd +- Partial MPLS TE support +- PBRd +- New CLI: `set vrf unchanged|NAME` +- BFDd +- VRF Support +- New CLI: 'show bfd peers brief' +- New CLI: 'clear bfd peer ...' +- PIMd +- Significant Speedups in accessing Internal Data for higher scale +- Support for joining any-source Multicast +- Updated CLI: 'show ip pim upstream-join-desired' +- New CLI: 'show ip pim channel' +- Debug Cleanup +- MLAG experimental support +- VRRPd +- VRF Support +- Northbound Conversion- NHRPd +- LDPd +- vtysh +- New CLI: `banner motd line LINE...` +- yang +- New CLI: `show yang operational-data XPATH` +- New CLI: `debug northbound` +- Zebra +- Nexthop Group support +- New CLI: 'debug zebra nexthop [detail]' +- New CLI: 'show router-id' +- MLAG experimental support +- watchfrr +- Additional status messages of system state to systemd +- New CLI: `watchfrr ignore DAEMON` +- Others +- As always all daemons have received too many bug fixes to fully list +- There has been a significant focus on increasing test coverage +- Change in Behavior: +- ISISd +- All areas created default automatically to level-1-2 +- Zebra +- Nexthop Group Installation in Kernel is turned on by default + if the kernel supports- New CLI: 'show nexthop-group rib [singleton]' +- Man Pages +- Renamed to frr-* to remove collision with other packages + +* Fri Jan 17 2020 Martin Winter - 7.2.1 +- BGPd +- Fix Addpath issue +- Do not apply eBGP policy for iBGP peers +- Show `ip` and `fqdn` in json output for `show [ip] bgp json` +- Fix large route-distinguisher's format +- Fix `no bgp listen range ...` configuration command +- Autocomplete neighbor for clear bgp +- Reflect the distance in RIB when it is changed for an arbitrary afi/safi +- Notify "Peer De-configured" after entering 'no neighbor cmd +- Fix per afi/safi addpath peer counting +- Rework BGP dampening to be per AFI/SAFI +- Do not send next-hop as :: in MP_REACH_NLRI if no link-local exists +- Override peer's TTL only if peer-group is configured with TTL +- Remove error message for unkown afi/safi combination +- Keep the session down if maximum-prefix is reached +- OSPFd +- Fix BFD down not tearing down OSPF adjacency for point-to-point net +- BFDd +- Fix multiple VRF handling +- VRF security improvement +- PIMd +- Fix rp crash +- NHRPd +- Make sure `no ip nhrp map ` works as expected +- LDPd +- Add missing sanity check in the parsing of label messages +- Zebra +- Use correct state when installing evpn macs +- Capture dplane plugin flags +- lib +- Fix interface config when vrf changes +- Fix Interface Infinite Loop Walk (for special interfaces such as bond) +- snapcraft +- fix missing vrrpd daemon +- Others +- Rename man pages (to avoid conflicts with other packages) +- Various other fixes for code cleanup and memory leaks + +* Fri Dec 27 2019 Donatas Abraitis +- Add CentOS 8 support + +* Tue Oct 15 2019 Martin Winter - 7.2 +- ALL Daemons +- -N to allow for config file locating when running FRR inside + of a namespace +- Impoved Testing across all daemons +- BFD +- VRF Support +- Conversion to Northbound interface +- BGP +- Aggregate-address add route-map support +- BMP Support +- Improved JSON output for many commands +- `show bgp afi safi summary failed` command +- `clear bop *` clears all peers +- Show FQDN for `show bgp ipv4 uni` commands +- Display BestPath selection reason as part of show commands +- EIGRP +- Infrastructure changes to allow VRF's +- SIGHUP signals the config reload +- Conversion to Northbound interface +- ISIS +- BFD Support +- Support for circuits with MTU > 8192 +- PBRD +- fwmark support as part of match criteria +- autocompletion of PBRMAPS +- Improved Nexthop Support +- PIMD +- PIM-BSM receive support +- Improved debugging support +- Store ECMP paths that are not currently legal for use +- Disallow igmp query from a non-connected source +- Many new cli improvements and changes +- VRRPD +- Add Support for RFC 3768 and RFC 5798 +- Route-Maps +- Add sequence numbers to access-lists +- Add `match ip next-hop type blackhole` +- Improved ability to notice dependency changes +- SHARPD +- `sharp watch [import|nexthop]` you can now specify a prefix instead + of assuming a /32 +- STATICD +- Significantly Improved NHT +- ZEBRA +- Many dataplane improvements for routes, neighbor table and EVPN +- NHT cli can now be specified per VRF and improved ability to control + NHT data being shown +- Removed duplicate processing of routes +- Improved debugablility +- RMAC and VxLan support for the FPM +- LIB +- RCU support +- Nexthop Group Improvements +- `log-filter WORD` added +- Building +- openssl support +- libcap should be used as part of build or significant slowdowns + will be experienced +- Lua builds have been fixed +- Improved Cross building + +* Mon Jun 17 2019 David Lamparter - 7.1 +- gRPC northbound plugin +- "table NNN" removed from zebra +- more dataplane MT work +- EVPN in non-default VRFs +- RFC 8212 (default deny policy for eBGP) +- RFC 8106 (IPv6 RA DNS options) + +* Wed May 8 2019 Martin Winter - 7.0.1 +- bgp: +- Don't send Updates with BGP Max-Prefix Overflow +- Make sure `next-hop-self all` backward compatible with force +- Fix as-path validation in "show bgp regexp" +- Fix interface-based peers to override peergroups +- Fix removing private AS numbers if local-as is used +- Fix show bgp labeled_unicast +- Add command to lookup prefixes in rpki table +- Fix peer count in "show bgp ipv6 summary" +- Add missing ipv6 only peer flag action +- Fix address family output in "show bgp [ipv4|ipv6] neighbors" +- Add missing checks for vpnv6 nexthops +- Fix nexthop for ipv6 vpn case +- rip: Fix removal of passive interfaces +- ospf: +- Fix json timer output +- Fix milliseconds in json output +- bfd: +- Fix source port according RFC 5881, Sec 4 +- Fix IPv6 link-local peer removal +- Fix interface clean up when deleting interface +- pim: Fix interface clean up when deleting interface +- nhrp: Fix interface clean up when deleting interface +- lib: +- Workaround to get FRR building with libyang 0.x and 1.x +- Fix in priv handling +- Make priv elevation thread-safe +- zebra: +- Pseudowire event recovery +- Fix race condition in label manager +- Fix system routes selection and next-hop tracking +- Set connected route metric based on devaddr metric +- Display metric for connected routes +- Add selected fib details to json output +- Always use replace if installing new route +- watchfrr: Silently ignore declare failures (for backward compatibility) +- RPM packages: Switch to new init script + +* Thu Feb 28 2019 Martin Winter - 7.0 +- Added libyang dependency: New work for northbound interface based on libyang +- Fabricd: New Daemon based on https://datatracker.ietf.org/doc/draft-white-openfabric/ +- various bug fixes and other enhancements + +* Sun Oct 7 2018 Martin Winter - 6.0 +- Staticd: New daemon responsible for management of static routes +- ISISd: Implement dst-src routing as per draft-ietf-isis-ipv6-dst-src-routing +- BFDd: new daemon for BFD (Bidrectional Forwarding Detection). Responsible + for notifying link changes to make routing protocols converge faster. +- various bug fixes + +* Thu Jul 5 2018 Martin Winter - 5.0.1 +- Support Automake 1.16.1 +- BGPd: Support for flowspec ICMP, DSCP, packet length, fragment and tcp flags +- BGPd: fix rpki validation for ipv6 +- VRF: Workaround for kernel bug on Linux 4.14 and newer +- Zebra: Fix interface based routes from zebra not marked up +- Zebra: Fix large zebra memory usage when redistribute between protocols +- Zebra: Allow route-maps to match on source instance +- BGPd: Backport peer-attr overrides, peer-level enforce-first-as and filtered-routes fix +- BGPd: fix for crash during display of filtered-routes +- BGPd: Actually display labeled unicast routes received +- Label Manager: Fix to work correctly behind a label manager proxy + +* Thu Jun 7 2018 Martin Winter - 5.0 +- PIM: Add a Multicast Trace Command draft-ietf-idmr-traceroute-ipm-05 +- IS-IS: Implement Three-Way Handshake as per RFC5303 +- BGPD: Implement VPN-VRF route leaking per RFC4364. +- BGPD: Implement VRF with NETNS backend +- BGPD: Flowspec +- PBRD: Add a new Policy Based Routing Daemon + +* Mon May 28 2018 Rafael Zalamena - Add BFDd support * Sun May 20 2018 Martin Winter diff --git a/ripd/rip_cli.c b/ripd/rip_cli.c index 346e93b8ef..5e64b7afdb 100644 --- a/ripd/rip_cli.c +++ b/ripd/rip_cli.c @@ -29,7 +29,7 @@ #include "libfrr.h" #include "ripd/ripd.h" -#include "ripd/rip_cli.h" +#include "ripd/rip_nb.h" #ifndef VTYSH_EXTRACT_PL #include "ripd/rip_cli_clippy.c" #endif @@ -37,7 +37,7 @@ /* * XPath: /frr-ripd:ripd/instance */ -DEFPY_NOSH (router_rip, +DEFPY_YANG_NOSH (router_rip, router_rip_cmd, "router rip [vrf NAME]", "Enable a routing process\n" @@ -62,7 +62,7 @@ DEFPY_NOSH (router_rip, return ret; } -DEFPY (no_router_rip, +DEFPY_YANG (no_router_rip, no_router_rip_cmd, "no router rip [vrf NAME]", NO_STR @@ -100,7 +100,7 @@ void cli_show_router_rip(struct vty *vty, struct lyd_node *dnode, /* * XPath: /frr-ripd:ripd/instance/allow-ecmp */ -DEFPY (rip_allow_ecmp, +DEFPY_YANG (rip_allow_ecmp, rip_allow_ecmp_cmd, "[no] allow-ecmp", NO_STR @@ -124,7 +124,7 @@ void cli_show_rip_allow_ecmp(struct vty *vty, struct lyd_node *dnode, /* * XPath: /frr-ripd:ripd/instance/default-information-originate */ -DEFPY (rip_default_information_originate, +DEFPY_YANG (rip_default_information_originate, rip_default_information_originate_cmd, "[no] default-information originate", NO_STR @@ -150,7 +150,7 @@ void cli_show_rip_default_information_originate(struct vty *vty, /* * XPath: /frr-ripd:ripd/instance/default-metric */ -DEFPY (rip_default_metric, +DEFPY_YANG (rip_default_metric, rip_default_metric_cmd, "default-metric (1-16)", "Set a metric of redistribute routes\n" @@ -162,7 +162,7 @@ DEFPY (rip_default_metric, return nb_cli_apply_changes(vty, NULL); } -DEFPY (no_rip_default_metric, +DEFPY_YANG (no_rip_default_metric, no_rip_default_metric_cmd, "no default-metric [(1-16)]", NO_STR @@ -184,7 +184,7 @@ void cli_show_rip_default_metric(struct vty *vty, struct lyd_node *dnode, /* * XPath: /frr-ripd:ripd/instance/distance/default */ -DEFPY (rip_distance, +DEFPY_YANG (rip_distance, rip_distance_cmd, "distance (1-255)", "Administrative distance\n" @@ -196,7 +196,7 @@ DEFPY (rip_distance, return nb_cli_apply_changes(vty, NULL); } -DEFPY (no_rip_distance, +DEFPY_YANG (no_rip_distance, no_rip_distance_cmd, "no distance [(1-255)]", NO_STR @@ -221,7 +221,7 @@ void cli_show_rip_distance(struct vty *vty, struct lyd_node *dnode, /* * XPath: /frr-ripd:ripd/instance/distance/source */ -DEFPY (rip_distance_source, +DEFPY_YANG (rip_distance_source, rip_distance_source_cmd, "[no] distance (1-255) A.B.C.D/M$prefix [WORD$acl]", NO_STR @@ -258,7 +258,7 @@ void cli_show_rip_distance_source(struct vty *vty, struct lyd_node *dnode, /* * XPath: /frr-ripd:ripd/instance/explicit-neighbor */ -DEFPY (rip_neighbor, +DEFPY_YANG (rip_neighbor, rip_neighbor_cmd, "[no] neighbor A.B.C.D", NO_STR @@ -280,7 +280,7 @@ void cli_show_rip_neighbor(struct vty *vty, struct lyd_node *dnode, /* * XPath: /frr-ripd:ripd/instance/network */ -DEFPY (rip_network_prefix, +DEFPY_YANG (rip_network_prefix, rip_network_prefix_cmd, "[no] network A.B.C.D/M", NO_STR @@ -302,7 +302,7 @@ void cli_show_rip_network_prefix(struct vty *vty, struct lyd_node *dnode, /* * XPath: /frr-ripd:ripd/instance/interface */ -DEFPY (rip_network_if, +DEFPY_YANG (rip_network_if, rip_network_if_cmd, "[no] network WORD", NO_STR @@ -324,7 +324,7 @@ void cli_show_rip_network_interface(struct vty *vty, struct lyd_node *dnode, /* * XPath: /frr-ripd:ripd/instance/offset-list */ -DEFPY (rip_offset_list, +DEFPY_YANG (rip_offset_list, rip_offset_list_cmd, "[no] offset-list WORD$acl $direction (0-16)$metric [IFNAME]", NO_STR @@ -367,7 +367,7 @@ void cli_show_rip_offset_list(struct vty *vty, struct lyd_node *dnode, /* * XPath: /frr-ripd:ripd/instance/passive-default */ -DEFPY (rip_passive_default, +DEFPY_YANG (rip_passive_default, rip_passive_default_cmd, "[no] passive-interface default", NO_STR @@ -393,7 +393,7 @@ void cli_show_rip_passive_default(struct vty *vty, struct lyd_node *dnode, * XPath: /frr-ripd:ripd/instance/passive-interface * /frr-ripd:ripd/instance/non-passive-interface */ -DEFPY (rip_passive_interface, +DEFPY_YANG (rip_passive_interface, rip_passive_interface_cmd, "[no] passive-interface IFNAME", NO_STR @@ -434,7 +434,7 @@ void cli_show_rip_non_passive_interface(struct vty *vty, struct lyd_node *dnode, /* * XPath: /frr-ripd:ripd/instance/redistribute */ -DEFPY (rip_redistribute, +DEFPY_YANG (rip_redistribute, rip_redistribute_cmd, "[no] redistribute " FRR_REDIST_STR_RIPD "$protocol [{metric (0-16)|route-map WORD}]", NO_STR @@ -477,7 +477,7 @@ void cli_show_rip_redistribute(struct vty *vty, struct lyd_node *dnode, /* * XPath: /frr-ripd:ripd/instance/static-route */ -DEFPY (rip_route, +DEFPY_YANG (rip_route, rip_route_cmd, "[no] route A.B.C.D/M", NO_STR @@ -499,7 +499,7 @@ void cli_show_rip_route(struct vty *vty, struct lyd_node *dnode, /* * XPath: /frr-ripd:ripd/instance/timers */ -DEFPY (rip_timers, +DEFPY_YANG (rip_timers, rip_timers_cmd, "timers basic (5-2147483647)$update (5-2147483647)$timeout (5-2147483647)$garbage", "Adjust routing timers\n" @@ -518,7 +518,7 @@ DEFPY (rip_timers, return nb_cli_apply_changes(vty, "./timers"); } -DEFPY (no_rip_timers, +DEFPY_YANG (no_rip_timers, no_rip_timers_cmd, "no timers basic [(5-2147483647) (5-2147483647) (5-2147483647)]", NO_STR @@ -547,7 +547,7 @@ void cli_show_rip_timers(struct vty *vty, struct lyd_node *dnode, /* * XPath: /frr-ripd:ripd/instance/version */ -DEFPY (rip_version, +DEFPY_YANG (rip_version, rip_version_cmd, "version (1-2)", "Set routing protocol version\n" @@ -560,7 +560,7 @@ DEFPY (rip_version, return nb_cli_apply_changes(vty, NULL); } -DEFPY (no_rip_version, +DEFPY_YANG (no_rip_version, no_rip_version_cmd, "no version [(1-2)]", NO_STR @@ -596,7 +596,7 @@ void cli_show_rip_version(struct vty *vty, struct lyd_node *dnode, /* * XPath: /frr-interface:lib/interface/frr-ripd:rip/split-horizon */ -DEFPY (ip_rip_split_horizon, +DEFPY_YANG (ip_rip_split_horizon, ip_rip_split_horizon_cmd, "[no] ip rip split-horizon [poisoned-reverse$poisoned_reverse]", NO_STR @@ -641,7 +641,7 @@ void cli_show_ip_rip_split_horizon(struct vty *vty, struct lyd_node *dnode, /* * XPath: /frr-interface:lib/interface/frr-ripd:rip/v2-broadcast */ -DEFPY (ip_rip_v2_broadcast, +DEFPY_YANG (ip_rip_v2_broadcast, ip_rip_v2_broadcast_cmd, "[no] ip rip v2-broadcast", NO_STR @@ -667,7 +667,7 @@ void cli_show_ip_rip_v2_broadcast(struct vty *vty, struct lyd_node *dnode, /* * XPath: /frr-interface:lib/interface/frr-ripd:rip/version-receive */ -DEFPY (ip_rip_receive_version, +DEFPY_YANG (ip_rip_receive_version, ip_rip_receive_version_cmd, "ip rip receive version <{1$v1|2$v2}|none>", IP_STR @@ -694,7 +694,7 @@ DEFPY (ip_rip_receive_version, return nb_cli_apply_changes(vty, "./frr-ripd:rip"); } -DEFPY (no_ip_rip_receive_version, +DEFPY_YANG (no_ip_rip_receive_version, no_ip_rip_receive_version_cmd, "no ip rip receive version [<{1|2}|none>]", NO_STR @@ -736,7 +736,7 @@ void cli_show_ip_rip_receive_version(struct vty *vty, struct lyd_node *dnode, /* * XPath: /frr-interface:lib/interface/frr-ripd:rip/version-send */ -DEFPY (ip_rip_send_version, +DEFPY_YANG (ip_rip_send_version, ip_rip_send_version_cmd, "ip rip send version <{1$v1|2$v2}|none>", IP_STR @@ -763,7 +763,7 @@ DEFPY (ip_rip_send_version, return nb_cli_apply_changes(vty, "./frr-ripd:rip"); } -DEFPY (no_ip_rip_send_version, +DEFPY_YANG (no_ip_rip_send_version, no_ip_rip_send_version_cmd, "no ip rip send version [<{1|2}|none>]", NO_STR @@ -805,7 +805,7 @@ void cli_show_ip_rip_send_version(struct vty *vty, struct lyd_node *dnode, /* * XPath: /frr-interface:lib/interface/frr-ripd:rip/authentication-scheme */ -DEFPY (ip_rip_authentication_mode, +DEFPY_YANG (ip_rip_authentication_mode, ip_rip_authentication_mode_cmd, "ip rip authentication mode $auth_length]|text$mode>", IP_STR @@ -829,13 +829,15 @@ DEFPY (ip_rip_authentication_mode, nb_cli_enqueue_change(vty, "./authentication-scheme/mode", NB_OP_MODIFY, strmatch(mode, "md5") ? "md5" : "plain-text"); - nb_cli_enqueue_change(vty, "./authentication-scheme/md5-auth-length", - NB_OP_MODIFY, value); + if (strmatch(mode, "md5")) + nb_cli_enqueue_change(vty, + "./authentication-scheme/md5-auth-length", + NB_OP_MODIFY, value); return nb_cli_apply_changes(vty, "./frr-ripd:rip"); } -DEFPY (no_ip_rip_authentication_mode, +DEFPY_YANG (no_ip_rip_authentication_mode, no_ip_rip_authentication_mode_cmd, "no ip rip authentication mode []|text>]", NO_STR @@ -852,7 +854,7 @@ DEFPY (no_ip_rip_authentication_mode, nb_cli_enqueue_change(vty, "./authentication-scheme/mode", NB_OP_MODIFY, NULL); nb_cli_enqueue_change(vty, "./authentication-scheme/md5-auth-length", - NB_OP_MODIFY, NULL); + NB_OP_DESTROY, NULL); return nb_cli_apply_changes(vty, "./frr-ripd:rip"); } @@ -886,7 +888,7 @@ void cli_show_ip_rip_authentication_scheme(struct vty *vty, /* * XPath: /frr-interface:lib/interface/frr-ripd:rip/authentication-password */ -DEFPY (ip_rip_authentication_string, +DEFPY_YANG (ip_rip_authentication_string, ip_rip_authentication_string_cmd, "ip rip authentication string LINE$password", IP_STR @@ -914,7 +916,7 @@ DEFPY (ip_rip_authentication_string, return nb_cli_apply_changes(vty, "./frr-ripd:rip"); } -DEFPY (no_ip_rip_authentication_string, +DEFPY_YANG (no_ip_rip_authentication_string, no_ip_rip_authentication_string_cmd, "no ip rip authentication string [LINE]", NO_STR @@ -941,7 +943,7 @@ void cli_show_ip_rip_authentication_string(struct vty *vty, /* * XPath: /frr-interface:lib/interface/frr-ripd:rip/authentication-key-chain */ -DEFPY (ip_rip_authentication_key_chain, +DEFPY_YANG (ip_rip_authentication_key_chain, ip_rip_authentication_key_chain_cmd, "ip rip authentication key-chain LINE$keychain", IP_STR @@ -963,7 +965,7 @@ DEFPY (ip_rip_authentication_key_chain, return nb_cli_apply_changes(vty, "./frr-ripd:rip"); } -DEFPY (no_ip_rip_authentication_key_chain, +DEFPY_YANG (no_ip_rip_authentication_key_chain, no_ip_rip_authentication_key_chain_cmd, "no ip rip authentication key-chain [LINE]", NO_STR @@ -990,7 +992,7 @@ void cli_show_ip_rip_authentication_key_chain(struct vty *vty, /* * XPath: /frr-ripd:clear-rip-route */ -DEFPY (clear_ip_rip, +DEFPY_YANG (clear_ip_rip, clear_ip_rip_cmd, "clear ip rip [vrf WORD]", CLEAR_STR @@ -999,6 +1001,7 @@ DEFPY (clear_ip_rip, VRF_CMD_HELP_STR) { struct list *input; + int ret; input = list_new(); if (vrf) { @@ -1009,7 +1012,11 @@ DEFPY (clear_ip_rip, listnode_add(input, yang_vrf); } - return nb_cli_rpc("/frr-ripd:clear-rip-route", input, NULL); + ret = nb_cli_rpc("/frr-ripd:clear-rip-route", input, NULL); + + list_delete(&input); + + return ret; } void rip_cli_init(void) diff --git a/ripd/rip_cli.h b/ripd/rip_cli.h deleted file mode 100644 index ef1e1504e8..0000000000 --- a/ripd/rip_cli.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 1997, 1998, 1999 Kunihiro Ishiguro - * Copyright (C) 2018 NetDEF, Inc. - * Renato Westphal - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; see the file COPYING; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef _FRR_RIP_CLI_H_ -#define _FRR_RIP_CLI_H_ - -extern void cli_show_router_rip(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -extern void cli_show_rip_allow_ecmp(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -extern void cli_show_rip_default_information_originate(struct vty *vty, - struct lyd_node *dnode, - bool show_defaults); -extern void cli_show_rip_default_metric(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -extern void cli_show_rip_distance(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -extern void cli_show_rip_distance_source(struct vty *vty, - struct lyd_node *dnode, - bool show_defaults); -extern void cli_show_rip_neighbor(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -extern void cli_show_rip_network_prefix(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -extern void cli_show_rip_network_interface(struct vty *vty, - struct lyd_node *dnode, - bool show_defaults); -extern void cli_show_rip_offset_list(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -extern void cli_show_rip_passive_default(struct vty *vty, - struct lyd_node *dnode, - bool show_defaults); -extern void cli_show_rip_passive_interface(struct vty *vty, - struct lyd_node *dnode, - bool show_defaults); -extern void cli_show_rip_non_passive_interface(struct vty *vty, - struct lyd_node *dnode, - bool show_defaults); -extern void cli_show_rip_redistribute(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -extern void cli_show_rip_route(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -extern void cli_show_rip_timers(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -extern void cli_show_rip_version(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -extern void cli_show_ip_rip_split_horizon(struct vty *vty, - struct lyd_node *dnode, - bool show_defaults); -extern void cli_show_ip_rip_v2_broadcast(struct vty *vty, - struct lyd_node *dnode, - bool show_defaults); -extern void cli_show_ip_rip_receive_version(struct vty *vty, - struct lyd_node *dnode, - bool show_defaults); -extern void cli_show_ip_rip_send_version(struct vty *vty, - struct lyd_node *dnode, - bool show_defaults); -extern void cli_show_ip_rip_authentication_scheme(struct vty *vty, - struct lyd_node *dnode, - bool show_defaults); -extern void cli_show_ip_rip_authentication_string(struct vty *vty, - struct lyd_node *dnode, - bool show_defaults); -extern void cli_show_ip_rip_authentication_key_chain(struct vty *vty, - struct lyd_node *dnode, - bool show_defaults); - -#endif /* _FRR_RIP_CLI_H_ */ diff --git a/ripd/rip_debug.c b/ripd/rip_debug.c index 3356d99c2a..871ee8e87e 100644 --- a/ripd/rip_debug.c +++ b/ripd/rip_debug.c @@ -172,10 +172,14 @@ DEFUN (no_debug_rip_zebra, return CMD_SUCCESS; } +static int config_write_debug(struct vty *vty); /* Debug node. */ -static struct cmd_node debug_node = {DEBUG_NODE, - "", /* Debug node has no interface. */ - 1}; +static struct cmd_node debug_node = { + .name = "debug", + .node = DEBUG_NODE, + .prompt = "", + .config_write = config_write_debug, +}; static int config_write_debug(struct vty *vty) { @@ -210,7 +214,7 @@ void rip_debug_init(void) rip_debug_packet = 0; rip_debug_zebra = 0; - install_node(&debug_node, config_write_debug); + install_node(&debug_node); install_element(ENABLE_NODE, &show_debugging_rip_cmd); install_element(ENABLE_NODE, &debug_rip_events_cmd); diff --git a/ripd/rip_interface.c b/ripd/rip_interface.c index b909cbcb2b..aab756aee9 100644 --- a/ripd/rip_interface.c +++ b/ripd/rip_interface.c @@ -43,6 +43,8 @@ #include "ripd/rip_debug.h" #include "ripd/rip_interface.h" +DEFINE_MTYPE_STATIC(RIPD, RIP_INTERFACE, "RIP interface") +DEFINE_MTYPE(RIPD, RIP_INTERFACE_STRING, "RIP Interface String") DEFINE_HOOK(rip_ifaddr_add, (struct connected * ifc), (ifc)) DEFINE_HOOK(rip_ifaddr_del, (struct connected * ifc), (ifc)) @@ -116,8 +118,7 @@ void rip_interface_multicast_set(int sock, struct connected *connected) if (setsockopt_ipv4_multicast_if(sock, addr, connected->ifp->ifindex) < 0) { zlog_warn( - "Can't setsockopt IP_MULTICAST_IF on fd %d to " - "ifindex %d for interface %s", + "Can't setsockopt IP_MULTICAST_IF on fd %d to ifindex %d for interface %s", sock, connected->ifp->ifindex, connected->ifp->name); } @@ -214,7 +215,7 @@ rip_request_neighbor (struct in_addr addr) { struct sockaddr_in to; - memset (&to, 0, sizeof (struct sockaddr_in)); + memset (&to, 0, sizeof(struct sockaddr_in)); to.sin_port = htons (RIP_PORT_DEFAULT); to.sin_addr = addr; @@ -344,51 +345,34 @@ int if_check_address(struct rip *rip, struct in_addr addr) } /* Inteface link down message processing. */ -int rip_interface_down(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int rip_ifp_down(struct interface *ifp) { - struct interface *ifp; - struct stream *s; - - s = zclient->ibuf; - - /* zebra_interface_state_read() updates interface structure in - iflist. */ - ifp = zebra_interface_state_read(s, vrf_id); - - if (ifp == NULL) - return 0; - rip_interface_sync(ifp); rip_if_down(ifp); - if (IS_RIP_DEBUG_ZEBRA) + if (IS_RIP_DEBUG_ZEBRA) { + struct vrf *vrf = vrf_lookup_by_id(ifp->vrf_id); + zlog_debug( - "interface %s vrf %u index %d flags %llx metric %d mtu %d is down", - ifp->name, ifp->vrf_id, ifp->ifindex, + "interface %s vrf %s(%u) index %d flags %llx metric %d mtu %d is down", + ifp->name, VRF_LOGNAME(vrf), ifp->vrf_id, ifp->ifindex, (unsigned long long)ifp->flags, ifp->metric, ifp->mtu); + } return 0; } /* Inteface link up message processing */ -int rip_interface_up(int command, struct zclient *zclient, zebra_size_t length, - vrf_id_t vrf_id) +static int rip_ifp_up(struct interface *ifp) { - struct interface *ifp; - - /* zebra_interface_state_read () updates interface structure in - iflist. */ - ifp = zebra_interface_state_read(zclient->ibuf, vrf_id); - - if (ifp == NULL) - return 0; + if (IS_RIP_DEBUG_ZEBRA) { + struct vrf *vrf = vrf_lookup_by_id(ifp->vrf_id); - if (IS_RIP_DEBUG_ZEBRA) zlog_debug( - "interface %s vrf %u index %d flags %#llx metric %d mtu %d is up", - ifp->name, ifp->vrf_id, ifp->ifindex, + "interface %s vrf %s(%u) index %d flags %#llx metric %d mtu %d is up", + ifp->name, VRF_LOGNAME(vrf), ifp->vrf_id, ifp->ifindex, (unsigned long long)ifp->flags, ifp->metric, ifp->mtu); + } rip_interface_sync(ifp); @@ -405,19 +389,17 @@ int rip_interface_up(int command, struct zclient *zclient, zebra_size_t length, } /* Inteface addition message from zebra. */ -int rip_interface_add(int command, struct zclient *zclient, zebra_size_t length, - vrf_id_t vrf_id) +static int rip_ifp_create(struct interface *ifp) { - struct interface *ifp; - - ifp = zebra_interface_add_read(zclient->ibuf, vrf_id); rip_interface_sync(ifp); - if (IS_RIP_DEBUG_ZEBRA) + if (IS_RIP_DEBUG_ZEBRA) { + struct vrf *vrf = vrf_lookup_by_id(ifp->vrf_id); zlog_debug( - "interface add %s vrf %u index %d flags %#llx metric %d mtu %d", - ifp->name, ifp->vrf_id, ifp->ifindex, + "interface add %s vrf %s(%u) index %d flags %#llx metric %d mtu %d", + ifp->name, VRF_LOGNAME(vrf), ifp->vrf_id, ifp->ifindex, (unsigned long long)ifp->flags, ifp->metric, ifp->mtu); + } /* Check if this interface is RIP enabled or not.*/ rip_enable_apply(ifp); @@ -436,40 +418,26 @@ int rip_interface_add(int command, struct zclient *zclient, zebra_size_t length, return 0; } -int rip_interface_delete(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int rip_ifp_destroy(struct interface *ifp) { - struct interface *ifp; - struct stream *s; - - - s = zclient->ibuf; - /* zebra_interface_state_read() updates interface structure in iflist */ - ifp = zebra_interface_state_read(s, vrf_id); - - if (ifp == NULL) - return 0; + struct vrf *vrf = vrf_lookup_by_id(ifp->vrf_id); rip_interface_sync(ifp); if (if_is_up(ifp)) { rip_if_down(ifp); } - zlog_info( - "interface delete %s vrf %u index %d flags %#llx metric %d mtu %d", - ifp->name, ifp->vrf_id, ifp->ifindex, - (unsigned long long)ifp->flags, ifp->metric, ifp->mtu); - - /* To support pseudo interface do not free interface structure. */ - /* if_delete(ifp); */ - if_set_index(ifp, IFINDEX_INTERNAL); + if (IS_RIP_DEBUG_ZEBRA) + zlog_debug( + "interface delete %s vrf %s(%u) index %d flags %#llx metric %d mtu %d", + ifp->name, VRF_LOGNAME(vrf), ifp->vrf_id, ifp->ifindex, + (unsigned long long)ifp->flags, ifp->metric, ifp->mtu); return 0; } /* VRF update for an interface. */ -int rip_interface_vrf_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +int rip_interface_vrf_update(ZAPI_CALLBACK_ARGS) { struct interface *ifp; vrf_id_t new_vrf_id; @@ -479,9 +447,14 @@ int rip_interface_vrf_update(int command, struct zclient *zclient, if (!ifp) return 0; - if (IS_RIP_DEBUG_ZEBRA) - zlog_debug("interface %s VRF change vrf_id %u new vrf id %u", - ifp->name, vrf_id, new_vrf_id); + if (IS_RIP_DEBUG_ZEBRA) { + struct vrf *vrf = vrf_lookup_by_id(vrf_id); + struct vrf *nvrf = vrf_lookup_by_id(new_vrf_id); + + zlog_debug("interface %s VRF change vrf %s(%u) new vrf %s(%u)", + ifp->name, VRF_LOGNAME(vrf), vrf_id, + VRF_LOGNAME(nvrf), new_vrf_id); + } if_update_to_new_vrf(ifp, new_vrf_id); rip_interface_sync(ifp); @@ -615,8 +588,7 @@ static void rip_apply_address_add(struct connected *ifc) 0); } -int rip_interface_address_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +int rip_interface_address_add(ZAPI_CALLBACK_ARGS) { struct connected *ifc; struct prefix *p; @@ -669,8 +641,7 @@ static void rip_apply_address_del(struct connected *ifc) &address, ifc->ifp->ifindex); } -int rip_interface_address_delete(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +int rip_interface_address_delete(ZAPI_CALLBACK_ARGS) { struct connected *ifc; struct prefix *p; @@ -692,7 +663,7 @@ int rip_interface_address_delete(int command, struct zclient *zclient, rip_apply_address_del(ifc); } - connected_free(ifc); + connected_free(&ifc); } return 0; @@ -1222,8 +1193,13 @@ int rip_show_network_config(struct vty *vty, struct rip *rip) return 0; } +static int rip_interface_config_write(struct vty *vty); static struct cmd_node interface_node = { - INTERFACE_NODE, "%s(config-if)# ", 1, + .name = "interface", + .node = INTERFACE_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-if)# ", + .config_write = rip_interface_config_write, }; void rip_interface_sync(struct interface *ifp) @@ -1254,7 +1230,6 @@ static int rip_interface_delete_hook(struct interface *ifp) { rip_interface_reset(ifp->info); XFREE(MTYPE_RIP_INTERFACE, ifp->info); - ifp->info = NULL; return 0; } @@ -1266,6 +1241,8 @@ void rip_if_init(void) hook_register_prio(if_del, 0, rip_interface_delete_hook); /* Install interface node. */ - install_node(&interface_node, rip_interface_config_write); + install_node(&interface_node); if_cmd_init(); + if_zapi_callbacks(rip_ifp_create, rip_ifp_up, + rip_ifp_down, rip_ifp_destroy); } diff --git a/ripd/rip_interface.h b/ripd/rip_interface.h index 303be0315d..715daf2e50 100644 --- a/ripd/rip_interface.h +++ b/ripd/rip_interface.h @@ -20,8 +20,11 @@ #ifndef _QUAGGA_RIP_INTERFACE_H #define _QUAGGA_RIP_INTERFACE_H +#include "memory.h" #include "zclient.h" +DECLARE_MTYPE(RIP_INTERFACE_STRING) + extern int rip_interface_down(int, struct zclient *, zebra_size_t, vrf_id_t); extern int rip_interface_up(int, struct zclient *, zebra_size_t, vrf_id_t); extern int rip_interface_add(int, struct zclient *, zebra_size_t, vrf_id_t); @@ -30,8 +33,7 @@ extern int rip_interface_address_add(int, struct zclient *, zebra_size_t, vrf_id_t); extern int rip_interface_address_delete(int, struct zclient *, zebra_size_t, vrf_id_t); -extern int rip_interface_vrf_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id); +extern int rip_interface_vrf_update(ZAPI_CALLBACK_ARGS); extern void rip_interface_sync(struct interface *ifp); #endif /* _QUAGGA_RIP_INTERFACE_H */ diff --git a/ripd/rip_main.c b/ripd/rip_main.c index 65da51f83a..7e381887fc 100644 --- a/ripd/rip_main.c +++ b/ripd/rip_main.c @@ -25,7 +25,6 @@ #include "thread.h" #include "command.h" #include "memory.h" -#include "memory_vty.h" #include "prefix.h" #include "filter.h" #include "keychain.h" @@ -36,15 +35,14 @@ #include "vrf.h" #include "if_rmap.h" #include "libfrr.h" +#include "routemap.h" #include "ripd/ripd.h" +#include "ripd/rip_nb.h" #include "ripd/rip_errors.h" /* ripd options. */ -#if CONFDATE > 20190521 - CPP_NOTICE("-r / --retain has reached deprecation EOL, remove") -#endif -static struct option longopts[] = {{"retain", no_argument, NULL, 'r'}, {0}}; +static struct option longopts[] = {{0}}; /* ripd privileges */ zebra_capabilities_t _caps_p[] = {ZCAP_NET_RAW, ZCAP_BIND, ZCAP_SYS_ADMIN}; @@ -115,9 +113,12 @@ static struct quagga_signal_t ripd_signals[] = { }, }; -static const struct frr_yang_module_info *ripd_yang_modules[] = { +static const struct frr_yang_module_info *const ripd_yang_modules[] = { + &frr_filter_info, &frr_interface_info, &frr_ripd_info, + &frr_route_map_info, + &frr_vrf_info, }; FRR_DAEMON_INFO(ripd, RIP, .vty_port = RIP_VTY_PORT, @@ -129,10 +130,7 @@ FRR_DAEMON_INFO(ripd, RIP, .vty_port = RIP_VTY_PORT, .privs = &ripd_privs, .yang_modules = ripd_yang_modules, .n_yang_modules = array_size(ripd_yang_modules), ) -#if CONFDATE > 20190521 -CPP_NOTICE("-r / --retain has reached deprecation EOL, remove") -#endif -#define DEPRECATED_OPTIONS "r" +#define DEPRECATED_OPTIONS "" /* Main routine of ripd. */ int main(int argc, char **argv) @@ -184,5 +182,5 @@ int main(int argc, char **argv) frr_run(master); /* Not reached. */ - return (0); + return 0; } diff --git a/ripd/rip_memory.c b/ripd/rip_memory.c deleted file mode 100644 index 7d703a86db..0000000000 --- a/ripd/rip_memory.c +++ /dev/null @@ -1,36 +0,0 @@ -/* ripd memory type definitions - * - * Copyright (C) 2015 David Lamparter - * - * This file is part of Quagga. - * - * Quagga is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2, or (at your option) any - * later version. - * - * Quagga is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; see the file COPYING; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "rip_memory.h" - -DEFINE_MGROUP(RIPD, "ripd") -DEFINE_MTYPE(RIPD, RIP, "RIP structure") -DEFINE_MTYPE(RIPD, RIP_VRF_NAME, "RIP VRF name") -DEFINE_MTYPE(RIPD, RIP_INFO, "RIP route info") -DEFINE_MTYPE(RIPD, RIP_INTERFACE, "RIP interface") -DEFINE_MTYPE(RIPD, RIP_INTERFACE_STRING, "RIP Interface String") -DEFINE_MTYPE(RIPD, RIP_PEER, "RIP peer") -DEFINE_MTYPE(RIPD, RIP_OFFSET_LIST, "RIP offset list") -DEFINE_MTYPE(RIPD, RIP_DISTANCE, "RIP distance") diff --git a/ripd/rip_memory.h b/ripd/rip_memory.h deleted file mode 100644 index 1f9d8f500f..0000000000 --- a/ripd/rip_memory.h +++ /dev/null @@ -1,37 +0,0 @@ -/* ripd memory type declarations - * - * Copyright (C) 2015 David Lamparter - * - * This file is part of Quagga. - * - * Quagga is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2, or (at your option) any - * later version. - * - * Quagga is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; see the file COPYING; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef _QUAGGA_RIP_MEMORY_H -#define _QUAGGA_RIP_MEMORY_H - -#include "memory.h" - -DECLARE_MGROUP(RIPD) -DECLARE_MTYPE(RIP) -DECLARE_MTYPE(RIP_VRF_NAME) -DECLARE_MTYPE(RIP_INFO) -DECLARE_MTYPE(RIP_INTERFACE) -DECLARE_MTYPE(RIP_INTERFACE_STRING) -DECLARE_MTYPE(RIP_PEER) -DECLARE_MTYPE(RIP_OFFSET_LIST) -DECLARE_MTYPE(RIP_DISTANCE) - -#endif /* _QUAGGA_RIP_MEMORY_H */ diff --git a/ripd/rip_nb.c b/ripd/rip_nb.c new file mode 100644 index 0000000000..4716041ad6 --- /dev/null +++ b/ripd/rip_nb.c @@ -0,0 +1,369 @@ +/* + * Copyright (C) 2018 NetDEF, Inc. + * Renato Westphal + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "northbound.h" +#include "libfrr.h" + +#include "ripd/rip_nb.h" + +/* clang-format off */ +const struct frr_yang_module_info frr_ripd_info = { + .name = "frr-ripd", + .nodes = { + { + .xpath = "/frr-ripd:ripd/instance", + .cbs = { + .cli_show = cli_show_router_rip, + .create = ripd_instance_create, + .destroy = ripd_instance_destroy, + .get_keys = ripd_instance_get_keys, + .get_next = ripd_instance_get_next, + .lookup_entry = ripd_instance_lookup_entry, + }, + }, + { + .xpath = "/frr-ripd:ripd/instance/allow-ecmp", + .cbs = { + .cli_show = cli_show_rip_allow_ecmp, + .modify = ripd_instance_allow_ecmp_modify, + }, + }, + { + .xpath = "/frr-ripd:ripd/instance/default-information-originate", + .cbs = { + .cli_show = cli_show_rip_default_information_originate, + .modify = ripd_instance_default_information_originate_modify, + }, + }, + { + .xpath = "/frr-ripd:ripd/instance/default-metric", + .cbs = { + .cli_show = cli_show_rip_default_metric, + .modify = ripd_instance_default_metric_modify, + }, + }, + { + .xpath = "/frr-ripd:ripd/instance/distance/default", + .cbs = { + .cli_show = cli_show_rip_distance, + .modify = ripd_instance_distance_default_modify, + }, + }, + { + .xpath = "/frr-ripd:ripd/instance/distance/source", + .cbs = { + .cli_show = cli_show_rip_distance_source, + .create = ripd_instance_distance_source_create, + .destroy = ripd_instance_distance_source_destroy, + }, + }, + { + .xpath = "/frr-ripd:ripd/instance/distance/source/distance", + .cbs = { + .modify = ripd_instance_distance_source_distance_modify, + }, + }, + { + .xpath = "/frr-ripd:ripd/instance/distance/source/access-list", + .cbs = { + .destroy = ripd_instance_distance_source_access_list_destroy, + .modify = ripd_instance_distance_source_access_list_modify, + }, + }, + { + .xpath = "/frr-ripd:ripd/instance/explicit-neighbor", + .cbs = { + .cli_show = cli_show_rip_neighbor, + .create = ripd_instance_explicit_neighbor_create, + .destroy = ripd_instance_explicit_neighbor_destroy, + }, + }, + { + .xpath = "/frr-ripd:ripd/instance/network", + .cbs = { + .cli_show = cli_show_rip_network_prefix, + .create = ripd_instance_network_create, + .destroy = ripd_instance_network_destroy, + }, + }, + { + .xpath = "/frr-ripd:ripd/instance/interface", + .cbs = { + .cli_show = cli_show_rip_network_interface, + .create = ripd_instance_interface_create, + .destroy = ripd_instance_interface_destroy, + }, + }, + { + .xpath = "/frr-ripd:ripd/instance/offset-list", + .cbs = { + .cli_show = cli_show_rip_offset_list, + .create = ripd_instance_offset_list_create, + .destroy = ripd_instance_offset_list_destroy, + }, + }, + { + .xpath = "/frr-ripd:ripd/instance/offset-list/access-list", + .cbs = { + .modify = ripd_instance_offset_list_access_list_modify, + }, + }, + { + .xpath = "/frr-ripd:ripd/instance/offset-list/metric", + .cbs = { + .modify = ripd_instance_offset_list_metric_modify, + }, + }, + { + .xpath = "/frr-ripd:ripd/instance/passive-default", + .cbs = { + .cli_show = cli_show_rip_passive_default, + .modify = ripd_instance_passive_default_modify, + }, + }, + { + .xpath = "/frr-ripd:ripd/instance/passive-interface", + .cbs = { + .cli_show = cli_show_rip_passive_interface, + .create = ripd_instance_passive_interface_create, + .destroy = ripd_instance_passive_interface_destroy, + }, + }, + { + .xpath = "/frr-ripd:ripd/instance/non-passive-interface", + .cbs = { + .cli_show = cli_show_rip_non_passive_interface, + .create = ripd_instance_non_passive_interface_create, + .destroy = ripd_instance_non_passive_interface_destroy, + }, + }, + { + .xpath = "/frr-ripd:ripd/instance/redistribute", + .cbs = { + .apply_finish = ripd_instance_redistribute_apply_finish, + .cli_show = cli_show_rip_redistribute, + .create = ripd_instance_redistribute_create, + .destroy = ripd_instance_redistribute_destroy, + }, + }, + { + .xpath = "/frr-ripd:ripd/instance/redistribute/route-map", + .cbs = { + .destroy = ripd_instance_redistribute_route_map_destroy, + .modify = ripd_instance_redistribute_route_map_modify, + }, + }, + { + .xpath = "/frr-ripd:ripd/instance/redistribute/metric", + .cbs = { + .destroy = ripd_instance_redistribute_metric_destroy, + .modify = ripd_instance_redistribute_metric_modify, + }, + }, + { + .xpath = "/frr-ripd:ripd/instance/static-route", + .cbs = { + .cli_show = cli_show_rip_route, + .create = ripd_instance_static_route_create, + .destroy = ripd_instance_static_route_destroy, + }, + }, + { + .xpath = "/frr-ripd:ripd/instance/timers", + .cbs = { + .apply_finish = ripd_instance_timers_apply_finish, + .cli_show = cli_show_rip_timers, + }, + }, + { + .xpath = "/frr-ripd:ripd/instance/timers/flush-interval", + .cbs = { + .modify = ripd_instance_timers_flush_interval_modify, + }, + }, + { + .xpath = "/frr-ripd:ripd/instance/timers/holddown-interval", + .cbs = { + .modify = ripd_instance_timers_holddown_interval_modify, + }, + }, + { + .xpath = "/frr-ripd:ripd/instance/timers/update-interval", + .cbs = { + .modify = ripd_instance_timers_update_interval_modify, + }, + }, + { + .xpath = "/frr-ripd:ripd/instance/version", + .cbs = { + .cli_show = cli_show_rip_version, + }, + }, + { + .xpath = "/frr-ripd:ripd/instance/version/receive", + .cbs = { + .modify = ripd_instance_version_receive_modify, + }, + }, + { + .xpath = "/frr-ripd:ripd/instance/version/send", + .cbs = { + .modify = ripd_instance_version_send_modify, + }, + }, + { + .xpath = "/frr-interface:lib/interface/frr-ripd:rip/split-horizon", + .cbs = { + .cli_show = cli_show_ip_rip_split_horizon, + .modify = lib_interface_rip_split_horizon_modify, + }, + }, + { + .xpath = "/frr-interface:lib/interface/frr-ripd:rip/v2-broadcast", + .cbs = { + .cli_show = cli_show_ip_rip_v2_broadcast, + .modify = lib_interface_rip_v2_broadcast_modify, + }, + }, + { + .xpath = "/frr-interface:lib/interface/frr-ripd:rip/version-receive", + .cbs = { + .cli_show = cli_show_ip_rip_receive_version, + .modify = lib_interface_rip_version_receive_modify, + }, + }, + { + .xpath = "/frr-interface:lib/interface/frr-ripd:rip/version-send", + .cbs = { + .cli_show = cli_show_ip_rip_send_version, + .modify = lib_interface_rip_version_send_modify, + }, + }, + { + .xpath = "/frr-interface:lib/interface/frr-ripd:rip/authentication-scheme", + .cbs = { + .cli_show = cli_show_ip_rip_authentication_scheme, + }, + }, + { + .xpath = "/frr-interface:lib/interface/frr-ripd:rip/authentication-scheme/mode", + .cbs = { + .modify = lib_interface_rip_authentication_scheme_mode_modify, + }, + }, + { + .xpath = "/frr-interface:lib/interface/frr-ripd:rip/authentication-scheme/md5-auth-length", + .cbs = { + .destroy = lib_interface_rip_authentication_scheme_md5_auth_length_destroy, + .modify = lib_interface_rip_authentication_scheme_md5_auth_length_modify, + }, + }, + { + .xpath = "/frr-interface:lib/interface/frr-ripd:rip/authentication-password", + .cbs = { + .cli_show = cli_show_ip_rip_authentication_string, + .destroy = lib_interface_rip_authentication_password_destroy, + .modify = lib_interface_rip_authentication_password_modify, + }, + }, + { + .xpath = "/frr-interface:lib/interface/frr-ripd:rip/authentication-key-chain", + .cbs = { + .cli_show = cli_show_ip_rip_authentication_key_chain, + .destroy = lib_interface_rip_authentication_key_chain_destroy, + .modify = lib_interface_rip_authentication_key_chain_modify, + }, + }, + { + .xpath = "/frr-ripd:ripd/instance/state/neighbors/neighbor", + .cbs = { + .get_keys = ripd_instance_state_neighbors_neighbor_get_keys, + .get_next = ripd_instance_state_neighbors_neighbor_get_next, + .lookup_entry = ripd_instance_state_neighbors_neighbor_lookup_entry, + }, + }, + { + .xpath = "/frr-ripd:ripd/instance/state/neighbors/neighbor/address", + .cbs = { + .get_elem = ripd_instance_state_neighbors_neighbor_address_get_elem, + }, + }, + { + .xpath = "/frr-ripd:ripd/instance/state/neighbors/neighbor/last-update", + .cbs = { + .get_elem = ripd_instance_state_neighbors_neighbor_last_update_get_elem, + }, + }, + { + .xpath = "/frr-ripd:ripd/instance/state/neighbors/neighbor/bad-packets-rcvd", + .cbs = { + .get_elem = ripd_instance_state_neighbors_neighbor_bad_packets_rcvd_get_elem, + }, + }, + { + .xpath = "/frr-ripd:ripd/instance/state/neighbors/neighbor/bad-routes-rcvd", + .cbs = { + .get_elem = ripd_instance_state_neighbors_neighbor_bad_routes_rcvd_get_elem, + }, + }, + { + .xpath = "/frr-ripd:ripd/instance/state/routes/route", + .cbs = { + .get_keys = ripd_instance_state_routes_route_get_keys, + .get_next = ripd_instance_state_routes_route_get_next, + .lookup_entry = ripd_instance_state_routes_route_lookup_entry, + }, + }, + { + .xpath = "/frr-ripd:ripd/instance/state/routes/route/prefix", + .cbs = { + .get_elem = ripd_instance_state_routes_route_prefix_get_elem, + }, + }, + { + .xpath = "/frr-ripd:ripd/instance/state/routes/route/next-hop", + .cbs = { + .get_elem = ripd_instance_state_routes_route_next_hop_get_elem, + }, + }, + { + .xpath = "/frr-ripd:ripd/instance/state/routes/route/interface", + .cbs = { + .get_elem = ripd_instance_state_routes_route_interface_get_elem, + }, + }, + { + .xpath = "/frr-ripd:ripd/instance/state/routes/route/metric", + .cbs = { + .get_elem = ripd_instance_state_routes_route_metric_get_elem, + }, + }, + { + .xpath = "/frr-ripd:clear-rip-route", + .cbs = { + .rpc = clear_rip_route_rpc, + }, + }, + { + .xpath = NULL, + }, + } +}; diff --git a/ripd/rip_nb.h b/ripd/rip_nb.h new file mode 100644 index 0000000000..26bb3cb3bd --- /dev/null +++ b/ripd/rip_nb.h @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2018 NetDEF, Inc. + * Renato Westphal + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _FRR_RIP_NB_H_ +#define _FRR_RIP_NB_H_ + +extern const struct frr_yang_module_info frr_ripd_info; + +/* Mandatory callbacks. */ +int ripd_instance_create(struct nb_cb_create_args *args); +int ripd_instance_destroy(struct nb_cb_destroy_args *args); +const void *ripd_instance_get_next(struct nb_cb_get_next_args *args); +int ripd_instance_get_keys(struct nb_cb_get_keys_args *args); +const void *ripd_instance_lookup_entry(struct nb_cb_lookup_entry_args *args); +int ripd_instance_allow_ecmp_modify(struct nb_cb_modify_args *args); +int ripd_instance_default_information_originate_modify( + struct nb_cb_modify_args *args); +int ripd_instance_default_metric_modify(struct nb_cb_modify_args *args); +int ripd_instance_distance_default_modify(struct nb_cb_modify_args *args); +int ripd_instance_distance_source_create(struct nb_cb_create_args *args); +int ripd_instance_distance_source_destroy(struct nb_cb_destroy_args *args); +int ripd_instance_distance_source_distance_modify( + struct nb_cb_modify_args *args); +int ripd_instance_distance_source_access_list_modify( + struct nb_cb_modify_args *args); +int ripd_instance_distance_source_access_list_destroy( + struct nb_cb_destroy_args *args); +int ripd_instance_explicit_neighbor_create(struct nb_cb_create_args *args); +int ripd_instance_explicit_neighbor_destroy(struct nb_cb_destroy_args *args); +int ripd_instance_network_create(struct nb_cb_create_args *args); +int ripd_instance_network_destroy(struct nb_cb_destroy_args *args); +int ripd_instance_interface_create(struct nb_cb_create_args *args); +int ripd_instance_interface_destroy(struct nb_cb_destroy_args *args); +int ripd_instance_offset_list_create(struct nb_cb_create_args *args); +int ripd_instance_offset_list_destroy(struct nb_cb_destroy_args *args); +int ripd_instance_offset_list_access_list_modify( + struct nb_cb_modify_args *args); +int ripd_instance_offset_list_metric_modify(struct nb_cb_modify_args *args); +int ripd_instance_passive_default_modify(struct nb_cb_modify_args *args); +int ripd_instance_passive_interface_create(struct nb_cb_create_args *args); +int ripd_instance_passive_interface_destroy(struct nb_cb_destroy_args *args); +int ripd_instance_non_passive_interface_create(struct nb_cb_create_args *args); +int ripd_instance_non_passive_interface_destroy( + struct nb_cb_destroy_args *args); +int ripd_instance_redistribute_create(struct nb_cb_create_args *args); +int ripd_instance_redistribute_destroy(struct nb_cb_destroy_args *args); +int ripd_instance_redistribute_route_map_modify(struct nb_cb_modify_args *args); +int ripd_instance_redistribute_route_map_destroy( + struct nb_cb_destroy_args *args); +int ripd_instance_redistribute_metric_modify(struct nb_cb_modify_args *args); +int ripd_instance_redistribute_metric_destroy(struct nb_cb_destroy_args *args); +int ripd_instance_static_route_create(struct nb_cb_create_args *args); +int ripd_instance_static_route_destroy(struct nb_cb_destroy_args *args); +int ripd_instance_timers_flush_interval_modify(struct nb_cb_modify_args *args); +int ripd_instance_timers_holddown_interval_modify( + struct nb_cb_modify_args *args); +int ripd_instance_timers_update_interval_modify(struct nb_cb_modify_args *args); +int ripd_instance_version_receive_modify(struct nb_cb_modify_args *args); +int ripd_instance_version_send_modify(struct nb_cb_modify_args *args); +const void *ripd_instance_state_neighbors_neighbor_get_next( + struct nb_cb_get_next_args *args); +int ripd_instance_state_neighbors_neighbor_get_keys( + struct nb_cb_get_keys_args *args); +const void *ripd_instance_state_neighbors_neighbor_lookup_entry( + struct nb_cb_lookup_entry_args *args); +struct yang_data *ripd_instance_state_neighbors_neighbor_address_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data *ripd_instance_state_neighbors_neighbor_last_update_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +ripd_instance_state_neighbors_neighbor_bad_packets_rcvd_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +ripd_instance_state_neighbors_neighbor_bad_routes_rcvd_get_elem( + struct nb_cb_get_elem_args *args); +const void * +ripd_instance_state_routes_route_get_next(struct nb_cb_get_next_args *args); +int ripd_instance_state_routes_route_get_keys(struct nb_cb_get_keys_args *args); +const void *ripd_instance_state_routes_route_lookup_entry( + struct nb_cb_lookup_entry_args *args); +struct yang_data *ripd_instance_state_routes_route_prefix_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data *ripd_instance_state_routes_route_next_hop_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data *ripd_instance_state_routes_route_interface_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data *ripd_instance_state_routes_route_metric_get_elem( + struct nb_cb_get_elem_args *args); +int clear_rip_route_rpc(struct nb_cb_rpc_args *args); +int lib_interface_rip_split_horizon_modify(struct nb_cb_modify_args *args); +int lib_interface_rip_v2_broadcast_modify(struct nb_cb_modify_args *args); +int lib_interface_rip_version_receive_modify(struct nb_cb_modify_args *args); +int lib_interface_rip_version_send_modify(struct nb_cb_modify_args *args); +int lib_interface_rip_authentication_scheme_mode_modify( + struct nb_cb_modify_args *args); +int lib_interface_rip_authentication_scheme_md5_auth_length_modify( + struct nb_cb_modify_args *args); +int lib_interface_rip_authentication_scheme_md5_auth_length_destroy( + struct nb_cb_destroy_args *args); +int lib_interface_rip_authentication_password_modify( + struct nb_cb_modify_args *args); +int lib_interface_rip_authentication_password_destroy( + struct nb_cb_destroy_args *args); +int lib_interface_rip_authentication_key_chain_modify( + struct nb_cb_modify_args *args); +int lib_interface_rip_authentication_key_chain_destroy( + struct nb_cb_destroy_args *args); + +/* Optional 'apply_finish' callbacks. */ +void ripd_instance_redistribute_apply_finish( + struct nb_cb_apply_finish_args *args); +void ripd_instance_timers_apply_finish(struct nb_cb_apply_finish_args *args); + +/* Optional 'cli_show' callbacks. */ +void cli_show_router_rip(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_rip_allow_ecmp(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_rip_default_information_originate(struct vty *vty, + struct lyd_node *dnode, + bool show_defaults); +void cli_show_rip_default_metric(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_rip_distance(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_rip_distance_source(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_rip_neighbor(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_rip_network_prefix(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_rip_network_interface(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_rip_offset_list(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_rip_passive_default(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_rip_passive_interface(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_rip_non_passive_interface(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_rip_redistribute(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_rip_route(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_rip_timers(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_rip_version(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_ip_rip_split_horizon(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_ip_rip_v2_broadcast(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_ip_rip_receive_version(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_ip_rip_send_version(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_ip_rip_authentication_scheme(struct vty *vty, + struct lyd_node *dnode, + bool show_defaults); +void cli_show_ip_rip_authentication_string(struct vty *vty, + struct lyd_node *dnode, + bool show_defaults); +void cli_show_ip_rip_authentication_key_chain(struct vty *vty, + struct lyd_node *dnode, + bool show_defaults); + +/* Notifications. */ +extern void ripd_notif_send_auth_type_failure(const char *ifname); +extern void ripd_notif_send_auth_failure(const char *ifname); + +#endif /* _FRR_RIP_NB_H_ */ diff --git a/ripd/rip_nb_config.c b/ripd/rip_nb_config.c new file mode 100644 index 0000000000..c640ca27af --- /dev/null +++ b/ripd/rip_nb_config.c @@ -0,0 +1,1031 @@ +/* + * Copyright (C) 1997, 1998, 1999 Kunihiro Ishiguro + * Copyright (C) 2018 NetDEF, Inc. + * Renato Westphal + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "if.h" +#include "vrf.h" +#include "log.h" +#include "prefix.h" +#include "table.h" +#include "command.h" +#include "routemap.h" +#include "northbound.h" +#include "libfrr.h" + +#include "ripd/ripd.h" +#include "ripd/rip_nb.h" +#include "ripd/rip_debug.h" +#include "ripd/rip_interface.h" + +/* + * XPath: /frr-ripd:ripd/instance + */ +int ripd_instance_create(struct nb_cb_create_args *args) +{ + struct rip *rip; + struct vrf *vrf; + const char *vrf_name; + int socket; + + vrf_name = yang_dnode_get_string(args->dnode, "./vrf"); + vrf = vrf_lookup_by_name(vrf_name); + + /* + * Try to create a RIP socket only if the VRF is enabled, otherwise + * create a disabled RIP instance and wait for the VRF to be enabled. + */ + switch (args->event) { + case NB_EV_VALIDATE: + break; + case NB_EV_PREPARE: + if (!vrf || !vrf_is_enabled(vrf)) + break; + + socket = rip_create_socket(vrf); + if (socket < 0) + return NB_ERR_RESOURCE; + args->resource->fd = socket; + break; + case NB_EV_ABORT: + if (!vrf || !vrf_is_enabled(vrf)) + break; + + socket = args->resource->fd; + close(socket); + break; + case NB_EV_APPLY: + if (vrf && vrf_is_enabled(vrf)) + socket = args->resource->fd; + else + socket = -1; + + rip = rip_create(vrf_name, vrf, socket); + nb_running_set_entry(args->dnode, rip); + break; + } + + return NB_OK; +} + +int ripd_instance_destroy(struct nb_cb_destroy_args *args) +{ + struct rip *rip; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + rip = nb_running_unset_entry(args->dnode); + rip_clean(rip); + + return NB_OK; +} + +/* + * XPath: /frr-ripd:ripd/instance/allow-ecmp + */ +int ripd_instance_allow_ecmp_modify(struct nb_cb_modify_args *args) +{ + struct rip *rip; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + rip = nb_running_get_entry(args->dnode, NULL, true); + rip->ecmp = yang_dnode_get_bool(args->dnode, NULL); + if (!rip->ecmp) + rip_ecmp_disable(rip); + + return NB_OK; +} + +/* + * XPath: /frr-ripd:ripd/instance/default-information-originate + */ +int ripd_instance_default_information_originate_modify( + struct nb_cb_modify_args *args) +{ + struct rip *rip; + bool default_information; + struct prefix_ipv4 p; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + rip = nb_running_get_entry(args->dnode, NULL, true); + default_information = yang_dnode_get_bool(args->dnode, NULL); + + memset(&p, 0, sizeof(struct prefix_ipv4)); + p.family = AF_INET; + if (default_information) { + struct nexthop nh; + + memset(&nh, 0, sizeof(nh)); + nh.type = NEXTHOP_TYPE_IPV4; + rip_redistribute_add(rip, ZEBRA_ROUTE_RIP, RIP_ROUTE_DEFAULT, + &p, &nh, 0, 0, 0); + } else { + rip_redistribute_delete(rip, ZEBRA_ROUTE_RIP, RIP_ROUTE_DEFAULT, + &p, 0); + } + + return NB_OK; +} + +/* + * XPath: /frr-ripd:ripd/instance/default-metric + */ +int ripd_instance_default_metric_modify(struct nb_cb_modify_args *args) +{ + struct rip *rip; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + rip = nb_running_get_entry(args->dnode, NULL, true); + rip->default_metric = yang_dnode_get_uint8(args->dnode, NULL); + /* rip_update_default_metric (); */ + + return NB_OK; +} + +/* + * XPath: /frr-ripd:ripd/instance/distance/default + */ +int ripd_instance_distance_default_modify(struct nb_cb_modify_args *args) +{ + struct rip *rip; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + rip = nb_running_get_entry(args->dnode, NULL, true); + rip->distance = yang_dnode_get_uint8(args->dnode, NULL); + + return NB_OK; +} + +/* + * XPath: /frr-ripd:ripd/instance/distance/source + */ +int ripd_instance_distance_source_create(struct nb_cb_create_args *args) +{ + struct rip *rip; + struct prefix_ipv4 prefix; + struct route_node *rn; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + yang_dnode_get_ipv4p(&prefix, args->dnode, "./prefix"); + apply_mask_ipv4(&prefix); + + /* Get RIP distance node. */ + rip = nb_running_get_entry(args->dnode, NULL, true); + rn = route_node_get(rip->distance_table, (struct prefix *)&prefix); + rn->info = rip_distance_new(); + nb_running_set_entry(args->dnode, rn); + + return NB_OK; +} + +int ripd_instance_distance_source_destroy(struct nb_cb_destroy_args *args) +{ + struct route_node *rn; + struct rip_distance *rdistance; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + rn = nb_running_unset_entry(args->dnode); + rdistance = rn->info; + rip_distance_free(rdistance); + rn->info = NULL; + route_unlock_node(rn); + + return NB_OK; +} + +/* + * XPath: /frr-ripd:ripd/instance/distance/source/distance + */ +int ripd_instance_distance_source_distance_modify( + struct nb_cb_modify_args *args) +{ + struct route_node *rn; + uint8_t distance; + struct rip_distance *rdistance; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + /* Set distance value. */ + rn = nb_running_get_entry(args->dnode, NULL, true); + distance = yang_dnode_get_uint8(args->dnode, NULL); + rdistance = rn->info; + rdistance->distance = distance; + + return NB_OK; +} + +/* + * XPath: /frr-ripd:ripd/instance/distance/source/access-list + */ +int ripd_instance_distance_source_access_list_modify( + struct nb_cb_modify_args *args) +{ + const char *acl_name; + struct route_node *rn; + struct rip_distance *rdistance; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + acl_name = yang_dnode_get_string(args->dnode, NULL); + + /* Set access-list */ + rn = nb_running_get_entry(args->dnode, NULL, true); + rdistance = rn->info; + if (rdistance->access_list) + free(rdistance->access_list); + rdistance->access_list = strdup(acl_name); + + return NB_OK; +} + +int ripd_instance_distance_source_access_list_destroy( + struct nb_cb_destroy_args *args) +{ + struct route_node *rn; + struct rip_distance *rdistance; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + /* Reset access-list configuration. */ + rn = nb_running_get_entry(args->dnode, NULL, true); + rdistance = rn->info; + free(rdistance->access_list); + rdistance->access_list = NULL; + + return NB_OK; +} + +/* + * XPath: /frr-ripd:ripd/instance/explicit-neighbor + */ +int ripd_instance_explicit_neighbor_create(struct nb_cb_create_args *args) +{ + struct rip *rip; + struct prefix_ipv4 p; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + rip = nb_running_get_entry(args->dnode, NULL, true); + p.family = AF_INET; + p.prefixlen = IPV4_MAX_BITLEN; + yang_dnode_get_ipv4(&p.prefix, args->dnode, NULL); + + return rip_neighbor_add(rip, &p); +} + +int ripd_instance_explicit_neighbor_destroy(struct nb_cb_destroy_args *args) +{ + struct rip *rip; + struct prefix_ipv4 p; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + rip = nb_running_get_entry(args->dnode, NULL, true); + p.family = AF_INET; + p.prefixlen = IPV4_MAX_BITLEN; + yang_dnode_get_ipv4(&p.prefix, args->dnode, NULL); + + return rip_neighbor_delete(rip, &p); +} + +/* + * XPath: /frr-ripd:ripd/instance/network + */ +int ripd_instance_network_create(struct nb_cb_create_args *args) +{ + struct rip *rip; + struct prefix p; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + rip = nb_running_get_entry(args->dnode, NULL, true); + yang_dnode_get_ipv4p(&p, args->dnode, NULL); + apply_mask_ipv4((struct prefix_ipv4 *)&p); + + return rip_enable_network_add(rip, &p); +} + +int ripd_instance_network_destroy(struct nb_cb_destroy_args *args) +{ + struct rip *rip; + struct prefix p; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + rip = nb_running_get_entry(args->dnode, NULL, true); + yang_dnode_get_ipv4p(&p, args->dnode, NULL); + apply_mask_ipv4((struct prefix_ipv4 *)&p); + + return rip_enable_network_delete(rip, &p); +} + +/* + * XPath: /frr-ripd:ripd/instance/interface + */ +int ripd_instance_interface_create(struct nb_cb_create_args *args) +{ + struct rip *rip; + const char *ifname; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + rip = nb_running_get_entry(args->dnode, NULL, true); + ifname = yang_dnode_get_string(args->dnode, NULL); + + return rip_enable_if_add(rip, ifname); +} + +int ripd_instance_interface_destroy(struct nb_cb_destroy_args *args) +{ + struct rip *rip; + const char *ifname; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + rip = nb_running_get_entry(args->dnode, NULL, true); + ifname = yang_dnode_get_string(args->dnode, NULL); + + return rip_enable_if_delete(rip, ifname); +} + +/* + * XPath: /frr-ripd:ripd/instance/offset-list + */ +int ripd_instance_offset_list_create(struct nb_cb_create_args *args) +{ + struct rip *rip; + const char *ifname; + struct rip_offset_list *offset; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + rip = nb_running_get_entry(args->dnode, NULL, true); + ifname = yang_dnode_get_string(args->dnode, "./interface"); + + offset = rip_offset_list_new(rip, ifname); + nb_running_set_entry(args->dnode, offset); + + return NB_OK; +} + +int ripd_instance_offset_list_destroy(struct nb_cb_destroy_args *args) +{ + int direct; + struct rip_offset_list *offset; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + direct = yang_dnode_get_enum(args->dnode, "./direction"); + + offset = nb_running_unset_entry(args->dnode); + if (offset->direct[direct].alist_name) { + free(offset->direct[direct].alist_name); + offset->direct[direct].alist_name = NULL; + } + if (offset->direct[RIP_OFFSET_LIST_IN].alist_name == NULL + && offset->direct[RIP_OFFSET_LIST_OUT].alist_name == NULL) + offset_list_del(offset); + + return NB_OK; +} + +/* + * XPath: /frr-ripd:ripd/instance/offset-list/access-list + */ +int ripd_instance_offset_list_access_list_modify(struct nb_cb_modify_args *args) +{ + int direct; + struct rip_offset_list *offset; + const char *alist_name; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + direct = yang_dnode_get_enum(args->dnode, "../direction"); + alist_name = yang_dnode_get_string(args->dnode, NULL); + + offset = nb_running_get_entry(args->dnode, NULL, true); + if (offset->direct[direct].alist_name) + free(offset->direct[direct].alist_name); + offset->direct[direct].alist_name = strdup(alist_name); + + return NB_OK; +} + +/* + * XPath: /frr-ripd:ripd/instance/offset-list/metric + */ +int ripd_instance_offset_list_metric_modify(struct nb_cb_modify_args *args) +{ + int direct; + uint8_t metric; + struct rip_offset_list *offset; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + direct = yang_dnode_get_enum(args->dnode, "../direction"); + metric = yang_dnode_get_uint8(args->dnode, NULL); + + offset = nb_running_get_entry(args->dnode, NULL, true); + offset->direct[direct].metric = metric; + + return NB_OK; +} + +/* + * XPath: /frr-ripd:ripd/instance/passive-default + */ +int ripd_instance_passive_default_modify(struct nb_cb_modify_args *args) +{ + struct rip *rip; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + rip = nb_running_get_entry(args->dnode, NULL, true); + rip->passive_default = yang_dnode_get_bool(args->dnode, NULL); + rip_passive_nondefault_clean(rip); + + return NB_OK; +} + +/* + * XPath: /frr-ripd:ripd/instance/passive-interface + */ +int ripd_instance_passive_interface_create(struct nb_cb_create_args *args) +{ + struct rip *rip; + const char *ifname; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + rip = nb_running_get_entry(args->dnode, NULL, true); + ifname = yang_dnode_get_string(args->dnode, NULL); + + return rip_passive_nondefault_set(rip, ifname); +} + +int ripd_instance_passive_interface_destroy(struct nb_cb_destroy_args *args) +{ + struct rip *rip; + const char *ifname; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + rip = nb_running_get_entry(args->dnode, NULL, true); + ifname = yang_dnode_get_string(args->dnode, NULL); + + return rip_passive_nondefault_unset(rip, ifname); +} + +/* + * XPath: /frr-ripd:ripd/instance/non-passive-interface + */ +int ripd_instance_non_passive_interface_create(struct nb_cb_create_args *args) +{ + struct rip *rip; + const char *ifname; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + rip = nb_running_get_entry(args->dnode, NULL, true); + ifname = yang_dnode_get_string(args->dnode, NULL); + + return rip_passive_nondefault_unset(rip, ifname); +} + +int ripd_instance_non_passive_interface_destroy(struct nb_cb_destroy_args *args) +{ + struct rip *rip; + const char *ifname; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + rip = nb_running_get_entry(args->dnode, NULL, true); + ifname = yang_dnode_get_string(args->dnode, NULL); + + return rip_passive_nondefault_set(rip, ifname); +} + +/* + * XPath: /frr-ripd:ripd/instance/redistribute + */ +int ripd_instance_redistribute_create(struct nb_cb_create_args *args) +{ + struct rip *rip; + int type; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + rip = nb_running_get_entry(args->dnode, NULL, true); + type = yang_dnode_get_enum(args->dnode, "./protocol"); + + rip->redist[type].enabled = true; + + return NB_OK; +} + +int ripd_instance_redistribute_destroy(struct nb_cb_destroy_args *args) +{ + struct rip *rip; + int type; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + rip = nb_running_get_entry(args->dnode, NULL, true); + type = yang_dnode_get_enum(args->dnode, "./protocol"); + + rip->redist[type].enabled = false; + if (rip->redist[type].route_map.name) { + free(rip->redist[type].route_map.name); + rip->redist[type].route_map.name = NULL; + rip->redist[type].route_map.map = NULL; + } + rip->redist[type].metric_config = false; + rip->redist[type].metric = 0; + + if (rip->enabled) + rip_redistribute_conf_delete(rip, type); + + return NB_OK; +} + +void ripd_instance_redistribute_apply_finish( + struct nb_cb_apply_finish_args *args) +{ + struct rip *rip; + int type; + + rip = nb_running_get_entry(args->dnode, NULL, true); + type = yang_dnode_get_enum(args->dnode, "./protocol"); + + if (rip->enabled) + rip_redistribute_conf_update(rip, type); +} + +/* + * XPath: /frr-ripd:ripd/instance/redistribute/route-map + */ +int ripd_instance_redistribute_route_map_modify(struct nb_cb_modify_args *args) +{ + struct rip *rip; + int type; + const char *rmap_name; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + rip = nb_running_get_entry(args->dnode, NULL, true); + type = yang_dnode_get_enum(args->dnode, "../protocol"); + rmap_name = yang_dnode_get_string(args->dnode, NULL); + + if (rip->redist[type].route_map.name) + free(rip->redist[type].route_map.name); + rip->redist[type].route_map.name = strdup(rmap_name); + rip->redist[type].route_map.map = route_map_lookup_by_name(rmap_name); + + return NB_OK; +} + +int ripd_instance_redistribute_route_map_destroy( + struct nb_cb_destroy_args *args) +{ + struct rip *rip; + int type; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + rip = nb_running_get_entry(args->dnode, NULL, true); + type = yang_dnode_get_enum(args->dnode, "../protocol"); + + free(rip->redist[type].route_map.name); + rip->redist[type].route_map.name = NULL; + rip->redist[type].route_map.map = NULL; + + return NB_OK; +} + +/* + * XPath: /frr-ripd:ripd/instance/redistribute/metric + */ +int ripd_instance_redistribute_metric_modify(struct nb_cb_modify_args *args) +{ + struct rip *rip; + int type; + uint8_t metric; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + rip = nb_running_get_entry(args->dnode, NULL, true); + type = yang_dnode_get_enum(args->dnode, "../protocol"); + metric = yang_dnode_get_uint8(args->dnode, NULL); + + rip->redist[type].metric_config = true; + rip->redist[type].metric = metric; + + return NB_OK; +} + +int ripd_instance_redistribute_metric_destroy(struct nb_cb_destroy_args *args) +{ + struct rip *rip; + int type; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + rip = nb_running_get_entry(args->dnode, NULL, true); + type = yang_dnode_get_enum(args->dnode, "../protocol"); + + rip->redist[type].metric_config = false; + rip->redist[type].metric = 0; + + return NB_OK; +} + +/* + * XPath: /frr-ripd:ripd/instance/static-route + */ +int ripd_instance_static_route_create(struct nb_cb_create_args *args) +{ + struct rip *rip; + struct nexthop nh; + struct prefix_ipv4 p; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + rip = nb_running_get_entry(args->dnode, NULL, true); + yang_dnode_get_ipv4p(&p, args->dnode, NULL); + apply_mask_ipv4(&p); + + memset(&nh, 0, sizeof(nh)); + nh.type = NEXTHOP_TYPE_IPV4; + rip_redistribute_add(rip, ZEBRA_ROUTE_RIP, RIP_ROUTE_STATIC, &p, &nh, 0, + 0, 0); + + return NB_OK; +} + +int ripd_instance_static_route_destroy(struct nb_cb_destroy_args *args) +{ + struct rip *rip; + struct prefix_ipv4 p; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + rip = nb_running_get_entry(args->dnode, NULL, true); + yang_dnode_get_ipv4p(&p, args->dnode, NULL); + apply_mask_ipv4(&p); + + rip_redistribute_delete(rip, ZEBRA_ROUTE_RIP, RIP_ROUTE_STATIC, &p, 0); + + return NB_OK; +} + +/* + * XPath: /frr-ripd:ripd/instance/timers/ + */ +void ripd_instance_timers_apply_finish(struct nb_cb_apply_finish_args *args) +{ + struct rip *rip; + + rip = nb_running_get_entry(args->dnode, NULL, true); + + /* Reset update timer thread. */ + rip_event(rip, RIP_UPDATE_EVENT, 0); +} + +/* + * XPath: /frr-ripd:ripd/instance/timers/flush-interval + */ +int ripd_instance_timers_flush_interval_modify(struct nb_cb_modify_args *args) +{ + struct rip *rip; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + rip = nb_running_get_entry(args->dnode, NULL, true); + rip->garbage_time = yang_dnode_get_uint32(args->dnode, NULL); + + return NB_OK; +} + +/* + * XPath: /frr-ripd:ripd/instance/timers/holddown-interval + */ +int ripd_instance_timers_holddown_interval_modify( + struct nb_cb_modify_args *args) +{ + struct rip *rip; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + rip = nb_running_get_entry(args->dnode, NULL, true); + rip->timeout_time = yang_dnode_get_uint32(args->dnode, NULL); + + return NB_OK; +} + +/* + * XPath: /frr-ripd:ripd/instance/timers/update-interval + */ +int ripd_instance_timers_update_interval_modify(struct nb_cb_modify_args *args) +{ + struct rip *rip; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + rip = nb_running_get_entry(args->dnode, NULL, true); + rip->update_time = yang_dnode_get_uint32(args->dnode, NULL); + + return NB_OK; +} + +/* + * XPath: /frr-ripd:ripd/instance/version/receive + */ +int ripd_instance_version_receive_modify(struct nb_cb_modify_args *args) +{ + struct rip *rip; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + rip = nb_running_get_entry(args->dnode, NULL, true); + rip->version_recv = yang_dnode_get_enum(args->dnode, NULL); + + return NB_OK; +} + +/* + * XPath: /frr-ripd:ripd/instance/version/send + */ +int ripd_instance_version_send_modify(struct nb_cb_modify_args *args) +{ + struct rip *rip; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + rip = nb_running_get_entry(args->dnode, NULL, true); + rip->version_send = yang_dnode_get_enum(args->dnode, NULL); + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-ripd:rip/split-horizon + */ +int lib_interface_rip_split_horizon_modify(struct nb_cb_modify_args *args) +{ + struct interface *ifp; + struct rip_interface *ri; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + ri = ifp->info; + ri->split_horizon = yang_dnode_get_enum(args->dnode, NULL); + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-ripd:rip/v2-broadcast + */ +int lib_interface_rip_v2_broadcast_modify(struct nb_cb_modify_args *args) +{ + struct interface *ifp; + struct rip_interface *ri; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + ri = ifp->info; + ri->v2_broadcast = yang_dnode_get_bool(args->dnode, NULL); + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-ripd:rip/version-receive + */ +int lib_interface_rip_version_receive_modify(struct nb_cb_modify_args *args) +{ + struct interface *ifp; + struct rip_interface *ri; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + ri = ifp->info; + ri->ri_receive = yang_dnode_get_enum(args->dnode, NULL); + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-ripd:rip/version-send + */ +int lib_interface_rip_version_send_modify(struct nb_cb_modify_args *args) +{ + struct interface *ifp; + struct rip_interface *ri; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + ri = ifp->info; + ri->ri_send = yang_dnode_get_enum(args->dnode, NULL); + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-ripd:rip/authentication-scheme/mode + */ +int lib_interface_rip_authentication_scheme_mode_modify( + struct nb_cb_modify_args *args) +{ + struct interface *ifp; + struct rip_interface *ri; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + ri = ifp->info; + ri->auth_type = yang_dnode_get_enum(args->dnode, NULL); + + return NB_OK; +} + +/* + * XPath: + * /frr-interface:lib/interface/frr-ripd:rip/authentication-scheme/md5-auth-length + */ +int lib_interface_rip_authentication_scheme_md5_auth_length_modify( + struct nb_cb_modify_args *args) +{ + struct interface *ifp; + struct rip_interface *ri; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + ri = ifp->info; + ri->md5_auth_len = yang_dnode_get_enum(args->dnode, NULL); + + return NB_OK; +} + +int lib_interface_rip_authentication_scheme_md5_auth_length_destroy( + struct nb_cb_destroy_args *args) +{ + struct interface *ifp; + struct rip_interface *ri; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + ri = ifp->info; + ri->md5_auth_len = yang_get_default_enum( + "%s/authentication-scheme/md5-auth-length", RIP_IFACE); + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-ripd:rip/authentication-password + */ +int lib_interface_rip_authentication_password_modify( + struct nb_cb_modify_args *args) +{ + struct interface *ifp; + struct rip_interface *ri; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + ri = ifp->info; + XFREE(MTYPE_RIP_INTERFACE_STRING, ri->auth_str); + ri->auth_str = XSTRDUP(MTYPE_RIP_INTERFACE_STRING, + yang_dnode_get_string(args->dnode, NULL)); + + return NB_OK; +} + +int lib_interface_rip_authentication_password_destroy( + struct nb_cb_destroy_args *args) +{ + struct interface *ifp; + struct rip_interface *ri; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + ri = ifp->info; + XFREE(MTYPE_RIP_INTERFACE_STRING, ri->auth_str); + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-ripd:rip/authentication-key-chain + */ +int lib_interface_rip_authentication_key_chain_modify( + struct nb_cb_modify_args *args) +{ + struct interface *ifp; + struct rip_interface *ri; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + ri = ifp->info; + XFREE(MTYPE_RIP_INTERFACE_STRING, ri->key_chain); + ri->key_chain = XSTRDUP(MTYPE_RIP_INTERFACE_STRING, + yang_dnode_get_string(args->dnode, NULL)); + + return NB_OK; +} + +int lib_interface_rip_authentication_key_chain_destroy( + struct nb_cb_destroy_args *args) +{ + struct interface *ifp; + struct rip_interface *ri; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + ri = ifp->info; + XFREE(MTYPE_RIP_INTERFACE_STRING, ri->key_chain); + + return NB_OK; +} diff --git a/ripd/rip_nb_notifications.c b/ripd/rip_nb_notifications.c new file mode 100644 index 0000000000..28d3517dfd --- /dev/null +++ b/ripd/rip_nb_notifications.c @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2018 NetDEF, Inc. + * Renato Westphal + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "if.h" +#include "vrf.h" +#include "log.h" +#include "prefix.h" +#include "table.h" +#include "command.h" +#include "routemap.h" +#include "northbound.h" +#include "libfrr.h" + +#include "ripd/ripd.h" +#include "ripd/rip_nb.h" +#include "ripd/rip_debug.h" +#include "ripd/rip_interface.h" + +/* + * XPath: /frr-ripd:authentication-type-failure + */ +void ripd_notif_send_auth_type_failure(const char *ifname) +{ + const char *xpath = "/frr-ripd:authentication-type-failure"; + struct list *arguments; + char xpath_arg[XPATH_MAXLEN]; + struct yang_data *data; + + arguments = yang_data_list_new(); + + snprintf(xpath_arg, sizeof(xpath_arg), "%s/interface-name", xpath); + data = yang_data_new_string(xpath_arg, ifname); + listnode_add(arguments, data); + + nb_notification_send(xpath, arguments); +} + +/* + * XPath: /frr-ripd:authentication-failure + */ +void ripd_notif_send_auth_failure(const char *ifname) +{ + const char *xpath = "/frr-ripd:authentication-failure"; + struct list *arguments; + char xpath_arg[XPATH_MAXLEN]; + struct yang_data *data; + + arguments = yang_data_list_new(); + + snprintf(xpath_arg, sizeof(xpath_arg), "%s/interface-name", xpath); + data = yang_data_new_string(xpath_arg, ifname); + listnode_add(arguments, data); + + nb_notification_send(xpath, arguments); +} diff --git a/ripd/rip_nb_rpcs.c b/ripd/rip_nb_rpcs.c new file mode 100644 index 0000000000..52f2985cb3 --- /dev/null +++ b/ripd/rip_nb_rpcs.c @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2018 NetDEF, Inc. + * Renato Westphal + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "if.h" +#include "vrf.h" +#include "log.h" +#include "prefix.h" +#include "table.h" +#include "command.h" +#include "routemap.h" +#include "northbound.h" +#include "libfrr.h" + +#include "ripd/ripd.h" +#include "ripd/rip_nb.h" +#include "ripd/rip_debug.h" +#include "ripd/rip_interface.h" + +/* + * XPath: /frr-ripd:clear-rip-route + */ +static void clear_rip_route(struct rip *rip) +{ + struct route_node *rp; + + if (IS_RIP_DEBUG_EVENT) + zlog_debug("Clearing all RIP routes (VRF %s)", rip->vrf_name); + + /* Clear received RIP routes */ + for (rp = route_top(rip->table); rp; rp = route_next(rp)) { + struct list *list; + struct listnode *listnode; + struct rip_info *rinfo; + + list = rp->info; + if (!list) + continue; + + for (ALL_LIST_ELEMENTS_RO(list, listnode, rinfo)) { + if (!rip_route_rte(rinfo)) + continue; + + if (CHECK_FLAG(rinfo->flags, RIP_RTF_FIB)) + rip_zebra_ipv4_delete(rip, rp); + break; + } + + if (rinfo) { + RIP_TIMER_OFF(rinfo->t_timeout); + RIP_TIMER_OFF(rinfo->t_garbage_collect); + listnode_delete(list, rinfo); + rip_info_free(rinfo); + } + + if (list_isempty(list)) { + list_delete(&list); + rp->info = NULL; + route_unlock_node(rp); + } + } +} + +int clear_rip_route_rpc(struct nb_cb_rpc_args *args) +{ + struct rip *rip; + struct yang_data *yang_vrf; + + yang_vrf = yang_data_list_find(args->input, "%s/%s", args->xpath, + "input/vrf"); + if (yang_vrf) { + rip = rip_lookup_by_vrf_name(yang_vrf->value); + if (rip) + clear_rip_route(rip); + } else { + struct vrf *vrf; + + RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { + rip = vrf->info; + if (!rip) + continue; + + clear_rip_route(rip); + } + } + + return NB_OK; +} diff --git a/ripd/rip_nb_state.c b/ripd/rip_nb_state.c new file mode 100644 index 0000000000..184c760998 --- /dev/null +++ b/ripd/rip_nb_state.c @@ -0,0 +1,275 @@ +/* + * Copyright (C) 2018 NetDEF, Inc. + * Renato Westphal + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "if.h" +#include "vrf.h" +#include "log.h" +#include "prefix.h" +#include "table.h" +#include "command.h" +#include "routemap.h" +#include "northbound.h" +#include "libfrr.h" + +#include "ripd/ripd.h" +#include "ripd/rip_nb.h" +#include "ripd/rip_debug.h" +#include "ripd/rip_interface.h" + +/* + * XPath: /frr-ripd:ripd/instance + */ +const void *ripd_instance_get_next(struct nb_cb_get_next_args *args) +{ + struct rip *rip = (struct rip *)args->list_entry; + + if (args->list_entry == NULL) + rip = RB_MIN(rip_instance_head, &rip_instances); + else + rip = RB_NEXT(rip_instance_head, rip); + + return rip; +} + +int ripd_instance_get_keys(struct nb_cb_get_keys_args *args) +{ + const struct rip *rip = args->list_entry; + + args->keys->num = 1; + strlcpy(args->keys->key[0], rip->vrf_name, sizeof(args->keys->key[0])); + + return NB_OK; +} + +const void *ripd_instance_lookup_entry(struct nb_cb_lookup_entry_args *args) +{ + const char *vrf_name = args->keys->key[0]; + + return rip_lookup_by_vrf_name(vrf_name); +} + +/* + * XPath: /frr-ripd:ripd/instance/state/neighbors/neighbor + */ +const void *ripd_instance_state_neighbors_neighbor_get_next( + struct nb_cb_get_next_args *args) +{ + const struct rip *rip = args->parent_list_entry; + struct listnode *node; + + if (args->list_entry == NULL) + node = listhead(rip->peer_list); + else + node = listnextnode((struct listnode *)args->list_entry); + + return node; +} + +int ripd_instance_state_neighbors_neighbor_get_keys( + struct nb_cb_get_keys_args *args) +{ + const struct listnode *node = args->list_entry; + const struct rip_peer *peer = listgetdata(node); + + args->keys->num = 1; + (void)inet_ntop(AF_INET, &peer->addr, args->keys->key[0], + sizeof(args->keys->key[0])); + + return NB_OK; +} + +const void *ripd_instance_state_neighbors_neighbor_lookup_entry( + struct nb_cb_lookup_entry_args *args) +{ + const struct rip *rip = args->parent_list_entry; + struct in_addr address; + struct rip_peer *peer; + struct listnode *node; + + yang_str2ipv4(args->keys->key[0], &address); + + for (ALL_LIST_ELEMENTS_RO(rip->peer_list, node, peer)) { + if (IPV4_ADDR_SAME(&peer->addr, &address)) + return node; + } + + return NULL; +} + +/* + * XPath: /frr-ripd:ripd/instance/state/neighbors/neighbor/address + */ +struct yang_data *ripd_instance_state_neighbors_neighbor_address_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct listnode *node = args->list_entry; + const struct rip_peer *peer = listgetdata(node); + + return yang_data_new_ipv4(args->xpath, &peer->addr); +} + +/* + * XPath: /frr-ripd:ripd/instance/state/neighbors/neighbor/last-update + */ +struct yang_data *ripd_instance_state_neighbors_neighbor_last_update_get_elem( + struct nb_cb_get_elem_args *args) +{ + /* TODO: yang:date-and-time is tricky */ + return NULL; +} + +/* + * XPath: /frr-ripd:ripd/instance/state/neighbors/neighbor/bad-packets-rcvd + */ +struct yang_data * +ripd_instance_state_neighbors_neighbor_bad_packets_rcvd_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct listnode *node = args->list_entry; + const struct rip_peer *peer = listgetdata(node); + + return yang_data_new_uint32(args->xpath, peer->recv_badpackets); +} + +/* + * XPath: /frr-ripd:ripd/instance/state/neighbors/neighbor/bad-routes-rcvd + */ +struct yang_data * +ripd_instance_state_neighbors_neighbor_bad_routes_rcvd_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct listnode *node = args->list_entry; + const struct rip_peer *peer = listgetdata(node); + + return yang_data_new_uint32(args->xpath, peer->recv_badroutes); +} + +/* + * XPath: /frr-ripd:ripd/instance/state/routes/route + */ +const void * +ripd_instance_state_routes_route_get_next(struct nb_cb_get_next_args *args) +{ + const struct rip *rip = args->parent_list_entry; + struct route_node *rn; + + if (args->list_entry == NULL) + rn = route_top(rip->table); + else + rn = route_next((struct route_node *)args->list_entry); + /* Optimization: skip empty route nodes. */ + while (rn && rn->info == NULL) + rn = route_next(rn); + + return rn; +} + +int ripd_instance_state_routes_route_get_keys(struct nb_cb_get_keys_args *args) +{ + const struct route_node *rn = args->list_entry; + + args->keys->num = 1; + (void)prefix2str(&rn->p, args->keys->key[0], + sizeof(args->keys->key[0])); + + return NB_OK; +} + +const void *ripd_instance_state_routes_route_lookup_entry( + struct nb_cb_lookup_entry_args *args) +{ + const struct rip *rip = args->parent_list_entry; + struct prefix prefix; + struct route_node *rn; + + yang_str2ipv4p(args->keys->key[0], &prefix); + + rn = route_node_lookup(rip->table, &prefix); + if (!rn || !rn->info) + return NULL; + + route_unlock_node(rn); + + return rn; +} + +/* + * XPath: /frr-ripd:ripd/instance/state/routes/route/prefix + */ +struct yang_data *ripd_instance_state_routes_route_prefix_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct route_node *rn = args->list_entry; + const struct rip_info *rinfo = listnode_head(rn->info); + + return yang_data_new_ipv4p(args->xpath, &rinfo->rp->p); +} + +/* + * XPath: /frr-ripd:ripd/instance/state/routes/route/next-hop + */ +struct yang_data *ripd_instance_state_routes_route_next_hop_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct route_node *rn = args->list_entry; + const struct rip_info *rinfo = listnode_head(rn->info); + + switch (rinfo->nh.type) { + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + return yang_data_new_ipv4(args->xpath, &rinfo->nh.gate.ipv4); + default: + return NULL; + } +} + +/* + * XPath: /frr-ripd:ripd/instance/state/routes/route/interface + */ +struct yang_data *ripd_instance_state_routes_route_interface_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct route_node *rn = args->list_entry; + const struct rip_info *rinfo = listnode_head(rn->info); + const struct rip *rip = rip_info_get_instance(rinfo); + + switch (rinfo->nh.type) { + case NEXTHOP_TYPE_IFINDEX: + case NEXTHOP_TYPE_IPV4_IFINDEX: + return yang_data_new_string( + args->xpath, + ifindex2ifname(rinfo->nh.ifindex, rip->vrf->vrf_id)); + default: + return NULL; + } +} + +/* + * XPath: /frr-ripd:ripd/instance/state/routes/route/metric + */ +struct yang_data *ripd_instance_state_routes_route_metric_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct route_node *rn = args->list_entry; + const struct rip_info *rinfo = listnode_head(rn->info); + + return yang_data_new_uint8(args->xpath, rinfo->metric); +} diff --git a/ripd/rip_northbound.c b/ripd/rip_northbound.c deleted file mode 100644 index d4fde5519b..0000000000 --- a/ripd/rip_northbound.c +++ /dev/null @@ -1,1726 +0,0 @@ -/* - * Copyright (C) 1997, 1998, 1999 Kunihiro Ishiguro - * Copyright (C) 2018 NetDEF, Inc. - * Renato Westphal - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; see the file COPYING; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include - -#include "if.h" -#include "vrf.h" -#include "log.h" -#include "prefix.h" -#include "table.h" -#include "command.h" -#include "routemap.h" -#include "northbound.h" -#include "libfrr.h" - -#include "ripd/ripd.h" -#include "ripd/rip_debug.h" -#include "ripd/rip_cli.h" - -/* - * XPath: /frr-ripd:ripd/instance - */ -static int ripd_instance_create(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct rip *rip; - struct vrf *vrf; - const char *vrf_name; - int socket; - - vrf_name = yang_dnode_get_string(dnode, "./vrf"); - vrf = vrf_lookup_by_name(vrf_name); - - /* - * Try to create a RIP socket only if the VRF is enabled, otherwise - * create a disabled RIP instance and wait for the VRF to be enabled. - */ - switch (event) { - case NB_EV_VALIDATE: - break; - case NB_EV_PREPARE: - if (!vrf || !vrf_is_enabled(vrf)) - break; - - socket = rip_create_socket(vrf); - if (socket < 0) - return NB_ERR_RESOURCE; - resource->fd = socket; - break; - case NB_EV_ABORT: - if (!vrf || !vrf_is_enabled(vrf)) - break; - - socket = resource->fd; - close(socket); - break; - case NB_EV_APPLY: - if (vrf && vrf_is_enabled(vrf)) - socket = resource->fd; - else - socket = -1; - - rip = rip_create(vrf_name, vrf, socket); - nb_running_set_entry(dnode, rip); - break; - } - - return NB_OK; -} - -static int ripd_instance_destroy(enum nb_event event, - const struct lyd_node *dnode) -{ - struct rip *rip; - - if (event != NB_EV_APPLY) - return NB_OK; - - rip = nb_running_unset_entry(dnode); - rip_clean(rip); - - return NB_OK; -} - -static const void *ripd_instance_get_next(const void *parent_list_entry, - const void *list_entry) -{ - const struct rip *rip = list_entry; - - if (list_entry == NULL) - rip = RB_MIN(rip_instance_head, &rip_instances); - else - rip = RB_NEXT(rip_instance_head, (struct rip *)rip); - - return rip; -} - -static int ripd_instance_get_keys(const void *list_entry, - struct yang_list_keys *keys) -{ - const struct rip *rip = list_entry; - - keys->num = 1; - strlcpy(keys->key[0], rip->vrf_name, sizeof(keys->key[0])); - - return NB_OK; -} - -static const void *ripd_instance_lookup_entry(const void *parent_list_entry, - const struct yang_list_keys *keys) -{ - const char *vrf_name = keys->key[0]; - - return rip_lookup_by_vrf_name(vrf_name); -} - -/* - * XPath: /frr-ripd:ripd/instance/allow-ecmp - */ -static int ripd_instance_allow_ecmp_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct rip *rip; - - if (event != NB_EV_APPLY) - return NB_OK; - - rip = nb_running_get_entry(dnode, NULL, true); - rip->ecmp = yang_dnode_get_bool(dnode, NULL); - if (!rip->ecmp) - rip_ecmp_disable(rip); - - return NB_OK; -} - -/* - * XPath: /frr-ripd:ripd/instance/default-information-originate - */ -static int -ripd_instance_default_information_originate_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct rip *rip; - bool default_information; - struct prefix_ipv4 p; - - if (event != NB_EV_APPLY) - return NB_OK; - - rip = nb_running_get_entry(dnode, NULL, true); - default_information = yang_dnode_get_bool(dnode, NULL); - - memset(&p, 0, sizeof(struct prefix_ipv4)); - p.family = AF_INET; - if (default_information) { - struct nexthop nh; - - memset(&nh, 0, sizeof(nh)); - nh.type = NEXTHOP_TYPE_IPV4; - rip_redistribute_add(rip, ZEBRA_ROUTE_RIP, RIP_ROUTE_DEFAULT, - &p, &nh, 0, 0, 0); - } else { - rip_redistribute_delete(rip, ZEBRA_ROUTE_RIP, RIP_ROUTE_DEFAULT, - &p, 0); - } - - return NB_OK; -} - -/* - * XPath: /frr-ripd:ripd/instance/default-metric - */ -static int ripd_instance_default_metric_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct rip *rip; - - if (event != NB_EV_APPLY) - return NB_OK; - - rip = nb_running_get_entry(dnode, NULL, true); - rip->default_metric = yang_dnode_get_uint8(dnode, NULL); - /* rip_update_default_metric (); */ - - return NB_OK; -} - -/* - * XPath: /frr-ripd:ripd/instance/distance/default - */ -static int ripd_instance_distance_default_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct rip *rip; - - if (event != NB_EV_APPLY) - return NB_OK; - - rip = nb_running_get_entry(dnode, NULL, true); - rip->distance = yang_dnode_get_uint8(dnode, NULL); - - return NB_OK; -} - -/* - * XPath: /frr-ripd:ripd/instance/distance/source - */ -static int ripd_instance_distance_source_create(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct rip *rip; - struct prefix_ipv4 prefix; - struct route_node *rn; - - if (event != NB_EV_APPLY) - return NB_OK; - - yang_dnode_get_ipv4p(&prefix, dnode, "./prefix"); - apply_mask_ipv4(&prefix); - - /* Get RIP distance node. */ - rip = nb_running_get_entry(dnode, NULL, true); - rn = route_node_get(rip->distance_table, (struct prefix *)&prefix); - rn->info = rip_distance_new(); - nb_running_set_entry(dnode, rn); - - return NB_OK; -} - -static int ripd_instance_distance_source_destroy(enum nb_event event, - const struct lyd_node *dnode) -{ - struct route_node *rn; - struct rip_distance *rdistance; - - if (event != NB_EV_APPLY) - return NB_OK; - - rn = nb_running_unset_entry(dnode); - rdistance = rn->info; - rip_distance_free(rdistance); - rn->info = NULL; - route_unlock_node(rn); - - return NB_OK; -} - -/* - * XPath: /frr-ripd:ripd/instance/distance/source/distance - */ -static int -ripd_instance_distance_source_distance_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct route_node *rn; - uint8_t distance; - struct rip_distance *rdistance; - - if (event != NB_EV_APPLY) - return NB_OK; - - /* Set distance value. */ - rn = nb_running_get_entry(dnode, NULL, true); - distance = yang_dnode_get_uint8(dnode, NULL); - rdistance = rn->info; - rdistance->distance = distance; - - return NB_OK; -} - -/* - * XPath: /frr-ripd:ripd/instance/distance/source/access-list - */ -static int -ripd_instance_distance_source_access_list_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - const char *acl_name; - struct route_node *rn; - struct rip_distance *rdistance; - - if (event != NB_EV_APPLY) - return NB_OK; - - acl_name = yang_dnode_get_string(dnode, NULL); - - /* Set access-list */ - rn = nb_running_get_entry(dnode, NULL, true); - rdistance = rn->info; - if (rdistance->access_list) - free(rdistance->access_list); - rdistance->access_list = strdup(acl_name); - - return NB_OK; -} - -static int -ripd_instance_distance_source_access_list_destroy(enum nb_event event, - const struct lyd_node *dnode) -{ - struct route_node *rn; - struct rip_distance *rdistance; - - if (event != NB_EV_APPLY) - return NB_OK; - - /* Reset access-list configuration. */ - rn = nb_running_get_entry(dnode, NULL, true); - rdistance = rn->info; - free(rdistance->access_list); - rdistance->access_list = NULL; - - return NB_OK; -} - -/* - * XPath: /frr-ripd:ripd/instance/explicit-neighbor - */ -static int ripd_instance_explicit_neighbor_create(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct rip *rip; - struct prefix_ipv4 p; - - if (event != NB_EV_APPLY) - return NB_OK; - - rip = nb_running_get_entry(dnode, NULL, true); - p.family = AF_INET; - p.prefixlen = IPV4_MAX_BITLEN; - yang_dnode_get_ipv4(&p.prefix, dnode, NULL); - - return rip_neighbor_add(rip, &p); -} - -static int ripd_instance_explicit_neighbor_destroy(enum nb_event event, - const struct lyd_node *dnode) -{ - struct rip *rip; - struct prefix_ipv4 p; - - if (event != NB_EV_APPLY) - return NB_OK; - - rip = nb_running_get_entry(dnode, NULL, true); - p.family = AF_INET; - p.prefixlen = IPV4_MAX_BITLEN; - yang_dnode_get_ipv4(&p.prefix, dnode, NULL); - - return rip_neighbor_delete(rip, &p); -} - -/* - * XPath: /frr-ripd:ripd/instance/network - */ -static int ripd_instance_network_create(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct rip *rip; - struct prefix p; - - if (event != NB_EV_APPLY) - return NB_OK; - - rip = nb_running_get_entry(dnode, NULL, true); - yang_dnode_get_ipv4p(&p, dnode, NULL); - apply_mask_ipv4((struct prefix_ipv4 *)&p); - - return rip_enable_network_add(rip, &p); -} - -static int ripd_instance_network_destroy(enum nb_event event, - const struct lyd_node *dnode) -{ - struct rip *rip; - struct prefix p; - - if (event != NB_EV_APPLY) - return NB_OK; - - rip = nb_running_get_entry(dnode, NULL, true); - yang_dnode_get_ipv4p(&p, dnode, NULL); - apply_mask_ipv4((struct prefix_ipv4 *)&p); - - return rip_enable_network_delete(rip, &p); -} - -/* - * XPath: /frr-ripd:ripd/instance/interface - */ -static int ripd_instance_interface_create(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct rip *rip; - const char *ifname; - - if (event != NB_EV_APPLY) - return NB_OK; - - rip = nb_running_get_entry(dnode, NULL, true); - ifname = yang_dnode_get_string(dnode, NULL); - - return rip_enable_if_add(rip, ifname); -} - -static int ripd_instance_interface_destroy(enum nb_event event, - const struct lyd_node *dnode) -{ - struct rip *rip; - const char *ifname; - - if (event != NB_EV_APPLY) - return NB_OK; - - rip = nb_running_get_entry(dnode, NULL, true); - ifname = yang_dnode_get_string(dnode, NULL); - - return rip_enable_if_delete(rip, ifname); -} - -/* - * XPath: /frr-ripd:ripd/instance/offset-list - */ -static int ripd_instance_offset_list_create(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct rip *rip; - const char *ifname; - struct rip_offset_list *offset; - - if (event != NB_EV_APPLY) - return NB_OK; - - rip = nb_running_get_entry(dnode, NULL, true); - ifname = yang_dnode_get_string(dnode, "./interface"); - - offset = rip_offset_list_new(rip, ifname); - nb_running_set_entry(dnode, offset); - - return NB_OK; -} - -static int ripd_instance_offset_list_destroy(enum nb_event event, - const struct lyd_node *dnode) -{ - int direct; - struct rip_offset_list *offset; - - if (event != NB_EV_APPLY) - return NB_OK; - - direct = yang_dnode_get_enum(dnode, "./direction"); - - offset = nb_running_unset_entry(dnode); - if (offset->direct[direct].alist_name) { - free(offset->direct[direct].alist_name); - offset->direct[direct].alist_name = NULL; - } - if (offset->direct[RIP_OFFSET_LIST_IN].alist_name == NULL - && offset->direct[RIP_OFFSET_LIST_OUT].alist_name == NULL) - offset_list_del(offset); - - return NB_OK; -} - -/* - * XPath: /frr-ripd:ripd/instance/offset-list/access-list - */ -static int -ripd_instance_offset_list_access_list_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - int direct; - struct rip_offset_list *offset; - const char *alist_name; - - if (event != NB_EV_APPLY) - return NB_OK; - - direct = yang_dnode_get_enum(dnode, "../direction"); - alist_name = yang_dnode_get_string(dnode, NULL); - - offset = nb_running_get_entry(dnode, NULL, true); - if (offset->direct[direct].alist_name) - free(offset->direct[direct].alist_name); - offset->direct[direct].alist_name = strdup(alist_name); - - return NB_OK; -} - -/* - * XPath: /frr-ripd:ripd/instance/offset-list/metric - */ -static int ripd_instance_offset_list_metric_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - int direct; - uint8_t metric; - struct rip_offset_list *offset; - - if (event != NB_EV_APPLY) - return NB_OK; - - direct = yang_dnode_get_enum(dnode, "../direction"); - metric = yang_dnode_get_uint8(dnode, NULL); - - offset = nb_running_get_entry(dnode, NULL, true); - offset->direct[direct].metric = metric; - - return NB_OK; -} - -/* - * XPath: /frr-ripd:ripd/instance/passive-default - */ -static int ripd_instance_passive_default_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct rip *rip; - - if (event != NB_EV_APPLY) - return NB_OK; - - rip = nb_running_get_entry(dnode, NULL, true); - rip->passive_default = yang_dnode_get_bool(dnode, NULL); - rip_passive_nondefault_clean(rip); - - return NB_OK; -} - -/* - * XPath: /frr-ripd:ripd/instance/passive-interface - */ -static int ripd_instance_passive_interface_create(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct rip *rip; - const char *ifname; - - if (event != NB_EV_APPLY) - return NB_OK; - - rip = nb_running_get_entry(dnode, NULL, true); - ifname = yang_dnode_get_string(dnode, NULL); - - return rip_passive_nondefault_set(rip, ifname); -} - -static int ripd_instance_passive_interface_destroy(enum nb_event event, - const struct lyd_node *dnode) -{ - struct rip *rip; - const char *ifname; - - if (event != NB_EV_APPLY) - return NB_OK; - - rip = nb_running_get_entry(dnode, NULL, true); - ifname = yang_dnode_get_string(dnode, NULL); - - return rip_passive_nondefault_unset(rip, ifname); -} - -/* - * XPath: /frr-ripd:ripd/instance/non-passive-interface - */ -static int -ripd_instance_non_passive_interface_create(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct rip *rip; - const char *ifname; - - if (event != NB_EV_APPLY) - return NB_OK; - - rip = nb_running_get_entry(dnode, NULL, true); - ifname = yang_dnode_get_string(dnode, NULL); - - return rip_passive_nondefault_unset(rip, ifname); -} - -static int -ripd_instance_non_passive_interface_destroy(enum nb_event event, - const struct lyd_node *dnode) -{ - struct rip *rip; - const char *ifname; - - if (event != NB_EV_APPLY) - return NB_OK; - - rip = nb_running_get_entry(dnode, NULL, true); - ifname = yang_dnode_get_string(dnode, NULL); - - return rip_passive_nondefault_set(rip, ifname); -} - -/* - * XPath: /frr-ripd:ripd/instance/redistribute - */ -static int ripd_instance_redistribute_create(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct rip *rip; - int type; - - if (event != NB_EV_APPLY) - return NB_OK; - - rip = nb_running_get_entry(dnode, NULL, true); - type = yang_dnode_get_enum(dnode, "./protocol"); - - rip->redist[type].enabled = true; - - return NB_OK; -} - -static int ripd_instance_redistribute_destroy(enum nb_event event, - const struct lyd_node *dnode) -{ - struct rip *rip; - int type; - - if (event != NB_EV_APPLY) - return NB_OK; - - rip = nb_running_get_entry(dnode, NULL, true); - type = yang_dnode_get_enum(dnode, "./protocol"); - - rip->redist[type].enabled = false; - if (rip->redist[type].route_map.name) { - free(rip->redist[type].route_map.name); - rip->redist[type].route_map.name = NULL; - rip->redist[type].route_map.map = NULL; - } - rip->redist[type].metric_config = false; - rip->redist[type].metric = 0; - - if (rip->enabled) - rip_redistribute_conf_delete(rip, type); - - return NB_OK; -} - -static void -ripd_instance_redistribute_apply_finish(const struct lyd_node *dnode) -{ - struct rip *rip; - int type; - - rip = nb_running_get_entry(dnode, NULL, true); - type = yang_dnode_get_enum(dnode, "./protocol"); - - if (rip->enabled) - rip_redistribute_conf_update(rip, type); -} - -/* - * XPath: /frr-ripd:ripd/instance/redistribute/route-map - */ -static int -ripd_instance_redistribute_route_map_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct rip *rip; - int type; - const char *rmap_name; - - if (event != NB_EV_APPLY) - return NB_OK; - - rip = nb_running_get_entry(dnode, NULL, true); - type = yang_dnode_get_enum(dnode, "../protocol"); - rmap_name = yang_dnode_get_string(dnode, NULL); - - if (rip->redist[type].route_map.name) - free(rip->redist[type].route_map.name); - rip->redist[type].route_map.name = strdup(rmap_name); - rip->redist[type].route_map.map = route_map_lookup_by_name(rmap_name); - - return NB_OK; -} - -static int -ripd_instance_redistribute_route_map_destroy(enum nb_event event, - const struct lyd_node *dnode) -{ - struct rip *rip; - int type; - - if (event != NB_EV_APPLY) - return NB_OK; - - rip = nb_running_get_entry(dnode, NULL, true); - type = yang_dnode_get_enum(dnode, "../protocol"); - - free(rip->redist[type].route_map.name); - rip->redist[type].route_map.name = NULL; - rip->redist[type].route_map.map = NULL; - - return NB_OK; -} - -/* - * XPath: /frr-ripd:ripd/instance/redistribute/metric - */ -static int -ripd_instance_redistribute_metric_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct rip *rip; - int type; - uint8_t metric; - - if (event != NB_EV_APPLY) - return NB_OK; - - rip = nb_running_get_entry(dnode, NULL, true); - type = yang_dnode_get_enum(dnode, "../protocol"); - metric = yang_dnode_get_uint8(dnode, NULL); - - rip->redist[type].metric_config = true; - rip->redist[type].metric = metric; - - return NB_OK; -} - -static int -ripd_instance_redistribute_metric_destroy(enum nb_event event, - const struct lyd_node *dnode) -{ - struct rip *rip; - int type; - - if (event != NB_EV_APPLY) - return NB_OK; - - rip = nb_running_get_entry(dnode, NULL, true); - type = yang_dnode_get_enum(dnode, "../protocol"); - - rip->redist[type].metric_config = false; - rip->redist[type].metric = 0; - - return NB_OK; -} - -/* - * XPath: /frr-ripd:ripd/instance/static-route - */ -static int ripd_instance_static_route_create(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct rip *rip; - struct nexthop nh; - struct prefix_ipv4 p; - - if (event != NB_EV_APPLY) - return NB_OK; - - rip = nb_running_get_entry(dnode, NULL, true); - yang_dnode_get_ipv4p(&p, dnode, NULL); - apply_mask_ipv4(&p); - - memset(&nh, 0, sizeof(nh)); - nh.type = NEXTHOP_TYPE_IPV4; - rip_redistribute_add(rip, ZEBRA_ROUTE_RIP, RIP_ROUTE_STATIC, &p, &nh, 0, - 0, 0); - - return NB_OK; -} - -static int ripd_instance_static_route_destroy(enum nb_event event, - const struct lyd_node *dnode) -{ - struct rip *rip; - struct prefix_ipv4 p; - - if (event != NB_EV_APPLY) - return NB_OK; - - rip = nb_running_get_entry(dnode, NULL, true); - yang_dnode_get_ipv4p(&p, dnode, NULL); - apply_mask_ipv4(&p); - - rip_redistribute_delete(rip, ZEBRA_ROUTE_RIP, RIP_ROUTE_STATIC, &p, 0); - - return NB_OK; -} - -/* - * XPath: /frr-ripd:ripd/instance/timers/ - */ -static void ripd_instance_timers_apply_finish(const struct lyd_node *dnode) -{ - struct rip *rip; - - rip = nb_running_get_entry(dnode, NULL, true); - - /* Reset update timer thread. */ - rip_event(rip, RIP_UPDATE_EVENT, 0); -} - -/* - * XPath: /frr-ripd:ripd/instance/timers/flush-interval - */ -static int -ripd_instance_timers_flush_interval_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct rip *rip; - - if (event != NB_EV_APPLY) - return NB_OK; - - rip = nb_running_get_entry(dnode, NULL, true); - rip->garbage_time = yang_dnode_get_uint32(dnode, NULL); - - return NB_OK; -} - -/* - * XPath: /frr-ripd:ripd/instance/timers/holddown-interval - */ -static int -ripd_instance_timers_holddown_interval_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct rip *rip; - - if (event != NB_EV_APPLY) - return NB_OK; - - rip = nb_running_get_entry(dnode, NULL, true); - rip->timeout_time = yang_dnode_get_uint32(dnode, NULL); - - return NB_OK; -} - -/* - * XPath: /frr-ripd:ripd/instance/timers/update-interval - */ -static int -ripd_instance_timers_update_interval_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct rip *rip; - - if (event != NB_EV_APPLY) - return NB_OK; - - rip = nb_running_get_entry(dnode, NULL, true); - rip->update_time = yang_dnode_get_uint32(dnode, NULL); - - return NB_OK; -} - -/* - * XPath: /frr-ripd:ripd/instance/version/receive - */ -static int ripd_instance_version_receive_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct rip *rip; - - if (event != NB_EV_APPLY) - return NB_OK; - - rip = nb_running_get_entry(dnode, NULL, true); - rip->version_recv = yang_dnode_get_enum(dnode, NULL); - - return NB_OK; -} - -/* - * XPath: /frr-ripd:ripd/instance/version/send - */ -static int ripd_instance_version_send_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct rip *rip; - - if (event != NB_EV_APPLY) - return NB_OK; - - rip = nb_running_get_entry(dnode, NULL, true); - rip->version_send = yang_dnode_get_enum(dnode, NULL); - - return NB_OK; -} - -/* - * XPath: /frr-interface:lib/interface/frr-ripd:rip/split-horizon - */ -static int lib_interface_rip_split_horizon_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct interface *ifp; - struct rip_interface *ri; - - if (event != NB_EV_APPLY) - return NB_OK; - - ifp = nb_running_get_entry(dnode, NULL, true); - ri = ifp->info; - ri->split_horizon = yang_dnode_get_enum(dnode, NULL); - - return NB_OK; -} - -/* - * XPath: /frr-interface:lib/interface/frr-ripd:rip/v2-broadcast - */ -static int lib_interface_rip_v2_broadcast_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct interface *ifp; - struct rip_interface *ri; - - if (event != NB_EV_APPLY) - return NB_OK; - - ifp = nb_running_get_entry(dnode, NULL, true); - ri = ifp->info; - ri->v2_broadcast = yang_dnode_get_bool(dnode, NULL); - - return NB_OK; -} - -/* - * XPath: /frr-interface:lib/interface/frr-ripd:rip/version-receive - */ -static int -lib_interface_rip_version_receive_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct interface *ifp; - struct rip_interface *ri; - - if (event != NB_EV_APPLY) - return NB_OK; - - ifp = nb_running_get_entry(dnode, NULL, true); - ri = ifp->info; - ri->ri_receive = yang_dnode_get_enum(dnode, NULL); - - return NB_OK; -} - -/* - * XPath: /frr-interface:lib/interface/frr-ripd:rip/version-send - */ -static int lib_interface_rip_version_send_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct interface *ifp; - struct rip_interface *ri; - - if (event != NB_EV_APPLY) - return NB_OK; - - ifp = nb_running_get_entry(dnode, NULL, true); - ri = ifp->info; - ri->ri_send = yang_dnode_get_enum(dnode, NULL); - - return NB_OK; -} - -/* - * XPath: /frr-interface:lib/interface/frr-ripd:rip/authentication-scheme/mode - */ -static int lib_interface_rip_authentication_scheme_mode_modify( - enum nb_event event, const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct interface *ifp; - struct rip_interface *ri; - - if (event != NB_EV_APPLY) - return NB_OK; - - ifp = nb_running_get_entry(dnode, NULL, true); - ri = ifp->info; - ri->auth_type = yang_dnode_get_enum(dnode, NULL); - - return NB_OK; -} - -/* - * XPath: - * /frr-interface:lib/interface/frr-ripd:rip/authentication-scheme/md5-auth-length - */ -static int lib_interface_rip_authentication_scheme_md5_auth_length_modify( - enum nb_event event, const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct interface *ifp; - struct rip_interface *ri; - - if (event != NB_EV_APPLY) - return NB_OK; - - ifp = nb_running_get_entry(dnode, NULL, true); - ri = ifp->info; - ri->md5_auth_len = yang_dnode_get_enum(dnode, NULL); - - return NB_OK; -} - -static int lib_interface_rip_authentication_scheme_md5_auth_length_destroy( - enum nb_event event, const struct lyd_node *dnode) -{ - struct interface *ifp; - struct rip_interface *ri; - - if (event != NB_EV_APPLY) - return NB_OK; - - ifp = nb_running_get_entry(dnode, NULL, true); - ri = ifp->info; - ri->md5_auth_len = yang_get_default_enum( - "%s/authentication-scheme/md5-auth-length", RIP_IFACE); - - return NB_OK; -} - -/* - * XPath: /frr-interface:lib/interface/frr-ripd:rip/authentication-password - */ -static int -lib_interface_rip_authentication_password_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct interface *ifp; - struct rip_interface *ri; - - if (event != NB_EV_APPLY) - return NB_OK; - - ifp = nb_running_get_entry(dnode, NULL, true); - ri = ifp->info; - XFREE(MTYPE_RIP_INTERFACE_STRING, ri->auth_str); - ri->auth_str = XSTRDUP(MTYPE_RIP_INTERFACE_STRING, - yang_dnode_get_string(dnode, NULL)); - - return NB_OK; -} - -static int -lib_interface_rip_authentication_password_destroy(enum nb_event event, - const struct lyd_node *dnode) -{ - struct interface *ifp; - struct rip_interface *ri; - - if (event != NB_EV_APPLY) - return NB_OK; - - ifp = nb_running_get_entry(dnode, NULL, true); - ri = ifp->info; - XFREE(MTYPE_RIP_INTERFACE_STRING, ri->auth_str); - - return NB_OK; -} - -/* - * XPath: /frr-interface:lib/interface/frr-ripd:rip/authentication-key-chain - */ -static int -lib_interface_rip_authentication_key_chain_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct interface *ifp; - struct rip_interface *ri; - - if (event != NB_EV_APPLY) - return NB_OK; - - ifp = nb_running_get_entry(dnode, NULL, true); - ri = ifp->info; - XFREE(MTYPE_RIP_INTERFACE_STRING, ri->key_chain); - ri->key_chain = XSTRDUP(MTYPE_RIP_INTERFACE_STRING, - yang_dnode_get_string(dnode, NULL)); - - return NB_OK; -} - -static int -lib_interface_rip_authentication_key_chain_destroy(enum nb_event event, - const struct lyd_node *dnode) -{ - struct interface *ifp; - struct rip_interface *ri; - - if (event != NB_EV_APPLY) - return NB_OK; - - ifp = nb_running_get_entry(dnode, NULL, true); - ri = ifp->info; - XFREE(MTYPE_RIP_INTERFACE_STRING, ri->key_chain); - - return NB_OK; -} - -/* - * XPath: /frr-ripd:ripd/instance/state/neighbors/neighbor - */ -static const void * -ripd_instance_state_neighbors_neighbor_get_next(const void *parent_list_entry, - const void *list_entry) -{ - const struct rip *rip = parent_list_entry; - struct listnode *node; - - if (list_entry == NULL) - node = listhead(rip->peer_list); - else - node = listnextnode((struct listnode *)list_entry); - - return node; -} - -static int -ripd_instance_state_neighbors_neighbor_get_keys(const void *list_entry, - struct yang_list_keys *keys) -{ - const struct listnode *node = list_entry; - const struct rip_peer *peer = listgetdata(node); - - keys->num = 1; - (void)inet_ntop(AF_INET, &peer->addr, keys->key[0], - sizeof(keys->key[0])); - - return NB_OK; -} - -static const void *ripd_instance_state_neighbors_neighbor_lookup_entry( - const void *parent_list_entry, const struct yang_list_keys *keys) -{ - const struct rip *rip = parent_list_entry; - struct in_addr address; - struct rip_peer *peer; - struct listnode *node; - - yang_str2ipv4(keys->key[0], &address); - - for (ALL_LIST_ELEMENTS_RO(rip->peer_list, node, peer)) { - if (IPV4_ADDR_SAME(&peer->addr, &address)) - return node; - } - - return NULL; -} - -/* - * XPath: /frr-ripd:ripd/instance/state/neighbors/neighbor/address - */ -static struct yang_data * -ripd_instance_state_neighbors_neighbor_address_get_elem(const char *xpath, - const void *list_entry) -{ - const struct listnode *node = list_entry; - const struct rip_peer *peer = listgetdata(node); - - return yang_data_new_ipv4(xpath, &peer->addr); -} - -/* - * XPath: /frr-ripd:ripd/instance/state/neighbors/neighbor/last-update - */ -static struct yang_data * -ripd_instance_state_neighbors_neighbor_last_update_get_elem( - const char *xpath, const void *list_entry) -{ - /* TODO: yang:date-and-time is tricky */ - return NULL; -} - -/* - * XPath: /frr-ripd:ripd/instance/state/neighbors/neighbor/bad-packets-rcvd - */ -static struct yang_data * -ripd_instance_state_neighbors_neighbor_bad_packets_rcvd_get_elem( - const char *xpath, const void *list_entry) -{ - const struct listnode *node = list_entry; - const struct rip_peer *peer = listgetdata(node); - - return yang_data_new_uint32(xpath, peer->recv_badpackets); -} - -/* - * XPath: /frr-ripd:ripd/instance/state/neighbors/neighbor/bad-routes-rcvd - */ -static struct yang_data * -ripd_instance_state_neighbors_neighbor_bad_routes_rcvd_get_elem( - const char *xpath, const void *list_entry) -{ - const struct listnode *node = list_entry; - const struct rip_peer *peer = listgetdata(node); - - return yang_data_new_uint32(xpath, peer->recv_badroutes); -} - -/* - * XPath: /frr-ripd:ripd/instance/state/routes/route - */ -static const void * -ripd_instance_state_routes_route_get_next(const void *parent_list_entry, - const void *list_entry) -{ - const struct rip *rip = parent_list_entry; - struct route_node *rn; - - if (list_entry == NULL) - rn = route_top(rip->table); - else - rn = route_next((struct route_node *)list_entry); - while (rn && rn->info == NULL) - rn = route_next(rn); - - return rn; -} - -static int -ripd_instance_state_routes_route_get_keys(const void *list_entry, - struct yang_list_keys *keys) -{ - const struct route_node *rn = list_entry; - - keys->num = 1; - (void)prefix2str(&rn->p, keys->key[0], sizeof(keys->key[0])); - - return NB_OK; -} - -static const void * -ripd_instance_state_routes_route_lookup_entry(const void *parent_list_entry, - const struct yang_list_keys *keys) -{ - const struct rip *rip = parent_list_entry; - struct prefix prefix; - struct route_node *rn; - - yang_str2ipv4p(keys->key[0], &prefix); - - rn = route_node_lookup(rip->table, &prefix); - if (!rn || !rn->info) - return NULL; - - route_unlock_node(rn); - - return rn; -} - -/* - * XPath: /frr-ripd:ripd/instance/state/routes/route/prefix - */ -static struct yang_data * -ripd_instance_state_routes_route_prefix_get_elem(const char *xpath, - const void *list_entry) -{ - const struct route_node *rn = list_entry; - const struct rip_info *rinfo = listnode_head(rn->info); - - return yang_data_new_ipv4p(xpath, &rinfo->rp->p); -} - -/* - * XPath: /frr-ripd:ripd/instance/state/routes/route/next-hop - */ -static struct yang_data * -ripd_instance_state_routes_route_next_hop_get_elem(const char *xpath, - const void *list_entry) -{ - const struct route_node *rn = list_entry; - const struct rip_info *rinfo = listnode_head(rn->info); - - switch (rinfo->nh.type) { - case NEXTHOP_TYPE_IPV4: - case NEXTHOP_TYPE_IPV4_IFINDEX: - return yang_data_new_ipv4(xpath, &rinfo->nh.gate.ipv4); - default: - return NULL; - } -} - -/* - * XPath: /frr-ripd:ripd/instance/state/routes/route/interface - */ -static struct yang_data * -ripd_instance_state_routes_route_interface_get_elem(const char *xpath, - const void *list_entry) -{ - const struct route_node *rn = list_entry; - const struct rip_info *rinfo = listnode_head(rn->info); - const struct rip *rip = rip_info_get_instance(rinfo); - - switch (rinfo->nh.type) { - case NEXTHOP_TYPE_IFINDEX: - case NEXTHOP_TYPE_IPV4_IFINDEX: - return yang_data_new_string( - xpath, - ifindex2ifname(rinfo->nh.ifindex, rip->vrf->vrf_id)); - default: - return NULL; - } -} - -/* - * XPath: /frr-ripd:ripd/instance/state/routes/route/metric - */ -static struct yang_data * -ripd_instance_state_routes_route_metric_get_elem(const char *xpath, - const void *list_entry) -{ - const struct route_node *rn = list_entry; - const struct rip_info *rinfo = listnode_head(rn->info); - - return yang_data_new_uint8(xpath, rinfo->metric); -} - -/* - * XPath: /frr-ripd:clear-rip-route - */ -static void clear_rip_route(struct rip *rip) -{ - struct route_node *rp; - - if (IS_RIP_DEBUG_EVENT) - zlog_debug("Clearing all RIP routes (VRF %s)", rip->vrf_name); - - /* Clear received RIP routes */ - for (rp = route_top(rip->table); rp; rp = route_next(rp)) { - struct list *list; - struct listnode *listnode; - struct rip_info *rinfo; - - list = rp->info; - if (!list) - continue; - - for (ALL_LIST_ELEMENTS_RO(list, listnode, rinfo)) { - if (!rip_route_rte(rinfo)) - continue; - - if (CHECK_FLAG(rinfo->flags, RIP_RTF_FIB)) - rip_zebra_ipv4_delete(rip, rp); - break; - } - - if (rinfo) { - RIP_TIMER_OFF(rinfo->t_timeout); - RIP_TIMER_OFF(rinfo->t_garbage_collect); - listnode_delete(list, rinfo); - rip_info_free(rinfo); - } - - if (list_isempty(list)) { - list_delete(&list); - rp->info = NULL; - route_unlock_node(rp); - } - } -} - -static int clear_rip_route_rpc(const char *xpath, const struct list *input, - struct list *output) -{ - struct rip *rip; - struct yang_data *yang_vrf; - - yang_vrf = yang_data_list_find(input, "%s/%s", xpath, "input/vrf"); - if (yang_vrf) { - rip = rip_lookup_by_vrf_name(yang_vrf->value); - if (rip) - clear_rip_route(rip); - } else { - struct vrf *vrf; - - RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { - rip = vrf->info; - if (!rip) - continue; - - clear_rip_route(rip); - } - } - - return NB_OK; -} - -/* - * XPath: /frr-ripd:authentication-type-failure - */ -void ripd_notif_send_auth_type_failure(const char *ifname) -{ - const char *xpath = "/frr-ripd:authentication-type-failure"; - struct list *arguments; - char xpath_arg[XPATH_MAXLEN]; - struct yang_data *data; - - arguments = yang_data_list_new(); - - snprintf(xpath_arg, sizeof(xpath_arg), "%s/interface-name", xpath); - data = yang_data_new_string(xpath_arg, ifname); - listnode_add(arguments, data); - - nb_notification_send(xpath, arguments); -} - -/* - * XPath: /frr-ripd:authentication-failure - */ -void ripd_notif_send_auth_failure(const char *ifname) -{ - const char *xpath = "/frr-ripd:authentication-failure"; - struct list *arguments; - char xpath_arg[XPATH_MAXLEN]; - struct yang_data *data; - - arguments = yang_data_list_new(); - - snprintf(xpath_arg, sizeof(xpath_arg), "%s/interface-name", xpath); - data = yang_data_new_string(xpath_arg, ifname); - listnode_add(arguments, data); - - nb_notification_send(xpath, arguments); -} - -/* clang-format off */ -const struct frr_yang_module_info frr_ripd_info = { - .name = "frr-ripd", - .nodes = { - { - .xpath = "/frr-ripd:ripd/instance", - .cbs.create = ripd_instance_create, - .cbs.destroy = ripd_instance_destroy, - .cbs.get_next = ripd_instance_get_next, - .cbs.get_keys = ripd_instance_get_keys, - .cbs.lookup_entry = ripd_instance_lookup_entry, - .cbs.cli_show = cli_show_router_rip, - }, - { - .xpath = "/frr-ripd:ripd/instance/allow-ecmp", - .cbs.modify = ripd_instance_allow_ecmp_modify, - .cbs.cli_show = cli_show_rip_allow_ecmp, - }, - { - .xpath = "/frr-ripd:ripd/instance/default-information-originate", - .cbs.modify = ripd_instance_default_information_originate_modify, - .cbs.cli_show = cli_show_rip_default_information_originate, - }, - { - .xpath = "/frr-ripd:ripd/instance/default-metric", - .cbs.modify = ripd_instance_default_metric_modify, - .cbs.cli_show = cli_show_rip_default_metric, - }, - { - .xpath = "/frr-ripd:ripd/instance/distance/default", - .cbs.modify = ripd_instance_distance_default_modify, - .cbs.cli_show = cli_show_rip_distance, - }, - { - .xpath = "/frr-ripd:ripd/instance/distance/source", - .cbs.create = ripd_instance_distance_source_create, - .cbs.destroy = ripd_instance_distance_source_destroy, - .cbs.cli_show = cli_show_rip_distance_source, - }, - { - .xpath = "/frr-ripd:ripd/instance/distance/source/distance", - .cbs.modify = ripd_instance_distance_source_distance_modify, - }, - { - .xpath = "/frr-ripd:ripd/instance/distance/source/access-list", - .cbs.modify = ripd_instance_distance_source_access_list_modify, - .cbs.destroy = ripd_instance_distance_source_access_list_destroy, - }, - { - .xpath = "/frr-ripd:ripd/instance/explicit-neighbor", - .cbs.create = ripd_instance_explicit_neighbor_create, - .cbs.destroy = ripd_instance_explicit_neighbor_destroy, - .cbs.cli_show = cli_show_rip_neighbor, - }, - { - .xpath = "/frr-ripd:ripd/instance/network", - .cbs.create = ripd_instance_network_create, - .cbs.destroy = ripd_instance_network_destroy, - .cbs.cli_show = cli_show_rip_network_prefix, - }, - { - .xpath = "/frr-ripd:ripd/instance/interface", - .cbs.create = ripd_instance_interface_create, - .cbs.destroy = ripd_instance_interface_destroy, - .cbs.cli_show = cli_show_rip_network_interface, - }, - { - .xpath = "/frr-ripd:ripd/instance/offset-list", - .cbs.create = ripd_instance_offset_list_create, - .cbs.destroy = ripd_instance_offset_list_destroy, - .cbs.cli_show = cli_show_rip_offset_list, - }, - { - .xpath = "/frr-ripd:ripd/instance/offset-list/access-list", - .cbs.modify = ripd_instance_offset_list_access_list_modify, - }, - { - .xpath = "/frr-ripd:ripd/instance/offset-list/metric", - .cbs.modify = ripd_instance_offset_list_metric_modify, - }, - { - .xpath = "/frr-ripd:ripd/instance/passive-default", - .cbs.modify = ripd_instance_passive_default_modify, - .cbs.cli_show = cli_show_rip_passive_default, - }, - { - .xpath = "/frr-ripd:ripd/instance/passive-interface", - .cbs.create = ripd_instance_passive_interface_create, - .cbs.destroy = ripd_instance_passive_interface_destroy, - .cbs.cli_show = cli_show_rip_passive_interface, - }, - { - .xpath = "/frr-ripd:ripd/instance/non-passive-interface", - .cbs.create = ripd_instance_non_passive_interface_create, - .cbs.destroy = ripd_instance_non_passive_interface_destroy, - .cbs.cli_show = cli_show_rip_non_passive_interface, - }, - { - .xpath = "/frr-ripd:ripd/instance/redistribute", - .cbs.create = ripd_instance_redistribute_create, - .cbs.destroy = ripd_instance_redistribute_destroy, - .cbs.apply_finish = ripd_instance_redistribute_apply_finish, - .cbs.cli_show = cli_show_rip_redistribute, - }, - { - .xpath = "/frr-ripd:ripd/instance/redistribute/route-map", - .cbs.modify = ripd_instance_redistribute_route_map_modify, - .cbs.destroy = ripd_instance_redistribute_route_map_destroy, - }, - { - .xpath = "/frr-ripd:ripd/instance/redistribute/metric", - .cbs.modify = ripd_instance_redistribute_metric_modify, - .cbs.destroy = ripd_instance_redistribute_metric_destroy, - }, - { - .xpath = "/frr-ripd:ripd/instance/static-route", - .cbs.create = ripd_instance_static_route_create, - .cbs.destroy = ripd_instance_static_route_destroy, - .cbs.cli_show = cli_show_rip_route, - }, - { - .xpath = "/frr-ripd:ripd/instance/timers", - .cbs.apply_finish = ripd_instance_timers_apply_finish, - .cbs.cli_show = cli_show_rip_timers, - }, - { - .xpath = "/frr-ripd:ripd/instance/timers/flush-interval", - .cbs.modify = ripd_instance_timers_flush_interval_modify, - }, - { - .xpath = "/frr-ripd:ripd/instance/timers/holddown-interval", - .cbs.modify = ripd_instance_timers_holddown_interval_modify, - }, - { - .xpath = "/frr-ripd:ripd/instance/timers/update-interval", - .cbs.modify = ripd_instance_timers_update_interval_modify, - }, - { - .xpath = "/frr-ripd:ripd/instance/version", - .cbs.cli_show = cli_show_rip_version, - }, - { - .xpath = "/frr-ripd:ripd/instance/version/receive", - .cbs.modify = ripd_instance_version_receive_modify, - }, - { - .xpath = "/frr-ripd:ripd/instance/version/send", - .cbs.modify = ripd_instance_version_send_modify, - }, - { - .xpath = "/frr-interface:lib/interface/frr-ripd:rip/split-horizon", - .cbs.modify = lib_interface_rip_split_horizon_modify, - .cbs.cli_show = cli_show_ip_rip_split_horizon, - }, - { - .xpath = "/frr-interface:lib/interface/frr-ripd:rip/v2-broadcast", - .cbs.modify = lib_interface_rip_v2_broadcast_modify, - .cbs.cli_show = cli_show_ip_rip_v2_broadcast, - }, - { - .xpath = "/frr-interface:lib/interface/frr-ripd:rip/version-receive", - .cbs.modify = lib_interface_rip_version_receive_modify, - .cbs.cli_show = cli_show_ip_rip_receive_version, - }, - { - .xpath = "/frr-interface:lib/interface/frr-ripd:rip/version-send", - .cbs.modify = lib_interface_rip_version_send_modify, - .cbs.cli_show = cli_show_ip_rip_send_version, - }, - { - .xpath = "/frr-interface:lib/interface/frr-ripd:rip/authentication-scheme", - .cbs.cli_show = cli_show_ip_rip_authentication_scheme, - }, - { - .xpath = "/frr-interface:lib/interface/frr-ripd:rip/authentication-scheme/mode", - .cbs.modify = lib_interface_rip_authentication_scheme_mode_modify, - }, - { - .xpath = "/frr-interface:lib/interface/frr-ripd:rip/authentication-scheme/md5-auth-length", - .cbs.modify = lib_interface_rip_authentication_scheme_md5_auth_length_modify, - .cbs.destroy = lib_interface_rip_authentication_scheme_md5_auth_length_destroy, - }, - { - .xpath = "/frr-interface:lib/interface/frr-ripd:rip/authentication-password", - .cbs.modify = lib_interface_rip_authentication_password_modify, - .cbs.destroy = lib_interface_rip_authentication_password_destroy, - .cbs.cli_show = cli_show_ip_rip_authentication_string, - }, - { - .xpath = "/frr-interface:lib/interface/frr-ripd:rip/authentication-key-chain", - .cbs.modify = lib_interface_rip_authentication_key_chain_modify, - .cbs.destroy = lib_interface_rip_authentication_key_chain_destroy, - .cbs.cli_show = cli_show_ip_rip_authentication_key_chain, - }, - { - .xpath = "/frr-ripd:ripd/instance/state/neighbors/neighbor", - .cbs.get_next = ripd_instance_state_neighbors_neighbor_get_next, - .cbs.get_keys = ripd_instance_state_neighbors_neighbor_get_keys, - .cbs.lookup_entry = ripd_instance_state_neighbors_neighbor_lookup_entry, - }, - { - .xpath = "/frr-ripd:ripd/instance/state/neighbors/neighbor/address", - .cbs.get_elem = ripd_instance_state_neighbors_neighbor_address_get_elem, - }, - { - .xpath = "/frr-ripd:ripd/instance/state/neighbors/neighbor/last-update", - .cbs.get_elem = ripd_instance_state_neighbors_neighbor_last_update_get_elem, - }, - { - .xpath = "/frr-ripd:ripd/instance/state/neighbors/neighbor/bad-packets-rcvd", - .cbs.get_elem = ripd_instance_state_neighbors_neighbor_bad_packets_rcvd_get_elem, - }, - { - .xpath = "/frr-ripd:ripd/instance/state/neighbors/neighbor/bad-routes-rcvd", - .cbs.get_elem = ripd_instance_state_neighbors_neighbor_bad_routes_rcvd_get_elem, - }, - { - .xpath = "/frr-ripd:ripd/instance/state/routes/route", - .cbs.get_next = ripd_instance_state_routes_route_get_next, - .cbs.get_keys = ripd_instance_state_routes_route_get_keys, - .cbs.lookup_entry = ripd_instance_state_routes_route_lookup_entry, - }, - { - .xpath = "/frr-ripd:ripd/instance/state/routes/route/prefix", - .cbs.get_elem = ripd_instance_state_routes_route_prefix_get_elem, - }, - { - .xpath = "/frr-ripd:ripd/instance/state/routes/route/next-hop", - .cbs.get_elem = ripd_instance_state_routes_route_next_hop_get_elem, - }, - { - .xpath = "/frr-ripd:ripd/instance/state/routes/route/interface", - .cbs.get_elem = ripd_instance_state_routes_route_interface_get_elem, - }, - { - .xpath = "/frr-ripd:ripd/instance/state/routes/route/metric", - .cbs.get_elem = ripd_instance_state_routes_route_metric_get_elem, - }, - { - .xpath = "/frr-ripd:clear-rip-route", - .cbs.rpc = clear_rip_route_rpc, - }, - { - .xpath = NULL, - }, - } -}; diff --git a/ripd/rip_offset.c b/ripd/rip_offset.c index 8307a95d27..776f121d59 100644 --- a/ripd/rip_offset.c +++ b/ripd/rip_offset.c @@ -29,6 +29,8 @@ #include "ripd/ripd.h" +DEFINE_MTYPE_STATIC(RIPD, RIP_OFFSET_LIST, "RIP offset list") + #define OFFSET_LIST_IN_NAME(O) ((O)->direct[RIP_OFFSET_LIST_IN].alist_name) #define OFFSET_LIST_IN_METRIC(O) ((O)->direct[RIP_OFFSET_LIST_IN].metric) diff --git a/ripd/rip_peer.c b/ripd/rip_peer.c index 08aa61257d..77c73ab398 100644 --- a/ripd/rip_peer.c +++ b/ripd/rip_peer.c @@ -29,6 +29,8 @@ #include "ripd/ripd.h" +DEFINE_MTYPE_STATIC(RIPD, RIP_PEER, "RIP peer") + static struct rip_peer *rip_peer_new(void) { return XCALLOC(MTYPE_RIP_PEER, sizeof(struct rip_peer)); @@ -129,7 +131,6 @@ void rip_peer_bad_packet(struct rip *rip, struct sockaddr_in *from) static char *rip_peer_uptime(struct rip_peer *peer, char *buf, size_t len) { time_t uptime; - struct tm *tm; /* If there is no connection has been done before print `never'. */ if (peer->uptime == 0) { @@ -140,17 +141,9 @@ static char *rip_peer_uptime(struct rip_peer *peer, char *buf, size_t len) /* Get current time. */ uptime = time(NULL); uptime -= peer->uptime; - tm = gmtime(&uptime); - - if (uptime < ONE_DAY_SECOND) - snprintf(buf, len, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min, - tm->tm_sec); - else if (uptime < ONE_WEEK_SECOND) - snprintf(buf, len, "%dd%02dh%02dm", tm->tm_yday, tm->tm_hour, - tm->tm_min); - else - snprintf(buf, len, "%02dw%dd%02dh", tm->tm_yday / 7, - tm->tm_yday - ((tm->tm_yday / 7) * 7), tm->tm_hour); + + frrtime_to_interval(uptime, buf, len); + return buf; } diff --git a/ripd/rip_routemap.c b/ripd/rip_routemap.c index 3216b8f89f..77c2db8ceb 100644 --- a/ripd/rip_routemap.c +++ b/ripd/rip_routemap.c @@ -42,10 +42,9 @@ struct rip_metric_modifier { /* `match metric METRIC' */ /* Match function return 1 if match is success else return zero. */ -static route_map_result_t route_match_metric(void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_match_metric(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { uint32_t *metric; uint32_t check; @@ -89,16 +88,18 @@ static void route_match_metric_free(void *rule) } /* Route map commands for metric matching. */ -struct route_map_rule_cmd route_match_metric_cmd = { - "metric", route_match_metric, route_match_metric_compile, - route_match_metric_free}; +static const struct route_map_rule_cmd route_match_metric_cmd = { + "metric", + route_match_metric, + route_match_metric_compile, + route_match_metric_free +}; /* `match interface IFNAME' */ /* Match function return 1 if match is success else return zero. */ -static route_map_result_t route_match_interface(void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_match_interface(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { struct rip_info *rinfo; struct interface *ifp; @@ -136,17 +137,19 @@ static void route_match_interface_free(void *rule) } /* Route map commands for interface matching. */ -struct route_map_rule_cmd route_match_interface_cmd = { - "interface", route_match_interface, route_match_interface_compile, - route_match_interface_free}; +static const struct route_map_rule_cmd route_match_interface_cmd = { + "interface", + route_match_interface, + route_match_interface_compile, + route_match_interface_free +}; /* `match ip next-hop IP_ACCESS_LIST' */ /* Match function return 1 if match is success else return zero. */ -static route_map_result_t route_match_ip_next_hop(void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_match_ip_next_hop(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { struct access_list *alist; struct rip_info *rinfo; @@ -155,8 +158,9 @@ static route_map_result_t route_match_ip_next_hop(void *rule, if (type == RMAP_RIP) { rinfo = object; p.family = AF_INET; - p.prefix = (rinfo->nh.gate.ipv4.s_addr) ? rinfo->nh.gate.ipv4 - : rinfo->from; + p.prefix = (rinfo->nh.gate.ipv4.s_addr != INADDR_ANY) + ? rinfo->nh.gate.ipv4 + : rinfo->from; p.prefixlen = IPV4_MAX_BITLEN; alist = access_list_lookup(AFI_IP, (char *)rule); @@ -184,13 +188,16 @@ static void route_match_ip_next_hop_free(void *rule) } /* Route map commands for ip next-hop matching. */ -static struct route_map_rule_cmd route_match_ip_next_hop_cmd = { - "ip next-hop", route_match_ip_next_hop, route_match_ip_next_hop_compile, - route_match_ip_next_hop_free}; +static const struct route_map_rule_cmd route_match_ip_next_hop_cmd = { + "ip next-hop", + route_match_ip_next_hop, + route_match_ip_next_hop_compile, + route_match_ip_next_hop_free +}; /* `match ip next-hop prefix-list PREFIX_LIST' */ -static route_map_result_t +static enum route_map_cmd_result_t route_match_ip_next_hop_prefix_list(void *rule, const struct prefix *prefix, route_map_object_t type, void *object) { @@ -201,8 +208,9 @@ route_match_ip_next_hop_prefix_list(void *rule, const struct prefix *prefix, if (type == RMAP_RIP) { rinfo = object; p.family = AF_INET; - p.prefix = (rinfo->nh.gate.ipv4.s_addr) ? rinfo->nh.gate.ipv4 - : rinfo->from; + p.prefix = (rinfo->nh.gate.ipv4.s_addr != INADDR_ANY) + ? rinfo->nh.gate.ipv4 + : rinfo->from; p.prefixlen = IPV4_MAX_BITLEN; plist = prefix_list_lookup(AFI_IP, (char *)rule); @@ -226,19 +234,58 @@ static void route_match_ip_next_hop_prefix_list_free(void *rule) XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); } -static struct route_map_rule_cmd route_match_ip_next_hop_prefix_list_cmd = { - "ip next-hop prefix-list", route_match_ip_next_hop_prefix_list, +static const struct route_map_rule_cmd + route_match_ip_next_hop_prefix_list_cmd = { + "ip next-hop prefix-list", + route_match_ip_next_hop_prefix_list, route_match_ip_next_hop_prefix_list_compile, - route_match_ip_next_hop_prefix_list_free}; + route_match_ip_next_hop_prefix_list_free +}; + +/* `match ip next-hop type ' */ + +static enum route_map_cmd_result_t +route_match_ip_next_hop_type(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) +{ + struct rip_info *rinfo; + + if (type == RMAP_RIP && prefix->family == AF_INET) { + rinfo = (struct rip_info *)object; + if (!rinfo) + return RMAP_NOMATCH; + + if (rinfo->nh.type == NEXTHOP_TYPE_BLACKHOLE) + return RMAP_MATCH; + } + return RMAP_NOMATCH; +} + +static void *route_match_ip_next_hop_type_compile(const char *arg) +{ + return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg); +} + +static void route_match_ip_next_hop_type_free(void *rule) +{ + XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); +} + +static const struct route_map_rule_cmd + route_match_ip_next_hop_type_cmd = { + "ip next-hop type", + route_match_ip_next_hop_type, + route_match_ip_next_hop_type_compile, + route_match_ip_next_hop_type_free +}; /* `match ip address IP_ACCESS_LIST' */ /* Match function should return 1 if match is success else return zero. */ -static route_map_result_t route_match_ip_address(void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_match_ip_address(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { struct access_list *alist; @@ -268,13 +315,16 @@ static void route_match_ip_address_free(void *rule) } /* Route map commands for ip address matching. */ -static struct route_map_rule_cmd route_match_ip_address_cmd = { - "ip address", route_match_ip_address, route_match_ip_address_compile, - route_match_ip_address_free}; +static const struct route_map_rule_cmd route_match_ip_address_cmd = { + "ip address", + route_match_ip_address, + route_match_ip_address_compile, + route_match_ip_address_free +}; /* `match ip address prefix-list PREFIX_LIST' */ -static route_map_result_t +static enum route_map_cmd_result_t route_match_ip_address_prefix_list(void *rule, const struct prefix *prefix, route_map_object_t type, void *object) { @@ -302,15 +352,19 @@ static void route_match_ip_address_prefix_list_free(void *rule) XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); } -static struct route_map_rule_cmd route_match_ip_address_prefix_list_cmd = { - "ip address prefix-list", route_match_ip_address_prefix_list, +static const struct route_map_rule_cmd + route_match_ip_address_prefix_list_cmd = { + "ip address prefix-list", + route_match_ip_address_prefix_list, route_match_ip_address_prefix_list_compile, - route_match_ip_address_prefix_list_free}; + route_match_ip_address_prefix_list_free +}; /* `match tag TAG' */ /* Match function return 1 if match is success else return zero. */ -static route_map_result_t route_match_tag(void *rule, const struct prefix *p, - route_map_object_t type, void *object) +static enum route_map_cmd_result_t +route_match_tag(void *rule, const struct prefix *p, route_map_object_t type, + void *object) { route_tag_t *tag; struct rip_info *rinfo; @@ -331,18 +385,19 @@ static route_map_result_t route_match_tag(void *rule, const struct prefix *p, } /* Route map commands for tag matching. */ -static struct route_map_rule_cmd route_match_tag_cmd = { - "tag", route_match_tag, route_map_rule_tag_compile, +static const struct route_map_rule_cmd route_match_tag_cmd = { + "tag", + route_match_tag, + route_map_rule_tag_compile, route_map_rule_tag_free, }; /* `set metric METRIC' */ /* Set metric to attribute. */ -static route_map_result_t route_set_metric(void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_set_metric(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { if (type == RMAP_RIP) { struct rip_metric_modifier *mod; @@ -413,7 +468,7 @@ static void *route_set_metric_compile(const char *arg) if (metric > RIP_METRIC_INFINITY) { zlog_info( "%s: Metric specified: %ld is greater than RIP_METRIC_INFINITY, using INFINITY instead", - __PRETTY_FUNCTION__, metric); + __func__, metric); mod->metric = RIP_METRIC_INFINITY; } else mod->metric = metric; @@ -430,18 +485,20 @@ static void route_set_metric_free(void *rule) } /* Set metric rule structure. */ -static struct route_map_rule_cmd route_set_metric_cmd = { - "metric", route_set_metric, route_set_metric_compile, +static const struct route_map_rule_cmd route_set_metric_cmd = { + "metric", + route_set_metric, + route_set_metric_compile, route_set_metric_free, }; /* `set ip next-hop IP_ADDRESS' */ /* Set nexthop to object. ojbect must be pointer to struct attr. */ -static route_map_result_t route_set_ip_nexthop(void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t route_set_ip_nexthop(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object) { struct in_addr *address; struct rip_info *rinfo; @@ -484,15 +541,19 @@ static void route_set_ip_nexthop_free(void *rule) } /* Route map commands for ip nexthop set. */ -static struct route_map_rule_cmd route_set_ip_nexthop_cmd = { - "ip next-hop", route_set_ip_nexthop, route_set_ip_nexthop_compile, - route_set_ip_nexthop_free}; +static const struct route_map_rule_cmd route_set_ip_nexthop_cmd = { + "ip next-hop", + route_set_ip_nexthop, + route_set_ip_nexthop_compile, + route_set_ip_nexthop_free +}; /* `set tag TAG' */ /* Set tag to object. ojbect must be pointer to struct attr. */ -static route_map_result_t route_set_tag(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static enum route_map_cmd_result_t +route_set_tag(void *rule, const struct prefix *prefix, route_map_object_t type, + void *object) { route_tag_t *tag; struct rip_info *rinfo; @@ -510,9 +571,12 @@ static route_map_result_t route_set_tag(void *rule, const struct prefix *prefix, } /* Route map commands for tag set. */ -static struct route_map_rule_cmd route_set_tag_cmd = { - "tag", route_set_tag, route_map_rule_tag_compile, - route_map_rule_tag_free}; +static const struct route_map_rule_cmd route_set_tag_cmd = { + "tag", + route_set_tag, + route_map_rule_tag_compile, + route_map_rule_tag_free +}; #define MATCH_STR "Match values from routing table\n" #define SET_STR "Set values in destination routing protocol\n" @@ -537,6 +601,9 @@ void rip_route_map_init(void) route_map_match_ip_next_hop_prefix_list_hook(generic_match_add); route_map_no_match_ip_next_hop_prefix_list_hook(generic_match_delete); + route_map_match_ip_next_hop_type_hook(generic_match_add); + route_map_no_match_ip_next_hop_type_hook(generic_match_delete); + route_map_match_metric_hook(generic_match_add); route_map_no_match_metric_hook(generic_match_delete); @@ -556,6 +623,7 @@ void rip_route_map_init(void) route_map_install_match(&route_match_interface_cmd); route_map_install_match(&route_match_ip_next_hop_cmd); route_map_install_match(&route_match_ip_next_hop_prefix_list_cmd); + route_map_install_match(&route_match_ip_next_hop_type_cmd); route_map_install_match(&route_match_ip_address_cmd); route_map_install_match(&route_match_ip_address_prefix_list_cmd); route_map_install_match(&route_match_tag_cmd); diff --git a/ripd/rip_snmp.c b/ripd/rip_snmp.c index 5a6b71fbaa..be222c7a5f 100644 --- a/ripd/rip_snmp.c +++ b/ripd/rip_snmp.c @@ -170,13 +170,10 @@ static uint8_t *rip2Globals(struct variable *v, oid name[], size_t *length, switch (v->magic) { case RIP2GLOBALROUTECHANGES: return SNMP_INTEGER(rip->counters.route_changes); - break; case RIP2GLOBALQUERIES: return SNMP_INTEGER(rip->counters.queries); - break; default: return NULL; - break; } return NULL; } @@ -373,7 +370,6 @@ static uint8_t *rip2IfStatEntry(struct variable *v, oid name[], size_t *length, switch (v->magic) { case RIP2IFSTATADDRESS: return SNMP_IPADDRESS(addr); - break; case RIP2IFSTATRCVBADPACKETS: *var_len = sizeof(long); return (uint8_t *)&ri->recv_badpackets; @@ -551,13 +547,13 @@ static uint8_t *rip2PeerTable(struct variable *v, oid name[], size_t *length, return (uint8_t *)&domain; case RIP2PEERLASTUPDATE: -#if 0 +#if 0 /* We don't know the SNMP agent startup time. We have two choices here: * - assume ripd startup time equals SNMP agent startup time * - don't support this variable, at all * Currently, we do the latter... */ - *val_len = sizeof (time_t); + *val_len = sizeof(time_t); uptime = peer->uptime; /* now - snmp_agent_startup - peer->uptime */ return (uint8_t *) &uptime; #else diff --git a/ripd/rip_zebra.c b/ripd/rip_zebra.c index 4f0df12232..e07d218860 100644 --- a/ripd/rip_zebra.c +++ b/ripd/rip_zebra.c @@ -118,8 +118,7 @@ void rip_zebra_ipv4_delete(struct rip *rip, struct route_node *rp) } /* Zebra route add and delete treatment. */ -static int rip_zebra_read_route(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int rip_zebra_read_route(ZAPI_CALLBACK_ARGS) { struct rip *rip; struct zapi_route api; @@ -138,11 +137,11 @@ static int rip_zebra_read_route(int command, struct zclient *zclient, nh.ifindex = api.nexthops[0].ifindex; /* Then fetch IPv4 prefixes. */ - if (command == ZEBRA_REDISTRIBUTE_ROUTE_ADD) + if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD) rip_redistribute_add(rip, api.type, RIP_ROUTE_REDISTRIBUTE, (struct prefix_ipv4 *)&api.prefix, &nh, api.metric, api.distance, api.tag); - else if (command == ZEBRA_REDISTRIBUTE_ROUTE_DEL) + else if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_DEL) rip_redistribute_delete(rip, api.type, RIP_ROUTE_REDISTRIBUTE, (struct prefix_ipv4 *)&api.prefix, nh.ifindex); @@ -152,8 +151,8 @@ static int rip_zebra_read_route(int command, struct zclient *zclient, void rip_redistribute_conf_update(struct rip *rip, int type) { - zclient_redistribute(ZEBRA_REDISTRIBUTE_ADD, zclient, AFI_IP, type, - 0, rip->vrf->vrf_id); + zebra_redistribute_send(ZEBRA_REDISTRIBUTE_ADD, zclient, AFI_IP, + type, 0, rip->vrf->vrf_id); } void rip_redistribute_conf_delete(struct rip *rip, int type) @@ -239,12 +238,8 @@ void rip_zclient_init(struct thread_master *master) zclient = zclient_new(master, &zclient_options_default); zclient_init(zclient, ZEBRA_ROUTE_RIP, 0, &ripd_privs); zclient->zebra_connected = rip_zebra_connected; - zclient->interface_add = rip_interface_add; - zclient->interface_delete = rip_interface_delete; zclient->interface_address_add = rip_interface_address_add; zclient->interface_address_delete = rip_interface_address_delete; - zclient->interface_up = rip_interface_up; - zclient->interface_down = rip_interface_down; zclient->interface_vrf_update = rip_interface_vrf_update; zclient->redistribute_route_add = rip_zebra_read_route; zclient->redistribute_route_del = rip_zebra_read_route; diff --git a/ripd/ripd.c b/ripd/ripd.c index 3a1ffd17a6..389a54f224 100644 --- a/ripd/ripd.c +++ b/ripd/ripd.c @@ -37,13 +37,17 @@ #include "if_rmap.h" #include "plist.h" #include "distribute.h" +#ifdef CRYPTO_INTERNAL #include "md5.h" +#endif #include "keychain.h" #include "privs.h" #include "lib_errors.h" #include "northbound_cli.h" +#include "network.h" #include "ripd/ripd.h" +#include "ripd/rip_nb.h" #include "ripd/rip_debug.h" #include "ripd/rip_errors.h" #include "ripd/rip_interface.h" @@ -51,6 +55,12 @@ /* UDP receive buffer size */ #define RIP_UDP_RCV_BUF 41600 +DEFINE_MGROUP(RIPD, "ripd") +DEFINE_MTYPE_STATIC(RIPD, RIP, "RIP structure") +DEFINE_MTYPE_STATIC(RIPD, RIP_VRF_NAME, "RIP VRF name") +DEFINE_MTYPE_STATIC(RIPD, RIP_INFO, "RIP route info") +DEFINE_MTYPE_STATIC(RIPD, RIP_DISTANCE, "RIP distance") + /* Prototypes. */ static void rip_output_process(struct connected *, struct sockaddr_in *, int, uint8_t); @@ -95,7 +105,7 @@ static int sockopt_broadcast(int sock) int on = 1; ret = setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char *)&on, - sizeof on); + sizeof(on)); if (ret < 0) { zlog_warn("can't set sockopt SO_BROADCAST to socket %d", sock); return -1; @@ -732,8 +742,7 @@ static void rip_packet_dump(struct rip_packet *packet, int size, ntohs(md5->family), ntohs(md5->type)); zlog_debug( - " RIP-2 packet len %d Key ID %d" - " Auth Data len %d", + " RIP-2 packet len %d Key ID %d Auth Data len %d", ntohs(md5->packet_len), md5->keyid, md5->auth_len); zlog_debug(" Sequence Number %ld", @@ -747,8 +756,7 @@ static void rip_packet_dump(struct rip_packet *packet, int size, ntohs(rte->family), ntohs(rte->tag)); zlog_debug( - " MD5: %02X%02X%02X%02X%02X%02X%02X%02X" - "%02X%02X%02X%02X%02X%02X%02X%02X", + " MD5: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9], p[10], p[11], p[12], p[13], @@ -864,7 +872,11 @@ static int rip_auth_md5(struct rip_packet *packet, struct sockaddr_in *from, struct rip_md5_data *md5data; struct keychain *keychain; struct key *key; +#ifdef CRYPTO_OPENSSL + EVP_MD_CTX *ctx; +#elif CRYPTO_INTERNAL MD5_CTX ctx; +#endif uint8_t digest[RIP_AUTH_MD5_SIZE]; uint16_t packet_len; char auth_str[RIP_AUTH_MD5_SIZE] = {}; @@ -890,8 +902,7 @@ static int rip_auth_md5(struct rip_packet *packet, struct sockaddr_in *from, || (md5->auth_len == RIP_AUTH_MD5_COMPAT_SIZE))) { if (IS_RIP_DEBUG_EVENT) zlog_debug( - "RIPv2 MD5 authentication, strange authentication " - "length field %d", + "RIPv2 MD5 authentication, strange authentication length field %d", md5->auth_len); return 0; } @@ -902,8 +913,7 @@ static int rip_auth_md5(struct rip_packet *packet, struct sockaddr_in *from, if (packet_len > (length - RIP_HEADER_SIZE - RIP_AUTH_MD5_SIZE)) { if (IS_RIP_DEBUG_EVENT) zlog_debug( - "RIPv2 MD5 authentication, packet length field %d " - "greater than received length %d!", + "RIPv2 MD5 authentication, packet length field %d greater than received length %d!", md5->packet_len, length); return 0; } @@ -928,11 +938,21 @@ static int rip_auth_md5(struct rip_packet *packet, struct sockaddr_in *from, return 0; /* MD5 digest authentication. */ +#ifdef CRYPTO_OPENSSL + unsigned int md5_size = RIP_AUTH_MD5_SIZE; + ctx = EVP_MD_CTX_new(); + EVP_DigestInit(ctx, EVP_md5()); + EVP_DigestUpdate(ctx, packet, packet_len + RIP_HEADER_SIZE); + EVP_DigestUpdate(ctx, auth_str, RIP_AUTH_MD5_SIZE); + EVP_DigestFinal(ctx, digest, &md5_size); + EVP_MD_CTX_free(ctx); +#elif CRYPTO_INTERNAL memset(&ctx, 0, sizeof(ctx)); MD5Init(&ctx); MD5Update(&ctx, packet, packet_len + RIP_HEADER_SIZE); MD5Update(&ctx, auth_str, RIP_AUTH_MD5_SIZE); MD5Final(digest, &ctx); +#endif if (memcmp(md5data->digest, digest, RIP_AUTH_MD5_SIZE) == 0) return packet_len; @@ -1057,7 +1077,11 @@ static void rip_auth_md5_set(struct stream *s, struct rip_interface *ri, size_t doff, char *auth_str, int authlen) { unsigned long len; +#ifdef CRYPTO_OPENSSL + EVP_MD_CTX *ctx; +#elif CRYPTO_INTERNAL MD5_CTX ctx; +#endif unsigned char digest[RIP_AUTH_MD5_SIZE]; /* Make it sure this interface is configured as MD5 @@ -1086,11 +1110,21 @@ static void rip_auth_md5_set(struct stream *s, struct rip_interface *ri, stream_putw(s, RIP_AUTH_DATA); /* Generate a digest for the RIP packet. */ +#ifdef CRYPTO_OPENSSL + unsigned int md5_size = RIP_AUTH_MD5_SIZE; + ctx = EVP_MD_CTX_new(); + EVP_DigestInit(ctx, EVP_md5()); + EVP_DigestUpdate(ctx, STREAM_DATA(s), stream_get_endp(s)); + EVP_DigestUpdate(ctx, auth_str, RIP_AUTH_MD5_SIZE); + EVP_DigestFinal(ctx, digest, &md5_size); + EVP_MD_CTX_free(ctx); +#elif CRYPTO_INTERNAL memset(&ctx, 0, sizeof(ctx)); MD5Init(&ctx); MD5Update(&ctx, STREAM_DATA(s), stream_get_endp(s)); MD5Update(&ctx, auth_str, RIP_AUTH_MD5_SIZE); MD5Final(digest, &ctx); +#endif /* Copy the digest to the packet. */ stream_write(s, digest, RIP_AUTH_MD5_SIZE); @@ -1186,7 +1220,8 @@ static void rip_response_process(struct rip_packet *packet, int size, } /* RIPv1 does not have nexthop value. */ - if (packet->version == RIPv1 && rte->nexthop.s_addr != 0) { + if (packet->version == RIPv1 + && rte->nexthop.s_addr != INADDR_ANY) { zlog_info("RIPv1 packet with nexthop value %s", inet_ntoa(rte->nexthop)); rip_peer_bad_route(rip, from); @@ -1197,7 +1232,8 @@ static void rip_response_process(struct rip_packet *packet, int size, sub-optimal, but absolutely valid, route may be taken. If the received Next Hop is not directly reachable, it should be treated as 0.0.0.0. */ - if (packet->version == RIPv2 && rte->nexthop.s_addr != 0) { + if (packet->version == RIPv2 + && rte->nexthop.s_addr != INADDR_ANY) { uint32_t addrval; /* Multicast address check. */ @@ -1235,7 +1271,8 @@ static void rip_response_process(struct rip_packet *packet, int size, "Next hop %s is not directly reachable. Treat it as 0.0.0.0", inet_ntoa( rte->nexthop)); - rte->nexthop.s_addr = 0; + rte->nexthop.s_addr = + INADDR_ANY; } route_unlock_node(rn); @@ -1245,7 +1282,7 @@ static void rip_response_process(struct rip_packet *packet, int size, "Next hop %s is not directly reachable. Treat it as 0.0.0.0", inet_ntoa( rte->nexthop)); - rte->nexthop.s_addr = 0; + rte->nexthop.s_addr = INADDR_ANY; } } } @@ -1260,10 +1297,11 @@ static void rip_response_process(struct rip_packet *packet, int size, (/16 for class B's) except when the RIP packet does to inside the classful network in question. */ - if ((packet->version == RIPv1 && rte->prefix.s_addr != 0) + if ((packet->version == RIPv1 + && rte->prefix.s_addr != INADDR_ANY) || (packet->version == RIPv2 - && (rte->prefix.s_addr != 0 - && rte->mask.s_addr == 0))) { + && (rte->prefix.s_addr != INADDR_ANY + && rte->mask.s_addr == INADDR_ANY))) { uint32_t destination; if (subnetted == -1) { @@ -1315,7 +1353,8 @@ static void rip_response_process(struct rip_packet *packet, int size, /* In case of RIPv2, if prefix in RTE is not netmask applied one ignore the entry. */ - if ((packet->version == RIPv2) && (rte->mask.s_addr != 0) + if ((packet->version == RIPv2) + && (rte->mask.s_addr != INADDR_ANY) && ((rte->prefix.s_addr & rte->mask.s_addr) != rte->prefix.s_addr)) { zlog_warn( @@ -1326,12 +1365,13 @@ static void rip_response_process(struct rip_packet *packet, int size, } /* Default route's netmask is ignored. */ - if (packet->version == RIPv2 && (rte->prefix.s_addr == 0) - && (rte->mask.s_addr != 0)) { + if (packet->version == RIPv2 + && (rte->prefix.s_addr == INADDR_ANY) + && (rte->mask.s_addr != INADDR_ANY)) { if (IS_RIP_DEBUG_EVENT) zlog_debug( "Default route with non-zero netmask. Set zero to netmask"); - rte->mask.s_addr = 0; + rte->mask.s_addr = INADDR_ANY; } /* Routing table updates. */ @@ -1359,7 +1399,7 @@ int rip_create_socket(struct vrf *vrf) /* Make datagram socket. */ if (vrf->vrf_id != VRF_DEFAULT) vrf_dev = vrf->name; - frr_elevate_privs(&ripd_privs) { + frr_with_privs(&ripd_privs) { sock = vrf_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP, vrf->vrf_id, vrf_dev); if (sock < 0) { @@ -1379,7 +1419,7 @@ int rip_create_socket(struct vrf *vrf) #endif setsockopt_so_recvbuf(sock, RIP_UDP_RCV_BUF); - frr_elevate_privs(&ripd_privs) { + frr_with_privs(&ripd_privs) { if ((ret = bind(sock, (struct sockaddr *)&addr, sizeof(addr))) < 0) { zlog_err("%s: Can't bind socket %d to %s port %d: %s", @@ -1601,8 +1641,7 @@ void rip_redistribute_delete(struct rip *rip, int type, int sub_type, if (IS_RIP_DEBUG_EVENT) zlog_debug( - "Poison %s/%d on the interface %s with an " - "infinity metric [delete]", + "Poison %s/%d on the interface %s with an infinity metric [delete]", inet_ntoa(p->prefix), p->prefixlen, ifindex2ifname( @@ -1770,8 +1809,7 @@ static int rip_read(struct thread *t) if (ifc == NULL) { zlog_info( - "rip_read: cannot find connected address for packet from %s " - "port %d on interface %s (VRF %s)", + "rip_read: cannot find connected address for packet from %s port %d on interface %s (VRF %s)", inet_ntoa(from.sin_addr), ntohs(from.sin_port), ifp->name, rip->vrf_name); return -1; @@ -1893,8 +1931,7 @@ static int rip_read(struct thread *t) if (packet->command != RIP_REQUEST) { if (IS_RIP_DEBUG_PACKET) zlog_debug( - "RIPv1" - " dropped because authentication enabled"); + "RIPv1 dropped because authentication enabled"); ripd_notif_send_auth_type_failure(ifp->name); rip_peer_bad_packet(rip, &from); return -1; @@ -1917,8 +1954,7 @@ static int rip_read(struct thread *t) if (packet->rte->family != htons(RIP_FAMILY_AUTH)) { if (IS_RIP_DEBUG_PACKET) zlog_debug( - "RIPv2" - " dropped because authentication enabled"); + "RIPv2 dropped because authentication enabled"); ripd_notif_send_auth_type_failure(ifp->name); rip_peer_bad_packet(rip, &from); return -1; @@ -2604,7 +2640,7 @@ static int rip_triggered_update(struct thread *t) random interval between 1 and 5 seconds. If other changes that would trigger updates occur before the timer expires, a single update is triggered when the timer expires. */ - interval = (random() % 5) + 1; + interval = (frr_weak_random() % 5) + 1; rip->t_triggered_interval = NULL; thread_add_timer(master, rip_triggered_interval, rip, interval, @@ -2801,7 +2837,8 @@ static int rip_update_jitter(unsigned long time) if (jitter_input < JITTER_BOUND) jitter_input = JITTER_BOUND; - jitter = (((random() % ((jitter_input * 2) + 1)) - jitter_input)); + jitter = (((frr_weak_random() % ((jitter_input * 2) + 1)) + - jitter_input)); return jitter / JITTER_BOUND; } @@ -2929,8 +2966,8 @@ static void rip_distance_show(struct vty *vty, struct rip *rip) " Address Distance List\n"); header = 0; } - sprintf(buf, "%s/%d", inet_ntoa(rn->p.u.prefix4), - rn->p.prefixlen); + snprintf(buf, sizeof(buf), "%s/%d", + inet_ntoa(rn->p.u.prefix4), rn->p.prefixlen); vty_out(vty, " %-20s %4d %s\n", buf, rdistance->distance, rdistance->access_list ? rdistance->access_list @@ -2977,20 +3014,20 @@ void rip_ecmp_disable(struct rip *rip) static void rip_vty_out_uptime(struct vty *vty, struct rip_info *rinfo) { time_t clock; - struct tm *tm; + struct tm tm; #define TIME_BUF 25 char timebuf[TIME_BUF]; struct thread *thread; if ((thread = rinfo->t_timeout) != NULL) { clock = thread_timer_remain_second(thread); - tm = gmtime(&clock); - strftime(timebuf, TIME_BUF, "%M:%S", tm); + gmtime_r(&clock, &tm); + strftime(timebuf, TIME_BUF, "%M:%S", &tm); vty_out(vty, "%5s", timebuf); } else if ((thread = rinfo->t_garbage_collect) != NULL) { clock = thread_timer_remain_second(thread); - tm = gmtime(&clock); - strftime(timebuf, TIME_BUF, "%M:%S", tm); + gmtime_r(&clock, &tm); + strftime(timebuf, TIME_BUF, "%M:%S", &tm); vty_out(vty, "%5s", timebuf); } } @@ -3284,8 +3321,15 @@ static int config_write_rip(struct vty *vty) return write; } +static int config_write_rip(struct vty *vty); /* RIP node structure. */ -static struct cmd_node rip_node = {RIP_NODE, "%s(config-router)# ", 1}; +static struct cmd_node rip_node = { + .name = "rip", + .node = RIP_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-router)# ", + .config_write = config_write_rip, +}; /* Distribute-list update functions. */ static void rip_distribute_update(struct distribute_ctx *ctx, @@ -3378,6 +3422,8 @@ static void rip_distribute_update_all_wrapper(struct access_list *notused) /* Delete all added rip route. */ void rip_clean(struct rip *rip) { + rip_interfaces_clean(rip); + if (rip->enabled) rip_instance_disable(rip); @@ -3399,7 +3445,6 @@ void rip_clean(struct rip *rip) route_table_finish(rip->enable_network); vector_free(rip->passive_nondefault); list_delete(&rip->offset_list_master); - rip_interfaces_clean(rip); route_table_finish(rip->distance_table); RB_REMOVE(rip_instance_head, &rip_instances, rip); @@ -3605,6 +3650,43 @@ static int rip_vrf_enable(struct vrf *vrf) int socket; rip = rip_lookup_by_vrf_name(vrf->name); + if (!rip) { + char *old_vrf_name = NULL; + + rip = (struct rip *)vrf->info; + if (!rip) + return 0; + /* update vrf name */ + if (rip->vrf_name) + old_vrf_name = rip->vrf_name; + rip->vrf_name = XSTRDUP(MTYPE_RIP_VRF_NAME, vrf->name); + /* + * HACK: Change the RIP VRF in the running configuration directly, + * bypassing the northbound layer. This is necessary to avoid deleting + * the RIP and readding it in the new VRF, which would have + * several implications. + */ + 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++; + } + } + XFREE(MTYPE_RIP_VRF_NAME, old_vrf_name); + } if (!rip || rip->enabled) return 0; @@ -3646,7 +3728,7 @@ static int rip_vrf_disable(struct vrf *vrf) void rip_vrf_init(void) { vrf_init(rip_vrf_new, rip_vrf_enable, rip_vrf_disable, rip_vrf_delete, - NULL); + rip_vrf_enable); } void rip_vrf_terminate(void) @@ -3658,7 +3740,7 @@ void rip_vrf_terminate(void) void rip_init(void) { /* Install top nodes. */ - install_node(&rip_node, config_write_rip); + install_node(&rip_node); /* Install rip commands. */ install_element(VIEW_NODE, &show_ip_rip_cmd); diff --git a/ripd/ripd.h b/ripd/ripd.h index 44f5932fb6..417bd5b3b1 100644 --- a/ripd/ripd.h +++ b/ripd/ripd.h @@ -24,7 +24,7 @@ #include "hook.h" #include "nexthop.h" #include "distribute.h" -#include "rip_memory.h" +#include "memory.h" /* RIP version number. */ #define RIPv1 1 @@ -97,6 +97,8 @@ #define RIP_INSTANCE "/frr-ripd:ripd/instance" #define RIP_IFACE "/frr-interface:lib/interface/frr-ripd:rip" +DECLARE_MGROUP(RIPD) + /* RIP structure. */ struct rip { RB_ENTRY(rip) entry; @@ -519,10 +521,7 @@ extern int offset_list_cmp(struct rip_offset_list *o1, extern void rip_vrf_init(void); extern void rip_vrf_terminate(void); - -/* YANG notifications */ -extern void ripd_notif_send_auth_type_failure(const char *ifname); -extern void ripd_notif_send_auth_failure(const char *ifname); +extern void rip_cli_init(void); extern struct zebra_privs_t ripd_privs; extern struct rip_instance_head rip_instances; @@ -533,8 +532,4 @@ extern struct thread_master *master; DECLARE_HOOK(rip_ifaddr_add, (struct connected * ifc), (ifc)) DECLARE_HOOK(rip_ifaddr_del, (struct connected * ifc), (ifc)) -/* Northbound. */ -extern void rip_cli_init(void); -extern const struct frr_yang_module_info frr_ripd_info; - #endif /* _ZEBRA_RIP_H */ diff --git a/ripd/subdir.am b/ripd/subdir.am index 2a63cc5229..9b86c65517 100644 --- a/ripd/subdir.am +++ b/ripd/subdir.am @@ -7,15 +7,15 @@ noinst_LIBRARIES += ripd/librip.a sbin_PROGRAMS += ripd/ripd dist_examples_DATA += ripd/ripd.conf.sample vtysh_scan += \ - $(top_srcdir)/ripd/rip_cli.c \ - $(top_srcdir)/ripd/rip_debug.c \ - $(top_srcdir)/ripd/ripd.c \ + ripd/rip_cli.c \ + ripd/rip_debug.c \ + ripd/ripd.c \ # end if SNMP module_LTLIBRARIES += ripd/ripd_snmp.la endif -man8 += $(MANBUILD)/ripd.8 +man8 += $(MANBUILD)/frr-ripd.8 endif ripd_librip_a_SOURCES = \ @@ -23,24 +23,27 @@ ripd_librip_a_SOURCES = \ ripd/rip_debug.c \ ripd/rip_errors.c \ ripd/rip_interface.c \ - ripd/rip_memory.c \ ripd/rip_offset.c \ - ripd/rip_northbound.c \ + ripd/rip_nb.c \ + ripd/rip_nb_config.c \ + ripd/rip_nb_rpcs.c \ + ripd/rip_nb_notifications.c \ + ripd/rip_nb_state.c \ ripd/rip_peer.c \ ripd/rip_routemap.c \ ripd/rip_zebra.c \ ripd/ripd.c \ # end -ripd/rip_cli_clippy.c: $(CLIPPY_DEPS) -ripd/rip_cli.$(OBJEXT): ripd/rip_cli_clippy.c +clippy_scan += \ + ripd/rip_cli.c \ + # end noinst_HEADERS += \ - ripd/rip_cli.h \ ripd/rip_debug.h \ ripd/rip_errors.h \ ripd/rip_interface.h \ - ripd/rip_memory.h \ + ripd/rip_nb.h \ ripd/ripd.h \ # end diff --git a/ripngd/ripng_cli.c b/ripngd/ripng_cli.c index e95c0e95d6..f66de175fa 100644 --- a/ripngd/ripng_cli.c +++ b/ripngd/ripng_cli.c @@ -29,7 +29,7 @@ #include "libfrr.h" #include "ripngd/ripngd.h" -#include "ripngd/ripng_cli.h" +#include "ripngd/ripng_nb.h" #ifndef VTYSH_EXTRACT_PL #include "ripngd/ripng_cli_clippy.c" #endif @@ -37,7 +37,7 @@ /* * XPath: /frr-ripngd:ripngd/instance */ -DEFPY_NOSH (router_ripng, +DEFPY_YANG_NOSH (router_ripng, router_ripng_cmd, "router ripng [vrf NAME]", "Enable a routing process\n" @@ -62,7 +62,7 @@ DEFPY_NOSH (router_ripng, return ret; } -DEFPY (no_router_ripng, +DEFPY_YANG (no_router_ripng, no_router_ripng_cmd, "no router ripng [vrf NAME]", NO_STR @@ -100,7 +100,7 @@ void cli_show_router_ripng(struct vty *vty, struct lyd_node *dnode, /* * XPath: /frr-ripngd:ripngd/instance/allow-ecmp */ -DEFPY (ripng_allow_ecmp, +DEFPY_YANG (ripng_allow_ecmp, ripng_allow_ecmp_cmd, "[no] allow-ecmp", NO_STR @@ -124,7 +124,7 @@ void cli_show_ripng_allow_ecmp(struct vty *vty, struct lyd_node *dnode, /* * XPath: /frr-ripngd:ripngd/instance/default-information-originate */ -DEFPY (ripng_default_information_originate, +DEFPY_YANG (ripng_default_information_originate, ripng_default_information_originate_cmd, "[no] default-information originate", NO_STR @@ -150,7 +150,7 @@ void cli_show_ripng_default_information_originate(struct vty *vty, /* * XPath: /frr-ripngd:ripngd/instance/default-metric */ -DEFPY (ripng_default_metric, +DEFPY_YANG (ripng_default_metric, ripng_default_metric_cmd, "default-metric (1-16)", "Set a metric of redistribute routes\n" @@ -162,7 +162,7 @@ DEFPY (ripng_default_metric, return nb_cli_apply_changes(vty, NULL); } -DEFPY (no_ripng_default_metric, +DEFPY_YANG (no_ripng_default_metric, no_ripng_default_metric_cmd, "no default-metric [(1-16)]", NO_STR @@ -184,7 +184,7 @@ void cli_show_ripng_default_metric(struct vty *vty, struct lyd_node *dnode, /* * XPath: /frr-ripngd:ripngd/instance/network */ -DEFPY (ripng_network_prefix, +DEFPY_YANG (ripng_network_prefix, ripng_network_prefix_cmd, "[no] network X:X::X:X/M", NO_STR @@ -206,7 +206,7 @@ void cli_show_ripng_network_prefix(struct vty *vty, struct lyd_node *dnode, /* * XPath: /frr-ripngd:ripngd/instance/interface */ -DEFPY (ripng_network_if, +DEFPY_YANG (ripng_network_if, ripng_network_if_cmd, "[no] network WORD", NO_STR @@ -228,7 +228,7 @@ void cli_show_ripng_network_interface(struct vty *vty, struct lyd_node *dnode, /* * XPath: /frr-ripngd:ripngd/instance/offset-list */ -DEFPY (ripng_offset_list, +DEFPY_YANG (ripng_offset_list, ripng_offset_list_cmd, "[no] offset-list WORD$acl $direction (0-16)$metric [IFNAME]", NO_STR @@ -271,7 +271,7 @@ void cli_show_ripng_offset_list(struct vty *vty, struct lyd_node *dnode, /* * XPath: /frr-ripngd:ripngd/instance/passive-interface */ -DEFPY (ripng_passive_interface, +DEFPY_YANG (ripng_passive_interface, ripng_passive_interface_cmd, "[no] passive-interface IFNAME", NO_STR @@ -294,7 +294,7 @@ void cli_show_ripng_passive_interface(struct vty *vty, struct lyd_node *dnode, /* * XPath: /frr-ripngd:ripngd/instance/redistribute */ -DEFPY (ripng_redistribute, +DEFPY_YANG (ripng_redistribute, ripng_redistribute_cmd, "[no] redistribute " FRR_REDIST_STR_RIPNGD "$protocol [{metric (0-16)|route-map WORD}]", NO_STR @@ -337,7 +337,7 @@ void cli_show_ripng_redistribute(struct vty *vty, struct lyd_node *dnode, /* * XPath: /frr-ripngd:ripngd/instance/static-route */ -DEFPY (ripng_route, +DEFPY_YANG (ripng_route, ripng_route_cmd, "[no] route X:X::X:X/M", NO_STR @@ -359,7 +359,7 @@ void cli_show_ripng_route(struct vty *vty, struct lyd_node *dnode, /* * XPath: /frr-ripngd:ripngd/instance/aggregate-addres */ -DEFPY (ripng_aggregate_address, +DEFPY_YANG (ripng_aggregate_address, ripng_aggregate_address_cmd, "[no] aggregate-address X:X::X:X/M", NO_STR @@ -383,7 +383,7 @@ void cli_show_ripng_aggregate_address(struct vty *vty, struct lyd_node *dnode, /* * XPath: /frr-ripngd:ripngd/instance/timers */ -DEFPY (ripng_timers, +DEFPY_YANG (ripng_timers, ripng_timers_cmd, "timers basic (1-65535)$update (1-65535)$timeout (1-65535)$garbage", "RIPng timers setup\n" @@ -402,7 +402,7 @@ DEFPY (ripng_timers, return nb_cli_apply_changes(vty, "./timers"); } -DEFPY (no_ripng_timers, +DEFPY_YANG (no_ripng_timers, no_ripng_timers_cmd, "no timers basic [(1-65535) (1-65535) (1-65535)]", NO_STR @@ -431,7 +431,7 @@ void cli_show_ripng_timers(struct vty *vty, struct lyd_node *dnode, /* * XPath: /frr-interface:lib/interface/frr-ripngd:ripng/split-horizon */ -DEFPY (ipv6_ripng_split_horizon, +DEFPY_YANG (ipv6_ripng_split_horizon, ipv6_ripng_split_horizon_cmd, "[no] ipv6 ripng split-horizon [poisoned-reverse$poisoned_reverse]", NO_STR @@ -476,7 +476,7 @@ void cli_show_ipv6_ripng_split_horizon(struct vty *vty, struct lyd_node *dnode, /* * XPath: /frr-ripngd:clear-ripng-route */ -DEFPY (clear_ipv6_rip, +DEFPY_YANG (clear_ipv6_rip, clear_ipv6_rip_cmd, "clear ipv6 ripng [vrf WORD]", CLEAR_STR @@ -485,6 +485,7 @@ DEFPY (clear_ipv6_rip, VRF_CMD_HELP_STR) { struct list *input; + int ret; input = list_new(); if (vrf) { @@ -495,7 +496,11 @@ DEFPY (clear_ipv6_rip, listnode_add(input, yang_vrf); } - return nb_cli_rpc("/frr-ripngd:clear-ripng-route", input, NULL); + ret = nb_cli_rpc("/frr-ripngd:clear-ripng-route", input, NULL); + + list_delete(&input); + + return ret; } void ripng_cli_init(void) diff --git a/ripngd/ripng_cli.h b/ripngd/ripng_cli.h deleted file mode 100644 index d95747e0f8..0000000000 --- a/ripngd/ripng_cli.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 1998 Kunihiro Ishiguro - * Copyright (C) 2018 NetDEF, Inc. - * Renato Westphal - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; see the file COPYING; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef _FRR_RIPNG_CLI_H_ -#define _FRR_RIPNG_CLI_H_ - -extern void cli_show_router_ripng(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -extern void cli_show_ripng_allow_ecmp(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -extern void cli_show_ripng_default_information_originate(struct vty *vty, - struct lyd_node *dnode, - bool show_defaults); -extern void cli_show_ripng_default_metric(struct vty *vty, - struct lyd_node *dnode, - bool show_defaults); -extern void cli_show_ripng_network_prefix(struct vty *vty, - struct lyd_node *dnode, - bool show_defaults); -extern void cli_show_ripng_network_interface(struct vty *vty, - struct lyd_node *dnode, - bool show_defaults); -extern void cli_show_ripng_offset_list(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -extern void cli_show_ripng_passive_interface(struct vty *vty, - struct lyd_node *dnode, - bool show_defaults); -extern void cli_show_ripng_redistribute(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -extern void cli_show_ripng_route(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -extern void cli_show_ripng_aggregate_address(struct vty *vty, - struct lyd_node *dnode, - bool show_defaults); -extern void cli_show_ripng_timers(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); -extern void cli_show_ipv6_ripng_split_horizon(struct vty *vty, - struct lyd_node *dnode, - bool show_defaults); - -#endif /* _FRR_RIPNG_CLI_H_ */ diff --git a/ripngd/ripng_debug.c b/ripngd/ripng_debug.c index fe63d8fdea..539c01b3ec 100644 --- a/ripngd/ripng_debug.c +++ b/ripngd/ripng_debug.c @@ -174,10 +174,13 @@ DEFUN (no_debug_ripng_zebra, return CMD_SUCCESS; } +static int config_write_debug(struct vty *vty); /* Debug node. */ static struct cmd_node debug_node = { - DEBUG_NODE, "", /* Debug node has no interface. */ - 1 /* VTYSH */ + .name = "debug", + .node = DEBUG_NODE, + .prompt = "", + .config_write = config_write_debug, }; static int config_write_debug(struct vty *vty) @@ -213,9 +216,9 @@ void ripng_debug_init(void) ripng_debug_packet = 0; ripng_debug_zebra = 0; - install_node(&debug_node, config_write_debug); + install_node(&debug_node); - install_element(VIEW_NODE, &show_debugging_ripng_cmd); + install_element(ENABLE_NODE, &show_debugging_ripng_cmd); install_element(ENABLE_NODE, &debug_ripng_events_cmd); install_element(ENABLE_NODE, &debug_ripng_packet_cmd); diff --git a/ripngd/ripng_interface.c b/ripngd/ripng_interface.c index e35652b1ac..03c93668b9 100644 --- a/ripngd/ripng_interface.c +++ b/ripngd/ripng_interface.c @@ -43,12 +43,14 @@ /* If RFC2133 definition is used. */ #ifndef IPV6_JOIN_GROUP -#define IPV6_JOIN_GROUP IPV6_ADD_MEMBERSHIP +#define IPV6_JOIN_GROUP IPV6_ADD_MEMBERSHIP #endif #ifndef IPV6_LEAVE_GROUP -#define IPV6_LEAVE_GROUP IPV6_DROP_MEMBERSHIP +#define IPV6_LEAVE_GROUP IPV6_DROP_MEMBERSHIP #endif +DEFINE_MTYPE_STATIC(RIPNGD, RIPNG_IF, "ripng interface") + /* Static utility function. */ static void ripng_enable_apply(struct interface *); static void ripng_passive_interface_apply(struct interface *); @@ -73,7 +75,7 @@ static int ripng_multicast_join(struct interface *ifp, int sock) * While this is bogus, privs are available and easy to use * for this call as a workaround. */ - frr_elevate_privs(&ripngd_privs) { + frr_with_privs(&ripngd_privs) { ret = setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, (char *)&mreq, sizeof(mreq)); @@ -184,7 +186,8 @@ static int ripng_if_down(struct interface *ifp) zlog_debug("turn off %s", ifp->name); /* Leave from multicast group. */ - ripng_multicast_leave(ifp, ripng->sock); + if (ripng) + ripng_multicast_leave(ifp, ripng->sock); ri->running = 0; } @@ -193,25 +196,16 @@ static int ripng_if_down(struct interface *ifp) } /* Inteface link up message processing. */ -int ripng_interface_up(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ripng_ifp_up(struct interface *ifp) { - struct stream *s; - struct interface *ifp; - - /* zebra_interface_state_read() updates interface structure in iflist. - */ - s = zclient->ibuf; - ifp = zebra_interface_state_read(s, vrf_id); - - if (ifp == NULL) - return 0; + if (IS_RIPNG_DEBUG_ZEBRA) { + struct vrf *vrf = vrf_lookup_by_id(ifp->vrf_id); - if (IS_RIPNG_DEBUG_ZEBRA) zlog_debug( - "interface up %s vrf %u index %d flags %llx metric %d mtu %d", - ifp->name, ifp->vrf_id, ifp->ifindex, + "interface up %s vrf %s(%u) index %d flags %llx metric %d mtu %d", + ifp->name, VRF_LOGNAME(vrf), ifp->vrf_id, ifp->ifindex, (unsigned long long)ifp->flags, ifp->metric, ifp->mtu6); + } ripng_interface_sync(ifp); @@ -228,46 +222,36 @@ int ripng_interface_up(int command, struct zclient *zclient, } /* Inteface link down message processing. */ -int ripng_interface_down(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ripng_ifp_down(struct interface *ifp) { - struct stream *s; - struct interface *ifp; - - /* zebra_interface_state_read() updates interface structure in iflist. - */ - s = zclient->ibuf; - ifp = zebra_interface_state_read(s, vrf_id); - - if (ifp == NULL) - return 0; - ripng_interface_sync(ifp); ripng_if_down(ifp); - if (IS_RIPNG_DEBUG_ZEBRA) + if (IS_RIPNG_DEBUG_ZEBRA) { + struct vrf *vrf = vrf_lookup_by_id(ifp->vrf_id); + zlog_debug( - "interface down %s vrf %u index %d flags %#llx metric %d mtu %d", - ifp->name, ifp->vrf_id, ifp->ifindex, + "interface down %s vrf %s(%u) index %d flags %#llx metric %d mtu %d", + ifp->name, VRF_LOGNAME(vrf), ifp->vrf_id, ifp->ifindex, (unsigned long long)ifp->flags, ifp->metric, ifp->mtu6); + } return 0; } /* Inteface addition message from zebra. */ -int ripng_interface_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ripng_ifp_create(struct interface *ifp) { - struct interface *ifp; - - ifp = zebra_interface_add_read(zclient->ibuf, vrf_id); ripng_interface_sync(ifp); - if (IS_RIPNG_DEBUG_ZEBRA) + if (IS_RIPNG_DEBUG_ZEBRA) { + struct vrf *vrf = vrf_lookup_by_id(ifp->vrf_id); + zlog_debug( - "RIPng interface add %s vrf %u index %d flags %#llx metric %d mtu %d", - ifp->name, ifp->vrf_id, ifp->ifindex, + "RIPng interface add %s vrf %s(%u) index %d flags %#llx metric %d mtu %d", + ifp->name, VRF_LOGNAME(vrf), ifp->vrf_id, ifp->ifindex, (unsigned long long)ifp->flags, ifp->metric, ifp->mtu6); + } /* Check is this interface is RIP enabled or not.*/ ripng_enable_apply(ifp); @@ -281,40 +265,26 @@ int ripng_interface_add(int command, struct zclient *zclient, return 0; } -int ripng_interface_delete(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ripng_ifp_destroy(struct interface *ifp) { - struct interface *ifp; - struct stream *s; - - s = zclient->ibuf; - /* zebra_interface_state_read() updates interface structure in iflist - */ - ifp = zebra_interface_state_read(s, vrf_id); - - if (ifp == NULL) - return 0; + struct vrf *vrf = vrf_lookup_by_id(ifp->vrf_id); ripng_interface_sync(ifp); if (if_is_up(ifp)) { ripng_if_down(ifp); } - zlog_info( - "interface delete %s vrf %u index %d flags %#llx metric %d mtu %d", - ifp->name, ifp->vrf_id, ifp->ifindex, - (unsigned long long)ifp->flags, ifp->metric, ifp->mtu6); - - /* To support pseudo interface do not free interface structure. */ - /* if_delete(ifp); */ - if_set_index(ifp, IFINDEX_INTERNAL); + if (IS_RIPNG_DEBUG_ZEBRA) + zlog_debug( + "interface delete %s vrf %s(%u) index %d flags %#llx metric %d mtu %d", + ifp->name, VRF_LOGNAME(vrf), ifp->vrf_id, ifp->ifindex, + (unsigned long long)ifp->flags, ifp->metric, ifp->mtu6); return 0; } /* VRF update for an interface. */ -int ripng_interface_vrf_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +int ripng_interface_vrf_update(ZAPI_CALLBACK_ARGS) { struct interface *ifp; vrf_id_t new_vrf_id; @@ -324,9 +294,14 @@ int ripng_interface_vrf_update(int command, struct zclient *zclient, if (!ifp) return 0; - if (IS_RIPNG_DEBUG_ZEBRA) - zlog_debug("interface %s VRF change vrf_id %u new vrf id %u", - ifp->name, vrf_id, new_vrf_id); + if (IS_RIPNG_DEBUG_ZEBRA) { + struct vrf *vrf = vrf_lookup_by_id(ifp->vrf_id); + struct vrf *nvrf = vrf_lookup_by_id(new_vrf_id); + + zlog_debug("interface %s VRF change vrf %s(%u) new vrf %s(%u)", + ifp->name, VRF_LOGNAME(vrf), vrf_id, + VRF_LOGNAME(nvrf), new_vrf_id); + } if_update_to_new_vrf(ifp, new_vrf_id); ripng_interface_sync(ifp); @@ -383,8 +358,7 @@ static void ripng_apply_address_add(struct connected *ifc) ifc->ifp->ifindex, NULL, 0); } -int ripng_interface_address_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +int ripng_interface_address_add(ZAPI_CALLBACK_ARGS) { struct connected *c; struct prefix *p; @@ -450,8 +424,7 @@ static void ripng_apply_address_del(struct connected *ifc) ifc->ifp->ifindex); } -int ripng_interface_address_delete(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +int ripng_interface_address_delete(ZAPI_CALLBACK_ARGS) { struct connected *ifc; struct prefix *p; @@ -474,7 +447,7 @@ int ripng_interface_address_delete(int command, struct zclient *zclient, /* Check wether this prefix needs to be removed. */ ripng_apply_address_del(ifc); } - connected_free(ifc); + connected_free(&ifc); } return 0; @@ -895,17 +868,12 @@ int ripng_network_write(struct vty *vty, struct ripng *ripng) unsigned int i; const char *ifname; struct agg_node *node; - char buf[BUFSIZ]; /* Write enable network. */ for (node = agg_route_top(ripng->enable_network); node; node = agg_route_next(node)) - if (node->info) { - struct prefix *p = &node->p; - vty_out(vty, " %s/%d\n", - inet_ntop(p->family, &p->u.prefix, buf, BUFSIZ), - p->prefixlen); - } + if (node->info) + vty_out(vty, " %pRN\n", node); /* Write enable interface. */ for (i = 0; i < vector_active(ripng->enable_if); i++) @@ -919,7 +887,7 @@ static struct ripng_interface *ri_new(void) { struct ripng_interface *ri; - ri = XCALLOC(MTYPE_IF, sizeof(struct ripng_interface)); + ri = XCALLOC(MTYPE_RIPNG_IF, sizeof(struct ripng_interface)); /* Set default split-horizon behavior. If the interface is Frame Relay or SMDS is enabled, the default value for split-horizon is @@ -956,8 +924,7 @@ static int ripng_if_new_hook(struct interface *ifp) /* Called when interface structure deleted. */ static int ripng_if_delete_hook(struct interface *ifp) { - XFREE(MTYPE_IF, ifp->info); - ifp->info = NULL; + XFREE(MTYPE_RIPNG_IF, ifp->info); return 0; } @@ -988,9 +955,14 @@ static int interface_config_write(struct vty *vty) return write; } +static int interface_config_write(struct vty *vty); /* ripngd's interface node. */ static struct cmd_node interface_node = { - INTERFACE_NODE, "%s(config-if)# ", 1 /* VTYSH */ + .name = "interface", + .node = INTERFACE_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-if)# ", + .config_write = interface_config_write, }; /* Initialization of interface. */ @@ -1001,6 +973,8 @@ void ripng_if_init(void) hook_register_prio(if_del, 0, ripng_if_delete_hook); /* Install interface node. */ - install_node(&interface_node, interface_config_write); + install_node(&interface_node); if_cmd_init(); + if_zapi_callbacks(ripng_ifp_create, ripng_ifp_up, + ripng_ifp_down, ripng_ifp_destroy); } diff --git a/ripngd/ripng_main.c b/ripngd/ripng_main.c index c755bd83ce..010bac851b 100644 --- a/ripngd/ripng_main.c +++ b/ripngd/ripng_main.c @@ -27,7 +27,6 @@ #include "vty.h" #include "command.h" #include "memory.h" -#include "memory_vty.h" #include "thread.h" #include "log.h" #include "prefix.h" @@ -37,14 +36,13 @@ #include "vrf.h" #include "if_rmap.h" #include "libfrr.h" +#include "routemap.h" #include "ripngd/ripngd.h" +#include "ripngd/ripng_nb.h" /* RIPngd options. */ -#if CONFDATE > 20190521 - CPP_NOTICE("-r / --retain has reached deprecation EOL, remove") -#endif -struct option longopts[] = {{"retain", no_argument, NULL, 'r'}, {0}}; +struct option longopts[] = {{0}}; /* ripngd privileges */ zebra_capabilities_t _caps_p[] = {ZCAP_NET_RAW, ZCAP_BIND, ZCAP_SYS_ADMIN}; @@ -115,9 +113,12 @@ struct quagga_signal_t ripng_signals[] = { }, }; -static const struct frr_yang_module_info *ripngd_yang_modules[] = { +static const struct frr_yang_module_info *const ripngd_yang_modules[] = { + &frr_filter_info, &frr_interface_info, &frr_ripngd_info, + &frr_route_map_info, + &frr_vrf_info, }; FRR_DAEMON_INFO(ripngd, RIPNG, .vty_port = RIPNG_VTY_PORT, @@ -132,10 +133,7 @@ FRR_DAEMON_INFO(ripngd, RIPNG, .vty_port = RIPNG_VTY_PORT, .yang_modules = ripngd_yang_modules, .n_yang_modules = array_size(ripngd_yang_modules), ) -#if CONFDATE > 20190521 -CPP_NOTICE("-r / --retain has reached deprecation EOL, remove") -#endif -#define DEPRECATED_OPTIONS "r" +#define DEPRECATED_OPTIONS "" /* RIPngd main routine. */ int main(int argc, char **argv) diff --git a/ripngd/ripng_memory.c b/ripngd/ripng_memory.c deleted file mode 100644 index f459566bed..0000000000 --- a/ripngd/ripng_memory.c +++ /dev/null @@ -1,35 +0,0 @@ -/* ripngd memory type definitions - * - * Copyright (C) 2015 David Lamparter - * - * This file is part of Quagga. - * - * Quagga is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2, or (at your option) any - * later version. - * - * Quagga is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; see the file COPYING; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "ripng_memory.h" - -DEFINE_MGROUP(RIPNGD, "ripngd") -DEFINE_MTYPE(RIPNGD, RIPNG, "RIPng structure") -DEFINE_MTYPE(RIPNGD, RIPNG_VRF_NAME, "RIPng VRF name") -DEFINE_MTYPE(RIPNGD, RIPNG_ROUTE, "RIPng route info") -DEFINE_MTYPE(RIPNGD, RIPNG_AGGREGATE, "RIPng aggregate") -DEFINE_MTYPE(RIPNGD, RIPNG_PEER, "RIPng peer") -DEFINE_MTYPE(RIPNGD, RIPNG_OFFSET_LIST, "RIPng offset lst") -DEFINE_MTYPE(RIPNGD, RIPNG_RTE_DATA, "RIPng rte data") diff --git a/ripngd/ripng_memory.h b/ripngd/ripng_memory.h deleted file mode 100644 index 3dfc57b3ff..0000000000 --- a/ripngd/ripng_memory.h +++ /dev/null @@ -1,36 +0,0 @@ -/* ripngd memory type declarations - * - * Copyright (C) 2015 David Lamparter - * - * This file is part of Quagga. - * - * Quagga is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2, or (at your option) any - * later version. - * - * Quagga is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; see the file COPYING; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef _QUAGGA_RIPNG_MEMORY_H -#define _QUAGGA_RIPNG_MEMORY_H - -#include "memory.h" - -DECLARE_MGROUP(RIPNGD) -DECLARE_MTYPE(RIPNG) -DECLARE_MTYPE(RIPNG_VRF_NAME) -DECLARE_MTYPE(RIPNG_ROUTE) -DECLARE_MTYPE(RIPNG_AGGREGATE) -DECLARE_MTYPE(RIPNG_PEER) -DECLARE_MTYPE(RIPNG_OFFSET_LIST) -DECLARE_MTYPE(RIPNG_RTE_DATA) - -#endif /* _QUAGGA_RIPNG_MEMORY_H */ diff --git a/ripngd/ripng_nb.c b/ripngd/ripng_nb.c new file mode 100644 index 0000000000..a02a72112f --- /dev/null +++ b/ripngd/ripng_nb.c @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2018 NetDEF, Inc. + * Renato Westphal + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "northbound.h" +#include "libfrr.h" + +#include "ripngd/ripng_nb.h" + +/* clang-format off */ +const struct frr_yang_module_info frr_ripngd_info = { + .name = "frr-ripngd", + .nodes = { + { + .xpath = "/frr-ripngd:ripngd/instance", + .cbs = { + .cli_show = cli_show_router_ripng, + .create = ripngd_instance_create, + .destroy = ripngd_instance_destroy, + .get_keys = ripngd_instance_get_keys, + .get_next = ripngd_instance_get_next, + .lookup_entry = ripngd_instance_lookup_entry, + }, + }, + { + .xpath = "/frr-ripngd:ripngd/instance/allow-ecmp", + .cbs = { + .cli_show = cli_show_ripng_allow_ecmp, + .modify = ripngd_instance_allow_ecmp_modify, + }, + }, + { + .xpath = "/frr-ripngd:ripngd/instance/default-information-originate", + .cbs = { + .cli_show = cli_show_ripng_default_information_originate, + .modify = ripngd_instance_default_information_originate_modify, + }, + }, + { + .xpath = "/frr-ripngd:ripngd/instance/default-metric", + .cbs = { + .cli_show = cli_show_ripng_default_metric, + .modify = ripngd_instance_default_metric_modify, + }, + }, + { + .xpath = "/frr-ripngd:ripngd/instance/network", + .cbs = { + .cli_show = cli_show_ripng_network_prefix, + .create = ripngd_instance_network_create, + .destroy = ripngd_instance_network_destroy, + }, + }, + { + .xpath = "/frr-ripngd:ripngd/instance/interface", + .cbs = { + .cli_show = cli_show_ripng_network_interface, + .create = ripngd_instance_interface_create, + .destroy = ripngd_instance_interface_destroy, + }, + }, + { + .xpath = "/frr-ripngd:ripngd/instance/offset-list", + .cbs = { + .cli_show = cli_show_ripng_offset_list, + .create = ripngd_instance_offset_list_create, + .destroy = ripngd_instance_offset_list_destroy, + }, + }, + { + .xpath = "/frr-ripngd:ripngd/instance/offset-list/access-list", + .cbs = { + .modify = ripngd_instance_offset_list_access_list_modify, + }, + }, + { + .xpath = "/frr-ripngd:ripngd/instance/offset-list/metric", + .cbs = { + .modify = ripngd_instance_offset_list_metric_modify, + }, + }, + { + .xpath = "/frr-ripngd:ripngd/instance/passive-interface", + .cbs = { + .cli_show = cli_show_ripng_passive_interface, + .create = ripngd_instance_passive_interface_create, + .destroy = ripngd_instance_passive_interface_destroy, + }, + }, + { + .xpath = "/frr-ripngd:ripngd/instance/redistribute", + .cbs = { + .apply_finish = ripngd_instance_redistribute_apply_finish, + .cli_show = cli_show_ripng_redistribute, + .create = ripngd_instance_redistribute_create, + .destroy = ripngd_instance_redistribute_destroy, + }, + }, + { + .xpath = "/frr-ripngd:ripngd/instance/redistribute/route-map", + .cbs = { + .destroy = ripngd_instance_redistribute_route_map_destroy, + .modify = ripngd_instance_redistribute_route_map_modify, + }, + }, + { + .xpath = "/frr-ripngd:ripngd/instance/redistribute/metric", + .cbs = { + .destroy = ripngd_instance_redistribute_metric_destroy, + .modify = ripngd_instance_redistribute_metric_modify, + }, + }, + { + .xpath = "/frr-ripngd:ripngd/instance/static-route", + .cbs = { + .cli_show = cli_show_ripng_route, + .create = ripngd_instance_static_route_create, + .destroy = ripngd_instance_static_route_destroy, + }, + }, + { + .xpath = "/frr-ripngd:ripngd/instance/aggregate-address", + .cbs = { + .cli_show = cli_show_ripng_aggregate_address, + .create = ripngd_instance_aggregate_address_create, + .destroy = ripngd_instance_aggregate_address_destroy, + }, + }, + { + .xpath = "/frr-ripngd:ripngd/instance/timers", + .cbs = { + .apply_finish = ripngd_instance_timers_apply_finish, + .cli_show = cli_show_ripng_timers, + }, + }, + { + .xpath = "/frr-ripngd:ripngd/instance/timers/flush-interval", + .cbs = { + .modify = ripngd_instance_timers_flush_interval_modify, + }, + }, + { + .xpath = "/frr-ripngd:ripngd/instance/timers/holddown-interval", + .cbs = { + .modify = ripngd_instance_timers_holddown_interval_modify, + }, + }, + { + .xpath = "/frr-ripngd:ripngd/instance/timers/update-interval", + .cbs = { + .modify = ripngd_instance_timers_update_interval_modify, + }, + }, + { + .xpath = "/frr-ripngd:ripngd/instance/state/neighbors/neighbor", + .cbs = { + .get_keys = ripngd_instance_state_neighbors_neighbor_get_keys, + .get_next = ripngd_instance_state_neighbors_neighbor_get_next, + .lookup_entry = ripngd_instance_state_neighbors_neighbor_lookup_entry, + }, + }, + { + .xpath = "/frr-ripngd:ripngd/instance/state/neighbors/neighbor/address", + .cbs = { + .get_elem = ripngd_instance_state_neighbors_neighbor_address_get_elem, + }, + }, + { + .xpath = "/frr-ripngd:ripngd/instance/state/neighbors/neighbor/last-update", + .cbs = { + .get_elem = ripngd_instance_state_neighbors_neighbor_last_update_get_elem, + }, + }, + { + .xpath = "/frr-ripngd:ripngd/instance/state/neighbors/neighbor/bad-packets-rcvd", + .cbs = { + .get_elem = ripngd_instance_state_neighbors_neighbor_bad_packets_rcvd_get_elem, + }, + }, + { + .xpath = "/frr-ripngd:ripngd/instance/state/neighbors/neighbor/bad-routes-rcvd", + .cbs = { + .get_elem = ripngd_instance_state_neighbors_neighbor_bad_routes_rcvd_get_elem, + }, + }, + { + .xpath = "/frr-ripngd:ripngd/instance/state/routes/route", + .cbs = { + .get_keys = ripngd_instance_state_routes_route_get_keys, + .get_next = ripngd_instance_state_routes_route_get_next, + .lookup_entry = ripngd_instance_state_routes_route_lookup_entry, + }, + }, + { + .xpath = "/frr-ripngd:ripngd/instance/state/routes/route/prefix", + .cbs = { + .get_elem = ripngd_instance_state_routes_route_prefix_get_elem, + }, + }, + { + .xpath = "/frr-ripngd:ripngd/instance/state/routes/route/next-hop", + .cbs = { + .get_elem = ripngd_instance_state_routes_route_next_hop_get_elem, + }, + }, + { + .xpath = "/frr-ripngd:ripngd/instance/state/routes/route/interface", + .cbs = { + .get_elem = ripngd_instance_state_routes_route_interface_get_elem, + }, + }, + { + .xpath = "/frr-ripngd:ripngd/instance/state/routes/route/metric", + .cbs = { + .get_elem = ripngd_instance_state_routes_route_metric_get_elem, + }, + }, + { + .xpath = "/frr-ripngd:clear-ripng-route", + .cbs = { + .rpc = clear_ripng_route_rpc, + }, + }, + { + .xpath = "/frr-interface:lib/interface/frr-ripngd:ripng/split-horizon", + .cbs = { + .cli_show = cli_show_ipv6_ripng_split_horizon, + .modify = lib_interface_ripng_split_horizon_modify, + }, + }, + { + .xpath = NULL, + }, + } +}; diff --git a/ripngd/ripng_nb.h b/ripngd/ripng_nb.h new file mode 100644 index 0000000000..d6aecbf6b0 --- /dev/null +++ b/ripngd/ripng_nb.h @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2018 NetDEF, Inc. + * Renato Westphal + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _FRR_RIPNG_NB_H_ +#define _FRR_RIPNG_NB_H_ + +extern const struct frr_yang_module_info frr_ripngd_info; + +/* Mandatory callbacks. */ +int ripngd_instance_create(struct nb_cb_create_args *args); +int ripngd_instance_destroy(struct nb_cb_destroy_args *args); +const void *ripngd_instance_get_next(struct nb_cb_get_next_args *args); +int ripngd_instance_get_keys(struct nb_cb_get_keys_args *args); +const void *ripngd_instance_lookup_entry(struct nb_cb_lookup_entry_args *args); +int ripngd_instance_allow_ecmp_modify(struct nb_cb_modify_args *args); +int ripngd_instance_default_information_originate_modify( + struct nb_cb_modify_args *args); +int ripngd_instance_default_metric_modify(struct nb_cb_modify_args *args); +int ripngd_instance_network_create(struct nb_cb_create_args *args); +int ripngd_instance_network_destroy(struct nb_cb_destroy_args *args); +int ripngd_instance_interface_create(struct nb_cb_create_args *args); +int ripngd_instance_interface_destroy(struct nb_cb_destroy_args *args); +int ripngd_instance_offset_list_create(struct nb_cb_create_args *args); +int ripngd_instance_offset_list_destroy(struct nb_cb_destroy_args *args); +int ripngd_instance_offset_list_access_list_modify( + struct nb_cb_modify_args *args); +int ripngd_instance_offset_list_metric_modify(struct nb_cb_modify_args *args); +int ripngd_instance_passive_interface_create(struct nb_cb_create_args *args); +int ripngd_instance_passive_interface_destroy(struct nb_cb_destroy_args *args); +int ripngd_instance_redistribute_create(struct nb_cb_create_args *args); +int ripngd_instance_redistribute_destroy(struct nb_cb_destroy_args *args); +int ripngd_instance_redistribute_route_map_modify( + struct nb_cb_modify_args *args); +int ripngd_instance_redistribute_route_map_destroy( + struct nb_cb_destroy_args *args); +int ripngd_instance_redistribute_metric_modify(struct nb_cb_modify_args *args); +int ripngd_instance_redistribute_metric_destroy( + struct nb_cb_destroy_args *args); +int ripngd_instance_static_route_create(struct nb_cb_create_args *args); +int ripngd_instance_static_route_destroy(struct nb_cb_destroy_args *args); +int ripngd_instance_aggregate_address_create(struct nb_cb_create_args *args); +int ripngd_instance_aggregate_address_destroy(struct nb_cb_destroy_args *args); +int ripngd_instance_timers_flush_interval_modify( + struct nb_cb_modify_args *args); +int ripngd_instance_timers_holddown_interval_modify( + struct nb_cb_modify_args *args); +int ripngd_instance_timers_update_interval_modify( + struct nb_cb_modify_args *args); +const void *ripngd_instance_state_neighbors_neighbor_get_next( + struct nb_cb_get_next_args *args); +int ripngd_instance_state_neighbors_neighbor_get_keys( + struct nb_cb_get_keys_args *args); +const void *ripngd_instance_state_neighbors_neighbor_lookup_entry( + struct nb_cb_lookup_entry_args *args); +struct yang_data *ripngd_instance_state_neighbors_neighbor_address_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data *ripngd_instance_state_neighbors_neighbor_last_update_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +ripngd_instance_state_neighbors_neighbor_bad_packets_rcvd_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +ripngd_instance_state_neighbors_neighbor_bad_routes_rcvd_get_elem( + struct nb_cb_get_elem_args *args); +const void * +ripngd_instance_state_routes_route_get_next(struct nb_cb_get_next_args *args); +int ripngd_instance_state_routes_route_get_keys( + struct nb_cb_get_keys_args *args); +const void *ripngd_instance_state_routes_route_lookup_entry( + struct nb_cb_lookup_entry_args *args); +struct yang_data *ripngd_instance_state_routes_route_prefix_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data *ripngd_instance_state_routes_route_next_hop_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data *ripngd_instance_state_routes_route_interface_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data *ripngd_instance_state_routes_route_metric_get_elem( + struct nb_cb_get_elem_args *args); +int clear_ripng_route_rpc(struct nb_cb_rpc_args *args); +int lib_interface_ripng_split_horizon_modify(struct nb_cb_modify_args *args); + +/* Optional 'apply_finish' callbacks. */ +void ripngd_instance_redistribute_apply_finish( + struct nb_cb_apply_finish_args *args); +void ripngd_instance_timers_apply_finish(struct nb_cb_apply_finish_args *args); + +/* Optional 'cli_show' callbacks. */ +void cli_show_router_ripng(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_ripng_allow_ecmp(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_ripng_default_information_originate(struct vty *vty, + struct lyd_node *dnode, + bool show_defaults); +void cli_show_ripng_default_metric(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_ripng_network_prefix(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_ripng_network_interface(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_ripng_offset_list(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_ripng_passive_interface(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_ripng_redistribute(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_ripng_route(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_ripng_aggregate_address(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_ripng_timers(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_ipv6_ripng_split_horizon(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); + +#endif /* _FRR_RIPNG_NB_H_ */ diff --git a/ripngd/ripng_nb_config.c b/ripngd/ripng_nb_config.c new file mode 100644 index 0000000000..85f378bc9e --- /dev/null +++ b/ripngd/ripng_nb_config.c @@ -0,0 +1,673 @@ +/* + * Copyright (C) 1998 Kunihiro Ishiguro + * Copyright (C) 2018 NetDEF, Inc. + * Renato Westphal + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "if.h" +#include "vrf.h" +#include "log.h" +#include "prefix.h" +#include "table.h" +#include "command.h" +#include "routemap.h" +#include "agg_table.h" +#include "northbound.h" +#include "libfrr.h" + +#include "ripngd/ripngd.h" +#include "ripngd/ripng_nb.h" +#include "ripngd/ripng_debug.h" +#include "ripngd/ripng_route.h" + +/* + * XPath: /frr-ripngd:ripngd/instance + */ +int ripngd_instance_create(struct nb_cb_create_args *args) +{ + struct ripng *ripng; + struct vrf *vrf; + const char *vrf_name; + int socket; + + vrf_name = yang_dnode_get_string(args->dnode, "./vrf"); + vrf = vrf_lookup_by_name(vrf_name); + + /* + * Try to create a RIPng socket only if the VRF is enabled, otherwise + * create a disabled RIPng instance and wait for the VRF to be enabled. + */ + switch (args->event) { + case NB_EV_VALIDATE: + break; + case NB_EV_PREPARE: + if (!vrf || !vrf_is_enabled(vrf)) + break; + + socket = ripng_make_socket(vrf); + if (socket < 0) + return NB_ERR_RESOURCE; + args->resource->fd = socket; + break; + case NB_EV_ABORT: + if (!vrf || !vrf_is_enabled(vrf)) + break; + + socket = args->resource->fd; + close(socket); + break; + case NB_EV_APPLY: + if (vrf && vrf_is_enabled(vrf)) + socket = args->resource->fd; + else + socket = -1; + + ripng = ripng_create(vrf_name, vrf, socket); + nb_running_set_entry(args->dnode, ripng); + break; + } + + return NB_OK; +} + +int ripngd_instance_destroy(struct nb_cb_destroy_args *args) +{ + struct ripng *ripng; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ripng = nb_running_unset_entry(args->dnode); + ripng_clean(ripng); + + return NB_OK; +} + +const void *ripngd_instance_get_next(struct nb_cb_get_next_args *args) +{ + struct ripng *ripng = (struct ripng *)args->list_entry; + + if (args->list_entry == NULL) + ripng = RB_MIN(ripng_instance_head, &ripng_instances); + else + ripng = RB_NEXT(ripng_instance_head, ripng); + + return ripng; +} + +int ripngd_instance_get_keys(struct nb_cb_get_keys_args *args) +{ + const struct ripng *ripng = args->list_entry; + + args->keys->num = 1; + strlcpy(args->keys->key[0], ripng->vrf_name, + sizeof(args->keys->key[0])); + + return NB_OK; +} + +const void *ripngd_instance_lookup_entry(struct nb_cb_lookup_entry_args *args) +{ + const char *vrf_name = args->keys->key[0]; + + return ripng_lookup_by_vrf_name(vrf_name); +} + +/* + * XPath: /frr-ripngd:ripngd/instance/allow-ecmp + */ +int ripngd_instance_allow_ecmp_modify(struct nb_cb_modify_args *args) +{ + struct ripng *ripng; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ripng = nb_running_get_entry(args->dnode, NULL, true); + ripng->ecmp = yang_dnode_get_bool(args->dnode, NULL); + if (!ripng->ecmp) + ripng_ecmp_disable(ripng); + + return NB_OK; +} + +/* + * XPath: /frr-ripngd:ripngd/instance/default-information-originate + */ +int ripngd_instance_default_information_originate_modify( + struct nb_cb_modify_args *args) +{ + struct ripng *ripng; + bool default_information; + struct prefix_ipv6 p; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ripng = nb_running_get_entry(args->dnode, NULL, true); + default_information = yang_dnode_get_bool(args->dnode, NULL); + + str2prefix_ipv6("::/0", &p); + if (default_information) { + ripng_redistribute_add(ripng, ZEBRA_ROUTE_RIPNG, + RIPNG_ROUTE_DEFAULT, &p, 0, NULL, 0); + } else { + ripng_redistribute_delete(ripng, ZEBRA_ROUTE_RIPNG, + RIPNG_ROUTE_DEFAULT, &p, 0); + } + + return NB_OK; +} + +/* + * XPath: /frr-ripngd:ripngd/instance/default-metric + */ +int ripngd_instance_default_metric_modify(struct nb_cb_modify_args *args) +{ + struct ripng *ripng; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ripng = nb_running_get_entry(args->dnode, NULL, true); + ripng->default_metric = yang_dnode_get_uint8(args->dnode, NULL); + + return NB_OK; +} + +/* + * XPath: /frr-ripngd:ripngd/instance/network + */ +int ripngd_instance_network_create(struct nb_cb_create_args *args) +{ + struct ripng *ripng; + struct prefix p; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ripng = nb_running_get_entry(args->dnode, NULL, true); + yang_dnode_get_ipv6p(&p, args->dnode, NULL); + apply_mask_ipv6((struct prefix_ipv6 *)&p); + + return ripng_enable_network_add(ripng, &p); +} + +int ripngd_instance_network_destroy(struct nb_cb_destroy_args *args) +{ + struct ripng *ripng; + struct prefix p; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ripng = nb_running_get_entry(args->dnode, NULL, true); + yang_dnode_get_ipv6p(&p, args->dnode, NULL); + apply_mask_ipv6((struct prefix_ipv6 *)&p); + + return ripng_enable_network_delete(ripng, &p); +} + +/* + * XPath: /frr-ripngd:ripngd/instance/interface + */ +int ripngd_instance_interface_create(struct nb_cb_create_args *args) +{ + struct ripng *ripng; + const char *ifname; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ripng = nb_running_get_entry(args->dnode, NULL, true); + ifname = yang_dnode_get_string(args->dnode, NULL); + + return ripng_enable_if_add(ripng, ifname); +} + +int ripngd_instance_interface_destroy(struct nb_cb_destroy_args *args) +{ + struct ripng *ripng; + const char *ifname; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ripng = nb_running_get_entry(args->dnode, NULL, true); + ifname = yang_dnode_get_string(args->dnode, NULL); + + return ripng_enable_if_delete(ripng, ifname); +} + +/* + * XPath: /frr-ripngd:ripngd/instance/offset-list + */ +int ripngd_instance_offset_list_create(struct nb_cb_create_args *args) +{ + struct ripng *ripng; + const char *ifname; + struct ripng_offset_list *offset; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ripng = nb_running_get_entry(args->dnode, NULL, true); + ifname = yang_dnode_get_string(args->dnode, "./interface"); + + offset = ripng_offset_list_new(ripng, ifname); + nb_running_set_entry(args->dnode, offset); + + return NB_OK; +} + +int ripngd_instance_offset_list_destroy(struct nb_cb_destroy_args *args) +{ + int direct; + struct ripng_offset_list *offset; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + direct = yang_dnode_get_enum(args->dnode, "./direction"); + + offset = nb_running_unset_entry(args->dnode); + if (offset->direct[direct].alist_name) { + free(offset->direct[direct].alist_name); + offset->direct[direct].alist_name = NULL; + } + if (offset->direct[RIPNG_OFFSET_LIST_IN].alist_name == NULL + && offset->direct[RIPNG_OFFSET_LIST_OUT].alist_name == NULL) + ripng_offset_list_del(offset); + + return NB_OK; +} + +/* + * XPath: /frr-ripngd:ripngd/instance/offset-list/access-list + */ +int ripngd_instance_offset_list_access_list_modify( + struct nb_cb_modify_args *args) +{ + int direct; + struct ripng_offset_list *offset; + const char *alist_name; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + direct = yang_dnode_get_enum(args->dnode, "../direction"); + alist_name = yang_dnode_get_string(args->dnode, NULL); + + offset = nb_running_get_entry(args->dnode, NULL, true); + if (offset->direct[direct].alist_name) + free(offset->direct[direct].alist_name); + offset->direct[direct].alist_name = strdup(alist_name); + + return NB_OK; +} + +/* + * XPath: /frr-ripngd:ripngd/instance/offset-list/metric + */ +int ripngd_instance_offset_list_metric_modify(struct nb_cb_modify_args *args) +{ + int direct; + uint8_t metric; + struct ripng_offset_list *offset; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + direct = yang_dnode_get_enum(args->dnode, "../direction"); + metric = yang_dnode_get_uint8(args->dnode, NULL); + + offset = nb_running_get_entry(args->dnode, NULL, true); + offset->direct[direct].metric = metric; + + return NB_OK; +} + +/* + * XPath: /frr-ripngd:ripngd/instance/passive-interface + */ +int ripngd_instance_passive_interface_create(struct nb_cb_create_args *args) +{ + struct ripng *ripng; + const char *ifname; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ripng = nb_running_get_entry(args->dnode, NULL, true); + ifname = yang_dnode_get_string(args->dnode, NULL); + + return ripng_passive_interface_set(ripng, ifname); +} + +int ripngd_instance_passive_interface_destroy(struct nb_cb_destroy_args *args) +{ + struct ripng *ripng; + const char *ifname; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ripng = nb_running_get_entry(args->dnode, NULL, true); + ifname = yang_dnode_get_string(args->dnode, NULL); + + return ripng_passive_interface_unset(ripng, ifname); +} + +/* + * XPath: /frr-ripngd:ripngd/instance/redistribute + */ +int ripngd_instance_redistribute_create(struct nb_cb_create_args *args) +{ + struct ripng *ripng; + int type; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ripng = nb_running_get_entry(args->dnode, NULL, true); + type = yang_dnode_get_enum(args->dnode, "./protocol"); + + ripng->redist[type].enabled = true; + + return NB_OK; +} + +int ripngd_instance_redistribute_destroy(struct nb_cb_destroy_args *args) +{ + struct ripng *ripng; + int type; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ripng = nb_running_get_entry(args->dnode, NULL, true); + type = yang_dnode_get_enum(args->dnode, "./protocol"); + + ripng->redist[type].enabled = false; + if (ripng->redist[type].route_map.name) { + free(ripng->redist[type].route_map.name); + ripng->redist[type].route_map.name = NULL; + ripng->redist[type].route_map.map = NULL; + } + ripng->redist[type].metric_config = false; + ripng->redist[type].metric = 0; + + if (ripng->enabled) + ripng_redistribute_conf_delete(ripng, type); + + return NB_OK; +} + +void ripngd_instance_redistribute_apply_finish( + struct nb_cb_apply_finish_args *args) +{ + struct ripng *ripng; + int type; + + ripng = nb_running_get_entry(args->dnode, NULL, true); + type = yang_dnode_get_enum(args->dnode, "./protocol"); + + if (ripng->enabled) + ripng_redistribute_conf_update(ripng, type); +} + +/* + * XPath: /frr-ripngd:ripngd/instance/redistribute/route-map + */ +int ripngd_instance_redistribute_route_map_modify( + struct nb_cb_modify_args *args) +{ + struct ripng *ripng; + int type; + const char *rmap_name; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ripng = nb_running_get_entry(args->dnode, NULL, true); + type = yang_dnode_get_enum(args->dnode, "../protocol"); + rmap_name = yang_dnode_get_string(args->dnode, NULL); + + if (ripng->redist[type].route_map.name) + free(ripng->redist[type].route_map.name); + ripng->redist[type].route_map.name = strdup(rmap_name); + ripng->redist[type].route_map.map = route_map_lookup_by_name(rmap_name); + + return NB_OK; +} + +int ripngd_instance_redistribute_route_map_destroy( + struct nb_cb_destroy_args *args) +{ + struct ripng *ripng; + int type; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ripng = nb_running_get_entry(args->dnode, NULL, true); + type = yang_dnode_get_enum(args->dnode, "../protocol"); + + free(ripng->redist[type].route_map.name); + ripng->redist[type].route_map.name = NULL; + ripng->redist[type].route_map.map = NULL; + + return NB_OK; +} + +/* + * XPath: /frr-ripngd:ripngd/instance/redistribute/metric + */ +int ripngd_instance_redistribute_metric_modify(struct nb_cb_modify_args *args) +{ + struct ripng *ripng; + int type; + uint8_t metric; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ripng = nb_running_get_entry(args->dnode, NULL, true); + type = yang_dnode_get_enum(args->dnode, "../protocol"); + metric = yang_dnode_get_uint8(args->dnode, NULL); + + ripng->redist[type].metric_config = true; + ripng->redist[type].metric = metric; + + return NB_OK; +} + +int ripngd_instance_redistribute_metric_destroy(struct nb_cb_destroy_args *args) +{ + struct ripng *ripng; + int type; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ripng = nb_running_get_entry(args->dnode, NULL, true); + type = yang_dnode_get_enum(args->dnode, "../protocol"); + + ripng->redist[type].metric_config = false; + ripng->redist[type].metric = 0; + + return NB_OK; +} + +/* + * XPath: /frr-ripngd:ripngd/instance/static-route + */ +int ripngd_instance_static_route_create(struct nb_cb_create_args *args) +{ + struct ripng *ripng; + struct prefix_ipv6 p; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ripng = nb_running_get_entry(args->dnode, NULL, true); + yang_dnode_get_ipv6p(&p, args->dnode, NULL); + apply_mask_ipv6(&p); + + ripng_redistribute_add(ripng, ZEBRA_ROUTE_RIPNG, RIPNG_ROUTE_STATIC, &p, + 0, NULL, 0); + + return NB_OK; +} + +int ripngd_instance_static_route_destroy(struct nb_cb_destroy_args *args) +{ + struct ripng *ripng; + struct prefix_ipv6 p; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ripng = nb_running_get_entry(args->dnode, NULL, true); + yang_dnode_get_ipv6p(&p, args->dnode, NULL); + apply_mask_ipv6(&p); + + ripng_redistribute_delete(ripng, ZEBRA_ROUTE_RIPNG, RIPNG_ROUTE_STATIC, + &p, 0); + + return NB_OK; +} + +/* + * XPath: /frr-ripngd:ripngd/instance/aggregate-address + */ +int ripngd_instance_aggregate_address_create(struct nb_cb_create_args *args) +{ + struct ripng *ripng; + struct prefix_ipv6 p; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ripng = nb_running_get_entry(args->dnode, NULL, true); + yang_dnode_get_ipv6p(&p, args->dnode, NULL); + apply_mask_ipv6(&p); + + ripng_aggregate_add(ripng, (struct prefix *)&p); + + return NB_OK; +} + +int ripngd_instance_aggregate_address_destroy(struct nb_cb_destroy_args *args) +{ + struct ripng *ripng; + struct prefix_ipv6 p; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ripng = nb_running_get_entry(args->dnode, NULL, true); + yang_dnode_get_ipv6p(&p, args->dnode, NULL); + apply_mask_ipv6(&p); + + ripng_aggregate_delete(ripng, (struct prefix *)&p); + + return NB_OK; +} + +/* + * XPath: /frr-ripngd:ripngd/instance/timers + */ +void ripngd_instance_timers_apply_finish(struct nb_cb_apply_finish_args *args) +{ + struct ripng *ripng; + + ripng = nb_running_get_entry(args->dnode, NULL, true); + + /* Reset update timer thread. */ + ripng_event(ripng, RIPNG_UPDATE_EVENT, 0); +} + +/* + * XPath: /frr-ripngd:ripngd/instance/timers/flush-interval + */ +int ripngd_instance_timers_flush_interval_modify(struct nb_cb_modify_args *args) +{ + struct ripng *ripng; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ripng = nb_running_get_entry(args->dnode, NULL, true); + ripng->garbage_time = yang_dnode_get_uint16(args->dnode, NULL); + + return NB_OK; +} + +/* + * XPath: /frr-ripngd:ripngd/instance/timers/holddown-interval + */ +int ripngd_instance_timers_holddown_interval_modify( + struct nb_cb_modify_args *args) +{ + struct ripng *ripng; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ripng = nb_running_get_entry(args->dnode, NULL, true); + ripng->timeout_time = yang_dnode_get_uint16(args->dnode, NULL); + + return NB_OK; +} + +/* + * XPath: /frr-ripngd:ripngd/instance/timers/update-interval + */ +int ripngd_instance_timers_update_interval_modify( + struct nb_cb_modify_args *args) +{ + struct ripng *ripng; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ripng = nb_running_get_entry(args->dnode, NULL, true); + ripng->update_time = yang_dnode_get_uint16(args->dnode, NULL); + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-ripngd:ripng/split-horizon + */ +int lib_interface_ripng_split_horizon_modify(struct nb_cb_modify_args *args) +{ + struct interface *ifp; + struct ripng_interface *ri; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + ri = ifp->info; + ri->split_horizon = yang_dnode_get_enum(args->dnode, NULL); + + return NB_OK; +} diff --git a/ripngd/ripng_nb_rpcs.c b/ripngd/ripng_nb_rpcs.c new file mode 100644 index 0000000000..4dfe9d9640 --- /dev/null +++ b/ripngd/ripng_nb_rpcs.c @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2018 NetDEF, Inc. + * Renato Westphal + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "if.h" +#include "vrf.h" +#include "log.h" +#include "prefix.h" +#include "table.h" +#include "command.h" +#include "routemap.h" +#include "agg_table.h" +#include "northbound.h" +#include "libfrr.h" + +#include "ripngd/ripngd.h" +#include "ripngd/ripng_nb.h" +#include "ripngd/ripng_debug.h" +#include "ripngd/ripng_route.h" + +/* + * XPath: /frr-ripngd:clear-ripng-route + */ +static void clear_ripng_route(struct ripng *ripng) +{ + struct agg_node *rp; + + if (IS_RIPNG_DEBUG_EVENT) + zlog_debug("Clearing all RIPng routes (VRF %s)", + ripng->vrf_name); + + /* Clear received RIPng routes */ + for (rp = agg_route_top(ripng->table); rp; rp = agg_route_next(rp)) { + struct list *list; + struct listnode *listnode; + struct ripng_info *rinfo; + + list = rp->info; + if (list == NULL) + continue; + + for (ALL_LIST_ELEMENTS_RO(list, listnode, rinfo)) { + if (!ripng_route_rte(rinfo)) + continue; + + if (CHECK_FLAG(rinfo->flags, RIPNG_RTF_FIB)) + ripng_zebra_ipv6_delete(ripng, rp); + break; + } + + if (rinfo) { + RIPNG_TIMER_OFF(rinfo->t_timeout); + RIPNG_TIMER_OFF(rinfo->t_garbage_collect); + listnode_delete(list, rinfo); + ripng_info_free(rinfo); + } + + if (list_isempty(list)) { + list_delete(&list); + rp->info = NULL; + agg_unlock_node(rp); + } + } +} + +int clear_ripng_route_rpc(struct nb_cb_rpc_args *args) +{ + struct ripng *ripng; + struct yang_data *yang_vrf; + + yang_vrf = yang_data_list_find(args->input, "%s/%s", args->xpath, + "input/vrf"); + if (yang_vrf) { + ripng = ripng_lookup_by_vrf_name(yang_vrf->value); + if (ripng) + clear_ripng_route(ripng); + } else { + struct vrf *vrf; + + RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { + ripng = vrf->info; + if (!ripng) + continue; + + clear_ripng_route(ripng); + } + } + + return NB_OK; +} diff --git a/ripngd/ripng_nb_state.c b/ripngd/ripng_nb_state.c new file mode 100644 index 0000000000..02a00ac429 --- /dev/null +++ b/ripngd/ripng_nb_state.c @@ -0,0 +1,233 @@ +/* + * Copyright (C) 2018 NetDEF, Inc. + * Renato Westphal + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "if.h" +#include "vrf.h" +#include "log.h" +#include "prefix.h" +#include "table.h" +#include "command.h" +#include "routemap.h" +#include "agg_table.h" +#include "northbound.h" +#include "libfrr.h" + +#include "ripngd/ripngd.h" +#include "ripngd/ripng_nb.h" +#include "ripngd/ripng_debug.h" +#include "ripngd/ripng_route.h" + +/* + * XPath: /frr-ripngd:ripngd/instance/state/neighbors/neighbor + */ +const void *ripngd_instance_state_neighbors_neighbor_get_next( + struct nb_cb_get_next_args *args) +{ + const struct ripng *ripng = args->parent_list_entry; + struct listnode *node; + + if (args->list_entry == NULL) + node = listhead(ripng->peer_list); + else + node = listnextnode((struct listnode *)args->list_entry); + + return node; +} + +int ripngd_instance_state_neighbors_neighbor_get_keys( + struct nb_cb_get_keys_args *args) +{ + const struct listnode *node = args->list_entry; + const struct ripng_peer *peer = listgetdata(node); + + args->keys->num = 1; + (void)inet_ntop(AF_INET6, &peer->addr, args->keys->key[0], + sizeof(args->keys->key[0])); + + return NB_OK; +} + +const void *ripngd_instance_state_neighbors_neighbor_lookup_entry( + struct nb_cb_lookup_entry_args *args) +{ + const struct ripng *ripng = args->parent_list_entry; + struct in6_addr address; + struct ripng_peer *peer; + struct listnode *node; + + yang_str2ipv6(args->keys->key[0], &address); + + for (ALL_LIST_ELEMENTS_RO(ripng->peer_list, node, peer)) { + if (IPV6_ADDR_SAME(&peer->addr, &address)) + return node; + } + + return NULL; +} + +/* + * XPath: /frr-ripngd:ripngd/instance/state/neighbors/neighbor/address + */ +struct yang_data *ripngd_instance_state_neighbors_neighbor_address_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct listnode *node = args->list_entry; + const struct ripng_peer *peer = listgetdata(node); + + return yang_data_new_ipv6(args->xpath, &peer->addr); +} + +/* + * XPath: /frr-ripngd:ripngd/instance/state/neighbors/neighbor/last-update + */ +struct yang_data *ripngd_instance_state_neighbors_neighbor_last_update_get_elem( + struct nb_cb_get_elem_args *args) +{ + /* TODO: yang:date-and-time is tricky */ + return NULL; +} + +/* + * XPath: /frr-ripngd:ripngd/instance/state/neighbors/neighbor/bad-packets-rcvd + */ +struct yang_data * +ripngd_instance_state_neighbors_neighbor_bad_packets_rcvd_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct listnode *node = args->list_entry; + const struct ripng_peer *peer = listgetdata(node); + + return yang_data_new_uint32(args->xpath, peer->recv_badpackets); +} + +/* + * XPath: /frr-ripngd:ripngd/instance/state/neighbors/neighbor/bad-routes-rcvd + */ +struct yang_data * +ripngd_instance_state_neighbors_neighbor_bad_routes_rcvd_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct listnode *node = args->list_entry; + const struct ripng_peer *peer = listgetdata(node); + + return yang_data_new_uint32(args->xpath, peer->recv_badroutes); +} + +/* + * XPath: /frr-ripngd:ripngd/instance/state/routes/route + */ +const void * +ripngd_instance_state_routes_route_get_next(struct nb_cb_get_next_args *args) +{ + const struct ripng *ripng = args->parent_list_entry; + struct agg_node *rn; + + if (args->list_entry == NULL) + rn = agg_route_top(ripng->table); + else + rn = agg_route_next((struct agg_node *)args->list_entry); + /* Optimization: skip empty route nodes. */ + while (rn && rn->info == NULL) + rn = agg_route_next(rn); + + return rn; +} + +int ripngd_instance_state_routes_route_get_keys( + struct nb_cb_get_keys_args *args) +{ + const struct agg_node *rn = args->list_entry; + + args->keys->num = 1; + (void)prefix2str(agg_node_get_prefix(rn), args->keys->key[0], + sizeof(args->keys->key[0])); + + return NB_OK; +} + +const void *ripngd_instance_state_routes_route_lookup_entry( + struct nb_cb_lookup_entry_args *args) +{ + const struct ripng *ripng = args->parent_list_entry; + struct prefix prefix; + struct agg_node *rn; + + yang_str2ipv6p(args->keys->key[0], &prefix); + + rn = agg_node_lookup(ripng->table, &prefix); + if (!rn || !rn->info) + return NULL; + + agg_unlock_node(rn); + + return rn; +} + +/* + * XPath: /frr-ripngd:ripngd/instance/state/routes/route/prefix + */ +struct yang_data *ripngd_instance_state_routes_route_prefix_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct agg_node *rn = args->list_entry; + const struct ripng_info *rinfo = listnode_head(rn->info); + + return yang_data_new_ipv6p(args->xpath, agg_node_get_prefix(rinfo->rp)); +} + +/* + * XPath: /frr-ripngd:ripngd/instance/state/routes/route/next-hop + */ +struct yang_data *ripngd_instance_state_routes_route_next_hop_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct agg_node *rn = args->list_entry; + const struct ripng_info *rinfo = listnode_head(rn->info); + + return yang_data_new_ipv6(args->xpath, &rinfo->nexthop); +} + +/* + * XPath: /frr-ripngd:ripngd/instance/state/routes/route/interface + */ +struct yang_data *ripngd_instance_state_routes_route_interface_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct agg_node *rn = args->list_entry; + const struct ripng_info *rinfo = listnode_head(rn->info); + const struct ripng *ripng = ripng_info_get_instance(rinfo); + + return yang_data_new_string( + args->xpath, + ifindex2ifname(rinfo->ifindex, ripng->vrf->vrf_id)); +} + +/* + * XPath: /frr-ripngd:ripngd/instance/state/routes/route/metric + */ +struct yang_data *ripngd_instance_state_routes_route_metric_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct agg_node *rn = args->list_entry; + const struct ripng_info *rinfo = listnode_head(rn->info); + + return yang_data_new_uint8(args->xpath, rinfo->metric); +} diff --git a/ripngd/ripng_nexthop.c b/ripngd/ripng_nexthop.c index 882c2fbc8c..ba6e52fdda 100644 --- a/ripngd/ripng_nexthop.c +++ b/ripngd/ripng_nexthop.c @@ -39,6 +39,8 @@ #include "ripngd/ripng_debug.h" #include "ripngd/ripng_nexthop.h" +DEFINE_MTYPE_STATIC(RIPNGD, RIPNG_RTE_DATA, "RIPng rte data") + #define DEBUG 1 #define min(a, b) ((a) < (b) ? (a) : (b)) diff --git a/ripngd/ripng_northbound.c b/ripngd/ripng_northbound.c deleted file mode 100644 index f8ac4a5cd5..0000000000 --- a/ripngd/ripng_northbound.c +++ /dev/null @@ -1,1172 +0,0 @@ -/* - * Copyright (C) 1998 Kunihiro Ishiguro - * Copyright (C) 2018 NetDEF, Inc. - * Renato Westphal - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; see the file COPYING; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include - -#include "if.h" -#include "vrf.h" -#include "log.h" -#include "prefix.h" -#include "table.h" -#include "command.h" -#include "routemap.h" -#include "agg_table.h" -#include "northbound.h" -#include "libfrr.h" - -#include "ripngd/ripngd.h" -#include "ripngd/ripng_debug.h" -#include "ripngd/ripng_route.h" -#include "ripngd/ripng_cli.h" - -/* - * XPath: /frr-ripngd:ripngd/instance - */ -static int ripngd_instance_create(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct ripng *ripng; - struct vrf *vrf; - const char *vrf_name; - int socket; - - vrf_name = yang_dnode_get_string(dnode, "./vrf"); - vrf = vrf_lookup_by_name(vrf_name); - - /* - * Try to create a RIPng socket only if the VRF is enabled, otherwise - * create a disabled RIPng instance and wait for the VRF to be enabled. - */ - switch (event) { - case NB_EV_VALIDATE: - break; - case NB_EV_PREPARE: - if (!vrf || !vrf_is_enabled(vrf)) - break; - - socket = ripng_make_socket(vrf); - if (socket < 0) - return NB_ERR_RESOURCE; - resource->fd = socket; - break; - case NB_EV_ABORT: - if (!vrf || !vrf_is_enabled(vrf)) - break; - - socket = resource->fd; - close(socket); - break; - case NB_EV_APPLY: - if (vrf && vrf_is_enabled(vrf)) - socket = resource->fd; - else - socket = -1; - - ripng = ripng_create(vrf_name, vrf, socket); - nb_running_set_entry(dnode, ripng); - break; - } - - return NB_OK; -} - -static int ripngd_instance_destroy(enum nb_event event, - const struct lyd_node *dnode) -{ - struct ripng *ripng; - - if (event != NB_EV_APPLY) - return NB_OK; - - ripng = nb_running_unset_entry(dnode); - ripng_clean(ripng); - - return NB_OK; -} - -static const void *ripngd_instance_get_next(const void *parent_list_entry, - const void *list_entry) -{ - const struct ripng *ripng = list_entry; - - if (list_entry == NULL) - ripng = RB_MIN(ripng_instance_head, &ripng_instances); - else - ripng = RB_NEXT(ripng_instance_head, (struct ripng *)ripng); - - return ripng; -} - -static int ripngd_instance_get_keys(const void *list_entry, - struct yang_list_keys *keys) -{ - const struct ripng *ripng = list_entry; - - keys->num = 1; - strlcpy(keys->key[0], ripng->vrf_name, sizeof(keys->key[0])); - - return NB_OK; -} - -static const void * -ripngd_instance_lookup_entry(const void *parent_list_entry, - const struct yang_list_keys *keys) -{ - const char *vrf_name = keys->key[0]; - - return ripng_lookup_by_vrf_name(vrf_name); -} - -/* - * XPath: /frr-ripngd:ripngd/instance/allow-ecmp - */ -static int ripngd_instance_allow_ecmp_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct ripng *ripng; - - if (event != NB_EV_APPLY) - return NB_OK; - - ripng = nb_running_get_entry(dnode, NULL, true); - ripng->ecmp = yang_dnode_get_bool(dnode, NULL); - if (!ripng->ecmp) - ripng_ecmp_disable(ripng); - - return NB_OK; -} - -/* - * XPath: /frr-ripngd:ripngd/instance/default-information-originate - */ -static int ripngd_instance_default_information_originate_modify( - enum nb_event event, const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct ripng *ripng; - bool default_information; - struct prefix_ipv6 p; - - if (event != NB_EV_APPLY) - return NB_OK; - - ripng = nb_running_get_entry(dnode, NULL, true); - default_information = yang_dnode_get_bool(dnode, NULL); - - str2prefix_ipv6("::/0", &p); - if (default_information) { - ripng_redistribute_add(ripng, ZEBRA_ROUTE_RIPNG, - RIPNG_ROUTE_DEFAULT, &p, 0, NULL, 0); - } else { - ripng_redistribute_delete(ripng, ZEBRA_ROUTE_RIPNG, - RIPNG_ROUTE_DEFAULT, &p, 0); - } - - return NB_OK; -} - -/* - * XPath: /frr-ripngd:ripngd/instance/default-metric - */ -static int ripngd_instance_default_metric_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct ripng *ripng; - - if (event != NB_EV_APPLY) - return NB_OK; - - ripng = nb_running_get_entry(dnode, NULL, true); - ripng->default_metric = yang_dnode_get_uint8(dnode, NULL); - - return NB_OK; -} - -/* - * XPath: /frr-ripngd:ripngd/instance/network - */ -static int ripngd_instance_network_create(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct ripng *ripng; - struct prefix p; - - if (event != NB_EV_APPLY) - return NB_OK; - - ripng = nb_running_get_entry(dnode, NULL, true); - yang_dnode_get_ipv6p(&p, dnode, NULL); - apply_mask_ipv6((struct prefix_ipv6 *)&p); - - return ripng_enable_network_add(ripng, &p); -} - -static int ripngd_instance_network_destroy(enum nb_event event, - const struct lyd_node *dnode) -{ - struct ripng *ripng; - struct prefix p; - - if (event != NB_EV_APPLY) - return NB_OK; - - ripng = nb_running_get_entry(dnode, NULL, true); - yang_dnode_get_ipv6p(&p, dnode, NULL); - apply_mask_ipv6((struct prefix_ipv6 *)&p); - - return ripng_enable_network_delete(ripng, &p); -} - -/* - * XPath: /frr-ripngd:ripngd/instance/interface - */ -static int ripngd_instance_interface_create(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct ripng *ripng; - const char *ifname; - - if (event != NB_EV_APPLY) - return NB_OK; - - ripng = nb_running_get_entry(dnode, NULL, true); - ifname = yang_dnode_get_string(dnode, NULL); - - return ripng_enable_if_add(ripng, ifname); -} - -static int ripngd_instance_interface_destroy(enum nb_event event, - const struct lyd_node *dnode) -{ - struct ripng *ripng; - const char *ifname; - - if (event != NB_EV_APPLY) - return NB_OK; - - ripng = nb_running_get_entry(dnode, NULL, true); - ifname = yang_dnode_get_string(dnode, NULL); - - return ripng_enable_if_delete(ripng, ifname); -} - -/* - * XPath: /frr-ripngd:ripngd/instance/offset-list - */ -static int ripngd_instance_offset_list_create(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct ripng *ripng; - const char *ifname; - struct ripng_offset_list *offset; - - if (event != NB_EV_APPLY) - return NB_OK; - - ripng = nb_running_get_entry(dnode, NULL, true); - ifname = yang_dnode_get_string(dnode, "./interface"); - - offset = ripng_offset_list_new(ripng, ifname); - nb_running_set_entry(dnode, offset); - - return NB_OK; -} - -static int ripngd_instance_offset_list_destroy(enum nb_event event, - const struct lyd_node *dnode) -{ - int direct; - struct ripng_offset_list *offset; - - if (event != NB_EV_APPLY) - return NB_OK; - - direct = yang_dnode_get_enum(dnode, "./direction"); - - offset = nb_running_unset_entry(dnode); - if (offset->direct[direct].alist_name) { - free(offset->direct[direct].alist_name); - offset->direct[direct].alist_name = NULL; - } - if (offset->direct[RIPNG_OFFSET_LIST_IN].alist_name == NULL - && offset->direct[RIPNG_OFFSET_LIST_OUT].alist_name == NULL) - ripng_offset_list_del(offset); - - return NB_OK; -} - -/* - * XPath: /frr-ripngd:ripngd/instance/offset-list/access-list - */ -static int -ripngd_instance_offset_list_access_list_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - int direct; - struct ripng_offset_list *offset; - const char *alist_name; - - if (event != NB_EV_APPLY) - return NB_OK; - - direct = yang_dnode_get_enum(dnode, "../direction"); - alist_name = yang_dnode_get_string(dnode, NULL); - - offset = nb_running_get_entry(dnode, NULL, true); - if (offset->direct[direct].alist_name) - free(offset->direct[direct].alist_name); - offset->direct[direct].alist_name = strdup(alist_name); - - return NB_OK; -} - -/* - * XPath: /frr-ripngd:ripngd/instance/offset-list/metric - */ -static int -ripngd_instance_offset_list_metric_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - int direct; - uint8_t metric; - struct ripng_offset_list *offset; - - if (event != NB_EV_APPLY) - return NB_OK; - - direct = yang_dnode_get_enum(dnode, "../direction"); - metric = yang_dnode_get_uint8(dnode, NULL); - - offset = nb_running_get_entry(dnode, NULL, true); - offset->direct[direct].metric = metric; - - return NB_OK; -} - -/* - * XPath: /frr-ripngd:ripngd/instance/passive-interface - */ -static int -ripngd_instance_passive_interface_create(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct ripng *ripng; - const char *ifname; - - if (event != NB_EV_APPLY) - return NB_OK; - - ripng = nb_running_get_entry(dnode, NULL, true); - ifname = yang_dnode_get_string(dnode, NULL); - - return ripng_passive_interface_set(ripng, ifname); -} - -static int -ripngd_instance_passive_interface_destroy(enum nb_event event, - const struct lyd_node *dnode) -{ - struct ripng *ripng; - const char *ifname; - - if (event != NB_EV_APPLY) - return NB_OK; - - ripng = nb_running_get_entry(dnode, NULL, true); - ifname = yang_dnode_get_string(dnode, NULL); - - return ripng_passive_interface_unset(ripng, ifname); -} - -/* - * XPath: /frr-ripngd:ripngd/instance/redistribute - */ -static int ripngd_instance_redistribute_create(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct ripng *ripng; - int type; - - if (event != NB_EV_APPLY) - return NB_OK; - - ripng = nb_running_get_entry(dnode, NULL, true); - type = yang_dnode_get_enum(dnode, "./protocol"); - - ripng->redist[type].enabled = true; - - return NB_OK; -} - -static int ripngd_instance_redistribute_destroy(enum nb_event event, - const struct lyd_node *dnode) -{ - struct ripng *ripng; - int type; - - if (event != NB_EV_APPLY) - return NB_OK; - - ripng = nb_running_get_entry(dnode, NULL, true); - type = yang_dnode_get_enum(dnode, "./protocol"); - - ripng->redist[type].enabled = false; - if (ripng->redist[type].route_map.name) { - free(ripng->redist[type].route_map.name); - ripng->redist[type].route_map.name = NULL; - ripng->redist[type].route_map.map = NULL; - } - ripng->redist[type].metric_config = false; - ripng->redist[type].metric = 0; - - if (ripng->enabled) - ripng_redistribute_conf_delete(ripng, type); - - return NB_OK; -} - -static void -ripngd_instance_redistribute_apply_finish(const struct lyd_node *dnode) -{ - struct ripng *ripng; - int type; - - ripng = nb_running_get_entry(dnode, NULL, true); - type = yang_dnode_get_enum(dnode, "./protocol"); - - if (ripng->enabled) - ripng_redistribute_conf_update(ripng, type); -} - -/* - * XPath: /frr-ripngd:ripngd/instance/redistribute/route-map - */ -static int -ripngd_instance_redistribute_route_map_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct ripng *ripng; - int type; - const char *rmap_name; - - if (event != NB_EV_APPLY) - return NB_OK; - - ripng = nb_running_get_entry(dnode, NULL, true); - type = yang_dnode_get_enum(dnode, "../protocol"); - rmap_name = yang_dnode_get_string(dnode, NULL); - - if (ripng->redist[type].route_map.name) - free(ripng->redist[type].route_map.name); - ripng->redist[type].route_map.name = strdup(rmap_name); - ripng->redist[type].route_map.map = route_map_lookup_by_name(rmap_name); - - return NB_OK; -} - -static int -ripngd_instance_redistribute_route_map_destroy(enum nb_event event, - const struct lyd_node *dnode) -{ - struct ripng *ripng; - int type; - - if (event != NB_EV_APPLY) - return NB_OK; - - ripng = nb_running_get_entry(dnode, NULL, true); - type = yang_dnode_get_enum(dnode, "../protocol"); - - free(ripng->redist[type].route_map.name); - ripng->redist[type].route_map.name = NULL; - ripng->redist[type].route_map.map = NULL; - - return NB_OK; -} - -/* - * XPath: /frr-ripngd:ripngd/instance/redistribute/metric - */ -static int -ripngd_instance_redistribute_metric_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct ripng *ripng; - int type; - uint8_t metric; - - if (event != NB_EV_APPLY) - return NB_OK; - - ripng = nb_running_get_entry(dnode, NULL, true); - type = yang_dnode_get_enum(dnode, "../protocol"); - metric = yang_dnode_get_uint8(dnode, NULL); - - ripng->redist[type].metric_config = true; - ripng->redist[type].metric = metric; - - return NB_OK; -} - -static int -ripngd_instance_redistribute_metric_destroy(enum nb_event event, - const struct lyd_node *dnode) -{ - struct ripng *ripng; - int type; - - if (event != NB_EV_APPLY) - return NB_OK; - - ripng = nb_running_get_entry(dnode, NULL, true); - type = yang_dnode_get_enum(dnode, "../protocol"); - - ripng->redist[type].metric_config = false; - ripng->redist[type].metric = 0; - - return NB_OK; -} - -/* - * XPath: /frr-ripngd:ripngd/instance/static-route - */ -static int ripngd_instance_static_route_create(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct ripng *ripng; - struct prefix_ipv6 p; - - if (event != NB_EV_APPLY) - return NB_OK; - - ripng = nb_running_get_entry(dnode, NULL, true); - yang_dnode_get_ipv6p(&p, dnode, NULL); - apply_mask_ipv6(&p); - - ripng_redistribute_add(ripng, ZEBRA_ROUTE_RIPNG, RIPNG_ROUTE_STATIC, &p, - 0, NULL, 0); - - return NB_OK; -} - -static int ripngd_instance_static_route_destroy(enum nb_event event, - const struct lyd_node *dnode) -{ - struct ripng *ripng; - struct prefix_ipv6 p; - - if (event != NB_EV_APPLY) - return NB_OK; - - ripng = nb_running_get_entry(dnode, NULL, true); - yang_dnode_get_ipv6p(&p, dnode, NULL); - apply_mask_ipv6(&p); - - ripng_redistribute_delete(ripng, ZEBRA_ROUTE_RIPNG, RIPNG_ROUTE_STATIC, - &p, 0); - - return NB_OK; -} - -/* - * XPath: /frr-ripngd:ripngd/instance/aggregate-address - */ -static int -ripngd_instance_aggregate_address_create(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct ripng *ripng; - struct prefix_ipv6 p; - - if (event != NB_EV_APPLY) - return NB_OK; - - ripng = nb_running_get_entry(dnode, NULL, true); - yang_dnode_get_ipv6p(&p, dnode, NULL); - apply_mask_ipv6(&p); - - ripng_aggregate_add(ripng, (struct prefix *)&p); - - return NB_OK; -} - -static int -ripngd_instance_aggregate_address_destroy(enum nb_event event, - const struct lyd_node *dnode) -{ - struct ripng *ripng; - struct prefix_ipv6 p; - - if (event != NB_EV_APPLY) - return NB_OK; - - ripng = nb_running_get_entry(dnode, NULL, true); - yang_dnode_get_ipv6p(&p, dnode, NULL); - apply_mask_ipv6(&p); - - ripng_aggregate_delete(ripng, (struct prefix *)&p); - - return NB_OK; -} - -/* - * XPath: /frr-ripngd:ripngd/instance/timers - */ -static void ripngd_instance_timers_apply_finish(const struct lyd_node *dnode) -{ - struct ripng *ripng; - - ripng = nb_running_get_entry(dnode, NULL, true); - - /* Reset update timer thread. */ - ripng_event(ripng, RIPNG_UPDATE_EVENT, 0); -} - -/* - * XPath: /frr-ripngd:ripngd/instance/timers/flush-interval - */ -static int -ripngd_instance_timers_flush_interval_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct ripng *ripng; - - if (event != NB_EV_APPLY) - return NB_OK; - - ripng = nb_running_get_entry(dnode, NULL, true); - ripng->garbage_time = yang_dnode_get_uint16(dnode, NULL); - - return NB_OK; -} - -/* - * XPath: /frr-ripngd:ripngd/instance/timers/holddown-interval - */ -static int -ripngd_instance_timers_holddown_interval_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct ripng *ripng; - - if (event != NB_EV_APPLY) - return NB_OK; - - ripng = nb_running_get_entry(dnode, NULL, true); - ripng->timeout_time = yang_dnode_get_uint16(dnode, NULL); - - return NB_OK; -} - -/* - * XPath: /frr-ripngd:ripngd/instance/timers/update-interval - */ -static int -ripngd_instance_timers_update_interval_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct ripng *ripng; - - if (event != NB_EV_APPLY) - return NB_OK; - - ripng = nb_running_get_entry(dnode, NULL, true); - ripng->update_time = yang_dnode_get_uint16(dnode, NULL); - - return NB_OK; -} - -/* - * XPath: /frr-ripngd:ripngd/instance/state/neighbors/neighbor - */ -static const void * -ripngd_instance_state_neighbors_neighbor_get_next(const void *parent_list_entry, - const void *list_entry) -{ - const struct ripng *ripng = parent_list_entry; - struct listnode *node; - - if (list_entry == NULL) - node = listhead(ripng->peer_list); - else - node = listnextnode((struct listnode *)list_entry); - - return node; -} - -static int -ripngd_instance_state_neighbors_neighbor_get_keys(const void *list_entry, - struct yang_list_keys *keys) -{ - const struct listnode *node = list_entry; - const struct ripng_peer *peer = listgetdata(node); - - keys->num = 1; - (void)inet_ntop(AF_INET6, &peer->addr, keys->key[0], - sizeof(keys->key[0])); - - return NB_OK; -} - -static const void *ripngd_instance_state_neighbors_neighbor_lookup_entry( - const void *parent_list_entry, const struct yang_list_keys *keys) -{ - const struct ripng *ripng = parent_list_entry; - struct in6_addr address; - struct ripng_peer *peer; - struct listnode *node; - - yang_str2ipv6(keys->key[0], &address); - - for (ALL_LIST_ELEMENTS_RO(ripng->peer_list, node, peer)) { - if (IPV6_ADDR_SAME(&peer->addr, &address)) - return node; - } - - return NULL; -} - -/* - * XPath: /frr-ripngd:ripngd/instance/state/neighbors/neighbor/address - */ -static struct yang_data * -ripngd_instance_state_neighbors_neighbor_address_get_elem( - const char *xpath, const void *list_entry) -{ - const struct listnode *node = list_entry; - const struct ripng_peer *peer = listgetdata(node); - - return yang_data_new_ipv6(xpath, &peer->addr); -} - -/* - * XPath: /frr-ripngd:ripngd/instance/state/neighbors/neighbor/last-update - */ -static struct yang_data * -ripngd_instance_state_neighbors_neighbor_last_update_get_elem( - const char *xpath, const void *list_entry) -{ - /* TODO: yang:date-and-time is tricky */ - return NULL; -} - -/* - * XPath: /frr-ripngd:ripngd/instance/state/neighbors/neighbor/bad-packets-rcvd - */ -static struct yang_data * -ripngd_instance_state_neighbors_neighbor_bad_packets_rcvd_get_elem( - const char *xpath, const void *list_entry) -{ - const struct listnode *node = list_entry; - const struct ripng_peer *peer = listgetdata(node); - - return yang_data_new_uint32(xpath, peer->recv_badpackets); -} - -/* - * XPath: /frr-ripngd:ripngd/instance/state/neighbors/neighbor/bad-routes-rcvd - */ -static struct yang_data * -ripngd_instance_state_neighbors_neighbor_bad_routes_rcvd_get_elem( - const char *xpath, const void *list_entry) -{ - const struct listnode *node = list_entry; - const struct ripng_peer *peer = listgetdata(node); - - return yang_data_new_uint32(xpath, peer->recv_badroutes); -} - -/* - * XPath: /frr-ripngd:ripngd/instance/state/routes/route - */ -static const void * -ripngd_instance_state_routes_route_get_next(const void *parent_list_entry, - const void *list_entry) -{ - const struct ripng *ripng = parent_list_entry; - struct agg_node *rn; - - if (list_entry == NULL) - rn = agg_route_top(ripng->table); - else - rn = agg_route_next((struct agg_node *)list_entry); - while (rn && rn->info == NULL) - rn = agg_route_next(rn); - - return rn; -} - -static int -ripngd_instance_state_routes_route_get_keys(const void *list_entry, - struct yang_list_keys *keys) -{ - const struct agg_node *rn = list_entry; - - keys->num = 1; - (void)prefix2str(&rn->p, keys->key[0], sizeof(keys->key[0])); - - return NB_OK; -} - -static const void *ripngd_instance_state_routes_route_lookup_entry( - const void *parent_list_entry, const struct yang_list_keys *keys) -{ - const struct ripng *ripng = parent_list_entry; - struct prefix prefix; - struct agg_node *rn; - - yang_str2ipv6p(keys->key[0], &prefix); - - rn = agg_node_lookup(ripng->table, &prefix); - if (!rn || !rn->info) - return NULL; - - agg_unlock_node(rn); - - return rn; -} - -/* - * XPath: /frr-ripngd:ripngd/instance/state/routes/route/prefix - */ -static struct yang_data * -ripngd_instance_state_routes_route_prefix_get_elem(const char *xpath, - const void *list_entry) -{ - const struct agg_node *rn = list_entry; - const struct ripng_info *rinfo = listnode_head(rn->info); - - return yang_data_new_ipv6p(xpath, &rinfo->rp->p); -} - -/* - * XPath: /frr-ripngd:ripngd/instance/state/routes/route/next-hop - */ -static struct yang_data * -ripngd_instance_state_routes_route_next_hop_get_elem(const char *xpath, - const void *list_entry) -{ - const struct agg_node *rn = list_entry; - const struct ripng_info *rinfo = listnode_head(rn->info); - - return yang_data_new_ipv6(xpath, &rinfo->nexthop); -} - -/* - * XPath: /frr-ripngd:ripngd/instance/state/routes/route/interface - */ -static struct yang_data * -ripngd_instance_state_routes_route_interface_get_elem(const char *xpath, - const void *list_entry) -{ - const struct agg_node *rn = list_entry; - const struct ripng_info *rinfo = listnode_head(rn->info); - const struct ripng *ripng = ripng_info_get_instance(rinfo); - - return yang_data_new_string( - xpath, ifindex2ifname(rinfo->ifindex, ripng->vrf->vrf_id)); -} - -/* - * XPath: /frr-ripngd:ripngd/instance/state/routes/route/metric - */ -static struct yang_data * -ripngd_instance_state_routes_route_metric_get_elem(const char *xpath, - const void *list_entry) -{ - const struct agg_node *rn = list_entry; - const struct ripng_info *rinfo = listnode_head(rn->info); - - return yang_data_new_uint8(xpath, rinfo->metric); -} - -/* - * XPath: /frr-ripngd:clear-ripng-route - */ -static void clear_ripng_route(struct ripng *ripng) -{ - struct agg_node *rp; - - if (IS_RIPNG_DEBUG_EVENT) - zlog_debug("Clearing all RIPng routes (VRF %s)", - ripng->vrf_name); - - /* Clear received RIPng routes */ - for (rp = agg_route_top(ripng->table); rp; rp = agg_route_next(rp)) { - struct list *list; - struct listnode *listnode; - struct ripng_info *rinfo; - - list = rp->info; - if (list == NULL) - continue; - - for (ALL_LIST_ELEMENTS_RO(list, listnode, rinfo)) { - if (!ripng_route_rte(rinfo)) - continue; - - if (CHECK_FLAG(rinfo->flags, RIPNG_RTF_FIB)) - ripng_zebra_ipv6_delete(ripng, rp); - break; - } - - if (rinfo) { - RIPNG_TIMER_OFF(rinfo->t_timeout); - RIPNG_TIMER_OFF(rinfo->t_garbage_collect); - listnode_delete(list, rinfo); - ripng_info_free(rinfo); - } - - if (list_isempty(list)) { - list_delete(&list); - rp->info = NULL; - agg_unlock_node(rp); - } - } -} - -static int clear_ripng_route_rpc(const char *xpath, const struct list *input, - struct list *output) -{ - struct ripng *ripng; - struct yang_data *yang_vrf; - - yang_vrf = yang_data_list_find(input, "%s/%s", xpath, "input/vrf"); - if (yang_vrf) { - ripng = ripng_lookup_by_vrf_name(yang_vrf->value); - if (ripng) - clear_ripng_route(ripng); - } else { - struct vrf *vrf; - - RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { - ripng = vrf->info; - if (!ripng) - continue; - - clear_ripng_route(ripng); - } - } - - return NB_OK; -} - -/* - * XPath: /frr-interface:lib/interface/frr-ripngd:ripng/split-horizon - */ -static int -lib_interface_ripng_split_horizon_modify(enum nb_event event, - const struct lyd_node *dnode, - union nb_resource *resource) -{ - struct interface *ifp; - struct ripng_interface *ri; - - if (event != NB_EV_APPLY) - return NB_OK; - - ifp = nb_running_get_entry(dnode, NULL, true); - ri = ifp->info; - ri->split_horizon = yang_dnode_get_enum(dnode, NULL); - - return NB_OK; -} - -/* clang-format off */ -const struct frr_yang_module_info frr_ripngd_info = { - .name = "frr-ripngd", - .nodes = { - { - .xpath = "/frr-ripngd:ripngd/instance", - .cbs.create = ripngd_instance_create, - .cbs.destroy = ripngd_instance_destroy, - .cbs.get_next = ripngd_instance_get_next, - .cbs.get_keys = ripngd_instance_get_keys, - .cbs.lookup_entry = ripngd_instance_lookup_entry, - .cbs.cli_show = cli_show_router_ripng, - }, - { - .xpath = "/frr-ripngd:ripngd/instance/allow-ecmp", - .cbs.modify = ripngd_instance_allow_ecmp_modify, - .cbs.cli_show = cli_show_ripng_allow_ecmp, - }, - { - .xpath = "/frr-ripngd:ripngd/instance/default-information-originate", - .cbs.modify = ripngd_instance_default_information_originate_modify, - .cbs.cli_show = cli_show_ripng_default_information_originate, - }, - { - .xpath = "/frr-ripngd:ripngd/instance/default-metric", - .cbs.modify = ripngd_instance_default_metric_modify, - .cbs.cli_show = cli_show_ripng_default_metric, - }, - { - .xpath = "/frr-ripngd:ripngd/instance/network", - .cbs.create = ripngd_instance_network_create, - .cbs.destroy = ripngd_instance_network_destroy, - .cbs.cli_show = cli_show_ripng_network_prefix, - }, - { - .xpath = "/frr-ripngd:ripngd/instance/interface", - .cbs.create = ripngd_instance_interface_create, - .cbs.destroy = ripngd_instance_interface_destroy, - .cbs.cli_show = cli_show_ripng_network_interface, - }, - { - .xpath = "/frr-ripngd:ripngd/instance/offset-list", - .cbs.create = ripngd_instance_offset_list_create, - .cbs.destroy = ripngd_instance_offset_list_destroy, - .cbs.cli_show = cli_show_ripng_offset_list, - }, - { - .xpath = "/frr-ripngd:ripngd/instance/offset-list/access-list", - .cbs.modify = ripngd_instance_offset_list_access_list_modify, - }, - { - .xpath = "/frr-ripngd:ripngd/instance/offset-list/metric", - .cbs.modify = ripngd_instance_offset_list_metric_modify, - }, - { - .xpath = "/frr-ripngd:ripngd/instance/passive-interface", - .cbs.create = ripngd_instance_passive_interface_create, - .cbs.destroy = ripngd_instance_passive_interface_destroy, - .cbs.cli_show = cli_show_ripng_passive_interface, - }, - { - .xpath = "/frr-ripngd:ripngd/instance/redistribute", - .cbs.create = ripngd_instance_redistribute_create, - .cbs.destroy = ripngd_instance_redistribute_destroy, - .cbs.apply_finish = ripngd_instance_redistribute_apply_finish, - .cbs.cli_show = cli_show_ripng_redistribute, - }, - { - .xpath = "/frr-ripngd:ripngd/instance/redistribute/route-map", - .cbs.modify = ripngd_instance_redistribute_route_map_modify, - .cbs.destroy = ripngd_instance_redistribute_route_map_destroy, - }, - { - .xpath = "/frr-ripngd:ripngd/instance/redistribute/metric", - .cbs.modify = ripngd_instance_redistribute_metric_modify, - .cbs.destroy = ripngd_instance_redistribute_metric_destroy, - }, - { - .xpath = "/frr-ripngd:ripngd/instance/static-route", - .cbs.create = ripngd_instance_static_route_create, - .cbs.destroy = ripngd_instance_static_route_destroy, - .cbs.cli_show = cli_show_ripng_route, - }, - { - .xpath = "/frr-ripngd:ripngd/instance/aggregate-address", - .cbs.create = ripngd_instance_aggregate_address_create, - .cbs.destroy = ripngd_instance_aggregate_address_destroy, - .cbs.cli_show = cli_show_ripng_aggregate_address, - }, - { - .xpath = "/frr-ripngd:ripngd/instance/timers", - .cbs.apply_finish = ripngd_instance_timers_apply_finish, - .cbs.cli_show = cli_show_ripng_timers, - }, - { - .xpath = "/frr-ripngd:ripngd/instance/timers/flush-interval", - .cbs.modify = ripngd_instance_timers_flush_interval_modify, - }, - { - .xpath = "/frr-ripngd:ripngd/instance/timers/holddown-interval", - .cbs.modify = ripngd_instance_timers_holddown_interval_modify, - }, - { - .xpath = "/frr-ripngd:ripngd/instance/timers/update-interval", - .cbs.modify = ripngd_instance_timers_update_interval_modify, - }, - { - .xpath = "/frr-ripngd:ripngd/instance/state/neighbors/neighbor", - .cbs.get_next = ripngd_instance_state_neighbors_neighbor_get_next, - .cbs.get_keys = ripngd_instance_state_neighbors_neighbor_get_keys, - .cbs.lookup_entry = ripngd_instance_state_neighbors_neighbor_lookup_entry, - }, - { - .xpath = "/frr-ripngd:ripngd/instance/state/neighbors/neighbor/address", - .cbs.get_elem = ripngd_instance_state_neighbors_neighbor_address_get_elem, - }, - { - .xpath = "/frr-ripngd:ripngd/instance/state/neighbors/neighbor/last-update", - .cbs.get_elem = ripngd_instance_state_neighbors_neighbor_last_update_get_elem, - }, - { - .xpath = "/frr-ripngd:ripngd/instance/state/neighbors/neighbor/bad-packets-rcvd", - .cbs.get_elem = ripngd_instance_state_neighbors_neighbor_bad_packets_rcvd_get_elem, - }, - { - .xpath = "/frr-ripngd:ripngd/instance/state/neighbors/neighbor/bad-routes-rcvd", - .cbs.get_elem = ripngd_instance_state_neighbors_neighbor_bad_routes_rcvd_get_elem, - }, - { - .xpath = "/frr-ripngd:ripngd/instance/state/routes/route", - .cbs.get_next = ripngd_instance_state_routes_route_get_next, - .cbs.get_keys = ripngd_instance_state_routes_route_get_keys, - .cbs.lookup_entry = ripngd_instance_state_routes_route_lookup_entry, - }, - { - .xpath = "/frr-ripngd:ripngd/instance/state/routes/route/prefix", - .cbs.get_elem = ripngd_instance_state_routes_route_prefix_get_elem, - }, - { - .xpath = "/frr-ripngd:ripngd/instance/state/routes/route/next-hop", - .cbs.get_elem = ripngd_instance_state_routes_route_next_hop_get_elem, - }, - { - .xpath = "/frr-ripngd:ripngd/instance/state/routes/route/interface", - .cbs.get_elem = ripngd_instance_state_routes_route_interface_get_elem, - }, - { - .xpath = "/frr-ripngd:ripngd/instance/state/routes/route/metric", - .cbs.get_elem = ripngd_instance_state_routes_route_metric_get_elem, - }, - { - .xpath = "/frr-ripngd:clear-ripng-route", - .cbs.rpc = clear_ripng_route_rpc, - }, - { - .xpath = "/frr-interface:lib/interface/frr-ripngd:ripng/split-horizon", - .cbs.modify = lib_interface_ripng_split_horizon_modify, - .cbs.cli_show = cli_show_ipv6_ripng_split_horizon, - }, - { - .xpath = NULL, - }, - } -}; diff --git a/ripngd/ripng_offset.c b/ripngd/ripng_offset.c index fe95ccfc2b..0094c993ad 100644 --- a/ripngd/ripng_offset.c +++ b/ripngd/ripng_offset.c @@ -33,6 +33,8 @@ #include "ripngd/ripngd.h" +DEFINE_MTYPE_STATIC(RIPNGD, RIPNG_OFFSET_LIST, "RIPng offset lst") + #define OFFSET_LIST_IN_NAME(O) ((O)->direct[RIPNG_OFFSET_LIST_IN].alist_name) #define OFFSET_LIST_IN_METRIC(O) ((O)->direct[RIPNG_OFFSET_LIST_IN].metric) diff --git a/ripngd/ripng_peer.c b/ripngd/ripng_peer.c index 5376007747..e6ff58dd0c 100644 --- a/ripngd/ripng_peer.c +++ b/ripngd/ripng_peer.c @@ -34,6 +34,8 @@ #include "ripngd/ripngd.h" #include "ripngd/ripng_nexthop.h" +DEFINE_MTYPE_STATIC(RIPNGD, RIPNG_PEER, "RIPng peer") + static struct ripng_peer *ripng_peer_new(void) { return XCALLOC(MTYPE_RIPNG_PEER, sizeof(struct ripng_peer)); @@ -139,7 +141,6 @@ void ripng_peer_bad_packet(struct ripng *ripng, struct sockaddr_in6 *from) static char *ripng_peer_uptime(struct ripng_peer *peer, char *buf, size_t len) { time_t uptime; - struct tm *tm; /* If there is no connection has been done before print `never'. */ if (peer->uptime == 0) { @@ -150,17 +151,9 @@ static char *ripng_peer_uptime(struct ripng_peer *peer, char *buf, size_t len) /* Get current time. */ uptime = time(NULL); uptime -= peer->uptime; - tm = gmtime(&uptime); - - if (uptime < ONE_DAY_SECOND) - snprintf(buf, len, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min, - tm->tm_sec); - else if (uptime < ONE_WEEK_SECOND) - snprintf(buf, len, "%dd%02dh%02dm", tm->tm_yday, tm->tm_hour, - tm->tm_min); - else - snprintf(buf, len, "%02dw%dd%02dh", tm->tm_yday / 7, - tm->tm_yday - ((tm->tm_yday / 7) * 7), tm->tm_hour); + + frrtime_to_interval(uptime, buf, len); + return buf; } diff --git a/ripngd/ripng_route.c b/ripngd/ripng_route.c index 1bf1007fec..ed9d77a378 100644 --- a/ripngd/ripng_route.c +++ b/ripngd/ripng_route.c @@ -30,6 +30,8 @@ #include "ripngd/ripngd.h" #include "ripngd/ripng_route.h" +DEFINE_MTYPE_STATIC(RIPNGD, RIPNG_AGGREGATE, "RIPng aggregate") + static struct ripng_aggregate *ripng_aggregate_new(void) { struct ripng_aggregate *new; diff --git a/ripngd/ripng_routemap.c b/ripngd/ripng_routemap.c index 0604e272cd..b5f80d2ab0 100644 --- a/ripngd/ripng_routemap.c +++ b/ripngd/ripng_routemap.c @@ -38,10 +38,9 @@ struct rip_metric_modifier { /* `match metric METRIC' */ /* Match function return 1 if match is success else return zero. */ -static route_map_result_t route_match_metric(void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_match_metric(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { uint32_t *metric; struct ripng_info *rinfo; @@ -80,16 +79,18 @@ static void route_match_metric_free(void *rule) } /* Route map commands for metric matching. */ -static struct route_map_rule_cmd route_match_metric_cmd = { - "metric", route_match_metric, route_match_metric_compile, - route_match_metric_free}; +static const struct route_map_rule_cmd route_match_metric_cmd = { + "metric", + route_match_metric, + route_match_metric_compile, + route_match_metric_free +}; /* `match interface IFNAME' */ /* Match function return 1 if match is success else return zero. */ -static route_map_result_t route_match_interface(void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_match_interface(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { struct ripng_info *rinfo; struct interface *ifp; @@ -123,15 +124,19 @@ static void route_match_interface_free(void *rule) XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); } -static struct route_map_rule_cmd route_match_interface_cmd = { - "interface", route_match_interface, route_match_interface_compile, - route_match_interface_free}; +static const struct route_map_rule_cmd route_match_interface_cmd = { + "interface", + route_match_interface, + route_match_interface_compile, + route_match_interface_free +}; /* `match tag TAG' */ /* Match function return 1 if match is success else return zero. */ -static route_map_result_t route_match_tag(void *rule, - const struct prefix *prefix, - route_map_object_t type, void *object) +static enum route_map_cmd_result_t route_match_tag(void *rule, + const struct prefix *prefix, + route_map_object_t type, + void *object) { route_tag_t *tag; struct ripng_info *rinfo; @@ -151,18 +156,19 @@ static route_map_result_t route_match_tag(void *rule, return RMAP_NOMATCH; } -static struct route_map_rule_cmd route_match_tag_cmd = { - "tag", route_match_tag, route_map_rule_tag_compile, +static const struct route_map_rule_cmd route_match_tag_cmd = { + "tag", + route_match_tag, + route_map_rule_tag_compile, route_map_rule_tag_free, }; /* `set metric METRIC' */ /* Set metric to attribute. */ -static route_map_result_t route_set_metric(void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_set_metric(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { if (type == RMAP_RIPNG) { struct rip_metric_modifier *mod; @@ -231,9 +237,9 @@ static void *route_set_metric_compile(const char *arg) return mod; if (metric > RIPNG_METRIC_INFINITY) { - zlog_info("%s: Metric specified: %ld is being converted into METRIC_INFINITY", - __PRETTY_FUNCTION__, - metric); + zlog_info( + "%s: Metric specified: %ld is being converted into METRIC_INFINITY", + __func__, metric); mod->metric = RIPNG_METRIC_INFINITY; } else mod->metric = metric; @@ -248,18 +254,19 @@ static void route_set_metric_free(void *rule) XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); } -static struct route_map_rule_cmd route_set_metric_cmd = { - "metric", route_set_metric, route_set_metric_compile, +static const struct route_map_rule_cmd route_set_metric_cmd = { + "metric", + route_set_metric, + route_set_metric_compile, route_set_metric_free, }; /* `set ipv6 next-hop local IP_ADDRESS' */ /* Set nexthop to object. ojbect must be pointer to struct attr. */ -static route_map_result_t route_set_ipv6_nexthop_local(void *rule, - const struct prefix *p, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_set_ipv6_nexthop_local(void *rule, const struct prefix *p, + route_map_object_t type, void *object) { struct in6_addr *address; struct ripng_info *rinfo; @@ -302,17 +309,20 @@ static void route_set_ipv6_nexthop_local_free(void *rule) } /* Route map commands for ipv6 nexthop local set. */ -static struct route_map_rule_cmd route_set_ipv6_nexthop_local_cmd = { - "ipv6 next-hop local", route_set_ipv6_nexthop_local, +static const struct route_map_rule_cmd + route_set_ipv6_nexthop_local_cmd = { + "ipv6 next-hop local", + route_set_ipv6_nexthop_local, route_set_ipv6_nexthop_local_compile, - route_set_ipv6_nexthop_local_free}; + route_set_ipv6_nexthop_local_free +}; /* `set tag TAG' */ /* Set tag to object. ojbect must be pointer to struct attr. */ -static route_map_result_t route_set_tag(void *rule, - const struct prefix *prefix, - route_map_object_t type, void *object) +static enum route_map_cmd_result_t +route_set_tag(void *rule, const struct prefix *prefix, route_map_object_t type, + void *object) { route_tag_t *tag; struct ripng_info *rinfo; @@ -330,9 +340,12 @@ static route_map_result_t route_set_tag(void *rule, } /* Route map commands for tag set. */ -static struct route_map_rule_cmd route_set_tag_cmd = { - "tag", route_set_tag, route_map_rule_tag_compile, - route_map_rule_tag_free}; +static const struct route_map_rule_cmd route_set_tag_cmd = { + "tag", + route_set_tag, + route_map_rule_tag_compile, + route_map_rule_tag_free +}; #define MATCH_STR "Match values from routing table\n" #define SET_STR "Set values in destination routing protocol\n" diff --git a/ripngd/ripng_zebra.c b/ripngd/ripng_zebra.c index cf60de2de9..baf7f00961 100644 --- a/ripngd/ripng_zebra.c +++ b/ripngd/ripng_zebra.c @@ -46,12 +46,13 @@ static void ripng_zebra_ipv6_send(struct ripng *ripng, struct agg_node *rp, struct listnode *listnode = NULL; struct ripng_info *rinfo = NULL; int count = 0; + const struct prefix *p = agg_node_get_prefix(rp); memset(&api, 0, sizeof(api)); api.vrf_id = ripng->vrf->vrf_id; api.type = ZEBRA_ROUTE_RIPNG; api.safi = SAFI_UNICAST; - api.prefix = rp->p; + api.prefix = *p; SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP); for (ALL_LIST_ELEMENTS_RO(list, listnode, rinfo)) { @@ -85,18 +86,17 @@ static void ripng_zebra_ipv6_send(struct ripng *ripng, struct agg_node *rp, if (IS_RIPNG_DEBUG_ZEBRA) { if (ripng->ecmp) - zlog_debug("%s: %s/%d nexthops %d", + zlog_debug("%s: %pRN nexthops %d", (cmd == ZEBRA_ROUTE_ADD) ? "Install into zebra" : "Delete from zebra", - inet6_ntoa(rp->p.u.prefix6), rp->p.prefixlen, - count); + rp, count); else - zlog_debug( - "%s: %s/%d", - (cmd == ZEBRA_ROUTE_ADD) ? "Install into zebra" - : "Delete from zebra", - inet6_ntoa(rp->p.u.prefix6), rp->p.prefixlen); + zlog_debug("%s: %pRN", + (cmd == ZEBRA_ROUTE_ADD) + ? "Install into zebra" + : "Delete from zebra", + rp); } } @@ -113,8 +113,7 @@ void ripng_zebra_ipv6_delete(struct ripng *ripng, struct agg_node *rp) } /* Zebra route add and delete treatment. */ -static int ripng_zebra_read_route(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int ripng_zebra_read_route(ZAPI_CALLBACK_ARGS) { struct ripng *ripng; struct zapi_route api; @@ -138,7 +137,7 @@ static int ripng_zebra_read_route(int command, struct zclient *zclient, nexthop = api.nexthops[0].gate.ipv6; ifindex = api.nexthops[0].ifindex; - if (command == ZEBRA_REDISTRIBUTE_ROUTE_ADD) + if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD) ripng_redistribute_add(ripng, api.type, RIPNG_ROUTE_REDISTRIBUTE, (struct prefix_ipv6 *)&api.prefix, @@ -153,8 +152,8 @@ static int ripng_zebra_read_route(int command, struct zclient *zclient, void ripng_redistribute_conf_update(struct ripng *ripng, int type) { - zclient_redistribute(ZEBRA_REDISTRIBUTE_ADD, zclient, AFI_IP6, type, 0, - ripng->vrf->vrf_id); + zebra_redistribute_send(ZEBRA_REDISTRIBUTE_ADD, zclient, AFI_IP6, + type, 0, ripng->vrf->vrf_id); } void ripng_redistribute_conf_delete(struct ripng *ripng, int type) @@ -243,10 +242,6 @@ void zebra_init(struct thread_master *master) zclient_init(zclient, ZEBRA_ROUTE_RIPNG, 0, &ripngd_privs); zclient->zebra_connected = ripng_zebra_connected; - zclient->interface_up = ripng_interface_up; - zclient->interface_down = ripng_interface_down; - zclient->interface_add = ripng_interface_add; - zclient->interface_delete = ripng_interface_delete; zclient->interface_address_add = ripng_interface_address_add; zclient->interface_address_delete = ripng_interface_address_delete; zclient->interface_vrf_update = ripng_interface_vrf_update; diff --git a/ripngd/ripngd.c b/ripngd/ripngd.c index 411689a7a7..c2eb7c6ee4 100644 --- a/ripngd/ripngd.c +++ b/ripngd/ripngd.c @@ -37,12 +37,18 @@ #include "privs.h" #include "lib_errors.h" #include "northbound_cli.h" +#include "network.h" #include "ripngd/ripngd.h" #include "ripngd/ripng_route.h" #include "ripngd/ripng_debug.h" #include "ripngd/ripng_nexthop.h" +DEFINE_MGROUP(RIPNGD, "ripngd") +DEFINE_MTYPE_STATIC(RIPNGD, RIPNG, "RIPng structure") +DEFINE_MTYPE_STATIC(RIPNGD, RIPNG_VRF_NAME, "RIPng VRF name") +DEFINE_MTYPE_STATIC(RIPNGD, RIPNG_ROUTE, "RIPng route info") + enum { ripng_all_route, ripng_changed_route, }; @@ -115,8 +121,7 @@ int ripng_make_socket(struct vrf *vrf) /* Make datagram socket. */ if (vrf->vrf_id != VRF_DEFAULT) vrf_dev = vrf->name; - frr_elevate_privs(&ripngd_privs) - { + frr_with_privs(&ripngd_privs) { sock = vrf_socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP, vrf->vrf_id, vrf_dev); if (sock < 0) { @@ -155,7 +160,7 @@ int ripng_make_socket(struct vrf *vrf) #endif /* SIN6_LEN */ ripaddr.sin6_port = htons(RIPNG_PORT_DEFAULT); - frr_elevate_privs(&ripngd_privs) { + frr_with_privs(&ripngd_privs) { ret = bind(sock, (struct sockaddr *)&ripaddr, sizeof(ripaddr)); if (ret < 0) { zlog_err("Can't bind ripng socket: %s.", @@ -267,7 +272,7 @@ static int ripng_recv_packet(int sock, uint8_t *buf, int bufsize, msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = (void *)adata; - msg.msg_controllen = sizeof adata; + msg.msg_controllen = sizeof(adata); iov.iov_base = buf; iov.iov_len = bufsize; @@ -1042,8 +1047,7 @@ void ripng_redistribute_delete(struct ripng *ripng, int type, int sub_type, if (IS_RIPNG_DEBUG_EVENT) zlog_debug( - "Poisone %s/%d on the interface %s with an " - "infinity metric [delete]", + "Poisone %s/%d on the interface %s with an infinity metric [delete]", inet6_ntoa(p->prefix), p->prefixlen, ifindex2ifname( @@ -1083,7 +1087,8 @@ void ripng_redistribute_withdraw(struct ripng *ripng, int type) if (IS_RIPNG_DEBUG_EVENT) { struct prefix_ipv6 *p = - (struct prefix_ipv6 *)&rp->p; + (struct prefix_ipv6 *) + agg_node_get_prefix(rp); zlog_debug( "Poisone %s/%d on the interface %s [withdraw]", @@ -1451,7 +1456,7 @@ static int ripng_update(struct thread *t) if (ri->passive) continue; -#if RIPNG_ADVANCED +#ifdef RIPNG_ADVANCED if (ri->ri_send == RIPNG_SEND_OFF) { if (IS_RIPNG_DEBUG_EVENT) zlog_debug( @@ -1540,7 +1545,7 @@ int ripng_triggered_update(struct thread *t) random interval between 1 and 5 seconds. If other changes that would trigger updates occur before the timer expires, a single update is triggered when the timer expires. */ - interval = (random() % 5) + 1; + interval = (frr_weak_random() % 5) + 1; ripng->t_triggered_interval = NULL; thread_add_timer(master, ripng_triggered_interval, ripng, interval, @@ -1615,7 +1620,7 @@ void ripng_output_process(struct interface *ifp, struct sockaddr_in6 *to, * following * information. */ - p = (struct prefix_ipv6 *)&rp->p; + p = (struct prefix_ipv6 *)agg_node_get_prefix(rp); rinfo->metric_out = rinfo->metric; rinfo->tag_out = rinfo->tag; memset(&rinfo->nexthop_out, 0, @@ -1757,7 +1762,7 @@ void ripng_output_process(struct interface *ifp, struct sockaddr_in6 *to, * following * information. */ - p = (struct prefix_ipv6 *)&rp->p; + p = (struct prefix_ipv6 *)agg_node_get_prefix(rp); aggregate->metric_set = 0; aggregate->metric_out = aggregate->metric; aggregate->tag_out = aggregate->tag; @@ -1945,7 +1950,7 @@ int ripng_request(struct interface *ifp) static int ripng_update_jitter(int time) { - return ((random() % (time + 1)) - (time / 2)); + return ((frr_weak_random() % (time + 1)) - (time / 2)); } void ripng_event(struct ripng *ripng, enum ripng_event event, int sock) @@ -1987,20 +1992,20 @@ void ripng_event(struct ripng *ripng, enum ripng_event event, int sock) static void ripng_vty_out_uptime(struct vty *vty, struct ripng_info *rinfo) { time_t clock; - struct tm *tm; + struct tm tm; #define TIME_BUF 25 char timebuf[TIME_BUF]; struct thread *thread; if ((thread = rinfo->t_timeout) != NULL) { clock = thread_timer_remain_second(thread); - tm = gmtime(&clock); - strftime(timebuf, TIME_BUF, "%M:%S", tm); + gmtime_r(&clock, &tm); + strftime(timebuf, TIME_BUF, "%M:%S", &tm); vty_out(vty, "%5s", timebuf); } else if ((thread = rinfo->t_garbage_collect) != NULL) { clock = thread_timer_remain_second(thread); - tm = gmtime(&clock); - strftime(timebuf, TIME_BUF, "%M:%S", tm); + gmtime_r(&clock, &tm); + strftime(timebuf, TIME_BUF, "%M:%S", &tm); vty_out(vty, "%5s", timebuf); } } @@ -2011,26 +2016,26 @@ static char *ripng_route_subtype_print(struct ripng_info *rinfo) memset(str, 0, 3); if (rinfo->suppress) - strcat(str, "S"); + strlcat(str, "S", sizeof(str)); switch (rinfo->sub_type) { case RIPNG_ROUTE_RTE: - strcat(str, "n"); + strlcat(str, "n", sizeof(str)); break; case RIPNG_ROUTE_STATIC: - strcat(str, "s"); + strlcat(str, "s", sizeof(str)); break; case RIPNG_ROUTE_DEFAULT: - strcat(str, "d"); + strlcat(str, "d", sizeof(str)); break; case RIPNG_ROUTE_REDISTRIBUTE: - strcat(str, "r"); + strlcat(str, "r", sizeof(str)); break; case RIPNG_ROUTE_INTERFACE: - strcat(str, "i"); + strlcat(str, "i", sizeof(str)); break; default: - strcat(str, "?"); + strlcat(str, "?", sizeof(str)); break; } @@ -2049,7 +2054,6 @@ DEFUN (show_ipv6_ripng, struct agg_node *rp; struct ripng_info *rinfo; struct ripng_aggregate *aggregate; - struct prefix_ipv6 *p; struct list *list = NULL; struct listnode *listnode = NULL; int len; @@ -2081,15 +2085,11 @@ DEFUN (show_ipv6_ripng, for (rp = agg_route_top(ripng->table); rp; rp = agg_route_next(rp)) { if ((aggregate = rp->aggregate) != NULL) { - p = (struct prefix_ipv6 *)&rp->p; - #ifdef DEBUG - vty_out(vty, "R(a) %d/%d %s/%d ", aggregate->count, - aggregate->suppress, inet6_ntoa(p->prefix), - p->prefixlen); + vty_out(vty, "R(a) %d/%d %pRN ", aggregate->count, + aggregate->suppress, rp); #else - vty_out(vty, "R(a) %s/%d ", inet6_ntoa(p->prefix), - p->prefixlen); + vty_out(vty, "R(a) %pRN ", rp); #endif /* DEBUG */ vty_out(vty, "\n"); vty_out(vty, "%*s", 18, " "); @@ -2101,19 +2101,15 @@ DEFUN (show_ipv6_ripng, if ((list = rp->info) != NULL) for (ALL_LIST_ELEMENTS_RO(list, listnode, rinfo)) { - p = (struct prefix_ipv6 *)&rp->p; - #ifdef DEBUG - vty_out(vty, "%c(%s) 0/%d %s/%d ", + vty_out(vty, "%c(%s) 0/%d %pRN ", zebra_route_char(rinfo->type), ripng_route_subtype_print(rinfo), - rinfo->suppress, inet6_ntoa(p->prefix), - p->prefixlen); + rinfo->suppress, rp); #else - vty_out(vty, "%c(%s) %s/%d ", + vty_out(vty, "%c(%s) %pRN ", zebra_route_char(rinfo->type), - ripng_route_subtype_print(rinfo), - inet6_ntoa(p->prefix), p->prefixlen); + ripng_route_subtype_print(rinfo), rp); #endif /* DEBUG */ vty_out(vty, "\n"); vty_out(vty, "%*s", 18, " "); @@ -2357,7 +2353,7 @@ DEFUN (show_ipv6_protocols, return CMD_SUCCESS; vty_out (vty, "Routing Protocol is \"ripng\"\n"); - + vty_out (vty, "Sending updates every %ld seconds, next due in %d seconds\n", ripng->update_time, 0); @@ -2438,9 +2434,14 @@ static int ripng_config_write(struct vty *vty) return write; } +static int ripng_config_write(struct vty *vty); /* RIPng node structure. */ static struct cmd_node cmd_ripng_node = { - RIPNG_NODE, "%s(config-router)# ", 1, + .name = "ripng", + .node = RIPNG_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-router)# ", + .config_write = ripng_config_write, }; static void ripng_distribute_update(struct distribute_ctx *ctx, @@ -2532,6 +2533,8 @@ static void ripng_distribute_update_all_wrapper(struct access_list *notused) /* delete all the added ripng routes. */ void ripng_clean(struct ripng *ripng) { + ripng_interface_clean(ripng); + if (ripng->enabled) ripng_instance_disable(ripng); @@ -2553,7 +2556,6 @@ void ripng_clean(struct ripng *ripng) agg_table_finish(ripng->enable_network); vector_free(ripng->passive_interface); list_delete(&ripng->offset_list_master); - ripng_interface_clean(ripng); RB_REMOVE(ripng_instance_head, &ripng_instances, ripng); XFREE(MTYPE_RIPNG_VRF_NAME, ripng->vrf_name); @@ -2772,7 +2774,45 @@ static int ripng_vrf_enable(struct vrf *vrf) int socket; ripng = ripng_lookup_by_vrf_name(vrf->name); - if (!ripng || ripng->enabled) + if (!ripng) { + char *old_vrf_name = NULL; + + ripng = (struct ripng *)vrf->info; + if (!ripng) + return 0; + /* update vrf name */ + if (ripng->vrf_name) + old_vrf_name = ripng->vrf_name; + ripng->vrf_name = XSTRDUP(MTYPE_RIPNG_VRF_NAME, vrf->name); + /* + * HACK: Change the RIPng VRF in the running configuration directly, + * bypassing the northbound layer. This is necessary to avoid deleting + * the RIPng and readding it in the new VRF, which would have + * several implications. + */ + 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++; + } + } + XFREE(MTYPE_RIPNG_VRF_NAME, old_vrf_name); + } + + if (ripng->enabled) return 0; if (IS_RIPNG_DEBUG_EVENT) @@ -2780,13 +2820,11 @@ static int ripng_vrf_enable(struct vrf *vrf) vrf->vrf_id); /* Activate the VRF RIPng instance. */ - if (!ripng->enabled) { - socket = ripng_make_socket(vrf); - if (socket < 0) - return -1; + socket = ripng_make_socket(vrf); + if (socket < 0) + return -1; - ripng_instance_enable(ripng, vrf, socket); - } + ripng_instance_enable(ripng, vrf, socket); return 0; } @@ -2813,7 +2851,7 @@ static int ripng_vrf_disable(struct vrf *vrf) void ripng_vrf_init(void) { vrf_init(ripng_vrf_new, ripng_vrf_enable, ripng_vrf_disable, - ripng_vrf_delete, NULL); + ripng_vrf_delete, ripng_vrf_enable); } void ripng_vrf_terminate(void) @@ -2825,7 +2863,7 @@ void ripng_vrf_terminate(void) void ripng_init(void) { /* Install RIPNG_NODE. */ - install_node(&cmd_ripng_node, ripng_config_write); + install_node(&cmd_ripng_node); /* Install ripng commands. */ install_element(VIEW_NODE, &show_ipv6_ripng_cmd); diff --git a/ripngd/ripngd.h b/ripngd/ripngd.h index dc425b6958..70508d5cb0 100644 --- a/ripngd/ripngd.h +++ b/ripngd/ripngd.h @@ -26,8 +26,7 @@ #include #include #include - -#include "ripng_memory.h" +#include /* RIPng version and port number. */ #define RIPNG_V1 1 @@ -87,6 +86,8 @@ #define RIPNG_INSTANCE "/frr-ripngd:ripngd/instance" #define RIPNG_IFACE "/frr-interface:lib/interface/frr-ripngd:ripng" +DECLARE_MGROUP(RIPNGD) + /* RIPng structure. */ struct ripng { RB_ENTRY(ripng) entry; @@ -468,20 +469,13 @@ extern int ripng_send_packet(caddr_t buf, int bufsize, struct sockaddr_in6 *to, extern void ripng_packet_dump(struct ripng_packet *packet, int size, const char *sndrcv); -extern int ripng_interface_up(int command, struct zclient *, zebra_size_t, - vrf_id_t); -extern int ripng_interface_down(int command, struct zclient *, zebra_size_t, - vrf_id_t); -extern int ripng_interface_add(int command, struct zclient *, zebra_size_t, - vrf_id_t); -extern int ripng_interface_delete(int command, struct zclient *, zebra_size_t, - vrf_id_t); -extern int ripng_interface_address_add(int command, struct zclient *, - zebra_size_t, vrf_id_t); -extern int ripng_interface_address_delete(int command, struct zclient *, - zebra_size_t, vrf_id_t); -extern int ripng_interface_vrf_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id); +extern int ripng_interface_up(ZAPI_CALLBACK_ARGS); +extern int ripng_interface_down(ZAPI_CALLBACK_ARGS); +extern int ripng_interface_add(ZAPI_CALLBACK_ARGS); +extern int ripng_interface_delete(ZAPI_CALLBACK_ARGS); +extern int ripng_interface_address_add(ZAPI_CALLBACK_ARGS); +extern int ripng_interface_address_delete(ZAPI_CALLBACK_ARGS); +extern int ripng_interface_vrf_update(ZAPI_CALLBACK_ARGS); extern void ripng_interface_sync(struct interface *ifp); extern struct ripng *ripng_lookup_by_vrf_id(vrf_id_t vrf_id); @@ -500,9 +494,6 @@ extern struct ripng_info *ripng_ecmp_delete(struct ripng *ripng, extern void ripng_vrf_init(void); extern void ripng_vrf_terminate(void); - -/* Northbound. */ extern void ripng_cli_init(void); -extern const struct frr_yang_module_info frr_ripngd_info; #endif /* _ZEBRA_RIPNG_RIPNGD_H */ diff --git a/ripngd/subdir.am b/ripngd/subdir.am index ea0ccf1482..48ba361372 100644 --- a/ripngd/subdir.am +++ b/ripngd/subdir.am @@ -6,21 +6,23 @@ if RIPNGD noinst_LIBRARIES += ripngd/libripng.a sbin_PROGRAMS += ripngd/ripngd vtysh_scan += \ - $(top_srcdir)/ripngd/ripng_cli.c \ - $(top_srcdir)/ripngd/ripng_debug.c \ - $(top_srcdir)/ripngd/ripngd.c \ + ripngd/ripng_cli.c \ + ripngd/ripng_debug.c \ + ripngd/ripngd.c \ # end -man8 += $(MANBUILD)/ripngd.8 +man8 += $(MANBUILD)/frr-ripngd.8 endif ripngd_libripng_a_SOURCES = \ ripngd/ripng_cli.c \ ripngd/ripng_debug.c \ ripngd/ripng_interface.c \ - ripngd/ripng_memory.c \ ripngd/ripng_nexthop.c \ ripngd/ripng_offset.c \ - ripngd/ripng_northbound.c \ + ripngd/ripng_nb.c \ + ripngd/ripng_nb_config.c \ + ripngd/ripng_nb_rpcs.c \ + ripngd/ripng_nb_state.c \ ripngd/ripng_peer.c \ ripngd/ripng_route.c \ ripngd/ripng_routemap.c \ @@ -28,13 +30,13 @@ ripngd_libripng_a_SOURCES = \ ripngd/ripngd.c \ # end -ripngd/ripng_cli_clippy.c: $(CLIPPY_DEPS) -ripngd/ripng_cli.$(OBJEXT): ripngd/ripng_cli_clippy.c +clippy_scan += \ + ripngd/ripng_cli.c \ + # end noinst_HEADERS += \ - ripngd/ripng_cli.h \ ripngd/ripng_debug.h \ - ripngd/ripng_memory.h \ + ripngd/ripng_nb.h \ ripngd/ripng_nexthop.h \ ripngd/ripng_route.h \ ripngd/ripngd.h \ diff --git a/sharpd/sharp_globals.h b/sharpd/sharp_globals.h index 4e5c933667..8eba57f4dd 100644 --- a/sharpd/sharp_globals.h +++ b/sharpd/sharp_globals.h @@ -28,9 +28,11 @@ struct sharp_routes { /* The original prefix for route installation */ struct prefix orig_prefix; - /* The nexthop group we are using for installation */ + /* The nexthop info we are using for installation */ struct nexthop nhop; + struct nexthop backup_nhop; struct nexthop_group nhop_group; + struct nexthop_group backup_nhop_group; uint32_t total_routes; uint32_t installed_routes; diff --git a/sharpd/sharp_logpump.c b/sharpd/sharp_logpump.c new file mode 100644 index 0000000000..322d802b8a --- /dev/null +++ b/sharpd/sharp_logpump.c @@ -0,0 +1,157 @@ +/* + * testing log message generator + * Copyright (C) 2019-2020 David Lamparter for NetDEF, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "vty.h" +#include "command.h" +#include "prefix.h" +#include "nexthop.h" +#include "log.h" +#include "thread.h" +#include "vrf.h" +#include "zclient.h" +#include "frr_pthread.h" + +#include "sharpd/sharp_vty.h" + +/* this is quite hacky, but then again it's a test tool and it does its job. */ +static struct frr_pthread *lpt; + +static unsigned long lp_duration; +static unsigned lp_frequency; +static unsigned lp_burst; +static size_t lp_ctr, lp_expect; +static struct rusage lp_rusage; +static struct vty *lp_vty; + +extern struct thread_master *master; + +static int logpump_done(struct thread *thread) +{ + double x; + + vty_out(lp_vty, "\nlogpump done\n"); + vty_out(lp_vty, "%9zu messages written\n", lp_ctr); + x = (double)lp_ctr / (double)lp_expect * 100.; + vty_out(lp_vty, "%9zu messages targeted = %5.1lf%%\n", lp_expect, x); + + x = lp_rusage.ru_utime.tv_sec * 1000000 + lp_rusage.ru_utime.tv_usec; + x /= (double)lp_ctr; + vty_out(lp_vty, "%6llu.%06u usr %9.1lfns/msg\n", + (unsigned long long)lp_rusage.ru_utime.tv_sec, + (unsigned)lp_rusage.ru_utime.tv_usec, x * 1000.); + + x = lp_rusage.ru_stime.tv_sec * 1000000 + lp_rusage.ru_stime.tv_usec; + x /= (double)lp_ctr; + vty_out(lp_vty, "%6llu.%06u sys %9.1lfns/msg\n", + (unsigned long long)lp_rusage.ru_stime.tv_sec, + (unsigned)lp_rusage.ru_stime.tv_usec, x * 1000.); + + frr_pthread_stop(lpt, NULL); + frr_pthread_destroy(lpt); + lpt = NULL; + return 0; +} + +static void *logpump_run(void *arg) +{ + struct timespec start, next, now; + unsigned long delta, period; + + period = 1000000000L / lp_frequency; + + zlog_tls_buffer_init(); + + clock_gettime(CLOCK_MONOTONIC, &start); + next = start; + do { + for (size_t inburst = 0; inburst < lp_burst; inburst++) + zlog_debug("log pump: %zu (burst %zu)", + lp_ctr++, inburst); + + clock_gettime(CLOCK_MONOTONIC, &now); + delta = (now.tv_sec - start.tv_sec) * 1000000000L + + (now.tv_nsec - start.tv_nsec); + + next.tv_nsec += period; + if (next.tv_nsec > 1000000000L) { + next.tv_sec++; + next.tv_nsec -= 1000000000L; + } +#ifdef HAVE_CLOCK_NANOSLEEP + clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &next, NULL); +#else + struct timespec slpdur; + + slpdur.tv_sec = next.tv_sec - now.tv_sec; + slpdur.tv_nsec = next.tv_nsec - now.tv_nsec; + if (slpdur.tv_nsec < 0) { + slpdur.tv_sec--; + slpdur.tv_nsec += 1000000000L; + } + + nanosleep(&slpdur, NULL); +#endif + } while (delta < lp_duration); + + zlog_tls_buffer_fini(); + +#ifdef RUSAGE_THREAD + getrusage(RUSAGE_THREAD, &lp_rusage); +#else + getrusage(RUSAGE_SELF, &lp_rusage); +#endif + + thread_add_timer_msec(master, logpump_done, NULL, 0, NULL); + return NULL; +} + +static int logpump_halt(struct frr_pthread *fpt, void **res) +{ + return 0; +} + +/* default frr_pthread attributes */ +static const struct frr_pthread_attr attr = { + .start = logpump_run, + .stop = logpump_halt, +}; + +void sharp_logpump_run(struct vty *vty, unsigned duration, unsigned frequency, + unsigned burst) +{ + if (lpt != NULL) { + vty_out(vty, "logpump already running\n"); + return; + } + + vty_out(vty, "starting logpump...\n"); + vty_out(vty, "keep this VTY open and press Enter to see results\n"); + + lp_vty = vty; + lp_duration = duration * 1000000000UL; + lp_frequency = frequency; + lp_burst = burst; + lp_expect = duration * frequency * burst; + lp_ctr = 0; + + lpt = frr_pthread_new(&attr, "logpump", "logpump"); + frr_pthread_run(lpt, NULL); +} diff --git a/sharpd/sharp_main.c b/sharpd/sharp_main.c index 39453ee9ad..ccf34b10dd 100644 --- a/sharpd/sharp_main.c +++ b/sharpd/sharp_main.c @@ -81,6 +81,8 @@ static void sigint(void) { zlog_notice("Terminating on signal"); + frr_fini(); + exit(0); } @@ -111,7 +113,11 @@ struct quagga_signal_t sharp_signals[] = { #define SHARP_VTY_PORT 2614 -static const struct frr_yang_module_info *sharpd_yang_modules[] = { +static const struct frr_yang_module_info *const sharpd_yang_modules[] = { + &frr_filter_info, + &frr_interface_info, + &frr_route_map_info, + &frr_vrf_info, }; FRR_DAEMON_INFO(sharpd, SHARP, .vty_port = SHARP_VTY_PORT, @@ -161,9 +167,6 @@ int main(int argc, char **argv, char **envp) nexthop_group_init(NULL, NULL, NULL, NULL); vrf_init(NULL, NULL, NULL, NULL, NULL); - access_list_init(); - route_map_init(); - sharp_zebra_init(); /* Get configuration file. */ diff --git a/sharpd/sharp_vty.c b/sharpd/sharp_vty.c index fbcbbe3fdc..7db70d9a46 100644 --- a/sharpd/sharp_vty.c +++ b/sharpd/sharp_vty.c @@ -39,39 +39,41 @@ #endif DEFPY(watch_nexthop_v6, watch_nexthop_v6_cmd, - "sharp watch [vrf NAME$name] X:X::X:X$nhop [connected$connected]", + "sharp watch [vrf NAME$vrf_name] [connected$connected]", "Sharp routing Protocol\n" "Watch for changes\n" "The vrf we would like to watch if non-default\n" "The NAME of the vrf\n" "Watch for nexthop changes\n" - "Watch for import check changes\n" "The v6 nexthop to signal for watching\n" + "Watch for import check changes\n" + "The v6 prefix to signal for watching\n" "Should the route be connected\n") { struct vrf *vrf; struct prefix p; bool type_import; - if (!name) - name = VRF_DEFAULT_NAME; - vrf = vrf_lookup_by_name(name); + if (!vrf_name) + vrf_name = VRF_DEFAULT_NAME; + vrf = vrf_lookup_by_name(vrf_name); if (!vrf) { vty_out(vty, "The vrf NAME specified: %s does not exist\n", - name); + vrf_name); return CMD_WARNING; } - if (n) - type_import = false; - else - type_import = true; - memset(&p, 0, sizeof(p)); - p.prefixlen = 128; - memcpy(&p.u.prefix6, &nhop, 16); - p.family = AF_INET6; + if (n) { + type_import = false; + p.prefixlen = 128; + memcpy(&p.u.prefix6, &nhop, 16); + p.family = AF_INET6; + } else { + type_import = true; + p = *(const struct prefix *)inhop; + } sharp_nh_tracker_get(&p); sharp_zebra_nexthop_watch(&p, vrf->vrf_id, type_import, @@ -81,39 +83,42 @@ DEFPY(watch_nexthop_v6, watch_nexthop_v6_cmd, } DEFPY(watch_nexthop_v4, watch_nexthop_v4_cmd, - "sharp watch [vrf NAME$name] A.B.C.D$nhop [connected$connected]", + "sharp watch [vrf NAME$vrf_name] [connected$connected]", "Sharp routing Protocol\n" "Watch for changes\n" "The vrf we would like to watch if non-default\n" "The NAME of the vrf\n" "Watch for nexthop changes\n" + "The v4 address to signal for watching\n" "Watch for import check changes\n" - "The v4 nexthop to signal for watching\n" + "The v4 prefix for import check to watch\n" "Should the route be connected\n") { struct vrf *vrf; struct prefix p; bool type_import; - if (!name) - name = VRF_DEFAULT_NAME; - vrf = vrf_lookup_by_name(name); + if (!vrf_name) + vrf_name = VRF_DEFAULT_NAME; + vrf = vrf_lookup_by_name(vrf_name); if (!vrf) { vty_out(vty, "The vrf NAME specified: %s does not exist\n", - name); + vrf_name); return CMD_WARNING; } memset(&p, 0, sizeof(p)); - if (n) + if (n) { type_import = false; - else + p.prefixlen = 32; + p.u.prefix4 = nhop; + p.family = AF_INET; + } + else { type_import = true; - - p.prefixlen = 32; - p.u.prefix4 = nhop; - p.family = AF_INET; + p = *(const struct prefix *)inhop; + } sharp_nh_tracker_get(&p); sharp_zebra_nexthop_watch(&p, vrf->vrf_id, type_import, @@ -126,8 +131,8 @@ DEFPY(sharp_nht_data_dump, sharp_nht_data_dump_cmd, "sharp data nexthop", "Sharp routing Protocol\n" - "Nexthop information\n" - "Data Dump\n") + "Data about what is going on\n" + "Nexthop information\n") { sharp_nh_tracker_dump(vty); @@ -145,19 +150,24 @@ DEFPY (install_routes_data_dump, struct timeval r; timersub(&sg.r.t_end, &sg.r.t_start, &r); - vty_out(vty, "Prefix: %s Total: %u %u %u Time: %ld.%ld\n", + vty_out(vty, "Prefix: %s Total: %u %u %u Time: %jd.%ld\n", prefix2str(&sg.r.orig_prefix, buf, sizeof(buf)), sg.r.total_routes, sg.r.installed_routes, sg.r.removed_routes, - r.tv_sec, r.tv_usec); + (intmax_t)r.tv_sec, (long)r.tv_usec); return CMD_SUCCESS; } DEFPY (install_routes, install_routes_cmd, - "sharp install routes [vrf NAME$name] |nexthop-group NAME$nexthop_group> (1-1000000)$routes [instance (0-255)$instance] [repeat (2-1000)$rpt]", + "sharp install routes [vrf NAME$vrf_name]\ + \ + |\ + nexthop-group NHGNAME$nexthop_group>\ + [backup$backup ] \ + (1-1000000)$routes [instance (0-255)$instance] [repeat (2-1000)$rpt]", "Sharp routing Protocol\n" "install some routes\n" "Routes to install\n" @@ -170,6 +180,9 @@ DEFPY (install_routes, "V6 Nexthop address to use\n" "Nexthop-Group to use\n" "The Name of the nexthop-group\n" + "Backup nexthop to use(Can be an IPv4 or IPv6 address)\n" + "Backup V4 Nexthop address to use\n" + "Backup V6 Nexthop address to use\n" "How many to create\n" "Instance to use\n" "Instance\n" @@ -192,6 +205,8 @@ DEFPY (install_routes, memset(&sg.r.orig_prefix, 0, sizeof(sg.r.orig_prefix)); memset(&sg.r.nhop, 0, sizeof(sg.r.nhop)); memset(&sg.r.nhop_group, 0, sizeof(sg.r.nhop_group)); + memset(&sg.r.backup_nhop, 0, sizeof(sg.r.nhop)); + memset(&sg.r.backup_nhop_group, 0, sizeof(sg.r.nhop_group)); if (start4.s_addr != 0) { prefix.family = AF_INET; @@ -204,13 +219,19 @@ DEFPY (install_routes, } sg.r.orig_prefix = prefix; - if (!name) - name = VRF_DEFAULT_NAME; + if (!vrf_name) + vrf_name = VRF_DEFAULT_NAME; - vrf = vrf_lookup_by_name(name); + vrf = vrf_lookup_by_name(vrf_name); if (!vrf) { vty_out(vty, "The vrf NAME specified: %s does not exist\n", - name); + vrf_name); + return CMD_WARNING; + } + + /* Explicit backup not available with named nexthop-group */ + if (backup && nexthop_group) { + vty_out(vty, "%% Invalid: cannot specify both nexthop-group and backup\n"); return CMD_WARNING; } @@ -224,6 +245,22 @@ DEFPY (install_routes, } sg.r.nhop_group.nexthop = nhgc->nhg.nexthop; + + /* Use group's backup nexthop info if present */ + if (nhgc->backup_list_name[0]) { + struct nexthop_group_cmd *bnhgc = + nhgc_find(nhgc->backup_list_name); + + if (!bnhgc) { + vty_out(vty, "%% Backup group %s not found for group %s\n", + nhgc->backup_list_name, + nhgc->name); + return CMD_WARNING; + } + + sg.r.backup_nhop.vrf_id = vrf->vrf_id; + sg.r.backup_nhop_group.nexthop = bnhgc->nhg.nexthop; + } } else { if (nexthop4.s_addr != INADDR_ANY) { sg.r.nhop.gate.ipv4 = nexthop4; @@ -237,17 +274,37 @@ DEFPY (install_routes, sg.r.nhop_group.nexthop = &sg.r.nhop; } + /* Use single backup nexthop if specified */ + if (backup) { + /* Set flag and index in primary nexthop */ + SET_FLAG(sg.r.nhop.flags, NEXTHOP_FLAG_HAS_BACKUP); + sg.r.nhop.backup_num = 1; + sg.r.nhop.backup_idx[0] = 0; + + if (backup_nexthop4.s_addr != INADDR_ANY) { + sg.r.backup_nhop.gate.ipv4 = backup_nexthop4; + sg.r.backup_nhop.type = NEXTHOP_TYPE_IPV4; + } else { + sg.r.backup_nhop.gate.ipv6 = backup_nexthop6; + sg.r.backup_nhop.type = NEXTHOP_TYPE_IPV6; + } + + sg.r.backup_nhop.vrf_id = vrf->vrf_id; + sg.r.backup_nhop_group.nexthop = &sg.r.backup_nhop; + } + 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, &sg.r.nhop_group, rts); + sharp_install_routes_helper(&prefix, sg.r.vrf_id, sg.r.inst, + &sg.r.nhop_group, &sg.r.backup_nhop_group, + rts); return CMD_SUCCESS; } DEFPY(vrf_label, vrf_label_cmd, - "sharp label vrf NAME$name label (0-100000)$label", + "sharp label vrf NAME$vrf_name label (0-100000)$label", "Sharp Routing Protocol\n" "Give a vrf a label\n" "Pop and forward for IPv4\n" @@ -259,10 +316,10 @@ DEFPY(vrf_label, vrf_label_cmd, struct vrf *vrf; afi_t afi = (ipv4) ? AFI_IP : AFI_IP6; - if (strcmp(name, "default") == 0) + if (strcmp(vrf_name, "default") == 0) vrf = vrf_lookup_by_id(VRF_DEFAULT); else - vrf = vrf_lookup_by_name(name); + vrf = vrf_lookup_by_name(vrf_name); if (!vrf) { vty_out(vty, "Unable to find vrf you silly head"); @@ -278,7 +335,7 @@ DEFPY(vrf_label, vrf_label_cmd, DEFPY (remove_routes, remove_routes_cmd, - "sharp remove routes [vrf NAME$name] (1-1000000)$routes [instance (0-255)$instance]", + "sharp remove routes [vrf NAME$vrf_name] (1-1000000)$routes [instance (0-255)$instance]", "Sharp Routing Protocol\n" "Remove some routes\n" "Routes to remove\n" @@ -309,10 +366,10 @@ DEFPY (remove_routes, prefix.u.prefix6 = start6; } - vrf = vrf_lookup_by_name(name ? name : VRF_DEFAULT_NAME); + vrf = vrf_lookup_by_name(vrf_name ? vrf_name : VRF_DEFAULT_NAME); if (!vrf) { vty_out(vty, "The vrf NAME specified: %s does not exist\n", - name ? name : VRF_DEFAULT_NAME); + vrf_name ? vrf_name : VRF_DEFAULT_NAME); return CMD_WARNING; } @@ -332,7 +389,307 @@ DEFUN_NOSH (show_debugging_sharpd, DEBUG_STR "Sharp Information\n") { - vty_out(vty, "Sharp debugging status\n"); + vty_out(vty, "Sharp debugging status:\n"); + + return CMD_SUCCESS; +} + +DEFPY (sharp_lsp_prefix_v4, sharp_lsp_prefix_v4_cmd, + "sharp lsp [update]$update (0-100000)$inlabel\ + nexthop-group NHGNAME$nhgname\ + [prefix A.B.C.D/M$pfx\ + " FRR_IP_REDIST_STR_ZEBRA "$type_str [instance (0-255)$instance]]", + "Sharp Routing Protocol\n" + "Add an LSP\n" + "Update an LSP\n" + "The ingress label to use\n" + "Use nexthops from a nexthop-group\n" + "The nexthop-group name\n" + "Label a prefix\n" + "The v4 prefix to label\n" + FRR_IP_REDIST_HELP_STR_ZEBRA + "Instance to use\n" + "Instance\n") +{ + struct nexthop_group_cmd *nhgc = NULL; + struct nexthop_group_cmd *backup_nhgc = NULL; + struct nexthop_group *backup_nhg = NULL; + struct prefix p = {}; + int type = 0; + bool update_p; + + update_p = (update != NULL); + + /* We're offered a v4 prefix */ + if (pfx->family > 0 && type_str) { + p.family = pfx->family; + p.prefixlen = pfx->prefixlen; + p.u.prefix4 = pfx->prefix; + + type = proto_redistnum(AFI_IP, type_str); + if (type < 0) { + vty_out(vty, "%% Unknown route type '%s'\n", type_str); + return CMD_WARNING; + } + } else if (pfx->family > 0 || type_str) { + vty_out(vty, "%% Must supply both prefix and type\n"); + return CMD_WARNING; + } + + nhgc = nhgc_find(nhgname); + if (!nhgc) { + vty_out(vty, "%% Nexthop-group '%s' does not exist\n", + nhgname); + return CMD_WARNING; + } + + if (nhgc->nhg.nexthop == NULL) { + vty_out(vty, "%% Nexthop-group '%s' is empty\n", nhgname); + return CMD_WARNING; + } + + /* Use group's backup nexthop info if present */ + if (nhgc->backup_list_name[0]) { + backup_nhgc = nhgc_find(nhgc->backup_list_name); + + if (!backup_nhgc) { + vty_out(vty, + "%% Backup group %s not found for group %s\n", + nhgc->backup_list_name, + nhgname); + return CMD_WARNING; + } + backup_nhg = &(backup_nhgc->nhg); + } + + if (sharp_install_lsps_helper(true /*install*/, update_p, + pfx->family > 0 ? &p : NULL, + type, instance, inlabel, + &(nhgc->nhg), backup_nhg) == 0) + return CMD_SUCCESS; + else { + vty_out(vty, "%% LSP install failed!\n"); + return CMD_WARNING; + } +} + +DEFPY(sharp_remove_lsp_prefix_v4, sharp_remove_lsp_prefix_v4_cmd, + "sharp remove lsp \ + (0-100000)$inlabel\ + [nexthop-group NHGNAME$nhgname] \ + [prefix A.B.C.D/M$pfx\ + " FRR_IP_REDIST_STR_SHARPD "$type_str [instance (0-255)$instance]]", + "Sharp Routing Protocol\n" + "Remove data\n" + "Remove an LSP\n" + "The ingress label\n" + "Use nexthops from a nexthop-group\n" + "The nexthop-group name\n" + "Specify a v4 prefix\n" + "The v4 prefix to label\n" + FRR_IP_REDIST_HELP_STR_SHARPD + "Routing instance\n" + "Instance to use\n") +{ + struct nexthop_group_cmd *nhgc = NULL; + struct prefix p = {}; + int type = 0; + struct nexthop_group *nhg = NULL; + + /* We're offered a v4 prefix */ + if (pfx->family > 0 && type_str) { + p.family = pfx->family; + p.prefixlen = pfx->prefixlen; + p.u.prefix4 = pfx->prefix; + + type = proto_redistnum(AFI_IP, type_str); + if (type < 0) { + vty_out(vty, "%% Unknown route type '%s'\n", type_str); + return CMD_WARNING; + } + } else if (pfx->family > 0 || type_str) { + vty_out(vty, "%% Must supply both prefix and type\n"); + return CMD_WARNING; + } + + if (nhgname) { + nhgc = nhgc_find(nhgname); + if (!nhgc) { + vty_out(vty, "%% Nexthop-group '%s' does not exist\n", + nhgname); + return CMD_WARNING; + } + + if (nhgc->nhg.nexthop == NULL) { + vty_out(vty, "%% Nexthop-group '%s' is empty\n", + nhgname); + return CMD_WARNING; + } + nhg = &(nhgc->nhg); + } + + if (sharp_install_lsps_helper(false /*!install*/, false, + pfx->family > 0 ? &p : NULL, + type, instance, inlabel, nhg, NULL) == 0) + return CMD_SUCCESS; + else { + vty_out(vty, "%% LSP remove failed!\n"); + return CMD_WARNING; + } +} + +DEFPY (logpump, + logpump_cmd, + "sharp logpump duration (1-60) frequency (1-1000000) burst (1-1000)", + "Sharp Routing Protocol\n" + "Generate bulk log messages for testing\n" + "Duration of run (s)\n" + "Duration of run (s)\n" + "Frequency of bursts (s^-1)\n" + "Frequency of bursts (s^-1)\n" + "Number of log messages per each burst\n" + "Number of log messages per each burst\n") +{ + sharp_logpump_run(vty, duration, frequency, burst); + return CMD_SUCCESS; +} + +DEFPY (create_session, + create_session_cmd, + "sharp create session (1-1024)", + "Sharp Routing Protocol\n" + "Create data\n" + "Create a test session\n" + "Session ID\n") +{ + if (sharp_zclient_create(session) != 0) { + vty_out(vty, "%% Client session error\n"); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFPY (remove_session, + remove_session_cmd, + "sharp remove session (1-1024)", + "Sharp Routing Protocol\n" + "Remove data\n" + "Remove a test session\n" + "Session ID\n") +{ + sharp_zclient_delete(session); + return CMD_SUCCESS; +} + +DEFPY (send_opaque, + send_opaque_cmd, + "sharp send opaque type (1-255) (1-1000)$count", + SHARP_STR + "Send messages for testing\n" + "Send opaque messages\n" + "Type code to send\n" + "Type code to send\n" + "Number of messages to send\n") +{ + sharp_opaque_send(type, 0, 0, 0, count); + return CMD_SUCCESS; +} + +DEFPY (send_opaque_unicast, + send_opaque_unicast_cmd, + "sharp send opaque unicast type (1-255) \ + " FRR_IP_REDIST_STR_ZEBRA "$proto_str \ + [{instance (0-1000) | session (1-1000)}] (1-1000)$count", + SHARP_STR + "Send messages for testing\n" + "Send opaque messages\n" + "Send unicast messages\n" + "Type code to send\n" + "Type code to send\n" + FRR_IP_REDIST_HELP_STR_ZEBRA + "Daemon instance\n" + "Daemon instance\n" + "Session ID\n" + "Session ID\n" + "Number of messages to send\n") +{ + uint32_t proto; + + proto = proto_redistnum(AFI_IP, proto_str); + + sharp_opaque_send(type, proto, instance, session, count); + + return CMD_SUCCESS; +} + +DEFPY (send_opaque_reg, + send_opaque_reg_cmd, + "sharp send opaque \ + " FRR_IP_REDIST_STR_ZEBRA "$proto_str \ + [{instance (0-1000) | session (1-1000)}] type (1-1000)", + SHARP_STR + "Send messages for testing\n" + "Send opaque messages\n" + "Send opaque registration\n" + "Send opaque unregistration\n" + FRR_IP_REDIST_HELP_STR_ZEBRA + "Daemon instance\n" + "Daemon instance\n" + "Session ID\n" + "Session ID\n" + "Opaque sub-type code\n" + "Opaque sub-type code\n") +{ + int proto; + + proto = proto_redistnum(AFI_IP, proto_str); + + sharp_opaque_reg_send((reg != NULL), proto, instance, session, type); + return CMD_SUCCESS; +} + +DEFPY (neigh_discover, + neigh_discover_cmd, + "sharp neigh discover [vrf NAME$vrf_name] IFNAME$ifname", + SHARP_STR + "Discover neighbours\n" + "Send an ARP/NDP request\n" + VRF_CMD_HELP_STR + "v4 Destination address\n" + "v6 Destination address\n" + "Interface name\n") +{ + struct vrf *vrf; + struct interface *ifp; + struct prefix prefix; + + memset(&prefix, 0, sizeof(prefix)); + + if (dst4.s_addr != 0) { + prefix.family = AF_INET; + prefix.prefixlen = 32; + prefix.u.prefix4 = dst4; + } else { + prefix.family = AF_INET6; + prefix.prefixlen = 128; + prefix.u.prefix6 = dst6; + } + + vrf = vrf_lookup_by_name(vrf_name ? vrf_name : VRF_DEFAULT_NAME); + if (!vrf) { + vty_out(vty, "The vrf NAME specified: %s does not exist\n", + vrf_name ? vrf_name : VRF_DEFAULT_NAME); + return CMD_WARNING; + } + + ifp = if_lookup_by_name_vrf(ifname, vrf); + if (ifp == NULL) { + vty_out(vty, "%% Can't find interface %s\n", ifname); + return CMD_WARNING; + } + + sharp_zebra_send_arp(ifp, &prefix); return CMD_SUCCESS; } @@ -346,8 +703,17 @@ void sharp_vty_init(void) install_element(ENABLE_NODE, &sharp_nht_data_dump_cmd); install_element(ENABLE_NODE, &watch_nexthop_v6_cmd); install_element(ENABLE_NODE, &watch_nexthop_v4_cmd); - - install_element(VIEW_NODE, &show_debugging_sharpd_cmd); + install_element(ENABLE_NODE, &sharp_lsp_prefix_v4_cmd); + install_element(ENABLE_NODE, &sharp_remove_lsp_prefix_v4_cmd); + install_element(ENABLE_NODE, &logpump_cmd); + install_element(ENABLE_NODE, &create_session_cmd); + install_element(ENABLE_NODE, &remove_session_cmd); + install_element(ENABLE_NODE, &send_opaque_cmd); + install_element(ENABLE_NODE, &send_opaque_unicast_cmd); + install_element(ENABLE_NODE, &send_opaque_reg_cmd); + install_element(ENABLE_NODE, &neigh_discover_cmd); + + install_element(ENABLE_NODE, &show_debugging_sharpd_cmd); return; } diff --git a/sharpd/sharp_vty.h b/sharpd/sharp_vty.h index d4af095e89..0d1327259c 100644 --- a/sharpd/sharp_vty.h +++ b/sharpd/sharp_vty.h @@ -23,4 +23,10 @@ #define __SHARP_VTY_H__ extern void sharp_vty_init(void); + +struct vty; + +extern void sharp_logpump_run(struct vty *vty, unsigned duration, + unsigned frequency, unsigned burst); + #endif diff --git a/sharpd/sharp_zebra.c b/sharpd/sharp_zebra.c index f1e83628c2..208e0ae30f 100644 --- a/sharpd/sharp_zebra.c +++ b/sharpd/sharp_zebra.c @@ -25,14 +25,9 @@ #include "command.h" #include "network.h" #include "prefix.h" -#include "routemap.h" -#include "table.h" #include "stream.h" #include "memory.h" #include "zclient.h" -#include "filter.h" -#include "plist.h" -#include "log.h" #include "nexthop.h" #include "nexthop_group.h" @@ -46,93 +41,185 @@ struct zclient *zclient = NULL; /* For registering threads. */ extern struct thread_master *master; -static struct interface *zebra_interface_if_lookup(struct stream *s) -{ - char ifname_tmp[INTERFACE_NAMSIZ]; - - /* Read interface name. */ - stream_get(ifname_tmp, s, INTERFACE_NAMSIZ); +/* Privs info */ +extern struct zebra_privs_t sharp_privs; - /* And look it up. */ - return if_lookup_by_name(ifname_tmp, VRF_DEFAULT); -} +DEFINE_MTYPE_STATIC(SHARPD, ZC, "Test zclients"); -/* Inteface addition message from zebra. */ -static int interface_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) -{ - struct interface *ifp; +/* Struct to hold list of test zclients */ +struct sharp_zclient { + struct sharp_zclient *prev; + struct sharp_zclient *next; + struct zclient *client; +}; - ifp = zebra_interface_add_read(zclient->ibuf, vrf_id); +/* Head of test zclient list */ +static struct sharp_zclient *sharp_clients_head; - if (!ifp->info) - return 0; +static int sharp_opaque_handler(ZAPI_CALLBACK_ARGS); - return 0; -} - -static int interface_delete(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +/* Utility to add a test zclient struct to the list */ +static void add_zclient(struct zclient *client) { - struct interface *ifp; - struct stream *s; + struct sharp_zclient *node; - s = zclient->ibuf; - /* zebra_interface_state_read () updates interface structure in iflist - */ - ifp = zebra_interface_state_read(s, vrf_id); + node = XCALLOC(MTYPE_ZC, sizeof(struct sharp_zclient)); - if (ifp == NULL) - return 0; + node->client = client; - if_set_index(ifp, IFINDEX_INTERNAL); + node->next = sharp_clients_head; + if (sharp_clients_head) + sharp_clients_head->prev = node; + sharp_clients_head = node; +} +/* Interface addition message from zebra. */ +static int sharp_ifp_create(struct interface *ifp) +{ return 0; } -static int interface_address_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int sharp_ifp_destroy(struct interface *ifp) { + return 0; +} - zebra_interface_address_read(command, zclient->ibuf, vrf_id); +static int interface_address_add(ZAPI_CALLBACK_ARGS) +{ + zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); return 0; } -static int interface_address_delete(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int interface_address_delete(ZAPI_CALLBACK_ARGS) { struct connected *c; - c = zebra_interface_address_read(command, zclient->ibuf, vrf_id); + c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); if (!c) return 0; - connected_free(c); + connected_free(&c); return 0; } -static int interface_state_up(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int sharp_ifp_up(struct interface *ifp) { + return 0; +} - zebra_interface_if_lookup(zclient->ibuf); - +static int sharp_ifp_down(struct interface *ifp) +{ return 0; } -static int interface_state_down(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +int sharp_install_lsps_helper(bool install_p, bool update_p, + const struct prefix *p, uint8_t type, + int instance, uint32_t in_label, + const struct nexthop_group *nhg, + const struct nexthop_group *backup_nhg) { + struct zapi_labels zl = {}; + struct zapi_nexthop *znh; + const struct nexthop *nh; + int i, cmd, ret; + + zl.type = ZEBRA_LSP_SHARP; + zl.local_label = in_label; + + if (p) { + SET_FLAG(zl.message, ZAPI_LABELS_FTN); + prefix_copy(&zl.route.prefix, p); + zl.route.type = type; + zl.route.instance = instance; + } - zebra_interface_state_read(zclient->ibuf, vrf_id); + /* List of nexthops is optional for delete */ + i = 0; + if (nhg) { + for (ALL_NEXTHOPS_PTR(nhg, nh)) { + znh = &zl.nexthops[i]; + + /* Must have labels to be useful */ + if (nh->nh_label == NULL || + nh->nh_label->num_labels == 0) + continue; + + if (nh->type == NEXTHOP_TYPE_IFINDEX || + nh->type == NEXTHOP_TYPE_BLACKHOLE) + /* Hmm - can't really deal with these types */ + continue; + + ret = zapi_nexthop_from_nexthop(znh, nh); + if (ret < 0) + return -1; + + i++; + if (i >= MULTIPATH_NUM) + break; + } + } - return 0; + /* Whoops - no nexthops isn't very useful for install */ + if (i == 0 && install_p) + return -1; + + zl.nexthop_num = i; + + /* Add optional backup nexthop info. Since these are used by index, + * we can't just skip over an invalid backup nexthop: we will + * invalidate the entire operation. + */ + if (backup_nhg != NULL) { + i = 0; + for (ALL_NEXTHOPS_PTR(backup_nhg, nh)) { + znh = &zl.backup_nexthops[i]; + + /* Must have labels to be useful */ + if (nh->nh_label == NULL || + nh->nh_label->num_labels == 0) + return -1; + + if (nh->type == NEXTHOP_TYPE_IFINDEX || + nh->type == NEXTHOP_TYPE_BLACKHOLE) + /* Hmm - can't really deal with these types */ + return -1; + + ret = zapi_nexthop_from_nexthop(znh, nh); + if (ret < 0) + return -1; + + i++; + if (i >= MULTIPATH_NUM) + break; + } + + if (i > 0) + SET_FLAG(zl.message, ZAPI_LABELS_HAS_BACKUPS); + + zl.backup_nexthop_num = i; + } + + + if (install_p) { + if (update_p) + cmd = ZEBRA_MPLS_LABELS_REPLACE; + else + cmd = ZEBRA_MPLS_LABELS_ADD; + } else { + cmd = ZEBRA_MPLS_LABELS_DELETE; + } + + ret = zebra_send_mpls_labels(zclient, cmd, &zl); + + return ret; } void sharp_install_routes_helper(struct prefix *p, vrf_id_t vrf_id, - uint8_t instance, struct nexthop_group *nhg, + uint8_t instance, + const struct nexthop_group *nhg, + const struct nexthop_group *backup_nhg, uint32_t routes) { uint32_t temp, i; @@ -146,9 +233,13 @@ void sharp_install_routes_helper(struct prefix *p, vrf_id_t vrf_id, } else temp = ntohl(p->u.val32[3]); + /* Only use backup route/nexthops if present */ + if (backup_nhg && (backup_nhg->nexthop == NULL)) + backup_nhg = NULL; + monotime(&sg.r.t_start); for (i = 0; i < routes; i++) { - route_add(p, vrf_id, (uint8_t)instance, nhg); + route_add(p, vrf_id, (uint8_t)instance, nhg, backup_nhg); if (v4) p->u.prefix4.s_addr = htonl(++temp); else @@ -194,16 +285,16 @@ static void handle_repeated(bool installed) sg.r.inst, sg.r.total_routes); } - if (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.backup_nhop_group, sg.r.total_routes); } } -static int route_notify_owner(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int route_notify_owner(ZAPI_CALLBACK_ARGS) { struct timeval r; struct prefix p; @@ -219,8 +310,8 @@ static int route_notify_owner(int command, struct zclient *zclient, if (sg.r.total_routes == sg.r.installed_routes) { monotime(&sg.r.t_end); timersub(&sg.r.t_end, &sg.r.t_start, &r); - zlog_debug("Installed All Items %ld.%ld", r.tv_sec, - r.tv_usec); + zlog_debug("Installed All Items %jd.%ld", + (intmax_t)r.tv_sec, (long)r.tv_usec); handle_repeated(true); } break; @@ -235,8 +326,8 @@ static int route_notify_owner(int command, struct zclient *zclient, if (sg.r.total_routes == sg.r.removed_routes) { monotime(&sg.r.t_end); timersub(&sg.r.t_end, &sg.r.t_start, &r); - zlog_debug("Removed all Items %ld.%ld", r.tv_sec, - r.tv_usec); + zlog_debug("Removed all Items %jd.%ld", + (intmax_t)r.tv_sec, (long)r.tv_usec); handle_repeated(false); } break; @@ -250,6 +341,15 @@ static int route_notify_owner(int command, struct zclient *zclient, static void zebra_connected(struct zclient *zclient) { zclient_send_reg_requests(zclient, VRF_DEFAULT); + + /* + * Do not actually turn this on yet + * This is just the start of the infrastructure needed here + * This can be fixed at a later time. + * + * zebra_redistribute_send(ZEBRA_REDISTRIBUTE_ADD, zclient, AFI_IP, + * ZEBRA_ROUTE_ALL, 0, VRF_DEFAULT); + */ } void vrf_label_add(vrf_id_t vrf_id, afi_t afi, mpls_label_t label) @@ -257,8 +357,9 @@ 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(struct prefix *p, vrf_id_t vrf_id, - uint8_t instance, struct nexthop_group *nhg) +void route_add(const struct prefix *p, vrf_id_t vrf_id, + uint8_t instance, const struct nexthop_group *nhg, + const struct nexthop_group *backup_nhg) { struct zapi_route api; struct zapi_nexthop *api_nh; @@ -277,34 +378,29 @@ void route_add(struct prefix *p, vrf_id_t vrf_id, for (ALL_NEXTHOPS_PTR(nhg, nh)) { api_nh = &api.nexthops[i]; - api_nh->vrf_id = nh->vrf_id; - api_nh->type = nh->type; - switch (nh->type) { - case NEXTHOP_TYPE_IPV4: - api_nh->gate = nh->gate; - break; - case NEXTHOP_TYPE_IPV4_IFINDEX: - api_nh->gate = nh->gate; - api_nh->ifindex = nh->ifindex; - break; - case NEXTHOP_TYPE_IFINDEX: - api_nh->ifindex = nh->ifindex; - break; - case NEXTHOP_TYPE_IPV6: - memcpy(&api_nh->gate.ipv6, &nh->gate.ipv6, 16); - break; - case NEXTHOP_TYPE_IPV6_IFINDEX: - api_nh->ifindex = nh->ifindex; - memcpy(&api_nh->gate.ipv6, &nh->gate.ipv6, 16); - break; - case NEXTHOP_TYPE_BLACKHOLE: - api_nh->bh_type = nh->bh_type; - break; - } + + zapi_nexthop_from_nexthop(api_nh, nh); + i++; } api.nexthop_num = i; + /* Include backup nexthops, if present */ + if (backup_nhg && backup_nhg->nexthop) { + SET_FLAG(api.message, ZAPI_MESSAGE_BACKUP_NEXTHOPS); + + i = 0; + for (ALL_NEXTHOPS_PTR(backup_nhg, nh)) { + api_nh = &api.backup_nexthops[i]; + + zapi_backup_nexthop_from_nexthop(api_nh, nh); + + i++; + } + + api.backup_nexthop_num = i; + } + zclient_route_send(ZEBRA_ROUTE_ADD, zclient, &api); } @@ -341,39 +437,28 @@ void sharp_zebra_nexthop_watch(struct prefix *p, vrf_id_t vrf_id, bool import, } if (zclient_send_rnh(zclient, command, p, connected, vrf_id) < 0) - zlog_warn("%s: Failure to send nexthop to zebra", - __PRETTY_FUNCTION__); + zlog_warn("%s: Failure to send nexthop to zebra", __func__); } -static int sharp_nexthop_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int sharp_debug_nexthops(struct zapi_route *api) { - struct sharp_nh_tracker *nht; - struct zapi_route nhr; - char buf[PREFIX_STRLEN]; int i; + char buf[PREFIX_STRLEN]; - if (!zapi_nexthop_update_decode(zclient->ibuf, &nhr)) { - zlog_warn("%s: Decode of update failed", __PRETTY_FUNCTION__); - + if (api->nexthop_num == 0) { + zlog_debug( + " Not installed"); return 0; } - zlog_debug("Received update for %s", - prefix2str(&nhr.prefix, buf, sizeof(buf))); - - nht = sharp_nh_tracker_get(&nhr.prefix); - nht->nhop_num = nhr.nexthop_num; - nht->updates++; - - for (i = 0; i < nhr.nexthop_num; i++) { - struct zapi_nexthop *znh = &nhr.nexthops[i]; + for (i = 0; i < api->nexthop_num; i++) { + struct zapi_nexthop *znh = &api->nexthops[i]; switch (znh->type) { case NEXTHOP_TYPE_IPV4_IFINDEX: case NEXTHOP_TYPE_IPV4: zlog_debug( - "\tNexthop %s, type: %d, ifindex: %d, vrf: %d, label_num: %d", + " Nexthop %s, type: %d, ifindex: %d, vrf: %d, label_num: %d", inet_ntop(AF_INET, &znh->gate.ipv4.s_addr, buf, sizeof(buf)), znh->type, znh->ifindex, znh->vrf_id, @@ -382,41 +467,224 @@ static int sharp_nexthop_update(int command, struct zclient *zclient, case NEXTHOP_TYPE_IPV6_IFINDEX: case NEXTHOP_TYPE_IPV6: zlog_debug( - "\tNexthop %s, type: %d, ifindex: %d, vrf: %d, label_num: %d", + " Nexthop %s, type: %d, ifindex: %d, vrf: %d, label_num: %d", inet_ntop(AF_INET6, &znh->gate.ipv6, buf, sizeof(buf)), znh->type, znh->ifindex, znh->vrf_id, znh->label_num); break; case NEXTHOP_TYPE_IFINDEX: - zlog_debug("\tNexthop IFINDEX: %d, ifindex: %d", + zlog_debug(" Nexthop IFINDEX: %d, ifindex: %d", znh->type, znh->ifindex); break; case NEXTHOP_TYPE_BLACKHOLE: - zlog_debug("\tNexthop blackhole"); + zlog_debug(" Nexthop blackhole"); + break; + } + } + + return i; +} +static int sharp_nexthop_update(ZAPI_CALLBACK_ARGS) +{ + struct sharp_nh_tracker *nht; + struct zapi_route nhr; + + if (!zapi_nexthop_update_decode(zclient->ibuf, &nhr)) { + zlog_err("%s: Decode of update failed", __func__); + return 0; + } + + zlog_debug("Received update for %pFX", &nhr.prefix); + + nht = sharp_nh_tracker_get(&nhr.prefix); + nht->nhop_num = nhr.nexthop_num; + nht->updates++; + + sharp_debug_nexthops(&nhr); + + return 0; +} + +static int sharp_redistribute_route(ZAPI_CALLBACK_ARGS) +{ + struct zapi_route api; + + if (zapi_route_decode(zclient->ibuf, &api) < 0) + zlog_warn("%s: Decode of redistribute failed: %d", __func__, + ZEBRA_REDISTRIBUTE_ROUTE_ADD); + + zlog_debug("%s: %pFX (%s)", zserv_command_string(cmd), + &api.prefix, zebra_route_string(api.type)); + + sharp_debug_nexthops(&api); + + return 0; +} + +/* Add a zclient with a specified session id, for testing. */ +int sharp_zclient_create(uint32_t session_id) +{ + struct zclient *client; + struct sharp_zclient *node; + + /* Check for duplicates */ + for (node = sharp_clients_head; node != NULL; node = node->next) { + if (node->client->session_id == session_id) + return -1; + } + + client = zclient_new(master, &zclient_options_default); + client->sock = -1; + client->session_id = session_id; + + zclient_init(client, ZEBRA_ROUTE_SHARP, 0, &sharp_privs); + + /* Register handlers for messages we expect this session to see */ + client->opaque_msg_handler = sharp_opaque_handler; + + /* Enqueue on the list of test clients */ + add_zclient(client); + + return 0; +} + +/* Delete one of the extra test zclients */ +int sharp_zclient_delete(uint32_t session_id) +{ + struct sharp_zclient *node; + + /* Search for session */ + for (node = sharp_clients_head; node != NULL; node = node->next) { + if (node->client->session_id == session_id) { + /* Dequeue from list */ + if (node->next) + node->next->prev = node->prev; + if (node->prev) + node->prev->next = node->next; + if (node == sharp_clients_head) + sharp_clients_head = node->next; + + /* Clean up zclient */ + zclient_stop(node->client); + zclient_free(node->client); + + /* Free memory */ + XFREE(MTYPE_ZC, node); break; } } + return 0; } -extern struct zebra_privs_t sharp_privs; +/* Handler for opaque messages */ +static int sharp_opaque_handler(ZAPI_CALLBACK_ARGS) +{ + struct stream *s; + struct zapi_opaque_msg info; + + s = zclient->ibuf; + + if (zclient_opaque_decode(s, &info) != 0) + return -1; + + zlog_debug("%s: [%u] received opaque type %u", __func__, + zclient->session_id, info.type); + + return 0; +} + +/* + * Send OPAQUE messages, using subtype 'type'. + */ +void sharp_opaque_send(uint32_t type, uint32_t proto, uint32_t instance, + uint32_t session_id, uint32_t count) +{ + uint8_t buf[32]; + int ret; + uint32_t i; + + /* Prepare a small payload */ + for (i = 0; i < sizeof(buf); i++) { + if (type < 255) + buf[i] = type; + else + buf[i] = 255; + } + + /* Send some messages - broadcast and unicast are supported */ + for (i = 0; i < count; i++) { + if (proto == 0) + ret = zclient_send_opaque(zclient, type, buf, + sizeof(buf)); + else + ret = zclient_send_opaque_unicast(zclient, type, proto, + instance, session_id, + buf, sizeof(buf)); + if (ret < 0) { + zlog_debug("%s: send_opaque() failed => %d", + __func__, ret); + break; + } + } + +} + +/* + * Send OPAQUE registration messages, using subtype 'type'. + */ +void sharp_opaque_reg_send(bool is_reg, uint32_t proto, uint32_t instance, + uint32_t session_id, uint32_t type) +{ + struct stream *s; + + s = zclient->obuf; + stream_reset(s); + + if (is_reg) + zclient_create_header(s, ZEBRA_OPAQUE_REGISTER, VRF_DEFAULT); + else + zclient_create_header(s, ZEBRA_OPAQUE_UNREGISTER, VRF_DEFAULT); + + /* Send sub-type */ + stream_putl(s, type); + + /* Add zclient info */ + stream_putc(s, proto); + stream_putw(s, instance); + stream_putl(s, session_id); + + /* Put length at the first point of the stream. */ + stream_putw_at(s, 0, stream_get_endp(s)); + + (void)zclient_send_message(zclient); + +} + +void sharp_zebra_send_arp(const struct interface *ifp, const struct prefix *p) +{ + zclient_send_neigh_discovery_req(zclient, ifp, p); +} void sharp_zebra_init(void) { struct zclient_options opt = {.receive_notify = true}; + if_zapi_callbacks(sharp_ifp_create, sharp_ifp_up, + sharp_ifp_down, sharp_ifp_destroy); + zclient = zclient_new(master, &opt); zclient_init(zclient, ZEBRA_ROUTE_SHARP, 0, &sharp_privs); zclient->zebra_connected = zebra_connected; - zclient->interface_add = interface_add; - zclient->interface_delete = interface_delete; - zclient->interface_up = interface_state_up; - zclient->interface_down = interface_state_down; zclient->interface_address_add = interface_address_add; zclient->interface_address_delete = interface_address_delete; zclient->route_notify_owner = route_notify_owner; zclient->nexthop_update = sharp_nexthop_update; zclient->import_check_update = sharp_nexthop_update; + + zclient->redistribute_route_add = sharp_redistribute_route; + zclient->redistribute_route_del = sharp_redistribute_route; + zclient->opaque_msg_handler = sharp_opaque_handler; } diff --git a/sharpd/sharp_zebra.h b/sharpd/sharp_zebra.h index 57ffcc7690..0a44fa694f 100644 --- a/sharpd/sharp_zebra.h +++ b/sharpd/sharp_zebra.h @@ -24,17 +24,41 @@ extern void sharp_zebra_init(void); +/* Add and delete extra zapi client sessions, for testing */ +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 route_add(struct prefix *p, vrf_id_t, uint8_t instance, - struct nexthop_group *nhg); +extern void route_add(const struct prefix *p, vrf_id_t, uint8_t instance, + 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, - struct nexthop_group *nhg, + const struct nexthop_group *nhg, + const struct nexthop_group *backup_nhg, uint32_t routes); extern void sharp_remove_routes_helper(struct prefix *p, vrf_id_t vrf_id, uint8_t instance, uint32_t routes); + +int sharp_install_lsps_helper(bool install_p, bool update_p, + const struct prefix *p, uint8_t type, + int instance, uint32_t in_label, + const struct nexthop_group *nhg, + const struct nexthop_group *backup_nhg); + +/* Send OPAQUE messages, using subtype 'type'. */ +void sharp_opaque_send(uint32_t type, uint32_t proto, uint32_t instance, + uint32_t session_id, uint32_t count); + +/* Send OPAQUE registration messages, using subtype 'type'. */ +void sharp_opaque_reg_send(bool is_reg, uint32_t proto, uint32_t instance, + uint32_t session_id, uint32_t type); + +extern void sharp_zebra_send_arp(const struct interface *ifp, + const struct prefix *p); + #endif diff --git a/sharpd/sharpd.conf.sample b/sharpd/sharpd.conf.sample index bb1c2edca8..d1cc19a51f 100644 --- a/sharpd/sharpd.conf.sample +++ b/sharpd/sharpd.conf.sample @@ -1,3 +1,6 @@ +! Default sharpd configuration sample ! +! There are no `default` configuration commands for sharpd +! all commands are at the view or enable level. ! log stdout diff --git a/sharpd/subdir.am b/sharpd/subdir.am index 4a9028f6fe..4c43c50d43 100644 --- a/sharpd/subdir.am +++ b/sharpd/subdir.am @@ -6,14 +6,15 @@ if SHARPD noinst_LIBRARIES += sharpd/libsharp.a sbin_PROGRAMS += sharpd/sharpd dist_examples_DATA += sharpd/sharpd.conf.sample -vtysh_scan += $(top_srcdir)/sharpd/sharp_vty.c -man8 += $(MANBUILD)/sharpd.8 +vtysh_scan += sharpd/sharp_vty.c +man8 += $(MANBUILD)/frr-sharpd.8 endif sharpd_libsharp_a_SOURCES = \ sharpd/sharp_nht.c \ sharpd/sharp_zebra.c \ sharpd/sharp_vty.c \ + sharpd/sharp_logpump.c \ # end noinst_HEADERS += \ @@ -23,8 +24,9 @@ noinst_HEADERS += \ sharpd/sharp_zebra.h \ # end -sharpd/sharp_vty_clippy.c: $(CLIPPY_DEPS) -sharpd/sharp_vty.$(OBJEXT): sharpd/sharp_vty_clippy.c +clippy_scan += \ + sharpd/sharp_vty.c \ + # end sharpd_sharpd_SOURCES = sharpd/sharp_main.c sharpd_sharpd_LDADD = sharpd/libsharp.a lib/libfrr.la $(LIBCAP) diff --git a/snapcraft/README.snap_build.md b/snapcraft/README.snap_build.md index 7c42848451..e43f63f2d9 100644 --- a/snapcraft/README.snap_build.md +++ b/snapcraft/README.snap_build.md @@ -92,6 +92,14 @@ All the commands are prefixed with frr. frr.ripngd-debug frr.ldp-debug frr.zebra-debug + frr.pimd-debug + frr.nhrpd-debug + frr.babeld-debug + frr.eigrpd-debug + frr.pbrd-debug + frr.staticd-debug + frr.bfdd-debug + frr.fabricd-debug vtysh can be accessed as frr.vtysh (Make sure you have /snap/bin in your path). If access as `vtysh` instead of `frr.vtysh` is needed, you can enable it diff --git a/snapcraft/README.usage.md b/snapcraft/README.usage.md index a7b51a5656..6a0864c8c5 100644 --- a/snapcraft/README.usage.md +++ b/snapcraft/README.usage.md @@ -66,6 +66,8 @@ depend on them). These are mainly intended to debug the Snap Starts staticd daemon in foreground - `frr.bfdd-debug`: Starts bfdd daemon in foreground +- `frr.fabricd-debug`: + Starts fabricd daemon in foreground MPLS (LDP) ---------- diff --git a/snapcraft/defaults/fabricd.conf.default b/snapcraft/defaults/fabricd.conf.default new file mode 100644 index 0000000000..e69de29bb2 diff --git a/snapcraft/defaults/vrrpd.conf.default b/snapcraft/defaults/vrrpd.conf.default new file mode 100644 index 0000000000..e69de29bb2 diff --git a/snapcraft/scripts/Makefile b/snapcraft/scripts/Makefile index e3a7708f23..0435b3bc52 100644 --- a/snapcraft/scripts/Makefile +++ b/snapcraft/scripts/Makefile @@ -17,6 +17,8 @@ install: install -D -m 0755 pbrd-service $(DESTDIR)/bin/ install -D -m 0755 staticd-service $(DESTDIR)/bin/ install -D -m 0755 bfdd-service $(DESTDIR)/bin/ + install -D -m 0755 fabricd-service $(DESTDIR)/bin/ + install -D -m 0755 vrrpd-service $(DESTDIR)/bin/ install -D -m 0755 set-options $(DESTDIR)/bin/ install -D -m 0755 show_version $(DESTDIR)/bin/ diff --git a/snapcraft/scripts/bgpd-service b/snapcraft/scripts/bgpd-service index 6c3a6f5959..64273d9f80 100644 --- a/snapcraft/scripts/bgpd-service +++ b/snapcraft/scripts/bgpd-service @@ -10,7 +10,7 @@ fi if ! [ -e $SNAP_DATA/rpki.conf ]; then echo "-M rpki" > $SNAP_DATA/rpki.conf fi -EXTRA_OPTIONS="`cat $SNAP_DATA/rpki.conf`" +EXTRA_OPTIONS="`$SNAP/bin/cat $SNAP_DATA/rpki.conf`" exec $SNAP/sbin/bgpd \ -f $SNAP_DATA/bgpd.conf \ --pid_file $SNAP_DATA/bgpd.pid \ diff --git a/snapcraft/scripts/fabricd-service b/snapcraft/scripts/fabricd-service new file mode 100644 index 0000000000..586f061ef0 --- /dev/null +++ b/snapcraft/scripts/fabricd-service @@ -0,0 +1,13 @@ +#!/bin/sh + +set -e -x + +if ! [ -e $SNAP_DATA/fabricd.conf ]; then + cp $SNAP/etc/frr/fabricd.conf.default $SNAP_DATA/fabricd.conf +fi +exec $SNAP/sbin/fabricd \ + -f $SNAP_DATA/fabricd.conf \ + --pid_file $SNAP_DATA/fabricd.pid \ + --socket $SNAP_DATA/zsock \ + --vty_socket $SNAP_DATA + diff --git a/snapcraft/scripts/vrrpd-service b/snapcraft/scripts/vrrpd-service new file mode 100644 index 0000000000..1f60d11469 --- /dev/null +++ b/snapcraft/scripts/vrrpd-service @@ -0,0 +1,13 @@ +#!/bin/sh + +set -e -x + +if ! [ -e $SNAP_DATA/vrrpd.conf ]; then + cp $SNAP/etc/frr/vrrpd.conf.default $SNAP_DATA/vrrpd.conf +fi +exec $SNAP/sbin/vrrpd \ + -f $SNAP_DATA/vrrpd.conf \ + --pid_file $SNAP_DATA/vrrpd.pid \ + --socket $SNAP_DATA/zsock \ + --vty_socket $SNAP_DATA + diff --git a/snapcraft/snapcraft.yaml.in b/snapcraft/snapcraft.yaml.in index b70d6efee2..1836f34979 100644 --- a/snapcraft/snapcraft.yaml.in +++ b/snapcraft/snapcraft.yaml.in @@ -4,8 +4,8 @@ summary: FRRouting BGP/OSPFv2/OSPFv3/ISIS/RIP/RIPng/PIM/LDP/EIGRP/BFD routing da description: BGP/OSPFv2/OSPFv3/ISIS/RIP/RIPng/PIM/LDP/EIGRP/BFD routing daemon FRRouting (FRR) is free software which manages TCP/IP based routing protocols. It supports BGP4, BGP4+, OSPFv2, OSPFv3, IS-IS, RIPv1, RIPv2, - RIPng, PIM, LDP, Babel, EIGRP, PBR (Policy-based routing) and BFD as well as - the IPv6 versions of these. + RIPng, PIM, LDP, Babel, EIGRP, PBR (Policy-based routing), BFD and OpenFabric + as well as the IPv6 versions of these. FRRouting (frr) is a fork of Quagga. confinement: strict grade: devel @@ -127,6 +127,20 @@ apps: - network - network-bind - network-control + fabricd: + command: bin/fabricd-service + daemon: simple + plugs: + - network + - network-bind + - network-control + vrrpd: + command: bin/vrrpd-service + daemon: simple + plugs: + - network + - network-bind + - network-control set: command: bin/set-options zebra-debug: @@ -136,7 +150,7 @@ apps: - network-bind - network-control bgpd-debug: - command: sbin/bgpd -f $SNAP_DATA/bgpd.conf --pid_file $SNAP_DATA/bgpd.pid --socket $SNAP_DATA/zsock --vty_socket $SNAP_DATA --moduledir $SNAP/lib/frr/modules `cat $SNAP_DATA/rpki.conf 2> /dev/null` + command: sbin/bgpd -f $SNAP_DATA/bgpd.conf --pid_file $SNAP_DATA/bgpd.pid --socket $SNAP_DATA/zsock --vty_socket $SNAP_DATA --moduledir $SNAP/lib/frr/modules plugs: - network - network-bind @@ -219,7 +233,18 @@ apps: - network - network-bind - network-control - + fabricd-debug: + command: sbin/fabricd -f $SNAP_DATA/fabricd.conf --pid_file $SNAP_DATA/fabricd.pid --socket $SNAP_DATA/zsock --vty_socket $SNAP_DATA + plugs: + - network + - network-bind + - network-control + vrrpd-debug: + command: sbin/vrrpd -f $SNAP_DATA/vrrpd.conf --pid_file $SNAP_DATA/vrrpd.pid --socket $SNAP_DATA/zsock --vty_socket $SNAP_DATA + plugs: + - network + - network-bind + - network-control parts: rtrlib: build-packages: @@ -230,16 +255,33 @@ parts: stage-packages: - libssh-4 prime: - - lib/x86_64-linux-gnu/librtr.so* + - lib/librtr.so* - usr/lib/x86_64-linux-gnu/libssh.so* source: https://github.com/rtrlib/rtrlib.git source-type: git - source-tag: v0.5.0 + source-tag: v0.7.0 plugin: cmake configflags: - -DCMAKE_BUILD_TYPE=Release + libyang: + build-packages: + - cmake + - make + - gcc + - libpcre3-dev + stage-packages: + - libpcre3 + source: https://github.com/CESNET/libyang.git + source-type: git + source-tag: v1.0.184 + plugin: cmake + configflags: + - -DCMAKE_INSTALL_PREFIX:PATH=/usr + - -DENABLE_LYD_PRIV=ON + - -DENABLE_CACHE=ON + - -DCMAKE_BUILD_TYPE:String="Release" frr: - after: [rtrlib] + after: [rtrlib,libyang] build-packages: - gcc - autoconf @@ -272,13 +314,13 @@ parts: - iproute2 - logrotate - libcap2 - - libc6 - libtinfo5 - libreadline6 - libjson-c2 - libc-ares2 - libatm1 - libprotobuf-c1 + - libdb5.3 plugin: autotools source: ../frr-@PACKAGE_VERSION@.tar.gz configflags: @@ -297,6 +339,7 @@ parts: - --enable-fpm - --enable-protobuf - --enable-rpki + - --enable-vrrpd - --enable-configfile-mask=0640 - --enable-logfile-mask=0640 - --localstatedir=/var/run @@ -322,7 +365,10 @@ parts: eigrpd.conf.default: etc/frr/eigrpd.conf.default pbrd.conf.default: etc/frr/pbrd.conf.default bfdd.conf.default: etc/frr/bfdd.conf.default + fabricd.conf.default: etc/frr/fabricd.conf.default + vrrpd.conf.default: etc/frr/vrrpd.conf.default vtysh.conf.default: etc/frr/vtysh.conf.default + staticd.conf.default: etc/frr/staticd.conf.default frr-scripts: plugin: make source: scripts @@ -344,3 +390,8 @@ parts: README.snap_build.md: doc/README.snap_build.md extra_version_info.txt: doc/extra_version_info.txt +passthrough: + layout: + /usr/lib/x86_64-linux-gnu/libyang1: + bind: $SNAP/usr/lib/x86_64-linux-gnu/libyang1 + diff --git a/solaris/prototype.doc.in b/solaris/prototype.doc.in index a8644b3145..9f7995350a 100644 --- a/solaris/prototype.doc.in +++ b/solaris/prototype.doc.in @@ -8,10 +8,10 @@ d none @mandir@=$DESTDIR/@mandir@ 0755 root bin d none @mandir@/man1=$DESTDIR/@mandir@/man1 0755 root bin f none @mandir@/man1/vtysh.1=$DESTDIR/@mandir@/man1/vtysh.1 0644 root bin d none @mandir@/man8=$DESTDIR/@mandir@/man8 0755 root bin -f none @mandir@/man8/bgpd.8=$DESTDIR/@mandir@/man8/bgpd.8 0644 root bin -f none @mandir@/man8/ospf6d.8=$DESTDIR/@mandir@/man8/ospf6d.8 0644 root bin -f none @mandir@/man8/ospfd.8=$DESTDIR/@mandir@/man8/ospfd.8 0644 root bin -f none @mandir@/man8/ripd.8=$DESTDIR/@mandir@/man8/ripd.8 0644 root bin -f none @mandir@/man8/ripngd.8=$DESTDIR/@mandir@/man8/ripngd.8 0644 root bin -f none @mandir@/man8/zebra.8=$DESTDIR/@mandir@/man8/zebra.8 0644 root bin -f none @mandir@/man8/isisd.8=$DESTDIR/@mandir@/man8/isisd.8 0644 root bin +f none @mandir@/man8/frr-bgpd.8=$DESTDIR/@mandir@/man8/bgpd.8 0644 root bin +f none @mandir@/man8/frr-ospf6d.8=$DESTDIR/@mandir@/man8/frr-ospf6d.8 0644 root bin +f none @mandir@/man8/frr-ospfd.8=$DESTDIR/@mandir@/man8/frr-ospfd.8 0644 root bin +f none @mandir@/man8/frr-ripd.8=$DESTDIR/@mandir@/man8/frr-ripd.8 0644 root bin +f none @mandir@/man8/frr-ripngd.8=$DESTDIR/@mandir@/man8/frr-ripngd.8 0644 root bin +f none @mandir@/man8/frr-zebra.8=$DESTDIR/@mandir@/man8/frr-zebra.8 0644 root bin +f none @mandir@/man8/frr-isisd.8=$DESTDIR/@mandir@/man8/frr-isisd.8 0644 root bin diff --git a/staticd/static_debug.c b/staticd/static_debug.c new file mode 100644 index 0000000000..e43d4e79ff --- /dev/null +++ b/staticd/static_debug.c @@ -0,0 +1,124 @@ +/* + * Staticd debug related functions + * Copyright (C) 2019 Volta Networks Inc. + * Mark Stapp + * + * This file is part of FRRouting (FRR). + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "lib/command.h" +#include "lib/debug.h" + +#include "static_debug.h" + +/* + * Debug infra: a debug struct for each category, and a corresponding + * string. + */ + +/* clang-format off */ +struct debug static_dbg_events = {0, "Staticd events"}; + +struct debug *static_debug_arr[] = { + &static_dbg_events +}; + +const char *static_debugs_conflines[] = { + "debug static events" +}; +/* clang-format on */ + + +/* + * Set or unset all staticd debugs + * + * flags + * The flags to set + * + * set + * Whether to set or unset the specified flags + */ +static void static_debug_set_all(uint32_t flags, bool set) +{ + for (unsigned int i = 0; i < array_size(static_debug_arr); i++) { + DEBUG_FLAGS_SET(static_debug_arr[i], flags, set); + + /* if all modes have been turned off, don't preserve options */ + if (!DEBUG_MODE_CHECK(static_debug_arr[i], DEBUG_MODE_ALL)) + DEBUG_CLEAR(static_debug_arr[i]); + } +} + +static int static_debug_config_write_helper(struct vty *vty, bool config) +{ + uint32_t mode = DEBUG_MODE_ALL; + + if (config) + mode = DEBUG_MODE_CONF; + + for (unsigned int i = 0; i < array_size(static_debug_arr); i++) + if (DEBUG_MODE_CHECK(static_debug_arr[i], mode)) + vty_out(vty, "%s\n", static_debugs_conflines[i]); + + return 0; +} + +int static_config_write_debug(struct vty *vty) +{ + return static_debug_config_write_helper(vty, true); +} + +int static_debug_status_write(struct vty *vty) +{ + return static_debug_config_write_helper(vty, false); +} + +/* + * Set debugging status. + * + * vtynode + * vty->node + * + * onoff + * Whether to turn the specified debugs on or off + * + * events + * Debug general internal events + * + */ +void static_debug_set(int vtynode, bool onoff, bool events) +{ + uint32_t mode = DEBUG_NODE2MODE(vtynode); + + if (events) + DEBUG_MODE_SET(&static_dbg_events, mode, onoff); +} + +/* + * Debug lib initialization + */ + +struct debug_callbacks static_dbg_cbs = { + .debug_set_all = static_debug_set_all +}; + +void static_debug_init(void) +{ + debug_init(&static_dbg_cbs); +} diff --git a/staticd/static_debug.h b/staticd/static_debug.h new file mode 100644 index 0000000000..481c266e14 --- /dev/null +++ b/staticd/static_debug.h @@ -0,0 +1,73 @@ +/* + * Staticd debug related functions + * Copyright (C) 2019 Volta Networks Inc. + * Mark Stapp + * + * This file is part of FRRouting (FRR). + * + * GNU Zebra is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _STATIC_DEBUG_H +#define _STATIC_DEBUG_H + + +#include + +#include "lib/debug.h" + +/* staticd debugging records */ +extern struct debug static_dbg_events; + +/* + * Initialize staticd debugging. + * + * Installs VTY commands and registers callbacks. + */ +void static_debug_init(void); + +/* + * Print staticd debugging configuration. + * + * vty + * VTY to print debugging configuration to. + */ +int static_config_write_debug(struct vty *vty); + +/* + * Print staticd debugging configuration, human readable form. + * + * vty + * VTY to print debugging configuration to. + */ +int static_debug_status_write(struct vty *vty); + +/* + * Set debugging status. + * + * vtynode + * vty->node + * + * onoff + * Whether to turn the specified debugs on or off + * + * events + * Debug general internal events + * + */ +void static_debug_set(int vtynode, bool onoff, bool events); + + +#endif /* _STATIC_DEBUG_H */ diff --git a/staticd/static_main.c b/staticd/static_main.c index 77243994e2..560814771d 100644 --- a/staticd/static_main.c +++ b/staticd/static_main.c @@ -31,16 +31,20 @@ #include "vrf.h" #include "nexthop.h" #include "filter.h" +#include "routing_nb.h" #include "static_vrf.h" #include "static_vty.h" #include "static_routes.h" #include "static_zebra.h" +#include "static_debug.h" +#include "static_nb.h" char backup_config_file[256]; bool mpls_enabled; + zebra_capabilities_t _caps_p[] = { }; @@ -61,10 +65,12 @@ struct option longopts[] = { { 0 } }; /* Master of threads. */ struct thread_master *master; +static struct frr_daemon_info staticd_di; /* SIGHUP handler. */ static void sighup(void) { zlog_info("SIGHUP received"); + vty_read_config(NULL, staticd_di.config_file, config_default); } /* SIGINT / SIGTERM handler. */ @@ -72,6 +78,10 @@ static void sigint(void) { zlog_notice("Terminating on signal"); + static_vrf_terminate(); + + frr_fini(); + exit(0); } @@ -100,7 +110,12 @@ struct quagga_signal_t static_signals[] = { }, }; -static const struct frr_yang_module_info *staticd_yang_modules[] = { +static const struct frr_yang_module_info *const staticd_yang_modules[] = { + &frr_filter_info, + &frr_interface_info, + &frr_vrf_info, + &frr_routing_info, + &frr_staticd_info, }; #define STATIC_VTY_PORT 2616 @@ -140,12 +155,17 @@ int main(int argc, char **argv, char **envp) master = frr_init(); - access_list_init(); + static_debug_init(); static_vrf_init(); static_zebra_init(); static_vty_init(); + hook_register(routing_conf_event, + routing_control_plane_protocols_name_validate); + + routing_control_plane_protocols_register_vrf_dependency(); + snprintf(backup_config_file, sizeof(backup_config_file), "%s/zebra.conf", frr_sysconfdir); staticd_di.backup_config_file = backup_config_file; diff --git a/staticd/static_memory.c b/staticd/static_memory.c index 77ca4a3439..122cc9fce1 100644 --- a/staticd/static_memory.c +++ b/staticd/static_memory.c @@ -25,4 +25,4 @@ DEFINE_MGROUP(STATIC, "staticd") -DEFINE_MTYPE(STATIC, STATIC_ROUTE, "Static Route"); +DEFINE_MTYPE(STATIC, STATIC_NEXTHOP, "Static Nexthop"); diff --git a/staticd/static_memory.h b/staticd/static_memory.h index 77a0db3b12..077cd0f32b 100644 --- a/staticd/static_memory.h +++ b/staticd/static_memory.h @@ -24,5 +24,7 @@ DECLARE_MGROUP(STATIC) DECLARE_MTYPE(STATIC_ROUTE); +DECLARE_MTYPE(STATIC_NEXTHOP); +DECLARE_MTYPE(STATIC_PATH); #endif diff --git a/staticd/static_nb.c b/staticd/static_nb.c new file mode 100644 index 0000000000..2fdd0d2989 --- /dev/null +++ b/staticd/static_nb.c @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2018 Vmware + * Vishal Dhingra + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include "northbound.h" +#include "libfrr.h" +#include "static_nb.h" + + +/* clang-format off */ + +const struct frr_yang_module_info frr_staticd_info = { + .name = "frr-staticd", + .nodes = { + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list", + .cbs = { + .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_create, + .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_destroy, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list", + .cbs = { + .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_create, + .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_destroy, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/tag", + .cbs = { + .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_tag_modify, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop", + .cbs = { + .apply_finish = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_apply_finish, + .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_create, + .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_destroy, + .pre_validate = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_pre_validate, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/bh-type", + .cbs = { + .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_bh_type_modify, + .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_bh_type_destroy, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/onlink", + .cbs = { + .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_onlink_modify, + .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_onlink_destroy, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/srte-color", + .cbs = { + .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_color_modify, + .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_color_destroy, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry", + .cbs = { + .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_create, + .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_destroy, + + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry/label", + .cbs = { + .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_label_modify, + .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_label_destroy, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry/ttl", + .cbs = { + .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_modify, + .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_destroy, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry/traffic-class", + .cbs = { + .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_modify, + .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_destroy, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list", + .cbs = { + .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_create, + .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_destroy, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list", + .cbs = { + .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_create, + .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_destroy, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/tag", + .cbs = { + .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_tag_modify, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop", + .cbs = { + .apply_finish = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_apply_finish, + .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_create, + .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_destroy, + .pre_validate = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_pre_validate, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/bh-type", + .cbs = { + .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_bh_type_modify, + .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_bh_type_destroy, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/onlink", + .cbs = { + .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_onlink_modify, + .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_onlink_destroy, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/srte-color", + .cbs = { + .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_color_modify, + .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_color_destroy, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry", + .cbs = { + .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_create, + .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_destroy, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry/label", + .cbs = { + .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_label_modify, + .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_label_destroy, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry/ttl", + .cbs = { + .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_modify, + .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_destroy, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry/traffic-class", + .cbs = { + .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_modify, + .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_destroy, + } + }, + { + .xpath = NULL, + }, + } +}; diff --git a/staticd/static_nb.h b/staticd/static_nb.h new file mode 100644 index 0000000000..f4bc1f4ed5 --- /dev/null +++ b/staticd/static_nb.h @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2018 Vmware + * Vishal Dhingra + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef _FRR_STATIC_NB_H_ +#define _FRR_STATIC_NB_H_ + +extern const struct frr_yang_module_info frr_staticd_info; + +/* Mandatory callbacks. */ +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_create( + struct nb_cb_create_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_destroy( + struct nb_cb_destroy_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_create( + struct nb_cb_create_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_destroy( + struct nb_cb_destroy_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_tag_modify( + struct nb_cb_modify_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_create( + struct nb_cb_create_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_destroy( + struct nb_cb_destroy_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_bh_type_modify( + struct nb_cb_modify_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_bh_type_destroy( + struct nb_cb_destroy_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_onlink_modify( + struct nb_cb_modify_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_onlink_destroy( + struct nb_cb_destroy_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_color_modify( + struct nb_cb_modify_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_color_destroy( + struct nb_cb_destroy_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_create( + struct nb_cb_create_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_destroy( + struct nb_cb_destroy_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_label_modify( + struct nb_cb_modify_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_label_destroy( + struct nb_cb_destroy_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_modify( + struct nb_cb_modify_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_destroy( + struct nb_cb_destroy_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_modify( + struct nb_cb_modify_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_destroy( + struct nb_cb_destroy_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_create( + struct nb_cb_create_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_destroy( + struct nb_cb_destroy_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_create( + struct nb_cb_create_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_destroy( + struct nb_cb_destroy_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_tag_modify( + struct nb_cb_modify_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_create( + struct nb_cb_create_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_destroy( + struct nb_cb_destroy_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_bh_type_modify( + struct nb_cb_modify_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_bh_type_destroy( + struct nb_cb_destroy_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_onlink_modify( + struct nb_cb_modify_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_onlink_destroy( + struct nb_cb_destroy_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_color_modify( + struct nb_cb_modify_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_color_destroy( + struct nb_cb_destroy_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_create( + struct nb_cb_create_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_destroy( + struct nb_cb_destroy_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_label_modify( + struct nb_cb_modify_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_label_destroy( + struct nb_cb_destroy_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_modify( + struct nb_cb_modify_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_destroy( + struct nb_cb_destroy_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_modify( + struct nb_cb_modify_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_destroy( + struct nb_cb_destroy_args *args); + +/* Optional 'apply_finish' callbacks. */ + +void routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_apply_finish( + struct nb_cb_apply_finish_args *args); +void routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_apply_finish( + struct nb_cb_apply_finish_args *args); + +/* Optional 'pre_validate' callbacks. */ +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_pre_validate( + struct nb_cb_pre_validate_args *args); + +/* + * Callback registered with routing_nb lib to validate only + * one instance of staticd is allowed + */ +int routing_control_plane_protocols_name_validate( + struct nb_cb_create_args *args); + +/* xpath macros */ +/* route-list */ +#define FRR_STATIC_ROUTE_INFO_KEY_XPATH \ + "/frr-routing:routing/control-plane-protocols/" \ + "control-plane-protocol[type='%s'][name='%s'][vrf='%s']/" \ + "frr-staticd:staticd/route-list[prefix='%s'][afi-safi='%s']/" \ + "path-list[table-id='%u'][distance='%u']" + +#define FRR_STATIC_ROUTE_INFO_KEY_NO_DISTANCE_XPATH \ + "/frr-routing:routing/control-plane-protocols/" \ + "control-plane-protocol[type='%s'][name='%s'][vrf='%s']/" \ + "frr-staticd:staticd/route-list[prefix='%s'][afi-safi='%s']/" \ + "path-list[table-id='%u']" + + +#define FRR_STATIC_ROUTE_PATH_TAG_XPATH "/tag" + +/* route-list/frr-nexthops */ +#define FRR_STATIC_ROUTE_NH_KEY_XPATH \ + "/frr-nexthops/" \ + "nexthop[nh-type='%s'][vrf='%s'][gateway='%s'][interface='%s']" + +#define FRR_STATIC_ROUTE_NH_ONLINK_XPATH "/onlink" + +#define FRR_STATIC_ROUTE_NH_COLOR_XPATH "/srte-color" + +#define FRR_STATIC_ROUTE_NH_BH_XPATH "/bh-type" + +#define FRR_STATIC_ROUTE_NH_LABEL_XPATH "/mpls-label-stack" + +#define FRR_STATIC_ROUTE_NHLB_KEY_XPATH "/entry[id='%u']/label" + +/* route-list/srclist */ +#define FRR_S_ROUTE_SRC_INFO_KEY_XPATH \ + "/frr-routing:routing/control-plane-protocols/" \ + "control-plane-protocol[type='%s'][name='%s'][vrf='%s']/" \ + "frr-staticd:staticd/route-list[prefix='%s'][afi-safi='%s']/" \ + "src-list[src-prefix='%s']/path-list[table-id='%u'][distance='%u']" + +#define FRR_S_ROUTE_SRC_INFO_KEY_NO_DISTANCE_XPATH \ + "/frr-routing:routing/control-plane-protocols/" \ + "control-plane-protocol[type='%s'][name='%s'][vrf='%s']/" \ + "frr-staticd:staticd/route-list[prefix='%s'][afi-safi='%s']/" \ + "src-list[src-prefix='%s']/path-list[table-id='%u']" + +/* route-list/frr-nexthops */ +#define FRR_DEL_S_ROUTE_NH_KEY_XPATH \ + FRR_STATIC_ROUTE_INFO_KEY_XPATH \ + FRR_STATIC_ROUTE_NH_KEY_XPATH + +/* route-list/frr-nexthops */ +#define FRR_DEL_S_ROUTE_NH_KEY_NO_DISTANCE_XPATH \ + FRR_STATIC_ROUTE_INFO_KEY_NO_DISTANCE_XPATH \ + FRR_STATIC_ROUTE_NH_KEY_XPATH + +/* route-list/src/src-list/frr-nexthops*/ +#define FRR_DEL_S_ROUTE_SRC_NH_KEY_XPATH \ + FRR_S_ROUTE_SRC_INFO_KEY_XPATH \ + FRR_STATIC_ROUTE_NH_KEY_XPATH + +/* route-list/src/src-list/frr-nexthops*/ +#define FRR_DEL_S_ROUTE_SRC_NH_KEY_NO_DISTANCE_XPATH \ + FRR_S_ROUTE_SRC_INFO_KEY_NO_DISTANCE_XPATH \ + FRR_STATIC_ROUTE_NH_KEY_XPATH + +#endif diff --git a/staticd/static_nb_config.c b/staticd/static_nb_config.c new file mode 100644 index 0000000000..8d3ec1c3a8 --- /dev/null +++ b/staticd/static_nb_config.c @@ -0,0 +1,1243 @@ +/* + * Copyright (C) 2018 Vmware + * Vishal Dhingra + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include "northbound.h" +#include "libfrr.h" +#include "log.h" +#include "lib_errors.h" +#include "prefix.h" +#include "table.h" +#include "vrf.h" +#include "nexthop.h" +#include "srcdest_table.h" + +#include "static_vrf.h" +#include "static_routes.h" +#include "static_nb.h" + + +static int static_path_list_create(struct nb_cb_create_args *args) +{ + struct route_node *rn; + struct static_path *pn; + const struct lyd_node *vrf_dnode; + const char *vrf; + uint8_t distance; + uint32_t table_id; + + switch (args->event) { + case NB_EV_VALIDATE: + vrf_dnode = yang_dnode_get_parent(args->dnode, + "control-plane-protocol"); + vrf = yang_dnode_get_string(vrf_dnode, "./vrf"); + table_id = yang_dnode_get_uint32(args->dnode, "./table-id"); + + /* + * TableId is not applicable for VRF. Consider the case of + * l3mdev, there is one uint32_t space to work with. + * A l3mdev device points at a specific table that it + * relates to and a set of interfaces it belongs to. + */ + if (table_id && (strcmp(vrf, vrf_get_default_name()) != 0) + && !vrf_is_backend_netns()) { + snprintf( + args->errmsg, args->errmsg_len, + "%% table param only available when running on netns-based vrfs"); + return NB_ERR_VALIDATION; + } + break; + case NB_EV_ABORT: + case NB_EV_PREPARE: + break; + case NB_EV_APPLY: + rn = nb_running_get_entry(args->dnode, NULL, true); + distance = yang_dnode_get_uint8(args->dnode, "./distance"); + table_id = yang_dnode_get_uint32(args->dnode, "./table-id"); + pn = static_add_path(rn, table_id, distance); + nb_running_set_entry(args->dnode, pn); + } + + return NB_OK; +} + +static void static_path_list_destroy(struct nb_cb_destroy_args *args, + const struct lyd_node *rn_dnode, + struct stable_info *info) +{ + struct route_node *rn; + struct static_path *pn; + + pn = nb_running_unset_entry(args->dnode); + rn = nb_running_get_entry(rn_dnode, NULL, true); + static_del_path(rn, pn, info->safi, info->svrf); +} + +static void static_path_list_tag_modify(struct nb_cb_modify_args *args, + const struct lyd_node *rn_dnode, + struct stable_info *info) +{ + struct static_path *pn; + struct route_node *rn; + route_tag_t tag; + + tag = yang_dnode_get_uint32(args->dnode, NULL); + pn = nb_running_get_entry(args->dnode, NULL, true); + pn->tag = tag; + rn = nb_running_get_entry(rn_dnode, NULL, true); + + static_install_path(rn, pn, info->safi, info->svrf); +} + +static bool static_nexthop_create(struct nb_cb_create_args *args, + const struct lyd_node *rn_dnode, + struct stable_info *info) +{ + struct route_node *rn; + struct static_path *pn; + struct ipaddr ipaddr; + struct static_nexthop *nh; + int nh_type; + const char *ifname; + const char *nh_vrf; + + switch (args->event) { + case NB_EV_VALIDATE: + ifname = yang_dnode_get_string(args->dnode, "./interface"); + if (ifname != NULL) { + if (strcasecmp(ifname, "Null0") == 0 + || strcasecmp(ifname, "reject") == 0 + || strcasecmp(ifname, "blackhole") == 0) { + snprintf(args->errmsg, args->errmsg_len, + "%s: Nexthop interface name can not be from reserved keywords(Null0, reject, blackhole)", + ifname); + return NB_ERR_VALIDATION; + } + } + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + yang_dnode_get_ip(&ipaddr, args->dnode, "./gateway"); + nh_type = yang_dnode_get_enum(args->dnode, "./nh-type"); + ifname = yang_dnode_get_string(args->dnode, "./interface"); + nh_vrf = yang_dnode_get_string(args->dnode, "./vrf"); + pn = nb_running_get_entry(args->dnode, NULL, true); + rn = nb_running_get_entry(rn_dnode, NULL, true); + + if (!static_add_nexthop_validate(nh_vrf, nh_type, &ipaddr)) + flog_warn( + EC_LIB_NB_CB_CONFIG_VALIDATE, + "Warning!! Local connected address is configured as Gateway IP((%s))", + yang_dnode_get_string(args->dnode, + "./gateway")); + nh = static_add_nexthop(rn, pn, info->safi, info->svrf, nh_type, + &ipaddr, ifname, nh_vrf, 0); + nb_running_set_entry(args->dnode, nh); + break; + } + + return NB_OK; +} + +static bool static_nexthop_destroy(struct nb_cb_destroy_args *args, + const struct lyd_node *rn_dnode, + struct stable_info *info) +{ + struct route_node *rn; + struct static_path *pn; + const struct lyd_node *pn_dnode; + struct static_nexthop *nh; + int ret; + + nh = nb_running_unset_entry(args->dnode); + pn_dnode = yang_dnode_get_parent(args->dnode, "path-list"); + pn = nb_running_get_entry(pn_dnode, NULL, true); + rn = nb_running_get_entry(rn_dnode, NULL, true); + + ret = static_delete_nexthop(rn, pn, info->safi, info->svrf, nh); + if (!ret) { + char buf[SRCDEST2STR_BUFFER]; + + flog_warn(EC_LIB_NB_CB_CONFIG_APPLY, + "%s : nh [%d:%s:%s:%s] nexthop destroy failed", + srcdest_rnode2str(rn, buf, sizeof(buf)), + yang_dnode_get_enum(args->dnode, "./nh-type"), + yang_dnode_get_string(args->dnode, "./interface"), + yang_dnode_get_string(args->dnode, "./gateway"), + yang_dnode_get_string(args->dnode, "./vrf")); + return NB_ERR; + } + + return NB_OK; +} + +static int nexthop_mpls_label_stack_entry_create(struct nb_cb_create_args *args) +{ + struct static_nexthop *nh; + uint32_t pos; + uint8_t index; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + nh = nb_running_get_entry(args->dnode, NULL, true); + pos = yang_get_list_pos(args->dnode); + if (!pos) { + flog_warn(EC_LIB_NB_CB_CONFIG_APPLY, + "libyang returns invalid label position"); + return NB_ERR; + } + /* Mapping to array = list-index -1 */ + index = pos - 1; + nh->snh_label.label[index] = 0; + nh->snh_label.num_labels++; + break; + } + + return NB_OK; +} + +static int +nexthop_mpls_label_stack_entry_destroy(struct nb_cb_destroy_args *args) +{ + struct static_nexthop *nh; + uint32_t pos; + uint8_t index; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + nh = nb_running_get_entry(args->dnode, NULL, true); + pos = yang_get_list_pos(args->dnode); + if (!pos) { + flog_warn(EC_LIB_NB_CB_CONFIG_APPLY, + "libyang returns invalid label position"); + return NB_ERR; + } + index = pos - 1; + nh->snh_label.label[index] = 0; + nh->snh_label.num_labels--; + break; + } + + return NB_OK; +} + +static int static_nexthop_mpls_label_modify(struct nb_cb_modify_args *args) +{ + struct static_nexthop *nh; + uint32_t pos; + uint8_t index; + + nh = nb_running_get_entry(args->dnode, NULL, true); + pos = yang_get_list_pos(args->dnode->parent); + if (!pos) { + flog_warn(EC_LIB_NB_CB_CONFIG_APPLY, + "libyang returns invalid label position"); + return NB_ERR; + } + /* Mapping to array = list-index -1 */ + index = pos - 1; + nh->snh_label.label[index] = yang_dnode_get_uint32(args->dnode, NULL); + + return NB_OK; +} + +static int static_nexthop_onlink_modify(struct nb_cb_modify_args *args) +{ + struct static_nexthop *nh; + + nh = nb_running_get_entry(args->dnode, NULL, true); + nh->onlink = yang_dnode_get_bool(args->dnode, NULL); + + return NB_OK; +} + +static int static_nexthop_color_modify(struct nb_cb_modify_args *args) +{ + struct static_nexthop *nh; + + nh = nb_running_get_entry(args->dnode, NULL, true); + nh->color = yang_dnode_get_uint32(args->dnode, NULL); + + return NB_OK; +} + +static int static_nexthop_color_destroy(struct nb_cb_destroy_args *args) +{ + struct static_nexthop *nh; + + nh = nb_running_unset_entry(args->dnode); + nh->color = 0; + + return NB_OK; +} + +static int static_nexthop_bh_type_modify(struct nb_cb_modify_args *args) +{ + struct static_nexthop *nh; + + nh = nb_running_get_entry(args->dnode, NULL, true); + nh->bh_type = yang_dnode_get_enum(args->dnode, NULL); + + return NB_OK; +} + + +void routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_apply_finish( + struct nb_cb_apply_finish_args *args) +{ + struct static_nexthop *nh; + struct static_path *pn; + struct route_node *rn; + const struct lyd_node *pn_dnode; + const struct lyd_node *rn_dnode; + const char *ifname; + const char *nh_vrf; + struct stable_info *info; + int nh_type; + + nh_type = yang_dnode_get_enum(args->dnode, "./nh-type"); + ifname = yang_dnode_get_string(args->dnode, "./interface"); + nh_vrf = yang_dnode_get_string(args->dnode, "./vrf"); + + nh = nb_running_get_entry(args->dnode, NULL, true); + + pn_dnode = yang_dnode_get_parent(args->dnode, "path-list"); + pn = nb_running_get_entry(pn_dnode, NULL, true); + + rn_dnode = yang_dnode_get_parent(pn_dnode, "route-list"); + rn = nb_running_get_entry(rn_dnode, NULL, true); + info = route_table_get_info(rn->table); + + static_install_nexthop(rn, pn, nh, info->safi, info->svrf, ifname, + nh_type, nh_vrf); +} + + +void routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_apply_finish( + struct nb_cb_apply_finish_args *args) +{ + struct static_nexthop *nh; + struct static_path *pn; + struct route_node *rn; + struct route_node *src_rn; + const struct lyd_node *pn_dnode; + const struct lyd_node *rn_dnode; + const struct lyd_node *src_dnode; + const char *ifname; + const char *nh_vrf; + struct stable_info *info; + int nh_type; + + nh_type = yang_dnode_get_enum(args->dnode, "./nh-type"); + ifname = yang_dnode_get_string(args->dnode, "./interface"); + nh_vrf = yang_dnode_get_string(args->dnode, "./vrf"); + + nh = nb_running_get_entry(args->dnode, NULL, true); + + pn_dnode = yang_dnode_get_parent(args->dnode, "path-list"); + pn = nb_running_get_entry(pn_dnode, NULL, true); + + src_dnode = yang_dnode_get_parent(pn_dnode, "src-list"); + src_rn = nb_running_get_entry(src_dnode, NULL, true); + + rn_dnode = yang_dnode_get_parent(src_dnode, "route-list"); + rn = nb_running_get_entry(rn_dnode, NULL, true); + info = route_table_get_info(rn->table); + + static_install_nexthop(src_rn, pn, nh, info->safi, info->svrf, ifname, + nh_type, nh_vrf); +} +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_pre_validate( + struct nb_cb_pre_validate_args *args) +{ + const struct lyd_node *mls_dnode; + uint32_t count; + + mls_dnode = yang_dnode_get(args->dnode, "./mpls-label-stack"); + count = yang_get_list_elements_count(yang_dnode_get_child(mls_dnode)); + + if (count > MPLS_MAX_LABELS) { + snprintf(args->errmsg, args->errmsg_len, + "Too many labels, Enter %d or fewer", + MPLS_MAX_LABELS); + return NB_ERR_VALIDATION; + } + return NB_OK; +} + +int routing_control_plane_protocols_name_validate( + struct nb_cb_create_args *args) +{ + const char *name; + + name = yang_dnode_get_string(args->dnode, "./name"); + if (!strmatch(name, "staticd")) { + snprintf(args->errmsg, args->errmsg_len, + "static routing supports only one instance with name staticd"); + return NB_ERR_VALIDATION; + } + return NB_OK; +} +/* + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list + */ +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_create( + struct nb_cb_create_args *args) +{ + struct vrf *vrf; + struct static_vrf *s_vrf; + struct route_node *rn; + const struct lyd_node *vrf_dnode; + struct prefix prefix; + const char *afi_safi; + afi_t prefix_afi; + afi_t afi; + safi_t safi; + + switch (args->event) { + case NB_EV_VALIDATE: + yang_dnode_get_prefix(&prefix, args->dnode, "./prefix"); + afi_safi = yang_dnode_get_string(args->dnode, "./afi-safi"); + yang_afi_safi_identity2value(afi_safi, &afi, &safi); + prefix_afi = family2afi(prefix.family); + if (afi != prefix_afi) { + flog_warn( + EC_LIB_NB_CB_CONFIG_VALIDATE, + "route node %s creation failed", + yang_dnode_get_string(args->dnode, "./prefix")); + return NB_ERR_VALIDATION; + } + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + vrf_dnode = yang_dnode_get_parent(args->dnode, + "control-plane-protocol"); + vrf = nb_running_get_entry(vrf_dnode, NULL, true); + s_vrf = vrf->info; + + yang_dnode_get_prefix(&prefix, args->dnode, "./prefix"); + afi_safi = yang_dnode_get_string(args->dnode, "./afi-safi"); + yang_afi_safi_identity2value(afi_safi, &afi, &safi); + + rn = static_add_route(afi, safi, &prefix, NULL, s_vrf); + if (!rn) { + flog_warn( + EC_LIB_NB_CB_CONFIG_APPLY, + "route node %s creation failed", + yang_dnode_get_string(args->dnode, "./prefix")); + return NB_ERR; + } + nb_running_set_entry(args->dnode, rn); + break; + } + return NB_OK; +} + +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_destroy( + struct nb_cb_destroy_args *args) +{ + struct route_node *rn; + struct stable_info *info; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + rn = nb_running_unset_entry(args->dnode); + info = route_table_get_info(rn->table); + static_del_route(rn, info->safi, info->svrf); + break; + } + return NB_OK; +} + +/* + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list + */ +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_create( + struct nb_cb_create_args *args) +{ + return static_path_list_create(args); +} + +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_destroy( + struct nb_cb_destroy_args *args) +{ + const struct lyd_node *rn_dnode; + struct route_node *rn; + struct stable_info *info; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + rn_dnode = yang_dnode_get_parent(args->dnode, "route-list"); + rn = nb_running_get_entry(rn_dnode, NULL, true); + info = route_table_get_info(rn->table); + static_path_list_destroy(args, rn_dnode, info); + break; + } + return NB_OK; +} + +/* + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/tag + */ +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_tag_modify( + struct nb_cb_modify_args *args) +{ + struct stable_info *info; + struct route_node *rn; + const struct lyd_node *rn_dnode; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_ABORT: + case NB_EV_PREPARE: + break; + case NB_EV_APPLY: + rn_dnode = yang_dnode_get_parent(args->dnode, "route-list"); + rn = nb_running_get_entry(rn_dnode, NULL, true); + info = route_table_get_info(rn->table); + static_path_list_tag_modify(args, rn_dnode, info); + break; + } + + return NB_OK; +} + +/* + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop + */ +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_create( + struct nb_cb_create_args *args) +{ + struct route_node *rn; + const struct lyd_node *rn_dnode; + struct stable_info *info; + + switch (args->event) { + case NB_EV_VALIDATE: + rn_dnode = yang_dnode_get_parent(args->dnode, "route-list"); + if (static_nexthop_create(args, rn_dnode, NULL) != NB_OK) + return NB_ERR_VALIDATION; + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + rn_dnode = yang_dnode_get_parent(args->dnode, "route-list"); + rn = nb_running_get_entry(rn_dnode, NULL, true); + info = route_table_get_info(rn->table); + + if (static_nexthop_create(args, rn_dnode, info) != NB_OK) + return NB_ERR_VALIDATION; + break; + } + return NB_OK; +} + +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_destroy( + struct nb_cb_destroy_args *args) +{ + struct route_node *rn; + const struct lyd_node *rn_dnode; + struct stable_info *info; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + rn_dnode = yang_dnode_get_parent(args->dnode, "route-list"); + rn = nb_running_get_entry(rn_dnode, NULL, true); + info = route_table_get_info(rn->table); + + if (static_nexthop_destroy(args, rn_dnode, info) != NB_OK) + return NB_ERR; + break; + } + return NB_OK; +} + +/* + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/bh-type + */ +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_bh_type_modify( + struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + if (static_nexthop_bh_type_modify(args) != NB_OK) + return NB_ERR; + break; + } + return NB_OK; +} + +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_bh_type_destroy( + struct nb_cb_destroy_args *args) +{ + /* blackhole type has a boolean type with default value, + * so no need to do any operations in destroy callback + */ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + break; + } + return NB_OK; +} + +/* + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/onlink + */ +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_onlink_modify( + struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + if (static_nexthop_onlink_modify(args) != NB_OK) + return NB_ERR; + + break; + } + return NB_OK; +} +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_onlink_destroy( + struct nb_cb_destroy_args *args) +{ + /* onlink has a boolean type with default value, + * so no need to do any operations in destroy callback + */ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + break; + } + return NB_OK; +} + +/* + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/srte-color + */ +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_color_modify( + struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + if (static_nexthop_color_modify(args) != NB_OK) + return NB_ERR; + + break; + } + return NB_OK; +} + +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_color_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + if (static_nexthop_color_destroy(args) != NB_OK) + return NB_ERR; + break; + } + return NB_OK; +} + +/* + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry + */ +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_create( + struct nb_cb_create_args *args) +{ + return nexthop_mpls_label_stack_entry_create(args); +} + +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_destroy( + struct nb_cb_destroy_args *args) +{ + return nexthop_mpls_label_stack_entry_destroy(args); +} + +/* + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry/label + */ +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_label_modify( + struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + if (static_nexthop_mpls_label_modify(args) != NB_OK) + return NB_ERR; + break; + } + return NB_OK; +} + +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_label_destroy( + struct nb_cb_destroy_args *args) +{ + /* + * No operation is required in this call back. + * nexthop_mpls_label_stack_entry_destroy() will take care + * to reset the label vaue. + */ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + break; + } + return NB_OK; +} + +/* + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry/ttl + */ +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_modify( + struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + break; + } + + return NB_OK; +} + +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + break; + } + + return NB_OK; +} + +/* + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry/traffic-class + */ +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_modify( + struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + break; + } + + return NB_OK; +} + +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + break; + } + + return NB_OK; +} + +/* + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list + */ +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_create( + struct nb_cb_create_args *args) +{ + struct static_vrf *s_vrf; + struct route_node *rn; + struct route_node *src_rn; + struct prefix_ipv6 src_prefix = {}; + struct stable_info *info; + afi_t afi; + safi_t safi = SAFI_UNICAST; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + rn = nb_running_get_entry(args->dnode, NULL, true); + info = route_table_get_info(rn->table); + s_vrf = info->svrf; + yang_dnode_get_ipv6p(&src_prefix, args->dnode, "./src-prefix"); + afi = family2afi(src_prefix.family); + src_rn = + static_add_route(afi, safi, &rn->p, &src_prefix, s_vrf); + if (!src_rn) { + flog_warn(EC_LIB_NB_CB_CONFIG_APPLY, + "src rn %s creation failed", + yang_dnode_get_string(args->dnode, + "./src-prefix")); + return NB_ERR; + } + nb_running_set_entry(args->dnode, src_rn); + break; + } + return NB_OK; +} + +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_destroy( + struct nb_cb_destroy_args *args) +{ + struct route_node *src_rn; + struct route_node *rn; + struct stable_info *info; + const struct lyd_node *rn_dnode; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + src_rn = nb_running_unset_entry(args->dnode); + rn_dnode = yang_dnode_get_parent(args->dnode, "route-list"); + rn = nb_running_get_entry(rn_dnode, NULL, true); + info = route_table_get_info(rn->table); + static_del_route(src_rn, info->safi, info->svrf); + break; + } + + return NB_OK; +} + +/* + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list + */ +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_create( + struct nb_cb_create_args *args) +{ + return static_path_list_create(args); +} + +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_destroy( + struct nb_cb_destroy_args *args) +{ + struct route_node *rn; + const struct lyd_node *rn_dnode; + const struct lyd_node *srn_dnode; + struct stable_info *info; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + srn_dnode = yang_dnode_get_parent(args->dnode, "src-list"); + rn_dnode = yang_dnode_get_parent(srn_dnode, "route-list"); + rn = nb_running_get_entry(rn_dnode, NULL, true); + info = route_table_get_info(rn->table); + static_path_list_destroy(args, srn_dnode, info); + break; + } + return NB_OK; +} + +/* + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/tag + */ +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_tag_modify( + struct nb_cb_modify_args *args) +{ + struct stable_info *info; + struct route_node *rn; + const struct lyd_node *srn_dnode; + const struct lyd_node *rn_dnode; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_ABORT: + case NB_EV_PREPARE: + break; + case NB_EV_APPLY: + srn_dnode = yang_dnode_get_parent(args->dnode, "src-list"); + rn_dnode = yang_dnode_get_parent(srn_dnode, "route-list"); + rn = nb_running_get_entry(rn_dnode, NULL, true); + info = route_table_get_info(rn->table); + static_path_list_tag_modify(args, srn_dnode, info); + break; + } + return NB_OK; +} + +/* + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop + */ +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_create( + struct nb_cb_create_args *args) +{ + struct route_node *rn; + const struct lyd_node *rn_dnode; + const struct lyd_node *src_dnode; + struct stable_info *info; + + switch (args->event) { + case NB_EV_VALIDATE: + rn_dnode = yang_dnode_get_parent(args->dnode, "route-list"); + if (static_nexthop_create(args, rn_dnode, NULL) != NB_OK) + return NB_ERR_VALIDATION; + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + src_dnode = yang_dnode_get_parent(args->dnode, "src-list"); + rn_dnode = yang_dnode_get_parent(src_dnode, "route-list"); + rn = nb_running_get_entry(rn_dnode, NULL, true); + info = route_table_get_info(rn->table); + + if (static_nexthop_create(args, src_dnode, info) != NB_OK) + return NB_ERR_VALIDATION; + + break; + } + return NB_OK; +} + +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_destroy( + struct nb_cb_destroy_args *args) +{ + struct route_node *rn; + const struct lyd_node *rn_dnode; + const struct lyd_node *src_dnode; + struct stable_info *info; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + src_dnode = yang_dnode_get_parent(args->dnode, "src-list"); + rn_dnode = yang_dnode_get_parent(src_dnode, "route-list"); + rn = nb_running_get_entry(rn_dnode, NULL, true); + info = route_table_get_info(rn->table); + + if (static_nexthop_destroy(args, rn_dnode, info) != NB_OK) + return NB_ERR; + break; + } + return NB_OK; +} + +/* + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/bh-type + */ +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_bh_type_modify( + struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + if (static_nexthop_bh_type_modify(args) != NB_OK) + return NB_ERR; + break; + } + return NB_OK; +} + +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_bh_type_destroy( + struct nb_cb_destroy_args *args) +{ + /* blackhole type has a boolean type with default value, + * so no need to do any operations in destroy callback + */ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + break; + } + + return NB_OK; +} + +/* + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/onlink + */ +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_onlink_modify( + struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + if (static_nexthop_onlink_modify(args) != NB_OK) + return NB_ERR; + + break; + } + return NB_OK; +} + + +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_onlink_destroy( + struct nb_cb_destroy_args *args) +{ + /* onlink has a boolean type with default value, + * so no need to do any operations in destroy callback + */ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + break; + } + return NB_OK; +} + +/* + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/srte-color + */ +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_color_modify( + struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + if (static_nexthop_color_modify(args) != NB_OK) + return NB_ERR; + + break; + } + return NB_OK; +} + + +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_color_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + if (static_nexthop_color_destroy(args) != NB_OK) + return NB_ERR; + break; + } + return NB_OK; +} + +/* + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry + */ +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_create( + struct nb_cb_create_args *args) +{ + return nexthop_mpls_label_stack_entry_create(args); +} + +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_destroy( + struct nb_cb_destroy_args *args) +{ + return nexthop_mpls_label_stack_entry_destroy(args); +} + +/* + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry/label + */ +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_label_modify( + struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + if (static_nexthop_mpls_label_modify(args) != NB_OK) + return NB_ERR; + break; + } + return NB_OK; +} + +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_label_destroy( + struct nb_cb_destroy_args *args) +{ + /* + * No operation is required in this call back. + * nexthop_mpls_label_stack_entry_destroy() will take care + * to reset the label vaue. + */ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + break; + } + return NB_OK; +} + +/* + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry/ttl + */ +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_modify( + struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + break; + } + + return NB_OK; +} + +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + break; + } + + return NB_OK; +} + +/* + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry/traffic-class + */ +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_modify( + struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + break; + } + + return NB_OK; +} + +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + break; + } + + return NB_OK; +} diff --git a/staticd/static_nht.c b/staticd/static_nht.c index 9af30a587a..feb6e0f993 100644 --- a/staticd/static_nht.c +++ b/staticd/static_nht.c @@ -23,22 +23,52 @@ #include "table.h" #include "vrf.h" #include "nexthop.h" +#include "srcdest_table.h" #include "static_vrf.h" #include "static_routes.h" #include "static_zebra.h" #include "static_nht.h" -static void static_nht_update_safi(struct prefix *p, uint32_t nh_num, - afi_t afi, safi_t safi, struct vrf *vrf, - vrf_id_t nh_vrf_id) +static void static_nht_update_path(struct route_node *rn, + struct static_path *pn, struct prefix *nhp, + uint32_t nh_num, vrf_id_t nh_vrf_id, + struct vrf *vrf, safi_t safi) +{ + struct static_nexthop *nh; + + frr_each(static_nexthop_list, &pn->nexthop_list, nh) { + if (nh->nh_vrf_id != nh_vrf_id) + continue; + + if (nh->type != STATIC_IPV4_GATEWAY + && nh->type != STATIC_IPV4_GATEWAY_IFNAME + && nh->type != STATIC_IPV6_GATEWAY + && nh->type != STATIC_IPV6_GATEWAY_IFNAME) + continue; + + if (nhp->family == AF_INET + && nhp->u.prefix4.s_addr == nh->addr.ipv4.s_addr) + nh->nh_valid = !!nh_num; + + if (nhp->family == AF_INET6 + && memcmp(&nhp->u.prefix6, &nh->addr.ipv6, 16) == 0) + nh->nh_valid = !!nh_num; + + if (nh->state == STATIC_START) + static_zebra_route_add(rn, pn, safi, true); + } +} + +static void static_nht_update_safi(struct prefix *sp, struct prefix *nhp, + uint32_t nh_num, afi_t afi, safi_t safi, + struct vrf *vrf, vrf_id_t nh_vrf_id) { struct route_table *stable; - struct static_route *si; struct static_vrf *svrf; struct route_node *rn; - bool orig; - bool reinstall; + struct static_path *pn; + struct static_route_info *si; svrf = vrf->info; if (!svrf) @@ -48,49 +78,155 @@ static void static_nht_update_safi(struct prefix *p, uint32_t nh_num, if (!stable) return; - for (rn = route_top(stable); rn; rn = route_next(rn)) { - reinstall = false; - for (si = rn->info; si; si = si->next) { - if (si->nh_vrf_id != nh_vrf_id) - continue; - - if (si->type != STATIC_IPV4_GATEWAY - && si->type != STATIC_IPV4_GATEWAY_IFNAME - && si->type != STATIC_IPV6_GATEWAY - && si->type != STATIC_IPV6_GATEWAY_IFNAME) - continue; - - orig = si->nh_valid; - if (p->family == AF_INET - && p->u.prefix4.s_addr == si->addr.ipv4.s_addr) - si->nh_valid = !!nh_num; - - if (p->family == AF_INET6 - && memcmp(&p->u.prefix6, &si->addr.ipv6, 16) == 0) - si->nh_valid = !!nh_num; - - if (orig != si->nh_valid) - reinstall = true; - - if (reinstall) { - static_zebra_route_add(rn, si, vrf->vrf_id, - safi, true); - reinstall = false; + if (sp) { + rn = srcdest_rnode_lookup(stable, sp, NULL); + if (rn && rn->info) { + si = static_route_info_from_rnode(rn); + frr_each(static_path_list, &si->path_list, pn) { + static_nht_update_path(rn, pn, nhp, nh_num, + nh_vrf_id, vrf, safi); } + route_unlock_node(rn); + } + return; + } + + for (rn = route_top(stable); rn; rn = route_next(rn)) { + si = static_route_info_from_rnode(rn); + if (!si) + continue; + frr_each(static_path_list, &si->path_list, pn) { + static_nht_update_path(rn, pn, nhp, nh_num, nh_vrf_id, + vrf, safi); } } } -void static_nht_update(struct prefix *p, uint32_t nh_num, afi_t afi, - vrf_id_t nh_vrf_id) +void static_nht_update(struct prefix *sp, struct prefix *nhp, + uint32_t nh_num, afi_t afi, vrf_id_t nh_vrf_id) { struct vrf *vrf; RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { - static_nht_update_safi(p, nh_num, afi, SAFI_UNICAST, + static_nht_update_safi(sp, nhp, nh_num, afi, SAFI_UNICAST, vrf, nh_vrf_id); - static_nht_update_safi(p, nh_num, afi, SAFI_MULTICAST, + static_nht_update_safi(sp, nhp, nh_num, afi, SAFI_MULTICAST, vrf, nh_vrf_id); } } + +static void static_nht_reset_start_safi(struct prefix *nhp, afi_t afi, + safi_t safi, struct vrf *vrf, + vrf_id_t nh_vrf_id) +{ + struct static_vrf *svrf; + struct route_table *stable; + struct static_nexthop *nh; + struct static_path *pn; + struct route_node *rn; + struct static_route_info *si; + + svrf = vrf->info; + if (!svrf) + return; + + stable = static_vrf_static_table(afi, safi, svrf); + if (!stable) + return; + + for (rn = route_top(stable); rn; rn = route_next(rn)) { + si = static_route_info_from_rnode(rn); + if (!si) + continue; + frr_each(static_path_list, &si->path_list, pn) { + frr_each(static_nexthop_list, &pn->nexthop_list, nh) { + if (nh->nh_vrf_id != nh_vrf_id) + continue; + + if (nhp->family == AF_INET + && nhp->u.prefix4.s_addr + != nh->addr.ipv4.s_addr) + continue; + + if (nhp->family == AF_INET6 + && memcmp(&nhp->u.prefix6, &nh->addr.ipv6, + 16) + != 0) + continue; + + /* + * We've been told that a nexthop we + * depend on has changed in some manner, + * so reset the state machine to allow + * us to start over. + */ + nh->state = STATIC_START; + } + } + } +} + +void static_nht_reset_start(struct prefix *nhp, afi_t afi, vrf_id_t nh_vrf_id) +{ + struct vrf *vrf; + + RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { + static_nht_reset_start_safi(nhp, afi, SAFI_UNICAST, + vrf, nh_vrf_id); + static_nht_reset_start_safi(nhp, afi, SAFI_MULTICAST, + vrf, nh_vrf_id); + } +} + +static void static_nht_mark_state_safi(struct prefix *sp, afi_t afi, + safi_t safi, struct vrf *vrf, + enum static_install_states state) +{ + struct static_vrf *svrf; + struct route_table *stable; + struct route_node *rn; + struct static_nexthop *nh; + struct static_path *pn; + struct static_route_info *si; + + svrf = vrf->info; + if (!svrf) + return; + + stable = static_vrf_static_table(afi, safi, svrf); + if (!stable) + return; + + rn = srcdest_rnode_lookup(stable, sp, NULL); + if (!rn) + return; + si = rn->info; + if (si) { + frr_each(static_path_list, &si->path_list, pn) { + frr_each(static_nexthop_list, &pn->nexthop_list, nh) { + nh->state = state; + } + } + } + + route_unlock_node(rn); +} + +void static_nht_mark_state(struct prefix *sp, vrf_id_t vrf_id, + enum static_install_states state) +{ + struct vrf *vrf; + + afi_t afi = AFI_IP; + + if (sp->family == AF_INET6) + afi = AFI_IP6; + + vrf = vrf_lookup_by_id(vrf_id); + if (!vrf || !vrf->info) + return; + + static_nht_mark_state_safi(sp, afi, SAFI_UNICAST, vrf, state); + static_nht_mark_state_safi(sp, afi, SAFI_MULTICAST, vrf, state); +} diff --git a/staticd/static_nht.h b/staticd/static_nht.h index f273c71bba..18bb9e39ca 100644 --- a/staticd/static_nht.h +++ b/staticd/static_nht.h @@ -20,6 +20,31 @@ #ifndef __STATIC_NHT_H__ #define __STATIC_NHT_H__ -extern void static_nht_update(struct prefix *p, uint32_t nh_num, - afi_t afi, vrf_id_t vrf_id); +/* + * When we get notification that nexthop tracking has an answer for + * us call this function to find the nexthop we are tracking so it + * can be installed or removed. + * + * sp -> The route we are looking at. If NULL then look at all + * routes. + * nhp -> The nexthop that is being tracked. + * nh_num -> number of valid nexthops. + * afi -> The afi we are working in. + * vrf_id -> The vrf the nexthop is in. + */ +extern void static_nht_update(struct prefix *sp, struct prefix *nhp, + uint32_t nh_num, afi_t afi, vrf_id_t vrf_id); + +/* + * For the given tracked nexthop, nhp, mark all routes that use + * this route as in starting state again. + */ +extern void static_nht_reset_start(struct prefix *nhp, afi_t afi, + vrf_id_t nh_vrf_id); + +/* + * For the given prefix, sp, mark it as in a particular state + */ +extern void static_nht_mark_state(struct prefix *sp, vrf_id_t vrf_id, + enum static_install_states state); #endif diff --git a/staticd/static_routes.c b/staticd/static_routes.c index cde31df14f..42e13d8efd 100644 --- a/staticd/static_routes.c +++ b/staticd/static_routes.c @@ -32,248 +32,354 @@ #include "static_memory.h" #include "static_zebra.h" -/* Install static route into rib. */ -static void static_install_route(struct route_node *rn, - struct static_route *si_changed, safi_t safi) -{ - struct static_route *si; +DEFINE_MTYPE(STATIC, STATIC_ROUTE, "Static Route Info"); +DEFINE_MTYPE(STATIC, STATIC_PATH, "Static Path"); - for (si = rn->info; si; si = si->next) - static_zebra_nht_register(si, true); +/* Install static path into rib. */ +void static_install_path(struct route_node *rn, struct static_path *pn, + safi_t safi, struct static_vrf *svrf) +{ + struct static_nexthop *nh; - si = rn->info; - if (si) - static_zebra_route_add(rn, si_changed, si->vrf_id, safi, true); + frr_each(static_nexthop_list, &pn->nexthop_list, nh) + static_zebra_nht_register(rn, nh, true); + if (static_nexthop_list_count(&pn->nexthop_list) && svrf && svrf->vrf) + static_zebra_route_add(rn, pn, safi, true); } -/* Uninstall static route from RIB. */ -static void static_uninstall_route(vrf_id_t vrf_id, safi_t safi, - struct route_node *rn, - struct static_route *si_changed) +/* Uninstall static path from RIB. */ +static void static_uninstall_path(struct route_node *rn, struct static_path *pn, + safi_t safi, struct static_vrf *svrf) { - - if (rn->info) - static_zebra_route_add(rn, si_changed, vrf_id, safi, true); + if (static_nexthop_list_count(&pn->nexthop_list)) + static_zebra_route_add(rn, pn, safi, true); else - static_zebra_route_add(rn, si_changed, vrf_id, safi, false); + static_zebra_route_add(rn, pn, safi, false); } -int static_add_route(afi_t afi, safi_t safi, uint8_t type, struct prefix *p, - struct prefix_ipv6 *src_p, union g_addr *gate, - const char *ifname, enum static_blackhole_type bh_type, - route_tag_t tag, uint8_t distance, struct static_vrf *svrf, - struct static_vrf *nh_svrf, - struct static_nh_label *snh_label, uint32_t table_id, - bool onlink) +struct route_node *static_add_route(afi_t afi, safi_t safi, struct prefix *p, + struct prefix_ipv6 *src_p, + struct static_vrf *svrf) { struct route_node *rn; - struct static_route *si; - struct static_route *pp; - struct static_route *cp; - struct static_route *update = NULL; + struct static_route_info *si; struct route_table *stable = svrf->stable[afi][safi]; if (!stable) - return -1; - - if (!gate && (type == STATIC_IPV4_GATEWAY - || type == STATIC_IPV4_GATEWAY_IFNAME - || type == STATIC_IPV6_GATEWAY - || type == STATIC_IPV6_GATEWAY_IFNAME)) - return -1; - - if (!ifname - && (type == STATIC_IFNAME || type == STATIC_IPV4_GATEWAY_IFNAME - || type == STATIC_IPV6_GATEWAY_IFNAME)) - return -1; + return NULL; /* Lookup static route prefix. */ rn = srcdest_rnode_get(stable, p, src_p); - /* Do nothing if there is a same static route. */ - for (si = rn->info; si; si = si->next) { - if (type == si->type - && (!gate - || ((afi == AFI_IP - && IPV4_ADDR_SAME(&gate->ipv4, &si->addr.ipv4)) - || (afi == AFI_IP6 - && IPV6_ADDR_SAME(gate, &si->addr.ipv6)))) - && (!strcmp(ifname ? ifname : "", si->ifname))) { - if ((distance == si->distance) && (tag == si->tag) - && (table_id == si->table_id) - && !memcmp(&si->snh_label, snh_label, - sizeof(struct static_nh_label)) - && si->bh_type == bh_type && si->onlink == onlink) { - route_unlock_node(rn); - return 0; - } - update = si; + si = XCALLOC(MTYPE_STATIC_ROUTE, sizeof(struct static_route_info)); + static_route_info_init(si); + + rn->info = si; + + return rn; +} + +/* To delete the srcnodes */ +static void static_del_src_route(struct route_node *rn, safi_t safi, + struct static_vrf *svrf) +{ + struct static_path *pn; + struct static_route_info *si; + + si = rn->info; + + frr_each_safe(static_path_list, &si->path_list, pn) { + static_del_path(rn, pn, safi, svrf); + } + + XFREE(MTYPE_STATIC_ROUTE, rn->info); + route_unlock_node(rn); +} + +void static_del_route(struct route_node *rn, safi_t safi, + struct static_vrf *svrf) +{ + struct static_path *pn; + struct static_route_info *si; + struct route_table *src_table; + struct route_node *src_node; + + si = rn->info; + + frr_each_safe(static_path_list, &si->path_list, pn) { + static_del_path(rn, pn, safi, svrf); + } + + /* clean up for dst table */ + src_table = srcdest_srcnode_table(rn); + if (src_table) { + /* This means the route_node is part of the top hierarchy + * and refers to a destination prefix. + */ + for (src_node = route_top(src_table); src_node; + src_node = route_next(src_node)) { + static_del_src_route(src_node, safi, svrf); } } + XFREE(MTYPE_STATIC_ROUTE, rn->info); + route_unlock_node(rn); +} - /* Distance or tag or label changed, delete existing first. */ - if (update) - static_delete_route(afi, safi, type, p, src_p, gate, ifname, - update->tag, update->distance, svrf, - &update->snh_label, table_id); +bool static_add_nexthop_validate(const char *nh_vrf_name, static_types type, + struct ipaddr *ipaddr) +{ + struct vrf *vrf; + + vrf = vrf_lookup_by_name(nh_vrf_name); + if (!vrf) + return true; + + switch (type) { + case STATIC_IPV4_GATEWAY: + case STATIC_IPV4_GATEWAY_IFNAME: + if (if_lookup_exact_address(&ipaddr->ipaddr_v4, AF_INET, + vrf->vrf_id)) + return false; + break; + case STATIC_IPV6_GATEWAY: + case STATIC_IPV6_GATEWAY_IFNAME: + if (if_lookup_exact_address(&ipaddr->ipaddr_v6, AF_INET6, + vrf->vrf_id)) + return false; + break; + default: + break; + } + + return true; +} + +struct static_path *static_add_path(struct route_node *rn, uint32_t table_id, + uint8_t distance) +{ + struct static_path *pn; + struct static_route_info *si; + + route_lock_node(rn); + + /* Make new static route structure. */ + pn = XCALLOC(MTYPE_STATIC_PATH, sizeof(struct static_path)); + + pn->distance = distance; + pn->table_id = table_id; + static_nexthop_list_init(&(pn->nexthop_list)); + + si = rn->info; + static_path_list_add_head(&(si->path_list), pn); + + return pn; +} + +void static_del_path(struct route_node *rn, struct static_path *pn, safi_t safi, + struct static_vrf *svrf) +{ + struct static_route_info *si; + struct static_nexthop *nh; + + si = rn->info; + + static_path_list_del(&si->path_list, pn); + + frr_each_safe(static_nexthop_list, &pn->nexthop_list, nh) { + static_delete_nexthop(rn, pn, safi, svrf, nh); + } + + route_unlock_node(rn); + + XFREE(MTYPE_STATIC_PATH, pn); +} + +struct static_nexthop * +static_add_nexthop(struct route_node *rn, struct static_path *pn, safi_t safi, + struct static_vrf *svrf, static_types type, + struct ipaddr *ipaddr, const char *ifname, + const char *nh_vrf, uint32_t color) +{ + struct static_nexthop *nh; + struct static_vrf *nh_svrf; + struct interface *ifp; + struct static_nexthop *cp; + + route_lock_node(rn); + + nh_svrf = static_vrf_lookup_by_name(nh_vrf); /* Make new static route structure. */ - si = XCALLOC(MTYPE_STATIC_ROUTE, sizeof(struct static_route)); - - si->type = type; - si->distance = distance; - si->bh_type = bh_type; - si->tag = tag; - si->vrf_id = svrf->vrf->vrf_id; - si->nh_vrf_id = nh_svrf->vrf->vrf_id; - strcpy(si->nh_vrfname, nh_svrf->vrf->name); - si->table_id = table_id; - si->onlink = onlink; + nh = XCALLOC(MTYPE_STATIC_NEXTHOP, sizeof(struct static_nexthop)); + + nh->type = type; + nh->color = color; + + nh->nh_vrf_id = nh_svrf ? nh_svrf->vrf->vrf_id : VRF_UNKNOWN; + strlcpy(nh->nh_vrfname, nh_vrf, sizeof(nh->nh_vrfname)); if (ifname) - strlcpy(si->ifname, ifname, sizeof(si->ifname)); - si->ifindex = IFINDEX_INTERNAL; + strlcpy(nh->ifname, ifname, sizeof(nh->ifname)); + nh->ifindex = IFINDEX_INTERNAL; switch (type) { case STATIC_IPV4_GATEWAY: case STATIC_IPV4_GATEWAY_IFNAME: - si->addr.ipv4 = gate->ipv4; + nh->addr.ipv4 = ipaddr->ipaddr_v4; break; case STATIC_IPV6_GATEWAY: case STATIC_IPV6_GATEWAY_IFNAME: - si->addr.ipv6 = gate->ipv6; + nh->addr.ipv6 = ipaddr->ipaddr_v6; break; - case STATIC_IFNAME: + default: break; } - - /* Save labels, if any. */ - memcpy(&si->snh_label, snh_label, sizeof(struct static_nh_label)); - /* * Add new static route information to the tree with sort by - * distance value and gateway address. + * gateway address. */ - for (pp = NULL, cp = rn->info; cp; pp = cp, cp = cp->next) { - if (si->distance < cp->distance) - break; - if (si->distance > cp->distance) - continue; - if (si->type == STATIC_IPV4_GATEWAY + frr_each(static_nexthop_list, &pn->nexthop_list, cp) { + if (nh->type == STATIC_IPV4_GATEWAY && cp->type == STATIC_IPV4_GATEWAY) { - if (ntohl(si->addr.ipv4.s_addr) + if (ntohl(nh->addr.ipv4.s_addr) < ntohl(cp->addr.ipv4.s_addr)) break; - if (ntohl(si->addr.ipv4.s_addr) + if (ntohl(nh->addr.ipv4.s_addr) > ntohl(cp->addr.ipv4.s_addr)) continue; } } + static_nexthop_list_add_after(&(pn->nexthop_list), cp, nh); - /* Make linked list. */ - if (pp) - pp->next = si; - else - rn->info = si; - if (cp) - cp->prev = si; - si->prev = pp; - si->next = cp; + if (nh->nh_vrf_id == VRF_UNKNOWN) + return nh; /* check whether interface exists in system & install if it does */ - if (!ifname) - static_install_route(rn, si, safi); - else { - struct interface *ifp; + switch (nh->type) { + case STATIC_IPV4_GATEWAY: + case STATIC_IPV6_GATEWAY: + break; + case STATIC_IPV4_GATEWAY_IFNAME: + case STATIC_IPV6_GATEWAY_IFNAME: + ifp = if_lookup_by_name(ifname, nh->nh_vrf_id); + if (ifp && ifp->ifindex != IFINDEX_INTERNAL) + nh->ifindex = ifp->ifindex; + else + zlog_warn( + "Static Route using %s interface not installed because the interface does not exist in specified vrf", + ifname); - ifp = if_lookup_by_name(ifname, nh_svrf->vrf->vrf_id); + break; + case STATIC_BLACKHOLE: + nh->bh_type = STATIC_BLACKHOLE_NULL; + break; + case STATIC_IFNAME: + ifp = if_lookup_by_name(ifname, nh->nh_vrf_id); if (ifp && ifp->ifindex != IFINDEX_INTERNAL) { - si->ifindex = ifp->ifindex; - static_install_route(rn, si, safi); + nh->ifindex = ifp->ifindex; } else - zlog_warn("Static Route using %s interface not installed because the interface does not exist in specified vrf", - ifname); + zlog_warn( + "Static Route using %s interface not installed because the interface does not exist in specified vrf", + ifname); + break; } - return 1; + return nh; } -int static_delete_route(afi_t afi, safi_t safi, uint8_t type, struct prefix *p, - struct prefix_ipv6 *src_p, union g_addr *gate, - const char *ifname, route_tag_t tag, uint8_t distance, - struct static_vrf *svrf, - struct static_nh_label *snh_label, - uint32_t table_id) +void static_install_nexthop(struct route_node *rn, struct static_path *pn, + struct static_nexthop *nh, safi_t safi, + struct static_vrf *svrf, const char *ifname, + static_types type, const char *nh_vrf) { - struct route_node *rn; - struct static_route *si; - struct route_table *stable; + struct interface *ifp; - /* Lookup table. */ - stable = static_vrf_static_table(afi, safi, svrf); - if (!stable) - return -1; + if (nh->nh_vrf_id == VRF_UNKNOWN) + return; - /* Lookup static route prefix. */ - rn = srcdest_rnode_lookup(stable, p, src_p); - if (!rn) - return 0; - - /* Find same static route is the tree */ - for (si = rn->info; si; si = si->next) - if (type == si->type - && (!gate - || ((afi == AFI_IP - && IPV4_ADDR_SAME(&gate->ipv4, &si->addr.ipv4)) - || (afi == AFI_IP6 - && IPV6_ADDR_SAME(gate, &si->addr.ipv6)))) - && (!strcmp(ifname ? ifname : "", si->ifname)) - && (!tag || (tag == si->tag)) - && (table_id == si->table_id) - && (!snh_label->num_labels - || !memcmp(&si->snh_label, snh_label, - sizeof(struct static_nh_label)))) - break; - - /* Can't find static route. */ - if (!si) { - route_unlock_node(rn); - return 0; + /* check whether interface exists in system & install if it does */ + switch (nh->type) { + case STATIC_IPV4_GATEWAY: + case STATIC_IPV6_GATEWAY: + if (!static_zebra_nh_update(rn, nh)) + static_zebra_nht_register(rn, nh, true); + break; + case STATIC_IPV4_GATEWAY_IFNAME: + case STATIC_IPV6_GATEWAY_IFNAME: + if (!static_zebra_nh_update(rn, nh)) + static_zebra_nht_register(rn, nh, true); + break; + case STATIC_BLACKHOLE: + static_install_path(rn, pn, safi, svrf); + break; + case STATIC_IFNAME: + ifp = if_lookup_by_name(ifname, nh->nh_vrf_id); + if (ifp && ifp->ifindex != IFINDEX_INTERNAL) + static_install_path(rn, pn, safi, svrf); + + break; } +} - static_zebra_nht_register(si, false); +int static_delete_nexthop(struct route_node *rn, struct static_path *pn, + safi_t safi, struct static_vrf *svrf, + struct static_nexthop *nh) +{ + static_nexthop_list_del(&(pn->nexthop_list), nh); - /* Unlink static route from linked list. */ - if (si->prev) - si->prev->next = si->next; - else - rn->info = si->next; - if (si->next) - si->next->prev = si->prev; + if (nh->nh_vrf_id == VRF_UNKNOWN) + goto EXIT; + static_zebra_nht_register(rn, nh, false); /* * If we have other si nodes then route replace * else delete the route */ - static_uninstall_route(si->vrf_id, safi, rn, si); - route_unlock_node(rn); - - /* Free static route configuration. */ - XFREE(MTYPE_STATIC_ROUTE, si); + static_uninstall_path(rn, pn, safi, svrf); +EXIT: route_unlock_node(rn); + /* Free static route configuration. */ + XFREE(MTYPE_STATIC_NEXTHOP, nh); return 1; } +static void static_ifindex_update_nh(struct interface *ifp, bool up, + struct route_node *rn, + struct static_path *pn, + struct static_nexthop *nh, + struct static_vrf *svrf, safi_t safi) +{ + if (!nh->ifname[0]) + return; + if (up) { + if (strcmp(nh->ifname, ifp->name)) + return; + if (nh->nh_vrf_id != ifp->vrf_id) + return; + nh->ifindex = ifp->ifindex; + } else { + if (nh->ifindex != ifp->ifindex) + return; + if (nh->nh_vrf_id != ifp->vrf_id) + return; + nh->ifindex = IFINDEX_INTERNAL; + } + + static_install_path(rn, pn, safi, svrf); +} + static void static_ifindex_update_af(struct interface *ifp, bool up, afi_t afi, safi_t safi) { struct route_table *stable; struct route_node *rn; - struct static_route *si; + struct static_nexthop *nh; + struct static_path *pn; struct vrf *vrf; + struct static_route_info *si; RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { struct static_vrf *svrf; @@ -283,22 +389,17 @@ static void static_ifindex_update_af(struct interface *ifp, bool up, afi_t afi, stable = static_vrf_static_table(afi, safi, svrf); if (!stable) continue; - for (rn = route_top(stable); rn; rn = srcdest_route_next(rn)) { - for (si = rn->info; si; si = si->next) { - if (!si->ifname[0]) - continue; - if (up) { - if (strcmp(si->ifname, ifp->name)) - continue; - si->ifindex = ifp->ifindex; - } else { - if (si->ifindex != ifp->ifindex) - continue; - si->ifindex = IFINDEX_INTERNAL; + si = static_route_info_from_rnode(rn); + if (!si) + continue; + frr_each(static_path_list, &si->path_list, pn) { + frr_each(static_nexthop_list, + &pn->nexthop_list, nh) { + static_ifindex_update_nh(ifp, up, rn, + pn, nh, svrf, + safi); } - - static_install_route(rn, si, safi); } } } @@ -320,26 +421,34 @@ static void static_fixup_vrf(struct static_vrf *svrf, struct route_table *stable, afi_t afi, safi_t safi) { struct route_node *rn; - struct static_route *si; + struct static_nexthop *nh; struct interface *ifp; + struct static_path *pn; + struct static_route_info *si; for (rn = route_top(stable); rn; rn = route_next(rn)) { - for (si = rn->info; si; si = si->next) { - if (strcmp(svrf->vrf->name, si->nh_vrfname) != 0) - continue; - - si->nh_vrf_id = svrf->vrf->vrf_id; - si->nh_registered = false; - if (si->ifindex) { - ifp = if_lookup_by_name(si->ifname, - si->nh_vrf_id); - if (ifp) - si->ifindex = ifp->ifindex; - else + si = static_route_info_from_rnode(rn); + if (!si) + continue; + frr_each(static_path_list, &si->path_list, pn) { + frr_each(static_nexthop_list, &pn->nexthop_list, nh) { + if (strcmp(svrf->vrf->name, nh->nh_vrfname) + != 0) continue; - } - static_install_route(rn, si, safi); + nh->nh_vrf_id = svrf->vrf->vrf_id; + nh->nh_registered = false; + if (nh->ifindex) { + ifp = if_lookup_by_name(nh->ifname, + nh->nh_vrf_id); + if (ifp) + nh->ifindex = ifp->ifindex; + else + continue; + } + + static_install_path(rn, pn, safi, svrf); + } } } } @@ -354,26 +463,33 @@ static void static_fixup_vrf(struct static_vrf *svrf, * safi -> the safi in question */ static void static_enable_vrf(struct static_vrf *svrf, - struct route_table *stable, - afi_t afi, safi_t safi) + struct route_table *stable, afi_t afi, + safi_t safi) { struct route_node *rn; - struct static_route *si; + struct static_nexthop *nh; struct interface *ifp; - struct vrf *vrf = svrf->vrf; + struct static_path *pn; + struct static_route_info *si; for (rn = route_top(stable); rn; rn = route_next(rn)) { - for (si = rn->info; si; si = si->next) { - si->vrf_id = vrf->vrf_id; - if (si->ifindex) { - ifp = if_lookup_by_name(si->ifname, - si->nh_vrf_id); - if (ifp) - si->ifindex = ifp->ifindex; - else + si = static_route_info_from_rnode(rn); + if (!si) + continue; + frr_each(static_path_list, &si->path_list, pn) { + frr_each(static_nexthop_list, &pn->nexthop_list, nh) { + if (nh->ifindex) { + ifp = if_lookup_by_name(nh->ifname, + nh->nh_vrf_id); + if (ifp) + nh->ifindex = ifp->ifindex; + else + continue; + } + if (nh->nh_vrf_id == VRF_UNKNOWN) continue; + static_install_path(rn, pn, safi, svrf); } - static_install_route(rn, si, safi); } } } @@ -429,14 +545,22 @@ static void static_cleanup_vrf(struct static_vrf *svrf, afi_t afi, safi_t safi) { struct route_node *rn; - struct static_route *si; + struct static_nexthop *nh; + struct static_path *pn; + struct static_route_info *si; for (rn = route_top(stable); rn; rn = route_next(rn)) { - for (si = rn->info; si; si = si->next) { - if (strcmp(svrf->vrf->name, si->nh_vrfname) != 0) - continue; + si = static_route_info_from_rnode(rn); + if (!si) + continue; + frr_each(static_path_list, &si->path_list, pn) { + frr_each(static_nexthop_list, &pn->nexthop_list, nh) { + if (strcmp(svrf->vrf->name, nh->nh_vrfname) + != 0) + continue; - static_uninstall_route(si->vrf_id, safi, rn, si); + static_uninstall_path(rn, pn, safi, svrf); + } } } } @@ -453,11 +577,23 @@ static void static_disable_vrf(struct route_table *stable, afi_t afi, safi_t safi) { struct route_node *rn; - struct static_route *si; + struct static_nexthop *nh; + struct static_path *pn; + struct stable_info *info; + struct static_route_info *si; + + info = route_table_get_info(stable); - for (rn = route_top(stable); rn; rn = route_next(rn)) - for (si = rn->info; si; si = si->next) - static_uninstall_route(si->vrf_id, safi, rn, si); + for (rn = route_top(stable); rn; rn = route_next(rn)) { + si = static_route_info_from_rnode(rn); + if (!si) + continue; + frr_each(static_path_list, &si->path_list, pn) { + frr_each(static_nexthop_list, &pn->nexthop_list, nh) { + static_uninstall_path(rn, pn, safi, info->svrf); + } + } + } } /* @@ -512,17 +648,27 @@ static void static_fixup_intf_nh(struct route_table *stable, afi_t afi, safi_t safi) { struct route_node *rn; - struct static_route *si; + struct stable_info *info; + struct static_nexthop *nh; + struct static_path *pn; + struct static_route_info *si; + + info = route_table_get_info(stable); for (rn = route_top(stable); rn; rn = route_next(rn)) { - for (si = rn->info; si; si = si->next) { - if (si->nh_vrf_id != ifp->vrf_id) - continue; + si = static_route_info_from_rnode(rn); + if (!si) + continue; + frr_each(static_path_list, &si->path_list, pn) { + frr_each(static_nexthop_list, &pn->nexthop_list, nh) { + if (nh->nh_vrf_id != ifp->vrf_id) + continue; - if (si->ifindex != ifp->ifindex) - continue; + if (nh->ifindex != ifp->ifindex) + continue; - static_install_route(rn, si, safi); + static_install_path(rn, pn, safi, info->svrf); + } } } } @@ -566,3 +712,40 @@ void static_ifindex_update(struct interface *ifp, bool up) static_ifindex_update_af(ifp, up, AFI_IP6, SAFI_UNICAST); static_ifindex_update_af(ifp, up, AFI_IP6, SAFI_MULTICAST); } + +void static_get_nh_type(static_types stype, char *type, size_t size) +{ + switch (stype) { + case STATIC_IFNAME: + strlcpy(type, "ifindex", size); + break; + case STATIC_IPV4_GATEWAY: + strlcpy(type, "ip4", size); + break; + case STATIC_IPV4_GATEWAY_IFNAME: + strlcpy(type, "ip4-ifindex", size); + break; + case STATIC_BLACKHOLE: + strlcpy(type, "blackhole", size); + break; + case STATIC_IPV6_GATEWAY: + strlcpy(type, "ip6", size); + break; + case STATIC_IPV6_GATEWAY_IFNAME: + strlcpy(type, "ip6-ifindex", size); + break; + }; +} + +struct stable_info *static_get_stable_info(struct route_node *rn) +{ + struct route_table *table; + + table = srcdest_rnode_table(rn); + return table->info; +} + +void static_route_info_init(struct static_route_info *si) +{ + static_path_list_init(&(si->path_list)); +} diff --git a/staticd/static_routes.h b/staticd/static_routes.h index 6036bfe396..a3440a89eb 100644 --- a/staticd/static_routes.h +++ b/staticd/static_routes.h @@ -21,6 +21,7 @@ #define __STATIC_ROUTES_H__ #include "lib/mpls.h" +#include "table.h" /* Static route label information */ struct static_nh_label { @@ -35,31 +36,78 @@ enum static_blackhole_type { STATIC_BLACKHOLE_REJECT }; +/* + * The order for below macros should be in sync with + * yang model typedef nexthop-type + */ typedef enum { - STATIC_IFNAME, + STATIC_IFNAME = 1, STATIC_IPV4_GATEWAY, STATIC_IPV4_GATEWAY_IFNAME, - STATIC_BLACKHOLE, STATIC_IPV6_GATEWAY, STATIC_IPV6_GATEWAY_IFNAME, + STATIC_BLACKHOLE, } static_types; +/* + * Route Creation gives us: + * START -> Initial State, only exit is when we send the route to + * zebra for installation + * When we send the route to Zebra move to SENT_TO_ZEBRA + * SENT_TO_ZEBRA -> A way to notice that we've sent the route to zebra + * But have not received a response on it's status yet + * After The response from zebra we move to INSTALLED or FAILED + * INSTALLED -> Route was accepted + * FAILED -> Route was rejected + * When we receive notification about a nexthop that a route uses + * We move the route back to START and initiate the process again. + */ +enum static_install_states { + STATIC_START, + STATIC_SENT_TO_ZEBRA, + STATIC_INSTALLED, + STATIC_NOT_INSTALLED, +}; + +PREDECL_DLIST(static_path_list); +PREDECL_DLIST(static_nexthop_list); + +/* Static route information */ +struct static_route_info { + /* path list */ + struct static_path_list_head path_list; +}; + +/* Static path information */ +struct static_path { + /* Linkage for static path lists */ + struct static_path_list_item list; + /* Administrative distance. */ + uint8_t distance; + /* Tag */ + route_tag_t tag; + /* Table-id */ + uint32_t table_id; + /* Nexthop list */ + struct static_nexthop_list_head nexthop_list; +}; + +DECLARE_DLIST(static_path_list, struct static_path, list); + /* Static route information. */ -struct static_route { +struct static_nexthop { /* For linked list. */ - struct static_route *prev; - struct static_route *next; + struct static_nexthop_list_item list; /* VRF identifier. */ - vrf_id_t vrf_id; vrf_id_t nh_vrf_id; char nh_vrfname[VRF_NAMSIZ + 1]; - /* Administrative distance. */ - uint8_t distance; - - /* Tag */ - route_tag_t tag; + /* + * States that we walk the route through + * To know where we are. + */ + enum static_install_states state; /* Flag for this static route's type. */ static_types type; @@ -78,42 +126,75 @@ struct static_route { /* Label information */ struct static_nh_label snh_label; - uint32_t table_id; - /* * Whether to pretend the nexthop is directly attached to the specified * link. Only meaningful when both a gateway address and interface name * are specified. */ bool onlink; + + /* SR-TE color */ + uint32_t color; }; +DECLARE_DLIST(static_nexthop_list, struct static_nexthop, list); + + +/* + * rib_dest_from_rnode + */ +static inline struct static_route_info * +static_route_info_from_rnode(struct route_node *rn) +{ + return (struct static_route_info *)(rn->info); +} + extern bool mpls_enabled; extern struct zebra_privs_t static_privs; void static_fixup_vrf_ids(struct static_vrf *svrf); -extern int static_add_route(afi_t afi, safi_t safi, uint8_t type, - struct prefix *p, struct prefix_ipv6 *src_p, - union g_addr *gate, const char *ifname, - enum static_blackhole_type bh_type, route_tag_t tag, - uint8_t distance, struct static_vrf *svrf, - struct static_vrf *nh_svrf, - struct static_nh_label *snh_label, - uint32_t table_id, bool onlink); - -extern int static_delete_route(afi_t afi, safi_t safi, uint8_t type, - struct prefix *p, struct prefix_ipv6 *src_p, - union g_addr *gate, const char *ifname, - route_tag_t tag, uint8_t distance, - struct static_vrf *svrf, - struct static_nh_label *snh_label, - uint32_t table_id); +extern struct static_nexthop * +static_add_nexthop(struct route_node *rn, struct static_path *pn, safi_t safi, + struct static_vrf *svrf, static_types type, + struct ipaddr *ipaddr, const char *ifname, + const char *nh_vrf, uint32_t color); +extern void static_install_nexthop(struct route_node *rn, + struct static_path *pn, + struct static_nexthop *nh, safi_t safi, + struct static_vrf *svrf, const char *ifname, + static_types type, const char *nh_vrf); + +extern int static_delete_nexthop(struct route_node *rn, struct static_path *pn, + safi_t safi, struct static_vrf *svrf, + struct static_nexthop *nh); extern void static_cleanup_vrf_ids(struct static_vrf *disable_svrf); extern void static_install_intf_nh(struct interface *ifp); extern void static_ifindex_update(struct interface *ifp, bool up); + +extern void static_install_path(struct route_node *rn, struct static_path *pn, + safi_t safi, struct static_vrf *svrf); + +extern struct route_node *static_add_route(afi_t afi, safi_t safi, + struct prefix *p, + struct prefix_ipv6 *src_p, + struct static_vrf *svrf); +extern void static_del_route(struct route_node *rn, safi_t safi, + struct static_vrf *svrf); + +extern struct static_path *static_add_path(struct route_node *rn, + uint32_t table_id, uint8_t distance); +extern void static_del_path(struct route_node *rn, struct static_path *pn, + safi_t safi, struct static_vrf *svrf); + +extern void static_get_nh_type(static_types stype, char *type, size_t size); +extern bool static_add_nexthop_validate(const char *nh_vrf_name, + static_types type, + struct ipaddr *ipaddr); +extern struct stable_info *static_get_stable_info(struct route_node *rn); +extern void static_route_info_init(struct static_route_info *si); #endif diff --git a/staticd/static_vrf.c b/staticd/static_vrf.c index 9dd25fbdd1..c57ffe3b12 100644 --- a/staticd/static_vrf.c +++ b/staticd/static_vrf.c @@ -27,28 +27,77 @@ #include "static_memory.h" #include "static_vrf.h" #include "static_routes.h" +#include "static_zebra.h" #include "static_vty.h" +DEFINE_MTYPE_STATIC(STATIC, STATIC_RTABLE_INFO, "Static Route Table Info"); + static void zebra_stable_node_cleanup(struct route_table *table, struct route_node *node) { - struct static_route *si, *next; + struct static_nexthop *nh; + struct static_path *pn; + struct static_route_info *si; + struct route_table *src_table; + struct route_node *src_node; + struct static_path *src_pn; + struct static_route_info *src_si; + + si = node->info; + + if (si) { + frr_each_safe(static_path_list, &si->path_list, pn) { + frr_each_safe(static_nexthop_list, &pn->nexthop_list, + nh) { + static_nexthop_list_del(&pn->nexthop_list, nh); + XFREE(MTYPE_STATIC_NEXTHOP, nh); + } + static_path_list_del(&si->path_list, pn); + XFREE(MTYPE_STATIC_PATH, pn); + } - if (node->info) - for (si = node->info; si; si = next) { - next = si->next; - XFREE(MTYPE_STATIC_ROUTE, si); + /* clean up for dst table */ + src_table = srcdest_srcnode_table(node); + if (src_table) { + /* This means the route_node is part of the top + * hierarchy and refers to a destination prefix. + */ + for (src_node = route_top(src_table); src_node; + src_node = route_next(src_node)) { + src_si = src_node->info; + + frr_each_safe(static_path_list, + &src_si->path_list, src_pn) { + frr_each_safe(static_nexthop_list, + &src_pn->nexthop_list, + nh) { + static_nexthop_list_del( + &src_pn->nexthop_list, + nh); + XFREE(MTYPE_STATIC_NEXTHOP, nh); + } + static_path_list_del(&src_si->path_list, + src_pn); + XFREE(MTYPE_STATIC_PATH, src_pn); + } + + XFREE(MTYPE_STATIC_ROUTE, src_node->info); + } } + + XFREE(MTYPE_STATIC_ROUTE, node->info); + } } static struct static_vrf *static_vrf_alloc(void) { struct route_table *table; struct static_vrf *svrf; + struct stable_info *info; safi_t safi; afi_t afi; - svrf = XCALLOC(MTYPE_TMP, sizeof(struct static_vrf)); + svrf = XCALLOC(MTYPE_STATIC_RTABLE_INFO, sizeof(struct static_vrf)); for (afi = AFI_IP; afi <= AFI_IP6; afi++) { for (safi = SAFI_UNICAST; safi <= SAFI_MULTICAST; safi++) { @@ -56,6 +105,14 @@ static struct static_vrf *static_vrf_alloc(void) table = srcdest_table_init(); else table = route_table_init(); + + info = XCALLOC(MTYPE_STATIC_RTABLE_INFO, + sizeof(struct stable_info)); + info->svrf = svrf; + info->afi = afi; + info->safi = safi; + route_table_set_info(table, info); + table->cleanup = zebra_stable_node_cleanup; svrf->stable[afi][safi] = table; } @@ -76,19 +133,16 @@ static int static_vrf_new(struct vrf *vrf) static int static_vrf_enable(struct vrf *vrf) { - static_fixup_vrf_ids(vrf->info); + static_zebra_vrf_register(vrf); - /* - * We may have static routes that are now possible to - * insert into the appropriate tables - */ - static_config_install_delayed_routes(vrf->info); + static_fixup_vrf_ids(vrf->info); return 0; } static int static_vrf_disable(struct vrf *vrf) { + static_zebra_vrf_unregister(vrf); return 0; } @@ -98,15 +152,19 @@ static int static_vrf_delete(struct vrf *vrf) struct static_vrf *svrf; safi_t safi; afi_t afi; + void *info; svrf = vrf->info; for (afi = AFI_IP; afi <= AFI_IP6; afi++) { for (safi = SAFI_UNICAST; safi <= SAFI_MULTICAST; safi++) { table = svrf->stable[afi][safi]; + info = route_table_get_info(table); route_table_finish(table); + XFREE(MTYPE_STATIC_RTABLE_INFO, info); svrf->stable[afi][safi] = NULL; } } + XFREE(MTYPE_STATIC_RTABLE_INFO, svrf); return 0; } @@ -170,29 +228,6 @@ static int static_vrf_config_write(struct vty *vty) return 0; } -int static_vrf_has_config(struct static_vrf *svrf) -{ - struct route_table *table; - safi_t safi; - afi_t afi; - - /* - * NOTE: This is a don't care for the default VRF, but we go through - * the motions to keep things consistent. - */ - for (afi = AFI_IP; afi < AFI_MAX; afi++) { - for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) { - table = svrf->stable[afi][safi]; - if (!table) - continue; - if (route_table_count(table)) - return 1; - } - } - - return 0; -} - void static_vrf_init(void) { vrf_init(static_vrf_new, static_vrf_enable, @@ -200,3 +235,8 @@ void static_vrf_init(void) vrf_cmd_init(static_vrf_config_write, &static_privs); } + +void static_vrf_terminate(void) +{ + vrf_terminate(); +} diff --git a/staticd/static_vrf.h b/staticd/static_vrf.h index 28fcdd0d36..3977899054 100644 --- a/staticd/static_vrf.h +++ b/staticd/static_vrf.h @@ -26,13 +26,21 @@ struct static_vrf { struct route_table *stable[AFI_MAX][SAFI_MAX]; }; +struct stable_info { + struct static_vrf *svrf; + afi_t afi; + safi_t safi; +}; + +#define GET_STABLE_VRF_ID(info) info->svrf->vrf->vrf_id + struct static_vrf *static_vrf_lookup_by_name(const char *vrf_name); struct static_vrf *static_vrf_lookup_by_id(vrf_id_t vrf_id); -int static_vrf_has_config(struct static_vrf *svrf); - void static_vrf_init(void); struct route_table *static_vrf_static_table(afi_t afi, safi_t safi, struct static_vrf *svrf); +extern void static_vrf_terminate(void); + #endif diff --git a/staticd/static_vty.c b/staticd/static_vty.c index 3a9e4e8fa4..dfa7fa33cd 100644 --- a/staticd/static_vty.c +++ b/staticd/static_vty.c @@ -27,265 +27,60 @@ #include "table.h" #include "srcdest_table.h" #include "mpls.h" +#include "northbound.h" +#include "libfrr.h" +#include "routing_nb.h" +#include "northbound_cli.h" #include "static_vrf.h" #include "static_memory.h" #include "static_vty.h" #include "static_routes.h" +#include "static_debug.h" #ifndef VTYSH_EXTRACT_PL #include "staticd/static_vty_clippy.c" #endif - -static struct static_vrf *static_vty_get_unknown_vrf(struct vty *vty, - const char *vrf_name) -{ - struct static_vrf *svrf; - struct vrf *vrf; - - svrf = static_vrf_lookup_by_name(vrf_name); - - if (svrf) - return svrf; - - vrf = vrf_get(VRF_UNKNOWN, vrf_name); - if (!vrf) { - vty_out(vty, "%% Could not create vrf %s\n", vrf_name); - return NULL; - } - svrf = vrf->info; - if (!svrf) { - vty_out(vty, "%% Could not create vrf-info %s\n", - vrf_name); - return NULL; - } - /* Mark as having FRR configuration */ - vrf_set_user_cfged(vrf); - - return svrf; -} - -struct static_hold_route { - char *vrf_name; - char *nhvrf_name; - afi_t afi; - safi_t safi; - char *dest_str; - char *mask_str; - char *src_str; - char *gate_str; - char *ifname; - char *flag_str; - char *tag_str; - char *distance_str; - char *label_str; - char *table_str; - bool onlink; - - /* processed & masked destination, used for config display */ - struct prefix dest; -}; - -static struct list *static_list; - -static int static_list_compare_helper(const char *s1, const char *s2) -{ - /* extra (!s1 && !s2) to keep SA happy */ - if (s1 == s2 || (!s1 && !s2)) - return 0; - - if (!s1 && s2) - return -1; - - if (s1 && !s2) - return 1; - - return strcmp(s1, s2); -} - -static void static_list_delete(struct static_hold_route *shr) -{ - XFREE(MTYPE_STATIC_ROUTE, shr->vrf_name); - XFREE(MTYPE_STATIC_ROUTE, shr->nhvrf_name); - XFREE(MTYPE_STATIC_ROUTE, shr->dest_str); - XFREE(MTYPE_STATIC_ROUTE, shr->mask_str); - XFREE(MTYPE_STATIC_ROUTE, shr->src_str); - XFREE(MTYPE_STATIC_ROUTE, shr->gate_str); - XFREE(MTYPE_STATIC_ROUTE, shr->ifname); - XFREE(MTYPE_STATIC_ROUTE, shr->flag_str); - XFREE(MTYPE_STATIC_ROUTE, shr->tag_str); - XFREE(MTYPE_STATIC_ROUTE, shr->distance_str); - XFREE(MTYPE_STATIC_ROUTE, shr->label_str); - XFREE(MTYPE_STATIC_ROUTE, shr->table_str); - - XFREE(MTYPE_STATIC_ROUTE, shr); -} - -static int static_list_compare(void *arg1, void *arg2) -{ - struct static_hold_route *shr1 = arg1; - struct static_hold_route *shr2 = arg2; - int ret; - - ret = strcmp(shr1->vrf_name, shr2->vrf_name); - if (ret) - return ret; - - ret = strcmp(shr1->nhvrf_name, shr2->nhvrf_name); - if (ret) - return ret; - - ret = shr1->afi - shr2->afi; - if (ret) - return ret; - - ret = shr1->safi - shr2->safi; - if (ret) - return ret; - - ret = prefix_cmp(&shr1->dest, &shr2->dest); - if (ret) - return ret; - - ret = static_list_compare_helper(shr1->src_str, shr2->src_str); - if (ret) - return ret; - - ret = static_list_compare_helper(shr1->gate_str, shr2->gate_str); - if (ret) - return ret; - - ret = static_list_compare_helper(shr1->ifname, shr2->ifname); - if (ret) - return ret; - - ret = static_list_compare_helper(shr1->flag_str, shr2->flag_str); - if (ret) - return ret; - - ret = static_list_compare_helper(shr1->tag_str, shr2->tag_str); - if (ret) - return ret; - - ret = static_list_compare_helper(shr1->distance_str, - shr2->distance_str); - if (ret) - return ret; - - ret = static_list_compare_helper(shr1->table_str, - shr2->table_str); - if (ret) - return ret; - - return static_list_compare_helper(shr1->label_str, shr2->label_str); -} - - -/* General function for static route. */ -static int zebra_static_route_holdem( - struct static_vrf *svrf, struct static_vrf *nh_svrf, afi_t afi, - safi_t safi, const char *negate, struct prefix *dest, - const char *dest_str, const char *mask_str, const char *src_str, - const char *gate_str, const char *ifname, const char *flag_str, - const char *tag_str, const char *distance_str, const char *label_str, - const char *table_str, bool onlink) -{ - struct static_hold_route *shr, *lookup; - struct listnode *node; - - zlog_warn("Static Route to %s not installed currently because dependent config not fully available", - dest_str); - - shr = XCALLOC(MTYPE_STATIC_ROUTE, sizeof(*shr)); - shr->vrf_name = XSTRDUP(MTYPE_STATIC_ROUTE, svrf->vrf->name); - shr->nhvrf_name = XSTRDUP(MTYPE_STATIC_ROUTE, nh_svrf->vrf->name); - shr->afi = afi; - shr->safi = safi; - shr->onlink = onlink; - if (dest) - prefix_copy(&shr->dest, dest); - if (dest_str) - shr->dest_str = XSTRDUP(MTYPE_STATIC_ROUTE, dest_str); - if (mask_str) - shr->mask_str = XSTRDUP(MTYPE_STATIC_ROUTE, mask_str); - if (src_str) - shr->src_str = XSTRDUP(MTYPE_STATIC_ROUTE, src_str); - if (gate_str) - shr->gate_str = XSTRDUP(MTYPE_STATIC_ROUTE, gate_str); - if (ifname) - shr->ifname = XSTRDUP(MTYPE_STATIC_ROUTE, ifname); - if (flag_str) - shr->flag_str = XSTRDUP(MTYPE_STATIC_ROUTE, flag_str); - if (tag_str) - shr->tag_str = XSTRDUP(MTYPE_STATIC_ROUTE, tag_str); - if (distance_str) - shr->distance_str = XSTRDUP(MTYPE_STATIC_ROUTE, distance_str); - if (label_str) - shr->label_str = XSTRDUP(MTYPE_STATIC_ROUTE, label_str); - if (table_str) - shr->table_str = XSTRDUP(MTYPE_STATIC_ROUTE, table_str); - - for (ALL_LIST_ELEMENTS_RO(static_list, node, lookup)) { - if (static_list_compare(shr, lookup) == 0) - break; - } - - if (lookup) { - if (negate) { - listnode_delete(static_list, lookup); - static_list_delete(shr); - static_list_delete(lookup); - - return CMD_SUCCESS; - } - - /* - * If a person enters the same line again - * we need to silently accept it - */ - goto shr_cleanup; - } - - if (!negate) { - listnode_add_sort(static_list, shr); - return CMD_SUCCESS; - } - - shr_cleanup: - XFREE(MTYPE_STATIC_ROUTE, shr->nhvrf_name); - XFREE(MTYPE_STATIC_ROUTE, shr->vrf_name); - XFREE(MTYPE_STATIC_ROUTE, shr); - - return CMD_SUCCESS; -} - -static int static_route_leak( - struct vty *vty, struct static_vrf *svrf, struct static_vrf *nh_svrf, - afi_t afi, safi_t safi, const char *negate, const char *dest_str, - const char *mask_str, const char *src_str, const char *gate_str, - const char *ifname, const char *flag_str, const char *tag_str, - const char *distance_str, const char *label_str, const char *table_str, - bool onlink) +#include "static_nb.h" + +#define STATICD_STR "Static route daemon\n" + +static int static_route_leak(struct vty *vty, const char *svrf, + const char *nh_svrf, afi_t afi, safi_t safi, + const char *negate, const char *dest_str, + const char *mask_str, const char *src_str, + const char *gate_str, const char *ifname, + const char *flag_str, const char *tag_str, + const char *distance_str, const char *label_str, + const char *table_str, bool onlink, + const char *color_str) { int ret; - uint8_t distance; struct prefix p, src; - struct prefix_ipv6 *src_p = NULL; - union g_addr gate; - union g_addr *gatep = NULL; struct in_addr mask; - enum static_blackhole_type bh_type = 0; - route_tag_t tag = 0; uint8_t type; - struct static_nh_label snh_label; + const char *bh_type; + char xpath_prefix[XPATH_MAXLEN]; + char xpath_nexthop[XPATH_MAXLEN]; + char xpath_mpls[XPATH_MAXLEN]; + char xpath_label[XPATH_MAXLEN]; + char ab_xpath[XPATH_MAXLEN]; + char buf_prefix[PREFIX_STRLEN]; + char buf_src_prefix[PREFIX_STRLEN]; + char buf_nh_type[PREFIX_STRLEN]; + char buf_tag[PREFIX_STRLEN]; + uint8_t label_stack_id = 0; + const char *buf_gate_str; + uint8_t distance = ZEBRA_STATIC_DISTANCE_DEFAULT; + route_tag_t tag = 0; uint32_t table_id = 0; + const struct lyd_node *dnode; + + memset(buf_src_prefix, 0, PREFIX_STRLEN); + memset(buf_nh_type, 0, PREFIX_STRLEN); ret = str2prefix(dest_str, &p); if (ret <= 0) { - if (vty) - vty_out(vty, "%% Malformed address\n"); - else - zlog_warn("%s: Malformed address: %s", - __PRETTY_FUNCTION__, dest_str); + vty_out(vty, "%% Malformed address\n"); return CMD_WARNING_CONFIG_FAILED; } @@ -295,12 +90,7 @@ static int static_route_leak( if (mask_str) { ret = inet_aton(mask_str, &mask); if (ret == 0) { - if (vty) - vty_out(vty, "%% Malformed address\n"); - else - zlog_warn("%s: Malformed address: %s", - __PRETTY_FUNCTION__, - mask_str); + vty_out(vty, "%% Malformed address\n"); return CMD_WARNING_CONFIG_FAILED; } p.prefixlen = ip_masklen(mask); @@ -311,16 +101,9 @@ static int static_route_leak( if (src_str) { ret = str2prefix(src_str, &src); if (ret <= 0 || src.family != AF_INET6) { - if (vty) - vty_out(vty, - "%% Malformed source address\n"); - else - zlog_warn( - "%s: Malformed source address: %s", - __PRETTY_FUNCTION__, src_str); + vty_out(vty, "%% Malformed source address\n"); return CMD_WARNING_CONFIG_FAILED; } - src_p = (struct prefix_ipv6 *)&src; } break; default: @@ -330,30 +113,29 @@ static int static_route_leak( /* Apply mask for given prefix. */ apply_mask(&p); - if (svrf->vrf->vrf_id == VRF_UNKNOWN - || nh_svrf->vrf->vrf_id == VRF_UNKNOWN) { - vrf_set_user_cfged(svrf->vrf); - return zebra_static_route_holdem( - svrf, nh_svrf, afi, safi, negate, &p, dest_str, - mask_str, src_str, gate_str, ifname, flag_str, tag_str, - distance_str, label_str, table_str, onlink); - } + prefix2str(&p, buf_prefix, sizeof(buf_prefix)); - if (table_str) { - /* table configured. check consistent with vrf config - */ - if (svrf->vrf->data.l.table_id != RT_TABLE_MAIN) { - if (vty) - vty_out(vty, - "%% Table %s overlaps vrf table %u\n", - table_str, svrf->vrf->data.l.table_id); - else - zlog_warn( - "%s: Table %s overlaps vrf table %u", - __PRETTY_FUNCTION__, - table_str, svrf->vrf->data.l.table_id); - return CMD_WARNING_CONFIG_FAILED; - } + if (src_str) + prefix2str(&src, buf_src_prefix, sizeof(buf_src_prefix)); + if (gate_str) + buf_gate_str = gate_str; + else + buf_gate_str = ""; + + if (gate_str == NULL && ifname == NULL) + type = STATIC_BLACKHOLE; + else if (gate_str && ifname) { + if (afi == AFI_IP) + type = STATIC_IPV4_GATEWAY_IFNAME; + else + type = STATIC_IPV6_GATEWAY_IFNAME; + } else if (ifname) + type = STATIC_IFNAME; + else { + if (afi == AFI_IP) + type = STATIC_IPV4_GATEWAY; + else + type = STATIC_IPV6_GATEWAY; } /* Administrative distance. */ @@ -366,154 +148,196 @@ static int static_route_leak( if (tag_str) tag = strtoul(tag_str, NULL, 10); - /* Labels */ - memset(&snh_label, 0, sizeof(struct static_nh_label)); - if (label_str) { - if (!mpls_enabled) { - if (vty) - vty_out(vty, - "%% MPLS not turned on in kernel, ignoring command\n"); - else - zlog_warn( - "%s: MPLS not turned on in kernel ignoring static route to %s", - __PRETTY_FUNCTION__, dest_str); - return CMD_WARNING_CONFIG_FAILED; - } - int rc = mpls_str2label(label_str, &snh_label.num_labels, - snh_label.label); - if (rc < 0) { - switch (rc) { - case -1: - if (vty) - vty_out(vty, "%% Malformed label(s)\n"); - else - zlog_warn( - "%s: Malformed labels specified for route %s", - __PRETTY_FUNCTION__, dest_str); - break; - case -2: - if (vty) - vty_out(vty, - "%% Cannot use reserved label(s) (%d-%d)\n", - MPLS_LABEL_RESERVED_MIN, - MPLS_LABEL_RESERVED_MAX); - else - zlog_warn( - "%s: Cannot use reserved labels (%d-%d) for %s", - __PRETTY_FUNCTION__, - MPLS_LABEL_RESERVED_MIN, - MPLS_LABEL_RESERVED_MAX, - dest_str); - break; - case -3: - if (vty) - vty_out(vty, - "%% Too many labels. Enter %d or fewer\n", - MPLS_MAX_LABELS); - else - zlog_warn( - "%s: Too many labels, Enter %d or fewer for %s", - __PRETTY_FUNCTION__, - MPLS_MAX_LABELS, dest_str); - break; - } - return CMD_WARNING_CONFIG_FAILED; - } - } - /* TableID */ if (table_str) table_id = atol(table_str); - /* Null0 static route. */ - if (ifname != NULL) { - if (strncasecmp(ifname, "Null0", strlen(ifname)) == 0 - || strncasecmp(ifname, "reject", strlen(ifname)) == 0 - || strncasecmp(ifname, "blackhole", strlen(ifname)) == 0) { - if (vty) - vty_out(vty, - "%% Nexthop interface cannot be Null0, reject or blackhole\n"); - else - zlog_warn( - "%s: Nexthop interface cannot be Null0, reject or blackhole for %s", - __PRETTY_FUNCTION__, dest_str); - return CMD_WARNING_CONFIG_FAILED; - } - } + static_get_nh_type(type, buf_nh_type, PREFIX_STRLEN); + if (!negate) { + if (src_str) + snprintf(ab_xpath, sizeof(ab_xpath), + FRR_DEL_S_ROUTE_SRC_NH_KEY_NO_DISTANCE_XPATH, + "frr-staticd:staticd", "staticd", svrf, + buf_prefix, + yang_afi_safi_value2identity(afi, safi), + buf_src_prefix, table_id, buf_nh_type, nh_svrf, + buf_gate_str, ifname); + else + snprintf(ab_xpath, sizeof(ab_xpath), + FRR_DEL_S_ROUTE_NH_KEY_NO_DISTANCE_XPATH, + "frr-staticd:staticd", "staticd", svrf, + buf_prefix, + yang_afi_safi_value2identity(afi, safi), + table_id, buf_nh_type, nh_svrf, buf_gate_str, + ifname); - /* Route flags */ - if (flag_str) { - switch (flag_str[0]) { - case 'r': - bh_type = STATIC_BLACKHOLE_REJECT; - break; - case 'b': - bh_type = STATIC_BLACKHOLE_DROP; - break; - case 'N': - bh_type = STATIC_BLACKHOLE_NULL; - break; - default: - if (vty) - vty_out(vty, "%% Malformed flag %s \n", - flag_str); - else - zlog_warn("%s: Malformed flag %s for %s", - __PRETTY_FUNCTION__, flag_str, - dest_str); - return CMD_WARNING_CONFIG_FAILED; + /* + * If there's already the same nexthop but with a different + * distance, then remove it for the replacement. + */ + dnode = yang_dnode_get(vty->candidate_config->dnode, ab_xpath); + if (dnode) { + dnode = yang_get_subtree_with_no_sibling(dnode); + assert(dnode); + yang_dnode_get_path(dnode, ab_xpath, XPATH_MAXLEN); + + nb_cli_enqueue_change(vty, ab_xpath, NB_OP_DESTROY, + NULL); } - } - if (gate_str) { - if (inet_pton(afi2family(afi), gate_str, &gate) != 1) { - if (vty) - vty_out(vty, - "%% Malformed nexthop address %s\n", - gate_str); + /* route + path procesing */ + if (src_str) + snprintf(xpath_prefix, sizeof(xpath_prefix), + FRR_S_ROUTE_SRC_INFO_KEY_XPATH, + "frr-staticd:staticd", "staticd", svrf, + buf_prefix, + yang_afi_safi_value2identity(afi, safi), + buf_src_prefix, table_id, distance); + else + snprintf(xpath_prefix, sizeof(xpath_prefix), + FRR_STATIC_ROUTE_INFO_KEY_XPATH, + "frr-staticd:staticd", "staticd", svrf, + buf_prefix, + yang_afi_safi_value2identity(afi, safi), + table_id, distance); + + nb_cli_enqueue_change(vty, xpath_prefix, NB_OP_CREATE, NULL); + + /* Tag processing */ + snprintf(buf_tag, sizeof(buf_tag), "%u", tag); + strlcpy(ab_xpath, xpath_prefix, sizeof(ab_xpath)); + strlcat(ab_xpath, FRR_STATIC_ROUTE_PATH_TAG_XPATH, + sizeof(ab_xpath)); + nb_cli_enqueue_change(vty, ab_xpath, NB_OP_MODIFY, buf_tag); + + /* nexthop processing */ + + snprintf(ab_xpath, sizeof(ab_xpath), + FRR_STATIC_ROUTE_NH_KEY_XPATH, buf_nh_type, nh_svrf, + buf_gate_str, ifname); + strlcpy(xpath_nexthop, xpath_prefix, sizeof(xpath_nexthop)); + strlcat(xpath_nexthop, ab_xpath, sizeof(xpath_nexthop)); + nb_cli_enqueue_change(vty, xpath_nexthop, NB_OP_CREATE, NULL); + + if (type == STATIC_BLACKHOLE) { + strlcpy(ab_xpath, xpath_nexthop, sizeof(ab_xpath)); + strlcat(ab_xpath, FRR_STATIC_ROUTE_NH_BH_XPATH, + sizeof(ab_xpath)); + + /* Route flags */ + if (flag_str) { + switch (flag_str[0]) { + case 'r': + bh_type = "reject"; + break; + case 'b': + bh_type = "unspec"; + break; + case 'N': + bh_type = "null"; + break; + default: + bh_type = NULL; + break; + } + nb_cli_enqueue_change(vty, ab_xpath, + NB_OP_MODIFY, bh_type); + } else { + nb_cli_enqueue_change(vty, ab_xpath, + NB_OP_MODIFY, "null"); + } + } + if (type == STATIC_IPV4_GATEWAY_IFNAME + || type == STATIC_IPV6_GATEWAY_IFNAME) { + strlcpy(ab_xpath, xpath_nexthop, sizeof(ab_xpath)); + strlcat(ab_xpath, FRR_STATIC_ROUTE_NH_ONLINK_XPATH, + sizeof(ab_xpath)); + + if (onlink) + nb_cli_enqueue_change(vty, ab_xpath, + NB_OP_MODIFY, "true"); else - zlog_warn( - "%s: Malformed nexthop address %s for %s", - __PRETTY_FUNCTION__, gate_str, - dest_str); - return CMD_WARNING_CONFIG_FAILED; + nb_cli_enqueue_change(vty, ab_xpath, + NB_OP_MODIFY, "false"); } - gatep = &gate; - } - - if (gate_str == NULL && ifname == NULL) - type = STATIC_BLACKHOLE; - else if (gate_str && ifname) { - if (afi == AFI_IP) - type = STATIC_IPV4_GATEWAY_IFNAME; - else - type = STATIC_IPV6_GATEWAY_IFNAME; - } else if (ifname) - type = STATIC_IFNAME; - else { - if (afi == AFI_IP) - type = STATIC_IPV4_GATEWAY; - else - type = STATIC_IPV6_GATEWAY; - } - - if (!negate) { - static_add_route(afi, safi, type, &p, src_p, gatep, ifname, - bh_type, tag, distance, svrf, nh_svrf, - &snh_label, table_id, onlink); - /* Mark as having FRR configuration */ - vrf_set_user_cfged(svrf->vrf); + if (type == STATIC_IPV4_GATEWAY + || type == STATIC_IPV6_GATEWAY + || type == STATIC_IPV4_GATEWAY_IFNAME + || type == STATIC_IPV6_GATEWAY_IFNAME) { + strlcpy(ab_xpath, xpath_nexthop, sizeof(ab_xpath)); + strlcat(ab_xpath, FRR_STATIC_ROUTE_NH_COLOR_XPATH, + sizeof(ab_xpath)); + if (color_str) + nb_cli_enqueue_change(vty, ab_xpath, + NB_OP_MODIFY, color_str); + } + if (label_str) { + /* copy of label string (start) */ + char *ostr; + /* pointer to next segment */ + char *nump; + + strlcpy(xpath_mpls, xpath_nexthop, sizeof(xpath_mpls)); + strlcat(xpath_mpls, FRR_STATIC_ROUTE_NH_LABEL_XPATH, + sizeof(xpath_mpls)); + + nb_cli_enqueue_change(vty, xpath_mpls, NB_OP_DESTROY, + NULL); + + ostr = XSTRDUP(MTYPE_TMP, label_str); + while ((nump = strsep(&ostr, "/")) != NULL) { + snprintf(ab_xpath, sizeof(ab_xpath), + FRR_STATIC_ROUTE_NHLB_KEY_XPATH, + label_stack_id); + strlcpy(xpath_label, xpath_mpls, + sizeof(xpath_label)); + strlcat(xpath_label, ab_xpath, + sizeof(xpath_label)); + nb_cli_enqueue_change(vty, xpath_label, + NB_OP_MODIFY, nump); + label_stack_id++; + } + XFREE(MTYPE_TMP, ostr); + } else { + strlcpy(xpath_mpls, xpath_nexthop, sizeof(xpath_mpls)); + strlcat(xpath_mpls, FRR_STATIC_ROUTE_NH_LABEL_XPATH, + sizeof(xpath_mpls)); + nb_cli_enqueue_change(vty, xpath_mpls, NB_OP_DESTROY, + NULL); + } + ret = nb_cli_apply_changes(vty, xpath_prefix); } else { - static_delete_route(afi, safi, type, &p, src_p, gatep, ifname, - tag, distance, svrf, &snh_label, table_id); - /* If no other FRR config for this VRF, mark accordingly. */ - if (!static_vrf_has_config(svrf)) - vrf_reset_user_cfged(svrf->vrf); + if (src_str) + snprintf(ab_xpath, sizeof(ab_xpath), + FRR_DEL_S_ROUTE_SRC_NH_KEY_NO_DISTANCE_XPATH, + "frr-staticd:staticd", "staticd", svrf, + buf_prefix, + yang_afi_safi_value2identity(afi, safi), + buf_src_prefix, table_id, buf_nh_type, nh_svrf, + buf_gate_str, ifname); + else + snprintf(ab_xpath, sizeof(ab_xpath), + FRR_DEL_S_ROUTE_NH_KEY_NO_DISTANCE_XPATH, + "frr-staticd:staticd", "staticd", svrf, + buf_prefix, + yang_afi_safi_value2identity(afi, safi), + table_id, buf_nh_type, nh_svrf, buf_gate_str, + ifname); + + dnode = yang_dnode_get(vty->candidate_config->dnode, ab_xpath); + if (!dnode) + return ret; + + dnode = yang_get_subtree_with_no_sibling(dnode); + assert(dnode); + yang_dnode_get_path(dnode, ab_xpath, XPATH_MAXLEN); + + nb_cli_enqueue_change(vty, ab_xpath, NB_OP_DESTROY, NULL); + ret = nb_cli_apply_changes(vty, ab_xpath); } - return CMD_SUCCESS; + return ret; } - static int static_route(struct vty *vty, afi_t afi, safi_t safi, const char *negate, const char *dest_str, const char *mask_str, const char *src_str, @@ -522,204 +346,141 @@ static int static_route(struct vty *vty, afi_t afi, safi_t safi, const char *distance_str, const char *vrf_name, const char *label_str, const char *table_str) { - struct static_vrf *svrf; - - /* VRF id */ - svrf = static_vrf_lookup_by_name(vrf_name); - - /* When trying to delete, the VRF must exist. */ - if (negate && !svrf) { - vty_out(vty, "%% vrf %s is not defined\n", vrf_name); - return CMD_WARNING_CONFIG_FAILED; - } - - /* When trying to create, create the VRF if it doesn't exist. - * Note: The VRF isn't active until we hear about it from the kernel. - */ - if (!svrf) { - svrf = static_vty_get_unknown_vrf(vty, vrf_name); - if (!svrf) - return CMD_WARNING_CONFIG_FAILED; - } - return static_route_leak(vty, svrf, svrf, afi, safi, negate, dest_str, - mask_str, src_str, gate_str, ifname, flag_str, - tag_str, distance_str, label_str, table_str, - false); -} - -void static_config_install_delayed_routes(struct static_vrf *svrf) -{ - struct listnode *node, *nnode; - struct static_hold_route *shr; - struct static_vrf *osvrf, *nh_svrf; - int installed; - - for (ALL_LIST_ELEMENTS(static_list, node, nnode, shr)) { - osvrf = static_vrf_lookup_by_name(shr->vrf_name); - nh_svrf = static_vrf_lookup_by_name(shr->nhvrf_name); - - if (osvrf != svrf && nh_svrf != svrf) - continue; - - if (osvrf->vrf->vrf_id == VRF_UNKNOWN - || nh_svrf->vrf->vrf_id == VRF_UNKNOWN) - continue; + if (!vrf_name) + vrf_name = VRF_DEFAULT_NAME; - installed = static_route_leak( - NULL, osvrf, nh_svrf, shr->afi, shr->safi, NULL, - shr->dest_str, shr->mask_str, shr->src_str, - shr->gate_str, shr->ifname, shr->flag_str, shr->tag_str, - shr->distance_str, shr->label_str, shr->table_str, - shr->onlink); - - if (installed != CMD_SUCCESS) - zlog_debug( - "%s: Attempt to install %s as a route and it was rejected", - __PRETTY_FUNCTION__, shr->dest_str); - listnode_delete(static_list, shr); - static_list_delete(shr); - } + return static_route_leak(vty, vrf_name, vrf_name, afi, safi, negate, + dest_str, mask_str, src_str, gate_str, ifname, + flag_str, tag_str, distance_str, label_str, + table_str, false, NULL); } /* Write static route configuration. */ int static_config(struct vty *vty, struct static_vrf *svrf, afi_t afi, safi_t safi, const char *cmd) { - struct static_hold_route *shr; - struct listnode *node; char spacing[100]; struct route_node *rn; - struct static_route *si; + struct static_nexthop *nh; + struct static_path *pn; struct route_table *stable; + struct static_route_info *si; char buf[SRCDEST2STR_BUFFER]; int write = 0; + struct stable_info *info; stable = svrf->stable[afi][safi]; if (stable == NULL) return write; - sprintf(spacing, "%s%s", (svrf->vrf->vrf_id == VRF_DEFAULT) ? "" : " ", - cmd); - - /* - * Static routes for vrfs not fully inited - */ - for (ALL_LIST_ELEMENTS_RO(static_list, node, shr)) { - if (shr->afi != afi || shr->safi != safi) - continue; + snprintf(spacing, sizeof(spacing), "%s%s", + (svrf->vrf->vrf_id == VRF_DEFAULT) ? "" : " ", cmd); - if (strcmp(svrf->vrf->name, shr->vrf_name) != 0) + for (rn = route_top(stable); rn; rn = srcdest_route_next(rn)) { + si = static_route_info_from_rnode(rn); + if (!si) continue; - - char dest_str[PREFIX_STRLEN]; - - prefix2str(&shr->dest, dest_str, sizeof(dest_str)); - - vty_out(vty, "%s ", spacing); - if (shr->dest_str) - vty_out(vty, "%s ", dest_str); - if (shr->src_str) - vty_out(vty, "from %s ", shr->src_str); - if (shr->gate_str) - vty_out(vty, "%s ", shr->gate_str); - if (shr->ifname) - vty_out(vty, "%s ", shr->ifname); - if (shr->flag_str) - vty_out(vty, "%s ", shr->flag_str); - if (shr->tag_str) - vty_out(vty, "tag %s ", shr->tag_str); - if (shr->distance_str) - vty_out(vty, "%s ", shr->distance_str); - if (shr->label_str) - vty_out(vty, "label %s ", shr->label_str); - if (shr->table_str) - vty_out(vty, "table %s", shr->table_str); - if (strcmp(shr->vrf_name, shr->nhvrf_name) != 0) - vty_out(vty, "nexthop-vrf %s ", shr->nhvrf_name); - if (shr->onlink) - vty_out(vty, "onlink"); - vty_out(vty, "\n"); - } - - for (rn = route_top(stable); rn; rn = srcdest_route_next(rn)) - for (si = rn->info; si; si = si->next) { - vty_out(vty, "%s %s", spacing, - srcdest_rnode2str(rn, buf, sizeof(buf))); - - switch (si->type) { - case STATIC_IPV4_GATEWAY: - vty_out(vty, " %s", inet_ntoa(si->addr.ipv4)); - break; - case STATIC_IPV6_GATEWAY: - vty_out(vty, " %s", - inet_ntop(AF_INET6, &si->addr.ipv6, buf, - sizeof(buf))); - break; - case STATIC_IFNAME: - vty_out(vty, " %s", si->ifname); - break; - case STATIC_BLACKHOLE: - switch (si->bh_type) { - case STATIC_BLACKHOLE_DROP: - vty_out(vty, " blackhole"); + info = static_get_stable_info(rn); + frr_each(static_path_list, &si->path_list, pn) { + frr_each(static_nexthop_list, &pn->nexthop_list, nh) { + vty_out(vty, "%s %s", spacing, + srcdest_rnode2str(rn, buf, + sizeof(buf))); + + switch (nh->type) { + case STATIC_IPV4_GATEWAY: + vty_out(vty, " %s", + inet_ntoa(nh->addr.ipv4)); break; - case STATIC_BLACKHOLE_NULL: - vty_out(vty, " Null0"); + case STATIC_IPV6_GATEWAY: + vty_out(vty, " %s", + inet_ntop(AF_INET6, + &nh->addr.ipv6, buf, + sizeof(buf))); break; - case STATIC_BLACKHOLE_REJECT: - vty_out(vty, " reject"); + case STATIC_IFNAME: + vty_out(vty, " %s", nh->ifname); + break; + case STATIC_BLACKHOLE: + switch (nh->bh_type) { + case STATIC_BLACKHOLE_DROP: + vty_out(vty, " blackhole"); + break; + case STATIC_BLACKHOLE_NULL: + vty_out(vty, " Null0"); + break; + case STATIC_BLACKHOLE_REJECT: + vty_out(vty, " reject"); + break; + } + break; + case STATIC_IPV4_GATEWAY_IFNAME: + vty_out(vty, " %s %s", + inet_ntop(AF_INET, + &nh->addr.ipv4, buf, + sizeof(buf)), + nh->ifname); + break; + case STATIC_IPV6_GATEWAY_IFNAME: + vty_out(vty, " %s %s", + inet_ntop(AF_INET6, + &nh->addr.ipv6, buf, + sizeof(buf)), + nh->ifname); break; } - break; - case STATIC_IPV4_GATEWAY_IFNAME: - vty_out(vty, " %s %s", - inet_ntop(AF_INET, &si->addr.ipv4, buf, - sizeof(buf)), - si->ifname); - break; - case STATIC_IPV6_GATEWAY_IFNAME: - vty_out(vty, " %s %s", - inet_ntop(AF_INET6, &si->addr.ipv6, buf, - sizeof(buf)), - si->ifname); - break; - } - - if (si->tag) - vty_out(vty, " tag %" ROUTE_TAG_PRI, si->tag); - - if (si->distance != ZEBRA_STATIC_DISTANCE_DEFAULT) - vty_out(vty, " %d", si->distance); - - /* Label information */ - if (si->snh_label.num_labels) - vty_out(vty, " label %s", - mpls_label2str(si->snh_label.num_labels, - si->snh_label.label, buf, - sizeof(buf), 0)); - - if (si->nh_vrf_id != si->vrf_id) - vty_out(vty, " nexthop-vrf %s", si->nh_vrfname); - /* - * table ID from VRF overrides configured - */ - if (si->table_id && - svrf->vrf->data.l.table_id == RT_TABLE_MAIN) - vty_out(vty, " table %u", si->table_id); - - if (si->onlink) - vty_out(vty, " onlink"); - - vty_out(vty, "\n"); - - write = 1; + if (pn->tag) + vty_out(vty, " tag %" ROUTE_TAG_PRI, + pn->tag); + + if (pn->distance + != ZEBRA_STATIC_DISTANCE_DEFAULT) + vty_out(vty, " %u", pn->distance); + + /* Label information */ + if (nh->snh_label.num_labels) + vty_out(vty, " label %s", + mpls_label2str( + nh->snh_label + .num_labels, + nh->snh_label.label, + buf, sizeof(buf), 0)); + + if (!strmatch(nh->nh_vrfname, + info->svrf->vrf->name)) + vty_out(vty, " nexthop-vrf %s", + nh->nh_vrfname); + + /* + * table ID from VRF overrides + * configured + */ + if (pn->table_id + && svrf->vrf->data.l.table_id + == RT_TABLE_MAIN) + vty_out(vty, " table %u", pn->table_id); + + if (nh->onlink) + vty_out(vty, " onlink"); + + /* + * SR-TE color + */ + if (nh->color != 0) + vty_out(vty, " color %u", nh->color); + + vty_out(vty, "\n"); + + write = 1; + } } + } return write; } /* Static unicast routes for multicast RPF lookup. */ -DEFPY (ip_mroute_dist, +DEFPY_YANG (ip_mroute_dist, ip_mroute_dist_cmd, "[no] ip mroute A.B.C.D/M$prefix [(1-255)$distance]", NO_STR @@ -736,7 +497,7 @@ DEFPY (ip_mroute_dist, } /* Static route configuration. */ -DEFPY(ip_route_blackhole, +DEFPY_YANG(ip_route_blackhole, ip_route_blackhole_cmd, "[no] ip route\ \ @@ -763,18 +524,12 @@ DEFPY(ip_route_blackhole, "Table to configure\n" "The table number to configure\n") { - if (table_str && vrf && !vrf_is_backend_netns()) { - vty_out(vty, - "%% table param only available when running on netns-based vrfs\n"); - return CMD_WARNING_CONFIG_FAILED; - } - return static_route(vty, AFI_IP, SAFI_UNICAST, no, prefix, mask_str, NULL, NULL, NULL, flag, tag_str, distance_str, vrf, label, table_str); } -DEFPY(ip_route_blackhole_vrf, +DEFPY_YANG(ip_route_blackhole_vrf, ip_route_blackhole_vrf_cmd, "[no] ip route\ \ @@ -799,28 +554,29 @@ DEFPY(ip_route_blackhole_vrf, "Table to configure\n" "The table number to configure\n") { - VTY_DECLVAR_CONTEXT(vrf, vrf); - struct static_vrf *svrf = vrf->info; + const struct lyd_node *vrf_dnode; + const char *vrfname; - if (table_str && !vrf_is_backend_netns()) { - vty_out(vty, - "%% table param only available when running on netns-based vrfs\n"); + vrf_dnode = + yang_dnode_get(vty->candidate_config->dnode, VTY_CURR_XPATH); + if (!vrf_dnode) { + vty_out(vty, "%% Failed to get vrf dnode in candidate db\n"); return CMD_WARNING_CONFIG_FAILED; } - + vrfname = yang_dnode_get_string(vrf_dnode, "./name"); /* * Coverity is complaining that prefix could * be dereferenced, but we know that prefix will * valid. Add an assert to make it happy */ assert(prefix); - return static_route_leak(vty, svrf, svrf, AFI_IP, SAFI_UNICAST, no, - prefix, mask_str, NULL, NULL, NULL, flag, + return static_route_leak(vty, vrfname, vrfname, AFI_IP, SAFI_UNICAST, + no, prefix, mask_str, NULL, NULL, NULL, flag, tag_str, distance_str, label, table_str, - false); + false, NULL); } -DEFPY(ip_route_address_interface, +DEFPY_YANG(ip_route_address_interface, ip_route_address_interface_cmd, "[no] ip route\ \ @@ -834,6 +590,7 @@ DEFPY(ip_route_address_interface, |table (1-4294967295) \ |nexthop-vrf NAME \ |onlink$onlink \ + |color (1-4294967295) \ }]", NO_STR IP_STR "Establish static routes\n" @@ -851,46 +608,32 @@ DEFPY(ip_route_address_interface, "Table to configure\n" "The table number to configure\n" VRF_CMD_HELP_STR - "Treat the nexthop as directly attached to the interface") + "Treat the nexthop as directly attached to the interface\n" + "SR-TE color\n" + "The SR-TE color to configure\n") { - struct static_vrf *svrf; - struct static_vrf *nh_svrf; + const char *nh_vrf; const char *flag = NULL; if (ifname && !strncasecmp(ifname, "Null0", 5)) { flag = "Null0"; ifname = NULL; } - - svrf = static_vty_get_unknown_vrf(vty, vrf); - if (!svrf) { - vty_out(vty, "%% vrf %s is not defined\n", vrf); - return CMD_WARNING_CONFIG_FAILED; - } - - if (table_str && vrf && !vrf_is_backend_netns()) { - vty_out(vty, - "%% table param only available when running on netns-based vrfs\n"); - return CMD_WARNING_CONFIG_FAILED; - } + if (!vrf) + vrf = VRF_DEFAULT_NAME; if (nexthop_vrf) - nh_svrf = static_vty_get_unknown_vrf(vty, nexthop_vrf); + nh_vrf = nexthop_vrf; else - nh_svrf = svrf; - - if (!nh_svrf) { - vty_out(vty, "%% nexthop vrf %s is not defined\n", nexthop_vrf); - return CMD_WARNING_CONFIG_FAILED; - } + nh_vrf = vrf; - return static_route_leak(vty, svrf, nh_svrf, AFI_IP, SAFI_UNICAST, no, + return static_route_leak(vty, vrf, nh_vrf, AFI_IP, SAFI_UNICAST, no, prefix, mask_str, NULL, gate_str, ifname, flag, tag_str, distance_str, label, table_str, - !!onlink); + !!onlink, color_str); } -DEFPY(ip_route_address_interface_vrf, +DEFPY_YANG(ip_route_address_interface_vrf, ip_route_address_interface_vrf_cmd, "[no] ip route\ \ @@ -903,7 +646,8 @@ DEFPY(ip_route_address_interface_vrf, |table (1-4294967295) \ |nexthop-vrf NAME \ |onlink$onlink \ - }]", + |color (1-4294967295) \ + }]", NO_STR IP_STR "Establish static routes\n" "IP destination prefix (e.g. 10.0.0.0/8)\n" @@ -919,41 +663,39 @@ DEFPY(ip_route_address_interface_vrf, "Table to configure\n" "The table number to configure\n" VRF_CMD_HELP_STR - "Treat the nexthop as directly attached to the interface") + "Treat the nexthop as directly attached to the interface\n" + "SR-TE color\n" + "The SR-TE color to configure\n") { - VTY_DECLVAR_CONTEXT(vrf, vrf); + const char *nh_vrf; const char *flag = NULL; - struct static_vrf *svrf = vrf->info; - struct static_vrf *nh_svrf; + const struct lyd_node *vrf_dnode; + const char *vrfname; - if (table_str && !vrf_is_backend_netns()) { - vty_out(vty, - "%% table param only available when running on netns-based vrfs\n"); + vrf_dnode = + yang_dnode_get(vty->candidate_config->dnode, VTY_CURR_XPATH); + if (!vrf_dnode) { + vty_out(vty, "%% Failed to get vrf dnode in candidate db\n"); return CMD_WARNING_CONFIG_FAILED; } + vrfname = yang_dnode_get_string(vrf_dnode, "./name"); if (ifname && !strncasecmp(ifname, "Null0", 5)) { flag = "Null0"; ifname = NULL; } - if (nexthop_vrf) - nh_svrf = static_vty_get_unknown_vrf(vty, nexthop_vrf); + nh_vrf = nexthop_vrf; else - nh_svrf = svrf; + nh_vrf = vrfname; - if (!nh_svrf) { - vty_out(vty, "%% nexthop vrf %s is not defined\n", nexthop_vrf); - return CMD_WARNING_CONFIG_FAILED; - } - - return static_route_leak(vty, svrf, nh_svrf, AFI_IP, SAFI_UNICAST, no, + return static_route_leak(vty, vrfname, nh_vrf, AFI_IP, SAFI_UNICAST, no, prefix, mask_str, NULL, gate_str, ifname, flag, tag_str, distance_str, label, table_str, - !!onlink); + !!onlink, color_str); } -DEFPY(ip_route, +DEFPY_YANG(ip_route, ip_route_cmd, "[no] ip route\ \ @@ -965,6 +707,7 @@ DEFPY(ip_route, |label WORD \ |table (1-4294967295) \ |nexthop-vrf NAME \ + |color (1-4294967295) \ }]", NO_STR IP_STR "Establish static routes\n" @@ -981,46 +724,33 @@ DEFPY(ip_route, MPLS_LABEL_HELPSTR "Table to configure\n" "The table number to configure\n" - VRF_CMD_HELP_STR) + VRF_CMD_HELP_STR + "SR-TE color\n" + "The SR-TE color to configure\n") { - struct static_vrf *svrf; - struct static_vrf *nh_svrf; + const char *nh_vrf; const char *flag = NULL; - if (table_str && vrf && !vrf_is_backend_netns()) { - vty_out(vty, - "%% table param only available when running on netns-based vrfs\n"); - return CMD_WARNING_CONFIG_FAILED; - } - if (ifname && !strncasecmp(ifname, "Null0", 5)) { flag = "Null0"; ifname = NULL; } - svrf = static_vty_get_unknown_vrf(vty, vrf); - if (!svrf) { - vty_out(vty, "%% vrf %s is not defined\n", vrf); - return CMD_WARNING_CONFIG_FAILED; - } + if (!vrf) + vrf = VRF_DEFAULT_NAME; if (nexthop_vrf) - nh_svrf = static_vty_get_unknown_vrf(vty, nexthop_vrf); + nh_vrf = nexthop_vrf; else - nh_svrf = svrf; - - if (!nh_svrf) { - vty_out(vty, "%% nexthop vrf %s is not defined\n", nexthop_vrf); - return CMD_WARNING_CONFIG_FAILED; - } + nh_vrf = vrf; - return static_route_leak( - vty, svrf, nh_svrf, AFI_IP, SAFI_UNICAST, no, prefix, mask_str, - NULL, gate_str, ifname, flag, tag_str, distance_str, label, - table_str, false); + return static_route_leak(vty, vrf, nh_vrf, AFI_IP, SAFI_UNICAST, no, + prefix, mask_str, NULL, gate_str, ifname, flag, + tag_str, distance_str, label, table_str, + false, color_str); } -DEFPY(ip_route_vrf, +DEFPY_YANG(ip_route_vrf, ip_route_vrf_cmd, "[no] ip route\ \ @@ -1031,6 +761,7 @@ DEFPY(ip_route_vrf, |label WORD \ |table (1-4294967295) \ |nexthop-vrf NAME \ + |color (1-4294967295) \ }]", NO_STR IP_STR "Establish static routes\n" @@ -1046,41 +777,40 @@ DEFPY(ip_route_vrf, MPLS_LABEL_HELPSTR "Table to configure\n" "The table number to configure\n" - VRF_CMD_HELP_STR) + VRF_CMD_HELP_STR + "SR-TE color\n" + "The SR-TE color to configure\n") { - VTY_DECLVAR_CONTEXT(vrf, vrf); - struct static_vrf *svrf = vrf->info; - struct static_vrf *nh_svrf; + const char *nh_vrf; const char *flag = NULL; + const struct lyd_node *vrf_dnode; + const char *vrfname; - if (table_str && !vrf_is_backend_netns()) { - vty_out(vty, - "%% table param only available when running on netns-based vrfs\n"); + vrf_dnode = + yang_dnode_get(vty->candidate_config->dnode, VTY_CURR_XPATH); + if (!vrf_dnode) { + vty_out(vty, "%% Failed to get vrf dnode in candidate db\n"); return CMD_WARNING_CONFIG_FAILED; } + vrfname = yang_dnode_get_string(vrf_dnode, "./name"); + if (ifname && !strncasecmp(ifname, "Null0", 5)) { flag = "Null0"; ifname = NULL; } - if (nexthop_vrf) - nh_svrf = static_vty_get_unknown_vrf(vty, nexthop_vrf); + nh_vrf = nexthop_vrf; else - nh_svrf = svrf; - - if (!nh_svrf) { - vty_out(vty, "%% nexthop vrf %s is not defined\n", nexthop_vrf); - return CMD_WARNING_CONFIG_FAILED; - } + nh_vrf = vrfname; - return static_route_leak( - vty, svrf, nh_svrf, AFI_IP, SAFI_UNICAST, no, prefix, mask_str, - NULL, gate_str, ifname, flag, tag_str, distance_str, label, - table_str, false); + return static_route_leak(vty, vrfname, nh_vrf, AFI_IP, SAFI_UNICAST, no, + prefix, mask_str, NULL, gate_str, ifname, flag, + tag_str, distance_str, label, table_str, + false, color_str); } -DEFPY(ipv6_route_blackhole, +DEFPY_YANG(ipv6_route_blackhole, ipv6_route_blackhole_cmd, "[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \ $flag \ @@ -1107,18 +837,12 @@ DEFPY(ipv6_route_blackhole, "Table to configure\n" "The table number to configure\n") { - if (table_str && vrf && !vrf_is_backend_netns()) { - vty_out(vty, - "%% table param only available when running on netns-based vrfs\n"); - return CMD_WARNING_CONFIG_FAILED; - } - return static_route(vty, AFI_IP6, SAFI_UNICAST, no, prefix_str, NULL, from_str, NULL, NULL, flag, tag_str, distance_str, vrf, label, table_str); } -DEFPY(ipv6_route_blackhole_vrf, +DEFPY_YANG(ipv6_route_blackhole_vrf, ipv6_route_blackhole_vrf_cmd, "[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \ $flag \ @@ -1143,14 +867,16 @@ DEFPY(ipv6_route_blackhole_vrf, "Table to configure\n" "The table number to configure\n") { - VTY_DECLVAR_CONTEXT(vrf, vrf); - struct static_vrf *svrf = vrf->info; + const struct lyd_node *vrf_dnode; + const char *vrfname; - if (table_str && !vrf_is_backend_netns()) { - vty_out(vty, - "%% table param only available when running on netns-based vrfs\n"); + vrf_dnode = + yang_dnode_get(vty->candidate_config->dnode, VTY_CURR_XPATH); + if (!vrf_dnode) { + vty_out(vty, "%% Failed to get vrf dnode in candidate db\n"); return CMD_WARNING_CONFIG_FAILED; } + vrfname = yang_dnode_get_string(vrf_dnode, "./name"); /* * Coverity is complaining that prefix could @@ -1158,13 +884,14 @@ DEFPY(ipv6_route_blackhole_vrf, * valid. Add an assert to make it happy */ assert(prefix); - return static_route_leak( - vty, svrf, svrf, AFI_IP6, SAFI_UNICAST, no, prefix_str, NULL, - from_str, NULL, NULL, flag, tag_str, distance_str, label, - table_str, false); + + return static_route_leak(vty, vrfname, vrfname, AFI_IP6, SAFI_UNICAST, + no, prefix_str, NULL, from_str, NULL, NULL, + flag, tag_str, distance_str, label, table_str, + false, NULL); } -DEFPY(ipv6_route_address_interface, +DEFPY_YANG(ipv6_route_address_interface, ipv6_route_address_interface_cmd, "[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \ X:X::X:X$gate \ @@ -1177,6 +904,7 @@ DEFPY(ipv6_route_address_interface, |table (1-4294967295) \ |nexthop-vrf NAME \ |onlink$onlink \ + |color (1-4294967295) \ }]", NO_STR IPV6_STR @@ -1195,46 +923,33 @@ DEFPY(ipv6_route_address_interface, "Table to configure\n" "The table number to configure\n" VRF_CMD_HELP_STR - "Treat the nexthop as directly attached to the interface") + "Treat the nexthop as directly attached to the interface\n" + "SR-TE color\n" + "The SR-TE color to configure\n") { - struct static_vrf *svrf; - struct static_vrf *nh_svrf; + const char *nh_vrf; const char *flag = NULL; - if (table_str && vrf && !vrf_is_backend_netns()) { - vty_out(vty, - "%% table param only available when running on netns-based vrfs\n"); - return CMD_WARNING_CONFIG_FAILED; + if (ifname && !strncasecmp(ifname, "Null0", 5)) { + flag = "Null0"; + ifname = NULL; } - svrf = static_vty_get_unknown_vrf(vty, vrf); - if (!svrf) { - vty_out(vty, "%% vrf %s is not defined\n", vrf); - return CMD_WARNING_CONFIG_FAILED; - } + if (!vrf) + vrf = VRF_DEFAULT_NAME; if (nexthop_vrf) - nh_svrf = static_vty_get_unknown_vrf(vty, nexthop_vrf); + nh_vrf = nexthop_vrf; else - nh_svrf = svrf; - - if (!nh_svrf) { - vty_out(vty, "%% nexthop vrf %s is not defined\n", nexthop_vrf); - return CMD_WARNING_CONFIG_FAILED; - } - - if (ifname && !strncasecmp(ifname, "Null0", 5)) { - flag = "Null0"; - ifname = NULL; - } + nh_vrf = vrf; - return static_route_leak( - vty, svrf, nh_svrf, AFI_IP6, SAFI_UNICAST, no, prefix_str, NULL, - from_str, gate_str, ifname, flag, tag_str, distance_str, label, - table_str, !!onlink); + return static_route_leak(vty, vrf, nh_vrf, AFI_IP6, SAFI_UNICAST, no, + prefix_str, NULL, from_str, gate_str, ifname, + flag, tag_str, distance_str, label, table_str, + !!onlink, color_str); } -DEFPY(ipv6_route_address_interface_vrf, +DEFPY_YANG(ipv6_route_address_interface_vrf, ipv6_route_address_interface_vrf_cmd, "[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \ X:X::X:X$gate \ @@ -1246,6 +961,7 @@ DEFPY(ipv6_route_address_interface_vrf, |table (1-4294967295) \ |nexthop-vrf NAME \ |onlink$onlink \ + |color (1-4294967295) \ }]", NO_STR IPV6_STR @@ -1263,41 +979,39 @@ DEFPY(ipv6_route_address_interface_vrf, "Table to configure\n" "The table number to configure\n" VRF_CMD_HELP_STR - "Treat the nexthop as directly attached to the interface") + "Treat the nexthop as directly attached to the interface\n" + "SR-TE color\n" + "The SR-TE color to configure\n") { - VTY_DECLVAR_CONTEXT(vrf, vrf); - struct static_vrf *svrf = vrf->info; - struct static_vrf *nh_svrf; + const char *nh_vrf; const char *flag = NULL; + const struct lyd_node *vrf_dnode; + const char *vrfname; - if (table_str && !vrf_is_backend_netns()) { - vty_out(vty, - "%% table param only available when running on netns-based vrfs\n"); + vrf_dnode = + yang_dnode_get(vty->candidate_config->dnode, VTY_CURR_XPATH); + if (!vrf_dnode) { + vty_out(vty, "%% Failed to get vrf dnode in candidate db\n"); return CMD_WARNING_CONFIG_FAILED; } + vrfname = yang_dnode_get_string(vrf_dnode, "./name"); if (nexthop_vrf) - nh_svrf = static_vty_get_unknown_vrf(vty, nexthop_vrf); + nh_vrf = nexthop_vrf; else - nh_svrf = svrf; - - if (!nh_svrf) { - vty_out(vty, "%% nexthop vrf %s is not defined\n", nexthop_vrf); - return CMD_WARNING_CONFIG_FAILED; - } + nh_vrf = vrfname; if (ifname && !strncasecmp(ifname, "Null0", 5)) { flag = "Null0"; ifname = NULL; } - - return static_route_leak( - vty, svrf, nh_svrf, AFI_IP6, SAFI_UNICAST, no, prefix_str, NULL, - from_str, gate_str, ifname, flag, tag_str, distance_str, label, - table_str, !!onlink); + return static_route_leak(vty, vrfname, nh_vrf, AFI_IP6, SAFI_UNICAST, + no, prefix_str, NULL, from_str, gate_str, + ifname, flag, tag_str, distance_str, label, + table_str, !!onlink, color_str); } -DEFPY(ipv6_route, +DEFPY_YANG(ipv6_route, ipv6_route_cmd, "[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \ $ifname> \ @@ -1308,6 +1022,7 @@ DEFPY(ipv6_route, |label WORD \ |table (1-4294967295) \ |nexthop-vrf NAME \ + |color (1-4294967295) \ }]", NO_STR IPV6_STR @@ -1325,46 +1040,32 @@ DEFPY(ipv6_route, MPLS_LABEL_HELPSTR "Table to configure\n" "The table number to configure\n" - VRF_CMD_HELP_STR) + VRF_CMD_HELP_STR + "SR-TE color\n" + "The SR-TE color to configure\n") { - struct static_vrf *svrf; - struct static_vrf *nh_svrf; + const char *nh_vrf; const char *flag = NULL; - if (table_str && vrf && !vrf_is_backend_netns()) { - vty_out(vty, - "%% table param only available when running on netns-based vrfs\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - svrf = static_vty_get_unknown_vrf(vty, vrf); - if (!svrf) { - vty_out(vty, "%% vrf %s is not defined\n", vrf); - return CMD_WARNING_CONFIG_FAILED; - } + if (!vrf) + vrf = VRF_DEFAULT_NAME; if (nexthop_vrf) - nh_svrf = static_vty_get_unknown_vrf(vty, nexthop_vrf); + nh_vrf = nexthop_vrf; else - nh_svrf = svrf; - - if (!nh_svrf) { - vty_out(vty, "%% nexthop vrf %s is not defined\n", nexthop_vrf); - return CMD_WARNING_CONFIG_FAILED; - } + nh_vrf = vrf; if (ifname && !strncasecmp(ifname, "Null0", 5)) { flag = "Null0"; ifname = NULL; } - - return static_route_leak( - vty, svrf, nh_svrf, AFI_IP6, SAFI_UNICAST, no, prefix_str, NULL, - from_str, gate_str, ifname, flag, tag_str, distance_str, label, - table_str, false); + return static_route_leak(vty, vrf, nh_vrf, AFI_IP6, SAFI_UNICAST, no, + prefix_str, NULL, from_str, gate_str, ifname, + flag, tag_str, distance_str, label, table_str, + false, color_str); } -DEFPY(ipv6_route_vrf, +DEFPY_YANG(ipv6_route_vrf, ipv6_route_vrf_cmd, "[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \ $ifname> \ @@ -1374,6 +1075,7 @@ DEFPY(ipv6_route_vrf, |label WORD \ |table (1-4294967295) \ |nexthop-vrf NAME \ + |color (1-4294967295) \ }]", NO_STR IPV6_STR @@ -1390,54 +1092,79 @@ DEFPY(ipv6_route_vrf, MPLS_LABEL_HELPSTR "Table to configure\n" "The table number to configure\n" - VRF_CMD_HELP_STR) + VRF_CMD_HELP_STR + "SR-TE color\n" + "The SR-TE color to configure\n") { - VTY_DECLVAR_CONTEXT(vrf, vrf); - struct static_vrf *svrf = vrf->info; - struct static_vrf *nh_svrf; + const char *nh_vrf; const char *flag = NULL; + const struct lyd_node *vrf_dnode; + const char *vrfname; - if (table_str && !vrf_is_backend_netns()) { - vty_out(vty, - "%% table param only available when running on netns-based vrfs\n"); + vrf_dnode = + yang_dnode_get(vty->candidate_config->dnode, VTY_CURR_XPATH); + if (!vrf_dnode) { + vty_out(vty, "%% Failed to get vrf dnode in candidate db\n"); return CMD_WARNING_CONFIG_FAILED; } + vrfname = yang_dnode_get_string(vrf_dnode, "./name"); if (nexthop_vrf) - nh_svrf = static_vty_get_unknown_vrf(vty, nexthop_vrf); + nh_vrf = nexthop_vrf; else - nh_svrf = svrf; - - if (!nh_svrf) { - vty_out(vty, "%% nexthop vrf %s is not defined\n", nexthop_vrf); - return CMD_WARNING_CONFIG_FAILED; - } + nh_vrf = vrfname; if (ifname && !strncasecmp(ifname, "Null0", 5)) { flag = "Null0"; ifname = NULL; } + return static_route_leak(vty, vrfname, nh_vrf, AFI_IP6, SAFI_UNICAST, + no, prefix_str, NULL, from_str, gate_str, + ifname, flag, tag_str, distance_str, label, + table_str, false, color_str); +} +DEFPY_YANG(debug_staticd, + debug_staticd_cmd, + "[no] debug static [{events$events}]", + NO_STR + DEBUG_STR + STATICD_STR + "Debug events\n") +{ + /* If no specific category, change all */ + if (strmatch(argv[argc - 1]->text, "static")) + static_debug_set(vty->node, !no, true); + else + static_debug_set(vty->node, !no, !!events); - return static_route_leak( - vty, svrf, nh_svrf, AFI_IP6, SAFI_UNICAST, no, prefix_str, NULL, - from_str, gate_str, ifname, flag, tag_str, distance_str, label, - table_str, false); + return CMD_SUCCESS; } -DEFUN_NOSH (show_debugging_staticd, - show_debugging_staticd_cmd, +DEFUN_NOSH (show_debugging_static, + show_debugging_static_cmd, "show debugging [static]", SHOW_STR DEBUG_STR "Static Information\n") { - vty_out(vty, "Static debugging status\n"); + vty_out(vty, "Staticd debugging status\n"); + + static_debug_status_write(vty); return CMD_SUCCESS; } +static struct cmd_node debug_node = { + .name = "debug", + .node = DEBUG_NODE, + .prompt = "", + .config_write = static_config_write_debug, +}; + void static_vty_init(void) { + install_node(&debug_node); + install_element(CONFIG_NODE, &ip_mroute_dist_cmd); install_element(CONFIG_NODE, &ip_route_blackhole_cmd); @@ -1454,9 +1181,7 @@ void static_vty_init(void) install_element(CONFIG_NODE, &ipv6_route_cmd); install_element(VRF_NODE, &ipv6_route_vrf_cmd); - install_element(VIEW_NODE, &show_debugging_staticd_cmd); - - static_list = list_new(); - static_list->cmp = (int (*)(void *, void *))static_list_compare; - static_list->del = (void (*)(void *))static_list_delete; + install_element(ENABLE_NODE, &show_debugging_static_cmd); + install_element(ENABLE_NODE, &debug_staticd_cmd); + install_element(CONFIG_NODE, &debug_staticd_cmd); } diff --git a/staticd/static_vty.h b/staticd/static_vty.h index 2f65c08b8b..7ffc8d9c98 100644 --- a/staticd/static_vty.h +++ b/staticd/static_vty.h @@ -19,8 +19,6 @@ #ifndef __STATIC_VTY_H__ #define __STATIC_VTY_H__ -void static_config_install_delayed_routes(struct static_vrf *svrf); - int static_config(struct vty *vty, struct static_vrf *svrf, afi_t afi, safi_t safi, const char *cmd); diff --git a/staticd/static_zebra.c b/staticd/static_zebra.c index 3f31177524..57903daaed 100644 --- a/staticd/static_zebra.c +++ b/staticd/static_zebra.c @@ -43,111 +43,69 @@ #include "static_nht.h" #include "static_vty.h" +bool debug; + /* Zebra structure to hold current status. */ struct zclient *zclient; static struct hash *static_nht_hash; -static struct interface *zebra_interface_if_lookup(struct stream *s) -{ - char ifname_tmp[INTERFACE_NAMSIZ]; - - /* Read interface name. */ - stream_get(ifname_tmp, s, INTERFACE_NAMSIZ); - - /* And look it up. */ - return if_lookup_by_name(ifname_tmp, VRF_DEFAULT); -} - /* Inteface addition message from zebra. */ -static int interface_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int static_ifp_create(struct interface *ifp) { - struct interface *ifp; - - ifp = zebra_interface_add_read(zclient->ibuf, vrf_id); - - if (!ifp) - return 0; - static_ifindex_update(ifp, true); + return 0; } -static int interface_delete(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int static_ifp_destroy(struct interface *ifp) { - struct interface *ifp; - struct stream *s; - - s = zclient->ibuf; - /* zebra_interface_state_read () updates interface structure in iflist - */ - ifp = zebra_interface_state_read(s, vrf_id); - - if (ifp == NULL) - return 0; - - if_set_index(ifp, IFINDEX_INTERNAL); - static_ifindex_update(ifp, false); return 0; } -static int interface_address_add(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int interface_address_add(ZAPI_CALLBACK_ARGS) { - zebra_interface_address_read(command, zclient->ibuf, vrf_id); + zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); return 0; } -static int interface_address_delete(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int interface_address_delete(ZAPI_CALLBACK_ARGS) { struct connected *c; - c = zebra_interface_address_read(command, zclient->ibuf, vrf_id); + c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); if (!c) return 0; - connected_free(c); + connected_free(&c); return 0; } -static int interface_state_up(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int static_ifp_up(struct interface *ifp) { - struct interface *ifp; - - ifp = zebra_interface_if_lookup(zclient->ibuf); - - if (ifp) { - if (if_is_vrf(ifp)) { - struct static_vrf *svrf = - static_vrf_lookup_by_id(vrf_id); + if (if_is_vrf(ifp)) { + struct static_vrf *svrf = static_vrf_lookup_by_id(ifp->vrf_id); - static_fixup_vrf_ids(svrf); - static_config_install_delayed_routes(svrf); - } - - /* Install any static reliant on this interface coming up */ - static_install_intf_nh(ifp); + static_fixup_vrf_ids(svrf); } + /* Install any static reliant on this interface coming up */ + static_install_intf_nh(ifp); + static_ifindex_update(ifp, true); + return 0; } -static int interface_state_down(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int static_ifp_down(struct interface *ifp) { - zebra_interface_state_read(zclient->ibuf, vrf_id); + static_ifindex_update(ifp, false); return 0; } -static int route_notify_owner(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +static int route_notify_owner(ZAPI_CALLBACK_ARGS) { struct prefix p; enum zapi_route_notify_owner note; @@ -161,20 +119,26 @@ static int route_notify_owner(int command, struct zclient *zclient, switch (note) { case ZAPI_ROUTE_FAIL_INSTALL: + static_nht_mark_state(&p, vrf_id, STATIC_NOT_INSTALLED); zlog_warn("%s: Route %s failed to install for table: %u", - __PRETTY_FUNCTION__, buf, table_id); + __func__, buf, table_id); break; case ZAPI_ROUTE_BETTER_ADMIN_WON: - zlog_warn("%s: Route %s over-ridden by better route for table: %u", - __PRETTY_FUNCTION__, buf, table_id); + static_nht_mark_state(&p, vrf_id, STATIC_NOT_INSTALLED); + zlog_warn( + "%s: Route %s over-ridden by better route for table: %u", + __func__, buf, table_id); break; case ZAPI_ROUTE_INSTALLED: + static_nht_mark_state(&p, vrf_id, STATIC_INSTALLED); break; case ZAPI_ROUTE_REMOVED: + static_nht_mark_state(&p, vrf_id, STATIC_NOT_INSTALLED); break; case ZAPI_ROUTE_REMOVE_FAIL: + static_nht_mark_state(&p, vrf_id, STATIC_INSTALLED); zlog_warn("%s: Route %s failure to remove for table: %u", - __PRETTY_FUNCTION__, buf, table_id); + __func__, buf, table_id); break; } @@ -194,21 +158,45 @@ struct static_nht_data { uint8_t nh_num; }; -static int static_zebra_nexthop_update(int command, struct zclient *zclient, - zebra_size_t length, vrf_id_t vrf_id) +/* API to check whether the configured nexthop address is + * one of its local connected address or not. + */ +static bool +static_nexthop_is_local(vrf_id_t vrfid, struct prefix *addr, int family) +{ + if (family == AF_INET) { + if (if_lookup_exact_address(&addr->u.prefix4, + AF_INET, + vrfid)) + return true; + } else if (family == AF_INET6) { + if (if_lookup_exact_address(&addr->u.prefix6, + AF_INET6, + vrfid)) + return true; + } + return false; +} +static int static_zebra_nexthop_update(ZAPI_CALLBACK_ARGS) { struct static_nht_data *nhtd, lookup; struct zapi_route nhr; afi_t afi = AFI_IP; if (!zapi_nexthop_update_decode(zclient->ibuf, &nhr)) { - zlog_warn("Failure to decode nexthop update message"); + zlog_err("Failure to decode nexthop update message"); return 1; } if (nhr.prefix.family == AF_INET6) afi = AFI_IP6; + if (nhr.type == ZEBRA_ROUTE_CONNECT) { + if (static_nexthop_is_local(vrf_id, &nhr.prefix, + nhr.prefix.family)) + nhr.nexthop_num = 0; + } + memset(&lookup, 0, sizeof(lookup)); lookup.nh = &nhr.prefix; lookup.nh_vrf_id = vrf_id; @@ -218,7 +206,8 @@ static int static_zebra_nexthop_update(int command, struct zclient *zclient, if (nhtd) { nhtd->nh_num = nhr.nexthop_num; - static_nht_update(&nhr.prefix, nhr.nexthop_num, afi, + static_nht_reset_start(&nhr.prefix, afi, nhtd->nh_vrf_id); + static_nht_update(NULL, &nhr.prefix, nhr.nexthop_num, afi, nhtd->nh_vrf_id); } else zlog_err("No nhtd?"); @@ -231,9 +220,9 @@ static void static_zebra_capabilities(struct zclient_capabilities *cap) mpls_enabled = cap->mpls_enabled; } -static unsigned int static_nht_hash_key(void *data) +static unsigned int static_nht_hash_key(const void *data) { - struct static_nht_data *nhtd = data; + const struct static_nht_data *nhtd = data; unsigned int key = 0; key = prefix_hash_key(nhtd->nh); @@ -271,11 +260,12 @@ static void static_nht_hash_free(void *data) { struct static_nht_data *nhtd = data; - prefix_free(nhtd->nh); + prefix_free(&nhtd->nh); XFREE(MTYPE_TMP, nhtd); } -void static_zebra_nht_register(struct static_route *si, bool reg) +void static_zebra_nht_register(struct route_node *rn, struct static_nexthop *nh, + bool reg) { struct static_nht_data *nhtd, lookup; uint32_t cmd; @@ -285,14 +275,14 @@ void static_zebra_nht_register(struct static_route *si, bool reg) cmd = (reg) ? ZEBRA_NEXTHOP_REGISTER : ZEBRA_NEXTHOP_UNREGISTER; - if (si->nh_registered && reg) + if (nh->nh_registered && reg) return; - if (!si->nh_registered && !reg) + if (!nh->nh_registered && !reg) return; memset(&p, 0, sizeof(p)); - switch (si->type) { + switch (nh->type) { case STATIC_IFNAME: case STATIC_BLACKHOLE: return; @@ -300,32 +290,35 @@ void static_zebra_nht_register(struct static_route *si, bool reg) case STATIC_IPV4_GATEWAY_IFNAME: p.family = AF_INET; p.prefixlen = IPV4_MAX_BITLEN; - p.u.prefix4 = si->addr.ipv4; + p.u.prefix4 = nh->addr.ipv4; afi = AFI_IP; break; case STATIC_IPV6_GATEWAY: case STATIC_IPV6_GATEWAY_IFNAME: p.family = AF_INET6; p.prefixlen = IPV6_MAX_BITLEN; - p.u.prefix6 = si->addr.ipv6; + p.u.prefix6 = nh->addr.ipv6; afi = AFI_IP6; break; } memset(&lookup, 0, sizeof(lookup)); lookup.nh = &p; - lookup.nh_vrf_id = si->nh_vrf_id; + lookup.nh_vrf_id = nh->nh_vrf_id; - si->nh_registered = reg; + nh->nh_registered = reg; if (reg) { nhtd = hash_get(static_nht_hash, &lookup, static_nht_hash_alloc); nhtd->refcount++; - if (nhtd->refcount > 1) { - static_nht_update(nhtd->nh, nhtd->nh_num, - afi, si->nh_vrf_id); + if (debug) + zlog_debug("Registered nexthop(%pFX) for %pRN %d", &p, + rn, nhtd->nh_num); + if (nhtd->refcount > 1 && nhtd->nh_num) { + static_nht_update(&rn->p, nhtd->nh, nhtd->nh_num, afi, + nh->nh_vrf_id); return; } } else { @@ -341,26 +334,72 @@ void static_zebra_nht_register(struct static_route *si, bool reg) static_nht_hash_free(nhtd); } - if (zclient_send_rnh(zclient, cmd, &p, false, si->nh_vrf_id) < 0) - zlog_warn("%s: Failure to send nexthop to zebra", - __PRETTY_FUNCTION__); + if (zclient_send_rnh(zclient, cmd, &p, false, nh->nh_vrf_id) < 0) + zlog_warn("%s: Failure to send nexthop to zebra", __func__); +} +/* + * When nexthop gets updated via configuration then use the + * already registered NH and resend the route to zebra + */ +int static_zebra_nh_update(struct route_node *rn, struct static_nexthop *nh) +{ + struct static_nht_data *nhtd, lookup = {}; + struct prefix p = {}; + afi_t afi = AFI_IP; + + if (!nh->nh_registered) + return 0; + + switch (nh->type) { + case STATIC_IFNAME: + case STATIC_BLACKHOLE: + return 0; + case STATIC_IPV4_GATEWAY: + case STATIC_IPV4_GATEWAY_IFNAME: + p.family = AF_INET; + p.prefixlen = IPV4_MAX_BITLEN; + p.u.prefix4 = nh->addr.ipv4; + afi = AFI_IP; + break; + case STATIC_IPV6_GATEWAY: + case STATIC_IPV6_GATEWAY_IFNAME: + p.family = AF_INET6; + p.prefixlen = IPV6_MAX_BITLEN; + p.u.prefix6 = nh->addr.ipv6; + afi = AFI_IP6; + break; + } + + lookup.nh = &p; + lookup.nh_vrf_id = nh->nh_vrf_id; + + nhtd = hash_lookup(static_nht_hash, &lookup); + if (nhtd && nhtd->nh_num) { + nh->state = STATIC_START; + static_nht_update(&rn->p, nhtd->nh, nhtd->nh_num, afi, + nh->nh_vrf_id); + return 1; + } + return 0; } extern void static_zebra_route_add(struct route_node *rn, - struct static_route *si_changed, - vrf_id_t vrf_id, safi_t safi, bool install) + struct static_path *pn, safi_t safi, + bool install) { - struct static_route *si = rn->info; + struct static_nexthop *nh; const struct prefix *p, *src_pp; struct zapi_nexthop *api_nh; struct zapi_route api; uint32_t nh_num = 0; + struct stable_info *info; p = src_pp = NULL; srcdest_rnode_prefixes(rn, &p, &src_pp); memset(&api, 0, sizeof(api)); - api.vrf_id = vrf_id; + info = static_get_stable_info(rn); + api.vrf_id = GET_STABLE_VRF_ID(info); api.type = ZEBRA_ROUTE_STATIC; api.safi = safi; memcpy(&api.prefix, p, sizeof(api.prefix)); @@ -370,69 +409,71 @@ extern void static_zebra_route_add(struct route_node *rn, memcpy(&api.src_prefix, src_pp, sizeof(api.src_prefix)); } SET_FLAG(api.flags, ZEBRA_FLAG_RR_USE_DISTANCE); + SET_FLAG(api.flags, ZEBRA_FLAG_ALLOW_RECURSION); SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP); - if (si_changed->distance) { + if (pn->distance) { SET_FLAG(api.message, ZAPI_MESSAGE_DISTANCE); - api.distance = si_changed->distance; + api.distance = pn->distance; } - if (si_changed->tag) { + if (pn->tag) { SET_FLAG(api.message, ZAPI_MESSAGE_TAG); - api.tag = si_changed->tag; + api.tag = pn->tag; } - if (si_changed->table_id != 0) { + if (pn->table_id != 0) { SET_FLAG(api.message, ZAPI_MESSAGE_TABLEID); - api.tableid = si_changed->table_id; + api.tableid = pn->table_id; } - for (/*loaded above*/; si; si = si->next) { + frr_each(static_nexthop_list, &pn->nexthop_list, nh) { api_nh = &api.nexthops[nh_num]; - if (si->nh_vrf_id == VRF_UNKNOWN) + if (nh->nh_vrf_id == VRF_UNKNOWN) continue; - if (si->distance != si_changed->distance) - continue; - - if (si->table_id != si_changed->table_id) - continue; + api_nh->vrf_id = nh->nh_vrf_id; + if (nh->onlink) + SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_ONLINK); + if (nh->color != 0) { + SET_FLAG(api.message, ZAPI_MESSAGE_SRTE); + api_nh->srte_color = nh->color; + } - api_nh->vrf_id = si->nh_vrf_id; - api_nh->onlink = si->onlink; + nh->state = STATIC_SENT_TO_ZEBRA; - switch (si->type) { + switch (nh->type) { case STATIC_IFNAME: - if (si->ifindex == IFINDEX_INTERNAL) + if (nh->ifindex == IFINDEX_INTERNAL) continue; - api_nh->ifindex = si->ifindex; + api_nh->ifindex = nh->ifindex; api_nh->type = NEXTHOP_TYPE_IFINDEX; break; case STATIC_IPV4_GATEWAY: - if (!si->nh_valid) + if (!nh->nh_valid) continue; api_nh->type = NEXTHOP_TYPE_IPV4; - api_nh->gate = si->addr; + api_nh->gate = nh->addr; break; case STATIC_IPV4_GATEWAY_IFNAME: - if (si->ifindex == IFINDEX_INTERNAL) + if (nh->ifindex == IFINDEX_INTERNAL) continue; - api_nh->ifindex = si->ifindex; + api_nh->ifindex = nh->ifindex; api_nh->type = NEXTHOP_TYPE_IPV4_IFINDEX; - api_nh->gate = si->addr; + api_nh->gate = nh->addr; break; case STATIC_IPV6_GATEWAY: - if (!si->nh_valid) + if (!nh->nh_valid) continue; api_nh->type = NEXTHOP_TYPE_IPV6; - api_nh->gate = si->addr; + api_nh->gate = nh->addr; break; case STATIC_IPV6_GATEWAY_IFNAME: - if (si->ifindex == IFINDEX_INTERNAL) + if (nh->ifindex == IFINDEX_INTERNAL) continue; api_nh->type = NEXTHOP_TYPE_IPV6_IFINDEX; - api_nh->ifindex = si->ifindex; - api_nh->gate = si->addr; + api_nh->ifindex = nh->ifindex; + api_nh->gate = nh->addr; break; case STATIC_BLACKHOLE: api_nh->type = NEXTHOP_TYPE_BLACKHOLE; - switch (si->bh_type) { + switch (nh->bh_type) { case STATIC_BLACKHOLE_DROP: case STATIC_BLACKHOLE_NULL: api_nh->bh_type = BLACKHOLE_NULL; @@ -443,13 +484,13 @@ extern void static_zebra_route_add(struct route_node *rn, break; } - if (si->snh_label.num_labels) { + if (nh->snh_label.num_labels) { int i; - SET_FLAG(api.message, ZAPI_MESSAGE_LABEL); - api_nh->label_num = si->snh_label.num_labels; + SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_LABEL); + api_nh->label_num = nh->snh_label.num_labels; for (i = 0; i < api_nh->label_num; i++) - api_nh->labels[i] = si->snh_label.label[i]; + api_nh->labels[i] = nh->snh_label.label[i]; } nh_num++; } @@ -467,19 +508,19 @@ extern void static_zebra_route_add(struct route_node *rn, ZEBRA_ROUTE_ADD : ZEBRA_ROUTE_DELETE, zclient, &api); } + void static_zebra_init(void) { struct zclient_options opt = { .receive_notify = true }; + if_zapi_callbacks(static_ifp_create, static_ifp_up, + static_ifp_down, static_ifp_destroy); + zclient = zclient_new(master, &opt); zclient_init(zclient, ZEBRA_ROUTE_STATIC, 0, &static_privs); zclient->zebra_capabilities = static_zebra_capabilities; zclient->zebra_connected = zebra_connected; - zclient->interface_add = interface_add; - zclient->interface_delete = interface_delete; - zclient->interface_up = interface_state_up; - zclient->interface_down = interface_state_down; zclient->interface_address_add = interface_address_add; zclient->interface_address_delete = interface_address_delete; zclient->route_notify_owner = route_notify_owner; @@ -489,3 +530,17 @@ void static_zebra_init(void) static_nht_hash_cmp, "Static Nexthop Tracking hash"); } + +void static_zebra_vrf_register(struct vrf *vrf) +{ + if (vrf->vrf_id == VRF_DEFAULT) + return; + zclient_send_reg_requests(zclient, vrf->vrf_id); +} + +void static_zebra_vrf_unregister(struct vrf *vrf) +{ + if (vrf->vrf_id == VRF_DEFAULT) + return; + zclient_send_dereg_requests(zclient, vrf->vrf_id); +} diff --git a/staticd/static_zebra.h b/staticd/static_zebra.h index a82eb162e1..9f93f3ee63 100644 --- a/staticd/static_zebra.h +++ b/staticd/static_zebra.h @@ -21,10 +21,16 @@ extern struct thread_master *master; -extern void static_zebra_nht_register(struct static_route *si, bool reg); +extern void static_zebra_nht_register(struct route_node *rn, + struct static_nexthop *nh, bool reg); extern void static_zebra_route_add(struct route_node *rn, - struct static_route *si_changed, - vrf_id_t vrf_id, safi_t safi, bool install); + struct static_path *pn, safi_t safi, + bool install); extern void static_zebra_init(void); +extern void static_zebra_vrf_register(struct vrf *vrf); +extern void static_zebra_vrf_unregister(struct vrf *vrf); +extern int static_zebra_nh_update(struct route_node *rn, + struct static_nexthop *nh); + #endif diff --git a/staticd/staticd.conf.sample b/staticd/staticd.conf.sample index bb1c2edca8..3b64eb9c90 100644 --- a/staticd/staticd.conf.sample +++ b/staticd/staticd.conf.sample @@ -1,3 +1,5 @@ -! +! Default staticd configuration sample ! log stdout +! +! ip route 4.5.6.7/32 10.10.10.10 diff --git a/staticd/subdir.am b/staticd/subdir.am index 17c4536fe9..eba7f270bb 100644 --- a/staticd/subdir.am +++ b/staticd/subdir.am @@ -6,30 +6,40 @@ if STATICD noinst_LIBRARIES += staticd/libstatic.a sbin_PROGRAMS += staticd/staticd dist_examples_DATA += staticd/staticd.conf.sample -vtysh_scan += $(top_srcdir)/staticd/static_vty.c -man8 += $(MANBUILD)/staticd.8 +vtysh_scan += staticd/static_vty.c +man8 += $(MANBUILD)/frr-staticd.8 endif staticd_libstatic_a_SOURCES = \ + staticd/static_debug.c \ staticd/static_memory.c \ staticd/static_nht.c \ staticd/static_routes.c \ staticd/static_zebra.c \ staticd/static_vrf.c \ staticd/static_vty.c \ + staticd/static_nb.c \ + staticd/static_nb_config.c \ # end noinst_HEADERS += \ + staticd/static_debug.h \ staticd/static_memory.h \ staticd/static_nht.h \ staticd/static_zebra.h \ staticd/static_routes.h \ staticd/static_vty.h \ staticd/static_vrf.h \ + staticd/static_nb.h \ # end -staticd/static_vty_clippy.c: $(CLIPPY_DEPS) -staticd/static_vty.$(OBJEXT): staticd/static_vty_clippy.c +clippy_scan += \ + staticd/static_vty.c \ + # end staticd_staticd_SOURCES = staticd/static_main.c staticd_staticd_LDADD = staticd/libstatic.a lib/libfrr.la $(LIBCAP) + +nodist_staticd_staticd_SOURCES = \ + yang/frr-staticd.yang.c \ + # end diff --git a/tests/.gitignore b/tests/.gitignore index 380172487d..5e809a81e6 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -13,6 +13,7 @@ /isisd/test_fuzz_isis_tlv /isisd/test_fuzz_isis_tlv_tests.h /isisd/test_isis_lspdb +/isisd/test_isis_spf /isisd/test_isis_vertex_queue /lib/cli/test_cli /lib/cli/test_cli_clippy.c @@ -30,6 +31,9 @@ /lib/test_idalloc /lib/test_memory /lib/test_nexthop_iter +/lib/test_ntop +/lib/test_prefix2str +/lib/test_printfrr /lib/test_privs /lib/test_ringbuf /lib/test_segv @@ -42,6 +46,7 @@ /lib/test_timer_performance /lib/test_ttable /lib/test_typelist +/lib/test_versioncmp /lib/test_zlog /lib/test_zmq /ospf6d/test_lsdb diff --git a/tests/Makefile.in b/tests/Makefile.in deleted file mode 100644 index 29d903e70a..0000000000 --- a/tests/Makefile.in +++ /dev/null @@ -1,1338 +0,0 @@ -# Makefile.in generated by automake 1.15.1 from Makefile.am. -# @configure_input@ - -# Copyright (C) 1994-2017 Free Software Foundation, Inc. - -# This Makefile.in is free software; the Free Software Foundation -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY, to the extent permitted by law; without -# even the implied warranty of MERCHANTABILITY or FITNESS FOR A -# PARTICULAR PURPOSE. - -@SET_MAKE@ - -# -# Automake fragment intended to be shared by Makefile.am files in the -# tree. -# - -VPATH = @srcdir@ -am__is_gnu_make = { \ - if test -z '$(MAKELEVEL)'; then \ - false; \ - elif test -n '$(MAKE_HOST)'; then \ - true; \ - elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ - true; \ - else \ - false; \ - fi; \ -} -am__make_running_with_option = \ - case $${target_option-} in \ - ?) ;; \ - *) echo "am__make_running_with_option: internal error: invalid" \ - "target option '$${target_option-}' specified" >&2; \ - exit 1;; \ - esac; \ - has_opt=no; \ - sane_makeflags=$$MAKEFLAGS; \ - if $(am__is_gnu_make); then \ - sane_makeflags=$$MFLAGS; \ - else \ - case $$MAKEFLAGS in \ - *\\[\ \ ]*) \ - bs=\\; \ - sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ - | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ - esac; \ - fi; \ - skip_next=no; \ - strip_trailopt () \ - { \ - flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ - }; \ - for flg in $$sane_makeflags; do \ - test $$skip_next = yes && { skip_next=no; continue; }; \ - case $$flg in \ - *=*|--*) continue;; \ - -*I) strip_trailopt 'I'; skip_next=yes;; \ - -*I?*) strip_trailopt 'I';; \ - -*O) strip_trailopt 'O'; skip_next=yes;; \ - -*O?*) strip_trailopt 'O';; \ - -*l) strip_trailopt 'l'; skip_next=yes;; \ - -*l?*) strip_trailopt 'l';; \ - -[dEDm]) skip_next=yes;; \ - -[JT]) skip_next=yes;; \ - esac; \ - case $$flg in \ - *$$target_option*) has_opt=yes; break;; \ - esac; \ - done; \ - test $$has_opt = yes -am__make_dryrun = (target_option=n; $(am__make_running_with_option)) -am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) -pkgdatadir = $(datadir)/@PACKAGE@ -pkgincludedir = $(includedir)/@PACKAGE@ -pkglibdir = $(libdir)/@PACKAGE@ -pkglibexecdir = $(libexecdir)/@PACKAGE@ -am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd -install_sh_DATA = $(install_sh) -c -m 644 -install_sh_PROGRAM = $(install_sh) -c -install_sh_SCRIPT = $(install_sh) -c -INSTALL_HEADER = $(INSTALL_DATA) -transform = $(program_transform_name) -NORMAL_INSTALL = : -PRE_INSTALL = : -POST_INSTALL = : -NORMAL_UNINSTALL = : -PRE_UNINSTALL = : -POST_UNINSTALL = : -build_triplet = @build@ -host_triplet = @host@ -check_PROGRAMS = lib/test_buffer$(EXEEXT) lib/test_checksum$(EXEEXT) \ - lib/test_heavy_thread$(EXEEXT) lib/test_heavy_wq$(EXEEXT) \ - lib/test_heavy$(EXEEXT) lib/test_memory$(EXEEXT) \ - lib/test_nexthop_iter$(EXEEXT) lib/test_privs$(EXEEXT) \ - lib/test_ringbuf$(EXEEXT) lib/test_srcdest_table$(EXEEXT) \ - lib/test_segv$(EXEEXT) lib/test_sig$(EXEEXT) \ - lib/test_stream$(EXEEXT) lib/test_table$(EXEEXT) \ - lib/test_timer_correctness$(EXEEXT) \ - lib/test_timer_performance$(EXEEXT) lib/test_ttable$(EXEEXT) \ - lib/cli/test_cli$(EXEEXT) lib/cli/test_commands$(EXEEXT) \ - $(am__EXEEXT_1) $(am__EXEEXT_2) $(am__EXEEXT_3) \ - $(am__EXEEXT_4) -@ZEROMQ_TRUE@am__append_1 = \ -@ZEROMQ_TRUE@ lib/test_zmq \ -@ZEROMQ_TRUE@ # end - -subdir = tests -ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 -am__aclocal_m4_deps = $(top_srcdir)/m4/ax_compare_version.m4 \ - $(top_srcdir)/m4/ax_pthread.m4 $(top_srcdir)/m4/libtool.m4 \ - $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ - $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ - $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/configure.ac -am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ - $(ACLOCAL_M4) -DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ - $(am__DIST_COMMON) -mkinstalldirs = $(install_sh) -d -CONFIG_HEADER = $(top_builddir)/config.h -CONFIG_CLEAN_FILES = -CONFIG_CLEAN_VPATH_FILES = -@BGPD_TRUE@am__EXEEXT_1 = bgpd/test_aspath$(EXEEXT) \ -@BGPD_TRUE@ bgpd/test_capability$(EXEEXT) \ -@BGPD_TRUE@ bgpd/test_packet$(EXEEXT) \ -@BGPD_TRUE@ bgpd/test_ecommunity$(EXEEXT) \ -@BGPD_TRUE@ bgpd/test_mp_attr$(EXEEXT) bgpd/test_mpath$(EXEEXT) -@ISISD_TRUE@@SOLARIS_FALSE@am__EXEEXT_2 = \ -@ISISD_TRUE@@SOLARIS_FALSE@ isisd/test_fuzz_isis_tlv$(EXEEXT) \ -@ISISD_TRUE@@SOLARIS_FALSE@ isisd/test_isis_vertex_queue$(EXEEXT) -@OSPF6D_TRUE@am__EXEEXT_3 = ospf6d/test_lsdb$(EXEEXT) -@ZEROMQ_TRUE@am__EXEEXT_4 = lib/test_zmq$(EXEEXT) -am__dirstamp = $(am__leading_dot)dirstamp -am_bgpd_test_aspath_OBJECTS = bgpd/test_aspath.$(OBJEXT) -bgpd_test_aspath_OBJECTS = $(am_bgpd_test_aspath_OBJECTS) -@ENABLE_BGP_VNC_TRUE@am__DEPENDENCIES_1 = \ -@ENABLE_BGP_VNC_TRUE@ @top_builddir@/$(LIBRFP)/librfp.a -am__DEPENDENCIES_2 = ../lib/libfrr.la -am__DEPENDENCIES_3 = ../bgpd/libbgp.a $(am__DEPENDENCIES_1) \ - $(am__DEPENDENCIES_2) -bgpd_test_aspath_DEPENDENCIES = $(am__DEPENDENCIES_3) -AM_V_lt = $(am__v_lt_@AM_V@) -am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) -am__v_lt_0 = --silent -am__v_lt_1 = -am_bgpd_test_capability_OBJECTS = bgpd/test_capability.$(OBJEXT) -bgpd_test_capability_OBJECTS = $(am_bgpd_test_capability_OBJECTS) -bgpd_test_capability_DEPENDENCIES = $(am__DEPENDENCIES_3) -am_bgpd_test_ecommunity_OBJECTS = bgpd/test_ecommunity.$(OBJEXT) -bgpd_test_ecommunity_OBJECTS = $(am_bgpd_test_ecommunity_OBJECTS) -bgpd_test_ecommunity_DEPENDENCIES = $(am__DEPENDENCIES_3) -am_bgpd_test_mp_attr_OBJECTS = bgpd/test_mp_attr.$(OBJEXT) -bgpd_test_mp_attr_OBJECTS = $(am_bgpd_test_mp_attr_OBJECTS) -bgpd_test_mp_attr_DEPENDENCIES = $(am__DEPENDENCIES_3) -am_bgpd_test_mpath_OBJECTS = bgpd/test_mpath.$(OBJEXT) -bgpd_test_mpath_OBJECTS = $(am_bgpd_test_mpath_OBJECTS) -bgpd_test_mpath_DEPENDENCIES = $(am__DEPENDENCIES_3) -am_bgpd_test_packet_OBJECTS = bgpd/test_packet.$(OBJEXT) -bgpd_test_packet_OBJECTS = $(am_bgpd_test_packet_OBJECTS) -bgpd_test_packet_DEPENDENCIES = $(am__DEPENDENCIES_3) -am_isisd_test_fuzz_isis_tlv_OBJECTS = \ - isisd/isisd_test_fuzz_isis_tlv-test_fuzz_isis_tlv.$(OBJEXT) -isisd_test_fuzz_isis_tlv_OBJECTS = \ - $(am_isisd_test_fuzz_isis_tlv_OBJECTS) -am__DEPENDENCIES_4 = ../isisd/libisis.a $(am__DEPENDENCIES_2) -isisd_test_fuzz_isis_tlv_DEPENDENCIES = $(am__DEPENDENCIES_4) -am_isisd_test_isis_vertex_queue_OBJECTS = \ - isisd/test_isis_vertex_queue.$(OBJEXT) -isisd_test_isis_vertex_queue_OBJECTS = \ - $(am_isisd_test_isis_vertex_queue_OBJECTS) -isisd_test_isis_vertex_queue_DEPENDENCIES = $(am__DEPENDENCIES_4) -am_lib_cli_test_cli_OBJECTS = lib/cli/test_cli.$(OBJEXT) \ - lib/cli/common_cli.$(OBJEXT) -lib_cli_test_cli_OBJECTS = $(am_lib_cli_test_cli_OBJECTS) -lib_cli_test_cli_DEPENDENCIES = $(am__DEPENDENCIES_2) -am_lib_cli_test_commands_OBJECTS = \ - lib/cli/test_commands_defun.$(OBJEXT) \ - lib/cli/test_commands.$(OBJEXT) helpers/c/prng.$(OBJEXT) -lib_cli_test_commands_OBJECTS = $(am_lib_cli_test_commands_OBJECTS) -lib_cli_test_commands_DEPENDENCIES = $(am__DEPENDENCIES_2) -am_lib_test_buffer_OBJECTS = lib/test_buffer.$(OBJEXT) -lib_test_buffer_OBJECTS = $(am_lib_test_buffer_OBJECTS) -lib_test_buffer_DEPENDENCIES = $(am__DEPENDENCIES_2) -am_lib_test_checksum_OBJECTS = lib/test_checksum.$(OBJEXT) -lib_test_checksum_OBJECTS = $(am_lib_test_checksum_OBJECTS) -lib_test_checksum_DEPENDENCIES = $(am__DEPENDENCIES_2) -am_lib_test_heavy_OBJECTS = lib/test_heavy.$(OBJEXT) \ - helpers/c/main.$(OBJEXT) -lib_test_heavy_OBJECTS = $(am_lib_test_heavy_OBJECTS) -lib_test_heavy_DEPENDENCIES = $(am__DEPENDENCIES_2) -am_lib_test_heavy_thread_OBJECTS = lib/test_heavy_thread.$(OBJEXT) \ - helpers/c/main.$(OBJEXT) -lib_test_heavy_thread_OBJECTS = $(am_lib_test_heavy_thread_OBJECTS) -lib_test_heavy_thread_DEPENDENCIES = $(am__DEPENDENCIES_2) -am_lib_test_heavy_wq_OBJECTS = lib/test_heavy_wq.$(OBJEXT) \ - helpers/c/main.$(OBJEXT) -lib_test_heavy_wq_OBJECTS = $(am_lib_test_heavy_wq_OBJECTS) -lib_test_heavy_wq_DEPENDENCIES = $(am__DEPENDENCIES_2) -am_lib_test_memory_OBJECTS = lib/test_memory.$(OBJEXT) -lib_test_memory_OBJECTS = $(am_lib_test_memory_OBJECTS) -lib_test_memory_DEPENDENCIES = $(am__DEPENDENCIES_2) -am_lib_test_nexthop_iter_OBJECTS = lib/test_nexthop_iter.$(OBJEXT) \ - helpers/c/prng.$(OBJEXT) -lib_test_nexthop_iter_OBJECTS = $(am_lib_test_nexthop_iter_OBJECTS) -lib_test_nexthop_iter_DEPENDENCIES = $(am__DEPENDENCIES_2) -am_lib_test_privs_OBJECTS = lib/test_privs.$(OBJEXT) -lib_test_privs_OBJECTS = $(am_lib_test_privs_OBJECTS) -lib_test_privs_DEPENDENCIES = $(am__DEPENDENCIES_2) -am_lib_test_ringbuf_OBJECTS = lib/test_ringbuf.$(OBJEXT) -lib_test_ringbuf_OBJECTS = $(am_lib_test_ringbuf_OBJECTS) -lib_test_ringbuf_DEPENDENCIES = $(am__DEPENDENCIES_2) -am_lib_test_segv_OBJECTS = lib/test_segv.$(OBJEXT) -lib_test_segv_OBJECTS = $(am_lib_test_segv_OBJECTS) -lib_test_segv_DEPENDENCIES = $(am__DEPENDENCIES_2) -am_lib_test_sig_OBJECTS = lib/test_sig.$(OBJEXT) -lib_test_sig_OBJECTS = $(am_lib_test_sig_OBJECTS) -lib_test_sig_DEPENDENCIES = $(am__DEPENDENCIES_2) -am_lib_test_srcdest_table_OBJECTS = lib/test_srcdest_table.$(OBJEXT) \ - helpers/c/prng.$(OBJEXT) -lib_test_srcdest_table_OBJECTS = $(am_lib_test_srcdest_table_OBJECTS) -lib_test_srcdest_table_DEPENDENCIES = $(am__DEPENDENCIES_2) -am_lib_test_stream_OBJECTS = lib/test_stream.$(OBJEXT) -lib_test_stream_OBJECTS = $(am_lib_test_stream_OBJECTS) -lib_test_stream_DEPENDENCIES = $(am__DEPENDENCIES_2) -am_lib_test_table_OBJECTS = lib/test_table.$(OBJEXT) -lib_test_table_OBJECTS = $(am_lib_test_table_OBJECTS) -lib_test_table_DEPENDENCIES = $(am__DEPENDENCIES_2) -am_lib_test_timer_correctness_OBJECTS = \ - lib/test_timer_correctness.$(OBJEXT) helpers/c/prng.$(OBJEXT) -lib_test_timer_correctness_OBJECTS = \ - $(am_lib_test_timer_correctness_OBJECTS) -lib_test_timer_correctness_DEPENDENCIES = $(am__DEPENDENCIES_2) -am_lib_test_timer_performance_OBJECTS = \ - lib/test_timer_performance.$(OBJEXT) helpers/c/prng.$(OBJEXT) -lib_test_timer_performance_OBJECTS = \ - $(am_lib_test_timer_performance_OBJECTS) -lib_test_timer_performance_DEPENDENCIES = $(am__DEPENDENCIES_2) -am_lib_test_ttable_OBJECTS = lib/test_ttable.$(OBJEXT) -lib_test_ttable_OBJECTS = $(am_lib_test_ttable_OBJECTS) -lib_test_ttable_DEPENDENCIES = $(am__DEPENDENCIES_2) -am_lib_test_zmq_OBJECTS = lib/lib_test_zmq-test_zmq.$(OBJEXT) -lib_test_zmq_OBJECTS = $(am_lib_test_zmq_OBJECTS) -am__DEPENDENCIES_5 = -lib_test_zmq_DEPENDENCIES = ../lib/libfrrzmq.la $(am__DEPENDENCIES_2) \ - $(am__DEPENDENCIES_5) -lib_test_zmq_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ - $(LIBTOOLFLAGS) --mode=link $(CCLD) $(lib_test_zmq_CFLAGS) \ - $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ -am_ospf6d_test_lsdb_OBJECTS = ospf6d/test_lsdb.$(OBJEXT) \ - lib/cli/common_cli.$(OBJEXT) -ospf6d_test_lsdb_OBJECTS = $(am_ospf6d_test_lsdb_OBJECTS) -am__DEPENDENCIES_6 = ../ospf6d/libospf6.a $(am__DEPENDENCIES_2) -ospf6d_test_lsdb_DEPENDENCIES = $(am__DEPENDENCIES_6) -AM_V_P = $(am__v_P_@AM_V@) -am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) -am__v_P_0 = false -am__v_P_1 = : -AM_V_GEN = $(am__v_GEN_@AM_V@) -am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) -am__v_GEN_0 = @echo " GEN " $@; -am__v_GEN_1 = -AM_V_at = $(am__v_at_@AM_V@) -am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) -am__v_at_0 = @ -am__v_at_1 = -DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) -depcomp = $(SHELL) $(top_srcdir)/depcomp -am__depfiles_maybe = depfiles -am__mv = mv -f -COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ - $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ - $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ - $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ - $(AM_CFLAGS) $(CFLAGS) -AM_V_CC = $(am__v_CC_@AM_V@) -am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) -am__v_CC_0 = @echo " CC " $@; -am__v_CC_1 = -CCLD = $(CC) -LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ - $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ - $(AM_LDFLAGS) $(LDFLAGS) -o $@ -AM_V_CCLD = $(am__v_CCLD_@AM_V@) -am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) -am__v_CCLD_0 = @echo " CCLD " $@; -am__v_CCLD_1 = -SOURCES = $(bgpd_test_aspath_SOURCES) $(bgpd_test_capability_SOURCES) \ - $(bgpd_test_ecommunity_SOURCES) $(bgpd_test_mp_attr_SOURCES) \ - $(bgpd_test_mpath_SOURCES) $(bgpd_test_packet_SOURCES) \ - $(isisd_test_fuzz_isis_tlv_SOURCES) \ - $(isisd_test_isis_vertex_queue_SOURCES) \ - $(lib_cli_test_cli_SOURCES) $(lib_cli_test_commands_SOURCES) \ - $(lib_test_buffer_SOURCES) $(lib_test_checksum_SOURCES) \ - $(lib_test_heavy_SOURCES) $(lib_test_heavy_thread_SOURCES) \ - $(lib_test_heavy_wq_SOURCES) $(lib_test_memory_SOURCES) \ - $(lib_test_nexthop_iter_SOURCES) $(lib_test_privs_SOURCES) \ - $(lib_test_ringbuf_SOURCES) $(lib_test_segv_SOURCES) \ - $(lib_test_sig_SOURCES) $(lib_test_srcdest_table_SOURCES) \ - $(lib_test_stream_SOURCES) $(lib_test_table_SOURCES) \ - $(lib_test_timer_correctness_SOURCES) \ - $(lib_test_timer_performance_SOURCES) \ - $(lib_test_ttable_SOURCES) $(lib_test_zmq_SOURCES) \ - $(ospf6d_test_lsdb_SOURCES) -DIST_SOURCES = $(bgpd_test_aspath_SOURCES) \ - $(bgpd_test_capability_SOURCES) \ - $(bgpd_test_ecommunity_SOURCES) $(bgpd_test_mp_attr_SOURCES) \ - $(bgpd_test_mpath_SOURCES) $(bgpd_test_packet_SOURCES) \ - $(isisd_test_fuzz_isis_tlv_SOURCES) \ - $(isisd_test_isis_vertex_queue_SOURCES) \ - $(lib_cli_test_cli_SOURCES) $(lib_cli_test_commands_SOURCES) \ - $(lib_test_buffer_SOURCES) $(lib_test_checksum_SOURCES) \ - $(lib_test_heavy_SOURCES) $(lib_test_heavy_thread_SOURCES) \ - $(lib_test_heavy_wq_SOURCES) $(lib_test_memory_SOURCES) \ - $(lib_test_nexthop_iter_SOURCES) $(lib_test_privs_SOURCES) \ - $(lib_test_ringbuf_SOURCES) $(lib_test_segv_SOURCES) \ - $(lib_test_sig_SOURCES) $(lib_test_srcdest_table_SOURCES) \ - $(lib_test_stream_SOURCES) $(lib_test_table_SOURCES) \ - $(lib_test_timer_correctness_SOURCES) \ - $(lib_test_timer_performance_SOURCES) \ - $(lib_test_ttable_SOURCES) $(lib_test_zmq_SOURCES) \ - $(ospf6d_test_lsdb_SOURCES) -am__can_run_installinfo = \ - case $$AM_UPDATE_INFO_DIR in \ - n|no|NO) false;; \ - *) (install-info --version) >/dev/null 2>&1;; \ - esac -HEADERS = $(noinst_HEADERS) -am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) -# Read a list of newline-separated strings from the standard input, -# and print each of them once, without duplicates. Input order is -# *not* preserved. -am__uniquify_input = $(AWK) '\ - BEGIN { nonempty = 0; } \ - { items[$$0] = 1; nonempty = 1; } \ - END { if (nonempty) { for (i in items) print i; }; } \ -' -# Make sure the list of sources is unique. This is necessary because, -# e.g., the same source file might be shared among _SOURCES variables -# for different programs/libraries. -am__define_uniq_tagged_files = \ - list='$(am__tagged_files)'; \ - unique=`for i in $$list; do \ - if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ - done | $(am__uniquify_input)` -ETAGS = etags -CTAGS = ctags -am__DIST_COMMON = $(srcdir)/../common.am $(srcdir)/Makefile.in \ - $(top_srcdir)/depcomp -DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) -ACLOCAL = @ACLOCAL@ -AMTAR = @AMTAR@ -AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ -AR = @AR@ -AUTOCONF = @AUTOCONF@ -AUTOHEADER = @AUTOHEADER@ -AUTOMAKE = @AUTOMAKE@ -AWK = @AWK@ -BGPD = @BGPD@ -BISON_CLOSEBRACE = @BISON_CLOSEBRACE@ -BISON_OPENBRACE = @BISON_OPENBRACE@ -BISON_VERBOSE = @BISON_VERBOSE@ -CARES_CFLAGS = @CARES_CFLAGS@ -CARES_LIBS = @CARES_LIBS@ -CC = @CC@ -CCDEPMODE = @CCDEPMODE@ -CFG_MODULE = @CFG_MODULE@ -CFG_SBIN = @CFG_SBIN@ -CFG_STATE = @CFG_STATE@ -CFG_SYSCONF = @CFG_SYSCONF@ -CFLAGS = @CFLAGS@ -CONFDATE = @CONFDATE@ -CONFIG_ARGS = @CONFIG_ARGS@ -CPP = @CPP@ -CPPFLAGS = @CPPFLAGS@ -CURSES = @CURSES@ -CYGPATH_W = @CYGPATH_W@ -DEFS = @DEFS@ $(LOCAL_OPTS) -DSYSCONFDIR=\"$(sysconfdir)/\" -DEPDIR = @DEPDIR@ -DFLT_NAME = @DFLT_NAME@ -DLLTOOL = @DLLTOOL@ -DOC = @DOC@ -DSYMUTIL = @DSYMUTIL@ -DUMPBIN = @DUMPBIN@ -ECHO_C = @ECHO_C@ -ECHO_N = @ECHO_N@ -ECHO_T = @ECHO_T@ -EGREP = @EGREP@ -EXEEXT = @EXEEXT@ -FGREP = @FGREP@ -GREP = @GREP@ -HAVE_LIBPCREPOSIX = @HAVE_LIBPCREPOSIX@ -HOSTTOOLS = @HOSTTOOLS@ -INSTALL = @INSTALL@ -INSTALL_DATA = @INSTALL_DATA@ -INSTALL_PROGRAM = @INSTALL_PROGRAM@ -INSTALL_SCRIPT = @INSTALL_SCRIPT@ -INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ -LD = @LD@ -LDFLAGS = @LDFLAGS@ -LEX = @LEX@ -LEXLIB = @LEXLIB@ -LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@ -LIBCAP = @LIBCAP@ -LIBM = @LIBM@ -LIBOBJS = @LIBOBJS@ -LIBPAM = @LIBPAM@ -LIBREADLINE = @LIBREADLINE@ -LIBRFP = @LIBRFP@ -LIBS = @LIBS@ -LIBTOOL = @LIBTOOL@ -LIPO = @LIPO@ -LN_S = @LN_S@ -LTLIBOBJS = @LTLIBOBJS@ -LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ -MAKEINFO = @MAKEINFO@ -MANIFEST_TOOL = @MANIFEST_TOOL@ -MKDIR_P = @MKDIR_P@ -NETSNMP_CONFIG = @NETSNMP_CONFIG@ -NM = @NM@ -NMEDIT = @NMEDIT@ -OBJDUMP = @OBJDUMP@ -OBJEXT = @OBJEXT@ -OTOOL = @OTOOL@ -OTOOL64 = @OTOOL64@ -PACKAGE = @PACKAGE@ -PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ -PACKAGE_EXTRAVERSION = @PACKAGE_EXTRAVERSION@ -PACKAGE_FULLNAME = @PACKAGE_FULLNAME@ -PACKAGE_NAME = @PACKAGE_NAME@ -PACKAGE_STRING = @PACKAGE_STRING@ -PACKAGE_TARNAME = @PACKAGE_TARNAME@ -PACKAGE_URL = @PACKAGE_URL@ -PACKAGE_VERSION = @PACKAGE_VERSION@ -PATH_SEPARATOR = @PATH_SEPARATOR@ -PERL = @PERL@ -PKG_CONFIG = @PKG_CONFIG@ -PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ -PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ -PROTOBUF_C_CFLAGS = @PROTOBUF_C_CFLAGS@ -PROTOBUF_C_LIBS = @PROTOBUF_C_LIBS@ -PROTOC_C = @PROTOC_C@ -PTHREAD_CC = @PTHREAD_CC@ -PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ -PTHREAD_LIBS = @PTHREAD_LIBS@ -PYTHONCONFIG = @PYTHONCONFIG@ -PYTHON_CFLAGS = @PYTHON_CFLAGS@ -PYTHON_LIBS = @PYTHON_LIBS@ -RANLIB = @RANLIB@ -RFPINC = @RFPINC@ -RFPTEST = @RFPTEST@ -RTRLIB_CFLAGS = @RTRLIB_CFLAGS@ -RTRLIB_LIBS = @RTRLIB_LIBS@ -SED = @SED@ -SET_MAKE = @SET_MAKE@ -SHELL = @SHELL@ -SNMP_CFLAGS = @SNMP_CFLAGS@ -SNMP_LIBS = @SNMP_LIBS@ -SOLARIS = @SOLARIS@ -STRIP = @STRIP@ -VERSION = @VERSION@ -VNC_RFP_PATH = @VNC_RFP_PATH@ -VTYSH = @VTYSH@ -WERROR = @WERROR@ -YACC = @YACC@ -YFLAGS = @YFLAGS@ -ZEROMQ_CFLAGS = @ZEROMQ_CFLAGS@ -ZEROMQ_LIBS = @ZEROMQ_LIBS@ -abs_builddir = @abs_builddir@ -abs_srcdir = @abs_srcdir@ -abs_top_builddir = @abs_top_builddir@ -abs_top_srcdir = @abs_top_srcdir@ -ac_ct_AR = @ac_ct_AR@ -ac_ct_CC = @ac_ct_CC@ -ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ -ac_ct_PYTHONCONFIG = @ac_ct_PYTHONCONFIG@ -am__include = @am__include@ -am__leading_dot = @am__leading_dot@ -am__quote = @am__quote@ -am__tar = @am__tar@ -am__untar = @am__untar@ -ax_pthread_config = @ax_pthread_config@ -bindir = @bindir@ -build = @build@ -build_alias = @build_alias@ -build_cpu = @build_cpu@ -build_os = @build_os@ -build_vendor = @build_vendor@ -builddir = @builddir@ -datadir = @datadir@ -datarootdir = @datarootdir@ -docdir = @docdir@ -dvidir = @dvidir@ -enable_group = @enable_group@ -enable_user = @enable_user@ -enable_vty_group = @enable_vty_group@ -exampledir = @exampledir@ -exec_prefix = @exec_prefix@ -frr_statedir = @frr_statedir@ -host = @host@ -host_alias = @host_alias@ -host_cpu = @host_cpu@ -host_os = @host_os@ -host_vendor = @host_vendor@ -htmldir = @htmldir@ -includedir = @includedir@ -infodir = @infodir@ -install_sh = @install_sh@ -libdir = @libdir@ -libexecdir = @libexecdir@ -localedir = @localedir@ -localstatedir = @localstatedir@ -mandir = @mandir@ -mkdir_p = @mkdir_p@ -moduledir = @moduledir@ -oldincludedir = @oldincludedir@ -pdfdir = @pdfdir@ -pkgsrcrcdir = @pkgsrcrcdir@ -prefix = @prefix@ -program_transform_name = @program_transform_name@ -psdir = @psdir@ -runstatedir = @runstatedir@ -sbindir = @sbindir@ -sharedstatedir = @sharedstatedir@ -srcdir = @srcdir@ -sysconfdir = @sysconfdir@ -target_alias = @target_alias@ -top_build_prefix = @top_build_prefix@ -top_builddir = @top_builddir@ -top_srcdir = @top_srcdir@ -AM_V_CLIPPY = $(am__v_CLIPPY_$(V)) -am__v_CLIPPY_ = $(am__v_CLIPPY_$(AM_DEFAULT_VERBOSITY)) -am__v_CLIPPY_0 = @echo " CLIPPY " $@; -am__v_CLIPPY_1 = -CLIPPY_DEPS = $(HOSTTOOLS)lib/clippy $(top_srcdir)/python/clidef.py -SUFFIXES = _clippy.c .proto .pb-c.c .pb-c.h .pb.h - -# Uncomment to use an non-system version of libprotobuf-c. -# -# Q_PROTOBUF_C_CLIENT_INCLUDES = -I$(top_srcdir)/third-party/protobuf-c/src -# Q_PROTOBUF_C_CLIENT_LDOPTS = $(top_builddir)/third-party/protobuf-c/src/libprotobuf-c.la -@HAVE_PROTOBUF_TRUE@Q_PROTOBUF_C_CLIENT_INCLUDES = -@HAVE_PROTOBUF_TRUE@Q_PROTOBUF_C_CLIENT_LDOPTS = -lprotobuf-c -@HAVE_PROTOBUF_TRUE@Q_PROTOC = protoc -@HAVE_PROTOBUF_TRUE@Q_PROTOC_C = protoc-c -@HAVE_PROTOBUF_TRUE@AM_V_PROTOC_C = $(am__v_PROTOC_C_$(V)) -@HAVE_PROTOBUF_TRUE@am__v_PROTOC_C_ = $(am__v_PROTOC_C_$(AM_DEFAULT_VERBOSITY)) -@HAVE_PROTOBUF_TRUE@am__v_PROTOC_C_0 = @echo " PROTOC_C" $@; -@HAVE_PROTOBUF_TRUE@am__v_PROTOC_C_1 = - -# -# Information about how to link to various libraries. -# -@HAVE_PROTOBUF_TRUE@Q_FRR_PB_CLIENT_LDOPTS = $(top_srcdir)/qpb/libfrr_pb.la $(Q_PROTOBUF_C_CLIENT_LDOPTS) -@HAVE_PROTOBUF_TRUE@Q_FPM_PB_CLIENT_LDOPTS = $(top_srcdir)/fpm/libfrrfpm_pb.la $(Q_FRR_PB_CLIENT_LDOPTS) -AUTOMAKE_OPTIONS = subdir-objects -AM_CPPFLAGS = \ - -I.. \ - -I$(top_srcdir) \ - -I$(top_srcdir)/lib \ - -I$(top_builddir)/lib \ - -I$(top_srcdir)/tests/helpers/c \ - -I$(top_builddir)/tests/helpers/c \ - -O - -@BGPD_FALSE@TESTS_BGPD = -@BGPD_TRUE@TESTS_BGPD = \ -@BGPD_TRUE@ bgpd/test_aspath \ -@BGPD_TRUE@ bgpd/test_capability \ -@BGPD_TRUE@ bgpd/test_packet \ -@BGPD_TRUE@ bgpd/test_ecommunity \ -@BGPD_TRUE@ bgpd/test_mp_attr \ -@BGPD_TRUE@ bgpd/test_mpath - -@ISISD_FALSE@TESTS_ISISD = -@ISISD_TRUE@@SOLARIS_FALSE@TESTS_ISISD = \ -@ISISD_TRUE@@SOLARIS_FALSE@ isisd/test_fuzz_isis_tlv \ -@ISISD_TRUE@@SOLARIS_FALSE@ isisd/test_isis_vertex_queue \ -@ISISD_TRUE@@SOLARIS_FALSE@ # end - -@ISISD_TRUE@@SOLARIS_TRUE@TESTS_ISISD = -@OSPF6D_FALSE@TESTS_OSPF6D = -@OSPF6D_TRUE@TESTS_OSPF6D = \ -@OSPF6D_TRUE@ ospf6d/test_lsdb \ -@OSPF6D_TRUE@ # end - -@ENABLE_BGP_VNC_FALSE@BGP_VNC_RFP_LIB = -@ENABLE_BGP_VNC_TRUE@BGP_VNC_RFP_LIB = @top_builddir@/$(LIBRFP)/librfp.a -noinst_HEADERS = \ - ./helpers/c/prng.h \ - ./helpers/c/tests.h \ - ./lib/cli/common_cli.h - -lib_test_buffer_SOURCES = lib/test_buffer.c -lib_test_checksum_SOURCES = lib/test_checksum.c -lib_test_heavy_thread_SOURCES = lib/test_heavy_thread.c helpers/c/main.c -lib_test_heavy_wq_SOURCES = lib/test_heavy_wq.c helpers/c/main.c -lib_test_heavy_SOURCES = lib/test_heavy.c helpers/c/main.c -lib_test_memory_SOURCES = lib/test_memory.c -lib_test_nexthop_iter_SOURCES = lib/test_nexthop_iter.c helpers/c/prng.c -lib_test_privs_SOURCES = lib/test_privs.c -lib_test_srcdest_table_SOURCES = lib/test_srcdest_table.c \ - helpers/c/prng.c - -lib_test_ringbuf_SOURCES = lib/test_ringbuf.c -lib_test_segv_SOURCES = lib/test_segv.c -lib_test_sig_SOURCES = lib/test_sig.c -lib_test_stream_SOURCES = lib/test_stream.c -lib_test_table_SOURCES = lib/test_table.c -lib_test_timer_correctness_SOURCES = lib/test_timer_correctness.c \ - helpers/c/prng.c - -lib_test_timer_performance_SOURCES = lib/test_timer_performance.c \ - helpers/c/prng.c - -lib_test_ttable_SOURCES = lib/test_ttable.c -lib_test_zmq_SOURCES = lib/test_zmq.c -lib_test_zmq_CFLAGS = $(AM_CFLAGS) $(ZEROMQ_CFLAGS) -lib_cli_test_cli_SOURCES = lib/cli/test_cli.c lib/cli/common_cli.c -lib_cli_test_commands_SOURCES = lib/cli/test_commands_defun.c \ - lib/cli/test_commands.c \ - helpers/c/prng.c - -bgpd_test_aspath_SOURCES = bgpd/test_aspath.c -bgpd_test_capability_SOURCES = bgpd/test_capability.c -bgpd_test_packet_SOURCES = bgpd/test_packet.c -bgpd_test_ecommunity_SOURCES = bgpd/test_ecommunity.c -bgpd_test_mp_attr_SOURCES = bgpd/test_mp_attr.c -bgpd_test_mpath_SOURCES = bgpd/test_mpath.c -isisd_test_fuzz_isis_tlv_SOURCES = isisd/test_fuzz_isis_tlv.c -isisd_test_fuzz_isis_tlv_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_builddir)/tests/isisd -isisd_test_isis_vertex_queue_SOURCES = isisd/test_isis_vertex_queue.c -ospf6d_test_lsdb_SOURCES = ospf6d/test_lsdb.c lib/cli/common_cli.c -ALL_TESTS_LDADD = ../lib/libfrr.la @LIBCAP@ -BGP_TEST_LDADD = ../bgpd/libbgp.a $(BGP_VNC_RFP_LIB) $(ALL_TESTS_LDADD) -lm -ISISD_TEST_LDADD = ../isisd/libisis.a $(ALL_TESTS_LDADD) -OSPF6_TEST_LDADD = ../ospf6d/libospf6.a $(ALL_TESTS_LDADD) -lib_test_buffer_LDADD = $(ALL_TESTS_LDADD) -lib_test_checksum_LDADD = $(ALL_TESTS_LDADD) -lib_test_heavy_thread_LDADD = $(ALL_TESTS_LDADD) -lm -lib_test_heavy_wq_LDADD = $(ALL_TESTS_LDADD) -lm -lib_test_heavy_LDADD = $(ALL_TESTS_LDADD) -lm -lib_test_memory_LDADD = $(ALL_TESTS_LDADD) -lib_test_nexthop_iter_LDADD = $(ALL_TESTS_LDADD) -lib_test_privs_LDADD = $(ALL_TESTS_LDADD) -lib_test_ringbuf_LDADD = $(ALL_TESTS_LDADD) -lib_test_srcdest_table_LDADD = $(ALL_TESTS_LDADD) -lib_test_segv_LDADD = $(ALL_TESTS_LDADD) -lib_test_sig_LDADD = $(ALL_TESTS_LDADD) -lib_test_stream_LDADD = $(ALL_TESTS_LDADD) -lib_test_table_LDADD = $(ALL_TESTS_LDADD) -lm -lib_test_timer_correctness_LDADD = $(ALL_TESTS_LDADD) -lib_test_timer_performance_LDADD = $(ALL_TESTS_LDADD) -lib_test_ttable_LDADD = $(ALL_TESTS_LDADD) -lib_test_zmq_LDADD = ../lib/libfrrzmq.la $(ALL_TESTS_LDADD) $(ZEROMQ_LIBS) -lib_cli_test_cli_LDADD = $(ALL_TESTS_LDADD) -lib_cli_test_commands_LDADD = $(ALL_TESTS_LDADD) -bgpd_test_aspath_LDADD = $(BGP_TEST_LDADD) -bgpd_test_capability_LDADD = $(BGP_TEST_LDADD) -bgpd_test_packet_LDADD = $(BGP_TEST_LDADD) -bgpd_test_ecommunity_LDADD = $(BGP_TEST_LDADD) -bgpd_test_mp_attr_LDADD = $(BGP_TEST_LDADD) -bgpd_test_mpath_LDADD = $(BGP_TEST_LDADD) -isisd_test_fuzz_isis_tlv_LDADD = $(ISISD_TEST_LDADD) -isisd_test_isis_vertex_queue_LDADD = $(ISISD_TEST_LDADD) -ospf6d_test_lsdb_LDADD = $(OSPF6_TEST_LDADD) -EXTRA_DIST = \ - runtests.py \ - bgpd/test_aspath.py \ - bgpd/test_capability.py \ - bgpd/test_ecommunity.py \ - bgpd/test_mp_attr.py \ - bgpd/test_mpath.py \ - helpers/python/frrsix.py \ - helpers/python/frrtest.py \ - isisd/test_fuzz_isis_tlv.py \ - isisd/test_fuzz_isis_tlv_tests.h.gz \ - isisd/test_isis_vertex_queue.py \ - lib/cli/test_commands.in \ - lib/cli/test_commands.py \ - lib/cli/test_commands.refout \ - lib/cli/test_cli.in \ - lib/cli/test_cli.py \ - lib/cli/test_cli.refout \ - lib/test_nexthop_iter.py \ - lib/test_ringbuf.py \ - lib/test_srcdest_table.py \ - lib/test_stream.py \ - lib/test_stream.refout \ - lib/test_table.py \ - lib/test_timer_correctness.py \ - lib/test_ttable.py \ - lib/test_ttable.refout \ - ospf6d/test_lsdb.py \ - ospf6d/test_lsdb.in \ - ospf6d/test_lsdb.refout \ - # end - -all: all-am - -.SUFFIXES: -.SUFFIXES: _clippy.c .proto .pb-c.c .pb-c.h .pb.h .c .l .lo .o .obj .y -$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(srcdir)/../common.am $(am__configure_deps) - @for dep in $?; do \ - case '$(am__configure_deps)' in \ - *$$dep*) \ - ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ - && { if test -f $@; then exit 0; else break; fi; }; \ - exit 1;; \ - esac; \ - done; \ - echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu tests/Makefile'; \ - $(am__cd) $(top_srcdir) && \ - $(AUTOMAKE) --gnu tests/Makefile -Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status - @case '$?' in \ - *config.status*) \ - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ - *) \ - echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ - esac; -$(srcdir)/../common.am $(am__empty): - -$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh - -$(top_srcdir)/configure: $(am__configure_deps) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh -$(ACLOCAL_M4): $(am__aclocal_m4_deps) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh -$(am__aclocal_m4_deps): - -clean-checkPROGRAMS: - @list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \ - echo " rm -f" $$list; \ - rm -f $$list || exit $$?; \ - test -n "$(EXEEXT)" || exit 0; \ - list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ - echo " rm -f" $$list; \ - rm -f $$list -bgpd/$(am__dirstamp): - @$(MKDIR_P) bgpd - @: > bgpd/$(am__dirstamp) -bgpd/$(DEPDIR)/$(am__dirstamp): - @$(MKDIR_P) bgpd/$(DEPDIR) - @: > bgpd/$(DEPDIR)/$(am__dirstamp) -bgpd/test_aspath.$(OBJEXT): bgpd/$(am__dirstamp) \ - bgpd/$(DEPDIR)/$(am__dirstamp) - -bgpd/test_aspath$(EXEEXT): $(bgpd_test_aspath_OBJECTS) $(bgpd_test_aspath_DEPENDENCIES) $(EXTRA_bgpd_test_aspath_DEPENDENCIES) bgpd/$(am__dirstamp) - @rm -f bgpd/test_aspath$(EXEEXT) - $(AM_V_CCLD)$(LINK) $(bgpd_test_aspath_OBJECTS) $(bgpd_test_aspath_LDADD) $(LIBS) -bgpd/test_capability.$(OBJEXT): bgpd/$(am__dirstamp) \ - bgpd/$(DEPDIR)/$(am__dirstamp) - -bgpd/test_capability$(EXEEXT): $(bgpd_test_capability_OBJECTS) $(bgpd_test_capability_DEPENDENCIES) $(EXTRA_bgpd_test_capability_DEPENDENCIES) bgpd/$(am__dirstamp) - @rm -f bgpd/test_capability$(EXEEXT) - $(AM_V_CCLD)$(LINK) $(bgpd_test_capability_OBJECTS) $(bgpd_test_capability_LDADD) $(LIBS) -bgpd/test_ecommunity.$(OBJEXT): bgpd/$(am__dirstamp) \ - bgpd/$(DEPDIR)/$(am__dirstamp) - -bgpd/test_ecommunity$(EXEEXT): $(bgpd_test_ecommunity_OBJECTS) $(bgpd_test_ecommunity_DEPENDENCIES) $(EXTRA_bgpd_test_ecommunity_DEPENDENCIES) bgpd/$(am__dirstamp) - @rm -f bgpd/test_ecommunity$(EXEEXT) - $(AM_V_CCLD)$(LINK) $(bgpd_test_ecommunity_OBJECTS) $(bgpd_test_ecommunity_LDADD) $(LIBS) -bgpd/test_mp_attr.$(OBJEXT): bgpd/$(am__dirstamp) \ - bgpd/$(DEPDIR)/$(am__dirstamp) - -bgpd/test_mp_attr$(EXEEXT): $(bgpd_test_mp_attr_OBJECTS) $(bgpd_test_mp_attr_DEPENDENCIES) $(EXTRA_bgpd_test_mp_attr_DEPENDENCIES) bgpd/$(am__dirstamp) - @rm -f bgpd/test_mp_attr$(EXEEXT) - $(AM_V_CCLD)$(LINK) $(bgpd_test_mp_attr_OBJECTS) $(bgpd_test_mp_attr_LDADD) $(LIBS) -bgpd/test_mpath.$(OBJEXT): bgpd/$(am__dirstamp) \ - bgpd/$(DEPDIR)/$(am__dirstamp) - -bgpd/test_mpath$(EXEEXT): $(bgpd_test_mpath_OBJECTS) $(bgpd_test_mpath_DEPENDENCIES) $(EXTRA_bgpd_test_mpath_DEPENDENCIES) bgpd/$(am__dirstamp) - @rm -f bgpd/test_mpath$(EXEEXT) - $(AM_V_CCLD)$(LINK) $(bgpd_test_mpath_OBJECTS) $(bgpd_test_mpath_LDADD) $(LIBS) -bgpd/test_packet.$(OBJEXT): bgpd/$(am__dirstamp) \ - bgpd/$(DEPDIR)/$(am__dirstamp) - -bgpd/test_packet$(EXEEXT): $(bgpd_test_packet_OBJECTS) $(bgpd_test_packet_DEPENDENCIES) $(EXTRA_bgpd_test_packet_DEPENDENCIES) bgpd/$(am__dirstamp) - @rm -f bgpd/test_packet$(EXEEXT) - $(AM_V_CCLD)$(LINK) $(bgpd_test_packet_OBJECTS) $(bgpd_test_packet_LDADD) $(LIBS) -isisd/$(am__dirstamp): - @$(MKDIR_P) isisd - @: > isisd/$(am__dirstamp) -isisd/$(DEPDIR)/$(am__dirstamp): - @$(MKDIR_P) isisd/$(DEPDIR) - @: > isisd/$(DEPDIR)/$(am__dirstamp) -isisd/isisd_test_fuzz_isis_tlv-test_fuzz_isis_tlv.$(OBJEXT): \ - isisd/$(am__dirstamp) isisd/$(DEPDIR)/$(am__dirstamp) - -isisd/test_fuzz_isis_tlv$(EXEEXT): $(isisd_test_fuzz_isis_tlv_OBJECTS) $(isisd_test_fuzz_isis_tlv_DEPENDENCIES) $(EXTRA_isisd_test_fuzz_isis_tlv_DEPENDENCIES) isisd/$(am__dirstamp) - @rm -f isisd/test_fuzz_isis_tlv$(EXEEXT) - $(AM_V_CCLD)$(LINK) $(isisd_test_fuzz_isis_tlv_OBJECTS) $(isisd_test_fuzz_isis_tlv_LDADD) $(LIBS) -isisd/test_isis_vertex_queue.$(OBJEXT): isisd/$(am__dirstamp) \ - isisd/$(DEPDIR)/$(am__dirstamp) - -isisd/test_isis_vertex_queue$(EXEEXT): $(isisd_test_isis_vertex_queue_OBJECTS) $(isisd_test_isis_vertex_queue_DEPENDENCIES) $(EXTRA_isisd_test_isis_vertex_queue_DEPENDENCIES) isisd/$(am__dirstamp) - @rm -f isisd/test_isis_vertex_queue$(EXEEXT) - $(AM_V_CCLD)$(LINK) $(isisd_test_isis_vertex_queue_OBJECTS) $(isisd_test_isis_vertex_queue_LDADD) $(LIBS) -lib/cli/$(am__dirstamp): - @$(MKDIR_P) lib/cli - @: > lib/cli/$(am__dirstamp) -lib/cli/$(DEPDIR)/$(am__dirstamp): - @$(MKDIR_P) lib/cli/$(DEPDIR) - @: > lib/cli/$(DEPDIR)/$(am__dirstamp) -lib/cli/test_cli.$(OBJEXT): lib/cli/$(am__dirstamp) \ - lib/cli/$(DEPDIR)/$(am__dirstamp) -lib/cli/common_cli.$(OBJEXT): lib/cli/$(am__dirstamp) \ - lib/cli/$(DEPDIR)/$(am__dirstamp) - -lib/cli/test_cli$(EXEEXT): $(lib_cli_test_cli_OBJECTS) $(lib_cli_test_cli_DEPENDENCIES) $(EXTRA_lib_cli_test_cli_DEPENDENCIES) lib/cli/$(am__dirstamp) - @rm -f lib/cli/test_cli$(EXEEXT) - $(AM_V_CCLD)$(LINK) $(lib_cli_test_cli_OBJECTS) $(lib_cli_test_cli_LDADD) $(LIBS) -lib/cli/test_commands_defun.$(OBJEXT): lib/cli/$(am__dirstamp) \ - lib/cli/$(DEPDIR)/$(am__dirstamp) -lib/cli/test_commands.$(OBJEXT): lib/cli/$(am__dirstamp) \ - lib/cli/$(DEPDIR)/$(am__dirstamp) -helpers/c/$(am__dirstamp): - @$(MKDIR_P) helpers/c - @: > helpers/c/$(am__dirstamp) -helpers/c/$(DEPDIR)/$(am__dirstamp): - @$(MKDIR_P) helpers/c/$(DEPDIR) - @: > helpers/c/$(DEPDIR)/$(am__dirstamp) -helpers/c/prng.$(OBJEXT): helpers/c/$(am__dirstamp) \ - helpers/c/$(DEPDIR)/$(am__dirstamp) - -lib/cli/test_commands$(EXEEXT): $(lib_cli_test_commands_OBJECTS) $(lib_cli_test_commands_DEPENDENCIES) $(EXTRA_lib_cli_test_commands_DEPENDENCIES) lib/cli/$(am__dirstamp) - @rm -f lib/cli/test_commands$(EXEEXT) - $(AM_V_CCLD)$(LINK) $(lib_cli_test_commands_OBJECTS) $(lib_cli_test_commands_LDADD) $(LIBS) -lib/$(am__dirstamp): - @$(MKDIR_P) lib - @: > lib/$(am__dirstamp) -lib/$(DEPDIR)/$(am__dirstamp): - @$(MKDIR_P) lib/$(DEPDIR) - @: > lib/$(DEPDIR)/$(am__dirstamp) -lib/test_buffer.$(OBJEXT): lib/$(am__dirstamp) \ - lib/$(DEPDIR)/$(am__dirstamp) - -lib/test_buffer$(EXEEXT): $(lib_test_buffer_OBJECTS) $(lib_test_buffer_DEPENDENCIES) $(EXTRA_lib_test_buffer_DEPENDENCIES) lib/$(am__dirstamp) - @rm -f lib/test_buffer$(EXEEXT) - $(AM_V_CCLD)$(LINK) $(lib_test_buffer_OBJECTS) $(lib_test_buffer_LDADD) $(LIBS) -lib/test_checksum.$(OBJEXT): lib/$(am__dirstamp) \ - lib/$(DEPDIR)/$(am__dirstamp) - -lib/test_checksum$(EXEEXT): $(lib_test_checksum_OBJECTS) $(lib_test_checksum_DEPENDENCIES) $(EXTRA_lib_test_checksum_DEPENDENCIES) lib/$(am__dirstamp) - @rm -f lib/test_checksum$(EXEEXT) - $(AM_V_CCLD)$(LINK) $(lib_test_checksum_OBJECTS) $(lib_test_checksum_LDADD) $(LIBS) -lib/test_heavy.$(OBJEXT): lib/$(am__dirstamp) \ - lib/$(DEPDIR)/$(am__dirstamp) -helpers/c/main.$(OBJEXT): helpers/c/$(am__dirstamp) \ - helpers/c/$(DEPDIR)/$(am__dirstamp) - -lib/test_heavy$(EXEEXT): $(lib_test_heavy_OBJECTS) $(lib_test_heavy_DEPENDENCIES) $(EXTRA_lib_test_heavy_DEPENDENCIES) lib/$(am__dirstamp) - @rm -f lib/test_heavy$(EXEEXT) - $(AM_V_CCLD)$(LINK) $(lib_test_heavy_OBJECTS) $(lib_test_heavy_LDADD) $(LIBS) -lib/test_heavy_thread.$(OBJEXT): lib/$(am__dirstamp) \ - lib/$(DEPDIR)/$(am__dirstamp) - -lib/test_heavy_thread$(EXEEXT): $(lib_test_heavy_thread_OBJECTS) $(lib_test_heavy_thread_DEPENDENCIES) $(EXTRA_lib_test_heavy_thread_DEPENDENCIES) lib/$(am__dirstamp) - @rm -f lib/test_heavy_thread$(EXEEXT) - $(AM_V_CCLD)$(LINK) $(lib_test_heavy_thread_OBJECTS) $(lib_test_heavy_thread_LDADD) $(LIBS) -lib/test_heavy_wq.$(OBJEXT): lib/$(am__dirstamp) \ - lib/$(DEPDIR)/$(am__dirstamp) - -lib/test_heavy_wq$(EXEEXT): $(lib_test_heavy_wq_OBJECTS) $(lib_test_heavy_wq_DEPENDENCIES) $(EXTRA_lib_test_heavy_wq_DEPENDENCIES) lib/$(am__dirstamp) - @rm -f lib/test_heavy_wq$(EXEEXT) - $(AM_V_CCLD)$(LINK) $(lib_test_heavy_wq_OBJECTS) $(lib_test_heavy_wq_LDADD) $(LIBS) -lib/test_memory.$(OBJEXT): lib/$(am__dirstamp) \ - lib/$(DEPDIR)/$(am__dirstamp) - -lib/test_memory$(EXEEXT): $(lib_test_memory_OBJECTS) $(lib_test_memory_DEPENDENCIES) $(EXTRA_lib_test_memory_DEPENDENCIES) lib/$(am__dirstamp) - @rm -f lib/test_memory$(EXEEXT) - $(AM_V_CCLD)$(LINK) $(lib_test_memory_OBJECTS) $(lib_test_memory_LDADD) $(LIBS) -lib/test_nexthop_iter.$(OBJEXT): lib/$(am__dirstamp) \ - lib/$(DEPDIR)/$(am__dirstamp) - -lib/test_nexthop_iter$(EXEEXT): $(lib_test_nexthop_iter_OBJECTS) $(lib_test_nexthop_iter_DEPENDENCIES) $(EXTRA_lib_test_nexthop_iter_DEPENDENCIES) lib/$(am__dirstamp) - @rm -f lib/test_nexthop_iter$(EXEEXT) - $(AM_V_CCLD)$(LINK) $(lib_test_nexthop_iter_OBJECTS) $(lib_test_nexthop_iter_LDADD) $(LIBS) -lib/test_privs.$(OBJEXT): lib/$(am__dirstamp) \ - lib/$(DEPDIR)/$(am__dirstamp) - -lib/test_privs$(EXEEXT): $(lib_test_privs_OBJECTS) $(lib_test_privs_DEPENDENCIES) $(EXTRA_lib_test_privs_DEPENDENCIES) lib/$(am__dirstamp) - @rm -f lib/test_privs$(EXEEXT) - $(AM_V_CCLD)$(LINK) $(lib_test_privs_OBJECTS) $(lib_test_privs_LDADD) $(LIBS) -lib/test_ringbuf.$(OBJEXT): lib/$(am__dirstamp) \ - lib/$(DEPDIR)/$(am__dirstamp) - -lib/test_ringbuf$(EXEEXT): $(lib_test_ringbuf_OBJECTS) $(lib_test_ringbuf_DEPENDENCIES) $(EXTRA_lib_test_ringbuf_DEPENDENCIES) lib/$(am__dirstamp) - @rm -f lib/test_ringbuf$(EXEEXT) - $(AM_V_CCLD)$(LINK) $(lib_test_ringbuf_OBJECTS) $(lib_test_ringbuf_LDADD) $(LIBS) -lib/test_segv.$(OBJEXT): lib/$(am__dirstamp) \ - lib/$(DEPDIR)/$(am__dirstamp) - -lib/test_segv$(EXEEXT): $(lib_test_segv_OBJECTS) $(lib_test_segv_DEPENDENCIES) $(EXTRA_lib_test_segv_DEPENDENCIES) lib/$(am__dirstamp) - @rm -f lib/test_segv$(EXEEXT) - $(AM_V_CCLD)$(LINK) $(lib_test_segv_OBJECTS) $(lib_test_segv_LDADD) $(LIBS) -lib/test_sig.$(OBJEXT): lib/$(am__dirstamp) \ - lib/$(DEPDIR)/$(am__dirstamp) - -lib/test_sig$(EXEEXT): $(lib_test_sig_OBJECTS) $(lib_test_sig_DEPENDENCIES) $(EXTRA_lib_test_sig_DEPENDENCIES) lib/$(am__dirstamp) - @rm -f lib/test_sig$(EXEEXT) - $(AM_V_CCLD)$(LINK) $(lib_test_sig_OBJECTS) $(lib_test_sig_LDADD) $(LIBS) -lib/test_srcdest_table.$(OBJEXT): lib/$(am__dirstamp) \ - lib/$(DEPDIR)/$(am__dirstamp) - -lib/test_srcdest_table$(EXEEXT): $(lib_test_srcdest_table_OBJECTS) $(lib_test_srcdest_table_DEPENDENCIES) $(EXTRA_lib_test_srcdest_table_DEPENDENCIES) lib/$(am__dirstamp) - @rm -f lib/test_srcdest_table$(EXEEXT) - $(AM_V_CCLD)$(LINK) $(lib_test_srcdest_table_OBJECTS) $(lib_test_srcdest_table_LDADD) $(LIBS) -lib/test_stream.$(OBJEXT): lib/$(am__dirstamp) \ - lib/$(DEPDIR)/$(am__dirstamp) - -lib/test_stream$(EXEEXT): $(lib_test_stream_OBJECTS) $(lib_test_stream_DEPENDENCIES) $(EXTRA_lib_test_stream_DEPENDENCIES) lib/$(am__dirstamp) - @rm -f lib/test_stream$(EXEEXT) - $(AM_V_CCLD)$(LINK) $(lib_test_stream_OBJECTS) $(lib_test_stream_LDADD) $(LIBS) -lib/test_table.$(OBJEXT): lib/$(am__dirstamp) \ - lib/$(DEPDIR)/$(am__dirstamp) - -lib/test_table$(EXEEXT): $(lib_test_table_OBJECTS) $(lib_test_table_DEPENDENCIES) $(EXTRA_lib_test_table_DEPENDENCIES) lib/$(am__dirstamp) - @rm -f lib/test_table$(EXEEXT) - $(AM_V_CCLD)$(LINK) $(lib_test_table_OBJECTS) $(lib_test_table_LDADD) $(LIBS) -lib/test_timer_correctness.$(OBJEXT): lib/$(am__dirstamp) \ - lib/$(DEPDIR)/$(am__dirstamp) - -lib/test_timer_correctness$(EXEEXT): $(lib_test_timer_correctness_OBJECTS) $(lib_test_timer_correctness_DEPENDENCIES) $(EXTRA_lib_test_timer_correctness_DEPENDENCIES) lib/$(am__dirstamp) - @rm -f lib/test_timer_correctness$(EXEEXT) - $(AM_V_CCLD)$(LINK) $(lib_test_timer_correctness_OBJECTS) $(lib_test_timer_correctness_LDADD) $(LIBS) -lib/test_timer_performance.$(OBJEXT): lib/$(am__dirstamp) \ - lib/$(DEPDIR)/$(am__dirstamp) - -lib/test_timer_performance$(EXEEXT): $(lib_test_timer_performance_OBJECTS) $(lib_test_timer_performance_DEPENDENCIES) $(EXTRA_lib_test_timer_performance_DEPENDENCIES) lib/$(am__dirstamp) - @rm -f lib/test_timer_performance$(EXEEXT) - $(AM_V_CCLD)$(LINK) $(lib_test_timer_performance_OBJECTS) $(lib_test_timer_performance_LDADD) $(LIBS) -lib/test_ttable.$(OBJEXT): lib/$(am__dirstamp) \ - lib/$(DEPDIR)/$(am__dirstamp) - -lib/test_ttable$(EXEEXT): $(lib_test_ttable_OBJECTS) $(lib_test_ttable_DEPENDENCIES) $(EXTRA_lib_test_ttable_DEPENDENCIES) lib/$(am__dirstamp) - @rm -f lib/test_ttable$(EXEEXT) - $(AM_V_CCLD)$(LINK) $(lib_test_ttable_OBJECTS) $(lib_test_ttable_LDADD) $(LIBS) -lib/lib_test_zmq-test_zmq.$(OBJEXT): lib/$(am__dirstamp) \ - lib/$(DEPDIR)/$(am__dirstamp) - -lib/test_zmq$(EXEEXT): $(lib_test_zmq_OBJECTS) $(lib_test_zmq_DEPENDENCIES) $(EXTRA_lib_test_zmq_DEPENDENCIES) lib/$(am__dirstamp) - @rm -f lib/test_zmq$(EXEEXT) - $(AM_V_CCLD)$(lib_test_zmq_LINK) $(lib_test_zmq_OBJECTS) $(lib_test_zmq_LDADD) $(LIBS) -ospf6d/$(am__dirstamp): - @$(MKDIR_P) ospf6d - @: > ospf6d/$(am__dirstamp) -ospf6d/$(DEPDIR)/$(am__dirstamp): - @$(MKDIR_P) ospf6d/$(DEPDIR) - @: > ospf6d/$(DEPDIR)/$(am__dirstamp) -ospf6d/test_lsdb.$(OBJEXT): ospf6d/$(am__dirstamp) \ - ospf6d/$(DEPDIR)/$(am__dirstamp) - -ospf6d/test_lsdb$(EXEEXT): $(ospf6d_test_lsdb_OBJECTS) $(ospf6d_test_lsdb_DEPENDENCIES) $(EXTRA_ospf6d_test_lsdb_DEPENDENCIES) ospf6d/$(am__dirstamp) - @rm -f ospf6d/test_lsdb$(EXEEXT) - $(AM_V_CCLD)$(LINK) $(ospf6d_test_lsdb_OBJECTS) $(ospf6d_test_lsdb_LDADD) $(LIBS) - -mostlyclean-compile: - -rm -f *.$(OBJEXT) - -rm -f bgpd/*.$(OBJEXT) - -rm -f helpers/c/*.$(OBJEXT) - -rm -f isisd/*.$(OBJEXT) - -rm -f lib/*.$(OBJEXT) - -rm -f lib/cli/*.$(OBJEXT) - -rm -f ospf6d/*.$(OBJEXT) - -distclean-compile: - -rm -f *.tab.c - -@AMDEP_TRUE@@am__include@ @am__quote@bgpd/$(DEPDIR)/test_aspath.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@bgpd/$(DEPDIR)/test_capability.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@bgpd/$(DEPDIR)/test_ecommunity.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@bgpd/$(DEPDIR)/test_mp_attr.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@bgpd/$(DEPDIR)/test_mpath.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@bgpd/$(DEPDIR)/test_packet.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@helpers/c/$(DEPDIR)/main.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@helpers/c/$(DEPDIR)/prng.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@isisd/$(DEPDIR)/isisd_test_fuzz_isis_tlv-test_fuzz_isis_tlv.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@isisd/$(DEPDIR)/test_isis_vertex_queue.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/lib_test_zmq-test_zmq.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/test_buffer.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/test_checksum.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/test_heavy.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/test_heavy_thread.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/test_heavy_wq.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/test_memory.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/test_nexthop_iter.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/test_privs.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/test_ringbuf.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/test_segv.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/test_sig.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/test_srcdest_table.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/test_stream.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/test_table.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/test_timer_correctness.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/test_timer_performance.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/test_ttable.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@lib/cli/$(DEPDIR)/common_cli.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@lib/cli/$(DEPDIR)/test_cli.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@lib/cli/$(DEPDIR)/test_commands.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@lib/cli/$(DEPDIR)/test_commands_defun.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@ospf6d/$(DEPDIR)/test_lsdb.Po@am__quote@ - -.c.o: -@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ -@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ -@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< - -.c.obj: -@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ -@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ -@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` - -.c.lo: -@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ -@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ -@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< - -isisd/isisd_test_fuzz_isis_tlv-test_fuzz_isis_tlv.o: isisd/test_fuzz_isis_tlv.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(isisd_test_fuzz_isis_tlv_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT isisd/isisd_test_fuzz_isis_tlv-test_fuzz_isis_tlv.o -MD -MP -MF isisd/$(DEPDIR)/isisd_test_fuzz_isis_tlv-test_fuzz_isis_tlv.Tpo -c -o isisd/isisd_test_fuzz_isis_tlv-test_fuzz_isis_tlv.o `test -f 'isisd/test_fuzz_isis_tlv.c' || echo '$(srcdir)/'`isisd/test_fuzz_isis_tlv.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) isisd/$(DEPDIR)/isisd_test_fuzz_isis_tlv-test_fuzz_isis_tlv.Tpo isisd/$(DEPDIR)/isisd_test_fuzz_isis_tlv-test_fuzz_isis_tlv.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='isisd/test_fuzz_isis_tlv.c' object='isisd/isisd_test_fuzz_isis_tlv-test_fuzz_isis_tlv.o' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(isisd_test_fuzz_isis_tlv_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o isisd/isisd_test_fuzz_isis_tlv-test_fuzz_isis_tlv.o `test -f 'isisd/test_fuzz_isis_tlv.c' || echo '$(srcdir)/'`isisd/test_fuzz_isis_tlv.c - -isisd/isisd_test_fuzz_isis_tlv-test_fuzz_isis_tlv.obj: isisd/test_fuzz_isis_tlv.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(isisd_test_fuzz_isis_tlv_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT isisd/isisd_test_fuzz_isis_tlv-test_fuzz_isis_tlv.obj -MD -MP -MF isisd/$(DEPDIR)/isisd_test_fuzz_isis_tlv-test_fuzz_isis_tlv.Tpo -c -o isisd/isisd_test_fuzz_isis_tlv-test_fuzz_isis_tlv.obj `if test -f 'isisd/test_fuzz_isis_tlv.c'; then $(CYGPATH_W) 'isisd/test_fuzz_isis_tlv.c'; else $(CYGPATH_W) '$(srcdir)/isisd/test_fuzz_isis_tlv.c'; fi` -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) isisd/$(DEPDIR)/isisd_test_fuzz_isis_tlv-test_fuzz_isis_tlv.Tpo isisd/$(DEPDIR)/isisd_test_fuzz_isis_tlv-test_fuzz_isis_tlv.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='isisd/test_fuzz_isis_tlv.c' object='isisd/isisd_test_fuzz_isis_tlv-test_fuzz_isis_tlv.obj' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(isisd_test_fuzz_isis_tlv_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o isisd/isisd_test_fuzz_isis_tlv-test_fuzz_isis_tlv.obj `if test -f 'isisd/test_fuzz_isis_tlv.c'; then $(CYGPATH_W) 'isisd/test_fuzz_isis_tlv.c'; else $(CYGPATH_W) '$(srcdir)/isisd/test_fuzz_isis_tlv.c'; fi` - -lib/lib_test_zmq-test_zmq.o: lib/test_zmq.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lib_test_zmq_CFLAGS) $(CFLAGS) -MT lib/lib_test_zmq-test_zmq.o -MD -MP -MF lib/$(DEPDIR)/lib_test_zmq-test_zmq.Tpo -c -o lib/lib_test_zmq-test_zmq.o `test -f 'lib/test_zmq.c' || echo '$(srcdir)/'`lib/test_zmq.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) lib/$(DEPDIR)/lib_test_zmq-test_zmq.Tpo lib/$(DEPDIR)/lib_test_zmq-test_zmq.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='lib/test_zmq.c' object='lib/lib_test_zmq-test_zmq.o' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lib_test_zmq_CFLAGS) $(CFLAGS) -c -o lib/lib_test_zmq-test_zmq.o `test -f 'lib/test_zmq.c' || echo '$(srcdir)/'`lib/test_zmq.c - -lib/lib_test_zmq-test_zmq.obj: lib/test_zmq.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lib_test_zmq_CFLAGS) $(CFLAGS) -MT lib/lib_test_zmq-test_zmq.obj -MD -MP -MF lib/$(DEPDIR)/lib_test_zmq-test_zmq.Tpo -c -o lib/lib_test_zmq-test_zmq.obj `if test -f 'lib/test_zmq.c'; then $(CYGPATH_W) 'lib/test_zmq.c'; else $(CYGPATH_W) '$(srcdir)/lib/test_zmq.c'; fi` -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) lib/$(DEPDIR)/lib_test_zmq-test_zmq.Tpo lib/$(DEPDIR)/lib_test_zmq-test_zmq.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='lib/test_zmq.c' object='lib/lib_test_zmq-test_zmq.obj' libtool=no @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lib_test_zmq_CFLAGS) $(CFLAGS) -c -o lib/lib_test_zmq-test_zmq.obj `if test -f 'lib/test_zmq.c'; then $(CYGPATH_W) 'lib/test_zmq.c'; else $(CYGPATH_W) '$(srcdir)/lib/test_zmq.c'; fi` - -mostlyclean-libtool: - -rm -f *.lo - -clean-libtool: - -rm -rf .libs _libs - -rm -rf bgpd/.libs bgpd/_libs - -rm -rf isisd/.libs isisd/_libs - -rm -rf lib/.libs lib/_libs - -rm -rf lib/cli/.libs lib/cli/_libs - -rm -rf ospf6d/.libs ospf6d/_libs - -ID: $(am__tagged_files) - $(am__define_uniq_tagged_files); mkid -fID $$unique -tags: tags-am -TAGS: tags - -tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) - set x; \ - here=`pwd`; \ - $(am__define_uniq_tagged_files); \ - shift; \ - if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ - test -n "$$unique" || unique=$$empty_fix; \ - if test $$# -gt 0; then \ - $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ - "$$@" $$unique; \ - else \ - $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ - $$unique; \ - fi; \ - fi -ctags: ctags-am - -CTAGS: ctags -ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) - $(am__define_uniq_tagged_files); \ - test -z "$(CTAGS_ARGS)$$unique" \ - || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ - $$unique - -GTAGS: - here=`$(am__cd) $(top_builddir) && pwd` \ - && $(am__cd) $(top_srcdir) \ - && gtags -i $(GTAGS_ARGS) "$$here" -cscopelist: cscopelist-am - -cscopelist-am: $(am__tagged_files) - list='$(am__tagged_files)'; \ - case "$(srcdir)" in \ - [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ - *) sdir=$(subdir)/$(srcdir) ;; \ - esac; \ - for i in $$list; do \ - if test -f "$$i"; then \ - echo "$(subdir)/$$i"; \ - else \ - echo "$$sdir/$$i"; \ - fi; \ - done >> $(top_builddir)/cscope.files - -distclean-tags: - -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags - -distdir: $(DISTFILES) - @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ - topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ - list='$(DISTFILES)'; \ - dist_files=`for file in $$list; do echo $$file; done | \ - sed -e "s|^$$srcdirstrip/||;t" \ - -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ - case $$dist_files in \ - */*) $(MKDIR_P) `echo "$$dist_files" | \ - sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ - sort -u` ;; \ - esac; \ - for file in $$dist_files; do \ - if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ - if test -d $$d/$$file; then \ - dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ - if test -d "$(distdir)/$$file"; then \ - find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ - fi; \ - if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ - cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ - find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ - fi; \ - cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ - else \ - test -f "$(distdir)/$$file" \ - || cp -p $$d/$$file "$(distdir)/$$file" \ - || exit 1; \ - fi; \ - done -check-am: all-am - $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS) -check: check-am -all-am: Makefile $(HEADERS) -installdirs: -install: install-am -install-exec: install-exec-am -install-data: install-data-am -uninstall: uninstall-am - -install-am: all-am - @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am - -installcheck: installcheck-am -install-strip: - if test -z '$(STRIP)'; then \ - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - install; \ - else \ - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ - fi -mostlyclean-generic: - -clean-generic: - -distclean-generic: - -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) - -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) - -rm -f bgpd/$(DEPDIR)/$(am__dirstamp) - -rm -f bgpd/$(am__dirstamp) - -rm -f helpers/c/$(DEPDIR)/$(am__dirstamp) - -rm -f helpers/c/$(am__dirstamp) - -rm -f isisd/$(DEPDIR)/$(am__dirstamp) - -rm -f isisd/$(am__dirstamp) - -rm -f lib/$(DEPDIR)/$(am__dirstamp) - -rm -f lib/$(am__dirstamp) - -rm -f lib/cli/$(DEPDIR)/$(am__dirstamp) - -rm -f lib/cli/$(am__dirstamp) - -rm -f ospf6d/$(DEPDIR)/$(am__dirstamp) - -rm -f ospf6d/$(am__dirstamp) - -maintainer-clean-generic: - @echo "This command is intended for maintainers to use" - @echo "it deletes files that may require special tools to rebuild." -clean: clean-am - -clean-am: clean-checkPROGRAMS clean-generic clean-libtool \ - mostlyclean-am - -distclean: distclean-am - -rm -rf bgpd/$(DEPDIR) helpers/c/$(DEPDIR) isisd/$(DEPDIR) lib/$(DEPDIR) lib/cli/$(DEPDIR) ospf6d/$(DEPDIR) - -rm -f Makefile -distclean-am: clean-am distclean-compile distclean-generic \ - distclean-tags - -dvi: dvi-am - -dvi-am: - -html: html-am - -html-am: - -info: info-am - -info-am: - -install-data-am: - -install-dvi: install-dvi-am - -install-dvi-am: - -install-exec-am: - -install-html: install-html-am - -install-html-am: - -install-info: install-info-am - -install-info-am: - -install-man: - -install-pdf: install-pdf-am - -install-pdf-am: - -install-ps: install-ps-am - -install-ps-am: - -installcheck-am: - -maintainer-clean: maintainer-clean-am - -rm -rf bgpd/$(DEPDIR) helpers/c/$(DEPDIR) isisd/$(DEPDIR) lib/$(DEPDIR) lib/cli/$(DEPDIR) ospf6d/$(DEPDIR) - -rm -f Makefile -maintainer-clean-am: distclean-am maintainer-clean-generic - -mostlyclean: mostlyclean-am - -mostlyclean-am: mostlyclean-compile mostlyclean-generic \ - mostlyclean-libtool - -pdf: pdf-am - -pdf-am: - -ps: ps-am - -ps-am: - -uninstall-am: - -.MAKE: check-am install-am install-strip - -.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean \ - clean-checkPROGRAMS clean-generic clean-libtool cscopelist-am \ - ctags ctags-am distclean distclean-compile distclean-generic \ - distclean-libtool distclean-tags distdir dvi dvi-am html \ - html-am info info-am install install-am install-data \ - install-data-am install-dvi install-dvi-am install-exec \ - install-exec-am install-html install-html-am install-info \ - install-info-am install-man install-pdf install-pdf-am \ - install-ps install-ps-am install-strip installcheck \ - installcheck-am installdirs maintainer-clean \ - maintainer-clean-generic mostlyclean mostlyclean-compile \ - mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ - tags tags-am uninstall uninstall-am - -.PRECIOUS: Makefile - -.c_clippy.c: - @{ test -x $(top_builddir)/$(HOSTTOOLS)lib/clippy || $(MAKE) -C $(top_builddir)/$(HOSTTOOLS) lib/clippy; } - $(AM_V_CLIPPY)$(top_builddir)/$(HOSTTOOLS)lib/clippy $(top_srcdir)/python/clidef.py -o $@ $< - -.l.c: - $(AM_V_LEX)$(am__skiplex) $(LEXCOMPILE) $< -.y.c: - $(AM_V_YACC)$(am__skipyacc) $(YACCCOMPILE) $< - -# Rules -@HAVE_PROTOBUF_TRUE@.proto.pb.h: -@HAVE_PROTOBUF_TRUE@ $(Q_PROTOC) -I$(top_srcdir) --cpp_out=$(top_srcdir) $(top_srcdir)/$^ - -@HAVE_PROTOBUF_TRUE@.proto.pb-c.c: -@HAVE_PROTOBUF_TRUE@ $(AM_V_PROTOC_C)$(Q_PROTOC_C) -I$(top_srcdir) --c_out=$(top_srcdir) $(top_srcdir)/$^ -@HAVE_PROTOBUF_TRUE@.pb-c.c.pb-c.h: -@HAVE_PROTOBUF_TRUE@ @/bin/true - -PYTHON ?= python - -lib/cli/test_cli.o: lib/cli/test_cli_clippy.c -ospf6d/test_lsdb.o: ospf6d/test_lsdb_clippy.c - -../vtysh/vtysh_cmd.c: - $(MAKE) -C ../vtysh vtysh_cmd.c - -lib/cli/test_commands_defun.c: ../vtysh/vtysh_cmd.c - sed \ - -e 's/"vtysh\.h"/"tests.h"/' \ - -e 's/vtysh_init_cmd/test_init_cmd/' \ - -e 's/VTYSH_[A-Z][A-Z_0-9]*/0/g' \ - < ../vtysh/vtysh_cmd.c \ - > "$@" - -isisd/test_fuzz_isis_tlv_tests.h: $(top_srcdir)/tests/isisd/test_fuzz_isis_tlv_tests.h.gz - gzip -d < $(top_srcdir)/tests/isisd/test_fuzz_isis_tlv_tests.h.gz > "$@" -isisd/isisd_test_fuzz_isis_tlv-test_fuzz_isis_tlv.$(OBJEXT): \ - isisd/test_fuzz_isis_tlv_tests.h - -.PHONY: tests.xml -tests.xml: $(check_PROGRAMS) - $(PYTHON) $(srcdir)/runtests.py --junitxml=$@ -v $(srcdir) -check: tests.xml - -# Tell versions [3.59,3.63) of GNU make to not export all variables. -# Otherwise a system limit (for SysV at least) may be exceeded. -.NOEXPORT: diff --git a/tests/bgpd/test_aspath.c b/tests/bgpd/test_aspath.c index b2612892f9..439891b559 100644 --- a/tests/bgpd/test_aspath.c +++ b/tests/bgpd/test_aspath.c @@ -271,57 +271,9 @@ static struct test_segment { 0x03, 0xce, 0x01, 0x10, 0x00, 0x85, 0xed, }, 502, - {"8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285", - - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285", + {"8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285", + + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285", 250, 0, NOT_ALL_PRIVATE, 4096, 4, 8466}, }, { @@ -383,15 +335,13 @@ static struct test_segment { { /* 20 */ "reconcile_confed", - "confseq(123,456,789) confset(456,124,788) seq(6435,59408,21665)" - " set(23456,23456,23456), seq(23456,23456,23456)", + "confseq(123,456,789) confset(456,124,788) seq(6435,59408,21665) set(23456,23456,23456), seq(23456,23456,23456)", {0x3, 0x3, 0x00, 0x7b, 0x01, 0xc8, 0x03, 0x15, 0x4, 0x3, 0x01, 0xc8, 0x00, 0x7c, 0x03, 0x14, 0x2, 0x3, 0x19, 0x23, 0xe8, 0x10, 0x54, 0xa1, 0x1, 0x3, 0x5b, 0xa0, 0x5b, 0xa0, 0x5b, 0xa0, 0x2, 0x3, 0x5b, 0xa0, 0x5b, 0xa0, 0x5b, 0xa0}, 40, - {"(123 456 789) [124,456,788] 6435 59408 21665" - " {23456} 23456 23456 23456", + {"(123 456 789) [124,456,788] 6435 59408 21665 {23456} 23456 23456 23456", "6435 59408 21665 {23456} 23456 23456 23456", 7, 4, NOT_ALL_PRIVATE, 23456, 1, 6435}, }, @@ -474,6 +424,20 @@ static struct test_segment { 14, {NULL, NULL, 0, 0, 0, 0, 0, 0}, }, + { + /* 28 */ + "BGP_AS_ZERO", + "seq(8466,3,52737,0,4096)", + {0x2, 0x5, + 0x21, 0x12, + 0x00, 0x03, + 0xce, 0x01, + 0x00, 0x00, + 0x10, 0x00}, + 12, + {"8466 3 52737 0 4096", "8466 3 52737 0 4096", 5, 0, + NOT_ALL_PRIVATE, 4096, 4, 8466}, + }, {NULL, NULL, {0}, 0, {NULL, 0, 0}}}; #define COMMON_ATTRS \ @@ -653,7 +617,7 @@ static struct aspath_tests { &test_segments[6], NULL, AS4_DATA, - -1, + -2, PEER_CAP_AS4_ADV, { COMMON_ATTRS, @@ -678,6 +642,21 @@ static struct aspath_tests { COMMON_ATTR_SIZE + 3, &test_segments[0], }, + /* 13 */ + { + "4b AS4_PATH: BGP_AS_ZERO", + &test_segments[28], + "8466 3 52737 0 4096", + AS4_DATA, + -1, + PEER_CAP_AS4_RCV | PEER_CAP_AS4_ADV, + { + COMMON_ATTRS, + BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_OPTIONAL, + BGP_ATTR_AS4_PATH, 22, + }, + COMMON_ATTR_SIZE + 3, + }, {NULL, NULL, NULL, 0, 0, 0, {0}, 0}, }; @@ -710,10 +689,8 @@ static struct tests { /* 3 */ {&test_segments[4], &test_segments[5], - {"8467 59649 {4196,48658} {17322,30745} 6435 59408 21665" - " {2457,4369,61697} 1842 41590 51793", - "8467 59649 {4196,48658} {17322,30745} 6435 59408 21665" - " {2457,4369,61697} 1842 41590 51793", + {"8467 59649 {4196,48658} {17322,30745} 6435 59408 21665 {2457,4369,61697} 1842 41590 51793", + "8467 59649 {4196,48658} {17322,30745} 6435 59408 21665 {2457,4369,61697} 1842 41590 51793", 11, 0, NOT_ALL_PRIVATE, 61697, 1, 8467}}, /* 4 */ { @@ -748,59 +725,9 @@ static struct tests { { &test_segments[14], &test_segments[11], - {"8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 2 52737 4096 8722 4 8722", - - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " - "8466 2 52737 4096 8722 4 8722", + {"8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 2 52737 4096 8722 4 8722", + + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 2 52737 4096 8722 4 8722", 257, 0, NOT_ALL_PRIVATE, 4096, 1000, 8466}, }, {NULL, @@ -832,8 +759,7 @@ struct tests reconcile_tests[] = { { &test_segments[20], &test_segments[19], - {"(123 456 789) [124,456,788] 6435 59408 21665" - " {2457,4369,61697} 1842 41591 51793", + {"(123 456 789) [124,456,788] 6435 59408 21665 {2457,4369,61697} 1842 41591 51793", "6435 59408 21665 {2457,4369,61697} 1842 41591 51793", 7, 4, NOT_ALL_PRIVATE, 51793, 1, 6435}, }, @@ -1339,7 +1265,7 @@ int main(void) { int i = 0; qobj_init(); - bgp_master_init(thread_master_create(NULL)); + bgp_master_init(thread_master_create(NULL), BGP_SOCKET_SNDBUF_SIZE); master = bm->master; bgp_option_set(BGP_OPT_NO_LISTEN); bgp_attr_init(); @@ -1379,6 +1305,7 @@ int main(void) i = 0; + frr_pthread_init(); bgp_pthreads_init(); bgp_pth_ka->running = true; diff --git a/tests/bgpd/test_aspath.py b/tests/bgpd/test_aspath.py index 15ae514c87..5fa1f11629 100644 --- a/tests/bgpd/test_aspath.py +++ b/tests/bgpd/test_aspath.py @@ -52,6 +52,7 @@ def _attrtest(self, line): TestAspath.parsertest("zero-size overflow") TestAspath.parsertest("zero-size overflow + valid segment") TestAspath.parsertest("invalid segment type") +TestAspath.parsertest("BGP_AS_ZERO") for i in range(10): TestAspath.okfail("prepend test %d" % i) @@ -77,3 +78,4 @@ def _attrtest(self, line): TestAspath.attrtest("4b AS_PATH: bad flags") TestAspath.attrtest("4b AS4_PATH w/o AS_PATH") TestAspath.attrtest("4b AS4_PATH: confed") +TestAspath.attrtest("4b AS4_PATH: BGP_AS_ZERO") diff --git a/tests/bgpd/test_bgp_table.c b/tests/bgpd/test_bgp_table.c index 7b38df5f66..4eb132df55 100644 --- a/tests/bgpd/test_bgp_table.c +++ b/tests/bgpd/test_bgp_table.c @@ -53,15 +53,15 @@ static void add_node(struct bgp_table *table, const char *prefix_str) { struct prefix_ipv4 p; struct test_node_t *node; - struct bgp_node *rn; + struct bgp_dest *dest; assert(prefix_str); if (str2prefix_ipv4(prefix_str, &p) <= 0) assert(0); - rn = bgp_node_get(table, (struct prefix *)&p); - if (rn->info) { + dest = bgp_node_get(table, (struct prefix *)&p); + if (dest->info) { assert(0); return; } @@ -70,73 +70,67 @@ static void add_node(struct bgp_table *table, const char *prefix_str) assert(node); node->prefix_str = strdup(prefix_str); assert(node->prefix_str); - rn->info = node; + dest->info = node; } -static void print_range_result(struct list *list) +static bool prefix_in_array(const struct prefix *p, struct prefix *prefix_array, + size_t prefix_array_size) { - - struct listnode *listnode; - struct bgp_node *bnode; - - for (ALL_LIST_ELEMENTS_RO(list, listnode, bnode)) { - char buf[PREFIX2STR_BUFFER]; - - prefix2str(&bnode->p, buf, PREFIX2STR_BUFFER); - printf("%s\n", buf); + for (size_t i = 0; i < prefix_array_size; ++i) { + if (prefix_same(p, &prefix_array[i])) + return true; } + return false; } -static void check_lookup_result(struct list *list, va_list arglist) +static void check_lookup_result(struct bgp_dest *match, va_list arglist) { char *prefix_str; - unsigned int prefix_count = 0; + struct prefix *prefixes = NULL; + size_t prefix_count = 0; - printf("Searching results\n"); while ((prefix_str = va_arg(arglist, char *))) { - struct listnode *listnode; - struct bgp_node *bnode; - struct prefix p; - bool found = false; + ++prefix_count; + prefixes = realloc(prefixes, sizeof(*prefixes) * prefix_count); - prefix_count++; - printf("Searching for %s\n", prefix_str); - - if (str2prefix(prefix_str, &p) <= 0) + if (str2prefix(prefix_str, &prefixes[prefix_count - 1]) <= 0) assert(0); + } - for (ALL_LIST_ELEMENTS_RO(list, listnode, bnode)) { - if (prefix_same(&bnode->p, &p)) - found = true; - } + /* check if the result is empty and if it is allowd to be empty */ + assert((prefix_count == 0 && !match) || prefix_count > 0); + if (!match) + return; - assert(found); - } + struct bgp_dest *dest = match; + + while ((dest = bgp_route_next_until(dest, match))) { + const struct prefix *dest_p = bgp_dest_get_prefix(dest); - printf("Checking for unexpected result items\n"); - printf("Expecting %d found %d\n", prefix_count, listcount(list)); - assert(prefix_count == listcount(list)); + if (bgp_dest_has_bgp_path_info_data(dest) + && !prefix_in_array(dest_p, prefixes, prefix_count)) { + char buf[PREFIX2STR_BUFFER]; + + prefix2str(dest_p, buf, PREFIX2STR_BUFFER); + printf("prefix %s was not expected!\n", buf); + assert(0); + } + } } -static void do_test(struct bgp_table *table, const char *prefix, - uint32_t maxlen, ...) +static void do_test(struct bgp_table *table, const char *prefix, ...) { va_list arglist; - struct list *list = list_new(); struct prefix p; - list->del = (void (*)(void *))bgp_unlock_node; - va_start(arglist, maxlen); - printf("\nDoing lookup for %s-%d\n", prefix, maxlen); + va_start(arglist, prefix); + printf("\nDoing lookup for %s\n", prefix); if (str2prefix(prefix, &p) <= 0) assert(0); - bgp_table_range_lookup(table, &p, maxlen, list); - print_range_result(list); - - check_lookup_result(list, arglist); + struct bgp_dest *dest = bgp_table_subtree_lookup(table, &p); - list_delete(&list); + check_lookup_result(dest, arglist); va_end(arglist); @@ -163,27 +157,22 @@ static void test_range_lookup(void) for (int i = 0; i < num_prefixes; i++) add_node(table, prefixes[i]); - do_test(table, "1.16.0.0/17", 20, "1.16.64.0/19", "1.16.32.0/20", NULL); - do_test(table, "1.16.128.0/17", 20, "1.16.128.0/18", "1.16.192.0/18", - "1.16.160.0/19", NULL); - - do_test(table, "1.16.128.0/17", 20, "1.16.128.0/18", "1.16.192.0/18", + do_test(table, "1.16.0.0/17", "1.16.64.0/19", "1.16.32.0/20", + "1.16.32.0/20", "1.16.32.0/21", NULL); + do_test(table, "1.16.128.0/17", "1.16.128.0/18", "1.16.192.0/18", "1.16.160.0/19", NULL); - do_test(table, "1.16.0.0/16", 18, "1.16.0.0/16", "1.16.128.0/18", - "1.16.192.0/18", NULL); - - do_test(table, "1.16.0.0/16", 21, "1.16.0.0/16", "1.16.128.0/18", + do_test(table, "1.16.0.0/16", "1.16.0.0/16", "1.16.128.0/18", "1.16.192.0/18", "1.16.64.0/19", "1.16.160.0/19", "1.16.32.0/20", "1.16.32.0/21", NULL); - do_test(table, "1.17.0.0/16", 20, NULL); + do_test(table, "1.17.0.0/16", NULL); - do_test(table, "128.0.0.0/8", 16, NULL); + do_test(table, "128.0.0.0/8", NULL); - do_test(table, "16.0.0.0/8", 16, "16.0.0.0/16", NULL); + do_test(table, "16.0.0.0/8", "16.0.0.0/16", NULL); - do_test(table, "0.0.0.0/3", 21, "1.16.0.0/16", "1.16.128.0/18", + do_test(table, "0.0.0.0/2", "1.16.0.0/16", "1.16.128.0/18", "1.16.192.0/18", "1.16.64.0/19", "1.16.160.0/19", "1.16.32.0/20", "1.16.32.0/21", "16.0.0.0/16", NULL); } diff --git a/tests/bgpd/test_bgp_table.py b/tests/bgpd/test_bgp_table.py index 4423530fe0..53bd37233a 100644 --- a/tests/bgpd/test_bgp_table.py +++ b/tests/bgpd/test_bgp_table.py @@ -3,5 +3,5 @@ class TestTable(frrtest.TestMultiOut): program = './test_bgp_table' -for i in range(6): +for i in range(7): TestTable.onesimple('Checks successfull') diff --git a/tests/bgpd/test_capability.c b/tests/bgpd/test_capability.c index 968f9ac445..1b3f90434b 100644 --- a/tests/bgpd/test_capability.c +++ b/tests/bgpd/test_capability.c @@ -912,17 +912,18 @@ int main(void) qobj_init(); master = thread_master_create(NULL); - bgp_master_init(master); + bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE); vrf_init(NULL, NULL, NULL, NULL, NULL); bgp_option_set(BGP_OPT_NO_LISTEN); + frr_pthread_init(); bgp_pthreads_init(); bgp_pth_ka->running = true; if (fileno(stdout) >= 0) tty = isatty(fileno(stdout)); - if (bgp_get(&bgp, &asn, NULL, BGP_INSTANCE_TYPE_DEFAULT)) + if (bgp_get(&bgp, &asn, NULL, BGP_INSTANCE_TYPE_DEFAULT) < 0) return -1; peer = peer_create_accept(bgp); diff --git a/tests/bgpd/test_mp_attr.c b/tests/bgpd/test_mp_attr.c index 603b678cf1..7fabaad7fa 100644 --- a/tests/bgpd/test_mp_attr.c +++ b/tests/bgpd/test_mp_attr.c @@ -37,6 +37,7 @@ #include "bgpd/bgp_mplsvpn.h" #include "bgpd/bgp_nexthop.h" #include "bgpd/bgp_vty.h" +#include "bgpd/bgp_network.h" #define VT100_RESET "\x1b[0m" #define VT100_RED "\x1b[31m" @@ -950,12 +951,19 @@ static struct test_segment mp_prefix_sid[] = { "PREFIX-SID", "PREFIX-SID Test 1", { - 0x01, 0x00, 0x07, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x02, - 0x03, 0x00, 0x08, 0x00, - 0x00, 0x0a, 0x1b, 0xfe, - 0x00, 0x00, 0x0a + /* TLV[0] Latel-Index TLV */ + 0x01, /* Type 0x01:Label-Index */ + 0x00, 0x07, /* Length */ + 0x00, /* RESERVED */ + 0x00, 0x00, /* Flags */ + 0x00, 0x00, 0x00, 0x02, /* Label Index */ + + /* TLV[1] SRGB TLV */ + 0x03, /* Type 0x03:SRGB */ + 0x00, 0x08, /* Length */ + 0x00, 0x00, /* Flags */ + 0x0a, 0x1b, 0xfe, /* SRGB[0] first label */ + 0x00, 0x00, 0x0a /* SRBG[0] nb-labels in range */ }, .len = 21, .parses = SHOULD_PARSE, @@ -1026,7 +1034,7 @@ static void parse_test(struct peer *peer, struct test_segment *t, int type) parse_ret = bgp_mp_unreach_parse(&attr_args, &nlri); break; case BGP_ATTR_PREFIX_SID: - parse_ret = bgp_attr_prefix_sid(t->len, &attr_args, &nlri); + parse_ret = bgp_attr_prefix_sid(&attr_args); break; default: printf("unknown type"); @@ -1078,7 +1086,7 @@ int main(void) cmd_init(0); bgp_vty_init(); master = thread_master_create("test mp attr"); - bgp_master_init(master); + bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE); vrf_init(NULL, NULL, NULL, NULL, NULL); bgp_option_set(BGP_OPT_NO_LISTEN); bgp_attr_init(); @@ -1086,7 +1094,7 @@ int main(void) if (fileno(stdout) >= 0) tty = isatty(fileno(stdout)); - if (bgp_get(&bgp, &asn, NULL, BGP_INSTANCE_TYPE_DEFAULT)) + if (bgp_get(&bgp, &asn, NULL, BGP_INSTANCE_TYPE_DEFAULT) < 0) return -1; peer = peer_create_accept(bgp); diff --git a/tests/bgpd/test_mpath.c b/tests/bgpd/test_mpath.c index 0ecd0fdfec..99fb7b620d 100644 --- a/tests/bgpd/test_mpath.c +++ b/tests/bgpd/test_mpath.c @@ -38,6 +38,7 @@ #include "bgpd/bgp_nexthop.h" #include "bgpd/bgp_mpath.h" #include "bgpd/bgp_evpn.h" +#include "bgpd/bgp_network.h" #define VT100_RESET "\x1b[0m" #define VT100_RED "\x1b[31m" @@ -51,8 +52,8 @@ #define EXPECT_TRUE(expr, res) \ if (!(expr)) { \ - printf("Test failure in %s line %u: %s\n", __FUNCTION__, \ - __LINE__, #expr); \ + printf("Test failure in %s line %u: %s\n", __func__, __LINE__, \ + #expr); \ (res) = TEST_FAILED; \ } @@ -74,7 +75,7 @@ struct testcase_t__ { /* need these to link in libbgp */ struct thread_master *master = NULL; -struct zclient *zclient; +extern struct zclient *zclient; struct zebra_privs_t bgpd_privs = { .user = NULL, .group = NULL, @@ -296,7 +297,25 @@ struct bgp_node test_rn; static int setup_bgp_path_info_mpath_update(testcase_t *t) { int i; + struct bgp *bgp; + struct bgp_table *rt; + struct route_node *rt_node; + as_t asn = 1; + + t->tmp_data = bgp_create_fake(&asn, NULL); + if (!t->tmp_data) + return -1; + + bgp = t->tmp_data; + rt = bgp->rib[AFI_IP][SAFI_UNICAST]; + + if (!rt) + return -1; + str2prefix("42.1.1.0/24", &test_rn.p); + rt_node = bgp_dest_to_rnode(&test_rn); + memcpy((struct route_table *)&rt_node->table, &rt->route_table, + sizeof(struct route_table *)); setup_bgp_mp_list(t); for (i = 0; i < test_mp_list_info_count; i++) bgp_path_info_add(&test_rn, &test_mp_list_info[i]); @@ -351,7 +370,7 @@ static int cleanup_bgp_path_info_mpath_update(testcase_t *t) for (i = 0; i < test_mp_list_peer_count; i++) sockunion_free(test_mp_list_peer[i].su_remote); - return 0; + return bgp_delete((struct bgp *)t->tmp_data); } testcase_t test_bgp_path_info_mpath_update = { @@ -379,7 +398,7 @@ static int global_test_init(void) qobj_init(); master = thread_master_create(NULL); zclient = zclient_new(master, &zclient_options_default); - bgp_master_init(master); + bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE); vrf_init(NULL, NULL, NULL, NULL, NULL); bgp_option_set(BGP_OPT_NO_LISTEN); diff --git a/tests/bgpd/test_packet.c b/tests/bgpd/test_packet.c index 9719aceec9..d2c093fbea 100644 --- a/tests/bgpd/test_packet.c +++ b/tests/bgpd/test_packet.c @@ -34,6 +34,7 @@ #include "bgpd/bgp_debug.h" #include "bgpd/bgp_packet.h" #include "bgpd/bgp_aspath.h" +#include "bgpd/bgp_network.h" /* need these to link in libbgp */ struct zebra_privs_t *bgpd_privs = NULL; @@ -58,11 +59,11 @@ int main(int argc, char *argv[]) qobj_init(); bgp_attr_init(); master = thread_master_create(NULL); - bgp_master_init(master); + bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE); vrf_init(NULL, NULL, NULL, NULL, NULL); bgp_option_set(BGP_OPT_NO_LISTEN); - if (bgp_get(&bgp, &asn, NULL, BGP_INSTANCE_TYPE_DEFAULT)) + if (bgp_get(&bgp, &asn, NULL, BGP_INSTANCE_TYPE_DEFAULT) < 0) return -1; peer = peer_create_accept(bgp); diff --git a/tests/bgpd/test_peer_attr.c b/tests/bgpd/test_peer_attr.c index 78016dc9ce..0979622eb3 100644 --- a/tests/bgpd/test_peer_attr.c +++ b/tests/bgpd/test_peer_attr.c @@ -29,6 +29,7 @@ #include "bgpd/bgp_route.h" #include "bgpd/bgp_vty.h" #include "bgpd/bgp_zebra.h" +#include "bgpd/bgp_network.h" #ifdef ENABLE_BGP_VNC #include "bgpd/rfapi/rfapi_backend.h" @@ -1170,7 +1171,7 @@ static void test_peer_attr(struct test *test, struct test_peer_attr *pa) /* Test Preparation: Switch and activate address-family. */ if (!is_attr_type_global(pa->type)) { test_log(test, "prepare: switch address-family to [%s]", - afi_safi_print(pa->afi, pa->safi)); + get_afi_safi_str(pa->afi, pa->safi, false)); test_execute(test, "address-family %s %s", str_from_afi(pa->afi), str_from_safi(pa->safi)); test_execute(test, "neighbor %s activate", g->name); @@ -1237,7 +1238,7 @@ static void test_peer_attr(struct test *test, struct test_peer_attr *pa) /* Test Preparation: Switch and activate address-family. */ if (!is_attr_type_global(pa->type)) { test_log(test, "prepare: switch address-family to [%s]", - afi_safi_print(pa->afi, pa->safi)); + get_afi_safi_str(pa->afi, pa->safi, false)); test_execute(test, "address-family %s %s", str_from_afi(pa->afi), str_from_safi(pa->safi)); test_execute(test, "neighbor %s activate", g->name); @@ -1285,7 +1286,7 @@ static void test_peer_attr(struct test *test, struct test_peer_attr *pa) /* Test Preparation: Switch and activate address-family. */ if (!is_attr_type_global(pa->type)) { test_log(test, "prepare: switch address-family to [%s]", - afi_safi_print(pa->afi, pa->safi)); + get_afi_safi_str(pa->afi, pa->safi, false)); test_execute(test, "address-family %s %s", str_from_afi(pa->afi), str_from_safi(pa->safi)); test_execute(test, "neighbor %s activate", g->name); @@ -1380,17 +1381,17 @@ static void test_peer_attr(struct test *test, struct test_peer_attr *pa) static void bgp_startup(void) { cmd_init(1); - openzlog("testbgpd", "NONE", 0, LOG_CONS | LOG_NDELAY | LOG_PID, - LOG_DAEMON); + zlog_aux_init("NONE: ", LOG_DEBUG); zprivs_preinit(&bgpd_privs); zprivs_init(&bgpd_privs); master = thread_master_create(NULL); - yang_init(); - nb_init(master, NULL, 0); - bgp_master_init(master); + yang_init(true); + nb_init(master, NULL, 0, false); + bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE); bgp_option_set(BGP_OPT_NO_LISTEN); vrf_init(NULL, NULL, NULL, NULL, NULL); + frr_pthread_init(); bgp_init(0); bgp_pthreads_run(); } @@ -1436,7 +1437,6 @@ static void bgp_shutdown(void) zprivs_terminate(&bgpd_privs); thread_master_free(master); master = NULL; - closezlog(); } int main(void) diff --git a/tests/helpers/c/main.c b/tests/helpers/c/main.c index 11db2dabc3..7f15996daa 100644 --- a/tests/helpers/c/main.c +++ b/tests/helpers/c/main.c @@ -24,7 +24,7 @@ #include "vty.h" #include "command.h" #include "memory.h" -#include "memory_vty.h" +#include "lib_vty.h" extern void test_init(void); @@ -153,10 +153,10 @@ int main(int argc, char **argv) /* Library inits. */ cmd_init(1); - vty_init(master); - memory_init(); - yang_init(); - nb_init(master, NULL, 0); + vty_init(master, false); + lib_cmd_init(); + yang_init(true); + nb_init(master, NULL, 0, false); /* OSPF vty inits. */ test_vty_init(); diff --git a/tests/isisd/test_common.c b/tests/isisd/test_common.c new file mode 100644 index 0000000000..536847a1da --- /dev/null +++ b/tests/isisd/test_common.c @@ -0,0 +1,331 @@ +/* + * Copyright (C) 2020 NetDEF, Inc. + * Renato Westphal + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "isisd/isisd.h" +#include "isisd/isis_dynhn.h" +#include "isisd/isis_mt.h" + +#include "test_common.h" + +struct thread_master *master; +struct zebra_privs_t isisd_privs; + +int isis_sock_init(struct isis_circuit *circuit) +{ + return 0; +} + +const struct isis_test_node * +test_topology_find_node(const struct isis_topology *topology, + const char *hostname, uint8_t pseudonode_id) +{ + for (size_t i = 0; topology->nodes[i].hostname[0]; i++) + if (strmatch(hostname, topology->nodes[i].hostname) + && pseudonode_id == topology->nodes[i].pseudonode_id) + return &topology->nodes[i]; + + return NULL; +} + +const struct isis_topology * +test_topology_find(struct isis_topology *test_topologies, uint16_t number) +{ + for (size_t i = 0; test_topologies[i].number; i++) + if (test_topologies[i].number == number) + return &test_topologies[i]; + + return NULL; +} + +static const struct isis_test_node * +test_find_adjacency(const struct isis_test_node *tnode, const char *hostname) +{ + for (size_t i = 0; tnode->adjacencies[i].hostname[0]; i++) { + const struct isis_test_adj *tadj; + + tadj = &tnode->adjacencies[i]; + if (strmatch(hostname, tadj->hostname)) + return tnode; + } + + return NULL; +} + +static struct isis_lsp *lsp_add(struct lspdb_head *lspdb, + struct isis_area *area, int level, + const uint8_t *sysid, uint8_t pseudonode_id) +{ + struct isis_lsp *lsp; + uint8_t lspid[ISIS_SYS_ID_LEN + 2]; + + memcpy(lspid, sysid, ISIS_SYS_ID_LEN); + LSP_PSEUDO_ID(lspid) = pseudonode_id; + LSP_FRAGMENT(lspid) = 0; + + lsp = lsp_new(area, lspid, 6000, 1, 0, 0, NULL, level); + lsp->tlvs = isis_alloc_tlvs(); + lspdb_add(lspdb, lsp); + + return lsp; +} + +static void lsp_add_ip_reach(struct isis_lsp *lsp, + const struct isis_test_node *tnode, + const char *prefix_str, uint32_t *next_sid_index) +{ + struct prefix prefix; + struct sr_prefix_cfg pcfg = {}; + struct sr_prefix_cfg *pcfg_p = NULL; + + if (str2prefix(prefix_str, &prefix) != 1) { + zlog_debug("%s: invalid network: %s", __func__, prefix_str); + return; + } + + if (CHECK_FLAG(tnode->flags, F_ISIS_TEST_NODE_SR)) { + pcfg_p = &pcfg; + + pcfg.sid = *next_sid_index; + *next_sid_index = *next_sid_index + 1; + pcfg.sid_type = SR_SID_VALUE_TYPE_INDEX; + pcfg.last_hop_behavior = SR_LAST_HOP_BEHAVIOR_PHP; + } + + if (prefix.family == AF_INET) + isis_tlvs_add_extended_ip_reach(lsp->tlvs, + (struct prefix_ipv4 *)&prefix, + 10, false, pcfg_p); + else + isis_tlvs_add_ipv6_reach(lsp->tlvs, ISIS_MT_IPV6_UNICAST, + (struct prefix_ipv6 *)&prefix, 10, + false, pcfg_p); +} + +static void lsp_add_reach(struct isis_lsp *lsp, + const struct isis_test_node *tnode, + const uint8_t *ne_id, uint8_t pseudonode_id, + uint32_t metric, int family, mpls_label_t *next_label) +{ + uint8_t nodeid[ISIS_SYS_ID_LEN + 1]; + uint16_t mtid; + struct isis_ext_subtlvs *ext = NULL; + + memcpy(nodeid, ne_id, ISIS_SYS_ID_LEN); + LSP_PSEUDO_ID(nodeid) = pseudonode_id; + + if (CHECK_FLAG(tnode->flags, F_ISIS_TEST_NODE_SR)) { + struct isis_adj_sid *adj_sid; + + adj_sid = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*adj_sid)); + adj_sid->family = family; + SET_FLAG(adj_sid->flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG); + SET_FLAG(adj_sid->flags, EXT_SUBTLV_LINK_ADJ_SID_LFLG); + if (family == AF_INET6) + SET_FLAG(adj_sid->flags, EXT_SUBTLV_LINK_ADJ_SID_FFLG); + adj_sid->weight = 0; + adj_sid->sid = *next_label; + *next_label = *next_label + 1; + + ext = isis_alloc_ext_subtlvs(); + isis_tlvs_add_adj_sid(ext, adj_sid); + } + + mtid = (family == AF_INET) ? ISIS_MT_IPV4_UNICAST + : ISIS_MT_IPV6_UNICAST; + + isis_tlvs_add_extended_reach(lsp->tlvs, mtid, nodeid, metric, ext); +} + +static void lsp_add_router_capability(struct isis_lsp *lsp, + const struct isis_test_node *tnode) +{ + struct isis_router_cap cap = {}; + + if (!tnode->router_id) + return; + + if (inet_pton(AF_INET, tnode->router_id, &cap.router_id) != 1) { + zlog_debug("%s: invalid router-id: %s", __func__, + tnode->router_id); + return; + } + + if (CHECK_FLAG(tnode->flags, F_ISIS_TEST_NODE_SR)) { + cap.srgb.flags = + ISIS_SUBTLV_SRGB_FLAG_I | ISIS_SUBTLV_SRGB_FLAG_V; + cap.srgb.lower_bound = tnode->srgb.lower_bound + ? tnode->srgb.lower_bound + : SRGB_DFTL_LOWER_BOUND; + cap.srgb.range_size = tnode->srgb.range_size + ? tnode->srgb.range_size + : SRGB_DFTL_RANGE_SIZE; + cap.algo[0] = SR_ALGORITHM_SPF; + cap.algo[1] = SR_ALGORITHM_UNSET; + } + + isis_tlvs_set_router_capability(lsp->tlvs, &cap); +} + +static void lsp_add_mt_router_info(struct isis_lsp *lsp, + const struct isis_test_node *tnode) +{ + if (tnode->protocols.ipv4) + isis_tlvs_add_mt_router_info(lsp->tlvs, ISIS_MT_IPV4_UNICAST, 0, + false); + if (tnode->protocols.ipv6) + isis_tlvs_add_mt_router_info(lsp->tlvs, ISIS_MT_IPV6_UNICAST, 0, + false); +} + +static void lsp_add_protocols_supported(struct isis_lsp *lsp, + const struct isis_test_node *tnode) +{ + struct nlpids nlpids = {}; + + if (!tnode->protocols.ipv4 && !tnode->protocols.ipv6) + return; + + if (tnode->protocols.ipv4) { + nlpids.nlpids[nlpids.count] = NLPID_IP; + nlpids.count++; + } + if (tnode->protocols.ipv6) { + nlpids.nlpids[nlpids.count] = NLPID_IPV6; + nlpids.count++; + } + isis_tlvs_set_protocols_supported(lsp->tlvs, &nlpids); +} + +static int topology_load_node_level(const struct isis_topology *topology, + const struct isis_test_node *tnode, + size_t tnode_index, struct isis_area *area, + struct lspdb_head *lspdb, int level) +{ + struct isis_lsp *lsp; + uint32_t next_sid_index = (tnode_index + 1) * 10; + mpls_label_t next_label = 16; + + lsp = lsp_add(lspdb, area, level, tnode->sysid, tnode->pseudonode_id); + lsp_add_mt_router_info(lsp, tnode); + lsp_add_protocols_supported(lsp, tnode); + lsp_add_router_capability(lsp, tnode); + + /* Add IP Reachability Information. */ + for (size_t i = 0; tnode->networks[i]; i++) { + if (i > MAX_NETWORKS) { + zlog_debug( + "%s: node has too many networks (maximum is %u)", + __func__, MAX_NETWORKS); + return -1; + } + lsp_add_ip_reach(lsp, tnode, tnode->networks[i], + &next_sid_index); + } + + /* Add IS Reachability Information. */ + for (size_t i = 0; tnode->adjacencies[i].hostname[0]; i++) { + const struct isis_test_adj *tadj; + const struct isis_test_node *tadj_node; + + if (i > MAX_ADJACENCIES) { + zlog_debug( + "%s: node has too many adjacencies (maximum is %u)", + __func__, MAX_ADJACENCIES); + return -1; + } + + tadj = &tnode->adjacencies[i]; + tadj_node = test_topology_find_node(topology, tadj->hostname, + tadj->pseudonode_id); + if (!tadj_node) { + zlog_debug( + "%s: node \"%s\" has an adjacency with non-existing node \"%s\"", + __func__, tnode->hostname, tadj->hostname); + return -1; + } + if (!test_find_adjacency(tadj_node, tnode->hostname)) { + zlog_debug( + "%s: node \"%s\" has an one-way adjacency with node \"%s\"", + __func__, tnode->hostname, tadj->hostname); + return -1; + } + + if (tnode->pseudonode_id || tadj_node->pseudonode_id + || (tnode->protocols.ipv4 && tadj_node->protocols.ipv4)) + lsp_add_reach(lsp, tnode, tadj_node->sysid, + tadj_node->pseudonode_id, tadj->metric, + AF_INET, &next_label); + if (tadj_node->pseudonode_id + || (tnode->protocols.ipv6 && tadj_node->protocols.ipv6)) + lsp_add_reach(lsp, tnode, tadj_node->sysid, + tadj_node->pseudonode_id, tadj->metric, + AF_INET6, &next_label); + } + + return 0; +} + +static int topology_load_node(const struct isis_topology *topology, + const struct isis_test_node *tnode, + size_t tnode_index, struct isis_area *area, + struct lspdb_head lspdb[]) +{ + int ret; + + isis_dynhn_insert(tnode->sysid, tnode->hostname, tnode->level); + + for (int level = IS_LEVEL_1; level <= IS_LEVEL_2; level++) { + if ((tnode->level & level) == 0) + continue; + + ret = topology_load_node_level(topology, tnode, tnode_index, + area, &lspdb[level - 1], level); + if (ret != 0) + return ret; + } + + return 0; +} + +int test_topology_load(const struct isis_topology *topology, + struct isis_area *area, struct lspdb_head lspdb[]) +{ + for (int level = IS_LEVEL_1; level <= IS_LEVEL_2; level++) + lsp_db_init(&lspdb[level - 1]); + + for (size_t i = 0; topology->nodes[i].hostname[0]; i++) { + const struct isis_test_node *tnode = &topology->nodes[i]; + int ret; + + if (i > MAX_NODES) { + zlog_debug( + "%s: topology has too many nodes (maximum is %u)", + __func__, MAX_NODES); + return -1; + } + + ret = topology_load_node(topology, tnode, i, area, lspdb); + if (ret != 0) + return ret; + } + + return 0; +} diff --git a/tests/isisd/test_common.h b/tests/isisd/test_common.h new file mode 100644 index 0000000000..6fd0d3813e --- /dev/null +++ b/tests/isisd/test_common.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2020 NetDEF, Inc. + * Renato Westphal + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _COMMON_ISIS_H +#define _COMMON_ISIS_H + +#include "isisd/isisd.h" +#include "isisd/isis_spf.h" +#include "isisd/isis_spf_private.h" + +#define MAX_HOSTNAME 16 +#define MAX_NETWORKS 8 +#define MAX_ADJACENCIES 8 +#define MAX_NODES 12 + +#define SRGB_DFTL_LOWER_BOUND 16000 +#define SRGB_DFTL_RANGE_SIZE 8000 + +struct isis_test_adj { + char hostname[MAX_HOSTNAME]; + uint8_t pseudonode_id; + uint32_t metric; +}; + +struct isis_test_node { + char hostname[MAX_HOSTNAME]; + uint8_t sysid[ISIS_SYS_ID_LEN]; + uint8_t pseudonode_id; + int level; + struct { + bool ipv4; + bool ipv6; + } protocols; + const char *router_id; + struct { + uint32_t lower_bound; + uint32_t range_size; + } srgb; + const char *networks[MAX_NETWORKS + 1]; + struct isis_test_adj adjacencies[MAX_ADJACENCIES + 1]; + uint8_t flags; +}; +#define F_ISIS_TEST_NODE_SR 0x01 + +struct isis_topology { + uint16_t number; + struct isis_test_node nodes[MAX_NODES + 1]; +}; + +/* Prototypes. */ +extern int isis_sock_init(struct isis_circuit *circuit); +extern const struct isis_test_node * +test_topology_find_node(const struct isis_topology *topology, + const char *hostname, uint8_t pseudonode_id); +extern const struct isis_topology * +test_topology_find(struct isis_topology *test_topologies, uint16_t number); +extern int test_topology_load(const struct isis_topology *topology, + struct isis_area *area, + struct lspdb_head lspdb[]); + +/* Global variables. */ +extern struct thread_master *master; +extern struct zebra_privs_t isisd_privs; +extern struct isis_topology test_topologies[]; + +#endif /* _COMMON_ISIS_H */ diff --git a/tests/isisd/test_fuzz_isis_tlv.c b/tests/isisd/test_fuzz_isis_tlv.c index 917ea66f13..97aade6578 100644 --- a/tests/isisd/test_fuzz_isis_tlv.c +++ b/tests/isisd/test_fuzz_isis_tlv.c @@ -14,16 +14,9 @@ #include "isisd/isis_circuit.h" #include "isisd/isis_tlvs.h" -#define TEST_STREAM_SIZE 1500 - -struct thread_master *master; -int isis_sock_init(struct isis_circuit *circuit); -int isis_sock_init(struct isis_circuit *circuit) -{ - return 0; -} +#include "test_common.h" -struct zebra_privs_t isisd_privs; +#define TEST_STREAM_SIZE 1500 static bool atexit_registered; diff --git a/tests/isisd/test_fuzz_isis_tlv_tests.h.gz b/tests/isisd/test_fuzz_isis_tlv_tests.h.gz index 4a89bda84e..4f59d1d7c0 100644 Binary files a/tests/isisd/test_fuzz_isis_tlv_tests.h.gz and b/tests/isisd/test_fuzz_isis_tlv_tests.h.gz differ diff --git a/tests/isisd/test_isis_lspdb.c b/tests/isisd/test_isis_lspdb.c index f0baa482c7..244922ea4e 100644 --- a/tests/isisd/test_isis_lspdb.c +++ b/tests/isisd/test_isis_lspdb.c @@ -2,15 +2,7 @@ #include "isisd/isis_lsp.c" -struct thread_master *master; - -int isis_sock_init(struct isis_circuit *circuit); -int isis_sock_init(struct isis_circuit *circuit) -{ - return 0; -} - -struct zebra_privs_t isisd_privs; +#include "test_common.h" static void test_lsp_build_list_nonzero_ht(void) { @@ -82,6 +74,7 @@ static void test_lsp_build_list_nonzero_ht(void) int main(int argc, char **argv) { + struct isis *isis = NULL; isis = calloc(sizeof(*isis), 1); test_lsp_build_list_nonzero_ht(); return 0; diff --git a/tests/isisd/test_isis_spf.c b/tests/isisd/test_isis_spf.c new file mode 100644 index 0000000000..73bb531dc0 --- /dev/null +++ b/tests/isisd/test_isis_spf.c @@ -0,0 +1,313 @@ +/* + * Copyright (C) 2020 NetDEF, Inc. + * Renato Westphal + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include +#include "getopt.h" +#include "thread.h" +#include "vty.h" +#include "command.h" +#include "log.h" +#include "vrf.h" +#include "yang.h" + +#include "isisd/isisd.h" +#include "isisd/isis_dynhn.h" +#include "isisd/isis_misc.h" +#include "isisd/isis_spf.h" +#include "isisd/isis_spf_private.h" + +#include "test_common.h" + +enum test_type { + TEST_SPF = 1, + TEST_REVERSE_SPF, +}; + +#define F_DISPLAY_LSPDB 0x01 +#define F_IPV4_ONLY 0x02 +#define F_IPV6_ONLY 0x04 +#define F_LEVEL1_ONLY 0x08 +#define F_LEVEL2_ONLY 0x10 + +static struct isis *isis; + +static void test_run_spf(struct vty *vty, const struct isis_topology *topology, + const struct isis_test_node *root, + struct isis_area *area, struct lspdb_head *lspdb, + int level, int tree, bool reverse) +{ + struct isis_spftree *spftree; + enum spf_type spf_type; + + /* Run SPF. */ + spf_type = reverse ? SPF_TYPE_REVERSE : SPF_TYPE_FORWARD; + spftree = isis_spftree_new(area, lspdb, root->sysid, level, tree, + spf_type, F_SPFTREE_NO_ADJACENCIES); + isis_run_spf(spftree); + + /* Print the SPT and the corresponding routing table. */ + isis_print_spftree(vty, spftree); + isis_print_routes(vty, spftree); + + /* Cleanup SPF tree. */ + isis_spftree_del(spftree); +} + +static int test_run(struct vty *vty, const struct isis_topology *topology, + const struct isis_test_node *root, enum test_type test_type, + uint8_t flags) +{ + struct isis_area *area; + + /* Init topology. */ + memcpy(isis->sysid, root->sysid, sizeof(isis->sysid)); + area = isis_area_create("1", NULL); + area->is_type = IS_LEVEL_1_AND_2; + area->srdb.enabled = true; + if (test_topology_load(topology, area, area->lspdb) != 0) { + vty_out(vty, "%% Failed to load topology\n"); + return CMD_WARNING; + } + + for (int level = IS_LEVEL_1; level <= IS_LEVEL_2; level++) { + if (level == IS_LEVEL_1 && CHECK_FLAG(flags, F_LEVEL2_ONLY)) + continue; + if (level == IS_LEVEL_2 && CHECK_FLAG(flags, F_LEVEL1_ONLY)) + continue; + if ((root->level & level) == 0) + continue; + + /* Print the LDPDB. */ + if (CHECK_FLAG(flags, F_DISPLAY_LSPDB)) + show_isis_database_lspdb(vty, area, level - 1, + &area->lspdb[level - 1], NULL, + ISIS_UI_LEVEL_DETAIL); + + for (int tree = SPFTREE_IPV4; tree <= SPFTREE_IPV6; tree++) { + if (tree == SPFTREE_IPV4 + && CHECK_FLAG(flags, F_IPV6_ONLY)) + continue; + if (tree == SPFTREE_IPV6 + && CHECK_FLAG(flags, F_IPV4_ONLY)) + continue; + + switch (test_type) { + case TEST_SPF: + test_run_spf(vty, topology, root, area, + &area->lspdb[level - 1], level, + tree, false); + break; + case TEST_REVERSE_SPF: + test_run_spf(vty, topology, root, area, + &area->lspdb[level - 1], level, + tree, true); + break; + } + } + } + + /* Cleanup IS-IS area. */ + isis_area_destroy(area); + + /* Cleanup hostnames. */ + dyn_cache_cleanup_all(); + + return CMD_SUCCESS; +} + +DEFUN(test_isis, test_isis_cmd, + "test isis topology (1-13) root HOSTNAME\ + <\ + spf\ + |reverse-spf\ + >\ + [display-lspdb] [] []", + "Test command\n" + "IS-IS routing protocol\n" + "Test topology\n" + "Test topology number\n" + "SPF root\n" + "SPF root hostname\n" + "Normal Shortest Path First\n" + "Reverse Shortest Path First\n" + "Display the LSPDB\n" + "Do IPv4 processing only\n" + "Do IPv6 processing only\n" + "Skip L2 LSPs\n" + "Skip L1 LSPs\n") +{ + uint16_t topology_number; + const struct isis_topology *topology; + const struct isis_test_node *root; + enum test_type test_type; + uint8_t flags = 0; + int idx = 0; + + /* Load topology. */ + argv_find(argv, argc, "topology", &idx); + topology_number = atoi(argv[idx + 1]->arg); + topology = test_topology_find(test_topologies, topology_number); + if (!topology) { + vty_out(vty, "%% Topology \"%s\" not found\n", + argv[idx + 1]->arg); + return CMD_WARNING; + } + + /* Find root node. */ + argv_find(argv, argc, "root", &idx); + root = test_topology_find_node(topology, argv[idx + 1]->arg, 0); + if (!root) { + vty_out(vty, "%% Node \"%s\" not found\n", argv[idx + 1]->arg); + return CMD_WARNING; + } + + /* Parse test information. */ + if (argv_find(argv, argc, "spf", &idx)) + test_type = TEST_SPF; + else if (argv_find(argv, argc, "reverse-spf", &idx)) + test_type = TEST_REVERSE_SPF; + else + return CMD_WARNING; + + /* Parse control flags. */ + if (argv_find(argv, argc, "display-lspdb", &idx)) + SET_FLAG(flags, F_DISPLAY_LSPDB); + if (argv_find(argv, argc, "ipv4-only", &idx)) + SET_FLAG(flags, F_IPV4_ONLY); + else if (argv_find(argv, argc, "ipv6-only", &idx)) + SET_FLAG(flags, F_IPV6_ONLY); + if (argv_find(argv, argc, "level-1-only", &idx)) + SET_FLAG(flags, F_LEVEL1_ONLY); + else if (argv_find(argv, argc, "level-2-only", &idx)) + SET_FLAG(flags, F_LEVEL2_ONLY); + + return test_run(vty, topology, root, test_type, flags); +} + +static void vty_do_exit(int isexit) +{ + printf("\nend.\n"); + + isis_finish(isis); + cmd_terminate(); + vty_terminate(); + yang_terminate(); + thread_master_free(master); + + log_memstats(stderr, "test-isis-spf"); + if (!isexit) + exit(0); +} + +struct option longopts[] = {{"help", no_argument, NULL, 'h'}, + {"debug", no_argument, NULL, 'd'}, + {0}}; + +/* Help information display. */ +static void usage(char *progname, int status) +{ + if (status != 0) + fprintf(stderr, "Try `%s --help' for more information.\n", + progname); + else { + printf("Usage : %s [OPTION...]\n\ +isisd SPF test program.\n\n\ +-u, --debug Enable debugging\n\ +-h, --help Display this help and exit\n\ +\n\ +Report bugs to %s\n", + progname, FRR_BUG_ADDRESS); + } + exit(status); +} + +int main(int argc, char **argv) +{ + char *p; + char *progname; + struct thread thread; + bool debug = false; + + /* Set umask before anything for security */ + umask(0027); + + /* get program name */ + progname = ((p = strrchr(argv[0], '/')) ? ++p : argv[0]); + + while (1) { + int opt; + + opt = getopt_long(argc, argv, "hd", longopts, 0); + + if (opt == EOF) + break; + + switch (opt) { + case 0: + break; + case 'd': + debug = true; + break; + case 'h': + usage(progname, 0); + break; + default: + usage(progname, 1); + break; + } + } + + /* master init. */ + master = thread_master_create(NULL); + isis_master_init(master); + + /* Library inits. */ + cmd_init(1); + cmd_hostname_set("test"); + vty_init(master, false); + yang_init(true); + if (debug) + zlog_aux_init("NONE: ", LOG_DEBUG); + else + zlog_aux_init("NONE: ", ZLOG_DISABLED); + + /* IS-IS inits. */ + yang_module_load("frr-isisd"); + isis = isis_new(VRF_DEFAULT_NAME); + listnode_add(im->isis, isis); + SET_FLAG(im->options, F_ISIS_UNIT_TEST); + debug_spf_events |= DEBUG_SPF_EVENTS; + debug_events |= DEBUG_EVENTS; + debug_rte_events |= DEBUG_RTE_EVENTS; + + /* Install test command. */ + install_element(VIEW_NODE, &test_isis_cmd); + + /* Read input from .in file. */ + vty_stdio(vty_do_exit); + + /* Fetch next active thread. */ + while (thread_fetch(master, &thread)) + thread_call(&thread); + + /* Not reached. */ + exit(0); +} diff --git a/tests/isisd/test_isis_spf.in b/tests/isisd/test_isis_spf.in new file mode 100644 index 0000000000..d9a61782e9 --- /dev/null +++ b/tests/isisd/test_isis_spf.in @@ -0,0 +1,16 @@ +test isis topology 1 root rt1 spf +test isis topology 2 root rt1 spf +test isis topology 3 root rt1 spf ipv4-only +test isis topology 4 root rt1 spf ipv4-only +test isis topology 5 root rt1 spf ipv4-only +test isis topology 6 root rt1 spf ipv4-only +test isis topology 7 root rt1 spf ipv4-only +test isis topology 8 root rt1 spf ipv4-only +test isis topology 9 root rt1 spf +test isis topology 10 root rt1 spf +test isis topology 11 root rt1 spf +test isis topology 12 root rt1 spf ipv4-only +test isis topology 13 root rt1 spf ipv4-only + +test isis topology 4 root rt1 reverse-spf ipv4-only +test isis topology 11 root rt1 reverse-spf diff --git a/tests/isisd/test_isis_spf.py b/tests/isisd/test_isis_spf.py new file mode 100644 index 0000000000..21e7ef6269 --- /dev/null +++ b/tests/isisd/test_isis_spf.py @@ -0,0 +1,4 @@ +import frrtest + +class TestIsisSPF(frrtest.TestRefOut): + program = './test_isis_spf' diff --git a/tests/isisd/test_isis_spf.refout b/tests/isisd/test_isis_spf.refout new file mode 100644 index 0000000000..ed0569947c --- /dev/null +++ b/tests/isisd/test_isis_spf.refout @@ -0,0 +1,703 @@ +test# test isis topology 1 root rt1 spf +IS-IS paths to level-1 routers that speak IP +Vertex Type Metric Next-Hop Interface Parent +rt1 +10.0.255.1/32 IP internal 0 rt1(4) +rt2 TE-IS 10 rt2 - rt1(4) +rt3 TE-IS 10 rt3 - rt1(4) +rt4 TE-IS 20 rt2 - rt2(4) +rt5 TE-IS 20 rt3 - rt3(4) +10.0.255.2/32 IP TE 20 rt2 - rt2(4) +10.0.255.3/32 IP TE 20 rt3 - rt3(4) +rt6 TE-IS 30 rt2 - rt4(4) + rt3 - rt5(4) +10.0.255.4/32 IP TE 30 rt2 - rt4(4) +10.0.255.5/32 IP TE 30 rt3 - rt5(4) +10.0.255.6/32 IP TE 40 rt2 - rt6(4) + rt3 - + +IS-IS L1 IPv4 routing table: + + Prefix Metric Interface Nexthop Label(s) + ----------------------------------------------------- + 10.0.255.2/32 20 - rt2 - + 10.0.255.3/32 20 - rt3 - + 10.0.255.4/32 30 - rt2 - + 10.0.255.5/32 30 - rt3 - + 10.0.255.6/32 40 - rt2 - + - rt3 - + +IS-IS paths to level-1 routers that speak IPv6 +Vertex Type Metric Next-Hop Interface Parent +rt1 +2001:db8::1/128 IP6 internal 0 rt1(4) +rt2 TE-IS 10 rt2 - rt1(4) +rt3 TE-IS 10 rt3 - rt1(4) +rt4 TE-IS 20 rt2 - rt2(4) +rt5 TE-IS 20 rt3 - rt3(4) +2001:db8::2/128 IP6 internal 20 rt2 - rt2(4) +2001:db8::3/128 IP6 internal 20 rt3 - rt3(4) +rt6 TE-IS 30 rt2 - rt4(4) + rt3 - rt5(4) +2001:db8::4/128 IP6 internal 30 rt2 - rt4(4) +2001:db8::5/128 IP6 internal 30 rt3 - rt5(4) +2001:db8::6/128 IP6 internal 40 rt2 - rt6(4) + rt3 - + +IS-IS L1 IPv6 routing table: + + Prefix Metric Interface Nexthop Label(s) + ------------------------------------------------------- + 2001:db8::2/128 20 - rt2 - + 2001:db8::3/128 20 - rt3 - + 2001:db8::4/128 30 - rt2 - + 2001:db8::5/128 30 - rt3 - + 2001:db8::6/128 40 - rt2 - + - rt3 - + +test# test isis topology 2 root rt1 spf +IS-IS paths to level-1 routers that speak IP +Vertex Type Metric Next-Hop Interface Parent +rt1 +10.0.255.1/32 IP internal 0 rt1(4) +rt4 TE-IS 10 rt4 - rt1(4) +rt5 TE-IS 10 rt5 - rt1(4) +rt2 TE-IS 15 rt2 - rt1(4) +rt1 +rt6 TE-IS 20 rt4 - rt4(4) + rt5 - rt5(4) +10.0.255.4/32 IP TE 20 rt4 - rt4(4) +10.0.255.5/32 IP TE 20 rt5 - rt5(4) +10.0.255.2/32 IP TE 25 rt2 - rt2(4) +rt3 TE-IS 30 rt3 - rt1(4) +10.0.255.6/32 IP TE 30 rt4 - rt6(4) + rt5 - +10.0.255.3/32 IP TE 40 rt3 - rt3(4) + +IS-IS L1 IPv4 routing table: + + Prefix Metric Interface Nexthop Label(s) + ----------------------------------------------------- + 10.0.255.2/32 25 - rt2 - + 10.0.255.3/32 40 - rt3 - + 10.0.255.4/32 20 - rt4 - + 10.0.255.5/32 20 - rt5 - + 10.0.255.6/32 30 - rt4 - + - rt5 - + +IS-IS paths to level-1 routers that speak IPv6 +Vertex Type Metric Next-Hop Interface Parent +rt1 +2001:db8::1/128 IP6 internal 0 rt1(4) +rt4 TE-IS 10 rt4 - rt1(4) +rt5 TE-IS 10 rt5 - rt1(4) +rt2 TE-IS 15 rt2 - rt1(4) +rt1 +rt6 TE-IS 20 rt4 - rt4(4) + rt5 - rt5(4) +2001:db8::4/128 IP6 internal 20 rt4 - rt4(4) +2001:db8::5/128 IP6 internal 20 rt5 - rt5(4) +2001:db8::2/128 IP6 internal 25 rt2 - rt2(4) +rt3 TE-IS 30 rt3 - rt1(4) +2001:db8::6/128 IP6 internal 30 rt4 - rt6(4) + rt5 - +2001:db8::3/128 IP6 internal 40 rt3 - rt3(4) + +IS-IS L1 IPv6 routing table: + + Prefix Metric Interface Nexthop Label(s) + ------------------------------------------------------- + 2001:db8::2/128 25 - rt2 - + 2001:db8::3/128 40 - rt3 - + 2001:db8::4/128 20 - rt4 - + 2001:db8::5/128 20 - rt5 - + 2001:db8::6/128 30 - rt4 - + - rt5 - + +test# test isis topology 3 root rt1 spf ipv4-only +IS-IS paths to level-1 routers that speak IP +Vertex Type Metric Next-Hop Interface Parent +rt1 +10.0.255.1/32 IP internal 0 rt1(4) +rt2 TE-IS 10 rt2 - rt1(4) +rt3 TE-IS 10 rt3 - rt1(4) +rt4 TE-IS 20 rt2 - rt2(4) +10.0.255.2/32 IP TE 20 rt2 - rt2(4) +10.0.255.3/32 IP TE 20 rt3 - rt3(4) +rt5 TE-IS 30 rt2 - rt4(4) +rt6 TE-IS 30 rt2 - rt4(4) +10.0.255.4/32 IP TE 30 rt2 - rt4(4) +10.0.255.5/32 IP TE 40 rt2 - rt5(4) +10.0.255.6/32 IP TE 40 rt2 - rt6(4) + +IS-IS L1 IPv4 routing table: + + Prefix Metric Interface Nexthop Label(s) + ----------------------------------------------------- + 10.0.255.2/32 20 - rt2 - + 10.0.255.3/32 20 - rt3 - + 10.0.255.4/32 30 - rt2 - + 10.0.255.5/32 40 - rt2 - + 10.0.255.6/32 40 - rt2 - + +test# test isis topology 4 root rt1 spf ipv4-only +IS-IS paths to level-1 routers that speak IP +Vertex Type Metric Next-Hop Interface Parent +rt1 +10.0.255.1/32 IP internal 0 rt1(4) +rt2 TE-IS 10 rt2 - rt1(4) +rt3 TE-IS 10 rt3 - rt1(4) +rt4 TE-IS 20 rt2 - rt2(4) +rt5 TE-IS 20 rt3 - rt3(4) +10.0.255.2/32 IP TE 20 rt2 - rt2(4) +10.0.255.3/32 IP TE 20 rt3 - rt3(4) +rt6 TE-IS 30 rt2 - rt4(4) +rt7 TE-IS 30 rt3 - rt5(4) +10.0.255.4/32 IP TE 30 rt2 - rt4(4) +10.0.255.5/32 IP TE 30 rt3 - rt5(4) +rt8 TE-IS 40 rt2 - rt6(4) +10.0.255.6/32 IP TE 40 rt2 - rt6(4) +10.0.255.7/32 IP TE 40 rt3 - rt7(4) +10.0.255.8/32 IP TE 50 rt2 - rt8(4) + +IS-IS L1 IPv4 routing table: + + Prefix Metric Interface Nexthop Label(s) + ----------------------------------------------------- + 10.0.255.2/32 20 - rt2 - + 10.0.255.3/32 20 - rt3 - + 10.0.255.4/32 30 - rt2 - + 10.0.255.5/32 30 - rt3 - + 10.0.255.6/32 40 - rt2 - + 10.0.255.7/32 40 - rt3 - + 10.0.255.8/32 50 - rt2 - + +test# test isis topology 5 root rt1 spf ipv4-only +IS-IS paths to level-1 routers that speak IP +Vertex Type Metric Next-Hop Interface Parent +rt1 +10.0.255.1/32 IP internal 0 rt1(4) +rt2 TE-IS 10 rt2 - rt1(4) +rt3 TE-IS 10 rt3 - rt1(4) +rt4 TE-IS 20 rt2 - rt2(4) +rt5 TE-IS 20 rt3 - rt3(4) +10.0.255.2/32 IP TE 20 rt2 - rt2(4) +10.0.255.3/32 IP TE 20 rt3 - rt3(4) +rt6 TE-IS 30 rt2 - rt4(4) +rt7 TE-IS 30 rt3 - rt5(4) +10.0.255.4/32 IP TE 30 rt2 - rt4(4) +10.0.255.5/32 IP TE 30 rt3 - rt5(4) +rt8 TE-IS 40 rt2 - rt6(4) + rt3 - rt7(4) +10.0.255.6/32 IP TE 40 rt2 - rt6(4) +10.0.255.7/32 IP TE 40 rt3 - rt7(4) +10.0.255.8/32 IP TE 50 rt2 - rt8(4) + rt3 - + +IS-IS L1 IPv4 routing table: + + Prefix Metric Interface Nexthop Label(s) + ----------------------------------------------------- + 10.0.255.2/32 20 - rt2 - + 10.0.255.3/32 20 - rt3 - + 10.0.255.4/32 30 - rt2 - + 10.0.255.5/32 30 - rt3 - + 10.0.255.6/32 40 - rt2 - + 10.0.255.7/32 40 - rt3 - + 10.0.255.8/32 50 - rt2 - + - rt3 - + +test# test isis topology 6 root rt1 spf ipv4-only +IS-IS paths to level-1 routers that speak IP +Vertex Type Metric Next-Hop Interface Parent +rt1 +10.0.255.1/32 IP internal 0 rt1(4) +rt2 TE-IS 10 rt2 - rt1(4) +rt3 TE-IS 10 rt3 - rt1(4) +rt4 TE-IS 20 rt2 - rt2(4) + rt3 - rt3(4) +10.0.255.2/32 IP TE 20 rt2 - rt2(4) +10.0.255.3/32 IP TE 20 rt3 - rt3(4) +rt6 TE-IS 30 rt2 - rt4(4) + rt3 - +10.0.255.4/32 IP TE 30 rt2 - rt4(4) + rt3 - +rt5 TE-IS 40 rt2 - rt6(4) + rt3 - +rt8 TE-IS 40 rt2 - rt6(4) + rt3 - +10.0.255.6/32 IP TE 40 rt2 - rt6(4) + rt3 - +rt7 TE-IS 50 rt2 - rt5(4) + rt3 - rt8(4) +10.0.255.5/32 IP TE 50 rt2 - rt5(4) + rt3 - +10.0.255.8/32 IP TE 50 rt2 - rt8(4) + rt3 - +10.0.255.7/32 IP TE 60 rt2 - rt7(4) + rt3 - + +IS-IS L1 IPv4 routing table: + + Prefix Metric Interface Nexthop Label(s) + ----------------------------------------------------- + 10.0.255.2/32 20 - rt2 - + 10.0.255.3/32 20 - rt3 - + 10.0.255.4/32 30 - rt2 - + - rt3 - + 10.0.255.5/32 50 - rt2 - + - rt3 - + 10.0.255.6/32 40 - rt2 - + - rt3 - + 10.0.255.7/32 60 - rt2 - + - rt3 - + 10.0.255.8/32 50 - rt2 - + - rt3 - + +test# test isis topology 7 root rt1 spf ipv4-only +IS-IS paths to level-1 routers that speak IP +Vertex Type Metric Next-Hop Interface Parent +rt1 +10.0.255.1/32 IP internal 0 rt1(4) +rt4 TE-IS 10 rt4 - rt1(4) +rt5 TE-IS 20 rt4 - rt4(4) +rt7 TE-IS 20 rt4 - rt4(4) +10.0.255.4/32 IP TE 20 rt4 - rt4(4) +rt2 TE-IS 30 rt4 - rt5(4) +rt6 TE-IS 30 rt4 - rt5(4) +rt8 TE-IS 30 rt4 - rt5(4) + rt7(4) +10.0.255.5/32 IP TE 30 rt4 - rt5(4) +10.0.255.7/32 IP TE 30 rt4 - rt7(4) +rt10 TE-IS 40 rt4 - rt7(4) +rt3 TE-IS 40 rt4 - rt2(4) + rt6(4) +rt9 TE-IS 40 rt4 - rt8(4) +rt11 TE-IS 40 rt4 - rt8(4) +10.0.255.2/32 IP TE 40 rt4 - rt2(4) +10.0.255.6/32 IP TE 40 rt4 - rt6(4) +10.0.255.8/32 IP TE 40 rt4 - rt8(4) +rt12 TE-IS 50 rt4 - rt9(4) + rt11(4) +10.0.255.10/32 IP TE 50 rt4 - rt10(4) +10.0.255.3/32 IP TE 50 rt4 - rt3(4) +10.0.255.9/32 IP TE 50 rt4 - rt9(4) +10.0.255.11/32 IP TE 50 rt4 - rt11(4) +10.0.255.12/32 IP TE 60 rt4 - rt12(4) + +IS-IS L1 IPv4 routing table: + + Prefix Metric Interface Nexthop Label(s) + ------------------------------------------------------ + 10.0.255.2/32 40 - rt4 - + 10.0.255.3/32 50 - rt4 - + 10.0.255.4/32 20 - rt4 - + 10.0.255.5/32 30 - rt4 - + 10.0.255.6/32 40 - rt4 - + 10.0.255.7/32 30 - rt4 - + 10.0.255.8/32 40 - rt4 - + 10.0.255.9/32 50 - rt4 - + 10.0.255.10/32 50 - rt4 - + 10.0.255.11/32 50 - rt4 - + 10.0.255.12/32 60 - rt4 - + +test# test isis topology 8 root rt1 spf ipv4-only +IS-IS paths to level-1 routers that speak IP +Vertex Type Metric Next-Hop Interface Parent +rt1 +10.0.255.1/32 IP internal 0 rt1(4) +rt2 TE-IS 10 rt2 - rt1(4) +rt4 TE-IS 10 rt4 - rt1(4) +rt3 TE-IS 20 rt2 - rt2(4) +rt5 TE-IS 20 rt2 - rt2(4) +rt7 TE-IS 20 rt4 - rt4(4) +10.0.255.2/32 IP TE 20 rt2 - rt2(4) +10.0.255.4/32 IP TE 20 rt4 - rt4(4) +rt6 TE-IS 30 rt2 - rt3(4) + rt5(4) +rt8 TE-IS 30 rt2 - rt5(4) +rt10 TE-IS 30 rt4 - rt7(4) +10.0.255.3/32 IP TE 30 rt2 - rt3(4) +10.0.255.5/32 IP TE 30 rt2 - rt5(4) +10.0.255.7/32 IP TE 30 rt4 - rt7(4) +rt9 TE-IS 40 rt2 - rt8(4) +rt11 TE-IS 40 rt2 - rt8(4) +10.0.255.6/32 IP TE 40 rt2 - rt6(4) +10.0.255.8/32 IP TE 40 rt2 - rt8(4) +10.0.255.10/32 IP TE 40 rt4 - rt10(4) +rt12 TE-IS 50 rt2 - rt9(4) + rt11(4) +10.0.255.9/32 IP TE 50 rt2 - rt9(4) +10.0.255.11/32 IP TE 50 rt2 - rt11(4) +10.0.255.12/32 IP TE 60 rt2 - rt12(4) + +IS-IS L1 IPv4 routing table: + + Prefix Metric Interface Nexthop Label(s) + ------------------------------------------------------ + 10.0.255.2/32 20 - rt2 - + 10.0.255.3/32 30 - rt2 - + 10.0.255.4/32 20 - rt4 - + 10.0.255.5/32 30 - rt2 - + 10.0.255.6/32 40 - rt2 - + 10.0.255.7/32 30 - rt4 - + 10.0.255.8/32 40 - rt2 - + 10.0.255.9/32 50 - rt2 - + 10.0.255.10/32 40 - rt4 - + 10.0.255.11/32 50 - rt2 - + 10.0.255.12/32 60 - rt2 - + +test# test isis topology 9 root rt1 spf +IS-IS paths to level-1 routers that speak IP +Vertex Type Metric Next-Hop Interface Parent +rt1 +10.0.255.1/32 IP internal 0 rt1(4) +rt2 TE-IS 10 rt2 - rt1(4) +rt3 TE-IS 10 rt3 - rt1(4) +rt4 TE-IS 20 rt2 - rt2(4) +10.0.255.2/32 IP TE 20 rt2 - rt2(4) +10.0.255.3/32 IP TE 20 rt3 - rt3(4) +rt5 TE-IS 30 rt2 - rt4(4) +10.0.255.4/32 IP TE 30 rt2 - rt4(4) +rt9 TE-IS 40 rt2 - rt5(4) +10.0.255.5/32 IP TE 40 rt2 - rt5(4) +rt6 TE-IS 50 rt2 - rt4(4) + rt9(4) +rt7 TE-IS 50 rt2 - rt4(4) + rt9(4) +rt8 TE-IS 50 rt2 - rt4(4) + rt9(4) +10.0.255.9/32 IP TE 50 rt2 - rt9(4) +10.0.255.6/32 IP TE 60 rt2 - rt6(4) +10.0.255.7/32 IP TE 60 rt2 - rt7(4) +10.0.255.8/32 IP TE 60 rt2 - rt8(4) + +IS-IS L1 IPv4 routing table: + + Prefix Metric Interface Nexthop Label(s) + ----------------------------------------------------- + 10.0.255.2/32 20 - rt2 - + 10.0.255.3/32 20 - rt3 - + 10.0.255.4/32 30 - rt2 - + 10.0.255.5/32 40 - rt2 - + 10.0.255.6/32 60 - rt2 - + 10.0.255.7/32 60 - rt2 - + 10.0.255.8/32 60 - rt2 - + 10.0.255.9/32 50 - rt2 - + +IS-IS paths to level-1 routers that speak IPv6 +Vertex Type Metric Next-Hop Interface Parent +rt1 +2001:db8::1/128 IP6 internal 0 rt1(4) +rt2 TE-IS 10 rt2 - rt1(4) +rt3 TE-IS 10 rt3 - rt1(4) +rt4 TE-IS 20 rt2 - rt2(4) +2001:db8::2/128 IP6 internal 20 rt2 - rt2(4) +2001:db8::3/128 IP6 internal 20 rt3 - rt3(4) +rt5 TE-IS 30 rt2 - rt4(4) +2001:db8::4/128 IP6 internal 30 rt2 - rt4(4) +rt9 TE-IS 40 rt2 - rt5(4) +2001:db8::5/128 IP6 internal 40 rt2 - rt5(4) +rt6 TE-IS 50 rt2 - rt4(4) + rt9(4) +rt7 TE-IS 50 rt2 - rt4(4) + rt9(4) +rt8 TE-IS 50 rt2 - rt4(4) + rt9(4) +2001:db8::9/128 IP6 internal 50 rt2 - rt9(4) +2001:db8::6/128 IP6 internal 60 rt2 - rt6(4) +2001:db8::7/128 IP6 internal 60 rt2 - rt7(4) +2001:db8::8/128 IP6 internal 60 rt2 - rt8(4) + +IS-IS L1 IPv6 routing table: + + Prefix Metric Interface Nexthop Label(s) + ------------------------------------------------------- + 2001:db8::2/128 20 - rt2 - + 2001:db8::3/128 20 - rt3 - + 2001:db8::4/128 30 - rt2 - + 2001:db8::5/128 40 - rt2 - + 2001:db8::6/128 60 - rt2 - + 2001:db8::7/128 60 - rt2 - + 2001:db8::8/128 60 - rt2 - + 2001:db8::9/128 50 - rt2 - + +test# test isis topology 10 root rt1 spf +IS-IS paths to level-1 routers that speak IP +Vertex Type Metric Next-Hop Interface Parent +rt1 +10.0.255.1/32 IP internal 0 rt1(4) +rt2 TE-IS 10 rt2 - rt1(4) +rt3 TE-IS 20 rt3 - rt1(4) +rt4 TE-IS 20 rt4 - rt1(4) +rt5 TE-IS 20 rt2 - rt2(4) +10.0.255.2/32 IP TE 20 rt2 - rt2(4) +rt6 TE-IS 30 rt3 - rt3(4) +rt7 TE-IS 30 rt4 - rt4(4) +rt8 TE-IS 30 rt2 - rt5(4) +10.0.255.3/32 IP TE 30 rt3 - rt3(4) +10.0.255.4/32 IP TE 30 rt4 - rt4(4) +10.0.255.5/32 IP TE 30 rt2 - rt5(4) +10.0.255.6/32 IP TE 40 rt3 - rt6(4) +10.0.255.7/32 IP TE 40 rt4 - rt7(4) +10.0.255.8/32 IP TE 40 rt2 - rt8(4) + +IS-IS L1 IPv4 routing table: + + Prefix Metric Interface Nexthop Label(s) + ----------------------------------------------------- + 10.0.255.2/32 20 - rt2 - + 10.0.255.3/32 30 - rt3 - + 10.0.255.4/32 30 - rt4 - + 10.0.255.5/32 30 - rt2 - + 10.0.255.6/32 40 - rt3 - + 10.0.255.7/32 40 - rt4 - + 10.0.255.8/32 40 - rt2 - + +IS-IS paths to level-1 routers that speak IPv6 +Vertex Type Metric Next-Hop Interface Parent +rt1 +2001:db8::1/128 IP6 internal 0 rt1(4) +rt2 TE-IS 10 rt2 - rt1(4) +rt3 TE-IS 20 rt3 - rt1(4) +rt4 TE-IS 20 rt4 - rt1(4) +rt5 TE-IS 20 rt2 - rt2(4) +2001:db8::2/128 IP6 internal 20 rt2 - rt2(4) +rt6 TE-IS 30 rt3 - rt3(4) +rt7 TE-IS 30 rt4 - rt4(4) +rt8 TE-IS 30 rt2 - rt5(4) +2001:db8::3/128 IP6 internal 30 rt3 - rt3(4) +2001:db8::4/128 IP6 internal 30 rt4 - rt4(4) +2001:db8::5/128 IP6 internal 30 rt2 - rt5(4) +2001:db8::6/128 IP6 internal 40 rt3 - rt6(4) +2001:db8::7/128 IP6 internal 40 rt4 - rt7(4) +2001:db8::8/128 IP6 internal 40 rt2 - rt8(4) + +IS-IS L1 IPv6 routing table: + + Prefix Metric Interface Nexthop Label(s) + ------------------------------------------------------- + 2001:db8::2/128 20 - rt2 - + 2001:db8::3/128 30 - rt3 - + 2001:db8::4/128 30 - rt4 - + 2001:db8::5/128 30 - rt2 - + 2001:db8::6/128 40 - rt3 - + 2001:db8::7/128 40 - rt4 - + 2001:db8::8/128 40 - rt2 - + +test# test isis topology 11 root rt1 spf +IS-IS paths to level-1 routers that speak IP +Vertex Type Metric Next-Hop Interface Parent +rt1 +10.0.255.1/32 IP internal 0 rt1(4) +rt2 TE-IS 10 rt2 - rt1(4) +rt3 TE-IS 10 rt3 - rt1(4) +rt2 pseudo_TE-IS 20 rt3 - rt3(4) +rt4 TE-IS 20 rt2 - rt2(4) +rt5 TE-IS 20 rt3 - rt3(4) +10.0.255.2/32 IP TE 20 rt2 - rt2(4) +10.0.255.3/32 IP TE 20 rt3 - rt3(4) +rt6 TE-IS 30 rt2 - rt4(4) + rt3 - rt5(4) +10.0.255.4/32 IP TE 30 rt2 - rt4(4) +10.0.255.5/32 IP TE 30 rt3 - rt5(4) +10.0.255.6/32 IP TE 40 rt2 - rt6(4) + rt3 - + +IS-IS L1 IPv4 routing table: + + Prefix Metric Interface Nexthop Label(s) + ----------------------------------------------------- + 10.0.255.2/32 20 - rt2 - + 10.0.255.3/32 20 - rt3 - + 10.0.255.4/32 30 - rt2 - + 10.0.255.5/32 30 - rt3 - + 10.0.255.6/32 40 - rt2 - + - rt3 - + +IS-IS paths to level-1 routers that speak IPv6 +Vertex Type Metric Next-Hop Interface Parent +rt1 +2001:db8::1/128 IP6 internal 0 rt1(4) +rt2 TE-IS 10 rt2 - rt1(4) +rt3 TE-IS 10 rt3 - rt1(4) +rt2 pseudo_TE-IS 20 rt3 - rt3(4) +rt4 TE-IS 20 rt2 - rt2(4) +rt5 TE-IS 20 rt3 - rt3(4) +2001:db8::2/128 IP6 internal 20 rt2 - rt2(4) +2001:db8::3/128 IP6 internal 20 rt3 - rt3(4) +rt6 TE-IS 30 rt2 - rt4(4) + rt3 - rt5(4) +2001:db8::4/128 IP6 internal 30 rt2 - rt4(4) +2001:db8::5/128 IP6 internal 30 rt3 - rt5(4) +2001:db8::6/128 IP6 internal 40 rt2 - rt6(4) + rt3 - + +IS-IS L1 IPv6 routing table: + + Prefix Metric Interface Nexthop Label(s) + ------------------------------------------------------- + 2001:db8::2/128 20 - rt2 - + 2001:db8::3/128 20 - rt3 - + 2001:db8::4/128 30 - rt2 - + 2001:db8::5/128 30 - rt3 - + 2001:db8::6/128 40 - rt2 - + - rt3 - + +test# test isis topology 12 root rt1 spf ipv4-only +IS-IS paths to level-1 routers that speak IP +Vertex Type Metric Next-Hop Interface Parent +rt1 +10.0.255.1/32 IP internal 0 rt1(4) +rt2 TE-IS 10 rt2 - rt1(4) +rt3 TE-IS 10 rt3 - rt1(4) +rt4 TE-IS 20 rt2 - rt2(4) +rt5 TE-IS 20 rt3 - rt3(4) +10.0.255.2/32 IP TE 20 rt2 - rt2(4) +10.0.255.3/32 IP TE 20 rt3 - rt3(4) +rt6 TE-IS 30 rt2 - rt4(4) +rt7 TE-IS 30 rt3 - rt5(4) +10.0.255.4/32 IP TE 30 rt2 - rt4(4) +10.0.255.5/32 IP TE 30 rt3 - rt5(4) +rt8 TE-IS 40 rt2 - rt6(4) +rt9 TE-IS 40 rt3 - rt7(4) +10.0.255.6/32 IP TE 40 rt2 - rt6(4) +10.0.255.7/32 IP TE 40 rt3 - rt7(4) +rt10 TE-IS 50 rt2 - rt8(4) +10.0.255.8/32 IP TE 50 rt2 - rt8(4) +10.0.255.9/32 IP TE 50 rt3 - rt9(4) +10.0.255.10/32 IP TE 60 rt2 - rt10(4) + +IS-IS L1 IPv4 routing table: + + Prefix Metric Interface Nexthop Label(s) + ------------------------------------------------------ + 10.0.255.2/32 20 - rt2 - + 10.0.255.3/32 20 - rt3 - + 10.0.255.4/32 30 - rt2 - + 10.0.255.5/32 30 - rt3 - + 10.0.255.6/32 40 - rt2 - + 10.0.255.7/32 40 - rt3 - + 10.0.255.8/32 50 - rt2 - + 10.0.255.9/32 50 - rt3 - + 10.0.255.10/32 60 - rt2 - + +test# test isis topology 13 root rt1 spf ipv4-only +IS-IS paths to level-1 routers that speak IP +Vertex Type Metric Next-Hop Interface Parent +rt1 +10.0.255.1/32 IP internal 0 rt1(4) +rt2 TE-IS 10 rt2 - rt1(4) +rt3 TE-IS 10 rt3 - rt1(4) +rt4 TE-IS 20 rt2 - rt2(4) + rt3 - rt3(4) +rt5 TE-IS 20 rt3 - rt3(4) +rt6 TE-IS 20 rt3 - rt3(4) +10.0.255.2/32 IP TE 20 rt2 - rt2(4) +10.0.255.3/32 IP TE 20 rt3 - rt3(4) +rt7 TE-IS 30 rt3 - rt5(4) + rt6(4) +10.0.255.4/32 IP TE 30 rt2 - rt4(4) + rt3 - +10.0.255.5/32 IP TE 30 rt3 - rt5(4) +10.0.255.6/32 IP TE 30 rt3 - rt6(4) +10.0.255.7/32 IP TE 40 rt3 - rt7(4) + +IS-IS L1 IPv4 routing table: + + Prefix Metric Interface Nexthop Label(s) + ----------------------------------------------------- + 10.0.255.2/32 20 - rt2 - + 10.0.255.3/32 20 - rt3 - + 10.0.255.4/32 30 - rt2 - + - rt3 - + 10.0.255.5/32 30 - rt3 - + 10.0.255.6/32 30 - rt3 - + 10.0.255.7/32 40 - rt3 - + +test# +test# test isis topology 4 root rt1 reverse-spf ipv4-only +IS-IS paths to level-1 routers that speak IP +Vertex Type Metric Next-Hop Interface Parent +rt1 +10.0.255.1/32 IP internal 0 rt1(4) +rt2 TE-IS 10 rt2 - rt1(4) +rt3 TE-IS 10 rt3 - rt1(4) +rt4 TE-IS 20 rt2 - rt2(4) +rt5 TE-IS 20 rt3 - rt3(4) +10.0.255.2/32 IP TE 20 rt2 - rt2(4) +10.0.255.3/32 IP TE 20 rt3 - rt3(4) +rt6 TE-IS 30 rt2 - rt4(4) +rt7 TE-IS 30 rt3 - rt5(4) +10.0.255.4/32 IP TE 30 rt2 - rt4(4) +10.0.255.5/32 IP TE 30 rt3 - rt5(4) +rt8 TE-IS 40 rt2 - rt6(4) +10.0.255.6/32 IP TE 40 rt2 - rt6(4) +10.0.255.7/32 IP TE 40 rt3 - rt7(4) +10.0.255.8/32 IP TE 50 rt2 - rt8(4) + +IS-IS L1 IPv4 routing table: + + Prefix Metric Interface Nexthop Label(s) + ----------------------------------------------------- + 10.0.255.2/32 20 - rt2 - + 10.0.255.3/32 20 - rt3 - + 10.0.255.4/32 30 - rt2 - + 10.0.255.5/32 30 - rt3 - + 10.0.255.6/32 40 - rt2 - + 10.0.255.7/32 40 - rt3 - + 10.0.255.8/32 50 - rt2 - + +test# test isis topology 11 root rt1 reverse-spf +IS-IS paths to level-1 routers that speak IP +Vertex Type Metric Next-Hop Interface Parent +rt1 +10.0.255.1/32 IP internal 0 rt1(4) +rt2 TE-IS 10 rt1(4) +rt3 TE-IS 10 rt3 - rt1(4) +rt2 pseudo_TE-IS 20 rt3 - rt3(4) +rt4 TE-IS 20 rt2(4) +rt5 TE-IS 20 rt3 - rt3(4) +10.0.255.2/32 IP TE 20 rt2(4) +10.0.255.3/32 IP TE 20 rt3 - rt3(4) +rt6 TE-IS 30 rt3 - rt4(4) + rt5(4) +10.0.255.4/32 IP TE 30 rt4(4) +10.0.255.5/32 IP TE 30 rt3 - rt5(4) +10.0.255.6/32 IP TE 40 rt3 - rt6(4) + +IS-IS L1 IPv4 routing table: + + Prefix Metric Interface Nexthop Label(s) + ----------------------------------------------------- + 10.0.255.3/32 20 - rt3 - + 10.0.255.5/32 30 - rt3 - + 10.0.255.6/32 40 - rt3 - + +IS-IS paths to level-1 routers that speak IPv6 +Vertex Type Metric Next-Hop Interface Parent +rt1 +2001:db8::1/128 IP6 internal 0 rt1(4) +rt2 TE-IS 10 rt1(4) +rt3 TE-IS 10 rt3 - rt1(4) +rt2 pseudo_TE-IS 20 rt3 - rt3(4) +rt4 TE-IS 20 rt2(4) +rt5 TE-IS 20 rt3 - rt3(4) +2001:db8::2/128 IP6 internal 20 rt2(4) +2001:db8::3/128 IP6 internal 20 rt3 - rt3(4) +rt6 TE-IS 30 rt3 - rt4(4) + rt5(4) +2001:db8::4/128 IP6 internal 30 rt4(4) +2001:db8::5/128 IP6 internal 30 rt3 - rt5(4) +2001:db8::6/128 IP6 internal 40 rt3 - rt6(4) + +IS-IS L1 IPv6 routing table: + + Prefix Metric Interface Nexthop Label(s) + ------------------------------------------------------- + 2001:db8::3/128 20 - rt3 - + 2001:db8::5/128 30 - rt3 - + 2001:db8::6/128 40 - rt3 - + +test# +end. diff --git a/tests/isisd/test_isis_vertex_queue.c b/tests/isisd/test_isis_vertex_queue.c index 869dd732eb..d2d9f6293a 100644 --- a/tests/isisd/test_isis_vertex_queue.c +++ b/tests/isisd/test_isis_vertex_queue.c @@ -2,14 +2,7 @@ #include "isisd/isis_spf.c" -struct thread_master *master; -int isis_sock_init(struct isis_circuit *circuit); -int isis_sock_init(struct isis_circuit *circuit) -{ - return 0; -} - -struct zebra_privs_t isisd_privs; +#include "test_common.h" static struct isis_vertex **vertices; static size_t vertex_count; diff --git a/tests/isisd/test_topologies.c b/tests/isisd/test_topologies.c new file mode 100644 index 0000000000..ee89407a79 --- /dev/null +++ b/tests/isisd/test_topologies.c @@ -0,0 +1,3307 @@ +/* + * Copyright (C) 2020 NetDEF, Inc. + * Renato Westphal + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "isisd/isisd.h" + +#include "test_common.h" + +/* + * clang-format off + * + * All topologies have the following properties: + * - The System-ID is 0000.0000.000X, where X is the node number (in hex); + * - The Router-ID is 10.0.255.X, where X is the node number; + * - The default link metric is 10; + * - When SR is enabled, Adj-SIDs and Prefix-SIDs are generated automatically; + * - When SR is enabled, the default SRGB is [16000-23999] (can be overriden). + * + * Test topology 1: + * ================ + * + * +---------+ + * | | + * | RT1 | + * +----------+ +----------+ + * | | | | + * | +---------+ | + * | | + * | | + * | | + * +----+----+ +----+----+ + * | | | | + * | RT2 | | RT3 | + * | | | | + * | | | | + * +----+----+ +----+----+ + * | | + * | | + * | | + * +---+-+---+ +----+----+ + * | | | | + * | RT4 | | RT5 | + * | | | | + * | | | | + * +----+----+ +----+----+ + * | | + * | | + * | | + * | +---------+ | + * | | | | + * | | RT6 | | + * +----------+ +----------+ + * | | + * +---------+ + * + * Test topology 2: + * ================ + * + * +---------+ + * | | + * | RT1 | + * +----------+ +----------+ + * | | | | + * | +----+----+ | + * | | | + * 15 | | | 30 + * | | | + * +----+----+ | +----+----+ + * | | | | | + * | RT2 | | | RT3 | + * | | | | | + * | | | | | + * +----+----+ | +----+----+ + * | | | + * 40 | | | 40 + * | | | + * +----+----+ | +----+----+ + * | | | | | + * | RT4 | | | RT5 | + * | +----------+----------+ | + * | | | | + * +----+----+ +----+----+ + * | | + * | | + * | | + * | +---------+ | + * | | | | + * | | RT6 | | + * +----------+ +----------+ + * | | + * +---------+ + * + * Test topology 3: + * ================ + * + * +---------+ + * | | + * | RT1 | + * +----------+ +----------+ + * | | | | + * | +---------+ | + * | | + * | | + * | | + * +----+----+ +----+----+ + * | | | | + * | RT2 | | RT3 | + * | +---------------------+ | + * | | | | + * +----+----+ +----+----+ + * | | + * | | 30 + * | | + * +----+----+ +----+----+ + * | | | | + * | RT4 | | RT5 | + * | +---------------------+ | + * | | | | + * +----+----+ +----+----+ + * | | + * | | + * | | + * | +---------+ | + * | | | | + * | | RT6 | | + * +----------+ +----------+ + * | | + * +---------+ + * + * Test topology 4: + * ================ + * + * +---------+ +---------+ + * | | | | + * | RT1 | | RT2 | + * | +---------------------+ | + * | | | | + * +---+-----+ +------+--+ + * | | + * | | + * | | + * +---+-----+ +------+--+ + * | | | | + * | RT3 | | RT4 | + * | | | | + * | | | | + * +---+-----+ +------+--+ + * |^ | + * ||200 | + * | | + * +---+-----+ +------+--+ + * | | | | + * | RT5 | 50 | RT6 | + * | +---------------------+ | + * | | | | + * +---+-----+ +------+--+ + * | | + * | | + * | | + * +---+-----+ +------+--+ + * | | | | + * | RT7 | | RT8 | + * | | | | + * | | | | + * +---------+ +---------+ + * + * Test topology 5: + * ================ + * + * +---------+ +---------+ + * | | | | + * | RT1 | | RT2 | + * | +---------------------+ | + * | | | | + * +---+-----+ +------+--+ + * | | + * | | + * | | + * +---+-----+ +------+--+ + * | | | | + * | RT3 | | RT4 | + * | | | | + * | | | | + * +---+-----+ +------+--+ + * | | + * | | + * | | + * +---+-----+ +------+--+ + * | | | | + * | RT5 | | RT6 | + * | | | | + * | | | | + * +---+-----+ +------+--+ + * | | + * | | + * | | + * +---+-----+ +------+--+ + * | | | | + * | RT7 | | RT8 | + * | +---------------------+ | + * | | | | + * +---------+ +---------+ + * + * Test topology 6: + * ================ + * + * +---------+ +---------+ + * | | | | + * | RT1 | | RT2 | + * | +---------------------+ | + * | | | | + * +---+-----+ +------+--+ + * | | + * | | + * | | + * +---+-----+ +------+--+ + * | | | | + * | RT3 | | RT4 | + * | +---------------------+ | + * | | | | + * +---------+ +------+--+ + * | + * | + * | + * +---------+ +------+--+ + * | | | | + * | RT5 | | RT6 | + * | +---------------------+ | + * | | | | + * +---+-----+ +------+--+ + * | | + * | | + * | | + * +---+-----+ +------+--+ + * | | | | + * | RT7 | | RT8 | + * | +---------------------+ | + * | | | | + * +---------+ +---------+ + * + * Test topology 7: + * ================ + * + * +---------+ +---------+ +---------+ + * | | | | | | + * | RT1 | 40 | RT2 | | RT3 | + * | +---------------------+ +---------------------+ | + * | | | | | | + * +---+-----+ +----+----+ +------+--+ + * | | | + * | | | + * | | | + * +---+-----+ +----+----+ +------+--+ + * | | | | | | + * | RT4 | | RT5 | | RT6 | + * | +---------------------+ +---------------------+ | + * | | | | | | + * +---+-----+ +----+----+ +------+--+ + * | | | + * | | | 30 + * | | | + * +---+-----+ +----+----+ +------+--+ + * | | | | | | + * | RT7 | | RT8 | | RT9 | + * | +---------------------+ +---------------------+ | + * | | | | | | + * +---+-----+ +----+----+ +------+--+ + * | | | + * | 20 | | + * | | | + * +---+-----+ +----+----+ +------+--+ + * | | | | | | + * | RT10 | | RT11 | | RT12 | + * | +---------------------+ +---------------------+ | + * | | | | | | + * +---------+ +---------+ +---------+ + * + * Test topology 8: + * ================ + * + * +---------+ +---------+ +---------+ + * | | | | | | + * | RT1 | | RT2 | | RT3 | + * | +---------------------+ +---------------------+ | + * | | | | | | + * +---+-----+ +----+----+ +------+--+ + * | | | + * | | | + * | | | + * +---+-----+ +----+----+ +------+--+ + * | | | | | | + * | RT4 | | RT5 | | RT6 | + * | | | +---------------------+ | + * | | | | | | + * +---+-----+ +----+----+ +---------+ + * | | + * | | + * | | + * +---+-----+ +----+----+ +---------+ + * | | | | | | + * | RT7 | | RT8 | | RT9 | + * | | | +---------------------+ | + * | | | | | | + * +---+-----+ +----+----+ +------+--+ + * | | | + * | | | + * | | | + * +---+-----+ +----+----+ +------+--+ + * | | | | | | + * | RT10 | | RT11 | | RT12 | + * | +---------------------+ +---------------------+ | + * | | 30 | | | | + * +---------+ +---------+ +---------+ + * + * Test topology 9: + * ================ + * + * +---------+ + * | | + * | RT1 | + * +----------+ +----------+ + * | | | | + * | +---------+ | + * | | + * | | + * | | + * +----+----+ +----+----+ + * | | | | + * | RT2 | | RT3 | + * | | | | + * | | | | + * +----+----+ +------+--+ + * | | + * | | + * | |100 + * | +---------+ | + * | | | | + * +----------+ RT4 +------------+ + * +----------------| |----------------+ + * | +-+ +--+ | + * | | +---------+ | | + * | | | | + * | |30 |30 |30 + * | | | | + * +----+----+ +----+----+ +----+----+ +----+----+ + * | | | | | | | | + * | RT5 | | RT6 | | RT7 | | RT8 | + * | | | | | | | | + * | | | | | | | | + * +----+----+ +----+----+ +----+----+ +----+----+ + * | | | | + * | | | | + * | | | | + * | | +---------+ | | + * | +-+ +--+ | + * +----------------+ RT9 +----------------+ + * | | + * | | + * +---------+ + * + * Test topology 10: + * ================ + * + * +---------+ + * | | + * | RT1 | + * +----------+ +----------+ + * | | | | + * | +----+----+ | + * | | | + * | |20 |20 + * | | | + * +----+----+ +----+----+ +----+----+ + * | | | | | | + * | RT2 | | RT3 | | RT4 | + * | | | | | | + * | | | | | | + * +----+----+ +----+----+ +----+----+ + * | | | + * | | | + * | | | + * +----+----+ +----+----+ +----+----+ + * | | | | | | + * | RT5 | | RT6 | | RT7 | + * | | | | | | + * | | | | | | + * +----+----+ +----+----+ +----+----+ + * | | | + * | |50 |50 + * | | | + * | +----+----+ | + * | | | | + * +----------+ RT8 +----------+ + * | | + * | | + * +---------+ + * + * Test topology 11: + * ================ + * + * +---------+ + * | | + * | RT1 | + * | | + * | | + * +----+----+ + * | + * | + * | + * +---------+ | +---------+ + * | | | | | + * | RT2 |50 | | RT3 | + * | +----------+----------+ | + * | | | | + * +----+----+ +----+----+ + * | | + * | | + * | | + * +----+----+ +----+----+ + * | | | | + * | RT4 | | RT5 | + * | +---------------------+ | + * | | | | + * +----+----+ +----+----+ + * | | + * | | + * | | + * | +---------+ | + * | | | | + * | | RT6 | | + * +----------+ +----------+ + * | | + * +---------+ + * + * Test topology 12: + * ================ + * + * +---------+ +---------+ + * | | | | + * | RT1 | | RT2 | + * | +---------------------+ | + * | | | | + * +---+-----+ +------+--+ + * | | + * | | + * | | + * +---+-----+ +------+--+ + * | | | | + * | RT3 | | RT4 | + * | | | | + * | | | | + * +---+-----+ +------+--+ + * |^ | + * |400 | + * | | + * +---+-----+ +------+--+ + * | | | | + * | RT5 | | RT6 | + * | | | | + * | | | | + * +---+-----+ +------+--+ + * |^ | + * |200 | + * | | + * +---+-----+ +------+--+ + * | | | | + * | RT7 | | RT8 | + * | +---------------------+ | + * | | 100 | | + * +---+-----+ +------+--+ + * | | + * | | + * | | + * +---+-----+ +------+--+ + * | | | | + * | RT9 | | RT10 | + * | | | | + * | | | | + * +---------+ +---------+ + * + * Test topology 13: + * ================ + * + * +---------+ +---------+ + * | | | | + * | RT1 | | RT2 | + * | +---------------------+ | + * | | | | + * +---+-----+ +----+----+ + * | | + * | | + * | | + * | +----+----+ + * | | | + * | +----------+ RT4 | + * | | | | + * +---+-----+ | | | + * | | | +----+----+ + * | RT3 +----------+ | + * | +----------+ |100 + * | | | | + * +---+-----+ | +----+----+ + * | | | | + * | | | RT5 | + * | +----------+ | + * | | | + * | +----+----+ + * | | + * | | + * | | + * +---+-----+ +----+----+ + * | | | | + * | RT6 | | RT7 | + * | +---------------------+ | + * | | | | + * +---------+ +---------+ + */ + +struct isis_topology test_topologies[] = { + { + .number = 1, + .nodes = { + { + .hostname = "rt1", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.1", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.1/32", + "2001:db8::1/128", + }, + .adjacencies = { + { + .hostname = "rt2", + .metric = 10, + }, + { + .hostname = "rt3", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt2", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.2", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.2/32", + "2001:db8::2/128", + }, + .adjacencies = { + { + .hostname = "rt1", + .metric = 10, + }, + { + .hostname = "rt4", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt3", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x03}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.3", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.3/32", + "2001:db8::3/128", + }, + .adjacencies = { + { + .hostname = "rt1", + .metric = 10, + }, + { + .hostname = "rt5", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt4", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x04}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.4", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.4/32", + "2001:db8::4/128", + }, + .adjacencies = { + { + .hostname = "rt2", + .metric = 10, + }, + { + .hostname = "rt6", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt5", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x05}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.5", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.5/32", + "2001:db8::5/128", + }, + .adjacencies = { + { + .hostname = "rt3", + .metric = 10, + }, + { + .hostname = "rt6", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt6", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x06}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.6", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.6/32", + "2001:db8::6/128", + }, + .adjacencies = { + { + .hostname = "rt4", + .metric = 10, + }, + { + .hostname = "rt5", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + }, + }, + { + .number = 2, + .nodes = { + { + .hostname = "rt1", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.1", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.1/32", + "2001:db8::1/128", + }, + .adjacencies = { + { + .hostname = "rt1", + .pseudonode_id = 1, + .metric = 10, + }, + { + .hostname = "rt2", + .metric = 15, + }, + { + .hostname = "rt3", + .metric = 30, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt2", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.2", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.2/32", + "2001:db8::2/128", + }, + .adjacencies = { + { + .hostname = "rt1", + .metric = 15, + }, + { + .hostname = "rt4", + .metric = 40, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt3", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x03}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.3", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.3/32", + "2001:db8::3/128", + }, + .adjacencies = { + { + .hostname = "rt1", + .metric = 30, + }, + { + .hostname = "rt5", + .metric = 40, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt4", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x04}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.4", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.4/32", + "2001:db8::4/128", + }, + .adjacencies = { + { + .hostname = "rt1", + .pseudonode_id = 1, + .metric = 10, + }, + { + .hostname = "rt2", + .metric = 40, + }, + { + .hostname = "rt6", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt5", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x05}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.5", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.5/32", + "2001:db8::5/128", + }, + .adjacencies = { + { + .hostname = "rt1", + .pseudonode_id = 1, + .metric = 10, + }, + { + .hostname = "rt3", + .metric = 40, + }, + { + .hostname = "rt6", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt6", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x06}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.6", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.6/32", + "2001:db8::6/128", + }, + .adjacencies = { + { + .hostname = "rt4", + .metric = 10, + }, + { + .hostname = "rt5", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt1", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + .pseudonode_id = 1, + .level = IS_LEVEL_1, + .adjacencies = { + { + .hostname = "rt1", + .metric = 0, + }, + { + .hostname = "rt4", + .metric = 0, + }, + { + .hostname = "rt5", + .metric = 0, + }, + }, + }, + }, + }, + { + .number = 3, + .nodes = { + { + .hostname = "rt1", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.1", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.1/32", + }, + .adjacencies = { + { + .hostname = "rt2", + .metric = 10, + }, + { + .hostname = "rt3", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt2", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.2", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.2/32", + }, + .adjacencies = { + { + .hostname = "rt1", + .metric = 10, + }, + { + .hostname = "rt3", + .metric = 10, + }, + { + .hostname = "rt4", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt3", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x03}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.3", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.3/32", + }, + .adjacencies = { + { + .hostname = "rt1", + .metric = 10, + }, + { + .hostname = "rt2", + .metric = 10, + }, + { + .hostname = "rt5", + .metric = 30, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt4", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x04}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.4", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.4/32", + }, + .adjacencies = { + { + .hostname = "rt2", + .metric = 10, + }, + { + .hostname = "rt5", + .metric = 10, + }, + { + .hostname = "rt6", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt5", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x05}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.5", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.5/32", + }, + .adjacencies = { + { + .hostname = "rt3", + .metric = 30, + }, + { + .hostname = "rt4", + .metric = 10, + }, + { + .hostname = "rt6", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt6", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x06}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.6", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.6/32", + }, + .adjacencies = { + { + .hostname = "rt4", + .metric = 10, + }, + { + .hostname = "rt5", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + }, + }, + { + .number = 4, + .nodes = { + { + .hostname = "rt1", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.1", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.1/32", + }, + .adjacencies = { + { + .hostname = "rt2", + .metric = 10, + }, + { + .hostname = "rt3", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt2", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.2", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.2/32", + }, + .adjacencies = { + { + .hostname = "rt1", + .metric = 10, + }, + { + .hostname = "rt4", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt3", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x03}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.3", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.3/32", + }, + .adjacencies = { + { + .hostname = "rt1", + .metric = 10, + }, + { + .hostname = "rt5", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt4", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x04}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.4", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.4/32", + }, + .adjacencies = { + { + .hostname = "rt2", + .metric = 10, + }, + { + .hostname = "rt6", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt5", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x05}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.5", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.5/32", + }, + .adjacencies = { + { + .hostname = "rt3", + .metric = 200, + }, + { + .hostname = "rt6", + .metric = 50, + }, + { + .hostname = "rt7", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt6", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x06}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.6", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.6/32", + }, + .adjacencies = { + { + .hostname = "rt4", + .metric = 10, + }, + { + .hostname = "rt5", + .metric = 50, + }, + { + .hostname = "rt8", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt7", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x07}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.7", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.7/32", + }, + .adjacencies = { + { + .hostname = "rt5", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt8", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x08}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.8", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.8/32", + }, + .adjacencies = { + { + .hostname = "rt6", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + }, + }, + { + .number = 5, + .nodes = { + { + .hostname = "rt1", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.1", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.1/32", + }, + .adjacencies = { + { + .hostname = "rt2", + .metric = 10, + }, + { + .hostname = "rt3", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt2", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.2", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.2/32", + }, + .adjacencies = { + { + .hostname = "rt1", + .metric = 10, + }, + { + .hostname = "rt4", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt3", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x03}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.3", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.3/32", + }, + .adjacencies = { + { + .hostname = "rt1", + .metric = 10, + }, + { + .hostname = "rt5", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt4", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x04}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.4", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.4/32", + }, + .adjacencies = { + { + .hostname = "rt2", + .metric = 10, + }, + { + .hostname = "rt6", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt5", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x05}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.5", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.5/32", + }, + .adjacencies = { + { + .hostname = "rt3", + .metric = 10, + }, + { + .hostname = "rt7", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt6", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x06}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.6", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.6/32", + }, + .adjacencies = { + { + .hostname = "rt4", + .metric = 10, + }, + { + .hostname = "rt8", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt7", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x07}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.7", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.7/32", + }, + .adjacencies = { + { + .hostname = "rt5", + .metric = 10, + }, + { + .hostname = "rt8", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt8", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x08}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.8", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.8/32", + }, + .adjacencies = { + { + .hostname = "rt6", + .metric = 10, + }, + { + .hostname = "rt7", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + }, + }, + { + .number = 6, + .nodes = { + { + .hostname = "rt1", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.1", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.1/32", + }, + .adjacencies = { + { + .hostname = "rt2", + .metric = 10, + }, + { + .hostname = "rt3", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt2", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.2", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.2/32", + }, + .adjacencies = { + { + .hostname = "rt1", + .metric = 10, + }, + { + .hostname = "rt4", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt3", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x03}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.3", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.3/32", + }, + .adjacencies = { + { + .hostname = "rt1", + .metric = 10, + }, + { + .hostname = "rt4", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt4", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x04}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.4", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.4/32", + }, + .adjacencies = { + { + .hostname = "rt2", + .metric = 10, + }, + { + .hostname = "rt3", + .metric = 10, + }, + { + .hostname = "rt6", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt5", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x05}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.5", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.5/32", + }, + .adjacencies = { + { + .hostname = "rt6", + .metric = 10, + }, + { + .hostname = "rt7", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt6", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x06}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.6", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.6/32", + }, + .adjacencies = { + { + .hostname = "rt4", + .metric = 10, + }, + { + .hostname = "rt5", + .metric = 10, + }, + { + .hostname = "rt8", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt7", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x07}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.7", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.7/32", + }, + .adjacencies = { + { + .hostname = "rt5", + .metric = 10, + }, + { + .hostname = "rt8", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt8", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x08}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.8", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.8/32", + }, + .adjacencies = { + { + .hostname = "rt6", + .metric = 10, + }, + { + .hostname = "rt7", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + }, + }, + { + .number = 7, + .nodes = { + { + .hostname = "rt1", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.1", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.1/32", + }, + .adjacencies = { + { + .hostname = "rt2", + .metric = 40, + }, + { + .hostname = "rt4", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt2", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.2", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.2/32", + }, + .adjacencies = { + { + .hostname = "rt1", + .metric = 40, + }, + { + .hostname = "rt3", + .metric = 10, + }, + { + .hostname = "rt5", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt3", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x03}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.3", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.3/32", + }, + .adjacencies = { + { + .hostname = "rt2", + .metric = 10, + }, + { + .hostname = "rt6", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt4", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x04}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.4", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.4/32", + }, + .adjacencies = { + { + .hostname = "rt1", + .metric = 10, + }, + { + .hostname = "rt5", + .metric = 10, + }, + { + .hostname = "rt7", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt5", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x05}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.5", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.5/32", + }, + .adjacencies = { + { + .hostname = "rt2", + .metric = 10, + }, + { + .hostname = "rt4", + .metric = 10, + }, + { + .hostname = "rt6", + .metric = 10, + }, + { + .hostname = "rt8", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt6", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x06}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.6", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.6/32", + }, + .adjacencies = { + { + .hostname = "rt3", + .metric = 10, + }, + { + .hostname = "rt5", + .metric = 10, + }, + { + .hostname = "rt9", + .metric = 30, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt7", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x07}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.7", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.7/32", + }, + .adjacencies = { + { + .hostname = "rt4", + .metric = 10, + }, + { + .hostname = "rt8", + .metric = 10, + }, + { + .hostname = "rt10", + .metric = 20, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt8", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x08}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.8", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.8/32", + }, + .adjacencies = { + { + .hostname = "rt5", + .metric = 10, + }, + { + .hostname = "rt7", + .metric = 10, + }, + { + .hostname = "rt9", + .metric = 10, + }, + { + .hostname = "rt11", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt9", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x09}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.9", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.9/32", + }, + .adjacencies = { + { + .hostname = "rt6", + .metric = 30, + }, + { + .hostname = "rt8", + .metric = 10, + }, + { + .hostname = "rt12", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt10", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x0a}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.10", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.10/32", + }, + .adjacencies = { + { + .hostname = "rt7", + .metric = 20, + }, + { + .hostname = "rt11", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt11", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x0b}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.11", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.11/32", + }, + .adjacencies = { + { + .hostname = "rt8", + .metric = 10, + }, + { + .hostname = "rt10", + .metric = 10, + }, + { + .hostname = "rt12", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt12", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x0c}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.12", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.12/32", + }, + .adjacencies = { + { + .hostname = "rt9", + .metric = 10, + }, + { + .hostname = "rt11", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + }, + }, + { + .number = 8, + .nodes = { + { + .hostname = "rt1", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.1", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.1/32", + }, + .adjacencies = { + { + .hostname = "rt2", + .metric = 10, + }, + { + .hostname = "rt4", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt2", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.2", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.2/32", + }, + .adjacencies = { + { + .hostname = "rt1", + .metric = 10, + }, + { + .hostname = "rt3", + .metric = 10, + }, + { + .hostname = "rt5", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt3", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x03}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.3", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.3/32", + }, + .adjacencies = { + { + .hostname = "rt2", + .metric = 10, + }, + { + .hostname = "rt6", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt4", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x04}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.4", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.4/32", + }, + .adjacencies = { + { + .hostname = "rt1", + .metric = 10, + }, + { + .hostname = "rt7", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt5", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x05}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.5", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.5/32", + }, + .adjacencies = { + { + .hostname = "rt2", + .metric = 10, + }, + { + .hostname = "rt6", + .metric = 10, + }, + { + .hostname = "rt8", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt6", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x06}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.6", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.6/32", + }, + .adjacencies = { + { + .hostname = "rt3", + .metric = 10, + }, + { + .hostname = "rt5", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt7", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x07}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.7", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.7/32", + }, + .adjacencies = { + { + .hostname = "rt4", + .metric = 10, + }, + { + .hostname = "rt10", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt8", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x08}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.8", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.8/32", + }, + .adjacencies = { + { + .hostname = "rt5", + .metric = 10, + }, + { + .hostname = "rt9", + .metric = 10, + }, + { + .hostname = "rt11", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt9", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x09}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.9", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.9/32", + }, + .adjacencies = { + { + .hostname = "rt8", + .metric = 10, + }, + { + .hostname = "rt12", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt10", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x0a}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.10", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.10/32", + }, + .adjacencies = { + { + .hostname = "rt7", + .metric = 10, + }, + { + .hostname = "rt11", + .metric = 30, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt11", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x0b}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.11", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.11/32", + }, + .adjacencies = { + { + .hostname = "rt8", + .metric = 10, + }, + { + .hostname = "rt10", + .metric = 30, + }, + { + .hostname = "rt12", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt12", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x0c}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.12", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.12/32", + }, + .adjacencies = { + { + .hostname = "rt9", + .metric = 10, + }, + { + .hostname = "rt11", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + }, + }, + { + .number = 9, + .nodes = { + { + .hostname = "rt1", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.1", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.1/32", + "2001:db8::1/128", + }, + .adjacencies = { + { + .hostname = "rt2", + .metric = 10, + }, + { + .hostname = "rt3", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt2", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.2", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.2/32", + "2001:db8::2/128", + }, + .adjacencies = { + { + .hostname = "rt1", + .metric = 10, + }, + { + .hostname = "rt4", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt3", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x03}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.3", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.3/32", + "2001:db8::3/128", + }, + .adjacencies = { + { + .hostname = "rt1", + .metric = 10, + }, + { + .hostname = "rt4", + .metric = 100, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt4", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x04}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.4", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.4/32", + "2001:db8::4/128", + }, + .adjacencies = { + { + .hostname = "rt2", + .metric = 10, + }, + { + .hostname = "rt3", + .metric = 100, + }, + { + .hostname = "rt5", + .metric = 10, + }, + { + .hostname = "rt6", + .metric = 30, + }, + { + .hostname = "rt7", + .metric = 30, + }, + { + .hostname = "rt8", + .metric = 30, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt5", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x05}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.5", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.5/32", + "2001:db8::5/128", + }, + .adjacencies = { + { + .hostname = "rt4", + .metric = 10, + }, + { + .hostname = "rt9", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt6", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x06}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.6", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.6/32", + "2001:db8::6/128", + }, + .adjacencies = { + { + .hostname = "rt4", + .metric = 30, + }, + { + .hostname = "rt9", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt7", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x07}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.7", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.7/32", + "2001:db8::7/128", + }, + .adjacencies = { + { + .hostname = "rt4", + .metric = 30, + }, + { + .hostname = "rt9", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt8", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x08}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.8", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.8/32", + "2001:db8::8/128", + }, + .adjacencies = { + { + .hostname = "rt4", + .metric = 30, + }, + { + .hostname = "rt9", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt9", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x09}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.9", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.9/32", + "2001:db8::9/128", + }, + .adjacencies = { + { + .hostname = "rt5", + .metric = 10, + }, + { + .hostname = "rt6", + .metric = 10, + }, + { + .hostname = "rt7", + .metric = 10, + }, + { + .hostname = "rt8", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + }, + }, + { + .number = 10, + .nodes = { + { + .hostname = "rt1", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.1", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.1/32", + "2001:db8::1/128", + }, + .adjacencies = { + { + .hostname = "rt2", + .metric = 10, + }, + { + .hostname = "rt3", + .metric = 20, + }, + { + .hostname = "rt4", + .metric = 20, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt2", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.2", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.2/32", + "2001:db8::2/128", + }, + .adjacencies = { + { + .hostname = "rt1", + .metric = 10, + }, + { + .hostname = "rt5", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt3", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x03}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.3", + .srgb = { + .lower_bound = 20000, + .range_size = 8000, + }, + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.3/32", + "2001:db8::3/128", + }, + .adjacencies = { + { + .hostname = "rt1", + .metric = 20, + }, + { + .hostname = "rt6", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt4", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x04}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.4", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.4/32", + "2001:db8::4/128", + }, + .adjacencies = { + { + .hostname = "rt1", + .metric = 20, + }, + { + .hostname = "rt7", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt5", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x05}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.5", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.5/32", + "2001:db8::5/128", + }, + .adjacencies = { + { + .hostname = "rt2", + .metric = 10, + }, + { + .hostname = "rt8", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt6", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x06}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.6", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.6/32", + "2001:db8::6/128", + }, + .adjacencies = { + { + .hostname = "rt3", + .metric = 10, + }, + { + .hostname = "rt8", + .metric = 50, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt7", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x07}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.7", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.7/32", + "2001:db8::7/128", + }, + .adjacencies = { + { + .hostname = "rt4", + .metric = 10, + }, + { + .hostname = "rt8", + .metric = 50, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt8", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x08}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.8", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.8/32", + "2001:db8::8/128", + }, + .adjacencies = { + { + .hostname = "rt5", + .metric = 10, + }, + { + .hostname = "rt6", + .metric = 50, + }, + { + .hostname = "rt7", + .metric = 50, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + }, + }, + { + .number = 11, + .nodes = { + { + .hostname = "rt1", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.1", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.1/32", + "2001:db8::1/128", + }, + .adjacencies = { + { + .hostname = "rt2", + .pseudonode_id = 1, + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt2", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.2", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.2/32", + "2001:db8::2/128", + }, + .adjacencies = { + { + .hostname = "rt2", + .pseudonode_id = 1, + .metric = 50, + }, + { + .hostname = "rt4", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt3", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x03}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.3", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.3/32", + "2001:db8::3/128", + }, + .adjacencies = { + { + .hostname = "rt2", + .pseudonode_id = 1, + .metric = 10, + }, + { + .hostname = "rt5", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt4", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x04}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.4", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.4/32", + "2001:db8::4/128", + }, + .adjacencies = { + { + .hostname = "rt2", + .metric = 10, + }, + { + .hostname = "rt5", + .metric = 10, + }, + { + .hostname = "rt6", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt5", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x05}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.5", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.5/32", + "2001:db8::5/128", + }, + .adjacencies = { + { + .hostname = "rt3", + .metric = 10, + }, + { + .hostname = "rt4", + .metric = 10, + }, + { + .hostname = "rt6", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt6", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x06}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.6", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.6/32", + "2001:db8::6/128", + }, + .adjacencies = { + { + .hostname = "rt4", + .metric = 10, + }, + { + .hostname = "rt5", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt2", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + .pseudonode_id = 1, + .level = IS_LEVEL_1, + .adjacencies = { + { + .hostname = "rt1", + .metric = 0, + }, + { + .hostname = "rt2", + .metric = 0, + }, + { + .hostname = "rt3", + .metric = 0, + }, + }, + }, + }, + }, + { + .number = 12, + .nodes = { + { + .hostname = "rt1", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.1", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.1/32", + }, + .adjacencies = { + { + .hostname = "rt2", + .metric = 10, + }, + { + .hostname = "rt3", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt2", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.2", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.2/32", + }, + .adjacencies = { + { + .hostname = "rt1", + .metric = 10, + }, + { + .hostname = "rt4", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt3", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x03}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.3", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.3/32", + }, + .adjacencies = { + { + .hostname = "rt1", + .metric = 10, + }, + { + .hostname = "rt5", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt4", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x04}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.4", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.4/32", + }, + .adjacencies = { + { + .hostname = "rt2", + .metric = 10, + }, + { + .hostname = "rt6", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt5", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x05}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.5", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.5/32", + }, + .adjacencies = { + { + .hostname = "rt3", + .metric = 400, + }, + { + .hostname = "rt7", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt6", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x06}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.6", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.6/32", + }, + .adjacencies = { + { + .hostname = "rt4", + .metric = 10, + }, + { + .hostname = "rt8", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt7", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x07}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.7", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.7/32", + }, + .adjacencies = { + { + .hostname = "rt5", + .metric = 200, + }, + { + .hostname = "rt8", + .metric = 100, + }, + { + .hostname = "rt9", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt8", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x08}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.8", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.8/32", + }, + .adjacencies = { + { + .hostname = "rt6", + .metric = 10, + }, + { + .hostname = "rt7", + .metric = 100, + }, + { + .hostname = "rt10", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt9", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x09}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.9", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.9/32", + }, + .adjacencies = { + { + .hostname = "rt7", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt10", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x0a}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.10", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.10/32", + }, + .adjacencies = { + { + .hostname = "rt8", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + }, + }, + { + .number = 13, + .nodes = { + { + .hostname = "rt1", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.1", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.1/32", + }, + .adjacencies = { + { + .hostname = "rt2", + .metric = 10, + }, + { + .hostname = "rt3", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt2", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.2", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.2/32", + }, + .adjacencies = { + { + .hostname = "rt1", + .metric = 10, + }, + { + .hostname = "rt4", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt3", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x03}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.3", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.3/32", + }, + .adjacencies = { + { + .hostname = "rt1", + .metric = 10, + }, + { + .hostname = "rt4", + .metric = 10, + }, + { + .hostname = "rt5", + .metric = 10, + }, + { + .hostname = "rt6", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt4", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x04}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.4", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.4/32", + }, + .adjacencies = { + { + .hostname = "rt2", + .metric = 10, + }, + { + .hostname = "rt3", + .metric = 10, + }, + { + .hostname = "rt5", + .metric = 100, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt5", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x05}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.5", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.5/32", + }, + .adjacencies = { + { + .hostname = "rt3", + .metric = 10, + }, + { + .hostname = "rt4", + .metric = 100, + }, + { + .hostname = "rt7", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt6", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x06}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.6", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.6/32", + }, + .adjacencies = { + { + .hostname = "rt3", + .metric = 10, + }, + { + .hostname = "rt7", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt7", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x07}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.7", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.7/32", + }, + .adjacencies = { + { + .hostname = "rt5", + .metric = 10, + }, + { + .hostname = "rt6", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + }, + }, + { + /* sentinel */ + }, +}; diff --git a/tests/lib/cli/common_cli.c b/tests/lib/cli/common_cli.c index 393b588745..44cc6efe84 100644 --- a/tests/lib/cli/common_cli.c +++ b/tests/lib/cli/common_cli.c @@ -25,7 +25,7 @@ #include "vty.h" #include "command.h" #include "memory.h" -#include "memory_vty.h" +#include "lib_vty.h" #include "log.h" #include "common_cli.h" @@ -53,7 +53,6 @@ static void vty_do_exit(int isexit) nb_terminate(); yang_terminate(); thread_master_free(master); - closezlog(); log_memstats(stderr, "testcli"); if (!isexit) @@ -71,21 +70,17 @@ int main(int argc, char **argv) /* master init. */ master = thread_master_create(NULL); - openzlog("common-cli", "NONE", 0, LOG_CONS | LOG_NDELAY | LOG_PID, - LOG_DAEMON); - zlog_set_level(ZLOG_DEST_SYSLOG, ZLOG_DISABLED); - zlog_set_level(ZLOG_DEST_STDOUT, ZLOG_DISABLED); - zlog_set_level(ZLOG_DEST_MONITOR, LOG_DEBUG); + zlog_aux_init("NONE: ", ZLOG_DISABLED); /* Library inits. */ cmd_init(1); cmd_hostname_set("test"); cmd_domainname_set("test.domain"); - vty_init(master); - memory_init(); - yang_init(); - nb_init(master, NULL, 0); + vty_init(master, false); + lib_cmd_init(); + yang_init(true); + nb_init(master, NULL, 0, false); test_init(argc, argv); diff --git a/tests/lib/cli/test_commands.c b/tests/lib/cli/test_commands.c index ba46bdcea9..cb512211a4 100644 --- a/tests/lib/cli/test_commands.c +++ b/tests/lib/cli/test_commands.c @@ -49,50 +49,116 @@ static vector test_cmds; static char test_buf[32768]; static struct cmd_node bgp_node = { - BGP_NODE, "%s(config-router)# ", + .name = "bgp", + .node = BGP_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-router)# ", }; static struct cmd_node rip_node = { - RIP_NODE, "%s(config-router)# ", + .name = "rip", + .node = RIP_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-router)# ", }; static struct cmd_node isis_node = { - ISIS_NODE, "%s(config-router)# ", + .name = "isis", + .node = ISIS_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-router)# ", }; static struct cmd_node interface_node = { - INTERFACE_NODE, "%s(config-if)# ", + .name = "interface", + .node = INTERFACE_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-if)# ", }; -static struct cmd_node rmap_node = {RMAP_NODE, "%s(config-route-map)# "}; +static struct cmd_node rmap_node = { + .name = "routemap", + .node = RMAP_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-route-map)# ", +}; -static struct cmd_node zebra_node = {ZEBRA_NODE, "%s(config-router)# "}; +static struct cmd_node zebra_node = { + .name = "zebra", + .node = ZEBRA_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-router)# ", +}; -static struct cmd_node bgp_vpnv4_node = {BGP_VPNV4_NODE, - "%s(config-router-af)# "}; +static struct cmd_node bgp_vpnv4_node = { + .name = "bgp vpnv4", + .node = BGP_VPNV4_NODE, + .parent_node = BGP_NODE, + .prompt = "%s(config-router-af)# ", +}; -static struct cmd_node bgp_ipv4_node = {BGP_IPV4_NODE, - "%s(config-router-af)# "}; +static struct cmd_node bgp_ipv4_node = { + .name = "bgp ipv4 unicast", + .node = BGP_IPV4_NODE, + .parent_node = BGP_NODE, + .prompt = "%s(config-router-af)# ", +}; -static struct cmd_node bgp_ipv4m_node = {BGP_IPV4M_NODE, - "%s(config-router-af)# "}; +static struct cmd_node bgp_ipv4m_node = { + .name = "bgp ipv4 multicast", + .node = BGP_IPV4M_NODE, + .parent_node = BGP_NODE, + .prompt = "%s(config-router-af)# ", +}; -static struct cmd_node bgp_ipv6_node = {BGP_IPV6_NODE, - "%s(config-router-af)# "}; +static struct cmd_node bgp_ipv6_node = { + .name = "bgp ipv6", + .node = BGP_IPV6_NODE, + .parent_node = BGP_NODE, + .prompt = "%s(config-router-af)# ", +}; -static struct cmd_node bgp_ipv6m_node = {BGP_IPV6M_NODE, - "%s(config-router-af)# "}; +static struct cmd_node bgp_ipv6m_node = { + .name = "bgp ipv6 multicast", + .node = BGP_IPV6M_NODE, + .parent_node = BGP_NODE, + .prompt = "%s(config-router-af)# ", +}; -static struct cmd_node ospf_node = {OSPF_NODE, "%s(config-router)# "}; +static struct cmd_node ospf_node = { + .name = "ospf", + .node = OSPF_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-router)# ", +}; -static struct cmd_node ripng_node = {RIPNG_NODE, "%s(config-router)# "}; +static struct cmd_node ripng_node = { + .name = "ripng", + .node = RIPNG_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-router)# ", +}; -static struct cmd_node ospf6_node = {OSPF6_NODE, "%s(config-ospf6)# "}; +static struct cmd_node ospf6_node = { + .name = "ospf6", + .node = OSPF6_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-ospf6)# ", +}; -static struct cmd_node keychain_node = {KEYCHAIN_NODE, "%s(config-keychain)# "}; +static struct cmd_node keychain_node = { + .name = "keychain", + .node = KEYCHAIN_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-keychain)# ", +}; -static struct cmd_node keychain_key_node = {KEYCHAIN_KEY_NODE, - "%s(config-keychain-key)# "}; +static struct cmd_node keychain_key_node = { + .name = "keychain key", + .node = KEYCHAIN_KEY_NODE, + .parent_node = KEYCHAIN_NODE, + .prompt = "%s(config-keychain-key)# ", +}; static int test_callback(const struct cmd_element *cmd, struct vty *vty, int argc, struct cmd_token *argv[]) @@ -142,26 +208,26 @@ static void test_init(void) struct cmd_element *cmd; cmd_init(1); - yang_init(); - nb_init(master, NULL, 0); - - install_node(&bgp_node, NULL); - install_node(&rip_node, NULL); - install_node(&interface_node, NULL); - install_node(&rmap_node, NULL); - install_node(&zebra_node, NULL); - install_node(&bgp_vpnv4_node, NULL); - install_node(&bgp_ipv4_node, NULL); - install_node(&bgp_ipv4m_node, NULL); - install_node(&bgp_ipv6_node, NULL); - install_node(&bgp_ipv6m_node, NULL); - install_node(&ospf_node, NULL); - install_node(&ripng_node, NULL); - install_node(&ospf6_node, NULL); - install_node(&keychain_node, NULL); - install_node(&keychain_key_node, NULL); - install_node(&isis_node, NULL); - install_node(&vty_node, NULL); + yang_init(true); + nb_init(master, NULL, 0, false); + + install_node(&bgp_node); + install_node(&rip_node); + install_node(&interface_node); + install_node(&rmap_node); + install_node(&zebra_node); + install_node(&bgp_vpnv4_node); + install_node(&bgp_ipv4_node); + install_node(&bgp_ipv4m_node); + install_node(&bgp_ipv6_node); + install_node(&bgp_ipv6m_node); + install_node(&ospf_node); + install_node(&ripng_node); + install_node(&ospf6_node); + install_node(&keychain_node); + install_node(&keychain_key_node); + install_node(&isis_node); + install_node(&vty_node); test_init_cmd(); @@ -243,7 +309,8 @@ static void test_run(struct prng *prng, struct vty *vty, const char *cmd, (test_buf[0] != '\0') ? ", " : "", test_buf); - if (isspace((int)test_str[strlen(test_str) - 1])) { + if (isspace((unsigned char)test_str[ + strlen(test_str) - 1])) { vector_set(vline, NULL); appended_null = 1; } diff --git a/tests/lib/cxxcompat.c b/tests/lib/cxxcompat.c index 12c333c8b6..391ccd9268 100644 --- a/tests/lib/cxxcompat.c +++ b/tests/lib/cxxcompat.c @@ -17,6 +17,8 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#define test__cplusplus + #include "lib/zebra.h" #include "lib/agg_table.h" @@ -49,14 +51,13 @@ #include "lib/json.h" #include "lib/keychain.h" #include "lib/lib_errors.h" +#include "lib/lib_vty.h" #include "lib/libfrr.h" #include "lib/libospf.h" #include "lib/linklist.h" #include "lib/log.h" -#include "lib/logicalrouter.h" #include "lib/md5.h" #include "lib/memory.h" -#include "lib/memory_vty.h" #include "lib/mlag.h" #include "lib/module.h" #include "lib/monotime.h" @@ -71,7 +72,6 @@ #include "lib/openbsd-tree.h" #include "lib/pbr.h" #include "lib/plist.h" -#include "lib/pqueue.h" #include "lib/prefix.h" #include "lib/privs.h" #include "lib/ptm_lib.h" @@ -92,6 +92,8 @@ #include "lib/table.h" #include "lib/termtable.h" #include "lib/thread.h" +#include "lib/typesafe.h" +#include "lib/typerb.h" #include "lib/vector.h" #include "lib/vlan.h" #include "lib/vrf.h" @@ -105,6 +107,17 @@ #include "lib/zassert.h" #include "lib/zclient.h" +PREDECL_RBTREE_UNIQ(footree) +struct foo { + int dummy; + struct footree_item item; +}; +static int foocmp(const struct foo *a, const struct foo *b) +{ + return memcmp(&a->dummy, &b->dummy, sizeof(a->dummy)); +} +DECLARE_RBTREE_UNIQ(footree, struct foo, item, foocmp) + int main(int argc, char **argv) { return 0; diff --git a/tests/lib/northbound/test_oper_data.c b/tests/lib/northbound/test_oper_data.c index 7cd622854e..b5f257fa2f 100644 --- a/tests/lib/northbound/test_oper_data.c +++ b/tests/lib/northbound/test_oper_data.c @@ -23,7 +23,7 @@ #include "vty.h" #include "command.h" #include "memory.h" -#include "memory_vty.h" +#include "lib_vty.h" #include "log.h" #include "northbound.h" @@ -49,41 +49,38 @@ static struct list *vrfs; * XPath: /frr-test-module:frr-test-module/vrfs/vrf */ static const void * -frr_test_module_vrfs_vrf_get_next(const void *parent_list_entry, - const void *list_entry) +frr_test_module_vrfs_vrf_get_next(struct nb_cb_get_next_args *args) { struct listnode *node; - if (list_entry == NULL) + if (args->list_entry == NULL) node = listhead(vrfs); else - node = listnextnode((struct listnode *)list_entry); + node = listnextnode((struct listnode *)args->list_entry); return node; } -static int frr_test_module_vrfs_vrf_get_keys(const void *list_entry, - struct yang_list_keys *keys) +static int frr_test_module_vrfs_vrf_get_keys(struct nb_cb_get_keys_args *args) { const struct tvrf *vrf; - vrf = listgetdata((struct listnode *)list_entry); + vrf = listgetdata((struct listnode *)args->list_entry); - keys->num = 1; - strlcpy(keys->key[0], vrf->name, sizeof(keys->key[0])); + args->keys->num = 1; + strlcpy(args->keys->key[0], vrf->name, sizeof(args->keys->key[0])); return NB_OK; } static const void * -frr_test_module_vrfs_vrf_lookup_entry(const void *parent_list_entry, - const struct yang_list_keys *keys) +frr_test_module_vrfs_vrf_lookup_entry(struct nb_cb_lookup_entry_args *args) { struct listnode *node; struct tvrf *vrf; const char *vrfname; - vrfname = keys->key[0]; + vrfname = args->keys->key[0]; for (ALL_LIST_ELEMENTS_RO(vrfs, node, vrf)) { if (strmatch(vrf->name, vrfname)) @@ -97,39 +94,37 @@ frr_test_module_vrfs_vrf_lookup_entry(const void *parent_list_entry, * XPath: /frr-test-module:frr-test-module/vrfs/vrf/name */ static struct yang_data * -frr_test_module_vrfs_vrf_name_get_elem(const char *xpath, - const void *list_entry) +frr_test_module_vrfs_vrf_name_get_elem(struct nb_cb_get_elem_args *args) { const struct tvrf *vrf; - vrf = listgetdata((struct listnode *)list_entry); - return yang_data_new_string(xpath, vrf->name); + vrf = listgetdata((struct listnode *)args->list_entry); + return yang_data_new_string(args->xpath, vrf->name); } /* * XPath: /frr-test-module:frr-test-module/vrfs/vrf/interfaces/interface */ -static struct yang_data * -frr_test_module_vrfs_vrf_interfaces_interface_get_elem(const char *xpath, - const void *list_entry) +static struct yang_data *frr_test_module_vrfs_vrf_interfaces_interface_get_elem( + struct nb_cb_get_elem_args *args) { const char *interface; - interface = listgetdata((struct listnode *)list_entry); - return yang_data_new_string(xpath, interface); + interface = listgetdata((struct listnode *)args->list_entry); + return yang_data_new_string(args->xpath, interface); } static const void *frr_test_module_vrfs_vrf_interfaces_interface_get_next( - const void *parent_list_entry, const void *list_entry) + struct nb_cb_get_next_args *args) { const struct tvrf *vrf; struct listnode *node; - vrf = listgetdata((struct listnode *)parent_list_entry); - if (list_entry == NULL) + vrf = listgetdata((struct listnode *)args->parent_list_entry); + if (args->list_entry == NULL) node = listhead(vrf->interfaces); else - node = listnextnode((struct listnode *)list_entry); + node = listnextnode((struct listnode *)args->list_entry); return node; } @@ -138,17 +133,16 @@ static const void *frr_test_module_vrfs_vrf_interfaces_interface_get_next( * XPath: /frr-test-module:frr-test-module/vrfs/vrf/routes/route */ static const void * -frr_test_module_vrfs_vrf_routes_route_get_next(const void *parent_list_entry, - const void *list_entry) +frr_test_module_vrfs_vrf_routes_route_get_next(struct nb_cb_get_next_args *args) { const struct tvrf *vrf; struct listnode *node; - vrf = listgetdata((struct listnode *)parent_list_entry); - if (list_entry == NULL) + vrf = listgetdata((struct listnode *)args->parent_list_entry); + if (args->list_entry == NULL) node = listhead(vrf->routes); else - node = listnextnode((struct listnode *)list_entry); + node = listnextnode((struct listnode *)args->list_entry); return node; } @@ -156,67 +150,64 @@ frr_test_module_vrfs_vrf_routes_route_get_next(const void *parent_list_entry, /* * XPath: /frr-test-module:frr-test-module/vrfs/vrf/routes/route/prefix */ -static struct yang_data * -frr_test_module_vrfs_vrf_routes_route_prefix_get_elem(const char *xpath, - const void *list_entry) +static struct yang_data *frr_test_module_vrfs_vrf_routes_route_prefix_get_elem( + struct nb_cb_get_elem_args *args) { const struct troute *route; - route = listgetdata((struct listnode *)list_entry); - return yang_data_new_ipv4p(xpath, &route->prefix); + route = listgetdata((struct listnode *)args->list_entry); + return yang_data_new_ipv4p(args->xpath, &route->prefix); } /* * XPath: /frr-test-module:frr-test-module/vrfs/vrf/routes/route/next-hop */ static struct yang_data * -frr_test_module_vrfs_vrf_routes_route_next_hop_get_elem(const char *xpath, - const void *list_entry) +frr_test_module_vrfs_vrf_routes_route_next_hop_get_elem( + struct nb_cb_get_elem_args *args) { const struct troute *route; - route = listgetdata((struct listnode *)list_entry); - return yang_data_new_ipv4(xpath, &route->nexthop); + route = listgetdata((struct listnode *)args->list_entry); + return yang_data_new_ipv4(args->xpath, &route->nexthop); } /* * XPath: /frr-test-module:frr-test-module/vrfs/vrf/routes/route/interface */ static struct yang_data * -frr_test_module_vrfs_vrf_routes_route_interface_get_elem(const char *xpath, - const void *list_entry) +frr_test_module_vrfs_vrf_routes_route_interface_get_elem( + struct nb_cb_get_elem_args *args) { const struct troute *route; - route = listgetdata((struct listnode *)list_entry); - return yang_data_new_string(xpath, route->ifname); + route = listgetdata((struct listnode *)args->list_entry); + return yang_data_new_string(args->xpath, route->ifname); } /* * XPath: /frr-test-module:frr-test-module/vrfs/vrf/routes/route/metric */ -static struct yang_data * -frr_test_module_vrfs_vrf_routes_route_metric_get_elem(const char *xpath, - const void *list_entry) +static struct yang_data *frr_test_module_vrfs_vrf_routes_route_metric_get_elem( + struct nb_cb_get_elem_args *args) { const struct troute *route; - route = listgetdata((struct listnode *)list_entry); - return yang_data_new_uint8(xpath, route->metric); + route = listgetdata((struct listnode *)args->list_entry); + return yang_data_new_uint8(args->xpath, route->metric); } /* * XPath: /frr-test-module:frr-test-module/vrfs/vrf/routes/route/active */ -static struct yang_data * -frr_test_module_vrfs_vrf_routes_route_active_get_elem(const char *xpath, - const void *list_entry) +static struct yang_data *frr_test_module_vrfs_vrf_routes_route_active_get_elem( + struct nb_cb_get_elem_args *args) { const struct troute *route; - route = listgetdata((struct listnode *)list_entry); + route = listgetdata((struct listnode *)args->list_entry); if (route->active) - return yang_data_new(xpath, NULL); + return yang_data_new(args->xpath, NULL); return NULL; } @@ -271,7 +262,7 @@ const struct frr_yang_module_info frr_test_module_info = { }; /* clang-format on */ -static const struct frr_yang_module_info *modules[] = { +static const struct frr_yang_module_info *const modules[] = { &frr_test_module_info, }; @@ -374,7 +365,6 @@ static void vty_do_exit(int isexit) nb_terminate(); yang_terminate(); thread_master_free(master); - closezlog(); log_memstats(stderr, "test-nb-oper-data"); if (!isexit) @@ -402,19 +392,15 @@ int main(int argc, char **argv) /* master init. */ master = thread_master_create(NULL); - openzlog("test-nb-oper-data", "NONE", 0, - LOG_CONS | LOG_NDELAY | LOG_PID, LOG_DAEMON); - zlog_set_level(ZLOG_DEST_SYSLOG, ZLOG_DISABLED); - zlog_set_level(ZLOG_DEST_STDOUT, ZLOG_DISABLED); - zlog_set_level(ZLOG_DEST_MONITOR, LOG_DEBUG); + zlog_aux_init("NONE: ", ZLOG_DISABLED); /* Library inits. */ cmd_init(1); cmd_hostname_set("test"); - vty_init(master); - memory_init(); - yang_init(); - nb_init(master, modules, array_size(modules)); + vty_init(master, false); + lib_cmd_init(); + yang_init(true); + nb_init(master, modules, array_size(modules), false); /* Create artificial data. */ create_data(num_vrfs, num_interfaces, num_routes); diff --git a/tests/lib/test_atomlist.c b/tests/lib/test_atomlist.c index 078e05e336..40837b4722 100644 --- a/tests/lib/test_atomlist.c +++ b/tests/lib/test_atomlist.c @@ -29,6 +29,7 @@ #include "atomlist.h" #include "seqlock.h" #include "monotime.h" +#include "printfrr.h" /* * maybe test: @@ -253,7 +254,7 @@ static void *thr1func(void *arg) struct testrun *tr; for (tr = runs; tr; tr = tr->next) { - sv = seqlock_bump(&p->sqlo); + sv = seqlock_bump(&p->sqlo) - SEQLOCK_INCR; seqlock_wait(&sqlo, sv); tr->func(offset); @@ -288,14 +289,14 @@ static void run_tr(struct testrun *tr) size_t c = 0, s = 0, n = 0; struct item *item, *prev, dummy; - printf("[%02u] %35s %s\n", seqlock_cur(&sqlo) >> 1, "", desc); + printfrr("[%02u] %35s %s\n", seqlock_cur(&sqlo) >> 2, "", desc); fflush(stdout); if (tr->prefill != NOCLEAR) clear_list(tr->prefill); monotime(&tv); - sv = seqlock_bump(&sqlo); + sv = seqlock_bump(&sqlo) - SEQLOCK_INCR; for (size_t i = 0; i < NTHREADS; i++) { seqlock_wait(&thr[i].sqlo, seqlock_cur(&sqlo)); s += thr[i].counter; @@ -308,7 +309,7 @@ static void run_tr(struct testrun *tr) if (tr->sorted) { uint64_t prevval = 0; - for_each(asort, &shead, item) { + frr_each(asort, &shead, item) { assert(item->val1 >= prevval); prevval = item->val1; c++; @@ -316,7 +317,7 @@ static void run_tr(struct testrun *tr) assert(c == asort_count(&shead)); } else { prev = &dummy; - for_each(alist, &ahead, item) { + frr_each(alist, &ahead, item) { assert(item != prev); prev = item; c++; @@ -324,8 +325,8 @@ static void run_tr(struct testrun *tr) } assert(c == alist_count(&ahead)); } - printf("\033[1A[%02u] %9"PRId64"us c=%5zu s=%5zu n=%5zu %s\n", - sv >> 1, delta, c, s, n, desc); + printfrr("\033[1A[%02u] %9"PRId64"us c=%5zu s=%5zu n=%5zu %s\n", + sv >> 2, delta, c, s, n, desc); } #ifdef BASIC_TESTS @@ -334,9 +335,9 @@ static void dump(const char *lbl) struct item *item, *safe; size_t ctr = 0; - printf("dumping %s:\n", lbl); - for_each_safe(alist, &ahead, item) { - printf("%s %3zu %p %3"PRIu64" %3"PRIu64"\n", lbl, ctr++, + printfrr("dumping %s:\n", lbl); + frr_each_safe(alist, &ahead, item) { + printfrr("%s %3zu %p %3"PRIu64" %3"PRIu64"\n", lbl, ctr++, (void *)item, item->val1, item->val2); } } @@ -362,12 +363,12 @@ static void basic_tests(void) dump(""); alist_del(&ahead, &itm[1]); dump(""); - printf("POP: %p\n", alist_pop(&ahead)); + printfrr("POP: %p\n", alist_pop(&ahead)); dump(""); - printf("POP: %p\n", alist_pop(&ahead)); - printf("POP: %p\n", alist_pop(&ahead)); - printf("POP: %p\n", alist_pop(&ahead)); - printf("POP: %p\n", alist_pop(&ahead)); + printfrr("POP: %p\n", alist_pop(&ahead)); + printfrr("POP: %p\n", alist_pop(&ahead)); + printfrr("POP: %p\n", alist_pop(&ahead)); + printfrr("POP: %p\n", alist_pop(&ahead)); dump(""); } #else @@ -381,7 +382,7 @@ int main(int argc, char **argv) basic_tests(); seqlock_init(&sqlo); - seqlock_acquire_val(&sqlo, 1); + seqlock_acquire_val(&sqlo, SEQLOCK_STARTVAL); for (i = 0; i < NTHREADS; i++) { seqlock_init(&thr[i].sqlo); diff --git a/tests/lib/test_buffer.c b/tests/lib/test_buffer.c index b56cc30cf3..7fb9a769d3 100644 --- a/tests/lib/test_buffer.c +++ b/tests/lib/test_buffer.c @@ -20,7 +20,7 @@ #include #include -#include +#include #include struct thread_master *master; @@ -32,7 +32,7 @@ int main(int argc, char **argv) char junk[3]; char c = 'a'; - memory_init(); + lib_cmd_init(); if ((argc != 2) || (sscanf(argv[1], "%d%1s", &n, junk) != 1)) { fprintf(stderr, "Usage: %s \n", diff --git a/tests/lib/test_checksum.c b/tests/lib/test_checksum.c index 13d35b0e99..301078867a 100644 --- a/tests/lib/test_checksum.c +++ b/tests/lib/test_checksum.c @@ -23,6 +23,7 @@ #include #include "checksum.h" +#include "network.h" struct thread_master *master; @@ -477,7 +478,7 @@ int main(int argc, char **argv) exercise %= MAXDATALEN; for (i = 0; i < exercise; i += sizeof(long int)) { - long int rand = random(); + long int rand = frr_weak_random(); for (j = sizeof(long int); j > 0; j--) buffer[i + (sizeof(long int) - j)] = @@ -488,8 +489,7 @@ int main(int argc, char **argv) in_csum_res = in_cksum_optimized(buffer, exercise); in_csum_rfc = in_cksum_rfc(buffer, exercise); if (in_csum_res != in_csum || in_csum != in_csum_rfc) - printf("verify: in_chksum failed in_csum:%x, in_csum_res:%x," - "in_csum_rfc %x, len:%d\n", + printf("verify: in_chksum failed in_csum:%x, in_csum_res:%x,in_csum_rfc %x, len:%d\n", in_csum, in_csum_res, in_csum_rfc, exercise); ospfd = ospfd_checksum(buffer, exercise + sizeof(uint16_t), diff --git a/tests/lib/test_heavy_wq.c b/tests/lib/test_heavy_wq.c index 442b8c8380..cffd52ee02 100644 --- a/tests/lib/test_heavy_wq.c +++ b/tests/lib/test_heavy_wq.c @@ -81,7 +81,6 @@ static void slow_func_del(struct work_queue *wq, void *data) assert(hn && hn->str); printf("%s: %s\n", __func__, hn->str); XFREE(MTYPE_WQ_NODE_STR, hn->str); - hn->str = NULL; XFREE(MTYPE_WQ_NODE, hn); } diff --git a/tests/lib/test_idalloc.c b/tests/lib/test_idalloc.c index 3053c1c074..ce1582b1ba 100644 --- a/tests/lib/test_idalloc.c +++ b/tests/lib/test_idalloc.c @@ -1,3 +1,7 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include "id_alloc.h" #include diff --git a/tests/lib/test_ntop.c b/tests/lib/test_ntop.c new file mode 100644 index 0000000000..0a55ecddb8 --- /dev/null +++ b/tests/lib/test_ntop.c @@ -0,0 +1,86 @@ +/* + * frr_inet_ntop() unit test + * Copyright (C) 2019 David Lamparter + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "tests/helpers/c/prng.h" + +/* NB: libfrr is NOT linked for this unit test! */ + +#define INET_NTOP_NO_OVERRIDE +#include "lib/ntop.c" + +int main(int argc, char **argv) +{ + size_t i, j, k, l; + struct in_addr i4; + struct in6_addr i6, i6check; + char buf1[64], buf2[64]; + const char *rv; + struct prng *prng; + + prng = prng_new(0); + /* IPv4 */ + for (i = 0; i < 1000; i++) { + i4.s_addr = prng_rand(prng); + assert(frr_inet_ntop(AF_INET, &i4, buf1, sizeof(buf1))); + assert(inet_ntop(AF_INET, &i4, buf2, sizeof(buf2))); + assert(!strcmp(buf1, buf2)); + } + + /* check size limit */ + for (i = 0; i < sizeof(buf1); i++) { + memset(buf2, 0xcc, sizeof(buf2)); + rv = frr_inet_ntop(AF_INET, &i4, buf2, i); + if (i < strlen(buf1) + 1) + assert(!rv); + else + assert(rv && !strcmp(buf1, buf2)); + } + + /* IPv6 */ + for (i = 0; i < 10000; i++) { + uint16_t *i6w = (uint16_t *)&i6; + for (j = 0; j < 8; j++) + i6w[j] = prng_rand(prng); + + /* clear some words */ + l = prng_rand(prng) & 7; + for (j = 0; j < l; j++) { + uint32_t num = __builtin_ctz(prng_rand(prng)); + uint32_t where = prng_rand(prng) & 7; + + for (k = where; k < where + num && k < 8; k++) + i6w[k] = 0; + } + + assert(frr_inet_ntop(AF_INET6, &i6, buf1, sizeof(buf1))); + assert(inet_ntop(AF_INET6, &i6, buf2, sizeof(buf2))); + if (strcmp(buf1, buf2)) + printf("%-40s (FRR) != (SYS) %-40s\n", buf1, buf2); + + assert(inet_pton(AF_INET6, buf1, &i6check)); + assert(!memcmp(&i6, &i6check, sizeof(i6))); + } + return 0; +} diff --git a/tests/lib/test_ntop.py b/tests/lib/test_ntop.py new file mode 100644 index 0000000000..2526f53db5 --- /dev/null +++ b/tests/lib/test_ntop.py @@ -0,0 +1,6 @@ +import frrtest + +class TestNtop(frrtest.TestMultiOut): + program = './test_ntop' + +TestNtop.exit_cleanly() diff --git a/tests/lib/test_prefix2str.c b/tests/lib/test_prefix2str.c new file mode 100644 index 0000000000..cbfc20a797 --- /dev/null +++ b/tests/lib/test_prefix2str.c @@ -0,0 +1,80 @@ +/* + * prefix2str() unit test + * Copyright (C) 2019 David Lamparter + * Portions: + * Copyright (C) 2019 Cumulus Networks, Inc + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include + +#include "lib/prefix.h" + +#include "tests/helpers/c/prng.h" + +int main(int argc, char **argv) +{ + size_t i, j, k, l; + struct in6_addr i6; + char buf1[64], buf2[64], ntopbuf[64]; + struct prng *prng; + struct prefix p = {}; + + prng = prng_new(0); + /* IPv4 */ + p.family = AF_INET; + for (i = 0; i < 1000; i++) { + p.u.prefix = prng_rand(prng); + p.prefixlen = prng_rand(prng) >> 26; + snprintf(buf1, sizeof(buf1), "%s/%d", + inet_ntop(AF_INET, &p.u.prefix4, ntopbuf, + sizeof(ntopbuf)), + p.prefixlen); + prefix2str(&p, buf2, sizeof(buf2)); + assert(!strcmp(buf1, buf2)); + fprintf(stdout, "%s\n", buf1); + } + + /* IPv6 */ + p.family = AF_INET6; + for (i = 0; i < 10000; i++) { + uint16_t *i6w = (uint16_t *)&i6; + for (j = 0; j < 8; j++) + i6w[j] = prng_rand(prng); + + /* clear some words */ + l = prng_rand(prng) & 7; + for (j = 0; j < l; j++) { + uint32_t num = __builtin_ctz(prng_rand(prng)); + uint32_t where = prng_rand(prng) & 7; + + for (k = where; k < where + num && k < 8; k++) + i6w[k] = 0; + } + + p.prefixlen = prng_rand(prng) >> 24; + memcpy(&p.u.prefix, &i6, sizeof(i6)); + snprintf(buf1, sizeof(buf1), "%s/%d", + inet_ntop(AF_INET6, &p.u.prefix6, ntopbuf, + sizeof(ntopbuf)), + p.prefixlen); + prefix2str(&p, buf2, sizeof(buf2)); + assert(!strcmp(buf1, buf2)); + fprintf(stdout, "%s\n", buf1); + } + + return 0; +} diff --git a/tests/lib/test_prefix2str.py b/tests/lib/test_prefix2str.py new file mode 100644 index 0000000000..6e26d1b409 --- /dev/null +++ b/tests/lib/test_prefix2str.py @@ -0,0 +1,6 @@ +import frrtest + +class TestPrefix2str(frrtest.TestMultiOut): + program = './test_prefix2str' + +TestPrefix2str.exit_cleanly() diff --git a/tests/lib/test_printfrr.c b/tests/lib/test_printfrr.c new file mode 100644 index 0000000000..24de3fa88d --- /dev/null +++ b/tests/lib/test_printfrr.c @@ -0,0 +1,150 @@ +/* + * printfrr() unit test + * Copyright (C) 2019 David Lamparter + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "zebra.h" + +#include + +#include "lib/printfrr.h" +#include "lib/memory.h" +#include "lib/prefix.h" + +static int errors; + +static void printcmp(const char *fmt, ...) PRINTFRR(1, 2); +static void printcmp(const char *fmt, ...) +{ + va_list ap; + char buf[256], bufrr[256], *p; + int cmp; + memset(bufrr, 0xcc, sizeof(bufrr)); + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + + va_start(ap, fmt); + vsnprintfrr(bufrr, sizeof(bufrr), fmt, ap); + va_end(ap); + + cmp = strcmp(buf, bufrr); + + /* OS dependent "+nan" vs. "nan" */ + if (cmp && (p = strstr(bufrr, "+nan"))) { + p[0] = ' '; + if (!strcmp(buf, bufrr)) + cmp = 0; + p[0] = '+'; + } + printf("fmt: \"%s\"\nsys: \"%s\"\nfrr: \"%s\"\n%s\n\n", + fmt, buf, bufrr, cmp ? "ERROR" : "ok"); + + if (cmp) + errors++; +} + +static void printchk(const char *ref, const char *fmt, ...) PRINTFRR(2, 3); +static void printchk(const char *ref, const char *fmt, ...) +{ + va_list ap; + char bufrr[256]; + memset(bufrr, 0xcc, sizeof(bufrr)); + + va_start(ap, fmt); + vsnprintfrr(bufrr, sizeof(bufrr), fmt, ap); + va_end(ap); + + printf("fmt: \"%s\"\nref: \"%s\"\nfrr: \"%s\"\n%s\n\n", + fmt, ref, bufrr, strcmp(ref, bufrr) ? "ERROR" : "ok"); + if (strcmp(ref, bufrr)) + errors++; +} + +int main(int argc, char **argv) +{ + size_t i; + float flts[] = { + 123.456789, + 23.456789e-30, + 3.456789e+30, + INFINITY, + NAN, + }; + uint64_t ui64 = 0xfeed1278cafef00d; + struct in_addr ip; + char *p; + char buf[256]; + + printcmp("%d %u %d %u", 123, 123, -456, -456); + printcmp("%lld %llu %lld %llu", 123LL, 123LL, -456LL, -456LL); + + printcmp("%-20s,%20s,%.20s", "test", "test", "test"); + printcmp("%-3s,%3s,%.3s", "test", "test", "test"); + printcmp("%-6.3s,%6.3s,%6.3s", "test", "test", "test"); + printcmp("%*s,%*s,%.*s", -3, "test", 3, "test", 3, "test"); + + for (i = 0; i < array_size(flts); i++) { + printcmp("%-6.3e,%6.3e,%+06.3e", flts[i], flts[i], flts[i]); + printcmp("%-6.3f,%6.3f,%+06.3f", flts[i], flts[i], flts[i]); + printcmp("%-6.3g,%6.3g,%+06.3g", flts[i], flts[i], flts[i]); + printcmp("%-6.3a,%6.3a,%+06.3a", flts[i], flts[i], flts[i]); + } + + printchk("-77385308584349683 18369358765125201933 feed1278cafef00d", + "%Ld %Lu %Lx", ui64, ui64, ui64); + + inet_aton("192.168.1.2", &ip); + printchk("192.168.1.2", "%pI4", &ip); + printchk(" 192.168.1.2", "%20pI4", &ip); + + printcmp("%p", &ip); + + snprintfrr(buf, sizeof(buf), "test%s", "#1"); + csnprintfrr(buf, sizeof(buf), "test%s", "#2"); + assert(strcmp(buf, "test#1test#2") == 0); + + p = asnprintfrr(MTYPE_TMP, buf, sizeof(buf), "test%s", "#3"); + assert(p == buf); + assert(strcmp(buf, "test#3") == 0); + + p = asnprintfrr(MTYPE_TMP, buf, 4, "test%s", "#4"); + assert(p != buf); + assert(strcmp(p, "test#4") == 0); + XFREE(MTYPE_TMP, p); + + p = asprintfrr(MTYPE_TMP, "test%s", "#5"); + assert(strcmp(p, "test#5") == 0); + XFREE(MTYPE_TMP, p); + + struct prefix_sg sg; + sg.src.s_addr = INADDR_ANY; + sg.grp.s_addr = INADDR_ANY; + printchk("(*,*)", "%pSG4", &sg); + + inet_aton("192.168.1.2", &sg.src); + printchk("(192.168.1.2,*)", "%pSG4", &sg); + + inet_aton("224.1.2.3", &sg.grp); + printchk("(192.168.1.2,224.1.2.3)", "%pSG4", &sg); + + sg.src.s_addr = INADDR_ANY; + printchk("(*,224.1.2.3)", "%pSG4", &sg); + + return !!errors; +} diff --git a/tests/lib/test_printfrr.py b/tests/lib/test_printfrr.py new file mode 100644 index 0000000000..4fe238618e --- /dev/null +++ b/tests/lib/test_printfrr.py @@ -0,0 +1,6 @@ +import frrtest + +class TestPrintfrr(frrtest.TestMultiOut): + program = './test_printfrr' + +TestPrintfrr.exit_cleanly() diff --git a/tests/lib/test_privs.c b/tests/lib/test_privs.c index fc3d908661..c06ebbeb38 100644 --- a/tests/lib/test_privs.c +++ b/tests/lib/test_privs.c @@ -22,7 +22,7 @@ #include "getopt.h" #include "privs.h" #include "memory.h" -#include "memory_vty.h" +#include "lib_vty.h" zebra_capabilities_t _caps_p[] = { ZCAP_NET_RAW, ZCAP_BIND, ZCAP_NET_ADMIN, ZCAP_DAC_OVERRIDE, @@ -105,7 +105,7 @@ int main(int argc, char **argv) } /* Library inits. */ - memory_init(); + lib_cmd_init(); zprivs_preinit(&test_privs); zprivs_init(&test_privs); @@ -113,7 +113,7 @@ int main(int argc, char **argv) ((test_privs.current_state() == ZPRIVS_RAISED) ? "Raised" : "Lowered") printf("%s\n", PRIV_STATE()); - frr_elevate_privs(&test_privs) { + frr_with_privs(&test_privs) { printf("%s\n", PRIV_STATE()); } @@ -125,7 +125,7 @@ int main(int argc, char **argv) /* but these should continue to work... */ printf("%s\n", PRIV_STATE()); - frr_elevate_privs(&test_privs) { + frr_with_privs(&test_privs) { printf("%s\n", PRIV_STATE()); } diff --git a/tests/lib/test_segv.c b/tests/lib/test_segv.c index 43ca0837d5..8133637adc 100644 --- a/tests/lib/test_segv.c +++ b/tests/lib/test_segv.c @@ -73,11 +73,7 @@ int main(void) master = thread_master_create(NULL); signal_init(master, array_size(sigs), sigs); - openzlog("testsegv", "NONE", 0, LOG_CONS | LOG_NDELAY | LOG_PID, - LOG_DAEMON); - zlog_set_level(ZLOG_DEST_SYSLOG, ZLOG_DISABLED); - zlog_set_level(ZLOG_DEST_STDOUT, LOG_DEBUG); - zlog_set_level(ZLOG_DEST_MONITOR, ZLOG_DISABLED); + zlog_aux_init("NONE: ", LOG_DEBUG); thread_execute(master, threadfunc, 0, 0); diff --git a/tests/lib/test_seqlock.c b/tests/lib/test_seqlock.c index 6b2b9ed8a5..639c2bdc2b 100644 --- a/tests/lib/test_seqlock.c +++ b/tests/lib/test_seqlock.c @@ -18,6 +18,10 @@ * Place, Suite 330, Boston, MA 02111-1307 USA */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include #include #include @@ -62,20 +66,20 @@ static void *thr1func(void *arg) seqlock_wait(&sqlo, 1); writestr("thr1 @1\n"); - seqlock_wait(&sqlo, 3); - writestr("thr1 @3\n"); - seqlock_wait(&sqlo, 5); writestr("thr1 @5\n"); - seqlock_wait(&sqlo, 7); - writestr("thr1 @7\n"); - seqlock_wait(&sqlo, 9); writestr("thr1 @9\n"); - seqlock_wait(&sqlo, 11); - writestr("thr1 @11\n"); + seqlock_wait(&sqlo, 13); + writestr("thr1 @13\n"); + + seqlock_wait(&sqlo, 17); + writestr("thr1 @17\n"); + + seqlock_wait(&sqlo, 21); + writestr("thr1 @21\n"); return NULL; } @@ -91,11 +95,11 @@ int main(int argc, char **argv) assert(seqlock_cur(&sqlo) == 1); assert(seqlock_bump(&sqlo) == 1); - assert(seqlock_cur(&sqlo) == 3); - assert(seqlock_bump(&sqlo) == 3); + assert(seqlock_cur(&sqlo) == 5); assert(seqlock_bump(&sqlo) == 5); - assert(seqlock_bump(&sqlo) == 7); - assert(seqlock_cur(&sqlo) == 9); + assert(seqlock_bump(&sqlo) == 9); + assert(seqlock_bump(&sqlo) == 13); + assert(seqlock_cur(&sqlo) == 17); assert(seqlock_held(&sqlo)); seqlock_release(&sqlo); @@ -104,16 +108,16 @@ int main(int argc, char **argv) pthread_create(&thr1, NULL, thr1func, NULL); sleep(1); - writestr("main @3\n"); - seqlock_acquire_val(&sqlo, 3); + writestr("main @5\n"); + seqlock_acquire_val(&sqlo, 5); sleep(2); - writestr("main @5\n"); + writestr("main @9\n"); seqlock_bump(&sqlo); sleep(1); - writestr("main @9\n"); - seqlock_acquire_val(&sqlo, 9); + writestr("main @17\n"); + seqlock_acquire_val(&sqlo, 17); sleep(1); writestr("main @release\n"); diff --git a/tests/lib/test_sig.c b/tests/lib/test_sig.c index cf63a3d047..2aceafb8f0 100644 --- a/tests/lib/test_sig.c +++ b/tests/lib/test_sig.c @@ -57,11 +57,7 @@ int main(void) master = thread_master_create(NULL); signal_init(master, array_size(sigs), sigs); - openzlog("testsig", "NONE", 0, LOG_CONS | LOG_NDELAY | LOG_PID, - LOG_DAEMON); - zlog_set_level(ZLOG_DEST_SYSLOG, ZLOG_DISABLED); - zlog_set_level(ZLOG_DEST_STDOUT, LOG_DEBUG); - zlog_set_level(ZLOG_DEST_MONITOR, ZLOG_DISABLED); + zlog_aux_init("NONE: ", LOG_DEBUG); while (thread_fetch(master, &t)) thread_call(&t); diff --git a/tests/lib/test_srcdest_table.c b/tests/lib/test_srcdest_table.c index 19a40b2184..9d395bee89 100644 --- a/tests/lib/test_srcdest_table.c +++ b/tests/lib/test_srcdest_table.c @@ -4,7 +4,7 @@ * Copyright (C) 2017 by David Lamparter & Christian Franke, * Open Source Routing / NetDEF Inc. * - * This file is part of FreeRangeRouting (FRR) + * This file is part of FRRouting (FRR) * * FRR is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -81,9 +81,9 @@ static char *format_srcdest(const struct prefix_ipv6 *dst_p, return rv; } -static unsigned int log_key(void *data) +static unsigned int log_key(const void *data) { - struct prefix *hash_entry = data; + const struct prefix *hash_entry = data; struct prefix_ipv6 *dst_p = (struct prefix_ipv6 *)&hash_entry[0]; struct prefix_ipv6 *src_p = (struct prefix_ipv6 *)&hash_entry[1]; unsigned int hash = 0; @@ -391,8 +391,7 @@ static void test_state_del_one_route(struct test_state *test, struct prng *prng) } assert(rn); - srcdest_rnode_prefixes(rn, (const struct prefix **)&dst_p, - (const struct prefix **)&src_p); + srcdest_rnode_prefixes(rn, &dst_p, &src_p); memcpy(&dst6_p, dst_p, sizeof(dst6_p)); if (src_p) memcpy(&src6_p, src_p, sizeof(src6_p)); diff --git a/tests/lib/test_stream.c b/tests/lib/test_stream.c index 2ecfc87942..a45c2b4d54 100644 --- a/tests/lib/test_stream.c +++ b/tests/lib/test_stream.c @@ -23,6 +23,8 @@ #include #include +#include "printfrr.h" + static unsigned long long ham = 0xdeadbeefdeadbeef; struct thread_master *master; @@ -30,15 +32,15 @@ static void print_stream(struct stream *s) { size_t getp = stream_get_getp(s); - printf("endp: %zu, readable: %zu, writeable: %zu\n", stream_get_endp(s), - STREAM_READABLE(s), STREAM_WRITEABLE(s)); + printfrr("endp: %zu, readable: %zu, writeable: %zu\n", + stream_get_endp(s), STREAM_READABLE(s), STREAM_WRITEABLE(s)); while (STREAM_READABLE(s)) { - printf("0x%x ", *stream_pnt(s)); + printfrr("0x%x ", *stream_pnt(s)); stream_forward_getp(s, 1); } - printf("\n"); + printfrr("\n"); /* put getp back to where it was */ stream_set_getp(s, getp); @@ -61,10 +63,10 @@ int main(void) print_stream(s); - printf("c: 0x%hhx\n", stream_getc(s)); - printf("w: 0x%hx\n", stream_getw(s)); - printf("l: 0x%x\n", stream_getl(s)); - printf("q: 0x%" PRIx64 "\n", stream_getq(s)); + printfrr("c: 0x%hhx\n", stream_getc(s)); + printfrr("w: 0x%hx\n", stream_getw(s)); + printfrr("l: 0x%x\n", stream_getl(s)); + printfrr("q: 0x%" PRIx64 "\n", stream_getq(s)); return 0; } diff --git a/tests/lib/test_timer_correctness.c b/tests/lib/test_timer_correctness.c index 43e79ba9d0..cbf9b05546 100644 --- a/tests/lib/test_timer_correctness.c +++ b/tests/lib/test_timer_correctness.c @@ -28,7 +28,6 @@ #include #include "memory.h" -#include "pqueue.h" #include "prng.h" #include "thread.h" diff --git a/tests/lib/test_timer_performance.c b/tests/lib/test_timer_performance.c index d5f4badc85..2960e0d81e 100644 --- a/tests/lib/test_timer_performance.c +++ b/tests/lib/test_timer_performance.c @@ -28,7 +28,6 @@ #include #include "thread.h" -#include "pqueue.h" #include "prng.h" #define SCHEDULE_TIMERS 1000000 diff --git a/tests/lib/test_ttable.c b/tests/lib/test_ttable.c index 674179b6ab..43b8adcb97 100644 --- a/tests/lib/test_ttable.c +++ b/tests/lib/test_ttable.c @@ -93,8 +93,7 @@ int main(int argc, char **argv) /* add bigger row */ ttable_add_row(tt, "%s|%s||%s|%s", - "nebula dusk session streets twilight " - "pioneer beats yeah", + "nebula dusk session streets twilight pioneer beats yeah", "prarie dog", "cornmeal", ":O -*_-*"); assert(tt->ncols == 5); assert(tt->nrows == 2); diff --git a/tests/lib/test_typelist.c b/tests/lib/test_typelist.c index 68ea82ea41..607e29e56b 100644 --- a/tests/lib/test_typelist.c +++ b/tests/lib/test_typelist.c @@ -25,6 +25,7 @@ #include #include #include +#include #define WNO_ATOMLIST_UNSAFE_FIND @@ -32,6 +33,9 @@ #include "atomlist.h" #include "memory.h" #include "monotime.h" +#include "jhash.h" +#include "sha256.h" +#include "printfrr.h" #include "tests/helpers/c/prng.h" @@ -49,31 +53,32 @@ #define _DECLARE(type, ...) DECLARE_##type(__VA_ARGS__) #define DECLARE(type, ...) _DECLARE(type, __VA_ARGS__) -#define _U_SORTLIST_UNIQ 1 -#define _U_SORTLIST_NONUNIQ 0 -#define _U_HASH 1 -#define _U_SKIPLIST_UNIQ 1 -#define _U_SKIPLIST_NONUNIQ 0 -#define _U_RBTREE_UNIQ 1 -#define _U_RBTREE_NONUNIQ 0 -#define _U_ATOMSORT_UNIQ 1 -#define _U_ATOMSORT_NONUNIQ 0 - -#define _IS_UNIQ(type) _U_##type -#define IS_UNIQ(type) _IS_UNIQ(type) - -#define _H_SORTLIST_UNIQ 0 -#define _H_SORTLIST_NONUNIQ 0 -#define _H_HASH 1 -#define _H_SKIPLIST_UNIQ 0 -#define _H_SKIPLIST_NONUNIQ 0 -#define _H_RBTREE_UNIQ 0 -#define _H_RBTREE_NONUNIQ 0 -#define _H_ATOMSORT_UNIQ 0 -#define _H_ATOMSORT_NONUNIQ 0 - -#define _IS_HASH(type) _H_##type -#define IS_HASH(type) _IS_HASH(type) +#define T_SORTED (1 << 0) +#define T_UNIQ (1 << 1) +#define T_HASH (1 << 2) +#define T_HEAP (1 << 3) +#define T_ATOMIC (1 << 4) + +#define _T_LIST (0) +#define _T_DLIST (0) +#define _T_ATOMLIST (0 | T_ATOMIC) +#define _T_HEAP (T_SORTED | T_HEAP) +#define _T_SORTLIST_UNIQ (T_SORTED | T_UNIQ) +#define _T_SORTLIST_NONUNIQ (T_SORTED) +#define _T_HASH (T_SORTED | T_UNIQ | T_HASH) +#define _T_SKIPLIST_UNIQ (T_SORTED | T_UNIQ) +#define _T_SKIPLIST_NONUNIQ (T_SORTED) +#define _T_RBTREE_UNIQ (T_SORTED | T_UNIQ) +#define _T_RBTREE_NONUNIQ (T_SORTED) +#define _T_ATOMSORT_UNIQ (T_SORTED | T_UNIQ | T_ATOMIC) +#define _T_ATOMSORT_NONUNIQ (T_SORTED | T_ATOMIC) + +#define _T_TYPE(type) _T_##type +#define IS_SORTED(type) (_T_TYPE(type) & T_SORTED) +#define IS_UNIQ(type) (_T_TYPE(type) & T_UNIQ) +#define IS_HASH(type) (_T_TYPE(type) & T_HASH) +#define IS_HEAP(type) (_T_TYPE(type) & T_HEAP) +#define IS_ATOMIC(type) (_T_TYPE(type) & T_ATOMIC) static struct timeval ref, ref0; @@ -86,16 +91,28 @@ static void ts_ref(const char *text) { int64_t us; us = monotime_since(&ref, NULL); - printf("%7"PRId64"us %s\n", us, text); + printfrr("%7"PRId64"us %s\n", us, text); monotime(&ref); } static void ts_end(void) { int64_t us; us = monotime_since(&ref0, NULL); - printf("%7"PRId64"us total\n", us); + printfrr("%7"PRId64"us total\n", us); } +#define TYPE LIST +#include "test_typelist.h" + +#define TYPE DLIST +#include "test_typelist.h" + +#define TYPE ATOMLIST +#include "test_typelist.h" + +#define TYPE HEAP +#include "test_typelist.h" + #define TYPE SORTLIST_UNIQ #include "test_typelist.h" @@ -105,6 +122,12 @@ static void ts_end(void) #define TYPE HASH #include "test_typelist.h" +#define TYPE HASH_collisions +#define REALTYPE HASH +#define SHITTY_HASH +#include "test_typelist.h" +#undef SHITTY_HASH + #define TYPE SKIPLIST_UNIQ #include "test_typelist.h" @@ -127,9 +150,14 @@ int main(int argc, char **argv) { srandom(1); + test_LIST(); + test_DLIST(); + test_ATOMLIST(); + test_HEAP(); test_SORTLIST_UNIQ(); test_SORTLIST_NONUNIQ(); test_HASH(); + test_HASH_collisions(); test_SKIPLIST_UNIQ(); test_SKIPLIST_NONUNIQ(); test_RBTREE_UNIQ(); diff --git a/tests/lib/test_typelist.h b/tests/lib/test_typelist.h index 60a37e84ed..f86cadd398 100644 --- a/tests/lib/test_typelist.h +++ b/tests/lib/test_typelist.h @@ -25,38 +25,54 @@ #define list_hash concat(TYPE, _hash) #define list_init concat(TYPE, _init) #define list_fini concat(TYPE, _fini) +#define list_const_first concat(TYPE, _const_first) #define list_first concat(TYPE, _first) +#define list_const_next concat(TYPE, _const_next) #define list_next concat(TYPE, _next) #define list_next_safe concat(TYPE, _next_safe) #define list_count concat(TYPE, _count) #define list_add concat(TYPE, _add) +#define list_add_head concat(TYPE, _add_head) +#define list_add_tail concat(TYPE, _add_tail) +#define list_add_after concat(TYPE, _add_after) #define list_find concat(TYPE, _find) #define list_find_lt concat(TYPE, _find_lt) #define list_find_gteq concat(TYPE, _find_gteq) #define list_del concat(TYPE, _del) #define list_pop concat(TYPE, _pop) -PREDECL(TYPE, list) +#define ts_hash concat(ts_hash_, TYPE) + +#ifndef REALTYPE +#define REALTYPE TYPE +#endif + +PREDECL(REALTYPE, list) struct item { uint64_t val; struct list_item itm; int scratchpad; }; +#if IS_SORTED(REALTYPE) static int list_cmp(const struct item *a, const struct item *b); -#if IS_HASH(TYPE) +#if IS_HASH(REALTYPE) static uint32_t list_hash(const struct item *a); -DECLARE(TYPE, list, struct item, itm, list_cmp, list_hash) +DECLARE(REALTYPE, list, struct item, itm, list_cmp, list_hash) static uint32_t list_hash(const struct item *a) { +#ifdef SHITTY_HASH /* crappy hash to get some hash collisions */ return a->val ^ (a->val << 29) ^ 0x55AA0000U; +#else + return jhash_1word(a->val, 0xdeadbeef); +#endif } #else -DECLARE(TYPE, list, struct item, itm, list_cmp) +DECLARE(REALTYPE, list, struct item, itm, list_cmp) #endif static int list_cmp(const struct item *a, const struct item *b) @@ -68,29 +84,82 @@ static int list_cmp(const struct item *a, const struct item *b) return 0; } +#else /* !IS_SORTED */ +DECLARE(REALTYPE, list, struct item, itm) +#endif + #define NITEM 10000 struct item itm[NITEM]; -static struct list_head head = concat(INIT_, TYPE)(head); +static struct list_head head = concat(INIT_, REALTYPE)(head); + +static void ts_hash(const char *text, const char *expect) +{ + int64_t us = monotime_since(&ref, NULL); + SHA256_CTX ctx; + struct item *item; + unsigned i = 0; + uint8_t hash[32]; + char hashtext[65]; + uint32_t swap_count, count; + + count = list_count(&head); + swap_count = htonl(count); + + SHA256_Init(&ctx); + SHA256_Update(&ctx, &swap_count, sizeof(swap_count)); + + frr_each (list, &head, item) { + struct { + uint32_t val_upper, val_lower, index; + } hashitem = { + htonl(item->val >> 32), + htonl(item->val & 0xFFFFFFFFULL), + htonl(i), + }; + SHA256_Update(&ctx, &hashitem, sizeof(hashitem)); + i++; + assert(i <= count); + } + SHA256_Final(hash, &ctx); + + for (i = 0; i < sizeof(hash); i++) + sprintf(hashtext + i * 2, "%02x", hash[i]); + + printfrr("%7"PRId64"us %-25s %s%s\n", us, text, + expect ? " " : "*", hashtext); + if (expect && strcmp(expect, hashtext)) { + printfrr("%-21s %s\n", "EXPECTED:", expect); + assert(0); + } + monotime(&ref); +} +/* hashes will have different item ordering */ +#if IS_HASH(REALTYPE) || IS_HEAP(REALTYPE) +#define ts_hashx(pos, csum) ts_hash(pos, NULL) +#else +#define ts_hashx(pos, csum) ts_hash(pos, csum) +#endif static void concat(test_, TYPE)(void) { size_t i, j, k, l; struct prng *prng; - struct item *item, *prev; - struct item dummy; + struct item *item, *prev __attribute__((unused)); + struct item dummy __attribute__((unused)); memset(itm, 0, sizeof(itm)); for (i = 0; i < NITEM; i++) itm[i].val = i; - printf("%s start\n", str(TYPE)); + printfrr("%s start\n", str(TYPE)); ts_start(); list_init(&head); - ts_ref("init"); - assert(list_first(&head) == NULL); + ts_hash("init", "df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119"); + +#if IS_SORTED(REALTYPE) prng = prng_new(0); k = 0; for (i = 0; i < NITEM; i++) { @@ -99,29 +168,43 @@ static void concat(test_, TYPE)(void) list_add(&head, &itm[j]); itm[j].scratchpad = 1; k++; - } else + } +#if !IS_HEAP(REALTYPE) + else assert(list_add(&head, &itm[j]) == &itm[j]); +#endif } assert(list_count(&head) == k); assert(list_first(&head) != NULL); - ts_ref("fill"); + ts_hashx("fill", "a538546a6e6ab0484e925940aa8dd02fd934408bbaed8cb66a0721841584d838"); k = 0; - prev = NULL; - for_each(list, &head, item) { -#if IS_HASH(TYPE) + +#if IS_ATOMIC(REALTYPE) + struct list_head *chead = &head; + struct item *citem, *cprev = NULL; + + frr_each(list, chead, citem) { +#else + const struct list_head *chead = &head; + const struct item *citem, *cprev = NULL; + + frr_each(list_const, chead, citem) { +#endif + +#if IS_HASH(REALTYPE) || IS_HEAP(REALTYPE) /* hash table doesn't give sorting */ - (void)prev; + (void)cprev; #else - assert(!prev || prev->val < item->val); + assert(!cprev || cprev->val < citem->val); #endif - prev = item; + cprev = citem; k++; } - assert(list_count(&head) == k); + assert(list_count(chead) == k); ts_ref("walk"); -#if IS_UNIQ(TYPE) +#if IS_UNIQ(REALTYPE) prng_free(prng); prng = prng_new(0); @@ -140,11 +223,26 @@ static void concat(test_, TYPE)(void) assert(list_add(&head, &dummy) == &itm[j]); else { assert(list_add(&head, &dummy) == NULL); - list_del(&head, &dummy); + assert(list_del(&head, &dummy) != NULL); } } - ts_ref("add-dup"); -#else /* !IS_UNIQ(TYPE) */ + ts_hashx("add-dup", "a538546a6e6ab0484e925940aa8dd02fd934408bbaed8cb66a0721841584d838"); + +#elif IS_HEAP(REALTYPE) + /* heap - partially sorted. */ + prev = NULL; + l = k / 2; + for (i = 0; i < l; i++) { + item = list_pop(&head); + if (prev) + assert(prev->val < item->val); + item->scratchpad = 0; + k--; + prev = item; + } + ts_hash("pop", NULL); + +#else /* !IS_UNIQ(REALTYPE) && !IS_HEAP(REALTYPE) */ for (i = 0; i < NITEM; i++) { j = prng_rand(prng) % NITEM; memset(&dummy, 0, sizeof(dummy)); @@ -171,11 +269,11 @@ static void concat(test_, TYPE)(void) list_first(&head) == &dummy); } else if (list_next(&head, &dummy)) assert(list_next(&head, &dummy)->val > j); - list_del(&head, &dummy); + assert(list_del(&head, &dummy) != NULL); } - ts_ref("add-dup+find_{lt,gteq}"); + ts_hash("add-dup+find_{lt,gteq}", "a538546a6e6ab0484e925940aa8dd02fd934408bbaed8cb66a0721841584d838"); #endif -#if !IS_HASH(TYPE) +#if !IS_HASH(REALTYPE) && !IS_HEAP(REALTYPE) prng_free(prng); prng = prng_new(123456); @@ -196,7 +294,7 @@ static void concat(test_, TYPE)(void) assert(!tmp || tmp->val >= j); } else assert(gteq == list_first(&head)); - + if (gteq) assert(gteq->val >= j); } @@ -211,26 +309,52 @@ static void concat(test_, TYPE)(void) (void)prng_rand(prng); j = prng_rand(prng) % NITEM; if (itm[j].scratchpad == 1) { - list_del(&head, &itm[j]); + assert(list_del(&head, &itm[j]) != NULL); itm[j].scratchpad = 0; l++; } } assert(l + list_count(&head) == k); - ts_ref("del"); + ts_hashx("del", "cb2e5d80f08a803ef7b56c15e981b681adcea214bebc2f55e12e0bfb242b07ca"); - for_each_safe(list, &head, item) { + frr_each_safe(list, &head, item) { assert(item->scratchpad != 0); if (item->val & 1) { - list_del(&head, item); + assert(list_del(&head, item) != NULL); item->scratchpad = 0; l++; } } assert(l + list_count(&head) == k); - ts_ref("for_each_safe+del"); + ts_hashx("frr_each_safe+del", "e0beb71dd963a75af05b722b8e71b61b304587d860c8accdc4349067542b86bb"); + +#else /* !IS_SORTED */ + prng = prng_new(0); + k = 0; + for (i = 0; i < NITEM; i++) { + j = prng_rand(prng) % NITEM; + if (itm[j].scratchpad == 0) { + list_add_tail(&head, &itm[j]); + itm[j].scratchpad = 1; + k++; + } + } + assert(list_count(&head) == k); + assert(list_first(&head) != NULL); + ts_hash("fill / add_tail", "eabfcf1413936daaf20965abced95762f45110a6619b84aac7d38481bce4ea19"); + + for (i = 0; i < NITEM / 2; i++) { + j = prng_rand(prng) % NITEM; + if (itm[j].scratchpad == 1) { + assert(list_del(&head, &itm[j]) != NULL); + itm[j].scratchpad = 0; + k--; + } + } + ts_hash("del-prng", "86d568a95eb429dab3162976c5a5f3f75aabc835932cd682aa280b6923549564"); + l = 0; while ((item = list_pop(&head))) { assert(item->scratchpad != 0); @@ -240,14 +364,190 @@ static void concat(test_, TYPE)(void) assert(l == k); assert(list_count(&head) == 0); assert(list_first(&head) == NULL); - ts_ref("pop"); + ts_hash("pop", "df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119"); + + prng_free(prng); + prng = prng_new(0x1e5a2d69); + + k = 0; + for (i = 0; i < NITEM; i++) { + j = prng_rand(prng) % NITEM; + if (itm[j].scratchpad == 0) { + list_add_head(&head, &itm[j]); + itm[j].scratchpad = 1; + k++; + } + } + assert(list_count(&head) == k); + assert(list_first(&head) != NULL); + ts_hash("fill / add_head", "3084d8f8a28b8c756ccc0a92d60d86f6d776273734ddc3f9e1d89526f5ca2795"); + + for (i = 0; i < NITEM / 2; i++) { + j = prng_rand(prng) % NITEM; + if (itm[j].scratchpad == 1) { + assert(list_del(&head, &itm[j]) != NULL); + itm[j].scratchpad = 0; + k--; + } + } + ts_hash("del-prng", "dc916fa7ea4418792c7c8232d74df2887f9975ead4222f4b977be6bc0b52285e"); + + l = 0; + while ((item = list_pop(&head))) { + assert(item->scratchpad != 0); + + item->scratchpad = 0; + l++; + } + assert(l == k); + assert(list_count(&head) == 0); + assert(list_first(&head) == NULL); + ts_hash("pop", "df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119"); + + prng_free(prng); + prng = prng_new(0x692d1e5a); + + k = 0; + for (i = 0; i < NITEM; i++) { + j = prng_rand(prng) % NITEM; + if (itm[j].scratchpad == 0) { + if (prng_rand(prng) & 1) { + list_add_tail(&head, &itm[j]); + } else { + list_add_head(&head, &itm[j]); + } + itm[j].scratchpad = 1; + k++; + } + } + assert(list_count(&head) == k); + assert(list_first(&head) != NULL); + ts_hash("fill / add_{head,tail}", "93fa180a575c96e4b6c3775c2de7843ee3254dd6ed5af699bbe155f994114b06"); + + for (i = 0; i < NITEM * 3; i++) { + int op = prng_rand(prng); + j = prng_rand(prng) % NITEM; + + if (op & 1) { + /* delete or pop */ + if (op & 2) { + item = list_pop(&head); + if (!item) + continue; + } else { + item = &itm[j]; + if (item->scratchpad == 0) + continue; + assert(list_del(&head, item) != NULL); + } + item->scratchpad = 0; + k--; + } else { + item = &itm[j]; + if (item->scratchpad != 0) + continue; + + item->scratchpad = 1; + k++; + + switch ((op >> 1) & 1) { + case 0: + list_add_head(&head, item); + break; + case 1: + list_add_tail(&head, item); + break; + default: + assert(0); + } + } + } + assert(list_count(&head) == k); + assert(list_first(&head) != NULL); + ts_hash("prng add/del", "4909f31d06bb006efca4dfeebddb8de071733ddf502f89b6d532155208bbc6df"); + +#if !IS_ATOMIC(REALTYPE) + /* variant with add_after */ + + for (i = 0; i < NITEM * 3; i++) { + int op = prng_rand(prng); + j = prng_rand(prng) % NITEM; + + if (op & 1) { + /* delete or pop */ + if (op & 2) { + item = list_pop(&head); + if (!item) + continue; + } else { + item = &itm[j]; + if (item->scratchpad == 0) + continue; + assert(list_del(&head, item) != NULL); + } + item->scratchpad = 0; + k--; + } else { + item = &itm[j]; + if (item->scratchpad != 0) + continue; + + item->scratchpad = 1; + k++; + + switch ((op >> 1) & 3) { + case 0: + list_add_head(&head, item); + break; + case 1: + list_add_tail(&head, item); + break; + case 2: + case 3: + prev = NULL; + l = 0; + do { + j = prng_rand(prng) % NITEM; + prev = &itm[j]; + if (prev->scratchpad == 0 + || prev == item) + prev = NULL; + l++; + } while (!prev && l < 10); + list_add_after(&head, prev, item); + break; + default: + assert(0); + } + } + } + assert(list_count(&head) == k); + assert(list_first(&head) != NULL); + ts_hash("prng add/after/del", "84c5fc83294eabebb9808ccbba32a303c4fca084db87ed1277d2bae1f8c5bee4"); +#endif + + l = 0; +#endif + + while ((item = list_pop(&head))) { + assert(item->scratchpad != 0); + + item->scratchpad = 0; + l++; + } + assert(l == k); + assert(list_count(&head) == 0); + assert(list_first(&head) == NULL); + ts_hash("pop", "df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119"); list_fini(&head); ts_ref("fini"); ts_end(); - printf("%s end\n", str(TYPE)); + printfrr("%s end\n", str(TYPE)); } +#undef ts_hashx + #undef item #undef itm #undef head @@ -263,10 +563,14 @@ static void concat(test_, TYPE)(void) #undef list_next_safe #undef list_count #undef list_add +#undef list_add_head +#undef list_add_tail +#undef list_add_after #undef list_find #undef list_find_lt #undef list_find_gteq #undef list_del #undef list_pop +#undef REALTYPE #undef TYPE diff --git a/tests/lib/test_typelist.py b/tests/lib/test_typelist.py index 3b7373ceb8..0b3c743971 100644 --- a/tests/lib/test_typelist.py +++ b/tests/lib/test_typelist.py @@ -3,9 +3,14 @@ class TestTypelist(frrtest.TestMultiOut): program = './test_typelist' +TestTypelist.onesimple('LIST end') +TestTypelist.onesimple('DLIST end') +TestTypelist.onesimple('ATOMLIST end') +TestTypelist.onesimple('HEAP end') TestTypelist.onesimple('SORTLIST_UNIQ end') TestTypelist.onesimple('SORTLIST_NONUNIQ end') TestTypelist.onesimple('HASH end') +TestTypelist.onesimple('HASH_collisions end') TestTypelist.onesimple('SKIPLIST_UNIQ end') TestTypelist.onesimple('SKIPLIST_NONUNIQ end') TestTypelist.onesimple('RBTREE_UNIQ end') diff --git a/tests/lib/test_versioncmp.c b/tests/lib/test_versioncmp.c new file mode 100644 index 0000000000..bb819e36f5 --- /dev/null +++ b/tests/lib/test_versioncmp.c @@ -0,0 +1,66 @@ +/* + * frr_version_cmp() tests + * Copyright (C) 2018 David Lamparter for NetDEF, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include +#include + +static const char *rel(int x) +{ + if (x < 0) + return "<"; + if (x > 0) + return ">"; + return "=="; +} + +static int fail; + +static void compare(const char *a, const char *b, int expect) +{ + int result = frr_version_cmp(a, b); + + if (expect == result) + printf("\"%s\" %s \"%s\"\n", a, rel(result), b); + else { + printf("\"%s\" %s \"%s\", expected %s!\n", a, rel(result), b, + rel(expect)); + fail = 1; + } +} + +int main(int argc, char **argv) +{ + compare("", "", 0); + compare("1", "1", 0); + compare("1.0", "1.00", 0); + compare("10.0", "1", 1); + compare("10.0", "2", 1); + compare("2.1", "10.0", -1); + compare("1.1.1", "1.1.0", 1); + compare("1.0a", "1.0", 1); + compare("1.0a", "1.0b", -1); + compare("1.0a10", "1.0a2", 1); + compare("1.00a2", "1.0a2", 0); + compare("1.00a2", "1.0a3", -1); + compare("1.0-dev", "1.0", 1); + compare("1.0~foo", "1.0", -1); + compare("1.0~1", "1.0~0", 1); + compare("1.00~1", "1.0~0", 1); + printf("final tally: %s\n", fail ? "FAILED" : "ok"); + return fail; +} diff --git a/tests/lib/test_versioncmp.py b/tests/lib/test_versioncmp.py new file mode 100644 index 0000000000..0990757000 --- /dev/null +++ b/tests/lib/test_versioncmp.py @@ -0,0 +1,6 @@ +import frrtest + +class TestVersionCmp(frrtest.TestMultiOut): + program = './test_versioncmp' + +TestVersionCmp.exit_cleanly() diff --git a/tests/lib/test_zlog.c b/tests/lib/test_zlog.c index 790e65cfe9..48fa7bce94 100644 --- a/tests/lib/test_zlog.c +++ b/tests/lib/test_zlog.c @@ -20,6 +20,7 @@ #include #include #include "log.h" +#include "network.h" /* maximum amount of data to hexdump */ #define MAXDATA 16384 @@ -34,12 +35,14 @@ static bool test_zlog_hexdump(void) unsigned int nl = 1; do { - long d[nl]; + uint8_t d[nl]; for (unsigned int i = 0; i < nl; i++) - d[i] = random(); - zlog_hexdump(d, nl * sizeof(long)); - } while (++nl * sizeof(long) <= MAXDATA); + d[i] = frr_weak_random(); + zlog_hexdump(d, nl - 1); + + nl += 1 + (nl / 2); + } while (nl <= MAXDATA); return true; } @@ -50,9 +53,7 @@ bool (*tests[])(void) = { int main(int argc, char **argv) { - openzlog("testzlog", "NONE", 0, LOG_CONS | LOG_NDELAY | LOG_PID, - LOG_ERR); - zlog_set_file("test_zlog.log", LOG_DEBUG); + zlog_aux_init("NONE: ", ZLOG_DISABLED); for (unsigned int i = 0; i < array_size(tests); i++) if (!tests[i]()) diff --git a/tests/lib/test_zmq.c b/tests/lib/test_zmq.c index b6624915e8..fe330d98d4 100644 --- a/tests/lib/test_zmq.c +++ b/tests/lib/test_zmq.c @@ -120,7 +120,7 @@ static void run_client(int syncfd) /* write callback */ printf("---\n"); - snprintf(buf, 32, "Done receiving"); + snprintf(buf, sizeof(buf), "Done receiving"); printf("client send: %s\n", buf); fflush(stdout); send_delim(zmqsock); diff --git a/tests/subdir.am b/tests/subdir.am index ec5fea705e..e54bfc4a35 100644 --- a/tests/subdir.am +++ b/tests/subdir.am @@ -2,8 +2,6 @@ # tests # -PYTHON ?= python - if BGPD TESTS_BGPD = \ tests/bgpd/test_aspath \ @@ -25,6 +23,7 @@ else TESTS_ISISD = \ tests/isisd/test_fuzz_isis_tlv \ tests/isisd/test_isis_lspdb \ + tests/isisd/test_isis_spf \ tests/isisd/test_isis_vertex_queue \ # end endif @@ -40,10 +39,10 @@ else TESTS_OSPF6D = endif -tests/lib/cli/tests_lib_cli_test_cli-test_cli.$(OBJEXT): tests/lib/cli/test_cli_clippy.c -tests/lib/cli/test_cli-test_cli.$(OBJEXT): tests/lib/cli/test_cli_clippy.c -tests/ospf6d/tests_ospf6d_test_lsdb-test_lsdb.$(OBJEXT): tests/ospf6d/test_lsdb_clippy.c -tests/ospf6d/test_lsdb-test_lsdb.$(OBJEXT): tests/ospf6d/test_lsdb_clippy.c +clippy_scan += \ + tests/lib/cli/test_cli.c \ + tests/ospf6d/test_lsdb.c \ + # end check_PROGRAMS = \ tests/lib/cxxcompat \ @@ -56,6 +55,9 @@ check_PROGRAMS = \ tests/lib/test_idalloc \ tests/lib/test_memory \ tests/lib/test_nexthop_iter \ + tests/lib/test_ntop \ + tests/lib/test_prefix2str \ + tests/lib/test_printfrr \ tests/lib/test_privs \ tests/lib/test_ringbuf \ tests/lib/test_srcdest_table \ @@ -68,6 +70,7 @@ check_PROGRAMS = \ tests/lib/test_timer_performance \ tests/lib/test_ttable \ tests/lib/test_typelist \ + tests/lib/test_versioncmp \ tests/lib/test_zlog \ tests/lib/test_graph \ tests/lib/cli/test_cli \ @@ -85,6 +88,7 @@ check_PROGRAMS += \ endif tests/lib/cli/test_commands_defun.c: vtysh/vtysh_cmd.c + mkdir -p tests/lib/cli sed \ -e 's%"vtysh/vtysh\.h"%"tests/helpers/c/tests.h"%' \ -e 's/vtysh_init_cmd/test_init_cmd/' \ @@ -94,6 +98,7 @@ tests/lib/cli/test_commands_defun.c: vtysh/vtysh_cmd.c CLEANFILES += tests/lib/cli/test_commands_defun.c tests/isisd/test_fuzz_isis_tlv_tests.h: $(top_srcdir)/tests/isisd/test_fuzz_isis_tlv_tests.h.gz + mkdir -p tests/isisd gzip -d < $(top_srcdir)/tests/isisd/test_fuzz_isis_tlv_tests.h.gz > "$@" CLEANFILES += tests/isisd/test_fuzz_isis_tlv_tests.h @@ -107,6 +112,7 @@ noinst_HEADERS += \ tests/helpers/c/tests.h \ tests/lib/cli/common_cli.h \ tests/lib/test_typelist.h \ + tests/isisd/test_common.h \ # end # @@ -164,16 +170,21 @@ tests_bgpd_test_peer_attr_SOURCES = tests/bgpd/test_peer_attr.c tests_isisd_test_fuzz_isis_tlv_CFLAGS = $(TESTS_CFLAGS) -I$(top_builddir)/tests/isisd tests_isisd_test_fuzz_isis_tlv_CPPFLAGS = $(TESTS_CPPFLAGS) -I$(top_builddir)/tests/isisd tests_isisd_test_fuzz_isis_tlv_LDADD = $(ISISD_TEST_LDADD) -tests_isisd_test_fuzz_isis_tlv_SOURCES = tests/isisd/test_fuzz_isis_tlv.c +tests_isisd_test_fuzz_isis_tlv_SOURCES = tests/isisd/test_fuzz_isis_tlv.c tests/isisd/test_common.c nodist_tests_isisd_test_fuzz_isis_tlv_SOURCES = tests/isisd/test_fuzz_isis_tlv_tests.h tests_isisd_test_isis_lspdb_CFLAGS = $(TESTS_CFLAGS) tests_isisd_test_isis_lspdb_CPPFLAGS = $(TESTS_CPPFLAGS) tests_isisd_test_isis_lspdb_LDADD = $(ISISD_TEST_LDADD) -tests_isisd_test_isis_lspdb_SOURCES = tests/isisd/test_isis_lspdb.c +tests_isisd_test_isis_lspdb_SOURCES = tests/isisd/test_isis_lspdb.c tests/isisd/test_common.c +tests_isisd_test_isis_spf_CFLAGS = $(TESTS_CFLAGS) +tests_isisd_test_isis_spf_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_isisd_test_isis_spf_LDADD = $(ISISD_TEST_LDADD) +tests_isisd_test_isis_spf_SOURCES = tests/isisd/test_isis_spf.c tests/isisd/test_common.c tests/isisd/test_topologies.c +nodist_tests_isisd_test_isis_spf_SOURCES = yang/frr-isisd.yang.c tests_isisd_test_isis_vertex_queue_CFLAGS = $(TESTS_CFLAGS) tests_isisd_test_isis_vertex_queue_CPPFLAGS = $(TESTS_CPPFLAGS) tests_isisd_test_isis_vertex_queue_LDADD = $(ISISD_TEST_LDADD) -tests_isisd_test_isis_vertex_queue_SOURCES = tests/isisd/test_isis_vertex_queue.c +tests_isisd_test_isis_vertex_queue_SOURCES = tests/isisd/test_isis_vertex_queue.c tests/isisd/test_common.c tests_lib_cxxcompat_CFLAGS = $(TESTS_CFLAGS) $(CXX_COMPAT_CFLAGS) $(WERROR) tests_lib_cxxcompat_CPPFLAGS = $(TESTS_CPPFLAGS) @@ -232,6 +243,18 @@ tests_lib_test_nexthop_iter_CFLAGS = $(TESTS_CFLAGS) tests_lib_test_nexthop_iter_CPPFLAGS = $(TESTS_CPPFLAGS) tests_lib_test_nexthop_iter_LDADD = $(ALL_TESTS_LDADD) tests_lib_test_nexthop_iter_SOURCES = tests/lib/test_nexthop_iter.c tests/helpers/c/prng.c +tests_lib_test_ntop_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_ntop_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_ntop_LDADD = # none +tests_lib_test_ntop_SOURCES = tests/lib/test_ntop.c tests/helpers/c/prng.c +tests_lib_test_prefix2str_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_prefix2str_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_prefix2str_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_prefix2str_SOURCES = tests/lib/test_prefix2str.c tests/helpers/c/prng.c +tests_lib_test_printfrr_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_printfrr_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_printfrr_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_printfrr_SOURCES = tests/lib/test_printfrr.c tests_lib_test_privs_CFLAGS = $(TESTS_CFLAGS) tests_lib_test_privs_CPPFLAGS = $(TESTS_CPPFLAGS) tests_lib_test_privs_LDADD = $(ALL_TESTS_LDADD) @@ -280,6 +303,10 @@ tests_lib_test_typelist_CFLAGS = $(TESTS_CFLAGS) tests_lib_test_typelist_CPPFLAGS = $(TESTS_CPPFLAGS) tests_lib_test_typelist_LDADD = $(ALL_TESTS_LDADD) tests_lib_test_typelist_SOURCES = tests/lib/test_typelist.c tests/helpers/c/prng.c +tests_lib_test_versioncmp_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_versioncmp_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_versioncmp_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_versioncmp_SOURCES = tests/lib/test_versioncmp.c tests_lib_test_zlog_CFLAGS = $(TESTS_CFLAGS) tests_lib_test_zlog_CPPFLAGS = $(TESTS_CPPFLAGS) tests_lib_test_zlog_LDADD = $(ALL_TESTS_LDADD) @@ -307,6 +334,9 @@ EXTRA_DIST += \ tests/isisd/test_fuzz_isis_tlv.py \ tests/isisd/test_fuzz_isis_tlv_tests.h.gz \ tests/isisd/test_isis_lspdb.py \ + tests/isisd/test_isis_spf.py \ + tests/isisd/test_isis_spf.in \ + tests/isisd/test_isis_spf.refout \ tests/isisd/test_isis_vertex_queue.py \ tests/lib/cli/test_commands.in \ tests/lib/cli/test_commands.py \ @@ -319,6 +349,9 @@ EXTRA_DIST += \ tests/lib/northbound/test_oper_data.refout \ tests/lib/test_atomlist.py \ tests/lib/test_nexthop_iter.py \ + tests/lib/test_ntop.py \ + tests/lib/test_prefix2str.py \ + tests/lib/test_printfrr.py \ tests/lib/test_ringbuf.py \ tests/lib/test_srcdest_table.py \ tests/lib/test_stream.py \ @@ -328,6 +361,7 @@ EXTRA_DIST += \ tests/lib/test_ttable.py \ tests/lib/test_ttable.refout \ tests/lib/test_typelist.py \ + tests/lib/test_versioncmp.py \ tests/lib/test_zlog.py \ tests/lib/test_graph.py \ tests/lib/test_graph.refout \ diff --git a/tests/test_lblmgr.c b/tests/test_lblmgr.c deleted file mode 100644 index e71e680fad..0000000000 --- a/tests/test_lblmgr.c +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Label Manager Test - * - * Copyright (C) 2017 by Bingen Eguzkitza, - * Volta Networks Inc. - * - * This file is part of FreeRangeRouting (FRR) - * - * FRR is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2, or (at your option) any - * later version. - * - * FRR is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; see the file COPYING; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "lib/stream.h" -#include "lib/zclient.h" - -#define ZSERV_PATH "/tmp/zserv.api" // TODO!! -#define KEEP 0 /* change to 1 to avoid garbage collection */ -#define CHUNK_SIZE 32 - -struct zclient *zclient; -unsigned short instance = 1; - -const char *sequence = "GGRGGGRRG"; - -static int zebra_send_get_label_chunk(void); -static int zebra_send_release_label_chunk(uint32_t start, uint32_t end); - -static void process_next_call(uint32_t start, uint32_t end) -{ - sleep(3); - if (!*sequence) - exit(0); - if (*sequence == 'G') - zebra_send_get_label_chunk(); - else if (*sequence == 'R') - zebra_send_release_label_chunk(start, end); -} - -/* Connect to Label Manager */ - -static int zebra_send_label_manager_connect() -{ - int ret; - - printf("Connect to Label Manager\n"); - - ret = lm_label_manager_connect(zclient, 0); - printf("Label Manager connection result: %u \n", ret); - if (ret != 0) { - fprintf(stderr, "Error %d connecting to Label Manager %s\n", - ret, strerror(errno)); - exit(1); - } - - process_next_call(0, 0); -} - -/* Get Label Chunk */ - -static int zebra_send_get_label_chunk() -{ - uint32_t start; - uint32_t end; - int ret; - - printf("Ask for label chunk \n"); - - ret = lm_get_label_chunk(zclient, KEEP, CHUNK_SIZE, &start, &end); - if (ret != 0) { - fprintf(stderr, "Error %d requesting label chunk %s\n", ret, - strerror(errno)); - exit(1); - } - - sequence++; - - printf("Label Chunk assign: %u - %u \n", start, end); - - process_next_call(start, end); -} - -/* Release Label Chunk */ - -static int zebra_send_release_label_chunk(uint32_t start, uint32_t end) -{ - struct stream *s; - int ret; - - printf("Release label chunk: %u - %u\n", start, end); - - ret = lm_release_label_chunk(zclient, start, end); - if (ret != 0) { - fprintf(stderr, "Error releasing label chunk\n"); - exit(1); - } - - sequence++; - - process_next_call(start - CHUNK_SIZE, end - CHUNK_SIZE); -} - - -void init_zclient(struct thread_master *master, char *lm_zserv_path) -{ - frr_zclient_addr(&zclient_addr, &zclient_addr_len, lm_zserv_path); - - zclient = zclient_new(master, &zclient_options_default); - /* zclient_init(zclient, ZEBRA_LABEL_MANAGER, 0); */ - zclient->sock = -1; - zclient->redist_default = ZEBRA_ROUTE_LDP; - zclient->instance = instance; - if (zclient_socket_connect(zclient) < 0) { - printf("Error connecting synchronous zclient!\n"); - exit(1); - } -} - -int main(int argc, char *argv[]) -{ - struct thread_master *master; - struct thread thread; - int ret; - - printf("Sequence to be tested: %s\n", sequence); - - master = thread_master_create(NULL); - init_zclient(master, ZSERV_PATH); - - zebra_send_label_manager_connect(); - - return 0; -} diff --git a/tests/topotests/Dockerfile b/tests/topotests/Dockerfile index ea6fa4b9e0..b7c6298228 100644 --- a/tests/topotests/Dockerfile +++ b/tests/topotests/Dockerfile @@ -6,9 +6,11 @@ RUN export DEBIAN_FRONTEND=noninteractive \ autoconf \ binutils \ bison \ + ca-certificates \ flex \ gdb \ git \ + gpg \ install-info \ iputils-ping \ iproute2 \ @@ -17,13 +19,18 @@ RUN export DEBIAN_FRONTEND=noninteractive \ libjson-c-dev \ libpcre3-dev \ libpython-dev \ + libpython3-dev \ libreadline-dev \ libc-ares-dev \ + libcap-dev \ man \ mininet \ pkg-config \ python-pip \ - python-sphinx \ + python3 \ + python3-dev \ + python3-sphinx \ + python3-pytest \ rsync \ strace \ tcpdump \ @@ -37,17 +44,15 @@ RUN export DEBIAN_FRONTEND=noninteractive \ && pip install \ exabgp==3.4.17 \ ipaddr \ - pytest + pytest \ + && rm -rf /var/lib/apt/lists/* -RUN cd /tmp \ - && wget -q https://ci1.netdef.org/artifact/LIBYANG-YANGRELEASE/shared/build-1/Ubuntu-18.04-x86_64-Packages/libyang-dev_0.16.46_amd64.deb \ - -O libyang-dev.deb \ - && wget -q https://ci1.netdef.org/artifact/LIBYANG-YANGRELEASE/shared/build-1/Ubuntu-18.04-x86_64-Packages/libyang_0.16.46_amd64.deb \ - -O libyang.deb \ - && echo "039252cc66eb254a97e160b1c325af669470cde8a02d73ec9f7b920ed3c7997c libyang.deb" | sha256sum -c - \ - && echo "e7e2d5bfc7b33b3218df8bef404432970f9b4ad10d6dbbdcb0e0be2babbb68e9 libyang-dev.deb" | sha256sum -c - \ - && dpkg -i libyang*.deb \ - && rm libyang*.deb +RUN export DEBIAN_FRONTEND=noninteractive \ + && apt-key adv --keyserver keyserver.ubuntu.com --recv-key 5418F291D0D4A1AA \ + && echo "deb https://deb.frrouting.org/frr bionic frr-stable" > /etc/apt/sources.list.d/frr.list \ + && apt-get update \ + && apt-get install -y libyang-dev \ + && rm -rf /var/lib/apt/lists/* RUN groupadd -r -g 92 frr \ && groupadd -r -g 85 frrvty \ diff --git a/tests/topotests/all-protocol-startup/r1/babeld.conf b/tests/topotests/all-protocol-startup/r1/babeld.conf new file mode 100644 index 0000000000..3e119bf24d --- /dev/null +++ b/tests/topotests/all-protocol-startup/r1/babeld.conf @@ -0,0 +1,4 @@ +router babel + network 192.168.1.1 + network 192.168.2.1 +! \ No newline at end of file diff --git a/tests/topotests/all-protocol-startup/r1/bgpd.conf b/tests/topotests/all-protocol-startup/r1/bgpd.conf index 4614287f27..e000b4e625 100644 --- a/tests/topotests/all-protocol-startup/r1/bgpd.conf +++ b/tests/topotests/all-protocol-startup/r1/bgpd.conf @@ -4,6 +4,8 @@ log file bgpd.log router bgp 100 bgp router-id 192.168.0.1 bgp log-neighbor-changes + no bgp ebgp-requires-policy + no bgp network import-check neighbor 192.168.7.10 remote-as 100 neighbor 192.168.7.20 remote-as 200 neighbor fc00:0:0:8::1000 remote-as 100 @@ -45,3 +47,6 @@ route-map bgp-map permit 20 line vty ! +route-map LIES deny 10 + match interface notpresent +! diff --git a/tests/topotests/all-protocol-startup/r1/ipv4_routes.ref b/tests/topotests/all-protocol-startup/r1/ipv4_routes.ref index e75d896721..044cffae7a 100644 --- a/tests/topotests/all-protocol-startup/r1/ipv4_routes.ref +++ b/tests/topotests/all-protocol-startup/r1/ipv4_routes.ref @@ -8,5 +8,25 @@ C>* 192.168.6.0/26 is directly connected, r1-eth6, XX:XX:XX C>* 192.168.7.0/26 is directly connected, r1-eth7, XX:XX:XX C>* 192.168.8.0/26 is directly connected, r1-eth8, XX:XX:XX C>* 192.168.9.0/26 is directly connected, r1-eth9, XX:XX:XX -O 192.168.0.0/24 [110/10] is directly connected, r1-eth0, XX:XX:XX -O 192.168.3.0/26 [110/10] is directly connected, r1-eth3, XX:XX:XX +O 192.168.0.0/24 [110/10] is directly connected, r1-eth0, weight 1, XX:XX:XX +O 192.168.3.0/26 [110/10] is directly connected, r1-eth3, weight 1, XX:XX:XX +S>* 1.1.1.1/32 [1/0] is directly connected, r1-eth1, weight 1, XX:XX:XX +S>* 1.1.1.2/32 [1/0] is directly connected, r1-eth2, weight 1, XX:XX:XX +S>* 1.1.1.3/32 [1/0] is directly connected, r1-eth3, weight 1, XX:XX:XX +S>* 1.1.1.4/32 [1/0] is directly connected, r1-eth4, weight 1, XX:XX:XX +S>* 1.1.1.5/32 [1/0] is directly connected, r1-eth5, weight 1, XX:XX:XX +S>* 1.1.1.6/32 [1/0] is directly connected, r1-eth6, weight 1, XX:XX:XX +S>* 1.1.1.7/32 [1/0] is directly connected, r1-eth7, weight 1, XX:XX:XX +S>* 1.1.1.8/32 [1/0] is directly connected, r1-eth8, weight 1, XX:XX:XX +S>* 4.5.6.10/32 [1/0] via 192.168.0.2, r1-eth0, weight 1, XX:XX:XX +S>* 4.5.6.11/32 [1/0] via 192.168.0.2, r1-eth0, weight 1, XX:XX:XX +S>* 4.5.6.12/32 [1/0] is directly connected, r1-eth0, weight 1, XX:XX:XX +S>* 4.5.6.13/32 [1/0] unreachable (blackhole), weight 1, XX:XX:XX +S>* 4.5.6.14/32 [1/0] unreachable (blackhole), weight 1, XX:XX:XX +S 4.5.6.15/32 [255/0] via 192.168.0.2, r1-eth0, weight 1, XX:XX:XX +S 4.5.6.16/32 [10/0] via 192.168.0.4, r1-eth0, weight 1, XX:XX:XX +S>* 4.5.6.16/32 [5/0] via 192.168.0.2, r1-eth0, weight 1, XX:XX:XX +S>* 4.5.6.17/32 [1/0] via 192.168.0.2, r1-eth0, weight 1, XX:XX:XX +S>* 4.5.6.7/32 [1/0] unreachable (blackhole), weight 1, XX:XX:XX +S>* 4.5.6.8/32 [1/0] unreachable (blackhole), weight 1, XX:XX:XX +S>* 4.5.6.9/32 [1/0] unreachable (ICMP unreachable), weight 1, XX:XX:XX diff --git a/tests/topotests/all-protocol-startup/r1/ipv6_routes.ref b/tests/topotests/all-protocol-startup/r1/ipv6_routes.ref index 88cee964d6..ef12d615dc 100644 --- a/tests/topotests/all-protocol-startup/r1/ipv6_routes.ref +++ b/tests/topotests/all-protocol-startup/r1/ipv6_routes.ref @@ -19,4 +19,11 @@ C * fe80::/64 is directly connected, r1-eth6, XX:XX:XX C * fe80::/64 is directly connected, r1-eth7, XX:XX:XX C * fe80::/64 is directly connected, r1-eth8, XX:XX:XX C * fe80::/64 is directly connected, r1-eth9, XX:XX:XX -O fc00:0:0:4::/64 [110/10] is directly connected, r1-eth4, XX:XX:XX +O fc00:0:0:4::/64 [110/10] is directly connected, r1-eth4, weight 1, XX:XX:XX +S>* 4:5::6:10/128 [1/0] via fc00::2, r1-eth0, weight 1, XX:XX:XX +S>* 4:5::6:11/128 [1/0] via fc00::2, r1-eth0, weight 1, XX:XX:XX +S>* 4:5::6:12/128 [1/0] is directly connected, r1-eth0, weight 1, XX:XX:XX +S 4:5::6:15/128 [255/0] via fc00::2, r1-eth0, weight 1, XX:XX:XX +S>* 4:5::6:7/128 [1/0] unreachable (blackhole), weight 1, XX:XX:XX +S>* 4:5::6:8/128 [1/0] unreachable (blackhole), weight 1, XX:XX:XX +S>* 4:5::6:9/128 [1/0] unreachable (ICMP unreachable), weight 1, XX:XX:XX diff --git a/tests/topotests/all-protocol-startup/r1/nhrpd.conf b/tests/topotests/all-protocol-startup/r1/nhrpd.conf new file mode 100644 index 0000000000..74e0f12e32 --- /dev/null +++ b/tests/topotests/all-protocol-startup/r1/nhrpd.conf @@ -0,0 +1 @@ +! \ No newline at end of file diff --git a/tests/topotests/all-protocol-startup/r1/ospf6d.conf b/tests/topotests/all-protocol-startup/r1/ospf6d.conf index 941d3016c7..5c6f071644 100644 --- a/tests/topotests/all-protocol-startup/r1/ospf6d.conf +++ b/tests/topotests/all-protocol-startup/r1/ospf6d.conf @@ -14,3 +14,6 @@ router ospf6 ! line vty ! +route-map LIES deny 10 + match interface notpresent +! diff --git a/tests/topotests/all-protocol-startup/r1/ospfd.conf b/tests/topotests/all-protocol-startup/r1/ospfd.conf index 549f36fab4..bac9f61620 100644 --- a/tests/topotests/all-protocol-startup/r1/ospfd.conf +++ b/tests/topotests/all-protocol-startup/r1/ospfd.conf @@ -11,3 +11,6 @@ router ospf ! line vty ! +route-map LIES deny 10 + match interface notpresent +! diff --git a/tests/topotests/all-protocol-startup/r1/pbrd.conf b/tests/topotests/all-protocol-startup/r1/pbrd.conf new file mode 100644 index 0000000000..360fb13a1b --- /dev/null +++ b/tests/topotests/all-protocol-startup/r1/pbrd.conf @@ -0,0 +1,10 @@ +log file pbrd.log + +nexthop-group A + nexthop 192.168.161.4 +! +pbr-map FOO seq 10 + match dst-ip 4.5.6.7/32 + match src-ip 6.7.8.8/32 + set nexthop-group A +! \ No newline at end of file diff --git a/tests/topotests/all-protocol-startup/r1/ripd.conf b/tests/topotests/all-protocol-startup/r1/ripd.conf index 4b35630b36..ace7608873 100644 --- a/tests/topotests/all-protocol-startup/r1/ripd.conf +++ b/tests/topotests/all-protocol-startup/r1/ripd.conf @@ -10,3 +10,6 @@ router rip line vty ! +route-map LIES deny 10 + match interface notpresent +! diff --git a/tests/topotests/all-protocol-startup/r1/ripngd.conf b/tests/topotests/all-protocol-startup/r1/ripngd.conf index 199fe15ab9..2e0aef3a14 100644 --- a/tests/topotests/all-protocol-startup/r1/ripngd.conf +++ b/tests/topotests/all-protocol-startup/r1/ripngd.conf @@ -9,3 +9,6 @@ router ripng line vty ! +route-map LIES deny 10 + match interface notpresent +! diff --git a/tests/topotests/all-protocol-startup/r1/show_bgp_ipv6_summary.ref b/tests/topotests/all-protocol-startup/r1/show_bgp_ipv6_summary.ref index 85388c738d..1e41263e78 100644 --- a/tests/topotests/all-protocol-startup/r1/show_bgp_ipv6_summary.ref +++ b/tests/topotests/all-protocol-startup/r1/show_bgp_ipv6_summary.ref @@ -3,6 +3,6 @@ BGP table version 1 RIB entries 1, using XXXX bytes of memory Peers 2, using XXXX KiB of memory -Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd -fc00:0:0:8::1000 4 100 0 0 0 0 0 never Active -fc00:0:0:8::2000 4 200 0 0 0 0 0 never Active +Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt +fc00:0:0:8::1000 4 100 0 0 0 0 0 never Active 0 +fc00:0:0:8::2000 4 200 0 0 0 0 0 never Active 0 diff --git a/tests/topotests/all-protocol-startup/r1/show_ip_bgp_summary.ref b/tests/topotests/all-protocol-startup/r1/show_ip_bgp_summary.ref index 4f0ac1c910..3ffbf3ff42 100644 --- a/tests/topotests/all-protocol-startup/r1/show_ip_bgp_summary.ref +++ b/tests/topotests/all-protocol-startup/r1/show_ip_bgp_summary.ref @@ -3,8 +3,8 @@ BGP table version 1 RIB entries 1, using XXXX bytes of memory Peers 4, using XXXX KiB of memory -Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd -192.168.7.10 4 100 0 0 0 0 0 never Active -192.168.7.20 4 200 0 0 0 0 0 never Active -fc00:0:0:8::1000 4 100 0 0 0 0 0 never Active -fc00:0:0:8::2000 4 200 0 0 0 0 0 never Active +Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt +192.168.7.10 4 100 0 0 0 0 0 never Active 0 +192.168.7.20 4 200 0 0 0 0 0 never Active 0 +fc00:0:0:8::1000 4 100 0 0 0 0 0 never Active 0 +fc00:0:0:8::2000 4 200 0 0 0 0 0 never Active 0 diff --git a/tests/topotests/all-protocol-startup/r1/show_ip_ospf_interface.ref b/tests/topotests/all-protocol-startup/r1/show_ip_ospf_interface.ref index c29ed3db61..1e8f67f3f9 100644 --- a/tests/topotests/all-protocol-startup/r1/show_ip_ospf_interface.ref +++ b/tests/topotests/all-protocol-startup/r1/show_ip_ospf_interface.ref @@ -1,5 +1,5 @@ r1-eth0 is up - ifindex 2, MTU 1500 bytes, BW XX Mbit + ifindex X, MTU 1500 bytes, BW XX Mbit Internet Address 192.168.0.1/24, Broadcast 192.168.0.255, Area 0.0.0.0 MTU mismatch detection: enabled Router ID 192.168.0.1, Network Type BROADCAST, Cost: 10 @@ -10,7 +10,7 @@ r1-eth0 is up Hello due in XX.XXXs Neighbor Count is 0, Adjacent neighbor count is 0 r1-eth3 is up - ifindex 5, MTU 1500 bytes, BW XX Mbit + ifindex X, MTU 1500 bytes, BW XX Mbit Internet Address 192.168.3.1/26, Broadcast 192.168.3.63, Area 0.0.0.0 MTU mismatch detection: enabled Router ID 192.168.0.1, Network Type BROADCAST, Cost: 10 diff --git a/tests/topotests/all-protocol-startup/r1/show_route_map.ref b/tests/topotests/all-protocol-startup/r1/show_route_map.ref new file mode 100644 index 0000000000..612d0a729d --- /dev/null +++ b/tests/topotests/all-protocol-startup/r1/show_route_map.ref @@ -0,0 +1,72 @@ +ZEBRA: +route-map: LIES Invoked: 0 Optimization: enabled Processed Change: false + deny, sequence 10 Invoked 0 + Match clauses: + interface notpresent + Set clauses: + Call clause: + Action: + Exit routemap +RIP: +route-map: LIES Invoked: 0 Optimization: enabled Processed Change: false + deny, sequence 10 Invoked 0 + Match clauses: + interface notpresent + Set clauses: + Call clause: + Action: + Exit routemap +RIPNG: +route-map: LIES Invoked: 0 Optimization: enabled Processed Change: false + deny, sequence 10 Invoked 0 + Match clauses: + interface notpresent + Set clauses: + Call clause: + Action: + Exit routemap +OSPF: +route-map: LIES Invoked: 0 Optimization: enabled Processed Change: false + deny, sequence 10 Invoked 0 + Match clauses: + interface notpresent + Set clauses: + Call clause: + Action: + Exit routemap +OSPF6: +route-map: LIES Invoked: 0 Optimization: enabled Processed Change: false + deny, sequence 10 Invoked 0 + Match clauses: + interface notpresent + Set clauses: + Call clause: + Action: + Exit routemap +BGP: +route-map: LIES Invoked: 0 Optimization: enabled Processed Change: false + deny, sequence 10 Invoked 0 + Match clauses: + interface notpresent + Set clauses: + Call clause: + Action: + Exit routemap +route-map: bgp-map Invoked: 0 Optimization: enabled Processed Change: false + permit, sequence 10 Invoked 0 + Match clauses: + Set clauses: + community 100:100 additive + local-preference 100 + Call clause: + Action: + Exit routemap + permit, sequence 20 Invoked 0 + Match clauses: + Set clauses: + metric 10 + local-preference 200 + Call clause: + Action: + Exit routemap +ISIS: diff --git a/tests/topotests/all-protocol-startup/r1/zebra.conf b/tests/topotests/all-protocol-startup/r1/zebra.conf index 164104da7e..c5ef79630e 100644 --- a/tests/topotests/all-protocol-startup/r1/zebra.conf +++ b/tests/topotests/all-protocol-startup/r1/zebra.conf @@ -1,6 +1,51 @@ log file zebra.log ! hostname r1 +! +# Create the various blackhole route types +ip route 4.5.6.7/32 blackhole +ipv6 route 4:5::6:7/128 blackhole +ip route 4.5.6.8/32 Null0 +ipv6 route 4:5::6:8/128 Null0 +ip route 4.5.6.9/32 reject +ipv6 route 4:5::6:9/128 reject +# Test various spellings of NULL0 to make sure we accept them +ip route 4.5.6.13/32 null0 +ip route 4.5.6.14/32 NULL0 +# Create normal gateway routes +ip route 4.5.6.10/32 192.168.0.2 +ipv6 route 4:5::6:10/128 fc00:0:0:0::2 +# Create normal gateway + interface routes +ip route 4.5.6.11/32 192.168.0.2 r1-eth0 +ipv6 route 4:5::6:11/128 fc00:0:0:0::2 r1-eth0 +# Create ifname routes +ip route 4.5.6.12/32 r1-eth0 +ipv6 route 4:5::6:12/128 r1-eth0 +# Create a route that has a large admin distance +# an admin distance of 255 should be accepted +# by zebra but not installed. +ip route 4.5.6.15/32 192.168.0.2 255 +ipv6 route 4:5::6:15/128 fc00:0:0:0::2 255 +# Routes to put into a nexthop-group +ip route 1.1.1.1/32 r1-eth1 +ip route 1.1.1.2/32 r1-eth2 +ip route 1.1.1.3/32 r1-eth3 +ip route 1.1.1.4/32 r1-eth4 +ip route 1.1.1.5/32 r1-eth5 +ip route 1.1.1.6/32 r1-eth6 +ip route 1.1.1.7/32 r1-eth7 +ip route 1.1.1.8/32 r1-eth8 + +# Create a route that has overlapping distance +# so we have backups +ip route 4.5.6.16/32 192.168.0.2 5 +ip route 4.5.6.16/32 192.168.0.4 10 + +# Create routes that have different tags +# and how we handle it +ip route 4.5.6.17/32 192.168.0.2 tag 9000 +ip route 4.5.6.17/32 192.168.0.2 tag 10000 + ! interface r1-eth0 description to sw0 - no routing protocol @@ -70,3 +115,6 @@ ipv6 forwarding line vty ! +route-map LIES deny 10 + match interface notpresent +! 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 239de55bd6..fb19f5a04d 100755 --- a/tests/topotests/all-protocol-startup/test_all_protocol_startup.py +++ b/tests/topotests/all-protocol-startup/test_all_protocol_startup.py @@ -120,6 +120,10 @@ def setup_module(module): if net['r%s' % i].daemon_available('ldpd'): # Only test LDPd if it's installed and Kernel >= 4.5 net['r%s' % i].loadConf('ldpd', '%s/r%s/ldpd.conf' % (thisDir, i)) + net['r%s' % i].loadConf('sharpd') + net['r%s' % i].loadConf('nhrpd', '%s/r%s/nhrpd.conf' % (thisDir, i)) + net['r%s' % i].loadConf('babeld', '%s/r%s/babeld.conf' % (thisDir, i)) + net['r%s' % i].loadConf('pbrd', '%s/r%s/pbrd.conf' % (thisDir, i)) net['r%s' % i].startRouter() # For debugging after starting Quagga/FRR daemons, uncomment the next line @@ -259,6 +263,22 @@ def test_error_messages_daemons(): if log: error_logs += "r%s LDPd StdErr Output:\n" % i error_logs += log + + log = net['r1'].getStdErr('nhrpd') + if log: + error_logs += "r%s NHRPd StdErr Output:\n" % i + error_logs += log + + log = net['r1'].getStdErr('babeld') + if log: + error_logs += "r%s BABELd StdErr Output:\n" % i + error_logs += log + + log = net['r1'].getStdErr('pbrd') + if log: + error_logs += "r%s PBRd StdErr Output:\n" % i + error_logs += log + log = net['r%s' % i].getStdErr('zebra') if log: error_logs += "r%s Zebra StdErr Output:\n" @@ -306,7 +326,7 @@ def test_converge_protocols(): expected = open(v4_routesFile).read().rstrip() expected = ('\n'.join(expected.splitlines()) + '\n').splitlines(1) - actual = net['r%s' %i].cmd('vtysh -c "show ip route" | /usr/bin/tail -n +7 | sort 2> /dev/null').rstrip() + actual = net['r%s' %i].cmd('vtysh -c "show ip route" | /usr/bin/tail -n +7 | env LC_ALL=en_US.UTF-8 sort 2> /dev/null').rstrip() # Drop time in last update actual = re.sub(r" [0-2][0-9]:[0-5][0-9]:[0-5][0-9]", " XX:XX:XX", actual) actual = ('\n'.join(actual.splitlines()) + '\n').splitlines(1) @@ -328,7 +348,7 @@ def test_converge_protocols(): expected = open(v6_routesFile).read().rstrip() expected = ('\n'.join(expected.splitlines()) + '\n').splitlines(1) - actual = net['r%s' %i].cmd('vtysh -c "show ipv6 route" | /usr/bin/tail -n +7 | sort 2> /dev/null').rstrip() + actual = net['r%s' %i].cmd('vtysh -c "show ipv6 route" | /usr/bin/tail -n +7 | env LC_ALL=en_US.UTF-8 sort 2> /dev/null').rstrip() # Drop time in last update actual = re.sub(r" [0-2][0-9]:[0-5][0-9]:[0-5][0-9]", " XX:XX:XX", actual) actual = ('\n'.join(actual.splitlines()) + '\n').splitlines(1) @@ -346,6 +366,117 @@ def test_converge_protocols(): # For debugging after starting FRR/Quagga daemons, uncomment the next line ## CLI(net) +def route_get_nhg_id(route_str): + output = net["r1"].cmd('vtysh -c "show ip route %s nexthop-group"' % route_str) + match = re.search(r"Nexthop Group ID: (\d+)", output) + assert match is not None, "Nexthop Group ID not found for sharpd route %s" % route_str + + nhg_id = int(match.group(1)) + return nhg_id + +def verify_nexthop_group(nhg_id, recursive=False): + # 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) + 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): + # 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) + +def test_nexthop_groups(): + 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 + + ## Basic test + + # Create a lib nexthop-group + net["r1"].cmd('vtysh -c "c t" -c "nexthop-group basic" -c "nexthop 1.1.1.1" -c "nexthop 1.1.1.2"') + + # Create with sharpd using nexthop-group + net["r1"].cmd('vtysh -c "sharp install routes 2.2.2.1 nexthop-group basic 1"') + + verify_route_nexthop_group("2.2.2.1/32") + + ## Connected + + net["r1"].cmd('vtysh -c "c t" -c "nexthop-group connected" -c "nexthop r1-eth1" -c "nexthop r1-eth2"') + + net["r1"].cmd('vtysh -c "sharp install routes 2.2.2.2 nexthop-group connected 1"') + + verify_route_nexthop_group("2.2.2.2/32") + + ## Recursive + + net["r1"].cmd('vtysh -c "c t" -c "nexthop-group basic-recursive" -c "nexthop 2.2.2.1"') + + net["r1"].cmd('vtysh -c "sharp install routes 3.3.3.1 nexthop-group basic-recursive 1"') + + verify_route_nexthop_group("3.3.3.1/32", True) + + ## Duplicate + + net["r1"].cmd('vtysh -c "c t" -c "nexthop-group duplicate" -c "nexthop 2.2.2.1" -c "nexthop 1.1.1.1"') + + net["r1"].cmd('vtysh -c "sharp install routes 3.3.3.2 nexthop-group duplicate 1"') + + verify_route_nexthop_group("3.3.3.2/32") + + ## Two 4-Way ECMP + + net["r1"].cmd('vtysh -c "c t" -c "nexthop-group fourA" -c "nexthop 1.1.1.1" -c "nexthop 1.1.1.2" \ + -c "nexthop 1.1.1.3" -c "nexthop 1.1.1.4"') + + net["r1"].cmd('vtysh -c "sharp install routes 4.4.4.1 nexthop-group fourA 1"') + + verify_route_nexthop_group("4.4.4.1/32") + + net["r1"].cmd('vtysh -c "c t" -c "nexthop-group fourB" -c "nexthop 1.1.1.5" -c "nexthop 1.1.1.6" \ + -c "nexthop 1.1.1.7" -c "nexthop 1.1.1.8"') + + net["r1"].cmd('vtysh -c "sharp install routes 4.4.4.2 nexthop-group fourB 1"') + + verify_route_nexthop_group("4.4.4.2/32") + + ## Recursive to 8-Way ECMP + + net["r1"].cmd('vtysh -c "c t" -c "nexthop-group eight-recursive" -c "nexthop 4.4.4.1" -c "nexthop 4.4.4.2"') + + net["r1"].cmd('vtysh -c "sharp install routes 5.5.5.1 nexthop-group eight-recursive 1"') + + verify_route_nexthop_group("5.5.5.1/32") + + ##CLI(net) + + ## Remove all NHG routes + + net["r1"].cmd('vtysh -c "sharp remove routes 2.2.2.1 1"') + net["r1"].cmd('vtysh -c "sharp remove routes 2.2.2.2 1"') + net["r1"].cmd('vtysh -c "sharp remove routes 3.3.3.1 1"') + net["r1"].cmd('vtysh -c "sharp remove routes 3.3.3.2 1"') + net["r1"].cmd('vtysh -c "sharp remove routes 4.4.4.1 1"') + net["r1"].cmd('vtysh -c "sharp remove routes 4.4.4.2 1"') + net["r1"].cmd('vtysh -c "sharp remove routes 5.5.5.1 1"') def test_rip_status(): global fatal_error @@ -480,8 +611,11 @@ 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) + # Drop time in next due actual = re.sub(r"Hello due in [0-9\.]+s", "Hello due in XX.XXXs", actual) + actual = re.sub(r"Hello due in [0-9\.]+ usecs", "Hello due in XX.XXXs", actual) # Fix 'MTU mismatch detection: enabled' vs 'MTU mismatch detection:enabled' - accept both actual = re.sub(r"MTU mismatch detection:([a-z]+.*)", r"MTU mismatch detection: \1", actual) # Fix newlines (make them all the same) @@ -544,7 +678,8 @@ def test_isis_interfaces(): # Mask out SNPA mac address portion. They are random... actual = re.sub(r"SNPA: [0-9a-f\.]+", "SNPA: XXXX.XXXX.XXXX", actual) # Mask out Circuit ID number - actual = re.sub(r"Circuit Id: 0x[0-9]+", "Circuit Id: 0xXX", actual) + actual = re.sub(r"Circuit Id: 0x[0-9a-f]+", "Circuit Id: 0xXX", + actual) # Fix newlines (make them all the same) actual = ('\n'.join(actual.splitlines()) + '\n').splitlines(1) @@ -858,7 +993,114 @@ def test_bgp_ipv6(): # For debugging after starting FRR/Quagga daemons, uncomment the next line # CLI(net) +def test_route_map(): + global fatal_error + global net + + if (fatal_error != ""): + pytest.skip(fatal_error) + + thisDir = os.path.dirname(os.path.realpath(__file__)) + + print("\n\n** Verifying some basic routemap forward references\n") + print("*******************************************************\n") + failures = 0 + for i in range(1, 2): + refroutemap = '%s/r%s/show_route_map.ref' % (thisDir, i) + if os.path.isfile(refroutemap): + expected = open(refroutemap).read().rstrip() + expected = ('\n'.join(expected.splitlines()) + '\n').splitlines(1) + + actual = net['r%s' %i].cmd('vtysh -c "show route-map" 2> /dev/null').rstrip() + actual = ('\n'.join(actual.splitlines()) + '\n').splitlines(1) + + diff = topotest.get_textdiff(actual, expected, + title1="actual show route-map", + title2="expected show route-map") + + if diff: + sys.stderr.write('r%s failed show route-map command Check:\n%s\n' % (i, diff)) + failures += 1 + else: + print("r%s ok" %i) + + assert failures == 0, "Show route-map command failed for router r%s:\n%s" % (i, diff) +def test_nexthop_groups_with_route_maps(): + 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 With Route-Maps") + print("******************************************\n") + + ### Nexthop Group With Route-Map Tests + + # Create a lib nexthop-group + net["r1"].cmd('vtysh -c "c t" -c "nexthop-group test" -c "nexthop 1.1.1.1" -c "nexthop 1.1.1.2"') + + ## Route-Map Proto Source + + route_str = "2.2.2.1" + src_str = "192.168.0.1" + + net["r1"].cmd('vtysh -c "c t" -c "route-map NH-SRC permit 111" -c "set src %s"' % src_str) + net["r1"].cmd('vtysh -c "c t" -c "ip protocol sharp route-map NH-SRC"') + + net["r1"].cmd('vtysh -c "sharp install routes %s nexthop-group test 1"' % route_str) + + verify_route_nexthop_group("%s/32" % route_str) + + # Only a valid test on linux using nexthop objects + if sys.platform.startswith("linux"): + output = net["r1"].cmd('ip route show %s/32' % route_str) + match = re.search(r"src %s" % src_str, output) + assert match is not None, "Route %s/32 not installed with src %s" % (route_str, src_str) + + # Remove NHG routes and route-map + net["r1"].cmd('vtysh -c "sharp remove routes %s 1"' % route_str) + net["r1"].cmd('vtysh -c "c t" -c "no ip protocol sharp route-map NH-SRC"') + net["r1"].cmd('vtysh -c "c t" -c "no route-map NH-SRC permit 111" -c "set src %s"' % src_str) + net["r1"].cmd('vtysh -c "c t" -c "no route-map NH-SRC"') + + ## Route-Map Deny/Permit with same nexthop group + + permit_route_str = "3.3.3.1" + deny_route_str = "3.3.3.2" + + net["r1"].cmd('vtysh -c "c t" -c "ip prefix-list NOPE seq 5 permit %s/32"' % permit_route_str) + net["r1"].cmd('vtysh -c "c t" -c "route-map NOPE permit 111" -c "match ip address prefix-list NOPE"') + net["r1"].cmd('vtysh -c "c t" -c "route-map NOPE deny 222"') + net["r1"].cmd('vtysh -c "c t" -c "ip protocol sharp route-map NOPE"') + + # This route should be permitted + net["r1"].cmd('vtysh -c "sharp install routes %s nexthop-group test 1"' % permit_route_str) + + verify_route_nexthop_group("%s/32" % permit_route_str) + + # This route should be denied + net["r1"].cmd('vtysh -c "sharp install routes %s nexthop-group test 1"' % deny_route_str) + + nhg_id = route_get_nhg_id(deny_route_str) + output = net["r1"].cmd('vtysh -c "show nexthop-group rib %d"' % nhg_id) + + match = re.search(r"Valid", output) + assert match is None, "Nexthop Group ID=%d should not be marked Valid" % nhg_id + + match = re.search(r"Installed", output) + assert match is None, "Nexthop Group ID=%d should not be marked Installed" % nhg_id + + # Remove NHG routes and route-map + net["r1"].cmd('vtysh -c "sharp remove routes %s 1"' % permit_route_str) + net["r1"].cmd('vtysh -c "sharp remove routes %s 1"' % deny_route_str) + net["r1"].cmd('vtysh -c "c t" -c "no ip protocol sharp route-map NOPE"') + net["r1"].cmd('vtysh -c "c t" -c "no route-map NOPE permit 111"') + net["r1"].cmd('vtysh -c "c t" -c "no route-map NOPE deny 222"') + 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_mpls_interfaces(): global fatal_error @@ -957,6 +1199,19 @@ def test_shutdown_check_stderr(): log = net['r1'].getStdErr('bgpd') if log: print("\nBGPd StdErr Log:\n" + log) + + log = net['r1'].getStdErr('nhrpd') + if log: + print("\nNHRPd StdErr Log:\n" + log) + + log = net['r1'].getStdErr('pbrd') + if log: + print("\nPBRd StdErr Log:\n" + log) + + log = net['r1'].getStdErr('babeld') + if log: + print("\nBABELd StdErr Log:\n" + log) + if (net['r1'].daemon_available('ldpd')): log = net['r1'].getStdErr('ldpd') if log: diff --git a/tests/topotests/bfd-bgp-cbit-topo3/__init__.py b/tests/topotests/bfd-bgp-cbit-topo3/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bfd-bgp-cbit-topo3/r1/bfdd.conf b/tests/topotests/bfd-bgp-cbit-topo3/r1/bfdd.conf new file mode 100644 index 0000000000..f35e772790 --- /dev/null +++ b/tests/topotests/bfd-bgp-cbit-topo3/r1/bfdd.conf @@ -0,0 +1,5 @@ +! +debug bfd network +debug bfd peer +debug bfd zebra +! diff --git a/tests/topotests/bfd-bgp-cbit-topo3/r1/bgp_ipv6_routes_down.json b/tests/topotests/bfd-bgp-cbit-topo3/r1/bgp_ipv6_routes_down.json new file mode 100644 index 0000000000..5cba71ef20 --- /dev/null +++ b/tests/topotests/bfd-bgp-cbit-topo3/r1/bgp_ipv6_routes_down.json @@ -0,0 +1,98 @@ +{ + "vrfName": "default", + "routerId": "10.254.254.1", + "localAS": 101, + "routes": + { + "2001:db8:6::/64": [ + { + "stale": true, + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "2001:db8:6::", + "prefixLen": 64, + "network": "2001:db8:6::\/64", + "metric": 0, + "weight": 0, + "peerId": "2001:db8:4::1", + "origin": "IGP", + "nexthops": [ + { "ip": "2001:db8:4::1", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8:7::/64": [ + { + "stale": true, + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "2001:db8:7::", + "prefixLen": 64, "network": + "2001:db8:7::\/64", + "metric": 0, + "weight": 0, + "peerId": "2001:db8:4::1", + "origin": "IGP", + "nexthops": [ + { + "ip": "2001:db8:4::1", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8:8::/64": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "2001:db8:8::", + "prefixLen": 64, + "network": "2001:db8:8::\/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "origin": "IGP", + "nexthops": [ + { + "ip": "::", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8:9::/64": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "2001:db8:9::", + "prefixLen": 64, + "network": "2001:db8:9::\/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "origin": "IGP", + "nexthops": [ + { + "ip": "::", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ] + } +} + diff --git a/tests/topotests/bfd-bgp-cbit-topo3/r1/bgpd.conf b/tests/topotests/bfd-bgp-cbit-topo3/r1/bgpd.conf new file mode 100644 index 0000000000..82b189b292 --- /dev/null +++ b/tests/topotests/bfd-bgp-cbit-topo3/r1/bgpd.conf @@ -0,0 +1,22 @@ +debug bgp neighbor-events +router bgp 101 + bgp router-id 10.254.254.1 + no bgp ebgp-requires-policy + no bgp network import-check + timers bgp 8 24 + bgp graceful-restart + neighbor 2001:db8:4::1 remote-as 102 + neighbor 2001:db8:4::1 remote-as external + neighbor 2001:db8:4::1 bfd + neighbor 2001:db8:4::1 bfd check-control-plane-failure + neighbor 2001:db8:4::1 update-source 2001:db8:1::1 + neighbor 2001:db8:4::1 ebgp-multihop 5 + address-family ipv4 unicast + no neighbor 2001:db8:4::1 activate + exit-address-family + address-family ipv6 unicast + network 2001:db8:8::/64 + network 2001:db8:9::/64 + neighbor 2001:db8:4::1 activate + exit-address-family +! diff --git a/tests/topotests/bfd-bgp-cbit-topo3/r1/ipv6_routes.json b/tests/topotests/bfd-bgp-cbit-topo3/r1/ipv6_routes.json new file mode 100644 index 0000000000..8eea183285 --- /dev/null +++ b/tests/topotests/bfd-bgp-cbit-topo3/r1/ipv6_routes.json @@ -0,0 +1,80 @@ +{ + "2001:db8:1::/64": [{ + "distance": 0, + "protocol": "connected", + "metric": 0, + "selected": true, + "destSelected": true, + "prefix": "2001:db8:1::/64", + "nexthops": [{ + "directlyConnected": true, + "interfaceName": "r1-eth0", + "fib": true, + "flags": 3, + "active": true + } + ] + } + ], + "2001:db8:4::/64": [{ + "distance": 1, + "protocol": "static", + "metric": 0, + "selected": true, + "destSelected": true, + "prefix": "2001:db8:4::/64", + "nexthops": [{ + "interfaceName": "r1-eth0", + "fib": true, + "flags": 3, + "active": true, + "afi": "ipv6" + } + ] + } + ], + "2001:db8:6::/64": [{ + "distance": 20, + "protocol": "bgp", + "metric": 0, + "selected": true, + "destSelected": true, + "prefix": "2001:db8:6::/64", + "nexthops": [{ + "ip":"2001:db8:4::1", + "active": true, + "afi": "ipv6", + "recursive":true + }, + { + "fib":true, + "ip":"2001:db8:1::2", + "afi": "ipv6", + "interfaceName": "r1-eth0" + } + ] + } + ], + "2001:db8:7::/64": [{ + "distance": 20, + "protocol": "bgp", + "metric": 0, + "selected": true, + "destSelected": true, + "prefix": "2001:db8:7::/64", + "nexthops": [{ + "ip":"2001:db8:4::1", + "active": true, + "afi": "ipv6", + "recursive": true + }, + { + "fib":true, + "ip":"2001:db8:1::2", + "afi": "ipv6", + "interfaceName":"r1-eth0" + } + ] + } + ] +} diff --git a/tests/topotests/bfd-bgp-cbit-topo3/r1/peers.json b/tests/topotests/bfd-bgp-cbit-topo3/r1/peers.json new file mode 100644 index 0000000000..d1927ae49a --- /dev/null +++ b/tests/topotests/bfd-bgp-cbit-topo3/r1/peers.json @@ -0,0 +1,16 @@ +[ + { + "multihop":true, + "peer":"2001:db8:4::1", + "local":"2001:db8:1::1", + "status":"up", + "diagnostic":"ok", + "remote-diagnostic":"ok", + "receive-interval":300, + "transmit-interval":300, + "echo-interval":0, + "remote-receive-interval":300, + "remote-transmit-interval":300, + "remote-echo-interval":50 + } +] diff --git a/tests/topotests/bfd-bgp-cbit-topo3/r1/peers_down.json b/tests/topotests/bfd-bgp-cbit-topo3/r1/peers_down.json new file mode 100644 index 0000000000..25b47f18ec --- /dev/null +++ b/tests/topotests/bfd-bgp-cbit-topo3/r1/peers_down.json @@ -0,0 +1,14 @@ +[ + { + "multihop":true, + "peer":"2001:db8:4::1", + "local":"2001:db8:1::1", + "status":"up", + "receive-interval":300, + "transmit-interval":300, + "echo-interval":0, + "remote-receive-interval":300, + "remote-transmit-interval":300, + "remote-echo-interval":50 + } +] diff --git a/tests/topotests/bfd-bgp-cbit-topo3/r1/zebra.conf b/tests/topotests/bfd-bgp-cbit-topo3/r1/zebra.conf new file mode 100644 index 0000000000..3a30cd42fb --- /dev/null +++ b/tests/topotests/bfd-bgp-cbit-topo3/r1/zebra.conf @@ -0,0 +1,8 @@ +interface lo + ip address 10.254.254.1/32 +! +interface r1-eth0 + ipv6 address 2001:db8:1::1/64 +! +ipv6 route 2001:db8:4::/64 2001:db8:1::2 + diff --git a/tests/topotests/bfd-bgp-cbit-topo3/r2/zebra.conf b/tests/topotests/bfd-bgp-cbit-topo3/r2/zebra.conf new file mode 100644 index 0000000000..0f70be1bda --- /dev/null +++ b/tests/topotests/bfd-bgp-cbit-topo3/r2/zebra.conf @@ -0,0 +1,9 @@ +ip forwarding +ipv6 forwarding +! +interface r2-eth0 + ipv6 address 2001:db8:1::2/64 +! +interface r2-eth1 + ipv6 address 2001:db8:4::2/64 +! diff --git a/tests/topotests/bfd-bgp-cbit-topo3/r3/bfdd.conf b/tests/topotests/bfd-bgp-cbit-topo3/r3/bfdd.conf new file mode 100644 index 0000000000..f35e772790 --- /dev/null +++ b/tests/topotests/bfd-bgp-cbit-topo3/r3/bfdd.conf @@ -0,0 +1,5 @@ +! +debug bfd network +debug bfd peer +debug bfd zebra +! diff --git a/tests/topotests/bfd-bgp-cbit-topo3/r3/bgp_ipv6_routes_down.json b/tests/topotests/bfd-bgp-cbit-topo3/r3/bgp_ipv6_routes_down.json new file mode 100644 index 0000000000..c0cb3c4ac9 --- /dev/null +++ b/tests/topotests/bfd-bgp-cbit-topo3/r3/bgp_ipv6_routes_down.json @@ -0,0 +1,52 @@ +{ + "vrfName": "default", + "routerId": "10.254.254.3", + "localAS": 102, + "routes": + { + "2001:db8:6::/64": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "2001:db8:6::", + "prefixLen": 64, + "network": "2001:db8:6::\/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "origin": "IGP", + "nexthops": [ + { + "ip": "::", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8:7::/64": [ + { + "valid": true, + "bestpath": true, + "pathFrom": "external", + "prefix": "2001:db8:7::", + "prefixLen": 64, + "network": "2001:db8:7::\/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "origin": "IGP", + "nexthops": [ + { + "ip": "::", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ] + } +} diff --git a/tests/topotests/bfd-bgp-cbit-topo3/r3/bgpd.conf b/tests/topotests/bfd-bgp-cbit-topo3/r3/bgpd.conf new file mode 100644 index 0000000000..e566e08f7d --- /dev/null +++ b/tests/topotests/bfd-bgp-cbit-topo3/r3/bgpd.conf @@ -0,0 +1,27 @@ +debug bgp neighbor-events +router bgp 102 + bgp router-id 10.254.254.3 + no bgp ebgp-requires-policy + no bgp network import-check + timers bgp 20 60 + bgp graceful-restart + ! simulate NSF machine + bgp graceful-restart preserve-fw-state + bgp graceful-restart stalepath-time 900 + bgp graceful-restart restart-time 900 + neighbor 2001:db8:1::1 remote-as 101 + neighbor 2001:db8:1::1 remote-as external + neighbor 2001:db8:1::1 update-source 2001:db8:4::1 + neighbor 2001:db8:1::1 bfd + neighbor 2001:db8:1::1 ebgp-multihop 5 + ! + address-family ipv4 unicast + no neighbor 2001:db8:1::1 activate + exit-address-family + ! + address-family ipv6 unicast + neighbor 2001:db8:1::1 activate + network 2001:db8:6::/64 + network 2001:db8:7::/64 + exit-address-family +! diff --git a/tests/topotests/bfd-bgp-cbit-topo3/r3/ipv6_routes.json b/tests/topotests/bfd-bgp-cbit-topo3/r3/ipv6_routes.json new file mode 100644 index 0000000000..09808cc09a --- /dev/null +++ b/tests/topotests/bfd-bgp-cbit-topo3/r3/ipv6_routes.json @@ -0,0 +1,80 @@ +{ + "2001:db8:1::/64": [{ + "distance": 1, + "protocol": "static", + "metric": 0, + "selected": true, + "destSelected": true, + "prefix": "2001:db8:1::/64", + "nexthops": [{ + "interfaceName": "r3-eth0", + "fib": true, + "flags": 3, + "active": true, + "afi": "ipv6" + } + ] + } + ], + "2001:db8:4::/64": [{ + "distance": 0, + "protocol": "connected", + "metric": 0, + "selected": true, + "destSelected": true, + "prefix": "2001:db8:4::/64", + "nexthops": [{ + "directlyConnected": true, + "interfaceName": "r3-eth0", + "fib": true, + "flags": 3, + "active": true + } + ] + } + ], + "2001:db8:8::/64": [{ + "distance": 20, + "protocol": "bgp", + "metric": 0, + "selected": true, + "destSelected": true, + "prefix": "2001:db8:8::/64", + "nexthops": [{ + "ip":"2001:db8:1::1", + "active": true, + "afi": "ipv6", + "recursive":true + }, + { + "fib":true, + "ip":"2001:db8:4::2", + "afi": "ipv6", + "interfaceName":"r3-eth0" + } + ] + } + ], + "2001:db8:9::/64": [{ + "distance": 20, + "protocol": "bgp", + "metric": 0, + "selected": true, + "destSelected": true, + "prefix": "2001:db8:9::/64", + "nexthops": [{ + "ip":"2001:db8:1::1", + "active": true, + "afi": "ipv6", + "recursive":true + }, + { + "fib":true, + "ip":"2001:db8:4::2", + "afi": "ipv6", + "interfaceName":"r3-eth0" + } + ] + } + ] +} diff --git a/tests/topotests/bfd-bgp-cbit-topo3/r3/peers.json b/tests/topotests/bfd-bgp-cbit-topo3/r3/peers.json new file mode 100644 index 0000000000..5193f2a6e2 --- /dev/null +++ b/tests/topotests/bfd-bgp-cbit-topo3/r3/peers.json @@ -0,0 +1,16 @@ +[ + { + "multihop":true, + "peer":"2001:db8:1::1", + "local":"2001:db8:4::1", + "status":"up", + "diagnostic":"ok", + "remote-diagnostic":"ok", + "receive-interval":300, + "transmit-interval":300, + "echo-interval":0, + "remote-receive-interval":300, + "remote-transmit-interval":300, + "remote-echo-interval":50 + } +] diff --git a/tests/topotests/bfd-bgp-cbit-topo3/r3/peers_down.json b/tests/topotests/bfd-bgp-cbit-topo3/r3/peers_down.json new file mode 100644 index 0000000000..9e4bd2633f --- /dev/null +++ b/tests/topotests/bfd-bgp-cbit-topo3/r3/peers_down.json @@ -0,0 +1,14 @@ +[ + { + "multihop":true, + "peer":"2001:db8:1::1", + "local":"2001:db8:4::1", + "status":"down", + "receive-interval":300, + "transmit-interval":300, + "echo-interval":0, + "remote-receive-interval":300, + "remote-transmit-interval":300, + "remote-echo-interval":50 + } +] diff --git a/tests/topotests/bfd-bgp-cbit-topo3/r3/zebra.conf b/tests/topotests/bfd-bgp-cbit-topo3/r3/zebra.conf new file mode 100644 index 0000000000..7759251dc5 --- /dev/null +++ b/tests/topotests/bfd-bgp-cbit-topo3/r3/zebra.conf @@ -0,0 +1,7 @@ +interface lo + ip address 10.254.254.3/32 +! +interface r3-eth0 + ipv6 address 2001:db8:4::1/64 +! +ipv6 route 2001:db8:1::/64 2001:db8:4::2 diff --git a/tests/topotests/bfd-bgp-cbit-topo3/test_bfd_bgp_cbit_topo3.dot b/tests/topotests/bfd-bgp-cbit-topo3/test_bfd_bgp_cbit_topo3.dot new file mode 100644 index 0000000000..270de829c3 --- /dev/null +++ b/tests/topotests/bfd-bgp-cbit-topo3/test_bfd_bgp_cbit_topo3.dot @@ -0,0 +1,58 @@ +## Color coding: +######################### +## Main FRR: #f08080 red +## Switches: #d0e0d0 gray +## RIP: #19e3d9 Cyan +## RIPng: #fcb314 dark yellow +## OSPFv2: #32b835 Green +## OSPFv3: #19e3d9 Cyan +## ISIS IPv4 #fcb314 dark yellow +## ISIS IPv6 #9a81ec purple +## BGP IPv4 #eee3d3 beige +## BGP IPv6 #fdff00 yellow +##### Colors (see http://www.color-hex.com/) + +graph template { + label="bfd-topo2"; + + # Routers + r1 [ + shape=doubleoctagon, + label="r1", + fillcolor="#f08080", + style=filled, + ]; + r2 [ + shape=doubleoctagon + label="r2", + fillcolor="#f08080", + style=filled, + ]; + r3 [ + shape=doubleoctagon + label="r4", + fillcolor="#f08080", + style=filled, + ]; + + # Switches + sw1 [ + shape=oval, + label="sw1\n2001:db8:1::/64", + fillcolor="#d0e0d0", + style=filled, + ]; + sw2 [ + shape=oval, + label="sw2\n10.0.3.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + + # Connections + r1 -- sw1 [label="eth0"]; + r2 -- sw1 [label="eth0"]; + + r2 -- sw2 [label="eth1"]; + r3 -- sw2 [label="eth0"]; +} diff --git a/tests/topotests/bfd-bgp-cbit-topo3/test_bfd_bgp_cbit_topo3.py b/tests/topotests/bfd-bgp-cbit-topo3/test_bfd_bgp_cbit_topo3.py new file mode 100755 index 0000000000..186dac31a0 --- /dev/null +++ b/tests/topotests/bfd-bgp-cbit-topo3/test_bfd_bgp_cbit_topo3.py @@ -0,0 +1,255 @@ +#!/usr/bin/env python + +# +# test_bfd_bgp_cbit_topo3.py +# +# Copyright (c) 2019 6WIND +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_bfd_bgp_cbit_topo3.py: Test the FRR/Quagga BFD daemon with multihop and BGP +unnumbered. +""" + +import os +import sys +import json +from functools import partial +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + + +class BFDTopo(Topo): + "Test topology builder" + + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # Create 4 routers. + for routern in range(1, 4): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(BFDTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for rname, router in router_list.iteritems(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)), + ) + router.load_config( + TopoRouter.RD_BFD, os.path.join(CWD, "{}/bfdd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + # Initialize all routers. + tgen.start_router() + + # Verify that we are using the proper version and that the BFD + # daemon exists. + for router in router_list.values(): + # Check for Version + if router.has_version("<", "5.1"): + tgen.set_error("Unsupported FRR version") + break + + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + tgen.stop_topology() + + +def test_protocols_convergence(): + """ + Assert that all protocols have converged before checking for the BFD + statuses as they depend on it. + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Check IPv6 routing tables. + logger.info("Checking IPv6 routes for convergence") + for router in tgen.routers().values(): + if router.name == "r2": + continue + json_file = "{}/{}/ipv6_routes.json".format(CWD, router.name) + if not os.path.isfile(json_file): + logger.info("skipping file {}".format(json_file)) + continue + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, router, "show ipv6 route json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=40, wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + +def test_bfd_connection(): + "Assert that the BFD peers can find themselves." + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("waiting for bfd peers to go up") + for router in tgen.routers().values(): + if router.name == "r2": + continue + json_file = "{}/{}/peers.json".format(CWD, router.name) + expected = json.loads(open(json_file).read()) + + test_func = partial( + topotest.router_json_cmp, router, "show bfd peers json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=32, wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + +def test_bfd_loss_intermediate(): + """ + Assert that BFD notices the bfd link down failure. + but BGP entries should still be present + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("removing IPv6 address from r2 to simulate loss of connectivity") + # Disable r2-eth0 ipv6 address + cmd = 'vtysh -c "configure terminal" -c "interface r2-eth1" -c "no ipv6 address 2001:db8:4::2/64"' + tgen.net["r2"].cmd(cmd) + + # Wait the minimum time we can before checking that BGP/BFD + # converged. + logger.info("waiting for BFD converge down") + + # Check that BGP converged quickly. + for router in tgen.routers().values(): + if router.name == "r2": + continue + json_file = "{}/{}/peers_down.json".format(CWD, router.name) + expected = json.loads(open(json_file).read()) + + test_func = partial( + topotest.router_json_cmp, router, "show bfd peers json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=32, wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + logger.info("waiting for BGP entries to become stale") + for router in tgen.routers().values(): + if router.name == "r2": + continue + json_file = "{}/{}/bgp_ipv6_routes_down.json".format(CWD, router.name) + expected = json.loads(open(json_file).read()) + + test_func = partial( + topotest.router_json_cmp, router, "show bgp ipv6 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=50, wait=1) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + logger.info("Checking IPv6 routes on r1 should still be present") + for router in tgen.routers().values(): + if router.name == "r2": + continue + if router.name == "r3": + continue + json_file = "{}/r1/ipv6_routes.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, router, "show ipv6 route json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + +def test_bfd_comes_back_again(): + """ + Assert that BFD notices the bfd link up + and that ipv6 entries appear back + """ + tgen = get_topogen() + logger.info("re-adding IPv6 address from r2 to simulate connectivity is back") + # adds back r2-eth0 ipv6 address + cmd = 'vtysh -c "configure terminal" -c "interface r2-eth1" -c "ipv6 address 2001:db8:4::2/64"' + tgen.net["r2"].cmd(cmd) + + # Wait the minimum time we can before checking that BGP/BFD + # converged. + logger.info("waiting for BFD to converge up") + + # Check that BGP converged quickly. + for router in tgen.routers().values(): + if router.name == "r2": + continue + json_file = "{}/{}/peers.json".format(CWD, router.name) + expected = json.loads(open(json_file).read()) + + test_func = partial( + topotest.router_json_cmp, router, "show bfd peers json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=16, wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bfd-isis-topo1/__init__.py b/tests/topotests/bfd-isis-topo1/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bfd-isis-topo1/rt1/bfdd.conf b/tests/topotests/bfd-isis-topo1/rt1/bfdd.conf new file mode 100644 index 0000000000..57f9cd9e3d --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt1/bfdd.conf @@ -0,0 +1,19 @@ +! +debug bfd network +debug bfd peer +debug bfd zebra +! +bfd + peer 10.0.1.2 interface eth-rt2 + detect-multiplier 3 + receive-interval 300 + transmit-interval 300 + no shutdown + ! + peer 10.0.2.2 interface eth-rt3 + detect-multiplier 3 + receive-interval 300 + transmit-interval 300 + no shutdown + ! +! diff --git a/tests/topotests/bfd-isis-topo1/rt1/isisd.conf b/tests/topotests/bfd-isis-topo1/rt1/isisd.conf new file mode 100644 index 0000000000..3219371d2e --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt1/isisd.conf @@ -0,0 +1,35 @@ +log file isisd.log +log timestamp precision 3 +! +hostname rt1 +! +password 1 +! +debug isis events +debug isis route-events +debug isis spf-events +debug isis adj-packets +debug isis lsp-sched +! +interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive +! +interface eth-rt2 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 + isis bfd +! +interface eth-rt3 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis bfd +! +router isis 1 + net 49.0000.0000.0000.0001.00 + is-type level-1 +! diff --git a/tests/topotests/bfd-isis-topo1/rt1/step1/show_ip_route.ref b/tests/topotests/bfd-isis-topo1/rt1/step1/show_ip_route.ref new file mode 100644 index 0000000000..af6e45cf33 --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt1/step1/show_ip_route.ref @@ -0,0 +1,74 @@ +{ + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bfd-isis-topo1/rt1/step1/show_ipv6_route.ref b/tests/topotests/bfd-isis-topo1/rt1/step1/show_ipv6_route.ref new file mode 100644 index 0000000000..68d3fe2c44 --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt1/step1/show_ipv6_route.ref @@ -0,0 +1,70 @@ +{ + "::ffff:202:202\/128":[ + { + "prefix":"::ffff:202:202\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "::ffff:303:303\/128":[ + { + "prefix":"::ffff:303:303\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "::ffff:404:404\/128":[ + { + "prefix":"::ffff:404:404\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "::ffff:505:505\/128":[ + { + "prefix":"::ffff:505:505\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bfd-isis-topo1/rt1/step2/show_bfd_peers.ref b/tests/topotests/bfd-isis-topo1/rt1/step2/show_bfd_peers.ref new file mode 100644 index 0000000000..cb4083d2fd --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt1/step2/show_bfd_peers.ref @@ -0,0 +1,16 @@ +[ + { + "peer": "10.0.2.2", + "interface": "eth-rt3", + "status": "up", + "diagnostic": "ok", + "remote-diagnostic": "ok" + }, + { + "peer": "10.0.1.2", + "interface": "eth-rt2", + "status": "up", + "diagnostic": "ok", + "remote-diagnostic": "ok" + } +] diff --git a/tests/topotests/bfd-isis-topo1/rt1/step3/show_bfd_peers_healthy.ref b/tests/topotests/bfd-isis-topo1/rt1/step3/show_bfd_peers_healthy.ref new file mode 100644 index 0000000000..cb4083d2fd --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt1/step3/show_bfd_peers_healthy.ref @@ -0,0 +1,16 @@ +[ + { + "peer": "10.0.2.2", + "interface": "eth-rt3", + "status": "up", + "diagnostic": "ok", + "remote-diagnostic": "ok" + }, + { + "peer": "10.0.1.2", + "interface": "eth-rt2", + "status": "up", + "diagnostic": "ok", + "remote-diagnostic": "ok" + } +] diff --git a/tests/topotests/bfd-isis-topo1/rt1/step3/show_bfd_peers_rt2_down.ref b/tests/topotests/bfd-isis-topo1/rt1/step3/show_bfd_peers_rt2_down.ref new file mode 100644 index 0000000000..f00b9f3d32 --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt1/step3/show_bfd_peers_rt2_down.ref @@ -0,0 +1,9 @@ +[ + { + "peer": "10.0.2.2", + "interface": "eth-rt3", + "status": "up", + "diagnostic": "ok", + "remote-diagnostic": "ok" + } +] diff --git a/tests/topotests/bfd-isis-topo1/rt1/step3/show_bfd_peers_rt3_down.ref b/tests/topotests/bfd-isis-topo1/rt1/step3/show_bfd_peers_rt3_down.ref new file mode 100644 index 0000000000..f5bd276a4a --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt1/step3/show_bfd_peers_rt3_down.ref @@ -0,0 +1,9 @@ +[ + { + "peer": "10.0.1.2", + "interface": "eth-rt2", + "status": "up", + "diagnostic": "ok", + "remote-diagnostic": "ok" + } +] diff --git a/tests/topotests/bfd-isis-topo1/rt1/step3/show_ip_route_healthy.ref b/tests/topotests/bfd-isis-topo1/rt1/step3/show_ip_route_healthy.ref new file mode 100644 index 0000000000..af6e45cf33 --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt1/step3/show_ip_route_healthy.ref @@ -0,0 +1,74 @@ +{ + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bfd-isis-topo1/rt1/step3/show_ip_route_rt2_down.ref b/tests/topotests/bfd-isis-topo1/rt1/step3/show_ip_route_rt2_down.ref new file mode 100644 index 0000000000..b8366bc0b7 --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt1/step3/show_ip_route_rt2_down.ref @@ -0,0 +1,74 @@ +{ + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bfd-isis-topo1/rt1/step3/show_ip_route_rt3_down.ref b/tests/topotests/bfd-isis-topo1/rt1/step3/show_ip_route_rt3_down.ref new file mode 100644 index 0000000000..42bd6abf82 --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt1/step3/show_ip_route_rt3_down.ref @@ -0,0 +1,74 @@ +{ + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bfd-isis-topo1/rt1/step3/show_ipv6_route_healthy.ref b/tests/topotests/bfd-isis-topo1/rt1/step3/show_ipv6_route_healthy.ref new file mode 100644 index 0000000000..68d3fe2c44 --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt1/step3/show_ipv6_route_healthy.ref @@ -0,0 +1,70 @@ +{ + "::ffff:202:202\/128":[ + { + "prefix":"::ffff:202:202\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "::ffff:303:303\/128":[ + { + "prefix":"::ffff:303:303\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "::ffff:404:404\/128":[ + { + "prefix":"::ffff:404:404\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "::ffff:505:505\/128":[ + { + "prefix":"::ffff:505:505\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bfd-isis-topo1/rt1/step3/show_ipv6_route_rt2_down.ref b/tests/topotests/bfd-isis-topo1/rt1/step3/show_ipv6_route_rt2_down.ref new file mode 100644 index 0000000000..200053c3e8 --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt1/step3/show_ipv6_route_rt2_down.ref @@ -0,0 +1,70 @@ +{ + "::ffff:202:202\/128":[ + { + "prefix":"::ffff:202:202\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "::ffff:303:303\/128":[ + { + "prefix":"::ffff:303:303\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "::ffff:404:404\/128":[ + { + "prefix":"::ffff:404:404\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ], + "::ffff:505:505\/128":[ + { + "prefix":"::ffff:505:505\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bfd-isis-topo1/rt1/step3/show_ipv6_route_rt3_down.ref b/tests/topotests/bfd-isis-topo1/rt1/step3/show_ipv6_route_rt3_down.ref new file mode 100644 index 0000000000..4297f163b5 --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt1/step3/show_ipv6_route_rt3_down.ref @@ -0,0 +1,70 @@ +{ + "::ffff:202:202\/128":[ + { + "prefix":"::ffff:202:202\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "::ffff:303:303\/128":[ + { + "prefix":"::ffff:303:303\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "::ffff:404:404\/128":[ + { + "prefix":"::ffff:404:404\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ], + "::ffff:505:505\/128":[ + { + "prefix":"::ffff:505:505\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bfd-isis-topo1/rt1/zebra.conf b/tests/topotests/bfd-isis-topo1/rt1/zebra.conf new file mode 100644 index 0000000000..6003125b6b --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt1/zebra.conf @@ -0,0 +1,25 @@ +log file zebra.log +log timestamp precision 3 +! +hostname rt1 +! +debug zebra kernel +debug zebra packet +debug zebra events +debug zebra rib +! +interface lo + ip address 1.1.1.1/32 + ipv6 address ::ffff:0101:0101/128 +! +interface eth-rt2 + ip address 10.0.1.1/24 +! +interface eth-rt3 + ip address 10.0.2.1/24 +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/bfd-isis-topo1/rt2/bfdd.conf b/tests/topotests/bfd-isis-topo1/rt2/bfdd.conf new file mode 100644 index 0000000000..6b34e337d3 --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt2/bfdd.conf @@ -0,0 +1,13 @@ +! +debug bfd network +debug bfd peer +debug bfd zebra +! +bfd + peer 10.0.1.1 interface eth-rt1 + detect-multiplier 3 + receive-interval 300 + transmit-interval 300 + no shutdown + ! +! diff --git a/tests/topotests/bfd-isis-topo1/rt2/isisd.conf b/tests/topotests/bfd-isis-topo1/rt2/isisd.conf new file mode 100644 index 0000000000..63ccb640a4 --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt2/isisd.conf @@ -0,0 +1,30 @@ +log file isisd.log +! +hostname rt2 +! +password 1 +! +debug isis events +debug isis route-events +debug isis spf-events +! +interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive +! +interface eth-rt1 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 + isis bfd +! +interface eth-rt5 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 +! +router isis 1 + net 49.0000.0000.0000.0002.00 + is-type level-1 +! diff --git a/tests/topotests/bfd-isis-topo1/rt2/step2/show_bfd_peers.ref b/tests/topotests/bfd-isis-topo1/rt2/step2/show_bfd_peers.ref new file mode 100644 index 0000000000..8a90649efa --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt2/step2/show_bfd_peers.ref @@ -0,0 +1,9 @@ +[ + { + "peer": "10.0.1.1", + "interface": "eth-rt1", + "status": "up", + "diagnostic": "ok", + "remote-diagnostic": "ok" + } +] diff --git a/tests/topotests/bfd-isis-topo1/rt2/zebra.conf b/tests/topotests/bfd-isis-topo1/rt2/zebra.conf new file mode 100644 index 0000000000..5fc7fc5b28 --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt2/zebra.conf @@ -0,0 +1,22 @@ +log file zebra.log +! +hostname rt2 +! +debug zebra kernel +debug zebra packet +! +interface lo + ip address 2.2.2.2/32 + ipv6 address ::ffff:0202:0202/128 +! +interface eth-rt1 + ip address 10.0.1.2/24 +! +interface eth-rt5 + ip address 10.0.3.1/24 +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/bfd-isis-topo1/rt3/bfdd.conf b/tests/topotests/bfd-isis-topo1/rt3/bfdd.conf new file mode 100644 index 0000000000..22937fe46f --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt3/bfdd.conf @@ -0,0 +1,13 @@ +! +debug bfd network +debug bfd peer +debug bfd zebra +! +bfd + peer 10.0.2.1 interface eth-rt1 + detect-multiplier 3 + receive-interval 300 + transmit-interval 300 + no shutdown + ! +! diff --git a/tests/topotests/bfd-isis-topo1/rt3/isisd.conf b/tests/topotests/bfd-isis-topo1/rt3/isisd.conf new file mode 100644 index 0000000000..928f1e1a2b --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt3/isisd.conf @@ -0,0 +1,32 @@ +log file isisd.log +! +hostname rt3 +! +password 1 +! +debug isis events +debug isis route-events +debug isis spf-events +! +interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive +! +interface eth-rt1 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis bfd +! +interface eth-rt4 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 +! +router isis 1 + net 49.0000.0000.0000.0003.00 + is-type level-1 +! + diff --git a/tests/topotests/bfd-isis-topo1/rt3/step2/show_bfd_peers.ref b/tests/topotests/bfd-isis-topo1/rt3/step2/show_bfd_peers.ref new file mode 100644 index 0000000000..13eb2a2c69 --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt3/step2/show_bfd_peers.ref @@ -0,0 +1,9 @@ +[ + { + "peer": "10.0.2.1", + "interface": "eth-rt1", + "status": "up", + "diagnostic": "ok", + "remote-diagnostic": "ok" + } +] diff --git a/tests/topotests/bfd-isis-topo1/rt3/zebra.conf b/tests/topotests/bfd-isis-topo1/rt3/zebra.conf new file mode 100644 index 0000000000..d368de9bbe --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt3/zebra.conf @@ -0,0 +1,22 @@ +log file zebra.log +! +hostname rt3 +! +debug zebra kernel +debug zebra packet +! +interface lo + ip address 3.3.3.3/32 + ipv6 address ::ffff:0303:0303/128 +! +interface eth-rt1 + ip address 10.0.2.2/24 +! +interface eth-rt4 + ip address 10.0.4.1/24 +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/bfd-isis-topo1/rt4/bfdd.conf b/tests/topotests/bfd-isis-topo1/rt4/bfdd.conf new file mode 100644 index 0000000000..f35e772790 --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt4/bfdd.conf @@ -0,0 +1,5 @@ +! +debug bfd network +debug bfd peer +debug bfd zebra +! diff --git a/tests/topotests/bfd-isis-topo1/rt4/isisd.conf b/tests/topotests/bfd-isis-topo1/rt4/isisd.conf new file mode 100644 index 0000000000..fde97478a9 --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt4/isisd.conf @@ -0,0 +1,29 @@ +log file isisd.log +! +hostname rt4 +! +password 1 +! +debug isis events +debug isis route-events +debug isis spf-events +! +interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive +! +interface eth-rt3 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 +! +interface eth-rt5 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 +! +router isis 1 + net 49.0000.0000.0000.0004.00 + is-type level-1 +! diff --git a/tests/topotests/bfd-isis-topo1/rt4/zebra.conf b/tests/topotests/bfd-isis-topo1/rt4/zebra.conf new file mode 100644 index 0000000000..7b053bac35 --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt4/zebra.conf @@ -0,0 +1,22 @@ +log file zebra.log +! +hostname rt4 +! +debug zebra kernel +debug zebra packet +! +interface lo + ip address 4.4.4.4/32 + ipv6 address ::ffff:0404:0404/128 +! +interface eth-rt3 + ip address 10.0.4.2/24 +! +interface eth-rt5 + ip address 10.0.5.1/24 +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/bfd-isis-topo1/rt5/bfdd.conf b/tests/topotests/bfd-isis-topo1/rt5/bfdd.conf new file mode 100644 index 0000000000..f35e772790 --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt5/bfdd.conf @@ -0,0 +1,5 @@ +! +debug bfd network +debug bfd peer +debug bfd zebra +! diff --git a/tests/topotests/bfd-isis-topo1/rt5/isisd.conf b/tests/topotests/bfd-isis-topo1/rt5/isisd.conf new file mode 100644 index 0000000000..fd00cb1ddb --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt5/isisd.conf @@ -0,0 +1,29 @@ +log file isisd.log +! +hostname rt5 +! +password 1 +! +debug isis events +debug isis route-events +debug isis spf-events +! +interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive +! +interface eth-rt2 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 +! +interface eth-rt4 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 +! +router isis 1 + net 49.0000.0000.0000.0005.00 + is-type level-1 +! diff --git a/tests/topotests/bfd-isis-topo1/rt5/zebra.conf b/tests/topotests/bfd-isis-topo1/rt5/zebra.conf new file mode 100644 index 0000000000..0b7c9e02f3 --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/rt5/zebra.conf @@ -0,0 +1,22 @@ +log file zebra.log +! +hostname rt5 +! +debug zebra kernel +debug zebra packet +! +interface lo + ip address 5.5.5.5/32 + ipv6 address ::ffff:0505:0505/128 +! +interface eth-rt2 + ip address 10.0.3.2/24 +! +interface eth-rt4 + ip address 10.0.5.2/24 +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/bfd-isis-topo1/test_bfd_isis_topo1.py b/tests/topotests/bfd-isis-topo1/test_bfd_isis_topo1.py new file mode 100755 index 0000000000..a1ed0cc2af --- /dev/null +++ b/tests/topotests/bfd-isis-topo1/test_bfd_isis_topo1.py @@ -0,0 +1,304 @@ +#!/usr/bin/env python + +# +# test_bfd_isis_topo1.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2020 by +# Network Device Education Foundation, Inc. ("NetDEF") +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_bfd_isis_topo1.py: + + +---------+ + | | + eth-rt2 (.1) | RT1 | eth-rt3 (.1) + +----------+ 1.1.1.1 +----------+ + | | | | + | +---------+ | + | | + | 10.0.2.0/24 | + | | + | eth-rt1 | (.2) + | 10.0.1.0/24 +----+----+ + | | | + | | RT3 | + | | 3.3.3.3 | + | | | + (.2) | eth-rt1 +----+----+ + +----+----+ eth-rt4 | (.1) + | | | + | RT2 | | + | 2.2.2.2 | 10.0.4.0/24 | + | | | + +----+----+ | + (.1) | eth-rt5 eth-rt3 | (.2) + | +----+----+ + | | | + | | RT4 | + | | 4.4.4.4 | + | | | + | +----+----+ + | 10.0.3.0/24 eth-rt5 | (.1) + | | + | | + | 10.0.5.0/24 | + | | + | +---------+ | + | | | | + +----------+ RT5 +----------+ + eth-rt2 (.2) | 5.5.5.5 | eth-rt4 (.2) + | | + +---------+ + +""" + +import os +import sys +import pytest +import json +import re +from time import sleep +from time import time +from functools import partial + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + + +class TemplateTopo(Topo): + "Test topology builder" + + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # + # Define FRR Routers + # + for router in ["rt1", "rt2", "rt3", "rt4", "rt5"]: + tgen.add_router(router) + + # + # Define connections + # + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["rt1"], nodeif="eth-rt2") + switch.add_link(tgen.gears["rt2"], nodeif="eth-rt1") + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["rt1"], nodeif="eth-rt3") + switch.add_link(tgen.gears["rt3"], nodeif="eth-rt1") + + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["rt2"], nodeif="eth-rt5") + switch.add_link(tgen.gears["rt5"], nodeif="eth-rt2") + + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["rt3"], nodeif="eth-rt4") + switch.add_link(tgen.gears["rt4"], nodeif="eth-rt3") + + switch = tgen.add_switch("s5") + switch.add_link(tgen.gears["rt4"], nodeif="eth-rt5") + switch.add_link(tgen.gears["rt5"], nodeif="eth-rt4") + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(TemplateTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + # For all registered routers, load the zebra configuration file + for rname, router in router_list.iteritems(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BFD, os.path.join(CWD, "{}/bfdd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_ISIS, os.path.join(CWD, "{}/isisd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + + +def print_cmd_result(rname, command): + print(get_topogen().gears[rname].vtysh_cmd(command, isjson=False)) + + +def router_compare_json_output(rname, command, reference, count=120, wait=0.5): + "Compare router JSON output" + + logger.info('Comparing router "%s" "%s" output', rname, command) + + tgen = get_topogen() + filename = "{}/{}/{}".format(CWD, rname, reference) + expected = json.loads(open(filename).read()) + + # Run test function until we get an result. Wait at most 60 seconds. + test_func = partial(topotest.router_json_cmp, tgen.gears[rname], command, expected) + _, diff = topotest.run_and_expect(test_func, None, count=count, wait=wait) + assertmsg = '"{}" JSON output mismatches the expected result'.format(rname) + assert diff is None, assertmsg + + +## TEST STEPS + + +def test_rib_isis_step1(): + logger.info("Test (step 1): verify RIB (IPv4 and IPv6) for IS-IS") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router_compare_json_output( + "rt1", "show ip route isis json", "step1/show_ip_route.ref" + ) + router_compare_json_output( + "rt1", "show ipv6 route isis json", "step1/show_ipv6_route.ref" + ) + + +def test_bfd_isis_sessions_step2(): + logger.info("Test (step 2): verify BFD peers for IS-IS") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # BFD is just used on three routers + for rt in ["rt1", "rt2", "rt3"]: + router_compare_json_output( + rt, "show bfd peers json", "step2/show_bfd_peers.ref" + ) + + +def test_bfd_isis_interface_failure_rt2_step3(): + logger.info("Test (step 2): check failover handling when RT2 goes down") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Let's kill the interface on rt2 and see what happens with the RIB and BFD on rt1 + tgen.gears["rt2"].link_enable("eth-rt1", enabled=False) + + # By default BFD provides a recovery time of 900ms plus jitter, so let's wait + # initial 2 seconds to let the CI not suffer. + # TODO: add check for array size + sleep(2) + router_compare_json_output( + "rt1", "show ip route isis json", "step3/show_ip_route_rt2_down.ref", 1, 0 + ) + router_compare_json_output( + "rt1", "show ipv6 route isis json", "step3/show_ipv6_route_rt2_down.ref", 1, 0 + ) + router_compare_json_output( + "rt1", "show bfd peers json", "step3/show_bfd_peers_rt2_down.ref", 1, 0 + ) + + # Check recovery, this can take some time + tgen.gears["rt2"].link_enable("eth-rt1", enabled=True) + + router_compare_json_output( + "rt1", "show ip route isis json", "step3/show_ip_route_healthy.ref" + ) + router_compare_json_output( + "rt1", "show ipv6 route isis json", "step3/show_ipv6_route_healthy.ref" + ) + router_compare_json_output( + "rt1", "show bfd peers json", "step3/show_bfd_peers_healthy.ref" + ) + + +def test_bfd_isis_interface_failure_rt3_step3(): + logger.info("Test (step 2): check failover handling when RT2 goes down") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Let's kill the interface on rt3 and see what happens with the RIB and BFD on rt1 + tgen.gears["rt3"].link_enable("eth-rt1", enabled=False) + + # By default BFD provides a recovery time of 900ms plus jitter, so let's wait + # initial 2 seconds to let the CI not suffer. + # TODO: add check for array size + sleep(2) + router_compare_json_output( + "rt1", "show ip route isis json", "step3/show_ip_route_rt3_down.ref", 1, 0 + ) + router_compare_json_output( + "rt1", "show ipv6 route isis json", "step3/show_ipv6_route_rt3_down.ref", 1, 0 + ) + router_compare_json_output( + "rt1", "show bfd peers json", "step3/show_bfd_peers_rt3_down.ref", 1, 0 + ) + + # Check recovery, this can take some time + tgen.gears["rt3"].link_enable("eth-rt1", enabled=True) + + router_compare_json_output( + "rt1", "show ip route isis json", "step3/show_ip_route_healthy.ref" + ) + router_compare_json_output( + "rt1", "show ipv6 route isis json", "step3/show_ipv6_route_healthy.ref" + ) + router_compare_json_output( + "rt1", "show bfd peers json", "step3/show_bfd_peers_healthy.ref" + ) + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bfd-profiles-topo1/__init__.py b/tests/topotests/bfd-profiles-topo1/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bfd-profiles-topo1/r1/bfd-peers-initial.json b/tests/topotests/bfd-profiles-topo1/r1/bfd-peers-initial.json new file mode 100644 index 0000000000..bab24c4fa0 --- /dev/null +++ b/tests/topotests/bfd-profiles-topo1/r1/bfd-peers-initial.json @@ -0,0 +1,38 @@ +[ + { + "detect-multiplier": 3, + "diagnostic": "ok", + "id": "*", + "interface": "r1-eth1", + "multihop": false, + "peer": "172.16.100.2", + "receive-interval": 300, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-id": "*", + "remote-receive-interval": 300, + "remote-transmit-interval": 300, + "status": "up", + "transmit-interval": 300, + "uptime": "*", + "vrf": "default" + }, + { + "detect-multiplier": 3, + "diagnostic": "ok", + "id": "*", + "interface": "r1-eth0", + "multihop": false, + "peer": "172.16.0.1", + "receive-interval": 800, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-id": "*", + "remote-receive-interval": 800, + "remote-transmit-interval": 800, + "status": "up", + "transmit-interval": 800, + "uptime": "*", + "vrf": "default" + } +] diff --git a/tests/topotests/bfd-profiles-topo1/r1/bfdd.conf b/tests/topotests/bfd-profiles-topo1/r1/bfdd.conf new file mode 100644 index 0000000000..4d636ab052 --- /dev/null +++ b/tests/topotests/bfd-profiles-topo1/r1/bfdd.conf @@ -0,0 +1,14 @@ +debug bfd peer +debug bfd network +debug bfd zebra +! +bfd + profile slowtx + receive-interval 800 + transmit-interval 800 + ! + peer 172.16.0.1 interface r1-eth0 + profile slowtx + no shutdown + ! +! diff --git a/tests/topotests/bfd-profiles-topo1/r1/ospfd.conf b/tests/topotests/bfd-profiles-topo1/r1/ospfd.conf new file mode 100644 index 0000000000..4798d17c40 --- /dev/null +++ b/tests/topotests/bfd-profiles-topo1/r1/ospfd.conf @@ -0,0 +1,10 @@ +interface r1-eth1 + ip ospf area 0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 + ip ospf bfd +! +router ospf + ospf router-id 10.254.254.1 + redistribute connected +! diff --git a/tests/topotests/bfd-profiles-topo1/r1/zebra.conf b/tests/topotests/bfd-profiles-topo1/r1/zebra.conf new file mode 100644 index 0000000000..4b7982b235 --- /dev/null +++ b/tests/topotests/bfd-profiles-topo1/r1/zebra.conf @@ -0,0 +1,9 @@ +interface lo + ip address 10.254.254.1/32 +! +interface r1-eth0 + ip address 172.16.0.2/24 +! +interface r1-eth1 + ip address 172.16.100.1/24 +! diff --git a/tests/topotests/bfd-profiles-topo1/r2/bfd-peers-initial.json b/tests/topotests/bfd-profiles-topo1/r2/bfd-peers-initial.json new file mode 100644 index 0000000000..3df9ec9c9d --- /dev/null +++ b/tests/topotests/bfd-profiles-topo1/r2/bfd-peers-initial.json @@ -0,0 +1,39 @@ +[ + { + "detect-multiplier": 3, + "diagnostic": "ok", + "id": "*", + "interface": "r2-eth0", + "multihop": false, + "peer": "172.16.0.2", + "receive-interval": 800, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-id": "*", + "remote-receive-interval": 800, + "remote-transmit-interval": 800, + "status": "up", + "transmit-interval": 800, + "uptime": "*", + "vrf": "default" + }, + { + "detect-multiplier": 3, + "diagnostic": "ok", + "id": "*", + "interface": "r2-eth1", + "multihop": false, + "peer": "172.16.1.1", + "receive-interval": 250, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-echo-interval": 50, + "remote-id": "*", + "remote-receive-interval": 300, + "remote-transmit-interval": 300, + "status": "up", + "transmit-interval": 250, + "uptime": "*", + "vrf": "default" + } +] diff --git a/tests/topotests/bfd-profiles-topo1/r2/bfdd.conf b/tests/topotests/bfd-profiles-topo1/r2/bfdd.conf new file mode 100644 index 0000000000..23a39a6ee0 --- /dev/null +++ b/tests/topotests/bfd-profiles-topo1/r2/bfdd.conf @@ -0,0 +1,18 @@ +debug bfd peer +debug bfd network +debug bfd zebra +! +bfd + profile slowtx + receive-interval 800 + transmit-interval 800 + ! + profile fasttx + receive-interval 250 + transmit-interval 250 + ! + peer 172.16.0.2 interface r2-eth0 + profile slowtx + no shutdown + ! +! diff --git a/tests/topotests/bfd-profiles-topo1/r2/bgpd.conf b/tests/topotests/bfd-profiles-topo1/r2/bgpd.conf new file mode 100644 index 0000000000..824d01983f --- /dev/null +++ b/tests/topotests/bfd-profiles-topo1/r2/bgpd.conf @@ -0,0 +1,19 @@ +debug bgp neighbor-events +! +router bgp 100 + bgp router-id 10.254.254.2 + no bgp ebgp-requires-policy + neighbor 172.16.1.1 remote-as 100 + neighbor 172.16.1.1 bfd profile fasttx + neighbor 2001:db8:2::2 remote-as 200 + neighbor 2001:db8:2::2 ebgp-multihop 2 + neighbor 2001:db8:2::2 bfd profile slowtx + address-family ipv4 unicast + redistribute connected + exit-address-family + address-family ipv6 unicast + redistribute connected + neighbor 172.16.1.1 activate + neighbor 2001:db8:2::2 activate + exit-address-family +! diff --git a/tests/topotests/bfd-profiles-topo1/r2/zebra.conf b/tests/topotests/bfd-profiles-topo1/r2/zebra.conf new file mode 100644 index 0000000000..6acef139b9 --- /dev/null +++ b/tests/topotests/bfd-profiles-topo1/r2/zebra.conf @@ -0,0 +1,10 @@ +interface lo + ip address 10.254.254.2/32 +! +interface r2-eth0 + ip address 172.16.0.1/24 +! +interface r2-eth1 + ip address 172.16.1.2/24 + ipv6 address 2001:db8:1::2/64 +! diff --git a/tests/topotests/bfd-profiles-topo1/r3/bfd-peers-initial.json b/tests/topotests/bfd-profiles-topo1/r3/bfd-peers-initial.json new file mode 100644 index 0000000000..d2d0c601c3 --- /dev/null +++ b/tests/topotests/bfd-profiles-topo1/r3/bfd-peers-initial.json @@ -0,0 +1,39 @@ +[ + { + "detect-multiplier": 3, + "diagnostic": "ok", + "id": "*", + "interface": "r3-eth0", + "multihop": false, + "peer": "172.16.1.2", + "receive-interval": 300, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-id": "*", + "remote-receive-interval": 250, + "remote-transmit-interval": 250, + "status": "up", + "transmit-interval": 300, + "uptime": "*", + "vrf": "default" + }, + { + "detect-multiplier": 3, + "diagnostic": "ok", + "id": "*", + "interface": "r3-eth1", + "local": "*", + "multihop": false, + "peer": "*", + "receive-interval": 250, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-id": "*", + "remote-receive-interval": 300, + "remote-transmit-interval": 300, + "status": "up", + "transmit-interval": 250, + "uptime": "*", + "vrf": "default" + } +] diff --git a/tests/topotests/bfd-profiles-topo1/r3/bfdd.conf b/tests/topotests/bfd-profiles-topo1/r3/bfdd.conf new file mode 100644 index 0000000000..08eb0468d6 --- /dev/null +++ b/tests/topotests/bfd-profiles-topo1/r3/bfdd.conf @@ -0,0 +1,10 @@ +debug bfd peer +debug bfd network +debug bfd zebra +! +bfd + profile fasttx + receive-interval 250 + transmit-interval 250 + ! +! diff --git a/tests/topotests/bfd-profiles-topo1/r3/bgpd.conf b/tests/topotests/bfd-profiles-topo1/r3/bgpd.conf new file mode 100644 index 0000000000..c7b75d2fde --- /dev/null +++ b/tests/topotests/bfd-profiles-topo1/r3/bgpd.conf @@ -0,0 +1,13 @@ +router bgp 100 + bgp router-id 10.254.254.3 + neighbor 172.16.1.2 remote-as 100 + neighbor 172.16.1.2 bfd profile DOES_NOT_EXIST + address-family ipv4 unicast + redistribute connected + exit-address-family + address-family ipv6 unicast + redistribute connected + neighbor 172.16.1.2 activate + exit-address-family + ! +! diff --git a/tests/topotests/bfd-profiles-topo1/r3/isisd.conf b/tests/topotests/bfd-profiles-topo1/r3/isisd.conf new file mode 100644 index 0000000000..d27a783adf --- /dev/null +++ b/tests/topotests/bfd-profiles-topo1/r3/isisd.conf @@ -0,0 +1,16 @@ +hostname r3 +! +debug isis adj-packets +debug isis events +debug isis update-packets +! +interface r3-eth1 + ipv6 router isis lan + isis circuit-type level-1 + isis bfd + isis bfd profile fasttx +! +router isis lan + net 10.0000.0000.0000.0000.0000.0000.0000.0000.0001.00 + redistribute ipv6 connected level-1 +! diff --git a/tests/topotests/bfd-profiles-topo1/r3/zebra.conf b/tests/topotests/bfd-profiles-topo1/r3/zebra.conf new file mode 100644 index 0000000000..2297bfafe9 --- /dev/null +++ b/tests/topotests/bfd-profiles-topo1/r3/zebra.conf @@ -0,0 +1,12 @@ +ipv6 forwarding +! +interface lo + ip address 10.254.254.3/32 +! +interface r3-eth0 + ip address 172.16.1.1/24 + ipv6 address 2001:db8:1::1/64 +! +interface r3-eth1 + ipv6 address 2001:db8:2::1/64 +! diff --git a/tests/topotests/bfd-profiles-topo1/r4/bfd-peers-initial.json b/tests/topotests/bfd-profiles-topo1/r4/bfd-peers-initial.json new file mode 100644 index 0000000000..2c2e136abf --- /dev/null +++ b/tests/topotests/bfd-profiles-topo1/r4/bfd-peers-initial.json @@ -0,0 +1,41 @@ +[ + { + "detect-multiplier": 3, + "diagnostic": "ok", + "id": "*", + "interface": "r4-eth0", + "local": "*", + "multihop": false, + "peer": "*", + "receive-interval": 300, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-id": "*", + "remote-receive-interval": 250, + "remote-transmit-interval": 250, + "status": "up", + "transmit-interval": 300, + "uptime": "*", + "vrf": "default" + }, + { + "detect-multiplier": 3, + "diagnostic": "ok", + "id": "*", + "interface": "r4-eth1", + "local": "*", + "multihop": false, + "peer": "*", + "receive-interval": 300, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-echo-interval": 50, + "remote-id": "*", + "remote-receive-interval": 300, + "remote-transmit-interval": 300, + "status": "up", + "transmit-interval": 300, + "uptime": "*", + "vrf": "default" + } +] diff --git a/tests/topotests/bfd-profiles-topo1/r4/bfdd.conf b/tests/topotests/bfd-profiles-topo1/r4/bfdd.conf new file mode 100644 index 0000000000..36ef4f0403 --- /dev/null +++ b/tests/topotests/bfd-profiles-topo1/r4/bfdd.conf @@ -0,0 +1,6 @@ +debug bfd peer +debug bfd network +debug bfd zebra +! +bfd +! diff --git a/tests/topotests/bfd-profiles-topo1/r4/bgpd.conf b/tests/topotests/bfd-profiles-topo1/r4/bgpd.conf new file mode 100644 index 0000000000..aff1016dee --- /dev/null +++ b/tests/topotests/bfd-profiles-topo1/r4/bgpd.conf @@ -0,0 +1,17 @@ +debug bgp neighbor-events +! +router bgp 200 + bgp router-id 10.254.254.4 + no bgp ebgp-requires-policy + neighbor 2001:db8:1::2 remote-as 100 + neighbor 2001:db8:1::2 ebgp-multihop 2 + neighbor 2001:db8:1::2 bfd profile DOES_NOT_EXIST + address-family ipv4 unicast + redistribute connected + exit-address-family + address-family ipv6 unicast + redistribute connected + neighbor 2001:db8:1::2 activate + exit-address-family + ! +! diff --git a/tests/topotests/bfd-profiles-topo1/r4/isisd.conf b/tests/topotests/bfd-profiles-topo1/r4/isisd.conf new file mode 100644 index 0000000000..01e197bed5 --- /dev/null +++ b/tests/topotests/bfd-profiles-topo1/r4/isisd.conf @@ -0,0 +1,16 @@ +hostname r4 +! +debug isis adj-packets +debug isis events +debug isis update-packets +! +interface r4-eth0 + ipv6 router isis lan + isis circuit-type level-1 + isis bfd + isis bfd profile DOES_NOT_EXIST +! +router isis lan + net 10.0000.0000.0000.0000.0000.0000.0000.0000.0002.00 + redistribute ipv6 connected level-1 +! diff --git a/tests/topotests/bfd-profiles-topo1/r4/ospf6d.conf b/tests/topotests/bfd-profiles-topo1/r4/ospf6d.conf new file mode 100644 index 0000000000..84157de24d --- /dev/null +++ b/tests/topotests/bfd-profiles-topo1/r4/ospf6d.conf @@ -0,0 +1,8 @@ +interface r4-eth1 + ipv6 ospf6 bfd +! +router ospf6 + ospf6 router-id 10.254.254.4 + redistribute connected + interface r4-eth1 area 0.0.0.0 +! diff --git a/tests/topotests/bfd-profiles-topo1/r4/zebra.conf b/tests/topotests/bfd-profiles-topo1/r4/zebra.conf new file mode 100644 index 0000000000..753041f952 --- /dev/null +++ b/tests/topotests/bfd-profiles-topo1/r4/zebra.conf @@ -0,0 +1,12 @@ +ipv6 forwarding +! +interface lo + ip address 10.254.254.4/32 +! +interface r4-eth0 + ipv6 address 2001:db8:2::2/64 +! +interface r4-eth1 + ip address 172.16.3.1/24 + ipv6 address 2001:db8:3::1/64 +! diff --git a/tests/topotests/bfd-profiles-topo1/r5/bfd-peers-initial.json b/tests/topotests/bfd-profiles-topo1/r5/bfd-peers-initial.json new file mode 100644 index 0000000000..fcb090959e --- /dev/null +++ b/tests/topotests/bfd-profiles-topo1/r5/bfd-peers-initial.json @@ -0,0 +1,21 @@ +[ + { + "detect-multiplier": 3, + "diagnostic": "ok", + "id": "*", + "interface": "r5-eth0", + "local": "*", + "multihop": false, + "peer": "*", + "receive-interval": 300, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-id": "*", + "remote-receive-interval": 300, + "remote-transmit-interval": 300, + "status": "up", + "transmit-interval": 300, + "uptime": "*", + "vrf": "default" + } +] diff --git a/tests/topotests/bfd-profiles-topo1/r5/bfdd.conf b/tests/topotests/bfd-profiles-topo1/r5/bfdd.conf new file mode 100644 index 0000000000..74dae5a60d --- /dev/null +++ b/tests/topotests/bfd-profiles-topo1/r5/bfdd.conf @@ -0,0 +1,11 @@ +debug bfd peer +debug bfd network +debug bfd zebra +! +bfd + ! profile is commented out on purpose. + !profile fasttx + ! receive-interval 250 + ! transmit-interval 250 + !! +! diff --git a/tests/topotests/bfd-profiles-topo1/r5/ospf6d.conf b/tests/topotests/bfd-profiles-topo1/r5/ospf6d.conf new file mode 100644 index 0000000000..970c713558 --- /dev/null +++ b/tests/topotests/bfd-profiles-topo1/r5/ospf6d.conf @@ -0,0 +1,8 @@ +interface r5-eth0 + ipv6 ospf6 bfd +! +router ospf6 + ospf6 router-id 10.254.254.5 + redistribute connected + interface r5-eth0 area 0.0.0.0 +! diff --git a/tests/topotests/bfd-profiles-topo1/r5/zebra.conf b/tests/topotests/bfd-profiles-topo1/r5/zebra.conf new file mode 100644 index 0000000000..de8ae1644b --- /dev/null +++ b/tests/topotests/bfd-profiles-topo1/r5/zebra.conf @@ -0,0 +1,6 @@ +interface lo + ip address 10.254.254.5/32 +! +interface r5-eth0 + ipv6 address 2001:db8:3::2/64 +! diff --git a/tests/topotests/bfd-profiles-topo1/r6/bfd-peers-initial.json b/tests/topotests/bfd-profiles-topo1/r6/bfd-peers-initial.json new file mode 100644 index 0000000000..4e6fa869ba --- /dev/null +++ b/tests/topotests/bfd-profiles-topo1/r6/bfd-peers-initial.json @@ -0,0 +1,20 @@ +[ + { + "detect-multiplier": 3, + "diagnostic": "ok", + "id": "*", + "interface": "r6-eth0", + "multihop": false, + "peer": "172.16.100.1", + "receive-interval": 300, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-id": "*", + "remote-receive-interval": 300, + "remote-transmit-interval": 300, + "status": "up", + "transmit-interval": 300, + "uptime": "*", + "vrf": "default" + } +] diff --git a/tests/topotests/bfd-profiles-topo1/r6/bfdd.conf b/tests/topotests/bfd-profiles-topo1/r6/bfdd.conf new file mode 100644 index 0000000000..74dae5a60d --- /dev/null +++ b/tests/topotests/bfd-profiles-topo1/r6/bfdd.conf @@ -0,0 +1,11 @@ +debug bfd peer +debug bfd network +debug bfd zebra +! +bfd + ! profile is commented out on purpose. + !profile fasttx + ! receive-interval 250 + ! transmit-interval 250 + !! +! diff --git a/tests/topotests/bfd-profiles-topo1/r6/ospfd.conf b/tests/topotests/bfd-profiles-topo1/r6/ospfd.conf new file mode 100644 index 0000000000..d8fce344a8 --- /dev/null +++ b/tests/topotests/bfd-profiles-topo1/r6/ospfd.conf @@ -0,0 +1,10 @@ +interface r6-eth0 + ip ospf area 0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 + ip ospf bfd +! +router ospf + ospf router-id 10.254.254.6 + redistribute connected +! diff --git a/tests/topotests/bfd-profiles-topo1/r6/zebra.conf b/tests/topotests/bfd-profiles-topo1/r6/zebra.conf new file mode 100644 index 0000000000..c0804b94a7 --- /dev/null +++ b/tests/topotests/bfd-profiles-topo1/r6/zebra.conf @@ -0,0 +1,6 @@ +interface lo + ip address 10.254.254.6/32 +! +interface r6-eth0 + ip address 172.16.100.2/24 +! diff --git a/tests/topotests/bfd-profiles-topo1/test_bfd_profiles_topo1.dot b/tests/topotests/bfd-profiles-topo1/test_bfd_profiles_topo1.dot new file mode 100644 index 0000000000..a3936093aa --- /dev/null +++ b/tests/topotests/bfd-profiles-topo1/test_bfd_profiles_topo1.dot @@ -0,0 +1,97 @@ +## Color coding: +######################### +## Main FRR: #f08080 red +## Switches: #d0e0d0 gray +## RIP: #19e3d9 Cyan +## RIPng: #fcb314 dark yellow +## OSPFv2: #32b835 Green +## OSPFv3: #19e3d9 Cyan +## ISIS IPv4 #fcb314 dark yellow +## ISIS IPv6 #9a81ec purple +## BGP IPv4 #eee3d3 beige +## BGP IPv6 #fdff00 yellow +##### Colors (see http://www.color-hex.com/) + +graph template { + label="bfd-profiles-topo1"; + + # Routers + r1 [ + shape=doubleoctagon, + label="r1", + fillcolor="#f08080", + style=filled, + ]; + r2 [ + shape=doubleoctagon + label="r2", + fillcolor="#f08080", + style=filled, + ]; + r3 [ + shape=doubleoctagon + label="r3", + fillcolor="#f08080", + style=filled, + ]; + r4 [ + shape=doubleoctagon + label="r4", + fillcolor="#f08080", + style=filled, + ]; + r5 [ + shape=doubleoctagon + label="r5", + fillcolor="#f08080", + style=filled, + ]; + + # Switches + sw1 [ + shape=oval, + label="sw1\n172.16.0.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + sw2 [ + shape=oval, + label="sw2\n172.16.1.0/24\n2001:db8:1::/64", + fillcolor="#d0e0d0", + style=filled, + ]; + sw3 [ + shape=oval, + label="sw3\n2001:db8:2::/64", + fillcolor="#d0e0d0", + style=filled, + ]; + sw4 [ + shape=oval, + label="sw4\n2001:db8:3::/64", + fillcolor="#d0e0d0", + style=filled, + ]; + sw5 [ + shape=oval, + label="sw5\n172.16.100.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + + # Connections + r1 -- sw1 [label="eth0"]; + r2 -- sw1 [label="eth0"]; + + r2 -- sw2 [label="eth1"]; + r3 -- sw2 [label="eth0"]; + + r3 -- sw3 [label="eth1"]; + r4 -- sw3 [label="eth0"]; + + r4 -- sw4 [label="eth1"]; + r5 -- sw4 [label="eth0"]; + + r1 -- sw5 [label="eth1"]; + r6 -- sw5 [label="eth0"]; +} diff --git a/tests/topotests/bfd-profiles-topo1/test_bfd_profiles_topo1.png b/tests/topotests/bfd-profiles-topo1/test_bfd_profiles_topo1.png new file mode 100644 index 0000000000..775fae13f1 Binary files /dev/null and b/tests/topotests/bfd-profiles-topo1/test_bfd_profiles_topo1.png differ diff --git a/tests/topotests/bfd-profiles-topo1/test_bfd_profiles_topo1.py b/tests/topotests/bfd-profiles-topo1/test_bfd_profiles_topo1.py new file mode 100755 index 0000000000..02385b32e5 --- /dev/null +++ b/tests/topotests/bfd-profiles-topo1/test_bfd_profiles_topo1.py @@ -0,0 +1,198 @@ +#!/usr/bin/env python + +# +# test_bfd_profiles_topo1.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2020 by +# Network Device Education Foundation, Inc. ("NetDEF") +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_bfd_profiles_topo1.py: Test the FRR BFD profile protocol integration. +""" + +import os +import sys +import json +from functools import partial +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + + +class BFDProfTopo(Topo): + "Test topology builder" + + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # Create 6 routers + for routern in range(1, 7): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["r4"]) + + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["r4"]) + switch.add_link(tgen.gears["r5"]) + + switch = tgen.add_switch("s5") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r6"]) + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(BFDProfTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + for rname, router in router_list.iteritems(): + daemon_file = "{}/{}/bfdd.conf".format(CWD, rname) + if os.path.isfile(daemon_file): + router.load_config(TopoRouter.RD_BFD, daemon_file) + + daemon_file = "{}/{}/bgpd.conf".format(CWD, rname) + if os.path.isfile(daemon_file): + router.load_config(TopoRouter.RD_BGP, daemon_file) + + daemon_file = "{}/{}/isisd.conf".format(CWD, rname) + if os.path.isfile(daemon_file): + router.load_config(TopoRouter.RD_ISIS, daemon_file) + + daemon_file = "{}/{}/ospfd.conf".format(CWD, rname) + if os.path.isfile(daemon_file): + router.load_config(TopoRouter.RD_OSPF, daemon_file) + + daemon_file = "{}/{}/ospf6d.conf".format(CWD, rname) + if os.path.isfile(daemon_file): + router.load_config(TopoRouter.RD_OSPF6, daemon_file) + + daemon_file = "{}/{}/zebra.conf".format(CWD, rname) + if os.path.isfile(daemon_file): + router.load_config(TopoRouter.RD_ZEBRA, daemon_file) + + # Initialize all routers. + tgen.start_router() + + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + tgen.stop_topology() + +def test_wait_protocols_convergence(): + "Wait for all protocols to converge" + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("waiting for protocols to converge") + + def expect_loopback_route(router, iptype, route, proto): + "Wait until route is present on RIB for protocol." + logger.info('waiting route {} in {}'.format(route, router)) + test_func = partial( + topotest.router_json_cmp, + tgen.gears[router], + 'show {} route json'.format(iptype), + { route: [{ 'protocol': proto }] } + ) + _, result = topotest.run_and_expect(test_func, None, count=130, wait=1) + assertmsg = '"{}" OSPF convergence failure'.format(router) + assert result is None, assertmsg + + + # Wait for R1 <-> R6 convergence. + expect_loopback_route('r1', 'ip', '10.254.254.6/32', 'ospf') + + # Wait for R6 <-> R1 convergence. + expect_loopback_route('r6', 'ip', '10.254.254.1/32', 'ospf') + + # Wait for R2 <-> R3 convergence. + expect_loopback_route('r2', 'ip', '10.254.254.3/32', 'bgp') + + # Wait for R3 <-> R2 convergence. + expect_loopback_route('r3', 'ip', '10.254.254.2/32', 'bgp') + + # Wait for R3 <-> R4 convergence. + expect_loopback_route('r3', 'ipv6', '2001:db8:3::/64', 'isis') + + # Wait for R4 <-> R3 convergence. + expect_loopback_route('r4', 'ipv6', '2001:db8:1::/64', 'isis') + + # Wait for R4 <-> R5 convergence. + expect_loopback_route('r4', 'ipv6', '2001:db8:3::/64', 'ospf6') + + # Wait for R5 <-> R4 convergence. + expect_loopback_route('r5', 'ipv6', '2001:db8:2::/64', 'ospf6') + + +def test_bfd_profile_values(): + "Assert that the BFD peers can find themselves." + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("waiting for bfd peers to go up and checking profile values") + + for router in tgen.routers().values(): + json_file = "{}/{}/bfd-peers-initial.json".format(CWD, router.name) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, router, "show bfd peers json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=12, wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bfd-topo1/r1/bfdd.conf b/tests/topotests/bfd-topo1/r1/bfdd.conf index 131b01f0d9..4102000337 100644 --- a/tests/topotests/bfd-topo1/r1/bfdd.conf +++ b/tests/topotests/bfd-topo1/r1/bfdd.conf @@ -1,3 +1,8 @@ +! +debug bfd network +debug bfd peer +debug bfd zebra +! bfd peer 192.168.0.2 echo-mode diff --git a/tests/topotests/bfd-topo1/r1/bgp_prefixes.json b/tests/topotests/bfd-topo1/r1/bgp_prefixes.json index 4b2cc1ad62..1262f5e984 100644 --- a/tests/topotests/bfd-topo1/r1/bgp_prefixes.json +++ b/tests/topotests/bfd-topo1/r1/bgp_prefixes.json @@ -2,7 +2,7 @@ "routes": { "10.254.254.2/32": [ { - "aspath": "102", + "path": "102", "prefix": "10.254.254.2", "valid": true, "peerId": "192.168.0.2", @@ -18,7 +18,7 @@ ], "10.254.254.3/32": [ { - "aspath": "102 103", + "path": "102 103", "prefix": "10.254.254.3", "valid": true, "peerId": "192.168.0.2", @@ -34,7 +34,7 @@ ], "10.254.254.4/32": [ { - "aspath": "102 104", + "path": "102 104", "prefix": "10.254.254.4", "valid": true, "peerId": "192.168.0.2", diff --git a/tests/topotests/bfd-topo1/r1/bgpd.conf b/tests/topotests/bfd-topo1/r1/bgpd.conf index 78a5611844..25e16a6e0c 100644 --- a/tests/topotests/bfd-topo1/r1/bgpd.conf +++ b/tests/topotests/bfd-topo1/r1/bgpd.conf @@ -1,4 +1,6 @@ router bgp 101 + no bgp ebgp-requires-policy + no bgp network import-check neighbor 192.168.0.2 remote-as 102 neighbor 192.168.0.2 bfd address-family ipv4 unicast diff --git a/tests/topotests/bfd-topo1/r2/bfdd.conf b/tests/topotests/bfd-topo1/r2/bfdd.conf index cb4357172b..412450ca1e 100644 --- a/tests/topotests/bfd-topo1/r2/bfdd.conf +++ b/tests/topotests/bfd-topo1/r2/bfdd.conf @@ -1,3 +1,8 @@ +! +debug bfd network +debug bfd peer +debug bfd zebra +! bfd peer 192.168.0.1 receive-interval 1000 diff --git a/tests/topotests/bfd-topo1/r2/bgp_prefixes.json b/tests/topotests/bfd-topo1/r2/bgp_prefixes.json index 39f3c0a835..0d47c0fc30 100644 --- a/tests/topotests/bfd-topo1/r2/bgp_prefixes.json +++ b/tests/topotests/bfd-topo1/r2/bgp_prefixes.json @@ -2,7 +2,7 @@ "routes": { "10.254.254.1/32": [ { - "aspath": "101", + "path": "101", "prefix": "10.254.254.1", "valid": true, "peerId": "192.168.0.1", @@ -18,7 +18,7 @@ ], "10.254.254.3/32": [ { - "aspath": "103", + "path": "103", "prefix": "10.254.254.3", "valid": true, "peerId": "192.168.1.1", @@ -34,7 +34,7 @@ ], "10.254.254.4/32": [ { - "aspath": "104", + "path": "104", "prefix": "10.254.254.4", "valid": true, "peerId": "192.168.2.1", diff --git a/tests/topotests/bfd-topo1/r2/bgpd.conf b/tests/topotests/bfd-topo1/r2/bgpd.conf index af10cfaf40..693fb93411 100644 --- a/tests/topotests/bfd-topo1/r2/bgpd.conf +++ b/tests/topotests/bfd-topo1/r2/bgpd.conf @@ -1,4 +1,6 @@ router bgp 102 + no bgp ebgp-requires-policy + no bgp network import-check neighbor 192.168.0.1 remote-as 101 neighbor 192.168.0.1 bfd neighbor 192.168.1.1 remote-as 103 diff --git a/tests/topotests/bfd-topo1/r3/bfdd.conf b/tests/topotests/bfd-topo1/r3/bfdd.conf index a5d38c8162..4cf20bb904 100644 --- a/tests/topotests/bfd-topo1/r3/bfdd.conf +++ b/tests/topotests/bfd-topo1/r3/bfdd.conf @@ -1,3 +1,8 @@ +! +debug bfd network +debug bfd peer +debug bfd zebra +! bfd peer 192.168.1.2 echo-interval 100 diff --git a/tests/topotests/bfd-topo1/r3/bgp_prefixes.json b/tests/topotests/bfd-topo1/r3/bgp_prefixes.json index c92d4e052a..36fca17bbf 100644 --- a/tests/topotests/bfd-topo1/r3/bgp_prefixes.json +++ b/tests/topotests/bfd-topo1/r3/bgp_prefixes.json @@ -2,7 +2,7 @@ "routes": { "10.254.254.1/32": [ { - "aspath": "102 101", + "path": "102 101", "prefix": "10.254.254.1", "valid": true, "peerId": "192.168.1.2", @@ -18,7 +18,7 @@ ], "10.254.254.2/32": [ { - "aspath": "102", + "path": "102", "prefix": "10.254.254.2", "valid": true, "peerId": "192.168.1.2", @@ -34,7 +34,7 @@ ], "10.254.254.4/32": [ { - "aspath": "102 104", + "path": "102 104", "prefix": "10.254.254.4", "valid": true, "peerId": "192.168.1.2", diff --git a/tests/topotests/bfd-topo1/r3/bgpd.conf b/tests/topotests/bfd-topo1/r3/bgpd.conf index 041fd7a759..7584f98803 100644 --- a/tests/topotests/bfd-topo1/r3/bgpd.conf +++ b/tests/topotests/bfd-topo1/r3/bgpd.conf @@ -1,4 +1,6 @@ router bgp 103 + no bgp ebgp-requires-policy + no bgp network import-check neighbor 192.168.1.2 remote-as 102 neighbor 192.168.1.2 bfd address-family ipv4 unicast diff --git a/tests/topotests/bfd-topo1/r4/bfdd.conf b/tests/topotests/bfd-topo1/r4/bfdd.conf index 029dfba355..34b88c9a35 100644 --- a/tests/topotests/bfd-topo1/r4/bfdd.conf +++ b/tests/topotests/bfd-topo1/r4/bfdd.conf @@ -1,3 +1,8 @@ +! +debug bfd network +debug bfd peer +debug bfd zebra +! bfd peer 192.168.2.2 transmit-interval 2000 diff --git a/tests/topotests/bfd-topo1/r4/bgp_prefixes.json b/tests/topotests/bfd-topo1/r4/bgp_prefixes.json index cc8510dd61..efe7d47b1a 100644 --- a/tests/topotests/bfd-topo1/r4/bgp_prefixes.json +++ b/tests/topotests/bfd-topo1/r4/bgp_prefixes.json @@ -2,7 +2,7 @@ "routes": { "10.254.254.1/32": [ { - "aspath": "102 101", + "path": "102 101", "prefix": "10.254.254.1", "valid": true, "peerId": "192.168.2.2", @@ -18,7 +18,7 @@ ], "10.254.254.2/32": [ { - "aspath": "102", + "path": "102", "prefix": "10.254.254.2", "valid": true, "peerId": "192.168.2.2", @@ -34,7 +34,7 @@ ], "10.254.254.3/32": [ { - "aspath": "102 103", + "path": "102 103", "prefix": "10.254.254.3", "valid": true, "peerId": "192.168.2.2", diff --git a/tests/topotests/bfd-topo1/r4/bgpd.conf b/tests/topotests/bfd-topo1/r4/bgpd.conf index 9c504699ba..3c68e7eec9 100644 --- a/tests/topotests/bfd-topo1/r4/bgpd.conf +++ b/tests/topotests/bfd-topo1/r4/bgpd.conf @@ -1,4 +1,6 @@ router bgp 104 + no bgp ebgp-requires-policy + no bgp network import-check neighbor 192.168.2.2 remote-as 102 neighbor 192.168.2.2 bfd address-family ipv4 unicast diff --git a/tests/topotests/bfd-topo1/test_bfd_topo1.py b/tests/topotests/bfd-topo1/test_bfd_topo1.py index 4fd4f97436..e1865dc5a8 100644 --- a/tests/topotests/bfd-topo1/test_bfd_topo1.py +++ b/tests/topotests/bfd-topo1/test_bfd_topo1.py @@ -34,7 +34,7 @@ # Save the Current Working Directory to find configuration files. CWD = os.path.dirname(os.path.realpath(__file__)) -sys.path.append(os.path.join(CWD, '../')) +sys.path.append(os.path.join(CWD, "../")) # pylint: disable=C0413 # Import topogen and topotest helpers @@ -45,27 +45,29 @@ # Required to instantiate the topology builder class. from mininet.topo import Topo + class BFDTopo(Topo): "Test topology builder" + def build(self, *_args, **_opts): "Build function" tgen = get_topogen(self) # Create 4 routers for routern in range(1, 5): - tgen.add_router('r{}'.format(routern)) + tgen.add_router("r{}".format(routern)) - switch = tgen.add_switch('s1') - switch.add_link(tgen.gears['r1']) - switch.add_link(tgen.gears['r2']) + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) - switch = tgen.add_switch('s2') - switch.add_link(tgen.gears['r2']) - switch.add_link(tgen.gears['r3']) + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) - switch = tgen.add_switch('s3') - switch.add_link(tgen.gears['r2']) - switch.add_link(tgen.gears['r4']) + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r4"]) def setup_module(mod): @@ -76,16 +78,13 @@ def setup_module(mod): router_list = tgen.routers() for rname, router in router_list.iteritems(): router.load_config( - TopoRouter.RD_ZEBRA, - os.path.join(CWD, '{}/zebra.conf'.format(rname)) + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) ) router.load_config( - TopoRouter.RD_BFD, - os.path.join(CWD, '{}/bfdd.conf'.format(rname)) + TopoRouter.RD_BFD, os.path.join(CWD, "{}/bfdd.conf".format(rname)) ) router.load_config( - TopoRouter.RD_BGP, - os.path.join(CWD, '{}/bgpd.conf'.format(rname)) + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) ) # Initialize all routers. @@ -95,8 +94,8 @@ def setup_module(mod): # daemon exists. for router in router_list.values(): # Check for Version - if router.has_version('<', '5.1'): - tgen.set_error('Unsupported FRR version') + if router.has_version("<", "5.1"): + tgen.set_error("Unsupported FRR version") break @@ -112,14 +111,15 @@ def test_bfd_connection(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - logger.info('waiting for bfd peers to go up') + logger.info("waiting for bfd peers to go up") for router in tgen.routers().values(): - json_file = '{}/{}/peers.json'.format(CWD, router.name) + json_file = "{}/{}/peers.json".format(CWD, router.name) expected = json.loads(open(json_file).read()) - test_func = partial(topotest.router_json_cmp, - router, 'show bfd peers json', expected) + test_func = partial( + topotest.router_json_cmp, router, "show bfd peers json", expected + ) _, result = topotest.run_and_expect(test_func, None, count=8, wait=0.5) assertmsg = '"{}" JSON output mismatches'.format(router.name) assert result is None, assertmsg @@ -131,15 +131,16 @@ def test_bgp_convergence(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - logger.info('waiting for bgp peers to go up') + logger.info("waiting for bgp peers to go up") for router in tgen.routers().values(): - ref_file = '{}/{}/bgp_summary.json'.format(CWD, router.name) + ref_file = "{}/{}/bgp_summary.json".format(CWD, router.name) expected = json.loads(open(ref_file).read()) - test_func = partial(topotest.router_json_cmp, - router, 'show ip bgp summary json', expected) + test_func = partial( + topotest.router_json_cmp, router, "show ip bgp summary json", expected + ) _, res = topotest.run_and_expect(test_func, None, count=125, wait=1.0) - assertmsg = '{}: bgp did not converge'.format(router.name) + assertmsg = "{}: bgp did not converge".format(router.name) assert res is None, assertmsg @@ -149,15 +150,16 @@ def test_bgp_fast_convergence(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - logger.info('waiting for bgp peers converge') + logger.info("waiting for bgp peers converge") for router in tgen.routers().values(): - ref_file = '{}/{}/bgp_prefixes.json'.format(CWD, router.name) + ref_file = "{}/{}/bgp_prefixes.json".format(CWD, router.name) expected = json.loads(open(ref_file).read()) - test_func = partial(topotest.router_json_cmp, - router, 'show ip bgp json', expected) + test_func = partial( + topotest.router_json_cmp, router, "show ip bgp json", expected + ) _, res = topotest.run_and_expect(test_func, None, count=40, wait=0.5) - assertmsg = '{}: bgp did not converge'.format(router.name) + assertmsg = "{}: bgp did not converge".format(router.name) assert res is None, assertmsg @@ -171,29 +173,30 @@ def test_bfd_fast_convergence(): pytest.skip(tgen.errors) # Disable r1-eth0 link. - tgen.gears['r1'].link_enable('r1-eth0', enabled=False) + tgen.gears["r1"].link_enable("r1-eth0", enabled=False) # Wait the minimum time we can before checking that BGP/BFD # converged. - logger.info('waiting for BFD converge') + logger.info("waiting for BFD converge") # Check that BGP converged quickly. for router in tgen.routers().values(): - json_file = '{}/{}/peers.json'.format(CWD, router.name) + json_file = "{}/{}/peers.json".format(CWD, router.name) expected = json.loads(open(json_file).read()) # Load the same file as previous test, but expect R1 to be down. - if router.name == 'r1': + if router.name == "r1": for peer in expected: - if peer['peer'] == '192.168.0.2': - peer['status'] = 'down' + if peer["peer"] == "192.168.0.2": + peer["status"] = "down" else: for peer in expected: - if peer['peer'] == '192.168.0.1': - peer['status'] = 'down' + if peer["peer"] == "192.168.0.1": + peer["status"] = "down" - test_func = partial(topotest.router_json_cmp, - router, 'show bfd peers json', expected) + test_func = partial( + topotest.router_json_cmp, router, "show bfd peers json", expected + ) _, res = topotest.run_and_expect(test_func, None, count=20, wait=0.5) assertmsg = '"{}" JSON output mismatches'.format(router.name) assert res is None, assertmsg @@ -205,31 +208,27 @@ def test_bgp_fast_reconvergence(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - logger.info('waiting for BGP re convergence') + logger.info("waiting for BGP re convergence") # Check that BGP converged quickly. for router in tgen.routers().values(): - ref_file = '{}/{}/bgp_prefixes.json'.format(CWD, router.name) + ref_file = "{}/{}/bgp_prefixes.json".format(CWD, router.name) expected = json.loads(open(ref_file).read()) # Load the same file as previous test, but set networks to None # to test absence. - if router.name == 'r1': - expected['routes']['10.254.254.2/32'] = None - expected['routes']['10.254.254.3/32'] = None - expected['routes']['10.254.254.4/32'] = None + if router.name == "r1": + expected["routes"]["10.254.254.2/32"] = None + expected["routes"]["10.254.254.3/32"] = None + expected["routes"]["10.254.254.4/32"] = None else: - expected['routes']['10.254.254.1/32'] = None - - test_func = partial(topotest.router_json_cmp, - router, 'show ip bgp json', expected) - _, res = topotest.run_and_expect( - test_func, - None, - count=3, - wait=1 + expected["routes"]["10.254.254.1/32"] = None + + test_func = partial( + topotest.router_json_cmp, router, "show ip bgp json", expected ) - assertmsg = '{}: bgp did not converge'.format(router.name) + _, res = topotest.run_and_expect(test_func, None, count=3, wait=1) + assertmsg = "{}: bgp did not converge".format(router.name) assert res is None, assertmsg @@ -237,11 +236,11 @@ def test_memory_leak(): "Run the memory leak test and report results." tgen = get_topogen() if not tgen.is_memleak_enabled(): - pytest.skip('Memory leak test/report is disabled') + pytest.skip("Memory leak test/report is disabled") tgen.report_memory_leaks() -if __name__ == '__main__': +if __name__ == "__main__": args = ["-s"] + sys.argv[1:] sys.exit(pytest.main(args)) diff --git a/tests/topotests/bfd-topo2/r1/bfdd.conf b/tests/topotests/bfd-topo2/r1/bfdd.conf index 5c2571bdbd..f03135021e 100644 --- a/tests/topotests/bfd-topo2/r1/bfdd.conf +++ b/tests/topotests/bfd-topo2/r1/bfdd.conf @@ -1,3 +1,8 @@ +! +debug bfd network +debug bfd peer +debug bfd zebra +! bfd peer 2001:db8:4::1 multihop local-address 2001:db8:1::1 no shutdown diff --git a/tests/topotests/bfd-topo2/r1/bgpd.conf b/tests/topotests/bfd-topo2/r1/bgpd.conf index 1623b4578b..4d96bec2cb 100644 --- a/tests/topotests/bfd-topo2/r1/bgpd.conf +++ b/tests/topotests/bfd-topo2/r1/bgpd.conf @@ -1,5 +1,6 @@ router bgp 101 bgp router-id 10.254.254.1 + no bgp ebgp-requires-policy neighbor r2g peer-group neighbor r2g remote-as external neighbor r2g bfd diff --git a/tests/topotests/bfd-topo2/r1/ipv4_routes.json b/tests/topotests/bfd-topo2/r1/ipv4_routes.json index 8a2ec25baa..650c0a81bb 100644 --- a/tests/topotests/bfd-topo2/r1/ipv4_routes.json +++ b/tests/topotests/bfd-topo2/r1/ipv4_routes.json @@ -3,16 +3,13 @@ { "distance": 20, "protocol": "bgp", - "internalFlags": 8, "metric": 0, "selected": true, "installed": true, "prefix": "10.0.3.0/24", - "internalStatus": 34, "nexthops": [ { "interfaceName": "r1-eth0", - "interfaceIndex": 2, "fib": true, "flags": 3, "active": true, @@ -25,16 +22,13 @@ { "distance": 20, "protocol": "bgp", - "internalFlags": 8, "metric": 0, "selected": true, "installed": true, "prefix": "10.254.254.2/32", - "internalStatus": 34, "nexthops": [ { "interfaceName": "r1-eth0", - "interfaceIndex": 2, "fib": true, "flags": 3, "active": true, @@ -47,17 +41,14 @@ { "distance": 0, "protocol": "connected", - "internalFlags": 8, "metric": 0, "selected": true, "installed": true, "prefix": "10.254.254.1/32", - "internalStatus": 32, "nexthops": [ { "directlyConnected": true, "interfaceName": "lo", - "interfaceIndex": 1, "fib": true, "flags": 3, "active": true diff --git a/tests/topotests/bfd-topo2/r1/ipv6_routes.json b/tests/topotests/bfd-topo2/r1/ipv6_routes.json index 618853bd42..50c1f9ad8f 100644 --- a/tests/topotests/bfd-topo2/r1/ipv6_routes.json +++ b/tests/topotests/bfd-topo2/r1/ipv6_routes.json @@ -3,16 +3,13 @@ { "distance": 20, "protocol": "bgp", - "internalFlags": 8, "metric": 0, "selected": true, "installed": true, "prefix": "2001:db8:4::/64", - "internalStatus": 34, "nexthops": [ { "interfaceName": "r1-eth0", - "interfaceIndex": 2, "fib": true, "flags": 3, "active": true, @@ -25,15 +22,11 @@ { "distance": 20, "protocol": "bgp", - "internalFlags": 0, "metric": 0, - "internalStatus": 2, "prefix": "2001:db8:1::/64", "nexthops": [ { "interfaceName": "r1-eth0", - "interfaceIndex": 2, - "flags": 1, "active": true, "afi": "ipv6" } @@ -42,17 +35,14 @@ { "distance": 0, "protocol": "connected", - "internalFlags": 8, "metric": 0, "selected": true, "installed": true, "prefix": "2001:db8:1::/64", - "internalStatus": 32, "nexthops": [ { "directlyConnected": true, "interfaceName": "r1-eth0", - "interfaceIndex": 2, "fib": true, "flags": 3, "active": true diff --git a/tests/topotests/bfd-topo2/r2/bfdd.conf b/tests/topotests/bfd-topo2/r2/bfdd.conf new file mode 100644 index 0000000000..f35e772790 --- /dev/null +++ b/tests/topotests/bfd-topo2/r2/bfdd.conf @@ -0,0 +1,5 @@ +! +debug bfd network +debug bfd peer +debug bfd zebra +! diff --git a/tests/topotests/bfd-topo2/r2/bgpd.conf b/tests/topotests/bfd-topo2/r2/bgpd.conf index bf42d21812..4d02fc4f29 100644 --- a/tests/topotests/bfd-topo2/r2/bgpd.conf +++ b/tests/topotests/bfd-topo2/r2/bgpd.conf @@ -1,5 +1,6 @@ router bgp 102 bgp router-id 10.254.254.2 + no bgp ebgp-requires-policy neighbor r2g peer-group neighbor r2g remote-as external neighbor r2g bfd diff --git a/tests/topotests/bfd-topo2/r2/ipv4_routes.json b/tests/topotests/bfd-topo2/r2/ipv4_routes.json index b9d8afb430..3d49b178b5 100644 --- a/tests/topotests/bfd-topo2/r2/ipv4_routes.json +++ b/tests/topotests/bfd-topo2/r2/ipv4_routes.json @@ -3,16 +3,12 @@ { "distance": 110, "protocol": "ospf", - "internalFlags": 0, "metric": 10, - "internalStatus": 2, "prefix": "10.0.3.0/24", "nexthops": [ { "active": true, "directlyConnected": true, - "flags": 1, - "interfaceIndex": 3, "interfaceName": "r2-eth1" } ] @@ -20,17 +16,14 @@ { "distance": 0, "protocol": "connected", - "internalFlags": 8, "metric": 0, "selected": true, "installed": true, "prefix": "10.0.3.0/24", - "internalStatus": 32, "nexthops": [ { "directlyConnected": true, "interfaceName": "r2-eth1", - "interfaceIndex": 3, "fib": true, "flags": 3, "active": true @@ -42,17 +35,14 @@ { "distance": 110, "protocol": "ospf", - "internalFlags": 8, "metric": 20, "selected": true, "installed": true, "prefix": "10.254.254.3/32", - "internalStatus": 34, "nexthops": [ { "interfaceName": "r2-eth1", "ip": "10.0.3.1", - "interfaceIndex": 3, "fib": true, "flags": 3, "active": true, @@ -65,17 +55,14 @@ { "distance": 0, "protocol": "connected", - "internalFlags": 8, "metric": 0, "selected": true, "installed": true, "prefix": "10.254.254.2/32", - "internalStatus": 32, "nexthops": [ { "directlyConnected": true, "interfaceName": "lo", - "interfaceIndex": 1, "fib": true, "flags": 3, "active": true @@ -87,16 +74,13 @@ { "distance": 20, "protocol": "bgp", - "internalFlags": 8, "metric": 0, "selected": true, "installed": true, "prefix": "10.254.254.1/32", - "internalStatus": 34, "nexthops": [ { "interfaceName": "r2-eth0", - "interfaceIndex": 2, "fib": true, "flags": 3, "active": true, diff --git a/tests/topotests/bfd-topo2/r2/ipv6_routes.json b/tests/topotests/bfd-topo2/r2/ipv6_routes.json index 004e7588aa..4f3c74c5e2 100644 --- a/tests/topotests/bfd-topo2/r2/ipv6_routes.json +++ b/tests/topotests/bfd-topo2/r2/ipv6_routes.json @@ -3,16 +3,12 @@ { "distance": 110, "protocol": "ospf6", - "internalFlags": 0, "metric": 10, - "internalStatus": 2, "prefix": "2001:db8:4::/64", "nexthops": [ { "active": true, "directlyConnected": true, - "flags": 1, - "interfaceIndex": 4, "interfaceName": "r2-eth2" } ] @@ -20,17 +16,14 @@ { "distance": 0, "protocol": "connected", - "internalFlags": 8, "metric": 0, "selected": true, "installed": true, "prefix": "2001:db8:4::/64", - "internalStatus": 32, "nexthops": [ { "directlyConnected": true, "interfaceName": "r2-eth2", - "interfaceIndex": 4, "fib": true, "flags": 3, "active": true @@ -42,17 +35,14 @@ { "distance": 0, "protocol": "connected", - "internalFlags": 8, "metric": 0, "selected": true, "installed": true, "prefix": "2001:db8:1::/64", - "internalStatus": 32, "nexthops": [ { "directlyConnected": true, "interfaceName": "r2-eth0", - "interfaceIndex": 2, "fib": true, "flags": 3, "active": true diff --git a/tests/topotests/bfd-topo2/r2/ospf6d.conf b/tests/topotests/bfd-topo2/r2/ospf6d.conf index f1cdb50285..48a729ce19 100644 --- a/tests/topotests/bfd-topo2/r2/ospf6d.conf +++ b/tests/topotests/bfd-topo2/r2/ospf6d.conf @@ -1,5 +1,7 @@ interface r2-eth2 ipv6 ospf6 bfd + ipv6 ospf6 hello-interval 2 + ipv6 ospf6 dead-interval 10 ! router ospf6 ospf6 router-id 10.254.254.2 diff --git a/tests/topotests/bfd-topo2/r2/ospfd.conf b/tests/topotests/bfd-topo2/r2/ospfd.conf index 8e0c45980d..c786f1fe43 100644 --- a/tests/topotests/bfd-topo2/r2/ospfd.conf +++ b/tests/topotests/bfd-topo2/r2/ospfd.conf @@ -1,5 +1,7 @@ interface r2-eth1 ip ospf area 0.0.0.1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 ip ospf bfd ! router ospf diff --git a/tests/topotests/bfd-topo2/r3/bfdd.conf b/tests/topotests/bfd-topo2/r3/bfdd.conf new file mode 100644 index 0000000000..f35e772790 --- /dev/null +++ b/tests/topotests/bfd-topo2/r3/bfdd.conf @@ -0,0 +1,5 @@ +! +debug bfd network +debug bfd peer +debug bfd zebra +! diff --git a/tests/topotests/bfd-topo2/r3/ipv4_routes.json b/tests/topotests/bfd-topo2/r3/ipv4_routes.json index 14dfc692fe..e96fdc0b15 100644 --- a/tests/topotests/bfd-topo2/r3/ipv4_routes.json +++ b/tests/topotests/bfd-topo2/r3/ipv4_routes.json @@ -3,16 +3,12 @@ { "distance": 110, "protocol": "ospf", - "internalFlags": 0, "metric": 10, - "internalStatus": 0, "prefix": "10.0.3.0/24", "nexthops": [ { "active": true, "directlyConnected": true, - "flags": 1, - "interfaceIndex": 2, "interfaceName": "r3-eth0" } ] @@ -20,17 +16,14 @@ { "distance": 0, "protocol": "connected", - "internalFlags": 8, "metric": 0, "selected": true, "installed": true, "prefix": "10.0.3.0/24", - "internalStatus": 32, "nexthops": [ { "directlyConnected": true, "interfaceName": "r3-eth0", - "interfaceIndex": 2, "fib": true, "flags": 3, "active": true @@ -42,17 +35,14 @@ { "distance": 0, "protocol": "connected", - "internalFlags": 8, "metric": 0, "selected": true, "installed": true, "prefix": "10.254.254.3/32", - "internalStatus": 32, "nexthops": [ { "directlyConnected": true, "interfaceName": "lo", - "interfaceIndex": 1, "fib": true, "flags": 3, "active": true @@ -64,17 +54,14 @@ { "distance": 110, "protocol": "ospf", - "internalFlags": 8, "metric": 20, "selected": true, "installed": true, "prefix": "10.254.254.2/32", - "internalStatus": 34, "nexthops": [ { "interfaceName": "r3-eth0", "ip": "10.0.3.2", - "interfaceIndex": 2, "fib": true, "flags": 3, "active": true, @@ -87,17 +74,14 @@ { "distance": 110, "protocol": "ospf", - "internalFlags": 8, "metric": 20, "selected": true, "installed": true, "prefix": "10.254.254.1/32", - "internalStatus": 34, "nexthops": [ { "interfaceName": "r3-eth0", "ip": "10.0.3.2", - "interfaceIndex": 2, "fib": true, "flags": 3, "active": true, diff --git a/tests/topotests/bfd-topo2/r3/ospfd.conf b/tests/topotests/bfd-topo2/r3/ospfd.conf index cf2a1bdf76..932ab4da63 100644 --- a/tests/topotests/bfd-topo2/r3/ospfd.conf +++ b/tests/topotests/bfd-topo2/r3/ospfd.conf @@ -1,5 +1,7 @@ interface r3-eth0 ip ospf area 0.0.0.1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 ip ospf bfd ! router ospf diff --git a/tests/topotests/bfd-topo2/r4/bfdd.conf b/tests/topotests/bfd-topo2/r4/bfdd.conf index fdb4412446..0173dc9be2 100644 --- a/tests/topotests/bfd-topo2/r4/bfdd.conf +++ b/tests/topotests/bfd-topo2/r4/bfdd.conf @@ -1,3 +1,8 @@ +! +debug bfd network +debug bfd peer +debug bfd zebra +! bfd peer 2001:db8:1::1 multihop local-address 2001:db8:4::1 no shutdown diff --git a/tests/topotests/bfd-topo2/r4/ipv4_routes.json b/tests/topotests/bfd-topo2/r4/ipv4_routes.json index ae1e97b017..dc394aa891 100644 --- a/tests/topotests/bfd-topo2/r4/ipv4_routes.json +++ b/tests/topotests/bfd-topo2/r4/ipv4_routes.json @@ -3,17 +3,14 @@ { "distance": 0, "protocol": "connected", - "internalFlags": 8, "metric": 0, "selected": true, "installed": true, "prefix": "10.254.254.4/32", - "internalStatus": 32, "nexthops": [ { "directlyConnected": true, "interfaceName": "lo", - "interfaceIndex": 1, "fib": true, "flags": 3, "active": true diff --git a/tests/topotests/bfd-topo2/r4/ipv6_routes.json b/tests/topotests/bfd-topo2/r4/ipv6_routes.json index 33608b45aa..c828575c84 100644 --- a/tests/topotests/bfd-topo2/r4/ipv6_routes.json +++ b/tests/topotests/bfd-topo2/r4/ipv6_routes.json @@ -3,16 +3,12 @@ { "distance": 110, "protocol": "ospf6", - "internalFlags": 0, "metric": 10, - "internalStatus": 2, "prefix": "2001:db8:4::/64", "nexthops": [ { "active": true, "directlyConnected": true, - "flags": 1, - "interfaceIndex": 2, "interfaceName": "r4-eth0" } ] @@ -20,17 +16,14 @@ { "distance": 0, "protocol": "connected", - "internalFlags": 8, "metric": 0, "selected": true, "installed": true, "prefix": "2001:db8:4::/64", - "internalStatus": 32, "nexthops": [ { "directlyConnected": true, "interfaceName": "r4-eth0", - "interfaceIndex": 2, "fib": true, "flags": 3, "active": true @@ -42,16 +35,13 @@ { "distance": 110, "protocol": "ospf6", - "internalFlags": 8, "metric": 10, "selected": true, "installed": true, "prefix": "2001:db8:1::/64", - "internalStatus": 34, "nexthops": [ { "interfaceName": "r4-eth0", - "interfaceIndex": 2, "fib": true, "flags": 3, "active": true, diff --git a/tests/topotests/bfd-topo2/r4/ospf6d.conf b/tests/topotests/bfd-topo2/r4/ospf6d.conf index 756597d6f8..57f7f6c079 100644 --- a/tests/topotests/bfd-topo2/r4/ospf6d.conf +++ b/tests/topotests/bfd-topo2/r4/ospf6d.conf @@ -1,5 +1,7 @@ interface r4-eth0 ipv6 ospf6 bfd + ipv6 ospf6 hello-interval 2 + ipv6 ospf6 dead-interval 10 ! router ospf6 ospf6 router-id 10.254.254.4 diff --git a/tests/topotests/bfd-topo2/test_bfd_topo2.py b/tests/topotests/bfd-topo2/test_bfd_topo2.py index 773db129f0..3e87e8485a 100644 --- a/tests/topotests/bfd-topo2/test_bfd_topo2.py +++ b/tests/topotests/bfd-topo2/test_bfd_topo2.py @@ -35,7 +35,7 @@ # Save the Current Working Directory to find configuration files. CWD = os.path.dirname(os.path.realpath(__file__)) -sys.path.append(os.path.join(CWD, '../')) +sys.path.append(os.path.join(CWD, "../")) # pylint: disable=C0413 # Import topogen and topotest helpers @@ -49,25 +49,26 @@ class BFDTopo(Topo): "Test topology builder" + def build(self, *_args, **_opts): "Build function" tgen = get_topogen(self) # Create 4 routers. for routern in range(1, 5): - tgen.add_router('r{}'.format(routern)) + tgen.add_router("r{}".format(routern)) - switch = tgen.add_switch('s1') - switch.add_link(tgen.gears['r1']) - switch.add_link(tgen.gears['r2']) + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) - switch = tgen.add_switch('s2') - switch.add_link(tgen.gears['r2']) - switch.add_link(tgen.gears['r3']) + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) - switch = tgen.add_switch('s3') - switch.add_link(tgen.gears['r2']) - switch.add_link(tgen.gears['r4']) + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r4"]) def setup_module(mod): @@ -78,24 +79,19 @@ def setup_module(mod): router_list = tgen.routers() for rname, router in router_list.iteritems(): router.load_config( - TopoRouter.RD_ZEBRA, - os.path.join(CWD, '{}/zebra.conf'.format(rname)) + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) ) router.load_config( - TopoRouter.RD_BFD, - os.path.join(CWD, '{}/bfdd.conf'.format(rname)) + TopoRouter.RD_BFD, os.path.join(CWD, "{}/bfdd.conf".format(rname)) ) router.load_config( - TopoRouter.RD_BGP, - os.path.join(CWD, '{}/bgpd.conf'.format(rname)) + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) ) router.load_config( - TopoRouter.RD_OSPF, - os.path.join(CWD, '{}/ospfd.conf'.format(rname)) + TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname)) ) router.load_config( - TopoRouter.RD_OSPF6, - os.path.join(CWD, '{}/ospf6d.conf'.format(rname)) + TopoRouter.RD_OSPF6, os.path.join(CWD, "{}/ospf6d.conf".format(rname)) ) # Initialize all routers. @@ -105,8 +101,8 @@ def setup_module(mod): # daemon exists. for router in router_list.values(): # Check for Version - if router.has_version('<', '5.1'): - tgen.set_error('Unsupported FRR version') + if router.has_version("<", "5.1"): + tgen.set_error("Unsupported FRR version") break @@ -128,32 +124,32 @@ def test_protocols_convergence(): # Check IPv4 routing tables. logger.info("Checking IPv4 routes for convergence") for router in tgen.routers().values(): - json_file = '{}/{}/ipv4_routes.json'.format(CWD, router.name) + json_file = "{}/{}/ipv4_routes.json".format(CWD, router.name) if not os.path.isfile(json_file): - logger.info('skipping file {}'.format(json_file)) + logger.info("skipping file {}".format(json_file)) continue expected = json.loads(open(json_file).read()) - test_func = partial(topotest.router_json_cmp, - router, 'show ip route json', expected) - _, result = topotest.run_and_expect(test_func, None, count=160, - wait=0.5) + test_func = partial( + topotest.router_json_cmp, router, "show ip route json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=160, wait=0.5) assertmsg = '"{}" JSON output mismatches'.format(router.name) assert result is None, assertmsg # Check IPv6 routing tables. logger.info("Checking IPv6 routes for convergence") for router in tgen.routers().values(): - json_file = '{}/{}/ipv6_routes.json'.format(CWD, router.name) + json_file = "{}/{}/ipv6_routes.json".format(CWD, router.name) if not os.path.isfile(json_file): - logger.info('skipping file {}'.format(json_file)) + logger.info("skipping file {}".format(json_file)) continue expected = json.loads(open(json_file).read()) - test_func = partial(topotest.router_json_cmp, - router, 'show ipv6 route json', expected) - _, result = topotest.run_and_expect(test_func, None, count=160, - wait=0.5) + test_func = partial( + topotest.router_json_cmp, router, "show ipv6 route json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=160, wait=0.5) assertmsg = '"{}" JSON output mismatches'.format(router.name) assert result is None, assertmsg @@ -164,14 +160,15 @@ def test_bfd_connection(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - logger.info('waiting for bfd peers to go up') + logger.info("waiting for bfd peers to go up") for router in tgen.routers().values(): - json_file = '{}/{}/peers.json'.format(CWD, router.name) + json_file = "{}/{}/peers.json".format(CWD, router.name) expected = json.loads(open(json_file).read()) - test_func = partial(topotest.router_json_cmp, - router, 'show bfd peers json', expected) + test_func = partial( + topotest.router_json_cmp, router, "show bfd peers json", expected + ) _, result = topotest.run_and_expect(test_func, None, count=8, wait=0.5) assertmsg = '"{}" JSON output mismatches'.format(router.name) assert result is None, assertmsg @@ -181,11 +178,11 @@ def test_memory_leak(): "Run the memory leak test and report results." tgen = get_topogen() if not tgen.is_memleak_enabled(): - pytest.skip('Memory leak test/report is disabled') + pytest.skip("Memory leak test/report is disabled") tgen.report_memory_leaks() -if __name__ == '__main__': +if __name__ == "__main__": args = ["-s"] + sys.argv[1:] sys.exit(pytest.main(args)) diff --git a/tests/topotests/bfd-topo3/__init__.py b/tests/topotests/bfd-topo3/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bfd-topo3/r1/bfd-peers.json b/tests/topotests/bfd-topo3/r1/bfd-peers.json new file mode 100644 index 0000000000..56205d538b --- /dev/null +++ b/tests/topotests/bfd-topo3/r1/bfd-peers.json @@ -0,0 +1,68 @@ +[ + { + "detect-multiplier": 3, + "diagnostic": "ok", + "echo-interval": 0, + "id": "*", + "local": "2001:db8:1::1", + "minimum-ttl": 253, + "multihop": true, + "passive-mode": true, + "peer": "2001:db8:3::1", + "receive-interval": 2000, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-echo-interval": 50, + "remote-id": "*", + "remote-receive-interval": 2000, + "remote-transmit-interval": 2000, + "status": "up", + "transmit-interval": 2000, + "uptime": "*", + "vrf": "default" + }, + { + "detect-multiplier": 3, + "diagnostic": "ok", + "echo-interval": 0, + "id": "*", + "interface": "r1-eth0", + "local": "2001:db8:1::1", + "multihop": false, + "passive-mode": true, + "peer": "2001:db8:1::2", + "receive-interval": 600, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-echo-interval": 50, + "remote-id": "*", + "remote-receive-interval": 600, + "remote-transmit-interval": 600, + "status": "up", + "transmit-interval": 600, + "uptime": "*", + "vrf": "default" + }, + { + "detect-multiplier": 3, + "diagnostic": "ok", + "echo-interval": 0, + "id": "*", + "local": "192.168.1.1", + "minimum-ttl": 254, + "multihop": true, + "passive-mode": true, + "peer": "192.168.2.1", + "receive-interval": 2000, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-echo-interval": 50, + "remote-id": "*", + "remote-receive-interval": 2000, + "remote-transmit-interval": 2000, + "status": "up", + "transmit-interval": 2000, + "uptime": "*", + "vrf": "default" + } +] diff --git a/tests/topotests/bfd-topo3/r1/bfdd.conf b/tests/topotests/bfd-topo3/r1/bfdd.conf new file mode 100644 index 0000000000..8e40b76d41 --- /dev/null +++ b/tests/topotests/bfd-topo3/r1/bfdd.conf @@ -0,0 +1,17 @@ +! +debug bfd network +debug bfd peer +debug bfd zebra +! +bfd + profile fast-tx + receive-interval 600 + transmit-interval 600 + passive-mode + ! + profile slow-tx + receive-interval 2000 + transmit-interval 2000 + passive-mode + ! +! diff --git a/tests/topotests/bfd-topo3/r1/bgpd.conf b/tests/topotests/bfd-topo3/r1/bgpd.conf new file mode 100644 index 0000000000..a0281d50c6 --- /dev/null +++ b/tests/topotests/bfd-topo3/r1/bgpd.conf @@ -0,0 +1,20 @@ +router bgp 100 + no bgp ebgp-requires-policy + neighbor 2001:db8:1::2 remote-as internal + neighbor 2001:db8:1::2 bfd profile fast-tx + neighbor 192.168.2.1 remote-as external + neighbor 192.168.2.1 ebgp-multihop 2 + neighbor 192.168.2.1 bfd profile slow-tx + neighbor 2001:db8:3::1 remote-as external + neighbor 2001:db8:3::1 ebgp-multihop 3 + neighbor 2001:db8:3::1 bfd profile slow-tx + address-family ipv4 unicast + redistribute connected + exit-address-family + address-family ipv6 unicast + redistribute connected + neighbor 2001:db8:1::2 activate + neighbor 192.168.2.1 activate + neighbor 2001:db8:3::1 activate + exit-address-family +! diff --git a/tests/topotests/bfd-topo3/r1/zebra.conf b/tests/topotests/bfd-topo3/r1/zebra.conf new file mode 100644 index 0000000000..64aee48436 --- /dev/null +++ b/tests/topotests/bfd-topo3/r1/zebra.conf @@ -0,0 +1,10 @@ +ip forwarding +ipv6 forwarding +! +interface lo + ip address 10.254.254.1/32 +! +interface r1-eth0 + ip address 192.168.1.1/24 + ipv6 address 2001:db8:1::1/64 +! diff --git a/tests/topotests/bfd-topo3/r2/bfd-peers.json b/tests/topotests/bfd-topo3/r2/bfd-peers.json new file mode 100644 index 0000000000..cb8985b13e --- /dev/null +++ b/tests/topotests/bfd-topo3/r2/bfd-peers.json @@ -0,0 +1,46 @@ +[ + { + "detect-multiplier": 3, + "diagnostic": "ok", + "echo-interval": 0, + "id": "*", + "interface": "r2-eth0", + "local": "2001:db8:1::2", + "multihop": false, + "passive-mode": false, + "peer": "2001:db8:1::1", + "receive-interval": 600, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-echo-interval": 50, + "remote-id": "*", + "remote-receive-interval": 600, + "remote-transmit-interval": 600, + "status": "up", + "transmit-interval": 600, + "uptime": "*", + "vrf": "default" + }, + { + "detect-multiplier": 3, + "diagnostic": "ok", + "echo-interval": 0, + "id": "*", + "interface": "r2-eth1", + "local": "2001:db8:2::2", + "multihop": false, + "passive-mode": false, + "peer": "2001:db8:2::1", + "receive-interval": 2000, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-echo-interval": 50, + "remote-id": "*", + "remote-receive-interval": 2000, + "remote-transmit-interval": 2000, + "status": "up", + "transmit-interval": 2000, + "uptime": "*", + "vrf": "default" + } +] diff --git a/tests/topotests/bfd-topo3/r2/bfdd.conf b/tests/topotests/bfd-topo3/r2/bfdd.conf new file mode 100644 index 0000000000..2a92e463e0 --- /dev/null +++ b/tests/topotests/bfd-topo3/r2/bfdd.conf @@ -0,0 +1,15 @@ +! +debug bfd network +debug bfd peer +debug bfd zebra +! +bfd + profile fast-tx + receive-interval 600 + transmit-interval 600 + ! + profile slow-tx + receive-interval 2000 + transmit-interval 2000 + ! +! diff --git a/tests/topotests/bfd-topo3/r2/bgpd.conf b/tests/topotests/bfd-topo3/r2/bgpd.conf new file mode 100644 index 0000000000..0e96033023 --- /dev/null +++ b/tests/topotests/bfd-topo3/r2/bgpd.conf @@ -0,0 +1,15 @@ +router bgp 100 + no bgp ebgp-requires-policy + neighbor 2001:db8:1::1 remote-as internal + neighbor 2001:db8:1::1 bfd profile fast-tx + neighbor 2001:db8:2::1 remote-as external + neighbor 2001:db8:2::1 bfd profile slow-tx + address-family ipv4 unicast + redistribute connected + exit-address-family + address-family ipv6 unicast + redistribute connected + neighbor 2001:db8:1::1 activate + neighbor 2001:db8:2::1 activate + exit-address-family +! diff --git a/tests/topotests/bfd-topo3/r2/zebra.conf b/tests/topotests/bfd-topo3/r2/zebra.conf new file mode 100644 index 0000000000..c7e22d4804 --- /dev/null +++ b/tests/topotests/bfd-topo3/r2/zebra.conf @@ -0,0 +1,14 @@ +ip forwarding +ipv6 forwarding +! +interface lo + ip address 10.254.254.2/32 +! +interface r2-eth0 + ip address 192.168.1.2/24 + ipv6 address 2001:db8:1::2/64 +! +interface r2-eth1 + ip address 192.168.2.2/24 + ipv6 address 2001:db8:2::2/64 +! diff --git a/tests/topotests/bfd-topo3/r3/bfd-peers.json b/tests/topotests/bfd-topo3/r3/bfd-peers.json new file mode 100644 index 0000000000..8be35fd084 --- /dev/null +++ b/tests/topotests/bfd-topo3/r3/bfd-peers.json @@ -0,0 +1,68 @@ +[ + { + "detect-multiplier": 3, + "diagnostic": "ok", + "echo-interval": 0, + "id": "*", + "interface": "r3-eth1", + "local": "2001:db8:3::2", + "multihop": false, + "passive-mode": false, + "peer": "2001:db8:3::1", + "receive-interval": 2000, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-echo-interval": 50, + "remote-id": "*", + "remote-receive-interval": 2000, + "remote-transmit-interval": 2000, + "status": "up", + "transmit-interval": 2000, + "uptime": "*", + "vrf": "default" + }, + { + "detect-multiplier": 3, + "diagnostic": "ok", + "echo-interval": 0, + "id": "*", + "interface": "r3-eth0", + "local": "2001:db8:2::1", + "multihop": false, + "passive-mode": false, + "peer": "2001:db8:2::2", + "receive-interval": 2000, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-echo-interval": 50, + "remote-id": "*", + "remote-receive-interval": 2000, + "remote-transmit-interval": 2000, + "status": "up", + "transmit-interval": 2000, + "uptime": "*", + "vrf": "default" + }, + { + "detect-multiplier": 3, + "diagnostic": "ok", + "echo-interval": 0, + "id": "*", + "local": "192.168.2.1", + "minimum-ttl": 254, + "multihop": true, + "passive-mode": false, + "peer": "192.168.1.1", + "receive-interval": 2000, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-echo-interval": 50, + "remote-id": "*", + "remote-receive-interval": 2000, + "remote-transmit-interval": 2000, + "status": "up", + "transmit-interval": 2000, + "uptime": "*", + "vrf": "default" + } +] diff --git a/tests/topotests/bfd-topo3/r3/bfdd.conf b/tests/topotests/bfd-topo3/r3/bfdd.conf new file mode 100644 index 0000000000..f7972c6ce5 --- /dev/null +++ b/tests/topotests/bfd-topo3/r3/bfdd.conf @@ -0,0 +1,11 @@ +! +debug bfd network +debug bfd peer +debug bfd zebra +! +bfd + profile slow-tx + receive-interval 2000 + transmit-interval 2000 + ! +! diff --git a/tests/topotests/bfd-topo3/r3/bgpd.conf b/tests/topotests/bfd-topo3/r3/bgpd.conf new file mode 100644 index 0000000000..e14d2011a0 --- /dev/null +++ b/tests/topotests/bfd-topo3/r3/bgpd.conf @@ -0,0 +1,19 @@ +router bgp 300 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external + neighbor 192.168.1.1 ebgp-multihop 2 + neighbor 192.168.1.1 bfd profile slow-tx + neighbor 2001:db8:2::2 remote-as external + neighbor 2001:db8:2::2 bfd profile slow-tx + neighbor 2001:db8:3::1 remote-as external + neighbor 2001:db8:3::1 bfd profile slow-tx + address-family ipv4 unicast + redistribute connected + exit-address-family + address-family ipv6 unicast + redistribute connected + neighbor 192.168.1.1 activate + neighbor 2001:db8:2::2 activate + neighbor 2001:db8:3::1 activate + exit-address-family +! diff --git a/tests/topotests/bfd-topo3/r3/zebra.conf b/tests/topotests/bfd-topo3/r3/zebra.conf new file mode 100644 index 0000000000..14248fb6f7 --- /dev/null +++ b/tests/topotests/bfd-topo3/r3/zebra.conf @@ -0,0 +1,14 @@ +ip forwarding +ipv6 forwarding +! +interface lo + ip address 10.254.254.3/32 +! +interface r3-eth0 + ip address 192.168.2.1/24 + ipv6 address 2001:db8:2::1/64 +! +interface r3-eth1 + ip address 192.168.3.2/24 + ipv6 address 2001:db8:3::2/64 +! diff --git a/tests/topotests/bfd-topo3/r4/bfd-peers.json b/tests/topotests/bfd-topo3/r4/bfd-peers.json new file mode 100644 index 0000000000..e2e6722ef4 --- /dev/null +++ b/tests/topotests/bfd-topo3/r4/bfd-peers.json @@ -0,0 +1,46 @@ +[ + { + "detect-multiplier": 3, + "diagnostic": "ok", + "echo-interval": 0, + "id": "*", + "local": "2001:db8:3::1", + "minimum-ttl": 253, + "multihop": true, + "passive-mode": false, + "peer": "2001:db8:1::1", + "receive-interval": 2000, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-echo-interval": 50, + "remote-id": "*", + "remote-receive-interval": 2000, + "remote-transmit-interval": 2000, + "status": "up", + "transmit-interval": 2000, + "uptime": "*", + "vrf": "default" + }, + { + "detect-multiplier": 3, + "diagnostic": "ok", + "echo-interval": 0, + "id": "*", + "interface": "r4-eth0", + "local": "2001:db8:3::1", + "multihop": false, + "passive-mode": false, + "peer": "2001:db8:3::2", + "receive-interval": 2000, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-echo-interval": 50, + "remote-id": "*", + "remote-receive-interval": 2000, + "remote-transmit-interval": 2000, + "status": "up", + "transmit-interval": 2000, + "uptime": "*", + "vrf": "default" + } +] diff --git a/tests/topotests/bfd-topo3/r4/bfdd.conf b/tests/topotests/bfd-topo3/r4/bfdd.conf new file mode 100644 index 0000000000..f44abc0b8a --- /dev/null +++ b/tests/topotests/bfd-topo3/r4/bfdd.conf @@ -0,0 +1,16 @@ +! +debug bfd network +debug bfd peer +debug bfd zebra +! +bfd + profile slow-tx + receive-interval 2000 + transmit-interval 2000 + ! + profile slow-tx-mh + receive-interval 2000 + transmit-interval 2000 + minimum-ttl 250 + ! +! diff --git a/tests/topotests/bfd-topo3/r4/bgpd.conf b/tests/topotests/bfd-topo3/r4/bgpd.conf new file mode 100644 index 0000000000..3e81008d5d --- /dev/null +++ b/tests/topotests/bfd-topo3/r4/bgpd.conf @@ -0,0 +1,16 @@ +router bgp 400 + no bgp ebgp-requires-policy + neighbor 2001:db8:3::2 remote-as external + neighbor 2001:db8:3::2 bfd profile slow-tx + neighbor 2001:db8:1::1 remote-as external + neighbor 2001:db8:1::1 ebgp-multihop 3 + neighbor 2001:db8:1::1 bfd profile slow-tx-mh + address-family ipv4 unicast + redistribute connected + exit-address-family + address-family ipv6 unicast + redistribute connected + neighbor 2001:db8:1::1 activate + neighbor 2001:db8:3::2 activate + exit-address-family +! diff --git a/tests/topotests/bfd-topo3/r4/zebra.conf b/tests/topotests/bfd-topo3/r4/zebra.conf new file mode 100644 index 0000000000..bf0cfcf42c --- /dev/null +++ b/tests/topotests/bfd-topo3/r4/zebra.conf @@ -0,0 +1,10 @@ +ip forwarding +ipv6 forwarding +! +interface lo + ip address 10.254.254.4/32 +! +interface r4-eth0 + ip address 192.168.3.1/24 + ipv6 address 2001:db8:3::1/64 +! diff --git a/tests/topotests/bfd-topo3/test_bfd_topo3.dot b/tests/topotests/bfd-topo3/test_bfd_topo3.dot new file mode 100644 index 0000000000..502cea11f2 --- /dev/null +++ b/tests/topotests/bfd-topo3/test_bfd_topo3.dot @@ -0,0 +1,73 @@ +## Color coding: +######################### +## Main FRR: #f08080 red +## Switches: #d0e0d0 gray +## RIP: #19e3d9 Cyan +## RIPng: #fcb314 dark yellow +## OSPFv2: #32b835 Green +## OSPFv3: #19e3d9 Cyan +## ISIS IPv4 #fcb314 dark yellow +## ISIS IPv6 #9a81ec purple +## BGP IPv4 #eee3d3 beige +## BGP IPv6 #fdff00 yellow +##### Colors (see http://www.color-hex.com/) + +graph template { + label="bfd-topo3"; + + # Routers + r1 [ + shape=doubleoctagon, + label="r1", + fillcolor="#f08080", + style=filled, + ]; + r2 [ + shape=doubleoctagon + label="r2", + fillcolor="#f08080", + style=filled, + ]; + r3 [ + shape=doubleoctagon + label="r3", + fillcolor="#f08080", + style=filled, + ]; + r4 [ + shape=doubleoctagon + label="r4", + fillcolor="#f08080", + style=filled, + ]; + + # Switches + sw1 [ + shape=oval, + label="sw1\n192.168.1.0/24\n2001:db8:1::/64", + fillcolor="#d0e0d0", + style=filled, + ]; + sw2 [ + shape=oval, + label="sw2\n192.168.2.0/24\n2001:db8:2::/64", + fillcolor="#d0e0d0", + style=filled, + ]; + sw3 [ + shape=oval, + label="sw3\n192.168.3.0/24\n2001:db8:3::/64", + fillcolor="#d0e0d0", + style=filled, + ]; + + # Connections + r1 -- sw1 [label="eth0\n.1"]; + r2 -- sw1 [label="eth0\n.2"]; + + r3 -- sw2 [label="eth0\n.1"]; + r2 -- sw2 [label="eth1\n.2"]; + + r4 -- sw3 [label="eth0\n.1"]; + r3 -- sw3 [label="eth2\n.2"]; +} diff --git a/tests/topotests/bfd-topo3/test_bfd_topo3.jpg b/tests/topotests/bfd-topo3/test_bfd_topo3.jpg new file mode 100644 index 0000000000..6b532560bf Binary files /dev/null and b/tests/topotests/bfd-topo3/test_bfd_topo3.jpg differ diff --git a/tests/topotests/bfd-topo3/test_bfd_topo3.py b/tests/topotests/bfd-topo3/test_bfd_topo3.py new file mode 100644 index 0000000000..bcee338a92 --- /dev/null +++ b/tests/topotests/bfd-topo3/test_bfd_topo3.py @@ -0,0 +1,191 @@ +#!/usr/bin/env python + +# +# test_bfd_topo3.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2020 by +# Network Device Education Foundation, Inc. ("NetDEF") +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_bfd_topo3.py: Test the FRR BFD daemon multi hop. +""" + +import os +import sys +import json +from functools import partial +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + + +class BFDTopo(Topo): + "Test topology builder" + + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # Create 4 routers + for routern in range(1, 5): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["r4"]) + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(BFDTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + for rname, router in router_list.iteritems(): + daemon_file = "{}/{}/bfdd.conf".format(CWD, rname) + if os.path.isfile(daemon_file): + router.load_config(TopoRouter.RD_BFD, daemon_file) + + daemon_file = "{}/{}/zebra.conf".format(CWD, rname) + if os.path.isfile(daemon_file): + router.load_config(TopoRouter.RD_ZEBRA, daemon_file) + + daemon_file = "{}/{}/bgpd.conf".format(CWD, rname) + if os.path.isfile(daemon_file): + router.load_config(TopoRouter.RD_BGP, daemon_file) + + # Initialize all routers. + tgen.start_router() + + +def test_wait_bgp_convergence(): + "Wait for BGP to converge" + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("waiting for protocols to converge") + + def expect_loopback_route(router, iptype, route, proto): + "Wait until route is present on RIB for protocol." + logger.info('waiting route {} in {}'.format(route, router)) + test_func = partial( + topotest.router_json_cmp, + tgen.gears[router], + 'show {} route json'.format(iptype), + { route: [{ 'protocol': proto }] } + ) + _, result = topotest.run_and_expect(test_func, None, count=130, wait=1) + assertmsg = '"{}" OSPF convergence failure'.format(router) + assert result is None, assertmsg + + # Wait for R1 <-> R2 convergence. + expect_loopback_route('r1', 'ip', '10.254.254.2/32', 'bgp') + # Wait for R1 <-> R3 convergence. + expect_loopback_route('r1', 'ip', '10.254.254.3/32', 'bgp') + # Wait for R1 <-> R4 convergence. + expect_loopback_route('r1', 'ip', '10.254.254.4/32', 'bgp') + + # Wait for R2 <-> R1 convergence. + expect_loopback_route('r2', 'ip', '10.254.254.1/32', 'bgp') + # Wait for R2 <-> R3 convergence. + expect_loopback_route('r2', 'ip', '10.254.254.3/32', 'bgp') + # Wait for R2 <-> R4 convergence. + expect_loopback_route('r2', 'ip', '10.254.254.4/32', 'bgp') + + # Wait for R3 <-> R1 convergence. + expect_loopback_route('r3', 'ip', '10.254.254.1/32', 'bgp') + # Wait for R3 <-> R2 convergence. + expect_loopback_route('r3', 'ip', '10.254.254.2/32', 'bgp') + # Wait for R3 <-> R4 convergence. + expect_loopback_route('r3', 'ip', '10.254.254.4/32', 'bgp') + + # Wait for R4 <-> R1 convergence. + expect_loopback_route('r4', 'ip', '10.254.254.1/32', 'bgp') + # Wait for R4 <-> R2 convergence. + expect_loopback_route('r4', 'ip', '10.254.254.2/32', 'bgp') + # Wait for R4 <-> R3 convergence. + expect_loopback_route('r4', 'ip', '10.254.254.3/32', 'bgp') + + +def test_wait_bfd_convergence(): + "Wait for BFD to converge" + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("test BFD configurations") + + def expect_bfd_configuration(router): + "Load JSON file and compare with 'show bfd peer json'" + logger.info('waiting BFD configuration on router {}'.format(router)) + bfd_config = json.loads(open('{}/{}/bfd-peers.json'.format(CWD, router)).read()) + test_func = partial( + topotest.router_json_cmp, + tgen.gears[router], + 'show bfd peers json', + bfd_config + ) + _, result = topotest.run_and_expect(test_func, None, count=130, wait=1) + assertmsg = '"{}" BFD configuration failure'.format(router) + assert result is None, assertmsg + + expect_bfd_configuration('r1') + expect_bfd_configuration('r2') + expect_bfd_configuration('r3') + expect_bfd_configuration('r4') + + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + tgen.stop_topology() + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bfd-vrf-topo1/__init__.py b/tests/topotests/bfd-vrf-topo1/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bfd-vrf-topo1/r1/bfdd.conf b/tests/topotests/bfd-vrf-topo1/r1/bfdd.conf new file mode 100644 index 0000000000..5e736a7fcc --- /dev/null +++ b/tests/topotests/bfd-vrf-topo1/r1/bfdd.conf @@ -0,0 +1,11 @@ +! +debug bfd network +debug bfd peer +debug bfd zebra +! +bfd + peer 192.168.0.2 vrf r1-cust1 + echo-mode + no shutdown + ! +! diff --git a/tests/topotests/bfd-vrf-topo1/r1/bgp_prefixes.json b/tests/topotests/bfd-vrf-topo1/r1/bgp_prefixes.json new file mode 100644 index 0000000000..1262f5e984 --- /dev/null +++ b/tests/topotests/bfd-vrf-topo1/r1/bgp_prefixes.json @@ -0,0 +1,52 @@ +{ + "routes": { + "10.254.254.2/32": [ + { + "path": "102", + "prefix": "10.254.254.2", + "valid": true, + "peerId": "192.168.0.2", + "prefixLen": 32, + "nexthops": [ + { + "ip": "192.168.0.2", + "used": true, + "afi": "ipv4" + } + ] + } + ], + "10.254.254.3/32": [ + { + "path": "102 103", + "prefix": "10.254.254.3", + "valid": true, + "peerId": "192.168.0.2", + "prefixLen": 32, + "nexthops": [ + { + "ip": "192.168.0.2", + "used": true, + "afi": "ipv4" + } + ] + } + ], + "10.254.254.4/32": [ + { + "path": "102 104", + "prefix": "10.254.254.4", + "valid": true, + "peerId": "192.168.0.2", + "prefixLen": 32, + "nexthops": [ + { + "ip": "192.168.0.2", + "used": true, + "afi": "ipv4" + } + ] + } + ] + } +} diff --git a/tests/topotests/bfd-vrf-topo1/r1/bgp_summary.json b/tests/topotests/bfd-vrf-topo1/r1/bgp_summary.json new file mode 100644 index 0000000000..fa07d60df9 --- /dev/null +++ b/tests/topotests/bfd-vrf-topo1/r1/bgp_summary.json @@ -0,0 +1,11 @@ +{ + "ipv4Unicast": { + "as": 101, + "peers": { + "192.168.0.2": { + "remoteAs": 102, + "state": "Established" + } + } + } +} diff --git a/tests/topotests/bfd-vrf-topo1/r1/bgpd.conf b/tests/topotests/bfd-vrf-topo1/r1/bgpd.conf new file mode 100644 index 0000000000..439c58fb2a --- /dev/null +++ b/tests/topotests/bfd-vrf-topo1/r1/bgpd.conf @@ -0,0 +1,10 @@ +router bgp 101 vrf r1-cust1 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.0.2 remote-as 102 +! neighbor 192.168.0.2 ebgp-multihop 10 + neighbor 192.168.0.2 bfd + address-family ipv4 unicast + network 10.254.254.1/32 + exit-address-family +! diff --git a/tests/topotests/bfd-vrf-topo1/r1/peers.json b/tests/topotests/bfd-vrf-topo1/r1/peers.json new file mode 100644 index 0000000000..f49768ff75 --- /dev/null +++ b/tests/topotests/bfd-vrf-topo1/r1/peers.json @@ -0,0 +1,8 @@ +[ + { + "remote-receive-interval": 1000, + "remote-transmit-interval": 500, + "peer": "192.168.0.2", + "status": "up" + } +] diff --git a/tests/topotests/bfd-vrf-topo1/r1/zebra.conf b/tests/topotests/bfd-vrf-topo1/r1/zebra.conf new file mode 100644 index 0000000000..fcd1e7db17 --- /dev/null +++ b/tests/topotests/bfd-vrf-topo1/r1/zebra.conf @@ -0,0 +1,3 @@ +interface r1-eth0 vrf r1-cust1 + ip address 192.168.0.1/24 +! diff --git a/tests/topotests/bfd-vrf-topo1/r2/bfdd.conf b/tests/topotests/bfd-vrf-topo1/r2/bfdd.conf new file mode 100644 index 0000000000..94f502c7d9 --- /dev/null +++ b/tests/topotests/bfd-vrf-topo1/r2/bfdd.conf @@ -0,0 +1,17 @@ +! +debug bfd network +debug bfd peer +debug bfd zebra +! +bfd + peer 192.168.0.1 vrf r2-cust1 + receive-interval 1000 + transmit-interval 500 + echo-mode + no shutdown + ! + peer 192.168.1.1 vrf r2-cust1 + echo-mode + no shutdown + ! +! diff --git a/tests/topotests/bfd-vrf-topo1/r2/bgp_prefixes.json b/tests/topotests/bfd-vrf-topo1/r2/bgp_prefixes.json new file mode 100644 index 0000000000..0d47c0fc30 --- /dev/null +++ b/tests/topotests/bfd-vrf-topo1/r2/bgp_prefixes.json @@ -0,0 +1,52 @@ +{ + "routes": { + "10.254.254.1/32": [ + { + "path": "101", + "prefix": "10.254.254.1", + "valid": true, + "peerId": "192.168.0.1", + "prefixLen": 32, + "nexthops": [ + { + "ip": "192.168.0.1", + "used": true, + "afi": "ipv4" + } + ] + } + ], + "10.254.254.3/32": [ + { + "path": "103", + "prefix": "10.254.254.3", + "valid": true, + "peerId": "192.168.1.1", + "prefixLen": 32, + "nexthops": [ + { + "ip": "192.168.1.1", + "used": true, + "afi": "ipv4" + } + ] + } + ], + "10.254.254.4/32": [ + { + "path": "104", + "prefix": "10.254.254.4", + "valid": true, + "peerId": "192.168.2.1", + "prefixLen": 32, + "nexthops": [ + { + "ip": "192.168.2.1", + "used": true, + "afi": "ipv4" + } + ] + } + ] + } +} diff --git a/tests/topotests/bfd-vrf-topo1/r2/bgp_summary.json b/tests/topotests/bfd-vrf-topo1/r2/bgp_summary.json new file mode 100644 index 0000000000..c0ef11ac5f --- /dev/null +++ b/tests/topotests/bfd-vrf-topo1/r2/bgp_summary.json @@ -0,0 +1,19 @@ +{ + "ipv4Unicast": { + "as": 102, + "peers": { + "192.168.0.1": { + "remoteAs": 101, + "state": "Established" + }, + "192.168.1.1": { + "remoteAs": 103, + "state": "Established" + }, + "192.168.2.1": { + "remoteAs": 104, + "state": "Established" + } + } + } +} diff --git a/tests/topotests/bfd-vrf-topo1/r2/bgpd.conf b/tests/topotests/bfd-vrf-topo1/r2/bgpd.conf new file mode 100644 index 0000000000..4fac25d7bb --- /dev/null +++ b/tests/topotests/bfd-vrf-topo1/r2/bgpd.conf @@ -0,0 +1,13 @@ +router bgp 102 vrf r2-cust1 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.0.1 remote-as 101 + neighbor 192.168.0.1 bfd + neighbor 192.168.1.1 remote-as 103 + neighbor 192.168.1.1 bfd + neighbor 192.168.2.1 remote-as 104 + neighbor 192.168.2.1 bfd + address-family ipv4 unicast + network 10.254.254.2/32 + exit-address-family +! diff --git a/tests/topotests/bfd-vrf-topo1/r2/peers.json b/tests/topotests/bfd-vrf-topo1/r2/peers.json new file mode 100644 index 0000000000..5035d643c5 --- /dev/null +++ b/tests/topotests/bfd-vrf-topo1/r2/peers.json @@ -0,0 +1,17 @@ +[ + { + "peer": "192.168.0.1", + "status": "up" + }, + { + "remote-echo-interval": 100, + "peer": "192.168.1.1", + "status": "up" + }, + { + "remote-transmit-interval": 2000, + "remote-receive-interval": 2000, + "peer": "192.168.2.1", + "status": "up" + } +] diff --git a/tests/topotests/bfd-vrf-topo1/r2/zebra.conf b/tests/topotests/bfd-vrf-topo1/r2/zebra.conf new file mode 100644 index 0000000000..daffd1912e --- /dev/null +++ b/tests/topotests/bfd-vrf-topo1/r2/zebra.conf @@ -0,0 +1,9 @@ +interface r2-eth0 vrf r2-cust1 + ip address 192.168.0.2/24 +! +interface r2-eth1 vrf r2-cust1 + ip address 192.168.1.2/24 +! +interface r2-eth2 vrf r2-cust1 + ip address 192.168.2.2/24 +! diff --git a/tests/topotests/bfd-vrf-topo1/r3/bfdd.conf b/tests/topotests/bfd-vrf-topo1/r3/bfdd.conf new file mode 100644 index 0000000000..76910ac927 --- /dev/null +++ b/tests/topotests/bfd-vrf-topo1/r3/bfdd.conf @@ -0,0 +1,12 @@ +! +debug bfd network +debug bfd peer +debug bfd zebra +! +bfd + peer 192.168.1.2 vrf r3-cust1 + echo-interval 100 + echo-mode + no shutdown + ! +! diff --git a/tests/topotests/bfd-vrf-topo1/r3/bgp_prefixes.json b/tests/topotests/bfd-vrf-topo1/r3/bgp_prefixes.json new file mode 100644 index 0000000000..36fca17bbf --- /dev/null +++ b/tests/topotests/bfd-vrf-topo1/r3/bgp_prefixes.json @@ -0,0 +1,52 @@ +{ + "routes": { + "10.254.254.1/32": [ + { + "path": "102 101", + "prefix": "10.254.254.1", + "valid": true, + "peerId": "192.168.1.2", + "prefixLen": 32, + "nexthops": [ + { + "ip": "192.168.1.2", + "used": true, + "afi": "ipv4" + } + ] + } + ], + "10.254.254.2/32": [ + { + "path": "102", + "prefix": "10.254.254.2", + "valid": true, + "peerId": "192.168.1.2", + "prefixLen": 32, + "nexthops": [ + { + "ip": "192.168.1.2", + "used": true, + "afi": "ipv4" + } + ] + } + ], + "10.254.254.4/32": [ + { + "path": "102 104", + "prefix": "10.254.254.4", + "valid": true, + "peerId": "192.168.1.2", + "prefixLen": 32, + "nexthops": [ + { + "ip": "192.168.1.2", + "used": true, + "afi": "ipv4" + } + ] + } + ] + } +} diff --git a/tests/topotests/bfd-vrf-topo1/r3/bgp_summary.json b/tests/topotests/bfd-vrf-topo1/r3/bgp_summary.json new file mode 100644 index 0000000000..d47833377b --- /dev/null +++ b/tests/topotests/bfd-vrf-topo1/r3/bgp_summary.json @@ -0,0 +1,11 @@ +{ + "ipv4Unicast": { + "as": 103, + "peers": { + "192.168.1.2": { + "remoteAs": 102, + "state": "Established" + } + } + } +} diff --git a/tests/topotests/bfd-vrf-topo1/r3/bgpd.conf b/tests/topotests/bfd-vrf-topo1/r3/bgpd.conf new file mode 100644 index 0000000000..052707ae1b --- /dev/null +++ b/tests/topotests/bfd-vrf-topo1/r3/bgpd.conf @@ -0,0 +1,9 @@ +router bgp 103 vrf r3-cust1 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.1.2 remote-as 102 + neighbor 192.168.1.2 bfd + address-family ipv4 unicast + network 10.254.254.3/32 + exit-address-family +! diff --git a/tests/topotests/bfd-vrf-topo1/r3/peers.json b/tests/topotests/bfd-vrf-topo1/r3/peers.json new file mode 100644 index 0000000000..ef38008643 --- /dev/null +++ b/tests/topotests/bfd-vrf-topo1/r3/peers.json @@ -0,0 +1,6 @@ +[ + { + "peer": "192.168.1.2", + "status": "up" + } +] diff --git a/tests/topotests/bfd-vrf-topo1/r3/zebra.conf b/tests/topotests/bfd-vrf-topo1/r3/zebra.conf new file mode 100644 index 0000000000..f727c2d633 --- /dev/null +++ b/tests/topotests/bfd-vrf-topo1/r3/zebra.conf @@ -0,0 +1,3 @@ +interface r3-eth0 vrf r3-cust1 + ip address 192.168.1.1/24 +! diff --git a/tests/topotests/bfd-vrf-topo1/r4/bfdd.conf b/tests/topotests/bfd-vrf-topo1/r4/bfdd.conf new file mode 100644 index 0000000000..63d0da7805 --- /dev/null +++ b/tests/topotests/bfd-vrf-topo1/r4/bfdd.conf @@ -0,0 +1,12 @@ +! +debug bfd network +debug bfd peer +debug bfd zebra +! +bfd + peer 192.168.2.2 vrf r4-cust1 + transmit-interval 2000 + receive-interval 2000 + no shutdown + ! +! diff --git a/tests/topotests/bfd-vrf-topo1/r4/bgp_prefixes.json b/tests/topotests/bfd-vrf-topo1/r4/bgp_prefixes.json new file mode 100644 index 0000000000..efe7d47b1a --- /dev/null +++ b/tests/topotests/bfd-vrf-topo1/r4/bgp_prefixes.json @@ -0,0 +1,52 @@ +{ + "routes": { + "10.254.254.1/32": [ + { + "path": "102 101", + "prefix": "10.254.254.1", + "valid": true, + "peerId": "192.168.2.2", + "prefixLen": 32, + "nexthops": [ + { + "ip": "192.168.2.2", + "used": true, + "afi": "ipv4" + } + ] + } + ], + "10.254.254.2/32": [ + { + "path": "102", + "prefix": "10.254.254.2", + "valid": true, + "peerId": "192.168.2.2", + "prefixLen": 32, + "nexthops": [ + { + "ip": "192.168.2.2", + "used": true, + "afi": "ipv4" + } + ] + } + ], + "10.254.254.3/32": [ + { + "path": "102 103", + "prefix": "10.254.254.3", + "valid": true, + "peerId": "192.168.2.2", + "prefixLen": 32, + "nexthops": [ + { + "ip": "192.168.2.2", + "used": true, + "afi": "ipv4" + } + ] + } + ] + } +} diff --git a/tests/topotests/bfd-vrf-topo1/r4/bgp_summary.json b/tests/topotests/bfd-vrf-topo1/r4/bgp_summary.json new file mode 100644 index 0000000000..7d81784b56 --- /dev/null +++ b/tests/topotests/bfd-vrf-topo1/r4/bgp_summary.json @@ -0,0 +1,11 @@ +{ + "ipv4Unicast": { + "as": 104, + "peers": { + "192.168.2.2": { + "remoteAs": 102, + "state": "Established" + } + } + } +} diff --git a/tests/topotests/bfd-vrf-topo1/r4/bgpd.conf b/tests/topotests/bfd-vrf-topo1/r4/bgpd.conf new file mode 100644 index 0000000000..bcb0a17c01 --- /dev/null +++ b/tests/topotests/bfd-vrf-topo1/r4/bgpd.conf @@ -0,0 +1,9 @@ +router bgp 104 vrf r4-cust1 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.2.2 remote-as 102 + neighbor 192.168.2.2 bfd + address-family ipv4 unicast + network 10.254.254.4/32 + exit-address-family +! diff --git a/tests/topotests/bfd-vrf-topo1/r4/peers.json b/tests/topotests/bfd-vrf-topo1/r4/peers.json new file mode 100644 index 0000000000..37140089e1 --- /dev/null +++ b/tests/topotests/bfd-vrf-topo1/r4/peers.json @@ -0,0 +1,6 @@ +[ + { + "peer": "192.168.2.2", + "status": "up" + } +] diff --git a/tests/topotests/bfd-vrf-topo1/r4/zebra.conf b/tests/topotests/bfd-vrf-topo1/r4/zebra.conf new file mode 100644 index 0000000000..69770dd2bf --- /dev/null +++ b/tests/topotests/bfd-vrf-topo1/r4/zebra.conf @@ -0,0 +1,3 @@ +interface r4-eth0 vrf r4-cust1 + ip address 192.168.2.1/24 +! diff --git a/tests/topotests/bfd-vrf-topo1/test_bfd_vrf_topo1.dot b/tests/topotests/bfd-vrf-topo1/test_bfd_vrf_topo1.dot new file mode 100644 index 0000000000..c84ace2780 --- /dev/null +++ b/tests/topotests/bfd-vrf-topo1/test_bfd_vrf_topo1.dot @@ -0,0 +1,73 @@ +## Color coding: +######################### +## Main FRR: #f08080 red +## Switches: #d0e0d0 gray +## RIP: #19e3d9 Cyan +## RIPng: #fcb314 dark yellow +## OSPFv2: #32b835 Green +## OSPFv3: #19e3d9 Cyan +## ISIS IPv4 #fcb314 dark yellow +## ISIS IPv6 #9a81ec purple +## BGP IPv4 #eee3d3 beige +## BGP IPv6 #fdff00 yellow +##### Colors (see http://www.color-hex.com/) + +graph template { + label="bfd-topo1"; + + # Routers + r1 [ + shape=doubleoctagon, + label="r1", + fillcolor="#f08080", + style=filled, + ]; + r2 [ + shape=doubleoctagon + label="r2", + fillcolor="#f08080", + style=filled, + ]; + r3 [ + shape=doubleoctagon + label="r3", + fillcolor="#f08080", + style=filled, + ]; + r4 [ + shape=doubleoctagon + label="r4", + fillcolor="#f08080", + style=filled, + ]; + + # Switches + sw1 [ + shape=oval, + label="sw1\n192.168.0.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + sw2 [ + shape=oval, + label="sw2\n192.168.1.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + sw3 [ + shape=oval, + label="sw3\n192.168.2.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + + # Connections + r1 -- sw1 [label="eth0\n.1"]; + r2 -- sw1 [label="eth0\n.2"]; + + r3 -- sw2 [label="eth0\n.1"]; + r2 -- sw2 [label="eth1\n.2"]; + + r4 -- sw3 [label="eth0\n.1"]; + r2 -- sw3 [label="eth2\n.2"]; +} diff --git a/tests/topotests/bfd-vrf-topo1/test_bfd_vrf_topo1.jpg b/tests/topotests/bfd-vrf-topo1/test_bfd_vrf_topo1.jpg new file mode 100644 index 0000000000..4d6d56e072 Binary files /dev/null and b/tests/topotests/bfd-vrf-topo1/test_bfd_vrf_topo1.jpg differ diff --git a/tests/topotests/bfd-vrf-topo1/test_bfd_vrf_topo1.py b/tests/topotests/bfd-vrf-topo1/test_bfd_vrf_topo1.py new file mode 100755 index 0000000000..b1f755ad06 --- /dev/null +++ b/tests/topotests/bfd-vrf-topo1/test_bfd_vrf_topo1.py @@ -0,0 +1,306 @@ +#!/usr/bin/env python + +# +# test_bfd_vrf_topo1.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2018 by +# Network Device Education Foundation, Inc. ("NetDEF") +# Copyright (c) 2019 by 6WIND +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_bfd_vrf_topo1.py: Test the FRR/Quagga BFD daemon. +""" + +import os +import sys +import json +from functools import partial +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + + +class BFDTopo(Topo): + "Test topology builder" + + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # Create 4 routers + for routern in range(1, 5): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r4"]) + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(BFDTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + # check for zebra capability + for rname, router in router_list.iteritems(): + if router.check_capability(TopoRouter.RD_ZEBRA, "--vrfwnetns") == False: + return pytest.skip( + "Skipping BFD Topo1 VRF NETNS feature. VRF NETNS backend not available on FRR" + ) + + if os.system("ip netns list") != 0: + return pytest.skip( + "Skipping BFD Topo1 VRF NETNS Test. NETNS not available on System" + ) + + logger.info("Testing with VRF Namespace support") + + cmds = [ + "if [ -e /var/run/netns/{0}-cust1 ] ; then ip netns del {0}-cust1 ; fi", + "ip netns add {0}-cust1", + "ip link set dev {0}-eth0 netns {0}-cust1", + "ip netns exec {0}-cust1 ifconfig {0}-eth0 up", + ] + cmds2 = [ + "ip link set dev {0}-eth1 netns {0}-cust1", + "ip netns exec {0}-cust1 ifconfig {0}-eth1 up", + "ip link set dev {0}-eth2 netns {0}-cust1", + "ip netns exec {0}-cust1 ifconfig {0}-eth2 up", + ] + + for rname, router in router_list.iteritems(): + # create VRF rx-cust1 and link rx-eth0 to rx-cust1 + for cmd in cmds: + output = tgen.net[rname].cmd(cmd.format(rname)) + if rname == "r2": + for cmd in cmds2: + output = tgen.net[rname].cmd(cmd.format(rname)) + + for rname, router in router_list.iteritems(): + router.load_config( + TopoRouter.RD_ZEBRA, + os.path.join(CWD, "{}/zebra.conf".format(rname)), + "--vrfwnetns", + ) + router.load_config( + TopoRouter.RD_BFD, os.path.join(CWD, "{}/bfdd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + # Initialize all routers. + tgen.start_router() + + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + # move back rx-eth0 to default VRF + # delete rx-vrf + cmds = [ + "ip netns exec {0}-cust1 ip link set {0}-eth0 netns 1", + "ip netns delete {0}-cust1", + ] + cmds2 = [ + "ip netns exec {0}-cust1 ip link set {0}-eth1 netns 1", + "ip netns exec {0}-cust2 ip link set {0}-eth1 netns 1", + ] + + router_list = tgen.routers() + for rname, router in router_list.iteritems(): + if rname == "r2": + for cmd in cmds2: + tgen.net[rname].cmd(cmd.format(rname)) + for cmd in cmds: + tgen.net[rname].cmd(cmd.format(rname)) + tgen.stop_topology() + + +def test_bfd_connection(): + "Assert that the BFD peers can find themselves." + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("waiting for bfd peers to go up") + for router in tgen.routers().values(): + json_file = "{}/{}/peers.json".format(CWD, router.name) + expected = json.loads(open(json_file).read()) + + test_func = partial( + topotest.router_json_cmp, router, "show bfd peers json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=16, wait=1) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + +def test_bgp_convergence(): + "Assert that BGP is converging." + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("waiting for bgp peers to go up") + + for router in tgen.routers().values(): + ref_file = "{}/{}/bgp_summary.json".format(CWD, router.name) + expected = json.loads(open(ref_file).read()) + test_func = partial( + topotest.router_json_cmp, + router, + "show ip bgp vrf {}-cust1 summary json".format(router.name), + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=125, wait=1.0) + assertmsg = "{}: bgp did not converge".format(router.name) + assert res is None, assertmsg + + +def test_bgp_fast_convergence(): + "Assert that BGP is converging before setting a link down." + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("waiting for bgp peers converge") + + for router in tgen.routers().values(): + ref_file = "{}/{}/bgp_prefixes.json".format(CWD, router.name) + expected = json.loads(open(ref_file).read()) + test_func = partial( + topotest.router_json_cmp, + router, + "show ip bgp vrf {}-cust1 json".format(router.name), + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=40, wait=1) + assertmsg = "{}: bgp did not converge".format(router.name) + assert res is None, assertmsg + + +def test_bfd_fast_convergence(): + """ + Assert that BFD notices the link down after simulating network + failure. + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Disable r2-eth0 link + router2 = tgen.gears["r2"] + topotest.interface_set_status( + router2, "r2-eth0", ifaceaction=False, vrf_name="r2-cust1" + ) + + # Wait the minimum time we can before checking that BGP/BFD + # converged. + logger.info("waiting for BFD converge") + + # Check that BGP converged quickly. + for router in tgen.routers().values(): + json_file = "{}/{}/peers.json".format(CWD, router.name) + expected = json.loads(open(json_file).read()) + + # Load the same file as previous test, but expect R1 to be down. + if router.name == "r1": + for peer in expected: + if peer["peer"] == "192.168.0.2": + peer["status"] = "down" + else: + for peer in expected: + if peer["peer"] == "192.168.0.1": + peer["status"] = "down" + + test_func = partial( + topotest.router_json_cmp, router, "show bfd peers json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=40, wait=1) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert res is None, assertmsg + + +def test_bgp_fast_reconvergence(): + "Assert that BGP is converging after setting a link down." + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("waiting for BGP re convergence") + + # Check that BGP converged quickly. + for router in tgen.routers().values(): + ref_file = "{}/{}/bgp_prefixes.json".format(CWD, router.name) + expected = json.loads(open(ref_file).read()) + + # Load the same file as previous test, but set networks to None + # to test absence. + if router.name == "r1": + expected["routes"]["10.254.254.2/32"] = None + expected["routes"]["10.254.254.3/32"] = None + expected["routes"]["10.254.254.4/32"] = None + else: + expected["routes"]["10.254.254.1/32"] = None + + test_func = partial( + topotest.router_json_cmp, + router, + "show ip bgp vrf {}-cust1 json".format(router.name), + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=16, wait=1) + assertmsg = "{}: bgp did not converge".format(router.name) + assert res is None, assertmsg + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp-auth/R1/bgpd.conf b/tests/topotests/bgp-auth/R1/bgpd.conf new file mode 100644 index 0000000000..1cb26c6537 --- /dev/null +++ b/tests/topotests/bgp-auth/R1/bgpd.conf @@ -0,0 +1,18 @@ +router bgp 65001 + timers bgp 3 9 + bgp router-id 1.1.1.1 + neighbor 2.2.2.2 remote-as 65002 + neighbor 2.2.2.2 update-source lo + neighbor 2.2.2.2 ebgp-multihop 3 + neighbor 2.2.2.2 password hello1 + neighbor 2.2.2.2 timers 3 10 + neighbor 2.2.2.2 timers connect 10 + neighbor 3.3.3.3 remote-as 65003 + neighbor 3.3.3.3 update-source lo + neighbor 3.3.3.3 ebgp-multihop 3 + neighbor 3.3.3.3 password hello2 + neighbor 3.3.3.3 timers 3 10 + neighbor 3.3.3.3 timers connect 10 + address-family ipv4 unicast + neighbor 2.2.2.2 activate + neighbor 3.3.3.3 activate diff --git a/tests/topotests/bgp-auth/R1/bgpd_multi_vrf.conf b/tests/topotests/bgp-auth/R1/bgpd_multi_vrf.conf new file mode 100644 index 0000000000..aab35073cf --- /dev/null +++ b/tests/topotests/bgp-auth/R1/bgpd_multi_vrf.conf @@ -0,0 +1,40 @@ +log file /tmp/topotests/test_bgp_auth/R1/bgpd.log debugging +debug bgp neighbor-events + +router bgp 65001 vrf blue + timers bgp 3 9 + bgp router-id 1.1.1.1 + neighbor 2.2.2.2 remote-as 65002 + neighbor 2.2.2.2 update-source lo1 + neighbor 2.2.2.2 ebgp-multihop 3 + neighbor 2.2.2.2 timers 3 10 + neighbor 2.2.2.2 timers connect 10 + neighbor 2.2.2.2 password blue1 + neighbor 3.3.3.3 remote-as 65003 + neighbor 3.3.3.3 update-source lo1 + neighbor 3.3.3.3 ebgp-multihop 3 + neighbor 3.3.3.3 timers 3 10 + neighbor 3.3.3.3 timers connect 10 + neighbor 3.3.3.3 password blue2 + address-family ipv4 unicast + neighbor 2.2.2.2 activate + neighbor 3.3.3.3 activate + +router bgp 65001 vrf red + timers bgp 3 9 + bgp router-id 1.1.1.1 + neighbor 2.2.2.2 remote-as 65002 + neighbor 2.2.2.2 update-source lo2 + neighbor 2.2.2.2 ebgp-multihop 3 + neighbor 2.2.2.2 timers 3 10 + neighbor 2.2.2.2 timers connect 10 + neighbor 2.2.2.2 password red1 + neighbor 3.3.3.3 remote-as 65003 + neighbor 3.3.3.3 update-source lo2 + neighbor 3.3.3.3 ebgp-multihop 3 + neighbor 3.3.3.3 timers 3 10 + neighbor 3.3.3.3 timers connect 10 + neighbor 3.3.3.3 password red2 + address-family ipv4 unicast + neighbor 2.2.2.2 activate + neighbor 3.3.3.3 activate diff --git a/tests/topotests/bgp-auth/R1/bgpd_multi_vrf_prefix.conf b/tests/topotests/bgp-auth/R1/bgpd_multi_vrf_prefix.conf new file mode 100644 index 0000000000..7e15720c7e --- /dev/null +++ b/tests/topotests/bgp-auth/R1/bgpd_multi_vrf_prefix.conf @@ -0,0 +1,37 @@ +router bgp 65001 vrf blue + timers bgp 3 9 + bgp router-id 1.1.1.1 + neighbor TWO_GROUP_blue peer-group + neighbor TWO_GROUP_blue remote-as 65002 + neighbor TWO_GROUP_blue update-source 1.1.1.1 + neighbor TWO_GROUP_blue ebgp-multihop 3 + neighbor TWO_GROUP_blue password blue1 + neighbor THREE_GROUP_blue peer-group + neighbor THREE_GROUP_blue remote-as 65003 + neighbor THREE_GROUP_blue update-source 1.1.1.1 + neighbor THREE_GROUP_blue ebgp-multihop 3 + neighbor THREE_GROUP_blue password blue2 + bgp listen range 2.2.2.0/24 peer-group TWO_GROUP_blue + bgp listen range 3.3.3.0/24 peer-group THREE_GROUP_blue + address-family ipv4 unicast + neighbor TWO_GROUP_blue maximum-prefix 4294967295 + neighbor THREE_GROUP_blue maximum-prefix 4294967295 + +router bgp 65001 vrf red + timers bgp 3 9 + bgp router-id 1.1.1.1 + neighbor TWO_GROUP_red peer-group + neighbor TWO_GROUP_red remote-as 65002 + neighbor TWO_GROUP_red update-source 1.1.1.1 + neighbor TWO_GROUP_red ebgp-multihop 3 + neighbor TWO_GROUP_red password red1 + neighbor THREE_GROUP_red peer-group + neighbor THREE_GROUP_red remote-as 65003 + neighbor THREE_GROUP_red update-source 1.1.1.1 + neighbor THREE_GROUP_red ebgp-multihop 3 + neighbor THREE_GROUP_red password red2 + bgp listen range 2.2.2.0/24 peer-group TWO_GROUP_red + bgp listen range 3.3.3.0/24 peer-group THREE_GROUP_red + address-family ipv4 unicast + neighbor TWO_GROUP_red maximum-prefix 4294967295 + neighbor THREE_GROUP_red maximum-prefix 4294967295 diff --git a/tests/topotests/bgp-auth/R1/bgpd_prefix.conf b/tests/topotests/bgp-auth/R1/bgpd_prefix.conf new file mode 100644 index 0000000000..9200b0501d --- /dev/null +++ b/tests/topotests/bgp-auth/R1/bgpd_prefix.conf @@ -0,0 +1,18 @@ +router bgp 65001 + timers bgp 3 9 + bgp router-id 1.1.1.1 + neighbor TWO_GROUP peer-group + neighbor TWO_GROUP remote-as 65002 + neighbor TWO_GROUP update-source 1.1.1.1 + neighbor TWO_GROUP ebgp-multihop 3 + neighbor TWO_GROUP password hello1 + neighbor THREE_GROUP peer-group + neighbor THREE_GROUP remote-as 65003 + neighbor THREE_GROUP update-source 1.1.1.1 + neighbor THREE_GROUP ebgp-multihop 3 + neighbor THREE_GROUP password hello2 + bgp listen range 2.2.2.0/24 peer-group TWO_GROUP + bgp listen range 3.3.3.0/24 peer-group THREE_GROUP + address-family ipv4 unicast + neighbor TWO_GROUP maximum-prefix 4294967295 + neighbor THREE_GROUP maximum-prefix 4294967295 diff --git a/tests/topotests/bgp-auth/R1/bgpd_vrf.conf b/tests/topotests/bgp-auth/R1/bgpd_vrf.conf new file mode 100644 index 0000000000..73aa8c1a03 --- /dev/null +++ b/tests/topotests/bgp-auth/R1/bgpd_vrf.conf @@ -0,0 +1,21 @@ +log file /tmp/topotests/test_bgp_auth/R1/bgpd.log debugging +debug bgp neighbor-events + +router bgp 65001 vrf blue + timers bgp 3 9 + bgp router-id 1.1.1.1 + neighbor 2.2.2.2 remote-as 65002 + neighbor 2.2.2.2 update-source lo1 + neighbor 2.2.2.2 ebgp-multihop 3 + neighbor 2.2.2.2 timers 3 10 + neighbor 2.2.2.2 timers connect 10 + neighbor 2.2.2.2 password hello1 + neighbor 3.3.3.3 remote-as 65003 + neighbor 3.3.3.3 update-source lo1 + neighbor 3.3.3.3 ebgp-multihop 3 + neighbor 3.3.3.3 timers 3 10 + neighbor 3.3.3.3 timers connect 10 + neighbor 3.3.3.3 password hello2 + address-family ipv4 unicast + neighbor 2.2.2.2 activate + neighbor 3.3.3.3 activate diff --git a/tests/topotests/bgp-auth/R1/bgpd_vrf_prefix.conf b/tests/topotests/bgp-auth/R1/bgpd_vrf_prefix.conf new file mode 100644 index 0000000000..d68951b406 --- /dev/null +++ b/tests/topotests/bgp-auth/R1/bgpd_vrf_prefix.conf @@ -0,0 +1,18 @@ +router bgp 65001 vrf blue + timers bgp 3 9 + bgp router-id 1.1.1.1 + neighbor TWO_GROUP_blue peer-group + neighbor TWO_GROUP_blue remote-as 65002 + neighbor TWO_GROUP_blue update-source 1.1.1.1 + neighbor TWO_GROUP_blue ebgp-multihop 3 + neighbor TWO_GROUP_blue password hello1 + neighbor THREE_GROUP_blue peer-group + neighbor THREE_GROUP_blue remote-as 65003 + neighbor THREE_GROUP_blue update-source 1.1.1.1 + neighbor THREE_GROUP_blue ebgp-multihop 3 + neighbor THREE_GROUP_blue password hello2 + bgp listen range 2.2.2.0/24 peer-group TWO_GROUP_blue + bgp listen range 3.3.3.0/24 peer-group THREE_GROUP_blue + address-family ipv4 unicast + neighbor TWO_GROUP_blue maximum-prefix 4294967295 + neighbor THREE_GROUP_blue maximum-prefix 4294967295 diff --git a/tests/topotests/bgp-auth/R1/ospfd.conf b/tests/topotests/bgp-auth/R1/ospfd.conf new file mode 100644 index 0000000000..79eb0e33da --- /dev/null +++ b/tests/topotests/bgp-auth/R1/ospfd.conf @@ -0,0 +1,4 @@ +router ospf + network 10.10.0.0/16 area 0 + network 10.20.0.0/16 area 0 + network 1.1.1.1/32 area 0 diff --git a/tests/topotests/bgp-auth/R1/ospfd_multi_vrf.conf b/tests/topotests/bgp-auth/R1/ospfd_multi_vrf.conf new file mode 100644 index 0000000000..e2a28000b8 --- /dev/null +++ b/tests/topotests/bgp-auth/R1/ospfd_multi_vrf.conf @@ -0,0 +1,9 @@ +router ospf vrf blue + network 10.10.0.0/16 area 0 + network 10.20.0.0/16 area 0 + network 1.1.1.1/32 area 0 + +router ospf vrf red + network 10.10.0.0/16 area 0 + network 10.20.0.0/16 area 0 + network 1.1.1.1/32 area 0 diff --git a/tests/topotests/bgp-auth/R1/ospfd_vrf.conf b/tests/topotests/bgp-auth/R1/ospfd_vrf.conf new file mode 100644 index 0000000000..0b7fbae8c4 --- /dev/null +++ b/tests/topotests/bgp-auth/R1/ospfd_vrf.conf @@ -0,0 +1,4 @@ +router ospf vrf blue + network 10.10.0.0/16 area 0 + network 10.20.0.0/16 area 0 + network 1.1.1.1/32 area 0 diff --git a/tests/topotests/bgp-auth/R1/zebra.conf b/tests/topotests/bgp-auth/R1/zebra.conf new file mode 100644 index 0000000000..d39915335a --- /dev/null +++ b/tests/topotests/bgp-auth/R1/zebra.conf @@ -0,0 +1,21 @@ +log file zebra.log +! +interface lo + ip address 1.1.1.1/32 +interface lo1 vrf blue + ip address 1.1.1.1/32 +interface lo2 vrf red + ip address 1.1.1.1/32 +interface R1-eth0 + ip address 10.10.0.1/24 +interface R1-eth1 + ip address 10.20.0.1/24 +interface R1-eth2 vrf blue + ip address 10.10.0.1/24 +interface R1-eth3 vrf blue + ip address 10.20.0.1/24 +interface R1-eth4 vrf red + ip address 10.10.0.1/24 +interface R1-eth5 vrf red + ip address 10.20.0.1/24 +! \ No newline at end of file diff --git a/tests/topotests/bgp-auth/R2/bgpd.conf b/tests/topotests/bgp-auth/R2/bgpd.conf new file mode 100644 index 0000000000..fa2a570ef9 --- /dev/null +++ b/tests/topotests/bgp-auth/R2/bgpd.conf @@ -0,0 +1,18 @@ +router bgp 65002 + timers bgp 3 9 + bgp router-id 2.2.2.2 + neighbor 1.1.1.1 remote-as 65001 + neighbor 1.1.1.1 update-source lo + neighbor 1.1.1.1 ebgp-multihop 3 + neighbor 1.1.1.1 timers 3 10 + neighbor 1.1.1.1 timers connect 10 + neighbor 1.1.1.1 password hello1 + neighbor 3.3.3.3 remote-as 65003 + neighbor 3.3.3.3 update-source lo + neighbor 3.3.3.3 ebgp-multihop 3 + neighbor 3.3.3.3 timers 3 10 + neighbor 3.3.3.3 timers connect 10 + neighbor 3.3.3.3 password hello3 + address-family ipv4 unicast + neighbor 1.1.1.1 activate + neighbor 3.3.3.3 activate diff --git a/tests/topotests/bgp-auth/R2/bgpd_multi_vrf.conf b/tests/topotests/bgp-auth/R2/bgpd_multi_vrf.conf new file mode 100644 index 0000000000..d5f70edf68 --- /dev/null +++ b/tests/topotests/bgp-auth/R2/bgpd_multi_vrf.conf @@ -0,0 +1,37 @@ +router bgp 65002 vrf blue + timers bgp 3 9 + bgp router-id 2.2.2.2 + neighbor 1.1.1.1 remote-as 65001 + neighbor 1.1.1.1 update-source lo1 + neighbor 1.1.1.1 ebgp-multihop 3 + neighbor 1.1.1.1 timers 3 10 + neighbor 1.1.1.1 timers connect 10 + neighbor 1.1.1.1 password blue1 + neighbor 3.3.3.3 remote-as 65003 + neighbor 3.3.3.3 update-source lo1 + neighbor 3.3.3.3 ebgp-multihop 3 + neighbor 3.3.3.3 timers 3 10 + neighbor 3.3.3.3 timers connect 10 + neighbor 3.3.3.3 password blue3 + address-family ipv4 unicast + neighbor 1.1.1.1 activate + neighbor 3.3.3.3 activate + +router bgp 65002 vrf red + timers bgp 3 9 + bgp router-id 2.2.2.2 + neighbor 1.1.1.1 remote-as 65001 + neighbor 1.1.1.1 update-source lo2 + neighbor 1.1.1.1 ebgp-multihop 3 + neighbor 1.1.1.1 timers 3 10 + neighbor 1.1.1.1 timers connect 10 + neighbor 1.1.1.1 password red1 + neighbor 3.3.3.3 remote-as 65003 + neighbor 3.3.3.3 update-source lo2 + neighbor 3.3.3.3 ebgp-multihop 3 + neighbor 3.3.3.3 timers 3 10 + neighbor 3.3.3.3 timers connect 10 + neighbor 3.3.3.3 password red3 + address-family ipv4 unicast + neighbor 1.1.1.1 activate + neighbor 3.3.3.3 activate diff --git a/tests/topotests/bgp-auth/R2/bgpd_multi_vrf_prefix.conf b/tests/topotests/bgp-auth/R2/bgpd_multi_vrf_prefix.conf new file mode 100644 index 0000000000..d5f70edf68 --- /dev/null +++ b/tests/topotests/bgp-auth/R2/bgpd_multi_vrf_prefix.conf @@ -0,0 +1,37 @@ +router bgp 65002 vrf blue + timers bgp 3 9 + bgp router-id 2.2.2.2 + neighbor 1.1.1.1 remote-as 65001 + neighbor 1.1.1.1 update-source lo1 + neighbor 1.1.1.1 ebgp-multihop 3 + neighbor 1.1.1.1 timers 3 10 + neighbor 1.1.1.1 timers connect 10 + neighbor 1.1.1.1 password blue1 + neighbor 3.3.3.3 remote-as 65003 + neighbor 3.3.3.3 update-source lo1 + neighbor 3.3.3.3 ebgp-multihop 3 + neighbor 3.3.3.3 timers 3 10 + neighbor 3.3.3.3 timers connect 10 + neighbor 3.3.3.3 password blue3 + address-family ipv4 unicast + neighbor 1.1.1.1 activate + neighbor 3.3.3.3 activate + +router bgp 65002 vrf red + timers bgp 3 9 + bgp router-id 2.2.2.2 + neighbor 1.1.1.1 remote-as 65001 + neighbor 1.1.1.1 update-source lo2 + neighbor 1.1.1.1 ebgp-multihop 3 + neighbor 1.1.1.1 timers 3 10 + neighbor 1.1.1.1 timers connect 10 + neighbor 1.1.1.1 password red1 + neighbor 3.3.3.3 remote-as 65003 + neighbor 3.3.3.3 update-source lo2 + neighbor 3.3.3.3 ebgp-multihop 3 + neighbor 3.3.3.3 timers 3 10 + neighbor 3.3.3.3 timers connect 10 + neighbor 3.3.3.3 password red3 + address-family ipv4 unicast + neighbor 1.1.1.1 activate + neighbor 3.3.3.3 activate diff --git a/tests/topotests/bgp-auth/R2/bgpd_prefix.conf b/tests/topotests/bgp-auth/R2/bgpd_prefix.conf new file mode 100644 index 0000000000..fa2a570ef9 --- /dev/null +++ b/tests/topotests/bgp-auth/R2/bgpd_prefix.conf @@ -0,0 +1,18 @@ +router bgp 65002 + timers bgp 3 9 + bgp router-id 2.2.2.2 + neighbor 1.1.1.1 remote-as 65001 + neighbor 1.1.1.1 update-source lo + neighbor 1.1.1.1 ebgp-multihop 3 + neighbor 1.1.1.1 timers 3 10 + neighbor 1.1.1.1 timers connect 10 + neighbor 1.1.1.1 password hello1 + neighbor 3.3.3.3 remote-as 65003 + neighbor 3.3.3.3 update-source lo + neighbor 3.3.3.3 ebgp-multihop 3 + neighbor 3.3.3.3 timers 3 10 + neighbor 3.3.3.3 timers connect 10 + neighbor 3.3.3.3 password hello3 + address-family ipv4 unicast + neighbor 1.1.1.1 activate + neighbor 3.3.3.3 activate diff --git a/tests/topotests/bgp-auth/R2/bgpd_vrf.conf b/tests/topotests/bgp-auth/R2/bgpd_vrf.conf new file mode 100644 index 0000000000..d1f3847420 --- /dev/null +++ b/tests/topotests/bgp-auth/R2/bgpd_vrf.conf @@ -0,0 +1,18 @@ +router bgp 65002 vrf blue + timers bgp 3 9 + bgp router-id 2.2.2.2 + neighbor 1.1.1.1 remote-as 65001 + neighbor 1.1.1.1 update-source lo1 + neighbor 1.1.1.1 ebgp-multihop 3 + neighbor 1.1.1.1 timers 3 10 + neighbor 1.1.1.1 timers connect 10 + neighbor 1.1.1.1 password hello1 + neighbor 3.3.3.3 remote-as 65003 + neighbor 3.3.3.3 update-source lo1 + neighbor 3.3.3.3 ebgp-multihop 3 + neighbor 3.3.3.3 timers 3 10 + neighbor 3.3.3.3 timers connect 10 + neighbor 3.3.3.3 password hello3 + address-family ipv4 unicast + neighbor 1.1.1.1 activate + neighbor 3.3.3.3 activate diff --git a/tests/topotests/bgp-auth/R2/bgpd_vrf_prefix.conf b/tests/topotests/bgp-auth/R2/bgpd_vrf_prefix.conf new file mode 100644 index 0000000000..d1f3847420 --- /dev/null +++ b/tests/topotests/bgp-auth/R2/bgpd_vrf_prefix.conf @@ -0,0 +1,18 @@ +router bgp 65002 vrf blue + timers bgp 3 9 + bgp router-id 2.2.2.2 + neighbor 1.1.1.1 remote-as 65001 + neighbor 1.1.1.1 update-source lo1 + neighbor 1.1.1.1 ebgp-multihop 3 + neighbor 1.1.1.1 timers 3 10 + neighbor 1.1.1.1 timers connect 10 + neighbor 1.1.1.1 password hello1 + neighbor 3.3.3.3 remote-as 65003 + neighbor 3.3.3.3 update-source lo1 + neighbor 3.3.3.3 ebgp-multihop 3 + neighbor 3.3.3.3 timers 3 10 + neighbor 3.3.3.3 timers connect 10 + neighbor 3.3.3.3 password hello3 + address-family ipv4 unicast + neighbor 1.1.1.1 activate + neighbor 3.3.3.3 activate diff --git a/tests/topotests/bgp-auth/R2/ospfd.conf b/tests/topotests/bgp-auth/R2/ospfd.conf new file mode 100644 index 0000000000..028b546a0c --- /dev/null +++ b/tests/topotests/bgp-auth/R2/ospfd.conf @@ -0,0 +1,4 @@ +router ospf + network 10.10.0.0/16 area 0 + network 10.30.0.0/16 area 0 + network 2.2.2.2/32 area 0 diff --git a/tests/topotests/bgp-auth/R2/ospfd_multi_vrf.conf b/tests/topotests/bgp-auth/R2/ospfd_multi_vrf.conf new file mode 100644 index 0000000000..a05dfb8e41 --- /dev/null +++ b/tests/topotests/bgp-auth/R2/ospfd_multi_vrf.conf @@ -0,0 +1,9 @@ +router ospf vrf blue + network 10.10.0.0/16 area 0 + network 10.30.0.0/16 area 0 + network 2.2.2.2/32 area 0 + +router ospf vrf red + network 10.10.0.0/16 area 0 + network 10.30.0.0/16 area 0 + network 2.2.2.2/32 area 0 diff --git a/tests/topotests/bgp-auth/R2/ospfd_vrf.conf b/tests/topotests/bgp-auth/R2/ospfd_vrf.conf new file mode 100644 index 0000000000..b198d352e2 --- /dev/null +++ b/tests/topotests/bgp-auth/R2/ospfd_vrf.conf @@ -0,0 +1,4 @@ +router ospf vrf blue + network 10.10.0.0/16 area 0 + network 10.30.0.0/16 area 0 + network 2.2.2.2/32 area 0 diff --git a/tests/topotests/bgp-auth/R2/zebra.conf b/tests/topotests/bgp-auth/R2/zebra.conf new file mode 100644 index 0000000000..fece68472a --- /dev/null +++ b/tests/topotests/bgp-auth/R2/zebra.conf @@ -0,0 +1,21 @@ +log file zebra.log +! +interface lo + ip address 2.2.2.2/32 +interface lo1 vrf blue + ip address 2.2.2.2/32 +interface lo2 vrf red + ip address 2.2.2.2/32 +interface R2-eth0 + ip address 10.10.0.2/24 +interface R2-eth1 + ip address 10.30.0.2/24 +interface R2-eth2 vrf blue + ip address 10.10.0.2/24 +interface R2-eth3 vrf blue + ip address 10.30.0.2/24 +interface R2-eth4 vrf red + ip address 10.10.0.2/24 +interface R2-eth5 vrf red + ip address 10.30.0.2/24 +! \ No newline at end of file diff --git a/tests/topotests/bgp-auth/R3/bgpd.conf b/tests/topotests/bgp-auth/R3/bgpd.conf new file mode 100644 index 0000000000..deccfd418b --- /dev/null +++ b/tests/topotests/bgp-auth/R3/bgpd.conf @@ -0,0 +1,18 @@ +router bgp 65003 + timers bgp 3 9 + bgp router-id 3.3.3.3 + neighbor 1.1.1.1 remote-as 65001 + neighbor 1.1.1.1 update-source lo + neighbor 1.1.1.1 ebgp-multihop 3 + neighbor 1.1.1.1 timers 3 10 + neighbor 1.1.1.1 timers connect 10 + neighbor 1.1.1.1 password hello2 + neighbor 2.2.2.2 remote-as 65002 + neighbor 2.2.2.2 update-source lo + neighbor 2.2.2.2 ebgp-multihop 3 + neighbor 2.2.2.2 timers connect 10 + neighbor 2.2.2.2 timers 3 10 + neighbor 2.2.2.2 password hello3 + address-family ipv4 unicast + neighbor 1.1.1.1 activate + neighbor 2.2.2.2 activate diff --git a/tests/topotests/bgp-auth/R3/bgpd_multi_vrf.conf b/tests/topotests/bgp-auth/R3/bgpd_multi_vrf.conf new file mode 100644 index 0000000000..fe3e64d8d5 --- /dev/null +++ b/tests/topotests/bgp-auth/R3/bgpd_multi_vrf.conf @@ -0,0 +1,37 @@ +router bgp 65003 vrf blue + timers bgp 3 9 + bgp router-id 3.3.3.3 + neighbor 1.1.1.1 remote-as 65001 + neighbor 1.1.1.1 update-source lo1 + neighbor 1.1.1.1 ebgp-multihop 3 + neighbor 1.1.1.1 timers 3 10 + neighbor 1.1.1.1 timers connect 10 + neighbor 1.1.1.1 password blue2 + neighbor 2.2.2.2 remote-as 65002 + neighbor 2.2.2.2 update-source lo1 + neighbor 2.2.2.2 ebgp-multihop 3 + neighbor 2.2.2.2 timers connect 10 + neighbor 2.2.2.2 timers 3 10 + neighbor 2.2.2.2 password blue3 + address-family ipv4 unicast + neighbor 1.1.1.1 activate + neighbor 2.2.2.2 activate + +router bgp 65003 vrf red + timers bgp 3 9 + bgp router-id 3.3.3.3 + neighbor 1.1.1.1 remote-as 65001 + neighbor 1.1.1.1 update-source lo2 + neighbor 1.1.1.1 ebgp-multihop 3 + neighbor 1.1.1.1 timers 3 10 + neighbor 1.1.1.1 timers connect 10 + neighbor 1.1.1.1 password red2 + neighbor 2.2.2.2 remote-as 65002 + neighbor 2.2.2.2 update-source lo2 + neighbor 2.2.2.2 ebgp-multihop 3 + neighbor 2.2.2.2 timers connect 10 + neighbor 2.2.2.2 timers 3 10 + neighbor 2.2.2.2 password red3 + address-family ipv4 unicast + neighbor 1.1.1.1 activate + neighbor 2.2.2.2 activate diff --git a/tests/topotests/bgp-auth/R3/bgpd_multi_vrf_prefix.conf b/tests/topotests/bgp-auth/R3/bgpd_multi_vrf_prefix.conf new file mode 100644 index 0000000000..fe3e64d8d5 --- /dev/null +++ b/tests/topotests/bgp-auth/R3/bgpd_multi_vrf_prefix.conf @@ -0,0 +1,37 @@ +router bgp 65003 vrf blue + timers bgp 3 9 + bgp router-id 3.3.3.3 + neighbor 1.1.1.1 remote-as 65001 + neighbor 1.1.1.1 update-source lo1 + neighbor 1.1.1.1 ebgp-multihop 3 + neighbor 1.1.1.1 timers 3 10 + neighbor 1.1.1.1 timers connect 10 + neighbor 1.1.1.1 password blue2 + neighbor 2.2.2.2 remote-as 65002 + neighbor 2.2.2.2 update-source lo1 + neighbor 2.2.2.2 ebgp-multihop 3 + neighbor 2.2.2.2 timers connect 10 + neighbor 2.2.2.2 timers 3 10 + neighbor 2.2.2.2 password blue3 + address-family ipv4 unicast + neighbor 1.1.1.1 activate + neighbor 2.2.2.2 activate + +router bgp 65003 vrf red + timers bgp 3 9 + bgp router-id 3.3.3.3 + neighbor 1.1.1.1 remote-as 65001 + neighbor 1.1.1.1 update-source lo2 + neighbor 1.1.1.1 ebgp-multihop 3 + neighbor 1.1.1.1 timers 3 10 + neighbor 1.1.1.1 timers connect 10 + neighbor 1.1.1.1 password red2 + neighbor 2.2.2.2 remote-as 65002 + neighbor 2.2.2.2 update-source lo2 + neighbor 2.2.2.2 ebgp-multihop 3 + neighbor 2.2.2.2 timers connect 10 + neighbor 2.2.2.2 timers 3 10 + neighbor 2.2.2.2 password red3 + address-family ipv4 unicast + neighbor 1.1.1.1 activate + neighbor 2.2.2.2 activate diff --git a/tests/topotests/bgp-auth/R3/bgpd_prefix.conf b/tests/topotests/bgp-auth/R3/bgpd_prefix.conf new file mode 100644 index 0000000000..deccfd418b --- /dev/null +++ b/tests/topotests/bgp-auth/R3/bgpd_prefix.conf @@ -0,0 +1,18 @@ +router bgp 65003 + timers bgp 3 9 + bgp router-id 3.3.3.3 + neighbor 1.1.1.1 remote-as 65001 + neighbor 1.1.1.1 update-source lo + neighbor 1.1.1.1 ebgp-multihop 3 + neighbor 1.1.1.1 timers 3 10 + neighbor 1.1.1.1 timers connect 10 + neighbor 1.1.1.1 password hello2 + neighbor 2.2.2.2 remote-as 65002 + neighbor 2.2.2.2 update-source lo + neighbor 2.2.2.2 ebgp-multihop 3 + neighbor 2.2.2.2 timers connect 10 + neighbor 2.2.2.2 timers 3 10 + neighbor 2.2.2.2 password hello3 + address-family ipv4 unicast + neighbor 1.1.1.1 activate + neighbor 2.2.2.2 activate diff --git a/tests/topotests/bgp-auth/R3/bgpd_vrf.conf b/tests/topotests/bgp-auth/R3/bgpd_vrf.conf new file mode 100644 index 0000000000..c109aa801b --- /dev/null +++ b/tests/topotests/bgp-auth/R3/bgpd_vrf.conf @@ -0,0 +1,18 @@ +router bgp 65003 vrf blue + timers bgp 3 9 + bgp router-id 3.3.3.3 + neighbor 1.1.1.1 remote-as 65001 + neighbor 1.1.1.1 update-source lo1 + neighbor 1.1.1.1 ebgp-multihop 3 + neighbor 1.1.1.1 timers 3 10 + neighbor 1.1.1.1 timers connect 10 + neighbor 1.1.1.1 password hello2 + neighbor 2.2.2.2 remote-as 65002 + neighbor 2.2.2.2 update-source lo1 + neighbor 2.2.2.2 ebgp-multihop 3 + neighbor 2.2.2.2 timers connect 10 + neighbor 2.2.2.2 timers 3 10 + neighbor 2.2.2.2 password hello3 + address-family ipv4 unicast + neighbor 1.1.1.1 activate + neighbor 2.2.2.2 activate diff --git a/tests/topotests/bgp-auth/R3/bgpd_vrf_prefix.conf b/tests/topotests/bgp-auth/R3/bgpd_vrf_prefix.conf new file mode 100644 index 0000000000..c109aa801b --- /dev/null +++ b/tests/topotests/bgp-auth/R3/bgpd_vrf_prefix.conf @@ -0,0 +1,18 @@ +router bgp 65003 vrf blue + timers bgp 3 9 + bgp router-id 3.3.3.3 + neighbor 1.1.1.1 remote-as 65001 + neighbor 1.1.1.1 update-source lo1 + neighbor 1.1.1.1 ebgp-multihop 3 + neighbor 1.1.1.1 timers 3 10 + neighbor 1.1.1.1 timers connect 10 + neighbor 1.1.1.1 password hello2 + neighbor 2.2.2.2 remote-as 65002 + neighbor 2.2.2.2 update-source lo1 + neighbor 2.2.2.2 ebgp-multihop 3 + neighbor 2.2.2.2 timers connect 10 + neighbor 2.2.2.2 timers 3 10 + neighbor 2.2.2.2 password hello3 + address-family ipv4 unicast + neighbor 1.1.1.1 activate + neighbor 2.2.2.2 activate diff --git a/tests/topotests/bgp-auth/R3/ospfd.conf b/tests/topotests/bgp-auth/R3/ospfd.conf new file mode 100644 index 0000000000..0f0a2e926a --- /dev/null +++ b/tests/topotests/bgp-auth/R3/ospfd.conf @@ -0,0 +1,4 @@ +router ospf + network 10.20.0.0/16 area 0 + network 10.30.0.0/16 area 0 + network 3.3.3.3/32 area 0 diff --git a/tests/topotests/bgp-auth/R3/ospfd_multi_vrf.conf b/tests/topotests/bgp-auth/R3/ospfd_multi_vrf.conf new file mode 100644 index 0000000000..f32d2a8423 --- /dev/null +++ b/tests/topotests/bgp-auth/R3/ospfd_multi_vrf.conf @@ -0,0 +1,9 @@ +router ospf vrf blue + network 10.20.0.0/16 area 0 + network 10.30.0.0/16 area 0 + network 3.3.3.3/32 area 0 +! +router ospf vrf red + network 10.20.0.0/16 area 0 + network 10.30.0.0/16 area 0 + network 3.3.3.3/32 area 0 diff --git a/tests/topotests/bgp-auth/R3/ospfd_vrf.conf b/tests/topotests/bgp-auth/R3/ospfd_vrf.conf new file mode 100644 index 0000000000..6465b635aa --- /dev/null +++ b/tests/topotests/bgp-auth/R3/ospfd_vrf.conf @@ -0,0 +1,4 @@ +router ospf vrf blue + network 10.20.0.0/16 area 0 + network 10.30.0.0/16 area 0 + network 3.3.3.3/32 area 0 diff --git a/tests/topotests/bgp-auth/R3/zebra.conf b/tests/topotests/bgp-auth/R3/zebra.conf new file mode 100644 index 0000000000..0fe3acdfd0 --- /dev/null +++ b/tests/topotests/bgp-auth/R3/zebra.conf @@ -0,0 +1,21 @@ +log file zebra.log +! +interface lo + ip address 3.3.3.3/32 +interface lo1 vrf blue + ip address 3.3.3.3/32 +interface lo2 vrf red + ip address 3.3.3.3/32 +interface R3-eth0 + ip address 10.20.0.3/24 +interface R3-eth1 + ip address 10.30.0.3/24 +interface R3-eth2 vrf blue + ip address 10.20.0.3/24 +interface R3-eth3 vrf blue + ip address 10.30.0.3/24 +interface R3-eth4 vrf red + ip address 10.20.0.3/24 +interface R3-eth5 vrf red + ip address 10.30.0.3/24 +! \ No newline at end of file diff --git a/tests/topotests/bgp-auth/test_bgp_auth.py b/tests/topotests/bgp-auth/test_bgp_auth.py new file mode 100755 index 0000000000..6198997b86 --- /dev/null +++ b/tests/topotests/bgp-auth/test_bgp_auth.py @@ -0,0 +1,747 @@ +#!/usr/bin/env python + +# +# test_bgp_auth.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2020 by Volta Networks +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_bgp_auth.py: Test BGP Md5 Authentication + + +------+ + +--------| |--------+ + | +------| R1 |------+ | + | | -----| |----+ | | + | | | +------+ | | | + | | | | | | + +------+ +------+ + | |------------| | + | R2 |------------| R3 | + | |------------| | + +------+ +------+ + + +setup is 3 routers with 3 links between each each link in a different vrf +Default, blue and red respectively +Tests check various fiddling with passwords and checking that the peer +establishment is as expected and passwords are not leaked across sockets +for bgp instances +""" + +import os +import sys +import json +import platform +from functools import partial +import pytest +from time import sleep + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + +from lib.common_config import apply_raw_config + +ERROR_LIST = ["Malformed", "Failure", "Unknown", "Incomplete"] + + +class InvalidCLIError(Exception): + """Raise when the CLI command is wrong""" + + pass + + +class TemplateTopo(Topo): + "Test topology builder" + + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # This function only purpose is to define allocation and relationship + # between routers, switches and hosts. + # + # + # Create routers + tgen.add_router("R1") + tgen.add_router("R2") + tgen.add_router("R3") + + # R1-R2 1 + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["R1"]) + switch.add_link(tgen.gears["R2"]) + + # R1-R3 1 + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["R1"]) + switch.add_link(tgen.gears["R3"]) + + # R2-R3 1 + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["R2"]) + switch.add_link(tgen.gears["R3"]) + + # R1-R2 2 + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["R1"]) + switch.add_link(tgen.gears["R2"]) + + # R1-R3 2 + switch = tgen.add_switch("s5") + switch.add_link(tgen.gears["R1"]) + switch.add_link(tgen.gears["R3"]) + + # R2-R3 2 + switch = tgen.add_switch("s6") + switch.add_link(tgen.gears["R2"]) + switch.add_link(tgen.gears["R3"]) + + # R1-R2 3 + switch = tgen.add_switch("s7") + switch.add_link(tgen.gears["R1"]) + switch.add_link(tgen.gears["R2"]) + + # R1-R3 2 + switch = tgen.add_switch("s8") + switch.add_link(tgen.gears["R1"]) + switch.add_link(tgen.gears["R3"]) + + # R2-R3 2 + switch = tgen.add_switch("s9") + switch.add_link(tgen.gears["R2"]) + switch.add_link(tgen.gears["R3"]) + + +def setup_module(mod): + "Sets up the pytest environment" + # This function initiates the topology build with Topogen... + tgen = Topogen(TemplateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + tgen.start_topology() + + r1 = tgen.gears["R1"] + r2 = tgen.gears["R2"] + r3 = tgen.gears["R3"] + + # blue vrf + r1.run("ip link add blue type vrf table 1001") + r1.run("ip link set up dev blue") + r2.run("ip link add blue type vrf table 1001") + r2.run("ip link set up dev blue") + r3.run("ip link add blue type vrf table 1001") + r3.run("ip link set up dev blue") + + r1.run("ip link add lo1 type dummy") + r1.run("ip link set lo1 master blue") + r1.run("ip link set up dev lo1") + r2.run("ip link add lo1 type dummy") + r2.run("ip link set up dev lo1") + r2.run("ip link set lo1 master blue") + r3.run("ip link add lo1 type dummy") + r3.run("ip link set up dev lo1") + r3.run("ip link set lo1 master blue") + + r1.run("ip link set R1-eth2 master blue") + r1.run("ip link set R1-eth3 master blue") + r2.run("ip link set R2-eth2 master blue") + r2.run("ip link set R2-eth3 master blue") + r3.run("ip link set R3-eth2 master blue") + r3.run("ip link set R3-eth3 master blue") + + r1.run("ip link set up dev R1-eth2") + r1.run("ip link set up dev R1-eth3") + r2.run("ip link set up dev R2-eth2") + r2.run("ip link set up dev R2-eth3") + r3.run("ip link set up dev R3-eth2") + r3.run("ip link set up dev R3-eth3") + + # red vrf + r1.run("ip link add red type vrf table 1002") + r1.run("ip link set up dev red") + r2.run("ip link add red type vrf table 1002") + r2.run("ip link set up dev red") + r3.run("ip link add red type vrf table 1002") + r3.run("ip link set up dev red") + + r1.run("ip link add lo2 type dummy") + r1.run("ip link set lo2 master red") + r1.run("ip link set up dev lo2") + r2.run("ip link add lo2 type dummy") + r2.run("ip link set up dev lo2") + r2.run("ip link set lo2 master red") + r3.run("ip link add lo2 type dummy") + r3.run("ip link set up dev lo2") + r3.run("ip link set lo2 master red") + + r1.run("ip link set R1-eth4 master red") + r1.run("ip link set R1-eth5 master red") + r2.run("ip link set R2-eth4 master red") + r2.run("ip link set R2-eth5 master red") + r3.run("ip link set R3-eth4 master red") + r3.run("ip link set R3-eth5 master red") + + r1.run("ip link set up dev R1-eth4") + r1.run("ip link set up dev R1-eth5") + r2.run("ip link set up dev R2-eth4") + r2.run("ip link set up dev R2-eth5") + r3.run("ip link set up dev R3-eth4") + r3.run("ip link set up dev R3-eth5") + + # This is a sample of configuration loading. + router_list = tgen.routers() + + # For all registred routers, load the zebra configuration file + for rname, router in router_list.iteritems(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + # After loading the configurations, this function loads configured daemons. + tgen.start_router() + + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + + +def vrf_str(vrf): + if vrf == "": + vrf_str = "" + else: + vrf_str = "vrf {}".format(vrf) + + return vrf_str + + +def peer_name(rtr, prefix, vrf): + "generate VRF string for CLI" + if vrf == "": + vrf_str = "" + else: + vrf_str = "_" + vrf + + if prefix == "yes": + if rtr == "R2": + return "TWO_GROUP" + vrf_str + else: + return "THREE_GROUP" + vrf_str + else: + if rtr == "R2": + return "2.2.2.2" + else: + return "3.3.3.3" + + +def print_diag(vrf): + "print failure disagnostics" + + tgen = get_topogen() + router_list = tgen.routers() + for rname, router in router_list.iteritems(): + print(rname + ":") + print(router.vtysh_cmd("show run")) + print(router.vtysh_cmd("show ip route {}".format(vrf_str(vrf)))) + print(router.vtysh_cmd("show bgp {} neighbor".format(vrf_str(vrf)))) + + +def configure(conf_file): + "configure from a file" + + tgen = get_topogen() + router_list = tgen.routers() + for rname, router in router_list.iteritems(): + with open( + os.path.join(CWD, "{}/{}").format(router.name, conf_file), "r+" + ) as cfg: + new_config = cfg.read() + + output = router.vtysh_multicmd(new_config, pretty_output=False) + for out_err in ERROR_LIST: + if out_err.lower() in output.lower(): + raise InvalidCLIError("%s" % output) + + +def clear_bgp(vrf=""): + " clear bgp configuration for a vrf" + + tgen = get_topogen() + r1 = tgen.gears["R1"] + r2 = tgen.gears["R2"] + r3 = tgen.gears["R3"] + + router_list = tgen.routers() + if vrf == "": + r1.vtysh_cmd("conf t\nno router bgp 65001") + r2.vtysh_cmd("conf t\nno router bgp 65002") + r2.vtysh_cmd("conf t\nno router bgp 65003") + else: + r1.vtysh_cmd("conf t\nno router bgp 65001 vrf {}".format(vrf)) + r2.vtysh_cmd("conf t\nno router bgp 65002 vrf {}".format(vrf)) + r3.vtysh_cmd("conf t\nno router bgp 65003 vrf {}".format(vrf)) + + +def clear_ospf(vrf=""): + "clear ospf configuration for a vrf" + + tgen = get_topogen() + router_list = tgen.routers() + for rname, router in router_list.iteritems(): + if vrf == "": + router.vtysh_cmd("conf t\nno router ospf") + else: + router.vtysh_cmd("conf t\nno router ospf vrf {}".format(vrf)) + + +def check_neigh_state(router, peer, state, vrf=""): + "check BGP neighbor state on a router" + + count = 0 + matched = False + neigh_output = "" + while count < 125: + if vrf == "": + neigh_output = router.vtysh_cmd("show bgp neighbors {} json".format(peer)) + else: + neigh_output = router.vtysh_cmd( + "show bgp vrf {} neighbors {} json".format(vrf, peer) + ) + neigh_output_json = json.loads(neigh_output) + if neigh_output_json[peer]["bgpState"] == state: + matched = True + break + count += 1 + sleep(1) + + assertmsg = "{} could not peer {} state expected {} got {} ".format( + router.name, peer, state, neigh_output_json[peer]["bgpState"] + ) + if matched != True: + print_diag(vrf) + assert matched == True, assertmsg + + +def check_all_peers_established(vrf=""): + "standard check for extablished peers per vrf" + + tgen = get_topogen() + r1 = tgen.gears["R1"] + r2 = tgen.gears["R2"] + r3 = tgen.gears["R3"] + # do r1 last as he might be the dynamic one + check_neigh_state(r2, "1.1.1.1", "Established", vrf) + check_neigh_state(r2, "3.3.3.3", "Established", vrf) + check_neigh_state(r3, "1.1.1.1", "Established", vrf) + check_neigh_state(r3, "2.2.2.2", "Established", vrf) + check_neigh_state(r1, "2.2.2.2", "Established", vrf) + check_neigh_state(r1, "3.3.3.3", "Established", vrf) + + +def check_vrf_peer_remove_passwords(vrf="", prefix="no"): + "selectively remove passwords checking state" + + tgen = get_topogen() + r1 = tgen.gears["R1"] + r2 = tgen.gears["R2"] + r3 = tgen.gears["R3"] + + r1.vtysh_cmd( + "conf t\nrouter bgp 65001 {}\nno neighbor {} password".format( + vrf_str(vrf), peer_name("R2", prefix, vrf) + ) + ) + + check_neigh_state(r2, "1.1.1.1", "Connect", vrf) + check_neigh_state(r2, "3.3.3.3", "Established", vrf) + check_neigh_state(r3, "1.1.1.1", "Established", vrf) + check_neigh_state(r3, "2.2.2.2", "Established", vrf) + # don't check dynamic downed peers - they are removed + if prefix == "no": + check_neigh_state(r1, "2.2.2.2", "Connect", vrf) + check_neigh_state(r1, "3.3.3.3", "Established", vrf) + + r2.vtysh_cmd( + "conf t\nrouter bgp 65002 {}\nno neighbor 1.1.1.1 password".format(vrf_str(vrf)) + ) + check_all_peers_established(vrf) + + r1.vtysh_cmd( + "conf t\nrouter bgp 65001 {}\nno neighbor {} password".format( + vrf_str(vrf), peer_name("R3", prefix, vrf) + ) + ) + check_neigh_state(r2, "1.1.1.1", "Established", vrf) + check_neigh_state(r2, "3.3.3.3", "Established", vrf) + check_neigh_state(r3, "1.1.1.1", "Connect", vrf) + check_neigh_state(r3, "2.2.2.2", "Established", vrf) + check_neigh_state(r1, "2.2.2.2", "Established", vrf) + # don't check dynamic downed peers - they are removed + if prefix == "no": + check_neigh_state(r1, "3.3.3.3", "Connect", vrf) + + r3.vtysh_cmd( + "conf t\nrouter bgp 65003 {}\nno neighbor 1.1.1.1 password".format(vrf_str(vrf)) + ) + check_all_peers_established(vrf) + + r2.vtysh_cmd( + "conf t\nrouter bgp 65002 {}\nno neighbor 3.3.3.3 password".format(vrf_str(vrf)) + ) + check_neigh_state(r2, "1.1.1.1", "Established", vrf) + check_neigh_state(r2, "3.3.3.3", "Connect", vrf) + check_neigh_state(r3, "1.1.1.1", "Established", vrf) + check_neigh_state(r3, "2.2.2.2", "Connect", vrf) + check_neigh_state(r1, "2.2.2.2", "Established", vrf) + check_neigh_state(r1, "3.3.3.3", "Established", vrf) + + r3.vtysh_cmd( + "conf t\nrouter bgp 65003 {}\nno neighbor 2.2.2.2 password".format(vrf_str(vrf)) + ) + check_all_peers_established(vrf) + + +def check_vrf_peer_change_passwords(vrf="", prefix="no"): + "selectively change passwords checking state" + + tgen = get_topogen() + r1 = tgen.gears["R1"] + r2 = tgen.gears["R2"] + r3 = tgen.gears["R3"] + check_all_peers_established(vrf) + + r1.vtysh_cmd( + "conf t\nrouter bgp 65001 {}\nneighbor {} password change1".format( + vrf_str(vrf), peer_name("R2", prefix, vrf) + ) + ) + check_neigh_state(r2, "1.1.1.1", "Connect", vrf) + check_neigh_state(r2, "3.3.3.3", "Established", vrf) + check_neigh_state(r3, "1.1.1.1", "Established", vrf) + check_neigh_state(r3, "2.2.2.2", "Established", vrf) + # don't check dynamic downed peers - they are removed + if prefix == "no": + check_neigh_state(r1, "2.2.2.2", "Connect", vrf) + check_neigh_state(r1, "3.3.3.3", "Established", vrf) + + r2.vtysh_cmd( + "conf t\nrouter bgp 65002 {}\nneighbor 1.1.1.1 password change1".format( + vrf_str(vrf) + ) + ) + check_all_peers_established(vrf) + + r1.vtysh_cmd( + "conf t\nrouter bgp 65001 {}\nneighbor {} password change2".format( + vrf_str(vrf), peer_name("R3", prefix, vrf) + ) + ) + check_neigh_state(r2, "1.1.1.1", "Established", vrf) + check_neigh_state(r2, "3.3.3.3", "Established", vrf) + check_neigh_state(r3, "1.1.1.1", "Connect", vrf) + check_neigh_state(r3, "2.2.2.2", "Established", vrf) + check_neigh_state(r1, "2.2.2.2", "Established", vrf) + # don't check dynamic downed peers - they are removed + if prefix == "no": + check_neigh_state(r1, "3.3.3.3", "Connect", vrf) + + r3.vtysh_cmd( + "conf t\nrouter bgp 65003 {}\nneighbor 1.1.1.1 password change2".format( + vrf_str(vrf) + ) + ) + check_all_peers_established(vrf) + + r2.vtysh_cmd( + "conf t\nrouter bgp 65002 {}\nneighbor 3.3.3.3 password change3".format( + vrf_str(vrf) + ) + ) + check_neigh_state(r2, "1.1.1.1", "Established", vrf) + check_neigh_state(r2, "3.3.3.3", "Connect", vrf) + check_neigh_state(r3, "1.1.1.1", "Established", vrf) + check_neigh_state(r3, "2.2.2.2", "Connect", vrf) + check_neigh_state(r1, "2.2.2.2", "Established", vrf) + check_neigh_state(r1, "3.3.3.3", "Established", vrf) + + r3.vtysh_cmd( + "conf t\nrouter bgp 65003 {}\nneighbor 2.2.2.2 password change3".format( + vrf_str(vrf) + ) + ) + check_all_peers_established(vrf) + + +def test_default_peer_established(): + "default vrf 3 peers same password" + + check_all_peers_established() + clear_bgp() + # tgen.mininet_cli() + + +def test_default_peer_remove_passwords(): + "selectively remove passwords checking state" + + configure("bgpd.conf") + check_vrf_peer_remove_passwords() + clear_bgp() + + +def test_default_peer_change_passwords(): + "selectively change passwords checking state" + + configure("bgpd.conf") + check_vrf_peer_change_passwords() + clear_bgp() + + +def test_default_prefix_peer_established(): + "default vrf 3 peers same password with prefix config" + + # only supported in kernel > 5.3 + if topotest.version_cmp(platform.release(), "5.3") < 0: + return + + configure("bgpd_prefix.conf") + check_all_peers_established() + clear_bgp() + # tgen.mininet_cli() + + +def test_prefix_peer_remove_passwords(): + "selectively remove passwords checking state with prefix config" + + # only supported in kernel > 5.3 + if topotest.version_cmp(platform.release(), "5.3") < 0: + return + configure("bgpd_prefix.conf") + check_vrf_peer_remove_passwords(prefix="yes") + clear_bgp() + + +def test_prefix_peer_change_passwords(): + "selecively change passwords checkig state with prefix config" + + # only supported in kernel > 5.3 + if topotest.version_cmp(platform.release(), "5.3") < 0: + return + configure("bgpd_prefix.conf") + check_vrf_peer_change_passwords(prefix="yes") + clear_bgp() + clear_ospf() + + +def test_vrf_peer_established(): + "default vrf 3 peers same password with VRF config" + + # clean routers and load vrf config + configure("bgpd_vrf.conf") + configure("ospfd_vrf.conf") + + check_all_peers_established("blue") + clear_bgp("blue") + # tgen.mininet_cli() + + +def test_vrf_peer_remove_passwords(): + "selectively remove passwords checking state with VRF config" + + configure("bgpd_vrf.conf") + check_vrf_peer_remove_passwords(vrf="blue") + clear_bgp("blue") + + +def test_vrf_peer_change_passwords(): + "selectively change passwords checking state with VRF config" + + configure("bgpd_vrf.conf") + check_vrf_peer_change_passwords(vrf="blue") + clear_bgp("blue") + + +def test_vrf_prefix_peer_established(): + "default vrf 3 peers same password with VRF prefix config" + + # only supported in kernel > 5.3 + if topotest.version_cmp(platform.release(), "5.3") < 0: + clear_bgp("blue") + return + + configure("bgpd_vrf_prefix.conf") + check_all_peers_established("blue") + clear_bgp("blue") + + +def test_vrf_prefix_peer_remove_passwords(): + "selectively remove passwords checking state with VRF prefix config" + + # only supported in kernel > 5.3 + if topotest.version_cmp(platform.release(), "5.3") < 0: + return + + configure("bgpd_vrf_prefix.conf") + check_vrf_peer_remove_passwords(vrf="blue", prefix="yes") + clear_bgp("blue") + + +def test_vrf_prefix_peer_change_passwords(): + "selectively change passwords checking state with VRF prefix config" + + tgen = get_topogen() + r1 = tgen.gears["R1"] + r2 = tgen.gears["R2"] + r3 = tgen.gears["R3"] + + # only supported in kernel > 5.3 + if topotest.version_cmp(platform.release(), "5.3") < 0: + clear_ospf("blue") + return + + configure("bgpd_vrf_prefix.conf") + check_vrf_peer_change_passwords(vrf="blue", prefix="yes") + clear_bgp("blue") + clear_ospf("blue") + + +def test_multiple_vrf_peer_established(): + "default vrf 3 peers same password with multiple VRFs" + + configure("bgpd_multi_vrf.conf") + configure("ospfd_multi_vrf.conf") + check_all_peers_established("blue") + check_all_peers_established("red") + clear_bgp("blue") + clear_bgp("red") + # tgen.mininet_cli() + + +def test_multiple_vrf_peer_remove_passwords(): + "selectively remove passwords checking state with multiple VRFs" + + configure("bgpd_multi_vrf.conf") + check_vrf_peer_remove_passwords("blue") + check_all_peers_established("red") + check_vrf_peer_remove_passwords("red") + check_all_peers_established("blue") + clear_bgp("blue") + clear_bgp("red") + # tgen.mininet_cli() + + +def test_multiple_vrf_peer_change_passwords(): + "selectively change passwords checking state with multiple VRFs" + + configure("bgpd_multi_vrf.conf") + check_vrf_peer_change_passwords("blue") + check_all_peers_established("red") + check_vrf_peer_change_passwords("red") + check_all_peers_established("blue") + clear_bgp("blue") + clear_bgp("red") + # tgen.mininet_cli() + + +def test_multiple_vrf_prefix_peer_established(): + "default vrf 3 peers same password with multilpe VRFs and prefix config" + + # only supported in kernel > 5.3 + if topotest.version_cmp(platform.release(), "5.3") < 0: + return + + configure("bgpd_multi_vrf.conf") + configure("ospfd_multi_vrf.conf") + check_all_peers_established("blue") + check_all_peers_established("red") + clear_bgp("blue") + clear_bgp("red") + # tgen.mininet_cli() + + +def test_multiple_vrf_prefix_peer_remove_passwords(): + "selectively remove passwords checking state with multiple vrfs and prefix config" + + # only supported in kernel > 5.3 + if topotest.version_cmp(platform.release(), "5.3") < 0: + return + + configure("bgpd_multi_vrf_prefix.conf") + tgen = get_topogen() + check_vrf_peer_remove_passwords(vrf="blue", prefix="yes") + check_all_peers_established("red") + check_vrf_peer_remove_passwords(vrf="red", prefix="yes") + check_all_peers_established("blue") + clear_bgp("blue") + clear_bgp("red") + # tgen.mininet_cli() + + +def test_multiple_vrf_prefix_peer_change_passwords(): + "selectively change passwords checking state with multiple vrfs and prefix config" + + # only supported in kernel > 5.3 + if topotest.version_cmp(platform.release(), "5.3") < 0: + clear_bgp("blue") + clear_bgp("red") + clear_ospf("blue") + clear_ospf("red") + return + + configure("bgpd_multi_vrf_prefix.conf") + check_vrf_peer_change_passwords(vrf="blue", prefix="yes") + check_all_peers_established("red") + check_vrf_peer_change_passwords(vrf="red", prefix="yes") + check_all_peers_established("blue") + clear_bgp("blue") + clear_bgp("red") + clear_ospf("blue") + clear_ospf("red") + # tgen.mininet_cli() + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp-basic-functionality-topo1/__init__.py b/tests/topotests/bgp-basic-functionality-topo1/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bgp-basic-functionality-topo1/bgp_basic_functionality.json b/tests/topotests/bgp-basic-functionality-topo1/bgp_basic_functionality.json new file mode 100644 index 0000000000..ee1f1b74c0 --- /dev/null +++ b/tests/topotests/bgp-basic-functionality-topo1/bgp_basic_functionality.json @@ -0,0 +1,115 @@ +{ + "address_types": ["ipv4", "ipv6"], + "ipv4base": "10.0.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 30, "ipv6": "fd00::", "v6mask": 64}, + "lo_prefix": {"ipv4": "1.0.", "v4mask": 32, "ipv6": "2001:db8:f::", "v6mask": 128}, + "routers": { + "r1": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2": {"ipv4": "auto", "ipv6": "auto"}, + "r3": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": {"dest_link": {"r1": {}}}, + "r3": {"dest_link": {"r1": {}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": {"dest_link": {"r1": {}}}, + "r3": {"dest_link": {"r1": {}}} + } + } + } + } + } + }, + "r2": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1": {"ipv4": "auto", "ipv6": "auto"}, + "r3": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r2": {}}}, + "r3": {"dest_link": {"r2": {}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r2": {}}}, + "r3": {"dest_link": {"r2": {}}} + } + } + } + } + } + }, + "r3": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1": {"ipv4": "auto", "ipv6": "auto"}, + "r2": {"ipv4": "auto", "ipv6": "auto"}, + "r4": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r3": {}}}, + "r2": {"dest_link": {"r3": {}}}, + "r4": {"dest_link": {"r3": {}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r3": {}}}, + "r2": {"dest_link": {"r3": {}}}, + "r4": {"dest_link": {"r3": {}}} + } + } + } + } + } + }, + "r4": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r3": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "200", + "address_family": { + "ipv4": { + "unicast": {"neighbor": {"r3": {"dest_link": {"r4": {}}}}} + }, + "ipv6": { + "unicast": {"neighbor": {"r3": {"dest_link": {"r4": {}}}}} + } + } + } + } + } +} 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 new file mode 100755 index 0000000000..41fa7c0a09 --- /dev/null +++ b/tests/topotests/bgp-basic-functionality-topo1/test_bgp_basic_functionality.py @@ -0,0 +1,1153 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2019 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +Following tests are covered to test BGP basic functionality: + +Test steps +- Create topology (setup module) + Creating 4 routers topology, r1, r2, r3 are in IBGP and + r3, r4 are in EBGP +- Bring up topology +- Verify for bgp to converge +- Modify/Delete and verify router-id +- Modify and verify bgp timers +- Create and verify static routes +- Modify and verify admin distance for existing static routes +- Test advertise network using network command +- Verify clear bgp +- Test bgp convergence with loopback interface +- Test advertise network using network command +- Verify routes not installed in zebra when /32 routes received + with loopback BGP session subnet +""" + +import os +import sys +import json +import time +import pytest +from copy import deepcopy + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# Required to instantiate the topology builder class. + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from mininet.topo import Topo + +from lib.common_config import ( + step, + start_topology, + write_test_header, + write_test_footer, + reset_config_on_routers, + create_static_routes, + verify_rib, + verify_admin_distance_for_static_routes, + check_address_types, + apply_raw_config, + addKernelRoute, + verify_fib_routes, + create_prefix_lists, + create_route_maps, + verify_bgp_community, + required_linux_kernel_version +) +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + create_router_bgp, + verify_router_id, + modify_as_number, + verify_as_numbers, + clear_bgp_and_verify, + verify_bgp_timers_and_functionality, + verify_bgp_rib, +) +from lib.topojson import build_topo_from_json, build_config_from_json + +# Reading the data from JSON File for topology creation +jsonFile = "{}/bgp_basic_functionality.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +# Global Variable +KEEPALIVETIMER = 2 +HOLDDOWNTIMER = 6 +r1_ipv4_loopback = "1.0.1.0/24" +r2_ipv4_loopback = "1.0.2.0/24" +r3_ipv4_loopback = "1.0.3.0/24" +r4_ipv4_loopback = "1.0.4.0/24" +r1_ipv6_loopback = "2001:db8:f::1:0/120" +r2_ipv6_loopback = "2001:db8:f::2:0/120" +r3_ipv6_loopback = "2001:db8:f::3:0/120" +r4_ipv6_loopback = "2001:db8:f::4:0/120" +NETWORK = { + "ipv4": ["100.1.1.1/32", "100.1.1.2/32"], + "ipv6": ["100::1/128", "100::2/128"], +} + + +class CreateTopo(Topo): + """ + Test BasicTopo - topology 1 + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + """Build function""" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version('4.15') + if result is not True: + pytest.skip("Kernel requirements are not met") + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(CreateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + global ADDR_TYPES + global BGP_CONVERGENCE + ADDR_TYPES = check_address_types() + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# +# Testcases +# +##################################################### + + +def test_modify_and_delete_router_id(request): + """ Test to modify, delete and verify router-id. """ + + tgen = get_topogen() + if BGP_CONVERGENCE is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Modify router id + input_dict = { + "r1": {"bgp": {"router_id": "12.12.12.12"}}, + "r2": {"bgp": {"router_id": "22.22.22.22"}}, + "r3": {"bgp": {"router_id": "33.33.33.33"}}, + } + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + # Verifying router id once modified + result = verify_router_id(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + # Delete router id + input_dict = { + "r1": {"bgp": {"del_router_id": True}}, + "r2": {"bgp": {"del_router_id": True}}, + "r3": {"bgp": {"del_router_id": True}}, + } + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + # Verifying router id once deleted + # Once router-id is deleted, highest interface ip should become + # router-id + result = verify_router_id(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_bgp_config_with_4byte_as_number(request): + """ + Configure BGP with 4 byte ASN and verify it works fine + """ + + tgen = get_topogen() + if BGP_CONVERGENCE is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + input_dict = { + "r1": {"bgp": {"local_as": 131079}}, + "r2": {"bgp": {"local_as": 131079}}, + "r3": {"bgp": {"local_as": 131079}}, + "r4": {"bgp": {"local_as": 131080}}, + } + result = modify_as_number(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + result = verify_as_numbers(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_BGP_config_with_invalid_ASN_p2(request): + """ + Configure BGP with invalid ASN(ex - 0, reserved ASN) and verify test case + ended up with error + """ + + tgen = get_topogen() + global BGP_CONVERGENCE + + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Api call to modify AS number + input_dict = { + "r1": {"bgp": {"local_as": 0,}}, + "r2": {"bgp": {"local_as": 0,}}, + "r3": {"bgp": {"local_as": 0,}}, + "r4": {"bgp": {"local_as": 64000,}}, + } + result = modify_as_number(tgen, topo, input_dict) + try: + assert result is True + except AssertionError: + logger.info("Expected behaviour: {}".format(result)) + logger.info("BGP config is not created because of invalid ASNs") + + write_test_footer(tc_name) + + +def test_BGP_config_with_2byteAS_and_4byteAS_number_p1(request): + """ + Configure BGP with 4 byte and 2 byte ASN and verify BGP is converged + """ + + tgen = get_topogen() + global BGP_CONVERGENCE + + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Api call to modify AS number + input_dict = { + "r1": {"bgp": {"local_as": 131079}}, + "r2": {"bgp": {"local_as": 131079}}, + "r3": {"bgp": {"local_as": 131079}}, + "r4": {"bgp": {"local_as": 111}}, + } + result = modify_as_number(tgen, topo, input_dict) + if result != True: + assert False, "Testcase " + tc_name + " :Failed \n Error: {}".format(result) + + result = verify_as_numbers(tgen, topo, input_dict) + if result != True: + assert False, "Testcase " + tc_name + " :Failed \n Error: {}".format(result) + + # Api call verify whether BGP is converged + result = verify_bgp_convergence(tgen, topo) + if result != True: + assert False, "Testcase " + tc_name + " :Failed \n Error: {}".format(result) + + write_test_footer(tc_name) + + +def test_bgp_timers_functionality(request): + """ + Test to modify bgp timers and verify timers functionality. + """ + + tgen = get_topogen() + if BGP_CONVERGENCE is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + # Api call to modfiy BGP timerse + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, deepcopy(input_dict)) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + # Api call to clear bgp, so timer modification would take place + clear_bgp_and_verify(tgen, topo, "r1") + + # Verifying bgp timers functionality + result = verify_bgp_timers_and_functionality(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_static_routes(request): + """ Test to create and verify static routes. """ + + tgen = get_topogen() + if BGP_CONVERGENCE is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + # Api call to create static routes + input_dict = { + "r1": { + "static_routes": [ + { + "network": "10.0.20.1/32", + "no_of_ip": 9, + "admin_distance": 100, + "next_hop": "10.0.0.2", + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + # Api call to redistribute static routes + input_dict_1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + } + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + next_hop = ["10.0.0.2", "10.0.0.5"] + result = verify_rib( + tgen, "ipv4", dut, input_dict, next_hop=next_hop, protocol=protocol + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_admin_distance_for_existing_static_routes(request): + """ Test to modify and verify admin distance for existing static routes.""" + + tgen = get_topogen() + if BGP_CONVERGENCE is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + input_dict = { + "r1": { + "static_routes": [ + { + "network": "10.0.20.1/32", + "admin_distance": 10, + "next_hop": "10.0.0.2", + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + # Verifying admin distance once modified + result = verify_admin_distance_for_static_routes(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_advertise_network_using_network_command(request): + """ Test advertise networks using network command.""" + + tgen = get_topogen() + if BGP_CONVERGENCE is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + # Api call to advertise networks + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + {"network": "20.0.0.0/32", "no_of_network": 10}, + {"network": "30.0.0.0/32", "no_of_network": 10}, + ] + } + } + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r2" + protocol = "bgp" + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_clear_bgp_and_verify(request): + """ + Created few static routes and verified all routes are learned via BGP + cleared BGP and verified all routes are intact + """ + + tgen = get_topogen() + if BGP_CONVERGENCE is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + # clear ip bgp + result = clear_bgp_and_verify(tgen, topo, "r1") + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_BGP_attributes_with_vrf_default_keyword_p0(request): + """ + TC_9: + Verify BGP functionality for default vrf with + "vrf default" keyword. + """ + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + #reset_config_on_routers(tgen) + + step("Configure static routes and redistribute in BGP on R3") + for addr_type in ADDR_TYPES: + input_dict = { + "r3": { + "static_routes": [ + { + "network": NETWORK[addr_type][0], + "no_of_ip": 4, + "next_hop": "Null0", + } + ] + } + } + + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_2 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Create a route-map to match a specific prefix and modify" + "BGP attributes for matched prefix" + ) + input_dict_2 = { + "r3": { + "prefix_lists": { + "ipv4": { + "ABC": [ + { + "seqid": 10, + "action": "permit", + "network": NETWORK["ipv4"][0], + } + ] + }, + "ipv6": { + "XYZ": [ + { + "seqid": 100, + "action": "permit", + "network": NETWORK["ipv6"][0], + } + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + if addr_type == "ipv4": + pf_list = "ABC" + else: + pf_list = "XYZ" + + input_dict_6 = { + "r3": { + "route_maps": { + "BGP_ATTR_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": 10, + "match": {addr_type: {"prefix_lists": pf_list}}, + "set": { + "aspath": {"as_num": 500, "as_action": "prepend"}, + "localpref": 500, + "origin": "egp", + "community": {"num": "500:500", "action": "additive"}, + "large_community": { + "num": "500:500:500", + "action": "additive", + }, + }, + }, + {"action": "permit", "seq_id": 20}, + ] + }, + "BGP_ATTR_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": 100, + "match": {addr_type: {"prefix_lists": pf_list}}, + "set": { + "aspath": {"as_num": 500, "as_action": "prepend"}, + "localpref": 500, + "origin": "egp", + "community": {"num": "500:500", "action": "additive"}, + "large_community": { + "num": "500:500:500", + "action": "additive", + }, + }, + }, + {"action": "permit", "seq_id": 200}, + ], + } + } + + result = create_route_maps(tgen, input_dict_6) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Apply the route-map on R3 in outbound direction for peer R4") + + input_dict_7 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "BGP_ATTR_ipv4", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "BGP_ATTR_ipv6", + "direction": "out", + } + ] + } + } + } + } + } + }, + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_7) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "verify modified attributes for specific prefix with 'vrf default'" + "keyword on R4" + ) + for addr_type in ADDR_TYPES: + dut = "r4" + input_dict = { + "r3": { + "static_routes": [ + { + "network": NETWORK[addr_type][0], + "vrf": "default", + "largeCommunity": "500:500:500", + } + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, dut, input_dict) + assert result is True, "Testcase : Failed \n Error: {}".format(tc_name, result) + result = verify_rib(tgen, addr_type, dut, input_dict) + assert result is True, "Testcase : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + dut = "r4" + input_dict = { + "r3": { + "static_routes": [ + { + "network": NETWORK[addr_type][0], + "vrf": "default", + "community": "500:500", + } + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, dut, input_dict) + assert result is True, "Testcase : Failed \n Error: {}".format(tc_name, result) + result = verify_rib(tgen, addr_type, dut, input_dict) + assert result is True, "Testcase : Failed \n Error: {}".format(tc_name, result) + + input_dict_4 = {"largeCommunity": "500:500:500", "community": "500:500"} + + result = verify_bgp_community( + tgen, addr_type, dut, [NETWORK[addr_type][0]], input_dict_4 + ) + assert result is True, "Test case {} : Should fail \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_bgp_with_loopback_interface(request): + """ + Test BGP with loopback interface + + Adding keys:value pair "dest_link": "lo" and "source_link": "lo" + peer dict of input json file for all router's creating config using + loopback interface. Once BGP neighboship is up then verifying BGP + convergence + """ + + tgen = get_topogen() + if BGP_CONVERGENCE is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + for routerN in sorted(topo["routers"].keys()): + for bgp_neighbor in topo["routers"][routerN]["bgp"]["address_family"]["ipv4"][ + "unicast" + ]["neighbor"].keys(): + + # Adding ['source_link'] = 'lo' key:value pair + topo["routers"][routerN]["bgp"]["address_family"]["ipv4"]["unicast"][ + "neighbor" + ][bgp_neighbor]["dest_link"] = {"lo": {"source_link": "lo",}} + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + input_dict = { + "r1": { + "static_routes": [ + {"network": "1.0.2.17/32", "next_hop": "10.0.0.2"}, + {"network": "1.0.3.17/32", "next_hop": "10.0.0.6"}, + ] + }, + "r2": { + "static_routes": [ + {"network": "1.0.1.17/32", "next_hop": "10.0.0.1"}, + {"network": "1.0.3.17/32", "next_hop": "10.0.0.10"}, + ] + }, + "r3": { + "static_routes": [ + {"network": "1.0.1.17/32", "next_hop": "10.0.0.5"}, + {"network": "1.0.2.17/32", "next_hop": "10.0.0.9"}, + {"network": "1.0.4.17/32", "next_hop": "10.0.0.14"}, + ] + }, + "r4": {"static_routes": [{"network": "1.0.3.17/32", "next_hop": "10.0.0.13"}]}, + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + # Api call verify whether BGP is converged + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_bgp_with_loopback_with_same_subnet_p1(request): + """ + Verify routes not installed in zebra when /32 routes received + with loopback BGP session subnet + """ + + tgen = get_topogen() + if BGP_CONVERGENCE is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + step("Delete BGP seesion created initially") + input_dict_r1 = { + "r1": {"bgp": {"delete": True}}, + "r2": {"bgp": {"delete": True}}, + "r3": {"bgp": {"delete": True}}, + "r4": {"bgp": {"delete": True}}, + } + result = create_router_bgp(tgen, topo, input_dict_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Create BGP session over loop address") + topo_modify = deepcopy(topo) + + for routerN in sorted(topo["routers"].keys()): + for addr_type in ADDR_TYPES: + for bgp_neighbor in topo_modify["routers"][routerN]["bgp"][ + "address_family" + ][addr_type]["unicast"]["neighbor"].keys(): + + # Adding ['source_link'] = 'lo' key:value pair + topo_modify["routers"][routerN]["bgp"]["address_family"][addr_type][ + "unicast" + ]["neighbor"][bgp_neighbor]["dest_link"] = { + "lo": {"source_link": "lo", "ebgp_multihop": 2} + } + + result = create_router_bgp(tgen, topo_modify["routers"]) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Disable IPv6 BGP nbr from ipv4 address family") + raw_config = { + "r1": { + "raw_config": [ + "router bgp {}".format(topo["routers"]["r1"]["bgp"]["local_as"]), + "address-family ipv4 unicast", + "no neighbor {} activate".format( + topo["routers"]["r2"]["links"]["lo"]["ipv6"].split("/")[0] + ), + "no neighbor {} activate".format( + topo["routers"]["r3"]["links"]["lo"]["ipv6"].split("/")[0] + ), + ] + }, + "r2": { + "raw_config": [ + "router bgp {}".format(topo["routers"]["r2"]["bgp"]["local_as"]), + "address-family ipv4 unicast", + "no neighbor {} activate".format( + topo["routers"]["r1"]["links"]["lo"]["ipv6"].split("/")[0] + ), + "no neighbor {} activate".format( + topo["routers"]["r3"]["links"]["lo"]["ipv6"].split("/")[0] + ), + ] + }, + "r3": { + "raw_config": [ + "router bgp {}".format(topo["routers"]["r3"]["bgp"]["local_as"]), + "address-family ipv4 unicast", + "no neighbor {} activate".format( + topo["routers"]["r1"]["links"]["lo"]["ipv6"].split("/")[0] + ), + "no neighbor {} activate".format( + topo["routers"]["r2"]["links"]["lo"]["ipv6"].split("/")[0] + ), + "no neighbor {} activate".format( + topo["routers"]["r4"]["links"]["lo"]["ipv6"].split("/")[0] + ), + ] + }, + "r4": { + "raw_config": [ + "router bgp {}".format(topo["routers"]["r4"]["bgp"]["local_as"]), + "address-family ipv4 unicast", + "no neighbor {} activate".format( + topo["routers"]["r3"]["links"]["lo"]["ipv6"].split("/")[0] + ), + ] + }, + } + + step("Configure kernel routes") + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + r1_ipv4_lo = topo["routers"]["r1"]["links"]["lo"]["ipv4"] + r1_ipv6_lo = topo["routers"]["r1"]["links"]["lo"]["ipv6"] + r2_ipv4_lo = topo["routers"]["r2"]["links"]["lo"]["ipv4"] + r2_ipv6_lo = topo["routers"]["r2"]["links"]["lo"]["ipv6"] + r3_ipv4_lo = topo["routers"]["r3"]["links"]["lo"]["ipv4"] + r3_ipv6_lo = topo["routers"]["r3"]["links"]["lo"]["ipv6"] + r4_ipv4_lo = topo["routers"]["r4"]["links"]["lo"]["ipv4"] + r4_ipv6_lo = topo["routers"]["r4"]["links"]["lo"]["ipv6"] + + r1_r2 = topo["routers"]["r1"]["links"]["r2"]["ipv6"].split("/")[0] + r2_r1 = topo["routers"]["r2"]["links"]["r1"]["ipv6"].split("/")[0] + r1_r3 = topo["routers"]["r1"]["links"]["r3"]["ipv6"].split("/")[0] + r3_r1 = topo["routers"]["r3"]["links"]["r1"]["ipv6"].split("/")[0] + r2_r3 = topo["routers"]["r2"]["links"]["r3"]["ipv6"].split("/")[0] + r3_r2 = topo["routers"]["r3"]["links"]["r2"]["ipv6"].split("/")[0] + r3_r4 = topo["routers"]["r3"]["links"]["r4"]["ipv6"].split("/")[0] + r4_r3 = topo["routers"]["r4"]["links"]["r3"]["ipv6"].split("/")[0] + + r1_r2_ipv4 = topo["routers"]["r1"]["links"]["r2"]["ipv4"].split("/")[0] + r2_r1_ipv4 = topo["routers"]["r2"]["links"]["r1"]["ipv4"].split("/")[0] + r1_r3_ipv4 = topo["routers"]["r1"]["links"]["r3"]["ipv4"].split("/")[0] + r3_r1_ipv4 = topo["routers"]["r3"]["links"]["r1"]["ipv4"].split("/")[0] + r2_r3_ipv4 = topo["routers"]["r2"]["links"]["r3"]["ipv4"].split("/")[0] + r3_r2_ipv4 = topo["routers"]["r3"]["links"]["r2"]["ipv4"].split("/")[0] + r3_r4_ipv4 = topo["routers"]["r3"]["links"]["r4"]["ipv4"].split("/")[0] + r4_r3_ipv4 = topo["routers"]["r4"]["links"]["r3"]["ipv4"].split("/")[0] + + r1_r2_intf = topo["routers"]["r1"]["links"]["r2"]["interface"] + r2_r1_intf = topo["routers"]["r2"]["links"]["r1"]["interface"] + r1_r3_intf = topo["routers"]["r1"]["links"]["r3"]["interface"] + r3_r1_intf = topo["routers"]["r3"]["links"]["r1"]["interface"] + r2_r3_intf = topo["routers"]["r2"]["links"]["r3"]["interface"] + r3_r2_intf = topo["routers"]["r3"]["links"]["r2"]["interface"] + r3_r4_intf = topo["routers"]["r3"]["links"]["r4"]["interface"] + r4_r3_intf = topo["routers"]["r4"]["links"]["r3"]["interface"] + + ipv4_list = [ + ("r1", r1_r2_intf, r2_ipv4_loopback), + ("r1", r1_r3_intf, r3_ipv4_loopback), + ("r2", r2_r1_intf, r1_ipv4_loopback), + ("r2", r2_r3_intf, r3_ipv4_loopback), + ("r3", r3_r1_intf, r1_ipv4_loopback), + ("r3", r3_r2_intf, r2_ipv4_loopback), + ("r3", r3_r4_intf, r4_ipv4_loopback), + ("r4", r4_r3_intf, r3_ipv4_loopback), + ] + + ipv6_list = [ + ("r1", r1_r2_intf, r2_ipv6_loopback, r2_r1), + ("r1", r1_r3_intf, r3_ipv6_loopback, r3_r1), + ("r2", r2_r1_intf, r1_ipv6_loopback, r1_r2), + ("r2", r2_r3_intf, r3_ipv6_loopback, r3_r2), + ("r3", r3_r1_intf, r1_ipv6_loopback, r1_r3), + ("r3", r3_r2_intf, r2_ipv6_loopback, r2_r3), + ("r3", r3_r4_intf, r4_ipv6_loopback, r4_r3), + ("r4", r4_r3_intf, r3_ipv6_loopback, r3_r4), + ] + + for dut, intf, loop_addr in ipv4_list: + result = addKernelRoute(tgen, dut, intf, loop_addr) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + for dut, intf, loop_addr, next_hop in ipv6_list: + result = addKernelRoute(tgen, dut, intf, loop_addr, next_hop) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + step("Configure static routes") + + input_dict = { + "r1": { + "static_routes": [ + {"network": r2_ipv4_loopback, "next_hop": r2_r1_ipv4}, + {"network": r3_ipv4_loopback, "next_hop": r3_r1_ipv4}, + {"network": r2_ipv6_loopback, "next_hop": r2_r1}, + {"network": r3_ipv6_loopback, "next_hop": r3_r1}, + ] + }, + "r2": { + "static_routes": [ + {"network": r1_ipv4_loopback, "next_hop": r1_r2_ipv4}, + {"network": r3_ipv4_loopback, "next_hop": r3_r2_ipv4}, + {"network": r1_ipv6_loopback, "next_hop": r1_r2}, + {"network": r3_ipv6_loopback, "next_hop": r3_r2}, + ] + }, + "r3": { + "static_routes": [ + {"network": r1_ipv4_loopback, "next_hop": r1_r3_ipv4}, + {"network": r2_ipv4_loopback, "next_hop": r2_r3_ipv4}, + {"network": r4_ipv4_loopback, "next_hop": r4_r3_ipv4}, + {"network": r1_ipv6_loopback, "next_hop": r1_r3}, + {"network": r2_ipv6_loopback, "next_hop": r2_r3}, + {"network": r4_ipv6_loopback, "next_hop": r4_r3}, + ] + }, + "r4": { + "static_routes": [ + {"network": r3_ipv4_loopback, "next_hop": r3_r4_ipv4}, + {"network": r3_ipv6_loopback, "next_hop": r3_r4}, + ] + }, + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verify BGP session convergence") + + result = verify_bgp_convergence(tgen, topo_modify) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Configure redistribute connected on R2 and R4") + input_dict_1 = { + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "connected"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "connected"}]} + }, + } + } + }, + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "connected"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "connected"}]} + }, + } + } + }, + } + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verify Ipv4 and Ipv6 network installed in R1 RIB but not in FIB") + input_dict_r1 = { + "r1": { + "static_routes": [ + {"network": "1.0.2.17/32"}, + {"network": "2001:db8:f::2:17/128"}, + ] + } + } + + dut = "r1" + protocol = "bgp" + for addr_type in ADDR_TYPES: + result = verify_rib(tgen, addr_type, dut, input_dict_r1, protocol=protocol) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_fib_routes(tgen, addr_type, dut, input_dict_r1, expected=False) + assert result is not True, "Testcase {} : Failed \n" + "Expected behavior: routes should not present in fib \n" + "Error: {}".format(tc_name, result) + + step("Verify Ipv4 and Ipv6 network installed in r3 RIB but not in FIB") + input_dict_r3 = { + "r3": { + "static_routes": [ + {"network": "1.0.4.17/32"}, + {"network": "2001:db8:f::4:17/128"}, + ] + } + } + dut = "r3" + protocol = "bgp" + for addr_type in ADDR_TYPES: + result = verify_rib( + tgen, addr_type, dut, input_dict_r3, protocol=protocol, fib=None + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_fib_routes(tgen, addr_type, dut, input_dict_r1, expected=False) + assert result is not True, "Testcase {} : Failed \n" + "Expected behavior: routes should not present in fib \n" + "Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp-ecmp-topo1/peer1/exa-receive.py b/tests/topotests/bgp-ecmp-topo1/peer1/exa-receive.py index 5334ea5369..031ff455ca 100755 --- a/tests/topotests/bgp-ecmp-topo1/peer1/exa-receive.py +++ b/tests/topotests/bgp-ecmp-topo1/peer1/exa-receive.py @@ -4,7 +4,7 @@ exa-receive.py: Save received routes form ExaBGP into file """ -from sys import stdin,argv +from sys import stdin, argv from datetime import datetime # 1st arg is peer number @@ -13,12 +13,12 @@ # When the parent dies we are seeing continual newlines, so we only access so many before stopping counter = 0 -routesavefile = open('/tmp/peer%s-received.log' % peer, 'w') +routesavefile = open("/tmp/peer%s-received.log" % peer, "w") while True: try: line = stdin.readline() - timestamp = datetime.now().strftime('%Y%m%d_%H:%M:%S - ') + timestamp = datetime.now().strftime("%Y%m%d_%H:%M:%S - ") routesavefile.write(timestamp + line) routesavefile.flush() diff --git a/tests/topotests/bgp-ecmp-topo1/peer1/exa-send.py b/tests/topotests/bgp-ecmp-topo1/peer1/exa-send.py index 647c254250..d9ae3d1906 100755 --- a/tests/topotests/bgp-ecmp-topo1/peer1/exa-send.py +++ b/tests/topotests/bgp-ecmp-topo1/peer1/exa-send.py @@ -4,7 +4,7 @@ exa-send.py: Send a few testroutes with ExaBGP """ -from sys import stdout,argv +from sys import stdout, argv from time import sleep sleep(5) @@ -13,37 +13,54 @@ # 2nd arg is number of routes to send peer = int(argv[1]) numRoutes = int(argv[2]) -if (peer <= 10): +if peer <= 10: asnum = 99 else: - asnum = peer+100 + asnum = peer + 100 # Announce numRoutes equal routes per PE - different neighbor AS for i in range(0, numRoutes): - stdout.write('announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n' % (i, (((peer-1) / 5) + 1), peer+100)) + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) stdout.flush() # Announce numRoutes equal routes per PE - different neighbor AS, but same source AS for i in range(0, numRoutes): - stdout.write('announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, (((peer-1) / 5) + 1), peer+100, asnum)) + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) stdout.flush() # Announce numRoutes equal routes with different med per PE and different neighbor AS for i in range(0, numRoutes): - stdout.write('announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n' % (i, peer, (((peer-1) / 5) + 1), peer+100)) + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) stdout.flush() # Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS for i in range(0, numRoutes): - stdout.write('announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, peer, (((peer-1) / 5) + 1), peer+100, asnum)) + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) stdout.flush() # Announce 2 different route per peer -stdout.write('announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n' % (peer, (((peer-1) / 5) + 1), peer+100)) -stdout.write('announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (peer, (((peer-1) / 5) + 1), peer+100, asnum)) +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) stdout.flush() -#Loop endlessly to allow ExaBGP to continue running +# Loop endlessly to allow ExaBGP to continue running while True: sleep(1) - diff --git a/tests/topotests/bgp-ecmp-topo1/peer10/exa-receive.py b/tests/topotests/bgp-ecmp-topo1/peer10/exa-receive.py index 5334ea5369..031ff455ca 100755 --- a/tests/topotests/bgp-ecmp-topo1/peer10/exa-receive.py +++ b/tests/topotests/bgp-ecmp-topo1/peer10/exa-receive.py @@ -4,7 +4,7 @@ exa-receive.py: Save received routes form ExaBGP into file """ -from sys import stdin,argv +from sys import stdin, argv from datetime import datetime # 1st arg is peer number @@ -13,12 +13,12 @@ # When the parent dies we are seeing continual newlines, so we only access so many before stopping counter = 0 -routesavefile = open('/tmp/peer%s-received.log' % peer, 'w') +routesavefile = open("/tmp/peer%s-received.log" % peer, "w") while True: try: line = stdin.readline() - timestamp = datetime.now().strftime('%Y%m%d_%H:%M:%S - ') + timestamp = datetime.now().strftime("%Y%m%d_%H:%M:%S - ") routesavefile.write(timestamp + line) routesavefile.flush() diff --git a/tests/topotests/bgp-ecmp-topo1/peer10/exa-send.py b/tests/topotests/bgp-ecmp-topo1/peer10/exa-send.py index 647c254250..d9ae3d1906 100755 --- a/tests/topotests/bgp-ecmp-topo1/peer10/exa-send.py +++ b/tests/topotests/bgp-ecmp-topo1/peer10/exa-send.py @@ -4,7 +4,7 @@ exa-send.py: Send a few testroutes with ExaBGP """ -from sys import stdout,argv +from sys import stdout, argv from time import sleep sleep(5) @@ -13,37 +13,54 @@ # 2nd arg is number of routes to send peer = int(argv[1]) numRoutes = int(argv[2]) -if (peer <= 10): +if peer <= 10: asnum = 99 else: - asnum = peer+100 + asnum = peer + 100 # Announce numRoutes equal routes per PE - different neighbor AS for i in range(0, numRoutes): - stdout.write('announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n' % (i, (((peer-1) / 5) + 1), peer+100)) + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) stdout.flush() # Announce numRoutes equal routes per PE - different neighbor AS, but same source AS for i in range(0, numRoutes): - stdout.write('announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, (((peer-1) / 5) + 1), peer+100, asnum)) + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) stdout.flush() # Announce numRoutes equal routes with different med per PE and different neighbor AS for i in range(0, numRoutes): - stdout.write('announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n' % (i, peer, (((peer-1) / 5) + 1), peer+100)) + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) stdout.flush() # Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS for i in range(0, numRoutes): - stdout.write('announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, peer, (((peer-1) / 5) + 1), peer+100, asnum)) + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) stdout.flush() # Announce 2 different route per peer -stdout.write('announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n' % (peer, (((peer-1) / 5) + 1), peer+100)) -stdout.write('announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (peer, (((peer-1) / 5) + 1), peer+100, asnum)) +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) stdout.flush() -#Loop endlessly to allow ExaBGP to continue running +# Loop endlessly to allow ExaBGP to continue running while True: sleep(1) - diff --git a/tests/topotests/bgp-ecmp-topo1/peer11/exa-receive.py b/tests/topotests/bgp-ecmp-topo1/peer11/exa-receive.py index 5334ea5369..031ff455ca 100755 --- a/tests/topotests/bgp-ecmp-topo1/peer11/exa-receive.py +++ b/tests/topotests/bgp-ecmp-topo1/peer11/exa-receive.py @@ -4,7 +4,7 @@ exa-receive.py: Save received routes form ExaBGP into file """ -from sys import stdin,argv +from sys import stdin, argv from datetime import datetime # 1st arg is peer number @@ -13,12 +13,12 @@ # When the parent dies we are seeing continual newlines, so we only access so many before stopping counter = 0 -routesavefile = open('/tmp/peer%s-received.log' % peer, 'w') +routesavefile = open("/tmp/peer%s-received.log" % peer, "w") while True: try: line = stdin.readline() - timestamp = datetime.now().strftime('%Y%m%d_%H:%M:%S - ') + timestamp = datetime.now().strftime("%Y%m%d_%H:%M:%S - ") routesavefile.write(timestamp + line) routesavefile.flush() diff --git a/tests/topotests/bgp-ecmp-topo1/peer11/exa-send.py b/tests/topotests/bgp-ecmp-topo1/peer11/exa-send.py index 647c254250..d9ae3d1906 100755 --- a/tests/topotests/bgp-ecmp-topo1/peer11/exa-send.py +++ b/tests/topotests/bgp-ecmp-topo1/peer11/exa-send.py @@ -4,7 +4,7 @@ exa-send.py: Send a few testroutes with ExaBGP """ -from sys import stdout,argv +from sys import stdout, argv from time import sleep sleep(5) @@ -13,37 +13,54 @@ # 2nd arg is number of routes to send peer = int(argv[1]) numRoutes = int(argv[2]) -if (peer <= 10): +if peer <= 10: asnum = 99 else: - asnum = peer+100 + asnum = peer + 100 # Announce numRoutes equal routes per PE - different neighbor AS for i in range(0, numRoutes): - stdout.write('announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n' % (i, (((peer-1) / 5) + 1), peer+100)) + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) stdout.flush() # Announce numRoutes equal routes per PE - different neighbor AS, but same source AS for i in range(0, numRoutes): - stdout.write('announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, (((peer-1) / 5) + 1), peer+100, asnum)) + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) stdout.flush() # Announce numRoutes equal routes with different med per PE and different neighbor AS for i in range(0, numRoutes): - stdout.write('announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n' % (i, peer, (((peer-1) / 5) + 1), peer+100)) + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) stdout.flush() # Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS for i in range(0, numRoutes): - stdout.write('announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, peer, (((peer-1) / 5) + 1), peer+100, asnum)) + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) stdout.flush() # Announce 2 different route per peer -stdout.write('announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n' % (peer, (((peer-1) / 5) + 1), peer+100)) -stdout.write('announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (peer, (((peer-1) / 5) + 1), peer+100, asnum)) +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) stdout.flush() -#Loop endlessly to allow ExaBGP to continue running +# Loop endlessly to allow ExaBGP to continue running while True: sleep(1) - diff --git a/tests/topotests/bgp-ecmp-topo1/peer12/exa-receive.py b/tests/topotests/bgp-ecmp-topo1/peer12/exa-receive.py index 5334ea5369..031ff455ca 100755 --- a/tests/topotests/bgp-ecmp-topo1/peer12/exa-receive.py +++ b/tests/topotests/bgp-ecmp-topo1/peer12/exa-receive.py @@ -4,7 +4,7 @@ exa-receive.py: Save received routes form ExaBGP into file """ -from sys import stdin,argv +from sys import stdin, argv from datetime import datetime # 1st arg is peer number @@ -13,12 +13,12 @@ # When the parent dies we are seeing continual newlines, so we only access so many before stopping counter = 0 -routesavefile = open('/tmp/peer%s-received.log' % peer, 'w') +routesavefile = open("/tmp/peer%s-received.log" % peer, "w") while True: try: line = stdin.readline() - timestamp = datetime.now().strftime('%Y%m%d_%H:%M:%S - ') + timestamp = datetime.now().strftime("%Y%m%d_%H:%M:%S - ") routesavefile.write(timestamp + line) routesavefile.flush() diff --git a/tests/topotests/bgp-ecmp-topo1/peer12/exa-send.py b/tests/topotests/bgp-ecmp-topo1/peer12/exa-send.py index 647c254250..d9ae3d1906 100755 --- a/tests/topotests/bgp-ecmp-topo1/peer12/exa-send.py +++ b/tests/topotests/bgp-ecmp-topo1/peer12/exa-send.py @@ -4,7 +4,7 @@ exa-send.py: Send a few testroutes with ExaBGP """ -from sys import stdout,argv +from sys import stdout, argv from time import sleep sleep(5) @@ -13,37 +13,54 @@ # 2nd arg is number of routes to send peer = int(argv[1]) numRoutes = int(argv[2]) -if (peer <= 10): +if peer <= 10: asnum = 99 else: - asnum = peer+100 + asnum = peer + 100 # Announce numRoutes equal routes per PE - different neighbor AS for i in range(0, numRoutes): - stdout.write('announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n' % (i, (((peer-1) / 5) + 1), peer+100)) + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) stdout.flush() # Announce numRoutes equal routes per PE - different neighbor AS, but same source AS for i in range(0, numRoutes): - stdout.write('announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, (((peer-1) / 5) + 1), peer+100, asnum)) + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) stdout.flush() # Announce numRoutes equal routes with different med per PE and different neighbor AS for i in range(0, numRoutes): - stdout.write('announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n' % (i, peer, (((peer-1) / 5) + 1), peer+100)) + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) stdout.flush() # Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS for i in range(0, numRoutes): - stdout.write('announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, peer, (((peer-1) / 5) + 1), peer+100, asnum)) + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) stdout.flush() # Announce 2 different route per peer -stdout.write('announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n' % (peer, (((peer-1) / 5) + 1), peer+100)) -stdout.write('announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (peer, (((peer-1) / 5) + 1), peer+100, asnum)) +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) stdout.flush() -#Loop endlessly to allow ExaBGP to continue running +# Loop endlessly to allow ExaBGP to continue running while True: sleep(1) - diff --git a/tests/topotests/bgp-ecmp-topo1/peer13/exa-receive.py b/tests/topotests/bgp-ecmp-topo1/peer13/exa-receive.py index 5334ea5369..031ff455ca 100755 --- a/tests/topotests/bgp-ecmp-topo1/peer13/exa-receive.py +++ b/tests/topotests/bgp-ecmp-topo1/peer13/exa-receive.py @@ -4,7 +4,7 @@ exa-receive.py: Save received routes form ExaBGP into file """ -from sys import stdin,argv +from sys import stdin, argv from datetime import datetime # 1st arg is peer number @@ -13,12 +13,12 @@ # When the parent dies we are seeing continual newlines, so we only access so many before stopping counter = 0 -routesavefile = open('/tmp/peer%s-received.log' % peer, 'w') +routesavefile = open("/tmp/peer%s-received.log" % peer, "w") while True: try: line = stdin.readline() - timestamp = datetime.now().strftime('%Y%m%d_%H:%M:%S - ') + timestamp = datetime.now().strftime("%Y%m%d_%H:%M:%S - ") routesavefile.write(timestamp + line) routesavefile.flush() diff --git a/tests/topotests/bgp-ecmp-topo1/peer13/exa-send.py b/tests/topotests/bgp-ecmp-topo1/peer13/exa-send.py index 647c254250..d9ae3d1906 100755 --- a/tests/topotests/bgp-ecmp-topo1/peer13/exa-send.py +++ b/tests/topotests/bgp-ecmp-topo1/peer13/exa-send.py @@ -4,7 +4,7 @@ exa-send.py: Send a few testroutes with ExaBGP """ -from sys import stdout,argv +from sys import stdout, argv from time import sleep sleep(5) @@ -13,37 +13,54 @@ # 2nd arg is number of routes to send peer = int(argv[1]) numRoutes = int(argv[2]) -if (peer <= 10): +if peer <= 10: asnum = 99 else: - asnum = peer+100 + asnum = peer + 100 # Announce numRoutes equal routes per PE - different neighbor AS for i in range(0, numRoutes): - stdout.write('announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n' % (i, (((peer-1) / 5) + 1), peer+100)) + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) stdout.flush() # Announce numRoutes equal routes per PE - different neighbor AS, but same source AS for i in range(0, numRoutes): - stdout.write('announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, (((peer-1) / 5) + 1), peer+100, asnum)) + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) stdout.flush() # Announce numRoutes equal routes with different med per PE and different neighbor AS for i in range(0, numRoutes): - stdout.write('announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n' % (i, peer, (((peer-1) / 5) + 1), peer+100)) + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) stdout.flush() # Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS for i in range(0, numRoutes): - stdout.write('announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, peer, (((peer-1) / 5) + 1), peer+100, asnum)) + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) stdout.flush() # Announce 2 different route per peer -stdout.write('announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n' % (peer, (((peer-1) / 5) + 1), peer+100)) -stdout.write('announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (peer, (((peer-1) / 5) + 1), peer+100, asnum)) +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) stdout.flush() -#Loop endlessly to allow ExaBGP to continue running +# Loop endlessly to allow ExaBGP to continue running while True: sleep(1) - diff --git a/tests/topotests/bgp-ecmp-topo1/peer14/exa-receive.py b/tests/topotests/bgp-ecmp-topo1/peer14/exa-receive.py index 5334ea5369..031ff455ca 100755 --- a/tests/topotests/bgp-ecmp-topo1/peer14/exa-receive.py +++ b/tests/topotests/bgp-ecmp-topo1/peer14/exa-receive.py @@ -4,7 +4,7 @@ exa-receive.py: Save received routes form ExaBGP into file """ -from sys import stdin,argv +from sys import stdin, argv from datetime import datetime # 1st arg is peer number @@ -13,12 +13,12 @@ # When the parent dies we are seeing continual newlines, so we only access so many before stopping counter = 0 -routesavefile = open('/tmp/peer%s-received.log' % peer, 'w') +routesavefile = open("/tmp/peer%s-received.log" % peer, "w") while True: try: line = stdin.readline() - timestamp = datetime.now().strftime('%Y%m%d_%H:%M:%S - ') + timestamp = datetime.now().strftime("%Y%m%d_%H:%M:%S - ") routesavefile.write(timestamp + line) routesavefile.flush() diff --git a/tests/topotests/bgp-ecmp-topo1/peer14/exa-send.py b/tests/topotests/bgp-ecmp-topo1/peer14/exa-send.py index 647c254250..d9ae3d1906 100755 --- a/tests/topotests/bgp-ecmp-topo1/peer14/exa-send.py +++ b/tests/topotests/bgp-ecmp-topo1/peer14/exa-send.py @@ -4,7 +4,7 @@ exa-send.py: Send a few testroutes with ExaBGP """ -from sys import stdout,argv +from sys import stdout, argv from time import sleep sleep(5) @@ -13,37 +13,54 @@ # 2nd arg is number of routes to send peer = int(argv[1]) numRoutes = int(argv[2]) -if (peer <= 10): +if peer <= 10: asnum = 99 else: - asnum = peer+100 + asnum = peer + 100 # Announce numRoutes equal routes per PE - different neighbor AS for i in range(0, numRoutes): - stdout.write('announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n' % (i, (((peer-1) / 5) + 1), peer+100)) + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) stdout.flush() # Announce numRoutes equal routes per PE - different neighbor AS, but same source AS for i in range(0, numRoutes): - stdout.write('announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, (((peer-1) / 5) + 1), peer+100, asnum)) + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) stdout.flush() # Announce numRoutes equal routes with different med per PE and different neighbor AS for i in range(0, numRoutes): - stdout.write('announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n' % (i, peer, (((peer-1) / 5) + 1), peer+100)) + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) stdout.flush() # Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS for i in range(0, numRoutes): - stdout.write('announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, peer, (((peer-1) / 5) + 1), peer+100, asnum)) + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) stdout.flush() # Announce 2 different route per peer -stdout.write('announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n' % (peer, (((peer-1) / 5) + 1), peer+100)) -stdout.write('announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (peer, (((peer-1) / 5) + 1), peer+100, asnum)) +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) stdout.flush() -#Loop endlessly to allow ExaBGP to continue running +# Loop endlessly to allow ExaBGP to continue running while True: sleep(1) - diff --git a/tests/topotests/bgp-ecmp-topo1/peer15/exa-receive.py b/tests/topotests/bgp-ecmp-topo1/peer15/exa-receive.py index 5334ea5369..031ff455ca 100755 --- a/tests/topotests/bgp-ecmp-topo1/peer15/exa-receive.py +++ b/tests/topotests/bgp-ecmp-topo1/peer15/exa-receive.py @@ -4,7 +4,7 @@ exa-receive.py: Save received routes form ExaBGP into file """ -from sys import stdin,argv +from sys import stdin, argv from datetime import datetime # 1st arg is peer number @@ -13,12 +13,12 @@ # When the parent dies we are seeing continual newlines, so we only access so many before stopping counter = 0 -routesavefile = open('/tmp/peer%s-received.log' % peer, 'w') +routesavefile = open("/tmp/peer%s-received.log" % peer, "w") while True: try: line = stdin.readline() - timestamp = datetime.now().strftime('%Y%m%d_%H:%M:%S - ') + timestamp = datetime.now().strftime("%Y%m%d_%H:%M:%S - ") routesavefile.write(timestamp + line) routesavefile.flush() diff --git a/tests/topotests/bgp-ecmp-topo1/peer15/exa-send.py b/tests/topotests/bgp-ecmp-topo1/peer15/exa-send.py index 647c254250..d9ae3d1906 100755 --- a/tests/topotests/bgp-ecmp-topo1/peer15/exa-send.py +++ b/tests/topotests/bgp-ecmp-topo1/peer15/exa-send.py @@ -4,7 +4,7 @@ exa-send.py: Send a few testroutes with ExaBGP """ -from sys import stdout,argv +from sys import stdout, argv from time import sleep sleep(5) @@ -13,37 +13,54 @@ # 2nd arg is number of routes to send peer = int(argv[1]) numRoutes = int(argv[2]) -if (peer <= 10): +if peer <= 10: asnum = 99 else: - asnum = peer+100 + asnum = peer + 100 # Announce numRoutes equal routes per PE - different neighbor AS for i in range(0, numRoutes): - stdout.write('announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n' % (i, (((peer-1) / 5) + 1), peer+100)) + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) stdout.flush() # Announce numRoutes equal routes per PE - different neighbor AS, but same source AS for i in range(0, numRoutes): - stdout.write('announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, (((peer-1) / 5) + 1), peer+100, asnum)) + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) stdout.flush() # Announce numRoutes equal routes with different med per PE and different neighbor AS for i in range(0, numRoutes): - stdout.write('announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n' % (i, peer, (((peer-1) / 5) + 1), peer+100)) + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) stdout.flush() # Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS for i in range(0, numRoutes): - stdout.write('announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, peer, (((peer-1) / 5) + 1), peer+100, asnum)) + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) stdout.flush() # Announce 2 different route per peer -stdout.write('announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n' % (peer, (((peer-1) / 5) + 1), peer+100)) -stdout.write('announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (peer, (((peer-1) / 5) + 1), peer+100, asnum)) +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) stdout.flush() -#Loop endlessly to allow ExaBGP to continue running +# Loop endlessly to allow ExaBGP to continue running while True: sleep(1) - diff --git a/tests/topotests/bgp-ecmp-topo1/peer16/exa-receive.py b/tests/topotests/bgp-ecmp-topo1/peer16/exa-receive.py index 5334ea5369..031ff455ca 100755 --- a/tests/topotests/bgp-ecmp-topo1/peer16/exa-receive.py +++ b/tests/topotests/bgp-ecmp-topo1/peer16/exa-receive.py @@ -4,7 +4,7 @@ exa-receive.py: Save received routes form ExaBGP into file """ -from sys import stdin,argv +from sys import stdin, argv from datetime import datetime # 1st arg is peer number @@ -13,12 +13,12 @@ # When the parent dies we are seeing continual newlines, so we only access so many before stopping counter = 0 -routesavefile = open('/tmp/peer%s-received.log' % peer, 'w') +routesavefile = open("/tmp/peer%s-received.log" % peer, "w") while True: try: line = stdin.readline() - timestamp = datetime.now().strftime('%Y%m%d_%H:%M:%S - ') + timestamp = datetime.now().strftime("%Y%m%d_%H:%M:%S - ") routesavefile.write(timestamp + line) routesavefile.flush() diff --git a/tests/topotests/bgp-ecmp-topo1/peer16/exa-send.py b/tests/topotests/bgp-ecmp-topo1/peer16/exa-send.py index 647c254250..d9ae3d1906 100755 --- a/tests/topotests/bgp-ecmp-topo1/peer16/exa-send.py +++ b/tests/topotests/bgp-ecmp-topo1/peer16/exa-send.py @@ -4,7 +4,7 @@ exa-send.py: Send a few testroutes with ExaBGP """ -from sys import stdout,argv +from sys import stdout, argv from time import sleep sleep(5) @@ -13,37 +13,54 @@ # 2nd arg is number of routes to send peer = int(argv[1]) numRoutes = int(argv[2]) -if (peer <= 10): +if peer <= 10: asnum = 99 else: - asnum = peer+100 + asnum = peer + 100 # Announce numRoutes equal routes per PE - different neighbor AS for i in range(0, numRoutes): - stdout.write('announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n' % (i, (((peer-1) / 5) + 1), peer+100)) + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) stdout.flush() # Announce numRoutes equal routes per PE - different neighbor AS, but same source AS for i in range(0, numRoutes): - stdout.write('announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, (((peer-1) / 5) + 1), peer+100, asnum)) + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) stdout.flush() # Announce numRoutes equal routes with different med per PE and different neighbor AS for i in range(0, numRoutes): - stdout.write('announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n' % (i, peer, (((peer-1) / 5) + 1), peer+100)) + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) stdout.flush() # Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS for i in range(0, numRoutes): - stdout.write('announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, peer, (((peer-1) / 5) + 1), peer+100, asnum)) + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) stdout.flush() # Announce 2 different route per peer -stdout.write('announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n' % (peer, (((peer-1) / 5) + 1), peer+100)) -stdout.write('announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (peer, (((peer-1) / 5) + 1), peer+100, asnum)) +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) stdout.flush() -#Loop endlessly to allow ExaBGP to continue running +# Loop endlessly to allow ExaBGP to continue running while True: sleep(1) - diff --git a/tests/topotests/bgp-ecmp-topo1/peer17/exa-receive.py b/tests/topotests/bgp-ecmp-topo1/peer17/exa-receive.py index 5334ea5369..031ff455ca 100755 --- a/tests/topotests/bgp-ecmp-topo1/peer17/exa-receive.py +++ b/tests/topotests/bgp-ecmp-topo1/peer17/exa-receive.py @@ -4,7 +4,7 @@ exa-receive.py: Save received routes form ExaBGP into file """ -from sys import stdin,argv +from sys import stdin, argv from datetime import datetime # 1st arg is peer number @@ -13,12 +13,12 @@ # When the parent dies we are seeing continual newlines, so we only access so many before stopping counter = 0 -routesavefile = open('/tmp/peer%s-received.log' % peer, 'w') +routesavefile = open("/tmp/peer%s-received.log" % peer, "w") while True: try: line = stdin.readline() - timestamp = datetime.now().strftime('%Y%m%d_%H:%M:%S - ') + timestamp = datetime.now().strftime("%Y%m%d_%H:%M:%S - ") routesavefile.write(timestamp + line) routesavefile.flush() diff --git a/tests/topotests/bgp-ecmp-topo1/peer17/exa-send.py b/tests/topotests/bgp-ecmp-topo1/peer17/exa-send.py index 647c254250..d9ae3d1906 100755 --- a/tests/topotests/bgp-ecmp-topo1/peer17/exa-send.py +++ b/tests/topotests/bgp-ecmp-topo1/peer17/exa-send.py @@ -4,7 +4,7 @@ exa-send.py: Send a few testroutes with ExaBGP """ -from sys import stdout,argv +from sys import stdout, argv from time import sleep sleep(5) @@ -13,37 +13,54 @@ # 2nd arg is number of routes to send peer = int(argv[1]) numRoutes = int(argv[2]) -if (peer <= 10): +if peer <= 10: asnum = 99 else: - asnum = peer+100 + asnum = peer + 100 # Announce numRoutes equal routes per PE - different neighbor AS for i in range(0, numRoutes): - stdout.write('announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n' % (i, (((peer-1) / 5) + 1), peer+100)) + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) stdout.flush() # Announce numRoutes equal routes per PE - different neighbor AS, but same source AS for i in range(0, numRoutes): - stdout.write('announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, (((peer-1) / 5) + 1), peer+100, asnum)) + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) stdout.flush() # Announce numRoutes equal routes with different med per PE and different neighbor AS for i in range(0, numRoutes): - stdout.write('announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n' % (i, peer, (((peer-1) / 5) + 1), peer+100)) + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) stdout.flush() # Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS for i in range(0, numRoutes): - stdout.write('announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, peer, (((peer-1) / 5) + 1), peer+100, asnum)) + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) stdout.flush() # Announce 2 different route per peer -stdout.write('announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n' % (peer, (((peer-1) / 5) + 1), peer+100)) -stdout.write('announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (peer, (((peer-1) / 5) + 1), peer+100, asnum)) +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) stdout.flush() -#Loop endlessly to allow ExaBGP to continue running +# Loop endlessly to allow ExaBGP to continue running while True: sleep(1) - diff --git a/tests/topotests/bgp-ecmp-topo1/peer18/exa-receive.py b/tests/topotests/bgp-ecmp-topo1/peer18/exa-receive.py index 5334ea5369..031ff455ca 100755 --- a/tests/topotests/bgp-ecmp-topo1/peer18/exa-receive.py +++ b/tests/topotests/bgp-ecmp-topo1/peer18/exa-receive.py @@ -4,7 +4,7 @@ exa-receive.py: Save received routes form ExaBGP into file """ -from sys import stdin,argv +from sys import stdin, argv from datetime import datetime # 1st arg is peer number @@ -13,12 +13,12 @@ # When the parent dies we are seeing continual newlines, so we only access so many before stopping counter = 0 -routesavefile = open('/tmp/peer%s-received.log' % peer, 'w') +routesavefile = open("/tmp/peer%s-received.log" % peer, "w") while True: try: line = stdin.readline() - timestamp = datetime.now().strftime('%Y%m%d_%H:%M:%S - ') + timestamp = datetime.now().strftime("%Y%m%d_%H:%M:%S - ") routesavefile.write(timestamp + line) routesavefile.flush() diff --git a/tests/topotests/bgp-ecmp-topo1/peer18/exa-send.py b/tests/topotests/bgp-ecmp-topo1/peer18/exa-send.py index 647c254250..d9ae3d1906 100755 --- a/tests/topotests/bgp-ecmp-topo1/peer18/exa-send.py +++ b/tests/topotests/bgp-ecmp-topo1/peer18/exa-send.py @@ -4,7 +4,7 @@ exa-send.py: Send a few testroutes with ExaBGP """ -from sys import stdout,argv +from sys import stdout, argv from time import sleep sleep(5) @@ -13,37 +13,54 @@ # 2nd arg is number of routes to send peer = int(argv[1]) numRoutes = int(argv[2]) -if (peer <= 10): +if peer <= 10: asnum = 99 else: - asnum = peer+100 + asnum = peer + 100 # Announce numRoutes equal routes per PE - different neighbor AS for i in range(0, numRoutes): - stdout.write('announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n' % (i, (((peer-1) / 5) + 1), peer+100)) + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) stdout.flush() # Announce numRoutes equal routes per PE - different neighbor AS, but same source AS for i in range(0, numRoutes): - stdout.write('announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, (((peer-1) / 5) + 1), peer+100, asnum)) + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) stdout.flush() # Announce numRoutes equal routes with different med per PE and different neighbor AS for i in range(0, numRoutes): - stdout.write('announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n' % (i, peer, (((peer-1) / 5) + 1), peer+100)) + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) stdout.flush() # Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS for i in range(0, numRoutes): - stdout.write('announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, peer, (((peer-1) / 5) + 1), peer+100, asnum)) + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) stdout.flush() # Announce 2 different route per peer -stdout.write('announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n' % (peer, (((peer-1) / 5) + 1), peer+100)) -stdout.write('announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (peer, (((peer-1) / 5) + 1), peer+100, asnum)) +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) stdout.flush() -#Loop endlessly to allow ExaBGP to continue running +# Loop endlessly to allow ExaBGP to continue running while True: sleep(1) - diff --git a/tests/topotests/bgp-ecmp-topo1/peer19/exa-receive.py b/tests/topotests/bgp-ecmp-topo1/peer19/exa-receive.py index 5334ea5369..031ff455ca 100755 --- a/tests/topotests/bgp-ecmp-topo1/peer19/exa-receive.py +++ b/tests/topotests/bgp-ecmp-topo1/peer19/exa-receive.py @@ -4,7 +4,7 @@ exa-receive.py: Save received routes form ExaBGP into file """ -from sys import stdin,argv +from sys import stdin, argv from datetime import datetime # 1st arg is peer number @@ -13,12 +13,12 @@ # When the parent dies we are seeing continual newlines, so we only access so many before stopping counter = 0 -routesavefile = open('/tmp/peer%s-received.log' % peer, 'w') +routesavefile = open("/tmp/peer%s-received.log" % peer, "w") while True: try: line = stdin.readline() - timestamp = datetime.now().strftime('%Y%m%d_%H:%M:%S - ') + timestamp = datetime.now().strftime("%Y%m%d_%H:%M:%S - ") routesavefile.write(timestamp + line) routesavefile.flush() diff --git a/tests/topotests/bgp-ecmp-topo1/peer19/exa-send.py b/tests/topotests/bgp-ecmp-topo1/peer19/exa-send.py index 647c254250..d9ae3d1906 100755 --- a/tests/topotests/bgp-ecmp-topo1/peer19/exa-send.py +++ b/tests/topotests/bgp-ecmp-topo1/peer19/exa-send.py @@ -4,7 +4,7 @@ exa-send.py: Send a few testroutes with ExaBGP """ -from sys import stdout,argv +from sys import stdout, argv from time import sleep sleep(5) @@ -13,37 +13,54 @@ # 2nd arg is number of routes to send peer = int(argv[1]) numRoutes = int(argv[2]) -if (peer <= 10): +if peer <= 10: asnum = 99 else: - asnum = peer+100 + asnum = peer + 100 # Announce numRoutes equal routes per PE - different neighbor AS for i in range(0, numRoutes): - stdout.write('announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n' % (i, (((peer-1) / 5) + 1), peer+100)) + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) stdout.flush() # Announce numRoutes equal routes per PE - different neighbor AS, but same source AS for i in range(0, numRoutes): - stdout.write('announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, (((peer-1) / 5) + 1), peer+100, asnum)) + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) stdout.flush() # Announce numRoutes equal routes with different med per PE and different neighbor AS for i in range(0, numRoutes): - stdout.write('announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n' % (i, peer, (((peer-1) / 5) + 1), peer+100)) + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) stdout.flush() # Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS for i in range(0, numRoutes): - stdout.write('announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, peer, (((peer-1) / 5) + 1), peer+100, asnum)) + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) stdout.flush() # Announce 2 different route per peer -stdout.write('announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n' % (peer, (((peer-1) / 5) + 1), peer+100)) -stdout.write('announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (peer, (((peer-1) / 5) + 1), peer+100, asnum)) +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) stdout.flush() -#Loop endlessly to allow ExaBGP to continue running +# Loop endlessly to allow ExaBGP to continue running while True: sleep(1) - diff --git a/tests/topotests/bgp-ecmp-topo1/peer2/exa-receive.py b/tests/topotests/bgp-ecmp-topo1/peer2/exa-receive.py index 5334ea5369..031ff455ca 100755 --- a/tests/topotests/bgp-ecmp-topo1/peer2/exa-receive.py +++ b/tests/topotests/bgp-ecmp-topo1/peer2/exa-receive.py @@ -4,7 +4,7 @@ exa-receive.py: Save received routes form ExaBGP into file """ -from sys import stdin,argv +from sys import stdin, argv from datetime import datetime # 1st arg is peer number @@ -13,12 +13,12 @@ # When the parent dies we are seeing continual newlines, so we only access so many before stopping counter = 0 -routesavefile = open('/tmp/peer%s-received.log' % peer, 'w') +routesavefile = open("/tmp/peer%s-received.log" % peer, "w") while True: try: line = stdin.readline() - timestamp = datetime.now().strftime('%Y%m%d_%H:%M:%S - ') + timestamp = datetime.now().strftime("%Y%m%d_%H:%M:%S - ") routesavefile.write(timestamp + line) routesavefile.flush() diff --git a/tests/topotests/bgp-ecmp-topo1/peer2/exa-send.py b/tests/topotests/bgp-ecmp-topo1/peer2/exa-send.py index 647c254250..d9ae3d1906 100755 --- a/tests/topotests/bgp-ecmp-topo1/peer2/exa-send.py +++ b/tests/topotests/bgp-ecmp-topo1/peer2/exa-send.py @@ -4,7 +4,7 @@ exa-send.py: Send a few testroutes with ExaBGP """ -from sys import stdout,argv +from sys import stdout, argv from time import sleep sleep(5) @@ -13,37 +13,54 @@ # 2nd arg is number of routes to send peer = int(argv[1]) numRoutes = int(argv[2]) -if (peer <= 10): +if peer <= 10: asnum = 99 else: - asnum = peer+100 + asnum = peer + 100 # Announce numRoutes equal routes per PE - different neighbor AS for i in range(0, numRoutes): - stdout.write('announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n' % (i, (((peer-1) / 5) + 1), peer+100)) + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) stdout.flush() # Announce numRoutes equal routes per PE - different neighbor AS, but same source AS for i in range(0, numRoutes): - stdout.write('announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, (((peer-1) / 5) + 1), peer+100, asnum)) + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) stdout.flush() # Announce numRoutes equal routes with different med per PE and different neighbor AS for i in range(0, numRoutes): - stdout.write('announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n' % (i, peer, (((peer-1) / 5) + 1), peer+100)) + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) stdout.flush() # Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS for i in range(0, numRoutes): - stdout.write('announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, peer, (((peer-1) / 5) + 1), peer+100, asnum)) + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) stdout.flush() # Announce 2 different route per peer -stdout.write('announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n' % (peer, (((peer-1) / 5) + 1), peer+100)) -stdout.write('announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (peer, (((peer-1) / 5) + 1), peer+100, asnum)) +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) stdout.flush() -#Loop endlessly to allow ExaBGP to continue running +# Loop endlessly to allow ExaBGP to continue running while True: sleep(1) - diff --git a/tests/topotests/bgp-ecmp-topo1/peer20/exa-receive.py b/tests/topotests/bgp-ecmp-topo1/peer20/exa-receive.py index 5334ea5369..031ff455ca 100755 --- a/tests/topotests/bgp-ecmp-topo1/peer20/exa-receive.py +++ b/tests/topotests/bgp-ecmp-topo1/peer20/exa-receive.py @@ -4,7 +4,7 @@ exa-receive.py: Save received routes form ExaBGP into file """ -from sys import stdin,argv +from sys import stdin, argv from datetime import datetime # 1st arg is peer number @@ -13,12 +13,12 @@ # When the parent dies we are seeing continual newlines, so we only access so many before stopping counter = 0 -routesavefile = open('/tmp/peer%s-received.log' % peer, 'w') +routesavefile = open("/tmp/peer%s-received.log" % peer, "w") while True: try: line = stdin.readline() - timestamp = datetime.now().strftime('%Y%m%d_%H:%M:%S - ') + timestamp = datetime.now().strftime("%Y%m%d_%H:%M:%S - ") routesavefile.write(timestamp + line) routesavefile.flush() diff --git a/tests/topotests/bgp-ecmp-topo1/peer20/exa-send.py b/tests/topotests/bgp-ecmp-topo1/peer20/exa-send.py index 647c254250..d9ae3d1906 100755 --- a/tests/topotests/bgp-ecmp-topo1/peer20/exa-send.py +++ b/tests/topotests/bgp-ecmp-topo1/peer20/exa-send.py @@ -4,7 +4,7 @@ exa-send.py: Send a few testroutes with ExaBGP """ -from sys import stdout,argv +from sys import stdout, argv from time import sleep sleep(5) @@ -13,37 +13,54 @@ # 2nd arg is number of routes to send peer = int(argv[1]) numRoutes = int(argv[2]) -if (peer <= 10): +if peer <= 10: asnum = 99 else: - asnum = peer+100 + asnum = peer + 100 # Announce numRoutes equal routes per PE - different neighbor AS for i in range(0, numRoutes): - stdout.write('announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n' % (i, (((peer-1) / 5) + 1), peer+100)) + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) stdout.flush() # Announce numRoutes equal routes per PE - different neighbor AS, but same source AS for i in range(0, numRoutes): - stdout.write('announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, (((peer-1) / 5) + 1), peer+100, asnum)) + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) stdout.flush() # Announce numRoutes equal routes with different med per PE and different neighbor AS for i in range(0, numRoutes): - stdout.write('announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n' % (i, peer, (((peer-1) / 5) + 1), peer+100)) + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) stdout.flush() # Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS for i in range(0, numRoutes): - stdout.write('announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, peer, (((peer-1) / 5) + 1), peer+100, asnum)) + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) stdout.flush() # Announce 2 different route per peer -stdout.write('announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n' % (peer, (((peer-1) / 5) + 1), peer+100)) -stdout.write('announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (peer, (((peer-1) / 5) + 1), peer+100, asnum)) +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) stdout.flush() -#Loop endlessly to allow ExaBGP to continue running +# Loop endlessly to allow ExaBGP to continue running while True: sleep(1) - diff --git a/tests/topotests/bgp-ecmp-topo1/peer3/exa-receive.py b/tests/topotests/bgp-ecmp-topo1/peer3/exa-receive.py index 5334ea5369..031ff455ca 100755 --- a/tests/topotests/bgp-ecmp-topo1/peer3/exa-receive.py +++ b/tests/topotests/bgp-ecmp-topo1/peer3/exa-receive.py @@ -4,7 +4,7 @@ exa-receive.py: Save received routes form ExaBGP into file """ -from sys import stdin,argv +from sys import stdin, argv from datetime import datetime # 1st arg is peer number @@ -13,12 +13,12 @@ # When the parent dies we are seeing continual newlines, so we only access so many before stopping counter = 0 -routesavefile = open('/tmp/peer%s-received.log' % peer, 'w') +routesavefile = open("/tmp/peer%s-received.log" % peer, "w") while True: try: line = stdin.readline() - timestamp = datetime.now().strftime('%Y%m%d_%H:%M:%S - ') + timestamp = datetime.now().strftime("%Y%m%d_%H:%M:%S - ") routesavefile.write(timestamp + line) routesavefile.flush() diff --git a/tests/topotests/bgp-ecmp-topo1/peer3/exa-send.py b/tests/topotests/bgp-ecmp-topo1/peer3/exa-send.py index 647c254250..d9ae3d1906 100755 --- a/tests/topotests/bgp-ecmp-topo1/peer3/exa-send.py +++ b/tests/topotests/bgp-ecmp-topo1/peer3/exa-send.py @@ -4,7 +4,7 @@ exa-send.py: Send a few testroutes with ExaBGP """ -from sys import stdout,argv +from sys import stdout, argv from time import sleep sleep(5) @@ -13,37 +13,54 @@ # 2nd arg is number of routes to send peer = int(argv[1]) numRoutes = int(argv[2]) -if (peer <= 10): +if peer <= 10: asnum = 99 else: - asnum = peer+100 + asnum = peer + 100 # Announce numRoutes equal routes per PE - different neighbor AS for i in range(0, numRoutes): - stdout.write('announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n' % (i, (((peer-1) / 5) + 1), peer+100)) + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) stdout.flush() # Announce numRoutes equal routes per PE - different neighbor AS, but same source AS for i in range(0, numRoutes): - stdout.write('announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, (((peer-1) / 5) + 1), peer+100, asnum)) + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) stdout.flush() # Announce numRoutes equal routes with different med per PE and different neighbor AS for i in range(0, numRoutes): - stdout.write('announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n' % (i, peer, (((peer-1) / 5) + 1), peer+100)) + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) stdout.flush() # Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS for i in range(0, numRoutes): - stdout.write('announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, peer, (((peer-1) / 5) + 1), peer+100, asnum)) + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) stdout.flush() # Announce 2 different route per peer -stdout.write('announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n' % (peer, (((peer-1) / 5) + 1), peer+100)) -stdout.write('announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (peer, (((peer-1) / 5) + 1), peer+100, asnum)) +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) stdout.flush() -#Loop endlessly to allow ExaBGP to continue running +# Loop endlessly to allow ExaBGP to continue running while True: sleep(1) - diff --git a/tests/topotests/bgp-ecmp-topo1/peer4/exa-receive.py b/tests/topotests/bgp-ecmp-topo1/peer4/exa-receive.py index 5334ea5369..031ff455ca 100755 --- a/tests/topotests/bgp-ecmp-topo1/peer4/exa-receive.py +++ b/tests/topotests/bgp-ecmp-topo1/peer4/exa-receive.py @@ -4,7 +4,7 @@ exa-receive.py: Save received routes form ExaBGP into file """ -from sys import stdin,argv +from sys import stdin, argv from datetime import datetime # 1st arg is peer number @@ -13,12 +13,12 @@ # When the parent dies we are seeing continual newlines, so we only access so many before stopping counter = 0 -routesavefile = open('/tmp/peer%s-received.log' % peer, 'w') +routesavefile = open("/tmp/peer%s-received.log" % peer, "w") while True: try: line = stdin.readline() - timestamp = datetime.now().strftime('%Y%m%d_%H:%M:%S - ') + timestamp = datetime.now().strftime("%Y%m%d_%H:%M:%S - ") routesavefile.write(timestamp + line) routesavefile.flush() diff --git a/tests/topotests/bgp-ecmp-topo1/peer4/exa-send.py b/tests/topotests/bgp-ecmp-topo1/peer4/exa-send.py index 647c254250..d9ae3d1906 100755 --- a/tests/topotests/bgp-ecmp-topo1/peer4/exa-send.py +++ b/tests/topotests/bgp-ecmp-topo1/peer4/exa-send.py @@ -4,7 +4,7 @@ exa-send.py: Send a few testroutes with ExaBGP """ -from sys import stdout,argv +from sys import stdout, argv from time import sleep sleep(5) @@ -13,37 +13,54 @@ # 2nd arg is number of routes to send peer = int(argv[1]) numRoutes = int(argv[2]) -if (peer <= 10): +if peer <= 10: asnum = 99 else: - asnum = peer+100 + asnum = peer + 100 # Announce numRoutes equal routes per PE - different neighbor AS for i in range(0, numRoutes): - stdout.write('announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n' % (i, (((peer-1) / 5) + 1), peer+100)) + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) stdout.flush() # Announce numRoutes equal routes per PE - different neighbor AS, but same source AS for i in range(0, numRoutes): - stdout.write('announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, (((peer-1) / 5) + 1), peer+100, asnum)) + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) stdout.flush() # Announce numRoutes equal routes with different med per PE and different neighbor AS for i in range(0, numRoutes): - stdout.write('announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n' % (i, peer, (((peer-1) / 5) + 1), peer+100)) + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) stdout.flush() # Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS for i in range(0, numRoutes): - stdout.write('announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, peer, (((peer-1) / 5) + 1), peer+100, asnum)) + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) stdout.flush() # Announce 2 different route per peer -stdout.write('announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n' % (peer, (((peer-1) / 5) + 1), peer+100)) -stdout.write('announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (peer, (((peer-1) / 5) + 1), peer+100, asnum)) +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) stdout.flush() -#Loop endlessly to allow ExaBGP to continue running +# Loop endlessly to allow ExaBGP to continue running while True: sleep(1) - diff --git a/tests/topotests/bgp-ecmp-topo1/peer5/exa-receive.py b/tests/topotests/bgp-ecmp-topo1/peer5/exa-receive.py index 5334ea5369..031ff455ca 100755 --- a/tests/topotests/bgp-ecmp-topo1/peer5/exa-receive.py +++ b/tests/topotests/bgp-ecmp-topo1/peer5/exa-receive.py @@ -4,7 +4,7 @@ exa-receive.py: Save received routes form ExaBGP into file """ -from sys import stdin,argv +from sys import stdin, argv from datetime import datetime # 1st arg is peer number @@ -13,12 +13,12 @@ # When the parent dies we are seeing continual newlines, so we only access so many before stopping counter = 0 -routesavefile = open('/tmp/peer%s-received.log' % peer, 'w') +routesavefile = open("/tmp/peer%s-received.log" % peer, "w") while True: try: line = stdin.readline() - timestamp = datetime.now().strftime('%Y%m%d_%H:%M:%S - ') + timestamp = datetime.now().strftime("%Y%m%d_%H:%M:%S - ") routesavefile.write(timestamp + line) routesavefile.flush() diff --git a/tests/topotests/bgp-ecmp-topo1/peer5/exa-send.py b/tests/topotests/bgp-ecmp-topo1/peer5/exa-send.py index 647c254250..d9ae3d1906 100755 --- a/tests/topotests/bgp-ecmp-topo1/peer5/exa-send.py +++ b/tests/topotests/bgp-ecmp-topo1/peer5/exa-send.py @@ -4,7 +4,7 @@ exa-send.py: Send a few testroutes with ExaBGP """ -from sys import stdout,argv +from sys import stdout, argv from time import sleep sleep(5) @@ -13,37 +13,54 @@ # 2nd arg is number of routes to send peer = int(argv[1]) numRoutes = int(argv[2]) -if (peer <= 10): +if peer <= 10: asnum = 99 else: - asnum = peer+100 + asnum = peer + 100 # Announce numRoutes equal routes per PE - different neighbor AS for i in range(0, numRoutes): - stdout.write('announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n' % (i, (((peer-1) / 5) + 1), peer+100)) + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) stdout.flush() # Announce numRoutes equal routes per PE - different neighbor AS, but same source AS for i in range(0, numRoutes): - stdout.write('announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, (((peer-1) / 5) + 1), peer+100, asnum)) + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) stdout.flush() # Announce numRoutes equal routes with different med per PE and different neighbor AS for i in range(0, numRoutes): - stdout.write('announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n' % (i, peer, (((peer-1) / 5) + 1), peer+100)) + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) stdout.flush() # Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS for i in range(0, numRoutes): - stdout.write('announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, peer, (((peer-1) / 5) + 1), peer+100, asnum)) + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) stdout.flush() # Announce 2 different route per peer -stdout.write('announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n' % (peer, (((peer-1) / 5) + 1), peer+100)) -stdout.write('announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (peer, (((peer-1) / 5) + 1), peer+100, asnum)) +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) stdout.flush() -#Loop endlessly to allow ExaBGP to continue running +# Loop endlessly to allow ExaBGP to continue running while True: sleep(1) - diff --git a/tests/topotests/bgp-ecmp-topo1/peer6/exa-receive.py b/tests/topotests/bgp-ecmp-topo1/peer6/exa-receive.py index 5334ea5369..031ff455ca 100755 --- a/tests/topotests/bgp-ecmp-topo1/peer6/exa-receive.py +++ b/tests/topotests/bgp-ecmp-topo1/peer6/exa-receive.py @@ -4,7 +4,7 @@ exa-receive.py: Save received routes form ExaBGP into file """ -from sys import stdin,argv +from sys import stdin, argv from datetime import datetime # 1st arg is peer number @@ -13,12 +13,12 @@ # When the parent dies we are seeing continual newlines, so we only access so many before stopping counter = 0 -routesavefile = open('/tmp/peer%s-received.log' % peer, 'w') +routesavefile = open("/tmp/peer%s-received.log" % peer, "w") while True: try: line = stdin.readline() - timestamp = datetime.now().strftime('%Y%m%d_%H:%M:%S - ') + timestamp = datetime.now().strftime("%Y%m%d_%H:%M:%S - ") routesavefile.write(timestamp + line) routesavefile.flush() diff --git a/tests/topotests/bgp-ecmp-topo1/peer6/exa-send.py b/tests/topotests/bgp-ecmp-topo1/peer6/exa-send.py index 647c254250..d9ae3d1906 100755 --- a/tests/topotests/bgp-ecmp-topo1/peer6/exa-send.py +++ b/tests/topotests/bgp-ecmp-topo1/peer6/exa-send.py @@ -4,7 +4,7 @@ exa-send.py: Send a few testroutes with ExaBGP """ -from sys import stdout,argv +from sys import stdout, argv from time import sleep sleep(5) @@ -13,37 +13,54 @@ # 2nd arg is number of routes to send peer = int(argv[1]) numRoutes = int(argv[2]) -if (peer <= 10): +if peer <= 10: asnum = 99 else: - asnum = peer+100 + asnum = peer + 100 # Announce numRoutes equal routes per PE - different neighbor AS for i in range(0, numRoutes): - stdout.write('announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n' % (i, (((peer-1) / 5) + 1), peer+100)) + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) stdout.flush() # Announce numRoutes equal routes per PE - different neighbor AS, but same source AS for i in range(0, numRoutes): - stdout.write('announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, (((peer-1) / 5) + 1), peer+100, asnum)) + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) stdout.flush() # Announce numRoutes equal routes with different med per PE and different neighbor AS for i in range(0, numRoutes): - stdout.write('announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n' % (i, peer, (((peer-1) / 5) + 1), peer+100)) + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) stdout.flush() # Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS for i in range(0, numRoutes): - stdout.write('announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, peer, (((peer-1) / 5) + 1), peer+100, asnum)) + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) stdout.flush() # Announce 2 different route per peer -stdout.write('announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n' % (peer, (((peer-1) / 5) + 1), peer+100)) -stdout.write('announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (peer, (((peer-1) / 5) + 1), peer+100, asnum)) +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) stdout.flush() -#Loop endlessly to allow ExaBGP to continue running +# Loop endlessly to allow ExaBGP to continue running while True: sleep(1) - diff --git a/tests/topotests/bgp-ecmp-topo1/peer7/exa-receive.py b/tests/topotests/bgp-ecmp-topo1/peer7/exa-receive.py index 5334ea5369..031ff455ca 100755 --- a/tests/topotests/bgp-ecmp-topo1/peer7/exa-receive.py +++ b/tests/topotests/bgp-ecmp-topo1/peer7/exa-receive.py @@ -4,7 +4,7 @@ exa-receive.py: Save received routes form ExaBGP into file """ -from sys import stdin,argv +from sys import stdin, argv from datetime import datetime # 1st arg is peer number @@ -13,12 +13,12 @@ # When the parent dies we are seeing continual newlines, so we only access so many before stopping counter = 0 -routesavefile = open('/tmp/peer%s-received.log' % peer, 'w') +routesavefile = open("/tmp/peer%s-received.log" % peer, "w") while True: try: line = stdin.readline() - timestamp = datetime.now().strftime('%Y%m%d_%H:%M:%S - ') + timestamp = datetime.now().strftime("%Y%m%d_%H:%M:%S - ") routesavefile.write(timestamp + line) routesavefile.flush() diff --git a/tests/topotests/bgp-ecmp-topo1/peer7/exa-send.py b/tests/topotests/bgp-ecmp-topo1/peer7/exa-send.py index 647c254250..d9ae3d1906 100755 --- a/tests/topotests/bgp-ecmp-topo1/peer7/exa-send.py +++ b/tests/topotests/bgp-ecmp-topo1/peer7/exa-send.py @@ -4,7 +4,7 @@ exa-send.py: Send a few testroutes with ExaBGP """ -from sys import stdout,argv +from sys import stdout, argv from time import sleep sleep(5) @@ -13,37 +13,54 @@ # 2nd arg is number of routes to send peer = int(argv[1]) numRoutes = int(argv[2]) -if (peer <= 10): +if peer <= 10: asnum = 99 else: - asnum = peer+100 + asnum = peer + 100 # Announce numRoutes equal routes per PE - different neighbor AS for i in range(0, numRoutes): - stdout.write('announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n' % (i, (((peer-1) / 5) + 1), peer+100)) + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) stdout.flush() # Announce numRoutes equal routes per PE - different neighbor AS, but same source AS for i in range(0, numRoutes): - stdout.write('announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, (((peer-1) / 5) + 1), peer+100, asnum)) + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) stdout.flush() # Announce numRoutes equal routes with different med per PE and different neighbor AS for i in range(0, numRoutes): - stdout.write('announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n' % (i, peer, (((peer-1) / 5) + 1), peer+100)) + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) stdout.flush() # Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS for i in range(0, numRoutes): - stdout.write('announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, peer, (((peer-1) / 5) + 1), peer+100, asnum)) + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) stdout.flush() # Announce 2 different route per peer -stdout.write('announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n' % (peer, (((peer-1) / 5) + 1), peer+100)) -stdout.write('announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (peer, (((peer-1) / 5) + 1), peer+100, asnum)) +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) stdout.flush() -#Loop endlessly to allow ExaBGP to continue running +# Loop endlessly to allow ExaBGP to continue running while True: sleep(1) - diff --git a/tests/topotests/bgp-ecmp-topo1/peer8/exa-receive.py b/tests/topotests/bgp-ecmp-topo1/peer8/exa-receive.py index 5334ea5369..031ff455ca 100755 --- a/tests/topotests/bgp-ecmp-topo1/peer8/exa-receive.py +++ b/tests/topotests/bgp-ecmp-topo1/peer8/exa-receive.py @@ -4,7 +4,7 @@ exa-receive.py: Save received routes form ExaBGP into file """ -from sys import stdin,argv +from sys import stdin, argv from datetime import datetime # 1st arg is peer number @@ -13,12 +13,12 @@ # When the parent dies we are seeing continual newlines, so we only access so many before stopping counter = 0 -routesavefile = open('/tmp/peer%s-received.log' % peer, 'w') +routesavefile = open("/tmp/peer%s-received.log" % peer, "w") while True: try: line = stdin.readline() - timestamp = datetime.now().strftime('%Y%m%d_%H:%M:%S - ') + timestamp = datetime.now().strftime("%Y%m%d_%H:%M:%S - ") routesavefile.write(timestamp + line) routesavefile.flush() diff --git a/tests/topotests/bgp-ecmp-topo1/peer8/exa-send.py b/tests/topotests/bgp-ecmp-topo1/peer8/exa-send.py index 647c254250..d9ae3d1906 100755 --- a/tests/topotests/bgp-ecmp-topo1/peer8/exa-send.py +++ b/tests/topotests/bgp-ecmp-topo1/peer8/exa-send.py @@ -4,7 +4,7 @@ exa-send.py: Send a few testroutes with ExaBGP """ -from sys import stdout,argv +from sys import stdout, argv from time import sleep sleep(5) @@ -13,37 +13,54 @@ # 2nd arg is number of routes to send peer = int(argv[1]) numRoutes = int(argv[2]) -if (peer <= 10): +if peer <= 10: asnum = 99 else: - asnum = peer+100 + asnum = peer + 100 # Announce numRoutes equal routes per PE - different neighbor AS for i in range(0, numRoutes): - stdout.write('announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n' % (i, (((peer-1) / 5) + 1), peer+100)) + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) stdout.flush() # Announce numRoutes equal routes per PE - different neighbor AS, but same source AS for i in range(0, numRoutes): - stdout.write('announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, (((peer-1) / 5) + 1), peer+100, asnum)) + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) stdout.flush() # Announce numRoutes equal routes with different med per PE and different neighbor AS for i in range(0, numRoutes): - stdout.write('announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n' % (i, peer, (((peer-1) / 5) + 1), peer+100)) + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) stdout.flush() # Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS for i in range(0, numRoutes): - stdout.write('announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, peer, (((peer-1) / 5) + 1), peer+100, asnum)) + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) stdout.flush() # Announce 2 different route per peer -stdout.write('announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n' % (peer, (((peer-1) / 5) + 1), peer+100)) -stdout.write('announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (peer, (((peer-1) / 5) + 1), peer+100, asnum)) +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) stdout.flush() -#Loop endlessly to allow ExaBGP to continue running +# Loop endlessly to allow ExaBGP to continue running while True: sleep(1) - diff --git a/tests/topotests/bgp-ecmp-topo1/peer9/exa-receive.py b/tests/topotests/bgp-ecmp-topo1/peer9/exa-receive.py index 5334ea5369..031ff455ca 100755 --- a/tests/topotests/bgp-ecmp-topo1/peer9/exa-receive.py +++ b/tests/topotests/bgp-ecmp-topo1/peer9/exa-receive.py @@ -4,7 +4,7 @@ exa-receive.py: Save received routes form ExaBGP into file """ -from sys import stdin,argv +from sys import stdin, argv from datetime import datetime # 1st arg is peer number @@ -13,12 +13,12 @@ # When the parent dies we are seeing continual newlines, so we only access so many before stopping counter = 0 -routesavefile = open('/tmp/peer%s-received.log' % peer, 'w') +routesavefile = open("/tmp/peer%s-received.log" % peer, "w") while True: try: line = stdin.readline() - timestamp = datetime.now().strftime('%Y%m%d_%H:%M:%S - ') + timestamp = datetime.now().strftime("%Y%m%d_%H:%M:%S - ") routesavefile.write(timestamp + line) routesavefile.flush() diff --git a/tests/topotests/bgp-ecmp-topo1/peer9/exa-send.py b/tests/topotests/bgp-ecmp-topo1/peer9/exa-send.py index 647c254250..d9ae3d1906 100755 --- a/tests/topotests/bgp-ecmp-topo1/peer9/exa-send.py +++ b/tests/topotests/bgp-ecmp-topo1/peer9/exa-send.py @@ -4,7 +4,7 @@ exa-send.py: Send a few testroutes with ExaBGP """ -from sys import stdout,argv +from sys import stdout, argv from time import sleep sleep(5) @@ -13,37 +13,54 @@ # 2nd arg is number of routes to send peer = int(argv[1]) numRoutes = int(argv[2]) -if (peer <= 10): +if peer <= 10: asnum = 99 else: - asnum = peer+100 + asnum = peer + 100 # Announce numRoutes equal routes per PE - different neighbor AS for i in range(0, numRoutes): - stdout.write('announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n' % (i, (((peer-1) / 5) + 1), peer+100)) + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) stdout.flush() # Announce numRoutes equal routes per PE - different neighbor AS, but same source AS for i in range(0, numRoutes): - stdout.write('announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, (((peer-1) / 5) + 1), peer+100, asnum)) + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) stdout.flush() # Announce numRoutes equal routes with different med per PE and different neighbor AS for i in range(0, numRoutes): - stdout.write('announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n' % (i, peer, (((peer-1) / 5) + 1), peer+100)) + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) stdout.flush() # Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS for i in range(0, numRoutes): - stdout.write('announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, peer, (((peer-1) / 5) + 1), peer+100, asnum)) + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) stdout.flush() # Announce 2 different route per peer -stdout.write('announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n' % (peer, (((peer-1) / 5) + 1), peer+100)) -stdout.write('announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (peer, (((peer-1) / 5) + 1), peer+100, asnum)) +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) stdout.flush() -#Loop endlessly to allow ExaBGP to continue running +# Loop endlessly to allow ExaBGP to continue running while True: sleep(1) - diff --git a/tests/topotests/bgp-ecmp-topo1/r1/bgpd.conf b/tests/topotests/bgp-ecmp-topo1/r1/bgpd.conf index 627dc76c1b..d3beb2d320 100644 --- a/tests/topotests/bgp-ecmp-topo1/r1/bgpd.conf +++ b/tests/topotests/bgp-ecmp-topo1/r1/bgpd.conf @@ -5,6 +5,7 @@ log file bgpd.log router bgp 100 bgp router-id 10.0.255.1 bgp bestpath as-path multipath-relax + no bgp ebgp-requires-policy neighbor 10.0.1.101 remote-as 99 neighbor 10.0.1.102 remote-as 99 neighbor 10.0.1.103 remote-as 99 diff --git a/tests/topotests/bgp-ecmp-topo1/r1/summary.txt b/tests/topotests/bgp-ecmp-topo1/r1/summary.txt index bccc483d52..f0929536d3 100644 --- a/tests/topotests/bgp-ecmp-topo1/r1/summary.txt +++ b/tests/topotests/bgp-ecmp-topo1/r1/summary.txt @@ -2,128 +2,127 @@ "ipv4Unicast":{ "routerId":"10.0.255.1", "as":100, - "vrfId":0, "vrfName":"Default", "peerCount":20, "peers":{ "10.0.1.101":{ "outq":0, "inq":0, - "prefixReceivedCount":42, + "pfxRcd":42, "state":"Established" }, "10.0.1.102":{ "outq":0, "inq":0, - "prefixReceivedCount":42, + "pfxRcd":42, "state":"Established" }, "10.0.1.103":{ "outq":0, "inq":0, - "prefixReceivedCount":42, + "pfxRcd":42, "state":"Established" }, "10.0.1.104":{ "outq":0, "inq":0, - "prefixReceivedCount":42, + "pfxRcd":42, "state":"Established" }, "10.0.1.105":{ "outq":0, "inq":0, - "prefixReceivedCount":42, + "pfxRcd":42, "state":"Established" }, "10.0.2.106":{ "outq":0, "inq":0, - "prefixReceivedCount":42, + "pfxRcd":42, "state":"Established" }, "10.0.2.107":{ "outq":0, "inq":0, - "prefixReceivedCount":42, + "pfxRcd":42, "state":"Established" }, "10.0.2.108":{ "outq":0, "inq":0, - "prefixReceivedCount":42, + "pfxRcd":42, "state":"Established" }, "10.0.2.109":{ "outq":0, "inq":0, - "prefixReceivedCount":42, + "pfxRcd":42, "state":"Established" }, "10.0.2.110":{ "outq":0, "inq":0, - "prefixReceivedCount":42, + "pfxRcd":42, "state":"Established" }, "10.0.3.111":{ "outq":0, "inq":0, - "prefixReceivedCount":42, + "pfxRcd":42, "state":"Established" }, "10.0.3.112":{ "outq":0, "inq":0, - "prefixReceivedCount":42, + "pfxRcd":42, "state":"Established" }, "10.0.3.113":{ "outq":0, "inq":0, - "prefixReceivedCount":42, + "pfxRcd":42, "state":"Established" }, "10.0.3.114":{ "outq":0, "inq":0, - "prefixReceivedCount":42, + "pfxRcd":42, "state":"Established" }, "10.0.3.115":{ "outq":0, "inq":0, - "prefixReceivedCount":42, + "pfxRcd":42, "state":"Established" }, "10.0.4.116":{ "outq":0, "inq":0, - "prefixReceivedCount":42, + "pfxRcd":42, "state":"Established" }, "10.0.4.117":{ "outq":0, "inq":0, - "prefixReceivedCount":42, + "pfxRcd":42, "state":"Established" }, "10.0.4.118":{ "outq":0, "inq":0, - "prefixReceivedCount":42, + "pfxRcd":42, "state":"Established" }, "10.0.4.119":{ "outq":0, "inq":0, - "prefixReceivedCount":42, + "pfxRcd":42, "state":"Established" }, "10.0.4.120":{ "outq":0, "inq":0, - "prefixReceivedCount":42, + "pfxRcd":42, "state":"Established" } }, diff --git a/tests/topotests/bgp-ecmp-topo1/r1/summary20.txt b/tests/topotests/bgp-ecmp-topo1/r1/summary20.txt index 73ae256abe..9015f485f8 100644 --- a/tests/topotests/bgp-ecmp-topo1/r1/summary20.txt +++ b/tests/topotests/bgp-ecmp-topo1/r1/summary20.txt @@ -1,128 +1,127 @@ { "routerId":"10.0.255.1", "as":100, - "vrfId":0, "vrfName":"Default", "peerCount":20, "peers":{ "10.0.1.101":{ "outq":0, "inq":0, - "prefixReceivedCount":42, + "pfxRcd":42, "state":"Established" }, "10.0.1.102":{ "outq":0, "inq":0, - "prefixReceivedCount":42, + "pfxRcd":42, "state":"Established" }, "10.0.1.103":{ "outq":0, "inq":0, - "prefixReceivedCount":42, + "pfxRcd":42, "state":"Established" }, "10.0.1.104":{ "outq":0, "inq":0, - "prefixReceivedCount":42, + "pfxRcd":42, "state":"Established" }, "10.0.1.105":{ "outq":0, "inq":0, - "prefixReceivedCount":42, + "pfxRcd":42, "state":"Established" }, "10.0.2.106":{ "outq":0, "inq":0, - "prefixReceivedCount":42, + "pfxRcd":42, "state":"Established" }, "10.0.2.107":{ "outq":0, "inq":0, - "prefixReceivedCount":42, + "pfxRcd":42, "state":"Established" }, "10.0.2.108":{ "outq":0, "inq":0, - "prefixReceivedCount":42, + "pfxRcd":42, "state":"Established" }, "10.0.2.109":{ "outq":0, "inq":0, - "prefixReceivedCount":42, + "pfxRcd":42, "state":"Established" }, "10.0.2.110":{ "outq":0, "inq":0, - "prefixReceivedCount":42, + "pfxRcd":42, "state":"Established" }, "10.0.3.111":{ "outq":0, "inq":0, - "prefixReceivedCount":42, + "pfxRcd":42, "state":"Established" }, "10.0.3.112":{ "outq":0, "inq":0, - "prefixReceivedCount":42, + "pfxRcd":42, "state":"Established" }, "10.0.3.113":{ "outq":0, "inq":0, - "prefixReceivedCount":42, + "pfxRcd":42, "state":"Established" }, "10.0.3.114":{ "outq":0, "inq":0, - "prefixReceivedCount":42, + "pfxRcd":42, "state":"Established" }, "10.0.3.115":{ "outq":0, "inq":0, - "prefixReceivedCount":42, + "pfxRcd":42, "state":"Established" }, "10.0.4.116":{ "outq":0, "inq":0, - "prefixReceivedCount":42, + "pfxRcd":42, "state":"Established" }, "10.0.4.117":{ "outq":0, "inq":0, - "prefixReceivedCount":42, + "pfxRcd":42, "state":"Established" }, "10.0.4.118":{ "outq":0, "inq":0, - "prefixReceivedCount":42, + "pfxRcd":42, "state":"Established" }, "10.0.4.119":{ "outq":0, "inq":0, - "prefixReceivedCount":42, + "pfxRcd":42, "state":"Established" }, "10.0.4.120":{ "outq":0, "inq":0, - "prefixReceivedCount":42, + "pfxRcd":42, "state":"Established" } }, diff --git a/tests/topotests/bgp-ecmp-topo1/test_bgp_ecmp_topo1.py b/tests/topotests/bgp-ecmp-topo1/test_bgp_ecmp_topo1.py index d806226dff..c37f818b0f 100755 --- a/tests/topotests/bgp-ecmp-topo1/test_bgp_ecmp_topo1.py +++ b/tests/topotests/bgp-ecmp-topo1/test_bgp_ecmp_topo1.py @@ -34,7 +34,7 @@ # Save the Current Working Directory to find configuration files. CWD = os.path.dirname(os.path.realpath(__file__)) -sys.path.append(os.path.join(CWD, '../')) +sys.path.append(os.path.join(CWD, "../")) # pylint: disable=C0413 # Import topogen and topotest helpers @@ -61,23 +61,24 @@ def build(self, **_opts): tgen = get_topogen(self) # Create the BGP router - router = tgen.add_router('r1') + router = tgen.add_router("r1") # Setup Switches - 1 switch per 5 peering routers for swNum in range(1, (total_ebgp_peers + 4) / 5 + 1): - switch = tgen.add_switch('s{}'.format(swNum)) + switch = tgen.add_switch("s{}".format(swNum)) switch.add_link(router) # Add 'total_ebgp_peers' number of eBGP ExaBGP neighbors - for peerNum in range(1, total_ebgp_peers+1): - swNum = ((peerNum - 1) / 5 + 1) + for peerNum in range(1, total_ebgp_peers + 1): + swNum = (peerNum - 1) / 5 + 1 - peer_ip = '10.0.{}.{}'.format(swNum, peerNum + 100) - peer_route = 'via 10.0.{}.1'.format(swNum) - peer = tgen.add_exabgp_peer('peer{}'.format(peerNum), - ip=peer_ip, defaultRoute=peer_route) + peer_ip = "10.0.{}.{}".format(swNum, peerNum + 100) + peer_route = "via 10.0.{}.1".format(swNum) + peer = tgen.add_exabgp_peer( + "peer{}".format(peerNum), ip=peer_ip, defaultRoute=peer_route + ) - switch = tgen.gears['s{}'.format(swNum)] + switch = tgen.gears["s{}".format(swNum)] switch.add_link(peer) @@ -87,6 +88,7 @@ def build(self, **_opts): # ##################################################### + def setup_module(module): tgen = Topogen(BGPECMPTopo1, module.__name__) tgen.start_topology() @@ -95,21 +97,19 @@ def setup_module(module): router_list = tgen.routers() for rname, router in router_list.iteritems(): router.load_config( - TopoRouter.RD_ZEBRA, - os.path.join(CWD, '{}/zebra.conf'.format(rname)) + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) ) router.load_config( - TopoRouter.RD_BGP, - os.path.join(CWD, '{}/bgpd.conf'.format(rname)) + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) ) router.start() # Starting Hosts and init ExaBGP on each of them - topotest.sleep(10, 'starting BGP on all {} peers'.format(total_ebgp_peers)) + topotest.sleep(10, "starting BGP on all {} peers".format(total_ebgp_peers)) peer_list = tgen.exabgp_peers() for pname, peer in peer_list.iteritems(): peer_dir = os.path.join(CWD, pname) - env_file = os.path.join(CWD, 'exabgp.env') + env_file = os.path.join(CWD, "exabgp.env") peer.start(peer_dir, env_file) logger.info(pname) @@ -128,11 +128,11 @@ def test_bgp_convergence(): pytest.skip(tgen.errors) # Expected result - router = tgen.gears['r1'] - if router.has_version('<', '3.0'): - reffile = os.path.join(CWD, 'r1/summary20.txt') + router = tgen.gears["r1"] + if router.has_version("<", "3.0"): + reffile = os.path.join(CWD, "r1/summary20.txt") else: - reffile = os.path.join(CWD, 'r1/summary.txt') + reffile = os.path.join(CWD, "r1/summary.txt") expected = json.loads(open(reffile).read()) @@ -142,18 +142,19 @@ def _output_summary_cmp(router, cmd, data): with 'json') and compare with `data` contents. """ output = router.vtysh_cmd(cmd, isjson=True) - if 'ipv4Unicast' in output: - output['ipv4Unicast']['vrfName'] = \ - output['ipv4Unicast']['vrfName'].replace( - 'default', 'Default') - elif 'vrfName' in output: - output['vrfName'] = output['vrfName'].replace('default', 'Default') + if "ipv4Unicast" in output: + output["ipv4Unicast"]["vrfName"] = output["ipv4Unicast"]["vrfName"].replace( + "default", "Default" + ) + elif "vrfName" in output: + output["vrfName"] = output["vrfName"].replace("default", "Default") return topotest.json_cmp(output, data) test_func = functools.partial( - _output_summary_cmp, router, 'show ip bgp summary json', expected) + _output_summary_cmp, router, "show ip bgp summary json", expected + ) _, res = topotest.run_and_expect(test_func, None, count=60, wait=0.5) - assertmsg = 'BGP router network did not converge' + assertmsg = "BGP router network did not converge" assert res is None, assertmsg @@ -165,26 +166,26 @@ def test_bgp_ecmp(): pytest.skip(tgen.errors) expect = { - 'routerId': '10.0.255.1', - 'routes': { - }, + "routerId": "10.0.255.1", + "routes": {}, } for net in range(1, 5): for subnet in range(0, 10): - netkey = '10.20{}.{}.0/24'.format(net, subnet) - expect['routes'][netkey] = [] + netkey = "10.20{}.{}.0/24".format(net, subnet) + expect["routes"][netkey] = [] for _ in range(0, 10): - peer = {'multipath': True, 'valid': True} - expect['routes'][netkey].append(peer) + peer = {"multipath": True, "valid": True} + expect["routes"][netkey].append(peer) - test_func = functools.partial(topotest.router_json_cmp, - tgen.gears['r1'], 'show ip bgp json', expect) + test_func = functools.partial( + topotest.router_json_cmp, tgen.gears["r1"], "show ip bgp json", expect + ) _, res = topotest.run_and_expect(test_func, None, count=10, wait=0.5) assertmsg = 'expected multipath routes in "show ip bgp" output' assert res is None, assertmsg -if __name__ == '__main__': +if __name__ == "__main__": args = ["-s"] + sys.argv[1:] sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp-ecmp-topo2/ebgp_ecmp_topo2.json b/tests/topotests/bgp-ecmp-topo2/ebgp_ecmp_topo2.json new file mode 100755 index 0000000000..34f11c0a29 --- /dev/null +++ b/tests/topotests/bgp-ecmp-topo2/ebgp_ecmp_topo2.json @@ -0,0 +1,834 @@ +{ + "address_types": [ + "ipv4", + "ipv6" + ], + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 24, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:DB8:F::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2-link1": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {} + } + } + } + } + } + } + } + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link3": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link4": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link5": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link6": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link7": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link8": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link9": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link10": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link11": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link12": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link13": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link14": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link15": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link16": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link17": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link18": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link19": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link20": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link21": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link22": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link23": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link24": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link25": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link26": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link27": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link28": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link29": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link30": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link31": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link32": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "200", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {} + } + }, + "r3": { + "dest_link": { + "r2-link1": { + "next_hop_self": true + }, + "r2-link2": { + "next_hop_self": true + }, + "r2-link3": { + "next_hop_self": true + }, + "r2-link4": { + "next_hop_self": true + }, + "r2-link5": { + "next_hop_self": true + }, + "r2-link6": { + "next_hop_self": true + }, + "r2-link7": { + "next_hop_self": true + }, + "r2-link8": { + "next_hop_self": true + }, + "r2-link9": { + "next_hop_self": true + }, + "r2-link10": { + "next_hop_self": true + }, + "r2-link11": { + "next_hop_self": true + }, + "r2-link12": { + "next_hop_self": true + }, + "r2-link13": { + "next_hop_self": true + }, + "r2-link14": { + "next_hop_self": true + }, + "r2-link15": { + "next_hop_self": true + }, + "r2-link16": { + "next_hop_self": true + }, + "r2-link17": { + "next_hop_self": true + }, + "r2-link18": { + "next_hop_self": true + }, + "r2-link19": { + "next_hop_self": true + }, + "r2-link20": { + "next_hop_self": true + }, + "r2-link21": { + "next_hop_self": true + }, + "r2-link22": { + "next_hop_self": true + }, + "r2-link23": { + "next_hop_self": true + }, + "r2-link24": { + "next_hop_self": true + }, + "r2-link25": { + "next_hop_self": true + }, + "r2-link26": { + "next_hop_self": true + }, + "r2-link27": { + "next_hop_self": true + }, + "r2-link28": { + "next_hop_self": true + }, + "r2-link29": { + "next_hop_self": true + }, + "r2-link30": { + "next_hop_self": true + }, + "r2-link31": { + "next_hop_self": true + }, + "r2-link32": { + "next_hop_self": true + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {} + } + }, + "r3": { + "dest_link": { + "r2-link1": { + "next_hop_self": true + }, + "r2-link2": { + "next_hop_self": true + }, + "r2-link3": { + "next_hop_self": true + }, + "r2-link4": { + "next_hop_self": true + }, + "r2-link5": { + "next_hop_self": true + }, + "r2-link6": { + "next_hop_self": true + }, + "r2-link7": { + "next_hop_self": true + }, + "r2-link8": { + "next_hop_self": true + }, + "r2-link9": { + "next_hop_self": true + }, + "r2-link10": { + "next_hop_self": true + }, + "r2-link11": { + "next_hop_self": true + }, + "r2-link12": { + "next_hop_self": true + }, + "r2-link13": { + "next_hop_self": true + }, + "r2-link14": { + "next_hop_self": true + }, + "r2-link15": { + "next_hop_self": true + }, + "r2-link16": { + "next_hop_self": true + }, + "r2-link17": { + "next_hop_self": true + }, + "r2-link18": { + "next_hop_self": true + }, + "r2-link19": { + "next_hop_self": true + }, + "r2-link20": { + "next_hop_self": true + }, + "r2-link21": { + "next_hop_self": true + }, + "r2-link22": { + "next_hop_self": true + }, + "r2-link23": { + "next_hop_self": true + }, + "r2-link24": { + "next_hop_self": true + }, + "r2-link25": { + "next_hop_self": true + }, + "r2-link26": { + "next_hop_self": true + }, + "r2-link27": { + "next_hop_self": true + }, + "r2-link28": { + "next_hop_self": true + }, + "r2-link29": { + "next_hop_self": true + }, + "r2-link30": { + "next_hop_self": true + }, + "r2-link31": { + "next_hop_self": true + }, + "r2-link32": { + "next_hop_self": true + } + } + } + } + } + } + } + } + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link3": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link4": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link5": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link6": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link7": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link8": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link9": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link10": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link11": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link12": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link13": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link14": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link15": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link16": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link17": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link18": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link19": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link20": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link21": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link22": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link23": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link24": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link25": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link26": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link27": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link28": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link29": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link30": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link31": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link32": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + }, + "bgp": { + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": { + "maximum_paths": { + "ebgp": 32 + }, + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": {}, + "r3-link2": {}, + "r3-link3": {}, + "r3-link4": {}, + "r3-link5": {}, + "r3-link6": {}, + "r3-link7": {}, + "r3-link8": {}, + "r3-link9": {}, + "r3-link10": {}, + "r3-link11": {}, + "r3-link12": {}, + "r3-link13": {}, + "r3-link14": {}, + "r3-link15": {}, + "r3-link16": {}, + "r3-link17": {}, + "r3-link18": {}, + "r3-link19": {}, + "r3-link20": {}, + "r3-link21": {}, + "r3-link22": {}, + "r3-link23": {}, + "r3-link24": {}, + "r3-link25": {}, + "r3-link26": {}, + "r3-link27": {}, + "r3-link28": {}, + "r3-link29": {}, + "r3-link30": {}, + "r3-link31": {}, + "r3-link32": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "maximum_paths": { + "ebgp": 32 + }, + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link2": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link3": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link4": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link5": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link6": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link7": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link8": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link9": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link10": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link11": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link12": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link13": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link14": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link15": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link16": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link17": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link18": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link19": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link20": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link21": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link22": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link23": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link24": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link25": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link26": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link27": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link28": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link29": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link30": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link31": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link32": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/tests/topotests/bgp-ecmp-topo2/ibgp_ecmp_topo2.json b/tests/topotests/bgp-ecmp-topo2/ibgp_ecmp_topo2.json new file mode 100755 index 0000000000..9eea9073c7 --- /dev/null +++ b/tests/topotests/bgp-ecmp-topo2/ibgp_ecmp_topo2.json @@ -0,0 +1,844 @@ +{ + "address_types": [ + "ipv4", + "ipv6" + ], + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 24, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:DB8:F::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2-link1": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {} + } + } + } + } + } + } + } + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link3": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link4": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link5": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link6": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link7": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link8": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link9": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link10": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link11": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link12": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link13": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link14": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link15": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link16": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link17": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link18": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link19": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link20": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link21": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link22": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link23": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link24": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link25": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link26": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link27": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link28": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link29": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link30": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link31": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link32": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {} + } + }, + "r3": { + "dest_link": { + "r2-link1": { + "next_hop_self": true + }, + "r2-link2": { + "next_hop_self": true + }, + "r2-link3": { + "next_hop_self": true + }, + "r2-link4": { + "next_hop_self": true + }, + "r2-link5": { + "next_hop_self": true + }, + "r2-link6": { + "next_hop_self": true + }, + "r2-link7": { + "next_hop_self": true + }, + "r2-link8": { + "next_hop_self": true + }, + "r2-link9": { + "next_hop_self": true + }, + "r2-link10": { + "next_hop_self": true + }, + "r2-link11": { + "next_hop_self": true + }, + "r2-link12": { + "next_hop_self": true + }, + "r2-link13": { + "next_hop_self": true + }, + "r2-link14": { + "next_hop_self": true + }, + "r2-link15": { + "next_hop_self": true + }, + "r2-link16": { + "next_hop_self": true + }, + "r2-link17": { + "next_hop_self": true + }, + "r2-link18": { + "next_hop_self": true + }, + "r2-link19": { + "next_hop_self": true + }, + "r2-link20": { + "next_hop_self": true + }, + "r2-link21": { + "next_hop_self": true + }, + "r2-link22": { + "next_hop_self": true + }, + "r2-link23": { + "next_hop_self": true + }, + "r2-link24": { + "next_hop_self": true + }, + "r2-link25": { + "next_hop_self": true + }, + "r2-link26": { + "next_hop_self": true + }, + "r2-link27": { + "next_hop_self": true + }, + "r2-link28": { + "next_hop_self": true + }, + "r2-link29": { + "next_hop_self": true + }, + "r2-link30": { + "next_hop_self": true + }, + "r2-link31": { + "next_hop_self": true + }, + "r2-link32": { + "next_hop_self": true + } + } + } + }, + "redistribute": [ + { + "redist_type": "static" + } + ] + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {} + } + }, + "r3": { + "dest_link": { + "r2-link1": { + "next_hop_self": true + }, + "r2-link2": { + "next_hop_self": true + }, + "r2-link3": { + "next_hop_self": true + }, + "r2-link4": { + "next_hop_self": true + }, + "r2-link5": { + "next_hop_self": true + }, + "r2-link6": { + "next_hop_self": true + }, + "r2-link7": { + "next_hop_self": true + }, + "r2-link8": { + "next_hop_self": true + }, + "r2-link9": { + "next_hop_self": true + }, + "r2-link10": { + "next_hop_self": true + }, + "r2-link11": { + "next_hop_self": true + }, + "r2-link12": { + "next_hop_self": true + }, + "r2-link13": { + "next_hop_self": true + }, + "r2-link14": { + "next_hop_self": true + }, + "r2-link15": { + "next_hop_self": true + }, + "r2-link16": { + "next_hop_self": true + }, + "r2-link17": { + "next_hop_self": true + }, + "r2-link18": { + "next_hop_self": true + }, + "r2-link19": { + "next_hop_self": true + }, + "r2-link20": { + "next_hop_self": true + }, + "r2-link21": { + "next_hop_self": true + }, + "r2-link22": { + "next_hop_self": true + }, + "r2-link23": { + "next_hop_self": true + }, + "r2-link24": { + "next_hop_self": true + }, + "r2-link25": { + "next_hop_self": true + }, + "r2-link26": { + "next_hop_self": true + }, + "r2-link27": { + "next_hop_self": true + }, + "r2-link28": { + "next_hop_self": true + }, + "r2-link29": { + "next_hop_self": true + }, + "r2-link30": { + "next_hop_self": true + }, + "r2-link31": { + "next_hop_self": true + }, + "r2-link32": { + "next_hop_self": true + } + } + } + }, + "redistribute": [ + { + "redist_type": "static" + } + ] + } + } + } + } + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link3": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link4": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link5": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link6": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link7": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link8": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link9": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link10": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link11": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link12": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link13": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link14": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link15": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link16": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link17": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link18": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link19": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link20": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link21": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link22": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link23": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link24": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link25": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link26": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link27": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link28": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link29": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link30": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link31": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link32": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "maximum_paths": { + "ibgp": 32 + }, + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": {}, + "r3-link2": {}, + "r3-link3": {}, + "r3-link4": {}, + "r3-link5": {}, + "r3-link6": {}, + "r3-link7": {}, + "r3-link8": {}, + "r3-link9": {}, + "r3-link10": {}, + "r3-link11": {}, + "r3-link12": {}, + "r3-link13": {}, + "r3-link14": {}, + "r3-link15": {}, + "r3-link16": {}, + "r3-link17": {}, + "r3-link18": {}, + "r3-link19": {}, + "r3-link20": {}, + "r3-link21": {}, + "r3-link22": {}, + "r3-link23": {}, + "r3-link24": {}, + "r3-link25": {}, + "r3-link26": {}, + "r3-link27": {}, + "r3-link28": {}, + "r3-link29": {}, + "r3-link30": {}, + "r3-link31": {}, + "r3-link32": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "maximum_paths": { + "ibgp": 32 + }, + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link2": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link3": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link4": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link5": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link6": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link7": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link8": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link9": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link10": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link11": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link12": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link13": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link14": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link15": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link16": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link17": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link18": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link19": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link20": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link21": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link22": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link23": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link24": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link25": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link26": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link27": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link28": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link29": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link30": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link31": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + }, + "r3-link32": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/tests/topotests/bgp-ecmp-topo2/test_ebgp_ecmp_topo2.py b/tests/topotests/bgp-ecmp-topo2/test_ebgp_ecmp_topo2.py new file mode 100755 index 0000000000..9cb3046f00 --- /dev/null +++ b/tests/topotests/bgp-ecmp-topo2/test_ebgp_ecmp_topo2.py @@ -0,0 +1,763 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2019 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. +# ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + + +""" +Following tests are covered to test ecmp functionality on EBGP. +1. Verify routes installed as per maximum-paths configuration (8/16/32) +2. Disable/Shut selected paths nexthops and verify other next are installed in + the RIB of DUT. Enable interfaces and verify RIB count. +3. Verify BGP table and RIB in DUT after clear BGP routes and neighbors. +4. Verify routes are cleared from BGP and RIB table of DUT when + redistribute static configuration is removed. +5. Shut BGP neigbors one by one and verify BGP and routing table updated + accordingly in DUT +6. Delete static routes and verify routers are cleared from BGP table and RIB + of DUT. +7. Verify routes are cleared from BGP and RIB table of DUT when advertise + network configuration is removed. +""" +import os +import sys +import time +import json +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from mininet.topo import Topo + +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + verify_rib, + create_static_routes, + check_address_types, + interface_status, + reset_config_on_routers, + required_linux_kernel_version +) +from lib.topolog import logger +from lib.bgp import verify_bgp_convergence, create_router_bgp, clear_bgp +from lib.topojson import build_topo_from_json, build_config_from_json + +# Reading the data from JSON File for topology and configuration creation +jsonFile = "{}/ebgp_ecmp_topo2.json".format(CWD) + +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +# Global variables +NEXT_HOPS = {"ipv4": [], "ipv6": []} +INTF_LIST_R3 = [] +INTF_LIST_R2 = [] +NETWORK = {"ipv4": "11.0.20.1/32", "ipv6": "1::/64"} +NEXT_HOP_IP = {"ipv4": "10.0.0.1", "ipv6": "fd00::1"} +BGP_CONVERGENCE = False + + +class CreateTopo(Topo): + """ + Test topology builder. + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + """Build function.""" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + Sets up the pytest environment. + + * `mod`: module name + """ + global NEXT_HOPS, INTF_LIST_R3, INTF_LIST_R2, TEST_STATIC + global ADDR_TYPES + + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version('4.15') + if result is not True: + pytest.skip("Kernel requirements are not met") + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(CreateTopo, mod.__name__) + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # tgen.mininet_cli() + # Api call verify whether BGP is converged + ADDR_TYPES = check_address_types() + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error:" " {}".format( + BGP_CONVERGENCE + ) + + link_data = [ + val + for links, val in topo["routers"]["r2"]["links"].iteritems() + if "r3" in links + ] + for adt in ADDR_TYPES: + NEXT_HOPS[adt] = [val[adt].split("/")[0] for val in link_data] + if adt == "ipv4": + NEXT_HOPS[adt] = sorted(NEXT_HOPS[adt], key=lambda x: int(x.split(".")[2])) + elif adt == "ipv6": + NEXT_HOPS[adt] = sorted( + NEXT_HOPS[adt], key=lambda x: int(x.split(":")[-3], 16) + ) + + INTF_LIST_R2 = [val["interface"].split("/")[0] for val in link_data] + INTF_LIST_R2 = sorted(INTF_LIST_R2, key=lambda x: int(x.split("eth")[1])) + + link_data = [ + val + for links, val in topo["routers"]["r3"]["links"].iteritems() + if "r2" in links + ] + INTF_LIST_R3 = [val["interface"].split("/")[0] for val in link_data] + INTF_LIST_R3 = sorted(INTF_LIST_R3, key=lambda x: int(x.split("eth")[1])) + + # STATIC_ROUTE = True + logger.info("Running setup_module() done") + + +def teardown_module(): + """ + Teardown the pytest environment. + + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + +def static_or_nw(tgen, topo, tc_name, test_type, dut): + + if test_type == "redist_static": + input_dict_static = { + dut: { + "static_routes": [ + {"network": NETWORK["ipv4"], "next_hop": NEXT_HOP_IP["ipv4"]}, + {"network": NETWORK["ipv6"], "next_hop": NEXT_HOP_IP["ipv6"]}, + ] + } + } + logger.info("Configuring static route on router %s", dut) + result = create_static_routes(tgen, input_dict_static) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_2 = { + dut: { + "bgp": { + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + } + } + } + } + + logger.info("Configuring redistribute static route on router %s", dut) + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + elif test_type == "advertise_nw": + input_dict_nw = { + dut: { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [{"network": NETWORK["ipv4"]}] + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [{"network": NETWORK["ipv6"]}] + } + }, + } + } + } + } + + logger.info( + "Advertising networks %s %s from router %s", + NETWORK["ipv4"], + NETWORK["ipv6"], + dut, + ) + result = create_router_bgp(tgen, topo, input_dict_nw) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + +@pytest.mark.parametrize("ecmp_num", ["8", "16", "32"]) +@pytest.mark.parametrize("test_type", ["redist_static", "advertise_nw"]) +def test_modify_ecmp_max_paths(request, ecmp_num, test_type): + """ + Verify routes installed as per maximum-paths + configuration (8/16/32). + """ + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + reset_config_on_routers(tgen) + + static_or_nw(tgen, topo, tc_name, test_type, "r2") + + input_dict = { + "r3": { + "bgp": { + "address_family": { + "ipv4": {"unicast": {"maximum_paths": {"ebgp": ecmp_num,}}}, + "ipv6": {"unicast": {"maximum_paths": {"ebgp": ecmp_num,}}}, + } + } + } + } + + logger.info("Configuring bgp maximum-paths %s on router r3", ecmp_num) + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + + for addr_type in ADDR_TYPES: + input_dict_1 = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}} + + logger.info("Verifying %s routes on r3", addr_type) + + # Only test the count of nexthops; the actual nexthop addresses + # can vary and are not deterministic. + # + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_1, + next_hop=NEXT_HOPS[addr_type][: int(ecmp_num)], + protocol=protocol, + count_only=True + ) + + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +@pytest.mark.parametrize("test_type", ["redist_static", "advertise_nw"]) +def test_ecmp_after_clear_bgp(request, test_type): + """ Verify BGP table and RIB in DUT after clear BGP routes and neighbors""" + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + reset_config_on_routers(tgen) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + + static_or_nw(tgen, topo, tc_name, test_type, "r2") + for addr_type in ADDR_TYPES: + input_dict_1 = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}} + + logger.info("Verifying %s routes on r3", addr_type) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_1, + next_hop=NEXT_HOPS[addr_type], + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Clear BGP + for addr_type in ADDR_TYPES: + clear_bgp(tgen, addr_type, dut) + + # Verify BGP convergence + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + input_dict_1 = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}} + logger.info("Verifying %s routes on r3", addr_type) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_1, + next_hop=NEXT_HOPS[addr_type], + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_ecmp_remove_redistribute_static(request): + """ Verify routes are cleared from BGP and RIB table of DUT when + redistribute static configuration is removed.""" + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + reset_config_on_routers(tgen) + static_or_nw(tgen, topo, tc_name, "redist_static", "r2") + for addr_type in ADDR_TYPES: + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + input_dict_1 = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}} + + logger.info("Verifying %s routes on r3", addr_type) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_1, + next_hop=NEXT_HOPS[addr_type], + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_2 = { + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [{"redist_type": "static", "delete": True}] + } + }, + "ipv6": { + "unicast": { + "redistribute": [{"redist_type": "static", "delete": True}] + } + }, + } + } + } + } + + logger.info("Remove redistribute static") + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + input_dict_1 = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}} + + logger.info("Verifying %s routes on r3 are deleted", addr_type) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_1, + next_hop=[], + protocol=protocol, + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed \n Routes still" " present in RIB".format(tc_name) + + logger.info("Enable redistribute static") + input_dict_2 = { + "r2": { + "bgp": { + "address_family": { + "ipv4": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + input_dict_1 = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}} + logger.info("Verifying %s routes on r3", addr_type) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_1, + next_hop=NEXT_HOPS[addr_type], + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +@pytest.mark.parametrize("test_type", ["redist_static", "advertise_nw"]) +def test_ecmp_shut_bgp_neighbor(request, test_type): + """ Shut BGP neigbors one by one and verify BGP and routing table updated + accordingly in DUT """ + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + logger.info(INTF_LIST_R2) + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + + reset_config_on_routers(tgen) + static_or_nw(tgen, topo, tc_name, test_type, "r2") + + for addr_type in ADDR_TYPES: + input_dict = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}} + + logger.info("Verifying %s routes on r3", addr_type) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict, + next_hop=NEXT_HOPS[addr_type], + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for intf_num in range(len(INTF_LIST_R2) + 1, 16): + intf_val = INTF_LIST_R2[intf_num : intf_num + 16] + + input_dict_1 = {"r2": {"interface_list": [intf_val], "status": "down"}} + logger.info("Shutting down neighbor interface {} on r2".format(intf_val)) + result = interface_status(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + if intf_num + 16 < 32: + check_hops = NEXT_HOPS[addr_type] + else: + check_hops = [] + + input_dict = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}} + logger.info("Verifying %s routes on r3", addr_type) + result = verify_rib( + tgen, addr_type, dut, input_dict, next_hop=check_hops, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_1 = {"r2": {"interface_list": INTF_LIST_R2, "status": "up"}} + + logger.info("Enabling all neighbor interface {} on r2") + result = interface_status(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + static_or_nw(tgen, topo, tc_name, test_type, "r2") + for addr_type in ADDR_TYPES: + input_dict = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}} + + logger.info("Verifying %s routes on r3", addr_type) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict, + next_hop=NEXT_HOPS[addr_type], + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_ecmp_remove_static_route(request): + """ + Delete static routes and verify routers are cleared from BGP table, + and RIB of DUT. + """ + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + + reset_config_on_routers(tgen) + + static_or_nw(tgen, topo, tc_name, "redist_static", "r2") + for addr_type in ADDR_TYPES: + input_dict_1 = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}} + + logger.info("Verifying %s routes on r3", addr_type) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_1, + next_hop=NEXT_HOPS[addr_type], + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "r2": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "delete": True, + } + ] + } + } + + logger.info("Remove static routes") + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + logger.info("Verifying %s routes on r3 are removed", addr_type) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_2, + next_hop=[], + protocol=protocol, + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed \n Routes still" " present in RIB".format(tc_name) + + for addr_type in ADDR_TYPES: + # Enable static routes + input_dict_4 = { + "r2": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Enable static route") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + logger.info("Verifying %s routes on r3", addr_type) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=NEXT_HOPS[addr_type], + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + +def test_ecmp_remove_nw_advertise(request): + """ + Verify routes are cleared from BGP and RIB table of DUT, + when advertise network configuration is removed + """ + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + + reset_config_on_routers(tgen) + static_or_nw(tgen, topo, tc_name, "advertise_nw", "r2") + for addr_type in ADDR_TYPES: + input_dict = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}} + + logger.info("Verifying %s routes on r3", addr_type) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict, + next_hop=NEXT_HOPS[addr_type], + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_3 = { + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + {"network": NETWORK["ipv4"], "delete": True} + ] + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + {"network": NETWORK["ipv6"], "delete": True} + ] + } + }, + } + } + } + } + + logger.info("Withdraw advertised networks") + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + input_dict = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}} + + logger.info("Verifying %s routes on r3", addr_type) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict, + next_hop=[], + protocol=protocol, + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed \n Routes still" " present in RIB".format(tc_name) + + static_or_nw(tgen, topo, tc_name, "advertise_nw", "r2") + for addr_type in ADDR_TYPES: + input_dict = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}} + logger.info("Verifying %s routes on r3", addr_type) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict, + next_hop=NEXT_HOPS[addr_type], + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp-ecmp-topo2/test_ibgp_ecmp_topo2.py b/tests/topotests/bgp-ecmp-topo2/test_ibgp_ecmp_topo2.py new file mode 100755 index 0000000000..96cdabf79b --- /dev/null +++ b/tests/topotests/bgp-ecmp-topo2/test_ibgp_ecmp_topo2.py @@ -0,0 +1,764 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2019 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. +# ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + + +""" +Following tests are covered to test ecmp functionality on EBGP. +1. Verify routes installed as per maximum-paths configuration (8/16/32) +2. Disable/Shut selected paths nexthops and verify other next are installed in + the RIB of DUT. Enable interfaces and verify RIB count. +3. Verify BGP table and RIB in DUT after clear BGP routes and neighbors. +4. Verify routes are cleared from BGP and RIB table of DUT when + redistribute static configuration is removed. +5. Shut BGP neigbors one by one and verify BGP and routing table updated + accordingly in DUT +6. Delete static routes and verify routers are cleared from BGP table and RIB + of DUT. +7. Verify routes are cleared from BGP and RIB table of DUT when advertise + network configuration is removed. +""" +import os +import sys +import time +import json +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from mininet.topo import Topo + +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + verify_rib, + create_static_routes, + check_address_types, + interface_status, + reset_config_on_routers, + required_linux_kernel_version +) +from lib.topolog import logger +from lib.bgp import verify_bgp_convergence, create_router_bgp, clear_bgp +from lib.topojson import build_topo_from_json, build_config_from_json + +# Reading the data from JSON File for topology and configuration creation +jsonFile = "{}/ibgp_ecmp_topo2.json".format(CWD) + +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +# Global variables +NEXT_HOPS = {"ipv4": [], "ipv6": []} +INTF_LIST_R3 = [] +INTF_LIST_R2 = [] +NETWORK = {"ipv4": "11.0.20.1/32", "ipv6": "1::/64"} +NEXT_HOP_IP = {"ipv4": "10.0.0.1", "ipv6": "fd00::1"} +BGP_CONVERGENCE = False + + +class CreateTopo(Topo): + """ + Test topology builder. + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + """Build function.""" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + Sets up the pytest environment. + + * `mod`: module name + """ + global NEXT_HOPS, INTF_LIST_R3, INTF_LIST_R2, TEST_STATIC + global ADDR_TYPES + + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version('4.15') + if result is not True: + pytest.skip("Kernel requirements are not met") + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(CreateTopo, mod.__name__) + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # tgen.mininet_cli() + # Api call verify whether BGP is converged + ADDR_TYPES = check_address_types() + + for addr_type in ADDR_TYPES: + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error:" " {}".format( + BGP_CONVERGENCE + ) + + link_data = [ + val + for links, val in topo["routers"]["r2"]["links"].iteritems() + if "r3" in links + ] + for adt in ADDR_TYPES: + NEXT_HOPS[adt] = [val[adt].split("/")[0] for val in link_data] + if adt == "ipv4": + NEXT_HOPS[adt] = sorted(NEXT_HOPS[adt], key=lambda x: int(x.split(".")[2])) + elif adt == "ipv6": + NEXT_HOPS[adt] = sorted( + NEXT_HOPS[adt], key=lambda x: int(x.split(":")[-3], 16) + ) + + INTF_LIST_R2 = [val["interface"].split("/")[0] for val in link_data] + INTF_LIST_R2 = sorted(INTF_LIST_R2, key=lambda x: int(x.split("eth")[1])) + + link_data = [ + val + for links, val in topo["routers"]["r3"]["links"].iteritems() + if "r2" in links + ] + INTF_LIST_R3 = [val["interface"].split("/")[0] for val in link_data] + INTF_LIST_R3 = sorted(INTF_LIST_R3, key=lambda x: int(x.split("eth")[1])) + + # STATIC_ROUTE = True + logger.info("Running setup_module() done") + + +def teardown_module(): + """ + Teardown the pytest environment. + + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + +def static_or_nw(tgen, topo, tc_name, test_type, dut): + + if test_type == "redist_static": + input_dict_static = { + dut: { + "static_routes": [ + {"network": NETWORK["ipv4"], "next_hop": NEXT_HOP_IP["ipv4"]}, + {"network": NETWORK["ipv6"], "next_hop": NEXT_HOP_IP["ipv6"]}, + ] + } + } + logger.info("Configuring static route on router %s", dut) + result = create_static_routes(tgen, input_dict_static) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_2 = { + dut: { + "bgp": { + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + } + } + } + } + + logger.info("Configuring redistribute static route on router %s", dut) + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + elif test_type == "advertise_nw": + input_dict_nw = { + dut: { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [{"network": NETWORK["ipv4"]}] + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [{"network": NETWORK["ipv6"]}] + } + }, + } + } + } + } + + logger.info( + "Advertising networks %s %s from router %s", + NETWORK["ipv4"], + NETWORK["ipv6"], + dut, + ) + result = create_router_bgp(tgen, topo, input_dict_nw) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + +@pytest.mark.parametrize("ecmp_num", ["8", "16", "32"]) +@pytest.mark.parametrize("test_type", ["redist_static", "advertise_nw"]) +def test_modify_ecmp_max_paths(request, ecmp_num, test_type): + """ + Verify routes installed as per maximum-paths + configuration (8/16/32). + """ + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + reset_config_on_routers(tgen) + + static_or_nw(tgen, topo, tc_name, test_type, "r2") + + input_dict = { + "r3": { + "bgp": { + "address_family": { + "ipv4": {"unicast": {"maximum_paths": {"ibgp": ecmp_num,}}}, + "ipv6": {"unicast": {"maximum_paths": {"ibgp": ecmp_num,}}}, + } + } + } + } + + logger.info("Configuring bgp maximum-paths %s on router r3", ecmp_num) + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + + for addr_type in ADDR_TYPES: + input_dict_1 = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}} + + logger.info("Verifying %s routes on r3", addr_type) + + # Test only the count of nexthops, not the specific nexthop addresses - + # they're not deterministic + # + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_1, + next_hop=NEXT_HOPS[addr_type][: int(ecmp_num)], + protocol=protocol, + count_only=True + ) + + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +@pytest.mark.parametrize("test_type", ["redist_static", "advertise_nw"]) +def test_ecmp_after_clear_bgp(request, test_type): + """ Verify BGP table and RIB in DUT after clear BGP routes and neighbors""" + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + reset_config_on_routers(tgen) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + + static_or_nw(tgen, topo, tc_name, test_type, "r2") + for addr_type in ADDR_TYPES: + input_dict_1 = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}} + + logger.info("Verifying %s routes on r3", addr_type) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_1, + next_hop=NEXT_HOPS[addr_type], + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Clear BGP + for addr_type in ADDR_TYPES: + clear_bgp(tgen, addr_type, dut) + + # Verify BGP convergence + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + input_dict_1 = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}} + logger.info("Verifying %s routes on r3", addr_type) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_1, + next_hop=NEXT_HOPS[addr_type], + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_ecmp_remove_redistribute_static(request): + """ Verify routes are cleared from BGP and RIB table of DUT when + redistribute static configuration is removed.""" + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + reset_config_on_routers(tgen) + static_or_nw(tgen, topo, tc_name, "redist_static", "r2") + for addr_type in ADDR_TYPES: + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + input_dict_1 = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}} + + logger.info("Verifying %s routes on r3", addr_type) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_1, + next_hop=NEXT_HOPS[addr_type], + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_2 = { + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [{"redist_type": "static", "delete": True}] + } + }, + "ipv6": { + "unicast": { + "redistribute": [{"redist_type": "static", "delete": True}] + } + }, + } + } + } + } + + logger.info("Remove redistribute static") + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + input_dict_1 = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}} + + logger.info("Verifying %s routes on r3 are deleted", addr_type) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_1, + next_hop=[], + protocol=protocol, + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed \n Routes still" " present in RIB".format(tc_name) + + logger.info("Enable redistribute static") + input_dict_2 = { + "r2": { + "bgp": { + "address_family": { + "ipv4": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + input_dict_1 = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}} + logger.info("Verifying %s routes on r3", addr_type) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_1, + next_hop=NEXT_HOPS[addr_type], + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +@pytest.mark.parametrize("test_type", ["redist_static", "advertise_nw"]) +def test_ecmp_shut_bgp_neighbor(request, test_type): + """ Shut BGP neigbors one by one and verify BGP and routing table updated + accordingly in DUT """ + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + logger.info(INTF_LIST_R2) + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + + reset_config_on_routers(tgen) + static_or_nw(tgen, topo, tc_name, test_type, "r2") + + for addr_type in ADDR_TYPES: + input_dict = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}} + + logger.info("Verifying %s routes on r3", addr_type) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict, + next_hop=NEXT_HOPS[addr_type], + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for intf_num in range(len(INTF_LIST_R2) + 1, 16): + intf_val = INTF_LIST_R2[intf_num : intf_num + 16] + + input_dict_1 = {"r2": {"interface_list": [intf_val], "status": "down"}} + logger.info("Shutting down neighbor interface {} on r2".format(intf_val)) + result = interface_status(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + if intf_num + 16 < 32: + check_hops = NEXT_HOPS[addr_type] + else: + check_hops = [] + + input_dict = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}} + logger.info("Verifying %s routes on r3", addr_type) + result = verify_rib( + tgen, addr_type, dut, input_dict, next_hop=check_hops, protocol=protocol + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_1 = {"r2": {"interface_list": INTF_LIST_R2, "status": "up"}} + + logger.info("Enabling all neighbor interface {} on r2") + result = interface_status(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + static_or_nw(tgen, topo, tc_name, test_type, "r2") + for addr_type in ADDR_TYPES: + input_dict = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}} + + logger.info("Verifying %s routes on r3", addr_type) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict, + next_hop=NEXT_HOPS[addr_type], + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_ecmp_remove_static_route(request): + """ + Delete static routes and verify routers are cleared from BGP table, + and RIB of DUT. + """ + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + + reset_config_on_routers(tgen) + + static_or_nw(tgen, topo, tc_name, "redist_static", "r2") + for addr_type in ADDR_TYPES: + input_dict_1 = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}} + + logger.info("Verifying %s routes on r3", addr_type) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_1, + next_hop=NEXT_HOPS[addr_type], + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "r2": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "delete": True, + } + ] + } + } + + logger.info("Remove static routes") + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + logger.info("Verifying %s routes on r3 are removed", addr_type) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_2, + next_hop=[], + protocol=protocol, + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed \n Routes still" " present in RIB".format(tc_name) + + for addr_type in ADDR_TYPES: + # Enable static routes + input_dict_4 = { + "r2": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Enable static route") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + logger.info("Verifying %s routes on r3", addr_type) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=NEXT_HOPS[addr_type], + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_ecmp_remove_nw_advertise(request): + """ + Verify routes are cleared from BGP and RIB table of DUT, + when advertise network configuration is removed + """ + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + + reset_config_on_routers(tgen) + static_or_nw(tgen, topo, tc_name, "advertise_nw", "r2") + for addr_type in ADDR_TYPES: + input_dict = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}} + + logger.info("Verifying %s routes on r3", addr_type) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict, + next_hop=NEXT_HOPS[addr_type], + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_3 = { + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + {"network": NETWORK["ipv4"], "delete": True} + ] + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + {"network": NETWORK["ipv6"], "delete": True} + ] + } + }, + } + } + } + } + + logger.info("Withdraw advertised networks") + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + input_dict = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}} + + logger.info("Verifying %s routes on r3", addr_type) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict, + next_hop=[], + protocol=protocol, + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed \n Routes still" " present in RIB".format(tc_name) + + static_or_nw(tgen, topo, tc_name, "advertise_nw", "r2") + for addr_type in ADDR_TYPES: + input_dict = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}} + logger.info("Verifying %s routes on r3", addr_type) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict, + next_hop=NEXT_HOPS[addr_type], + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp-evpn-mh/evpn-mh-topo-tests.pdf b/tests/topotests/bgp-evpn-mh/evpn-mh-topo-tests.pdf new file mode 100644 index 0000000000..8858e21496 Binary files /dev/null and b/tests/topotests/bgp-evpn-mh/evpn-mh-topo-tests.pdf differ diff --git a/tests/topotests/bgp-evpn-mh/hostd11/evpn.conf b/tests/topotests/bgp-evpn-mh/hostd11/evpn.conf new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bgp-evpn-mh/hostd11/pim.conf b/tests/topotests/bgp-evpn-mh/hostd11/pim.conf new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bgp-evpn-mh/hostd11/zebra.conf b/tests/topotests/bgp-evpn-mh/hostd11/zebra.conf new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bgp-evpn-mh/hostd12/evpn.conf b/tests/topotests/bgp-evpn-mh/hostd12/evpn.conf new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bgp-evpn-mh/hostd12/pim.conf b/tests/topotests/bgp-evpn-mh/hostd12/pim.conf new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bgp-evpn-mh/hostd12/zebra.conf b/tests/topotests/bgp-evpn-mh/hostd12/zebra.conf new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bgp-evpn-mh/hostd21/evpn.conf b/tests/topotests/bgp-evpn-mh/hostd21/evpn.conf new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bgp-evpn-mh/hostd21/pim.conf b/tests/topotests/bgp-evpn-mh/hostd21/pim.conf new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bgp-evpn-mh/hostd21/zebra.conf b/tests/topotests/bgp-evpn-mh/hostd21/zebra.conf new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bgp-evpn-mh/hostd22/evpn.conf b/tests/topotests/bgp-evpn-mh/hostd22/evpn.conf new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bgp-evpn-mh/hostd22/pim.conf b/tests/topotests/bgp-evpn-mh/hostd22/pim.conf new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bgp-evpn-mh/hostd22/zebra.conf b/tests/topotests/bgp-evpn-mh/hostd22/zebra.conf new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bgp-evpn-mh/spine1/evpn.conf b/tests/topotests/bgp-evpn-mh/spine1/evpn.conf new file mode 100644 index 0000000000..2e26f60f44 --- /dev/null +++ b/tests/topotests/bgp-evpn-mh/spine1/evpn.conf @@ -0,0 +1,17 @@ +frr defaults datacenter +! +router bgp 65001 + bgp router-id 192.168.100.13 + no bgp ebgp-requires-policy + neighbor 192.168.1.2 remote-as external + neighbor 192.168.2.2 remote-as external + neighbor 192.168.3.2 remote-as external + neighbor 192.168.4.2 remote-as external + redistribute connected + address-family l2vpn evpn + neighbor 192.168.1.2 activate + neighbor 192.168.2.2 activate + neighbor 192.168.3.2 activate + neighbor 192.168.4.2 activate + exit-address-family +! diff --git a/tests/topotests/bgp-evpn-mh/spine1/pim.conf b/tests/topotests/bgp-evpn-mh/spine1/pim.conf new file mode 100644 index 0000000000..68e686e8c7 --- /dev/null +++ b/tests/topotests/bgp-evpn-mh/spine1/pim.conf @@ -0,0 +1,18 @@ +ip pim rp 192.168.100.13 +ip pim spt-switchover infinity-and-beyond +! +int lo + ip pim +! +int spine1-eth0 + ip pim +! +int spine1-eth1 + ip pim +! +int spine1-eth2 + ip pim +! +int spine1-eth3 + ip pim +! diff --git a/tests/topotests/bgp-evpn-mh/spine1/zebra.conf b/tests/topotests/bgp-evpn-mh/spine1/zebra.conf new file mode 100644 index 0000000000..80e9e5a263 --- /dev/null +++ b/tests/topotests/bgp-evpn-mh/spine1/zebra.conf @@ -0,0 +1,15 @@ +int spine1-eth0 + ip addr 192.168.1.1/24 +! +int spine1-eth1 + ip addr 192.168.2.1/24 +! +int spine1-eth2 + ip addr 192.168.3.1/24 +! +int spine1-eth3 + ip addr 192.168.4.1/24 +! +int lo + ip addr 192.168.100.13/32 + ip addr 192.168.100.100/32 diff --git a/tests/topotests/bgp-evpn-mh/spine2/evpn.conf b/tests/topotests/bgp-evpn-mh/spine2/evpn.conf new file mode 100644 index 0000000000..ec2e789276 --- /dev/null +++ b/tests/topotests/bgp-evpn-mh/spine2/evpn.conf @@ -0,0 +1,17 @@ +frr defaults datacenter +! +router bgp 65001 + bgp router-id 192.168.100.14 + no bgp ebgp-requires-policy + neighbor 192.168.5.2 remote-as external + neighbor 192.168.6.2 remote-as external + neighbor 192.168.7.2 remote-as external + neighbor 192.168.8.2 remote-as external + redistribute connected + address-family l2vpn evpn + neighbor 192.168.5.2 activate + neighbor 192.168.6.2 activate + neighbor 192.168.7.2 activate + neighbor 192.168.8.2 activate + exit-address-family +! diff --git a/tests/topotests/bgp-evpn-mh/spine2/pim.conf b/tests/topotests/bgp-evpn-mh/spine2/pim.conf new file mode 100644 index 0000000000..c1566240e6 --- /dev/null +++ b/tests/topotests/bgp-evpn-mh/spine2/pim.conf @@ -0,0 +1,18 @@ +ip pim rp 192.168.100.13 +ip pim spt-switchover infinity-and-beyond +! +int lo + ip pim +! +int spine2-eth0 + ip pim +! +int spine2-eth1 + ip pim +! +int spine2-eth2 + ip pim +! +int spine2-eth3 + ip pim +! diff --git a/tests/topotests/bgp-evpn-mh/spine2/zebra.conf b/tests/topotests/bgp-evpn-mh/spine2/zebra.conf new file mode 100644 index 0000000000..1cd1df8c81 --- /dev/null +++ b/tests/topotests/bgp-evpn-mh/spine2/zebra.conf @@ -0,0 +1,15 @@ +int spine2-eth0 + ip addr 192.168.5.1/24 +! +int spine2-eth1 + ip addr 192.168.6.1/24 +! +int spine2-eth2 + ip addr 192.168.7.1/24 +! +int spine2-eth3 + ip addr 192.168.8.1/24 +! +int lo + ip addr 192.168.100.14/32 + ip addr 192.168.100.100/32 diff --git a/tests/topotests/bgp-evpn-mh/test_evpn_mh.py b/tests/topotests/bgp-evpn-mh/test_evpn_mh.py new file mode 100755 index 0000000000..fe28f79bd4 --- /dev/null +++ b/tests/topotests/bgp-evpn-mh/test_evpn_mh.py @@ -0,0 +1,651 @@ +#!/usr/bin/env python + +# +# test_evpn_mh.py +# +# Copyright (c) 2020 by +# Cumulus Networks, Inc. +# Anuradha Karuppiah +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_evpn_mh.py: Testing EVPN multihoming + +""" + +import os +import re +import sys +import pytest +import json +import platform +from functools import partial + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + +##################################################### +## +## Network Topology Definition +## +## See topology picture at evpn-mh-topo-tests.pdf +##################################################### + + +class NetworkTopo(Topo): + ''' + EVPN Multihoming Topology - + 1. Two level CLOS + 2. Two spine switches - spine1, spine2 + 3. Two racks with Top-of-Rack switches per rack - tormx1, tormx2 + 4. Two dual attached hosts per-rack - hostdx1, hostdx2 + ''' + + def build(self, **_opts): + "Build function" + + tgen = get_topogen(self) + + tgen.add_router("spine1") + tgen.add_router("spine2") + tgen.add_router("torm11") + tgen.add_router("torm12") + tgen.add_router("torm21") + tgen.add_router("torm22") + tgen.add_router("hostd11") + tgen.add_router("hostd12") + tgen.add_router("hostd21") + tgen.add_router("hostd22") + + # On main router + # First switch is for a dummy interface (for local network) + + + ##################### spine1 ######################## + # spine1-eth0 is connected to torm11-eth0 + switch = tgen.add_switch("sw1") + switch.add_link(tgen.gears["spine1"]) + switch.add_link(tgen.gears["torm11"]) + + # spine1-eth1 is connected to torm12-eth0 + switch = tgen.add_switch("sw2") + switch.add_link(tgen.gears["spine1"]) + switch.add_link(tgen.gears["torm12"]) + + # spine1-eth2 is connected to torm21-eth0 + switch = tgen.add_switch("sw3") + switch.add_link(tgen.gears["spine1"]) + switch.add_link(tgen.gears["torm21"]) + + # spine1-eth3 is connected to torm22-eth0 + switch = tgen.add_switch("sw4") + switch.add_link(tgen.gears["spine1"]) + switch.add_link(tgen.gears["torm22"]) + + ##################### spine2 ######################## + # spine2-eth0 is connected to torm11-eth1 + switch = tgen.add_switch("sw5") + switch.add_link(tgen.gears["spine2"]) + switch.add_link(tgen.gears["torm11"]) + + # spine2-eth1 is connected to torm12-eth1 + switch = tgen.add_switch("sw6") + switch.add_link(tgen.gears["spine2"]) + switch.add_link(tgen.gears["torm12"]) + + # spine2-eth2 is connected to torm21-eth1 + switch = tgen.add_switch("sw7") + switch.add_link(tgen.gears["spine2"]) + switch.add_link(tgen.gears["torm21"]) + + # spine2-eth3 is connected to torm22-eth1 + switch = tgen.add_switch("sw8") + switch.add_link(tgen.gears["spine2"]) + switch.add_link(tgen.gears["torm22"]) + + ##################### torm11 ######################## + # torm11-eth2 is connected to hostd11-eth0 + switch = tgen.add_switch("sw9") + switch.add_link(tgen.gears["torm11"]) + switch.add_link(tgen.gears["hostd11"]) + + # torm11-eth3 is connected to hostd12-eth0 + switch = tgen.add_switch("sw10") + switch.add_link(tgen.gears["torm11"]) + switch.add_link(tgen.gears["hostd12"]) + + ##################### torm12 ######################## + # torm12-eth2 is connected to hostd11-eth1 + switch = tgen.add_switch("sw11") + switch.add_link(tgen.gears["torm12"]) + switch.add_link(tgen.gears["hostd11"]) + + # torm12-eth3 is connected to hostd12-eth1 + switch = tgen.add_switch("sw12") + switch.add_link(tgen.gears["torm12"]) + switch.add_link(tgen.gears["hostd12"]) + + ##################### torm21 ######################## + # torm21-eth2 is connected to hostd21-eth0 + switch = tgen.add_switch("sw13") + switch.add_link(tgen.gears["torm21"]) + switch.add_link(tgen.gears["hostd21"]) + + # torm21-eth3 is connected to hostd22-eth0 + switch = tgen.add_switch("sw14") + switch.add_link(tgen.gears["torm21"]) + switch.add_link(tgen.gears["hostd22"]) + + ##################### torm22 ######################## + # torm22-eth2 is connected to hostd21-eth1 + switch = tgen.add_switch("sw15") + switch.add_link(tgen.gears["torm22"]) + switch.add_link(tgen.gears["hostd21"]) + + # torm22-eth3 is connected to hostd22-eth1 + switch = tgen.add_switch("sw16") + switch.add_link(tgen.gears["torm22"]) + switch.add_link(tgen.gears["hostd22"]) + + +##################################################### +## +## Tests starting +## +##################################################### + +tor_ips = {"torm11" : "192.168.100.15", \ + "torm12" : "192.168.100.16", \ + "torm21" : "192.168.100.17", \ + "torm22" : "192.168.100.18"} + +svi_ips = {"torm11" : "45.0.0.2", \ + "torm12" : "45.0.0.3", \ + "torm21" : "45.0.0.4", \ + "torm22" : "45.0.0.5"} + +tor_ips_rack_1 = {"torm11" : "192.168.100.15", \ + "torm12" : "192.168.100.16"} + +tor_ips_rack_2 = {"torm21" : "192.168.100.17", \ + "torm22" : "192.168.100.18"} + +host_es_map = {"hostd11" : "03:44:38:39:ff:ff:01:00:00:01", + "hostd12" : "03:44:38:39:ff:ff:01:00:00:02", + "hostd21" : "03:44:38:39:ff:ff:02:00:00:01", + "hostd22" : "03:44:38:39:ff:ff:02:00:00:02"} + +def config_bond(node, bond_name, bond_members, bond_ad_sys_mac, br): + ''' + Used to setup bonds on the TORs and hosts for MH + ''' + node.run("ip link add dev %s type bond mode 802.3ad" % bond_name) + node.run("ip link set dev %s type bond lacp_rate 1" % bond_name) + node.run("ip link set dev %s type bond miimon 100" % bond_name) + node.run("ip link set dev %s type bond xmit_hash_policy layer3+4" % bond_name) + node.run("ip link set dev %s type bond min_links 1" % bond_name) + node.run("ip link set dev %s type bond ad_actor_system %s" %\ + (bond_name, bond_ad_sys_mac)) + + for bond_member in bond_members: + node.run("ip link set dev %s down" % bond_member) + node.run("ip link set dev %s master %s" % (bond_member, bond_name)) + node.run("ip link set dev %s up" % bond_member) + + node.run("ip link set dev %s up" % bond_name) + + # if bridge is specified add the bond as a bridge member + if br: + node.run(" ip link set dev %s master bridge" % bond_name) + node.run("/sbin/bridge link set dev %s priority 8" % bond_name) + node.run("/sbin/bridge vlan del vid 1 dev %s" % bond_name) + node.run("/sbin/bridge vlan del vid 1 untagged pvid dev %s" % bond_name) + node.run("/sbin/bridge vlan add vid 1000 dev %s" % bond_name) + node.run("/sbin/bridge vlan add vid 1000 untagged pvid dev %s"\ + % bond_name) + + +def config_mcast_tunnel_termination_device(node): + ''' + The kernel requires a device to terminate VxLAN multicast tunnels + when EVPN-PIM is used for flooded traffic + ''' + node.run("ip link add dev ipmr-lo type dummy") + node.run("ip link set dev ipmr-lo mtu 16000") + node.run("ip link set dev ipmr-lo mode dormant") + node.run("ip link set dev ipmr-lo up") + + +def config_bridge(node): + ''' + Create a VLAN aware bridge + ''' + node.run("ip link add dev bridge type bridge stp_state 0") + node.run("ip link set dev bridge type bridge vlan_filtering 1") + node.run("ip link set dev bridge mtu 9216") + node.run("ip link set dev bridge type bridge ageing_time 1800") + node.run("ip link set dev bridge type bridge mcast_snooping 0") + node.run("ip link set dev bridge type bridge vlan_stats_enabled 1") + node.run("ip link set dev bridge up") + node.run("/sbin/bridge vlan add vid 1000 dev bridge") + + +def config_vxlan(node, node_ip): + ''' + Create a VxLAN device for VNI 1000 and add it to the bridge. + VLAN-1000 is mapped to VNI-1000. + ''' + node.run("ip link add dev vx-1000 type vxlan id 1000 dstport 4789") + node.run("ip link set dev vx-1000 type vxlan nolearning") + node.run("ip link set dev vx-1000 type vxlan local %s" % node_ip) + node.run("ip link set dev vx-1000 type vxlan ttl 64") + node.run("ip link set dev vx-1000 mtu 9152") + node.run("ip link set dev vx-1000 type vxlan dev ipmr-lo group 239.1.1.100") + node.run("ip link set dev vx-1000 up") + + # bridge attrs + node.run("ip link set dev vx-1000 master bridge") + node.run("/sbin/bridge link set dev vx-1000 neigh_suppress on") + node.run("/sbin/bridge link set dev vx-1000 learning off") + node.run("/sbin/bridge link set dev vx-1000 priority 8") + node.run("/sbin/bridge vlan del vid 1 dev vx-1000") + node.run("/sbin/bridge vlan del vid 1 untagged pvid dev vx-1000") + node.run("/sbin/bridge vlan add vid 1000 dev vx-1000") + node.run("/sbin/bridge vlan add vid 1000 untagged pvid dev vx-1000") + + +def config_svi(node, svi_pip): + ''' + Create an SVI for VLAN 1000 + ''' + node.run("ip link add link bridge name vlan1000 type vlan id 1000 protocol 802.1q") + node.run("ip addr add %s/24 dev vlan1000" % svi_pip) + node.run("ip link set dev vlan1000 up") + node.run("/sbin/sysctl net.ipv4.conf.vlan1000.arp_accept=1") + node.run("ip link add link vlan1000 name vlan1000-v0 type macvlan mode private") + node.run("/sbin/sysctl net.ipv6.conf.vlan1000-v0.accept_dad=0") + node.run("/sbin/sysctl net.ipv6.conf.vlan1000-v0.dad_transmits") + node.run("/sbin/sysctl net.ipv6.conf.vlan1000-v0.dad_transmits=0") + node.run("ip link set dev vlan1000-v0 address 00:00:5e:00:01:01") + node.run("ip link set dev vlan1000-v0 up") + # metric 1024 is not working + node.run("ip addr add 45.0.0.1/24 dev vlan1000-v0") + + +def config_tor(tor_name, tor, tor_ip, svi_pip): + ''' + Create the bond/vxlan-bridge on the TOR which acts as VTEP and EPN-PE + ''' + # create a device for terminating VxLAN multicast tunnels + config_mcast_tunnel_termination_device(tor) + + # create a vlan aware bridge + config_bridge(tor) + + # create vxlan device and add it to bridge + config_vxlan(tor, tor_ip) + + # create hostbonds and add them to the bridge + if "torm1" in tor_name: + sys_mac = "44:38:39:ff:ff:01" + else: + sys_mac = "44:38:39:ff:ff:02" + bond_member = tor_name + "-eth2" + config_bond(tor, "hostbond1", [bond_member], sys_mac, "bridge") + + bond_member = tor_name + "-eth3" + config_bond(tor, "hostbond2", [bond_member], sys_mac, "bridge") + + # create SVI + config_svi(tor, svi_pip) + + +def config_tors(tgen, tors): + for tor_name in tors: + tor = tgen.gears[tor_name] + config_tor(tor_name, tor, tor_ips.get(tor_name), svi_ips.get(tor_name)) + +def compute_host_ip_mac(host_name): + host_id = host_name.split("hostd")[1] + host_ip = "45.0.0."+ host_id + "/24" + host_mac = "00:00:00:00:00:" + host_id + + return host_ip, host_mac + +def config_host(host_name, host): + ''' + Create the dual-attached bond on host nodes for MH + ''' + bond_members = [] + bond_members.append(host_name + "-eth0") + bond_members.append(host_name + "-eth1") + bond_name = "torbond" + config_bond(host, bond_name, bond_members, "00:00:00:00:00:00", None) + + host_ip, host_mac = compute_host_ip_mac(host_name) + host.run("ip addr add %s dev %s" % (host_ip, bond_name)) + host.run("ip link set dev %s address %s" % (bond_name, host_mac)) + + +def config_hosts(tgen, hosts): + for host_name in hosts: + host = tgen.gears[host_name] + config_host(host_name, host) + + +def setup_module(module): + "Setup topology" + tgen = Topogen(NetworkTopo, module.__name__) + tgen.start_topology() + + krel = platform.release() + if topotest.version_cmp(krel, "4.19") < 0: + tgen.errors = "kernel 4.19 needed for multihoming tests" + pytest.skip(tgen.errors) + + tors = [] + tors.append("torm11") + tors.append("torm12") + tors.append("torm21") + tors.append("torm22") + config_tors(tgen, tors) + + hosts = [] + hosts.append("hostd11") + hosts.append("hostd12") + hosts.append("hostd21") + hosts.append("hostd22") + config_hosts(tgen, hosts) + + # tgen.mininet_cli() + # This is a sample of configuration loading. + router_list = tgen.routers() + for rname, router in router_list.iteritems(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_PIM, os.path.join(CWD, "{}/pim.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/evpn.conf".format(rname)) + ) + tgen.start_router() + # tgen.mininet_cli() + + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + + +def check_local_es(esi, vtep_ips, dut_name, down_vteps): + ''' + Check if ES peers are setup correctly on local ESs + ''' + peer_ips = [] + if "torm1" in dut_name: + tor_ips_rack = tor_ips_rack_1 + else: + tor_ips_rack = tor_ips_rack_2 + + for tor_name, tor_ip in tor_ips_rack.iteritems(): + if dut_name not in tor_name: + peer_ips.append(tor_ip) + + # remove down VTEPs from the peer check list + peer_set = set(peer_ips) + down_vtep_set = set(down_vteps) + peer_set = peer_set - down_vtep_set + + vtep_set = set(vtep_ips) + diff = peer_set.symmetric_difference(vtep_set) + + return (esi, diff) if diff else None + + +def check_remote_es(esi, vtep_ips, dut_name, down_vteps): + ''' + Verify list of PEs associated with a remote ES + ''' + remote_ips = [] + + if "torm1" in dut_name: + tor_ips_rack = tor_ips_rack_2 + else: + tor_ips_rack = tor_ips_rack_1 + + for tor_name, tor_ip in tor_ips_rack.iteritems(): + remote_ips.append(tor_ip) + + # remove down VTEPs from the remote check list + remote_set = set(remote_ips) + down_vtep_set = set(down_vteps) + remote_set = remote_set - down_vtep_set + + vtep_set = set(vtep_ips) + diff = remote_set.symmetric_difference(vtep_set) + + return (esi, diff) if diff else None + +def check_es(dut): + ''' + Verify list of PEs associated all ESs, local and remote + ''' + bgp_es = dut.vtysh_cmd("show bgp l2vp evpn es json") + bgp_es_json = json.loads(bgp_es) + + result = None + + expected_es_set = set([v for k, v in host_es_map.iteritems()]) + curr_es_set = [] + + # check is ES content is correct + for es in bgp_es_json: + esi = es["esi"] + curr_es_set.append(esi) + types = es["type"] + vtep_ips = [] + for vtep in es["vteps"]: + vtep_ips.append(vtep["vtep_ip"]) + + if "local" in types: + result = check_local_es(esi, vtep_ips, dut.name, []) + else: + result = check_remote_es(esi, vtep_ips, dut.name, []) + + if result: + return result + + # check if all ESs are present + curr_es_set = set(curr_es_set) + result = curr_es_set.symmetric_difference(expected_es_set) + + return result if result else None + +def check_one_es(dut, esi, down_vteps): + ''' + Verify list of PEs associated all ESs, local and remote + ''' + bgp_es = dut.vtysh_cmd("show bgp l2vp evpn es %s json" % esi) + es = json.loads(bgp_es) + + if not es: + return "esi %s not found" % esi + + esi = es["esi"] + types = es["type"] + vtep_ips = [] + for vtep in es["vteps"]: + vtep_ips.append(vtep["vtep_ip"]) + + if "local" in types: + result = check_local_es(esi, vtep_ips, dut.name, down_vteps) + else: + result = check_remote_es(esi, vtep_ips, dut.name, down_vteps) + + return result + +def test_evpn_es(): + ''' + Two ES are setup on each rack. This test checks if - + 1. ES peer has been added to the local ES (via Type-1/EAD route) + 2. The remote ESs are setup with the right list of PEs (via Type-1) + ''' + + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + dut_name = "torm11" + dut = tgen.gears[dut_name] + test_fn = partial(check_es, dut) + _, result = topotest.run_and_expect(test_fn, None, count=20, wait=3) + + assertmsg = '"{}" ES content incorrect'.format(dut_name) + assert result is None, assertmsg + # tgen.mininet_cli() + +def test_evpn_ead_update(): + ''' + Flap a host link one the remote rack and check if the EAD updates + are sent/processed for the corresponding ESI + ''' + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # dut on rack1 and host link flap on rack2 + dut_name = "torm11" + dut = tgen.gears[dut_name] + + remote_tor_name = "torm21" + remote_tor = tgen.gears[remote_tor_name] + + host_name = "hostd21" + host = tgen.gears[host_name] + esi = host_es_map.get(host_name) + + # check if the VTEP list is right to start with + down_vteps = [] + test_fn = partial(check_one_es, dut, esi, down_vteps) + _, result = topotest.run_and_expect(test_fn, None, count=20, wait=3) + assertmsg = '"{}" ES content incorrect'.format(dut_name) + assert result is None, assertmsg + + # down a remote host link and check if the EAD withdraw is rxed + # Note: LACP is not working as expected so I am temporarily shutting + # down the link on the remote TOR instead of the remote host + remote_tor.run("ip link set dev %s-%s down" % (remote_tor_name, "eth2")) + down_vteps.append(tor_ips.get(remote_tor_name)) + _, result = topotest.run_and_expect(test_fn, None, count=20, wait=3) + assertmsg = '"{}" ES incorrect after remote link down'.format(dut_name) + assert result is None, assertmsg + + # bring up remote host link and check if the EAD update is rxed + down_vteps.remove(tor_ips.get(remote_tor_name)) + remote_tor.run("ip link set dev %s-%s up" % (remote_tor_name, "eth2")) + _, result = topotest.run_and_expect(test_fn, None, count=20, wait=3) + assertmsg = '"{}" ES incorrect after remote link flap'.format(dut_name) + assert result is None, assertmsg + + # tgen.mininet_cli() + +def check_mac(dut, vni, mac, m_type, esi, intf): + ''' + checks if mac is present and if desination matches the one provided + ''' + + out = dut.vtysh_cmd("show evpn mac vni %d mac %s json" % (vni, mac)) + + mac_js = json.loads(out) + for mac, info in mac_js.iteritems(): + tmp_esi = info.get("esi", "") + tmp_m_type = info.get("type", "") + tmp_intf = info.get("intf", "") if tmp_m_type == "local" else "" + if tmp_esi == esi and tmp_m_type == m_type and intf == intf: + return None + + return "invalid vni %d mac %s out %s" % (vni, mac, mac_js) + +def test_evpn_mac(): + ''' + 1. Add a MAC on hostd11 and check if the MAC is synced between + torm11 and torm12. And installed as a local MAC. + 2. Add a MAC on hostd21 and check if the MAC is installed as a + remote MAC on torm11 and torm12 + ''' + + tgen = get_topogen() + + local_host = tgen.gears["hostd11"] + remote_host = tgen.gears["hostd21"] + tors = [] + tors.append(tgen.gears["torm11"]) + tors.append(tgen.gears["torm12"]) + + # ping the anycast gw from the local and remote hosts to populate + # the mac address on the PEs + local_host.run("arping -I torbond -c 1 45.0.0.1") + remote_host.run("arping -I torbond -c 1 45.0.0.1") + + vni = 1000 + + # check if the rack-1 host MAC is present on all rack-1 PEs + # and points to local access port + m_type = "local" + _, mac = compute_host_ip_mac(local_host.name) + esi = host_es_map.get(local_host.name) + intf = "hostbond1" + + for tor in tors: + test_fn = partial(check_mac, tor, vni, mac, m_type, esi, intf) + _, result = topotest.run_and_expect(test_fn, None, count=20, wait=3) + assertmsg = '"{}" local MAC content incorrect'.format(tor.name) + assert result is None, assertmsg + + # check if the rack-2 host MAC is present on all rack-1 PEs + # and points to the remote ES destination + m_type = "remote" + _, mac = compute_host_ip_mac(remote_host.name) + esi = host_es_map.get(remote_host.name) + intf = "" + + for tor in tors: + test_fn = partial(check_mac, tor, vni, mac, m_type, esi, intf) + _, result = topotest.run_and_expect(test_fn, None, count=20, wait=3) + assertmsg = '"{}" remote MAC content incorrect'.format(tor.name) + assert result is None, assertmsg + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp-evpn-mh/torm11/evpn.conf b/tests/topotests/bgp-evpn-mh/torm11/evpn.conf new file mode 100644 index 0000000000..01f4b65704 --- /dev/null +++ b/tests/topotests/bgp-evpn-mh/torm11/evpn.conf @@ -0,0 +1,21 @@ +! +frr defaults datacenter +! +debug bgp evpn mh es +debug bgp evpn mh route +debug bgp zebra +! +! +router bgp 65002 + bgp router-id 192.168.100.15 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external + neighbor 192.168.5.1 remote-as external + redistribute connected + address-family l2vpn evpn + neighbor 192.168.1.1 activate + neighbor 192.168.5.1 activate + advertise-all-vni + advertise-svi-ip + exit-address-family +! diff --git a/tests/topotests/bgp-evpn-mh/torm11/pim.conf b/tests/topotests/bgp-evpn-mh/torm11/pim.conf new file mode 100644 index 0000000000..fbba735873 --- /dev/null +++ b/tests/topotests/bgp-evpn-mh/torm11/pim.conf @@ -0,0 +1,13 @@ +! +ip pim rp 192.168.100.13 239.1.1.0/24 +ip pim spt-switchover infinity-and-beyond +! +interface lo + ip igmp + ip pim +! +interface torm11-eth0 + ip pim +! +interface torm11-eth1 + ip pim diff --git a/tests/topotests/bgp-evpn-mh/torm11/zebra.conf b/tests/topotests/bgp-evpn-mh/torm11/zebra.conf new file mode 100644 index 0000000000..ee4e87e1c2 --- /dev/null +++ b/tests/topotests/bgp-evpn-mh/torm11/zebra.conf @@ -0,0 +1,23 @@ +debug zebra evpn mh es +debug zebra evpn mh mac +debug zebra evpn mh neigh +debug zebra evpn mh nh +debug zebra vxlan +! +int torm11-eth0 + ip addr 192.168.1.2/24 +! +int torm11-eth1 + ip addr 192.168.5.2/24 +! +int lo + ip addr 192.168.100.15/32 +! +interface hostbond1 + evpn mh es-id 1 + evpn mh es-sys-mac 44:38:39:ff:ff:01 +! +interface hostbond2 + evpn mh es-id 2 + evpn mh es-sys-mac 44:38:39:ff:ff:01 +! diff --git a/tests/topotests/bgp-evpn-mh/torm12/evpn.conf b/tests/topotests/bgp-evpn-mh/torm12/evpn.conf new file mode 100644 index 0000000000..2c13024bbc --- /dev/null +++ b/tests/topotests/bgp-evpn-mh/torm12/evpn.conf @@ -0,0 +1,21 @@ +! +frr defaults datacenter +! +debug bgp evpn mh es +debug bgp evpn mh route +debug bgp zebra +! +! +router bgp 65003 + bgp router-id 192.168.100.16 + no bgp ebgp-requires-policy + neighbor 192.168.2.1 remote-as external + neighbor 192.168.6.1 remote-as external + redistribute connected + address-family l2vpn evpn + neighbor 192.168.2.1 activate + neighbor 192.168.6.1 activate + advertise-all-vni + advertise-svi-ip + exit-address-family +! diff --git a/tests/topotests/bgp-evpn-mh/torm12/pim.conf b/tests/topotests/bgp-evpn-mh/torm12/pim.conf new file mode 100644 index 0000000000..3dd63b44ca --- /dev/null +++ b/tests/topotests/bgp-evpn-mh/torm12/pim.conf @@ -0,0 +1,13 @@ +! +ip pim rp 192.168.100.13 239.1.1.0/24 +ip pim spt-switchover infinity-and-beyond +! +interface lo + ip igmp + ip pim +! +interface torm12-eth0 + ip pim +! +interface torm12-eth1 + ip pim diff --git a/tests/topotests/bgp-evpn-mh/torm12/zebra.conf b/tests/topotests/bgp-evpn-mh/torm12/zebra.conf new file mode 100644 index 0000000000..736af4159e --- /dev/null +++ b/tests/topotests/bgp-evpn-mh/torm12/zebra.conf @@ -0,0 +1,23 @@ +debug zebra evpn mh es +debug zebra evpn mh mac +debug zebra evpn mh neigh +debug zebra evpn mh nh +debug zebra vxlan +! +int torm12-eth0 + ip addr 192.168.2.2/24 +! +int torm12-eth1 + ip addr 192.168.6.2/24 +! +int lo + ip addr 192.168.100.16/32 +! +interface hostbond1 + evpn mh es-id 1 + evpn mh es-sys-mac 44:38:39:ff:ff:01 +! +interface hostbond2 + evpn mh es-id 2 + evpn mh es-sys-mac 44:38:39:ff:ff:01 +! diff --git a/tests/topotests/bgp-evpn-mh/torm21/evpn.conf b/tests/topotests/bgp-evpn-mh/torm21/evpn.conf new file mode 100644 index 0000000000..2a2ba061c6 --- /dev/null +++ b/tests/topotests/bgp-evpn-mh/torm21/evpn.conf @@ -0,0 +1,21 @@ +! +frr defaults datacenter +! +debug bgp evpn mh es +debug bgp evpn mh route +debug bgp zebra +! +! +router bgp 65004 + bgp router-id 192.168.100.17 + no bgp ebgp-requires-policy + neighbor 192.168.3.1 remote-as external + neighbor 192.168.7.1 remote-as external + redistribute connected + address-family l2vpn evpn + neighbor 192.168.3.1 activate + neighbor 192.168.7.1 activate + advertise-all-vni + advertise-svi-ip + exit-address-family +! diff --git a/tests/topotests/bgp-evpn-mh/torm21/pim.conf b/tests/topotests/bgp-evpn-mh/torm21/pim.conf new file mode 100644 index 0000000000..71aa91a06d --- /dev/null +++ b/tests/topotests/bgp-evpn-mh/torm21/pim.conf @@ -0,0 +1,13 @@ +! +ip pim rp 192.168.100.13 239.1.1.0/24 +ip pim spt-switchover infinity-and-beyond +! +interface lo + ip igmp + ip pim +! +interface torm21-eth0 + ip pim +! +interface torm21-eth1 + ip pim diff --git a/tests/topotests/bgp-evpn-mh/torm21/zebra.conf b/tests/topotests/bgp-evpn-mh/torm21/zebra.conf new file mode 100644 index 0000000000..0ebe6f2d95 --- /dev/null +++ b/tests/topotests/bgp-evpn-mh/torm21/zebra.conf @@ -0,0 +1,23 @@ +debug zebra evpn mh es +debug zebra evpn mh mac +debug zebra evpn mh neigh +debug zebra evpn mh nh +debug zebra vxlan +! +int torm21-eth0 + ip addr 192.168.3.2/24 +! +int torm21-eth1 + ip addr 192.168.7.2/24 +! +int lo + ip addr 192.168.100.17/32 +! +interface hostbond1 + evpn mh es-id 1 + evpn mh es-sys-mac 44:38:39:ff:ff:02 +! +interface hostbond2 + evpn mh es-id 2 + evpn mh es-sys-mac 44:38:39:ff:ff:02 +! diff --git a/tests/topotests/bgp-evpn-mh/torm22/evpn.conf b/tests/topotests/bgp-evpn-mh/torm22/evpn.conf new file mode 100644 index 0000000000..b4f4f1dc25 --- /dev/null +++ b/tests/topotests/bgp-evpn-mh/torm22/evpn.conf @@ -0,0 +1,21 @@ +! +frr defaults datacenter +! +debug bgp evpn mh es +debug bgp evpn mh route +debug bgp zebra +! +! +router bgp 65005 + bgp router-id 192.168.100.18 + no bgp ebgp-requires-policy + neighbor 192.168.4.1 remote-as external + neighbor 192.168.8.1 remote-as external + redistribute connected + address-family l2vpn evpn + neighbor 192.168.4.1 activate + neighbor 192.168.8.1 activate + advertise-all-vni + advertise-svi-ip + exit-address-family +! diff --git a/tests/topotests/bgp-evpn-mh/torm22/pim.conf b/tests/topotests/bgp-evpn-mh/torm22/pim.conf new file mode 100644 index 0000000000..46f330f5cd --- /dev/null +++ b/tests/topotests/bgp-evpn-mh/torm22/pim.conf @@ -0,0 +1,13 @@ +! +ip pim rp 192.168.100.13 239.1.1.0/24 +ip pim spt-switchover infinity-and-beyond +! +interface lo + ip igmp + ip pim +! +interface torm22-eth0 + ip pim +! +interface torm22-eth1 + ip pim diff --git a/tests/topotests/bgp-evpn-mh/torm22/zebra.conf b/tests/topotests/bgp-evpn-mh/torm22/zebra.conf new file mode 100644 index 0000000000..356d8a43e7 --- /dev/null +++ b/tests/topotests/bgp-evpn-mh/torm22/zebra.conf @@ -0,0 +1,23 @@ +debug zebra evpn mh es +debug zebra evpn mh mac +debug zebra evpn mh neigh +debug zebra evpn mh nh +debug zebra vxlan +! +int torm22-eth0 + ip addr 192.168.4.2/24 +! +int torm22-eth1 + ip addr 192.168.8.2/24 +! +int lo + ip addr 192.168.100.18/32 +! +interface hostbond1 + evpn mh es-id 1 + evpn mh es-sys-mac 44:38:39:ff:ff:02 +! +interface hostbond2 + evpn mh es-id 2 + evpn mh es-sys-mac 44:38:39:ff:ff:02 +! diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/P1/bgpd.conf b/tests/topotests/bgp-evpn-vxlan_topo1/P1/bgpd.conf new file mode 100644 index 0000000000..cdf4cb4feb --- /dev/null +++ b/tests/topotests/bgp-evpn-vxlan_topo1/P1/bgpd.conf @@ -0,0 +1 @@ +! diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/P1/ospfd.conf b/tests/topotests/bgp-evpn-vxlan_topo1/P1/ospfd.conf new file mode 100644 index 0000000000..2db7edb806 --- /dev/null +++ b/tests/topotests/bgp-evpn-vxlan_topo1/P1/ospfd.conf @@ -0,0 +1,13 @@ +! +router ospf + network 10.20.0.0/16 area 0 + network 10.20.20.20/32 area 0 +! +int P1-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +int P1-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/P1/zebra.conf b/tests/topotests/bgp-evpn-vxlan_topo1/P1/zebra.conf new file mode 100644 index 0000000000..95b5da8402 --- /dev/null +++ b/tests/topotests/bgp-evpn-vxlan_topo1/P1/zebra.conf @@ -0,0 +1,7 @@ +! +interface lo + ip address 10.20.20.20/32 +interface P1-eth0 + ip address 10.20.1.2/24 +interface P1-eth1 + ip address 10.20.2.2/24 diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/PE1/bgpd.conf b/tests/topotests/bgp-evpn-vxlan_topo1/PE1/bgpd.conf new file mode 100644 index 0000000000..c7a76a98ed --- /dev/null +++ b/tests/topotests/bgp-evpn-vxlan_topo1/PE1/bgpd.conf @@ -0,0 +1,10 @@ +router bgp 65000 + timers 3 9 + bgp router-id 10.10.10.10 + no bgp default ipv4-unicast + neighbor 10.30.30.30 remote-as 65000 + neighbor 10.30.30.30 update-source lo + neighbor 10.30.30.30 timers 3 10 + address-family l2vpn evpn + neighbor 10.30.30.30 activate + advertise-all-vni diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/PE1/evpn.vni.json b/tests/topotests/bgp-evpn-vxlan_topo1/PE1/evpn.vni.json new file mode 100644 index 0000000000..2937504244 --- /dev/null +++ b/tests/topotests/bgp-evpn-vxlan_topo1/PE1/evpn.vni.json @@ -0,0 +1,15 @@ +{ + "vni":101, + "type":"L2", + "vrf":"default", + "vxlanInterface":"vxlan101", + "vtepIp":"10.10.10.10", + "mcastGroup":"0.0.0.0", + "advertiseGatewayMacip":"No", + "numMacs":5, + "numArpNd":3, + "numRemoteVteps":[ + "10.30.30.30" + ] +} + diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/PE1/ospfd.conf b/tests/topotests/bgp-evpn-vxlan_topo1/PE1/ospfd.conf new file mode 100644 index 0000000000..f1c2b42dc1 --- /dev/null +++ b/tests/topotests/bgp-evpn-vxlan_topo1/PE1/ospfd.conf @@ -0,0 +1,9 @@ +! +router ospf + network 10.20.0.0/16 area 0 + network 10.10.10.10/32 area 0 +! +int PE1-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/PE1/zebra.conf b/tests/topotests/bgp-evpn-vxlan_topo1/PE1/zebra.conf new file mode 100644 index 0000000000..e2699475c9 --- /dev/null +++ b/tests/topotests/bgp-evpn-vxlan_topo1/PE1/zebra.conf @@ -0,0 +1,8 @@ +! +log file zebra.log +! +interface lo + ip address 10.10.10.10/32 +interface PE1-eth1 + ip address 10.20.1.1/24 +! diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/PE2/bgpd.conf b/tests/topotests/bgp-evpn-vxlan_topo1/PE2/bgpd.conf new file mode 100644 index 0000000000..0a24158bb8 --- /dev/null +++ b/tests/topotests/bgp-evpn-vxlan_topo1/PE2/bgpd.conf @@ -0,0 +1,11 @@ +router bgp 65000 + timers bgp 3 9 + bgp router-id 10.30.30.30 + no bgp default ipv4-unicast + neighbor 10.10.10.10 remote-as 65000 + neighbor 10.10.10.10 update-source lo + neighbor 10.10.10.10 timers 3 10 + ! + address-family l2vpn evpn + neighbor 10.10.10.10 activate + advertise-all-vni diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/PE2/evpn.vni.json b/tests/topotests/bgp-evpn-vxlan_topo1/PE2/evpn.vni.json new file mode 100644 index 0000000000..0853147a00 --- /dev/null +++ b/tests/topotests/bgp-evpn-vxlan_topo1/PE2/evpn.vni.json @@ -0,0 +1,14 @@ +{ + "vni":101, + "type":"L2", + "vrf":"default", + "vxlanInterface":"vxlan101", + "vtepIp":"10.30.30.30", + "mcastGroup":"0.0.0.0", + "advertiseGatewayMacip":"No", + "numMacs":5, + "numArpNd":3, + "numRemoteVteps":[ + "10.10.10.10" + ] +} diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/PE2/ospfd.conf b/tests/topotests/bgp-evpn-vxlan_topo1/PE2/ospfd.conf new file mode 100644 index 0000000000..065c993303 --- /dev/null +++ b/tests/topotests/bgp-evpn-vxlan_topo1/PE2/ospfd.conf @@ -0,0 +1,9 @@ +! +router ospf + network 10.20.0.0/16 area 0 + network 10.30.30.30/32 area 0 +! +int PE2-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/PE2/zebra.conf b/tests/topotests/bgp-evpn-vxlan_topo1/PE2/zebra.conf new file mode 100644 index 0000000000..9738916ab0 --- /dev/null +++ b/tests/topotests/bgp-evpn-vxlan_topo1/PE2/zebra.conf @@ -0,0 +1,6 @@ +! +interface lo + ip address 10.30.30.30/32 +interface PE2-eth0 + ip address 10.20.2.3/24 +! diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/__init__.py b/tests/topotests/bgp-evpn-vxlan_topo1/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/host1/bgpd.conf b/tests/topotests/bgp-evpn-vxlan_topo1/host1/bgpd.conf new file mode 100644 index 0000000000..cdf4cb4feb --- /dev/null +++ b/tests/topotests/bgp-evpn-vxlan_topo1/host1/bgpd.conf @@ -0,0 +1 @@ +! diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/host1/ospfd.conf b/tests/topotests/bgp-evpn-vxlan_topo1/host1/ospfd.conf new file mode 100644 index 0000000000..cdf4cb4feb --- /dev/null +++ b/tests/topotests/bgp-evpn-vxlan_topo1/host1/ospfd.conf @@ -0,0 +1 @@ +! diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/host1/zebra.conf b/tests/topotests/bgp-evpn-vxlan_topo1/host1/zebra.conf new file mode 100644 index 0000000000..91fae9eeba --- /dev/null +++ b/tests/topotests/bgp-evpn-vxlan_topo1/host1/zebra.conf @@ -0,0 +1,3 @@ +! +int host1-eth0 + ip address 10.10.1.55/24 diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/host2/bgpd.conf b/tests/topotests/bgp-evpn-vxlan_topo1/host2/bgpd.conf new file mode 100644 index 0000000000..cdf4cb4feb --- /dev/null +++ b/tests/topotests/bgp-evpn-vxlan_topo1/host2/bgpd.conf @@ -0,0 +1 @@ +! diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/host2/ospfd.conf b/tests/topotests/bgp-evpn-vxlan_topo1/host2/ospfd.conf new file mode 100644 index 0000000000..cdf4cb4feb --- /dev/null +++ b/tests/topotests/bgp-evpn-vxlan_topo1/host2/ospfd.conf @@ -0,0 +1 @@ +! diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/host2/zebra.conf b/tests/topotests/bgp-evpn-vxlan_topo1/host2/zebra.conf new file mode 100644 index 0000000000..df9adeb3b5 --- /dev/null +++ b/tests/topotests/bgp-evpn-vxlan_topo1/host2/zebra.conf @@ -0,0 +1,3 @@ +! +interface host2-eth0 + ip address 10.10.1.56/24 diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/test_bgp_evpn_vxlan.py b/tests/topotests/bgp-evpn-vxlan_topo1/test_bgp_evpn_vxlan.py new file mode 100755 index 0000000000..90144f5c66 --- /dev/null +++ b/tests/topotests/bgp-evpn-vxlan_topo1/test_bgp_evpn_vxlan.py @@ -0,0 +1,394 @@ +#!/usr/bin/env python + +# +# test_bgp_evpn_vxlan.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2020 by Volta Networks +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_bgp_evpn_vxlan.py: Test VXLAN EVPN MAC a route signalling over BGP. +""" + +import os +import sys +import json +from functools import partial +from time import sleep +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + + +class TemplateTopo(Topo): + "Test topology builder" + + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # This function only purpose is to define allocation and relationship + # between routers, switches and hosts. + # + # + # Create routers + tgen.add_router("P1") + tgen.add_router("PE1") + tgen.add_router("PE2") + tgen.add_router("host1") + tgen.add_router("host2") + + # Host1-PE1 + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["host1"]) + switch.add_link(tgen.gears["PE1"]) + + # PE1-P1 + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["PE1"]) + switch.add_link(tgen.gears["P1"]) + + # P1-PE2 + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["P1"]) + switch.add_link(tgen.gears["PE2"]) + + # PE2-host2 + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["PE2"]) + switch.add_link(tgen.gears["host2"]) + + +def setup_module(mod): + "Sets up the pytest environment" + # This function initiates the topology build with Topogen... + tgen = Topogen(TemplateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + tgen.start_topology() + + pe1 = tgen.gears["PE1"] + pe2 = tgen.gears["PE2"] + p1 = tgen.gears["P1"] + + # set up PE bridges with the EVPN member interfaces facing the CE hosts + pe1.run("ip link add name br101 type bridge stp_state 0") + pe1.run("ip addr add 10.10.1.1/24 dev br101") + pe1.run("ip link set dev br101 up") + pe1.run( + "ip link add vxlan101 type vxlan id 101 dstport 4789 local 10.10.10.10 nolearning" + ) + pe1.run("ip link set dev vxlan101 master br101") + pe1.run("ip link set up dev vxlan101") + pe1.run("ip link set dev PE1-eth0 master br101") + + pe2.run("ip link add name br101 type bridge stp_state 0") + pe2.run("ip addr add 10.10.1.3/24 dev br101") + pe2.run("ip link set dev br101 up") + pe2.run( + "ip link add vxlan101 type vxlan id 101 dstport 4789 local 10.30.30.30 nolearning" + ) + pe2.run("ip link set dev vxlan101 master br101") + pe2.run("ip link set up dev vxlan101") + pe2.run("ip link set dev PE2-eth1 master br101") + p1.run("sysctl -w net.ipv4.ip_forward=1") + + # This is a sample of configuration loading. + router_list = tgen.routers() + + # For all registred routers, load the zebra configuration file + for rname, router in router_list.iteritems(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + # After loading the configurations, this function loads configured daemons. + tgen.start_router() + + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + + +def show_vni_json_elide_ifindex(pe, vni, expected): + output_json = pe.vtysh_cmd("show evpn vni {} json".format(vni), isjson=True) + + if "ifindex" in output_json: + output_json.pop("ifindex") + + return topotest.json_cmp(output_json, expected) + + +def test_pe1_converge_evpn(): + "Wait for protocol convergence" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + pe1 = tgen.gears["PE1"] + json_file = "{}/{}/evpn.vni.json".format(CWD, pe1.name) + expected = json.loads(open(json_file).read()) + + test_func = partial(show_vni_json_elide_ifindex, pe1, 101, expected) + _, result = topotest.run_and_expect(test_func, None, count=125, wait=1) + assertmsg = '"{}" JSON output mismatches'.format(pe1.name) + assert result is None, assertmsg + # tgen.mininet_cli() + + +def test_pe2_converge_evpn(): + "Wait for protocol convergence" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + pe2 = tgen.gears["PE2"] + json_file = "{}/{}/evpn.vni.json".format(CWD, pe2.name) + expected = json.loads(open(json_file).read()) + + test_func = partial(show_vni_json_elide_ifindex, pe2, 101, expected) + _, result = topotest.run_and_expect(test_func, None, count=125, wait=1) + assertmsg = '"{}" JSON output mismatches'.format(pe2.name) + assert result is None, assertmsg + # tgen.mininet_cli() + + +def mac_learn_test(host, local): + "check the host MAC gets learned by the VNI" + + host_output = host.vtysh_cmd("show interface {}-eth0".format(host.name)) + int_lines = host_output.splitlines() + line_items = int_lines[7].split(": ") + mac = line_items[1] + mac_output = local.vtysh_cmd("show evpn mac vni 101 mac {} json".format(mac)) + mac_output_json = json.loads(mac_output) + assertmsg = "Local MAC output does not match interface mac {}".format(mac) + assert mac_output_json[mac]["type"] == "local", assertmsg + + +def mac_test_local_remote(local, remote): + "test MAC transfer between local and remote" + + local_output = local.vtysh_cmd("show evpn mac vni all json") + remote_output = remote.vtysh_cmd("show evpn mac vni all json") + local_output_vni = local.vtysh_cmd("show evpn vni detail json") + local_output_json = json.loads(local_output) + remote_output_json = json.loads(remote_output) + local_output_vni_json = json.loads(local_output_vni) + + for vni in local_output_json: + mac_list = local_output_json[vni]["macs"] + for mac in mac_list: + if mac_list[mac]["type"] == "local" and mac_list[mac]["intf"] != "br101": + assertmsg = "JSON output mismatches local: {} remote: {}".format( + local_output_vni_json[0]["vtepIp"], + remote_output_json[vni]["macs"][mac]["remoteVtep"], + ) + assert ( + remote_output_json[vni]["macs"][mac]["remoteVtep"] + == local_output_vni_json[0]["vtepIp"] + ), assertmsg + + +def test_learning_pe1(): + "test MAC learning on PE1" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + host1 = tgen.gears["host1"] + pe1 = tgen.gears["PE1"] + mac_learn_test(host1, pe1) + + +def test_learning_pe2(): + "test MAC learning on PE2" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + host2 = tgen.gears["host2"] + pe2 = tgen.gears["PE2"] + mac_learn_test(host2, pe2) + + +def test_local_remote_mac_pe1(): + " Test MAC transfer PE1 local and PE2 remote" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + pe1 = tgen.gears["PE1"] + pe2 = tgen.gears["PE2"] + mac_test_local_remote(pe1, pe2) + + +def test_local_remote_mac_pe2(): + " Test MAC transfer PE2 local and PE1 remote" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + pe1 = tgen.gears["PE1"] + pe2 = tgen.gears["PE2"] + mac_test_local_remote(pe2, pe1) + + # Memory leak test template + + +def ip_learn_test(tgen, host, local, remote, ip_addr): + "check the host IP gets learned by the VNI" + host_output = host.vtysh_cmd("show interface {}-eth0".format(host.name)) + int_lines = host_output.splitlines() + mac_line = int_lines[7].split(": ") + mac = mac_line[1] + print(host_output) + + # check we have a local association between the MAC and IP + local_output = local.vtysh_cmd("show evpn mac vni 101 mac {} json".format(mac)) + print(local_output) + local_output_json = json.loads(local_output) + mac_type = local_output_json[mac]["type"] + assertmsg = "Failed to learn local IP address on host {}".format(host.name) + assert local_output_json[mac]["neighbors"] != "none", assertmsg + learned_ip = local_output_json[mac]["neighbors"]["active"][0] + + assertmsg = "local learned mac wrong type: {} ".format(mac_type) + assert mac_type == "local", assertmsg + + assertmsg = "learned address mismatch with configured address host: {} learned: {}".format( + ip_addr, learned_ip + ) + assert ip_addr == learned_ip, assertmsg + + # now lets check the remote + count = 0 + converged = False + while count < 30: + remote_output = remote.vtysh_cmd( + "show evpn mac vni 101 mac {} json".format(mac) + ) + print(remote_output) + remote_output_json = json.loads(remote_output) + type = remote_output_json[mac]["type"] + if not remote_output_json[mac]["neighbors"] == "none": + # due to a kernel quirk, learned IPs can be inactive + if ( + remote_output_json[mac]["neighbors"]["active"] + or remote_output_json[mac]["neighbors"]["inactive"] + ): + converged = True + break + count += 1 + sleep(1) + + print("tries: {}".format(count)) + assertmsg = "{} remote learned mac no address: {} ".format(host.name, mac) + # some debug for this failure + if not converged == True: + log_output = remote.run("cat zebra.log") + print(log_output) + + assert converged == True, assertmsg + if remote_output_json[mac]["neighbors"]["active"]: + learned_ip = remote_output_json[mac]["neighbors"]["active"][0] + else: + learned_ip = remote_output_json[mac]["neighbors"]["inactive"][0] + assertmsg = "remote learned mac wrong type: {} ".format(type) + assert type == "remote", assertmsg + + assertmsg = "remote learned address mismatch with configured address host: {} learned: {}".format( + ip_addr, learned_ip + ) + assert ip_addr == learned_ip, assertmsg + + +def test_ip_pe1_learn(): + "run the IP learn test for PE1" + + tgen = get_topogen() + host1 = tgen.gears["host1"] + pe1 = tgen.gears["PE1"] + pe2 = tgen.gears["PE2"] + pe2.vtysh_cmd("debug zebra vxlan") + pe2.vtysh_cmd("debug zebra kernel") + # lets populate that arp cache + host1.run("ping -c1 10.10.1.1") + ip_learn_test(tgen, host1, pe1, pe2, "10.10.1.55") + # tgen.mininet_cli() + + +def test_ip_pe2_learn(): + "run the IP learn test for PE2" + + tgen = get_topogen() + host2 = tgen.gears["host2"] + pe1 = tgen.gears["PE1"] + pe2 = tgen.gears["PE2"] + pe1.vtysh_cmd("debug zebra vxlan") + pe1.vtysh_cmd("debug zebra kernel") + # lets populate that arp cache + host2.run("ping -c1 10.10.1.3") + ip_learn_test(tgen, host2, pe2, pe1, "10.10.1.56") + # tgen.mininet_cli() + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp-path-attributes-topo1/__init__.py b/tests/topotests/bgp-path-attributes-topo1/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bgp-path-attributes-topo1/bgp_path_attributes.json b/tests/topotests/bgp-path-attributes-topo1/bgp_path_attributes.json new file mode 100644 index 0000000000..de2bffa33d --- /dev/null +++ b/tests/topotests/bgp-path-attributes-topo1/bgp_path_attributes.json @@ -0,0 +1,363 @@ +{ + "ipv4base":"10.0.0.0", + "ipv4mask":30, + "ipv6base":"fd00::", + "ipv6mask":64, + "link_ip_start":{"ipv4":"10.0.0.0", "v4mask":30, "ipv6":"fd00::", "v6mask":64}, + "lo_prefix":{"ipv4":"1.0.", "v4mask":32, "ipv6":"2001:DB8:F::", "v6mask":128}, + "routers":{ + "r1":{ + "links":{ + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2":{"ipv4":"auto", "ipv6":"auto"}, + "r3":{"ipv4":"auto", "ipv6":"auto"} + }, + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + } + ] + }, + "bgp":{ + "local_as":"555", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r3": { + "dest_link": { + "r1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r3": { + "dest_link": { + "r1": {} + } + } + } + } + } + } + } + }, + "r2":{ + "links":{ + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1": {"ipv4": "auto", "ipv6": "auto"}, + "r3": {"ipv4": "auto", "ipv6": "auto"}, + "r4-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r4-link2": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp":{ + "local_as":"555", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r3": { + "dest_link": { + "r2": {} + } + }, + "r4": { + "dest_link": { + "r2-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r3": { + "dest_link": { + "r2": {} + } + }, + "r4": { + "dest_link": { + "r2-link1": {} + } + } + } + } + } + } + } + }, + "r3":{ + "links":{ + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1":{"ipv4":"auto", "ipv6":"auto"}, + "r2":{"ipv4":"auto", "ipv6":"auto"}, + "r5":{"ipv4":"auto", "ipv6":"auto"} + }, + "bgp":{ + "local_as":"555", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {} + } + }, + "r2": { + "dest_link": { + "r3": {} + } + }, + "r5": { + "dest_link": { + "r3": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {} + } + }, + "r2": { + "dest_link": { + "r3": {} + } + }, + "r5": { + "dest_link": { + "r3": {} + } + } + } + } + } + } + } + }, + "r4":{ + "links":{ + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r2-link2": {"ipv4": "auto", "ipv6": "auto"}, + "r6": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "666", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4-link1": {} + } + }, + "r6": { + "dest_link": { + "r4": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4-link1": {} + } + }, + "r6": { + "dest_link": { + "r4": {} + } + } + } + } + } + } + } + }, + "r5":{ + "links":{ + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r3": {"ipv4": "auto", "ipv6": "auto"}, + "r7": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp":{ + "local_as":"666", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r5": {} + } + }, + "r7": { + "dest_link": { + "r5": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r5": {} + } + }, + "r7": { + "dest_link": { + "r5": {} + } + } + } + } + } + } + } + }, + "r6":{ + "links":{ + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r4": {"ipv4": "auto", "ipv6": "auto"}, + "r7": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp":{ + "local_as":"777", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r6": {} + } + }, + "r7": { + "dest_link": { + "r6": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r6": {} + } + }, + "r7": { + "dest_link": { + "r6": {} + } + } + } + } + } + } + } + }, + "r7":{ + "links":{ + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r5": {"ipv4": "auto", "ipv6": "auto"}, + "r6": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp":{ + "local_as":"888", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r5": { + "dest_link": { + "r7": {} + } + }, + "r6": { + "dest_link": { + "r7": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r5": { + "dest_link": { + "r7": {} + } + }, + "r6": { + "dest_link": { + "r7": {} + } + } + } + } + } + } + } + } + } +} 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 new file mode 100755 index 0000000000..607b036c6a --- /dev/null +++ b/tests/topotests/bgp-path-attributes-topo1/test_bgp_path_attributes.py @@ -0,0 +1,1567 @@ +#!/usr/bin/env python + +# +# Modified work Copyright (c) 2019 by VMware, Inc. ("VMware") +# Original work Copyright (c) 2018 by Network Device Education +# Foundation, Inc. ("NetDEF") +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +Following tests are covered to test AS-Path functionality: + +Setup module: +- Create topology (setup module) +- Bring up topology +- Verify BGP convergence + +Test cases: +1. Test next_hop attribute and verify best path is installed as per + reachable next_hop +2. Test aspath attribute and verify best path is installed as per + shortest AS-Path +3. Test localpref attribute and verify best path is installed as per + shortest local-preference +4. Test weight attribute and and verify best path is installed as per + highest weight +5. Test origin attribute and verify best path is installed as per + IGP>EGP>INCOMPLETE rule +6. Test med attribute and verify best path is installed as per lowest + med value +7. Test admin distance and verify best path is installed as per lowest + admin distance + +Teardown module: +- Bring down the topology +- stop routers + +""" + +import os +import sys +import pdb +import json +import time +import inspect +from time import sleep +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from mininet.topo import Topo +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen + +# Required to instantiate the topology builder class. +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + reset_config_on_routers, + verify_rib, + create_static_routes, + create_prefix_lists, + verify_prefix_lists, + create_route_maps, + check_address_types, +) +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + create_router_bgp, + clear_bgp_and_verify, + verify_best_path_as_per_bgp_attribute, + verify_best_path_as_per_admin_distance, + modify_as_number, + verify_as_numbers, +) +from lib.topojson import build_topo_from_json, build_config_from_json + +# Reading the data from JSON File for topology creation +jsonFile = "{}/bgp_path_attributes.json".format(CWD) + +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +# Address read from env variables +ADDR_TYPES = check_address_types() + +#### +class CreateTopo(Topo): + """ + Test CreateTopo - topology 1 + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # Building topology and configuration from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + global ADDR_TYPES + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: %s", testsuite_run_time) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(CreateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Checking BGP convergence + result = verify_bgp_convergence(tgen, topo) + assert result is True, "setup_module :Failed \n Error:" " {}".format(result) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """ + Teardown the pytest environment + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info("Testsuite end time: %s", time.asctime(time.localtime(time.time()))) + logger.info("=" * 40) + + +##################################################### +## +## Testcases +## +##################################################### + + +def test_next_hop_attribute(request): + """ + Verifying route are not getting installed in, as next_hop is + unreachable, Making next hop reachable using next_hop_self + command and verifying routes are installed. + """ + + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + # Api call to advertise networks + input_dict = { + "r7": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + {"network": "200.50.2.0/32"}, + {"network": "200.60.2.0/32"}, + ] + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + {"network": "200:50:2::/128"}, + {"network": "200:60:2::/128"}, + ] + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r1" + protocol = "bgp" + # Verification should fail as nexthop-self is not enabled + for addr_type in ADDR_TYPES: + result = verify_rib( + tgen, addr_type, dut, input_dict, protocol=protocol, expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n Error: " + "{} routes are not present in RIB".format(addr_type, tc_name) + ) + + # Configure next-hop-self to bgp neighbor + input_dict_1 = { + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r2": {"next_hop_self": True}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r2": {"next_hop_self": True}}} + } + } + }, + } + } + }, + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r3": {"next_hop_self": True}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r3": {"next_hop_self": True}}} + } + } + }, + } + } + }, + } + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r1" + protocol = "bgp" + for addr_type in ADDR_TYPES: + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_aspath_attribute(request): + " Verifying AS_PATH attribute functionality" + + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + # Api call to advertise networks + input_dict = { + "r7": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + {"network": "200.50.2.0/32"}, + {"network": "200.60.2.0/32"}, + ] + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + {"network": "200:50:2::/128"}, + {"network": "200:60:2::/128"}, + ] + } + }, + } + } + }, + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r2": {"next_hop_self": True}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r2": {"next_hop_self": True}}} + } + } + }, + } + } + }, + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r3": {"next_hop_self": True}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r3": {"next_hop_self": True}}} + } + } + }, + } + } + }, + } + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying best path + dut = "r1" + attribute = "path" + for addr_type in ADDR_TYPES: + result = verify_best_path_as_per_bgp_attribute( + tgen, addr_type, dut, {"r7": input_dict["r7"]}, attribute + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Modify AS-Path and verify best path is changed + # Create Prefix list + + input_dict_2 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_ls_1_ipv4": [ + { + "seqid": 10, + "network": "200.0.0.0/8", + "le": "32", + "action": "permit", + } + ] + }, + "ipv6": { + "pf_ls_1_ipv6": [ + { + "seqid": 10, + "network": "200::/8", + "le": "128", + "action": "permit", + } + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Create route map + input_dict_3 = { + "r3": { + "route_maps": { + "RMAP_AS_PATH": [ + { + "action": "permit", + "match": {"ipv4": {"prefix_lists": "pf_ls_1_ipv4"}}, + "set": {"path": {"as_num": "111 222", "as_action": "prepend"}}, + }, + { + "action": "permit", + "match": {"ipv6": {"prefix_lists": "pf_ls_1_ipv6"}}, + "set": {"path": {"as_num": "111 222", "as_action": "prepend"}}, + }, + ] + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Configure neighbor for route map + input_dict_4 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r5": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "RMAP_AS_PATH", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r5": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "RMAP_AS_PATH", + "direction": "in", + } + ] + } + } + } + } + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying best path + dut = "r1" + attribute = "path" + for addr_type in ADDR_TYPES: + result = verify_best_path_as_per_bgp_attribute( + tgen, addr_type, dut, {"r7": input_dict["r7"]}, attribute + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_localpref_attribute(request): + " Verifying LOCAL PREFERENCE attribute functionality" + + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + # Api call to advertise networks + input_dict = { + "r7": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + {"network": "200.50.2.0/32"}, + {"network": "200.60.2.0/32"}, + ] + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + {"network": "200:50:2::/128"}, + {"network": "200:60:2::/128"}, + ] + } + }, + } + } + }, + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r2": {"next_hop_self": True}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r2": {"next_hop_self": True}}} + } + } + }, + } + } + }, + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r3": {"next_hop_self": True}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r3": {"next_hop_self": True}}} + } + } + }, + } + } + }, + } + + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Create Prefix list + input_dict_2 = { + "r2": { + "prefix_lists": { + "ipv4": { + "pf_ls_1_ipv4": [ + { + "seqid": 10, + "network": "200.0.0.0/8", + "le": "32", + "action": "permit", + } + ] + }, + "ipv6": { + "pf_ls_1_ipv6": [ + { + "seqid": 10, + "network": "200::/8", + "le": "128", + "action": "permit", + } + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Create route map + input_dict_3 = { + "r2": { + "route_maps": { + "RMAP_LOCAL_PREF": [ + { + "action": "permit", + "seq_id": "10", + "match": {"ipv4": {"prefix_lists": "pf_ls_1_ipv4"}}, + "set": {"locPrf": 1111}, + }, + { + "action": "permit", + "seq_id": "20", + "match": {"ipv6": {"prefix_lists": "pf_ls_1_ipv6"}}, + "set": {"locPrf": 1111}, + }, + ] + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Configure neighbor for route map + input_dict_4 = { + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2-link1": { + "route_maps": [ + { + "name": "RMAP_LOCAL_PREF", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2-link1": { + "route_maps": [ + { + "name": "RMAP_LOCAL_PREF", + "direction": "in", + } + ] + } + } + } + } + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying best path + dut = "r1" + attribute = "locPrf" + for addr_type in ADDR_TYPES: + result = verify_best_path_as_per_bgp_attribute( + tgen, addr_type, dut, {"r7": input_dict["r7"]}, attribute + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Modify route map + input_dict_3 = { + "r2": { + "route_maps": { + "RMAP_LOCAL_PREF": [ + { + "action": "permit", + "seq_id": "10", + "match": {"ipv4": {"prefix_lists": "pf_ls_1_ipv4"}}, + "set": {"locPrf": 50}, + }, + { + "action": "permit", + "seq_id": "20", + "match": {"ipv6": {"prefix_lists": "pf_ls_1_ipv6"}}, + "set": {"locPrf": 50}, + }, + ] + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying best path + dut = "r1" + attribute = "locPrf" + for addr_type in ADDR_TYPES: + result = verify_best_path_as_per_bgp_attribute( + tgen, addr_type, dut, {"r7": input_dict["r7"]}, attribute + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_weight_attribute(request): + """ + Test configure/modify weight attribute and + verify best path is installed as per highest weight + """ + + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + # Api call to advertise networks + input_dict = { + "r7": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + {"network": "200.50.2.0/32"}, + {"network": "200.60.2.0/32"}, + ] + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + {"network": "200:50:2::/128"}, + {"network": "200:60:2::/128"}, + ] + } + }, + } + } + }, + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r2": {"next_hop_self": True}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r2": {"next_hop_self": True}}} + } + } + }, + } + } + }, + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r3": {"next_hop_self": True}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r3": {"next_hop_self": True}}} + } + } + }, + } + } + }, + } + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Create Prefix list + input_dict_2 = { + "r1": { + "prefix_lists": { + "ipv4": { + "pf_ls_1_ipv4": [ + { + "seqid": 10, + "network": "200.0.0.0/8", + "le": "32", + "action": "permit", + } + ] + }, + "ipv6": { + "pf_ls_1_ipv6": [ + { + "seqid": 10, + "network": "200::/8", + "le": "128", + "action": "permit", + } + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Create route map + input_dict_3 = { + "r1": { + "route_maps": { + "RMAP_WEIGHT": [ + { + "action": "permit", + "seq_id": "5", + "match": {"ipv4": {"prefix_lists": "pf_ls_1_ipv4"}}, + "set": {"weight": 500}, + }, + { + "action": "permit", + "seq_id": "10", + "match": {"ipv6": {"prefix_lists": "pf_ls_1_ipv6"}}, + "set": {"weight": 500}, + }, + ] + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Configure neighbor for route map + input_dict_4 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": { + "route_maps": [ + { + "name": "RMAP_WEIGHT", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": { + "route_maps": [ + { + "name": "RMAP_WEIGHT", + "direction": "in", + } + ] + } + } + } + } + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying best path + dut = "r1" + attribute = "weight" + for addr_type in ADDR_TYPES: + result = verify_best_path_as_per_bgp_attribute( + tgen, addr_type, dut, {"r7": input_dict["r7"]}, attribute + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Modify route map + input_dict_3 = { + "r1": { + "route_maps": { + "RMAP_WEIGHT": [ + { + "action": "permit", + "seq_id": "5", + "match": {"ipv4": {"prefix_lists": "pf_ls_1_ipv4"}}, + "set": {"weight": 1000}, + }, + { + "action": "permit", + "seq_id": "10", + "match": {"ipv6": {"prefix_lists": "pf_ls_1_ipv6"}}, + "set": {"weight": 1000}, + }, + ] + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying best path + dut = "r1" + attribute = "weight" + for addr_type in ADDR_TYPES: + result = verify_best_path_as_per_bgp_attribute( + tgen, addr_type, dut, {"r7": input_dict["r7"]}, attribute + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_origin_attribute(request): + """ + Test origin attribute and verify best path is + installed as per IGP>EGP>INCOMPLETE rule + """ + + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + # Api call to advertise networks + input_dict = { + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + {"network": "200.50.2.0/32"}, + {"network": "200.60.2.0/32"}, + ] + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + {"network": "200:50:2::/128"}, + {"network": "200:60:2::/128"}, + ] + } + }, + } + } + }, + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r2": {"next_hop_self": True}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r2": {"next_hop_self": True}}} + } + } + }, + } + } + }, + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r3": {"next_hop_self": True}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r3": {"next_hop_self": True}}} + } + } + }, + } + } + }, + "r5": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + } + } + }, + } + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Api call to create static routes + input_dict_3 = { + "r5": { + "static_routes": [ + {"network": "200.50.2.0/32", "next_hop": "Null0"}, + {"network": "200.60.2.0/32", "next_hop": "Null0"}, + {"network": "200:50:2::/128", "next_hop": "Null0"}, + {"network": "200:60:2::/128", "next_hop": "Null0"}, + ] + } + } + result = create_static_routes(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying best path + dut = "r1" + attribute = "origin" + for addr_type in ADDR_TYPES: + result = verify_best_path_as_per_bgp_attribute( + tgen, addr_type, dut, {"r4": input_dict["r4"]}, attribute + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_med_attribute(request): + """ + Test configure/modify MED attribute and verify best path + is installed as per lowest med value + """ + + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + # Api call to advertise networks + input_dict = { + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + {"network": "200.50.2.0/32"}, + {"network": "200.60.2.0/32"}, + ] + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + {"network": "200:50:2::/128"}, + {"network": "200:60:2::/128"}, + ] + } + }, + } + } + }, + "r5": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + {"network": "200.50.2.0/32"}, + {"network": "200.60.2.0/32"}, + ] + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + {"network": "200:50:2::/128"}, + {"network": "200:60:2::/128"}, + ] + } + }, + } + } + }, + } + + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Create Prefix list + input_dict_2 = { + "r2": { + "prefix_lists": { + "ipv4": { + "pf_ls_r2_ipv4": [ + { + "seqid": 10, + "network": "200.0.0.0/8", + "le": "32", + "action": "permit", + } + ] + }, + "ipv6": { + "pf_ls_r2_ipv6": [ + { + "seqid": 20, + "network": "200::/8", + "le": "128", + "action": "permit", + } + ] + }, + } + }, + "r3": { + "prefix_lists": { + "ipv4": { + "pf_ls_r3_ipv4": [ + { + "seqid": 10, + "network": "200.0.0.0/8", + "le": "32", + "action": "permit", + } + ] + }, + "ipv6": { + "pf_ls_r3_ipv6": [ + { + "seqid": 20, + "network": "200::/8", + "le": "128", + "action": "permit", + } + ] + }, + } + }, + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Create route map + input_dict_3 = { + "r2": { + "route_maps": { + "RMAP_MED_R2": [ + { + "action": "permit", + "seq_id": "10", + "match": {"ipv4": {"prefix_lists": "pf_ls_r2_ipv4"}}, + "set": {"metric": 100}, + }, + { + "action": "permit", + "seq_id": "20", + "match": {"ipv6": {"prefix_lists": "pf_ls_r2_ipv6"}}, + "set": {"metric": 100}, + }, + ] + } + }, + "r3": { + "route_maps": { + "RMAP_MED_R3": [ + { + "action": "permit", + "seq_id": "10", + "match": {"ipv4": {"prefix_lists": "pf_ls_r3_ipv4"}}, + "set": {"metric": 10}, + }, + { + "action": "permit", + "seq_id": "20", + "match": {"ipv6": {"prefix_lists": "pf_ls_r3_ipv6"}}, + "set": {"metric": 10}, + }, + ] + } + }, + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Configure neighbor for route map + input_dict_4 = { + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2-link1": { + "route_maps": [ + { + "name": "RMAP_MED_R2", + "direction": "in", + } + ] + } + } + }, + "r1": {"dest_link": {"r2": {"next_hop_self": True}}}, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2-link1": { + "route_maps": [ + { + "name": "RMAP_MED_R2", + "direction": "in", + } + ] + } + } + }, + "r1": {"dest_link": {"r2": {"next_hop_self": True}}}, + } + } + }, + } + } + }, + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r3": {"next_hop_self": True}}}, + "r5": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "RMAP_MED_R3", + "direction": "in", + } + ] + } + } + }, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r3": {"next_hop_self": True}}}, + "r5": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "RMAP_MED_R3", + "direction": "in", + } + ] + } + } + }, + } + } + }, + } + } + }, + } + + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying best path + dut = "r1" + attribute = "metric" + for addr_type in ADDR_TYPES: + result = verify_best_path_as_per_bgp_attribute( + tgen, addr_type, dut, input_dict, attribute + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Modify route-map to set med value + input_dict_3 = { + "r3": { + "route_maps": { + "RMAP_MED_R3": [ + { + "action": "permit", + "seq_id": "10", + "match": {"ipv4": {"prefix_lists": "pf_ls_r3_ipv4"}}, + "set": {"metric": 200}, + }, + { + "action": "permit", + "seq_id": "20", + "match": {"ipv6": {"prefix_lists": "pf_ls_r3_ipv6"}}, + "set": {"metric": 200}, + }, + ] + } + } + } + + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying best path + dut = "r1" + attribute = "metric" + for addr_type in ADDR_TYPES: + result = verify_best_path_as_per_bgp_attribute( + tgen, addr_type, dut, input_dict, attribute + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + # Uncomment next line for debugging + # tgen.mininet_cli() + + +def test_admin_distance(request): + " Verifying admin distance functionality" + + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + # Api call to create static routes + input_dict = { + "r2": { + "static_routes": [ + { + "network": "200.50.2.0/32", + "admin_distance": 80, + "next_hop": "10.0.0.14", + }, + { + "network": "200.50.2.0/32", + "admin_distance": 60, + "next_hop": "10.0.0.18", + }, + { + "network": "200:50:2::/128", + "admin_distance": 80, + "next_hop": "fd00::1", + }, + { + "network": "200:50:2::/128", + "admin_distance": 60, + "next_hop": "fd00::1", + }, + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Api call to redistribute static routes + input_dict_2 = { + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + } + } + } + } + result = create_router_bgp(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying best path + dut = "r1" + attribute = "admin_distance" + + input_dict = { + "ipv4": { + "r2": { + "static_routes": [ + { + "network": "200.50.2.0/32", + "admin_distance": 80, + "next_hop": "10.0.0.14", + }, + { + "network": "200.50.2.0/32", + "admin_distance": 60, + "next_hop": "10.0.0.18", + }, + ] + } + }, + "ipv6": { + "r2": { + "static_routes": [ + { + "network": "200:50:2::/128", + "admin_distance": 80, + "next_hop": "fd00::1", + }, + { + "network": "200:50:2::/128", + "admin_distance": 60, + "next_hop": "fd00::1", + }, + ] + } + }, + } + + for addr_type in ADDR_TYPES: + result = verify_best_path_as_per_admin_distance( + tgen, addr_type, dut, input_dict[addr_type], attribute + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp-prefix-list-topo1/__init__.py b/tests/topotests/bgp-prefix-list-topo1/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bgp-prefix-list-topo1/prefix_lists.json b/tests/topotests/bgp-prefix-list-topo1/prefix_lists.json new file mode 100644 index 0000000000..3bb07ad994 --- /dev/null +++ b/tests/topotests/bgp-prefix-list-topo1/prefix_lists.json @@ -0,0 +1,123 @@ +{ + "address_types": ["ipv4"], + "ipv4base":"10.0.0.0", + "ipv4mask":30, + "ipv6base":"fd00::", + "ipv6mask":64, + "link_ip_start":{"ipv4":"10.0.0.0", "v4mask":30, "ipv6":"fd00::", "v6mask":64}, + "lo_prefix":{"ipv4":"1.0.", "v4mask":32, "ipv6":"2001:DB8:F::", "v6mask":128}, + "routers":{ + "r1":{ + "links":{ + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback", "add_static_route":"yes"}, + "r2":{"ipv4":"auto", "ipv6":"auto"}, + "r3":{"ipv4":"auto", "ipv6":"auto"} + }, + "bgp":{ + "local_as":"100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {} + } + }, + "r3": { + "dest_link": { + "r1": {} + } + } + } + } + } + } + } + }, + "r2":{ + "links":{ + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback", "add_static_route":"yes"}, + "r1":{"ipv4":"auto", "ipv6":"auto"}, + "r3":{"ipv4":"auto", "ipv6":"auto"} + }, + "bgp":{ + "local_as":"100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r3": { + "dest_link": { + "r2": {} + } + } + } + } + } + } + } + }, + "r3":{ + "links":{ + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback", "add_static_route":"yes"}, + "r1":{"ipv4":"auto", "ipv6":"auto"}, + "r2":{"ipv4":"auto", "ipv6":"auto"}, + "r4":{"ipv4":"auto", "ipv6":"auto"} + }, + "bgp":{ + "local_as":"100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {} + } + }, + "r2": { + "dest_link": { + "r3": {} + } + }, + "r4": { + "dest_link": { + "r3": {} + } + } + } + } + } + } + } + }, + "r4":{ + "links":{ + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback", "add_static_route":"yes"}, + "r3":{"ipv4":"auto", "ipv6":"auto"} + }, + "bgp":{ + "local_as":"200", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": {} + } + } + } + } + } + } + } + } + } +} diff --git a/tests/topotests/bgp-prefix-list-topo1/test_prefix_lists.py b/tests/topotests/bgp-prefix-list-topo1/test_prefix_lists.py new file mode 100755 index 0000000000..22952f645c --- /dev/null +++ b/tests/topotests/bgp-prefix-list-topo1/test_prefix_lists.py @@ -0,0 +1,1355 @@ +#!/usr/bin/python + +# +# Copyright (c) 2019 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +Following tests are covered to test prefix-list functionality: + +Test steps +- Create topology (setup module) + Creating 4 routers topology, r1, r2, r3 are in IBGP and + r3, r4 are in EBGP +- Bring up topology +- Verify for bgp to converge + +IP prefix-list tests +- Test ip prefix-lists IN permit +- Test ip prefix-lists OUT permit +- Test ip prefix-lists IN deny and permit any +- Test delete ip prefix-lists +- Test ip prefix-lists OUT deny and permit any +- Test modify ip prefix-lists IN permit to deny +- Test modify ip prefix-lists IN deny to permit +- Test modify ip prefix-lists OUT permit to deny +- Test modify prefix-lists OUT deny to permit +- Test ip prefix-lists implicit deny +""" + +import sys +import json +import time +import os +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from mininet.topo import Topo +from lib.topogen import Topogen, get_topogen + +# Import topoJson from lib, to create topology and initial configuration +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + reset_config_on_routers, + verify_rib, + create_static_routes, + create_prefix_lists, + verify_prefix_lists, +) +from lib.topolog import logger +from lib.bgp import verify_bgp_convergence, create_router_bgp, clear_bgp_and_verify +from lib.topojson import build_topo_from_json, build_config_from_json + +# Reading the data from JSON File for topology creation +jsonFile = "{}/prefix_lists.json".format(CWD) + +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +# Global variables +bgp_convergence = False + + +class BGPPrefixListTopo(Topo): + """ + Test BGPPrefixListTopo - topology 1 + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(BGPPrefixListTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Checking BGP convergence + global BGP_CONVERGENCE + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Api call verify whether BGP is converged + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error:" " {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """ + Teardown the pytest environment + + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# +# Tests starting +# +##################################################### + + +def test_ip_prefix_lists_in_permit(request): + """ + Create ip prefix list and test permit prefixes IN direction + """ + + tgen = get_topogen() + if BGP_CONVERGENCE is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Create Static routes + input_dict = { + "r1": { + "static_routes": [ + {"network": "20.0.20.1/32", "no_of_ip": 1, "next_hop": "10.0.0.2"} + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Create ip prefix list + input_dict_2 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1": [{"seqid": 10, "network": "any", "action": "permit"}] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Configure bgp neighbor with prefix list + input_dict_3 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + } + } + } + }, + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "prefix_lists": [ + {"name": "pf_list_1", "direction": "in"} + ] + } + } + } + } + } + } + } + } + }, + } + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_ip_prefix_lists_out_permit(request): + """ + Create ip prefix list and test permit prefixes out direction + """ + + tgen = get_topogen() + if BGP_CONVERGENCE is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + # Create Static routes + input_dict = { + "r1": { + "static_routes": [ + {"network": "10.0.20.1/32", "no_of_ip": 1, "next_hop": "10.0.0.2"} + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Create Static routes + input_dict_1 = { + "r1": { + "static_routes": [ + {"network": "20.0.20.1/32", "no_of_ip": 1, "next_hop": "10.0.0.2"} + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + input_dict_5 = { + "r3": { + "static_routes": [ + {"network": "10.0.0.2/30", "no_of_ip": 1, "next_hop": "10.0.0.9"} + ] + } + } + result = create_static_routes(tgen, input_dict_5) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Api call to redistribute static routes + + # Create ip prefix list + input_dict_2 = { + "r1": { + "prefix_lists": { + "ipv4": { + "pf_list_1": [ + {"seqid": 10, "network": "20.0.20.1/32", "action": "permit"} + ] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Configure prefix list to bgp neighbor + # Configure bgp neighbor with prefix list + input_dict_3 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1": { + "prefix_lists": [ + { + "name": "pf_list_1", + "direction": "out", + } + ] + } + } + } + }, + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ], + } + } + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + result = verify_rib(tgen, "ipv4", dut, input_dict_1, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + result = verify_rib( + tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed \n Error: Routes still" " present in RIB".format(tc_name) + write_test_footer(tc_name) + + +def test_ip_prefix_lists_in_deny_and_permit_any(request): + """ + Create ip prefix list and test permit/deny prefixes IN direction + """ + + tgen = get_topogen() + if BGP_CONVERGENCE is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + # Create Static Routes + input_dict = { + "r1": { + "static_routes": [ + {"network": "10.0.20.1/32", "no_of_ip": 1, "next_hop": "10.0.0.2"} + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Api call to redistribute static routes + # Create ip prefix list + input_dict_2 = { + "r1": { + "prefix_lists": { + "ipv4": { + "pf_list_1": [ + {"seqid": "10", "network": "10.0.20.1/32", "action": "deny"}, + {"seqid": "11", "network": "any", "action": "permit"}, + ] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Configure bgp neighbor with prefix list + input_dict_3 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + } + } + } + }, + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "prefix_lists": [ + {"name": "pf_list_1", "direction": "in"} + ] + } + } + } + } + } + } + } + } + }, + } + # Configure prefix list to bgp neighbor + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + result = verify_rib( + tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed \n Error: Routes still" " present in RIB".format(tc_name) + + write_test_footer(tc_name) + + +def test_delete_prefix_lists(request): + """ + Delete ip prefix list + """ + + tgen = get_topogen() + if BGP_CONVERGENCE is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + # Create ip prefix list + input_dict_2 = { + "r1": { + "prefix_lists": { + "ipv4": { + "pf_list_1": [ + {"seqid": "10", "network": "10.0.20.1/32", "action": "deny"} + ] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + result = verify_prefix_lists(tgen, input_dict_2) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Delete prefix list + input_dict_2 = { + "r1": { + "prefix_lists": { + "ipv4": { + "pf_list_1": [ + { + "seqid": "10", + "network": "10.0.20.1/32", + "action": "deny", + "delete": True, + } + ] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + result = verify_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_ip_prefix_lists_out_deny_and_permit_any(request): + """ + Create ip prefix list and test deny/permit any prefixes OUT direction + """ + + tgen = get_topogen() + if BGP_CONVERGENCE is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + # Create Static Routes + input_dict = { + "r1": { + "static_routes": [ + {"network": "10.0.20.1/32", "no_of_ip": 9, "next_hop": "10.0.0.2"} + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Create Static Routes + input_dict_1 = { + "r2": { + "static_routes": [ + {"network": "20.0.20.1/32", "no_of_ip": 9, "next_hop": "10.0.0.1"} + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Api call to redistribute static routes + + # Create ip prefix list + input_dict_3 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1": [ + { + "seqid": "10", + "network": "10.0.0.0/8", + "le": "32", + "action": "deny", + }, + {"seqid": "11", "network": "any", "action": "permit"}, + ] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Configure prefix list to bgp neighbor + input_dict_4 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + } + } + } + }, + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + } + } + } + }, + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "prefix_lists": [ + { + "name": "pf_list_1", + "direction": "out", + } + ] + } + } + } + } + } + } + } + } + }, + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r4" + protocol = "bgp" + result = verify_rib(tgen, "ipv4", dut, input_dict_1, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r4" + protocol = "bgp" + result = verify_rib( + tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed \n Error: Routes still" " present in RIB".format(tc_name) + write_test_footer(tc_name) + + +def test_modify_prefix_lists_in_permit_to_deny(request): + """ + Modify ip prefix list and test permit to deny prefixes IN direction + """ + + tgen = get_topogen() + if BGP_CONVERGENCE is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + # Create Static Routes + input_dict = { + "r1": { + "static_routes": [ + {"network": "10.0.20.1/32", "no_of_ip": 9, "next_hop": "10.0.0.2"} + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Api call to redistribute static routes + + # Create ip prefix list + input_dict_2 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1": [ + { + "seqid": "10", + "network": "10.0.0.0/8", + "le": "32", + "action": "permit", + } + ] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Configure prefix list to bgp neighbor + input_dict_3 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + } + } + } + }, + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "prefix_lists": [ + {"name": "pf_list_1", "direction": "in"} + ] + } + } + } + } + } + } + } + } + }, + } + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Modify prefix list + input_dict_1 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1": [ + { + "seqid": "10", + "network": "10.0.0.0/8", + "le": "32", + "action": "deny", + }, + {"seqid": "11", "network": "any", "action": "permit"}, + ] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Api call to clear bgp, so config changes would be reflected + dut = "r3" + result = clear_bgp_and_verify(tgen, topo, dut) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + result = verify_rib( + tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed \n Error: Routes still" " present in RIB".format(tc_name) + + write_test_footer(tc_name) + + +def test_modify_prefix_lists_in_deny_to_permit(request): + """ + Modify ip prefix list and test deny to permit prefixes IN direction + """ + + tgen = get_topogen() + if BGP_CONVERGENCE is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + # Create Static Routes + input_dict = { + "r1": { + "static_routes": [ + {"network": "10.0.20.1/32", "no_of_ip": 9, "next_hop": "10.0.0.2"} + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Api call to redistribute static routes + + # Create ip prefix list + input_dict_1 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1": [ + { + "seqid": "10", + "network": "10.0.0.0/8", + "le": "32", + "action": "deny", + }, + {"seqid": "11", "network": "any", "action": "permit"}, + ] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Configure prefix list to bgp neighbor + input_dict_2 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + } + } + } + }, + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "prefix_lists": [ + {"name": "pf_list_1", "direction": "in"} + ] + } + } + } + } + } + } + } + } + }, + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + result = verify_rib( + tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed \n Error: Routes still" " present in RIB".format(tc_name) + + # Modify ip prefix list + input_dict_1 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1": [ + { + "seqid": "10", + "network": "10.0.0.0/8", + "le": "32", + "action": "permit", + } + ] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Api call to clear bgp, so config changes would be reflected + dut = "r3" + result = clear_bgp_and_verify(tgen, topo, dut) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_modify_prefix_lists_out_permit_to_deny(request): + """ + Modify ip prefix list and test permit to deny prefixes OUT direction + """ + + tgen = get_topogen() + if BGP_CONVERGENCE is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + # Create Static Routes + input_dict = { + "r1": { + "static_routes": [ + {"network": "10.0.20.1/32", "no_of_ip": 9, "next_hop": "10.0.0.2"} + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Api call to redistribute static routes + + # Create ip prefix list + input_dict_1 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1": [ + { + "seqid": "10", + "network": "10.0.0.0/8", + "le": "32", + "action": "permit", + } + ] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Configure prefix list to bgp neighbor + input_dict_2 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + } + } + } + }, + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "prefix_lists": [ + { + "name": "pf_list_1", + "direction": "out", + } + ] + } + } + } + } + } + } + } + } + }, + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r4" + protocol = "bgp" + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Modify ip prefix list + input_dict_1 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1": [ + { + "seqid": "10", + "network": "10.0.0.0/8", + "le": "32", + "action": "deny", + }, + {"seqid": "11", "network": "any", "action": "permit"}, + ] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Api call to clear bgp, so config changes would be reflected + dut = "r3" + result = clear_bgp_and_verify(tgen, topo, dut) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r4" + protocol = "bgp" + result = verify_rib( + tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed \n Error: Routes still" " present in RIB".format(tc_name) + + write_test_footer(tc_name) + + +def test_modify_prefix_lists_out_deny_to_permit(request): + """ + Modify ip prefix list and test deny to permit prefixes OUT direction + """ + + tgen = get_topogen() + if BGP_CONVERGENCE is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + # Create Static Routes + input_dict = { + "r1": { + "static_routes": [ + {"network": "10.0.20.1/32", "no_of_ip": 9, "next_hop": "10.0.0.2"} + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Api call to redistribute static routes + # Create ip prefix list + input_dict_1 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1": [ + { + "seqid": "10", + "network": "10.0.0.0/8", + "le": "32", + "action": "deny", + }, + {"seqid": "11", "network": "any", "action": "permit"}, + ] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Configure prefix list to bgp neighbor + input_dict_2 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + } + } + } + }, + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "prefix_lists": [ + { + "name": "pf_list_1", + "direction": "out", + } + ] + } + } + } + } + } + } + } + } + }, + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r4" + protocol = "bgp" + result = verify_rib( + tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed \n Error: Routes still" " present in RIB".format(tc_name) + + # Modify ip prefix list + input_dict_1 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1": [ + { + "seqid": "10", + "network": "10.0.0.0/8", + "le": "32", + "action": "permit", + } + ] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Api call to clear bgp, so config changes would be reflected + dut = "r3" + result = clear_bgp_and_verify(tgen, topo, dut) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r4" + protocol = "bgp" + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_ip_prefix_lists_implicit_deny(request): + """ + Create ip prefix list and test implicit deny + """ + + tgen = get_topogen() + if BGP_CONVERGENCE is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + # Create Static Routes + input_dict = { + "r1": { + "static_routes": [ + {"network": "10.0.20.1/32", "no_of_ip": 9, "next_hop": "10.0.0.2"} + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Create Static Routes + input_dict_1 = { + "r2": { + "static_routes": [ + {"network": "20.0.20.1/32", "no_of_ip": 9, "next_hop": "10.0.0.1"} + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Api call to redistribute static routes + # Create ip prefix list + input_dict_3 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1": [ + { + "seqid": "10", + "network": "10.0.0.0/8", + "le": "32", + "action": "permit", + } + ] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Configure prefix list to bgp neighbor + input_dict_4 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + } + } + } + }, + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + } + } + } + }, + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "prefix_lists": [ + { + "name": "pf_list_1", + "direction": "out", + } + ] + } + } + } + } + } + } + } + } + }, + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r4" + protocol = "bgp" + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r4" + protocol = "bgp" + result = verify_rib( + tgen, "ipv4", dut, input_dict_1, protocol=protocol, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed \n Error: Routes still" " present in RIB".format(tc_name) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp-route-map/bgp_route_map_topo1.json b/tests/topotests/bgp-route-map/bgp_route_map_topo1.json new file mode 100644 index 0000000000..e89263961d --- /dev/null +++ b/tests/topotests/bgp-route-map/bgp_route_map_topo1.json @@ -0,0 +1,187 @@ +{ + "address_types": ["ipv4","ipv6"], + "ipv4base":"10.0.0.0", + "ipv4mask":30, + "ipv6base":"fd00::", + "ipv6mask":64, + "link_ip_start":{"ipv4":"10.0.0.0", "v4mask":30, "ipv6":"fd00::", "v6mask":64}, + "lo_prefix":{"ipv4":"1.0.", "v4mask":32, "ipv6":"2001:DB8:F::", "v6mask":128}, + "routers":{ + "r1":{ + "links":{ + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2":{"ipv4":"auto", "ipv6":"auto"}, + "r3":{"ipv4":"auto", "ipv6":"auto"} + }, + "bgp":{ + "local_as":"100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {} + } + }, + "r3": { + "dest_link": { + "r1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {} + } + }, + "r3": { + "dest_link": { + "r1": {} + } + } + } + } + } + } + } + }, + "r2":{ + "links":{ + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1":{"ipv4":"auto", "ipv6":"auto"}, + "r3":{"ipv4":"auto", "ipv6":"auto"} + }, + "bgp":{ + "local_as":"100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r3": { + "dest_link": { + "r2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r3": { + "dest_link": { + "r2": {} + } + } + } + } + } + } + } + }, + "r3":{ + "links":{ + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1":{"ipv4":"auto", "ipv6":"auto"}, + "r2":{"ipv4":"auto", "ipv6":"auto"}, + "r4":{"ipv4":"auto", "ipv6":"auto"} + }, + "bgp":{ + "local_as":"100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {} + } + }, + "r2": { + "dest_link": { + "r3": {} + } + }, + "r4": { + "dest_link": { + "r3": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {} + } + }, + "r2": { + "dest_link": { + "r3": {} + } + }, + "r4": { + "dest_link": { + "r3": {} + } + } + } + } + } + } + } + }, + "r4":{ + "links":{ + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r3":{"ipv4":"auto", "ipv6":"auto"} + }, + "bgp":{ + "local_as":"200", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": {} + } + } + } + } + } + } + } + } + } +} diff --git a/tests/topotests/bgp-route-map/bgp_route_map_topo2.json b/tests/topotests/bgp-route-map/bgp_route_map_topo2.json new file mode 100755 index 0000000000..c22a4c3ea7 --- /dev/null +++ b/tests/topotests/bgp-route-map/bgp_route_map_topo2.json @@ -0,0 +1,316 @@ +{ + "address_types": ["ipv4", "ipv6"], + "ipv4base": "10.0.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 30, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:DB8:F::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {} + } + }, + "r3": { + "dest_link": { + "r1": {} + } + } + }, + "redistribute": [{ + "redist_type": "static" + }, + { + "redist_type": "connected" + } + ] + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {} + } + }, + "r3": { + "dest_link": { + "r1": {} + } + } + }, + "redistribute": [{ + "redist_type": "static" + }, + { + "redist_type": "connected" + } + ] + } + } + } + }, + + "static_routes": [{ + "network": "10.0.20.1/32", + "no_of_ip": 2, + "next_hop": "10.0.0.2" + }, + { + "network": "1::1/128", + "no_of_ip": 2, + "next_hop": "fd00::2" + }] + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r3": { + "dest_link": { + "r2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r3": { + "dest_link": { + "r2": {} + } + } + } + } + } + } + } + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r5": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {} + } + }, + "r2": { + "dest_link": { + "r3": {} + } + }, + "r4": { + "dest_link": { + "r3": {} + } + }, + "r5": { + "dest_link": { + "r3": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {} + } + }, + "r2": { + "dest_link": { + "r3": {} + } + }, + "r4": { + "dest_link": { + "r3": {} + } + }, + "r5": { + "dest_link": { + "r3": {} + } + } + } + } + } + } + } + }, + "r4": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "200", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": {} + } + } + } + } + } + } + } + }, + "r5": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r5": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r5": {} + } + } + } + } + } + } + } + } + } +} diff --git a/tests/topotests/bgp-route-map/test_route_map_topo1.py b/tests/topotests/bgp-route-map/test_route_map_topo1.py new file mode 100755 index 0000000000..1aa951edaa --- /dev/null +++ b/tests/topotests/bgp-route-map/test_route_map_topo1.py @@ -0,0 +1,1405 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2019 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +################################# +# TOPOLOGY +################################# +""" + + +-------+ + +------- | R2 | + | +-------+ + | | + +-------+ | + | R1 | | + +-------+ | + | | + | +-------+ +-------+ + +---------- | R3 |----------| R4 | + +-------+ +-------+ + +""" + +################################# +# TEST SUMMARY +################################# +""" +Following tests are covered to test route-map functionality: +TC_34: + Verify if route-maps is applied in both inbound and + outbound direction to same neighbor/interface. +TC_36: + Test permit/deny statements operation in route-maps with a + permutation and combination of permit/deny in prefix-lists +TC_35: + Test multiple sequence numbers in a single route-map for different + match/set clauses. +TC_37: + Test add/remove route-maps with multiple set + clauses and without any match statement.(Set only) +TC_38: + Test add/remove route-maps with multiple match + clauses and without any set statement.(Match only) +""" + +import sys +import json +import time +import pytest +import inspect +import os +from time import sleep + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, get_topogen +from mininet.topo import Topo + +# Required to instantiate the topology builder class. +from lib.topojson import * +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + verify_bgp_community, + verify_rib, + delete_route_maps, + create_bgp_community_lists, + interface_status, + create_route_maps, + create_prefix_lists, + verify_route_maps, + check_address_types, + shutdown_bringup_interface, + verify_prefix_lists, + reset_config_on_routers, +) +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + create_router_bgp, + clear_bgp_and_verify, + verify_bgp_attributes, +) +from lib.topojson import build_topo_from_json, build_config_from_json + + +# Global variables +bgp_convergence = False +BGP_CONVERGENCE = False +ADDR_TYPES = check_address_types() +# Reading the data from JSON File for topology and configuration creation +jsonFile = "{}/bgp_route_map_topo1.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +# Global variables +bgp_convergence = False +NETWORK = {"ipv4": ["11.0.20.1/32", "20.0.20.1/32"], "ipv6": ["1::1/128", "2::1/128"]} +MASK = {"ipv4": "32", "ipv6": "128"} +NEXT_HOP = {"ipv4": "10.0.0.2", "ipv6": "fd00::2"} +ADDR_TYPES = check_address_types() + + +class CreateTopo(Topo): + """ + Test topology builder + + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + """Build function""" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + global ADDR_TYPES + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(CreateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Checking BGP convergence + global bgp_convergence + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Api call verify whether BGP is converged + bgp_convergence = verify_bgp_convergence(tgen, topo) + assert bgp_convergence is True, "setup_module :Failed \n Error:" " {}".format( + bgp_convergence + ) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """ + Teardown the pytest environment + + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +def test_route_map_inbound_outbound_same_neighbor_p0(request): + """ + TC_34: + Verify if route-maps is applied in both inbound and + outbound direction to same neighbor/interface. + """ + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + for adt in ADDR_TYPES: + + # Create Static routes + input_dict = { + "r1": { + "static_routes": [ + { + "network": NETWORK[adt][0], + "no_of_ip": 9, + "next_hop": NEXT_HOP[adt], + } + ] + } + } + + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Api call to redistribute static routes + input_dict_1 = { + "r1": { + "bgp": { + "local_as": 100, + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + }, + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_2 = { + "r4": { + "static_routes": [ + { + "network": NETWORK[adt][1], + "no_of_ip": 9, + "next_hop": NEXT_HOP[adt], + } + ] + } + } + + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Api call to redistribute static routes + input_dict_5 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_5) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_2 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + { + "seqid": 10, + "action": "permit", + "network": NETWORK["ipv4"][0], + } + ], + "pf_list_2_ipv4": [ + { + "seqid": 10, + "action": "permit", + "network": NETWORK["ipv4"][1], + } + ], + }, + "ipv6": { + "pf_list_1_ipv6": [ + { + "seqid": 100, + "action": "permit", + "network": NETWORK["ipv6"][0], + } + ], + "pf_list_2_ipv6": [ + { + "seqid": 100, + "action": "permit", + "network": NETWORK["ipv6"][1], + } + ], + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Create route map + for addr_type in ADDR_TYPES: + input_dict_6 = { + "r3": { + "route_maps": { + "rmap_match_tag_1_{}".format(addr_type): [ + { + "action": "deny", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + } + ], + "rmap_match_tag_2_{}".format(addr_type): [ + { + "action": "permit", + "match": { + addr_type: { + "prefix_lists": "pf_list_2_{}".format(addr_type) + } + }, + } + ], + } + } + } + result = create_route_maps(tgen, input_dict_6) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Configure neighbor for route map + input_dict_7 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_tag_1_ipv4", + "direction": "in", + }, + { + "name": "rmap_match_tag_1_ipv4", + "direction": "out", + }, + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_tag_1_ipv6", + "direction": "in", + }, + { + "name": "rmap_match_tag_1_ipv6", + "direction": "out", + }, + ] + } + } + } + } + } + }, + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_7) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for adt in ADDR_TYPES: + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + input_dict_2 = { + "r4": { + "static_routes": [ + { + "network": [NETWORK[adt][1]], + "no_of_ip": 9, + "next_hop": NEXT_HOP[adt], + } + ] + } + } + + result = verify_rib( + tgen, adt, dut, input_dict_2, protocol=protocol, expected=False + ) + assert result is not True, "Testcase {} : Failed \n" + "routes are not present in rib \n Error: {}".format(tc_name, result) + logger.info("Expected behaviour: {}".format(result)) + + # Verifying RIB routes + dut = "r4" + input_dict = { + "r1": { + "static_routes": [ + { + "network": [NETWORK[adt][0]], + "no_of_ip": 9, + "next_hop": NEXT_HOP[adt], + } + ] + } + } + result = verify_rib( + tgen, adt, dut, input_dict, protocol=protocol, expected=False + ) + assert result is not True, "Testcase {} : Failed \n " + "routes are not present in rib \n Error: {}".format(tc_name, result) + logger.info("Expected behaviour: {}".format(result)) + + write_test_footer(tc_name) + + +@pytest.mark.parametrize( + "prefix_action, rmap_action", + [("permit", "permit"), ("permit", "deny"), ("deny", "permit"), ("deny", "deny")], +) +def test_route_map_with_action_values_combination_of_prefix_action_p0( + request, prefix_action, rmap_action +): + """ + TC_36: + Test permit/deny statements operation in route-maps with a permutation and + combination of permit/deny in prefix-lists + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + for adt in ADDR_TYPES: + # Create Static routes + input_dict = { + "r1": { + "static_routes": [ + { + "network": NETWORK[adt][0], + "no_of_ip": 9, + "next_hop": NEXT_HOP[adt], + } + ] + } + } + + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Api call to redistribute static routes + input_dict_1 = { + "r1": { + "bgp": { + "local_as": 100, + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + }, + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Permit in perfix list and route-map + input_dict_2 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + {"seqid": 10, "network": "any", "action": prefix_action} + ] + }, + "ipv6": { + "pf_list_1_ipv6": [ + {"seqid": 100, "network": "any", "action": prefix_action} + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Create route map + for addr_type in ADDR_TYPES: + input_dict_3 = { + "r3": { + "route_maps": { + "rmap_match_pf_1_{}".format(addr_type): [ + { + "action": rmap_action, + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Configure neighbor for route map + input_dict_7 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_7) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + dut = "r3" + protocol = "bgp" + input_dict_2 = { + "r1": { + "static_routes": [ + { + "network": [NETWORK[adt][0]], + "no_of_ip": 9, + "next_hop": NEXT_HOP[adt], + } + ] + } + } + + # tgen.mininet_cli() + result = verify_rib( + tgen, adt, dut, input_dict_2, protocol=protocol, expected=False + ) + if "deny" in [prefix_action, rmap_action]: + assert result is not True, "Testcase {} : Failed \n " + "Routes are still present \n Error: {}".format(tc_name, result) + logger.info("Expected behaviour: {}".format(result)) + else: + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + +def test_route_map_multiple_seq_different_match_set_clause_p0(request): + """ + TC_35: + Test multiple sequence numbers in a single route-map for different + match/set clauses. + """ + + tgen = get_topogen() + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + for adt in ADDR_TYPES: + # Create Static routes + input_dict = { + "r1": { + "static_routes": [ + { + "network": NETWORK[adt][0], + "no_of_ip": 1, + "next_hop": NEXT_HOP[adt], + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Api call to redistribute static routes + input_dict_1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Create ip prefix list + input_dict_2 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + {"seqid": 10, "network": "any", "action": "permit"} + ] + }, + "ipv6": { + "pf_list_1_ipv6": [ + {"seqid": 100, "network": "any", "action": "permit"} + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Create route map + for addr_type in ADDR_TYPES: + input_dict_3 = { + "r3": { + "route_maps": { + "rmap_match_pf_1_{}".format(addr_type): [ + { + "action": "permit", + "match": { + addr_type: { + "prefix_lists": "pf_list_2_{}".format(addr_type) + } + }, + "set": {"path": {"as_num": 500}}, + }, + { + "action": "permit", + "match": { + addr_type: { + "prefix_lists": "pf_list_2_{}".format(addr_type) + } + }, + "set": {"locPrf": 150,}, + }, + { + "action": "permit", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + "set": {"metric": 50}, + }, + ] + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Configure neighbor for route map + input_dict_4 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv4", + "direction": "in", + } + ] + } + } + }, + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv4", + "direction": "out", + } + ] + } + } + }, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv6", + "direction": "in", + } + ] + } + } + }, + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv6", + "direction": "out", + } + ] + } + } + }, + } + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for adt in ADDR_TYPES: + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + input_dict = { + "r3": {"route_maps": {"rmap_match_pf_list1": [{"set": {"metric": 50,}}],}} + } + + static_routes = [NETWORK[adt][0]] + + time.sleep(2) + result = verify_bgp_attributes( + tgen, adt, dut, static_routes, "rmap_match_pf_list1", input_dict + ) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + dut = "r4" + result = verify_bgp_attributes( + tgen, adt, dut, static_routes, "rmap_match_pf_list1", input_dict + ) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + logger.info("Testcase " + tc_name + " :Passed \n") + + # Uncomment next line for debugging + # tgen.mininet_cli() + + +def test_route_map_set_only_no_match_p0(request): + """ + TC_37: + Test add/remove route-maps with multiple set + clauses and without any match statement.(Set only) + """ + + tgen = get_topogen() + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + for adt in ADDR_TYPES: + # Create Static routes + input_dict = { + "r1": { + "static_routes": [ + { + "network": NETWORK[adt][0], + "no_of_ip": 1, + "next_hop": NEXT_HOP[adt], + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Api call to redistribute static routes + input_dict_1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Create route map + input_dict_3 = { + "r3": { + "route_maps": { + "rmap_match_pf_1": [ + { + "action": "permit", + "set": {"metric": 50, "locPrf": 150, "weight": 4000}, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Configure neighbor for route map + input_dict_4 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1", + "direction": "in", + } + ] + } + } + }, + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1", + "direction": "out", + } + ] + } + } + }, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1", + "direction": "in", + } + ] + } + } + }, + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1", + "direction": "out", + } + ] + } + } + }, + } + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + time.sleep(2) + for adt in ADDR_TYPES: + input_dict_4 = { + "r3": { + "route_maps": { + "rmap_match_pf_1": [{"action": "permit", "set": {"metric": 50,}}] + } + } + } + # Verifying RIB routes + static_routes = [NETWORK[adt][0]] + result = verify_bgp_attributes( + tgen, adt, "r3", static_routes, "rmap_match_pf_1", input_dict_3 + ) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_attributes( + tgen, adt, "r4", static_routes, "rmap_match_pf_1", input_dict_4 + ) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + logger.info("Testcase " + tc_name + " :Passed \n") + + # Uncomment next line for debugging + # tgen.mininet_cli() + + +def test_route_map_match_only_no_set_p0(request): + """ + TC_38: + Test add/remove route-maps with multiple match + clauses and without any set statement.(Match only) + """ + + tgen = get_topogen() + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + for adt in ADDR_TYPES: + # Create Static routes + input_dict = { + "r1": { + "static_routes": [ + { + "network": NETWORK[adt][0], + "no_of_ip": 1, + "next_hop": NEXT_HOP[adt], + } + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Api call to redistribute static routes + input_dict_1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Create ip prefix list + input_dict_2 = { + "r1": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + {"seqid": 10, "network": "any", "action": "permit"} + ] + }, + "ipv6": { + "pf_list_1_ipv6": [ + {"seqid": 100, "network": "any", "action": "permit"} + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Create route map + for addr_type in ADDR_TYPES: + input_dict_3 = { + "r1": { + "route_maps": { + "rmap_match_pf_1_{}".format(addr_type): [ + {"action": "permit", "set": {"metric": 50, "locPrf": 150,}} + ] + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Configure neighbor for route map + input_dict_4 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv4", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv6", + "direction": "out", + } + ] + } + } + } + } + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Create ip prefix list + input_dict_5 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + {"seqid": 10, "network": "any", "action": "permit"} + ] + }, + "ipv6": { + "pf_list_1_ipv6": [ + {"seqid": 100, "network": "any", "action": "permit"} + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_5) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Create route map + for addr_type in ADDR_TYPES: + input_dict_6 = { + "r3": { + "route_maps": { + "rmap_match_pf_2_{}".format(addr_type): [ + { + "action": "permit", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_6) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Configure neighbor for route map + input_dict_7 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_2_ipv4", + "direction": "in", + } + ] + } + } + }, + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_2_ipv4", + "direction": "out", + } + ] + } + } + }, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_2_ipv6", + "direction": "in", + } + ] + } + } + }, + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_2_ipv6", + "direction": "out", + } + ] + } + } + }, + } + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_7) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for adt in ADDR_TYPES: + # Verifying RIB routes + static_routes = [NETWORK[adt][0]] + result = verify_bgp_attributes( + tgen, adt, "r3", static_routes, "rmap_match_pf_1", input_dict_3 + ) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp-route-map/test_route_map_topo2.py b/tests/topotests/bgp-route-map/test_route_map_topo2.py new file mode 100755 index 0000000000..3056aa29f3 --- /dev/null +++ b/tests/topotests/bgp-route-map/test_route_map_topo2.py @@ -0,0 +1,3935 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2019 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +"""Following tests are covered to test route-map functionality. +TC_57: + Create route map to match prefix-list and permit inbound + and outbound prefixes and set criteria on match +TC_52: + Test modify set/match clauses in a route-map to see + if it takes immediate effect. +TC_61: + Delete the route maps. +TC_50_1: + Test modify/remove prefix-lists referenced by a + route-map for match statement. +TC_50_2: + Remove prefix-list referencec by route-map match cluase + and verifying it reflecting as intended +TC_51: + Add and remove community-list referencec by route-map match cluase + and verifying it reflecting as intended +TC_45: + Test multiple match statements as part of a route-map"s single + sequence number. (Logical OR-ed of multiple match statements) +TC_44: + Test multiple match statements as part of a route-map"s single + sequence number. (Logical AND of multiple match statements) +TC_41: + Test add/remove route-maps to specific neighbor and see if + it takes effect as intended +TC_56: + Test clear BGP sessions and interface flaps to see if + route-map properties are intact. +TC_46: + Verify if a blank sequence number can be create(without any + match/set clause) and check if it allows all the traffic/prefixes +TC_48: + Create route map setting local preference and weight to eBGP peeer + and metric to ibgp peer and verifying it should not get advertised +TC_43: + Test multiple set statements as part of a route-map"s + single sequence number. +TC_54: + Verify route-maps continue clause functionality. +TC_55: + Verify route-maps goto clause functionality. +TC_53: + Verify route-maps call clause functionality. +TC_58: + Create route map deny inbound and outbound prefixes on + match prefix list and set criteria on match +TC_59: + Create route map to permit inbound prefixes with filter + match tag and set criteria +TC_60 + Create route map to deny outbound prefixes with filter match tag, + and set criteria +""" + +################################# +# TOPOLOGY +################################# +""" + + +-------+ + +--------- | R2 | + | +-------+ + |iBGP | + +-------+ | + | R1 | |iBGP + +-------+ | + | | + | iBGP +-------+ eBGP +-------+ + +---------- | R3 |----------| R4 | + +-------+ +-------+ + | + |eBGP + | + +-------+ + | R5 | + +-------+ + + +""" + +import sys +import json +import time +import pytest +import inspect +import os +from time import sleep + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, get_topogen +from mininet.topo import Topo + +# Required to instantiate the topology builder class. +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + create_static_routes, + verify_rib, + delete_route_maps, + create_bgp_community_lists, + interface_status, + create_route_maps, + create_prefix_lists, + verify_route_maps, + check_address_types, + verify_bgp_community, + shutdown_bringup_interface, + verify_prefix_lists, + reset_config_on_routers, + verify_create_community_list, +) +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + create_router_bgp, + clear_bgp_and_verify, + verify_bgp_attributes, +) +from lib.topojson import build_topo_from_json, build_config_from_json + +# Reading the data from JSON File for topology and configuration creation +jsonFile = "{}/bgp_route_map_topo2.json".format(CWD) + +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +# Global variables +# Global variables +bgp_convergence = False +NETWORK = {"ipv4": ["11.0.20.1/32", "11.0.20.2/32"], "ipv6": ["2::1/128", "2::2/128"]} + +bgp_convergence = False +BGP_CONVERGENCE = False +ADDR_TYPES = check_address_types() + + +class BGPRmapTopo(Topo): + """BGPRmapTopo. + + BGPRmap topology 1 + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + """Build function.""" + tgen = get_topogen(self) + + # Building topology and configuration from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """setup_module. + + Set up the pytest environment + * `mod`: module name + """ + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(BGPRmapTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Checking BGP convergence + global bgp_convergence + global ADDR_TYPES + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Api call verify whether BGP is converged + bgp_convergence = verify_bgp_convergence(tgen, topo) + assert bgp_convergence is True, "setup_module :Failed \n Error:" " {}".format( + bgp_convergence + ) + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """teardown_module. + + Teardown the pytest environment. + * `mod`: module name + """ + logger.info("Running teardown_module to delete topology") + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# Tests starting +##################################################### + + +def test_rmap_match_prefix_list_permit_in_and_outbound_prefixes_p0(): + """ + TC: 57 + Create route map to match prefix-list and permit inbound + and outbound prefixes and set criteria on match + """ + tgen = get_topogen() + global bgp_convergence + + if bgp_convergence is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = inspect.stack()[0][3] + write_test_header(tc_name) + reset_config_on_routers(tgen) + + # Create ip prefix list + input_dict_2 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + {"seqid": 10, "network": "any", "action": "permit",} + ] + }, + "ipv6": { + "pf_list_1_ipv6": [ + {"seqid": 10, "network": "any", "action": "permit",} + ] + }, + } + } + } + + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + for addr_type in ADDR_TYPES: + # Create route map + input_dict_3 = { + "r3": { + "route_maps": { + "rmap_match_pf_1_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": "5", + "match": { + addr_type: {"prefix_lists": "pf_list_1_" + addr_type} + }, + "set": {"locPrf": 150, "weight": 100}, + }, + ], + "rmap_match_pf_2_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": "5", + "match": { + addr_type: {"prefix_lists": "pf_list_1_" + addr_type} + }, + "set": {"metric": 50}, + }, + ], + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Configure neighbor for route map + input_dict_4 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv4", + "direction": "in", + } + ] + } + } + }, + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_2_ipv4", + "direction": "out", + } + ] + } + } + }, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv6", + "direction": "in", + } + ] + } + } + }, + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_2_ipv6", + "direction": "out", + } + ] + } + } + }, + } + } + }, + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + input_dict = topo["routers"] + + # dual stack changes + for addr_type in ADDR_TYPES: + result4 = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result4 is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result4 + ) + + # Verifying BGP set attributes + dut = "r3" + routes = { + "ipv4": ["10.0.20.1/32", "10.0.20.2/32"], + "ipv6": ["1::1/128", "1::2/128"], + } + # dual stack changes + for addr_type in ADDR_TYPES: + rmap_name = "rmap_match_pf_1_{}".format(addr_type) + result4 = verify_bgp_attributes( + tgen, addr_type, dut, routes[addr_type], rmap_name, input_dict_3 + ) + assert result4 is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result4 + ) + + # Verifying RIB routes + dut = "r4" + protocol = "bgp" + # dual stack changes + for addr_type in ADDR_TYPES: + result4 = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result4 is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result4 + ) + + # Verifying BGP set attributes + dut = "r4" + routes = { + "ipv4": ["10.0.20.1/32", "10.0.20.2/32"], + "ipv6": ["1::1/128", "1::2/128"], + } + # dual stack changes + for addr_type in ADDR_TYPES: + rmap_name = "rmap_match_pf_2_{}".format(addr_type) + result = verify_bgp_attributes( + tgen, addr_type, dut, routes[addr_type], rmap_name, input_dict_3 + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + write_test_footer(tc_name) + + # Uncomment next line for debugging + # tgen.mininet_cli() + + +def test_modify_set_match_clauses_in_rmap_p0(): + """ + TC_52: + Test modify set/match clauses in a route-map to see + if it takes immediate effect. + """ + + tgen = get_topogen() + global bgp_convergence + + if bgp_convergence is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = inspect.stack()[0][3] + write_test_header(tc_name) + reset_config_on_routers(tgen) + + # Create ip prefix list + + input_dict_2 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + {"seqid": 10, "network": "any", "action": "permit",} + ], + "pf_list_2_ipv4": [ + {"seqid": 10, "network": "any", "action": "permit"} + ], + }, + "ipv6": { + "pf_list_1_ipv6": [ + {"seqid": 10, "network": "any", "action": "permit",} + ], + "pf_list_2_ipv6": [ + {"seqid": 10, "network": "any", "action": "permit"} + ], + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Create route map + for addr_type in ADDR_TYPES: + input_dict_3 = { + "r3": { + "route_maps": { + "rmap_match_pf_1_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": "5", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + "set": {"locPrf": 150,}, + } + ], + "rmap_match_pf_2_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": "5", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + "set": {"metric": 50}, + } + ], + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Configure neighbor for route map + input_dict_4 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv4", + "direction": "in", + } + ] + } + } + }, + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_2_ipv4", + "direction": "out", + } + ] + } + } + }, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv6", + "direction": "in", + } + ] + } + } + }, + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_2_ipv6", + "direction": "out", + } + ] + } + } + }, + } + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + input_dict = topo["routers"] + for addr_type in ADDR_TYPES: + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verifying BGP set attributes + dut = "r3" + routes = { + "ipv4": ["10.0.20.1/32", "10.0.20.2/32"], + "ipv6": ["1::1/128", "1::2/128"], + } + # dual stack changes + for addr_type in ADDR_TYPES: + rmap_name = "rmap_match_pf_1_{}".format(addr_type) + result4 = verify_bgp_attributes( + tgen, addr_type, dut, routes[addr_type], rmap_name, input_dict_3 + ) + assert result4 is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result4 + ) + + # Verifying RIB routes + dut = "r4" + protocol = "bgp" + # dual stack changes + for addr_type in ADDR_TYPES: + result4 = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result4 is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result4 + ) + + # Verifying BGP set attributes + dut = "r4" + routes = { + "ipv4": ["10.0.20.1/32", "10.0.20.2/32"], + "ipv6": ["1::1/128", "1::2/128"], + } + for addr_type in ADDR_TYPES: + rmap_name = "rmap_match_pf_2_{}".format(addr_type) + result = verify_bgp_attributes( + tgen, addr_type, dut, routes[addr_type], rmap_name, input_dict_3 + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Modify set/match clause of in-used route map + for addr_type in ADDR_TYPES: + input_dict_3 = { + "r3": { + "route_maps": { + "rmap_match_pf_1_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": "5", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + "set": {"locPrf": 1000,}, + } + ], + "rmap_match_pf_2_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": "5", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + "set": {"metric": 2000}, + } + ], + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + for addr_type in ADDR_TYPES: + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verifying BGP set attributes + dut = "r3" + routes = { + "ipv4": ["10.0.20.1/32", "10.0.20.2/32"], + "ipv6": ["1::1/128", "1::2/128"], + } + for addr_type in ADDR_TYPES: + rmap_name = "rmap_match_pf_1_{}".format(addr_type) + result = verify_bgp_attributes( + tgen, addr_type, dut, routes[addr_type], rmap_name, input_dict_3 + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verifying RIB routes + dut = "r4" + protocol = "bgp" + for addr_type in ADDR_TYPES: + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verifying BGP set attributes + dut = "r4" + routes = { + "ipv4": ["10.0.20.1/32", "10.0.20.2/32"], + "ipv6": ["1::1/128", "1::2/128"], + } + for addr_type in ADDR_TYPES: + rmap_name = "rmap_match_pf_2_{}".format(addr_type) + result = verify_bgp_attributes( + tgen, addr_type, dut, routes[addr_type], rmap_name, input_dict_3 + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + # Uncomment next line for debugging + # tgen.mininet_cli() + + +def test_delete_route_maps_p1(): + """ + TC_61: + Delete the route maps. + """ + + tgen = get_topogen() + global bgp_convergence + + if bgp_convergence is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = inspect.stack()[0][3] + write_test_header(tc_name) + reset_config_on_routers(tgen) + + # Create route map + for addr_type in ADDR_TYPES: + input_dict_3 = { + "r3": { + "route_maps": { + "rmap_match_tag_1_{}".format(addr_type): [ + {"action": "deny", "match": {addr_type: {"tag": "4001"}}} + ] + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Delete route maps + for addr_type in ADDR_TYPES: + input_dict = {"r3": {"route_maps": ["rmap_match_tag_1_{}".format(addr_type)]}} + result = delete_route_maps(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_route_maps(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + write_test_footer(tc_name) + + # Uncomment next line for debugging + # tgen.mininet_cli() + + +def test_modify_prefix_list_referenced_by_rmap_p0(): + """ + TC_50_1: + Test modify/remove prefix-lists referenced by a + route-map for match statement. + """ + + tgen = get_topogen() + global bgp_convergence + + if bgp_convergence is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = inspect.stack()[0][3] + write_test_header(tc_name) + reset_config_on_routers(tgen) + + # Create ip prefix list + input_dict_2 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + {"seqid": 10, "network": "any", "action": "permit",} + ] + }, + "ipv6": { + "pf_list_1_ipv6": [ + {"seqid": 100, "network": "any", "action": "permit",} + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Create route map + for addr_type in ADDR_TYPES: + input_dict_3 = { + "r3": { + "route_maps": { + "rmap_match_pf_1_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": "5", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + "set": {"locPrf": 150, "weight": 100}, + } + ], + "rmap_match_pf_2_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": "5", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + "set": {"metric": 50}, + } + ], + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Configure neighbor for route map + input_dict_4 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv4", + "direction": "in", + } + ] + } + } + }, + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_2_ipv4", + "direction": "out", + } + ] + } + } + }, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv6", + "direction": "in", + } + ] + } + } + }, + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_2_ipv6", + "direction": "out", + } + ] + } + } + }, + } + } + }, + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + input_dict = topo["routers"] + for addr_type in ADDR_TYPES: + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verifying BGP set attributes + dut = "r3" + routes = { + "ipv4": ["10.0.20.1/32", "10.0.20.2/32"], + "ipv6": ["1::1/128", "1::2/128"], + } + for addr_type in ADDR_TYPES: + rmap_name = "rmap_match_pf_1_{}".format(addr_type) + result = verify_bgp_attributes( + tgen, addr_type, dut, routes[addr_type], rmap_name, input_dict_3 + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verifying RIB routes + dut = "r4" + protocol = "bgp" + for addr_type in ADDR_TYPES: + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verifying BGP set attributes + dut = "r4" + routes = { + "ipv4": ["10.0.20.1/32", "10.0.20.2/32"], + "ipv6": ["1::1/128", "1::2/128"], + } + + for addr_type in ADDR_TYPES: + rmap_name = "rmap_match_pf_2_{}".format(addr_type) + result = verify_bgp_attributes( + tgen, addr_type, dut, routes[addr_type], rmap_name, input_dict_3 + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Modify ip prefix list + input_dict_2 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + {"seqid": 10, "network": "any", "action": "deny"} + ] + }, + "ipv6": { + "pf_list_1_ipv6": [ + {"seqid": 100, "network": "any", "action": "deny"} + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + sleep(5) + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + for addr_type in ADDR_TYPES: + result = verify_rib( + tgen, addr_type, dut, input_dict, protocol=protocol, expected=False + ) + assert result is not True, "Testcase {} : Failed \n" + "routes are not present \n Error: {}".format(tc_name, result) + logger.info("Expected behaviour: {}".format(result)) + + # Verifying RIB routes + dut = "r4" + protocol = "bgp" + for addr_type in ADDR_TYPES: + result = verify_rib( + tgen, addr_type, dut, input_dict, protocol=protocol, expected=False + ) + assert result is not True, "Testcase {} : Failed \n" + "Expected behaviour: routes are not present \n " + "Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + # Uncomment next line for debugging + # tgen.mininet_cli() + + +def test_remove_prefix_list_referenced_by_rmap_p0(): + """ + TC_50_2: + Remove prefix-list referencec by route-map match cluase + and verifying it reflecting as intended + """ + tgen = get_topogen() + global bgp_convergence + + if bgp_convergence is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = inspect.stack()[0][3] + write_test_header(tc_name) + reset_config_on_routers(tgen) + + # Create ip prefix list + input_dict_2 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + {"seqid": 10, "network": "any", "action": "permit"} + ] + }, + "ipv6": { + "pf_list_1_ipv6": [ + {"seqid": 100, "network": "any", "action": "permit"} + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Create route map + for addr_type in ADDR_TYPES: + input_dict_3 = { + "r3": { + "route_maps": { + "rmap_match_pf_1_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": "5", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + "set": {"locPrf": 150,}, + } + ], + "rmap_match_pf_2_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": "5", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + "set": {"metric": 50}, + } + ], + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Configure neighbor for route map + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv4", + "direction": "in", + } + ] + } + } + }, + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_2_ipv4", + "direction": "out", + } + ] + } + } + }, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv6", + "direction": "in", + } + ] + } + } + }, + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_2_ipv6", + "direction": "out", + } + ] + } + } + }, + } + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + input_dict = topo["routers"] + for addr_type in ADDR_TYPES: + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verifying BGP set attributes + dut = "r3" + routes = { + "ipv4": ["10.0.20.1/32", "10.0.20.2/32"], + "ipv6": ["1::1/128", "1::2/128"], + } + for addr_type in ADDR_TYPES: + rmap_name = "rmap_match_pf_1_{}".format(addr_type) + result = verify_bgp_attributes( + tgen, addr_type, dut, routes[addr_type], rmap_name, input_dict_3 + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verifying RIB routes + dut = "r4" + protocol = "bgp" + for addr_type in ADDR_TYPES: + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verifying BGP set attributes + dut = "r4" + routes = { + "ipv4": ["10.0.20.1/32", "10.0.20.2/32"], + "ipv6": ["1::1/128", "1::2/128"], + } + for addr_type in ADDR_TYPES: + rmap_name = "rmap_match_pf_2_{}".format(addr_type) + result = verify_bgp_attributes( + tgen, addr_type, dut, routes[addr_type], rmap_name, input_dict_3 + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Remove/Delete prefix list + input_dict_3 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + { + "seqid": 10, + "network": "any", + "action": "permit", + "delete": True, + } + ] + }, + "ipv6": { + "pf_list_1_ipv6": [ + { + "seqid": 100, + "network": "any", + "action": "permit", + "delete": True, + } + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + result = verify_prefix_lists(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Api call to clear bgp, so config changes would be reflected + dut = "r3" + result = clear_bgp_and_verify(tgen, topo, dut) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + for addr_type in ADDR_TYPES: + result = verify_rib( + tgen, addr_type, dut, input_dict, protocol=protocol, expected=False + ) + assert result is not True, "Testcase {} : Failed \n" + "routes are not present \n Error: {}".format(tc_name, result) + logger.info("Expected behaviour: {}".format(result)) + + # Verifying RIB routes + dut = "r4" + protocol = "bgp" + for addr_type in ADDR_TYPES: + result = verify_rib( + tgen, addr_type, dut, input_dict, protocol=protocol, expected=False + ) + assert result is not True, "Testcase {} : Failed \n" + "routes are not present \n Error: {}".format(tc_name, result) + logger.info("Expected behaviour: {}".format(result)) + + write_test_footer(tc_name) + + # Uncomment next line for debugging + # tgen.mininet_cli() + + +def test_add_and_remove_community_list_referenced_by_rmap_p0(): + """ + TC_51: + Add and remove community-list referencec by route-map match cluase + and verifying it reflecting as intended + """ + tgen = get_topogen() + global bgp_convergence + + if bgp_convergence is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = inspect.stack()[0][3] + write_test_header(tc_name) + reset_config_on_routers(tgen) + + # Creating configuration from JSON + # build_config_from_json(tgen, topo) + + # Create route map + for addr_type in ADDR_TYPES: + input_dict_5 = { + "r1": { + "route_maps": { + "rm_r1_out_{}".format(addr_type): [ + { + "action": "permit", + "set": { + "large_community": {"num": "1:1:1 1:2:3 2:1:1 2:2:2"} + }, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_5) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Configure neighbor for route map + input_dict_6 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1": { + "route_maps": [ + { + "name": "rm_r1_out_ipv4", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1": { + "route_maps": [ + { + "name": "rm_r1_out_ipv6", + "direction": "out", + } + ] + } + } + } + } + } + }, + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_6) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + # Create standard large commumity-list + input_dict_1 = { + "r3": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "rmap_lcomm_{}".format(addr_type), + "value": "1:1:1 1:2:3 2:1:1 2:2:2", + "large": True, + } + ] + } + } + result = create_bgp_community_lists(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verify BGP large community is created + result = verify_create_community_list(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + # Create route map + input_dict_2 = { + "r3": { + "route_maps": { + "rm_r3_in_{}".format(addr_type): [ + { + "action": "permit", + "match": { + addr_type: { + "large-community-list": { + "id": "rmap_lcomm_" + addr_type + } + } + }, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Configure neighbor for route map + input_dict_3 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rm_r3_in_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rm_r3_in_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_3) + + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + sleep(5) + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + input_dict = topo["routers"] + for addr_type in ADDR_TYPES: + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verify large-community-list + dut = "r3" + networks = { + "ipv4": ["10.0.20.1/32", "10.0.20.2/32"], + "ipv6": ["1::1/128", "1::2/128"], + } + input_dict_4 = {"largeCommunity": "1:1:1 1:2:3 2:1:1 2:2:2"} + for addr_type in ADDR_TYPES: + result = verify_bgp_community( + tgen, addr_type, dut, networks[addr_type], input_dict_4 + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + write_test_footer(tc_name) + + # Uncomment next line for debugging + # tgen.mininet_cli() + + +def test_multiple_match_statement_in_route_map_logical_ORed_p0(): + """ + TC_45: + Test multiple match statements as part of a route-map"s single + sequence number. (Logical OR-ed of multiple match statements) + """ + tgen = get_topogen() + global bgp_convergence + + if bgp_convergence is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = inspect.stack()[0][3] + write_test_header(tc_name) + reset_config_on_routers(tgen) + + # Api call to advertise networks + input_dict_nw1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": {"advertise_networks": [{"network": "10.0.30.1/32"}]} + }, + "ipv6": { + "unicast": {"advertise_networks": [{"network": "1::1/128"}]} + }, + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_nw1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Api call to advertise networks + input_dict_nw2 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": {"advertise_networks": [{"network": "20.0.30.1/32"}]} + }, + "ipv6": { + "unicast": {"advertise_networks": [{"network": "2::1/128"}]} + }, + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_nw2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Create ip prefix list + input_dict_2 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + {"seqid": 10, "network": "any", "action": "permit"} + ] + }, + "ipv6": { + "pf_list_1_ipv6": [ + {"seqid": 100, "network": "any", "action": "permit"} + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Create ip prefix list + input_dict_2 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_2_ipv4": [ + {"seqid": 10, "network": "any", "action": "permit"} + ] + }, + "ipv6": { + "pf_list_2_ipv6": [ + {"seqid": 100, "network": "any", "action": "permit"} + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + input_dict_3_addr_type = {} + # Create route map + for addr_type in ADDR_TYPES: + input_dict_3 = { + "r3": { + "route_maps": { + "rmap_match_pf_1_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": "5", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + "set": {"locPrf": 150}, + } + ] + } + } + } + input_dict_3_addr_type[addr_type] = input_dict_3 + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Create route map + for addr_type in ADDR_TYPES: + input_dict_3 = { + "r3": { + "route_maps": { + "rmap_match_pf_1_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": "5", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + "set": {"locPrf": 200}, + } + ] + } + } + } + input_dict_3_addr_type[addr_type] = input_dict_3 + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Configure neighbor for route map + input_dict_6 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_6) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + input_dict = topo["routers"] + for addr_type in ADDR_TYPES: + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verifying BGP set attributes + dut = "r3" + routes = {"ipv4": ["10.0.30.1/32"], "ipv6": ["1::1/128"]} + for addr_type in ADDR_TYPES: + rmap_name = "rmap_match_pf_1_{}".format(addr_type) + result = verify_bgp_attributes( + tgen, + addr_type, + dut, + routes[addr_type], + rmap_name, + input_dict_3_addr_type[addr_type], + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verifying BGP set attributes + routes = {"ipv4": ["20.0.30.1/32"], "ipv6": ["2::1/128"]} + for addr_type in ADDR_TYPES: + result = verify_bgp_attributes( + tgen, addr_type, dut, routes[addr_type], rmap_name, input_dict_3 + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + # Uncomment next line for debugging + # tgen.mininet_cli() + + +def test_multiple_match_statement_in_route_map_logical_ANDed_p1(): + """ + TC_44: + Test multiple match statements as part of a route-map"s single + sequence number. (Logical AND of multiple match statements) + """ + tgen = get_topogen() + global bgp_convergence + + if bgp_convergence is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = inspect.stack()[0][3] + write_test_header(tc_name) + reset_config_on_routers(tgen) + + # Create route map + for addr_type in ADDR_TYPES: + input_dict_5 = { + "r1": { + "route_maps": { + "rm_r1_out_{}".format(addr_type): [ + { + "action": "permit", + "set": { + "large_community": {"num": "1:1:1 1:2:3 2:1:1 2:2:2"} + }, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_5) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Configure neighbor for route map + for addr_type in ADDR_TYPES: + input_dict_6 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1": { + "route_maps": [ + { + "name": "rm_r1_out_{}".format( + addr_type + ), + "direction": "out", + } + ] + } + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_6) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Create ip prefix list + input_dict_2 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + {"seqid": 10, "network": "any", "action": "permit"} + ] + }, + "ipv6": { + "pf_list_1_ipv6": [ + {"seqid": 100, "network": "any", "action": "permit"} + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + # Create standard large commumity-list + input_dict_1 = { + "r3": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "rmap_lcomm_{}".format(addr_type), + "value": "1:1:1 1:2:3 2:1:1 2:2:2", + "large": True, + } + ] + } + } + result = create_bgp_community_lists(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verify BGP large community is created + result = verify_create_community_list(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Create route map + for addr_type in ADDR_TYPES: + input_dict_3 = { + "r3": { + "route_maps": { + "rmap_match_pf_1_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": "5", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + "set": {"locPrf": 150,}, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + # Create route map + input_dict_3 = { + "r3": { + "route_maps": { + "rmap_match_pf_1_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": "5", + "match": { + addr_type: { + "large_community_list": { + "id": "rmap_lcomm_" + addr_type + } + } + }, + "set": {"locPrf": 150,}, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + # Configure neighbor for route map + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r3": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_{}".format( + addr_type + ), + "direction": "in", + } + ] + } + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + # sleep(10) + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + input_dict = topo["routers"] + for addr_type in ADDR_TYPES: + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verifying BGP set attributes + dut = "r3" + routes = { + "ipv4": ["10.0.20.1/32", "10.0.20.2/32"], + "ipv6": ["1::1/128", "1::2/128"], + } + for addr_type in ADDR_TYPES: + rmap_name = "rmap_match_pf_1_{}".format(addr_type) + result = verify_bgp_attributes( + tgen, addr_type, dut, routes[addr_type], rmap_name, input_dict_3 + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + # Uncomment next line for debugging + # tgen.mininet_cli() + + +def test_add_remove_rmap_to_specific_neighbor_p0(): + """ + TC_41: + Test add/remove route-maps to specific neighbor and see if + it takes effect as intended + """ + tgen = get_topogen() + global bgp_convergence + + if bgp_convergence is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = inspect.stack()[0][3] + write_test_header(tc_name) + reset_config_on_routers(tgen) + + # Create ip prefix list + input_dict_2 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + {"seqid": 10, "network": "any", "action": "deny"} + ] + }, + "ipv6": { + "pf_list_1_ipv6": [ + {"seqid": 100, "network": "any", "action": "deny"} + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Create route map + for addr_type in ADDR_TYPES: + input_dict_3 = { + "r3": { + "route_maps": { + "rmap_match_pf_1_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": "5", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + "set": {"locPrf": 150,}, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Configure neighbor for route map + input_dict_4 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + input_dict = topo["routers"] + for addr_type in ADDR_TYPES: + result = verify_rib( + tgen, addr_type, dut, input_dict, protocol=protocol, expected=False + ) + assert result is not True, "Testcase {} : Failed \n Error" + "Routes are still present: {}".format(tc_name, result) + logger.info("Expected behaviour: {}".format(result)) + + # Remove applied rmap from neighbor + input_dict_4 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv4", + "direction": "in", + "delete": True, + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv6", + "direction": "in", + "delete": True, + } + ] + } + } + } + } + } + }, + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + input_dict = topo["routers"] + for addr_type in ADDR_TYPES: + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + # Uncomment next line for debugging + # tgen.mininet_cli() + + +def test_clear_bgp_and_flap_interface_to_verify_rmap_properties_p0(): + """ + TC_56: + Test clear BGP sessions and interface flaps to see if + route-map properties are intact. + """ + tgen = get_topogen() + global bgp_convergence + + if bgp_convergence is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = inspect.stack()[0][3] + write_test_header(tc_name) + reset_config_on_routers(tgen) + + # Create ip prefix list + input_dict_2 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + {"seqid": 10, "network": "any", "action": "permit"} + ] + }, + "ipv6": { + "pf_list_1_ipv6": [ + {"seqid": 100, "network": "any", "action": "permit"} + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Create route map + for addr_type in ADDR_TYPES: + input_dict_3 = { + "r3": { + "route_maps": { + "rmap_match_pf_1_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": "5", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + "set": {"locPrf": 150, "weight": 100}, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Configure neighbor for route map + input_dict_4 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + input_dict = topo["routers"] + for addr_type in ADDR_TYPES: + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verifying BGP set attributes + dut = "r3" + routes = { + "ipv4": ["10.0.20.1/32", "10.0.20.2/32"], + "ipv6": ["1::1/128", "1::2/128"], + } + for addr_type in ADDR_TYPES: + rmap_name = "rmap_match_pf_1_{}".format(addr_type) + result = verify_bgp_attributes( + tgen, addr_type, dut, routes[addr_type], rmap_name, input_dict_3 + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # clear bgp, so config changes would be reflected + dut = "r3" + result = clear_bgp_and_verify(tgen, topo, dut) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + input_dict = topo["routers"] + for addr_type in ADDR_TYPES: + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verifying BGP set attributes + dut = "r3" + routes = { + "ipv4": ["10.0.20.1/32", "10.0.20.2/32"], + "ipv6": ["1::1/128", "1::2/128"], + } + for addr_type in ADDR_TYPES: + rmap_name = "rmap_match_pf_1_{}".format(addr_type) + result = verify_bgp_attributes( + tgen, addr_type, dut, routes[addr_type], rmap_name, input_dict_3 + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Flap interface to see if route-map properties are intact + # Shutdown interface + dut = "r3" + intf = "r3-r1-eth0" + shutdown_bringup_interface(tgen, dut, intf, False) + + sleep(5) + + # Bringup interface + dut = "r3" + intf = "r3-r1-eth0" + shutdown_bringup_interface(tgen, dut, intf, True) + + # Verify BGP convergence once interface is up + result = verify_bgp_convergence(tgen, topo) + assert result is True, "setup_module :Failed \n Error:" " {}".format(result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + input_dict = topo["routers"] + for addr_type in ADDR_TYPES: + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verifying BGP set attributes + dut = "r3" + routes = { + "ipv4": ["10.0.20.1/32", "10.0.20.2/32"], + "ipv6": ["1::1/128", "1::2/128"], + } + for addr_type in ADDR_TYPES: + rmap_name = "rmap_match_pf_1_{}".format(addr_type) + result = verify_bgp_attributes( + tgen, addr_type, dut, routes[addr_type], rmap_name, input_dict_3 + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + # Uncomment next line for debugging + # tgen.mininet_cli() + + +def test_rmap_without_match_and_set_clause_p0(): + """ + TC_46: + Verify if a blank sequence number can be create(without any + match/set clause) and check if it allows all the traffic/prefixes + """ + tgen = get_topogen() + global bgp_convergence + + if bgp_convergence is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = inspect.stack()[0][3] + write_test_header(tc_name) + reset_config_on_routers(tgen) + + # Create route map + for addr_type in ADDR_TYPES: + input_dict_3 = { + "r3": { + "route_maps": { + "rmap_no_match_set_1_{}".format(addr_type): [ + {"action": "permit", "seq_id": "5"} + ], + "rmap_no_match_set_2_{}".format(addr_type): [ + {"action": "deny", "seq_id": "5"} + ], + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Configure neighbor for route map + input_dict_4 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_no_match_set_1_ipv4", + "direction": "in", + } + ] + } + } + }, + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_no_match_set_2_ipv4", + "direction": "out", + } + ] + } + } + }, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_no_match_set_1_ipv6", + "direction": "in", + } + ] + } + } + }, + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_no_match_set_2_ipv6", + "direction": "out", + } + ] + } + } + }, + } + } + }, + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + input_dict = topo["routers"] + for addr_type in ADDR_TYPES: + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verifying RIB routes + dut = "r4" + protocol = "bgp" + for addr_type in ADDR_TYPES: + result = verify_rib( + tgen, addr_type, dut, input_dict, protocol=protocol, expected=False + ) + assert result is not True, "Testcase {} : Failed \n" + "routes are not present \n Error: {}".format(tc_name, result) + logger.info("Expected behaviour: {}".format(result)) + + write_test_footer(tc_name) + # Uncomment next line for debugging + # tgen.mininet_cli() + + +def test_set_localpref_weight_to_ebgp_and_med_to_ibgp_peers_p0(): + """ + TC_48: + Create route map setting local preference and weight to eBGP peeer + and metric to ibgp peer and verifying it should not get advertised + """ + tgen = get_topogen() + global bgp_convergence + + if bgp_convergence is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = inspect.stack()[0][3] + write_test_header(tc_name) + reset_config_on_routers(tgen) + + # Create ip prefix list + input_dict_2 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + {"seqid": 10, "network": "any", "action": "permit"} + ] + }, + "ipv6": { + "pf_list_1_ipv6": [ + {"seqid": 100, "network": "any", "action": "permit"} + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Create route map + input_dict_3_addr_type = {} + for addr_type in ADDR_TYPES: + input_dict_3 = { + "r3": { + "route_maps": { + "rmap_match_pf_1_{}".format(addr_type): [ + { + "action": "permit", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + "set": {"metric": 50}, + } + ], + "rmap_match_pf_2_{}".format(addr_type): [ + { + "action": "permit", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + "set": {"locPrf": 150}, + } + ], + "rmap_match_pf_3_{}".format(addr_type): [ + { + "action": "permit", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + "set": {"weight": 1000}, + } + ], + } + } + } + input_dict_3_addr_type[addr_type] = input_dict_3 + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Configure neighbor for route map + input_dict_4 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv4", + "direction": "in", + } + ] + } + } + }, + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_2_ipv4", + "direction": "out", + } + ] + } + } + }, + "r5": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_3_ipv4", + "direction": "out", + } + ] + } + } + }, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv6", + "direction": "in", + } + ] + } + } + }, + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_2_ipv6", + "direction": "out", + } + ] + } + } + }, + "r5": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_3_ipv6", + "direction": "out", + } + ] + } + } + }, + } + } + }, + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + input_dict = topo["routers"] + for addr_type in ADDR_TYPES: + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verifying BGP set attributes + dut = "r3" + routes = { + "ipv4": ["10.0.20.1/32", "10.0.20.2/32"], + "ipv6": ["1::1/128", "1::2/128"], + } + rmap_name = "rmap_match_pf_1" + for addr_type in ADDR_TYPES: + rmap_name = "rmap_match_pf_1_{}".format(addr_type) + result = verify_bgp_attributes( + tgen, addr_type, dut, routes[addr_type], rmap_name, input_dict_3 + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verifying RIB routes + dut = "r4" + protocol = "bgp" + for addr_type in ADDR_TYPES: + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verifying BGP set attributes + dut = "r4" + routes = { + "ipv4": ["10.0.20.1/32", "10.0.20.2/32"], + "ipv6": ["1::1/128", "1::2/128"], + } + rmap_name = "rmap_match_pf_2" + for addr_type in ADDR_TYPES: + rmap_name = "rmap_match_pf_2_{}".format(addr_type) + + result = verify_bgp_attributes( + tgen, + addr_type, + dut, + routes[addr_type], + rmap_name, + input_dict_3_addr_type[addr_type], + expected=False, + ) + assert result is not True, "Testcase {} : Failed \n" + "Attributes are not set \n Error: {}".format(tc_name, result) + logger.info("Expected behaviour: {}".format(result)) + + # Verifying RIB routes + dut = "r5" + protocol = "bgp" + for addr_type in ADDR_TYPES: + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + # Verifying BGP set attributes + dut = "r5" + routes = { + "ipv4": ["10.0.20.1/32", "10.0.20.2/32"], + "ipv6": ["1::1/128", "1::2/128"], + } + + rmap_name = "rmap_match_pf_3" + for addr_type in ADDR_TYPES: + rmap_name = "rmap_match_pf_3_{}".format(addr_type) + result = verify_bgp_attributes( + tgen, + addr_type, + dut, + routes[addr_type], + rmap_name, + input_dict_3_addr_type[addr_type], + expected=False, + ) + assert result is not True, "Testcase {} : Failed \n" + "Attributes are not set \n Error: {}".format(tc_name, result) + logger.info("Expected behaviour: {}".format(result)) + + write_test_footer(tc_name) + + # Uncomment next line for debugging + # tgen.mininet_cli() + + +def test_multiple_set_on_single_sequence_in_rmap_p0(): + """ + TC_43: + Test multiple set statements as part of a route-map"s + single sequence number. + """ + tgen = get_topogen() + global bgp_convergence + + if bgp_convergence is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = inspect.stack()[0][3] + write_test_header(tc_name) + reset_config_on_routers(tgen) + + # Create ip prefix list + input_dict_2 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + {"seqid": 10, "network": "any", "action": "permit"} + ] + }, + "ipv6": { + "pf_list_1_ipv6": [ + {"seqid": 100, "network": "any", "action": "permit"} + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Create route map + for addr_type in ADDR_TYPES: + input_dict_3 = { + "r3": { + "route_maps": { + "rmap_match_pf_1_{}".format(addr_type): [ + { + "action": "permit", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + "set": {"locPrf": 150, "weight": 100, "metric": 50}, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Configure neighbor for route map + input_dict_4 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + input_dict = topo["routers"] + for addr_type in ADDR_TYPES: + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verifying BGP set attributes + dut = "r3" + routes = { + "ipv4": ["10.0.20.1/32", "10.0.20.2/32"], + "ipv6": ["1::1/128", "1::2/128"], + } + + rmap_name = "rmap_match_pf_1" + for addr_type in ADDR_TYPES: + rmap_name = "rmap_match_pf_1_{}".format(addr_type) + result = verify_bgp_attributes( + tgen, addr_type, dut, routes[addr_type], rmap_name, input_dict_3 + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + # Uncomment next line for debugging + # tgen.mininet_cli() + + +def test_route_maps_with_continue_clause_p0(): + """ + TC_54: + Verify route-maps continue clause functionality. + """ + tgen = get_topogen() + global bgp_convergence + + if bgp_convergence is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = inspect.stack()[0][3] + write_test_header(tc_name) + reset_config_on_routers(tgen) + + # Create ip prefix list + input_dict_2 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + {"seqid": 10, "network": "any", "action": "permit"} + ] + }, + "ipv6": { + "pf_list_1_ipv6": [ + {"seqid": 100, "network": "any", "action": "permit"} + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Create route map + for addr_type in ADDR_TYPES: + input_dict_3 = { + "r3": { + "route_maps": { + "rmap_match_pf_1_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": "10", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + "set": {"locPrf": 150}, + "continue": "30", + }, + { + "action": "permit", + "seq_id": "20", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + "set": {"metric": 200}, + }, + { + "action": "permit", + "seq_id": "30", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + "set": {"metric": 100}, + }, + ] + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Configure neighbor for route map + input_dict_4 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + input_dict = topo["routers"] + for addr_type in ADDR_TYPES: + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verifying BGP set attributes + dut = "r3" + rmap_name = "rmap_match_pf_1" + routes = { + "ipv4": ["10.0.20.1/32", "10.0.20.2/32"], + "ipv6": ["1::1/128", "1::2/128"], + } + seq_id = {"ipv4": ["10", "30"], "ipv6": ["10", "30"]} + for addr_type in ADDR_TYPES: + rmap_name = "rmap_match_pf_1_{}".format(addr_type) + result = verify_bgp_attributes( + tgen, + addr_type, + dut, + routes[addr_type], + rmap_name, + input_dict_3, + seq_id[addr_type], + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + # Uncomment next line for debugging + # tgen.mininet_cli() + + +def test_route_maps_with_goto_clause_p0(): + """ + TC_55: + Verify route-maps goto clause functionality. + """ + tgen = get_topogen() + global bgp_convergence + + if bgp_convergence is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = inspect.stack()[0][3] + write_test_header(tc_name) + reset_config_on_routers(tgen) + + # Create ip prefix list + input_dict_2 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + {"seqid": 10, "network": "any", "action": "permit"} + ] + }, + "ipv6": { + "pf_list_1_ipv6": [ + {"seqid": 100, "network": "any", "action": "permit"} + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Create route map + for addr_type in ADDR_TYPES: + input_dict_3 = { + "r3": { + "route_maps": { + "rmap_match_pf_1_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": "10", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + "goto": "30", + }, + { + "action": "permit", + "seq_id": "20", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + "set": {"metric": 100}, + }, + { + "action": "permit", + "seq_id": "30", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + "set": {"metric": 200}, + }, + ] + } + } + } + result = create_route_maps(tgen, input_dict_3) + # tgen.mininet_cli() + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Configure neighbor for route map + input_dict_4 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + input_dict = topo["routers"] + for addr_type in ADDR_TYPES: + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verifying BGP set attributes + dut = "r3" + rmap_name = "rmap_match_pf_1" + routes = { + "ipv4": ["10.0.20.1/32", "10.0.20.2/32"], + "ipv6": ["1::1/128", "1::2/128"], + } + seq_id = {"ipv4": ["10", "30"], "ipv6": ["10", "30"]} + for addr_type in ADDR_TYPES: + rmap_name = "rmap_match_pf_1_{}".format(addr_type) + result = verify_bgp_attributes( + tgen, + addr_type, + dut, + routes[addr_type], + rmap_name, + input_dict_3, + seq_id[addr_type], + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + # Uncomment next line for debugging + # tgen.mininet_cli() + + +def test_route_maps_with_call_clause_p0(): + """ + TC_53: + Verify route-maps call clause functionality. + """ + tgen = get_topogen() + global bgp_convergence + + if bgp_convergence is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = inspect.stack()[0][3] + write_test_header(tc_name) + reset_config_on_routers(tgen) + + # Create ip prefix list + input_dict_2 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + {"seqid": 10, "network": "any", "action": "permit"} + ] + }, + "ipv6": { + "pf_list_1_ipv6": [ + {"seqid": 100, "network": "any", "action": "permit"} + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Create route map + for addr_type in ADDR_TYPES: + input_dict_3 = { + "r3": { + "route_maps": { + "rmap_match_pf_1_{}".format(addr_type): [ + { + "action": "permit", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + "set": {"locPrf": 150}, + "call": "rmap_match_pf_2_{}".format(addr_type), + } + ], + "rmap_match_pf_2_{}".format(addr_type): [ + { + "action": "permit", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + "set": {"metric": 200}, + } + ], + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Configure neighbor for route map + input_dict_4 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + input_dict = topo["routers"] + for addr_type in ADDR_TYPES: + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Verifying BGP set attributes + dut = "r3" + routes = { + "ipv4": ["10.0.20.1/32", "10.0.20.2/32"], + "ipv6": ["1::1/128", "1::2/128"], + } + rmap_name = "rmap_match_pf_1" + for addr_type in ADDR_TYPES: + rmap_name = "rmap_match_pf_1_{}".format(addr_type) + result = verify_bgp_attributes( + tgen, addr_type, dut, routes[addr_type], rmap_name, input_dict_3 + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + rmap_name = "rmap_match_pf_2" + for addr_type in ADDR_TYPES: + rmap_name = "rmap_match_pf_2_{}".format(addr_type) + result = verify_bgp_attributes( + tgen, addr_type, dut, routes[addr_type], rmap_name, input_dict_3 + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + # Uncomment next line for debugging + # tgen.mininet_cli() + + +def test_create_rmap_match_prefix_list_to_deny_in_and_outbound_prefixes_p0(): + """ + TC_58: + Create route map deny inbound and outbound prefixes on + match prefix list and set criteria on match + """ + tgen = get_topogen() + global bgp_convergence + + if bgp_convergence is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = inspect.stack()[0][3] + write_test_header(tc_name) + reset_config_on_routers(tgen) + + # Create ip prefix list + input_dict_2 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + {"seqid": 10, "network": "any", "action": "permit"} + ] + }, + "ipv6": { + "pf_list_1_ipv6": [ + {"seqid": 100, "network": "any", "action": "permit"} + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Create route map + for addr_type in ADDR_TYPES: + input_dict_3 = { + "r3": { + "route_maps": { + "rmap_match_pf_1_{}".format(addr_type): [ + { + "action": "deny", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + "set": {"locPrf": 150,}, + } + ], + "rmap_match_pf_2_{}".format(addr_type): [ + { + "action": "deny", + "match": { + addr_type: { + "prefix_lists": "pf_list_1_{}".format(addr_type) + } + }, + "set": {"metric": 50}, + } + ], + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Configure neighbor for route map + input_dict_4 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv4", + "direction": "in", + } + ] + } + } + }, + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_2_ipv6", + "direction": "out", + } + ] + } + } + }, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1_ipv4", + "direction": "in", + } + ] + } + } + }, + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_2_ipv6", + "direction": "out", + } + ] + } + } + }, + } + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + input_dict = topo["routers"] + for addr_type in ADDR_TYPES: + result = verify_rib( + tgen, addr_type, dut, input_dict, protocol=protocol, expected=False + ) + assert result is not True, "Testcase {} : Failed \n" + "routes are not present \n Error: {}".format(tc_name, result) + logger.info("Expected behaviour: {}".format(result)) + + # Verifying RIB routes + dut = "r4" + protocol = "bgp" + for addr_type in ADDR_TYPES: + result = verify_rib( + tgen, addr_type, dut, input_dict, protocol=protocol, expected=False + ) + assert result is not True, "Testcase {} : Failed \n" + "routes are not present \n Error: {}".format(tc_name, result) + logger.info("Expected behaviour: {}".format(result)) + + write_test_footer(tc_name) + + # Uncomment next line for debugging + # tgen.mininet_cli() + + +def test_create_rmap_to_match_tag_permit_inbound_prefixes_p0(): + """ + TC_59: + Create route map to permit inbound prefixes with filter + match tag and set criteria + """ + tgen = get_topogen() + global bgp_convergence + + if bgp_convergence is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = inspect.stack()[0][3] + write_test_header(tc_name) + reset_config_on_routers(tgen) + + for addr_type in ADDR_TYPES: + # Create Static routes + input_dict = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": "Null0", "tag": 4001} + ] + } + } + + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Api call to redistribute static routes + input_dict_1 = { + "r1": { + "bgp": { + "local_as": 100, + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + }, + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Create route map + input_dict_3 = { + "r1": { + "route_maps": { + "rmap_match_tag_1_{}".format(addr_type): [ + {"action": "permit", "match": {addr_type: {"tag": "4001"}}} + ] + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Configure neighbor for route map + input_dict_4 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1": { + "route_maps": [ + { + "name": "rmap_match_tag_1_ipv4", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1": { + "route_maps": [ + { + "name": "rmap_match_tag_1_ipv6", + "direction": "out", + } + ] + } + } + } + } + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + + for addr_type in ADDR_TYPES: + input_dict = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": "Null0", "tag": 4001} + ] + } + } + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + # Uncomment next line for debugging + # tgen.mininet_cli() + + +def test_create_rmap_to_match_tag_deny_outbound_prefixes_p0(): + """ + TC_60 + Create route map to deny outbound prefixes with filter match tag, + and set criteria + """ + tgen = get_topogen() + global bgp_convergence + + if bgp_convergence is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = inspect.stack()[0][3] + write_test_header(tc_name) + reset_config_on_routers(tgen) + + for addr_type in ADDR_TYPES: + # Create Static routes + input_dict = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": "Null0", "tag": 4001} + ] + } + } + + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Api call to redistribute static routes + input_dict_1 = { + "r1": { + "bgp": { + "local_as": 100, + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + }, + }, + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Create route map + input_dict_3 = { + "r1": { + "route_maps": { + "rmap_match_tag_1_{}".format(addr_type): [ + {"action": "deny", "match": {addr_type: {"tag": "4001"}}} + ] + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Configure neighbor for route map + input_dict_4 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1": { + "route_maps": [ + { + "name": "rmap_match_tag_1_ipv4", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1": { + "route_maps": [ + { + "name": "rmap_match_tag_1_ipv6", + "direction": "out", + } + ] + } + } + } + } + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Verifying RIB routes + dut = "r3" + protocol = "bgp" + + for addr_type in ADDR_TYPES: + input_dict = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": "Null0", "tag": 4001} + ] + } + } + result = verify_rib( + tgen, addr_type, dut, input_dict, protocol=protocol, expected=False + ) + assert result is not True, "Testcase {} : Failed \n" + "routes are denied \n Error: {}".format(tc_name, result) + logger.info("Expected behaviour: {}".format(result)) + + write_test_footer(tc_name) + + # Uncomment next line for debugging + # tgen.mininet_cli() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp-vrf-route-leak-basic/r1/bgpd.conf b/tests/topotests/bgp-vrf-route-leak-basic/r1/bgpd.conf index 626c268392..03dfbf9322 100644 --- a/tests/topotests/bgp-vrf-route-leak-basic/r1/bgpd.conf +++ b/tests/topotests/bgp-vrf-route-leak-basic/r1/bgpd.conf @@ -1,14 +1,16 @@ hostname r1 router bgp 99 vrf DONNA + no bgp ebgp-requires-policy address-family ipv4 unicast redistribute connected import vrf EVA ! ! router bgp 99 vrf EVA + no bgp ebgp-requires-policy address-family ipv4 unicast redistribute connected import vrf DONNA ! -! \ No newline at end of file +! diff --git a/tests/topotests/bgp-vrf-route-leak-basic/test_bgp-vrf-route-leak-basic.py b/tests/topotests/bgp-vrf-route-leak-basic/test_bgp-vrf-route-leak-basic.py new file mode 100755 index 0000000000..6178bfc63a --- /dev/null +++ b/tests/topotests/bgp-vrf-route-leak-basic/test_bgp-vrf-route-leak-basic.py @@ -0,0 +1,187 @@ +#!/usr/bin/env python + +# +# test_bgp-vrf-route-leak-basic.py +# +# Copyright (c) 2018 Cumulus Networks, Inc. +# Donald Sharp +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND Cumulus Networks DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_bgp-vrf-route-leak-basic.py.py: Test basic vrf route leaking +""" + +import json +import os +import sys +from functools import partial +import pytest + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +from mininet.topo import Topo + + +class BGPVRFTopo(Topo): + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + for routern in range(1, 2): + tgen.add_router("r{}".format(routern)) + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(BGPVRFTopo, mod.__name__) + tgen.start_topology() + + # For all registered routers, load the zebra configuration file + for rname, router in tgen.routers().iteritems(): + router.run("/bin/bash {}/setup_vrfs".format(CWD)) + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + # After loading the configurations, this function loads configured daemons. + tgen.start_router() + # tgen.mininet_cli() + + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + + +def test_vrf_route_leak(): + logger.info("Ensure that routes are leaked back and forth") + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + + # Test DONNA VRF. + expect = { + '10.0.0.0/24': [ + { + 'protocol': 'connected', + } + ], + '10.0.1.0/24': [ + { + 'protocol': 'bgp', + 'selected': True, + 'nexthops': [ + { + 'fib': True + } + ] + } + ], + '10.0.2.0/24': [ + { + 'protocol': 'connected' + } + ], + '10.0.3.0/24': [ + { + 'protocol': 'bgp', + 'selected': True, + 'nexthops': [ + { + 'fib': True + } + ] + } + ] + } + + test_func = partial( + topotest.router_json_cmp, r1, 'show ip route vrf DONNA json', expect + ) + result, diff = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert result, "BGP VRF DONNA check failed:\n{}".format(diff) + + # Test EVA VRF. + expect = { + '10.0.0.0/24': [ + { + 'protocol': 'bgp', + 'selected': True, + 'nexthops': [ + { + 'fib': True + } + ] + } + ], + '10.0.1.0/24': [ + { + 'protocol': 'connected', + } + ], + '10.0.2.0/24': [ + { + 'protocol': 'bgp', + 'selected': True, + 'nexthops': [ + { + 'fib': True + } + ] + } + ], + '10.0.3.0/24': [ + { + 'protocol': 'connected', + } + ] + } + + test_func = partial( + topotest.router_json_cmp, r1, 'show ip route vrf EVA json', expect + ) + result, diff = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert result, "BGP VRF EVA check failed:\n{}".format(diff) + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp-vrf-route-leak-basic/test_bgp.py b/tests/topotests/bgp-vrf-route-leak-basic/test_bgp.py deleted file mode 100644 index b0d60403db..0000000000 --- a/tests/topotests/bgp-vrf-route-leak-basic/test_bgp.py +++ /dev/null @@ -1,132 +0,0 @@ -#!/usr/bin/env python - -# -# test_bgp.py -# -# Copyright (c) 2018 Cumulus Networks, Inc. -# Donald Sharp -# -# Permission to use, copy, modify, and/or distribute this software -# for any purpose with or without fee is hereby granted, provided -# that the above copyright notice and this permission notice appear -# in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND Cumulus Networks DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY -# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, -# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS -# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE -# OF THIS SOFTWARE. -# - -""" -test_bgp.py: Test basic vrf route leaking -""" - -import json -import os -import sys -import pytest - -CWD = os.path.dirname(os.path.realpath(__file__)) -sys.path.append(os.path.join(CWD, '../')) - -# pylint: disable=C0413 -from lib import topotest -from lib.topogen import Topogen, TopoRouter, get_topogen -from lib.topolog import logger - -from mininet.topo import Topo - - -class BGPVRFTopo(Topo): - def build(self, *_args, **_opts): - "Build function" - tgen = get_topogen(self) - - for routern in range(1, 2): - tgen.add_router('r{}'.format(routern)) - -def setup_module(mod): - "Sets up the pytest environment" - tgen = Topogen(BGPVRFTopo, mod.__name__) - tgen.start_topology() - - # For all registered routers, load the zebra configuration file - for rname, router in tgen.routers().iteritems(): - router.run("/bin/bash {}/setup_vrfs".format(CWD)) - router.load_config( - TopoRouter.RD_ZEBRA, - os.path.join(CWD, '{}/zebra.conf'.format(rname)) - ) - router.load_config( - TopoRouter.RD_BGP, - os.path.join(CWD, '{}/bgpd.conf'.format(rname)) - ) - - # After loading the configurations, this function loads configured daemons. - tgen.start_router() - #tgen.mininet_cli() - -def teardown_module(mod): - "Teardown the pytest environment" - tgen = get_topogen() - - # This function tears down the whole topology. - tgen.stop_topology() - -def test_vrf_route_leak(): - logger.info("Ensure that routes are leaked back and forth") - tgen = get_topogen() - # Don't run this test if we have any failure. - if tgen.routers_have_failure(): - pytest.skip(tgen.errors) - - r1 = tgen.gears['r1'] - - donna = r1.vtysh_cmd("show ip route vrf DONNA json", isjson=True) - route0 = donna["10.0.0.0/24"][0] - assert route0['protocol'] == "connected" - route1 = donna["10.0.1.0/24"][0] - assert route1['protocol'] == "bgp" - assert route1['selected'] == True - nhop = route1['nexthops'][0] - assert nhop['fib'] == True - route2 = donna["10.0.2.0/24"][0] - assert route2['protocol'] == "connected" - route3 = donna["10.0.3.0/24"][0] - assert route3['protocol'] == "bgp" - assert route3['selected'] == True - nhop = route3['nexthops'][0] - assert nhop['fib'] == True - eva = r1.vtysh_cmd("show ip route vrf EVA json", isjson=True) - route0 = eva["10.0.0.0/24"][0] - assert route0['protocol'] == "bgp" - assert route0['selected'] == True - nhop = route0['nexthops'][0] - assert nhop['fib'] == True - route1 = eva["10.0.1.0/24"][0] - assert route1['protocol'] == "connected" - route2 = eva["10.0.2.0/24"][0] - assert route2['protocol'] == "bgp" - assert route2['selected'] == True - nhop = route2['nexthops'][0] - assert nhop['fib'] == True - route3 = eva["10.0.3.0/24"][0] - assert route3['protocol'] == "connected" - #tgen.mininet_cli() - -def test_memory_leak(): - "Run the memory leak test and report results." - tgen = get_topogen() - if not tgen.is_memleak_enabled(): - pytest.skip('Memory leak test/report is disabled') - - tgen.report_memory_leaks() - - -if __name__ == '__main__': - args = ["-s"] + sys.argv[1:] - sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_aggregate-address_origin/__init__.py b/tests/topotests/bgp_aggregate-address_origin/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bgp_aggregate-address_origin/r1/bgpd.conf b/tests/topotests/bgp_aggregate-address_origin/r1/bgpd.conf new file mode 100644 index 0000000000..9d519fae88 --- /dev/null +++ b/tests/topotests/bgp_aggregate-address_origin/r1/bgpd.conf @@ -0,0 +1,8 @@ +router bgp 65000 + no bgp ebgp-requires-policy + neighbor 192.168.255.2 remote-as 65001 + address-family ipv4 unicast + redistribute connected + aggregate-address 172.16.255.0/24 origin igp + exit-address-family +! diff --git a/tests/topotests/bgp_aggregate-address_origin/r1/zebra.conf b/tests/topotests/bgp_aggregate-address_origin/r1/zebra.conf new file mode 100644 index 0000000000..0a283c06d5 --- /dev/null +++ b/tests/topotests/bgp_aggregate-address_origin/r1/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 172.16.255.254/32 +! +interface r1-eth0 + ip address 192.168.255.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_aggregate-address_origin/r2/bgpd.conf b/tests/topotests/bgp_aggregate-address_origin/r2/bgpd.conf new file mode 100644 index 0000000000..38cf5aaca7 --- /dev/null +++ b/tests/topotests/bgp_aggregate-address_origin/r2/bgpd.conf @@ -0,0 +1,5 @@ +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as 65000 + exit-address-family +! diff --git a/tests/topotests/bgp_aggregate-address_origin/r2/zebra.conf b/tests/topotests/bgp_aggregate-address_origin/r2/zebra.conf new file mode 100644 index 0000000000..606c17bec9 --- /dev/null +++ b/tests/topotests/bgp_aggregate-address_origin/r2/zebra.conf @@ -0,0 +1,6 @@ +! +interface r2-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_aggregate-address_origin/test_bgp_aggregate-address_origin.py b/tests/topotests/bgp_aggregate-address_origin/test_bgp_aggregate-address_origin.py new file mode 100644 index 0000000000..fa799f8256 --- /dev/null +++ b/tests/topotests/bgp_aggregate-address_origin/test_bgp_aggregate-address_origin.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python + +# +# bgp_aggregate-address_origin.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2020 by +# Donatas Abraitis +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +bgp_aggregate-address_origin.py: + +Test if works the following commands: +router bgp 65031 + address-family ipv4 unicast + aggregate-address 192.168.255.0/24 origin igp +""" + +import os +import sys +import json +import time +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from mininet.topo import Topo + + +class TemplateTopo(Topo): + def build(self, *_args, **_opts): + tgen = get_topogen(self) + + for routern in range(1, 3): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + tgen = Topogen(TemplateTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.iteritems(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_aggregate_address_origin(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r2"] + + def _bgp_converge(router): + output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json")) + expected = { + "192.168.255.1": { + "bgpState": "Established", + "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 3}}, + } + } + return topotest.json_cmp(output, expected) + + def _bgp_aggregate_address_has_metric(router): + output = json.loads(router.vtysh_cmd("show ip bgp 172.16.255.0/24 json")) + expected = {"paths": [{"origin": "IGP"}]} + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge, router) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + + assert result is None, 'Failed to see bgp convergence in "{}"'.format(router) + + test_func = functools.partial(_bgp_aggregate_address_has_metric, router) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + + assert ( + result is None + ), 'Failed to see applied ORIGIN (igp) for aggregated prefix in "{}"'.format(router) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_aggregate-address_route-map/__init__.py b/tests/topotests/bgp_aggregate-address_route-map/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bgp_aggregate-address_route-map/r1/bgpd.conf b/tests/topotests/bgp_aggregate-address_route-map/r1/bgpd.conf new file mode 100644 index 0000000000..292f0e967f --- /dev/null +++ b/tests/topotests/bgp_aggregate-address_route-map/r1/bgpd.conf @@ -0,0 +1,11 @@ +router bgp 65000 + no bgp ebgp-requires-policy + neighbor 192.168.255.2 remote-as 65001 + address-family ipv4 unicast + redistribute connected + aggregate-address 172.16.255.0/24 route-map aggr-rmap + exit-address-family +! +route-map aggr-rmap permit 10 + set metric 123 +! diff --git a/tests/topotests/bgp_aggregate-address_route-map/r1/zebra.conf b/tests/topotests/bgp_aggregate-address_route-map/r1/zebra.conf new file mode 100644 index 0000000000..0a283c06d5 --- /dev/null +++ b/tests/topotests/bgp_aggregate-address_route-map/r1/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 172.16.255.254/32 +! +interface r1-eth0 + ip address 192.168.255.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_aggregate-address_route-map/r2/bgpd.conf b/tests/topotests/bgp_aggregate-address_route-map/r2/bgpd.conf new file mode 100644 index 0000000000..38cf5aaca7 --- /dev/null +++ b/tests/topotests/bgp_aggregate-address_route-map/r2/bgpd.conf @@ -0,0 +1,5 @@ +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as 65000 + exit-address-family +! diff --git a/tests/topotests/bgp_aggregate-address_route-map/r2/zebra.conf b/tests/topotests/bgp_aggregate-address_route-map/r2/zebra.conf new file mode 100644 index 0000000000..606c17bec9 --- /dev/null +++ b/tests/topotests/bgp_aggregate-address_route-map/r2/zebra.conf @@ -0,0 +1,6 @@ +! +interface r2-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_aggregate-address_route-map/test_bgp_aggregate-address_route-map.py b/tests/topotests/bgp_aggregate-address_route-map/test_bgp_aggregate-address_route-map.py new file mode 100644 index 0000000000..9c06c9d382 --- /dev/null +++ b/tests/topotests/bgp_aggregate-address_route-map/test_bgp_aggregate-address_route-map.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python + +# +# bgp_aggregate-address_route-map.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2019 by +# Network Device Education Foundation, Inc. ("NetDEF") +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +bgp_aggregate-address_route-map.py: + +Test if works the following commands: +router bgp 65031 + address-family ipv4 unicast + aggregate-address 192.168.255.0/24 route-map aggr-rmap + +route-map aggr-rmap permit 10 + set metric 123 +""" + +import os +import sys +import json +import time +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from mininet.topo import Topo + + +class TemplateTopo(Topo): + def build(self, *_args, **_opts): + tgen = get_topogen(self) + + for routern in range(1, 3): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + tgen = Topogen(TemplateTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.iteritems(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_maximum_prefix_invalid(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r2"] + + def _bgp_converge(router): + output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json")) + expected = { + "192.168.255.1": { + "bgpState": "Established", + "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 3}}, + } + } + return topotest.json_cmp(output, expected) + + def _bgp_aggregate_address_has_metric(router): + output = json.loads(router.vtysh_cmd("show ip bgp 172.16.255.0/24 json")) + expected = {"paths": [{"metric": 123}]} + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge, router) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + + assert result is None, 'Failed to see bgp convergence in "{}"'.format(router) + + test_func = functools.partial(_bgp_aggregate_address_has_metric, router) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + + assert ( + result is None + ), 'Failed to see applied metric for aggregated prefix in "{}"'.format(router) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_as_allow_in/bgp_as_allow_in.json b/tests/topotests/bgp_as_allow_in/bgp_as_allow_in.json new file mode 100644 index 0000000000..943876cdac --- /dev/null +++ b/tests/topotests/bgp_as_allow_in/bgp_as_allow_in.json @@ -0,0 +1,266 @@ +{ + "address_types": [ + "ipv4", + "ipv6" + ], + "ipv4base": "10.0.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 30, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:db8:f::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "200", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {} + } + } + } + } + } + } + } + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r3": { + "dest_link": { + "r2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r3": { + "dest_link": { + "r2": {} + } + } + } + } + } + } + } + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r5": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "200", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r5": { + "dest_link": { + "r3": {} + } + }, + "r2": { + "dest_link": { + "r3": {} + } + }, + "r4": { + "dest_link": { + "r3": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r5": { + "dest_link": { + "r3": {} + } + }, + "r2": { + "dest_link": { + "r3": {} + } + }, + "r4": { + "dest_link": { + "r3": {} + } + } + } + } + } + } + } + }, + "r4": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "200", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": {} + } + } + } + } + } + } + } + }, + "r5": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "500", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r5": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r5": {} + } + } + } + } + } + } + } + } + } +} \ No newline at end of file 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 new file mode 100755 index 0000000000..f9d22a3a36 --- /dev/null +++ b/tests/topotests/bgp_as_allow_in/test_bgp_as_allow_in.py @@ -0,0 +1,981 @@ +#!/usr/bin/python + +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +Following tests are covered to test bgp allowas-in functionality: + +- Verify that routes coming from same AS are accepted only when + '"allowas-in" is configuerd. +- Verify that "allowas-in" feature works per address-family/VRF + 'basis and doesn't impact the other AFIs. +- Verify that the if number of occurrences of AS number in path is + 'more than the configured allowas-in value then we do not accept + 'the route. +- Verify that when we advertise a network, learned from the same AS + 'via allowas-in command, to an iBGP neighbor we see multiple + 'occurrences. +- Verify that when we advertise a network, learned from the same AS + 'via allowas-in command, to an eBGP neighbor we see multiple + 'occurrences of our own AS based on configured value+1. +""" + +import os +import sys +import time +import json +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from mininet.topo import Topo +from lib.topogen import Topogen, get_topogen + +# Import topoJson from lib, to create topology and initial configuration +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + reset_config_on_routers, + verify_rib, + create_static_routes, + create_route_maps, + check_address_types, + step, + required_linux_kernel_version +) +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + create_router_bgp, + clear_bgp_and_verify, + verify_bgp_rib, +) +from lib.topojson import build_topo_from_json, build_config_from_json + +# Reading the data from JSON File for topology creation +jsonFile = "{}/bgp_as_allow_in.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +# Global variables +BGP_CONVERGENCE = False +ADDR_TYPES = check_address_types() +NETWORK = {"ipv4": "2.2.2.2/32", "ipv6": "22:22::2/128"} +NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"} + + +class BGPALLOWASIN(Topo): + """ + Test BGPALLOWASIN - topology 1 + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + """Build function""" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version('4.15') + if result is not True: + pytest.skip("Kernel requirements are not met") + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(BGPALLOWASIN, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Checking BGP convergence + global BGP_CONVERGENCE + global ADDR_TYPES + + # Api call verify whether BGP is converged + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error:" " {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """ + Teardown the pytest environment + + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# +# Tests starting +# +##################################################### + + +def test_bgp_allowas_in_p0(request): + """ + Verify that routes coming from same AS are accepted only when + "allowas-in" is configuerd. + + """ + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + reset_config_on_routers(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Advertise prefix 2.2.2.2/32 from Router-1(AS-200).") + step("Advertise an ipv6 prefix 22:22::2/128 from Router-1(AS-200).") + # configure static routes + dut = "r3" + protocol = "bgp" + + for addr_type in ADDR_TYPES: + # Enable static routes + input_dict_4 = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R1") + + input_dict_2 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + step( + 'Check BGP table of router R3 using "sh bgp ipv4" and "sh bgp ' + 'ipv6" command.' + ) + step( + "We should not see prefix advertised from R1 in R3's BGP " + "table without allowas-in." + ) + logger.info("Verifying %s routes on r3, route should not be present", addr_type) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=NEXT_HOP_IP[addr_type], + protocol=protocol, + expected=False, + ) + assert result is not True, "Testcase {} : Failed \n" + "Expected behavior: routes should not present in rib \n" + "Error: {}".format(tc_name, result) + + step("Configure allowas-in on R3 for R2.") + step("We should see the prefix advertised from R1 in R3's BGP table.") + # Api call to enable allowas-in in bgp process. + input_dict_1 = { + "r3": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": { + "allowas-in": {"number_occurences": 1} + } + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + result = verify_rib(tgen, addr_type, dut, input_dict_4, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_bgp_allowas_in_per_addr_family_p0(request): + """ + Verify that "allowas-in" feature works per address-family/VRF + basis and doesn't impact the other AFIs. + + """ + + # This test is applicable only for dual stack. + if "ipv4" not in ADDR_TYPES or "ipv6" not in ADDR_TYPES: + pytest.skip("NOT APPLICABLE") + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + reset_config_on_routers(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Advertise prefix 2.2.2.2/32 from Router-1(AS-200).") + step("Advertise an ipv6 prefix 22:22::2/128 from Router-1(AS-200).") + # configure static routes routes + dut = "r3" + protocol = "bgp" + + for addr_type in ADDR_TYPES: + # Enable static routes + input_dict_4 = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R1") + + input_dict_2 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure allowas-in on R3 for R2 under IPv4 addr-family only") + # Api call to enable allowas-in in bgp process. + input_dict_1 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": {"allowas-in": {"number_occurences": 1}} + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + static_route_ipv4 = { + "r1": { + "static_routes": [ + {"network": NETWORK["ipv4"], "next_hop": NEXT_HOP_IP["ipv4"]} + ] + } + } + + static_route_ipv6 = { + "r1": { + "static_routes": [ + {"network": NETWORK["ipv6"], "next_hop": NEXT_HOP_IP["ipv6"]} + ] + } + } + step("We should see R1 advertised prefix only in IPv4 AFI " "not in IPv6 AFI.") + result = verify_rib(tgen, "ipv4", dut, static_route_ipv4, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + result = verify_rib( + tgen, "ipv6", dut, static_route_ipv6, protocol=protocol, expected=False + ) + assert result is not True, "Testcase {} : Failed \n" + "Expected behavior: routes are should not be present in ipv6 rib\n" + " Error: {}".format(tc_name, result) + + step("Repeat the same test for IPv6 AFI.") + step("Configure allowas-in on R3 for R2 under IPv6 addr-family only") + # Api call to enable allowas-in in bgp process. + input_dict_1 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": { + "allowas-in": { + "number_occurences": 2, + "delete": True, + } + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": {"allowas-in": {"number_occurences": 2}} + } + } + } + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + step("We should see R1 advertised prefix only in IPv6 AFI " "not in IPv4 AFI.") + result = verify_rib( + tgen, "ipv4", dut, static_route_ipv4, protocol=protocol, expected=False + ) + assert result is not True, "Testcase {} : Failed \n" + "Expected behavior: routes should not be present in ipv4 rib\n" + " Error: {}".format(tc_name, result) + result = verify_rib(tgen, "ipv6", dut, static_route_ipv6, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_bgp_allowas_in_no_of_occurrences_p0(request): + """ + Verify that the if number of occurrences of AS number in path is + more than the configured allowas-in value then we do not accept + the route. + + """ + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + reset_config_on_routers(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + dut = "r3" + protocol = "bgp" + + for addr_type in ADDR_TYPES: + # Enable static routes + static_routes = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, static_routes) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R1") + + input_dict_2 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure a route-map on R1 to prepend AS 4 times.") + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r1": { + "route_maps": { + "ASP_{}".format(addr_type): [ + { + "action": "permit", + "set": { + "path": { + "as_num": "200 200 200 200", + "as_action": "prepend", + } + }, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure route map in out direction on R1") + # Configure neighbor for route map + input_dict_7 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": { + "route_maps": [ + { + "name": "ASP_{}".format( + addr_type + ), + "direction": "out", + } + ] + } + } + } + } + } + } + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_7) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + step('Configure "allowas-in 4" on R3 for R2.') + # Api call to enable allowas-in in bgp process. + input_dict_1 = { + "r3": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": { + "allowas-in": {"number_occurences": 4} + } + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + result = verify_rib( + tgen, addr_type, dut, static_routes, protocol=protocol, expected=False + ) + assert result is not True, "Testcase {} : Failed \n " + "Expected behavior: routes are should not be present in rib\n" + "Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + step('Configure "allowas-in 5" on R3 for R2.') + input_dict_1 = { + "r3": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": { + "allowas-in": {"number_occurences": 5} + } + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + static_routes = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + result = verify_rib(tgen, addr_type, dut, static_routes, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_bgp_allowas_in_sameastoibgp_p1(request): + """ + Verify that when we advertise a network, learned from the same AS + via allowas-in command, to an iBGP neighbor we see multiple + occurrences. + + """ + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + reset_config_on_routers(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + dut = "r3" + protocol = "bgp" + + for addr_type in ADDR_TYPES: + # Enable static routes + static_routes = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, static_routes) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R1") + + input_dict_2 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure a route-map on R2 to prepend AS 2 times.") + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r2": { + "route_maps": { + "ASP_{}".format(addr_type): [ + { + "action": "permit", + "set": { + "path": {"as_num": "200 200", "as_action": "prepend"} + }, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure route map in out direction on R2") + # Configure neighbor for route map + input_dict_7 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2": { + "route_maps": [ + { + "name": "ASP_{}".format( + addr_type + ), + "direction": "out", + } + ] + } + } + } + } + } + } + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_7) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step('Configure "allowas-in 3" on R3 for R1.') + input_dict_1 = { + "r3": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": { + "allowas-in": {"number_occurences": 3} + } + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_1 = { + "r4": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": { + "allowas-in": {"number_occurences": 3} + } + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + static_routes = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + dut = "r4" + path = "100 200 200 200" + result = verify_bgp_rib(tgen, addr_type, dut, static_routes, aspath=path) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_bgp_allowas_in_sameastoebgp_p1(request): + """ + Verify that when we advertise a network, learned from the same AS + via allowas-in command, to an eBGP neighbor we see multiple + occurrences of our own AS based on configured value+1. + + """ + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + reset_config_on_routers(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + dut = "r3" + protocol = "bgp" + + for addr_type in ADDR_TYPES: + # Enable static routes + static_routes = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, static_routes) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R1") + + input_dict_2 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure a route-map on R2 to prepend AS 2 times.") + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r2": { + "route_maps": { + "ASP_{}".format(addr_type): [ + { + "action": "permit", + "set": { + "path": {"as_num": "200 200", "as_action": "prepend"} + }, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure route map in out direction on R2") + # Configure neighbor for route map + input_dict_7 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2": { + "route_maps": [ + { + "name": "ASP_{}".format( + addr_type + ), + "direction": "out", + } + ] + } + } + } + } + } + } + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_7) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + step('Configure "allowas-in 3" on R3 for R1.') + input_dict_1 = { + "r3": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": { + "allowas-in": {"number_occurences": 3} + } + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + static_routes = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + dut = "r5" + path = "200 100 200 200 200" + result = verify_bgp_rib(tgen, addr_type, dut, static_routes, aspath=path) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_as_wide_bgp_identifier/__init__.py b/tests/topotests/bgp_as_wide_bgp_identifier/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bgp_as_wide_bgp_identifier/r1/bgpd.conf b/tests/topotests/bgp_as_wide_bgp_identifier/r1/bgpd.conf new file mode 100644 index 0000000000..75741a3c3e --- /dev/null +++ b/tests/topotests/bgp_as_wide_bgp_identifier/r1/bgpd.conf @@ -0,0 +1,6 @@ +! exit1 +router bgp 65001 + bgp router-id 10.10.10.10 + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as 65002 +! diff --git a/tests/topotests/bgp_as_wide_bgp_identifier/r1/zebra.conf b/tests/topotests/bgp_as_wide_bgp_identifier/r1/zebra.conf new file mode 100644 index 0000000000..c060e1402e --- /dev/null +++ b/tests/topotests/bgp_as_wide_bgp_identifier/r1/zebra.conf @@ -0,0 +1,6 @@ +! exit1 +interface r1-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_as_wide_bgp_identifier/r2/bgpd.conf b/tests/topotests/bgp_as_wide_bgp_identifier/r2/bgpd.conf new file mode 100644 index 0000000000..18a6c66f69 --- /dev/null +++ b/tests/topotests/bgp_as_wide_bgp_identifier/r2/bgpd.conf @@ -0,0 +1,7 @@ +! spine +router bgp 65002 + bgp router-id 10.10.10.10 + no bgp ebgp-requires-policy + neighbor 192.168.255.2 remote-as 65001 + neighbor 192.168.255.3 remote-as 65002 +! diff --git a/tests/topotests/bgp_as_wide_bgp_identifier/r2/zebra.conf b/tests/topotests/bgp_as_wide_bgp_identifier/r2/zebra.conf new file mode 100644 index 0000000000..a45520f97f --- /dev/null +++ b/tests/topotests/bgp_as_wide_bgp_identifier/r2/zebra.conf @@ -0,0 +1,6 @@ +! spine +interface r2-eth0 + ip address 192.168.255.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_as_wide_bgp_identifier/r3/bgpd.conf b/tests/topotests/bgp_as_wide_bgp_identifier/r3/bgpd.conf new file mode 100644 index 0000000000..27bf126000 --- /dev/null +++ b/tests/topotests/bgp_as_wide_bgp_identifier/r3/bgpd.conf @@ -0,0 +1,6 @@ +! exit2 +router bgp 65002 + bgp router-id 10.10.10.10 + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as 65002 +! diff --git a/tests/topotests/bgp_as_wide_bgp_identifier/r3/zebra.conf b/tests/topotests/bgp_as_wide_bgp_identifier/r3/zebra.conf new file mode 100644 index 0000000000..2f4dbc5efd --- /dev/null +++ b/tests/topotests/bgp_as_wide_bgp_identifier/r3/zebra.conf @@ -0,0 +1,6 @@ +! exit2 +interface r3-eth0 + ip address 192.168.255.3/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_as_wide_bgp_identifier/test_bgp_as_wide_bgp_identifier.py b/tests/topotests/bgp_as_wide_bgp_identifier/test_bgp_as_wide_bgp_identifier.py new file mode 100644 index 0000000000..459af486ff --- /dev/null +++ b/tests/topotests/bgp_as_wide_bgp_identifier/test_bgp_as_wide_bgp_identifier.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python + +# +# test_bgp_as_wide_bgp_identifier.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2020 by +# Donatas Abraitis +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +rfc6286: Autonomous-System-Wide Unique BGP Identifier for BGP-4 +Test if 'Bad BGP Identifier' notification is sent only to +internal peers (autonomous-system-wide). eBGP peers are not +affected and should work. +""" + +import os +import sys +import json +import time +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from mininet.topo import Topo + + +class TemplateTopo(Topo): + def build(self, *_args, **_opts): + tgen = get_topogen(self) + + for routern in range(1, 4): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + +def setup_module(mod): + tgen = Topogen(TemplateTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.iteritems(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_as_wide_bgp_identifier(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def _bgp_converge(router): + output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json")) + expected = {"192.168.255.1": {"bgpState": "Established"}} + return topotest.json_cmp(output, expected) + + def _bgp_failed(router): + output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json")) + expected = { + "192.168.255.1": { + "lastNotificationReason": "OPEN Message Error/Bad BGP Identifier" + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge, tgen.gears["r1"]) + success, result = topotest.run_and_expect(test_func, None, count=260, wait=0.5) + + assert result is None, 'Failed to converge: "{}"'.format(tgen.gears["r1"]) + + test_func = functools.partial(_bgp_failed, tgen.gears["r3"]) + success, result = topotest.run_and_expect(test_func, None, count=260, wait=0.5) + + assert result is None, 'Bad BGP Identifier notification not sent: "{}"'.format( + tgen.gears["r3"] + ) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_comm-list_delete/__init__.py b/tests/topotests/bgp_comm-list_delete/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bgp_comm-list_delete/r1/bgpd.conf b/tests/topotests/bgp_comm-list_delete/r1/bgpd.conf new file mode 100644 index 0000000000..9518894351 --- /dev/null +++ b/tests/topotests/bgp_comm-list_delete/r1/bgpd.conf @@ -0,0 +1,10 @@ +router bgp 65000 + no bgp ebgp-requires-policy + neighbor 192.168.255.2 remote-as 65001 + address-family ipv4 unicast + redistribute connected route-map r2-out + exit-address-family +! +route-map r2-out permit 10 + set community 111:111 222:222 333:333 444:444 +! diff --git a/tests/topotests/bgp_comm-list_delete/r1/zebra.conf b/tests/topotests/bgp_comm-list_delete/r1/zebra.conf new file mode 100644 index 0000000000..0a283c06d5 --- /dev/null +++ b/tests/topotests/bgp_comm-list_delete/r1/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 172.16.255.254/32 +! +interface r1-eth0 + ip address 192.168.255.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_comm-list_delete/r2/bgpd.conf b/tests/topotests/bgp_comm-list_delete/r2/bgpd.conf new file mode 100644 index 0000000000..e4c1167745 --- /dev/null +++ b/tests/topotests/bgp_comm-list_delete/r2/bgpd.conf @@ -0,0 +1,12 @@ +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as 65000 + address-family ipv4 + neighbor 192.168.255.1 route-map r1-in in + exit-address-family +! +bgp community-list standard r1 permit 333:333 +! +route-map r1-in permit 10 + set comm-list r1 delete +! diff --git a/tests/topotests/bgp_comm-list_delete/r2/zebra.conf b/tests/topotests/bgp_comm-list_delete/r2/zebra.conf new file mode 100644 index 0000000000..606c17bec9 --- /dev/null +++ b/tests/topotests/bgp_comm-list_delete/r2/zebra.conf @@ -0,0 +1,6 @@ +! +interface r2-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_comm-list_delete/test_bgp_comm-list_delete.py b/tests/topotests/bgp_comm-list_delete/test_bgp_comm-list_delete.py new file mode 100644 index 0000000000..314ad12a6d --- /dev/null +++ b/tests/topotests/bgp_comm-list_delete/test_bgp_comm-list_delete.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python + +# +# bgp_comm-list_delete.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2019 by +# Network Device Education Foundation, Inc. ("NetDEF") +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +bgp_comm-list_delete.py: + +Test if works the following commands: +route-map test permit 10 + set comm-list delete +""" + +import os +import sys +import json +import time +import pytest + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from mininet.topo import Topo + + +class TemplateTopo(Topo): + def build(self, *_args, **_opts): + tgen = get_topogen(self) + + for routern in range(1, 3): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + tgen = Topogen(TemplateTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.iteritems(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_maximum_prefix_invalid(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def _bgp_converge(router): + while True: + output = json.loads( + tgen.gears[router].vtysh_cmd("show ip bgp neighbor 192.168.255.1 json") + ) + if output["192.168.255.1"]["bgpState"] == "Established": + if ( + output["192.168.255.1"]["addressFamilyInfo"]["ipv4Unicast"][ + "acceptedPrefixCounter" + ] + == 2 + ): + return True + + def _bgp_comm_list_delete(router): + output = json.loads( + tgen.gears[router].vtysh_cmd("show ip bgp 172.16.255.254/32 json") + ) + if "333:333" in output["paths"][0]["community"]["list"]: + return False + return True + + if _bgp_converge("r2"): + assert _bgp_comm_list_delete("r2") == True + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_communities_topo1/bgp_communities.json b/tests/topotests/bgp_communities_topo1/bgp_communities.json new file mode 100644 index 0000000000..da6aec239f --- /dev/null +++ b/tests/topotests/bgp_communities_topo1/bgp_communities.json @@ -0,0 +1,175 @@ +{ + "address_types": [ + "ipv4", + "ipv6" + ], + "ipv4base": "10.0.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 30, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:db8:f::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r0": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {} + } + } + } + } + } + } + } + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "200", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r3": { + "dest_link": { + "r2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r3": { + "dest_link": { + "r2": {} + } + } + } + } + } + } + } + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": {} + } + } + } + } + } + } + } + }, + "r0": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1": { + "ipv4": "auto", + "ipv6": "auto" + } + } + } + } +} \ No newline at end of file diff --git a/tests/topotests/bgp_communities_topo1/test_bgp_communities.py b/tests/topotests/bgp_communities_topo1/test_bgp_communities.py new file mode 100644 index 0000000000..57e8e0d34a --- /dev/null +++ b/tests/topotests/bgp_communities_topo1/test_bgp_communities.py @@ -0,0 +1,641 @@ +#!/usr/bin/python + +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +Following tests are covered to test bgp community functionality: +- Verify routes are not advertised when NO-ADVERTISE Community is applied + +""" + +import os +import sys +import time +import json +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from mininet.topo import Topo +from lib.topogen import Topogen, get_topogen + +# Import topoJson from lib, to create topology and initial configuration +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + reset_config_on_routers, + verify_rib, + create_static_routes, + check_address_types, + step, + create_route_maps, + create_prefix_lists, + create_route_maps, + required_linux_kernel_version +) +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + create_router_bgp, + clear_bgp_and_verify, + verify_bgp_rib, +) +from lib.topojson import build_topo_from_json, build_config_from_json +from copy import deepcopy + +# Reading the data from JSON File for topology creation +jsonFile = "{}/bgp_communities.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +# Global variables +BGP_CONVERGENCE = False +ADDR_TYPES = check_address_types() +NETWORK = {"ipv4": "2.2.2.2/32", "ipv6": "22:22::2/128"} +NEXT_HOP_IP = {} + + +class BGPCOMMUNITIES(Topo): + """ + Test BGPCOMMUNITIES - topology 1 + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + """Build function""" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version('4.15') + if result is not True: + pytest.skip("Kernel requirements are not met") + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(BGPCOMMUNITIES, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Checking BGP convergence + global BGP_CONVERGENCE + global ADDR_TYPES + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Api call verify whether BGP is converged + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error:" " {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """ + Teardown the pytest environment + + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# +# Tests starting +# +##################################################### + + +def test_bgp_no_advertise_community_p0(request): + """ + Verify routes are not advertised when NO-ADVERTISE Community is applied + + """ + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + reset_config_on_routers(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + NEXT_HOP_IP = { + "ipv4": topo["routers"]["r0"]["links"]["r1"]["ipv4"].split("/")[0], + "ipv6": topo["routers"]["r0"]["links"]["r1"]["ipv6"].split("/")[0], + } + + # configure static routes + dut = "r3" + protocol = "bgp" + + for addr_type in ADDR_TYPES: + # Enable static routes + input_dict = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static and connected in Router BGP " "in R1") + + input_dict_2 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "BGP neighbors are up, static and connected route advertised from" + " R1 are present on R2 BGP table and RIB using show ip bgp and " + " show ip route" + ) + step( + "Static and connected route advertised from R1 are present on R3" + " BGP table and RIB using show ip bgp and show ip route" + ) + + dut = "r3" + protocol = "bgp" + result = verify_bgp_rib(tgen, addr_type, dut, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure prefix list P1 on R2 to permit route coming from R1") + # Create ip prefix list + input_dict_2 = { + "r2": { + "prefix_lists": { + addr_type: { + "pf_list_1_{}".format(addr_type): [ + {"seqid": 10, "network": "any", "action": "permit"} + ] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Create route map + input_dict_3 = { + "r2": { + "route_maps": { + "rmap_match_pf_1_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": "5", + "match": { + addr_type: {"prefix_lists": "pf_list_1_" + addr_type} + }, + "set": {"community": {"num": "no-advertise"}}, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + step( + "Apply route-map RM1 on R2, R2 to R3 BGP neighbor with no" + " advertise community" + ) + # Configure neighbor for route map + input_dict_4 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": { + "route_maps": [ + { + "name": "rmap_match_pf_1_" + + addr_type, + "direction": "in", + } + ] + } + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "After advertising no advertise community to BGP neighbor " + "static and connected router got removed from R3 verify using " + "show ip bgp & show ip route" + ) + + result = verify_bgp_rib(tgen, addr_type, dut, input_dict, expected=False) + assert result is not True, "Testcase {} : Failed \n " + " Routes still present in R3 router. Error: {}".format(tc_name, result) + + result = verify_rib( + tgen, addr_type, dut, input_dict, protocol=protocol, expected=False + ) + assert result is not True, "Testcase {} : Failed \n " + " Routes still present in R3 router. Error: {}".format(tc_name, result) + + step("Remove and Add no advertise community") + # Configure neighbor for route map + input_dict_4 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": { + "route_maps": [ + { + "name": "rmap_match_pf_1_" + + addr_type, + "direction": "in", + "delete": True, + } + ] + } + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "After removing no advertise community from BGP neighbor " + "static and connected router got advertised to R3 and " + "removing route-map, verify route using show ip bgp" + " and show ip route" + ) + + result = verify_bgp_rib(tgen, addr_type, dut, input_dict) + assert result is True, "Testcase {} : Failed \n " + " Routes still present in R3 router. Error: {}".format(tc_name, result) + + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n " + " Routes still present in R3 router. Error: {}".format(tc_name, result) + + step("Repeat above steps when IBGP nbr configured between R1, R2 & R2, R3") + topo1 = deepcopy(topo) + + topo1["routers"]["r1"]["bgp"]["local_as"] = "100" + topo1["routers"]["r2"]["bgp"]["local_as"] = "100" + topo1["routers"]["r3"]["bgp"]["local_as"] = "100" + + for rtr in ["r1", "r2", "r3"]: + if "bgp" in topo1["routers"][rtr].keys(): + delete_bgp = {rtr: {"bgp": {"delete": True}}} + result = create_router_bgp(tgen, topo1, delete_bgp) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + config_bgp = { + rtr: {"bgp": {"local_as": topo1["routers"][rtr]["bgp"]["local_as"]}} + } + result = create_router_bgp(tgen, topo1, config_bgp) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + build_config_from_json(tgen, topo1, save_bkup=False) + + step("verify bgp convergence before starting test case") + + bgp_convergence = verify_bgp_convergence(tgen, topo1) + assert bgp_convergence is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, bgp_convergence + ) + + # configure static routes + dut = "r3" + protocol = "bgp" + + for addr_type in ADDR_TYPES: + # Enable static routes + input_dict = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static and connected in Router " "BGP in R1") + + input_dict_2 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "BGP neighbors are up, static and connected route advertised from" + " R1 are present on R2 BGP table and RIB using show ip bgp and " + " show ip route" + ) + step( + "Static and connected route advertised from R1 are present on R3" + " BGP table and RIB using show ip bgp and show ip route" + ) + + dut = "r2" + protocol = "bgp" + result = verify_bgp_rib(tgen, addr_type, dut, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure prefix list P1 on R2 to permit route coming from R1") + # Create ip prefix list + input_dict_2 = { + "r2": { + "prefix_lists": { + addr_type: { + "pf_list_1_{}".format(addr_type): [ + {"seqid": 10, "network": "any", "action": "permit"} + ] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Create route map + input_dict_3 = { + "r2": { + "route_maps": { + "rmap_match_pf_1_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": "5", + "match": { + addr_type: {"prefix_lists": "pf_list_1_" + addr_type} + }, + "set": {"community": {"num": "no-advertise"}}, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + step( + "Apply route-map RM1 on R2, R2 to R3 BGP neighbor with no" + " advertise community" + ) + + # Configure neighbor for route map + input_dict_4 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": { + "route_maps": [ + { + "name": "rmap_match_pf_1_" + + addr_type, + "direction": "in", + } + ] + } + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "After advertising no advertise community to BGP neighbor " + "static and connected router got removed from R3 verify using " + "show ip bgp & show ip route" + ) + + result = verify_bgp_rib(tgen, addr_type, dut, input_dict) + assert result is True, "Testcase {} : Failed \n " + " Routes still present in R3 router. Error: {}".format(tc_name, result) + + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n " + " Routes still present in R3 router. Error: {}".format(tc_name, result) + + step("Remove and Add no advertise community") + # Configure neighbor for route map + input_dict_4 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": { + "route_maps": [ + { + "name": "rmap_match_pf_1_" + + addr_type, + "direction": "in", + "delete": True, + } + ] + } + } + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "After removing no advertise community from BGP neighbor " + "static and connected router got advertised to R3 and " + "removing route verify using show ip bgp and " + " show ip route" + ) + + result = verify_bgp_rib(tgen, addr_type, dut, input_dict) + assert result is True, "Testcase {} : Failed \n " + " Routes still present in R3 router. Error: {}".format(tc_name, result) + + result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n " + " Routes still present in R3 router. Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_default-route/__init__.py b/tests/topotests/bgp_default-route/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bgp_default-route/r1/bgpd.conf b/tests/topotests/bgp_default-route/r1/bgpd.conf new file mode 100644 index 0000000000..8699d62ff2 --- /dev/null +++ b/tests/topotests/bgp_default-route/r1/bgpd.conf @@ -0,0 +1,8 @@ +router bgp 65000 + no bgp ebgp-requires-policy + neighbor 192.168.255.2 remote-as 65001 + neighbor 192.168.255.2 timers 3 10 + address-family ipv4 unicast + neighbor 192.168.255.2 default-originate + exit-address-family +! diff --git a/tests/topotests/bgp_default-route/r1/zebra.conf b/tests/topotests/bgp_default-route/r1/zebra.conf new file mode 100644 index 0000000000..0a283c06d5 --- /dev/null +++ b/tests/topotests/bgp_default-route/r1/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 172.16.255.254/32 +! +interface r1-eth0 + ip address 192.168.255.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_default-route/r2/bgpd.conf b/tests/topotests/bgp_default-route/r2/bgpd.conf new file mode 100644 index 0000000000..b6b560aa4d --- /dev/null +++ b/tests/topotests/bgp_default-route/r2/bgpd.conf @@ -0,0 +1,7 @@ +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as 65000 + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_default-route/r2/zebra.conf b/tests/topotests/bgp_default-route/r2/zebra.conf new file mode 100644 index 0000000000..606c17bec9 --- /dev/null +++ b/tests/topotests/bgp_default-route/r2/zebra.conf @@ -0,0 +1,6 @@ +! +interface r2-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_default-route/test_bgp_default-originate.py b/tests/topotests/bgp_default-route/test_bgp_default-originate.py new file mode 100644 index 0000000000..d8de0f0ac6 --- /dev/null +++ b/tests/topotests/bgp_default-route/test_bgp_default-originate.py @@ -0,0 +1,114 @@ +#!/usr/bin/env python + +# Copyright (c) 2019-2020 by +# Donatas Abraitis +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +Test if default-originate works without route-map. +""" + +import os +import sys +import json +import time +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from mininet.topo import Topo + + +class TemplateTopo(Topo): + def build(self, *_args, **_opts): + tgen = get_topogen(self) + + for routern in range(1, 3): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + tgen = Topogen(TemplateTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_default_originate_route_map(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r2"] + + def _bgp_converge(router): + output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json")) + expected = { + "192.168.255.1": { + "bgpState": "Established", + "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 1}}, + } + } + return topotest.json_cmp(output, expected) + + def _bgp_default_route_is_valid(router): + output = json.loads(router.vtysh_cmd("show ip bgp 0.0.0.0/0 json")) + expected = {"paths": [{"valid": True}]} + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge, router) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + + assert result is None, 'Failed to see bgp convergence in "{}"'.format(router) + + test_func = functools.partial(_bgp_default_route_is_valid, router) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + + assert ( + result is None + ), 'Failed to see applied metric for default route in "{}"'.format(router) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_default-route_route-map_match/__init__.py b/tests/topotests/bgp_default-route_route-map_match/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bgp_default-route_route-map_match/r1/bgpd.conf b/tests/topotests/bgp_default-route_route-map_match/r1/bgpd.conf new file mode 100644 index 0000000000..97b440f5ce --- /dev/null +++ b/tests/topotests/bgp_default-route_route-map_match/r1/bgpd.conf @@ -0,0 +1,17 @@ +router bgp 65000 + no bgp ebgp-requires-policy + neighbor 192.168.255.2 remote-as 65001 + neighbor 192.168.255.2 timers 3 10 + address-family ipv4 unicast + network 192.168.13.0/24 route-map internal + neighbor 192.168.255.2 default-originate route-map default + exit-address-family +! +bgp community-list standard default seq 5 permit 65000:1 +! +route-map default permit 10 + match community default +! +route-map internal permit 10 + set community 65000:1 +! diff --git a/tests/topotests/bgp_default-route_route-map_match/r1/zebra.conf b/tests/topotests/bgp_default-route_route-map_match/r1/zebra.conf new file mode 100644 index 0000000000..9e581a7be7 --- /dev/null +++ b/tests/topotests/bgp_default-route_route-map_match/r1/zebra.conf @@ -0,0 +1,11 @@ +! +interface lo + ip address 172.16.255.254/32 +! +interface r1-eth0 + ip address 192.168.255.1/24 +! +ip route 192.168.13.0./24 Null0 +! +ip forwarding +! diff --git a/tests/topotests/bgp_default-route_route-map_match/r2/bgpd.conf b/tests/topotests/bgp_default-route_route-map_match/r2/bgpd.conf new file mode 100644 index 0000000000..00c96cc58b --- /dev/null +++ b/tests/topotests/bgp_default-route_route-map_match/r2/bgpd.conf @@ -0,0 +1,8 @@ +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as 65000 + neighbor 192.168.255.1 timers 3 10 + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_default-route_route-map_match/r2/zebra.conf b/tests/topotests/bgp_default-route_route-map_match/r2/zebra.conf new file mode 100644 index 0000000000..606c17bec9 --- /dev/null +++ b/tests/topotests/bgp_default-route_route-map_match/r2/zebra.conf @@ -0,0 +1,6 @@ +! +interface r2-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_default-route_route-map_match/test_bgp_default-originate_route-map_match.py b/tests/topotests/bgp_default-route_route-map_match/test_bgp_default-originate_route-map_match.py new file mode 100644 index 0000000000..089c9a964e --- /dev/null +++ b/tests/topotests/bgp_default-route_route-map_match/test_bgp_default-originate_route-map_match.py @@ -0,0 +1,114 @@ +#!/usr/bin/env python + +# Copyright (c) 2019-2020 by +# Donatas Abraitis +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +Test if default-originate works with ONLY match operations. +""" + +import os +import sys +import json +import time +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from mininet.topo import Topo + + +class TemplateTopo(Topo): + def build(self, *_args, **_opts): + tgen = get_topogen(self) + + for routern in range(1, 3): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + tgen = Topogen(TemplateTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_default_originate_route_map(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r2"] + + def _bgp_converge(router): + output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json")) + expected = { + "192.168.255.1": { + "bgpState": "Established", + "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 1}}, + } + } + return topotest.json_cmp(output, expected) + + def _bgp_default_route_is_valid(router): + output = json.loads(router.vtysh_cmd("show ip bgp 0.0.0.0/0 json")) + expected = {"paths": [{"valid": True}]} + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge, router) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + + assert result is None, 'Failed to see bgp convergence in "{}"'.format(router) + + test_func = functools.partial(_bgp_default_route_is_valid, router) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + + assert ( + result is None + ), 'Failed to see applied metric for default route in "{}"'.format(router) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_default-route_route-map_match_set/__init__.py b/tests/topotests/bgp_default-route_route-map_match_set/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bgp_default-route_route-map_match_set/r1/bgpd.conf b/tests/topotests/bgp_default-route_route-map_match_set/r1/bgpd.conf new file mode 100644 index 0000000000..6ef8b1c0f4 --- /dev/null +++ b/tests/topotests/bgp_default-route_route-map_match_set/r1/bgpd.conf @@ -0,0 +1,18 @@ +router bgp 65000 + no bgp ebgp-requires-policy + neighbor 192.168.255.2 remote-as 65001 + neighbor 192.168.255.2 timers 3 10 + address-family ipv4 unicast + network 192.168.13.0/24 route-map internal + neighbor 192.168.255.2 default-originate route-map default + exit-address-family +! +bgp community-list standard default seq 5 permit 65000:1 +! +route-map default permit 10 + match community default + set metric 123 +! +route-map internal permit 10 + set community 65000:1 +! diff --git a/tests/topotests/bgp_default-route_route-map_match_set/r1/zebra.conf b/tests/topotests/bgp_default-route_route-map_match_set/r1/zebra.conf new file mode 100644 index 0000000000..9e581a7be7 --- /dev/null +++ b/tests/topotests/bgp_default-route_route-map_match_set/r1/zebra.conf @@ -0,0 +1,11 @@ +! +interface lo + ip address 172.16.255.254/32 +! +interface r1-eth0 + ip address 192.168.255.1/24 +! +ip route 192.168.13.0./24 Null0 +! +ip forwarding +! diff --git a/tests/topotests/bgp_default-route_route-map_match_set/r2/bgpd.conf b/tests/topotests/bgp_default-route_route-map_match_set/r2/bgpd.conf new file mode 100644 index 0000000000..00c96cc58b --- /dev/null +++ b/tests/topotests/bgp_default-route_route-map_match_set/r2/bgpd.conf @@ -0,0 +1,8 @@ +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as 65000 + neighbor 192.168.255.1 timers 3 10 + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_default-route_route-map_match_set/r2/zebra.conf b/tests/topotests/bgp_default-route_route-map_match_set/r2/zebra.conf new file mode 100644 index 0000000000..606c17bec9 --- /dev/null +++ b/tests/topotests/bgp_default-route_route-map_match_set/r2/zebra.conf @@ -0,0 +1,6 @@ +! +interface r2-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_default-route_route-map_match_set/test_bgp_default-originate_route-map_match_set.py b/tests/topotests/bgp_default-route_route-map_match_set/test_bgp_default-originate_route-map_match_set.py new file mode 100644 index 0000000000..398989a119 --- /dev/null +++ b/tests/topotests/bgp_default-route_route-map_match_set/test_bgp_default-originate_route-map_match_set.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python + +# Copyright (c) 2020 by +# Donatas Abraitis +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +Test if default-originate works with match operations. +And verify if set operations work as well. +""" + +import os +import sys +import json +import time +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from mininet.topo import Topo + + +class TemplateTopo(Topo): + def build(self, *_args, **_opts): + tgen = get_topogen(self) + + for routern in range(1, 3): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + tgen = Topogen(TemplateTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.iteritems(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_default_originate_route_map(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r2"] + + def _bgp_converge(router): + output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json")) + expected = { + "192.168.255.1": { + "bgpState": "Established", + "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 1}}, + } + } + return topotest.json_cmp(output, expected) + + def _bgp_default_route_has_metric(router): + output = json.loads(router.vtysh_cmd("show ip bgp 0.0.0.0/0 json")) + expected = {"paths": [{"metric": 123}]} + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge, router) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + + assert result is None, 'Failed to see bgp convergence in "{}"'.format(router) + + test_func = functools.partial(_bgp_default_route_has_metric, router) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + + assert ( + result is None + ), 'Failed to see applied metric for default route in "{}"'.format(router) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_default-route_route-map_set/__init__.py b/tests/topotests/bgp_default-route_route-map_set/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bgp_default-route_route-map_set/r1/bgpd.conf b/tests/topotests/bgp_default-route_route-map_set/r1/bgpd.conf new file mode 100644 index 0000000000..12e56e27c4 --- /dev/null +++ b/tests/topotests/bgp_default-route_route-map_set/r1/bgpd.conf @@ -0,0 +1,10 @@ +router bgp 65000 + no bgp ebgp-requires-policy + neighbor 192.168.255.2 remote-as 65001 + address-family ipv4 unicast + neighbor 192.168.255.2 default-originate route-map default + exit-address-family +! +route-map default permit 10 + set metric 123 +! diff --git a/tests/topotests/bgp_default-route_route-map_set/r1/zebra.conf b/tests/topotests/bgp_default-route_route-map_set/r1/zebra.conf new file mode 100644 index 0000000000..0a283c06d5 --- /dev/null +++ b/tests/topotests/bgp_default-route_route-map_set/r1/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 172.16.255.254/32 +! +interface r1-eth0 + ip address 192.168.255.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_default-route_route-map_set/r2/bgpd.conf b/tests/topotests/bgp_default-route_route-map_set/r2/bgpd.conf new file mode 100644 index 0000000000..00c96cc58b --- /dev/null +++ b/tests/topotests/bgp_default-route_route-map_set/r2/bgpd.conf @@ -0,0 +1,8 @@ +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as 65000 + neighbor 192.168.255.1 timers 3 10 + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_default-route_route-map_set/r2/zebra.conf b/tests/topotests/bgp_default-route_route-map_set/r2/zebra.conf new file mode 100644 index 0000000000..606c17bec9 --- /dev/null +++ b/tests/topotests/bgp_default-route_route-map_set/r2/zebra.conf @@ -0,0 +1,6 @@ +! +interface r2-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_default-route_route-map_set/test_bgp_default-originate_route-map_set.py b/tests/topotests/bgp_default-route_route-map_set/test_bgp_default-originate_route-map_set.py new file mode 100644 index 0000000000..9a22c58b16 --- /dev/null +++ b/tests/topotests/bgp_default-route_route-map_set/test_bgp_default-originate_route-map_set.py @@ -0,0 +1,114 @@ +#!/usr/bin/env python + +# Copyright (c) 2019-2020 by +# Donatas Abraitis +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +Test if default-originate works with ONLY set operations. +""" + +import os +import sys +import json +import time +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from mininet.topo import Topo + + +class TemplateTopo(Topo): + def build(self, *_args, **_opts): + tgen = get_topogen(self) + + for routern in range(1, 3): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + tgen = Topogen(TemplateTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_default_originate_route_map(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r2"] + + def _bgp_converge(router): + output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json")) + expected = { + "192.168.255.1": { + "bgpState": "Established", + "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 1}}, + } + } + return topotest.json_cmp(output, expected) + + def _bgp_default_route_has_metric(router): + output = json.loads(router.vtysh_cmd("show ip bgp 0.0.0.0/0 json")) + expected = {"paths": [{"metric": 123}]} + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge, router) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + + assert result is None, 'Failed to see bgp convergence in "{}"'.format(router) + + test_func = functools.partial(_bgp_default_route_has_metric, router) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + + assert ( + result is None + ), 'Failed to see applied metric for default route in "{}"'.format(router) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_distance_change/__init__.py b/tests/topotests/bgp_distance_change/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bgp_distance_change/r1/bgpd.conf b/tests/topotests/bgp_distance_change/r1/bgpd.conf new file mode 100644 index 0000000000..cd2ef675fc --- /dev/null +++ b/tests/topotests/bgp_distance_change/r1/bgpd.conf @@ -0,0 +1,5 @@ +router bgp 65000 + no bgp ebgp-requires-policy + neighbor 192.168.255.2 remote-as 65001 + exit-address-family +! diff --git a/tests/topotests/bgp_distance_change/r1/zebra.conf b/tests/topotests/bgp_distance_change/r1/zebra.conf new file mode 100644 index 0000000000..6e9b0b4a7e --- /dev/null +++ b/tests/topotests/bgp_distance_change/r1/zebra.conf @@ -0,0 +1,6 @@ +! +interface r1-eth0 + ip address 192.168.255.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_distance_change/r2/bgpd.conf b/tests/topotests/bgp_distance_change/r2/bgpd.conf new file mode 100644 index 0000000000..0faec85032 --- /dev/null +++ b/tests/topotests/bgp_distance_change/r2/bgpd.conf @@ -0,0 +1,7 @@ +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as 65000 + address-family ipv4 + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_distance_change/r2/zebra.conf b/tests/topotests/bgp_distance_change/r2/zebra.conf new file mode 100644 index 0000000000..93e3590448 --- /dev/null +++ b/tests/topotests/bgp_distance_change/r2/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 172.16.255.254/32 +! +interface r2-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_distance_change/test_bgp_distance_change.py b/tests/topotests/bgp_distance_change/test_bgp_distance_change.py new file mode 100644 index 0000000000..6d09cd2e8c --- /dev/null +++ b/tests/topotests/bgp_distance_change/test_bgp_distance_change.py @@ -0,0 +1,137 @@ +#!/usr/bin/env python + +# +# bgp_distance_change.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2019 by +# Donatas Abraitis +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +bgp_distance_change.py: + +Test if works the following commands: +router bgp 65031 + address-family ipv4 unicast + distance bgp 123 123 123 + +Changed distance should reflect to RIB after changes. +""" + +import os +import sys +import json +import time +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from mininet.topo import Topo + + +class TemplateTopo(Topo): + def build(self, *_args, **_opts): + tgen = get_topogen(self) + + for routern in range(1, 3): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + tgen = Topogen(TemplateTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.iteritems(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_maximum_prefix_invalid(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r1"] + + def _bgp_converge(router): + output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.2 json")) + expected = { + "192.168.255.2": { + "bgpState": "Established", + "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 2}}, + } + } + return topotest.json_cmp(output, expected) + + def _bgp_distance_change(router): + router.vtysh_cmd( + """ + configure terminal + router bgp 65000 + address-family ipv4 unicast + distance bgp 123 123 123 + """ + ) + + def _bgp_check_distance_change(router): + output = json.loads(router.vtysh_cmd("show ip route 172.16.255.254/32 json")) + expected = {"172.16.255.254/32": [{"protocol": "bgp", "distance": 123}]} + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge, router) + success, result = topotest.run_and_expect(test_func, None, count=15, wait=0.5) + + assert result is None, 'Failed to see BGP convergence in "{}"'.format(router) + + _bgp_distance_change(router) + + test_func = functools.partial(_bgp_check_distance_change, router) + success, result = topotest.run_and_expect(test_func, None, count=15, wait=0.5) + + assert result is None, 'Failed to see applied BGP distance in RIB "{}"'.format( + router + ) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_ebgp_requires_policy/__init__.py b/tests/topotests/bgp_ebgp_requires_policy/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bgp_ebgp_requires_policy/r1/bgpd.conf b/tests/topotests/bgp_ebgp_requires_policy/r1/bgpd.conf new file mode 100644 index 0000000000..aaa01ebcf9 --- /dev/null +++ b/tests/topotests/bgp_ebgp_requires_policy/r1/bgpd.conf @@ -0,0 +1,11 @@ +router bgp 65000 + neighbor 192.168.255.2 remote-as 1000 + neighbor 192.168.255.2 local-as 500 + address-family ipv4 unicast + redistribute connected + neighbor 192.168.255.2 route-map outgoing out +! +ip prefix-list peer-out permit 172.16.255.254/32 +route-map outgoing permit 10 + match ip address prefix-list peer-out +! diff --git a/tests/topotests/bgp_ebgp_requires_policy/r1/zebra.conf b/tests/topotests/bgp_ebgp_requires_policy/r1/zebra.conf new file mode 100644 index 0000000000..0a283c06d5 --- /dev/null +++ b/tests/topotests/bgp_ebgp_requires_policy/r1/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 172.16.255.254/32 +! +interface r1-eth0 + ip address 192.168.255.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_ebgp_requires_policy/r2/bgpd.conf b/tests/topotests/bgp_ebgp_requires_policy/r2/bgpd.conf new file mode 100644 index 0000000000..27427a9aaa --- /dev/null +++ b/tests/topotests/bgp_ebgp_requires_policy/r2/bgpd.conf @@ -0,0 +1,3 @@ +router bgp 1000 + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as 500 diff --git a/tests/topotests/bgp_ebgp_requires_policy/r2/zebra.conf b/tests/topotests/bgp_ebgp_requires_policy/r2/zebra.conf new file mode 100644 index 0000000000..606c17bec9 --- /dev/null +++ b/tests/topotests/bgp_ebgp_requires_policy/r2/zebra.conf @@ -0,0 +1,6 @@ +! +interface r2-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_ebgp_requires_policy/r3/bgpd.conf b/tests/topotests/bgp_ebgp_requires_policy/r3/bgpd.conf new file mode 100644 index 0000000000..2deb4b663d --- /dev/null +++ b/tests/topotests/bgp_ebgp_requires_policy/r3/bgpd.conf @@ -0,0 +1,5 @@ +router bgp 65000 + neighbor 192.168.255.2 remote-as 1000 + neighbor 192.168.255.2 local-as 500 + address-family ipv4 unicast + redistribute connected diff --git a/tests/topotests/bgp_ebgp_requires_policy/r3/zebra.conf b/tests/topotests/bgp_ebgp_requires_policy/r3/zebra.conf new file mode 100644 index 0000000000..39499a198d --- /dev/null +++ b/tests/topotests/bgp_ebgp_requires_policy/r3/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 172.16.255.254/32 +! +interface r3-eth0 + ip address 192.168.255.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_ebgp_requires_policy/r4/bgpd.conf b/tests/topotests/bgp_ebgp_requires_policy/r4/bgpd.conf new file mode 100644 index 0000000000..27427a9aaa --- /dev/null +++ b/tests/topotests/bgp_ebgp_requires_policy/r4/bgpd.conf @@ -0,0 +1,3 @@ +router bgp 1000 + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as 500 diff --git a/tests/topotests/bgp_ebgp_requires_policy/r4/zebra.conf b/tests/topotests/bgp_ebgp_requires_policy/r4/zebra.conf new file mode 100644 index 0000000000..b85911504e --- /dev/null +++ b/tests/topotests/bgp_ebgp_requires_policy/r4/zebra.conf @@ -0,0 +1,6 @@ +! +interface r4-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_ebgp_requires_policy/r5/bgpd.conf b/tests/topotests/bgp_ebgp_requires_policy/r5/bgpd.conf new file mode 100644 index 0000000000..92a2797921 --- /dev/null +++ b/tests/topotests/bgp_ebgp_requires_policy/r5/bgpd.conf @@ -0,0 +1,6 @@ +router bgp 65000 + no bgp ebgp-requires-policy + neighbor 192.168.255.2 remote-as 65000 + address-family ipv4 unicast + redistribute connected +! diff --git a/tests/topotests/bgp_ebgp_requires_policy/r5/zebra.conf b/tests/topotests/bgp_ebgp_requires_policy/r5/zebra.conf new file mode 100644 index 0000000000..7ef77a6015 --- /dev/null +++ b/tests/topotests/bgp_ebgp_requires_policy/r5/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 172.16.255.254/32 +! +interface r5-eth0 + ip address 192.168.255.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_ebgp_requires_policy/r6/bgpd.conf b/tests/topotests/bgp_ebgp_requires_policy/r6/bgpd.conf new file mode 100644 index 0000000000..342f53d4c7 --- /dev/null +++ b/tests/topotests/bgp_ebgp_requires_policy/r6/bgpd.conf @@ -0,0 +1,2 @@ +router bgp 65000 + neighbor 192.168.255.1 remote-as 65000 diff --git a/tests/topotests/bgp_ebgp_requires_policy/r6/zebra.conf b/tests/topotests/bgp_ebgp_requires_policy/r6/zebra.conf new file mode 100644 index 0000000000..1c617c4272 --- /dev/null +++ b/tests/topotests/bgp_ebgp_requires_policy/r6/zebra.conf @@ -0,0 +1,6 @@ +! +interface r6-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_ebgp_requires_policy/test_bgp_ebgp_requires_policy.py b/tests/topotests/bgp_ebgp_requires_policy/test_bgp_ebgp_requires_policy.py new file mode 100644 index 0000000000..5c2af2b30b --- /dev/null +++ b/tests/topotests/bgp_ebgp_requires_policy/test_bgp_ebgp_requires_policy.py @@ -0,0 +1,150 @@ +#!/usr/bin/env python + +# +# bgp_ebgp_requires_policy.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2019 by +# Donatas Abraitis +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +bgp_ebgp_requires_policy.py: + +Test if eBGP sender without a filter applied to the peer is allowed +to send advertisements. + +Scenario 1: + r1 has a filter applied for outgoing direction, + r2 receives 192.168.255.1/32. +Scenario 2: + r3 hasn't a filter appied for outgoing direction, + r4 does not receive 192.168.255.1/32. +Scenario 3: + r5 and r6 establish iBGP session which in turn should ignore + RFC8212. All routes for both directions MUST work. +""" + +import os +import sys +import json +import time +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from mininet.topo import Topo + + +class TemplateTopo(Topo): + def build(self, *_args, **_opts): + tgen = get_topogen(self) + + for routern in range(1, 7): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["r4"]) + + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r5"]) + switch.add_link(tgen.gears["r6"]) + + +def setup_module(mod): + tgen = Topogen(TemplateTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.iteritems(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_ebgp_requires_policy(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def _bgp_converge(router): + output = json.loads( + tgen.gears[router].vtysh_cmd("show ip bgp neighbor 192.168.255.1 json") + ) + expected = {"192.168.255.1": {"bgpState": "Established"}} + return topotest.json_cmp(output, expected) + + def _bgp_has_routes(router): + output = json.loads( + tgen.gears[router].vtysh_cmd( + "show ip bgp neighbor 192.168.255.1 routes json" + ) + ) + expected = {"routes": {"172.16.255.254/32": [{"valid": True}]}} + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge, "r2") + success, result = topotest.run_and_expect(test_func, None, count=65, wait=2) + assert success is True, 'Failed bgp convergence (r2) in "{}"'.format(tgen.gears["r2"]) + + test_func = functools.partial(_bgp_has_routes, "r2") + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert success is True, 'eBGP policy is not working (r2) in "{}"'.format(tgen.gears["r2"]) + + test_func = functools.partial(_bgp_converge, "r4") + success, result = topotest.run_and_expect(test_func, None, count=65, wait=2) + assert success is True, 'Failed bgp convergence (r4) in "{}"'.format(tgen.gears["r4"]) + + test_func = functools.partial(_bgp_has_routes, "r4") + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert success is False, 'eBGP policy is not working (r4) in "{}"'.format(tgen.gears["r4"]) + + test_func = functools.partial(_bgp_converge, "r6") + success, result = topotest.run_and_expect(test_func, None, count=65, wait=2) + assert success is True, 'Failed bgp convergence (r6) in "{}"'.format(tgen.gears["r6"]) + + test_func = functools.partial(_bgp_has_routes, "r6") + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert success is True, 'eBGP policy is not working (r6) in "{}"'.format(tgen.gears["r6"]) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_evpn_rt5/__init__.py b/tests/topotests/bgp_evpn_rt5/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bgp_evpn_rt5/r1/bgpd.conf b/tests/topotests/bgp_evpn_rt5/r1/bgpd.conf new file mode 100644 index 0000000000..9237682067 --- /dev/null +++ b/tests/topotests/bgp_evpn_rt5/r1/bgpd.conf @@ -0,0 +1,26 @@ +debug bgp neighbor-events +debug bgp updates +debug bgp zebra +router bgp 65000 + bgp router-id 192.168.100.21 + bgp log-neighbor-changes + no bgp default ipv4-unicast + neighbor 192.168.100.41 remote-as 65000 + neighbor 192.168.100.41 capability extended-nexthop + ! + address-family l2vpn evpn + neighbor 192.168.100.41 activate + advertise-all-vni + exit-address-family +! +router bgp 65000 vrf r1-vrf-101 + bgp router-id 192.168.102.21 + bgp log-neighbor-changes + no bgp network import-check + address-family ipv4 unicast + network 192.168.102.21/32 + exit-address-family + address-family l2vpn evpn + advertise ipv4 unicast + exit-address-family + ! diff --git a/tests/topotests/bgp_evpn_rt5/r1/zebra.conf b/tests/topotests/bgp_evpn_rt5/r1/zebra.conf new file mode 100644 index 0000000000..f5eaab1953 --- /dev/null +++ b/tests/topotests/bgp_evpn_rt5/r1/zebra.conf @@ -0,0 +1,22 @@ +log stdout + +hostname r1 +password zebra + +debug zebra vxlan +debug zebra kernel +debug zebra dplane +debug zebra rib +log stdout +vrf r1-vrf-101 + vni 101 + exit-vrf +! +interface r1-eth0 + ip address 192.168.100.21/24 +! +interface loop101 vrf r1-vrf-101 + ip address 192.168.102.21/32 +! + + diff --git a/tests/topotests/bgp_evpn_rt5/r2/bgpd.conf b/tests/topotests/bgp_evpn_rt5/r2/bgpd.conf new file mode 100644 index 0000000000..6dcacd288d --- /dev/null +++ b/tests/topotests/bgp_evpn_rt5/r2/bgpd.conf @@ -0,0 +1,27 @@ +debug bgp neighbor-events +debug bgp updates +debug bgp zebra +router bgp 65000 + bgp router-id 192.168.100.41 + bgp log-neighbor-changes + no bgp default ipv4-unicast + neighbor 192.168.100.21 peer-group + neighbor 192.168.100.21 remote-as 65000 + neighbor 192.168.100.21 capability extended-nexthop + ! + address-family l2vpn evpn + neighbor 192.168.100.21 activate + advertise-all-vni + exit-address-family +! +router bgp 65000 vrf r2-vrf-101 + bgp router-id 192.168.101.41 + bgp log-neighbor-changes + no bgp network import-check + address-family ipv4 unicast + network 192.168.101.41/32 + exit-address-family + address-family l2vpn evpn + advertise ipv4 unicast + exit-address-family + ! diff --git a/tests/topotests/bgp_evpn_rt5/r2/zebra.conf b/tests/topotests/bgp_evpn_rt5/r2/zebra.conf new file mode 100644 index 0000000000..e5f962d254 --- /dev/null +++ b/tests/topotests/bgp_evpn_rt5/r2/zebra.conf @@ -0,0 +1,18 @@ +log stdout + +hostname r2 +password zebra + +debug zebra vxlan + +vrf r2-vrf-101 + vni 101 + exit-vrf +! +interface loop101 vrf r2-vrf-101 + ip address 192.168.101.41/32 +! +interface r2-eth0 + ip address 192.168.100.41/24 +! + diff --git a/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py b/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py new file mode 100755 index 0000000000..f5e83fa666 --- /dev/null +++ b/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py @@ -0,0 +1,227 @@ +#!/usr/bin/env python + +# +# test_bgp_evpn.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2019 by 6WIND +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" + test_bgp_evpn.py: Test the FRR/Quagga BGP daemon with BGP IPv6 interface + with route advertisements on a separate netns. +""" + +import os +import sys +import json +from functools import partial +import pytest +import platform + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, '../')) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from lib.common_config import adjust_router_l3mdev + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + + +class BGPEVPNTopo(Topo): + "Test topology builder" + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + tgen.add_router('r1') + tgen.add_router('r2') + + switch = tgen.add_switch('s1') + switch.add_link(tgen.gears['r1']) + switch.add_link(tgen.gears['r2']) + + switch = tgen.add_switch('s2') + switch.add_link(tgen.gears['r1']) + + switch = tgen.add_switch('s3') + switch.add_link(tgen.gears['r2']) + +def setup_module(mod): + "Sets up the pytest environment" + + tgen = Topogen(BGPEVPNTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + krel = platform.release() + if topotest.version_cmp(krel, '4.18') < 0: + logger.info('BGP EVPN RT5 NETNS tests will not run (have kernel "{}", but it requires 4.18)'.format(krel)) + return pytest.skip('Skipping BGP EVPN RT5 NETNS Test. Kernel not supported') + + # create VRF vrf-101 on R1 and R2 + # create loop101 + cmds_vrflite = ['ip link add {}-vrf-101 type vrf table 101', + 'ip ru add oif {}-vrf-101 table 101', + 'ip ru add iif {}-vrf-101 table 101', + 'ip link set dev {}-vrf-101 up', + 'ip link add loop101 type dummy', + 'ip link set dev loop101 master {}-vrf-101', + 'ip link set dev loop101 up'] + cmds_netns = ['ip netns add {}-vrf-101', + 'ip link add loop101 type dummy', + 'ip link set dev loop101 netns {}-vrf-101', + 'ip netns exec {}-vrf-101 ip link set dev loop101 up'] + + cmds_r2 = [ # config routing 101 + 'ip link add name bridge-101 up type bridge stp_state 0', + 'ip link set bridge-101 master {}-vrf-101', + 'ip link set dev bridge-101 up', + 'ip link add name vxlan-101 type vxlan id 101 dstport 4789 dev r2-eth0 local 192.168.100.41', + 'ip link set dev vxlan-101 master bridge-101', + 'ip link set vxlan-101 up type bridge_slave learning off flood off mcast_flood off'] + + cmds_r1_netns_method3 = ['ip link add name vxlan-{1} type vxlan id {1} dstport 4789 dev {0}-eth0 local 192.168.100.21', + 'ip link set dev vxlan-{1} netns {0}-vrf-{1}', + 'ip netns exec {0}-vrf-{1} ip li set dev lo up', + 'ip netns exec {0}-vrf-{1} ip link add name bridge-{1} up type bridge stp_state 0', + 'ip netns exec {0}-vrf-{1} ip link set dev vxlan-{1} master bridge-{1}', + 'ip netns exec {0}-vrf-{1} ip link set bridge-{1} up', + 'ip netns exec {0}-vrf-{1} ip link set vxlan-{1} up'] + + router = tgen.gears['r1'] + for cmd in cmds_netns: + logger.info('cmd to r1: '+cmd); + output = router.run(cmd.format('r1')) + logger.info('result: '+output); + + router = tgen.gears['r2'] + adjust_router_l3mdev(tgen, 'r2') + for cmd in cmds_vrflite: + logger.info('cmd to r2: '+cmd.format('r2')); + output = router.run(cmd.format('r2')) + logger.info('result: '+output); + + for cmd in cmds_r2: + logger.info('cmd to r2: '+cmd.format('r2')); + output = router.run(cmd.format('r2')) + logger.info('result: '+output); + + router = tgen.gears['r1'] + bridge_id = '101' + for cmd in cmds_r1_netns_method3: + logger.info('cmd to r1: '+cmd.format('r1', bridge_id)); + output = router.run(cmd.format('r1', bridge_id)) + logger.info('result: '+output); + router = tgen.gears['r1'] + + for rname, router in router_list.iteritems(): + if rname == 'r1': + router.load_config( + TopoRouter.RD_ZEBRA, + os.path.join(CWD, '{}/zebra.conf'.format(rname)), + '--vrfwnetns -o vrf0' + ) + else: + router.load_config( + TopoRouter.RD_ZEBRA, + os.path.join(CWD, '{}/zebra.conf'.format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, + os.path.join(CWD, '{}/bgpd.conf'.format(rname)) + ) + + # Initialize all routers. + tgen.start_router() + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + cmds_rx_netns = ['ip netns del {}-vrf-101'] + + router = tgen.gears['r1'] + for cmd in cmds_rx_netns: + logger.info('cmd to r1: '+cmd.format('r1')); + output = router.run(cmd.format('r1')) + tgen.stop_topology() + + +def test_protocols_convergence(): + """ + Assert that all protocols have converged + statuses as they depend on it. + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + topotest.sleep(4, 'waiting 4 seconds for bgp convergence') + # Check IPv4/IPv6 routing tables. + output = tgen.gears['r1'].vtysh_cmd('show bgp l2vpn evpn', isjson=False) + logger.info('==== result from show bgp l2vpn evpn') + logger.info(output) + output = tgen.gears['r1'].vtysh_cmd('show bgp l2vpn evpn route detail', isjson=False) + logger.info('==== result from show bgp l2vpn evpn route detail') + logger.info(output) + output = tgen.gears['r1'].vtysh_cmd('show bgp vrf r1-vrf-101 ipv4', isjson=False) + logger.info('==== result from show bgp vrf r1-vrf-101 ipv4') + logger.info(output) + output = tgen.gears['r1'].vtysh_cmd('show bgp vrf r1-vrf-101', isjson=False) + logger.info('==== result from show bgp vrf r1-vrf-101 ') + logger.info(output) + output = tgen.gears['r1'].vtysh_cmd('show ip route vrf r1-vrf-101', isjson=False) + logger.info('==== result from show ip route vrf r1-vrf-101') + logger.info(output) + output = tgen.gears['r1'].vtysh_cmd('show evpn vni detail', isjson=False) + logger.info('==== result from show evpn vni detail') + logger.info(output) + output = tgen.gears['r1'].vtysh_cmd('show evpn next-hops vni all', isjson=False) + logger.info('==== result from show evpn next-hops vni all') + logger.info(output) + output = tgen.gears['r1'].vtysh_cmd('show evpn rmac vni all', isjson=False) + logger.info('==== result from show evpn next-hops vni all') + logger.info(output) + # Check IPv4 and IPv6 connectivity between r1 and r2 ( routing vxlan evpn) + pingrouter = tgen.gears['r1'] + logger.info('Check Ping IPv4 from R1(r1-vrf-101) to R2(r2-vrf-101 = 192.168.101.41)') + output = pingrouter.run('ip netns exec r1-vrf-101 ping 192.168.101.41 -f -c 1000') + logger.info(output) + if '1000 packets transmitted, 1000 received' not in output: + assertmsg = 'expected ping IPv4 from R1(r1-vrf-101) to R2(192.168.101.41) should be ok' + assert 0, assertmsg + else: + logger.info('Check Ping IPv4 from R1(r1-vrf-101) to R2(192.168.101.41) OK') + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip('Memory leak test/report is disabled') + + tgen.report_memory_leaks() + + +if __name__ == '__main__': + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_features/r1/bgp_shutdown_summary.json b/tests/topotests/bgp_features/r1/bgp_shutdown_summary.json new file mode 100644 index 0000000000..d986071768 --- /dev/null +++ b/tests/topotests/bgp_features/r1/bgp_shutdown_summary.json @@ -0,0 +1,19 @@ +{ +"ipv4Unicast":{ + "routerId":"192.168.0.1", + "as":65000, + "vrfName":"default", + "peerCount":2, + "peers":{ + "192.168.0.2":{ + "remoteAs":65000, + "state":"Idle (Admin)" + }, + "192.168.101.2":{ + "remoteAs":65100, + "state":"Idle (Admin)" + } + }, + "totalPeers":2 +} +} diff --git a/tests/topotests/bgp_features/r1/bgp_summary.json b/tests/topotests/bgp_features/r1/bgp_summary.json new file mode 100644 index 0000000000..1ad734220a --- /dev/null +++ b/tests/topotests/bgp_features/r1/bgp_summary.json @@ -0,0 +1,27 @@ +{ +"ipv4Unicast":{ + "routerId":"192.168.0.1", + "as":65000, + "vrfName":"default", + "peerCount":2, + "peers":{ + "192.168.0.2":{ + "hostname":"r2", + "remoteAs":65000, + "outq":0, + "inq":0, + "pfxRcd":7, + "state":"Established" + }, + "192.168.101.2":{ + "hostname":"r4", + "remoteAs":65100, + "outq":0, + "inq":0, + "pfxRcd":3, + "state":"Established" + } + }, + "totalPeers":2 +} +} diff --git a/tests/topotests/bgp_features/r1/bgpd.conf b/tests/topotests/bgp_features/r1/bgpd.conf new file mode 100644 index 0000000000..7aea2dc161 --- /dev/null +++ b/tests/topotests/bgp_features/r1/bgpd.conf @@ -0,0 +1,39 @@ +! +hostname r1 +log file bgpd.log +! +router bgp 65000 + timers bgp 3 10 + coalesce-time 0 + bgp router-id 192.168.0.1 + bgp log-neighbor-changes + no bgp ebgp-requires-policy + neighbor 192.168.0.2 remote-as 65000 + neighbor 192.168.0.2 description Router R2 (iBGP) + neighbor 192.168.0.2 update-source lo + neighbor 192.168.0.2 timers connect 5 + neighbor 192.168.101.2 remote-as 65100 + neighbor 192.168.101.2 description Router R4 (eBGP AS 65100) + neighbor 192.168.101.2 timers connect 5 + ! + address-family ipv4 unicast + network 192.168.0.0/24 + network 192.168.1.0/24 + network 192.168.2.0/24 + network 192.168.3.0/24 + network 192.168.6.0/24 + network 192.168.8.0/24 + neighbor 192.168.101.2 route-map testmap-in in + neighbor 192.168.101.2 route-map testmap-out out + exit-address-family +! +! +! +route-map testmap-in permit 999 +! +route-map testmap-out permit 999 +! +! +line vty +! + diff --git a/tests/topotests/bgp_features/r1/ospf6d.conf b/tests/topotests/bgp_features/r1/ospf6d.conf new file mode 100644 index 0000000000..532da39fb7 --- /dev/null +++ b/tests/topotests/bgp_features/r1/ospf6d.conf @@ -0,0 +1,22 @@ +log file ospf6d.log +! +debug ospf6 neighbor +! +interface r1-lo +! +interface r1-eth0 +! +interface r1-eth1 +! +interface r1-eth2 +! +router ospf6 + ospf6 router-id 192.168.0.1 + log-adjacency-changes + interface r1-lo area 0.0.0.0 + interface r1-eth0 area 0.0.0.0 + interface r1-eth1 area 0.0.0.0 + interface r1-eth2 area 0.0.0.0 +! +line vty +! diff --git a/tests/topotests/bgp_features/r1/ospf_neighbor.json b/tests/topotests/bgp_features/r1/ospf_neighbor.json new file mode 100644 index 0000000000..ecb97d8d74 --- /dev/null +++ b/tests/topotests/bgp_features/r1/ospf_neighbor.json @@ -0,0 +1,16 @@ +{ + "neighbors":{ + "192.168.0.2":[ + { + "priority":1, + "state":"Full\/DR" + } + ], + "192.168.0.3":[ + { + "priority":1, + "state":"Full\/DR" + } + ] + } +} diff --git a/tests/topotests/bgp_features/r1/ospfd.conf b/tests/topotests/bgp_features/r1/ospfd.conf new file mode 100644 index 0000000000..8f1711db7b --- /dev/null +++ b/tests/topotests/bgp_features/r1/ospfd.conf @@ -0,0 +1,23 @@ +log file ospfd.log +! +debug ospf event +debug ospf zebra +! +router ospf + ospf router-id 192.168.0.1 + log-adjacency-changes + network 192.168.0.0/20 area 0.0.0.0 + timers throttle spf 0 0 0 + timers lsa min-arrival 10 + timers throttle lsa all 0 + refresh timer 10 +! +line vty +interface r1-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +interface r1-eth2 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/bgp_features/r1/show_bgp.json b/tests/topotests/bgp_features/r1/show_bgp.json new file mode 100644 index 0000000000..45e0e17d7b --- /dev/null +++ b/tests/topotests/bgp_features/r1/show_bgp.json @@ -0,0 +1,350 @@ +{ + "vrfName": "default", + "routerId": "192.168.0.1", + "defaultLocPrf": 100, + "localAS": 65000, + "routes": { "0.0.0.0/0": [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"0.0.0.0", + "prefixLen":0, + "network":"0.0.0.0\/0", + "weight":0, + "peerId":"192.168.101.2", + "path":"65100", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.101.2", + "hostname":"r4", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.0.0/24": [ + { + "pathFrom":"external", + "prefix":"192.168.0.0", + "prefixLen":24, + "network":"192.168.0.0\/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.1.0/24": [ + { + "valid":true, + "pathFrom":"internal", + "prefix":"192.168.1.0", + "prefixLen":24, + "network":"192.168.1.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"192.168.0.2", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.0.2", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + }, + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"192.168.1.0", + "prefixLen":24, + "network":"192.168.1.0\/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.2.0/24": [ + { + "valid":true, + "pathFrom":"internal", + "prefix":"192.168.2.0", + "prefixLen":24, + "network":"192.168.2.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"192.168.0.2", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.0.2", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + }, + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"192.168.2.0", + "prefixLen":24, + "network":"192.168.2.0\/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.3.0/24": [ + { + "valid":true, + "pathFrom":"internal", + "prefix":"192.168.3.0", + "prefixLen":24, + "network":"192.168.3.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"192.168.0.2", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.0.2", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + }, + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"192.168.3.0", + "prefixLen":24, + "network":"192.168.3.0\/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.6.0/24": [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"192.168.6.0", + "prefixLen":24, + "network":"192.168.6.0\/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.7.0/24": [ + { + "valid":true, + "bestpath":true, + "pathFrom":"internal", + "prefix":"192.168.7.0", + "prefixLen":24, + "network":"192.168.7.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"192.168.0.2", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.0.2", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.8.0/24": [ + { + "valid":true, + "pathFrom":"internal", + "prefix":"192.168.8.0", + "prefixLen":24, + "network":"192.168.8.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"192.168.0.2", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.0.2", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + }, + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"192.168.8.0", + "prefixLen":24, + "network":"192.168.8.0\/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.101.0/24": [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"192.168.101.0", + "prefixLen":24, + "network":"192.168.101.0\/24", + "metric":0, + "weight":0, + "peerId":"192.168.101.2", + "path":"65100", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.101.2", + "hostname":"r4", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.102.0/24": [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"192.168.102.0", + "prefixLen":24, + "network":"192.168.102.0\/24", + "metric":0, + "weight":0, + "peerId":"192.168.101.2", + "path":"65100", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.101.2", + "hostname":"r4", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.201.0/24": [ + { + "pathFrom":"internal", + "prefix":"192.168.201.0", + "prefixLen":24, + "network":"192.168.201.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"192.168.0.2", + "path":"65200", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.201.2", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.202.0/24": [ + { + "pathFrom":"internal", + "prefix":"192.168.202.0", + "prefixLen":24, + "network":"192.168.202.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"192.168.0.2", + "path":"65200", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.201.2", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } +] } } diff --git a/tests/topotests/bgp_features/r1/show_bgp_metric_test.json b/tests/topotests/bgp_features/r1/show_bgp_metric_test.json new file mode 100644 index 0000000000..1720572bcb --- /dev/null +++ b/tests/topotests/bgp_features/r1/show_bgp_metric_test.json @@ -0,0 +1,57 @@ +{ + "routerId": "192.168.0.1", + "routes": { + "192.168.1.0/24": [ + { + "valid":true, + "prefix":"192.168.1.0", + "prefixLen":24, + "metric":11, + "nexthops":[ + { + "ip":"192.168.0.2", + "used":true + } + ] + }, + { + "valid":true, + "prefix":"192.168.1.0", + "prefixLen":24, + "metric":0, + "nexthops":[ + { + "ip":"0.0.0.0", + "used":true + } + ] + } +],"192.168.101.0/24": [ + { + "prefix":"192.168.101.0", + "prefixLen":24, + "metric":111, + "peerId":"192.168.101.2" + } +],"192.168.102.0/24": [ + { + "prefix":"192.168.102.0", + "prefixLen":24, + "metric":0, + "peerId":"192.168.101.2" + } +],"192.168.201.0/24": [ + { + "prefix":"192.168.201.0", + "prefixLen":24, + "metric":210, + "peerId":"192.168.0.2" + } +],"192.168.202.0/24": [ + { + "prefix":"192.168.202.0", + "prefixLen":24, + "metric":11, + "peerId":"192.168.0.2" + } +] } } diff --git a/tests/topotests/bgp_features/r1/zebra.conf b/tests/topotests/bgp_features/r1/zebra.conf new file mode 100644 index 0000000000..61564f1a1a --- /dev/null +++ b/tests/topotests/bgp_features/r1/zebra.conf @@ -0,0 +1,28 @@ +! +hostname r1 +log file zebra.log +! +interface lo + ip address 192.168.0.1/32 + ipv6 address fc00::1/128 +! +interface r1-eth0 + description SW6 Stub Network + ip address 192.168.6.1/24 + ipv6 address fc00:0:0:6::1/64 +! +interface r1-eth1 + description SW0 R1-R2 OSPF & BGP Network + ip address 192.168.1.1/24 + ipv6 address fc00:0:0:1::1/64 +! +interface r1-eth2 + description SW2 R1-R3 OSPF Network + ip address 192.168.3.1/24 + ipv6 address fc00:0:0:3::1/64 +! +interface r1-eth3 + description SW4 R1-R4 eBGP Network + ip address 192.168.101.1/24 + ipv6 address fc00:100:0:1::1/64 +! diff --git a/tests/topotests/bgp_features/r2/bgp_shutdown_summary.json b/tests/topotests/bgp_features/r2/bgp_shutdown_summary.json new file mode 100644 index 0000000000..b7892639a2 --- /dev/null +++ b/tests/topotests/bgp_features/r2/bgp_shutdown_summary.json @@ -0,0 +1,19 @@ +{ +"ipv4Unicast":{ + "routerId":"192.168.0.2", + "as":65000, + "vrfName":"default", + "peerCount":2, + "peers":{ + "192.168.0.1":{ + "remoteAs":65000, + "state":"Active" + }, + "192.168.201.2":{ + "remoteAs":65200, + "state":"Established" + } + }, + "totalPeers":2 +} +} diff --git a/tests/topotests/bgp_features/r2/bgp_summary.json b/tests/topotests/bgp_features/r2/bgp_summary.json new file mode 100644 index 0000000000..30e0ef47a8 --- /dev/null +++ b/tests/topotests/bgp_features/r2/bgp_summary.json @@ -0,0 +1,27 @@ +{ +"ipv4Unicast":{ + "routerId":"192.168.0.2", + "as":65000, + "vrfName":"default", + "peerCount":2, + "peers":{ + "192.168.0.1":{ + "hostname":"r1", + "remoteAs":65000, + "outq":0, + "inq":0, + "pfxRcd":8, + "state":"Established" + }, + "192.168.201.2":{ + "hostname":"r5", + "remoteAs":65200, + "outq":0, + "inq":0, + "pfxRcd":2, + "state":"Established" + } + }, + "totalPeers":2 +} +} diff --git a/tests/topotests/bgp_features/r2/bgpd.conf b/tests/topotests/bgp_features/r2/bgpd.conf new file mode 100644 index 0000000000..3daf9842ce --- /dev/null +++ b/tests/topotests/bgp_features/r2/bgpd.conf @@ -0,0 +1,39 @@ +! +hostname r2 +log file bgpd.log +! +router bgp 65000 + bgp router-id 192.168.0.2 + timers bgp 3 10 + coalesce-time 0 + bgp log-neighbor-changes + no bgp ebgp-requires-policy + neighbor 192.168.0.1 remote-as 65000 + neighbor 192.168.0.1 description Router R1 (iBGP) + neighbor 192.168.0.1 update-source lo + neighbor 192.168.0.1 timers connect 5 + neighbor 192.168.201.2 remote-as 65200 + neighbor 192.168.201.2 description Router R5 (eBGP AS 65200) + neighbor 192.168.201.2 timers connect 5 + ! + address-family ipv4 unicast + network 192.168.0.0/24 + network 192.168.1.0/24 + network 192.168.2.0/24 + network 192.168.3.0/24 + network 192.168.7.0/24 + network 192.168.8.0/24 + neighbor 192.168.201.2 route-map testmap-in in + neighbor 192.168.201.2 route-map testmap-out out + exit-address-family +! +! +! +route-map testmap-in permit 999 +! +route-map testmap-out permit 999 +! +! +line vty +! + diff --git a/tests/topotests/bgp_features/r2/ospf6d.conf b/tests/topotests/bgp_features/r2/ospf6d.conf new file mode 100644 index 0000000000..283d205489 --- /dev/null +++ b/tests/topotests/bgp_features/r2/ospf6d.conf @@ -0,0 +1,22 @@ +log file ospf6d.log +! +debug ospf6 neighbor +! +interface r2-lo +! +interface r2-eth0 +! +interface r2-eth1 +! +interface r2-eth2 +! +router ospf6 + ospf6 router-id 192.168.0.2 + log-adjacency-changes + interface r2-lo area 0.0.0.0 + interface r2-eth0 area 0.0.0.0 + interface r2-eth1 area 0.0.0.0 + interface r2-eth2 area 0.0.0.0 +! +line vty +! diff --git a/tests/topotests/bgp_features/r2/ospf_neighbor.json b/tests/topotests/bgp_features/r2/ospf_neighbor.json new file mode 100644 index 0000000000..5fcbd82ee1 --- /dev/null +++ b/tests/topotests/bgp_features/r2/ospf_neighbor.json @@ -0,0 +1,16 @@ +{ + "neighbors":{ + "192.168.0.1":[ + { + "priority":1, + "state":"Full\/Backup" + } + ], + "192.168.0.3":[ + { + "priority":1, + "state":"Full\/DR" + } + ] + } +} diff --git a/tests/topotests/bgp_features/r2/ospfd.conf b/tests/topotests/bgp_features/r2/ospfd.conf new file mode 100644 index 0000000000..2174fddb11 --- /dev/null +++ b/tests/topotests/bgp_features/r2/ospfd.conf @@ -0,0 +1,23 @@ +log file ospfd.log +! +debug ospf event +debug ospf zebra +! +router ospf + ospf router-id 192.168.0.2 + log-adjacency-changes + network 192.168.0.0/20 area 0.0.0.0 + timers throttle spf 0 0 0 + timers lsa min-arrival 10 + timers throttle lsa all 0 + refresh timer 10 +! +int r2-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +int r2-eth2 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +line vty +! diff --git a/tests/topotests/bgp_features/r2/show_bgp.json b/tests/topotests/bgp_features/r2/show_bgp.json new file mode 100644 index 0000000000..830d5a96f0 --- /dev/null +++ b/tests/topotests/bgp_features/r2/show_bgp.json @@ -0,0 +1,349 @@ +{ + "vrfName": "default", + "routerId": "192.168.0.2", + "defaultLocPrf": 100, + "localAS": 65000, + "routes": { "0.0.0.0/0": [ + { + "pathFrom":"internal", + "prefix":"0.0.0.0", + "prefixLen":0, + "network":"0.0.0.0\/0", + "locPrf":100, + "weight":0, + "peerId":"192.168.0.1", + "path":"65100", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.101.2", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.0.0/24": [ + { + "pathFrom":"external", + "prefix":"192.168.0.0", + "prefixLen":24, + "network":"192.168.0.0\/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.1.0/24": [ + { + "valid":true, + "pathFrom":"internal", + "prefix":"192.168.1.0", + "prefixLen":24, + "network":"192.168.1.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"192.168.0.1", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.0.1", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + }, + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"192.168.1.0", + "prefixLen":24, + "network":"192.168.1.0\/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.2.0/24": [ + { + "valid":true, + "pathFrom":"internal", + "prefix":"192.168.2.0", + "prefixLen":24, + "network":"192.168.2.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"192.168.0.1", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.0.1", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + }, + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"192.168.2.0", + "prefixLen":24, + "network":"192.168.2.0\/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.3.0/24": [ + { + "valid":true, + "pathFrom":"internal", + "prefix":"192.168.3.0", + "prefixLen":24, + "network":"192.168.3.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"192.168.0.1", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.0.1", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + }, + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"192.168.3.0", + "prefixLen":24, + "network":"192.168.3.0\/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.6.0/24": [ + { + "valid":true, + "bestpath":true, + "pathFrom":"internal", + "prefix":"192.168.6.0", + "prefixLen":24, + "network":"192.168.6.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"192.168.0.1", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.0.1", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.7.0/24": [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"192.168.7.0", + "prefixLen":24, + "network":"192.168.7.0\/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.8.0/24": [ + { + "valid":true, + "pathFrom":"internal", + "prefix":"192.168.8.0", + "prefixLen":24, + "network":"192.168.8.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"192.168.0.1", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.0.1", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + }, + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"192.168.8.0", + "prefixLen":24, + "network":"192.168.8.0\/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"IGP", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.101.0/24": [ + { + "pathFrom":"internal", + "prefix":"192.168.101.0", + "prefixLen":24, + "network":"192.168.101.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"192.168.0.1", + "path":"65100", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.101.2", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.102.0/24": [ + { + "pathFrom":"internal", + "prefix":"192.168.102.0", + "prefixLen":24, + "network":"192.168.102.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"192.168.0.1", + "path":"65100", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.101.2", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.201.0/24": [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"192.168.201.0", + "prefixLen":24, + "network":"192.168.201.0\/24", + "metric":0, + "weight":0, + "peerId":"192.168.201.2", + "path":"65200", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.201.2", + "hostname":"r5", + "afi":"ipv4", + "used":true + } + ] + } +],"192.168.202.0/24": [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"192.168.202.0", + "prefixLen":24, + "network":"192.168.202.0\/24", + "metric":0, + "weight":0, + "peerId":"192.168.201.2", + "path":"65200", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.201.2", + "hostname":"r5", + "afi":"ipv4", + "used":true + } + ] + } +] } } diff --git a/tests/topotests/bgp_features/r2/show_bgp_metric_test.json b/tests/topotests/bgp_features/r2/show_bgp_metric_test.json new file mode 100644 index 0000000000..960f13d204 --- /dev/null +++ b/tests/topotests/bgp_features/r2/show_bgp_metric_test.json @@ -0,0 +1,57 @@ +{ + "routerId": "192.168.0.2", + "routes": { + "192.168.2.0/24": [ + { + "valid":true, + "prefix":"192.168.2.0", + "prefixLen":24, + "metric":0, + "nexthops":[ + { + "ip":"192.168.0.1", + "used":true + } + ] + }, + { + "valid":true, + "prefix":"192.168.2.0", + "prefixLen":24, + "metric":0, + "nexthops":[ + { + "ip":"0.0.0.0", + "used":true + } + ] + } +],"192.168.101.0/24": [ + { + "prefix":"192.168.101.0", + "prefixLen":24, + "metric":101, + "peerId":"192.168.0.1" + } +],"192.168.102.0/24": [ + { + "prefix":"192.168.102.0", + "prefixLen":24, + "metric":0, + "peerId":"192.168.0.1" + } +],"192.168.201.0/24": [ + { + "prefix":"192.168.201.0", + "prefixLen":24, + "metric":222, + "peerId":"192.168.201.2" + } +],"192.168.202.0/24": [ + { + "prefix":"192.168.202.0", + "prefixLen":24, + "metric":0, + "peerId":"192.168.201.2" + } +] } } diff --git a/tests/topotests/bgp_features/r2/zebra.conf b/tests/topotests/bgp_features/r2/zebra.conf new file mode 100644 index 0000000000..1d427da6f5 --- /dev/null +++ b/tests/topotests/bgp_features/r2/zebra.conf @@ -0,0 +1,28 @@ +! +hostname r2 +log file zebra.log +! +interface lo + ip address 192.168.0.2/32 + ipv6 address fc00::2/128 +! +interface r2-eth0 + description SW7 Stub Network + ip address 192.168.7.1/24 + ipv6 address fc00:0:0:7::1/64 +! +interface r2-eth1 + description SW0 R1-R2 OSPF & BGP Network + ip address 192.168.1.2/24 + ipv6 address fc00:0:0:1::2/64 +! +interface r2-eth2 + description SW1 R2-R3 OSPF Network + ip address 192.168.2.1/24 + ipv6 address fc00:0:0:2::1/64 +! +interface r2-eth3 + description SW5 R2-R5 eBGP Network + ip address 192.168.201.1/24 + ipv6 address fc00:200:0:1::1/64 +! diff --git a/tests/topotests/bgp_features/r3/bgp_summary.json b/tests/topotests/bgp_features/r3/bgp_summary.json new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bgp_features/r3/ospf6d.conf b/tests/topotests/bgp_features/r3/ospf6d.conf new file mode 100644 index 0000000000..7a6623f979 --- /dev/null +++ b/tests/topotests/bgp_features/r3/ospf6d.conf @@ -0,0 +1,22 @@ +log file ospf6d.log +! +debug ospf6 neighbor +! +interface r3-lo +! +interface r3-eth0 +! +interface r3-eth1 +! +interface r3-eth2 +! +router ospf6 + ospf6 router-id 192.168.0.3 + log-adjacency-changes + interface r3-lo area 0.0.0.0 + interface r3-eth0 area 0.0.0.0 + interface r3-eth1 area 0.0.0.0 + interface r3-eth2 area 0.0.0.0 +! +line vty +! diff --git a/tests/topotests/bgp_features/r3/ospf_neighbor.json b/tests/topotests/bgp_features/r3/ospf_neighbor.json new file mode 100644 index 0000000000..e90a70a8f6 --- /dev/null +++ b/tests/topotests/bgp_features/r3/ospf_neighbor.json @@ -0,0 +1,16 @@ +{ + "neighbors":{ + "192.168.0.1":[ + { + "priority":1, + "state":"Full\/Backup" + } + ], + "192.168.0.2":[ + { + "priority":1, + "state":"Full\/Backup" + } + ] + } +} diff --git a/tests/topotests/bgp_features/r3/ospfd.conf b/tests/topotests/bgp_features/r3/ospfd.conf new file mode 100644 index 0000000000..795344fbe6 --- /dev/null +++ b/tests/topotests/bgp_features/r3/ospfd.conf @@ -0,0 +1,23 @@ +log file ospfd.log +! +debug ospf event +debug ospf zebra +! +router ospf + ospf router-id 192.168.0.3 + log-adjacency-changes + network 192.168.0.0/20 area 0.0.0.0 + timers throttle spf 0 0 0 + timers lsa min-arrival 10 + timers throttle lsa all 0 + refresh timer 10 +! +int r3-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +int r3-eth2 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +line vty +! diff --git a/tests/topotests/bgp_features/r3/zebra.conf b/tests/topotests/bgp_features/r3/zebra.conf new file mode 100644 index 0000000000..62ba04d81b --- /dev/null +++ b/tests/topotests/bgp_features/r3/zebra.conf @@ -0,0 +1,23 @@ +! +hostname r3 +log file zebra.log +! +interface lo + ip address 192.168.0.3/32 + ipv6 address fc00::3/128 +! +interface r3-eth0 + description SW8 Stub Network + ip address 192.168.8.1/24 + ipv6 address fc00:0:0:8::1/64 +! +interface r3-eth1 + description SW1 R2-R3 OSPF Network + ip address 192.168.2.2/24 + ipv6 address fc00:0:0:2::2/64 +! +interface r3-eth2 + description SW2 R1-R3 OSPF Network + ip address 192.168.3.2/24 + ipv6 address fc00:0:0:3::2/64 +! diff --git a/tests/topotests/bgp_features/r4/bgp_shutdown_summary.json b/tests/topotests/bgp_features/r4/bgp_shutdown_summary.json new file mode 100644 index 0000000000..ede4dd67b9 --- /dev/null +++ b/tests/topotests/bgp_features/r4/bgp_shutdown_summary.json @@ -0,0 +1,14 @@ +{ +"ipv4Unicast":{ + "routerId":"192.168.100.1", + "as":65100, + "vrfName":"default", + "peerCount":1, + "peers":{ + "192.168.101.1":{ + "remoteAs":65000, + "state":"Active" } + }, + "totalPeers":1 +} +} diff --git a/tests/topotests/bgp_features/r4/bgp_summary.json b/tests/topotests/bgp_features/r4/bgp_summary.json new file mode 100644 index 0000000000..c0dfe78c6d --- /dev/null +++ b/tests/topotests/bgp_features/r4/bgp_summary.json @@ -0,0 +1,18 @@ +{ +"ipv4Unicast":{ + "routerId":"192.168.100.1", + "as":65100, + "vrfName":"default", + "peerCount":1, + "peers":{ + "192.168.101.1":{ + "hostname":"r1", + "remoteAs":65000, + "outq":0, + "inq":0, + "pfxRcd":6, + "state":"Established" } + }, + "totalPeers":1 +} +} diff --git a/tests/topotests/bgp_features/r4/bgpd.conf b/tests/topotests/bgp_features/r4/bgpd.conf new file mode 100644 index 0000000000..fe1a4d4ffe --- /dev/null +++ b/tests/topotests/bgp_features/r4/bgpd.conf @@ -0,0 +1,33 @@ +! +hostname r4 +log file bgpd.log +! +router bgp 65100 + bgp router-id 192.168.100.1 + timers bgp 3 10 + coalesce-time 0 + bgp log-neighbor-changes + no bgp ebgp-requires-policy + neighbor 192.168.101.1 remote-as 65000 + neighbor 192.168.101.1 description Router R1 (eBGP AS 65000) + neighbor 192.168.101.1 timers connect 5 + ! + address-family ipv4 unicast + network 192.168.100.0/24 + network 192.168.101.0/24 + network 192.168.102.0/24 + neighbor 192.168.101.1 default-originate + neighbor 192.168.101.1 route-map testmap-in in + neighbor 192.168.101.1 route-map testmap-out out + exit-address-family +! +! +! +route-map testmap-in permit 999 +! +route-map testmap-out permit 999 +! +! +line vty +! + diff --git a/tests/topotests/bgp_features/r4/show_bgp_metric_test.json b/tests/topotests/bgp_features/r4/show_bgp_metric_test.json new file mode 100644 index 0000000000..2329498ca2 --- /dev/null +++ b/tests/topotests/bgp_features/r4/show_bgp_metric_test.json @@ -0,0 +1,27 @@ +{ + "routerId": "192.168.100.1", + "localAS": 65100, + "routes": { + "192.168.1.0/24": [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"192.168.1.0", + "prefixLen":24, + "network":"192.168.1.0\/24", + "metric":1011, + "weight":0, + "peerId":"192.168.101.1", + "path":"65000", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.101.1", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } +] } } diff --git a/tests/topotests/bgp_features/r4/zebra.conf b/tests/topotests/bgp_features/r4/zebra.conf new file mode 100644 index 0000000000..08e3e1aec9 --- /dev/null +++ b/tests/topotests/bgp_features/r4/zebra.conf @@ -0,0 +1,18 @@ +! +hostname r4 +log file zebra.log +! +interface lo + ip address 192.168.100.1/32 + ipv6 address fc00:100::1/128 +! +interface r4-eth0 + description SW5 Stub Network + ip address 192.168.102.1/24 + ipv6 address fc00:100:0:2::1/64 +! +interface r4-eth1 + description SW0 R1-R2 OSPF & BGP Network + ip address 192.168.101.2/24 + ipv6 address fc00:100:0:1::2/64 +! diff --git a/tests/topotests/bgp_features/r5/bgp_summary.json b/tests/topotests/bgp_features/r5/bgp_summary.json new file mode 100644 index 0000000000..b854af59a6 --- /dev/null +++ b/tests/topotests/bgp_features/r5/bgp_summary.json @@ -0,0 +1,19 @@ +{ +"ipv4Unicast":{ + "routerId":"192.168.200.1", + "as":65200, + "vrfName":"default", + "peerCount":1, + "peers":{ + "192.168.201.1":{ + "hostname":"r2", + "remoteAs":65000, + "outq":0, + "inq":0, + "pfxRcd":6, + "state":"Established" + } + }, + "totalPeers":1 +} +} diff --git a/tests/topotests/bgp_features/r5/bgpd.conf b/tests/topotests/bgp_features/r5/bgpd.conf new file mode 100644 index 0000000000..8504213b41 --- /dev/null +++ b/tests/topotests/bgp_features/r5/bgpd.conf @@ -0,0 +1,33 @@ +! +hostname r5 +log file bgpd.log +! +router bgp 65200 + bgp router-id 192.168.200.1 + timers bgp 3 10 + coalesce-time 0 + bgp log-neighbor-changes + no bgp ebgp-requires-policy + neighbor 192.168.201.1 remote-as 65000 + neighbor 192.168.201.1 description Router R2 (eBGP AS 65000) + neighbor 192.168.201.1 timers connect 5 + ! + address-family ipv4 unicast + network 192.168.200.0/24 + network 192.168.201.0/24 + network 192.168.202.0/24 + neighbor 192.168.101.1 default-originate + neighbor 192.168.201.1 route-map testmap-in in + neighbor 192.168.201.1 route-map testmap-out out + exit-address-family +! +! +! +route-map testmap-in permit 999 +! +route-map testmap-out permit 999 +! +! +line vty +! + diff --git a/tests/topotests/bgp_features/r5/show_bgp_metric_test.json b/tests/topotests/bgp_features/r5/show_bgp_metric_test.json new file mode 100644 index 0000000000..e6608b46a8 --- /dev/null +++ b/tests/topotests/bgp_features/r5/show_bgp_metric_test.json @@ -0,0 +1,27 @@ +{ + "routerId": "192.168.200.1", + "localAS": 65200, + "routes": { + "192.168.2.0/24": [ + { + "valid":true, + "bestpath":true, + "pathFrom":"external", + "prefix":"192.168.2.0", + "prefixLen":24, + "network":"192.168.2.0\/24", + "metric":2022, + "weight":0, + "peerId":"192.168.201.1", + "path":"65000", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.201.1", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } +] } } diff --git a/tests/topotests/bgp_features/r5/zebra.conf b/tests/topotests/bgp_features/r5/zebra.conf new file mode 100644 index 0000000000..4d9064a593 --- /dev/null +++ b/tests/topotests/bgp_features/r5/zebra.conf @@ -0,0 +1,18 @@ +! +hostname r5 +log file zebra.log +! +interface lo + ip address 192.168.200.1/32 + ipv6 address fc00:200::1/128 +! +interface r5-eth0 + description SW6 Stub Network + ip address 192.168.202.1/24 + ipv6 address fc00:200:0:2::1/64 +! +interface r5-eth1 + description SW0 R1-R2 OSPF & BGP Network + ip address 192.168.201.2/24 + ipv6 address fc00:200:0:1::2/64 +! diff --git a/tests/topotests/bgp_features/test_bgp_features.dot b/tests/topotests/bgp_features/test_bgp_features.dot new file mode 100644 index 0000000000..70b126ce8b --- /dev/null +++ b/tests/topotests/bgp_features/test_bgp_features.dot @@ -0,0 +1,83 @@ +## GraphViz file for test_all_protocol_startup +## +## Color coding: +######################### +## Main FRR: #f08080 red +## No protocol: #d0e0d0 gray +## RIP: #19e3d9 Cyan +## RIPng: #fcb314 dark yellow +## OSPFv2: #32b835 Green +## OSPFv3: #19e3d9 Cyan +## ISIS IPv4 #33ff99 light green +## ISIS IPv6 #9a81ec purple +## BGP IPv4 #eee3d3 beige +## BGP IPv6 #fdff00 yellow +## LDP IPv4 #fedbe2 light pink +##### Colors (see http://www.color-hex.com/) + +graph test_all_protocol_startup { + overlap=false; + constraint=false; + + // title + labelloc="t"; + label="Test Topologoy BGP Features"; + rankdir = TB; + + ###################### + # Routers + ###################### + + # Main FRR Router with all protocols + R4 [shape=doubleoctagon, label="R4 FRR\nAS 65100\nlo: 192.168.100.1/32\nfc00:100::1/128", fillcolor="#f08080", style=filled]; + R5 [shape=doubleoctagon, label="R5 FRR\nAS 65200\nlo: 192.168.200.1/32\nfc00:200::1/128", fillcolor="#f08080", style=filled]; + #{ rank = same {R4, R5}} + + R1 [shape=doubleoctagon, label="R1 FRR\nAS 65000\nlo: 192.168.0.1/32\nfc00::1/128", fillcolor="#f08080", style=filled]; + R2 [shape=doubleoctagon, label="R2 FRR\nAS 65000\nlo: 192.168.0.1/32\nfc00::1/128", fillcolor="#f08080", style=filled]; + #{ rank = same { R1, R2}} + + R3 [shape=doubleoctagon, label="R3 FRR\nAS 65000\nlo: 192.168.0.1/32\nfc00::1/128", fillcolor="#f08080", style=filled]; + + ###################### + # Network Lists + ###################### + + SW1_R1_R2 [label="SW1 OSPF & iBGP\n192.168.1.0/24\nfc00:0:0:1::/64", fillcolor="#32b835", style=filled]; + SW2_R2_R3 [label="SW2 OSPF\n192.168.2.0/24\nfc00:0:0:2::/64", fillcolor="#19e3d9", style=filled]; + SW3_R3_R1 [label="SW3 OSPF\n192.168.3.0/24\nfc00:0:0:3::/64", fillcolor="#19e3d9", style=filled]; + + SW4_R4_R1 [label="SW4 eBGP\n192.168.101.0/24\nfc00:100:0:1::/64", fillcolor="#fedbe2", style=filled]; + SW5_R5_R2 [label="SW5 eBGP\n192.168.201.0/24\nfc00:200:0:1::/64", fillcolor="#fedbe2", style=filled]; + #{ rank = same {SW4_R4_R1, SW5_R5_R2}} + + SW6_STUB_R1 [label="SW6\n192.168.6.0/24\nfc00:0:0:6::/64", fillcolor="#d0e0d0", style=filled]; + SW7_STUB_R2 [label="SW7\n192.168.7.0/24\nfc00:0:0:7::/64", fillcolor="#d0e0d0", style=filled]; + SW8_STUB_R3 [label="SW8\n192.168.8.0/24\nfc00:0:0:8::/64", fillcolor="#d0e0d0", style=filled]; + SW9_STUB_R4 [label="SW9\n192.168.102.0/24\nfc00:100:0:2::/64", fillcolor="#d0e0d0", style=filled]; + SW10_STUB_R5 [label="SW10\n192.168.202.0/24\nfc00:200:0:2::/64", fillcolor="#d0e0d0", style=filled]; + + + ###################### + # Network Connections + ###################### + + R1 -- SW6_STUB_R1 [label = "eth0\n.1\n::1"]; + R2 -- SW7_STUB_R2 [label = "eth0\n.1\n::1"]; + R3 -- SW8_STUB_R3 [label = "eth0\n.1\n::1"]; + R4 -- SW9_STUB_R4 [label = "eth0\n.1\n::1"]; + R5 -- SW10_STUB_R5 [label = "eth0\n.1\n::1"]; + + R1 -- SW1_R1_R2 [label = "eth1\n.1\n::1"]; + R1 -- SW3_R3_R1 [label = "eth2\n.1\n::1"]; + R2 -- SW1_R1_R2 [label = "eth1\n.2\n::2"]; + R2 -- SW2_R2_R3 [label = "eth2\n.1\n::1"]; + R3 -- SW2_R2_R3 [label = "eth1\n.2\n::2"]; + R3 -- SW3_R3_R1 [label = "eth2\n.2\n::2"]; + + R1 -- SW4_R4_R1 [label = "eth3\n.1\n::1"]; + R2 -- SW5_R5_R2 [label = "eth3\n.1\n::1"]; + R4 -- SW4_R4_R1 [label = "eth1\n.2\n::2"]; + R5 -- SW5_R5_R2 [label = "eth1\n.2\n::2"]; + +} diff --git a/tests/topotests/bgp_features/test_bgp_features.pdf b/tests/topotests/bgp_features/test_bgp_features.pdf new file mode 100644 index 0000000000..cb52a54762 Binary files /dev/null and b/tests/topotests/bgp_features/test_bgp_features.pdf differ diff --git a/tests/topotests/bgp_features/test_bgp_features.py b/tests/topotests/bgp_features/test_bgp_features.py new file mode 100755 index 0000000000..21cc8d227b --- /dev/null +++ b/tests/topotests/bgp_features/test_bgp_features.py @@ -0,0 +1,543 @@ +#!/usr/bin/env python + +# +# test_bgp_features.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2020 by +# Network Device Education Foundation, Inc. ("NetDEF") +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_bgp_features.py: Test various BGP features. +""" + +import json +import functools +import os +import sys +import pytest +import re + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + +##################################################### +# +# Network Topology Definition +# +##################################################### + + +class BGPFeaturesTopo1(Topo): + "BGP Features Topology 1" + + def build(self, **_opts): + tgen = get_topogen(self) + + # Create the routers + for rtrNum in range(1, 6): + tgen.add_router("r{}".format(rtrNum)) + + # Setup Switches and connections + for swNum in range(1, 11): + tgen.add_switch("sw{}".format(swNum)) + + # Add connections to stub switches + tgen.gears["r1"].add_link(tgen.gears["sw6"]) + tgen.gears["r2"].add_link(tgen.gears["sw7"]) + tgen.gears["r3"].add_link(tgen.gears["sw8"]) + tgen.gears["r4"].add_link(tgen.gears["sw9"]) + tgen.gears["r5"].add_link(tgen.gears["sw10"]) + + # Add connections to R1-R2-R3 core + tgen.gears["r1"].add_link(tgen.gears["sw1"]) + tgen.gears["r1"].add_link(tgen.gears["sw3"]) + tgen.gears["r2"].add_link(tgen.gears["sw1"]) + tgen.gears["r2"].add_link(tgen.gears["sw2"]) + tgen.gears["r3"].add_link(tgen.gears["sw2"]) + tgen.gears["r3"].add_link(tgen.gears["sw3"]) + + # Add connections to external R4/R5 Routers + tgen.gears["r1"].add_link(tgen.gears["sw4"]) + tgen.gears["r4"].add_link(tgen.gears["sw4"]) + tgen.gears["r2"].add_link(tgen.gears["sw5"]) + tgen.gears["r5"].add_link(tgen.gears["sw5"]) + + +##################################################### +# +# Tests starting +# +##################################################### + + +def setup_module(module): + tgen = Topogen(BGPFeaturesTopo1, module.__name__) + tgen.start_topology() + + # Starting Routers + router_list = tgen.routers() + for rname, router in router_list.iteritems(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + if os.path.exists(os.path.join(CWD, "{}/bgpd.conf".format(rname))): + logger.info("{} uses BGPd".format(rname)) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + if os.path.exists(os.path.join(CWD, "{}/ospfd.conf".format(rname))): + logger.info("{} uses OSPFd".format(rname)) + router.load_config( + TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname)) + ) + if os.path.exists(os.path.join(CWD, "{}/ospf6d.conf".format(rname))): + logger.info("{} uses OSPF6d".format(rname)) + router.load_config( + TopoRouter.RD_OSPF6, os.path.join(CWD, "{}/ospf6d.conf".format(rname)) + ) + router.start() + + +def teardown_module(module): + tgen = get_topogen() + tgen.stop_topology() + + +def test_ospf_convergence(): + "Test for OSPFv2 topology convergence" + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Check Router r1, r2 & r3 OSPF + for rtrNum in range(1, 4): + logger.info("Checking OSPFv2 convergence on router r{}".format(rtrNum)) + + router = tgen.gears["r{}".format(rtrNum)] + reffile = os.path.join(CWD, "r{}/ospf_neighbor.json".format(rtrNum)) + expected = json.loads(open(reffile).read()) + + test_func = functools.partial( + topotest.router_json_cmp, router, "show ip ospf neighbor json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=60, wait=2) + assertmsg = "OSPF router R{} did not converge".format(rtrNum) + assert res is None, assertmsg + + +def test_bgp_convergence(): + "Test for BGP topology convergence" + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Check Router r1 & r2 BGP + for rtrNum in [1, 2, 4, 5]: + logger.info("Checking BGP IPv4 convergence on router r{}".format(rtrNum)) + + router = tgen.gears["r{}".format(rtrNum)] + reffile = os.path.join(CWD, "r{}/bgp_summary.json".format(rtrNum)) + expected = json.loads(open(reffile).read()) + + test_func = functools.partial( + topotest.router_json_cmp, router, "show ip bgp summary json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=60, wait=2) + assertmsg = "BGP router R{} did not converge".format(rtrNum) + assert res is None, assertmsg + + # tgen.mininet_cli() + + +def test_bgp_shutdown(): + "Test BGP instance shutdown" + + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.net['r1'].cmd('vtysh -c \"conf t\" -c \"router bgp 65000\" -c \"bgp shutdown message ABCDabcd\"') + + # Check BGP Summary on local and remote routers + for rtrNum in [1, 2, 4]: + logger.info("Checking BGP Summary after shutdown of R1 BGP on router r{}".format(rtrNum)) + + router = tgen.gears["r{}".format(rtrNum)] + reffile = os.path.join(CWD, "r{}/bgp_shutdown_summary.json".format(rtrNum)) + expected = json.loads(open(reffile).read()) + + test_func = functools.partial( + topotest.router_json_cmp, router, "show ip bgp summary json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=60, wait=2) + assertmsg = "BGP sessions on router R{} are in incorrect state (not down as expected?)".format(rtrNum) + assert res is None, assertmsg + + +def test_bgp_shutdown_message(): + "Test BGP Peer Shutdown Message" + + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rtrNum in [2, 4]: + logger.info("Checking BGP shutdown received on router r{}".format(rtrNum)) + + shut_message = tgen.net['r{}'.format(rtrNum)].cmd( + 'tail bgpd.log | grep "NOTIFICATION.*Cease/Administratively Shutdown"') + assertmsg = "BGP shutdown message not received on router R{}".format(rtrNum) + assert shut_message != '', assertmsg + + m = re.search('.*([0-9]+ bytes[ 0-9a-fA-F]+)', shut_message) + if m: + found = m.group(1) + else: + found = '' + assertmsg = "Incorrect BGP shutdown message received on router R{}".format(rtrNum) + assert found == '8 bytes 41 42 43 44 61 62 63 64', assertmsg + + # tgen.mininet_cli() + + +def test_bgp_no_shutdown(): + "Test BGP instance no shutdown" + + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.net['r1'].cmd('vtysh -c \"conf t\" -c \"router bgp 65000\" -c \"no bgp shutdown\"') + + # Check BGP Summary on local and remote routers + for rtrNum in [1, 2, 4]: + logger.info("Checking BGP Summary after removing bgp shutdown on router r1 on router r{}".format(rtrNum)) + + router = tgen.gears["r{}".format(rtrNum)] + reffile = os.path.join(CWD, "r{}/bgp_summary.json".format(rtrNum)) + expected = json.loads(open(reffile).read()) + + test_func = functools.partial( + topotest.router_json_cmp, router, "show ip bgp summary json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=60, wait=2) + assertmsg = "BGP sessions on router R{} are in incorrect state (not down as expected?)".format(rtrNum) + assert res is None, assertmsg + + +def test_bgp_metric_config(): + "Test BGP Changing metric values in route-maps" + + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Configuring bgp route-maps on router r1 and r2 to update metric") + + # # Adding the following configuration to r1: + # router bgp 65000 + # address-family ipv4 unicast + # neighbor 192.168.0.2 route-map addmetric-in in + # neighbor 192.168.0.2 route-map addmetric-out out + # neighbor 192.168.101.2 route-map setmetric-in in + # neighbor 192.168.101.2 route-map setmetric-out out + # exit-address-family + # ! + # ip prefix-list net1 seq 10 permit 192.168.101.0/24 + # ip prefix-list net2 seq 20 permit 192.168.1.0/24 + # ! + # route-map setmetric-in permit 10 + # match ip address prefix-list net1 + # set metric 111 + # ! + # route-map setmetric-in permit 20 + # ! + # route-map setmetric-out permit 10 + # match ip address prefix-list net2 + # set metric 1011 + # ! + # route-map setmetric-out permit 20 + # ! + # route-map addmetric-in permit 10 + # set metric +11 + # ! + # route-map addmetric-out permit 10 + # set metric +12 + # ! + + tgen.net['r1'].cmd('vtysh -c "conf t" -c "router bgp 65000" '+ + '-c "address-family ipv4 unicast" '+ + '-c "neighbor 192.168.0.2 route-map addmetric-in in" '+ + '-c "neighbor 192.168.0.2 route-map addmetric-out out" '+ + '-c "neighbor 192.168.101.2 route-map setmetric-in in" '+ + '-c "neighbor 192.168.101.2 route-map setmetric-out out" ') + tgen.net['r1'].cmd('vtysh -c "conf t" '+ + '-c "ip prefix-list net1 seq 10 permit 192.168.101.0/24" '+ + '-c "ip prefix-list net2 seq 20 permit 192.168.1.0/24"') + tgen.net['r1'].cmd('vtysh -c "conf t" '+ + '-c "route-map setmetric-in permit 10" '+ + '-c "match ip address prefix-list net1" '+ + '-c "set metric 111" '+ + '-c "route-map setmetric-in permit 20"') + tgen.net['r1'].cmd('vtysh -c "conf t" '+ + '-c "route-map setmetric-out permit 10" '+ + '-c "match ip address prefix-list net2" '+ + '-c "set metric 1011" '+ + '-c "route-map setmetric-out permit 20"') + tgen.net['r1'].cmd('vtysh -c "conf t" '+ + '-c "route-map addmetric-in permit 10" '+ + '-c "set metric +11"') + tgen.net['r1'].cmd('vtysh -c "conf t" '+ + '-c "route-map addmetric-out permit 10" '+ + '-c "set metric +12"') + + # # Adding the following configuration to r2: + # router bgp 65000 + # address-family ipv4 unicast + # neighbor 192.168.0.1 route-map subtractmetric-in in + # neighbor 192.168.0.1 route-map subtractmetric-out out + # neighbor 192.168.201.2 route-map setmetric-in in + # neighbor 192.168.201.2 route-map setmetric-out out + # exit-address-family + # ! + # ip prefix-list net1 seq 10 permit 192.168.201.0/24 + # ip prefix-list net2 seq 20 permit 192.168.2.0/24 + # ! + # route-map setmetric-in permit 10 + # match ip address prefix-list net1 + # set metric 222 + # ! + # route-map setmetric-in permit 20 + # ! + # route-map setmetric-out permit 10 + # match ip address prefix-list net2 + # set metric 2022 + # ! + # route-map setmetric-out permit 20 + # ! + # route-map subtractmetric-in permit 10 + # set metric -22 + # ! + # route-map subtractmetric-out permit 10 + # set metric -23 + # ! + + tgen.net['r2'].cmd('vtysh -c "conf t" -c "router bgp 65000" '+ + '-c "address-family ipv4 unicast" '+ + '-c "neighbor 192.168.0.1 route-map subtractmetric-in in" '+ + '-c "neighbor 192.168.0.1 route-map subtractmetric-out out" '+ + '-c "neighbor 192.168.201.2 route-map setmetric-in in" ' + + '-c "neighbor 192.168.201.2 route-map setmetric-out out" ') + tgen.net['r2'].cmd('vtysh -c "conf t" '+ + '-c "ip prefix-list net1 seq 10 permit 192.168.201.0/24" '+ + '-c "ip prefix-list net2 seq 20 permit 192.168.2.0/24" ') + tgen.net['r2'].cmd('vtysh -c "conf t" '+ + '-c "route-map setmetric-in permit 10" '+ + '-c "match ip address prefix-list net1" '+ + '-c "set metric 222" '+ + '-c "route-map setmetric-in permit 20"') + tgen.net['r2'].cmd('vtysh -c "conf t" '+ + '-c "route-map setmetric-out permit 10" '+ + '-c "match ip address prefix-list net2" '+ + '-c "set metric 2022" '+ + '-c "route-map setmetric-out permit 20"') + tgen.net['r2'].cmd('vtysh -c "conf t" '+ + '-c "route-map subtractmetric-in permit 10" '+ + '-c "set metric -22"') + tgen.net['r2'].cmd('vtysh -c "conf t" '+ + '-c "route-map subtractmetric-out permit 10" '+ + '-c "set metric -23"') + + # Clear IN the bgp neighbors to make sure the route-maps are applied + tgen.net['r1'].cmd('vtysh -c "clear ip bgp 192.168.0.2 in" '+ + '-c "clear ip bgp 192.168.101.2 in"') + tgen.net['r2'].cmd('vtysh -c "clear ip bgp 192.168.0.1 in" '+ + '-c "clear ip bgp 192.168.201.2 in"') + + # tgen.mininet_cli() + + # Checking BGP config - should show the bgp metric settings in the route-maps + logger.info("Checking BGP configuration for correct 'set metric' values") + + setmetric111 = tgen.net['r1'].cmd('vtysh -c "show running" | grep "^ set metric 111"').rstrip() + assertmsg = "'set metric 111' configuration applied to R1, but not visible in configuration" + assert setmetric111 == ' set metric 111', assertmsg + + setmetric222 = tgen.net['r2'].cmd('vtysh -c "show running" | grep "^ set metric 222"').rstrip() + assertmsg = "'set metric 222' configuration applied to R2, but not visible in configuration" + assert setmetric222 == ' set metric 222', assertmsg + + +def test_bgp_metric_add_config(): + "Test BGP Changing metric values in route-maps" + + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Checking BGP configuration for correct 'set metric' ADD value") + + setmetricP11 = tgen.net['r1'].cmd('vtysh -c "show running" | grep "^ set metric +11"').rstrip() + assertmsg = "'set metric +11' configuration applied to R1, but not visible in configuration" + assert setmetricP11 == ' set metric +11', assertmsg + + +def test_bgp_metric_subtract_config(): + "Test BGP Changing metric values in route-maps" + + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Checking BGP configuration for correct 'set metric' SUBTRACT value") + + setmetricM22 = tgen.net['r2'].cmd('vtysh -c "show running" | grep "^ set metric -22"').rstrip() + assertmsg = "'set metric -22' configuration applied to R2, but not visible in configuration" + assert setmetricM22 == ' set metric -22', assertmsg + + +def test_bgp_set_metric(): + "Test setting metrics" + + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Test absolute metric") + + # Check BGP Summary on local and remote routers + for rtrNum in [1, 2, 4, 5]: + logger.info("Checking metrics of BGP router on r{}".format(rtrNum)) + + router = tgen.gears["r{}".format(rtrNum)] + reffile = os.path.join(CWD, "r{}/show_bgp_metric_test.json".format(rtrNum)) + expected = json.loads(open(reffile).read()) + + test_func = functools.partial( + topotest.router_json_cmp, router, "show ip bgp json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=60, wait=2) + assertmsg = "BGP metrics on router r{} wrong".format(rtrNum) + assert res is None, assertmsg + + +def test_bgp_remove_metric_rmaps(): + "Test removing route-maps with metric changes again" + + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Test absolute metric") + + # Remove metric route-maps and relevant comfiguration + + tgen.net['r1'].cmd('vtysh -c "conf t" -c "router bgp 65000" '+ + '-c "address-family ipv4 unicast" '+ + '-c "no neighbor 192.168.0.2 route-map addmetric-in in" '+ + '-c "no neighbor 192.168.0.2 route-map addmetric-out out" '+ + '-c "no neighbor 192.168.101.2 route-map setmetric-in in" '+ + '-c "no neighbor 192.168.101.2 route-map setmetric-out out" ') + tgen.net['r1'].cmd('vtysh -c "conf t" '+ + '-c "no ip prefix-list net1" '+ + '-c "no ip prefix-list net2"') + tgen.net['r1'].cmd('vtysh -c "conf t" '+ + '-c "no route-map setmetric-in" ') + tgen.net['r1'].cmd('vtysh -c "conf t" '+ + '-c "no route-map setmetric-out" ') + tgen.net['r1'].cmd('vtysh -c "conf t" '+ + '-c "no route-map addmetric-in" ') + tgen.net['r1'].cmd('vtysh -c "conf t" '+ + '-c "no route-map addmetric-out" ') + + tgen.net['r2'].cmd('vtysh -c "conf t" -c "router bgp 65000" '+ + '-c "address-family ipv4 unicast" '+ + '-c "no neighbor 192.168.0.1 route-map subtractmetric-in in" '+ + '-c "no neighbor 192.168.0.1 route-map subtractmetric-out out" '+ + '-c "no neighbor 192.168.201.2 route-map setmetric-in in" ' + + '-c "no neighbor 192.168.201.2 route-map setmetric-out out" ') + tgen.net['r2'].cmd('vtysh -c "conf t" '+ + '-c "no ip prefix-list net1" '+ + '-c "no ip prefix-list net2" ') + tgen.net['r2'].cmd('vtysh -c "conf t" '+ + '-c "no route-map setmetric-in" ') + tgen.net['r2'].cmd('vtysh -c "conf t" '+ + '-c "no route-map setmetric-out" ') + tgen.net['r2'].cmd('vtysh -c "conf t" '+ + '-c "no route-map addmetric-in" ') + tgen.net['r2'].cmd('vtysh -c "conf t" '+ + '-c "no route-map addmetric-out" ') + + # Clear IN the bgp neighbors to make sure the route-maps are applied + tgen.net['r1'].cmd('vtysh -c "clear ip bgp 192.168.0.2 in" '+ + '-c "clear ip bgp 192.168.101.2 in"') + tgen.net['r2'].cmd('vtysh -c "clear ip bgp 192.168.0.1 in" '+ + '-c "clear ip bgp 192.168.201.2 in"') + + # tgen.mininet_cli() + + # Check BGP Summary on local and remote routers + for rtrNum in [1, 2]: + logger.info("Checking metrics of BGP router on r{}".format(rtrNum)) + + router = tgen.gears["r{}".format(rtrNum)] + reffile = os.path.join(CWD, "r{}/show_bgp.json".format(rtrNum)) + expected = json.loads(open(reffile).read()) + + test_func = functools.partial( + topotest.router_json_cmp, router, "show ip bgp json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=60, wait=2) + assertmsg = "BGP routes on router r{} are wrong after removing metric route-maps".format(rtrNum) + assert res is None, assertmsg + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_flowspec/__init__.py b/tests/topotests/bgp_flowspec/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bgp_flowspec/exabgp.env b/tests/topotests/bgp_flowspec/exabgp.env new file mode 100644 index 0000000000..a328e04962 --- /dev/null +++ b/tests/topotests/bgp_flowspec/exabgp.env @@ -0,0 +1,54 @@ + +[exabgp.api] +encoder = text +highres = false +respawn = false +socket = '' + +[exabgp.bgp] +openwait = 60 + +[exabgp.cache] +attributes = true +nexthops = true + +[exabgp.daemon] +daemonize = true +pid = '/var/run/exabgp/exabgp.pid' +user = 'exabgp' +##daemonize = false + +[exabgp.log] +all = false +configuration = true +daemon = true +destination = '/var/log/exabgp.log' +enable = true +level = INFO +message = false +network = true +packets = false +parser = false +processes = true +reactor = true +rib = false +routes = false +short = false +timers = false + +[exabgp.pdb] +enable = false + +[exabgp.profile] +enable = false +file = '' + +[exabgp.reactor] +speed = 1.0 + +[exabgp.tcp] +acl = false +bind = '' +delay = 0 +once = false +port = 179 diff --git a/tests/topotests/bgp_flowspec/peer1/exabgp.cfg b/tests/topotests/bgp_flowspec/peer1/exabgp.cfg new file mode 100644 index 0000000000..cd1fae5aba --- /dev/null +++ b/tests/topotests/bgp_flowspec/peer1/exabgp.cfg @@ -0,0 +1,32 @@ +neighbor 10.0.1.1 { +router-id 10.0.1.101; +local-address 10.0.1.101; +local-as 100; +peer-as 100; +flow { +route { +match { +source 1.1.1.2/32; +destination 3.3.3.3/32; +packet-length <200; +} +then { +redirect 50.0.0.2; +rate-limit 55; +} +} +#end route 1 +route { +match { +source 1::2/128/0; +destination 3::3/128/0; +packet-length <200; +} +then { +redirect 50::2; +rate-limit 55; +} +} +#end route 2 +} +} diff --git a/tests/topotests/bgp_flowspec/r1/bgpd.conf b/tests/topotests/bgp_flowspec/r1/bgpd.conf new file mode 100644 index 0000000000..6dc91b24a9 --- /dev/null +++ b/tests/topotests/bgp_flowspec/r1/bgpd.conf @@ -0,0 +1,18 @@ +! +hostname r1 +password zebra +log stdout debugging +router bgp 100 + bgp router-id 10.0.1.1 + neighbor 10.0.1.101 remote-as 100 + neighbor 10.0.1.101 update-source 10.0.1.1 + address-family ipv6 flowspec + local-install r1-eth0 + neighbor 10.0.1.101 activate + exit-address-family + address-family ipv4 flowspec + local-install r1-eth0 + neighbor 10.0.1.101 activate + exit-address-family + ! +! diff --git a/tests/topotests/bgp_flowspec/r1/summary.txt b/tests/topotests/bgp_flowspec/r1/summary.txt new file mode 100644 index 0000000000..82426f3162 --- /dev/null +++ b/tests/topotests/bgp_flowspec/r1/summary.txt @@ -0,0 +1,50 @@ +{ +"ipv4Unicast":{ + "routerId":"10.0.1.1", + "as":100, + "vrfName":"default", + "peerCount":1, + "peers":{ + "10.0.1.101":{ + "outq":0, + "inq":0, + "pfxRcd":0, + "pfxSnt":0, + "state":"Established" + } + }, + "totalPeers":1 +}, +"ipv4Flowspec":{ + "routerId":"10.0.1.1", + "as":100, + "vrfName":"default", + "peerCount":1, + "peers":{ + "10.0.1.101":{ + "outq":0, + "inq":0, + "pfxRcd":1, + "pfxSnt":0, + "state":"Established" + } + }, + "totalPeers":1 +}, +"ipv6Flowspec":{ + "routerId":"10.0.1.1", + "as":100, + "vrfName":"default", + "peerCount":1, + "peers":{ + "10.0.1.101":{ + "outq":0, + "inq":0, + "pfxRcd":1, + "pfxSnt":0, + "state":"Established" + } + }, + "totalPeers":1 +} +} diff --git a/tests/topotests/bgp_flowspec/r1/zebra.conf b/tests/topotests/bgp_flowspec/r1/zebra.conf new file mode 100644 index 0000000000..e4d5a21194 --- /dev/null +++ b/tests/topotests/bgp_flowspec/r1/zebra.conf @@ -0,0 +1,8 @@ +! +hostname r1 +password zebra +interface r1-eth0 + ip address 10.0.1.1/24 + ipv6 address 1001::1/112 +! + diff --git a/tests/topotests/bgp_flowspec/test_bgp_flowspec_topo.py b/tests/topotests/bgp_flowspec/test_bgp_flowspec_topo.py new file mode 100755 index 0000000000..a7e2c31cde --- /dev/null +++ b/tests/topotests/bgp_flowspec/test_bgp_flowspec_topo.py @@ -0,0 +1,208 @@ +#!/usr/bin/env python + +# +# test_bgp_flowspec_topo.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2019 by 6WIND +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_bgp_flowspec_topo.py: Test BGP topology with Flowspec EBGP peering + + + +------+------+ + | peer1 | + | BGP peer 1 | + |192.168.0.161| + | | + +------+------+ + .2 | r1-eth0 + | + ~~~~~~~~~ + +---~~ s1 ~~------+ + ~~ ~~ + ~~~~~~~~~ + | 10.0.1.1 r1-eth0 + | 1001::1 r1-eth0 + +--------+--------+ + | r1 | + |BGP 192.168.0.162| + | | + | | + | | + +-----------------+ + +""" + +import json +import functools +import os +import sys +import pytest +import getopt + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from lib.lutil import lUtil +from lib.lutil import luCommand + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + +##################################################### +## +## Network Topology Definition +## +##################################################### + + +class BGPFLOWSPECTopo1(Topo): + "BGP EBGP Flowspec Topology 1" + + def build(self, **_opts): + tgen = get_topogen(self) + + # Setup Routers + tgen.add_router("r1") + + # Setup Control Path Switch 1. r1-eth0 + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + + ## Add eBGP ExaBGP neighbors + peer_ip = "10.0.1.101" ## peer + peer_route = "via 10.0.1.1" ## router + peer = tgen.add_exabgp_peer("peer1", ip=peer_ip, defaultRoute=peer_route) + switch.add_link(peer) + + +##################################################### +## +## Tests starting +## +##################################################### + + +def setup_module(module): + tgen = Topogen(BGPFLOWSPECTopo1, module.__name__) + + tgen.start_topology() + # check for zebra capability + router = tgen.gears["r1"] + + # Get r1 reference and run Daemons + logger.info("Launching BGP and ZEBRA on r1") + router = tgen.gears["r1"] + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format("r1")) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format("r1")) + ) + router.start() + + peer_list = tgen.exabgp_peers() + for pname, peer in peer_list.iteritems(): + peer_dir = os.path.join(CWD, pname) + env_file = os.path.join(CWD, "exabgp.env") + peer.start(peer_dir, env_file) + logger.info(pname) + + +def teardown_module(module): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_convergence(): + "Test for BGP topology convergence" + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("waiting for bgp convergence") + + # Expected result + router = tgen.gears["r1"] + reffile = os.path.join(CWD, "r1/summary.txt") + + expected = json.loads(open(reffile).read()) + + test_func = functools.partial( + topotest.router_json_cmp, router, "show bgp summary json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=90, wait=0.5) + assertmsg = "BGP router network did not converge" + assert res is None, assertmsg + + +def test_bgp_flowspec(): + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r1"] + + logger.info("Check BGP FS entry for 3.3.3.3 with redirect IP") + output = router.vtysh_cmd( + "show bgp ipv4 flowspec 3.3.3.3", isjson=False, daemon="bgpd" + ) + logger.info(output) + if ( + "NH 50.0.0.2" not in output + or "FS:redirect IP" not in output + or "Packet Length < 200" not in output + ): + assertmsg = "traffic to 3.3.3.3 should have been detected as FS entry. NOK" + assert 0, assertmsg + else: + logger.info("Check BGP FS entry for 3.3.3.3 with redirect IP OK") + + logger.info("Check BGP FS entry for 3::3 with redirect IP") + output = router.vtysh_cmd( + "show bgp ipv6 flowspec 3::3", isjson=False, daemon="bgpd" + ) + logger.info(output) + if ( + "NH 50::2" not in output + or "FS:redirect IP" not in output + or "Packet Length < 200" not in output + ): + assertmsg = "traffic to 3::3 should have been detected as FS entry. NOK" + assert 0, assertmsg + else: + logger.info("Check BGP FS entry for 3::3 with redirect IP OK") + +if __name__ == "__main__": + + args = ["-s"] + sys.argv[1:] + ret = pytest.main(args) + + sys.exit(ret) diff --git a/tests/topotests/bgp_gr_functionality_topo1/bgp_gr_topojson_topo1.json b/tests/topotests/bgp_gr_functionality_topo1/bgp_gr_topojson_topo1.json new file mode 100644 index 0000000000..7b3cac814f --- /dev/null +++ b/tests/topotests/bgp_gr_functionality_topo1/bgp_gr_topojson_topo1.json @@ -0,0 +1,115 @@ +{ + "ipv4base":"192.168.0.0", + "ipv4mask":24, + "ipv6base":"fd00::", + "ipv6mask":64, + "link_ip_start":{"ipv4":"192.168.0.0", "v4mask":24, "ipv6":"fd00::", "v6mask":64}, + "lo_prefix":{"ipv4":"1.0.", "v4mask":32, "ipv6":"2001:DB8:F::", "v6mask":128}, + "routers":{ + "r1":{ + "links":{ + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2-link1": {"ipv4":"auto", "ipv6":"auto"}, + "r2-link2": {"ipv4":"auto", "ipv6":"auto"} + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": { + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": { + } + } + } + } + } + } + } + }, + "static_routes":[ + { + "network":"100.0.10.1/32", + "no_of_ip":5, + "next_hop":"192.168.1.2" + }, + { + "network":"1::1/128", + "no_of_ip":5, + "next_hop":"fd00:0:0:1::2" + }]}, + "r2":{ + "links":{ + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1-link1": {"ipv4":"auto", "ipv6":"auto"}, + "r1-link2": {"ipv4":"auto", "ipv6":"auto"} + }, + "bgp": { + "local_as": "200", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": { + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": { + } + } + } + } + } + } + } + }, + "static_routes":[ + { + "network":"200.0.20.1/32", + "no_of_ip":5, + "next_hop":"192.168.1.1" + }, + { + "network":"2::1/128", + "no_of_ip":5, + "next_hop":"fd00:0:0:1::1" + }] + } + } +} 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 new file mode 100755 index 0000000000..fdbd317093 --- /dev/null +++ b/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1.py @@ -0,0 +1,3501 @@ +#!/usr/bin/env python +# +# Copyright (c) 2019 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. ("NetDEF") +# in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +Following tests are covered to test BGP Gracefull Restart functionality. +Basic Common Test steps for all the test case below : +- Create topology (setup module) + Creating 2 routers topology, r1, r2 in IBGP +- Bring up topology +- Verify for bgp to converge +- Configure BGP Garceful Restart on both the routers. + +1. Helper BGP router R1, mark and unmark IPV4 routes + as stale as the restarting router R2 come up within the restart time. +2. Helper BGP router R1, mark IPV4 routes as stale and + deletes them as the restarting router R2 did-not come up within restart + time. +3. Restart BGP router R1, detects it is connected to R2, + which is a helper router. Verify the restart capability i.e. R bit + are sent after R1 reloads and comes back. +4. Verify that the restarting node sets "R" bit while sending the + BGP open messages after the node restart, only if GR is enabled. +5. Verify if restarting node resets R bit in BGP open message + during normal BGP session flaps as well, even when GR restarting mode is enabled. + Here link flap happen due to interface UP/DOWN. +6. Verify if restarting node resets R bit in BGP open message + during normal BGP session flaps as well, even when GR restarting mode is enabled. + Here link flap happen due to neigh router restarts +7. Verify if restarting node resets R bit in BGP open message + during normal BGP session flaps when GR helper mode is enabled. + Here link flap happen due to interface UP/DOWN. +8. Verify if restarting node resets R bit in BGP open message + during normal BGP session flaps when GR helper mode is enabled. + Here link flap happen due to neigh router restarts. +9. Verify that restarting nodes set "F" bit while sending + the BGP open messages after it restarts, only when BGP GR is enabled. +10. Verify that restarting nodes reset "F" bit while sending + the BGP open messages after it's restarts, when BGP GR is **NOT** enabled. +11. Verify that only GR helper routers keep the stale + route entries, not any GR disabled router. +12. Verify that GR helper routers keeps all the routes received + from restarting node if both the routers are configured as GR restarting node. +13. Verify that GR helper routers delete all the routes + received from a node if both the routers are configured as GR helper node. +14. Test Objective : After BGP neighborship is established and GR capability + is exchanged, transition helper router to disabled state. +15.Test Objective : After BGP neighborship is established and GR capability + is exchanged, transition disabled router to helper state. +16. Verify transition from Global Restarting to Disable and then + Global Disable to Restarting. +17. Verify transition from Global Helper to Disable and then Global + Disable to Helper. +18. Verify transition from Global Restart to Helper and then Global + Helper to Restart. +19. Verify transition from Peer-level helper to Global Restarting. +20. Verify transition from Peer-level restart to Global Restart. +21. Verify transition from Peer-level disabled to Global Restart. +22. Verify Peer-level inherit from Global Restarting mode. +23. Verify transition from Peer-level helper to Global inherit helper. +24. Verify transition from Peer-level restart to Global inherit helper. +25. Verify transition from Peer-level disbale to Global inherit helper. +26. Verify default GR functional mode is Helper. +27. Verify transition from Peer-level Helper to Global Disable. +28. Verify transition from Peer-level Restarting to Global Disable. +29. Verify transition from Peer-level Disable to Global Disable. +30. Verfiy Peer-level inherit from Global Disable mode. + +""" + +import os +import sys +import json +import time +import inspect +import pytest +from time import sleep + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join("../")) +sys.path.append(os.path.join("../lib/")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + +# Import topoJson from lib, to create topology and initial configuration +from lib.topojson import build_topo_from_json, build_config_from_json +from lib.bgp import ( + clear_bgp, + verify_bgp_rib, + verify_graceful_restart, + create_router_bgp, + verify_r_bit, + verify_f_bit, + verify_bgp_convergence, + verify_graceful_restart_timers, +) + +from lib.common_config import ( + write_test_header, + reset_config_on_routers, + start_topology, + kill_router_daemons, + start_router_daemons, + verify_rib, + check_address_types, + write_test_footer, + check_router_status, + shutdown_bringup_interface, + step, + kill_mininet_routers_process, + get_frr_ipv6_linklocal, + create_route_maps, + required_linux_kernel_version +) + +# Reading the data from JSON File for topology and configuration creation +jsonFile = "{}/bgp_gr_topojson_topo1.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + logger.info("Could not read file:", jsonFile) + + +# Global variables +NEXT_HOP_IP = {"ipv4": "192.168.1.10", "ipv6": "fd00:0:0:1::10"} +NEXT_HOP_IP_1 = {"ipv4": "192.168.0.1", "ipv6": "fd00::1"} +NEXT_HOP_IP_2 = {"ipv4": "192.168.0.2", "ipv6": "fd00::2"} +BGP_CONVERGENCE = False +GR_RESTART_TIMER = 20 +PREFERRED_NEXT_HOP = "link_local" + + +class GenerateTopo(Topo): + """ + Test topology builder + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # This function only purpose is to create topology + # as defined in input json file. + # + # Create topology (setup module) + # Creating 2 routers topology, r1, r2in IBGP + # Bring up topology + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + global ADDR_TYPES + + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version('4.15') + if result is not True: + pytest.skip("Kernel requirements are not met") + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(GenerateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # Kill stale mininet routers and process + kill_mininet_routers_process(tgen) + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Api call verify whether BGP is converged + ADDR_TYPES = check_address_types() + + for addr_type in ADDR_TYPES: + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error:" " {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """ + Teardown the pytest environment + + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +def configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut, peer): + """ + This function groups the repetitive function calls into one function. + """ + + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + clear_bgp(tgen, addr_type, dut) + + return True + + +def next_hop_per_address_family( + tgen, dut, peer, addr_type, next_hop_dict, preferred_next_hop=PREFERRED_NEXT_HOP +): + """ + This function returns link_local or global next_hop per address-family + """ + + intferface = topo["routers"][peer]["links"]["{}-link1".format(dut)]["interface"] + if addr_type == "ipv6" and "link_local" in preferred_next_hop: + next_hop = get_frr_ipv6_linklocal(tgen, peer, intf=intferface) + else: + next_hop = next_hop_dict[addr_type] + + return next_hop + + +def test_BGP_GR_TC_46_p1(request): + """ + Test Objective : transition from Peer-level helper to Global Restarting + Global Mode : GR Restarting + PerPeer Mode : GR Helper + GR Mode effective : GR Helper + + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step( + "Configure R1 and R2 as GR restarting node in global" + " and helper in per-Peer-level" + ) + + input_dict = { + "r1": { + "bgp": { + "graceful-restart": {"graceful-restart": True,}, + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + }, + } + }, + "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}}, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + step("Verify on R2 that R1 advertises GR capabilities as a restarting node") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Kill BGP on R2") + + kill_router_daemons(tgen, "r2", ["bgpd"]) + + step( + "Verify that R1 keeps the stale entries in RIB & FIB and R2 keeps stale entries in FIB using" + ) + + for addr_type in ADDR_TYPES: + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, "r2", "r1", addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_rib(tgen, addr_type, "r2", input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + next_hop = next_hop_per_address_family( + tgen, "r1", "r2", addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_bgp_rib(tgen, addr_type, "r1", input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step( + "Bring up BGP on R1 and remove Peer-level GR config" + " from R1 following by a session reset" + ) + + start_router_daemons(tgen, "r2", ["bgpd"]) + + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-helper": False} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-helper": False} + } + } + } + } + }, + } + } + } + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + step("Verify on R2 that R1 advertises GR capabilities as a restarting node") + + input_dict = { + "r1": {"bgp": {"graceful-restart": {"graceful-restart": True}}}, + "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}}, + } + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Kill BGP on R1") + + kill_router_daemons(tgen, "r1", ["bgpd"]) + + step( + "Verify that R1 keeps the stale entries in FIB command and R2 keeps stale entries in RIB & FIB" + ) + + for addr_type in ADDR_TYPES: + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, "r1", "r2", addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_rib(tgen, addr_type, "r1", input_topo, next_hop, protocol) + assert ( + result is True + ), "Testcase {} : Failed \n Routes are still present \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + next_hop = next_hop_per_address_family( + tgen, "r2", "r1", addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_bgp_rib(tgen, addr_type, "r2", input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r2", input_topo, next_hop, protocol) + assert ( + result is True + ), "Testcase {} : Failed \n Routes are still present \n Error {}".format( + tc_name, result + ) + + step("Start BGP on R1") + + start_router_daemons(tgen, "r1", ["bgpd"]) + + write_test_footer(tc_name) + + +def test_BGP_GR_TC_50_p1(request): + """ + Test Objective : Transition from Peer-level helper to Global inherit helper + Global Mode : None + PerPeer Mode : Helper + GR Mode effective : GR Helper + + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step( + "Configure R1 as GR helper node at per Peer-level for R2" + " and configure R2 as global restarting node." + ) + + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + } + } + }, + "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}}, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + step("Verify on R2 that R1 advertises GR capabilities as a helper node") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Kill BGP on R2") + + kill_router_daemons(tgen, "r2", ["bgpd"]) + + step( + "Verify that R2 keeps the stale entries in FIB & R1 keeps stale entries in RIB & FIB" + ) + + for addr_type in ADDR_TYPES: + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, "r2", "r1", addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_rib(tgen, addr_type, "r2", input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + next_hop = next_hop_per_address_family( + tgen, "r1", "r2", addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_bgp_rib(tgen, addr_type, "r1", input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Bring up BGP on R2 and remove Peer-level GR config from R1 ") + + start_router_daemons(tgen, "r2", ["bgpd"]) + + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-helper": False} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-helper": False} + } + } + } + } + }, + } + } + } + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + step("Verify on R2 that R1 still advertises GR capabilities as a helper node") + + input_dict = { + "r1": {"bgp": {"graceful-restart": {"graceful-restart-helper": True}}}, + "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}}, + } + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Kill BGP on R2") + + kill_router_daemons(tgen, "r2", ["bgpd"]) + + step( + "Verify that R2 keeps the stale entries in FIB & R1 keeps stale entries in RIB & FIB" + ) + + for addr_type in ADDR_TYPES: + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, "r2", "r1", addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_rib(tgen, addr_type, "r2", input_topo, next_hop, protocol) + assert ( + result is True + ), "Testcase {} : Failed \n Routes are still present \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + next_hop = next_hop_per_address_family( + tgen, "r1", "r2", addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_bgp_rib(tgen, addr_type, "r1", input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_topo, next_hop, protocol) + assert ( + result is True + ), "Testcase {} : Failed \n Routes are still present \n Error {}".format( + tc_name, result + ) + + step("Start BGP on R2") + + start_router_daemons(tgen, "r2", ["bgpd"]) + + write_test_footer(tc_name) + + +def test_BGP_GR_TC_51_p1(request): + """ + Test Objective : Transition from Peer-level restarting to Global inherit helper + Global Mode : None + PerPeer Mode : GR Restart + GR Mode effective : GR Restart + + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step("Configure R1 as GR restarting node at per Peer-level for R2") + + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart": True} + } + } + } + } + }, + } + } + }, + "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}}, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + step("Verify on R2 that R1 advertises GR capabilities as a restarting node") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Kill BGP on R1") + + kill_router_daemons(tgen, "r1", ["bgpd"]) + + step( + "Verify that R1 keeps the stale entries in FIB & R2 keeps stale entries in RIB & FIB" + ) + + for addr_type in ADDR_TYPES: + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, "r1", "r2", addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_rib(tgen, addr_type, "r1", input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + next_hop = next_hop_per_address_family( + tgen, "r2", "r1", addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_bgp_rib(tgen, addr_type, "r2", input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r2", input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Bring up BGP on R1 and remove Peer-level GR config") + + start_router_daemons(tgen, "r1", ["bgpd"]) + + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart": False} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart": False} + } + } + } + } + }, + } + } + } + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + step("Verify on R2 that R1 advertises GR capabilities as a helper node") + + input_dict = { + "r1": {"bgp": {"graceful-restart": {"graceful-restart-helper": True}}}, + "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}}, + } + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Kill BGPd on R2") + + kill_router_daemons(tgen, "r2", ["bgpd"]) + + step( + "Verify that R2 keeps the stale entries in FIB & R1 keeps stale entries in RIB & FIB" + ) + + for addr_type in ADDR_TYPES: + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, "r2", "r1", addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_rib(tgen, addr_type, "r2", input_topo, next_hop, protocol) + assert ( + result is True + ), "Testcase {} : Failed \n Routes are still present \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + next_hop = next_hop_per_address_family( + tgen, "r1", "r2", addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_bgp_rib(tgen, addr_type, "r1", input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_topo, next_hop, protocol) + assert ( + result is True + ), "Testcase {} : Failed \n Routes are still present \n Error {}".format( + tc_name, result + ) + + step("Start BGP on R2") + + start_router_daemons(tgen, "r2", ["bgpd"]) + + write_test_footer(tc_name) + + +def test_BGP_GR_TC_53_p1(request): + """ + Test Objective : Default GR functional mode is Helper. + Global Mode : None + PerPeer Mode : None + GR Mode effective : GR Helper + + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step("configure R2 as global restarting node") + + input_dict = {"r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}}} + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + step( + "Verify on R2 that R1 advertises GR capabilities as a helper node based on inherit" + ) + + input_dict = { + "r1": {"bgp": {"graceful-restart": {"graceful-restart-helper": True}}}, + "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}}, + } + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Kill BGPd on R2") + + kill_router_daemons(tgen, "r2", ["bgpd"]) + + step( + "Verify that R2 keeps the stale entries in FIB & R1 keeps stale entries in RIB & FIB" + ) + + for addr_type in ADDR_TYPES: + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, "r2", "r1", addr_type, NEXT_HOP_IP_1 + ) + input_topo = {"r1": topo["routers"]["r1"]} + result = verify_rib(tgen, addr_type, "r2", input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + next_hop = next_hop_per_address_family( + tgen, "r1", "r2", addr_type, NEXT_HOP_IP_2 + ) + input_topo = {"r2": topo["routers"]["r2"]} + result = verify_bgp_rib(tgen, addr_type, "r1", input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Start BGP on R2") + + start_router_daemons(tgen, "r2", ["bgpd"]) + + write_test_footer(tc_name) + + +def test_BGP_GR_UTP_1_3_p0(request): + """ + Test Objective : Helper BGP router R1, mark and unmark IPV4 routes + as stale as the restarting router R2 come up within the restart time + + Test Objective : Helper BGP router R1, mark IPV4 routes as stale and + deletes them as the restarting router R2 did-not come up within + restart time. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Create route-map to prefer global next-hop + input_dict = { + "r1": { + "route_maps": { + "rmap_global": [ + {"action": "permit", "set": {"ipv6": {"nexthop": "prefer-global"}}} + ] + } + }, + "r2": { + "route_maps": { + "rmap_global": [ + {"action": "permit", "set": {"ipv6": {"nexthop": "prefer-global"}}} + ] + } + }, + } + result = create_route_maps(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Configure neighbor for route map + input_dict_1 = { + "r1": { + "bgp": { + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": { + "route_maps": [ + { + "name": "rmap_global", + "direction": "in", + } + ] + } + } + } + } + } + } + } + } + }, + "r2": { + "bgp": { + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": { + "route_maps": [ + { + "name": "rmap_global", + "direction": "in", + } + ] + } + } + } + } + } + } + } + } + }, + } + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Configure graceful-restart + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + } + } + }, + "r2": { + "bgp": { + "graceful-restart": {"timer": {"restart-time": GR_RESTART_TIMER}}, + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {"graceful-restart": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {"graceful-restart": True} + } + } + } + } + }, + }, + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r2", peer="r1") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r2", peer="r1" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes + dut = "r1" + peer = "r2" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2, preferred_next_hop="global" + ) + input_topo = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, "bgp") + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info("[Phase 2] : R2 goes for reload ") + + kill_router_daemons(tgen, "r2", ["bgpd"]) + + logger.info( + "[Phase 3] : R2 is still down, restart time 120 sec." + " So time verify the routes are present in BGP RIB" + " and ZEBRA" + ) + + for addr_type in ADDR_TYPES: + # Verifying BGP RIB routes + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2, preferred_next_hop="global" + ) + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + protocol = "bgp" + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info("[Phase 4] : sleep for {} sec".format(GR_RESTART_TIMER)) + sleep(GR_RESTART_TIMER) + + logger.info("[Phase 5] : Verify the routes from r2 ") + + for addr_type in ADDR_TYPES: + # Verifying BGP RIB routes + next_hop = NEXT_HOP_IP_2[addr_type] + input_topo = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, expected=False) + assert result is not True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + logger.info(" Expected behavior: {}".format(result)) + + # Verifying RIB routes + result = verify_rib( + tgen, addr_type, dut, input_topo, next_hop, "bgp", expected=False + ) + assert result is not True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + logger.info(" Expected behavior: {}".format(result)) + + logger.info("[Phase 5] : R2 is about to come up now ") + start_router_daemons(tgen, "r2", ["bgpd"]) + + logger.info("[Phase 5] : R2 is UP Now ! ") + + for addr_type in ADDR_TYPES: + # Verifying GR stats + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r2", peer="r1" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_r_bit(tgen, topo, addr_type, input_dict, dut="r1", peer="r2") + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2, preferred_next_hop="global" + ) + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_BGP_GR_UTP_15_TC_9_p1(request): + """ + Test Objective : Restart BGP router R1, detects it is connected to R2, + which is a helper router. Verify the restart capability i.e. R bit + are sent after R1 reloads and comes back. + + Test Objective : Verify that restarting nodes reset "F" bit while sending + the BGP open messages after it's restarts, when BGP GR is **NOT** enabled. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Checking router status, starting if not running + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + # reset_config_on_routers(tgen) + + # Create route-map to prefer global next-hop + input_dict = { + "r1": { + "route_maps": { + "rmap_global": [ + {"action": "permit", "set": {"ipv6": {"nexthop": "prefer-global"}}} + ] + } + }, + "r2": { + "route_maps": { + "rmap_global": [ + {"action": "permit", "set": {"ipv6": {"nexthop": "prefer-global"}}} + ] + } + }, + } + result = create_route_maps(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Configure neighbor for route map + input_dict_1 = { + "r1": { + "bgp": { + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": { + "route_maps": [ + { + "name": "rmap_global", + "direction": "in", + } + ] + } + } + } + } + } + } + } + } + }, + "r2": { + "bgp": { + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": { + "route_maps": [ + { + "name": "rmap_global", + "direction": "in", + } + ] + } + } + } + } + } + } + } + } + }, + } + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + logger.info( + "[Phase 1] : Test Setup " "[Helper Mode]R1-----R2[Restart Mode] initialized " + ) + + # Configure graceful-restart + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart": True} + } + } + } + } + }, + } + } + }, + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + } + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes + dut = "r1" + peer = "r2" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2, preferred_next_hop="global" + ) + input_topo = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + protocol = "bgp" + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info("[Phase 2] : R1 goes for reload ") + + kill_router_daemons(tgen, "r1", ["bgpd"]) + + logger.info("[Phase 6] : R1 is about to come up now ") + start_router_daemons(tgen, "r1", ["bgpd"]) + + for addr_type in ADDR_TYPES: + # Verifying GR stats + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_r_bit(tgen, topo, addr_type, input_dict, dut="r2", peer="r1") + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2, preferred_next_hop="global" + ) + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_r_bit(tgen, topo, addr_type, input_dict, dut="r1", peer="r2") + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_f_bit( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2", expected=False + ) + assert result is not True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_BGP_GR_UTP_35_p1(request): + """ + Test Objective : Restart BGP router R1 connected to R2, + which is a restart router. + R1 should not send any GR capability in the open message, + however it would process open message from R2 with GR -restart + capability, but would not perform any BGP GR functionality. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + logger.info( + "[Phase 1] : Test Setup" " [Disable Mode]R1-----R2[Restart Mode] initialized " + ) + + # Configure graceful-restart + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-disable": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-disable": True} + } + } + } + } + }, + } + } + }, + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {"graceful-restart": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {"graceful-restart": True} + } + } + } + } + }, + } + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes + dut = "r1" + peer = "r2" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + protocol = "bgp" + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info("[Phase 2] : R1 goes for reload ") + + kill_router_daemons(tgen, "r1", ["bgpd"]) + + logger.info("[Phase 3] : R1 is about to come up now ") + start_router_daemons(tgen, "r1", ["bgpd"]) + + logger.info("[Phase 4] : R2 is UP now, so time to collect GR stats ") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_BGP_GR_TC_4_p0(request): + """ + Test Objective : Verify that the restarting node sets "R" bit while sending the + BGP open messages after the node restart, only if GR is enabled. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + logger.info( + "[Phase 1] : Test Setup" " [Restart Mode]R1-----R2[Helper Mode] initialized " + ) + + # Configure graceful-restart + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart": True} + } + } + } + } + }, + } + } + }, + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + } + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes + dut = "r1" + peer = "r2" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + protocol = "bgp" + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info("[Phase 2] : R2 goes for reload ") + + kill_router_daemons(tgen, "r2", ["bgpd"]) + + logger.info( + "[Phase 3] : R2 is still down, restart time {} sec." + "So time verify the routes are present in BGP RIB and ZEBRA ".format( + GR_RESTART_TIMER + ) + ) + + for addr_type in ADDR_TYPES: + # Verifying BGP RIB routes + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib( + tgen, addr_type, dut, input_topo, next_hop, expected=False + ) + assert result is not True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + logger.info(" Expected behavior: {}".format(result)) + + # Verifying RIB routes + result = verify_rib( + tgen, addr_type, dut, input_topo, next_hop, protocol, expected=False + ) + assert result is not True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + logger.info(" Expected behavior: {}".format(result)) + + logger.info("[Phase 5] : R2 is about to come up now ") + start_router_daemons(tgen, "r2", ["bgpd"]) + + logger.info("[Phase 4] : R2 is UP now, so time to collect GR stats ") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_r_bit(tgen, topo, addr_type, input_dict, dut="r1", peer="r2") + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_BGP_GR_TC_5_1_2_p1(request): + """ + Test Objective : Verify if restarting node resets R bit in BGP open message + during normal BGP session flaps as well, even when GR restarting mode is enabled. + Here link flap happen due to interface UP/DOWN. + + """ + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + logger.info( + "[Phase 1] : Test Setup" " [Restart Mode]R1-----R2[Restart Mode] initialized " + ) + + # Configure graceful-restart + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart": True} + } + } + } + } + }, + } + } + }, + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {"graceful-restart": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {"graceful-restart": True} + } + } + } + } + }, + } + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes + dut = "r1" + peer = "r2" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + protocol = "bgp" + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info("[Phase 2] : Now flap the link running the BGP session ") + # Shutdown interface + intf = "r2-r1-eth0" + shutdown_bringup_interface(tgen, "r2", intf) + + # Bring up Interface + shutdown_bringup_interface(tgen, dut, intf, ifaceaction=True) + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_r_bit(tgen, topo, addr_type, input_dict, dut="r1", peer="r2") + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info("[Phase 2] : Restart BGPd on router R2. ") + kill_router_daemons(tgen, "r2", ["bgpd"]) + + start_router_daemons(tgen, "r2", ["bgpd"]) + + logger.info("[Phase 4] : R2 is UP now, so time to collect GR stats ") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_r_bit(tgen, topo, addr_type, input_dict, dut="r1", peer="r2") + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_BGP_GR_TC_6_1_2_p1(request): + """ + Test Objective : Verify if restarting node resets R bit in BGP + open message during normal BGP session flaps when GR is disabled. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + logger.info( + "[Phase 1] : Test Setup" "[Restart Mode]R1-----R2[Helper Mode] initialized " + ) + + # Configure graceful-restart + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart": True} + } + } + } + } + }, + } + } + }, + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + } + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes + dut = "r1" + peer = "r2" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + protocol = "bgp" + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info("[Phase 1] : Changing mode" "[Disable Mode]R1-----R2[Helper Mode]") + + # Configure graceful-restart + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-disable": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-disable": True} + } + } + } + } + }, + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + clear_bgp(tgen, addr_type, "r1") + clear_bgp(tgen, addr_type, "r2") + + # Verify GR stats + input_dict = { + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + } + } + }, + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-disable": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-disable": True} + } + } + } + } + }, + } + } + }, + } + + # here the verify_graceful_restart fro the neighbor would be + # "NotReceived" as the latest GR config is not yet applied. + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info("[Phase 2] : Now flap the link running the BGP session ") + # Shutdown interface + intf = "r2-r1-eth0" + shutdown_bringup_interface(tgen, "r2", intf) + + # Bring up Interface + shutdown_bringup_interface(tgen, dut, intf, ifaceaction=True) + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_r_bit( + tgen, topo, addr_type, input_dict, dut="r2", peer="r1", expected=False + ) + assert result is not True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info("Restart BGPd on R2 ") + kill_router_daemons(tgen, "r2", ["bgpd"]) + + start_router_daemons(tgen, "r2", ["bgpd"]) + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_r_bit( + tgen, topo, addr_type, input_dict, dut="r2", peer="r1", expected=False + ) + assert result is not True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_BGP_GR_TC_8_p1(request): + """ + Test Objective : Verify that restarting nodes set "F" bit while sending + the BGP open messages after it restarts, only when BGP GR is enabled. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + logger.info( + "[Phase 1] : Test Setup" " [Restart Mode]R1-----R2[Restart Mode] initialized " + ) + + # Configure graceful-restart + input_dict = { + "r1": { + "bgp": { + "graceful-restart": {"preserve-fw-state": True}, + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart": True} + } + } + } + } + }, + }, + } + }, + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {"graceful-restart": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {"graceful-restart": True} + } + } + } + } + }, + } + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes + dut = "r1" + peer = "r2" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + protocol = "bgp" + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info("[Phase 2] : R1 goes for reload ") + + kill_router_daemons(tgen, "r1", ["bgpd"]) + + logger.info("[Phase 3] : R1 is about to come up now ") + start_router_daemons(tgen, "r1", ["bgpd"]) + + logger.info("[Phase 4] : R2 is UP now, so time to collect GR stats ") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_r_bit(tgen, topo, addr_type, input_dict, dut="r2", peer="r1") + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_f_bit(tgen, topo, addr_type, input_dict, dut="r2", peer="r1") + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_BGP_GR_TC_17_p1(request): + """ + Test Objective : Verify that only GR helper routers keep the stale + route entries, not any GR disabled router. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + logger.info("[Phase 1] : Test Setup [Disable]R1-----R2[Restart] initialized ") + + # Configure graceful-restart + input_dict = { + "r1": { + "bgp": { + "graceful-restart": { + "graceful-restart": True, + "preserve-fw-state": True, + }, + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-disable": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-disable": True} + } + } + } + } + }, + }, + } + }, + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {"graceful-restart": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {"graceful-restart": True} + } + } + } + } + }, + } + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes + dut = "r1" + peer = "r2" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + protocol = "bgp" + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info("[Phase 2] : R2 goes for reload ") + + kill_router_daemons(tgen, "r2", ["bgpd"]) + + logger.info( + "[Phase 3] : R2 is still down, restart time 120 sec." + " So time verify the routes are present in BGP RIB and ZEBRA " + ) + + for addr_type in ADDR_TYPES: + # Verifying BGP RIB routes + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib( + tgen, addr_type, dut, input_topo, next_hop, expected=False + ) + assert result is not True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + logger.info(" Expected behavior: {}".format(result)) + + # Verifying RIB routes + result = verify_rib( + tgen, addr_type, dut, input_topo, next_hop, protocol, expected=False + ) + assert result is not True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + logger.info(" Expected behavior: {}".format(result)) + + logger.info("[Phase 5] : R2 is about to come up now ") + start_router_daemons(tgen, "r2", ["bgpd"]) + + logger.info("[Phase 4] : R2 is UP now, so time to collect GR stats ") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_r_bit( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2", expected=False + ) + assert result is not True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_BGP_GR_TC_19_p1(request): + """ + Test Objective : Verify that GR helper routers keeps all the routes received + from restarting node if both the routers are configured as GR restarting node. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + logger.info("[Phase 1] : Test Setup [Helper]R1-----R2[Restart] initialized ") + + # Configure graceful-restart + input_dict = { + "r1": { + "bgp": { + "graceful-restart": { + "graceful-restart": True, + "preserve-fw-state": True, + }, + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + }, + } + }, + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {"graceful-restart": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {"graceful-restart": True} + } + } + } + } + }, + } + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes + dut = "r1" + peer = "r2" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + protocol = "bgp" + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info( + "[Phase 2] : R1's Gr state cahnge to Graceful" + " Restart without resetting the session " + ) + + # Configure graceful-restart + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart": True} + } + } + } + } + }, + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + logger.info( + "[Phase 3] : R2 is still down, restart time 120 sec." + " So time verify the routes are present in BGP RIB and ZEBRA " + ) + + for addr_type in ADDR_TYPES: + # Verifying BGP RIB routes + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_BGP_GR_TC_20_p1(request): + """ + Test Objective : Verify that GR helper routers delete all the routes + received from a node if both the routers are configured as GR helper node. + """ + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + logger.info("[Phase 1] : Test Setup [Helper]R1-----R2[Helper] initialized ") + + # Configure graceful-restart + input_dict = { + "r1": { + "bgp": { + "graceful-restart": { + "graceful-restart": True, + "preserve-fw-state": True, + }, + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + }, + } + }, + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + } + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes + dut = "r1" + peer = "r2" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + protocol = "bgp" + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + kill_router_daemons(tgen, "r2", ["bgpd"]) + + for addr_type in ADDR_TYPES: + # Verifying BGP RIB routes + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib( + tgen, addr_type, dut, input_topo, next_hop, expected=False + ) + assert result is not True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + logger.info(" Expected behavior: {}".format(result)) + + # Verifying RIB routes + result = verify_rib( + tgen, addr_type, dut, input_topo, next_hop, protocol, expected=False + ) + assert result is not True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + logger.info(" Expected behavior: {}".format(result)) + + logger.info("[Phase 5] : R2 is about to come up now ") + + start_router_daemons(tgen, "r2", ["bgpd"]) + + logger.info("[Phase 4] : R2 is UP now, so time to collect GR stats ") + + for addr_type in ADDR_TYPES: + # Verifying BGP RIB routes + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_BGP_GR_TC_31_1_p1(request): + """ + After BGP neighborship is established and GR capability is exchanged, + transition restarting router to disabled state and vice versa. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + logger.info( + "[Phase 1] : Test Setup" " [Helper Mode]R2-----R1[Restart Mode] initialized " + ) + + # Configure graceful-restart + input_dict = { + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + } + } + }, + "r1": { + "bgp": { + "graceful-restart": {"preserve-fw-state": True}, + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart": True} + } + } + } + } + }, + }, + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes + dut = "r1" + peer = "r2" + protocol = "bgp" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info("[Phase 2] : R1 Goes from Restart to Disable Mode ") + + # Configure graceful-restart + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-disable": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-disable": True} + } + } + } + } + }, + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + clear_bgp(tgen, addr_type, "r1") + clear_bgp(tgen, addr_type, "r2") + + # Verify GR stats + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-disable": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-disable": True} + } + } + } + } + }, + } + } + }, + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + } + } + }, + } + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info("[Phase 2] : R1 goes for reload ") + + kill_router_daemons(tgen, "r1", ["bgpd"]) + + logger.info( + "[Phase 3] : R1 is still down, restart time 120 sec." + " So time verify the routes are not present in BGP RIB and ZEBRA" + ) + + for addr_type in ADDR_TYPES: + # Verifying RIB routes + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {key: topo["routers"][key] for key in ["r2"]} + result = verify_rib( + tgen, addr_type, dut, input_topo, next_hop, protocol, expected=False + ) + assert result is not True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info("[Phase 4] : R1 is about to come up now ") + start_router_daemons(tgen, "r1", ["bgpd"]) + + logger.info("[Phase 5] : R1 is UP now, so time to collect GR stats ") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_BGP_GR_TC_31_2_p1(request): + """ + After BGP neighborship is established and GR capability is exchanged, + transition restarting router to disabled state and vice versa. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + logger.info( + "[Phase 1] : Test Setup " "[Disable Mode]R1-----R2[Restart Mode] initialized " + ) + + # Configure graceful-restart + input_dict = { + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + } + } + }, + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-disable": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart-disable": True} + } + } + } + } + }, + } + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes + dut = "r1" + peer = "r2" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + protocol = "bgp" + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info("[Phase 2] : R2 Goes from Disable to Restart Mode ") + + # Configure graceful-restart + input_dict = { + "r1": { + "bgp": { + "graceful-restart": {"preserve-fw-state": True}, + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart": True} + } + } + } + } + }, + }, + } + } + } + + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + clear_bgp(tgen, addr_type, "r1") + clear_bgp(tgen, addr_type, "r2") + + # Verify GR stats + input_dict = { + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {"graceful-restart-helper": True} + } + } + } + } + }, + } + } + }, + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"graceful-restart": True} + } + } + } + } + }, + } + } + }, + } + + # here the verify_graceful_restart fro the neighbor would be + # "NotReceived" as the latest GR config is not yet applied. + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info("[Phase 3] : R1 goes for reload ") + + kill_router_daemons(tgen, "r1", ["bgpd"]) + + logger.info( + "[Phase 4] : R1 is still down, restart time 120 sec." + " So time verify the routes are present in BGP RIB and ZEBRA " + ) + + for addr_type in ADDR_TYPES: + # Verifying RIB routes + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {key: topo["routers"][key] for key in ["r2"]} + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info("[Phase 6] : R1 is about to come up now ") + start_router_daemons(tgen, "r1", ["bgpd"]) + + logger.info("[Phase 4] : R1 is UP now, so time to collect GR stats ") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2 + ) + input_topo = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_gr_functionality_topo2/bgp_gr_topojson_topo2.json b/tests/topotests/bgp_gr_functionality_topo2/bgp_gr_topojson_topo2.json new file mode 100644 index 0000000000..75f192d3b6 --- /dev/null +++ b/tests/topotests/bgp_gr_functionality_topo2/bgp_gr_topojson_topo2.json @@ -0,0 +1,334 @@ +{ + "ipv4base":"192.168.0.0", + "ipv4mask":24, + "ipv6base":"fd00::", + "ipv6mask":64, + "link_ip_start":{"ipv4":"192.168.0.0", "v4mask":24, "ipv6":"fd00::", "v6mask":64}, + "lo_prefix":{"ipv4":"1.0.", "v4mask":32, "ipv6":"2001:DB8:F::", "v6mask":128}, + "routers":{ + "r1":{ + "links":{ + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2": {"ipv4":"auto", "ipv6":"auto"}, + "r3": {"ipv4":"auto", "ipv6":"auto"}, + "r5": {"ipv4":"auto", "ipv6":"auto"}, + "r6": {"ipv4":"auto", "ipv6":"auto"} + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + { + "network": "101.0.20.1/32", + "no_of_network": 5 + } + ], + "neighbor": { + "r2": { + "dest_link": { + "r1": {} + } + }, + "r3": { + "dest_link": { + "r1": {} + } + }, + "r5": { + "dest_link": { + "r1": {} + } + }, + "r6": { + "dest_link": { + "r1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + { + "network": "1::1/128", + "no_of_network": 5 + } + ], + "neighbor": { + "r2": { + "dest_link": { + "r1": { + } + } + }, + "r3": { + "dest_link": { + "r1": { + } + } + }, + "r5": { + "dest_link": { + "r1": { + } + } + }, + "r6": { + "dest_link": { + "r1": { + } + } + } + } + } + } + } + } + }, + "r2":{ + "links":{ + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1": {"ipv4":"auto", "ipv6":"auto"} + }, + "bgp":{ + "local_as": "200", + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + { + "network": "102.0.20.1/32", + "no_of_network": 5 + } + ], + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + { + "network": "2::1/128", + "no_of_network": 5 + } + ], + "neighbor": { + "r1": { + "dest_link": { + "r2": { + } + } + } + } + } + } + } + } + }, + "r3":{ + "links":{ + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1": {"ipv4":"auto", "ipv6":"auto"}, + "r4": {"ipv4":"auto", "ipv6":"auto"} + }, + "bgp":{ + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + { + "network": "103.0.20.1/32", + "no_of_network": 5 + } + ], + "neighbor": { + "r1": { + "dest_link": { + "r3": {} + } + }, + "r4": { + "dest_link": { + "r3": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + { + "network": "3::1/128", + "no_of_network": 5 + } + ], + "neighbor": { + "r1": { + "dest_link": { + "r3": { + } + } + }, + "r4": { + "dest_link": { + "r3": { + } + } + } + } + } + } + } + } + }, + "r4":{ + "links":{ + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r3": {"ipv4":"auto", "ipv6":"auto"} + }, + "bgp":{ + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + { + "network": "104.0.20.1/32", + "no_of_network": 5 + } + ], + "neighbor": { + "r3": { + "dest_link": { + "r4": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + { + "network": "4::1/128", + "no_of_network": 5 + } + ], + "neighbor": { + "r3": { + "dest_link": { + "r4": { + } + } + } + } + } + } + } + } + }, + "r5":{ + "links":{ + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1": {"ipv4":"auto", "ipv6":"auto"} + }, + "bgp":{ + "local_as": "500", + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + { + "network": "105.0.20.1/32", + "no_of_network": 5 + } + ], + "neighbor": { + "r1": { + "dest_link": { + "r5": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + { + "network": "5::1/128", + "no_of_network": 5 + } + ], + "neighbor": { + "r1": { + "dest_link": { + "r5": { + } + } + } + } + } + } + } + } + }, + "r6":{ + "links":{ + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1": {"ipv4":"auto", "ipv6":"auto"} + }, + "bgp":{ + "local_as": "600", + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + { + "network": "106.0.20.1/32", + "no_of_network": 5 + } + ], + "neighbor": { + "r1": { + "dest_link": { + "r6": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + { + "network": "6::1/128", + "no_of_network": 5 + } + ], + "neighbor": { + "r1": { + "dest_link": { + "r6": { + } + } + } + } + } + } + } + } + } + } +} 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 new file mode 100755 index 0000000000..e1ec0ea81b --- /dev/null +++ b/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2.py @@ -0,0 +1,3091 @@ +#!/usr/bin/env python +# +# Copyright (c) 2019 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. ("NetDEF") +# in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +Following tests are covered to test BGP Graceful Restart functionality. +Basic Common Test steps for all the test case below : +- Create topology (setup module) + Creating 7 routers topology +- Bring up topology +- Verify for bgp to converge +- Configure BGP Graceful Restart on both the routers. + +TC_1_2: + Verify that EOR message is sent out only after initial convergence + Verify whether EOR message is received from all the peers after restart +TC_3: + Verify the selection deferral timer functionality when EOR is not sent + by the helper router +TC_11: + Verify that selection-deferral timer sets the maximum time to + avoid deadlock during which the best-path +TC_10: + Test Objective : Test GR scenarios on helper router by enabling + Graceful Restart for multiple address families. +TC_15: + Test Objective : Test GR scenarios by enabling Graceful Restart + for multiple address families.. +TC_16: + Test Objective : Verify BGP-GR feature when restarting node + is a transit router for it's iBGP peers. +TC_18: + Test Objective : Verify that GR helper router deletes stale routes + received from restarting node, if GR capability is not present in +TC_19: + Test Objective : Verify that GR routers keeps all the routes + received from restarting node if both the routers are +TC_26: + Test Objective : Test GR scenarios on helper router by enabling + Graceful Restart for multiple address families. +TC_28: + Test Objective : Verify if helper node goes down before restarting + node comes up online, helper node sets the R-bit to avoid dead-lock +TC_29: + Test Objective : Change timers on the fly, and + verify if it takes immediate effect. +TC_33: + Test Objective : Helper router receives same prefixes from two + different routers (GR-restarting and GR-disabled). Keeps the +TC_34_1: + Test Objective : Restarting node doesn't preserve forwarding + state, helper router should not keep the stale entries. +TC_34_2: + Test Objective : Restarting node doesn't preserve the forwarding + state verify the behaviour on helper node, if it still keeps the +TC_32: + Test Objective : Restarting node is connected to multiple helper + nodes, one of them doesn't send EOR to restarting router. Verify +TC_37: + Test Objective : Verify if helper node restarts before sending the + EOR message, restarting node doesn't wait until stale path timer +TC_30: + Test Objective : Restarting node removes stale routes from Zebra + after receiving an EOR from helper router. + +""" + +import os +import sys +import json +import time +import pytest +from time import sleep +from copy import deepcopy + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join("../")) +sys.path.append(os.path.join("../lib/")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + +# Import topoJson from lib, to create topology and initial configuration +from lib.topojson import build_topo_from_json, build_config_from_json +from lib.bgp import ( + clear_bgp, + verify_bgp_rib, + verify_graceful_restart, + create_router_bgp, + verify_r_bit, + verify_eor, + verify_f_bit, + verify_bgp_convergence, + verify_gr_address_family, + modify_bgp_config_when_bgpd_down, + verify_graceful_restart_timers, +) + +from lib.common_config import ( + write_test_header, + reset_config_on_routers, + start_topology, + kill_router_daemons, + start_router_daemons, + verify_rib, + check_address_types, + write_test_footer, + check_router_status, + shutdown_bringup_interface, + step, + kill_mininet_routers_process, + get_frr_ipv6_linklocal, + create_route_maps, + required_linux_kernel_version +) + +# Reading the data from JSON File for topology and configuration creation +jsonFile = "{}/bgp_gr_topojson_topo2.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + logger.info("Could not read file:", jsonFile) + +# Global variables +BGP_CONVERGENCE = False +GR_RESTART_TIMER = 5 +GR_SELECT_DEFER_TIMER = 5 +PREFERRED_NEXT_HOP = "link_local" +NEXT_HOP_4 = ["192.168.1.1", "192.168.4.2"] +NEXT_HOP_6 = ["fd00:0:0:1::1", "fd00:0:0:4::2"] + + +class GenerateTopo(Topo): + """ + Test topology builder + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # This function only purpose is to create topology + # as defined in input json file. + # + # Create topology (setup module) + # Creating 2 routers topology, r1, r2in IBGP + # Bring up topology + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version('4.15') + if result is not True: + pytest.skip("Kernel requirements are not met") + + global ADDR_TYPES + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(GenerateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Api call verify whether BGP is converged + ADDR_TYPES = check_address_types() + + for addr_type in ADDR_TYPES: + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error:" " {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """ + Teardown the pytest environment + + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +def configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut, peer): + """ + This function groups the repetitive function calls into one function. + """ + + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + clear_bgp(tgen, addr_type, dut) + + return True + + +def next_hop_per_address_family(tgen, dut, peer, addr_type, next_hop_dict): + """ + This function returns link_local or global next_hop per address-family + """ + + intferface = topo["routers"][peer]["links"]["{}-link1".format(dut)]["interface"] + if addr_type == "ipv6" and "link_local" in PREFERRED_NEXT_HOP: + next_hop = get_frr_ipv6_linklocal(tgen, peer, intf=intferface) + else: + next_hop = next_hop_dict[addr_type] + + return next_hop + + +def test_BGP_GR_TC_1_2_p0(request): + """ + Verify that EOR message is sent out only after initial convergence + Verify whether EOR message is received from all the peers after restart + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + logger.info( + "Verify EOR Sent and Received : BGP_GR_TC_1_2 >> " + "BGP GR [Helper Mode]R3-----R1[Restart Mode] " + ) + + # Configure graceful-restart + input_dict = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {"graceful-restart-helper": True} + } + } + } + } + }, + } + } + }, + "r1": { + "bgp": { + "graceful-restart": { + "graceful-restart": True, + "preserve-fw-state": True, + }, + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r1": {"graceful-restart": True}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r1": {"graceful-restart": True}}} + } + } + }, + }, + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r3") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r3" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes received from router R3 + dut = "r1" + input_dict_1 = {key: topo["routers"][key] for key in ["r3"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info("R1 goes for reload") + kill_router_daemons(tgen, "r1", ["bgpd"]) + + for addr_type in ADDR_TYPES: + # Verifying RIB routes + input_dict_1 = {key: topo["routers"][key] for key in ["r3"]} + # Verifying RIB routes + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info("Starting bgpd process") + start_router_daemons(tgen, "r1", ["bgpd"]) + logger.info("R1 is UP Now") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r3" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes received from router R3 + input_dict_1 = {key: topo["routers"][key] for key in ["r3"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying EOR on restarting router + result = verify_eor(tgen, topo, addr_type, input_dict, dut="r3", peer="r1") + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_BGP_GR_TC_3_p0(request): + """ + Verify the selection deferral timer functionality when EOR is not sent + by the helper router + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + logger.info( + " Verify route download to RIB: BGP_GR_TC_3 >> " + "BGP GR [Helper Mode]R1-----R2[Restart Mode] " + ) + + # Configure graceful-restart + input_dict = { + "r1": { + "bgp": { + "graceful-restart": {"disable-eor": True,}, + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {"graceful-restart-helper": True} + } + } + } + } + }, + }, + } + }, + "r2": { + "bgp": { + "graceful-restart": { + "graceful-restart": True, + "preserve-fw-state": True, + "timer": {"select-defer-time": GR_SELECT_DEFER_TIMER}, + }, + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r2": {"graceful-restart": True}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r2": {"graceful-restart": True}}} + } + } + }, + }, + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes received from router R1 + dut = "r2" + input_dict_1 = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info("R2 goes for reload ") + kill_router_daemons(tgen, "r2", ["bgpd"]) + + logger.info("R2 is about to come up now") + start_router_daemons(tgen, "r2", ["bgpd"]) + logger.info("R2 is UP Now") + + for addr_type in ADDR_TYPES: + # Verifying BGP RIB routes received from router R1 + input_dict_1 = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verify EOR on restarting router + result = verify_eor( + tgen, topo, addr_type, input_dict, dut="r2", peer="r1", expected=False + ) + assert result is not True, ( + "Testcase " + tc_name + " : Failed \n Error: {}".format(result) + ) + + logger.info( + "Waiting for selection deferral timer({} sec)..".format(GR_SELECT_DEFER_TIMER) + ) + sleep(GR_SELECT_DEFER_TIMER) + + for addr_type in ADDR_TYPES: + # Verifying RIB routes + result = verify_rib(tgen, addr_type, "r2", input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_BGP_GR_TC_11_p0(request): + """ + Verify that selection-deferral timer sets the maximum time to + avoid deadlock during which the best-path + selection process is deferred, after a peer session was restarted + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + logger.info("Verify EOR Sent after deferral timeout : BGP_GR_TC_11") + + # Configure graceful-restart + input_dict = { + "r1": { + "bgp": { + "graceful-restart": { + "graceful-restart": True, + "select-defer-time": GR_SELECT_DEFER_TIMER, + }, + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": {"dest_link": {"r1": {"graceful-restart": True}}}, + "r3": {"dest_link": {"r1": {"graceful-restart": True}}}, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": {"dest_link": {"r1": {"graceful-restart": True}}}, + "r3": {"dest_link": {"r1": {"graceful-restart": True}}}, + } + } + }, + }, + } + }, + "r3": { + "bgp": { + "graceful-restart": {"disable-eor": True}, + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {"graceful-restart-helper": True} + } + } + } + } + }, + }, + } + }, + } + + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + clear_bgp(tgen, addr_type, "r1") + clear_bgp(tgen, addr_type, "r3") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r3" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes received from router R1 + dut = "r1" + input_dict_1 = {key: topo["routers"][key] for key in ["r3"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info("R1 goes for reload") + kill_router_daemons(tgen, "r1", ["bgpd"]) + + logger.info("Starting bgpd process") + start_router_daemons(tgen, "r1", ["bgpd"]) + logger.info("R1 is UP Now") + + for addr_type in ADDR_TYPES: + # Verify EOR on restarting router + result = verify_eor( + tgen, topo, addr_type, input_dict, dut="r1", peer="r3", expected=False + ) + assert result is not True, ( + "Testcase " + tc_name + " : Failed \n Error: {}".format(result) + ) + + logger.info( + "Waiting for selection deferral timer({} sec).. ".format( + GR_SELECT_DEFER_TIMER + 2 + ) + ) + sleep(GR_SELECT_DEFER_TIMER + 2) + + for addr_type in ADDR_TYPES: + # Verifying BGP RIB routes received from router R1 + input_dict_1 = {key: topo["routers"][key] for key in ["r3"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying EOR on restarting router + result = verify_eor( + tgen, topo, addr_type, input_dict, dut="r3", peer="r1", expected=False + ) + assert result is not True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_BGP_GR_10_p2(request): + """ + Test Objective : Test GR scenarios on helper router by enabling + Graceful Restart for multiple address families. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + logger.info( + "[Step 1] : Test Setup " "[Helper Mode]R3-----R1[Restart Mode] initialized" + ) + + # Configure graceful-restart + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1": { + "next_hop_self": True, + "graceful-restart": True, + "activate": "ipv6", + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1": { + "next_hop_self": True, + "graceful-restart": True, + "activate": "ipv4", + } + } + } + } + } + }, + } + } + }, + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "graceful-restart-helper": True, + "activate": "ipv6", + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "graceful-restart-helper": True, + "activate": "ipv4", + } + } + } + } + } + }, + } + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r3") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r3" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes + dut = "r3" + input_topo = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_topo) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # verify multi address family + result = verify_gr_address_family( + tgen, topo, addr_type, "ipv4Unicast", dut="r1" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # verify multi address family + result = verify_gr_address_family( + tgen, topo, addr_type, "ipv6Unicast", dut="r1" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # verify multi address family + result = verify_gr_address_family( + tgen, topo, addr_type, "ipv4Unicast", dut="r3" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # verify multi address family + result = verify_gr_address_family( + tgen, topo, addr_type, "ipv6Unicast", dut="r3" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Kill BGPd daemon on R1 + kill_router_daemons(tgen, "r1", ["bgpd"]) + + for addr_type in ADDR_TYPES: + # Verifying BGP RIB routes + input_topo = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_topo) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Start BGPd daemon on R1 + start_router_daemons(tgen, "r1", ["bgpd"]) + + for addr_type in ADDR_TYPES: + # Verifying BGP RIB routes + input_topo = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_topo) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_BGP_GR_16_p2(request): + """ + Test Objective : Verify BGP-GR feature when restarting node + is a transit router for it's iBGP peers. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + logger.info( + "[Step 1] : Test Setup " "[Helper Mode]R3-----R1[Restart Mode] initialized" + ) + + # Configure graceful-restart and timers + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1": { + "graceful-restart": True, + "next_hop_self": True, + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1": { + "graceful-restart": True, + "next_hop_self": True, + } + } + } + } + } + }, + } + } + }, + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {"graceful-restart-helper": True} + } + } + } + } + }, + } + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r3") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r3" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info( + "[Step 2] : Test Setup " + "[Helper Mode]R3-----R1[Restart Mode]" + "--------R6[Helper Mode] initialized" + ) + + # Configure graceful-restart and timers + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": {"dest_link": {"r1": {"graceful-restart": True}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": {"dest_link": {"r1": {"graceful-restart": True}}} + } + } + }, + } + } + }, + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {"graceful-restart-helper": True} + } + } + } + } + }, + } + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes + dut = "r3" + input_dict_1 = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes + input_dict_2 = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Kill BGPd daemon on R1 + kill_router_daemons(tgen, "r1", ["bgpd"]) + + for addr_type in ADDR_TYPES: + # Verifying BGP RIB routes + input_dict_1 = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes + input_dict_2 = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Start BGPd daemon on R1 + start_router_daemons(tgen, "r1", ["bgpd"]) + + for addr_type in ADDR_TYPES: + # Verifying BGP RIB routes + input_dict_1 = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes + input_dict_2 = {key: topo["routers"][key] for key in ["r2"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_BGP_GR_18_p1(request): + """ + Test Objective : Verify that GR helper router deletes stale routes + received from restarting node, if GR capability is not present in + restarting node's OPEN message. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + logger.info( + "[Step 1] : Test Setup " "[Helper Mode]R6-----R1[Restart Mode] initialized" + ) + + # Configure graceful-restart and timers + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r6": {"dest_link": {"r1": {"graceful-restart": True}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r6": {"dest_link": {"r1": {"graceful-restart": True}}} + } + } + }, + } + } + }, + "r6": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r6": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r6": {"graceful-restart-helper": True} + } + } + } + } + }, + } + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r6") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r6" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info( + "[Step 2] : Test Setup " + "[Helper Mode]R6-----R1[Restart Mode]" + "--------R2[Helper Mode] initialized" + ) + + # Configure graceful-restart and timers + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": {"dest_link": {"r1": {"graceful-restart": True}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": {"dest_link": {"r1": {"graceful-restart": True}}} + } + } + }, + } + } + }, + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {"graceful-restart-helper": True} + } + } + } + } + }, + } + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r2" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes + dut = "r6" + input_dict_1 = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes + dut = "r2" + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Kill BGPd daemon on R1 + kill_router_daemons(tgen, "r1", ["bgpd"]) + + logger.info("[Step 3] : Configure R1 to prevent sending EOR") + + # Modify graceful-restart config to prevent sending EOR + input_dict_3 = {"r1": {"bgp": {"graceful-restart": {"disable-eor": True}}}} + + result = modify_bgp_config_when_bgpd_down(tgen, topo, input_dict_3) + + # Modify configuration to delete routes + network = {"ipv4": "101.0.20.1/32", "ipv6": "1::1/128"} + for addr_type in ADDR_TYPES: + input_dict_3 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "advertise_networks": [ + { + "network": network[addr_type], + "no_of_network": 5, + "delete": True, + } + ] + } + } + } + } + } + } + + result = modify_bgp_config_when_bgpd_down(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Modify graceful-restart config + input_dict_3 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {"graceful-restart-disable": True} + } + }, + "r6": { + "dest_link": { + "r1": {"graceful-restart-disable": True} + } + }, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {"graceful-restart-disable": True} + } + }, + "r6": { + "dest_link": { + "r1": {"graceful-restart-disable": True} + } + }, + } + } + }, + } + } + } + } + + result = modify_bgp_config_when_bgpd_down(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error {}".format(tc_name, result) + + logger.info("[Step 4] : Bring up the BGPd daemon on R1 for 30" " seconds..") + + # Start BGPd daemon on R1 + start_router_daemons(tgen, "r1", ["bgpd"]) + + for addr_type in ADDR_TYPES: + # Verifying BGP RIB routes + dut = "r6" + input_dict_1 = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1, expected=False) + assert result is not True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + logger.info(" Expected behavior: {}".format(result)) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_dict_1, expected=False) + assert result is not True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + logger.info(" Expected behavior: {}".format(result)) + + # Verifying BGP RIB routes + dut = "r2" + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1, expected=False) + assert result is not True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + logger.info(" Expected behavior: {}".format(result)) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_dict_1, expected=False) + assert result is not True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + logger.info(" Expected behavior: {}".format(result)) + + write_test_footer(tc_name) + + +def test_BGP_GR_26_p2(request): + """ + Test Objective : Test GR scenarios on helper router by enabling + Graceful Restart for multiple address families. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + logger.info( + "[Step 1] : Test Setup " "[Helper Mode]R3-----R1[Restart Mode] initialized" + ) + + # Configure graceful-restart + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1": { + "graceful-restart": True, + "next_hop_self": True, + "activate": "ipv6", + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1": { + "graceful-restart": True, + "next_hop_self": True, + "activate": "ipv4", + } + } + } + } + } + }, + } + } + }, + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "graceful-restart-helper": True, + "activate": "ipv6", + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "graceful-restart-helper": True, + "activate": "ipv4", + } + } + } + } + } + }, + } + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r3") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r3", peer="r1" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes + dut = "r3" + input_topo = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_topo) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Kill BGPd daemon on R1 + kill_router_daemons(tgen, "r1", ["bgpd"]) + + for addr_type in ADDR_TYPES: + # Verifying BGP RIB routes + input_topo = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_topo) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Start BGPd daemon on R1 + start_router_daemons(tgen, "r1", ["bgpd"]) + + for addr_type in ADDR_TYPES: + # Verifying BGP RIB routes + input_topo = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_topo) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # verify multi address family + result = verify_gr_address_family( + tgen, topo, addr_type, "ipv4Unicast", dut="r1" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # verify multi address family + result = verify_gr_address_family( + tgen, topo, addr_type, "ipv6Unicast", dut="r1" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # verify multi address family + result = verify_gr_address_family( + tgen, topo, addr_type, "ipv4Unicast", dut="r3" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # verify multi address family + result = verify_gr_address_family( + tgen, topo, addr_type, "ipv6Unicast", dut="r3" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_BGP_GR_chaos_28_p1(request): + """ + Test Objective : Verify if helper node goes down before restarting + node comes up online, helper node sets the R-bit to avoid dead-lock + till SDT expiry. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + logger.info( + "Test Case: test_BGP_GR_chaos_28 :" + "[Helper Mode]R3-----R1[Restart Mode] initialized" + ) + + # Configure graceful-restart + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r1": {"graceful-restart": True}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r1": {"graceful-restart": True}}} + } + } + }, + } + } + }, + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {"graceful-restart-helper": True} + } + } + } + } + }, + } + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r3") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r3" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info("[Step 1] : Kill BGPd daemon on R1..") + + # Kill BGPd daemon on R1 + kill_router_daemons(tgen, "r1", ["bgpd"]) + + logger.info("[Step 2] : Kill BGPd daemon on R3..") + + # Kill BGPd daemon on R3 + kill_router_daemons(tgen, "r3", ["bgpd"]) + + logger.info("[Step 3] : Start BGPd daemon on R1..") + + # Start BGPd daemon on R1 + start_router_daemons(tgen, "r1", ["bgpd"]) + + logger.info("[Step 4] : Start BGPd daemon on R3..") + + # Start BGPd daemon on R3 + start_router_daemons(tgen, "r3", ["bgpd"]) + + # Verify r_bit + for addr_type in ADDR_TYPES: + result = verify_r_bit(tgen, topo, addr_type, input_dict, dut="r3", peer="r1") + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_BGP_GR_chaos_29_p1(request): + """ + Test Objective : Change timers on the fly, and + verify if it takes immediate effect. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + logger.info( + " Test Case : BGP_GR_UTP_29" + " BGP GR [Helper Mode]R3-----R1[Restart Mode]" + " and [restart-time 150]R1 initialized" + ) + + # Configure graceful-restart and timers + input_dict = { + "r1": { + "bgp": { + "graceful-restart": {"timer": {"restart-time": GR_RESTART_TIMER}}, + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r1": {"graceful-restart": True}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r1": {"graceful-restart": True}}} + } + } + }, + }, + } + }, + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {"graceful-restart-helper": True} + } + } + } + } + }, + } + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r3") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r3" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verify graceful-restart timers + input_dict_2 = { + "r1": { + "bgp": { + "graceful-restart": {"timer": {"restart-time": GR_RESTART_TIMER + 5}} + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "r1": { + "bgp": { + "graceful-restart": {"timer": {"restart-time": GR_RESTART_TIMER}} + } + } + } + + result = verify_graceful_restart_timers( + tgen, topo, addr_type, input_dict_2, dut="r3", peer="r1" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + # Verifying BGP RIB routes before shutting down BGPd daemon + dut = "r3" + input_dict = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_dict) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info("[Step 2] : Kill BGPd daemon on R1..") + + # Kill BGPd daemon on R1 + kill_router_daemons(tgen, "r1", ["bgpd"]) + + # Waiting for 120 sec + logger.info("[Step 3] : Wait for {} seconds..".format(GR_RESTART_TIMER)) + + # Waiting for 120 sec + sleep(GR_RESTART_TIMER) + + for addr_type in ADDR_TYPES: + # Verifying BGP RIB routes before shutting down BGPd daemon + input_dict = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict, expected=False) + assert result is not True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + logger.info(" Expected behavior: {}".format(result)) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_dict, expected=False) + assert result is not True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + logger.info(" Expected behavior: {}".format(result)) + + logger.info("[Step 4] : Start BGPd daemon on R1..") + + # Start BGPd daemon on R1 + start_router_daemons(tgen, "r1", ["bgpd"]) + + write_test_footer(tc_name) + + +def test_BGP_GR_chaos_33_p1(request): + """ + Test Objective : Helper router receives same prefixes from two + different routers (GR-restarting and GR-disabled). Keeps the + stale entry only for GR-restarting node(next-hop is correct). + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + logger.info( + " Test Case : test_BGP_GR_chaos_33 " + "BGP GR " + "[Restart Mode]R1--R3[Helper Mode]--R4[Disabled Mode]" + ) + + # Configure graceful-restart + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r1": {"graceful-restart": True}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r1": {"graceful-restart": True}}} + } + } + }, + } + } + }, + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {"graceful-restart-helper": True} + } + } + } + } + }, + } + } + }, + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": {"graceful-restart-disable": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": {"graceful-restart-disable": True} + } + } + } + } + }, + } + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r3") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r3" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info("[Step 2] : Advertise same networks from R1 and R4..") + + # Api call to delete advertised networks + input_dict_2 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + {"network": "200.0.20.1/32", "no_of_network": 2,} + ] + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + {"network": "2001::1/128", "no_of_network": 2} + ] + } + }, + } + } + }, + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + {"network": "200.0.20.1/32", "no_of_network": 2} + ] + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + {"network": "2001::1/128", "no_of_network": 2} + ] + } + }, + } + } + }, + } + + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + # Verifying RIB routes + dut = "r3" + peer1 = "r1" + peer2 = "r4" + intf1 = topo["routers"][peer1]["links"][dut]["interface"] + intf2 = topo["routers"][peer2]["links"][dut]["interface"] + + if addr_type == "ipv4": + next_hop_4 = NEXT_HOP_4 + result = verify_rib(tgen, addr_type, dut, input_dict_2, next_hop_4) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + if addr_type == "ipv6": + if "link_local" in PREFERRED_NEXT_HOP: + next_hop1 = get_frr_ipv6_linklocal(tgen, peer1, intf=intf1) + next_hop2 = get_frr_ipv6_linklocal(tgen, peer2, intf=intf2) + + next_hop_6 = [next_hop1, next_hop2] + else: + next_hop_6 = NEXT_HOP_6 + + result = verify_rib(tgen, addr_type, dut, input_dict_2, next_hop_6) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info("[Step 3] : Kill BGPd daemon on R1 and R4..") + + # Kill BGPd daemon on R1 + kill_router_daemons(tgen, "r1", ["bgpd"]) + + # Kill BGPd daemon on R4 + kill_router_daemons(tgen, "r4", ["bgpd"]) + + for addr_type in ADDR_TYPES: + # Verifying RIB routes + next_hop_6 = ["fd00:0:0:1::1"] + if addr_type == "ipv4": + next_hop_4 = NEXT_HOP_4[0] + + result = verify_rib(tgen, addr_type, dut, input_dict_2, next_hop_4) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + if addr_type == "ipv6": + if "link_local" in PREFERRED_NEXT_HOP: + next_hop_6 = get_frr_ipv6_linklocal(tgen, peer1, intf=intf1) + else: + next_hop_6 = NEXT_HOP_6[0] + + result = verify_rib(tgen, addr_type, dut, input_dict_2, next_hop_6) + + # Verifying RIB routes + if addr_type == "ipv4": + next_hop_4 = NEXT_HOP_4[1] + result = verify_rib( + tgen, addr_type, dut, input_dict_2, next_hop_4, expected=False + ) + assert result is not True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + logger.info(" Expected behavior: {}".format(result)) + + if addr_type == "ipv6": + if "link_local" in PREFERRED_NEXT_HOP: + next_hop_6 = get_frr_ipv6_linklocal(tgen, peer2, intf=intf2) + else: + next_hop_6 = NEXT_HOP_6[1] + + result = verify_rib(tgen, addr_type, dut, input_dict_2, next_hop_6) + + logger.info("[Step 4] : Start BGPd daemon on R1 and R4..") + + # Start BGPd daemon on R1 + start_router_daemons(tgen, "r1", ["bgpd"]) + + # Start BGPd daemon on R4 + start_router_daemons(tgen, "r4", ["bgpd"]) + + write_test_footer(tc_name) + + +def test_BGP_GR_chaos_34_2_p1(request): + """ + Test Objective : Restarting node doesn't preserve the forwarding + state verify the behaviour on helper node, if it still keeps the + stale routes. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + logger.info( + " Test Case : test_BGP_GR_chaos_34 " + "BGP GR " + "[Restart Mode]R1---R3[Helper Mode]" + ) + + logger.info("[Step 1] : Configure restarting" " router R1 to prevent ") + logger.info("[Step 2] : Reset the session" " between R1 and R3..") + + # Configure graceful-restart + input_dict = { + "r1": { + "bgp": { + "graceful-restart": {"preserve-fw-state": True, "disable-eor": True}, + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r1": {"graceful-restart": True}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r1": {"graceful-restart": True}}} + } + } + }, + }, + } + }, + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {"graceful-restart-helper": True} + } + } + } + } + }, + } + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r3") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r3" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verify f-bit before killing BGPd daemon + result = verify_f_bit(tgen, topo, addr_type, input_dict, "r3", "r1") + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes after starting BGPd daemon + dut = "r3" + input_dict_1 = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info("[Step 3] : Kill BGPd daemon on R1..") + + # Kill BGPd daemon on R1 + kill_router_daemons(tgen, "r1", ["bgpd"]) + + logger.info("[Step 4] : Withdraw/delete the prefixes " "originated from R1..") + + # Api call to delete advertised networks + network = {"ipv4": "101.0.20.1/32", "ipv6": "1::1/128"} + for addr_type in ADDR_TYPES: + input_dict_2 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "advertise_networks": [ + { + "network": network[addr_type], + "no_of_network": 5, + "delete": True, + } + ] + } + } + } + } + } + } + + result = modify_bgp_config_when_bgpd_down(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info("[Step 5] : Remove the CLI from R1's config to " "set the F-bit..") + + # Modify graceful-restart config not to set f-bit + # and write to /etc/frr + input_dict_2 = {"r1": {"bgp": {"graceful-restart": {"preserve-fw-state": False}}}} + + result = modify_bgp_config_when_bgpd_down(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error {}".format(tc_name, result) + + logger.info("[Step 6] : Bring up the BGPd daemon on R1 again..") + + # Start BGPd daemon on R1 + start_router_daemons(tgen, "r1", ["bgpd"]) + + for addr_type in ADDR_TYPES: + # Verify f-bit after starting BGPd daemon + result = verify_f_bit( + tgen, topo, addr_type, input_dict, "r3", "r1", expected=False + ) + assert result is not True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + logger.info(" Expected behavior: {}".format(result)) + + # Verifying BGP RIB routes after starting BGPd daemon + input_dict_1 = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1, expected=False) + assert result is not True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + logger.info(" Expected behavior: {}".format(result)) + + # Verifying RIB routes + result = verify_rib(tgen, addr_type, dut, input_dict_1, expected=False) + assert result is not True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + logger.info(" Expected behavior: {}".format(result)) + + write_test_footer(tc_name) + + +def test_BGP_GR_chaos_34_1_p1(request): + """ + Test Objective : Restarting node doesn't preserve forwarding + state, helper router should not keep the stale entries. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + logger.info( + " Test Case : test_BGP_GR_chaos_31 " + "BGP GR " + "[Restart Mode]R1---R3[Helper Mode]" + ) + + # Configure graceful-restart + input_dict = { + "r1": { + "bgp": { + "graceful-restart": { + "preserve-fw-state": True, + "timer": {"restart-time": GR_RESTART_TIMER}, + }, + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r1": {"graceful-restart": True}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r1": {"graceful-restart": True}}} + } + } + }, + }, + } + }, + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {"graceful-restart-helper": True} + } + } + } + } + }, + } + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r3") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r3" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes after starting BGPd daemon + dut = "r3" + input_dict_1 = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info( + "[Step 1] : Remove the preserve-fw-state command" + " from restarting node R1's config" + ) + + # Configure graceful-restart to set f-bit as False + input_dict_2 = {"r1": {"bgp": {"graceful-restart": {"preserve-fw-state": False}}}} + + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + logger.info("[Step 2] : Reset the session between R1 and R3..") + + # Reset sessions + for addr_type in ADDR_TYPES: + clear_bgp(tgen, addr_type, "r1") + + for addr_type in ADDR_TYPES: + # Verify f-bit after starting BGPd daemon + result = verify_f_bit( + tgen, topo, addr_type, input_dict_2, "r3", "r1", expected=False + ) + assert result is not True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + logger.info(" Expected behavior: {}".format(result)) + + logger.info("[Step 3] : Kill BGPd daemon on R1..") + + # Kill BGPd daemon on R1 + kill_router_daemons(tgen, "r1", ["bgpd"]) + + # Waiting for 120 sec + logger.info("Waiting for {} sec..".format(GR_RESTART_TIMER)) + sleep(GR_RESTART_TIMER) + + for addr_type in ADDR_TYPES: + # Verifying BGP RIB routes + input_dict = {key: topo["routers"][key] for key in ["r1"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict, expected=False) + assert result is not True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + logger.info(" Expected behavior: {}".format(result)) + + # Verifying RIB routes + result = verify_rib(tgen, addr_type, dut, input_dict, expected=False) + assert result is not True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + logger.info(" Expected behavior: {}".format(result)) + + # Start BGPd daemon on R1 + start_router_daemons(tgen, "r1", ["bgpd"]) + + write_test_footer(tc_name) + + +def test_BGP_GR_chaos_32_p1(request): + """ + Test Objective : Restarting node is connected to multiple helper + nodes, one of them doesn't send EOR to restarting router. Verify + that only after SDT restarting node send EOR to all helper peers + excluding the prefixes originated by faulty router. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + logger.info( + " Test Case : test_BGP_GR_chaos_32 " + "BGP GR " + "[Restart Mode]R1---R3&R5[Helper Mode]" + ) + + logger.info( + "[Step 1] : Change the mode on R1 be a restarting" " node on global level" + ) + + # Configure graceful-restart + input_dict = { + "r1": { + "bgp": { + "graceful-restart": {"graceful-restart": True}, + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r1": {"next_hop_self": True}}}, + "r5": {"dest_link": {"r1": {"graceful-restart": True}}}, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r1": {"next_hop_self": True}}}, + "r5": {"dest_link": {"r1": {"graceful-restart": True}}}, + } + } + }, + }, + } + }, + "r5": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r5": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r5": {"graceful-restart-helper": True} + } + } + } + } + }, + } + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r5") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r5" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying BGP RIB routes after starting BGPd daemon + dut = "r3" + input_dict_1 = {key: topo["routers"][key] for key in ["r5"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info("[Step 2] : Kill BGPd daemon on R1..") + # Kill BGPd daemon on R1 + kill_router_daemons(tgen, "r1", ["bgpd"]) + + logger.info("[Step 3] : Withdraw all the advertised prefixes from R5") + + # Api call to delete advertised networks + network = {"ipv4": "105.0.20.1/32", "ipv6": "5::1/128"} + for addr_type in ADDR_TYPES: + input_dict_2 = { + "r5": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "advertise_networks": [ + { + "network": network[addr_type], + "no_of_network": 5, + "delete": True, + } + ] + } + } + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + logger.info( + "[Step 4] : Stop the helper router R5 from sending EOR" " message using CLI" + ) + + # Modify graceful-restart config to prevent sending EOR + input_dict_3 = {"r5": {"bgp": {"graceful-restart": {"disable-eor": True}}}} + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + logger.info("[Step 5] : Bring up the BGPd daemon on R1..") + + # Start BGPd daemon on R1 + start_router_daemons(tgen, "r1", ["bgpd"]) + + for addr_type in ADDR_TYPES: + # Verify EOR is disabled + result = verify_eor( + tgen, topo, addr_type, input_dict_3, dut="r5", peer="r1", expected=False + ) + assert result is not True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + logger.info(" Expected behavior: {}".format(result)) + + # Verifying BGP RIB routes after starting BGPd daemon + input_dict_1 = {key: topo["routers"][key] for key in ["r5"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1, expected=False) + assert result is not True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + logger.info(" Expected behavior: {}".format(result)) + + # Verifying RIB routes + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is not True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + logger.info(" Expected behavior: {}".format(result)) + + write_test_footer(tc_name) + + +def test_BGP_GR_chaos_37_p1(request): + """ + Test Objective : Verify if helper node restarts before sending the + EOR message, restarting node doesn't wait until stale path timer + expiry to do the best path selection and sends an EOR + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + logger.info( + " Test Case : test_BGP_GR_chaos_37 " + "BGP GR " + "[Restart Mode]R1---R3[Helper Mode]" + ) + + logger.info( + "[Step 1] : Configure restarting router R3 to prevent " "sending an EOR.." + ) + + logger.info("[Step 2] : Reset the session between R3 and R1..") + + # Configure graceful-restart + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r1": {"graceful-restart": True}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r1": {"graceful-restart": True}}} + } + } + }, + } + } + }, + "r3": { + "bgp": { + "graceful-restart": {"disable-eor": True}, + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {"graceful-restart-helper": True} + } + } + } + } + }, + }, + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r3") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r3" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verify EOR is disabled + result = verify_eor( + tgen, topo, addr_type, input_dict, dut="r3", peer="r1", expected=False + ) + assert result is not True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + logger.info(" Expected behavior: {}".format(result)) + + # Verifying BGP RIB routes after starting BGPd daemon + dut = "r1" + input_dict_1 = {key: topo["routers"][key] for key in ["r3"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info("[Step 3] : Kill BGPd daemon on R1..") + + # Kill BGPd daemon on R1 + kill_router_daemons(tgen, "r1", ["bgpd"]) + + logger.info("[Step 4] : Start BGPd daemon on R1..") + + # Start BGPd daemon on R1 + start_router_daemons(tgen, "r1", ["bgpd"]) + + logger.info("[Step 5] : Kill BGPd daemon on R3..") + + # Kill BGPd daemon on R3 + kill_router_daemons(tgen, "r3", ["bgpd"]) + + # Modify graceful-restart config to prevent sending EOR + input_dict_2 = {"r3": {"bgp": {"graceful-restart": {"disable-eor": True}}}} + + result = modify_bgp_config_when_bgpd_down(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error {}".format(tc_name, result) + + logger.info("[Step 6] : Start BGPd daemon on R3..") + + # Start BGPd daemon on R3 + start_router_daemons(tgen, "r3", ["bgpd"]) + + for addr_type in ADDR_TYPES: + # Verify r_bit + result = verify_r_bit(tgen, topo, addr_type, input_dict, dut="r1", peer="r3") + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes + input_dict_1 = {key: topo["routers"][key] for key in ["r3"]} + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verify EOR is send from R1 to R3 + input_dict_3 = {"r1": {"bgp": {"graceful-restart": {"disable-eor": True}}}} + + result = verify_eor( + tgen, topo, addr_type, input_dict_3, dut="r1", peer="r3", expected=False + ) + assert result is not True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_BGP_GR_chaos_30_p1(request): + """ + Test Objective : Restarting node removes stale routes from Zebra + after receiving an EOR from helper router. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Check router status + check_router_status(tgen) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + logger.info( + " Test Case : test_BGP_GR_chaos_30 " + "BGP GR [Helper Mode]R3-----R1[Restart Mode] " + ) + + # Configure graceful-restart and timers + input_dict = { + "r1": { + "bgp": { + "graceful-restart": {"preserve-fw-state": True}, + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r1": {"graceful-restart": True}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r1": {"graceful-restart": True}}} + } + } + }, + }, + } + }, + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {"graceful-restart-helper": True} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {"graceful-restart-helper": True} + } + } + } + } + }, + } + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r3") + + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r1", peer="r3" + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + # Verifying BGP RIB routes before shutting down BGPd daemon + dut = "r1" + input_dict = {key: topo["routers"][key] for key in ["r3"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_dict) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + logger.info("[Step 2] : Kill BGPd daemon on R1..") + + # Kill BGPd daemon on R1 + kill_router_daemons(tgen, "r1", ["bgpd"]) + + logger.info("[Step 3] : Withdraw advertised prefixes from R3...") + + # Api call to delete advertised networks + network = {"ipv4": "103.0.20.1/32", "ipv6": "3::1/128"} + for addr_type in ADDR_TYPES: + input_dict = { + "r3": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "advertise_networks": [ + { + "network": network[addr_type], + "no_of_network": 5, + "delete": True, + } + ] + } + } + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + logger.info("[Step 4] : Start BGPd daemon on R1..") + + # Start BGPd daemon on R1 + start_router_daemons(tgen, "r1", ["bgpd"]) + + for addr_type in ADDR_TYPES: + # Verifying BGP RIB routes before shutting down BGPd daemon + input_dict = {key: topo["routers"][key] for key in ["r3"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_dict, expected=False) + assert result is not True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + logger.info(" Expected behavior: {}".format(result)) + + # Verifying RIB routes before shutting down BGPd daemon + result = verify_rib(tgen, addr_type, dut, input_dict, expected=False) + assert result is not True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + logger.info(" Expected behavior: {}".format(result)) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_instance_del_test/__init__.py b/tests/topotests/bgp_instance_del_test/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bgp_instance_del_test/ce1 b/tests/topotests/bgp_instance_del_test/ce1 new file mode 120000 index 0000000000..0924eb5f39 --- /dev/null +++ b/tests/topotests/bgp_instance_del_test/ce1 @@ -0,0 +1 @@ +../bgp_l3vpn_to_bgp_vrf/ce1 \ No newline at end of file diff --git a/tests/topotests/bgp_instance_del_test/ce2 b/tests/topotests/bgp_instance_del_test/ce2 new file mode 120000 index 0000000000..8c7a6777a9 --- /dev/null +++ b/tests/topotests/bgp_instance_del_test/ce2 @@ -0,0 +1 @@ +../bgp_l3vpn_to_bgp_vrf/ce2 \ No newline at end of file diff --git a/tests/topotests/bgp_instance_del_test/ce3 b/tests/topotests/bgp_instance_del_test/ce3 new file mode 120000 index 0000000000..0abb8e55ed --- /dev/null +++ b/tests/topotests/bgp_instance_del_test/ce3 @@ -0,0 +1 @@ +../bgp_l3vpn_to_bgp_vrf/ce3 \ No newline at end of file diff --git a/tests/topotests/bgp_instance_del_test/ce4 b/tests/topotests/bgp_instance_del_test/ce4 new file mode 120000 index 0000000000..ddee1eff9e --- /dev/null +++ b/tests/topotests/bgp_instance_del_test/ce4 @@ -0,0 +1 @@ +../bgp_l3vpn_to_bgp_vrf/ce4 \ No newline at end of file diff --git a/tests/topotests/bgp_instance_del_test/customize.py b/tests/topotests/bgp_instance_del_test/customize.py new file mode 120000 index 0000000000..99fcf39eb5 --- /dev/null +++ b/tests/topotests/bgp_instance_del_test/customize.py @@ -0,0 +1 @@ +../bgp_l3vpn_to_bgp_vrf/customize.py \ No newline at end of file diff --git a/tests/topotests/bgp_instance_del_test/r1 b/tests/topotests/bgp_instance_del_test/r1 new file mode 120000 index 0000000000..16babfa8d5 --- /dev/null +++ b/tests/topotests/bgp_instance_del_test/r1 @@ -0,0 +1 @@ +../bgp_l3vpn_to_bgp_vrf/r1 \ No newline at end of file diff --git a/tests/topotests/bgp_instance_del_test/r2 b/tests/topotests/bgp_instance_del_test/r2 new file mode 120000 index 0000000000..e25b93276f --- /dev/null +++ b/tests/topotests/bgp_instance_del_test/r2 @@ -0,0 +1 @@ +../bgp_l3vpn_to_bgp_vrf/r2 \ No newline at end of file diff --git a/tests/topotests/bgp_instance_del_test/r3 b/tests/topotests/bgp_instance_del_test/r3 new file mode 120000 index 0000000000..0d7c189c6a --- /dev/null +++ b/tests/topotests/bgp_instance_del_test/r3 @@ -0,0 +1 @@ +../bgp_l3vpn_to_bgp_vrf/r3 \ No newline at end of file diff --git a/tests/topotests/bgp_instance_del_test/r4 b/tests/topotests/bgp_instance_del_test/r4 new file mode 120000 index 0000000000..2d667d37af --- /dev/null +++ b/tests/topotests/bgp_instance_del_test/r4 @@ -0,0 +1 @@ +../bgp_l3vpn_to_bgp_vrf/r4 \ No newline at end of file diff --git a/tests/topotests/bgp_instance_del_test/scripts b/tests/topotests/bgp_instance_del_test/scripts new file mode 120000 index 0000000000..c46bf1f07b --- /dev/null +++ b/tests/topotests/bgp_instance_del_test/scripts @@ -0,0 +1 @@ +../bgp_l3vpn_to_bgp_vrf/scripts \ No newline at end of file diff --git a/tests/topotests/bgp_instance_del_test/test_bgp_instance_del_test.py b/tests/topotests/bgp_instance_del_test/test_bgp_instance_del_test.py new file mode 100755 index 0000000000..47cc0eb39d --- /dev/null +++ b/tests/topotests/bgp_instance_del_test/test_bgp_instance_del_test.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python + +# +# Part of NetDEF Topology Tests +# +# Copyright (c) 2018, LabN Consulting, L.L.C. +# Authored by Lou Berger +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +import os +import sys +import pytest + +sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "../")) + +from lib.ltemplate import * + + +def test_check_linux_vrf(): + CliOnFail = None + # For debugging, uncomment the next line + # CliOnFail = 'tgen.mininet_cli' + CheckFunc = "ltemplateVersionCheck('4.1', iproute2='4.9')" + # uncomment next line to start cli *before* script is run + # CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True, iproute2=\'4.9\')' + ltemplateTest("scripts/check_linux_vrf.py", False, CliOnFail, CheckFunc) + + +def test_adjacencies(): + CliOnFail = None + # For debugging, uncomment the next line + # CliOnFail = 'tgen.mininet_cli' + CheckFunc = "ltemplateVersionCheck('4.1')" + # uncomment next line to start cli *before* script is run + # CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True)' + ltemplateTest("scripts/adjacencies.py", False, CliOnFail, CheckFunc) + + +def SKIP_test_add_routes(): + CliOnFail = None + # For debugging, uncomment the next line + # CliOnFail = 'tgen.mininet_cli' + CheckFunc = "ltemplateVersionCheck('4.1')" + # uncomment next line to start cli *before* script is run + # CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True)' + ltemplateTest("scripts/add_routes.py", False, CliOnFail, CheckFunc) + + +def test_check_routes(): + CliOnFail = None + # For debugging, uncomment the next line + # CliOnFail = 'tgen.mininet_cli' + CheckFunc = "ltemplateVersionCheck('4.1')" + # uncomment next line to start cli *before* script is run + # CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True)' + ltemplateTest("scripts/check_routes.py", False, CliOnFail, CheckFunc) + + +# manual data path setup test - remove once have bgp/zebra vrf path working +def test_check_linux_mpls(): + CliOnFail = None + # For debugging, uncomment the next line + # CliOnFail = 'tgen.mininet_cli' + CheckFunc = "ltemplateVersionCheck('4.1', iproute2='4.9')" + # uncomment next line to start cli *before* script is run + # CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True, iproute2=\'4.9\')' + ltemplateTest("scripts/check_linux_mpls.py", False, CliOnFail, CheckFunc) + + +def test_del_bgp_instances(): + CliOnFail = None + # For debugging, uncomment the next line + # CliOnFail = 'tgen.mininet_cli' + CheckFunc = "ltemplateVersionCheck('4.1')" + # uncomment next line to start cli *before* script is run + # CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True)' + ltemplateTest("scripts/del_bgp_instances.py", False, CliOnFail, CheckFunc) + + +if __name__ == "__main__": + retval = pytest.main(["-s"]) + sys.exit(retval) diff --git a/tests/topotests/bgp_ipv6_rtadv/__init__.py b/tests/topotests/bgp_ipv6_rtadv/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bgp_ipv6_rtadv/r1/bgpd.conf b/tests/topotests/bgp_ipv6_rtadv/r1/bgpd.conf new file mode 100644 index 0000000000..4d96bec2cb --- /dev/null +++ b/tests/topotests/bgp_ipv6_rtadv/r1/bgpd.conf @@ -0,0 +1,14 @@ +router bgp 101 + bgp router-id 10.254.254.1 + no bgp ebgp-requires-policy + neighbor r2g peer-group + neighbor r2g remote-as external + neighbor r2g bfd + neighbor r1-eth0 interface peer-group r2g + address-family ipv4 unicast + redistribute connected + exit-address-family + address-family ipv6 unicast + neighbor r2g activate + exit-address-family +! diff --git a/tests/topotests/bgp_ipv6_rtadv/r1/ipv4_routes.json b/tests/topotests/bgp_ipv6_rtadv/r1/ipv4_routes.json new file mode 100644 index 0000000000..73c7fbbf5e --- /dev/null +++ b/tests/topotests/bgp_ipv6_rtadv/r1/ipv4_routes.json @@ -0,0 +1,40 @@ +{ + "10.254.254.2/32": [ + { + "distance": 20, + "protocol": "bgp", + "metric": 0, + "selected": true, + "destSelected": true, + "prefix": "10.254.254.2/32", + "nexthops": [ + { + "interfaceName": "r1-eth0", + "fib": true, + "flags": 3, + "active": true, + "afi": "ipv6" + } + ] + } + ], + "10.254.254.1/32": [ + { + "distance": 0, + "protocol": "connected", + "metric": 0, + "selected": true, + "destSelected": true, + "prefix": "10.254.254.1/32", + "nexthops": [ + { + "directlyConnected": true, + "interfaceName": "lo", + "fib": true, + "flags": 3, + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_ipv6_rtadv/r1/ipv6_routes.json b/tests/topotests/bgp_ipv6_rtadv/r1/ipv6_routes.json new file mode 100644 index 0000000000..a50bffd0a8 --- /dev/null +++ b/tests/topotests/bgp_ipv6_rtadv/r1/ipv6_routes.json @@ -0,0 +1,34 @@ +{ + "2001:db8:1::/64": [ + { + "distance": 20, + "protocol": "bgp", + "metric": 0, + "prefix": "2001:db8:1::/64", + "nexthops": [ + { + "interfaceName": "r1-eth0", + "active": true, + "afi": "ipv6" + } + ] + }, + { + "distance": 0, + "protocol": "connected", + "metric": 0, + "selected": true, + "destSelected": true, + "prefix": "2001:db8:1::/64", + "nexthops": [ + { + "directlyConnected": true, + "interfaceName": "r1-eth0", + "fib": true, + "flags": 3, + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_ipv6_rtadv/r1/zebra.conf b/tests/topotests/bgp_ipv6_rtadv/r1/zebra.conf new file mode 100644 index 0000000000..f95c3b07a7 --- /dev/null +++ b/tests/topotests/bgp_ipv6_rtadv/r1/zebra.conf @@ -0,0 +1,9 @@ +debug zebra packet recv +debug zebra packet send +log stdout +interface lo + ip address 10.254.254.1/32 +! +interface r1-eth0 + ipv6 address 2001:db8:1::1/64 +! diff --git a/tests/topotests/bgp_ipv6_rtadv/r2/bgpd.conf b/tests/topotests/bgp_ipv6_rtadv/r2/bgpd.conf new file mode 100644 index 0000000000..4d02fc4f29 --- /dev/null +++ b/tests/topotests/bgp_ipv6_rtadv/r2/bgpd.conf @@ -0,0 +1,17 @@ +router bgp 102 + bgp router-id 10.254.254.2 + no bgp ebgp-requires-policy + neighbor r2g peer-group + neighbor r2g remote-as external + neighbor r2g bfd + neighbor r2-eth0 interface peer-group r2g + ! + address-family ipv4 unicast + redistribute connected + exit-address-family + ! + address-family ipv6 unicast + redistribute connected + neighbor r2g activate + exit-address-family +! diff --git a/tests/topotests/bgp_ipv6_rtadv/r2/ipv4_routes.json b/tests/topotests/bgp_ipv6_rtadv/r2/ipv4_routes.json new file mode 100644 index 0000000000..d4b1410ea5 --- /dev/null +++ b/tests/topotests/bgp_ipv6_rtadv/r2/ipv4_routes.json @@ -0,0 +1,40 @@ +{ + "10.254.254.2/32": [ + { + "distance": 0, + "protocol": "connected", + "metric": 0, + "selected": true, + "destSelected": true, + "prefix": "10.254.254.2/32", + "nexthops": [ + { + "directlyConnected": true, + "interfaceName": "lo", + "fib": true, + "flags": 3, + "active": true + } + ] + } + ], + "10.254.254.1/32": [ + { + "distance": 20, + "protocol": "bgp", + "metric": 0, + "selected": true, + "destSelected": true, + "prefix": "10.254.254.1/32", + "nexthops": [ + { + "interfaceName": "r2-eth0", + "fib": true, + "flags": 3, + "active": true, + "afi": "ipv6" + } + ] + } + ] +} diff --git a/tests/topotests/bgp_ipv6_rtadv/r2/ipv6_routes.json b/tests/topotests/bgp_ipv6_rtadv/r2/ipv6_routes.json new file mode 100644 index 0000000000..e7bfb9914a --- /dev/null +++ b/tests/topotests/bgp_ipv6_rtadv/r2/ipv6_routes.json @@ -0,0 +1,21 @@ +{ + "2001:db8:1::/64": [ + { + "distance": 0, + "protocol": "connected", + "metric": 0, + "selected": true, + "destSelected": true, + "prefix": "2001:db8:1::/64", + "nexthops": [ + { + "directlyConnected": true, + "interfaceName": "r2-eth0", + "fib": true, + "flags": 3, + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_ipv6_rtadv/r2/zebra.conf b/tests/topotests/bgp_ipv6_rtadv/r2/zebra.conf new file mode 100644 index 0000000000..0131a11be0 --- /dev/null +++ b/tests/topotests/bgp_ipv6_rtadv/r2/zebra.conf @@ -0,0 +1,9 @@ +ip forwarding +ipv6 forwarding +! +interface lo + ip address 10.254.254.2/32 +! +interface r2-eth0 + ipv6 address 2001:db8:1::2/64 +! diff --git a/tests/topotests/bgp_ipv6_rtadv/test_bgp_ipv6_rtadv.dot b/tests/topotests/bgp_ipv6_rtadv/test_bgp_ipv6_rtadv.dot new file mode 100644 index 0000000000..da67c29a09 --- /dev/null +++ b/tests/topotests/bgp_ipv6_rtadv/test_bgp_ipv6_rtadv.dot @@ -0,0 +1,44 @@ +## Color coding: +######################### +## Main FRR: #f08080 red +## Switches: #d0e0d0 gray +## RIP: #19e3d9 Cyan +## RIPng: #fcb314 dark yellow +## OSPFv2: #32b835 Green +## OSPFv3: #19e3d9 Cyan +## ISIS IPv4 #fcb314 dark yellow +## ISIS IPv6 #9a81ec purple +## BGP IPv4 #eee3d3 beige +## BGP IPv6 #fdff00 yellow +##### Colors (see http://www.color-hex.com/) + +graph template { + label="bfd-topo2"; + + # Routers + r1 [ + shape=doubleoctagon, + label="r1", + fillcolor="#f08080", + style=filled, + ]; + r2 [ + shape=doubleoctagon + label="r2", + fillcolor="#f08080", + style=filled, + ]; + + # Switches + sw1 [ + shape=oval, + label="sw1\n2001:db8:1::/64", + fillcolor="#d0e0d0", + style=filled, + ]; + + # Connections + r1 -- sw1 [label="eth0"]; + r2 -- sw1 [label="eth0"]; + +} diff --git a/tests/topotests/bgp_ipv6_rtadv/test_bgp_ipv6_rtadv.py b/tests/topotests/bgp_ipv6_rtadv/test_bgp_ipv6_rtadv.py new file mode 100644 index 0000000000..10b2f3595f --- /dev/null +++ b/tests/topotests/bgp_ipv6_rtadv/test_bgp_ipv6_rtadv.py @@ -0,0 +1,150 @@ +#!/usr/bin/env python + +# +# test_bgp_ipv6_rtadv.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2019 by 6WIND +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" + test_bgp_ipv6_rtadv.py: Test the FRR/Quagga BGP daemon with BGP IPv6 interface + with route advertisements on a separate netns. +""" + +import os +import sys +import json +from functools import partial +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + + +class BGPIPV6RTADVTopo(Topo): + "Test topology builder" + + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # Create 2 routers. + tgen.add_router("r1") + tgen.add_router("r2") + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(BGPIPV6RTADVTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for rname, router in router_list.iteritems(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + # Initialize all routers. + tgen.start_router() + + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + + tgen.stop_topology() + + +def test_protocols_convergence(): + """ + Assert that all protocols have converged + statuses as they depend on it. + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Check IPv4 routing tables. + logger.info("Checking IPv4 routes for convergence") + for router in tgen.routers().values(): + json_file = "{}/{}/ipv4_routes.json".format(CWD, router.name) + if not os.path.isfile(json_file): + logger.info("skipping file {}".format(json_file)) + continue + + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, + router, + "show ip route json".format(router.name), + expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=160, wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + # Check IPv6 routing tables. + logger.info("Checking IPv6 routes for convergence") + for router in tgen.routers().values(): + json_file = "{}/{}/ipv6_routes.json".format(CWD, router.name) + if not os.path.isfile(json_file): + logger.info("skipping file {}".format(json_file)) + continue + + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, + router, + "show ipv6 route json".format(router.name), + expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=160, wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/ce1/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/ce1/bgpd.conf index bd10248d7b..8b885341eb 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_direct/ce1/bgpd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/ce1/bgpd.conf @@ -3,12 +3,14 @@ frr defaults traditional hostname ce1 password zebra log stdout notifications -log monitor notifications log commands router bgp 5226 + no bgp network import-check bgp router-id 99.0.0.1 + no bgp ebgp-requires-policy neighbor 192.168.1.1 remote-as 5226 neighbor 192.168.1.1 update-source 192.168.1.2 + neighbor 192.168.1.1 timers 3 10 address-family ipv4 unicast network 5.1.0.0/24 route-map rm-nh network 5.1.1.0/24 route-map rm-nh @@ -28,6 +30,3 @@ route-map rm-nh permit 10 ! end - - - diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/ce2/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/ce2/bgpd.conf index ab86c5e1b8..2accaae163 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_direct/ce2/bgpd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/ce2/bgpd.conf @@ -3,12 +3,14 @@ frr defaults traditional hostname ce2 password zebra log stdout notifications -log monitor notifications log commands router bgp 5226 + no bgp network import-check bgp router-id 99.0.0.2 + no bgp ebgp-requires-policy neighbor 192.168.1.1 remote-as 5226 neighbor 192.168.1.1 update-source 192.168.1.2 + neighbor 192.168.1.1 timers 3 10 address-family ipv4 unicast network 5.1.0.0/24 route-map rm-nh network 5.1.1.0/24 route-map rm-nh @@ -28,6 +30,3 @@ route-map rm-nh permit 10 ! end - - - diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/ce3/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/ce3/bgpd.conf index 7d239b0bd5..42eff1b9b1 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_direct/ce3/bgpd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/ce3/bgpd.conf @@ -3,12 +3,14 @@ frr defaults traditional hostname ce3 password zebra log stdout notifications -log monitor notifications log commands router bgp 5226 + no bgp network import-check bgp router-id 99.0.0.3 + no bgp ebgp-requires-policy neighbor 192.168.1.1 remote-as 5226 neighbor 192.168.1.1 update-source 192.168.1.2 + neighbor 192.168.1.1 timers 3 10 address-family ipv4 unicast network 5.1.2.0/24 route-map rm-nh network 5.1.3.0/24 route-map rm-nh @@ -28,6 +30,3 @@ route-map rm-nh permit 10 ! end - - - diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/customize.py b/tests/topotests/bgp_l3vpn_to_bgp_direct/customize.py index b4649059bc..05db9ab14b 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_direct/customize.py +++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/customize.py @@ -88,12 +88,15 @@ from mininet.topo import Topo import shutil + CWD = os.path.dirname(os.path.realpath(__file__)) # test name based on directory TEST = os.path.basename(CWD) + class ThisTestTopo(Topo): "Test topology builder" + def build(self, *_args, **_opts): "Build function" tgen = get_topogen(self) @@ -102,68 +105,71 @@ def build(self, *_args, **_opts): # between routers, switches and hosts. # # Create P/PE routers - tgen.add_router('r1') - #check for mpls + tgen.add_router("r1") + # check for mpls if tgen.hasmpls != True: - logger.info('MPLS not available, tests will be skipped') + logger.info("MPLS not available, tests will be skipped") return for routern in range(2, 5): - tgen.add_router('r{}'.format(routern)) + tgen.add_router("r{}".format(routern)) # Create CE routers for routern in range(1, 4): - tgen.add_router('ce{}'.format(routern)) + tgen.add_router("ce{}".format(routern)) - #CE/PE links - tgen.add_link(tgen.gears['ce1'], tgen.gears['r1'], 'ce1-eth0', 'r1-eth4') - tgen.add_link(tgen.gears['ce2'], tgen.gears['r3'], 'ce2-eth0', 'r3-eth4') - tgen.add_link(tgen.gears['ce3'], tgen.gears['r4'], 'ce3-eth0', 'r4-eth4') + # CE/PE links + tgen.add_link(tgen.gears["ce1"], tgen.gears["r1"], "ce1-eth0", "r1-eth4") + tgen.add_link(tgen.gears["ce2"], tgen.gears["r3"], "ce2-eth0", "r3-eth4") + tgen.add_link(tgen.gears["ce3"], tgen.gears["r4"], "ce3-eth0", "r4-eth4") # Create a switch with just one router connected to it to simulate a # empty network. switch = {} - switch[0] = tgen.add_switch('sw0') - switch[0].add_link(tgen.gears['r1'], nodeif='r1-eth0') - switch[0].add_link(tgen.gears['r2'], nodeif='r2-eth0') + switch[0] = tgen.add_switch("sw0") + switch[0].add_link(tgen.gears["r1"], nodeif="r1-eth0") + switch[0].add_link(tgen.gears["r2"], nodeif="r2-eth0") - switch[1] = tgen.add_switch('sw1') - switch[1].add_link(tgen.gears['r2'], nodeif='r2-eth1') - switch[1].add_link(tgen.gears['r3'], nodeif='r3-eth0') - switch[1].add_link(tgen.gears['r4'], nodeif='r4-eth0') + switch[1] = tgen.add_switch("sw1") + switch[1].add_link(tgen.gears["r2"], nodeif="r2-eth1") + switch[1].add_link(tgen.gears["r3"], nodeif="r3-eth0") + switch[1].add_link(tgen.gears["r4"], nodeif="r4-eth0") + + switch[1] = tgen.add_switch("sw2") + switch[1].add_link(tgen.gears["r2"], nodeif="r2-eth2") + switch[1].add_link(tgen.gears["r3"], nodeif="r3-eth1") - switch[1] = tgen.add_switch('sw2') - switch[1].add_link(tgen.gears['r2'], nodeif='r2-eth2') - switch[1].add_link(tgen.gears['r3'], nodeif='r3-eth1') def ltemplatePreRouterStartHook(): cc = ltemplateRtrCmd() tgen = get_topogen() - logger.info('pre router-start hook') - #check for mpls + logger.info("pre router-start hook") + # check for mpls if tgen.hasmpls != True: - logger.info('MPLS not available, skipping setup') + logger.info("MPLS not available, skipping setup") return False - #check for normal init + # check for normal init if len(tgen.net) == 1: - logger.info('Topology not configured, skipping setup') + logger.info("Topology not configured, skipping setup") return False - #configure r2 mpls interfaces - intfs = ['lo', 'r2-eth0', 'r2-eth1', 'r2-eth2'] + # configure r2 mpls interfaces + intfs = ["lo", "r2-eth0", "r2-eth1", "r2-eth2"] for intf in intfs: - cc.doCmd(tgen, 'r2', 'echo 1 > /proc/sys/net/mpls/conf/{}/input'.format(intf)) - #configure MPLS - rtrs = ['r1', 'r3', 'r4'] - cmds = ['echo 1 > /proc/sys/net/mpls/conf/lo/input'] + cc.doCmd(tgen, "r2", "echo 1 > /proc/sys/net/mpls/conf/{}/input".format(intf)) + # configure MPLS + rtrs = ["r1", "r3", "r4"] + cmds = ["echo 1 > /proc/sys/net/mpls/conf/lo/input"] for rtr in rtrs: router = tgen.gears[rtr] for cmd in cmds: cc.doCmd(tgen, rtr, cmd) - intfs = ['lo', rtr+'-eth0', rtr+'-eth4'] + intfs = ["lo", rtr + "-eth0", rtr + "-eth4"] for intf in intfs: - cc.doCmd(tgen, rtr, 'echo 1 > /proc/sys/net/mpls/conf/{}/input'.format(intf)) - logger.info('setup mpls input') + cc.doCmd( + tgen, rtr, "echo 1 > /proc/sys/net/mpls/conf/{}/input".format(intf) + ) + logger.info("setup mpls input") return True + def ltemplatePostRouterStartHook(): - logger.info('post router-start hook') + logger.info("post router-start hook") return True - diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r1/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r1/bgpd.conf index 7ec941ee6b..a564da9411 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_direct/r1/bgpd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r1/bgpd.conf @@ -3,16 +3,18 @@ frr defaults traditional hostname r1 password zebra log stdout notifications -log monitor notifications log commands router bgp 5226 bgp router-id 1.1.1.1 bgp cluster-id 1.1.1.1 + no bgp ebgp-requires-policy neighbor 192.168.1.2 remote-as 5226 neighbor 192.168.1.2 update-source 192.168.1.1 neighbor 192.168.1.2 route-reflector-client + neighbor 192.168.1.2 timers 3 10 neighbor 2.2.2.2 remote-as 5226 neighbor 2.2.2.2 update-source 1.1.1.1 + neighbor 2.2.2.2 timers 3 10 ! address-family ipv4 unicast redistribute vnc-direct diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r1/ospfd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r1/ospfd.conf index c5097e214f..460a8fb016 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_direct/r1/ospfd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r1/ospfd.conf @@ -6,3 +6,7 @@ router ospf network 0.0.0.0/4 area 0 redistribute static ! +int r1-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r2/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r2/bgpd.conf index 241c2ac0ae..3167306700 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_direct/r2/bgpd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r2/bgpd.conf @@ -3,17 +3,20 @@ frr defaults traditional hostname r2 password zebra log stdout notifications -log monitor notifications log commands router bgp 5226 bgp router-id 2.2.2.2 bgp cluster-id 2.2.2.2 + no bgp ebgp-requires-policy neighbor 1.1.1.1 remote-as 5226 neighbor 1.1.1.1 update-source 2.2.2.2 + neighbor 1.1.1.1 timers 3 10 neighbor 3.3.3.3 remote-as 5226 neighbor 3.3.3.3 update-source 2.2.2.2 + neighbor 3.3.3.3 timers 3 10 neighbor 4.4.4.4 remote-as 5226 neighbor 4.4.4.4 update-source 2.2.2.2 + neighbor 4.4.4.4 timers 3 10 address-family ipv4 unicast no neighbor 1.1.1.1 activate no neighbor 3.3.3.3 activate @@ -28,6 +31,3 @@ router bgp 5226 neighbor 4.4.4.4 route-reflector-client exit-address-family end - - - diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r2/ospfd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r2/ospfd.conf index 8678813665..dbed6189c8 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_direct/r2/ospfd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r2/ospfd.conf @@ -5,3 +5,15 @@ router ospf router-id 2.2.2.2 network 0.0.0.0/0 area 0 ! +int r2-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +int r2-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +int r2-eth2 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r3/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r3/bgpd.conf index 5591c633c6..445da08248 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_direct/r3/bgpd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r3/bgpd.conf @@ -3,16 +3,18 @@ frr defaults traditional hostname r3 password zebra log stdout notifications -log monitor notifications log commands router bgp 5226 bgp router-id 3.3.3.3 bgp cluster-id 3.3.3.3 + no bgp ebgp-requires-policy neighbor 192.168.1.2 remote-as 5226 - neighbor 192.168.1.2 update-source 192.168.1.2 + neighbor 192.168.1.2 update-source 192.168.1.2 neighbor 192.168.1.2 route-reflector-client + neighbor 192.168.1.2 timers 3 10 neighbor 2.2.2.2 remote-as 5226 neighbor 2.2.2.2 update-source 3.3.3.3 + neighbor 2.2.2.2 timers 3 10 ! address-family ipv4 unicast redistribute vnc-direct @@ -37,6 +39,3 @@ router bgp 5226 vnc redistribute ipv4 bgp-direct ! end - - - diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r3/ospfd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r3/ospfd.conf index c7c358f9dc..0e64ed6fc5 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_direct/r3/ospfd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r3/ospfd.conf @@ -7,3 +7,11 @@ router ospf network 0.0.0.0/4 area 0 redistribute static ! +int r3-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +int r3-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r4/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r4/bgpd.conf index 145390d724..1941352450 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_direct/r4/bgpd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r4/bgpd.conf @@ -3,16 +3,18 @@ frr defaults traditional hostname r4 password zebra log stdout notifications -log monitor notifications log commands router bgp 5226 bgp router-id 4.4.4.4 bgp cluster-id 4.4.4.4 + no bgp ebgp-requires-policy neighbor 192.168.1.2 remote-as 5226 neighbor 192.168.1.2 update-source 192.168.1.1 neighbor 192.168.1.2 route-reflector-client + neighbor 192.168.1.2 timers 3 10 neighbor 2.2.2.2 remote-as 5226 neighbor 2.2.2.2 update-source 4.4.4.4 + neighbor 2.2.2.2 timers 3 10 ! address-family ipv4 unicast redistribute vnc-direct @@ -37,6 +39,3 @@ router bgp 5226 vnc redistribute ipv4 bgp-direct ! end - - - diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r4/ospfd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r4/ospfd.conf index 83d09c09a4..89e37df479 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_direct/r4/ospfd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r4/ospfd.conf @@ -6,3 +6,7 @@ router ospf network 0.0.0.0/4 area 0 redistribute static ! +int r4-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/add_routes.py b/tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/add_routes.py index 3a24367a56..3f1157ad72 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/add_routes.py +++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/add_routes.py @@ -1,51 +1,193 @@ from lutil import luCommand -luCommand('r1','vtysh -c "show bgp next"','99.0.0.. valid', 'wait', 'See CE static NH') -luCommand('r3','vtysh -c "show bgp next"','99.0.0.. valid', 'wait', 'See CE static NH') -luCommand('r4','vtysh -c "show bgp next"','99.0.0.. valid', 'wait', 'See CE static NH') -luCommand('r1','vtysh -c "show bgp ipv4 uni"','i5.*i5','wait','See CE routes') -luCommand('r3','vtysh -c "show bgp ipv4 uni"','i5.*i5','wait','See CE routes') -luCommand('r4','vtysh -c "show bgp ipv4 uni"','i5.*i5','wait','See CE routes') -luCommand('ce1','vtysh -c "show bgp ipv4 uni 5.1.0.0/24"','','none','See CE routes') -luCommand('r1','vtysh -c "show bgp ipv4 uni 5.1.0.0/24"','','none','See CE routes') -luCommand('ce2','vtysh -c "show bgp ipv4 uni 5.1.0.0/24"','','none','See CE routes') -luCommand('r3','vtysh -c "show bgp ipv4 uni 5.1.0.0/24"','','none','See CE routes') -luCommand('ce3','vtysh -c "show bgp ipv4 uni 5.1.2.0/24"','','none','See CE routes') -luCommand('r4','vtysh -c "show bgp ipv4 uni 5.1.2.0/24"','','none','See CE routes') -luCommand('r1','vtysh -c "add vrf cust1 prefix 99.0.0.1/32"','.','none','IP Address') -luCommand('r1','vtysh -c "show vnc registrations local"','99.0.0.1','wait','Local Registration') -luCommand('r1','vtysh -c "show vnc registrations imported"','2 out of 2 imported','wait','Imported Registrations') -luCommand('r3','vtysh -c "show bgp ipv4 vpn"','i99.0.0.1/32','wait','See R1s static address') -luCommand('r4','vtysh -c "show bgp ipv4 vpn"','i99.0.0.1/32','wait','See R1s static address') -luCommand('r3','vtysh -c "show bgp ipv4 vpn rd 10:1"','i5.*i5','wait','See R1s imports') -luCommand('r4','vtysh -c "show bgp ipv4 vpn rd 10:1"','i5.*i5','wait','See R1s imports') +luCommand( + "r1", 'vtysh -c "show bgp next"', "99.0.0.. valid", "wait", "See CE static NH" +) +luCommand( + "r3", 'vtysh -c "show bgp next"', "99.0.0.. valid", "wait", "See CE static NH" +) +luCommand( + "r4", 'vtysh -c "show bgp next"', "99.0.0.. valid", "wait", "See CE static NH" +) +luCommand("r1", 'vtysh -c "show bgp ipv4 uni"', "i5.*i5", "wait", "See CE routes") +luCommand("r3", 'vtysh -c "show bgp ipv4 uni"', "i5.*i5", "wait", "See CE routes") +luCommand("r4", 'vtysh -c "show bgp ipv4 uni"', "i5.*i5", "wait", "See CE routes") +luCommand("ce1", 'vtysh -c "show bgp ipv4 uni 5.1.0.0/24"', "", "none", "See CE routes") +luCommand("r1", 'vtysh -c "show bgp ipv4 uni 5.1.0.0/24"', "", "none", "See CE routes") +luCommand("ce2", 'vtysh -c "show bgp ipv4 uni 5.1.0.0/24"', "", "none", "See CE routes") +luCommand("r3", 'vtysh -c "show bgp ipv4 uni 5.1.0.0/24"', "", "none", "See CE routes") +luCommand("ce3", 'vtysh -c "show bgp ipv4 uni 5.1.2.0/24"', "", "none", "See CE routes") +luCommand("r4", 'vtysh -c "show bgp ipv4 uni 5.1.2.0/24"', "", "none", "See CE routes") -luCommand('r3','vtysh -c "add vrf cust1 prefix 99.0.0.2/32"','.','none','IP Address') -luCommand('r3','vtysh -c "show vnc registrations local"','99.0.0.2','wait','Local Registration') -have2ndImports = luCommand('r3','vtysh -c "show vnc registrations imported"','2 out of 2 imported','none','Imported Registrations',2) +luCommand( + "r1", 'vtysh -c "add vrf cust1 prefix 99.0.0.1/32"', ".", "none", "IP Address" +) +luCommand( + "r1", + 'vtysh -c "show vnc registrations local"', + "99.0.0.1", + "wait", + "Local Registration", +) +luCommand( + "r1", + 'vtysh -c "show vnc registrations imported"', + "2 out of 2 imported", + "wait", + "Imported Registrations", +) +luCommand( + "r3", + 'vtysh -c "show bgp ipv4 vpn"', + "i99.0.0.1/32", + "wait", + "See R1s static address", +) +luCommand( + "r4", + 'vtysh -c "show bgp ipv4 vpn"', + "i99.0.0.1/32", + "wait", + "See R1s static address", +) +luCommand( + "r3", 'vtysh -c "show bgp ipv4 vpn rd 10:1"', "i5.*i5", "wait", "See R1s imports" +) +luCommand( + "r4", 'vtysh -c "show bgp ipv4 vpn rd 10:1"', "i5.*i5", "wait", "See R1s imports" +) + +luCommand( + "r3", 'vtysh -c "add vrf cust1 prefix 99.0.0.2/32"', ".", "none", "IP Address" +) +luCommand( + "r3", + 'vtysh -c "show vnc registrations local"', + "99.0.0.2", + "wait", + "Local Registration", +) +have2ndImports = luCommand( + "r3", + 'vtysh -c "show vnc registrations imported"', + "2 out of 2 imported", + "none", + "Imported Registrations", + 2, +) if have2ndImports: - luCommand('r3','vtysh -c "show vnc registrations imported"','2 out of 2 imported','pass','Imported Registrations') -luCommand('r1','vtysh -c "show bgp ipv4 vpn"','i99.0.0.2/32','wait','See R3s static address') -luCommand('r4','vtysh -c "show bgp ipv4 vpn"','i99.0.0.2/32','wait','See R3s static address') + luCommand( + "r3", + 'vtysh -c "show vnc registrations imported"', + "2 out of 2 imported", + "pass", + "Imported Registrations", + ) +luCommand( + "r1", + 'vtysh -c "show bgp ipv4 vpn"', + "i99.0.0.2/32", + "wait", + "See R3s static address", +) +luCommand( + "r4", + 'vtysh -c "show bgp ipv4 vpn"', + "i99.0.0.2/32", + "wait", + "See R3s static address", +) if have2ndImports: - luCommand('r1','vtysh -c "show bgp ipv4 vpn rd 10:3"','i5.*i5','none','See R3s imports') - luCommand('r4','vtysh -c "show bgp ipv4 vpn rd 10:3"','i5.*i5','none','See R3s imports') + luCommand( + "r1", + 'vtysh -c "show bgp ipv4 vpn rd 10:3"', + "i5.*i5", + "none", + "See R3s imports", + ) + luCommand( + "r4", + 'vtysh -c "show bgp ipv4 vpn rd 10:3"', + "i5.*i5", + "none", + "See R3s imports", + ) -luCommand('r4','vtysh -c "add vrf cust1 prefix 99.0.0.3/32"','.','none','IP Address') -luCommand('r4','vtysh -c "show vnc registrations local"','99.0.0.3','wait','Local Registration') -luCommand('r4','vtysh -c "show vnc registrations imported"','2 out of 2 imported','wait','Imported Registrations') -luCommand('r1','vtysh -c "show bgp ipv4 vpn"','i99.0.0.3/32','wait','See R4s static address') -luCommand('r3','vtysh -c "show bgp ipv4 vpn"','i99.0.0.3/32','wait','See R4s static address') -luCommand('r1','vtysh -c "show bgp ipv4 vpn rd 10:4"','i5.*i5','wait','See R4s imports') -luCommand('r3','vtysh -c "show bgp ipv4 vpn rd 10:4"','i5.*i5','wait','See R4s imports') +luCommand( + "r4", 'vtysh -c "add vrf cust1 prefix 99.0.0.3/32"', ".", "none", "IP Address" +) +luCommand( + "r4", + 'vtysh -c "show vnc registrations local"', + "99.0.0.3", + "wait", + "Local Registration", +) +luCommand( + "r4", + 'vtysh -c "show vnc registrations imported"', + "2 out of 2 imported", + "wait", + "Imported Registrations", +) +luCommand( + "r1", + 'vtysh -c "show bgp ipv4 vpn"', + "i99.0.0.3/32", + "wait", + "See R4s static address", +) +luCommand( + "r3", + 'vtysh -c "show bgp ipv4 vpn"', + "i99.0.0.3/32", + "wait", + "See R4s static address", +) +luCommand( + "r1", 'vtysh -c "show bgp ipv4 vpn rd 10:4"', "i5.*i5", "wait", "See R4s imports" +) +luCommand( + "r3", 'vtysh -c "show bgp ipv4 vpn rd 10:4"', "i5.*i5", "wait", "See R4s imports" +) -luCommand('r1','vtysh -c "show vnc registrations remote"','5.1.2.0/24 .*5.1.3.0/24','wait','R4s registrations') -luCommand('r3','vtysh -c "show vnc registrations remote"','5.1.2.0/24 .*5.1.3.0/24','wait','R4s registrations') +luCommand( + "r1", + 'vtysh -c "show vnc registrations remote"', + "5.1.2.0/24 .*5.1.3.0/24", + "wait", + "R4s registrations", +) +luCommand( + "r3", + 'vtysh -c "show vnc registrations remote"', + "5.1.2.0/24 .*5.1.3.0/24", + "wait", + "R4s registrations", +) if have2ndImports: - luCommand('r1','vtysh -c "show vnc registrations remote"','5.1.0.0/24 .*5.1.1.0/24','wait','Remote registrations') - luCommand('r3','vtysh -c "show vnc registrations remote"','5.1.0.0/24 .*5.1.1.0/24','wait','Remote registrations') -luCommand('r4','vtysh -c "show vnc registrations remote"','5.1.0.0/24 .*5.1.1.0/24','wait','Remote registrations') -luCommand('r1','vtysh -c "show vnc registrations"','.','none') -luCommand('r3','vtysh -c "show vnc registrations"','.','none') -luCommand('r4','vtysh -c "show vnc registrations"','.','none') + luCommand( + "r1", + 'vtysh -c "show vnc registrations remote"', + "5.1.0.0/24 .*5.1.1.0/24", + "wait", + "Remote registrations", + ) + luCommand( + "r3", + 'vtysh -c "show vnc registrations remote"', + "5.1.0.0/24 .*5.1.1.0/24", + "wait", + "Remote registrations", + ) +luCommand( + "r4", + 'vtysh -c "show vnc registrations remote"', + "5.1.0.0/24 .*5.1.1.0/24", + "wait", + "Remote registrations", +) +luCommand("r1", 'vtysh -c "show vnc registrations"', ".", "none") +luCommand("r3", 'vtysh -c "show vnc registrations"', ".", "none") +luCommand("r4", 'vtysh -c "show vnc registrations"', ".", "none") diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/adjacencies.py b/tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/adjacencies.py index 7b3a883afa..ea059c576e 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/adjacencies.py +++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/adjacencies.py @@ -1,20 +1,64 @@ from lutil import luCommand -luCommand('ce1','ping 192.168.1.1 -c 1',' 0. packet loss','pass','CE->PE ping') -luCommand('ce2','ping 192.168.1.1 -c 1',' 0. packet loss','pass','CE->PE ping') -luCommand('ce3','ping 192.168.1.1 -c 1',' 0. packet loss','pass','CE->PE ping') -luCommand('ce1','vtysh -c "show bgp summary"',' 00:0','wait','Adjacencies up',90) -luCommand('ce2','vtysh -c "show bgp summary"',' 00:0','wait','Adjacencies up') -luCommand('ce3','vtysh -c "show bgp summary"',' 00:0','wait','Adjacencies up') -luCommand('r1','ping 2.2.2.2 -c 1',' 0. packet loss','wait','PE->P2 (loopback) ping',60) -luCommand('r3','ping 2.2.2.2 -c 1',' 0. packet loss','wait','PE->P2 (loopback) ping',60) -luCommand('r4','ping 2.2.2.2 -c 1',' 0. packet loss','wait','PE->P2 (loopback) ping',60) -luCommand('r2','vtysh -c "show bgp summary"',' 00:0.* 00:0.* 00:0','wait','Core adjacencies up') -luCommand('r1','vtysh -c "show bgp summary"',' 00:0','pass','Core adjacencies up') -luCommand('r3','vtysh -c "show bgp summary"',' 00:0','pass','Core adjacencies up') -luCommand('r4','vtysh -c "show bgp summary"',' 00:0','pass','Core adjacencies up') -luCommand('r1','vtysh -c "show bgp vrf all summary"',' 00:0.* 00:0','pass','All adjacencies up') -luCommand('r3','vtysh -c "show bgp vrf all summary"',' 00:0.* 00:0','pass','All adjacencies up') -luCommand('r4','vtysh -c "show bgp vrf all summary"',' 00:0.* 00:0','pass','All adjacencies up') -luCommand('r1','ping 3.3.3.3 -c 1',' 0. packet loss','wait','PE->PE3 (loopback) ping') -luCommand('r1','ping 4.4.4.4 -c 1',' 0. packet loss','wait','PE->PE4 (loopback) ping') -luCommand('r4','ping 3.3.3.3 -c 1',' 0. packet loss','wait','PE->PE3 (loopback) ping') + +luCommand("ce1", "ping 192.168.1.1 -c 1", " 0. packet loss", "pass", "CE->PE ping") +luCommand("ce2", "ping 192.168.1.1 -c 1", " 0. packet loss", "pass", "CE->PE ping") +luCommand("ce3", "ping 192.168.1.1 -c 1", " 0. packet loss", "pass", "CE->PE ping") +luCommand("ce1", 'vtysh -c "show bgp summary"', " 00:0", "wait", "Adjacencies up", 180) +luCommand("ce2", 'vtysh -c "show bgp summary"', " 00:0", "wait", "Adjacencies up", 180) +luCommand("ce3", 'vtysh -c "show bgp summary"', " 00:0", "wait", "Adjacencies up", 180) +luCommand( + "r1", "ping 2.2.2.2 -c 1", " 0. packet loss", "wait", "PE->P2 (loopback) ping", 60 +) +luCommand( + "r3", "ping 2.2.2.2 -c 1", " 0. packet loss", "wait", "PE->P2 (loopback) ping", 60 +) +luCommand( + "r4", "ping 2.2.2.2 -c 1", " 0. packet loss", "wait", "PE->P2 (loopback) ping", 60 +) +luCommand( + "r2", + 'vtysh -c "show bgp summary"', + " 00:0.* 00:0.* 00:0", + "wait", + "Core adjacencies up", + 180, +) +luCommand( + "r1", 'vtysh -c "show bgp summary"', " 00:0", "wait", "Core adjacencies up", 180 +) +luCommand( + "r3", 'vtysh -c "show bgp summary"', " 00:0", "wait", "Core adjacencies up", 180 +) +luCommand( + "r4", 'vtysh -c "show bgp summary"', " 00:0", "wait", "Core adjacencies up", 180 +) +luCommand( + "r1", + 'vtysh -c "show bgp vrf all summary"', + " 00:0.* 00:0", + "pass", + "All adjacencies up", +) +luCommand( + "r3", + 'vtysh -c "show bgp vrf all summary"', + " 00:0.* 00:0", + "pass", + "All adjacencies up", +) +luCommand( + "r4", + 'vtysh -c "show bgp vrf all summary"', + " 00:0.* 00:0", + "pass", + "All adjacencies up", +) +luCommand( + "r1", "ping 3.3.3.3 -c 1", " 0. packet loss", "wait", "PE->PE3 (loopback) ping" +) +luCommand( + "r1", "ping 4.4.4.4 -c 1", " 0. packet loss", "wait", "PE->PE4 (loopback) ping" +) +luCommand( + "r4", "ping 3.3.3.3 -c 1", " 0. packet loss", "wait", "PE->PE3 (loopback) ping" +) diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/check_routes.py b/tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/check_routes.py index 492be9e4da..96b4978261 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/check_routes.py +++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/check_routes.py @@ -1,17 +1,55 @@ from lutil import luCommand -luCommand('ce1','vtysh -c "show bgp ipv4 uni"','7 routes and 7','wait','Local and remote routes') -luCommand('ce2','vtysh -c "show bgp ipv4 uni"','7 routes and 9','wait','Local and remote routes') -luCommand('ce3','vtysh -c "show bgp ipv4 uni"','7 routes and 7','wait','Local and remote routes') -luCommand('r1','vtysh -c "show bgp ipv4 uni"','7 routes and 9','pass','Unicast SAFI') -luCommand('r2','vtysh -c "show bgp ipv4 uni"','No BGP prefixes displayed','pass','Unicast SAFI') -luCommand('r3','vtysh -c "show bgp ipv4 uni"','7 routes and 9','pass','Unicast SAFI') -luCommand('r4','vtysh -c "show bgp ipv4 uni"','7 routes and 9','pass','Unicast SAFI') -have2ndImports = luCommand('r3','vtysh -c "show vnc registrations imported"','2 out of 2 imported','none','Imported Registrations',2) + +luCommand( + "ce1", + 'vtysh -c "show bgp ipv4 uni"', + "7 routes and 7", + "wait", + "Local and remote routes", +) +luCommand( + "ce2", + 'vtysh -c "show bgp ipv4 uni"', + "7 routes and 9", + "wait", + "Local and remote routes", +) +luCommand( + "ce3", + 'vtysh -c "show bgp ipv4 uni"', + "7 routes and 7", + "wait", + "Local and remote routes", +) +luCommand( + "r1", 'vtysh -c "show bgp ipv4 uni"', "7 routes and 9", "pass", "Unicast SAFI" +) +luCommand( + "r2", + 'vtysh -c "show bgp ipv4 uni"', + "No BGP prefixes displayed", + "pass", + "Unicast SAFI", +) +luCommand( + "r3", 'vtysh -c "show bgp ipv4 uni"', "7 routes and 9", "pass", "Unicast SAFI" +) +luCommand( + "r4", 'vtysh -c "show bgp ipv4 uni"', "7 routes and 9", "pass", "Unicast SAFI" +) +have2ndImports = luCommand( + "r3", + 'vtysh -c "show vnc registrations imported"', + "2 out of 2 imported", + "none", + "Imported Registrations", + 2, +) if have2ndImports: - num = '9 routes and 9' + num = "9 routes and 9" else: - num = '7 routes and 7' -luCommand('r1','vtysh -c "show bgp ipv4 vpn"',num,'pass','VPN SAFI') -luCommand('r2','vtysh -c "show bgp ipv4 vpn"',num,'pass','VPN SAFI') -luCommand('r3','vtysh -c "show bgp ipv4 vpn"',num,'pass','VPN SAFI') -luCommand('r4','vtysh -c "show bgp ipv4 vpn"',num,'pass','VPN SAFI') + num = "7 routes and 7" +luCommand("r1", 'vtysh -c "show bgp ipv4 vpn"', num, "pass", "VPN SAFI") +luCommand("r2", 'vtysh -c "show bgp ipv4 vpn"', num, "pass", "VPN SAFI") +luCommand("r3", 'vtysh -c "show bgp ipv4 vpn"', num, "pass", "VPN SAFI") +luCommand("r4", 'vtysh -c "show bgp ipv4 vpn"', num, "pass", "VPN SAFI") diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/cleanup_all.py b/tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/cleanup_all.py index 3a2f037833..9f21d99913 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/cleanup_all.py +++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/cleanup_all.py @@ -1,17 +1,114 @@ from lutil import luCommand -luCommand('r1','vtysh -c "clear vrf cust1 prefix 99.0.0.1/32"','.','none','Cleared VRF route') -luCommand('r3','vtysh -c "clear vrf cust1 prefix 99.0.0.2/32"','.','none','Cleared VRF route') -luCommand('r4','vtysh -c "clear vrf cust1 prefix 99.0.0.3/32"','.','none','Cleared VRF route') -luCommand('r1','vtysh -c "show vnc registrations local"','99.0.0.1','fail','Local Registration cleared') -luCommand('r3','vtysh -c "show vnc registrations local"','99.0.0.2','fail','Local Registration cleared') -luCommand('r4','vtysh -c "show vnc registrations local"','99.0.0.3','fail','Local Registration cleared') -luCommand('r1','vtysh -c "show bgp ipv4 uni"','2 routes and 2','wait','Unicast SAFI updated') -luCommand('r2','vtysh -c "show bgp ipv4 uni"','No BGP prefixes displayed','pass','Unicast SAFI') -luCommand('r3','vtysh -c "show bgp ipv4 uni"','2 routes and 2','wait','Unicast SAFI updated') -luCommand('r4','vtysh -c "show bgp ipv4 uni"','2 routes and 2','wait','Unicast SAFI updated') -luCommand('ce1','vtysh -c "show bgp ipv4 uni"','2 routes and 2','wait','Local and remote routes') -luCommand('ce2','vtysh -c "show bgp ipv4 uni"','2 routes and 2','wait','Local and remote routes') -luCommand('ce3','vtysh -c "show bgp ipv4 uni"','2 routes and 2','wait','Local and remote routes') -luCommand('r1','vtysh -c "show vnc registrations remote"','Prefix ','fail','Remote Registration cleared') -luCommand('r3','vtysh -c "show vnc registrations remote"','Prefix ','fail','Remote Registration cleared') -luCommand('r4','vtysh -c "show vnc registrations remote"','Prefix ','fail','Remote Registration cleared') + +luCommand( + "r1", + 'vtysh -c "clear vrf cust1 prefix 99.0.0.1/32"', + ".", + "none", + "Cleared VRF route", +) +luCommand( + "r3", + 'vtysh -c "clear vrf cust1 prefix 99.0.0.2/32"', + ".", + "none", + "Cleared VRF route", +) +luCommand( + "r4", + 'vtysh -c "clear vrf cust1 prefix 99.0.0.3/32"', + ".", + "none", + "Cleared VRF route", +) +luCommand( + "r1", + 'vtysh -c "show vnc registrations local"', + "99.0.0.1", + "fail", + "Local Registration cleared", +) +luCommand( + "r3", + 'vtysh -c "show vnc registrations local"', + "99.0.0.2", + "fail", + "Local Registration cleared", +) +luCommand( + "r4", + 'vtysh -c "show vnc registrations local"', + "99.0.0.3", + "fail", + "Local Registration cleared", +) +luCommand( + "r1", + 'vtysh -c "show bgp ipv4 uni"', + "2 routes and 2", + "wait", + "Unicast SAFI updated", +) +luCommand( + "r2", + 'vtysh -c "show bgp ipv4 uni"', + "No BGP prefixes displayed", + "pass", + "Unicast SAFI", +) +luCommand( + "r3", + 'vtysh -c "show bgp ipv4 uni"', + "2 routes and 2", + "wait", + "Unicast SAFI updated", +) +luCommand( + "r4", + 'vtysh -c "show bgp ipv4 uni"', + "2 routes and 2", + "wait", + "Unicast SAFI updated", +) +luCommand( + "ce1", + 'vtysh -c "show bgp ipv4 uni"', + "2 routes and 2", + "wait", + "Local and remote routes", +) +luCommand( + "ce2", + 'vtysh -c "show bgp ipv4 uni"', + "2 routes and 2", + "wait", + "Local and remote routes", +) +luCommand( + "ce3", + 'vtysh -c "show bgp ipv4 uni"', + "2 routes and 2", + "wait", + "Local and remote routes", +) +luCommand( + "r1", + 'vtysh -c "show vnc registrations remote"', + "Prefix ", + "fail", + "Remote Registration cleared", +) +luCommand( + "r3", + 'vtysh -c "show vnc registrations remote"', + "Prefix ", + "fail", + "Remote Registration cleared", +) +luCommand( + "r4", + 'vtysh -c "show vnc registrations remote"', + "Prefix ", + "fail", + "Remote Registration cleared", +) diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/test_bgp_l3vpn_to_bgp_direct.py b/tests/topotests/bgp_l3vpn_to_bgp_direct/test_bgp_l3vpn_to_bgp_direct.py index f710c84c37..d226904102 100755 --- a/tests/topotests/bgp_l3vpn_to_bgp_direct/test_bgp_l3vpn_to_bgp_direct.py +++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/test_bgp_l3vpn_to_bgp_direct.py @@ -25,46 +25,51 @@ import sys import pytest -sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), '..')) +sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")) from lib.ltemplate import * + def test_adjacencies(): CliOnFail = None # For debugging, uncomment the next line - #CliOnFail = 'tgen.mininet_cli' - CheckFunc = 'ltemplateVersionCheck(\'3.1\')' - #uncomment next line to start cli *before* script is run - #CheckFunc = 'ltemplateVersionCheck(\'3.1\', cli=True)' - ltemplateTest('scripts/adjacencies.py', False, CliOnFail, CheckFunc) + # CliOnFail = 'tgen.mininet_cli' + CheckFunc = "ltemplateVersionCheck('3.1')" + # uncomment next line to start cli *before* script is run + # CheckFunc = 'ltemplateVersionCheck(\'3.1\', cli=True)' + ltemplateTest("scripts/adjacencies.py", False, CliOnFail, CheckFunc) + def test_add_routes(): CliOnFail = None # For debugging, uncomment the next line - #CliOnFail = 'tgen.mininet_cli' - CheckFunc = 'ltemplateVersionCheck(\'3.1\')' - #uncomment next line to start cli *before* script is run - #CheckFunc = 'ltemplateVersionCheck(\'3.1\', cli=True)' - ltemplateTest('scripts/add_routes.py', False, CliOnFail, CheckFunc) + # CliOnFail = 'tgen.mininet_cli' + CheckFunc = "ltemplateVersionCheck('3.1')" + # uncomment next line to start cli *before* script is run + # CheckFunc = 'ltemplateVersionCheck(\'3.1\', cli=True)' + ltemplateTest("scripts/add_routes.py", False, CliOnFail, CheckFunc) + def test_check_routes(): CliOnFail = None # For debugging, uncomment the next line - #CliOnFail = 'tgen.mininet_cli' - CheckFunc = 'ltemplateVersionCheck(\'3.1\')' - #uncomment next line to start cli *before* script is run - #CheckFunc = 'ltemplateVersionCheck(\'3.1\', cli=True)' - ltemplateTest('scripts/check_routes.py', False, CliOnFail, CheckFunc) + # CliOnFail = 'tgen.mininet_cli' + CheckFunc = "ltemplateVersionCheck('3.1')" + # uncomment next line to start cli *before* script is run + # CheckFunc = 'ltemplateVersionCheck(\'3.1\', cli=True)' + ltemplateTest("scripts/check_routes.py", False, CliOnFail, CheckFunc) + def test_cleanup_all(): CliOnFail = None # For debugging, uncomment the next line - #CliOnFail = 'tgen.mininet_cli' - CheckFunc = 'ltemplateVersionCheck(\'3.1\')' - #uncomment next line to start cli *before* script is run - #CheckFunc = 'ltemplateVersionCheck(\'3.1\', cli=True)' - ltemplateTest('scripts/cleanup_all.py', False, CliOnFail, CheckFunc) + # CliOnFail = 'tgen.mininet_cli' + CheckFunc = "ltemplateVersionCheck('3.1')" + # uncomment next line to start cli *before* script is run + # CheckFunc = 'ltemplateVersionCheck(\'3.1\', cli=True)' + ltemplateTest("scripts/cleanup_all.py", False, CliOnFail, CheckFunc) + -if __name__ == '__main__': +if __name__ == "__main__": retval = pytest.main(["-s"]) sys.exit(retval) diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/bgpd.conf index 4bd0f95f2c..6e6b9edde2 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/bgpd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/bgpd.conf @@ -3,18 +3,21 @@ frr defaults traditional hostname ce1 password zebra log stdout notifications -log monitor notifications log commands log file bgpd.log router bgp 5227 + no bgp network import-check bgp router-id 99.0.0.1 + no bgp ebgp-requires-policy neighbor 192.168.1.1 remote-as 5227 neighbor 192.168.1.1 update-source 192.168.1.2 + neighbor 192.168.1.1 timers 3 10 address-family ipv4 unicast network 99.0.0.1/32 network 5.1.0.0/24 route-map rm-nh network 5.1.1.0/24 route-map rm-nh + redistribute sharp route-map sharp-nh neighbor 192.168.1.1 activate exit-address-family ! @@ -29,8 +32,14 @@ route-map rm-nh permit 10 set extcommunity rt 89:123 set community 0:67 ! +route-map sharp-nh permit 10 + match ip address al-any + set ip next-hop 99.0.0.1 + set local-preference 200 + set metric 200 + set large-community 90:12:34 + set extcommunity rt 80:987 + set community 0:65 +! end - - - diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/sharpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/sharpd.conf new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/bgpd.conf index 2115f08741..618acabd9f 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/bgpd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/bgpd.conf @@ -3,18 +3,21 @@ frr defaults traditional hostname ce2 password zebra log stdout notifications -log monitor notifications log commands log file bgpd.log router bgp 5227 + no bgp network import-check bgp router-id 99.0.0.2 + no bgp ebgp-requires-policy neighbor 192.168.1.1 remote-as 5227 neighbor 192.168.1.1 update-source 192.168.1.2 + neighbor 192.168.1.1 timers 3 10 address-family ipv4 unicast network 99.0.0.2/32 network 5.1.0.0/24 route-map rm-nh network 5.1.1.0/24 route-map rm-nh + redistribute sharp route-map sharp-nh neighbor 192.168.1.1 activate exit-address-family ! @@ -29,8 +32,14 @@ route-map rm-nh permit 10 set extcommunity rt 89:123 set community 0:67 ! +route-map sharp-nh permit 10 + match ip address al-any + set ip next-hop 99.0.0.2 + set local-preference 200 + set metric 200 + set large-community 78:90:12 + set extcommunity rt 70:456 + set community 0:66 +! end - - - diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/sharpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/sharpd.conf new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce3/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce3/bgpd.conf index a65b36f364..85c5973e6f 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce3/bgpd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce3/bgpd.conf @@ -3,14 +3,16 @@ frr defaults traditional hostname ce3 password zebra log stdout notifications -log monitor notifications log commands log file bgpd.log router bgp 5227 + no bgp network import-check bgp router-id 99.0.0.3 + no bgp ebgp-requires-policy neighbor 192.168.1.1 remote-as 5227 neighbor 192.168.1.1 update-source 192.168.1.2 + neighbor 192.168.1.1 timers 3 10 address-family ipv4 unicast network 99.0.0.3/32 network 5.1.2.0/24 route-map rm-nh @@ -31,6 +33,3 @@ route-map rm-nh permit 10 ! end - - - diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce4/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce4/bgpd.conf index cb08db5314..6a5075a000 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce4/bgpd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce4/bgpd.conf @@ -3,14 +3,16 @@ frr defaults traditional hostname ce4 password zebra log stdout notifications -log monitor notifications log commands log file bgpd.log router bgp 5228 vrf ce4-cust2 + no bgp network import-check bgp router-id 99.0.0.4 + no bgp ebgp-requires-policy neighbor 192.168.2.1 remote-as 5228 neighbor 192.168.2.1 update-source 192.168.2.2 + neighbor 192.168.2.1 timers 3 10 address-family ipv4 unicast network 99.0.0.4/32 network 5.4.2.0/24 route-map rm-nh @@ -31,6 +33,3 @@ route-map rm-nh permit 10 ! end - - - diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/customize.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/customize.py index ce542413ba..3530141622 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/customize.py +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/customize.py @@ -84,17 +84,21 @@ from lib.topogen import Topogen, TopoRouter, get_topogen from lib.topolog import logger from lib.ltemplate import ltemplateRtrCmd +from lib.common_config import adjust_router_l3mdev # Required to instantiate the topology builder class. from mininet.topo import Topo import shutil + CWD = os.path.dirname(os.path.realpath(__file__)) # test name based on directory TEST = os.path.basename(CWD) + class ThisTestTopo(Topo): "Test topology builder" + def build(self, *_args, **_opts): "Build function" tgen = get_topogen(self) @@ -103,121 +107,137 @@ def build(self, *_args, **_opts): # between routers, switches and hosts. # # Create P/PE routers - #check for mpls - tgen.add_router('r1') + # check for mpls + tgen.add_router("r1") if tgen.hasmpls != True: - logger.info('MPLS not available, tests will be skipped') + logger.info("MPLS not available, tests will be skipped") return mach = platform.machine() krel = platform.release() - if mach[:1] == 'a' and topotest.version_cmp(krel, '4.11') < 0: - logger.info('Need Kernel version 4.11 to run on arm processor') + if mach[:1] == "a" and topotest.version_cmp(krel, "4.11") < 0: + logger.info("Need Kernel version 4.11 to run on arm processor") return for routern in range(2, 5): - tgen.add_router('r{}'.format(routern)) + tgen.add_router("r{}".format(routern)) # Create CE routers for routern in range(1, 5): - tgen.add_router('ce{}'.format(routern)) + tgen.add_router("ce{}".format(routern)) - #CE/PE links - tgen.add_link(tgen.gears['ce1'], tgen.gears['r1'], 'ce1-eth0', 'r1-eth4') - tgen.add_link(tgen.gears['ce2'], tgen.gears['r3'], 'ce2-eth0', 'r3-eth4') - tgen.add_link(tgen.gears['ce3'], tgen.gears['r4'], 'ce3-eth0', 'r4-eth4') - tgen.add_link(tgen.gears['ce4'], tgen.gears['r4'], 'ce4-eth0', 'r4-eth5') + # CE/PE links + tgen.add_link(tgen.gears["ce1"], tgen.gears["r1"], "ce1-eth0", "r1-eth4") + tgen.add_link(tgen.gears["ce2"], tgen.gears["r3"], "ce2-eth0", "r3-eth4") + tgen.add_link(tgen.gears["ce3"], tgen.gears["r4"], "ce3-eth0", "r4-eth4") + tgen.add_link(tgen.gears["ce4"], tgen.gears["r4"], "ce4-eth0", "r4-eth5") # Create a switch with just one router connected to it to simulate a # empty network. switch = {} - switch[0] = tgen.add_switch('sw0') - switch[0].add_link(tgen.gears['r1'], nodeif='r1-eth0') - switch[0].add_link(tgen.gears['r2'], nodeif='r2-eth0') + switch[0] = tgen.add_switch("sw0") + switch[0].add_link(tgen.gears["r1"], nodeif="r1-eth0") + switch[0].add_link(tgen.gears["r2"], nodeif="r2-eth0") - switch[1] = tgen.add_switch('sw1') - switch[1].add_link(tgen.gears['r2'], nodeif='r2-eth1') - switch[1].add_link(tgen.gears['r3'], nodeif='r3-eth0') - switch[1].add_link(tgen.gears['r4'], nodeif='r4-eth0') + switch[1] = tgen.add_switch("sw1") + switch[1].add_link(tgen.gears["r2"], nodeif="r2-eth1") + switch[1].add_link(tgen.gears["r3"], nodeif="r3-eth0") + switch[1].add_link(tgen.gears["r4"], nodeif="r4-eth0") - switch[1] = tgen.add_switch('sw2') - switch[1].add_link(tgen.gears['r2'], nodeif='r2-eth2') - switch[1].add_link(tgen.gears['r3'], nodeif='r3-eth1') + switch[1] = tgen.add_switch("sw2") + switch[1].add_link(tgen.gears["r2"], nodeif="r2-eth2") + switch[1].add_link(tgen.gears["r3"], nodeif="r3-eth1") -l3mdev_accept = 0 def ltemplatePreRouterStartHook(): - global l3mdev_accept cc = ltemplateRtrCmd() krel = platform.release() tgen = get_topogen() - logger.info('pre router-start hook, kernel=' + krel) - if topotest.version_cmp(krel, '4.15') == 0: - l3mdev_accept = 1 - else: - l3mdev_accept = 0 - logger.info('setting net.ipv4.tcp_l3mdev_accept={}'.format(l3mdev_accept)) - #check for mpls + logger.info("pre router-start hook, kernel=" + krel) + + # check for mpls if tgen.hasmpls != True: - logger.info('MPLS not available, skipping setup') + logger.info("MPLS not available, skipping setup") return False - #check for normal init + # check for normal init if len(tgen.net) == 1: - logger.info('Topology not configured, skipping setup') + logger.info("Topology not configured, skipping setup") return False - #trace errors/unexpected output + # trace errors/unexpected output cc.resetCounts() - #configure r2 mpls interfaces - intfs = ['lo', 'r2-eth0', 'r2-eth1', 'r2-eth2'] + # configure r2 mpls interfaces + intfs = ["lo", "r2-eth0", "r2-eth1", "r2-eth2"] for intf in intfs: - cc.doCmd(tgen, 'r2', 'echo 1 > /proc/sys/net/mpls/conf/{}/input'.format(intf)) - - #configure cust1 VRFs & MPLS - rtrs = ['r1', 'r3', 'r4'] - cmds = ['ip link add {0}-cust1 type vrf table 10', - 'ip ru add oif {0}-cust1 table 10', - 'ip ru add iif {0}-cust1 table 10', - 'ip link set dev {0}-cust1 up', - 'sysctl -w net.ipv4.tcp_l3mdev_accept={}'.format(l3mdev_accept)] + cc.doCmd(tgen, "r2", "echo 1 > /proc/sys/net/mpls/conf/{}/input".format(intf)) + + # configure cust1 VRFs & MPLS + rtrs = ["r1", "r3", "r4"] + cmds = [ + "ip link add {0}-cust1 type vrf table 10", + "ip ru add oif {0}-cust1 table 10", + "ip ru add iif {0}-cust1 table 10", + "ip link set dev {0}-cust1 up", + ] for rtr in rtrs: - router = tgen.gears[rtr] + # adjust handling of VRF traffic + adjust_router_l3mdev(tgen, rtr) + for cmd in cmds: cc.doCmd(tgen, rtr, cmd.format(rtr)) - cc.doCmd(tgen, rtr, 'ip link set dev {0}-eth4 master {0}-cust1'.format(rtr)) - intfs = [rtr+'-cust1', 'lo', rtr+'-eth0', rtr+'-eth4'] + cc.doCmd(tgen, rtr, "ip link set dev {0}-eth4 master {0}-cust1".format(rtr)) + intfs = [rtr + "-cust1", "lo", rtr + "-eth0", rtr + "-eth4"] for intf in intfs: - cc.doCmd(tgen, rtr, 'echo 1 > /proc/sys/net/mpls/conf/{}/input'.format(intf)) - logger.info('setup {0} vrf {0}-cust1, {0}-eth4. enabled mpls input.'.format(rtr)) - #configure cust2 VRFs & MPLS - rtrs = ['r4'] - cmds = ['ip link add {0}-cust2 type vrf table 20', - 'ip ru add oif {0}-cust2 table 20', - 'ip ru add iif {0}-cust2 table 20', - 'ip link set dev {0}-cust2 up'] + cc.doCmd( + tgen, rtr, "echo 1 > /proc/sys/net/mpls/conf/{}/input".format(intf) + ) + logger.info( + "setup {0} vrf {0}-cust1, {0}-eth4. enabled mpls input.".format(rtr) + ) + # configure cust2 VRFs & MPLS + rtrs = ["r4"] + cmds = [ + "ip link add {0}-cust2 type vrf table 20", + "ip ru add oif {0}-cust2 table 20", + "ip ru add iif {0}-cust2 table 20", + "ip link set dev {0}-cust2 up", + ] for rtr in rtrs: for cmd in cmds: cc.doCmd(tgen, rtr, cmd.format(rtr)) - cc.doCmd(tgen, rtr, 'ip link set dev {0}-eth5 master {0}-cust2'.format(rtr)) - intfs = [rtr+'-cust2', rtr+'-eth5'] + cc.doCmd(tgen, rtr, "ip link set dev {0}-eth5 master {0}-cust2".format(rtr)) + intfs = [rtr + "-cust2", rtr + "-eth5"] for intf in intfs: - cc.doCmd(tgen, rtr, 'echo 1 > /proc/sys/net/mpls/conf/{}/input'.format(intf)) - logger.info('setup {0} vrf {0}-cust2, {0}-eth5. enabled mpls input.'.format(rtr)) - #put ce4-eth0 into a VRF (no default instance!) - rtrs = ['ce4'] - cmds = ['ip link add {0}-cust2 type vrf table 20', - 'ip ru add oif {0}-cust2 table 20', - 'ip ru add iif {0}-cust2 table 20', - 'ip link set dev {0}-cust2 up', - 'sysctl -w net.ipv4.tcp_l3mdev_accept={}'.format(l3mdev_accept)] + cc.doCmd( + tgen, rtr, "echo 1 > /proc/sys/net/mpls/conf/{}/input".format(intf) + ) + logger.info( + "setup {0} vrf {0}-cust2, {0}-eth5. enabled mpls input.".format(rtr) + ) + # put ce4-eth0 into a VRF (no default instance!) + rtrs = ["ce4"] + cmds = [ + "ip link add {0}-cust2 type vrf table 20", + "ip ru add oif {0}-cust2 table 20", + "ip ru add iif {0}-cust2 table 20", + "ip link set dev {0}-cust2 up", + ] for rtr in rtrs: + # adjust handling of VRF traffic + adjust_router_l3mdev(tgen, rtr) + for cmd in cmds: cc.doCmd(tgen, rtr, cmd.format(rtr)) - cc.doCmd(tgen, rtr, 'ip link set dev {0}-eth0 master {0}-cust2'.format(rtr)) + cc.doCmd(tgen, rtr, "ip link set dev {0}-eth0 master {0}-cust2".format(rtr)) if cc.getOutput() != 4: InitSuccess = False - logger.info('Unexpected output seen ({} times, tests will be skipped'.format(cc.getOutput())) + logger.info( + "Unexpected output seen ({} times, tests will be skipped".format( + cc.getOutput() + ) + ) else: InitSuccess = True - logger.info('VRF config successful!') + logger.info("VRF config successful!") return InitSuccess + def ltemplatePostRouterStartHook(): - logger.info('post router-start hook') + logger.info("post router-start hook") return True diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/bgpd.conf index c1bad0b7c6..8d42cfc0d8 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/bgpd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/bgpd.conf @@ -3,7 +3,6 @@ frr defaults traditional hostname r1 password zebra log stdout notifications -log monitor notifications log commands log file bgpd.log debugging @@ -16,8 +15,10 @@ log file bgpd.log debugging router bgp 5226 bgp router-id 1.1.1.1 bgp cluster-id 1.1.1.1 + no bgp ebgp-requires-policy neighbor 2.2.2.2 remote-as 5226 neighbor 2.2.2.2 update-source 1.1.1.1 + neighbor 2.2.2.2 timers 3 10 address-family ipv4 unicast no neighbor 2.2.2.2 activate @@ -31,9 +32,11 @@ router bgp 5226 router bgp 5227 vrf r1-cust1 bgp router-id 192.168.1.1 + no bgp ebgp-requires-policy neighbor 192.168.1.2 remote-as 5227 neighbor 192.168.1.2 update-source 192.168.1.1 + neighbor 192.168.1.2 timers 3 10 address-family ipv4 unicast neighbor 192.168.1.2 activate diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/ospfd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/ospfd.conf index c5097e214f..460a8fb016 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/ospfd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/ospfd.conf @@ -6,3 +6,7 @@ router ospf network 0.0.0.0/4 area 0 redistribute static ! +int r1-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/bgpd.conf index 4ccb0ca5c0..7b42b770b5 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/bgpd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/bgpd.conf @@ -3,19 +3,22 @@ frr defaults traditional hostname r2 password zebra log stdout notifications -log monitor notifications log commands log file bgpd.log debugging router bgp 5226 bgp router-id 2.2.2.2 bgp cluster-id 2.2.2.2 + no bgp ebgp-requires-policy neighbor 1.1.1.1 remote-as 5226 neighbor 1.1.1.1 update-source 2.2.2.2 + neighbor 1.1.1.1 timers 3 10 neighbor 3.3.3.3 remote-as 5226 neighbor 3.3.3.3 update-source 2.2.2.2 + neighbor 3.3.3.3 timers 3 10 neighbor 4.4.4.4 remote-as 5226 neighbor 4.4.4.4 update-source 2.2.2.2 + neighbor 4.4.4.4 timers 3 10 address-family ipv4 unicast no neighbor 1.1.1.1 activate no neighbor 3.3.3.3 activate @@ -30,6 +33,3 @@ router bgp 5226 neighbor 4.4.4.4 route-reflector-client exit-address-family end - - - diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/ospfd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/ospfd.conf index 8678813665..dbed6189c8 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/ospfd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/ospfd.conf @@ -5,3 +5,15 @@ router ospf router-id 2.2.2.2 network 0.0.0.0/0 area 0 ! +int r2-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +int r2-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +int r2-eth2 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/bgpd.conf index 2004612557..6c9640eea0 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/bgpd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/bgpd.conf @@ -3,16 +3,17 @@ frr defaults traditional hostname r3 password zebra log stdout notifications -log monitor notifications log commands log file bgpd.log -debug bgp vpn label +#debug bgp vpn label router bgp 5226 bgp router-id 3.3.3.3 bgp cluster-id 3.3.3.3 + no bgp ebgp-requires-policy neighbor 2.2.2.2 remote-as 5226 neighbor 2.2.2.2 update-source 3.3.3.3 + neighbor 2.2.2.2 timers 3 10 address-family ipv4 unicast no neighbor 2.2.2.2 activate @@ -25,9 +26,11 @@ router bgp 5226 router bgp 5227 vrf r3-cust1 bgp router-id 192.168.1.1 + no bgp ebgp-requires-policy neighbor 192.168.1.2 remote-as 5227 - neighbor 192.168.1.2 update-source 192.168.1.1 + neighbor 192.168.1.2 update-source 192.168.1.1 + neighbor 192.168.1.2 timers 3 10 address-family ipv4 unicast neighbor 192.168.1.2 activate diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/ospfd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/ospfd.conf index c7c358f9dc..0e64ed6fc5 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/ospfd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/ospfd.conf @@ -7,3 +7,11 @@ router ospf network 0.0.0.0/4 area 0 redistribute static ! +int r3-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +int r3-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/bgpd.conf index b2df5990ce..ca9e627172 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/bgpd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/bgpd.conf @@ -3,19 +3,20 @@ frr defaults traditional hostname r4 password zebra log stdout notifications -log monitor notifications log commands log file bgpd.log debug -debug bgp vpn label -debug bgp nht -debug bgp zebra +#debug bgp vpn label +#debug bgp nht +#debug bgp zebra router bgp 5226 bgp router-id 4.4.4.4 bgp cluster-id 4.4.4.4 + no bgp ebgp-requires-policy neighbor 2.2.2.2 remote-as 5226 neighbor 2.2.2.2 update-source 4.4.4.4 + neighbor 2.2.2.2 timers 3 10 address-family ipv4 unicast no neighbor 2.2.2.2 activate @@ -28,9 +29,11 @@ router bgp 5226 router bgp 5227 vrf r4-cust1 bgp router-id 192.168.1.1 + no bgp ebgp-requires-policy neighbor 192.168.1.2 remote-as 5227 - neighbor 192.168.1.2 update-source 192.168.1.1 + neighbor 192.168.1.2 update-source 192.168.1.1 + neighbor 192.168.1.2 timers 3 10 address-family ipv4 unicast neighbor 192.168.1.2 activate @@ -47,9 +50,11 @@ router bgp 5227 vrf r4-cust1 router bgp 5228 vrf r4-cust2 bgp router-id 192.168.2.1 + no bgp ebgp-requires-policy neighbor 192.168.2.2 remote-as 5228 neighbor 192.168.2.2 update-source 192.168.2.1 + neighbor 192.168.2.2 timers 3 10 address-family ipv4 unicast neighbor 192.168.2.2 activate diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/ospfd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/ospfd.conf index 83d09c09a4..89e37df479 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/ospfd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/ospfd.conf @@ -6,3 +6,7 @@ router ospf network 0.0.0.0/4 area 0 redistribute static ! +int r4-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/add_routes.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/add_routes.py index 19b73d2057..5c7427763d 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/add_routes.py +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/add_routes.py @@ -1,13 +1,59 @@ from lutil import luCommand -luCommand('r1','vtysh -c "add vrf r1-cust1 prefix 99.0.0.1/32"','.','none','IP Address') -luCommand('r3','vtysh -c "add vrf r3-cust1 prefix 99.0.0.2/32"','.','none','IP Address') -luCommand('r4','vtysh -c "add vrf r4-cust1 prefix 99.0.0.3/32"','.','none','IP Address') -luCommand('r1','vtysh -c "show vnc registrations local"','99.0.0.1','pass','Local Registration') -luCommand('r3','vtysh -c "show vnc registrations local"','99.0.0.2','pass','Local Registration') -luCommand('r4','vtysh -c "show vnc registrations local"','99.0.0.3','pass','Local Registration') -luCommand('r1','vtysh -c "show vnc registrations remote"','4 out of 4','wait','Remote Registration', 10) -luCommand('r3','vtysh -c "show vnc registrations remote"','6 out of 6','wait','Remote Registration', 10) -luCommand('r4','vtysh -c "show vnc registrations remote"','4 out of 4','wait','Remote Registration', 10) -luCommand('r1','vtysh -c "show vnc registrations"','.','none') -luCommand('r3','vtysh -c "show vnc registrations"','.','none') -luCommand('r4','vtysh -c "show vnc registrations"','.','none') + +luCommand( + "r1", 'vtysh -c "add vrf r1-cust1 prefix 99.0.0.1/32"', ".", "none", "IP Address" +) +luCommand( + "r3", 'vtysh -c "add vrf r3-cust1 prefix 99.0.0.2/32"', ".", "none", "IP Address" +) +luCommand( + "r4", 'vtysh -c "add vrf r4-cust1 prefix 99.0.0.3/32"', ".", "none", "IP Address" +) +luCommand( + "r1", + 'vtysh -c "show vnc registrations local"', + "99.0.0.1", + "pass", + "Local Registration", +) +luCommand( + "r3", + 'vtysh -c "show vnc registrations local"', + "99.0.0.2", + "pass", + "Local Registration", +) +luCommand( + "r4", + 'vtysh -c "show vnc registrations local"', + "99.0.0.3", + "pass", + "Local Registration", +) +luCommand( + "r1", + 'vtysh -c "show vnc registrations remote"', + "4 out of 4", + "wait", + "Remote Registration", + 10, +) +luCommand( + "r3", + 'vtysh -c "show vnc registrations remote"', + "6 out of 6", + "wait", + "Remote Registration", + 10, +) +luCommand( + "r4", + 'vtysh -c "show vnc registrations remote"', + "4 out of 4", + "wait", + "Remote Registration", + 10, +) +luCommand("r1", 'vtysh -c "show vnc registrations"', ".", "none") +luCommand("r3", 'vtysh -c "show vnc registrations"', ".", "none") +luCommand("r4", 'vtysh -c "show vnc registrations"', ".", "none") diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/adjacencies.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/adjacencies.py index 5674120b9c..53cf353fa0 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/adjacencies.py +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/adjacencies.py @@ -1,18 +1,64 @@ from lutil import luCommand -luCommand('ce1','vtysh -c "show bgp summary"',' 00:0','wait','Adjacencies up',180) -luCommand('ce2','vtysh -c "show bgp summary"',' 00:0','wait','Adjacencies up',180) -luCommand('ce3','vtysh -c "show bgp summary"',' 00:0','wait','Adjacencies up',180) -luCommand('ce4','vtysh -c "show bgp vrf all summary"',' 00:0','wait','Adjacencies up',180) -luCommand('r1','ping 2.2.2.2 -c 1',' 0. packet loss','wait','PE->P2 (loopback) ping',60) -luCommand('r3','ping 2.2.2.2 -c 1',' 0. packet loss','wait','PE->P2 (loopback) ping',60) -luCommand('r4','ping 2.2.2.2 -c 1',' 0. packet loss','wait','PE->P2 (loopback) ping',60) -luCommand('r2','vtysh -c "show bgp summary"',' 00:0.* 00:0.* 00:0','wait','Core adjacencies up',300) -luCommand('r1','vtysh -c "show bgp summary"',' 00:0','pass','Core adjacencies up') -luCommand('r3','vtysh -c "show bgp summary"',' 00:0','pass','Core adjacencies up') -luCommand('r4','vtysh -c "show bgp summary"',' 00:0','pass','Core adjacencies up') -luCommand('r1','vtysh -c "show bgp vrf all summary"',' 00:0.* 00:0','pass','All adjacencies up') -luCommand('r3','vtysh -c "show bgp vrf all summary"',' 00:0.* 00:0','pass','All adjacencies up') -luCommand('r4','vtysh -c "show bgp vrf all summary"',' 00:0.* 00:0.* 00:0','pass','All adjacencies up') -luCommand('r1','ping 3.3.3.3 -c 1',' 0. packet loss','wait','PE->PE3 (loopback) ping') -luCommand('r1','ping 4.4.4.4 -c 1',' 0. packet loss','wait','PE->PE4 (loopback) ping') -luCommand('r4','ping 3.3.3.3 -c 1',' 0. packet loss','wait','PE->PE3 (loopback) ping') + +luCommand("ce1", 'vtysh -c "show bgp summary"', " 00:0", "wait", "Adjacencies up", 180) +luCommand("ce2", 'vtysh -c "show bgp summary"', " 00:0", "wait", "Adjacencies up", 180) +luCommand("ce3", 'vtysh -c "show bgp summary"', " 00:0", "wait", "Adjacencies up", 180) +luCommand( + "ce4", 'vtysh -c "show bgp vrf all summary"', " 00:0", "wait", "Adjacencies up", 180 +) +luCommand( + "r1", "ping 2.2.2.2 -c 1", " 0. packet loss", "wait", "PE->P2 (loopback) ping", 60 +) +luCommand( + "r3", "ping 2.2.2.2 -c 1", " 0. packet loss", "wait", "PE->P2 (loopback) ping", 60 +) +luCommand( + "r4", "ping 2.2.2.2 -c 1", " 0. packet loss", "wait", "PE->P2 (loopback) ping", 60 +) +luCommand( + "r2", + 'vtysh -c "show bgp summary"', + " 00:0.* 00:0.* 00:0", + "wait", + "Core adjacencies up", + 180, +) +luCommand( + "r1", 'vtysh -c "show bgp summary"', " 00:0", "wait", "Core adjacencies up", 180 +) +luCommand( + "r3", 'vtysh -c "show bgp summary"', " 00:0", "wait", "Core adjacencies up", 180 +) +luCommand( + "r4", 'vtysh -c "show bgp summary"', " 00:0", "wait", "Core adjacencies up", 180 +) +luCommand( + "r1", + 'vtysh -c "show bgp vrf all summary"', + " 00:0.* 00:0", + "pass", + "All adjacencies up", +) +luCommand( + "r3", + 'vtysh -c "show bgp vrf all summary"', + " 00:0.* 00:0", + "pass", + "All adjacencies up", +) +luCommand( + "r4", + 'vtysh -c "show bgp vrf all summary"', + " 00:0.* 00:0.* 00:0", + "pass", + "All adjacencies up", +) +luCommand( + "r1", "ping 3.3.3.3 -c 1", " 0. packet loss", "wait", "PE->PE3 (loopback) ping" +) +luCommand( + "r1", "ping 4.4.4.4 -c 1", " 0. packet loss", "wait", "PE->PE4 (loopback) ping" +) +luCommand( + "r4", "ping 3.3.3.3 -c 1", " 0. packet loss", "wait", "PE->PE3 (loopback) ping" +) diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_linux_mpls.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_linux_mpls.py index 9827a9e2c1..20113b1058 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_linux_mpls.py +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_linux_mpls.py @@ -1,47 +1,83 @@ from lutil import luCommand, luLast from lib import topotest -ret = luCommand('r2', 'ip -M route show', - '\d*(?= via inet 10.0.2.4 dev r2-eth1)','wait','See mpls route to r4') +ret = luCommand( + "r2", + "ip -M route show", + "\d*(?= via inet 10.0.2.4 dev r2-eth1)", + "wait", + "See mpls route to r4", +) found = luLast() if ret != False and found != None: label4r4 = found.group(0) - luCommand('r2', 'ip -M route show', - '.', 'pass', - 'See %s as label to r4' % label4r4) - ret = luCommand('r2', 'ip -M route show', - '\d*(?= via inet 10.0.1.1 dev r2-eth0)', 'wait', - 'See mpls route to r1') + luCommand("r2", "ip -M route show", ".", "pass", "See %s as label to r4" % label4r4) + ret = luCommand( + "r2", + "ip -M route show", + "\d*(?= via inet 10.0.1.1 dev r2-eth0)", + "wait", + "See mpls route to r1", + ) found = luLast() if ret != False and found != None: label4r1 = found.group(0) - luCommand('r2', 'ip -M route show', - '.', 'pass', 'See %s as label to r1' % label4r1) - - luCommand('r1', 'ip route show vrf r1-cust1', - '99.0.0.4', 'pass', 'VRF->MPLS PHP route installed') - luCommand('r4', 'ip route show vrf r4-cust2', - '99.0.0.1','pass', 'VRF->MPLS PHP route installed') - - luCommand('r1', 'ip -M route show', '101', 'pass', 'MPLS->VRF route installed') - luCommand('r4', 'ip -M route show', '1041', 'pass', 'MPLS->VRF1 route installed') - luCommand('r4', 'ip -M route show', '1042', 'pass', 'MPLS->VRF2 route installed') - - luCommand('ce1', 'ping 99.0.0.4 -I 99.0.0.1 -c 1', - ' 0. packet loss','wait','CE->CE (loopback) ping - l3vpn+zebra case') - #skip due to VRF weirdness - #luCommand('ce4', 'ping 99.0.0.1 -I 99.0.0.4 -c 1', + luCommand("r2", "ip -M route show", ".", "pass", "See %s as label to r1" % label4r1) + + luCommand( + "r1", + "ip route show vrf r1-cust1", + "99.0.0.4", + "pass", + "VRF->MPLS PHP route installed", + ) + luCommand( + "r4", + "ip route show vrf r4-cust2", + "99.0.0.1", + "pass", + "VRF->MPLS PHP route installed", + ) + + luCommand("r1", "ip -M route show", "101", "pass", "MPLS->VRF route installed") + luCommand("r4", "ip -M route show", "1041", "pass", "MPLS->VRF1 route installed") + luCommand("r4", "ip -M route show", "1042", "pass", "MPLS->VRF2 route installed") + + luCommand( + "ce1", + "ping 99.0.0.4 -I 99.0.0.1 -c 1", + " 0. packet loss", + "wait", + "CE->CE (loopback) ping - l3vpn+zebra case", + ) + # skip due to VRF weirdness + # luCommand('ce4', 'ping 99.0.0.1 -I 99.0.0.4 -c 1', # ' 0. packet loss','wait','CE->CE (loopback) ping - l3vpn+zebra case') - luCommand('ce1', 'ping 99.0.0.4 -I 99.0.0.1 -c 1', - ' 0. packet loss','wait','CE->CE (loopback) ping') - #luCommand('ce4', 'ping 99.0.0.1 -I 99.0.0.4 -c 1', + luCommand( + "ce1", + "ping 99.0.0.4 -I 99.0.0.1 -c 1", + " 0. packet loss", + "wait", + "CE->CE (loopback) ping", + ) + # luCommand('ce4', 'ping 99.0.0.1 -I 99.0.0.4 -c 1', # ' 0. packet loss','wait','CE->CE (loopback) ping') - luCommand('r3', 'ip -M route show', '103', 'pass', 'MPLS->VRF route installed') - luCommand('ce2', 'ping 99.0.0.3 -I 99.0.0.2 -c 1', - ' 0. packet loss','wait','CE2->CE3 (loopback) ping') - luCommand('ce3', 'ping 99.0.0.4 -I 99.0.0.3 -c 1', - ' 0. packet loss','wait','CE3->CE4 (loopback) ping') + luCommand("r3", "ip -M route show", "103", "pass", "MPLS->VRF route installed") + luCommand( + "ce2", + "ping 99.0.0.3 -I 99.0.0.2 -c 1", + " 0. packet loss", + "wait", + "CE2->CE3 (loopback) ping", + ) + luCommand( + "ce3", + "ping 99.0.0.4 -I 99.0.0.3 -c 1", + " 0. packet loss", + "wait", + "CE3->CE4 (loopback) ping", + ) diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_linux_vrf.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_linux_vrf.py index f5d73a8c49..862de9407f 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_linux_vrf.py +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_linux_vrf.py @@ -1,25 +1,94 @@ from lutil import luCommand -from customize import l3mdev_accept -l3mdev_rtrs = ['r1', 'r3', 'r4', 'ce4'] +from common_config import kernel_requires_l3mdev_adjustment + +l3mdev_accept = kernel_requires_l3mdev_adjustment() +l3mdev_rtrs = ["r1", "r3", "r4", "ce4"] for rtr in l3mdev_rtrs: - luCommand(rtr,'sysctl net.ipv4.tcp_l3mdev_accept',' = \d*','none','') + luCommand(rtr, "sysctl net.ipv4.tcp_l3mdev_accept", " = \d*", "none", "") found = luLast() - luCommand(rtr,'ss -naep',':179','pass','IPv4:bgp, l3mdev{}'.format(found.group(0))) - luCommand(rtr,'ss -naep',':.*:179','pass','IPv6:bgp') - luCommand(rtr,'sysctl net.ipv4.tcp_l3mdev_accept',' = {}'.format(l3mdev_accept),'pass','l3mdev matches expected (real/expected{}/{})'.format(found.group(0),l3mdev_accept)) + luCommand( + rtr, "ss -naep", ":179", "pass", "IPv4:bgp, l3mdev{}".format(found.group(0)) + ) + luCommand(rtr, "ss -naep", ":.*:179", "pass", "IPv6:bgp") + luCommand( + rtr, + "sysctl net.ipv4.tcp_l3mdev_accept", + " = {}".format(l3mdev_accept), + "pass", + "l3mdev matches expected (real/expected{}/{})".format( + found.group(0), l3mdev_accept + ), + ) -rtrs = ['r1', 'r3', 'r4'] +rtrs = ["r1", "r3", "r4"] for rtr in rtrs: - luCommand(rtr, 'ip link show type vrf {}-cust1'.format(rtr),'cust1: .*UP,LOWER_UP','pass','VRF cust1 up') - luCommand(rtr, 'ip add show vrf {}-cust1'.format(rtr),'r..eth4: .*UP,LOWER_UP.* 192.168','pass','VRF cust1 IP config') - luCommand(rtr, 'ip route show vrf {}-cust1'.format(rtr),'192.168...0/24 dev r.-eth','pass','VRF cust1 interface route') -luCommand('r4', 'ip link show type vrf r4-cust2','cust2: .*UP,LOWER_UP','pass','VRF cust2 up') -luCommand('r4', 'ip add show vrf r4-cust2','r..eth5.*UP,LOWER_UP.* 192.168','pass','VRF cust1 IP config') -luCommand(rtr, 'ip route show vrf r4-cust2'.format(rtr),'192.168...0/24 dev r.-eth','pass','VRF cust2 interface route') -rtrs = ['ce1', 'ce2', 'ce3'] + luCommand( + rtr, + "ip link show type vrf {}-cust1".format(rtr), + "cust1: .*UP", + "pass", + "VRF cust1 intf up", + ) + luCommand( + rtr, + "ip add show vrf {}-cust1".format(rtr), + "r..eth4.*UP", + "pass", + "VRF cust1 IP intf up", + ) + luCommand( + rtr, + "ip add show vrf {}-cust1".format(rtr), + "192.168", + "pass", + "VRF cust1 IP config", + ) + luCommand( + rtr, + "ip route show vrf {}-cust1".format(rtr), + "192.168...0/24 dev r.-eth", + "pass", + "VRF cust1 interface route", + ) +luCommand("r4", "ip link show type vrf r4-cust2", "cust2: .*UP", "pass", "VRF cust2 up") +luCommand( + "r4", + "ip add show vrf r4-cust2", + "r..eth5.*UP.* 192.168", + "pass", + "VRF cust1 IP config", +) +luCommand( + rtr, + "ip route show vrf r4-cust2".format(rtr), + "192.168...0/24 dev r.-eth", + "pass", + "VRF cust2 interface route", +) +rtrs = ["ce1", "ce2", "ce3"] for rtr in rtrs: - luCommand(rtr, 'ip route show','192.168...0/24 dev ce.-eth0','pass','CE interface route') - luCommand(rtr,'ping 192.168.1.1 -c 1',' 0. packet loss','wait','CE->PE ping') -luCommand('ce4', 'ip link show type vrf ce4-cust2','cust2: .*UP,LOWER_UP','pass','VRF cust2 up') -luCommand('ce4', 'ip route show vrf ce4-cust2','192.168...0/24 dev ce.-eth0','pass','CE interface route') -luCommand('ce4','ping 192.168.2.1 -c 1 -I ce4-cust2',' 0. packet loss','wait','CE4->PE4 ping') + luCommand( + rtr, + "ip route show", + "192.168...0/24 dev ce.-eth0", + "pass", + "CE interface route", + ) + luCommand(rtr, "ping 192.168.1.1 -c 1", " 0. packet loss", "wait", "CE->PE ping") +luCommand( + "ce4", "ip link show type vrf ce4-cust2", "cust2: .*UP", "pass", "VRF cust2 up" +) +luCommand( + "ce4", + "ip route show vrf ce4-cust2", + "192.168...0/24 dev ce.-eth0", + "pass", + "CE interface route", +) +luCommand( + "ce4", + "ping 192.168.2.1 -c 1 -I ce4-cust2", + " 0. packet loss", + "wait", + "CE4->PE4 ping", +) diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_routes.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_routes.py index e47ea5f2cd..f5a29b95c9 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_routes.py +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_routes.py @@ -1,5 +1,5 @@ from lutil import luCommand -from bgprib import bgpribRequireVpnRoutes,bgpribRequireUnicastRoutes +from bgprib import bgpribRequireVpnRoutes, bgpribRequireUnicastRoutes ######################################################################## # CE routers: contain routes they originate @@ -12,32 +12,32 @@ # ce4 vtysh -c "show bgp ipv4 uni" want = [ - {'p':'5.1.0.0/24', 'n':'99.0.0.1'}, - {'p':'5.1.1.0/24', 'n':'99.0.0.1'}, - {'p':'99.0.0.1/32', 'n':'0.0.0.0'}, + {"p": "5.1.0.0/24", "n": "99.0.0.1"}, + {"p": "5.1.1.0/24", "n": "99.0.0.1"}, + {"p": "99.0.0.1/32", "n": "0.0.0.0"}, ] -bgpribRequireUnicastRoutes('ce1','ipv4','','Cust 1 routes in ce1',want) +bgpribRequireUnicastRoutes("ce1", "ipv4", "", "Cust 1 routes in ce1", want) want = [ - {'p':'5.1.0.0/24', 'n':'99.0.0.2'}, - {'p':'5.1.1.0/24', 'n':'99.0.0.2'}, - {'p':'99.0.0.2/32', 'n':'0.0.0.0'}, + {"p": "5.1.0.0/24", "n": "99.0.0.2"}, + {"p": "5.1.1.0/24", "n": "99.0.0.2"}, + {"p": "99.0.0.2/32", "n": "0.0.0.0"}, ] -bgpribRequireUnicastRoutes('ce2','ipv4','','Cust 2 routes in ce1',want) +bgpribRequireUnicastRoutes("ce2", "ipv4", "", "Cust 2 routes in ce1", want) want = [ - {'p':'5.1.2.0/24', 'n':'99.0.0.3'}, - {'p':'5.1.3.0/24', 'n':'99.0.0.3'}, - {'p':'99.0.0.3/32', 'n':'0.0.0.0'}, + {"p": "5.1.2.0/24", "n": "99.0.0.3"}, + {"p": "5.1.3.0/24", "n": "99.0.0.3"}, + {"p": "99.0.0.3/32", "n": "0.0.0.0"}, ] -bgpribRequireUnicastRoutes('ce3','ipv4','','Cust 3 routes in ce1',want) +bgpribRequireUnicastRoutes("ce3", "ipv4", "", "Cust 3 routes in ce1", want) want = [ - {'p':'5.4.2.0/24', 'n':'99.0.0.4'}, - {'p':'5.4.3.0/24', 'n':'99.0.0.4'}, - {'p':'99.0.0.4/32', 'n':'0.0.0.0'}, + {"p": "5.4.2.0/24", "n": "99.0.0.4"}, + {"p": "5.4.3.0/24", "n": "99.0.0.4"}, + {"p": "99.0.0.4/32", "n": "0.0.0.0"}, ] -bgpribRequireUnicastRoutes('ce4','ipv4','ce4-cust2','Cust 4 routes in ce1',want) +bgpribRequireUnicastRoutes("ce4", "ipv4", "ce4-cust2", "Cust 4 routes in ce1", want) ######################################################################## @@ -47,116 +47,169 @@ # r1 vtysh -c "show bgp vrf r1-cust1 ipv4" # want_r1_cust1_routes = [ - {'p':'5.1.0.0/24', 'n':'99.0.0.1'}, - {'p':'5.1.1.0/24', 'n':'99.0.0.1'}, - {'p':'99.0.0.1/32', 'n':'192.168.1.2'}, + {"p": "5.1.0.0/24", "n": "99.0.0.1"}, + {"p": "5.1.1.0/24", "n": "99.0.0.1"}, + {"p": "99.0.0.1/32", "n": "192.168.1.2"}, ] -bgpribRequireUnicastRoutes('r1','ipv4','r1-cust1','Customer 1 routes in r1 vrf',want_r1_cust1_routes) +bgpribRequireUnicastRoutes( + "r1", "ipv4", "r1-cust1", "Customer 1 routes in r1 vrf", want_r1_cust1_routes +) want_r3_cust1_routes = [ - {'p':'5.1.0.0/24', 'n':'99.0.0.2'}, - {'p':'5.1.1.0/24', 'n':'99.0.0.2'}, - {'p':'99.0.0.2/32', 'n':'192.168.1.2'}, + {"p": "5.1.0.0/24", "n": "99.0.0.2"}, + {"p": "5.1.1.0/24", "n": "99.0.0.2"}, + {"p": "99.0.0.2/32", "n": "192.168.1.2"}, ] -bgpribRequireUnicastRoutes('r3','ipv4','r3-cust1','Customer 1 routes in r3 vrf',want_r3_cust1_routes) +bgpribRequireUnicastRoutes( + "r3", "ipv4", "r3-cust1", "Customer 1 routes in r3 vrf", want_r3_cust1_routes +) want_r4_cust1_routes = [ - {'p':'5.1.2.0/24', 'n':'99.0.0.3'}, - {'p':'5.1.3.0/24', 'n':'99.0.0.3'}, - {'p':'99.0.0.3/32', 'n':'192.168.1.2'}, + {"p": "5.1.2.0/24", "n": "99.0.0.3"}, + {"p": "5.1.3.0/24", "n": "99.0.0.3"}, + {"p": "99.0.0.3/32", "n": "192.168.1.2"}, ] -bgpribRequireUnicastRoutes('r4','ipv4','r4-cust1','Customer 1 routes in r4 vrf',want_r4_cust1_routes) +bgpribRequireUnicastRoutes( + "r4", "ipv4", "r4-cust1", "Customer 1 routes in r4 vrf", want_r4_cust1_routes +) want_r4_cust2_routes = [ - {'p':'5.4.2.0/24', 'n':'99.0.0.4'}, - {'p':'5.4.3.0/24', 'n':'99.0.0.4'}, - {'p':'99.0.0.4/32', 'n':'192.168.2.2'}, + {"p": "5.4.2.0/24", "n": "99.0.0.4"}, + {"p": "5.4.3.0/24", "n": "99.0.0.4"}, + {"p": "99.0.0.4/32", "n": "192.168.2.2"}, ] -bgpribRequireUnicastRoutes('r4','ipv4','r4-cust2','Customer 2 routes in r4 vrf',want_r4_cust2_routes) +bgpribRequireUnicastRoutes( + "r4", "ipv4", "r4-cust2", "Customer 2 routes in r4 vrf", want_r4_cust2_routes +) ######################################################################## # PE routers: core unicast routes are empty ######################################################################## -luCommand('r1','vtysh -c "show bgp ipv4 uni"','No BGP prefixes displayed','pass','Core Unicast SAFI clean') -luCommand('r2','vtysh -c "show bgp ipv4 uni"','No BGP prefixes displayed','pass','Core Unicast SAFI clean') -luCommand('r3','vtysh -c "show bgp ipv4 uni"','No BGP prefixes displayed','pass','Core Unicast SAFI clean') -luCommand('r4','vtysh -c "show bgp ipv4 uni"','No BGP prefixes displayed','pass','Core Unicast SAFI clean') +luCommand( + "r1", + 'vtysh -c "show bgp ipv4 uni"', + "No BGP prefixes displayed", + "pass", + "Core Unicast SAFI clean", +) +luCommand( + "r2", + 'vtysh -c "show bgp ipv4 uni"', + "No BGP prefixes displayed", + "pass", + "Core Unicast SAFI clean", +) +luCommand( + "r3", + 'vtysh -c "show bgp ipv4 uni"', + "No BGP prefixes displayed", + "pass", + "Core Unicast SAFI clean", +) +luCommand( + "r4", + 'vtysh -c "show bgp ipv4 uni"', + "No BGP prefixes displayed", + "pass", + "Core Unicast SAFI clean", +) ######################################################################## # PE routers: local ce-originated routes are leaked to vpn ######################################################################## # nhzero is for the new code that sets nh of locally-leaked routes to 0 -#nhzero = 1 +# nhzero = 1 nhzero = 0 if nhzero: - luCommand('r1','vtysh -c "show bgp ipv4 vpn"', - 'Distinguisher: *10:1.*5.1.0.0/24 *0.0.0.0 .*5.1.1.0/24 *0.0.0.0 .*99.0.0.1/32 *0.0.0.0 ', - 'pass','vrf->vpn routes') - luCommand('r3','vtysh -c "show bgp ipv4 vpn"', - 'Distinguisher: *10:3.*5.1.0.0/24 *0.0.0.0 .*5.1.1.0/24 *0.0.0.0 .*99.0.0.2/32 *0.0.0.0 ', - 'pass','vrf->vpn routes') + luCommand( + "r1", + 'vtysh -c "show bgp ipv4 vpn"', + "Distinguisher: *10:1.*5.1.0.0/24 *0.0.0.0 .*5.1.1.0/24 *0.0.0.0 .*99.0.0.1/32 *0.0.0.0 ", + "pass", + "vrf->vpn routes", + ) + luCommand( + "r3", + 'vtysh -c "show bgp ipv4 vpn"', + "Distinguisher: *10:3.*5.1.0.0/24 *0.0.0.0 .*5.1.1.0/24 *0.0.0.0 .*99.0.0.2/32 *0.0.0.0 ", + "pass", + "vrf->vpn routes", + ) want = [ - {'rd':'10:41', 'p':'5.1.2.0/24', 'n':'0.0.0.0'}, - {'rd':'10:41', 'p':'5.1.3.0/24', 'n':'0.0.0.0'}, - {'rd':'10:41', 'p':'99.0.0.3/32', 'n':'0.0.0.0'}, - - {'rd':'10:42', 'p':'5.4.2.0/24', 'n':'0.0.0.0'}, - {'rd':'10:42', 'p':'5.4.3.0/24', 'n':'0.0.0.0'}, - {'rd':'10:42', 'p':'99.0.0.4/32', 'n':'0.0.0.0'}, + {"rd": "10:41", "p": "5.1.2.0/24", "n": "0.0.0.0"}, + {"rd": "10:41", "p": "5.1.3.0/24", "n": "0.0.0.0"}, + {"rd": "10:41", "p": "99.0.0.3/32", "n": "0.0.0.0"}, + {"rd": "10:42", "p": "5.4.2.0/24", "n": "0.0.0.0"}, + {"rd": "10:42", "p": "5.4.3.0/24", "n": "0.0.0.0"}, + {"rd": "10:42", "p": "99.0.0.4/32", "n": "0.0.0.0"}, ] - bgpribRequireVpnRoutes('r4','vrf->vpn routes',want) + bgpribRequireVpnRoutes("r4", "vrf->vpn routes", want) else: - luCommand('r1','vtysh -c "show bgp ipv4 vpn"', - r'Distinguisher: *10:1.*5.1.0.0/24 *99.0.0.1\b.*5.1.1.0/24 *99.0.0.1\b.*99.0.0.1/32 *192.168.1.2\b', - 'pass','vrf->vpn routes') - luCommand('r3','vtysh -c "show bgp ipv4 vpn"', - r'Distinguisher: *10:3.*5.1.0.0/24 *99.0.0.2\b.*5.1.1.0/24 *99.0.0.2\b.*99.0.0.2/32 *192.168.1.2\b', - 'pass','vrf->vpn routes') + luCommand( + "r1", + 'vtysh -c "show bgp ipv4 vpn"', + r"Distinguisher: *10:1.*5.1.0.0/24 *99.0.0.1\b.*5.1.1.0/24 *99.0.0.1\b.*99.0.0.1/32 *192.168.1.2\b", + "pass", + "vrf->vpn routes", + ) + luCommand( + "r3", + 'vtysh -c "show bgp ipv4 vpn"', + r"Distinguisher: *10:3.*5.1.0.0/24 *99.0.0.2\b.*5.1.1.0/24 *99.0.0.2\b.*99.0.0.2/32 *192.168.1.2\b", + "pass", + "vrf->vpn routes", + ) want = [ - {'rd':'10:41', 'p':'5.1.2.0/24', 'n':'99.0.0.3'}, - {'rd':'10:41', 'p':'5.1.3.0/24', 'n':'99.0.0.3'}, - {'rd':'10:41', 'p':'99.0.0.3/32', 'n':'192.168.1.2'}, - - {'rd':'10:42', 'p':'5.4.2.0/24', 'n':'99.0.0.4'}, - {'rd':'10:42', 'p':'5.4.3.0/24', 'n':'99.0.0.4'}, - {'rd':'10:42', 'p':'99.0.0.4/32', 'n':'192.168.2.2'}, + {"rd": "10:41", "p": "5.1.2.0/24", "n": "99.0.0.3"}, + {"rd": "10:41", "p": "5.1.3.0/24", "n": "99.0.0.3"}, + {"rd": "10:41", "p": "99.0.0.3/32", "n": "192.168.1.2"}, + {"rd": "10:42", "p": "5.4.2.0/24", "n": "99.0.0.4"}, + {"rd": "10:42", "p": "5.4.3.0/24", "n": "99.0.0.4"}, + {"rd": "10:42", "p": "99.0.0.4/32", "n": "192.168.2.2"}, ] - bgpribRequireVpnRoutes('r4','vrf->vpn routes',want) + bgpribRequireVpnRoutes("r4", "vrf->vpn routes", want) ######################################################################## # PE routers: exporting vrfs set MPLS vrf labels in kernel ######################################################################## -luCommand('r1','vtysh -c "show mpls table"',' 101 *BGP *r1-cust1','pass','vrf labels') -luCommand('r3','vtysh -c "show mpls table"',' 103 *BGP *r3-cust1','pass','vrf labels') -luCommand('r4','vtysh -c "show mpls table"',' 1041 *BGP *r4-cust1 .*1042 *BGP *r4-cust2','pass','vrf labels') +luCommand( + "r1", 'vtysh -c "show mpls table"', " 101 *BGP *r1-cust1", "pass", "vrf labels" +) +luCommand( + "r3", 'vtysh -c "show mpls table"', " 103 *BGP *r3-cust1", "pass", "vrf labels" +) +luCommand( + "r4", + 'vtysh -c "show mpls table"', + " 1041 *BGP *r4-cust1 .*1042 *BGP *r4-cust2", + "pass", + "vrf labels", +) ######################################################################## # Core VPN router: all customer routes ######################################################################## want_rd_routes = [ - {'rd':'10:1', 'p':'5.1.0.0/24', 'n':'1.1.1.1'}, - {'rd':'10:1', 'p':'5.1.0.0/24', 'n':'1.1.1.1'}, - {'rd':'10:1', 'p':'99.0.0.1/32', 'n':'1.1.1.1'}, - - {'rd':'10:3', 'p':'5.1.0.0/24', 'n':'3.3.3.3'}, - {'rd':'10:3', 'p':'5.1.0.0/24', 'n':'3.3.3.3'}, - {'rd':'10:3', 'p':'99.0.0.2/32', 'n':'3.3.3.3'}, - - {'rd':'10:41', 'p':'5.1.2.0/24', 'n':'4.4.4.4'}, - {'rd':'10:41', 'p':'5.1.3.0/24', 'n':'4.4.4.4'}, - {'rd':'10:41', 'p':'99.0.0.3/32', 'n':'4.4.4.4'}, - - {'rd':'10:42', 'p':'5.4.2.0/24', 'n':'4.4.4.4'}, - {'rd':'10:42', 'p':'5.4.3.0/24', 'n':'4.4.4.4'}, - {'rd':'10:42', 'p':'99.0.0.4/32', 'n':'4.4.4.4'}, + {"rd": "10:1", "p": "5.1.0.0/24", "n": "1.1.1.1"}, + {"rd": "10:1", "p": "5.1.0.0/24", "n": "1.1.1.1"}, + {"rd": "10:1", "p": "99.0.0.1/32", "n": "1.1.1.1"}, + {"rd": "10:3", "p": "5.1.0.0/24", "n": "3.3.3.3"}, + {"rd": "10:3", "p": "5.1.0.0/24", "n": "3.3.3.3"}, + {"rd": "10:3", "p": "99.0.0.2/32", "n": "3.3.3.3"}, + {"rd": "10:41", "p": "5.1.2.0/24", "n": "4.4.4.4"}, + {"rd": "10:41", "p": "5.1.3.0/24", "n": "4.4.4.4"}, + {"rd": "10:41", "p": "99.0.0.3/32", "n": "4.4.4.4"}, + {"rd": "10:42", "p": "5.4.2.0/24", "n": "4.4.4.4"}, + {"rd": "10:42", "p": "5.4.3.0/24", "n": "4.4.4.4"}, + {"rd": "10:42", "p": "99.0.0.4/32", "n": "4.4.4.4"}, ] -bgpribRequireVpnRoutes('r2','Customer routes in provider vpn core',want_rd_routes) +bgpribRequireVpnRoutes("r2", "Customer routes in provider vpn core", want_rd_routes) ######################################################################## # PE routers: VPN routes from remote customers @@ -165,46 +218,46 @@ # r1 vtysh -c "show bgp ipv4 vpn" # want_r1_remote_vpn_routes = [ - {'rd':'10:3', 'p':'5.1.0.0/24', 'n':'3.3.3.3'}, - {'rd':'10:3', 'p':'5.1.1.0/24', 'n':'3.3.3.3'}, - {'rd':'10:3', 'p':'99.0.0.2/32', 'n':'3.3.3.3'}, - - {'rd':'10:41', 'p':'5.1.2.0/24', 'n':'4.4.4.4'}, - {'rd':'10:41', 'p':'5.1.3.0/24', 'n':'4.4.4.4'}, - {'rd':'10:41', 'p':'99.0.0.3/32', 'n':'4.4.4.4'}, - - {'rd':'10:42', 'p':'5.4.2.0/24', 'n':'4.4.4.4'}, - {'rd':'10:42', 'p':'5.4.3.0/24', 'n':'4.4.4.4'}, - {'rd':'10:42', 'p':'99.0.0.4/32', 'n':'4.4.4.4'}, + {"rd": "10:3", "p": "5.1.0.0/24", "n": "3.3.3.3"}, + {"rd": "10:3", "p": "5.1.1.0/24", "n": "3.3.3.3"}, + {"rd": "10:3", "p": "99.0.0.2/32", "n": "3.3.3.3"}, + {"rd": "10:41", "p": "5.1.2.0/24", "n": "4.4.4.4"}, + {"rd": "10:41", "p": "5.1.3.0/24", "n": "4.4.4.4"}, + {"rd": "10:41", "p": "99.0.0.3/32", "n": "4.4.4.4"}, + {"rd": "10:42", "p": "5.4.2.0/24", "n": "4.4.4.4"}, + {"rd": "10:42", "p": "5.4.3.0/24", "n": "4.4.4.4"}, + {"rd": "10:42", "p": "99.0.0.4/32", "n": "4.4.4.4"}, ] -bgpribRequireVpnRoutes('r1','Remote Customer routes in R1 vpn',want_r1_remote_vpn_routes) +bgpribRequireVpnRoutes( + "r1", "Remote Customer routes in R1 vpn", want_r1_remote_vpn_routes +) want_r3_remote_vpn_routes = [ - {'rd':'10:1', 'p':'5.1.0.0/24', 'n':'1.1.1.1'}, - {'rd':'10:1', 'p':'5.1.1.0/24', 'n':'1.1.1.1'}, - {'rd':'10:1', 'p':'99.0.0.1/32', 'n':'1.1.1.1'}, - - {'rd':'10:41', 'p':'5.1.2.0/24', 'n':'4.4.4.4'}, - {'rd':'10:41', 'p':'5.1.3.0/24', 'n':'4.4.4.4'}, - {'rd':'10:41', 'p':'99.0.0.3/32', 'n':'4.4.4.4'}, - - {'rd':'10:42', 'p':'5.4.2.0/24', 'n':'4.4.4.4'}, - {'rd':'10:42', 'p':'5.4.3.0/24', 'n':'4.4.4.4'}, - {'rd':'10:42', 'p':'99.0.0.4/32', 'n':'4.4.4.4'}, + {"rd": "10:1", "p": "5.1.0.0/24", "n": "1.1.1.1"}, + {"rd": "10:1", "p": "5.1.1.0/24", "n": "1.1.1.1"}, + {"rd": "10:1", "p": "99.0.0.1/32", "n": "1.1.1.1"}, + {"rd": "10:41", "p": "5.1.2.0/24", "n": "4.4.4.4"}, + {"rd": "10:41", "p": "5.1.3.0/24", "n": "4.4.4.4"}, + {"rd": "10:41", "p": "99.0.0.3/32", "n": "4.4.4.4"}, + {"rd": "10:42", "p": "5.4.2.0/24", "n": "4.4.4.4"}, + {"rd": "10:42", "p": "5.4.3.0/24", "n": "4.4.4.4"}, + {"rd": "10:42", "p": "99.0.0.4/32", "n": "4.4.4.4"}, ] -bgpribRequireVpnRoutes('r3','Remote Customer routes in R3 vpn',want_r3_remote_vpn_routes) +bgpribRequireVpnRoutes( + "r3", "Remote Customer routes in R3 vpn", want_r3_remote_vpn_routes +) want_r4_remote_vpn_routes = [ - {'rd':'10:1', 'p':'5.1.0.0/24', 'n':'1.1.1.1'}, - {'rd':'10:1', 'p':'5.1.1.0/24', 'n':'1.1.1.1'}, - {'rd':'10:1', 'p':'99.0.0.1/32', 'n':'1.1.1.1'}, - - {'rd':'10:3', 'p':'5.1.0.0/24', 'n':'3.3.3.3'}, - {'rd':'10:3', 'p':'5.1.1.0/24', 'n':'3.3.3.3'}, - {'rd':'10:3', 'p':'99.0.0.2/32', 'n':'3.3.3.3'}, + {"rd": "10:1", "p": "5.1.0.0/24", "n": "1.1.1.1"}, + {"rd": "10:1", "p": "5.1.1.0/24", "n": "1.1.1.1"}, + {"rd": "10:1", "p": "99.0.0.1/32", "n": "1.1.1.1"}, + {"rd": "10:3", "p": "5.1.0.0/24", "n": "3.3.3.3"}, + {"rd": "10:3", "p": "5.1.1.0/24", "n": "3.3.3.3"}, + {"rd": "10:3", "p": "99.0.0.2/32", "n": "3.3.3.3"}, ] -bgpribRequireVpnRoutes('r4','Remote Customer routes in R4 vpn',want_r4_remote_vpn_routes) - +bgpribRequireVpnRoutes( + "r4", "Remote Customer routes in R4 vpn", want_r4_remote_vpn_routes +) # r1 vtysh -c "show bgp vrf r1-cust1 ipv4" @@ -213,54 +266,58 @@ # PE routers: VRFs contain routes from remote customer nets ######################################################################## want_r1_remote_cust1_routes = [ - {'p':'5.1.0.0/24', 'n':'3.3.3.3'}, - {'p':'5.1.1.0/24', 'n':'3.3.3.3'}, - {'p':'99.0.0.2/32', 'n':'3.3.3.3'}, - - {'p':'5.1.2.0/24', 'n':'4.4.4.4'}, - {'p':'5.1.3.0/24', 'n':'4.4.4.4'}, - {'p':'99.0.0.3/32', 'n':'4.4.4.4'}, - - {'p':'5.4.2.0/24', 'n':'4.4.4.4'}, - {'p':'5.4.3.0/24', 'n':'4.4.4.4'}, - {'p':'99.0.0.3/32', 'n':'4.4.4.4'}, + {"p": "5.1.0.0/24", "n": "3.3.3.3"}, + {"p": "5.1.1.0/24", "n": "3.3.3.3"}, + {"p": "99.0.0.2/32", "n": "3.3.3.3"}, + {"p": "5.1.2.0/24", "n": "4.4.4.4"}, + {"p": "5.1.3.0/24", "n": "4.4.4.4"}, + {"p": "99.0.0.3/32", "n": "4.4.4.4"}, + {"p": "5.4.2.0/24", "n": "4.4.4.4"}, + {"p": "5.4.3.0/24", "n": "4.4.4.4"}, + {"p": "99.0.0.3/32", "n": "4.4.4.4"}, ] -bgpribRequireUnicastRoutes('r1','ipv4','r1-cust1','Customer 1 routes in r1 vrf',want_r1_remote_cust1_routes) +bgpribRequireUnicastRoutes( + "r1", "ipv4", "r1-cust1", "Customer 1 routes in r1 vrf", want_r1_remote_cust1_routes +) want_r3_remote_cust1_routes = [ - {'p':'5.1.0.0/24', 'n':'1.1.1.1'}, - {'p':'5.1.1.0/24', 'n':'1.1.1.1'}, - {'p':'99.0.0.1/32', 'n':'1.1.1.1'}, - - {'p':'5.1.2.0/24', 'n':'4.4.4.4'}, - {'p':'5.1.3.0/24', 'n':'4.4.4.4'}, - {'p':'99.0.0.3/32', 'n':'4.4.4.4'}, - - {'p':'5.4.2.0/24', 'n':'4.4.4.4'}, - {'p':'5.4.3.0/24', 'n':'4.4.4.4'}, - {'p':'99.0.0.3/32', 'n':'4.4.4.4'}, + {"p": "5.1.0.0/24", "n": "1.1.1.1"}, + {"p": "5.1.1.0/24", "n": "1.1.1.1"}, + {"p": "99.0.0.1/32", "n": "1.1.1.1"}, + {"p": "5.1.2.0/24", "n": "4.4.4.4"}, + {"p": "5.1.3.0/24", "n": "4.4.4.4"}, + {"p": "99.0.0.3/32", "n": "4.4.4.4"}, + {"p": "5.4.2.0/24", "n": "4.4.4.4"}, + {"p": "5.4.3.0/24", "n": "4.4.4.4"}, + {"p": "99.0.0.3/32", "n": "4.4.4.4"}, ] -bgpribRequireUnicastRoutes('r3','ipv4','r3-cust1','Customer 1 routes in r3 vrf',want_r3_remote_cust1_routes) +bgpribRequireUnicastRoutes( + "r3", "ipv4", "r3-cust1", "Customer 1 routes in r3 vrf", want_r3_remote_cust1_routes +) want_r4_remote_cust1_routes = [ - {'p':'5.1.0.0/24', 'n':'1.1.1.1'}, - {'p':'5.1.1.0/24', 'n':'1.1.1.1'}, - {'p':'5.1.0.0/24', 'n':'3.3.3.3'}, - {'p':'5.1.1.0/24', 'n':'3.3.3.3'}, - {'p':'99.0.0.1/32', 'n':'1.1.1.1'}, - {'p':'99.0.0.2/32', 'n':'3.3.3.3'}, + {"p": "5.1.0.0/24", "n": "1.1.1.1"}, + {"p": "5.1.1.0/24", "n": "1.1.1.1"}, + {"p": "5.1.0.0/24", "n": "3.3.3.3"}, + {"p": "5.1.1.0/24", "n": "3.3.3.3"}, + {"p": "99.0.0.1/32", "n": "1.1.1.1"}, + {"p": "99.0.0.2/32", "n": "3.3.3.3"}, ] -bgpribRequireUnicastRoutes('r4','ipv4','r4-cust1','Customer 1 routes in r4 vrf',want_r4_remote_cust1_routes) +bgpribRequireUnicastRoutes( + "r4", "ipv4", "r4-cust1", "Customer 1 routes in r4 vrf", want_r4_remote_cust1_routes +) want_r4_remote_cust2_routes = [ - {'p':'5.1.0.0/24', 'n':'1.1.1.1'}, - {'p':'5.1.1.0/24', 'n':'1.1.1.1'}, - {'p':'5.1.0.0/24', 'n':'3.3.3.3'}, - {'p':'5.1.1.0/24', 'n':'3.3.3.3'}, - {'p':'99.0.0.1/32', 'n':'1.1.1.1'}, - {'p':'99.0.0.2/32', 'n':'3.3.3.3'}, + {"p": "5.1.0.0/24", "n": "1.1.1.1"}, + {"p": "5.1.1.0/24", "n": "1.1.1.1"}, + {"p": "5.1.0.0/24", "n": "3.3.3.3"}, + {"p": "5.1.1.0/24", "n": "3.3.3.3"}, + {"p": "99.0.0.1/32", "n": "1.1.1.1"}, + {"p": "99.0.0.2/32", "n": "3.3.3.3"}, ] -bgpribRequireUnicastRoutes('r4','ipv4','r4-cust2','Customer 2 routes in r4 vrf',want_r4_remote_cust2_routes) +bgpribRequireUnicastRoutes( + "r4", "ipv4", "r4-cust2", "Customer 2 routes in r4 vrf", want_r4_remote_cust2_routes +) ######################################################################### @@ -270,49 +327,78 @@ # r1 vtysh -c "show bgp vrf r1-cust1 ipv4" # r1 vtysh -c "show bgp vrf r1-cust1 ipv4 5.1.2.0/24" -luCommand('ce1','vtysh -c "show bgp ipv4 uni"','10 routes and 10','wait','Local and remote routes', 10) +luCommand( + "ce1", + 'vtysh -c "show bgp ipv4 uni"', + "10 routes and 10", + "wait", + "Local and remote routes", + 10, +) want = [ - {'p':'5.1.2.0/24', 'n':'192.168.1.1'}, - {'p':'5.1.3.0/24', 'n':'192.168.1.1'}, - {'p':'5.4.2.0/24', 'n':'192.168.1.1'}, - {'p':'5.4.3.0/24', 'n':'192.168.1.1'}, + {"p": "5.1.2.0/24", "n": "192.168.1.1"}, + {"p": "5.1.3.0/24", "n": "192.168.1.1"}, + {"p": "5.4.2.0/24", "n": "192.168.1.1"}, + {"p": "5.4.3.0/24", "n": "192.168.1.1"}, ] -bgpribRequireUnicastRoutes('ce1','ipv4','','Cust 1 routes from remote',want) - -luCommand('ce2','vtysh -c "show bgp ipv4 uni"','10 routes and 12','wait','Local and remote routes', 10) +bgpribRequireUnicastRoutes("ce1", "ipv4", "", "Cust 1 routes from remote", want) + +luCommand( + "ce2", + 'vtysh -c "show bgp ipv4 uni"', + "10 routes and 12", + "wait", + "Local and remote routes", + 10, +) want = [ - {'p':'5.1.0.0/24', 'n':'192.168.1.1'}, - {'p':'5.1.1.0/24', 'n':'192.168.1.1'}, - {'p':'5.1.2.0/24', 'n':'192.168.1.1'}, - {'p':'5.1.3.0/24', 'n':'192.168.1.1'}, - {'p':'5.4.2.0/24', 'n':'192.168.1.1'}, - {'p':'5.4.3.0/24', 'n':'192.168.1.1'}, + {"p": "5.1.0.0/24", "n": "192.168.1.1"}, + {"p": "5.1.1.0/24", "n": "192.168.1.1"}, + {"p": "5.1.2.0/24", "n": "192.168.1.1"}, + {"p": "5.1.3.0/24", "n": "192.168.1.1"}, + {"p": "5.4.2.0/24", "n": "192.168.1.1"}, + {"p": "5.4.3.0/24", "n": "192.168.1.1"}, ] -bgpribRequireUnicastRoutes('ce2','ipv4','','Cust 1 routes from remote',want) +bgpribRequireUnicastRoutes("ce2", "ipv4", "", "Cust 1 routes from remote", want) # human readable output for debugging -luCommand('r4','vtysh -c "show bgp vrf r4-cust1 ipv4 uni"') -luCommand('r4','vtysh -c "show bgp vrf r4-cust2 ipv4 uni"') -luCommand('r4','vtysh -c "show bgp ipv4 vpn"') -luCommand('r4','vtysh -c "show ip route vrf r4-cust1"') -luCommand('r4','vtysh -c "show ip route vrf r4-cust2"') - -luCommand('ce3','vtysh -c "show bgp ipv4 uni"','10 routes and 10','wait','Local and remote routes', 10) +luCommand("r4", 'vtysh -c "show bgp vrf r4-cust1 ipv4 uni"') +luCommand("r4", 'vtysh -c "show bgp vrf r4-cust2 ipv4 uni"') +luCommand("r4", 'vtysh -c "show bgp ipv4 vpn"') +luCommand("r4", 'vtysh -c "show ip route vrf r4-cust1"') +luCommand("r4", 'vtysh -c "show ip route vrf r4-cust2"') + +luCommand( + "ce3", + 'vtysh -c "show bgp ipv4 uni"', + "10 routes and 10", + "wait", + "Local and remote routes", + 10, +) # Requires bvl-bug-degenerate-no-label fix (FRR PR #2053) want = [ - {'p':'5.1.0.0/24', 'n':'192.168.1.1'}, - {'p':'5.1.1.0/24', 'n':'192.168.1.1'}, - {'p':'5.4.2.0/24', 'n':'192.168.1.1'}, - {'p':'5.4.3.0/24', 'n':'192.168.1.1'}, + {"p": "5.1.0.0/24", "n": "192.168.1.1"}, + {"p": "5.1.1.0/24", "n": "192.168.1.1"}, + {"p": "5.4.2.0/24", "n": "192.168.1.1"}, + {"p": "5.4.3.0/24", "n": "192.168.1.1"}, ] -bgpribRequireUnicastRoutes('ce3','ipv4','','Cust 1 routes from remote',want) - -luCommand('ce4','vtysh -c "show bgp vrf ce4-cust2 ipv4 uni"','10 routes and 10','wait','Local and remote routes', 10) +bgpribRequireUnicastRoutes("ce3", "ipv4", "", "Cust 1 routes from remote", want) + +luCommand( + "ce4", + 'vtysh -c "show bgp vrf ce4-cust2 ipv4 uni"', + "10 routes and 10", + "wait", + "Local and remote routes", + 10, +) want = [ - {'p':'5.1.0.0/24', 'n':'192.168.2.1'}, - {'p':'5.1.1.0/24', 'n':'192.168.2.1'}, - {'p':'5.1.2.0/24', 'n':'192.168.2.1'}, - {'p':'5.1.3.0/24', 'n':'192.168.2.1'}, + {"p": "5.1.0.0/24", "n": "192.168.2.1"}, + {"p": "5.1.1.0/24", "n": "192.168.2.1"}, + {"p": "5.1.2.0/24", "n": "192.168.2.1"}, + {"p": "5.1.3.0/24", "n": "192.168.2.1"}, ] -bgpribRequireUnicastRoutes('ce4','ipv4','ce4-cust2','Cust 2 routes from remote',want) - +bgpribRequireUnicastRoutes( + "ce4", "ipv4", "ce4-cust2", "Cust 2 routes from remote", want +) diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/cleanup_all.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/cleanup_all.py index a721cf21bd..af77ab01c1 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/cleanup_all.py +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/cleanup_all.py @@ -1,17 +1,120 @@ from lutil import luCommand -luCommand('r1','vtysh -c "clear vrf r1-cust1 prefix 99.0.0.1/32"','.','none','Cleared VRF route') -luCommand('r3','vtysh -c "clear vrf r3-cust1 prefix 99.0.0.2/32"','.','none','Cleared VRF route') -luCommand('r4','vtysh -c "clear vrf r3-cust1 prefix 99.0.0.3/32"','.','none','Cleared VRF route') -luCommand('r1','vtysh -c "show vnc registrations local"','99.0.0.1','fail','Local Registration cleared') -luCommand('r3','vtysh -c "show vnc registrations local"','99.0.0.2','fail','Local Registration cleared') -luCommand('r4','vtysh -c "show vnc registrations local"','99.0.0.3','fail','Local Registration cleared') -luCommand('r1','vtysh -c "show bgp ipv4 uni"','2 routes and 2','wait','Unicast SAFI updated', 10) -luCommand('r2','vtysh -c "show bgp ipv4 uni"','No BGP prefixes displayed','pass','Unicast SAFI') -luCommand('r3','vtysh -c "show bgp ipv4 uni"','2 routes and 2','wait','Unicast SAFI updated', 10) -luCommand('r4','vtysh -c "show bgp ipv4 uni"','2 routes and 2','wait','Unicast SAFI updated', 10) -luCommand('ce1','vtysh -c "show bgp ipv4 uni"','2 routes and 2','wait','Local and remote routes', 10) -luCommand('ce2','vtysh -c "show bgp ipv4 uni"','2 routes and 2','wait','Local and remote routes', 10) -luCommand('ce3','vtysh -c "show bgp ipv4 uni"','2 routes and 2','wait','Local and remote routes', 10) -luCommand('r1','vtysh -c "show vnc registrations remote"','Prefix ','fail','Remote Registration cleared') -luCommand('r3','vtysh -c "show vnc registrations remote"','Prefix ','fail','Remote Registration cleared') -luCommand('r4','vtysh -c "show vnc registrations remote"','Prefix ','fail','Remote Registration cleared') + +luCommand( + "r1", + 'vtysh -c "clear vrf r1-cust1 prefix 99.0.0.1/32"', + ".", + "none", + "Cleared VRF route", +) +luCommand( + "r3", + 'vtysh -c "clear vrf r3-cust1 prefix 99.0.0.2/32"', + ".", + "none", + "Cleared VRF route", +) +luCommand( + "r4", + 'vtysh -c "clear vrf r3-cust1 prefix 99.0.0.3/32"', + ".", + "none", + "Cleared VRF route", +) +luCommand( + "r1", + 'vtysh -c "show vnc registrations local"', + "99.0.0.1", + "fail", + "Local Registration cleared", +) +luCommand( + "r3", + 'vtysh -c "show vnc registrations local"', + "99.0.0.2", + "fail", + "Local Registration cleared", +) +luCommand( + "r4", + 'vtysh -c "show vnc registrations local"', + "99.0.0.3", + "fail", + "Local Registration cleared", +) +luCommand( + "r1", + 'vtysh -c "show bgp ipv4 uni"', + "2 routes and 2", + "wait", + "Unicast SAFI updated", + 10, +) +luCommand( + "r2", + 'vtysh -c "show bgp ipv4 uni"', + "No BGP prefixes displayed", + "pass", + "Unicast SAFI", +) +luCommand( + "r3", + 'vtysh -c "show bgp ipv4 uni"', + "2 routes and 2", + "wait", + "Unicast SAFI updated", + 10, +) +luCommand( + "r4", + 'vtysh -c "show bgp ipv4 uni"', + "2 routes and 2", + "wait", + "Unicast SAFI updated", + 10, +) +luCommand( + "ce1", + 'vtysh -c "show bgp ipv4 uni"', + "2 routes and 2", + "wait", + "Local and remote routes", + 10, +) +luCommand( + "ce2", + 'vtysh -c "show bgp ipv4 uni"', + "2 routes and 2", + "wait", + "Local and remote routes", + 10, +) +luCommand( + "ce3", + 'vtysh -c "show bgp ipv4 uni"', + "2 routes and 2", + "wait", + "Local and remote routes", + 10, +) +luCommand( + "r1", + 'vtysh -c "show vnc registrations remote"', + "Prefix ", + "fail", + "Remote Registration cleared", +) +luCommand( + "r3", + 'vtysh -c "show vnc registrations remote"', + "Prefix ", + "fail", + "Remote Registration cleared", +) +luCommand( + "r4", + 'vtysh -c "show vnc registrations remote"', + "Prefix ", + "fail", + "Remote Registration cleared", +) diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/del_bgp_instances.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/del_bgp_instances.py new file mode 100644 index 0000000000..477578bdbd --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/del_bgp_instances.py @@ -0,0 +1,30 @@ +from lutil import luCommand + +luCommand( + "r1", + '/usr/lib/frr/vtysh -c "conf ter" -c "no router bgp 5227 vrf r1-cust1" -c "no router bgp 5226"', + ".", + "none", + "Cleared bgp instances", +) +luCommand( + "r2", + '/usr/lib/frr/vtysh -c "conf ter" -c "no router bgp 5226"', + ".", + "none", + "Cleared bgp instances", +) +luCommand( + "r3", + '/usr/lib/frr/vtysh -c "conf ter" -c "no router bgp 5227 vrf r3-cust1" -c "no router bgp 5226"', + ".", + "none", + "Cleared bgp instances", +) +luCommand( + "r4", + '/usr/lib/frr/vtysh -c "conf ter" -c "no router bgp 5228 vrf r4-cust2" -c "no router bgp 5227 vrf r4-cust1" -c "no router bgp 5226"', + ".", + "none", + "Cleared bgp instances", +) diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/notification_check.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/notification_check.py new file mode 100644 index 0000000000..2b0a85a91a --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/notification_check.py @@ -0,0 +1,22 @@ +from lutil import luCommand + +rtrs = ["ce1", "ce2", "ce3", "r1", "r2", "r3", "r4"] +for rtr in rtrs: + ret = luCommand( + rtr, + 'vtysh -c "show bgp neigh"', + "Notification received .([A-Za-z0-9/ ]*)", + "none", + "collect neighbor stats", + ) + found = luLast() + if ret != False and found != None: + val = found.group(1) + ret = luCommand( + rtr, + 'vtysh -c "show bgp neigh"', + "Notification received", + "fail", + "Notify RXed! {}".format(val), + ) +# done diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_down.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_down.py new file mode 100644 index 0000000000..b4fa240495 --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_down.py @@ -0,0 +1,87 @@ +from lutil import luCommand + +ret = luCommand( + "ce1", + 'vtysh -c "show ip route" | grep -c \\ 10\\.\\*/32', + "(.*)", + "pass", + "Looking for sharp routes", +) +found = luLast() +if ret != False and found != None: + num = int(found.group()) + luCommand( + "ce3", 'vtysh -c "show bgp sum"', ".", "pass", "See %s sharp routes" % num + ) + if num > 0: + rtrs = ["ce1", "ce2", "ce3"] + for rtr in rtrs: + luCommand( + rtr, + 'vtysh -c "show bgp ipv4 uni" | grep Display', + ".", + "none", + "BGP routes pre remove", + ) + luCommand( + rtr, + "ip route show | cat -n | tail", + ".", + "none", + "Linux routes pre remove", + ) + wait = 2 * num / 500 + luCommand( + "ce1", + 'vtysh -c "sharp remove routes 10.0.0.0 {}"'.format(num), + ".", + "none", + "Removing {} routes".format(num), + ) + luCommand( + "ce2", + 'vtysh -c "sharp remove routes 10.0.0.0 {}"'.format(num), + ".", + "none", + "Removing {} routes".format(num), + ) + for rtr in rtrs: + luCommand( + rtr, + 'vtysh -c "show bgp ipv4 uni" | grep Display', + " 10 route", + "wait", + "BGP routes removed", + wait, + wait_time=10, + ) + luCommand( + rtr, + 'vtysh -c "show bgp ipv4 uni"', + ".", + "none", + "BGP routes post remove", + ) + for rtr in rtrs: + luCommand( + rtr, + "ip route show | grep -c \\^10\\.", + "^0$", + "wait", + "Linux routes removed", + wait, + wait_time=10, + ) + luCommand(rtr, "ip route show", ".", "none", "Linux routes post remove") + rtrs = ["r1", "r3", "r4"] + for rtr in rtrs: + luCommand( + rtr, + "ip route show vrf {}-cust1 | grep -c \\^10\\.".format(rtr), + "^0$", + "wait", + "VRF route removed", + wait, + wait_time=10, + ) +# done diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_up.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_up.py new file mode 100644 index 0000000000..3c768640a1 --- /dev/null +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_up.py @@ -0,0 +1,234 @@ +from lutil import luCommand + +num = 50000 +b = int(num / (256 * 256)) +if b > 0: + r = num - b * (256 * 256) +else: + r = num +c = int(r / 256) +if c > 0: + d = r - c * 256 - 1 +else: + d = r +wait = 2 * num / 1000 +mem_z = {} +mem_b = {} +rtrs = ["ce1", "ce2", "ce3", "r1", "r2", "r3", "r4"] +for rtr in rtrs: + mem_z[rtr] = {"value": 0, "units": "unknown"} + mem_b[rtr] = {"value": 0, "units": "unknown"} + ret = luCommand( + rtr, + 'vtysh -c "show memory"', + "zebra: System allocator statistics: Total heap allocated: *(\d*) ([A-Za-z]*) .*bgpd: System allocator statistics: Total heap allocated: *(\d*) ([A-Za-z]*)", + "none", + "collect bgpd memory stats", + ) + found = luLast() + if ret != False and found != None: + mem_z[rtr] = {"value": int(found.group(1)), "units": found.group(2)} + mem_b[rtr] = {"value": int(found.group(3)), "units": found.group(4)} + +luCommand( + "ce1", 'vtysh -c "show mem"', "qmem sharpd", "none", "check if sharpd running" +) +doSharp = False +found = luLast() +if ret != False and found != None: + if len(found.group()): + doSharp = True + +if doSharp != True: + luCommand( + "ce1", + 'vtysh -c "sharp data nexthop"', + ".", + "pass", + "sharpd NOT running, skipping test", + ) +else: + luCommand( + "ce1", + 'vtysh -c "sharp install routes 10.0.0.0 nexthop 99.0.0.1 {}"'.format(num), + "", + "pass", + "Adding {} routes".format(num), + ) + luCommand( + "ce2", + 'vtysh -c "sharp install routes 10.0.0.0 nexthop 99.0.0.2 {}"'.format(num), + "", + "pass", + "Adding {} routes".format(num), + ) + rtrs = ["ce1", "ce2", "ce3"] + for rtr in rtrs: + luCommand( + rtr, + 'vtysh -c "show bgp ipv4 uni 10.{}.{}.{}"'.format(b, c, d), + "Last update:", + "wait", + "RXed last route, 10.{}.{}.{}".format(b, c, d), + wait, + wait_time=10, + ) + luCommand( + rtr, + 'vtysh -c "show bgp ipv4 uni" | grep -c 10\\.\\*/32', + str(num), + "wait", + "See all sharp routes in BGP", + wait, + wait_time=10, + ) + luCommand( + "r1", + 'vtysh -c "show bgp vrf r1-cust1 ipv4 uni 10.{}.{}.{}"'.format(b, c, d), + "99.0.0.1", + "wait", + "RXed -> 10.{}.{}.{} from CE1".format(b, c, d), + wait, + wait_time=10, + ) + luCommand( + "r3", + 'vtysh -c "show bgp vrf r3-cust1 ipv4 uni 10.{}.{}.{}"'.format(b, c, d), + "99.0.0.2", + "wait", + "RXed -> 10.{}.{}.{} from CE2".format(b, c, d), + wait, + wait_time=10, + ) + luCommand( + "r1", + 'vtysh -c "show bgp ipv4 vpn 10.{}.{}.{}"'.format(b, c, d), + "99.0.0.1", + "wait", + "see VPN safi -> 10.{}.{}.{} from CE1".format(b, c, d), + ) + luCommand( + "r3", + 'vtysh -c "show bgp ipv4 vpn 10.{}.{}.{}"'.format(b, c, d), + "99.0.0.2", + "wait", + "see VPN safi -> 10.{}.{}.{} from CE2".format(b, c, d), + ) + luCommand( + "r3", + 'vtysh -c "show bgp ipv4 vpn 10.{}.{}.{}"'.format(b, c, d), + "1.1.1.1", + "wait", + "see VPN safi -> 10.{}.{}.{} from CE1".format(b, c, d), + ) + luCommand( + "r1", + 'vtysh -c "show bgp ipv4 vpn 10.{}.{}.{}"'.format(b, c, d), + "3.3.3.3", + "wait", + "see VPN safi -> 10.{}.{}.{} from CE2".format(b, c, d), + ) + luCommand( + "r4", + 'vtysh -c "show bgp ipv4 vpn 10.{}.{}.{}"'.format(b, c, d), + "1.1.1.1", + "wait", + "see VPN safi -> 10.{}.{}.{} from CE1".format(b, c, d), + ) + luCommand( + "r4", + 'vtysh -c "show bgp ipv4 vpn 10.{}.{}.{}"'.format(b, c, d), + "3.3.3.3", + "wait", + "see VPN safi -> 10.{}.{}.{} from CE2".format(b, c, d), + ) + rtrs = ["ce1", "ce2", "ce3"] + for rtr in rtrs: + luCommand( + rtr, + "ip route get 10.{}.{}.{}".format(b, c, d), + "dev", + "wait", + "Route to 10.{}.{}.{} available".format(b, c, d), + wait, + wait_time=10, + ) + luCommand( + rtr, + "ip route show | grep -c \\^10\\.", + str(num), + "wait", + "See {} linux routes".format(num), + wait, + wait_time=10, + ) + + rtrs = ["r1", "r3", "r4"] + for rtr in rtrs: + luCommand( + rtr, + "ip route get vrf {}-cust1 10.{}.{}.{}".format(rtr, b, c, d), + "dev", + "wait", + "VRF route available", + wait, + wait_time=10, + ) + luCommand( + rtr, + "ip route show vrf {}-cust1 | grep -c \\^10\\.".format(rtr), + str(num), + "wait", + "See {} linux routes".format(num), + wait, + wait_time=10, + ) + rtrs = ["ce1", "ce2", "ce3", "r1", "r2", "r3", "r4"] + for rtr in rtrs: + ret = luCommand( + rtr, + 'vtysh -c "show memory"', + "zebra: System allocator statistics: Total heap allocated: *(\d*) ([A-Za-z]*) .*bgpd: System allocator statistics: Total heap allocated: *(\d*) ([A-Za-z]*)", + "none", + "collect bgpd memory stats", + ) + found = luLast() + if ret != False and found != None: + val_z = int(found.group(1)) + if mem_z[rtr]["units"] != found.group(2): + val_z *= 1000 + delta_z = val_z - int(mem_z[rtr]["value"]) + ave_z = float(delta_z) / float(num) + + val_b = int(found.group(3)) + if mem_b[rtr]["units"] != found.group(4): + val_b *= 1000 + delta_b = val_b - int(mem_b[rtr]["value"]) + ave_b = float(delta_b) / float(num) + luCommand( + rtr, + 'vtysh -c "show thread cpu"', + ".", + "pass", + "BGPd heap: {0} {1} --> {2} {3} ({4} {1}/vpn route)".format( + mem_b[rtr]["value"], + mem_b[rtr]["units"], + found.group(3), + found.group(4), + round(ave_b, 4), + ), + ) + luCommand( + rtr, + 'vtysh -c "show thread cpu"', + ".", + "pass", + "Zebra heap: {0} {1} --> {2} {3} ({4} {1}/vpn route)".format( + mem_z[rtr]["value"], + mem_z[rtr]["units"], + found.group(1), + found.group(2), + round(ave_z, 4), + ), + ) +# done diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/test_bgp_l3vpn_to_bgp_vrf.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/test_bgp_l3vpn_to_bgp_vrf.py index 1da1066f0e..b537735c65 100755 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/test_bgp_l3vpn_to_bgp_vrf.py +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/test_bgp_l3vpn_to_bgp_vrf.py @@ -25,65 +25,132 @@ import sys import pytest -sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), '../')) +sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "../")) from lib.ltemplate import * + def test_check_linux_vrf(): CliOnFail = None # For debugging, uncomment the next line - #CliOnFail = 'tgen.mininet_cli' - CheckFunc = 'ltemplateVersionCheck(\'4.1\', iproute2=\'4.9\')' - #uncomment next line to start cli *before* script is run - #CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True, iproute2=\'4.9\')' - ltemplateTest('scripts/check_linux_vrf.py', False, CliOnFail, CheckFunc) + # CliOnFail = 'tgen.mininet_cli' + CheckFunc = "ltemplateVersionCheck('4.1', iproute2='4.9')" + # uncomment next line to start cli *before* script is run + # CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True, iproute2=\'4.9\')' + ltemplateTest("scripts/check_linux_vrf.py", False, CliOnFail, CheckFunc) + def test_adjacencies(): CliOnFail = None # For debugging, uncomment the next line - #CliOnFail = 'tgen.mininet_cli' - CheckFunc = 'ltemplateVersionCheck(\'4.1\')' - #uncomment next line to start cli *before* script is run - #CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True)' - ltemplateTest('scripts/adjacencies.py', False, CliOnFail, CheckFunc) + # CliOnFail = 'tgen.mininet_cli' + CheckFunc = "ltemplateVersionCheck('4.1')" + # uncomment next line to start cli *before* script is run + # CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True)' + ltemplateTest("scripts/adjacencies.py", False, CliOnFail, CheckFunc) + + +def test_notification_check(): + CliOnFail = None + # For debugging, uncomment the next line + # CliOnFail = 'tgen.mininet_cli' + CheckFunc = "ltemplateVersionCheck('4.1', iproute2='4.9')" + # uncomment next line to start cli *before* script is run + # CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True, iproute2=\'4.9\')' + ltemplateTest("scripts/notification_check.py", False, CliOnFail, CheckFunc) + def SKIP_test_add_routes(): CliOnFail = None # For debugging, uncomment the next line - #CliOnFail = 'tgen.mininet_cli' - CheckFunc = 'ltemplateVersionCheck(\'4.1\')' - #uncomment next line to start cli *before* script is run - #CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True)' - ltemplateTest('scripts/add_routes.py', False, CliOnFail, CheckFunc) + # CliOnFail = 'tgen.mininet_cli' + CheckFunc = "ltemplateVersionCheck('4.1')" + # uncomment next line to start cli *before* script is run + # CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True)' + ltemplateTest("scripts/add_routes.py", False, CliOnFail, CheckFunc) + def test_check_routes(): CliOnFail = None # For debugging, uncomment the next line - #CliOnFail = 'tgen.mininet_cli' - CheckFunc = 'ltemplateVersionCheck(\'4.1\')' - #uncomment next line to start cli *before* script is run - #CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True)' - ltemplateTest('scripts/check_routes.py', False, CliOnFail, CheckFunc) + # CliOnFail = 'tgen.mininet_cli' + CheckFunc = "ltemplateVersionCheck('4.1')" + # uncomment next line to start cli *before* script is run + # CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True)' + ltemplateTest("scripts/check_routes.py", False, CliOnFail, CheckFunc) -#manual data path setup test - remove once have bgp/zebra vrf path working + +# manual data path setup test - remove once have bgp/zebra vrf path working def test_check_linux_mpls(): CliOnFail = None # For debugging, uncomment the next line - #CliOnFail = 'tgen.mininet_cli' - CheckFunc = 'ltemplateVersionCheck(\'4.1\', iproute2=\'4.9\')' - #uncomment next line to start cli *before* script is run - #CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True, iproute2=\'4.9\')' - ltemplateTest('scripts/check_linux_mpls.py', False, CliOnFail, CheckFunc) + # CliOnFail = 'tgen.mininet_cli' + CheckFunc = "ltemplateVersionCheck('4.1', iproute2='4.9')" + # uncomment next line to start cli *before* script is run + # CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True, iproute2=\'4.9\')' + ltemplateTest("scripts/check_linux_mpls.py", False, CliOnFail, CheckFunc) + + +def test_notification_check(): + CliOnFail = None + # For debugging, uncomment the next line + # CliOnFail = 'tgen.mininet_cli' + CheckFunc = "ltemplateVersionCheck('4.1', iproute2='4.9')" + # uncomment next line to start cli *before* script is run + # CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True, iproute2=\'4.9\')' + ltemplateTest("scripts/notification_check.py", False, CliOnFail, CheckFunc) + + +def test_check_scale_up(): + CliOnFail = None + # For debugging, uncomment the next line + # CliOnFail = 'tgen.mininet_cli' + CheckFunc = "ltemplateVersionCheck('4.1', iproute2='4.9')" + # uncomment next line to start cli *before* script is run + # CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True, iproute2=\'4.9\')' + ltemplateTest("scripts/scale_up.py", False, CliOnFail, CheckFunc) + + +def test_notification_check(): + CliOnFail = None + # For debugging, uncomment the next line + # CliOnFail = 'tgen.mininet_cli' + CheckFunc = "ltemplateVersionCheck('4.1', iproute2='4.9')" + # uncomment next line to start cli *before* script is run + # CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True, iproute2=\'4.9\')' + ltemplateTest("scripts/notification_check.py", False, CliOnFail, CheckFunc) + + +def test_check_scale_down(): + CliOnFail = None + # For debugging, uncomment the next line + # CliOnFail = 'tgen.mininet_cli' + CheckFunc = "ltemplateVersionCheck('4.1', iproute2='4.9')" + # uncomment next line to start cli *before* script is run + # CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True, iproute2=\'4.9\')' + ltemplateTest("scripts/scale_down.py", False, CliOnFail, CheckFunc) + + +def test_notification_check(): + CliOnFail = None + # For debugging, uncomment the next line + # CliOnFail = 'tgen.mininet_cli' + CheckFunc = "ltemplateVersionCheck('4.1', iproute2='4.9')" + # uncomment next line to start cli *before* script is run + # CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True, iproute2=\'4.9\')' + ltemplateTest("scripts/notification_check.py", False, CliOnFail, CheckFunc) + def SKIP_test_cleanup_all(): CliOnFail = None # For debugging, uncomment the next line - #CliOnFail = 'tgen.mininet_cli' - CheckFunc = 'ltemplateVersionCheck(\'4.1\')' - #uncomment next line to start cli *before* script is run - #CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True)' - ltemplateTest('scripts/cleanup_all.py', False, CliOnFail, CheckFunc) + # CliOnFail = 'tgen.mininet_cli' + CheckFunc = "ltemplateVersionCheck('4.1')" + # uncomment next line to start cli *before* script is run + # CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True)' + ltemplateTest("scripts/cleanup_all.py", False, CliOnFail, CheckFunc) + -if __name__ == '__main__': +if __name__ == "__main__": retval = pytest.main(["-s"]) sys.exit(retval) diff --git a/tests/topotests/bgp_large_community/__init__.py b/tests/topotests/bgp_large_community/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bgp_large_community/bgp_large_community_topo_1.json b/tests/topotests/bgp_large_community/bgp_large_community_topo_1.json new file mode 100644 index 0000000000..902c01bcbe --- /dev/null +++ b/tests/topotests/bgp_large_community/bgp_large_community_topo_1.json @@ -0,0 +1,262 @@ +{ + "ipv4base": "192.168.1.0", + "ipv4mask": 24, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "192.168.1.0", + "v4mask": 24, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:DB8:F::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link1": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {} + } + }, + "r3": { + "dest_link": { + "r1-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {} + } + }, + "r3": { + "dest_link": { + "r1-link1": {} + } + } + } + } + } + } + } + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4-link1": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "1000000", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {} + } + }, + "r4": { + "dest_link": { + "r2-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {} + } + }, + "r4": { + "dest_link": { + "r2-link1": {} + } + } + } + } + } + } + } + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1-link1": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3-link1": {} + } + } + } + } + } + } + } + }, + "r4": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r5-link1": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "4000000", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4-link1": {} + } + }, + "r5": { + "dest_link": { + "r4-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4-link1": {} + } + }, + "r5": { + "dest_link": { + "r4-link1": {} + } + } + } + } + } + } + } + }, + "r5": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r4-link1": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "6000000", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r5-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r5-link1": {} + } + } + } + } + } + } + } + } + } +} diff --git a/tests/topotests/bgp_large_community/bgp_large_community_topo_2.json b/tests/topotests/bgp_large_community/bgp_large_community_topo_2.json new file mode 100644 index 0000000000..6f1ca90afb --- /dev/null +++ b/tests/topotests/bgp_large_community/bgp_large_community_topo_2.json @@ -0,0 +1,344 @@ +{ + "ipv4base": "10.0.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 30, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:DB8:F::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "1000000", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {} + } + }, + "r3": { + "dest_link": { + "r1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {} + } + }, + "r3": { + "dest_link": { + "r1": {} + } + } + } + } + } + } + } + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "1000000", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r4": { + "dest_link": { + "r2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r4": { + "dest_link": { + "r2": {} + } + } + } + } + } + } + } + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r5": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {} + } + }, + "r5": { + "dest_link": { + "r3": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {} + } + }, + "r5": { + "dest_link": { + "r3": {} + } + } + } + } + } + } + } + }, + "r4": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r6": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "4000000", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": {} + } + }, + "r6": { + "dest_link": { + "r4": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": {} + } + }, + "r6": { + "dest_link": { + "r4": {} + } + } + } + } + } + } + } + }, + "r5": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r6": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "5000000", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r5": {} + } + }, + "r6": { + "dest_link": { + "r5": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r5": {} + } + }, + "r6": { + "dest_link": { + "r5": {} + } + } + } + } + } + } + } + }, + "r6": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r4": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r5": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "6000000", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r6": {} + } + }, + "r5": { + "dest_link": { + "r6": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r6": {} + } + }, + "r5": { + "dest_link": { + "r6": {} + } + } + } + } + } + } + } + } + } +} 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 new file mode 100755 index 0000000000..1ed25b1787 --- /dev/null +++ b/tests/topotests/bgp_large_community/test_bgp_large_community_topo_1.py @@ -0,0 +1,1248 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2019 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + + +""" +Following tests are covered to test large-community/community functionality: +1. Verify if large community attribute can be configured only in correct + canonical format. +2. Verify that the community attribute value, which we have advertised are + received in correct format and values, at the receiving end. +3. Verify BGP Large Community attribute"s transitive property attribute. +4. Verify that BGP Large Communities attribute are malformed, if the length of + the BGP Large Communities Attribute value, expressed in octets, + is not a non-zero multiple of 12. +5. Verify if overriding large community values works fine. +6. Verify that large community values" aggregation works fine. +7. Standard community also work fine in conjunction with large-community. +8. Matching prefixes based on attributes other than prefix list and make use + of set clause (IPV6). +9. Matching prefixes based on attributes other than prefix list and make use + of set clause (IPV4). +10. Verify community and large-community list operations in route-map with all + clause (exact, all, any, regex) works. +11. Verify that any value in BGP Large communities for boundary values. +12. Clear BGP neighbor-ship and check if large community and community + attributes are getting re-populated. + +""" + +import pytest +import time +from os import path as os_path +import sys +from json import load as json_load + +# Required to instantiate the topology builder class. +from lib.topogen import Topogen, get_topogen +from mininet.topo import Topo + +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + reset_config_on_routers, + create_route_maps, + create_bgp_community_lists, + create_prefix_lists, + verify_bgp_community, + step, + check_address_types, + required_linux_kernel_version +) +from lib.topolog import logger +from lib.bgp import verify_bgp_convergence, create_router_bgp, clear_bgp_and_verify +from lib.topojson import build_topo_from_json, build_config_from_json + +# Save the Current Working Directory to find configuration files. +CWD = os_path.dirname(os_path.realpath(__file__)) +sys.path.append(os_path.join(CWD, "../")) +sys.path.append(os_path.join(CWD, "../lib/")) + +# Reading the data from JSON File for topology and configuration creation +jsonFile = "{}/bgp_large_community_topo_1.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json_load(topoJson) +except IOError: + logger.info("Could not read file:", jsonFile) + +# Global variables +bgp_convergence = False +NETWORK = { + "ipv4": ["200.50.2.0", "200.50.2.1", "200.50.2.0"], + "ipv6": ["1::1", "1::2", "1::0"], +} +MASK = {"ipv4": "32", "ipv6": "128"} +NET_MASK = {"ipv4": "24", "ipv6": "120"} +IPV4_NET = ["200.50.2.0"] +IPV6_NET = ["1::0"] +CONFIG_ROUTER_R1 = False +CONFIG_ROUTER_R2 = False +CONFIG_ROUTER_ADDITIVE = False +ADDR_TYPES = [] +LARGE_COMM = { + "r1": "1:1:1 1:2:1 1:3:1 1:4:1 1:5:1", + "r2": "2:1:1 2:2:1 2:3:1 2:4:1 2:5:1", + "mal_1": "1:1 1:2 1:3 1:4 1:5", + "pf_list_1": "0:0:1 0:0:10 0:0:100", + "pf_list_2": "0:0:2 0:0:20 0:0:200", + "agg_1": "0:0:1 0:0:2 0:0:10 0:0:20 0:0:100 0:0:200 2:1:1 " + "2:2:1 2:3:1 2:4:1 2:5:1", + "agg_2": "0:0:2 0:0:20 0:0:200 2:1:1 " "2:2:1 2:3:1 2:4:1 2:5:1", +} +STANDARD_COMM = { + "r1": "1:1 1:2 1:3 1:4 1:5", + "r2": "2:1 2:2 2:3 2:4 2:5", + "mal_1": "1 2 3 4 5", + "pf_list_1": "0:1 0:10 0:100", + "pf_list_2": "0:2 0:20 0:200", + "agg_1": "0:1 0:2 0:10 0:20 0:100 0:200 2:1 2:2 2:3 2:4 2:5", + "agg_2": "0:2 0:20 0:200 2:1 2:2 2:3 2:4 2:5", +} + + +class CreateTopo(Topo): + """ + Test topology builder + + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + """Build function""" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version('4.15') + if result is not True: + pytest.skip("Kernel requirements are not met") + + global ADDR_TYPES + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(CreateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Checking BGP convergence + global bgp_convergence + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + ##tgen.mininet_cli() + # Api call verify whether BGP is converged + bgp_convergence = verify_bgp_convergence(tgen, topo) + assert bgp_convergence is True, "setup_module :Failed \n Error:" " {}".format( + bgp_convergence + ) + + ADDR_TYPES = check_address_types() + logger.info("Running setup_module() done") + + +def teardown_module(): + """ + Teardown the pytest environment + + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +def config_router_r1(tgen, topo, tc_name): + global CONFIG_ROUTER_R1 + + input_dict_1 = { + "r1": { + "route_maps": { + "LC1": [ + { + "action": "permit", + "seq_id": "10", + "set": { + "large_community": {"num": LARGE_COMM["r1"]}, + "community": {"num": STANDARD_COMM["r1"]}, + }, + } + ] + } + } + } + + step("Configuring LC1 on r1") + result = create_route_maps(tgen, input_dict_1) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + # Configure neighbor for route map + input_dict_2 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + { + "network": "%s/%s" + % (NETWORK["ipv4"][0], MASK["ipv4"]), + "no_of_network": 4, + } + ], + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": { + "route_maps": [ + {"name": "LC1", "direction": "out"} + ] + } + } + }, + "r3": { + "dest_link": { + "r1-link1": { + "route_maps": [ + {"name": "LC1", "direction": "out"} + ] + } + } + }, + }, + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + { + "network": "%s/%s" + % (NETWORK["ipv6"][0], MASK["ipv6"]), + "no_of_network": 4, + } + ], + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": { + "route_maps": [ + {"name": "LC1", "direction": "out"} + ] + } + } + }, + "r3": { + "dest_link": { + "r1-link1": { + "route_maps": [ + {"name": "LC1", "direction": "out"} + ] + } + } + }, + }, + } + }, + } + } + } + } + + step("Applying LC1 on r1 neighbors and advertising networks") + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + CONFIG_ROUTER_R1 = True + + +def config_router_r2(tgen, topo, tc_name): + global CONFIG_ROUTER_R2 + + input_dict = { + "r2": { + "route_maps": { + "LC2": [ + { + "action": "permit", + "seq_id": "10", + "set": { + "large_community": {"num": LARGE_COMM["r2"]}, + "community": {"num": STANDARD_COMM["r2"]}, + }, + } + ] + } + } + } + + step("Configuring route-maps LC2 on r2") + result = create_route_maps(tgen, input_dict) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + input_dict_1 = { + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2-link1": { + "route_maps": [ + {"name": "LC2", "direction": "out"} + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2-link1": { + "route_maps": [ + {"name": "LC2", "direction": "out"} + ] + } + } + } + } + } + }, + } + } + } + } + + step("Applying LC2 on r2 neighbors in out direction") + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + CONFIG_ROUTER_R2 = True + + +def config_router_additive(tgen, topo, tc_name): + global CONFIG_ROUTER_ADDITIVE + + input_dict = { + "r2": { + "route_maps": { + "LC2": [ + { + "action": "permit", + "seq_id": "10", + "set": { + "large_community": { + "num": LARGE_COMM["r2"], + "action": "additive", + }, + "community": { + "num": STANDARD_COMM["r2"], + "action": "additive", + }, + }, + } + ] + } + } + } + + step("Configuring LC2 with community attributes as additive") + result = create_route_maps(tgen, input_dict) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + # tgen.mininet_cli() + CONFIG_ROUTER_ADDITIVE = True + + +def config_for_as_path(tgen, topo, tc_name): + config_router_r1(tgen, topo, tc_name) + + config_router_r2(tgen, topo, tc_name) + + # Create ipv6 prefix list + input_dict_1 = { + "r1": { + "prefix_lists": { + "ipv4": { + "pf_list_1": [ + { + "seqid": "10", + "network": "%s/%s" % (NETWORK["ipv4"][0], MASK["ipv4"]), + "action": "permit", + } + ], + "pf_list_2": [ + { + "seqid": "10", + "network": "%s/%s" % (NETWORK["ipv4"][1], MASK["ipv4"]), + "action": "permit", + } + ], + }, + "ipv6": { + "pf_list_3": [ + { + "seqid": "10", + "network": "%s/%s" % (NETWORK["ipv6"][0], MASK["ipv6"]), + "action": "permit", + } + ], + "pf_list_4": [ + { + "seqid": "10", + "network": "%s/%s" % (NETWORK["ipv6"][1], MASK["ipv6"]), + "action": "permit", + } + ], + }, + } + } + } + + step("Configuring prefix-lists on r1 to filter networks") + result = create_prefix_lists(tgen, input_dict_1) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + input_dict_2 = { + "r1": { + "route_maps": { + "LC1": [ + { + "action": "permit", + "seq_id": 10, + "match": {"ipv4": {"prefix_lists": "pf_list_1"}}, + "set": { + "large_community": {"num": LARGE_COMM["pf_list_1"]}, + "community": {"num": STANDARD_COMM["pf_list_1"]}, + }, + }, + { + "action": "permit", + "seq_id": 20, + "match": {"ipv6": {"prefix_lists": "pf_list_3"}}, + "set": { + "large_community": {"num": LARGE_COMM["pf_list_1"]}, + "community": {"num": STANDARD_COMM["pf_list_1"]}, + }, + }, + { + "action": "permit", + "seq_id": 30, + "match": {"ipv4": {"prefix_lists": "pf_list_2"}}, + "set": { + "large_community": {"num": LARGE_COMM["pf_list_2"]}, + "community": {"num": STANDARD_COMM["pf_list_2"]}, + }, + }, + { + "action": "permit", + "seq_id": 40, + "match": {"ipv6": {"prefix_lists": "pf_list_4"}}, + "set": { + "large_community": {"num": LARGE_COMM["pf_list_2"]}, + "community": {"num": STANDARD_COMM["pf_list_2"]}, + }, + }, + ] + } + } + } + + step( + "Applying prefix-lists match in route-map LC1 on r1. Setting" + " community attritbute for filtered networks" + ) + result = create_route_maps(tgen, input_dict_2) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + config_router_additive(tgen, topo, tc_name) + + input_dict_3 = { + "r4": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "ANY", + "value": LARGE_COMM["pf_list_1"], + "large": True, + }, + { + "community_type": "standard", + "action": "permit", + "name": "ANY", + "value": STANDARD_COMM["pf_list_1"], + }, + ] + } + } + + step("Configuring bgp community lists on r4") + result = create_bgp_community_lists(tgen, input_dict_3) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + input_dict_4 = { + "r4": { + "route_maps": { + "LC4": [ + { + "action": "permit", + "seq_id": "10", + "match": { + "large_community_list": {"id": "ANY"}, + "community_list": {"id": "ANY"}, + }, + "set": {"path": {"as_num": "4000000", "as_action": "prepend"}}, + } + ] + } + } + } + + step("Applying community list on route-map on r4") + result = create_route_maps(tgen, input_dict_4) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + input_dict_5 = { + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r5": { + "dest_link": { + "r4-link1": { + "route_maps": [ + {"name": "LC4", "direction": "out"} + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r5": { + "dest_link": { + "r4-link1": { + "route_maps": [ + {"name": "LC4", "direction": "out"} + ] + } + } + } + } + } + }, + } + } + } + } + + step("Applying route-map LC4 out from r4 to r5 ") + result = create_router_bgp(tgen, topo, input_dict_5) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + +##################################################### +# +# Test cases +# +##################################################### +def test_large_community_set(request): + """ + Verify if large community attribute can be configured only in correct + canonical format. + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # API call to modify router id + # input_dict dictionary to be provided to configure route_map + input_dict = { + "r1": { + "route_maps": { + "LC1": [ + { + "action": "permit", + "seq_id": "10", + "set": { + "large_community": {"num": LARGE_COMM["r1"]}, + "community": {"num": STANDARD_COMM["r1"]}, + }, + } + ] + } + } + } + + step("Trying to set bgp communities") + result = create_route_maps(tgen, input_dict) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_large_community_advertise(request): + """ + Verify that the community attribute value, which we have advertised are + received in correct format and values, at the receiving end. + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + reset_config_on_routers(tgen) + config_router_r1(tgen, topo, tc_name) + + input_dict = { + "largeCommunity": LARGE_COMM["r1"], + "community": STANDARD_COMM["r1"], + } + + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, "r2", [NETWORK[adt][0]], input_dict) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_community(tgen, adt, "r3", [NETWORK[adt][0]], input_dict) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_large_community_transitive(request): + """ + Verify BGP Large Community attribute"s transitive property attribute. + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + reset_config_on_routers(tgen) + + config_router_r1(tgen, topo, tc_name) + + input_dict_1 = { + "largeCommunity": LARGE_COMM["r1"], + "community": STANDARD_COMM["r1"], + } + + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, "r4", [NETWORK[adt][0]], input_dict_1) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_large_community_override(request): + """ + Verify if overriding large community values works fine. + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + reset_config_on_routers(tgen) + config_router_r1(tgen, topo, tc_name) + + config_router_r2(tgen, topo, tc_name) + + input_dict_3 = { + "largeCommunity": LARGE_COMM["r2"], + "community": STANDARD_COMM["r2"], + } + + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, "r4", [NETWORK[adt][1]], input_dict_3) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_large_community_additive(request): + """ + Verify that large community values" aggregation works fine. + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + reset_config_on_routers(tgen) + config_router_r1(tgen, topo, tc_name) + + config_router_r2(tgen, topo, tc_name) + + config_router_additive(tgen, topo, tc_name) + + input_dict_1 = { + "largeCommunity": "%s %s" % (LARGE_COMM["r1"], LARGE_COMM["r2"]), + "community": "%s %s" % (STANDARD_COMM["r1"], STANDARD_COMM["r2"]), + } + + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, "r4", [NETWORK[adt][0]], input_dict_1) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_large_community_match_as_path(request): + """ + Matching prefixes based on attributes other than prefix list and make use + of set clause. + """ + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + reset_config_on_routers(tgen) + config_for_as_path(tgen, topo, tc_name) + + input_dict = { + "largeCommunity": "%s %s" % (LARGE_COMM["pf_list_1"], LARGE_COMM["r2"]), + "community": "%s %s" % (STANDARD_COMM["pf_list_1"], STANDARD_COMM["r2"]), + } + + input_dict_1 = { + "largeCommunity": "%s %s" % (LARGE_COMM["pf_list_2"], LARGE_COMM["r2"]), + "community": "%s %s" % (STANDARD_COMM["pf_list_2"], STANDARD_COMM["r2"]), + } + + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, "r5", [NETWORK[adt][0]], input_dict) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_community( + tgen, adt, "r5", [NETWORK[adt][1]], input_dict_1, expected=False + ) + + assert result is not True, "Test case {} : Should fail \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_large_community_match_all(request): + """ + Verify community and large-community list operations in route-map with all + clause (exact, all, any, regex) works. + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + reset_config_on_routers(tgen) + config_router_r1(tgen, topo, tc_name) + + config_router_r2(tgen, topo, tc_name) + + config_router_additive(tgen, topo, tc_name) + + input_dict_1 = { + "r4": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "ANY", + "value": "1:1:1", + "large": True, + }, + { + "community_type": "standard", + "action": "permit", + "name": "ALL", + "value": "1:1:1 1:2:1 1:3:1 1:4:1 1:5:1 2:1:1 2:2:1", + "large": True, + }, + { + "community_type": "expanded", + "action": "permit", + "name": "EXP_ALL", + "value": "1:1:1 1:2:1 1:3:1 1:4:1 1:5:1 2:[1-5]:1", + "large": True, + }, + ] + } + } + + step("Create bgp community lists for ANY, EXACT and EXP_ALL match") + + result = create_bgp_community_lists(tgen, input_dict_1) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + input_dict_2 = { + "r4": { + "route_maps": { + "LC4": [ + { + "action": "permit", + "seq_id": "10", + "match": {"large-community-list": {"id": "ANY"}}, + }, + { + "action": "permit", + "seq_id": "20", + "match": {"large-community-list": {"id": "EXACT"}}, + }, + { + "action": "permit", + "seq_id": "30", + "match": {"large-community-list": {"id": "EXP_ALL"}}, + }, + ] + } + } + } + + step("Applying bgp community lits on LC4 route-map") + result = create_route_maps(tgen, input_dict_2) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + input_dict_3 = { + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r5": { + "dest_link": { + "r4-link1": { + "route_maps": [ + {"name": "LC4", "direction": "in"} + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r5": { + "dest_link": { + "r4-link1": { + "route_maps": [ + {"name": "LC4", "direction": "in"} + ] + } + } + } + } + } + }, + } + } + } + } + + step("Apply route-mpa LC4 on r4 for r2 neighbor, direction 'in'") + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + input_dict_4 = { + "largeCommunity": "1:1:1 1:2:1 1:3:1 1:4:1 1:5:1 2:1:1 2:2:1 2:3:1 " + "2:4:1 2:5:1" + } + + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, "r4", [NETWORK[adt][0]], input_dict_4) + assert result is True, "Test case {} : Should fail \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +# @pytest.mark.skip(reason="as-set not working for ipv6") +def test_large_community_aggregate_network(request): + """ + Restart router and check if large community and community + attributes are getting re-populated. + """ + + tc_name = request.node.name + write_test_header(tc_name) + + tgen = get_topogen() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + reset_config_on_routers(tgen) + + config_for_as_path(tgen, topo, tc_name) + + input_dict = { + "community": STANDARD_COMM["agg_1"], + "largeCommunity": LARGE_COMM["agg_1"], + } + + input_dict_1 = { + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "aggregate_address": [ + { + "network": "%s/%s" + % (NETWORK["ipv4"][2], NET_MASK["ipv4"]), + "as_set": True, + } + ] + } + }, + "ipv6": { + "unicast": { + "aggregate_address": [ + { + "network": "%s/%s" + % (NETWORK["ipv6"][2], NET_MASK["ipv6"]), + "as_set": True, + } + ] + } + }, + } + } + } + } + + step("Configuring aggregate address as-set on r2") + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + for adt in ADDR_TYPES: + result = verify_bgp_community( + tgen, adt, "r4", ["%s/%s" % (NETWORK[adt][2], NET_MASK[adt])], input_dict + ) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_2 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + { + "network": "%s/%s" + % (NETWORK["ipv4"][0], MASK["ipv4"]), + "no_of_network": 1, + "delete": True, + } + ] + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + { + "network": "%s/%s" + % (NETWORK["ipv6"][0], MASK["ipv6"]), + "no_of_network": 1, + "delete": True, + } + ] + } + }, + } + } + } + } + + step("Stop advertising one of the networks") + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result) + + input_dict_3 = { + "community": STANDARD_COMM["agg_2"], + "largeCommunity": LARGE_COMM["agg_2"], + } + + for adt in ADDR_TYPES: + step("Verifying bgp community values on r5 is also modified") + result = verify_bgp_community( + tgen, adt, "r4", ["%s/%s" % (NETWORK[adt][2], NET_MASK[adt])], input_dict_3 + ) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_large_community_boundary_values(request): + """ + Verify that any value in BGP Large communities for boundary values. + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + input_dict = { + "r4": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "ANY", + "value": "0:-1", + } + ] + } + } + + step("Checking boundary value for community 0:-1") + result = create_bgp_community_lists(tgen, input_dict) + assert result is not True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Checking community attribute 0:65536") + input_dict_2 = { + "r4": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "ANY", + "value": "0:65536", + } + ] + } + } + + step("Checking boundary value for community 0:65536") + result = create_bgp_community_lists(tgen, input_dict_2) + assert result is not True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Checking boundary value for community 0:4294967296") + input_dict_3 = { + "r4": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "ANY", + "value": "0:4294967296", + "large": True, + } + ] + } + } + + result = create_bgp_community_lists(tgen, input_dict_3) + assert result is not True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + step("Checking boundary value for community 0:-1:1") + + input_dict_4 = { + "r4": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "ANY", + "value": "0:-1:1", + "large": True, + } + ] + } + } + + result = create_bgp_community_lists(tgen, input_dict_4) + assert result is not True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + +def test_large_community_invalid_chars(request): + """ + BGP canonical lcommunities must only be digits + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + input_dict = { + "r4": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "ANY", + "value": "1:a:2", + "large": True, + } + ] + } + } + + step("Checking boundary value for community 1:a:2") + result = create_bgp_community_lists(tgen, input_dict) + assert result is not True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + +def test_large_community_after_clear_bgp(request): + """ + Clear BGP neighbor-ship and check if large community and community + attributes are getting re-populated. + """ + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + reset_config_on_routers(tgen) + config_router_r1(tgen, topo, tc_name) + + input_dict = {"largeCommunity": LARGE_COMM["r1"], "community": STANDARD_COMM["r1"]} + + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, "r2", [NETWORK[adt][0]], input_dict) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Clearing BGP on r1") + clear_bgp_and_verify(tgen, topo, "r1") + + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, "r2", [NETWORK[adt][0]], input_dict) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_large_community/test_bgp_large_community_topo_2.py b/tests/topotests/bgp_large_community/test_bgp_large_community_topo_2.py new file mode 100755 index 0000000000..bb88e47415 --- /dev/null +++ b/tests/topotests/bgp_large_community/test_bgp_large_community_topo_2.py @@ -0,0 +1,2240 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2019 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_bgp_large_community_topo_1.py: Test BGP large community. + +Following tests are covered: +1. Verify the standard large-community-lists can permit or deny + large community attribute only in the correct canonical format. +2. Verify the expanded large-community-lists can permit or deny + large community attribute both in the correct canonical format + as well as REG_EX. +3. Verify that we can modify a large-community-list is in use, + to add/remove attribute value and it takes immediate effect. +4. Verify that large community attribute gets advertised when + route-map is applied to a neighbor and cleared when route-map + is removed. +5. Verify that duplicate BGP Large Community values are NOT be transmitted. +6. Verify if we want to remove all the large-community attributes from a + set of prefix we can set the value as NONE. +7. Redistribute connected and static routes in BGP process with a route-map + appending/removing L-comm attributes. +8. Verify if we want to remove specific large-community values from + a set of prefix we can make use of DELETE operation based on L-comm list. +9. Verify that if community values are NOT be advertised to a specific + neighbour, we negate send-community command. + (Send-community all is enabled by default for all neighbors) +10. Verify that large-community lists can not be configured without providing + specific L-community values(for match/delete operation in a route-map). +11. Verify that Match_EXACT clause should pass only if all of the L-comm + values configured (horizontally) in the community list is present in + the prefix. There must be no additional L-communities in the prefix. +12. Verify that Match_ALL clause should pass only if ALL of the L-comm values + configured (horizontally) in the community list is present in the prefix. + There could be additional L-communities in the prefix that are not present + in the L-comm list. +13. Verify that Match_ANY clause should pass only if at-least any one L-comm + value configured(vertically) in large-community list, is present in prefixes. +14. Verify large-community lists operation in a route-map with match RegEx + statements. +""" + +import os +import sys +import json +import pytest +import time + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +# Import topoJson from lib, to create topology and initial configuration +from lib.topogen import Topogen, get_topogen +from mininet.topo import Topo + +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + reset_config_on_routers, + create_route_maps, + create_bgp_community_lists, + create_prefix_lists, + verify_bgp_community, + step, + verify_create_community_list, + delete_route_maps, + verify_route_maps, + create_static_routes, + check_address_types, + required_linux_kernel_version +) +from lib.topolog import logger +from lib.bgp import verify_bgp_convergence, create_router_bgp, clear_bgp_and_verify +from lib.topojson import build_topo_from_json, build_config_from_json + +# Reading the data from JSON File for topology and configuration creation +jsonFile = "{}/bgp_large_community_topo_2.json".format(CWD) + +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +# Global variables +bgp_convergence = False + +NETWORKS = {"ipv4": ["200.50.2.0/32"], "ipv6": ["1::1/128"]} + + +class GenerateTopo(Topo): + """ + Test topology builder + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version('4.15') + if result is not True: + pytest.skip("Kernel requirements are not met") + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(GenerateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + # Checking BGP convergence + global bgp_convergence, ADDR_TYPES + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Api call verify whether BGP is converged + # Ipv4 + bgp_convergence = verify_bgp_convergence(tgen, topo) + assert bgp_convergence is True, "setup_module :Failed \n Error:" " {}".format( + bgp_convergence + ) + ADDR_TYPES = check_address_types() + + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """ + Teardown the pytest environment + + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# +# Testcases +# +##################################################### + + +def test_create_bgp_standard_large_community_list(request): + """ + Create standard large-community-list and verify it can permit + or deny large community attribute only in the correct canonical + format. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + reset_config_on_routers(tgen) + + step("Create srtandard large community list") + input_dict = { + "r4": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "LC_1_STD", + "value": "2:1:1 2:1:2 1:2:3", + "large": True, + }, + { + "community_type": "standard", + "action": "permit", + "name": "LC_2_STD", + "value": "3:1:1 3:1:2", + "large": True, + }, + ] + } + } + result = create_bgp_community_lists(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify BGP large community is created") + result = verify_create_community_list(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Create srtandard large community list with in-correct values") + input_dict = { + "r4": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "LC_1_STD_ERR", + "value": "0:0:0", + "large": True, + } + ] + } + } + result = create_bgp_community_lists(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + ## TODO should fail + step("Verify BGP large community is created") + result = verify_create_community_list(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_create_bgp_expanded_large_community_list(request): + """ + Create expanded large-community-list and verify it can permit + or deny large community attribute both in the correct canonical + format as well as REG_EX + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step("Create expanded large community list") + input_dict = { + "r4": { + "bgp_community_lists": [ + { + "community_type": "expanded", + "action": "permit", + "name": "LC_1_EXP", + "value": "1:1:200 1:2:* 3:2:1", + "large": True, + } + ] + } + } + result = create_bgp_community_lists(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify BGP large community is created") + result = verify_create_community_list(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_modify_large_community_lists_referenced_by_rmap(request): + """ + This test is to verify that we can modify a large-community-list + is in use, add/remove attribute value and it takes immediate effect. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step("Create standard large community list") + input_dict_1 = { + "r4": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "LC_DEL", + "value": "1:2:1 1:3:1 2:1:1 2:2:2 3:3:3", + "large": True, + } + ] + } + } + result = create_bgp_community_lists(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Create route map") + input_dict_2 = { + "r1": { + "route_maps": { + "RM_R2_OUT": [ + { + "action": "permit", + "seq_id": "10", + "set": { + "large_community": { + "num": "1:2:1 1:3:1 2:10:1 3:3:3 4:4:4 5:5:5", + "action": "additive", + } + }, + } + ] + } + }, + "r4": { + "route_maps": { + "RM_R4_IN": [ + { + "action": "permit", + "seq_id": "10", + "set": {"large_comm_list": {"id": "LC_DEL", "delete": True}}, + } + ] + } + }, + } + result = create_route_maps(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure neighbor for route map and advertise networks") + input_dict_3 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [{"network": "200.50.2.0/32"}], + "neighbor": { + "r2": { + "dest_link": { + "r1": { + "route_maps": [ + { + "name": "RM_R2_OUT", + "direction": "out", + } + ] + } + } + } + }, + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [{"network": "1::1/128"}], + "neighbor": { + "r2": { + "dest_link": { + "r1": { + "route_maps": [ + { + "name": "RM_R2_OUT", + "direction": "out", + } + ] + } + } + } + }, + } + }, + } + } + }, + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": { + "route_maps": [ + {"name": "RM_R4_IN", "direction": "in"} + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": { + "route_maps": [ + {"name": "RM_R4_IN", "direction": "in"} + ] + } + } + } + } + } + }, + } + } + }, + } + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify Community-list") + dut = "r4" + input_dict_4 = {"largeCommunity": "2:10:1 4:4:4 5:5:5"} + + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, dut, NETWORKS[adt], input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_large_community_lists_with_rmap_apply_and_remove(request): + """ + This test is to verify that large community attribute gets advertised when + route-map is applied to a neighbor and cleared when route-map is removed + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step("Create route map") + input_dict_1 = { + "r4": { + "route_maps": { + "RM_LC1": [ + { + "action": "permit", + "seq_id": "10", + "set": { + "large_community": { + "num": "200:200:1 200:200:10 200:200:20000", + "action": "additive", + } + }, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure neighbor for route map and advertise networks") + input_dict_2 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [{"network": "200.50.2.0/32"}] + } + }, + "ipv6": { + "unicast": {"advertise_networks": [{"network": "1::1/128"}]} + }, + } + } + }, + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r6": { + "dest_link": { + "r4": { + "route_maps": [ + {"name": "RM_LC1", "direction": "out"} + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r6": { + "dest_link": { + "r4": { + "route_maps": [ + {"name": "RM_LC1", "direction": "out"} + ] + } + } + } + } + } + }, + } + } + }, + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify large-community-list") + dut = "r6" + input_dict_4 = {"largeCommunity": "200:200:1 200:200:10 200:200:20000"} + + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, dut, NETWORKS[adt], input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Delete route map reference by community-list") + input_dict_3 = {"r4": {"route_maps": ["RM_LC1"]}} + result = delete_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify route map is deleted") + result = verify_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify large-community-list") + for adt in ADDR_TYPES: + result = verify_bgp_community( + tgen, adt, dut, NETWORKS[adt], input_dict_4, expected=False + ) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_duplicate_large_community_list_attributes_not_transitive(request): + """ + This test is to verify that duplicate BGP Large Community values + are NOT be transmitted. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step("Create route map") + input_dict_1 = { + "r4": { + "route_maps": { + "RM_R4_IN": [ + { + "action": "permit", + "seq_id": "10", + "set": { + "large_community": { + "num": "0:0:1 0:0:10 0:0:100 2:0:1 2:0:2 2:0:3" + " 2:0:4 2:0:5", + "action": "additive", + } + }, + } + ], + "RM_R4_OUT": [ + { + "action": "permit", + "seq_id": "10", + "set": { + "large_community": { + "num": "0:0:1 0:0:10 0:0:10000 2:0:1 2:0:2", + "action": "additive", + } + }, + } + ], + } + } + } + result = create_route_maps(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure neighbor for route map and advertise networks") + input_dict_2 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [{"network": "200.50.2.0/32"}] + } + }, + "ipv6": { + "unicast": {"advertise_networks": [{"network": "1::1/128"}]} + }, + } + } + }, + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": { + "route_maps": [ + {"name": "RM_R4_IN", "direction": "in"} + ] + } + } + }, + "r6": { + "dest_link": { + "r4": { + "route_maps": [ + { + "name": "RM_R4_OUT", + "direction": "out", + } + ] + } + } + }, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": { + "route_maps": [ + {"name": "RM_R4_IN", "direction": "in"} + ] + } + } + }, + "r6": { + "dest_link": { + "r4": { + "route_maps": [ + { + "name": "RM_R4_OUT", + "direction": "out", + } + ] + } + } + }, + } + } + }, + } + } + }, + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify large-community-list") + dut = "r6" + input_dict_4 = { + "largeCommunity": "0:0:1 0:0:10 0:0:100 0:0:10000 2:0:1 2:0:2 2:0:3 2:0:4 2:0:5" + } + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, dut, NETWORKS[adt], input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_large_community_lists_with_rmap_set_none(request): + """ + This test is to verify if we want to remove all the large-community + attributes from a set of prefix we can set the value as NONE. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step("Create route map") + input_dict_1 = { + "r4": { + "route_maps": { + "RM_R4_IN": [ + { + "action": "permit", + "seq_id": "10", + "set": { + "large_community": { + "num": "0:0:1 0:0:10 0:0:100 2:0:1 2:0:2 2:0:3" + " 2:0:4", + "action": "additive", + } + }, + } + ] + } + }, + "r6": { + "route_maps": { + "RM_R6_IN": [ + { + "action": "permit", + "seq_id": "10", + "set": {"large_community": {"num": "none"}}, + } + ] + } + }, + } + result = create_route_maps(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure neighbor for route map") + input_dict_2 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [{"network": "200.50.2.0/32"}] + } + }, + "ipv6": { + "unicast": {"advertise_networks": [{"network": "1::1/128"}]} + }, + } + } + }, + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": { + "route_maps": [ + {"name": "RM_R4_IN", "direction": "in"} + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": { + "route_maps": [ + {"name": "RM_R4_IN", "direction": "in"} + ] + } + } + } + } + } + }, + } + } + }, + "r6": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r6": { + "route_maps": [ + {"name": "RM_R6_IN", "direction": "in"} + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r6": { + "route_maps": [ + {"name": "RM_R6_IN", "direction": "in"} + ] + } + } + } + } + } + }, + } + } + }, + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify Community-list") + dut = "r6" + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, dut, NETWORKS[adt], expected=False) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_lcomm_lists_with_redistribute_static_connected_rmap(request): + """ + This test is to verify redistribute connected and static ipv4 routes + in BGP process with a route-map appending/removing L-comm attributes. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step("create static routes") + input_dict = { + "r1": { + "static_routes": [ + {"network": "200.50.2.0/32", "next_hop": "10.0.0.6"}, + {"network": "1::1/128", "next_hop": "fd00:0:0:1::2"}, + ] + } + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("redistribute static routes") + input_dict_1 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + { + "redist_type": "static", + "attribute": "route-map RM_R2_OUT", + }, + { + "redist_type": "connected", + "attribute": "route-map RM_R2_OUT", + }, + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + { + "redist_type": "static", + "attribute": "route-map RM_R2_OUT", + }, + { + "redist_type": "connected", + "attribute": "route-map RM_R2_OUT", + }, + ] + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Create route map") + input_dict_3 = { + "r1": { + "route_maps": { + "RM_R2_OUT": [ + { + "action": "permit", + "set": {"large_community": {"num": "55:55:55 555:555:555"}}, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify large-community-list for static and connected ipv4 route on" " r2") + + input_dict_5 = {"largeCommunity": "55:55:55 555:555:555"} + + if "ipv4" in ADDR_TYPES: + dut = "r2" + networks = ["200.50.2.0/32", "1.0.1.17/32"] + result = verify_bgp_community(tgen, "ipv4", dut, networks, input_dict_5) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify large-community-list for static and connected ipv4 route" " on r4") + dut = "r4" + networks = ["200.50.2.0/32", "1.0.1.17/32"] + result = verify_bgp_community(tgen, "ipv4", dut, networks, input_dict_5) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + if "ipv6" in ADDR_TYPES: + step("Verify large-community-list for static and connected ipv6 route" " on r2") + dut = "r2" + networks = ["1::1/128", "2001:db8:f::1:17/128"] + result = verify_bgp_community(tgen, "ipv6", dut, networks, input_dict_5) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify large-community-list for static and connected ipv6 route" " on r4") + dut = "r4" + networks = ["1::1/128", "2001:db8:f::1:17/128"] + result = verify_bgp_community(tgen, "ipv6", dut, networks, input_dict_5) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_large_community_lists_with_rmap_set_delete(request): + """ + This test is to verify if we want to remove specific large-community + values from a set of prefix we can make use of DELETE operation based + on L-comm list + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step("configure route_map") + input_dict_2 = { + "r6": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "Test", + "value": "1:2:1 1:1:10 1:3:100", + "large": True, + } + ] + } + } + result = create_bgp_community_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Create route map") + input_dict_3 = { + "r6": { + "route_maps": { + "RM_R6_IN": [ + { + "action": "permit", + "seq_id": "10", + "set": {"large_comm_list": {"id": "Test", "delete": True}}, + } + ] + } + }, + "r4": { + "route_maps": { + "RM_R4_IN": [ + { + "action": "permit", + "seq_id": "10", + "set": { + "large_community": { + "num": "1:2:1 1:1:10 1:3:100 2:1:1 2:2:2 2:3:3" + " 2:4:4 2:5:5", + "action": "additive", + } + }, + } + ] + } + }, + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure neighbor for route map and advertise networks") + input_dict_4 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [{"network": "200.50.2.0/32"}] + } + }, + "ipv6": { + "unicast": {"advertise_networks": [{"network": "1::1/128"}]} + }, + } + } + }, + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": { + "route_maps": [ + {"name": "RM_R4_IN", "direction": "in"} + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": { + "route_maps": [ + {"name": "RM_R4_IN", "direction": "in"} + ] + } + } + } + } + } + }, + } + } + }, + "r6": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r6": { + "route_maps": [ + {"name": "RM_R6_IN", "direction": "in"} + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r6": { + "route_maps": [ + {"name": "RM_R6_IN", "direction": "in"} + ] + } + } + } + } + } + }, + } + } + }, + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify large-community-list") + dut = "r6" + input_dict_5 = {"largeCommunity": "2:1:1 2:2:2 2:3:3 2:4:4 2:5:5"} + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, dut, NETWORKS[adt], input_dict_5) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_large_community_lists_with_no_send_community(request): + """ + This test is to verify if we want to remove specific large-community + values from a set of prefix we can make use of DELETE operation based + on L-comm list + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step("Create route map") + input_dict_2 = { + "r5": { + "route_maps": { + "RM_R6_OUT": [ + { + "action": "permit", + "seq_id": "10", + "set": { + "large_community": {"num": "2:1:1 2:2:2 2:3:3 2:4:4 2:5:5"} + }, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure neighbor for route map and advertise networks") + input_dict_3 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [{"network": "200.50.2.0/32"}] + } + }, + "ipv6": { + "unicast": {"advertise_networks": [{"network": "1::1/128"}]} + }, + } + } + }, + "r5": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r6": { + "dest_link": { + "r5": { + "route_maps": [ + { + "name": "RM_R6_OUT", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r6": { + "dest_link": { + "r5": { + "route_maps": [ + { + "name": "RM_R6_OUT", + "direction": "out", + } + ] + } + } + } + } + } + }, + } + } + }, + } + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify large-community-list") + dut = "r6" + input_dict_4 = {"largeCommunity": "2:1:1 2:2:2 2:3:3 2:4:4 2:5:5"} + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, dut, NETWORKS[adt], input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure neighbor for no-send-community") + input_dict_5 = { + "r5": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r6": { + "dest_link": {"r5": {"no_send_community": "large"}} + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r6": { + "dest_link": {"r5": {"no_send_community": "large"}} + } + } + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_5) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify Community-list") + for adt in ADDR_TYPES: + result = verify_bgp_community( + tgen, adt, dut, NETWORKS[adt], input_dict_4, expected=False + ) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_create_large_community_lists_with_no_attribute_values(request): + """ + This test is to verify that large-community lists can not be + configured without providing specific L-community values + (for match/delete operation in a route-map). + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step("Create standard large commumity-list") + input_dict_1 = { + "r5": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "Test1", + "large": True, + } + ] + } + } + result = create_bgp_community_lists(tgen, input_dict_1) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_large_community_lists_with_rmap_match_exact(request): + """ + This test is to verify that Match_EXACT clause should pass + only if all of the L-comm values configured (horizontally) + in the community list is present in the prefix. There must + be no additional L-communities in the prefix. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step("Create route map") + input_dict_2 = { + "r2": { + "route_maps": { + "RM_R4_OUT": [ + { + "action": "permit", + "seq_id": "10", + "set": { + "large_community": {"num": "2:1:1 2:2:2 2:3:3 2:4:4 2:5:5"} + }, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure neighbor for route map and advertise networks") + input_dict_3 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [{"network": "200.50.2.0/32"}] + } + }, + "ipv6": { + "unicast": {"advertise_networks": [{"network": "1::1/128"}]} + }, + } + } + }, + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2": { + "route_maps": [ + { + "name": "RM_R4_OUT", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2": { + "route_maps": [ + { + "name": "RM_R4_OUT", + "direction": "out", + } + ] + } + } + } + } + } + }, + } + } + }, + } + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Create standard large commumity-list") + input_dict_4 = { + "r4": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "EXACT", + "value": "2:1:1 2:2:2 2:3:3 2:4:4 2:5:5", + "large": True, + } + ] + } + } + result = create_bgp_community_lists(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify BGP large community is created") + result = verify_create_community_list(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Create route map") + input_dict_5 = { + "r4": { + "route_maps": { + "RM_R4_IN": [ + { + "action": "permit", + "seq_id": "10", + "match": { + "large-community-list": ["EXACT"], + "match_exact": True, + }, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_5) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure neighbor for route map") + input_dict_6 = { + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": { + "route_maps": [ + {"name": "RM_R4_IN", "direction": "in"} + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": { + "route_maps": [ + {"name": "RM_R4_IN", "direction": "in"} + ] + } + } + } + } + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_6) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify large-community-list") + dut = "r4" + input_dict_4 = {"largeCommunity": "2:1:1 2:2:2 2:3:3 2:4:4 2:5:5"} + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, dut, NETWORKS[adt], input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_large_community_lists_with_rmap_match_all(request): + """ + This test is to verify that Match_ALL clause should pass + only if ALL of the L-comm values configured (horizontally) + in the community list are present in the prefix. There + could be additional L-communities in the prefix that are + not present in the L-comm list. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step("Create route map") + input_dict_2 = { + "r2": { + "route_maps": { + "RM_R4_OUT": [ + { + "action": "permit", + "set": { + "large_community": { + "num": "1:1:1 1:2:3 2:1:1 2:2:2 2:3:3 2:4:4 2:5:5" + } + }, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure neighbor for route map") + input_dict_3 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [{"network": "200.50.2.0/32"}] + } + }, + "ipv6": { + "unicast": {"advertise_networks": [{"network": "1::1/128"}]} + }, + } + } + }, + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2": { + "route_maps": [ + { + "name": "RM_R4_OUT", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2": { + "route_maps": [ + { + "name": "RM_R4_OUT", + "direction": "out", + } + ] + } + } + } + } + } + }, + } + } + }, + } + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Create standard large commumity-list") + input_dict_4 = { + "r3": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "ALL", + "value": "2:1:1 2:2:2 2:3:3 2:4:4 2:5:5", + "large": True, + } + ] + } + } + result = create_bgp_community_lists(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify BGP large community is created") + result = verify_create_community_list(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Create route map") + input_dict_5 = { + "r4": { + "route_maps": { + "RM_R4_IN": [ + { + "action": "permit", + "seq_id": "10", + "match": {"large-community-list": {"id": "ALL"}}, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_5) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure neighbor for route map") + input_dict_6 = { + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": { + "route_maps": [ + {"name": "RM_R4_IN", "direction": "in"} + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": { + "route_maps": [ + {"name": "RM_R4_IN", "direction": "in"} + ] + } + } + } + } + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_6) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify large-community-list") + dut = "r4" + input_dict_4 = {"largeCommunity": "1:1:1 1:2:3 2:1:1 2:2:2 2:3:3 2:4:4 2:5:5"} + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, dut, NETWORKS[adt], input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_large_community_lists_with_rmap_match_any(request): + """ + This test is to verify that Match_ANY clause should pass + only if at-least any one L-comm value configured(vertically) + in large-community list, is present in prefixes. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step("Create route map") + input_dict_2 = { + "r2": { + "route_maps": { + "RM_R4_OUT": [ + { + "action": "permit", + "seq_id": "10", + "set": { + "large_community": {"num": "2:1:1 2:2:2 2:3:3 2:4:4 2:5:5"} + }, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure neighbor for route map") + input_dict_3 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [{"network": "200.50.2.0/32"}] + } + }, + "ipv6": { + "unicast": {"advertise_networks": [{"network": "1::1/128"}]} + }, + } + } + }, + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2": { + "route_maps": [ + { + "name": "RM_R4_OUT", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2": { + "route_maps": [ + { + "name": "RM_R4_OUT", + "direction": "out", + } + ] + } + } + } + } + } + }, + } + } + }, + } + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Create standard large commumity-list") + input_dict_4 = { + "r4": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "ANY", + "value": "2:1:1", + "large": True, + }, + { + "community_type": "standard", + "action": "permit", + "name": "ANY", + "value": "2:2:1", + "large": True, + }, + { + "community_type": "standard", + "action": "permit", + "name": "ANY", + "value": "2:3:1", + "large": True, + }, + { + "community_type": "standard", + "action": "permit", + "name": "ANY", + "value": "2:4:1", + "large": True, + }, + ] + } + } + result = create_bgp_community_lists(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify BGP large community is created") + result = verify_create_community_list(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Create route map") + input_dict_5 = { + "r4": { + "route_maps": { + "RM_R4_IN": [ + { + "action": "permit", + "seq_id": "10", + "match": {"large-community-list": {"id": "ANY"}}, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_5) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure neighbor for route map") + input_dict_6 = { + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": { + "route_maps": [ + {"name": "RM_R4_IN", "direction": "in"} + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": { + "route_maps": [ + {"name": "RM_R4_IN", "direction": "in"} + ] + } + } + } + } + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_6) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify large-community-list") + dut = "r4" + input_dict_7 = {"largeCommunity": "2:1:1 2:2:2 2:3:3 2:4:4 2:5:5"} + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, dut, NETWORKS[adt], input_dict_7) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_large_community_lists_with_rmap_match_regex(request): + """ + This test is to verify large-community lists" operation in a route-map + with match RegEx statements. Match clause should pass only if the + complete string of L-comm values are matched + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + step("Create route map") + input_dict_2 = { + "r2": { + "route_maps": { + "RM_R4_OUT": [ + { + "action": "permit", + "seq_id": "10", + "set": { + "large_community": { + "num": "1:1:1 1:1:2 2:1:3 2:1:4 2:1:5", + }, + "community": {"num": "1:1 1:2 1:3 1:4 1:5"}, + }, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure neighbor for route map") + input_dict_3 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [{"network": "200.50.2.0/32"}] + } + }, + "ipv6": { + "unicast": {"advertise_networks": [{"network": "1::1/128"}]} + }, + } + } + }, + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2": { + "route_maps": [ + { + "name": "RM_R4_OUT", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2": { + "route_maps": [ + { + "name": "RM_R4_OUT", + "direction": "out", + } + ] + } + } + } + } + } + }, + } + } + }, + } + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Create standard large commumity-list") + input_dict_4 = { + "r4": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "ALL", + "value": "1:1:1 2:1:3 2:1:4 2:1:5", + "large": True, + }, + { + "community_type": "expanded", + "action": "permit", + "name": "EXP_ALL", + "value": "1:1:1 2:1:[3-5]", + "large": True, + }, + ] + } + } + result = create_bgp_community_lists(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify BGP large community is created") + result = verify_create_community_list(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Create route map") + input_dict_5 = { + "r4": { + "route_maps": { + "RM_R4_IN": [ + { + "action": "permit", + "seq_id": "10", + "match": {"large_community_list": {"id": "ALL",},}, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_5) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure neighbor for route map") + input_dict_6 = { + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": { + "route_maps": [ + {"name": "RM_R4_IN", "direction": "in"} + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": { + "route_maps": [ + {"name": "RM_R4_IN", "direction": "in"} + ] + } + } + } + } + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_6) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify large-community-list") + dut = "r4" + input_dict_7 = {"largeCommunity": "1:1:1 1:1:2 2:1:3 2:1:4 2:1:5"} + for adt in ADDR_TYPES: + result = verify_bgp_community(tgen, adt, dut, NETWORKS[adt], input_dict_7) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Delete route map reference by community-list") + input_dict_3 = {"r4": {"route_maps": ["RM_R4_IN"]}} + result = delete_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + result = verify_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Create route map") + input_dict_5 = { + "r4": { + "route_maps": { + "RM_R4_IN": [ + { + "action": "permit", + "seq_id": "20", + "match": {"large_community_list": {"id": "EXP_ALL",},}, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_5) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("clear ip bgp") + result = clear_bgp_and_verify(tgen, topo, "r4") + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verify large-community-list") + dut = "r4" + input_dict_7 = {"largeCommunity": "1:1:1 1:1:2 2:1:3 2:1:4 2:1:5"} + for adt in ADDR_TYPES: + result = verify_bgp_community( + tgen, adt, dut, NETWORKS[adt], input_dict_7, expected=False + ) + assert result is not True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_link_bw_ip/__init__.py b/tests/topotests/bgp_link_bw_ip/__init__.py new file mode 100755 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bgp_link_bw_ip/r1/bgp-route-1.json b/tests/topotests/bgp_link_bw_ip/r1/bgp-route-1.json new file mode 100644 index 0000000000..3e3c35ee08 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r1/bgp-route-1.json @@ -0,0 +1,19 @@ +{ + "prefix":"198.10.1.1\/32", + "paths":[ + { + "valid":true, + "bestpath":{ + "overall":true + }, + "extendedCommunity":{ + "string":"LB:65301:125000 (1.000 Mbps)" + }, + "nexthops":[ + { + "ip":"11.1.1.2" + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r1/bgp-route-2.json b/tests/topotests/bgp_link_bw_ip/r1/bgp-route-2.json new file mode 100644 index 0000000000..f07e89b495 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r1/bgp-route-2.json @@ -0,0 +1,32 @@ +{ + "prefix":"198.10.1.1\/32", + "paths":[ + { + "valid":true, + "multipath":true, + "extendedCommunity":{ + "string":"LB:65303:125000 (1.000 Mbps)" + }, + "nexthops":[ + { + "ip":"11.1.1.6" + } + ] + }, + { + "valid":true, + "multipath":true, + "bestpath":{ + "overall":true + }, + "extendedCommunity":{ + "string":"LB:65201:375000 (3.000 Mbps)" + }, + "nexthops":[ + { + "ip":"11.1.1.2" + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r1/bgp-route-3.json b/tests/topotests/bgp_link_bw_ip/r1/bgp-route-3.json new file mode 100644 index 0000000000..3501d12e70 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r1/bgp-route-3.json @@ -0,0 +1,32 @@ +{ + "prefix":"198.10.1.1\/32", + "paths":[ + { + "valid":true, + "multipath":true, + "extendedCommunity":{ + "string":"LB:65303:125000 (1.000 Mbps)" + }, + "nexthops":[ + { + "ip":"11.1.1.6" + } + ] + }, + { + "valid":true, + "multipath":true, + "bestpath":{ + "overall":true + }, + "extendedCommunity":{ + "string":"LB:65301:250000 (2.000 Mbps)" + }, + "nexthops":[ + { + "ip":"11.1.1.2" + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r1/bgp-route-4.json b/tests/topotests/bgp_link_bw_ip/r1/bgp-route-4.json new file mode 100644 index 0000000000..b1ed004490 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r1/bgp-route-4.json @@ -0,0 +1,32 @@ +{ + "prefix":"198.10.1.11\/32", + "paths":[ + { + "valid":true, + "multipath":true, + "extendedCommunity":{ + "string":"LB:65303:125000 (1.000 Mbps)" + }, + "nexthops":[ + { + "ip":"11.1.1.6" + } + ] + }, + { + "valid":true, + "multipath":true, + "bestpath":{ + "overall":true + }, + "extendedCommunity":{ + "string":"LB:65201:250000 (2.000 Mbps)" + }, + "nexthops":[ + { + "ip":"11.1.1.2" + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r1/bgp-route-5.json b/tests/topotests/bgp_link_bw_ip/r1/bgp-route-5.json new file mode 100644 index 0000000000..89469b8ace --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r1/bgp-route-5.json @@ -0,0 +1,29 @@ +{ + "prefix":"198.10.1.1\/32", + "paths":[ + { + "valid":true, + "multipath":true, + "nexthops":[ + { + "ip":"11.1.1.6" + } + ] + }, + { + "valid":true, + "multipath":true, + "bestpath":{ + "overall":true + }, + "extendedCommunity":{ + "string":"LB:65201:375000 (3.000 Mbps)" + }, + "nexthops":[ + { + "ip":"11.1.1.2" + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r1/bgpd.conf b/tests/topotests/bgp_link_bw_ip/r1/bgpd.conf new file mode 100644 index 0000000000..40c19062a2 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r1/bgpd.conf @@ -0,0 +1,9 @@ +hostname r1 +! +router bgp 65101 + bgp router-id 11.1.1.1 + no bgp ebgp-requires-policy + bgp bestpath as-path multipath-relax + neighbor 11.1.1.2 remote-as external + neighbor 11.1.1.6 remote-as external +! diff --git a/tests/topotests/bgp_link_bw_ip/r1/ip-route-1.json b/tests/topotests/bgp_link_bw_ip/r1/ip-route-1.json new file mode 100644 index 0000000000..3c02e2675d --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r1/ip-route-1.json @@ -0,0 +1,20 @@ +{ + "198.10.1.1\/32":[ + { + "prefix":"198.10.1.1\/32", + "selected":true, + "nexthops":[ + { + "fib":true, + "ip":"11.1.1.6", + "weight":25 + }, + { + "fib":true, + "ip":"11.1.1.2", + "weight":75 + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r1/ip-route-2.json b/tests/topotests/bgp_link_bw_ip/r1/ip-route-2.json new file mode 100644 index 0000000000..3c2d42caac --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r1/ip-route-2.json @@ -0,0 +1,20 @@ +{ + "198.10.1.1\/32":[ + { + "prefix":"198.10.1.1\/32", + "selected":true, + "nexthops":[ + { + "fib":true, + "ip":"11.1.1.6", + "weight":33 + }, + { + "fib":true, + "ip":"11.1.1.2", + "weight":66 + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r1/ip-route-3.json b/tests/topotests/bgp_link_bw_ip/r1/ip-route-3.json new file mode 100644 index 0000000000..3d80018cea --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r1/ip-route-3.json @@ -0,0 +1,20 @@ +{ + "198.10.1.11\/32":[ + { + "prefix":"198.10.1.11\/32", + "selected":true, + "nexthops":[ + { + "fib":true, + "ip":"11.1.1.6", + "weight":33 + }, + { + "fib":true, + "ip":"11.1.1.2", + "weight":66 + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r1/ip-route-4.json b/tests/topotests/bgp_link_bw_ip/r1/ip-route-4.json new file mode 100644 index 0000000000..6b757ef9ed --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r1/ip-route-4.json @@ -0,0 +1,20 @@ +{ + "198.10.1.1\/32":[ + { + "prefix":"198.10.1.1\/32", + "selected":true, + "nexthops":[ + { + "fib":true, + "ip":"11.1.1.2", + "weight":1 + }, + { + "fib":true, + "ip":"11.1.1.6", + "weight":1 + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r1/ip-route-5.json b/tests/topotests/bgp_link_bw_ip/r1/ip-route-5.json new file mode 100644 index 0000000000..641ecabf47 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r1/ip-route-5.json @@ -0,0 +1,20 @@ +{ + "198.10.1.11\/32":[ + { + "prefix":"198.10.1.11\/32", + "selected":true, + "nexthops":[ + { + "fib":true, + "ip":"11.1.1.2", + "weight":1 + }, + { + "fib":true, + "ip":"11.1.1.6", + "weight":1 + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r1/ip-route-6.json b/tests/topotests/bgp_link_bw_ip/r1/ip-route-6.json new file mode 100644 index 0000000000..6ed3f8ef55 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r1/ip-route-6.json @@ -0,0 +1,15 @@ +{ + "198.10.1.1\/32":[ + { + "prefix":"198.10.1.1\/32", + "selected":true, + "nexthops":[ + { + "fib":true, + "ip":"11.1.1.2", + "weight":100 + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r1/ip-route-7.json b/tests/topotests/bgp_link_bw_ip/r1/ip-route-7.json new file mode 100644 index 0000000000..95531d99be --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r1/ip-route-7.json @@ -0,0 +1,15 @@ +{ + "198.10.1.11\/32":[ + { + "prefix":"198.10.1.11\/32", + "selected":true, + "nexthops":[ + { + "fib":true, + "ip":"11.1.1.2", + "weight":100 + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r1/ip-route-8.json b/tests/topotests/bgp_link_bw_ip/r1/ip-route-8.json new file mode 100644 index 0000000000..beac501360 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r1/ip-route-8.json @@ -0,0 +1,20 @@ +{ + "198.10.1.1\/32":[ + { + "prefix":"198.10.1.1\/32", + "selected":true, + "nexthops":[ + { + "fib":true, + "ip":"11.1.1.6", + "weight":1 + }, + { + "fib":true, + "ip":"11.1.1.2", + "weight":100 + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r1/ip-route-9.json b/tests/topotests/bgp_link_bw_ip/r1/ip-route-9.json new file mode 100644 index 0000000000..eb27ce2633 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r1/ip-route-9.json @@ -0,0 +1,20 @@ +{ + "198.10.1.11\/32":[ + { + "prefix":"198.10.1.11\/32", + "selected":true, + "nexthops":[ + { + "fib":true, + "ip":"11.1.1.6", + "weight":1 + }, + { + "fib":true, + "ip":"11.1.1.2", + "weight":100 + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r1/v4_route.json b/tests/topotests/bgp_link_bw_ip/r1/v4_route.json new file mode 100644 index 0000000000..76c6396169 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r1/v4_route.json @@ -0,0 +1,84 @@ +{ + "10.0.1.1\/32":[ + { + "prefix":"10.0.1.1\/32", + "protocol":"ospf", + "distance":110, + "metric":10, + "table":254, + "nexthops":[ + { + "flags":9, + "ip":"0.0.0.0", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "onLink":true + } + ] + }, + { + "prefix":"10.0.1.1\/32", + "protocol":"connected", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"r1-eth0", + "active":true + } + ] + } + ], + "10.0.3.4\/32":[ + { + "prefix":"10.0.3.4\/32", + "protocol":"connected", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"r1-eth1", + "active":true + } + ] + } + ], + "10.0.20.1\/32":[ + { + "prefix":"10.0.20.1\/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "distance":110, + "metric":20, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":11, + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"r1-eth1", + "active":true, + "onLink":true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r1/zebra.conf b/tests/topotests/bgp_link_bw_ip/r1/zebra.conf new file mode 100644 index 0000000000..0fc81f9bac --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r1/zebra.conf @@ -0,0 +1,7 @@ +! +interface r1-eth0 + ip address 11.1.1.1/30 +! +interface r1-eth1 + ip address 11.1.1.5/30 +! diff --git a/tests/topotests/bgp_link_bw_ip/r10/bgpd.conf b/tests/topotests/bgp_link_bw_ip/r10/bgpd.conf new file mode 100644 index 0000000000..80588e7961 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r10/bgpd.conf @@ -0,0 +1,16 @@ +hostname r10 +! +ip prefix-list redist seq 10 permit 0.0.0.0/0 ge 32 +! +route-map redist permit 10 + match ip address prefix-list redist +! +router bgp 65354 + bgp router-id 11.1.6.2 + no bgp ebgp-requires-policy + neighbor 11.1.6.1 remote-as external + ! + address-family ipv4 unicast + redistribute connected route-map redist + ! +! diff --git a/tests/topotests/bgp_link_bw_ip/r10/zebra.conf b/tests/topotests/bgp_link_bw_ip/r10/zebra.conf new file mode 100644 index 0000000000..1a24fdaea7 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r10/zebra.conf @@ -0,0 +1,6 @@ +interface r10-eth0 + ip address 11.1.6.2/30 +! +interface r10-eth1 + ip address 50.1.1.10/32 +! diff --git a/tests/topotests/bgp_link_bw_ip/r2/bgp-route-1.json b/tests/topotests/bgp_link_bw_ip/r2/bgp-route-1.json new file mode 100644 index 0000000000..3c38689a37 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r2/bgp-route-1.json @@ -0,0 +1,19 @@ +{ + "prefix":"198.10.1.1\/32", + "paths":[ + { + "valid":true, + "bestpath":{ + "overall":true + }, + "extendedCommunity":{ + "string":"LB:65301:125000 (1.000 Mbps)" + }, + "nexthops":[ + { + "ip":"11.1.2.2" + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r2/bgp-route-2.json b/tests/topotests/bgp_link_bw_ip/r2/bgp-route-2.json new file mode 100644 index 0000000000..1895cd822e --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r2/bgp-route-2.json @@ -0,0 +1,19 @@ +{ + "prefix":"198.10.1.1\/32", + "paths":[ + { + "valid":true, + "bestpath":{ + "overall":true + }, + "extendedCommunity":{ + "string":"LB:65301:250000 (2.000 Mbps)" + }, + "nexthops":[ + { + "ip":"11.1.2.2" + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r2/bgp-route-3.json b/tests/topotests/bgp_link_bw_ip/r2/bgp-route-3.json new file mode 100644 index 0000000000..dfc4171bad --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r2/bgp-route-3.json @@ -0,0 +1,32 @@ +{ + "prefix":"198.10.1.1\/32", + "paths":[ + { + "valid":true, + "multipath":true, + "extendedCommunity":{ + "string":"LB:65302:125000 (1.000 Mbps)" + }, + "nexthops":[ + { + "ip":"11.1.2.6" + } + ] + }, + { + "valid":true, + "multipath":true, + "bestpath":{ + "overall":true + }, + "extendedCommunity":{ + "string":"LB:65301:250000 (2.000 Mbps)" + }, + "nexthops":[ + { + "ip":"11.1.2.2" + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r2/bgpd.conf b/tests/topotests/bgp_link_bw_ip/r2/bgpd.conf new file mode 100644 index 0000000000..6fec1913c8 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r2/bgpd.conf @@ -0,0 +1,10 @@ +hostname r2 +! +router bgp 65201 + bgp router-id 11.1.2.1 + bgp bestpath as-path multipath-relax + no bgp ebgp-requires-policy + neighbor 11.1.1.1 remote-as external + neighbor 11.1.2.2 remote-as external + neighbor 11.1.2.6 remote-as external +! diff --git a/tests/topotests/bgp_link_bw_ip/r2/ip-route-1.json b/tests/topotests/bgp_link_bw_ip/r2/ip-route-1.json new file mode 100644 index 0000000000..131100a684 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r2/ip-route-1.json @@ -0,0 +1,19 @@ +{ + "198.10.1.1\/32":[ + { + "prefix":"198.10.1.1\/32", + "protocol":"bgp", + "selected":true, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"11.1.2.2", + "interfaceName":"r2-eth1", + "active":true, + "weight":1 + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r2/ip-route-2.json b/tests/topotests/bgp_link_bw_ip/r2/ip-route-2.json new file mode 100644 index 0000000000..7e2fa6be25 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r2/ip-route-2.json @@ -0,0 +1,20 @@ +{ + "198.10.1.1\/32":[ + { + "prefix":"198.10.1.1\/32", + "selected":true, + "nexthops":[ + { + "fib":true, + "ip":"11.1.2.6", + "weight":33 + }, + { + "fib":true, + "ip":"11.1.2.2", + "weight":66 + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r2/ip-route-3.json b/tests/topotests/bgp_link_bw_ip/r2/ip-route-3.json new file mode 100644 index 0000000000..d0509bbd29 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r2/ip-route-3.json @@ -0,0 +1,15 @@ +{ + "198.10.1.1\/32":[ + { + "prefix":"198.10.1.1\/32", + "selected":true, + "nexthops":[ + { + "fib":true, + "ip":"11.1.2.2", + "weight":1 + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r2/zebra.conf b/tests/topotests/bgp_link_bw_ip/r2/zebra.conf new file mode 100644 index 0000000000..23573a108d --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r2/zebra.conf @@ -0,0 +1,10 @@ +! +interface r2-eth0 + ip address 11.1.1.2/30 +! +interface r2-eth1 + ip address 11.1.2.1/30 +! +interface r2-eth2 + ip address 11.1.2.5/30 +! diff --git a/tests/topotests/bgp_link_bw_ip/r3/bgpd.conf b/tests/topotests/bgp_link_bw_ip/r3/bgpd.conf new file mode 100644 index 0000000000..1c2ca88306 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r3/bgpd.conf @@ -0,0 +1,9 @@ +hostname r3 +! +router bgp 65202 + bgp router-id 11.1.3.1 + bgp bestpath as-path multipath-relax + no bgp ebgp-requires-policy + neighbor 11.1.1.5 remote-as external + neighbor 11.1.3.2 remote-as external +! diff --git a/tests/topotests/bgp_link_bw_ip/r3/zebra.conf b/tests/topotests/bgp_link_bw_ip/r3/zebra.conf new file mode 100644 index 0000000000..d667669821 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r3/zebra.conf @@ -0,0 +1,7 @@ +! +interface r3-eth0 + ip address 11.1.1.6/30 +! +interface r3-eth1 + ip address 11.1.3.1/30 +! diff --git a/tests/topotests/bgp_link_bw_ip/r4/bgp-route-1.json b/tests/topotests/bgp_link_bw_ip/r4/bgp-route-1.json new file mode 100644 index 0000000000..87d1ae0b44 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r4/bgp-route-1.json @@ -0,0 +1,23 @@ +{ + "prefix":"198.10.1.1\/32", + "paths":[ + { + "valid":true, + "multipath":true, + "nexthops":[ + { + "ip":"11.1.4.6" + } + ] + }, + { + "valid":true, + "multipath":true, + "nexthops":[ + { + "ip":"11.1.4.2" + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r4/bgpd.conf b/tests/topotests/bgp_link_bw_ip/r4/bgpd.conf new file mode 100644 index 0000000000..022a230643 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r4/bgpd.conf @@ -0,0 +1,29 @@ +! +log file bgpd.log +! +debug bgp updates +debug bgp zebra +debug bgp bestpath 198.10.1.1/32 +! +hostname r4 +! +ip prefix-list anycast_ip seq 10 permit 198.10.1.0/24 le 32 +! +route-map anycast_ip permit 10 + match ip address prefix-list anycast_ip + set extcommunity bandwidth num-multipaths +! +route-map anycast_ip permit 20 +! +router bgp 65301 + bgp router-id 11.1.4.1 + bgp bestpath as-path multipath-relax + no bgp ebgp-requires-policy + neighbor 11.1.2.1 remote-as external + neighbor 11.1.4.2 remote-as external + neighbor 11.1.4.6 remote-as external + ! + address-family ipv4 unicast + neighbor 11.1.2.1 route-map anycast_ip out + ! +! diff --git a/tests/topotests/bgp_link_bw_ip/r4/ip-route-1.json b/tests/topotests/bgp_link_bw_ip/r4/ip-route-1.json new file mode 100644 index 0000000000..a9ccf07c82 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r4/ip-route-1.json @@ -0,0 +1,21 @@ +{ + "198.10.1.1\/32":[ + { + "prefix":"198.10.1.1\/32", + "protocol":"bgp", + "selected":true, + "nexthops":[ + { + "fib":true, + "ip":"11.1.4.2", + "weight":1 + }, + { + "fib":true, + "ip":"11.1.4.6", + "weight":1 + } + ] + } + ] +} diff --git a/tests/topotests/bgp_link_bw_ip/r4/zebra.conf b/tests/topotests/bgp_link_bw_ip/r4/zebra.conf new file mode 100644 index 0000000000..ef61f7eb1b --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r4/zebra.conf @@ -0,0 +1,10 @@ +! +interface r4-eth0 + ip address 11.1.2.2/30 +! +interface r4-eth1 + ip address 11.1.4.1/30 +! +interface r4-eth2 + ip address 11.1.4.5/30 +! diff --git a/tests/topotests/bgp_link_bw_ip/r5/bgpd.conf b/tests/topotests/bgp_link_bw_ip/r5/bgpd.conf new file mode 100644 index 0000000000..fc4e3888d8 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r5/bgpd.conf @@ -0,0 +1,21 @@ +hostname r5 +! +ip prefix-list anycast_ip seq 10 permit 198.10.1.0/24 le 32 +! +route-map anycast_ip permit 10 + match ip address prefix-list anycast_ip + set extcommunity bandwidth num-multipaths +! +route-map anycast_ip permit 20 +! +router bgp 65302 + bgp router-id 11.1.5.1 + bgp bestpath as-path multipath-relax + no bgp ebgp-requires-policy + neighbor 11.1.2.5 remote-as external + neighbor 11.1.5.2 remote-as external + ! + address-family ipv4 unicast + neighbor 11.1.2.5 route-map anycast_ip out + ! +! diff --git a/tests/topotests/bgp_link_bw_ip/r5/zebra.conf b/tests/topotests/bgp_link_bw_ip/r5/zebra.conf new file mode 100644 index 0000000000..66c65964e2 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r5/zebra.conf @@ -0,0 +1,7 @@ +! +interface r5-eth0 + ip address 11.1.2.6/30 +! +interface r5-eth1 + ip address 11.1.5.1/30 +! diff --git a/tests/topotests/bgp_link_bw_ip/r6/bgpd.conf b/tests/topotests/bgp_link_bw_ip/r6/bgpd.conf new file mode 100644 index 0000000000..f08f6337f4 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r6/bgpd.conf @@ -0,0 +1,21 @@ +hostname r6 +! +ip prefix-list anycast_ip seq 10 permit 198.10.1.0/24 le 32 +! +route-map anycast_ip permit 10 + match ip address prefix-list anycast_ip + set extcommunity bandwidth num-multipaths +! +route-map anycast_ip permit 20 +! +router bgp 65303 + bgp router-id 11.1.6.1 + bgp bestpath as-path multipath-relax + no bgp ebgp-requires-policy + neighbor 11.1.3.1 remote-as external + neighbor 11.1.6.2 remote-as external + ! + address-family ipv4 unicast + neighbor 11.1.3.1 route-map anycast_ip out + ! +! diff --git a/tests/topotests/bgp_link_bw_ip/r6/zebra.conf b/tests/topotests/bgp_link_bw_ip/r6/zebra.conf new file mode 100644 index 0000000000..66ff563269 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r6/zebra.conf @@ -0,0 +1,7 @@ +! +interface r6-eth0 + ip address 11.1.3.2/30 +! +interface r6-eth1 + ip address 11.1.6.1/30 +! diff --git a/tests/topotests/bgp_link_bw_ip/r7/bgpd.conf b/tests/topotests/bgp_link_bw_ip/r7/bgpd.conf new file mode 100644 index 0000000000..98dfe2471a --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r7/bgpd.conf @@ -0,0 +1,16 @@ +hostname r7 +! +ip prefix-list redist seq 10 permit 0.0.0.0/0 ge 32 +! +route-map redist permit 10 + match ip address prefix-list redist +! +router bgp 65351 + bgp router-id 11.1.4.2 + no bgp ebgp-requires-policy + neighbor 11.1.4.1 remote-as external + ! + address-family ipv4 unicast + redistribute connected route-map redist + ! +! diff --git a/tests/topotests/bgp_link_bw_ip/r7/zebra.conf b/tests/topotests/bgp_link_bw_ip/r7/zebra.conf new file mode 100644 index 0000000000..38e36cac30 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r7/zebra.conf @@ -0,0 +1,6 @@ +interface r7-eth0 + ip address 11.1.4.2/30 +! +interface r7-eth1 + ip address 50.1.1.7/32 +! diff --git a/tests/topotests/bgp_link_bw_ip/r8/bgpd.conf b/tests/topotests/bgp_link_bw_ip/r8/bgpd.conf new file mode 100644 index 0000000000..b4ba8e8a72 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r8/bgpd.conf @@ -0,0 +1,16 @@ +hostname r8 +! +ip prefix-list redist seq 10 permit 0.0.0.0/0 ge 32 +! +route-map redist permit 10 + match ip address prefix-list redist +! +router bgp 65352 + bgp router-id 11.1.4.6 + no bgp ebgp-requires-policy + neighbor 11.1.4.5 remote-as external + ! + address-family ipv4 unicast + redistribute connected route-map redist + ! +! diff --git a/tests/topotests/bgp_link_bw_ip/r8/zebra.conf b/tests/topotests/bgp_link_bw_ip/r8/zebra.conf new file mode 100644 index 0000000000..1369e19c06 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r8/zebra.conf @@ -0,0 +1,6 @@ +interface r8-eth0 + ip address 11.1.4.6/30 +! +interface r8-eth1 + ip address 50.1.1.8/32 +! diff --git a/tests/topotests/bgp_link_bw_ip/r9/bgpd.conf b/tests/topotests/bgp_link_bw_ip/r9/bgpd.conf new file mode 100644 index 0000000000..31f971dd08 --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r9/bgpd.conf @@ -0,0 +1,16 @@ +hostname r9 +! +ip prefix-list redist seq 10 permit 0.0.0.0/0 ge 32 +! +route-map redist permit 10 + match ip address prefix-list redist +! +router bgp 65353 + bgp router-id 11.1.5.2 + no bgp ebgp-requires-policy + neighbor 11.1.5.1 remote-as external + ! + address-family ipv4 unicast + redistribute connected route-map redist + ! +! diff --git a/tests/topotests/bgp_link_bw_ip/r9/zebra.conf b/tests/topotests/bgp_link_bw_ip/r9/zebra.conf new file mode 100644 index 0000000000..c73caf3bfc --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/r9/zebra.conf @@ -0,0 +1,6 @@ +interface r9-eth0 + ip address 11.1.5.2/30 +! +interface r9-eth1 + ip address 50.1.1.9/32 +! diff --git a/tests/topotests/bgp_link_bw_ip/test_bgp_linkbw_ip.py b/tests/topotests/bgp_link_bw_ip/test_bgp_linkbw_ip.py new file mode 100755 index 0000000000..86eb2969ce --- /dev/null +++ b/tests/topotests/bgp_link_bw_ip/test_bgp_linkbw_ip.py @@ -0,0 +1,515 @@ +#!/usr/bin/env python + +# +# test_bgp_linkbw_ip.py +# +# Copyright (c) 2020 by +# Cumulus Networks, Inc +# Vivek Venkatraman +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_bgp_linkbw_ip.py: Test weighted ECMP using BGP link-bandwidth +""" + +import os +import re +import sys +from functools import partial +import pytest +import json + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, '../')) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + +""" +This topology is for validating one of the primary use cases for +weighted ECMP (a.k.a. Unequal cost multipath) using BGP link-bandwidth: +https://tools.ietf.org/html/draft-mohanty-bess-ebgp-dmz + +The topology consists of two PODs. Pod-1 consists of a spine switch +and two leaf switches, with two servers attached to the first leaf and +one to the second leaf. Pod-2 consists of one spine and one leaf, with +one server connected to the leaf. The PODs are connected by a super-spine +switch. + +Note that the use of the term "switch" above is in keeping with common +data-center terminology. These devices are all regular routers; for +this scenario, the servers are also routers as they have to announce +anycast IP (VIP) addresses via BGP. +""" + +class BgpLinkBwTopo(Topo): + "Test topology builder" + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # Create 10 routers - 1 super-spine, 2 spines, 3 leafs + # and 4 servers + routers = {} + for i in range(1, 11): + routers[i] = tgen.add_router('r{}'.format(i)) + + # Create 13 "switches" - to interconnect the above routers + switches = {} + for i in range(1, 14): + switches[i] = tgen.add_switch('s{}'.format(i)) + + # Interconnect R1 (super-spine) to R2 and R3 (the two spines) + switches[1].add_link(tgen.gears['r1']) + switches[1].add_link(tgen.gears['r2']) + switches[2].add_link(tgen.gears['r1']) + switches[2].add_link(tgen.gears['r3']) + + # Interconnect R2 (spine in pod-1) to R4 and R5 (the associated + # leaf switches) + switches[3].add_link(tgen.gears['r2']) + switches[3].add_link(tgen.gears['r4']) + switches[4].add_link(tgen.gears['r2']) + switches[4].add_link(tgen.gears['r5']) + + # Interconnect R3 (spine in pod-2) to R6 (associated leaf) + switches[5].add_link(tgen.gears['r3']) + switches[5].add_link(tgen.gears['r6']) + + # Interconnect leaf switches to servers + switches[6].add_link(tgen.gears['r4']) + switches[6].add_link(tgen.gears['r7']) + switches[7].add_link(tgen.gears['r4']) + switches[7].add_link(tgen.gears['r8']) + switches[8].add_link(tgen.gears['r5']) + switches[8].add_link(tgen.gears['r9']) + switches[9].add_link(tgen.gears['r6']) + switches[9].add_link(tgen.gears['r10']) + + # Create empty networks for the servers + switches[10].add_link(tgen.gears['r7']) + switches[11].add_link(tgen.gears['r8']) + switches[12].add_link(tgen.gears['r9']) + switches[13].add_link(tgen.gears['r10']) + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(BgpLinkBwTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + for rname, router in router_list.iteritems(): + router.load_config( + TopoRouter.RD_ZEBRA, + os.path.join(CWD, '{}/zebra.conf'.format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, + os.path.join(CWD, '{}/bgpd.conf'.format(rname)) + ) + + # Initialize all routers. + tgen.start_router() + + #tgen.mininet_cli() + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + tgen.stop_topology() + +def test_bgp_linkbw_adv(): + "Test #1: Test BGP link-bandwidth advertisement based on number of multipaths" + logger.info('\nTest #1: Test BGP link-bandwidth advertisement based on number of multipaths') + + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip('skipped because of router(s) failure') + + r1 = tgen.gears['r1'] + r2 = tgen.gears['r2'] + + # Configure anycast IP on server r7 + logger.info('Configure anycast IP on server r7') + + tgen.net['r7'].cmd('ip addr add 198.10.1.1/32 dev r7-eth1') + + # Check on spine router r2 for link-bw advertisement by leaf router r4 + logger.info('Check on spine router r2 for link-bw advertisement by leaf router r4') + + json_file = '{}/r2/bgp-route-1.json'.format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + r2, 'show bgp ipv4 uni 198.10.1.1/32 json', expected) + _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5) + assertmsg = 'JSON output mismatch on spine router r2' + assert result is None, assertmsg + + # Check on spine router r2 that default weight is used as there is no multipath + logger.info('Check on spine router r2 that default weight is used as there is no multipath') + + json_file = '{}/r2/ip-route-1.json'.format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + r2, 'show ip route 198.10.1.1/32 json', expected) + _, result = topotest.run_and_expect(test_func, None, count=50, wait=0.5) + assertmsg = 'JSON output mismatch on spine router r2' + assert result is None, assertmsg + + # Check on super-spine router r1 that link-bw has been propagated by spine router r2 + logger.info('Check on super-spine router r1 that link-bw has been propagated by spine router r2') + + json_file = '{}/r1/bgp-route-1.json'.format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + r1, 'show bgp ipv4 uni 198.10.1.1/32 json', expected) + _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5) + assertmsg = 'JSON output mismatch on super-spine router r1' + assert result is None, assertmsg + +def test_bgp_cumul_linkbw(): + "Test #2: Test cumulative link-bandwidth propagation" + logger.info('\nTest #2: Test cumulative link-bandwidth propagation') + + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip('skipped because of router(s) failure') + + r1 = tgen.gears['r1'] + r2 = tgen.gears['r2'] + r4 = tgen.gears['r4'] + + # Configure anycast IP on additional server r8 + logger.info('Configure anycast IP on server r8') + + tgen.net['r8'].cmd('ip addr add 198.10.1.1/32 dev r8-eth1') + + # Check multipath on leaf router r4 + logger.info('Check multipath on leaf router r4') + + json_file = '{}/r4/bgp-route-1.json'.format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + r4, 'show bgp ipv4 uni 198.10.1.1/32 json', expected) + _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5) + assertmsg = 'JSON output mismatch on leaf router r4' + assert result is None, assertmsg + + # Check regular ECMP is in effect on leaf router r4 + logger.info('Check regular ECMP is in effect on leaf router r4') + + json_file = '{}/r4/ip-route-1.json'.format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + r4, 'show ip route 198.10.1.1/32 json', expected) + _, result = topotest.run_and_expect(test_func, None, count=50, wait=0.5) + assertmsg = 'JSON output mismatch on leaf router r4' + assert result is None, assertmsg + + # Check on spine router r2 that leaf has propagated the cumulative link-bw based on num-multipaths + logger.info('Check on spine router r2 that leaf has propagated the cumulative link-bw based on num-multipaths') + + json_file = '{}/r2/bgp-route-2.json'.format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + r2, 'show bgp ipv4 uni 198.10.1.1/32 json', expected) + _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5) + assertmsg = 'JSON output mismatch on spine router r2' + assert result is None, assertmsg + +def test_weighted_ecmp(): + "Test #3: Test weighted ECMP - multipath with next hop weights" + logger.info('\nTest #3: Test weighted ECMP - multipath with next hop weights') + + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip('skipped because of router(s) failure') + + r1 = tgen.gears['r1'] + r2 = tgen.gears['r2'] + + # Configure anycast IP on additional server r9 + logger.info('Configure anycast IP on server r9') + + tgen.net['r9'].cmd('ip addr add 198.10.1.1/32 dev r9-eth1') + + # Check multipath on spine router r2 + logger.info('Check multipath on spine router r2') + json_file = '{}/r2/bgp-route-3.json'.format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + r2, 'show bgp ipv4 uni 198.10.1.1/32 json', expected) + _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5) + assertmsg = 'JSON output mismatch on spine router r2' + assert result is None, assertmsg + + # Check weighted ECMP is in effect on the spine router r2 + logger.info('Check weighted ECMP is in effect on the spine router r2') + + json_file = '{}/r2/ip-route-2.json'.format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + r2, 'show ip route 198.10.1.1/32 json', expected) + _, result = topotest.run_and_expect(test_func, None, count=50, wait=0.5) + assertmsg = 'JSON output mismatch on spine router r2' + assert result is None, assertmsg + + # Configure anycast IP on additional server r10 + logger.info('Configure anycast IP on server r10') + + tgen.net['r10'].cmd('ip addr add 198.10.1.1/32 dev r10-eth1') + + # Check multipath on super-spine router r1 + logger.info('Check multipath on super-spine router r1') + json_file = '{}/r1/bgp-route-2.json'.format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + r1, 'show bgp ipv4 uni 198.10.1.1/32 json', expected) + _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5) + assertmsg = 'JSON output mismatch on super-spine router r1' + assert result is None, assertmsg + + # Check weighted ECMP is in effect on the super-spine router r1 + logger.info('Check weighted ECMP is in effect on the super-spine router r1') + json_file = '{}/r1/ip-route-1.json'.format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + r1, 'show ip route 198.10.1.1/32 json', expected) + _, result = topotest.run_and_expect(test_func, None, count=50, wait=0.5) + assertmsg = 'JSON output mismatch on super-spine router r1' + assert result is None, assertmsg + +def test_weighted_ecmp_link_flap(): + "Test #4: Test weighted ECMP rebalancing upon change (link flap)" + logger.info('\nTest #4: Test weighted ECMP rebalancing upon change (link flap)') + + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip('skipped because of router(s) failure') + + r1 = tgen.gears['r1'] + r2 = tgen.gears['r2'] + + # Bring down link on server r9 + logger.info('Bring down link on server r9') + + tgen.net['r9'].cmd('ip link set dev r9-eth1 down') + + # Check spine router r2 has only one path + logger.info('Check spine router r2 has only one path') + + json_file = '{}/r2/ip-route-3.json'.format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + r2, 'show ip route 198.10.1.1/32 json', expected) + _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5) + assertmsg = 'JSON output mismatch on spine router r2' + assert result is None, assertmsg + + # Check link-bandwidth change and weighted ECMP rebalance on super-spine router r1 + logger.info('Check link-bandwidth change and weighted ECMP rebalance on super-spine router r1') + + json_file = '{}/r1/bgp-route-3.json'.format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + r1, 'show bgp ipv4 uni 198.10.1.1/32 json', expected) + _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5) + assertmsg = 'JSON output mismatch on super-spine router r1' + assert result is None, assertmsg + + json_file = '{}/r1/ip-route-2.json'.format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + r1, 'show ip route 198.10.1.1/32 json', expected) + _, result = topotest.run_and_expect(test_func, None, count=50, wait=0.5) + assertmsg = 'JSON output mismatch on super-spine router r1' + assert result is None, assertmsg + + # Bring up link on server r9 + logger.info('Bring up link on server r9') + + tgen.net['r9'].cmd('ip link set dev r9-eth1 up') + + # Check link-bandwidth change and weighted ECMP rebalance on super-spine router r1 + logger.info('Check link-bandwidth change and weighted ECMP rebalance on super-spine router r1') + + json_file = '{}/r1/bgp-route-2.json'.format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + r1, 'show bgp ipv4 uni 198.10.1.1/32 json', expected) + _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5) + assertmsg = 'JSON output mismatch on super-spine router r1' + assert result is None, assertmsg + + json_file = '{}/r1/ip-route-1.json'.format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + r1, 'show ip route 198.10.1.1/32 json', expected) + _, result = topotest.run_and_expect(test_func, None, count=50, wait=0.5) + assertmsg = 'JSON output mismatch on super-spine router r1' + assert result is None, assertmsg + +def test_weighted_ecmp_second_anycast_ip(): + "Test #5: Test weighted ECMP for a second anycast IP" + logger.info('\nTest #5: Test weighted ECMP for a second anycast IP') + + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip('skipped because of router(s) failure') + + r1 = tgen.gears['r1'] + r2 = tgen.gears['r2'] + + # Configure anycast IP on additional server r7, r9 and r10 + logger.info('Configure anycast IP on server r7, r9 and r10') + + tgen.net['r7'].cmd('ip addr add 198.10.1.11/32 dev r7-eth1') + tgen.net['r9'].cmd('ip addr add 198.10.1.11/32 dev r9-eth1') + tgen.net['r10'].cmd('ip addr add 198.10.1.11/32 dev r10-eth1') + + # Check link-bandwidth and weighted ECMP on super-spine router r1 + logger.info('Check link-bandwidth and weighted ECMP on super-spine router r1') + + json_file = '{}/r1/bgp-route-4.json'.format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + r1, 'show bgp ipv4 uni 198.10.1.11/32 json', expected) + _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5) + assertmsg = 'JSON output mismatch on super-spine router r1' + assert result is None, assertmsg + + json_file = '{}/r1/ip-route-3.json'.format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + r1, 'show ip route 198.10.1.11/32 json', expected) + _, result = topotest.run_and_expect(test_func, None, count=50, wait=0.5) + assertmsg = 'JSON output mismatch on super-spine router r1' + assert result is None, assertmsg + +def test_paths_with_and_without_linkbw(): + "Test #6: Test paths with and without link-bandwidth - receiver should resort to regular ECMP" + logger.info('\nTest #6: Test paths with and without link-bandwidth - receiver should resort to regular ECMP') + + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip('skipped because of router(s) failure') + + r1 = tgen.gears['r1'] + + # Configure leaf router r6 to not advertise any link-bandwidth + logger.info('Configure leaf router r6 to not advertise any link-bandwidth') + + tgen.net['r6'].cmd('vtysh -c \"conf t\" -c \"router bgp 65303\" -c \"address-family ipv4 unicast\" -c \"no neighbor 11.1.3.1 route-map anycast_ip out\"') + + # Check link-bandwidth change on super-spine router r1 + logger.info('Check link-bandwidth change on super-spine router r1') + + json_file = '{}/r1/bgp-route-5.json'.format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + r1, 'show bgp ipv4 uni 198.10.1.1/32 json', expected) + _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5) + assertmsg = 'JSON output mismatch on super-spine router r1' + assert result is None, assertmsg + + # Check super-spine router r1 resorts to regular ECMP + logger.info('Check super-spine router r1 resorts to regular ECMP') + + json_file = '{}/r1/ip-route-4.json'.format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + r1, 'show ip route 198.10.1.1/32 json', expected) + _, result = topotest.run_and_expect(test_func, None, count=50, wait=0.5) + assertmsg = 'JSON output mismatch on super-spine router r1' + assert result is None, assertmsg + + json_file = '{}/r1/ip-route-5.json'.format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + r1, 'show ip route 198.10.1.11/32 json', expected) + _, result = topotest.run_and_expect(test_func, None, count=50, wait=0.5) + assertmsg = 'JSON output mismatch on super-spine router r1' + assert result is None, assertmsg + +def test_linkbw_handling_options(): + "Test #7: Test different options for processing link-bandwidth on the receiver" + logger.info('\nTest #7: Test different options for processing link-bandwidth on the receiver') + + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip('skipped because of router(s) failure') + + r1 = tgen.gears['r1'] + + # Configure super-spine r1 to skip multipaths without link-bandwidth + logger.info('Configure super-spine r1 to skip multipaths without link-bandwidth') + + tgen.net['r1'].cmd('vtysh -c \"conf t\" -c \"router bgp 65101\" -c \"bgp bestpath bandwidth skip-missing\"') + + # Check super-spine router r1 resorts to only one path as other path is skipped + logger.info('Check super-spine router r1 resorts to only one path as other path is skipped') + + json_file = '{}/r1/ip-route-6.json'.format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + r1, 'show ip route 198.10.1.1/32 json', expected) + _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5) + assertmsg = 'JSON output mismatch on super-spine router r1' + assert result is None, assertmsg + + json_file = '{}/r1/ip-route-7.json'.format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + r1, 'show ip route 198.10.1.11/32 json', expected) + _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5) + assertmsg = 'JSON output mismatch on super-spine router r1' + assert result is None, assertmsg + + # Configure super-spine r1 to use default-weight for multipaths without link-bandwidth + logger.info('Configure super-spine r1 to use default-weight for multipaths without link-bandwidth') + + tgen.net['r1'].cmd('vtysh -c \"conf t\" -c \"router bgp 65101\" -c \"bgp bestpath bandwidth default-weight-for-missing\"') + + # Check super-spine router r1 uses ECMP with weight 1 for path without link-bandwidth + logger.info('Check super-spine router r1 uses ECMP with weight 1 for path without link-bandwidth') + + json_file = '{}/r1/ip-route-8.json'.format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + r1, 'show ip route 198.10.1.1/32 json', expected) + _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5) + assertmsg = 'JSON output mismatch on super-spine router r1' + assert result is None, assertmsg + + json_file = '{}/r1/ip-route-9.json'.format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + r1, 'show ip route 198.10.1.11/32 json', expected) + _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5) + assertmsg = 'JSON output mismatch on super-spine router r1' + assert result is None, assertmsg + +if __name__ == '__main__': + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_local_as_private_remove/r1/bgpd.conf b/tests/topotests/bgp_local_as_private_remove/r1/bgpd.conf index e2f034453f..6f8fcd753d 100644 --- a/tests/topotests/bgp_local_as_private_remove/r1/bgpd.conf +++ b/tests/topotests/bgp_local_as_private_remove/r1/bgpd.conf @@ -1,4 +1,5 @@ router bgp 65000 + no bgp ebgp-requires-policy neighbor 192.168.255.2 remote-as 1000 neighbor 192.168.255.2 local-as 500 address-family ipv4 unicast diff --git a/tests/topotests/bgp_local_as_private_remove/r2/bgpd.conf b/tests/topotests/bgp_local_as_private_remove/r2/bgpd.conf index 0549697ff0..27427a9aaa 100644 --- a/tests/topotests/bgp_local_as_private_remove/r2/bgpd.conf +++ b/tests/topotests/bgp_local_as_private_remove/r2/bgpd.conf @@ -1,2 +1,3 @@ router bgp 1000 + no bgp ebgp-requires-policy neighbor 192.168.255.1 remote-as 500 diff --git a/tests/topotests/bgp_local_as_private_remove/r3/bgpd.conf b/tests/topotests/bgp_local_as_private_remove/r3/bgpd.conf index 4e57f71c48..f2050ddfdb 100644 --- a/tests/topotests/bgp_local_as_private_remove/r3/bgpd.conf +++ b/tests/topotests/bgp_local_as_private_remove/r3/bgpd.conf @@ -1,4 +1,5 @@ router bgp 3000 + no bgp ebgp-requires-policy neighbor 192.168.255.2 remote-as 1000 neighbor 192.168.255.2 local-as 500 address-family ipv4 unicast diff --git a/tests/topotests/bgp_local_as_private_remove/r4/bgpd.conf b/tests/topotests/bgp_local_as_private_remove/r4/bgpd.conf index 0549697ff0..27427a9aaa 100644 --- a/tests/topotests/bgp_local_as_private_remove/r4/bgpd.conf +++ b/tests/topotests/bgp_local_as_private_remove/r4/bgpd.conf @@ -1,2 +1,3 @@ router bgp 1000 + no bgp ebgp-requires-policy neighbor 192.168.255.1 remote-as 500 diff --git a/tests/topotests/bgp_local_as_private_remove/test_bgp_local_as_private_remove.py b/tests/topotests/bgp_local_as_private_remove/test_bgp_local_as_private_remove.py index da4b67b087..56bb14411a 100644 --- a/tests/topotests/bgp_local_as_private_remove/test_bgp_local_as_private_remove.py +++ b/tests/topotests/bgp_local_as_private_remove/test_bgp_local_as_private_remove.py @@ -35,7 +35,7 @@ import pytest CWD = os.path.dirname(os.path.realpath(__file__)) -sys.path.append(os.path.join(CWD, '../')) +sys.path.append(os.path.join(CWD, "../")) # pylint: disable=C0413 from lib import topotest @@ -43,20 +43,22 @@ from lib.topolog import logger from mininet.topo import Topo + class TemplateTopo(Topo): def build(self, *_args, **_opts): tgen = get_topogen(self) for routern in range(1, 5): - tgen.add_router('r{}'.format(routern)) + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) - switch = tgen.add_switch('s1') - switch.add_link(tgen.gears['r1']) - switch.add_link(tgen.gears['r2']) + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["r4"]) - switch = tgen.add_switch('s2') - switch.add_link(tgen.gears['r3']) - switch.add_link(tgen.gears['r4']) def setup_module(mod): tgen = Topogen(TemplateTopo, mod.__name__) @@ -66,20 +68,20 @@ def setup_module(mod): for i, (rname, router) in enumerate(router_list.iteritems(), 1): router.load_config( - TopoRouter.RD_ZEBRA, - os.path.join(CWD, '{}/zebra.conf'.format(rname)) + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) ) router.load_config( - TopoRouter.RD_BGP, - os.path.join(CWD, '{}/bgpd.conf'.format(rname)) + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) ) tgen.start_router() + def teardown_module(mod): tgen = get_topogen() tgen.stop_topology() + def test_bgp_remove_private_as(): tgen = get_topogen() @@ -88,24 +90,29 @@ def test_bgp_remove_private_as(): def _bgp_converge(router): while True: - output = json.loads(tgen.gears[router].vtysh_cmd("show ip bgp neighbor 192.168.255.1 json")) - if output['192.168.255.1']['bgpState'] == 'Established': + output = json.loads( + tgen.gears[router].vtysh_cmd("show ip bgp neighbor 192.168.255.1 json") + ) + if output["192.168.255.1"]["bgpState"] == "Established": time.sleep(1) return True def _bgp_as_path(router): - output = json.loads(tgen.gears[router].vtysh_cmd("show ip bgp 172.16.255.254/32 json")) - if output['prefix'] == '172.16.255.254/32': - return output['paths'][0]['aspath']['segments'][0]['list'] + output = json.loads( + tgen.gears[router].vtysh_cmd("show ip bgp 172.16.255.254/32 json") + ) + if output["prefix"] == "172.16.255.254/32": + return output["paths"][0]["aspath"]["segments"][0]["list"] + + if _bgp_converge("r2"): + assert len(_bgp_as_path("r2")) == 1 + assert 65000 not in _bgp_as_path("r2") - if _bgp_converge('r2'): - assert len(_bgp_as_path('r2')) == 1 - assert 65000 not in _bgp_as_path('r2') + if _bgp_converge("r4"): + assert len(_bgp_as_path("r4")) == 2 + assert 3000 in _bgp_as_path("r4") - if _bgp_converge('r4'): - assert len(_bgp_as_path('r4')) == 2 - assert 3000 in _bgp_as_path('r4') -if __name__ == '__main__': +if __name__ == "__main__": args = ["-s"] + sys.argv[1:] sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_maximum_prefix_invalid_update/r1/bgpd.conf b/tests/topotests/bgp_maximum_prefix_invalid_update/r1/bgpd.conf index 235b42b3d5..f0df56e947 100644 --- a/tests/topotests/bgp_maximum_prefix_invalid_update/r1/bgpd.conf +++ b/tests/topotests/bgp_maximum_prefix_invalid_update/r1/bgpd.conf @@ -1,4 +1,5 @@ router bgp 65000 + no bgp ebgp-requires-policy neighbor 192.168.255.2 remote-as 65001 address-family ipv4 unicast redistribute connected diff --git a/tests/topotests/bgp_maximum_prefix_invalid_update/r2/bgpd.conf b/tests/topotests/bgp_maximum_prefix_invalid_update/r2/bgpd.conf index e016284159..ef50dd0d7f 100644 --- a/tests/topotests/bgp_maximum_prefix_invalid_update/r2/bgpd.conf +++ b/tests/topotests/bgp_maximum_prefix_invalid_update/r2/bgpd.conf @@ -1,4 +1,5 @@ router bgp 65001 + no bgp ebgp-requires-policy neighbor 192.168.255.1 remote-as 65000 address-family ipv4 neighbor 192.168.255.1 maximum-prefix 1 diff --git a/tests/topotests/bgp_maximum_prefix_invalid_update/test_bgp_maximum_prefix_invalid_update.py b/tests/topotests/bgp_maximum_prefix_invalid_update/test_bgp_maximum_prefix_invalid_update.py index 69b8c7ca5e..5e7c6d4b63 100644 --- a/tests/topotests/bgp_maximum_prefix_invalid_update/test_bgp_maximum_prefix_invalid_update.py +++ b/tests/topotests/bgp_maximum_prefix_invalid_update/test_bgp_maximum_prefix_invalid_update.py @@ -39,7 +39,7 @@ import pytest CWD = os.path.dirname(os.path.realpath(__file__)) -sys.path.append(os.path.join(CWD, '../')) +sys.path.append(os.path.join(CWD, "../")) # pylint: disable=C0413 from lib import topotest @@ -47,16 +47,18 @@ from lib.topolog import logger from mininet.topo import Topo + class TemplateTopo(Topo): def build(self, *_args, **_opts): tgen = get_topogen(self) for routern in range(1, 3): - tgen.add_router('r{}'.format(routern)) + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) - switch = tgen.add_switch('s1') - switch.add_link(tgen.gears['r1']) - switch.add_link(tgen.gears['r2']) def setup_module(mod): tgen = Topogen(TemplateTopo, mod.__name__) @@ -66,20 +68,20 @@ def setup_module(mod): for i, (rname, router) in enumerate(router_list.iteritems(), 1): router.load_config( - TopoRouter.RD_ZEBRA, - os.path.join(CWD, '{}/zebra.conf'.format(rname)) + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) ) router.load_config( - TopoRouter.RD_BGP, - os.path.join(CWD, '{}/bgpd.conf'.format(rname)) + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) ) tgen.start_router() + def teardown_module(mod): tgen = get_topogen() tgen.stop_topology() + def test_bgp_maximum_prefix_invalid(): tgen = get_topogen() @@ -88,13 +90,16 @@ def test_bgp_maximum_prefix_invalid(): def _bgp_converge(router): while True: - output = json.loads(tgen.gears[router].vtysh_cmd("show ip bgp neighbor 192.168.255.1 json")) - if output['192.168.255.1']['connectionsEstablished'] > 3: + output = json.loads( + tgen.gears[router].vtysh_cmd("show ip bgp neighbor 192.168.255.1 json") + ) + if output["192.168.255.1"]["connectionsEstablished"] > 0: return True - time.sleep(1) def _bgp_parsing_nlri(router): - cmd_max_exceeded = 'grep "%MAXPFXEXCEED: No. of IPv4 Unicast prefix received" bgpd.log' + cmd_max_exceeded = ( + 'grep "%MAXPFXEXCEED: No. of IPv4 Unicast prefix received" bgpd.log' + ) cmdt_error_parsing_nlri = 'grep "Error parsing NLRI" bgpd.log' output_max_exceeded = tgen.gears[router].run(cmd_max_exceeded) output_error_parsing_nlri = tgen.gears[router].run(cmdt_error_parsing_nlri) @@ -104,10 +109,10 @@ def _bgp_parsing_nlri(router): return False return True + if _bgp_converge("r2"): + assert _bgp_parsing_nlri("r2") == True - if _bgp_converge('r2'): - assert _bgp_parsing_nlri('r2') == True -if __name__ == '__main__': +if __name__ == "__main__": args = ["-s"] + sys.argv[1:] sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_maximum_prefix_out/__init__.py b/tests/topotests/bgp_maximum_prefix_out/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bgp_maximum_prefix_out/r1/bgpd.conf b/tests/topotests/bgp_maximum_prefix_out/r1/bgpd.conf new file mode 100644 index 0000000000..03e3eb6e08 --- /dev/null +++ b/tests/topotests/bgp_maximum_prefix_out/r1/bgpd.conf @@ -0,0 +1,10 @@ +! +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as 65002 + address-family ipv4 unicast + redistribute connected + neighbor 192.168.255.1 maximum-prefix-out 2 + exit-address-family + ! +! diff --git a/tests/topotests/bgp_maximum_prefix_out/r1/zebra.conf b/tests/topotests/bgp_maximum_prefix_out/r1/zebra.conf new file mode 100644 index 0000000000..24162258b7 --- /dev/null +++ b/tests/topotests/bgp_maximum_prefix_out/r1/zebra.conf @@ -0,0 +1,13 @@ +! +interface lo + ip address 172.16.255.250/32 + ip address 172.16.255.251/32 + ip address 172.16.255.252/32 + ip address 172.16.255.253/32 + ip address 172.16.255.254/32 +! +interface r1-eth0 + ip address 192.168.255.2/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_maximum_prefix_out/r2/bgpd.conf b/tests/topotests/bgp_maximum_prefix_out/r2/bgpd.conf new file mode 100644 index 0000000000..2d47b2f661 --- /dev/null +++ b/tests/topotests/bgp_maximum_prefix_out/r2/bgpd.conf @@ -0,0 +1,7 @@ +! +router bgp 65002 + no bgp ebgp-requires-policy + neighbor 192.168.255.2 remote-as 65001 + exit-address-family + ! +! diff --git a/tests/topotests/bgp_maximum_prefix_out/r2/zebra.conf b/tests/topotests/bgp_maximum_prefix_out/r2/zebra.conf new file mode 100644 index 0000000000..08dd374dee --- /dev/null +++ b/tests/topotests/bgp_maximum_prefix_out/r2/zebra.conf @@ -0,0 +1,6 @@ +! +interface r2-eth0 + ip address 192.168.255.1/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_maximum_prefix_out/test_bgp_maximum_prefix_out.py b/tests/topotests/bgp_maximum_prefix_out/test_bgp_maximum_prefix_out.py new file mode 100644 index 0000000000..708684f696 --- /dev/null +++ b/tests/topotests/bgp_maximum_prefix_out/test_bgp_maximum_prefix_out.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python + +# +# test_bgp_maximum_prefix_out.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2020 by +# Donatas Abraitis +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +Test if `neighbor maximum-prefix-out ` is working +correctly. +""" + +import os +import sys +import json +import time +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from mininet.topo import Topo + + +class TemplateTopo(Topo): + def build(self, *_args, **_opts): + tgen = get_topogen(self) + + for routern in range(1, 3): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + tgen = Topogen(TemplateTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.iteritems(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_maximum_prefix_out(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r2"] + + def _bgp_converge(router): + output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.2 json")) + expected = { + "192.168.255.2": { + "bgpState": "Established", + "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 2}}, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge, router) + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + + assert result is None, 'Failed bgp convergence in "{}"'.format(router) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_multi_vrf_topo1/bgp_multi_vrf_topo1.json b/tests/topotests/bgp_multi_vrf_topo1/bgp_multi_vrf_topo1.json new file mode 100644 index 0000000000..327744d3a1 --- /dev/null +++ b/tests/topotests/bgp_multi_vrf_topo1/bgp_multi_vrf_topo1.json @@ -0,0 +1,884 @@ +{ + "address_types": ["ipv4","ipv6"], + "ipv4base": "10.0.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 30, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:db8:f::", + "v6mask": 128 + }, + "routers": { + "red1": { + "links": { + "r1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "r1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"} + }, + "vrfs":[ + { + "name": "RED_A", + "id": "1" + }, + { + "name": "RED_B", + "id": "2" + } + ], + "bgp": + [ + { + "local_as": "500", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link1": {} + } + } + } + } + } + } + }, + { + "local_as": "500", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link2": {} + } + } + } + } + } + } + } + ] + }, + "blue1": { + "links": { + "r1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"}, + "r1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"} + }, + "vrfs":[ + { + "name": "BLUE_A", + "id": "1" + }, + { + "name": "BLUE_B", + "id": "2" + } + ], + "bgp": + [ + { + "local_as": "800", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "blue1-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "blue1-link1": {} + } + } + } + } + } + } + }, + { + "local_as": "800", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "blue1-link2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "blue1-link2": {} + } + } + } + } + } + } + } + ] + }, + "r1": { + "links": { + "red1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "red1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"}, + "blue1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"}, + "blue1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"}, + "r2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "r2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"}, + "r2-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"}, + "r2-link4": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"} + }, + "vrfs":[ + { + "name": "RED_A", + "id": "1" + }, + { + "name": "RED_B", + "id": "2" + }, + { + "name": "BLUE_A", + "id": "3" + }, + { + "name": "BLUE_B", + "id": "4" + } + ], + "bgp": + [ + { + "local_as": "100", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link1": {} + } + }, + "r2": { + "dest_link": { + "r1-link1": + { "next_hop_self": true } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link1": {} + } + }, + "r2": { + "dest_link": { + "r1-link1": + { "next_hop_self": true } + } + } + } + } + } + } + }, + { + "local_as": "100", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link2": {} + } + }, + "r2": { + "dest_link": { + "r1-link2": + { "next_hop_self": true } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link2": {} + } + }, + "r2": { + "dest_link": { + "r1-link2": + { "next_hop_self": true } + } + } + } + } + } + } + }, + { + "local_as": "100", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link1": {} + } + }, + "r2": { + "dest_link": { + "r1-link3": + { "next_hop_self": true } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link1": {} + } + }, + "r2": { + "dest_link": { + "r1-link3": + { "next_hop_self": true } + } + } + } + } + } + } + }, + { + "local_as": "100", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link2": {} + } + }, + "r2": { + "dest_link": { + "r1-link4": + { "next_hop_self": true } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link2": {} + } + }, + "r2": { + "dest_link": { + "r1-link4": + { "next_hop_self": true } + } + } + } + } + } + } + } + ] + }, + "r2": { + "links": { + "r1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "r1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"}, + "r1-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"}, + "r1-link4": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"}, + "r3-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "r3-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"}, + "r3-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"}, + "r3-link4": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"} + }, + "vrfs":[ + { + "name": "RED_A", + "id": "1" + }, + { + "name": "RED_B", + "id": "2" + }, + { + "name": "BLUE_A", + "id": "3" + }, + { + "name": "BLUE_B", + "id": "4" + } + ], + "bgp": + [ + { + "local_as": "100", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {} + } + }, + "r3": { + "dest_link": { + "r2-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {} + } + }, + "r3": { + "dest_link": { + "r2-link1": {} + } + } + } + } + } + } + }, + { + "local_as": "100", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link2": {} + } + }, + "r3": { + "dest_link": { + "r2-link2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link2": {} + } + }, + "r3": { + "dest_link": { + "r2-link2": {} + } + } + } + } + } + } + }, + { + "local_as": "100", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link3": {} + } + }, + "r3": { + "dest_link": { + "r2-link3": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link3": {} + } + }, + "r3": { + "dest_link": { + "r2-link3": {} + } + } + } + } + } + } + }, + { + "local_as": "100", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link4": {} + } + }, + "r3": { + "dest_link": { + "r2-link4": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link4": {} + } + }, + "r3": { + "dest_link": { + "r2-link4": {} + } + } + } + } + } + } + } + ] + }, + "r3": { + "links": { + "r2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "r2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"}, + "r2-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"}, + "r2-link4": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"}, + "red2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "red2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"}, + "blue2-link1": {"ipv4": "auto", "ipv6": "autor3", "vrf": "BLUE_A"}, + "blue2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"} + }, + "vrfs":[ + { + "name": "RED_A", + "id": "1" + }, + { + "name": "RED_B", + "id": "2" + }, + { + "name": "BLUE_A", + "id": "3" + }, + { + "name": "BLUE_B", + "id": "4" + } + ], + "bgp": + [ + { + "local_as": "200", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": {} + } + }, + "red2": { + "dest_link": { + "r3-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": {} + } + }, + "red2": { + "dest_link": { + "r3-link1": {} + } + } + } + } + } + } + }, + { + "local_as": "200", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link2": {} + } + }, + "red2": { + "dest_link": { + "r3-link2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link2": {} + } + }, + "red2": { + "dest_link": { + "r3-link2": {} + } + } + } + } + } + } + }, + { + "local_as": "200", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link3": {} + } + }, + "blue2": { + "dest_link": { + "r3-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link3": {} + } + }, + "blue2": { + "dest_link": { + "r3-link1": {} + } + } + } + } + } + } + }, + { + "local_as": "200", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link4": {} + } + }, + "blue2": { + "dest_link": { + "r3-link2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link4": {} + } + }, + "blue2": { + "dest_link": { + "r3-link2": {} + } + } + } + } + } + } + } + ] + }, + "red2": { + "links": { + "r3-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "r3-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"} + }, + "vrfs":[ + { + "name": "RED_A", + "id": "1" + }, + { + "name": "RED_B", + "id": "2" + } + ], + "bgp": + [ + { + "local_as": "500", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "red2-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "red2-link1": {} + } + } + } + } + } + } + }, + { + "local_as": "500", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "red2-link2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "red2-link2": {} + } + } + } + } + } + } + } + ] + }, + "blue2": { + "links": { + "r3-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"}, + "r3-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"} + }, + "vrfs":[ + { + "name": "BLUE_A", + "id": "1" + }, + { + "name": "BLUE_B", + "id": "2" + } + ], + "bgp": + [ + { + "local_as": "800", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "blue2-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "blue2-link1": {} + } + } + } + } + } + } + }, + { + "local_as": "800", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "blue2-link2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "blue2-link2": {} + } + } + } + } + } + } + } + ] + } + } +} 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 new file mode 100755 index 0000000000..c15b88d371 --- /dev/null +++ b/tests/topotests/bgp_multi_vrf_topo1/test_bgp_multi_vrf_topo1.py @@ -0,0 +1,6305 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +Following tests are covered to test BGP Multi-VRF: + +FUNC_1: + Within each VRF, each address must be unambiguous on DUT. +FUNC_2: + Different VRFs can have ambiguous/overlapping + addresses on DUT. +FUNC_3: + Create static routes(IPv4+IPv6) associated to specific VRFs + and verify on DUT that same prefixes are present in corresponding + routing table. +FUNC_4_&_5: + Each VRF should be mapped with a unique VLAN on DUT + for traffic segregation, when using a single physical interface. +FUNC_6: + Advertise same set of prefixes from different VRFs + and verify on remote router that these prefixes are not + leaking to each other +FUNC_7: + Redistribute Static routes and verify on remote routers + that routes are advertised within specific VRF instance, which + those static routes belong to. +FUNC_8: + Test end to end traffic isolation based on VRF tables. +FUNC_9: + Use static routes for inter-vrf communication + (route-leaking) on DUT. +FUNC_10: + Verify intra-vrf and inter-vrf communication between + iBGP peers. +FUNC_11: + Verify intra-vrf and inter-vrf communication + between eBGP peers. +FUNC_12_a: + Configure route-maps within a VRF, to alter BGP attributes. + Verify that route-map doesn't affect any other VRF instances' + routing on DUT. +FUNC_12_b: + Configure route-maps within a VRF, to alter BGP attributes. + Verify that route-map doesn't affect any other VRF instances' + routing on DUT. +FUNC_12_c: + Configure route-maps within a VRF, to alter BGP attributes. + Verify that route-map doesn't affect any other VRF instances' + routing on DUT. +FUNC_12_d: + Configure route-maps within a VRF, to alter BGP attributes. + Verify that route-map doesn't affect any other VRF instances' + routing on DUT. +FUNC_12_e: + Configure route-maps within a VRF, to alter BGP attributes. + Verify that route-map doesn't affect any other VRF instances' + routing on DUT. +FUNC_12_f: + Configure route-maps within a VRF, to alter BGP attributes. + Verify that route-map doesn't affect any other VRF instances' + routing on DUT. +FUNC_13: + Configure a route-map on DUT to match traffic based + on a VRF interfaces. +FUNC_14: + Test VRF-lite with Static+BGP originated routes. +FUNC_15: + Configure prefix-lists on DUT and apply to BGP peers to + permit/deny prefixes. +FUNC_16_1: + Configure a route-map on DUT to match traffic based various + match/set causes. +FUNC_16_2: + Configure a route-map on DUT to match traffic based various + match/set causes. +FUNC_16_3: + Configure a route-map on DUT to match traffic based various + match/set causes. +""" + +import os +import sys +import json +import time +import pytest +from copy import deepcopy + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# Required to instantiate the topology builder class. + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from mininet.topo import Topo + +from lib.common_config import ( + step, + verify_rib, + start_topology, + write_test_header, + check_address_types, + write_test_footer, + reset_config_on_routers, + create_route_maps, + create_static_routes, + create_prefix_lists, + create_interface_in_kernel, + kill_mininet_routers_process, + create_bgp_community_lists, + check_router_status, + apply_raw_config, + required_linux_kernel_version +) + +from lib.topolog import logger +from lib.bgp import ( + clear_bgp, + verify_bgp_rib, + create_router_bgp, + verify_bgp_community, + verify_bgp_convergence, + verify_best_path_as_per_bgp_attribute, +) +from lib.topojson import build_topo_from_json, build_config_from_json + +# Reading the data from JSON File for topology creation +jsonFile = "{}/bgp_multi_vrf_topo1.json".format(CWD) + +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +# Global variables +NETWORK1_1 = {"ipv4": "1.1.1.1/32", "ipv6": "1::1/128"} +NETWORK1_2 = {"ipv4": "1.1.1.2/32", "ipv6": "1::2/128"} +NETWORK2_1 = {"ipv4": "2.1.1.1/32", "ipv6": "2::1/128"} +NETWORK2_2 = {"ipv4": "2.1.1.2/32", "ipv6": "2::2/128"} +NETWORK3_1 = {"ipv4": "3.1.1.1/32", "ipv6": "3::1/128"} +NETWORK3_2 = {"ipv4": "3.1.1.2/32", "ipv6": "3::2/128"} +NETWORK4_1 = {"ipv4": "4.1.1.1/32", "ipv6": "4::1/128"} +NETWORK4_2 = {"ipv4": "4.1.1.2/32", "ipv6": "4::2/128"} +NETWORK5_1 = {"ipv4": "5.1.1.1/32", "ipv6": "5::1/128"} +NETWORK5_2 = {"ipv4": "5.1.1.2/32", "ipv6": "5::2/128"} +NETWORK6_1 = {"ipv4": "6.1.1.1/32", "ipv6": "6::1/128"} +NETWORK6_2 = {"ipv4": "6.1.1.2/32", "ipv6": "6::2/128"} +NETWORK7_1 = {"ipv4": "7.1.1.1/32", "ipv6": "7::1/128"} +NETWORK7_2 = {"ipv4": "7.1.1.2/32", "ipv6": "7::2/128"} +NETWORK8_1 = {"ipv4": "8.1.1.1/32", "ipv6": "8::1/128"} +NETWORK8_2 = {"ipv4": "8.1.1.2/32", "ipv6": "8::2/128"} + +NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"} + +LOOPBACK_1 = { + "ipv4": "10.10.10.10/32", + "ipv6": "10::10:10/128", + "ipv4_mask": "255.255.255.255", + "ipv6_mask": None, +} +LOOPBACK_2 = { + "ipv4": "20.20.20.20/32", + "ipv6": "20::20:20/128", + "ipv4_mask": "255.255.255.255", + "ipv6_mask": None, +} + + +class CreateTopo(Topo): + """ + Test BasicTopo - topology 1 + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + """Build function""" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version('4.15') + if result is not True: + pytest.skip("Kernel requirements are not met") + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(CreateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # Kill stale mininet routers and process + kill_mininet_routers_process(tgen) + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + global BGP_CONVERGENCE + global ADDR_TYPES + ADDR_TYPES = check_address_types() + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# +# Testcases +# +##################################################### + + +def test_address_unambiguous_within_each_vrf_p0(request): + """ + FUNC_1: + Within each VRF, each address must be unambiguous on DUT. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step("Configure a set of static routes(IPv4+IPv6) in " "RED_A on router RED-1") + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": NETWORK1_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + } + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Configure the same static routes(IPv4+IPv6) with a TAG value" + "of 500 in RED_A on router RED-1" + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "red1": { + "static_routes": [ + { + "network": NETWORK1_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "tag": 500, + "vrf": "RED_A", + } + ] + } + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = { + "red1": { + "bgp": { + "local_as": "500", + "vrf": "RED_A", + "address_family": { + "ipv4": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + }, + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that static routes(IPv4+IPv6) is overridden and doesn't" + " have duplicate entries within VRF RED_A on router RED-1" + ) + + for addr_type in ADDR_TYPES: + dut = "red1" + input_dict_2 = { + "red1": { + "static_routes": [ + { + "network": NETWORK1_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "tag": 500, + "vrf": "RED_A", + } + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_2, tag=500) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Make sure routes are not present in global routing table") + + for addr_type in ADDR_TYPES: + dut = "red1" + input_dict_2 = { + "red1": { + "static_routes": [ + { + "network": NETWORK1_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + } + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_2, expected=False) + assert result is not True, ( + "Testcase {} : Failed \n Expected Behaviour: Routes are not " + "present on Global Routing table \n Error {}".format(tc_name, result) + ) + + write_test_footer(tc_name) + + +def test_ambiguous_overlapping_addresses_in_different_vrfs_p0(request): + """ + FUNC_2: + Different VRFs can have ambiguous/overlapping + addresses on DUT. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step("Configure a set of static routes(IPv4+IPv6) in vrf RED_A" "on router RED-1") + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + } + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Configure the same static routes(IPv4+IPv6) with a" + " TAG value of 500 in vrf RED_B on router RED-1" + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "tag": 500, + "vrf": "RED_B", + } + ] + } + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = {} + for dut in ["red1"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + if "red" in dut: + VRFS = ["RED_A", "RED_B"] + AS_NUM = [500, 500] + elif "blue" in dut: + VRFS = ["BLUE_A", "BLUE_B"] + AS_NUM = [800, 800] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify that RED_A has the static routes without any" " TAG value") + + for addr_type in ADDR_TYPES: + dut = "red1" + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + } + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, input_dict_1, tag=500, expected=False) + assert result is not True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + logger.info("Expected Behavior: {}".format(result)) + + step( + "Verify that RED_B has the same routes with TAG value " + "500 on same device RED-1" + ) + + for addr_type in ADDR_TYPES: + dut = "red1" + input_dict_2 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "tag": 500, + "vrf": "RED_B", + } + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_2, tag=500) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Make sure routes are not present in global routing table") + + for addr_type in ADDR_TYPES: + dut = "red1" + input_dict_2 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + } + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_2, expected=False) + assert result is not True, ( + "Testcase {} : Failed \n Expected Behaviour: Routes are not " + "present on Global Routing table \n Error {}".format(tc_name, result) + ) + + write_test_footer(tc_name) + + +def test_static_routes_associated_to_specific_vrfs_p0(request): + """ + FUNC_3: + Create static routes(IPv4+IPv6) associated to specific VRFs + and verify on DUT that same prefixes are present in corresponding + routing table. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step( + "Configure a set of unique static(IPv4+IPv6) routes in vrf" + " RED_A on router RED-1" + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Configure set of unique static routes(IPv4+IPv6) in vrf " + "RED_B on router RED-1" + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = {} + for dut in ["red1", "blue1"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + if "red" in dut: + VRFS = ["RED_A", "RED_B"] + AS_NUM = [500, 500] + elif "blue" in dut: + VRFS = ["BLUE_A", "BLUE_B"] + AS_NUM = [800, 800] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that static routes 1.x.x.x/32 and 1::x/128 appear " "in VRF RED_A table" + ) + step( + "Verify that static routes 2.x.x.x/32 and 2::x/128 appear " "in VRF RED_B table" + ) + + for addr_type in ADDR_TYPES: + dut = "red1" + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step( + "Verify that static routes 1.x.x.x/32 and 1::x/128 appear " "in VRF BLUE_A table" + ) + step( + "Verify that static routes 2.x.x.x/32 and 2::x/128 appear " "in VRF BLUE_B table" + ) + + for addr_type in ADDR_TYPES: + dut = "blue1" + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Make sure routes are not present in global routing table") + + for addr_type in ADDR_TYPES: + dut = "blue1" + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_2, expected=False) + assert result is not True, ( + "Testcase {} : Failed \n Expected Behaviour: Routes are not " + "present on Global Routing table \n Error {}".format(tc_name, result) + ) + + write_test_footer(tc_name) + + +def test_vrf_with_unique_physical_interface_p0(request): + """ + FUNC_4_&_5: + Each VRF should be mapped with a unique VLAN on DUT + for traffic segregation, when using a single physical interface. + + Each VRF should be mapped to a unique physical + interface(without VLAN tagging) on DUT for traffic segregation. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step( + "R1 is receiving routes in 4 VRFs instances " + "(RED_A, RED_B, BLUE_A, BLUE_B) from RED_1 and BLUE_1." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise a set of unique BGP prefixes(IPv4+IPv6) from " + "routers RED_1 & BLUE_1 in each VRF using static redistribution" + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = {} + for dut in ["red1", "blue1"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + if "red" in dut: + VRFS = ["RED_A", "RED_B"] + AS_NUM = [500, 500] + elif "blue" in dut: + VRFS = ["BLUE_A", "BLUE_B"] + AS_NUM = [800, 800] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Each VRF table on R2 should maintain it's associated " + "routes and and accordingly install in zebra" + ) + + for addr_type in ADDR_TYPES: + dut = "r2" + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_prefixes_leaking_p0(request): + """ + FUNC_6: + Advertise same set of prefixes from different VRFs + and verify on remote router that these prefixes are not + leaking to each other + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step("Configure a set of static routes(IPv4+IPv6) in vrf " "RED_A on router RED-1") + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + } + ] + }, + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + } + ] + }, + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Configure a set of static routes(IPv4+IPv6) in vrf " "BLUE_A on router BLUE-1" + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + } + ] + }, + "blue1": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + } + ] + }, + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Configure the same set of static routes with a " + "metric value of 123 in vrf RED_B on router RED-1" + ) + step( + "Configure the same set of static routes with a " + "metric value of 123 in vrf BLUE_B on router BLUE-1" + ) + + input_dict_3 = { + "red1": { + "bgp": [ + { + "local_as": "500", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + }, + { + "local_as": "500", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + { + "redist_type": "static", + "attribute": {"metric": 123}, + } + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + { + "redist_type": "static", + "attribute": {"metric": 123}, + } + ] + } + }, + }, + }, + ] + }, + "blue1": { + "bgp": [ + { + "local_as": "800", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + }, + { + "local_as": "800", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + { + "redist_type": "static", + "attribute": {"metric": 123}, + } + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + { + "redist_type": "static", + "attribute": {"metric": 123}, + } + ] + } + }, + }, + }, + ] + }, + } + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify on R1 that RED_A doesn't receive any static " + "route with metric value 123" + ) + + for addr_type in ADDR_TYPES: + dut = "r1" + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + } + ] + }, + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + } + ] + }, + } + + input_dict_2 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + } + ] + }, + "blue1": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + } + ] + }, + } + + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib( + tgen, addr_type, dut, input_dict_1, metric=123, expected=False + ) + assert result is not True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + logger.info("Expected Behavior: {}".format(result)) + + result = verify_rib(tgen, addr_type, dut, input_dict_2, metric=123) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib( + tgen, addr_type, dut, input_dict_2, metric=0, expected=False + ) + assert result is not True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + logger.info("Expected Behavior: {}".format(result)) + + write_test_footer(tc_name) + + +def test_static_routes_advertised_within_specific_vrf_p0(request): + """ + FUNC_7: + Redistribute Static routes and verify on remote routers + that routes are advertised within specific VRF instance, which + those static routes belong to. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step( + "Advertise a set of unique BGP prefixes(IPv4+IPv6) " + "through static redistribution into VRF RED_A and RED_B" + " from router RED-1." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise same as above set of BGP prefixes(IPv4+IPv6) " + "through static redistribution into VRF BLUE_A and BLUE_B" + " from router BLUE-1." + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = {} + for dut in ["red1", "blue1"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + if "red" in dut: + VRFS = ["RED_A", "RED_B"] + AS_NUM = [500, 500] + elif "blue" in dut: + VRFS = ["BLUE_A", "BLUE_B"] + AS_NUM = [800, 800] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that static routes are installed into vrfs RED_A" + "and RED_B tables only, not in global routing table of RED_1" + ) + + for addr_type in ADDR_TYPES: + dut = "red1" + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_1, protocol="static") + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step( + "Verify that static routes are installed into vrfs BLUE_A and" + "BLUE_B tables only, not in global routing table of BLUE_1." + ) + + for addr_type in ADDR_TYPES: + dut = "blue1" + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_2, protocol="static") + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step( + "Verify on router R1, that each set of prefixes is received" + " into associated vrf tables only." + ) + + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} : Failed \n Error {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + dut = "r1" + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_end_to_end_traffic_isolation_p0(request): + """ + FUNC_8: + Test end to end traffic isolation based on VRF tables. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step( + "Advertise unique BGP prefixes(IPv4+IPv6) from RED_1 " + "in vrf instances(RED_A and RED_B)." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise unique BGP prefixes(IPv4+IPv6) from from BLUE_1 in" + " vrf instances(BLUE_A and BLUE_B)." + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = {} + for dut in ["red1", "blue1"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + if "red" in dut: + VRFS = ["RED_A", "RED_B"] + AS_NUM = [500, 500] + elif "blue" in dut: + VRFS = ["BLUE_A", "BLUE_B"] + AS_NUM = [800, 800] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Use below commands to send prefixes with as-path prepend" + "VRF BLUE_A and BLUE_B from router BLUE-1." + ) + + for addr_type in ADDR_TYPES: + input_dict_4 = { + "blue1": { + "route_maps": { + "ASP_{}".format(addr_type): [ + { + "action": "permit", + "set": {"path": {"as_num": 123, "as_action": "prepend"}}, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Apply route-map to neighbours") + + input_dict_5 = { + "blue1": { + "bgp": [ + { + "local_as": "800", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "blue1-link1": { + "route_maps": [ + { + "name": "ASP_ipv4", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "blue1-link1": { + "route_maps": [ + { + "name": "ASP_ipv6", + "direction": "out", + } + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "800", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "blue1-link2": { + "route_maps": [ + { + "name": "ASP_ipv4", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "blue1-link2": { + "route_maps": [ + { + "name": "ASP_ipv6", + "direction": "out", + } + ] + } + } + } + } + } + }, + }, + }, + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_5) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify on R1 that BLUE_A and BLUE_B VRFs are receiving the" + " prefixes with as-path 123 prepended." + ) + + for addr_type in ADDR_TYPES: + dut = "r1" + input_dict_6 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_6) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, input_dict_6) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + dut = "r1" + input_dict_7 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + { + "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_7) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, input_dict_7) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step( + "Use below commands to send prefixes with as-path prepend VRF" + " BLUE_A and BLUE_B from router BLUE-1." + ) + + input_dict_6 = { + "red2": { + "bgp": [ + { + "local_as": "500", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "red2-link1": { + "allowas-in": {"number_occurences": 2} + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "red2-link1": { + "allowas-in": {"number_occurences": 2} + } + } + } + } + } + }, + }, + }, + { + "local_as": "500", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "red2-link2": { + "allowas-in": {"number_occurences": 2} + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "red2-link2": { + "allowas-in": {"number_occurences": 2} + } + } + } + } + } + }, + }, + }, + ] + }, + "blue2": { + "bgp": [ + { + "local_as": "800", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "blue2-link1": { + "allowas-in": {"number_occurences": 2} + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "blue2-link1": { + "allowas-in": {"number_occurences": 2} + } + } + } + } + } + }, + }, + }, + { + "local_as": "800", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "blue2-link2": { + "allowas-in": {"number_occurences": 2} + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "blue2-link2": { + "allowas-in": {"number_occurences": 2} + } + } + } + } + } + }, + }, + }, + ] + }, + } + + result = create_router_bgp(tgen, topo, input_dict_6) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify that router RED-2 receives the prefixes in respective" " VRF tables.") + + for addr_type in ADDR_TYPES: + dut = "red2" + input_dict_6 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_6) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_6) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + dut = "blue2" + input_dict_7 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_7) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_7) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_static_routes_for_inter_vrf_route_leaking_p0(request): + """ + FUNC_9: + Use static routes for inter-vrf communication + (route-leaking) on DUT. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step( + "Configure unique loopback interfaces in VRFs RED_A " + "and RED_B on router RED_1." + ) + + for addr_type in ADDR_TYPES: + create_interface_in_kernel( + tgen, + "red1", + "loopback1", + LOOPBACK_1[addr_type], + "RED_A", + LOOPBACK_1["{}_mask".format(addr_type)], + ) + create_interface_in_kernel( + tgen, + "red1", + "loopback2", + LOOPBACK_2[addr_type], + "RED_B", + LOOPBACK_2["{}_mask".format(addr_type)], + ) + + step( + "Create a static routes in vrf RED_B on router RED_1 pointing" + " next-hop as interface's IP in vrf RED_A" + ) + + intf_red1_r11 = topo["routers"]["red1"]["links"]["r1-link1"]["interface"] + intf_red1_r10 = topo["routers"]["red1"]["links"]["r1-link2"]["interface"] + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": LOOPBACK_1[addr_type], + "interface": intf_red1_r10, + "nexthop_vrf": "RED_B", + "vrf": "RED_A", + }, + { + "network": LOOPBACK_2[addr_type], + "interface": intf_red1_r11, + "nexthop_vrf": "RED_A", + "vrf": "RED_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = {} + for dut in ["red1"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + if "red" in dut: + VRFS = ["RED_A", "RED_B"] + AS_NUM = [500, 500] + elif "blue" in dut: + VRFS = ["BLUE_A", "BLUE_B"] + AS_NUM = [800, 800] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that static routes are installed into vrfs RED_A" + "and RED_B tables only, not in global routing table of RED_1" + ) + for addr_type in ADDR_TYPES: + dut = "red1" + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": LOOPBACK_1[addr_type], + "interface": intf_red1_r10, + "nexthop_vrf": "RED_B", + "vrf": "RED_A", + }, + { + "network": LOOPBACK_2[addr_type], + "interface": intf_red1_r11, + "nexthop_vrf": "RED_A", + "vrf": "RED_B", + }, + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_1, protocol="static") + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_inter_vrf_and_intra_vrf_communication_iBGP_p0(request): + """ + FUNC_10: + Verify intra-vrf and inter-vrf communication between + iBGP peers. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step( + "Configure unique loopback IP(IPv4+IPv6) in vrf RED_A on router" + " R1 and advertise it in BGP process using redistribute " + "connected command." + ) + + for addr_type in ADDR_TYPES: + create_interface_in_kernel( + tgen, + "r1", + "loopback1", + LOOPBACK_1[addr_type], + "RED_A", + LOOPBACK_1["{}_mask".format(addr_type)], + ) + + create_interface_in_kernel( + tgen, + "r1", + "loopback2", + LOOPBACK_2[addr_type], + "BLUE_A", + LOOPBACK_2["{}_mask".format(addr_type)], + ) + + step( + "Create a static routes in vrf RED_B on router RED_1 pointing" + " next-hop as interface's IP in vrf RED_A" + ) + + intf_r2_r12 = topo["routers"]["r2"]["links"]["r1-link1"]["interface"] + intf_r2_r10 = topo["routers"]["r2"]["links"]["r1-link3"]["interface"] + for addr_type in ADDR_TYPES: + input_dict_1 = { + "r2": { + "static_routes": [ + { + "network": LOOPBACK_2[addr_type], + "interface": intf_r2_r10, + "nexthop_vrf": "BLUE_A", + "vrf": "RED_A", + }, + { + "network": LOOPBACK_1[addr_type], + "interface": intf_r2_r12, + "nexthop_vrf": "RED_A", + "vrf": "BLUE_A", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute connected..") + + input_dict_3 = {} + for dut in ["r1"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + VRFS = ["RED_A", "BLUE_A"] + AS_NUM = [100, 100] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "connected"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "connected"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Redistribute static..") + + input_dict_3 = {} + for dut in ["r2"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + VRFS = ["RED_A", "BLUE_A"] + AS_NUM = [100, 100] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that static routes are installed into vrfs RED_A" + "and RED_B tables only, not in global routing table of RED_1" + ) + + for addr_type in ADDR_TYPES: + dut = "r2" + input_dict = { + "r2": { + "static_routes": [ + { + "network": LOOPBACK_2[addr_type], + "interface": intf_r2_r10, + "nexthop_vrf": "BLUE_A", + "vrf": "RED_A", + }, + { + "network": LOOPBACK_1[addr_type], + "interface": intf_r2_r12, + "nexthop_vrf": "RED_A", + "vrf": "BLUE_A", + }, + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_inter_vrf_and_intra_vrf_communication_eBGP_p0(request): + """ + FUNC_11: + Verify intra-vrf and inter-vrf communication + between eBGP peers. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step( + "Configure unique loopback IP(IPv4+IPv6) in vrf RED_A on router" + " R2 and advertise it in BGP process using redistribute " + "connected command." + ) + + step( + "Configure unique loopback IP(IPv4+IPv6) in vrf BLUE_A on router" + " R2 and advertise it in BGP process using redistribute " + "connected command." + ) + + for addr_type in ADDR_TYPES: + create_interface_in_kernel( + tgen, + "r2", + "loopback1", + LOOPBACK_1[addr_type], + "RED_A", + LOOPBACK_1["{}_mask".format(addr_type)], + ) + create_interface_in_kernel( + tgen, + "r2", + "loopback2", + LOOPBACK_2[addr_type], + "BLUE_A", + LOOPBACK_2["{}_mask".format(addr_type)], + ) + + step( + "Create a static routes in vrf RED_B on router RED_1 pointing" + " next-hop as interface's IP in vrf RED_A" + ) + + intf_r3_r21 = topo["routers"]["r3"]["links"]["r2-link1"]["interface"] + intf_r3_r23 = topo["routers"]["r3"]["links"]["r2-link3"]["interface"] + for addr_type in ADDR_TYPES: + input_dict_1 = { + "r3": { + "static_routes": [ + { + "network": LOOPBACK_2[addr_type], + "interface": intf_r3_r23, + "nexthop_vrf": "BLUE_A", + "vrf": "RED_A", + }, + { + "network": LOOPBACK_1[addr_type], + "interface": intf_r3_r21, + "nexthop_vrf": "RED_A", + "vrf": "BLUE_A", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = {} + for dut in ["r3"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + VRFS = ["RED_A", "BLUE_A"] + AS_NUM = [200, 200] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Redistribute connected..") + + input_dict_3 = {} + for dut in ["r2"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + VRFS = ["RED_A", "BLUE_A"] + AS_NUM = [100, 100] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "connected"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "connected"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that static routes are installed into vrfs RED_A" + "and RED_B tables only, not in global routing table of RED_1" + ) + + for addr_type in ADDR_TYPES: + dut = "r3" + input_dict = { + "r3": { + "static_routes": [ + { + "network": LOOPBACK_2[addr_type], + "interface": intf_r3_r23, + "nexthop_vrf": "BLUE_A", + "vrf": "RED_A", + }, + { + "network": LOOPBACK_1[addr_type], + "interface": intf_r3_r21, + "nexthop_vrf": "RED_A", + "vrf": "BLUE_A", + }, + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_route_map_within_vrf_to_alter_bgp_attribute_nexthop_p0(request): + """ + FUNC_12_a: + Configure route-maps within a VRF, to alter BGP attributes. + Verify that route-map doesn't affect any other VRF instances' + routing on DUT. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step( + "Advertise a set of BGP prefixes(IPv4+IPv6) from RED_1 and" + " RED_2 in vrf instances(RED_A and RED_B)." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise same set of BGP prefixes(IPv4+IPv6) from BLUE_1 and" + "BLUE_2 in vrf instances(BLUE_A and BLUE_B)" + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = {} + for dut in ["red1", "blue1"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + if "red" in dut: + VRFS = ["RED_A", "RED_B"] + AS_NUM = [500, 500] + elif "blue" in dut: + VRFS = ["BLUE_A", "BLUE_B"] + AS_NUM = [800, 800] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that within vrf instances, BGP best path selection" + " algorithm remains intact and doesn't affect any other VRFs" + " routing decision." + ) + + for addr_type in ADDR_TYPES: + dut = "r2" + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Delete nexthop-self configure from r1") + + input_dict_4 = { + "r1": { + "bgp": [ + { + "local_as": "100", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"next_hop_self": False} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"next_hop_self": False} + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link2": {"next_hop_self": False} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link2": {"next_hop_self": False} + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link3": {"next_hop_self": False} + } + }, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link3": {"next_hop_self": False} + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link4": {"next_hop_self": False} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link4": {"next_hop_self": False} + } + } + } + } + }, + }, + }, + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that within vrf instances, BGP best path selection" + " algorithm remains intact and doesn't affect any other VRFs" + " routing decision." + ) + + for addr_type in ADDR_TYPES: + dut = "r2" + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_1, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n Expected Behaviour: Routes are rejected because" + " nexthop-self config is deleted \n Error {}".format(tc_name, result) + + result = verify_rib(tgen, addr_type, dut, input_dict_2, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n Expected Behaviour: Routes are rejected because" + " nexthop-self config is deleted \n Error {}".format(tc_name, result) + + write_test_footer(tc_name) + + +@pytest.mark.parametrize("attribute", ["locPrf", "weight", "metric"]) +def test_route_map_within_vrf_to_alter_bgp_attribute_p0(request, attribute): + """ + FUNC_12_b/c/d: + Configure route-maps within a VRF, to alter BGP attributes. + Verify that route-map doesn't affect any other VRF instances' + routing on DUT. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step( + "Advertise a set of BGP prefixes(IPv4+IPv6) from RED_1 and" + " RED_2 in vrf instances(RED_A and RED_B)." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + }, + "red2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + }, + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise same set of BGP prefixes(IPv4+IPv6) from BLUE_1 and" + "BLUE_2 in vrf instances(BLUE_A and BLUE_B)" + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + }, + "blue2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + }, + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = {} + for dut in ["red1", "red2", "blue1", "blue2"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + if "red" in dut: + VRFS = ["RED_A", "RED_B"] + AS_NUM = [500, 500] + elif "blue" in dut: + VRFS = ["BLUE_A", "BLUE_B"] + AS_NUM = [800, 800] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure a route-maps to influence BGP parameters - " " Local Preference") + + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r2": { + "route_maps": { + "rmap_r1_{}".format(addr_type): [ + {"action": "permit", "set": {attribute: 120}} + ], + "rmap_r3_{}".format(addr_type): [ + {"action": "permit", "set": {attribute: 150}} + ], + } + } + } + + result = create_route_maps(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure neighbor for route map") + input_dict_4 = { + "r2": { + "bgp": [ + { + "local_as": "100", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": { + "route_maps": [ + { + "name": "rmap_r1_ipv4", + "direction": "in", + } + ] + } + } + }, + "r3": { + "dest_link": { + "r2-link1": { + "route_maps": [ + { + "name": "rmap_r3_ipv4", + "direction": "in", + } + ] + } + } + }, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": { + "route_maps": [ + { + "name": "rmap_r1_ipv6", + "direction": "in", + } + ] + } + } + }, + "r3": { + "dest_link": { + "r2-link1": { + "route_maps": [ + { + "name": "rmap_r3_ipv6", + "direction": "in", + } + ] + } + } + }, + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link2": { + "route_maps": [ + { + "name": "rmap_r1_ipv4", + "direction": "in", + } + ] + } + } + }, + "r3": { + "dest_link": { + "r2-link2": { + "route_maps": [ + { + "name": "rmap_r3_ipv4", + "direction": "in", + } + ] + } + } + }, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link2": { + "route_maps": [ + { + "name": "rmap_r1_ipv6", + "direction": "in", + } + ] + } + } + }, + "r3": { + "dest_link": { + "r2-link2": { + "route_maps": [ + { + "name": "rmap_r3_ipv6", + "direction": "in", + } + ] + } + } + }, + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link3": { + "route_maps": [ + { + "name": "rmap_r1_ipv4", + "direction": "in", + } + ] + } + } + }, + "r3": { + "dest_link": { + "r2-link3": { + "route_maps": [ + { + "name": "rmap_r3_ipv4", + "direction": "in", + } + ] + } + } + }, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link3": { + "route_maps": [ + { + "name": "rmap_r1_ipv6", + "direction": "in", + } + ] + } + } + }, + "r3": { + "dest_link": { + "r2-link3": { + "route_maps": [ + { + "name": "rmap_r3_ipv6", + "direction": "in", + } + ] + } + } + }, + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link4": { + "route_maps": [ + { + "name": "rmap_r1_ipv4", + "direction": "in", + } + ] + } + } + }, + "r3": { + "dest_link": { + "r2-link4": { + "route_maps": [ + { + "name": "rmap_r3_ipv4", + "direction": "in", + } + ] + } + } + }, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link4": { + "route_maps": [ + { + "name": "rmap_r1_ipv6", + "direction": "in", + } + ] + } + } + }, + "r3": { + "dest_link": { + "r2-link4": { + "route_maps": [ + { + "name": "rmap_r3_ipv6", + "direction": "in", + } + ] + } + } + }, + } + } + }, + }, + }, + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that within vrf instances, BGP best path selection" + " algorithm remains intact and doesn't affect any other VRFs" + " routing decision." + ) + + dut = "r2" + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + + result = verify_best_path_as_per_bgp_attribute( + tgen, addr_type, dut, input_dict_1, attribute + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_best_path_as_per_bgp_attribute( + tgen, addr_type, dut, input_dict_2, attribute + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_route_map_within_vrf_to_alter_bgp_attribute_aspath_p0(request): + """ + FUNC_12_e: + Configure route-maps within a VRF, to alter BGP attributes. + Verify that route-map doesn't affect any other VRF instances' + routing on DUT. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step( + "Advertise a set of BGP prefixes(IPv4+IPv6) from RED_1 and" + " RED_2 in vrf instances(RED_A and RED_B)." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + }, + "red2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + }, + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise same set of BGP prefixes(IPv4+IPv6) from BLUE_1 and" + "BLUE_2 in vrf instances(BLUE_A and BLUE_B)" + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + }, + "blue2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + }, + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = {} + for dut in ["red1", "red2", "blue1", "blue2"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + if "red" in dut: + VRFS = ["RED_A", "RED_B"] + AS_NUM = [500, 500] + elif "blue" in dut: + VRFS = ["BLUE_A", "BLUE_B"] + AS_NUM = [800, 800] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure a route-maps to influence BGP parameters - " " Local Preference") + + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r2": { + "route_maps": { + "rmap_r1_{}".format(addr_type): [ + { + "action": "permit", + "set": { + "path": {"as_num": "111 222", "as_action": "prepend"} + }, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure neighbor for route map") + input_dict_4 = { + "r2": { + "bgp": [ + { + "local_as": "100", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": { + "route_maps": [ + { + "name": "rmap_r1_ipv4", + "direction": "in", + } + ] + } + } + }, + "r3": {"dest_link": {"r2-link1": {}}}, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": { + "route_maps": [ + { + "name": "rmap_r1_ipv6", + "direction": "in", + } + ] + } + } + }, + "r3": {"dest_link": {"r2-link1": {}}}, + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link2": { + "route_maps": [ + { + "name": "rmap_r1_ipv4", + "direction": "in", + } + ] + } + } + }, + "r3": {"dest_link": {"r2-link2": {}}}, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link2": { + "route_maps": [ + { + "name": "rmap_r1_ipv6", + "direction": "in", + } + ] + } + } + }, + "r3": {"dest_link": {"r2-link2": {}}}, + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link3": { + "route_maps": [ + { + "name": "rmap_r1_ipv4", + "direction": "in", + } + ] + } + } + }, + "r3": {"dest_link": {"r2-link3": {}}}, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link3": { + "route_maps": [ + { + "name": "rmap_r1_ipv6", + "direction": "in", + } + ] + } + } + }, + "r3": {"dest_link": {"r2-link3": {}}}, + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link4": { + "route_maps": [ + { + "name": "rmap_r1_ipv4", + "direction": "in", + } + ] + } + } + }, + "r3": {"dest_link": {"r2-link4": {}}}, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link4": { + "route_maps": [ + { + "name": "rmap_r1_ipv6", + "direction": "in", + } + ] + } + } + }, + "r3": {"dest_link": {"r2-link4": {}}}, + } + } + }, + }, + }, + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that within vrf instances, BGP best path selection" + " algorithm remains intact and doesn't affect any other VRFs" + " routing decision." + ) + + dut = "r2" + attribute = "path" + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + + result = verify_best_path_as_per_bgp_attribute( + tgen, addr_type, dut, input_dict_1, attribute + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_best_path_as_per_bgp_attribute( + tgen, addr_type, dut, input_dict_2, attribute + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_route_map_within_vrf_to_alter_bgp_attribute_lcomm_p0(request): + """ + FUNC_12_f: + Configure route-maps within a VRF, to alter BGP attributes. + Verify that route-map doesn't affect any other VRF instances' + routing on DUT. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step( + "Advertise a set of BGP prefixes(IPv4+IPv6) from RED_1 and" + " RED_2 in vrf instances(RED_A and RED_B)." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + }, + "red2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + }, + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise same set of BGP prefixes(IPv4+IPv6) from BLUE_1 and" + "BLUE_2 in vrf instances(BLUE_A and BLUE_B)" + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + }, + "blue2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + }, + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = {} + for dut in ["red1", "red2", "blue1", "blue2"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + if "red" in dut: + VRFS = ["RED_A", "RED_B"] + AS_NUM = [500, 500] + elif "blue" in dut: + VRFS = ["BLUE_A", "BLUE_B"] + AS_NUM = [800, 800] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure a route-maps to influence BGP parameters - " " Large-community") + + step("Create standard large commumity-list in r2") + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "r2": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "rmap_lcomm_{}".format(addr_type), + "value": "1:1:1 1:2:3 2:1:1 2:2:2", + "large": True, + } + ] + } + } + result = create_bgp_community_lists(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Create route-maps in red1 and r1") + + for addr_type in ADDR_TYPES: + input_dict_4 = { + "red1": { + "route_maps": { + "rmap_red1_{}".format(addr_type): [ + { + "action": "permit", + "set": { + "large_community": {"num": "1:1:1 1:2:3 2:1:1 2:2:2"} + }, + } + ] + } + }, + "r2": { + "route_maps": { + "rmap_r1_{}".format(addr_type): [ + { + "action": "permit", + "match": { + "large_community_list": { + "id": "rmap_lcomm_" + addr_type + } + }, + } + ] + } + }, + } + result = create_route_maps(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure neighbor for route map in red1") + + input_dict_4 = { + "red1": { + "bgp": [ + { + "local_as": "500", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link1": { + "route_maps": [ + { + "name": "rmap_red1_ipv4", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link1": { + "route_maps": [ + { + "name": "rmap_red1_ipv6", + "direction": "out", + } + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "500", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link2": { + "route_maps": [ + { + "name": "rmap_red1_ipv4", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link2": { + "route_maps": [ + { + "name": "rmap_red1_ipv6", + "direction": "out", + } + ] + } + } + } + } + } + }, + }, + }, + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure neighbor for route map in r2") + + input_dict_4 = { + "r2": { + "bgp": [ + { + "local_as": "100", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": { + "route_maps": [ + { + "name": "rmap_r1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": { + "route_maps": [ + { + "name": "rmap_r1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link2": { + "route_maps": [ + { + "name": "rmap_r1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link2": { + "route_maps": [ + { + "name": "rmap_r1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link3": { + "route_maps": [ + { + "name": "rmap_r1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link3": { + "route_maps": [ + { + "name": "rmap_r1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link4": { + "route_maps": [ + { + "name": "rmap_r1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link4": { + "route_maps": [ + { + "name": "rmap_r1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + }, + }, + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "All the prefixes advertised from RED_1 and BLUE_1 should carry" + " attributes set by outbound route-maps within specific vrfs. " + "Router R1 should be able to match and permit/deny those " + "prefixes based on received attributes. Please use below " + "commands to verify." + ) + + input_dict = { + "largeCommunity": "1:1:1 1:2:3 2:1:1 2:2:2", + } + + for addr_type in ADDR_TYPES: + vrf = "RED_A" + routes = [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]] + result = verify_bgp_community(tgen, addr_type, "r2", routes, input_dict, vrf) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + vrf = "RED_B" + routes = [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]] + result = verify_bgp_community(tgen, addr_type, "r2", routes, input_dict, vrf) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_route_map_match_traffic_based_on_vrf_p0(request): + """ + FUNC_13: + Configure a route-map on DUT to match traffic based + on a VRF interfaces. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step( + "Advertise unique BGP prefixes(IPv4+IPv6) from RED_1 " + "in vrf instances(RED_A and RED_B)." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise unique BGP prefixes(IPv4+IPv6) from from BLUE_1 in" + " vrf instances(BLUE_A and BLUE_B)." + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = {} + for dut in ["red1", "blue1"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + if "red" in dut: + VRFS = ["RED_A", "RED_B"] + AS_NUM = [500, 500] + elif "blue" in dut: + VRFS = ["BLUE_A", "BLUE_B"] + AS_NUM = [800, 800] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Configure a route-map on R1 to match the prefixes " + "coming from vrf RED_A and set as-prepend to these routes." + ) + + input_dict_4 = { + "r1": { + "route_maps": { + "ABC": [ + { + "action": "permit", + "match": {"source-vrf": "RED_A"}, + "set": {"path": {"as_num": 1, "as_action": "prepend"}}, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "On R1, import the routes form vrf RED_A and RED_B to BLUE_A and" + " apply the route-map under vrf BLUE_A while importing" + ) + + raw_config = { + "r1": { + "raw_config": [ + "router bgp 100 vrf BLUE_A", + "address-family ipv4 unicast", + "import vrf RED_A", + "import vrf RED_B", + "import vrf route-map ABC", + "address-family ipv6 unicast", + "import vrf RED_A", + "import vrf RED_B", + "import vrf route-map ABC", + ] + } + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "All the prefixes advertised from RED_1 and BLUE_1 in vrfs " + "RED_B and BLUE_B must prepend the AS number in as-path on R2." + ) + + for addr_type in ADDR_TYPES: + input_dict_7 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_dict_7) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_vrf_lite_with_static_bgp_originated_routes_p0(request): + """ + FUNC_14: + Test VRF-lite with Static+BGP originated routes. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step( + "Advertise unique BGP prefixes(IPv4+IPv6) from from RED_1" + " in vrf instances(RED_A and RED_B)." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise unique BGP prefixes(IPv4+IPv6) from from BLUE_1 in" + " vrf instances(BLUE_A and BLUE_B)." + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_3 = { + "red1": { + "bgp": [ + { + "local_as": "500", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + { + "network": [NETWORK5_1["ipv4"]] + + [NETWORK5_2["ipv4"]] + } + ], + "redistribute": [{"redist_type": "static"}], + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + { + "network": [NETWORK5_1["ipv6"]] + + [NETWORK5_2["ipv6"]] + } + ], + "redistribute": [{"redist_type": "static"}], + } + }, + }, + }, + { + "local_as": "500", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + { + "network": [NETWORK6_1["ipv4"]] + + [NETWORK6_2["ipv4"]] + } + ], + "redistribute": [{"redist_type": "static"}], + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + { + "network": [NETWORK6_1["ipv6"]] + + [NETWORK6_2["ipv6"]] + } + ], + "redistribute": [{"redist_type": "static"}], + } + }, + }, + }, + ] + }, + "blue1": { + "bgp": [ + { + "local_as": "800", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + { + "network": [NETWORK7_1["ipv4"]] + + [NETWORK7_2["ipv4"]] + } + ], + "redistribute": [{"redist_type": "static"}], + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + { + "network": [NETWORK7_1["ipv6"]] + + [NETWORK7_2["ipv6"]] + } + ], + "redistribute": [{"redist_type": "static"}], + } + }, + }, + }, + { + "local_as": "800", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + { + "network": [NETWORK8_1["ipv4"]] + + [NETWORK8_2["ipv4"]] + } + ], + "redistribute": [{"redist_type": "static"}], + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + { + "network": [NETWORK8_1["ipv6"]] + + [NETWORK8_2["ipv6"]] + } + ], + "redistribute": [{"redist_type": "static"}], + } + }, + }, + }, + ] + }, + } + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Static routes must be installed in associated VRF" " table only.") + + for addr_type in ADDR_TYPES: + dut = "r1" + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step( + "All the routers must receive advertised as well as " + "redistributed(static) prefixes in associated VRF tables." + ) + + for addr_type in ADDR_TYPES: + dut = "r1" + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_prefix_list_to_permit_deny_prefixes_p0(request): + """ + FUNC_15: + Configure prefix-lists on DUT and apply to BGP peers to + permit/deny prefixes. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step( + "Advertise unique BGP prefixes(IPv4+IPv6) from from RED_1" + " in vrf instances(RED_A and RED_B)." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise unique BGP prefixes(IPv4+IPv6) from from BLUE_1 in" + " vrf instances(BLUE_A and BLUE_B)." + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = {} + for dut in ["red1", "blue1"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + if "red" in dut: + VRFS = ["RED_A", "RED_B"] + AS_NUM = [500, 500] + elif "blue" in dut: + VRFS = ["BLUE_A", "BLUE_B"] + AS_NUM = [800, 800] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify routes are present before applying prefix-list") + for addr_type in ADDR_TYPES: + dut = "r1" + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step( + "On routers RED_1 and BLUE_1, configure prefix-lists to permit" + " 4 prefixes and deny 1 prefix x.x.x.5. Apply these in outbound" + "direction for each neighbour." + ) + + for addr_type in ADDR_TYPES: + input_dict_4 = { + "red1": { + "prefix_lists": { + addr_type: { + "pflist_red1_{}".format(addr_type): [ + { + "seqid": 10, + "network": NETWORK1_1[addr_type], + "action": "permit", + }, + { + "seqid": 11, + "network": NETWORK2_1[addr_type], + "action": "permit", + }, + { + "seqid": 12, + "network": NETWORK1_2[addr_type], + "action": "deny", + }, + { + "seqid": 13, + "network": NETWORK2_2[addr_type], + "action": "deny", + }, + ] + } + } + }, + "blue1": { + "prefix_lists": { + addr_type: { + "pflist_blue1_{}".format(addr_type): [ + { + "seqid": 10, + "network": NETWORK1_1[addr_type], + "action": "permit", + }, + { + "seqid": 11, + "network": NETWORK2_1[addr_type], + "action": "permit", + }, + { + "seqid": 12, + "network": NETWORK1_2[addr_type], + "action": "deny", + }, + { + "seqid": 13, + "network": NETWORK2_2[addr_type], + "action": "deny", + }, + ] + } + } + }, + "r1": { + "prefix_lists": { + addr_type: { + "pflist_r1_{}".format(addr_type): [ + { + "seqid": 10, + "network": NETWORK1_1[addr_type], + "action": "permit", + }, + { + "seqid": 11, + "network": NETWORK2_1[addr_type], + "action": "deny", + }, + ] + } + } + }, + } + result = create_prefix_lists(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_5 = { + "red1": { + "bgp": [ + { + "local_as": "500", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link1": { + "prefix_lists": [ + { + "name": "pflist_red1_ipv4", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link1": { + "prefix_lists": [ + { + "name": "pflist_red1_ipv6", + "direction": "out", + } + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "500", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link2": { + "prefix_lists": [ + { + "name": "pflist_red1_ipv4", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link2": { + "prefix_lists": [ + { + "name": "pflist_red1_ipv6", + "direction": "out", + } + ] + } + } + } + } + } + }, + }, + }, + ] + }, + "blue1": { + "bgp": [ + { + "local_as": "800", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "blue1-link1": { + "prefix_lists": [ + { + "name": "pflist_blue1_ipv4", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "blue1-link1": { + "prefix_lists": [ + { + "name": "pflist_blue1_ipv6", + "direction": "out", + } + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "800", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "blue1-link2": { + "prefix_lists": [ + { + "name": "pflist_blue1_ipv4", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "blue1-link2": { + "prefix_lists": [ + { + "name": "pflist_blue1_ipv6", + "direction": "out", + } + ] + } + } + } + } + } + }, + }, + }, + ] + }, + } + + result = create_router_bgp(tgen, topo, input_dict_5) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that within vrf instances, each BGP neighbor receives 1" + " prefixes in routing table and drops (x.x.x.2)." + ) + + for addr_type in ADDR_TYPES: + dut = "r1" + permitted_routes = { + "red1": { + "static_routes": [ + {"network": [NETWORK1_1[addr_type]], "vrf": "RED_A"}, + {"network": [NETWORK2_1[addr_type]], "vrf": "RED_B"}, + ] + } + } + + denied_routes = { + "red1": { + "static_routes": [ + {"network": [NETWORK1_2[addr_type]], "vrf": "RED_A"}, + {"network": [NETWORK2_2[addr_type]], "vrf": "RED_B"}, + ] + } + } + + result = verify_rib(tgen, addr_type, dut, permitted_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, denied_routes, expected=False) + assert result is not True, "Testcase {} : Failed \n" + "Expected behaviour: Routes are denied by prefix-list \n" + "Error {}".format(tc_name, result) + + step( + "On router R1, configure prefix-lists to permit 2 " + "prefixes(x.x.x.1-2) and deny 2 prefix(x.x.x.3-4). Apply" + " these in inbound direction for each neighbour." + ) + + input_dict_6 = { + "r1": { + "bgp": [ + { + "local_as": "100", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link1": { + "prefix_lists": [ + { + "name": "pflist_r1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link1": { + "prefix_lists": [ + { + "name": "pflist_r1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link2": { + "prefix_lists": [ + { + "name": "pflist_r1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link2": { + "prefix_lists": [ + { + "name": "pflist_r1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link1": { + "prefix_lists": [ + { + "name": "pflist_r1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link1": { + "prefix_lists": [ + { + "name": "pflist_r1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link2": { + "prefix_lists": [ + { + "name": "pflist_r1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link2": { + "prefix_lists": [ + { + "name": "pflist_r1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + }, + }, + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_6) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that within vrf instances, each BGP neighbor installs" + " only 1 prefix (x.x.x.1)." + ) + for addr_type in ADDR_TYPES: + dut = "r2" + permitted_routes = { + "red1": { + "static_routes": [{"network": [NETWORK1_1[addr_type]], "vrf": "RED_A"}] + } + } + + denied_routes = { + "red1": { + "static_routes": [{"network": [NETWORK2_1[addr_type]], "vrf": "RED_A"}] + } + } + + result = verify_rib(tgen, addr_type, dut, permitted_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, denied_routes, expected=False) + assert result is not True, "Testcase {} : Failed \n" + "Expected behaviour: Routes are denied by prefix-list \n" + "Error {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_route_map_set_and_match_tag_p0(request): + """ + FUNC_16_1: + Configure a route-map on DUT to match traffic based various + match/set causes. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step( + "Advertise unique BGP prefixes(IPv4+IPv6) from RED_1" + " in vrf instances(RED_A and RED_B)." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "tag": 4001, + "vrf": "RED_A", + }, + { + "network": [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise same set of BGP prefixes(IPv4+IPv6) from BLUE_1 and" + "BLUE_2 in vrf instances(BLUE_A and BLUE_B)" + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK3_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "tag": 4001, + "vrf": "BLUE_A", + }, + { + "network": [NETWORK3_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = {} + for dut in ["red1", "blue1"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + if "red" in dut: + VRFS = ["RED_A", "RED_B"] + AS_NUM = [500, 500] + elif "blue" in dut: + VRFS = ["BLUE_A", "BLUE_B"] + AS_NUM = [800, 800] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure a route-maps to match tag") + + for addr_type in ADDR_TYPES: + input_dict_4 = { + "red1": { + "route_maps": { + "rmap1_{}".format(addr_type): [ + {"action": "permit", "match": {addr_type: {"tag": "4001"}}} + ] + } + } + } + result = create_route_maps(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure neighbor for route map") + input_dict_4 = { + "red1": { + "bgp": [ + { + "local_as": "500", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link1": { + "route_maps": [ + { + "name": "rmap1_ipv4", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link1": { + "route_maps": [ + { + "name": "rmap1_ipv6", + "direction": "out", + } + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "500", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link2": { + "route_maps": [ + { + "name": "rmap1_ipv4", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link2": { + "route_maps": [ + { + "name": "rmap1_ipv6", + "direction": "out", + } + ] + } + } + } + } + } + }, + }, + }, + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that within vrf instances, BGP best path selection" + " algorithm remains intact and doesn't affect any other VRFs" + " routing decision." + ) + + dut = "r1" + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "tag": 4001, + "vrf": "RED_A", + } + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_2, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n Expected Behavior: Routes are denied \n" + "Error {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_route_map_set_and_match_metric_p0(request): + """ + FUNC_16_2: + Configure a route-map on DUT to match traffic based various + match/set causes. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step( + "Advertise unique BGP prefixes(IPv4+IPv6) from RED_1" + " in vrf instances(RED_A and RED_B)." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise same set of BGP prefixes(IPv4+IPv6) from BLUE_1 and" + "BLUE_2 in vrf instances(BLUE_A and BLUE_B)" + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = { + "red1": { + "bgp": [ + { + "local_as": "500", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + { + "redist_type": "static", + "attribute": {"metric": 123}, + } + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + { + "redist_type": "static", + "attribute": {"metric": 123}, + } + ] + } + }, + }, + }, + { + "local_as": "500", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + }, + ] + }, + "blue1": { + "bgp": [ + { + "local_as": "800", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + { + "redist_type": "static", + "attribute": {"metric": 123}, + } + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + { + "redist_type": "static", + "attribute": {"metric": 123}, + } + ] + } + }, + }, + }, + { + "local_as": "800", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + }, + ] + }, + } + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure a route-maps to match tag") + + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r1": { + "route_maps": { + "rmap1_{}".format(addr_type): [ + {"action": "permit", "match": {"metric": 123}} + ] + } + } + } + result = create_route_maps(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure neighbor for route map") + input_dict_4 = { + "r1": { + "bgp": [ + { + "local_as": "100", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link1": { + "route_maps": [ + { + "name": "rmap1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link1": { + "route_maps": [ + { + "name": "rmap1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link2": { + "route_maps": [ + { + "name": "rmap1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link2": { + "route_maps": [ + { + "name": "rmap1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link1": { + "route_maps": [ + { + "name": "rmap1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link1": { + "route_maps": [ + { + "name": "rmap1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link2": { + "route_maps": [ + { + "name": "rmap1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link2": { + "route_maps": [ + { + "name": "rmap1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + }, + }, + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that within vrf instances, BGP best path selection" + " algorithm remains intact and doesn't affect any other VRFs" + " routing decision." + ) + + dut = "r1" + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + } + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + } + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_2, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n Expected Behavior: Routes are denied \n" + "Error {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_route_map_set_and_match_community_p0(request): + """ + FUNC_16_3: + Configure a route-map on DUT to match traffic based various + match/set causes. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step( + "Advertise unique BGP prefixes(IPv4+IPv6) from RED_1" + " in vrf instances(RED_A and RED_B)." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise same set of BGP prefixes(IPv4+IPv6) from BLUE_1 and" + "BLUE_2 in vrf instances(BLUE_A and BLUE_B)" + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = {} + for dut in ["red1", "blue1"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + if "red" in dut: + VRFS = ["RED_A", "RED_B"] + AS_NUM = [500, 500] + elif "blue" in dut: + VRFS = ["BLUE_A", "BLUE_B"] + AS_NUM = [800, 800] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Create community-list") + + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r1": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "rmap_lcomm_{}".format(addr_type), + "value": "1:1 1:2 1:3 1:4 1:5", + } + ] + } + } + result = create_bgp_community_lists(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure a route-maps to match tag") + + step("Create route-maps in red1 and r1") + + for addr_type in ADDR_TYPES: + input_dict_4 = { + "red1": { + "route_maps": { + "rmap_red1_{}".format(addr_type): [ + { + "action": "permit", + "set": {"community": {"num": "1:1 1:2 1:3 1:4 1:5"}}, + } + ] + } + }, + "r1": { + "route_maps": { + "rmap1_{}".format(addr_type): [ + { + "action": "permit", + "match": { + "community_list": {"id": "rmap_lcomm_" + addr_type} + }, + } + ] + } + }, + } + result = create_route_maps(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure neighbor for route map") + input_dict_4 = { + "red1": { + "bgp": [ + { + "local_as": "500", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link1": { + "route_maps": [ + { + "name": "rmap_red1_ipv4", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link1": { + "route_maps": [ + { + "name": "rmap_red1_ipv6", + "direction": "out", + } + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "500", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link2": { + "route_maps": [ + { + "name": "rmap_red1_ipv4", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link2": { + "route_maps": [ + { + "name": "rmap_red1_ipv6", + "direction": "out", + } + ] + } + } + } + } + } + }, + }, + }, + ] + }, + "r1": { + "bgp": [ + { + "local_as": "100", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link1": { + "route_maps": [ + { + "name": "rmap1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link1": { + "route_maps": [ + { + "name": "rmap1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link2": { + "route_maps": [ + { + "name": "rmap1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link2": { + "route_maps": [ + { + "name": "rmap1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link1": { + "route_maps": [ + { + "name": "rmap1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link1": { + "route_maps": [ + { + "name": "rmap1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link2": { + "route_maps": [ + { + "name": "rmap1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link2": { + "route_maps": [ + { + "name": "rmap1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + }, + }, + ] + }, + } + + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "All the prefixes advertised from RED_1 and BLUE_1 should carry" + " attributes set by outbound route-maps within specific vrfs. " + "Router R1 should be able to match and permit/deny those " + "prefixes based on received attributes. Please use below " + "commands to verify." + ) + + input_dict = { + "community": "1:1 1:2 1:3 1:4 1:5", + } + + for addr_type in ADDR_TYPES: + vrf = "RED_A" + routes = [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]] + result = verify_bgp_community(tgen, addr_type, "r1", routes, input_dict, vrf) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + vrf = "RED_B" + routes = [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]] + result = verify_bgp_community(tgen, addr_type, "r1", routes, input_dict, vrf) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_multi_vrf_topo2/bgp_multi_vrf_topo2.json b/tests/topotests/bgp_multi_vrf_topo2/bgp_multi_vrf_topo2.json new file mode 100644 index 0000000000..ab570fcc16 --- /dev/null +++ b/tests/topotests/bgp_multi_vrf_topo2/bgp_multi_vrf_topo2.json @@ -0,0 +1,1277 @@ +{ + "address_types": ["ipv4","ipv6"], + "ipv4base": "10.0.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 30, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:db8:f::", + "v6mask": 128 + }, + "routers": { + "red1": { + "links": { + "r1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "r1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"} + }, + "vrfs":[ + { + "name": "RED_A", + "id": "1" + }, + { + "name": "RED_B", + "id": "2" + } + ], + "bgp": + [ + { + "local_as": "500", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link1": {} + } + } + } + } + } + } + }, + { + "local_as": "500", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link2": {} + } + } + } + } + } + } + } + ] + }, + "blue1": { + "links": { + "r1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"}, + "r1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"} + }, + "vrfs":[ + { + "name": "BLUE_A", + "id": "1" + }, + { + "name": "BLUE_B", + "id": "2" + } + ], + "bgp": + [ + { + "local_as": "800", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "blue1-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "blue1-link1": {} + } + } + } + } + } + } + }, + { + "local_as": "800", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "blue1-link2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "blue1-link2": {} + } + } + } + } + } + } + } + ] + }, + "r1": { + "links": { + "red1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "red1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"}, + "blue1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"}, + "blue1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"}, + "r2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "r2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"}, + "r2-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"}, + "r2-link4": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"}, + "r4-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "r4-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"}, + "r4-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"}, + "r4-link4": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"} + }, + "vrfs":[ + { + "name": "RED_A", + "id": "1" + }, + { + "name": "RED_B", + "id": "2" + }, + { + "name": "BLUE_A", + "id": "3" + }, + { + "name": "BLUE_B", + "id": "4" + } + ], + "bgp": + [ + { + "local_as": "100", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link1": {} + } + }, + "r2": { + "dest_link": { + "r1-link1": + { "next_hop_self": true } + } + }, + "r4": { + "dest_link": { + "r1-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link1": {} + } + }, + "r2": { + "dest_link": { + "r1-link1": + { "next_hop_self": true } + } + }, + "r4": { + "dest_link": { + "r1-link1": {} + } + } + } + } + } + } + }, + { + "local_as": "100", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link2": {} + } + }, + "r2": { + "dest_link": { + "r1-link2": + { "next_hop_self": true } + } + }, + "r4": { + "dest_link": { + "r1-link2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link2": {} + } + }, + "r2": { + "dest_link": { + "r1-link2": + { "next_hop_self": true } + } + }, + "r4": { + "dest_link": { + "r1-link2": {} + } + } + } + } + } + } + }, + { + "local_as": "100", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link1": {} + } + }, + "r2": { + "dest_link": { + "r1-link3": + { "next_hop_self": true } + } + }, + "r4": { + "dest_link": { + "r1-link3": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link1": {} + } + }, + "r2": { + "dest_link": { + "r1-link3": + { "next_hop_self": true } + } + }, + "r4": { + "dest_link": { + "r1-link3": {} + } + } + } + } + } + } + }, + { + "local_as": "100", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link2": {} + } + }, + "r2": { + "dest_link": { + "r1-link4": + { "next_hop_self": true } + } + }, + "r4": { + "dest_link": { + "r1-link4": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link2": {} + } + }, + "r2": { + "dest_link": { + "r1-link4": + { "next_hop_self": true } + } + }, + "r4": { + "dest_link": { + "r1-link4": {} + } + } + } + } + } + } + } + ] + }, + "r2": { + "links": { + "r1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "r1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"}, + "r1-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"}, + "r1-link4": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"}, + "r3-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "r3-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"}, + "r3-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"}, + "r3-link4": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"} + }, + "vrfs":[ + { + "name": "RED_A", + "id": "1" + }, + { + "name": "RED_B", + "id": "2" + }, + { + "name": "BLUE_A", + "id": "3" + }, + { + "name": "BLUE_B", + "id": "4" + } + ], + "bgp": + [ + { + "local_as": "100", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {} + } + }, + "r3": { + "dest_link": { + "r2-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {} + } + }, + "r3": { + "dest_link": { + "r2-link1": {} + } + } + } + } + } + } + }, + { + "local_as": "100", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link2": {} + } + }, + "r3": { + "dest_link": { + "r2-link2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link2": {} + } + }, + "r3": { + "dest_link": { + "r2-link2": {} + } + } + } + } + } + } + }, + { + "local_as": "100", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link3": {} + } + }, + "r3": { + "dest_link": { + "r2-link3": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link3": {} + } + }, + "r3": { + "dest_link": { + "r2-link3": {} + } + } + } + } + } + } + }, + { + "local_as": "100", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link4": {} + } + }, + "r3": { + "dest_link": { + "r2-link4": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link4": {} + } + }, + "r3": { + "dest_link": { + "r2-link4": {} + } + } + } + } + } + } + } + ] + }, + "r3": { + "links": { + "r2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "r2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"}, + "r2-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"}, + "r2-link4": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"}, + "r4-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "r4-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"}, + "r4-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"}, + "r4-link4": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"}, + "red2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "red2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"}, + "blue2-link1": {"ipv4": "auto", "ipv6": "autor3", "vrf": "BLUE_A"}, + "blue2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"} + }, + "vrfs":[ + { + "name": "RED_A", + "id": "1" + }, + { + "name": "RED_B", + "id": "2" + }, + { + "name": "BLUE_A", + "id": "3" + }, + { + "name": "BLUE_B", + "id": "4" + } + ], + "bgp": + [ + { + "local_as": "200", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": {} + } + }, + "r4": { + "dest_link": { + "r3-link1": {} + } + }, + "red2": { + "dest_link": { + "r3-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r4": { + "dest_link": { + "r3-link1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "red2": { + "dest_link": { + "r3-link1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "200", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link2": {} + } + }, + "r4": { + "dest_link": { + "r3-link2": {} + } + }, + "red2": { + "dest_link": { + "r3-link2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link2": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r4": { + "dest_link": { + "r3-link2": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "red2": { + "dest_link": { + "r3-link2": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "200", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link3": {} + } + }, + "r4": { + "dest_link": { + "r3-link3": {} + } + }, + "blue2": { + "dest_link": { + "r3-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link3": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r4": { + "dest_link": { + "r3-link3": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "blue2": { + "dest_link": { + "r3-link1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "200", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link4": {} + } + }, + "r4": { + "dest_link": { + "r3-link4": {} + } + }, + "blue2": { + "dest_link": { + "r3-link2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link4": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r4": { + "dest_link": { + "r3-link4": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "blue2": { + "dest_link": { + "r3-link2": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + } + ], + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + }, + "r4": { + "links": { + "r1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "r1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"}, + "r1-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"}, + "r1-link4": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"}, + "r3-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "r3-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"}, + "r3-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"}, + "r3-link4": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"} + }, + "vrfs":[ + { + "name": "RED_A", + "id": "1" + }, + { + "name": "RED_B", + "id": "2" + }, + { + "name": "BLUE_A", + "id": "3" + }, + { + "name": "BLUE_B", + "id": "4" + } + ], + "bgp": + [ + { + "local_as": "400", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r4-link1": {} + } + }, + "r3": { + "dest_link": { + "r4-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r4-link1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r3": { + "dest_link": { + "r4-link1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "400", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r4-link2": {} + } + }, + "r3": { + "dest_link": { + "r4-link2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r4-link2": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r3": { + "dest_link": { + "r4-link2": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "400", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r4-link3": {} + } + }, + "r3": { + "dest_link": { + "r4-link3": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r4-link3": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r3": { + "dest_link": { + "r4-link3": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "400", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r4-link4": {} + } + }, + "r3": { + "dest_link": { + "r4-link4": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r4-link4": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r3": { + "dest_link": { + "r4-link4": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + } + ], + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + }, + "red2": { + "links": { + "r3-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "r3-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"} + }, + "vrfs":[ + { + "name": "RED_A", + "id": "1" + }, + { + "name": "RED_B", + "id": "2" + } + ], + "bgp": + [ + { + "local_as": "500", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "red2-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "red2-link1": {} + } + } + } + } + } + } + }, + { + "local_as": "500", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "red2-link2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "red2-link2": {} + } + } + } + } + } + } + } + ] + }, + "blue2": { + "links": { + "r3-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"}, + "r3-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"} + }, + "vrfs":[ + { + "name": "BLUE_A", + "id": "1" + }, + { + "name": "BLUE_B", + "id": "2" + } + ], + "bgp": + [ + { + "local_as": "800", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "blue2-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "blue2-link1": {} + } + } + } + } + } + } + }, + { + "local_as": "800", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "blue2-link2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "blue2-link2": {} + } + } + } + } + } + } + } + ] + } + } +} 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 new file mode 100755 index 0000000000..bb13d54019 --- /dev/null +++ b/tests/topotests/bgp_multi_vrf_topo2/test_bgp_multi_vrf_topo2.py @@ -0,0 +1,2016 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +Following tests are covered to test BGP Multi-VRF: + +CHAOS_1: + Do a shut and no shut on connecting interface of DUT, + to see if all vrf instances clear their respective BGP tables + during the interface down and restores when interface brought +kCHAOS_3: + VRF leaking - next-hop interface is flapping. +CHAOS_5: + VRF - VLANs - Routing Table ID - combination testcase + on DUT. +CHAOS_9: + Verify that all vrf instances fall back + to backup path, if primary link goes down. + +""" + +import os +import sys +import json +import time +import pytest +from copy import deepcopy +from time import sleep + + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# Required to instantiate the topology builder class. + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from mininet.topo import Topo + +from lib.common_config import ( + step, + verify_rib, + start_topology, + write_test_header, + check_address_types, + write_test_footer, + reset_config_on_routers, + create_route_maps, + shutdown_bringup_interface, + start_router_daemons, + create_static_routes, + create_vrf_cfg, + create_interfaces_cfg, + create_interface_in_kernel, + kill_mininet_routers_process, + get_frr_ipv6_linklocal, + check_router_status, + apply_raw_config, + required_linux_kernel_version +) + +from lib.topolog import logger +from lib.bgp import clear_bgp, verify_bgp_rib, create_router_bgp, verify_bgp_convergence +from lib.topojson import build_config_from_json, build_topo_from_json + +# Reading the data from JSON File for topology creation +jsonFile = "{}/bgp_multi_vrf_topo2.json".format(CWD) + +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +# Global variables +NETWORK1_1 = {"ipv4": "1.1.1.1/32", "ipv6": "1::1/128"} +NETWORK1_2 = {"ipv4": "1.1.1.2/32", "ipv6": "1::2/128"} +NETWORK2_1 = {"ipv4": "2.1.1.1/32", "ipv6": "2::1/128"} +NETWORK2_2 = {"ipv4": "2.1.1.2/32", "ipv6": "2::2/128"} +NETWORK3_1 = {"ipv4": "3.1.1.1/32", "ipv6": "3::1/128"} +NETWORK3_2 = {"ipv4": "3.1.1.2/32", "ipv6": "3::2/128"} +NETWORK4_1 = {"ipv4": "4.1.1.1/32", "ipv6": "4::1/128"} +NETWORK4_2 = {"ipv4": "4.1.1.2/32", "ipv6": "4::2/128"} +NETWORK9_1 = {"ipv4": "100.1.0.1/30", "ipv6": "100::1/126"} +NETWORK9_2 = {"ipv4": "100.1.0.2/30", "ipv6": "100::2/126"} + +NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"} + +LOOPBACK_2 = { + "ipv4": "20.20.20.20/32", + "ipv6": "20::20:20/128", + "ipv4_mask": "255.255.255.255", + "ipv6_mask": None, +} + +MAX_PATHS = 2 +KEEPALIVETIMER = 1 +HOLDDOWNTIMER = 3 +PREFERRED_NEXT_HOP = "link_local" + + +class CreateTopo(Topo): + """ + Test BasicTopo - topology 1 + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + """Build function""" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version('4.15') + if result is not True: + pytest.skip("Kernel requirements are not met") + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(CreateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # Kill stale mininet routers and process + kill_mininet_routers_process(tgen) + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + global BGP_CONVERGENCE + global ADDR_TYPES + ADDR_TYPES = check_address_types() + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# +# Testcases +# +##################################################### + + +def test_vrf_with_multiple_links_p1(request): + """ + CHAOS_9: + Verify that all vrf instances fall back + to backup path, if primary link goes down. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step( + "Configure BGP neighborships(IPv4+IPv6) between R1 and R4 " + "using exact same link IPs for all 4 VRFs." + ) + + topo_modify = deepcopy(topo) + build_config_from_json(tgen, topo_modify) + + interfaces = ["link1", "link2", "link3", "link4"] + for interface in interfaces: + topo_modify["routers"]["r1"]["links"]["r4-{}".format(interface)][ + "delete" + ] = True + topo_modify["routers"]["r4"]["links"]["r1-{}".format(interface)][ + "delete" + ] = True + + step("Build interface config from json") + create_interfaces_cfg(tgen, topo_modify["routers"]) + + interfaces = ["link1", "link2", "link3", "link4"] + for interface in interfaces: + del topo_modify["routers"]["r1"]["links"]["r4-{}".format(interface)]["delete"] + del topo_modify["routers"]["r4"]["links"]["r1-{}".format(interface)]["delete"] + + r1_config = [] + r4_config = [] + for addr_type in ADDR_TYPES: + interfaces = ["link1", "link2", "link3", "link4"] + for interface in interfaces: + intf_name_r1 = topo_modify["routers"]["r1"]["links"][ + "r4-{}".format(interface) + ]["interface"] + topo_modify["routers"]["r1"]["links"]["r4-{}".format(interface)][ + addr_type + ] = NETWORK9_1[addr_type] + + intf_name_r4 = topo_modify["routers"]["r4"]["links"][ + "r1-{}".format(interface) + ]["interface"] + topo_modify["routers"]["r4"]["links"]["r1-{}".format(interface)][ + addr_type + ] = NETWORK9_2[addr_type] + + r1_config.append("interface {}".format(intf_name_r1)) + r4_config.append("interface {}".format(intf_name_r4)) + if addr_type == "ipv4": + r1_config.append("no ip address {}".format(NETWORK9_1[addr_type])) + r4_config.append("no ip address {}".format(NETWORK9_2[addr_type])) + else: + r1_config.append("no ipv6 address {}".format(NETWORK9_1[addr_type])) + r4_config.append("no ipv6 address {}".format(NETWORK9_2[addr_type])) + + step("Build interface config from json") + create_interfaces_cfg(tgen, topo_modify["routers"]) + + step("Create bgp config") + result = create_router_bgp(tgen, topo_modify["routers"]) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify BGP convergence") + + result = verify_bgp_convergence(tgen, topo_modify) + assert result is True, "Testcase {} : Failed \n Error {}".format(tc_name, result) + + step( + "Advertise below prefixes in BGP using static redistribution" + " for both vrfs (RED_A and BLUE_A) on router R2.." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "r1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + { + "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = {} + for dut in ["r1"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + VRFS = ["RED_A", "RED_B", "BLUE_A", "BLUE_B"] + AS_NUM = [100, 100, 100, 100] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo_modify, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify routes are installed with same nexthop in different" " VRFs") + + for addr_type in ADDR_TYPES: + dut = "r4" + _input_dict = { + "r1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + } + ] + } + } + + R1_NEXTHOP = topo_modify["routers"]["r1"]["links"]["r4-link1"][addr_type].split( + "/" + )[0] + + result = verify_rib(tgen, addr_type, dut, _input_dict, next_hop=R1_NEXTHOP) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + _input_dict = { + "r1": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + } + ] + } + } + + R1_NEXTHOP = topo_modify["routers"]["r1"]["links"]["r4-link1"][addr_type].split( + "/" + )[0] + + result = verify_rib(tgen, addr_type, dut, _input_dict, next_hop=R1_NEXTHOP) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + _input_dict = { + "r1": { + "static_routes": [ + { + "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + } + ] + } + } + + R1_NEXTHOP = topo_modify["routers"]["r1"]["links"]["r4-link1"][addr_type].split( + "/" + )[0] + + result = verify_rib(tgen, addr_type, dut, _input_dict, next_hop=R1_NEXTHOP) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + _input_dict = { + "r1": { + "static_routes": [ + { + "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + } + ] + } + } + + R1_NEXTHOP = topo_modify["routers"]["r1"]["links"]["r4-link1"][addr_type].split( + "/" + )[0] + + result = verify_rib(tgen, addr_type, dut, _input_dict, next_hop=R1_NEXTHOP) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step( + "Configure a route-map on R3 to prepend as-path and apply" + " for neighbour router R2 in both vrfs, in inbound direction." + ) + + input_dict_4 = { + "r3": { + "route_maps": { + "ASP": [ + { + "action": "permit", + "set": {"path": {"as_num": 123, "as_action": "prepend"}}, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Apply route-map to neighbours") + step( + "Configure ECMP on router R3 using 'max-path' command for both" + " VRFs RED_A and BLUE_A." + ) + + input_dict_5 = { + "r3": { + "bgp": [ + { + "local_as": "200", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "route_maps": [ + {"name": "ASP", "direction": "in"} + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "route_maps": [ + {"name": "ASP", "direction": "in"} + ] + } + } + }, + "r4": { + "dest_link": { + "r3-link1": { + "route_maps": [ + { + "name": "rmap_global", + "direction": "in", + } + ] + } + } + }, + } + } + }, + }, + }, + { + "local_as": "200", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link2": { + "route_maps": [ + {"name": "ASP", "direction": "in"} + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link2": { + "route_maps": [ + {"name": "ASP", "direction": "in"} + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "200", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link3": { + "route_maps": [ + {"name": "ASP", "direction": "in"} + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link3": { + "route_maps": [ + {"name": "ASP", "direction": "in"} + ] + } + } + }, + "r4": { + "dest_link": { + "r3-link3": { + "route_maps": [ + { + "name": "rmap_global", + "direction": "in", + } + ] + } + } + }, + } + } + }, + }, + }, + { + "local_as": "200", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link4": { + "route_maps": [ + {"name": "ASP", "direction": "in"} + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link4": { + "route_maps": [ + {"name": "ASP", "direction": "in"} + ] + } + } + } + } + } + }, + }, + }, + ] + } + } + + result = create_router_bgp(tgen, topo_modify, input_dict_5) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + dut = "r3" + peer = "r2" + input_dict = { + "r2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "vrf": "RED_A", + } + ] + } + } + + intf = topo_modify["routers"][peer]["links"]["r3-link1"]["interface"] + if addr_type == "ipv6" and "link_local" in PREFERRED_NEXT_HOP: + R2_NEXTHOP = get_frr_ipv6_linklocal(tgen, peer, intf=intf, vrf="RED_A") + else: + R2_NEXTHOP = topo_modify["routers"]["r2"]["links"]["r3-link1"][ + addr_type + ].split("/")[0] + + result = verify_rib(tgen, addr_type, dut, input_dict, next_hop=R2_NEXTHOP) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + input_dict = { + "r2": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "vrf": "BLUE_A", + } + ] + } + } + + intf = topo["routers"][peer]["links"]["r3-link3"]["interface"] + if addr_type == "ipv6" and "link_local" in PREFERRED_NEXT_HOP: + R2_NEXTHOP = get_frr_ipv6_linklocal(tgen, peer, intf=intf, vrf="BLUE_A") + else: + R2_NEXTHOP = topo_modify["routers"]["r2"]["links"]["r3-link3"][ + addr_type + ].split("/")[0] + + result = verify_rib(tgen, addr_type, dut, input_dict, next_hop=R2_NEXTHOP) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step( + "Configure ECMP on router R3 using max-path command for" + " both VRFs RED_A and BLUE_A." + ) + + input_dict_7 = { + "r3": { + "bgp": [ + { + "local_as": "200", + "vrf": "RED_A", + "address_family": { + "ipv4": {"unicast": {"maximum_paths": {"ebgp": MAX_PATHS,}}}, + "ipv6": {"unicast": {"maximum_paths": {"ebgp": MAX_PATHS,}}}, + }, + }, + { + "local_as": "200", + "vrf": "BLUE_A", + "address_family": { + "ipv4": {"unicast": {"maximum_paths": {"ebgp": MAX_PATHS,}}}, + "ipv6": {"unicast": {"maximum_paths": {"ebgp": MAX_PATHS,}}}, + }, + }, + ] + } + } + + result = create_router_bgp(tgen, topo_modify, input_dict_7) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("R3 should install prefixes from both next-hops (R2 and R4)") + + for addr_type in ADDR_TYPES: + dut = "r3" + peer = "r2" + input_dict = { + "r2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "vrf": "RED_A", + } + ] + } + } + + intf = topo_modify["routers"][peer]["links"]["r3-link1"]["interface"] + if addr_type == "ipv6" and "link_local" in PREFERRED_NEXT_HOP: + R2_NEXTHOP = get_frr_ipv6_linklocal(tgen, peer, intf=intf, vrf="RED_A") + else: + R2_NEXTHOP = topo_modify["routers"]["r2"]["links"]["r3-link1"][ + addr_type + ].split("/")[0] + + result = verify_rib(tgen, addr_type, dut, input_dict, next_hop=R2_NEXTHOP) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + input_dict = { + "r2": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "vrf": "BLUE_A", + } + ] + } + } + + intf = topo_modify["routers"][peer]["links"]["r3-link3"]["interface"] + if addr_type == "ipv6" and "link_local" in PREFERRED_NEXT_HOP: + R2_NEXTHOP = get_frr_ipv6_linklocal(tgen, peer, intf=intf, vrf="BLUE_A") + else: + R2_NEXTHOP = topo_modify["routers"]["r2"]["links"]["r3-link3"][ + addr_type + ].split("/")[0] + + result = verify_rib(tgen, addr_type, dut, input_dict, next_hop=R2_NEXTHOP) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Shutdown interface between R2 and R3 for vrfs RED_A and " "BLUE_A.") + + intf1 = topo_modify["routers"]["r2"]["links"]["r3-link1"]["interface"] + intf2 = topo_modify["routers"]["r2"]["links"]["r3-link3"]["interface"] + + interfaces = [intf1, intf2] + for intf in interfaces: + shutdown_bringup_interface(tgen, "r2", intf, False) + + for addr_type in ADDR_TYPES: + dut = "r3" + peer = "r4" + input_dict = { + "r2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "vrf": "RED_A", + } + ] + } + } + + R4_NEXTHOP = topo_modify["routers"]["r4"]["links"]["r3-link1"][addr_type].split( + "/" + )[0] + + result = verify_rib(tgen, addr_type, dut, input_dict, next_hop=R4_NEXTHOP) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + input_dict = { + "r2": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "vrf": "BLUE_A", + } + ] + } + } + + R4_NEXTHOP = topo_modify["routers"]["r4"]["links"]["r3-link3"][addr_type].split( + "/" + )[0] + + result = verify_rib(tgen, addr_type, dut, input_dict, next_hop=R4_NEXTHOP) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Unshut the interfaces between R2 and R3 for vrfs RED_A and BLUE_A.") + + for intf in interfaces: + shutdown_bringup_interface(tgen, "r2", intf, True) + + for addr_type in ADDR_TYPES: + dut = "r3" + peer = "r2" + input_dict = { + "r2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "vrf": "RED_A", + } + ] + } + } + + R4_NEXTHOP = topo_modify["routers"]["r4"]["links"]["r3-link1"][addr_type].split( + "/" + )[0] + + result = verify_rib(tgen, addr_type, dut, input_dict, next_hop=R4_NEXTHOP) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + input_dict = { + "r2": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "vrf": "BLUE_A", + } + ] + } + } + + R4_NEXTHOP = topo_modify["routers"]["r4"]["links"]["r3-link3"][addr_type].split( + "/" + )[0] + + result = verify_rib(tgen, addr_type, dut, input_dict, next_hop=R4_NEXTHOP) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Remove route-map from R3 for vrfs RED_A and BLUE_A.") + + input_dict_6 = { + "r3": { + "bgp": [ + { + "local_as": "200", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "route_maps": [ + { + "name": "ASP_ipv4", + "direction": "in", + "delete": True, + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "route_maps": [ + { + "name": "ASP_ipv6", + "direction": "in", + "delete": True, + }, + { + "name": "rmap_global", + "direction": "in", + }, + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "200", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link3": { + "route_maps": [ + { + "name": "ASP_ipv4", + "direction": "in", + "delete": True, + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link3": { + "route_maps": [ + { + "name": "ASP_ipv6", + "direction": "in", + "delete": True, + }, + { + "name": "rmap_global", + "direction": "in", + }, + ] + } + } + } + } + } + }, + }, + }, + ] + } + } + + result = create_router_bgp(tgen, topo_modify, input_dict_6) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + dut = "r3" + input_dict = { + "r2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "vrf": "RED_A", + } + ] + } + } + + R2_NEXTHOP = topo_modify["routers"]["r2"]["links"]["r3-link1"][addr_type].split( + "/" + )[0] + + result = verify_rib(tgen, addr_type, dut, input_dict, next_hop=R2_NEXTHOP) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + input_dict = { + "r2": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "vrf": "BLUE_A", + } + ] + } + } + + R2_NEXTHOP = topo_modify["routers"]["r2"]["links"]["r3-link3"][addr_type].split( + "/" + )[0] + + result = verify_rib(tgen, addr_type, dut, input_dict, next_hop=R2_NEXTHOP) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Shutdown links between between R2 and R3 for vrfs RED_A and" " BLUE_A.") + + for intf in interfaces: + shutdown_bringup_interface(tgen, "r2", intf, False) + + for addr_type in ADDR_TYPES: + dut = "r3" + input_dict = { + "r2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "vrf": "RED_A", + } + ] + } + } + + R4_NEXTHOP = topo_modify["routers"]["r4"]["links"]["r3-link1"][addr_type].split( + "/" + )[0] + + result = verify_rib(tgen, addr_type, dut, input_dict, next_hop=R4_NEXTHOP) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + input_dict = { + "r2": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "vrf": "BLUE_A", + } + ] + } + } + + R4_NEXTHOP = topo_modify["routers"]["r4"]["links"]["r3-link3"][addr_type].split( + "/" + )[0] + + result = verify_rib(tgen, addr_type, dut, input_dict, next_hop=R4_NEXTHOP) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Bringup links between between R2 and R3 for vrfs RED_A and" " BLUE_A.") + + for intf in interfaces: + shutdown_bringup_interface(tgen, "r2", intf, True) + + step("Deleting manualy assigned ip address from router r1 and r4 interfaces") + raw_config = {"r1": {"raw_config": r1_config}, "r4": {"raw_config": r4_config}} + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_shut_noshut_p1(request): + """ + CHAOS_1: + Do a shut and no shut on connecting interface of DUT, + to see if all vrf instances clear their respective BGP tables + during the interface down and restores when interface brought + back up again. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step("Build interface config from json") + create_interfaces_cfg(tgen, topo["routers"]) + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step( + "Advertise unique prefixes in BGP using static redistribution" + " for both vrfs (RED_A and RED_B) on router RED_1." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise unique prefixes in BGP using static redistribution" + " for both vrfs (BLUE_A and BLUE_B) on router BLUE_1." + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = {} + for dut in ["red1", "blue1"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + if "red" in dut: + VRFS = ["RED_A", "RED_B"] + AS_NUM = [500, 500] + elif "blue" in dut: + VRFS = ["BLUE_A", "BLUE_B"] + AS_NUM = [800, 800] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Api call to modfiy BGP timerse") + + input_dict_4 = { + "r1": { + "bgp": [ + { + "local_as": "100", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link2": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link2": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link3": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link3": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link4": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link4": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + }, + }, + ] + }, + "r2": { + "bgp": [ + { + "local_as": "100", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link2": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link2": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link3": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link3": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link4": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link4": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + }, + }, + ] + }, + } + + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + clear_bgp(tgen, addr_type, "r1", vrf=["RED_A", "RED_B", "BLUE_A", "BLUE_B"]) + + clear_bgp(tgen, addr_type, "r2", vrf=["RED_A", "RED_B", "BLUE_A", "BLUE_B"]) + + step("Shut down connecting interface between R1<<>>R2 on R1.") + step("Repeat step-3 and step-4 10 times.") + + for count in range(1, 2): + step("Iteration {}".format(count)) + step("Shut down connecting interface between R1<<>>R2 on R1.") + + intf1 = topo["routers"]["r1"]["links"]["r2-link1"]["interface"] + intf2 = topo["routers"]["r1"]["links"]["r2-link2"]["interface"] + intf3 = topo["routers"]["r1"]["links"]["r2-link3"]["interface"] + intf4 = topo["routers"]["r1"]["links"]["r2-link4"]["interface"] + + interfaces = [intf1, intf2, intf3, intf4] + for intf in interfaces: + shutdown_bringup_interface(tgen, "r1", intf, False) + + step( + "On R2, all BGP peering in respective vrf instances go down" + " when the interface is shut" + ) + + step("Sleeping for holddowntimer+1 sec..") + sleep(HOLDDOWNTIMER + 1) + + result = verify_bgp_convergence(tgen, topo, expected=False) + assert result is not True, "Testcase {} : Failed \n " + "Expected Behaviour: BGP will not be converged \n " + "Error {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + dut = "r2" + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_1, expected=False) + assert result is not True, "Testcase {} : Failed \n " + " Expected Behaviour: Routes are flushed out \n " + "Error {}".format(tc_name, result) + + result = verify_rib(tgen, addr_type, dut, input_dict_2, expected=False) + assert result is not True, "Testcase {} : Failed \n " + " Expected Behaviour: Routes are flushed out \n " + "Error {}".format(tc_name, result) + + step("Bring up connecting interface between R1<<>>R2 on R1.") + for intf in interfaces: + shutdown_bringup_interface(tgen, "r1", intf, True) + + step( + "R2 restores BGP peering and routing tables in all vrf " + "instances when interface brought back up again" + ) + + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + dut = "r2" + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_vrf_vlan_routing_table_p1(request): + """ + CHAOS_5: + VRF - VLANs - Routing Table ID - combination testcase + on DUT. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step( + "Advertise unique prefixes(IPv4+IPv6) in BGP using" + " network command for vrf RED_A on router R2" + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "r2": { + "static_routes": [ + { + "network": NETWORK1_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + } + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = { + "r2": { + "bgp": [ + { + "local_as": "100", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that static routes(IPv4+IPv6) is overridden and doesn't" + " have duplicate entries within VRF RED_A on router RED-1" + ) + + for addr_type in ADDR_TYPES: + dut = "r3" + input_dict_1 = { + "r2": { + "static_routes": [ + { + "network": NETWORK1_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + } + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Api call to modfiy BGP timerse") + + input_dict_4 = { + "r3": { + "bgp": [ + { + "local_as": "200", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + }, + } + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + clear_bgp(tgen, addr_type, "r3", vrf=["RED_A"]) + + step("Repeat for 5 times.") + + for count in range(1, 2): + step("Iteration {}..".format(count)) + step("Delete a specific VRF instance(RED_A) from router R3") + + input_dict = {"r3": {"vrfs": [{"name": "RED_A", "id": "1", "delete": True}]}} + + result = create_vrf_cfg(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Sleeping for holdowntimer+1 sec..") + sleep(HOLDDOWNTIMER + 1) + + for addr_type in ADDR_TYPES: + dut = "r3" + input_dict_1 = { + "r2": { + "static_routes": [ + { + "network": NETWORK1_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + } + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n Expected Behaviour: Routes are" + " cleaned \n Error {}".format(tc_name, result) + + step("Add/reconfigure the same VRF instance again") + + result = create_vrf_cfg(tgen, {"r3": topo["routers"]["r3"]}) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step( + "After deleting VRFs ipv6 addresses wil be deleted from kernel " + " Adding back ipv6 addresses" + ) + + dut = "r3" + vrf = "RED_A" + + for c_link, c_data in topo["routers"][dut]["links"].items(): + if c_data["vrf"] != vrf: + continue + + intf_name = c_data["interface"] + intf_ipv6 = c_data["ipv6"] + + create_interface_in_kernel( + tgen, dut, intf_name, intf_ipv6, vrf, create=False + ) + + step("Sleeping for holdowntimer+1 sec..") + sleep(HOLDDOWNTIMER + 1) + + for addr_type in ADDR_TYPES: + dut = "r3" + input_dict_1 = { + "r2": { + "static_routes": [ + { + "network": NETWORK1_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + } + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_vrf_route_leaking_next_hop_interface_flapping_p1(request): + """ + CHAOS_3: + VRF leaking - next-hop interface is flapping. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step("Create loopback interface") + + for addr_type in ADDR_TYPES: + create_interface_in_kernel( + tgen, + "red1", + "loopback2", + LOOPBACK_2[addr_type], + "RED_B", + LOOPBACK_2["{}_mask".format(addr_type)], + ) + + intf_red1_r11 = topo["routers"]["red1"]["links"]["r1-link2"]["interface"] + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": LOOPBACK_2[addr_type], + "interface": intf_red1_r11, + "nexthop_vrf": "RED_B", + "vrf": "RED_A", + } + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = { + "red1": { + "bgp": [ + { + "local_as": "500", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("VRF RED_A should install a route for vrf RED_B's " "loopback ip.") + for addr_type in ADDR_TYPES: + dut = "red1" + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": LOOPBACK_2[addr_type], + "interface": intf_red1_r11, + "nexthop_vrf": "RED_B", + "vrf": "RED_A", + } + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_1, protocol="static") + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Repeat step-2 to 4 at least 5 times") + + for count in range(1, 2): + intf1 = topo["routers"]["red1"]["links"]["r1-link2"]["interface"] + + step( + "Iteration {}: Shutdown interface {} on router" + "RED_1.".format(count, intf1) + ) + shutdown_bringup_interface(tgen, "red1", intf1, False) + + step("Verify that RED_A removes static route from routing " "table.") + + for addr_type in ADDR_TYPES: + dut = "red1" + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": LOOPBACK_2[addr_type], + "interface": intf_red1_r11, + "nexthop_vrf": "RED_B", + "vrf": "RED_A", + } + ] + } + } + + result = verify_rib( + tgen, addr_type, dut, input_dict_1, protocol="static", expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n Expected Behaviour: Routes are" + " not present Error {}".format(tc_name, result) + ) + + step("Bring up interface {} on router RED_1 again.".format(intf1)) + shutdown_bringup_interface(tgen, "red1", intf1, True) + + step( + "Verify that RED_A reinstalls static route pointing to " + "RED_B's IP in routing table again" + ) + + for addr_type in ADDR_TYPES: + dut = "red1" + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": LOOPBACK_2[addr_type], + "interface": intf_red1_r11, + "nexthop_vrf": "RED_B", + "vrf": "RED_A", + } + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_1, protocol="static") + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_multiview_topo1/README.md b/tests/topotests/bgp_multiview_topo1/README.md index b9982d4900..c1a1445894 100644 --- a/tests/topotests/bgp_multiview_topo1/README.md +++ b/tests/topotests/bgp_multiview_topo1/README.md @@ -1,4 +1,4 @@ -# Simple FreeRangeRouting Route-Server Test +# Simple FRRouting Route-Server Test ## Topology +----------+ +----------+ +----------+ +----------+ +----------+ @@ -55,8 +55,6 @@ Simplified `R1` config: ip address 172.16.1.254/24 no link-detect ! - bgp multiple-instance - ! router bgp 100 view 1 bgp router-id 172.30.1.1 network 172.20.0.0/28 route-map local1 @@ -96,7 +94,7 @@ Simplified `R1` config: Test is executed by running - vtysh -c "show log" | grep "Logging configuration for" + vtysh -c "show logging" | grep "Logging configuration for" on router `R1`. This should return the logging information for all daemons registered to Zebra and the list of running daemons is compared to the daemons started for this diff --git a/tests/topotests/bgp_multiview_topo1/peer1/exa-receive.py b/tests/topotests/bgp_multiview_topo1/peer1/exa-receive.py index 5334ea5369..031ff455ca 100755 --- a/tests/topotests/bgp_multiview_topo1/peer1/exa-receive.py +++ b/tests/topotests/bgp_multiview_topo1/peer1/exa-receive.py @@ -4,7 +4,7 @@ exa-receive.py: Save received routes form ExaBGP into file """ -from sys import stdin,argv +from sys import stdin, argv from datetime import datetime # 1st arg is peer number @@ -13,12 +13,12 @@ # When the parent dies we are seeing continual newlines, so we only access so many before stopping counter = 0 -routesavefile = open('/tmp/peer%s-received.log' % peer, 'w') +routesavefile = open("/tmp/peer%s-received.log" % peer, "w") while True: try: line = stdin.readline() - timestamp = datetime.now().strftime('%Y%m%d_%H:%M:%S - ') + timestamp = datetime.now().strftime("%Y%m%d_%H:%M:%S - ") routesavefile.write(timestamp + line) routesavefile.flush() diff --git a/tests/topotests/bgp_multiview_topo1/peer1/exa-send.py b/tests/topotests/bgp_multiview_topo1/peer1/exa-send.py index 2de2bce40a..505b08d6aa 100755 --- a/tests/topotests/bgp_multiview_topo1/peer1/exa-send.py +++ b/tests/topotests/bgp_multiview_topo1/peer1/exa-send.py @@ -4,7 +4,7 @@ exa-send.py: Send a few testroutes with ExaBGP """ -from sys import stdout,argv +from sys import stdout, argv from time import sleep sleep(5) @@ -16,13 +16,16 @@ # Announce numRoutes different routes per PE for i in range(0, numRoutes): - stdout.write('announce route 10.%s.%s.0/24 med 100 community %i:1 next-hop 172.16.1.%i\n' % ((peer+100), i, peer, peer)) + stdout.write( + "announce route 10.%s.%s.0/24 med 100 community %i:1 next-hop 172.16.1.%i\n" + % ((peer + 100), i, peer, peer) + ) stdout.flush() # Announce 1 overlapping route per peer -stdout.write('announce route 10.0.1.0/24 next-hop 172.16.1.%i\n' % peer) +stdout.write("announce route 10.0.1.0/24 next-hop 172.16.1.%i\n" % peer) stdout.flush() -#Loop endlessly to allow ExaBGP to continue running +# Loop endlessly to allow ExaBGP to continue running while True: sleep(1) diff --git a/tests/topotests/bgp_multiview_topo1/peer2/exa-receive.py b/tests/topotests/bgp_multiview_topo1/peer2/exa-receive.py index 5334ea5369..031ff455ca 100755 --- a/tests/topotests/bgp_multiview_topo1/peer2/exa-receive.py +++ b/tests/topotests/bgp_multiview_topo1/peer2/exa-receive.py @@ -4,7 +4,7 @@ exa-receive.py: Save received routes form ExaBGP into file """ -from sys import stdin,argv +from sys import stdin, argv from datetime import datetime # 1st arg is peer number @@ -13,12 +13,12 @@ # When the parent dies we are seeing continual newlines, so we only access so many before stopping counter = 0 -routesavefile = open('/tmp/peer%s-received.log' % peer, 'w') +routesavefile = open("/tmp/peer%s-received.log" % peer, "w") while True: try: line = stdin.readline() - timestamp = datetime.now().strftime('%Y%m%d_%H:%M:%S - ') + timestamp = datetime.now().strftime("%Y%m%d_%H:%M:%S - ") routesavefile.write(timestamp + line) routesavefile.flush() diff --git a/tests/topotests/bgp_multiview_topo1/peer2/exa-send.py b/tests/topotests/bgp_multiview_topo1/peer2/exa-send.py index 2de2bce40a..505b08d6aa 100755 --- a/tests/topotests/bgp_multiview_topo1/peer2/exa-send.py +++ b/tests/topotests/bgp_multiview_topo1/peer2/exa-send.py @@ -4,7 +4,7 @@ exa-send.py: Send a few testroutes with ExaBGP """ -from sys import stdout,argv +from sys import stdout, argv from time import sleep sleep(5) @@ -16,13 +16,16 @@ # Announce numRoutes different routes per PE for i in range(0, numRoutes): - stdout.write('announce route 10.%s.%s.0/24 med 100 community %i:1 next-hop 172.16.1.%i\n' % ((peer+100), i, peer, peer)) + stdout.write( + "announce route 10.%s.%s.0/24 med 100 community %i:1 next-hop 172.16.1.%i\n" + % ((peer + 100), i, peer, peer) + ) stdout.flush() # Announce 1 overlapping route per peer -stdout.write('announce route 10.0.1.0/24 next-hop 172.16.1.%i\n' % peer) +stdout.write("announce route 10.0.1.0/24 next-hop 172.16.1.%i\n" % peer) stdout.flush() -#Loop endlessly to allow ExaBGP to continue running +# Loop endlessly to allow ExaBGP to continue running while True: sleep(1) diff --git a/tests/topotests/bgp_multiview_topo1/peer3/exa-receive.py b/tests/topotests/bgp_multiview_topo1/peer3/exa-receive.py index 5334ea5369..031ff455ca 100755 --- a/tests/topotests/bgp_multiview_topo1/peer3/exa-receive.py +++ b/tests/topotests/bgp_multiview_topo1/peer3/exa-receive.py @@ -4,7 +4,7 @@ exa-receive.py: Save received routes form ExaBGP into file """ -from sys import stdin,argv +from sys import stdin, argv from datetime import datetime # 1st arg is peer number @@ -13,12 +13,12 @@ # When the parent dies we are seeing continual newlines, so we only access so many before stopping counter = 0 -routesavefile = open('/tmp/peer%s-received.log' % peer, 'w') +routesavefile = open("/tmp/peer%s-received.log" % peer, "w") while True: try: line = stdin.readline() - timestamp = datetime.now().strftime('%Y%m%d_%H:%M:%S - ') + timestamp = datetime.now().strftime("%Y%m%d_%H:%M:%S - ") routesavefile.write(timestamp + line) routesavefile.flush() diff --git a/tests/topotests/bgp_multiview_topo1/peer3/exa-send.py b/tests/topotests/bgp_multiview_topo1/peer3/exa-send.py index 2de2bce40a..505b08d6aa 100755 --- a/tests/topotests/bgp_multiview_topo1/peer3/exa-send.py +++ b/tests/topotests/bgp_multiview_topo1/peer3/exa-send.py @@ -4,7 +4,7 @@ exa-send.py: Send a few testroutes with ExaBGP """ -from sys import stdout,argv +from sys import stdout, argv from time import sleep sleep(5) @@ -16,13 +16,16 @@ # Announce numRoutes different routes per PE for i in range(0, numRoutes): - stdout.write('announce route 10.%s.%s.0/24 med 100 community %i:1 next-hop 172.16.1.%i\n' % ((peer+100), i, peer, peer)) + stdout.write( + "announce route 10.%s.%s.0/24 med 100 community %i:1 next-hop 172.16.1.%i\n" + % ((peer + 100), i, peer, peer) + ) stdout.flush() # Announce 1 overlapping route per peer -stdout.write('announce route 10.0.1.0/24 next-hop 172.16.1.%i\n' % peer) +stdout.write("announce route 10.0.1.0/24 next-hop 172.16.1.%i\n" % peer) stdout.flush() -#Loop endlessly to allow ExaBGP to continue running +# Loop endlessly to allow ExaBGP to continue running while True: sleep(1) diff --git a/tests/topotests/bgp_multiview_topo1/peer4/exa-receive.py b/tests/topotests/bgp_multiview_topo1/peer4/exa-receive.py index 5334ea5369..031ff455ca 100755 --- a/tests/topotests/bgp_multiview_topo1/peer4/exa-receive.py +++ b/tests/topotests/bgp_multiview_topo1/peer4/exa-receive.py @@ -4,7 +4,7 @@ exa-receive.py: Save received routes form ExaBGP into file """ -from sys import stdin,argv +from sys import stdin, argv from datetime import datetime # 1st arg is peer number @@ -13,12 +13,12 @@ # When the parent dies we are seeing continual newlines, so we only access so many before stopping counter = 0 -routesavefile = open('/tmp/peer%s-received.log' % peer, 'w') +routesavefile = open("/tmp/peer%s-received.log" % peer, "w") while True: try: line = stdin.readline() - timestamp = datetime.now().strftime('%Y%m%d_%H:%M:%S - ') + timestamp = datetime.now().strftime("%Y%m%d_%H:%M:%S - ") routesavefile.write(timestamp + line) routesavefile.flush() diff --git a/tests/topotests/bgp_multiview_topo1/peer4/exa-send.py b/tests/topotests/bgp_multiview_topo1/peer4/exa-send.py index 2de2bce40a..505b08d6aa 100755 --- a/tests/topotests/bgp_multiview_topo1/peer4/exa-send.py +++ b/tests/topotests/bgp_multiview_topo1/peer4/exa-send.py @@ -4,7 +4,7 @@ exa-send.py: Send a few testroutes with ExaBGP """ -from sys import stdout,argv +from sys import stdout, argv from time import sleep sleep(5) @@ -16,13 +16,16 @@ # Announce numRoutes different routes per PE for i in range(0, numRoutes): - stdout.write('announce route 10.%s.%s.0/24 med 100 community %i:1 next-hop 172.16.1.%i\n' % ((peer+100), i, peer, peer)) + stdout.write( + "announce route 10.%s.%s.0/24 med 100 community %i:1 next-hop 172.16.1.%i\n" + % ((peer + 100), i, peer, peer) + ) stdout.flush() # Announce 1 overlapping route per peer -stdout.write('announce route 10.0.1.0/24 next-hop 172.16.1.%i\n' % peer) +stdout.write("announce route 10.0.1.0/24 next-hop 172.16.1.%i\n" % peer) stdout.flush() -#Loop endlessly to allow ExaBGP to continue running +# Loop endlessly to allow ExaBGP to continue running while True: sleep(1) diff --git a/tests/topotests/bgp_multiview_topo1/peer5/exa-receive.py b/tests/topotests/bgp_multiview_topo1/peer5/exa-receive.py index 5334ea5369..031ff455ca 100755 --- a/tests/topotests/bgp_multiview_topo1/peer5/exa-receive.py +++ b/tests/topotests/bgp_multiview_topo1/peer5/exa-receive.py @@ -4,7 +4,7 @@ exa-receive.py: Save received routes form ExaBGP into file """ -from sys import stdin,argv +from sys import stdin, argv from datetime import datetime # 1st arg is peer number @@ -13,12 +13,12 @@ # When the parent dies we are seeing continual newlines, so we only access so many before stopping counter = 0 -routesavefile = open('/tmp/peer%s-received.log' % peer, 'w') +routesavefile = open("/tmp/peer%s-received.log" % peer, "w") while True: try: line = stdin.readline() - timestamp = datetime.now().strftime('%Y%m%d_%H:%M:%S - ') + timestamp = datetime.now().strftime("%Y%m%d_%H:%M:%S - ") routesavefile.write(timestamp + line) routesavefile.flush() diff --git a/tests/topotests/bgp_multiview_topo1/peer5/exa-send.py b/tests/topotests/bgp_multiview_topo1/peer5/exa-send.py index 2de2bce40a..505b08d6aa 100755 --- a/tests/topotests/bgp_multiview_topo1/peer5/exa-send.py +++ b/tests/topotests/bgp_multiview_topo1/peer5/exa-send.py @@ -4,7 +4,7 @@ exa-send.py: Send a few testroutes with ExaBGP """ -from sys import stdout,argv +from sys import stdout, argv from time import sleep sleep(5) @@ -16,13 +16,16 @@ # Announce numRoutes different routes per PE for i in range(0, numRoutes): - stdout.write('announce route 10.%s.%s.0/24 med 100 community %i:1 next-hop 172.16.1.%i\n' % ((peer+100), i, peer, peer)) + stdout.write( + "announce route 10.%s.%s.0/24 med 100 community %i:1 next-hop 172.16.1.%i\n" + % ((peer + 100), i, peer, peer) + ) stdout.flush() # Announce 1 overlapping route per peer -stdout.write('announce route 10.0.1.0/24 next-hop 172.16.1.%i\n' % peer) +stdout.write("announce route 10.0.1.0/24 next-hop 172.16.1.%i\n" % peer) stdout.flush() -#Loop endlessly to allow ExaBGP to continue running +# Loop endlessly to allow ExaBGP to continue running while True: sleep(1) diff --git a/tests/topotests/bgp_multiview_topo1/peer6/exa-receive.py b/tests/topotests/bgp_multiview_topo1/peer6/exa-receive.py index 5334ea5369..031ff455ca 100755 --- a/tests/topotests/bgp_multiview_topo1/peer6/exa-receive.py +++ b/tests/topotests/bgp_multiview_topo1/peer6/exa-receive.py @@ -4,7 +4,7 @@ exa-receive.py: Save received routes form ExaBGP into file """ -from sys import stdin,argv +from sys import stdin, argv from datetime import datetime # 1st arg is peer number @@ -13,12 +13,12 @@ # When the parent dies we are seeing continual newlines, so we only access so many before stopping counter = 0 -routesavefile = open('/tmp/peer%s-received.log' % peer, 'w') +routesavefile = open("/tmp/peer%s-received.log" % peer, "w") while True: try: line = stdin.readline() - timestamp = datetime.now().strftime('%Y%m%d_%H:%M:%S - ') + timestamp = datetime.now().strftime("%Y%m%d_%H:%M:%S - ") routesavefile.write(timestamp + line) routesavefile.flush() diff --git a/tests/topotests/bgp_multiview_topo1/peer6/exa-send.py b/tests/topotests/bgp_multiview_topo1/peer6/exa-send.py index 2de2bce40a..505b08d6aa 100755 --- a/tests/topotests/bgp_multiview_topo1/peer6/exa-send.py +++ b/tests/topotests/bgp_multiview_topo1/peer6/exa-send.py @@ -4,7 +4,7 @@ exa-send.py: Send a few testroutes with ExaBGP """ -from sys import stdout,argv +from sys import stdout, argv from time import sleep sleep(5) @@ -16,13 +16,16 @@ # Announce numRoutes different routes per PE for i in range(0, numRoutes): - stdout.write('announce route 10.%s.%s.0/24 med 100 community %i:1 next-hop 172.16.1.%i\n' % ((peer+100), i, peer, peer)) + stdout.write( + "announce route 10.%s.%s.0/24 med 100 community %i:1 next-hop 172.16.1.%i\n" + % ((peer + 100), i, peer, peer) + ) stdout.flush() # Announce 1 overlapping route per peer -stdout.write('announce route 10.0.1.0/24 next-hop 172.16.1.%i\n' % peer) +stdout.write("announce route 10.0.1.0/24 next-hop 172.16.1.%i\n" % peer) stdout.flush() -#Loop endlessly to allow ExaBGP to continue running +# Loop endlessly to allow ExaBGP to continue running while True: sleep(1) diff --git a/tests/topotests/bgp_multiview_topo1/peer7/exa-receive.py b/tests/topotests/bgp_multiview_topo1/peer7/exa-receive.py index 5334ea5369..031ff455ca 100755 --- a/tests/topotests/bgp_multiview_topo1/peer7/exa-receive.py +++ b/tests/topotests/bgp_multiview_topo1/peer7/exa-receive.py @@ -4,7 +4,7 @@ exa-receive.py: Save received routes form ExaBGP into file """ -from sys import stdin,argv +from sys import stdin, argv from datetime import datetime # 1st arg is peer number @@ -13,12 +13,12 @@ # When the parent dies we are seeing continual newlines, so we only access so many before stopping counter = 0 -routesavefile = open('/tmp/peer%s-received.log' % peer, 'w') +routesavefile = open("/tmp/peer%s-received.log" % peer, "w") while True: try: line = stdin.readline() - timestamp = datetime.now().strftime('%Y%m%d_%H:%M:%S - ') + timestamp = datetime.now().strftime("%Y%m%d_%H:%M:%S - ") routesavefile.write(timestamp + line) routesavefile.flush() diff --git a/tests/topotests/bgp_multiview_topo1/peer7/exa-send.py b/tests/topotests/bgp_multiview_topo1/peer7/exa-send.py index 2de2bce40a..505b08d6aa 100755 --- a/tests/topotests/bgp_multiview_topo1/peer7/exa-send.py +++ b/tests/topotests/bgp_multiview_topo1/peer7/exa-send.py @@ -4,7 +4,7 @@ exa-send.py: Send a few testroutes with ExaBGP """ -from sys import stdout,argv +from sys import stdout, argv from time import sleep sleep(5) @@ -16,13 +16,16 @@ # Announce numRoutes different routes per PE for i in range(0, numRoutes): - stdout.write('announce route 10.%s.%s.0/24 med 100 community %i:1 next-hop 172.16.1.%i\n' % ((peer+100), i, peer, peer)) + stdout.write( + "announce route 10.%s.%s.0/24 med 100 community %i:1 next-hop 172.16.1.%i\n" + % ((peer + 100), i, peer, peer) + ) stdout.flush() # Announce 1 overlapping route per peer -stdout.write('announce route 10.0.1.0/24 next-hop 172.16.1.%i\n' % peer) +stdout.write("announce route 10.0.1.0/24 next-hop 172.16.1.%i\n" % peer) stdout.flush() -#Loop endlessly to allow ExaBGP to continue running +# Loop endlessly to allow ExaBGP to continue running while True: sleep(1) diff --git a/tests/topotests/bgp_multiview_topo1/peer8/exa-receive.py b/tests/topotests/bgp_multiview_topo1/peer8/exa-receive.py index 5334ea5369..031ff455ca 100755 --- a/tests/topotests/bgp_multiview_topo1/peer8/exa-receive.py +++ b/tests/topotests/bgp_multiview_topo1/peer8/exa-receive.py @@ -4,7 +4,7 @@ exa-receive.py: Save received routes form ExaBGP into file """ -from sys import stdin,argv +from sys import stdin, argv from datetime import datetime # 1st arg is peer number @@ -13,12 +13,12 @@ # When the parent dies we are seeing continual newlines, so we only access so many before stopping counter = 0 -routesavefile = open('/tmp/peer%s-received.log' % peer, 'w') +routesavefile = open("/tmp/peer%s-received.log" % peer, "w") while True: try: line = stdin.readline() - timestamp = datetime.now().strftime('%Y%m%d_%H:%M:%S - ') + timestamp = datetime.now().strftime("%Y%m%d_%H:%M:%S - ") routesavefile.write(timestamp + line) routesavefile.flush() diff --git a/tests/topotests/bgp_multiview_topo1/peer8/exa-send.py b/tests/topotests/bgp_multiview_topo1/peer8/exa-send.py index 2de2bce40a..505b08d6aa 100755 --- a/tests/topotests/bgp_multiview_topo1/peer8/exa-send.py +++ b/tests/topotests/bgp_multiview_topo1/peer8/exa-send.py @@ -4,7 +4,7 @@ exa-send.py: Send a few testroutes with ExaBGP """ -from sys import stdout,argv +from sys import stdout, argv from time import sleep sleep(5) @@ -16,13 +16,16 @@ # Announce numRoutes different routes per PE for i in range(0, numRoutes): - stdout.write('announce route 10.%s.%s.0/24 med 100 community %i:1 next-hop 172.16.1.%i\n' % ((peer+100), i, peer, peer)) + stdout.write( + "announce route 10.%s.%s.0/24 med 100 community %i:1 next-hop 172.16.1.%i\n" + % ((peer + 100), i, peer, peer) + ) stdout.flush() # Announce 1 overlapping route per peer -stdout.write('announce route 10.0.1.0/24 next-hop 172.16.1.%i\n' % peer) +stdout.write("announce route 10.0.1.0/24 next-hop 172.16.1.%i\n" % peer) stdout.flush() -#Loop endlessly to allow ExaBGP to continue running +# Loop endlessly to allow ExaBGP to continue running while True: sleep(1) diff --git a/tests/topotests/bgp_multiview_topo1/r1/bgpd.conf b/tests/topotests/bgp_multiview_topo1/r1/bgpd.conf index 0c24942f43..5f65a54d7f 100644 --- a/tests/topotests/bgp_multiview_topo1/r1/bgpd.conf +++ b/tests/topotests/bgp_multiview_topo1/r1/bgpd.conf @@ -11,10 +11,9 @@ log file bgpd.log !debug bgp filters !debug bgp zebra ! -bgp multiple-instance -! router bgp 100 view 1 bgp router-id 172.30.1.1 + no bgp ebgp-requires-policy network 172.20.0.0/28 route-map local1 timers bgp 60 180 neighbor 172.16.1.1 remote-as 65001 @@ -23,6 +22,7 @@ router bgp 100 view 1 ! router bgp 100 view 2 bgp router-id 172.30.1.1 + no bgp ebgp-requires-policy network 172.20.0.0/28 route-map local2 timers bgp 60 180 neighbor 172.16.1.3 remote-as 65003 @@ -30,6 +30,7 @@ router bgp 100 view 2 ! router bgp 100 view 3 bgp router-id 172.30.1.1 + no bgp ebgp-requires-policy network 172.20.0.0/28 timers bgp 60 180 neighbor 172.16.1.6 remote-as 65006 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 7607fe986b..a2020ffa55 100755 --- a/tests/topotests/bgp_multiview_topo1/test_bgp_multiview_topo1.py +++ b/tests/topotests/bgp_multiview_topo1/test_bgp_multiview_topo1.py @@ -29,19 +29,19 @@ | peer1 | | peer2 | | peer3 | | peer4 | | peer5 | | AS 65001 | | AS 65002 | | AS 65003 | | AS 65004 | | AS 65005 | +-----+----+ +-----+----+ +-----+----+ +-----+----+ +-----+----+ - | .1 | .2 | .3 | .4 | .5 + | .1 | .2 | .3 | .4 | .5 | ______/ / / _________/ - \ / ________________/ / / - | | / _________________________/ / +----------+ + \ / ________________/ / / + | | / _________________________/ / +----------+ | | | / __________________________/ ___| peer6 | | | | | / ____________________________/.6 | AS 65006 | | | | | | / _________________________ +----------+ - | | | | | | / __________________ \ +----------+ + | | | | | | / __________________ \ +----------+ | | | | | | | / \ \___| peer7 | | | | | | | | | \ .7 | AS 65007 | ~~~~~~~~~~~~~~~~~~~~~ \ +----------+ ~~ SW1 ~~ \ +----------+ - ~~ Switch ~~ \_____| peer8 | + ~~ Switch ~~ \_____| peer8 | ~~ 172.16.1.0/24 ~~ .8 | AS 65008 | ~~~~~~~~~~~~~~~~~~~~~ +----------+ | @@ -49,7 +49,7 @@ +---------+---------+ | FRR R1 | | BGP Multi-View | - | Peer 1-3 > View 1 | + | Peer 1-3 > View 1 | | Peer 4-5 > View 2 | | Peer 6-8 > View 3 | +---------+---------+ @@ -60,7 +60,7 @@ ~~ 172.20.0.1/28 ~~ attributes (using route-map) ~~ Stub Switch ~~ ~~~~~~~~~~~~~ -""" +""" import os import re @@ -90,37 +90,39 @@ ## ##################################################### + class NetworkTopo(Topo): "BGP Multiview Topology 1" def build(self, **_opts): - exabgpPrivateDirs = ['/etc/exabgp', - '/var/run/exabgp', - '/var/log'] + exabgpPrivateDirs = ["/etc/exabgp", "/var/run/exabgp", "/var/log"] # Setup Routers router = {} for i in range(1, 2): - router[i] = topotest.addRouter(self, 'r%s' % i) + router[i] = topotest.addRouter(self, "r%s" % i) # Setup Provider BGP peers peer = {} for i in range(1, 9): - peer[i] = self.addHost('peer%s' % i, ip='172.16.1.%s/24' % i, - defaultRoute='via 172.16.1.254', - privateDirs=exabgpPrivateDirs) + peer[i] = self.addHost( + "peer%s" % i, + ip="172.16.1.%s/24" % i, + defaultRoute="via 172.16.1.254", + privateDirs=exabgpPrivateDirs, + ) # Setup Switches switch = {} # First switch is for a dummy interface (for local network) - switch[0] = self.addSwitch('sw0', cls=topotest.LegacySwitch) - self.addLink(switch[0], router[1], intfName2='r1-stub') + switch[0] = self.addSwitch("sw0", cls=topotest.LegacySwitch) + self.addLink(switch[0], router[1], intfName2="r1-stub") # Second switch is for connection to all peering routers - switch[1] = self.addSwitch('sw1', cls=topotest.LegacySwitch) - self.addLink(switch[1], router[1], intfName2='r1-eth0') + switch[1] = self.addSwitch("sw1", cls=topotest.LegacySwitch) + self.addLink(switch[1], router[1], intfName2="r1-eth0") for j in range(1, 9): - self.addLink(switch[1], peer[j], intfName2='peer%s-eth0' % j) + self.addLink(switch[1], peer[j], intfName2="peer%s-eth0" % j) ##################################################### @@ -129,6 +131,7 @@ def build(self, **_opts): ## ##################################################### + def setup_module(module): global topo, net @@ -136,7 +139,7 @@ def setup_module(module): print("******************************************\n") print("Cleanup old Mininet runs") - os.system('sudo mn -c > /dev/null 2>&1') + os.system("sudo mn -c > /dev/null 2>&1") thisDir = os.path.dirname(os.path.realpath(__file__)) topo = NetworkTopo() @@ -146,26 +149,26 @@ def setup_module(module): # Starting Routers for i in range(1, 2): - net['r%s' % i].loadConf('zebra', '%s/r%s/zebra.conf' % (thisDir, i)) - net['r%s' % i].loadConf('bgpd', '%s/r%s/bgpd.conf' % (thisDir, i)) - net['r%s' % i].startRouter() + net["r%s" % i].loadConf("zebra", "%s/r%s/zebra.conf" % (thisDir, i)) + net["r%s" % i].loadConf("bgpd", "%s/r%s/bgpd.conf" % (thisDir, i)) + net["r%s" % i].startRouter() # Starting PE Hosts and init ExaBGP on each of them - print('*** Starting BGP on all 8 Peers in 10s') - sleep(10) + print("*** Starting BGP on all 8 Peers") for i in range(1, 9): - net['peer%s' % i].cmd('cp %s/exabgp.env /etc/exabgp/exabgp.env' % thisDir) - net['peer%s' % i].cmd('cp %s/peer%s/* /etc/exabgp/' % (thisDir, i)) - net['peer%s' % i].cmd('chmod 644 /etc/exabgp/*') - net['peer%s' % i].cmd('chmod 755 /etc/exabgp/*.py') - net['peer%s' % i].cmd('chown -R exabgp:exabgp /etc/exabgp') - net['peer%s' % i].cmd('exabgp -e /etc/exabgp/exabgp.env /etc/exabgp/exabgp.cfg') - print('peer%s' % i), - print('') + net["peer%s" % i].cmd("cp %s/exabgp.env /etc/exabgp/exabgp.env" % thisDir) + net["peer%s" % i].cmd("cp %s/peer%s/* /etc/exabgp/" % (thisDir, i)) + net["peer%s" % i].cmd("chmod 644 /etc/exabgp/*") + net["peer%s" % i].cmd("chmod 755 /etc/exabgp/*.py") + net["peer%s" % i].cmd("chown -R exabgp:exabgp /etc/exabgp") + net["peer%s" % i].cmd("exabgp -e /etc/exabgp/exabgp.env /etc/exabgp/exabgp.cfg") + print("peer%s" % i), + print("") # For debugging after starting Quagga/FRR daemons, uncomment the next line # CLI(net) + def teardown_module(module): global net @@ -173,29 +176,29 @@ def teardown_module(module): print("******************************************\n") # Shutdown - clean up everything - print('*** Killing BGP on Peer routers') + print("*** Killing BGP on Peer routers") # Killing ExaBGP for i in range(1, 9): - net['peer%s' % i].cmd('kill `cat /var/run/exabgp/exabgp.pid`') + net["peer%s" % i].cmd("kill `cat /var/run/exabgp/exabgp.pid`") # End - Shutdown network net.stop() + def test_router_running(): global fatal_error global net # Skip if previous fatal error condition is raised - if (fatal_error != ""): + if fatal_error != "": pytest.skip(fatal_error) print("\n\n** Check if FRR/Quagga is running on each Router node") print("******************************************\n") - sleep(5) # Starting Routers for i in range(1, 2): - fatal_error = net['r%s' % i].checkRouterRunning() + fatal_error = net["r%s" % i].checkRouterRunning() assert fatal_error == "", fatal_error # For debugging after starting FRR/Quagga daemons, uncomment the next line @@ -209,22 +212,25 @@ def test_bgp_converge(): global net # Skip if previous fatal error condition is raised - if (fatal_error != ""): + if fatal_error != "": pytest.skip(fatal_error) # Wait for BGP to converge (All Neighbors in either Full or TwoWay State) print("\n\n** Verify for BGP to converge") print("******************************************\n") - timeout = 60 + timeout = 125 while timeout > 0: print("Timeout in %s: " % timeout), sys.stdout.flush() # Look for any node not yet converged for i in range(1, 2): for view in range(1, 4): - notConverged = net['r%s' % i].cmd('vtysh -c "show ip bgp view %s summary" 2> /dev/null | grep ^[0-9] | grep -v " 11$"' % view) + notConverged = net["r%s" % i].cmd( + 'vtysh -c "show ip bgp view %s summary" 2> /dev/null | grep ^[0-9] | grep -vP " 11\s+(\d+)$"' + % view + ) if notConverged: - print('Waiting for r%s, view %s' % (i, view)) + print("Waiting for r%s, view %s" % (i, view)) sys.stdout.flush() break if notConverged: @@ -233,17 +239,17 @@ def test_bgp_converge(): sleep(5) timeout -= 5 else: - print('Done') + print("Done") break else: # Bail out with error if a router fails to converge - bgpStatus = net['r%s' % i].cmd('vtysh -c "show ip bgp view %s summary"' % view) + bgpStatus = net["r%s" % i].cmd('vtysh -c "show ip bgp view %s summary"' % view) assert False, "BGP did not converge:\n%s" % bgpStatus - # Wait for an extra 30s to announce all routes - print('Waiting 30s for routes to be announced'); - sleep(30) - + # Wait for an extra 5s to announce all routes + print("Waiting 5s for routes to be announced") + sleep(5) + print("BGP converged.") # if timeout < 60: @@ -253,18 +259,19 @@ def test_bgp_converge(): # Make sure that all daemons are running for i in range(1, 2): - fatal_error = net['r%s' % i].checkRouterRunning() + fatal_error = net["r%s" % i].checkRouterRunning() assert fatal_error == "", fatal_error # For debugging after starting Quagga/FRR daemons, uncomment the next line # CLI(net) + def test_bgp_routingTable(): global fatal_error global net # Skip if previous fatal error condition is raised - if (fatal_error != ""): + if fatal_error != "": pytest.skip(fatal_error) thisDir = os.path.dirname(os.path.realpath(__file__)) @@ -276,56 +283,68 @@ def test_bgp_routingTable(): for view in range(1, 4): success = 0 # This glob pattern should work as long as number of views < 10 - for refTableFile in (glob.glob( - '%s/r%s/show_ip_bgp_view_%s*.ref' % (thisDir, i, view))): + for refTableFile in glob.glob( + "%s/r%s/show_ip_bgp_view_%s*.ref" % (thisDir, i, view) + ): 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) + expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1) # Actual output from router - actual = net['r%s' % i].cmd('vtysh -c "show ip bgp view %s" 2> /dev/null' % view).rstrip() - + actual = ( + net["r%s" % i] + .cmd('vtysh -c "show ip bgp view %s" 2> /dev/null' % view) + .rstrip() + ) + # Fix inconsitent spaces between 0.99.24 and newer versions of Quagga... - actual = re.sub('0 0', '0 0', actual) - actual = re.sub(r'([0-9]) 32768', r'\1 32768', actual) + actual = re.sub("0 0", "0 0", actual) + actual = re.sub( + r"([0-9]) 32768", r"\1 32768", actual + ) # Remove summary line (changed recently) - actual = re.sub(r'Total number.*', '', actual) - actual = re.sub(r'Displayed.*', '', actual) + actual = re.sub(r"Total number.*", "", actual) + actual = re.sub(r"Displayed.*", "", actual) actual = actual.rstrip() # Fix table version (ignore it) - actual = re.sub(r'(BGP table version is )[0-9]+', r'\1XXX', actual) + actual = re.sub(r"(BGP table version is )[0-9]+", r"\1XXX", actual) # Fix newlines (make them all the same) - actual = ('\n'.join(actual.splitlines()) + '\n').splitlines(1) + actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1) # Generate Diff - diff = topotest.get_textdiff(actual, expected, + diff = topotest.get_textdiff( + actual, + expected, title1="actual BGP routing table", - title2="expected BGP routing table") + title2="expected BGP routing table", + ) if diff: diffresult[refTableFile] = diff else: success = 1 print("template %s matched: r%s ok" % (refTableFile, i)) - break; + break if not success: - resultstr = 'No template matched.\n' + resultstr = "No template matched.\n" for f in diffresult.iterkeys(): resultstr += ( - 'template %s: r%s failed Routing Table Check for view %s:\n%s\n' - % (f, i, view, diffresult[f])) + "template %s: r%s failed Routing Table Check for view %s:\n%s\n" + % (f, i, view, diffresult[f]) + ) raise AssertionError( - "Routing Table verification failed for router r%s, view %s:\n%s" % (i, view, resultstr)) - + "Routing Table verification failed for router r%s, view %s:\n%s" + % (i, view, resultstr) + ) # Make sure that all daemons are running for i in range(1, 2): - fatal_error = net['r%s' % i].checkRouterRunning() + fatal_error = net["r%s" % i].checkRouterRunning() assert fatal_error == "", fatal_error # For debugging after starting FRR/Quagga daemons, uncomment the next line @@ -337,24 +356,26 @@ def test_shutdown_check_stderr(): global net # Skip if previous fatal error condition is raised - if (fatal_error != ""): + if fatal_error != "": pytest.skip(fatal_error) - if os.environ.get('TOPOTESTS_CHECK_STDERR') is None: - print("SKIPPED final check on StdErr output: Disabled (TOPOTESTS_CHECK_STDERR undefined)\n") - pytest.skip('Skipping test for Stderr output') + if os.environ.get("TOPOTESTS_CHECK_STDERR") is None: + print( + "SKIPPED final check on StdErr output: Disabled (TOPOTESTS_CHECK_STDERR undefined)\n" + ) + pytest.skip("Skipping test for Stderr output") thisDir = os.path.dirname(os.path.realpath(__file__)) print("\n\n** Verifying unexpected STDERR output from daemons") print("******************************************\n") - net['r1'].stopRouter() + net["r1"].stopRouter() - log = net['r1'].getStdErr('bgpd') + log = net["r1"].getStdErr("bgpd") if log: print("\nBGPd StdErr Log:\n" + log) - log = net['r1'].getStdErr('zebra') + log = net["r1"].getStdErr("zebra") if log: print("\nZebra StdErr Log:\n" + log) @@ -364,22 +385,26 @@ def test_shutdown_check_memleak(): global net # Skip if previous fatal error condition is raised - if (fatal_error != ""): + if fatal_error != "": pytest.skip(fatal_error) - if os.environ.get('TOPOTESTS_CHECK_MEMLEAK') is None: - print("SKIPPED final check on Memory leaks: Disabled (TOPOTESTS_CHECK_MEMLEAK undefined)\n") - pytest.skip('Skipping test for memory leaks') - + if os.environ.get("TOPOTESTS_CHECK_MEMLEAK") is None: + print( + "SKIPPED final check on Memory leaks: Disabled (TOPOTESTS_CHECK_MEMLEAK undefined)\n" + ) + pytest.skip("Skipping test for memory leaks") + thisDir = os.path.dirname(os.path.realpath(__file__)) - net['r1'].stopRouter() - net['r1'].report_memory_leaks(os.environ.get('TOPOTESTS_CHECK_MEMLEAK'), os.path.basename(__file__)) + net["r1"].stopRouter() + net["r1"].report_memory_leaks( + os.environ.get("TOPOTESTS_CHECK_MEMLEAK"), os.path.basename(__file__) + ) -if __name__ == '__main__': +if __name__ == "__main__": - setLogLevel('info') + setLogLevel("info") # To suppress tracebacks, either use the following pytest call or add "--tb=no" to cli # retval = pytest.main(["-s", "--tb=no"]) retval = pytest.main(["-s"]) diff --git a/tests/topotests/bgp_prefix_sid/__init__.py b/tests/topotests/bgp_prefix_sid/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bgp_prefix_sid/exabgp.env b/tests/topotests/bgp_prefix_sid/exabgp.env new file mode 100644 index 0000000000..6c554f5fa8 --- /dev/null +++ b/tests/topotests/bgp_prefix_sid/exabgp.env @@ -0,0 +1,53 @@ + +[exabgp.api] +encoder = text +highres = false +respawn = false +socket = '' + +[exabgp.bgp] +openwait = 60 + +[exabgp.cache] +attributes = true +nexthops = true + +[exabgp.daemon] +daemonize = true +pid = '/var/run/exabgp/exabgp.pid' +user = 'exabgp' + +[exabgp.log] +all = false +configuration = true +daemon = true +destination = '/var/log/exabgp.log' +enable = true +level = INFO +message = false +network = true +packets = false +parser = false +processes = true +reactor = true +rib = false +routes = false +short = false +timers = false + +[exabgp.pdb] +enable = false + +[exabgp.profile] +enable = false +file = '' + +[exabgp.reactor] +speed = 1.0 + +[exabgp.tcp] +acl = false +bind = '' +delay = 0 +once = false +port = 179 diff --git a/tests/topotests/bgp_prefix_sid/peer1/exabgp.cfg b/tests/topotests/bgp_prefix_sid/peer1/exabgp.cfg new file mode 100644 index 0000000000..5b55366a0e --- /dev/null +++ b/tests/topotests/bgp_prefix_sid/peer1/exabgp.cfg @@ -0,0 +1,103 @@ +group controller { + neighbor 10.0.0.1 { + router-id 10.0.0.101; + local-address 10.0.0.101; + local-as 2; + peer-as 1; + + family { + ipv4 nlri-mpls; + } + + static { + # ref: draft-ietf-idr-bgp-prefix-sid-27 + # + # IANA temporarily assigned the following: + # attribute code type (suggested value: 40) to + # the BGP Prefix-SID attribute + # + # 0 1 2 3 + # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # | Type | Length | RESERVED | + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # | Flags | Label Index | + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # | Label Index | + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # Figure. Label-Index TLV (Prefix-SID type-1) + # + # 0 1 2 3 + # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # | Type | Length | Flags | + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # | Flags | + # +-+-+-+-+-+-+-+-+ + # + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # | SRGB 1 (6 octets) | + # | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # | | + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # | SRGB n (6 octets) | + # | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # | | + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # Figure. Originator SRGB TLV (Prefix-SID type-3) + + # ExaBGP generic-attribute binary pattern: + # Attribute-type: 0x28 (40:BGP_PREFIX_SID) + # Attribute-flag: 0xc0 (Option, Transitive) + # Attribute-body: Label-Index TLV and Originator SRGB TLV + # Label-Index TLV: 0x01000700000000000001 + # Type (08bit): 0x01 + # Length (16bit): 0x0007 + # RESERVED (08bit): 0x00 + # Flags (16bit): 0x0000 + # Label Index (32bit): 0x00000001 + # Originator SRGB TLV: 0x03000800000c350000000a + # Type (08bit): 0x03 + # Length (16bit): 0x0008 (nb-SRGB is 1) + # Flags (16bit): 0x0000 + # SRGB1 (48bit): 0x0c3500:0x00000a (800000-800010 is SRGB1) + route 3.0.0.1/32 next-hop 10.0.0.101 label [800001] attribute [0x28 0xc0 0x0100070000000000000103000800000c350000000a]; + + # ExaBGP generic-attribute binary pattern: + # Attribute-type: 0x28 (40:BGP_PREFIX_SID) + # Attribute-flag: 0xc0 (Option, Transitive) + # Attribute-body: Label-Index TLV and Originator SRGB TLV + # Label-Index TLV: 0x01000700000000000001 + # Type (08bit): 0x01 + # Length (16bit): 0x0007 + # RESERVED (08bit): 0x00 + # Flags (16bit): 0x0000 + # Label Index (32bit): 0x00000002 + # Originator SRGB TLV: 0x03000800000c350000000a + # Type (08bit): 0x03 + # Length (16bit): 0x0008 (nb-SRGB is 1) + # Flags (16bit): 0x0000 + # SRGB1 (48bit): 0x0c3500:0x00000a (800000-800010 is SRGB1) + route 3.0.0.2/32 next-hop 10.0.0.101 label [800002] attribute [0x28 0xc0 0x0100070000000000000203000800000c350000000a]; + + # ExaBGP generic-attribute binary pattern: + # Attribute-type: 0x28 (40:BGP_PREFIX_SID) + # Attribute-flag: 0xc0 (Option, Transitive) + # Attribute-body: Label-Index TLV and Originator SRGB TLV + # Label-Index TLV: 0x01000700000000000001 + # Type (08bit): 0x01 + # Length (16bit): 0x0007 + # RESERVED (08bit): 0x00 + # Flags (16bit): 0x0000 + # Label Index (32bit): 0x00000003 + # Originator SRGB TLV: 0x03000800000c350000000a + # Type (08bit): 0x03 + # Length (16bit): 0x0008 (nb-SRGB is 1) + # Flags (16bit): 0x0000 + # SRGB1 (48bit): 0x0c3500:0x00000a (800000-800010 is SRGB1) + route 3.0.0.3/32 next-hop 10.0.0.101 label [800003] attribute [0x28 0xc0 0x0100070000000000000303000800000c350000000a]; + } + } +} diff --git a/tests/topotests/bgp_prefix_sid/peer2/exa-receive.py b/tests/topotests/bgp_prefix_sid/peer2/exa-receive.py new file mode 100755 index 0000000000..f1ec9fa5ba --- /dev/null +++ b/tests/topotests/bgp_prefix_sid/peer2/exa-receive.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python + +""" +exa-receive.py: Save received routes form ExaBGP into file +""" + +from sys import stdin, argv +from datetime import datetime + +# 1st arg is peer number +peer = int(argv[1]) + +# When the parent dies we are seeing continual newlines, so we only access so many before stopping +counter = 0 + +routesavefile = open("/tmp/peer%s-received.log" % peer, "w") + +while True: + try: + line = stdin.readline() + routesavefile.write(line) + routesavefile.flush() + + if line == "": + counter += 1 + if counter > 100: + break + continue + + counter = 0 + except KeyboardInterrupt: + pass + except IOError: + # most likely a signal during readline + pass + +routesavefile.close() diff --git a/tests/topotests/bgp_prefix_sid/peer2/exabgp.cfg b/tests/topotests/bgp_prefix_sid/peer2/exabgp.cfg new file mode 100644 index 0000000000..dabd88e03d --- /dev/null +++ b/tests/topotests/bgp_prefix_sid/peer2/exabgp.cfg @@ -0,0 +1,19 @@ +group controller { + + process receive-routes { + run "/etc/exabgp/exa-receive.py 2"; + receive-routes; + encoder json; + } + + neighbor 10.0.0.1 { + router-id 10.0.0.102; + local-address 10.0.0.102; + local-as 3; + peer-as 1; + + family { + ipv4 nlri-mpls; + } + } +} diff --git a/tests/topotests/bgp_prefix_sid/r1/bgpd.conf b/tests/topotests/bgp_prefix_sid/r1/bgpd.conf new file mode 100644 index 0000000000..06bdc31f8c --- /dev/null +++ b/tests/topotests/bgp_prefix_sid/r1/bgpd.conf @@ -0,0 +1,15 @@ +log stdout notifications +log commands +! +router bgp 1 + bgp router-id 10.0.0.1 + no bgp default ipv4-unicast + no bgp ebgp-requires-policy + neighbor 10.0.0.101 remote-as 2 + neighbor 10.0.0.102 remote-as 3 + ! + address-family ipv4 labeled-unicast + neighbor 10.0.0.101 activate + neighbor 10.0.0.102 activate + exit-address-family +! diff --git a/tests/topotests/bgp_prefix_sid/r1/zebra.conf b/tests/topotests/bgp_prefix_sid/r1/zebra.conf new file mode 100644 index 0000000000..0cd26052f2 --- /dev/null +++ b/tests/topotests/bgp_prefix_sid/r1/zebra.conf @@ -0,0 +1,7 @@ +hostname r1 +! +interface r1-eth0 + ip address 10.0.0.1/24 + no shutdown +! +line vty diff --git a/tests/topotests/bgp_prefix_sid/test_bgp_prefix_sid.py b/tests/topotests/bgp_prefix_sid/test_bgp_prefix_sid.py new file mode 100755 index 0000000000..3a6aefe7ee --- /dev/null +++ b/tests/topotests/bgp_prefix_sid/test_bgp_prefix_sid.py @@ -0,0 +1,185 @@ +#!/usr/bin/env python + +# +# test_bgp_prefix_sid.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2020 by LINE Corporation +# Copyright (c) 2020 by Hiroki Shirokura +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_bgp_prefix_sid.py: Test BGP topology with EBGP on prefix-sid +""" + +import json +import os +import sys +import functools +import pytest + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from mininet.topo import Topo + + +class TemplateTopo(Topo): + def build(self, **_opts): + tgen = get_topogen(self) + router = tgen.add_router("r1") + switch = tgen.add_switch("s1") + switch.add_link(router) + + switch = tgen.gears["s1"] + peer1 = tgen.add_exabgp_peer( + "peer1", ip="10.0.0.101", defaultRoute="via 10.0.0.1" + ) + peer2 = tgen.add_exabgp_peer( + "peer2", ip="10.0.0.102", defaultRoute="via 10.0.0.1" + ) + switch.add_link(peer1) + switch.add_link(peer2) + + +def setup_module(module): + tgen = Topogen(TemplateTopo, module.__name__) + tgen.start_topology() + + router = tgen.gears["r1"] + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format("r1")) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format("r1")) + ) + router.start() + + logger.info("starting exaBGP on peer1") + peer_list = tgen.exabgp_peers() + for pname, peer in peer_list.iteritems(): + peer_dir = os.path.join(CWD, pname) + env_file = os.path.join(CWD, "exabgp.env") + logger.info("Running ExaBGP peer") + peer.start(peer_dir, env_file) + logger.info(pname) + + +def teardown_module(module): + tgen = get_topogen() + tgen.stop_topology() + + +def test_r1_receive_and_advertise_prefix_sid_type1(): + tgen = get_topogen() + router = tgen.gears["r1"] + + def _check_type1_r1(router, prefix, remoteLabel, labelIndex): + output = router.vtysh_cmd( + "show bgp ipv4 labeled-unicast {} json".format(prefix) + ) + output = json.loads(output) + expected = { + "prefix": prefix, + "advertisedTo": {"10.0.0.101": {}, "10.0.0.102": {}}, + "paths": [ + {"valid": True, "remoteLabel": remoteLabel, "labelIndex": labelIndex,} + ], + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_check_type1_r1, router, "3.0.0.1/32", 800001, 1) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert result is None, 'Failed _check_type1_r1 in "{}"'.format(router) + + test_func = functools.partial(_check_type1_r1, router, "3.0.0.2/32", 800002, 2) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert result is None, 'Failed _check_type1_r1 in "{}"'.format(router) + + +def exabgp_get_update_prefix(filename, afi, nexthop, prefix): + with open("/tmp/peer2-received.log") as f: + for line in f.readlines(): + output = json.loads(line) + ret = output.get("neighbor") + if ret is None: + continue + ret = ret.get("message") + if ret is None: + continue + ret = ret.get("update") + if ret is None: + continue + ret = ret.get("announce") + if ret is None: + continue + ret = ret.get(afi) + if ret is None: + continue + ret = ret.get(nexthop) + if ret is None: + continue + ret = ret.get(prefix) + if ret is None: + continue + return output + return "Not found" + + +def test_peer2_receive_prefix_sid_type1(): + tgen = get_topogen() + peer2 = tgen.gears["peer2"] + + def _check_type1_peer2(prefix, labelindex): + output = exabgp_get_update_prefix( + "/tmp/peer2-received.log", "ipv4 nlri-mpls", "10.0.0.101", prefix + ) + expected = { + "type": "update", + "neighbor": { + "ip": "10.0.0.1", + "message": { + "update": { + "attribute": { + "attribute-0x28-0xE0": "0x010007000000{:08x}".format( + labelindex + ) + }, + "announce": {"ipv4 nlri-mpls": {"10.0.0.101": {}}}, + } + }, + }, + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_check_type1_peer2, "3.0.0.1/32", labelindex=1) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert result is None, 'Failed _check_type1_peer2 in "{}"'.format("peer2") + + test_func = functools.partial(_check_type1_peer2, "3.0.0.2/32", labelindex=2) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert result is None, 'Failed _check_type1_peer2 in "{}"'.format("peer2") + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + ret = pytest.main(args) + sys.exit(ret) diff --git a/tests/topotests/bgp_recursive_route_ebgp_multi_hop/bgp_recursive_route_ebgp_multi_hop.json b/tests/topotests/bgp_recursive_route_ebgp_multi_hop/bgp_recursive_route_ebgp_multi_hop.json new file mode 100644 index 0000000000..52995a06e5 --- /dev/null +++ b/tests/topotests/bgp_recursive_route_ebgp_multi_hop/bgp_recursive_route_ebgp_multi_hop.json @@ -0,0 +1,321 @@ +{ + "address_types": [ + "ipv4", + "ipv6" + ], + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 24, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:db8:f::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {} + } + }, + "r3": { + "dest_link": { + "r1": { + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r3": { + "dest_link": { + "r1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r4": { + "dest_link": { + "r2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r4": { + "dest_link": { + "r2": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {} + } + }, + "r4": { + "dest_link": { + "r3": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r4": { + "dest_link": { + "r3": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + }, + "r4": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "400", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": {} + } + }, + "r3": { + "dest_link": { + "r4": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r3": { + "dest_link": { + "r4": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + } + } +} 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 new file mode 100755 index 0000000000..fef6eb71dc --- /dev/null +++ b/tests/topotests/bgp_recursive_route_ebgp_multi_hop/test_bgp_recursive_route_ebgp_multi_hop.py @@ -0,0 +1,2353 @@ +#!/usr/bin/python +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +Following tests are covered to test bgp recursive route and ebgp +multi-hop functionality: + +1. Verify that BGP routes are installed in iBGP peer, only when there + is a recursive route for next-hop reachability. +2. Verify that any BGP prefix received with next hop as self-ip is + not installed in BGP RIB or FIB table. +3. Verify password authentication for eBGP and iBGP peers. +4. Verify that for a BGP prefix next-hop information doesn't change + when same prefix is received from another peer via recursive lookup. +5. Verify that BGP path attributes are present in CLI outputs and + JSON format, even if set to default. +6. Verifying the BGP peering between loopback and physical link's IP + of 2 peer routers. +7. Verify that BGP Active/Standby/Pre-emption/ECMP. +""" + +import os +import sys +import time +import json +import pytest +from time import sleep +from copy import deepcopy + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from mininet.topo import Topo +from lib.topogen import Topogen, get_topogen + +# Import topoJson from lib, to create topology and initial configuration +from lib.common_config import ( + start_topology, + write_test_header, + apply_raw_config, + write_test_footer, + reset_config_on_routers, + verify_rib, + create_static_routes, + check_address_types, + step, + create_route_maps, + create_interface_in_kernel, + shutdown_bringup_interface, + addKernelRoute, + delete_route_maps, + kill_mininet_routers_process, +) +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + create_router_bgp, + clear_bgp_and_verify, + verify_bgp_rib, + verify_bgp_convergence_from_running_config, + modify_as_number, + verify_bgp_attributes, + clear_bgp, +) +from lib.topojson import build_topo_from_json, build_config_from_json + +# Reading the data from JSON File for topology and configuration creation +jsonFile = "{}/bgp_recursive_route_ebgp_multi_hop.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError : + logger.info("Could not read file:", jsonFile) + +# Global variables +BGP_CONVERGENCE = False +KEEP_ALIVE_TIMER = 2 +HOLD_DOWN_TIMER = 6 +ADDR_TYPES = check_address_types() +NETWORK = { + "ipv4": ["100.1.1.1/32", "100.1.1.2/32"], + "ipv6": ["100::1/128", "100::2/128"], +} + +RECUR_NEXT_HOP = { + "N1": {"ipv4": "20.20.20.20/24", "ipv6": "20:20::20:20/120"}, + "N2": {"ipv4": "30.30.30.30/24", "ipv6": "30:30::30:30/120"}, + "N3": {"ipv4": "40.40.40.40/24", "ipv6": "40:40::40:40/120"}, +} + +CHANGED_NEXT_HOP = { + "4thOctate": {"ipv4": "10.0.1.250/24", "ipv6": "fd00:0:0:1::100/64"}, + "3rdOctate": {"ipv4": "10.0.10.2/24", "ipv6": "fd00:0:0:10::2/64"}, +} + +Loopabck_IP = { + "Lo_R1": {"ipv4": "1.1.1.1/32", "ipv6": "1:1::1:1/128"}, + "Lo_R4": {"ipv4": "4.4.4.4/32", "ipv6": "4:4::4:4/128"}, +} + + +class CreateTopo(Topo): + """ + Test BasicTopo - topology 1 + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + """Build function""" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(CreateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + global BGP_CONVERGENCE + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error : {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# +# Tests starting +# +##################################################### + + +def test_recursive_routes_iBGP_peer_p1(request): + """ + Verify that BGP routes are installed in iBGP peer, only + when there is a recursive route for next-hop reachability. + """ + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Initial config :Configure BGP neighborship between R1 and R3.") + reset_config_on_routers(tgen) + + dut = "r1" + protocol = "static" + + step( + "Configure static routes on R1 pointing next-hop as connected" + "link between R1 & R3's IP" + ) + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r1": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": topo["routers"]["r3"]["links"]["r1"][ + addr_type + ].split("/")[0], + } + ] + } + } + result = create_static_routes(tgen, input_dict_4) + + step( + "Verify on router R1 that these static routes are " + "installed in RIB+FIB of R1" + ) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0], + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error : {}".format( + tc_name, result + ) + + step("Redistribute these static routes in BGP on router R1") + input_dict_2 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2) + + step( + "Verify on router R1 that these static routes are installed" + "in RIB table as well" + ) + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r1": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": topo["routers"]["r3"]["links"]["r1"][ + addr_type + ].split("/")[0], + } + ] + } + } + result = verify_bgp_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0], + ) + assert result is True, "Testcase : Failed \n Error : {}".format(tc_name, result) + + step( + "Configure a static routes for next hop IP on R2 via multiple" + "recursive static routes" + ) + dut = "r2" + create_interface_in_kernel( + tgen, dut, "lo", "40.40.40.50", netmask="255.255.255.0", create=True + ) + create_interface_in_kernel( + tgen, dut, "lo", "40:40::40:50", netmask="120", create=True + ) + for addr_type in ADDR_TYPES: + input_dict_3 = { + "r2": { + "static_routes": [ + { + "network": topo["routers"]["r3"]["links"]["r1"][addr_type], + "next_hop": RECUR_NEXT_HOP["N1"][addr_type].split("/")[0], + }, + { + "network": RECUR_NEXT_HOP["N1"][addr_type], + "next_hop": RECUR_NEXT_HOP["N2"][addr_type].split("/")[0], + }, + { + "network": RECUR_NEXT_HOP["N2"][addr_type], + "next_hop": RECUR_NEXT_HOP["N3"][addr_type].split("/")[0], + }, + ] + } + } + result = create_static_routes(tgen, input_dict_3) + assert result is True, "Testcase : Failed \n Error : {}".format(tc_name, result) + + step("verify if redistributed routes are now installed in FIB of R2") + result = verify_rib( + tgen, + addr_type, + "r2", + input_dict_4, + next_hop=topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0], + protocol="bgp", + ) + assert result is True, "Testcase : Failed \n Error : {}".format(tc_name, result) + + step("Delete 1 route from static recursive for the next-hop IP") + dut = "r2" + for addr_type in ADDR_TYPES: + input_dict_3 = { + "r2": { + "static_routes": [ + { + "network": RECUR_NEXT_HOP["N1"][addr_type], + "next_hop": RECUR_NEXT_HOP["N2"][addr_type].split("/")[0], + "delete": True, + } + ] + } + } + result = create_static_routes(tgen, input_dict_3) + assert result is True, "Testcase : Failed \n Error : {}".format(tc_name, result) + + step("Verify that redistributed routes are withdrawn from FIB of R2") + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0], + protocol="bgp", + expected=False + ) + assert result is not True, "Testcase : Failed \n Error : {}".format( + tc_name, result + ) + + step("Reconfigure the same static route on R2 again") + dut = "r2" + for addr_type in ADDR_TYPES: + input_dict_3 = { + "r2": { + "static_routes": [ + { + "network": RECUR_NEXT_HOP["N1"][addr_type], + "next_hop": RECUR_NEXT_HOP["N2"][addr_type].split("/")[0], + } + ] + } + } + result = create_static_routes(tgen, input_dict_3) + assert result is True, "Testcase : Failed \n Error : {}".format(tc_name, result) + + step("Verify that redistributed routes are again installed" "in FIB of R2") + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0], + protocol="bgp", + ) + assert result is True, "Testcase : Failed \n Error : {}".format(tc_name, result) + + step("Configure static route with changed next-hop from same subnet") + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r1": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": topo["routers"]["r3"]["links"]["r1"][ + addr_type + ].split("/")[0], + "delete": True, + }, + { + "network": NETWORK[addr_type], + "next_hop": CHANGED_NEXT_HOP["4thOctate"][addr_type].split("/")[ + 0 + ], + }, + ] + } + } + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase : Failed \n Error : {}".format(tc_name, result) + + result = verify_rib(tgen, addr_type, "r1", input_dict_4, protocol="static") + assert result is True, "Testcase {} : Failed \n Error : {}".format( + tc_name, result + ) + + step( + "Verify that redistributed routes are not withdrawn as changed" + "next-hop IP, belongs to the same subnet" + ) + result = verify_rib(tgen, addr_type, "r2", input_dict_4, protocol="bgp") + assert result is True, "Testcase {} : Failed \n Error : {}".format( + tc_name, result + ) + + step("Configure static route with changed next-hop from different subnet") + dut = "r1" + create_interface_in_kernel( + tgen, dut, "lo10", "10.0.10.10", netmask="255.255.255.0", create=True + ) + create_interface_in_kernel( + tgen, dut, "lo10", "fd00:0:0:10::104", netmask="64", create=True + ) + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r1": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": CHANGED_NEXT_HOP["4thOctate"][addr_type].split("/")[ + 0 + ], + "delete": True, + }, + { + "network": NETWORK[addr_type], + "next_hop": CHANGED_NEXT_HOP["3rdOctate"][addr_type].split("/")[ + 0 + ], + }, + ] + } + } + result = create_static_routes(tgen, input_dict_4) + assert result is True, "Testcase : Failed \n Error : {}".format(tc_name, result) + + result = verify_rib(tgen, addr_type, "r1", input_dict_4, protocol="static") + assert result is True, "Testcase {} : Failed \n Error : {}".format( + tc_name, result + ) + + step( + "Verify that redistributed routes are withdrawn as changed " + "next-hop IP, belongs to different subnet" + ) + result = verify_rib( + tgen, addr_type, "r2", input_dict_4, protocol="bgp", expected=False + ) + assert result is not True, "Testcase {} : Failed \n Error : {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_next_hop_as_self_ip_p1(request): + """ + Verify that any BGP prefix received with next hop as + self-ip is not installed in BGP RIB or FIB table. + """ + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Initial config :Configure BGP neighborship between R1 and R3.") + reset_config_on_routers(tgen) + + step( + "Configure static routes on R1 with a next-hop IP belonging" + "to the same subnet of R2's link IP." + ) + dut = "r1" + create_interface_in_kernel( + tgen, + dut, + "lo10", + topo["routers"]["r4"]["links"]["r2"]["ipv4"].split("/")[0], + netmask="255.255.255.0", + create=True, + ) + create_interface_in_kernel( + tgen, + dut, + "lo10", + topo["routers"]["r4"]["links"]["r2"]["ipv6"].split("/")[0], + netmask="64", + create=True, + ) + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r1": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": topo["routers"]["r2"]["links"]["r4"][ + addr_type + ].split("/")[0], + } + ] + } + } + result = create_static_routes(tgen, input_dict_4) + + step("Verify that static routes are installed in RIB and FIB of R1") + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_4, + next_hop=topo["routers"]["r2"]["links"]["r4"][addr_type].split("/")[0], + protocol="static", + ) + assert result is True, "Testcase {} : Failed \n Error : {}".format( + tc_name, result + ) + + step("Redistribute static routes into BGP on R1") + input_dict_2 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2) + + step( + "Verify that R2 denies the prefixes received in update message," + "as next-hop IP belongs to connected interface" + ) + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r1": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": topo["routers"]["r2"]["links"]["r4"][ + addr_type + ].split("/")[0], + } + ] + } + } + result = verify_bgp_rib( + tgen, + addr_type, + "r2", + input_dict_4, + next_hop=topo["routers"]["r2"]["links"]["r4"][addr_type].split("/")[0], + expected=False + ) + assert result is not True, "Testcase : Failed \n Error : {}".format( + tc_name, result + ) + + step("Shut interface on R2 that has IP from the subnet as BGP next-hop") + intf_r2_r4 = topo["routers"]["r2"]["links"]["r4"]["interface"] + shutdown_bringup_interface(tgen, "r2", intf_r2_r4) + + for addr_type in ADDR_TYPES: + clear_bgp(tgen, addr_type, "r2") + step( + "Verify that redistributed routes now appear only in BGP table," + "as next-hop IP is no more active on R2" + ) + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r1": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": topo["routers"]["r2"]["links"]["r4"][ + addr_type + ].split("/")[0], + } + ] + } + } + result = verify_bgp_rib( + tgen, + addr_type, + "r2", + input_dict_4, + next_hop=topo["routers"]["r2"]["links"]["r4"][addr_type].split("/")[0], + ) + assert result is True, "Testcase : Failed \n Error : {}".format(tc_name, result) + + step("No shutdown interface on R2 which was shut in previous step") + intf_r2_r4 = topo["routers"]["r2"]["links"]["r4"]["interface"] + shutdown_bringup_interface(tgen, "r2", intf_r2_r4, ifaceaction=True) + + step( + "Verify that R2 dosn't install prefixes RIB to FIB as next-hop" + "interface is up now" + ) + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r1": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": topo["routers"]["r2"]["links"]["r4"][ + addr_type + ].split("/")[0], + } + ] + } + } + result = verify_bgp_rib( + tgen, + addr_type, + "r2", + input_dict_4, + next_hop=topo["routers"]["r2"]["links"]["r4"][addr_type].split("/")[0], + ) + assert result is True, "Testcase : Failed \n Error : {}".format(tc_name, result) + result = verify_rib( + tgen, + addr_type, + "r2", + input_dict_4, + next_hop=topo["routers"]["r2"]["links"]["r4"][addr_type].split("/")[0], + expected=False + ) + assert result is not True, "Testcase : Failed \n Error : {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_next_hop_with_recursive_lookup_p1(request): + """ + Verify that for a BGP prefix next-hop information doesn't change + when same prefix is received from another peer via recursive lookup. + """ + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Initial config :Configure BGP neighborship between R1 and R3.") + reset_config_on_routers(tgen) + + step("Verify that BGP peering comes up.") + + result = verify_bgp_convergence_from_running_config(tgen) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step("Do redistribute connected on router R3.") + input_dict_1 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "connected"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "connected"}]} + }, + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step("Verify that R1 receives all connected") + for addr_type in ADDR_TYPES: + routes = { + "ipv4": ["1.0.3.17/32", "10.0.1.0/24", "10.0.3.0/24"], + "ipv6": ["2001:db8:f::3:17/128", "fd00:0:0:1::/64", "fd00:0:0:3::/64"], + } + input_dict = {"r1": {"static_routes": [{"network": routes[addr_type]}]}} + result = verify_rib(tgen, addr_type, "r1", input_dict, protocol="bgp") + assert result is True, "Testcase {} : Failed \n Error : {}".format( + tc_name, result + ) + + step( + "Configure a BGP neighborship between R1 and R4, directly via " + "eBGP multi-hop." + ) + r1_local_as = topo["routers"]["r1"]["bgp"]["local_as"] + r1_r3_addr = topo["routers"]["r1"]["links"]["r3"] + r4_local_as = topo["routers"]["r4"]["bgp"]["local_as"] + r4_r3_addr = topo["routers"]["r4"]["links"]["r3"] + ebgp_multi_hop = 3 + + for addr_type in ADDR_TYPES: + raw_config = { + "r1": { + "raw_config": [ + "router bgp {}".format(r1_local_as), + "neighbor {} remote-as {}".format( + r4_r3_addr[addr_type].split("/")[0], r4_local_as + ), + "neighbor {} timers {} {}".format( + r4_r3_addr[addr_type].split("/")[0], + KEEP_ALIVE_TIMER, + HOLD_DOWN_TIMER, + ), + "neighbor {} ebgp-multihop {}".format( + r4_r3_addr[addr_type].split("/")[0], ebgp_multi_hop + ), + ] + }, + "r4": { + "raw_config": [ + "router bgp {}".format(r4_local_as), + "neighbor {} remote-as {}".format( + r1_r3_addr[addr_type].split("/")[0], r1_local_as + ), + "neighbor {} timers {} {}".format( + r1_r3_addr[addr_type].split("/")[0], + KEEP_ALIVE_TIMER, + HOLD_DOWN_TIMER, + ), + "neighbor {} ebgp-multihop {}".format( + r1_r3_addr[addr_type].split("/")[0], ebgp_multi_hop + ), + ] + }, + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error : {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + if addr_type == "ipv4": + raw_config = { + "r1": { + "raw_config": [ + "router bgp {}".format(r1_local_as), + "address-family {} unicast".format(addr_type), + "no neighbor {} activate".format( + r4_r3_addr["ipv6"].split("/")[0] + ), + ] + }, + "r4": { + "raw_config": [ + "router bgp {}".format(r4_local_as), + "address-family {} unicast".format(addr_type), + "no neighbor {} activate".format( + r1_r3_addr["ipv6"].split("/")[0] + ), + ] + }, + } + else: + raw_config = { + "r1": { + "raw_config": [ + "router bgp {}".format(r1_local_as), + "address-family {} unicast".format(addr_type), + "neighbor {} activate".format( + r4_r3_addr[addr_type].split("/")[0] + ), + ] + }, + "r4": { + "raw_config": [ + "router bgp {}".format(r4_local_as), + "address-family {} unicast".format(addr_type), + "neighbor {} activate".format( + r1_r3_addr[addr_type].split("/")[0] + ), + ] + }, + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error : {}".format(tc_name, result) + + step("Verify that BGP session between R1 and R4 comes up" "(recursively via R3).") + result = verify_bgp_convergence_from_running_config(tgen) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step("Do redistribute connected on router R4.") + input_dict_1 = { + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "connected"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "connected"}]} + }, + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step( + "Verify that R1 now receives BGP prefix of link r3-r4 via 2 " + "next-hops R3 and R4. however do not install with NHT R4 in FIB." + ) + for addr_type in ADDR_TYPES: + routes = {"ipv4": ["10.0.3.0/24"], "ipv6": ["fd00:0:0:3::/64"]} + + input_dict = {"r1": {"static_routes": [{"network": routes[addr_type]}]}} + next_hop = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0] + + result = verify_rib( + tgen, addr_type, "r1", input_dict, protocol="bgp", next_hop=next_hop + ) + assert result is True, "Testcase {} : Failed \n Error : {}".format( + tc_name, result + ) + + step("Clear bgp sessions from R1 using 'clear ip bgp *'") + for addr_type in ADDR_TYPES: + clear_bgp(tgen, addr_type, "r1") + + step( + "Verify that prefix of link r3-r4 is again learned via 2 " + "next-hops (from R3 and R4 directly)" + ) + for addr_type in ADDR_TYPES: + routes = {"ipv4": ["10.0.3.0/24"], "ipv6": ["fd00:0:0:3::/64"]} + + input_dict = {"r1": {"static_routes": [{"network": routes[addr_type]}]}} + next_hop = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0] + + result = verify_rib( + tgen, addr_type, "r1", input_dict, protocol="bgp", next_hop=next_hop + ) + assert result is True, "Testcase {} : Failed \n Error : {}".format( + tc_name, result + ) + + step("Remove redistribution from router R3.") + input_dict_1 = { + "r3": { + "bgp": { + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "connected", "delete": True} + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "connected", "delete": True} + ] + } + }, + }, + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step( + "Verify that peering between R1-R4 goes down and prefix " + "of link r3-r4, with NHT R4 is withdrawn." + ) + + logger.info("Sleeping for holddowntimer: {}".format(HOLD_DOWN_TIMER)) + sleep(HOLD_DOWN_TIMER + 1) + + result = verify_bgp_convergence_from_running_config(tgen, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n" "BGP is converged \n Error : {}".format(tc_name, result) + logger.info("Expected behaviour: {}".format(result)) + + for addr_type in ADDR_TYPES: + routes = {"ipv4": ["10.0.3.0/24"], "ipv6": ["fd00:0:0:3::/64"]} + + input_dict = {"r1": {"static_routes": [{"network": routes[addr_type]}]}} + next_hop = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0] + + result = verify_rib( + tgen, addr_type, "r1", input_dict, protocol="bgp", next_hop=next_hop + ) + assert result is True, "Testcase {} : Failed \n Error : {}".format( + tc_name, result + ) + + step("Re-apply redistribution on R3.") + + input_dict_1 = { + "r3": { + "bgp": { + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "connected"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "connected"}]} + }, + }, + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step( + "Verify that peering between R1-R4 goes down and prefix " + "of link r3-r4 with NHT R4 is withdrawn." + ) + + result = verify_bgp_convergence_from_running_config(tgen) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + routes = {"ipv4": ["10.0.3.0/24"], "ipv6": ["fd00:0:0:3::/64"]} + + input_dict = {"r1": {"static_routes": [{"network": routes[addr_type]}]}} + next_hop = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0] + + result = verify_rib( + tgen, addr_type, "r1", input_dict, protocol="bgp", next_hop=next_hop + ) + assert result is True, "Testcase {} : Failed \n Error : {}".format( + tc_name, result + ) + + step("Remove redistribution from router R4.") + + input_dict_1 = { + "r4": { + "bgp": { + "local_as": "400", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "connected", "delete": True} + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "connected", "delete": True} + ] + } + }, + }, + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step( + "Verify that peering between R1-R4 doesn't go down but prefix " + "of link r3-r4 with NHT R4 is withdrawn." + ) + + logger.info("Sleeping for holddowntimer: {}".format(HOLD_DOWN_TIMER)) + sleep(HOLD_DOWN_TIMER + 1) + + result = verify_bgp_convergence_from_running_config(tgen) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + routes = {"ipv4": ["10.0.3.0/24"], "ipv6": ["fd00:0:0:3::/64"]} + + input_dict = {"r1": {"static_routes": [{"network": routes[addr_type]}]}} + next_hop = topo["routers"]["r4"]["links"]["r3"][addr_type].split("/")[0] + + result = verify_rib( + tgen, + addr_type, + "r1", + input_dict, + protocol="bgp", + next_hop=next_hop, + expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Route is still present \n Error : {}".format(tc_name, result) + ) + + step("Re-apply redistribution on R4.") + + input_dict_1 = { + "r4": { + "bgp": { + "local_as": "400", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "connected", "delete": True} + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "connected", "delete": True} + ] + } + }, + }, + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step("Verify that prefix of link r3-r4 is re-learned via NHT R4.") + + for addr_type in ADDR_TYPES: + routes = {"ipv4": ["10.0.3.0/24"], "ipv6": ["fd00:0:0:3::/64"]} + + input_dict = {"r1": {"static_routes": [{"network": routes[addr_type]}]}} + next_hop = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0] + + result = verify_rib( + tgen, addr_type, "r1", input_dict, protocol="bgp", next_hop=next_hop + ) + assert result is True, "Testcase {} : Failed \n Error : {}".format( + tc_name, result + ) + + step("Toggle the interface on R3(ifconfig 192.34).") + + intf_r3_r4 = topo["routers"]["r3"]["links"]["r4"]["interface"] + shutdown_bringup_interface(tgen, "r3", intf_r3_r4) + + step( + "Verify that peering between R1-R4 goes down and comes up when " + "interface is toggled. Also prefix of link r3-r4(via both NHTs) is" + " withdrawn and re-learned accordingly." + ) + + result = verify_bgp_convergence_from_running_config(tgen, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n" "BGP is converged \n Error : {}".format(tc_name, result) + logger.info("Expected behaviour: {}".format(result)) + + for addr_type in ADDR_TYPES: + routes = {"ipv4": ["10.0.3.0/24"], "ipv6": ["fd00:0:0:3::/64"]} + + input_dict = {"r1": {"static_routes": [{"network": routes[addr_type]}]}} + next_hop = topo["routers"]["r4"]["links"]["r3"][addr_type].split("/")[0] + + result = verify_rib( + tgen, + addr_type, + "r1", + input_dict, + protocol="bgp", + next_hop=next_hop, + expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Route is still present \n Error : {}".format(tc_name, result) + ) + + shutdown_bringup_interface(tgen, "r3", intf_r3_r4, True) + + result = verify_bgp_convergence_from_running_config(tgen) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + routes = {"ipv4": ["10.0.3.0/24"], "ipv6": ["fd00:0:0:3::/64"]} + + input_dict = {"r1": {"static_routes": [{"network": routes[addr_type]}]}} + next_hop = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0] + + result = verify_rib( + tgen, addr_type, "r1", input_dict, protocol="bgp", next_hop=next_hop + ) + assert result is True, "Testcase {} : Failed \n Error : {}".format( + tc_name, result + ) + + step("Toggle the interface on R4(ifconfig 192.34).") + + intf_r4_r3 = topo["routers"]["r4"]["links"]["r3"]["interface"] + shutdown_bringup_interface(tgen, "r4", intf_r4_r3) + + step( + "Verify that peering between R1-R4 goes down and comes up when" + "interface is toggled. Also prefix of link r3-r4(via R4)" + " is withdrawn and re-learned accordingly." + ) + + result = verify_bgp_convergence_from_running_config(tgen, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n" "BGP is converged \n Error : {}".format(tc_name, result) + logger.info("Expected behaviour: {}".format(result)) + + for addr_type in ADDR_TYPES: + routes = {"ipv4": ["10.0.3.0/24"], "ipv6": ["fd00:0:0:3::/64"]} + + input_dict = {"r1": {"static_routes": [{"network": routes[addr_type]}]}} + next_hop = topo["routers"]["r4"]["links"]["r3"][addr_type].split("/")[0] + + result = verify_rib( + tgen, + addr_type, + "r1", + input_dict, + protocol="bgp", + next_hop=next_hop, + expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n " + "Route is still present \n Error : {}".format(tc_name, result) + ) + + shutdown_bringup_interface(tgen, "r4", intf_r4_r3, True) + + result = verify_bgp_convergence_from_running_config(tgen) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + routes = {"ipv4": ["10.0.3.0/24"], "ipv6": ["fd00:0:0:3::/64"]} + + input_dict = {"r1": {"static_routes": [{"network": routes[addr_type]}]}} + next_hop = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0] + + result = verify_rib( + tgen, addr_type, "r1", input_dict, protocol="bgp", next_hop=next_hop + ) + assert result is True, "Testcase {} : Failed \n Error : {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_BGP_path_attributes_default_values_p1(request): + """ + Verify that BGP path attributes are present in CLI + outputs and JSON format, even if set to default. + """ + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Initial config: Configure BGP neighborship, between R1-R2 & R1-R3") + reset_config_on_routers(tgen) + + step("Advertise a set of prefixes from R1 to both peers R2 and R3") + for addr_type in ADDR_TYPES: + input_dict_1 = { + "r1": { + "static_routes": [{"network": NETWORK[addr_type], "next_hop": "null0"}] + } + } + result = create_static_routes(tgen, input_dict_1) + + input_dict_2 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2) + + step( + "Verify that advertised prefixes are received on R4 and well" + "known attributes are present in the CLI and JSON outputs with" + "default values without any route-map config." + ) + for addr_type in ADDR_TYPES: + input_dict_3 = {"r4": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib( + tgen, + addr_type, + "r4", + input_dict_3, + next_hop=[ + topo["routers"]["r2"]["links"]["r4"][addr_type].split("/")[0], + topo["routers"]["r3"]["links"]["r4"][addr_type].split("/")[0], + ], + ) + assert result is True, "Testcase : Failed \n Error : {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r4": { + "route_maps": { + "rmap_pf": [{"set": {"origin": "incomplete", "aspath": "300 100"}}] + } + } + } + + result = verify_bgp_attributes( + tgen, + addr_type, + "r4", + NETWORK[addr_type], + rmap_name="rmap_pf", + input_dict=input_dict_4, + ) + assert result is True, "Testcase : Failed \n Error : {}".format(tc_name, result) + + step( + "Configure a route-map to set below attribute value as 500" + "and apply on R4 in an inbound direction" + ) + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r4": { + "route_maps": { + "Path_Attribue": [ + { + "action": "permit", + "set": { + "path": {"as_num": 500, "as_action": "prepend"}, + "locPrf": 500, + "origin": "egp", + }, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + input_dict_5 = { + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": { + "route_maps": [ + { + "name": "Path_Attribue", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": { + "route_maps": [ + { + "name": "Path_Attribue", + "direction": "in", + } + ] + } + } + } + } + } + }, + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_5) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step( + "Verify that once the route-map is applied all the attributes" + "part of route-map, changes value to 500" + ) + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r4": { + "route_maps": { + "rmap_pf": [ + { + "set": { + "locPrf": 500, + "aspath": "500 300 100", + "origin": "EGP", + } + } + ] + } + } + } + result = verify_bgp_attributes( + tgen, + addr_type, + "r4", + NETWORK[addr_type], + rmap_name="rmap_pf", + input_dict=input_dict_4, + ) + assert result is True, "Testcase : Failed \n Error : {}".format(tc_name, result) + + step("Remove the route-map from R4") + input_dict_5 = { + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": { + "route_maps": [ + { + "name": "Path_Attribue", + "direction": "in", + "delete": True, + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": { + "route_maps": [ + { + "name": "Path_Attribue", + "direction": "in", + "delete": True, + } + ] + } + } + } + } + } + }, + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_5) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step( + "Verify on R4 that well known attributes are present in the CLI &" + "JSON outputs again with default values without route-map config" + ) + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r4": { + "route_maps": { + "rmap_pf": [{"set": {"aspath": "300 100", "origin": "incomplete"}}] + } + } + } + result = verify_bgp_attributes( + tgen, + addr_type, + "r4", + NETWORK[addr_type], + rmap_name="rmap_pf", + input_dict=input_dict_4, + nexthop=None, + ) + assert result is True, "Testcase : Failed \n Error : {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_BGP_peering_bw_loopback_and_physical_p1(request): + """ + Verifying the BGP peering between loopback and + physical link's IP of 2 peer routers. + """ + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Initial config :Configure BGP neighborship between R1 and R3.") + reset_config_on_routers(tgen) + + step("Configure a loopback interface on R1") + dut = "r1" + create_interface_in_kernel( + tgen, dut, "lo10", "1.1.1.1", netmask="255.255.255.255", create=True + ) + create_interface_in_kernel( + tgen, dut, "lo10", "1:1::1:1", netmask="128", create=True + ) + + step("Configure BGP session between R1's loopbak & R3") + for addr_type in ADDR_TYPES: + input_dict_1 = { + "r3": { + "static_routes": [ + { + "network": Loopabck_IP["Lo_R1"][addr_type], + "next_hop": topo["routers"]["r1"]["links"]["r3"][ + addr_type + ].split("/")[0], + } + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error : {}".format( + tc_name, result + ) + result = verify_rib( + tgen, + addr_type, + "r3", + input_dict_1, + protocol="static", + next_hop=topo["routers"]["r1"]["links"]["r3"][addr_type].split("/")[0], + ) + assert result is True, "Testcase {} : Failed \n Error : {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + raw_config = { + "r1": { + "raw_config": [ + "router bgp {}".format(topo["routers"]["r1"]["bgp"]["local_as"]), + "address-family {} unicast".format(addr_type), + "neighbor {} update-source lo10".format( + topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0] + ), + "neighbor {} timers 1 3".format( + topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0] + ), + ] + }, + "r3": { + "raw_config": [ + "router bgp {}".format(topo["routers"]["r3"]["bgp"]["local_as"]), + "address-family {} unicast".format(addr_type), + "no neighbor {} remote-as {}".format( + topo["routers"]["r1"]["links"]["r3"][addr_type].split("/")[0], + topo["routers"]["r1"]["bgp"]["local_as"], + ), + "neighbor {} remote-as {}".format( + Loopabck_IP["Lo_R1"][addr_type].split("/")[0], + topo["routers"]["r1"]["bgp"]["local_as"], + ), + "neighbor {} ebgp-multihop 3".format( + Loopabck_IP["Lo_R1"][addr_type].split("/")[0] + ), + ] + }, + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error : {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + if addr_type == "ipv6": + raw_config = { + "r3": { + "raw_config": [ + "router bgp {}".format( + topo["routers"]["r3"]["bgp"]["local_as"] + ), + "address-family {} unicast".format(addr_type), + "neighbor {} activate".format( + Loopabck_IP["Lo_R1"][addr_type].split("/")[0] + ), + ] + } + } + else: + raw_config = { + "r3": { + "raw_config": [ + "router bgp {}".format( + topo["routers"]["r3"]["bgp"]["local_as"] + ), + "address-family {} unicast".format(addr_type), + "no neighbor {} activate".format( + Loopabck_IP["Lo_R1"]["ipv6"].split("/")[0] + ), + ] + } + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error : {}".format(tc_name, result) + + step("Verify that BGP neighborship between R1 and R3 comes up") + result = verify_bgp_convergence_from_running_config(tgen) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step("Remove ebgp-multihop command from R3") + for addr_type in ADDR_TYPES: + raw_config = { + "r3": { + "raw_config": [ + "router bgp {}".format(topo["routers"]["r3"]["bgp"]["local_as"]), + "no neighbor {} ebgp-multihop 3".format( + Loopabck_IP["Lo_R1"][addr_type].split("/")[0] + ), + ] + } + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error : {}".format(tc_name, result) + + step("Verify that once eBGP multi-hop is removed, BGP session goes down") + result = verify_bgp_convergence_from_running_config(tgen, expected=False) + assert result is not True, "Testcase {} : Failed \n Error : {}".format( + tc_name, result + ) + + step("Add ebgp-multihop command on R3 again") + for addr_type in ADDR_TYPES: + raw_config = { + "r3": { + "raw_config": [ + "router bgp {}".format(topo["routers"]["r3"]["bgp"]["local_as"]), + "neighbor {} ebgp-multihop 3".format( + Loopabck_IP["Lo_R1"][addr_type].split("/")[0] + ), + ] + } + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error : {}".format(tc_name, result) + + step("Verify that BGP neighborship between R1 and R3 comes up") + result = verify_bgp_convergence_from_running_config(tgen) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step("Remove update-source command from R1") + for addr_type in ADDR_TYPES: + raw_config = { + "r1": { + "raw_config": [ + "router bgp {}".format(topo["routers"]["r1"]["bgp"]["local_as"]), + "no neighbor {} update-source lo10".format( + topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0] + ), + ] + } + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error : {}".format(tc_name, result) + + step("Verify that BGP session goes down, when update-source is removed") + result = verify_bgp_convergence_from_running_config(tgen, expected=False) + assert result is not True, "Testcase {} : Failed \n Error : {}".format( + tc_name, result + ) + + step("Add update-source command on R1 again") + for addr_type in ADDR_TYPES: + raw_config = { + "r1": { + "raw_config": [ + "router bgp {}".format(topo["routers"]["r1"]["bgp"]["local_as"]), + "neighbor {} update-source lo10".format( + topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0] + ), + ] + } + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error : {}".format(tc_name, result) + + step("Verify that BGP neighborship between R1 and R3 comes up") + result = verify_bgp_convergence_from_running_config(tgen) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step("Remove static route from R3") + for addr_type in ADDR_TYPES: + input_dict_1 = { + "r3": { + "static_routes": [ + { + "network": Loopabck_IP["Lo_R1"][addr_type], + "next_hop": topo["routers"]["r1"]["links"]["r3"][ + addr_type + ].split("/")[0], + "delete": True, + } + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error : {}".format( + tc_name, result + ) + result = verify_rib( + tgen, + addr_type, + "r3", + input_dict_1, + protocol="static", + next_hop=topo["routers"]["r1"]["links"]["r3"][addr_type].split("/")[0], + expected=False + ) + assert result is not True, "Testcase {} : Failed \n Error : {}".format( + tc_name, result + ) + + sleep(3) + step("Verify that BGP session goes down, when static route is removed") + result = verify_bgp_convergence_from_running_config(tgen, expected=False) + assert result is not True, "Testcase {} : Failed \n Error : {}".format( + tc_name, result + ) + + step("Add static route on R3 again") + for addr_type in ADDR_TYPES: + input_dict_1 = { + "r3": { + "static_routes": [ + { + "network": Loopabck_IP["Lo_R1"][addr_type], + "next_hop": topo["routers"]["r1"]["links"]["r3"][ + addr_type + ].split("/")[0], + } + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error : {}".format( + tc_name, result + ) + result = verify_rib( + tgen, + addr_type, + "r3", + input_dict_1, + protocol="static", + next_hop=topo["routers"]["r1"]["links"]["r3"][addr_type].split("/")[0], + ) + assert result is True, "Testcase {} : Failed \n Error : {}".format( + tc_name, result + ) + + step("Verify that BGP neighborship between R1 and R3 comes up") + result = verify_bgp_convergence_from_running_config(tgen) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step("Toggle physical interface on R1") + intf_r1_r3 = topo["routers"]["r1"]["links"]["r3"]["interface"] + shutdown_bringup_interface(tgen, "r1", intf_r1_r3) + sleep(3) + step("Verify that BGP neighborship between R1 and R3 goes down") + result = verify_bgp_convergence_from_running_config(tgen, expected=False) + assert result is not True, "Testcase {} : Failed \n Error : {}".format( + tc_name, result + ) + + intf_r1_r3 = topo["routers"]["r1"]["links"]["r3"]["interface"] + shutdown_bringup_interface(tgen, "r1", intf_r1_r3, True) + + step("Verify that BGP neighborship between R1 and R3 comes up") + result = verify_bgp_convergence_from_running_config(tgen) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_BGP_active_standby_preemption_and_ecmp_p1(request): + """ + Verify that BGP Active/Standby/Pre-emption/ECMP. + """ + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Initial config :Configure BGP neighborship between R1 and R3.") + reset_config_on_routers(tgen) + + step("Change the AS number on R2 as 200") + input_dict = {"r2": {"bgp": {"local_as": 200}}} + result = modify_as_number(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step("Verify BGP converge after changing the AS number on R2") + result = verify_bgp_convergence_from_running_config(tgen) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step("Advertise a set of prefixes from R1 to both peers R2 & R3") + for addr_type in ADDR_TYPES: + input_dict_1 = { + "r1": { + "static_routes": [{"network": NETWORK[addr_type], "next_hop": "null0"}] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error : {}".format( + tc_name, result + ) + + input_dict_2 = { + "r1": { + "bgp": { + "address_family": { + "ipv4": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step("Verify that R4 receives BGP prefixes via both peer routers R2 & R3") + for addr_type in ADDR_TYPES: + input_dict_3 = {"r4": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib( + tgen, + addr_type, + "r4", + input_dict_3, + next_hop=[ + topo["routers"]["r2"]["links"]["r4"][addr_type].split("/")[0], + topo["routers"]["r3"]["links"]["r4"][addr_type].split("/")[0], + ], + ) + assert result is True, "Testcase : Failed \n Error : {}".format(tc_name, result) + + step( + "Configure a route-map to set as-path attribute and" + "apply on R3 in an inbound direction:" + ) + + input_dict_4 = { + "r3": { + "route_maps": { + "Path_Attribue": [ + { + "action": "permit", + "set": {"path": {"as_num": 123, "as_action": "prepend"}}, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + input_dict_5 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "Path_Attribue", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "Path_Attribue", + "direction": "in", + } + ] + } + } + } + } + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_5) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step("Verify on R4, BGP routes with shorter as-path are installed in FIB") + for addr_type in ADDR_TYPES: + dut = "r4" + protocol = "bgp" + input_dict_6 = {"r4": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_6, + next_hop=topo["routers"]["r2"]["links"]["r4"][addr_type].split("/")[0], + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error : {}".format( + tc_name, result + ) + + step("Shutdown BGP neighorship between R1-R2") + dut = "r4" + intf_r4_r2 = topo["routers"]["r4"]["links"]["r2"]["interface"] + shutdown_bringup_interface(tgen, dut, intf_r4_r2) + + step( + "Verify that prefixes from next-hop via R2 are withdrawn" + "and installed via next-hop as R3" + ) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_2, + next_hop=topo["routers"]["r3"]["links"]["r4"][addr_type].split("/")[0], + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step("Do a no shut for BGP neighorship between R2-R4") + shutdown_bringup_interface(tgen, dut, intf_r4_r2, ifaceaction=True) + + step( + "Verify that prefixes from next-hop via R3 are withdrawn" + "from R4 and installed via next-hop as R2 (preemption)" + ) + result = verify_rib( + tgen, + addr_type, + dut, + input_dict_2, + next_hop=topo["routers"]["r2"]["links"]["r4"][addr_type].split("/")[0], + protocol=protocol, + ) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step("Remove the route-map from R3's neighbor statement") + input_dict_5 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "Path_Attribue", + "direction": "in", + "delete": True, + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "Path_Attribue", + "direction": "in", + "delete": True, + } + ] + } + } + } + } + } + }, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_5) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step("Configure multipath-relax and maximum-paths 2 on R4 for ECMP") + input_dict_8 = { + "r4": { + "bgp": { + "address_family": { + "ipv4": {"unicast": {"maximum_paths": {"ebgp": 2}}}, + "ipv6": {"unicast": {"maximum_paths": {"ebgp": 2}}}, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_8) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + maxpath_relax = { + "r4": {"bgp": {"local_as": "400", "bestpath": {"aspath": "multipath-relax"}}} + } + + result = create_router_bgp(tgen, topo, maxpath_relax) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step("Verify FIB of R4, BGP prefixes with ECMP next-hop via R2 and R3") + for addr_type in ADDR_TYPES: + input_dict = {"r4": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_rib( + tgen, + addr_type, + "r4", + input_dict, + next_hop=[ + topo["routers"]["r2"]["links"]["r4"][addr_type].split("/")[0], + topo["routers"]["r3"]["links"]["r4"][addr_type].split("/")[0], + ], + ) + assert result is True, "Testcase {} : Failed \n Error : {}".format( + tc_name, result + ) + + step("Remove multipath-relax command from R4") + + del_maxpath_relax = { + "r4": { + "bgp": { + "local_as": "400", + "bestpath": {"aspath": "multipath-relax", "delete": True}, + } + } + } + + result = create_router_bgp(tgen, topo, del_maxpath_relax) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step("Verify that ECMP is no longer happening on R4.") + for addr_type in ADDR_TYPES: + input_dict = {"r4": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_rib( + tgen, + addr_type, + "r4", + input_dict, + next_hop=[ + topo["routers"]["r2"]["links"]["r4"][addr_type].split("/")[0], + topo["routers"]["r3"]["links"]["r4"][addr_type].split("/")[0], + ], + expected=False + ) + assert result is not True, "Testcase {} : Failed \n Error : {}".format( + tc_name, result + ) + + step("Reconfigure multipath-relax command on R4") + result = create_router_bgp(tgen, topo, maxpath_relax) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step("Verify FIB of R4, BGP prefixes with ECMP next-hop via R2 and R3") + result = verify_rib( + tgen, + addr_type, + "r4", + input_dict, + next_hop=[ + topo["routers"]["r2"]["links"]["r4"][addr_type].split("/")[0], + topo["routers"]["r3"]["links"]["r4"][addr_type].split("/")[0], + ], + ) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step("Remove maximum-path 2 command from R4") + input_dict_8 = { + "r4": { + "bgp": { + "address_family": { + "ipv4": {"unicast": {"maximum_paths": {"ebgp": 1,}}}, + "ipv6": {"unicast": {"maximum_paths": {"ebgp": 1,}}}, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_8) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step("Verify that ECMP is no longer happening on R4") + result = verify_rib( + tgen, + addr_type, + "r4", + input_dict, + next_hop=[ + topo["routers"]["r2"]["links"]["r4"][addr_type].split("/")[0], + topo["routers"]["r3"]["links"]["r4"][addr_type].split("/")[0], + ], + expected=False + ) + assert result is not True, "Testcase {} : Failed \n Error : {}".format( + tc_name, result + ) + + step("Re-configure maximum-path 2 command on R4") + input_dict_8 = { + "r4": { + "bgp": { + "address_family": { + "ipv4": {"unicast": {"maximum_paths": {"ebgp": 2,}}}, + "ipv6": {"unicast": {"maximum_paths": {"ebgp": 2,}}}, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_8) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step("Verify FIB of R4, BGP prefixes with ECMP next-hop via R2 and R3") + result = verify_rib( + tgen, + addr_type, + "r4", + input_dict, + next_hop=[ + topo["routers"]["r2"]["links"]["r4"][addr_type].split("/")[0], + topo["routers"]["r3"]["links"]["r4"][addr_type].split("/")[0], + ], + ) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_password_authentication_for_eBGP_and_iBGP_peers_p1(request): + """ + Verify password authentication for eBGP and iBGP peers. + """ + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Initial config :Configure BGP neighborship between R1 and R3.") + reset_config_on_routers(tgen) + + step( + "Add a static route on R1 for loopbacks IP's reachability of R2, R3" + "and on R2 and R3 for loopback IP of R1" + ) + for addr_type in ADDR_TYPES: + nh1 = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0] + nh2 = topo["routers"]["r1"]["links"]["r2"][addr_type].split("/")[0] + nh3 = topo["routers"]["r1"]["links"]["r3"][addr_type].split("/")[0] + nh4 = topo["routers"]["r2"]["links"]["r1"][addr_type].split("/")[0] + input_dict_1 = { + "r1": { + "static_routes": [ + { + "network": topo["routers"]["r3"]["links"]["lo"][addr_type], + "next_hop": nh1, + } + ] + } + } + input_dict_2 = { + "r2": { + "static_routes": [ + { + "network": topo["routers"]["r1"]["links"]["lo"][addr_type], + "next_hop": nh2, + } + ] + } + } + input_dict_3 = { + "r3": { + "static_routes": [ + { + "network": topo["routers"]["r1"]["links"]["lo"][addr_type], + "next_hop": nh3, + } + ] + } + } + input_dict_4 = { + "r1": { + "static_routes": [ + { + "network": topo["routers"]["r2"]["links"]["lo"][addr_type], + "next_hop": nh4, + } + ] + } + } + dut_list = ["r1", "r2", "r3", "r1"] + nexthop_list = [nh1, nh2, nh3, nh4] + input_dict_list = [input_dict_1, input_dict_2, input_dict_3, input_dict_4] + for dut, next_hop, input_dict in zip(dut_list, nexthop_list, input_dict_list): + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error : {}".format( + tc_name, result + ) + + step("Verify that static routes are installed in FIB of routers") + result = verify_rib( + tgen, addr_type, dut, input_dict, next_hop=next_hop, protocol="static" + ) + assert result is True, "Testcase {} : Failed \n Error : {}".format( + tc_name, result + ) + + step("Configure BGP sessions between R1-R2 and R1-R3 over loopback IPs") + for routerN in ["r1", "r3"]: + for addr_type in ADDR_TYPES: + if routerN == "r1": + bgp_neighbor = "r3" + elif routerN == "r3": + bgp_neighbor = "r1" + topo["routers"][routerN]["bgp"]["address_family"][addr_type]["unicast"][ + "neighbor" + ][bgp_neighbor]["dest_link"] = { + "lo": {"ebgp_multihop": 2, "source_link": "lo"} + } + build_config_from_json(tgen, topo, save_bkup=False) + + for routerN in ["r1", "r2"]: + for addr_type in ADDR_TYPES: + if routerN == "r1": + bgp_neighbor = "r2" + elif routerN == "r2": + bgp_neighbor = "r1" + topo["routers"][routerN]["bgp"]["address_family"][addr_type]["unicast"][ + "neighbor" + ][bgp_neighbor]["dest_link"] = {"lo": {"source_link": "lo"}} + build_config_from_json(tgen, topo, save_bkup=False) + + for routerN in ["r1", "r2", "r3"]: + for addr_type in ADDR_TYPES: + for bgp_neighbor in topo["routers"][routerN]["bgp"]["address_family"][ + addr_type + ]["unicast"]["neighbor"].keys(): + if routerN in ["r1", "r2", "r3"] and bgp_neighbor == "r4": + continue + if addr_type == "ipv4": + topo["routers"][routerN]["bgp"]["address_family"][addr_type][ + "unicast" + ]["neighbor"][bgp_neighbor]["dest_link"] = { + "lo": {"deactivate": "ipv6"} + } + elif addr_type == "ipv6": + topo["routers"][routerN]["bgp"]["address_family"][addr_type][ + "unicast" + ]["neighbor"][bgp_neighbor]["dest_link"] = { + "lo": {"deactivate": "ipv4"} + } + build_config_from_json(tgen, topo, save_bkup=False) + + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step("Configure authentication password on R1 for neighbor statements") + for bgp_neighbor in ["r2", "r3"]: + for addr_type in ADDR_TYPES: + topo["routers"]["r1"]["bgp"]["address_family"][addr_type]["unicast"][ + "neighbor" + ][bgp_neighbor]["dest_link"] = {"lo": {"password": "vmware"}} + build_config_from_json(tgen, topo, save_bkup=False) + + step( + "Verify that both sessions go down as only R1 has password" + "configured but not peer routers" + ) + result = verify_bgp_convergence(tgen, topo, expected=False) + assert result is not True, "Testcase {} : Failed \n Error : {}".format( + tc_name, result + ) + + step("configure same password on R2 and R3") + for routerN in ["r2", "r3"]: + for addr_type in ADDR_TYPES: + topo["routers"][routerN]["bgp"]["address_family"][addr_type]["unicast"][ + "neighbor" + ]["r1"]["dest_link"] = {"lo": {"password": "vmware"}} + build_config_from_json(tgen, topo, save_bkup=False) + + step("Verify that all BGP sessions come up due to identical passwords") + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step("Configure same password on R2 and R3, but in CAPs.") + for routerN in ["r2", "r3"]: + for addr_type in ADDR_TYPES: + topo["routers"][routerN]["bgp"]["address_family"][addr_type]["unicast"][ + "neighbor" + ]["r1"]["dest_link"] = {"lo": {"password": "VMWARE"}} + build_config_from_json(tgen, topo, save_bkup=False) + + step( + "Verify that BGP sessions do not come up as password" + "strings are in CAPs on R2 and R3" + ) + result = verify_bgp_convergence(tgen, topo, expected=False) + assert result is not True, "Testcase {} : Failed \n Error : {}".format( + tc_name, result + ) + + step("Configure same password on R2 and R3 without CAPs") + for routerN in ["r2", "r3"]: + for addr_type in ADDR_TYPES: + topo["routers"][routerN]["bgp"]["address_family"][addr_type]["unicast"][ + "neighbor" + ]["r1"]["dest_link"] = {"lo": {"password": "vmware"}} + build_config_from_json(tgen, topo, save_bkup=False) + + step("Verify all BGP sessions come up again due to identical passwords") + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + step("Remove password from R1") + for bgp_neighbor in ["r2", "r3"]: + for addr_type in ADDR_TYPES: + topo["routers"]["r1"]["bgp"]["address_family"][addr_type]["unicast"][ + "neighbor" + ][bgp_neighbor]["dest_link"] = {"lo": {"no_password": "vmware"}} + build_config_from_json(tgen, topo, save_bkup=False) + + step("Verify if password is removed from R1, both sessions go down again") + result = verify_bgp_convergence(tgen, topo, expected=False) + assert result is not True, "Testcase {} : Failed \n Error : {}".format( + tc_name, result + ) + + step("Configure alphanumeric password on R1 and peer routers R2,R3") + for bgp_neighbor in ["r2", "r3"]: + for addr_type in ADDR_TYPES: + topo["routers"]["r1"]["bgp"]["address_family"][addr_type]["unicast"][ + "neighbor" + ][bgp_neighbor]["dest_link"] = {"lo": {"password": "Vmware@123"}} + build_config_from_json(tgen, topo, save_bkup=False) + + for routerN in ["r2", "r3"]: + for addr_type in ADDR_TYPES: + topo["routers"][routerN]["bgp"]["address_family"][addr_type]["unicast"][ + "neighbor" + ]["r1"]["dest_link"] = {"lo": {"password": "Vmware@123"}} + build_config_from_json(tgen, topo, save_bkup=False) + + step( + "Verify that sessions Come up irrespective of characters" + "used in password string" + ) + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_reject_as_sets/__init__.py b/tests/topotests/bgp_reject_as_sets/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bgp_reject_as_sets/r1/bgpd.conf b/tests/topotests/bgp_reject_as_sets/r1/bgpd.conf new file mode 100644 index 0000000000..94bfc5e561 --- /dev/null +++ b/tests/topotests/bgp_reject_as_sets/r1/bgpd.conf @@ -0,0 +1,9 @@ +! exit1 +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as 65002 + address-family ipv4 unicast + redistribute connected + exit-address-family + ! +! diff --git a/tests/topotests/bgp_reject_as_sets/r1/zebra.conf b/tests/topotests/bgp_reject_as_sets/r1/zebra.conf new file mode 100644 index 0000000000..9904bb4e16 --- /dev/null +++ b/tests/topotests/bgp_reject_as_sets/r1/zebra.conf @@ -0,0 +1,9 @@ +! exit1 +interface lo + ip address 172.16.255.254/32 +! +interface r1-eth0 + ip address 192.168.255.2/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_reject_as_sets/r2/bgpd.conf b/tests/topotests/bgp_reject_as_sets/r2/bgpd.conf new file mode 100644 index 0000000000..f217b7f794 --- /dev/null +++ b/tests/topotests/bgp_reject_as_sets/r2/bgpd.conf @@ -0,0 +1,11 @@ +! spine +router bgp 65002 + bgp reject-as-sets + no bgp ebgp-requires-policy + neighbor 192.168.255.2 remote-as 65001 + neighbor 192.168.254.2 remote-as 65003 + address-family ipv4 unicast + aggregate-address 172.16.0.0/16 as-set summary-only + exit-address-family + ! +! diff --git a/tests/topotests/bgp_reject_as_sets/r2/zebra.conf b/tests/topotests/bgp_reject_as_sets/r2/zebra.conf new file mode 100644 index 0000000000..f0d357c5ff --- /dev/null +++ b/tests/topotests/bgp_reject_as_sets/r2/zebra.conf @@ -0,0 +1,9 @@ +! spine +interface r2-eth0 + ip address 192.168.255.1/30 +! +interface r2-eth1 + ip address 192.168.254.1/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_reject_as_sets/r3/bgpd.conf b/tests/topotests/bgp_reject_as_sets/r3/bgpd.conf new file mode 100644 index 0000000000..8d085a0e4b --- /dev/null +++ b/tests/topotests/bgp_reject_as_sets/r3/bgpd.conf @@ -0,0 +1,10 @@ +! exit2 +router bgp 65003 + no bgp ebgp-requires-policy + neighbor 192.168.254.1 remote-as 65002 + address-family ipv4 unicast + neighbor 192.168.254.1 allowas-in + redistribute connected + exit-address-family + ! +! diff --git a/tests/topotests/bgp_reject_as_sets/r3/zebra.conf b/tests/topotests/bgp_reject_as_sets/r3/zebra.conf new file mode 100644 index 0000000000..f490d97afe --- /dev/null +++ b/tests/topotests/bgp_reject_as_sets/r3/zebra.conf @@ -0,0 +1,9 @@ +! exit2 +interface lo + ip address 172.16.254.254/32 +! +interface r3-eth0 + ip address 192.168.254.2/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_reject_as_sets/test_bgp_reject_as_sets.py b/tests/topotests/bgp_reject_as_sets/test_bgp_reject_as_sets.py new file mode 100644 index 0000000000..b49a57b308 --- /dev/null +++ b/tests/topotests/bgp_reject_as_sets/test_bgp_reject_as_sets.py @@ -0,0 +1,155 @@ +#!/usr/bin/env python + +# +# test_bgp_reject_as_sets.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2019 by +# Donatas Abraitis +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +Test if an aggregated route with AS_SET is not sent to peers. +Addressing draft-ietf-idr-deprecate-as-set-confed-set recommendations. + +BGP speakers conforming to this document (i.e., conformant BGP + speakers) MUST NOT locally generate BGP UPDATE messages containing + AS_SET or AS_CONFED_SET. Conformant BGP speakers SHOULD NOT send BGP + UPDATE messages containing AS_SET or AS_CONFED_SET. Upon receipt of + such messages, conformant BGP speakers SHOULD use the "Treat-as- + withdraw" error handling behavior as per [RFC7606]. +""" + +import os +import sys +import json +import time +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from mininet.topo import Topo + + +class TemplateTopo(Topo): + def build(self, *_args, **_opts): + tgen = get_topogen(self) + + for routern in range(1, 4): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + +def setup_module(mod): + tgen = Topogen(TemplateTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.iteritems(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_reject_as_sets(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r2"] + + def _bgp_converge(router): + output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.2 json")) + expected = { + "192.168.255.2": { + "bgpState": "Established", + "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 2}}, + } + } + return topotest.json_cmp(output, expected) + + def _bgp_has_aggregated_route_with_stripped_as_set(router): + output = json.loads(router.vtysh_cmd("show ip bgp 172.16.0.0/16 json")) + expected = { + "paths": [{"aspath": {"string": "Local", "segments": [], "length": 0}}] + } + return topotest.json_cmp(output, expected) + + def _bgp_announce_route_without_as_sets(router): + output = json.loads( + router.vtysh_cmd( + "show ip bgp neighbor 192.168.254.2 advertised-routes json" + ) + ) + expected = { + "advertisedRoutes": { + "172.16.0.0/16": {"path": ""}, + "192.168.254.0/30": {"path": "65003"}, + "192.168.255.0/30": {"path": "65001"}, + }, + "totalPrefixCounter": 3, + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge, router) + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + + assert result is None, 'Failed bgp convergence in "{}"'.format(router) + + test_func = functools.partial( + _bgp_has_aggregated_route_with_stripped_as_set, router + ) + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + + assert result is None, 'Failed to see an aggregated route in "{}"'.format(router) + + test_func = functools.partial(_bgp_announce_route_without_as_sets, router) + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + + assert ( + result is None + ), 'Route 172.16.0.0/16 should be sent without AS_SET to r3 "{}"'.format(router) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_rfapi_basic_sanity/customize.py b/tests/topotests/bgp_rfapi_basic_sanity/customize.py index a125c6582f..ea548a7337 100644 --- a/tests/topotests/bgp_rfapi_basic_sanity/customize.py +++ b/tests/topotests/bgp_rfapi_basic_sanity/customize.py @@ -75,12 +75,15 @@ from mininet.topo import Topo import shutil + CWD = os.path.dirname(os.path.realpath(__file__)) # test name based on directory TEST = os.path.basename(CWD) + class ThisTestTopo(Topo): "Test topology builder" + def build(self, *_args, **_opts): "Build function" tgen = get_topogen(self) @@ -89,36 +92,37 @@ def build(self, *_args, **_opts): # between routers, switches and hosts. # # Create P/PE routers - tgen.add_router('r1') + tgen.add_router("r1") for routern in range(2, 5): - tgen.add_router('r{}'.format(routern)) + tgen.add_router("r{}".format(routern)) # Create a switch with just one router connected to it to simulate a # empty network. switch = {} - switch[0] = tgen.add_switch('sw0') - switch[0].add_link(tgen.gears['r1'], nodeif='r1-eth0') - switch[0].add_link(tgen.gears['r2'], nodeif='r2-eth0') + switch[0] = tgen.add_switch("sw0") + switch[0].add_link(tgen.gears["r1"], nodeif="r1-eth0") + switch[0].add_link(tgen.gears["r2"], nodeif="r2-eth0") - switch[1] = tgen.add_switch('sw1') - switch[1].add_link(tgen.gears['r2'], nodeif='r2-eth1') - switch[1].add_link(tgen.gears['r3'], nodeif='r3-eth0') - switch[1].add_link(tgen.gears['r4'], nodeif='r4-eth0') + switch[1] = tgen.add_switch("sw1") + switch[1].add_link(tgen.gears["r2"], nodeif="r2-eth1") + switch[1].add_link(tgen.gears["r3"], nodeif="r3-eth0") + switch[1].add_link(tgen.gears["r4"], nodeif="r4-eth0") + + switch[2] = tgen.add_switch("sw2") + switch[2].add_link(tgen.gears["r2"], nodeif="r2-eth2") + switch[2].add_link(tgen.gears["r3"], nodeif="r3-eth1") - switch[2] = tgen.add_switch('sw2') - switch[2].add_link(tgen.gears['r2'], nodeif='r2-eth2') - switch[2].add_link(tgen.gears['r3'], nodeif='r3-eth1') def ltemplatePreRouterStartHook(): cc = ltemplateRtrCmd() tgen = get_topogen() - logger.info('pre router-start hook') - #check for normal init + logger.info("pre router-start hook") + # check for normal init if len(tgen.net) == 1: - logger.info('Topology not configured, skipping setup') + logger.info("Topology not configured, skipping setup") return False return True + def ltemplatePostRouterStartHook(): - logger.info('post router-start hook') + logger.info("post router-start hook") return True - diff --git a/tests/topotests/bgp_rfapi_basic_sanity/r1/bgpd.conf b/tests/topotests/bgp_rfapi_basic_sanity/r1/bgpd.conf index 05eac758f1..ada354bd62 100644 --- a/tests/topotests/bgp_rfapi_basic_sanity/r1/bgpd.conf +++ b/tests/topotests/bgp_rfapi_basic_sanity/r1/bgpd.conf @@ -3,11 +3,11 @@ frr defaults traditional hostname r1 password zebra log stdout notifications -log monitor notifications log commands router bgp 5226 bgp router-id 1.1.1.1 bgp cluster-id 1.1.1.1 + no bgp ebgp-requires-policy neighbor 2.2.2.2 remote-as 5226 neighbor 2.2.2.2 update-source 1.1.1.1 ! diff --git a/tests/topotests/bgp_rfapi_basic_sanity/r1/ospfd.conf b/tests/topotests/bgp_rfapi_basic_sanity/r1/ospfd.conf index c5097e214f..460a8fb016 100644 --- a/tests/topotests/bgp_rfapi_basic_sanity/r1/ospfd.conf +++ b/tests/topotests/bgp_rfapi_basic_sanity/r1/ospfd.conf @@ -6,3 +6,7 @@ router ospf network 0.0.0.0/4 area 0 redistribute static ! +int r1-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/bgp_rfapi_basic_sanity/r2/bgpd.conf b/tests/topotests/bgp_rfapi_basic_sanity/r2/bgpd.conf index 241c2ac0ae..95890f25b9 100644 --- a/tests/topotests/bgp_rfapi_basic_sanity/r2/bgpd.conf +++ b/tests/topotests/bgp_rfapi_basic_sanity/r2/bgpd.conf @@ -3,11 +3,11 @@ frr defaults traditional hostname r2 password zebra log stdout notifications -log monitor notifications log commands router bgp 5226 bgp router-id 2.2.2.2 bgp cluster-id 2.2.2.2 + no bgp ebgp-requires-policy neighbor 1.1.1.1 remote-as 5226 neighbor 1.1.1.1 update-source 2.2.2.2 neighbor 3.3.3.3 remote-as 5226 @@ -28,6 +28,3 @@ router bgp 5226 neighbor 4.4.4.4 route-reflector-client exit-address-family end - - - diff --git a/tests/topotests/bgp_rfapi_basic_sanity/r2/ospfd.conf b/tests/topotests/bgp_rfapi_basic_sanity/r2/ospfd.conf index 8678813665..dbed6189c8 100644 --- a/tests/topotests/bgp_rfapi_basic_sanity/r2/ospfd.conf +++ b/tests/topotests/bgp_rfapi_basic_sanity/r2/ospfd.conf @@ -5,3 +5,15 @@ router ospf router-id 2.2.2.2 network 0.0.0.0/0 area 0 ! +int r2-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +int r2-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +int r2-eth2 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/bgp_rfapi_basic_sanity/r3/bgpd.conf b/tests/topotests/bgp_rfapi_basic_sanity/r3/bgpd.conf index 67b26e3a50..4932d63d4f 100644 --- a/tests/topotests/bgp_rfapi_basic_sanity/r3/bgpd.conf +++ b/tests/topotests/bgp_rfapi_basic_sanity/r3/bgpd.conf @@ -3,11 +3,11 @@ frr defaults traditional hostname r3 password zebra log stdout notifications -log monitor notifications log commands router bgp 5226 bgp router-id 3.3.3.3 bgp cluster-id 3.3.3.3 + no bgp ebgp-requires-policy neighbor 2.2.2.2 remote-as 5226 neighbor 2.2.2.2 update-source 3.3.3.3 ! @@ -45,6 +45,3 @@ router bgp 5226 exit-vnc ! end - - - diff --git a/tests/topotests/bgp_rfapi_basic_sanity/r3/ospfd.conf b/tests/topotests/bgp_rfapi_basic_sanity/r3/ospfd.conf index c7c358f9dc..0e64ed6fc5 100644 --- a/tests/topotests/bgp_rfapi_basic_sanity/r3/ospfd.conf +++ b/tests/topotests/bgp_rfapi_basic_sanity/r3/ospfd.conf @@ -7,3 +7,11 @@ router ospf network 0.0.0.0/4 area 0 redistribute static ! +int r3-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +int r3-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/bgp_rfapi_basic_sanity/r4/bgpd.conf b/tests/topotests/bgp_rfapi_basic_sanity/r4/bgpd.conf index 2ba5c74e5b..1a5e41aae6 100644 --- a/tests/topotests/bgp_rfapi_basic_sanity/r4/bgpd.conf +++ b/tests/topotests/bgp_rfapi_basic_sanity/r4/bgpd.conf @@ -3,11 +3,11 @@ frr defaults traditional hostname r4 password zebra log stdout notifications -log monitor notifications log commands router bgp 5226 bgp router-id 4.4.4.4 bgp cluster-id 4.4.4.4 + no bgp ebgp-requires-policy neighbor 2.2.2.2 remote-as 5226 neighbor 2.2.2.2 update-source 4.4.4.4 ! @@ -46,6 +46,3 @@ router bgp 5226 exit-vnc ! end - - - diff --git a/tests/topotests/bgp_rfapi_basic_sanity/r4/ospfd.conf b/tests/topotests/bgp_rfapi_basic_sanity/r4/ospfd.conf index 83d09c09a4..89e37df479 100644 --- a/tests/topotests/bgp_rfapi_basic_sanity/r4/ospfd.conf +++ b/tests/topotests/bgp_rfapi_basic_sanity/r4/ospfd.conf @@ -6,3 +6,7 @@ router ospf network 0.0.0.0/4 area 0 redistribute static ! +int r4-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/bgp_rfapi_basic_sanity/scripts/add_routes.py b/tests/topotests/bgp_rfapi_basic_sanity/scripts/add_routes.py index 4d6a7582ba..f4b4da55d2 100644 --- a/tests/topotests/bgp_rfapi_basic_sanity/scripts/add_routes.py +++ b/tests/topotests/bgp_rfapi_basic_sanity/scripts/add_routes.py @@ -1,36 +1,159 @@ from lutil import luCommand -holddownFactorSet = luCommand('r1','vtysh -c "show running"','rfp holddown-factor','none','Holddown factor set') + +holddownFactorSet = luCommand( + "r1", + 'vtysh -c "show running"', + "rfp holddown-factor", + "none", + "Holddown factor set", +) if not holddownFactorSet: to = "-1" cost = "" else: to = "6" cost = "cost 50" -luCommand('r1','vtysh -c "debug rfapi-dev open vn 10.0.0.1 un 1.1.1.1"','rfapi_set_response_cb: status 0', 'pass', 'Opened RFAPI') -luCommand('r1','vtysh -c "debug rfapi-dev query vn 10.0.0.1 un 1.1.1.1 target 11.11.11.11"','rc=2', 'pass', 'Clean query') -luCommand('r1','vtysh -c "debug rfapi-dev register vn 10.0.0.1 un 1.1.1.1 prefix 11.11.11.0/24 lifetime {}"'.format(to),'', 'none', 'Prefix registered') -luCommand('r1','vtysh -c "show vnc registrations local"','1 out of 1','wait','Local registration') -luCommand('r1','vtysh -c "debug rfapi-dev response-omit-self off"','.','none') -luCommand('r1','vtysh -c "debug rfapi-dev query vn 10.0.0.1 un 1.1.1.1 target 11.11.11.11"','11.11.11.0/24', 'pass', 'Query self') +luCommand( + "r1", + 'vtysh -c "debug rfapi-dev open vn 10.0.0.1 un 1.1.1.1"', + "rfapi_set_response_cb: status 0", + "pass", + "Opened RFAPI", +) +luCommand( + "r1", + 'vtysh -c "debug rfapi-dev query vn 10.0.0.1 un 1.1.1.1 target 11.11.11.11"', + "rc=2", + "pass", + "Clean query", +) +luCommand( + "r1", + 'vtysh -c "debug rfapi-dev register vn 10.0.0.1 un 1.1.1.1 prefix 11.11.11.0/24 lifetime {}"'.format( + to + ), + "", + "none", + "Prefix registered", +) +luCommand( + "r1", + 'vtysh -c "show vnc registrations local"', + "1 out of 1", + "wait", + "Local registration", +) +luCommand("r1", 'vtysh -c "debug rfapi-dev response-omit-self off"', ".", "none") +luCommand( + "r1", + 'vtysh -c "debug rfapi-dev query vn 10.0.0.1 un 1.1.1.1 target 11.11.11.11"', + "11.11.11.0/24", + "pass", + "Query self", +) -luCommand('r3','vtysh -c "debug rfapi-dev open vn 10.0.0.2 un 2.2.2.2"','rfapi_set_response_cb: status 0', 'pass', 'Opened RFAPI') -luCommand('r3','vtysh -c "debug rfapi-dev register vn 10.0.0.2 un 2.2.2.2 prefix 22.22.22.0/24 lifetime {}"'.format(to),'', 'none', 'Prefix registered') -luCommand('r3','vtysh -c "show vnc registrations local"','1 out of 1','wait','Local registration') -luCommand('r3','vtysh -c "debug rfapi-dev response-omit-self on"','.','none') -luCommand('r3','vtysh -c "debug rfapi-dev query vn 10.0.0.2 un 2.2.2.2 target 22.22.22.22"','rc=2', 'pass', 'Self excluded') -luCommand('r3','vtysh -c "debug rfapi-dev open vn 10.0.1.2 un 2.1.1.2"','rfapi_set_response_cb: status 0', 'pass', 'Opened query only RFAPI') -luCommand('r3','vtysh -c "debug rfapi-dev query vn 10.0.1.2 un 2.1.1.2 target 22.22.22.22"','22.22.22.0/24', 'pass', 'See local') +luCommand( + "r3", + 'vtysh -c "debug rfapi-dev open vn 10.0.0.2 un 2.2.2.2"', + "rfapi_set_response_cb: status 0", + "pass", + "Opened RFAPI", +) +luCommand( + "r3", + 'vtysh -c "debug rfapi-dev register vn 10.0.0.2 un 2.2.2.2 prefix 22.22.22.0/24 lifetime {}"'.format( + to + ), + "", + "none", + "Prefix registered", +) +luCommand( + "r3", + 'vtysh -c "show vnc registrations local"', + "1 out of 1", + "wait", + "Local registration", +) +luCommand("r3", 'vtysh -c "debug rfapi-dev response-omit-self on"', ".", "none") +luCommand( + "r3", + 'vtysh -c "debug rfapi-dev query vn 10.0.0.2 un 2.2.2.2 target 22.22.22.22"', + "rc=2", + "pass", + "Self excluded", +) +luCommand( + "r3", + 'vtysh -c "debug rfapi-dev open vn 10.0.1.2 un 2.1.1.2"', + "rfapi_set_response_cb: status 0", + "pass", + "Opened query only RFAPI", +) +luCommand( + "r3", + 'vtysh -c "debug rfapi-dev query vn 10.0.1.2 un 2.1.1.2 target 22.22.22.22"', + "22.22.22.0/24", + "pass", + "See local", +) -luCommand('r4','vtysh -c "debug rfapi-dev open vn 10.0.0.3 un 3.3.3.3"','rfapi_set_response_cb: status 0', 'pass', 'Opened RFAPI') -luCommand('r4','vtysh -c "debug rfapi-dev register vn 10.0.0.3 un 3.3.3.3 prefix 33.33.33.0/24 lifetime {}"'.format(to),'', 'none', 'Prefix registered') -luCommand('r4','vtysh -c "show vnc registrations local"','1 out of 1','wait','Local registration') -luCommand('r4','vtysh -c "debug rfapi-dev response-omit-self off"','.','none') -luCommand('r4','vtysh -c "debug rfapi-dev query vn 10.0.0.3 un 3.3.3.3 target 33.33.33.33"','33.33.33.0/24', 'pass', 'Query self') +luCommand( + "r4", + 'vtysh -c "debug rfapi-dev open vn 10.0.0.3 un 3.3.3.3"', + "rfapi_set_response_cb: status 0", + "pass", + "Opened RFAPI", +) +luCommand( + "r4", + 'vtysh -c "debug rfapi-dev register vn 10.0.0.3 un 3.3.3.3 prefix 33.33.33.0/24 lifetime {}"'.format( + to + ), + "", + "none", + "Prefix registered", +) +luCommand( + "r4", + 'vtysh -c "show vnc registrations local"', + "1 out of 1", + "wait", + "Local registration", +) +luCommand("r4", 'vtysh -c "debug rfapi-dev response-omit-self off"', ".", "none") +luCommand( + "r4", + 'vtysh -c "debug rfapi-dev query vn 10.0.0.3 un 3.3.3.3 target 33.33.33.33"', + "33.33.33.0/24", + "pass", + "Query self", +) -luCommand('r4','vtysh -c "debug rfapi-dev register vn 10.0.0.3 un 3.3.3.3 prefix 11.11.11.0/24 lifetime {} {}"'.format(to, cost),'', 'none', 'MP Prefix registered') -luCommand('r4','vtysh -c "show vnc registrations local"','2 out of 2','wait','Local registration') -luCommand('r4','vtysh -c "debug rfapi-dev query vn 10.0.0.3 un 3.3.3.3 target 11.11.11.11"','11.11.11.0/24', 'pass', 'Query self MP') +luCommand( + "r4", + 'vtysh -c "debug rfapi-dev register vn 10.0.0.3 un 3.3.3.3 prefix 11.11.11.0/24 lifetime {} {}"'.format( + to, cost + ), + "", + "none", + "MP Prefix registered", +) +luCommand( + "r4", + 'vtysh -c "show vnc registrations local"', + "2 out of 2", + "wait", + "Local registration", +) +luCommand( + "r4", + 'vtysh -c "debug rfapi-dev query vn 10.0.0.3 un 3.3.3.3 target 11.11.11.11"', + "11.11.11.0/24", + "pass", + "Query self MP", +) -luCommand('r1','vtysh -c "show vnc registrations"','.','none') -luCommand('r3','vtysh -c "show vnc registrations"','.','none') -luCommand('r4','vtysh -c "show vnc registrations"','.','none') +luCommand("r1", 'vtysh -c "show vnc registrations"', ".", "none") +luCommand("r3", 'vtysh -c "show vnc registrations"', ".", "none") +luCommand("r4", 'vtysh -c "show vnc registrations"', ".", "none") diff --git a/tests/topotests/bgp_rfapi_basic_sanity/scripts/adjacencies.py b/tests/topotests/bgp_rfapi_basic_sanity/scripts/adjacencies.py index 1f53791f6a..6ad3e735ee 100644 --- a/tests/topotests/bgp_rfapi_basic_sanity/scripts/adjacencies.py +++ b/tests/topotests/bgp_rfapi_basic_sanity/scripts/adjacencies.py @@ -1,10 +1,48 @@ -luCommand('r1','ping 2.2.2.2 -c 1',' 0. packet loss','wait','PE->P2 (loopback) ping',60) -luCommand('r3','ping 2.2.2.2 -c 1',' 0. packet loss','wait','PE->P2 (loopback) ping',60) -luCommand('r4','ping 2.2.2.2 -c 1',' 0. packet loss','wait','PE->P2 (loopback) ping',60) -luCommand('r2','vtysh -c "show bgp summary"',' 00:0.* 00:0.* 00:0','wait','Core adjacencies up',30) -luCommand('r1','vtysh -c "show bgp vrf all summary"',' 00:0','pass','All adjacencies up') -luCommand('r3','vtysh -c "show bgp vrf all summary"',' 00:0','pass','All adjacencies up') -luCommand('r4','vtysh -c "show bgp vrf all summary"',' 00:0','pass','All adjacencies up') -luCommand('r1','ping 3.3.3.3 -c 1',' 0. packet loss','wait','PE->PE3 (loopback) ping') -luCommand('r1','ping 4.4.4.4 -c 1',' 0. packet loss','wait','PE->PE4 (loopback) ping') -#luCommand('r4','ping 3.3.3.3 -c 1',' 0. packet loss','wait','PE->PE3 (loopback) ping') +luCommand( + "r1", "ping 2.2.2.2 -c 1", " 0. packet loss", "wait", "PE->P2 (loopback) ping", 60 +) +luCommand( + "r3", "ping 2.2.2.2 -c 1", " 0. packet loss", "wait", "PE->P2 (loopback) ping", 60 +) +luCommand( + "r4", "ping 2.2.2.2 -c 1", " 0. packet loss", "wait", "PE->P2 (loopback) ping", 60 +) +luCommand( + "r2", + 'vtysh -c "show bgp summary"', + " 00:0.* 00:0.* 00:0", + "wait", + "Core adjacencies up", + 180, +) +luCommand( + "r1", + 'vtysh -c "show bgp vrf all summary"', + " 00:0", + "wait", + "All adjacencies up", + 180, +) +luCommand( + "r3", + 'vtysh -c "show bgp vrf all summary"', + " 00:0", + "wait", + "All adjacencies up", + 180, +) +luCommand( + "r4", + 'vtysh -c "show bgp vrf all summary"', + " 00:0", + "wait", + "All adjacencies up", + 180, +) +luCommand( + "r1", "ping 3.3.3.3 -c 1", " 0. packet loss", "wait", "PE->PE3 (loopback) ping" +) +luCommand( + "r1", "ping 4.4.4.4 -c 1", " 0. packet loss", "wait", "PE->PE4 (loopback) ping" +) +# luCommand('r4','ping 3.3.3.3 -c 1',' 0. packet loss','wait','PE->PE3 (loopback) ping') diff --git a/tests/topotests/bgp_rfapi_basic_sanity/scripts/check_close.py b/tests/topotests/bgp_rfapi_basic_sanity/scripts/check_close.py index 5fffce7ca0..9fdef84cdf 100644 --- a/tests/topotests/bgp_rfapi_basic_sanity/scripts/check_close.py +++ b/tests/topotests/bgp_rfapi_basic_sanity/scripts/check_close.py @@ -1,19 +1,102 @@ from lutil import luCommand -holddownFactorSet = luCommand('r1','vtysh -c "show running"','rfp holddown-factor','none','Holddown factor set') + +holddownFactorSet = luCommand( + "r1", + 'vtysh -c "show running"', + "rfp holddown-factor", + "none", + "Holddown factor set", +) if not holddownFactorSet: to = "-1" else: to = "1" -luCommand('r1','vtysh -c "debug rfapi-dev open vn 20.0.0.1 un 1.1.1.21"','rfapi_set_response_cb: status 0', 'pass', 'Opened RFAPI') -luCommand('r1','vtysh -c "debug rfapi-dev register vn 20.0.0.1 un 1.1.1.21 prefix 111.111.111.0/24 lifetime {}"'.format(to),'', 'none', 'Prefix registered') -luCommand('r1','vtysh -c "show vnc registrations local"','111.111.111.0/24','wait','Local registration',1) -luCommand('r1','vtysh -c "show vnc registrations"','.','none') -luCommand('r3','vtysh -c "show vnc registrations"','111.111.111.0/24','wait','See registration') -luCommand('r4','vtysh -c "show vnc registrations"','111.111.111.0/24','wait','See registration') -luCommand('r1','vtysh -c "debug rfapi-dev close vn 20.0.0.1 un 1.1.1.21"','status 0', 'pass', 'Closed RFAPI') -luCommand('r1','vtysh -c "show vnc registrations"','Locally: *Active: 1 .* Remotely: *Active: 3','wait','See cleanup') -luCommand('r3','vtysh -c "show vnc registrations"','Locally: *Active: 1 .* Remotely: *Active: 3','wait','See cleanup') -luCommand('r4','vtysh -c "show vnc registrations"','Locally: *Active: 2 .* Remotely: *Active: 2','wait','See cleanup') -luCommand('r1','vtysh -c "show vnc registrations"','In Holddown: *Active: 0','wait','Out of holddown',20) -luCommand('r3','vtysh -c "show vnc registrations"','In Holddown: *Active: 0','wait','Out of holddown') -luCommand('r4','vtysh -c "show vnc registrations"','In Holddown: *Active: 0','wait','Out of holddown') +luCommand( + "r1", + 'vtysh -c "debug rfapi-dev open vn 20.0.0.1 un 1.1.1.21"', + "rfapi_set_response_cb: status 0", + "pass", + "Opened RFAPI", +) +luCommand( + "r1", + 'vtysh -c "debug rfapi-dev register vn 20.0.0.1 un 1.1.1.21 prefix 111.111.111.0/24 lifetime {}"'.format( + to + ), + "", + "none", + "Prefix registered", +) +luCommand( + "r1", + 'vtysh -c "show vnc registrations local"', + "111.111.111.0/24", + "wait", + "Local registration", + 1, +) +luCommand("r1", 'vtysh -c "show vnc registrations"', ".", "none") +luCommand( + "r3", + 'vtysh -c "show vnc registrations"', + "111.111.111.0/24", + "wait", + "See registration", +) +luCommand( + "r4", + 'vtysh -c "show vnc registrations"', + "111.111.111.0/24", + "wait", + "See registration", +) +luCommand( + "r1", + 'vtysh -c "debug rfapi-dev close vn 20.0.0.1 un 1.1.1.21"', + "status 0", + "pass", + "Closed RFAPI", +) +luCommand( + "r1", + 'vtysh -c "show vnc registrations"', + "Locally: *Active: 1 .* Remotely: *Active: 3", + "wait", + "See cleanup", +) +luCommand( + "r3", + 'vtysh -c "show vnc registrations"', + "Locally: *Active: 1 .* Remotely: *Active: 3", + "wait", + "See cleanup", +) +luCommand( + "r4", + 'vtysh -c "show vnc registrations"', + "Locally: *Active: 2 .* Remotely: *Active: 2", + "wait", + "See cleanup", +) +luCommand( + "r1", + 'vtysh -c "show vnc registrations"', + "In Holddown: *Active: 0", + "wait", + "Out of holddown", + 20, +) +luCommand( + "r3", + 'vtysh -c "show vnc registrations"', + "In Holddown: *Active: 0", + "wait", + "Out of holddown", +) +luCommand( + "r4", + 'vtysh -c "show vnc registrations"', + "In Holddown: *Active: 0", + "wait", + "Out of holddown", +) diff --git a/tests/topotests/bgp_rfapi_basic_sanity/scripts/check_routes.py b/tests/topotests/bgp_rfapi_basic_sanity/scripts/check_routes.py index a380c79fcf..1caa827ce2 100644 --- a/tests/topotests/bgp_rfapi_basic_sanity/scripts/check_routes.py +++ b/tests/topotests/bgp_rfapi_basic_sanity/scripts/check_routes.py @@ -1,19 +1,74 @@ from lutil import luCommand -luCommand('r1','vtysh -c "show bgp ipv4 vpn"','','none','VPN SAFI') -luCommand('r2','vtysh -c "show bgp ipv4 vpn"','','none','VPN SAFI') -luCommand('r3','vtysh -c "show bgp ipv4 vpn"','','none','VPN SAFI') -luCommand('r4','vtysh -c "show bgp ipv4 vpn"','','none','VPN SAFI') -luCommand('r1','vtysh -c "show vnc registrations"','Locally: *Active: 1 .* Remotely: *Active: 3','wait','See all registrations') -luCommand('r3','vtysh -c "show vnc registrations"','Locally: *Active: 1 .* Remotely: *Active: 3','wait','See all registrations') -luCommand('r4','vtysh -c "show vnc registrations"','Locally: *Active: 2 .* Remotely: *Active: 2','wait','See all registrations') -num = '4 routes and 4' -luCommand('r1','vtysh -c "show bgp ipv4 vpn"',num,'pass','VPN SAFI okay') -luCommand('r2','vtysh -c "show bgp ipv4 vpn"',num,'pass','VPN SAFI okay') -luCommand('r3','vtysh -c "show bgp ipv4 vpn"',num,'pass','VPN SAFI okay') -luCommand('r4','vtysh -c "show bgp ipv4 vpn"',num,'pass','VPN SAFI okay') -luCommand('r1','vtysh -c "debug rfapi-dev query vn 10.0.0.1 un 1.1.1.1 target 22.22.22.22"','pfx=', 'pass', 'Query R2s info') -luCommand('r1','vtysh -c "debug rfapi-dev query vn 10.0.0.1 un 1.1.1.1 target 33.33.33.33"','pfx=', 'pass', 'Query R4s info') -luCommand('r3','vtysh -c "debug rfapi-dev query vn 10.0.0.2 un 2.2.2.2 target 11.11.11.11"','11.11.11.0/24.*11.11.11.0/24.*', 'pass', 'Query R1s+R4s info') -luCommand('r3','vtysh -c "debug rfapi-dev query vn 10.0.0.2 un 2.2.2.2 target 33.33.33.33"','pfx=', 'pass', 'Query R4s info') -luCommand('r4','vtysh -c "debug rfapi-dev query vn 10.0.0.3 un 3.3.3.3 target 11.11.11.11"','11.11.11.0/24.*11.11.11.0/24.*', 'pass', 'Query R1s+R4s info') -luCommand('r4','vtysh -c "debug rfapi-dev query vn 10.0.0.3 un 3.3.3.3 target 22.22.22.22"','pfx=', 'pass', 'Query R2s info') + +luCommand("r1", 'vtysh -c "show bgp ipv4 vpn"', "", "none", "VPN SAFI") +luCommand("r2", 'vtysh -c "show bgp ipv4 vpn"', "", "none", "VPN SAFI") +luCommand("r3", 'vtysh -c "show bgp ipv4 vpn"', "", "none", "VPN SAFI") +luCommand("r4", 'vtysh -c "show bgp ipv4 vpn"', "", "none", "VPN SAFI") +luCommand( + "r1", + 'vtysh -c "show vnc registrations"', + "Locally: *Active: 1 .* Remotely: *Active: 3", + "wait", + "See all registrations", +) +luCommand( + "r3", + 'vtysh -c "show vnc registrations"', + "Locally: *Active: 1 .* Remotely: *Active: 3", + "wait", + "See all registrations", +) +luCommand( + "r4", + 'vtysh -c "show vnc registrations"', + "Locally: *Active: 2 .* Remotely: *Active: 2", + "wait", + "See all registrations", +) +num = "4 routes and 4" +luCommand("r1", 'vtysh -c "show bgp ipv4 vpn"', num, "pass", "VPN SAFI okay") +luCommand("r2", 'vtysh -c "show bgp ipv4 vpn"', num, "pass", "VPN SAFI okay") +luCommand("r3", 'vtysh -c "show bgp ipv4 vpn"', num, "pass", "VPN SAFI okay") +luCommand("r4", 'vtysh -c "show bgp ipv4 vpn"', num, "pass", "VPN SAFI okay") +luCommand( + "r1", + 'vtysh -c "debug rfapi-dev query vn 10.0.0.1 un 1.1.1.1 target 22.22.22.22"', + "pfx=", + "pass", + "Query R2s info", +) +luCommand( + "r1", + 'vtysh -c "debug rfapi-dev query vn 10.0.0.1 un 1.1.1.1 target 33.33.33.33"', + "pfx=", + "pass", + "Query R4s info", +) +luCommand( + "r3", + 'vtysh -c "debug rfapi-dev query vn 10.0.0.2 un 2.2.2.2 target 11.11.11.11"', + "11.11.11.0/24.*11.11.11.0/24.*", + "pass", + "Query R1s+R4s info", +) +luCommand( + "r3", + 'vtysh -c "debug rfapi-dev query vn 10.0.0.2 un 2.2.2.2 target 33.33.33.33"', + "pfx=", + "pass", + "Query R4s info", +) +luCommand( + "r4", + 'vtysh -c "debug rfapi-dev query vn 10.0.0.3 un 3.3.3.3 target 11.11.11.11"', + "11.11.11.0/24.*11.11.11.0/24.*", + "pass", + "Query R1s+R4s info", +) +luCommand( + "r4", + 'vtysh -c "debug rfapi-dev query vn 10.0.0.3 un 3.3.3.3 target 22.22.22.22"', + "pfx=", + "pass", + "Query R2s info", +) diff --git a/tests/topotests/bgp_rfapi_basic_sanity/scripts/check_timeout.py b/tests/topotests/bgp_rfapi_basic_sanity/scripts/check_timeout.py index f4467ecc33..e68e9e93ab 100644 --- a/tests/topotests/bgp_rfapi_basic_sanity/scripts/check_timeout.py +++ b/tests/topotests/bgp_rfapi_basic_sanity/scripts/check_timeout.py @@ -1,68 +1,325 @@ from lutil import luCommand -holddownFactorSet = luCommand('r1','vtysh -c "show running"','rfp holddown-factor','none','Holddown factor set') -luCommand('r1','vtysh -c "show vnc registrations"','.','none') -luCommand('r3','vtysh -c "show vnc registrations"','.','none') -luCommand('r4','vtysh -c "show vnc registrations"','.','none') + +holddownFactorSet = luCommand( + "r1", + 'vtysh -c "show running"', + "rfp holddown-factor", + "none", + "Holddown factor set", +) +luCommand("r1", 'vtysh -c "show vnc registrations"', ".", "none") +luCommand("r3", 'vtysh -c "show vnc registrations"', ".", "none") +luCommand("r4", 'vtysh -c "show vnc registrations"', ".", "none") if not holddownFactorSet: - luCommand('r1','vtysh -c "show vnc summary"','.','pass','Holddown factor not set -- skipping test') + luCommand( + "r1", + 'vtysh -c "show vnc summary"', + ".", + "pass", + "Holddown factor not set -- skipping test", + ) else: - #holddown time test - luCommand('r1','vtysh -c "debug rfapi-dev register vn 10.0.0.1 un 1.1.1.1 prefix 1.111.0.0/16 lifetime 10"','', 'none', 'Prefix registered') - luCommand('r1','vtysh -c "show vnc registrations local"','1.111.0.0/16','wait','Local registration') + # holddown time test + luCommand( + "r1", + 'vtysh -c "debug rfapi-dev register vn 10.0.0.1 un 1.1.1.1 prefix 1.111.0.0/16 lifetime 10"', + "", + "none", + "Prefix registered", + ) + luCommand( + "r1", + 'vtysh -c "show vnc registrations local"', + "1.111.0.0/16", + "wait", + "Local registration", + ) - luCommand('r3','vtysh -c "debug rfapi-dev register vn 10.0.0.2 un 2.2.2.2 prefix 1.222.0.0/16 lifetime 10"','', 'none', 'Prefix registered') - luCommand('r3','vtysh -c "show vnc registrations local"','1.222.0.0/16','wait','Local registration') + luCommand( + "r3", + 'vtysh -c "debug rfapi-dev register vn 10.0.0.2 un 2.2.2.2 prefix 1.222.0.0/16 lifetime 10"', + "", + "none", + "Prefix registered", + ) + luCommand( + "r3", + 'vtysh -c "show vnc registrations local"', + "1.222.0.0/16", + "wait", + "Local registration", + ) - luCommand('r4','vtysh -c "show vnc registrations"','Remotely: *Active: 4 ','wait', 'See registrations, L=10') + luCommand( + "r4", + 'vtysh -c "show vnc registrations"', + "Remotely: *Active: 4 ", + "wait", + "See registrations, L=10", + ) - luCommand('r4','vtysh -c "debug rfapi-dev register vn 10.0.0.3 un 3.3.3.3 prefix 1.222.0.0/16 lifetime 5 cost 50"','', 'none', 'MP Prefix registered') - luCommand('r4','vtysh -c "show vnc registrations local"','1.222.0.0/16','wait','Local registration (MP prefix)') + luCommand( + "r4", + 'vtysh -c "debug rfapi-dev register vn 10.0.0.3 un 3.3.3.3 prefix 1.222.0.0/16 lifetime 5 cost 50"', + "", + "none", + "MP Prefix registered", + ) + luCommand( + "r4", + 'vtysh -c "show vnc registrations local"', + "1.222.0.0/16", + "wait", + "Local registration (MP prefix)", + ) - luCommand('r1','vtysh -c "show vnc registrations"','.','none') - luCommand('r3','vtysh -c "show vnc registrations"','.','none') + luCommand("r1", 'vtysh -c "show vnc registrations"', ".", "none") + luCommand("r3", 'vtysh -c "show vnc registrations"', ".", "none") - luCommand('r4','vtysh -c "debug rfapi-dev query vn 10.0.0.3 un 3.3.3.3 target 1.111.111.111"','pfx=', 'pass', 'Query R1s info') - luCommand('r4','vtysh -c "debug rfapi-dev query vn 10.0.0.3 un 3.3.3.3 target 1.222.222.222"','1.222.0.0/16.*1.222.0.0/16', 'pass', 'Query R3s+R4s info') + luCommand( + "r4", + 'vtysh -c "debug rfapi-dev query vn 10.0.0.3 un 3.3.3.3 target 1.111.111.111"', + "pfx=", + "pass", + "Query R1s info", + ) + luCommand( + "r4", + 'vtysh -c "debug rfapi-dev query vn 10.0.0.3 un 3.3.3.3 target 1.222.222.222"', + "1.222.0.0/16.*1.222.0.0/16", + "pass", + "Query R3s+R4s info", + ) - luCommand('r4','vtysh -c "debug rfapi-dev unregister vn 10.0.0.3 un 3.3.3.3 prefix 1.222.0.0/16"','', 'none', 'MP Prefix removed') - luCommand('r4','vtysh -c "show vnc registrations"','In Holddown: *Active: 1 ','wait', 'MP prefix in holddown') - luCommand('r1','vtysh -c "show vnc registrations"','In Holddown: *Active: 1 ','wait', 'MP prefix in holddown') - luCommand('r3','vtysh -c "show vnc registrations"','In Holddown: *Active: 1 ','wait', 'MP prefix in holddown') - luCommand('r1','vtysh -c "debug rfapi-dev query vn 10.0.0.1 un 1.1.1.1 target 1.222.222.222"','1.222.0.0/16', 'pass', 'Query R3s info') - luCommand('r1','vtysh -c "debug rfapi-dev unregister vn 10.0.0.1 un 1.1.1.1 prefix 1.111.0.0/16"','', 'none', 'Prefix timeout') - luCommand('r1','vtysh -c "show vnc registrations holddown"','1.111.0.0/16','wait','Local holddown',1) - luCommand('r3','vtysh -c "debug rfapi-dev unregister vn 10.0.0.2 un 2.2.2.2 prefix 1.222.0.0/16"','', 'none', 'Prefix timeout') - luCommand('r3','vtysh -c "show vnc registrations holddown"','1.222.0.0/16','wait','Local holddown',1) - luCommand('r4','vtysh -c "show vnc registrations"','.','none') - luCommand('r4','vtysh -c "show vnc registrations"','.','none') + luCommand( + "r4", + 'vtysh -c "debug rfapi-dev unregister vn 10.0.0.3 un 3.3.3.3 prefix 1.222.0.0/16"', + "", + "none", + "MP Prefix removed", + ) + luCommand( + "r4", + 'vtysh -c "show vnc registrations"', + "In Holddown: *Active: 1 ", + "wait", + "MP prefix in holddown", + ) + luCommand( + "r1", + 'vtysh -c "show vnc registrations"', + "In Holddown: *Active: 1 ", + "wait", + "MP prefix in holddown", + ) + luCommand( + "r3", + 'vtysh -c "show vnc registrations"', + "In Holddown: *Active: 1 ", + "wait", + "MP prefix in holddown", + ) + luCommand( + "r1", + 'vtysh -c "debug rfapi-dev query vn 10.0.0.1 un 1.1.1.1 target 1.222.222.222"', + "1.222.0.0/16", + "pass", + "Query R3s info", + ) + luCommand( + "r1", + 'vtysh -c "debug rfapi-dev unregister vn 10.0.0.1 un 1.1.1.1 prefix 1.111.0.0/16"', + "", + "none", + "Prefix timeout", + ) + luCommand( + "r1", + 'vtysh -c "show vnc registrations holddown"', + "1.111.0.0/16", + "wait", + "Local holddown", + 1, + ) + luCommand( + "r3", + 'vtysh -c "debug rfapi-dev unregister vn 10.0.0.2 un 2.2.2.2 prefix 1.222.0.0/16"', + "", + "none", + "Prefix timeout", + ) + luCommand( + "r3", + 'vtysh -c "show vnc registrations holddown"', + "1.222.0.0/16", + "wait", + "Local holddown", + 1, + ) + luCommand("r4", 'vtysh -c "show vnc registrations"', ".", "none") + luCommand("r4", 'vtysh -c "show vnc registrations"', ".", "none") - luCommand('r4','vtysh -c "show vnc registrations"','In Holddown: *Active: 2 ','wait', 'In holddown') - luCommand('r1','vtysh -c "show vnc registrations"','In Holddown: *Active: 2 ','wait', 'In holddown') - luCommand('r3','vtysh -c "show vnc registrations"','In Holddown: *Active: 2 ','wait', 'In holddown') + luCommand( + "r4", + 'vtysh -c "show vnc registrations"', + "In Holddown: *Active: 2 ", + "wait", + "In holddown", + ) + luCommand( + "r1", + 'vtysh -c "show vnc registrations"', + "In Holddown: *Active: 2 ", + "wait", + "In holddown", + ) + luCommand( + "r3", + 'vtysh -c "show vnc registrations"', + "In Holddown: *Active: 2 ", + "wait", + "In holddown", + ) - luCommand('r1','vtysh -c "show vnc registrations"','In Holddown: *Active: 0','wait','Out of holddown',20) - luCommand('r3','vtysh -c "show vnc registrations"','In Holddown: *Active: 0','wait','Out of holddown') - luCommand('r4','vtysh -c "show vnc registrations"','In Holddown: *Active: 0','wait','Out of holddown') + luCommand( + "r1", + 'vtysh -c "show vnc registrations"', + "In Holddown: *Active: 0", + "wait", + "Out of holddown", + 20, + ) + luCommand( + "r3", + 'vtysh -c "show vnc registrations"', + "In Holddown: *Active: 0", + "wait", + "Out of holddown", + ) + luCommand( + "r4", + 'vtysh -c "show vnc registrations"', + "In Holddown: *Active: 0", + "wait", + "Out of holddown", + ) - #kill test - luCommand('r1','vtysh -c "debug rfapi-dev register vn 10.0.0.1 un 1.1.1.1 prefix 1.111.0.0/16 lifetime 10"','', 'none', 'Prefix registered') - luCommand('r1','vtysh -c "show vnc registrations local"','1.111.0.0/16','wait','Local registration') + # kill test + luCommand( + "r1", + 'vtysh -c "debug rfapi-dev register vn 10.0.0.1 un 1.1.1.1 prefix 1.111.0.0/16 lifetime 10"', + "", + "none", + "Prefix registered", + ) + luCommand( + "r1", + 'vtysh -c "show vnc registrations local"', + "1.111.0.0/16", + "wait", + "Local registration", + ) - luCommand('r3','vtysh -c "debug rfapi-dev register vn 10.0.0.2 un 2.2.2.2 prefix 1.222.0.0/16 lifetime 10"','', 'none', 'Prefix registered') - luCommand('r3','vtysh -c "show vnc registrations local"','1.222.0.0/16','wait','Local registration') + luCommand( + "r3", + 'vtysh -c "debug rfapi-dev register vn 10.0.0.2 un 2.2.2.2 prefix 1.222.0.0/16 lifetime 10"', + "", + "none", + "Prefix registered", + ) + luCommand( + "r3", + 'vtysh -c "show vnc registrations local"', + "1.222.0.0/16", + "wait", + "Local registration", + ) - luCommand('r4','vtysh -c "show vnc registrations"','Remotely: *Active: 4 ','wait', 'See registrations L=10 (pre-kill)',5) - luCommand('r1','vtysh -c "show vnc registrations"','.','none') - luCommand('r3','vtysh -c "show vnc registrations"','.','none') - luCommand('r1','vtysh -c "debug rfapi-dev unregister vn 10.0.0.1 un 1.1.1.1 prefix 1.111.0.0/16 kill"','', 'none', 'Prefix kill') - luCommand('r1','vtysh -c "show vnc registrations"','Locally: *Active: 1 .* Remotely: *Active: 4 .*In Holddown: *Active: 0','wait','Registration killed',1) - luCommand('r3','vtysh -c "show vnc registrations"','Locally: *Active: 2 .* Remotely: *Active: 3 .*In Holddown: *Active: 1','wait','Remote in holddown',5) - luCommand('r4','vtysh -c "show vnc registrations"','Locally: *Active: 2 .* Remotely: *Active: 3 .*In Holddown: *Active: 1','wait','Remote in holddown',5) + luCommand( + "r4", + 'vtysh -c "show vnc registrations"', + "Remotely: *Active: 4 ", + "wait", + "See registrations L=10 (pre-kill)", + 5, + ) + luCommand("r1", 'vtysh -c "show vnc registrations"', ".", "none") + luCommand("r3", 'vtysh -c "show vnc registrations"', ".", "none") + luCommand( + "r1", + 'vtysh -c "debug rfapi-dev unregister vn 10.0.0.1 un 1.1.1.1 prefix 1.111.0.0/16 kill"', + "", + "none", + "Prefix kill", + ) + luCommand( + "r1", + 'vtysh -c "show vnc registrations"', + "Locally: *Active: 1 .* Remotely: *Active: 4 .*In Holddown: *Active: 0", + "wait", + "Registration killed", + 1, + ) + luCommand( + "r3", + 'vtysh -c "show vnc registrations"', + "Locally: *Active: 2 .* Remotely: *Active: 3 .*In Holddown: *Active: 1", + "wait", + "Remote in holddown", + 5, + ) + luCommand( + "r4", + 'vtysh -c "show vnc registrations"', + "Locally: *Active: 2 .* Remotely: *Active: 3 .*In Holddown: *Active: 1", + "wait", + "Remote in holddown", + 5, + ) - luCommand('r3','vtysh -c "debug rfapi-dev unregister vn 10.0.0.2 un 2.2.2.2 prefix 1.222.0.0/16 kill"','', 'none', 'Prefix kill') - luCommand('r3','vtysh -c "show vnc registrations"','Locally: *Active: 1 .* Remotely: *Active: 3 .*In Holddown: *Active: 1','wait','Registration killed',1) - luCommand('r4','vtysh -c "show vnc registrations"','Locally: *Active: 2 .* Remotely: *Active: 2 .*In Holddown: *Active: 2','wait','Remote in holddown',5) + luCommand( + "r3", + 'vtysh -c "debug rfapi-dev unregister vn 10.0.0.2 un 2.2.2.2 prefix 1.222.0.0/16 kill"', + "", + "none", + "Prefix kill", + ) + luCommand( + "r3", + 'vtysh -c "show vnc registrations"', + "Locally: *Active: 1 .* Remotely: *Active: 3 .*In Holddown: *Active: 1", + "wait", + "Registration killed", + 1, + ) + luCommand( + "r4", + 'vtysh -c "show vnc registrations"', + "Locally: *Active: 2 .* Remotely: *Active: 2 .*In Holddown: *Active: 2", + "wait", + "Remote in holddown", + 5, + ) - luCommand('r1','vtysh -c "show vnc registrations"','Locally: *Active: 1 .* Remotely: *Active: 3 .*In Holddown: *Active: 0','wait','Out of holddown',20) - luCommand('r3','vtysh -c "show vnc registrations"','Locally: *Active: 1 .* Remotely: *Active: 3 .*In Holddown: *Active: 0','wait','Out of holddown') - luCommand('r4','vtysh -c "show vnc registrations"','Locally: *Active: 2 .* Remotely: *Active: 2 .*In Holddown: *Active: 0','wait','Out of holddown') + luCommand( + "r1", + 'vtysh -c "show vnc registrations"', + "Locally: *Active: 1 .* Remotely: *Active: 3 .*In Holddown: *Active: 0", + "wait", + "Out of holddown", + 20, + ) + luCommand( + "r3", + 'vtysh -c "show vnc registrations"', + "Locally: *Active: 1 .* Remotely: *Active: 3 .*In Holddown: *Active: 0", + "wait", + "Out of holddown", + ) + luCommand( + "r4", + 'vtysh -c "show vnc registrations"', + "Locally: *Active: 2 .* Remotely: *Active: 2 .*In Holddown: *Active: 0", + "wait", + "Out of holddown", + ) diff --git a/tests/topotests/bgp_rfapi_basic_sanity/scripts/cleanup_all.py b/tests/topotests/bgp_rfapi_basic_sanity/scripts/cleanup_all.py index 096e97fa94..eea977bfaf 100644 --- a/tests/topotests/bgp_rfapi_basic_sanity/scripts/cleanup_all.py +++ b/tests/topotests/bgp_rfapi_basic_sanity/scripts/cleanup_all.py @@ -1,32 +1,124 @@ from lutil import luCommand -luCommand('r1','vtysh -c "debug rfapi-dev unregister vn 10.0.0.1 un 1.1.1.1 prefix 11.11.11.0/24"','', 'none', 'Prefix removed') -luCommand('r1','vtysh -c "show vnc registrations"','Locally: *Active: 0 ','wait','Local registration removed') -luCommand('r1','vtysh -c "debug rfapi-dev close vn 10.0.0.1 un 1.1.1.1"','status 0', 'pass', 'Closed RFAPI') -luCommand('r3','vtysh -c "debug rfapi-dev unregister vn 10.0.0.2 un 2.2.2.2 prefix 22.22.22.0/24"','', 'none', 'Prefix removed') -luCommand('r3','vtysh -c "show vnc registrations"','Locally: *Active: 0 ','wait','Local registration removed') -luCommand('r3','vtysh -c "debug rfapi-dev close vn 10.0.0.2 un 2.2.2.2"','status 0', 'pass', 'Closed RFAPI') +luCommand( + "r1", + 'vtysh -c "debug rfapi-dev unregister vn 10.0.0.1 un 1.1.1.1 prefix 11.11.11.0/24"', + "", + "none", + "Prefix removed", +) +luCommand( + "r1", + 'vtysh -c "show vnc registrations"', + "Locally: *Active: 0 ", + "wait", + "Local registration removed", +) +luCommand( + "r1", + 'vtysh -c "debug rfapi-dev close vn 10.0.0.1 un 1.1.1.1"', + "status 0", + "pass", + "Closed RFAPI", +) -luCommand('r4','vtysh -c "debug rfapi-dev unregister vn 10.0.0.3 un 3.3.3.3 prefix 33.33.33.0/24"','', 'none', 'Prefix removed') -luCommand('r4','vtysh -c "debug rfapi-dev unregister vn 10.0.0.3 un 3.3.3.3 prefix 11.11.11.0/24"','', 'none', 'MP prefix removed') -luCommand('r4','vtysh -c "show vnc registrations"','Locally: *Active: 0 ','wait','Local registration removed') -luCommand('r4','vtysh -c "debug rfapi-dev close vn 10.0.0.3 un 3.3.3.3"','status 0', 'pass', 'Closed RFAPI') +luCommand( + "r3", + 'vtysh -c "debug rfapi-dev unregister vn 10.0.0.2 un 2.2.2.2 prefix 22.22.22.0/24"', + "", + "none", + "Prefix removed", +) +luCommand( + "r3", + 'vtysh -c "show vnc registrations"', + "Locally: *Active: 0 ", + "wait", + "Local registration removed", +) +luCommand( + "r3", + 'vtysh -c "debug rfapi-dev close vn 10.0.0.2 un 2.2.2.2"', + "status 0", + "pass", + "Closed RFAPI", +) -luCommand('r1','vtysh -c "show vnc registrations"','Locally: *Active: 0 .* Remotely: *Active: 0','wait','All registrations cleared') -luCommand('r3','vtysh -c "show vnc registrations"','Locally: *Active: 0 .* Remotely: *Active: 0','wait','All registrations cleared') -luCommand('r4','vtysh -c "show vnc registrations"','Locally: *Active: 0 .* Remotely: *Active: 0','wait','All registrations cleared') +luCommand( + "r4", + 'vtysh -c "debug rfapi-dev unregister vn 10.0.0.3 un 3.3.3.3 prefix 33.33.33.0/24"', + "", + "none", + "Prefix removed", +) +luCommand( + "r4", + 'vtysh -c "debug rfapi-dev unregister vn 10.0.0.3 un 3.3.3.3 prefix 11.11.11.0/24"', + "", + "none", + "MP prefix removed", +) +luCommand( + "r4", + 'vtysh -c "show vnc registrations"', + "Locally: *Active: 0 ", + "wait", + "Local registration removed", +) +# luCommand('r4','vtysh -c "debug rfapi-dev close vn 10.0.0.3 un 3.3.3.3"','status 0', 'pass', 'Closed RFAPI') +luCommand("r4", 'vtysh -c "clear vnc nve *"', ".", "pass", "Cleared NVEs") -num = '0 exist' -luCommand('r1','vtysh -c "show bgp ipv4 vpn"',num,'pass','VPN SAFI clear') -luCommand('r2','vtysh -c "show bgp ipv4 vpn"',num,'pass','VPN SAFI clear') -luCommand('r3','vtysh -c "show bgp ipv4 vpn"',num,'pass','VPN SAFI clear') -luCommand('r4','vtysh -c "show bgp ipv4 vpn"',num,'pass','VPN SAFI clear') +luCommand( + "r1", + 'vtysh -c "show vnc registrations"', + "Locally: *Active: 0 .* Remotely: *Active: 0", + "wait", + "All registrations cleared", +) +luCommand( + "r3", + 'vtysh -c "show vnc registrations"', + "Locally: *Active: 0 .* Remotely: *Active: 0", + "wait", + "All registrations cleared", +) +luCommand( + "r4", + 'vtysh -c "show vnc registrations"', + "Locally: *Active: 0 .* Remotely: *Active: 0", + "wait", + "All registrations cleared", +) -luCommand('r1','vtysh -c "show vnc registrations"','Locally: *Active: 0 .* Remotely: *Active: 0 .*In Holddown: *Active: 0','wait','No holddowns',20) -luCommand('r3','vtysh -c "show vnc registrations"','Locally: *Active: 0 .* Remotely: *Active: 0 .*In Holddown: *Active: 0','wait','No holddowns') -luCommand('r4','vtysh -c "show vnc registrations"','Locally: *Active: 0 .* Remotely: *Active: 0 .*In Holddown: *Active: 0','wait','No holddowns') +num = "0 exist" +luCommand("r1", 'vtysh -c "show bgp ipv4 vpn"', num, "pass", "VPN SAFI clear") +luCommand("r2", 'vtysh -c "show bgp ipv4 vpn"', num, "pass", "VPN SAFI clear") +luCommand("r3", 'vtysh -c "show bgp ipv4 vpn"', num, "pass", "VPN SAFI clear") +luCommand("r4", 'vtysh -c "show bgp ipv4 vpn"', num, "pass", "VPN SAFI clear") -luCommand('r1','vtysh -c "show vnc summary"','.','none') -luCommand('r3','vtysh -c "show vnc summary"','.','none') -luCommand('r4','vtysh -c "show vnc summary"','.','none') +luCommand( + "r1", + 'vtysh -c "show vnc registrations"', + "Locally: *Active: 0 .* Remotely: *Active: 0 .*In Holddown: *Active: 0", + "wait", + "No holddowns", + 20, +) +luCommand( + "r3", + 'vtysh -c "show vnc registrations"', + "Locally: *Active: 0 .* Remotely: *Active: 0 .*In Holddown: *Active: 0", + "wait", + "No holddowns", +) +luCommand( + "r4", + 'vtysh -c "show vnc registrations"', + "Locally: *Active: 0 .* Remotely: *Active: 0 .*In Holddown: *Active: 0", + "wait", + "No holddowns", +) +luCommand("r1", 'vtysh -c "show vnc summary"', ".", "none") +luCommand("r3", 'vtysh -c "show vnc summary"', ".", "none") +luCommand("r4", 'vtysh -c "show vnc summary"', ".", "none") diff --git a/tests/topotests/bgp_rfapi_basic_sanity/test_bgp_rfapi_basic_sanity.py b/tests/topotests/bgp_rfapi_basic_sanity/test_bgp_rfapi_basic_sanity.py index 0e1f236b7d..cd59bbc395 100755 --- a/tests/topotests/bgp_rfapi_basic_sanity/test_bgp_rfapi_basic_sanity.py +++ b/tests/topotests/bgp_rfapi_basic_sanity/test_bgp_rfapi_basic_sanity.py @@ -25,64 +25,71 @@ import sys import pytest -sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), '..')) +sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")) from lib.ltemplate import * + def test_add_routes(): CliOnFail = None # For debugging, uncomment the next line - #CliOnFail = 'tgen.mininet_cli' - CheckFunc = 'ltemplateVersionCheck(\'3.1\')' - #uncomment next line to start cli *before* script is run - #CheckFunc = 'ltemplateVersionCheck(\'3.1\', cli=True)' - ltemplateTest('scripts/add_routes.py', False, CliOnFail, CheckFunc) + # CliOnFail = 'tgen.mininet_cli' + CheckFunc = "ltemplateVersionCheck('3.1')" + # uncomment next line to start cli *before* script is run + # CheckFunc = 'ltemplateVersionCheck(\'3.1\', cli=True)' + ltemplateTest("scripts/add_routes.py", False, CliOnFail, CheckFunc) + def test_adjacencies(): CliOnFail = None # For debugging, uncomment the next line - #CliOnFail = 'tgen.mininet_cli' - CheckFunc = 'ltemplateVersionCheck(\'3.1\')' - #uncomment next line to start cli *before* script is run - #CheckFunc = 'ltemplateVersionCheck(\'3.1\', cli=True)' - ltemplateTest('scripts/adjacencies.py', False, CliOnFail, CheckFunc) + # CliOnFail = 'tgen.mininet_cli' + CheckFunc = "ltemplateVersionCheck('3.1')" + # uncomment next line to start cli *before* script is run + # CheckFunc = 'ltemplateVersionCheck(\'3.1\', cli=True)' + ltemplateTest("scripts/adjacencies.py", False, CliOnFail, CheckFunc) + def test_check_routes(): CliOnFail = None # For debugging, uncomment the next line - #CliOnFail = 'tgen.mininet_cli' - CheckFunc = 'ltemplateVersionCheck(\'3.1\')' - #uncomment next line to start cli *before* script is run - #CheckFunc = 'ltemplateVersionCheck(\'3.1\', cli=True)' - ltemplateTest('scripts/check_routes.py', False, CliOnFail, CheckFunc) + # CliOnFail = 'tgen.mininet_cli' + CheckFunc = "ltemplateVersionCheck('3.1')" + # uncomment next line to start cli *before* script is run + # CheckFunc = 'ltemplateVersionCheck(\'3.1\', cli=True)' + ltemplateTest("scripts/check_routes.py", False, CliOnFail, CheckFunc) + def test_check_close(): CliOnFail = None # For debugging, uncomment the next line - #CliOnFail = 'tgen.mininet_cli' - CheckFunc = 'ltemplateVersionCheck(\'3.1\')' - #uncomment next line to start cli *before* script is run - #CheckFunc = 'ltemplateVersionCheck(\'3.1\', cli=True)' - ltemplateTest('scripts/check_close.py', False, CliOnFail, CheckFunc) + # CliOnFail = 'tgen.mininet_cli' + CheckFunc = "ltemplateVersionCheck('3.1')" + # uncomment next line to start cli *before* script is run + # CheckFunc = 'ltemplateVersionCheck(\'3.1\', cli=True)' + ltemplateTest("scripts/check_close.py", False, CliOnFail, CheckFunc) + def test_check_timeout(): CliOnFail = None # For debugging, uncomment the next line - #CliOnFail = 'tgen.mininet_cli' - CheckFunc = 'ltemplateVersionCheck(\'3.1\')' - #uncomment next line to start cli *before* script is run - #CheckFunc = 'ltemplateVersionCheck(\'3.1\', cli=True)' - ltemplateTest('scripts/check_timeout.py', False, CliOnFail, CheckFunc) + # CliOnFail = 'tgen.mininet_cli' + CheckFunc = "ltemplateVersionCheck('3.1')" + # uncomment next line to start cli *before* script is run + # CheckFunc = 'ltemplateVersionCheck(\'3.1\', cli=True)' + ltemplateTest("scripts/check_timeout.py", False, CliOnFail, CheckFunc) + def test_cleanup_all(): CliOnFail = None # For debugging, uncomment the next line - #CliOnFail = 'tgen.mininet_cli' - CheckFunc = 'ltemplateVersionCheck(\'3.1\')' - #uncomment next line to start cli *before* script is run - #CheckFunc = 'ltemplateVersionCheck(\'3.1\', cli=True)' - ltemplateTest('scripts/cleanup_all.py', False, CliOnFail, CheckFunc) + # CliOnFail = 'tgen.mininet_cli' + CheckFunc = "ltemplateVersionCheck('3.1')" + # uncomment next line to start cli *before* script is run + # CheckFunc = 'ltemplateVersionCheck(\'3.1\', cli=True)' + ltemplateTest("scripts/cleanup_all.py", False, CliOnFail, CheckFunc) + -if __name__ == '__main__': +if __name__ == "__main__": retval = pytest.main(["-s"]) sys.exit(retval) diff --git a/tests/topotests/bgp_rfapi_basic_sanity_config2/r1/bgpd.conf b/tests/topotests/bgp_rfapi_basic_sanity_config2/r1/bgpd.conf index f7f5e2ee96..a38afd632f 100644 --- a/tests/topotests/bgp_rfapi_basic_sanity_config2/r1/bgpd.conf +++ b/tests/topotests/bgp_rfapi_basic_sanity_config2/r1/bgpd.conf @@ -3,11 +3,11 @@ frr defaults traditional hostname r1 password zebra log stdout notifications -log monitor notifications log commands router bgp 5226 bgp router-id 1.1.1.1 bgp cluster-id 1.1.1.1 + no bgp ebgp-requires-policy neighbor 2.2.2.2 remote-as 5226 neighbor 2.2.2.2 update-source 1.1.1.1 ! diff --git a/tests/topotests/bgp_rfapi_basic_sanity_config2/r2/bgpd.conf b/tests/topotests/bgp_rfapi_basic_sanity_config2/r2/bgpd.conf index 241c2ac0ae..95890f25b9 100644 --- a/tests/topotests/bgp_rfapi_basic_sanity_config2/r2/bgpd.conf +++ b/tests/topotests/bgp_rfapi_basic_sanity_config2/r2/bgpd.conf @@ -3,11 +3,11 @@ frr defaults traditional hostname r2 password zebra log stdout notifications -log monitor notifications log commands router bgp 5226 bgp router-id 2.2.2.2 bgp cluster-id 2.2.2.2 + no bgp ebgp-requires-policy neighbor 1.1.1.1 remote-as 5226 neighbor 1.1.1.1 update-source 2.2.2.2 neighbor 3.3.3.3 remote-as 5226 @@ -28,6 +28,3 @@ router bgp 5226 neighbor 4.4.4.4 route-reflector-client exit-address-family end - - - diff --git a/tests/topotests/bgp_rfapi_basic_sanity_config2/r3/bgpd.conf b/tests/topotests/bgp_rfapi_basic_sanity_config2/r3/bgpd.conf index 17e351988d..dbeb2c4665 100644 --- a/tests/topotests/bgp_rfapi_basic_sanity_config2/r3/bgpd.conf +++ b/tests/topotests/bgp_rfapi_basic_sanity_config2/r3/bgpd.conf @@ -3,11 +3,11 @@ frr defaults traditional hostname r3 password zebra log stdout notifications -log monitor notifications log commands router bgp 5226 bgp router-id 3.3.3.3 bgp cluster-id 3.3.3.3 + no bgp ebgp-requires-policy neighbor 2.2.2.2 remote-as 5226 neighbor 2.2.2.2 update-source 3.3.3.3 ! @@ -46,6 +46,3 @@ router bgp 5226 exit-vnc ! end - - - diff --git a/tests/topotests/bgp_rfapi_basic_sanity_config2/r4/bgpd.conf b/tests/topotests/bgp_rfapi_basic_sanity_config2/r4/bgpd.conf index 0b8808cb80..ae1787718c 100644 --- a/tests/topotests/bgp_rfapi_basic_sanity_config2/r4/bgpd.conf +++ b/tests/topotests/bgp_rfapi_basic_sanity_config2/r4/bgpd.conf @@ -3,11 +3,11 @@ frr defaults traditional hostname r4 password zebra log stdout notifications -log monitor notifications log commands router bgp 5226 bgp router-id 4.4.4.4 bgp cluster-id 4.4.4.4 + no bgp ebgp-requires-policy neighbor 2.2.2.2 remote-as 5226 neighbor 2.2.2.2 update-source 4.4.4.4 ! @@ -47,6 +47,3 @@ router bgp 5226 exit-vnc ! end - - - diff --git a/tests/topotests/bgp_rr_ibgp/spine1/bgpd.conf b/tests/topotests/bgp_rr_ibgp/spine1/bgpd.conf new file mode 100644 index 0000000000..fa77cce073 --- /dev/null +++ b/tests/topotests/bgp_rr_ibgp/spine1/bgpd.conf @@ -0,0 +1,9 @@ +hostname spine1 +router bgp 99 + no bgp ebgp-requires-policy + neighbor 192.168.2.1 remote-as internal + neighbor 192.168.4.2 remote-as internal + address-family ipv4 uni + redistribute connected + neighbor 192.168.2.1 route-reflector-client + neighbor 192.168.4.2 route-reflector-client diff --git a/tests/topotests/bgp_rr_ibgp/spine1/show_ip_route.json_ref b/tests/topotests/bgp_rr_ibgp/spine1/show_ip_route.json_ref new file mode 100644 index 0000000000..75ce1b149e --- /dev/null +++ b/tests/topotests/bgp_rr_ibgp/spine1/show_ip_route.json_ref @@ -0,0 +1,144 @@ +{ + "192.168.1.0\/24":[ + { + "prefix":"192.168.1.0\/24", + "protocol":"bgp", + "selected":true, + "destSelected":true, + "distance":200, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":13, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.2.1", + "afi":"ipv4", + "interfaceName":"spine1-eth0", + "active":true + } + ] + } + ], + "192.168.2.0\/24":[ + { + "prefix":"192.168.2.0\/24", + "protocol":"connected", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":8, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"spine1-eth0", + "active":true + } + ] + } + ], + "192.168.3.0\/24":[ + { + "prefix":"192.168.3.0\/24", + "protocol":"bgp", + "selected":true, + "destSelected":true, + "distance":200, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":13, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.4.2", + "afi":"ipv4", + "interfaceName":"spine1-eth1", + "active":true + } + ] + } + ], + "192.168.4.0\/24":[ + { + "prefix":"192.168.4.0\/24", + "protocol":"connected", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":8, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"spine1-eth1", + "active":true + } + ] + } + ], + "192.168.5.1\/32":[ + { + "prefix":"192.168.5.1\/32", + "protocol":"bgp", + "selected":true, + "destSelected":true, + "distance":200, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":13, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.2.1", + "afi":"ipv4", + "interfaceName":"spine1-eth0", + "active":true + } + ] + } + ], + "192.168.6.2\/32":[ + { + "prefix":"192.168.6.2\/32", + "protocol":"bgp", + "selected":true, + "destSelected":true, + "distance":200, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":13, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.4.2", + "afi":"ipv4", + "interfaceName":"spine1-eth1", + "active":true + } + ] + } + ] +} \ No newline at end of file diff --git a/tests/topotests/bgp_rr_ibgp/spine1/staticd.conf b/tests/topotests/bgp_rr_ibgp/spine1/staticd.conf new file mode 100644 index 0000000000..6d8f0952d3 --- /dev/null +++ b/tests/topotests/bgp_rr_ibgp/spine1/staticd.conf @@ -0,0 +1 @@ +hostname spine1 diff --git a/tests/topotests/bgp_rr_ibgp/spine1/zebra.conf b/tests/topotests/bgp_rr_ibgp/spine1/zebra.conf new file mode 100644 index 0000000000..ea25462d53 --- /dev/null +++ b/tests/topotests/bgp_rr_ibgp/spine1/zebra.conf @@ -0,0 +1,9 @@ +hostname spine1 +ip forwarding +ipv6 forwarding + +int spine1-eth0 + ip addr 192.168.2.3/24 + +int spine1-eth1 + ip addr 192.168.4.3/24 diff --git a/tests/topotests/bgp_rr_ibgp/test_bgp_rr_ibgp_topo1.py b/tests/topotests/bgp_rr_ibgp/test_bgp_rr_ibgp_topo1.py new file mode 100755 index 0000000000..da45e73ab4 --- /dev/null +++ b/tests/topotests/bgp_rr_ibgp/test_bgp_rr_ibgp_topo1.py @@ -0,0 +1,200 @@ +#!/usr/bin/env python + +# +# test_bgp_rr_ibgp_topo1.py +# +# Copyright (c) 2019 by +# Cumulus Networks, Inc. +# Donald Sharp +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_bgp_rr_ibgp_topo1.py: Testing IBGP with RR and no IGP + +Ensure that a basic rr topology comes up and correctly passes +routes around + +""" + +import os +import re +import sys +import pytest +import json + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + +##################################################### +## +## Network Topology Definition +## +##################################################### + + +class NetworkTopo(Topo): + "BGP_RR_IBGP Topology 1" + + def build(self, **_opts): + "Build function" + + tgen = get_topogen(self) + + tgen.add_router("tor1") + tgen.add_router("tor2") + tgen.add_router("spine1") + + # First switch is for a dummy interface (for local network) + # on tor1 + # 192.168.1.0/24 + switch = tgen.add_switch("sw1") + switch.add_link(tgen.gears["tor1"]) + + # 192.168.2.0/24 - tor1 <-> spine1 connection + switch = tgen.add_switch("sw2") + switch.add_link(tgen.gears["tor1"]) + switch.add_link(tgen.gears["spine1"]) + + # 3rd switch is for a dummy interface (for local netwokr) + # 192.168.3.0/24 - tor2 + switch = tgen.add_switch("sw3") + switch.add_link(tgen.gears["tor2"]) + + # 192.168.4.0/24 - tor2 <-> spine1 connection + switch = tgen.add_switch("sw4") + switch.add_link(tgen.gears["tor2"]) + switch.add_link(tgen.gears["spine1"]) + + +##################################################### +## +## Tests starting +## +##################################################### + + +def setup_module(module): + "Setup topology" + tgen = Topogen(NetworkTopo, module.__name__) + tgen.start_topology() + + # This is a sample of configuration loading. + router_list = tgen.routers() + for rname, router in router_list.iteritems(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + # tgen.mininet_cli() + + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + + +def test_converge_protocols(): + "Wait for protocol convergence" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + topotest.sleep(5, "Waiting for BGP_RR_IBGP convergence") + + +def test_bgp_rr_ibgp_routes(): + "Test Route Reflection" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Verify BGP_RR_IBGP Status + logger.info("Verifying BGP_RR_IBGP routes") + + +def test_zebra_ipv4_routingTable(): + "Test 'show ip route'" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + failures = 0 + router_list = tgen.routers().values() + for router in router_list: + output = router.vtysh_cmd("show ip route json", isjson=True) + refTableFile = "{}/{}/show_ip_route.json_ref".format(CWD, router.name) + expected = json.loads(open(refTableFile).read()) + + assertmsg = "Zebra IPv4 Routing Table verification failed for router {}".format( + router.name + ) + assert topotest.json_cmp(output, expected) is None, assertmsg + + +def test_shutdown_check_stderr(): + if os.environ.get("TOPOTESTS_CHECK_STDERR") is None: + pytest.skip("Skipping test for Stderr output and memory leaks") + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Verifying unexpected STDERR output from daemons") + + router_list = tgen.routers().values() + for router in router_list: + router.stop() + + log = tgen.net[router.name].getStdErr("bgpd") + if log: + logger.error("BGPd StdErr Log:" + log) + log = tgen.net[router.name].getStdErr("zebra") + if log: + logger.error("Zebra StdErr Log:" + log) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) + +# +# Auxiliary Functions +# diff --git a/tests/topotests/bgp_rr_ibgp/tor1/bgpd.conf b/tests/topotests/bgp_rr_ibgp/tor1/bgpd.conf new file mode 100644 index 0000000000..b028ab4e8b --- /dev/null +++ b/tests/topotests/bgp_rr_ibgp/tor1/bgpd.conf @@ -0,0 +1,5 @@ +hostname tor1 +router bgp 99 + no bgp ebgp-requires-policy + neighbor 192.168.2.3 remote-as internal + redistribute connected diff --git a/tests/topotests/bgp_rr_ibgp/tor1/show_ip_route.json_ref b/tests/topotests/bgp_rr_ibgp/tor1/show_ip_route.json_ref new file mode 100644 index 0000000000..6cfa02441f --- /dev/null +++ b/tests/topotests/bgp_rr_ibgp/tor1/show_ip_route.json_ref @@ -0,0 +1,157 @@ +{ + "192.168.1.0\/24":[ + { + "prefix":"192.168.1.0\/24", + "protocol":"connected", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":8, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"tor1-eth0", + "active":true + } + ] + } + ], + "192.168.2.0\/24":[ + { + "prefix":"192.168.2.0\/24", + "protocol":"connected", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":8, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"tor1-eth1", + "active":true + } + ] + } + ], + "192.168.3.0\/24":[ + { + "prefix":"192.168.3.0\/24", + "protocol":"bgp", + "selected":true, + "destSelected":true, + "distance":200, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":13, + "nexthops":[ + { + "flags":5, + "ip":"192.168.4.2", + "afi":"ipv4", + "active":true, + "recursive":true + }, + { + "flags":3, + "fib":true, + "ip":"192.168.2.3", + "afi":"ipv4", + "interfaceName":"tor1-eth1", + "active":true + } + ] + } + ], + "192.168.4.0\/24":[ + { + "prefix":"192.168.4.0\/24", + "protocol":"bgp", + "selected":true, + "destSelected":true, + "distance":200, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":13, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.2.3", + "afi":"ipv4", + "interfaceName":"tor1-eth1", + "active":true + } + ] + } + ], + "192.168.5.1\/32":[ + { + "prefix":"192.168.5.1\/32", + "protocol":"connected", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":8, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"lo", + "active":true + } + ] + } + ], + "192.168.6.2\/32":[ + { + "prefix":"192.168.6.2\/32", + "protocol":"bgp", + "selected":true, + "destSelected":true, + "distance":200, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":13, + "nexthops":[ + { + "flags":5, + "ip":"192.168.4.2", + "afi":"ipv4", + "active":true, + "recursive":true + }, + { + "flags":3, + "fib":true, + "ip":"192.168.2.3", + "afi":"ipv4", + "interfaceName":"tor1-eth1", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_rr_ibgp/tor1/staticd.conf b/tests/topotests/bgp_rr_ibgp/tor1/staticd.conf new file mode 100644 index 0000000000..bb8d510b01 --- /dev/null +++ b/tests/topotests/bgp_rr_ibgp/tor1/staticd.conf @@ -0,0 +1 @@ +hostname tor1 diff --git a/tests/topotests/bgp_rr_ibgp/tor1/zebra.conf b/tests/topotests/bgp_rr_ibgp/tor1/zebra.conf new file mode 100644 index 0000000000..25b4fcfd0f --- /dev/null +++ b/tests/topotests/bgp_rr_ibgp/tor1/zebra.conf @@ -0,0 +1,12 @@ +hostname tor1 +ip forwarding +ipv6 forwarding + +int tor1-eth0 + ip addr 192.168.1.1/24 + +int tor1-eth1 + ip addr 192.168.2.1/24 + +int lo + ip addr 192.168.5.1/32 diff --git a/tests/topotests/bgp_rr_ibgp/tor2/bgpd.conf b/tests/topotests/bgp_rr_ibgp/tor2/bgpd.conf new file mode 100644 index 0000000000..99c34158b9 --- /dev/null +++ b/tests/topotests/bgp_rr_ibgp/tor2/bgpd.conf @@ -0,0 +1,5 @@ +hostname tor2 +router bgp 99 + no bgp ebgp-requires-policy + neighbor 192.168.4.3 remote-as internal + redistribute connected diff --git a/tests/topotests/bgp_rr_ibgp/tor2/show_ip_route.json_ref b/tests/topotests/bgp_rr_ibgp/tor2/show_ip_route.json_ref new file mode 100644 index 0000000000..d9e9290e61 --- /dev/null +++ b/tests/topotests/bgp_rr_ibgp/tor2/show_ip_route.json_ref @@ -0,0 +1,157 @@ +{ + "192.168.1.0\/24":[ + { + "prefix":"192.168.1.0\/24", + "protocol":"bgp", + "selected":true, + "destSelected":true, + "distance":200, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":13, + "nexthops":[ + { + "flags":5, + "ip":"192.168.2.1", + "afi":"ipv4", + "active":true, + "recursive":true + }, + { + "flags":3, + "fib":true, + "ip":"192.168.4.3", + "afi":"ipv4", + "interfaceName":"tor2-eth1", + "active":true + } + ] + } + ], + "192.168.2.0\/24":[ + { + "prefix":"192.168.2.0\/24", + "protocol":"bgp", + "selected":true, + "destSelected":true, + "distance":200, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":13, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.4.3", + "afi":"ipv4", + "interfaceName":"tor2-eth1", + "active":true + } + ] + } + ], + "192.168.3.0\/24":[ + { + "prefix":"192.168.3.0\/24", + "protocol":"connected", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":8, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"tor2-eth0", + "active":true + } + ] + } + ], + "192.168.4.0\/24":[ + { + "prefix":"192.168.4.0\/24", + "protocol":"connected", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":8, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"tor2-eth1", + "active":true + } + ] + } + ], + "192.168.5.1\/32":[ + { + "prefix":"192.168.5.1\/32", + "protocol":"bgp", + "selected":true, + "destSelected":true, + "distance":200, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":13, + "nexthops":[ + { + "flags":5, + "ip":"192.168.2.1", + "afi":"ipv4", + "active":true, + "recursive":true + }, + { + "flags":3, + "fib":true, + "ip":"192.168.4.3", + "afi":"ipv4", + "interfaceName":"tor2-eth1", + "active":true + } + ] + } + ], + "192.168.6.2\/32":[ + { + "prefix":"192.168.6.2\/32", + "protocol":"connected", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":8, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"lo", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_rr_ibgp/tor2/staticd.conf b/tests/topotests/bgp_rr_ibgp/tor2/staticd.conf new file mode 100644 index 0000000000..03098e75d9 --- /dev/null +++ b/tests/topotests/bgp_rr_ibgp/tor2/staticd.conf @@ -0,0 +1 @@ +hostname tor2 diff --git a/tests/topotests/bgp_rr_ibgp/tor2/zebra.conf b/tests/topotests/bgp_rr_ibgp/tor2/zebra.conf new file mode 100644 index 0000000000..e1a06b14fc --- /dev/null +++ b/tests/topotests/bgp_rr_ibgp/tor2/zebra.conf @@ -0,0 +1,13 @@ +hostname tor2 +ip forwarding +ipv6 forwarding + +int tor2-eth0 + ip addr 192.168.3.2/24 + +int tor2-eth1 + ip addr 192.168.4.2/24 + + +int lo + ip addr 192.168.6.2/32 diff --git a/tests/topotests/bgp_sender-as-path-loop-detection/__init__.py b/tests/topotests/bgp_sender-as-path-loop-detection/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bgp_sender-as-path-loop-detection/r1/bgpd.conf b/tests/topotests/bgp_sender-as-path-loop-detection/r1/bgpd.conf new file mode 100644 index 0000000000..b16e94d7c1 --- /dev/null +++ b/tests/topotests/bgp_sender-as-path-loop-detection/r1/bgpd.conf @@ -0,0 +1,13 @@ +! exit1 +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as 65002 + address-family ipv4 unicast + neighbor 192.168.255.1 route-map prepend out + redistribute connected + exit-address-family + ! +! +route-map prepend permit 10 + set as-path prepend 65003 +! diff --git a/tests/topotests/bgp_sender-as-path-loop-detection/r1/zebra.conf b/tests/topotests/bgp_sender-as-path-loop-detection/r1/zebra.conf new file mode 100644 index 0000000000..9904bb4e16 --- /dev/null +++ b/tests/topotests/bgp_sender-as-path-loop-detection/r1/zebra.conf @@ -0,0 +1,9 @@ +! exit1 +interface lo + ip address 172.16.255.254/32 +! +interface r1-eth0 + ip address 192.168.255.2/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_sender-as-path-loop-detection/r2/bgpd.conf b/tests/topotests/bgp_sender-as-path-loop-detection/r2/bgpd.conf new file mode 100644 index 0000000000..674877edd3 --- /dev/null +++ b/tests/topotests/bgp_sender-as-path-loop-detection/r2/bgpd.conf @@ -0,0 +1,9 @@ +! spine +router bgp 65002 + no bgp ebgp-requires-policy + neighbor 192.168.255.2 remote-as 65001 + neighbor 192.168.255.2 solo + neighbor 192.168.254.2 remote-as 65003 + neighbor 192.168.254.2 solo + neighbor 192.168.254.2 sender-as-path-loop-detection +! diff --git a/tests/topotests/bgp_sender-as-path-loop-detection/r2/zebra.conf b/tests/topotests/bgp_sender-as-path-loop-detection/r2/zebra.conf new file mode 100644 index 0000000000..f0d357c5ff --- /dev/null +++ b/tests/topotests/bgp_sender-as-path-loop-detection/r2/zebra.conf @@ -0,0 +1,9 @@ +! spine +interface r2-eth0 + ip address 192.168.255.1/30 +! +interface r2-eth1 + ip address 192.168.254.1/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_sender-as-path-loop-detection/r3/bgpd.conf b/tests/topotests/bgp_sender-as-path-loop-detection/r3/bgpd.conf new file mode 100644 index 0000000000..4ee7a39ab2 --- /dev/null +++ b/tests/topotests/bgp_sender-as-path-loop-detection/r3/bgpd.conf @@ -0,0 +1,5 @@ +! exit2 +router bgp 65003 + no bgp ebgp-requires-policy + neighbor 192.168.254.1 remote-as 65002 +! diff --git a/tests/topotests/bgp_sender-as-path-loop-detection/r3/zebra.conf b/tests/topotests/bgp_sender-as-path-loop-detection/r3/zebra.conf new file mode 100644 index 0000000000..a10fe3a3c7 --- /dev/null +++ b/tests/topotests/bgp_sender-as-path-loop-detection/r3/zebra.conf @@ -0,0 +1,6 @@ +! exit2 +interface r3-eth0 + ip address 192.168.254.2/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_sender-as-path-loop-detection/test_bgp_sender-as-path-loop-detection.py b/tests/topotests/bgp_sender-as-path-loop-detection/test_bgp_sender-as-path-loop-detection.py new file mode 100644 index 0000000000..56a98c1ef8 --- /dev/null +++ b/tests/topotests/bgp_sender-as-path-loop-detection/test_bgp_sender-as-path-loop-detection.py @@ -0,0 +1,146 @@ +#!/usr/bin/env python + +# +# test_bgp_sender-as-path-loop-detection.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2019 by +# Donatas Abraitis +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +Test if neighbor sender-as-path-loop-detection +command works as expeced. +""" + +import os +import sys +import json +import time +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from mininet.topo import Topo + + +class TemplateTopo(Topo): + def build(self, *_args, **_opts): + tgen = get_topogen(self) + + for routern in range(1, 4): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + +def setup_module(mod): + tgen = Topogen(TemplateTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.iteritems(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_sender_as_path_loop_detection(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r2"] + + def _bgp_converge(router): + output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.2 json")) + expected = { + "192.168.255.2": { + "bgpState": "Established", + "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 2}}, + } + } + return topotest.json_cmp(output, expected) + + def _bgp_has_route_from_r1(router): + output = json.loads(router.vtysh_cmd("show ip bgp 172.16.255.254/32 json")) + expected = { + "paths": [ + { + "aspath": { + "segments": [{"type": "as-sequence", "list": [65001, 65003]}], + "length": 2, + } + } + ] + } + return topotest.json_cmp(output, expected) + + def _bgp_suppress_route_to_r3(router): + output = json.loads( + router.vtysh_cmd( + "show ip bgp neighbor 192.168.254.2 advertised-routes json" + ) + ) + expected = {"totalPrefixCounter": 0} + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge, router) + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + + assert result is None, 'Failed bgp convergence in "{}"'.format(router) + + test_func = functools.partial(_bgp_has_route_from_r1, router) + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + + assert result is None, 'Failed to see a route from r1 in "{}"'.format(router) + + test_func = functools.partial(_bgp_suppress_route_to_r3, router) + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + + assert ( + result is None + ), 'Route 172.16.255.254/32 should not be sent to r3 "{}"'.format(router) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_set_local-preference_add_subtract/__init__.py b/tests/topotests/bgp_set_local-preference_add_subtract/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bgp_set_local-preference_add_subtract/r1/bgpd.conf b/tests/topotests/bgp_set_local-preference_add_subtract/r1/bgpd.conf new file mode 100644 index 0000000000..7dab52fef0 --- /dev/null +++ b/tests/topotests/bgp_set_local-preference_add_subtract/r1/bgpd.conf @@ -0,0 +1,6 @@ +router bgp 65000 + no bgp ebgp-requires-policy + neighbor 192.168.255.2 remote-as 65000 + neighbor 192.168.255.3 remote-as 65000 + exit-address-family +! diff --git a/tests/topotests/bgp_set_local-preference_add_subtract/r1/zebra.conf b/tests/topotests/bgp_set_local-preference_add_subtract/r1/zebra.conf new file mode 100644 index 0000000000..6e9b0b4a7e --- /dev/null +++ b/tests/topotests/bgp_set_local-preference_add_subtract/r1/zebra.conf @@ -0,0 +1,6 @@ +! +interface r1-eth0 + ip address 192.168.255.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_set_local-preference_add_subtract/r2/bgpd.conf b/tests/topotests/bgp_set_local-preference_add_subtract/r2/bgpd.conf new file mode 100644 index 0000000000..a8a0384632 --- /dev/null +++ b/tests/topotests/bgp_set_local-preference_add_subtract/r2/bgpd.conf @@ -0,0 +1,10 @@ +router bgp 65000 + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as 65000 + address-family ipv4 + redistribute connected + neighbor 192.168.255.1 route-map r1-out out + exit-address-family +! +route-map r1-out permit 10 + set local-preference +50 diff --git a/tests/topotests/bgp_set_local-preference_add_subtract/r2/zebra.conf b/tests/topotests/bgp_set_local-preference_add_subtract/r2/zebra.conf new file mode 100644 index 0000000000..93e3590448 --- /dev/null +++ b/tests/topotests/bgp_set_local-preference_add_subtract/r2/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 172.16.255.254/32 +! +interface r2-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_set_local-preference_add_subtract/r3/bgpd.conf b/tests/topotests/bgp_set_local-preference_add_subtract/r3/bgpd.conf new file mode 100644 index 0000000000..2f5dceede2 --- /dev/null +++ b/tests/topotests/bgp_set_local-preference_add_subtract/r3/bgpd.conf @@ -0,0 +1,10 @@ +router bgp 65000 + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as 65000 + address-family ipv4 + redistribute connected + neighbor 192.168.255.1 route-map r1-out out + exit-address-family +! +route-map r1-out permit 10 + set local-preference -50 diff --git a/tests/topotests/bgp_set_local-preference_add_subtract/r3/zebra.conf b/tests/topotests/bgp_set_local-preference_add_subtract/r3/zebra.conf new file mode 100644 index 0000000000..b5e060c3d6 --- /dev/null +++ b/tests/topotests/bgp_set_local-preference_add_subtract/r3/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 172.16.255.254/32 +! +interface r3-eth0 + ip address 192.168.255.3/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_set_local-preference_add_subtract/test_bgp_set_local-preference_add_subtract.py b/tests/topotests/bgp_set_local-preference_add_subtract/test_bgp_set_local-preference_add_subtract.py new file mode 100644 index 0000000000..ce3165db25 --- /dev/null +++ b/tests/topotests/bgp_set_local-preference_add_subtract/test_bgp_set_local-preference_add_subtract.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python + +# +# bgp_set_local-preference_add_subtract.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2020 by +# Donatas Abraitis +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +bgp_set_local-preference_add_subtract.py: +Test if we can add/subtract the value to/from an existing +LOCAL_PREF in route-maps. +""" + +import os +import sys +import json +import time +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from mininet.topo import Topo + + +class TemplateTopo(Topo): + def build(self, *_args, **_opts): + tgen = get_topogen(self) + + for routern in range(1, 4): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + +def setup_module(mod): + tgen = Topogen(TemplateTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.iteritems(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_set_local_preference(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r1"] + + def _bgp_converge(router): + output = json.loads(router.vtysh_cmd("show ip bgp neighbor json")) + expected = { + "192.168.255.2": { + "bgpState": "Established", + "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 2}}, + }, + "192.168.255.3": { + "bgpState": "Established", + "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 2}}, + }, + } + return topotest.json_cmp(output, expected) + + def _bgp_check_local_preference(router): + output = json.loads(router.vtysh_cmd("show ip bgp 172.16.255.254/32 json")) + expected = { + "paths": [ + {"locPrf": 50, "nexthops": [{"ip": "192.168.255.3"}]}, + {"locPrf": 150, "nexthops": [{"ip": "192.168.255.2"}]}, + ] + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge, router) + success, result = topotest.run_and_expect(test_func, None, count=15, wait=0.5) + + assert result is None, 'Failed to see BGP convergence in "{}"'.format(router) + + test_func = functools.partial(_bgp_check_local_preference, router) + success, result = topotest.run_and_expect(test_func, None, count=15, wait=0.5) + + assert result is None, 'Failed to see applied BGP local-preference in "{}"'.format( + router + ) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_update_delay/__init__.py b/tests/topotests/bgp_update_delay/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bgp_update_delay/r1/bgpd.conf b/tests/topotests/bgp_update_delay/r1/bgpd.conf new file mode 100644 index 0000000000..8ebb509d6d --- /dev/null +++ b/tests/topotests/bgp_update_delay/r1/bgpd.conf @@ -0,0 +1,10 @@ +! exit1 +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as 65002 + neighbor 192.168.255.1 timers connect 10 + address-family ipv4 unicast + redistribute connected + exit-address-family + ! +! diff --git a/tests/topotests/bgp_update_delay/r1/zebra.conf b/tests/topotests/bgp_update_delay/r1/zebra.conf new file mode 100644 index 0000000000..9904bb4e16 --- /dev/null +++ b/tests/topotests/bgp_update_delay/r1/zebra.conf @@ -0,0 +1,9 @@ +! exit1 +interface lo + ip address 172.16.255.254/32 +! +interface r1-eth0 + ip address 192.168.255.2/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_update_delay/r2/bgpd.conf b/tests/topotests/bgp_update_delay/r2/bgpd.conf new file mode 100644 index 0000000000..438f9950a5 --- /dev/null +++ b/tests/topotests/bgp_update_delay/r2/bgpd.conf @@ -0,0 +1,18 @@ +! spine +router bgp 65002 + no bgp ebgp-requires-policy + timers bgp 3 9 + neighbor 192.168.255.2 remote-as 65001 + neighbor 192.168.254.2 remote-as 65003 + neighbor 192.168.253.2 remote-as 65004 + neighbor 192.168.255.2 timers connect 10 + neighbor 192.168.254.2 timers connect 10 + neighbor 192.168.253.2 timers connect 10 +! + router bgp 65002 vrf vrf1 + no bgp ebgp-requires-policy + timers bgp 3 9 + neighbor 192.168.252.2 remote-as 65005 + neighbor 192.168.252.2 timers connect 10 + ! +! diff --git a/tests/topotests/bgp_update_delay/r2/zebra.conf b/tests/topotests/bgp_update_delay/r2/zebra.conf new file mode 100644 index 0000000000..420f00d974 --- /dev/null +++ b/tests/topotests/bgp_update_delay/r2/zebra.conf @@ -0,0 +1,20 @@ +! spine +interface r2-eth0 + ip address 192.168.255.1/30 +! +interface r2-eth1 + ip address 192.168.254.1/30 +! +interface r2-eth2 + ip address 192.168.253.1/30 +! +interface r2-eth3 + ip address 192.168.252.1/30 + vrf vrf1 +! +auto vrf1 +iface vrf1 + vrf-table auto +! +ip forwarding +! diff --git a/tests/topotests/bgp_update_delay/r3/bgpd.conf b/tests/topotests/bgp_update_delay/r3/bgpd.conf new file mode 100644 index 0000000000..53e51788f7 --- /dev/null +++ b/tests/topotests/bgp_update_delay/r3/bgpd.conf @@ -0,0 +1,10 @@ +! exit2 +router bgp 65003 + no bgp ebgp-requires-policy + timers bgp 3 9 + neighbor 192.168.254.1 remote-as 65002 + neighbor 192.168.254.1 timers connect 10 + address-family ipv4 unicast + redistribute connected + ! +! diff --git a/tests/topotests/bgp_update_delay/r3/zebra.conf b/tests/topotests/bgp_update_delay/r3/zebra.conf new file mode 100644 index 0000000000..f490d97afe --- /dev/null +++ b/tests/topotests/bgp_update_delay/r3/zebra.conf @@ -0,0 +1,9 @@ +! exit2 +interface lo + ip address 172.16.254.254/32 +! +interface r3-eth0 + ip address 192.168.254.2/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_update_delay/r4/bgpd.conf b/tests/topotests/bgp_update_delay/r4/bgpd.conf new file mode 100644 index 0000000000..34cb429c0a --- /dev/null +++ b/tests/topotests/bgp_update_delay/r4/bgpd.conf @@ -0,0 +1,11 @@ +! exit2 +router bgp 65004 + no bgp ebgp-requires-policy + timers bgp 3 9 + neighbor 192.168.253.1 remote-as 65002 + neighbor 192.168.253.1 timers connect 10 + address-family ipv4 unicast + redistribute connected + exit-address-family + ! +! diff --git a/tests/topotests/bgp_update_delay/r4/zebra.conf b/tests/topotests/bgp_update_delay/r4/zebra.conf new file mode 100644 index 0000000000..baba04c160 --- /dev/null +++ b/tests/topotests/bgp_update_delay/r4/zebra.conf @@ -0,0 +1,9 @@ +! exit2 +interface lo + ip address 172.16.253.254/32 +! +interface r4-eth0 + ip address 192.168.253.2/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_update_delay/r5/bgpd.conf b/tests/topotests/bgp_update_delay/r5/bgpd.conf new file mode 100644 index 0000000000..66ecc703cd --- /dev/null +++ b/tests/topotests/bgp_update_delay/r5/bgpd.conf @@ -0,0 +1,11 @@ +! exit1 +router bgp 65005 + no bgp ebgp-requires-policy + timers bgp 3 9 + neighbor 192.168.252.1 remote-as 65002 + neighbor 192.168.252.1 timers connect 10 + address-family ipv4 unicast + redistribute connected + exit-address-family + ! +! diff --git a/tests/topotests/bgp_update_delay/r5/zebra.conf b/tests/topotests/bgp_update_delay/r5/zebra.conf new file mode 100644 index 0000000000..8adf6f89e0 --- /dev/null +++ b/tests/topotests/bgp_update_delay/r5/zebra.conf @@ -0,0 +1,9 @@ +! exit1 +interface lo + ip address 172.16.252.254/32 +! +interface r1-eth0 + ip address 192.168.252.2/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_update_delay/test_bgp_update_delay.py b/tests/topotests/bgp_update_delay/test_bgp_update_delay.py new file mode 100755 index 0000000000..9d2818b965 --- /dev/null +++ b/tests/topotests/bgp_update_delay/test_bgp_update_delay.py @@ -0,0 +1,295 @@ +#!/usr/bin/env python + +# +# test_bgp_update_delay.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2019 by +# Don Slice +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +Test the ability to define update-delay to delay bestpath, rib install +and advertisement to peers when frr is started, restarted or "clear ip +bgp *" is performed. Test both the vrf-specific and global configuration +and operation. + +r1 +| +r2----r3 +| \ +| \ +r5 r4 + + +r2 is UUT and peers with r1, r3, and r4 in default bgp instance. +r2 peers with r5 in vrf vrf1. + +Check r2 initial convergence in default table +Define update-delay with max-delay in the default bgp instance on r2 +Shutdown peering on r1 toward r2 so that delay timers can be exercised +Clear bgp neighbors on r2 and then check for the 'in progress' indicator +Check that r2 only installs route learned from r4 after the max-delay timer expires +Define update-delay with max-delay and estabish-wait and check json output showing set +Clear neighbors on r2 and check that r3 installs route from r4 after establish-wait time +Remove update-delay timer on r2 to verify that it goes back to normal behavior +Clear neighbors on r2 and check that route install time on r2 does not delay +Define global bgp update-delay with max-delay and establish-wait on r2 +Check that r2 default instance and vrf1 have the max-delay and establish set +Clear neighbors on r2 and check route-install time is after the establish-wait timer + +Note that the keepalive/hold times were changed to 3/9 and the connect retry timer +to 10 to improve the odds the convergence timing in this test case is useful in the +event of packet loss. +""" + +import os +import sys +import json +import time +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from mininet.topo import Topo + + +class TemplateTopo(Topo): + def build(self, *_args, **_opts): + tgen = get_topogen(self) + + for routern in range(1, 6): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r4"]) + + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r5"]) + + +def setup_module(mod): + tgen = Topogen(TemplateTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.iteritems(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_update_delay(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + + # initial convergence without update-delay defined + def _bgp_converge(router): + output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.2 json")) + expected = { + "192.168.255.2": { + "bgpState": "Established", + "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 2}}, + } + } + return topotest.json_cmp(output, expected) + + def _bgp_check_update_delay(router): + output = json.loads(router.vtysh_cmd("show ip bgp sum json")) + expected = {"ipv4Unicast": {"updateDelayLimit": 20}} + + return topotest.json_cmp(output, expected) + + def _bgp_check_update_delay_in_progress(router): + output = json.loads(router.vtysh_cmd("show ip bgp sum json")) + expected = {"ipv4Unicast": {"updateDelayInProgress":True}} + + return topotest.json_cmp(output, expected) + + def _bgp_check_route_install(router): + output = json.loads(router.vtysh_cmd("show ip route 172.16.253.254/32 json")) + expected = {"172.16.253.254/32": [ {"protocol": "bgp"}]} + + return topotest.json_cmp(output, expected) + + def _bgp_check_update_delay_and_wait(router): + output = json.loads(router.vtysh_cmd("show ip bgp sum json")) + expected = { + "ipv4Unicast": { + "updateDelayLimit": 20, + "updateDelayEstablishWait": 10}} + + return topotest.json_cmp(output, expected) + + def _bgp_check_update_delay(router): + output = json.loads(router.vtysh_cmd("show ip bgp sum json")) + expected = {"ipv4Unicast": {"updateDelayLimit": 20}} + + return topotest.json_cmp(output, expected) + + def _bgp_check_vrf_update_delay_and_wait(router): + output = json.loads(router.vtysh_cmd("show ip bgp vrf vrf1 sum json")) + expected = { + "ipv4Unicast": { + "updateDelayLimit": 20, + "updateDelayEstablishWait": 10}} + + + return topotest.json_cmp(output, expected) + + + # Check r2 initial convergence in default table + test_func = functools.partial(_bgp_converge, router2) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + + assert result is None, 'Failed bgp convergence in "{}"'.format(router2) + + # Define update-delay with max-delay in the default bgp instance on r2 + router2.vtysh_cmd( + """ + configure terminal + router bgp 65002 + update-delay 20 + """ + ) + + # Shutdown peering on r1 toward r2 so that delay timers can be exercised + router1.vtysh_cmd( + """ + configure terminal + router bgp 65001 + neighbor 192.168.255.1 shut + """ + ) + + # Clear bgp neighbors on r2 and then check for the 'in progress' indicator + router2.vtysh_cmd("""clear ip bgp *""") + + test_func = functools.partial(_bgp_check_update_delay_in_progress, router2) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + + assert result is None, 'Failed to set update-delay max-delay timer "{}"'.format(router2) + + # Check that r2 only installs route learned from r4 after the max-delay timer expires + test_func = functools.partial(_bgp_check_route_install, router2) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + + assert result is None, 'Failed to install route after update-delay "{}"'.format(router2) + + # Define update-delay with max-delay and estabish-wait and check json output showing set + router2.vtysh_cmd( + """ + configure terminal + router bgp 65002 + update-delay 20 10 + """ + ) + + test_func = functools.partial(_bgp_check_update_delay_and_wait, router2) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + + assert result is None, 'Failed to set max-delay and establish-weight timers in "{}"'.format(router2) + + # Define update-delay with max-delay and estabish-wait and check json output showing set + router2.vtysh_cmd("""clear ip bgp *""") + + test_func = functools.partial(_bgp_check_route_install, router3) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + + assert result is None, 'Failed to installed advertised route after establish-wait timer espired "{}"'.format(router2) + + # Remove update-delay timer on r2 to verify that it goes back to normal behavior + router2.vtysh_cmd( + """ + configure terminal + router bgp 65002 + no update-delay + """ + ) + + # Clear neighbors on r2 and check that route install time on r2 does not delay + router2.vtysh_cmd("""clear ip bgp *""") + + test_func = functools.partial(_bgp_check_route_install, router2) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + + assert result is None, 'Failed to remove update-delay delay timing "{}"'.format(router2) + + # Define global bgp update-delay with max-delay and establish-wait on r2 + router2.vtysh_cmd( + """ + configure terminal + bgp update-delay 20 10 + """ + ) + + # Check that r2 default instance and vrf1 have the max-delay and establish set + test_func = functools.partial(_bgp_check_update_delay_and_wait, router2) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + + assert result is None, 'Failed to set update-delay in default instance "{}"'.format(router2) + + test_func = functools.partial(_bgp_check_vrf_update_delay_and_wait, router2) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + + assert result is None, 'Failed to set update-delay in vrf1 "{}"'.format(router2) + + # Clear neighbors on r2 and check route-install time is after the establish-wait timer + router2.vtysh_cmd("""clear ip bgp *""") + + test_func = functools.partial(_bgp_check_route_install, router3) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + + assert result is None, 'Failed to installed advertised route after establish-wait timer espired "{}"'.format(router2) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_vrf_dynamic_route_leak/bgp_vrf_dynamic_route_leak_topo1.json b/tests/topotests/bgp_vrf_dynamic_route_leak/bgp_vrf_dynamic_route_leak_topo1.json new file mode 100644 index 0000000000..b1d7d09db8 --- /dev/null +++ b/tests/topotests/bgp_vrf_dynamic_route_leak/bgp_vrf_dynamic_route_leak_topo1.json @@ -0,0 +1,563 @@ +{ + "address_types": ["ipv4","ipv6"], + "ipv4base": "10.0.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 30, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:db8:f::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "r2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "ISR"}, + "r3-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r4-link1": {"ipv4": "auto", "ipv6": "auto"} + }, + "vrfs":[ + { + "name": "ISR", + "id": "1" + } + ], + "bgp": + [ + { + "local_as": "100", + "vrf": "ISR", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "next_hop_self": true + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "next_hop_self": true, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + } + ], + "static_routes":[ + { + "network": ["11.11.11.1/32", "11.11.11.11/32"], + "next_hop":"Null0", + "vrf": "ISR" + }, + { + "network": ["11:11::1/128", "11:11::11/128"], + "next_hop":"Null0", + "vrf": "ISR" + }, + { + "network": ["10.10.10.10/32", "10.10.10.100/32"], + "next_hop":"Null0" + }, + { + "network": ["10:10::10/128", "10:10::100/128"], + "next_hop":"Null0" + } + ], + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + }, + "r2": { + "links": { + "r1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "ISR"}, + "r3-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r4-link1": {"ipv4": "auto", "ipv6": "auto"} + }, + "vrfs":[ + { + "name": "ISR", + "id": "1" + } + ], + "bgp": + [ + { + "local_as": "100", + "vrf": "ISR", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "next_hop_self": true + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "next_hop_self": true, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + } + ], + "static_routes":[ + { + "network": ["22.22.22.2/32", "22.22.22.22/32"], + "next_hop":"Null0", + "vrf": "ISR" + }, + { + "network": ["22:22::2/128", "22:22::22/128"], + "next_hop":"Null0", + "vrf": "ISR" + }, + { + "network": ["20.20.20.20/32", "20.20.20.200/32"], + "next_hop":"Null0" + }, + { + "network": ["20:20::20/128", "20:20::200/128"], + "next_hop":"Null0" + } + ], + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + }, + "r3": { + "links": { + "r1-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r2-link1": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": + [ + { + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + } + ], + "static_routes":[ + { + "network": ["30.30.30.3/32", "30.30.30.30/32"], + "next_hop":"Null0" + }, + { + "network": ["30:30::3/128", "30:30::30/128"], + "next_hop":"Null0" + }, + { + "network": ["50.50.50.5/32", "50.50.50.50/32"], + "next_hop":"Null0" + }, + { + "network": ["50:50::5/128", "50:50::50/128"], + "next_hop":"Null0" + } + ], + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + }, + "r4": { + "links": { + "r1-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r2-link1": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": + [ + { + "local_as": "400", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r4-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r4-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "400", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + } + ], + "static_routes":[ + { + "network": ["40.40.40.4/32", "40.40.40.40/32"], + "next_hop":"Null0" + }, + { + "network": ["40:40::4/128", "40:40::40/128"], + "next_hop":"Null0" + }, + { + "network": ["50.50.50.5/32", "50.50.50.50/32"], + "next_hop":"Null0" + }, + { + "network": ["50:50::5/128", "50:50::50/128"], + "next_hop":"Null0" + } + ], + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + } + } +} diff --git a/tests/topotests/bgp_vrf_dynamic_route_leak/bgp_vrf_dynamic_route_leak_topo2.json b/tests/topotests/bgp_vrf_dynamic_route_leak/bgp_vrf_dynamic_route_leak_topo2.json new file mode 100644 index 0000000000..b1d7d09db8 --- /dev/null +++ b/tests/topotests/bgp_vrf_dynamic_route_leak/bgp_vrf_dynamic_route_leak_topo2.json @@ -0,0 +1,563 @@ +{ + "address_types": ["ipv4","ipv6"], + "ipv4base": "10.0.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 30, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:db8:f::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "r2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "ISR"}, + "r3-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r4-link1": {"ipv4": "auto", "ipv6": "auto"} + }, + "vrfs":[ + { + "name": "ISR", + "id": "1" + } + ], + "bgp": + [ + { + "local_as": "100", + "vrf": "ISR", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "next_hop_self": true + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "next_hop_self": true, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + } + ], + "static_routes":[ + { + "network": ["11.11.11.1/32", "11.11.11.11/32"], + "next_hop":"Null0", + "vrf": "ISR" + }, + { + "network": ["11:11::1/128", "11:11::11/128"], + "next_hop":"Null0", + "vrf": "ISR" + }, + { + "network": ["10.10.10.10/32", "10.10.10.100/32"], + "next_hop":"Null0" + }, + { + "network": ["10:10::10/128", "10:10::100/128"], + "next_hop":"Null0" + } + ], + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + }, + "r2": { + "links": { + "r1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "ISR"}, + "r3-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r4-link1": {"ipv4": "auto", "ipv6": "auto"} + }, + "vrfs":[ + { + "name": "ISR", + "id": "1" + } + ], + "bgp": + [ + { + "local_as": "100", + "vrf": "ISR", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "next_hop_self": true + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "next_hop_self": true, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + } + ], + "static_routes":[ + { + "network": ["22.22.22.2/32", "22.22.22.22/32"], + "next_hop":"Null0", + "vrf": "ISR" + }, + { + "network": ["22:22::2/128", "22:22::22/128"], + "next_hop":"Null0", + "vrf": "ISR" + }, + { + "network": ["20.20.20.20/32", "20.20.20.200/32"], + "next_hop":"Null0" + }, + { + "network": ["20:20::20/128", "20:20::200/128"], + "next_hop":"Null0" + } + ], + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + }, + "r3": { + "links": { + "r1-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r2-link1": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": + [ + { + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + } + ], + "static_routes":[ + { + "network": ["30.30.30.3/32", "30.30.30.30/32"], + "next_hop":"Null0" + }, + { + "network": ["30:30::3/128", "30:30::30/128"], + "next_hop":"Null0" + }, + { + "network": ["50.50.50.5/32", "50.50.50.50/32"], + "next_hop":"Null0" + }, + { + "network": ["50:50::5/128", "50:50::50/128"], + "next_hop":"Null0" + } + ], + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + }, + "r4": { + "links": { + "r1-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r2-link1": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": + [ + { + "local_as": "400", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r4-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r4-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "400", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + } + ], + "static_routes":[ + { + "network": ["40.40.40.4/32", "40.40.40.40/32"], + "next_hop":"Null0" + }, + { + "network": ["40:40::4/128", "40:40::40/128"], + "next_hop":"Null0" + }, + { + "network": ["50.50.50.5/32", "50.50.50.50/32"], + "next_hop":"Null0" + }, + { + "network": ["50:50::5/128", "50:50::50/128"], + "next_hop":"Null0" + } + ], + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + } + } +} 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 new file mode 100755 index 0000000000..1947548b3e --- /dev/null +++ b/tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo1.py @@ -0,0 +1,1848 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +Following tests are covered to test BGP Multi-VRF Dynamic Route Leaking: + +1. Verify that dynamically imported routes are further advertised + to iBGP peers(peer in cluster). +2. Verify matching a prefix based on community attribute and + importing it by stripping off this value +3. Verify the route-map operation along with dynamic import command. +4. Verifying the JSON outputs for all supported commands +""" + +import os +import sys +import json +import time +import pytest +import platform + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, '../')) +sys.path.append(os.path.join(CWD, '../lib/')) + +# Required to instantiate the topology builder class. + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from lib.topotest import version_cmp +from mininet.topo import Topo + +from lib.common_config import ( + start_topology, write_test_header, check_address_types, + write_test_footer, reset_config_on_routers, + verify_rib, step, create_route_maps, + shutdown_bringup_interface, create_static_routes, + create_prefix_lists, create_bgp_community_lists, + create_interface_in_kernel, + check_router_status, verify_cli_json, + get_frr_ipv6_linklocal, verify_fib_routes +) + +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, create_router_bgp, + clear_bgp, verify_bgp_community, verify_bgp_rib +) +from lib.topojson import build_topo_from_json, build_config_from_json + +# Reading the data from JSON File for topology creation +jsonFile = "{}/bgp_vrf_dynamic_route_leak_topo1.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +# Global variables +NETWORK1_1 = {"ipv4": "11.11.11.1/32", "ipv6": "11:11::1/128"} +NETWORK1_2 = {"ipv4": "11.11.11.11/32", "ipv6": "11:11::11/128"} +NETWORK1_3 = {"ipv4": "10.10.10.10/32", "ipv6": "10:10::10/128"} +NETWORK1_4 = {"ipv4": "10.10.10.100/32", "ipv6": "10:10::100/128"} + +NETWORK2_1 = {"ipv4": "22.22.22.2/32", "ipv6": "22:22::2/128"} +NETWORK2_2 = {"ipv4": "22.22.22.22/32", "ipv6": "22:22::22/128"} +NETWORK2_3 = {"ipv4": "20.20.20.20/32", "ipv6": "20:20::20/128"} +NETWORK2_4 = {"ipv4": "20.20.20.200/32", "ipv6": "20:20::200/128"} + +NETWORK3_1 = {"ipv4": "30.30.30.3/32", "ipv6": "30:30::3/128"} +NETWORK3_2 = {"ipv4": "30.30.30.30/32", "ipv6": "30:30::30/128"} +NETWORK3_3 = {"ipv4": "50.50.50.5/32", "ipv6": "50:50::5/128"} +NETWORK3_4 = {"ipv4": "50.50.50.50/32", "ipv6": "50:50::50/128"} + +NETWORK4_1 = {"ipv4": "40.40.40.4/32", "ipv6": "40:40::4/128"} +NETWORK4_2 = {"ipv4": "40.40.40.40/32", "ipv6": "40:40::40/128"} +NETWORK4_3 = {"ipv4": "50.50.50.5/32", "ipv6": "50:50::5/128"} +NETWORK4_4 = {"ipv4": "50.50.50.50/32", "ipv6": "50:50::50/128"} + +NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"} +LOOPBACK_1 = {"ipv4": "10.0.0.7/24", "ipv6": "fd00:0:0:1::7/64", + "ipv4_mask": "255.255.255.0", "ipv6_mask": None} +LOOPBACK_2 = {"ipv4": "10.0.0.16/24", "ipv6": "fd00:0:0:3::5/64", + "ipv4_mask": "255.255.255.0", "ipv6_mask": None} +PREFERRED_NEXT_HOP = "global" + + +class CreateTopo(Topo): + """ + Test BasicTopo - topology 1 + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + """Build function""" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + global topo + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(CreateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen) + + # Run these tests for kernel version 4.19 or above + if version_cmp(platform.release(), '4.19') < 0: + error_msg = ('BGP vrf dynamic route leak tests will not run ' + '(have kernel "{}", but it requires >= 4.19)'.\ + format(platform.release())) + pytest.skip(error_msg) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + global BGP_CONVERGENCE + global ADDR_TYPES + ADDR_TYPES = check_address_types() + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error: {}". \ + format(BGP_CONVERGENCE) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info("Testsuite end time: {}". + format(time.asctime(time.localtime(time.time())))) + logger.info("=" * 40) + +##################################################### +# +# Local APIs +# +##################################################### + +def disable_route_map_to_prefer_global_next_hop(tgen, topo): + """ + This API is to remove prefer global route-map applied on neighbors + + Parameter: + ---------- + * `tgen` : Topogen object + * `topo` : Input JSON data + + Returns: + -------- + True/errormsg + + """ + + logger.info("Remove prefer-global rmap applied on neighbors") + input_dict = { + "r1": { + "bgp": + [ + { + "local_as": "100", + "vrf": "ISR", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in", + "delete": True + }] + } + } + } + } + } + } + } + }, + { + "local_as": "100", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1-link1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in", + "delete": True + }] + } + } + } + } + } + } + } + }, + { + "local_as": "100", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r1-link1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in", + "delete": True + }] + } + } + } + } + } + } + } + } + ] + }, + "r2": { + "bgp": + [ + { + "local_as": "100", + "vrf": "ISR", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in", + "delete": True + }] + } + } + } + } + } + } + } + }, + { + "local_as": "100", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in", + "delete": True + }] + } + } + } + } + } + } + } + }, + { + "local_as": "100", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2-link1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in", + "delete": True + }] + } + } + } + } + } + } + } + } + ] + }, + "r3": { + "bgp": + [ + { + "local_as": "300", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3-link1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in", + "delete": True + }] + } + } + } + } + } + } + } + }, + { + "local_as": "300", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in", + "delete": True + }] + } + } + } + } + } + } + } + } + ] + }, + "r4": { + "bgp": + [ + { + "local_as": "400", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r4-link1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in", + "delete": True + }] + } + } + } + } + } + } + } + }, + { + "local_as": "400", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4-link1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in", + "delete": True + }] + } + } + } + } + } + } + } + } + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}". \ + format(tc_name, result) + + return True + + +##################################################### +# +# Testcases +# +##################################################### + +def test_dynamic_imported_routes_advertised_to_iBGP_peer_p0(request): + """ + TC5_FUNC_5: + 1.5.5. Verify that dynamically imported routes are further advertised + to iBGP peers(peer in cluster). + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + build_config_from_json(tgen, topo) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + for addr_type in ADDR_TYPES: + + step("Redistribute configured static routes into BGP process" + " on R1 and R3/R4") + + input_dict_1={} + DUT = ["r1", "r3", "r4"] + VRFS = ["default", "default", "default"] + AS_NUM = [100, 300, 400] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_1.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "redistribute": [{ + "redist_type": "static" + }] + } + } + } + }) + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error: {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Verify that R1 receives BGP routes from R3 and R4 in " + "vrf default.") + + input_routes_r3 = { + "r3": { + "static_routes": [{ + "network": [ + NETWORK3_1[addr_type], \ + NETWORK3_2[addr_type], \ + NETWORK3_3[addr_type], \ + NETWORK3_4[addr_type] + ] + }] + } + } + + input_routes_r4 = { + "r4": { + "static_routes": [{ + "network": [ + NETWORK4_1[addr_type], \ + NETWORK4_2[addr_type], \ + NETWORK4_3[addr_type], \ + NETWORK4_4[addr_type] + ] + }] + } + } + + DUT = ["r1", "r2"] + INPUT_DICT = [input_routes_r3, input_routes_r4] + + for dut, routes in zip(DUT, INPUT_DICT): + result = verify_bgp_rib(tgen, addr_type, dut, routes) + assert result is True, \ + "Testcase {} : Failed \n Error {}". \ + format(tc_name, result) + + result = verify_fib_routes(tgen, addr_type, dut, routes) + assert result is True, \ + "Testcase {} : Failed \n Error {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Import from default vrf into vrf ISR on R1") + + input_dict_isr={} + DUT = ["r1", "r2"] + VRFS = ["ISR", "ISR"] + AS_NUM = [100, 100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_isr.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "default" + } + } + } + } + }) + + result = create_router_bgp(tgen, topo, input_dict_isr) + assert result is True, "Testcase {} : Failed \n Error: {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Verify that default vrf's imported routes are installed " + "in RIB/FIB of vrf ISR on R1:") + + input_routes_r3 = { + "r3": { + "static_routes": [{ + "network": [ + NETWORK3_1[addr_type], \ + NETWORK3_2[addr_type], \ + NETWORK3_3[addr_type], \ + NETWORK3_4[addr_type] + ], + "vrf": "ISR" + }] + } + } + + input_routes_r4 = { + "r4": { + "static_routes": [{ + "network": [ + NETWORK4_1[addr_type], \ + NETWORK4_2[addr_type], \ + NETWORK4_3[addr_type], \ + NETWORK4_4[addr_type] + ], + "vrf": "ISR" + }] + } + } + + INPUT_DICT_VRF = [input_routes_r3, input_routes_r4] + + for routes in INPUT_DICT_VRF: + result = verify_bgp_rib(tgen, addr_type, "r1", routes) + assert result is True, \ + "Testcase {} : Failed \n Error {}". \ + format(tc_name, result) + + result = verify_fib_routes(tgen, addr_type, "r1", routes) + assert result is True, \ + "Testcase {} : Failed \n Error {}". \ + format(tc_name, result) + + intf_r2_r1 = topo["routers"]["r2"]["links"]["r1-link1"] + for addr_type in ADDR_TYPES: + + step("Create a loopback10 interface on R1 with below IP address and " + "associate with vrf ISR:") + + create_interface_in_kernel(tgen, "r1", "loopback2", + LOOPBACK_2[addr_type], + "ISR", + LOOPBACK_2["{}_mask".\ + format(addr_type)]) + + for addr_type in ADDR_TYPES: + + step("On router R1 Change the next-hop of static routes in vrf " + "ISR to LOOPBACK_1") + + input_routes_r1= { + "r1": { + "static_routes":[ + { + "network": [NETWORK1_3[addr_type], NETWORK1_4[addr_type]], + "next_hop":"Null0", + "delete": True + } + ] + } + } + + result = create_static_routes(tgen, input_routes_r1) + assert result is True, "Testcase {} :Failed \n Error: {}". \ + format(tc_name, result) + + input_routes_r1= { + "r1": { + "static_routes":[ + { + "network": [NETWORK1_3[addr_type], NETWORK1_4[addr_type]], + "next_hop": (intf_r2_r1[addr_type]).split("/")[0] + } + ] + } + } + + result = create_static_routes(tgen, input_routes_r1) + assert result is True, "Testcase {} :Failed \n Error: {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Verify that, though R1 originating BGP routes with next-hop" + " 24.1.1.2/24::1:2, which is local to R2(but in default vrf)" + ", R2 must receives and install all routes from R1 in vrf ISR.") + step("Verify on R2, that it now rejects 10.10.10.x routes originated " + "from R1. As next-hop IP is local to R2's vrf ISR.") + + input_routes_r1= { + "r1": { + "static_routes":[ + { + "network": [NETWORK1_3[addr_type], NETWORK1_4[addr_type]], + "vrf": "ISR" + } + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1, + expected=False) + assert result is not True, ( + "Testcase {} : Failed \n Routes are still present \n Error {}". \ + format(tc_name, result)) + + write_test_footer(tc_name) + + +def test_dynamic_imported_matching_prefix_based_on_community_list_p0(request): + """ + TC7_FUNC_7: + 1.5.7. Verify matching a prefix based on community attribute and + importing it by stripping off this value + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + build_config_from_json(tgen, topo) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + for addr_type in ADDR_TYPES: + + step("Configure route-map to set community attribute for a specific" + "prefix on R1 in vrf ISR") + + input_dict_pf = { + "r1": { + "prefix_lists": { + addr_type: { + "pflist_ABC_{}".format(addr_type): [{ + "seqid": 10, + "network": NETWORK1_1[addr_type], + "action": "permit" + }] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_pf) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + input_dict_cl = { + "r1": { + "bgp_community_lists": [ + { + "community_type": "expanded", + "action": "permit", + "name": "COMM", + "value": "100:100" + } + ] + } + } + result = create_bgp_community_lists(tgen, input_dict_cl) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + for addr_type in ADDR_TYPES: + input_dict_rm = { + "r1": { + "route_maps": { + "rmap_XYZ_{}".format(addr_type): [{ + "action": "permit", + "match": { + addr_type: { + "prefix_lists": + "pflist_ABC_{}".format(addr_type) + } + }, + "set": { + "community": {"num": "100:100"} + } + }] + } + } + } + result = create_route_maps(tgen, input_dict_rm) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Apply this route-map on R1 to vrf ISR while redistributing the" + " prefixes into BGP") + + input_dict_1={} + DUT = ["r1"] + VRFS = ["ISR"] + AS_NUM = [100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_1.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "redistribute": [{ + "redist_type": "static", + "attribute": { + "route-map" : "rmap_XYZ_{}".\ + format(addr_type) + } + } + ] + } + } + } + }) + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error: {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Configure another route-map for filtering the prefixes based on" + " community attribute while importing into default vrf") + + input_dict_rm = { + "r1": { + "route_maps": { + "rmap_IMP_{}".format(addr_type): [{ + "action": "permit", + "match": { + "community_list": {"id": "COMM"} + }, + "set": { + "community": {"num": "none"} + } + }] + } + } + } + result = create_route_maps(tgen, input_dict_rm) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Apply the route-map while Importing vrf ISR's prefixes into " + "default vrf on router R1:") + + input_dict_isr={} + DUT = ["r1"] + VRFS = ["default"] + AS_NUM = [100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_isr.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "ISR" + } + } + } + } + }) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "route-map rmap_IMP_{}".format(addr_type) + } + } + } + } + }) + + result = create_router_bgp(tgen, topo, input_dict_isr) + assert result is True, "Testcase {} : Failed \n Error: {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Verify on R1 that only prefixes with community value 100:100" + "in vrf ISR are imported to vrf default. While importing, the" + " community value has been stripped off:") + + input_routes_r1 = { + "r1": { + "static_routes": [{ + "network": [ + NETWORK1_1[addr_type] + ], + "vrf": "default" + }] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1) + assert result is True, \ + "Testcase {} : Failed \n Error {}". \ + format(tc_name, result) + + input_dict_comm = { + "community": "100:100" + } + + result = verify_bgp_community(tgen, addr_type, dut, [NETWORK1_1[addr_type]], + input_dict_comm, expected=False) + assert result is not True, ( + "Testcase {} : Failed \n Error: Commnunity is not stipped off, {}".format( + tc_name, result)) + + for addr_type in ADDR_TYPES: + + step("Remove/re-add route-map XYZ from redistribution.") + + input_dict_1={} + DUT = ["r1"] + VRFS = ["ISR"] + AS_NUM = [100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_1.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "redistribute": [{ + "redist_type": "static", + "attribute": { + "route-map" : "rmap_XYZ_{}".\ + format(addr_type) + }, + "delete": True + }] + } + } + } + }) + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error: {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Verify that all the routes disappear from vrf default when " + "route-map is removed from redistribution, and appear again " + "when route-map is re-added to redistribution in vrf ISR.") + + input_routes_r1 = { + "r1": { + "static_routes": [{ + "network": [ + NETWORK1_1[addr_type] + ], + "vrf": "default" + }] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1, + expected=False) + assert result is not True, ( + "Testcase {} : Failed \n Error : Routes are still present \n {}".\ + format(tc_name, result)) + + for addr_type in ADDR_TYPES: + + input_dict_1={} + DUT = ["r1"] + VRFS = ["ISR"] + AS_NUM = [100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_1.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "redistribute": [{ + "redist_type": "static", + "attribute": { + "route-map" : "rmap_XYZ_{}".\ + format(addr_type) + } + }] + } + } + } + }) + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error: {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + input_routes_r1 = { + "r1": { + "static_routes": [{ + "network": [ + NETWORK1_1[addr_type] + ], + "vrf": "default" + }] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1) + assert result is True, \ + "Testcase {} : Failed \n Error {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Remove/re-add route-map IMP form import statement.") + + input_dict_isr={} + DUT = ["r1"] + VRFS = ["default"] + AS_NUM = [100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_isr.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "ISR" + } + } + } + } + }) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "route-map rmap_IMP_{}".format(addr_type), + "delete": True + } + } + } + } + }) + + result = create_router_bgp(tgen, topo, input_dict_isr) + assert result is True, "Testcase {} : Failed \n Error: {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Verify that when route-map IMP is removed all the prefixes of" + " vrf ISR are imported to vrf default. However when route-map " + "IMP is re-added only 11.11.11.1 and 11:11::1 (with community " + "value) are imported.") + + input_routes_r1 = { + "r1": { + "static_routes": [{ + "network": [ + NETWORK1_1[addr_type] + ], + "vrf": "default" + }] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1) + assert result is True, \ + "Testcase {} : Failed \n Error {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + input_dict_isr={} + DUT = ["r1"] + VRFS = ["default"] + AS_NUM = [100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_isr.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "ISR" + } + } + } + } + }) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "route-map rmap_IMP_{}".format(addr_type) + } + } + } + } + }) + + result = create_router_bgp(tgen, topo, input_dict_isr) + assert result is True, "Testcase {} : Failed \n Error: {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + input_routes_r1 = { + "r1": { + "static_routes": [{ + "network": [ + NETWORK1_1[addr_type] + ], + "vrf": "default" + }] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1) + assert result is True, \ + "Testcase {} : Failed \n Error {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Delete/Re-add prefix-list ABC.") + + input_dict_pf = { + "r1": { + "prefix_lists": { + addr_type: { + "pflist_ABC_{}".format(addr_type): [{ + "seqid": 10, + "network": NETWORK1_1[addr_type], + "action": "permit", + "delete": True + }] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_pf) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + input_routes_r1 = { + "r1": { + "static_routes": [{ + "network": [ + NETWORK1_1[addr_type] + ], + "vrf": "default" + }] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1, + expected=False) + assert result is not True, ( + "Testcase {} : Failed \n Error : Routes are still present \n {}".\ + format(tc_name, result)) + + input_dict_pf["r1"]["prefix_lists"][addr_type]["pflist_ABC_{}".\ + format(addr_type)][0]["delete"]=False + + result = create_prefix_lists(tgen, input_dict_pf) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1) + assert result is True, \ + "Testcase {} : Failed \n Error {}". \ + format(tc_name, result) + + step("Delete/Re-add community-list COMM.") + + input_dict_cl = { + "r1": { + "bgp_community_lists": [ + { + "community_type": "expanded", + "action": "permit", + "name": "COMM", + "value": "100:100", + "delete": True + } + ] + } + } + result = create_bgp_community_lists(tgen, input_dict_cl) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1, + expected=False) + assert result is not True, ( + "Testcase {} : Failed \n Error : Routes are still present \n {}".\ + format(tc_name, result)) + + input_dict_cl["r1"]["bgp_community_lists"][0]["delete"]=False + + result = create_bgp_community_lists(tgen, input_dict_cl) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1) + assert result is True, \ + "Testcase {} : Failed \n Error {}". \ + format(tc_name, result) + + step("Delete/Re-add route-map XYZ.") + + input_dict_rm = { + "r1": { + "route_maps": { + "rmap_XYZ_{}".format(addr_type): [{ + "action": "permit", + "match": { + addr_type: { + "prefix_lists": + "pflist_ABC_{}".format(addr_type) + } + }, + "set": { + "community": {"num": "100:100"} + }, + "delete": True + }] + } + } + } + result = create_route_maps(tgen, input_dict_rm) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1, + expected=False) + assert result is not True, ( + "Testcase {} : Failed \n Error : Routes are still present \n {}".\ + format(tc_name, result)) + + input_dict_rm["r1"]["route_maps"]["rmap_XYZ_{}".format(addr_type)][0]["delete"]=False + + result = create_route_maps(tgen, input_dict_rm) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1) + assert result is True, \ + "Testcase {} : Failed \n Error {}". \ + format(tc_name, result) + + step("Delete/Re-add route-map IMP.") + + input_dict_rm2 = { + "r1": { + "route_maps": { + "rmap_IMP_{}".format(addr_type): [{ + "action": "permit", + "match": { + "community_list": {"id": "COMM"} + }, + "set": { + "community": {"num": "none"} + }, + "delete": True + }] + } + } + } + result = create_route_maps(tgen, input_dict_rm2) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1, + expected=False) + assert result is not True, ( + "Testcase {} : Failed \n Error : Routes are still present \n {}".\ + format(tc_name, result)) + + input_dict_rm2["r1"]["route_maps"]["rmap_IMP_{}".format(addr_type)][0]["delete"]=False + + result = create_route_maps(tgen, input_dict_rm2) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1) + assert result is True, \ + "Testcase {} : Failed \n Error {}". \ + format(tc_name, result) + + write_test_footer(tc_name) + + +def test_routemap_operatons_with_dynamic_import_p0(request): + """ + TC8_FUNC_8: + 1.5.8. Verify the route-map operation along with dynamic import command. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + build_config_from_json(tgen, topo) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + for addr_type in ADDR_TYPES: + + step("Configure route-map to set community attribute for a specific" + "prefix on R1 in vrf ISR") + + input_dict_pf = { + "r1": { + "prefix_lists": { + addr_type: { + "pflist_ABC_{}".format(addr_type): [{ + "seqid": 10, + "network": NETWORK1_1[addr_type], + "action": "permit" + }] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_pf) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + input_dict_cl = { + "r1": { + "bgp_community_lists": [ + { + "community_type": "expanded", + "action": "permit", + "name": "COMM", + "value": "100:100" + } + ] + } + } + result = create_bgp_community_lists(tgen, input_dict_cl) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + for addr_type in ADDR_TYPES: + input_dict_rm = { + "r1": { + "route_maps": { + "rmap_XYZ_{}".format(addr_type): [{ + "action": "permit", + "match": { + addr_type: { + "prefix_lists": + "pflist_ABC_{}".format(addr_type) + } + }, + "set": { + "community": {"num": "100:100"} + } + }] + } + } + } + result = create_route_maps(tgen, input_dict_rm) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Apply this route-map on R1 to vrf ISR while redistributing the" + " prefixes into BGP") + + input_dict_1={} + DUT = ["r1"] + VRFS = ["ISR"] + AS_NUM = [100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_1.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "redistribute": [{ + "redist_type": "static", + "attribute": { + "route-map" : "rmap_XYZ_{}".\ + format(addr_type) + } + } + ] + } + } + } + }) + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error: {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Configure another route-map for filtering the prefixes based on" + " community attribute while importing into default vrf") + + input_dict_rm = { + "r1": { + "route_maps": { + "rmap_IMP_{}".format(addr_type): [{ + "action": "permit", + "match": { + "community_list": {"id": "COMM"} + }, + "set": { + "community": {"num": "500:500"} + } + }] + } + } + } + result = create_route_maps(tgen, input_dict_rm) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Apply the route-map while Importing vrf ISR's prefixes into " + "default vrf on router R1:") + + input_dict_isr={} + DUT = ["r1"] + VRFS = ["default"] + AS_NUM = [100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_isr.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "ISR" + } + } + } + } + }) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "route-map rmap_IMP_{}".format(addr_type) + } + } + } + } + }) + + result = create_router_bgp(tgen, topo, input_dict_isr) + assert result is True, "Testcase {} : Failed \n Error: {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Verify on R1 that only prefixes with community value 100:100" + "in vrf ISR are imported to vrf default. While importing, the" + " community value has been stripped off:") + + input_routes_r1 = { + "r1": { + "static_routes": [{ + "network": [ + NETWORK1_1[addr_type] + ], + "vrf": "default" + }] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1) + assert result is True, \ + "Testcase {} : Failed \n Error {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Applying route-map first followed by import VRF command.") + step("Apply the route-map while Importing vrf ISR's prefixes into " + "default vrf on router R1:") + + input_dict_isr={} + DUT = ["r1"] + VRFS = ["default"] + AS_NUM = [100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_isr.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "ISR", + "delete": True + } + } + } + } + }) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "route-map rmap_IMP_{}".format(addr_type) + } + } + } + } + }) + + result = create_router_bgp(tgen, topo, input_dict_isr) + assert result is True, "Testcase {} : Failed \n Error: {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Verify that until 'import VRF command' is not configured, " + "routes are not imported. After configuring 'import VRF command'" + " repeat step-4 for verification") + + input_routes_r1 = { + "r1": { + "static_routes": [{ + "network": [ + NETWORK1_1[addr_type] + ], + "vrf": "default" + }] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1, + expected=False) + assert result is not True, ( + "Testcase {} : Failed \n Error : Routes are still present \n {}".\ + format(tc_name, result)) + + for addr_type in ADDR_TYPES: + + input_dict_isr={} + DUT = ["r1"] + VRFS = ["default"] + AS_NUM = [100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_isr.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "ISR" + } + } + } + } + }) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "route-map rmap_IMP_{}".format(addr_type) + } + } + } + } + }) + + result = create_router_bgp(tgen, topo, input_dict_isr) + assert result is True, "Testcase {} : Failed \n Error: {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + input_routes_r1 = { + "r1": { + "static_routes": [{ + "network": [ + NETWORK1_1[addr_type] + ], + "vrf": "default" + }] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1) + assert result is True, \ + "Testcase {} : Failed \n Error {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Delete/re-add import vrf ISR command multiple times in default" + "vrf.") + + input_dict_isr={} + DUT = ["r1"] + VRFS = ["default"] + AS_NUM = [100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_isr.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "ISR", + "delete": True + } + } + } + } + }) + + result = create_router_bgp(tgen, topo, input_dict_isr) + assert result is True, "Testcase {} : Failed \n Error: {}". \ + format(tc_name, result) + + step("Verify that when import vrf ISR command is deleted, " + "all routes of vrf ISR disappear from default vrf and " + "when it's re-configured, repeat step-4 for verification.") + + input_routes_r1 = { + "r1": { + "static_routes": [{ + "network": [ + NETWORK1_1[addr_type] + ], + "vrf": "default" + }] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1, + expected=False) + assert result is not True, ( + "Testcase {} : Failed \n Routes are still present, Error {}". \ + format(tc_name, result)) + + input_dict_isr["r1"]["bgp"][0]["address_family"][addr_type]["unicast"][ + "import"]["delete"]=False + + result = create_router_bgp(tgen, topo, input_dict_isr) + assert result is True, "Testcase {} : Failed \n Error: {}". \ + format(tc_name, result) + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1) + assert result is True, ( + "Testcase {} : Failed \n Error {}". \ + format(tc_name, result)) + + for addr_type in ADDR_TYPES: + + step("Delete and re-configure route-map IMP from global config when " + "import and route-maps are applied in a ISR vrf.") + + input_dict_rm = { + "r1": { + "route_maps": { + "rmap_IMP_{}".format(addr_type): [{ + "action": "permit", + "match": { + "community_list": {"id": "COMM"} + }, + "set": { + "community": {"num": "500:500"} + }, + "delete": True + }] + } + } + } + + result = create_route_maps(tgen, input_dict_rm) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + input_routes_r1 = { + "r1": { + "static_routes": [{ + "network": [ + NETWORK1_1[addr_type] + ], + "vrf": "default" + }] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1, + expected=False) + assert result is not True, ( + "Testcase {} : Failed \n Routes are still present, Error {}". \ + format(tc_name, result)) + + input_dict_rm["r1"]["route_maps"]["rmap_IMP_{}".\ + format(addr_type)][0]["delete"]=False + + result = create_route_maps(tgen, input_dict_rm) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + input_dict_comm = { + "community": "500:500" + } + + result = verify_bgp_community(tgen, addr_type, dut, [NETWORK1_1[addr_type]], + input_dict_comm) + assert result is True, ( + "Testcase {} : Failed \n Error: {}".format( + tc_name, result)) + + write_test_footer(tc_name) + + +def test_verify_cli_json_p1(request): + """ + TC8_FUNC_9: + 1.5.9. Verifying the JSON outputs for all supported commands: + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + build_config_from_json(tgen, topo) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + input_dict = { + "r1":{ + "cli": ["show bgp vrf default ipv4 summary", + "show bgp vrf all ipv6 summary", + "show bgp neighbors" + ] + } + } + + result = verify_cli_json(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + write_test_footer(tc_name) + + +if __name__ == '__main__': + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_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 new file mode 100755 index 0000000000..6c106060b8 --- /dev/null +++ b/tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo2.py @@ -0,0 +1,962 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +Following tests are covered to test BGP Multi-VRF Dynamic Route Leaking: + +1. Verify that Changing route-map configurations(match/set clauses) on + the fly it takes immediate effect. +2. Verify BGP best path selection algorithm works fine when + routes are imported from ISR to default vrf and vice versa. +""" + +import os +import sys +import json +import time +import pytest +import platform + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, '../')) +sys.path.append(os.path.join(CWD, '../lib/')) + +# Required to instantiate the topology builder class. + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from lib.topotest import version_cmp +from mininet.topo import Topo + +from lib.common_config import ( + start_topology, write_test_header, check_address_types, + write_test_footer, + verify_rib, step, create_route_maps, + create_static_routes, stop_router, start_router, + create_prefix_lists, + create_bgp_community_lists, + check_router_status, + get_frr_ipv6_linklocal, + shutdown_bringup_interface +) + +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, create_router_bgp, + verify_bgp_community, verify_bgp_attributes, + verify_best_path_as_per_bgp_attribute, verify_bgp_rib +) +from lib.topojson import build_topo_from_json, build_config_from_json + +# Reading the data from JSON File for topology creation +jsonFile = "{}/bgp_vrf_dynamic_route_leak_topo2.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +# Global variables +NETWORK1_1 = {"ipv4": "11.11.11.1/32", "ipv6": "11:11::1/128"} +NETWORK3_3 = {"ipv4": "50.50.50.5/32", "ipv6": "50:50::5/128"} +NETWORK3_4 = {"ipv4": "50.50.50.50/32", "ipv6": "50:50::50/128"} + +PREFERRED_NEXT_HOP = "global" + + +class CreateTopo(Topo): + """ + Test BasicTopo - topology 1 + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + """Build function""" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + global topo + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(CreateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen) + + # Run these tests for kernel version 4.19 or above + if version_cmp(platform.release(), '4.19') < 0: + error_msg = ('BGP vrf dynamic route leak tests will not run ' + '(have kernel "{}", but it requires >= 4.19)'.\ + format(platform.release())) + pytest.skip(error_msg) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + global BGP_CONVERGENCE + global ADDR_TYPES + ADDR_TYPES = check_address_types() + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error: {}". \ + format(BGP_CONVERGENCE) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info("Testsuite end time: {}". + format(time.asctime(time.localtime(time.time())))) + logger.info("=" * 40) + + +##################################################### +# +# Testcases +# +##################################################### + +def test_bgp_best_path_with_dynamic_import_p0(request): + """ + TC6_FUNC_6: + 1.5.6. Verify BGP best path selection algorithm works fine when + routes are imported from ISR to default vrf and vice versa. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + build_config_from_json(tgen, topo) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + for addr_type in ADDR_TYPES: + + step("Redistribute configured static routes into BGP process" + " on R1/R2 and R3") + + input_dict_1={} + DUT = ["r1", "r2", "r3", "r4"] + VRFS = ["ISR", "ISR", "default", "default"] + AS_NUM = [100, 100, 300, 400] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_1.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "redistribute": [{ + "redist_type": "static" + }] + } + } + } + }) + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error: {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Import from default vrf into vrf ISR on R1 and R2 as below") + + input_dict_vrf={} + DUT = ["r1", "r2"] + VRFS = ["ISR", "ISR"] + AS_NUM = [100, 100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_vrf.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "default" + } + } + } + } + }) + + result = create_router_bgp(tgen, topo, input_dict_vrf) + assert result is True, "Testcase {} : Failed \n Error: {}". \ + format(tc_name, result) + + input_dict_default={} + DUT = ["r1", "r2"] + VRFS = ["default", "default"] + AS_NUM = [100, 100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_default.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "ISR" + } + } + } + } + }) + + result = create_router_bgp(tgen, topo, input_dict_default) + assert result is True, "Testcase {} : Failed \n Error: {}". \ + format(tc_name, result) + + step("Verify ECMP/Next-hop/Imported routes Vs Locally originated " + "routes/eBGP routes vs iBGP routes --already covered in almost" + " all tests") + + for addr_type in ADDR_TYPES: + + step("Verify Pre-emption") + + input_routes_r3 = { + "r3": { + "static_routes": [{ + "network": [ + NETWORK3_3[addr_type] + ] + }] + } + } + + intf_r3_r1 = topo["routers"]["r3"]["links"]["r1-link1"]["interface"] + intf_r4_r1 = topo["routers"]["r4"]["links"]["r1-link1"]["interface"] + + if addr_type == "ipv6" and "link_local" in PREFERRED_NEXT_HOP: + nh_r3_r1 = get_frr_ipv6_linklocal(tgen, "r3", intf=intf_r3_r1) + nh_r4_r1 = get_frr_ipv6_linklocal(tgen, "r4", intf=intf_r4_r1) + else: + nh_r3_r1 = topo["routers"]["r3"]["links"]\ + ["r1-link1"][addr_type].split("/")[0] + nh_r4_r1 = topo["routers"]["r4"]["links"]\ + ["r1-link1"][addr_type].split("/")[0] + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r3, + next_hop=[nh_r4_r1]) + assert result is True, ( + "Testcase {} : Failed \n Error {}". \ + format(tc_name, result)) + + step("Shutdown interface connected to r1 from r4:") + shutdown_bringup_interface(tgen, 'r4', intf_r4_r1, False) + + for addr_type in ADDR_TYPES: + + input_routes_r3 = { + "r3": { + "static_routes": [{ + "network": [ + NETWORK3_3[addr_type] + ] + }] + } + } + + intf_r3_r1 = topo["routers"]["r3"]["links"]["r1-link1"]["interface"] + intf_r4_r1 = topo["routers"]["r4"]["links"]["r1-link1"]["interface"] + + if addr_type == "ipv6" and "link_local" in PREFERRED_NEXT_HOP: + nh_r3_r1 = get_frr_ipv6_linklocal(tgen, "r3", intf=intf_r3_r1) + nh_r4_r1 = get_frr_ipv6_linklocal(tgen, "r4", intf=intf_r4_r1) + else: + nh_r3_r1 = topo["routers"]["r3"]["links"]\ + ["r1-link1"][addr_type].split("/")[0] + nh_r4_r1 = topo["routers"]["r4"]["links"]\ + ["r1-link1"][addr_type].split("/")[0] + + step("Verify next-hop is changed") + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r3, + next_hop=[nh_r3_r1]) + assert result is True, ( + "Testcase {} : Failed \n Error {}". \ + format(tc_name, result)) + + step("Bringup interface connected to r1 from r4:") + shutdown_bringup_interface(tgen, 'r4', intf_r4_r1, True) + + for addr_type in ADDR_TYPES: + + input_routes_r3 = { + "r3": { + "static_routes": [{ + "network": [ + NETWORK3_3[addr_type] + ] + }] + } + } + + intf_r3_r1 = topo["routers"]["r3"]["links"]["r1-link1"]["interface"] + intf_r4_r1 = topo["routers"]["r4"]["links"]["r1-link1"]["interface"] + + if addr_type == "ipv6" and "link_local" in PREFERRED_NEXT_HOP: + nh_r3_r1 = get_frr_ipv6_linklocal(tgen, "r3", intf=intf_r3_r1) + nh_r4_r1 = get_frr_ipv6_linklocal(tgen, "r4", intf=intf_r4_r1) + else: + nh_r3_r1 = topo["routers"]["r3"]["links"]\ + ["r1-link1"][addr_type].split("/")[0] + nh_r4_r1 = topo["routers"]["r4"]["links"]\ + ["r1-link1"][addr_type].split("/")[0] + + step("Verify next-hop is not chnaged aftr shutdown:") + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r3, + next_hop=[nh_r3_r1]) + assert result is True, ( + "Testcase {} : Failed \n Error {}". \ + format(tc_name, result)) + + step("Active-Standby scenario(as-path prepend and Local pref)") + + for addr_type in ADDR_TYPES: + + step("Create prefix-list") + + input_dict_pf = { + "r1": { + "prefix_lists": { + addr_type: { + "pf_ls_{}".format(addr_type): [{ + "seqid": 10, + "network": NETWORK3_4[addr_type], + "action": "permit" + }] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_pf) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Create route-map to match prefix-list and set localpref 500") + + input_dict_rm = { + "r1": { + "route_maps": { + "rmap_PATH1_{}".format(addr_type): [{ + "action": "permit", + "seq_id": 10, + "match": { + addr_type: { + "prefix_lists": + "pf_ls_{}".format(addr_type) + } + }, + "set": { + "locPrf": 500 + } + }] + } + } + } + + result = create_route_maps(tgen, input_dict_rm) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + step("Create route-map to match prefix-list and set localpref 600") + + input_dict_rm = { + "r1": { + "route_maps": { + "rmap_PATH2_{}".format(addr_type): [{ + "action": "permit", + "seq_id": 20, + "match": { + addr_type: { + "prefix_lists": + "pf_ls_{}".format(addr_type) + } + }, + "set": { + "locPrf": 600 + } + }] + } + } + } + + result = create_route_maps(tgen, input_dict_rm) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + input_dict_rma={ + "r1": { + "bgp": + [ + { + "local_as": "100", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1-link1": { + "route_maps": [{ + "name": "rmap_PATH1_{}".\ + format(addr_type), + "direction": "in" + }] + } + } + }, + "r4": { + "dest_link": { + "r1-link1": { + "route_maps": [{ + "name": "rmap_PATH2_{}".\ + format(addr_type), + "direction": "in" + }] + } + } + } + } + } + } + } + } + ]} + } + + result = create_router_bgp(tgen, topo, input_dict_rma) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + dut = "r1" + attribute = "locPrf" + + for addr_type in ADDR_TYPES: + + step("Verify bestpath is installed as per highest localpref") + + input_routes_r3 = { + "r3": { + "static_routes": [{ + "network": [ + NETWORK3_3[addr_type], \ + NETWORK3_4[addr_type] + ] + }] + } + } + + result = verify_best_path_as_per_bgp_attribute(tgen, addr_type, dut, + input_routes_r3, + attribute) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Create route-map to match prefix-list and set localpref 700") + + input_dict_rm = { + "r1": { + "route_maps": { + "rmap_PATH1_{}".format(addr_type): [{ + "action": "permit", + "seq_id": 10, + "match": { + addr_type: { + "prefix_lists": + "pf_ls_{}".format(addr_type) + } + }, + "set": { + "locPrf": 700 + } + }] + } + } + } + + result = create_route_maps(tgen, input_dict_rm) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Verify bestpath is changed as per highest localpref") + + input_routes_r3 = { + "r3": { + "static_routes": [{ + "network": [ + NETWORK3_3[addr_type], \ + NETWORK3_4[addr_type] + ] + }] + } + } + + result = verify_best_path_as_per_bgp_attribute(tgen, addr_type, dut, + input_routes_r3, + attribute) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Create route-map to match prefix-list and set as-path prepend") + + input_dict_rm = { + "r1": { + "route_maps": { + "rmap_PATH2_{}".format(addr_type): [{ + "action": "permit", + "seq_id": 20, + "match": { + addr_type: { + "prefix_lists": + "pf_ls_{}".format(addr_type) + } + }, + "set": { + "localpref": 700, + "path": { + "as_num": "111", + "as_action": "prepend" + } + } + }] + } + } + } + + result = create_route_maps(tgen, input_dict_rm) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + attribute = "path" + + for addr_type in ADDR_TYPES: + + step("Verify bestpath is changed as per shortest as-path") + + input_routes_r3 = { + "r3": { + "static_routes": [{ + "network": [ + NETWORK3_3[addr_type], \ + NETWORK3_4[addr_type] + ] + }] + } + } + + result = verify_best_path_as_per_bgp_attribute(tgen, addr_type, dut, + input_routes_r3, + attribute) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + write_test_footer(tc_name) + + +def test_modify_route_map_match_set_clauses_p1(request): + """ + TC13_CHAOS_4: + 1.5.13. Verify that Changing route-map configurations(match/set clauses) on + the fly it takes immediate effect. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + build_config_from_json(tgen, topo) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + for addr_type in ADDR_TYPES: + + step("Configure route-map to set community attribute for a specific" + "prefix on R1 in vrf ISR") + + input_dict_pf = { + "r1": { + "prefix_lists": { + addr_type: { + "pflist_ABC_{}".format(addr_type): [{ + "seqid": 10, + "network": NETWORK1_1[addr_type], + "action": "permit" + }] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_pf) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + input_dict_cl = { + "r1": { + "bgp_community_lists": [ + { + "community_type": "expanded", + "action": "permit", + "name": "COMM", + "value": "100:100" + } + ] + } + } + result = create_bgp_community_lists(tgen, input_dict_cl) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + for addr_type in ADDR_TYPES: + input_dict_rm = { + "r1": { + "route_maps": { + "rmap_XYZ_{}".format(addr_type): [{ + "action": "permit", + "match": { + addr_type: { + "prefix_lists": + "pflist_ABC_{}".format(addr_type) + } + }, + "set": { + "community": {"num": "100:100"} + } + }] + } + } + } + result = create_route_maps(tgen, input_dict_rm) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Apply this route-map on R1 to vrf ISR while redistributing the" + " prefixes into BGP") + + input_dict_1={} + DUT = ["r1"] + VRFS = ["ISR"] + AS_NUM = [100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_1.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "redistribute": [{ + "redist_type": "static", + "attribute": { + "route-map" : "rmap_XYZ_{}".\ + format(addr_type) + } + } + ] + } + } + } + }) + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error: {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Configure another route-map for filtering the prefixes based on" + " community attribute while importing into default vrf") + + input_dict_rm = { + "r1": { + "route_maps": { + "rmap_IMP_{}".format(addr_type): [{ + "action": "permit", + "seq_id": 10, + "match": { + "community_list": {"id": "COMM"} + }, + "set": { + "community": {"num": "none"} + } + }] + } + } + } + result = create_route_maps(tgen, input_dict_rm) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Apply the route-map while Importing vrf ISR's prefixes into " + "default vrf on router R1:") + + input_dict_isr={} + DUT = ["r1"] + VRFS = ["default"] + AS_NUM = [100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_isr.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "ISR" + } + } + } + } + }) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "route-map rmap_IMP_{}".format(addr_type) + } + } + } + } + }) + + result = create_router_bgp(tgen, topo, input_dict_isr) + assert result is True, "Testcase {} : Failed \n Error: {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Verify on R1 that only prefixes with community value 100:100" + "in vrf ISR are imported to vrf default. While importing, the" + " community value has been stripped off:") + + input_routes_r1 = { + "r1": { + "static_routes": [{ + "network": [ + NETWORK1_1[addr_type] + ], + "vrf": "default" + }] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1) + assert result is True, \ + "Testcase {} : Failed \n Error {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Add set clause in route-map IMP:") + + input_dict_rm = { + "r1": { + "route_maps": { + "rmap_IMP_{}".format(addr_type): [{ + "action": "permit", + "seq_id": 10, + "match": { + "community_list": {"id": "COMM"} + }, + "set": { + "large_community": {"num": "100:100:100"}, + "locPrf": 500, + "path": { + "as_num": "100 100", + "as_action": "prepend" + } + } + }] + } + } + } + result = create_route_maps(tgen, input_dict_rm) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Verify that as we continue adding different attributes " + "step-by-step in route-map IMP those attributes gets " + "attached to prefixes:") + + input_routes_r1 = { + "r1": { + "static_routes": [{ + "network": [ + NETWORK1_1[addr_type] + ], + "vrf": "default" + }] + } + } + + input_dict_comm = { + "largeCommunity": "100:100:100" + } + + result = verify_bgp_community(tgen, addr_type, dut, [NETWORK1_1[addr_type]], + input_dict_comm) + assert result is True, ( + "Testcase {} : Failed \n Error {}".format( + tc_name, result)) + + input_rmap = { + "r1": { + "route_maps": { + "rmap_IMP_{}".format(addr_type): [ + { + "set": { + "locPrf": 500 + } + } + ] + } + } + } + + result = verify_bgp_attributes(tgen, addr_type, "r1",\ + [NETWORK1_1[addr_type]], + rmap_name="rmap_IMP_{}".format(addr_type),\ + input_dict=input_rmap) + assert result is True, "Testcase : Failed \n Error: {}".format( + tc_name, result) + + step("Change community-list to match a different value then " + "100:100.") + + input_dict_cl = { + "r1": { + "bgp_community_lists": [ + { + "community_type": "expanded", + "action": "permit", + "name": "COMM", + "value": "100:100", + "delete": True + } + ] + } + } + result = create_bgp_community_lists(tgen, input_dict_cl) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + for addr_type in ADDR_TYPES: + + input_routes_r1 = { + "r1": { + "static_routes": [{ + "network": [ + NETWORK1_1[addr_type] + ], + "vrf": "default" + }] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1, + expected=False) + assert result is not True, ( + "Testcase {} : Failed \n Error : Routes are still " + "present {}".\ + format(tc_name, result)) + + write_test_footer(tc_name) + + +if __name__ == '__main__': + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/__init__.py b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/bgpd.conf b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/bgpd.conf new file mode 100644 index 0000000000..002cecd1fa --- /dev/null +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/bgpd.conf @@ -0,0 +1,14 @@ +router bgp 101 vrf r1-cust1 + bgp router-id 10.254.254.1 + no bgp ebgp-requires-policy + neighbor r2g peer-group + neighbor r2g remote-as external + neighbor r2g bfd + neighbor r1-eth0 interface peer-group r2g + address-family ipv4 unicast + redistribute connected + exit-address-family + address-family ipv6 unicast + neighbor r2g activate + exit-address-family +! diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/ipv4_routes.json b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/ipv4_routes.json new file mode 100644 index 0000000000..1c7500b7be --- /dev/null +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/ipv4_routes.json @@ -0,0 +1,44 @@ +{ + "10.254.254.2/32": [ + { + "prefix": "10.254.254.2/32", + "protocol": "bgp", + "vrfName": "r1-cust1", + "selected": true, + "destSelected": true, + "distance": 20, + "metric": 0, + "installed": true, + "nexthops": [ + { + "flags": 3, + "fib": true, + "afi": "ipv6", + "interfaceName": "r1-eth0", + "active": true + } + ] + } + ], + "10.254.254.1/32": [ + { + "prefix": "10.254.254.1/32", + "protocol": "connected", + "vrfName": "r1-cust1", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "loop1", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/ipv6_routes.json b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/ipv6_routes.json new file mode 100644 index 0000000000..0e1de87c34 --- /dev/null +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/ipv6_routes.json @@ -0,0 +1,37 @@ +{ + "2001:db8:1::/64": [ + { + "prefix": "2001:db8:1::/64", + "protocol": "bgp", + "vrfName": "r1-cust1", + "distance": 20, + "metric": 0, + "nexthops": [ + { + "afi": "ipv6", + "interfaceName": "r1-eth0", + "active": true + } + ] + }, + { + "prefix": "2001:db8:1::/64", + "protocol": "connected", + "vrfName": "r1-cust1", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "r1-eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/zebra.conf b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/zebra.conf new file mode 100644 index 0000000000..f19c497208 --- /dev/null +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/zebra.conf @@ -0,0 +1,9 @@ +debug zebra packet recv +debug zebra packet send +log stdout +interface loop1 vrf r1-cust1 + ip address 10.254.254.1/32 +! +interface r1-eth0 vrf r1-cust1 + ipv6 address 2001:db8:1::1/64 +! diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/bgpd.conf b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/bgpd.conf new file mode 100644 index 0000000000..0878b9b995 --- /dev/null +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/bgpd.conf @@ -0,0 +1,17 @@ +router bgp 102 vrf r2-cust1 + bgp router-id 10.254.254.2 + no bgp ebgp-requires-policy + neighbor r2g peer-group + neighbor r2g remote-as external + neighbor r2g bfd + neighbor r2-eth0 interface peer-group r2g + ! + address-family ipv4 unicast + redistribute connected + exit-address-family + ! + address-family ipv6 unicast + redistribute connected + neighbor r2g activate + exit-address-family +! diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/ipv4_routes.json b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/ipv4_routes.json new file mode 100644 index 0000000000..4d7b289aeb --- /dev/null +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/ipv4_routes.json @@ -0,0 +1,44 @@ +{ + "10.254.254.2/32": [ + { + "prefix": "10.254.254.2/32", + "protocol": "connected", + "vrfName": "r2-cust1", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "loop1", + "active": true + } + ] + } + ], + "10.254.254.1/32": [ + { + "prefix": "10.254.254.1/32", + "protocol": "bgp", + "vrfName": "r2-cust1", + "selected": true, + "destSelected": true, + "distance": 20, + "metric": 0, + "installed": true, + "nexthops": [ + { + "flags": 3, + "fib": true, + "afi": "ipv6", + "interfaceName": "r2-eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/ipv6_routes.json b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/ipv6_routes.json new file mode 100644 index 0000000000..805b57d36f --- /dev/null +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/ipv6_routes.json @@ -0,0 +1,23 @@ +{ + "2001:db8:1::/64": [ + { + "prefix": "2001:db8:1::/64", + "protocol": "connected", + "vrfName": "r2-cust1", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "r2-eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/zebra.conf b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/zebra.conf new file mode 100644 index 0000000000..c3795ab959 --- /dev/null +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/zebra.conf @@ -0,0 +1,9 @@ +ip forwarding +ipv6 forwarding +! +interface loop1 vrf r2-cust1 + ip address 10.254.254.2/32 +! +interface r2-eth0 vrf r2-cust1 + ipv6 address 2001:db8:1::2/64 +! diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.dot b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.dot new file mode 100644 index 0000000000..da67c29a09 --- /dev/null +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.dot @@ -0,0 +1,44 @@ +## Color coding: +######################### +## Main FRR: #f08080 red +## Switches: #d0e0d0 gray +## RIP: #19e3d9 Cyan +## RIPng: #fcb314 dark yellow +## OSPFv2: #32b835 Green +## OSPFv3: #19e3d9 Cyan +## ISIS IPv4 #fcb314 dark yellow +## ISIS IPv6 #9a81ec purple +## BGP IPv4 #eee3d3 beige +## BGP IPv6 #fdff00 yellow +##### Colors (see http://www.color-hex.com/) + +graph template { + label="bfd-topo2"; + + # Routers + r1 [ + shape=doubleoctagon, + label="r1", + fillcolor="#f08080", + style=filled, + ]; + r2 [ + shape=doubleoctagon + label="r2", + fillcolor="#f08080", + style=filled, + ]; + + # Switches + sw1 [ + shape=oval, + label="sw1\n2001:db8:1::/64", + fillcolor="#d0e0d0", + style=filled, + ]; + + # Connections + r1 -- sw1 [label="eth0"]; + r2 -- sw1 [label="eth0"]; + +} diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.py b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.py new file mode 100644 index 0000000000..4551ed37d4 --- /dev/null +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.py @@ -0,0 +1,169 @@ +#!/usr/bin/env python + +# +# test_bgp_ipv6_rtadv.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2019 by 6WIND +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" + test_bgp_ipv6_rtadv.py: Test the FRR/Quagga BGP daemon with BGP IPv6 interface + with route advertisements on a separate netns. +""" + +import os +import sys +import json +from functools import partial +import pytest +import platform + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from lib.common_config import adjust_router_l3mdev + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + + +class BGPIPV6RTADVVRFTopo(Topo): + "Test topology builder" + + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # Create 2 routers. + tgen.add_router("r1") + tgen.add_router("r2") + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(BGPIPV6RTADVVRFTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + logger.info("Testing with VRF Lite support") + + cmds = [ + "ip link add {0}-cust1 type vrf table 1001", + "ip link add loop1 type dummy", + "ip link set loop1 master {0}-cust1", + "ip link set {0}-eth0 master {0}-cust1", + ] + + for rname, router in router_list.iteritems(): + for cmd in cmds: + output = tgen.net[rname].cmd(cmd.format(rname)) + + # adjust handling of vrf traffic + adjust_router_l3mdev(tgen, rname) + + for rname, router in router_list.iteritems(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + # Initialize all routers. + tgen.start_router() + + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + + tgen.stop_topology() + + +def test_protocols_convergence(): + """ + Assert that all protocols have converged + statuses as they depend on it. + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Check IPv4 routing tables. + logger.info("Checking IPv4 routes for convergence") + + for router in tgen.routers().values(): + json_file = "{}/{}/ipv4_routes.json".format(CWD, router.name) + if not os.path.isfile(json_file): + logger.info("skipping file {}".format(json_file)) + continue + + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, + router, + "show ip route vrf {}-cust1 json".format(router.name), + expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=160, wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + # Check IPv6 routing tables. + logger.info("Checking IPv6 routes for convergence") + for router in tgen.routers().values(): + json_file = "{}/{}/ipv6_routes.json".format(CWD, router.name) + if not os.path.isfile(json_file): + logger.info("skipping file {}".format(json_file)) + continue + + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, + router, + "show ipv6 route vrf {}-cust1 json".format(router.name), + expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=160, wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_vrf_netns/peer1/exa-receive.py b/tests/topotests/bgp_vrf_netns/peer1/exa-receive.py index 5334ea5369..031ff455ca 100755 --- a/tests/topotests/bgp_vrf_netns/peer1/exa-receive.py +++ b/tests/topotests/bgp_vrf_netns/peer1/exa-receive.py @@ -4,7 +4,7 @@ exa-receive.py: Save received routes form ExaBGP into file """ -from sys import stdin,argv +from sys import stdin, argv from datetime import datetime # 1st arg is peer number @@ -13,12 +13,12 @@ # When the parent dies we are seeing continual newlines, so we only access so many before stopping counter = 0 -routesavefile = open('/tmp/peer%s-received.log' % peer, 'w') +routesavefile = open("/tmp/peer%s-received.log" % peer, "w") while True: try: line = stdin.readline() - timestamp = datetime.now().strftime('%Y%m%d_%H:%M:%S - ') + timestamp = datetime.now().strftime("%Y%m%d_%H:%M:%S - ") routesavefile.write(timestamp + line) routesavefile.flush() diff --git a/tests/topotests/bgp_vrf_netns/peer1/exa-send.py b/tests/topotests/bgp_vrf_netns/peer1/exa-send.py index 9a2a201c57..9279cc45ff 100755 --- a/tests/topotests/bgp_vrf_netns/peer1/exa-send.py +++ b/tests/topotests/bgp_vrf_netns/peer1/exa-send.py @@ -4,7 +4,7 @@ exa-send.py: Send a few testroutes with ExaBGP """ -from sys import stdout,argv +from sys import stdout, argv from time import sleep sleep(5) @@ -17,10 +17,12 @@ # Announce numRoutes equal routes per PE - different neighbor AS for i in range(0, numRoutes): - stdout.write('announce route 10.201.%s.0/24 med 100 community %i:1 next-hop 10.0.%i.%i\n' % (i, i, (((peer-1) / 5) + 1), peer+100)) + stdout.write( + "announce route 10.201.%s.0/24 med 100 community %i:1 next-hop 10.0.%i.%i\n" + % (i, i, (((peer - 1) / 5) + 1), peer + 100) + ) stdout.flush() -#Loop endlessly to allow ExaBGP to continue running +# Loop endlessly to allow ExaBGP to continue running while True: sleep(1) - diff --git a/tests/topotests/bgp_vrf_netns/r1/bgpd.conf b/tests/topotests/bgp_vrf_netns/r1/bgpd.conf index e3f158d7b3..dabf9521ac 100644 --- a/tests/topotests/bgp_vrf_netns/r1/bgpd.conf +++ b/tests/topotests/bgp_vrf_netns/r1/bgpd.conf @@ -2,6 +2,7 @@ router bgp 100 vrf r1-cust1 bgp router-id 10.0.1.1 bgp bestpath as-path multipath-relax + no bgp ebgp-requires-policy neighbor 10.0.1.101 remote-as 99 ! ! diff --git a/tests/topotests/bgp_vrf_netns/r1/summary.txt b/tests/topotests/bgp_vrf_netns/r1/summary.txt index 7473fa2151..1a079ff130 100644 --- a/tests/topotests/bgp_vrf_netns/r1/summary.txt +++ b/tests/topotests/bgp_vrf_netns/r1/summary.txt @@ -8,7 +8,7 @@ "10.0.1.101":{ "outq":0, "inq":0, - "prefixReceivedCount":10, + "pfxRcd":10, "state":"Established" } }, diff --git a/tests/topotests/bgp_vrf_netns/r1/summary20.txt b/tests/topotests/bgp_vrf_netns/r1/summary20.txt index 18318e07a8..2b5787e6da 100644 --- a/tests/topotests/bgp_vrf_netns/r1/summary20.txt +++ b/tests/topotests/bgp_vrf_netns/r1/summary20.txt @@ -7,7 +7,7 @@ "10.0.1.101":{ "outq":0, "inq":0, - "prefixReceivedCount":10, + "pfxRcd":10, "state":"Established" } }, diff --git a/tests/topotests/bgp_vrf_netns/test_bgp_vrf_netns_topo.py b/tests/topotests/bgp_vrf_netns/test_bgp_vrf_netns_topo.py index a5590bcaf6..ae48f01a0e 100755 --- a/tests/topotests/bgp_vrf_netns/test_bgp_vrf_netns_topo.py +++ b/tests/topotests/bgp_vrf_netns/test_bgp_vrf_netns_topo.py @@ -33,7 +33,7 @@ # Save the Current Working Directory to find configuration files. CWD = os.path.dirname(os.path.realpath(__file__)) -sys.path.append(os.path.join(CWD, '../')) +sys.path.append(os.path.join(CWD, "../")) # pylint: disable=C0413 # Import topogen and topotest helpers @@ -53,6 +53,7 @@ ## ##################################################### + class BGPVRFNETNSTopo1(Topo): "BGP EBGP VRF NETNS Topology 1" @@ -60,18 +61,17 @@ def build(self, **_opts): tgen = get_topogen(self) # Setup Routers - tgen.add_router('r1') + tgen.add_router("r1") # Setup Switches - switch = tgen.add_switch('s1') - switch.add_link(tgen.gears['r1']) + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) # Add eBGP ExaBGP neighbors - peer_ip = '10.0.1.101' - peer_route = 'via 10.0.1.1' - peer = tgen.add_exabgp_peer('peer1', - ip=peer_ip, defaultRoute=peer_route) - switch = tgen.gears['s1'] + peer_ip = "10.0.1.101" + peer_route = "via 10.0.1.1" + peer = tgen.add_exabgp_peer("peer1", ip=peer_ip, defaultRoute=peer_route) + switch = tgen.gears["s1"] switch.add_link(peer) @@ -81,74 +81,87 @@ def build(self, **_opts): ## ##################################################### + def setup_module(module): tgen = Topogen(BGPVRFNETNSTopo1, module.__name__) tgen.start_topology() # Get r1 reference - router = tgen.gears['r1'] + router = tgen.gears["r1"] # check for zebra capability if CustomizeVrfWithNetns == True: - if router.check_capability( - TopoRouter.RD_ZEBRA, - '--vrfwnetns' - ) == False: - return pytest.skip('Skipping BGP VRF NETNS Test. VRF NETNS backend not available on FRR') - if os.system('ip netns list') != 0: - return pytest.skip('Skipping BGP VRF NETNS Test. NETNS not available on System') + if router.check_capability(TopoRouter.RD_ZEBRA, "--vrfwnetns") == False: + return pytest.skip( + "Skipping BGP VRF NETNS Test. VRF NETNS backend not available on FRR" + ) + if os.system("ip netns list") != 0: + return pytest.skip( + "Skipping BGP VRF NETNS Test. NETNS not available on System" + ) # retrieve VRF backend kind if CustomizeVrfWithNetns == True: - logger.info('Testing with VRF Namespace support') + logger.info("Testing with VRF Namespace support") # create VRF r1-cust1 # move r1-eth0 to VRF r1-cust1 - cmds = ['if [ -e /var/run/netns/{0}-cust1 ] ; then ip netns del {0}-cust1 ; fi', - 'ip netns add {0}-cust1', - 'ip link set dev {0}-eth0 netns {0}-cust1', - 'ip netns exec {0}-cust1 ifconfig {0}-eth0 up'] + cmds = [ + "if [ -e /var/run/netns/{0}-cust1 ] ; then ip netns del {0}-cust1 ; fi", + "ip netns add {0}-cust1", + "ip link set dev {0}-eth0 netns {0}-cust1", + "ip netns exec {0}-cust1 ifconfig {0}-eth0 up", + ] for cmd in cmds: - cmd = cmd.format('r1') - logger.info('cmd: '+cmd); - output = router.run(cmd.format('r1')) + cmd = cmd.format("r1") + logger.info("cmd: " + cmd) + output = router.run(cmd.format("r1")) if output != None and len(output) > 0: - logger.info('Aborting due to unexpected output: cmd="{}" output=\n{}'.format(cmd, output)) - return pytest.skip('Skipping BGP VRF NETNS Test. Unexpected output to command: '+cmd) - #run daemons + logger.info( + 'Aborting due to unexpected output: cmd="{}" output=\n{}'.format( + cmd, output + ) + ) + return pytest.skip( + "Skipping BGP VRF NETNS Test. Unexpected output to command: " + cmd + ) + # run daemons router.load_config( TopoRouter.RD_ZEBRA, - os.path.join(CWD, '{}/zebra.conf'.format('r1')), - '--vrfwnetns' + os.path.join(CWD, "{}/zebra.conf".format("r1")), + "--vrfwnetns", ) router.load_config( - TopoRouter.RD_BGP, - os.path.join(CWD, '{}/bgpd.conf'.format('r1')) + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format("r1")) ) - logger.info('Launching BGP and ZEBRA') + logger.info("Launching BGP and ZEBRA") # BGP and ZEBRA start without underlying VRF router.start() # Starting Hosts and init ExaBGP on each of them - logger.info('starting exaBGP on peer1') + logger.info("starting exaBGP on peer1") peer_list = tgen.exabgp_peers() for pname, peer in peer_list.iteritems(): peer_dir = os.path.join(CWD, pname) - env_file = os.path.join(CWD, 'exabgp.env') - logger.info('Running ExaBGP peer') + env_file = os.path.join(CWD, "exabgp.env") + logger.info("Running ExaBGP peer") peer.start(peer_dir, env_file) logger.info(pname) + def teardown_module(module): tgen = get_topogen() # move back r1-eth0 to default VRF # delete VRF r1-cust1 - cmds = ['ip netns exec {0}-cust1 ip link set {0}-eth0 netns 1', - 'ip netns delete {0}-cust1'] + cmds = [ + "ip netns exec {0}-cust1 ip link set {0}-eth0 netns 1", + "ip netns delete {0}-cust1", + ] for cmd in cmds: - tgen.net['r1'].cmd(cmd.format('r1')) + tgen.net["r1"].cmd(cmd.format("r1")) tgen.stop_topology() + def test_bgp_vrf_learn(): "Test daemon learnt VRF context" tgen = get_topogen() @@ -158,11 +171,11 @@ def test_bgp_vrf_learn(): pytest.skip(tgen.errors) # Expected result - output = tgen.gears['r1'].vtysh_cmd("show vrf", isjson=False) - logger.info('output is: {}'.format(output)) + output = tgen.gears["r1"].vtysh_cmd("show vrf", isjson=False) + logger.info("output is: {}".format(output)) - output = tgen.gears['r1'].vtysh_cmd("show bgp vrfs", isjson=False) - logger.info('output is: {}'.format(output)) + output = tgen.gears["r1"].vtysh_cmd("show bgp vrfs", isjson=False) + logger.info("output is: {}".format(output)) def test_bgp_convergence(): @@ -175,23 +188,25 @@ def test_bgp_convergence(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - logger.info('waiting for bgp convergence') + logger.info("waiting for bgp convergence") # Expected result - router = tgen.gears['r1'] - if router.has_version('<', '3.0'): - reffile = os.path.join(CWD, 'r1/summary20.txt') + router = tgen.gears["r1"] + if router.has_version("<", "3.0"): + reffile = os.path.join(CWD, "r1/summary20.txt") else: - reffile = os.path.join(CWD, 'r1/summary.txt') + reffile = os.path.join(CWD, "r1/summary.txt") expected = json.loads(open(reffile).read()) - test_func = functools.partial(topotest.router_json_cmp, - router, 'show bgp vrf r1-cust1 summary json', expected) + test_func = functools.partial( + topotest.router_json_cmp, router, "show bgp vrf r1-cust1 summary json", expected + ) _, res = topotest.run_and_expect(test_func, None, count=90, wait=0.5) - assertmsg = 'BGP router network did not converge' + assertmsg = "BGP router network did not converge" assert res is None, assertmsg + def test_bgp_vrf_netns(): tgen = get_topogen() @@ -200,24 +215,28 @@ def test_bgp_vrf_netns(): pytest.skip(tgen.errors) expect = { - 'routerId': '10.0.1.1', - 'routes': { - }, + "routerId": "10.0.1.1", + "routes": {}, } for subnet in range(0, 10): - netkey = '10.201.{}.0/24'.format(subnet) - expect['routes'][netkey] = [] - peer = {'valid': True} - expect['routes'][netkey].append(peer) - - test_func = functools.partial(topotest.router_json_cmp, - tgen.gears['r1'], 'show ip bgp vrf r1-cust1 ipv4 json', expect) + netkey = "10.201.{}.0/24".format(subnet) + expect["routes"][netkey] = [] + peer = {"valid": True} + expect["routes"][netkey].append(peer) + + test_func = functools.partial( + topotest.router_json_cmp, + tgen.gears["r1"], + "show ip bgp vrf r1-cust1 ipv4 json", + expect, + ) _, res = topotest.run_and_expect(test_func, None, count=12, wait=0.5) assertmsg = 'expected routes in "show ip bgp vrf r1-cust1 ipv4" output' assert res is None, assertmsg -if __name__ == '__main__': + +if __name__ == "__main__": args = ["-s"] + sys.argv[1:] ret = pytest.main(args) diff --git a/tests/topotests/conftest.py b/tests/topotests/conftest.py index 327e4625f2..04e9961f10 100755 --- a/tests/topotests/conftest.py +++ b/tests/topotests/conftest.py @@ -7,28 +7,36 @@ from lib.topolog import logger import pytest +topology_only = False + + def pytest_addoption(parser): """ Add topology-only option to the topology tester. This option makes pytest only run the setup_module() to setup the topology without running any tests. """ - parser.addoption('--topology-only', action='store_true', - help='Only set up this topology, don\'t run tests') + parser.addoption( + "--topology-only", + action="store_true", + help="Only set up this topology, don't run tests", + ) + def pytest_runtest_call(): """ This function must be run after setup_module(), it does standarized post setup routines. It is only being used for the 'topology-only' option. """ - # pylint: disable=E1101 - # Trust me, 'config' exists. - if pytest.config.getoption('--topology-only'): + global topology_only + + if topology_only: tgen = get_topogen() if tgen is not None: # Allow user to play with the setup. tgen.mininet_cli() - pytest.exit('the topology executed successfully') + pytest.exit("the topology executed successfully") + def pytest_assertrepr_compare(op, left, right): """ @@ -40,12 +48,20 @@ def pytest_assertrepr_compare(op, left, right): if not isinstance(json_result, json_cmp_result): return None - return json_result.errors + return json_result.gen_report() + def pytest_configure(config): "Assert that the environment is correctly configured." + + global topology_only + if not diagnose_env(): - pytest.exit('enviroment has errors, please read the logs') + pytest.exit("enviroment has errors, please read the logs") + + if config.getoption("--topology-only"): + topology_only = True + def pytest_runtest_makereport(item, call): "Log all assert messages to default logger with error level" @@ -57,12 +73,22 @@ def pytest_runtest_makereport(item, call): modname = parent.module.__name__ # Treat skips as non errors - if call.excinfo.typename != 'AssertionError': - logger.info('assert skipped at "{}/{}": {}'.format( - modname, item.name, call.excinfo.value)) + if call.excinfo.typename != "AssertionError": + logger.info( + 'assert skipped at "{}/{}": {}'.format( + modname, item.name, call.excinfo.value + ) + ) return # Handle assert failures parent._previousfailed = item - logger.error('assert failed at "{}/{}": {}'.format( - modname, item.name, call.excinfo.value)) + logger.error( + 'assert failed at "{}/{}": {}'.format(modname, item.name, call.excinfo.value) + ) + + # (topogen) Set topology error to avoid advancing in the test. + tgen = get_topogen() + if tgen is not None: + # This will cause topogen to report error on `routers_have_failure`. + tgen.set_error("{}/{}".format(modname, item.name)) diff --git a/tests/topotests/docker/inner/compile_frr.sh b/tests/topotests/docker/inner/compile_frr.sh index 2d72082c1e..dee0ec8118 100755 --- a/tests/topotests/docker/inner/compile_frr.sh +++ b/tests/topotests/docker/inner/compile_frr.sh @@ -84,6 +84,7 @@ if [ ! -e Makefile ]; then --enable-static-bin \ --enable-static \ --enable-shared \ + --enable-dev-build \ --with-moduledir=/usr/lib/frr/modules \ --prefix=/usr \ --localstatedir=/var/run/frr \ diff --git a/tests/topotests/eigrp-topo1/test_eigrp_topo1.py b/tests/topotests/eigrp-topo1/test_eigrp_topo1.py index 1c00face43..c1dd88823b 100755 --- a/tests/topotests/eigrp-topo1/test_eigrp_topo1.py +++ b/tests/topotests/eigrp-topo1/test_eigrp_topo1.py @@ -35,7 +35,7 @@ # Save the Current Working Directory to find configuration files. CWD = os.path.dirname(os.path.realpath(__file__)) -sys.path.append(os.path.join(CWD, '../')) +sys.path.append(os.path.join(CWD, "../")) # pylint: disable=C0413 # Import topogen and topotest helpers @@ -52,6 +52,7 @@ ## ##################################################### + class NetworkTopo(Topo): "EIGRP Topology 1" @@ -61,27 +62,27 @@ def build(self, **_opts): tgen = get_topogen(self) for routern in range(1, 4): - tgen.add_router('r{}'.format(routern)) + tgen.add_router("r{}".format(routern)) # On main router # First switch is for a dummy interface (for local network) - switch = tgen.add_switch('sw1') - switch.add_link(tgen.gears['r1']) + switch = tgen.add_switch("sw1") + switch.add_link(tgen.gears["r1"]) # Switches for EIGRP # switch 2 switch is for connection to EIGRP router - switch = tgen.add_switch('sw2') - switch.add_link(tgen.gears['r1']) - switch.add_link(tgen.gears['r2']) + switch = tgen.add_switch("sw2") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) # switch 4 is stub on remote EIGRP router - switch = tgen.add_switch('sw4') - switch.add_link(tgen.gears['r3']) + switch = tgen.add_switch("sw4") + switch.add_link(tgen.gears["r3"]) # switch 3 is between EIGRP routers - switch = tgen.add_switch('sw3') - switch.add_link(tgen.gears['r2']) - switch.add_link(tgen.gears['r3']) + switch = tgen.add_switch("sw3") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) ##################################################### @@ -90,6 +91,7 @@ def build(self, **_opts): ## ##################################################### + def setup_module(module): "Setup topology" tgen = Topogen(NetworkTopo, module.__name__) @@ -99,12 +101,10 @@ def setup_module(module): router_list = tgen.routers() for rname, router in router_list.iteritems(): router.load_config( - TopoRouter.RD_ZEBRA, - os.path.join(CWD, '{}/zebra.conf'.format(rname)) + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) ) router.load_config( - TopoRouter.RD_EIGRP, - os.path.join(CWD, '{}/eigrpd.conf'.format(rname)) + TopoRouter.RD_EIGRP, os.path.join(CWD, "{}/eigrpd.conf".format(rname)) ) tgen.start_router() @@ -126,7 +126,7 @@ def test_converge_protocols(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - topotest.sleep(5, 'Waiting for EIGRP convergence') + topotest.sleep(5, "Waiting for EIGRP convergence") def test_eigrp_routes(): @@ -142,7 +142,7 @@ def test_eigrp_routes(): router_list = tgen.routers().values() for router in router_list: - refTableFile = '{}/{}/show_ip_eigrp.json'.format(CWD, router.name) + refTableFile = "{}/{}/show_ip_eigrp.json".format(CWD, router.name) # Read expected result from file expected = json.loads(open(refTableFile).read()) @@ -153,6 +153,7 @@ def test_eigrp_routes(): assertmsg = '"show ip eigrp topo" mismatches on {}'.format(router.name) assert topotest.json_cmp(actual, expected) is None, assertmsg + def test_zebra_ipv4_routingTable(): "Test 'show ip route'" @@ -164,27 +165,29 @@ def test_zebra_ipv4_routingTable(): failures = 0 router_list = tgen.routers().values() for router in router_list: - output = router.vtysh_cmd('show ip route json', isjson=True) - refTableFile = '{}/{}/show_ip_route.json_ref'.format(CWD, router.name) + output = router.vtysh_cmd("show ip route json", isjson=True) + refTableFile = "{}/{}/show_ip_route.json_ref".format(CWD, router.name) expected = json.loads(open(refTableFile).read()) - assertmsg = 'Zebra IPv4 Routing Table verification failed for router {}'.format(router.name) + assertmsg = "Zebra IPv4 Routing Table verification failed for router {}".format( + router.name + ) assert topotest.json_cmp(output, expected) is None, assertmsg + def test_shut_interface_and_recover(): "Test shutdown of an interface and recovery of the interface" tgen = get_topogen() - router = tgen.gears['r1'] - router.run('ip link set r1-eth1 down') - topotest.sleep(5, 'Waiting for EIGRP convergence') - router.run('ip link set r1-eth1 up') - + router = tgen.gears["r1"] + router.run("ip link set r1-eth1 down") + topotest.sleep(5, "Waiting for EIGRP convergence") + router.run("ip link set r1-eth1 up") def test_shutdown_check_stderr(): - if os.environ.get('TOPOTESTS_CHECK_STDERR') is None: - pytest.skip('Skipping test for Stderr output and memory leaks') + if os.environ.get("TOPOTESTS_CHECK_STDERR") is None: + pytest.skip("Skipping test for Stderr output and memory leaks") tgen = get_topogen() # Don't run this test if we have any failure. @@ -197,15 +200,15 @@ def test_shutdown_check_stderr(): for router in router_list: router.stop() - log = tgen.net[router.name].getStdErr('eigrpd') + log = tgen.net[router.name].getStdErr("eigrpd") if log: - logger.error('EIGRPd StdErr Log:' + log) - log = tgen.net[router.name].getStdErr('zebra') + logger.error("EIGRPd StdErr Log:" + log) + log = tgen.net[router.name].getStdErr("zebra") if log: - logger.error('Zebra StdErr Log:' + log) + logger.error("Zebra StdErr Log:" + log) -if __name__ == '__main__': +if __name__ == "__main__": args = ["-s"] + sys.argv[1:] sys.exit(pytest.main(args)) @@ -237,23 +240,23 @@ def ip_eigrp_topo(node): } } """ - output = topotest.normalize_text(node.vtysh_cmd('show ip eigrp topo')).splitlines() + output = topotest.normalize_text(node.vtysh_cmd("show ip eigrp topo")).splitlines() result = {} for idx, line in enumerate(output): - columns = line.split(' ', 1) + columns = line.split(" ", 1) # Parse the following format into python dicts # code A.B.C.D/E, X successors, FD is Y, serno: Z # via FOO, interface-name code = columns[0] - if code not in ['P', 'A', 'U', 'Q', 'R', 'r', 's']: + if code not in ["P", "A", "U", "Q", "R", "r", "s"]: continue if not result.has_key(code): result[code] = {} # Split network from the rest - columns = columns[1].split(',') + columns = columns[1].split(",") # Parse first line data network = columns[0] @@ -263,33 +266,33 @@ def ip_eigrp_topo(node): if column == columns[0]: continue - match = re.search(r'(\d+) successors', column) + match = re.search(r"(\d+) successors", column) if match is not None: - result[code][network]['successors'] = match.group(1) + result[code][network]["successors"] = match.group(1) continue - match = re.search(r'FD is (\d+)', column) + match = re.search(r"FD is (\d+)", column) if match is not None: - result[code][network]['fd'] = match.group(1) + result[code][network]["fd"] = match.group(1) continue - match = re.search(r'serno: (\d+)', column) + match = re.search(r"serno: (\d+)", column) if match is not None: - result[code][network]['serno'] = match.group(1) + result[code][network]["serno"] = match.group(1) continue # Parse second line data nextline = output[idx + 1] - columns = topotest.normalize_text(nextline).split(',') + columns = topotest.normalize_text(nextline).split(",") for column in columns: - match = re.search(r'via (.+)', column) + match = re.search(r"via (.+)", column) if match is not None: - result[code][network]['via'] = match.group(1) + result[code][network]["via"] = match.group(1) continue - match = re.search(r'(.+)', column) + match = re.search(r"(.+)", column) if match is not None: - result[code][network]['interface'] = match.group(1) + result[code][network]["interface"] = match.group(1) continue return result diff --git a/tests/topotests/evpn-pim-1/host1/bgpd.conf b/tests/topotests/evpn-pim-1/host1/bgpd.conf new file mode 100644 index 0000000000..cdf4cb4feb --- /dev/null +++ b/tests/topotests/evpn-pim-1/host1/bgpd.conf @@ -0,0 +1 @@ +! diff --git a/tests/topotests/evpn-pim-1/host1/pimd.conf b/tests/topotests/evpn-pim-1/host1/pimd.conf new file mode 100644 index 0000000000..63a44c1333 --- /dev/null +++ b/tests/topotests/evpn-pim-1/host1/pimd.conf @@ -0,0 +1,4 @@ +int lo +! + + diff --git a/tests/topotests/evpn-pim-1/host1/zebra.conf b/tests/topotests/evpn-pim-1/host1/zebra.conf new file mode 100644 index 0000000000..45ad031017 --- /dev/null +++ b/tests/topotests/evpn-pim-1/host1/zebra.conf @@ -0,0 +1,5 @@ +int host1-eth0 + ip addr 192.168.3.4/24 + +int lo + ip addr 192.168.100.4/32 diff --git a/tests/topotests/evpn-pim-1/host2/bgpd.conf b/tests/topotests/evpn-pim-1/host2/bgpd.conf new file mode 100644 index 0000000000..cdf4cb4feb --- /dev/null +++ b/tests/topotests/evpn-pim-1/host2/bgpd.conf @@ -0,0 +1 @@ +! diff --git a/tests/topotests/evpn-pim-1/host2/pimd.conf b/tests/topotests/evpn-pim-1/host2/pimd.conf new file mode 100644 index 0000000000..63a44c1333 --- /dev/null +++ b/tests/topotests/evpn-pim-1/host2/pimd.conf @@ -0,0 +1,4 @@ +int lo +! + + diff --git a/tests/topotests/evpn-pim-1/host2/zebra.conf b/tests/topotests/evpn-pim-1/host2/zebra.conf new file mode 100644 index 0000000000..bfae53017f --- /dev/null +++ b/tests/topotests/evpn-pim-1/host2/zebra.conf @@ -0,0 +1,5 @@ +int host-eth0 + ip addr 192.168.4.5/24 + +int lo + ip addr 192.168.100.5/32 diff --git a/tests/topotests/evpn-pim-1/leaf1/bgpd.conf b/tests/topotests/evpn-pim-1/leaf1/bgpd.conf new file mode 100644 index 0000000000..4dedfecd61 --- /dev/null +++ b/tests/topotests/evpn-pim-1/leaf1/bgpd.conf @@ -0,0 +1,10 @@ + +router bgp 65002 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external + redistribute connected + address-family l2vpn evpn + neighbor 192.168.1.1 activate + advertise-all-vni + ! +! diff --git a/tests/topotests/evpn-pim-1/leaf1/pimd.conf b/tests/topotests/evpn-pim-1/leaf1/pimd.conf new file mode 100644 index 0000000000..293e252086 --- /dev/null +++ b/tests/topotests/evpn-pim-1/leaf1/pimd.conf @@ -0,0 +1,15 @@ +debug pim events +debug pim nht +debug pim zebra +ip pim rp 192.168.100.1 +! +int lo + ip pim +! +int leaf1-eth0 + ip pim +! +int leaf1-eth1 + ip pim + ip igmp + diff --git a/tests/topotests/evpn-pim-1/leaf1/zebra.conf b/tests/topotests/evpn-pim-1/leaf1/zebra.conf new file mode 100644 index 0000000000..581cc6e7be --- /dev/null +++ b/tests/topotests/evpn-pim-1/leaf1/zebra.conf @@ -0,0 +1,6 @@ +int leaf1-eth0 + ip addr 192.168.1.2/24 +int leaf1-eth1 + ip addr 192.168.3.2/24 +int lo + ip addr 192.168.100.2/32 diff --git a/tests/topotests/evpn-pim-1/leaf2/bgpd.conf b/tests/topotests/evpn-pim-1/leaf2/bgpd.conf new file mode 100644 index 0000000000..5bc708240d --- /dev/null +++ b/tests/topotests/evpn-pim-1/leaf2/bgpd.conf @@ -0,0 +1,10 @@ + +router bgp 65003 + no bgp ebgp-requires-policy + neighbor 192.168.2.1 remote-as external + redistribute connected + address-family l2vpn evpn + neighbor 192.168.2.1 activate + advertise-all-vni + ! +! diff --git a/tests/topotests/evpn-pim-1/leaf2/pimd.conf b/tests/topotests/evpn-pim-1/leaf2/pimd.conf new file mode 100644 index 0000000000..08d5a19a2a --- /dev/null +++ b/tests/topotests/evpn-pim-1/leaf2/pimd.conf @@ -0,0 +1,13 @@ +ip pim rp 192.168.100.1 +! +int lo + ip pim +! +int leaf2-eth0 + ip pim +! +int leaf2-eth1 + ip pim + ip igmp +! + diff --git a/tests/topotests/evpn-pim-1/leaf2/zebra.conf b/tests/topotests/evpn-pim-1/leaf2/zebra.conf new file mode 100644 index 0000000000..1bcf8e1ded --- /dev/null +++ b/tests/topotests/evpn-pim-1/leaf2/zebra.conf @@ -0,0 +1,6 @@ +int leaf2-eth0 + ip addr 192.168.2.3/24 +int leaf2-eth1 + ip addr 192.168.4.3/24 +int lo + ip addr 192.168.100.3/32 diff --git a/tests/topotests/evpn-pim-1/spine/bgp.summ.json b/tests/topotests/evpn-pim-1/spine/bgp.summ.json new file mode 100644 index 0000000000..5ff4b096fd --- /dev/null +++ b/tests/topotests/evpn-pim-1/spine/bgp.summ.json @@ -0,0 +1,41 @@ +{ + "routerId":"192.168.100.1", + "as":65001, + "vrfName":"default", + "tableVersion":7, + "peerCount":2, + "peers":{ + "192.168.1.2":{ + "remoteAs":65002, + "version":4, + "tableVersion":0, + "outq":0, + "inq":0, + "pfxRcd":3, + "pfxSnt":7, + "state":"Established", + "connectionsEstablished":1, + "connectionsDropped":0, + "idType":"ipv4" + }, + "192.168.2.3":{ + "remoteAs":65003, + "version":4, + "tableVersion":0, + "outq":0, + "inq":0, + "pfxRcd":3, + "pfxSnt":7, + "state":"Established", + "connectionsEstablished":1, + "connectionsDropped":0, + "idType":"ipv4" + } + }, + "failedPeers":0, + "totalPeers":2, + "dynamicPeers":0, + "bestPath":{ + "multiPathRelax":"false" + } +} diff --git a/tests/topotests/evpn-pim-1/spine/bgpd.conf b/tests/topotests/evpn-pim-1/spine/bgpd.conf new file mode 100644 index 0000000000..16c17b29cc --- /dev/null +++ b/tests/topotests/evpn-pim-1/spine/bgpd.conf @@ -0,0 +1,11 @@ + +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.1.2 remote-as external + neighbor 192.168.2.3 remote-as external + redistribute connected + address-family l2vpn evpn + neighbor 192.168.1.2 activate + neighbor 192.168.2.3 activate + exit-address-family +! diff --git a/tests/topotests/evpn-pim-1/spine/join-info.json b/tests/topotests/evpn-pim-1/spine/join-info.json new file mode 100644 index 0000000000..3d135fb964 --- /dev/null +++ b/tests/topotests/evpn-pim-1/spine/join-info.json @@ -0,0 +1,34 @@ +{ + "spine-eth0":{ + "name":"spine-eth0", + "state":"up", + "address":"192.168.1.1", + "flagMulticast":true, + "flagBroadcast":true, + "lanDelayEnabled":true, + "239.1.1.1":{ + "*":{ + "source":"*", + "group":"239.1.1.1", + "prune":"--:--", + "channelJoinName":"JOIN" + } + } + }, + "spine-eth1":{ + "name":"spine-eth1", + "state":"up", + "address":"192.168.2.1", + "flagMulticast":true, + "flagBroadcast":true, + "lanDelayEnabled":true, + "239.1.1.1":{ + "*":{ + "source":"*", + "group":"239.1.1.1", + "prune":"--:--", + "channelJoinName":"JOIN" + } + } + } +} diff --git a/tests/topotests/evpn-pim-1/spine/pimd.conf b/tests/topotests/evpn-pim-1/spine/pimd.conf new file mode 100644 index 0000000000..56adda5cc4 --- /dev/null +++ b/tests/topotests/evpn-pim-1/spine/pimd.conf @@ -0,0 +1,13 @@ +ip pim rp 192.168.100.1 +! +int lo + ip pim +! +int spine-eth0 + ip pim +! +int spine-eth1 + ip pim +! + + diff --git a/tests/topotests/evpn-pim-1/spine/zebra.conf b/tests/topotests/evpn-pim-1/spine/zebra.conf new file mode 100644 index 0000000000..2cb719486e --- /dev/null +++ b/tests/topotests/evpn-pim-1/spine/zebra.conf @@ -0,0 +1,8 @@ +int spine-eth0 + ip addr 192.168.1.1/24 +! +int spine-eth1 + ip addr 192.168.2.1/24 +! +int lo + ip addr 192.168.100.1/32 diff --git a/tests/topotests/evpn-pim-1/test_evpn_pim_topo1.py b/tests/topotests/evpn-pim-1/test_evpn_pim_topo1.py new file mode 100755 index 0000000000..94bb91d49f --- /dev/null +++ b/tests/topotests/evpn-pim-1/test_evpn_pim_topo1.py @@ -0,0 +1,221 @@ +#!/usr/bin/env python + +# +# test_evpn-pim_topo1.py +# +# Copyright (c) 2017 by +# Cumulus Networks, Inc. +# Donald Sharp +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_evpn_pim_topo1.py: Testing evpn-pim + +""" + +import os +import re +import sys +import pytest +import json +from functools import partial + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + +##################################################### +## +## Network Topology Definition +## +##################################################### + + +class NetworkTopo(Topo): + "evpn-pim Topology 1" + + def build(self, **_opts): + "Build function" + + tgen = get_topogen(self) + + tgen.add_router("spine") + tgen.add_router("leaf1") + tgen.add_router("leaf2") + tgen.add_router("host1") + tgen.add_router("host2") + + # On main router + # First switch is for a dummy interface (for local network) + # spine-eth0 is connected to leaf1-eth0 + switch = tgen.add_switch("sw1") + switch.add_link(tgen.gears["spine"]) + switch.add_link(tgen.gears["leaf1"]) + + # spine-eth1 is connected to leaf2-eth0 + switch = tgen.add_switch("sw2") + switch.add_link(tgen.gears["spine"]) + switch.add_link(tgen.gears["leaf2"]) + + # leaf1-eth1 is connected to host1-eth0 + switch = tgen.add_switch("sw3") + switch.add_link(tgen.gears["leaf1"]) + switch.add_link(tgen.gears["host1"]) + + # leaf2-eth1 is connected to host2-eth0 + switch = tgen.add_switch("sw4") + switch.add_link(tgen.gears["leaf2"]) + switch.add_link(tgen.gears["host2"]) + + +##################################################### +## +## Tests starting +## +##################################################### + + +def setup_module(module): + "Setup topology" + tgen = Topogen(NetworkTopo, module.__name__) + tgen.start_topology() + + leaf1 = tgen.gears["leaf1"] + leaf2 = tgen.gears["leaf2"] + + leaf1.run("brctl addbr brleaf1") + leaf2.run("brctl addbr brleaf2") + leaf1.run("ip link set dev brleaf1 up") + leaf2.run("ip link set dev brleaf2 up") + leaf1.run( + "ip link add vxlan0 type vxlan id 42 group 239.1.1.1 dev leaf1-eth1 dstport 4789" + ) + leaf2.run( + "ip link add vxlan0 type vxlan id 42 group 239.1.1.1 dev leaf2-eth1 dstport 4789" + ) + leaf1.run("brctl addif brleaf1 vxlan0") + leaf2.run("brctl addif brleaf2 vxlan0") + leaf1.run("ip link set up dev vxlan0") + leaf2.run("ip link set up dev vxlan0") + # tgen.mininet_cli() + # This is a sample of configuration loading. + router_list = tgen.routers() + for rname, router in router_list.iteritems(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_PIM, os.path.join(CWD, "{}/pimd.conf".format(rname)) + ) + tgen.start_router() + # tgen.mininet_cli() + + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + + +def test_converge_protocols(): + "Wait for protocol convergence" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + spine = tgen.gears["spine"] + json_file = "{}/{}/bgp.summ.json".format(CWD, spine.name) + expected = json.loads(open(json_file).read()) + + test_func = partial( + topotest.router_json_cmp, spine, "show bgp ipv4 uni summ json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=125, wait=1) + assertmsg = '"{}" JSON output mismatches'.format(spine.name) + assert result is None, assertmsg + # tgen.mininet_cli() + + +def test_multicast_groups_on_rp(): + "Ensure the multicast groups show up on the spine" + # This test implicitly tests the auto mcast groups + # of the created vlans and then the auto-joins that + # pim will do to the RP( spine ) + + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + spine = tgen.gears["spine"] + json_file = "{}/{}/join-info.json".format(CWD, spine.name) + expected = json.loads(open(json_file).read()) + + test_func = partial( + topotest.router_json_cmp, spine, "show ip pim join json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assertmsg = '"{}" JSON output mismatches'.format(spine.name) + assert result is None, assertmsg + # tgen.mininet_cli() + + +def test_shutdown_check_stderr(): + if os.environ.get("TOPOTESTS_CHECK_STDERR") is None: + pytest.skip("Skipping test for Stderr output and memory leaks") + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Verifying unexpected STDERR output from daemons") + + router_list = tgen.routers().values() + for router in router_list: + router.stop() + + log = tgen.net[router.name].getStdErr("pimd") + if log: + logger.error("PIMd StdErr Log:" + log) + log = tgen.net[router.name].getStdErr("bgpd") + if log: + logger.error("BGPd StdErr Log:" + log) + log = tgen.net[router.name].getStdErr("zebra") + if log: + logger.error("Zebra StdErr Log:" + log) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/evpn_type5_test_topo1/__init__.py b/tests/topotests/evpn_type5_test_topo1/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/evpn_type5_test_topo1/evpn_type5_chaos_topo1.json b/tests/topotests/evpn_type5_test_topo1/evpn_type5_chaos_topo1.json new file mode 100644 index 0000000000..14842da326 --- /dev/null +++ b/tests/topotests/evpn_type5_test_topo1/evpn_type5_chaos_topo1.json @@ -0,0 +1,887 @@ +{ + "address_types": ["ipv4","ipv6"], + "ipv4base": "10.0.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 30, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:db8:f::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "e1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"} + }, + "vrfs":[ + { + "name": "RED", + "id": "1" + } + ], + "bgp": + [ + { + "local_as": "1", + "vrf": "RED", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "e1": { + "dest_link": { + "r1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "e1": { + "dest_link": { + "r1": {} + } + } + } + } + } + } + } + ], + "static_routes":[ + { + "network":"10.1.1.1/32", + "next_hop":"Null0", + "vrf": "RED" + }, + { + "network":"10::1/128", + "next_hop":"Null0", + "vrf": "RED" + } + ] + }, + "r2": { + "links": { + "e1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"}, + "e1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"} + }, + "vrfs":[ + { + "name": "BLUE", + "id": "1" + }, + { + "name": "GREEN", + "id": "2" + } + ], + "bgp": + [ + { + "local_as": "2", + "vrf": "BLUE", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "e1": { + "dest_link": { + "r2-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "e1": { + "dest_link": { + "r2-link1": {} + } + } + } + } + } + } + }, + { + "local_as": "2", + "vrf": "GREEN", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "e1": { + "dest_link": { + "r2-link2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "e1": { + "dest_link": { + "r2-link2": {} + } + } + } + } + } + } + } + ], + "static_routes":[ + { + "network":"20.1.1.1/32", + "next_hop":"Null0", + "vrf": "BLUE" + }, + { + "network":"20::1/128", + "next_hop":"Null0", + "vrf": "BLUE" + }, + { + "network":"30.1.1.1/32", + "next_hop":"Null0", + "vrf": "GREEN" + }, + { + "network":"30::1/128", + "next_hop":"Null0", + "vrf": "GREEN" + } + ] + }, + "e1": { + "links": { + "r1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"}, + "r2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"}, + "r2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"}, + "d1-link1": {"ipv4": "auto", "ipv6": "auto"}, + "d2-link1": {"ipv4": "auto", "ipv6": "auto"} + }, + "vrfs":[ + { + "name": "RED", + "id": "1", + "vni": 75100 + }, + { + "name": "BLUE", + "id": "2", + "vni": 75200 + }, + { + "name": "GREEN", + "id": "3", + "vni": 75300 + } + ], + "bgp": + [ + { + "local_as": "100", + "vrf": "RED", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "e1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "e1": {} + } + } + } + } + }, + "l2vpn": { + "evpn": { + "advertise": { + "ipv4": { + "unicast": {} + }, + "ipv6": { + "unicast": {} + } + } + } + } + } + }, + { + "local_as": "100", + "vrf": "BLUE", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "e1-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "e1-link1": {} + } + } + } + } + }, + "l2vpn": { + "evpn": { + "advertise": { + "ipv4": { + "unicast": {} + }, + "ipv6": { + "unicast": {} + } + } + } + } + } + }, + { + "local_as": "100", + "vrf": "GREEN", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "e1-link2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "e1-link2": {} + } + } + } + } + }, + "l2vpn": { + "evpn": { + "advertise": { + "ipv4": { + "unicast": {} + }, + "ipv6": { + "unicast": {} + } + } + } + } + } + }, + { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "d1": { + "dest_link": { + "e1-link1": { + "deactivate": "ipv4" + } + } + }, + "d2": { + "dest_link": { + "e1-link1": { + "deactivate": "ipv4" + } + } + } + } + } + }, + "l2vpn": { + "evpn": { + "neighbor": { + "d1": { + "ipv4":{ + "e1-link1": "activate" + } + }, + "d2": { + "ipv4":{ + "e1-link1": "activate" + } + } + }, + "advertise-all-vni": true + } + } + } + } + ] + }, + "d1": { + "links": { + "e1-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r3": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"}, + "r4-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"}, + "r4-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"} + }, + "vrfs":[ + { + "name": "RED", + "id": "1", + "vni": 75100 + }, + { + "name": "BLUE", + "id": "2", + "vni": 75200 + }, + { + "name": "GREEN", + "id": "3", + "vni": 75300 + } + ], + "bgp": + [ + { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "e1": { + "dest_link": { + "d1-link1": { + "deactivate": "ipv4" + } + } + } + } + } + }, + "l2vpn": { + "evpn": { + "neighbor": { + "e1": { + "ipv4":{ + "d1-link1": "activate" + } + } + }, + "advertise-all-vni": true + } + } + } + }, + { + "local_as": "100", + "vrf": "RED", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "d1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "d1": {} + } + } + } + } + }, + "l2vpn": { + "evpn": { + "advertise": { + "ipv4": { + "unicast": {} + }, + "ipv6": { + "unicast": {} + } + } + } + } + } + }, + { + "local_as": "100", + "vrf": "BLUE", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "d1-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "d1-link1": {} + } + } + } + } + }, + "l2vpn": { + "evpn": { + "advertise": { + "ipv4": { + "unicast": {} + }, + "ipv6": { + "unicast": {} + } + } + } + } + } + }, + { + "local_as": "100", + "vrf": "GREEN", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "d1-link2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "d1-link2": {} + } + } + } + } + }, + "l2vpn": { + "evpn": { + "advertise": { + "ipv4": { + "unicast": {} + }, + "ipv6": { + "unicast": {} + } + } + } + } + } + } + ] + }, + "d2": { + "links": { + "e1-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r3": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"}, + "r4-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"}, + "r4-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"} + }, + "vrfs":[ + { + "name": "RED", + "id": "1", + "vni": 75100 + }, + { + "name": "BLUE", + "id": "2", + "vni": 75200 + }, + { + "name": "GREEN", + "id": "3", + "vni": 75300 + } + ], + "bgp": + [ + { + "local_as": "200", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "e1": { + "dest_link": { + "d2-link1": { + "deactivate": "ipv4" + } + } + } + } + } + }, + "l2vpn": { + "evpn": { + "neighbor": { + "e1": { + "ipv4":{ + "d2-link1": "activate" + } + } + }, + "advertise-all-vni": true + } + } + } + }, + { + "local_as": "200", + "vrf": "RED", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "d2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "d2": {} + } + } + } + } + }, + "l2vpn": { + "evpn": { + "advertise": { + "ipv4": { + "unicast": {} + }, + "ipv6": { + "unicast": {} + } + } + } + } + } + }, + { + "local_as": "200", + "vrf": "BLUE", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "d2-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "d2-link1": {} + } + } + } + } + }, + "l2vpn": { + "evpn": { + "advertise": { + "ipv4": { + "unicast": {} + }, + "ipv6": { + "unicast": {} + } + } + } + } + } + }, + { + "local_as": "200", + "vrf": "GREEN", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "d2-link2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "d2-link2": {} + } + } + } + } + }, + "l2vpn": { + "evpn": { + "advertise": { + "ipv4": { + "unicast": {} + }, + "ipv6": { + "unicast": {} + } + } + } + } + } + } + ] + }, + "r3": { + "links": { + "d1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"}, + "d2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"} + }, + "vrfs":[ + { + "name": "RED", + "id": "1" + } + ], + "bgp": + [ + { + "local_as": "3", + "vrf": "RED", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "d1": { + "dest_link": { + "r3": {} + } + }, + "d2": { + "dest_link": { + "r3": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "d1": { + "dest_link": { + "r3": {} + } + }, + "d2": { + "dest_link": { + "r3": {} + } + } + } + } + } + } + } + ] + }, + "r4": { + "links": { + "d1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"}, + "d1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"}, + "d2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"}, + "d2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"} + }, + "vrfs":[ + { + "name": "BLUE", + "id": "1" + }, + { + "name": "GREEN", + "id": "2" + } + ], + "bgp": + [ + { + "local_as": "4", + "vrf": "BLUE", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "d1": { + "dest_link": { + "r4-link1": {} + } + }, + "d2": { + "dest_link": { + "r4-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "d1": { + "dest_link": { + "r4-link1": {} + } + }, + "d2": { + "dest_link": { + "r4-link1": {} + } + } + } + } + } + } + }, + { + "local_as": "4", + "vrf": "GREEN", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "d1": { + "dest_link": { + "r4-link2": {} + } + }, + "d2": { + "dest_link": { + "r4-link2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "d1": { + "dest_link": { + "r4-link2": {} + } + }, + "d2": { + "dest_link": { + "r4-link2": {} + } + } + } + } + } + } + } + ] + } + } +} diff --git a/tests/topotests/evpn_type5_test_topo1/evpn_type5_topo1.json b/tests/topotests/evpn_type5_test_topo1/evpn_type5_topo1.json new file mode 100644 index 0000000000..14842da326 --- /dev/null +++ b/tests/topotests/evpn_type5_test_topo1/evpn_type5_topo1.json @@ -0,0 +1,887 @@ +{ + "address_types": ["ipv4","ipv6"], + "ipv4base": "10.0.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 30, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:db8:f::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "e1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"} + }, + "vrfs":[ + { + "name": "RED", + "id": "1" + } + ], + "bgp": + [ + { + "local_as": "1", + "vrf": "RED", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "e1": { + "dest_link": { + "r1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "e1": { + "dest_link": { + "r1": {} + } + } + } + } + } + } + } + ], + "static_routes":[ + { + "network":"10.1.1.1/32", + "next_hop":"Null0", + "vrf": "RED" + }, + { + "network":"10::1/128", + "next_hop":"Null0", + "vrf": "RED" + } + ] + }, + "r2": { + "links": { + "e1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"}, + "e1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"} + }, + "vrfs":[ + { + "name": "BLUE", + "id": "1" + }, + { + "name": "GREEN", + "id": "2" + } + ], + "bgp": + [ + { + "local_as": "2", + "vrf": "BLUE", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "e1": { + "dest_link": { + "r2-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "e1": { + "dest_link": { + "r2-link1": {} + } + } + } + } + } + } + }, + { + "local_as": "2", + "vrf": "GREEN", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "e1": { + "dest_link": { + "r2-link2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "e1": { + "dest_link": { + "r2-link2": {} + } + } + } + } + } + } + } + ], + "static_routes":[ + { + "network":"20.1.1.1/32", + "next_hop":"Null0", + "vrf": "BLUE" + }, + { + "network":"20::1/128", + "next_hop":"Null0", + "vrf": "BLUE" + }, + { + "network":"30.1.1.1/32", + "next_hop":"Null0", + "vrf": "GREEN" + }, + { + "network":"30::1/128", + "next_hop":"Null0", + "vrf": "GREEN" + } + ] + }, + "e1": { + "links": { + "r1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"}, + "r2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"}, + "r2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"}, + "d1-link1": {"ipv4": "auto", "ipv6": "auto"}, + "d2-link1": {"ipv4": "auto", "ipv6": "auto"} + }, + "vrfs":[ + { + "name": "RED", + "id": "1", + "vni": 75100 + }, + { + "name": "BLUE", + "id": "2", + "vni": 75200 + }, + { + "name": "GREEN", + "id": "3", + "vni": 75300 + } + ], + "bgp": + [ + { + "local_as": "100", + "vrf": "RED", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "e1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "e1": {} + } + } + } + } + }, + "l2vpn": { + "evpn": { + "advertise": { + "ipv4": { + "unicast": {} + }, + "ipv6": { + "unicast": {} + } + } + } + } + } + }, + { + "local_as": "100", + "vrf": "BLUE", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "e1-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "e1-link1": {} + } + } + } + } + }, + "l2vpn": { + "evpn": { + "advertise": { + "ipv4": { + "unicast": {} + }, + "ipv6": { + "unicast": {} + } + } + } + } + } + }, + { + "local_as": "100", + "vrf": "GREEN", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "e1-link2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "e1-link2": {} + } + } + } + } + }, + "l2vpn": { + "evpn": { + "advertise": { + "ipv4": { + "unicast": {} + }, + "ipv6": { + "unicast": {} + } + } + } + } + } + }, + { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "d1": { + "dest_link": { + "e1-link1": { + "deactivate": "ipv4" + } + } + }, + "d2": { + "dest_link": { + "e1-link1": { + "deactivate": "ipv4" + } + } + } + } + } + }, + "l2vpn": { + "evpn": { + "neighbor": { + "d1": { + "ipv4":{ + "e1-link1": "activate" + } + }, + "d2": { + "ipv4":{ + "e1-link1": "activate" + } + } + }, + "advertise-all-vni": true + } + } + } + } + ] + }, + "d1": { + "links": { + "e1-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r3": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"}, + "r4-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"}, + "r4-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"} + }, + "vrfs":[ + { + "name": "RED", + "id": "1", + "vni": 75100 + }, + { + "name": "BLUE", + "id": "2", + "vni": 75200 + }, + { + "name": "GREEN", + "id": "3", + "vni": 75300 + } + ], + "bgp": + [ + { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "e1": { + "dest_link": { + "d1-link1": { + "deactivate": "ipv4" + } + } + } + } + } + }, + "l2vpn": { + "evpn": { + "neighbor": { + "e1": { + "ipv4":{ + "d1-link1": "activate" + } + } + }, + "advertise-all-vni": true + } + } + } + }, + { + "local_as": "100", + "vrf": "RED", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "d1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "d1": {} + } + } + } + } + }, + "l2vpn": { + "evpn": { + "advertise": { + "ipv4": { + "unicast": {} + }, + "ipv6": { + "unicast": {} + } + } + } + } + } + }, + { + "local_as": "100", + "vrf": "BLUE", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "d1-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "d1-link1": {} + } + } + } + } + }, + "l2vpn": { + "evpn": { + "advertise": { + "ipv4": { + "unicast": {} + }, + "ipv6": { + "unicast": {} + } + } + } + } + } + }, + { + "local_as": "100", + "vrf": "GREEN", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "d1-link2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "d1-link2": {} + } + } + } + } + }, + "l2vpn": { + "evpn": { + "advertise": { + "ipv4": { + "unicast": {} + }, + "ipv6": { + "unicast": {} + } + } + } + } + } + } + ] + }, + "d2": { + "links": { + "e1-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r3": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"}, + "r4-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"}, + "r4-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"} + }, + "vrfs":[ + { + "name": "RED", + "id": "1", + "vni": 75100 + }, + { + "name": "BLUE", + "id": "2", + "vni": 75200 + }, + { + "name": "GREEN", + "id": "3", + "vni": 75300 + } + ], + "bgp": + [ + { + "local_as": "200", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "e1": { + "dest_link": { + "d2-link1": { + "deactivate": "ipv4" + } + } + } + } + } + }, + "l2vpn": { + "evpn": { + "neighbor": { + "e1": { + "ipv4":{ + "d2-link1": "activate" + } + } + }, + "advertise-all-vni": true + } + } + } + }, + { + "local_as": "200", + "vrf": "RED", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "d2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "d2": {} + } + } + } + } + }, + "l2vpn": { + "evpn": { + "advertise": { + "ipv4": { + "unicast": {} + }, + "ipv6": { + "unicast": {} + } + } + } + } + } + }, + { + "local_as": "200", + "vrf": "BLUE", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "d2-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "d2-link1": {} + } + } + } + } + }, + "l2vpn": { + "evpn": { + "advertise": { + "ipv4": { + "unicast": {} + }, + "ipv6": { + "unicast": {} + } + } + } + } + } + }, + { + "local_as": "200", + "vrf": "GREEN", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "d2-link2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "d2-link2": {} + } + } + } + } + }, + "l2vpn": { + "evpn": { + "advertise": { + "ipv4": { + "unicast": {} + }, + "ipv6": { + "unicast": {} + } + } + } + } + } + } + ] + }, + "r3": { + "links": { + "d1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"}, + "d2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"} + }, + "vrfs":[ + { + "name": "RED", + "id": "1" + } + ], + "bgp": + [ + { + "local_as": "3", + "vrf": "RED", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "d1": { + "dest_link": { + "r3": {} + } + }, + "d2": { + "dest_link": { + "r3": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "d1": { + "dest_link": { + "r3": {} + } + }, + "d2": { + "dest_link": { + "r3": {} + } + } + } + } + } + } + } + ] + }, + "r4": { + "links": { + "d1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"}, + "d1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"}, + "d2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"}, + "d2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"} + }, + "vrfs":[ + { + "name": "BLUE", + "id": "1" + }, + { + "name": "GREEN", + "id": "2" + } + ], + "bgp": + [ + { + "local_as": "4", + "vrf": "BLUE", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "d1": { + "dest_link": { + "r4-link1": {} + } + }, + "d2": { + "dest_link": { + "r4-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "d1": { + "dest_link": { + "r4-link1": {} + } + }, + "d2": { + "dest_link": { + "r4-link1": {} + } + } + } + } + } + } + }, + { + "local_as": "4", + "vrf": "GREEN", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "d1": { + "dest_link": { + "r4-link2": {} + } + }, + "d2": { + "dest_link": { + "r4-link2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "d1": { + "dest_link": { + "r4-link2": {} + } + }, + "d2": { + "dest_link": { + "r4-link2": {} + } + } + } + } + } + } + } + ] + } + } +} 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 new file mode 100755 index 0000000000..e913105e43 --- /dev/null +++ b/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_chaos_topo1.py @@ -0,0 +1,1046 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +Following tests are covered to test EVPN-Type5 functionality: +1. In absence of an overlay index all IP-Prefixes(RT-5) + are advertised with default values for below parameters: + --> Ethernet Tag ID = GW IP address = ESI=0 +2. EVPN CLI output and JSON format validation. +3. RT verification(auto) +""" + +import os +import re +import sys +import json +import time +import pytest +import platform +from copy import deepcopy +from time import sleep + + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# Required to instantiate the topology builder class. + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topotest import version_cmp +from lib.topogen import Topogen, get_topogen +from mininet.topo import Topo + +from lib.common_config import ( + start_topology, + write_test_header, + check_address_types, + write_test_footer, + reset_config_on_routers, + verify_rib, + step, + start_router_daemons, + create_static_routes, + create_vrf_cfg, + create_route_maps, + create_interface_in_kernel, + check_router_status, + configure_vxlan, + configure_brctl, + apply_raw_config, + verify_vrf_vni, + verify_cli_json +) + +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + create_router_bgp, + clear_bgp, + verify_best_path_as_per_bgp_attribute, + verify_attributes_for_evpn_routes, + verify_evpn_routes +) +from lib.topojson import build_topo_from_json, build_config_from_json + +# Reading the data from JSON File for topology creation +jsonFile = "{}/evpn_type5_chaos_topo1.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +# Reading the data from JSON File for topology creation +# Global variables +TCPDUMP_FILE = "evpn_log.txt" +LOGDIR = "/tmp/topotests/" +NETWORK1_1 = {"ipv4": "10.1.1.1/32", "ipv6": "10::1/128"} +NETWORK1_2 = {"ipv4": "40.1.1.1/32", "ipv6": "40::1/128"} +NETWORK1_3 = {"ipv4": "40.1.1.2/32", "ipv6": "40::2/128"} +NETWORK1_4 = {"ipv4": "40.1.1.3/32", "ipv6": "40::3/128"} +NETWORK2_1 = {"ipv4": "20.1.1.1/32", "ipv6": "20::1/128"} +NETWORK3_1 = {"ipv4": "30.1.1.1/32", "ipv6": "30::1/128"} +NETWORK4_1 = {"ipv4": "100.1.1.1/32 ", "ipv6": "100::100/128"} +NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"} +VNI_1 = 75100 +VNI_2 = 75200 +VNI_3 = 75300 +MAC_1 = "00:80:48:ba:d1:00" +MAC_2 = "00:80:48:ba:d1:01" +MAC_3 = "00:80:48:ba:d1:02" +BRCTL_1 = "br100" +BRCTL_2 = "br200" +BRCTL_3 = "br300" +VXLAN_1 = "vxlan75100" +VXLAN_2 = "vxlan75200" +VXLAN_3 = "vxlan75300" +BRIDGE_INTF1 = "120.0.0.1" +BRIDGE_INTF2 = "120.0.0.2" +BRIDGE_INTF3 = "120.0.0.3" +MULTICAST_MAC1 = "01:00:5e:00:52:02" + +VXLAN = { + "vxlan_name": [VXLAN_1, VXLAN_2, VXLAN_3], + "vxlan_id": [75100, 75200, 75300], + "dstport": 4789, + "local_addr": {"e1": BRIDGE_INTF1, "d1": BRIDGE_INTF2, "d2": BRIDGE_INTF3}, + "learning": "no", +} +BRCTL = { + "brctl_name": [BRCTL_1, BRCTL_2, BRCTL_3], + "addvxlan": [VXLAN_1, VXLAN_2, VXLAN_3], + "vrf": ["RED", "BLUE", "GREEN"], + "stp": [0, 0, 0], +} + + +class CreateTopo(Topo): + """ + Test BasicTopo - topology 1 + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + """Build function""" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + global topo + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(CreateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + if version_cmp(platform.release(), '4.19') < 0: + error_msg = ('EVPN tests will not run (have kernel "{}", ' + 'but it requires >= 4.19)'.format(platform.release())) + pytest.skip(error_msg) + + global BGP_CONVERGENCE + global ADDR_TYPES + ADDR_TYPES = check_address_types() + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + logger.info("Pre-requisite config for testsuite") + prerequisite_config_for_test_suite(tgen) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# +# Testcases +# +##################################################### + + +def prerequisite_config_for_test_suite(tgen): + """ + API to do prerequisite config for testsuite + + parameters: + ----------- + * `tgen`: topogen object + """ + + step("Configure vxlan, bridge interface") + for dut in ["e1", "d1", "d2"]: + step("[DUT: ]Configure vxlan") + vxlan_input = { + dut: { + "vxlan": [ + { + "vxlan_name": VXLAN["vxlan_name"], + "vxlan_id": VXLAN["vxlan_id"], + "dstport": VXLAN["dstport"], + "local_addr": VXLAN["local_addr"][dut], + "learning": VXLAN["learning"], + } + ] + } + } + + result = configure_vxlan(tgen, vxlan_input) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure bridge interface") + brctl_input = { + dut: { + "brctl": [ + { + "brctl_name": BRCTL["brctl_name"], + "addvxlan": BRCTL["addvxlan"], + "vrf": BRCTL["vrf"], + "stp": BRCTL["stp"], + } + ] + } + } + result = configure_brctl(tgen, topo, brctl_input) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure default routes") + add_default_routes(tgen) + + +def add_default_routes(tgen): + """ + API to do prerequisite config for testsuite + + parameters: + ----------- + * `tgen`: topogen object + """ + + step("Add default routes..") + + default_routes = { + "e1": { + "static_routes": [ + { + "network": "{}/32".format(VXLAN["local_addr"]["d1"]), + "next_hop": topo["routers"]["d1"]["links"]["e1-link1"][ + "ipv4" + ].split("/")[0], + }, + { + "network": "{}/32".format(VXLAN["local_addr"]["d2"]), + "next_hop": topo["routers"]["d2"]["links"]["e1-link1"][ + "ipv4" + ].split("/")[0], + }, + ] + }, + "d1": { + "static_routes": [ + { + "network": "{}/32".format(VXLAN["local_addr"]["e1"]), + "next_hop": topo["routers"]["e1"]["links"]["d1-link1"][ + "ipv4" + ].split("/")[0], + }, + { + "network": "{}/32".format(VXLAN["local_addr"]["d2"]), + "next_hop": topo["routers"]["e1"]["links"]["d1-link1"][ + "ipv4" + ].split("/")[0], + }, + ] + }, + "d2": { + "static_routes": [ + { + "network": "{}/32".format(VXLAN["local_addr"]["d1"]), + "next_hop": topo["routers"]["e1"]["links"]["d2-link1"][ + "ipv4" + ].split("/")[0], + }, + { + "network": "{}/32".format(VXLAN["local_addr"]["e1"]), + "next_hop": topo["routers"]["e1"]["links"]["d2-link1"][ + "ipv4" + ].split("/")[0], + }, + ] + }, + } + + result = create_static_routes(tgen, default_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + +def test_verify_overlay_index_p1(request): + """ + In absence of an overlay index all IP-Prefixes(RT-5) + are advertised with default values for below parameters: + --> Ethernet Tag ID = GW IP address = ESI=0 + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + check_router_status(tgen) + reset_config_on_routers(tgen) + add_default_routes(tgen) + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Following steps are taken care in base config:") + step( + "Configure BGP neighborship for both address families" + "(IPv4 & IPv6) between Edge-1 and VFN routers(R1 and R2)" + ) + step( + "Advertise prefixes from VNF routers R1 and R2 in associated " + "VRFs for both address-family." + ) + step("Advertise VRF routes as in EVPN address family from Edge-1 " "router.") + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "r1": { + "static_routes": [ + { + "network": NETWORK1_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED", + } + ] + }, + "r2": { + "static_routes": [ + { + "network": NETWORK2_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE", + }, + { + "network": NETWORK3_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "GREEN", + } + ] + } + } + + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify: Prefixes are received in all VRFs on Edge-1 router.") + + for addr_type in ADDR_TYPES: + input_routes = {key: topo["routers"][key] for key in ["r1"]} + result = verify_rib(tgen, addr_type, "e1", input_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + input_routes = {key: topo["routers"][key] for key in ["r2"]} + result = verify_rib(tgen, addr_type, "e1", input_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that EVPN routes, received on DCG-1 and DCG-2 do not " + "carry any overlay index and these indexes are set to default " + "value=0. " + ) + + for addr_type in ADDR_TYPES: + input_routes = {key: topo["routers"][key] for key in ["r1"]} + + result = verify_attributes_for_evpn_routes( + tgen, topo, "d1", input_routes, ethTag=0 + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_attributes_for_evpn_routes( + tgen, topo, "d2", input_routes, ethTag=0 + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_evpn_cli_json_available_p1(request): + """ + EVPN CLI output and JSON format validation. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + check_router_status(tgen) + reset_config_on_routers(tgen) + add_default_routes(tgen) + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Need to verify below CLIs and associated JSON format " "outputs:") + + input_dict = { + "e1": { + "cli": [ + "show evpn vni detail", + "show bgp l2vpn evpn all overlay", + "show bgp l2vpn evpn vni" + ] + } + } + + result = verify_cli_json(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_RT_verification_auto_p0(request): + """ + RT verification(auto) + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + check_router_status(tgen) + reset_config_on_routers(tgen) + add_default_routes(tgen) + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step( + "Advertise overlapping prefixes from VNFs R1 and R2 in all VRFs " + "RED, GREEN and BLUE 100.1.1.1/32 and 100::100/128" + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "r1": { + "static_routes": [ + { + "network": NETWORK4_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED", + } + ] + }, + "r2": { + "static_routes": [ + { + "network": NETWORK4_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE", + }, + { + "network": NETWORK4_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "GREEN", + } + ] + } + } + + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that Edge-1 receives same prefixes in all 3 VRFs via " + "corresponding next-hop in associated VRF sh bgp vrf all" + ) + + for addr_type in ADDR_TYPES: + input_routes = { + "r1": { + "static_routes": [ + { + "network": NETWORK4_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED", + } + ] + }, + "r2": { + "static_routes": [ + { + "network": NETWORK4_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE", + }, + { + "network": NETWORK4_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "GREEN", + }, + ] + }, + } + + result = verify_rib(tgen, addr_type, "e1", input_routes) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Configure 4-byte local AS number on Edge-1 and establish EVPN " + "neighborship with DCG-1 & DCG-2." + ) + + topo_local = deepcopy(topo) + + step("Delete BGP config for vrf RED.") + + input_dict_vni = { + "e1": { + "vrfs": [ + {"name": "RED", "no_vni": VNI_1}, + {"name": "BLUE", "no_vni": VNI_2}, + {"name": "GREEN", "no_vni": VNI_3}, + ] + } + } + result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + input_dict_2 = {} + for dut in ["e1"]: + temp = {dut: {"bgp": []}} + input_dict_2.update(temp) + + INDEX = [0, 1, 2, 3] + VRFS = ["RED", "BLUE", "GREEN", None] + AS_NUM = [100, 100, 100, 100] + + for index, vrf, as_num in zip(INDEX, VRFS, AS_NUM): + topo_local["routers"][dut]["bgp"][index]["local_as"] = 4294967293 + if vrf: + temp[dut]["bgp"].append( + {"local_as": as_num, "vrf": vrf, "delete": True} + ) + else: + temp[dut]["bgp"].append({"local_as": as_num, "delete": True}) + + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + result = create_router_bgp(tgen, topo_local["routers"]) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + input_dict_vni = { + "e1": { + "vrfs": [ + {"name": "RED", "vni": VNI_1}, + {"name": "BLUE", "vni": VNI_2}, + {"name": "GREEN", "vni": VNI_3}, + ] + } + } + result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that all overlapping prefixes across different VRFs are " + "advertised in EVPN with unique RD value(auto derived)." + ) + step( + "Verify that FRR uses only the lower 2 bytes of ASN+VNI for auto " + "derived RT value." + ) + + for addr_type in ADDR_TYPES: + input_routes_1 = { + "r1": {"static_routes": [{"network": NETWORK4_1[addr_type], "vrf": "RED"}]} + } + input_routes_2 = { + "r2": {"static_routes": [{"network": NETWORK4_1[addr_type], "vrf": "BLUE"}]} + } + input_routes_3 = { + "r2": { + "static_routes": [{"network": NETWORK4_1[addr_type], "vrf": "GREEN"}] + } + } + + result = verify_attributes_for_evpn_routes( + tgen, topo, "e1", input_routes_1, rd="auto", rd_peer="e1" + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_attributes_for_evpn_routes( + tgen, topo, "e1", input_routes_1, rt="auto", rt_peer="e1" + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_attributes_for_evpn_routes( + tgen, topo, "e1", input_routes_2, rd="auto", rd_peer="e1" + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_attributes_for_evpn_routes( + tgen, topo, "e1", input_routes_2, rt="auto", rt_peer="e1" + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_attributes_for_evpn_routes( + tgen, topo, "e1", input_routes_3, rd="auto", rd_peer="e1" + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_attributes_for_evpn_routes( + tgen, topo, "e1", input_routes_3, rt="auto", rt_peer="e1" + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that DCG-1(iBGP peer) automatically imports the prefixes" + " from EVPN address-family to respective VRFs." + ) + step( + "Verify if DCG-2(eBGP peer) automatically imports the prefixes " + "from EVPN address-family to respective VRFs or not." + ) + + for addr_type in ADDR_TYPES: + input_routes = { + "r1": { + "static_routes": [ + { + "network": NETWORK4_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED", + } + ] + }, + "r2": { + "static_routes": [ + { + "network": NETWORK4_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE", + }, + { + "network": NETWORK4_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "GREEN", + }, + ] + }, + } + + result = verify_rib(tgen, addr_type, "d1", input_routes) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "d2", input_routes) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Change the VNI number for all 3 VRFs on Edge-1 as:" + "RED : 75400, GREEN: 75500, BLUE: 75600" + ) + + input_dict_vni = { + "e1": { + "vrfs": [ + {"name": "RED", "no_vni": VNI_1}, + {"name": "BLUE", "no_vni": VNI_2}, + {"name": "GREEN", "no_vni": VNI_3}, + ] + } + } + result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + input_dict_vni = { + "e1": { + "vrfs": [ + {"name": "RED", "vni": 75400}, + {"name": "BLUE", "vni": 75500}, + {"name": "GREEN", "vni": 75600}, + ] + } + } + result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Delete configured vxlan") + dut = "e1" + vxlan_input = { + dut: { + "vxlan": [ + { + "vxlan_name": VXLAN["vxlan_name"], + "vxlan_id": VXLAN["vxlan_id"], + "dstport": VXLAN["dstport"], + "local_addr": VXLAN["local_addr"][dut], + "learning": VXLAN["learning"], + "delete": True, + } + ] + } + } + + result = configure_vxlan(tgen, vxlan_input) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Configured vxlan") + VXLAN["vxlan_id"] = [75400, 75500, 75600] + vxlan_input = { + dut: { + "vxlan": [ + { + "vxlan_name": VXLAN["vxlan_name"], + "vxlan_id": VXLAN["vxlan_id"], + "dstport": VXLAN["dstport"], + "local_addr": VXLAN["local_addr"][dut], + "learning": VXLAN["learning"], + } + ] + } + } + + result = configure_vxlan(tgen, vxlan_input) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Configure bridge interface") + brctl_input = { + dut: { + "brctl": [ + { + "brctl_name": BRCTL["brctl_name"], + "addvxlan": BRCTL["addvxlan"], + "vrf": BRCTL["vrf"], + "stp": BRCTL["stp"], + } + ] + } + } + result = configure_brctl(tgen, topo, brctl_input) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify on Edge-1 that auto derived RT value has changed for " + "each VRF based on VNI number.." + ) + + input_dict = { + "e1": { + "vrfs": [ + {"RED": {"vni": 75400}}, + {"BLUE": {"vni": 75500}}, + {"GREEN": {"vni": 75600}}, + ] + } + } + + result = verify_vrf_vni(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify on Edge-1 that auto derived RT value has changed for " + "each VRF based on VNI number." + ) + + for addr_type in ADDR_TYPES: + input_routes = { + "r1": {"static_routes": [{"network": NETWORK4_1[addr_type], "vrf": "RED"}]} + } + + result = verify_attributes_for_evpn_routes( + tgen, topo, "e1", input_routes, rt="auto", rt_peer="e1" + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify on DCG-2 that prefixes are not imported from EVPN " + "address-family to VRFs as RT values are different on sending(" + "edge-1) and receiving(DCG-2) end." + ) + + for addr_type in ADDR_TYPES: + input_routes = { + "r1": {"static_routes": [{"network": NETWORK4_1[addr_type], "vrf": "RED"}]} + } + + result = verify_rib(tgen, addr_type, "d2", input_routes, expected=False) + assert result is not True, "Testcase {} :Failed \n " + "Routes are still present: {}".format(tc_name, result) + logger.info("Expected Behavior: {}".format(result)) + + step( + "Revert back to original VNI number for all 3 VRFs on Edge-1 " + "as: RED : 75100, GREEN: 75200, BLUE: 75300" + ) + + input_dict_vni = { + "e1": { + "vrfs": [ + {"name": "RED", "no_vni": 75400}, + {"name": "BLUE", "no_vni": 75500}, + {"name": "GREEN", "no_vni": 75600}, + ] + } + } + result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + input_dict_vni = { + "e1": { + "vrfs": [ + {"name": "RED", "vni": VNI_1}, + {"name": "BLUE", "vni": VNI_2}, + {"name": "GREEN", "vni": VNI_3}, + ] + } + } + result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Delete configured vxlan") + dut = "e1" + vxlan_input = { + dut: { + "vxlan": [ + { + "vxlan_name": VXLAN["vxlan_name"], + "vxlan_id": VXLAN["vxlan_id"], + "dstport": VXLAN["dstport"], + "local_addr": VXLAN["local_addr"][dut], + "learning": VXLAN["learning"], + "delete": True, + } + ] + } + } + result = configure_vxlan(tgen, vxlan_input) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Configured vxlan") + VXLAN["vxlan_id"] = [75100, 75200, 75300] + vxlan_input = { + dut: { + "vxlan": [ + { + "vxlan_name": VXLAN["vxlan_name"], + "vxlan_id": VXLAN["vxlan_id"], + "dstport": VXLAN["dstport"], + "local_addr": VXLAN["local_addr"][dut], + "learning": VXLAN["learning"], + } + ] + } + } + result = configure_vxlan(tgen, vxlan_input) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Configure bridge interface") + brctl_input = { + dut: { + "brctl": [ + { + "brctl_name": BRCTL["brctl_name"], + "addvxlan": BRCTL["addvxlan"], + "vrf": BRCTL["vrf"], + "stp": BRCTL["stp"], + } + ] + } + } + result = configure_brctl(tgen, topo, brctl_input) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify on Edge-1 that auto derived RT value has changed for " + "each VRF based on VNI number." + ) + step( + "Verify that DCG-1(iBGP peer) automatically imports the prefixes" + " from EVPN address-family to respective VRFs." + ) + + for addr_type in ADDR_TYPES: + input_routes = { + "r1": {"static_routes": [{"network": NETWORK4_1[addr_type], "vrf": "RED"}]} + } + + result = verify_attributes_for_evpn_routes( + tgen, topo, "e1", input_routes, rt="auto", rt_peer="e1" + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "d1", input_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Test with smaller VNI numbers (1-75000)") + + input_dict_vni = {"e1": {"vrfs": [{"name": "RED", "no_vni": VNI_1}]}} + result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + input_dict_vni = {"e1": {"vrfs": [{"name": "RED", "vni": 111}]}} + result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that DCG-2 receives EVPN prefixes along with auto " + "derived RT values(based on smaller VNI numbers)" + ) + + for addr_type in ADDR_TYPES: + input_routes_1 = { + "r1": {"static_routes": [{"network": NETWORK4_1[addr_type], "vrf": "RED"}]} + } + + result = verify_attributes_for_evpn_routes( + tgen, topo, "d2", input_routes_1, rt="auto", rt_peer="e1", expected=False + ) + assert result is not True, "Testcase {} :Failed \n " + "Malfaromed Auto-RT value accepted: {}".format(tc_name, result) + logger.info("Expected Behavior: {}".format(result)) + + step("Configure VNI number more than boundary limit (16777215)") + + input_dict_vni = {"e1": {"vrfs": [{"name": "RED", "no_vni": 111}]}} + result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + input_dict_vni = {"e1": {"vrfs": [{"name": "RED", "vni": 16777215}]}} + result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("CLI error for malformed VNI.") + input_dict = { + "e1": { + "vrfs": [{"RED": {"vni": 16777215, "routerMac": "None", "state": "Down"}}] + } + } + + result = verify_vrf_vni(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + input_routes_1 = { + "r1": {"static_routes": [{"network": NETWORK4_1[addr_type], "vrf": "RED"}]} + } + + result = verify_attributes_for_evpn_routes( + tgen, topo, "d2", input_routes_1, rt="auto", rt_peer="e1", expected=False + ) + assert result is not True, "Testcase {} :Failed \n " + "Malfaromed Auto-RT value accepted: {}".format(tc_name, result) + logger.info("Expected Behavior: {}".format(result)) + + step("Un-configure VNI number more than boundary limit (16777215)") + + input_dict_vni = {"e1": {"vrfs": [{"name": "RED", "no_vni": 16777215}]}} + result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_topo1.py b/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_topo1.py new file mode 100755 index 0000000000..9e385823fc --- /dev/null +++ b/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_topo1.py @@ -0,0 +1,2116 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +Following tests are covered to test 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. +4. EVPN routes are advertised/withdrawn, based on VNFs + advertising/withdrawing IP prefixes. +5. Route-map operations for EVPN address family. +6. BGP attributes for EVPN address-family. +""" + +import os +import re +import sys +import json +import time +import pytest +import platform +from copy import deepcopy +from time import sleep + + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# Required to instantiate the topology builder class. + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topotest import version_cmp +from lib.topogen import Topogen, get_topogen +from mininet.topo import Topo + +from lib.common_config import ( + start_topology, + write_test_header, + check_address_types, + write_test_footer, + reset_config_on_routers, + verify_rib, + step, + create_route_maps, + verify_cli_json, + start_router_daemons, + create_static_routes, + stop_router, + start_router, + create_vrf_cfg, + check_router_status, + apply_raw_config, + configure_vxlan, + configure_brctl, + verify_vrf_vni, + create_interface_in_kernel +) + +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + create_router_bgp, + clear_bgp, + verify_best_path_as_per_bgp_attribute, + verify_attributes_for_evpn_routes, + verify_evpn_routes +) +from lib.topojson import build_topo_from_json, build_config_from_json + +# Reading the data from JSON File for topology creation +jsonFile = "{}/evpn_type5_topo1.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +# Global variables +NETWORK1_1 = {"ipv4": "10.1.1.1/32", "ipv6": "10::1/128"} +NETWORK1_2 = {"ipv4": "40.1.1.1/32", "ipv6": "40::1/128"} +NETWORK1_3 = {"ipv4": "40.1.1.2/32", "ipv6": "40::2/128"} +NETWORK1_4 = {"ipv4": "40.1.1.3/32", "ipv6": "40::3/128"} +NETWORK2_1 = {"ipv4": "20.1.1.1/32", "ipv6": "20::1/128"} +NETWORK3_1 = {"ipv4": "30.1.1.1/32", "ipv6": "30::1/128"} +NETWORK4_1 = {"ipv4": "100.1.1.1/32 ", "ipv6": "100::100/128"} +NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"} +VNI_1 = 75100 +VNI_2 = 75200 +VNI_3 = 75300 +MAC_1 = "00:80:48:ba:d1:00" +MAC_2 = "00:80:48:ba:d1:01" +MAC_3 = "00:80:48:ba:d1:02" +BRCTL_1 = "br100" +BRCTL_2 = "br200" +BRCTL_3 = "br300" +VXLAN_1 = "vxlan75100" +VXLAN_2 = "vxlan75200" +VXLAN_3 = "vxlan75300" +BRIDGE_INTF1 = "120.0.0.1" +BRIDGE_INTF2 = "120.0.0.2" +BRIDGE_INTF3 = "120.0.0.3" + +VXLAN = { + "vxlan_name": [VXLAN_1, VXLAN_2, VXLAN_3], + "vxlan_id": [75100, 75200, 75300], + "dstport": 4789, + "local_addr": {"e1": BRIDGE_INTF1, "d1": BRIDGE_INTF2, "d2": BRIDGE_INTF3}, + "learning": "no", +} +BRCTL = { + "brctl_name": [BRCTL_1, BRCTL_2, BRCTL_3], + "addvxlan": [VXLAN_1, VXLAN_2, VXLAN_3], + "vrf": ["RED", "BLUE", "GREEN"], + "stp": [0, 0, 0], +} + + +class CreateTopo(Topo): + """ + Test BasicTopo - topology 1 + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + """Build function""" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + global topo + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(CreateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + if version_cmp(platform.release(), '4.19') < 0: + error_msg = ('EVPN tests will not run (have kernel "{}", ' + 'but it requires >= 4.19)'.format(platform.release())) + pytest.skip(error_msg) + + global BGP_CONVERGENCE + global ADDR_TYPES + ADDR_TYPES = check_address_types() + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + logger.info("Pre-requisite config for testsuite") + prerequisite_config_for_test_suite(tgen) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# +# Testcases +# +##################################################### + + +def prerequisite_config_for_test_suite(tgen): + """ + API to do prerequisite config for testsuite + + parameters: + ----------- + * `tgen`: topogen object + """ + + step("Configure vxlan, bridge interface") + for dut in ["e1", "d1", "d2"]: + step("[DUT: ]Configure vxlan") + vxlan_input = { + dut: { + "vxlan": [ + { + "vxlan_name": VXLAN["vxlan_name"], + "vxlan_id": VXLAN["vxlan_id"], + "dstport": VXLAN["dstport"], + "local_addr": VXLAN["local_addr"][dut], + "learning": VXLAN["learning"], + } + ] + } + } + + result = configure_vxlan(tgen, vxlan_input) + assert result is True, "Testcase {} on {} :Failed \n Error: {}".format( + tc_name, dut, result + ) + + step("Configure bridge interface") + brctl_input = { + dut: { + "brctl": [ + { + "brctl_name": BRCTL["brctl_name"], + "addvxlan": BRCTL["addvxlan"], + "vrf": BRCTL["vrf"], + "stp": BRCTL["stp"], + } + ] + } + } + result = configure_brctl(tgen, topo, brctl_input) + assert result is True, "Testcase {} on {} :Failed \n Error: {}".format( + tc_name, dut, result + ) + + step("Configure default routes") + add_default_routes(tgen) + + +def add_default_routes(tgen): + """ + API to do prerequisite config for testsuite + + parameters: + ----------- + * `tgen`: topogen object + """ + + step("Add default routes..") + + default_routes = { + "e1": { + "static_routes": [ + { + "network": "{}/32".format(VXLAN["local_addr"]["d1"]), + "next_hop": topo["routers"]["d1"]["links"]["e1-link1"][ + "ipv4" + ].split("/")[0], + }, + { + "network": "{}/32".format(VXLAN["local_addr"]["d2"]), + "next_hop": topo["routers"]["d2"]["links"]["e1-link1"][ + "ipv4" + ].split("/")[0], + }, + ] + }, + "d1": { + "static_routes": [ + { + "network": "{}/32".format(VXLAN["local_addr"]["e1"]), + "next_hop": topo["routers"]["e1"]["links"]["d1-link1"][ + "ipv4" + ].split("/")[0], + }, + { + "network": "{}/32".format(VXLAN["local_addr"]["d2"]), + "next_hop": topo["routers"]["e1"]["links"]["d1-link1"][ + "ipv4" + ].split("/")[0], + }, + ] + }, + "d2": { + "static_routes": [ + { + "network": "{}/32".format(VXLAN["local_addr"]["d1"]), + "next_hop": topo["routers"]["e1"]["links"]["d2-link1"][ + "ipv4" + ].split("/")[0], + }, + { + "network": "{}/32".format(VXLAN["local_addr"]["e1"]), + "next_hop": topo["routers"]["e1"]["links"]["d2-link1"][ + "ipv4" + ].split("/")[0], + }, + ] + }, + } + + result = create_static_routes(tgen, default_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + +def test_RD_verification_manual_and_auto_p0(request): + """ + RD verification (manual/auto). + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + check_router_status(tgen) + reset_config_on_routers(tgen) + add_default_routes(tgen) + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step( + "Advertise prefixes from VNF routers R1 and R2 in associated " + "VRFs for both address-family." + ) + step( + "Advertise vrf RED's routes in EVPN address family from Edge-1 router" + ", without manual configuration of RD." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "r1": { + "static_routes": [ + { + "network": NETWORK1_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED", + } + ] + }, + "r2": { + "static_routes": [ + { + "network": NETWORK2_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE", + }, + { + "network": NETWORK3_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "GREEN", + } + ] + } + } + + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify on DCG-1 and DCG-2:") + step("EVPN route for 10.1.1.1/32 has auto-assigned RD value.") + + for dut in ["d1", "d2"]: + input_routes = {key: topo["routers"][key] for key in ["r1"]} + result = verify_attributes_for_evpn_routes( + tgen, topo, dut, input_routes, rd="auto", rd_peer="e1" + ) + assert result is True, "Testcase {} on {} :Failed \n Error: {}".format( + tc_name, dut, result + ) + + step( + "Configure RD for vrf RED manually as 50.50.50.50:50 and " + "advertise vrf RED's routes in EVPN address family from " + "Edge-1 router." + ) + + input_dict_rd = { + "e1": { + "bgp": [ + { + "local_as": "100", + "vrf": "RED", + "address_family": {"l2vpn": {"evpn": {"rd": "50.50.50.50:50"}}}, + } + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_rd) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("EVPN route for vrf RED has RD value as 50.50.50.50:50") + for dut in ["d1", "d2"]: + input_routes = {key: topo["routers"][key] for key in ["r1"]} + result = verify_attributes_for_evpn_routes( + tgen, topo, dut, input_routes, rd="50.50.50.50:50" + ) + assert result is True, "Testcase {} on {} :Failed \n Error: {}".format( + tc_name, dut, result + ) + + step( + "Configure RD for vrf RED manually as 100.100.100.100:100 and " + "advertise vrf RED's routes in EVPN address family from Edge-1 " + "router." + ) + input_dict_rd = { + "e1": { + "bgp": [ + { + "local_as": "100", + "vrf": "RED", + "address_family": { + "l2vpn": {"evpn": {"rd": "100.100.100.100:100"}} + } + } + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_rd) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "EVPN route for vrf RED is overridden with RD value as " "100.100.100.100:100." + ) + + for dut in ["d1", "d2"]: + input_routes = {key: topo["routers"][key] for key in ["r1"]} + result = verify_attributes_for_evpn_routes( + tgen, topo, dut, input_routes, rd="100.100.100.100:100" + ) + assert result is True, "Testcase {} on {} :Failed \n Error: {}".format( + tc_name, dut, result + ) + + step( + "Configure RD for vrf BLUE manually same as vrf RED " + "(100.100.100.100:100) and advertise vrf RED and BLUE's routes " + "in EVPN address family from Edge-1 router." + ) + + input_dict_rd = { + "e1": { + "bgp": [ + { + "local_as": "100", + "vrf": "BLUE", + "address_family": { + "l2vpn": {"evpn": {"rd": "100.100.100.100:100"}} + }, + } + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_rd) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Delete manually configured RD and advertise vrf RED's routes " + "in EVPN address family from Edge-1 router." + ) + + input_dict_rd = { + "e1": { + "bgp": [ + { + "local_as": "100", + "vrf": "RED", + "address_family": { + "l2vpn": {"evpn": {"no rd": "100.100.100.100:100"}} + }, + } + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_rd) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Configure same RD value for vrf GREEN, as auto generated RD " + "value for vrf RED on Edge-1 router." + ) + + input_dict_rd = { + "e1": { + "bgp": [ + { + "local_as": "100", + "vrf": "GREEN", + "address_family": {"l2vpn": {"evpn": {"rd": "10.0.0.33:1"}}}, + } + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_rd) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Delete auto configured RD value from vrf RED in EVPN " "address family.") + + input_dict_rd = { + "e1": { + "bgp": [ + { + "local_as": "100", + "vrf": "GREEN", + "address_family": {"l2vpn": {"evpn": {"no rd": "10.0.0.33:1"}}}, + } + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_rd) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Configure RD value as 100.100.100:100") + + input_dict_rd = { + "e1": { + "bgp": [ + { + "local_as": "100", + "vrf": "GREEN", + "address_family": {"l2vpn": {"evpn": {"rd": "100.100.100:100"}}}, + } + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_rd) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_RT_verification_manual_p0(request): + """ + RT verification(manual) + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + check_router_status(tgen) + reset_config_on_routers(tgen) + add_default_routes(tgen) + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step( + "Advertise prefixes from VNF routers R1 and R2 in associated " + "VRFs for both address-family." + ) + step("Advertise VRF routes as in EVPN address family from Edge-1 " "router.") + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "r1": { + "static_routes": [ + { + "network": NETWORK1_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED", + } + ] + }, + "r2": { + "static_routes": [ + { + "network": NETWORK2_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE", + }, + { + "network": NETWORK3_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "GREEN", + } + ] + } + } + + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Configure RT for vrf RED manually as export 100:100 " + "and advertise vrf RED's routes in EVPN address family" + " from Edge-1 router." + ) + + input_dict_rt = { + "e1": { + "bgp": [ + { + "local_as": "100", + "vrf": "RED", + "address_family": { + "ipv4": { + "unicast": {"neighbor": {"r1": {"dest_link": {"e1": {}}}}} + }, + "ipv6": { + "unicast": {"neighbor": {"r1": {"dest_link": {"e1": {}}}}} + }, + "l2vpn": { + "evpn": {"route-target": {"export": [{"value": "100:100"}]}} + }, + } + } + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_rt) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify on dcg-1 and dcg-2, EVPN route for 10.1.1.1/32" + " and 10::1/128 have RT value as 100:100." + ) + + for dut in ["d1", "d2"]: + input_routes = {key: topo["routers"][key] for key in ["r1"]} + result = verify_attributes_for_evpn_routes( + tgen, topo, dut, input_routes, rt="100:100" + ) + assert result is True, "Testcase {} on {} :Failed \n Error: {}".format( + tc_name, dut, result + ) + + step( + "Configure RT for vrf RED manually as export 500:500 and" + " advertise vrf RED's routes in EVPN address family from" + " e1 router." + ) + + input_dict_rt = { + "e1": { + "bgp": [ + { + "local_as": "100", + "vrf": "RED", + "address_family": { + "l2vpn": { + "evpn": {"route-target": {"export": [{"value": "500:500"}]}} + } + }, + } + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_rt) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify on dcg-1 and dcg-2, EVPN route for 10.1.1.1/32" + " and 10::1/128 have RT value as 500:500." + ) + + for dut in ["d1", "d2"]: + input_routes = {key: topo["routers"][key] for key in ["r1"]} + result = verify_attributes_for_evpn_routes( + tgen, topo, dut, input_routes, rt=["100:100", "500:500"] + ) + assert result is True, "Testcase {} on {} :Failed \n Error: {}".format( + tc_name, dut, result + ) + + step( + "Import RT value 100:100 and 500:500 in vrf BLUE manually on" + " peer router DCG-1 and DCG-2." + ) + + input_dict_rt = { + "d1": { + "bgp": [ + { + "local_as": "100", + "vrf": "BLUE", + "address_family": { + "l2vpn": { + "evpn": { + "route-target": { + "import": [ + {"value": "100:100"}, + {"value": "500:500"}, + ] + } + } + } + }, + } + ] + }, + "d2": { + "bgp": [ + { + "local_as": "200", + "vrf": "BLUE", + "address_family": { + "l2vpn": { + "evpn": { + "route-target": { + "import": [ + {"value": "100:100"}, + {"value": "500:500"}, + ] + } + } + } + }, + } + ] + }, + } + + result = create_router_bgp(tgen, topo, input_dict_rt) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "EVPN route for 10.1.1.1/32 and 10::1 should be installed " + "in vrf BLUE on DCG-1 and DCG-2 and further advertised to " + "VNF router." + ) + + for addr_type in ADDR_TYPES: + input_routes = { + "r1": { + "static_routes": [{"network": [NETWORK1_1[addr_type]], "vrf": "BLUE"}] + } + } + result = verify_rib(tgen, addr_type, "d1", input_routes) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + result = verify_rib(tgen, addr_type, "d2", input_routes) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step( + "Delete import RT value 500:500 in vrf BLUE manually on " + "peer router DCG-1 and DCG-2." + ) + + input_dict_rt = { + "d1": { + "bgp": [ + { + "local_as": "100", + "vrf": "BLUE", + "address_family": { + "l2vpn": { + "evpn": { + "route-target": { + "import": [{"value": "500:500", "delete": True}] + } + } + } + }, + } + ] + }, + "d2": { + "bgp": [ + { + "local_as": "200", + "vrf": "BLUE", + "address_family": { + "l2vpn": { + "evpn": { + "route-target": { + "import": [{"value": "500:500", "delete": True}] + } + } + } + }, + } + ] + }, + } + + result = create_router_bgp(tgen, topo, input_dict_rt) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + for dut in ["d1", "d2"]: + input_routes = {key: topo["routers"][key] for key in ["r1"]} + result = verify_attributes_for_evpn_routes( + tgen, topo, dut, input_routes, rt=["100:100", "500:500"] + ) + assert result is True, "Testcase {} on {} :Failed \n Error: {}".format( + tc_name, dut, result + ) + + step("Delete RT export value 100:100 for vrf RED on Edge-1") + + input_dict_rt = { + "e1": { + "bgp": [ + { + "local_as": "100", + "vrf": "RED", + "address_family": { + "l2vpn": { + "evpn": { + "route-target": { + "export": [{"value": "100:100", "delete": True}] + } + } + } + }, + } + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_rt) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "EVPN route for 10.1.1.1/32 and 10::1 should be withdrawn " + "from vrf BLUE on DCG-1,DCG-2 and VNF router." + ) + + for addr_type in ADDR_TYPES: + input_routes = { + "r1": { + "static_routes": [{"network": [NETWORK1_1[addr_type]], "vrf": "BLUE"}] + } + } + result = verify_rib(tgen, addr_type, "d1", input_routes, expected=False) + assert result is not True, ( + "Testcase {} :Failed \n Expected Behavior: Routes are still " + "present \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behavior: {}".format(result)) + + result = verify_rib(tgen, addr_type, "d2", input_routes, expected=False) + assert result is not True, ( + "Testcase {} :Failed \n Expected Behavior: Routes are still " + "present \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behavior: {}".format(result)) + + step( + "Configure RT value as 100:100000010000010000101010 to check " + "the boundary value." + ) + + input_dict_rt = { + "e1": { + "bgp": [ + { + "local_as": "100", + "vrf": "RED", + "address_family": { + "l2vpn": { + "evpn": { + "route-target": { + "export": [ + {"value": "100:100000010000010000101010"} + ] + } + } + } + }, + } + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_rt) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "CLI error: RT value: 100:100000010000010000101010 should not " "be configured" + ) + + dut = "e1" + input_routes = {key: topo["routers"][key] for key in ["r1"]} + result = verify_attributes_for_evpn_routes( + tgen, topo, dut, input_routes, rt="100:100000010000010000101010", expected=False + ) + assert result is not True, ( + "Testcase {} :Failed \n Expected Behavior: RT value of out" + " of boundary \n Error: {}".format(tc_name, result) + ) + logger.info("Expected Behavior: {}".format(result)) + + write_test_footer(tc_name) + + +def test_active_standby_evpn_implementation_p1(request): + """ + In an active/standby EVPN implementation, if active DCG goes down, + secondary takes over. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + check_router_status(tgen) + reset_config_on_routers(tgen) + add_default_routes(tgen) + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step( + "Taken care in base config: Configure BGP neighborship for both " + "address families(IPv4 & IPv6) between DCG-1/DCG-2 and VFN routers" + "(R3 and R4)." + ) + + step( + "BGP neighborships come up within defined VRFs. Please use below " + "command: sh bgp vrf all summary" + ) + + result = verify_bgp_convergence(tgen, topo, "d1") + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + result = verify_bgp_convergence(tgen, topo, "d2") + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Advertise prefixes from VNF routers R3 and R4 in associated " + "VRFs for both address-families." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "r3": { + "static_routes": [ + { + "network": NETWORK1_2[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED", + } + ] + }, + "r4": { + "static_routes": [ + { + "network": NETWORK1_3[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE", + }, + { + "network": NETWORK1_4[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "GREEN", + } + ] + } + } + + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Redistribute static in (IPv4 and IPv6) address-family " + "on Edge-1 for all VRFs." + ) + + input_dict_2 = {} + for dut in ["r3", "r4"]: + temp = {dut: {"bgp": []}} + input_dict_2.update(temp) + + if dut == "r3": + VRFS = ["RED"] + AS_NUM = [3] + if dut == "r4": + VRFS = ["BLUE", "GREEN"] + AS_NUM = [4, 4] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Prefixes are received in respective VRFs on DCG-1/DCG-2.") + + for addr_type in ADDR_TYPES: + input_routes = { + "r3": { + "static_routes": [ + { + "network": NETWORK1_2[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED", + } + ] + }, + "r4": { + "static_routes": [ + { + "network": NETWORK1_3[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE", + }, + { + "network": NETWORK1_4[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "GREEN", + }, + ] + }, + } + + result = verify_rib(tgen, addr_type, "d1", input_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "d2", input_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Taken care in base config: Advertise VRF routes in EVPN " + "address-family from DCG-1 and DCG-2 router." + ) + + step("Verify on Edge-1 that EVPN routes are installed via next-hop " "as DCG-2.") + + for addr_type in ADDR_TYPES: + input_routes = { + "r3": { + "static_routes": [ + { + "network": NETWORK1_2[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED", + } + ] + }, + "r4": { + "static_routes": [ + { + "network": NETWORK1_3[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE", + }, + { + "network": NETWORK1_4[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "GREEN", + }, + ] + }, + } + + if addr_type == "ipv4": + result = verify_rib( + tgen, addr_type, "e1", input_routes, next_hop=BRIDGE_INTF2 + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + else: + result = verify_rib(tgen, addr_type, "e1", input_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Configure 'next-hop self' on DCG-1 for peer Edge-1 in EVPN " "address-family." + ) + + input_dict_3 = { + "d1": { + "bgp": [ + { + "local_as": "100", + "address_family": { + "l2vpn": { + "evpn": { + "neighbor": { + "e1": { + "ipv4": {"d1-link1": {"next_hop_self": True}} + } + } + } + } + }, + } + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + logger.info( + "Creating route-map so ipv6 glpbal ip wpuld be preferred " "as next-hop" + ) + + step( + "Verify on Edge-1 that EVPN routes are now preferred via " + "next-hop as DCG-1(iBGP) due to shortest AS-Path." + ) + + for addr_type in ADDR_TYPES: + + logger.info("Verifying only ipv4 routes") + if addr_type != "ipv4": + continue + + input_routes = { + "r3": { + "static_routes": [ + { + "network": NETWORK1_2[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED", + } + ] + }, + "r4": { + "static_routes": [ + { + "network": NETWORK1_3[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE", + }, + { + "network": NETWORK1_4[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "GREEN", + }, + ] + }, + } + + next_hop = topo["routers"]["d1"]["links"]["e1-link1"]["ipv4"].split("/")[0] + + result = verify_rib(tgen, addr_type, "e1", input_routes, next_hop=next_hop) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_evpn_routes_from_VNFs_p1(request): + """ + EVPN routes are advertised/withdrawn, based on VNFs + advertising/withdrawing IP prefixes. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + check_router_status(tgen) + reset_config_on_routers(tgen) + add_default_routes(tgen) + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step( + "Advertise prefixes from VNF routers R1 and R2 in associated " + "VRFs for both address-family." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "r1": { + "static_routes": [ + { + "network": NETWORK1_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED", + } + ] + }, + "r2": { + "static_routes": [ + { + "network": NETWORK2_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE", + }, + { + "network": NETWORK3_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "GREEN", + } + ] + } + } + + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Taken care in base config: Advertise VNFs'(R1 and R2) " + "originated routes in EVPN address-family from Edge-1 to " + "DCG-1 and DCG-2 routers." + ) + step( + "Taken care in base config: Advertise IPv4 and IPv6 routes " + "from default vrf in EVPN address-family from Edge-1." + ) + + step( + "Verify on DCG-2 that VNF routes are received in respective " + "VRFs along with auto derived RD/RT values 'show bgp l2vpn evpn'" + ) + for dut in ["d1", "d2"]: + input_routes = {key: topo["routers"][key] for key in ["r1"]} + result = verify_evpn_routes(tgen, topo, dut, input_routes) + assert result is True, "Testcase {} on {} :Failed \n Error: {}".format( + tc_name, dut, result + ) + + input_routes = {key: topo["routers"][key] for key in ["r2"]} + result = verify_evpn_routes(tgen, topo, dut, input_routes) + assert result is True, "Testcase {} on {} :Failed \n Error: {}".format( + tc_name, dut, result + ) + + step( + "Verify on R3 and R4 that DCG-2 further advertises all EVPN " + "routes to corresponding VRFs." + ) + for addr_type in ADDR_TYPES: + input_routes = {key: topo["routers"][key] for key in ["r1"]} + result = verify_rib(tgen, addr_type, "r3", input_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + input_routes = {key: topo["routers"][key] for key in ["r2"]} + result = verify_rib(tgen, addr_type, "r4", input_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that DCG-2 receives EVPN routes associated to default " + "VRF and install in default IP routing table as well." + ) + for addr_type in ADDR_TYPES: + input_routes = {key: topo["routers"][key] for key in ["r1"]} + result = verify_rib(tgen, addr_type, "d2", input_routes, expected=False) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + input_routes = {key: topo["routers"][key] for key in ["r2"]} + result = verify_rib(tgen, addr_type, "d2", input_routes, expected=False) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Withdraw the IP prefixes from VFN(R1).") + dut = "r1" + input_dict_2 = {} + static_routes = topo["routers"][dut]["static_routes"] + for static_route in static_routes: + static_route["delete"] = True + temp = {dut: {"static_routes": [static_route]}} + input_dict_2.update(temp) + + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that DCG-2 removes EVPN routes corresponding to vrf RED and " + "send an withdraw to VNF(R3) as well." + ) + for addr_type in ADDR_TYPES: + input_routes = {key: topo["routers"][key] for key in ["r1"]} + result = verify_rib(tgen, addr_type, "d2", input_routes, expected=False) + assert result is not True, "Testcase {} :Failed \n " + "Routes are still present: {}".format(tc_name, result) + logger.info("Expected Behavior: {}".format(result)) + + for addr_type in ADDR_TYPES: + input_routes = {key: topo["routers"][key] for key in ["r1"]} + result = verify_rib(tgen, addr_type, "r3", input_routes, expected=False) + assert result is not True, "Testcase {} :Failed \n " + "Routes are still present: {}".format(tc_name, result) + logger.info("Expected Behavior: {}".format(result)) + + step("Re-advertise IP prefixes from VFN(R1).") + step( + "Advertise prefixes from VNF routers R1 and R2 in associated " + "VRFs for both address-family." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "r1": { + "static_routes": [ + { + "network": NETWORK1_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED", + } + ] + }, + "r2": { + "static_routes": [ + { + "network": NETWORK2_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE", + }, + { + "network": NETWORK3_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "GREEN", + } + ] + } + } + + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that DCG-2 receives EVPN routes corresponding to vrf RED " + "again and send an update to VNF(R3) as well." + ) + for addr_type in ADDR_TYPES: + input_routes = {key: topo["routers"][key] for key in ["r1"]} + result = verify_rib(tgen, addr_type, "d2", input_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + input_routes = {key: topo["routers"][key] for key in ["r1"]} + result = verify_rib(tgen, addr_type, "r3", input_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Delete vrf BLUE from router Edge-1") + input_dict_3 = {"e1": {"vrfs": [{"name": "BLUE", "id": "2", "delete": True}]}} + + result = create_vrf_cfg(tgen, input_dict_3) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that DCG-2 removes EVPN routes corresponding to " + "vrf BLUE and send an withdraw to VNF(R4) as well." + ) + for addr_type in ADDR_TYPES: + input_routes = { + "r2": {"static_routes": [{"network": NETWORK2_1[addr_type], "vrf": "BLUE"}]} + } + + result = verify_rib(tgen, addr_type, "d2", input_routes, expected=False) + assert result is not True, "Testcase {} :Failed \n " + "Routes are still present: {}".format(tc_name, result) + logger.info("Expected Behavior: {}".format(result)) + + result = verify_rib(tgen, addr_type, "r4", input_routes, expected=False) + assert result is not True, "Testcase {} :Failed \n " + "Routes are still present: {}".format(tc_name, result) + logger.info("Expected Behavior: {}".format(result)) + + step("Add vrf BLUE on router Edge-1 again.") + interface = topo["routers"]["e1"]["links"]["r2-link1"]["interface"] + input_dict_3 = { + "e1": { + "links": { + "r2-link1": { + "interface": interface, + "ipv4": "auto", + "ipv6": "auto", + "vrf": "BLUE", + } + }, + "vrfs": [{"name": "BLUE", "id": "2"}], + } + } + result = create_vrf_cfg(tgen, input_dict_3) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + logger.info( + "After deleting VRFs ipv6 addresses wil be deleted " + "from kernel Adding back ipv6 addresses" + ) + dut = "e1" + vrfs = ["BLUE"] + + for vrf in vrfs: + for c_link, c_data in topo["routers"][dut]["links"].items(): + if "vrf" in c_data: + if c_data["vrf"] != vrf: + continue + + intf_name = c_data["interface"] + intf_ipv6 = c_data["ipv6"] + + create_interface_in_kernel( + tgen, dut, intf_name, intf_ipv6, vrf, create=False + ) + + logger.info("Wait for 60 sec.") + sleep(60) + + step( + "Verify that DCG-2 receives EVPN routes corresponding to " + "vrf BLUE again and send an update to VNF(R4) as well." + ) + for addr_type in ADDR_TYPES: + input_routes = { + "r2": {"static_routes": [{"network": NETWORK2_1[addr_type], "vrf": "BLUE"}]} + } + + result = verify_rib(tgen, addr_type, "d2", input_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r4", input_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Withdraw IPv6 address-family in EVPN advertisements for " "VRF GREEN") + addr_type = "ipv6" + input_dict_4 = { + "e1": { + "bgp": [ + { + "local_as": "100", + "vrf": "GREEN", + "address_family": { + "l2vpn": { + "evpn": { + "advertise": {addr_type: {"unicast": {"delete": True}}} + } + } + }, + } + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that EVPN routes (IPv6)associated with vrf GREEN are " + "withdrawn from DCG-2 and VNF R4." + ) + input_routes = { + "r2": {"static_routes": [{"network": NETWORK3_1[addr_type], "vrf": "GREEN"}]} + } + + result = verify_rib(tgen, addr_type, "d2", input_routes, expected=False) + assert result is not True, "Testcase {} :Failed \n " + "Routes are still present: {}".format(tc_name, result) + logger.info("Expected Behavior: {}".format(result)) + + result = verify_rib(tgen, addr_type, "r4", input_routes, expected=False) + assert result is not True, "Testcase {} :Failed \n " + "Routes are still present: {}".format(tc_name, result) + logger.info("Expected Behavior: {}".format(result)) + + step("Advertise IPv6 address-family in EVPN advertisements " "for VRF GREEN.") + addr_type = "ipv6" + input_dict_4 = { + "e1": { + "bgp": [ + { + "local_as": "100", + "vrf": "GREEN", + "address_family": { + "l2vpn": {"evpn": {"advertise": {addr_type: {"unicast": {}}}}} + }, + } + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + input_routes = { + "r2": { + "static_routes": [{"network": NETWORK3_1[addr_type], "vrf": "GREEN"}] + } + } + + result = verify_rib(tgen, addr_type, "d2", input_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r4", input_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +@pytest.mark.parametrize( + "attribute", [{"route-type": "prefix"}, {"vni": VNI_1}, {"rt": "300:300"}] +) +def test_route_map_operations_for_evpn_address_family_p1(request, attribute): + """ + Route-map operations for EVPN address family. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + check_router_status(tgen) + reset_config_on_routers(tgen) + add_default_routes(tgen) + + step( + "Advertise prefixes from VNF routers R1 and R2 in associated " + "VRFs for both address-family." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "r1": { + "static_routes": [ + { + "network": NETWORK1_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED", + } + ] + }, + "r2": { + "static_routes": [ + { + "network": NETWORK2_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE", + }, + { + "network": NETWORK3_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "GREEN", + } + ] + } + } + + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step( + "Advertise VRF routes in EVPN address family from Edge-1 router." + " Configure a route-map on e1 to filter EVPN routes based on" + " below keywords: route-type: prefix" + ) + + for key, value in attribute.items(): + if key == "rt": + logger.info("Creating extcommunity using raw_config") + raw_config = { + "d2": { + "raw_config": [ + "bgp extcommunity-list standard ECOMM300 permit {} {}".format( + key, value + ) + ] + } + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format( + tc_name, result + ) + + input_dict_1 = { + "e1": { + "route_maps": { + "rmap_route_type": [ + {"action": "permit", "set": {"extcommunity": {key: value}}} + ] + } + }, + "d2": { + "route_maps": { + "rmap_route_type": [ + {"action": "permit", "match": {"extcommunity": "ECOMM300"}} + ] + } + }, + } + + else: + input_dict_1 = { + "e1": { + "route_maps": { + "rmap_route_type": [ + {"action": "permit", "match": {"evpn": {key: value}}} + ] + } + }, + "d2": { + "route_maps": { + "rmap_route_type": [ + {"action": "permit", "match": {"evpn": {key: value}}} + ] + } + }, + } + result = create_route_maps(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_2 = { + "e1": { + "bgp": [ + { + "local_as": "100", + "address_family": { + "l2vpn": { + "evpn": { + "neighbor": { + "d2": { + "ipv4": { + "e1-link1": { + "route_maps": [ + { + "name": "rmap_route_type", + "direction": "out", + } + ] + } + } + } + } + } + } + }, + } + ] + }, + "d2": { + "bgp": [ + { + "local_as": "200", + "address_family": { + "l2vpn": { + "evpn": { + "neighbor": { + "e1": { + "ipv4": { + "d2-link1": { + "route_maps": [ + { + "name": "rmap_route_type", + "direction": "in", + } + ] + } + } + } + } + } + } + }, + } + ] + }, + } + + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify on router DCG-2 that EVPN routes corresponding to all " + "VRFs are received. As all EVPN routes are type-5 only." + ) + + input_routes = {key: topo["routers"][key] for key in ["r1"]} + result = verify_evpn_routes(tgen, topo, "d2", input_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + input_routes = {key: topo["routers"][key] for key in ["r2"]} + result = verify_evpn_routes(tgen, topo, "d2", input_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +@pytest.mark.parametrize("attribute", ["locPrf", "weight", "path"]) +def test_bgp_attributes_for_evpn_address_family_p1(request, attribute): + """ + BGP attributes for EVPN address-family. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + check_router_status(tgen) + reset_config_on_routers(tgen) + add_default_routes(tgen) + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step( + "Advertise prefixes from VNF routers R1 and R2 in associated " + "VRFs for both address-family." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "r1": { + "static_routes": [ + { + "network": NETWORK1_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED", + } + ] + }, + "r2": { + "static_routes": [ + { + "network": NETWORK2_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE", + }, + { + "network": NETWORK3_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "GREEN", + } + ] + } + } + + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + topo_local = deepcopy(topo) + + logger.info("Modifying topology b/w e1 and d1 from iBGP to eBGP") + step("Delete BGP config for vrf RED.") + + if attribute == "locPrf": + input_dict_vni = { + "d1": { + "vrfs": [ + {"name": "RED", "no_vni": VNI_1}, + {"name": "BLUE", "no_vni": VNI_2}, + {"name": "GREEN", "no_vni": VNI_3}, + ] + } + } + result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_2 = {} + for dut in ["d1"]: + temp = {dut: {"bgp": []}} + input_dict_2.update(temp) + + INDEX = [0, 1, 2, 3] + VRFS = ["RED", "BLUE", "GREEN", None] + AS_NUM = [100, 100, 100, 100] + + for index, vrf, as_num in zip(INDEX, VRFS, AS_NUM): + topo_local["routers"][dut]["bgp"][index]["local_as"] = 200 + if vrf: + temp[dut]["bgp"].append( + {"local_as": as_num, "vrf": vrf, "delete": True} + ) + else: + temp[dut]["bgp"].append({"local_as": as_num, "delete": True}) + + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} on d1 :Failed \n Error: {}".format( + tc_name, result + ) + + result = create_router_bgp(tgen, topo_local["routers"]) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Advertise VRF routes in EVPN address-family from DCG-1 " "and DCG-2 routers.") + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "r3": { + "static_routes": [ + { + "network": NETWORK1_2[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED", + } + ] + }, + "r4": { + "static_routes": [ + { + "network": NETWORK1_3[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE", + }, + { + "network": NETWORK1_4[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "GREEN", + }, + ] + }, + } + + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Redistribute static in (IPv4 and IPv6) address-family " + "on Edge-1 for all VRFs." + ) + + input_dict_2 = {} + for dut in ["r3", "r4"]: + temp = {dut: {"bgp": []}} + input_dict_2.update(temp) + + if dut == "r3": + VRFS = ["RED"] + AS_NUM = [3] + if dut == "r4": + VRFS = ["BLUE", "GREEN"] + AS_NUM = [4, 4] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify on router Edge-1 that EVPN routes corresponding to " + "all VRFs are received from both routers DCG-1 and DCG-2" + ) + + for addr_type in ADDR_TYPES: + input_routes = { + "r3": { + "static_routes": [ + { + "network": NETWORK1_2[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED", + } + ] + }, + "r4": { + "static_routes": [ + { + "network": NETWORK1_3[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE", + }, + { + "network": NETWORK1_4[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "GREEN", + }, + ] + }, + } + + result = verify_rib(tgen, addr_type, "e1", input_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Configure a route-map on Edge-1 to modify below BGP attributes " + "for EVPN address-family:" + ) + + if attribute == "path": + input_dict_1 = { + "e1": { + "route_maps": { + "rmap_d1".format(addr_type): [ + { + "action": "permit", + "set": { + attribute: { + "as_num": "123 231 321", + "as_action": "prepend", + } + }, + } + ], + "rmap_d2".format(addr_type): [ + { + "action": "permit", + "set": { + attribute: {"as_num": "121", "as_action": "prepend"} + }, + } + ], + } + } + } + else: + input_dict_1 = { + "e1": { + "route_maps": { + "rmap_d1".format(addr_type): [ + {"action": "permit", "set": {attribute: 120}} + ], + "rmap_d2".format(addr_type): [ + {"action": "permit", "set": {attribute: 150}} + ], + } + } + } + result = create_route_maps(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + input_dict_2 = { + "e1": { + "bgp": [ + { + "local_as": "100", + "address_family": { + "l2vpn": { + "evpn": { + "neighbor": { + "d1": { + "ipv4": { + "e1-link1": { + "route_maps": [ + { + "name": "rmap_d1", + "direction": "in", + } + ] + } + } + }, + "d2": { + "ipv4": { + "e1-link1": { + "route_maps": [ + { + "name": "rmap_d2", + "direction": "in", + } + ] + } + } + }, + } + } + } + }, + } + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify on router Edge-1 that EVPN routes are preferred via" + " DCG-1 or DCG-2 based on best path selection criteria " + "(according to the configured BGP attribute values in route-map)." + ) + + for addr_type in ADDR_TYPES: + input_routes = { + "r3": { + "static_routes": [ + { + "network": NETWORK1_2[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED", + } + ] + }, + "r4": { + "static_routes": [ + { + "network": NETWORK1_3[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE", + }, + { + "network": NETWORK1_4[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "GREEN", + }, + ] + }, + } + + result = verify_best_path_as_per_bgp_attribute( + tgen, addr_type, "e1", input_routes, attribute + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/example-test/test_example.py b/tests/topotests/example-test/test_example.py index 8e37ad11d4..72eceee612 100755 --- a/tests/topotests/example-test/test_example.py +++ b/tests/topotests/example-test/test_example.py @@ -9,52 +9,61 @@ fatal_error = "" + def setup_module(module): - print ("setup_module module:%s" % module.__name__) + print("setup_module module:%s" % module.__name__) + def teardown_module(module): - print ("teardown_module module:%s" % module.__name__) + print("teardown_module module:%s" % module.__name__) + def setup_function(function): - print ("setup_function function:%s" % function.__name__) + print("setup_function function:%s" % function.__name__) + def teardown_function(function): - print ("teardown_function function:%s" % function.__name__) + print("teardown_function function:%s" % function.__name__) + def test_numbers_compare(): a = 12 - print ("Dummy Output") - assert( a == 12 ) + print("Dummy Output") + assert a == 12 + def test_fail_example(): assert True, "Some Text with explaination in case of failure" + def test_ls_exits_zero(): "Tests for ls command on invalid file" global fatal_error proc = subprocess.Popen( - ["ls", "/some/nonexistant/file"], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, + ["ls", "/some/nonexistant/file"], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, ) stdout, stderr = proc.communicate() - if (proc.returncode != 0): + if proc.returncode != 0: # Mark this as a fatal error which skips some other tests on failure fatal_error = "test_fail_example failed" assert proc.returncode == 0, "Return Code is non-Zero:\n%s" % stderr + def test_skipped_on_fatalerror(): global fatal_error # Skip if previous fatal error condition is raised - if (fatal_error != ""): + if fatal_error != "": pytest.skip(fatal_error) assert True, "Some Text with explaination in case of failure" -if __name__ == '__main__': + +if __name__ == "__main__": retval = pytest.main(["-s"]) sys.exit(retval) diff --git a/tests/topotests/example-test/test_template.py b/tests/topotests/example-test/test_template.py index 4e35ce8b9f..afe974876a 100755 --- a/tests/topotests/example-test/test_template.py +++ b/tests/topotests/example-test/test_template.py @@ -32,7 +32,7 @@ # Save the Current Working Directory to find configuration files. CWD = os.path.dirname(os.path.realpath(__file__)) -sys.path.append(os.path.join(CWD, '../')) +sys.path.append(os.path.join(CWD, "../")) # pylint: disable=C0413 # Import topogen and topotest helpers @@ -43,8 +43,10 @@ # Required to instantiate the topology builder class. from mininet.topo import Topo + class TemplateTopo(Topo): "Test topology builder" + def build(self, *_args, **_opts): "Build function" tgen = get_topogen(self) @@ -56,17 +58,18 @@ def build(self, *_args, **_opts): # # Create 2 routers for routern in range(1, 3): - tgen.add_router('r{}'.format(routern)) + tgen.add_router("r{}".format(routern)) # Create a switch with just one router connected to it to simulate a # empty network. - switch = tgen.add_switch('s1') - switch.add_link(tgen.gears['r1']) + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) # Create a connection between r1 and r2 - switch = tgen.add_switch('s2') - switch.add_link(tgen.gears['r1']) - switch.add_link(tgen.gears['r2']) + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + def setup_module(mod): "Sets up the pytest environment" @@ -83,12 +86,13 @@ def setup_module(mod): router.load_config( TopoRouter.RD_ZEBRA, # Uncomment next line to load configuration from ./router/zebra.conf - #os.path.join(CWD, '{}/zebra.conf'.format(rname)) + # os.path.join(CWD, '{}/zebra.conf'.format(rname)) ) # After loading the configurations, this function loads configured daemons. tgen.start_router() + def teardown_module(mod): "Teardown the pytest environment" tgen = get_topogen() @@ -96,6 +100,7 @@ def teardown_module(mod): # This function tears down the whole topology. tgen.stop_topology() + def test_call_mininet_cli(): "Dummy test that just calls mininet CLI so we can interact with the build." tgen = get_topogen() @@ -103,18 +108,20 @@ def test_call_mininet_cli(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - logger.info('calling mininet CLI') + logger.info("calling mininet CLI") tgen.mininet_cli() + # Memory leak test template def test_memory_leak(): "Run the memory leak test and report results." tgen = get_topogen() if not tgen.is_memleak_enabled(): - pytest.skip('Memory leak test/report is disabled') + pytest.skip("Memory leak test/report is disabled") tgen.report_memory_leaks() -if __name__ == '__main__': + +if __name__ == "__main__": args = ["-s"] + sys.argv[1:] sys.exit(pytest.main(args)) diff --git a/tests/topotests/example-topojson-test/__init__.py b/tests/topotests/example-topojson-test/__init__.py new file mode 100755 index 0000000000..e69de29bb2 diff --git a/tests/topotests/example-topojson-test/test_topo_json_multiple_links/__init__.py b/tests/topotests/example-topojson-test/test_topo_json_multiple_links/__init__.py new file mode 100755 index 0000000000..e69de29bb2 diff --git a/tests/topotests/example-topojson-test/test_topo_json_multiple_links/example_topojson_multiple_links.json b/tests/topotests/example-topojson-test/test_topo_json_multiple_links/example_topojson_multiple_links.json new file mode 100644 index 0000000000..3968348b1f --- /dev/null +++ b/tests/topotests/example-topojson-test/test_topo_json_multiple_links/example_topojson_multiple_links.json @@ -0,0 +1,152 @@ +{ + "ipv4base": "10.0.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 30, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:DB8:F::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link2": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {} + } + } + } + } + } + } + } + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r1-link2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link2": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + { + "redist_type": "static" + } + ], + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {} + } + }, + "r3": { + "dest_link": { + "r2-link1": {} + } + } + } + } + } + } + }, + "static_routes": [ + { + "network": "100.0.20.1/32", + "no_of_ip": 9, + "admin_distance": 100, + "next_hop": "10.0.0.1" + } + ] + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link2": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": {} + } + } + } + } + } + } + }, + "static_routes": [ + { + "network": "10.0.0.1/30", + "next_hop": "10.0.0.5" + } + ] + } + } +} + diff --git a/tests/topotests/example-topojson-test/test_topo_json_multiple_links/test_example_topojson_multiple_links.py b/tests/topotests/example-topojson-test/test_topo_json_multiple_links/test_example_topojson_multiple_links.py new file mode 100755 index 0000000000..f24f463b8a --- /dev/null +++ b/tests/topotests/example-topojson-test/test_topo_json_multiple_links/test_example_topojson_multiple_links.py @@ -0,0 +1,194 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2019 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. ("NetDEF") +# in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +.py: Test . +""" + +import os +import sys +import json +import time +import inspect +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../../")) + +# pylint: disable=C0413 +from lib.topogen import Topogen, get_topogen + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + +# Import topoJson from lib, to create topology and initial configuration +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + verify_rib, +) +from lib.topolog import logger +from lib.bgp import verify_bgp_convergence +from lib.topojson import build_topo_from_json, build_config_from_json + +# Reading the data from JSON File for topology and configuration creation +jsonFile = "{}/example_topojson_multiple_links.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +# Global variables +bgp_convergence = False +input_dict = {} + + +class TemplateTopo(Topo): + """ + Test topology builder + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # This function only purpose is to create topology + # as defined in input json file. + # + # Example + # + # Creating 2 routers having 2 links in between, + # one is used to establised BGP neighborship + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(TemplateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen) + + # This function only purpose is to create configuration + # as defined in input json file. + # + # Example + # + # Creating configuration defined in input JSON + # file, example, BGP config, interface config, static routes + # config, prefix list config + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """ + Teardown the pytest environment + + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + +def test_bgp_convergence(request): + " Test BGP daemon convergence " + + tgen = get_topogen() + global bgp_convergence + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Api call verify whether BGP is converged + bgp_convergence = verify_bgp_convergence(tgen, topo) + assert ( + bgp_convergence is True + ), "test_bgp_convergence failed.. \n" " Error: {}".format(bgp_convergence) + + logger.info("BGP is converged successfully \n") + write_test_footer(tc_name) + + +def test_static_routes(request): + " Test to create and verify static routes. " + + tgen = get_topogen() + if bgp_convergence is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Static routes are created as part of initial configuration, + # verifying RIB + dut = "r3" + protocol = "bgp" + next_hop = "10.0.0.1" + input_dict = {"r1": topo["routers"]["r1"]} + + # Uncomment below to debug + # tgen.mininet_cli() + result = verify_rib(tgen, "ipv4", dut, input_dict, next_hop=next_hop) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/example-topojson-test/test_topo_json_single_link/__init__.py b/tests/topotests/example-topojson-test/test_topo_json_single_link/__init__.py new file mode 100755 index 0000000000..e69de29bb2 diff --git a/tests/topotests/example-topojson-test/test_topo_json_single_link/example_topojson.json b/tests/topotests/example-topojson-test/test_topo_json_single_link/example_topojson.json new file mode 100644 index 0000000000..629d2d6d78 --- /dev/null +++ b/tests/topotests/example-topojson-test/test_topo_json_single_link/example_topojson.json @@ -0,0 +1,153 @@ +{ + "ipv4base": "10.0.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 30, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:DB8:F::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {} + } + }, + "r3": { + "dest_link": { + "r1": {} + } + } + } + } + } + } + } + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + { + "redist_type": "static" + } + ], + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r3": { + "dest_link": { + "r2": {} + } + } + } + } + } + } + }, + "static_routes": [ + { + "network": "100.0.20.1/32", + "no_of_ip": 9, + "admin_distance": 100, + "next_hop": "10.0.0.1" + } + ] + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r1": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": {} + } + }, + "r1": { + "dest_link": { + "r3": {} + } + } + } + } + } + } + }, + "static_routes": [ + { + "network": "10.0.0.1/30", + "next_hop": "10.0.0.5" + } + ] + } + } +} diff --git a/tests/topotests/example-topojson-test/test_topo_json_single_link/test_example_topojson.py b/tests/topotests/example-topojson-test/test_topo_json_single_link/test_example_topojson.py new file mode 100755 index 0000000000..3ae3c9f4fe --- /dev/null +++ b/tests/topotests/example-topojson-test/test_topo_json_single_link/test_example_topojson.py @@ -0,0 +1,193 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2019 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. ("NetDEF") +# in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +.py: Test . +""" + +import os +import sys +import time +import json +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../../")) + +# pylint: disable=C0413 +from lib.topogen import Topogen, get_topogen + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + +# Import topoJson from lib, to create topology and initial configuration +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + verify_rib, +) +from lib.topolog import logger +from lib.bgp import verify_bgp_convergence +from lib.topojson import build_topo_from_json, build_config_from_json + +# Reading the data from JSON File for topology and configuration creation +jsonFile = "{}/example_topojson.json".format(CWD) + +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +# Global variables +bgp_convergence = False +input_dict = {} + + +class TemplateTopo(Topo): + """ + Test topology builder + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # This function only purpose is to create topology + # as defined in input json file. + # + # Example + # + # Creating 2 routers having single links in between, + # which is used to establised BGP neighborship + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(TemplateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen) + + # This function only purpose is to create configuration + # as defined in input json file. + # + # Example + # + # Creating configuration defined in input JSON + # file, example, BGP config, interface config, static routes + # config, prefix list config + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """ + Teardown the pytest environment + + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + +def test_bgp_convergence(request): + " Test BGP daemon convergence " + + tgen = get_topogen() + global bgp_convergence + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Api call verify whether BGP is converged + bgp_convergence = verify_bgp_convergence(tgen, topo) + assert ( + bgp_convergence is True + ), "test_bgp_convergence failed.. \n" " Error: {}".format(bgp_convergence) + + logger.info("BGP is converged successfully \n") + write_test_footer(tc_name) + + +def test_static_routes(request): + " Test to create and verify static routes. " + + tgen = get_topogen() + if bgp_convergence is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Static routes are created as part of initial configuration, + # verifying RIB + dut = "r3" + next_hop = "10.0.0.1" + input_dict = {"r1": topo["routers"]["r1"]} + + # Uncomment below to debug + # tgen.mininet_cli() + result = verify_rib(tgen, "ipv4", dut, input_dict, next_hop=next_hop) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/example-topojson-test/test_topo_json_single_link_loopback/__init__.py b/tests/topotests/example-topojson-test/test_topo_json_single_link_loopback/__init__.py new file mode 100755 index 0000000000..e69de29bb2 diff --git a/tests/topotests/example-topojson-test/test_topo_json_single_link_loopback/example_topojson.json b/tests/topotests/example-topojson-test/test_topo_json_single_link_loopback/example_topojson.json new file mode 100644 index 0000000000..c76c6264be --- /dev/null +++ b/tests/topotests/example-topojson-test/test_topo_json_single_link_loopback/example_topojson.json @@ -0,0 +1,161 @@ +{ + "ipv4base": "10.0.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 30, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:DB8:F::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "lo": { + "source_link": "lo" + } + } + } + } + } + } + } + }, + "static_routes": [ + { + "network": "1.0.2.17/32", + "next_hop": "10.0.0.2" + } + ] + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + { + "redist_type": "static" + } + ], + "neighbor": { + "r1": { + "dest_link": { + "lo": { + "source_link": "lo" + } + } + }, + "r3": { + "dest_link": { + "lo": { + "source_link": "lo" + } + } + } + } + } + } + } + }, + "static_routes": [ + { + "network": "100.0.20.1/32", + "no_of_ip": 9, + "admin_distance": 100, + "next_hop": "10.0.0.1" + }, + { + "network": "1.0.1.17/32", + "next_hop": "10.0.0.1" + }, + { + "network": "1.0.3.17/32", + "next_hop": "10.0.0.6" + } + ] + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "lo": { + "source_link": "lo" + } + } + } + } + } + } + } + }, + "static_routes": [ + { + "network": "1.0.2.17/32", + "next_hop": "10.0.0.5" + }, + { + "network": "10.0.0.1/30", + "next_hop": "10.0.0.5" + } + ] + } + } +} diff --git a/tests/topotests/example-topojson-test/test_topo_json_single_link_loopback/test_example_topojson.py b/tests/topotests/example-topojson-test/test_topo_json_single_link_loopback/test_example_topojson.py new file mode 100755 index 0000000000..06fa2f4626 --- /dev/null +++ b/tests/topotests/example-topojson-test/test_topo_json_single_link_loopback/test_example_topojson.py @@ -0,0 +1,205 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2019 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. +# ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +.py: Test . +""" + +import os +import sys +import time +import json +import inspect +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + +# Import topoJson from lib, to create topology and initial configuration +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + verify_rib, +) +from lib.topolog import logger +from lib.bgp import verify_bgp_convergence +from lib.topojson import build_topo_from_json, build_config_from_json + +# Reading the data from JSON File for topology and configuration creation +jsonFile = "{}/example_topojson.json".format(CWD) + +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +# Global variables +bgp_convergence = False +input_dict = {} + + +class TemplateTopo(Topo): + """ + Test topology builder + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # This function only purpose is to create topology + # as defined in input json file. + # + # Example + # + # Creating 2 routers having single links in between, + # which is used to establised BGP neighborship + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(TemplateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen) + + # This function only purpose is to create configuration + # as defined in input json file. + # + # Example + # + # Creating configuration defined in input JSON + # file, example, BGP config, interface config, static routes + # config, prefix list config + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """ + Teardown the pytest environment + + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + +def test_bgp_convergence(request): + " Test BGP daemon convergence " + + tgen = get_topogen() + global bgp_convergence + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Api call verify whether BGP is converged + bgp_convergence = verify_bgp_convergence(tgen, topo) + assert ( + bgp_convergence is True + ), "test_bgp_convergence failed.. \n" " Error: {}".format(bgp_convergence) + + logger.info("BGP is converged successfully \n") + write_test_footer(tc_name) + + +def test_static_routes(request): + " Test to create and verify static routes. " + + tgen = get_topogen() + if bgp_convergence is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Static routes are created as part of initial configuration, + # verifying RIB + dut = "r3" + next_hop = ["10.0.0.1", "10.0.0.5"] + input_dict = { + "r1": { + "static_routes": [ + { + "network": "100.0.20.1/32", + "no_of_ip": 9, + "admin_distance": 100, + "next_hop": "10.0.0.1", + } + ] + } + } + # Uncomment below to debug + # tgen.mininet_cli() + result = verify_rib(tgen, "ipv4", dut, input_dict, next_hop=next_hop) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/isis-sr-topo1/__init__.py b/tests/topotests/isis-sr-topo1/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/isis-sr-topo1/rt1/isisd.conf b/tests/topotests/isis-sr-topo1/rt1/isisd.conf new file mode 100644 index 0000000000..26ec4eb261 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt1/isisd.conf @@ -0,0 +1,31 @@ +password 1 +hostname rt1 +log file isisd.log +! +debug isis events +debug isis route-events +debug isis spf-events +debug isis sr-events +debug isis lsp-gen +! +interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive +! +interface eth-sw1 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 +! +router isis 1 + net 49.0000.0000.0000.0001.00 + is-type level-1 + lsp-gen-interval 2 + topology ipv6-unicast + segment-routing on + segment-routing global-block 16000 23999 + segment-routing node-msd 8 + segment-routing prefix 1.1.1.1/32 index 10 + segment-routing prefix 2001:db8:1000::1/128 index 11 +! diff --git a/tests/topotests/isis-sr-topo1/rt1/step1/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt1/step1/show_ip_route.ref new file mode 100644 index 0000000000..6b4a59011a --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt1/step1/show_ip_route.ref @@ -0,0 +1,294 @@ +{ + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16020 + ] + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17030 + ] + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16040 + ] + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17050 + ] + } + ] + } + ], + "6.6.6.6\/32":[ + { + "prefix":"6.6.6.6\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":40, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16060 + ] + }, + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17060 + ] + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1" + }, + { + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1" + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.6.0\/24":[ + { + "prefix":"10.0.6.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + }, + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt1/step1/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt1/step1/show_ipv6_route.ref new file mode 100644 index 0000000000..c507688f5b --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt1/step1/show_ipv6_route.ref @@ -0,0 +1,121 @@ +{ + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16021 + ] + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17031 + ] + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16041 + ] + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17051 + ] + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":40, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16061 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17061 + ] + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt1/step1/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt1/step1/show_mpls_table.ref new file mode 100644 index 0000000000..773f5e3d43 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt1/step1/show_mpls_table.ref @@ -0,0 +1,134 @@ +{ + "16020":{ + "inLabel":16020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16020, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "16021":{ + "inLabel":16021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16021, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16030":{ + "inLabel":16030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17030, + "installed":true, + "nexthop":"10.0.1.3" + } + ] + }, + "16031":{ + "inLabel":16031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17031, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16040":{ + "inLabel":16040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16040, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "16041":{ + "inLabel":16041, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16041, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16050":{ + "inLabel":16050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17050, + "installed":true, + "nexthop":"10.0.1.3" + } + ] + }, + "16051":{ + "inLabel":16051, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17051, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16060":{ + "inLabel":16060, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16060, + "installed":true, + "nexthop":"10.0.1.2" + }, + { + "type":"SR (IS-IS)", + "outLabel":17060, + "installed":true, + "nexthop":"10.0.1.3" + } + ] + }, + "16061":{ + "inLabel":16061, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16061, + "installed":true, + "interface":"eth-sw1" + }, + { + "type":"SR (IS-IS)", + "outLabel":17061, + "installed":true, + "interface":"eth-sw1" + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt1/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt1/step1/show_yang_interface_isis_adjacencies.ref new file mode 100644 index 0000000000..26f0dffa7a --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt1/step1/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1,32 @@ +{ + "frr-interface:lib": { + "interface": [ + { + "name": "eth-sw1", + "vrf": "default", + "state": { + "frr-isisd:isis": { + "adjacencies": { + "adjacency": [ + { + "neighbor-sys-type": "level-1", + "neighbor-sysid": "0000.0000.0003", + "hold-timer": 9, + "neighbor-priority": 64, + "state": "up" + }, + { + "neighbor-sys-type": "level-1", + "neighbor-sysid": "0000.0000.0002", + "hold-timer": 9, + "neighbor-priority": 64, + "state": "up" + } + ] + } + } + } + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt1/step10/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt1/step10/show_ip_route.ref new file mode 100644 index 0000000000..6b34d5e4ff --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt1/step10/show_ip_route.ref @@ -0,0 +1,287 @@ +{ + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16020 + ] + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17030 + ] + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16040 + ] + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17050 + ] + } + ] + } + ], + "6.6.6.6\/32":[ + { + "prefix":"6.6.6.6\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":40, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16060 + ] + }, + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17060 + ] + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1" + }, + { + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1" + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.6.0\/24":[ + { + "prefix":"10.0.6.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt1/step10/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt1/step10/show_ipv6_route.ref new file mode 100644 index 0000000000..c507688f5b --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt1/step10/show_ipv6_route.ref @@ -0,0 +1,121 @@ +{ + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16021 + ] + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17031 + ] + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16041 + ] + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17051 + ] + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":40, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16061 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17061 + ] + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt1/step10/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt1/step10/show_mpls_table.ref new file mode 100644 index 0000000000..25a48c2bfc --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt1/step10/show_mpls_table.ref @@ -0,0 +1,156 @@ +{ + "16010":{ + "inLabel":16010, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16011":{ + "inLabel":16011, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16020":{ + "inLabel":16020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16020, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "16021":{ + "inLabel":16021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16021, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16030":{ + "inLabel":16030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17030, + "installed":true, + "nexthop":"10.0.1.3" + } + ] + }, + "16031":{ + "inLabel":16031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17031, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16040":{ + "inLabel":16040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16040, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "16041":{ + "inLabel":16041, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16041, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16050":{ + "inLabel":16050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17050, + "installed":true, + "nexthop":"10.0.1.3" + } + ] + }, + "16051":{ + "inLabel":16051, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17051, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16060":{ + "inLabel":16060, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16060, + "installed":true, + "nexthop":"10.0.1.2" + }, + { + "type":"SR (IS-IS)", + "outLabel":17060, + "installed":true, + "nexthop":"10.0.1.3" + } + ] + }, + "16061":{ + "inLabel":16061, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16061, + "installed":true, + "interface":"eth-sw1" + }, + { + "type":"SR (IS-IS)", + "outLabel":17061, + "installed":true, + "interface":"eth-sw1" + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt1/step10/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt1/step10/show_yang_interface_isis_adjacencies.ref new file mode 120000 index 0000000000..0879b84d23 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt1/step10/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1 @@ +../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file diff --git a/tests/topotests/isis-sr-topo1/rt1/step2/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt1/step2/show_ip_route.ref new file mode 100644 index 0000000000..6b34d5e4ff --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt1/step2/show_ip_route.ref @@ -0,0 +1,287 @@ +{ + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16020 + ] + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17030 + ] + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16040 + ] + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17050 + ] + } + ] + } + ], + "6.6.6.6\/32":[ + { + "prefix":"6.6.6.6\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":40, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16060 + ] + }, + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17060 + ] + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1" + }, + { + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1" + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.6.0\/24":[ + { + "prefix":"10.0.6.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt1/step2/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt1/step2/show_ipv6_route.ref new file mode 100644 index 0000000000..c507688f5b --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt1/step2/show_ipv6_route.ref @@ -0,0 +1,121 @@ +{ + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16021 + ] + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17031 + ] + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16041 + ] + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17051 + ] + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":40, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16061 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17061 + ] + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt1/step2/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt1/step2/show_mpls_table.ref new file mode 100644 index 0000000000..773f5e3d43 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt1/step2/show_mpls_table.ref @@ -0,0 +1,134 @@ +{ + "16020":{ + "inLabel":16020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16020, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "16021":{ + "inLabel":16021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16021, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16030":{ + "inLabel":16030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17030, + "installed":true, + "nexthop":"10.0.1.3" + } + ] + }, + "16031":{ + "inLabel":16031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17031, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16040":{ + "inLabel":16040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16040, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "16041":{ + "inLabel":16041, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16041, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16050":{ + "inLabel":16050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17050, + "installed":true, + "nexthop":"10.0.1.3" + } + ] + }, + "16051":{ + "inLabel":16051, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17051, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16060":{ + "inLabel":16060, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16060, + "installed":true, + "nexthop":"10.0.1.2" + }, + { + "type":"SR (IS-IS)", + "outLabel":17060, + "installed":true, + "nexthop":"10.0.1.3" + } + ] + }, + "16061":{ + "inLabel":16061, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16061, + "installed":true, + "interface":"eth-sw1" + }, + { + "type":"SR (IS-IS)", + "outLabel":17061, + "installed":true, + "interface":"eth-sw1" + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt1/step2/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt1/step2/show_yang_interface_isis_adjacencies.ref new file mode 120000 index 0000000000..0879b84d23 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt1/step2/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1 @@ +../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file diff --git a/tests/topotests/isis-sr-topo1/rt1/step3/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt1/step3/show_ip_route.ref new file mode 100644 index 0000000000..05a8498693 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt1/step3/show_ip_route.ref @@ -0,0 +1,254 @@ +{ + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16020 + ] + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17030 + ] + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16040 + ] + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17050 + ] + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1" + }, + { + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1" + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.6.0\/24":[ + { + "prefix":"10.0.6.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt1/step3/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt1/step3/show_ipv6_route.ref new file mode 100644 index 0000000000..d50952c6c4 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt1/step3/show_ipv6_route.ref @@ -0,0 +1,90 @@ +{ + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16021 + ] + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17031 + ] + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16041 + ] + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17051 + ] + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt1/step3/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt1/step3/show_mpls_table.ref new file mode 100644 index 0000000000..73f517a6e5 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt1/step3/show_mpls_table.ref @@ -0,0 +1,98 @@ +{ + "16020":{ + "inLabel":16020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16020, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "16021":{ + "inLabel":16021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16021, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16030":{ + "inLabel":16030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17030, + "installed":true, + "nexthop":"10.0.1.3" + } + ] + }, + "16031":{ + "inLabel":16031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17031, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16040":{ + "inLabel":16040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16040, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "16041":{ + "inLabel":16041, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16041, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16050":{ + "inLabel":16050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17050, + "installed":true, + "nexthop":"10.0.1.3" + } + ] + }, + "16051":{ + "inLabel":16051, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17051, + "installed":true, + "interface":"eth-sw1" + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt1/step3/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt1/step3/show_yang_interface_isis_adjacencies.ref new file mode 120000 index 0000000000..0879b84d23 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt1/step3/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1 @@ +../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file diff --git a/tests/topotests/isis-sr-topo1/rt1/step4/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt1/step4/show_ip_route.ref new file mode 100644 index 0000000000..6b34d5e4ff --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt1/step4/show_ip_route.ref @@ -0,0 +1,287 @@ +{ + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16020 + ] + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17030 + ] + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16040 + ] + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17050 + ] + } + ] + } + ], + "6.6.6.6\/32":[ + { + "prefix":"6.6.6.6\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":40, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16060 + ] + }, + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17060 + ] + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1" + }, + { + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1" + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.6.0\/24":[ + { + "prefix":"10.0.6.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt1/step4/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt1/step4/show_ipv6_route.ref new file mode 100644 index 0000000000..c507688f5b --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt1/step4/show_ipv6_route.ref @@ -0,0 +1,121 @@ +{ + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16021 + ] + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17031 + ] + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16041 + ] + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17051 + ] + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":40, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16061 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17061 + ] + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt1/step4/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt1/step4/show_mpls_table.ref new file mode 100644 index 0000000000..ac39920ee5 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt1/step4/show_mpls_table.ref @@ -0,0 +1,134 @@ +{ + "16020":{ + "inLabel":16020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16020, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "16021":{ + "inLabel":16021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16021, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16030":{ + "inLabel":16030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17030, + "installed":true, + "nexthop":"10.0.1.3" + } + ] + }, + "16031":{ + "inLabel":16031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17031, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16040":{ + "inLabel":16040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16040, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "16041":{ + "inLabel":16041, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16041, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16050":{ + "inLabel":16050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17050, + "installed":true, + "nexthop":"10.0.1.3" + } + ] + }, + "16051":{ + "inLabel":16051, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17051, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16060":{ + "inLabel":16060, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16060, + "installed":true, + "nexthop":"10.0.1.2" + }, + { + "type":"SR (IS-IS)", + "outLabel":17060, + "installed":true, + "nexthop":"10.0.1.3" + } + ] + }, + "16061":{ + "inLabel":16061, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17061, + "installed":true, + "interface":"eth-sw1" + }, + { + "type":"SR (IS-IS)", + "outLabel":16061, + "installed":true, + "interface":"eth-sw1" + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt1/step4/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt1/step4/show_yang_interface_isis_adjacencies.ref new file mode 120000 index 0000000000..0879b84d23 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt1/step4/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1 @@ +../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file diff --git a/tests/topotests/isis-sr-topo1/rt1/step5/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt1/step5/show_ip_route.ref new file mode 100644 index 0000000000..59213686f2 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt1/step5/show_ip_route.ref @@ -0,0 +1,281 @@ +{ + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16020 + ] + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17030 + ] + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16040 + ] + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17050 + ] + } + ] + } + ], + "6.6.6.6\/32":[ + { + "prefix":"6.6.6.6\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":40, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + }, + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1" + }, + { + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1" + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.6.0\/24":[ + { + "prefix":"10.0.6.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt1/step5/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt1/step5/show_ipv6_route.ref new file mode 100644 index 0000000000..cdfae284ba --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt1/step5/show_ipv6_route.ref @@ -0,0 +1,115 @@ +{ + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16021 + ] + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17031 + ] + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16041 + ] + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17051 + ] + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":40, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt1/step5/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt1/step5/show_mpls_table.ref new file mode 100644 index 0000000000..73f517a6e5 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt1/step5/show_mpls_table.ref @@ -0,0 +1,98 @@ +{ + "16020":{ + "inLabel":16020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16020, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "16021":{ + "inLabel":16021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16021, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16030":{ + "inLabel":16030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17030, + "installed":true, + "nexthop":"10.0.1.3" + } + ] + }, + "16031":{ + "inLabel":16031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17031, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16040":{ + "inLabel":16040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16040, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "16041":{ + "inLabel":16041, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16041, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16050":{ + "inLabel":16050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17050, + "installed":true, + "nexthop":"10.0.1.3" + } + ] + }, + "16051":{ + "inLabel":16051, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17051, + "installed":true, + "interface":"eth-sw1" + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt1/step5/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt1/step5/show_yang_interface_isis_adjacencies.ref new file mode 120000 index 0000000000..0879b84d23 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt1/step5/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1 @@ +../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file diff --git a/tests/topotests/isis-sr-topo1/rt1/step6/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt1/step6/show_ip_route.ref new file mode 100644 index 0000000000..6b34d5e4ff --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt1/step6/show_ip_route.ref @@ -0,0 +1,287 @@ +{ + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16020 + ] + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17030 + ] + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16040 + ] + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17050 + ] + } + ] + } + ], + "6.6.6.6\/32":[ + { + "prefix":"6.6.6.6\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":40, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16060 + ] + }, + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17060 + ] + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1" + }, + { + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1" + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.6.0\/24":[ + { + "prefix":"10.0.6.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt1/step6/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt1/step6/show_ipv6_route.ref new file mode 100644 index 0000000000..c507688f5b --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt1/step6/show_ipv6_route.ref @@ -0,0 +1,121 @@ +{ + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16021 + ] + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17031 + ] + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16041 + ] + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17051 + ] + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":40, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16061 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17061 + ] + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt1/step6/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt1/step6/show_mpls_table.ref new file mode 100644 index 0000000000..773f5e3d43 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt1/step6/show_mpls_table.ref @@ -0,0 +1,134 @@ +{ + "16020":{ + "inLabel":16020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16020, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "16021":{ + "inLabel":16021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16021, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16030":{ + "inLabel":16030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17030, + "installed":true, + "nexthop":"10.0.1.3" + } + ] + }, + "16031":{ + "inLabel":16031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17031, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16040":{ + "inLabel":16040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16040, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "16041":{ + "inLabel":16041, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16041, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16050":{ + "inLabel":16050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17050, + "installed":true, + "nexthop":"10.0.1.3" + } + ] + }, + "16051":{ + "inLabel":16051, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17051, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16060":{ + "inLabel":16060, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16060, + "installed":true, + "nexthop":"10.0.1.2" + }, + { + "type":"SR (IS-IS)", + "outLabel":17060, + "installed":true, + "nexthop":"10.0.1.3" + } + ] + }, + "16061":{ + "inLabel":16061, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16061, + "installed":true, + "interface":"eth-sw1" + }, + { + "type":"SR (IS-IS)", + "outLabel":17061, + "installed":true, + "interface":"eth-sw1" + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt1/step6/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt1/step6/show_yang_interface_isis_adjacencies.ref new file mode 120000 index 0000000000..0879b84d23 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt1/step6/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1 @@ +../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file diff --git a/tests/topotests/isis-sr-topo1/rt1/step7/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt1/step7/show_ip_route.ref new file mode 100644 index 0000000000..6b34d5e4ff --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt1/step7/show_ip_route.ref @@ -0,0 +1,287 @@ +{ + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16020 + ] + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17030 + ] + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16040 + ] + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17050 + ] + } + ] + } + ], + "6.6.6.6\/32":[ + { + "prefix":"6.6.6.6\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":40, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16060 + ] + }, + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17060 + ] + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1" + }, + { + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1" + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.6.0\/24":[ + { + "prefix":"10.0.6.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt1/step7/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt1/step7/show_ipv6_route.ref new file mode 100644 index 0000000000..c507688f5b --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt1/step7/show_ipv6_route.ref @@ -0,0 +1,121 @@ +{ + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16021 + ] + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17031 + ] + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16041 + ] + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17051 + ] + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":40, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16061 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17061 + ] + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt1/step7/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt1/step7/show_mpls_table.ref new file mode 100644 index 0000000000..773f5e3d43 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt1/step7/show_mpls_table.ref @@ -0,0 +1,134 @@ +{ + "16020":{ + "inLabel":16020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16020, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "16021":{ + "inLabel":16021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16021, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16030":{ + "inLabel":16030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17030, + "installed":true, + "nexthop":"10.0.1.3" + } + ] + }, + "16031":{ + "inLabel":16031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17031, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16040":{ + "inLabel":16040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16040, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "16041":{ + "inLabel":16041, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16041, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16050":{ + "inLabel":16050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17050, + "installed":true, + "nexthop":"10.0.1.3" + } + ] + }, + "16051":{ + "inLabel":16051, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17051, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16060":{ + "inLabel":16060, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16060, + "installed":true, + "nexthop":"10.0.1.2" + }, + { + "type":"SR (IS-IS)", + "outLabel":17060, + "installed":true, + "nexthop":"10.0.1.3" + } + ] + }, + "16061":{ + "inLabel":16061, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16061, + "installed":true, + "interface":"eth-sw1" + }, + { + "type":"SR (IS-IS)", + "outLabel":17061, + "installed":true, + "interface":"eth-sw1" + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt1/step7/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt1/step7/show_yang_interface_isis_adjacencies.ref new file mode 120000 index 0000000000..0879b84d23 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt1/step7/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1 @@ +../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file diff --git a/tests/topotests/isis-sr-topo1/rt1/step8/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt1/step8/show_ip_route.ref new file mode 100644 index 0000000000..6b34d5e4ff --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt1/step8/show_ip_route.ref @@ -0,0 +1,287 @@ +{ + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16020 + ] + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17030 + ] + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16040 + ] + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17050 + ] + } + ] + } + ], + "6.6.6.6\/32":[ + { + "prefix":"6.6.6.6\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":40, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16060 + ] + }, + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17060 + ] + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1" + }, + { + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1" + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.6.0\/24":[ + { + "prefix":"10.0.6.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt1/step8/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt1/step8/show_ipv6_route.ref new file mode 100644 index 0000000000..c507688f5b --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt1/step8/show_ipv6_route.ref @@ -0,0 +1,121 @@ +{ + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16021 + ] + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17031 + ] + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16041 + ] + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17051 + ] + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":40, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16061 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17061 + ] + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt1/step8/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt1/step8/show_mpls_table.ref new file mode 100644 index 0000000000..773f5e3d43 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt1/step8/show_mpls_table.ref @@ -0,0 +1,134 @@ +{ + "16020":{ + "inLabel":16020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16020, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "16021":{ + "inLabel":16021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16021, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16030":{ + "inLabel":16030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17030, + "installed":true, + "nexthop":"10.0.1.3" + } + ] + }, + "16031":{ + "inLabel":16031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17031, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16040":{ + "inLabel":16040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16040, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "16041":{ + "inLabel":16041, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16041, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16050":{ + "inLabel":16050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17050, + "installed":true, + "nexthop":"10.0.1.3" + } + ] + }, + "16051":{ + "inLabel":16051, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17051, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16060":{ + "inLabel":16060, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16060, + "installed":true, + "nexthop":"10.0.1.2" + }, + { + "type":"SR (IS-IS)", + "outLabel":17060, + "installed":true, + "nexthop":"10.0.1.3" + } + ] + }, + "16061":{ + "inLabel":16061, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16061, + "installed":true, + "interface":"eth-sw1" + }, + { + "type":"SR (IS-IS)", + "outLabel":17061, + "installed":true, + "interface":"eth-sw1" + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt1/step8/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt1/step8/show_yang_interface_isis_adjacencies.ref new file mode 120000 index 0000000000..0879b84d23 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt1/step8/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1 @@ +../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file diff --git a/tests/topotests/isis-sr-topo1/rt1/step9/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt1/step9/show_ip_route.ref new file mode 100644 index 0000000000..6b34d5e4ff --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt1/step9/show_ip_route.ref @@ -0,0 +1,287 @@ +{ + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16020 + ] + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17030 + ] + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16040 + ] + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17050 + ] + } + ] + } + ], + "6.6.6.6\/32":[ + { + "prefix":"6.6.6.6\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":40, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16060 + ] + }, + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17060 + ] + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1" + }, + { + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1" + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.6.0\/24":[ + { + "prefix":"10.0.6.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt1/step9/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt1/step9/show_ipv6_route.ref new file mode 100644 index 0000000000..c507688f5b --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt1/step9/show_ipv6_route.ref @@ -0,0 +1,121 @@ +{ + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16021 + ] + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17031 + ] + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16041 + ] + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17051 + ] + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":40, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16061 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17061 + ] + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt1/step9/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt1/step9/show_mpls_table.ref new file mode 100644 index 0000000000..25a48c2bfc --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt1/step9/show_mpls_table.ref @@ -0,0 +1,156 @@ +{ + "16010":{ + "inLabel":16010, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16011":{ + "inLabel":16011, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16020":{ + "inLabel":16020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16020, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "16021":{ + "inLabel":16021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16021, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16030":{ + "inLabel":16030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17030, + "installed":true, + "nexthop":"10.0.1.3" + } + ] + }, + "16031":{ + "inLabel":16031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17031, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16040":{ + "inLabel":16040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16040, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "16041":{ + "inLabel":16041, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16041, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16050":{ + "inLabel":16050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17050, + "installed":true, + "nexthop":"10.0.1.3" + } + ] + }, + "16051":{ + "inLabel":16051, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17051, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16060":{ + "inLabel":16060, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16060, + "installed":true, + "nexthop":"10.0.1.2" + }, + { + "type":"SR (IS-IS)", + "outLabel":17060, + "installed":true, + "nexthop":"10.0.1.3" + } + ] + }, + "16061":{ + "inLabel":16061, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16061, + "installed":true, + "interface":"eth-sw1" + }, + { + "type":"SR (IS-IS)", + "outLabel":17061, + "installed":true, + "interface":"eth-sw1" + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt1/step9/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt1/step9/show_yang_interface_isis_adjacencies.ref new file mode 120000 index 0000000000..0879b84d23 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt1/step9/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1 @@ +../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file diff --git a/tests/topotests/isis-sr-topo1/rt1/zebra.conf b/tests/topotests/isis-sr-topo1/rt1/zebra.conf new file mode 100644 index 0000000000..9d71d3005f --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt1/zebra.conf @@ -0,0 +1,19 @@ +log file zebra.log +! +hostname rt1 +! +debug zebra kernel +debug zebra packet +debug zebra mpls +! +interface lo + ip address 1.1.1.1/32 + ipv6 address 2001:db8:1000::1/128 +! +interface eth-sw1 + ip address 10.0.1.1/24 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/isis-sr-topo1/rt2/isisd.conf b/tests/topotests/isis-sr-topo1/rt2/isisd.conf new file mode 100644 index 0000000000..8704a28b6c --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt2/isisd.conf @@ -0,0 +1,42 @@ +hostname rt2 +log file isisd.log +! +debug isis events +debug isis route-events +debug isis spf-events +debug isis sr-events +debug isis lsp-gen +! +interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive +! +interface eth-sw1 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 +! +interface eth-rt4-1 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 +! +interface eth-rt4-2 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 +! +router isis 1 + net 49.0000.0000.0000.0002.00 + is-type level-1 + lsp-gen-interval 2 + topology ipv6-unicast + segment-routing on + segment-routing global-block 16000 23999 + segment-routing node-msd 8 + segment-routing prefix 2.2.2.2/32 index 20 no-php-flag + segment-routing prefix 2001:db8:1000::2/128 index 21 no-php-flag +! diff --git a/tests/topotests/isis-sr-topo1/rt2/step1/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt2/step1/show_ip_route.ref new file mode 100644 index 0000000000..be037aba8b --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt2/step1/show_ip_route.ref @@ -0,0 +1,347 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 3 + ] + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17030 + ] + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-1", + "active":true, + "labels":[ + 16040 + ] + }, + { + "fib":true, + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2", + "active":true, + "labels":[ + 16040 + ] + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17050 + ] + }, + { + "fib":true, + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-1", + "active":true, + "labels":[ + 16050 + ] + }, + { + "fib":true, + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2", + "active":true, + "labels":[ + 16050 + ] + } + ] + } + ], + "6.6.6.6\/32":[ + { + "prefix":"6.6.6.6\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-1", + "active":true, + "labels":[ + 16060 + ] + }, + { + "fib":true, + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2", + "active":true, + "labels":[ + 16060 + ] + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceName":"eth-sw1" + }, + { + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1" + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-1" + }, + { + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-1", + "active":true + }, + { + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2" + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.6.0\/24":[ + { + "prefix":"10.0.6.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2", + "active":true + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2", + "active":true + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + }, + { + "fib":true, + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt2/step1/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt2/step1/show_ipv6_route.ref new file mode 100644 index 0000000000..a888198ac8 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt2/step1/show_ipv6_route.ref @@ -0,0 +1,148 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 3 + ] + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17031 + ] + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4-1", + "active":true, + "labels":[ + 16041 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4-2", + "active":true, + "labels":[ + 16041 + ] + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4-1", + "active":true, + "labels":[ + 16051 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4-2", + "active":true, + "labels":[ + 16051 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17051 + ] + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4-1", + "active":true, + "labels":[ + 16061 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4-2", + "active":true, + "labels":[ + 16061 + ] + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt2/step1/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt2/step1/show_mpls_table.ref new file mode 100644 index 0000000000..42fde2d77f --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt2/step1/show_mpls_table.ref @@ -0,0 +1,192 @@ +{ + "16010":{ + "inLabel":16010, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true, + "nexthop":"10.0.1.1" + } + ] + }, + "16011":{ + "inLabel":16011, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16020":{ + "inLabel":16020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16021":{ + "inLabel":16021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16030":{ + "inLabel":16030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17030, + "installed":true, + "nexthop":"10.0.1.3" + } + ] + }, + "16031":{ + "inLabel":16031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17031, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16040":{ + "inLabel":16040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16040, + "installed":true, + "nexthop":"10.0.3.4" + }, + { + "type":"SR (IS-IS)", + "outLabel":16040, + "installed":true, + "nexthop":"10.0.2.4" + } + ] + }, + "16041":{ + "inLabel":16041, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16041, + "installed":true, + "interface":"eth-rt4-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16041, + "installed":true, + "interface":"eth-rt4-1" + } + ] + }, + "16050":{ + "inLabel":16050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17050, + "installed":true, + "nexthop":"10.0.1.3" + }, + { + "type":"SR (IS-IS)", + "outLabel":16050, + "installed":true, + "nexthop":"10.0.3.4" + }, + { + "type":"SR (IS-IS)", + "outLabel":16050, + "installed":true, + "nexthop":"10.0.2.4" + } + ] + }, + "16051":{ + "inLabel":16051, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17051, + "installed":true, + "interface":"eth-sw1" + }, + { + "type":"SR (IS-IS)", + "outLabel":16051, + "installed":true, + "interface":"eth-rt4-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16051, + "installed":true, + "interface":"eth-rt4-1" + } + ] + }, + "16060":{ + "inLabel":16060, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16060, + "installed":true, + "nexthop":"10.0.3.4" + }, + { + "type":"SR (IS-IS)", + "outLabel":16060, + "installed":true, + "nexthop":"10.0.2.4" + } + ] + }, + "16061":{ + "inLabel":16061, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16061, + "installed":true, + "interface":"eth-rt4-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16061, + "installed":true, + "interface":"eth-rt4-1" + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt2/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt2/step1/show_yang_interface_isis_adjacencies.ref new file mode 100644 index 0000000000..07f43e5999 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt2/step1/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1,70 @@ +{ + "frr-interface:lib": { + "interface": [ + { + "name": "eth-rt4-1", + "vrf": "default", + "state": { + "frr-isisd:isis": { + "adjacencies": { + "adjacency": [ + { + "neighbor-sys-type": "level-1", + "neighbor-sysid": "0000.0000.0004", + "hold-timer": 9, + "neighbor-priority": 0, + "state": "up" + } + ] + } + } + } + }, + { + "name": "eth-rt4-2", + "vrf": "default", + "state": { + "frr-isisd:isis": { + "adjacencies": { + "adjacency": [ + { + "neighbor-sys-type": "level-1", + "neighbor-sysid": "0000.0000.0004", + "hold-timer": 9, + "neighbor-priority": 0, + "state": "up" + } + ] + } + } + } + }, + { + "name": "eth-sw1", + "vrf": "default", + "state": { + "frr-isisd:isis": { + "adjacencies": { + "adjacency": [ + { + "neighbor-sys-type": "level-1", + "neighbor-sysid": "0000.0000.0003", + "hold-timer": 9, + "neighbor-priority": 64, + "state": "up" + }, + { + "neighbor-sys-type": "level-1", + "neighbor-sysid": "0000.0000.0001", + "hold-timer": 9, + "neighbor-priority": 64, + "state": "up" + } + ] + } + } + } + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt2/step10/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt2/step10/show_ip_route.ref new file mode 100644 index 0000000000..33fbdba28f --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt2/step10/show_ip_route.ref @@ -0,0 +1,259 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16010 + ] + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17030 + ] + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2", + "active":true, + "labels":[ + 16040 + ] + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17050 + ] + } + ] + } + ], + "6.6.6.6\/32":[ + { + "prefix":"6.6.6.6\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2", + "active":true, + "labels":[ + 16060 + ] + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceName":"eth-sw1" + }, + { + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1" + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2" + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.6.0\/24":[ + { + "prefix":"10.0.6.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2", + "active":true + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + }, + { + "fib":true, + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt2/step10/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt2/step10/show_ipv6_route.ref new file mode 100644 index 0000000000..19837bc700 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt2/step10/show_ipv6_route.ref @@ -0,0 +1,130 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16011 + ] + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17031 + ] + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4-1", + "active":true, + "labels":[ + 16041 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4-2", + "active":true, + "labels":[ + 16041 + ] + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17051 + ] + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4-1", + "active":true, + "labels":[ + 16061 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4-2", + "active":true, + "labels":[ + 16061 + ] + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt2/step10/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt2/step10/show_mpls_table.ref new file mode 100644 index 0000000000..29ec55a589 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt2/step10/show_mpls_table.ref @@ -0,0 +1,156 @@ +{ + "16010":{ + "inLabel":16010, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16010, + "installed":true, + "nexthop":"10.0.1.1" + } + ] + }, + "16011":{ + "inLabel":16011, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16011, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16020":{ + "inLabel":16020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16021":{ + "inLabel":16021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16030":{ + "inLabel":16030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17030, + "installed":true, + "nexthop":"10.0.1.3" + } + ] + }, + "16031":{ + "inLabel":16031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17031, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16040":{ + "inLabel":16040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16040, + "installed":true, + "nexthop":"10.0.3.4" + } + ] + }, + "16041":{ + "inLabel":16041, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16041, + "installed":true, + "interface":"eth-rt4-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16041, + "installed":true, + "interface":"eth-rt4-1" + } + ] + }, + "16050":{ + "inLabel":16050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17050, + "installed":true, + "nexthop":"10.0.1.3" + } + ] + }, + "16051":{ + "inLabel":16051, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17051, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16060":{ + "inLabel":16060, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16060, + "installed":true, + "nexthop":"10.0.3.4" + } + ] + }, + "16061":{ + "inLabel":16061, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16061, + "installed":true, + "interface":"eth-rt4-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16061, + "installed":true, + "interface":"eth-rt4-1" + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt2/step10/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt2/step10/show_yang_interface_isis_adjacencies.ref new file mode 120000 index 0000000000..0879b84d23 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt2/step10/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1 @@ +../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file diff --git a/tests/topotests/isis-sr-topo1/rt2/step2/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt2/step2/show_ip_route.ref new file mode 100644 index 0000000000..a110c51077 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt2/step2/show_ip_route.ref @@ -0,0 +1,320 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 3 + ] + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17030 + ] + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-1", + "active":true, + "labels":[ + 16040 + ] + }, + { + "fib":true, + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2", + "active":true, + "labels":[ + 16040 + ] + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17050 + ] + } + ] + } + ], + "6.6.6.6\/32":[ + { + "prefix":"6.6.6.6\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-1", + "active":true, + "labels":[ + 16060 + ] + }, + { + "fib":true, + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2", + "active":true, + "labels":[ + 16060 + ] + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceName":"eth-sw1" + }, + { + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1" + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-1" + }, + { + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-1", + "active":true + }, + { + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2" + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.6.0\/24":[ + { + "prefix":"10.0.6.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2", + "active":true + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + }, + { + "fib":true, + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt2/step2/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt2/step2/show_ipv6_route.ref new file mode 100644 index 0000000000..cb426897ce --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt2/step2/show_ipv6_route.ref @@ -0,0 +1,130 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 3 + ] + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17031 + ] + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4-1", + "active":true, + "labels":[ + 16041 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4-2", + "active":true, + "labels":[ + 16041 + ] + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17051 + ] + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4-1", + "active":true, + "labels":[ + 16061 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4-2", + "active":true, + "labels":[ + 16061 + ] + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt2/step2/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt2/step2/show_mpls_table.ref new file mode 100644 index 0000000000..118ec89b5c --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt2/step2/show_mpls_table.ref @@ -0,0 +1,168 @@ +{ + "16010":{ + "inLabel":16010, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true, + "nexthop":"10.0.1.1" + } + ] + }, + "16011":{ + "inLabel":16011, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16020":{ + "inLabel":16020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16021":{ + "inLabel":16021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16030":{ + "inLabel":16030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17030, + "installed":true, + "nexthop":"10.0.1.3" + } + ] + }, + "16031":{ + "inLabel":16031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17031, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16040":{ + "inLabel":16040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16040, + "installed":true, + "nexthop":"10.0.3.4" + }, + { + "type":"SR (IS-IS)", + "outLabel":16040, + "installed":true, + "nexthop":"10.0.2.4" + } + ] + }, + "16041":{ + "inLabel":16041, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16041, + "installed":true, + "interface":"eth-rt4-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16041, + "installed":true, + "interface":"eth-rt4-1" + } + ] + }, + "16050":{ + "inLabel":16050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17050, + "installed":true, + "nexthop":"10.0.1.3" + } + ] + }, + "16051":{ + "inLabel":16051, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17051, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16060":{ + "inLabel":16060, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16060, + "installed":true, + "nexthop":"10.0.3.4" + }, + { + "type":"SR (IS-IS)", + "outLabel":16060, + "installed":true, + "nexthop":"10.0.2.4" + } + ] + }, + "16061":{ + "inLabel":16061, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16061, + "installed":true, + "interface":"eth-rt4-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16061, + "installed":true, + "interface":"eth-rt4-1" + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt2/step2/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt2/step2/show_yang_interface_isis_adjacencies.ref new file mode 120000 index 0000000000..0879b84d23 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt2/step2/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1 @@ +../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file diff --git a/tests/topotests/isis-sr-topo1/rt2/step3/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt2/step3/show_ip_route.ref new file mode 100644 index 0000000000..d31affeb59 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt2/step3/show_ip_route.ref @@ -0,0 +1,273 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 3 + ] + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17030 + ] + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-1", + "active":true, + "labels":[ + 16040 + ] + }, + { + "fib":true, + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2", + "active":true, + "labels":[ + 16040 + ] + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17050 + ] + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceName":"eth-sw1" + }, + { + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1" + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-1" + }, + { + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-1", + "active":true + }, + { + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2" + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.6.0\/24":[ + { + "prefix":"10.0.6.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2", + "active":true + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt2/step3/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt2/step3/show_ipv6_route.ref new file mode 100644 index 0000000000..d92df1918c --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt2/step3/show_ipv6_route.ref @@ -0,0 +1,99 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 3 + ] + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17031 + ] + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4-1", + "active":true, + "labels":[ + 16041 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4-2", + "active":true, + "labels":[ + 16041 + ] + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17051 + ] + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt2/step3/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt2/step3/show_mpls_table.ref new file mode 100644 index 0000000000..f1e18be26b --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt2/step3/show_mpls_table.ref @@ -0,0 +1,132 @@ +{ + "16010":{ + "inLabel":16010, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true, + "nexthop":"10.0.1.1" + } + ] + }, + "16011":{ + "inLabel":16011, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16020":{ + "inLabel":16020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16021":{ + "inLabel":16021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16030":{ + "inLabel":16030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17030, + "installed":true, + "nexthop":"10.0.1.3" + } + ] + }, + "16031":{ + "inLabel":16031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17031, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16040":{ + "inLabel":16040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16040, + "installed":true, + "nexthop":"10.0.3.4" + }, + { + "type":"SR (IS-IS)", + "outLabel":16040, + "installed":true, + "nexthop":"10.0.2.4" + } + ] + }, + "16041":{ + "inLabel":16041, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16041, + "installed":true, + "interface":"eth-rt4-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16041, + "installed":true, + "interface":"eth-rt4-1" + } + ] + }, + "16050":{ + "inLabel":16050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17050, + "installed":true, + "nexthop":"10.0.1.3" + } + ] + }, + "16051":{ + "inLabel":16051, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17051, + "installed":true, + "interface":"eth-sw1" + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt2/step3/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt2/step3/show_yang_interface_isis_adjacencies.ref new file mode 120000 index 0000000000..0879b84d23 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt2/step3/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1 @@ +../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file diff --git a/tests/topotests/isis-sr-topo1/rt2/step4/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt2/step4/show_ip_route.ref new file mode 100644 index 0000000000..a110c51077 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt2/step4/show_ip_route.ref @@ -0,0 +1,320 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 3 + ] + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17030 + ] + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-1", + "active":true, + "labels":[ + 16040 + ] + }, + { + "fib":true, + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2", + "active":true, + "labels":[ + 16040 + ] + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17050 + ] + } + ] + } + ], + "6.6.6.6\/32":[ + { + "prefix":"6.6.6.6\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-1", + "active":true, + "labels":[ + 16060 + ] + }, + { + "fib":true, + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2", + "active":true, + "labels":[ + 16060 + ] + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceName":"eth-sw1" + }, + { + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1" + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-1" + }, + { + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-1", + "active":true + }, + { + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2" + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.6.0\/24":[ + { + "prefix":"10.0.6.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2", + "active":true + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + }, + { + "fib":true, + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt2/step4/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt2/step4/show_ipv6_route.ref new file mode 100644 index 0000000000..cb426897ce --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt2/step4/show_ipv6_route.ref @@ -0,0 +1,130 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 3 + ] + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17031 + ] + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4-1", + "active":true, + "labels":[ + 16041 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4-2", + "active":true, + "labels":[ + 16041 + ] + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17051 + ] + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4-1", + "active":true, + "labels":[ + 16061 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4-2", + "active":true, + "labels":[ + 16061 + ] + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt2/step4/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt2/step4/show_mpls_table.ref new file mode 100644 index 0000000000..118ec89b5c --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt2/step4/show_mpls_table.ref @@ -0,0 +1,168 @@ +{ + "16010":{ + "inLabel":16010, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true, + "nexthop":"10.0.1.1" + } + ] + }, + "16011":{ + "inLabel":16011, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16020":{ + "inLabel":16020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16021":{ + "inLabel":16021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16030":{ + "inLabel":16030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17030, + "installed":true, + "nexthop":"10.0.1.3" + } + ] + }, + "16031":{ + "inLabel":16031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17031, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16040":{ + "inLabel":16040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16040, + "installed":true, + "nexthop":"10.0.3.4" + }, + { + "type":"SR (IS-IS)", + "outLabel":16040, + "installed":true, + "nexthop":"10.0.2.4" + } + ] + }, + "16041":{ + "inLabel":16041, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16041, + "installed":true, + "interface":"eth-rt4-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16041, + "installed":true, + "interface":"eth-rt4-1" + } + ] + }, + "16050":{ + "inLabel":16050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17050, + "installed":true, + "nexthop":"10.0.1.3" + } + ] + }, + "16051":{ + "inLabel":16051, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17051, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16060":{ + "inLabel":16060, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16060, + "installed":true, + "nexthop":"10.0.3.4" + }, + { + "type":"SR (IS-IS)", + "outLabel":16060, + "installed":true, + "nexthop":"10.0.2.4" + } + ] + }, + "16061":{ + "inLabel":16061, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16061, + "installed":true, + "interface":"eth-rt4-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16061, + "installed":true, + "interface":"eth-rt4-1" + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt2/step4/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt2/step4/show_yang_interface_isis_adjacencies.ref new file mode 120000 index 0000000000..0879b84d23 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt2/step4/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1 @@ +../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file diff --git a/tests/topotests/isis-sr-topo1/rt2/step5/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt2/step5/show_ip_route.ref new file mode 100644 index 0000000000..f378e41d8d --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt2/step5/show_ip_route.ref @@ -0,0 +1,314 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 3 + ] + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17030 + ] + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-1", + "active":true, + "labels":[ + 16040 + ] + }, + { + "fib":true, + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2", + "active":true, + "labels":[ + 16040 + ] + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17050 + ] + } + ] + } + ], + "6.6.6.6\/32":[ + { + "prefix":"6.6.6.6\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2", + "active":true + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceName":"eth-sw1" + }, + { + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1" + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-1" + }, + { + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-1", + "active":true + }, + { + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2" + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.6.0\/24":[ + { + "prefix":"10.0.6.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2", + "active":true + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + }, + { + "fib":true, + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt2/step5/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt2/step5/show_ipv6_route.ref new file mode 100644 index 0000000000..d63e7ceba5 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt2/step5/show_ipv6_route.ref @@ -0,0 +1,124 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 3 + ] + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17031 + ] + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4-1", + "active":true, + "labels":[ + 16041 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4-2", + "active":true, + "labels":[ + 16041 + ] + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17051 + ] + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4-1", + "active":true + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4-2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt2/step5/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt2/step5/show_mpls_table.ref new file mode 100644 index 0000000000..f1e18be26b --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt2/step5/show_mpls_table.ref @@ -0,0 +1,132 @@ +{ + "16010":{ + "inLabel":16010, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true, + "nexthop":"10.0.1.1" + } + ] + }, + "16011":{ + "inLabel":16011, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16020":{ + "inLabel":16020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16021":{ + "inLabel":16021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16030":{ + "inLabel":16030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17030, + "installed":true, + "nexthop":"10.0.1.3" + } + ] + }, + "16031":{ + "inLabel":16031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17031, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16040":{ + "inLabel":16040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16040, + "installed":true, + "nexthop":"10.0.3.4" + }, + { + "type":"SR (IS-IS)", + "outLabel":16040, + "installed":true, + "nexthop":"10.0.2.4" + } + ] + }, + "16041":{ + "inLabel":16041, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16041, + "installed":true, + "interface":"eth-rt4-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16041, + "installed":true, + "interface":"eth-rt4-1" + } + ] + }, + "16050":{ + "inLabel":16050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17050, + "installed":true, + "nexthop":"10.0.1.3" + } + ] + }, + "16051":{ + "inLabel":16051, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17051, + "installed":true, + "interface":"eth-sw1" + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt2/step5/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt2/step5/show_yang_interface_isis_adjacencies.ref new file mode 120000 index 0000000000..0879b84d23 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt2/step5/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1 @@ +../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file diff --git a/tests/topotests/isis-sr-topo1/rt2/step6/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt2/step6/show_ip_route.ref new file mode 100644 index 0000000000..a110c51077 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt2/step6/show_ip_route.ref @@ -0,0 +1,320 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 3 + ] + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17030 + ] + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-1", + "active":true, + "labels":[ + 16040 + ] + }, + { + "fib":true, + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2", + "active":true, + "labels":[ + 16040 + ] + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17050 + ] + } + ] + } + ], + "6.6.6.6\/32":[ + { + "prefix":"6.6.6.6\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-1", + "active":true, + "labels":[ + 16060 + ] + }, + { + "fib":true, + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2", + "active":true, + "labels":[ + 16060 + ] + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceName":"eth-sw1" + }, + { + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1" + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-1" + }, + { + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-1", + "active":true + }, + { + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2" + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.6.0\/24":[ + { + "prefix":"10.0.6.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2", + "active":true + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + }, + { + "fib":true, + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt2/step6/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt2/step6/show_ipv6_route.ref new file mode 100644 index 0000000000..cb426897ce --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt2/step6/show_ipv6_route.ref @@ -0,0 +1,130 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 3 + ] + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17031 + ] + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4-1", + "active":true, + "labels":[ + 16041 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4-2", + "active":true, + "labels":[ + 16041 + ] + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17051 + ] + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4-1", + "active":true, + "labels":[ + 16061 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4-2", + "active":true, + "labels":[ + 16061 + ] + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt2/step6/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt2/step6/show_mpls_table.ref new file mode 100644 index 0000000000..118ec89b5c --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt2/step6/show_mpls_table.ref @@ -0,0 +1,168 @@ +{ + "16010":{ + "inLabel":16010, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true, + "nexthop":"10.0.1.1" + } + ] + }, + "16011":{ + "inLabel":16011, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16020":{ + "inLabel":16020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16021":{ + "inLabel":16021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16030":{ + "inLabel":16030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17030, + "installed":true, + "nexthop":"10.0.1.3" + } + ] + }, + "16031":{ + "inLabel":16031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17031, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16040":{ + "inLabel":16040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16040, + "installed":true, + "nexthop":"10.0.3.4" + }, + { + "type":"SR (IS-IS)", + "outLabel":16040, + "installed":true, + "nexthop":"10.0.2.4" + } + ] + }, + "16041":{ + "inLabel":16041, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16041, + "installed":true, + "interface":"eth-rt4-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16041, + "installed":true, + "interface":"eth-rt4-1" + } + ] + }, + "16050":{ + "inLabel":16050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17050, + "installed":true, + "nexthop":"10.0.1.3" + } + ] + }, + "16051":{ + "inLabel":16051, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17051, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16060":{ + "inLabel":16060, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16060, + "installed":true, + "nexthop":"10.0.3.4" + }, + { + "type":"SR (IS-IS)", + "outLabel":16060, + "installed":true, + "nexthop":"10.0.2.4" + } + ] + }, + "16061":{ + "inLabel":16061, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16061, + "installed":true, + "interface":"eth-rt4-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16061, + "installed":true, + "interface":"eth-rt4-1" + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt2/step6/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt2/step6/show_yang_interface_isis_adjacencies.ref new file mode 120000 index 0000000000..0879b84d23 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt2/step6/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1 @@ +../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file diff --git a/tests/topotests/isis-sr-topo1/rt2/step7/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt2/step7/show_ip_route.ref new file mode 100644 index 0000000000..a9b086a248 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt2/step7/show_ip_route.ref @@ -0,0 +1,317 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17030 + ] + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-1", + "active":true, + "labels":[ + 16040 + ] + }, + { + "fib":true, + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2", + "active":true, + "labels":[ + 16040 + ] + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17050 + ] + } + ] + } + ], + "6.6.6.6\/32":[ + { + "prefix":"6.6.6.6\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-1", + "active":true, + "labels":[ + 16060 + ] + }, + { + "fib":true, + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2", + "active":true, + "labels":[ + 16060 + ] + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceName":"eth-sw1" + }, + { + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1" + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-1" + }, + { + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-1", + "active":true + }, + { + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2" + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.6.0\/24":[ + { + "prefix":"10.0.6.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2", + "active":true + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + }, + { + "fib":true, + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt2/step7/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt2/step7/show_ipv6_route.ref new file mode 100644 index 0000000000..1c61f91451 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt2/step7/show_ipv6_route.ref @@ -0,0 +1,127 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17031 + ] + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4-1", + "active":true, + "labels":[ + 16041 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4-2", + "active":true, + "labels":[ + 16041 + ] + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17051 + ] + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4-1", + "active":true, + "labels":[ + 16061 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4-2", + "active":true, + "labels":[ + 16061 + ] + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt2/step7/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt2/step7/show_mpls_table.ref new file mode 100644 index 0000000000..0f0d24bbfb --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt2/step7/show_mpls_table.ref @@ -0,0 +1,144 @@ +{ + "16020":{ + "inLabel":16020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16021":{ + "inLabel":16021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16030":{ + "inLabel":16030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17030, + "installed":true, + "nexthop":"10.0.1.3" + } + ] + }, + "16031":{ + "inLabel":16031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17031, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16040":{ + "inLabel":16040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16040, + "installed":true, + "nexthop":"10.0.3.4" + }, + { + "type":"SR (IS-IS)", + "outLabel":16040, + "installed":true, + "nexthop":"10.0.2.4" + } + ] + }, + "16041":{ + "inLabel":16041, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16041, + "installed":true, + "interface":"eth-rt4-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16041, + "installed":true, + "interface":"eth-rt4-1" + } + ] + }, + "16050":{ + "inLabel":16050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17050, + "installed":true, + "nexthop":"10.0.1.3" + } + ] + }, + "16051":{ + "inLabel":16051, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17051, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16060":{ + "inLabel":16060, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16060, + "installed":true, + "nexthop":"10.0.3.4" + }, + { + "type":"SR (IS-IS)", + "outLabel":16060, + "installed":true, + "nexthop":"10.0.2.4" + } + ] + }, + "16061":{ + "inLabel":16061, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16061, + "installed":true, + "interface":"eth-rt4-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16061, + "installed":true, + "interface":"eth-rt4-1" + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt2/step7/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt2/step7/show_yang_interface_isis_adjacencies.ref new file mode 120000 index 0000000000..0879b84d23 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt2/step7/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1 @@ +../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file diff --git a/tests/topotests/isis-sr-topo1/rt2/step8/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt2/step8/show_ip_route.ref new file mode 100644 index 0000000000..a110c51077 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt2/step8/show_ip_route.ref @@ -0,0 +1,320 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 3 + ] + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17030 + ] + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-1", + "active":true, + "labels":[ + 16040 + ] + }, + { + "fib":true, + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2", + "active":true, + "labels":[ + 16040 + ] + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17050 + ] + } + ] + } + ], + "6.6.6.6\/32":[ + { + "prefix":"6.6.6.6\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-1", + "active":true, + "labels":[ + 16060 + ] + }, + { + "fib":true, + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2", + "active":true, + "labels":[ + 16060 + ] + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceName":"eth-sw1" + }, + { + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1" + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-1" + }, + { + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-1", + "active":true + }, + { + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2" + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.6.0\/24":[ + { + "prefix":"10.0.6.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2", + "active":true + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + }, + { + "fib":true, + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt2/step8/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt2/step8/show_ipv6_route.ref new file mode 100644 index 0000000000..cb426897ce --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt2/step8/show_ipv6_route.ref @@ -0,0 +1,130 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 3 + ] + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17031 + ] + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4-1", + "active":true, + "labels":[ + 16041 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4-2", + "active":true, + "labels":[ + 16041 + ] + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17051 + ] + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4-1", + "active":true, + "labels":[ + 16061 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4-2", + "active":true, + "labels":[ + 16061 + ] + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt2/step8/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt2/step8/show_mpls_table.ref new file mode 100644 index 0000000000..118ec89b5c --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt2/step8/show_mpls_table.ref @@ -0,0 +1,168 @@ +{ + "16010":{ + "inLabel":16010, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true, + "nexthop":"10.0.1.1" + } + ] + }, + "16011":{ + "inLabel":16011, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16020":{ + "inLabel":16020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16021":{ + "inLabel":16021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16030":{ + "inLabel":16030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17030, + "installed":true, + "nexthop":"10.0.1.3" + } + ] + }, + "16031":{ + "inLabel":16031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17031, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16040":{ + "inLabel":16040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16040, + "installed":true, + "nexthop":"10.0.3.4" + }, + { + "type":"SR (IS-IS)", + "outLabel":16040, + "installed":true, + "nexthop":"10.0.2.4" + } + ] + }, + "16041":{ + "inLabel":16041, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16041, + "installed":true, + "interface":"eth-rt4-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16041, + "installed":true, + "interface":"eth-rt4-1" + } + ] + }, + "16050":{ + "inLabel":16050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17050, + "installed":true, + "nexthop":"10.0.1.3" + } + ] + }, + "16051":{ + "inLabel":16051, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17051, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16060":{ + "inLabel":16060, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16060, + "installed":true, + "nexthop":"10.0.3.4" + }, + { + "type":"SR (IS-IS)", + "outLabel":16060, + "installed":true, + "nexthop":"10.0.2.4" + } + ] + }, + "16061":{ + "inLabel":16061, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16061, + "installed":true, + "interface":"eth-rt4-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16061, + "installed":true, + "interface":"eth-rt4-1" + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt2/step8/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt2/step8/show_yang_interface_isis_adjacencies.ref new file mode 120000 index 0000000000..0879b84d23 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt2/step8/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1 @@ +../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file diff --git a/tests/topotests/isis-sr-topo1/rt2/step9/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt2/step9/show_ip_route.ref new file mode 100644 index 0000000000..2e4c20257f --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt2/step9/show_ip_route.ref @@ -0,0 +1,320 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16010 + ] + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17030 + ] + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-1", + "active":true, + "labels":[ + 16040 + ] + }, + { + "fib":true, + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2", + "active":true, + "labels":[ + 16040 + ] + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17050 + ] + } + ] + } + ], + "6.6.6.6\/32":[ + { + "prefix":"6.6.6.6\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-1", + "active":true, + "labels":[ + 16060 + ] + }, + { + "fib":true, + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2", + "active":true, + "labels":[ + 16060 + ] + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceName":"eth-sw1" + }, + { + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1" + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-1" + }, + { + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-1", + "active":true + }, + { + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2" + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.6.0\/24":[ + { + "prefix":"10.0.6.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2", + "active":true + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.3", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + }, + { + "fib":true, + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"eth-rt4-2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt2/step9/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt2/step9/show_ipv6_route.ref new file mode 100644 index 0000000000..19837bc700 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt2/step9/show_ipv6_route.ref @@ -0,0 +1,130 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16011 + ] + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17031 + ] + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4-1", + "active":true, + "labels":[ + 16041 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4-2", + "active":true, + "labels":[ + 16041 + ] + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 17051 + ] + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4-1", + "active":true, + "labels":[ + 16061 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4-2", + "active":true, + "labels":[ + 16061 + ] + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt2/step9/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt2/step9/show_mpls_table.ref new file mode 100644 index 0000000000..7c910fc6f6 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt2/step9/show_mpls_table.ref @@ -0,0 +1,168 @@ +{ + "16010":{ + "inLabel":16010, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16010, + "installed":true, + "nexthop":"10.0.1.1" + } + ] + }, + "16011":{ + "inLabel":16011, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16011, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16020":{ + "inLabel":16020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16021":{ + "inLabel":16021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16030":{ + "inLabel":16030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17030, + "installed":true, + "nexthop":"10.0.1.3" + } + ] + }, + "16031":{ + "inLabel":16031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17031, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16040":{ + "inLabel":16040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16040, + "installed":true, + "nexthop":"10.0.3.4" + }, + { + "type":"SR (IS-IS)", + "outLabel":16040, + "installed":true, + "nexthop":"10.0.2.4" + } + ] + }, + "16041":{ + "inLabel":16041, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16041, + "installed":true, + "interface":"eth-rt4-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16041, + "installed":true, + "interface":"eth-rt4-1" + } + ] + }, + "16050":{ + "inLabel":16050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17050, + "installed":true, + "nexthop":"10.0.1.3" + } + ] + }, + "16051":{ + "inLabel":16051, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17051, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "16060":{ + "inLabel":16060, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16060, + "installed":true, + "nexthop":"10.0.3.4" + }, + { + "type":"SR (IS-IS)", + "outLabel":16060, + "installed":true, + "nexthop":"10.0.2.4" + } + ] + }, + "16061":{ + "inLabel":16061, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16061, + "installed":true, + "interface":"eth-rt4-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16061, + "installed":true, + "interface":"eth-rt4-1" + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt2/step9/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt2/step9/show_yang_interface_isis_adjacencies.ref new file mode 120000 index 0000000000..0879b84d23 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt2/step9/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1 @@ +../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file diff --git a/tests/topotests/isis-sr-topo1/rt2/zebra.conf b/tests/topotests/isis-sr-topo1/rt2/zebra.conf new file mode 100644 index 0000000000..dcb0686dc2 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt2/zebra.conf @@ -0,0 +1,25 @@ +log file zebra.log +! +hostname rt2 +! +debug zebra kernel +debug zebra packet +debug zebra mpls +! +interface lo + ip address 2.2.2.2/32 + ipv6 address 2001:db8:1000::2/128 +! +interface eth-sw1 + ip address 10.0.1.2/24 +! +interface eth-rt4-1 + ip address 10.0.2.2/24 +! +interface eth-rt4-2 + ip address 10.0.3.2/24 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/isis-sr-topo1/rt3/isisd.conf b/tests/topotests/isis-sr-topo1/rt3/isisd.conf new file mode 100644 index 0000000000..5a0add22a9 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt3/isisd.conf @@ -0,0 +1,42 @@ +hostname rt3 +log file isisd.log +! +debug isis events +debug isis route-events +debug isis spf-events +debug isis sr-events +debug isis lsp-gen +! +interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive +! +interface eth-sw1 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 +! +interface eth-rt5-1 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 +! +interface eth-rt5-2 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 +! +router isis 1 + net 49.0000.0000.0000.0003.00 + is-type level-1 + lsp-gen-interval 2 + topology ipv6-unicast + segment-routing on + segment-routing global-block 17000 24999 + segment-routing node-msd 8 + segment-routing prefix 3.3.3.3/32 index 30 no-php-flag + segment-routing prefix 2001:db8:1000::3/128 index 31 no-php-flag +! diff --git a/tests/topotests/isis-sr-topo1/rt3/step1/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt3/step1/show_ip_route.ref new file mode 100644 index 0000000000..8d4fbec4b5 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt3/step1/show_ip_route.ref @@ -0,0 +1,347 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 3 + ] + } + ] + } + ], + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16020 + ] + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16040 + ] + }, + { + "fib":true, + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true, + "labels":[ + 16040 + ] + }, + { + "fib":true, + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true, + "labels":[ + 16040 + ] + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true, + "labels":[ + 16050 + ] + }, + { + "fib":true, + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true, + "labels":[ + 16050 + ] + } + ] + } + ], + "6.6.6.6\/32":[ + { + "prefix":"6.6.6.6\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true, + "labels":[ + 16060 + ] + }, + { + "fib":true, + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true, + "labels":[ + 16060 + ] + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceName":"eth-sw1" + }, + { + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1" + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1" + }, + { + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true + }, + { + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2" + } + ] + } + ], + "10.0.6.0\/24":[ + { + "prefix":"10.0.6.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + }, + { + "fib":true, + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt3/step1/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt3/step1/show_ipv6_route.ref new file mode 100644 index 0000000000..4e4961eaf0 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt3/step1/show_ipv6_route.ref @@ -0,0 +1,148 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 3 + ] + } + ] + } + ], + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16021 + ] + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5-2", + "active":true, + "labels":[ + 16041 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16041 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5-1", + "active":true, + "labels":[ + 16041 + ] + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5-2", + "active":true, + "labels":[ + 16051 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5-1", + "active":true, + "labels":[ + 16051 + ] + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5-2", + "active":true, + "labels":[ + 16061 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5-1", + "active":true, + "labels":[ + 16061 + ] + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt3/step1/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt3/step1/show_mpls_table.ref new file mode 100644 index 0000000000..b7bdc3e4af --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt3/step1/show_mpls_table.ref @@ -0,0 +1,192 @@ +{ + "17010":{ + "inLabel":17010, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true, + "nexthop":"10.0.1.1" + } + ] + }, + "17011":{ + "inLabel":17011, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "17020":{ + "inLabel":17020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16020, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "17021":{ + "inLabel":17021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16021, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "17030":{ + "inLabel":17030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "17031":{ + "inLabel":17031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "17040":{ + "inLabel":17040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16040, + "installed":true, + "nexthop":"10.0.1.2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16040, + "installed":true, + "nexthop":"10.0.5.5" + }, + { + "type":"SR (IS-IS)", + "outLabel":16040, + "installed":true, + "nexthop":"10.0.4.5" + } + ] + }, + "17041":{ + "inLabel":17041, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16041, + "installed":true, + "interface":"eth-sw1" + }, + { + "type":"SR (IS-IS)", + "outLabel":16041, + "installed":true, + "interface":"eth-rt5-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16041, + "installed":true, + "interface":"eth-rt5-1" + } + ] + }, + "17050":{ + "inLabel":17050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16050, + "installed":true, + "nexthop":"10.0.5.5" + }, + { + "type":"SR (IS-IS)", + "outLabel":16050, + "installed":true, + "nexthop":"10.0.4.5" + } + ] + }, + "17051":{ + "inLabel":17051, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16051, + "installed":true, + "interface":"eth-rt5-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16051, + "installed":true, + "interface":"eth-rt5-1" + } + ] + }, + "17060":{ + "inLabel":17060, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16060, + "installed":true, + "nexthop":"10.0.5.5" + }, + { + "type":"SR (IS-IS)", + "outLabel":16060, + "installed":true, + "nexthop":"10.0.4.5" + } + ] + }, + "17061":{ + "inLabel":17061, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16061, + "installed":true, + "interface":"eth-rt5-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16061, + "installed":true, + "interface":"eth-rt5-1" + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt3/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt3/step1/show_yang_interface_isis_adjacencies.ref new file mode 100644 index 0000000000..7fa6f5cf47 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt3/step1/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1,70 @@ +{ + "frr-interface:lib": { + "interface": [ + { + "name": "eth-rt5-1", + "vrf": "default", + "state": { + "frr-isisd:isis": { + "adjacencies": { + "adjacency": [ + { + "neighbor-sys-type": "level-1", + "neighbor-sysid": "0000.0000.0005", + "hold-timer": 9, + "neighbor-priority": 0, + "state": "up" + } + ] + } + } + } + }, + { + "name": "eth-rt5-2", + "vrf": "default", + "state": { + "frr-isisd:isis": { + "adjacencies": { + "adjacency": [ + { + "neighbor-sys-type": "level-1", + "neighbor-sysid": "0000.0000.0005", + "hold-timer": 9, + "neighbor-priority": 0, + "state": "up" + } + ] + } + } + } + }, + { + "name": "eth-sw1", + "vrf": "default", + "state": { + "frr-isisd:isis": { + "adjacencies": { + "adjacency": [ + { + "neighbor-sys-type": "level-1", + "neighbor-sysid": "0000.0000.0001", + "hold-timer": 9, + "neighbor-priority": 64, + "state": "up" + }, + { + "neighbor-sys-type": "level-1", + "neighbor-sysid": "0000.0000.0002", + "hold-timer": 9, + "neighbor-priority": 64, + "state": "up" + } + ] + } + } + } + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt3/step10/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt3/step10/show_ip_route.ref new file mode 100644 index 0000000000..9522b141b0 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt3/step10/show_ip_route.ref @@ -0,0 +1,327 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16010 + ] + } + ] + } + ], + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16020 + ] + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16040 + ] + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true, + "labels":[ + 16050 + ] + }, + { + "fib":true, + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true, + "labels":[ + 16050 + ] + } + ] + } + ], + "6.6.6.6\/32":[ + { + "prefix":"6.6.6.6\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true, + "labels":[ + 16060 + ] + }, + { + "fib":true, + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true, + "labels":[ + 16060 + ] + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceName":"eth-sw1" + }, + { + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1" + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1" + }, + { + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true + }, + { + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2" + } + ] + } + ], + "10.0.6.0\/24":[ + { + "prefix":"10.0.6.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + }, + { + "fib":true, + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt3/step10/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt3/step10/show_ipv6_route.ref new file mode 100644 index 0000000000..fb630bc68f --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt3/step10/show_ipv6_route.ref @@ -0,0 +1,130 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16011 + ] + } + ] + } + ], + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16021 + ] + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16041 + ] + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5-2", + "active":true, + "labels":[ + 16051 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5-1", + "active":true, + "labels":[ + 16051 + ] + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5-2", + "active":true, + "labels":[ + 16061 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5-1", + "active":true, + "labels":[ + 16061 + ] + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt3/step10/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt3/step10/show_mpls_table.ref new file mode 100644 index 0000000000..4aec3b6904 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt3/step10/show_mpls_table.ref @@ -0,0 +1,168 @@ +{ + "17010":{ + "inLabel":17010, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16010, + "installed":true, + "nexthop":"10.0.1.1" + } + ] + }, + "17011":{ + "inLabel":17011, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16011, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "17020":{ + "inLabel":17020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16020, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "17021":{ + "inLabel":17021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16021, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "17030":{ + "inLabel":17030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "17031":{ + "inLabel":17031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "17040":{ + "inLabel":17040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16040, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "17041":{ + "inLabel":17041, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16041, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "17050":{ + "inLabel":17050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16050, + "installed":true, + "nexthop":"10.0.5.5" + }, + { + "type":"SR (IS-IS)", + "outLabel":16050, + "installed":true, + "nexthop":"10.0.4.5" + } + ] + }, + "17051":{ + "inLabel":17051, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16051, + "installed":true, + "interface":"eth-rt5-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16051, + "installed":true, + "interface":"eth-rt5-1" + } + ] + }, + "17060":{ + "inLabel":17060, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16060, + "installed":true, + "nexthop":"10.0.5.5" + }, + { + "type":"SR (IS-IS)", + "outLabel":16060, + "installed":true, + "nexthop":"10.0.4.5" + } + ] + }, + "17061":{ + "inLabel":17061, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16061, + "installed":true, + "interface":"eth-rt5-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16061, + "installed":true, + "interface":"eth-rt5-1" + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt3/step10/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt3/step10/show_yang_interface_isis_adjacencies.ref new file mode 120000 index 0000000000..0879b84d23 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt3/step10/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1 @@ +../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file diff --git a/tests/topotests/isis-sr-topo1/rt3/step2/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt3/step2/show_ip_route.ref new file mode 100644 index 0000000000..46ebeb8ab9 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt3/step2/show_ip_route.ref @@ -0,0 +1,327 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 3 + ] + } + ] + } + ], + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16020 + ] + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16040 + ] + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true, + "labels":[ + 16050 + ] + }, + { + "fib":true, + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true, + "labels":[ + 16050 + ] + } + ] + } + ], + "6.6.6.6\/32":[ + { + "prefix":"6.6.6.6\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true, + "labels":[ + 16060 + ] + }, + { + "fib":true, + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true, + "labels":[ + 16060 + ] + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceName":"eth-sw1" + }, + { + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1" + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1" + }, + { + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true + }, + { + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2" + } + ] + } + ], + "10.0.6.0\/24":[ + { + "prefix":"10.0.6.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + }, + { + "fib":true, + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt3/step2/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt3/step2/show_ipv6_route.ref new file mode 100644 index 0000000000..b2c774d493 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt3/step2/show_ipv6_route.ref @@ -0,0 +1,130 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 3 + ] + } + ] + } + ], + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16021 + ] + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16041 + ] + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5-2", + "active":true, + "labels":[ + 16051 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5-1", + "active":true, + "labels":[ + 16051 + ] + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5-2", + "active":true, + "labels":[ + 16061 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5-1", + "active":true, + "labels":[ + 16061 + ] + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt3/step2/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt3/step2/show_mpls_table.ref new file mode 100644 index 0000000000..a1e64afd67 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt3/step2/show_mpls_table.ref @@ -0,0 +1,168 @@ +{ + "17010":{ + "inLabel":17010, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true, + "nexthop":"10.0.1.1" + } + ] + }, + "17011":{ + "inLabel":17011, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "17020":{ + "inLabel":17020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16020, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "17021":{ + "inLabel":17021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16021, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "17030":{ + "inLabel":17030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "17031":{ + "inLabel":17031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "17040":{ + "inLabel":17040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16040, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "17041":{ + "inLabel":17041, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16041, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "17050":{ + "inLabel":17050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16050, + "installed":true, + "nexthop":"10.0.5.5" + }, + { + "type":"SR (IS-IS)", + "outLabel":16050, + "installed":true, + "nexthop":"10.0.4.5" + } + ] + }, + "17051":{ + "inLabel":17051, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16051, + "installed":true, + "interface":"eth-rt5-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16051, + "installed":true, + "interface":"eth-rt5-1" + } + ] + }, + "17060":{ + "inLabel":17060, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16060, + "installed":true, + "nexthop":"10.0.5.5" + }, + { + "type":"SR (IS-IS)", + "outLabel":16060, + "installed":true, + "nexthop":"10.0.4.5" + } + ] + }, + "17061":{ + "inLabel":17061, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16061, + "installed":true, + "interface":"eth-rt5-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16061, + "installed":true, + "interface":"eth-rt5-1" + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt3/step2/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt3/step2/show_yang_interface_isis_adjacencies.ref new file mode 120000 index 0000000000..0879b84d23 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt3/step2/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1 @@ +../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file diff --git a/tests/topotests/isis-sr-topo1/rt3/step3/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt3/step3/show_ip_route.ref new file mode 100644 index 0000000000..738aa17406 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt3/step3/show_ip_route.ref @@ -0,0 +1,280 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 3 + ] + } + ] + } + ], + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16020 + ] + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16040 + ] + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true, + "labels":[ + 16050 + ] + }, + { + "fib":true, + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true, + "labels":[ + 16050 + ] + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceName":"eth-sw1" + }, + { + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1" + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1" + }, + { + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true + }, + { + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2" + } + ] + } + ], + "10.0.6.0\/24":[ + { + "prefix":"10.0.6.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt3/step3/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt3/step3/show_ipv6_route.ref new file mode 100644 index 0000000000..b6423cd2b8 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt3/step3/show_ipv6_route.ref @@ -0,0 +1,99 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 3 + ] + } + ] + } + ], + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16021 + ] + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16041 + ] + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5-2", + "active":true, + "labels":[ + 16051 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5-1", + "active":true, + "labels":[ + 16051 + ] + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt3/step3/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt3/step3/show_mpls_table.ref new file mode 100644 index 0000000000..1a2b8728e6 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt3/step3/show_mpls_table.ref @@ -0,0 +1,132 @@ +{ + "17010":{ + "inLabel":17010, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true, + "nexthop":"10.0.1.1" + } + ] + }, + "17011":{ + "inLabel":17011, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "17020":{ + "inLabel":17020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16020, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "17021":{ + "inLabel":17021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16021, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "17030":{ + "inLabel":17030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "17031":{ + "inLabel":17031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "17040":{ + "inLabel":17040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16040, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "17041":{ + "inLabel":17041, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16041, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "17050":{ + "inLabel":17050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16050, + "installed":true, + "nexthop":"10.0.5.5" + }, + { + "type":"SR (IS-IS)", + "outLabel":16050, + "installed":true, + "nexthop":"10.0.4.5" + } + ] + }, + "17051":{ + "inLabel":17051, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16051, + "installed":true, + "interface":"eth-rt5-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16051, + "installed":true, + "interface":"eth-rt5-1" + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt3/step3/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt3/step3/show_yang_interface_isis_adjacencies.ref new file mode 120000 index 0000000000..0879b84d23 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt3/step3/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1 @@ +../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file diff --git a/tests/topotests/isis-sr-topo1/rt3/step4/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt3/step4/show_ip_route.ref new file mode 100644 index 0000000000..46ebeb8ab9 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt3/step4/show_ip_route.ref @@ -0,0 +1,327 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 3 + ] + } + ] + } + ], + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16020 + ] + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16040 + ] + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true, + "labels":[ + 16050 + ] + }, + { + "fib":true, + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true, + "labels":[ + 16050 + ] + } + ] + } + ], + "6.6.6.6\/32":[ + { + "prefix":"6.6.6.6\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true, + "labels":[ + 16060 + ] + }, + { + "fib":true, + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true, + "labels":[ + 16060 + ] + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceName":"eth-sw1" + }, + { + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1" + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1" + }, + { + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true + }, + { + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2" + } + ] + } + ], + "10.0.6.0\/24":[ + { + "prefix":"10.0.6.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + }, + { + "fib":true, + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt3/step4/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt3/step4/show_ipv6_route.ref new file mode 100644 index 0000000000..b2c774d493 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt3/step4/show_ipv6_route.ref @@ -0,0 +1,130 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 3 + ] + } + ] + } + ], + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16021 + ] + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16041 + ] + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5-2", + "active":true, + "labels":[ + 16051 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5-1", + "active":true, + "labels":[ + 16051 + ] + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5-2", + "active":true, + "labels":[ + 16061 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5-1", + "active":true, + "labels":[ + 16061 + ] + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt3/step4/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt3/step4/show_mpls_table.ref new file mode 100644 index 0000000000..a1e64afd67 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt3/step4/show_mpls_table.ref @@ -0,0 +1,168 @@ +{ + "17010":{ + "inLabel":17010, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true, + "nexthop":"10.0.1.1" + } + ] + }, + "17011":{ + "inLabel":17011, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "17020":{ + "inLabel":17020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16020, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "17021":{ + "inLabel":17021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16021, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "17030":{ + "inLabel":17030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "17031":{ + "inLabel":17031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "17040":{ + "inLabel":17040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16040, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "17041":{ + "inLabel":17041, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16041, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "17050":{ + "inLabel":17050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16050, + "installed":true, + "nexthop":"10.0.5.5" + }, + { + "type":"SR (IS-IS)", + "outLabel":16050, + "installed":true, + "nexthop":"10.0.4.5" + } + ] + }, + "17051":{ + "inLabel":17051, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16051, + "installed":true, + "interface":"eth-rt5-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16051, + "installed":true, + "interface":"eth-rt5-1" + } + ] + }, + "17060":{ + "inLabel":17060, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16060, + "installed":true, + "nexthop":"10.0.5.5" + }, + { + "type":"SR (IS-IS)", + "outLabel":16060, + "installed":true, + "nexthop":"10.0.4.5" + } + ] + }, + "17061":{ + "inLabel":17061, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16061, + "installed":true, + "interface":"eth-rt5-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16061, + "installed":true, + "interface":"eth-rt5-1" + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt3/step4/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt3/step4/show_yang_interface_isis_adjacencies.ref new file mode 120000 index 0000000000..0879b84d23 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt3/step4/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1 @@ +../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file diff --git a/tests/topotests/isis-sr-topo1/rt3/step5/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt3/step5/show_ip_route.ref new file mode 100644 index 0000000000..489b495bb1 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt3/step5/show_ip_route.ref @@ -0,0 +1,321 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 3 + ] + } + ] + } + ], + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16020 + ] + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16040 + ] + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true, + "labels":[ + 16050 + ] + }, + { + "fib":true, + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true, + "labels":[ + 16050 + ] + } + ] + } + ], + "6.6.6.6\/32":[ + { + "prefix":"6.6.6.6\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceName":"eth-sw1" + }, + { + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1" + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1" + }, + { + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true + }, + { + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2" + } + ] + } + ], + "10.0.6.0\/24":[ + { + "prefix":"10.0.6.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + }, + { + "fib":true, + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt3/step5/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt3/step5/show_ipv6_route.ref new file mode 100644 index 0000000000..46ee7ba28e --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt3/step5/show_ipv6_route.ref @@ -0,0 +1,124 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 3 + ] + } + ] + } + ], + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16021 + ] + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16041 + ] + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5-2", + "active":true, + "labels":[ + 16051 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5-1", + "active":true, + "labels":[ + 16051 + ] + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5-2", + "active":true + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5-1", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt3/step5/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt3/step5/show_mpls_table.ref new file mode 100644 index 0000000000..1a2b8728e6 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt3/step5/show_mpls_table.ref @@ -0,0 +1,132 @@ +{ + "17010":{ + "inLabel":17010, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true, + "nexthop":"10.0.1.1" + } + ] + }, + "17011":{ + "inLabel":17011, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "17020":{ + "inLabel":17020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16020, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "17021":{ + "inLabel":17021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16021, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "17030":{ + "inLabel":17030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "17031":{ + "inLabel":17031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "17040":{ + "inLabel":17040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16040, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "17041":{ + "inLabel":17041, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16041, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "17050":{ + "inLabel":17050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16050, + "installed":true, + "nexthop":"10.0.5.5" + }, + { + "type":"SR (IS-IS)", + "outLabel":16050, + "installed":true, + "nexthop":"10.0.4.5" + } + ] + }, + "17051":{ + "inLabel":17051, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16051, + "installed":true, + "interface":"eth-rt5-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16051, + "installed":true, + "interface":"eth-rt5-1" + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt3/step5/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt3/step5/show_yang_interface_isis_adjacencies.ref new file mode 120000 index 0000000000..0879b84d23 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt3/step5/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1 @@ +../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file diff --git a/tests/topotests/isis-sr-topo1/rt3/step6/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt3/step6/show_ip_route.ref new file mode 100644 index 0000000000..46ebeb8ab9 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt3/step6/show_ip_route.ref @@ -0,0 +1,327 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 3 + ] + } + ] + } + ], + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16020 + ] + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16040 + ] + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true, + "labels":[ + 16050 + ] + }, + { + "fib":true, + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true, + "labels":[ + 16050 + ] + } + ] + } + ], + "6.6.6.6\/32":[ + { + "prefix":"6.6.6.6\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true, + "labels":[ + 16060 + ] + }, + { + "fib":true, + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true, + "labels":[ + 16060 + ] + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceName":"eth-sw1" + }, + { + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1" + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1" + }, + { + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true + }, + { + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2" + } + ] + } + ], + "10.0.6.0\/24":[ + { + "prefix":"10.0.6.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + }, + { + "fib":true, + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt3/step6/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt3/step6/show_ipv6_route.ref new file mode 100644 index 0000000000..b2c774d493 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt3/step6/show_ipv6_route.ref @@ -0,0 +1,130 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 3 + ] + } + ] + } + ], + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16021 + ] + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16041 + ] + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5-2", + "active":true, + "labels":[ + 16051 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5-1", + "active":true, + "labels":[ + 16051 + ] + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5-2", + "active":true, + "labels":[ + 16061 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5-1", + "active":true, + "labels":[ + 16061 + ] + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt3/step6/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt3/step6/show_mpls_table.ref new file mode 100644 index 0000000000..a1e64afd67 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt3/step6/show_mpls_table.ref @@ -0,0 +1,168 @@ +{ + "17010":{ + "inLabel":17010, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true, + "nexthop":"10.0.1.1" + } + ] + }, + "17011":{ + "inLabel":17011, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "17020":{ + "inLabel":17020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16020, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "17021":{ + "inLabel":17021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16021, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "17030":{ + "inLabel":17030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "17031":{ + "inLabel":17031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "17040":{ + "inLabel":17040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16040, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "17041":{ + "inLabel":17041, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16041, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "17050":{ + "inLabel":17050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16050, + "installed":true, + "nexthop":"10.0.5.5" + }, + { + "type":"SR (IS-IS)", + "outLabel":16050, + "installed":true, + "nexthop":"10.0.4.5" + } + ] + }, + "17051":{ + "inLabel":17051, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16051, + "installed":true, + "interface":"eth-rt5-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16051, + "installed":true, + "interface":"eth-rt5-1" + } + ] + }, + "17060":{ + "inLabel":17060, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16060, + "installed":true, + "nexthop":"10.0.5.5" + }, + { + "type":"SR (IS-IS)", + "outLabel":16060, + "installed":true, + "nexthop":"10.0.4.5" + } + ] + }, + "17061":{ + "inLabel":17061, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16061, + "installed":true, + "interface":"eth-rt5-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16061, + "installed":true, + "interface":"eth-rt5-1" + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt3/step6/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt3/step6/show_yang_interface_isis_adjacencies.ref new file mode 120000 index 0000000000..0879b84d23 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt3/step6/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1 @@ +../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file diff --git a/tests/topotests/isis-sr-topo1/rt3/step7/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt3/step7/show_ip_route.ref new file mode 100644 index 0000000000..1e8c27c01f --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt3/step7/show_ip_route.ref @@ -0,0 +1,324 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16020 + ] + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16040 + ] + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true, + "labels":[ + 16050 + ] + }, + { + "fib":true, + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true, + "labels":[ + 16050 + ] + } + ] + } + ], + "6.6.6.6\/32":[ + { + "prefix":"6.6.6.6\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true, + "labels":[ + 16060 + ] + }, + { + "fib":true, + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true, + "labels":[ + 16060 + ] + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceName":"eth-sw1" + }, + { + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1" + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1" + }, + { + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true + }, + { + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2" + } + ] + } + ], + "10.0.6.0\/24":[ + { + "prefix":"10.0.6.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + }, + { + "fib":true, + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt3/step7/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt3/step7/show_ipv6_route.ref new file mode 100644 index 0000000000..d21700d407 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt3/step7/show_ipv6_route.ref @@ -0,0 +1,127 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16021 + ] + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16041 + ] + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5-2", + "active":true, + "labels":[ + 16051 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5-1", + "active":true, + "labels":[ + 16051 + ] + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5-2", + "active":true, + "labels":[ + 16061 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5-1", + "active":true, + "labels":[ + 16061 + ] + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt3/step7/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt3/step7/show_mpls_table.ref new file mode 100644 index 0000000000..e97e0d017b --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt3/step7/show_mpls_table.ref @@ -0,0 +1,144 @@ +{ + "17020":{ + "inLabel":17020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16020, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "17021":{ + "inLabel":17021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16021, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "17030":{ + "inLabel":17030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "17031":{ + "inLabel":17031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "17040":{ + "inLabel":17040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16040, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "17041":{ + "inLabel":17041, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16041, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "17050":{ + "inLabel":17050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16050, + "installed":true, + "nexthop":"10.0.5.5" + }, + { + "type":"SR (IS-IS)", + "outLabel":16050, + "installed":true, + "nexthop":"10.0.4.5" + } + ] + }, + "17051":{ + "inLabel":17051, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16051, + "installed":true, + "interface":"eth-rt5-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16051, + "installed":true, + "interface":"eth-rt5-1" + } + ] + }, + "17060":{ + "inLabel":17060, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16060, + "installed":true, + "nexthop":"10.0.5.5" + }, + { + "type":"SR (IS-IS)", + "outLabel":16060, + "installed":true, + "nexthop":"10.0.4.5" + } + ] + }, + "17061":{ + "inLabel":17061, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16061, + "installed":true, + "interface":"eth-rt5-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16061, + "installed":true, + "interface":"eth-rt5-1" + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt3/step7/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt3/step7/show_yang_interface_isis_adjacencies.ref new file mode 120000 index 0000000000..0879b84d23 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt3/step7/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1 @@ +../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file diff --git a/tests/topotests/isis-sr-topo1/rt3/step8/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt3/step8/show_ip_route.ref new file mode 100644 index 0000000000..46ebeb8ab9 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt3/step8/show_ip_route.ref @@ -0,0 +1,327 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 3 + ] + } + ] + } + ], + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16020 + ] + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16040 + ] + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true, + "labels":[ + 16050 + ] + }, + { + "fib":true, + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true, + "labels":[ + 16050 + ] + } + ] + } + ], + "6.6.6.6\/32":[ + { + "prefix":"6.6.6.6\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true, + "labels":[ + 16060 + ] + }, + { + "fib":true, + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true, + "labels":[ + 16060 + ] + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceName":"eth-sw1" + }, + { + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1" + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1" + }, + { + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true + }, + { + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2" + } + ] + } + ], + "10.0.6.0\/24":[ + { + "prefix":"10.0.6.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + }, + { + "fib":true, + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt3/step8/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt3/step8/show_ipv6_route.ref new file mode 100644 index 0000000000..b2c774d493 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt3/step8/show_ipv6_route.ref @@ -0,0 +1,130 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 3 + ] + } + ] + } + ], + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16021 + ] + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16041 + ] + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5-2", + "active":true, + "labels":[ + 16051 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5-1", + "active":true, + "labels":[ + 16051 + ] + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5-2", + "active":true, + "labels":[ + 16061 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5-1", + "active":true, + "labels":[ + 16061 + ] + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt3/step8/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt3/step8/show_mpls_table.ref new file mode 100644 index 0000000000..a1e64afd67 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt3/step8/show_mpls_table.ref @@ -0,0 +1,168 @@ +{ + "17010":{ + "inLabel":17010, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true, + "nexthop":"10.0.1.1" + } + ] + }, + "17011":{ + "inLabel":17011, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "17020":{ + "inLabel":17020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16020, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "17021":{ + "inLabel":17021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16021, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "17030":{ + "inLabel":17030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "17031":{ + "inLabel":17031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "17040":{ + "inLabel":17040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16040, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "17041":{ + "inLabel":17041, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16041, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "17050":{ + "inLabel":17050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16050, + "installed":true, + "nexthop":"10.0.5.5" + }, + { + "type":"SR (IS-IS)", + "outLabel":16050, + "installed":true, + "nexthop":"10.0.4.5" + } + ] + }, + "17051":{ + "inLabel":17051, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16051, + "installed":true, + "interface":"eth-rt5-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16051, + "installed":true, + "interface":"eth-rt5-1" + } + ] + }, + "17060":{ + "inLabel":17060, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16060, + "installed":true, + "nexthop":"10.0.5.5" + }, + { + "type":"SR (IS-IS)", + "outLabel":16060, + "installed":true, + "nexthop":"10.0.4.5" + } + ] + }, + "17061":{ + "inLabel":17061, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16061, + "installed":true, + "interface":"eth-rt5-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16061, + "installed":true, + "interface":"eth-rt5-1" + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt3/step8/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt3/step8/show_yang_interface_isis_adjacencies.ref new file mode 120000 index 0000000000..0879b84d23 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt3/step8/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1 @@ +../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file diff --git a/tests/topotests/isis-sr-topo1/rt3/step9/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt3/step9/show_ip_route.ref new file mode 100644 index 0000000000..9522b141b0 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt3/step9/show_ip_route.ref @@ -0,0 +1,327 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16010 + ] + } + ] + } + ], + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16020 + ] + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16040 + ] + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true, + "labels":[ + 16050 + ] + }, + { + "fib":true, + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true, + "labels":[ + 16050 + ] + } + ] + } + ], + "6.6.6.6\/32":[ + { + "prefix":"6.6.6.6\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true, + "labels":[ + 16060 + ] + }, + { + "fib":true, + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true, + "labels":[ + 16060 + ] + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceName":"eth-sw1" + }, + { + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1" + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1" + }, + { + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true + }, + { + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2" + } + ] + } + ], + "10.0.6.0\/24":[ + { + "prefix":"10.0.6.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"eth-sw1", + "active":true + }, + { + "fib":true, + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.5", + "afi":"ipv4", + "interfaceName":"eth-rt5-2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt3/step9/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt3/step9/show_ipv6_route.ref new file mode 100644 index 0000000000..fb630bc68f --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt3/step9/show_ipv6_route.ref @@ -0,0 +1,130 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16011 + ] + } + ] + } + ], + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16021 + ] + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-sw1", + "active":true, + "labels":[ + 16041 + ] + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5-2", + "active":true, + "labels":[ + 16051 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5-1", + "active":true, + "labels":[ + 16051 + ] + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5-2", + "active":true, + "labels":[ + 16061 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5-1", + "active":true, + "labels":[ + 16061 + ] + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt3/step9/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt3/step9/show_mpls_table.ref new file mode 100644 index 0000000000..4aec3b6904 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt3/step9/show_mpls_table.ref @@ -0,0 +1,168 @@ +{ + "17010":{ + "inLabel":17010, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16010, + "installed":true, + "nexthop":"10.0.1.1" + } + ] + }, + "17011":{ + "inLabel":17011, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16011, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "17020":{ + "inLabel":17020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16020, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "17021":{ + "inLabel":17021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16021, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "17030":{ + "inLabel":17030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "17031":{ + "inLabel":17031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "17040":{ + "inLabel":17040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16040, + "installed":true, + "nexthop":"10.0.1.2" + } + ] + }, + "17041":{ + "inLabel":17041, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16041, + "installed":true, + "interface":"eth-sw1" + } + ] + }, + "17050":{ + "inLabel":17050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16050, + "installed":true, + "nexthop":"10.0.5.5" + }, + { + "type":"SR (IS-IS)", + "outLabel":16050, + "installed":true, + "nexthop":"10.0.4.5" + } + ] + }, + "17051":{ + "inLabel":17051, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16051, + "installed":true, + "interface":"eth-rt5-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16051, + "installed":true, + "interface":"eth-rt5-1" + } + ] + }, + "17060":{ + "inLabel":17060, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16060, + "installed":true, + "nexthop":"10.0.5.5" + }, + { + "type":"SR (IS-IS)", + "outLabel":16060, + "installed":true, + "nexthop":"10.0.4.5" + } + ] + }, + "17061":{ + "inLabel":17061, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16061, + "installed":true, + "interface":"eth-rt5-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16061, + "installed":true, + "interface":"eth-rt5-1" + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt3/step9/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt3/step9/show_yang_interface_isis_adjacencies.ref new file mode 120000 index 0000000000..0879b84d23 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt3/step9/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1 @@ +../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file diff --git a/tests/topotests/isis-sr-topo1/rt3/zebra.conf b/tests/topotests/isis-sr-topo1/rt3/zebra.conf new file mode 100644 index 0000000000..3254529386 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt3/zebra.conf @@ -0,0 +1,25 @@ +log file zebra.log +! +hostname rt3 +! +debug zebra kernel +debug zebra packet +debug zebra mpls +! +interface lo + ip address 3.3.3.3/32 + ipv6 address 2001:db8:1000::3/128 +! +interface eth-sw1 + ip address 10.0.1.3/24 +! +interface eth-rt5-1 + ip address 10.0.4.3/24 +! +interface eth-rt5-2 + ip address 10.0.5.3/24 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/isis-sr-topo1/rt4/isisd.conf b/tests/topotests/isis-sr-topo1/rt4/isisd.conf new file mode 100644 index 0000000000..39003b9d7b --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt4/isisd.conf @@ -0,0 +1,49 @@ +hostname rt4 +log file isisd.log +! +debug isis events +debug isis route-events +debug isis spf-events +debug isis sr-events +debug isis lsp-gen +! +interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive +! +interface eth-rt2-1 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 +! +interface eth-rt2-2 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 +! +interface eth-rt5 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 +! +interface eth-rt6 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 +! +router isis 1 + net 49.0000.0000.0000.0004.00 + is-type level-1 + lsp-gen-interval 2 + topology ipv6-unicast + segment-routing on + segment-routing global-block 16000 23999 + segment-routing node-msd 8 + segment-routing prefix 4.4.4.4/32 index 40 no-php-flag + segment-routing prefix 2001:db8:1000::4/128 index 41 no-php-flag +! diff --git a/tests/topotests/isis-sr-topo1/rt4/step1/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt4/step1/show_ip_route.ref new file mode 100644 index 0000000000..f06182b088 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt4/step1/show_ip_route.ref @@ -0,0 +1,323 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true, + "labels":[ + 16010 + ] + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16010 + ] + } + ] + } + ], + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true, + "labels":[ + 16020 + ] + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16020 + ] + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true, + "labels":[ + 16030 + ] + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16030 + ] + }, + { + "fib":true, + "ip":"10.0.6.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true, + "labels":[ + 16030 + ] + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.6.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true, + "labels":[ + 16050 + ] + } + ] + } + ], + "6.6.6.6\/32":[ + { + "prefix":"6.6.6.6\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 0 + ] + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1" + }, + { + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true + }, + { + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2" + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.6.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.6.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true + } + ] + } + ], + "10.0.6.0\/24":[ + { + "prefix":"10.0.6.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.6.5", + "afi":"ipv4", + "interfaceName":"eth-rt5" + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6" + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.6.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true + }, + { + "fib":true, + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt4/step1/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt4/step1/show_ipv6_route.ref new file mode 100644 index 0000000000..f5772f2726 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt4/step1/show_ipv6_route.ref @@ -0,0 +1,148 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16011 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-1", + "active":true, + "labels":[ + 16011 + ] + } + ] + } + ], + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16021 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-1", + "active":true, + "labels":[ + 16021 + ] + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16031 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5", + "active":true, + "labels":[ + 16031 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-1", + "active":true, + "labels":[ + 16031 + ] + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5", + "active":true, + "labels":[ + 16051 + ] + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 2 + ] + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt4/step1/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt4/step1/show_mpls_table.ref new file mode 100644 index 0000000000..b7fb69dcde --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt4/step1/show_mpls_table.ref @@ -0,0 +1,192 @@ +{ + "16010":{ + "inLabel":16010, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16010, + "installed":true, + "nexthop":"10.0.3.2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16010, + "installed":true, + "nexthop":"10.0.2.2" + } + ] + }, + "16011":{ + "inLabel":16011, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16011, + "installed":true, + "interface":"eth-rt2-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16011, + "installed":true, + "interface":"eth-rt2-1" + } + ] + }, + "16020":{ + "inLabel":16020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16020, + "installed":true, + "nexthop":"10.0.3.2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16020, + "installed":true, + "nexthop":"10.0.2.2" + } + ] + }, + "16021":{ + "inLabel":16021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16021, + "installed":true, + "interface":"eth-rt2-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16021, + "installed":true, + "interface":"eth-rt2-1" + } + ] + }, + "16030":{ + "inLabel":16030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16030, + "installed":true, + "nexthop":"10.0.3.2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16030, + "installed":true, + "nexthop":"10.0.2.2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16030, + "installed":true, + "nexthop":"10.0.6.5" + } + ] + }, + "16031":{ + "inLabel":16031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16031, + "installed":true, + "interface":"eth-rt2-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16031, + "installed":true, + "interface":"eth-rt2-1" + }, + { + "type":"SR (IS-IS)", + "outLabel":16031, + "installed":true, + "interface":"eth-rt5" + } + ] + }, + "16040":{ + "inLabel":16040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16041":{ + "inLabel":16041, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16050":{ + "inLabel":16050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16050, + "installed":true, + "nexthop":"10.0.6.5" + } + ] + }, + "16051":{ + "inLabel":16051, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16051, + "installed":true, + "interface":"eth-rt5" + } + ] + }, + "16060":{ + "inLabel":16060, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":0, + "installed":true, + "nexthop":"10.0.7.6" + } + ] + }, + "16061":{ + "inLabel":16061, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":2, + "installed":true, + "interface":"eth-rt6" + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt4/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt4/step1/show_yang_interface_isis_adjacencies.ref new file mode 100644 index 0000000000..2eb64b6fc9 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt4/step1/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1,82 @@ +{ + "frr-interface:lib": { + "interface": [ + { + "name": "eth-rt2-1", + "vrf": "default", + "state": { + "frr-isisd:isis": { + "adjacencies": { + "adjacency": [ + { + "neighbor-sys-type": "level-1", + "neighbor-sysid": "0000.0000.0002", + "hold-timer": 9, + "neighbor-priority": 0, + "state": "up" + } + ] + } + } + } + }, + { + "name": "eth-rt2-2", + "vrf": "default", + "state": { + "frr-isisd:isis": { + "adjacencies": { + "adjacency": [ + { + "neighbor-sys-type": "level-1", + "neighbor-sysid": "0000.0000.0002", + "hold-timer": 9, + "neighbor-priority": 0, + "state": "up" + } + ] + } + } + } + }, + { + "name": "eth-rt5", + "vrf": "default", + "state": { + "frr-isisd:isis": { + "adjacencies": { + "adjacency": [ + { + "neighbor-sys-type": "level-1", + "neighbor-sysid": "0000.0000.0005", + "hold-timer": 9, + "neighbor-priority": 0, + "state": "up" + } + ] + } + } + } + }, + { + "name": "eth-rt6", + "vrf": "default", + "state": { + "frr-isisd:isis": { + "adjacencies": { + "adjacency": [ + { + "neighbor-sys-type": "level-1", + "neighbor-sysid": "0000.0000.0006", + "hold-timer": 9, + "neighbor-priority": 0, + "state": "up" + } + ] + } + } + } + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt4/step10/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt4/step10/show_ip_route.ref new file mode 100644 index 0000000000..d7d42120a0 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt4/step10/show_ip_route.ref @@ -0,0 +1,277 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16010 + ] + } + ] + } + ], + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16020 + ] + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16030 + ] + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 18050 + ] + } + ] + } + ], + "6.6.6.6\/32":[ + { + "prefix":"6.6.6.6\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 3 + ] + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2" + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true + }, + { + "fib":true, + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true + }, + { + "fib":true, + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "10.0.6.0\/24":[ + { + "prefix":"10.0.6.0\/24", + "protocol":"isis", + "distance":115, + "metric":30, + "nexthops":[ + { + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6" + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt4/step10/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt4/step10/show_ipv6_route.ref new file mode 100644 index 0000000000..235c1facc6 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt4/step10/show_ipv6_route.ref @@ -0,0 +1,139 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16011 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-1", + "active":true, + "labels":[ + 16011 + ] + } + ] + } + ], + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16021 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-1", + "active":true, + "labels":[ + 16021 + ] + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16031 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-1", + "active":true, + "labels":[ + 16031 + ] + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 18051 + ] + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 3 + ] + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt4/step10/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt4/step10/show_mpls_table.ref new file mode 100644 index 0000000000..86ceaf4883 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt4/step10/show_mpls_table.ref @@ -0,0 +1,162 @@ +{ + "16010":{ + "inLabel":16010, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16010, + "installed":true, + "nexthop":"10.0.3.2" + } + ] + }, + "16011":{ + "inLabel":16011, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16011, + "installed":true, + "interface":"eth-rt2-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16011, + "installed":true, + "interface":"eth-rt2-1" + } + ] + }, + "16020":{ + "inLabel":16020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16020, + "installed":true, + "nexthop":"10.0.3.2" + } + ] + }, + "16021":{ + "inLabel":16021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16021, + "installed":true, + "interface":"eth-rt2-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16021, + "installed":true, + "interface":"eth-rt2-1" + } + ] + }, + "16030":{ + "inLabel":16030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16030, + "installed":true, + "nexthop":"10.0.3.2" + } + ] + }, + "16031":{ + "inLabel":16031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16031, + "installed":true, + "interface":"eth-rt2-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16031, + "installed":true, + "interface":"eth-rt2-1" + } + ] + }, + "16040":{ + "inLabel":16040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16041":{ + "inLabel":16041, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16050":{ + "inLabel":16050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":18050, + "installed":true, + "nexthop":"10.0.7.6" + } + ] + }, + "16051":{ + "inLabel":16051, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":18051, + "installed":true, + "interface":"eth-rt6" + } + ] + }, + "16060":{ + "inLabel":16060, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true, + "nexthop":"10.0.7.6" + } + ] + }, + "16061":{ + "inLabel":16061, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true, + "interface":"eth-rt6" + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt4/step10/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt4/step10/show_yang_interface_isis_adjacencies.ref new file mode 120000 index 0000000000..761cb2fd2f --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt4/step10/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1 @@ +../step2/show_yang_interface_isis_adjacencies.ref \ No newline at end of file diff --git a/tests/topotests/isis-sr-topo1/rt4/step2/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt4/step2/show_ip_route.ref new file mode 100644 index 0000000000..4789f7268f --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt4/step2/show_ip_route.ref @@ -0,0 +1,335 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true, + "labels":[ + 16010 + ] + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16010 + ] + } + ] + } + ], + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true, + "labels":[ + 16020 + ] + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16020 + ] + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true, + "labels":[ + 16030 + ] + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16030 + ] + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 16050 + ] + } + ] + } + ], + "6.6.6.6\/32":[ + { + "prefix":"6.6.6.6\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 0 + ] + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1" + }, + { + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true + }, + { + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2" + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true + }, + { + "fib":true, + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true + }, + { + "fib":true, + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "10.0.6.0\/24":[ + { + "prefix":"10.0.6.0\/24", + "protocol":"isis", + "distance":115, + "metric":30, + "nexthops":[ + { + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6" + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt4/step2/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt4/step2/show_ipv6_route.ref new file mode 100644 index 0000000000..871b303cab --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt4/step2/show_ipv6_route.ref @@ -0,0 +1,139 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16011 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-1", + "active":true, + "labels":[ + 16011 + ] + } + ] + } + ], + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16021 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-1", + "active":true, + "labels":[ + 16021 + ] + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16031 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-1", + "active":true, + "labels":[ + 16031 + ] + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 16051 + ] + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 2 + ] + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt4/step2/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt4/step2/show_mpls_table.ref new file mode 100644 index 0000000000..ff83c374f0 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt4/step2/show_mpls_table.ref @@ -0,0 +1,180 @@ +{ + "16010":{ + "inLabel":16010, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16010, + "installed":true, + "nexthop":"10.0.3.2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16010, + "installed":true, + "nexthop":"10.0.2.2" + } + ] + }, + "16011":{ + "inLabel":16011, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16011, + "installed":true, + "interface":"eth-rt2-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16011, + "installed":true, + "interface":"eth-rt2-1" + } + ] + }, + "16020":{ + "inLabel":16020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16020, + "installed":true, + "nexthop":"10.0.3.2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16020, + "installed":true, + "nexthop":"10.0.2.2" + } + ] + }, + "16021":{ + "inLabel":16021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16021, + "installed":true, + "interface":"eth-rt2-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16021, + "installed":true, + "interface":"eth-rt2-1" + } + ] + }, + "16030":{ + "inLabel":16030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16030, + "installed":true, + "nexthop":"10.0.3.2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16030, + "installed":true, + "nexthop":"10.0.2.2" + } + ] + }, + "16031":{ + "inLabel":16031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16031, + "installed":true, + "interface":"eth-rt2-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16031, + "installed":true, + "interface":"eth-rt2-1" + } + ] + }, + "16040":{ + "inLabel":16040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16041":{ + "inLabel":16041, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16050":{ + "inLabel":16050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16050, + "installed":true, + "nexthop":"10.0.7.6" + } + ] + }, + "16051":{ + "inLabel":16051, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16051, + "installed":true, + "interface":"eth-rt6" + } + ] + }, + "16060":{ + "inLabel":16060, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":0, + "installed":true, + "nexthop":"10.0.7.6" + } + ] + }, + "16061":{ + "inLabel":16061, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":2, + "installed":true, + "interface":"eth-rt6" + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt4/step2/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt4/step2/show_yang_interface_isis_adjacencies.ref new file mode 100644 index 0000000000..be1e00b8a2 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt4/step2/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1,63 @@ +{ + "frr-interface:lib": { + "interface": [ + { + "name": "eth-rt2-1", + "vrf": "default", + "state": { + "frr-isisd:isis": { + "adjacencies": { + "adjacency": [ + { + "neighbor-sys-type": "level-1", + "neighbor-sysid": "0000.0000.0002", + "hold-timer": 9, + "neighbor-priority": 0, + "state": "up" + } + ] + } + } + } + }, + { + "name": "eth-rt2-2", + "vrf": "default", + "state": { + "frr-isisd:isis": { + "adjacencies": { + "adjacency": [ + { + "neighbor-sys-type": "level-1", + "neighbor-sysid": "0000.0000.0002", + "hold-timer": 9, + "neighbor-priority": 0, + "state": "up" + } + ] + } + } + } + }, + { + "name": "eth-rt6", + "vrf": "default", + "state": { + "frr-isisd:isis": { + "adjacencies": { + "adjacency": [ + { + "neighbor-sys-type": "level-1", + "neighbor-sysid": "0000.0000.0006", + "hold-timer": 9, + "neighbor-priority": 0, + "state": "up" + } + ] + } + } + } + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt4/step3/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt4/step3/show_ip_route.ref new file mode 100644 index 0000000000..d86562deb9 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt4/step3/show_ip_route.ref @@ -0,0 +1,306 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true, + "labels":[ + 16010 + ] + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16010 + ] + } + ] + } + ], + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true, + "labels":[ + 16020 + ] + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16020 + ] + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true, + "labels":[ + 16030 + ] + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16030 + ] + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":40, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true, + "labels":[ + 16050 + ] + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16050 + ] + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1" + }, + { + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true + }, + { + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2" + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true + } + ] + } + ], + "10.0.6.0\/24":[ + { + "prefix":"10.0.6.0\/24", + "protocol":"isis", + "distance":115, + "metric":40, + "nexthops":[ + { + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true + }, + { + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":40, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt4/step3/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt4/step3/show_ipv6_route.ref new file mode 100644 index 0000000000..c09f584641 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt4/step3/show_ipv6_route.ref @@ -0,0 +1,126 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16011 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-1", + "active":true, + "labels":[ + 16011 + ] + } + ] + } + ], + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16021 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-1", + "active":true, + "labels":[ + 16021 + ] + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16031 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-1", + "active":true, + "labels":[ + 16031 + ] + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":40, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16051 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-1", + "active":true, + "labels":[ + 16051 + ] + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt4/step3/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt4/step3/show_mpls_table.ref new file mode 100644 index 0000000000..85c6c055c9 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt4/step3/show_mpls_table.ref @@ -0,0 +1,168 @@ +{ + "16010":{ + "inLabel":16010, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16010, + "installed":true, + "nexthop":"10.0.3.2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16010, + "installed":true, + "nexthop":"10.0.2.2" + } + ] + }, + "16011":{ + "inLabel":16011, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16011, + "installed":true, + "interface":"eth-rt2-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16011, + "installed":true, + "interface":"eth-rt2-1" + } + ] + }, + "16020":{ + "inLabel":16020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16020, + "installed":true, + "nexthop":"10.0.3.2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16020, + "installed":true, + "nexthop":"10.0.2.2" + } + ] + }, + "16021":{ + "inLabel":16021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16021, + "installed":true, + "interface":"eth-rt2-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16021, + "installed":true, + "interface":"eth-rt2-1" + } + ] + }, + "16030":{ + "inLabel":16030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16030, + "installed":true, + "nexthop":"10.0.3.2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16030, + "installed":true, + "nexthop":"10.0.2.2" + } + ] + }, + "16031":{ + "inLabel":16031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16031, + "installed":true, + "interface":"eth-rt2-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16031, + "installed":true, + "interface":"eth-rt2-1" + } + ] + }, + "16040":{ + "inLabel":16040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16041":{ + "inLabel":16041, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16050":{ + "inLabel":16050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16050, + "installed":true, + "nexthop":"10.0.3.2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16050, + "installed":true, + "nexthop":"10.0.2.2" + } + ] + }, + "16051":{ + "inLabel":16051, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16051, + "installed":true, + "interface":"eth-rt2-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16051, + "installed":true, + "interface":"eth-rt2-1" + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt4/step3/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt4/step3/show_yang_interface_isis_adjacencies.ref new file mode 100644 index 0000000000..bcade1ca90 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt4/step3/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1,44 @@ +{ + "frr-interface:lib": { + "interface": [ + { + "name": "eth-rt2-1", + "vrf": "default", + "state": { + "frr-isisd:isis": { + "adjacencies": { + "adjacency": [ + { + "neighbor-sys-type": "level-1", + "neighbor-sysid": "0000.0000.0002", + "hold-timer": 9, + "neighbor-priority": 0, + "state": "up" + } + ] + } + } + } + }, + { + "name": "eth-rt2-2", + "vrf": "default", + "state": { + "frr-isisd:isis": { + "adjacencies": { + "adjacency": [ + { + "neighbor-sys-type": "level-1", + "neighbor-sysid": "0000.0000.0002", + "hold-timer": 9, + "neighbor-priority": 0, + "state": "up" + } + ] + } + } + } + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt4/step4/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt4/step4/show_ip_route.ref new file mode 100644 index 0000000000..3c7dfda0a3 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt4/step4/show_ip_route.ref @@ -0,0 +1,335 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true, + "labels":[ + 16010 + ] + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16010 + ] + } + ] + } + ], + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true, + "labels":[ + 16020 + ] + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16020 + ] + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true, + "labels":[ + 16030 + ] + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16030 + ] + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 18050 + ] + } + ] + } + ], + "6.6.6.6\/32":[ + { + "prefix":"6.6.6.6\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 0 + ] + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1" + }, + { + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true + }, + { + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2" + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true + }, + { + "fib":true, + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true + }, + { + "fib":true, + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "10.0.6.0\/24":[ + { + "prefix":"10.0.6.0\/24", + "protocol":"isis", + "distance":115, + "metric":30, + "nexthops":[ + { + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6" + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt4/step4/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt4/step4/show_ipv6_route.ref new file mode 100644 index 0000000000..38b51822dd --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt4/step4/show_ipv6_route.ref @@ -0,0 +1,139 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16011 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-1", + "active":true, + "labels":[ + 16011 + ] + } + ] + } + ], + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16021 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-1", + "active":true, + "labels":[ + 16021 + ] + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16031 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-1", + "active":true, + "labels":[ + 16031 + ] + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 18051 + ] + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 2 + ] + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt4/step4/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt4/step4/show_mpls_table.ref new file mode 100644 index 0000000000..4e5638f34f --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt4/step4/show_mpls_table.ref @@ -0,0 +1,180 @@ +{ + "16010":{ + "inLabel":16010, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16010, + "installed":true, + "nexthop":"10.0.3.2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16010, + "installed":true, + "nexthop":"10.0.2.2" + } + ] + }, + "16011":{ + "inLabel":16011, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16011, + "installed":true, + "interface":"eth-rt2-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16011, + "installed":true, + "interface":"eth-rt2-1" + } + ] + }, + "16020":{ + "inLabel":16020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16020, + "installed":true, + "nexthop":"10.0.3.2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16020, + "installed":true, + "nexthop":"10.0.2.2" + } + ] + }, + "16021":{ + "inLabel":16021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16021, + "installed":true, + "interface":"eth-rt2-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16021, + "installed":true, + "interface":"eth-rt2-1" + } + ] + }, + "16030":{ + "inLabel":16030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16030, + "installed":true, + "nexthop":"10.0.3.2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16030, + "installed":true, + "nexthop":"10.0.2.2" + } + ] + }, + "16031":{ + "inLabel":16031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16031, + "installed":true, + "interface":"eth-rt2-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16031, + "installed":true, + "interface":"eth-rt2-1" + } + ] + }, + "16040":{ + "inLabel":16040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16041":{ + "inLabel":16041, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16050":{ + "inLabel":16050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":18050, + "installed":true, + "nexthop":"10.0.7.6" + } + ] + }, + "16051":{ + "inLabel":16051, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":18051, + "installed":true, + "interface":"eth-rt6" + } + ] + }, + "16060":{ + "inLabel":16060, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":0, + "installed":true, + "nexthop":"10.0.7.6" + } + ] + }, + "16061":{ + "inLabel":16061, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":2, + "installed":true, + "interface":"eth-rt6" + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt4/step4/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt4/step4/show_yang_interface_isis_adjacencies.ref new file mode 120000 index 0000000000..761cb2fd2f --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt4/step4/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1 @@ +../step2/show_yang_interface_isis_adjacencies.ref \ No newline at end of file diff --git a/tests/topotests/isis-sr-topo1/rt4/step5/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt4/step5/show_ip_route.ref new file mode 100644 index 0000000000..90f69c06b8 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt4/step5/show_ip_route.ref @@ -0,0 +1,329 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true, + "labels":[ + 16010 + ] + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16010 + ] + } + ] + } + ], + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true, + "labels":[ + 16020 + ] + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16020 + ] + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true, + "labels":[ + 16030 + ] + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16030 + ] + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "6.6.6.6\/32":[ + { + "prefix":"6.6.6.6\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1" + }, + { + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true + }, + { + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2" + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true + }, + { + "fib":true, + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true + }, + { + "fib":true, + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "10.0.6.0\/24":[ + { + "prefix":"10.0.6.0\/24", + "protocol":"isis", + "distance":115, + "metric":30, + "nexthops":[ + { + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6" + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt4/step5/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt4/step5/show_ipv6_route.ref new file mode 100644 index 0000000000..04056ed873 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt4/step5/show_ipv6_route.ref @@ -0,0 +1,133 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16011 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-1", + "active":true, + "labels":[ + 16011 + ] + } + ] + } + ], + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16021 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-1", + "active":true, + "labels":[ + 16021 + ] + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16031 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-1", + "active":true, + "labels":[ + 16031 + ] + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt4/step5/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt4/step5/show_mpls_table.ref new file mode 100644 index 0000000000..4df722be4f --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt4/step5/show_mpls_table.ref @@ -0,0 +1,132 @@ +{ + "16010":{ + "inLabel":16010, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16010, + "installed":true, + "nexthop":"10.0.3.2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16010, + "installed":true, + "nexthop":"10.0.2.2" + } + ] + }, + "16011":{ + "inLabel":16011, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16011, + "installed":true, + "interface":"eth-rt2-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16011, + "installed":true, + "interface":"eth-rt2-1" + } + ] + }, + "16020":{ + "inLabel":16020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16020, + "installed":true, + "nexthop":"10.0.3.2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16020, + "installed":true, + "nexthop":"10.0.2.2" + } + ] + }, + "16021":{ + "inLabel":16021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16021, + "installed":true, + "interface":"eth-rt2-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16021, + "installed":true, + "interface":"eth-rt2-1" + } + ] + }, + "16030":{ + "inLabel":16030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16030, + "installed":true, + "nexthop":"10.0.3.2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16030, + "installed":true, + "nexthop":"10.0.2.2" + } + ] + }, + "16031":{ + "inLabel":16031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16031, + "installed":true, + "interface":"eth-rt2-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16031, + "installed":true, + "interface":"eth-rt2-1" + } + ] + }, + "16040":{ + "inLabel":16040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16041":{ + "inLabel":16041, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt4/step5/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt4/step5/show_yang_interface_isis_adjacencies.ref new file mode 120000 index 0000000000..761cb2fd2f --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt4/step5/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1 @@ +../step2/show_yang_interface_isis_adjacencies.ref \ No newline at end of file diff --git a/tests/topotests/isis-sr-topo1/rt4/step6/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt4/step6/show_ip_route.ref new file mode 100644 index 0000000000..3c7dfda0a3 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt4/step6/show_ip_route.ref @@ -0,0 +1,335 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true, + "labels":[ + 16010 + ] + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16010 + ] + } + ] + } + ], + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true, + "labels":[ + 16020 + ] + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16020 + ] + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true, + "labels":[ + 16030 + ] + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16030 + ] + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 18050 + ] + } + ] + } + ], + "6.6.6.6\/32":[ + { + "prefix":"6.6.6.6\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 0 + ] + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1" + }, + { + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true + }, + { + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2" + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true + }, + { + "fib":true, + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true + }, + { + "fib":true, + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "10.0.6.0\/24":[ + { + "prefix":"10.0.6.0\/24", + "protocol":"isis", + "distance":115, + "metric":30, + "nexthops":[ + { + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6" + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt4/step6/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt4/step6/show_ipv6_route.ref new file mode 100644 index 0000000000..38b51822dd --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt4/step6/show_ipv6_route.ref @@ -0,0 +1,139 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16011 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-1", + "active":true, + "labels":[ + 16011 + ] + } + ] + } + ], + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16021 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-1", + "active":true, + "labels":[ + 16021 + ] + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16031 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-1", + "active":true, + "labels":[ + 16031 + ] + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 18051 + ] + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 2 + ] + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt4/step6/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt4/step6/show_mpls_table.ref new file mode 100644 index 0000000000..4e5638f34f --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt4/step6/show_mpls_table.ref @@ -0,0 +1,180 @@ +{ + "16010":{ + "inLabel":16010, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16010, + "installed":true, + "nexthop":"10.0.3.2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16010, + "installed":true, + "nexthop":"10.0.2.2" + } + ] + }, + "16011":{ + "inLabel":16011, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16011, + "installed":true, + "interface":"eth-rt2-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16011, + "installed":true, + "interface":"eth-rt2-1" + } + ] + }, + "16020":{ + "inLabel":16020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16020, + "installed":true, + "nexthop":"10.0.3.2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16020, + "installed":true, + "nexthop":"10.0.2.2" + } + ] + }, + "16021":{ + "inLabel":16021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16021, + "installed":true, + "interface":"eth-rt2-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16021, + "installed":true, + "interface":"eth-rt2-1" + } + ] + }, + "16030":{ + "inLabel":16030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16030, + "installed":true, + "nexthop":"10.0.3.2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16030, + "installed":true, + "nexthop":"10.0.2.2" + } + ] + }, + "16031":{ + "inLabel":16031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16031, + "installed":true, + "interface":"eth-rt2-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16031, + "installed":true, + "interface":"eth-rt2-1" + } + ] + }, + "16040":{ + "inLabel":16040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16041":{ + "inLabel":16041, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16050":{ + "inLabel":16050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":18050, + "installed":true, + "nexthop":"10.0.7.6" + } + ] + }, + "16051":{ + "inLabel":16051, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":18051, + "installed":true, + "interface":"eth-rt6" + } + ] + }, + "16060":{ + "inLabel":16060, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":0, + "installed":true, + "nexthop":"10.0.7.6" + } + ] + }, + "16061":{ + "inLabel":16061, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":2, + "installed":true, + "interface":"eth-rt6" + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt4/step6/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt4/step6/show_yang_interface_isis_adjacencies.ref new file mode 120000 index 0000000000..761cb2fd2f --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt4/step6/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1 @@ +../step2/show_yang_interface_isis_adjacencies.ref \ No newline at end of file diff --git a/tests/topotests/isis-sr-topo1/rt4/step7/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt4/step7/show_ip_route.ref new file mode 100644 index 0000000000..a3ac4ac109 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt4/step7/show_ip_route.ref @@ -0,0 +1,329 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true + } + ] + } + ], + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true, + "labels":[ + 16020 + ] + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16020 + ] + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true, + "labels":[ + 16030 + ] + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16030 + ] + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 18050 + ] + } + ] + } + ], + "6.6.6.6\/32":[ + { + "prefix":"6.6.6.6\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 0 + ] + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1" + }, + { + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true + }, + { + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2" + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true + }, + { + "fib":true, + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true + }, + { + "fib":true, + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "10.0.6.0\/24":[ + { + "prefix":"10.0.6.0\/24", + "protocol":"isis", + "distance":115, + "metric":30, + "nexthops":[ + { + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6" + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt4/step7/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt4/step7/show_ipv6_route.ref new file mode 100644 index 0000000000..c59abbd2f5 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt4/step7/show_ipv6_route.ref @@ -0,0 +1,133 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-2", + "active":true + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-1", + "active":true + } + ] + } + ], + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16021 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-1", + "active":true, + "labels":[ + 16021 + ] + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16031 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-1", + "active":true, + "labels":[ + 16031 + ] + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 18051 + ] + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 2 + ] + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt4/step7/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt4/step7/show_mpls_table.ref new file mode 100644 index 0000000000..512c057b31 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt4/step7/show_mpls_table.ref @@ -0,0 +1,144 @@ +{ + "16020":{ + "inLabel":16020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16020, + "installed":true, + "nexthop":"10.0.3.2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16020, + "installed":true, + "nexthop":"10.0.2.2" + } + ] + }, + "16021":{ + "inLabel":16021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16021, + "installed":true, + "interface":"eth-rt2-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16021, + "installed":true, + "interface":"eth-rt2-1" + } + ] + }, + "16030":{ + "inLabel":16030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16030, + "installed":true, + "nexthop":"10.0.3.2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16030, + "installed":true, + "nexthop":"10.0.2.2" + } + ] + }, + "16031":{ + "inLabel":16031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16031, + "installed":true, + "interface":"eth-rt2-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16031, + "installed":true, + "interface":"eth-rt2-1" + } + ] + }, + "16040":{ + "inLabel":16040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16041":{ + "inLabel":16041, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16050":{ + "inLabel":16050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":18050, + "installed":true, + "nexthop":"10.0.7.6" + } + ] + }, + "16051":{ + "inLabel":16051, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":18051, + "installed":true, + "interface":"eth-rt6" + } + ] + }, + "16060":{ + "inLabel":16060, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":0, + "installed":true, + "nexthop":"10.0.7.6" + } + ] + }, + "16061":{ + "inLabel":16061, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":2, + "installed":true, + "interface":"eth-rt6" + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt4/step7/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt4/step7/show_yang_interface_isis_adjacencies.ref new file mode 120000 index 0000000000..761cb2fd2f --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt4/step7/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1 @@ +../step2/show_yang_interface_isis_adjacencies.ref \ No newline at end of file diff --git a/tests/topotests/isis-sr-topo1/rt4/step8/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt4/step8/show_ip_route.ref new file mode 100644 index 0000000000..3c7dfda0a3 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt4/step8/show_ip_route.ref @@ -0,0 +1,335 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true, + "labels":[ + 16010 + ] + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16010 + ] + } + ] + } + ], + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true, + "labels":[ + 16020 + ] + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16020 + ] + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true, + "labels":[ + 16030 + ] + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16030 + ] + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 18050 + ] + } + ] + } + ], + "6.6.6.6\/32":[ + { + "prefix":"6.6.6.6\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 0 + ] + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1" + }, + { + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true + }, + { + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2" + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true + }, + { + "fib":true, + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true + }, + { + "fib":true, + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "10.0.6.0\/24":[ + { + "prefix":"10.0.6.0\/24", + "protocol":"isis", + "distance":115, + "metric":30, + "nexthops":[ + { + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6" + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt4/step8/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt4/step8/show_ipv6_route.ref new file mode 100644 index 0000000000..38b51822dd --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt4/step8/show_ipv6_route.ref @@ -0,0 +1,139 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16011 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-1", + "active":true, + "labels":[ + 16011 + ] + } + ] + } + ], + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16021 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-1", + "active":true, + "labels":[ + 16021 + ] + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16031 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-1", + "active":true, + "labels":[ + 16031 + ] + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 18051 + ] + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 2 + ] + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt4/step8/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt4/step8/show_mpls_table.ref new file mode 100644 index 0000000000..4e5638f34f --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt4/step8/show_mpls_table.ref @@ -0,0 +1,180 @@ +{ + "16010":{ + "inLabel":16010, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16010, + "installed":true, + "nexthop":"10.0.3.2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16010, + "installed":true, + "nexthop":"10.0.2.2" + } + ] + }, + "16011":{ + "inLabel":16011, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16011, + "installed":true, + "interface":"eth-rt2-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16011, + "installed":true, + "interface":"eth-rt2-1" + } + ] + }, + "16020":{ + "inLabel":16020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16020, + "installed":true, + "nexthop":"10.0.3.2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16020, + "installed":true, + "nexthop":"10.0.2.2" + } + ] + }, + "16021":{ + "inLabel":16021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16021, + "installed":true, + "interface":"eth-rt2-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16021, + "installed":true, + "interface":"eth-rt2-1" + } + ] + }, + "16030":{ + "inLabel":16030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16030, + "installed":true, + "nexthop":"10.0.3.2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16030, + "installed":true, + "nexthop":"10.0.2.2" + } + ] + }, + "16031":{ + "inLabel":16031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16031, + "installed":true, + "interface":"eth-rt2-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16031, + "installed":true, + "interface":"eth-rt2-1" + } + ] + }, + "16040":{ + "inLabel":16040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16041":{ + "inLabel":16041, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16050":{ + "inLabel":16050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":18050, + "installed":true, + "nexthop":"10.0.7.6" + } + ] + }, + "16051":{ + "inLabel":16051, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":18051, + "installed":true, + "interface":"eth-rt6" + } + ] + }, + "16060":{ + "inLabel":16060, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":0, + "installed":true, + "nexthop":"10.0.7.6" + } + ] + }, + "16061":{ + "inLabel":16061, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":2, + "installed":true, + "interface":"eth-rt6" + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt4/step8/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt4/step8/show_yang_interface_isis_adjacencies.ref new file mode 120000 index 0000000000..761cb2fd2f --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt4/step8/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1 @@ +../step2/show_yang_interface_isis_adjacencies.ref \ No newline at end of file diff --git a/tests/topotests/isis-sr-topo1/rt4/step9/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt4/step9/show_ip_route.ref new file mode 100644 index 0000000000..73598e4605 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt4/step9/show_ip_route.ref @@ -0,0 +1,335 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true, + "labels":[ + 16010 + ] + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16010 + ] + } + ] + } + ], + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true, + "labels":[ + 16020 + ] + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16020 + ] + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true, + "labels":[ + 16030 + ] + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16030 + ] + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 18050 + ] + } + ] + } + ], + "6.6.6.6\/32":[ + { + "prefix":"6.6.6.6\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 3 + ] + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1" + }, + { + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true + }, + { + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2" + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true + }, + { + "fib":true, + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"eth-rt2-2", + "active":true + }, + { + "fib":true, + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "10.0.6.0\/24":[ + { + "prefix":"10.0.6.0\/24", + "protocol":"isis", + "distance":115, + "metric":30, + "nexthops":[ + { + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6" + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt4/step9/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt4/step9/show_ipv6_route.ref new file mode 100644 index 0000000000..235c1facc6 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt4/step9/show_ipv6_route.ref @@ -0,0 +1,139 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16011 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-1", + "active":true, + "labels":[ + 16011 + ] + } + ] + } + ], + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16021 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-1", + "active":true, + "labels":[ + 16021 + ] + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-2", + "active":true, + "labels":[ + 16031 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt2-1", + "active":true, + "labels":[ + 16031 + ] + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 18051 + ] + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 3 + ] + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt4/step9/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt4/step9/show_mpls_table.ref new file mode 100644 index 0000000000..5cdd99e425 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt4/step9/show_mpls_table.ref @@ -0,0 +1,180 @@ +{ + "16010":{ + "inLabel":16010, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16010, + "installed":true, + "nexthop":"10.0.3.2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16010, + "installed":true, + "nexthop":"10.0.2.2" + } + ] + }, + "16011":{ + "inLabel":16011, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16011, + "installed":true, + "interface":"eth-rt2-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16011, + "installed":true, + "interface":"eth-rt2-1" + } + ] + }, + "16020":{ + "inLabel":16020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16020, + "installed":true, + "nexthop":"10.0.3.2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16020, + "installed":true, + "nexthop":"10.0.2.2" + } + ] + }, + "16021":{ + "inLabel":16021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16021, + "installed":true, + "interface":"eth-rt2-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16021, + "installed":true, + "interface":"eth-rt2-1" + } + ] + }, + "16030":{ + "inLabel":16030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16030, + "installed":true, + "nexthop":"10.0.3.2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16030, + "installed":true, + "nexthop":"10.0.2.2" + } + ] + }, + "16031":{ + "inLabel":16031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16031, + "installed":true, + "interface":"eth-rt2-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":16031, + "installed":true, + "interface":"eth-rt2-1" + } + ] + }, + "16040":{ + "inLabel":16040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16041":{ + "inLabel":16041, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16050":{ + "inLabel":16050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":18050, + "installed":true, + "nexthop":"10.0.7.6" + } + ] + }, + "16051":{ + "inLabel":16051, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":18051, + "installed":true, + "interface":"eth-rt6" + } + ] + }, + "16060":{ + "inLabel":16060, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true, + "nexthop":"10.0.7.6" + } + ] + }, + "16061":{ + "inLabel":16061, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true, + "interface":"eth-rt6" + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt4/step9/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt4/step9/show_yang_interface_isis_adjacencies.ref new file mode 120000 index 0000000000..761cb2fd2f --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt4/step9/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1 @@ +../step2/show_yang_interface_isis_adjacencies.ref \ No newline at end of file diff --git a/tests/topotests/isis-sr-topo1/rt4/zebra.conf b/tests/topotests/isis-sr-topo1/rt4/zebra.conf new file mode 100644 index 0000000000..4945897e9d --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt4/zebra.conf @@ -0,0 +1,28 @@ +log file zebra.log +! +hostname rt4 +! +debug zebra kernel +debug zebra packet +debug zebra mpls +! +interface lo + ip address 4.4.4.4/32 + ipv6 address 2001:db8:1000::4/128 +! +interface eth-rt2-1 + ip address 10.0.2.4/24 +! +interface eth-rt2-2 + ip address 10.0.3.4/24 +! +interface eth-rt5 + ip address 10.0.6.4/24 +! +interface eth-rt6 + ip address 10.0.7.4/24 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/isis-sr-topo1/rt5/isisd.conf b/tests/topotests/isis-sr-topo1/rt5/isisd.conf new file mode 100644 index 0000000000..e693ca156c --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt5/isisd.conf @@ -0,0 +1,49 @@ +hostname rt5 +log file isisd.log +! +debug isis events +debug isis route-events +debug isis spf-events +debug isis sr-events +debug isis lsp-gen +! +interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive +! +interface eth-rt3-1 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 +! +interface eth-rt3-2 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 +! +interface eth-rt4 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 +! +interface eth-rt6 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 +! +router isis 1 + net 49.0000.0000.0000.0005.00 + is-type level-1 + lsp-gen-interval 2 + topology ipv6-unicast + segment-routing on + segment-routing global-block 16000 23999 + segment-routing node-msd 8 + segment-routing prefix 5.5.5.5/32 index 50 no-php-flag + segment-routing prefix 2001:db8:1000::5/128 index 51 no-php-flag +! diff --git a/tests/topotests/isis-sr-topo1/rt5/step1/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt5/step1/show_ip_route.ref new file mode 100644 index 0000000000..8eaf40f236 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt5/step1/show_ip_route.ref @@ -0,0 +1,323 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17010 + ] + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17010 + ] + } + ] + } + ], + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17020 + ] + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17020 + ] + }, + { + "fib":true, + "ip":"10.0.6.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true, + "labels":[ + 16020 + ] + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17030 + ] + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17030 + ] + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.6.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true, + "labels":[ + 16040 + ] + } + ] + } + ], + "6.6.6.6\/32":[ + { + "prefix":"6.6.6.6\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 0 + ] + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.6.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.6.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1" + }, + { + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true + }, + { + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2" + } + ] + } + ], + "10.0.6.0\/24":[ + { + "prefix":"10.0.6.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.6.4", + "afi":"ipv4", + "interfaceName":"eth-rt4" + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.6.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true + }, + { + "fib":true, + "ip":"10.0.8.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.8.6", + "afi":"ipv4", + "interfaceName":"eth-rt6" + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt5/step1/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt5/step1/show_ipv6_route.ref new file mode 100644 index 0000000000..294567edc8 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt5/step1/show_ipv6_route.ref @@ -0,0 +1,148 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17011 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17011 + ] + } + ] + } + ], + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4", + "active":true, + "labels":[ + 16021 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17021 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17021 + ] + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17031 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17031 + ] + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4", + "active":true, + "labels":[ + 16041 + ] + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 2 + ] + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt5/step1/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt5/step1/show_mpls_table.ref new file mode 100644 index 0000000000..9054c9c4af --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt5/step1/show_mpls_table.ref @@ -0,0 +1,192 @@ +{ + "16010":{ + "inLabel":16010, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17010, + "installed":true, + "nexthop":"10.0.5.3" + }, + { + "type":"SR (IS-IS)", + "outLabel":17010, + "installed":true, + "nexthop":"10.0.4.3" + } + ] + }, + "16011":{ + "inLabel":16011, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17011, + "installed":true, + "interface":"eth-rt3-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":17011, + "installed":true, + "interface":"eth-rt3-1" + } + ] + }, + "16020":{ + "inLabel":16020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17020, + "installed":true, + "nexthop":"10.0.5.3" + }, + { + "type":"SR (IS-IS)", + "outLabel":17020, + "installed":true, + "nexthop":"10.0.4.3" + }, + { + "type":"SR (IS-IS)", + "outLabel":16020, + "installed":true, + "nexthop":"10.0.6.4" + } + ] + }, + "16021":{ + "inLabel":16021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17021, + "installed":true, + "interface":"eth-rt3-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":17021, + "installed":true, + "interface":"eth-rt3-1" + }, + { + "type":"SR (IS-IS)", + "outLabel":16021, + "installed":true, + "interface":"eth-rt4" + } + ] + }, + "16030":{ + "inLabel":16030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17030, + "installed":true, + "nexthop":"10.0.5.3" + }, + { + "type":"SR (IS-IS)", + "outLabel":17030, + "installed":true, + "nexthop":"10.0.4.3" + } + ] + }, + "16031":{ + "inLabel":16031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17031, + "installed":true, + "interface":"eth-rt3-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":17031, + "installed":true, + "interface":"eth-rt3-1" + } + ] + }, + "16040":{ + "inLabel":16040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16040, + "installed":true, + "nexthop":"10.0.6.4" + } + ] + }, + "16041":{ + "inLabel":16041, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16041, + "installed":true, + "interface":"eth-rt4" + } + ] + }, + "16050":{ + "inLabel":16050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16051":{ + "inLabel":16051, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16060":{ + "inLabel":16060, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":0, + "installed":true, + "nexthop":"10.0.8.6" + } + ] + }, + "16061":{ + "inLabel":16061, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":2, + "installed":true, + "interface":"eth-rt6" + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt5/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt5/step1/show_yang_interface_isis_adjacencies.ref new file mode 100644 index 0000000000..1ff8c2cd4e --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt5/step1/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1,82 @@ +{ + "frr-interface:lib": { + "interface": [ + { + "name": "eth-rt3-1", + "vrf": "default", + "state": { + "frr-isisd:isis": { + "adjacencies": { + "adjacency": [ + { + "neighbor-sys-type": "level-1", + "neighbor-sysid": "0000.0000.0003", + "hold-timer": 9, + "neighbor-priority": 0, + "state": "up" + } + ] + } + } + } + }, + { + "name": "eth-rt3-2", + "vrf": "default", + "state": { + "frr-isisd:isis": { + "adjacencies": { + "adjacency": [ + { + "neighbor-sys-type": "level-1", + "neighbor-sysid": "0000.0000.0003", + "hold-timer": 9, + "neighbor-priority": 0, + "state": "up" + } + ] + } + } + } + }, + { + "name": "eth-rt4", + "vrf": "default", + "state": { + "frr-isisd:isis": { + "adjacencies": { + "adjacency": [ + { + "neighbor-sys-type": "level-1", + "neighbor-sysid": "0000.0000.0004", + "hold-timer": 9, + "neighbor-priority": 0, + "state": "up" + } + ] + } + } + } + }, + { + "name": "eth-rt6", + "vrf": "default", + "state": { + "frr-isisd:isis": { + "adjacencies": { + "adjacency": [ + { + "neighbor-sys-type": "level-1", + "neighbor-sysid": "0000.0000.0006", + "hold-timer": 9, + "neighbor-priority": 0, + "state": "up" + } + ] + } + } + } + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt5/step10/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt5/step10/show_ip_route.ref new file mode 100644 index 0000000000..a5a0bacaad --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt5/step10/show_ip_route.ref @@ -0,0 +1,312 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17010 + ] + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17010 + ] + } + ] + } + ], + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17020 + ] + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17020 + ] + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17030 + ] + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17030 + ] + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 18040 + ] + } + ] + } + ], + "6.6.6.6\/32":[ + { + "prefix":"6.6.6.6\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 3 + ] + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true + }, + { + "fib":true, + "ip":"10.0.8.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1" + }, + { + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true + }, + { + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2" + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.8.6", + "afi":"ipv4", + "interfaceName":"eth-rt6" + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt5/step10/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt5/step10/show_ipv6_route.ref new file mode 100644 index 0000000000..c02d3cfaea --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt5/step10/show_ipv6_route.ref @@ -0,0 +1,139 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17011 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17011 + ] + } + ] + } + ], + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17021 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17021 + ] + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17031 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17031 + ] + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 18041 + ] + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 3 + ] + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt5/step10/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt5/step10/show_mpls_table.ref new file mode 100644 index 0000000000..e43ef6671d --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt5/step10/show_mpls_table.ref @@ -0,0 +1,180 @@ +{ + "16010":{ + "inLabel":16010, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17010, + "installed":true, + "nexthop":"10.0.5.3" + }, + { + "type":"SR (IS-IS)", + "outLabel":17010, + "installed":true, + "nexthop":"10.0.4.3" + } + ] + }, + "16011":{ + "inLabel":16011, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17011, + "installed":true, + "interface":"eth-rt3-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":17011, + "installed":true, + "interface":"eth-rt3-1" + } + ] + }, + "16020":{ + "inLabel":16020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17020, + "installed":true, + "nexthop":"10.0.5.3" + }, + { + "type":"SR (IS-IS)", + "outLabel":17020, + "installed":true, + "nexthop":"10.0.4.3" + } + ] + }, + "16021":{ + "inLabel":16021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17021, + "installed":true, + "interface":"eth-rt3-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":17021, + "installed":true, + "interface":"eth-rt3-1" + } + ] + }, + "16030":{ + "inLabel":16030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17030, + "installed":true, + "nexthop":"10.0.5.3" + }, + { + "type":"SR (IS-IS)", + "outLabel":17030, + "installed":true, + "nexthop":"10.0.4.3" + } + ] + }, + "16031":{ + "inLabel":16031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17031, + "installed":true, + "interface":"eth-rt3-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":17031, + "installed":true, + "interface":"eth-rt3-1" + } + ] + }, + "16040":{ + "inLabel":16040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":18040, + "installed":true, + "nexthop":"10.0.8.6" + } + ] + }, + "16041":{ + "inLabel":16041, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":18041, + "installed":true, + "interface":"eth-rt6" + } + ] + }, + "16050":{ + "inLabel":16050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16051":{ + "inLabel":16051, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16060":{ + "inLabel":16060, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true, + "nexthop":"10.0.8.6" + } + ] + }, + "16061":{ + "inLabel":16061, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true, + "interface":"eth-rt6" + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt5/step10/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt5/step10/show_yang_interface_isis_adjacencies.ref new file mode 120000 index 0000000000..7cdaabf53f --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt5/step10/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1 @@ +../step4/show_yang_interface_isis_adjacencies.ref \ No newline at end of file diff --git a/tests/topotests/isis-sr-topo1/rt5/step2/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt5/step2/show_ip_route.ref new file mode 100644 index 0000000000..101b811d3b --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt5/step2/show_ip_route.ref @@ -0,0 +1,319 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17010 + ] + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17010 + ] + } + ] + } + ], + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17020 + ] + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17020 + ] + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17030 + ] + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17030 + ] + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 16040 + ] + } + ] + } + ], + "6.6.6.6\/32":[ + { + "prefix":"6.6.6.6\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 0 + ] + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true + }, + { + "fib":true, + "ip":"10.0.8.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true + }, + { + "fib":true, + "ip":"10.0.8.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1" + }, + { + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true + }, + { + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2" + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.8.6", + "afi":"ipv4", + "interfaceName":"eth-rt6" + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt5/step2/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt5/step2/show_ipv6_route.ref new file mode 100644 index 0000000000..fa426317ba --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt5/step2/show_ipv6_route.ref @@ -0,0 +1,139 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17011 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17011 + ] + } + ] + } + ], + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17021 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17021 + ] + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17031 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17031 + ] + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 16041 + ] + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 2 + ] + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt5/step2/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt5/step2/show_mpls_table.ref new file mode 100644 index 0000000000..660e319a50 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt5/step2/show_mpls_table.ref @@ -0,0 +1,180 @@ +{ + "16010":{ + "inLabel":16010, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17010, + "installed":true, + "nexthop":"10.0.5.3" + }, + { + "type":"SR (IS-IS)", + "outLabel":17010, + "installed":true, + "nexthop":"10.0.4.3" + } + ] + }, + "16011":{ + "inLabel":16011, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17011, + "installed":true, + "interface":"eth-rt3-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":17011, + "installed":true, + "interface":"eth-rt3-1" + } + ] + }, + "16020":{ + "inLabel":16020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17020, + "installed":true, + "nexthop":"10.0.5.3" + }, + { + "type":"SR (IS-IS)", + "outLabel":17020, + "installed":true, + "nexthop":"10.0.4.3" + } + ] + }, + "16021":{ + "inLabel":16021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17021, + "installed":true, + "interface":"eth-rt3-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":17021, + "installed":true, + "interface":"eth-rt3-1" + } + ] + }, + "16030":{ + "inLabel":16030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17030, + "installed":true, + "nexthop":"10.0.5.3" + }, + { + "type":"SR (IS-IS)", + "outLabel":17030, + "installed":true, + "nexthop":"10.0.4.3" + } + ] + }, + "16031":{ + "inLabel":16031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17031, + "installed":true, + "interface":"eth-rt3-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":17031, + "installed":true, + "interface":"eth-rt3-1" + } + ] + }, + "16040":{ + "inLabel":16040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16040, + "installed":true, + "nexthop":"10.0.8.6" + } + ] + }, + "16041":{ + "inLabel":16041, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16041, + "installed":true, + "interface":"eth-rt6" + } + ] + }, + "16050":{ + "inLabel":16050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16051":{ + "inLabel":16051, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16060":{ + "inLabel":16060, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":0, + "installed":true, + "nexthop":"10.0.8.6" + } + ] + }, + "16061":{ + "inLabel":16061, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":2, + "installed":true, + "interface":"eth-rt6" + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt5/step2/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt5/step2/show_yang_interface_isis_adjacencies.ref new file mode 100644 index 0000000000..d9ac0a8d00 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt5/step2/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1,63 @@ +{ + "frr-interface:lib": { + "interface": [ + { + "name": "eth-rt3-1", + "vrf": "default", + "state": { + "frr-isisd:isis": { + "adjacencies": { + "adjacency": [ + { + "neighbor-sys-type": "level-1", + "neighbor-sysid": "0000.0000.0003", + "hold-timer": 9, + "neighbor-priority": 0, + "state": "up" + } + ] + } + } + } + }, + { + "name": "eth-rt3-2", + "vrf": "default", + "state": { + "frr-isisd:isis": { + "adjacencies": { + "adjacency": [ + { + "neighbor-sys-type": "level-1", + "neighbor-sysid": "0000.0000.0003", + "hold-timer": 9, + "neighbor-priority": 0, + "state": "up" + } + ] + } + } + } + }, + { + "name": "eth-rt6", + "vrf": "default", + "state": { + "frr-isisd:isis": { + "adjacencies": { + "adjacency": [ + { + "neighbor-sys-type": "level-1", + "neighbor-sysid": "0000.0000.0006", + "hold-timer": 9, + "neighbor-priority": 0, + "state": "up" + } + ] + } + } + } + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt5/step3/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt5/step3/show_ip_route.ref new file mode 100644 index 0000000000..dff6c5f7eb --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt5/step3/show_ip_route.ref @@ -0,0 +1,284 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17010 + ] + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17010 + ] + } + ] + } + ], + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17020 + ] + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17020 + ] + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17030 + ] + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17030 + ] + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":40, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17040 + ] + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17040 + ] + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1" + }, + { + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true + }, + { + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2" + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":40, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt5/step3/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt5/step3/show_ipv6_route.ref new file mode 100644 index 0000000000..a3e705f384 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt5/step3/show_ipv6_route.ref @@ -0,0 +1,126 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17011 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17011 + ] + } + ] + } + ], + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17021 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17021 + ] + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17031 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17031 + ] + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":40, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17041 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17041 + ] + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt5/step3/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt5/step3/show_mpls_table.ref new file mode 100644 index 0000000000..9df3fc9ef6 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt5/step3/show_mpls_table.ref @@ -0,0 +1,168 @@ +{ + "16010":{ + "inLabel":16010, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17010, + "installed":true, + "nexthop":"10.0.5.3" + }, + { + "type":"SR (IS-IS)", + "outLabel":17010, + "installed":true, + "nexthop":"10.0.4.3" + } + ] + }, + "16011":{ + "inLabel":16011, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17011, + "installed":true, + "interface":"eth-rt3-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":17011, + "installed":true, + "interface":"eth-rt3-1" + } + ] + }, + "16020":{ + "inLabel":16020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17020, + "installed":true, + "nexthop":"10.0.5.3" + }, + { + "type":"SR (IS-IS)", + "outLabel":17020, + "installed":true, + "nexthop":"10.0.4.3" + } + ] + }, + "16021":{ + "inLabel":16021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17021, + "installed":true, + "interface":"eth-rt3-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":17021, + "installed":true, + "interface":"eth-rt3-1" + } + ] + }, + "16030":{ + "inLabel":16030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17030, + "installed":true, + "nexthop":"10.0.5.3" + }, + { + "type":"SR (IS-IS)", + "outLabel":17030, + "installed":true, + "nexthop":"10.0.4.3" + } + ] + }, + "16031":{ + "inLabel":16031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17031, + "installed":true, + "interface":"eth-rt3-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":17031, + "installed":true, + "interface":"eth-rt3-1" + } + ] + }, + "16040":{ + "inLabel":16040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17040, + "installed":true, + "nexthop":"10.0.5.3" + }, + { + "type":"SR (IS-IS)", + "outLabel":17040, + "installed":true, + "nexthop":"10.0.4.3" + } + ] + }, + "16041":{ + "inLabel":16041, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17041, + "installed":true, + "interface":"eth-rt3-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":17041, + "installed":true, + "interface":"eth-rt3-1" + } + ] + }, + "16050":{ + "inLabel":16050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16051":{ + "inLabel":16051, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt5/step3/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt5/step3/show_yang_interface_isis_adjacencies.ref new file mode 100644 index 0000000000..0b8e6ba5b9 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt5/step3/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1,44 @@ +{ + "frr-interface:lib": { + "interface": [ + { + "name": "eth-rt3-1", + "vrf": "default", + "state": { + "frr-isisd:isis": { + "adjacencies": { + "adjacency": [ + { + "neighbor-sys-type": "level-1", + "neighbor-sysid": "0000.0000.0003", + "hold-timer": 9, + "neighbor-priority": 0, + "state": "up" + } + ] + } + } + } + }, + { + "name": "eth-rt3-2", + "vrf": "default", + "state": { + "frr-isisd:isis": { + "adjacencies": { + "adjacency": [ + { + "neighbor-sys-type": "level-1", + "neighbor-sysid": "0000.0000.0003", + "hold-timer": 9, + "neighbor-priority": 0, + "state": "up" + } + ] + } + } + } + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt5/step4/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt5/step4/show_ip_route.ref new file mode 100644 index 0000000000..6b29ff2d44 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt5/step4/show_ip_route.ref @@ -0,0 +1,319 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17010 + ] + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17010 + ] + } + ] + } + ], + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17020 + ] + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17020 + ] + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17030 + ] + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17030 + ] + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 18040 + ] + } + ] + } + ], + "6.6.6.6\/32":[ + { + "prefix":"6.6.6.6\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 0 + ] + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true + }, + { + "fib":true, + "ip":"10.0.8.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true + }, + { + "fib":true, + "ip":"10.0.8.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1" + }, + { + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true + }, + { + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2" + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.8.6", + "afi":"ipv4", + "interfaceName":"eth-rt6" + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt5/step4/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt5/step4/show_ipv6_route.ref new file mode 100644 index 0000000000..e9f8fe227c --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt5/step4/show_ipv6_route.ref @@ -0,0 +1,139 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17011 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17011 + ] + } + ] + } + ], + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17021 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17021 + ] + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17031 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17031 + ] + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 18041 + ] + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 2 + ] + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt5/step4/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt5/step4/show_mpls_table.ref new file mode 100644 index 0000000000..4d13108d7d --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt5/step4/show_mpls_table.ref @@ -0,0 +1,180 @@ +{ + "16010":{ + "inLabel":16010, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17010, + "installed":true, + "nexthop":"10.0.5.3" + }, + { + "type":"SR (IS-IS)", + "outLabel":17010, + "installed":true, + "nexthop":"10.0.4.3" + } + ] + }, + "16011":{ + "inLabel":16011, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17011, + "installed":true, + "interface":"eth-rt3-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":17011, + "installed":true, + "interface":"eth-rt3-1" + } + ] + }, + "16020":{ + "inLabel":16020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17020, + "installed":true, + "nexthop":"10.0.5.3" + }, + { + "type":"SR (IS-IS)", + "outLabel":17020, + "installed":true, + "nexthop":"10.0.4.3" + } + ] + }, + "16021":{ + "inLabel":16021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17021, + "installed":true, + "interface":"eth-rt3-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":17021, + "installed":true, + "interface":"eth-rt3-1" + } + ] + }, + "16030":{ + "inLabel":16030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17030, + "installed":true, + "nexthop":"10.0.5.3" + }, + { + "type":"SR (IS-IS)", + "outLabel":17030, + "installed":true, + "nexthop":"10.0.4.3" + } + ] + }, + "16031":{ + "inLabel":16031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17031, + "installed":true, + "interface":"eth-rt3-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":17031, + "installed":true, + "interface":"eth-rt3-1" + } + ] + }, + "16040":{ + "inLabel":16040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":18040, + "installed":true, + "nexthop":"10.0.8.6" + } + ] + }, + "16041":{ + "inLabel":16041, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":18041, + "installed":true, + "interface":"eth-rt6" + } + ] + }, + "16050":{ + "inLabel":16050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16051":{ + "inLabel":16051, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16060":{ + "inLabel":16060, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":0, + "installed":true, + "nexthop":"10.0.8.6" + } + ] + }, + "16061":{ + "inLabel":16061, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":2, + "installed":true, + "interface":"eth-rt6" + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt5/step4/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt5/step4/show_yang_interface_isis_adjacencies.ref new file mode 100644 index 0000000000..d9ac0a8d00 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt5/step4/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1,63 @@ +{ + "frr-interface:lib": { + "interface": [ + { + "name": "eth-rt3-1", + "vrf": "default", + "state": { + "frr-isisd:isis": { + "adjacencies": { + "adjacency": [ + { + "neighbor-sys-type": "level-1", + "neighbor-sysid": "0000.0000.0003", + "hold-timer": 9, + "neighbor-priority": 0, + "state": "up" + } + ] + } + } + } + }, + { + "name": "eth-rt3-2", + "vrf": "default", + "state": { + "frr-isisd:isis": { + "adjacencies": { + "adjacency": [ + { + "neighbor-sys-type": "level-1", + "neighbor-sysid": "0000.0000.0003", + "hold-timer": 9, + "neighbor-priority": 0, + "state": "up" + } + ] + } + } + } + }, + { + "name": "eth-rt6", + "vrf": "default", + "state": { + "frr-isisd:isis": { + "adjacencies": { + "adjacency": [ + { + "neighbor-sys-type": "level-1", + "neighbor-sysid": "0000.0000.0006", + "hold-timer": 9, + "neighbor-priority": 0, + "state": "up" + } + ] + } + } + } + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt5/step5/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt5/step5/show_ip_route.ref new file mode 100644 index 0000000000..cadb674ba3 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt5/step5/show_ip_route.ref @@ -0,0 +1,313 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17010 + ] + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17010 + ] + } + ] + } + ], + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17020 + ] + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17020 + ] + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17030 + ] + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17030 + ] + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "6.6.6.6\/32":[ + { + "prefix":"6.6.6.6\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true + }, + { + "fib":true, + "ip":"10.0.8.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true + }, + { + "fib":true, + "ip":"10.0.8.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1" + }, + { + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true + }, + { + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2" + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.8.6", + "afi":"ipv4", + "interfaceName":"eth-rt6" + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt5/step5/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt5/step5/show_ipv6_route.ref new file mode 100644 index 0000000000..60c645092e --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt5/step5/show_ipv6_route.ref @@ -0,0 +1,133 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17011 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17011 + ] + } + ] + } + ], + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17021 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17021 + ] + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17031 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17031 + ] + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt5/step5/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt5/step5/show_mpls_table.ref new file mode 100644 index 0000000000..c60383093f --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt5/step5/show_mpls_table.ref @@ -0,0 +1,132 @@ +{ + "16010":{ + "inLabel":16010, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17010, + "installed":true, + "nexthop":"10.0.5.3" + }, + { + "type":"SR (IS-IS)", + "outLabel":17010, + "installed":true, + "nexthop":"10.0.4.3" + } + ] + }, + "16011":{ + "inLabel":16011, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17011, + "installed":true, + "interface":"eth-rt3-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":17011, + "installed":true, + "interface":"eth-rt3-1" + } + ] + }, + "16020":{ + "inLabel":16020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17020, + "installed":true, + "nexthop":"10.0.5.3" + }, + { + "type":"SR (IS-IS)", + "outLabel":17020, + "installed":true, + "nexthop":"10.0.4.3" + } + ] + }, + "16021":{ + "inLabel":16021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17021, + "installed":true, + "interface":"eth-rt3-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":17021, + "installed":true, + "interface":"eth-rt3-1" + } + ] + }, + "16030":{ + "inLabel":16030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17030, + "installed":true, + "nexthop":"10.0.5.3" + }, + { + "type":"SR (IS-IS)", + "outLabel":17030, + "installed":true, + "nexthop":"10.0.4.3" + } + ] + }, + "16031":{ + "inLabel":16031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17031, + "installed":true, + "interface":"eth-rt3-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":17031, + "installed":true, + "interface":"eth-rt3-1" + } + ] + }, + "16050":{ + "inLabel":16050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16051":{ + "inLabel":16051, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt5/step5/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt5/step5/show_yang_interface_isis_adjacencies.ref new file mode 120000 index 0000000000..7cdaabf53f --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt5/step5/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1 @@ +../step4/show_yang_interface_isis_adjacencies.ref \ No newline at end of file diff --git a/tests/topotests/isis-sr-topo1/rt5/step6/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt5/step6/show_ip_route.ref new file mode 100644 index 0000000000..6b29ff2d44 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt5/step6/show_ip_route.ref @@ -0,0 +1,319 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17010 + ] + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17010 + ] + } + ] + } + ], + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17020 + ] + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17020 + ] + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17030 + ] + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17030 + ] + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 18040 + ] + } + ] + } + ], + "6.6.6.6\/32":[ + { + "prefix":"6.6.6.6\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 0 + ] + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true + }, + { + "fib":true, + "ip":"10.0.8.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true + }, + { + "fib":true, + "ip":"10.0.8.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1" + }, + { + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true + }, + { + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2" + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.8.6", + "afi":"ipv4", + "interfaceName":"eth-rt6" + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt5/step6/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt5/step6/show_ipv6_route.ref new file mode 100644 index 0000000000..e9f8fe227c --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt5/step6/show_ipv6_route.ref @@ -0,0 +1,139 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17011 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17011 + ] + } + ] + } + ], + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17021 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17021 + ] + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17031 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17031 + ] + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 18041 + ] + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 2 + ] + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt5/step6/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt5/step6/show_mpls_table.ref new file mode 100644 index 0000000000..4d13108d7d --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt5/step6/show_mpls_table.ref @@ -0,0 +1,180 @@ +{ + "16010":{ + "inLabel":16010, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17010, + "installed":true, + "nexthop":"10.0.5.3" + }, + { + "type":"SR (IS-IS)", + "outLabel":17010, + "installed":true, + "nexthop":"10.0.4.3" + } + ] + }, + "16011":{ + "inLabel":16011, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17011, + "installed":true, + "interface":"eth-rt3-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":17011, + "installed":true, + "interface":"eth-rt3-1" + } + ] + }, + "16020":{ + "inLabel":16020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17020, + "installed":true, + "nexthop":"10.0.5.3" + }, + { + "type":"SR (IS-IS)", + "outLabel":17020, + "installed":true, + "nexthop":"10.0.4.3" + } + ] + }, + "16021":{ + "inLabel":16021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17021, + "installed":true, + "interface":"eth-rt3-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":17021, + "installed":true, + "interface":"eth-rt3-1" + } + ] + }, + "16030":{ + "inLabel":16030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17030, + "installed":true, + "nexthop":"10.0.5.3" + }, + { + "type":"SR (IS-IS)", + "outLabel":17030, + "installed":true, + "nexthop":"10.0.4.3" + } + ] + }, + "16031":{ + "inLabel":16031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17031, + "installed":true, + "interface":"eth-rt3-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":17031, + "installed":true, + "interface":"eth-rt3-1" + } + ] + }, + "16040":{ + "inLabel":16040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":18040, + "installed":true, + "nexthop":"10.0.8.6" + } + ] + }, + "16041":{ + "inLabel":16041, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":18041, + "installed":true, + "interface":"eth-rt6" + } + ] + }, + "16050":{ + "inLabel":16050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16051":{ + "inLabel":16051, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16060":{ + "inLabel":16060, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":0, + "installed":true, + "nexthop":"10.0.8.6" + } + ] + }, + "16061":{ + "inLabel":16061, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":2, + "installed":true, + "interface":"eth-rt6" + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt5/step6/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt5/step6/show_yang_interface_isis_adjacencies.ref new file mode 120000 index 0000000000..7cdaabf53f --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt5/step6/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1 @@ +../step4/show_yang_interface_isis_adjacencies.ref \ No newline at end of file diff --git a/tests/topotests/isis-sr-topo1/rt5/step7/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt5/step7/show_ip_route.ref new file mode 100644 index 0000000000..72b89ccf69 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt5/step7/show_ip_route.ref @@ -0,0 +1,313 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true + } + ] + } + ], + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17020 + ] + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17020 + ] + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17030 + ] + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17030 + ] + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 18040 + ] + } + ] + } + ], + "6.6.6.6\/32":[ + { + "prefix":"6.6.6.6\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 0 + ] + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true + }, + { + "fib":true, + "ip":"10.0.8.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true + }, + { + "fib":true, + "ip":"10.0.8.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1" + }, + { + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true + }, + { + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2" + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.8.6", + "afi":"ipv4", + "interfaceName":"eth-rt6" + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt5/step7/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt5/step7/show_ipv6_route.ref new file mode 100644 index 0000000000..e05cc1e282 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt5/step7/show_ipv6_route.ref @@ -0,0 +1,133 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-2", + "active":true + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-1", + "active":true + } + ] + } + ], + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17021 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17021 + ] + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17031 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17031 + ] + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 18041 + ] + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 2 + ] + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt5/step7/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt5/step7/show_mpls_table.ref new file mode 100644 index 0000000000..2b1e67ea71 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt5/step7/show_mpls_table.ref @@ -0,0 +1,144 @@ +{ + "16020":{ + "inLabel":16020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17020, + "installed":true, + "nexthop":"10.0.5.3" + }, + { + "type":"SR (IS-IS)", + "outLabel":17020, + "installed":true, + "nexthop":"10.0.4.3" + } + ] + }, + "16021":{ + "inLabel":16021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17021, + "installed":true, + "interface":"eth-rt3-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":17021, + "installed":true, + "interface":"eth-rt3-1" + } + ] + }, + "16030":{ + "inLabel":16030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17030, + "installed":true, + "nexthop":"10.0.5.3" + }, + { + "type":"SR (IS-IS)", + "outLabel":17030, + "installed":true, + "nexthop":"10.0.4.3" + } + ] + }, + "16031":{ + "inLabel":16031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17031, + "installed":true, + "interface":"eth-rt3-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":17031, + "installed":true, + "interface":"eth-rt3-1" + } + ] + }, + "16040":{ + "inLabel":16040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":18040, + "installed":true, + "nexthop":"10.0.8.6" + } + ] + }, + "16041":{ + "inLabel":16041, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":18041, + "installed":true, + "interface":"eth-rt6" + } + ] + }, + "16050":{ + "inLabel":16050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16051":{ + "inLabel":16051, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16060":{ + "inLabel":16060, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":0, + "installed":true, + "nexthop":"10.0.8.6" + } + ] + }, + "16061":{ + "inLabel":16061, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":2, + "installed":true, + "interface":"eth-rt6" + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt5/step7/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt5/step7/show_yang_interface_isis_adjacencies.ref new file mode 120000 index 0000000000..7cdaabf53f --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt5/step7/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1 @@ +../step4/show_yang_interface_isis_adjacencies.ref \ No newline at end of file diff --git a/tests/topotests/isis-sr-topo1/rt5/step8/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt5/step8/show_ip_route.ref new file mode 100644 index 0000000000..6b29ff2d44 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt5/step8/show_ip_route.ref @@ -0,0 +1,319 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17010 + ] + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17010 + ] + } + ] + } + ], + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17020 + ] + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17020 + ] + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17030 + ] + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17030 + ] + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 18040 + ] + } + ] + } + ], + "6.6.6.6\/32":[ + { + "prefix":"6.6.6.6\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 0 + ] + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true + }, + { + "fib":true, + "ip":"10.0.8.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true + }, + { + "fib":true, + "ip":"10.0.8.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1" + }, + { + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true + }, + { + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2" + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.8.6", + "afi":"ipv4", + "interfaceName":"eth-rt6" + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt5/step8/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt5/step8/show_ipv6_route.ref new file mode 100644 index 0000000000..e9f8fe227c --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt5/step8/show_ipv6_route.ref @@ -0,0 +1,139 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17011 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17011 + ] + } + ] + } + ], + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17021 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17021 + ] + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17031 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17031 + ] + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 18041 + ] + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 2 + ] + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt5/step8/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt5/step8/show_mpls_table.ref new file mode 100644 index 0000000000..4d13108d7d --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt5/step8/show_mpls_table.ref @@ -0,0 +1,180 @@ +{ + "16010":{ + "inLabel":16010, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17010, + "installed":true, + "nexthop":"10.0.5.3" + }, + { + "type":"SR (IS-IS)", + "outLabel":17010, + "installed":true, + "nexthop":"10.0.4.3" + } + ] + }, + "16011":{ + "inLabel":16011, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17011, + "installed":true, + "interface":"eth-rt3-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":17011, + "installed":true, + "interface":"eth-rt3-1" + } + ] + }, + "16020":{ + "inLabel":16020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17020, + "installed":true, + "nexthop":"10.0.5.3" + }, + { + "type":"SR (IS-IS)", + "outLabel":17020, + "installed":true, + "nexthop":"10.0.4.3" + } + ] + }, + "16021":{ + "inLabel":16021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17021, + "installed":true, + "interface":"eth-rt3-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":17021, + "installed":true, + "interface":"eth-rt3-1" + } + ] + }, + "16030":{ + "inLabel":16030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17030, + "installed":true, + "nexthop":"10.0.5.3" + }, + { + "type":"SR (IS-IS)", + "outLabel":17030, + "installed":true, + "nexthop":"10.0.4.3" + } + ] + }, + "16031":{ + "inLabel":16031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17031, + "installed":true, + "interface":"eth-rt3-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":17031, + "installed":true, + "interface":"eth-rt3-1" + } + ] + }, + "16040":{ + "inLabel":16040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":18040, + "installed":true, + "nexthop":"10.0.8.6" + } + ] + }, + "16041":{ + "inLabel":16041, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":18041, + "installed":true, + "interface":"eth-rt6" + } + ] + }, + "16050":{ + "inLabel":16050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16051":{ + "inLabel":16051, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16060":{ + "inLabel":16060, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":0, + "installed":true, + "nexthop":"10.0.8.6" + } + ] + }, + "16061":{ + "inLabel":16061, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":2, + "installed":true, + "interface":"eth-rt6" + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt5/step8/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt5/step8/show_yang_interface_isis_adjacencies.ref new file mode 120000 index 0000000000..7cdaabf53f --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt5/step8/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1 @@ +../step4/show_yang_interface_isis_adjacencies.ref \ No newline at end of file diff --git a/tests/topotests/isis-sr-topo1/rt5/step9/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt5/step9/show_ip_route.ref new file mode 100644 index 0000000000..cc37894d67 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt5/step9/show_ip_route.ref @@ -0,0 +1,319 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17010 + ] + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17010 + ] + } + ] + } + ], + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17020 + ] + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17020 + ] + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17030 + ] + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17030 + ] + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 18040 + ] + } + ] + } + ], + "6.6.6.6\/32":[ + { + "prefix":"6.6.6.6\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 3 + ] + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true + }, + { + "fib":true, + "ip":"10.0.8.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true + }, + { + "fib":true, + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true + }, + { + "fib":true, + "ip":"10.0.8.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1" + }, + { + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.4.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-1", + "active":true + }, + { + "ip":"10.0.5.3", + "afi":"ipv4", + "interfaceName":"eth-rt3-2" + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.6", + "afi":"ipv4", + "interfaceName":"eth-rt6", + "active":true + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.8.6", + "afi":"ipv4", + "interfaceName":"eth-rt6" + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt5/step9/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt5/step9/show_ipv6_route.ref new file mode 100644 index 0000000000..c02d3cfaea --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt5/step9/show_ipv6_route.ref @@ -0,0 +1,139 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17011 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17011 + ] + } + ] + } + ], + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17021 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17021 + ] + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-2", + "active":true, + "labels":[ + 17031 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt3-1", + "active":true, + "labels":[ + 17031 + ] + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 18041 + ] + } + ] + } + ], + "2001:db8:1000::6\/128":[ + { + "prefix":"2001:db8:1000::6\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt6", + "active":true, + "labels":[ + 3 + ] + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt5/step9/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt5/step9/show_mpls_table.ref new file mode 100644 index 0000000000..e43ef6671d --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt5/step9/show_mpls_table.ref @@ -0,0 +1,180 @@ +{ + "16010":{ + "inLabel":16010, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17010, + "installed":true, + "nexthop":"10.0.5.3" + }, + { + "type":"SR (IS-IS)", + "outLabel":17010, + "installed":true, + "nexthop":"10.0.4.3" + } + ] + }, + "16011":{ + "inLabel":16011, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17011, + "installed":true, + "interface":"eth-rt3-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":17011, + "installed":true, + "interface":"eth-rt3-1" + } + ] + }, + "16020":{ + "inLabel":16020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17020, + "installed":true, + "nexthop":"10.0.5.3" + }, + { + "type":"SR (IS-IS)", + "outLabel":17020, + "installed":true, + "nexthop":"10.0.4.3" + } + ] + }, + "16021":{ + "inLabel":16021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17021, + "installed":true, + "interface":"eth-rt3-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":17021, + "installed":true, + "interface":"eth-rt3-1" + } + ] + }, + "16030":{ + "inLabel":16030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17030, + "installed":true, + "nexthop":"10.0.5.3" + }, + { + "type":"SR (IS-IS)", + "outLabel":17030, + "installed":true, + "nexthop":"10.0.4.3" + } + ] + }, + "16031":{ + "inLabel":16031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":17031, + "installed":true, + "interface":"eth-rt3-2" + }, + { + "type":"SR (IS-IS)", + "outLabel":17031, + "installed":true, + "interface":"eth-rt3-1" + } + ] + }, + "16040":{ + "inLabel":16040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":18040, + "installed":true, + "nexthop":"10.0.8.6" + } + ] + }, + "16041":{ + "inLabel":16041, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":18041, + "installed":true, + "interface":"eth-rt6" + } + ] + }, + "16050":{ + "inLabel":16050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16051":{ + "inLabel":16051, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true + } + ] + }, + "16060":{ + "inLabel":16060, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true, + "nexthop":"10.0.8.6" + } + ] + }, + "16061":{ + "inLabel":16061, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "installed":true, + "interface":"eth-rt6" + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt5/step9/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt5/step9/show_yang_interface_isis_adjacencies.ref new file mode 120000 index 0000000000..7cdaabf53f --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt5/step9/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1 @@ +../step4/show_yang_interface_isis_adjacencies.ref \ No newline at end of file diff --git a/tests/topotests/isis-sr-topo1/rt5/zebra.conf b/tests/topotests/isis-sr-topo1/rt5/zebra.conf new file mode 100644 index 0000000000..4cfea1a59f --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt5/zebra.conf @@ -0,0 +1,28 @@ +log file zebra.log +! +hostname rt5 +! +debug zebra kernel +debug zebra packet +debug zebra mpls +! +interface lo + ip address 5.5.5.5/32 + ipv6 address 2001:db8:1000::5/128 +! +interface eth-rt3-1 + ip address 10.0.4.5/24 +! +interface eth-rt3-2 + ip address 10.0.5.5/24 +! +interface eth-rt4 + ip address 10.0.6.5/24 +! +interface eth-rt6 + ip address 10.0.8.5/24 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/isis-sr-topo1/rt6/isisd.conf b/tests/topotests/isis-sr-topo1/rt6/isisd.conf new file mode 100644 index 0000000000..3b85dbae4e --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt6/isisd.conf @@ -0,0 +1,37 @@ +hostname rt6 +log file isisd.log +! +debug isis events +debug isis route-events +debug isis spf-events +debug isis sr-events +debug isis lsp-gen +! +interface lo + ip router isis 1 + ipv6 router isis 1 + isis passive +! +interface eth-rt4 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 +! +interface eth-rt5 + ip router isis 1 + ipv6 router isis 1 + isis network point-to-point + isis hello-multiplier 3 +! +router isis 1 + net 49.0000.0000.0000.0006.00 + is-type level-1 + lsp-gen-interval 2 + topology ipv6-unicast + segment-routing on + segment-routing global-block 16000 23999 + segment-routing node-msd 8 + segment-routing prefix 6.6.6.6/32 index 60 explicit-null + segment-routing prefix 2001:db8:1000::6/128 index 61 explicit-null +! diff --git a/tests/topotests/isis-sr-topo1/rt6/step1/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt6/step1/show_ip_route.ref new file mode 100644 index 0000000000..324b71f7b8 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt6/step1/show_ip_route.ref @@ -0,0 +1,291 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":40, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true, + "labels":[ + 16010 + ] + }, + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true, + "labels":[ + 16010 + ] + } + ] + } + ], + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true, + "labels":[ + 16020 + ] + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true, + "labels":[ + 16030 + ] + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true, + "labels":[ + 16040 + ] + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true, + "labels":[ + 16050 + ] + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true + }, + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true + } + ] + } + ], + "10.0.6.0\/24":[ + { + "prefix":"10.0.6.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true + }, + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4" + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5" + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt6/step1/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt6/step1/show_ipv6_route.ref new file mode 100644 index 0000000000..eee9dea4d3 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt6/step1/show_ipv6_route.ref @@ -0,0 +1,121 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":40, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5", + "active":true, + "labels":[ + 16011 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4", + "active":true, + "labels":[ + 16011 + ] + } + ] + } + ], + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4", + "active":true, + "labels":[ + 16021 + ] + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5", + "active":true, + "labels":[ + 16031 + ] + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4", + "active":true, + "labels":[ + 16041 + ] + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5", + "active":true, + "labels":[ + 16051 + ] + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt6/step1/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt6/step1/show_mpls_table.ref new file mode 100644 index 0000000000..970251fe8a --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt6/step1/show_mpls_table.ref @@ -0,0 +1,134 @@ +{ + "16010":{ + "inLabel":16010, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16010, + "installed":true, + "nexthop":"10.0.7.4" + }, + { + "type":"SR (IS-IS)", + "outLabel":16010, + "installed":true, + "nexthop":"10.0.8.5" + } + ] + }, + "16011":{ + "inLabel":16011, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16011, + "installed":true, + "interface":"eth-rt4" + }, + { + "type":"SR (IS-IS)", + "outLabel":16011, + "installed":true, + "interface":"eth-rt5" + } + ] + }, + "16020":{ + "inLabel":16020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16020, + "installed":true, + "nexthop":"10.0.7.4" + } + ] + }, + "16021":{ + "inLabel":16021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16021, + "installed":true, + "interface":"eth-rt4" + } + ] + }, + "16030":{ + "inLabel":16030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16030, + "installed":true, + "nexthop":"10.0.8.5" + } + ] + }, + "16031":{ + "inLabel":16031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16031, + "installed":true, + "interface":"eth-rt5" + } + ] + }, + "16040":{ + "inLabel":16040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16040, + "installed":true, + "nexthop":"10.0.7.4" + } + ] + }, + "16041":{ + "inLabel":16041, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16041, + "installed":true, + "interface":"eth-rt4" + } + ] + }, + "16050":{ + "inLabel":16050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16050, + "installed":true, + "nexthop":"10.0.8.5" + } + ] + }, + "16051":{ + "inLabel":16051, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16051, + "installed":true, + "interface":"eth-rt5" + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt6/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt6/step1/show_yang_interface_isis_adjacencies.ref new file mode 100644 index 0000000000..734832358f --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt6/step1/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1,44 @@ +{ + "frr-interface:lib": { + "interface": [ + { + "name": "eth-rt4", + "vrf": "default", + "state": { + "frr-isisd:isis": { + "adjacencies": { + "adjacency": [ + { + "neighbor-sys-type": "level-1", + "neighbor-sysid": "0000.0000.0004", + "hold-timer": 9, + "neighbor-priority": 0, + "state": "up" + } + ] + } + } + } + }, + { + "name": "eth-rt5", + "vrf": "default", + "state": { + "frr-isisd:isis": { + "adjacencies": { + "adjacency": [ + { + "neighbor-sys-type": "level-1", + "neighbor-sysid": "0000.0000.0005", + "hold-timer": 9, + "neighbor-priority": 0, + "state": "up" + } + ] + } + } + } + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt6/step10/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt6/step10/show_ip_route.ref new file mode 100644 index 0000000000..9d0c331ff2 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt6/step10/show_ip_route.ref @@ -0,0 +1,284 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":40, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true, + "labels":[ + 16010 + ] + }, + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true, + "labels":[ + 16010 + ] + } + ] + } + ], + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true, + "labels":[ + 16020 + ] + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true, + "labels":[ + 16030 + ] + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true, + "labels":[ + 16040 + ] + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true, + "labels":[ + 16050 + ] + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true + }, + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true + } + ] + } + ], + "10.0.6.0\/24":[ + { + "prefix":"10.0.6.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4" + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5" + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt6/step10/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt6/step10/show_ipv6_route.ref new file mode 100644 index 0000000000..eee9dea4d3 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt6/step10/show_ipv6_route.ref @@ -0,0 +1,121 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":40, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5", + "active":true, + "labels":[ + 16011 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4", + "active":true, + "labels":[ + 16011 + ] + } + ] + } + ], + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4", + "active":true, + "labels":[ + 16021 + ] + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5", + "active":true, + "labels":[ + 16031 + ] + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4", + "active":true, + "labels":[ + 16041 + ] + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5", + "active":true, + "labels":[ + 16051 + ] + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt6/step10/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt6/step10/show_mpls_table.ref new file mode 100644 index 0000000000..a79406b300 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt6/step10/show_mpls_table.ref @@ -0,0 +1,134 @@ +{ + "18010":{ + "inLabel":18010, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16010, + "installed":true, + "nexthop":"10.0.8.5" + }, + { + "type":"SR (IS-IS)", + "outLabel":16010, + "installed":true, + "nexthop":"10.0.7.4" + } + ] + }, + "18011":{ + "inLabel":18011, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16011, + "installed":true, + "interface":"eth-rt5" + }, + { + "type":"SR (IS-IS)", + "outLabel":16011, + "installed":true, + "interface":"eth-rt4" + } + ] + }, + "18020":{ + "inLabel":18020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16020, + "installed":true, + "nexthop":"10.0.7.4" + } + ] + }, + "18021":{ + "inLabel":18021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16021, + "installed":true, + "interface":"eth-rt4" + } + ] + }, + "18030":{ + "inLabel":18030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16030, + "installed":true, + "nexthop":"10.0.8.5" + } + ] + }, + "18031":{ + "inLabel":18031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16031, + "installed":true, + "interface":"eth-rt5" + } + ] + }, + "18040":{ + "inLabel":18040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16040, + "installed":true, + "nexthop":"10.0.7.4" + } + ] + }, + "18041":{ + "inLabel":18041, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16041, + "installed":true, + "interface":"eth-rt4" + } + ] + }, + "18050":{ + "inLabel":18050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16050, + "installed":true, + "nexthop":"10.0.8.5" + } + ] + }, + "18051":{ + "inLabel":18051, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16051, + "installed":true, + "interface":"eth-rt5" + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt6/step10/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt6/step10/show_yang_interface_isis_adjacencies.ref new file mode 120000 index 0000000000..0879b84d23 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt6/step10/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1 @@ +../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file diff --git a/tests/topotests/isis-sr-topo1/rt6/step2/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt6/step2/show_ip_route.ref new file mode 100644 index 0000000000..e4df0d846e --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt6/step2/show_ip_route.ref @@ -0,0 +1,284 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":40, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true, + "labels":[ + 16010 + ] + }, + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true, + "labels":[ + 16010 + ] + } + ] + } + ], + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true, + "labels":[ + 16020 + ] + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true, + "labels":[ + 16030 + ] + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true, + "labels":[ + 16040 + ] + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true, + "labels":[ + 16050 + ] + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true + }, + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true + } + ] + } + ], + "10.0.6.0\/24":[ + { + "prefix":"10.0.6.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4" + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5" + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt6/step2/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt6/step2/show_ipv6_route.ref new file mode 100644 index 0000000000..eee9dea4d3 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt6/step2/show_ipv6_route.ref @@ -0,0 +1,121 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":40, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5", + "active":true, + "labels":[ + 16011 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4", + "active":true, + "labels":[ + 16011 + ] + } + ] + } + ], + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4", + "active":true, + "labels":[ + 16021 + ] + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5", + "active":true, + "labels":[ + 16031 + ] + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4", + "active":true, + "labels":[ + 16041 + ] + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5", + "active":true, + "labels":[ + 16051 + ] + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt6/step2/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt6/step2/show_mpls_table.ref new file mode 100644 index 0000000000..970251fe8a --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt6/step2/show_mpls_table.ref @@ -0,0 +1,134 @@ +{ + "16010":{ + "inLabel":16010, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16010, + "installed":true, + "nexthop":"10.0.7.4" + }, + { + "type":"SR (IS-IS)", + "outLabel":16010, + "installed":true, + "nexthop":"10.0.8.5" + } + ] + }, + "16011":{ + "inLabel":16011, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16011, + "installed":true, + "interface":"eth-rt4" + }, + { + "type":"SR (IS-IS)", + "outLabel":16011, + "installed":true, + "interface":"eth-rt5" + } + ] + }, + "16020":{ + "inLabel":16020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16020, + "installed":true, + "nexthop":"10.0.7.4" + } + ] + }, + "16021":{ + "inLabel":16021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16021, + "installed":true, + "interface":"eth-rt4" + } + ] + }, + "16030":{ + "inLabel":16030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16030, + "installed":true, + "nexthop":"10.0.8.5" + } + ] + }, + "16031":{ + "inLabel":16031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16031, + "installed":true, + "interface":"eth-rt5" + } + ] + }, + "16040":{ + "inLabel":16040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16040, + "installed":true, + "nexthop":"10.0.7.4" + } + ] + }, + "16041":{ + "inLabel":16041, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16041, + "installed":true, + "interface":"eth-rt4" + } + ] + }, + "16050":{ + "inLabel":16050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16050, + "installed":true, + "nexthop":"10.0.8.5" + } + ] + }, + "16051":{ + "inLabel":16051, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16051, + "installed":true, + "interface":"eth-rt5" + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt6/step2/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt6/step2/show_yang_interface_isis_adjacencies.ref new file mode 120000 index 0000000000..0879b84d23 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt6/step2/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1 @@ +../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file diff --git a/tests/topotests/isis-sr-topo1/rt6/step3/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt6/step3/show_ip_route.ref new file mode 100644 index 0000000000..2c63c08510 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt6/step3/show_ip_route.ref @@ -0,0 +1,2 @@ +{ +} diff --git a/tests/topotests/isis-sr-topo1/rt6/step3/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt6/step3/show_ipv6_route.ref new file mode 100644 index 0000000000..2c63c08510 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt6/step3/show_ipv6_route.ref @@ -0,0 +1,2 @@ +{ +} diff --git a/tests/topotests/isis-sr-topo1/rt6/step3/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt6/step3/show_mpls_table.ref new file mode 100644 index 0000000000..2c63c08510 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt6/step3/show_mpls_table.ref @@ -0,0 +1,2 @@ +{ +} diff --git a/tests/topotests/isis-sr-topo1/rt6/step3/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt6/step3/show_yang_interface_isis_adjacencies.ref new file mode 100644 index 0000000000..0db3279e44 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt6/step3/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1,3 @@ +{ + +} diff --git a/tests/topotests/isis-sr-topo1/rt6/step4/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt6/step4/show_ip_route.ref new file mode 100644 index 0000000000..e4df0d846e --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt6/step4/show_ip_route.ref @@ -0,0 +1,284 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":40, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true, + "labels":[ + 16010 + ] + }, + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true, + "labels":[ + 16010 + ] + } + ] + } + ], + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true, + "labels":[ + 16020 + ] + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true, + "labels":[ + 16030 + ] + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true, + "labels":[ + 16040 + ] + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true, + "labels":[ + 16050 + ] + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true + }, + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true + } + ] + } + ], + "10.0.6.0\/24":[ + { + "prefix":"10.0.6.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4" + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5" + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt6/step4/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt6/step4/show_ipv6_route.ref new file mode 100644 index 0000000000..eee9dea4d3 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt6/step4/show_ipv6_route.ref @@ -0,0 +1,121 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":40, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5", + "active":true, + "labels":[ + 16011 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4", + "active":true, + "labels":[ + 16011 + ] + } + ] + } + ], + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4", + "active":true, + "labels":[ + 16021 + ] + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5", + "active":true, + "labels":[ + 16031 + ] + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4", + "active":true, + "labels":[ + 16041 + ] + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5", + "active":true, + "labels":[ + 16051 + ] + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt6/step4/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt6/step4/show_mpls_table.ref new file mode 100644 index 0000000000..a79406b300 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt6/step4/show_mpls_table.ref @@ -0,0 +1,134 @@ +{ + "18010":{ + "inLabel":18010, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16010, + "installed":true, + "nexthop":"10.0.8.5" + }, + { + "type":"SR (IS-IS)", + "outLabel":16010, + "installed":true, + "nexthop":"10.0.7.4" + } + ] + }, + "18011":{ + "inLabel":18011, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16011, + "installed":true, + "interface":"eth-rt5" + }, + { + "type":"SR (IS-IS)", + "outLabel":16011, + "installed":true, + "interface":"eth-rt4" + } + ] + }, + "18020":{ + "inLabel":18020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16020, + "installed":true, + "nexthop":"10.0.7.4" + } + ] + }, + "18021":{ + "inLabel":18021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16021, + "installed":true, + "interface":"eth-rt4" + } + ] + }, + "18030":{ + "inLabel":18030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16030, + "installed":true, + "nexthop":"10.0.8.5" + } + ] + }, + "18031":{ + "inLabel":18031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16031, + "installed":true, + "interface":"eth-rt5" + } + ] + }, + "18040":{ + "inLabel":18040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16040, + "installed":true, + "nexthop":"10.0.7.4" + } + ] + }, + "18041":{ + "inLabel":18041, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16041, + "installed":true, + "interface":"eth-rt4" + } + ] + }, + "18050":{ + "inLabel":18050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16050, + "installed":true, + "nexthop":"10.0.8.5" + } + ] + }, + "18051":{ + "inLabel":18051, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16051, + "installed":true, + "interface":"eth-rt5" + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt6/step4/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt6/step4/show_yang_interface_isis_adjacencies.ref new file mode 120000 index 0000000000..0879b84d23 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt6/step4/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1 @@ +../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file diff --git a/tests/topotests/isis-sr-topo1/rt6/step5/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt6/step5/show_ip_route.ref new file mode 100644 index 0000000000..c200a9f476 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt6/step5/show_ip_route.ref @@ -0,0 +1,266 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":40, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true + }, + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true + } + ] + } + ], + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true + }, + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true + } + ] + } + ], + "10.0.6.0\/24":[ + { + "prefix":"10.0.6.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4" + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5" + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt6/step5/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt6/step5/show_ipv6_route.ref new file mode 100644 index 0000000000..2bf4b70be8 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt6/step5/show_ipv6_route.ref @@ -0,0 +1,103 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":40, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5", + "active":true + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4", + "active":true + } + ] + } + ], + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4", + "active":true + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5", + "active":true + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4", + "active":true + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt6/step5/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt6/step5/show_mpls_table.ref new file mode 100644 index 0000000000..2c63c08510 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt6/step5/show_mpls_table.ref @@ -0,0 +1,2 @@ +{ +} diff --git a/tests/topotests/isis-sr-topo1/rt6/step5/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt6/step5/show_yang_interface_isis_adjacencies.ref new file mode 120000 index 0000000000..0879b84d23 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt6/step5/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1 @@ +../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file diff --git a/tests/topotests/isis-sr-topo1/rt6/step6/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt6/step6/show_ip_route.ref new file mode 100644 index 0000000000..e4df0d846e --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt6/step6/show_ip_route.ref @@ -0,0 +1,284 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":40, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true, + "labels":[ + 16010 + ] + }, + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true, + "labels":[ + 16010 + ] + } + ] + } + ], + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true, + "labels":[ + 16020 + ] + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true, + "labels":[ + 16030 + ] + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true, + "labels":[ + 16040 + ] + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true, + "labels":[ + 16050 + ] + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true + }, + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true + } + ] + } + ], + "10.0.6.0\/24":[ + { + "prefix":"10.0.6.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4" + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5" + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt6/step6/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt6/step6/show_ipv6_route.ref new file mode 100644 index 0000000000..eee9dea4d3 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt6/step6/show_ipv6_route.ref @@ -0,0 +1,121 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":40, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5", + "active":true, + "labels":[ + 16011 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4", + "active":true, + "labels":[ + 16011 + ] + } + ] + } + ], + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4", + "active":true, + "labels":[ + 16021 + ] + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5", + "active":true, + "labels":[ + 16031 + ] + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4", + "active":true, + "labels":[ + 16041 + ] + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5", + "active":true, + "labels":[ + 16051 + ] + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt6/step6/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt6/step6/show_mpls_table.ref new file mode 100644 index 0000000000..a79406b300 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt6/step6/show_mpls_table.ref @@ -0,0 +1,134 @@ +{ + "18010":{ + "inLabel":18010, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16010, + "installed":true, + "nexthop":"10.0.8.5" + }, + { + "type":"SR (IS-IS)", + "outLabel":16010, + "installed":true, + "nexthop":"10.0.7.4" + } + ] + }, + "18011":{ + "inLabel":18011, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16011, + "installed":true, + "interface":"eth-rt5" + }, + { + "type":"SR (IS-IS)", + "outLabel":16011, + "installed":true, + "interface":"eth-rt4" + } + ] + }, + "18020":{ + "inLabel":18020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16020, + "installed":true, + "nexthop":"10.0.7.4" + } + ] + }, + "18021":{ + "inLabel":18021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16021, + "installed":true, + "interface":"eth-rt4" + } + ] + }, + "18030":{ + "inLabel":18030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16030, + "installed":true, + "nexthop":"10.0.8.5" + } + ] + }, + "18031":{ + "inLabel":18031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16031, + "installed":true, + "interface":"eth-rt5" + } + ] + }, + "18040":{ + "inLabel":18040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16040, + "installed":true, + "nexthop":"10.0.7.4" + } + ] + }, + "18041":{ + "inLabel":18041, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16041, + "installed":true, + "interface":"eth-rt4" + } + ] + }, + "18050":{ + "inLabel":18050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16050, + "installed":true, + "nexthop":"10.0.8.5" + } + ] + }, + "18051":{ + "inLabel":18051, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16051, + "installed":true, + "interface":"eth-rt5" + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt6/step6/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt6/step6/show_yang_interface_isis_adjacencies.ref new file mode 120000 index 0000000000..0879b84d23 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt6/step6/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1 @@ +../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file diff --git a/tests/topotests/isis-sr-topo1/rt6/step7/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt6/step7/show_ip_route.ref new file mode 100644 index 0000000000..b21e5db928 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt6/step7/show_ip_route.ref @@ -0,0 +1,278 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":40, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true + }, + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true + } + ] + } + ], + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true, + "labels":[ + 16020 + ] + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true, + "labels":[ + 16030 + ] + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true, + "labels":[ + 16040 + ] + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true, + "labels":[ + 16050 + ] + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true + }, + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true + } + ] + } + ], + "10.0.6.0\/24":[ + { + "prefix":"10.0.6.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4" + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5" + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt6/step7/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt6/step7/show_ipv6_route.ref new file mode 100644 index 0000000000..dfbb1954b8 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt6/step7/show_ipv6_route.ref @@ -0,0 +1,115 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":40, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5", + "active":true + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4", + "active":true + } + ] + } + ], + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4", + "active":true, + "labels":[ + 16021 + ] + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5", + "active":true, + "labels":[ + 16031 + ] + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4", + "active":true, + "labels":[ + 16041 + ] + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5", + "active":true, + "labels":[ + 16051 + ] + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt6/step7/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt6/step7/show_mpls_table.ref new file mode 100644 index 0000000000..43d771bcbd --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt6/step7/show_mpls_table.ref @@ -0,0 +1,98 @@ +{ + "18020":{ + "inLabel":18020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16020, + "installed":true, + "nexthop":"10.0.7.4" + } + ] + }, + "18021":{ + "inLabel":18021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16021, + "installed":true, + "interface":"eth-rt4" + } + ] + }, + "18030":{ + "inLabel":18030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16030, + "installed":true, + "nexthop":"10.0.8.5" + } + ] + }, + "18031":{ + "inLabel":18031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16031, + "installed":true, + "interface":"eth-rt5" + } + ] + }, + "18040":{ + "inLabel":18040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16040, + "installed":true, + "nexthop":"10.0.7.4" + } + ] + }, + "18041":{ + "inLabel":18041, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16041, + "installed":true, + "interface":"eth-rt4" + } + ] + }, + "18050":{ + "inLabel":18050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16050, + "installed":true, + "nexthop":"10.0.8.5" + } + ] + }, + "18051":{ + "inLabel":18051, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16051, + "installed":true, + "interface":"eth-rt5" + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt6/step7/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt6/step7/show_yang_interface_isis_adjacencies.ref new file mode 120000 index 0000000000..0879b84d23 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt6/step7/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1 @@ +../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file diff --git a/tests/topotests/isis-sr-topo1/rt6/step8/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt6/step8/show_ip_route.ref new file mode 100644 index 0000000000..e4df0d846e --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt6/step8/show_ip_route.ref @@ -0,0 +1,284 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":40, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true, + "labels":[ + 16010 + ] + }, + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true, + "labels":[ + 16010 + ] + } + ] + } + ], + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true, + "labels":[ + 16020 + ] + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true, + "labels":[ + 16030 + ] + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true, + "labels":[ + 16040 + ] + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true, + "labels":[ + 16050 + ] + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true + }, + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true + } + ] + } + ], + "10.0.6.0\/24":[ + { + "prefix":"10.0.6.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4" + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5" + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt6/step8/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt6/step8/show_ipv6_route.ref new file mode 100644 index 0000000000..eee9dea4d3 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt6/step8/show_ipv6_route.ref @@ -0,0 +1,121 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":40, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5", + "active":true, + "labels":[ + 16011 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4", + "active":true, + "labels":[ + 16011 + ] + } + ] + } + ], + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4", + "active":true, + "labels":[ + 16021 + ] + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5", + "active":true, + "labels":[ + 16031 + ] + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4", + "active":true, + "labels":[ + 16041 + ] + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5", + "active":true, + "labels":[ + 16051 + ] + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt6/step8/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt6/step8/show_mpls_table.ref new file mode 100644 index 0000000000..a79406b300 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt6/step8/show_mpls_table.ref @@ -0,0 +1,134 @@ +{ + "18010":{ + "inLabel":18010, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16010, + "installed":true, + "nexthop":"10.0.8.5" + }, + { + "type":"SR (IS-IS)", + "outLabel":16010, + "installed":true, + "nexthop":"10.0.7.4" + } + ] + }, + "18011":{ + "inLabel":18011, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16011, + "installed":true, + "interface":"eth-rt5" + }, + { + "type":"SR (IS-IS)", + "outLabel":16011, + "installed":true, + "interface":"eth-rt4" + } + ] + }, + "18020":{ + "inLabel":18020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16020, + "installed":true, + "nexthop":"10.0.7.4" + } + ] + }, + "18021":{ + "inLabel":18021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16021, + "installed":true, + "interface":"eth-rt4" + } + ] + }, + "18030":{ + "inLabel":18030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16030, + "installed":true, + "nexthop":"10.0.8.5" + } + ] + }, + "18031":{ + "inLabel":18031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16031, + "installed":true, + "interface":"eth-rt5" + } + ] + }, + "18040":{ + "inLabel":18040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16040, + "installed":true, + "nexthop":"10.0.7.4" + } + ] + }, + "18041":{ + "inLabel":18041, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16041, + "installed":true, + "interface":"eth-rt4" + } + ] + }, + "18050":{ + "inLabel":18050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16050, + "installed":true, + "nexthop":"10.0.8.5" + } + ] + }, + "18051":{ + "inLabel":18051, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16051, + "installed":true, + "interface":"eth-rt5" + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt6/step8/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt6/step8/show_yang_interface_isis_adjacencies.ref new file mode 120000 index 0000000000..0879b84d23 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt6/step8/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1 @@ +../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file diff --git a/tests/topotests/isis-sr-topo1/rt6/step9/show_ip_route.ref b/tests/topotests/isis-sr-topo1/rt6/step9/show_ip_route.ref new file mode 100644 index 0000000000..e4df0d846e --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt6/step9/show_ip_route.ref @@ -0,0 +1,284 @@ +{ + "1.1.1.1\/32":[ + { + "prefix":"1.1.1.1\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":40, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true, + "labels":[ + 16010 + ] + }, + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true, + "labels":[ + 16010 + ] + } + ] + } + ], + "2.2.2.2\/32":[ + { + "prefix":"2.2.2.2\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true, + "labels":[ + 16020 + ] + } + ] + } + ], + "3.3.3.3\/32":[ + { + "prefix":"3.3.3.3\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true, + "labels":[ + 16030 + ] + } + ] + } + ], + "4.4.4.4\/32":[ + { + "prefix":"4.4.4.4\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true, + "labels":[ + 16040 + ] + } + ] + } + ], + "5.5.5.5\/32":[ + { + "prefix":"5.5.5.5\/32", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true, + "labels":[ + 16050 + ] + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true + }, + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4", + "active":true + } + ] + } + ], + "10.0.4.0\/24":[ + { + "prefix":"10.0.4.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true + } + ] + } + ], + "10.0.5.0\/24":[ + { + "prefix":"10.0.5.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true + } + ] + } + ], + "10.0.6.0\/24":[ + { + "prefix":"10.0.6.0\/24", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5", + "active":true + } + ] + } + ], + "10.0.7.0\/24":[ + { + "prefix":"10.0.7.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.7.4", + "afi":"ipv4", + "interfaceName":"eth-rt4" + } + ] + } + ], + "10.0.8.0\/24":[ + { + "prefix":"10.0.8.0\/24", + "protocol":"isis", + "distance":115, + "metric":20, + "nexthops":[ + { + "ip":"10.0.8.5", + "afi":"ipv4", + "interfaceName":"eth-rt5" + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt6/step9/show_ipv6_route.ref b/tests/topotests/isis-sr-topo1/rt6/step9/show_ipv6_route.ref new file mode 100644 index 0000000000..eee9dea4d3 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt6/step9/show_ipv6_route.ref @@ -0,0 +1,121 @@ +{ + "2001:db8:1000::1\/128":[ + { + "prefix":"2001:db8:1000::1\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":40, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5", + "active":true, + "labels":[ + 16011 + ] + }, + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4", + "active":true, + "labels":[ + 16011 + ] + } + ] + } + ], + "2001:db8:1000::2\/128":[ + { + "prefix":"2001:db8:1000::2\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4", + "active":true, + "labels":[ + 16021 + ] + } + ] + } + ], + "2001:db8:1000::3\/128":[ + { + "prefix":"2001:db8:1000::3\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5", + "active":true, + "labels":[ + 16031 + ] + } + ] + } + ], + "2001:db8:1000::4\/128":[ + { + "prefix":"2001:db8:1000::4\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt4", + "active":true, + "labels":[ + 16041 + ] + } + ] + } + ], + "2001:db8:1000::5\/128":[ + { + "prefix":"2001:db8:1000::5\/128", + "protocol":"isis", + "selected":true, + "destSelected":true, + "distance":115, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "afi":"ipv6", + "interfaceName":"eth-rt5", + "active":true, + "labels":[ + 16051 + ] + } + ] + } + ] +} diff --git a/tests/topotests/isis-sr-topo1/rt6/step9/show_mpls_table.ref b/tests/topotests/isis-sr-topo1/rt6/step9/show_mpls_table.ref new file mode 100644 index 0000000000..a79406b300 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt6/step9/show_mpls_table.ref @@ -0,0 +1,134 @@ +{ + "18010":{ + "inLabel":18010, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16010, + "installed":true, + "nexthop":"10.0.8.5" + }, + { + "type":"SR (IS-IS)", + "outLabel":16010, + "installed":true, + "nexthop":"10.0.7.4" + } + ] + }, + "18011":{ + "inLabel":18011, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16011, + "installed":true, + "interface":"eth-rt5" + }, + { + "type":"SR (IS-IS)", + "outLabel":16011, + "installed":true, + "interface":"eth-rt4" + } + ] + }, + "18020":{ + "inLabel":18020, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16020, + "installed":true, + "nexthop":"10.0.7.4" + } + ] + }, + "18021":{ + "inLabel":18021, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16021, + "installed":true, + "interface":"eth-rt4" + } + ] + }, + "18030":{ + "inLabel":18030, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16030, + "installed":true, + "nexthop":"10.0.8.5" + } + ] + }, + "18031":{ + "inLabel":18031, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16031, + "installed":true, + "interface":"eth-rt5" + } + ] + }, + "18040":{ + "inLabel":18040, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16040, + "installed":true, + "nexthop":"10.0.7.4" + } + ] + }, + "18041":{ + "inLabel":18041, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16041, + "installed":true, + "interface":"eth-rt4" + } + ] + }, + "18050":{ + "inLabel":18050, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16050, + "installed":true, + "nexthop":"10.0.8.5" + } + ] + }, + "18051":{ + "inLabel":18051, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":16051, + "installed":true, + "interface":"eth-rt5" + } + ] + } +} diff --git a/tests/topotests/isis-sr-topo1/rt6/step9/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis-sr-topo1/rt6/step9/show_yang_interface_isis_adjacencies.ref new file mode 120000 index 0000000000..0879b84d23 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt6/step9/show_yang_interface_isis_adjacencies.ref @@ -0,0 +1 @@ +../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file diff --git a/tests/topotests/isis-sr-topo1/rt6/zebra.conf b/tests/topotests/isis-sr-topo1/rt6/zebra.conf new file mode 100644 index 0000000000..6084010a93 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/rt6/zebra.conf @@ -0,0 +1,22 @@ +log file zebra.log +! +hostname rt6 +! +debug zebra kernel +debug zebra packet +debug zebra mpls +! +interface lo + ip address 6.6.6.6/32 + ipv6 address 2001:db8:1000::6/128 +! +interface eth-rt4 + ip address 10.0.7.6/24 +! +interface eth-rt5 + ip address 10.0.8.6/24 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/isis-sr-topo1/test_isis_sr_topo1.py b/tests/topotests/isis-sr-topo1/test_isis_sr_topo1.py new file mode 100755 index 0000000000..72bc96e4d0 --- /dev/null +++ b/tests/topotests/isis-sr-topo1/test_isis_sr_topo1.py @@ -0,0 +1,915 @@ +#!/usr/bin/env python + +# +# test_isis_sr_topo1.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2019 by +# Network Device Education Foundation, Inc. ("NetDEF") +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_isis_sr_topo1.py: + + +---------+ + | | + | RT1 | + | 1.1.1.1 | + | | + +---------+ + |eth-sw1 + | + | + | + +---------+ | +---------+ + | | | | | + | RT2 |eth-sw1 | eth-sw1| RT3 | + | 2.2.2.2 +----------+----------+ 3.3.3.3 | + | | 10.0.1.0/24 | | + +---------+ +---------+ + eth-rt4-1| |eth-rt4-2 eth-rt5-1| |eth-rt5-2 + | | | | + 10.0.2.0/24| |10.0.3.0/24 10.0.4.0/24| |10.0.5.0/24 + | | | | + eth-rt2-1| |eth-rt2-2 eth-rt3-1| |eth-rt3-2 + +---------+ +---------+ + | | | | + | RT4 | 10.0.6.0/24 | RT5 | + | 4.4.4.4 +---------------------+ 5.5.5.5 | + | |eth-rt5 eth-rt4| | + +---------+ +---------+ + eth-rt6| |eth-rt6 + | | + 10.0.7.0/24| |10.0.8.0/24 + | +---------+ | + | | | | + | | RT6 | | + +----------+ 6.6.6.6 +-----------+ + eth-rt4| |eth-rt5 + +---------+ +""" + +import os +import sys +import pytest +import json +import re +from time import sleep +from functools import partial + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, '../')) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + +class TemplateTopo(Topo): + "Test topology builder" + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # + # Define FRR Routers + # + for router in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: + tgen.add_router(router) + + # + # Define connections + # + switch = tgen.add_switch('s1') + switch.add_link(tgen.gears['rt1'], nodeif="eth-sw1") + switch.add_link(tgen.gears['rt2'], nodeif="eth-sw1") + switch.add_link(tgen.gears['rt3'], nodeif="eth-sw1") + + switch = tgen.add_switch('s2') + switch.add_link(tgen.gears['rt2'], nodeif="eth-rt4-1") + switch.add_link(tgen.gears['rt4'], nodeif="eth-rt2-1") + + switch = tgen.add_switch('s3') + switch.add_link(tgen.gears['rt2'], nodeif="eth-rt4-2") + switch.add_link(tgen.gears['rt4'], nodeif="eth-rt2-2") + + switch = tgen.add_switch('s4') + switch.add_link(tgen.gears['rt3'], nodeif="eth-rt5-1") + switch.add_link(tgen.gears['rt5'], nodeif="eth-rt3-1") + + switch = tgen.add_switch('s5') + switch.add_link(tgen.gears['rt3'], nodeif="eth-rt5-2") + switch.add_link(tgen.gears['rt5'], nodeif="eth-rt3-2") + + switch = tgen.add_switch('s6') + switch.add_link(tgen.gears['rt4'], nodeif="eth-rt5") + switch.add_link(tgen.gears['rt5'], nodeif="eth-rt4") + + switch = tgen.add_switch('s7') + switch.add_link(tgen.gears['rt4'], nodeif="eth-rt6") + switch.add_link(tgen.gears['rt6'], nodeif="eth-rt4") + + switch = tgen.add_switch('s8') + switch.add_link(tgen.gears['rt5'], nodeif="eth-rt6") + switch.add_link(tgen.gears['rt6'], nodeif="eth-rt5") + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(TemplateTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + # For all registered routers, load the zebra configuration file + for rname, router in router_list.iteritems(): + router.load_config( + TopoRouter.RD_ZEBRA, + os.path.join(CWD, '{}/zebra.conf'.format(rname)) + ) + router.load_config( + TopoRouter.RD_ISIS, + os.path.join(CWD, '{}/isisd.conf'.format(rname)) + ) + + tgen.start_router() + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + +def router_compare_json_output(rname, command, reference): + "Compare router JSON output" + + logger.info('Comparing router "%s" "%s" output', rname, command) + + tgen = get_topogen() + filename = '{}/{}/{}'.format(CWD, rname, reference) + expected = json.loads(open(filename).read()) + + # Run test function until we get an result. Wait at most 60 seconds. + test_func = partial(topotest.router_json_cmp, + tgen.gears[rname], command, expected) + _, diff = topotest.run_and_expect(test_func, None, count=120, wait=0.5) + assertmsg = '"{}" JSON output mismatches the expected result'.format(rname) + assert diff is None, assertmsg + +# +# Step 1 +# +# Test initial network convergence +# +def test_isis_adjacencies_step1(): + logger.info("Test (step 1): check IS-IS adjacencies") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: + router_compare_json_output(rname, "show yang operational-data /frr-interface:lib isisd", + "step1/show_yang_interface_isis_adjacencies.ref") + +def test_rib_ipv4_step1(): + logger.info("Test (step 1): verify IPv4 RIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: + router_compare_json_output(rname, "show ip route isis json", + "step1/show_ip_route.ref") + +def test_rib_ipv6_step1(): + logger.info("Test (step 1): verify IPv6 RIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: + router_compare_json_output(rname, "show ipv6 route isis json", + "step1/show_ipv6_route.ref") + +def test_mpls_lib_step1(): + logger.info("Test (step 1): verify MPLS LIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: + router_compare_json_output(rname, "show mpls table json", + "step1/show_mpls_table.ref") + +# +# Step 2 +# +# Action(s): +# -Disable IS-IS on the eth-rt5 interface on rt4 +# +# Expected changes: +# -rt4 should uninstall the Adj-SIDs pointing to rt5 +# -rt5 should uninstall the Adj-SIDs pointing to rt4 +# -rt2 should reinstall rt5's Prefix-SIDs (2 nexthops deleted) +# -rt3 should reinstall rt4's Prefix-SIDs (2 nexthops deleted) +# -rt4 should reinstall rt3's Prefix-SIDs (1 nexthop deleted) +# -rt4 should reinstall rt5's Prefix-SIDs (1 nexthop changed) +# -rt5 should reinstall rt2's Prefix-SIDs (1 nexthop deleted) +# -rt5 should reinstall rt4's Prefix-SIDs (1 nexthop changed) +# +def test_isis_adjacencies_step2(): + logger.info("Test (step 2): check IS-IS adjacencies") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info('Disabling IS-IS on the eth-rt5 interface on rt4') + tgen.net['rt4'].cmd('vtysh -c "conf t" -c "interface eth-rt5" -c "no ip router isis 1"') + tgen.net['rt4'].cmd('vtysh -c "conf t" -c "interface eth-rt5" -c "no ipv6 router isis 1"') + + for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: + router_compare_json_output(rname, "show yang operational-data /frr-interface:lib isisd", + "step2/show_yang_interface_isis_adjacencies.ref") + +def test_rib_ipv4_step2(): + logger.info("Test (step 2): verify IPv4 RIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: + router_compare_json_output(rname, "show ip route isis json", + "step2/show_ip_route.ref") + +def test_rib_ipv6_step2(): + logger.info("Test (step 2): verify IPv6 RIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: + router_compare_json_output(rname, "show ipv6 route isis json", + "step2/show_ipv6_route.ref") + +def test_mpls_lib_step2(): + logger.info("Test (step 2): verify MPLS LIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: + router_compare_json_output(rname, "show mpls table json", + "step2/show_mpls_table.ref") + +# +# Step 3 +# +# Action(s): +# -Shut down the eth-rt4 interface on rt6 +# -Shut down the eth-rt5 interface on rt6 +# +# Expected changes: +# -All routers should uninstall rt6's Prefix-SIDs +# -rt4 and rt5 should uninstall the Adj-SIDs pointing to rt6 +# -rt4 should reconverge rt5's Prefix-SIDs through rt2 using ECMP +# -rt5 should reconverge rt4's Prefix-SIDs through rt3 using ECMP +# -rt6 should uninstall all its IS-IS routes, Prefix-SIDs and Adj-SIDs +# +def test_isis_adjacencies_step3(): + logger.info("Test (step 3): check IS-IS adjacencies") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info('Shutting down the eth-rt4 interface on rt6') + tgen.net['rt6'].cmd('vtysh -c "conf t" -c "interface eth-rt4" -c "shutdown"') + logger.info('Shutting down the eth-rt5 interface on rt6') + tgen.net['rt6'].cmd('vtysh -c "conf t" -c "interface eth-rt5" -c "shutdown"') + + for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: + router_compare_json_output(rname, "show yang operational-data /frr-interface:lib isisd", + "step3/show_yang_interface_isis_adjacencies.ref") + +def test_rib_ipv4_step3(): + logger.info("Test (step 3): verify IPv4 RIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: + router_compare_json_output(rname, "show ip route isis json", + "step3/show_ip_route.ref") + +def test_rib_ipv6_step3(): + logger.info("Test (step 3): verify IPv6 RIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: + router_compare_json_output(rname, "show ipv6 route isis json", + "step3/show_ipv6_route.ref") + +def test_mpls_lib_step3(): + logger.info("Test (step 3): verify MPLS LIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: + router_compare_json_output(rname, "show mpls table json", + "step3/show_mpls_table.ref") + +# +# Step 4 +# +# Action(s): +# -Bring up the eth-rt4 interface on rt6 +# -Bring up the eth-rt5 interface on rt6 +# -Change rt6's SRGB +# +# Expected changes: +# -All routers should install rt6's Prefix-SIDs +# -rt4 and rt5 should install Adj-SIDs for rt6 +# -rt4 should reconverge rt5's Prefix-SIDs through rt6 using the new SRGB +# -rt5 should reconverge rt4's Prefix-SIDs through rt6 using the new SRGB +# -rt6 should reinstall all IS-IS routes and Prefix-SIDs from the network, and Adj-SIDs for rt4 and rt5 +# +def test_isis_adjacencies_step4(): + logger.info("Test (step 4): check IS-IS adjacencies") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info('Bringing up the eth-rt4 interface on rt6') + tgen.net['rt6'].cmd('vtysh -c "conf t" -c "interface eth-rt4" -c "no shutdown"') + logger.info('Bringing up the eth-rt5 interface on rt6') + tgen.net['rt6'].cmd('vtysh -c "conf t" -c "interface eth-rt5" -c "no shutdown"') + logger.info('Changing rt6\'s SRGB') + tgen.net['rt6'].cmd('vtysh -c "conf t" -c "router isis 1" -c "segment-routing global-block 18000 25999"') + + for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: + router_compare_json_output(rname, "show yang operational-data /frr-interface:lib isisd", + "step4/show_yang_interface_isis_adjacencies.ref") + +def test_rib_ipv4_step4(): + logger.info("Test (step 4): verify IPv4 RIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: + router_compare_json_output(rname, "show ip route isis json", + "step4/show_ip_route.ref") + +def test_rib_ipv6_step4(): + logger.info("Test (step 4): verify IPv6 RIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: + router_compare_json_output(rname, "show ipv6 route isis json", + "step4/show_ipv6_route.ref") + +def test_mpls_lib_step4(): + logger.info("Test (step 4): verify MPLS LIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: + router_compare_json_output(rname, "show mpls table json", + "step4/show_mpls_table.ref") + +# +# Step 5 +# +# Action(s): +# -Disable SR on rt6 +# +# Expected changes: +# -All routers should uninstall rt6's Prefix-SIDs +# -rt4 should uninstall rt5's Prefix-SIDs since the nexthop router hasn't SR enabled anymore +# -rt5 should uninstall rt4's Prefix-SIDs since the nexthop router hasn't SR enabled anymore +# -rt6 should uninstall all Prefix-SIDs from the network, and the Adj-SIDs for rt4 and rt5 +# +def test_isis_adjacencies_step5(): + logger.info("Test (step 5): check IS-IS adjacencies") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info('Disabling SR on rt6') + tgen.net['rt6'].cmd('vtysh -c "conf t" -c "router isis 1" -c "no segment-routing on"') + + for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: + router_compare_json_output(rname, "show yang operational-data /frr-interface:lib isisd", + "step5/show_yang_interface_isis_adjacencies.ref") + +def test_rib_ipv4_step5(): + logger.info("Test (step 5): verify IPv4 RIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: + router_compare_json_output(rname, "show ip route isis json", + "step5/show_ip_route.ref") + +def test_rib_ipv6_step5(): + logger.info("Test (step 5): verify IPv6 RIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: + router_compare_json_output(rname, "show ipv6 route isis json", + "step5/show_ipv6_route.ref") + +def test_mpls_lib_step5(): + logger.info("Test (step 5): verify MPLS LIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: + router_compare_json_output(rname, "show mpls table json", + "step5/show_mpls_table.ref") + +# +# Step 6 +# +# Action(s): +# -Enable SR on rt6 +# +# Expected changes: +# -All routers should install rt6's Prefix-SIDs +# -rt4 should install rt5's Prefix-SIDs through rt6 +# -rt5 should install rt4's Prefix-SIDs through rt6 +# -rt6 should install all Prefix-SIDs from the network, and Adj-SIDs for rt4 and rt5 +# +def test_isis_adjacencies_step6(): + logger.info("Test (step 6): check IS-IS adjacencies") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info('Enabling SR on rt6') + tgen.net['rt6'].cmd('vtysh -c "conf t" -c "router isis 1" -c "segment-routing on"') + + for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: + router_compare_json_output(rname, "show yang operational-data /frr-interface:lib isisd", + "step6/show_yang_interface_isis_adjacencies.ref") + +def test_rib_ipv4_step6(): + logger.info("Test (step 6): verify IPv4 RIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: + router_compare_json_output(rname, "show ip route isis json", + "step6/show_ip_route.ref") + +def test_rib_ipv6_step6(): + logger.info("Test (step 6): verify IPv6 RIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: + router_compare_json_output(rname, "show ipv6 route isis json", + "step6/show_ipv6_route.ref") + +def test_mpls_lib_step6(): + logger.info("Test (step 6): verify MPLS LIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: + router_compare_json_output(rname, "show mpls table json", + "step6/show_mpls_table.ref") + +# +# Step 7 +# +# Action(s): +# -Delete rt1's Prefix-SIDs +# +# Expected changes: +# -All routers should uninstall rt1's Prefix-SIDs +# +def test_isis_adjacencies_step7(): + logger.info("Test (step 7): check IS-IS adjacencies") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info('Deleting rt1\'s Prefix-SIDs') + tgen.net['rt1'].cmd('vtysh -c "conf t" -c "router isis 1" -c "no segment-routing prefix 1.1.1.1/32 index 10"') + tgen.net['rt1'].cmd('vtysh -c "conf t" -c "router isis 1" -c "no segment-routing prefix 2001:db8:1000::1/128 index 11"') + + for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: + router_compare_json_output(rname, "show yang operational-data /frr-interface:lib isisd", + "step7/show_yang_interface_isis_adjacencies.ref") + +def test_rib_ipv4_step7(): + logger.info("Test (step 7): verify IPv4 RIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: + router_compare_json_output(rname, "show ip route isis json", + "step7/show_ip_route.ref") + +def test_rib_ipv6_step7(): + logger.info("Test (step 7): verify IPv6 RIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: + router_compare_json_output(rname, "show ipv6 route isis json", + "step7/show_ipv6_route.ref") + +def test_mpls_lib_step7(): + logger.info("Test (step 7): verify MPLS LIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: + router_compare_json_output(rname, "show mpls table json", + "step7/show_mpls_table.ref") + +# +# Step 8 +# +# Action(s): +# -Re-add rt1's Prefix-SIDs +# +# Expected changes: +# -All routers should install rt1's Prefix-SIDs +# +def test_isis_adjacencies_step8(): + logger.info("Test (step 8): check IS-IS adjacencies") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info('Re-adding rt1\'s Prefix-SIDs') + tgen.net['rt1'].cmd('vtysh -c "conf t" -c "router isis 1" -c "segment-routing prefix 1.1.1.1/32 index 10"') + tgen.net['rt1'].cmd('vtysh -c "conf t" -c "router isis 1" -c "segment-routing prefix 2001:db8:1000::1/128 index 11"') + + for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: + router_compare_json_output(rname, "show yang operational-data /frr-interface:lib isisd", + "step8/show_yang_interface_isis_adjacencies.ref") + +def test_rib_ipv4_step8(): + logger.info("Test (step 8): verify IPv4 RIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: + router_compare_json_output(rname, "show ip route isis json", + "step8/show_ip_route.ref") + +def test_rib_ipv6_step8(): + logger.info("Test (step 8): verify IPv6 RIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: + router_compare_json_output(rname, "show ipv6 route isis json", + "step8/show_ipv6_route.ref") + +def test_mpls_lib_step8(): + logger.info("Test (step 8): verify MPLS LIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: + router_compare_json_output(rname, "show mpls table json", + "step8/show_mpls_table.ref") + +# +# Step 9 +# +# Action(s): +# -Change rt1's Prefix-SIDs to use the no-php option +# -Change rt6's Prefix-SIDs to stop using the explicit-null option +# +# Expected changes: +# -rt2 and rt3 should reinstall rt1's Prefix-SIDs accordingly +# -rt4 and rt5 should reinstall rt6's Prefix-SIDs accordingly +# +def test_isis_adjacencies_step9(): + logger.info("Test (step 9): check IS-IS adjacencies") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info('Changing rt1\'s Prefix-SIDs to use the no-php option') + tgen.net['rt1'].cmd('vtysh -c "conf t" -c "router isis 1" -c "segment-routing prefix 1.1.1.1/32 index 10 no-php-flag"') + tgen.net['rt1'].cmd('vtysh -c "conf t" -c "router isis 1" -c "segment-routing prefix 2001:db8:1000::1/128 index 11 no-php-flag"') + logger.info('Change rt6\'s Prefix-SIDs to stop using the explicit-null option') + tgen.net['rt6'].cmd('vtysh -c "conf t" -c "router isis 1" -c "segment-routing prefix 6.6.6.6/32 index 60"') + tgen.net['rt6'].cmd('vtysh -c "conf t" -c "router isis 1" -c "segment-routing prefix 2001:db8:1000::6/128 index 61"') + + for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: + router_compare_json_output(rname, "show yang operational-data /frr-interface:lib isisd", + "step9/show_yang_interface_isis_adjacencies.ref") + +def test_rib_ipv4_step9(): + logger.info("Test (step 9): verify IPv4 RIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: + router_compare_json_output(rname, "show ip route isis json", + "step9/show_ip_route.ref") + +def test_rib_ipv6_step9(): + logger.info("Test (step 9): verify IPv6 RIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: + router_compare_json_output(rname, "show ipv6 route isis json", + "step9/show_ipv6_route.ref") + +def test_mpls_lib_step9(): + logger.info("Test (step 9): verify MPLS LIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: + router_compare_json_output(rname, "show mpls table json", + "step9/show_mpls_table.ref") + +# +# Step 10 +# +# Action(s): +# -Remove the IPv4 address from rt4's eth-rt2-1 interface +# +# Expected changes: +# -rt2 should uninstall the IPv4 Adj-SIDs attached to the eth-rt4-1 interface +# -rt2 should reinstall all IPv4 Prefix-SIDs whose nexthop router is rt4 (ECMP shouldn't be used anymore) +# -rt4 should reinstall all IPv4 Prefix-SIDs whose nexthop router is rt2 (ECMP shouldn't be used anymore) +# +def test_isis_adjacencies_step10(): + logger.info("Test (step 10): check IS-IS adjacencies") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info('Removing the IPv4 address from rt4\'s eth-rt2-1 interface') + tgen.net['rt4'].cmd('vtysh -c "conf t" -c "interface eth-rt2-1" -c "no ip address 10.0.2.4/24"') + + for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: + router_compare_json_output(rname, "show yang operational-data /frr-interface:lib isisd", + "step10/show_yang_interface_isis_adjacencies.ref") + +def test_rib_ipv4_step10(): + logger.info("Test (step 10): verify IPv4 RIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: + router_compare_json_output(rname, "show ip route isis json", + "step10/show_ip_route.ref") + +def test_rib_ipv6_step10(): + logger.info("Test (step 10): verify IPv6 RIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: + router_compare_json_output(rname, "show ipv6 route isis json", + "step10/show_ipv6_route.ref") + +def test_mpls_lib_step10(): + logger.info("Test (step 10): verify MPLS LIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: + router_compare_json_output(rname, "show mpls table json", + "step10/show_mpls_table.ref") + +# +# Step 11 +# +# Action(s): +# -Enter invalid SR configuration +# +# Expected changes: +# -All commands should be rejected +# +def test_isis_invalid_config_step11(): + logger.info("Test (step 11): check if invalid configuration is rejected") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info('Entering invalid Segment Routing configuration...') + ret = tgen.net['rt1'].cmd('vtysh -c "conf t" -c "router isis 1" -c "segment-routing prefix 1.1.1.1/32 index 10000"') + assert re.search("Configuration failed", ret) is not None, "Invalid SR configuration wasn't rejected" + ret = tgen.net['rt1'].cmd('vtysh -c "conf t" -c "router isis 1" -c "segment-routing global-block 16000 14999"') + assert re.search("Configuration failed", ret) is not None, "Invalid SR configuration wasn't rejected" + ret = tgen.net['rt1'].cmd('vtysh -c "conf t" -c "router isis 1" -c "segment-routing global-block 16000 16001"') + assert re.search("Configuration failed", ret) is not None, "Invalid SR configuration wasn't rejected" + +# +# Step 12 +# +# Action(s): +# -Restore the original network setup +# +# Expected changes: +# -All routes, Prefix-SIDs and Adj-SIDs should be the same as they were after the initial network convergence (step 1) +# +def test_isis_adjacencies_step12(): + logger.info("Test (step 12): check IS-IS adjacencies") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info('Restoring the original network setup') + tgen.net['rt4'].cmd('vtysh -c "conf t" -c "interface eth-rt5" -c "ip router isis 1"') + tgen.net['rt4'].cmd('vtysh -c "conf t" -c "interface eth-rt5" -c "ipv6 router isis 1"') + tgen.net['rt6'].cmd('vtysh -c "conf t" -c "router isis 1" -c "segment-routing global-block 16000 23999"') + tgen.net['rt1'].cmd('vtysh -c "conf t" -c "router isis 1" -c "segment-routing prefix 1.1.1.1/32 index 10"') + tgen.net['rt1'].cmd('vtysh -c "conf t" -c "router isis 1" -c "segment-routing prefix 2001:db8:1000::1/128 index 11"') + tgen.net['rt6'].cmd('vtysh -c "conf t" -c "router isis 1" -c "segment-routing prefix 6.6.6.6/32 index 60 explicit-null"') + tgen.net['rt6'].cmd('vtysh -c "conf t" -c "router isis 1" -c "segment-routing prefix 2001:db8:1000::6/128 index 61 explicit-null"') + tgen.net['rt4'].cmd('vtysh -c "conf t" -c "interface eth-rt2-1" -c "ip address 10.0.2.4/24"') + + for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: + router_compare_json_output(rname, "show yang operational-data /frr-interface:lib isisd", + "step1/show_yang_interface_isis_adjacencies.ref") + +def test_rib_ipv4_step12(): + logger.info("Test (step 12): verify IPv4 RIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: + router_compare_json_output(rname, "show ip route isis json", + "step1/show_ip_route.ref") + +def test_rib_ipv6_step12(): + logger.info("Test (step 12): verify IPv6 RIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: + router_compare_json_output(rname, "show ipv6 route isis json", + "step1/show_ipv6_route.ref") + +def test_mpls_lib_step12(): + logger.info("Test (step 12): verify MPLS LIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ['rt1', 'rt2', 'rt3', 'rt4', 'rt5', 'rt6']: + router_compare_json_output(rname, "show mpls table json", + "step1/show_mpls_table.ref") + +# Memory leak test template +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip('Memory leak test/report is disabled') + + tgen.report_memory_leaks() + +if __name__ == '__main__': + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/isis-topo1-vrf/__init__.py b/tests/topotests/isis-topo1-vrf/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/isis-topo1-vrf/r1/isisd.conf b/tests/topotests/isis-topo1-vrf/r1/isisd.conf new file mode 100755 index 0000000000..4ac4597015 --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r1/isisd.conf @@ -0,0 +1,15 @@ +hostname r1 +debug isis adj-packets +debug isis events +debug isis update-packets +interface r1-eth0 + ip router isis 1 vrf r1-cust1 + ipv6 router isis 1 vrf r1-cust1 + isis circuit-type level-2-only +! +router isis 1 vrf r1-cust1 + net 10.0000.0000.0000.0000.0000.0000.0000.0000.0000.00 + metric-style wide + redistribute ipv4 connected level-2 + redistribute ipv6 connected level-2 +! diff --git a/tests/topotests/isis-topo1-vrf/r1/r1_route.json b/tests/topotests/isis-topo1-vrf/r1/r1_route.json new file mode 100644 index 0000000000..77d2ad9c63 --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r1/r1_route.json @@ -0,0 +1,51 @@ +{ + "10.0.10.0/24": [ + { + "distance": 115, + "metric": 20, + "nexthops": [ + { + "active": true, + "afi": "ipv4", + "fib": true, + "interfaceName": "r1-eth0", + "ip": "10.0.20.1" + } + ], + "prefix": "10.0.10.0/24", + "protocol": "isis", + "selected": true, + "vrfName": "r1-cust1" + } + ], + "10.0.20.0/24": [ + { + "distance": 115, + "metric": 20, + "nexthops": [ + { + "afi": "ipv4", + "interfaceName": "r1-eth0", + "ip": "10.0.20.1" + } + ], + "prefix": "10.0.20.0/24", + "protocol": "isis", + "vrfName": "r1-cust1" + }, + { + "nexthops": [ + { + "active": true, + "directlyConnected": true, + "fib": true, + "interfaceName": "r1-eth0" + } + ], + "prefix": "10.0.20.0/24", + "protocol": "connected", + "selected": true, + "vrfName": "r1-cust1" + } + ] +} diff --git a/tests/topotests/isis-topo1-vrf/r1/r1_route6.json b/tests/topotests/isis-topo1-vrf/r1/r1_route6.json new file mode 100644 index 0000000000..888b9e2c42 --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r1/r1_route6.json @@ -0,0 +1,36 @@ +{ + "2001:db8:1:1::/64": [ + { + "nexthops": [ + { + "active": true, + "directlyConnected": true, + "fib": true, + "interfaceName": "r1-eth0" + } + ], + "prefix": "2001:db8:1:1::/64", + "protocol": "connected", + "selected": true, + "vrfName": "r1-cust1" + } + ], + "2001:db8:2:1::/64": [ + { + "distance": 115, + "metric": 20, + "nexthops": [ + { + "active": true, + "afi": "ipv6", + "fib": true, + "interfaceName": "r1-eth0" + } + ], + "prefix": "2001:db8:2:1::/64", + "protocol": "isis", + "selected": true, + "vrfName": "r1-cust1" + } + ] +} diff --git a/tests/topotests/isis-topo1-vrf/r1/r1_route6_linux.json b/tests/topotests/isis-topo1-vrf/r1/r1_route6_linux.json new file mode 100755 index 0000000000..d1ace402ba --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r1/r1_route6_linux.json @@ -0,0 +1,14 @@ +{ + "2001:db8:1:1::/64": { + "dev": "r1-eth0", + "metric": "256", + "pref": "medium", + "proto": "kernel" + }, + "2001:db8:2:1::/64": { + "dev": "r1-eth0", + "metric": "20", + "pref": "medium", + "proto": "187" + } +} diff --git a/tests/topotests/isis-topo1-vrf/r1/r1_route_linux.json b/tests/topotests/isis-topo1-vrf/r1/r1_route_linux.json new file mode 100755 index 0000000000..6af22297e9 --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r1/r1_route_linux.json @@ -0,0 +1,13 @@ +{ + "10.0.10.0/24": { + "dev": "r1-eth0", + "metric": "20", + "proto": "187", + "via": "10.0.20.1" + }, + "10.0.20.0/24": { + "dev": "r1-eth0", + "proto": "kernel", + "scope": "link" + } +} diff --git a/tests/topotests/isis-topo1-vrf/r1/r1_topology.json b/tests/topotests/isis-topo1-vrf/r1/r1_topology.json new file mode 100644 index 0000000000..8e3cdc7bd6 --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r1/r1_topology.json @@ -0,0 +1,80 @@ +{ + "1": { + "level-1": { + "ipv4": [ + { + "vertex": "r1" + } + ], + "ipv6": [ + { + "vertex": "r1" + } + ] + }, + "level-2": { + "ipv4": [ + { + "vertex": "r1" + }, + { + "metric": "internal", + "parent": "0", + "type": "IP", + "vertex": "10.0.20.0/24" + }, + { + "interface": "r1-eth0", + "metric": "10", + "next-hop": "r3", + "parent": "r1(4)", + "type": "TE-IS", + "vertex": "r3" + }, + { + "interface": "r3", + "metric": "TE", + "next-hop": "20", + "parent": "r1-eth0", + "type": "IP", + "vertex": "10.0.20.0/24" + }, + { + "interface": "r3", + "metric": "TE", + "next-hop": "20", + "parent": "r1-eth0", + "type": "IP", + "vertex": "10.0.10.0/24" + } + ], + "ipv6": [ + { + "vertex": "r1" + }, + { + "metric": "internal", + "parent": "0", + "type": "IP6", + "vertex": "2001:db8:1:1::/64" + }, + { + "interface": "r1-eth0", + "metric": "10", + "next-hop": "r3", + "parent": "r1(4)", + "type": "TE-IS", + "vertex": "r3" + }, + { + "interface": "r3", + "metric": "internal", + "next-hop": "20", + "parent": "r1-eth0", + "type": "IP6", + "vertex": "2001:db8:2:1::/64" + } + ] + } + } +} diff --git a/tests/topotests/isis-topo1-vrf/r1/zebra.conf b/tests/topotests/isis-topo1-vrf/r1/zebra.conf new file mode 100755 index 0000000000..fa1c02e5f8 --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r1/zebra.conf @@ -0,0 +1,9 @@ +hostname r1 +interface r1-eth0 vrf r1-cust1 + ip address 10.0.20.2/24 + ipv6 address 2001:db8:1:1::2/64 +! +interface lo + ip address 10.254.0.1/32 + ipv6 address 2001:db8:F::1/128 +! diff --git a/tests/topotests/isis-topo1-vrf/r2/isisd.conf b/tests/topotests/isis-topo1-vrf/r2/isisd.conf new file mode 100755 index 0000000000..4c68540265 --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r2/isisd.conf @@ -0,0 +1,15 @@ +hostname r2 +debug isis adj-packets +debug isis events +debug isis update-packets +interface r2-eth0 + ip router isis 1 vrf r2-cust1 + ipv6 router isis 1 vrf r2-cust1 + isis circuit-type level-2-only +! +router isis 1 vrf r2-cust1 + net 10.0000.0000.0000.0000.0000.0000.0000.0000.0001.00 + metric-style wide + redistribute ipv4 connected level-2 + redistribute ipv6 connected level-2 +! diff --git a/tests/topotests/isis-topo1-vrf/r2/r2_route.json b/tests/topotests/isis-topo1-vrf/r2/r2_route.json new file mode 100644 index 0000000000..98252c5939 --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r2/r2_route.json @@ -0,0 +1,51 @@ +{ + "10.0.11.0/24": [ + { + "distance": 115, + "metric": 20, + "nexthops": [ + { + "active": true, + "afi": "ipv4", + "fib": true, + "interfaceName": "r2-eth0", + "ip": "10.0.21.1" + } + ], + "prefix": "10.0.11.0/24", + "protocol": "isis", + "selected": true, + "vrfName": "r2-cust1" + } + ], + "10.0.21.0/24": [ + { + "distance": 115, + "metric": 20, + "nexthops": [ + { + "afi": "ipv4", + "interfaceName": "r2-eth0", + "ip": "10.0.21.1" + } + ], + "prefix": "10.0.21.0/24", + "protocol": "isis", + "vrfName": "r2-cust1" + }, + { + "nexthops": [ + { + "active": true, + "directlyConnected": true, + "fib": true, + "interfaceName": "r2-eth0" + } + ], + "prefix": "10.0.21.0/24", + "protocol": "connected", + "selected": true, + "vrfName": "r2-cust1" + } + ] +} diff --git a/tests/topotests/isis-topo1-vrf/r2/r2_route6.json b/tests/topotests/isis-topo1-vrf/r2/r2_route6.json new file mode 100644 index 0000000000..b01789b8d9 --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r2/r2_route6.json @@ -0,0 +1,36 @@ +{ + "2001:db8:1:2::/64": [ + { + "nexthops": [ + { + "active": true, + "directlyConnected": true, + "fib": true, + "interfaceName": "r2-eth0" + } + ], + "prefix": "2001:db8:1:2::/64", + "protocol": "connected", + "selected": true, + "vrfName": "r2-cust1" + } + ], + "2001:db8:2:2::/64": [ + { + "distance": 115, + "metric": 20, + "nexthops": [ + { + "active": true, + "afi": "ipv6", + "fib": true, + "interfaceName": "r2-eth0" + } + ], + "prefix": "2001:db8:2:2::/64", + "protocol": "isis", + "selected": true, + "vrfName": "r2-cust1" + } + ] +} diff --git a/tests/topotests/isis-topo1-vrf/r2/r2_route6_linux.json b/tests/topotests/isis-topo1-vrf/r2/r2_route6_linux.json new file mode 100755 index 0000000000..27423e1936 --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r2/r2_route6_linux.json @@ -0,0 +1,14 @@ +{ + "2001:db8:1:2::/64": { + "dev": "r2-eth0", + "metric": "256", + "pref": "medium", + "proto": "kernel" + }, + "2001:db8:2:2::/64": { + "dev": "r2-eth0", + "metric": "20", + "pref": "medium", + "proto": "187" + } +} diff --git a/tests/topotests/isis-topo1-vrf/r2/r2_route_linux.json b/tests/topotests/isis-topo1-vrf/r2/r2_route_linux.json new file mode 100755 index 0000000000..744b0780f3 --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r2/r2_route_linux.json @@ -0,0 +1,13 @@ +{ + "10.0.11.0/24": { + "dev": "r2-eth0", + "metric": "20", + "proto": "187", + "via": "10.0.21.1" + }, + "10.0.21.0/24": { + "dev": "r2-eth0", + "proto": "kernel", + "scope": "link" + } +} diff --git a/tests/topotests/isis-topo1-vrf/r2/r2_topology.json b/tests/topotests/isis-topo1-vrf/r2/r2_topology.json new file mode 100644 index 0000000000..72022a8167 --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r2/r2_topology.json @@ -0,0 +1,80 @@ +{ + "1": { + "level-1": { + "ipv4": [ + { + "vertex": "r2" + } + ], + "ipv6": [ + { + "vertex": "r2" + } + ] + }, + "level-2": { + "ipv4": [ + { + "vertex": "r2" + }, + { + "metric": "internal", + "parent": "0", + "type": "IP", + "vertex": "10.0.21.0/24" + }, + { + "interface": "r2-eth0", + "metric": "10", + "next-hop": "r4", + "parent": "r2(4)", + "type": "TE-IS", + "vertex": "r4" + }, + { + "interface": "r4", + "metric": "TE", + "next-hop": "20", + "parent": "r2-eth0", + "type": "IP", + "vertex": "10.0.21.0/24" + }, + { + "interface": "r4", + "metric": "TE", + "next-hop": "20", + "parent": "r2-eth0", + "type": "IP", + "vertex": "10.0.11.0/24" + } + ], + "ipv6": [ + { + "vertex": "r2" + }, + { + "metric": "internal", + "parent": "0", + "type": "IP6", + "vertex": "2001:db8:1:2::/64" + }, + { + "interface": "r2-eth0", + "metric": "10", + "next-hop": "r4", + "parent": "r2(4)", + "type": "TE-IS", + "vertex": "r4" + }, + { + "interface": "r4", + "metric": "internal", + "next-hop": "20", + "parent": "r2-eth0", + "type": "IP6", + "vertex": "2001:db8:2:2::/64" + } + ] + } + } +} \ No newline at end of file diff --git a/tests/topotests/isis-topo1-vrf/r2/zebra.conf b/tests/topotests/isis-topo1-vrf/r2/zebra.conf new file mode 100755 index 0000000000..a62af1749e --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r2/zebra.conf @@ -0,0 +1,9 @@ +hostname r2 +interface r2-eth0 vrf r2-cust1 + ip address 10.0.21.2/24 + ipv6 address 2001:db8:1:2::2/64 +! +interface lo + ip address 10.254.0.2/32 + ipv6 address 2001:db8:F::2/128 +! diff --git a/tests/topotests/isis-topo1-vrf/r3/isisd.conf b/tests/topotests/isis-topo1-vrf/r3/isisd.conf new file mode 100755 index 0000000000..ca01876690 --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r3/isisd.conf @@ -0,0 +1,22 @@ +hostname r3 +debug isis adj-packets +debug isis events +debug isis update-packets +interface r3-eth0 + ip router isis 1 vrf r3-cust1 + ipv6 router isis 1 vrf r3-cust1 + isis circuit-type level-2-only +! +interface r3-eth1 + ip router isis 1 vrf r3-cust1 + ipv6 router isis 1 vrf r3-cust1 + isis circuit-type level-1 +! +router isis 1 vrf r3-cust1 + net 10.0000.0000.0000.0000.0000.0000.0000.0000.0002.00 + metric-style wide + redistribute ipv4 connected level-1 + redistribute ipv4 connected level-2 + redistribute ipv6 connected level-1 + redistribute ipv6 connected level-2 +! diff --git a/tests/topotests/isis-topo1-vrf/r3/r3_route.json b/tests/topotests/isis-topo1-vrf/r3/r3_route.json new file mode 100644 index 0000000000..de158876f1 --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r3/r3_route.json @@ -0,0 +1,100 @@ +{ + "10.0.10.0/24": [ + { + "distance": 115, + "metric": 20, + "nexthops": [ + { + "afi": "ipv4", + "interfaceName": "r3-eth1", + "ip": "10.0.10.1" + } + ], + "prefix": "10.0.10.0/24", + "protocol": "isis", + "vrfName": "r3-cust1" + }, + { + "nexthops": [ + { + "active": true, + "directlyConnected": true, + "fib": true, + "interfaceName": "r3-eth1" + } + ], + "prefix": "10.0.10.0/24", + "protocol": "connected", + "selected": true, + "vrfName": "r3-cust1" + } + ], + "10.0.11.0/24": [ + { + "distance": 115, + "metric": 20, + "nexthops": [ + { + "active": true, + "afi": "ipv4", + "fib": true, + "interfaceName": "r3-eth1", + "ip": "10.0.10.1" + } + ], + "prefix": "10.0.11.0/24", + "protocol": "isis", + "selected": true, + "vrfName": "r3-cust1" + } + ], + "10.0.20.0/24": [ + { + "distance": 115, + "metric": 20, + "nexthops": [ + { + "afi": "ipv4", + "interfaceName": "r3-eth0", + "ip": "10.0.20.2" + } + ], + "prefix": "10.0.20.0/24", + "protocol": "isis", + "vrfName": "r3-cust1" + }, + { + "nexthops": [ + { + "active": true, + "directlyConnected": true, + "fib": true, + "interfaceName": "r3-eth0" + } + ], + "prefix": "10.0.20.0/24", + "protocol": "connected", + "selected": true, + "vrfName": "r3-cust1" + } + ], + "10.0.21.0/24": [ + { + "distance": 115, + "metric": 30, + "nexthops": [ + { + "active": true, + "afi": "ipv4", + "fib": true, + "interfaceName": "r3-eth1", + "ip": "10.0.10.1" + } + ], + "prefix": "10.0.21.0/24", + "protocol": "isis", + "selected": true, + "vrfName": "r3-cust1" + } + ] +} diff --git a/tests/topotests/isis-topo1-vrf/r3/r3_route6.json b/tests/topotests/isis-topo1-vrf/r3/r3_route6.json new file mode 100644 index 0000000000..31a1e4620f --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r3/r3_route6.json @@ -0,0 +1,70 @@ +{ + "2001:db8:1:1::/64": [ + { + "nexthops": [ + { + "active": true, + "directlyConnected": true, + "fib": true, + "interfaceName": "r3-eth0" + } + ], + "prefix": "2001:db8:1:1::/64", + "protocol": "connected", + "selected": true, + "vrfName": "r3-cust1" + } + ], + "2001:db8:1:2::/64": [ + { + "distance": 115, + "metric": 30, + "nexthops": [ + { + "active": true, + "afi": "ipv6", + "fib": true, + "interfaceName": "r3-eth1" + } + ], + "prefix": "2001:db8:1:2::/64", + "protocol": "isis", + "selected": true, + "vrfName": "r3-cust1" + } + ], + "2001:db8:2:1::/64": [ + { + "nexthops": [ + { + "active": true, + "directlyConnected": true, + "fib": true, + "interfaceName": "r3-eth1" + } + ], + "prefix": "2001:db8:2:1::/64", + "protocol": "connected", + "selected": true, + "vrfName": "r3-cust1" + } + ], + "2001:db8:2:2::/64": [ + { + "distance": 115, + "metric": 20, + "nexthops": [ + { + "active": true, + "afi": "ipv6", + "fib": true, + "interfaceName": "r3-eth1" + } + ], + "prefix": "2001:db8:2:2::/64", + "protocol": "isis", + "selected": true, + "vrfName": "r3-cust1" + } + ] +} diff --git a/tests/topotests/isis-topo1-vrf/r3/r3_route6_linux.json b/tests/topotests/isis-topo1-vrf/r3/r3_route6_linux.json new file mode 100755 index 0000000000..bc527d2e1e --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r3/r3_route6_linux.json @@ -0,0 +1,26 @@ +{ + "2001:db8:1:1::/64": { + "dev": "r3-eth0", + "metric": "256", + "pref": "medium", + "proto": "kernel" + }, + "2001:db8:1:2::/64": { + "dev": "r3-eth1", + "metric": "20", + "pref": "medium", + "proto": "187" + }, + "2001:db8:2:1::/64": { + "dev": "r3-eth1", + "metric": "256", + "pref": "medium", + "proto": "kernel" + }, + "2001:db8:2:2::/64": { + "dev": "r3-eth1", + "metric": "20", + "pref": "medium", + "proto": "187" + } +} diff --git a/tests/topotests/isis-topo1-vrf/r3/r3_route_linux.json b/tests/topotests/isis-topo1-vrf/r3/r3_route_linux.json new file mode 100755 index 0000000000..515d376475 --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r3/r3_route_linux.json @@ -0,0 +1,24 @@ +{ + "10.0.10.0/24": { + "dev": "r3-eth1", + "proto": "kernel", + "scope": "link" + }, + "10.0.11.0/24": { + "dev": "r3-eth1", + "metric": "20", + "proto": "187", + "via": "10.0.10.1" + }, + "10.0.20.0/24": { + "dev": "r3-eth0", + "proto": "kernel", + "scope": "link" + }, + "10.0.21.0/24": { + "dev": "r3-eth1", + "metric": "20", + "proto": "187", + "via": "10.0.10.1" + } +} diff --git a/tests/topotests/isis-topo1-vrf/r3/r3_topology.json b/tests/topotests/isis-topo1-vrf/r3/r3_topology.json new file mode 100644 index 0000000000..62b895766e --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r3/r3_topology.json @@ -0,0 +1,132 @@ +{ + "1": { + "level-1": { + "ipv4": [ + { + "vertex": "r3" + }, + { + "metric": "internal", + "parent": "0", + "type": "IP", + "vertex": "10.0.10.0/24" + }, + { + "interface": "r3-eth1", + "metric": "10", + "next-hop": "r5", + "parent": "r3(4)", + "type": "TE-IS", + "vertex": "r5" + }, + { + "interface": "r5", + "metric": "TE", + "next-hop": "20", + "parent": "r3-eth1", + "type": "IP", + "vertex": "10.0.10.0/24" + }, + { + "interface": "r5", + "metric": "TE", + "next-hop": "20", + "parent": "r3-eth1", + "type": "IP", + "vertex": "10.0.11.0/24" + }, + { + "interface": "r5", + "metric": "TE", + "next-hop": "30", + "parent": "r3-eth1", + "type": "IP", + "vertex": "10.0.21.0/24" + } + ], + "ipv6": [ + { + "vertex": "r3" + }, + { + "metric": "internal", + "parent": "0", + "type": "IP6", + "vertex": "2001:db8:2:1::/64" + }, + { + "interface": "r3-eth1", + "metric": "10", + "next-hop": "r5", + "parent": "r3(4)", + "type": "TE-IS", + "vertex": "r5" + }, + { + "interface": "r5", + "metric": "internal", + "next-hop": "20", + "parent": "r3-eth1", + "type": "IP6", + "vertex": "2001:db8:2:2::/64" + }, + { + "interface": "r5", + "metric": "internal", + "next-hop": "30", + "parent": "r3-eth1", + "type": "IP6", + "vertex": "2001:db8:1:2::/64" + } + ] + }, + "level-2": { + "ipv4": [ + { + "vertex": "r3" + }, + { + "metric": "internal", + "parent": "0", + "type": "IP", + "vertex": "10.0.20.0/24" + }, + { + "interface": "r3-eth0", + "metric": "10", + "next-hop": "r3", + "parent": "r3(4)", + "type": "TE-IS", + "vertex": "r3" + }, + { + "interface": "r3", + "metric": "TE", + "next-hop": "20", + "parent": "r3-eth0", + "type": "IP", + "vertex": "10.0.20.0/24" + } + ], + "ipv6": [ + { + "vertex": "r3" + }, + { + "metric": "internal", + "parent": "0", + "type": "IP6", + "vertex": "2001:db8:1:1::/64" + }, + { + "interface": "r3-eth0", + "metric": "10", + "next-hop": "r3", + "parent": "r3(4)", + "type": "TE-IS", + "vertex": "r3" + } + ] + } + } +} diff --git a/tests/topotests/isis-topo1-vrf/r3/zebra.conf b/tests/topotests/isis-topo1-vrf/r3/zebra.conf new file mode 100755 index 0000000000..ac0b810fce --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r3/zebra.conf @@ -0,0 +1,13 @@ +hostname r3 +interface r3-eth0 vrf r3-cust1 + ip address 10.0.20.1/24 + ipv6 address 2001:db8:1:1::1/64 +! +interface r3-eth1 vrf r3-cust1 + ip address 10.0.10.2/24 + ipv6 address 2001:db8:2:1::2/64 +! +interface lo + ip address 10.254.0.3/32 + ipv6 address 2001:db8:F::3/128 +! diff --git a/tests/topotests/isis-topo1-vrf/r4/isisd.conf b/tests/topotests/isis-topo1-vrf/r4/isisd.conf new file mode 100755 index 0000000000..74b1603d85 --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r4/isisd.conf @@ -0,0 +1,25 @@ +hostname r4 +debug isis adj-packets +debug isis events +debug isis update-packets +debug isis lsp-gen +debug isis lsp-sched + +interface r4-eth0 + ip router isis 1 vrf r4-cust1 + ipv6 router isis 1 vrf r4-cust1 + isis circuit-type level-2-only +! +interface r4-eth1 + ip router isis 1 vrf r4-cust1 + ipv6 router isis 1 vrf r4-cust1 + isis circuit-type level-1 +! +router isis 1 vrf r4-cust1 + net 10.0000.0000.0000.0000.0000.0000.0000.0000.0004.00 + metric-style wide + redistribute ipv4 connected level-1 + redistribute ipv4 connected level-2 + redistribute ipv6 connected level-1 + redistribute ipv6 connected level-2 +! diff --git a/tests/topotests/isis-topo1-vrf/r4/r4_route.json b/tests/topotests/isis-topo1-vrf/r4/r4_route.json new file mode 100644 index 0000000000..b3ed4f2cbe --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r4/r4_route.json @@ -0,0 +1,93 @@ +{ + "10.0.10.0/24": [ + { + "metric": 20, + "nexthops": [ + { + "active": true, + "afi": "ipv4", + "fib": true, + "interfaceName": "r4-eth1", + "ip": "10.0.11.1" + } + ], + "prefix": "10.0.10.0/24", + "protocol": "isis", + "selected": true, + "vrfName": "r4-cust1" + } + ], + "10.0.11.0/24": [ + { + "nexthops": [ + { + "afi": "ipv4", + "interfaceName": "r4-eth1", + "ip": "10.0.11.1" + } + ], + "prefix": "10.0.11.0/24", + "protocol": "isis", + "vrfName": "r4-cust1" + }, + { + "nexthops": [ + { + "active": true, + "directlyConnected": true, + "fib": true, + "interfaceName": "r4-eth1" + } + ], + "prefix": "10.0.11.0/24", + "protocol": "connected", + "selected": true, + "vrfName": "r4-cust1" + } + ], + "10.0.20.0/24": [ + { + "nexthops": [ + { + "active": true, + "afi": "ipv4", + "fib": true, + "interfaceName": "r4-eth1", + "ip": "10.0.11.1" + } + ], + "prefix": "10.0.20.0/24", + "protocol": "isis", + "selected": true, + "vrfName": "r4-cust1" + } + ], + "10.0.21.0/24": [ + { + "nexthops": [ + { + "afi": "ipv4", + "interfaceName": "r4-eth0", + "ip": "10.0.21.2" + } + ], + "prefix": "10.0.21.0/24", + "protocol": "isis", + "vrfName": "r4-cust1" + }, + { + "nexthops": [ + { + "active": true, + "directlyConnected": true, + "fib": true, + "interfaceName": "r4-eth0" + } + ], + "prefix": "10.0.21.0/24", + "protocol": "connected", + "selected": true, + "vrfName": "r4-cust1" + } + ] +} diff --git a/tests/topotests/isis-topo1-vrf/r4/r4_route6.json b/tests/topotests/isis-topo1-vrf/r4/r4_route6.json new file mode 100644 index 0000000000..88a91749c2 --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r4/r4_route6.json @@ -0,0 +1,70 @@ +{ + "2001:db8:1:1::/64": [ + { + "distance": 115, + "metric": 30, + "nexthops": [ + { + "active": true, + "afi": "ipv6", + "fib": true, + "interfaceName": "r4-eth1" + } + ], + "prefix": "2001:db8:1:1::/64", + "protocol": "isis", + "selected": true, + "vrfName": "r4-cust1" + } + ], + "2001:db8:1:2::/64": [ + { + "nexthops": [ + { + "active": true, + "directlyConnected": true, + "fib": true, + "interfaceName": "r4-eth0" + } + ], + "prefix": "2001:db8:1:2::/64", + "protocol": "connected", + "selected": true, + "vrfName": "r4-cust1" + } + ], + "2001:db8:2:1::/64": [ + { + "distance": 115, + "metric": 20, + "nexthops": [ + { + "active": true, + "afi": "ipv6", + "fib": true, + "interfaceName": "r4-eth1" + } + ], + "prefix": "2001:db8:2:1::/64", + "protocol": "isis", + "selected": true, + "vrfName": "r4-cust1" + } + ], + "2001:db8:2:2::/64": [ + { + "nexthops": [ + { + "active": true, + "directlyConnected": true, + "fib": true, + "interfaceName": "r4-eth1" + } + ], + "prefix": "2001:db8:2:2::/64", + "protocol": "connected", + "selected": true, + "vrfName": "r4-cust1" + } + ] +} diff --git a/tests/topotests/isis-topo1-vrf/r4/r4_route6_linux.json b/tests/topotests/isis-topo1-vrf/r4/r4_route6_linux.json new file mode 100755 index 0000000000..b1cd5b9db9 --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r4/r4_route6_linux.json @@ -0,0 +1,26 @@ +{ + "2001:db8:1:1::/64": { + "dev": "r4-eth1", + "metric": "20", + "pref": "medium", + "proto": "187" + }, + "2001:db8:1:2::/64": { + "dev": "r4-eth0", + "metric": "256", + "pref": "medium", + "proto": "kernel" + }, + "2001:db8:2:1::/64": { + "dev": "r4-eth1", + "metric": "20", + "pref": "medium", + "proto": "187" + }, + "2001:db8:2:2::/64": { + "dev": "r4-eth1", + "metric": "256", + "pref": "medium", + "proto": "kernel" + } +} diff --git a/tests/topotests/isis-topo1-vrf/r4/r4_route_linux.json b/tests/topotests/isis-topo1-vrf/r4/r4_route_linux.json new file mode 100755 index 0000000000..3198b85789 --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r4/r4_route_linux.json @@ -0,0 +1,24 @@ +{ + "10.0.10.0/24": { + "dev": "r4-eth1", + "metric": "20", + "proto": "187", + "via": "10.0.11.1" + }, + "10.0.11.0/24": { + "dev": "r4-eth1", + "proto": "kernel", + "scope": "link" + }, + "10.0.20.0/24": { + "dev": "r4-eth1", + "metric": "20", + "proto": "187", + "via": "10.0.11.1" + }, + "10.0.21.0/24": { + "dev": "r4-eth0", + "proto": "kernel", + "scope": "link" + } +} diff --git a/tests/topotests/isis-topo1-vrf/r4/r4_topology.json b/tests/topotests/isis-topo1-vrf/r4/r4_topology.json new file mode 100644 index 0000000000..0d69550cad --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r4/r4_topology.json @@ -0,0 +1,132 @@ +{ + "1": { + "level-1": { + "ipv4": [ + { + "vertex": "r4" + }, + { + "metric": "internal", + "parent": "0", + "type": "IP", + "vertex": "10.0.11.0/24" + }, + { + "interface": "r4-eth1", + "metric": "10", + "next-hop": "r5", + "parent": "r4(4)", + "type": "TE-IS", + "vertex": "r5" + }, + { + "interface": "r5", + "metric": "TE", + "next-hop": "20", + "parent": "r4-eth1", + "type": "IP", + "vertex": "10.0.10.0/24" + }, + { + "interface": "r5", + "metric": "TE", + "next-hop": "20", + "parent": "r4-eth1", + "type": "IP", + "vertex": "10.0.11.0/24" + }, + { + "interface": "r5", + "metric": "TE", + "next-hop": "30", + "parent": "r4-eth1", + "type": "IP", + "vertex": "10.0.20.0/24" + } + ], + "ipv6": [ + { + "vertex": "r4" + }, + { + "metric": "internal", + "parent": "0", + "type": "IP6", + "vertex": "2001:db8:2:2::/64" + }, + { + "interface": "r4-eth1", + "metric": "10", + "next-hop": "r5", + "parent": "r4(4)", + "type": "TE-IS", + "vertex": "r5" + }, + { + "interface": "r5", + "metric": "internal", + "next-hop": "20", + "parent": "r4-eth1", + "type": "IP6", + "vertex": "2001:db8:2:1::/64" + }, + { + "interface": "r5", + "metric": "internal", + "next-hop": "30", + "parent": "r4-eth1", + "type": "IP6", + "vertex": "2001:db8:1:1::/64" + } + ] + }, + "level-2": { + "ipv4": [ + { + "vertex": "r4" + }, + { + "metric": "internal", + "parent": "0", + "type": "IP", + "vertex": "10.0.21.0/24" + }, + { + "interface": "r4-eth0", + "metric": "10", + "next-hop": "r2", + "parent": "r4(4)", + "type": "TE-IS", + "vertex": "r2" + }, + { + "interface": "r2", + "metric": "TE", + "next-hop": "20", + "parent": "r4-eth0", + "type": "IP", + "vertex": "10.0.21.0/24" + } + ], + "ipv6": [ + { + "vertex": "r4" + }, + { + "metric": "internal", + "parent": "0", + "type": "IP6", + "vertex": "2001:db8:1:2::/64" + }, + { + "interface": "r4-eth0", + "metric": "10", + "next-hop": "r2", + "parent": "r4(4)", + "type": "TE-IS", + "vertex": "r2" + } + ] + } + } +} diff --git a/tests/topotests/isis-topo1-vrf/r4/zebra.conf b/tests/topotests/isis-topo1-vrf/r4/zebra.conf new file mode 100755 index 0000000000..9c8941f7a5 --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r4/zebra.conf @@ -0,0 +1,13 @@ +hostname r4 +interface r4-eth0 vrf r4-cust1 + ip address 10.0.21.1/24 + ipv6 address 2001:db8:1:2::1/64 +! +interface r4-eth1 vrf r4-cust1 + ip address 10.0.11.2/24 + ipv6 address 2001:db8:2:2::2/64 +! +interface lo + ip address 10.254.0.4/32 + ipv6 address 2001:db8:F::4/128 +! diff --git a/tests/topotests/isis-topo1-vrf/r5/isisd.conf b/tests/topotests/isis-topo1-vrf/r5/isisd.conf new file mode 100755 index 0000000000..9e9b030455 --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r5/isisd.conf @@ -0,0 +1,21 @@ +hostname r5 +debug isis adj-packets +debug isis events +debug isis update-packets +interface r5-eth0 + ip router isis 1 vrf r5-cust1 + ipv6 router isis 1 vrf r5-cust1 + isis circuit-type level-1 +! +interface r5-eth1 + ip router isis 1 vrf r5-cust1 + ipv6 router isis 1 vrf r5-cust1 + isis circuit-type level-1 +! +router isis 1 vrf r5-cust1 + net 10.0000.0000.0000.0000.0000.0000.0000.0000.0005.00 + metric-style wide + is-type level-1 + redistribute ipv4 connected level-1 + redistribute ipv6 connected level-1 +! diff --git a/tests/topotests/isis-topo1-vrf/r5/r5_route.json b/tests/topotests/isis-topo1-vrf/r5/r5_route.json new file mode 100644 index 0000000000..5efa36bce6 --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r5/r5_route.json @@ -0,0 +1,94 @@ +{ + "10.0.10.0/24": [ + { + "distance": 115, + "metric": 20, + "nexthops": [ + { + "afi": "ipv4", + "interfaceName": "r5-eth0", + "ip": "10.0.10.2" + } + ], + "prefix": "10.0.10.0/24", + "protocol": "isis", + "vrfName": "r5-cust1" + }, + { + "nexthops": [ + { + "active": true, + "directlyConnected": true, + "fib": true, + "interfaceName": "r5-eth0" + } + ], + "prefix": "10.0.10.0/24", + "protocol": "connected", + "selected": true, + "vrfName": "r5-cust1" + } + ], + "10.0.11.0/24": [ + { + "nexthops": [ + { + "afi": "ipv4", + "interfaceName": "r5-eth1", + "ip": "10.0.11.2" + } + ], + "prefix": "10.0.11.0/24", + "protocol": "isis", + "vrfName": "r5-cust1" + }, + { + "nexthops": [ + { + "active": true, + "directlyConnected": true, + "fib": true, + "interfaceName": "r5-eth1" + } + ], + "prefix": "10.0.11.0/24", + "protocol": "connected", + "selected": true, + "vrfName": "r5-cust1" + } + ], + "10.0.20.0/24": [ + { + "nexthops": [ + { + "active": true, + "afi": "ipv4", + "fib": true, + "interfaceName": "r5-eth0", + "ip": "10.0.10.2" + } + ], + "prefix": "10.0.20.0/24", + "protocol": "isis", + "selected": true, + "vrfName": "r5-cust1" + } + ], + "10.0.21.0/24": [ + { + "nexthops": [ + { + "active": true, + "afi": "ipv4", + "fib": true, + "interfaceName": "r5-eth1", + "ip": "10.0.11.2" + } + ], + "prefix": "10.0.21.0/24", + "protocol": "isis", + "selected": true, + "vrfName": "r5-cust1" + } + ] +} diff --git a/tests/topotests/isis-topo1-vrf/r5/r5_route6.json b/tests/topotests/isis-topo1-vrf/r5/r5_route6.json new file mode 100644 index 0000000000..5e8f6364af --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r5/r5_route6.json @@ -0,0 +1,70 @@ +{ + "2001:db8:1:1::/64": [ + { + "distance": 115, + "metric": 20, + "nexthops": [ + { + "active": true, + "afi": "ipv6", + "fib": true, + "interfaceName": "r5-eth0" + } + ], + "prefix": "2001:db8:1:1::/64", + "protocol": "isis", + "selected": true, + "vrfName": "r5-cust1" + } + ], + "2001:db8:1:2::/64": [ + { + "distance": 115, + "metric": 20, + "nexthops": [ + { + "active": true, + "afi": "ipv6", + "fib": true, + "interfaceName": "r5-eth1" + } + ], + "prefix": "2001:db8:1:2::/64", + "protocol": "isis", + "selected": true, + "vrfName": "r5-cust1" + } + ], + "2001:db8:2:1::/64": [ + { + "nexthops": [ + { + "active": true, + "directlyConnected": true, + "fib": true, + "interfaceName": "r5-eth0" + } + ], + "prefix": "2001:db8:2:1::/64", + "protocol": "connected", + "selected": true, + "vrfName": "r5-cust1" + } + ], + "2001:db8:2:2::/64": [ + { + "nexthops": [ + { + "active": true, + "directlyConnected": true, + "fib": true, + "interfaceName": "r5-eth1" + } + ], + "prefix": "2001:db8:2:2::/64", + "protocol": "connected", + "selected": true, + "vrfName": "r5-cust1" + } + ] +} diff --git a/tests/topotests/isis-topo1-vrf/r5/r5_route6_linux.json b/tests/topotests/isis-topo1-vrf/r5/r5_route6_linux.json new file mode 100755 index 0000000000..3db3c93ea6 --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r5/r5_route6_linux.json @@ -0,0 +1,26 @@ +{ + "2001:db8:1:1::/64": { + "dev": "r5-eth0", + "metric": "20", + "pref": "medium", + "proto": "187" + }, + "2001:db8:1:2::/64": { + "dev": "r5-eth1", + "metric": "20", + "pref": "medium", + "proto": "187" + }, + "2001:db8:2:1::/64": { + "dev": "r5-eth0", + "metric": "256", + "pref": "medium", + "proto": "kernel" + }, + "2001:db8:2:2::/64": { + "dev": "r5-eth1", + "metric": "256", + "pref": "medium", + "proto": "kernel" + } +} diff --git a/tests/topotests/isis-topo1-vrf/r5/r5_route_linux.json b/tests/topotests/isis-topo1-vrf/r5/r5_route_linux.json new file mode 100755 index 0000000000..6a38ba864a --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r5/r5_route_linux.json @@ -0,0 +1,24 @@ +{ + "10.0.10.0/24": { + "dev": "r5-eth0", + "proto": "kernel", + "scope": "link" + }, + "10.0.11.0/24": { + "dev": "r5-eth1", + "proto": "kernel", + "scope": "link" + }, + "10.0.20.0/24": { + "dev": "r5-eth0", + "metric": "20", + "proto": "187", + "via": "10.0.10.2" + }, + "10.0.21.0/24": { + "dev": "r5-eth1", + "metric": "20", + "proto": "187", + "via": "10.0.11.2" + } +} diff --git a/tests/topotests/isis-topo1-vrf/r5/r5_topology.json b/tests/topotests/isis-topo1-vrf/r5/r5_topology.json new file mode 100644 index 0000000000..b4ed6a069d --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r5/r5_topology.json @@ -0,0 +1,124 @@ +{ + "1": { + "level-1": { + "ipv4": [ + { + "vertex": "r5" + }, + { + "metric": "internal", + "parent": "0", + "type": "IP", + "vertex": "10.0.10.0/24" + }, + { + "metric": "internal", + "parent": "0", + "type": "IP", + "vertex": "10.0.11.0/24" + }, + { + "interface": "r5-eth0", + "metric": "10", + "next-hop": "r3", + "parent": "r5(4)", + "type": "TE-IS", + "vertex": "r3" + }, + { + "interface": "r5-eth1", + "metric": "10", + "next-hop": "r4", + "parent": "r5(4)", + "type": "TE-IS", + "vertex": "r4" + }, + { + "interface": "r3", + "metric": "TE", + "next-hop": "20", + "parent": "r5-eth0", + "type": "IP", + "vertex": "10.0.20.0/24" + }, + { + "interface": "r3", + "metric": "TE", + "next-hop": "20", + "parent": "r5-eth0", + "type": "IP", + "vertex": "10.0.10.0/24" + }, + { + "interface": "r4", + "metric": "TE", + "next-hop": "20", + "parent": "r5-eth1", + "type": "IP", + "vertex": "10.0.21.0/24" + }, + { + "interface": "r4", + "metric": "TE", + "next-hop": "20", + "parent": "r5-eth1", + "type": "IP", + "vertex": "10.0.11.0/24" + } + ], + "ipv6": [ + { + "vertex": "r5" + }, + { + "metric": "internal", + "parent": "0", + "type": "IP6", + "vertex": "2001:db8:2:1::/64" + }, + { + "metric": "internal", + "parent": "0", + "type": "IP6", + "vertex": "2001:db8:2:2::/64" + }, + { + "interface": "r5-eth0", + "metric": "10", + "next-hop": "r3", + "parent": "r5(4)", + "type": "TE-IS", + "vertex": "r3" + }, + { + "interface": "r5-eth1", + "metric": "10", + "next-hop": "r4", + "parent": "r5(4)", + "type": "TE-IS", + "vertex": "r4" + }, + { + "interface": "r3", + "metric": "internal", + "next-hop": "20", + "parent": "r5-eth0", + "type": "IP6", + "vertex": "2001:db8:1:1::/64" + }, + { + "interface": "r4", + "metric": "internal", + "next-hop": "20", + "parent": "r5-eth1", + "type": "IP6", + "vertex": "2001:db8:1:2::/64" + } + ] + }, + "level-2": { + "ipv4": [], + "ipv6": [] + } + } +} \ No newline at end of file diff --git a/tests/topotests/isis-topo1-vrf/r5/zebra.conf b/tests/topotests/isis-topo1-vrf/r5/zebra.conf new file mode 100755 index 0000000000..c6bc6302fc --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r5/zebra.conf @@ -0,0 +1,13 @@ +hostname r5 +interface r5-eth0 vrf r5-cust1 + ip address 10.0.10.1/24 + ipv6 address 2001:db8:2:1::1/64 +! +interface r5-eth1 vrf r5-cust1 + ip address 10.0.11.1/24 + ipv6 address 2001:db8:2:2::1/64 +! +interface lo + ip address 10.254.0.5/32 + ipv6 address 2001:db8:F::5/128 +! diff --git a/tests/topotests/isis-topo1-vrf/test_isis_topo1_vrf.dot b/tests/topotests/isis-topo1-vrf/test_isis_topo1_vrf.dot new file mode 100755 index 0000000000..01f9ba780f --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/test_isis_topo1_vrf.dot @@ -0,0 +1,100 @@ +## Color coding: +######################### +## Main FRR: #f08080 red +## Switches: #d0e0d0 gray +## RIP: #19e3d9 Cyan +## RIPng: #fcb314 dark yellow +## OSPFv2: #32b835 Green +## OSPFv3: #19e3d9 Cyan +## ISIS IPv4 #fcb314 dark yellow +## ISIS IPv6 #9a81ec purple +## BGP IPv4 #eee3d3 beige +## BGP IPv6 #fdff00 yellow +##### Colors (see http://www.color-hex.com/) + +graph template { + label="isis topo1"; + + # Routers + r1 [ + shape=doubleoctagon, + label="r1\n10.254.0.1\n2001:DB8:F::1", + fillcolor="#f08080", + style=filled, + ]; + r2 [ + shape=doubleoctagon + label="r2\n10.254.0.2\n2001:DB8:F::2", + fillcolor="#f08080", + style=filled, + ]; + r3 [ + shape=doubleoctagon + label="r3\n10.254.0.3\n2001:DB8:F::3", + fillcolor="#f08080", + style=filled, + ]; + r4 [ + shape=doubleoctagon + label="r4\n10.254.0.4\n2001:DB8:F::4", + fillcolor="#f08080", + style=filled, + ]; + r5 [ + shape=doubleoctagon + label="r5\n10.254.0.5\n2001:DB8:F::5", + fillcolor="#f08080", + style=filled, + ]; + + # Switches + sw1 [ + shape=oval, + label="sw1\n10.0.20.0/24\n2001:DB8:1:1::/64", + fillcolor="#d0e0d0", + style=filled, + ]; + sw2 [ + shape=oval, + label="sw2\n10.0.21.0/24\n2001:DB8:1:2::/64", + fillcolor="#d0e0d0", + style=filled, + ]; + sw3 [ + shape=oval, + label="sw3\n10.0.10.0/24\n2001:DB8:2:1::/64", + fillcolor="#d0e0d0", + style=filled, + ]; + sw4 [ + shape=oval, + label="sw4\n10.0.11.0/24\n2001:DB8:2:2::/64", + fillcolor="#d0e0d0", + style=filled, + ]; + + # Connections + subgraph cluster0 { + label="level 2"; + + r1 -- sw1 [label="eth0\n.2"]; + r2 -- sw2 [label="eth0\n.2"]; + } + + subgraph cluster1 { + label="level 1/2"; + + r3 -- sw1 [label="eth0\n.1"]; + r3 -- sw3 [label="eth1\n.2"]; + + r4 -- sw4 [label="eth1\n.2"]; + r4 -- sw2 [label="eth0\n.1"]; + } + + subgraph cluster2 { + label="level 1"; + + r5 -- sw3 [label="eth0\n.1"]; + r5 -- sw4 [label="eth1\n.1"]; + } +} diff --git a/tests/topotests/isis-topo1-vrf/test_isis_topo1_vrf.jpg b/tests/topotests/isis-topo1-vrf/test_isis_topo1_vrf.jpg new file mode 100755 index 0000000000..4ad730f2a0 Binary files /dev/null and b/tests/topotests/isis-topo1-vrf/test_isis_topo1_vrf.jpg differ diff --git a/tests/topotests/isis-topo1-vrf/test_isis_topo1_vrf.py b/tests/topotests/isis-topo1-vrf/test_isis_topo1_vrf.py new file mode 100755 index 0000000000..7671ed9322 --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/test_isis_topo1_vrf.py @@ -0,0 +1,434 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2020 by Niral Networks, Inc. ("Niral Networks") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_isis_topo1_vrf.py: Test ISIS vrf topology. +""" + +import collections +import functools +import json +import os +import re +import sys +import pytest +import platform + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from lib.common_config import adjust_router_l3mdev + +from mininet.topo import Topo + + +class ISISTopo1(Topo): + "Simple two layer ISIS vrf topology" + + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # Add ISIS routers: + # r1 r2 + # | sw1 | sw2 + # r3 r4 + # | | + # sw3 sw4 + # \ / + # r5 + for routern in range(1, 6): + tgen.add_router("r{}".format(routern)) + + # r1 <- sw1 -> r3 + sw = tgen.add_switch("sw1") + sw.add_link(tgen.gears["r1"]) + sw.add_link(tgen.gears["r3"]) + + # r2 <- sw2 -> r4 + sw = tgen.add_switch("sw2") + sw.add_link(tgen.gears["r2"]) + sw.add_link(tgen.gears["r4"]) + + # r3 <- sw3 -> r5 + sw = tgen.add_switch("sw3") + sw.add_link(tgen.gears["r3"]) + sw.add_link(tgen.gears["r5"]) + + # r4 <- sw4 -> r5 + sw = tgen.add_switch("sw4") + sw.add_link(tgen.gears["r4"]) + sw.add_link(tgen.gears["r5"]) + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(ISISTopo1, mod.__name__) + tgen.start_topology() + + logger.info("Testing with VRF Lite support") + + cmds = [ + "ip link add {0}-cust1 type vrf table 1001", + "ip link add loop1 type dummy", + "ip link set {0}-eth0 master {0}-cust1", + "ip link set {0}-eth1 master {0}-cust1", + ] + + # For all registered routers, load the zebra configuration file + for rname, router in tgen.routers().iteritems(): + # create VRF rx-cust1 and link rx-eth0 to rx-cust1 + for cmd in cmds: + output = tgen.net[rname].cmd(cmd.format(rname)) + + # adjust handling of vrf traffic + adjust_router_l3mdev(tgen, rname) + + for rname, router in tgen.routers().iteritems(): + router.load_config( + TopoRouter.RD_ZEBRA, + os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_ISIS, + os.path.join(CWD, "{}/isisd.conf".format(rname)) + ) + # After loading the configurations, this function loads configured daemons. + tgen.start_router() + + has_version_20 = False + for router in tgen.routers().values(): + if router.has_version("<", "4"): + has_version_20 = True + + if has_version_20: + logger.info("Skipping ISIS vrf tests for FRR 2.0") + tgen.set_error("ISIS has convergence problems with IPv6") + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + # move back rx-eth0 to default VRF + # delete rx-vrf + tgen.stop_topology() + +def test_isis_convergence(): + "Wait for the protocol to converge before starting to test" + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("waiting for ISIS protocol to converge") + + for rname, router in tgen.routers().iteritems(): + filename = "{0}/{1}/{1}_topology.json".format(CWD, rname) + expected = json.loads(open(filename).read()) + def compare_isis_topology(router, expected): + "Helper function to test ISIS vrf topology convergence." + actual = show_isis_topology(router) + + return topotest.json_cmp(actual, expected) + + test_func = functools.partial(compare_isis_topology, router, expected) + (result, diff) = topotest.run_and_expect(test_func, None, wait=0.5, count=120) + assert result, "ISIS did not converge on {}:\n{}".format(rname, diff) + +def test_isis_route_installation(): + "Check whether all expected routes are present" + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Checking routers for installed ISIS vrf routes") + # Check for routes in 'show ip route vrf {}-cust1 json' + for rname, router in tgen.routers().iteritems(): + filename = "{0}/{1}/{1}_route.json".format(CWD, rname) + expected = json.loads(open(filename, "r").read()) + actual = router.vtysh_cmd("show ip route vrf {0}-cust1 json".format(rname) , isjson=True) + # Older FRR versions don't list interfaces in some ISIS routes + if router.has_version("<", "3.1"): + for network, routes in expected.iteritems(): + for route in routes: + if route["protocol"] != "isis": + continue + + for nexthop in route["nexthops"]: + nexthop.pop("interfaceIndex", None) + nexthop.pop("interfaceName", None) + + assertmsg = "Router '{}' routes mismatch".format(rname) + assert topotest.json_cmp(actual, expected) is None, assertmsg + + +def test_isis_linux_route_installation(): + + dist = platform.dist() + + if (dist[1] == "16.04"): + pytest.skip("Kernel not supported for vrf") + + "Check whether all expected routes are present and installed in the OS" + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Checking routers for installed ISIS vrf routes in OS") + # Check for routes in `ip route show vrf {}-cust1` + for rname, router in tgen.routers().iteritems(): + filename = "{0}/{1}/{1}_route_linux.json".format(CWD, rname) + expected = json.loads(open(filename, "r").read()) + actual = topotest.ip4_vrf_route(router) + + # Older FRR versions install routes using different proto + if router.has_version("<", "3.1"): + for network, netoptions in expected.iteritems(): + if "proto" in netoptions and netoptions["proto"] == "187": + netoptions["proto"] = "zebra" + + assertmsg = "Router '{}' OS routes mismatch".format(rname) + assert topotest.json_cmp(actual, expected) is None, assertmsg + +def test_isis_route6_installation(): + "Check whether all expected routes are present" + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Checking routers for installed ISIS vrf IPv6 routes") + # Check for routes in 'show ipv6 route vrf {}-cust1 json' + for rname, router in tgen.routers().iteritems(): + filename = "{0}/{1}/{1}_route6.json".format(CWD, rname) + expected = json.loads(open(filename, "r").read()) + actual = router.vtysh_cmd("show ipv6 route vrf {}-cust1 json".format(rname) , isjson=True) + + # Older FRR versions don't list interfaces in some ISIS routes + if router.has_version("<", "3.1"): + for network, routes in expected.iteritems(): + for route in routes: + if route["protocol"] != "isis": + continue + + for nexthop in route["nexthops"]: + nexthop.pop("interfaceIndex", None) + nexthop.pop("interfaceName", None) + + assertmsg = "Router '{}' routes mismatch".format(rname) + assert topotest.json_cmp(actual, expected) is None, assertmsg + +def test_isis_linux_route6_installation(): + + dist = platform.dist() + + if (dist[1] == "16.04"): + pytest.skip("Kernel not supported for vrf") + + "Check whether all expected routes are present and installed in the OS" + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Checking routers for installed ISIS vrf IPv6 routes in OS") + # Check for routes in `ip -6 route show vrf {}-cust1` + for rname, router in tgen.routers().iteritems(): + filename = "{0}/{1}/{1}_route6_linux.json".format(CWD, rname) + expected = json.loads(open(filename, "r").read()) + actual = topotest.ip6_vrf_route(router) + + # Older FRR versions install routes using different proto + if router.has_version("<", "3.1"): + for network, netoptions in expected.iteritems(): + if "proto" in netoptions and netoptions["proto"] == "187": + netoptions["proto"] = "zebra" + + assertmsg = "Router '{}' OS routes mismatch".format(rname) + assert topotest.json_cmp(actual, expected) is None, assertmsg + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) + + +# +# Auxiliary functions +# + + +def dict_merge(dct, merge_dct): + """ + Recursive dict merge. Inspired by :meth:``dict.update()``, instead of + updating only top-level keys, dict_merge recurses down into dicts nested + to an arbitrary depth, updating keys. The ``merge_dct`` is merged into + ``dct``. + :param dct: dict onto which the merge is executed + :param merge_dct: dct merged into dct + :return: None + + Source: + https://gist.github.com/angstwad/bf22d1822c38a92ec0a9 + """ + for k, v in merge_dct.iteritems(): + if ( + k in dct + and isinstance(dct[k], dict) + and isinstance(merge_dct[k], collections.Mapping) + ): + dict_merge(dct[k], merge_dct[k]) + else: + dct[k] = merge_dct[k] + + +def parse_topology(lines, level): + """ + Parse the output of 'show isis topology level-X' into a Python dict. + """ + areas = {} + area = None + ipv = None + + for line in lines: + area_match = re.match(r"Area (.+):", line) + if area_match: + area = area_match.group(1) + if area not in areas: + areas[area] = {level: {"ipv4": [], "ipv6": []}} + ipv = None + continue + elif area is None: + continue + + if re.match(r"IS\-IS paths to level-. routers that speak IPv6", line): + ipv = "ipv6" + continue + if re.match(r"IS\-IS paths to level-. routers that speak IP", line): + ipv = "ipv4" + continue + + item_match = re.match(r"([^ ]+) ([^ ]+) ([^ ]+) ([^ ]+) ([^ ]+) ([^ ]+)", line) + if item_match is not None: + # Skip header + if ( + item_match.group(1) == "Vertex" + and item_match.group(2) == "Type" + and item_match.group(3) == "Metric" + and item_match.group(4) == "Next-Hop" + and item_match.group(5) == "Interface" + and item_match.group(6) == "Parent" + ): + continue + + areas[area][level][ipv].append( + { + "vertex": item_match.group(1), + "type": item_match.group(2), + "metric": item_match.group(3), + "next-hop": item_match.group(4), + "interface": item_match.group(5), + "parent": item_match.group(6), + } + ) + continue + + item_match = re.match(r"([^ ]+) ([^ ]+) ([^ ]+) ([^ ]+)", line) + if item_match is not None: + areas[area][level][ipv].append( + { + "vertex": item_match.group(1), + "type": item_match.group(2), + "metric": item_match.group(3), + "parent": item_match.group(4), + } + ) + continue + + item_match = re.match(r"([^ ]+)", line) + if item_match is not None: + areas[area][level][ipv].append({"vertex": item_match.group(1)}) + continue + + return areas + + +def show_isis_topology(router): + """ + Get the ISIS vrf topology in a dictionary format. + + Sample: + { + 'area-name': { + 'level-1': [ + { + 'vertex': 'r1' + } + ], + 'level-2': [ + { + 'vertex': '10.0.0.1/24', + 'type': 'IP', + 'parent': '0', + 'metric': 'internal' + } + ] + }, + 'area-name-2': { + 'level-2': [ + { + "interface": "rX-ethY", + "metric": "Z", + "next-hop": "rA", + "parent": "rC(B)", + "type": "TE-IS", + "vertex": "rD" + } + ] + } + } + """ + l1out = topotest.normalize_text( + router.vtysh_cmd("show isis vrf {}-cust1 topology level-1".format(router.name)) + ).splitlines() + l2out = topotest.normalize_text( + router.vtysh_cmd("show isis vrf {}-cust1 topology level-2".format(router.name)) + ).splitlines() + + l1 = parse_topology(l1out, "level-1") + l2 = parse_topology(l2out, "level-2") + + dict_merge(l1, l2) + return l1 + diff --git a/tests/topotests/isis-topo1/r1/r1_route.json b/tests/topotests/isis-topo1/r1/r1_route.json index 6f5041e3c2..123b4dd163 100644 --- a/tests/topotests/isis-topo1/r1/r1_route.json +++ b/tests/topotests/isis-topo1/r1/r1_route.json @@ -8,7 +8,6 @@ "active": true, "afi": "ipv4", "fib": true, - "interfaceIndex": 2, "interfaceName": "r1-eth0", "ip": "10.0.20.1" } @@ -25,7 +24,6 @@ "nexthops": [ { "afi": "ipv4", - "interfaceIndex": 2, "interfaceName": "r1-eth0", "ip": "10.0.20.1" } @@ -39,7 +37,6 @@ "active": true, "directlyConnected": true, "fib": true, - "interfaceIndex": 2, "interfaceName": "r1-eth0" } ], @@ -55,7 +52,6 @@ "active": true, "directlyConnected": true, "fib": true, - "interfaceIndex": 1, "interfaceName": "lo" } ], @@ -73,7 +69,6 @@ "active": true, "afi": "ipv4", "fib": true, - "interfaceIndex": 2, "interfaceName": "r1-eth0", "ip": "10.0.20.1" } diff --git a/tests/topotests/isis-topo1/r1/r1_route6.json b/tests/topotests/isis-topo1/r1/r1_route6.json index 1060150a59..bd09839fea 100644 --- a/tests/topotests/isis-topo1/r1/r1_route6.json +++ b/tests/topotests/isis-topo1/r1/r1_route6.json @@ -6,7 +6,6 @@ "active": true, "directlyConnected": true, "fib": true, - "interfaceIndex": 2, "interfaceName": "r1-eth0" } ], @@ -24,7 +23,6 @@ "active": true, "afi": "ipv6", "fib": true, - "interfaceIndex": 2, "interfaceName": "r1-eth0" } ], @@ -40,7 +38,6 @@ "active": true, "directlyConnected": true, "fib": true, - "interfaceIndex": 1, "interfaceName": "lo" } ], @@ -58,7 +55,6 @@ "active": true, "afi": "ipv6", "fib": true, - "interfaceIndex": 2, "interfaceName": "r1-eth0" } ], diff --git a/tests/topotests/isis-topo1/r2/r2_route.json b/tests/topotests/isis-topo1/r2/r2_route.json index 3b14f160c0..fe2de05734 100644 --- a/tests/topotests/isis-topo1/r2/r2_route.json +++ b/tests/topotests/isis-topo1/r2/r2_route.json @@ -8,7 +8,6 @@ "active": true, "afi": "ipv4", "fib": true, - "interfaceIndex": 2, "interfaceName": "r2-eth0", "ip": "10.0.21.1" } @@ -25,7 +24,6 @@ "nexthops": [ { "afi": "ipv4", - "interfaceIndex": 2, "interfaceName": "r2-eth0", "ip": "10.0.21.1" } @@ -39,7 +37,6 @@ "active": true, "directlyConnected": true, "fib": true, - "interfaceIndex": 2, "interfaceName": "r2-eth0" } ], @@ -55,7 +52,6 @@ "active": true, "directlyConnected": true, "fib": true, - "interfaceIndex": 1, "interfaceName": "lo" } ], @@ -73,7 +69,6 @@ "active": true, "afi": "ipv4", "fib": true, - "interfaceIndex": 2, "interfaceName": "r2-eth0", "ip": "10.0.21.1" } diff --git a/tests/topotests/isis-topo1/r2/r2_route6.json b/tests/topotests/isis-topo1/r2/r2_route6.json index 49477a7ad3..78c31b3cc5 100644 --- a/tests/topotests/isis-topo1/r2/r2_route6.json +++ b/tests/topotests/isis-topo1/r2/r2_route6.json @@ -6,7 +6,6 @@ "active": true, "directlyConnected": true, "fib": true, - "interfaceIndex": 2, "interfaceName": "r2-eth0" } ], @@ -24,7 +23,6 @@ "active": true, "afi": "ipv6", "fib": true, - "interfaceIndex": 2, "interfaceName": "r2-eth0" } ], @@ -40,7 +38,6 @@ "active": true, "directlyConnected": true, "fib": true, - "interfaceIndex": 1, "interfaceName": "lo" } ], @@ -58,7 +55,6 @@ "active": true, "afi": "ipv6", "fib": true, - "interfaceIndex": 2, "interfaceName": "r2-eth0" } ], diff --git a/tests/topotests/isis-topo1/r3/r3_route.json b/tests/topotests/isis-topo1/r3/r3_route.json index d688e63ba1..1f0fcdfcd6 100644 --- a/tests/topotests/isis-topo1/r3/r3_route.json +++ b/tests/topotests/isis-topo1/r3/r3_route.json @@ -6,7 +6,6 @@ "nexthops": [ { "afi": "ipv4", - "interfaceIndex": 3, "interfaceName": "r3-eth1", "ip": "10.0.10.1" } @@ -20,7 +19,6 @@ "active": true, "directlyConnected": true, "fib": true, - "interfaceIndex": 3, "interfaceName": "r3-eth1" } ], @@ -38,7 +36,6 @@ "active": true, "afi": "ipv4", "fib": true, - "interfaceIndex": 3, "interfaceName": "r3-eth1", "ip": "10.0.10.1" } @@ -55,7 +52,6 @@ "nexthops": [ { "afi": "ipv4", - "interfaceIndex": 2, "interfaceName": "r3-eth0", "ip": "10.0.20.2" } @@ -69,7 +65,6 @@ "active": true, "directlyConnected": true, "fib": true, - "interfaceIndex": 2, "interfaceName": "r3-eth0" } ], @@ -87,7 +82,6 @@ "active": true, "afi": "ipv4", "fib": true, - "interfaceIndex": 3, "interfaceName": "r3-eth1", "ip": "10.0.10.1" } @@ -106,7 +100,6 @@ "active": true, "afi": "ipv4", "fib": true, - "interfaceIndex": 2, "interfaceName": "r3-eth0", "ip": "10.0.20.2" } @@ -123,7 +116,6 @@ "active": true, "directlyConnected": true, "fib": true, - "interfaceIndex": 1, "interfaceName": "lo" } ], @@ -141,7 +133,6 @@ "active": true, "afi": "ipv4", "fib": true, - "interfaceIndex": 3, "interfaceName": "r3-eth1", "ip": "10.0.10.1" } @@ -160,7 +151,6 @@ "active": true, "afi": "ipv4", "fib": true, - "interfaceIndex": 3, "interfaceName": "r3-eth1", "ip": "10.0.10.1" } diff --git a/tests/topotests/isis-topo1/r3/r3_route6.json b/tests/topotests/isis-topo1/r3/r3_route6.json index 7bb2be0aef..41040240ea 100644 --- a/tests/topotests/isis-topo1/r3/r3_route6.json +++ b/tests/topotests/isis-topo1/r3/r3_route6.json @@ -6,7 +6,6 @@ "active": true, "directlyConnected": true, "fib": true, - "interfaceIndex": 2, "interfaceName": "r3-eth0" } ], @@ -24,7 +23,6 @@ "active": true, "afi": "ipv6", "fib": true, - "interfaceIndex": 3, "interfaceName": "r3-eth1" } ], @@ -40,7 +38,6 @@ "active": true, "directlyConnected": true, "fib": true, - "interfaceIndex": 3, "interfaceName": "r3-eth1" } ], @@ -58,7 +55,6 @@ "active": true, "afi": "ipv6", "fib": true, - "interfaceIndex": 3, "interfaceName": "r3-eth1" } ], @@ -76,7 +72,6 @@ "active": true, "afi": "ipv6", "fib": true, - "interfaceIndex": 2, "interfaceName": "r3-eth0" } ], @@ -92,7 +87,6 @@ "active": true, "directlyConnected": true, "fib": true, - "interfaceIndex": 1, "interfaceName": "lo" } ], @@ -110,7 +104,6 @@ "active": true, "afi": "ipv6", "fib": true, - "interfaceIndex": 3, "interfaceName": "r3-eth1" } ], @@ -128,7 +121,6 @@ "active": true, "afi": "ipv6", "fib": true, - "interfaceIndex": 3, "interfaceName": "r3-eth1" } ], diff --git a/tests/topotests/isis-topo1/r4/r4_route.json b/tests/topotests/isis-topo1/r4/r4_route.json index 282565a273..597e953c09 100644 --- a/tests/topotests/isis-topo1/r4/r4_route.json +++ b/tests/topotests/isis-topo1/r4/r4_route.json @@ -6,7 +6,6 @@ "active": true, "directlyConnected": true, "fib": true, - "interfaceIndex": 3, "interfaceName": "r4-eth1" } ], @@ -22,7 +21,6 @@ "nexthops": [ { "afi": "ipv4", - "interfaceIndex": 2, "interfaceName": "r4-eth0", "ip": "10.0.21.2" } @@ -36,7 +34,6 @@ "active": true, "directlyConnected": true, "fib": true, - "interfaceIndex": 2, "interfaceName": "r4-eth0" } ], @@ -54,7 +51,6 @@ "active": true, "afi": "ipv4", "fib": true, - "interfaceIndex": 2, "interfaceName": "r4-eth0", "ip": "10.0.21.2" } @@ -71,7 +67,6 @@ "active": true, "directlyConnected": true, "fib": true, - "interfaceIndex": 1, "interfaceName": "lo" } ], diff --git a/tests/topotests/isis-topo1/r4/r4_route6.json b/tests/topotests/isis-topo1/r4/r4_route6.json index 8f52bcea2c..c0ace9a193 100644 --- a/tests/topotests/isis-topo1/r4/r4_route6.json +++ b/tests/topotests/isis-topo1/r4/r4_route6.json @@ -8,7 +8,6 @@ "active": true, "afi": "ipv6", "fib": true, - "interfaceIndex": 3, "interfaceName": "r4-eth1" } ], @@ -24,7 +23,6 @@ "active": true, "directlyConnected": true, "fib": true, - "interfaceIndex": 2, "interfaceName": "r4-eth0" } ], @@ -42,7 +40,6 @@ "active": true, "afi": "ipv6", "fib": true, - "interfaceIndex": 3, "interfaceName": "r4-eth1" } ], @@ -58,7 +55,6 @@ "active": true, "directlyConnected": true, "fib": true, - "interfaceIndex": 3, "interfaceName": "r4-eth1" } ], @@ -76,7 +72,6 @@ "active": true, "afi": "ipv6", "fib": true, - "interfaceIndex": 2, "interfaceName": "r4-eth0" } ], @@ -94,7 +89,6 @@ "active": true, "afi": "ipv6", "fib": true, - "interfaceIndex": 3, "interfaceName": "r4-eth1" } ], @@ -110,7 +104,6 @@ "active": true, "directlyConnected": true, "fib": true, - "interfaceIndex": 1, "interfaceName": "lo" } ], @@ -128,7 +121,6 @@ "active": true, "afi": "ipv6", "fib": true, - "interfaceIndex": 3, "interfaceName": "r4-eth1" } ], diff --git a/tests/topotests/isis-topo1/r5/r5_route.json b/tests/topotests/isis-topo1/r5/r5_route.json index 5bdde09f1b..cca844b27c 100644 --- a/tests/topotests/isis-topo1/r5/r5_route.json +++ b/tests/topotests/isis-topo1/r5/r5_route.json @@ -6,7 +6,6 @@ "nexthops": [ { "afi": "ipv4", - "interfaceIndex": 2, "interfaceName": "r5-eth0", "ip": "10.0.10.2" } @@ -20,7 +19,6 @@ "active": true, "directlyConnected": true, "fib": true, - "interfaceIndex": 2, "interfaceName": "r5-eth0" } ], @@ -36,7 +34,6 @@ "nexthops": [ { "afi": "ipv4", - "interfaceIndex": 3, "interfaceName": "r5-eth1", "ip": "10.0.11.2" } @@ -50,7 +47,6 @@ "active": true, "directlyConnected": true, "fib": true, - "interfaceIndex": 3, "interfaceName": "r5-eth1" } ], @@ -68,7 +64,6 @@ "active": true, "afi": "ipv4", "fib": true, - "interfaceIndex": 2, "interfaceName": "r5-eth0", "ip": "10.0.10.2" } @@ -87,7 +82,6 @@ "active": true, "afi": "ipv4", "fib": true, - "interfaceIndex": 3, "interfaceName": "r5-eth1", "ip": "10.0.11.2" } @@ -106,7 +100,6 @@ "active": true, "afi": "ipv4", "fib": true, - "interfaceIndex": 2, "interfaceName": "r5-eth0", "ip": "10.0.10.2" } @@ -125,7 +118,6 @@ "active": true, "afi": "ipv4", "fib": true, - "interfaceIndex": 3, "interfaceName": "r5-eth1", "ip": "10.0.11.2" } @@ -142,7 +134,6 @@ "active": true, "directlyConnected": true, "fib": true, - "interfaceIndex": 1, "interfaceName": "lo" } ], diff --git a/tests/topotests/isis-topo1/r5/r5_route6.json b/tests/topotests/isis-topo1/r5/r5_route6.json index 694d8c8814..b9468764d4 100644 --- a/tests/topotests/isis-topo1/r5/r5_route6.json +++ b/tests/topotests/isis-topo1/r5/r5_route6.json @@ -6,7 +6,6 @@ "active": true, "directlyConnected": true, "fib": true, - "interfaceIndex": 2, "interfaceName": "r5-eth0" } ], @@ -22,7 +21,6 @@ "active": true, "directlyConnected": true, "fib": true, - "interfaceIndex": 3, "interfaceName": "r5-eth1" } ], @@ -40,7 +38,6 @@ "active": true, "afi": "ipv6", "fib": true, - "interfaceIndex": 2, "interfaceName": "r5-eth0" } ], @@ -58,7 +55,6 @@ "active": true, "afi": "ipv6", "fib": true, - "interfaceIndex": 3, "interfaceName": "r5-eth1" } ], @@ -76,7 +72,6 @@ "active": true, "afi": "ipv6", "fib": true, - "interfaceIndex": 2, "interfaceName": "r5-eth0" } ], @@ -94,7 +89,6 @@ "active": true, "afi": "ipv6", "fib": true, - "interfaceIndex": 3, "interfaceName": "r5-eth1" } ], @@ -110,7 +104,6 @@ "active": true, "directlyConnected": true, "fib": true, - "interfaceIndex": 1, "interfaceName": "lo" } ], diff --git a/tests/topotests/isis-topo1/test_isis_topo1.py b/tests/topotests/isis-topo1/test_isis_topo1.py index 941f917c6b..6b1d9a8964 100644 --- a/tests/topotests/isis-topo1/test_isis_topo1.py +++ b/tests/topotests/isis-topo1/test_isis_topo1.py @@ -36,7 +36,7 @@ import time CWD = os.path.dirname(os.path.realpath(__file__)) -sys.path.append(os.path.join(CWD, '../')) +sys.path.append(os.path.join(CWD, "../")) # pylint: disable=C0413 from lib import topotest @@ -48,6 +48,7 @@ class ISISTopo1(Topo): "Simple two layer ISIS topology" + def build(self, *_args, **_opts): "Build function" tgen = get_topogen(self) @@ -61,27 +62,27 @@ def build(self, *_args, **_opts): # \ / # r5 for routern in range(1, 6): - tgen.add_router('r{}'.format(routern)) + tgen.add_router("r{}".format(routern)) # r1 <- sw1 -> r3 - sw = tgen.add_switch('sw1') - sw.add_link(tgen.gears['r1']) - sw.add_link(tgen.gears['r3']) + sw = tgen.add_switch("sw1") + sw.add_link(tgen.gears["r1"]) + sw.add_link(tgen.gears["r3"]) # r2 <- sw2 -> r4 - sw = tgen.add_switch('sw2') - sw.add_link(tgen.gears['r2']) - sw.add_link(tgen.gears['r4']) + sw = tgen.add_switch("sw2") + sw.add_link(tgen.gears["r2"]) + sw.add_link(tgen.gears["r4"]) # r3 <- sw3 -> r5 - sw = tgen.add_switch('sw3') - sw.add_link(tgen.gears['r3']) - sw.add_link(tgen.gears['r5']) + sw = tgen.add_switch("sw3") + sw.add_link(tgen.gears["r3"]) + sw.add_link(tgen.gears["r5"]) # r4 <- sw4 -> r5 - sw = tgen.add_switch('sw4') - sw.add_link(tgen.gears['r4']) - sw.add_link(tgen.gears['r5']) + sw = tgen.add_switch("sw4") + sw.add_link(tgen.gears["r4"]) + sw.add_link(tgen.gears["r5"]) def setup_module(mod): @@ -92,12 +93,10 @@ def setup_module(mod): # For all registered routers, load the zebra configuration file for rname, router in tgen.routers().iteritems(): router.load_config( - TopoRouter.RD_ZEBRA, - os.path.join(CWD, '{}/zebra.conf'.format(rname)) + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) ) router.load_config( - TopoRouter.RD_ISIS, - os.path.join(CWD, '{}/isisd.conf'.format(rname)) + TopoRouter.RD_ISIS, os.path.join(CWD, "{}/isisd.conf".format(rname)) ) # After loading the configurations, this function loads configured daemons. @@ -105,12 +104,12 @@ def setup_module(mod): has_version_20 = False for router in tgen.routers().values(): - if router.has_version('<', '3'): + if router.has_version("<", "3"): has_version_20 = True if has_version_20: - logger.info('Skipping ISIS tests for FRR 2.0') - tgen.set_error('ISIS has convergence problems with IPv6') + logger.info("Skipping ISIS tests for FRR 2.0") + tgen.set_error("ISIS has convergence problems with IPv6") def teardown_module(mod): @@ -136,7 +135,7 @@ def test_isis_convergence(): # ) for rname, router in tgen.routers().iteritems(): - filename = '{0}/{1}/{1}_topology.json'.format(CWD, rname) + filename = "{0}/{1}/{1}_topology.json".format(CWD, rname) expected = json.loads(open(filename).read()) def compare_isis_topology(router, expected): @@ -145,9 +144,8 @@ def compare_isis_topology(router, expected): return topotest.json_cmp(actual, expected) test_func = functools.partial(compare_isis_topology, router, expected) - (result, diff) = topotest.run_and_expect(test_func, None, - wait=0.5, count=120) - assert result, 'ISIS did not converge on {}:\n{}'.format(rname, diff) + (result, diff) = topotest.run_and_expect(test_func, None, wait=0.5, count=120) + assert result, "ISIS did not converge on {}:\n{}".format(rname, diff) def test_isis_route_installation(): @@ -157,24 +155,24 @@ def test_isis_route_installation(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - logger.info('Checking routers for installed ISIS routes') + logger.info("Checking routers for installed ISIS routes") # Check for routes in 'show ip route json' for rname, router in tgen.routers().iteritems(): - filename = '{0}/{1}/{1}_route.json'.format(CWD, rname) - expected = json.loads(open(filename, 'r').read()) - actual = router.vtysh_cmd('show ip route json', isjson=True) + filename = "{0}/{1}/{1}_route.json".format(CWD, rname) + expected = json.loads(open(filename, "r").read()) + actual = router.vtysh_cmd("show ip route json", isjson=True) # Older FRR versions don't list interfaces in some ISIS routes - if router.has_version('<', '3.1'): + if router.has_version("<", "3.1"): for network, routes in expected.iteritems(): for route in routes: - if route['protocol'] != 'isis': + if route["protocol"] != "isis": continue - for nexthop in route['nexthops']: - nexthop.pop('interfaceIndex', None) - nexthop.pop('interfaceName', None) + for nexthop in route["nexthops"]: + nexthop.pop("interfaceIndex", None) + nexthop.pop("interfaceName", None) assertmsg = "Router '{}' routes mismatch".format(rname) assert topotest.json_cmp(actual, expected) is None, assertmsg @@ -187,19 +185,19 @@ def test_isis_linux_route_installation(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - logger.info('Checking routers for installed ISIS routes in OS') + logger.info("Checking routers for installed ISIS routes in OS") # Check for routes in `ip route` for rname, router in tgen.routers().iteritems(): - filename = '{0}/{1}/{1}_route_linux.json'.format(CWD, rname) - expected = json.loads(open(filename, 'r').read()) + filename = "{0}/{1}/{1}_route_linux.json".format(CWD, rname) + expected = json.loads(open(filename, "r").read()) actual = topotest.ip4_route(router) # Older FRR versions install routes using different proto - if router.has_version('<', '3.1'): + if router.has_version("<", "3.1"): for network, netoptions in expected.iteritems(): - if 'proto' in netoptions and netoptions['proto'] == '187': - netoptions['proto'] = 'zebra' + if "proto" in netoptions and netoptions["proto"] == "187": + netoptions["proto"] = "zebra" assertmsg = "Router '{}' OS routes mismatch".format(rname) assert topotest.json_cmp(actual, expected) is None, assertmsg @@ -212,27 +210,27 @@ def test_isis_route6_installation(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - logger.info('Checking routers for installed ISIS IPv6 routes') + logger.info("Checking routers for installed ISIS IPv6 routes") # Check for routes in 'show ip route json' for rname, router in tgen.routers().iteritems(): - filename = '{0}/{1}/{1}_route6.json'.format(CWD, rname) - expected = json.loads(open(filename, 'r').read()) - actual = router.vtysh_cmd('show ipv6 route json', isjson=True) + filename = "{0}/{1}/{1}_route6.json".format(CWD, rname) + expected = json.loads(open(filename, "r").read()) + actual = router.vtysh_cmd("show ipv6 route json", isjson=True) # Older FRR versions don't list interfaces in some ISIS routes - if router.has_version('<', '3.1'): + if router.has_version("<", "3.1"): for network, routes in expected.iteritems(): for route in routes: # Older versions display different metrics for IPv6 routes - route.pop('metric', None) + route.pop("metric", None) - if route['protocol'] != 'isis': + if route["protocol"] != "isis": continue - for nexthop in route['nexthops']: - nexthop.pop('interfaceIndex', None) - nexthop.pop('interfaceName', None) + for nexthop in route["nexthops"]: + nexthop.pop("interfaceIndex", None) + nexthop.pop("interfaceName", None) assertmsg = "Router '{}' routes mismatch".format(rname) assert topotest.json_cmp(actual, expected) is None, assertmsg @@ -245,19 +243,19 @@ def test_isis_linux_route6_installation(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - logger.info('Checking routers for installed ISIS IPv6 routes in OS') + logger.info("Checking routers for installed ISIS IPv6 routes in OS") # Check for routes in `ip route` for rname, router in tgen.routers().iteritems(): - filename = '{0}/{1}/{1}_route6_linux.json'.format(CWD, rname) - expected = json.loads(open(filename, 'r').read()) + filename = "{0}/{1}/{1}_route6_linux.json".format(CWD, rname) + expected = json.loads(open(filename, "r").read()) actual = topotest.ip6_route(router) # Older FRR versions install routes using different proto - if router.has_version('<', '3.1'): + if router.has_version("<", "3.1"): for network, netoptions in expected.iteritems(): - if 'proto' in netoptions and netoptions['proto'] == '187': - netoptions['proto'] = 'zebra' + if "proto" in netoptions and netoptions["proto"] == "187": + netoptions["proto"] = "zebra" assertmsg = "Router '{}' OS routes mismatch".format(rname) assert topotest.json_cmp(actual, expected) is None, assertmsg @@ -267,12 +265,12 @@ def test_memory_leak(): "Run the memory leak test and report results." tgen = get_topogen() if not tgen.is_memleak_enabled(): - pytest.skip('Memory leak test/report is disabled') + pytest.skip("Memory leak test/report is disabled") tgen.report_memory_leaks() -if __name__ == '__main__': +if __name__ == "__main__": args = ["-s"] + sys.argv[1:] sys.exit(pytest.main(args)) @@ -296,8 +294,11 @@ def dict_merge(dct, merge_dct): https://gist.github.com/angstwad/bf22d1822c38a92ec0a9 """ for k, v in merge_dct.iteritems(): - if (k in dct and isinstance(dct[k], dict) - and isinstance(merge_dct[k], collections.Mapping)): + if ( + k in dct + and isinstance(dct[k], dict) + and isinstance(merge_dct[k], collections.Mapping) + ): dict_merge(dct[k], merge_dct[k]) else: dct[k] = merge_dct[k] @@ -316,59 +317,59 @@ def parse_topology(lines, level): if area_match: area = area_match.group(1) if area not in areas: - areas[area] = { - level: { - 'ipv4': [], - 'ipv6': [] - } - } + areas[area] = {level: {"ipv4": [], "ipv6": []}} ipv = None continue elif area is None: continue if re.match(r"IS\-IS paths to level-. routers that speak IPv6", line): - ipv = 'ipv6' + ipv = "ipv6" continue if re.match(r"IS\-IS paths to level-. routers that speak IP", line): - ipv = 'ipv4' + ipv = "ipv4" continue - item_match = re.match( - r"([^ ]+) ([^ ]+) ([^ ]+) ([^ ]+) ([^ ]+) ([^ ]+)", line) + item_match = re.match(r"([^ ]+) ([^ ]+) ([^ ]+) ([^ ]+) ([^ ]+) ([^ ]+)", line) if item_match is not None: # Skip header - if (item_match.group(1) == 'Vertex' and - item_match.group(2) == 'Type' and - item_match.group(3) == 'Metric' and - item_match.group(4) == 'Next-Hop' and - item_match.group(5) == 'Interface' and - item_match.group(6) == 'Parent'): + if ( + item_match.group(1) == "Vertex" + and item_match.group(2) == "Type" + and item_match.group(3) == "Metric" + and item_match.group(4) == "Next-Hop" + and item_match.group(5) == "Interface" + and item_match.group(6) == "Parent" + ): continue - areas[area][level][ipv].append({ - 'vertex': item_match.group(1), - 'type': item_match.group(2), - 'metric': item_match.group(3), - 'next-hop': item_match.group(4), - 'interface': item_match.group(5), - 'parent': item_match.group(6), - }) + areas[area][level][ipv].append( + { + "vertex": item_match.group(1), + "type": item_match.group(2), + "metric": item_match.group(3), + "next-hop": item_match.group(4), + "interface": item_match.group(5), + "parent": item_match.group(6), + } + ) continue item_match = re.match(r"([^ ]+) ([^ ]+) ([^ ]+) ([^ ]+)", line) if item_match is not None: - areas[area][level][ipv].append({ - 'vertex': item_match.group(1), - 'type': item_match.group(2), - 'metric': item_match.group(3), - 'parent': item_match.group(4), - }) + areas[area][level][ipv].append( + { + "vertex": item_match.group(1), + "type": item_match.group(2), + "metric": item_match.group(3), + "parent": item_match.group(4), + } + ) continue item_match = re.match(r"([^ ]+)", line) if item_match is not None: - areas[area][level][ipv].append({'vertex': item_match.group(1)}) + areas[area][level][ipv].append({"vertex": item_match.group(1)}) continue return areas @@ -410,14 +411,14 @@ def show_isis_topology(router): } """ l1out = topotest.normalize_text( - router.vtysh_cmd('show isis topology level-1') + router.vtysh_cmd("show isis topology level-1") ).splitlines() l2out = topotest.normalize_text( - router.vtysh_cmd('show isis topology level-2') + router.vtysh_cmd("show isis topology level-2") ).splitlines() - l1 = parse_topology(l1out, 'level-1') - l2 = parse_topology(l2out, 'level-2') + l1 = parse_topology(l1out, "level-1") + l2 = parse_topology(l2out, "level-2") dict_merge(l1, l2) return l1 diff --git a/tests/topotests/ldp-oc-acl-topo1/r1/ldpd.conf b/tests/topotests/ldp-oc-acl-topo1/r1/ldpd.conf new file mode 100644 index 0000000000..85bb970fdf --- /dev/null +++ b/tests/topotests/ldp-oc-acl-topo1/r1/ldpd.conf @@ -0,0 +1,25 @@ +hostname r1 +log file ldpd.log +! +debug mpls ldp zebra +debug mpls ldp event +debug mpls ldp errors +debug mpls ldp messages recv +debug mpls ldp messages sent +debug mpls ldp discovery hello recv +debug mpls ldp discovery hello sent +! +mpls ldp + router-id 1.1.1.1 + ordered-control + ! + address-family ipv4 + discovery transport-address 1.1.1.1 + label local allocate host-routes + ! + interface r1-eth0 + ! + ! +! +line vty +! diff --git a/tests/topotests/ldp-oc-acl-topo1/r1/ospfd.conf b/tests/topotests/ldp-oc-acl-topo1/r1/ospfd.conf new file mode 100644 index 0000000000..87d5703d9e --- /dev/null +++ b/tests/topotests/ldp-oc-acl-topo1/r1/ospfd.conf @@ -0,0 +1,11 @@ +hostname r1 +log file ospfd.log +! +router ospf + router-id 1.1.1.1 + network 0.0.0.0/0 area 0 +! +int r1-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/ldp-oc-acl-topo1/r1/show_ip_ospf_neighbor.json b/tests/topotests/ldp-oc-acl-topo1/r1/show_ip_ospf_neighbor.json new file mode 100644 index 0000000000..2c493173f5 --- /dev/null +++ b/tests/topotests/ldp-oc-acl-topo1/r1/show_ip_ospf_neighbor.json @@ -0,0 +1,12 @@ +{ + "neighbors":{ + "2.2.2.2":[ + { + "priority":1, + "state":"Full\/DR", + "address":"10.0.1.2", + "ifaceName":"r1-eth0:10.0.1.1" + } + ] + } +} diff --git a/tests/topotests/ldp-oc-acl-topo1/r1/show_ip_route.ref b/tests/topotests/ldp-oc-acl-topo1/r1/show_ip_route.ref new file mode 100644 index 0000000000..2131668fc3 --- /dev/null +++ b/tests/topotests/ldp-oc-acl-topo1/r1/show_ip_route.ref @@ -0,0 +1,160 @@ +{ + "1.1.1.1/32":[ + { + "prefix":"1.1.1.1/32", + "protocol":"ospf", + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"lo", + "active":true + } + ] + }, + { + "prefix":"1.1.1.1/32", + "protocol":"connected", + "selected":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceName":"lo", + "active":true + } + ] + } + ], + "2.2.2.2/32":[ + { + "prefix":"2.2.2.2/32", + "protocol":"ospf", + "selected":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true + } + ] + } + ], + "3.3.3.3/32":[ + { + "prefix":"3.3.3.3/32", + "protocol":"ospf", + "selected":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true + } + ] + } + ], + "4.4.4.4/32":[ + { + "prefix":"4.4.4.4/32", + "protocol":"ospf", + "selected":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true + } + ] + } + ], + "10.0.1.0/24":[ + { + "prefix":"10.0.1.0/24", + "protocol":"ospf", + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"r1-eth0", + "active":true + } + ] + }, + { + "prefix":"10.0.1.0/24", + "protocol":"connected", + "selected":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceName":"r1-eth0", + "active":true + } + ] + } + ], + "10.0.2.0/24":[ + { + "prefix":"10.0.2.0/24", + "protocol":"ospf", + "selected":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true + } + ] + } + ], + "10.0.3.0/24":[ + { + "prefix":"10.0.3.0/24", + "protocol":"ospf", + "selected":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true + } + ] + } + ], + "123.0.1.0/24":[ + { + "prefix":"123.0.1.0/24", + "protocol":"ospf", + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"r1-eth0", + "active":true + } + ] + }, + { + "prefix":"123.0.1.0/24", + "protocol":"connected", + "selected":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceName":"r1-eth0", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/ldp-oc-acl-topo1/r1/show_ldp_all_binding.ref b/tests/topotests/ldp-oc-acl-topo1/r1/show_ldp_all_binding.ref new file mode 100644 index 0000000000..99a59668f8 --- /dev/null +++ b/tests/topotests/ldp-oc-acl-topo1/r1/show_ldp_all_binding.ref @@ -0,0 +1,61 @@ +{ + "bindings":[ + { + "addressFamily":"ipv4", + "prefix":"1.1.1.1/32", + "neighborId":"0.0.0.0", + "localLabel":"imp-null", + "remoteLabel":"-", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"2.2.2.2/32", + "neighborId":"2.2.2.2", + "remoteLabel":"imp-null", + "inUse":1 + }, + { + "addressFamily":"ipv4", + "prefix":"3.3.3.3/32", + "neighborId":"2.2.2.2", + "inUse":1 + }, + { + "addressFamily":"ipv4", + "prefix":"4.4.4.4/32", + "neighborId":"0.0.0.0", + "remoteLabel":"-", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"10.0.1.0/24", + "neighborId":"2.2.2.2", + "localLabel":"imp-null", + "remoteLabel":"imp-null", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"10.0.2.0/24", + "neighborId":"2.2.2.2", + "remoteLabel":"imp-null", + "inUse":1 + }, + { + "addressFamily":"ipv4", + "prefix":"10.0.3.0/24", + "neighborId":"2.2.2.2", + "inUse":1 + }, + { + "addressFamily":"ipv4", + "prefix":"123.0.1.0/24", + "neighborId":"0.0.0.0", + "localLabel":"imp-null", + "remoteLabel":"-", + "inUse":0 + } + ] +} diff --git a/tests/topotests/ldp-oc-acl-topo1/r1/show_ldp_binding.ref b/tests/topotests/ldp-oc-acl-topo1/r1/show_ldp_binding.ref new file mode 100644 index 0000000000..ccc8413646 --- /dev/null +++ b/tests/topotests/ldp-oc-acl-topo1/r1/show_ldp_binding.ref @@ -0,0 +1,55 @@ +{ + "bindings":[ + { + "addressFamily":"ipv4", + "prefix":"1.1.1.1/32", + "neighborId":"0.0.0.0", + "localLabel":"imp-null", + "remoteLabel":"-", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"2.2.2.2/32", + "neighborId":"2.2.2.2", + "remoteLabel":"imp-null", + "inUse":1 + }, + { + "addressFamily":"ipv4", + "prefix":"3.3.3.3/32", + "neighborId":"2.2.2.2", + "inUse":1 + }, + { + "addressFamily":"ipv4", + "prefix":"4.4.4.4/32", + "neighborId":"0.0.0.0", + "remoteLabel":"-", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"10.0.1.0/24", + "neighborId":"2.2.2.2", + "localLabel":"-", + "remoteLabel":"imp-null", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"10.0.2.0/24", + "neighborId":"2.2.2.2", + "localLabel":"-", + "remoteLabel":"imp-null", + "inUse":1 + }, + { + "addressFamily":"ipv4", + "prefix":"10.0.3.0/24", + "neighborId":"2.2.2.2", + "localLabel":"-", + "inUse":1 + } + ] +} diff --git a/tests/topotests/ldp-oc-acl-topo1/r1/show_ldp_discovery.ref b/tests/topotests/ldp-oc-acl-topo1/r1/show_ldp_discovery.ref new file mode 100644 index 0000000000..b349f4418f --- /dev/null +++ b/tests/topotests/ldp-oc-acl-topo1/r1/show_ldp_discovery.ref @@ -0,0 +1,11 @@ +{ + "adjacencies":[ + { + "addressFamily":"ipv4", + "neighborId":"2.2.2.2", + "type":"link", + "interface":"r1-eth0", + "helloHoldtime":15 + } + ] +} diff --git a/tests/topotests/ldp-oc-acl-topo1/r1/show_ldp_neighbor.ref b/tests/topotests/ldp-oc-acl-topo1/r1/show_ldp_neighbor.ref new file mode 100644 index 0000000000..4bff444a46 --- /dev/null +++ b/tests/topotests/ldp-oc-acl-topo1/r1/show_ldp_neighbor.ref @@ -0,0 +1,10 @@ +{ + "neighbors":[ + { + "addressFamily":"ipv4", + "neighborId":"2.2.2.2", + "state":"OPERATIONAL", + "transportAddress":"2.2.2.2" + } + ] +} diff --git a/tests/topotests/ldp-oc-acl-topo1/r1/zebra.conf b/tests/topotests/ldp-oc-acl-topo1/r1/zebra.conf new file mode 100644 index 0000000000..83aea46e64 --- /dev/null +++ b/tests/topotests/ldp-oc-acl-topo1/r1/zebra.conf @@ -0,0 +1,17 @@ +log file zebra.log +! +hostname r1 +! +interface lo + ip address 1.1.1.1/32 +! +interface r1-eth0 + description to sw0 + ip address 10.0.1.1/24 + ip address 123.0.1.1/24 +! +ip forwarding +! +! +line vty +! diff --git a/tests/topotests/ldp-oc-acl-topo1/r2/ldpd.conf b/tests/topotests/ldp-oc-acl-topo1/r2/ldpd.conf new file mode 100644 index 0000000000..e1a552c701 --- /dev/null +++ b/tests/topotests/ldp-oc-acl-topo1/r2/ldpd.conf @@ -0,0 +1,28 @@ +hostname r2 +log file ldpd.log +! +debug mpls ldp zebra +debug mpls ldp event +debug mpls ldp errors +debug mpls ldp messages recv +debug mpls ldp messages sent +debug mpls ldp discovery hello recv +debug mpls ldp discovery hello sent +! +mpls ldp + router-id 2.2.2.2 + ordered-control + ! + address-family ipv4 + discovery transport-address 2.2.2.2 + ! + interface r2-eth0 + ! + interface r2-eth1 + ! + interface r2-eth2 + ! + ! +! +line vty +! diff --git a/tests/topotests/ldp-oc-acl-topo1/r2/ospfd.conf b/tests/topotests/ldp-oc-acl-topo1/r2/ospfd.conf new file mode 100644 index 0000000000..51317202bb --- /dev/null +++ b/tests/topotests/ldp-oc-acl-topo1/r2/ospfd.conf @@ -0,0 +1,15 @@ +hostname r2 +log file ospfd.log +! +router ospf + router-id 2.2.2.2 + network 0.0.0.0/0 area 0 +! +int r2-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +int r2-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/ldp-oc-acl-topo1/r2/show_ip_ospf_neighbor.json b/tests/topotests/ldp-oc-acl-topo1/r2/show_ip_ospf_neighbor.json new file mode 100644 index 0000000000..55f12359e5 --- /dev/null +++ b/tests/topotests/ldp-oc-acl-topo1/r2/show_ip_ospf_neighbor.json @@ -0,0 +1,31 @@ +{ + "neighbors":{ + "1.1.1.1":[ + { + "priority":1, + "state":"Full\/Backup", + "address":"10.0.1.1", + "ifaceName":"r2-eth0:10.0.1.2", + "retransmitCounter":0, + "requestCounter":0, + "dbSummaryCounter":0 + } + ], + "3.3.3.3":[ + { + "priority":1, + "state":"Full\/Backup", + "address":"10.0.2.3", + "ifaceName":"r2-eth1:10.0.2.2" + } + ], + "4.4.4.4":[ + { + "priority":1, + "state":"Full\/DR", + "address":"10.0.2.4", + "ifaceName":"r2-eth1:10.0.2.2" + } + ] + } +} diff --git a/tests/topotests/ldp-oc-acl-topo1/r2/show_ip_route.ref b/tests/topotests/ldp-oc-acl-topo1/r2/show_ip_route.ref new file mode 100644 index 0000000000..4b1d31a5d3 --- /dev/null +++ b/tests/topotests/ldp-oc-acl-topo1/r2/show_ip_route.ref @@ -0,0 +1,198 @@ +{ + "1.1.1.1/32":[ + { + "prefix":"1.1.1.1/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "distance":110, + "metric":10, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceName":"r2-eth0", + "active":true + } + ] + } + ], + "2.2.2.2/32":[ + { + "prefix":"2.2.2.2/32", + "protocol":"ospf", + "distance":110, + "metric":0, + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"lo", + "active":true + } + ] + }, + { + "prefix":"2.2.2.2/32", + "protocol":"connected", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceName":"lo", + "active":true + } + ] + } + ], + "3.3.3.3/32":[ + { + "prefix":"3.3.3.3/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "distance":110, + "metric":10, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.3", + "afi":"ipv4", + "interfaceName":"r2-eth1", + "active":true + } + ] + } + ], + "4.4.4.4/32":[ + { + "prefix":"4.4.4.4/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "distance":110, + "metric":10, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"r2-eth1", + "active":true + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"ospf", + "distance":110, + "metric":10, + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"r2-eth0", + "active":true + } + ] + }, + { + "prefix":"10.0.1.0\/24", + "protocol":"connected", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceName":"r2-eth0", + "active":true + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"ospf", + "distance":110, + "metric":10, + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"r2-eth1", + "active":true + } + ] + }, + { + "prefix":"10.0.2.0\/24", + "protocol":"connected", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceName":"r2-eth1", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "distance":110, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.3", + "afi":"ipv4", + "interfaceName":"r2-eth1", + "active":true + } + ] + } + ], + "123.0.1.0\/24":[ + { + "prefix":"123.0.1.0\/24", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "distance":110, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceName":"r2-eth0", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/ldp-oc-acl-topo1/r2/show_ldp_all_binding.ref b/tests/topotests/ldp-oc-acl-topo1/r2/show_ldp_all_binding.ref new file mode 100644 index 0000000000..95fb847c1e --- /dev/null +++ b/tests/topotests/ldp-oc-acl-topo1/r2/show_ldp_all_binding.ref @@ -0,0 +1,63 @@ +{ + "bindings":[ + { + "addressFamily":"ipv4", + "prefix":"1.1.1.1/32", + "neighborId":"1.1.1.1", + "remoteLabel":"imp-null", + "inUse":1 + }, + { + "addressFamily":"ipv4", + "prefix":"2.2.2.2/32", + "neighborId":"0.0.0.0", + "localLabel":"imp-null", + "remoteLabel":"-", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"3.3.3.3/32", + "neighborId":"3.3.3.3", + "remoteLabel":"imp-null", + "inUse":1 + }, + { + "addressFamily":"ipv4", + "prefix":"4.4.4.4/32", + "neighborId":"0.0.0.0", + "remoteLabel":"-", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"10.0.1.0/24", + "neighborId":"1.1.1.1", + "localLabel":"imp-null", + "remoteLabel":"imp-null", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"10.0.2.0/24", + "neighborId":"3.3.3.3", + "localLabel":"imp-null", + "remoteLabel":"imp-null", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"10.0.3.0/24", + "neighborId":"3.3.3.3", + "remoteLabel":"imp-null", + "inUse":1 + }, + { + "addressFamily":"ipv4", + "prefix":"123.0.1.0/24", + "neighborId":"1.1.1.1", + "remoteLabel":"imp-null", + "inUse":1 + } + ] +} diff --git a/tests/topotests/ldp-oc-acl-topo1/r2/show_ldp_binding.ref b/tests/topotests/ldp-oc-acl-topo1/r2/show_ldp_binding.ref new file mode 100644 index 0000000000..ea32de3eda --- /dev/null +++ b/tests/topotests/ldp-oc-acl-topo1/r2/show_ldp_binding.ref @@ -0,0 +1,63 @@ +{ + "bindings":[ + { + "addressFamily":"ipv4", + "prefix":"1.1.1.1/32", + "neighborId":"1.1.1.1", + "remoteLabel":"imp-null", + "inUse":1 + }, + { + "addressFamily":"ipv4", + "prefix":"2.2.2.2/32", + "neighborId":"0.0.0.0", + "localLabel":"imp-null", + "remoteLabel":"-", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"3.3.3.3/32", + "neighborId":"3.3.3.3", + "remoteLabel":"imp-null", + "inUse":1 + }, + { + "addressFamily":"ipv4", + "prefix":"4.4.4.4/32", + "neighborId":"0.0.0.0", + "remoteLabel":"-", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"10.0.1.0/24", + "neighborId":"0.0.0.0", + "localLabel":"imp-null", + "remoteLabel":"-", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"10.0.2.0/24", + "neighborId":"3.3.3.3", + "localLabel":"imp-null", + "remoteLabel":"imp-null", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"10.0.3.0/24", + "neighborId":"3.3.3.3", + "remoteLabel":"imp-null", + "inUse":1 + }, + { + "addressFamily":"ipv4", + "prefix":"123.0.1.0/24", + "neighborId":"0.0.0.0", + "remoteLabel":"-", + "inUse":0 + } + ] +} diff --git a/tests/topotests/ldp-oc-acl-topo1/r2/show_ldp_discovery.ref b/tests/topotests/ldp-oc-acl-topo1/r2/show_ldp_discovery.ref new file mode 100644 index 0000000000..8129570082 --- /dev/null +++ b/tests/topotests/ldp-oc-acl-topo1/r2/show_ldp_discovery.ref @@ -0,0 +1,18 @@ +{ + "adjacencies":[ + { + "addressFamily":"ipv4", + "neighborId":"1.1.1.1", + "type":"link", + "interface":"r2-eth0", + "helloHoldtime":15 + }, + { + "addressFamily":"ipv4", + "neighborId":"3.3.3.3", + "type":"link", + "interface":"r2-eth1", + "helloHoldtime":15 + } + ] +} diff --git a/tests/topotests/ldp-oc-acl-topo1/r2/show_ldp_neighbor.ref b/tests/topotests/ldp-oc-acl-topo1/r2/show_ldp_neighbor.ref new file mode 100644 index 0000000000..eed35289ea --- /dev/null +++ b/tests/topotests/ldp-oc-acl-topo1/r2/show_ldp_neighbor.ref @@ -0,0 +1,16 @@ +{ + "neighbors":[ + { + "addressFamily":"ipv4", + "neighborId":"1.1.1.1", + "state":"OPERATIONAL", + "transportAddress":"1.1.1.1" + }, + { + "addressFamily":"ipv4", + "neighborId":"3.3.3.3", + "state":"OPERATIONAL", + "transportAddress":"3.3.3.3" + } + ] +} diff --git a/tests/topotests/ldp-oc-acl-topo1/r2/zebra.conf b/tests/topotests/ldp-oc-acl-topo1/r2/zebra.conf new file mode 100644 index 0000000000..1f1e3e391a --- /dev/null +++ b/tests/topotests/ldp-oc-acl-topo1/r2/zebra.conf @@ -0,0 +1,27 @@ +log file zebra.log +! +hostname r2 +! +interface lo + ip address 2.2.2.2/32 +! +interface r2-eth0 + description to sw0 + ip address 10.0.1.2/24 +! no link-detect +! +interface r2-eth1 + description to sw1 + ip address 10.0.2.2/24 +! no link-detect +! +interface r2-eths2 + description to sw2 + ip address 10.0.3.2/24 +! no link-detect +! +ip forwarding +! +! +line vty +! diff --git a/tests/topotests/ldp-oc-acl-topo1/r3/ldpd.conf b/tests/topotests/ldp-oc-acl-topo1/r3/ldpd.conf new file mode 100644 index 0000000000..4e66b140ac --- /dev/null +++ b/tests/topotests/ldp-oc-acl-topo1/r3/ldpd.conf @@ -0,0 +1,24 @@ +hostname r3 +log file ldpd.log +! +debug mpls ldp zebra +debug mpls ldp event +debug mpls ldp errors +debug mpls ldp messages recv +debug mpls ldp messages sent +debug mpls ldp discovery hello recv +debug mpls ldp discovery hello sent +! +mpls ldp + router-id 3.3.3.3 + ordered-control + ! + address-family ipv4 + discovery transport-address 3.3.3.3 + ! + interface r3-eth0 + ! + ! +! +line vty +! diff --git a/tests/topotests/ldp-oc-acl-topo1/r3/ospfd.conf b/tests/topotests/ldp-oc-acl-topo1/r3/ospfd.conf new file mode 100644 index 0000000000..4566976b7b --- /dev/null +++ b/tests/topotests/ldp-oc-acl-topo1/r3/ospfd.conf @@ -0,0 +1,12 @@ +hostname r3 +password 1 +log file ospfd.log +! +router ospf + router-id 3.3.3.3 + network 0.0.0.0/0 area 0 +! +int r3-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/ldp-oc-acl-topo1/r3/show_ip_ospf_neighbor.json b/tests/topotests/ldp-oc-acl-topo1/r3/show_ip_ospf_neighbor.json new file mode 100644 index 0000000000..24502ed813 --- /dev/null +++ b/tests/topotests/ldp-oc-acl-topo1/r3/show_ip_ospf_neighbor.json @@ -0,0 +1,20 @@ +{ + "neighbors":{ + "2.2.2.2":[ + { + "priority":1, + "state":"Full\/DROther", + "address":"10.0.2.2", + "ifaceName":"r3-eth0:10.0.2.3" + } + ], + "4.4.4.4":[ + { + "priority":1, + "state":"Full\/DR", + "address":"10.0.2.4", + "ifaceName":"r3-eth0:10.0.2.3" + } + ] + } +} diff --git a/tests/topotests/ldp-oc-acl-topo1/r3/show_ip_route.ref b/tests/topotests/ldp-oc-acl-topo1/r3/show_ip_route.ref new file mode 100644 index 0000000000..4d115caf3e --- /dev/null +++ b/tests/topotests/ldp-oc-acl-topo1/r3/show_ip_route.ref @@ -0,0 +1,198 @@ +{ + "1.1.1.1/32":[ + { + "prefix":"1.1.1.1/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "distance":110, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"r3-eth0", + "active":true + } + ] + } + ], + "2.2.2.2/32":[ + { + "prefix":"2.2.2.2/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "distance":110, + "metric":10, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"r3-eth0", + "active":true + } + ] + } + ], + "3.3.3.3/32":[ + { + "prefix":"3.3.3.3/32", + "protocol":"ospf", + "distance":110, + "metric":0, + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"lo", + "active":true + } + ] + }, + { + "prefix":"3.3.3.3/32", + "protocol":"connected", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceName":"lo", + "active":true + } + ] + } + ], + "4.4.4.4/32":[ + { + "prefix":"4.4.4.4/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "distance":110, + "metric":10, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"r3-eth0", + "active":true + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "distance":110, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"r3-eth0", + "active":true + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"ospf", + "distance":110, + "metric":10, + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"r3-eth0", + "active":true + } + ] + }, + { + "prefix":"10.0.2.0\/24", + "protocol":"connected", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceName":"r3-eth0", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"ospf", + "distance":110, + "metric":10, + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"r3-eth1", + "active":true + } + ] + }, + { + "prefix":"10.0.3.0\/24", + "protocol":"connected", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceName":"r3-eth1", + "active":true + } + ] + } + ], + "123.0.1.0\/24":[ + { + "prefix":"123.0.1.0\/24", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "distance":110, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"r3-eth0", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/ldp-oc-acl-topo1/r3/show_ldp_all_binding.ref b/tests/topotests/ldp-oc-acl-topo1/r3/show_ldp_all_binding.ref new file mode 100644 index 0000000000..100dd307ea --- /dev/null +++ b/tests/topotests/ldp-oc-acl-topo1/r3/show_ldp_all_binding.ref @@ -0,0 +1,61 @@ +{ + "bindings":[ + { + "addressFamily":"ipv4", + "prefix":"1.1.1.1/32", + "neighborId":"2.2.2.2", + "inUse":1 + }, + { + "addressFamily":"ipv4", + "prefix":"2.2.2.2/32", + "neighborId":"2.2.2.2", + "remoteLabel":"imp-null", + "inUse":1 + }, + { + "addressFamily":"ipv4", + "prefix":"3.3.3.3/32", + "neighborId":"0.0.0.0", + "localLabel":"imp-null", + "remoteLabel":"-", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"4.4.4.4/32", + "neighborId":"0.0.0.0", + "remoteLabel":"-", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"10.0.1.0/24", + "neighborId":"2.2.2.2", + "remoteLabel":"imp-null", + "inUse":1 + }, + { + "addressFamily":"ipv4", + "prefix":"10.0.2.0/24", + "neighborId":"2.2.2.2", + "localLabel":"imp-null", + "remoteLabel":"imp-null", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"10.0.3.0/24", + "neighborId":"0.0.0.0", + "localLabel":"imp-null", + "remoteLabel":"-", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"123.0.1.0/24", + "neighborId":"2.2.2.2", + "inUse":1 + } + ] +} diff --git a/tests/topotests/ldp-oc-acl-topo1/r3/show_ldp_binding.ref b/tests/topotests/ldp-oc-acl-topo1/r3/show_ldp_binding.ref new file mode 100644 index 0000000000..bb1b2b3023 --- /dev/null +++ b/tests/topotests/ldp-oc-acl-topo1/r3/show_ldp_binding.ref @@ -0,0 +1,62 @@ +{ + "bindings":[ + { + "addressFamily":"ipv4", + "prefix":"1.1.1.1/32", + "neighborId":"2.2.2.2", + "inUse":1 + }, + { + "addressFamily":"ipv4", + "prefix":"2.2.2.2/32", + "neighborId":"2.2.2.2", + "remoteLabel":"imp-null", + "inUse":1 + }, + { + "addressFamily":"ipv4", + "prefix":"3.3.3.3/32", + "neighborId":"0.0.0.0", + "localLabel":"imp-null", + "remoteLabel":"-", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"4.4.4.4/32", + "neighborId":"0.0.0.0", + "remoteLabel":"-", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"10.0.1.0/24", + "neighborId":"2.2.2.2", + "remoteLabel":"imp-null", + "inUse":1 + }, + { + "addressFamily":"ipv4", + "prefix":"10.0.2.0/24", + "neighborId":"2.2.2.2", + "localLabel":"imp-null", + "remoteLabel":"imp-null", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"10.0.3.0/24", + "neighborId":"0.0.0.0", + "localLabel":"imp-null", + "remoteLabel":"-", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"123.0.1.0/24", + "neighborId":"0.0.0.0", + "remoteLabel":"-", + "inUse":0 + } + ] +} diff --git a/tests/topotests/ldp-oc-acl-topo1/r3/show_ldp_discovery.ref b/tests/topotests/ldp-oc-acl-topo1/r3/show_ldp_discovery.ref new file mode 100644 index 0000000000..c3a07e7e38 --- /dev/null +++ b/tests/topotests/ldp-oc-acl-topo1/r3/show_ldp_discovery.ref @@ -0,0 +1,11 @@ +{ + "adjacencies":[ + { + "addressFamily":"ipv4", + "neighborId":"2.2.2.2", + "type":"link", + "interface":"r3-eth0", + "helloHoldtime":15 + } + ] +} diff --git a/tests/topotests/ldp-oc-acl-topo1/r3/show_ldp_neighbor.ref b/tests/topotests/ldp-oc-acl-topo1/r3/show_ldp_neighbor.ref new file mode 100644 index 0000000000..4bff444a46 --- /dev/null +++ b/tests/topotests/ldp-oc-acl-topo1/r3/show_ldp_neighbor.ref @@ -0,0 +1,10 @@ +{ + "neighbors":[ + { + "addressFamily":"ipv4", + "neighborId":"2.2.2.2", + "state":"OPERATIONAL", + "transportAddress":"2.2.2.2" + } + ] +} diff --git a/tests/topotests/ldp-oc-acl-topo1/r3/zebra.conf b/tests/topotests/ldp-oc-acl-topo1/r3/zebra.conf new file mode 100644 index 0000000000..234c215ddf --- /dev/null +++ b/tests/topotests/ldp-oc-acl-topo1/r3/zebra.conf @@ -0,0 +1,22 @@ +log file zebra.log +! +hostname r3 +! +interface lo + ip address 3.3.3.3/32 +! +interface r3-eth0 + description to sw1 + ip address 10.0.2.3/24 +! no link-detect +! +interface r3-eth1 + description to sw2 + ip address 10.0.3.3/24 +! no link-detect +! +ip forwarding +! +! +line vty +! diff --git a/tests/topotests/ldp-oc-acl-topo1/r4/ldpd.conf b/tests/topotests/ldp-oc-acl-topo1/r4/ldpd.conf new file mode 100644 index 0000000000..6b7d28f983 --- /dev/null +++ b/tests/topotests/ldp-oc-acl-topo1/r4/ldpd.conf @@ -0,0 +1,24 @@ +hostname r4 +log file ldpd.log +! +debug mpls ldp zebra +debug mpls ldp event +debug mpls ldp errors +debug mpls ldp messages recv +debug mpls ldp messages sent +debug mpls ldp discovery hello recv +debug mpls ldp discovery hello sent +! +mpls ldp + router-id 4.4.4.4 + ordered-control + ! + address-family ipv4 + discovery transport-address 4.4.4.4 + ! + !interface r4-eth0 + ! + ! +! +line vty +! diff --git a/tests/topotests/ldp-oc-acl-topo1/r4/ospfd.conf b/tests/topotests/ldp-oc-acl-topo1/r4/ospfd.conf new file mode 100644 index 0000000000..5aae885a12 --- /dev/null +++ b/tests/topotests/ldp-oc-acl-topo1/r4/ospfd.conf @@ -0,0 +1,11 @@ +hostname r4 +log file ospfd.log +! +router ospf + router-id 4.4.4.4 + network 0.0.0.0/0 area 0 +! +int r4-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/ldp-oc-acl-topo1/r4/show_ip_ospf_neighbor.json b/tests/topotests/ldp-oc-acl-topo1/r4/show_ip_ospf_neighbor.json new file mode 100644 index 0000000000..794410522d --- /dev/null +++ b/tests/topotests/ldp-oc-acl-topo1/r4/show_ip_ospf_neighbor.json @@ -0,0 +1,21 @@ + +{ + "neighbors":{ + "2.2.2.2":[ + { + "priority":1, + "state":"Full\/DROther", + "address":"10.0.2.2", + "ifaceName":"r4-eth0:10.0.2.4" + } + ], + "3.3.3.3":[ + { + "priority":1, + "state":"Full\/Backup", + "address":"10.0.2.3", + "ifaceName":"r4-eth0:10.0.2.4" + } + ] + } +} diff --git a/tests/topotests/ldp-oc-acl-topo1/r4/show_ip_route.ref b/tests/topotests/ldp-oc-acl-topo1/r4/show_ip_route.ref new file mode 100644 index 0000000000..223cbded84 --- /dev/null +++ b/tests/topotests/ldp-oc-acl-topo1/r4/show_ip_route.ref @@ -0,0 +1,186 @@ +{ + "1.1.1.1/32":[ + { + "prefix":"1.1.1.1/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "distance":110, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"r4-eth0", + "active":true + } + ] + } + ], + "2.2.2.2/32":[ + { + "prefix":"2.2.2.2/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "distance":110, + "metric":10, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"r4-eth0", + "active":true + } + ] + } + ], + "3.3.3.3/32":[ + { + "prefix":"3.3.3.3/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "distance":110, + "metric":10, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.3", + "afi":"ipv4", + "interfaceName":"r4-eth0", + "active":true + } + ] + } + ], + "4.4.4.4/32":[ + { + "prefix":"4.4.4.4/32", + "protocol":"ospf", + "distance":110, + "metric":0, + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"lo", + "active":true + } + ] + }, + { + "prefix":"4.4.4.4/32", + "protocol":"connected", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceName":"lo", + "active":true + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "distance":110, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"r4-eth0", + "active":true + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"ospf", + "distance":110, + "metric":10, + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"r4-eth0", + "active":true + } + ] + }, + { + "prefix":"10.0.2.0\/24", + "protocol":"connected", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceName":"r4-eth0", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "distance":110, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.3", + "afi":"ipv4", + "interfaceName":"r4-eth0", + "active":true + } + ] + } + ], + "123.0.1.0\/24":[ + { + "prefix":"123.0.1.0\/24", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "distance":110, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"r4-eth0", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/ldp-oc-acl-topo1/r4/show_ldp_all_binding.ref b/tests/topotests/ldp-oc-acl-topo1/r4/show_ldp_all_binding.ref new file mode 100644 index 0000000000..2a46c40346 --- /dev/null +++ b/tests/topotests/ldp-oc-acl-topo1/r4/show_ldp_all_binding.ref @@ -0,0 +1,68 @@ +{ + "bindings":[ + { + "addressFamily":"ipv4", + "prefix":"1.1.1.1/32", + "neighborId":"0.0.0.0", + "localLabel":"imp-null", + "remoteLabel":"-", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"2.2.2.2/32", + "neighborId":"0.0.0.0", + "localLabel":"imp-null", + "remoteLabel":"-", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"3.3.3.3/32", + "neighborId":"0.0.0.0", + "localLabel":"imp-null", + "remoteLabel":"-", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"4.4.4.4/32", + "neighborId":"0.0.0.0", + "localLabel":"imp-null", + "remoteLabel":"-", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"10.0.1.0/24", + "neighborId":"0.0.0.0", + "localLabel":"imp-null", + "remoteLabel":"-", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"10.0.2.0/24", + "neighborId":"0.0.0.0", + "localLabel":"imp-null", + "remoteLabel":"-", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"10.0.3.0/24", + "neighborId":"0.0.0.0", + "localLabel":"imp-null", + "remoteLabel":"-", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"123.0.1.0/24", + "neighborId":"0.0.0.0", + "localLabel":"imp-null", + "remoteLabel":"-", + "inUse":0 + } + ] +} diff --git a/tests/topotests/ldp-oc-acl-topo1/r4/show_ldp_binding.ref b/tests/topotests/ldp-oc-acl-topo1/r4/show_ldp_binding.ref new file mode 100644 index 0000000000..2a46c40346 --- /dev/null +++ b/tests/topotests/ldp-oc-acl-topo1/r4/show_ldp_binding.ref @@ -0,0 +1,68 @@ +{ + "bindings":[ + { + "addressFamily":"ipv4", + "prefix":"1.1.1.1/32", + "neighborId":"0.0.0.0", + "localLabel":"imp-null", + "remoteLabel":"-", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"2.2.2.2/32", + "neighborId":"0.0.0.0", + "localLabel":"imp-null", + "remoteLabel":"-", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"3.3.3.3/32", + "neighborId":"0.0.0.0", + "localLabel":"imp-null", + "remoteLabel":"-", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"4.4.4.4/32", + "neighborId":"0.0.0.0", + "localLabel":"imp-null", + "remoteLabel":"-", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"10.0.1.0/24", + "neighborId":"0.0.0.0", + "localLabel":"imp-null", + "remoteLabel":"-", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"10.0.2.0/24", + "neighborId":"0.0.0.0", + "localLabel":"imp-null", + "remoteLabel":"-", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"10.0.3.0/24", + "neighborId":"0.0.0.0", + "localLabel":"imp-null", + "remoteLabel":"-", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"123.0.1.0/24", + "neighborId":"0.0.0.0", + "localLabel":"imp-null", + "remoteLabel":"-", + "inUse":0 + } + ] +} diff --git a/tests/topotests/ldp-oc-acl-topo1/r4/show_ldp_discovery.ref b/tests/topotests/ldp-oc-acl-topo1/r4/show_ldp_discovery.ref new file mode 100644 index 0000000000..2c63c08510 --- /dev/null +++ b/tests/topotests/ldp-oc-acl-topo1/r4/show_ldp_discovery.ref @@ -0,0 +1,2 @@ +{ +} diff --git a/tests/topotests/ldp-oc-acl-topo1/r4/show_ldp_neighbor.ref b/tests/topotests/ldp-oc-acl-topo1/r4/show_ldp_neighbor.ref new file mode 100644 index 0000000000..2c63c08510 --- /dev/null +++ b/tests/topotests/ldp-oc-acl-topo1/r4/show_ldp_neighbor.ref @@ -0,0 +1,2 @@ +{ +} diff --git a/tests/topotests/ldp-oc-acl-topo1/r4/zebra.conf b/tests/topotests/ldp-oc-acl-topo1/r4/zebra.conf new file mode 100644 index 0000000000..7e291053e5 --- /dev/null +++ b/tests/topotests/ldp-oc-acl-topo1/r4/zebra.conf @@ -0,0 +1,17 @@ +log file zebra.log +! +hostname r4 +! +interface lo + ip address 4.4.4.4/32 +! +interface r4-eth0 + description to sw1 + ip address 10.0.2.4/24 +! no link-detect +! +ip forwarding +! +! +line vty +! diff --git a/tests/topotests/ldp-oc-acl-topo1/test_ldp_oc_acl_topo1.dot b/tests/topotests/ldp-oc-acl-topo1/test_ldp_oc_acl_topo1.dot new file mode 100644 index 0000000000..62058e3cb1 --- /dev/null +++ b/tests/topotests/ldp-oc-acl-topo1/test_ldp_oc_acl_topo1.dot @@ -0,0 +1,76 @@ +## Color coding: +######################### +## Main FRR: #f08080 red +## Switches: #d0e0d0 gray +## RIP: #19e3d9 Cyan +## RIPng: #fcb314 dark yellow +## OSPFv2: #32b835 Green +## OSPFv3: #19e3d9 Cyan +## ISIS IPv4 #fcb314 dark yellow +## ISIS IPv6 #9a81ec purple +## BGP IPv4 #eee3d3 beige +## BGP IPv6 #fdff00 yellow +##### Colors (see http://www.color-hex.com/) + +graph template { + label="Test Topology - LDP-OC 1"; + + # Routers + r1 [ + shape=doubleoctagon, + label="r1", + fillcolor="#f08080", + style=filled, + ]; + r2 [ + shape=doubleoctagon + label="r2", + fillcolor="#f08080", + style=filled, + ]; + r3 [ + shape=doubleoctagon + label="r3", + fillcolor="#f08080", + style=filled, + ]; + r4 [ + shape=doubleoctagon + label="r4", + fillcolor="#f08080", + style=filled, + ]; + + + # Switches + s0 [ + shape=oval, + label="10.0.1.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + s1 [ + shape=oval, + label="10.0.2.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + s2 [ + shape=oval, + label="10.0.3.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + + # Connections + + r1 -- s0 [label="eth0"]; + r2 -- s0 [label="eth0"]; + + r2 -- s1 [label="eth1"]; + r3 -- s1 [label="eth0"]; + r4 -- s1 [label="eth0"]; + + r2 -- s2 [label="eth2"]; + r3 -- s2 [label="eth1"]; +} diff --git a/tests/topotests/ldp-oc-acl-topo1/test_ldp_oc_acl_topo1.py b/tests/topotests/ldp-oc-acl-topo1/test_ldp_oc_acl_topo1.py new file mode 100755 index 0000000000..450d35e16c --- /dev/null +++ b/tests/topotests/ldp-oc-acl-topo1/test_ldp_oc_acl_topo1.py @@ -0,0 +1,261 @@ +#!/usr/bin/env python + +# +# test_ldp_oc_acl_topo1.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2020 by Volta Networks +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_ldp_oc_acl_topo1.py: Simple FRR/Quagga LDP Test + + +---------+ + | r1 | + | 1.1.1.1 | + +----+----+ + | .1 r1-eth0 + | + ~~~~~~~~~~~~~ + ~~ sw0 ~~ + ~~ 10.0.1.0/24 ~~ + ~~~~~~~~~~~~~ + |10.0.1.0/24 + | + | .2 r2-eth0 + +----+----+ + | r2 | + | 2.2.2.2 | + +--+---+--+ + r2-eth2 .2 | | .2 r2-eth1 + ______/ \______ + / \ + ~~~~~~~~~~~~~ ~~~~~~~~~~~~~ +~~ sw2 ~~ ~~ sw1 ~~ +~~ 10.0.3.0/24 ~~ ~~ 10.0.2.0/24 ~~ + ~~~~~~~~~~~~~ ~~~~~~~~~~~~~ + | / | + \ _________/ | + \ / \ +r3-eth1 .3 | | .3 r3-eth0 | .4 r4-eth0 + +----+--+---+ +----+----+ + | r3 | | r4 | + | 3.3.3.3 | | 4.4.4.4 | + +-----------+ +---------+ +""" + +import os +import sys +import pytest +import json +from time import sleep +from functools import partial + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + + +class TemplateTopo(Topo): + "Test topology builder" + + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # + # Define FRR Routers + # + for router in ["r1", "r2", "r3", "r4"]: + tgen.add_router(router) + + # + # Define connections + # + switch = tgen.add_switch("s0") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["r4"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(TemplateTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + # For all registered routers, load the zebra configuration file + for rname, router in router_list.iteritems(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + # Don't start ospfd and ldpd in the CE nodes + if router.name[0] == "r": + router.load_config( + TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_LDP, os.path.join(CWD, "{}/ldpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + + +def router_compare_json_output(rname, command, reference): + "Compare router JSON output" + + logger.info('Comparing router "%s" "%s" output', rname, command) + + tgen = get_topogen() + filename = "{}/{}/{}".format(CWD, rname, reference) + expected = json.loads(open(filename).read()) + + # Run test function until we get an result. Wait at most 80 seconds. + test_func = partial(topotest.router_json_cmp, tgen.gears[rname], command, expected) + _, diff = topotest.run_and_expect(test_func, None, count=160, wait=0.5) + + assertmsg = '"{}" JSON output mismatches the expected result'.format(rname) + assert diff is None, assertmsg + + +def test_ospf_convergence(): + logger.info("Test: check OSPF adjacencies") + + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r2", "r3", "r4"]: + router_compare_json_output( + rname, "show ip ospf neighbor json", "show_ip_ospf_neighbor.json" + ) + + +def test_rib(): + logger.info("Test: verify RIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r2", "r3", "r4"]: + router_compare_json_output(rname, "show ip route json", "show_ip_route.ref") + + +def test_ldp_adjacencies(): + logger.info("Test: verify LDP adjacencies") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r2", "r3", "r4"]: + router_compare_json_output( + rname, "show mpls ldp discovery json", "show_ldp_discovery.ref" + ) + + +def test_ldp_neighbors(): + logger.info("Test: verify LDP neighbors") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r2", "r3", "r4"]: + router_compare_json_output( + rname, "show mpls ldp neighbor json", "show_ldp_neighbor.ref" + ) + + +def test_ldp_bindings(): + logger.info("Test: verify LDP bindings") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r2", "r3", "r4"]: + router_compare_json_output( + rname, "show mpls ldp binding json", "show_ldp_binding.ref" + ) + + +def test_ldp_bindings_all_routes(): + logger.info("Test: verify LDP bindings after host filter removed") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # remove ACL that blocks advertising everything but host routes */ + cmd = 'vtysh -c "configure terminal" -c "mpls ldp" -c "address-family ipv4" -c "no label local allocate host-routes"' + tgen.net["r1"].cmd(cmd) + sleep(2) + + for rname in ["r1", "r2", "r3", "r4"]: + router_compare_json_output( + rname, "show mpls ldp binding json", "show_ldp_all_binding.ref" + ) + + +# Memory leak test template +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/ldp-oc-topo1/r1/ldpd.conf b/tests/topotests/ldp-oc-topo1/r1/ldpd.conf new file mode 100644 index 0000000000..2a8e023832 --- /dev/null +++ b/tests/topotests/ldp-oc-topo1/r1/ldpd.conf @@ -0,0 +1,24 @@ +hostname r1 +log file ldpd.log +! +debug mpls ldp zebra +debug mpls ldp event +debug mpls ldp errors +debug mpls ldp messages recv +debug mpls ldp messages sent +debug mpls ldp discovery hello recv +debug mpls ldp discovery hello sent +! +mpls ldp + router-id 1.1.1.1 + ordered-control + ! + address-family ipv4 + discovery transport-address 1.1.1.1 + ! + interface r1-eth0 + ! + ! +! +line vty +! diff --git a/tests/topotests/ldp-oc-topo1/r1/ospfd.conf b/tests/topotests/ldp-oc-topo1/r1/ospfd.conf new file mode 100644 index 0000000000..87d5703d9e --- /dev/null +++ b/tests/topotests/ldp-oc-topo1/r1/ospfd.conf @@ -0,0 +1,11 @@ +hostname r1 +log file ospfd.log +! +router ospf + router-id 1.1.1.1 + network 0.0.0.0/0 area 0 +! +int r1-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/ldp-oc-topo1/r1/show_ip_ospf_neighbor.json b/tests/topotests/ldp-oc-topo1/r1/show_ip_ospf_neighbor.json new file mode 100644 index 0000000000..2c493173f5 --- /dev/null +++ b/tests/topotests/ldp-oc-topo1/r1/show_ip_ospf_neighbor.json @@ -0,0 +1,12 @@ +{ + "neighbors":{ + "2.2.2.2":[ + { + "priority":1, + "state":"Full\/DR", + "address":"10.0.1.2", + "ifaceName":"r1-eth0:10.0.1.1" + } + ] + } +} diff --git a/tests/topotests/ldp-oc-topo1/r1/show_ip_route.ref b/tests/topotests/ldp-oc-topo1/r1/show_ip_route.ref new file mode 100644 index 0000000000..2131668fc3 --- /dev/null +++ b/tests/topotests/ldp-oc-topo1/r1/show_ip_route.ref @@ -0,0 +1,160 @@ +{ + "1.1.1.1/32":[ + { + "prefix":"1.1.1.1/32", + "protocol":"ospf", + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"lo", + "active":true + } + ] + }, + { + "prefix":"1.1.1.1/32", + "protocol":"connected", + "selected":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceName":"lo", + "active":true + } + ] + } + ], + "2.2.2.2/32":[ + { + "prefix":"2.2.2.2/32", + "protocol":"ospf", + "selected":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true + } + ] + } + ], + "3.3.3.3/32":[ + { + "prefix":"3.3.3.3/32", + "protocol":"ospf", + "selected":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true + } + ] + } + ], + "4.4.4.4/32":[ + { + "prefix":"4.4.4.4/32", + "protocol":"ospf", + "selected":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true + } + ] + } + ], + "10.0.1.0/24":[ + { + "prefix":"10.0.1.0/24", + "protocol":"ospf", + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"r1-eth0", + "active":true + } + ] + }, + { + "prefix":"10.0.1.0/24", + "protocol":"connected", + "selected":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceName":"r1-eth0", + "active":true + } + ] + } + ], + "10.0.2.0/24":[ + { + "prefix":"10.0.2.0/24", + "protocol":"ospf", + "selected":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true + } + ] + } + ], + "10.0.3.0/24":[ + { + "prefix":"10.0.3.0/24", + "protocol":"ospf", + "selected":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true + } + ] + } + ], + "123.0.1.0/24":[ + { + "prefix":"123.0.1.0/24", + "protocol":"ospf", + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"r1-eth0", + "active":true + } + ] + }, + { + "prefix":"123.0.1.0/24", + "protocol":"connected", + "selected":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceName":"r1-eth0", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/ldp-oc-topo1/r1/show_ldp_binding.ref b/tests/topotests/ldp-oc-topo1/r1/show_ldp_binding.ref new file mode 100644 index 0000000000..99a59668f8 --- /dev/null +++ b/tests/topotests/ldp-oc-topo1/r1/show_ldp_binding.ref @@ -0,0 +1,61 @@ +{ + "bindings":[ + { + "addressFamily":"ipv4", + "prefix":"1.1.1.1/32", + "neighborId":"0.0.0.0", + "localLabel":"imp-null", + "remoteLabel":"-", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"2.2.2.2/32", + "neighborId":"2.2.2.2", + "remoteLabel":"imp-null", + "inUse":1 + }, + { + "addressFamily":"ipv4", + "prefix":"3.3.3.3/32", + "neighborId":"2.2.2.2", + "inUse":1 + }, + { + "addressFamily":"ipv4", + "prefix":"4.4.4.4/32", + "neighborId":"0.0.0.0", + "remoteLabel":"-", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"10.0.1.0/24", + "neighborId":"2.2.2.2", + "localLabel":"imp-null", + "remoteLabel":"imp-null", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"10.0.2.0/24", + "neighborId":"2.2.2.2", + "remoteLabel":"imp-null", + "inUse":1 + }, + { + "addressFamily":"ipv4", + "prefix":"10.0.3.0/24", + "neighborId":"2.2.2.2", + "inUse":1 + }, + { + "addressFamily":"ipv4", + "prefix":"123.0.1.0/24", + "neighborId":"0.0.0.0", + "localLabel":"imp-null", + "remoteLabel":"-", + "inUse":0 + } + ] +} diff --git a/tests/topotests/ldp-oc-topo1/r1/show_ldp_discovery.ref b/tests/topotests/ldp-oc-topo1/r1/show_ldp_discovery.ref new file mode 100644 index 0000000000..b349f4418f --- /dev/null +++ b/tests/topotests/ldp-oc-topo1/r1/show_ldp_discovery.ref @@ -0,0 +1,11 @@ +{ + "adjacencies":[ + { + "addressFamily":"ipv4", + "neighborId":"2.2.2.2", + "type":"link", + "interface":"r1-eth0", + "helloHoldtime":15 + } + ] +} diff --git a/tests/topotests/ldp-oc-topo1/r1/show_ldp_neighbor.ref b/tests/topotests/ldp-oc-topo1/r1/show_ldp_neighbor.ref new file mode 100644 index 0000000000..4bff444a46 --- /dev/null +++ b/tests/topotests/ldp-oc-topo1/r1/show_ldp_neighbor.ref @@ -0,0 +1,10 @@ +{ + "neighbors":[ + { + "addressFamily":"ipv4", + "neighborId":"2.2.2.2", + "state":"OPERATIONAL", + "transportAddress":"2.2.2.2" + } + ] +} diff --git a/tests/topotests/ldp-oc-topo1/r1/zebra.conf b/tests/topotests/ldp-oc-topo1/r1/zebra.conf new file mode 100644 index 0000000000..83aea46e64 --- /dev/null +++ b/tests/topotests/ldp-oc-topo1/r1/zebra.conf @@ -0,0 +1,17 @@ +log file zebra.log +! +hostname r1 +! +interface lo + ip address 1.1.1.1/32 +! +interface r1-eth0 + description to sw0 + ip address 10.0.1.1/24 + ip address 123.0.1.1/24 +! +ip forwarding +! +! +line vty +! diff --git a/tests/topotests/ldp-oc-topo1/r2/ldpd.conf b/tests/topotests/ldp-oc-topo1/r2/ldpd.conf new file mode 100644 index 0000000000..e1a552c701 --- /dev/null +++ b/tests/topotests/ldp-oc-topo1/r2/ldpd.conf @@ -0,0 +1,28 @@ +hostname r2 +log file ldpd.log +! +debug mpls ldp zebra +debug mpls ldp event +debug mpls ldp errors +debug mpls ldp messages recv +debug mpls ldp messages sent +debug mpls ldp discovery hello recv +debug mpls ldp discovery hello sent +! +mpls ldp + router-id 2.2.2.2 + ordered-control + ! + address-family ipv4 + discovery transport-address 2.2.2.2 + ! + interface r2-eth0 + ! + interface r2-eth1 + ! + interface r2-eth2 + ! + ! +! +line vty +! diff --git a/tests/topotests/ldp-oc-topo1/r2/ospfd.conf b/tests/topotests/ldp-oc-topo1/r2/ospfd.conf new file mode 100644 index 0000000000..51317202bb --- /dev/null +++ b/tests/topotests/ldp-oc-topo1/r2/ospfd.conf @@ -0,0 +1,15 @@ +hostname r2 +log file ospfd.log +! +router ospf + router-id 2.2.2.2 + network 0.0.0.0/0 area 0 +! +int r2-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +int r2-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/ldp-oc-topo1/r2/show_ip_ospf_neighbor.json b/tests/topotests/ldp-oc-topo1/r2/show_ip_ospf_neighbor.json new file mode 100644 index 0000000000..55f12359e5 --- /dev/null +++ b/tests/topotests/ldp-oc-topo1/r2/show_ip_ospf_neighbor.json @@ -0,0 +1,31 @@ +{ + "neighbors":{ + "1.1.1.1":[ + { + "priority":1, + "state":"Full\/Backup", + "address":"10.0.1.1", + "ifaceName":"r2-eth0:10.0.1.2", + "retransmitCounter":0, + "requestCounter":0, + "dbSummaryCounter":0 + } + ], + "3.3.3.3":[ + { + "priority":1, + "state":"Full\/Backup", + "address":"10.0.2.3", + "ifaceName":"r2-eth1:10.0.2.2" + } + ], + "4.4.4.4":[ + { + "priority":1, + "state":"Full\/DR", + "address":"10.0.2.4", + "ifaceName":"r2-eth1:10.0.2.2" + } + ] + } +} diff --git a/tests/topotests/ldp-oc-topo1/r2/show_ip_route.ref b/tests/topotests/ldp-oc-topo1/r2/show_ip_route.ref new file mode 100644 index 0000000000..4b1d31a5d3 --- /dev/null +++ b/tests/topotests/ldp-oc-topo1/r2/show_ip_route.ref @@ -0,0 +1,198 @@ +{ + "1.1.1.1/32":[ + { + "prefix":"1.1.1.1/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "distance":110, + "metric":10, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceName":"r2-eth0", + "active":true + } + ] + } + ], + "2.2.2.2/32":[ + { + "prefix":"2.2.2.2/32", + "protocol":"ospf", + "distance":110, + "metric":0, + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"lo", + "active":true + } + ] + }, + { + "prefix":"2.2.2.2/32", + "protocol":"connected", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceName":"lo", + "active":true + } + ] + } + ], + "3.3.3.3/32":[ + { + "prefix":"3.3.3.3/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "distance":110, + "metric":10, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.3", + "afi":"ipv4", + "interfaceName":"r2-eth1", + "active":true + } + ] + } + ], + "4.4.4.4/32":[ + { + "prefix":"4.4.4.4/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "distance":110, + "metric":10, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"r2-eth1", + "active":true + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"ospf", + "distance":110, + "metric":10, + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"r2-eth0", + "active":true + } + ] + }, + { + "prefix":"10.0.1.0\/24", + "protocol":"connected", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceName":"r2-eth0", + "active":true + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"ospf", + "distance":110, + "metric":10, + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"r2-eth1", + "active":true + } + ] + }, + { + "prefix":"10.0.2.0\/24", + "protocol":"connected", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceName":"r2-eth1", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "distance":110, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.3", + "afi":"ipv4", + "interfaceName":"r2-eth1", + "active":true + } + ] + } + ], + "123.0.1.0\/24":[ + { + "prefix":"123.0.1.0\/24", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "distance":110, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.1.1", + "afi":"ipv4", + "interfaceName":"r2-eth0", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/ldp-oc-topo1/r2/show_ldp_binding.ref b/tests/topotests/ldp-oc-topo1/r2/show_ldp_binding.ref new file mode 100644 index 0000000000..95fb847c1e --- /dev/null +++ b/tests/topotests/ldp-oc-topo1/r2/show_ldp_binding.ref @@ -0,0 +1,63 @@ +{ + "bindings":[ + { + "addressFamily":"ipv4", + "prefix":"1.1.1.1/32", + "neighborId":"1.1.1.1", + "remoteLabel":"imp-null", + "inUse":1 + }, + { + "addressFamily":"ipv4", + "prefix":"2.2.2.2/32", + "neighborId":"0.0.0.0", + "localLabel":"imp-null", + "remoteLabel":"-", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"3.3.3.3/32", + "neighborId":"3.3.3.3", + "remoteLabel":"imp-null", + "inUse":1 + }, + { + "addressFamily":"ipv4", + "prefix":"4.4.4.4/32", + "neighborId":"0.0.0.0", + "remoteLabel":"-", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"10.0.1.0/24", + "neighborId":"1.1.1.1", + "localLabel":"imp-null", + "remoteLabel":"imp-null", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"10.0.2.0/24", + "neighborId":"3.3.3.3", + "localLabel":"imp-null", + "remoteLabel":"imp-null", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"10.0.3.0/24", + "neighborId":"3.3.3.3", + "remoteLabel":"imp-null", + "inUse":1 + }, + { + "addressFamily":"ipv4", + "prefix":"123.0.1.0/24", + "neighborId":"1.1.1.1", + "remoteLabel":"imp-null", + "inUse":1 + } + ] +} diff --git a/tests/topotests/ldp-oc-topo1/r2/show_ldp_discovery.ref b/tests/topotests/ldp-oc-topo1/r2/show_ldp_discovery.ref new file mode 100644 index 0000000000..8129570082 --- /dev/null +++ b/tests/topotests/ldp-oc-topo1/r2/show_ldp_discovery.ref @@ -0,0 +1,18 @@ +{ + "adjacencies":[ + { + "addressFamily":"ipv4", + "neighborId":"1.1.1.1", + "type":"link", + "interface":"r2-eth0", + "helloHoldtime":15 + }, + { + "addressFamily":"ipv4", + "neighborId":"3.3.3.3", + "type":"link", + "interface":"r2-eth1", + "helloHoldtime":15 + } + ] +} diff --git a/tests/topotests/ldp-oc-topo1/r2/show_ldp_neighbor.ref b/tests/topotests/ldp-oc-topo1/r2/show_ldp_neighbor.ref new file mode 100644 index 0000000000..eed35289ea --- /dev/null +++ b/tests/topotests/ldp-oc-topo1/r2/show_ldp_neighbor.ref @@ -0,0 +1,16 @@ +{ + "neighbors":[ + { + "addressFamily":"ipv4", + "neighborId":"1.1.1.1", + "state":"OPERATIONAL", + "transportAddress":"1.1.1.1" + }, + { + "addressFamily":"ipv4", + "neighborId":"3.3.3.3", + "state":"OPERATIONAL", + "transportAddress":"3.3.3.3" + } + ] +} diff --git a/tests/topotests/ldp-oc-topo1/r2/zebra.conf b/tests/topotests/ldp-oc-topo1/r2/zebra.conf new file mode 100644 index 0000000000..1f1e3e391a --- /dev/null +++ b/tests/topotests/ldp-oc-topo1/r2/zebra.conf @@ -0,0 +1,27 @@ +log file zebra.log +! +hostname r2 +! +interface lo + ip address 2.2.2.2/32 +! +interface r2-eth0 + description to sw0 + ip address 10.0.1.2/24 +! no link-detect +! +interface r2-eth1 + description to sw1 + ip address 10.0.2.2/24 +! no link-detect +! +interface r2-eths2 + description to sw2 + ip address 10.0.3.2/24 +! no link-detect +! +ip forwarding +! +! +line vty +! diff --git a/tests/topotests/ldp-oc-topo1/r3/ldpd.conf b/tests/topotests/ldp-oc-topo1/r3/ldpd.conf new file mode 100644 index 0000000000..4e66b140ac --- /dev/null +++ b/tests/topotests/ldp-oc-topo1/r3/ldpd.conf @@ -0,0 +1,24 @@ +hostname r3 +log file ldpd.log +! +debug mpls ldp zebra +debug mpls ldp event +debug mpls ldp errors +debug mpls ldp messages recv +debug mpls ldp messages sent +debug mpls ldp discovery hello recv +debug mpls ldp discovery hello sent +! +mpls ldp + router-id 3.3.3.3 + ordered-control + ! + address-family ipv4 + discovery transport-address 3.3.3.3 + ! + interface r3-eth0 + ! + ! +! +line vty +! diff --git a/tests/topotests/ldp-oc-topo1/r3/ospfd.conf b/tests/topotests/ldp-oc-topo1/r3/ospfd.conf new file mode 100644 index 0000000000..4566976b7b --- /dev/null +++ b/tests/topotests/ldp-oc-topo1/r3/ospfd.conf @@ -0,0 +1,12 @@ +hostname r3 +password 1 +log file ospfd.log +! +router ospf + router-id 3.3.3.3 + network 0.0.0.0/0 area 0 +! +int r3-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/ldp-oc-topo1/r3/show_ip_ospf_neighbor.json b/tests/topotests/ldp-oc-topo1/r3/show_ip_ospf_neighbor.json new file mode 100644 index 0000000000..24502ed813 --- /dev/null +++ b/tests/topotests/ldp-oc-topo1/r3/show_ip_ospf_neighbor.json @@ -0,0 +1,20 @@ +{ + "neighbors":{ + "2.2.2.2":[ + { + "priority":1, + "state":"Full\/DROther", + "address":"10.0.2.2", + "ifaceName":"r3-eth0:10.0.2.3" + } + ], + "4.4.4.4":[ + { + "priority":1, + "state":"Full\/DR", + "address":"10.0.2.4", + "ifaceName":"r3-eth0:10.0.2.3" + } + ] + } +} diff --git a/tests/topotests/ldp-oc-topo1/r3/show_ip_route.ref b/tests/topotests/ldp-oc-topo1/r3/show_ip_route.ref new file mode 100644 index 0000000000..4d115caf3e --- /dev/null +++ b/tests/topotests/ldp-oc-topo1/r3/show_ip_route.ref @@ -0,0 +1,198 @@ +{ + "1.1.1.1/32":[ + { + "prefix":"1.1.1.1/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "distance":110, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"r3-eth0", + "active":true + } + ] + } + ], + "2.2.2.2/32":[ + { + "prefix":"2.2.2.2/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "distance":110, + "metric":10, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"r3-eth0", + "active":true + } + ] + } + ], + "3.3.3.3/32":[ + { + "prefix":"3.3.3.3/32", + "protocol":"ospf", + "distance":110, + "metric":0, + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"lo", + "active":true + } + ] + }, + { + "prefix":"3.3.3.3/32", + "protocol":"connected", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceName":"lo", + "active":true + } + ] + } + ], + "4.4.4.4/32":[ + { + "prefix":"4.4.4.4/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "distance":110, + "metric":10, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.4", + "afi":"ipv4", + "interfaceName":"r3-eth0", + "active":true + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "distance":110, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"r3-eth0", + "active":true + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"ospf", + "distance":110, + "metric":10, + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"r3-eth0", + "active":true + } + ] + }, + { + "prefix":"10.0.2.0\/24", + "protocol":"connected", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceName":"r3-eth0", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"ospf", + "distance":110, + "metric":10, + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"r3-eth1", + "active":true + } + ] + }, + { + "prefix":"10.0.3.0\/24", + "protocol":"connected", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceName":"r3-eth1", + "active":true + } + ] + } + ], + "123.0.1.0\/24":[ + { + "prefix":"123.0.1.0\/24", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "distance":110, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"r3-eth0", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/ldp-oc-topo1/r3/show_ldp_binding.ref b/tests/topotests/ldp-oc-topo1/r3/show_ldp_binding.ref new file mode 100644 index 0000000000..100dd307ea --- /dev/null +++ b/tests/topotests/ldp-oc-topo1/r3/show_ldp_binding.ref @@ -0,0 +1,61 @@ +{ + "bindings":[ + { + "addressFamily":"ipv4", + "prefix":"1.1.1.1/32", + "neighborId":"2.2.2.2", + "inUse":1 + }, + { + "addressFamily":"ipv4", + "prefix":"2.2.2.2/32", + "neighborId":"2.2.2.2", + "remoteLabel":"imp-null", + "inUse":1 + }, + { + "addressFamily":"ipv4", + "prefix":"3.3.3.3/32", + "neighborId":"0.0.0.0", + "localLabel":"imp-null", + "remoteLabel":"-", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"4.4.4.4/32", + "neighborId":"0.0.0.0", + "remoteLabel":"-", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"10.0.1.0/24", + "neighborId":"2.2.2.2", + "remoteLabel":"imp-null", + "inUse":1 + }, + { + "addressFamily":"ipv4", + "prefix":"10.0.2.0/24", + "neighborId":"2.2.2.2", + "localLabel":"imp-null", + "remoteLabel":"imp-null", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"10.0.3.0/24", + "neighborId":"0.0.0.0", + "localLabel":"imp-null", + "remoteLabel":"-", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"123.0.1.0/24", + "neighborId":"2.2.2.2", + "inUse":1 + } + ] +} diff --git a/tests/topotests/ldp-oc-topo1/r3/show_ldp_discovery.ref b/tests/topotests/ldp-oc-topo1/r3/show_ldp_discovery.ref new file mode 100644 index 0000000000..c3a07e7e38 --- /dev/null +++ b/tests/topotests/ldp-oc-topo1/r3/show_ldp_discovery.ref @@ -0,0 +1,11 @@ +{ + "adjacencies":[ + { + "addressFamily":"ipv4", + "neighborId":"2.2.2.2", + "type":"link", + "interface":"r3-eth0", + "helloHoldtime":15 + } + ] +} diff --git a/tests/topotests/ldp-oc-topo1/r3/show_ldp_neighbor.ref b/tests/topotests/ldp-oc-topo1/r3/show_ldp_neighbor.ref new file mode 100644 index 0000000000..4bff444a46 --- /dev/null +++ b/tests/topotests/ldp-oc-topo1/r3/show_ldp_neighbor.ref @@ -0,0 +1,10 @@ +{ + "neighbors":[ + { + "addressFamily":"ipv4", + "neighborId":"2.2.2.2", + "state":"OPERATIONAL", + "transportAddress":"2.2.2.2" + } + ] +} diff --git a/tests/topotests/ldp-oc-topo1/r3/zebra.conf b/tests/topotests/ldp-oc-topo1/r3/zebra.conf new file mode 100644 index 0000000000..234c215ddf --- /dev/null +++ b/tests/topotests/ldp-oc-topo1/r3/zebra.conf @@ -0,0 +1,22 @@ +log file zebra.log +! +hostname r3 +! +interface lo + ip address 3.3.3.3/32 +! +interface r3-eth0 + description to sw1 + ip address 10.0.2.3/24 +! no link-detect +! +interface r3-eth1 + description to sw2 + ip address 10.0.3.3/24 +! no link-detect +! +ip forwarding +! +! +line vty +! diff --git a/tests/topotests/ldp-oc-topo1/r4/ldpd.conf b/tests/topotests/ldp-oc-topo1/r4/ldpd.conf new file mode 100644 index 0000000000..6b7d28f983 --- /dev/null +++ b/tests/topotests/ldp-oc-topo1/r4/ldpd.conf @@ -0,0 +1,24 @@ +hostname r4 +log file ldpd.log +! +debug mpls ldp zebra +debug mpls ldp event +debug mpls ldp errors +debug mpls ldp messages recv +debug mpls ldp messages sent +debug mpls ldp discovery hello recv +debug mpls ldp discovery hello sent +! +mpls ldp + router-id 4.4.4.4 + ordered-control + ! + address-family ipv4 + discovery transport-address 4.4.4.4 + ! + !interface r4-eth0 + ! + ! +! +line vty +! diff --git a/tests/topotests/ldp-oc-topo1/r4/ospfd.conf b/tests/topotests/ldp-oc-topo1/r4/ospfd.conf new file mode 100644 index 0000000000..5aae885a12 --- /dev/null +++ b/tests/topotests/ldp-oc-topo1/r4/ospfd.conf @@ -0,0 +1,11 @@ +hostname r4 +log file ospfd.log +! +router ospf + router-id 4.4.4.4 + network 0.0.0.0/0 area 0 +! +int r4-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/ldp-oc-topo1/r4/show_ip_ospf_neighbor.json b/tests/topotests/ldp-oc-topo1/r4/show_ip_ospf_neighbor.json new file mode 100644 index 0000000000..794410522d --- /dev/null +++ b/tests/topotests/ldp-oc-topo1/r4/show_ip_ospf_neighbor.json @@ -0,0 +1,21 @@ + +{ + "neighbors":{ + "2.2.2.2":[ + { + "priority":1, + "state":"Full\/DROther", + "address":"10.0.2.2", + "ifaceName":"r4-eth0:10.0.2.4" + } + ], + "3.3.3.3":[ + { + "priority":1, + "state":"Full\/Backup", + "address":"10.0.2.3", + "ifaceName":"r4-eth0:10.0.2.4" + } + ] + } +} diff --git a/tests/topotests/ldp-oc-topo1/r4/show_ip_route.ref b/tests/topotests/ldp-oc-topo1/r4/show_ip_route.ref new file mode 100644 index 0000000000..223cbded84 --- /dev/null +++ b/tests/topotests/ldp-oc-topo1/r4/show_ip_route.ref @@ -0,0 +1,186 @@ +{ + "1.1.1.1/32":[ + { + "prefix":"1.1.1.1/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "distance":110, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"r4-eth0", + "active":true + } + ] + } + ], + "2.2.2.2/32":[ + { + "prefix":"2.2.2.2/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "distance":110, + "metric":10, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"r4-eth0", + "active":true + } + ] + } + ], + "3.3.3.3/32":[ + { + "prefix":"3.3.3.3/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "distance":110, + "metric":10, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.3", + "afi":"ipv4", + "interfaceName":"r4-eth0", + "active":true + } + ] + } + ], + "4.4.4.4/32":[ + { + "prefix":"4.4.4.4/32", + "protocol":"ospf", + "distance":110, + "metric":0, + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"lo", + "active":true + } + ] + }, + { + "prefix":"4.4.4.4/32", + "protocol":"connected", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceName":"lo", + "active":true + } + ] + } + ], + "10.0.1.0\/24":[ + { + "prefix":"10.0.1.0\/24", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "distance":110, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"r4-eth0", + "active":true + } + ] + } + ], + "10.0.2.0\/24":[ + { + "prefix":"10.0.2.0\/24", + "protocol":"ospf", + "distance":110, + "metric":10, + "nexthops":[ + { + "directlyConnected":true, + "interfaceName":"r4-eth0", + "active":true + } + ] + }, + { + "prefix":"10.0.2.0\/24", + "protocol":"connected", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceName":"r4-eth0", + "active":true + } + ] + } + ], + "10.0.3.0\/24":[ + { + "prefix":"10.0.3.0\/24", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "distance":110, + "metric":20, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.3", + "afi":"ipv4", + "interfaceName":"r4-eth0", + "active":true + } + ] + } + ], + "123.0.1.0\/24":[ + { + "prefix":"123.0.1.0\/24", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "distance":110, + "metric":30, + "installed":true, + "nexthops":[ + { + "fib":true, + "ip":"10.0.2.2", + "afi":"ipv4", + "interfaceName":"r4-eth0", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/ldp-oc-topo1/r4/show_ldp_binding.ref b/tests/topotests/ldp-oc-topo1/r4/show_ldp_binding.ref new file mode 100644 index 0000000000..2a46c40346 --- /dev/null +++ b/tests/topotests/ldp-oc-topo1/r4/show_ldp_binding.ref @@ -0,0 +1,68 @@ +{ + "bindings":[ + { + "addressFamily":"ipv4", + "prefix":"1.1.1.1/32", + "neighborId":"0.0.0.0", + "localLabel":"imp-null", + "remoteLabel":"-", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"2.2.2.2/32", + "neighborId":"0.0.0.0", + "localLabel":"imp-null", + "remoteLabel":"-", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"3.3.3.3/32", + "neighborId":"0.0.0.0", + "localLabel":"imp-null", + "remoteLabel":"-", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"4.4.4.4/32", + "neighborId":"0.0.0.0", + "localLabel":"imp-null", + "remoteLabel":"-", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"10.0.1.0/24", + "neighborId":"0.0.0.0", + "localLabel":"imp-null", + "remoteLabel":"-", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"10.0.2.0/24", + "neighborId":"0.0.0.0", + "localLabel":"imp-null", + "remoteLabel":"-", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"10.0.3.0/24", + "neighborId":"0.0.0.0", + "localLabel":"imp-null", + "remoteLabel":"-", + "inUse":0 + }, + { + "addressFamily":"ipv4", + "prefix":"123.0.1.0/24", + "neighborId":"0.0.0.0", + "localLabel":"imp-null", + "remoteLabel":"-", + "inUse":0 + } + ] +} diff --git a/tests/topotests/ldp-oc-topo1/r4/show_ldp_discovery.ref b/tests/topotests/ldp-oc-topo1/r4/show_ldp_discovery.ref new file mode 100644 index 0000000000..2c63c08510 --- /dev/null +++ b/tests/topotests/ldp-oc-topo1/r4/show_ldp_discovery.ref @@ -0,0 +1,2 @@ +{ +} diff --git a/tests/topotests/ldp-oc-topo1/r4/show_ldp_neighbor.ref b/tests/topotests/ldp-oc-topo1/r4/show_ldp_neighbor.ref new file mode 100644 index 0000000000..2c63c08510 --- /dev/null +++ b/tests/topotests/ldp-oc-topo1/r4/show_ldp_neighbor.ref @@ -0,0 +1,2 @@ +{ +} diff --git a/tests/topotests/ldp-oc-topo1/r4/zebra.conf b/tests/topotests/ldp-oc-topo1/r4/zebra.conf new file mode 100644 index 0000000000..7e291053e5 --- /dev/null +++ b/tests/topotests/ldp-oc-topo1/r4/zebra.conf @@ -0,0 +1,17 @@ +log file zebra.log +! +hostname r4 +! +interface lo + ip address 4.4.4.4/32 +! +interface r4-eth0 + description to sw1 + ip address 10.0.2.4/24 +! no link-detect +! +ip forwarding +! +! +line vty +! diff --git a/tests/topotests/ldp-oc-topo1/test_ldp_oc_topo1.dot b/tests/topotests/ldp-oc-topo1/test_ldp_oc_topo1.dot new file mode 100644 index 0000000000..62058e3cb1 --- /dev/null +++ b/tests/topotests/ldp-oc-topo1/test_ldp_oc_topo1.dot @@ -0,0 +1,76 @@ +## Color coding: +######################### +## Main FRR: #f08080 red +## Switches: #d0e0d0 gray +## RIP: #19e3d9 Cyan +## RIPng: #fcb314 dark yellow +## OSPFv2: #32b835 Green +## OSPFv3: #19e3d9 Cyan +## ISIS IPv4 #fcb314 dark yellow +## ISIS IPv6 #9a81ec purple +## BGP IPv4 #eee3d3 beige +## BGP IPv6 #fdff00 yellow +##### Colors (see http://www.color-hex.com/) + +graph template { + label="Test Topology - LDP-OC 1"; + + # Routers + r1 [ + shape=doubleoctagon, + label="r1", + fillcolor="#f08080", + style=filled, + ]; + r2 [ + shape=doubleoctagon + label="r2", + fillcolor="#f08080", + style=filled, + ]; + r3 [ + shape=doubleoctagon + label="r3", + fillcolor="#f08080", + style=filled, + ]; + r4 [ + shape=doubleoctagon + label="r4", + fillcolor="#f08080", + style=filled, + ]; + + + # Switches + s0 [ + shape=oval, + label="10.0.1.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + s1 [ + shape=oval, + label="10.0.2.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + s2 [ + shape=oval, + label="10.0.3.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + + # Connections + + r1 -- s0 [label="eth0"]; + r2 -- s0 [label="eth0"]; + + r2 -- s1 [label="eth1"]; + r3 -- s1 [label="eth0"]; + r4 -- s1 [label="eth0"]; + + r2 -- s2 [label="eth2"]; + r3 -- s2 [label="eth1"]; +} diff --git a/tests/topotests/ldp-oc-topo1/test_ldp_oc_topo1.py b/tests/topotests/ldp-oc-topo1/test_ldp_oc_topo1.py new file mode 100755 index 0000000000..ac99eb1a26 --- /dev/null +++ b/tests/topotests/ldp-oc-topo1/test_ldp_oc_topo1.py @@ -0,0 +1,242 @@ +#!/usr/bin/env python + +# +# test_ldp_oc_topo1.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2020 by by Volta Networks +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_ldp_oc_topo1.py: Simple FRR/Quagga LDP Test + + +---------+ + | r1 | + | 1.1.1.1 | + +----+----+ + | .1 r1-eth0 + | + ~~~~~~~~~~~~~ + ~~ sw0 ~~ + ~~ 10.0.1.0/24 ~~ + ~~~~~~~~~~~~~ + |10.0.1.0/24 + | + | .2 r2-eth0 + +----+----+ + | r2 | + | 2.2.2.2 | + +--+---+--+ + r2-eth2 .2 | | .2 r2-eth1 + ______/ \______ + / \ + ~~~~~~~~~~~~~ ~~~~~~~~~~~~~ +~~ sw2 ~~ ~~ sw1 ~~ +~~ 10.0.3.0/24 ~~ ~~ 10.0.2.0/24 ~~ + ~~~~~~~~~~~~~ ~~~~~~~~~~~~~ + | / | + \ _________/ | + \ / \ +r3-eth1 .3 | | .3 r3-eth0 | .4 r4-eth0 + +----+--+---+ +----+----+ + | r3 | | r4 | + | 3.3.3.3 | | 4.4.4.4 | + +-----------+ +---------+ +""" + +import os +import sys +import pytest +import json +from time import sleep +from functools import partial + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + + +class TemplateTopo(Topo): + "Test topology builder" + + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # + # Define FRR Routers + # + for router in ["r1", "r2", "r3", "r4"]: + tgen.add_router(router) + + # + # Define connections + # + switch = tgen.add_switch("s0") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["r4"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(TemplateTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + # For all registered routers, load the zebra configuration file + for rname, router in router_list.iteritems(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + # Don't start ospfd and ldpd in the CE nodes + if router.name[0] == "r": + router.load_config( + TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_LDP, os.path.join(CWD, "{}/ldpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + + +def router_compare_json_output(rname, command, reference): + "Compare router JSON output" + + logger.info('Comparing router "%s" "%s" output', rname, command) + + tgen = get_topogen() + filename = "{}/{}/{}".format(CWD, rname, reference) + expected = json.loads(open(filename).read()) + + # Run test function until we get an result. Wait at most 80 seconds. + test_func = partial(topotest.router_json_cmp, tgen.gears[rname], command, expected) + _, diff = topotest.run_and_expect(test_func, None, count=160, wait=0.5) + + assertmsg = '"{}" JSON output mismatches the expected result'.format(rname) + assert diff is None, assertmsg + + +def test_ospf_convergence(): + logger.info("Test: check OSPF adjacencies") + + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r2", "r3", "r4"]: + router_compare_json_output( + rname, "show ip ospf neighbor json", "show_ip_ospf_neighbor.json" + ) + + +def test_rib(): + logger.info("Test: verify RIB") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r2", "r3", "r4"]: + router_compare_json_output(rname, "show ip route json", "show_ip_route.ref") + + +def test_ldp_adjacencies(): + logger.info("Test: verify LDP adjacencies") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r2", "r3", "r4"]: + router_compare_json_output( + rname, "show mpls ldp discovery json", "show_ldp_discovery.ref" + ) + + +def test_ldp_neighbors(): + logger.info("Test: verify LDP neighbors") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r2", "r3", "r4"]: + router_compare_json_output( + rname, "show mpls ldp neighbor json", "show_ldp_neighbor.ref" + ) + + +def test_ldp_bindings(): + logger.info("Test: verify LDP bindings") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["r1", "r2", "r3", "r4"]: + router_compare_json_output( + rname, "show mpls ldp binding json", "show_ldp_binding.ref" + ) + + +# Memory leak test template +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/ldp-topo1/r1/ip_mpls_route.ref b/tests/topotests/ldp-topo1/r1/ip_mpls_route.ref index f244122f1a..a13c1d459b 100644 --- a/tests/topotests/ldp-topo1/r1/ip_mpls_route.ref +++ b/tests/topotests/ldp-topo1/r1/ip_mpls_route.ref @@ -3,3 +3,4 @@ xx as to xx via inet 10.0.1.2 dev r1-eth0 proto xx xx via inet 10.0.1.2 dev r1-eth0 proto xx xx via inet 10.0.1.2 dev r1-eth0 proto xx xx via inet 10.0.1.2 dev r1-eth0 proto xx + diff --git a/tests/topotests/ldp-topo1/r1/ip_mpls_route.ref-1 b/tests/topotests/ldp-topo1/r1/ip_mpls_route.ref-1 deleted file mode 100644 index f244122f1a..0000000000 --- a/tests/topotests/ldp-topo1/r1/ip_mpls_route.ref-1 +++ /dev/null @@ -1,5 +0,0 @@ -xx as to xx via inet 10.0.1.2 dev r1-eth0 proto xx -xx as to xx via inet 10.0.1.2 dev r1-eth0 proto xx -xx via inet 10.0.1.2 dev r1-eth0 proto xx -xx via inet 10.0.1.2 dev r1-eth0 proto xx -xx via inet 10.0.1.2 dev r1-eth0 proto xx diff --git a/tests/topotests/ldp-topo1/r1/ospfd.conf b/tests/topotests/ldp-topo1/r1/ospfd.conf index 6daf034d18..87d5703d9e 100644 --- a/tests/topotests/ldp-topo1/r1/ospfd.conf +++ b/tests/topotests/ldp-topo1/r1/ospfd.conf @@ -5,3 +5,7 @@ router ospf router-id 1.1.1.1 network 0.0.0.0/0 area 0 ! +int r1-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/ldp-topo1/r1/show_ipv4_route.ref b/tests/topotests/ldp-topo1/r1/show_ipv4_route.ref index 7819d303d1..7d398887c4 100644 --- a/tests/topotests/ldp-topo1/r1/show_ipv4_route.ref +++ b/tests/topotests/ldp-topo1/r1/show_ipv4_route.ref @@ -1,7 +1,7 @@ -O 1.1.1.1/32 [110/0] is directly connected, lo -O>* 2.2.2.2/32 [110/10] via 10.0.1.2, r1-eth0, label implicit-null -O>* 3.3.3.3/32 [110/20] via 10.0.1.2, r1-eth0, label xxx -O>* 4.4.4.4/32 [110/20] via 10.0.1.2, r1-eth0, label xxx -O 10.0.1.0/24 [110/10] is directly connected, r1-eth0 -O>* 10.0.2.0/24 [110/20] via 10.0.1.2, r1-eth0, label implicit-null -O>* 10.0.3.0/24 [110/20] via 10.0.1.2, r1-eth0, label implicit-null +O 1.1.1.1/32 [110/0] is directly connected, lo, weight 1 +O>* 2.2.2.2/32 [110/10] via 10.0.1.2, r1-eth0, label implicit-null, weight 1 +O>* 3.3.3.3/32 [110/20] via 10.0.1.2, r1-eth0, label xxx, weight 1 +O>* 4.4.4.4/32 [110/20] via 10.0.1.2, r1-eth0, label xxx, weight 1 +O 10.0.1.0/24 [110/10] is directly connected, r1-eth0, weight 1 +O>* 10.0.2.0/24 [110/20] via 10.0.1.2, r1-eth0, label implicit-null, weight 1 +O>* 10.0.3.0/24 [110/20] via 10.0.1.2, r1-eth0, label implicit-null, weight 1 diff --git a/tests/topotests/ldp-topo1/r1/show_ipv4_route.ref-1 b/tests/topotests/ldp-topo1/r1/show_ipv4_route.ref-1 deleted file mode 100644 index ff99ff9866..0000000000 --- a/tests/topotests/ldp-topo1/r1/show_ipv4_route.ref-1 +++ /dev/null @@ -1,7 +0,0 @@ -O 1.1.1.1/32 [110/0] is directly connected, lo -O>* 2.2.2.2/32 [110/10] via 10.0.1.2, r1-eth0 -O>* 3.3.3.3/32 [110/20] via 10.0.1.2, r1-eth0, label xxx -O>* 4.4.4.4/32 [110/20] via 10.0.1.2, r1-eth0, label xxx -O 10.0.1.0/24 [110/10] is directly connected, r1-eth0 -O>* 10.0.2.0/24 [110/20] via 10.0.1.2, r1-eth0 -O>* 10.0.3.0/24 [110/20] via 10.0.1.2, r1-eth0 diff --git a/tests/topotests/ldp-topo1/r1/show_mpls_ldp_binding.ref-1 b/tests/topotests/ldp-topo1/r1/show_mpls_ldp_binding.ref-1 deleted file mode 100644 index ff72a1c0b7..0000000000 --- a/tests/topotests/ldp-topo1/r1/show_mpls_ldp_binding.ref-1 +++ /dev/null @@ -1,42 +0,0 @@ -1.1.1.1/32 - Local binding: label: imp-null - Remote bindings: - Peer Label - ----------------- --------- - 2.2.2.2 xxx -2.2.2.2/32 - Local binding: label: xxx - Remote bindings: - Peer Label - ----------------- --------- - 2.2.2.2 imp-null -3.3.3.3/32 - Local binding: label: xxx - Remote bindings: - Peer Label - ----------------- --------- - 2.2.2.2 xxx -4.4.4.4/32 - Local binding: label: xxx - Remote bindings: - Peer Label - ----------------- --------- - 2.2.2.2 xxx -10.0.1.0/24 - Local binding: label: imp-null - Remote bindings: - Peer Label - ----------------- --------- - 2.2.2.2 imp-null -10.0.2.0/24 - Local binding: label: xxx - Remote bindings: - Peer Label - ----------------- --------- - 2.2.2.2 imp-null -10.0.3.0/24 - Local binding: label: xxx - Remote bindings: - Peer Label - ----------------- --------- - 2.2.2.2 imp-null diff --git a/tests/topotests/ldp-topo1/r1/show_mpls_ldp_discovery.ref-1 b/tests/topotests/ldp-topo1/r1/show_mpls_ldp_discovery.ref-1 deleted file mode 100644 index 38522e162e..0000000000 --- a/tests/topotests/ldp-topo1/r1/show_mpls_ldp_discovery.ref-1 +++ /dev/null @@ -1,7 +0,0 @@ -Local LDP Identifier: 1.1.1.1:0 -Discovery Sources: - Interfaces: - r1-eth0: xmit/recv - LDP Id: 2.2.2.2:0, Transport address: 2.2.2.2 - Hold time: 15 sec - Targeted Hellos: diff --git a/tests/topotests/ldp-topo1/r1/show_mpls_ldp_interface.ref-1 b/tests/topotests/ldp-topo1/r1/show_mpls_ldp_interface.ref-1 deleted file mode 100644 index 0fb15d2da7..0000000000 --- a/tests/topotests/ldp-topo1/r1/show_mpls_ldp_interface.ref-1 +++ /dev/null @@ -1,2 +0,0 @@ -AF Interface State Uptime Hello Timers ac -ipv4 r1-eth0 ACTIVE xx:xx:xx 5/15 1 diff --git a/tests/topotests/ldp-topo1/r1/show_mpls_ldp_neighbor.ref-1 b/tests/topotests/ldp-topo1/r1/show_mpls_ldp_neighbor.ref-1 deleted file mode 100644 index 3df98bfae5..0000000000 --- a/tests/topotests/ldp-topo1/r1/show_mpls_ldp_neighbor.ref-1 +++ /dev/null @@ -1,8 +0,0 @@ -Peer LDP Identifier: 2.2.2.2:0 - TCP connection: 1.1.1.1:xxx - 2.2.2.2:xxx - Session Holdtime: 180 sec - State: OPERATIONAL; Downstream-Unsolicited - Up time: xx:xx:xx - LDP Discovery Sources: - IPv4: - Interface: r1-eth0 diff --git a/tests/topotests/ldp-topo1/r1/show_mpls_table.ref b/tests/topotests/ldp-topo1/r1/show_mpls_table.ref index 61cb9eec82..7e24359af3 100644 --- a/tests/topotests/ldp-topo1/r1/show_mpls_table.ref +++ b/tests/topotests/ldp-topo1/r1/show_mpls_table.ref @@ -1,8 +1,8 @@ - Inbound Outbound - Label Type Nexthop Label --------- ------- --------------- -------- - XX LDP 10.0.1.2 XX - XX LDP 10.0.1.2 XX - XX LDP 10.0.1.2 implicit-null - XX LDP 10.0.1.2 implicit-null - XX LDP 10.0.1.2 implicit-null + Inbound Label Type Nexthop Outbound Label + ----------------------------------------------- + XX LDP 10.0.1.2 XX + XX LDP 10.0.1.2 XX + XX LDP 10.0.1.2 implicit-null + XX LDP 10.0.1.2 implicit-null + XX LDP 10.0.1.2 implicit-null + diff --git a/tests/topotests/ldp-topo1/r1/show_mpls_table.ref-1 b/tests/topotests/ldp-topo1/r1/show_mpls_table.ref-1 deleted file mode 100644 index 912a082019..0000000000 --- a/tests/topotests/ldp-topo1/r1/show_mpls_table.ref-1 +++ /dev/null @@ -1,8 +0,0 @@ - Inbound Outbound - Label Type Nexthop Label --------- ------- --------------- -------- - XX LDP 10.0.1.2 3 - XX LDP 10.0.1.2 3 - XX LDP 10.0.1.2 3 - XX LDP 10.0.1.2 XX - XX LDP 10.0.1.2 XX diff --git a/tests/topotests/ldp-topo1/r1/show_mpls_table.ref-no-impl-null b/tests/topotests/ldp-topo1/r1/show_mpls_table.ref-no-impl-null deleted file mode 100644 index 912a082019..0000000000 --- a/tests/topotests/ldp-topo1/r1/show_mpls_table.ref-no-impl-null +++ /dev/null @@ -1,8 +0,0 @@ - Inbound Outbound - Label Type Nexthop Label --------- ------- --------------- -------- - XX LDP 10.0.1.2 3 - XX LDP 10.0.1.2 3 - XX LDP 10.0.1.2 3 - XX LDP 10.0.1.2 XX - XX LDP 10.0.1.2 XX diff --git a/tests/topotests/ldp-topo1/r2/ospfd.conf b/tests/topotests/ldp-topo1/r2/ospfd.conf index 8678813665..dbed6189c8 100644 --- a/tests/topotests/ldp-topo1/r2/ospfd.conf +++ b/tests/topotests/ldp-topo1/r2/ospfd.conf @@ -5,3 +5,15 @@ router ospf router-id 2.2.2.2 network 0.0.0.0/0 area 0 ! +int r2-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +int r2-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +int r2-eth2 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/ldp-topo1/r2/show_ipv4_route.ref b/tests/topotests/ldp-topo1/r2/show_ipv4_route.ref index 2a97757757..90e18962a8 100644 --- a/tests/topotests/ldp-topo1/r2/show_ipv4_route.ref +++ b/tests/topotests/ldp-topo1/r2/show_ipv4_route.ref @@ -1,7 +1,7 @@ -O>* 1.1.1.1/32 [110/10] via 10.0.1.1, r2-eth0, label implicit-null -O 2.2.2.2/32 [110/0] is directly connected, lo -O>* 3.3.3.3/32 [110/10] via 10.0.2.3, r2-eth1, label implicit-null -O>* 4.4.4.4/32 [110/10] via 10.0.2.4, r2-eth1, label implicit-null -O 10.0.1.0/24 [110/10] is directly connected, r2-eth0 -O 10.0.2.0/24 [110/10] is directly connected, r2-eth1 -O 10.0.3.0/24 [110/10] is directly connected, r2-eth2 +O>* 1.1.1.1/32 [110/10] via 10.0.1.1, r2-eth0, label implicit-null, weight 1 +O 2.2.2.2/32 [110/0] is directly connected, lo, weight 1 +O>* 3.3.3.3/32 [110/10] via 10.0.2.3, r2-eth1, label implicit-null, weight 1 +O>* 4.4.4.4/32 [110/10] via 10.0.2.4, r2-eth1, label implicit-null, weight 1 +O 10.0.1.0/24 [110/10] is directly connected, r2-eth0, weight 1 +O 10.0.2.0/24 [110/10] is directly connected, r2-eth1, weight 1 +O 10.0.3.0/24 [110/10] is directly connected, r2-eth2, weight 1 diff --git a/tests/topotests/ldp-topo1/r2/show_ipv4_route.ref-1 b/tests/topotests/ldp-topo1/r2/show_ipv4_route.ref-1 deleted file mode 100644 index eaec2f16b9..0000000000 --- a/tests/topotests/ldp-topo1/r2/show_ipv4_route.ref-1 +++ /dev/null @@ -1,7 +0,0 @@ -O>* 1.1.1.1/32 [110/10] via 10.0.1.1, r2-eth0 -O 2.2.2.2/32 [110/0] is directly connected, lo -O>* 3.3.3.3/32 [110/10] via 10.0.2.3, r2-eth1 -O>* 4.4.4.4/32 [110/10] via 10.0.2.4, r2-eth1 -O 10.0.1.0/24 [110/10] is directly connected, r2-eth0 -O 10.0.2.0/24 [110/10] is directly connected, r2-eth1 -O 10.0.3.0/24 [110/10] is directly connected, r2-eth2 diff --git a/tests/topotests/ldp-topo1/r2/show_mpls_ldp_binding.ref-1 b/tests/topotests/ldp-topo1/r2/show_mpls_ldp_binding.ref-1 deleted file mode 100644 index 54ee39080a..0000000000 --- a/tests/topotests/ldp-topo1/r2/show_mpls_ldp_binding.ref-1 +++ /dev/null @@ -1,56 +0,0 @@ -1.1.1.1/32 - Local binding: label: xxx - Remote bindings: - Peer Label - ----------------- --------- - 1.1.1.1 imp-null - 3.3.3.3 xxx - 4.4.4.4 xxx -2.2.2.2/32 - Local binding: label: imp-null - Remote bindings: - Peer Label - ----------------- --------- - 1.1.1.1 xxx - 3.3.3.3 xxx - 4.4.4.4 xxx -3.3.3.3/32 - Local binding: label: xxx - Remote bindings: - Peer Label - ----------------- --------- - 1.1.1.1 xxx - 3.3.3.3 imp-null - 4.4.4.4 xxx -4.4.4.4/32 - Local binding: label: xxx - Remote bindings: - Peer Label - ----------------- --------- - 1.1.1.1 xxx - 3.3.3.3 xxx - 4.4.4.4 imp-null -10.0.1.0/24 - Local binding: label: imp-null - Remote bindings: - Peer Label - ----------------- --------- - 1.1.1.1 imp-null - 3.3.3.3 xxx - 4.4.4.4 xxx -10.0.2.0/24 - Local binding: label: imp-null - Remote bindings: - Peer Label - ----------------- --------- - 1.1.1.1 xxx - 3.3.3.3 imp-null - 4.4.4.4 imp-null -10.0.3.0/24 - Local binding: label: imp-null - Remote bindings: - Peer Label - ----------------- --------- - 1.1.1.1 xxx - 3.3.3.3 imp-null - 4.4.4.4 xxx diff --git a/tests/topotests/ldp-topo1/r2/show_mpls_ldp_discovery.ref-1 b/tests/topotests/ldp-topo1/r2/show_mpls_ldp_discovery.ref-1 deleted file mode 100644 index b1bebd7c46..0000000000 --- a/tests/topotests/ldp-topo1/r2/show_mpls_ldp_discovery.ref-1 +++ /dev/null @@ -1,12 +0,0 @@ -Local LDP Identifier: 2.2.2.2:0 -Discovery Sources: - Interfaces: - r2-eth0: xmit/recv - LDP Id: 1.1.1.1:0, Transport address: 1.1.1.1 - Hold time: 15 sec - r2-eth1: xmit/recv - LDP Id: 3.3.3.3:0, Transport address: 3.3.3.3 - Hold time: 15 sec - LDP Id: 4.4.4.4:0, Transport address: 4.4.4.4 - Hold time: 15 sec - Targeted Hellos: diff --git a/tests/topotests/ldp-topo1/r2/show_mpls_ldp_interface.ref-1 b/tests/topotests/ldp-topo1/r2/show_mpls_ldp_interface.ref-1 deleted file mode 100644 index f9fc98408c..0000000000 --- a/tests/topotests/ldp-topo1/r2/show_mpls_ldp_interface.ref-1 +++ /dev/null @@ -1,3 +0,0 @@ -AF Interface State Uptime Hello Timers ac -ipv4 r2-eth0 ACTIVE xx:xx:xx 5/15 1 -ipv4 r2-eth1 ACTIVE xx:xx:xx 5/15 2 diff --git a/tests/topotests/ldp-topo1/r2/show_mpls_ldp_neighbor.ref-1 b/tests/topotests/ldp-topo1/r2/show_mpls_ldp_neighbor.ref-1 deleted file mode 100644 index a70e2f48c6..0000000000 --- a/tests/topotests/ldp-topo1/r2/show_mpls_ldp_neighbor.ref-1 +++ /dev/null @@ -1,26 +0,0 @@ -Peer LDP Identifier: 1.1.1.1:0 - TCP connection: 2.2.2.2:xxx - 1.1.1.1:xxx - Session Holdtime: 180 sec - State: OPERATIONAL; Downstream-Unsolicited - Up time: xx:xx:xx - LDP Discovery Sources: - IPv4: - Interface: r2-eth0 - -Peer LDP Identifier: 3.3.3.3:0 - TCP connection: 2.2.2.2:xxx - 3.3.3.3:xxx - Session Holdtime: 180 sec - State: OPERATIONAL; Downstream-Unsolicited - Up time: xx:xx:xx - LDP Discovery Sources: - IPv4: - Interface: r2-eth1 - -Peer LDP Identifier: 4.4.4.4:0 - TCP connection: 2.2.2.2:xxx - 4.4.4.4:xxx - Session Holdtime: 180 sec - State: OPERATIONAL; Downstream-Unsolicited - Up time: xx:xx:xx - LDP Discovery Sources: - IPv4: - Interface: r2-eth1 diff --git a/tests/topotests/ldp-topo1/r2/show_mpls_table.ref b/tests/topotests/ldp-topo1/r2/show_mpls_table.ref index 46420ccd11..df05a6b31a 100644 --- a/tests/topotests/ldp-topo1/r2/show_mpls_table.ref +++ b/tests/topotests/ldp-topo1/r2/show_mpls_table.ref @@ -1,7 +1,7 @@ - Inbound Outbound - Label Type Nexthop Label --------- ------- --------------- -------- - XX LDP 10.0.1.1 implicit-null - XX LDP 10.0.2.3 implicit-null - XX LDP 10.0.2.4 implicit-null - XX LDP 10.0.3.3 implicit-null + Inbound Label Type Nexthop Outbound Label + ----------------------------------------------- + XX LDP 10.0.1.1 implicit-null + XX LDP 10.0.2.3 implicit-null + XX LDP 10.0.2.4 implicit-null + XX LDP 10.0.3.3 implicit-null + diff --git a/tests/topotests/ldp-topo1/r2/show_mpls_table.ref-1 b/tests/topotests/ldp-topo1/r2/show_mpls_table.ref-1 deleted file mode 100644 index ba244e76ec..0000000000 --- a/tests/topotests/ldp-topo1/r2/show_mpls_table.ref-1 +++ /dev/null @@ -1,7 +0,0 @@ - Inbound Outbound - Label Type Nexthop Label --------- ------- --------------- -------- - XX LDP 10.0.1.1 3 - XX LDP 10.0.2.3 3 - XX LDP 10.0.2.4 3 - XX LDP 10.0.3.3 3 diff --git a/tests/topotests/ldp-topo1/r2/show_mpls_table.ref-no-impl-null b/tests/topotests/ldp-topo1/r2/show_mpls_table.ref-no-impl-null deleted file mode 100644 index ba244e76ec..0000000000 --- a/tests/topotests/ldp-topo1/r2/show_mpls_table.ref-no-impl-null +++ /dev/null @@ -1,7 +0,0 @@ - Inbound Outbound - Label Type Nexthop Label --------- ------- --------------- -------- - XX LDP 10.0.1.1 3 - XX LDP 10.0.2.3 3 - XX LDP 10.0.2.4 3 - XX LDP 10.0.3.3 3 diff --git a/tests/topotests/ldp-topo1/r3/how_mpls_table.ref b/tests/topotests/ldp-topo1/r3/how_mpls_table.ref deleted file mode 100644 index 18f7df0ee4..0000000000 --- a/tests/topotests/ldp-topo1/r3/how_mpls_table.ref +++ /dev/null @@ -1,10 +0,0 @@ - Inbound Outbound - Label Type Nexthop Label --------- ------- --------------- -------- - 16 LDP 10.0.2.2 3 - 16 LDP 10.0.3.2 3 - 17 LDP 10.0.2.2 3 - 17 LDP 10.0.3.2 3 - 18 LDP 10.0.2.2 17 - 18 LDP 10.0.3.2 17 - 19 LDP 10.0.2.4 3 diff --git a/tests/topotests/ldp-topo1/r3/ospfd.conf b/tests/topotests/ldp-topo1/r3/ospfd.conf index 202be238ec..bd86fe4f24 100644 --- a/tests/topotests/ldp-topo1/r3/ospfd.conf +++ b/tests/topotests/ldp-topo1/r3/ospfd.conf @@ -6,3 +6,11 @@ router ospf router-id 3.3.3.3 network 0.0.0.0/0 area 0 ! +int r3-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +int r3-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/ldp-topo1/r3/show_ipv4_route.ref b/tests/topotests/ldp-topo1/r3/show_ipv4_route.ref index 645224a97b..9b9c763339 100644 --- a/tests/topotests/ldp-topo1/r3/show_ipv4_route.ref +++ b/tests/topotests/ldp-topo1/r3/show_ipv4_route.ref @@ -1,7 +1,7 @@ -O>* 1.1.1.1/32 [110/20] via 10.0.2.2, r3-eth0, label xxx -O>* 2.2.2.2/32 [110/10] via 10.0.2.2, r3-eth0, label implicit-null -O 3.3.3.3/32 [110/0] is directly connected, lo -O>* 4.4.4.4/32 [110/10] via 10.0.2.4, r3-eth0, label implicit-null -O>* 10.0.1.0/24 [110/20] via 10.0.2.2, r3-eth0, label implicit-null -O 10.0.2.0/24 [110/10] is directly connected, r3-eth0 -O 10.0.3.0/24 [110/10] is directly connected, r3-eth1 +O>* 1.1.1.1/32 [110/20] via 10.0.2.2, r3-eth0, label xxx, weight 1 +O>* 2.2.2.2/32 [110/10] via 10.0.2.2, r3-eth0, label implicit-null, weight 1 +O 3.3.3.3/32 [110/0] is directly connected, lo, weight 1 +O>* 4.4.4.4/32 [110/10] via 10.0.2.4, r3-eth0, label implicit-null, weight 1 +O>* 10.0.1.0/24 [110/20] via 10.0.2.2, r3-eth0, label implicit-null, weight 1 +O 10.0.2.0/24 [110/10] is directly connected, r3-eth0, weight 1 +O 10.0.3.0/24 [110/10] is directly connected, r3-eth1, weight 1 diff --git a/tests/topotests/ldp-topo1/r3/show_ipv4_route.ref-1 b/tests/topotests/ldp-topo1/r3/show_ipv4_route.ref-1 deleted file mode 100644 index c8a29400b2..0000000000 --- a/tests/topotests/ldp-topo1/r3/show_ipv4_route.ref-1 +++ /dev/null @@ -1,7 +0,0 @@ -O>* 1.1.1.1/32 [110/20] via 10.0.2.2, r3-eth0, label xxx -O>* 2.2.2.2/32 [110/10] via 10.0.2.2, r3-eth0 -O 3.3.3.3/32 [110/0] is directly connected, lo -O>* 4.4.4.4/32 [110/10] via 10.0.2.4, r3-eth0 -O>* 10.0.1.0/24 [110/20] via 10.0.2.2, r3-eth0 -O 10.0.2.0/24 [110/10] is directly connected, r3-eth0 -O 10.0.3.0/24 [110/10] is directly connected, r3-eth1 diff --git a/tests/topotests/ldp-topo1/r3/show_mpls_ldp_binding.ref-1 b/tests/topotests/ldp-topo1/r3/show_mpls_ldp_binding.ref-1 deleted file mode 100644 index e04d2b7e4a..0000000000 --- a/tests/topotests/ldp-topo1/r3/show_mpls_ldp_binding.ref-1 +++ /dev/null @@ -1,49 +0,0 @@ -1.1.1.1/32 - Local binding: label: xxx - Remote bindings: - Peer Label - ----------------- --------- - 2.2.2.2 xxx - 4.4.4.4 xxx -2.2.2.2/32 - Local binding: label: xxx - Remote bindings: - Peer Label - ----------------- --------- - 2.2.2.2 imp-null - 4.4.4.4 xxx -3.3.3.3/32 - Local binding: label: imp-null - Remote bindings: - Peer Label - ----------------- --------- - 2.2.2.2 xxx - 4.4.4.4 xxx -4.4.4.4/32 - Local binding: label: xxx - Remote bindings: - Peer Label - ----------------- --------- - 2.2.2.2 xxx - 4.4.4.4 imp-null -10.0.1.0/24 - Local binding: label: xxx - Remote bindings: - Peer Label - ----------------- --------- - 2.2.2.2 imp-null - 4.4.4.4 xxx -10.0.2.0/24 - Local binding: label: imp-null - Remote bindings: - Peer Label - ----------------- --------- - 2.2.2.2 imp-null - 4.4.4.4 imp-null -10.0.3.0/24 - Local binding: label: imp-null - Remote bindings: - Peer Label - ----------------- --------- - 2.2.2.2 imp-null - 4.4.4.4 xxx diff --git a/tests/topotests/ldp-topo1/r3/show_mpls_ldp_discovery.ref-1 b/tests/topotests/ldp-topo1/r3/show_mpls_ldp_discovery.ref-1 deleted file mode 100644 index 5e299fff9c..0000000000 --- a/tests/topotests/ldp-topo1/r3/show_mpls_ldp_discovery.ref-1 +++ /dev/null @@ -1,9 +0,0 @@ -Local LDP Identifier: 3.3.3.3:0 -Discovery Sources: - Interfaces: - r3-eth0: xmit/recv - LDP Id: 2.2.2.2:0, Transport address: 2.2.2.2 - Hold time: 15 sec - LDP Id: 4.4.4.4:0, Transport address: 4.4.4.4 - Hold time: 15 sec - Targeted Hellos: diff --git a/tests/topotests/ldp-topo1/r3/show_mpls_ldp_interface.ref-1 b/tests/topotests/ldp-topo1/r3/show_mpls_ldp_interface.ref-1 deleted file mode 100644 index 243811e3a9..0000000000 --- a/tests/topotests/ldp-topo1/r3/show_mpls_ldp_interface.ref-1 +++ /dev/null @@ -1,2 +0,0 @@ -AF Interface State Uptime Hello Timers ac -ipv4 r3-eth0 ACTIVE xx:xx:xx 5/15 2 diff --git a/tests/topotests/ldp-topo1/r3/show_mpls_ldp_neighbor.ref-1 b/tests/topotests/ldp-topo1/r3/show_mpls_ldp_neighbor.ref-1 deleted file mode 100644 index ee1983ac29..0000000000 --- a/tests/topotests/ldp-topo1/r3/show_mpls_ldp_neighbor.ref-1 +++ /dev/null @@ -1,17 +0,0 @@ -Peer LDP Identifier: 2.2.2.2:0 - TCP connection: 3.3.3.3:xxx - 2.2.2.2:xxx - Session Holdtime: 180 sec - State: OPERATIONAL; Downstream-Unsolicited - Up time: xx:xx:xx - LDP Discovery Sources: - IPv4: - Interface: r3-eth0 - -Peer LDP Identifier: 4.4.4.4:0 - TCP connection: 3.3.3.3:xxx - 4.4.4.4:xxx - Session Holdtime: 180 sec - State: OPERATIONAL; Downstream-Unsolicited - Up time: xx:xx:xx - LDP Discovery Sources: - IPv4: - Interface: r3-eth0 diff --git a/tests/topotests/ldp-topo1/r3/show_mpls_table.ref b/tests/topotests/ldp-topo1/r3/show_mpls_table.ref index c367f240f4..3978895613 100644 --- a/tests/topotests/ldp-topo1/r3/show_mpls_table.ref +++ b/tests/topotests/ldp-topo1/r3/show_mpls_table.ref @@ -1,10 +1,10 @@ - Inbound Outbound - Label Type Nexthop Label --------- ------- --------------- -------- - XX LDP 10.0.2.2 XX - XX LDP 10.0.2.2 implicit-null - XX LDP 10.0.2.2 implicit-null - XX LDP 10.0.2.4 implicit-null - XX LDP 10.0.3.2 XX - XX LDP 10.0.3.2 implicit-null - XX LDP 10.0.3.2 implicit-null + Inbound Label Type Nexthop Outbound Label + ----------------------------------------------- + XX LDP 10.0.2.2 XX + XX LDP 10.0.2.2 implicit-null + XX LDP 10.0.2.2 implicit-null + XX LDP 10.0.2.4 implicit-null + XX LDP 10.0.3.2 XX + XX LDP 10.0.3.2 implicit-null + XX LDP 10.0.3.2 implicit-null + diff --git a/tests/topotests/ldp-topo1/r3/show_mpls_table.ref-1 b/tests/topotests/ldp-topo1/r3/show_mpls_table.ref-1 deleted file mode 100644 index 9198969bd5..0000000000 --- a/tests/topotests/ldp-topo1/r3/show_mpls_table.ref-1 +++ /dev/null @@ -1,10 +0,0 @@ - Inbound Outbound - Label Type Nexthop Label --------- ------- --------------- -------- - XX LDP 10.0.2.2 3 - XX LDP 10.0.2.2 3 - XX LDP 10.0.2.2 XX - XX LDP 10.0.2.4 3 - XX LDP 10.0.3.2 3 - XX LDP 10.0.3.2 3 - XX LDP 10.0.3.2 XX diff --git a/tests/topotests/ldp-topo1/r3/show_mpls_table.ref-no-impl-null b/tests/topotests/ldp-topo1/r3/show_mpls_table.ref-no-impl-null deleted file mode 100644 index 9198969bd5..0000000000 --- a/tests/topotests/ldp-topo1/r3/show_mpls_table.ref-no-impl-null +++ /dev/null @@ -1,10 +0,0 @@ - Inbound Outbound - Label Type Nexthop Label --------- ------- --------------- -------- - XX LDP 10.0.2.2 3 - XX LDP 10.0.2.2 3 - XX LDP 10.0.2.2 XX - XX LDP 10.0.2.4 3 - XX LDP 10.0.3.2 3 - XX LDP 10.0.3.2 3 - XX LDP 10.0.3.2 XX diff --git a/tests/topotests/ldp-topo1/r4/how_mpls_table.ref b/tests/topotests/ldp-topo1/r4/how_mpls_table.ref deleted file mode 100644 index 40efab8b5b..0000000000 --- a/tests/topotests/ldp-topo1/r4/how_mpls_table.ref +++ /dev/null @@ -1,9 +0,0 @@ - Inbound Outbound - Label Type Nexthop Label --------- ------- --------------- -------- - 16 LDP 10.0.2.2 17 - 17 LDP 10.0.2.2 3 - 18 LDP 10.0.2.3 3 - 19 LDP 10.0.2.2 3 - 20 LDP 10.0.2.3 3 - 20 LDP 10.0.2.2 3 diff --git a/tests/topotests/ldp-topo1/r4/ospfd.conf b/tests/topotests/ldp-topo1/r4/ospfd.conf index 569dbc54e2..5aae885a12 100644 --- a/tests/topotests/ldp-topo1/r4/ospfd.conf +++ b/tests/topotests/ldp-topo1/r4/ospfd.conf @@ -5,3 +5,7 @@ router ospf router-id 4.4.4.4 network 0.0.0.0/0 area 0 ! +int r4-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/ldp-topo1/r4/show_ipv4_route.ref b/tests/topotests/ldp-topo1/r4/show_ipv4_route.ref index 321828bfae..7444cc924b 100644 --- a/tests/topotests/ldp-topo1/r4/show_ipv4_route.ref +++ b/tests/topotests/ldp-topo1/r4/show_ipv4_route.ref @@ -1,7 +1,7 @@ -O>* 1.1.1.1/32 [110/20] via 10.0.2.2, r4-eth0, label xxx -O>* 2.2.2.2/32 [110/10] via 10.0.2.2, r4-eth0, label implicit-null -O>* 3.3.3.3/32 [110/10] via 10.0.2.3, r4-eth0, label implicit-null -O 4.4.4.4/32 [110/0] is directly connected, lo -O>* 10.0.1.0/24 [110/20] via 10.0.2.2, r4-eth0, label implicit-null -O 10.0.2.0/24 [110/10] is directly connected, r4-eth0 -O>* 10.0.3.0/24 [110/20] via 10.0.2.2, r4-eth0, label implicit-null +O>* 1.1.1.1/32 [110/20] via 10.0.2.2, r4-eth0, label xxx, weight 1 +O>* 2.2.2.2/32 [110/10] via 10.0.2.2, r4-eth0, label implicit-null, weight 1 +O>* 3.3.3.3/32 [110/10] via 10.0.2.3, r4-eth0, label implicit-null, weight 1 +O 4.4.4.4/32 [110/0] is directly connected, lo, weight 1 +O>* 10.0.1.0/24 [110/20] via 10.0.2.2, r4-eth0, label implicit-null, weight 1 +O 10.0.2.0/24 [110/10] is directly connected, r4-eth0, weight 1 +O>* 10.0.3.0/24 [110/20] via 10.0.2.2, r4-eth0, label implicit-null, weight 1 diff --git a/tests/topotests/ldp-topo1/r4/show_ipv4_route.ref-1 b/tests/topotests/ldp-topo1/r4/show_ipv4_route.ref-1 deleted file mode 100644 index df2a2b585f..0000000000 --- a/tests/topotests/ldp-topo1/r4/show_ipv4_route.ref-1 +++ /dev/null @@ -1,7 +0,0 @@ -O>* 1.1.1.1/32 [110/20] via 10.0.2.2, r4-eth0, label xxx -O>* 2.2.2.2/32 [110/10] via 10.0.2.2, r4-eth0 -O>* 3.3.3.3/32 [110/10] via 10.0.2.3, r4-eth0 -O 4.4.4.4/32 [110/0] is directly connected, lo -O>* 10.0.1.0/24 [110/20] via 10.0.2.2, r4-eth0 -O 10.0.2.0/24 [110/10] is directly connected, r4-eth0 -O>* 10.0.3.0/24 [110/20] via 10.0.2.2, r4-eth0 diff --git a/tests/topotests/ldp-topo1/r4/show_mpls_ldp_binding.ref-1 b/tests/topotests/ldp-topo1/r4/show_mpls_ldp_binding.ref-1 deleted file mode 100644 index 3d55805d7c..0000000000 --- a/tests/topotests/ldp-topo1/r4/show_mpls_ldp_binding.ref-1 +++ /dev/null @@ -1,49 +0,0 @@ -1.1.1.1/32 - Local binding: label: xxx - Remote bindings: - Peer Label - ----------------- --------- - 2.2.2.2 xxx - 3.3.3.3 xxx -2.2.2.2/32 - Local binding: label: xxx - Remote bindings: - Peer Label - ----------------- --------- - 2.2.2.2 imp-null - 3.3.3.3 xxx -3.3.3.3/32 - Local binding: label: xxx - Remote bindings: - Peer Label - ----------------- --------- - 2.2.2.2 xxx - 3.3.3.3 imp-null -4.4.4.4/32 - Local binding: label: imp-null - Remote bindings: - Peer Label - ----------------- --------- - 2.2.2.2 xxx - 3.3.3.3 xxx -10.0.1.0/24 - Local binding: label: xxx - Remote bindings: - Peer Label - ----------------- --------- - 2.2.2.2 imp-null - 3.3.3.3 xxx -10.0.2.0/24 - Local binding: label: imp-null - Remote bindings: - Peer Label - ----------------- --------- - 2.2.2.2 imp-null - 3.3.3.3 imp-null -10.0.3.0/24 - Local binding: label: xxx - Remote bindings: - Peer Label - ----------------- --------- - 2.2.2.2 imp-null - 3.3.3.3 imp-null diff --git a/tests/topotests/ldp-topo1/r4/show_mpls_ldp_discovery.ref-1 b/tests/topotests/ldp-topo1/r4/show_mpls_ldp_discovery.ref-1 deleted file mode 100644 index 3ebddd606a..0000000000 --- a/tests/topotests/ldp-topo1/r4/show_mpls_ldp_discovery.ref-1 +++ /dev/null @@ -1,9 +0,0 @@ -Local LDP Identifier: 4.4.4.4:0 -Discovery Sources: - Interfaces: - r4-eth0: xmit/recv - LDP Id: 2.2.2.2:0, Transport address: 2.2.2.2 - Hold time: 15 sec - LDP Id: 3.3.3.3:0, Transport address: 3.3.3.3 - Hold time: 15 sec - Targeted Hellos: diff --git a/tests/topotests/ldp-topo1/r4/show_mpls_ldp_interface.ref-1 b/tests/topotests/ldp-topo1/r4/show_mpls_ldp_interface.ref-1 deleted file mode 100644 index dd57656f15..0000000000 --- a/tests/topotests/ldp-topo1/r4/show_mpls_ldp_interface.ref-1 +++ /dev/null @@ -1,2 +0,0 @@ -AF Interface State Uptime Hello Timers ac -ipv4 r4-eth0 ACTIVE xx:xx:xx 5/15 2 diff --git a/tests/topotests/ldp-topo1/r4/show_mpls_ldp_neighbor.ref-1 b/tests/topotests/ldp-topo1/r4/show_mpls_ldp_neighbor.ref-1 deleted file mode 100644 index fb0e7d7dfa..0000000000 --- a/tests/topotests/ldp-topo1/r4/show_mpls_ldp_neighbor.ref-1 +++ /dev/null @@ -1,17 +0,0 @@ -Peer LDP Identifier: 2.2.2.2:0 - TCP connection: 4.4.4.4:xxx - 2.2.2.2:xxx - Session Holdtime: 180 sec - State: OPERATIONAL; Downstream-Unsolicited - Up time: xx:xx:xx - LDP Discovery Sources: - IPv4: - Interface: r4-eth0 - -Peer LDP Identifier: 3.3.3.3:0 - TCP connection: 4.4.4.4:xxx - 3.3.3.3:xxx - Session Holdtime: 180 sec - State: OPERATIONAL; Downstream-Unsolicited - Up time: xx:xx:xx - LDP Discovery Sources: - IPv4: - Interface: r4-eth0 diff --git a/tests/topotests/ldp-topo1/r4/show_mpls_table.ref b/tests/topotests/ldp-topo1/r4/show_mpls_table.ref index 9f86cd67cc..174dcebd4d 100644 --- a/tests/topotests/ldp-topo1/r4/show_mpls_table.ref +++ b/tests/topotests/ldp-topo1/r4/show_mpls_table.ref @@ -1,9 +1,9 @@ - Inbound Outbound - Label Type Nexthop Label --------- ------- --------------- -------- - XX LDP 10.0.2.2 XX - XX LDP 10.0.2.2 implicit-null - XX LDP 10.0.2.2 implicit-null - XX LDP 10.0.2.2 implicit-null - XX LDP 10.0.2.3 implicit-null - XX LDP 10.0.2.3 implicit-null + Inbound Label Type Nexthop Outbound Label + ----------------------------------------------- + XX LDP 10.0.2.2 XX + XX LDP 10.0.2.2 implicit-null + XX LDP 10.0.2.2 implicit-null + XX LDP 10.0.2.2 implicit-null + XX LDP 10.0.2.3 implicit-null + XX LDP 10.0.2.3 implicit-null + diff --git a/tests/topotests/ldp-topo1/r4/show_mpls_table.ref-1 b/tests/topotests/ldp-topo1/r4/show_mpls_table.ref-1 deleted file mode 100644 index b8cf5a2702..0000000000 --- a/tests/topotests/ldp-topo1/r4/show_mpls_table.ref-1 +++ /dev/null @@ -1,9 +0,0 @@ - Inbound Outbound - Label Type Nexthop Label --------- ------- --------------- -------- - XX LDP 10.0.2.2 3 - XX LDP 10.0.2.2 3 - XX LDP 10.0.2.2 3 - XX LDP 10.0.2.2 XX - XX LDP 10.0.2.3 3 - XX LDP 10.0.2.3 3 diff --git a/tests/topotests/ldp-topo1/r4/show_mpls_table.ref-no-impl-null b/tests/topotests/ldp-topo1/r4/show_mpls_table.ref-no-impl-null deleted file mode 100644 index b8cf5a2702..0000000000 --- a/tests/topotests/ldp-topo1/r4/show_mpls_table.ref-no-impl-null +++ /dev/null @@ -1,9 +0,0 @@ - Inbound Outbound - Label Type Nexthop Label --------- ------- --------------- -------- - XX LDP 10.0.2.2 3 - XX LDP 10.0.2.2 3 - XX LDP 10.0.2.2 3 - XX LDP 10.0.2.2 XX - XX LDP 10.0.2.3 3 - XX LDP 10.0.2.3 3 diff --git a/tests/topotests/ldp-topo1/test_ldp_topo1.py b/tests/topotests/ldp-topo1/test_ldp_topo1.py index 409a5f54c8..cef4d6587e 100755 --- a/tests/topotests/ldp-topo1/test_ldp_topo1.py +++ b/tests/topotests/ldp-topo1/test_ldp_topo1.py @@ -57,7 +57,7 @@ | r3 | | r4 | | 3.3.3.3 | | 4.4.4.4 | +-----------+ +---------+ -""" +""" import os import re @@ -77,18 +77,13 @@ fatal_error = "" -# Expected version of CLI Output - Appendix to filename -# empty string = current, latest output (default) -# "-1" ... "-NNN" previous versions (incrementing with each version) -cli_version = "" - - ##################################################### ## ## Network Topology Definition ## ##################################################### + class NetworkTopo(Topo): "LDP Test Topology 1" @@ -97,23 +92,65 @@ def build(self, **_opts): # Setup Routers router = {} for i in range(1, 5): - router[i] = topotest.addRouter(self, 'r%s' % i) + router[i] = topotest.addRouter(self, "r%s" % i) # Setup Switches, add Interfaces and Connections switch = {} # First switch - switch[0] = self.addSwitch('sw0', cls=topotest.LegacySwitch) - self.addLink(switch[0], router[1], intfName2='r1-eth0', addr1='80:AA:00:00:00:00', addr2='00:11:00:01:00:00') - self.addLink(switch[0], router[2], intfName2='r2-eth0', addr1='80:AA:00:00:00:01', addr2='00:11:00:02:00:00') + switch[0] = self.addSwitch("sw0", cls=topotest.LegacySwitch) + self.addLink( + switch[0], + router[1], + intfName2="r1-eth0", + addr1="80:AA:00:00:00:00", + addr2="00:11:00:01:00:00", + ) + self.addLink( + switch[0], + router[2], + intfName2="r2-eth0", + addr1="80:AA:00:00:00:01", + addr2="00:11:00:02:00:00", + ) # Second switch - switch[1] = self.addSwitch('sw1', cls=topotest.LegacySwitch) - self.addLink(switch[1], router[2], intfName2='r2-eth1', addr1='80:AA:00:01:00:00', addr2='00:11:00:02:00:01') - self.addLink(switch[1], router[3], intfName2='r3-eth0', addr1='80:AA:00:01:00:01', addr2='00:11:00:03:00:00') - self.addLink(switch[1], router[4], intfName2='r4-eth0', addr1='80:AA:00:01:00:02', addr2='00:11:00:04:00:00') + switch[1] = self.addSwitch("sw1", cls=topotest.LegacySwitch) + self.addLink( + switch[1], + router[2], + intfName2="r2-eth1", + addr1="80:AA:00:01:00:00", + addr2="00:11:00:02:00:01", + ) + self.addLink( + switch[1], + router[3], + intfName2="r3-eth0", + addr1="80:AA:00:01:00:01", + addr2="00:11:00:03:00:00", + ) + self.addLink( + switch[1], + router[4], + intfName2="r4-eth0", + addr1="80:AA:00:01:00:02", + addr2="00:11:00:04:00:00", + ) # Third switch - switch[2] = self.addSwitch('sw2', cls=topotest.LegacySwitch) - self.addLink(switch[2], router[2], intfName2='r2-eth2', addr1='80:AA:00:02:00:00', addr2='00:11:00:02:00:02') - self.addLink(switch[2], router[3], intfName2='r3-eth1', addr1='80:AA:00:02:00:01', addr2='00:11:00:03:00:01') + switch[2] = self.addSwitch("sw2", cls=topotest.LegacySwitch) + self.addLink( + switch[2], + router[2], + intfName2="r2-eth2", + addr1="80:AA:00:02:00:00", + addr2="00:11:00:02:00:02", + ) + self.addLink( + switch[2], + router[3], + intfName2="r3-eth1", + addr1="80:AA:00:02:00:01", + addr2="00:11:00:03:00:01", + ) ##################################################### @@ -122,6 +159,7 @@ def build(self, **_opts): ## ##################################################### + def setup_module(module): global topo, net global fatal_error @@ -130,7 +168,7 @@ def setup_module(module): print("******************************************\n") print("Cleanup old Mininet runs") - os.system('sudo mn -c > /dev/null 2>&1') + os.system("sudo mn -c > /dev/null 2>&1") thisDir = os.path.dirname(os.path.realpath(__file__)) topo = NetworkTopo() @@ -140,10 +178,10 @@ def setup_module(module): # Starting Routers for i in range(1, 5): - net['r%s' % i].loadConf('zebra', '%s/r%s/zebra.conf' % (thisDir, i)) - net['r%s' % i].loadConf('ospfd', '%s/r%s/ospfd.conf' % (thisDir, i)) - net['r%s' % i].loadConf('ldpd', '%s/r%s/ldpd.conf' % (thisDir, i)) - fatal_error = net['r%s' % i].startRouter() + net["r%s" % i].loadConf("zebra", "%s/r%s/zebra.conf" % (thisDir, i)) + net["r%s" % i].loadConf("ospfd", "%s/r%s/ospfd.conf" % (thisDir, i)) + net["r%s" % i].loadConf("ldpd", "%s/r%s/ldpd.conf" % (thisDir, i)) + fatal_error = net["r%s" % i].startRouter() if fatal_error != "": break @@ -151,6 +189,7 @@ def setup_module(module): # For debugging after starting FRR/Quagga daemons, uncomment the next line # CLI(net) + def teardown_module(module): global net @@ -164,10 +203,9 @@ def teardown_module(module): def test_router_running(): global fatal_error global net - global cli_version # Skip if previous fatal error condition is raised - if (fatal_error != ""): + if fatal_error != "": pytest.skip(fatal_error) print("\n\n** Check if FRR/Quagga is running on each Router node") @@ -176,84 +214,73 @@ def test_router_running(): # Starting Routers for i in range(1, 5): - fatal_error = net['r%s' % i].checkRouterRunning() + fatal_error = net["r%s" % i].checkRouterRunning() assert fatal_error == "", fatal_error - # Detect CLI Version - # At this time, there are only 2 possible outputs, so simple check - output = net['r1'].cmd('vtysh -c "show mpls ldp discovery" 2> /dev/null').rstrip() - - # Check if old or new format of CLI Output. Default is to current format - # - # Old (v1) output looks like this: - # Local LDP Identifier: 1.1.1.1:0 - # Discovery Sources: - # Interfaces: - # r1-eth0: xmit/recv - # LDP Id: 2.2.2.2:0, Transport address: 2.2.2.2 - # Hold time: 15 sec - # Targeted Hellos: - # - # Current (v0) output looks like this: - # AF ID Type Source Holdtime - # ipv4 2.2.2.2 Link r1-eth0 15 - pattern = re.compile("^Local LDP Identifier.*") - if pattern.match(output): - cli_version = "-1" - # For debugging after starting FRR/Quagga daemons, uncomment the next line # CLI(net) + def test_mpls_interfaces(): global fatal_error global net - global cli_version # Skip if previous fatal error condition is raised - if (fatal_error != ""): + if fatal_error != "": pytest.skip(fatal_error) thisDir = os.path.dirname(os.path.realpath(__file__)) - # Verify OSPFv3 Routing Table + # Verify MPLS Interfaces print("\n\n** Verifying MPLS Interfaces") print("******************************************\n") failures = 0 for i in range(1, 5): - refTableFile = '%s/r%s/show_mpls_ldp_interface.ref%s' % (thisDir, i, cli_version) + refTableFile = "%s/r%s/show_mpls_ldp_interface.ref" 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) + expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1) # Actual output from router - actual = net['r%s' % i].cmd('vtysh -c "show mpls ldp interface" 2> /dev/null').rstrip() + actual = ( + net["r%s" % i] + .cmd('vtysh -c "show mpls ldp interface" 2> /dev/null') + .rstrip() + ) # Mask out Timer in Uptime actual = re.sub(r" [0-9][0-9]:[0-9][0-9]:[0-9][0-9] ", " xx:xx:xx ", actual) # Fix newlines (make them all the same) - actual = ('\n'.join(actual.splitlines()) + '\n').splitlines(1) + actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1) # Generate Diff - diff = topotest.get_textdiff(actual, expected, + diff = topotest.get_textdiff( + actual, + expected, title1="actual MPLS LDP interface status", - title2="expected MPLS LDP interface status") + title2="expected MPLS LDP interface status", + ) # Empty string if it matches, otherwise diff contains unified diff if diff: - sys.stderr.write('r%s failed MPLS LDP Interface status Check:\n%s\n' % (i, diff)) + sys.stderr.write( + "r%s failed MPLS LDP Interface status Check:\n%s\n" % (i, diff) + ) failures += 1 else: print("r%s ok" % i) - if failures>0: + if failures > 0: fatal_error = "MPLS LDP Interface status failed" - assert failures == 0, "MPLS LDP Interface status failed for router r%s:\n%s" % (i, diff) + assert ( + failures == 0 + ), "MPLS LDP Interface status failed for router r%s:\n%s" % (i, diff) # Make sure that all daemons are running for i in range(1, 5): - fatal_error = net['r%s' % i].checkRouterRunning() + fatal_error = net["r%s" % i].checkRouterRunning() assert fatal_error == "", fatal_error # For debugging after starting FRR/Quagga daemons, uncomment the next line @@ -263,13 +290,12 @@ def test_mpls_interfaces(): def test_mpls_ldp_neighbor_establish(): global fatal_error global net - global cli_version # Skip if previous fatal error condition is raised - if (fatal_error != ""): + if fatal_error != "": pytest.skip(fatal_error) - # Wait for OSPF6 to converge (All Neighbors in either Full or TwoWay State) + # Wait for MPLS LDP neighbors to establish. print("\n\n** Verify MPLS LDP neighbors to establish") print("******************************************\n") timeout = 90 @@ -278,32 +304,38 @@ def test_mpls_ldp_neighbor_establish(): sys.stdout.flush() # Look for any node not yet converged for i in range(1, 5): - established = net['r%s' % i].cmd('vtysh -c "show mpls ldp neighbor" 2> /dev/null').rstrip() - if cli_version != "-1": - # On current version, we need to make sure they all turn to OPERATIONAL on all lines - # - lines = ('\n'.join(established.splitlines()) + '\n').splitlines(1) - # Check all lines to be either table header (starting with ^AF or show OPERATIONAL) - header = r'^AF.*' - operational = r'^ip.*OPERATIONAL.*' - found_operational = 0 - for j in range(1, len(lines)): - if (not re.search(header, lines[j])) and (not re.search(operational, lines[j])): - established = "" # Empty string shows NOT established - if re.search(operational, lines[j]): - found_operational += 1 - if found_operational < 1: - # Need at least one operational neighbor + established = ( + net["r%s" % i] + .cmd('vtysh -c "show mpls ldp neighbor" 2> /dev/null') + .rstrip() + ) + + # On current version, we need to make sure they all turn to OPERATIONAL on all lines + # + lines = ("\n".join(established.splitlines()) + "\n").splitlines(1) + # Check all lines to be either table header (starting with ^AF or show OPERATIONAL) + header = r"^AF.*" + operational = r"^ip.*OPERATIONAL.*" + found_operational = 0 + for j in range(1, len(lines)): + if (not re.search(header, lines[j])) and ( + not re.search(operational, lines[j]) + ): established = "" # Empty string shows NOT established + if re.search(operational, lines[j]): + found_operational += 1 + if found_operational < 1: + # Need at least one operational neighbor + established = "" # Empty string shows NOT established if not established: - print('Waiting for r%s' %i) + print("Waiting for r%s" % i) sys.stdout.flush() break if not established: sleep(5) timeout -= 5 else: - print('Done') + print("Done") break else: # Bail out with error if a router fails to converge @@ -316,62 +348,76 @@ def test_mpls_ldp_neighbor_establish(): # Only wait if we actually went through a convergence print("\nwaiting 15s for LDP sessions to establish") sleep(15) - + # Make sure that all daemons are running for i in range(1, 5): - fatal_error = net['r%s' % i].checkRouterRunning() + fatal_error = net["r%s" % i].checkRouterRunning() assert fatal_error == "", fatal_error def test_mpls_ldp_discovery(): global fatal_error global net - global cli_version # Skip if previous fatal error condition is raised - if (fatal_error != ""): + if fatal_error != "": pytest.skip(fatal_error) thisDir = os.path.dirname(os.path.realpath(__file__)) - # Verify OSPFv3 Routing Table + # Verify MPLS LDP discovery print("\n\n** Verifying MPLS LDP discovery") print("******************************************\n") failures = 0 for i in range(1, 5): - refTableFile = '%s/r%s/show_mpls_ldp_discovery.ref%s' % (thisDir, i, cli_version) + refTableFile = "%s/r%s/show_mpls_ldp_discovery.ref" % (thisDir, i) if os.path.isfile(refTableFile): # Actual output from router - actual = net['r%s' % i].cmd('vtysh -c "show mpls ldp discovery" 2> /dev/null').rstrip() + actual = ( + net["r%s" % i] + .cmd('vtysh -c "show mpls ldp discovery" 2> /dev/null') + .rstrip() + ) # 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) + expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1) # Actual output from router - actual = net['r%s' % i].cmd('vtysh -c "show mpls ldp discovery" 2> /dev/null').rstrip() + actual = ( + net["r%s" % i] + .cmd('vtysh -c "show mpls ldp discovery" 2> /dev/null') + .rstrip() + ) # Fix newlines (make them all the same) - actual = ('\n'.join(actual.splitlines()) + '\n').splitlines(1) + actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1) # Generate Diff - diff = topotest.get_textdiff(actual, expected, + diff = topotest.get_textdiff( + actual, + expected, title1="actual MPLS LDP discovery output", - title2="expected MPLS LDP discovery output") + title2="expected MPLS LDP discovery output", + ) # Empty string if it matches, otherwise diff contains unified diff if diff: - sys.stderr.write('r%s failed MPLS LDP discovery output Check:\n%s\n' % (i, diff)) + sys.stderr.write( + "r%s failed MPLS LDP discovery output Check:\n%s\n" % (i, diff) + ) failures += 1 else: print("r%s ok" % i) - assert failures == 0, "MPLS LDP Interface discovery output for router r%s:\n%s" % (i, diff) + assert ( + failures == 0 + ), "MPLS LDP Interface discovery output for router r%s:\n%s" % (i, diff) # Make sure that all daemons are running for i in range(1, 5): - fatal_error = net['r%s' % i].checkRouterRunning() + fatal_error = net["r%s" % i].checkRouterRunning() assert fatal_error == "", fatal_error # For debugging after starting FRR/Quagga daemons, uncomment the next line @@ -381,172 +427,191 @@ def test_mpls_ldp_discovery(): def test_mpls_ldp_neighbor(): global fatal_error global net - global cli_version # Skip if previous fatal error condition is raised - if (fatal_error != ""): + if fatal_error != "": pytest.skip(fatal_error) thisDir = os.path.dirname(os.path.realpath(__file__)) - # Verify OSPFv3 Routing Table + # Verify MPLS LDP neighbor print("\n\n** Verifying MPLS LDP neighbor") print("******************************************\n") failures = 0 for i in range(1, 5): - refTableFile = '%s/r%s/show_mpls_ldp_neighbor.ref%s' % (thisDir, i, cli_version) + refTableFile = "%s/r%s/show_mpls_ldp_neighbor.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) + expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1) # Actual output from router - actual = net['r%s' % i].cmd('vtysh -c "show mpls ldp neighbor" 2> /dev/null').rstrip() + actual = ( + net["r%s" % i] + .cmd('vtysh -c "show mpls ldp neighbor" 2> /dev/null') + .rstrip() + ) # Mask out changing parts in output - if cli_version == "-1": - # Mask out Timer in Uptime - actual = re.sub(r"Up time: [0-9][0-9]:[0-9][0-9]:[0-9][0-9]", "Up time: xx:xx:xx", actual) - # Mask out Port numbers in TCP connection - actual = re.sub(r"TCP connection: ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]):[0-9]+ - ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]):[0-9]+", - r"TCP connection: \1:xxx - \2:xxx", actual) - else: - # Current Version - # - # Mask out Timer in Uptime - actual = re.sub(r"(ipv4 [0-9\.]+ +OPERATIONAL [0-9\.]+ +)[0-9][0-9]:[0-9][0-9]:[0-9][0-9]", r"\1xx:xx:xx", actual) + # Mask out Timer in Uptime + actual = re.sub( + r"(ipv4 [0-9\.]+ +OPERATIONAL [0-9\.]+ +)[0-9][0-9]:[0-9][0-9]:[0-9][0-9]", + r"\1xx:xx:xx", + actual, + ) # Fix newlines (make them all the same) - actual = ('\n'.join(actual.splitlines()) + '\n').splitlines(1) + actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1) # Generate Diff - diff = topotest.get_textdiff(actual, expected, + diff = topotest.get_textdiff( + actual, + expected, title1="actual MPLS LDP neighbor output", - title2="expected MPLS LDP neighbor output") + title2="expected MPLS LDP neighbor output", + ) # Empty string if it matches, otherwise diff contains unified diff if diff: - sys.stderr.write('r%s failed MPLS LDP neighbor output Check:\n%s\n' % (i, diff)) + sys.stderr.write( + "r%s failed MPLS LDP neighbor output Check:\n%s\n" % (i, diff) + ) failures += 1 else: print("r%s ok" % i) - assert failures == 0, "MPLS LDP Interface neighbor output for router r%s:\n%s" % (i, diff) + assert ( + failures == 0 + ), "MPLS LDP Interface neighbor output for router r%s:\n%s" % (i, diff) # Make sure that all daemons are running for i in range(1, 5): - fatal_error = net['r%s' % i].checkRouterRunning() + fatal_error = net["r%s" % i].checkRouterRunning() assert fatal_error == "", fatal_error # For debugging after starting FRR/Quagga daemons, uncomment the next line - #CLI(net) + # CLI(net) def test_mpls_ldp_binding(): global fatal_error global net - global cli_version # Skip this test for now until proper sorting of the output # is implemented # pytest.skip("Skipping test_mpls_ldp_binding") # Skip if previous fatal error condition is raised - if (fatal_error != ""): + if fatal_error != "": pytest.skip(fatal_error) thisDir = os.path.dirname(os.path.realpath(__file__)) - # Verify OSPFv3 Routing Table + # Verify MPLS LDP binding print("\n\n** Verifying MPLS LDP binding") print("******************************************\n") failures = 0 for i in range(1, 5): - refTableFile = '%s/r%s/show_mpls_ldp_binding.ref%s' % (thisDir, i, cli_version) + refTableFile = "%s/r%s/show_mpls_ldp_binding.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) + expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1) # Actual output from router - actual = net['r%s' % i].cmd('vtysh -c "show mpls ldp binding" 2> /dev/null').rstrip() + actual = ( + net["r%s" % i] + .cmd('vtysh -c "show mpls ldp binding" 2> /dev/null') + .rstrip() + ) # Mask out changing parts in output - if cli_version == "-1": - # Mask out label - actual = re.sub(r"label: [0-9]+", "label: xxx", actual) - actual = re.sub(r"(\s+[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+[ ]+)[0-9]+", r"\1xxx", actual) - else: - # Current Version - # - # Mask out label - actual = re.sub(r"(ipv4 [0-9\./]+ +[0-9\.]+ +)[0-9][0-9] (.*)", r"\1xxx\2", actual) - actual = re.sub(r"(ipv4 [0-9\./]+ +[0-9\.]+ +[a-z\-]+ +)[0-9][0-9] (.*)", r"\1xxx\2", actual) + # Mask out label + actual = re.sub( + r"(ipv4 [0-9\./]+ +[0-9\.]+ +)[0-9][0-9] (.*)", r"\1xxx\2", actual + ) + actual = re.sub( + r"(ipv4 [0-9\./]+ +[0-9\.]+ +[a-z\-]+ +)[0-9][0-9] (.*)", + r"\1xxx\2", + actual, + ) # Fix newlines (make them all the same) - actual = ('\n'.join(actual.splitlines()) + '\n').splitlines(1) + actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1) # Sort lines which start with "xx via inet " - pattern = r'^\s+[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+\s+' + pattern = r"^\s+[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+\s+" swapped = True while swapped: swapped = False for j in range(1, len(actual)): - if re.search(pattern, actual[j]) and re.search(pattern, actual[j-1]): - if actual[j-1] > actual[j]: - temp = actual[j-1] - actual[j-1] = actual[j] + if re.search(pattern, actual[j]) and re.search( + pattern, actual[j - 1] + ): + if actual[j - 1] > actual[j]: + temp = actual[j - 1] + actual[j - 1] = actual[j] actual[j] = temp swapped = True # Generate Diff - diff = topotest.get_textdiff(actual, expected, + diff = topotest.get_textdiff( + actual, + expected, title1="actual MPLS LDP binding output", - title2="expected MPLS LDP binding output") + title2="expected MPLS LDP binding output", + ) # Empty string if it matches, otherwise diff contains unified diff if diff: - sys.stderr.write('r%s failed MPLS LDP binding output Check:\n%s\n' % (i, diff)) + sys.stderr.write( + "r%s failed MPLS LDP binding output Check:\n%s\n" % (i, diff) + ) failures += 1 else: print("r%s ok" % i) - assert failures == 0, "MPLS LDP Interface binding output for router r%s:\n%s" % (i, diff) + assert ( + failures == 0 + ), "MPLS LDP Interface binding output for router r%s:\n%s" % (i, diff) # Make sure that all daemons are running for i in range(1, 5): - fatal_error = net['r%s' % i].checkRouterRunning() + fatal_error = net["r%s" % i].checkRouterRunning() assert fatal_error == "", fatal_error # For debugging after starting FRR/Quagga daemons, uncomment the next line - #CLI(net) + # CLI(net) def test_zebra_ipv4_routingTable(): global fatal_error global net - global cli_version # Skip if previous fatal error condition is raised - if (fatal_error != ""): + if fatal_error != "": pytest.skip(fatal_error) thisDir = os.path.dirname(os.path.realpath(__file__)) - # Verify OSPFv3 Routing Table + # Verify Zebra IPv4 Routing Table print("\n\n** Verifying Zebra IPv4 Routing Table") print("******************************************\n") failures = 0 for i in range(1, 5): - refTableFile = '%s/r%s/show_ipv4_route.ref%s' % (thisDir, i, cli_version) + refTableFile = "%s/r%s/show_ipv4_route.ref" % (thisDir, i) if os.path.isfile(refTableFile): # Read expected result from file expected = open(refTableFile).read().rstrip() # Actual output from router - actual = net['r%s' % i].cmd('vtysh -c "show ip route" 2> /dev/null | grep "^O"').rstrip() + actual = ( + net["r%s" % i] + .cmd('vtysh -c "show ip route" 2> /dev/null | grep "^O"') + .rstrip() + ) # Drop timers on end of line (older Quagga Versions) actual = re.sub(r", [0-2][0-9]:[0-5][0-9]:[0-5][0-9]", "", actual) @@ -556,35 +621,40 @@ def test_zebra_ipv4_routingTable(): # and translating remaining implicit (single-digit) labels to label implicit-null actual = re.sub(r" label [0-9]+", " label implicit-null", actual) # Check if we have implicit labels - if not, then remove them from reference - if (not re.search(r" label implicit-null", actual)): + if not re.search(r" label implicit-null", actual): expected = re.sub(r", label implicit-null", "", expected) # now fix newlines of expected (make them all the same) - expected = ('\n'.join(expected.splitlines()) + '\n').splitlines(1) - - # Add missing comma before label (for old version) - actual = re.sub(r"([0-9]) label ", r"\1, label ", actual) + expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1) # Fix newlines (make them all the same) - actual = ('\n'.join(actual.splitlines()) + '\n').splitlines(1) + actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1) # Generate Diff - diff = topotest.get_textdiff(actual, expected, + diff = topotest.get_textdiff( + actual, + expected, title1="actual IPv4 zebra routing table", - title2="expected IPv4 zebra routing table") + title2="expected IPv4 zebra routing table", + ) # Empty string if it matches, otherwise diff contains unified diff if diff: - sys.stderr.write('r%s failed IPv4 Zebra Routing Table Check:\n%s\n' % (i, diff)) + sys.stderr.write( + "r%s failed IPv4 Zebra Routing Table Check:\n%s\n" % (i, diff) + ) failures += 1 else: print("r%s ok" % i) - assert failures == 0, "IPv4 Zebra Routing Table verification failed for router r%s:\n%s" % (i, diff) + assert failures == 0, ( + "IPv4 Zebra Routing Table verification failed for router r%s:\n%s" + % (i, diff) + ) # Make sure that all daemons are running for i in range(1, 5): - fatal_error = net['r%s' % i].checkRouterRunning() + fatal_error = net["r%s" % i].checkRouterRunning() assert fatal_error == "", fatal_error # For debugging after starting FRR/Quagga daemons, uncomment the next line @@ -594,66 +664,67 @@ def test_zebra_ipv4_routingTable(): def test_mpls_table(): global fatal_error global net - global cli_version # Skip if previous fatal error condition is raised - if (fatal_error != ""): + if fatal_error != "": pytest.skip(fatal_error) thisDir = os.path.dirname(os.path.realpath(__file__)) - # Verify OSPFv3 Routing Table + # Verify MPLS table print("\n\n** Verifying MPLS table") print("******************************************\n") failures = 0 - version = cli_version - if (version == ""): - # check for new output without implicit-null - output = net['r1'].cmd('vtysh -c "show mpls table" 2> /dev/null').rstrip() - if 'LDP 10.0.1.2 3' in output: - version = "-no-impl-null" - for i in range(1, 5): - refTableFile = '%s/r%s/show_mpls_table.ref%s' % (thisDir, i, version) + refTableFile = "%s/r%s/show_mpls_table.ref" % (thisDir, i) if os.path.isfile(refTableFile): # Read expected result from file - expected = open(refTableFile).read().rstrip() + expected = open(refTableFile).read() # Fix newlines (make them all the same) - expected = ('\n'.join(expected.splitlines()) + '\n').splitlines(1) + expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1) # Actual output from router - actual = net['r%s' % i].cmd('vtysh -c "show mpls table" 2> /dev/null').rstrip() - + actual = net["r%s" % i].cmd('vtysh -c "show mpls table" 2> /dev/null') + # Fix inconsistent Label numbers at beginning of line actual = re.sub(r"(\s+)[0-9]+(\s+LDP)", r"\1XX\2", actual) # Fix inconsistent Label numbers at end of line - actual = re.sub(r"(\s+[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+\s+)[0-9][0-9]", r"\1XX", actual) + actual = re.sub( + r"(\s+[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+\s+)[0-9][0-9]", r"\1XX", actual + ) # Fix newlines (make them all the same) - actual = ('\n'.join(actual.splitlines()) + '\n').splitlines(1) + actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1) # Sort lines which start with " XX LDP" - pattern = r'^\s+[0-9X]+\s+LDP' + pattern = r"^\s+[0-9X]+\s+LDP" swapped = True while swapped: swapped = False for j in range(1, len(actual)): - if re.search(pattern, actual[j]) and re.search(pattern, actual[j-1]): - if actual[j-1] > actual[j]: - temp = actual[j-1] - actual[j-1] = actual[j] + if re.search(pattern, actual[j]) and re.search( + pattern, actual[j - 1] + ): + if actual[j - 1] > actual[j]: + temp = actual[j - 1] + actual[j - 1] = actual[j] actual[j] = temp swapped = True # Generate Diff - diff = topotest.get_textdiff(actual, expected, + diff = topotest.get_textdiff( + actual, + expected, title1="actual MPLS table output", - title2="expected MPLS table output") + title2="expected MPLS table output", + ) # Empty string if it matches, otherwise diff contains unified diff if diff: - sys.stderr.write('r%s failed MPLS table output Check:\n%s\n' % (i, diff)) + sys.stderr.write( + "r%s failed MPLS table output Check:\n%s\n" % (i, diff) + ) failures += 1 else: print("r%s ok" % i) @@ -662,7 +733,7 @@ def test_mpls_table(): # Make sure that all daemons are running for i in range(1, 5): - fatal_error = net['r%s' % i].checkRouterRunning() + fatal_error = net["r%s" % i].checkRouterRunning() assert fatal_error == "", fatal_error # For debugging after starting FRR/Quagga daemons, uncomment the next line @@ -672,28 +743,29 @@ def test_mpls_table(): def test_linux_mpls_routes(): global fatal_error global net - global cli_version - # Skip if previous fatal error condition is raised - if (fatal_error != ""): + # Skip if previous fatal error condition is raised + if fatal_error != "": pytest.skip(fatal_error) thisDir = os.path.dirname(os.path.realpath(__file__)) - # Verify OSPFv3 Routing Table + # Verify Linux Kernel MPLS routes print("\n\n** Verifying Linux Kernel MPLS routes") print("******************************************\n") failures = 0 for i in range(1, 5): - refTableFile = '%s/r%s/ip_mpls_route.ref' % (thisDir, i) + refTableFile = "%s/r%s/ip_mpls_route.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) + expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1) # Actual output from router - actual = net['r%s' % i].cmd('ip -o -family mpls route 2> /dev/null').rstrip() + actual = ( + net["r%s" % i].cmd("ip -o -family mpls route 2> /dev/null").rstrip() + ) # Mask out label and protocol actual = re.sub(r"[0-9][0-9] via inet ", "xx via inet ", actual) @@ -704,39 +776,48 @@ def test_linux_mpls_routes(): # Sort nexthops nexthop_sorted = [] for line in actual.splitlines(): - tokens = re.split(r'\\\t', line.strip()) - nexthop_sorted.append('{} {}'.format( - tokens[0].strip(), - ' '.join([ token.strip() for token in sorted(tokens[1:]) ]) - ).strip()) + tokens = re.split(r"\\\t", line.strip()) + nexthop_sorted.append( + "{} {}".format( + tokens[0].strip(), + " ".join([token.strip() for token in sorted(tokens[1:])]), + ).strip() + ) # Sort lines and fixup differences between old and new iproute - actual = '\n'.join(sorted(nexthop_sorted)) + actual = "\n".join(sorted(nexthop_sorted)) actual = re.sub(r"nexthop via", "nexthopvia", actual) actual = re.sub(r" nexthop as to xx via inet ", " nexthopvia inet ", actual) actual = re.sub(r" weight 1", "", actual) actual = re.sub(r" [ ]+", " ", actual) # put \n back at line ends - actual = ('\n'.join(actual.splitlines()) + '\n').splitlines(1) + actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1) # Generate Diff - diff = topotest.get_textdiff(actual, expected, + diff = topotest.get_textdiff( + actual, + expected, title1="actual Linux Kernel MPLS route", - title2="expected Linux Kernel MPLS route") + title2="expected Linux Kernel MPLS route", + ) # Empty string if it matches, otherwise diff contains unified diff if diff: - sys.stderr.write('r%s failed Linux Kernel MPLS route output Check:\n%s\n' % (i, diff)) + sys.stderr.write( + "r%s failed Linux Kernel MPLS route output Check:\n%s\n" % (i, diff) + ) failures += 1 else: print("r%s ok" % i) - assert failures == 0, "Linux Kernel MPLS route output for router r%s:\n%s" % (i, diff) + assert ( + failures == 0 + ), "Linux Kernel MPLS route output for router r%s:\n%s" % (i, diff) # Make sure that all daemons are running for i in range(1, 5): - fatal_error = net['r%s' % i].checkRouterRunning() + fatal_error = net["r%s" % i].checkRouterRunning() assert fatal_error == "", fatal_error # For debugging after starting FRR/Quagga daemons, uncomment the next line @@ -748,12 +829,14 @@ def test_shutdown_check_stderr(): global net # Skip if previous fatal error condition is raised - if (fatal_error != ""): + if fatal_error != "": pytest.skip(fatal_error) - if os.environ.get('TOPOTESTS_CHECK_STDERR') is None: - print("SKIPPED final check on StdErr output: Disabled (TOPOTESTS_CHECK_STDERR undefined)\n") - pytest.skip('Skipping test for Stderr output') + if os.environ.get("TOPOTESTS_CHECK_STDERR") is None: + print( + "SKIPPED final check on StdErr output: Disabled (TOPOTESTS_CHECK_STDERR undefined)\n" + ) + pytest.skip("Skipping test for Stderr output") thisDir = os.path.dirname(os.path.realpath(__file__)) @@ -761,14 +844,14 @@ def test_shutdown_check_stderr(): print("******************************************\n") for i in range(1, 5): - net['r%s' % i].stopRouter() - log = net['r%s' % i].getStdErr('ldpd') + net["r%s" % i].stopRouter() + log = net["r%s" % i].getStdErr("ldpd") if log: print("\nRouter r%s LDPd StdErr Log:\n%s" % (i, log)) - log = net['r%s' % i].getStdErr('ospfd') + log = net["r%s" % i].getStdErr("ospfd") if log: print("\nRouter r%s OSPFd StdErr Log:\n%s" % (i, log)) - log = net['r%s' % i].getStdErr('zebra') + log = net["r%s" % i].getStdErr("zebra") if log: print("\nRouter r%s Zebra StdErr Log:\n%s" % (i, log)) @@ -778,23 +861,27 @@ def test_shutdown_check_memleak(): global net # Skip if previous fatal error condition is raised - if (fatal_error != ""): + if fatal_error != "": pytest.skip(fatal_error) - if os.environ.get('TOPOTESTS_CHECK_MEMLEAK') is None: - print("SKIPPED final check on Memory leaks: Disabled (TOPOTESTS_CHECK_MEMLEAK undefined)\n") - pytest.skip('Skipping test for memory leaks') - + if os.environ.get("TOPOTESTS_CHECK_MEMLEAK") is None: + print( + "SKIPPED final check on Memory leaks: Disabled (TOPOTESTS_CHECK_MEMLEAK undefined)\n" + ) + pytest.skip("Skipping test for memory leaks") + thisDir = os.path.dirname(os.path.realpath(__file__)) for i in range(1, 5): - net['r%s' % i].stopRouter() - net['r%s' % i].report_memory_leaks(os.environ.get('TOPOTESTS_CHECK_MEMLEAK'), os.path.basename(__file__)) + net["r%s" % i].stopRouter() + net["r%s" % i].report_memory_leaks( + os.environ.get("TOPOTESTS_CHECK_MEMLEAK"), os.path.basename(__file__) + ) -if __name__ == '__main__': +if __name__ == "__main__": - setLogLevel('info') + setLogLevel("info") # To suppress tracebacks, either use the following pytest call or add "--tb=no" to cli # retval = pytest.main(["-s", "--tb=no"]) retval = pytest.main(["-s"]) diff --git a/tests/topotests/ldp-vpls-topo1/r1/ospfd.conf b/tests/topotests/ldp-vpls-topo1/r1/ospfd.conf index 6daf034d18..76ea32fb61 100644 --- a/tests/topotests/ldp-vpls-topo1/r1/ospfd.conf +++ b/tests/topotests/ldp-vpls-topo1/r1/ospfd.conf @@ -5,3 +5,11 @@ router ospf router-id 1.1.1.1 network 0.0.0.0/0 area 0 ! +int r1-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +int r1-eth2 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/ldp-vpls-topo1/r1/show_ip_ospf_neighbor.ref b/tests/topotests/ldp-vpls-topo1/r1/show_ip_ospf_neighbor.ref deleted file mode 100644 index fb193265be..0000000000 --- a/tests/topotests/ldp-vpls-topo1/r1/show_ip_ospf_neighbor.ref +++ /dev/null @@ -1,31 +0,0 @@ -{ - "neighbors":[ - { - "2.2.2.2":[ - { - "priority":1, - "state":"Full/DR", - "address":"10.0.1.2", - "ifaceName":"r1-eth1:10.0.1.1", - "retransmitCounter":0, - "requestCounter":0, - "dbSummaryCounter":0 - } - ] - }, - { - "3.3.3.3":[ - { - "priority":1, - "state":"Full/DR", - "address":"10.0.2.3", - "ifaceName":"r1-eth2:10.0.2.1", - "retransmitCounter":0, - "requestCounter":0, - "dbSummaryCounter":0 - } - ] - } - ] -} - diff --git a/tests/topotests/ldp-vpls-topo1/r1/show_ip_ospf_neighbor.ref-no-neigh b/tests/topotests/ldp-vpls-topo1/r1/show_ip_ospf_neighbor.ref-no-neigh deleted file mode 100644 index 7c4d0ab58c..0000000000 --- a/tests/topotests/ldp-vpls-topo1/r1/show_ip_ospf_neighbor.ref-no-neigh +++ /dev/null @@ -1,18 +0,0 @@ -{ - "2.2.2.2":[ - { - "priority":1, - "state":"Full/DR", - "address":"10.0.1.2", - "ifaceName":"r1-eth1:10.0.1.1" - } - ], - "3.3.3.3":[ - { - "priority":1, - "state":"Full/DR", - "address":"10.0.2.3", - "ifaceName":"r1-eth2:10.0.2.1" - } - ] -} diff --git a/tests/topotests/ldp-vpls-topo1/r1/show_ip_ospf_neighbor.ref-old-nolist b/tests/topotests/ldp-vpls-topo1/r1/show_ip_ospf_neighbor.ref-old-nolist deleted file mode 100644 index 2270c3fdde..0000000000 --- a/tests/topotests/ldp-vpls-topo1/r1/show_ip_ospf_neighbor.ref-old-nolist +++ /dev/null @@ -1,14 +0,0 @@ -{ - "2.2.2.2":{ - "priority":1, - "state":"Full/DR", - "address":"10.0.1.2", - "ifaceName":"r1-eth1:10.0.1.1" - }, - "3.3.3.3":{ - "priority":1, - "state":"Full/DR", - "address":"10.0.2.3", - "ifaceName":"r1-eth2:10.0.2.1" - } -} diff --git a/tests/topotests/ldp-vpls-topo1/r1/show_ip_route.ref b/tests/topotests/ldp-vpls-topo1/r1/show_ip_route.ref index 8ccd60ca41..fdb81f212d 100644 --- a/tests/topotests/ldp-vpls-topo1/r1/show_ip_route.ref +++ b/tests/topotests/ldp-vpls-topo1/r1/show_ip_route.ref @@ -8,7 +8,6 @@ "nexthops":[ { "directlyConnected":true, - "interfaceIndex":1, "interfaceName":"lo", "active":true } @@ -22,7 +21,6 @@ { "fib":true, "directlyConnected":true, - "interfaceIndex":1, "interfaceName":"lo", "active":true } @@ -41,7 +39,6 @@ "fib":true, "ip":"10.0.1.2", "afi":"ipv4", - "interfaceIndex":3, "interfaceName":"r1-eth1", "active":true } @@ -60,7 +57,6 @@ "fib":true, "ip":"10.0.2.3", "afi":"ipv4", - "interfaceIndex":4, "interfaceName":"r1-eth2", "active":true } @@ -76,7 +72,6 @@ "nexthops":[ { "directlyConnected":true, - "interfaceIndex":3, "interfaceName":"r1-eth1", "active":true } @@ -90,7 +85,6 @@ { "fib":true, "directlyConnected":true, - "interfaceIndex":3, "interfaceName":"r1-eth1", "active":true } @@ -106,7 +100,6 @@ "nexthops":[ { "directlyConnected":true, - "interfaceIndex":4, "interfaceName":"r1-eth2", "active":true } @@ -120,7 +113,6 @@ { "fib":true, "directlyConnected":true, - "interfaceIndex":4, "interfaceName":"r1-eth2", "active":true } @@ -139,7 +131,6 @@ "fib":true, "ip":"10.0.1.2", "afi":"ipv4", - "interfaceIndex":3, "interfaceName":"r1-eth1", "active":true }, @@ -147,7 +138,6 @@ "fib":true, "ip":"10.0.2.3", "afi":"ipv4", - "interfaceIndex":4, "interfaceName":"r1-eth2", "active":true } diff --git a/tests/topotests/ldp-vpls-topo1/r1/zebra.conf b/tests/topotests/ldp-vpls-topo1/r1/zebra.conf index edfa1780a9..ea047355ad 100644 --- a/tests/topotests/ldp-vpls-topo1/r1/zebra.conf +++ b/tests/topotests/ldp-vpls-topo1/r1/zebra.conf @@ -14,17 +14,14 @@ interface lo ! interface r1-eth0 description to s1 - no link-detect ! interface r1-eth1 description to s4 ip address 10.0.1.1/24 - no link-detect ! interface r1-eth2 description to s5 ip address 10.0.2.1/24 - no link-detect ! ip forwarding ! diff --git a/tests/topotests/ldp-vpls-topo1/r2/ospfd.conf b/tests/topotests/ldp-vpls-topo1/r2/ospfd.conf index 8678813665..7b3ddfe371 100644 --- a/tests/topotests/ldp-vpls-topo1/r2/ospfd.conf +++ b/tests/topotests/ldp-vpls-topo1/r2/ospfd.conf @@ -5,3 +5,11 @@ router ospf router-id 2.2.2.2 network 0.0.0.0/0 area 0 ! +int r2-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +int r2-eth2 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/ldp-vpls-topo1/r2/show_ip_ospf_neighbor.ref b/tests/topotests/ldp-vpls-topo1/r2/show_ip_ospf_neighbor.ref deleted file mode 100644 index 1376579757..0000000000 --- a/tests/topotests/ldp-vpls-topo1/r2/show_ip_ospf_neighbor.ref +++ /dev/null @@ -1,30 +0,0 @@ -{ - "neighbors":[ - { - "1.1.1.1":[ - { - "priority":1, - "state":"Full/Backup", - "address":"10.0.1.1", - "ifaceName":"r2-eth1:10.0.1.2", - "retransmitCounter":0, - "requestCounter":0, - "dbSummaryCounter":0 - } - ] - }, - { - "3.3.3.3":[ - { - "priority":1, - "state":"Full/DR", - "address":"10.0.3.3", - "ifaceName":"r2-eth2:10.0.3.2", - "retransmitCounter":0, - "requestCounter":0, - "dbSummaryCounter":0 - } - ] - } - ] -} diff --git a/tests/topotests/ldp-vpls-topo1/r2/show_ip_ospf_neighbor.ref-no-neigh b/tests/topotests/ldp-vpls-topo1/r2/show_ip_ospf_neighbor.ref-no-neigh deleted file mode 100644 index a982c1cbd3..0000000000 --- a/tests/topotests/ldp-vpls-topo1/r2/show_ip_ospf_neighbor.ref-no-neigh +++ /dev/null @@ -1,18 +0,0 @@ -{ - "1.1.1.1":[ - { - "priority":1, - "state":"Full/Backup", - "address":"10.0.1.1", - "ifaceName":"r2-eth1:10.0.1.2" - } - ], - "3.3.3.3":[ - { - "priority":1, - "state":"Full/DR", - "address":"10.0.3.3", - "ifaceName":"r2-eth2:10.0.3.2" - } - ] -} diff --git a/tests/topotests/ldp-vpls-topo1/r2/show_ip_ospf_neighbor.ref-old-nolist b/tests/topotests/ldp-vpls-topo1/r2/show_ip_ospf_neighbor.ref-old-nolist deleted file mode 100644 index 18ffbc2f8a..0000000000 --- a/tests/topotests/ldp-vpls-topo1/r2/show_ip_ospf_neighbor.ref-old-nolist +++ /dev/null @@ -1,14 +0,0 @@ -{ - "1.1.1.1":{ - "priority":1, - "state":"Full/Backup", - "address":"10.0.1.1", - "ifaceName":"r2-eth1:10.0.1.2" - }, - "3.3.3.3":{ - "priority":1, - "state":"Full/DR", - "address":"10.0.3.3", - "ifaceName":"r2-eth2:10.0.3.2" - } -} diff --git a/tests/topotests/ldp-vpls-topo1/r2/show_ip_route.ref b/tests/topotests/ldp-vpls-topo1/r2/show_ip_route.ref index 7147c6a595..6056feffe3 100644 --- a/tests/topotests/ldp-vpls-topo1/r2/show_ip_route.ref +++ b/tests/topotests/ldp-vpls-topo1/r2/show_ip_route.ref @@ -11,7 +11,6 @@ "fib":true, "ip":"10.0.1.1", "afi":"ipv4", - "interfaceIndex":3, "interfaceName":"r2-eth1", "active":true } @@ -27,7 +26,6 @@ "nexthops":[ { "directlyConnected":true, - "interfaceIndex":1, "interfaceName":"lo", "active":true } @@ -41,7 +39,6 @@ { "fib":true, "directlyConnected":true, - "interfaceIndex":1, "interfaceName":"lo", "active":true } @@ -60,7 +57,6 @@ "fib":true, "ip":"10.0.3.3", "afi":"ipv4", - "interfaceIndex":4, "interfaceName":"r2-eth2", "active":true } @@ -76,7 +72,6 @@ "nexthops":[ { "directlyConnected":true, - "interfaceIndex":3, "interfaceName":"r2-eth1", "active":true } @@ -90,7 +85,6 @@ { "fib":true, "directlyConnected":true, - "interfaceIndex":3, "interfaceName":"r2-eth1", "active":true } @@ -109,7 +103,6 @@ "fib":true, "ip":"10.0.1.1", "afi":"ipv4", - "interfaceIndex":3, "interfaceName":"r2-eth1", "active":true }, @@ -117,7 +110,6 @@ "fib":true, "ip":"10.0.3.3", "afi":"ipv4", - "interfaceIndex":4, "interfaceName":"r2-eth2", "active":true } @@ -133,7 +125,6 @@ "nexthops":[ { "directlyConnected":true, - "interfaceIndex":4, "interfaceName":"r2-eth2", "active":true } @@ -147,7 +138,6 @@ { "fib":true, "directlyConnected":true, - "interfaceIndex":4, "interfaceName":"r2-eth2", "active":true } diff --git a/tests/topotests/ldp-vpls-topo1/r2/zebra.conf b/tests/topotests/ldp-vpls-topo1/r2/zebra.conf index 6b95efdce8..c244442876 100644 --- a/tests/topotests/ldp-vpls-topo1/r2/zebra.conf +++ b/tests/topotests/ldp-vpls-topo1/r2/zebra.conf @@ -13,17 +13,14 @@ interface lo ! interface r2-eth0 description to s2 - no link-detect ! interface r2-eth1 description to s4 ip address 10.0.1.2/24 - no link-detect ! interface r2-eth2 description to s6 ip address 10.0.3.2/24 - no link-detect ! ip forwarding ! diff --git a/tests/topotests/ldp-vpls-topo1/r3/ospfd.conf b/tests/topotests/ldp-vpls-topo1/r3/ospfd.conf index b6bafba205..b424f2e108 100644 --- a/tests/topotests/ldp-vpls-topo1/r3/ospfd.conf +++ b/tests/topotests/ldp-vpls-topo1/r3/ospfd.conf @@ -5,3 +5,11 @@ router ospf router-id 3.3.3.3 network 0.0.0.0/0 area 0 ! +int r3-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +int r3-eth2 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/ldp-vpls-topo1/r3/show_ip_ospf_neighbor.ref b/tests/topotests/ldp-vpls-topo1/r3/show_ip_ospf_neighbor.ref deleted file mode 100644 index 41de304b2b..0000000000 --- a/tests/topotests/ldp-vpls-topo1/r3/show_ip_ospf_neighbor.ref +++ /dev/null @@ -1,30 +0,0 @@ -{ - "neighbors":[ - { - "1.1.1.1":[ - { - "priority":1, - "state":"Full/Backup", - "address":"10.0.2.1", - "ifaceName":"r3-eth1:10.0.2.3", - "retransmitCounter":0, - "requestCounter":0, - "dbSummaryCounter":0 - } - ] - }, - { - "2.2.2.2":[ - { - "priority":1, - "state":"Full/Backup", - "address":"10.0.3.2", - "ifaceName":"r3-eth2:10.0.3.3", - "retransmitCounter":0, - "requestCounter":0, - "dbSummaryCounter":0 - } - ] - } - ] -} diff --git a/tests/topotests/ldp-vpls-topo1/r3/show_ip_ospf_neighbor.ref-no-neigh b/tests/topotests/ldp-vpls-topo1/r3/show_ip_ospf_neighbor.ref-no-neigh deleted file mode 100644 index d7e0e42405..0000000000 --- a/tests/topotests/ldp-vpls-topo1/r3/show_ip_ospf_neighbor.ref-no-neigh +++ /dev/null @@ -1,18 +0,0 @@ -{ - "1.1.1.1":[ - { - "priority":1, - "state":"Full/Backup", - "address":"10.0.2.1", - "ifaceName":"r3-eth1:10.0.2.3" - } - ], - "2.2.2.2":[ - { - "priority":1, - "state":"Full/Backup", - "address":"10.0.3.2", - "ifaceName":"r3-eth2:10.0.3.3" - } - ] -} diff --git a/tests/topotests/ldp-vpls-topo1/r3/show_ip_ospf_neighbor.ref-old-nolist b/tests/topotests/ldp-vpls-topo1/r3/show_ip_ospf_neighbor.ref-old-nolist deleted file mode 100644 index b0669742ab..0000000000 --- a/tests/topotests/ldp-vpls-topo1/r3/show_ip_ospf_neighbor.ref-old-nolist +++ /dev/null @@ -1,14 +0,0 @@ -{ - "1.1.1.1":{ - "priority":1, - "state":"Full/Backup", - "address":"10.0.2.1", - "ifaceName":"r3-eth1:10.0.2.3" - }, - "2.2.2.2":{ - "priority":1, - "state":"Full/Backup", - "address":"10.0.3.2", - "ifaceName":"r3-eth2:10.0.3.3" - } -} diff --git a/tests/topotests/ldp-vpls-topo1/r3/show_ip_route.ref b/tests/topotests/ldp-vpls-topo1/r3/show_ip_route.ref index d77de7c9e3..fc96ada443 100644 --- a/tests/topotests/ldp-vpls-topo1/r3/show_ip_route.ref +++ b/tests/topotests/ldp-vpls-topo1/r3/show_ip_route.ref @@ -11,7 +11,6 @@ "fib":true, "ip":"10.0.2.1", "afi":"ipv4", - "interfaceIndex":3, "interfaceName":"r3-eth1", "active":true } @@ -30,7 +29,6 @@ "fib":true, "ip":"10.0.3.2", "afi":"ipv4", - "interfaceIndex":4, "interfaceName":"r3-eth2", "active":true } @@ -46,7 +44,6 @@ "nexthops":[ { "directlyConnected":true, - "interfaceIndex":1, "interfaceName":"lo", "active":true } @@ -60,7 +57,6 @@ { "fib":true, "directlyConnected":true, - "interfaceIndex":1, "interfaceName":"lo", "active":true } @@ -79,7 +75,6 @@ "fib":true, "ip":"10.0.2.1", "afi":"ipv4", - "interfaceIndex":3, "interfaceName":"r3-eth1", "active":true }, @@ -87,7 +82,6 @@ "fib":true, "ip":"10.0.3.2", "afi":"ipv4", - "interfaceIndex":4, "interfaceName":"r3-eth2", "active":true } @@ -103,7 +97,6 @@ "nexthops":[ { "directlyConnected":true, - "interfaceIndex":3, "interfaceName":"r3-eth1", "active":true } @@ -117,7 +110,6 @@ { "fib":true, "directlyConnected":true, - "interfaceIndex":3, "interfaceName":"r3-eth1", "active":true } @@ -133,7 +125,6 @@ "nexthops":[ { "directlyConnected":true, - "interfaceIndex":4, "interfaceName":"r3-eth2", "active":true } @@ -147,7 +138,6 @@ { "fib":true, "directlyConnected":true, - "interfaceIndex":4, "interfaceName":"r3-eth2", "active":true } diff --git a/tests/topotests/ldp-vpls-topo1/r3/zebra.conf b/tests/topotests/ldp-vpls-topo1/r3/zebra.conf index 85ec68ff32..6b1eaa2ca0 100644 --- a/tests/topotests/ldp-vpls-topo1/r3/zebra.conf +++ b/tests/topotests/ldp-vpls-topo1/r3/zebra.conf @@ -13,17 +13,14 @@ interface lo ! interface r3-eth0 description to s3 - no link-detect ! interface r3-eth1 description to s5 ip address 10.0.2.3/24 - no link-detect ! interface r3-eth2 description to s6 ip address 10.0.3.3/24 - no link-detect ! ip forwarding ! diff --git a/tests/topotests/ldp-vpls-topo1/test_ldp_vpls_topo1.py b/tests/topotests/ldp-vpls-topo1/test_ldp_vpls_topo1.py index 0948c2e41b..a1662dc411 100755 --- a/tests/topotests/ldp-vpls-topo1/test_ldp_vpls_topo1.py +++ b/tests/topotests/ldp-vpls-topo1/test_ldp_vpls_topo1.py @@ -69,7 +69,7 @@ # Save the Current Working Directory to find configuration files. CWD = os.path.dirname(os.path.realpath(__file__)) -sys.path.append(os.path.join(CWD, '../')) +sys.path.append(os.path.join(CWD, "../")) # pylint: disable=C0413 # Import topogen and topotest helpers @@ -80,8 +80,10 @@ # Required to instantiate the topology builder class. from mininet.topo import Topo + class TemplateTopo(Topo): "Test topology builder" + def build(self, *_args, **_opts): "Build function" tgen = get_topogen(self) @@ -89,35 +91,36 @@ def build(self, *_args, **_opts): # # Define FRR Routers # - for router in ['ce1', 'ce2', 'ce3', 'r1', 'r2', 'r3']: + for router in ["ce1", "ce2", "ce3", "r1", "r2", "r3"]: tgen.add_router(router) # # Define connections # - switch = tgen.add_switch('s1') - switch.add_link(tgen.gears['ce1']) - switch.add_link(tgen.gears['r1']) + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["ce1"]) + switch.add_link(tgen.gears["r1"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["ce2"]) + switch.add_link(tgen.gears["r2"]) - switch = tgen.add_switch('s2') - switch.add_link(tgen.gears['ce2']) - switch.add_link(tgen.gears['r2']) + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["ce3"]) + switch.add_link(tgen.gears["r3"]) - switch = tgen.add_switch('s3') - switch.add_link(tgen.gears['ce3']) - switch.add_link(tgen.gears['r3']) + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) - switch = tgen.add_switch('s4') - switch.add_link(tgen.gears['r1']) - switch.add_link(tgen.gears['r2']) + switch = tgen.add_switch("s5") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r3"]) - switch = tgen.add_switch('s5') - switch.add_link(tgen.gears['r1']) - switch.add_link(tgen.gears['r3']) + switch = tgen.add_switch("s6") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) - switch = tgen.add_switch('s6') - switch.add_link(tgen.gears['r2']) - switch.add_link(tgen.gears['r3']) def setup_module(mod): "Sets up the pytest environment" @@ -129,24 +132,19 @@ def setup_module(mod): # For all registered routers, load the zebra configuration file for rname, router in router_list.iteritems(): router.load_config( - TopoRouter.RD_ZEBRA, - os.path.join(CWD, '{}/zebra.conf'.format(rname)) + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) ) # Don't start ospfd and ldpd in the CE nodes - if router.name[0] == 'r': + if router.name[0] == "r": router.load_config( - TopoRouter.RD_OSPF, - os.path.join(CWD, '{}/ospfd.conf'.format(rname)) + TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname)) ) router.load_config( - TopoRouter.RD_LDP, - os.path.join(CWD, '{}/ldpd.conf'.format(rname)) + TopoRouter.RD_LDP, os.path.join(CWD, "{}/ldpd.conf".format(rname)) ) tgen.start_router() - for router in router_list.values(): - if router.has_version('<', '3'): - tgen.set_error('unsupported version') + def teardown_module(mod): "Teardown the pytest environment" @@ -156,22 +154,22 @@ def teardown_module(mod): tgen.stop_topology() -def router_compare_json_output(rname, command, reference): +def router_compare_json_output(rname, command, reference, count=80, wait=1): "Compare router JSON output" logger.info('Comparing router "%s" "%s" output', rname, command) tgen = get_topogen() - filename = '{}/{}/{}'.format(CWD, rname, reference) + filename = "{}/{}/{}".format(CWD, rname, reference) expected = json.loads(open(filename).read()) - # Run test function until we get an result. Wait at most 80 seconds. - test_func = partial(topotest.router_json_cmp, - tgen.gears[rname], command, expected) - _, diff = topotest.run_and_expect(test_func, None, count=160, wait=0.5) + # Run test function until we get an result. + test_func = partial(topotest.router_json_cmp, tgen.gears[rname], command, expected) + _, diff = topotest.run_and_expect(test_func, None, count, wait) assertmsg = '"{}" JSON output mismatches the expected result'.format(rname) assert diff is None, assertmsg + def test_ospf_convergence(): logger.info("Test: check OSPF adjacencies") tgen = get_topogen() @@ -180,30 +178,11 @@ def test_ospf_convergence(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - # Old output (before FRR PR1383) didn't show a list of neighbors. - # Check for dict object and compare to old output if this is the case - tgen = get_topogen() - router = tgen.gears['r1'] - output = router.vtysh_cmd("show ip ospf neighbor json", isjson=True) - - # We could have either old format (without "neighbors" and direct list - # of IP's or new format from PR1659 with "neighbors". - # Trying old formats first and fall back to new format - # - # New format: neighbors have dict instead of list of dicts (PR1723). - if output.has_key('neighbors'): - if isinstance(output['neighbors'], dict): - reffile = "show_ip_ospf_neighbor.json" - else: - reffile = "show_ip_ospf_neighbor.ref" - else: - if isinstance(output["2.2.2.2"], dict): - reffile = "show_ip_ospf_neighbor.ref-old-nolist" - else: - reffile = "show_ip_ospf_neighbor.ref-no-neigh" - - for rname in ['r1', 'r2', 'r3']: - router_compare_json_output(rname, "show ip ospf neighbor json", reffile) + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show ip ospf neighbor json", "show_ip_ospf_neighbor.json" + ) + def test_rib(): logger.info("Test: verify RIB") @@ -213,9 +192,10 @@ def test_rib(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - for rname in ['r1', 'r2', 'r3']: + for rname in ["r1", "r2", "r3"]: router_compare_json_output(rname, "show ip route json", "show_ip_route.ref") + def test_ldp_adjacencies(): logger.info("Test: verify LDP adjacencies") tgen = get_topogen() @@ -224,8 +204,11 @@ def test_ldp_adjacencies(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - for rname in ['r1', 'r2', 'r3']: - router_compare_json_output(rname, "show mpls ldp discovery json", "show_ldp_discovery.ref") + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show mpls ldp discovery json", "show_ldp_discovery.ref" + ) + def test_ldp_neighbors(): logger.info("Test: verify LDP neighbors") @@ -235,8 +218,11 @@ def test_ldp_neighbors(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - for rname in ['r1', 'r2', 'r3']: - router_compare_json_output(rname, "show mpls ldp neighbor json", "show_ldp_neighbor.ref") + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show mpls ldp neighbor json", "show_ldp_neighbor.ref" + ) + def test_ldp_bindings(): logger.info("Test: verify LDP bindings") @@ -246,8 +232,11 @@ def test_ldp_bindings(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - for rname in ['r1', 'r2', 'r3']: - router_compare_json_output(rname, "show mpls ldp binding json", "show_ldp_binding.ref") + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show mpls ldp binding json", "show_ldp_binding.ref" + ) + def test_ldp_pwid_bindings(): logger.info("Test: verify LDP PW-ID bindings") @@ -257,8 +246,11 @@ def test_ldp_pwid_bindings(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - for rname in ['r1', 'r2', 'r3']: - router_compare_json_output(rname, "show l2vpn atom binding json", "show_l2vpn_binding.ref") + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show l2vpn atom binding json", "show_l2vpn_binding.ref" + ) + def test_ldp_pseudowires(): logger.info("Test: verify LDP pseudowires") @@ -268,8 +260,11 @@ def test_ldp_pseudowires(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - for rname in ['r1', 'r2', 'r3']: - router_compare_json_output(rname, "show l2vpn atom vc json", "show_l2vpn_vc.ref") + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show l2vpn atom vc json", "show_l2vpn_vc.ref" + ) + def test_ldp_pseudowires_after_link_down(): logger.info("Test: verify LDP pseudowires after r1-r2 link goes down") @@ -281,21 +276,28 @@ def test_ldp_pseudowires_after_link_down(): # Shut down r1-r2 link */ tgen = get_topogen() - tgen.gears['r1'].peer_link_enable('r1-eth1', False) + tgen.gears["r1"].peer_link_enable("r1-eth1", False) + topotest.sleep(5, "Waiting for the network to reconverge") + + # check if the pseudowire is still up (using an alternate path + # for nexthop resolution). Give some extra wait time. + for rname in ["r1", "r2", "r3"]: + router_compare_json_output( + rname, "show l2vpn atom vc json", "show_l2vpn_vc.ref", + count=160, wait=1 + ) - # check if the pseudowire is still up (using an alternate path for nexthop resolution) - for rname in ['r1', 'r2', 'r3']: - router_compare_json_output(rname, "show l2vpn atom vc json", "show_l2vpn_vc.ref") # Memory leak test template def test_memory_leak(): "Run the memory leak test and report results." tgen = get_topogen() if not tgen.is_memleak_enabled(): - pytest.skip('Memory leak test/report is disabled') + pytest.skip("Memory leak test/report is disabled") tgen.report_memory_leaks() -if __name__ == '__main__': + +if __name__ == "__main__": args = ["-s"] + sys.argv[1:] sys.exit(pytest.main(args)) diff --git a/tests/topotests/lib/bgp.py b/tests/topotests/lib/bgp.py new file mode 100644 index 0000000000..e2f887bb89 --- /dev/null +++ b/tests/topotests/lib/bgp.py @@ -0,0 +1,4208 @@ +# +# Copyright (c) 2019 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. +# ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +from copy import deepcopy +from time import sleep +import traceback +import ipaddress +import os +import sys +from lib import topotest +from lib.topolog import logger + +from lib.topogen import TopoRouter, get_topogen + +# Import common_config to use commomnly used APIs +from lib.common_config import ( + create_common_configuration, + InvalidCLIError, + load_config_to_router, + check_address_types, + generate_ips, + validate_ip_address, + find_interface_with_greater_ip, + run_frr_cmd, + FRRCFG_FILE, + retry, +) + +LOGDIR = "/tmp/topotests/" +TMPDIR = None + + +def create_router_bgp(tgen, topo, input_dict=None, build=False, load_config=True): + """ + API to configure bgp on router + + Parameters + ---------- + * `tgen` : Topogen object + * `topo` : json file data + * `input_dict` : Input dict data, required when configuring from testcase + * `build` : Only for initial setup phase this is set as True. + + Usage + ----- + input_dict = { + "r1": { + "bgp": { + "local_as": "200", + "router_id": "22.22.22.22", + "graceful-restart": { + "graceful-restart": True, + "preserve-fw-state": True, + "timer": { + "restart-time": 30, + "rib-stale-time": 30, + "select-defer-time": 30, + } + }, + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [{ + "redist_type": "static", + "attribute": { + "metric" : 123 + } + }, + {"redist_type": "connected"} + ], + "advertise_networks": [ + { + "network": "20.0.0.0/32", + "no_of_network": 10 + }, + { + "network": "30.0.0.0/32", + "no_of_network": 10 + } + ], + "neighbor": { + "r3": { + "keepalivetimer": 60, + "holddowntimer": 180, + "dest_link": { + "r4": { + "allowas-in": { + "number_occurences": 2 + }, + "prefix_lists": [ + { + "name": "pf_list_1", + "direction": "in" + } + ], + "route_maps": [{ + "name": "RMAP_MED_R3", + "direction": "in" + }], + "next_hop_self": True + }, + "r1": {"graceful-restart-helper": True} + } + } + } + } + } + } + } + } + } + + + Returns + ------- + True or False + """ + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + result = False + + # Flag is used when testing ipv6 over ipv4 or vice-versa + afi_test = False + + if not input_dict: + input_dict = deepcopy(topo) + else: + topo = topo["routers"] + input_dict = deepcopy(input_dict) + + for router in input_dict.keys(): + if "bgp" not in input_dict[router]: + logger.debug("Router %s: 'bgp' not present in input_dict", router) + continue + + bgp_data_list = input_dict[router]["bgp"] + + if type(bgp_data_list) is not list: + bgp_data_list = [bgp_data_list] + + for bgp_data in bgp_data_list: + data_all_bgp = __create_bgp_global(tgen, bgp_data, router, build) + if data_all_bgp: + bgp_addr_data = bgp_data.setdefault("address_family", {}) + + if not bgp_addr_data: + logger.debug( + "Router %s: 'address_family' not present in " + "input_dict for BGP", + router, + ) + else: + + ipv4_data = bgp_addr_data.setdefault("ipv4", {}) + ipv6_data = bgp_addr_data.setdefault("ipv6", {}) + l2vpn_data = bgp_addr_data.setdefault("l2vpn", {}) + + neigh_unicast = ( + True + if ipv4_data.setdefault("unicast", {}) + or ipv6_data.setdefault("unicast", {}) + else False + ) + + l2vpn_evpn = True if l2vpn_data.setdefault("evpn", {}) else False + + if neigh_unicast: + data_all_bgp = __create_bgp_unicast_neighbor( + tgen, + topo, + bgp_data, + router, + afi_test, + config_data=data_all_bgp, + ) + + if l2vpn_evpn: + data_all_bgp = __create_l2vpn_evpn_address_family( + tgen, topo, bgp_data, router, config_data=data_all_bgp + ) + + try: + result = create_common_configuration( + tgen, router, data_all_bgp, "bgp", build, load_config + ) + except InvalidCLIError: + # Traceback + errormsg = traceback.format_exc() + logger.error(errormsg) + return errormsg + + logger.debug("Exiting lib API: create_router_bgp()") + return result + + +def __create_bgp_global(tgen, input_dict, router, build=False): + """ + Helper API to create bgp global configuration. + + Parameters + ---------- + * `tgen` : Topogen object + * `input_dict` : Input dict data, required when configuring from testcase + * `router` : router id to be configured. + * `build` : Only for initial setup phase this is set as True. + + Returns + ------- + True or False + """ + + result = False + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + bgp_data = input_dict + del_bgp_action = bgp_data.setdefault("delete", False) + + config_data = [] + + if "local_as" not in bgp_data and build: + logger.debug( + "Router %s: 'local_as' not present in input_dict" "for BGP", router + ) + return False + + local_as = bgp_data.setdefault("local_as", "") + cmd = "router bgp {}".format(local_as) + vrf_id = bgp_data.setdefault("vrf", None) + if vrf_id: + cmd = "{} vrf {}".format(cmd, vrf_id) + + if del_bgp_action: + cmd = "no {}".format(cmd) + config_data.append(cmd) + + return config_data + + config_data.append(cmd) + config_data.append("no bgp ebgp-requires-policy") + + router_id = bgp_data.setdefault("router_id", None) + del_router_id = bgp_data.setdefault("del_router_id", False) + if del_router_id: + config_data.append("no bgp router-id") + if router_id: + config_data.append("bgp router-id {}".format(router_id)) + + config_data.append("no bgp network import-check") + + bst_path = bgp_data.setdefault("bestpath", None) + if bst_path: + if "aspath" in bst_path: + if "delete" in bst_path: + config_data.append( + "no bgp bestpath as-path {}".format(bst_path["aspath"]) + ) + else: + config_data.append("bgp bestpath as-path {}".format(bst_path["aspath"])) + + if "graceful-restart" in bgp_data: + graceful_config = bgp_data["graceful-restart"] + + graceful_restart = graceful_config.setdefault("graceful-restart", None) + + graceful_restart_disable = graceful_config.setdefault( + "graceful-restart-disable", None + ) + + preserve_fw_state = graceful_config.setdefault("preserve-fw-state", None) + + disable_eor = graceful_config.setdefault("disable-eor", None) + + if graceful_restart == False: + cmd = "no bgp graceful-restart" + if graceful_restart: + cmd = "bgp graceful-restart" + + if graceful_restart is not None: + config_data.append(cmd) + + if graceful_restart_disable == False: + cmd = "no bgp graceful-restart-disable" + if graceful_restart_disable: + cmd = "bgp graceful-restart-disable" + + if graceful_restart_disable is not None: + config_data.append(cmd) + + if preserve_fw_state == False: + cmd = "no bgp graceful-restart preserve-fw-state" + if preserve_fw_state: + cmd = "bgp graceful-restart preserve-fw-state" + + if preserve_fw_state is not None: + config_data.append(cmd) + + if disable_eor == False: + cmd = "no bgp graceful-restart disable-eor" + if disable_eor: + cmd = "bgp graceful-restart disable-eor" + + if disable_eor is not None: + config_data.append(cmd) + + if "timer" in bgp_data["graceful-restart"]: + timer = bgp_data["graceful-restart"]["timer"] + + if "delete" in timer: + del_action = timer["delete"] + else: + del_action = False + + for rs_timer, value in timer.items(): + rs_timer_value = timer.setdefault(rs_timer, None) + + if rs_timer_value and rs_timer != "delete": + cmd = "bgp graceful-restart {} {}".format(rs_timer, rs_timer_value) + + if del_action: + cmd = "no {}".format(cmd) + + config_data.append(cmd) + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return config_data + + +def __create_bgp_unicast_neighbor( + tgen, topo, input_dict, router, afi_test, config_data=None +): + """ + Helper API to create configuration for address-family unicast + + Parameters + ---------- + * `tgen` : Topogen object + * `topo` : json file data + * `input_dict` : Input dict data, required when configuring from testcase + * `router` : router id to be configured. + * `afi_test` : use when ipv6 needs to be tested over ipv4 or vice-versa + * `build` : Only for initial setup phase this is set as True. + """ + + result = False + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + add_neigh = True + bgp_data = input_dict + if "router bgp" in config_data: + add_neigh = False + + bgp_data = input_dict["address_family"] + + for addr_type, addr_dict in bgp_data.iteritems(): + if not addr_dict: + continue + + if not check_address_types(addr_type) and not afi_test: + continue + + addr_data = addr_dict["unicast"] + if addr_data: + config_data.append("address-family {} unicast".format(addr_type)) + advertise_network = addr_data.setdefault("advertise_networks", []) + for advertise_network_dict in advertise_network: + network = advertise_network_dict["network"] + if type(network) is not list: + network = [network] + + if "no_of_network" in advertise_network_dict: + no_of_network = advertise_network_dict["no_of_network"] + else: + no_of_network = 1 + + del_action = advertise_network_dict.setdefault("delete", False) + + # Generating IPs for verification + prefix = str(ipaddress.ip_network(unicode(network[0])).prefixlen) + network_list = generate_ips(network, no_of_network) + for ip in network_list: + ip = str(ipaddress.ip_network(unicode(ip)).network_address) + + cmd = "network {}/{}".format(ip, prefix) + if del_action: + cmd = "no {}".format(cmd) + + config_data.append(cmd) + + max_paths = addr_data.setdefault("maximum_paths", {}) + if max_paths: + ibgp = max_paths.setdefault("ibgp", None) + ebgp = max_paths.setdefault("ebgp", None) + if ibgp: + config_data.append("maximum-paths ibgp {}".format(ibgp)) + if ebgp: + config_data.append("maximum-paths {}".format(ebgp)) + + aggregate_addresses = addr_data.setdefault("aggregate_address", []) + for aggregate_address in aggregate_addresses: + network = aggregate_address.setdefault("network", None) + if not network: + logger.debug( + "Router %s: 'network' not present in " "input_dict for BGP", router + ) + else: + cmd = "aggregate-address {}".format(network) + + as_set = aggregate_address.setdefault("as_set", False) + summary = aggregate_address.setdefault("summary", False) + del_action = aggregate_address.setdefault("delete", False) + if as_set: + cmd = "{} as-set".format(cmd) + if summary: + cmd = "{} summary".format(cmd) + + if del_action: + cmd = "no {}".format(cmd) + + config_data.append(cmd) + + redistribute_data = addr_data.setdefault("redistribute", {}) + if redistribute_data: + for redistribute in redistribute_data: + if "redist_type" not in redistribute: + logger.debug( + "Router %s: 'redist_type' not present in " "input_dict", router + ) + else: + cmd = "redistribute {}".format(redistribute["redist_type"]) + redist_attr = redistribute.setdefault("attribute", None) + if redist_attr: + if isinstance(redist_attr, dict): + for key, value in redist_attr.items(): + cmd = "{} {} {}".format(cmd, key, value) + else: + cmd = "{} {}".format(cmd, redist_attr) + + del_action = redistribute.setdefault("delete", False) + if del_action: + cmd = "no {}".format(cmd) + config_data.append(cmd) + + import_vrf_data = addr_data.setdefault("import", {}) + if import_vrf_data: + cmd = "import vrf {}".format(import_vrf_data["vrf"]) + + del_action = import_vrf_data.setdefault("delete", False) + if del_action: + cmd = "no {}".format(cmd) + config_data.append(cmd) + + if "neighbor" in addr_data: + neigh_data = __create_bgp_neighbor( + topo, input_dict, router, addr_type, add_neigh + ) + config_data.extend(neigh_data) + + for addr_type, addr_dict in bgp_data.iteritems(): + if not addr_dict or not check_address_types(addr_type): + continue + + addr_data = addr_dict["unicast"] + if "neighbor" in addr_data: + neigh_addr_data = __create_bgp_unicast_address_family( + topo, input_dict, router, addr_type, add_neigh + ) + + config_data.extend(neigh_addr_data) + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return config_data + + +def __create_l2vpn_evpn_address_family( + tgen, topo, input_dict, router, config_data=None +): + """ + Helper API to create configuration for l2vpn evpn address-family + + Parameters + ---------- + * `tgen` : Topogen object + * `topo` : json file data + * `input_dict` : Input dict data, required when configuring + from testcase + * `router` : router id to be configured. + * `build` : Only for initial setup phase this is set as True. + """ + + result = False + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + bgp_data = input_dict["address_family"] + + for family_type, family_dict in bgp_data.iteritems(): + if family_type != "l2vpn": + continue + + family_data = family_dict["evpn"] + if family_data: + config_data.append("address-family l2vpn evpn") + + advertise_data = family_data.setdefault("advertise", {}) + neighbor_data = family_data.setdefault("neighbor", {}) + advertise_all_vni_data = family_data.setdefault("advertise-all-vni", None) + rd_data = family_data.setdefault("rd", None) + no_rd_data = family_data.setdefault("no rd", False) + route_target_data = family_data.setdefault("route-target", {}) + + if advertise_data: + for address_type, unicast_type in advertise_data.items(): + + if isinstance(unicast_type, dict): + for key, value in unicast_type.items(): + cmd = "advertise {} {}".format(address_type, key) + + if value: + route_map = value.setdefault("route-map", {}) + advertise_del_action = value.setdefault("delete", None) + + if route_map: + cmd = "{} route-map {}".format(cmd, route_map) + + if advertise_del_action: + cmd = "no {}".format(cmd) + + config_data.append(cmd) + + if neighbor_data: + for neighbor, neighbor_data in neighbor_data.items(): + ipv4_neighbor = neighbor_data.setdefault("ipv4", {}) + ipv6_neighbor = neighbor_data.setdefault("ipv6", {}) + + if ipv4_neighbor: + for neighbor_name, action in ipv4_neighbor.items(): + neighbor_ip = topo[neighbor]["links"][neighbor_name][ + "ipv4" + ].split("/")[0] + + if isinstance(action, dict): + next_hop_self = action.setdefault("next_hop_self", None) + route_maps = action.setdefault("route_maps", {}) + + if next_hop_self is not None: + if next_hop_self is True: + config_data.append( + "neighbor {} " + "next-hop-self".format(neighbor_ip) + ) + elif next_hop_self is False: + config_data.append( + "no neighbor {} " + "next-hop-self".format(neighbor_ip) + ) + + if route_maps: + for route_map in route_maps: + name = route_map.setdefault("name", {}) + direction = route_map.setdefault("direction", "in") + del_action = route_map.setdefault("delete", False) + + if not name: + logger.info( + "Router %s: 'name' " + "not present in " + "input_dict for BGP " + "neighbor route name", + router, + ) + else: + cmd = "neighbor {} route-map {} " "{}".format( + neighbor_ip, name, direction + ) + + if del_action: + cmd = "no {}".format(cmd) + + config_data.append(cmd) + + else: + if action == "activate": + cmd = "neighbor {} activate".format(neighbor_ip) + elif action == "deactivate": + cmd = "no neighbor {} activate".format(neighbor_ip) + + config_data.append(cmd) + + if ipv6_neighbor: + for neighbor_name, action in ipv4_neighbor.items(): + neighbor_ip = topo[neighbor]["links"][neighbor_name][ + "ipv6" + ].split("/")[0] + if action == "activate": + cmd = "neighbor {} activate".format(neighbor_ip) + elif action == "deactivate": + cmd = "no neighbor {} activate".format(neighbor_ip) + + config_data.append(cmd) + + if advertise_all_vni_data == True: + cmd = "advertise-all-vni" + config_data.append(cmd) + elif advertise_all_vni_data == False: + cmd = "no advertise-all-vni" + config_data.append(cmd) + + if rd_data: + cmd = "rd {}".format(rd_data) + config_data.append(cmd) + + if no_rd_data: + cmd = "no rd {}".format(no_rd_data) + config_data.append(cmd) + + if route_target_data: + for rt_type, rt_dict in route_target_data.items(): + for _rt_dict in rt_dict: + rt_value = _rt_dict.setdefault("value", None) + del_rt = _rt_dict.setdefault("delete", None) + + if rt_value: + cmd = "route-target {} {}".format(rt_type, rt_value) + if del_rt: + cmd = "no {}".format(cmd) + + config_data.append(cmd) + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + + return config_data + + +def __create_bgp_neighbor(topo, input_dict, router, addr_type, add_neigh=True): + """ + Helper API to create neighbor specific configuration + + Parameters + ---------- + * `tgen` : Topogen object + * `topo` : json file data + * `input_dict` : Input dict data, required when configuring from testcase + * `router` : router id to be configured + """ + + config_data = [] + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + bgp_data = input_dict["address_family"] + neigh_data = bgp_data[addr_type]["unicast"]["neighbor"] + + for name, peer_dict in neigh_data.iteritems(): + for dest_link, peer in peer_dict["dest_link"].iteritems(): + nh_details = topo[name] + + if "vrfs" in topo[router] or type(nh_details["bgp"]) is list: + remote_as = nh_details["bgp"][0]["local_as"] + else: + remote_as = nh_details["bgp"]["local_as"] + + update_source = None + + if dest_link in nh_details["links"].keys(): + ip_addr = nh_details["links"][dest_link][addr_type].split("/")[0] + # Loopback interface + if "source_link" in peer and peer["source_link"] == "lo": + update_source = topo[router]["links"]["lo"][addr_type].split("/")[0] + + neigh_cxt = "neighbor {}".format(ip_addr) + + if add_neigh: + config_data.append("{} remote-as {}".format(neigh_cxt, remote_as)) + if addr_type == "ipv6": + config_data.append("address-family ipv6 unicast") + config_data.append("{} activate".format(neigh_cxt)) + + disable_connected = peer.setdefault("disable_connected_check", False) + keep_alive = peer.setdefault("keepalivetimer", 60) + hold_down = peer.setdefault("holddowntimer", 180) + password = peer.setdefault("password", None) + no_password = peer.setdefault("no_password", None) + max_hop_limit = peer.setdefault("ebgp_multihop", 1) + graceful_restart = peer.setdefault("graceful-restart", None) + graceful_restart_helper = peer.setdefault("graceful-restart-helper", None) + graceful_restart_disable = peer.setdefault("graceful-restart-disable", None) + + if update_source: + config_data.append( + "{} update-source {}".format(neigh_cxt, update_source) + ) + if disable_connected: + config_data.append( + "{} disable-connected-check".format(disable_connected) + ) + if update_source: + config_data.append( + "{} update-source {}".format(neigh_cxt, update_source) + ) + if int(keep_alive) != 60 and int(hold_down) != 180: + config_data.append( + "{} timers {} {}".format(neigh_cxt, keep_alive, hold_down) + ) + + if graceful_restart: + config_data.append("{} graceful-restart".format(neigh_cxt)) + elif graceful_restart == False: + config_data.append("no {} graceful-restart".format(neigh_cxt)) + + if graceful_restart_helper: + config_data.append("{} graceful-restart-helper".format(neigh_cxt)) + elif graceful_restart_helper == False: + config_data.append("no {} graceful-restart-helper".format(neigh_cxt)) + + if graceful_restart_disable: + config_data.append("{} graceful-restart-disable".format(neigh_cxt)) + elif graceful_restart_disable == False: + config_data.append("no {} graceful-restart-disable".format(neigh_cxt)) + + if password: + config_data.append("{} password {}".format(neigh_cxt, password)) + + if no_password: + config_data.append("no {} password {}".format(neigh_cxt, no_password)) + + if max_hop_limit > 1: + config_data.append( + "{} ebgp-multihop {}".format(neigh_cxt, max_hop_limit) + ) + config_data.append("{} enforce-multihop".format(neigh_cxt)) + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return config_data + + +def __create_bgp_unicast_address_family( + topo, input_dict, router, addr_type, add_neigh=True +): + """ + API prints bgp global config to bgp_json file. + + Parameters + ---------- + * `bgp_cfg` : BGP class variables have BGP config saved in it for + particular router, + * `local_as_no` : Local as number + * `router_id` : Router-id + * `ecmp_path` : ECMP max path + * `gr_enable` : BGP global gracefull restart config + """ + + config_data = [] + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + bgp_data = input_dict["address_family"] + neigh_data = bgp_data[addr_type]["unicast"]["neighbor"] + + for peer_name, peer_dict in deepcopy(neigh_data).iteritems(): + for dest_link, peer in peer_dict["dest_link"].iteritems(): + deactivate = None + activate = None + nh_details = topo[peer_name] + activate_addr_family = peer.setdefault("activate", None) + deactivate_addr_family = peer.setdefault("deactivate", None) + # Loopback interface + if "source_link" in peer and peer["source_link"] == "lo": + for destRouterLink, data in sorted(nh_details["links"].iteritems()): + if "type" in data and data["type"] == "loopback": + if dest_link == destRouterLink: + ip_addr = nh_details["links"][destRouterLink][ + addr_type + ].split("/")[0] + + # Physical interface + else: + if dest_link in nh_details["links"].keys(): + + ip_addr = nh_details["links"][dest_link][addr_type].split("/")[0] + if addr_type == "ipv4" and bgp_data["ipv6"]: + deactivate = nh_details["links"][dest_link]["ipv6"].split("/")[ + 0 + ] + + neigh_cxt = "neighbor {}".format(ip_addr) + config_data.append("address-family {} unicast".format(addr_type)) + + if activate_addr_family is not None: + config_data.append( + "address-family {} unicast".format(activate_addr_family) + ) + + config_data.append("{} activate".format(neigh_cxt)) + + if deactivate and activate_addr_family is None: + config_data.append("no neighbor {} activate".format(deactivate)) + + if deactivate_addr_family is not None: + config_data.append( + "address-family {} unicast".format(deactivate_addr_family) + ) + config_data.append("no {} activate".format(neigh_cxt)) + + next_hop_self = peer.setdefault("next_hop_self", None) + send_community = peer.setdefault("send_community", None) + prefix_lists = peer.setdefault("prefix_lists", {}) + route_maps = peer.setdefault("route_maps", {}) + no_send_community = peer.setdefault("no_send_community", None) + allowas_in = peer.setdefault("allowas-in", None) + + # next-hop-self + if next_hop_self is not None: + if next_hop_self is True: + config_data.append("{} next-hop-self".format(neigh_cxt)) + else: + config_data.append("no {} next-hop-self".format(neigh_cxt)) + + # send_community + if send_community: + config_data.append("{} send-community".format(neigh_cxt)) + + # no_send_community + if no_send_community: + config_data.append( + "no {} send-community {}".format(neigh_cxt, no_send_community) + ) + + if "allowas_in" in peer: + allow_as_in = peer["allowas_in"] + config_data.append("{} allowas-in {}".format(neigh_cxt, allow_as_in)) + + if "no_allowas_in" in peer: + allow_as_in = peer["no_allowas_in"] + config_data.append("no {} allowas-in {}".format(neigh_cxt, allow_as_in)) + if prefix_lists: + for prefix_list in prefix_lists: + name = prefix_list.setdefault("name", {}) + direction = prefix_list.setdefault("direction", "in") + del_action = prefix_list.setdefault("delete", False) + if not name: + logger.info( + "Router %s: 'name' not present in " + "input_dict for BGP neighbor prefix lists", + router, + ) + else: + cmd = "{} prefix-list {} {}".format(neigh_cxt, name, direction) + if del_action: + cmd = "no {}".format(cmd) + config_data.append(cmd) + + if route_maps: + for route_map in route_maps: + name = route_map.setdefault("name", {}) + direction = route_map.setdefault("direction", "in") + del_action = route_map.setdefault("delete", False) + if not name: + logger.info( + "Router %s: 'name' not present in " + "input_dict for BGP neighbor route name", + router, + ) + else: + cmd = "{} route-map {} {}".format(neigh_cxt, name, direction) + if del_action: + cmd = "no {}".format(cmd) + config_data.append(cmd) + + if allowas_in: + number_occurences = allowas_in.setdefault("number_occurences", {}) + del_action = allowas_in.setdefault("delete", False) + + cmd = "{} allowas-in {}".format(neigh_cxt, number_occurences) + + if del_action: + cmd = "no {}".format(cmd) + + config_data.append(cmd) + + return config_data + + +def modify_bgp_config_when_bgpd_down(tgen, topo, input_dict): + """ + API will save the current config to router's /etc/frr/ for BGPd + daemon(bgpd.conf file) + + Paramters + --------- + * `tgen` : Topogen object + * `topo` : json file data + * `input_dict` : defines for which router, and which config + needs to be modified + + Usage: + ------ + # Modify graceful-restart config not to set f-bit + # and write to /etc/frr + + # Api call to delete advertised networks + input_dict_2 = { + "r5": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + { + "network": "101.0.20.1/32", + "no_of_network": 5, + "delete": True + } + ], + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + { + "network": "5::1/128", + "no_of_network": 5, + "delete": True + } + ], + } + } + } + } + } + } + + result = modify_bgp_config_when_bgpd_down(tgen, topo, input_dict) + + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + try: + + global LOGDIR + + result = create_router_bgp( + tgen, topo, input_dict, build=False, load_config=False + ) + if result is not True: + return result + + # Copy bgp config file to /etc/frr + for dut in input_dict.keys(): + router_list = tgen.routers() + for router, rnode in router_list.iteritems(): + if router != dut: + continue + + TMPDIR = os.path.join(LOGDIR, tgen.modname) + + logger.info("Delete BGP config when BGPd is down in {}".format(router)) + # Reading the config from /tmp/topotests and + # copy to /etc/frr/bgpd.conf + cmd = "cat {}/{}/{} >> /etc/frr/bgpd.conf".format( + TMPDIR, router, FRRCFG_FILE + ) + router_list[router].run(cmd) + + except Exception as e: + # handle any exception + logger.error("Error %s occured. Arguments %s.", e.message, e.args) + + # Traceback + errormsg = traceback.format_exc() + logger.error(errormsg) + return errormsg + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return True + + +############################################# +# Verification APIs +############################################# +@retry(attempts=3, wait=2, return_is_str=True) +def verify_router_id(tgen, topo, input_dict): + """ + Running command "show ip bgp json" for DUT and reading router-id + from input_dict and verifying with command output. + 1. Statically modfified router-id should take place + 2. When static router-id is deleted highest loopback should + become router-id + 3. When loopback intf is down then highest physcial intf + should become router-id + + Parameters + ---------- + * `tgen`: topogen object + * `topo`: input json file data + * `input_dict`: input dictionary, have details of Device Under Test, for + which user wants to test the data + Usage + ----- + # Verify if router-id for r1 is 12.12.12.12 + input_dict = { + "r1":{ + "router_id": "12.12.12.12" + } + # Verify that router-id for r1 is highest interface ip + input_dict = { + "routers": ["r1"] + } + result = verify_router_id(tgen, topo, input_dict) + + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + for router in input_dict.keys(): + if router not in tgen.routers(): + continue + + rnode = tgen.routers()[router] + + del_router_id = input_dict[router]["bgp"].setdefault("del_router_id", False) + + 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)) + + # Once router-id is deleted, highest interface ip should become + # router-id + if del_router_id: + 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)) + + if router_id == router_id_out: + logger.info("Found expected router-id %s for router %s", router_id, router) + else: + errormsg = ( + "Router-id for router:{} mismatch, expected:" + " {} but found:{}".format(router, router_id, router_id_out) + ) + return errormsg + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return True + + +@retry(attempts=50, wait=3, return_is_str=True) +def verify_bgp_convergence(tgen, topo, dut=None): + """ + API will verify if BGP is converged with in the given time frame. + Running "show bgp summary json" command and verify bgp neighbor + state is established, + Parameters + ---------- + * `tgen`: topogen object + * `topo`: input json file data + * `dut`: device under test + Usage + ----- + # To veriry is BGP is converged for all the routers used in + topology + results = verify_bgp_convergence(tgen, topo, dut="r1") + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: verify_bgp_convergence()") + for router, rnode in tgen.routers().iteritems(): + if "bgp" not in topo["routers"][router]: + continue + + if dut is not None and dut != router: + continue + + logger.info("Verifying BGP Convergence on router %s:", router) + show_bgp_json = run_frr_cmd(rnode, "show bgp vrf all summary json", isjson=True) + # Verifying output dictionary show_bgp_json is empty or not + if not bool(show_bgp_json): + errormsg = "BGP is not running" + return errormsg + + # To find neighbor ip type + bgp_data_list = topo["routers"][router]["bgp"] + + if type(bgp_data_list) is not list: + bgp_data_list = [bgp_data_list] + + for bgp_data in bgp_data_list: + if "vrf" in bgp_data: + vrf = bgp_data["vrf"] + if vrf is None: + vrf = "default" + else: + vrf = "default" + + # To find neighbor ip type + bgp_addr_type = bgp_data["address_family"] + if "l2vpn" in bgp_addr_type: + total_evpn_peer = 0 + + if "neighbor" not in bgp_addr_type["l2vpn"]["evpn"]: + continue + + bgp_neighbors = bgp_addr_type["l2vpn"]["evpn"]["neighbor"] + total_evpn_peer += len(bgp_neighbors) + + no_of_evpn_peer = 0 + for bgp_neighbor, peer_data in bgp_neighbors.items(): + for _addr_type, dest_link_dict in peer_data.items(): + data = topo["routers"][bgp_neighbor]["links"] + for dest_link in dest_link_dict.keys(): + if dest_link in data: + peer_details = peer_data[_addr_type][dest_link] + + neighbor_ip = data[dest_link][_addr_type].split("/")[0] + nh_state = None + + if ( + "ipv4Unicast" in show_bgp_json[vrf] + or "ipv6Unicast" in show_bgp_json[vrf] + ): + errormsg = ( + "[DUT: %s] VRF: %s, " + "ipv4Unicast/ipv6Unicast" + " address-family present" + " under l2vpn" % (router, vrf) + ) + return errormsg + + l2VpnEvpn_data = show_bgp_json[vrf]["l2VpnEvpn"][ + "peers" + ] + nh_state = l2VpnEvpn_data[neighbor_ip]["state"] + + if nh_state == "Established": + no_of_evpn_peer += 1 + + if no_of_evpn_peer == total_evpn_peer: + logger.info( + "[DUT: %s] VRF: %s, BGP is Converged for " "epvn peers", + router, + vrf, + ) + else: + errormsg = ( + "[DUT: %s] VRF: %s, BGP is not converged " + "for evpn peers" % (router, vrf) + ) + return errormsg + else: + for addr_type in bgp_addr_type.keys(): + if not check_address_types(addr_type): + continue + total_peer = 0 + + bgp_neighbors = bgp_addr_type[addr_type]["unicast"]["neighbor"] + + for bgp_neighbor in bgp_neighbors: + total_peer += len(bgp_neighbors[bgp_neighbor]["dest_link"]) + + for addr_type in bgp_addr_type.keys(): + if not check_address_types(addr_type): + continue + bgp_neighbors = bgp_addr_type[addr_type]["unicast"]["neighbor"] + + no_of_peer = 0 + for bgp_neighbor, peer_data in bgp_neighbors.items(): + for dest_link in peer_data["dest_link"].keys(): + data = topo["routers"][bgp_neighbor]["links"] + if dest_link in data: + peer_details = peer_data["dest_link"][dest_link] + # for link local neighbors + if ( + "neighbor_type" in peer_details + and peer_details["neighbor_type"] == "link-local" + ): + neighbor_ip = get_ipv6_linklocal_address( + topo["routers"], bgp_neighbor, dest_link + ) + elif "source_link" in peer_details: + neighbor_ip = topo["routers"][bgp_neighbor][ + "links" + ][peer_details["source_link"]][addr_type].split( + "/" + )[ + 0 + ] + elif ( + "neighbor_type" in peer_details + and peer_details["neighbor_type"] == "unnumbered" + ): + neighbor_ip = data[dest_link]["peer-interface"] + else: + neighbor_ip = data[dest_link][addr_type].split("/")[ + 0 + ] + nh_state = None + + if addr_type == "ipv4": + ipv4_data = show_bgp_json[vrf]["ipv4Unicast"][ + "peers" + ] + nh_state = ipv4_data[neighbor_ip]["state"] + else: + ipv6_data = show_bgp_json[vrf]["ipv6Unicast"][ + "peers" + ] + nh_state = ipv6_data[neighbor_ip]["state"] + + if nh_state == "Established": + no_of_peer += 1 + + if no_of_peer == total_peer: + logger.info( + "[DUT: %s] VRF: %s, BGP is Converged for %s address-family", + router, + vrf, + addr_type, + ) + else: + errormsg = ( + "[DUT: %s] VRF: %s, BGP is not converged for %s address-family" + % (router, vrf, addr_type) + ) + return errormsg + + logger.debug("Exiting API: verify_bgp_convergence()") + return True + + +@retry(attempts=3, wait=4, return_is_str=True) +def verify_bgp_community( + tgen, addr_type, router, network, input_dict=None, vrf=None, bestpath=False +): + """ + API to veiryf BGP large community is attached in route for any given + DUT by running "show bgp ipv4/6 {route address} json" command. + + Parameters + ---------- + * `tgen`: topogen object + * `addr_type` : ip type, ipv4/ipv6 + * `dut`: Device Under Test + * `network`: network for which set criteria needs to be verified + * `input_dict`: having details like - for which router, community and + values needs to be verified + * `vrf`: VRF name + * `bestpath`: To check best path cli + + Usage + ----- + networks = ["200.50.2.0/32"] + input_dict = { + "largeCommunity": "2:1:1 2:2:2 2:3:3 2:4:4 2:5:5" + } + result = verify_bgp_community(tgen, "ipv4", dut, network, input_dict=None) + + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: verify_bgp_community()") + if router not in tgen.routers(): + return False + + rnode = tgen.routers()[router] + + logger.info( + "Verifying BGP community attributes on dut %s: for %s " "network %s", + router, + addr_type, + network, + ) + + command = "show bgp" + + sleep(5) + for net in network: + if vrf: + cmd = "{} vrf {} {} {} json".format(command, vrf, addr_type, net) + elif bestpath: + cmd = "{} {} {} bestpath json".format(command, addr_type, net) + else: + cmd = "{} {} {} json".format(command, addr_type, net) + + show_bgp_json = run_frr_cmd(rnode, cmd, isjson=True) + if "paths" not in show_bgp_json: + return "Prefix {} not found in BGP table of router: {}".format(net, router) + + as_paths = show_bgp_json["paths"] + found = False + for i in range(len(as_paths)): + if ( + "largeCommunity" in show_bgp_json["paths"][i] + or "community" in show_bgp_json["paths"][i] + ): + found = True + logger.info( + "Large Community attribute is found for route:" " %s in router: %s", + net, + router, + ) + if input_dict is not None: + for criteria, comm_val in input_dict.items(): + show_val = show_bgp_json["paths"][i][criteria]["string"] + if comm_val == show_val: + logger.info( + "Verifying BGP %s for prefix: %s" + " in router: %s, found expected" + " value: %s", + criteria, + net, + router, + comm_val, + ) + else: + errormsg = ( + "Failed: Verifying BGP attribute" + " {} for route: {} in router: {}" + ", expected value: {} but found" + ": {}".format(criteria, net, router, comm_val, show_val) + ) + return errormsg + + if not found: + errormsg = ( + "Large Community attribute is not found for route: " + "{} in router: {} ".format(net, router) + ) + return errormsg + + logger.debug("Exiting lib API: verify_bgp_community()") + return True + + +def modify_as_number(tgen, topo, input_dict): + """ + API reads local_as and remote_as from user defined input_dict and + modify router"s ASNs accordingly. Router"s config is modified and + recent/changed config is loadeded to router. + + Parameters + ---------- + * `tgen` : Topogen object + * `topo` : json file data + * `input_dict` : defines for which router ASNs needs to be modified + + Usage + ----- + To modify ASNs for router r1 + input_dict = { + "r1": { + "bgp": { + "local_as": 131079 + } + } + result = modify_as_number(tgen, topo, input_dict) + + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + try: + + new_topo = deepcopy(topo["routers"]) + router_dict = {} + for router in input_dict.keys(): + # Remove bgp configuration + + router_dict.update({router: {"bgp": {"delete": True}}}) + + new_topo[router]["bgp"]["local_as"] = input_dict[router]["bgp"]["local_as"] + + logger.info("Removing bgp configuration") + create_router_bgp(tgen, topo, router_dict) + + logger.info("Applying modified bgp configuration") + create_router_bgp(tgen, new_topo) + + except Exception as e: + # handle any exception + logger.error("Error %s occured. Arguments %s.", e.message, e.args) + + # Traceback + errormsg = traceback.format_exc() + logger.error(errormsg) + return errormsg + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return True + + +@retry(attempts=3, wait=2, return_is_str=True) +def verify_as_numbers(tgen, topo, input_dict): + """ + This API is to verify AS numbers for given DUT by running + "show ip bgp neighbor json" command. Local AS and Remote AS + will ve verified with input_dict data and command output. + + Parameters + ---------- + * `tgen`: topogen object + * `topo`: input json file data + * `addr_type` : ip type, ipv4/ipv6 + * `input_dict`: defines - for which router, AS numbers needs to be verified + + Usage + ----- + input_dict = { + "r1": { + "bgp": { + "local_as": 131079 + } + } + } + result = verify_as_numbers(tgen, topo, addr_type, input_dict) + + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + for router in input_dict.keys(): + if router not in tgen.routers(): + continue + + rnode = tgen.routers()[router] + + logger.info("Verifying AS numbers for dut %s:", router) + + show_ip_bgp_neighbor_json = run_frr_cmd( + rnode, "show ip bgp neighbor json", isjson=True + ) + local_as = input_dict[router]["bgp"]["local_as"] + bgp_addr_type = topo["routers"][router]["bgp"]["address_family"] + + for addr_type in bgp_addr_type: + if not check_address_types(addr_type): + continue + + bgp_neighbors = bgp_addr_type[addr_type]["unicast"]["neighbor"] + + for bgp_neighbor, peer_data in bgp_neighbors.iteritems(): + remote_as = input_dict[bgp_neighbor]["bgp"]["local_as"] + for dest_link, peer_dict in peer_data["dest_link"].iteritems(): + neighbor_ip = None + data = topo["routers"][bgp_neighbor]["links"] + + if dest_link in data: + neighbor_ip = data[dest_link][addr_type].split("/")[0] + neigh_data = show_ip_bgp_neighbor_json[neighbor_ip] + # Verify Local AS for router + if neigh_data["localAs"] != local_as: + errormsg = ( + "Failed: Verify local_as for dut {}," + " found: {} but expected: {}".format( + router, neigh_data["localAs"], local_as + ) + ) + return errormsg + else: + logger.info( + "Verified local_as for dut %s, found" " expected: %s", + router, + local_as, + ) + + # Verify Remote AS for neighbor + if neigh_data["remoteAs"] != remote_as: + errormsg = ( + "Failed: Verify remote_as for dut " + "{}'s neighbor {}, found: {} but " + "expected: {}".format( + router, bgp_neighbor, neigh_data["remoteAs"], remote_as + ) + ) + return errormsg + else: + logger.info( + "Verified remote_as for dut %s's " + "neighbor %s, found expected: %s", + router, + bgp_neighbor, + remote_as, + ) + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return True + + +@retry(attempts=50, wait=3, return_is_str=True) +def verify_bgp_convergence_from_running_config(tgen, dut=None): + """ + API to verify BGP convergence b/w loopback and physical interface. + This API would be used when routers have BGP neighborship is loopback + to physical or vice-versa + + Parameters + ---------- + * `tgen`: topogen object + * `dut`: device under test + + Usage + ----- + results = verify_bgp_convergence_bw_lo_and_phy_intf(tgen, topo, + dut="r1") + + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + for router, rnode in tgen.routers().iteritems(): + if dut is not None and dut != router: + continue + + logger.info("Verifying BGP Convergence on router %s:", router) + show_bgp_json = run_frr_cmd(rnode, "show bgp vrf all summary json", isjson=True) + # Verifying output dictionary show_bgp_json is empty or not + if not bool(show_bgp_json): + errormsg = "BGP is not running" + return errormsg + + for vrf, addr_family_data in show_bgp_json.items(): + for address_family, neighborship_data in addr_family_data.items(): + total_peer = 0 + no_of_peer = 0 + + total_peer = len(neighborship_data["peers"].keys()) + + for peer, peer_data in neighborship_data["peers"].items(): + if peer_data["state"] == "Established": + no_of_peer += 1 + + if total_peer != no_of_peer: + errormsg = ( + "[DUT: %s] VRF: %s, BGP is not converged" + " for peer: %s" % (router, vrf, peer) + ) + return errormsg + + logger.info("[DUT: %s]: vrf: %s, BGP is Converged", router, vrf) + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + + return True + + +def clear_bgp(tgen, addr_type, router, vrf=None): + """ + This API is to clear bgp neighborship by running + clear ip bgp */clear bgp ipv6 * command, + + Parameters + ---------- + * `tgen`: topogen object + * `addr_type`: ip type ipv4/ipv6 + * `router`: device under test + * `vrf`: vrf name + + Usage + ----- + clear_bgp(tgen, addr_type, "r1") + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + if router not in tgen.routers(): + return False + + rnode = tgen.routers()[router] + + if vrf: + if type(vrf) is not list: + vrf = [vrf] + + # Clearing BGP + logger.info("Clearing BGP neighborship for router %s..", router) + if addr_type == "ipv4": + if vrf: + for _vrf in vrf: + run_frr_cmd(rnode, "clear ip bgp vrf {} *".format(_vrf)) + else: + run_frr_cmd(rnode, "clear ip bgp *") + elif addr_type == "ipv6": + if vrf: + for _vrf in vrf: + run_frr_cmd(rnode, "clear bgp vrf {} ipv6 *".format(_vrf)) + else: + run_frr_cmd(rnode, "clear bgp ipv6 *") + else: + run_frr_cmd(rnode, "clear bgp *") + + sleep(5) + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + + +def clear_bgp_and_verify(tgen, topo, router): + """ + This API is to clear bgp neighborship and verify bgp neighborship + is coming up(BGP is converged) usinf "show bgp summary json" command + and also verifying for all bgp neighbors uptime before and after + clear bgp sessions is different as the uptime must be changed once + bgp sessions are cleared using "clear ip bgp */clear bgp ipv6 *" cmd. + + Parameters + ---------- + * `tgen`: topogen object + * `topo`: input json file data + * `router`: device under test + + Usage + ----- + result = clear_bgp_and_verify(tgen, topo, addr_type, dut) + + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + if router not in tgen.routers(): + return False + + rnode = tgen.routers()[router] + + peer_uptime_before_clear_bgp = {} + sleeptime = 3 + + # Verifying BGP convergence before bgp clear command + for retry in range(50): + # Waiting for BGP to converge + logger.info( + "Waiting for %s sec for BGP to converge on router" " %s...", + sleeptime, + router, + ) + sleep(sleeptime) + + show_bgp_json = run_frr_cmd(rnode, "show bgp summary json", isjson=True) + # Verifying output dictionary show_bgp_json is empty or not + if not bool(show_bgp_json): + errormsg = "BGP is not running" + return errormsg + + # To find neighbor ip type + bgp_addr_type = topo["routers"][router]["bgp"]["address_family"] + total_peer = 0 + for addr_type in bgp_addr_type.keys(): + + if not check_address_types(addr_type): + continue + + bgp_neighbors = bgp_addr_type[addr_type]["unicast"]["neighbor"] + + for bgp_neighbor in bgp_neighbors: + total_peer += len(bgp_neighbors[bgp_neighbor]["dest_link"]) + + no_of_peer = 0 + for addr_type in bgp_addr_type: + bgp_neighbors = bgp_addr_type[addr_type]["unicast"]["neighbor"] + + for bgp_neighbor, peer_data in bgp_neighbors.iteritems(): + for dest_link, peer_dict in peer_data["dest_link"].iteritems(): + data = topo["routers"][bgp_neighbor]["links"] + + if dest_link in data: + neighbor_ip = data[dest_link][addr_type].split("/")[0] + if addr_type == "ipv4": + ipv4_data = show_bgp_json["ipv4Unicast"]["peers"] + nh_state = ipv4_data[neighbor_ip]["state"] + + # Peer up time dictionary + peer_uptime_before_clear_bgp[bgp_neighbor] = ipv4_data[ + neighbor_ip + ]["peerUptimeEstablishedEpoch"] + else: + ipv6_data = show_bgp_json["ipv6Unicast"]["peers"] + nh_state = ipv6_data[neighbor_ip]["state"] + + # Peer up time dictionary + peer_uptime_before_clear_bgp[bgp_neighbor] = ipv6_data[ + neighbor_ip + ]["peerUptimeEstablishedEpoch"] + + if nh_state == "Established": + no_of_peer += 1 + + if no_of_peer == total_peer: + logger.info("BGP is Converged for router %s before bgp" " clear", router) + break + else: + logger.info( + "BGP is not yet Converged for router %s " "before bgp clear", router + ) + else: + errormsg = ( + "TIMEOUT!! BGP is not converged in {} seconds for" + " router {}".format(retry * sleeptime, router) + ) + return errormsg + + # Clearing BGP + logger.info("Clearing BGP neighborship for router %s..", router) + for addr_type in bgp_addr_type.keys(): + if addr_type == "ipv4": + run_frr_cmd(rnode, "clear ip bgp *") + elif addr_type == "ipv6": + run_frr_cmd(rnode, "clear bgp ipv6 *") + + peer_uptime_after_clear_bgp = {} + # Verifying BGP convergence after bgp clear command + for retry in range(50): + + # Waiting for BGP to converge + logger.info( + "Waiting for %s sec for BGP to converge on router" " %s...", + sleeptime, + router, + ) + sleep(sleeptime) + + show_bgp_json = run_frr_cmd(rnode, "show bgp summary json", isjson=True) + # Verifying output dictionary show_bgp_json is empty or not + if not bool(show_bgp_json): + errormsg = "BGP is not running" + return errormsg + + # To find neighbor ip type + bgp_addr_type = topo["routers"][router]["bgp"]["address_family"] + total_peer = 0 + for addr_type in bgp_addr_type.keys(): + if not check_address_types(addr_type): + continue + + bgp_neighbors = bgp_addr_type[addr_type]["unicast"]["neighbor"] + + for bgp_neighbor in bgp_neighbors: + total_peer += len(bgp_neighbors[bgp_neighbor]["dest_link"]) + + no_of_peer = 0 + for addr_type in bgp_addr_type: + bgp_neighbors = bgp_addr_type[addr_type]["unicast"]["neighbor"] + + for bgp_neighbor, peer_data in bgp_neighbors.iteritems(): + for dest_link, peer_dict in peer_data["dest_link"].iteritems(): + data = topo["routers"][bgp_neighbor]["links"] + + if dest_link in data: + neighbor_ip = data[dest_link][addr_type].split("/")[0] + if addr_type == "ipv4": + ipv4_data = show_bgp_json["ipv4Unicast"]["peers"] + nh_state = ipv4_data[neighbor_ip]["state"] + peer_uptime_after_clear_bgp[bgp_neighbor] = ipv4_data[ + neighbor_ip + ]["peerUptimeEstablishedEpoch"] + else: + ipv6_data = show_bgp_json["ipv6Unicast"]["peers"] + nh_state = ipv6_data[neighbor_ip]["state"] + # Peer up time dictionary + peer_uptime_after_clear_bgp[bgp_neighbor] = ipv6_data[ + neighbor_ip + ]["peerUptimeEstablishedEpoch"] + + if nh_state == "Established": + no_of_peer += 1 + + if no_of_peer == total_peer: + logger.info("BGP is Converged for router %s after bgp clear", router) + break + else: + logger.info( + "BGP is not yet Converged for router %s after" " bgp clear", router + ) + else: + errormsg = ( + "TIMEOUT!! BGP is not converged in {} seconds for" + " router {}".format(retry * sleeptime, router) + ) + return errormsg + + # Comparing peerUptimeEstablishedEpoch dictionaries + if peer_uptime_before_clear_bgp != peer_uptime_after_clear_bgp: + logger.info("BGP neighborship is reset after clear BGP on router %s", router) + else: + errormsg = ( + "BGP neighborship is not reset after clear bgp on router" + " {}".format(router) + ) + return errormsg + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return True + + +def verify_bgp_timers_and_functionality(tgen, topo, input_dict): + """ + To verify BGP timer config, execute "show ip bgp neighbor json" command + and verify bgp timers with input_dict data. + To veirfy bgp timers functonality, shutting down peer interface + and verify BGP neighborship status. + + Parameters + ---------- + * `tgen`: topogen object + * `topo`: input json file data + * `addr_type`: ip type, ipv4/ipv6 + * `input_dict`: defines for which router, bgp timers needs to be verified + + Usage: + # To verify BGP timers for neighbor r2 of router r1 + input_dict = { + "r1": { + "bgp": { + "bgp_neighbors":{ + "r2":{ + "keepalivetimer": 5, + "holddowntimer": 15, + }}}}} + result = verify_bgp_timers_and_functionality(tgen, topo, "ipv4", + input_dict) + + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + sleep(5) + router_list = tgen.routers() + for router in input_dict.keys(): + if router not in router_list: + continue + + rnode = router_list[router] + + logger.info("Verifying bgp timers functionality, DUT is %s:", router) + + show_ip_bgp_neighbor_json = run_frr_cmd( + rnode, "show ip bgp neighbor json", isjson=True + ) + + bgp_addr_type = input_dict[router]["bgp"]["address_family"] + + for addr_type in bgp_addr_type: + if not check_address_types(addr_type): + continue + + bgp_neighbors = bgp_addr_type[addr_type]["unicast"]["neighbor"] + for bgp_neighbor, peer_data in bgp_neighbors.iteritems(): + for dest_link, peer_dict in peer_data["dest_link"].iteritems(): + data = topo["routers"][bgp_neighbor]["links"] + + keepalivetimer = peer_dict["keepalivetimer"] + holddowntimer = peer_dict["holddowntimer"] + + if dest_link in data: + neighbor_ip = data[dest_link][addr_type].split("/")[0] + neighbor_intf = data[dest_link]["interface"] + + # Verify HoldDownTimer for neighbor + bgpHoldTimeMsecs = show_ip_bgp_neighbor_json[neighbor_ip][ + "bgpTimerHoldTimeMsecs" + ] + if bgpHoldTimeMsecs != holddowntimer * 1000: + errormsg = ( + "Verifying holddowntimer for bgp " + "neighbor {} under dut {}, found: {} " + "but expected: {}".format( + neighbor_ip, + router, + bgpHoldTimeMsecs, + holddowntimer * 1000, + ) + ) + return errormsg + + # Verify KeepAliveTimer for neighbor + bgpKeepAliveTimeMsecs = show_ip_bgp_neighbor_json[neighbor_ip][ + "bgpTimerKeepAliveIntervalMsecs" + ] + if bgpKeepAliveTimeMsecs != keepalivetimer * 1000: + errormsg = ( + "Verifying keepalivetimer for bgp " + "neighbor {} under dut {}, found: {} " + "but expected: {}".format( + neighbor_ip, + router, + bgpKeepAliveTimeMsecs, + keepalivetimer * 1000, + ) + ) + return errormsg + + #################### + # Shutting down peer interface after keepalive time and + # after some time bringing up peer interface. + # verifying BGP neighborship in (hold down-keep alive) + # time, it should not go down + #################### + + # Wait till keep alive time + logger.info("=" * 20) + logger.info("Scenario 1:") + logger.info( + "Shutdown and bring up peer interface: %s " + "in keep alive time : %s sec and verify " + " BGP neighborship is intact in %s sec ", + neighbor_intf, + keepalivetimer, + (holddowntimer - keepalivetimer), + ) + logger.info("=" * 20) + logger.info("Waiting for %s sec..", keepalivetimer) + sleep(keepalivetimer) + + # Shutting down peer ineterface + logger.info( + "Shutting down interface %s on router %s", + neighbor_intf, + bgp_neighbor, + ) + topotest.interface_set_status( + router_list[bgp_neighbor], neighbor_intf, ifaceaction=False + ) + + # Bringing up peer interface + sleep(5) + logger.info( + "Bringing up interface %s on router %s..", + neighbor_intf, + bgp_neighbor, + ) + topotest.interface_set_status( + router_list[bgp_neighbor], neighbor_intf, ifaceaction=True + ) + + # Verifying BGP neighborship is intact in + # (holddown - keepalive) time + for timer in range( + keepalivetimer, holddowntimer, int(holddowntimer / 3) + ): + logger.info("Waiting for %s sec..", keepalivetimer) + sleep(keepalivetimer) + sleep(2) + show_bgp_json = run_frr_cmd( + rnode, "show bgp summary json", isjson=True + ) + + if addr_type == "ipv4": + ipv4_data = show_bgp_json["ipv4Unicast"]["peers"] + nh_state = ipv4_data[neighbor_ip]["state"] + else: + ipv6_data = show_bgp_json["ipv6Unicast"]["peers"] + nh_state = ipv6_data[neighbor_ip]["state"] + + if timer == (holddowntimer - keepalivetimer): + if nh_state != "Established": + errormsg = ( + "BGP neighborship has not gone " + "down in {} sec for neighbor {}".format( + timer, bgp_neighbor + ) + ) + return errormsg + else: + logger.info( + "BGP neighborship is intact in %s" + " sec for neighbor %s", + timer, + bgp_neighbor, + ) + + #################### + # Shutting down peer interface and verifying that BGP + # neighborship is going down in holddown time + #################### + logger.info("=" * 20) + logger.info("Scenario 2:") + logger.info( + "Shutdown peer interface: %s and verify BGP" + " neighborship has gone down in hold down " + "time %s sec", + neighbor_intf, + holddowntimer, + ) + logger.info("=" * 20) + + logger.info( + "Shutting down interface %s on router %s..", + neighbor_intf, + bgp_neighbor, + ) + topotest.interface_set_status( + router_list[bgp_neighbor], neighbor_intf, ifaceaction=False + ) + + # Verifying BGP neighborship is going down in holddown time + for timer in range( + keepalivetimer, + (holddowntimer + keepalivetimer), + int(holddowntimer / 3), + ): + logger.info("Waiting for %s sec..", keepalivetimer) + sleep(keepalivetimer) + sleep(2) + show_bgp_json = run_frr_cmd( + rnode, "show bgp summary json", isjson=True + ) + + if addr_type == "ipv4": + ipv4_data = show_bgp_json["ipv4Unicast"]["peers"] + nh_state = ipv4_data[neighbor_ip]["state"] + else: + ipv6_data = show_bgp_json["ipv6Unicast"]["peers"] + nh_state = ipv6_data[neighbor_ip]["state"] + + if timer == holddowntimer: + if nh_state == "Established": + errormsg = ( + "BGP neighborship has not gone " + "down in {} sec for neighbor {}".format( + timer, bgp_neighbor + ) + ) + return errormsg + else: + logger.info( + "BGP neighborship has gone down in" + " %s sec for neighbor %s", + timer, + bgp_neighbor, + ) + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return True + + +@retry(attempts=3, wait=4, return_is_str=True) +def verify_bgp_attributes( + tgen, + addr_type, + dut, + static_routes, + rmap_name=None, + input_dict=None, + seq_id=None, + nexthop=None, +): + """ + API will verify BGP attributes set by Route-map for given prefix and + DUT. it will run "show bgp ipv4/ipv6 {prefix_address} json" command + in DUT to verify BGP attributes set by route-map, Set attributes + values will be read from input_dict and verified with command output. + + * `tgen`: topogen object + * `addr_type` : ip type, ipv4/ipv6 + * `dut`: Device Under Test + * `static_routes`: Static Routes for which BGP set attributes needs to be + verified + * `rmap_name`: route map name for which set criteria needs to be verified + * `input_dict`: defines for which router, AS numbers needs + * `seq_id`: sequence number of rmap, default is None + + Usage + ----- + # To verify BGP attribute "localpref" set to 150 and "med" set to 30 + for prefix 10.0.20.1/32 in router r3. + input_dict = { + "r3": { + "route_maps": { + "rmap_match_pf_list1": [ + { + "action": "PERMIT", + "match": {"prefix_list": "pf_list_1"}, + "set": {"localpref": 150, "med": 30} + } + ], + }, + "as_path": "500 400" + } + } + static_routes (list) = ["10.0.20.1/32"] + + + + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: verify_bgp_attributes()") + for router, rnode in tgen.routers().iteritems(): + if router != dut: + continue + + logger.info("Verifying BGP set attributes for dut {}:".format(router)) + + for static_route in static_routes: + cmd = "show bgp {} {} json".format(addr_type, static_route) + show_bgp_json = run_frr_cmd(rnode, cmd, isjson=True) + + dict_to_test = [] + tmp_list = [] + + if "route_maps" in input_dict.values()[0]: + for rmap_router in input_dict.keys(): + for rmap, values in input_dict[rmap_router]["route_maps"].items(): + if rmap == rmap_name: + dict_to_test = values + for rmap_dict in values: + if seq_id is not None: + if type(seq_id) is not list: + seq_id = [seq_id] + + if "seq_id" in rmap_dict: + rmap_seq_id = rmap_dict["seq_id"] + for _seq_id in seq_id: + if _seq_id == rmap_seq_id: + tmp_list.append(rmap_dict) + if tmp_list: + dict_to_test = tmp_list + + value = None + for rmap_dict in dict_to_test: + if "set" in rmap_dict: + for criteria in rmap_dict["set"].keys(): + found = False + for path in show_bgp_json["paths"]: + if criteria not in path: + continue + + if criteria == "aspath": + value = path[criteria]["string"] + else: + value = path[criteria] + + if rmap_dict["set"][criteria] == value: + found = True + logger.info( + "Verifying BGP " + "attribute {} for" + " route: {} in " + "router: {}, found" + " expected value:" + " {}".format( + criteria, + static_route, + dut, + value, + ) + ) + break + + if not found: + errormsg = ( + "Failed: Verifying BGP " + "attribute {} for route:" + " {} in router: {}, " + " expected value: {} but" + " found: {}".format( + criteria, + static_route, + dut, + rmap_dict["set"][criteria], + value, + ) + ) + return errormsg + + logger.debug("Exiting lib API: verify_bgp_attributes()") + return True + + +@retry(attempts=4, wait=2, return_is_str=True, initial_wait=2) +def verify_best_path_as_per_bgp_attribute( + tgen, addr_type, router, input_dict, attribute +): + """ + API is to verify best path according to BGP attributes for given routes. + "show bgp ipv4/6 json" command will be run and verify best path according + to shortest as-path, highest local-preference and med, lowest weight and + route origin IGP>EGP>INCOMPLETE. + Parameters + ---------- + * `tgen` : topogen object + * `addr_type` : ip type, ipv4/ipv6 + * `tgen` : topogen object + * `attribute` : calculate best path using this attribute + * `input_dict`: defines different routes to calculate for which route + best path is selected + Usage + ----- + # To verify best path for routes 200.50.2.0/32 and 200.60.2.0/32 from + router r7 to router r1(DUT) as per shortest as-path attribute + input_dict = { + "r7": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + { + "network": "200.50.2.0/32" + }, + { + "network": "200.60.2.0/32" + } + ] + } + } + } + } + } + } + attribute = "locPrf" + result = verify_best_path_as_per_bgp_attribute(tgen, "ipv4", dut, \ + input_dict, attribute) + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + if router not in tgen.routers(): + return False + + rnode = tgen.routers()[router] + + # Verifying show bgp json + command = "show bgp" + + sleep(2) + logger.info("Verifying router %s RIB for best path:", router) + + static_route = False + advertise_network = False + for route_val in input_dict.values(): + if "static_routes" in route_val: + static_route = True + networks = route_val["static_routes"] + else: + advertise_network = True + net_data = route_val["bgp"]["address_family"][addr_type]["unicast"] + networks = net_data["advertise_networks"] + + for network in networks: + _network = network["network"] + no_of_ip = network.setdefault("no_of_ip", 1) + vrf = network.setdefault("vrf", None) + + if vrf: + cmd = "{} vrf {}".format(command, vrf) + else: + cmd = command + + cmd = "{} {}".format(cmd, addr_type) + cmd = "{} json".format(cmd) + sh_ip_bgp_json = run_frr_cmd(rnode, cmd, isjson=True) + + routes = generate_ips(_network, no_of_ip) + for route in routes: + route = str(ipaddress.ip_network(unicode(route))) + + if route in sh_ip_bgp_json["routes"]: + route_attributes = sh_ip_bgp_json["routes"][route] + _next_hop = None + compare = None + attribute_dict = {} + for route_attribute in route_attributes: + next_hops = route_attribute["nexthops"] + for next_hop in next_hops: + next_hop_ip = next_hop["ip"] + attribute_dict[next_hop_ip] = route_attribute[attribute] + + # AS_PATH attribute + if attribute == "path": + # Find next_hop for the route have minimum as_path + _next_hop = min( + attribute_dict, key=lambda x: len(set(attribute_dict[x])) + ) + compare = "SHORTEST" + + # LOCAL_PREF attribute + elif attribute == "locPrf": + # Find next_hop for the route have highest local preference + _next_hop = max( + attribute_dict, key=(lambda k: attribute_dict[k]) + ) + compare = "HIGHEST" + + # WEIGHT attribute + elif attribute == "weight": + # Find next_hop for the route have highest weight + _next_hop = max( + attribute_dict, key=(lambda k: attribute_dict[k]) + ) + compare = "HIGHEST" + + # ORIGIN attribute + elif attribute == "origin": + # Find next_hop for the route have IGP as origin, - + # - rule is IGP>EGP>INCOMPLETE + _next_hop = [ + key + for (key, value) in attribute_dict.iteritems() + if value == "IGP" + ][0] + compare = "" + + # MED attribute + elif attribute == "metric": + # Find next_hop for the route have LOWEST MED + _next_hop = min( + attribute_dict, key=(lambda k: attribute_dict[k]) + ) + compare = "LOWEST" + + # Show ip route + if addr_type == "ipv4": + command_1 = "show ip route" + else: + command_1 = "show ipv6 route" + + if vrf: + cmd = "{} vrf {} json".format(command_1, vrf) + else: + cmd = "{} json".format(command_1) + + rib_routes_json = run_frr_cmd(rnode, cmd, isjson=True) + + # Verifying output dictionary rib_routes_json is not empty + if not bool(rib_routes_json): + errormsg = "No route found in RIB of router {}..".format(router) + return errormsg + + st_found = False + nh_found = False + # Find best is installed in RIB + if route in rib_routes_json: + st_found = True + # Verify next_hop in rib_routes_json + if ( + rib_routes_json[route][0]["nexthops"][0]["ip"] + in attribute_dict + ): + nh_found = True + else: + errormsg = ( + "Incorrect Nexthop for BGP route {} in " + "RIB of router {}, Expected: {}, Found:" + " {}\n".format( + route, + router, + rib_routes_json[route][0]["nexthops"][0]["ip"], + _next_hop, + ) + ) + return errormsg + + if st_found and nh_found: + logger.info( + "Best path for prefix: %s with next_hop: %s is " + "installed according to %s %s: (%s) in RIB of " + "router %s", + route, + _next_hop, + compare, + attribute, + attribute_dict[_next_hop], + router, + ) + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return True + + +def verify_best_path_as_per_admin_distance( + tgen, addr_type, router, input_dict, attribute +): + """ + API is to verify best path according to admin distance for given + route. "show ip/ipv6 route json" command will be run and verify + best path accoring to shortest admin distanc. + + Parameters + ---------- + * `addr_type` : ip type, ipv4/ipv6 + * `dut`: Device Under Test + * `tgen` : topogen object + * `attribute` : calculate best path using admin distance + * `input_dict`: defines different routes with different admin distance + to calculate for which route best path is selected + Usage + ----- + # To verify best path for route 200.50.2.0/32 from router r2 to + router r1(DUT) as per shortest admin distance which is 60. + input_dict = { + "r2": { + "static_routes": [{"network": "200.50.2.0/32", \ + "admin_distance": 80, "next_hop": "10.0.0.14"}, + {"network": "200.50.2.0/32", \ + "admin_distance": 60, "next_hop": "10.0.0.18"}] + }} + attribute = "locPrf" + result = verify_best_path_as_per_admin_distance(tgen, "ipv4", dut, \ + input_dict, attribute): + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + router_list = tgen.routers() + if router not in router_list: + return False + + rnode = tgen.routers()[router] + + sleep(5) + logger.info("Verifying router %s RIB for best path:", router) + + # Show ip route cmd + if addr_type == "ipv4": + command = "show ip route json" + else: + command = "show ipv6 route json" + + for routes_from_router in input_dict.keys(): + sh_ip_route_json = router_list[routes_from_router].vtysh_cmd( + command, isjson=True + ) + networks = input_dict[routes_from_router]["static_routes"] + for network in networks: + route = network["network"] + + route_attributes = sh_ip_route_json[route] + _next_hop = None + compare = None + attribute_dict = {} + for route_attribute in route_attributes: + next_hops = route_attribute["nexthops"] + for next_hop in next_hops: + next_hop_ip = next_hop["ip"] + attribute_dict[next_hop_ip] = route_attribute["distance"] + + # Find next_hop for the route have LOWEST Admin Distance + _next_hop = min(attribute_dict, key=(lambda k: attribute_dict[k])) + compare = "LOWEST" + + # Show ip route + rib_routes_json = run_frr_cmd(rnode, command, isjson=True) + + # Verifying output dictionary rib_routes_json is not empty + if not bool(rib_routes_json): + errormsg = "No route found in RIB of router {}..".format(router) + return errormsg + + st_found = False + nh_found = False + # Find best is installed in RIB + if route in rib_routes_json: + st_found = True + # Verify next_hop in rib_routes_json + if rib_routes_json[route][0]["nexthops"][0]["ip"] == _next_hop: + nh_found = True + else: + errormsg = ( + "Nexthop {} is Missing for BGP route {}" + " in RIB of router {}\n".format(_next_hop, route, router) + ) + return errormsg + + if st_found and nh_found: + logger.info( + "Best path for prefix: %s is installed according" + " to %s %s: (%s) in RIB of router %s", + route, + compare, + attribute, + attribute_dict[_next_hop], + router, + ) + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return True + + +@retry(attempts=5, wait=2, return_is_str=True, initial_wait=2) +def verify_bgp_rib(tgen, addr_type, dut, input_dict, next_hop=None, aspath=None): + """ + This API is to verify whether bgp rib has any + matching route for a nexthop. + + Parameters + ---------- + * `tgen`: topogen object + * `dut`: input dut router name + * `addr_type` : ip type ipv4/ipv6 + * `input_dict` : input dict, has details of static routes + * `next_hop`[optional]: next_hop which needs to be verified, + default = static + * 'aspath'[optional]: aspath which needs to be verified + + Usage + ----- + dut = 'r1' + next_hop = "192.168.1.10" + input_dict = topo['routers'] + aspath = "100 200 300" + result = verify_bgp_rib(tgen, addr_type, dut, tgen, input_dict, + next_hop, aspath) + + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: verify_bgp_rib()") + + router_list = tgen.routers() + additional_nexthops_in_required_nhs = [] + list1 = [] + list2 = [] + for routerInput in input_dict.keys(): + for router, rnode in router_list.iteritems(): + if router != dut: + continue + + # Verifying RIB routes + command = "show bgp" + + # Static routes + sleep(2) + logger.info("Checking router {} BGP RIB:".format(dut)) + + if "static_routes" in input_dict[routerInput]: + static_routes = input_dict[routerInput]["static_routes"] + + for static_route in static_routes: + found_routes = [] + missing_routes = [] + st_found = False + nh_found = False + + vrf = static_route.setdefault("vrf", None) + community = static_route.setdefault("community", None) + largeCommunity = static_route.setdefault("largeCommunity", None) + + if vrf: + cmd = "{} vrf {} {}".format(command, vrf, addr_type) + + if community: + cmd = "{} community {}".format(cmd, community) + + if largeCommunity: + cmd = "{} large-community {}".format(cmd, largeCommunity) + else: + cmd = "{} {}".format(command, addr_type) + + cmd = "{} json".format(cmd) + + rib_routes_json = run_frr_cmd(rnode, cmd, isjson=True) + + # Verifying output dictionary rib_routes_json is not empty + if bool(rib_routes_json) == False: + errormsg = "No route found in rib of router {}..".format(router) + return errormsg + + network = static_route["network"] + + if "no_of_ip" in static_route: + no_of_ip = static_route["no_of_ip"] + else: + no_of_ip = 1 + + # Generating IPs for verification + ip_list = generate_ips(network, no_of_ip) + + for st_rt in ip_list: + st_rt = str(ipaddress.ip_network(unicode(st_rt))) + + _addr_type = validate_ip_address(st_rt) + if _addr_type != addr_type: + continue + + if st_rt in rib_routes_json["routes"]: + st_found = True + found_routes.append(st_rt) + + if next_hop: + if not isinstance(next_hop, list): + next_hop = [next_hop] + list1 = next_hop + + found_hops = [ + rib_r["ip"] + for rib_r in rib_routes_json["routes"][st_rt][0][ + "nexthops" + ] + ] + list2 = found_hops + + missing_list_of_nexthops = set(list2).difference(list1) + additional_nexthops_in_required_nhs = set( + list1 + ).difference(list2) + + if list2: + if additional_nexthops_in_required_nhs: + logger.info( + "Missing nexthop %s for route" + " %s in RIB of router %s\n", + additional_nexthops_in_required_nhs, + st_rt, + dut, + ) + errormsg = ( + "Nexthop {} is Missing for " + "route {} in RIB of router {}\n".format( + additional_nexthops_in_required_nhs, + st_rt, + dut, + ) + ) + return errormsg + else: + nh_found = True + if aspath: + found_paths = rib_routes_json["routes"][st_rt][0][ + "path" + ] + if aspath == found_paths: + aspath_found = True + logger.info( + "Found AS path {} for route" + " {} in RIB of router " + "{}\n".format(aspath, st_rt, dut) + ) + else: + errormsg = ( + "AS Path {} is missing for route" + "for route {} in RIB of router {}\n".format( + aspath, st_rt, dut + ) + ) + return errormsg + + else: + missing_routes.append(st_rt) + + if nh_found: + logger.info( + "Found next_hop {} for all bgp" + " routes in RIB of" + " router {}\n".format(next_hop, router) + ) + + if len(missing_routes) > 0: + errormsg = ( + "Missing route in RIB of router {}, " + "routes: {}\n".format(dut, missing_routes) + ) + return errormsg + + if found_routes: + logger.info( + "Verified routes in router {} BGP RIB, " + "found routes are: {} \n".format(dut, found_routes) + ) + continue + + if "bgp" not in input_dict[routerInput]: + continue + + # Advertise networks + bgp_data_list = input_dict[routerInput]["bgp"] + + if type(bgp_data_list) is not list: + bgp_data_list = [bgp_data_list] + + for bgp_data in bgp_data_list: + vrf_id = bgp_data.setdefault("vrf", None) + if vrf_id: + cmd = "{} vrf {} {}".format(command, vrf_id, addr_type) + else: + cmd = "{} {}".format(command, addr_type) + + cmd = "{} json".format(cmd) + + rib_routes_json = run_frr_cmd(rnode, cmd, isjson=True) + + # Verifying output dictionary rib_routes_json is not empty + if bool(rib_routes_json) == False: + errormsg = "No route found in rib of router {}..".format(router) + return errormsg + + bgp_net_advertise = bgp_data["address_family"][addr_type]["unicast"] + advertise_network = bgp_net_advertise.setdefault( + "advertise_networks", [] + ) + + for advertise_network_dict in advertise_network: + found_routes = [] + missing_routes = [] + found = False + + network = advertise_network_dict["network"] + + if "no_of_network" in advertise_network_dict: + no_of_network = advertise_network_dict["no_of_network"] + else: + no_of_network = 1 + + # Generating IPs for verification + ip_list = generate_ips(network, no_of_network) + + for st_rt in ip_list: + st_rt = str(ipaddress.ip_network(unicode(st_rt))) + + _addr_type = validate_ip_address(st_rt) + if _addr_type != addr_type: + continue + + if st_rt in rib_routes_json["routes"]: + found = True + found_routes.append(st_rt) + else: + found = False + missing_routes.append(st_rt) + + if len(missing_routes) > 0: + errormsg = ( + "Missing route in BGP RIB of router {}," + " are: {}\n".format(dut, missing_routes) + ) + return errormsg + + if found_routes: + logger.info( + "Verified routes in router {} BGP RIB, found " + "routes are: {}\n".format(dut, found_routes) + ) + + logger.debug("Exiting lib API: verify_bgp_rib()") + return True + + +@retry(attempts=4, wait=2, return_is_str=True, initial_wait=2) +def verify_graceful_restart(tgen, topo, addr_type, input_dict, dut, peer): + """ + This API is to verify verify_graceful_restart configuration of DUT and + cross verify the same from the peer bgp routerrouter. + + Parameters + ---------- + * `tgen`: topogen object + * `topo`: input json file data + * `addr_type` : ip type ipv4/ipv6 + * `input_dict`: input dictionary, have details of Device Under Test, for + which user wants to test the data + * `dut`: input dut router name + * `peer`: input peer router name + + Usage + ----- + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link":{ + "r1": { + "graceful-restart": True + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link":{ + "r1": { + "graceful-restart": True + } + } + } + } + } + } + } + } + } + } + + result = verify_graceful_restart(tgen, topo, addr_type, input_dict, + dut = "r1", peer = 'r2') + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + for router, rnode in tgen.routers().iteritems(): + if router != dut: + continue + + bgp_addr_type = topo["routers"][dut]["bgp"]["address_family"] + + if addr_type in bgp_addr_type: + if not check_address_types(addr_type): + continue + + bgp_neighbors = bgp_addr_type[addr_type]["unicast"]["neighbor"] + + for bgp_neighbor, peer_data in bgp_neighbors.items(): + if bgp_neighbor != peer: + continue + + for dest_link, peer_dict in peer_data["dest_link"].items(): + data = topo["routers"][bgp_neighbor]["links"] + + if dest_link in data: + neighbor_ip = data[dest_link][addr_type].split("/")[0] + + logger.info( + "[DUT: {}]: Checking bgp graceful-restart show" + " o/p {}".format(dut, neighbor_ip) + ) + + show_bgp_graceful_json = None + + show_bgp_graceful_json = run_frr_cmd( + rnode, + "show bgp {} neighbor {} graceful-restart json".format( + addr_type, neighbor_ip + ), + isjson=True, + ) + + show_bgp_graceful_json_out = show_bgp_graceful_json[neighbor_ip] + + if show_bgp_graceful_json_out["neighborAddr"] == neighbor_ip: + logger.info( + "[DUT: {}]: Neighbor ip matched {}".format(dut, neighbor_ip) + ) + else: + errormsg = "[DUT: {}]: Neighbor ip NOT a matched {}".format( + dut, neighbor_ip + ) + return errormsg + + lmode = None + rmode = None + # Local GR mode + if "address_family" in input_dict[dut]["bgp"]: + bgp_neighbors = input_dict[dut]["bgp"]["address_family"][addr_type][ + "unicast" + ]["neighbor"][peer]["dest_link"] + + for dest_link, data in bgp_neighbors.items(): + if ( + "graceful-restart-helper" in data + and data["graceful-restart-helper"] + ): + lmode = "Helper" + elif "graceful-restart" in data and data["graceful-restart"]: + lmode = "Restart" + elif ( + "graceful-restart-disable" in data + and data["graceful-restart-disable"] + ): + lmode = "Disable" + else: + lmode = None + + if lmode is None: + if "graceful-restart" in input_dict[dut]["bgp"]: + + if ( + "graceful-restart" in input_dict[dut]["bgp"]["graceful-restart"] + and input_dict[dut]["bgp"]["graceful-restart"][ + "graceful-restart" + ] + ): + lmode = "Restart*" + elif ( + "graceful-restart-disable" + in input_dict[dut]["bgp"]["graceful-restart"] + and input_dict[dut]["bgp"]["graceful-restart"][ + "graceful-restart-disable" + ] + ): + lmode = "Disable*" + else: + lmode = "Helper*" + else: + lmode = "Helper*" + + if lmode == "Disable" or lmode == "Disable*": + return True + + # Remote GR mode + if "address_family" in input_dict[peer]["bgp"]: + bgp_neighbors = input_dict[peer]["bgp"]["address_family"][addr_type][ + "unicast" + ]["neighbor"][dut]["dest_link"] + + for dest_link, data in bgp_neighbors.items(): + if ( + "graceful-restart-helper" in data + and data["graceful-restart-helper"] + ): + rmode = "Helper" + elif "graceful-restart" in data and data["graceful-restart"]: + rmode = "Restart" + elif ( + "graceful-restart-disable" in data + and data["graceful-restart-disable"] + ): + rmode = "Disable" + else: + rmode = None + + if rmode is None: + if "graceful-restart" in input_dict[peer]["bgp"]: + + if ( + "graceful-restart" + in input_dict[peer]["bgp"]["graceful-restart"] + and input_dict[peer]["bgp"]["graceful-restart"][ + "graceful-restart" + ] + ): + rmode = "Restart" + elif ( + "graceful-restart-disable" + in input_dict[peer]["bgp"]["graceful-restart"] + and input_dict[peer]["bgp"]["graceful-restart"][ + "graceful-restart-disable" + ] + ): + rmode = "Disable" + else: + rmode = "Helper" + else: + rmode = "Helper" + + if show_bgp_graceful_json_out["localGrMode"] == lmode: + logger.info( + "[DUT: {}]: localGrMode : {} ".format( + dut, show_bgp_graceful_json_out["localGrMode"] + ) + ) + else: + errormsg = ( + "[DUT: {}]: localGrMode is not correct" + " Expected: {}, Found: {}".format( + dut, lmode, show_bgp_graceful_json_out["localGrMode"] + ) + ) + return errormsg + + if show_bgp_graceful_json_out["remoteGrMode"] == rmode: + logger.info( + "[DUT: {}]: remoteGrMode : {} ".format( + dut, show_bgp_graceful_json_out["remoteGrMode"] + ) + ) + elif ( + show_bgp_graceful_json_out["remoteGrMode"] == "NotApplicable" + and rmode == "Disable" + ): + logger.info( + "[DUT: {}]: remoteGrMode : {} ".format( + dut, show_bgp_graceful_json_out["remoteGrMode"] + ) + ) + else: + errormsg = ( + "[DUT: {}]: remoteGrMode is not correct" + " Expected: {}, Found: {}".format( + dut, rmode, show_bgp_graceful_json_out["remoteGrMode"] + ) + ) + return errormsg + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return True + + +@retry(attempts=4, wait=2, return_is_str=True, initial_wait=2) +def verify_r_bit(tgen, topo, addr_type, input_dict, dut, peer): + """ + This API is to verify r_bit in the BGP gr capability advertised + by the neighbor router + + Parameters + ---------- + * `tgen`: topogen object + * `topo`: input json file data + * `addr_type` : ip type ipv4/ipv6 + * `input_dict`: input dictionary, have details of Device Under Test, for + which user wants to test the data + * `dut`: input dut router name + * `peer`: peer name + Usage + ----- + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link":{ + "r1": { + "graceful-restart": True + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link":{ + "r1": { + "graceful-restart": True + } + } + } + } + } + } + } + } + } + } + result = verify_r_bit(tgen, topo, addr_type, input_dict, dut, peer) + + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + for router, rnode in tgen.routers().iteritems(): + if router != dut: + continue + + bgp_addr_type = topo["routers"][router]["bgp"]["address_family"] + + if addr_type in bgp_addr_type: + if not check_address_types(addr_type): + continue + + bgp_neighbors = bgp_addr_type[addr_type]["unicast"]["neighbor"] + + for bgp_neighbor, peer_data in bgp_neighbors.items(): + if bgp_neighbor != peer: + continue + + for dest_link, peer_dict in peer_data["dest_link"].items(): + data = topo["routers"][bgp_neighbor]["links"] + + if dest_link in data: + neighbor_ip = data[dest_link][addr_type].split("/")[0] + + logger.info( + "[DUT: {}]: Checking bgp graceful-restart show" + " o/p {}".format(dut, neighbor_ip) + ) + + show_bgp_graceful_json = run_frr_cmd( + rnode, + "show bgp {} neighbor {} graceful-restart json".format( + addr_type, neighbor_ip + ), + isjson=True, + ) + + show_bgp_graceful_json_out = show_bgp_graceful_json[neighbor_ip] + + if show_bgp_graceful_json_out["neighborAddr"] == neighbor_ip: + logger.info( + "[DUT: {}]: Neighbor ip matched {}".format(dut, neighbor_ip) + ) + else: + errormsg = "[DUT: {}]: Neighbor ip NOT a matched {}".format( + dut, neighbor_ip + ) + return errormsg + + if "rBit" in show_bgp_graceful_json_out: + if show_bgp_graceful_json_out["rBit"]: + logger.info("[DUT: {}]: Rbit true {}".format(dut, neighbor_ip)) + else: + errormsg = "[DUT: {}]: Rbit false {}".format(dut, neighbor_ip) + return errormsg + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return True + + +@retry(attempts=4, wait=2, return_is_str=True, initial_wait=2) +def verify_eor(tgen, topo, addr_type, input_dict, dut, peer): + """ + This API is to verify EOR + + Parameters + ---------- + * `tgen`: topogen object + * `topo`: input json file data + * `addr_type` : ip type ipv4/ipv6 + * `input_dict`: input dictionary, have details of DUT, for + which user wants to test the data + * `dut`: input dut router name + * `peer`: peer name + Usage + ----- + input_dict = { + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link":{ + "r1": { + "graceful-restart": True + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link":{ + "r1": { + "graceful-restart": True + } + } + } + } + } + } + } + } + } + } + + result = verify_eor(tgen, topo, addr_type, input_dict, dut, peer) + + Returns + ------- + errormsg(str) or True + """ + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + for router, rnode in tgen.routers().iteritems(): + if router != dut: + continue + + bgp_addr_type = topo["routers"][router]["bgp"]["address_family"] + + if addr_type in bgp_addr_type: + if not check_address_types(addr_type): + continue + + bgp_neighbors = bgp_addr_type[addr_type]["unicast"]["neighbor"] + + for bgp_neighbor, peer_data in bgp_neighbors.items(): + if bgp_neighbor != peer: + continue + + for dest_link, peer_dict in peer_data["dest_link"].items(): + data = topo["routers"][bgp_neighbor]["links"] + + if dest_link in data: + neighbor_ip = data[dest_link][addr_type].split("/")[0] + + logger.info( + "[DUT: %s]: Checking bgp graceful-restart" " show o/p %s", + dut, + neighbor_ip, + ) + + show_bgp_graceful_json = run_frr_cmd( + rnode, + "show bgp {} neighbor {} graceful-restart json".format( + addr_type, neighbor_ip + ), + isjson=True, + ) + + show_bgp_graceful_json_out = show_bgp_graceful_json[neighbor_ip] + + if show_bgp_graceful_json_out["neighborAddr"] == neighbor_ip: + logger.info("[DUT: %s]: Neighbor ip matched %s", dut, neighbor_ip) + else: + errormsg = "[DUT: %s]: Neighbor ip is NOT matched %s" % ( + dut, + neighbor_ip, + ) + return errormsg + + if addr_type == "ipv4": + afi = "ipv4Unicast" + elif addr_type == "ipv6": + afi = "ipv6Unicast" + else: + errormsg = "Address type %s is not supported" % (addr_type) + return errormsg + + eor_json = show_bgp_graceful_json_out[afi]["endOfRibStatus"] + if "endOfRibSend" in eor_json: + + if eor_json["endOfRibSend"]: + logger.info( + "[DUT: %s]: EOR Send true for %s " "%s", dut, neighbor_ip, afi + ) + else: + errormsg = "[DUT: %s]: EOR Send false for %s" " %s" % ( + dut, + neighbor_ip, + afi, + ) + return errormsg + + if "endOfRibRecv" in eor_json: + if eor_json["endOfRibRecv"]: + logger.info( + "[DUT: %s]: EOR Recv true %s " "%s", dut, neighbor_ip, afi + ) + else: + errormsg = "[DUT: %s]: EOR Recv false %s " "%s" % ( + dut, + neighbor_ip, + afi, + ) + return errormsg + + if "endOfRibSentAfterUpdate" in eor_json: + if eor_json["endOfRibSentAfterUpdate"]: + logger.info( + "[DUT: %s]: EOR SendTime true for %s" " %s", + dut, + neighbor_ip, + afi, + ) + else: + errormsg = "[DUT: %s]: EOR SendTime false for " "%s %s" % ( + dut, + neighbor_ip, + afi, + ) + return errormsg + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return True + + +@retry(attempts=4, wait=2, return_is_str=True, initial_wait=2) +def verify_f_bit(tgen, topo, addr_type, input_dict, dut, peer): + """ + This API is to verify f_bit in the BGP gr capability advertised + by the neighbor router + + Parameters + ---------- + * `tgen`: topogen object + * `topo`: input json file data + * `addr_type` : ip type ipv4/ipv6 + * `input_dict`: input dictionary, have details of Device Under Test, for + which user wants to test the data + * `dut`: input dut router name + * `peer`: peer name + + Usage + ----- + input_dict = { + "r1": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link":{ + "r1": { + "graceful-restart": True + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link":{ + "r1": { + "graceful-restart": True + } + } + } + } + } + } + } + } + } + } + + result = verify_f_bit(tgen, topo, 'ipv4', input_dict, dut, peer) + + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + for router, rnode in tgen.routers().iteritems(): + if router != dut: + continue + + bgp_addr_type = topo["routers"][router]["bgp"]["address_family"] + + if addr_type in bgp_addr_type: + if not check_address_types(addr_type): + continue + + bgp_neighbors = bgp_addr_type[addr_type]["unicast"]["neighbor"] + + for bgp_neighbor, peer_data in bgp_neighbors.items(): + if bgp_neighbor != peer: + continue + + for dest_link, peer_dict in peer_data["dest_link"].items(): + data = topo["routers"][bgp_neighbor]["links"] + + if dest_link in data: + neighbor_ip = data[dest_link][addr_type].split("/")[0] + + logger.info( + "[DUT: {}]: Checking bgp graceful-restart show" + " o/p {}".format(dut, neighbor_ip) + ) + + show_bgp_graceful_json = run_frr_cmd( + rnode, + "show bgp {} neighbor {} graceful-restart json".format( + addr_type, neighbor_ip + ), + isjson=True, + ) + + show_bgp_graceful_json_out = show_bgp_graceful_json[neighbor_ip] + + if show_bgp_graceful_json_out["neighborAddr"] == neighbor_ip: + logger.info( + "[DUT: {}]: Neighbor ip matched {}".format(dut, neighbor_ip) + ) + else: + errormsg = "[DUT: {}]: Neighbor ip NOT a match {}".format( + dut, neighbor_ip + ) + return errormsg + + if "ipv4Unicast" in show_bgp_graceful_json_out: + if show_bgp_graceful_json_out["ipv4Unicast"]["fBit"]: + logger.info( + "[DUT: {}]: Fbit True for {} IPv4" + " Unicast".format(dut, neighbor_ip) + ) + else: + errormsg = "[DUT: {}]: Fbit False for {} IPv4" " Unicast".format( + dut, neighbor_ip + ) + return errormsg + + elif "ipv6Unicast" in show_bgp_graceful_json_out: + if show_bgp_graceful_json_out["ipv6Unicast"]["fBit"]: + logger.info( + "[DUT: {}]: Fbit True for {} IPv6" + " Unicast".format(dut, neighbor_ip) + ) + else: + errormsg = "[DUT: {}]: Fbit False for {} IPv6" " Unicast".format( + dut, neighbor_ip + ) + return errormsg + else: + show_bgp_graceful_json_out["ipv4Unicast"] + show_bgp_graceful_json_out["ipv6Unicast"] + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return True + + +@retry(attempts=4, wait=2, return_is_str=True, initial_wait=2) +def verify_graceful_restart_timers(tgen, topo, addr_type, input_dict, dut, peer): + """ + This API is to verify graceful restart timers, configured and recieved + + Parameters + ---------- + * `tgen`: topogen object + * `topo`: input json file data + * `addr_type` : ip type ipv4/ipv6 + * `input_dict`: input dictionary, have details of Device Under Test, + for which user wants to test the data + * `dut`: input dut router name + * `peer`: peer name + Usage + ----- + # Configure graceful-restart + input_dict_1 = { + "r1": { + "bgp": { + "bgp_neighbors": { + "r3": { + "graceful-restart": "graceful-restart-helper" + } + }, + "gracefulrestart": ["restart-time 150"] + } + }, + "r3": { + "bgp": { + "bgp_neighbors": { + "r1": { + "graceful-restart": "graceful-restart" + } + } + } + } + } + + result = verify_graceful_restart_timers(tgen, topo, 'ipv4', input_dict) + + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + for router, rnode in tgen.routers().iteritems(): + if router != dut: + continue + + bgp_addr_type = topo["routers"][dut]["bgp"]["address_family"] + + if addr_type in bgp_addr_type: + if not check_address_types(addr_type): + continue + + bgp_neighbors = bgp_addr_type[addr_type]["unicast"]["neighbor"] + + for bgp_neighbor, peer_data in bgp_neighbors.items(): + if bgp_neighbor != peer: + continue + + for dest_link, peer_dict in peer_data["dest_link"].items(): + data = topo["routers"][bgp_neighbor]["links"] + + if dest_link in data: + neighbor_ip = data[dest_link][addr_type].split("/")[0] + + logger.info( + "[DUT: {}]: Checking bgp graceful-restart show" + " o/p {}".format(dut, neighbor_ip) + ) + + show_bgp_graceful_json = run_frr_cmd( + rnode, + "show bgp {} neighbor {} graceful-restart json".format( + addr_type, neighbor_ip + ), + isjson=True, + ) + + show_bgp_graceful_json_out = show_bgp_graceful_json[neighbor_ip] + if show_bgp_graceful_json_out["neighborAddr"] == neighbor_ip: + logger.info( + "[DUT: {}]: Neighbor ip matched {}".format(dut, neighbor_ip) + ) + else: + errormsg = "[DUT: {}]: Neighbor ip is NOT matched {}".format( + dut, neighbor_ip + ) + return errormsg + + # Graceful-restart timer + if "graceful-restart" in input_dict[peer]["bgp"]: + if "timer" in input_dict[peer]["bgp"]["graceful-restart"]: + for rs_timer, value in input_dict[peer]["bgp"]["graceful-restart"][ + "timer" + ].items(): + if rs_timer == "restart-time": + + receivedTimer = value + if ( + show_bgp_graceful_json_out["timers"][ + "receivedRestartTimer" + ] + == receivedTimer + ): + logger.info( + "receivedRestartTimer is {}" + " on {} from peer {}".format( + receivedTimer, router, peer + ) + ) + else: + errormsg = ( + "receivedRestartTimer is not" + " as expected {}".format(receivedTimer) + ) + return errormsg + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return True + + +@retry(attempts=4, wait=2, return_is_str=True, initial_wait=2) +def verify_gr_address_family(tgen, topo, addr_type, addr_family, dut): + """ + This API is to verify gr_address_family in the BGP gr capability advertised + by the neighbor router + + Parameters + ---------- + * `tgen`: topogen object + * `topo`: input json file data + * `addr_type` : ip type ipv4/ipv6 + * `addr_type` : ip type IPV4 Unicast/IPV6 Unicast + * `dut`: input dut router name + + Usage + ----- + + result = verify_gr_address_family(tgen, topo, "ipv4", "ipv4Unicast", "r1") + + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + for router, rnode in tgen.routers().iteritems(): + if router != dut: + continue + + bgp_addr_type = topo["routers"][router]["bgp"]["address_family"] + + if addr_type in bgp_addr_type: + if not check_address_types(addr_type): + continue + + bgp_neighbors = bgp_addr_type[addr_type]["unicast"]["neighbor"] + + for bgp_neighbor, peer_data in bgp_neighbors.items(): + for dest_link, peer_dict in peer_data["dest_link"].items(): + data = topo["routers"][bgp_neighbor]["links"] + + if dest_link in data: + neighbor_ip = data[dest_link][addr_type].split("/")[0] + + logger.info( + "[DUT: {}]: Checking bgp graceful-restart" + " show o/p {}".format(dut, neighbor_ip) + ) + + show_bgp_graceful_json = run_frr_cmd( + rnode, + "show bgp {} neighbor {} graceful-restart json".format( + addr_type, neighbor_ip + ), + isjson=True, + ) + + show_bgp_graceful_json_out = show_bgp_graceful_json[neighbor_ip] + + if show_bgp_graceful_json_out["neighborAddr"] == neighbor_ip: + logger.info("Neighbor ip matched {}".format(neighbor_ip)) + else: + errormsg = "Neighbor ip NOT a match {}".format(neighbor_ip) + return errormsg + + if addr_family == "ipv4Unicast": + if "ipv4Unicast" in show_bgp_graceful_json_out: + logger.info("ipv4Unicast present for {} ".format(neighbor_ip)) + return True + else: + errormsg = "ipv4Unicast NOT present for {} ".format(neighbor_ip) + return errormsg + + elif addr_family == "ipv6Unicast": + if "ipv6Unicast" in show_bgp_graceful_json_out: + logger.info("ipv6Unicast present for {} ".format(neighbor_ip)) + return True + else: + errormsg = "ipv6Unicast NOT present for {} ".format(neighbor_ip) + return errormsg + else: + errormsg = "Aaddress family: {} present for {} ".format( + addr_family, neighbor_ip + ) + return errormsg + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + + +@retry(attempts=5, wait=2, return_is_str=True, initial_wait=2) +def verify_attributes_for_evpn_routes( + tgen, + topo, + dut, + input_dict, + rd=None, + rt=None, + ethTag=None, + ipLen=None, + rd_peer=None, + rt_peer=None, +): + """ + API to verify rd and rt value using "sh bgp l2vpn evpn 10.1.1.1" + command. + + Parameters + ---------- + * `tgen`: topogen object + * `topo` : json file data + * `dut` : device under test + * `input_dict`: having details like - for which route, rd value + needs to be verified + * `rd` : route distinguisher + * `rt` : route target + * `ethTag` : Ethernet Tag + * `ipLen` : IP prefix length + * `rd_peer` : Peer name from which RD will be auto-generated + * `rt_peer` : Peer name from which RT will be auto-generated + + Usage + ----- + input_dict_1 = { + "r1": { + "static_routes": [{ + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED" + }] + } + } + + result = verify_attributes_for_evpn_routes(tgen, topo, + input_dict, rd = "10.0.0.33:1") + + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + for router in input_dict.keys(): + rnode = tgen.routers()[dut] + + if "static_routes" in input_dict[router]: + for static_route in input_dict[router]["static_routes"]: + network = static_route["network"] + + if "vrf" in static_route: + vrf = static_route["vrf"] + + if type(network) is not list: + network = [network] + + for route in network: + route = route.split("/")[0] + _addr_type = validate_ip_address(route) + if "v4" in _addr_type: + input_afi = "v4" + elif "v6" in _addr_type: + input_afi = "v6" + + cmd = "show bgp l2vpn evpn {} json".format(route) + evpn_rd_value_json = run_frr_cmd(rnode, cmd, isjson=True) + if not bool(evpn_rd_value_json): + errormsg = "No output for '{}' cli".format(cmd) + return errormsg + + if rd is not None and rd != "auto": + logger.info( + "[DUT: %s]: Verifying rd value for " "evpn route %s:", + dut, + route, + ) + + if rd in evpn_rd_value_json: + rd_value_json = evpn_rd_value_json[rd] + if rd_value_json["rd"] != rd: + errormsg = ( + "[DUT: %s] Failed: Verifying" + " RD value for EVPN route: %s" + "[FAILED]!!, EXPECTED : %s " + " FOUND : %s" + % (dut, route, rd, rd_value_json["rd"]) + ) + return errormsg + + else: + logger.info( + "[DUT %s]: Verifying RD value for" + " EVPN route: %s [PASSED]|| " + "Found Exprected: %s", + dut, + route, + rd, + ) + return True + + else: + errormsg = ( + "[DUT: %s] RD : %s is not present" + " in cli json output" % (dut, rd) + ) + return errormsg + + if rd == "auto": + logger.info( + "[DUT: %s]: Verifying auto-rd value for " "evpn route %s:", + dut, + route, + ) + + if rd_peer: + index = 1 + vni_dict = {} + + rnode = tgen.routers()[rd_peer] + vrfs = topo["routers"][rd_peer]["vrfs"] + for vrf_dict in vrfs: + vni_dict[vrf_dict["name"]] = index + index += 1 + + show_bgp_json = run_frr_cmd( + rnode, "show bgp vrf all summary json", isjson=True + ) + + # Verifying output dictionary show_bgp_json is empty + if not bool(show_bgp_json): + errormsg = "BGP is not running" + return errormsg + + show_bgp_json_vrf = show_bgp_json[vrf] + for afi, afi_data in show_bgp_json_vrf.items(): + if input_afi not in afi: + continue + router_id = afi_data["routerId"] + + rd = "{}:{}".format(router_id, vni_dict[vrf]) + if rd in evpn_rd_value_json: + rd_value_json = evpn_rd_value_json[rd] + if rd_value_json["rd"] != rd: + errormsg = ( + "[DUT: %s] Failed: Verifying" + " RD value for EVPN route: %s" + "[FAILED]!!, EXPECTED : %s " + " FOUND : %s" + % (dut, route, rd, rd_value_json["rd"]) + ) + return errormsg + + else: + logger.info( + "[DUT %s]: Verifying RD value for" + " EVPN route: %s [PASSED]|| " + "Found Exprected: %s", + dut, + route, + rd, + ) + return True + + else: + errormsg = ( + "[DUT: %s] RD : %s is not present" + " in cli json output" % (dut, rd) + ) + return errormsg + + if rt == "auto": + logger.info( + "[DUT: %s]: Verifying auto-rt value for " "evpn route %s:", + dut, + route, + ) + + if rt_peer: + vni_dict = {} + + rnode = tgen.routers()[rt_peer] + show_bgp_json = run_frr_cmd( + rnode, "show bgp vrf all summary json", isjson=True + ) + + # Verifying output dictionary show_bgp_json is empty + if not bool(show_bgp_json): + errormsg = "BGP is not running" + return errormsg + + show_bgp_json_vrf = show_bgp_json[vrf] + for afi, afi_data in show_bgp_json_vrf.items(): + if input_afi not in afi: + continue + as_num = afi_data["as"] + + show_vrf_vni_json = run_frr_cmd( + rnode, "show vrf vni json", isjson=True + ) + + vrfs = show_vrf_vni_json["vrfs"] + for vrf_dict in vrfs: + if vrf_dict["vrf"] == vrf: + vni_dict[vrf_dict["vrf"]] = str(vrf_dict["vni"]) + + # If AS is 4 byte, FRR uses only the lower 2 bytes of ASN+VNI + # for auto derived RT value. + if as_num > 65535: + as_bin = bin(as_num) + as_bin = as_bin[-16:] + as_num = int(as_bin, 2) + + rt = "{}:{}".format(str(as_num), vni_dict[vrf]) + for _rd, route_data in evpn_rd_value_json.items(): + if route_data["ip"] == route: + for rt_data in route_data["paths"]: + if vni_dict[vrf] == rt_data["VNI"]: + rt_string = rt_data["extendedCommunity"][ + "string" + ] + rt_input = "RT:{}".format(rt) + if rt_input not in rt_string: + errormsg = ( + "[DUT: %s] Failed:" + " Verifying RT " + "value for EVPN " + " route: %s" + "[FAILED]!!," + " EXPECTED : %s " + " FOUND : %s" + % (dut, route, rt_input, rt_string) + ) + return errormsg + + else: + logger.info( + "[DUT %s]: Verifying " + "RT value for EVPN " + "route: %s [PASSED]||" + "Found Exprected: %s", + dut, + route, + rt_input, + ) + return True + + else: + errormsg = ( + "[DUT: %s] Route : %s is not" + " present in cli json output" % (dut, route) + ) + return errormsg + + if rt is not None and rt != "auto": + logger.info( + "[DUT: %s]: Verifying rt value for " "evpn route %s:", + dut, + route, + ) + + if type(rt) is not list: + rt = [rt] + + for _rt in rt: + for _rd, route_data in evpn_rd_value_json.items(): + if route_data["ip"] == route: + for rt_data in route_data["paths"]: + rt_string = rt_data["extendedCommunity"][ + "string" + ] + rt_input = "RT:{}".format(_rt) + if rt_input not in rt_string: + errormsg = ( + "[DUT: %s] Failed: " + "Verifying RT value " + "for EVPN route: %s" + "[FAILED]!!," + " EXPECTED : %s " + " FOUND : %s" + % (dut, route, rt_input, rt_string) + ) + return errormsg + + else: + logger.info( + "[DUT %s]: Verifying RT" + " value for EVPN route:" + " %s [PASSED]|| " + "Found Exprected: %s", + dut, + route, + rt_input, + ) + return True + + else: + errormsg = ( + "[DUT: %s] Route : %s is not" + " present in cli json output" % (dut, route) + ) + return errormsg + + if ethTag is not None: + logger.info( + "[DUT: %s]: Verifying ethTag value for " "evpn route :", dut + ) + + for _rd, route_data in evpn_rd_value_json.items(): + if route_data["ip"] == route: + if route_data["ethTag"] != ethTag: + errormsg = ( + "[DUT: %s] RD: %s, Failed: " + "Verifying ethTag value " + "for EVPN route: %s" + "[FAILED]!!," + " EXPECTED : %s " + " FOUND : %s" + % ( + dut, + _rd, + route, + ethTag, + route_data["ethTag"], + ) + ) + return errormsg + + else: + logger.info( + "[DUT %s]: RD: %s, Verifying " + "ethTag value for EVPN route:" + " %s [PASSED]|| " + "Found Exprected: %s", + dut, + _rd, + route, + ethTag, + ) + return True + + else: + errormsg = ( + "[DUT: %s] RD: %s, Route : %s " + "is not present in cli json " + "output" % (dut, _rd, route) + ) + return errormsg + + if ipLen is not None: + logger.info( + "[DUT: %s]: Verifying ipLen value for " "evpn route :", dut + ) + + for _rd, route_data in evpn_rd_value_json.items(): + if route_data["ip"] == route: + if route_data["ipLen"] != int(ipLen): + errormsg = ( + "[DUT: %s] RD: %s, Failed: " + "Verifying ipLen value " + "for EVPN route: %s" + "[FAILED]!!," + " EXPECTED : %s " + " FOUND : %s" + % (dut, _rd, route, ipLen, route_data["ipLen"]) + ) + return errormsg + + else: + logger.info( + "[DUT %s]: RD: %s, Verifying " + "ipLen value for EVPN route:" + " %s [PASSED]|| " + "Found Exprected: %s", + dut, + _rd, + route, + ipLen, + ) + return True + + else: + errormsg = ( + "[DUT: %s] RD: %s, Route : %s " + "is not present in cli json " + "output " % (dut, route) + ) + return errormsg + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return False + + +@retry(attempts=5, wait=2, return_is_str=True, initial_wait=2) +def verify_evpn_routes( + tgen, topo, dut, input_dict, routeType=5, EthTag=0, next_hop=None +): + """ + API to verify evpn routes using "sh bgp l2vpn evpn" + command. + + Parameters + ---------- + * `tgen`: topogen object + * `topo` : json file data + * `dut` : device under test + * `input_dict`: having details like - for which route, rd value + needs to be verified + * `route_type` : Route type 5 is supported as of now + * `EthTag` : Ethernet tag, by-default is 0 + * `next_hop` : Prefered nexthop for the evpn routes + + Usage + ----- + input_dict_1 = { + "r1": { + "static_routes": [{ + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED" + }] + } + } + result = verify_evpn_routes(tgen, topo, input_dict) + + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + for router in input_dict.keys(): + rnode = tgen.routers()[dut] + + logger.info("[DUT: %s]: Verifying evpn routes: ", dut) + + if "static_routes" in input_dict[router]: + for static_route in input_dict[router]["static_routes"]: + network = static_route["network"] + + if type(network) is not list: + network = [network] + + missing_routes = {} + for route in network: + rd_keys = 0 + ip_len = route.split("/")[1] + route = route.split("/")[0] + + prefix = "[{}]:[{}]:[{}]:[{}]".format( + routeType, EthTag, ip_len, route + ) + + cmd = "show bgp l2vpn evpn route json" + evpn_value_json = run_frr_cmd(rnode, cmd, isjson=True) + + if not bool(evpn_value_json): + errormsg = "No output for '{}' cli".format(cmd) + return errormsg + + if evpn_value_json["numPrefix"] == 0: + errormsg = "[DUT: %s]: No EVPN prefixes exist" % (dut) + return errormsg + + for key, route_data_json in evpn_value_json.items(): + if isinstance(route_data_json, dict): + rd_keys += 1 + if prefix not in route_data_json: + missing_routes[key] = prefix + + if rd_keys == len(missing_routes.keys()): + errormsg = ( + "[DUT: %s]: " + "Missing EVPN routes: " + "%s [FAILED]!!" % (dut, list(set(missing_routes.values()))) + ) + return errormsg + + for key, route_data_json in evpn_value_json.items(): + if isinstance(route_data_json, dict): + if prefix not in route_data_json: + continue + + for paths in route_data_json[prefix]["paths"]: + for path in paths: + if path["routeType"] != routeType: + errormsg = ( + "[DUT: %s]: " + "Verifying routeType " + "for EVPN route: %s " + "[FAILED]!! " + "Expected: %s, " + "Found: %s" + % ( + dut, + prefix, + routeType, + path["routeType"], + ) + ) + return errormsg + + elif next_hop: + for nh_dict in path["nexthops"]: + if nh_dict["ip"] != next_hop: + errormsg = ( + "[DUT: %s]: " + "Verifying " + "nexthop for " + "EVPN route: %s" + "[FAILED]!! " + "Expected: %s," + " Found: %s" + % ( + dut, + prefix, + next_hop, + nh_dict["ip"], + ) + ) + return errormsg + + else: + logger.info( + "[DUT %s]: Verifying " + "EVPN route : %s, " + "routeType: %s is " + "installed " + "[PASSED]|| ", + dut, + prefix, + routeType, + ) + return True + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + + return False diff --git a/tests/topotests/lib/bgprib.py b/tests/topotests/lib/bgprib.py index 5a81036643..3d92718c78 100644 --- a/tests/topotests/lib/bgprib.py +++ b/tests/topotests/lib/bgprib.py @@ -16,14 +16,14 @@ # with this program; see the file COPYING; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -# +# # want_rd_routes = [ # {'rd':'10:1', 'p':'5.1.0.0/24', 'n':'1.1.1.1'}, # {'rd':'10:1', 'p':'5.1.0.0/24', 'n':'1.1.1.1'}, -# +# # {'rd':'10:3', 'p':'5.1.0.0/24', 'n':'3.3.3.3'}, # ] -# +# # ribRequireVpnRoutes('r2','Customer routes',want_rd_routes) # # want_unicast_routes = [ @@ -34,116 +34,133 @@ # ribRequireUnicastRoutes('r1','ipv4','','Customer routes in default',want_unicast_routes) # -from lutil import luCommand,luResult +from lutil import luCommand, luResult import json import re # gpz: get rib in json form and compare against desired routes class BgpRib: - def routes_include_wanted(self,pfxtbl,want,debug): - # helper function to RequireVpnRoutes - for pfx in pfxtbl.iterkeys(): - if debug: - print 'trying pfx ' + pfx - if pfx != want['p']: - if debug: - print 'want pfx=' + want['p'] + ', not ' + pfx - continue - if debug: - print 'have pfx=' + pfx - for r in pfxtbl[pfx]: - if debug: - print 'trying route' - nexthops = r['nexthops'] - for nh in nexthops: - if debug: - print 'trying nh ' + nh['ip'] - if nh['ip'] == want['n']: - if debug: - print 'found ' + want['n'] - return 1 - else: - if debug: - print 'want nh=' + want['n'] + ', not ' + nh['ip'] - if debug: - print 'missing route: pfx=' + want['p'] + ', nh=' + want['n'] - return 0 + def routes_include_wanted(self, pfxtbl, want, debug): + # helper function to RequireVpnRoutes + for pfx in pfxtbl.iterkeys(): + if debug: + print "trying pfx " + pfx + if pfx != want["p"]: + if debug: + print "want pfx=" + want["p"] + ", not " + pfx + continue + if debug: + print "have pfx=" + pfx + for r in pfxtbl[pfx]: + if debug: + print "trying route" + nexthops = r["nexthops"] + for nh in nexthops: + if debug: + print "trying nh " + nh["ip"] + if nh["ip"] == want["n"]: + if debug: + print "found " + want["n"] + return 1 + else: + if debug: + print "want nh=" + want["n"] + ", not " + nh["ip"] + if debug: + print "missing route: pfx=" + want["p"] + ", nh=" + want["n"] + return 0 def RequireVpnRoutes(self, target, title, wantroutes, debug=0): - import json + import json + logstr = "RequireVpnRoutes " + str(wantroutes) - #non json form for humans - luCommand(target,'vtysh -c "show bgp ipv4 vpn"','.','None','Get VPN RIB (non-json)') - ret = luCommand(target,'vtysh -c "show bgp ipv4 vpn json"','.*','None','Get VPN RIB (json)') - if re.search(r'^\s*$', ret): + # non json form for humans + luCommand( + target, + 'vtysh -c "show bgp ipv4 vpn"', + ".", + "None", + "Get VPN RIB (non-json)", + ) + ret = luCommand( + target, + 'vtysh -c "show bgp ipv4 vpn json"', + ".*", + "None", + "Get VPN RIB (json)", + ) + if re.search(r"^\s*$", ret): # degenerate case: empty json means no routes if len(wantroutes) > 0: luResult(target, False, title, logstr) return luResult(target, True, title, logstr) - rib = json.loads(ret) - rds = rib['routes']['routeDistinguishers'] - for want in wantroutes: - found = 0 - if debug: - print "want rd " + want['rd'] - for rd in rds.iterkeys(): - if rd != want['rd']: - continue - if debug: - print "found rd " + rd - table = rds[rd] - if self.routes_include_wanted(table,want,debug): - found = 1 - break - if not found: - luResult(target, False, title, logstr) - return - luResult(target, True, title, logstr) + rib = json.loads(ret) + rds = rib["routes"]["routeDistinguishers"] + for want in wantroutes: + found = 0 + if debug: + print "want rd " + want["rd"] + for rd in rds.iterkeys(): + if rd != want["rd"]: + continue + if debug: + print "found rd " + rd + table = rds[rd] + if self.routes_include_wanted(table, want, debug): + found = 1 + break + if not found: + luResult(target, False, title, logstr) + return + luResult(target, True, title, logstr) - def RequireUnicastRoutes(self,target,afi,vrf,title,wantroutes,debug=0): + def RequireUnicastRoutes(self, target, afi, vrf, title, wantroutes, debug=0): logstr = "RequireVpnRoutes " + str(wantroutes) - vrfstr = '' - if vrf != '': - vrfstr = 'vrf %s' % (vrf) + vrfstr = "" + if vrf != "": + vrfstr = "vrf %s" % (vrf) - if (afi != 'ipv4') and (afi != 'ipv6'): - print "ERROR invalid afi"; + if (afi != "ipv4") and (afi != "ipv6"): + print "ERROR invalid afi" - cmdstr = 'show bgp %s %s unicast' % (vrfstr, afi) - #non json form for humans - cmd = 'vtysh -c "%s"' % cmdstr - luCommand(target,cmd,'.','None','Get %s %s RIB (non-json)' % (vrfstr, afi)) + cmdstr = "show bgp %s %s unicast" % (vrfstr, afi) + # non json form for humans + cmd = 'vtysh -c "%s"' % cmdstr + luCommand(target, cmd, ".", "None", "Get %s %s RIB (non-json)" % (vrfstr, afi)) cmd = 'vtysh -c "%s json"' % cmdstr - ret = luCommand(target,cmd,'.*','None','Get %s %s RIB (json)' % (vrfstr, afi)) - if re.search(r'^\s*$', ret): + ret = luCommand( + target, cmd, ".*", "None", "Get %s %s RIB (json)" % (vrfstr, afi) + ) + if re.search(r"^\s*$", ret): # degenerate case: empty json means no routes if len(wantroutes) > 0: luResult(target, False, title, logstr) return luResult(target, True, title, logstr) - rib = json.loads(ret) + rib = json.loads(ret) try: - table = rib['routes'] - # KeyError: 'routes' probably means missing/bad VRF + table = rib["routes"] + # KeyError: 'routes' probably means missing/bad VRF except KeyError as err: - if vrf != '': - errstr = '-script ERROR: check if wrong vrf (%s)' % (vrf) + if vrf != "": + errstr = "-script ERROR: check if wrong vrf (%s)" % (vrf) else: - errstr = '-script ERROR: check if vrf missing' - luResult(target, False, title + errstr, logstr) - return - for want in wantroutes: - if not self.routes_include_wanted(table,want,debug): - luResult(target, False, title, logstr) - return - luResult(target, True, title, logstr) + errstr = "-script ERROR: check if vrf missing" + luResult(target, False, title + errstr, logstr) + return + for want in wantroutes: + if not self.routes_include_wanted(table, want, debug): + luResult(target, False, title, logstr) + return + luResult(target, True, title, logstr) -BgpRib=BgpRib() +BgpRib = BgpRib() + def bgpribRequireVpnRoutes(target, title, wantroutes, debug=0): BgpRib.RequireVpnRoutes(target, title, wantroutes, debug) + def bgpribRequireUnicastRoutes(target, afi, vrf, title, wantroutes, debug=0): BgpRib.RequireUnicastRoutes(target, afi, vrf, title, wantroutes, debug) diff --git a/tests/topotests/lib/common_config.py b/tests/topotests/lib/common_config.py new file mode 100644 index 0000000000..88069afc0d --- /dev/null +++ b/tests/topotests/lib/common_config.py @@ -0,0 +1,4073 @@ +# +# Copyright (c) 2019 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. +# ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +from collections import OrderedDict +from datetime import datetime +from time import sleep +from copy import deepcopy +from subprocess import call +from subprocess import STDOUT as SUB_STDOUT +from subprocess import PIPE as SUB_PIPE +from subprocess import Popen +from functools import wraps +from re import search as re_search +from tempfile import mkdtemp + +import os +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 + import ConfigParser as configparser + +from lib.topolog import logger, logger_config +from lib.topogen import TopoRouter, get_topogen +from lib.topotest import interface_set_status, version_cmp + +FRRCFG_FILE = "frr_json.conf" +FRRCFG_BKUP_FILE = "frr_json_initial.conf" + +ERROR_LIST = ["Malformed", "Failure", "Unknown", "Incomplete"] +ROUTER_LIST = [] + +#### +CD = os.path.dirname(os.path.realpath(__file__)) +PYTESTINI_PATH = os.path.join(CD, "../pytest.ini") + +# Creating tmp dir with testsuite name to avoid conflict condition when +# multiple testsuites run together. All temporary files would be created +# in this dir and this dir would be removed once testsuite run is +# completed +LOGDIR = "/tmp/topotests/" +TMPDIR = None + +# NOTE: to save execution logs to log file frrtest_log_dir must be configured +# in `pytest.ini`. +config = configparser.ConfigParser() +config.read(PYTESTINI_PATH) + +config_section = "topogen" + +if config.has_option("topogen", "verbosity"): + loglevel = config.get("topogen", "verbosity") + loglevel = loglevel.upper() +else: + loglevel = "INFO" + +if config.has_option("topogen", "frrtest_log_dir"): + frrtest_log_dir = config.get("topogen", "frrtest_log_dir") + time_stamp = datetime.time(datetime.now()) + logfile_name = "frr_test_bgp_" + frrtest_log_file = frrtest_log_dir + logfile_name + str(time_stamp) + print("frrtest_log_file..", frrtest_log_file) + + logger = logger_config.get_logger( + name="test_execution_logs", log_level=loglevel, target=frrtest_log_file + ) + print("Logs will be sent to logfile: {}".format(frrtest_log_file)) + +if config.has_option("topogen", "show_router_config"): + show_router_config = config.get("topogen", "show_router_config") +else: + show_router_config = False + +# env variable for setting what address type to test +ADDRESS_TYPES = os.environ.get("ADDRESS_TYPES") + + +# Saves sequence id numbers +SEQ_ID = {"prefix_lists": {}, "route_maps": {}} + + +def get_seq_id(obj_type, router, obj_name): + """ + Generates and saves sequence number in interval of 10 + Parameters + ---------- + * `obj_type`: prefix_lists or route_maps + * `router`: router name + *` obj_name`: name of the prefix-list or route-map + Returns + -------- + Sequence number generated + """ + + router_data = SEQ_ID[obj_type].setdefault(router, {}) + obj_data = router_data.setdefault(obj_name, {}) + seq_id = obj_data.setdefault("seq_id", 0) + + seq_id = int(seq_id) + 10 + obj_data["seq_id"] = seq_id + + return seq_id + + +def set_seq_id(obj_type, router, id, obj_name): + """ + Saves sequence number if not auto-generated and given by user + Parameters + ---------- + * `obj_type`: prefix_lists or route_maps + * `router`: router name + *` obj_name`: name of the prefix-list or route-map + """ + router_data = SEQ_ID[obj_type].setdefault(router, {}) + obj_data = router_data.setdefault(obj_name, {}) + seq_id = obj_data.setdefault("seq_id", 0) + + seq_id = int(seq_id) + int(id) + obj_data["seq_id"] = seq_id + + +class InvalidCLIError(Exception): + """Raise when the CLI command is wrong""" + + pass + + +def run_frr_cmd(rnode, cmd, isjson=False): + """ + Execute frr show commands in priviledged mode + * `rnode`: router node on which commands needs to executed + * `cmd`: Command to be executed on frr + * `isjson`: If command is to get json data or not + :return str: + """ + + if cmd: + ret_data = rnode.vtysh_cmd(cmd, isjson=isjson) + + if True: + if isjson: + logger.debug(ret_data) + print_data = rnode.vtysh_cmd(cmd.rstrip("json"), isjson=False) + else: + print_data = ret_data + + logger.info( + "Output for command [ %s] on router %s:\n%s", + cmd.rstrip("json"), + rnode.name, + print_data, + ) + return ret_data + + else: + raise InvalidCLIError("No actual cmd passed") + + +def apply_raw_config(tgen, input_dict): + + """ + API to configure raw configuration on device. This can be used for any cli + which does has not been implemented in JSON. + + Parameters + ---------- + * `tgen`: tgen onject + * `input_dict`: configuration that needs to be applied + + Usage + ----- + input_dict = { + "r2": { + "raw_config": [ + "router bgp", + "no bgp update-group-split-horizon" + ] + } + } + Returns + ------- + True or errormsg + """ + + result = True + for router_name in input_dict.keys(): + config_cmd = input_dict[router_name]["raw_config"] + + if not isinstance(config_cmd, list): + config_cmd = [config_cmd] + + frr_cfg_file = "{}/{}/{}".format(TMPDIR, router_name, FRRCFG_FILE) + with open(frr_cfg_file, "w") as cfg: + for cmd in config_cmd: + cfg.write("{}\n".format(cmd)) + + result = load_config_to_router(tgen, router_name) + + return result + + +def create_common_configuration( + tgen, router, data, config_type=None, build=False, load_config=True +): + """ + API to create object of class FRRConfig and also create frr_json.conf + file. It will create interface and common configurations and save it to + frr_json.conf and load to router + Parameters + ---------- + * `tgen`: tgen onject + * `data`: Congiguration data saved in a list. + * `router` : router id to be configured. + * `config_type` : Syntactic information while writing configuration. Should + be one of the value as mentioned in the config_map below. + * `build` : Only for initial setup phase this is set as True + Returns + ------- + True or False + """ + TMPDIR = os.path.join(LOGDIR, tgen.modname) + + fname = "{}/{}/{}".format(TMPDIR, router, FRRCFG_FILE) + + config_map = OrderedDict( + { + "general_config": "! FRR General Config\n", + "interface_config": "! Interfaces Config\n", + "static_route": "! Static Route Config\n", + "prefix_list": "! Prefix List Config\n", + "bgp_community_list": "! Community List Config\n", + "route_maps": "! Route Maps Config\n", + "bgp": "! BGP Config\n", + "vrf": "! VRF Config\n", + } + ) + + if build: + mode = "a" + elif not load_config: + mode = "a" + else: + mode = "w" + + try: + frr_cfg_fd = open(fname, mode) + if config_type: + frr_cfg_fd.write(config_map[config_type]) + for line in data: + frr_cfg_fd.write("{} \n".format(str(line))) + frr_cfg_fd.write("\n") + + except IOError as err: + logger.error( + "Unable to open FRR Config File. error(%s): %s" % (err.errno, err.strerror) + ) + return False + finally: + frr_cfg_fd.close() + + # If configuration applied from build, it will done at last + if not build and load_config: + load_config_to_router(tgen, router) + + return True + + +def kill_router_daemons(tgen, router, daemons): + """ + Router's current config would be saved to /etc/frr/ for each deamon + and deamon would be killed forcefully using SIGKILL. + * `tgen` : topogen object + * `router`: Device under test + * `daemons`: list of daemons to be killed + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + try: + router_list = tgen.routers() + + # Saving router config to /etc/frr, which will be loaded to router + # when it starts + router_list[router].vtysh_cmd("write memory") + + # Kill Daemons + result = router_list[router].killDaemons(daemons) + if len(result) > 0: + assert "Errors found post shutdown - details follow:" == 0, result + return result + + except Exception as e: + errormsg = traceback.format_exc() + logger.error(errormsg) + return errormsg + + +def start_router_daemons(tgen, router, daemons): + """ + Daemons defined by user would be started + * `tgen` : topogen object + * `router`: Device under test + * `daemons`: list of daemons to be killed + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + try: + router_list = tgen.routers() + + # Start daemons + result = router_list[router].startDaemons(daemons) + return result + + except Exception as e: + errormsg = traceback.format_exc() + logger.error(errormsg) + return errormsg + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return True + + +def kill_mininet_routers_process(tgen): + """ + Kill all mininet stale router' processes + * `tgen` : topogen object + """ + + router_list = tgen.routers() + for rname, router in router_list.iteritems(): + daemon_list = [ + "zebra", + "ospfd", + "ospf6d", + "bgpd", + "ripd", + "ripngd", + "isisd", + "pimd", + "ldpd", + "staticd", + ] + for daemon in daemon_list: + router.run("killall -9 {}".format(daemon)) + + +def check_router_status(tgen): + """ + Check if all daemons are running for all routers in topology + * `tgen` : topogen object + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + try: + router_list = tgen.routers() + for router, rnode in router_list.iteritems(): + + result = rnode.check_router_running() + if result != "": + daemons = [] + if "bgpd" in result: + daemons.append("bgpd") + if "zebra" in result: + daemons.append("zebra") + + rnode.startDaemons(daemons) + + except Exception as e: + errormsg = traceback.format_exc() + logger.error(errormsg) + return errormsg + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return True + +def getStrIO(): + """ + Return a StringIO object appropriate for the current python version. + """ + if sys.version_info[0] > 2: + return io.StringIO() + else: + return StringIO.StringIO() + +def reset_config_on_routers(tgen, routerName=None): + """ + Resets configuration on routers to the snapshot created using input JSON + file. It replaces existing router configuration with FRRCFG_BKUP_FILE + + Parameters + ---------- + * `tgen` : Topogen object + * `routerName` : router config is to be reset + """ + + logger.debug("Entering API: reset_config_on_routers") + + router_list = tgen.routers() + for rname in ROUTER_LIST: + if routerName and routerName != rname: + continue + + router = router_list[rname] + logger.info("Configuring router %s to initial test configuration", rname) + + cfg = router.run("vtysh -c 'show running'") + fname = "{}/{}/frr.sav".format(TMPDIR, rname) + dname = "{}/{}/delta.conf".format(TMPDIR, rname) + f = open(fname, "w") + for line in cfg.split("\n"): + line = line.strip() + + if ( + line == "Building configuration..." + or line == "Current configuration:" + or not line + ): + continue + f.write(line) + f.write("\n") + + f.close() + run_cfg_file = "{}/{}/frr.sav".format(TMPDIR, rname) + init_cfg_file = "{}/{}/frr_json_initial.conf".format(TMPDIR, rname) + command = "/usr/lib/frr/frr-reload.py --input {} --test {} > {}".format( + run_cfg_file, init_cfg_file, dname + ) + result = call(command, shell=True, stderr=SUB_STDOUT, stdout=SUB_PIPE) + + # Assert if command fail + if result > 0: + logger.error("Delta file creation failed. Command executed %s", command) + with open(run_cfg_file, "r") as fd: + logger.info( + "Running configuration saved in %s is:\n%s", run_cfg_file, fd.read() + ) + with open(init_cfg_file, "r") as fd: + logger.info( + "Test configuration saved in %s is:\n%s", init_cfg_file, fd.read() + ) + + err_cmd = ["/usr/bin/vtysh", "-m", "-f", run_cfg_file] + result = Popen(err_cmd, stdout=SUB_PIPE, stderr=SUB_PIPE) + output = result.communicate() + for out_data in output: + temp_data = out_data.decode("utf-8").lower() + for out_err in ERROR_LIST: + if out_err.lower() in temp_data: + logger.error( + "Found errors while validating data in" " %s", run_cfg_file + ) + raise InvalidCLIError(out_data) + raise InvalidCLIError("Unknown error in %s", output) + + f = open(dname, "r") + delta = getStrIO() + delta.write("configure terminal\n") + t_delta = f.read() + + # Don't disable debugs + check_debug = True + + for line in t_delta.split("\n"): + line = line.strip() + if line == "Lines To Delete" or line == "===============" or not line: + continue + + if line == "Lines To Add": + check_debug = False + continue + + if line == "============" or not line: + continue + + # Leave debugs and log output alone + if check_debug: + if "debug" in line or "log file" in line: + continue + + delta.write(line) + delta.write("\n") + + f.close() + + delta.write("end\n") + + output = router.vtysh_multicmd(delta.getvalue(), pretty_output=False) + + delta.close() + delta = getStrIO() + cfg = router.run("vtysh -c 'show running'") + for line in cfg.split("\n"): + line = line.strip() + delta.write(line) + delta.write("\n") + + # Router current configuration to log file or console if + # "show_router_config" is defined in "pytest.ini" + if show_router_config: + logger.info("Configuration on router {} after reset:".format(rname)) + logger.info(delta.getvalue()) + delta.close() + + logger.debug("Exiting API: reset_config_on_routers") + return True + + +def load_config_to_router(tgen, routerName, save_bkup=False): + """ + Loads configuration on router from the file FRRCFG_FILE. + + Parameters + ---------- + * `tgen` : Topogen object + * `routerName` : router for which configuration to be loaded + * `save_bkup` : If True, Saves snapshot of FRRCFG_FILE to FRRCFG_BKUP_FILE + """ + + logger.debug("Entering API: load_config_to_router") + + router_list = tgen.routers() + for rname in ROUTER_LIST: + if routerName and rname != routerName: + continue + + router = router_list[rname] + try: + frr_cfg_file = "{}/{}/{}".format(TMPDIR, rname, FRRCFG_FILE) + frr_cfg_bkup = "{}/{}/{}".format(TMPDIR, rname, FRRCFG_BKUP_FILE) + with open(frr_cfg_file, "r+") as cfg: + data = cfg.read() + logger.info( + "Applying following configuration on router" + " {}:\n{}".format(rname, data) + ) + if save_bkup: + with open(frr_cfg_bkup, "w") as bkup: + bkup.write(data) + + output = router.vtysh_multicmd(data, pretty_output=False) + for out_err in ERROR_LIST: + if out_err.lower() in output.lower(): + raise InvalidCLIError("%s" % output) + + cfg.truncate(0) + + except IOError as err: + errormsg = ( + "Unable to open config File. error(%s):" " %s", + (err.errno, err.strerror), + ) + return errormsg + + # Router current configuration to log file or console if + # "show_router_config" is defined in "pytest.ini" + if show_router_config: + logger.info("New configuration for router {}:".format(rname)) + new_config = router.run("vtysh -c 'show running'") + logger.info(new_config) + + logger.debug("Exiting API: load_config_to_router") + return True + + +def get_frr_ipv6_linklocal(tgen, router, intf=None, vrf=None): + """ + API to get the link local ipv6 address of a perticular interface using + FRR command 'show interface' + + * `tgen`: tgen onject + * `router` : router for which hightest interface should be + calculated + * `intf` : interface for which linklocal address needs to be taken + * `vrf` : VRF name + + Usage + ----- + linklocal = get_frr_ipv6_linklocal(tgen, router, "intf1", RED_A) + + Returns + ------- + 1) array of interface names to link local ips. + """ + + router_list = tgen.routers() + for rname, rnode in router_list.iteritems(): + if rname != router: + continue + + linklocal = [] + + if vrf: + cmd = "show interface vrf {}".format(vrf) + else: + cmd = "show interface" + + ifaces = router_list[router].run('vtysh -c "{}"'.format(cmd)) + + # Fix newlines (make them all the same) + ifaces = ("\n".join(ifaces.splitlines()) + "\n").splitlines() + + interface = None + ll_per_if_count = 0 + for line in ifaces: + # Interface name + m = re_search("Interface ([a-zA-Z0-9-]+) is", line) + if m: + interface = m.group(1).split(" ")[0] + ll_per_if_count = 0 + + # Interface ip + m1 = re_search("inet6 (fe80[:a-fA-F0-9]+[\/0-9]+)", line) + if m1: + local = m1.group(1) + ll_per_if_count += 1 + if ll_per_if_count > 1: + linklocal += [["%s-%s" % (interface, ll_per_if_count), local]] + else: + linklocal += [[interface, local]] + + if linklocal: + if intf: + return [_linklocal[1] for _linklocal in linklocal if _linklocal[0] == intf][ + 0 + ].split("/")[0] + return linklocal + else: + errormsg = "Link local ip missing on router {}" + return errormsg + + +def generate_support_bundle(): + """ + API to generate support bundle on any verification ste failure. + it runs a python utility, /usr/lib/frr/generate_support_bundle.py, + which basically runs defined CLIs and dumps the data to specified location + """ + + tgen = get_topogen() + router_list = tgen.routers() + test_name = sys._getframe(2).f_code.co_name + TMPDIR = os.path.join(LOGDIR, tgen.modname) + + for rname, rnode in router_list.iteritems(): + logger.info("Generating support bundle for {}".format(rname)) + rnode.run("mkdir -p /var/log/frr") + bundle_log = rnode.run("python2 /usr/lib/frr/generate_support_bundle.py") + logger.info(bundle_log) + + dst_bundle = "{}/{}/support_bundles/{}".format(TMPDIR, rname, test_name) + src_bundle = "/var/log/frr" + rnode.run("rm -rf {}".format(dst_bundle)) + rnode.run("mkdir -p {}".format(dst_bundle)) + rnode.run("mv -f {}/* {}".format(src_bundle, dst_bundle)) + + return True + + +def start_topology(tgen): + """ + Starting topology, create tmp files which are loaded to routers + to start deamons and then start routers + * `tgen` : topogen object + """ + + global TMPDIR, ROUTER_LIST + # Starting topology + tgen.start_topology() + + # Starting deamons + + router_list = tgen.routers() + ROUTER_LIST = sorted( + router_list.keys(), key=lambda x: int(re_search("\d+", x).group(0)) + ) + TMPDIR = os.path.join(LOGDIR, tgen.modname) + + 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) + + try: + os.chdir(TMPDIR) + + # Creating router named dir and empty zebra.conf bgpd.conf files + # inside the current directory + if os.path.isdir("{}".format(rname)): + os.system("rm -rf {}".format(rname)) + os.mkdir("{}".format(rname)) + os.system("chmod -R go+rw {}".format(rname)) + os.chdir("{}/{}".format(TMPDIR, rname)) + os.system("touch zebra.conf bgpd.conf") + else: + os.mkdir("{}".format(rname)) + os.system("chmod -R go+rw {}".format(rname)) + os.chdir("{}/{}".format(TMPDIR, rname)) + os.system("touch zebra.conf bgpd.conf") + + except IOError as err: + logger.error("I/O error({0}): {1}".format(err.errno, err.strerror)) + + # Loading empty zebra.conf file to router, to start the zebra deamon + router.load_config( + TopoRouter.RD_ZEBRA, "{}/{}/zebra.conf".format(TMPDIR, rname) + ) + # Loading empty bgpd.conf file to router, to start the bgp deamon + router.load_config(TopoRouter.RD_BGP, "{}/{}/bgpd.conf".format(TMPDIR, rname)) + + # Starting routers + logger.info("Starting all routers once topology is created") + tgen.start_router() + + +def stop_router(tgen, router): + """ + Router"s current config would be saved to /etc/frr/ for each deamon + and router and its deamons would be stopped. + + * `tgen` : topogen object + * `router`: Device under test + """ + + router_list = tgen.routers() + + # Saving router config to /etc/frr, which will be loaded to router + # when it starts + router_list[router].vtysh_cmd("write memory") + + # Stop router + router_list[router].stop() + + +def start_router(tgen, router): + """ + Router will started and config would be loaded from /etc/frr/ for each + deamon + + * `tgen` : topogen object + * `router`: Device under test + """ + + logger.debug("Entering lib API: start_router") + + try: + router_list = tgen.routers() + + # Router and its deamons would be started and config would + # be loaded to router for each deamon from /etc/frr + router_list[router].start() + + # Waiting for router to come up + sleep(5) + + except Exception as e: + errormsg = traceback.format_exc() + logger.error(errormsg) + return errormsg + + logger.debug("Exiting lib API: start_router()") + return True + + +def number_to_row(routerName): + """ + Returns the number for the router. + Calculation based on name a0 = row 0, a1 = row 1, b2 = row 2, z23 = row 23 + etc + """ + return int(routerName[1:]) + + +def number_to_column(routerName): + """ + Returns the number for the router. + Calculation based on name a0 = columnn 0, a1 = column 0, b2= column 1, + z23 = column 26 etc + """ + return ord(routerName[0]) - 97 + + +############################################# +# Common APIs, will be used by all protocols +############################################# + + +def create_vrf_cfg(tgen, topo, input_dict=None, build=False): + """ + Create vrf configuration for created topology. VRF + configuration is provided in input json file. + + VRF config is done in Linux Kernel: + * Create VRF + * Attach interface to VRF + * Bring up VRF + + Parameters + ---------- + * `tgen` : Topogen object + * `topo` : json file data + * `input_dict` : Input dict data, required when configuring + from testcase + * `build` : Only for initial setup phase this is set as True. + + Usage + ----- + input_dict={ + "r3": { + "links": { + "r2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "r2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"}, + "r2-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"}, + "r2-link4": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"}, + }, + "vrfs":[ + { + "name": "RED_A", + "id": "1" + }, + { + "name": "RED_B", + "id": "2" + }, + { + "name": "BLUE_A", + "id": "3", + "delete": True + }, + { + "name": "BLUE_B", + "id": "4" + } + ] + } + } + result = create_vrf_cfg(tgen, topo, input_dict) + + Returns + ------- + True or False + """ + result = True + if not input_dict: + input_dict = deepcopy(topo) + else: + input_dict = deepcopy(input_dict) + + try: + for c_router, c_data in input_dict.iteritems(): + rnode = tgen.routers()[c_router] + if "vrfs" in c_data: + for vrf in c_data["vrfs"]: + config_data = [] + del_action = vrf.setdefault("delete", False) + name = vrf.setdefault("name", None) + table_id = vrf.setdefault("id", None) + vni = vrf.setdefault("vni", None) + del_vni = vrf.setdefault("no_vni", None) + + if del_action: + # Kernel cmd- Add VRF and table + cmd = "ip link del {} type vrf table {}".format( + vrf["name"], vrf["id"] + ) + + logger.info("[DUT: %s]: Running kernel cmd [%s]", c_router, cmd) + rnode.run(cmd) + + # Kernel cmd - Bring down VRF + cmd = "ip link set dev {} down".format(name) + logger.info("[DUT: %s]: Running kernel cmd [%s]", c_router, cmd) + rnode.run(cmd) + + else: + if name and table_id: + # Kernel cmd- Add VRF and table + cmd = "ip link add {} type vrf table {}".format( + name, table_id + ) + logger.info( + "[DUT: %s]: Running kernel cmd " "[%s]", c_router, cmd + ) + rnode.run(cmd) + + # Kernel cmd - Bring up VRF + cmd = "ip link set dev {} up".format(name) + logger.info( + "[DUT: %s]: Running kernel " "cmd [%s]", c_router, cmd + ) + rnode.run(cmd) + + if "links" in c_data: + for destRouterLink, data in sorted( + c_data["links"].iteritems() + ): + # Loopback interfaces + if "type" in data and data["type"] == "loopback": + interface_name = destRouterLink + else: + interface_name = data["interface"] + + if "vrf" in data: + vrf_list = data["vrf"] + + if type(vrf_list) is not list: + vrf_list = [vrf_list] + + for _vrf in vrf_list: + cmd = "ip link set {} master {}".format( + interface_name, _vrf + ) + + logger.info( + "[DUT: %s]: Running" " kernel cmd [%s]", + c_router, + cmd, + ) + rnode.run(cmd) + + if vni: + config_data.append("vrf {}".format(vrf["name"])) + cmd = "vni {}".format(vni) + config_data.append(cmd) + + if del_vni: + config_data.append("vrf {}".format(vrf["name"])) + cmd = "no vni {}".format(del_vni) + config_data.append(cmd) + + result = create_common_configuration( + tgen, c_router, config_data, "vrf", build=build + ) + + except InvalidCLIError: + # Traceback + errormsg = traceback.format_exc() + logger.error(errormsg) + return errormsg + + return result + + +def create_interface_in_kernel( + tgen, dut, name, ip_addr, vrf=None, netmask=None, create=True +): + """ + Cretae interfaces in kernel for ipv4/ipv6 + Config is done in Linux Kernel: + + Parameters + ---------- + * `tgen` : Topogen object + * `dut` : Device for which interfaces to be added + * `name` : interface name + * `ip_addr` : ip address for interface + * `vrf` : VRF name, to which interface will be associated + * `netmask` : netmask value, default is None + * `create`: Create interface in kernel, if created then no need + to create + """ + + rnode = tgen.routers()[dut] + + if create: + cmd = "sudo ip link add name {} type dummy".format(name) + rnode.run(cmd) + + addr_type = validate_ip_address(ip_addr) + if addr_type == "ipv4": + cmd = "ifconfig {} {} netmask {}".format(name, ip_addr, netmask) + else: + cmd = "ifconfig {} inet6 add {}/{}".format(name, ip_addr, netmask) + + rnode.run(cmd) + + if vrf: + cmd = "ip link set {} master {}".format(name, vrf) + rnode.run(cmd) + + +def shutdown_bringup_interface_in_kernel(tgen, dut, intf_name, ifaceaction=False): + """ + Cretae interfaces in kernel for ipv4/ipv6 + Config is done in Linux Kernel: + + Parameters + ---------- + * `tgen` : Topogen object + * `dut` : Device for which interfaces to be added + * `intf_name` : interface name + * `ifaceaction` : False to shutdown and True to bringup the + ineterface + """ + + rnode = tgen.routers()[dut] + + cmd = "ip link set dev" + if ifaceaction: + action = "up" + cmd = "{} {} {}".format(cmd, intf_name, action) + else: + action = "down" + cmd = "{} {} {}".format(cmd, intf_name, action) + + logger.info("[DUT: %s]: Running command: %s", dut, cmd) + rnode.run(cmd) + + +def validate_ip_address(ip_address): + """ + Validates the type of ip address + Parameters + ---------- + * `ip_address`: IPv4/IPv6 address + Returns + ------- + Type of address as string + """ + + if "/" in ip_address: + ip_address = ip_address.split("/")[0] + + v4 = True + v6 = True + try: + socket.inet_aton(ip_address) + except socket.error as error: + logger.debug("Not a valid IPv4 address") + v4 = False + else: + return "ipv4" + + try: + socket.inet_pton(socket.AF_INET6, ip_address) + except socket.error as error: + logger.debug("Not a valid IPv6 address") + v6 = False + else: + return "ipv6" + + if not v4 and not v6: + raise Exception( + "InvalidIpAddr", "%s is neither valid IPv4 or IPv6" " address" % ip_address + ) + + +def check_address_types(addr_type=None): + """ + Checks environment variable set and compares with the current address type + """ + + addr_types_env = os.environ.get("ADDRESS_TYPES") + if not addr_types_env: + addr_types_env = "dual" + + if addr_types_env == "dual": + addr_types = ["ipv4", "ipv6"] + elif addr_types_env == "ipv4": + addr_types = ["ipv4"] + elif addr_types_env == "ipv6": + addr_types = ["ipv6"] + + if addr_type is None: + return addr_types + + if addr_type not in addr_types: + logger.debug( + "{} not in supported/configured address types {}".format( + addr_type, addr_types + ) + ) + return False + + return True + + +def generate_ips(network, no_of_ips): + """ + Returns list of IPs. + based on start_ip and no_of_ips + * `network` : from here the ip will start generating, + start_ip will be + * `no_of_ips` : these many IPs will be generated + """ + + ipaddress_list = [] + if type(network) is not list: + network = [network] + + for start_ipaddr in network: + if "/" in start_ipaddr: + start_ip = start_ipaddr.split("/")[0] + mask = int(start_ipaddr.split("/")[1]) + + addr_type = validate_ip_address(start_ip) + if addr_type == "ipv4": + start_ip = ipaddress.IPv4Address(unicode(start_ip)) + step = 2 ** (32 - mask) + if addr_type == "ipv6": + start_ip = ipaddress.IPv6Address(unicode(start_ip)) + step = 2 ** (128 - mask) + + next_ip = start_ip + count = 0 + while count < no_of_ips: + ipaddress_list.append("{}/{}".format(next_ip, mask)) + if addr_type == "ipv6": + next_ip = ipaddress.IPv6Address(int(next_ip) + step) + else: + next_ip += step + count += 1 + + return ipaddress_list + + +def find_interface_with_greater_ip(topo, router, loopback=True, interface=True): + """ + Returns highest interface ip for ipv4/ipv6. If loopback is there then + it will return highest IP from loopback IPs otherwise from physical + interface IPs. + * `topo` : json file data + * `router` : router for which hightest interface should be calculated + """ + + link_data = topo["routers"][router]["links"] + lo_list = [] + interfaces_list = [] + lo_exists = False + for destRouterLink, data in sorted(link_data.iteritems()): + if loopback: + if "type" in data and data["type"] == "loopback": + lo_exists = True + ip_address = topo["routers"][router]["links"][destRouterLink][ + "ipv4" + ].split("/")[0] + lo_list.append(ip_address) + if interface: + ip_address = topo["routers"][router]["links"][destRouterLink]["ipv4"].split( + "/" + )[0] + interfaces_list.append(ip_address) + + if lo_exists: + return sorted(lo_list)[-1] + + return sorted(interfaces_list)[-1] + + +def write_test_header(tc_name): + """ Display message at beginning of test case""" + count = 20 + logger.info("*" * (len(tc_name) + count)) + step("START -> Testcase : %s" % tc_name, reset=True) + logger.info("*" * (len(tc_name) + count)) + + +def write_test_footer(tc_name): + """ Display message at end of test case""" + count = 21 + logger.info("=" * (len(tc_name) + count)) + logger.info("Testcase : %s -> PASSED", tc_name) + logger.info("=" * (len(tc_name) + count)) + + +def interface_status(tgen, topo, input_dict): + """ + Delete ip route maps from device + * `tgen` : Topogen object + * `topo` : json file data + * `input_dict` : for which router, route map has to be deleted + Usage + ----- + input_dict = { + "r3": { + "interface_list": ['eth1-r1-r2', 'eth2-r1-r3'], + "status": "down" + } + } + Returns + ------- + errormsg(str) or True + """ + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + try: + global frr_cfg + for router in input_dict.keys(): + + interface_list = input_dict[router]["interface_list"] + status = input_dict[router].setdefault("status", "up") + for intf in interface_list: + rnode = tgen.routers()[router] + interface_set_status(rnode, intf, status) + + # Load config to router + load_config_to_router(tgen, router) + + except Exception as e: + # handle any exception + logger.error("Error %s occured. Arguments %s.", e.message, e.args) + + # Traceback + errormsg = traceback.format_exc() + logger.error(errormsg) + return errormsg + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return True + + +def retry(attempts=3, wait=2, return_is_str=True, initial_wait=0, + return_is_dict=False): + """ + Retries function execution, if return is an errormsg or exception + + * `attempts`: Number of attempts to make + * `wait`: Number of seconds to wait between each attempt + * `return_is_str`: Return val is an errormsg in case of failure + * `initial_wait`: Sleeps for this much seconds before executing function + + """ + + def _retry(func): + + @wraps(func) + def func_retry(*args, **kwargs): + _wait = kwargs.pop('wait', wait) + _attempts = kwargs.pop('attempts', attempts) + _attempts = int(_attempts) + expected = True + if _attempts < 0: + raise ValueError("attempts must be 0 or greater") + + if initial_wait > 0: + logger.info("Waiting for [%s]s as initial delay", initial_wait) + sleep(initial_wait) + + _return_is_str = kwargs.pop('return_is_str', return_is_str) + _return_is_dict = kwargs.pop('return_is_str', return_is_dict) + for i in range(1, _attempts + 1): + try: + _expected = kwargs.setdefault('expected', True) + if _expected is False: + expected = _expected + kwargs.pop('expected') + ret = func(*args, **kwargs) + logger.debug("Function returned %s", ret) + if _return_is_str and isinstance(ret, bool) and _expected: + return ret + if (isinstance(ret, str) or isinstance(ret, unicode)) and _expected is False: + return ret + if _return_is_dict and isinstance(ret, dict): + return ret + + if _attempts == i and expected: + generate_support_bundle() + return ret + except Exception as err: + if _attempts == i and expected: + generate_support_bundle() + logger.info("Max number of attempts (%r) reached", + _attempts) + raise + else: + logger.info("Function returned %s", err) + if i < _attempts: + logger.info("Retry [#%r] after sleeping for %ss" + % (i, _wait)) + sleep(_wait) + func_retry._original = func + return func_retry + return _retry + + +class Stepper: + """ + Prints step number for the test case step being executed + """ + + count = 1 + + def __call__(self, msg, reset): + if reset: + Stepper.count = 1 + logger.info(msg) + else: + logger.info("STEP %s: '%s'", Stepper.count, msg) + Stepper.count += 1 + + +def step(msg, reset=False): + """ + Call Stepper to print test steps. Need to reset at the beginning of test. + * ` msg` : Step message body. + * `reset` : Reset step count to 1 when set to True. + """ + _step = Stepper() + _step(msg, reset) + + +############################################# +# These APIs, will used by testcase +############################################# +def create_interfaces_cfg(tgen, topo, build=False): + """ + Create interface configuration for created topology. Basic Interface + configuration is provided in input json file. + + Parameters + ---------- + * `tgen` : Topogen object + * `topo` : json file data + * `build` : Only for initial setup phase this is set as True. + + Returns + ------- + True or False + """ + result = False + topo = deepcopy(topo) + + try: + for c_router, c_data in topo.iteritems(): + interface_data = [] + for destRouterLink, data in sorted(c_data["links"].iteritems()): + # Loopback interfaces + if "type" in data and data["type"] == "loopback": + interface_name = destRouterLink + else: + interface_name = data["interface"] + + # Include vrf if present + if "vrf" in data: + interface_data.append( + "interface {} vrf {}".format( + str(interface_name), str(data["vrf"]) + ) + ) + else: + interface_data.append("interface {}".format(str(interface_name))) + + if "ipv4" in data: + intf_addr = c_data["links"][destRouterLink]["ipv4"] + + if "delete" in data and data["delete"]: + interface_data.append("no ip address {}".format(intf_addr)) + else: + interface_data.append("ip address {}".format(intf_addr)) + if "ipv6" in data: + intf_addr = c_data["links"][destRouterLink]["ipv6"] + + if "delete" in data and data["delete"]: + interface_data.append("no ipv6 address {}".format(intf_addr)) + else: + interface_data.append("ipv6 address {}".format(intf_addr)) + + if "ipv6-link-local" in data: + intf_addr = c_data["links"][destRouterLink]["ipv6-link-local"] + + if "delete" in data and data["delete"]: + interface_data.append("no ipv6 address {}".format(intf_addr)) + else: + interface_data.append("ipv6 address {}\n".format(intf_addr)) + + result = create_common_configuration( + tgen, c_router, interface_data, "interface_config", build=build + ) + except InvalidCLIError: + # Traceback + errormsg = traceback.format_exc() + logger.error(errormsg) + return errormsg + + return result + + +def create_static_routes(tgen, input_dict, build=False): + """ + Create static routes for given router as defined in input_dict + + Parameters + ---------- + * `tgen` : Topogen object + * `input_dict` : Input dict data, required when configuring from testcase + * `build` : Only for initial setup phase this is set as True. + + Usage + ----- + input_dict should be in the format below: + # static_routes: list of all routes + # network: network address + # no_of_ip: number of next-hop address that will be configured + # admin_distance: admin distance for route/routes. + # next_hop: starting next-hop address + # tag: tag id for static routes + # vrf: VRF name in which static routes needs to be created + # delete: True if config to be removed. Default False. + + Example: + "routers": { + "r1": { + "static_routes": [ + { + "network": "100.0.20.1/32", + "no_of_ip": 9, + "admin_distance": 100, + "next_hop": "10.0.0.1", + "tag": 4001, + "vrf": "RED_A" + "delete": true + } + ] + } + } + + Returns + ------- + errormsg(str) or True + """ + result = False + logger.debug("Entering lib API: create_static_routes()") + input_dict = deepcopy(input_dict) + + try: + for router in input_dict.keys(): + if "static_routes" not in input_dict[router]: + errormsg = "static_routes not present in input_dict" + logger.info(errormsg) + continue + + static_routes_list = [] + + static_routes = input_dict[router]["static_routes"] + for static_route in static_routes: + del_action = static_route.setdefault("delete", False) + no_of_ip = static_route.setdefault("no_of_ip", 1) + network = static_route.setdefault("network", []) + if type(network) is not list: + network = [network] + + admin_distance = static_route.setdefault("admin_distance", None) + tag = static_route.setdefault("tag", None) + vrf = static_route.setdefault("vrf", None) + interface = static_route.setdefault("interface", None) + next_hop = static_route.setdefault("next_hop", None) + nexthop_vrf = static_route.setdefault("nexthop_vrf", None) + + ip_list = generate_ips(network, no_of_ip) + for ip in ip_list: + addr_type = validate_ip_address(ip) + + if addr_type == "ipv4": + cmd = "ip route {}".format(ip) + else: + cmd = "ipv6 route {}".format(ip) + + if interface: + cmd = "{} {}".format(cmd, interface) + + if next_hop: + cmd = "{} {}".format(cmd, next_hop) + + if nexthop_vrf: + cmd = "{} nexthop-vrf {}".format(cmd, nexthop_vrf) + + if vrf: + cmd = "{} vrf {}".format(cmd, vrf) + + if tag: + cmd = "{} tag {}".format(cmd, str(tag)) + + if admin_distance: + cmd = "{} {}".format(cmd, admin_distance) + + if del_action: + cmd = "no {}".format(cmd) + + static_routes_list.append(cmd) + + result = create_common_configuration( + tgen, router, static_routes_list, "static_route", build=build + ) + + except InvalidCLIError: + # Traceback + errormsg = traceback.format_exc() + logger.error(errormsg) + return errormsg + + logger.debug("Exiting lib API: create_static_routes()") + return result + + +def create_prefix_lists(tgen, input_dict, build=False): + """ + Create ip prefix lists as per the config provided in input + JSON or input_dict + Parameters + ---------- + * `tgen` : Topogen object + * `input_dict` : Input dict data, required when configuring from testcase + * `build` : Only for initial setup phase this is set as True. + Usage + ----- + # pf_lists_1: name of prefix-list, user defined + # seqid: prefix-list seqid, auto-generated if not given by user + # network: criteria for applying prefix-list + # action: permit/deny + # le: less than or equal number of bits + # ge: greater than or equal number of bits + Example + ------- + input_dict = { + "r1": { + "prefix_lists":{ + "ipv4": { + "pf_list_1": [ + { + "seqid": 10, + "network": "any", + "action": "permit", + "le": "32", + "ge": "30", + "delete": True + } + ] + } + } + } + } + Returns + ------- + errormsg or True + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + result = False + try: + for router in input_dict.keys(): + if "prefix_lists" not in input_dict[router]: + errormsg = "prefix_lists not present in input_dict" + logger.debug(errormsg) + continue + + config_data = [] + prefix_lists = input_dict[router]["prefix_lists"] + for addr_type, prefix_data in prefix_lists.iteritems(): + if not check_address_types(addr_type): + continue + + for prefix_name, prefix_list in prefix_data.iteritems(): + for prefix_dict in prefix_list: + if "action" not in prefix_dict or "network" not in prefix_dict: + errormsg = "'action' or network' missing in" " input_dict" + return errormsg + + network_addr = prefix_dict["network"] + action = prefix_dict["action"] + le = prefix_dict.setdefault("le", None) + ge = prefix_dict.setdefault("ge", None) + seqid = prefix_dict.setdefault("seqid", None) + del_action = prefix_dict.setdefault("delete", False) + if seqid is None: + seqid = get_seq_id("prefix_lists", router, prefix_name) + else: + set_seq_id("prefix_lists", router, seqid, prefix_name) + + if addr_type == "ipv4": + protocol = "ip" + else: + protocol = "ipv6" + + cmd = "{} prefix-list {} seq {} {} {}".format( + protocol, prefix_name, seqid, action, network_addr + ) + if le: + cmd = "{} le {}".format(cmd, le) + if ge: + cmd = "{} ge {}".format(cmd, ge) + + if del_action: + cmd = "no {}".format(cmd) + + config_data.append(cmd) + result = create_common_configuration( + tgen, router, config_data, "prefix_list", build=build + ) + + except InvalidCLIError: + # Traceback + errormsg = traceback.format_exc() + logger.error(errormsg) + return errormsg + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return result + + +def create_route_maps(tgen, input_dict, build=False): + """ + Create route-map on the devices as per the arguments passed + Parameters + ---------- + * `tgen` : Topogen object + * `input_dict` : Input dict data, required when configuring from testcase + * `build` : Only for initial setup phase this is set as True. + Usage + ----- + # route_maps: key, value pair for route-map name and its attribute + # rmap_match_prefix_list_1: user given name for route-map + # action: PERMIT/DENY + # match: key,value pair for match criteria. prefix_list, community-list, + large-community-list or tag. Only one option at a time. + # prefix_list: name of prefix list + # large-community-list: name of large community list + # community-ist: name of community list + # tag: tag id for static routes + # set: key, value pair for modifying route attributes + # localpref: preference value for the network + # med: metric value advertised for AS + # aspath: set AS path value + # weight: weight for the route + # community: standard community value to be attached + # large_community: large community value to be attached + # community_additive: if set to "additive", adds community/large-community + value to the existing values of the network prefix + Example: + -------- + input_dict = { + "r1": { + "route_maps": { + "rmap_match_prefix_list_1": [ + { + "action": "PERMIT", + "match": { + "ipv4": { + "prefix_list": "pf_list_1" + } + "ipv6": { + "prefix_list": "pf_list_1" + } + "large-community-list": { + "id": "community_1", + "exact_match": True + } + "community_list": { + "id": "community_2", + "exact_match": True + } + "tag": "tag_id" + }, + "set": { + "locPrf": 150, + "metric": 30, + "path": { + "num": 20000, + "action": "prepend", + }, + "weight": 500, + "community": { + "num": "1:2 2:3", + "action": additive + } + "large_community": { + "num": "1:2:3 4:5;6", + "action": additive + }, + } + } + ] + } + } + } + Returns + ------- + errormsg(str) or True + """ + + result = False + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + input_dict = deepcopy(input_dict) + try: + for router in input_dict.keys(): + if "route_maps" not in input_dict[router]: + logger.debug("route_maps not present in input_dict") + continue + rmap_data = [] + for rmap_name, rmap_value in input_dict[router]["route_maps"].iteritems(): + + for rmap_dict in rmap_value: + del_action = rmap_dict.setdefault("delete", False) + + if del_action: + rmap_data.append("no route-map {}".format(rmap_name)) + continue + + if "action" not in rmap_dict: + errormsg = "action not present in input_dict" + logger.error(errormsg) + return False + + rmap_action = rmap_dict.setdefault("action", "deny") + + seq_id = rmap_dict.setdefault("seq_id", None) + if seq_id is None: + seq_id = get_seq_id("route_maps", router, rmap_name) + else: + set_seq_id("route_maps", router, seq_id, rmap_name) + + rmap_data.append( + "route-map {} {} {}".format(rmap_name, rmap_action, seq_id) + ) + + if "continue" in rmap_dict: + continue_to = rmap_dict["continue"] + if continue_to: + rmap_data.append("on-match goto {}".format(continue_to)) + else: + logger.error( + "In continue, 'route-map entry " + "sequence number' is not provided" + ) + return False + + if "goto" in rmap_dict: + go_to = rmap_dict["goto"] + if go_to: + rmap_data.append("on-match goto {}".format(go_to)) + else: + logger.error( + "In goto, 'Goto Clause number' is not" " provided" + ) + return False + + if "call" in rmap_dict: + call_rmap = rmap_dict["call"] + if call_rmap: + rmap_data.append("call {}".format(call_rmap)) + else: + logger.error( + "In call, 'destination Route-Map' is" " not provided" + ) + return False + + # Verifying if SET criteria is defined + if "set" in rmap_dict: + set_data = rmap_dict["set"] + ipv4_data = set_data.setdefault("ipv4", {}) + ipv6_data = set_data.setdefault("ipv6", {}) + local_preference = set_data.setdefault("locPrf", None) + metric = set_data.setdefault("metric", None) + as_path = set_data.setdefault("path", {}) + weight = set_data.setdefault("weight", None) + community = set_data.setdefault("community", {}) + large_community = set_data.setdefault("large_community", {}) + large_comm_list = set_data.setdefault("large_comm_list", {}) + set_action = set_data.setdefault("set_action", None) + nexthop = set_data.setdefault("nexthop", None) + origin = set_data.setdefault("origin", None) + ext_comm_list = set_data.setdefault("extcommunity", {}) + + # Local Preference + if local_preference: + rmap_data.append( + "set local-preference {}".format(local_preference) + ) + + # Metric + if metric: + rmap_data.append("set metric {} \n".format(metric)) + + # Origin + if origin: + rmap_data.append("set origin {} \n".format(origin)) + + # AS Path Prepend + if as_path: + as_num = as_path.setdefault("as_num", None) + as_action = as_path.setdefault("as_action", None) + if as_action and as_num: + rmap_data.append( + "set as-path {} {}".format(as_action, as_num) + ) + + # Community + if community: + num = community.setdefault("num", None) + comm_action = community.setdefault("action", None) + if num: + cmd = "set community {}".format(num) + if comm_action: + cmd = "{} {}".format(cmd, comm_action) + rmap_data.append(cmd) + else: + logger.error("In community, AS Num not" " provided") + return False + + if large_community: + num = large_community.setdefault("num", None) + comm_action = large_community.setdefault("action", None) + if num: + cmd = "set large-community {}".format(num) + if comm_action: + cmd = "{} {}".format(cmd, comm_action) + + rmap_data.append(cmd) + else: + logger.error( + "In large_community, AS Num not" " provided" + ) + return False + if large_comm_list: + id = large_comm_list.setdefault("id", None) + del_comm = large_comm_list.setdefault("delete", None) + if id: + cmd = "set large-comm-list {}".format(id) + if del_comm: + cmd = "{} delete".format(cmd) + + rmap_data.append(cmd) + else: + logger.error("In large_comm_list 'id' not" " provided") + return False + + if ext_comm_list: + rt = ext_comm_list.setdefault("rt", None) + del_comm = ext_comm_list.setdefault("delete", None) + if rt: + cmd = "set extcommunity rt {}".format(rt) + if del_comm: + cmd = "{} delete".format(cmd) + + rmap_data.append(cmd) + else: + logger.debug("In ext_comm_list 'rt' not" " provided") + return False + + # Weight + if weight: + rmap_data.append("set weight {}".format(weight)) + if ipv6_data: + nexthop = ipv6_data.setdefault("nexthop", None) + if nexthop: + rmap_data.append("set ipv6 next-hop {}".format(nexthop)) + + # Adding MATCH and SET sequence to RMAP if defined + if "match" in rmap_dict: + match_data = rmap_dict["match"] + ipv4_data = match_data.setdefault("ipv4", {}) + ipv6_data = match_data.setdefault("ipv6", {}) + community = match_data.setdefault("community_list", {}) + large_community = match_data.setdefault("large_community", {}) + large_community_list = match_data.setdefault( + "large_community_list", {} + ) + + metric = match_data.setdefault("metric", None) + source_vrf = match_data.setdefault("source-vrf", None) + + if ipv4_data: + # fetch prefix list data from rmap + prefix_name = ipv4_data.setdefault("prefix_lists", None) + if prefix_name: + rmap_data.append( + "match ip address" + " prefix-list {}".format(prefix_name) + ) + + # fetch tag data from rmap + tag = ipv4_data.setdefault("tag", None) + if tag: + rmap_data.append("match tag {}".format(tag)) + + # fetch large community data from rmap + large_community_list = ipv4_data.setdefault( + "large_community_list", {} + ) + large_community = match_data.setdefault( + "large_community", {} + ) + + if ipv6_data: + prefix_name = ipv6_data.setdefault("prefix_lists", None) + if prefix_name: + rmap_data.append( + "match ipv6 address" + " prefix-list {}".format(prefix_name) + ) + + # fetch tag data from rmap + tag = ipv6_data.setdefault("tag", None) + if tag: + rmap_data.append("match tag {}".format(tag)) + + # fetch large community data from rmap + large_community_list = ipv6_data.setdefault( + "large_community_list", {} + ) + large_community = match_data.setdefault( + "large_community", {} + ) + + if community: + if "id" not in community: + logger.error( + "'id' is mandatory for " + "community-list in match" + " criteria" + ) + return False + cmd = "match community {}".format(community["id"]) + exact_match = community.setdefault("exact_match", False) + if exact_match: + cmd = "{} exact-match".format(cmd) + + rmap_data.append(cmd) + if large_community: + if "id" not in large_community: + logger.error( + "'id' is mandatory for " + "large-community-list in match " + "criteria" + ) + return False + cmd = "match large-community {}".format( + large_community["id"] + ) + exact_match = large_community.setdefault( + "exact_match", False + ) + if exact_match: + cmd = "{} exact-match".format(cmd) + rmap_data.append(cmd) + if large_community_list: + if "id" not in large_community_list: + logger.error( + "'id' is mandatory for " + "large-community-list in match " + "criteria" + ) + return False + cmd = "match large-community {}".format( + large_community_list["id"] + ) + exact_match = large_community_list.setdefault( + "exact_match", False + ) + if exact_match: + cmd = "{} exact-match".format(cmd) + rmap_data.append(cmd) + + if source_vrf: + cmd = "match source-vrf {}".format(source_vrf) + rmap_data.append(cmd) + + if metric: + cmd = "match metric {}".format(metric) + rmap_data.append(cmd) + + result = create_common_configuration( + tgen, router, rmap_data, "route_maps", build=build + ) + + except InvalidCLIError: + # Traceback + errormsg = traceback.format_exc() + logger.error(errormsg) + return errormsg + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return result + + +def delete_route_maps(tgen, input_dict): + """ + Delete ip route maps from device + * `tgen` : Topogen object + * `input_dict` : for which router, + route map has to be deleted + Usage + ----- + # Delete route-map rmap_1 and rmap_2 from router r1 + input_dict = { + "r1": { + "route_maps": ["rmap_1", "rmap__2"] + } + } + result = delete_route_maps("ipv4", input_dict) + Returns + ------- + errormsg(str) or True + """ + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + for router in input_dict.keys(): + route_maps = input_dict[router]["route_maps"][:] + rmap_data = input_dict[router] + rmap_data["route_maps"] = {} + for route_map_name in route_maps: + rmap_data["route_maps"].update({route_map_name: [{"delete": True}]}) + + return create_route_maps(tgen, input_dict) + + +def create_bgp_community_lists(tgen, input_dict, build=False): + """ + Create bgp community-list or large-community-list on the devices as per + the arguments passed. Takes list of communities in input. + Parameters + ---------- + * `tgen` : Topogen object + * `input_dict` : Input dict data, required when configuring from testcase + * `build` : Only for initial setup phase this is set as True. + Usage + ----- + input_dict_1 = { + "r3": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "rmap_lcomm_{}".format(addr_type), + "value": "1:1:1 1:2:3 2:1:1 2:2:2", + "large": True + } + ] + } + } + } + result = create_bgp_community_lists(tgen, input_dict_1) + """ + + result = False + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + input_dict = deepcopy(input_dict) + try: + for router in input_dict.keys(): + if "bgp_community_lists" not in input_dict[router]: + errormsg = "bgp_community_lists not present in input_dict" + logger.debug(errormsg) + continue + + config_data = [] + + community_list = input_dict[router]["bgp_community_lists"] + for community_dict in community_list: + del_action = community_dict.setdefault("delete", False) + community_type = community_dict.setdefault("community_type", None) + action = community_dict.setdefault("action", None) + value = community_dict.setdefault("value", "") + large = community_dict.setdefault("large", None) + name = community_dict.setdefault("name", None) + if large: + cmd = "bgp large-community-list" + else: + cmd = "bgp community-list" + + if not large and not (community_type and action and value): + errormsg = ( + "community_type, action and value are " + "required in bgp_community_list" + ) + logger.error(errormsg) + return False + + try: + community_type = int(community_type) + cmd = "{} {} {} {}".format(cmd, community_type, action, value) + except ValueError: + + cmd = "{} {} {} {} {}".format( + cmd, community_type, name, action, value + ) + + if del_action: + cmd = "no {}".format(cmd) + + config_data.append(cmd) + + result = create_common_configuration( + tgen, router, config_data, "bgp_community_list", build=build + ) + + except InvalidCLIError: + # Traceback + errormsg = traceback.format_exc() + logger.error(errormsg) + return errormsg + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return result + + +def shutdown_bringup_interface(tgen, dut, intf_name, ifaceaction=False): + """ + Shutdown or bringup router's interface " + * `tgen` : Topogen object + * `dut` : Device under test + * `intf_name` : Interface name to be shut/no shut + * `ifaceaction` : Action, to shut/no shut interface, + by default is False + Usage + ----- + dut = "r3" + intf = "r3-r1-eth0" + # Shut down ineterface + shutdown_bringup_interface(tgen, dut, intf, False) + # Bring up ineterface + shutdown_bringup_interface(tgen, dut, intf, True) + Returns + ------- + errormsg(str) or True + """ + + router_list = tgen.routers() + if ifaceaction: + logger.info("Bringing up interface : {}".format(intf_name)) + else: + logger.info("Shutting down interface : {}".format(intf_name)) + + interface_set_status(router_list[dut], intf_name, ifaceaction) + + +def addKernelRoute( + tgen, router, intf, group_addr_range, next_hop=None, src=None, del_action=None +): + """ + Add route to kernel + + Parameters: + ----------- + * `tgen` : Topogen object + * `router`: router for which kernal routes needs to be added + * `intf`: interface name, for which kernal routes needs to be added + * `bindToAddress`: bind to , an interface or multicast + address + + returns: + -------- + errormsg or True + """ + + logger.debug("Entering lib API: addKernelRoute()") + + rnode = tgen.routers()[router] + + if type(group_addr_range) is not list: + group_addr_range = [group_addr_range] + + for grp_addr in group_addr_range: + + addr_type = validate_ip_address(grp_addr) + if addr_type == "ipv4": + if next_hop is not None: + cmd = "ip route add {} via {}".format(grp_addr, next_hop) + else: + cmd = "ip route add {} dev {}".format(grp_addr, intf) + if del_action: + cmd = "ip route del {}".format(grp_addr) + verify_cmd = "ip route" + elif addr_type == "ipv6": + if intf and src: + cmd = "ip -6 route add {} dev {} src {}".format(grp_addr, intf, src) + else: + cmd = "ip -6 route add {} via {}".format(grp_addr, next_hop) + verify_cmd = "ip -6 route" + if del_action: + cmd = "ip -6 route del {}".format(grp_addr) + + logger.info("[DUT: {}]: Running command: [{}]".format(router, cmd)) + output = rnode.run(cmd) + + # Verifying if ip route added to kernal + result = rnode.run(verify_cmd) + logger.debug("{}\n{}".format(verify_cmd, result)) + if "/" in grp_addr: + ip, mask = grp_addr.split("/") + if mask == "32" or mask == "128": + grp_addr = ip + + if not re_search(r"{}".format(grp_addr), result) and mask != "0": + errormsg = ( + "[DUT: {}]: Kernal route is not added for group" + " address {} Config output: {}".format(router, grp_addr, output) + ) + + return errormsg + + logger.debug("Exiting lib API: addKernelRoute()") + return True + + +def configure_vxlan(tgen, input_dict): + """ + Add and configure vxlan + + * `tgen`: tgen onject + * `input_dict` : data for vxlan config + + Usage: + ------ + input_dict= { + "dcg2":{ + "vxlan":[{ + "vxlan_name": "vxlan75100", + "vxlan_id": "75100", + "dstport": 4789, + "local_addr": "120.0.0.1", + "learning": "no", + "delete": True + }] + } + } + + configure_vxlan(tgen, input_dict) + + Returns: + ------- + True or errormsg + + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + router_list = tgen.routers() + for dut in input_dict.keys(): + rnode = tgen.routers()[dut] + + if "vxlan" in input_dict[dut]: + for vxlan_dict in input_dict[dut]["vxlan"]: + cmd = "ip link " + + del_vxlan = vxlan_dict.setdefault("delete", None) + vxlan_names = vxlan_dict.setdefault("vxlan_name", []) + vxlan_ids = vxlan_dict.setdefault("vxlan_id", []) + dstport = vxlan_dict.setdefault("dstport", None) + local_addr = vxlan_dict.setdefault("local_addr", None) + learning = vxlan_dict.setdefault("learning", None) + + config_data = [] + if vxlan_names and vxlan_ids: + for vxlan_name, vxlan_id in zip(vxlan_names, vxlan_ids): + cmd = "ip link" + + if del_vxlan: + cmd = "{} del {} type vxlan id {}".format( + cmd, vxlan_name, vxlan_id + ) + else: + cmd = "{} add {} type vxlan id {}".format( + cmd, vxlan_name, vxlan_id + ) + + if dstport: + cmd = "{} dstport {}".format(cmd, dstport) + + if local_addr: + ip_cmd = "ip addr add {} dev {}".format( + local_addr, vxlan_name + ) + if del_vxlan: + ip_cmd = "ip addr del {} dev {}".format( + local_addr, vxlan_name + ) + + config_data.append(ip_cmd) + + cmd = "{} local {}".format(cmd, local_addr) + + if learning == "no": + cmd = "{} nolearning".format(cmd) + + elif learning == "yes": + cmd = "{} learning".format(cmd) + + config_data.append(cmd) + + try: + for _cmd in config_data: + logger.info("[DUT: %s]: Running command: %s", dut, _cmd) + rnode.run(_cmd) + + except InvalidCLIError: + # Traceback + errormsg = traceback.format_exc() + logger.error(errormsg) + return errormsg + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + + return True + + +def configure_brctl(tgen, topo, input_dict): + """ + Add and configure brctl + + * `tgen`: tgen onject + * `input_dict` : data for brctl config + + Usage: + ------ + input_dict= { + dut:{ + "brctl": [{ + "brctl_name": "br100", + "addvxlan": "vxlan75100", + "vrf": "RED", + "stp": "off" + }] + } + } + + configure_brctl(tgen, topo, input_dict) + + Returns: + ------- + True or errormsg + + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + router_list = tgen.routers() + for dut in input_dict.keys(): + rnode = tgen.routers()[dut] + + if "brctl" in input_dict[dut]: + for brctl_dict in input_dict[dut]["brctl"]: + + brctl_names = brctl_dict.setdefault("brctl_name", []) + addvxlans = brctl_dict.setdefault("addvxlan", []) + stp_values = brctl_dict.setdefault("stp", []) + vrfs = brctl_dict.setdefault("vrf", []) + + ip_cmd = "ip link set" + for brctl_name, vxlan, vrf, stp in zip( + brctl_names, addvxlans, vrfs, stp_values + ): + + ip_cmd_list = [] + cmd = "ip link add name {} type bridge stp_state {}".format( + brctl_name, stp + ) + + logger.info("[DUT: %s]: Running command: %s", dut, cmd) + rnode.run(cmd) + + ip_cmd_list.append("{} up dev {}".format(ip_cmd, brctl_name)) + + if vxlan: + cmd = "{} dev {} master {}".format(ip_cmd, vxlan, brctl_name) + + logger.info("[DUT: %s]: Running command: %s", dut, cmd) + rnode.run(cmd) + + ip_cmd_list.append("{} up dev {}".format(ip_cmd, vxlan)) + + if vrf: + ip_cmd_list.append( + "{} dev {} master {}".format(ip_cmd, brctl_name, vrf) + ) + + for intf_name, data in topo["routers"][dut]["links"].items(): + if "vrf" not in data: + continue + + if data["vrf"] == vrf: + ip_cmd_list.append( + "{} up dev {}".format(ip_cmd, data["interface"]) + ) + + try: + for _ip_cmd in ip_cmd_list: + logger.info("[DUT: %s]: Running command: %s", dut, _ip_cmd) + rnode.run(_ip_cmd) + + except InvalidCLIError: + # Traceback + errormsg = traceback.format_exc() + logger.error(errormsg) + return errormsg + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return True + + +def configure_interface_mac(tgen, input_dict): + """ + Add and configure brctl + + * `tgen`: tgen onject + * `input_dict` : data for mac config + + input_mac= { + "edge1":{ + "br75100": "00:80:48:BA:d1:00, + "br75200": "00:80:48:BA:d1:00 + } + } + + configure_interface_mac(tgen, input_mac) + + Returns: + ------- + True or errormsg + + """ + + router_list = tgen.routers() + for dut in input_dict.keys(): + rnode = tgen.routers()[dut] + + for intf, mac in input_dict[dut].items(): + cmd = "ifconfig {} hw ether {}".format(intf, mac) + logger.info("[DUT: %s]: Running command: %s", dut, cmd) + + try: + result = rnode.run(cmd) + if len(result) != 0: + return result + + except InvalidCLIError: + # Traceback + errormsg = traceback.format_exc() + logger.error(errormsg) + return errormsg + + return True + + +############################################# +# Verification APIs +############################################# +@retry(attempts=5, wait=2, return_is_str=True, initial_wait=2) +def verify_rib( + tgen, + addr_type, + dut, + input_dict, + next_hop=None, + protocol=None, + tag=None, + metric=None, + fib=None, + count_only=False +): + """ + Data will be read from input_dict or input JSON file, API will generate + same prefixes, which were redistributed by either create_static_routes() or + advertise_networks_using_network_command() and do will verify next_hop and + each prefix/routes is present in "show ip/ipv6 route {bgp/stataic} json" + command o/p. + + Parameters + ---------- + * `tgen` : topogen object + * `addr_type` : ip type, ipv4/ipv6 + * `dut`: Device Under Test, for which user wants to test the data + * `input_dict` : input dict, has details of static routes + * `next_hop`[optional]: next_hop which needs to be verified, + default: static + * `protocol`[optional]: protocol, default = None + * `count_only`[optional]: count of nexthops only, not specific addresses, + default = False + + Usage + ----- + # RIB can be verified for static routes OR network advertised using + network command. Following are input_dicts to create static routes + and advertise networks using network command. Any one of the input_dict + can be passed to verify_rib() to verify routes in DUT"s RIB. + + # Creating static routes for r1 + input_dict = { + "r1": { + "static_routes": [{"network": "10.0.20.1/32", "no_of_ip": 9, \ + "admin_distance": 100, "next_hop": "10.0.0.2", "tag": 4001}] + }} + # Advertising networks using network command in router r1 + input_dict = { + "r1": { + "advertise_networks": [{"start_ip": "20.0.0.0/32", + "no_of_network": 10}, + {"start_ip": "30.0.0.0/32"}] + }} + # Verifying ipv4 routes in router r1 learned via BGP + dut = "r2" + protocol = "bgp" + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol = protocol) + + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + router_list = tgen.routers() + additional_nexthops_in_required_nhs = [] + found_hops = [] + for routerInput in input_dict.keys(): + for router, rnode in router_list.iteritems(): + if router != dut: + continue + + logger.info("Checking router %s RIB:", router) + + # Verifying RIB routes + if addr_type == "ipv4": + command = "show ip route" + else: + command = "show ipv6 route" + + found_routes = [] + missing_routes = [] + + if "static_routes" in input_dict[routerInput]: + static_routes = input_dict[routerInput]["static_routes"] + + for static_route in static_routes: + if "vrf" in static_route and static_route["vrf"] is not None: + + logger.info( + "[DUT: {}]: Verifying routes for VRF:" + " {}".format(router, static_route["vrf"]) + ) + + cmd = "{} vrf {}".format(command, static_route["vrf"]) + + else: + cmd = "{}".format(command) + + if protocol: + cmd = "{} {}".format(cmd, protocol) + + cmd = "{} json".format(cmd) + + rib_routes_json = run_frr_cmd(rnode, cmd, isjson=True) + + # Verifying output dictionary rib_routes_json is not empty + if bool(rib_routes_json) is False: + errormsg = "No route found in rib of router {}..".format(router) + return errormsg + + network = static_route["network"] + if "no_of_ip" in static_route: + no_of_ip = static_route["no_of_ip"] + else: + no_of_ip = 1 + + if "tag" in static_route: + _tag = static_route["tag"] + else: + _tag = None + + # Generating IPs for verification + ip_list = generate_ips(network, no_of_ip) + st_found = False + nh_found = False + + for st_rt in ip_list: + st_rt = str(ipaddress.ip_network(unicode(st_rt))) + + _addr_type = validate_ip_address(st_rt) + if _addr_type != addr_type: + continue + + if st_rt in rib_routes_json: + st_found = True + found_routes.append(st_rt) + + if fib and next_hop: + if type(next_hop) is not list: + next_hop = [next_hop] + + for mnh in range(0, len(rib_routes_json[st_rt])): + if ( + "fib" + in rib_routes_json[st_rt][mnh]["nexthops"][0] + ): + found_hops.append( + [ + rib_r["ip"] + for rib_r in rib_routes_json[st_rt][ + mnh + ]["nexthops"] + ] + ) + + if found_hops[0]: + missing_list_of_nexthops = set( + found_hops[0] + ).difference(next_hop) + additional_nexthops_in_required_nhs = set( + next_hop + ).difference(found_hops[0]) + + if additional_nexthops_in_required_nhs: + logger.info( + "Nexthop " + "%s is not active for route %s in " + "RIB of router %s\n", + additional_nexthops_in_required_nhs, + st_rt, + dut, + ) + errormsg = ( + "Nexthop {} is not active" + " for route {} in RIB of router" + " {}\n".format( + additional_nexthops_in_required_nhs, + st_rt, + dut, + ) + ) + return errormsg + else: + nh_found = True + + elif next_hop and fib is None: + if type(next_hop) is not list: + next_hop = [next_hop] + found_hops = [ + rib_r["ip"] + for rib_r in rib_routes_json[st_rt][0]["nexthops"] + ] + + # Check only the count of nexthops + if count_only: + if len(next_hop) == len(found_hops): + nh_found = True + else: + errormsg = ( + "Nexthops are missing for " + "route {} in RIB of router {}: " + "expected {}, found {}\n".format( + st_rt, dut, len(next_hop), + len(found_hops) + ) + ) + return errormsg + + # Check the actual nexthops + elif found_hops: + missing_list_of_nexthops = set( + found_hops + ).difference(next_hop) + additional_nexthops_in_required_nhs = set( + next_hop + ).difference(found_hops) + + if additional_nexthops_in_required_nhs: + logger.info( + "Missing nexthop %s for route" + " %s in RIB of router %s\n", + additional_nexthops_in_required_nhs, + st_rt, + dut, + ) + errormsg = ( + "Nexthop {} is Missing for " + "route {} in RIB of router {}\n".format( + additional_nexthops_in_required_nhs, + st_rt, + dut, + ) + ) + return errormsg + else: + nh_found = True + + if tag: + if "tag" not in rib_routes_json[st_rt][0]: + errormsg = ( + "[DUT: {}]: tag is not" + " present for" + " route {} in RIB \n".format(dut, st_rt) + ) + return errormsg + + if _tag != rib_routes_json[st_rt][0]["tag"]: + errormsg = ( + "[DUT: {}]: tag value {}" + " is not matched for" + " route {} in RIB \n".format(dut, _tag, st_rt,) + ) + return errormsg + + if metric is not None: + if "metric" not in rib_routes_json[st_rt][0]: + errormsg = ( + "[DUT: {}]: metric is" + " not present for" + " route {} in RIB \n".format(dut, st_rt) + ) + return errormsg + + if metric != rib_routes_json[st_rt][0]["metric"]: + errormsg = ( + "[DUT: {}]: metric value " + "{} is not matched for " + "route {} in RIB \n".format(dut, metric, st_rt,) + ) + return errormsg + + else: + missing_routes.append(st_rt) + + if nh_found: + logger.info( + "[DUT: {}]: Found next_hop {} for all bgp" + " routes in RIB".format(router, next_hop) + ) + + if len(missing_routes) > 0: + errormsg = "[DUT: {}]: Missing route in RIB, " "routes: {}".format( + dut, missing_routes + ) + return errormsg + + if found_routes: + logger.info( + "[DUT: %s]: Verified routes in RIB, found" " routes are: %s\n", + dut, + found_routes, + ) + + continue + + if "bgp" in input_dict[routerInput]: + if ( + "advertise_networks" + not in input_dict[routerInput]["bgp"]["address_family"][addr_type][ + "unicast" + ] + ): + continue + + found_routes = [] + missing_routes = [] + advertise_network = input_dict[routerInput]["bgp"]["address_family"][ + addr_type + ]["unicast"]["advertise_networks"] + + # Continue if there are no network advertise + if len(advertise_network) == 0: + continue + + for advertise_network_dict in advertise_network: + if "vrf" in advertise_network_dict: + cmd = "{} vrf {} json".format(command, static_route["vrf"]) + else: + cmd = "{} json".format(command) + + rib_routes_json = run_frr_cmd(rnode, cmd, isjson=True) + + # Verifying output dictionary rib_routes_json is not empty + if bool(rib_routes_json) is False: + errormsg = "No route found in rib of router {}..".format(router) + return errormsg + + start_ip = advertise_network_dict["network"] + if "no_of_network" in advertise_network_dict: + no_of_network = advertise_network_dict["no_of_network"] + else: + no_of_network = 1 + + # Generating IPs for verification + ip_list = generate_ips(start_ip, no_of_network) + st_found = False + nh_found = False + + for st_rt in ip_list: + st_rt = str(ipaddress.ip_network(unicode(st_rt))) + + _addr_type = validate_ip_address(st_rt) + if _addr_type != addr_type: + continue + + if st_rt in rib_routes_json: + st_found = True + found_routes.append(st_rt) + + if next_hop: + if type(next_hop) is not list: + next_hop = [next_hop] + + count = 0 + for nh in next_hop: + for nh_dict in rib_routes_json[st_rt][0]["nexthops"]: + if nh_dict["ip"] != nh: + continue + else: + count += 1 + + if count == len(next_hop): + nh_found = True + else: + errormsg = ( + "Nexthop {} is Missing" + " for route {} in " + "RIB of router {}\n".format(next_hop, st_rt, dut) + ) + return errormsg + else: + missing_routes.append(st_rt) + + if nh_found: + logger.info( + "Found next_hop {} for all routes in RIB" + " of router {}\n".format(next_hop, dut) + ) + + if len(missing_routes) > 0: + errormsg = ( + "Missing {} route in RIB of router {}, " + "routes: {} \n".format(addr_type, dut, missing_routes) + ) + return errormsg + + if found_routes: + logger.info( + "Verified {} routes in router {} RIB, found" + " routes are: {}\n".format(addr_type, dut, found_routes) + ) + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return True + + +@retry(attempts=5, wait=2, return_is_str=True, initial_wait=2) +def verify_fib_routes(tgen, addr_type, dut, input_dict, next_hop=None): + """ + Data will be read from input_dict or input JSON file, API will generate + same prefixes, which were redistributed by either create_static_routes() or + advertise_networks_using_network_command() and will verify next_hop and + each prefix/routes is present in "show ip/ipv6 fib json" + command o/p. + + Parameters + ---------- + * `tgen` : topogen object + * `addr_type` : ip type, ipv4/ipv6 + * `dut`: Device Under Test, for which user wants to test the data + * `input_dict` : input dict, has details of static routes + * `next_hop`[optional]: next_hop which needs to be verified, + default: static + + Usage + ----- + input_routes_r1 = { + "r1": { + "static_routes": [{ + "network": ["1.1.1.1/32], + "next_hop": "Null0", + "vrf": "RED" + }] + } + } + result = result = verify_fib_routes(tgen, "ipv4, "r1", input_routes_r1) + + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + router_list = tgen.routers() + for routerInput in input_dict.keys(): + for router, rnode in router_list.iteritems(): + if router != dut: + continue + + logger.info("Checking router %s FIB routes:", router) + + # Verifying RIB routes + if addr_type == "ipv4": + command = "show ip fib" + else: + command = "show ipv6 fib" + + found_routes = [] + missing_routes = [] + + if "static_routes" in input_dict[routerInput]: + static_routes = input_dict[routerInput]["static_routes"] + + for static_route in static_routes: + if "vrf" in static_route and static_route["vrf"] is not None: + + logger.info( + "[DUT: {}]: Verifying routes for VRF:" + " {}".format(router, static_route["vrf"]) + ) + + cmd = "{} vrf {}".format(command, static_route["vrf"]) + + else: + cmd = "{}".format(command) + + cmd = "{} json".format(cmd) + + rib_routes_json = run_frr_cmd(rnode, cmd, isjson=True) + + # Verifying output dictionary rib_routes_json is not empty + if bool(rib_routes_json) is False: + errormsg = "[DUT: {}]: No route found in fib".format(router) + return errormsg + + network = static_route["network"] + if "no_of_ip" in static_route: + no_of_ip = static_route["no_of_ip"] + else: + no_of_ip = 1 + + # Generating IPs for verification + ip_list = generate_ips(network, no_of_ip) + st_found = False + nh_found = False + + for st_rt in ip_list: + st_rt = str(ipaddress.ip_network(unicode(st_rt))) + #st_rt = str(ipaddr.IPNetwork(unicode(st_rt))) + + _addr_type = validate_ip_address(st_rt) + if _addr_type != addr_type: + continue + + if st_rt in rib_routes_json: + st_found = True + found_routes.append(st_rt) + + if next_hop: + if type(next_hop) is not list: + next_hop = [next_hop] + + count = 0 + for nh in next_hop: + for nh_dict in rib_routes_json[st_rt][0][ + "nexthops" + ]: + if nh_dict["ip"] != nh: + continue + else: + count += 1 + + if count == len(next_hop): + nh_found = True + else: + missing_routes.append(st_rt) + errormsg = ( + "Nexthop {} is Missing" + " for route {} in " + "RIB of router {}\n".format( + next_hop, st_rt, dut + ) + ) + return errormsg + + else: + missing_routes.append(st_rt) + + if len(missing_routes) > 0: + errormsg = "[DUT: {}]: Missing route in FIB:" " {}".format( + dut, missing_routes + ) + return errormsg + + if nh_found: + logger.info( + "Found next_hop {} for all routes in RIB" + " of router {}\n".format(next_hop, dut) + ) + + if found_routes: + logger.info( + "[DUT: %s]: Verified routes in FIB, found" " routes are: %s\n", + dut, + found_routes, + ) + + continue + + if "bgp" in input_dict[routerInput]: + if ( + "advertise_networks" + not in input_dict[routerInput]["bgp"]["address_family"][addr_type][ + "unicast" + ] + ): + continue + + found_routes = [] + missing_routes = [] + advertise_network = input_dict[routerInput]["bgp"]["address_family"][ + addr_type + ]["unicast"]["advertise_networks"] + + # Continue if there are no network advertise + if len(advertise_network) == 0: + continue + + for advertise_network_dict in advertise_network: + if "vrf" in advertise_network_dict: + cmd = "{} vrf {} json".format(command, static_route["vrf"]) + else: + cmd = "{} json".format(command) + + rib_routes_json = run_frr_cmd(rnode, cmd, isjson=True) + + # Verifying output dictionary rib_routes_json is not empty + if bool(rib_routes_json) is False: + errormsg = "No route found in rib of router {}..".format(router) + return errormsg + + start_ip = advertise_network_dict["network"] + if "no_of_network" in advertise_network_dict: + no_of_network = advertise_network_dict["no_of_network"] + else: + no_of_network = 1 + + # Generating IPs for verification + ip_list = generate_ips(start_ip, no_of_network) + st_found = False + nh_found = False + + for st_rt in ip_list: + #st_rt = str(ipaddr.IPNetwork(unicode(st_rt))) + st_rt = str(ipaddress.ip_network(unicode(st_rt))) + + _addr_type = validate_ip_address(st_rt) + if _addr_type != addr_type: + continue + + if st_rt in rib_routes_json: + st_found = True + found_routes.append(st_rt) + + if next_hop: + if type(next_hop) is not list: + next_hop = [next_hop] + + count = 0 + for nh in next_hop: + for nh_dict in rib_routes_json[st_rt][0]["nexthops"]: + if nh_dict["ip"] != nh: + continue + else: + count += 1 + + if count == len(next_hop): + nh_found = True + else: + missing_routes.append(st_rt) + errormsg = ( + "Nexthop {} is Missing" + " for route {} in " + "RIB of router {}\n".format(next_hop, st_rt, dut) + ) + return errormsg + else: + missing_routes.append(st_rt) + + if len(missing_routes) > 0: + errormsg = "[DUT: {}]: Missing route in FIB: " "{} \n".format( + dut, missing_routes + ) + return errormsg + + if nh_found: + logger.info( + "Found next_hop {} for all routes in RIB" + " of router {}\n".format(next_hop, dut) + ) + + if found_routes: + logger.info( + "[DUT: {}]: Verified routes FIB" + ", found routes are: {}\n".format(dut, found_routes) + ) + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return True + + +@retry(attempts=5, wait=2, return_is_str=True, initial_wait=2) +def verify_fib_routes(tgen, addr_type, dut, input_dict, next_hop=None): + """ + Data will be read from input_dict or input JSON file, API will generate + same prefixes, which were redistributed by either create_static_routes() or + advertise_networks_using_network_command() and will verify next_hop and + each prefix/routes is present in "show ip/ipv6 fib json" + command o/p. + + Parameters + ---------- + * `tgen` : topogen object + * `addr_type` : ip type, ipv4/ipv6 + * `dut`: Device Under Test, for which user wants to test the data + * `input_dict` : input dict, has details of static routes + * `next_hop`[optional]: next_hop which needs to be verified, + default: static + + Usage + ----- + input_routes_r1 = { + "r1": { + "static_routes": [{ + "network": ["1.1.1.1/32], + "next_hop": "Null0", + "vrf": "RED" + }] + } + } + result = result = verify_fib_routes(tgen, "ipv4, "r1", input_routes_r1) + + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + router_list = tgen.routers() + for routerInput in input_dict.keys(): + for router, rnode in router_list.iteritems(): + if router != dut: + continue + + logger.info("Checking router %s FIB routes:", router) + + # Verifying RIB routes + if addr_type == "ipv4": + command = "show ip fib" + else: + command = "show ipv6 fib" + + found_routes = [] + missing_routes = [] + + if "static_routes" in input_dict[routerInput]: + static_routes = input_dict[routerInput]["static_routes"] + + for static_route in static_routes: + if "vrf" in static_route and static_route["vrf"] is not None: + + logger.info( + "[DUT: {}]: Verifying routes for VRF:" + " {}".format(router, static_route["vrf"]) + ) + + cmd = "{} vrf {}".format(command, static_route["vrf"]) + + else: + cmd = "{}".format(command) + + cmd = "{} json".format(cmd) + + rib_routes_json = run_frr_cmd(rnode, cmd, isjson=True) + + # Verifying output dictionary rib_routes_json is not empty + if bool(rib_routes_json) is False: + errormsg = "[DUT: {}]: No route found in fib".format(router) + return errormsg + + network = static_route["network"] + if "no_of_ip" in static_route: + no_of_ip = static_route["no_of_ip"] + else: + no_of_ip = 1 + + # Generating IPs for verification + ip_list = generate_ips(network, no_of_ip) + st_found = False + nh_found = False + + for st_rt in ip_list: + st_rt = str(ipaddress.ip_network(unicode(st_rt))) + + _addr_type = validate_ip_address(st_rt) + if _addr_type != addr_type: + continue + + if st_rt in rib_routes_json: + st_found = True + found_routes.append(st_rt) + + if next_hop: + if type(next_hop) is not list: + next_hop = [next_hop] + + count = 0 + for nh in next_hop: + for nh_dict in rib_routes_json[st_rt][0][ + "nexthops" + ]: + if nh_dict["ip"] != nh: + continue + else: + count += 1 + + if count == len(next_hop): + nh_found = True + else: + missing_routes.append(st_rt) + errormsg = ( + "Nexthop {} is Missing" + " for route {} in " + "RIB of router {}\n".format( + next_hop, st_rt, dut + ) + ) + return errormsg + + else: + missing_routes.append(st_rt) + + if len(missing_routes) > 0: + errormsg = "[DUT: {}]: Missing route in FIB:" " {}".format( + dut, missing_routes + ) + return errormsg + + if nh_found: + logger.info( + "Found next_hop {} for all routes in RIB" + " of router {}\n".format(next_hop, dut) + ) + + if found_routes: + logger.info( + "[DUT: %s]: Verified routes in FIB, found" " routes are: %s\n", + dut, + found_routes, + ) + + continue + + if "bgp" in input_dict[routerInput]: + if ( + "advertise_networks" + not in input_dict[routerInput]["bgp"]["address_family"][addr_type][ + "unicast" + ] + ): + continue + + found_routes = [] + missing_routes = [] + advertise_network = input_dict[routerInput]["bgp"]["address_family"][ + addr_type + ]["unicast"]["advertise_networks"] + + # Continue if there are no network advertise + if len(advertise_network) == 0: + continue + + for advertise_network_dict in advertise_network: + if "vrf" in advertise_network_dict: + cmd = "{} vrf {} json".format(command, static_route["vrf"]) + else: + cmd = "{} json".format(command) + + rib_routes_json = run_frr_cmd(rnode, cmd, isjson=True) + + # Verifying output dictionary rib_routes_json is not empty + if bool(rib_routes_json) is False: + errormsg = "No route found in rib of router {}..".format(router) + return errormsg + + start_ip = advertise_network_dict["network"] + if "no_of_network" in advertise_network_dict: + no_of_network = advertise_network_dict["no_of_network"] + else: + no_of_network = 1 + + # Generating IPs for verification + ip_list = generate_ips(start_ip, no_of_network) + st_found = False + nh_found = False + + for st_rt in ip_list: + st_rt = str(ipaddress.ip_network(unicode(st_rt))) + + _addr_type = validate_ip_address(st_rt) + if _addr_type != addr_type: + continue + + if st_rt in rib_routes_json: + st_found = True + found_routes.append(st_rt) + + if next_hop: + if type(next_hop) is not list: + next_hop = [next_hop] + + count = 0 + for nh in next_hop: + for nh_dict in rib_routes_json[st_rt][0]["nexthops"]: + if nh_dict["ip"] != nh: + continue + else: + count += 1 + + if count == len(next_hop): + nh_found = True + else: + missing_routes.append(st_rt) + errormsg = ( + "Nexthop {} is Missing" + " for route {} in " + "RIB of router {}\n".format(next_hop, st_rt, dut) + ) + return errormsg + else: + missing_routes.append(st_rt) + + if len(missing_routes) > 0: + errormsg = "[DUT: {}]: Missing route in FIB: " "{} \n".format( + dut, missing_routes + ) + return errormsg + + if nh_found: + logger.info( + "Found next_hop {} for all routes in RIB" + " of router {}\n".format(next_hop, dut) + ) + + if found_routes: + logger.info( + "[DUT: {}]: Verified routes FIB" + ", found routes are: {}\n".format(dut, found_routes) + ) + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return True + + +def verify_admin_distance_for_static_routes(tgen, input_dict): + """ + API to verify admin distance for static routes as defined in input_dict/ + input JSON by running show ip/ipv6 route json command. + Parameter + --------- + * `tgen` : topogen object + * `input_dict`: having details like - for which router and static routes + admin dsitance needs to be verified + Usage + ----- + # To verify admin distance is 10 for prefix 10.0.20.1/32 having next_hop + 10.0.0.2 in router r1 + input_dict = { + "r1": { + "static_routes": [{ + "network": "10.0.20.1/32", + "admin_distance": 10, + "next_hop": "10.0.0.2" + }] + } + } + result = verify_admin_distance_for_static_routes(tgen, input_dict) + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + for router in input_dict.keys(): + if router not in tgen.routers(): + continue + + rnode = tgen.routers()[router] + + for static_route in input_dict[router]["static_routes"]: + addr_type = validate_ip_address(static_route["network"]) + # Command to execute + if addr_type == "ipv4": + command = "show ip route json" + else: + command = "show ipv6 route json" + show_ip_route_json = run_frr_cmd(rnode, command, isjson=True) + + logger.info( + "Verifying admin distance for static route %s" " under dut %s:", + static_route, + router, + ) + network = static_route["network"] + next_hop = static_route["next_hop"] + admin_distance = static_route["admin_distance"] + route_data = show_ip_route_json[network][0] + if network in show_ip_route_json: + if route_data["nexthops"][0]["ip"] == next_hop: + if route_data["distance"] != admin_distance: + errormsg = ( + "Verification failed: admin distance" + " for static route {} under dut {}," + " found:{} but expected:{}".format( + static_route, + router, + route_data["distance"], + admin_distance, + ) + ) + return errormsg + else: + logger.info( + "Verification successful: admin" + " distance for static route %s under" + " dut %s, found:%s", + static_route, + router, + route_data["distance"], + ) + + else: + errormsg = ( + "Static route {} not found in " + "show_ip_route_json for dut {}".format(network, router) + ) + return errormsg + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return True + + +def verify_prefix_lists(tgen, input_dict): + """ + Running "show ip prefix-list" command and verifying given prefix-list + is present in router. + Parameters + ---------- + * `tgen` : topogen object + * `input_dict`: data to verify prefix lists + Usage + ----- + # To verify pf_list_1 is present in router r1 + input_dict = { + "r1": { + "prefix_lists": ["pf_list_1"] + }} + result = verify_prefix_lists("ipv4", input_dict, tgen) + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + for router in input_dict.keys(): + if router not in tgen.routers(): + continue + + rnode = tgen.routers()[router] + + # Show ip prefix list + show_prefix_list = run_frr_cmd(rnode, "show ip prefix-list") + + # Verify Prefix list is deleted + prefix_lists_addr = input_dict[router]["prefix_lists"] + for addr_type in prefix_lists_addr: + if not check_address_types(addr_type): + continue + + for prefix_list in prefix_lists_addr[addr_type].keys(): + if prefix_list in show_prefix_list: + errormsg = ( + "Prefix list {} is/are present in the router" + " {}".format(prefix_list, router) + ) + return errormsg + + logger.info( + "Prefix list %s is/are not present in the router" " from router %s", + prefix_list, + router, + ) + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return True + + +@retry(attempts=2, wait=4, return_is_str=True, initial_wait=2) +def verify_route_maps(tgen, input_dict): + """ + Running "show route-map" command and verifying given route-map + is present in router. + Parameters + ---------- + * `tgen` : topogen object + * `input_dict`: data to verify prefix lists + Usage + ----- + # To verify rmap_1 and rmap_2 are present in router r1 + input_dict = { + "r1": { + "route_maps": ["rmap_1", "rmap_2"] + } + } + result = verify_route_maps(tgen, input_dict) + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + for router in input_dict.keys(): + if router not in tgen.routers(): + continue + + rnode = tgen.routers()[router] + # Show ip route-map + show_route_maps = rnode.vtysh_cmd("show route-map") + + # Verify route-map is deleted + route_maps = input_dict[router]["route_maps"] + for route_map in route_maps: + if route_map in show_route_maps: + errormsg = "Route map {} is not deleted from router" " {}".format( + route_map, router + ) + return errormsg + + logger.info( + "Route map %s is/are deleted successfully from" " router %s", + route_maps, + router, + ) + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return True + + +@retry(attempts=3, wait=4, return_is_str=True) +def verify_bgp_community(tgen, addr_type, router, network, input_dict=None): + """ + API to veiryf BGP large community is attached in route for any given + DUT by running "show bgp ipv4/6 {route address} json" command. + Parameters + ---------- + * `tgen`: topogen object + * `addr_type` : ip type, ipv4/ipv6 + * `dut`: Device Under Test + * `network`: network for which set criteria needs to be verified + * `input_dict`: having details like - for which router, community and + values needs to be verified + Usage + ----- + networks = ["200.50.2.0/32"] + input_dict = { + "largeCommunity": "2:1:1 2:2:2 2:3:3 2:4:4 2:5:5" + } + result = verify_bgp_community(tgen, "ipv4", dut, network, input_dict=None) + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + if router not in tgen.routers(): + return False + + rnode = tgen.routers()[router] + + logger.debug( + "Verifying BGP community attributes on dut %s: for %s " "network %s", + router, + addr_type, + network, + ) + + for net in network: + cmd = "show bgp {} {} json".format(addr_type, net) + show_bgp_json = rnode.vtysh_cmd(cmd, isjson=True) + logger.info(show_bgp_json) + if "paths" not in show_bgp_json: + return "Prefix {} not found in BGP table of router: {}".format(net, router) + + as_paths = show_bgp_json["paths"] + found = False + for i in range(len(as_paths)): + if ( + "largeCommunity" in show_bgp_json["paths"][i] + or "community" in show_bgp_json["paths"][i] + ): + found = True + logger.info( + "Large Community attribute is found for route:" " %s in router: %s", + net, + router, + ) + if input_dict is not None: + for criteria, comm_val in input_dict.items(): + show_val = show_bgp_json["paths"][i][criteria]["string"] + if comm_val == show_val: + logger.info( + "Verifying BGP %s for prefix: %s" + " in router: %s, found expected" + " value: %s", + criteria, + net, + router, + comm_val, + ) + else: + errormsg = ( + "Failed: Verifying BGP attribute" + " {} for route: {} in router: {}" + ", expected value: {} but found" + ": {}".format(criteria, net, router, comm_val, show_val) + ) + return errormsg + + if not found: + errormsg = ( + "Large Community attribute is not found for route: " + "{} in router: {} ".format(net, router) + ) + return errormsg + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return True + + +def verify_create_community_list(tgen, input_dict): + """ + API is to verify if large community list is created for any given DUT in + input_dict by running "sh bgp large-community-list {"comm_name"} detail" + command. + Parameters + ---------- + * `tgen`: topogen object + * `input_dict`: having details like - for which router, large community + needs to be verified + Usage + ----- + input_dict = { + "r1": { + "large-community-list": { + "standard": { + "Test1": [{"action": "PERMIT", "attribute":\ + ""}] + }}}} + result = verify_create_community_list(tgen, input_dict) + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + for router in input_dict.keys(): + if router not in tgen.routers(): + continue + + rnode = tgen.routers()[router] + + logger.info("Verifying large-community is created for dut %s:", router) + + for comm_data in input_dict[router]["bgp_community_lists"]: + comm_name = comm_data["name"] + comm_type = comm_data["community_type"] + show_bgp_community = run_frr_cmd( + rnode, "show bgp large-community-list {} detail".format(comm_name) + ) + + # Verify community list and type + if comm_name in show_bgp_community and comm_type in show_bgp_community: + logger.info( + "BGP %s large-community-list %s is" " created", comm_type, comm_name + ) + else: + errormsg = "BGP {} large-community-list {} is not" " created".format( + comm_type, comm_name + ) + return errormsg + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return True + + +def verify_cli_json(tgen, input_dict): + """ + API to verify if JSON is available for clis + command. + + Parameters + ---------- + * `tgen`: topogen object + * `input_dict`: CLIs for which JSON needs to be verified + Usage + ----- + input_dict = { + "edge1":{ + "cli": ["show evpn vni detail", show evpn rmac vni all] + } + } + + result = verify_cli_json(tgen, input_dict) + + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + for dut in input_dict.keys(): + rnode = tgen.routers()[dut] + + for cli in input_dict[dut]["cli"]: + logger.info( + "[DUT: %s]: Verifying JSON is available for " "CLI %s :", dut, cli + ) + + test_cli = "{} json".format(cli) + ret_json = rnode.vtysh_cmd(test_cli, isjson=True) + if not bool(ret_json): + errormsg = "CLI: %s, JSON format is not available" % (cli) + return errormsg + elif "unknown" in ret_json or "Unknown" in ret_json: + errormsg = "CLI: %s, JSON format is not available" % (cli) + return errormsg + else: + logger.info( + "CLI : %s JSON format is available: " "\n %s", cli, ret_json + ) + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + + return True + + +@retry(attempts=2, wait=4, return_is_str=True, initial_wait=2) +def verify_evpn_vni(tgen, input_dict): + """ + API to verify evpn vni details using "show evpn vni detail json" + command. + + Parameters + ---------- + * `tgen`: topogen object + * `input_dict`: having details like - for which router, evpn details + needs to be verified + Usage + ----- + input_dict = { + "edge1":{ + "vni": [ + { + "75100":{ + "vrf": "RED", + "vxlanIntf": "vxlan75100", + "localVtepIp": "120.1.1.1", + "sviIntf": "br100" + } + } + ] + } + } + + result = verify_evpn_vni(tgen, input_dict) + + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + for dut in input_dict.keys(): + rnode = tgen.routers()[dut] + + logger.info("[DUT: %s]: Verifying evpn vni details :", dut) + + cmd = "show evpn vni detail json" + evpn_all_vni_json = run_frr_cmd(rnode, cmd, isjson=True) + if not bool(evpn_all_vni_json): + errormsg = "No output for '{}' cli".format(cmd) + return errormsg + + if "vni" in input_dict[dut]: + for vni_dict in input_dict[dut]["vni"]: + found = False + vni = vni_dict["name"] + for evpn_vni_json in evpn_all_vni_json: + if "vni" in evpn_vni_json: + if evpn_vni_json["vni"] != int(vni): + continue + + for attribute in vni_dict.keys(): + if vni_dict[attribute] != evpn_vni_json[attribute]: + errormsg = ( + "[DUT: %s] Verifying " + "%s for VNI: %s [FAILED]||" + ", EXPECTED : %s " + " FOUND : %s" + % ( + dut, + attribute, + vni, + vni_dict[attribute], + evpn_vni_json[attribute], + ) + ) + return errormsg + + else: + found = True + logger.info( + "[DUT: %s] Verifying" + " %s for VNI: %s , " + "Found Expected : %s ", + dut, + attribute, + vni, + evpn_vni_json[attribute], + ) + + if evpn_vni_json["state"] != "Up": + errormsg = ( + "[DUT: %s] Failed: Verifying" + " State for VNI: %s is not Up" % (dut, vni) + ) + return errormsg + + else: + errormsg = ( + "[DUT: %s] Failed:" + " VNI: %s is not present in JSON" % (dut, vni) + ) + return errormsg + + if found: + logger.info( + "[DUT %s]: Verifying VNI : %s " + "details and state is Up [PASSED]!!", + dut, + vni, + ) + return True + + else: + errormsg = ( + "[DUT: %s] Failed:" " vni details are not present in input data" % (dut) + ) + return errormsg + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return False + + +@retry(attempts=2, wait=4, return_is_str=True, initial_wait=2) +def verify_vrf_vni(tgen, input_dict): + """ + API to verify vrf vni details using "show vrf vni json" + command. + + Parameters + ---------- + * `tgen`: topogen object + * `input_dict`: having details like - for which router, evpn details + needs to be verified + Usage + ----- + input_dict = { + "edge1":{ + "vrfs": [ + { + "RED":{ + "vni": 75000, + "vxlanIntf": "vxlan75100", + "sviIntf": "br100", + "routerMac": "00:80:48:ba:d1:00", + "state": "Up" + } + } + ] + } + } + + result = verify_vrf_vni(tgen, input_dict) + + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + for dut in input_dict.keys(): + rnode = tgen.routers()[dut] + + logger.info("[DUT: %s]: Verifying vrf vni details :", dut) + + cmd = "show vrf vni json" + vrf_all_vni_json = run_frr_cmd(rnode, cmd, isjson=True) + if not bool(vrf_all_vni_json): + errormsg = "No output for '{}' cli".format(cmd) + return errormsg + + if "vrfs" in input_dict[dut]: + for vrfs in input_dict[dut]["vrfs"]: + for vrf, vrf_dict in vrfs.items(): + found = False + for vrf_vni_json in vrf_all_vni_json["vrfs"]: + if "vrf" in vrf_vni_json: + if vrf_vni_json["vrf"] != vrf: + continue + + for attribute in vrf_dict.keys(): + if vrf_dict[attribute] == vrf_vni_json[attribute]: + found = True + logger.info( + "[DUT %s]: VRF: %s, " + "verifying %s " + ", Found Expected: %s " + "[PASSED]!!", + dut, + vrf, + attribute, + vrf_vni_json[attribute], + ) + else: + errormsg = ( + "[DUT: %s] VRF: %s, " + "verifying %s [FAILED!!] " + ", EXPECTED : %s " + ", FOUND : %s" + % ( + dut, + vrf, + attribute, + vrf_dict[attribute], + vrf_vni_json[attribute], + ) + ) + return errormsg + + else: + errormsg = "[DUT: %s] VRF: %s " "is not present in JSON" % ( + dut, + vrf, + ) + return errormsg + + if found: + logger.info( + "[DUT %s] Verifying VRF: %s " " details [PASSED]!!", + dut, + vrf, + ) + return True + + else: + errormsg = ( + "[DUT: %s] Failed:" " vrf details are not present in input data" % (dut) + ) + return errormsg + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return False + + +def required_linux_kernel_version(required_version): + """ + This API is used to check linux version compatibility of the test suite. + If version mentioned in required_version is higher than the linux kernel + of the system, test suite will be skipped. This API returns true or errormsg. + + Parameters + ---------- + * `required_version` : Kernel version required for the suites to run. + + Usage + ----- + result = linux_kernel_version_lowerthan('4.15') + + Returns + ------- + errormsg(str) or True + """ + system_kernel = platform.release() + if version_cmp(system_kernel, required_version) < 0: + error_msg = ('These tests will not run on kernel "{}", ' + 'they require kernel >= {})'.format(system_kernel, + required_version )) + return error_msg + return True + + +def kernel_requires_l3mdev_adjustment(): + """ + Checks if the L3 master device needs to be adjusted to handle VRF traffic + based on kernel version. + + Returns + ------- + 1 or 0 + """ + + if version_cmp(platform.release(), "4.15") >= 0: + return 1 + return 0 + + +def adjust_router_l3mdev(tgen, router): + """ + Adjusts a routers L3 master device to handle VRF traffic depending on kernel + version. + + Parameters + ---------- + * `tgen` : tgen object + * `router` : router id to be configured. + + Returns + ------- + True + """ + + l3mdev_accept = kernel_requires_l3mdev_adjustment() + + logger.info( + "router {0}: setting net.ipv4.tcp_l3mdev_accept={1}".format( + router, l3mdev_accept + ) + ) + + output = tgen.net[router].cmd("sysctl -n net.ipv4.tcp_l3mdev_accept") + logger.info( + "router {0}: existing tcp_l3mdev_accept was {1}".format(router, output) + ) + + tgen.net[router].cmd( + "sysctl -w net.ipv4.tcp_l3mdev_accept={}".format(l3mdev_accept) + ) + + return True diff --git a/tests/topotests/lib/ltemplate.py b/tests/topotests/lib/ltemplate.py index 31eaec7009..a76d8e4b08 100644 --- a/tests/topotests/lib/ltemplate.py +++ b/tests/topotests/lib/ltemplate.py @@ -83,22 +83,15 @@ def setup_module(self, mod): # For all registred routers, load the zebra configuration file for rname, router in router_list.iteritems(): - print("Setting up %s" % rname) - config = os.path.join(self.testdir, '{}/zebra.conf'.format(rname)) - if os.path.exists(config): - router.load_config(TopoRouter.RD_ZEBRA, config) - config = os.path.join(self.testdir, '{}/ospfd.conf'.format(rname)) - if os.path.exists(config): - router.load_config(TopoRouter.RD_OSPF, config) - config = os.path.join(self.testdir, '{}/ldpd.conf'.format(rname)) - if os.path.exists(config): - router.load_config(TopoRouter.RD_LDP, config) - config = os.path.join(self.testdir, '{}/bgpd.conf'.format(rname)) - if os.path.exists(config): - router.load_config(TopoRouter.RD_BGP, config) - config = os.path.join(self.testdir, '{}/isisd.conf'.format(rname)) - if os.path.exists(config): - router.load_config(TopoRouter.RD_ISIS, config) + logger.info("Setting up %s" % rname) + for rd_val in TopoRouter.RD: + config = os.path.join(self.testdir, '{}/{}.conf'.format(rname,TopoRouter.RD[rd_val])) + prog = os.path.join(tgen.net[rname].daemondir, TopoRouter.RD[rd_val]) + if os.path.exists(config): + if os.path.exists(prog): + router.load_config(rd_val, config) + else: + logger.warning("{} not found, but have {}.conf file".format(prog, TopoRouter.RD[rd_val])) # After loading the configurations, this function loads configured daemons. logger.info('Starting routers') @@ -141,6 +134,7 @@ def teardown_module(mod): tgen = get_topogen() if _lt != None and _lt.scriptdir != None and _lt.prestarthooksuccess == True: + luShowResults(logger.info) print(luFinish()) # This function tears down the whole topology. diff --git a/tests/topotests/lib/lutil.py b/tests/topotests/lib/lutil.py index 3ae18018bf..4ea97a3692 100755 --- a/tests/topotests/lib/lutil.py +++ b/tests/topotests/lib/lutil.py @@ -22,6 +22,7 @@ import time import datetime import json +import math from topolog import logger from mininet.net import Mininet @@ -37,7 +38,7 @@ class lUtil: base_log_dir = '.' fout_name = 'output.log' fsum_name = 'summary.txt' - l_level = 9 + l_level = 6 CallOnFail = False l_total = 0 @@ -53,12 +54,12 @@ class lUtil: fsum = '' net = '' - def log(self, str): + def log(self, str, level=6): if self.l_level > 0: if self.fout == '': self.fout = open(self.fout_name, 'w', 0) self.fout.write(str+'\n') - if self.l_level > 5: + if level <= self.l_level: print(str) def summary(self, str): @@ -226,36 +227,43 @@ def command(self, target, command, regexp, op, result, returnJson): ret = success else: ret = search.group() - self.log('found:%s:' % ret) if op != 'fail': success = True + level = 7 else: 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)) + 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: return js return ret - def wait(self, target, command, regexp, op, result, wait, returnJson): - self.log('%s:%s WAIT:%s:%s:%s:%s:%s:%s:' % \ - (self.l_filename, self.l_line, target, command, regexp, op, result,wait)) - llevel = LUtil.l_level + def wait(self, target, command, regexp, op, result, wait, returnJson, wait_time=0.5): + self.log('%s:%s WAIT:%s:%s:%s:%s:%s:%s:%s:' % \ + (self.l_filename, self.l_line, target, command, regexp, op, result,wait,wait_time)) found = False n = 0 startt = time.time() - delta = time.time() - startt - while delta < wait and found is False: + + # Calculate the amount of `sleep`s we are going to peform. + wait_count = int(math.ceil(wait / wait_time)) + 1 + + while wait_count > 0: + n += 1 found = self.command(target, command, regexp, op, result, returnJson) - n+=1 - LUtil.l_level = 0 - delta = time.time() - startt - if delta < wait and found is False: - time.sleep (0.5) - LUtil.l_level = llevel + if found is not False: + break + + wait_count -= 1 + if wait_count > 0: + time.sleep(wait_time) + + delta = time.time() - startt self.log('Done after %d loops, time=%s, Found=%s' % (n, delta, found)) found = self.command(target, command, regexp, 'pass', '%s +%4.2f secs' % (result, delta), returnJson) return found @@ -265,7 +273,7 @@ def wait(self, target, command, regexp, op, result, wait, returnJson): #entry calls def luStart(baseScriptDir='.', baseLogDir='.', net='', - fout='output.log', fsum='summary.txt', level=9): + fout='output.log', fsum='summary.txt', level=None): global LUtil #init class LUtil=lUtil() @@ -276,24 +284,25 @@ def luStart(baseScriptDir='.', baseLogDir='.', net='', LUtil.fout_name = baseLogDir + '/' + fout if fsum != None: LUtil.fsum_name = baseLogDir + '/' + fsum - LUtil.l_level = level + if level != None: + LUtil.l_level = level LUtil.l_dotall_experiment = False LUtil.l_dotall_experiment = True -def luCommand(target, command, regexp='.', op='none', result='', time=10, returnJson=False): +def luCommand(target, command, regexp='.', op='none', result='', time=10, returnJson=False, wait_time=0.5): if op != 'wait': return LUtil.command(target, command, regexp, op, result, returnJson) else: - return LUtil.wait(target, command, regexp, op, result, time, returnJson) + return LUtil.wait(target, command, regexp, op, result, time, returnJson, wait_time) def luLast(usenl=False): if usenl: if LUtil.l_last_nl != None: - LUtil.log('luLast:%s:' % LUtil.l_last_nl.group()) + 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()) + LUtil.log('luLast:%s:' % LUtil.l_last.group(), 7) return LUtil.l_last def luInclude(filename, CallOnFail=None): @@ -327,6 +336,14 @@ def luNumPass(): def luResult(target, success, str, logstr=None): return LUtil.result(target, success, str, logstr) +def luShowResults(prFunction): + printed = 0 + sf = open(LUtil.fsum_name, 'r') + for line in sf: + printed+=1 + prFunction(line.rstrip()) + sf.close() + def luShowFail(): printed = 0 sf = open(LUtil.fsum_name, 'r') diff --git a/tests/topotests/lib/test/test_json.py b/tests/topotests/lib/test/test_json.py index 3927ba095d..7a061a9bc6 100755 --- a/tests/topotests/lib/test/test_json.py +++ b/tests/topotests/lib/test/test_json.py @@ -32,36 +32,37 @@ # Save the Current Working Directory to find lib files. CWD = os.path.dirname(os.path.realpath(__file__)) -sys.path.append(os.path.join(CWD, '../../')) +sys.path.append(os.path.join(CWD, "../../")) # pylint: disable=C0413 from lib.topotest import json_cmp + def test_json_intersect_true(): "Test simple correct JSON intersections" dcomplete = { - 'i1': 'item1', - 'i2': 'item2', - 'i3': 'item3', - 'i100': 'item4', + "i1": "item1", + "i2": "item2", + "i3": "item3", + "i100": "item4", } dsub1 = { - 'i1': 'item1', - 'i3': 'item3', + "i1": "item1", + "i3": "item3", } dsub2 = { - 'i1': 'item1', - 'i2': 'item2', + "i1": "item1", + "i2": "item2", } dsub3 = { - 'i100': 'item4', - 'i2': 'item2', + "i100": "item4", + "i2": "item2", } dsub4 = { - 'i50': None, - 'i100': 'item4', + "i50": None, + "i100": "item4", } assert json_cmp(dcomplete, dsub1) is None @@ -69,99 +70,66 @@ def test_json_intersect_true(): assert json_cmp(dcomplete, dsub3) is None assert json_cmp(dcomplete, dsub4) is None + def test_json_intersect_false(): "Test simple incorrect JSON intersections" dcomplete = { - 'i1': 'item1', - 'i2': 'item2', - 'i3': 'item3', - 'i100': 'item4', + "i1": "item1", + "i2": "item2", + "i3": "item3", + "i100": "item4", } # Incorrect value for 'i1' dsub1 = { - 'i1': 'item3', - 'i3': 'item3', + "i1": "item3", + "i3": "item3", } # Non-existing key 'i5' dsub2 = { - 'i1': 'item1', - 'i5': 'item2', + "i1": "item1", + "i5": "item2", } # Key should not exist dsub3 = { - 'i100': None, + "i100": None, } assert json_cmp(dcomplete, dsub1) is not None assert json_cmp(dcomplete, dsub2) is not None assert json_cmp(dcomplete, dsub3) is not None + def test_json_intersect_multilevel_true(): "Test multi level correct JSON intersections" dcomplete = { - 'i1': 'item1', - 'i2': 'item2', - 'i3': { - 'i100': 'item100', + "i1": "item1", + "i2": "item2", + "i3": {"i100": "item100",}, + "i4": { + "i41": {"i411": "item411",}, + "i42": {"i421": "item421", "i422": "item422",}, }, - 'i4': { - 'i41': { - 'i411': 'item411', - }, - 'i42': { - 'i421': 'item421', - 'i422': 'item422', - } - } } dsub1 = { - 'i1': 'item1', - 'i3': { - 'i100': 'item100', - }, - 'i10': None, + "i1": "item1", + "i3": {"i100": "item100",}, + "i10": None, } dsub2 = { - 'i1': 'item1', - 'i2': 'item2', - 'i3': {}, + "i1": "item1", + "i2": "item2", + "i3": {}, } dsub3 = { - 'i2': 'item2', - 'i4': { - 'i41': { - 'i411': 'item411', - }, - 'i42': { - 'i422': 'item422', - 'i450': None, - } - } - } - dsub4 = { - 'i2': 'item2', - 'i4': { - 'i41': {}, - 'i42': { - 'i450': None, - } - } - } - dsub5 = { - 'i2': 'item2', - 'i3': { - 'i100': 'item100', - }, - 'i4': { - 'i42': { - 'i450': None, - } - } + "i2": "item2", + "i4": {"i41": {"i411": "item411",}, "i42": {"i422": "item422", "i450": None,}}, } + dsub4 = {"i2": "item2", "i4": {"i41": {}, "i42": {"i450": None,}}} + dsub5 = {"i2": "item2", "i3": {"i100": "item100",}, "i4": {"i42": {"i450": None,}}} assert json_cmp(dcomplete, dsub1) is None assert json_cmp(dcomplete, dsub2) is None @@ -169,78 +137,43 @@ def test_json_intersect_multilevel_true(): assert json_cmp(dcomplete, dsub4) is None assert json_cmp(dcomplete, dsub5) is None + def test_json_intersect_multilevel_false(): "Test multi level incorrect JSON intersections" dcomplete = { - 'i1': 'item1', - 'i2': 'item2', - 'i3': { - 'i100': 'item100', + "i1": "item1", + "i2": "item2", + "i3": {"i100": "item100",}, + "i4": { + "i41": {"i411": "item411",}, + "i42": {"i421": "item421", "i422": "item422",}, }, - 'i4': { - 'i41': { - 'i411': 'item411', - }, - 'i42': { - 'i421': 'item421', - 'i422': 'item422', - } - } } # Incorrect sub-level value dsub1 = { - 'i1': 'item1', - 'i3': { - 'i100': 'item00', - }, - 'i10': None, + "i1": "item1", + "i3": {"i100": "item00",}, + "i10": None, } # Inexistent sub-level dsub2 = { - 'i1': 'item1', - 'i2': 'item2', - 'i3': None, + "i1": "item1", + "i2": "item2", + "i3": None, } # Inexistent sub-level value dsub3 = { - 'i1': 'item1', - 'i3': { - 'i100': None, - }, + "i1": "item1", + "i3": {"i100": None,}, } # Inexistent sub-sub-level value - dsub4 = { - 'i4': { - 'i41': { - 'i412': 'item412', - }, - 'i42': { - 'i421': 'item421', - } - } - } + dsub4 = {"i4": {"i41": {"i412": "item412",}, "i42": {"i421": "item421",}}} # Invalid sub-sub-level value - dsub5 = { - 'i4': { - 'i41': { - 'i411': 'item411', - }, - 'i42': { - 'i421': 'item420000', - } - } - } + dsub5 = {"i4": {"i41": {"i411": "item411",}, "i42": {"i421": "item420000",}}} # sub-sub-level should be value - dsub6 = { - 'i4': { - 'i41': { - 'i411': 'item411', - }, - 'i42': 'foobar', - } - } + dsub6 = {"i4": {"i41": {"i411": "item411",}, "i42": "foobar",}} assert json_cmp(dcomplete, dsub1) is not None assert json_cmp(dcomplete, dsub2) is not None @@ -249,80 +182,54 @@ def test_json_intersect_multilevel_false(): assert json_cmp(dcomplete, dsub5) is not None assert json_cmp(dcomplete, dsub6) is not None + def test_json_with_list_sucess(): "Test successful json comparisons that have lists." dcomplete = { - 'list': [ - { - 'i1': 'item 1', - 'i2': 'item 2', - }, - { - 'i10': 'item 10', - }, - ], - 'i100': 'item 100', + "list": [{"i1": "item 1", "i2": "item 2",}, {"i10": "item 10",},], + "i100": "item 100", } # Test list type dsub1 = { - 'list': [], + "list": [], } # Test list correct list items dsub2 = { - 'list': [ - { - 'i1': 'item 1', - }, - ], - 'i100': 'item 100', + "list": [{"i1": "item 1",},], + "i100": "item 100", } # Test list correct list size dsub3 = { - 'list': [ - {}, {}, - ], + "list": [{}, {},], } assert json_cmp(dcomplete, dsub1) is None assert json_cmp(dcomplete, dsub2) is None assert json_cmp(dcomplete, dsub3) is None + def test_json_with_list_failure(): "Test failed json comparisons that have lists." dcomplete = { - 'list': [ - { - 'i1': 'item 1', - 'i2': 'item 2', - }, - { - 'i10': 'item 10', - }, - ], - 'i100': 'item 100', + "list": [{"i1": "item 1", "i2": "item 2",}, {"i10": "item 10",},], + "i100": "item 100", } # Test list type dsub1 = { - 'list': {}, + "list": {}, } # Test list incorrect list items dsub2 = { - 'list': [ - { - 'i1': 'item 2', - }, - ], - 'i100': 'item 100', + "list": [{"i1": "item 2",},], + "i100": "item 100", } # Test list correct list size dsub3 = { - 'list': [ - {}, {}, {}, - ], + "list": [{}, {}, {},], } assert json_cmp(dcomplete, dsub1) is not None @@ -334,53 +241,20 @@ def test_json_list_start_success(): "Test JSON encoded data that starts with a list that should succeed." dcomplete = [ - { - "id": 100, - "value": "abc", - }, - { - "id": 200, - "value": "abcd", - }, - { - "id": 300, - "value": "abcde", - }, + {"id": 100, "value": "abc",}, + {"id": 200, "value": "abcd",}, + {"id": 300, "value": "abcde",}, ] - dsub1 = [ - { - "id": 100, - "value": "abc", - } - ] + dsub1 = [{"id": 100, "value": "abc",}] - dsub2 = [ - { - "id": 100, - "value": "abc", - }, - { - "id": 200, - "value": "abcd", - } - ] + dsub2 = [{"id": 100, "value": "abc",}, {"id": 200, "value": "abcd",}] - dsub3 = [ - { - "id": 300, - "value": "abcde", - } - ] + dsub3 = [{"id": 300, "value": "abcde",}] - dsub4 = [ - ] + dsub4 = [] - dsub5 = [ - { - "id": 100, - } - ] + dsub5 = [{"id": 100,}] assert json_cmp(dcomplete, dsub1) is None assert json_cmp(dcomplete, dsub2) is None @@ -393,64 +267,235 @@ def test_json_list_start_failure(): "Test JSON encoded data that starts with a list that should fail." dcomplete = [ - { - "id": 100, - "value": "abc" - }, - { - "id": 200, - "value": "abcd" - }, - { - "id": 300, - "value": "abcde" - }, + {"id": 100, "value": "abc"}, + {"id": 200, "value": "abcd"}, + {"id": 300, "value": "abcde"}, + ] + + dsub1 = [{"id": 100, "value": "abcd",}] + + dsub2 = [{"id": 100, "value": "abc",}, {"id": 200, "value": "abc",}] + + dsub3 = [{"id": 100, "value": "abc",}, {"id": 350, "value": "abcde",}] + + dsub4 = [{"value": "abcx",}, {"id": 300, "value": "abcde",}] + + assert json_cmp(dcomplete, dsub1) is not None + assert json_cmp(dcomplete, dsub2) is not None + assert json_cmp(dcomplete, dsub3) is not None + assert json_cmp(dcomplete, dsub4) is not None + + +def test_json_list_ordered(): + "Test JSON encoded data that should be ordered using the '__ordered__' tag." + + dcomplete = [ + {"id": 1, "value": "abc"}, + "some string", + 123, ] dsub1 = [ - { - "id": 100, - "value": "abcd", - } + '__ordered__', + "some string", + {"id": 1, "value": "abc"}, + 123, + ] + + assert json_cmp(dcomplete, dsub1) is not None + + +def test_json_list_exact_matching(): + "Test JSON array on exact matching using the 'exact' parameter." + + dcomplete = [ + {"id": 1, "value": "abc"}, + "some string", + 123, + [1,2,3], + ] + + dsub1 = [ + "some string", + {"id": 1, "value": "abc"}, + 123, + [1,2,3], ] dsub2 = [ - { - "id": 100, - "value": "abc", - }, - { - "id": 200, - "value": "abc", - } + {"id": 1}, + "some string", + 123, + [1,2,3], + ] + + dsub3 = [ + {"id": 1, "value": "abc"}, + "some string", + 123, + [1,3,2], + ] + + assert json_cmp(dcomplete, dsub1, exact=True) is not None + assert json_cmp(dcomplete, dsub2, exact=True) is not None + + +def test_json_object_exact_matching(): + "Test JSON object on exact matching using the 'exact' parameter." + + dcomplete = { + 'a': {"id": 1, "value": "abc"}, + 'b': "some string", + 'c': 123, + 'd': [1,2,3], + } + + dsub1 = { + 'a': {"id": 1, "value": "abc"}, + 'c': 123, + 'd': [1,2,3], + } + + dsub2 = { + 'a': {"id": 1}, + 'b': "some string", + 'c': 123, + 'd': [1,2,3], + } + + dsub3 = { + 'a': {"id": 1, "value": "abc"}, + 'b': "some string", + 'c': 123, + 'd': [1,3], + } + + assert json_cmp(dcomplete, dsub1, exact=True) is not None + assert json_cmp(dcomplete, dsub2, exact=True) is not None + assert json_cmp(dcomplete, dsub3, exact=True) is not None + + +def test_json_list_asterisk_matching(): + "Test JSON array elements on matching '*' as a placeholder for arbitrary data." + + dcomplete = [ + {"id": 1, "value": "abc"}, + "some string", + 123, + [1,2,3], + ] + + dsub1 = [ + '*', + "some string", + 123, + [1,2,3], + ] + + dsub2 = [ + {"id": '*', "value": "abc"}, + "some string", + 123, + [1,2,3], ] dsub3 = [ + {"id": 1, "value": "abc"}, + "some string", + 123, + [1,'*',3], + ] + + dsub4 = [ + '*', + "some string", + '*', + [1,2,3], + ] + + assert json_cmp(dcomplete, dsub1) is None + assert json_cmp(dcomplete, dsub2) is None + assert json_cmp(dcomplete, dsub3) is None + assert json_cmp(dcomplete, dsub4) is None + + +def test_json_object_asterisk_matching(): + "Test JSON object value elements on matching '*' as a placeholder for arbitrary data." + + dcomplete = { + 'a': {"id": 1, "value": "abc"}, + 'b': "some string", + 'c': 123, + 'd': [1,2,3], + } + + dsub1 = { + 'a': '*', + 'b': "some string", + 'c': 123, + 'd': [1,2,3], + } + + dsub2 = { + 'a': {"id": 1, "value": "abc"}, + 'b': "some string", + 'c': 123, + 'd': [1,'*',3], + } + + dsub3 = { + 'a': {"id": '*', "value": "abc"}, + 'b': "some string", + 'c': 123, + 'd': [1,2,3], + } + + dsub4 = { + 'a': '*', + 'b': "some string", + 'c': '*', + 'd': [1,2,3], + } + + assert json_cmp(dcomplete, dsub1) is None + assert json_cmp(dcomplete, dsub2) is None + assert json_cmp(dcomplete, dsub3) is None + assert json_cmp(dcomplete, dsub4) is None + + +def test_json_list_nested_with_objects(): + + dcomplete = [ { - "id": 100, - "value": "abc", + "key": 1, + "list": [ + 123 + ] }, { - "id": 350, - "value": "abcde", + "key": 2, + "list": [ + 123 + ] } ] - dsub4 = [ + dsub1 = [ { - "value": "abcx", + "key": 2, + "list": [ + 123 + ] }, { - "id": 300, - "value": "abcde", + "key": 1, + "list": [ + 123 + ] } ] - assert json_cmp(dcomplete, dsub1) is not None - assert json_cmp(dcomplete, dsub2) is not None - assert json_cmp(dcomplete, dsub3) is not None - assert json_cmp(dcomplete, dsub4) is not None - + assert json_cmp(dcomplete, dsub1) is None -if __name__ == '__main__': +if __name__ == "__main__": sys.exit(pytest.main()) diff --git a/tests/topotests/lib/test/test_run_and_expect.py b/tests/topotests/lib/test/test_run_and_expect.py new file mode 100755 index 0000000000..d65d5baf37 --- /dev/null +++ b/tests/topotests/lib/test/test_run_and_expect.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python + +# +# test_run_and_expect.py +# Tests for library function: run_and_expect(_type)(). +# +# Copyright (c) 2019 by +# Network Device Education Foundation, Inc. ("NetDEF") +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +Tests for the `run_and_expect(_type)()` functions. +""" + +import os +import sys +import pytest + +# Save the Current Working Directory to find lib files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../../")) + +# pylint: disable=C0413 +from lib.topotest import run_and_expect_type + + +def test_run_and_expect_type(): + "Test basic `run_and_expect_type` functionality." + + def return_true(): + "Test function that returns `True`." + return True + + # Test value success. + success, value = run_and_expect_type( + return_true, bool, count=1, wait=0, avalue=True + ) + assert success is True + assert value is True + + # Test value failure. + success, value = run_and_expect_type( + return_true, bool, count=1, wait=0, avalue=False + ) + assert success is False + assert value is True + + # Test type success. + success, value = run_and_expect_type(return_true, bool, count=1, wait=0) + assert success is True + assert value is True + + # Test type failure. + success, value = run_and_expect_type(return_true, str, count=1, wait=0) + assert success is False + assert value is True + + # Test type failure, return correct type. + success, value = run_and_expect_type(return_true, str, count=1, wait=0, avalue=True) + assert success is False + assert value is True + + +if __name__ == "__main__": + sys.exit(pytest.main()) diff --git a/tests/topotests/lib/test/test_version.py b/tests/topotests/lib/test/test_version.py index 9204ac2084..7c2df00337 100755 --- a/tests/topotests/lib/test/test_version.py +++ b/tests/topotests/lib/test/test_version.py @@ -32,21 +32,22 @@ # Save the Current Working Directory to find lib files. CWD = os.path.dirname(os.path.realpath(__file__)) -sys.path.append(os.path.join(CWD, '../../')) +sys.path.append(os.path.join(CWD, "../../")) # pylint: disable=C0413 from lib.topotest import version_cmp + def test_valid_versions(): "Test valid version compare results" - curver = '3.0' - samever = '3' - oldver = '2.0' - newver = '3.0.1' - newerver = '3.0.11' - vercustom = '3.0-dev' - verysmallinc = '3.0.0.0.0.0.0.1' + curver = "3.0" + samever = "3" + oldver = "2.0" + newver = "3.0.1" + newerver = "3.0.11" + vercustom = "3.0-dev" + verysmallinc = "3.0.0.0.0.0.0.1" assert version_cmp(curver, oldver) == 1 assert version_cmp(curver, newver) == -1 @@ -64,14 +65,15 @@ def test_valid_versions(): assert version_cmp(verysmallinc, verysmallinc) == 0 assert version_cmp(vercustom, verysmallinc) == -1 + def test_invalid_versions(): "Test invalid version strings" - curver = '3.0' - badver1 = '.1' - badver2 = '-1.0' - badver3 = '.' - badver4 = '3.-0.3' + curver = "3.0" + badver1 = ".1" + badver2 = "-1.0" + badver3 = "." + badver4 = "3.-0.3" with pytest.raises(ValueError): assert version_cmp(curver, badver1) @@ -79,9 +81,10 @@ def test_invalid_versions(): assert version_cmp(curver, badver3) assert version_cmp(curver, badver4) + def test_regression_1(): """ Test regression on the following type of comparison: '3.0.2' > '3' Expected result is 1. """ - assert version_cmp('3.0.2', '3') == 1 + assert version_cmp("3.0.2", "3") == 1 diff --git a/tests/topotests/lib/topogen.py b/tests/topotests/lib/topogen.py index 0bc1596eb2..a6cc5280ec 100644 --- a/tests/topotests/lib/topogen.py +++ b/tests/topotests/lib/topogen.py @@ -61,6 +61,7 @@ from lib import topotest from lib.topolog import logger, logger_config +from lib.topotest import set_sysctl CWD = os.path.dirname(os.path.realpath(__file__)) @@ -69,6 +70,7 @@ # all test functions without declaring a test local variable. global_tgen = None + def get_topogen(topo=None): """ Helper function to retrieve Topogen. Must be called with `topo` when called @@ -78,31 +80,34 @@ def get_topogen(topo=None): global_tgen.topo = topo return global_tgen + def set_topogen(tgen): "Helper function to set Topogen" # pylint: disable=W0603 global global_tgen global_tgen = tgen + # # Main class: topology builder # # Topogen configuration defaults tgen_defaults = { - 'verbosity': 'info', - 'frrdir': '/usr/lib/frr', - 'quaggadir': '/usr/lib/quagga', - 'routertype': 'frr', - 'memleak_path': None, + "verbosity": "info", + "frrdir": "/usr/lib/frr", + "quaggadir": "/usr/lib/quagga", + "routertype": "frr", + "memleak_path": None, } + class Topogen(object): "A topology test builder helper." - CONFIG_SECTION = 'topogen' + CONFIG_SECTION = "topogen" - def __init__(self, cls, modname='unnamed'): + def __init__(self, cls, modname="unnamed"): """ Topogen initialization function, takes the following arguments: * `cls`: the topology class that is child of mininet.topo @@ -116,16 +121,16 @@ def __init__(self, cls, modname='unnamed'): self.switchn = 1 self.modname = modname self.errorsd = {} - self.errors = '' + self.errors = "" self.peern = 1 self._init_topo(cls) - logger.info('loading topology: {}'.format(self.modname)) + logger.info("loading topology: {}".format(self.modname)) @staticmethod def _mininet_reset(): "Reset the mininet environment" # Clean up the mininet environment - os.system('mn -c > /dev/null 2>&1') + os.system("mn -c > /dev/null 2>&1") def _init_topo(self, cls): """ @@ -137,10 +142,10 @@ def _init_topo(self, cls): # Test for MPLS Kernel modules available self.hasmpls = False - if not topotest.module_present('mpls-router'): - logger.info('MPLS tests will not run (missing mpls-router kernel module)') - elif not topotest.module_present('mpls-iptunnel'): - logger.info('MPLS tests will not run (missing mpls-iptunnel kernel module)') + if not topotest.module_present("mpls-router"): + logger.info("MPLS tests will not run (missing mpls-router kernel module)") + elif not topotest.module_present("mpls-iptunnel"): + logger.info("MPLS tests will not run (missing mpls-iptunnel kernel module)") else: self.hasmpls = True # Load the default topology configurations @@ -159,7 +164,7 @@ def _load_config(self): topotests. """ self.config = configparser.ConfigParser(tgen_defaults) - pytestini_path = os.path.join(CWD, '../pytest.ini') + pytestini_path = os.path.join(CWD, "../pytest.ini") self.config.read(pytestini_path) def add_router(self, name=None, cls=topotest.Router, **params): @@ -172,15 +177,15 @@ def add_router(self, name=None, cls=topotest.Router, **params): Returns a TopoRouter. """ if name is None: - name = 'r{}'.format(self.routern) + name = "r{}".format(self.routern) if name in self.gears: - raise KeyError('router already exists') + raise KeyError("router already exists") - params['frrdir'] = self.config.get(self.CONFIG_SECTION, 'frrdir') - params['quaggadir'] = self.config.get(self.CONFIG_SECTION, 'quaggadir') - params['memleak_path'] = self.config.get(self.CONFIG_SECTION, 'memleak_path') - if not params.has_key('routertype'): - params['routertype'] = self.config.get(self.CONFIG_SECTION, 'routertype') + params["frrdir"] = self.config.get(self.CONFIG_SECTION, "frrdir") + params["quaggadir"] = self.config.get(self.CONFIG_SECTION, "quaggadir") + params["memleak_path"] = self.config.get(self.CONFIG_SECTION, "memleak_path") + if not params.has_key("routertype"): + params["routertype"] = self.config.get(self.CONFIG_SECTION, "routertype") self.gears[name] = TopoRouter(self, cls, name, **params) self.routern += 1 @@ -194,9 +199,9 @@ def add_switch(self, name=None, cls=topotest.LegacySwitch): Returns the switch name and number. """ if name is None: - name = 's{}'.format(self.switchn) + name = "s{}".format(self.switchn) if name in self.gears: - raise KeyError('switch already exists') + raise KeyError("switch already exists") self.gears[name] = TopoSwitch(self, cls, name) self.switchn += 1 @@ -210,9 +215,9 @@ def add_exabgp_peer(self, name, ip, defaultRoute): * `defaultRoute`: the peer default route (e.g. 'via 1.2.3.1') """ if name is None: - name = 'peer{}'.format(self.peern) + name = "peer{}".format(self.peern) if name in self.gears: - raise KeyError('exabgp peer already exists') + raise KeyError("exabgp peer already exists") self.gears[name] = TopoExaBGP(self, name, ip=ip, defaultRoute=defaultRoute) self.peern += 1 @@ -227,9 +232,9 @@ def add_link(self, node1, node2, ifname1=None, ifname2=None): * TopoSwitch """ if not isinstance(node1, TopoGear): - raise ValueError('invalid node1 type') + raise ValueError("invalid node1 type") if not isinstance(node2, TopoGear): - raise ValueError('invalid node2 type') + raise ValueError("invalid node2 type") if ifname1 is None: ifname1 = node1.new_link() @@ -238,8 +243,7 @@ def add_link(self, node1, node2, ifname1=None, ifname2=None): node1.register_link(ifname1, node2, ifname2) node2.register_link(ifname2, node1, ifname1) - self.topo.addLink(node1.name, node2.name, - intfName1=ifname1, intfName2=ifname2) + self.topo.addLink(node1.name, node2.name, intfName1=ifname1, intfName2=ifname2) def get_gears(self, geartype): """ @@ -261,8 +265,11 @@ def get_gears(self, geartype): # Do stuff ``` """ - return dict((name, gear) for name, gear in self.gears.iteritems() - if isinstance(gear, geartype)) + return dict( + (name, gear) + for name, gear in self.gears.iteritems() + if isinstance(gear, geartype) + ) def routers(self): """ @@ -290,16 +297,16 @@ def start_topology(self, log_level=None): """ # If log_level is not specified use the configuration. if log_level is None: - log_level = self.config.get(self.CONFIG_SECTION, 'verbosity') + log_level = self.config.get(self.CONFIG_SECTION, "verbosity") # Set python logger level logger_config.set_log_level(log_level) # Run mininet - if log_level == 'debug': + if log_level == "debug": setLogLevel(log_level) - logger.info('starting topology: {}'.format(self.modname)) + logger.info("starting topology: {}".format(self.modname)) self.net.start() def start_router(self, router=None): @@ -325,12 +332,10 @@ def stop_topology(self): first is a simple kill with no sleep, the second will sleep if not killed and try with a different signal. """ - logger.info('stopping topology: {}'.format(self.modname)) + logger.info("stopping topology: {}".format(self.modname)) errors = "" for gear in self.gears.values(): - gear.stop(False, False) - for gear in self.gears.values(): - errors += gear.stop(True, False) + errors += gear.stop() if len(errors) > 0: assert "Errors found post shutdown - details follow:" == 0, errors @@ -343,7 +348,8 @@ def mininet_cli(self): """ if not sys.stdin.isatty(): raise EnvironmentError( - 'you must run pytest with \'-s\' in order to use mininet CLI') + "you must run pytest with '-s' in order to use mininet CLI" + ) CLI(self.net) @@ -353,8 +359,9 @@ def is_memleak_enabled(self): if self.routers_have_failure(): return False - memleak_file = (os.environ.get('TOPOTESTS_CHECK_MEMLEAK') or - self.config.get(self.CONFIG_SECTION, 'memleak_path')) + memleak_file = os.environ.get("TOPOTESTS_CHECK_MEMLEAK") or self.config.get( + self.CONFIG_SECTION, "memleak_path" + ) if memleak_file is None: return False return True @@ -381,7 +388,7 @@ def set_error(self, message, code=None): code = len(self.errorsd) self.errorsd[code] = message - self.errors += '\n{}: {}'.format(code, message) + self.errors += "\n{}: {}".format(code, message) def has_errors(self): "Returns whether errors exist or not." @@ -392,23 +399,25 @@ def routers_have_failure(self): if self.has_errors(): return True - errors = '' + errors = "" router_list = self.routers().values() for router in router_list: result = router.check_router_running() - if result != '': - errors += result + '\n' + if result != "": + errors += result + "\n" - if errors != '': - self.set_error(errors, 'router_error') + if errors != "": + self.set_error(errors, "router_error") assert False, errors return True return False + # # Topology gears (equipment) # + class TopoGear(object): "Abstract class for type checking" @@ -420,11 +429,11 @@ def __init__(self): self.linkn = 0 def __str__(self): - links = '' + links = "" for myif, dest in self.links.iteritems(): _, destif = dest - if links != '': - links += ',' + if links != "": + links += "," links += '"{}"<->"{}"'.format(myif, destif) return 'TopoGear'.format(self.name, links) @@ -461,20 +470,22 @@ def link_enable(self, myif, enabled=True, netns=None): enabled: whether we should enable or disable the interface """ if myif not in self.links.keys(): - raise KeyError('interface doesn\'t exists') + raise KeyError("interface doesn't exists") if enabled is True: - operation = 'up' + operation = "up" else: - operation = 'down' + operation = "down" - logger.info('setting node "{}" link "{}" to state "{}"'.format( - self.name, myif, operation - )) - extract='' + logger.info( + 'setting node "{}" link "{}" to state "{}"'.format( + self.name, myif, operation + ) + ) + extract = "" if netns is not None: - extract = 'ip netns exec {} '.format(netns) - return self.run('{}ip link set dev {} {}'.format(extract, myif, operation)) + extract = "ip netns exec {} ".format(netns) + return self.run("{}ip link set dev {} {}".format(extract, myif, operation)) def peer_link_enable(self, myif, enabled=True, netns=None): """ @@ -486,7 +497,7 @@ def peer_link_enable(self, myif, enabled=True, netns=None): peer disables their interface our interface status changes to no link. """ if myif not in self.links.keys(): - raise KeyError('interface doesn\'t exists') + raise KeyError("interface doesn't exists") node, nodeif = self.links[myif] node.link_enable(nodeif, enabled, netns) @@ -497,7 +508,7 @@ def new_link(self): NOTE: This function should only be called by Topogen. """ - ifname = '{}-eth{}'.format(self.name, self.linkn) + ifname = "{}-eth{}".format(self.name, self.linkn) self.linkn += 1 return ifname @@ -508,10 +519,11 @@ def register_link(self, myif, node, nodeif): NOTE: This function should only be called by Topogen. """ if myif in self.links.keys(): - raise KeyError('interface already exists') + raise KeyError("interface already exists") self.links[myif] = (node, nodeif) + class TopoRouter(TopoGear): """ Router abstraction. @@ -519,11 +531,11 @@ class TopoRouter(TopoGear): # The default required directories by Quagga/FRR PRIVATE_DIRS = [ - '/etc/frr', - '/etc/quagga', - '/var/run/frr', - '/var/run/quagga', - '/var/log' + "/etc/frr", + "/etc/quagga", + "/var/run/frr", + "/var/run/quagga", + "/var/log", ] # Router Daemon enumeration definition. @@ -540,20 +552,26 @@ class TopoRouter(TopoGear): RD_NHRP = 11 RD_STATIC = 12 RD_BFD = 13 + RD_SHARP = 14 + RD_BABEL = 15 + RD_PBRD = 16 RD = { - RD_ZEBRA: 'zebra', - RD_RIP: 'ripd', - RD_RIPNG: 'ripngd', - RD_OSPF: 'ospfd', - RD_OSPF6: 'ospf6d', - RD_ISIS: 'isisd', - RD_BGP: 'bgpd', - RD_PIM: 'pimd', - RD_LDP: 'ldpd', - RD_EIGRP: 'eigrpd', - RD_NHRP: 'nhrpd', - RD_STATIC: 'staticd', - RD_BFD: 'bfdd', + RD_ZEBRA: "zebra", + RD_RIP: "ripd", + RD_RIPNG: "ripngd", + RD_OSPF: "ospfd", + RD_OSPF6: "ospf6d", + RD_ISIS: "isisd", + RD_BGP: "bgpd", + RD_PIM: "pimd", + RD_LDP: "ldpd", + RD_EIGRP: "eigrpd", + RD_NHRP: "nhrpd", + RD_STATIC: "staticd", + RD_BFD: "bfdd", + RD_SHARP: "sharpd", + RD_BABEL: "babeld", + RD_PBRD: "pbrd", } def __init__(self, tgen, cls, name, **params): @@ -571,34 +589,34 @@ def __init__(self, tgen, cls, name, **params): self.name = name self.cls = cls self.options = {} - self.routertype = params.get('routertype', 'frr') - if not params.has_key('privateDirs'): - params['privateDirs'] = self.PRIVATE_DIRS + self.routertype = params.get("routertype", "frr") + if not params.has_key("privateDirs"): + params["privateDirs"] = self.PRIVATE_DIRS - self.options['memleak_path'] = params.get('memleak_path', None) + self.options["memleak_path"] = params.get("memleak_path", None) # Create new log directory - self.logdir = '/tmp/topotests/{}'.format(self.tgen.modname) + self.logdir = "/tmp/topotests/{}".format(self.tgen.modname) # Clean up before starting new log files: avoids removing just created # log files. self._prepare_tmpfiles() # Propagate the router log directory - params['logdir'] = self.logdir + params["logdir"] = self.logdir - #setup the per node directory - dir = '{}/{}'.format(self.logdir, self.name) - os.system('mkdir -p ' + dir) - os.system('chmod -R go+rw /tmp/topotests') + # setup the per node directory + dir = "{}/{}".format(self.logdir, self.name) + os.system("mkdir -p " + dir) + os.system("chmod -R go+rw /tmp/topotests") # Open router log file - logfile = '{0}/{1}.log'.format(self.logdir, name) + logfile = "{0}/{1}.log".format(self.logdir, name) self.logger = logger_config.get_logger(name=name, target=logfile) self.tgen.topo.addNode(self.name, cls=self.cls, **params) def __str__(self): gear = super(TopoRouter, self).__str__() - gear += ' TopoRouter<>' + gear += " TopoRouter<>" return gear def _prepare_tmpfiles(self): @@ -619,9 +637,9 @@ def _prepare_tmpfiles(self): os.chmod(self.logdir, 0o1777) # Try to find relevant old logfiles in /tmp and delete them - map(os.remove, glob.glob('{}/{}/*.log'.format(self.logdir, self.name))) + map(os.remove, glob.glob("{}/{}/*.log".format(self.logdir, self.name))) # Remove old core files - map(os.remove, glob.glob('{}/{}/*.dmp'.format(self.logdir, self.name))) + map(os.remove, glob.glob("{}/{}/*.dmp".format(self.logdir, self.name))) def check_capability(self, daemon, param): """ @@ -638,7 +656,7 @@ def load_config(self, daemon, source=None, param=None): Possible daemon values are: TopoRouter.RD_ZEBRA, TopoRouter.RD_RIP, TopoRouter.RD_RIPNG, TopoRouter.RD_OSPF, TopoRouter.RD_OSPF6, TopoRouter.RD_ISIS, TopoRouter.RD_BGP, TopoRouter.RD_LDP, - TopoRouter.RD_PIM. + TopoRouter.RD_PIM, TopoRouter.RD_PBR. """ daemonstr = self.RD.get(daemon) self.logger.info('loading "{}" configuration: {}'.format(daemonstr, source)) @@ -648,7 +666,7 @@ def check_router_running(self): """ Run a series of checks and returns a status string. """ - self.logger.info('checking if daemons are running') + self.logger.info("checking if daemons are running") return self.tgen.net[self.name].checkRouterRunning() def start(self): @@ -660,7 +678,7 @@ def start(self): * Start daemons (e.g. FRR/Quagga) * Configure daemon logging files """ - self.logger.debug('starting') + self.logger.debug("starting") nrouter = self.tgen.net[self.name] result = nrouter.startRouter(self.tgen) @@ -669,22 +687,72 @@ def start(self): for daemon, enabled in nrouter.daemons.iteritems(): if enabled == 0: continue - self.vtysh_cmd('configure terminal\nlog commands\nlog file {}.log'.format( - daemon), daemon=daemon) + self.vtysh_cmd( + "configure terminal\nlog commands\nlog file {}.log".format(daemon), + daemon=daemon, + ) - if result != '': + if result != "": self.tgen.set_error(result) + else: + # Enable MPLS processing on all interfaces. + for interface in self.links.keys(): + set_sysctl(nrouter, "net.mpls.conf.{}.input".format(interface), 1) return result - def stop(self, wait=True, assertOnError=True): + def __stop_internal(self, wait=True, assertOnError=True): """ - Stop router: + Stop router, private internal version * Kill daemons """ - self.logger.debug('stopping') + self.logger.debug("stopping: wait {}, assert {}".format( + wait, assertOnError)) return self.tgen.net[self.name].stopRouter(wait, assertOnError) + + def stop(self): + """ + Stop router cleanly: + * Signal daemons twice, once without waiting, and then a second time + with a wait to ensure the daemons exit cleanly + """ + self.logger.debug("stopping") + self.__stop_internal(False, False) + return self.__stop_internal() + + def startDaemons(self, daemons): + """ + Start Daemons: to start specific daemon(user defined daemon only) + * Start daemons (e.g. FRR/Quagga) + * Configure daemon logging files + """ + self.logger.debug('starting') + nrouter = self.tgen.net[self.name] + result = nrouter.startRouterDaemons(daemons) + + # Enable all daemon command logging, logging files + # and set them to the start dir. + for daemon, enabled in nrouter.daemons.iteritems(): + for d in daemons: + if enabled == 0: + continue + self.vtysh_cmd('configure terminal\nlog commands\nlog file {}.log'.\ + format(daemon), daemon=daemon) + + if result != '': + self.tgen.set_error(result) + + return result + + def killDaemons(self, daemons, wait=True, assertOnError=True): + """ + Kill specific daemon(user defined daemon only) + forcefully using SIGKILL + """ + self.logger.debug('Killing daemons using SIGKILL..') + return self.tgen.net[self.name].killRouterDaemons(daemons, wait, assertOnError) + def vtysh_cmd(self, command, isjson=False, daemon=None): """ Runs the provided command string in the vty shell and returns a string @@ -694,25 +762,26 @@ def vtysh_cmd(self, command, isjson=False, daemon=None): return output for each command. See vtysh_multicmd() for more details. """ # Detect multi line commands - if command.find('\n') != -1: + if command.find("\n") != -1: return self.vtysh_multicmd(command, daemon=daemon) - dparam = '' + dparam = "" if daemon is not None: - dparam += '-d {}'.format(daemon) + dparam += "-d {}".format(daemon) vtysh_command = 'vtysh {} -c "{}" 2>/dev/null'.format(dparam, command) output = self.run(vtysh_command) - self.logger.info('\nvtysh command => {}\nvtysh output <= {}'.format( - command, output)) + self.logger.info( + "\nvtysh command => {}\nvtysh output <= {}".format(command, output) + ) if isjson is False: return output try: return json.loads(output) except ValueError: - logger.warning('vtysh_cmd: failed to convert json output') + logger.warning("vtysh_cmd: failed to convert json output") return {} def vtysh_multicmd(self, commands, pretty_output=True, daemon=None): @@ -727,21 +796,22 @@ def vtysh_multicmd(self, commands, pretty_output=True, daemon=None): # Prepare the temporary file that will hold the commands fname = topotest.get_file(commands) - dparam = '' + dparam = "" if daemon is not None: - dparam += '-d {}'.format(daemon) + dparam += "-d {}".format(daemon) # Run the commands and delete the temporary file if pretty_output: - vtysh_command = 'vtysh {} < {}'.format(dparam, fname) + vtysh_command = "vtysh {} < {}".format(dparam, fname) else: - vtysh_command = 'vtysh {} -f {}'.format(dparam, fname) + vtysh_command = "vtysh {} -f {}".format(dparam, fname) res = self.run(vtysh_command) os.unlink(fname) - self.logger.info('\nvtysh command => "{}"\nvtysh output <= "{}"'.format( - vtysh_command, res)) + self.logger.info( + '\nvtysh command => "{}"\nvtysh output <= "{}"'.format(vtysh_command, res) + ) return res @@ -753,27 +823,30 @@ def report_memory_leaks(self, testname): NOTE: to run this you must have the environment variable TOPOTESTS_CHECK_MEMLEAK set or memleak_path configured in `pytest.ini`. """ - memleak_file = os.environ.get('TOPOTESTS_CHECK_MEMLEAK') or self.options['memleak_path'] + memleak_file = ( + os.environ.get("TOPOTESTS_CHECK_MEMLEAK") or self.options["memleak_path"] + ) if memleak_file is None: return self.stop() - self.logger.info('running memory leak report') + + self.logger.info("running memory leak report") self.tgen.net[self.name].report_memory_leaks(memleak_file, testname) def version_info(self): "Get equipment information from 'show version'." - output = self.vtysh_cmd('show version').split('\n')[0] - columns = topotest.normalize_text(output).split(' ') + output = self.vtysh_cmd("show version").split("\n")[0] + columns = topotest.normalize_text(output).split(" ") try: return { - 'type': columns[0], - 'version': columns[1], + "type": columns[0], + "version": columns[1], } except IndexError: return { - 'type': None, - 'version': None, + "type": None, + "version": None, } def has_version(self, cmpop, version): @@ -795,19 +868,21 @@ def has_type(self, rtype): Compares router type with `rtype`. Returns `True` if the type matches, otherwise `false`. """ - curtype = self.version_info()['type'] + curtype = self.version_info()["type"] return rtype == curtype def has_mpls(self): nrouter = self.tgen.net[self.name] return nrouter.hasmpls + class TopoSwitch(TopoGear): """ Switch abstraction. Has the following properties: * cls: switch class that will be used to instantiate * name: switch name """ + # pylint: disable=too-few-public-methods def __init__(self, tgen, cls, name): @@ -820,9 +895,10 @@ def __init__(self, tgen, cls, name): def __str__(self): gear = super(TopoSwitch, self).__str__() - gear += ' TopoSwitch<>' + gear += " TopoSwitch<>" return gear + class TopoHost(TopoGear): "Host abstraction." # pylint: disable=too-few-public-methods @@ -846,18 +922,21 @@ def __init__(self, tgen, name, **params): def __str__(self): gear = super(TopoHost, self).__str__() gear += ' TopoHost'.format( - self.options['ip'], self.options['defaultRoute'], - str(self.options['privateDirs'])) + self.options["ip"], + self.options["defaultRoute"], + str(self.options["privateDirs"]), + ) return gear + class TopoExaBGP(TopoHost): "ExaBGP peer abstraction." # pylint: disable=too-few-public-methods PRIVATE_DIRS = [ - '/etc/exabgp', - '/var/run/exabgp', - '/var/log', + "/etc/exabgp", + "/var/run/exabgp", + "/var/log", ] def __init__(self, tgen, name, **params): @@ -871,13 +950,13 @@ def __init__(self, tgen, name, **params): has a privateDirs already defined and contains functions to handle ExaBGP things. """ - params['privateDirs'] = self.PRIVATE_DIRS + params["privateDirs"] = self.PRIVATE_DIRS super(TopoExaBGP, self).__init__(tgen, name, **params) self.tgen.topo.addHost(name, **params) def __str__(self): gear = super(TopoExaBGP, self).__str__() - gear += ' TopoExaBGP<>'.format() + gear += " TopoExaBGP<>".format() return gear def start(self, peer_dir, env_file=None): @@ -888,22 +967,22 @@ def start(self, peer_dir, env_file=None): * Make all python files runnable * Run ExaBGP with env file `env_file` and configuration peer*/exabgp.cfg """ - self.run('mkdir /etc/exabgp') - self.run('chmod 755 /etc/exabgp') - self.run('cp {}/* /etc/exabgp/'.format(peer_dir)) + self.run("mkdir /etc/exabgp") + self.run("chmod 755 /etc/exabgp") + self.run("cp {}/* /etc/exabgp/".format(peer_dir)) if env_file is not None: - self.run('cp {} /etc/exabgp/exabgp.env'.format(env_file)) - self.run('chmod 644 /etc/exabgp/*') - self.run('chmod a+x /etc/exabgp/*.py') - self.run('chown -R exabgp:exabgp /etc/exabgp') - output = self.run('exabgp -e /etc/exabgp/exabgp.env /etc/exabgp/exabgp.cfg') + self.run("cp {} /etc/exabgp/exabgp.env".format(env_file)) + self.run("chmod 644 /etc/exabgp/*") + self.run("chmod a+x /etc/exabgp/*.py") + self.run("chown -R exabgp:exabgp /etc/exabgp") + output = self.run("exabgp -e /etc/exabgp/exabgp.env /etc/exabgp/exabgp.cfg") if output == None or len(output) == 0: - output = '' - logger.info('{} exabgp started, output={}'.format(self.name, output)) + output = "" + logger.info("{} exabgp started, output={}".format(self.name, output)) def stop(self, wait=True, assertOnError=True): "Stop ExaBGP peer and kill the daemon" - self.run('kill `cat /var/run/exabgp/exabgp.pid`') + self.run("kill `cat /var/run/exabgp/exabgp.pid`") return "" @@ -921,160 +1000,191 @@ def diagnose_env_linux(): ret = True # Test log path exists before installing handler. - if not os.path.isdir('/tmp'): - logger.warning('could not find /tmp for logs') + if not os.path.isdir("/tmp"): + logger.warning("could not find /tmp for logs") else: - os.system('mkdir /tmp/topotests') + os.system("mkdir /tmp/topotests") # Log diagnostics to file so it can be examined later. - fhandler = logging.FileHandler(filename='/tmp/topotests/diagnostics.txt') + fhandler = logging.FileHandler(filename="/tmp/topotests/diagnostics.txt") fhandler.setLevel(logging.DEBUG) fhandler.setFormatter( - logging.Formatter(fmt='%(asctime)s %(levelname)s: %(message)s') + logging.Formatter(fmt="%(asctime)s %(levelname)s: %(message)s") ) logger.addHandler(fhandler) - logger.info('Running environment diagnostics') + logger.info("Running environment diagnostics") # Load configuration config = configparser.ConfigParser(tgen_defaults) - pytestini_path = os.path.join(CWD, '../pytest.ini') + pytestini_path = os.path.join(CWD, "../pytest.ini") config.read(pytestini_path) # Assert that we are running as root if os.getuid() != 0: - logger.error('you must run topotest as root') + logger.error("you must run topotest as root") ret = False # Assert that we have mininet - if os.system('which mn >/dev/null 2>/dev/null') != 0: - logger.error('could not find mininet binary (mininet is not installed)') + if os.system("which mn >/dev/null 2>/dev/null") != 0: + logger.error("could not find mininet binary (mininet is not installed)") ret = False # Assert that we have iproute installed - if os.system('which ip >/dev/null 2>/dev/null') != 0: - logger.error('could not find ip binary (iproute is not installed)') + if os.system("which ip >/dev/null 2>/dev/null") != 0: + logger.error("could not find ip binary (iproute is not installed)") ret = False # Assert that we have gdb installed - if os.system('which gdb >/dev/null 2>/dev/null') != 0: - logger.error('could not find gdb binary (gdb is not installed)') + if os.system("which gdb >/dev/null 2>/dev/null") != 0: + logger.error("could not find gdb binary (gdb is not installed)") ret = False # Assert that FRR utilities exist - frrdir = config.get('topogen', 'frrdir') + frrdir = config.get("topogen", "frrdir") hasfrr = False if not os.path.isdir(frrdir): - logger.error('could not find {} directory'.format(frrdir)) + logger.error("could not find {} directory".format(frrdir)) ret = False else: hasfrr = True try: - pwd.getpwnam('frr')[2] + pwd.getpwnam("frr")[2] except KeyError: logger.warning('could not find "frr" user') try: - grp.getgrnam('frr')[2] + grp.getgrnam("frr")[2] except KeyError: logger.warning('could not find "frr" group') try: - if 'frr' not in grp.getgrnam('frrvty').gr_mem: - logger.error('"frr" user and group exist, but user is not under "frrvty"') + if "frr" not in grp.getgrnam("frrvty").gr_mem: + logger.error( + '"frr" user and group exist, but user is not under "frrvty"' + ) except KeyError: logger.warning('could not find "frrvty" group') - for fname in ['zebra', 'ospfd', 'ospf6d', 'bgpd', 'ripd', 'ripngd', - 'isisd', 'pimd', 'ldpd']: + for fname in [ + "zebra", + "ospfd", + "ospf6d", + "bgpd", + "ripd", + "ripngd", + "isisd", + "pimd", + "ldpd", + "pbrd" + ]: path = os.path.join(frrdir, fname) if not os.path.isfile(path): # LDPd is an exception - if fname == 'ldpd': - logger.info('could not find {} in {}'.format(fname, frrdir) + - '(LDPd tests will not run)') + if fname == "ldpd": + logger.info( + "could not find {} in {}".format(fname, frrdir) + + "(LDPd tests will not run)" + ) continue - logger.warning('could not find {} in {}'.format(fname, frrdir)) + logger.warning("could not find {} in {}".format(fname, frrdir)) ret = False else: - if fname != 'zebra': + if fname != "zebra": continue - os.system( - '{} -v 2>&1 >/tmp/topotests/frr_zebra.txt'.format(path) - ) + os.system("{} -v 2>&1 >/tmp/topotests/frr_zebra.txt".format(path)) # Assert that Quagga utilities exist - quaggadir = config.get('topogen', 'quaggadir') + quaggadir = config.get("topogen", "quaggadir") if hasfrr: # if we have frr, don't check for quagga pass elif not os.path.isdir(quaggadir): - logger.info('could not find {} directory (quagga tests will not run)'.format(quaggadir)) + logger.info( + "could not find {} directory (quagga tests will not run)".format(quaggadir) + ) else: ret = True try: - pwd.getpwnam('quagga')[2] + pwd.getpwnam("quagga")[2] except KeyError: logger.info('could not find "quagga" user') try: - grp.getgrnam('quagga')[2] + grp.getgrnam("quagga")[2] except KeyError: logger.info('could not find "quagga" group') try: - if 'quagga' not in grp.getgrnam('quaggavty').gr_mem: - logger.error('"quagga" user and group exist, but user is not under "quaggavty"') + if "quagga" not in grp.getgrnam("quaggavty").gr_mem: + logger.error( + '"quagga" user and group exist, but user is not under "quaggavty"' + ) except KeyError: logger.warning('could not find "quaggavty" group') - for fname in ['zebra', 'ospfd', 'ospf6d', 'bgpd', 'ripd', 'ripngd', - 'isisd', 'pimd']: + for fname in [ + "zebra", + "ospfd", + "ospf6d", + "bgpd", + "ripd", + "ripngd", + "isisd", + "pimd", + "pbrd" + ]: path = os.path.join(quaggadir, fname) if not os.path.isfile(path): - logger.warning('could not find {} in {}'.format(fname, quaggadir)) + logger.warning("could not find {} in {}".format(fname, quaggadir)) ret = False else: - if fname != 'zebra': + if fname != "zebra": continue - os.system( - '{} -v 2>&1 >/tmp/topotests/quagga_zebra.txt'.format(path) - ) + os.system("{} -v 2>&1 >/tmp/topotests/quagga_zebra.txt".format(path)) # Test MPLS availability krel = platform.release() - if topotest.version_cmp(krel, '4.5') < 0: - logger.info('LDPd tests will not run (have kernel "{}", but it requires 4.5)'.format(krel)) + if topotest.version_cmp(krel, "4.5") < 0: + logger.info( + 'LDPd tests will not run (have kernel "{}", but it requires 4.5)'.format( + krel + ) + ) # Test for MPLS Kernel modules available - if not topotest.module_present('mpls-router', load=False) != 0: - logger.info('LDPd tests will not run (missing mpls-router kernel module)') - if not topotest.module_present('mpls-iptunnel', load=False) != 0: - logger.info('LDPd tests will not run (missing mpls-iptunnel kernel module)') + if not topotest.module_present("mpls-router", load=False) != 0: + logger.info("LDPd tests will not run (missing mpls-router kernel module)") + if not topotest.module_present("mpls-iptunnel", load=False) != 0: + logger.info("LDPd tests will not run (missing mpls-iptunnel kernel module)") # 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: - logger.warning('BGP topologies are still using exabgp version 3, expect failures') + output = subprocess.check_output(["exabgp", "-v"]) + line = output.split("\n")[0] + version = line.split(" ")[2] + if topotest.version_cmp(version, "4") >= 0: + logger.warning( + "BGP topologies are still using exabgp version 3, expect failures" + ) # We want to catch all exceptions # pylint: disable=W0702 except: - logger.warning('failed to find exabgp or returned error') + logger.warning("failed to find exabgp or returned error") # After we logged the output to file, remove the handler. logger.removeHandler(fhandler) return ret + def diagnose_env_freebsd(): return True + def diagnose_env(): if sys.platform.startswith("linux"): return diagnose_env_linux() diff --git a/tests/topotests/lib/topojson.py b/tests/topotests/lib/topojson.py new file mode 100644 index 0000000000..9c2baedde4 --- /dev/null +++ b/tests/topotests/lib/topojson.py @@ -0,0 +1,228 @@ +# +# Modified work Copyright (c) 2019 by VMware, Inc. ("VMware") +# Original work Copyright (c) 2018 by Network Device Education +# Foundation, Inc. ("NetDEF") +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +from collections import OrderedDict +from json import dumps as json_dumps +from re import search as re_search +import ipaddress +import pytest + +# Import topogen and topotest helpers +from lib.topolog import logger + +# Required to instantiate the topology builder class. +from lib.common_config import ( + number_to_row, + number_to_column, + load_config_to_router, + create_interfaces_cfg, + create_static_routes, + create_prefix_lists, + create_route_maps, + create_bgp_community_lists, + create_vrf_cfg, +) + +from lib.bgp import create_router_bgp + +ROUTER_LIST = [] + + +def build_topo_from_json(tgen, topo): + """ + Reads configuration from JSON file. Adds routers, creates interface + names dynamically and link routers as defined in JSON to create + topology. Assigns IPs dynamically to all interfaces of each router. + * `tgen`: Topogen object + * `topo`: json file data + """ + + ROUTER_LIST = sorted( + topo["routers"].keys(), key=lambda x: int(re_search("\d+", x).group(0)) + ) + + listRouters = ROUTER_LIST[:] + for routerN in ROUTER_LIST: + logger.info("Topo: Add router {}".format(routerN)) + tgen.add_router(routerN) + listRouters.append(routerN) + + if "ipv4base" in topo: + ipv4Next = ipaddress.IPv4Address(topo["link_ip_start"]["ipv4"]) + ipv4Step = 2 ** (32 - topo["link_ip_start"]["v4mask"]) + if topo["link_ip_start"]["v4mask"] < 32: + ipv4Next += 1 + if "ipv6base" in topo: + ipv6Next = ipaddress.IPv6Address(topo["link_ip_start"]["ipv6"]) + ipv6Step = 2 ** (128 - topo["link_ip_start"]["v6mask"]) + if topo["link_ip_start"]["v6mask"] < 127: + ipv6Next += 1 + for router in listRouters: + topo["routers"][router]["nextIfname"] = 0 + + while listRouters != []: + curRouter = listRouters.pop(0) + # Physical Interfaces + if "links" in topo["routers"][curRouter]: + + def link_sort(x): + if x == "lo": + return 0 + elif "link" in x: + return int(x.split("-link")[1]) + else: + return int(re_search("\d+", x).group(0)) + + for destRouterLink, data in sorted( + topo["routers"][curRouter]["links"].iteritems(), + key=lambda x: link_sort(x[0]), + ): + currRouter_lo_json = topo["routers"][curRouter]["links"][destRouterLink] + # Loopback interfaces + if "type" in data and data["type"] == "loopback": + if ( + "ipv4" in currRouter_lo_json + and currRouter_lo_json["ipv4"] == "auto" + ): + currRouter_lo_json["ipv4"] = "{}{}.{}/{}".format( + topo["lo_prefix"]["ipv4"], + number_to_row(curRouter), + number_to_column(curRouter), + topo["lo_prefix"]["v4mask"], + ) + if ( + "ipv6" in currRouter_lo_json + and currRouter_lo_json["ipv6"] == "auto" + ): + currRouter_lo_json["ipv6"] = "{}{}:{}/{}".format( + topo["lo_prefix"]["ipv6"], + number_to_row(curRouter), + number_to_column(curRouter), + topo["lo_prefix"]["v6mask"], + ) + + if "-" in destRouterLink: + # Spliting and storing destRouterLink data in tempList + tempList = destRouterLink.split("-") + + # destRouter + destRouter = tempList.pop(0) + + # Current Router Link + tempList.insert(0, curRouter) + curRouterLink = "-".join(tempList) + else: + destRouter = destRouterLink + curRouterLink = curRouter + + if destRouter in listRouters: + currRouter_link_json = topo["routers"][curRouter]["links"][ + destRouterLink + ] + destRouter_link_json = topo["routers"][destRouter]["links"][ + curRouterLink + ] + + # Assigning name to interfaces + currRouter_link_json["interface"] = "{}-{}-eth{}".format( + curRouter, destRouter, topo["routers"][curRouter]["nextIfname"] + ) + destRouter_link_json["interface"] = "{}-{}-eth{}".format( + destRouter, curRouter, topo["routers"][destRouter]["nextIfname"] + ) + + topo["routers"][curRouter]["nextIfname"] += 1 + topo["routers"][destRouter]["nextIfname"] += 1 + + # Linking routers to each other as defined in JSON file + tgen.gears[curRouter].add_link( + tgen.gears[destRouter], + topo["routers"][curRouter]["links"][destRouterLink][ + "interface" + ], + topo["routers"][destRouter]["links"][curRouterLink][ + "interface" + ], + ) + + # IPv4 + if "ipv4" in currRouter_link_json: + if currRouter_link_json["ipv4"] == "auto": + currRouter_link_json["ipv4"] = "{}/{}".format( + ipv4Next, topo["link_ip_start"]["v4mask"] + ) + destRouter_link_json["ipv4"] = "{}/{}".format( + ipv4Next + 1, topo["link_ip_start"]["v4mask"] + ) + ipv4Next += ipv4Step + # IPv6 + if "ipv6" in currRouter_link_json: + if currRouter_link_json["ipv6"] == "auto": + currRouter_link_json["ipv6"] = "{}/{}".format( + ipv6Next, topo["link_ip_start"]["v6mask"] + ) + destRouter_link_json["ipv6"] = "{}/{}".format( + ipv6Next + 1, topo["link_ip_start"]["v6mask"] + ) + ipv6Next = ipaddress.IPv6Address(int(ipv6Next) + ipv6Step) + + logger.debug( + "Generated link data for router: %s\n%s", + curRouter, + json_dumps( + topo["routers"][curRouter]["links"], indent=4, sort_keys=True + ), + ) + + +def build_config_from_json(tgen, topo, save_bkup=True): + """ + Reads initial configuraiton from JSON for each router, builds + configuration and loads its to router. + + * `tgen`: Topogen object + * `topo`: json file data + """ + + func_dict = OrderedDict( + [ + ("vrfs", create_vrf_cfg), + ("links", create_interfaces_cfg), + ("static_routes", create_static_routes), + ("prefix_lists", create_prefix_lists), + ("bgp_community_list", create_bgp_community_lists), + ("route_maps", create_route_maps), + ("bgp", create_router_bgp), + ] + ) + + data = topo["routers"] + for func_type in func_dict.keys(): + logger.info("Checking for {} configuration in input data".format(func_type)) + + func_dict.get(func_type)(tgen, data, build=True) + + for router in sorted(topo["routers"].keys()): + logger.debug("Configuring router {}...".format(router)) + + result = load_config_to_router(tgen, router, save_bkup) + if not result: + logger.info("Failed while configuring {}".format(router)) + pytest.exit(1) diff --git a/tests/topotests/lib/topolog.py b/tests/topotests/lib/topolog.py index f149f34eb3..0dfa870930 100644 --- a/tests/topotests/lib/topolog.py +++ b/tests/topotests/lib/topolog.py @@ -31,22 +31,25 @@ # Helper dictionary to convert Topogen logging levels to Python's logging. DEBUG_TOPO2LOGGING = { - 'debug': logging.DEBUG, - 'info': logging.INFO, - 'output': logging.INFO, - 'warning': logging.WARNING, - 'error': logging.ERROR, - 'critical': logging.CRITICAL, + "debug": logging.DEBUG, + "info": logging.INFO, + "output": logging.INFO, + "warning": logging.WARNING, + "error": logging.ERROR, + "critical": logging.CRITICAL, } + class InfoFilter(logging.Filter): def filter(self, rec): return rec.levelno in (logging.DEBUG, logging.INFO) + # # Logger class definition # + class Logger(object): """ Logger class that encapsulates logging functions, internaly it uses Python @@ -58,32 +61,32 @@ class Logger(object): def __init__(self): # Create default global logger self.log_level = logging.INFO - self.logger = logging.Logger('topolog', level=self.log_level) + self.logger = logging.Logger("topolog", level=self.log_level) handler_stdout = logging.StreamHandler(sys.stdout) handler_stdout.setLevel(logging.DEBUG) handler_stdout.addFilter(InfoFilter()) handler_stdout.setFormatter( - logging.Formatter(fmt='%(asctime)s %(levelname)s: %(message)s') + logging.Formatter(fmt="%(asctime)s %(levelname)s: %(message)s") ) handler_stderr = logging.StreamHandler() handler_stderr.setLevel(logging.WARNING) handler_stderr.setFormatter( - logging.Formatter(fmt='%(asctime)s %(levelname)s: %(message)s') + logging.Formatter(fmt="%(asctime)s %(levelname)s: %(message)s") ) self.logger.addHandler(handler_stdout) self.logger.addHandler(handler_stderr) # Handle more loggers - self.loggers = {'topolog': self.logger} + self.loggers = {"topolog": self.logger} def set_log_level(self, level): "Set the logging level" self.log_level = DEBUG_TOPO2LOGGING.get(level) self.logger.setLevel(self.log_level) - def get_logger(self, name='topolog', log_level=None, target=sys.stdout): + def get_logger(self, name="topolog", log_level=None, target=sys.stdout): """ Get a new logger entry. Allows creating different loggers for formating, filtering or handling (file, stream or stdout/stderr). @@ -100,12 +103,13 @@ def get_logger(self, name='topolog', log_level=None, target=sys.stdout): handler = logging.StreamHandler(stream=target) handler.setFormatter( - logging.Formatter(fmt='%(asctime)s %(levelname)s: %(message)s') + logging.Formatter(fmt="%(asctime)s %(levelname)s: %(message)s") ) nlogger.addHandler(handler) self.loggers[name] = nlogger return nlogger + # # Global variables # diff --git a/tests/topotests/lib/topotest.py b/tests/topotests/lib/topotest.py index e0da20e07f..dbc76e56ff 100644 --- a/tests/topotests/lib/topotest.py +++ b/tests/topotests/lib/topotest.py @@ -29,14 +29,20 @@ import sys import functools import glob -import StringIO import subprocess import tempfile import platform import difflib import time +import signal from lib.topolog import logger +from copy import deepcopy + +if sys.version_info[0] > 2: + import configparser +else: + import ConfigParser as configparser from mininet.topo import Topo from mininet.net import Mininet @@ -45,6 +51,36 @@ from mininet.cli import CLI from mininet.link import Intf +def gdb_core(obj, daemon, corefiles): + gdbcmds = ''' + info threads + bt full + disassemble + up + disassemble + up + disassemble + up + disassemble + up + disassemble + up + disassemble + ''' + gdbcmds = [['-ex', i.strip()] for i in gdbcmds.strip().split('\n')] + gdbcmds = [item for sl in gdbcmds for item in sl] + + daemon_path = os.path.join(obj.daemondir, daemon) + backtrace = subprocess.check_output( + ['gdb', daemon_path, corefiles[0], '--batch'] + gdbcmds + ) + sys.stderr.write( + "\n%s: %s crashed. Core file found - Backtrace follows:\n" + % (obj.name, daemon) + ) + sys.stderr.write("%s" % backtrace) + return backtrace + class json_cmp_result(object): "json_cmp result class for better assertion messages" @@ -60,147 +96,232 @@ def has_errors(self): "Returns True if there were errors, otherwise False." return len(self.errors) > 0 + def gen_report(self): + headline = ["Generated JSON diff error report:", ""] + return headline + self.errors + def __str__(self): - return '\n'.join(self.errors) + return ( + "Generated JSON diff error report:\n\n\n" + "\n".join(self.errors) + "\n\n" + ) -def json_diff(d1, d2): - """ - Returns a string with the difference between JSON data. +def gen_json_diff_report(d1, d2, exact=False, path="> $", acc=(0, "")): """ - json_format_opts = { - 'indent': 4, - 'sort_keys': True, - } - dstr1 = json.dumps(d1, **json_format_opts) - dstr2 = json.dumps(d2, **json_format_opts) - return difflines(dstr2, dstr1, title1='Expected value', title2='Current value', n=0) - - -def _json_list_cmp(list1, list2, parent, result): - "Handles list type entries." - # Check second list2 type - if not isinstance(list1, type([])) or not isinstance(list2, type([])): - result.add_error( - '{} has different type than expected '.format(parent) + - '(have {}, expected {}):\n{}'.format( - type(list1), type(list2), json_diff(list1, list2))) - return - - # Check list size - if len(list2) > len(list1): - result.add_error( - '{} too few items '.format(parent) + - '(have {}, expected {}:\n {})'.format( - len(list1), len(list2), - json_diff(list1, list2))) - return - - # List all unmatched items errors - unmatched = [] - for expected in list2: - matched = False - for value in list1: - if json_cmp({'json': value}, {'json': expected}) is None: - matched = True - break - - if not matched: - unmatched.append(expected) - - # If there are unmatched items, error out. - if unmatched: - result.add_error( - '{} value is different (\n{})'.format( - parent, json_diff(list1, list2))) - - -def json_cmp(d1, d2): - """ - JSON compare function. Receives two parameters: - * `d1`: json value - * `d2`: json subset which we expect - - Returns `None` when all keys that `d1` has matches `d2`, - otherwise a string containing what failed. - - Note: key absence can be tested by adding a key with value `None`. + Internal workhorse which compares two JSON data structures and generates an error report suited to be read by a human eye. """ - squeue = [(d1, d2, 'json')] - result = json_cmp_result() - for s in squeue: - nd1, nd2, parent = s + def dump_json(v): + if isinstance(v, (dict, list)): + return "\t" + "\t".join( + json.dumps(v, indent=4, separators=(",", ": ")).splitlines(True) + ) + else: + return "'{}'".format(v) + + def json_type(v): + if isinstance(v, (list, tuple)): + return "Array" + elif isinstance(v, dict): + return "Object" + elif isinstance(v, (int, float)): + return "Number" + elif isinstance(v, bool): + return "Boolean" + elif isinstance(v, str): + return "String" + elif v == None: + return "null" + + def get_errors(other_acc): + return other_acc[1] + + def get_errors_n(other_acc): + return other_acc[0] + + def add_error(acc, msg, points=1): + return (acc[0] + points, acc[1] + "{}: {}\n".format(path, msg)) + + def merge_errors(acc, other_acc): + return (acc[0] + other_acc[0], acc[1] + other_acc[1]) + + def add_idx(idx): + return "{}[{}]".format(path, idx) + + def add_key(key): + return "{}->{}".format(path, key) + + def has_errors(other_acc): + return other_acc[0] > 0 + + if d2 == "*" or ( + not isinstance(d1, (list, dict)) + and not isinstance(d2, (list, dict)) + and d1 == d2 + ): + return acc + elif ( + not isinstance(d1, (list, dict)) + and not isinstance(d2, (list, dict)) + and d1 != d2 + ): + acc = add_error( + acc, + "d1 has element with value '{}' but in d2 it has value '{}'".format(d1, d2), + ) + elif ( + isinstance(d1, list) + and isinstance(d2, list) + and ((len(d2) > 0 and d2[0] == "__ordered__") or exact) + ): + if not exact: + del d2[0] + if len(d1) != len(d2): + acc = add_error( + acc, + "d1 has Array of length {} but in d2 it is of length {}".format( + len(d1), len(d2) + ), + ) + else: + for idx, v1, v2 in zip(range(0, len(d1)), d1, d2): + acc = merge_errors( + acc, gen_json_diff_report(v1, v2, exact=exact, path=add_idx(idx)) + ) + elif isinstance(d1, list) and isinstance(d2, list): + if len(d1) < len(d2): + acc = add_error( + acc, + "d1 has Array of length {} but in d2 it is of length {}".format( + len(d1), len(d2) + ), + ) + else: + for idx2, v2 in zip(range(0, len(d2)), d2): + found_match = False + closest_diff = None + closest_idx = None + for idx1, v1 in zip(range(0, len(d1)), d1): + tmp_v1 = deepcopy(v1) + tmp_v2 = deepcopy(v2) + tmp_diff = gen_json_diff_report(tmp_v1, tmp_v2, path=add_idx(idx1)) + if not has_errors(tmp_diff): + found_match = True + del d1[idx1] + break + elif not closest_diff or get_errors_n(tmp_diff) < get_errors_n( + closest_diff + ): + closest_diff = tmp_diff + closest_idx = idx1 + if not found_match and isinstance(v2, (list, dict)): + sub_error = "\n\n\t{}".format( + "\t".join(get_errors(closest_diff).splitlines(True)) + ) + acc = add_error( + acc, + ( + "d2 has the following element at index {} which is not present in d1: " + + "\n\n{}\n\n\tClosest match in d1 is at index {} with the following errors: {}" + ).format(idx2, dump_json(v2), closest_idx, sub_error), + ) + if not found_match and not isinstance(v2, (list, dict)): + acc = add_error( + acc, + "d2 has the following element at index {} which is not present in d1: {}".format( + idx2, dump_json(v2) + ), + ) + elif isinstance(d1, dict) and isinstance(d2, dict) and exact: + invalid_keys_d1 = [k for k in d1.keys() if k not in d2.keys()] + invalid_keys_d2 = [k for k in d2.keys() if k not in d1.keys()] + for k in invalid_keys_d1: + acc = add_error(acc, "d1 has key '{}' which is not present in d2".format(k)) + for k in invalid_keys_d2: + acc = add_error(acc, "d2 has key '{}' which is not present in d1".format(k)) + valid_keys_intersection = [k for k in d1.keys() if k in d2.keys()] + for k in valid_keys_intersection: + acc = merge_errors( + acc, gen_json_diff_report(d1[k], d2[k], exact=exact, path=add_key(k)) + ) + elif isinstance(d1, dict) and isinstance(d2, dict): + none_keys = [k for k, v in d2.items() if v == None] + none_keys_present = [k for k in d1.keys() if k in none_keys] + for k in none_keys_present: + acc = add_error( + acc, "d1 has key '{}' which is not supposed to be present".format(k) + ) + keys = [k for k, v in d2.items() if v != None] + invalid_keys_intersection = [k for k in keys if k not in d1.keys()] + for k in invalid_keys_intersection: + acc = add_error(acc, "d2 has key '{}' which is not present in d1".format(k)) + valid_keys_intersection = [k for k in keys if k in d1.keys()] + for k in valid_keys_intersection: + acc = merge_errors( + acc, gen_json_diff_report(d1[k], d2[k], exact=exact, path=add_key(k)) + ) + else: + acc = add_error( + acc, + "d1 has element of type '{}' but the corresponding element in d2 is of type '{}'".format( + json_type(d1), json_type(d2) + ), + points=2, + ) - # Handle JSON beginning with lists. - if isinstance(nd1, type([])) or isinstance(nd2, type([])): - _json_list_cmp(nd1, nd2, parent, result) - if result.has_errors(): - return result - else: - return None - - # Expect all required fields to exist. - s1, s2 = set(nd1), set(nd2) - s2_req = set([key for key in nd2 if nd2[key] is not None]) - diff = s2_req - s1 - if diff != set({}): - result.add_error('expected key(s) {} in {} (have {}):\n{}'.format( - str(list(diff)), parent, str(list(s1)), json_diff(nd1, nd2))) - - for key in s2.intersection(s1): - # Test for non existence of key in d2 - if nd2[key] is None: - result.add_error('"{}" should not exist in {} (have {}):\n{}'.format( - key, parent, str(s1), json_diff(nd1[key], nd2[key]))) - continue + return acc - # If nd1 key is a dict, we have to recurse in it later. - if isinstance(nd2[key], type({})): - if not isinstance(nd1[key], type({})): - result.add_error( - '{}["{}"] has different type than expected '.format(parent, key) + - '(have {}, expected {}):\n{}'.format( - type(nd1[key]), type(nd2[key]), json_diff(nd1[key], nd2[key]))) - continue - nparent = '{}["{}"]'.format(parent, key) - squeue.append((nd1[key], nd2[key], nparent)) - continue - # Check list items - if isinstance(nd2[key], type([])): - _json_list_cmp(nd1[key], nd2[key], parent, result) - continue +def json_cmp(d1, d2, exact=False): + """ + JSON compare function. Receives two parameters: + * `d1`: parsed JSON data structure + * `d2`: parsed JSON data structure + + Returns 'None' when all JSON Object keys and all Array elements of d2 have a match + in d1, e.g. when d2 is a "subset" of d1 without honoring any order. Otherwise an + error report is generated and wrapped in a 'json_cmp_result()'. There are special + parameters and notations explained below which can be used to cover rather unusual + cases: + + * when 'exact is set to 'True' then d1 and d2 are tested for equality (including + order within JSON Arrays) + * using 'null' (or 'None' in Python) as JSON Object value is checking for key + absence in d1 + * using '*' as JSON Object value or Array value is checking for presence in d1 + without checking the values + * using '__ordered__' as first element in a JSON Array in d2 will also check the + order when it is compared to an Array in d1 + """ - # Compare JSON values - if nd1[key] != nd2[key]: - result.add_error( - '{}["{}"] value is different (\n{})'.format( - parent, key, json_diff(nd1[key], nd2[key]))) - continue + (errors_n, errors) = gen_json_diff_report(deepcopy(d1), deepcopy(d2), exact=exact) - if result.has_errors(): + if errors_n > 0: + result = json_cmp_result() + result.add_error(errors) return result - - return None + else: + return None def router_output_cmp(router, cmd, expected): """ Runs `cmd` in router and compares the output with `expected`. """ - return difflines(normalize_text(router.vtysh_cmd(cmd)), - normalize_text(expected), - title1="Current output", - title2="Expected output") + return difflines( + normalize_text(router.vtysh_cmd(cmd)), + normalize_text(expected), + title1="Current output", + title2="Expected output", + ) -def router_json_cmp(router, cmd, data): +def router_json_cmp(router, cmd, data, exact=False): """ Runs `cmd` that returns JSON data (normally the command ends with 'json') and compare with `data` contents. """ - return json_cmp(router.vtysh_cmd(cmd, isjson=True), data) + return json_cmp(router.vtysh_cmd(cmd, isjson=True), data, exact) def run_and_expect(func, what, count=20, wait=3): @@ -227,7 +348,9 @@ def run_and_expect(func, what, count=20, wait=3): logger.info( "'{}' polling started (interval {} secs, maximum wait {} secs)".format( - func_name, wait, int(wait * count))) + func_name, wait, int(wait * count) + ) + ) while count > 0: result = func() @@ -237,13 +360,73 @@ def run_and_expect(func, what, count=20, wait=3): continue end_time = time.time() - logger.info("'{}' succeeded after {:.2f} seconds".format( - func_name, end_time - start_time)) + logger.info( + "'{}' succeeded after {:.2f} seconds".format( + func_name, end_time - start_time + ) + ) return (True, result) end_time = time.time() - logger.error("'{}' failed after {:.2f} seconds".format( - func_name, end_time - start_time)) + logger.error( + "'{}' failed after {:.2f} seconds".format(func_name, end_time - start_time) + ) + return (False, result) + + +def run_and_expect_type(func, etype, count=20, wait=3, avalue=None): + """ + Run `func` and compare the result with `etype`. Do it for `count` times + waiting `wait` seconds between tries. By default it tries 20 times with + 3 seconds delay between tries. + + This function is used when you want to test the return type and, + optionally, the return value. + + Returns (True, func-return) on success or + (False, func-return) on failure. + """ + start_time = time.time() + func_name = "" + if func.__class__ == functools.partial: + func_name = func.func.__name__ + else: + func_name = func.__name__ + + logger.info( + "'{}' polling started (interval {} secs, maximum wait {} secs)".format( + func_name, wait, int(wait * count) + ) + ) + + while count > 0: + result = func() + if not isinstance(result, etype): + logger.debug( + "Expected result type '{}' got '{}' instead".format(etype, type(result)) + ) + time.sleep(wait) + count -= 1 + continue + + if etype != type(None) and avalue != None and result != avalue: + logger.debug("Expected value '{}' got '{}' instead".format(avalue, result)) + time.sleep(wait) + count -= 1 + continue + + end_time = time.time() + logger.info( + "'{}' succeeded after {:.2f} seconds".format( + func_name, end_time - start_time + ) + ) + return (True, result) + + end_time = time.time() + logger.error( + "'{}' failed after {:.2f} seconds".format(func_name, end_time - start_time) + ) return (False, result) @@ -252,18 +435,25 @@ def int2dpid(dpid): try: dpid = hex(dpid)[2:] - dpid = '0'*(16-len(dpid))+dpid + dpid = "0" * (16 - len(dpid)) + dpid return dpid except IndexError: - raise Exception('Unable to derive default datapath ID - ' - 'please either specify a dpid or use a ' - 'canonical switch name such as s23.') + raise Exception( + "Unable to derive default datapath ID - " + "please either specify a dpid or use a " + "canonical switch name such as s23." + ) + def pid_exists(pid): "Check whether pid exists in the current process table." if pid <= 0: return False + try: + os.waitpid(pid, os.WNOHANG) + except: + pass try: os.kill(pid, 0) except OSError as err: @@ -280,70 +470,78 @@ def pid_exists(pid): else: return True + def get_textdiff(text1, text2, title1="", title2="", **opts): "Returns empty string if same or formatted diff" - diff = '\n'.join(difflib.unified_diff(text1, text2, - fromfile=title1, tofile=title2, **opts)) + diff = "\n".join( + difflib.unified_diff(text1, text2, fromfile=title1, tofile=title2, **opts) + ) # Clean up line endings diff = os.linesep.join([s for s in diff.splitlines() if s]) return diff -def difflines(text1, text2, title1='', title2='', **opts): + +def difflines(text1, text2, title1="", title2="", **opts): "Wrapper for get_textdiff to avoid string transformations." - text1 = ('\n'.join(text1.rstrip().splitlines()) + '\n').splitlines(1) - text2 = ('\n'.join(text2.rstrip().splitlines()) + '\n').splitlines(1) + text1 = ("\n".join(text1.rstrip().splitlines()) + "\n").splitlines(1) + text2 = ("\n".join(text2.rstrip().splitlines()) + "\n").splitlines(1) return get_textdiff(text1, text2, title1, title2, **opts) + def get_file(content): """ Generates a temporary file in '/tmp' with `content` and returns the file name. """ - fde = tempfile.NamedTemporaryFile(mode='w', delete=False) + fde = tempfile.NamedTemporaryFile(mode="w", delete=False) fname = fde.name fde.write(content) fde.close() return fname + def normalize_text(text): """ Strips formating spaces/tabs, carriage returns and trailing whitespace. """ - text = re.sub(r'[ \t]+', ' ', text) - text = re.sub(r'\r', '', text) + text = re.sub(r"[ \t]+", " ", text) + text = re.sub(r"\r", "", text) # Remove whitespace in the middle of text. - text = re.sub(r'[ \t]+\n', '\n', text) + text = re.sub(r"[ \t]+\n", "\n", text) # Remove whitespace at the end of the text. text = text.rstrip() return text + def module_present_linux(module, load): """ Returns whether `module` is present. If `load` is true, it will try to load it via modprobe. """ - with open('/proc/modules', 'r') as modules_file: - if module.replace('-','_') in modules_file.read(): + with open("/proc/modules", "r") as modules_file: + if module.replace("-", "_") in modules_file.read(): return True - cmd = '/sbin/modprobe {}{}'.format('' if load else '-n ', - module) + cmd = "/sbin/modprobe {}{}".format("" if load else "-n ", module) if os.system(cmd) != 0: return False else: return True + def module_present_freebsd(module, load): return True + def module_present(module, load=True): if sys.platform.startswith("linux"): return module_present_linux(module, load) elif sys.platform.startswith("freebsd"): return module_present_freebsd(module, load) + def version_cmp(v1, v2): """ Compare two version strings and returns: @@ -354,15 +552,15 @@ def version_cmp(v1, v2): Raises `ValueError` if versions are not well formated. """ - vregex = r'(?P\d+(\.(\d+))*)' + vregex = r"(?P\d+(\.(\d+))*)" v1m = re.match(vregex, v1) v2m = re.match(vregex, v2) if v1m is None or v2m is None: raise ValueError("got a invalid version string") # Split values - v1g = v1m.group('whole').split('.') - v2g = v2m.group('whole').split('.') + v1g = v1m.group("whole").split(".") + v2g = v2m.group("whole").split(".") # Get the longest version string vnum = len(v1g) @@ -399,50 +597,86 @@ def version_cmp(v1, v2): return -1 return 0 + def interface_set_status(node, ifacename, ifaceaction=False, vrf_name=None): if ifaceaction: - str_ifaceaction = 'no shutdown' + str_ifaceaction = "no shutdown" else: - str_ifaceaction = 'shutdown' + str_ifaceaction = "shutdown" if vrf_name == None: - cmd = 'vtysh -c \"configure terminal\" -c \"interface {0}\" -c \"{1}\"'.format(ifacename, str_ifaceaction) + cmd = 'vtysh -c "configure terminal" -c "interface {0}" -c "{1}"'.format( + ifacename, str_ifaceaction + ) else: - cmd = 'vtysh -c \"configure terminal\" -c \"interface {0} vrf {1}\" -c \"{2}\"'.format(ifacename, vrf_name, str_ifaceaction) + cmd = 'vtysh -c "configure terminal" -c "interface {0} vrf {1}" -c "{2}"'.format( + ifacename, vrf_name, str_ifaceaction + ) node.run(cmd) + def ip4_route_zebra(node, vrf_name=None): """ Gets an output of 'show ip route' command. It can be used with comparing the output to a reference """ if vrf_name == None: - tmp = node.vtysh_cmd('show ip route') + tmp = node.vtysh_cmd("show ip route") + else: + tmp = node.vtysh_cmd("show ip route vrf {0}".format(vrf_name)) + output = re.sub(r" [0-2][0-9]:[0-5][0-9]:[0-5][0-9]", " XX:XX:XX", tmp) + + lines = output.splitlines() + header_found = False + while lines and (not lines[0].strip() or not header_found): + if "> - selected route" in lines[0]: + header_found = True + lines = lines[1:] + return "\n".join(lines) + + +def ip6_route_zebra(node, vrf_name=None): + """ + Retrieves the output of 'show ipv6 route [vrf vrf_name]', then + canonicalizes it by eliding link-locals. + """ + + if vrf_name == None: + tmp = node.vtysh_cmd("show ipv6 route") else: - tmp = node.vtysh_cmd('show ip route vrf {0}'.format(vrf_name)) + tmp = node.vtysh_cmd("show ipv6 route vrf {0}".format(vrf_name)) + + # Mask out timestamp output = re.sub(r" [0-2][0-9]:[0-5][0-9]:[0-5][0-9]", " XX:XX:XX", tmp) + # Mask out the link-local addresses + output = re.sub(r"fe80::[^ ]+,", "fe80::XXXX:XXXX:XXXX:XXXX,", output) + lines = output.splitlines() header_found = False while lines and (not lines[0].strip() or not header_found): - if '> - selected route' in lines[0]: + if "> - selected route" in lines[0]: header_found = True lines = lines[1:] - return '\n'.join(lines) + + return "\n".join(lines) + def proto_name_to_number(protocol): return { - 'bgp': '186', - 'isis': '187', - 'ospf': '188', - 'rip': '189', - 'ripng': '190', - 'nhrp': '191', - 'eigrp': '192', - 'ldp': '193', - 'sharp': '194', - 'pbr': '195', - 'static': '196' - }.get(protocol, protocol) # default return same as input + "bgp": "186", + "isis": "187", + "ospf": "188", + "rip": "189", + "ripng": "190", + "nhrp": "191", + "eigrp": "192", + "ldp": "193", + "sharp": "194", + "pbr": "195", + "static": "196", + }.get( + protocol, protocol + ) # default return same as input def ip4_route(node): @@ -463,28 +697,72 @@ def ip4_route(node): } } """ - output = normalize_text(node.run('ip route')).splitlines() + output = normalize_text(node.run("ip route")).splitlines() result = {} for line in output: - columns = line.split(' ') + columns = line.split(" ") route = result[columns[0]] = {} prev = None for column in columns: - if prev == 'dev': - route['dev'] = column - if prev == 'via': - route['via'] = column - if prev == 'proto': + if prev == "dev": + route["dev"] = column + if prev == "via": + route["via"] = column + if prev == "proto": # translate protocol names back to numbers - route['proto'] = proto_name_to_number(column) - if prev == 'metric': - route['metric'] = column - if prev == 'scope': - route['scope'] = column + route["proto"] = proto_name_to_number(column) + if prev == "metric": + route["metric"] = column + if prev == "scope": + route["scope"] = column prev = column return result + +def ip4_vrf_route(node): + """ + Gets a structured return of the command 'ip route show vrf {0}-cust1'. + It can be used in conjuction with json_cmp() to provide accurate assert explanations. + + Return example: + { + '10.0.1.0/24': { + 'dev': 'eth0', + 'via': '172.16.0.1', + 'proto': '188', + }, + '10.0.2.0/24': { + 'dev': 'eth1', + 'proto': 'kernel', + } + } + """ + output = normalize_text( + node.run("ip route show vrf {0}-cust1".format(node.name))).splitlines() + + result = {} + for line in output: + columns = line.split(" ") + route = result[columns[0]] = {} + prev = None + for column in columns: + if prev == "dev": + route["dev"] = column + if prev == "via": + route["via"] = column + if prev == "proto": + # translate protocol names back to numbers + route["proto"] = proto_name_to_number(column) + if prev == "metric": + route["metric"] = column + if prev == "scope": + route["scope"] = column + prev = column + + return result + + def ip6_route(node): """ Gets a structured return of the command 'ip -6 route'. It can be used in @@ -502,80 +780,195 @@ def ip6_route(node): } } """ - output = normalize_text(node.run('ip -6 route')).splitlines() + output = normalize_text(node.run("ip -6 route")).splitlines() + result = {} + for line in output: + columns = line.split(" ") + route = result[columns[0]] = {} + prev = None + for column in columns: + if prev == "dev": + route["dev"] = column + if prev == "via": + route["via"] = column + if prev == "proto": + # translate protocol names back to numbers + route["proto"] = proto_name_to_number(column) + if prev == "metric": + route["metric"] = column + if prev == "pref": + route["pref"] = column + prev = column + + return result + + +def ip6_vrf_route(node): + """ + Gets a structured return of the command 'ip -6 route show vrf {0}-cust1'. + It can be used in conjuction with json_cmp() to provide accurate assert explanations. + + Return example: + { + '2001:db8:1::/64': { + 'dev': 'eth0', + 'proto': '188', + }, + '2001:db8:2::/64': { + 'dev': 'eth1', + 'proto': 'kernel', + } + } + """ + output = normalize_text( + node.run("ip -6 route show vrf {0}-cust1".format(node.name))).splitlines() result = {} for line in output: - columns = line.split(' ') + columns = line.split(" ") route = result[columns[0]] = {} prev = None for column in columns: - if prev == 'dev': - route['dev'] = column - if prev == 'via': - route['via'] = column - if prev == 'proto': + if prev == "dev": + route["dev"] = column + if prev == "via": + route["via"] = column + if prev == "proto": # translate protocol names back to numbers - route['proto'] = proto_name_to_number(column) - if prev == 'metric': - route['metric'] = column - if prev == 'pref': - route['pref'] = column + route["proto"] = proto_name_to_number(column) + if prev == "metric": + route["metric"] = column + if prev == "pref": + route["pref"] = column prev = column return result + +def ip_rules(node): + """ + Gets a structured return of the command 'ip rule'. It can be used in + conjuction with json_cmp() to provide accurate assert explanations. + + Return example: + [ + { + "pref": "0" + "from": "all" + }, + { + "pref": "32766" + "from": "all" + }, + { + "to": "3.4.5.0/24", + "iif": "r1-eth2", + "pref": "304", + "from": "1.2.0.0/16", + "proto": "zebra" + } + ] + """ + output = normalize_text(node.run("ip rule")).splitlines() + result = [] + for line in output: + columns = line.split(" ") + + route = {} + # remove last character, since it is ':' + pref = columns[0][:-1] + route["pref"] = pref + prev = None + for column in columns: + if prev == "from": + route["from"] = column + if prev == "to": + route["to"] = column + if prev == "proto": + route["proto"] = column + if prev == "iif": + route["iif"] = column + if prev == "fwmark": + route["fwmark"] = column + prev = column + + result.append(route) + return result + + def sleep(amount, reason=None): """ Sleep wrapper that registers in the log the amount of sleep """ if reason is None: - logger.info('Sleeping for {} seconds'.format(amount)) + logger.info("Sleeping for {} seconds".format(amount)) else: - logger.info(reason + ' ({} seconds)'.format(amount)) + logger.info(reason + " ({} seconds)".format(amount)) time.sleep(amount) + def checkAddressSanitizerError(output, router, component): "Checks for AddressSanitizer in output. If found, then logs it and returns true, false otherwise" - addressSantizerError = re.search('(==[0-9]+==)ERROR: AddressSanitizer: ([^\s]*) ', output) + addressSantizerError = re.search( + "(==[0-9]+==)ERROR: AddressSanitizer: ([^\s]*) ", output + ) if addressSantizerError: - sys.stderr.write("%s: %s triggered an exception by AddressSanitizer\n" % (router, component)) + sys.stderr.write( + "%s: %s triggered an exception by AddressSanitizer\n" % (router, component) + ) # Sanitizer Error found in log pidMark = addressSantizerError.group(1) - addressSantizerLog = re.search('%s(.*)%s' % (pidMark, pidMark), output, re.DOTALL) + addressSantizerLog = re.search( + "%s(.*)%s" % (pidMark, pidMark), output, re.DOTALL + ) if addressSantizerLog: - callingTest = os.path.basename(sys._current_frames().values()[0].f_back.f_back.f_globals['__file__']) + callingTest = os.path.basename( + sys._current_frames().values()[0].f_back.f_back.f_globals["__file__"] + ) callingProc = sys._getframe(2).f_code.co_name with open("/tmp/AddressSanitzer.txt", "a") as addrSanFile: - sys.stderr.write('\n'.join(addressSantizerLog.group(1).splitlines()) + '\n') + sys.stderr.write( + "\n".join(addressSantizerLog.group(1).splitlines()) + "\n" + ) addrSanFile.write("## Error: %s\n\n" % addressSantizerError.group(2)) - addrSanFile.write("### AddressSanitizer error in topotest `%s`, test `%s`, router `%s`\n\n" % (callingTest, callingProc, router)) - addrSanFile.write(' '+ '\n '.join(addressSantizerLog.group(1).splitlines()) + '\n') + addrSanFile.write( + "### AddressSanitizer error in topotest `%s`, test `%s`, router `%s`\n\n" + % (callingTest, callingProc, router) + ) + addrSanFile.write( + " " + + "\n ".join(addressSantizerLog.group(1).splitlines()) + + "\n" + ) addrSanFile.write("\n---------------\n") return True return False + def addRouter(topo, name): "Adding a FRRouter (or Quagga) to Topology" - MyPrivateDirs = ['/etc/frr', - '/etc/quagga', - '/var/run/frr', - '/var/run/quagga', - '/var/log'] + MyPrivateDirs = [ + "/etc/frr", + "/etc/quagga", + "/var/run/frr", + "/var/run/quagga", + "/var/log", + ] if sys.platform.startswith("linux"): return topo.addNode(name, cls=LinuxRouter, privateDirs=MyPrivateDirs) elif sys.platform.startswith("freebsd"): return topo.addNode(name, cls=FreeBSDRouter, privateDirs=MyPrivateDirs) + def set_sysctl(node, sysctl, value): "Set a sysctl value and return None on success or an error string" - valuestr = '{}'.format(value) + valuestr = "{}".format(value) command = "sysctl {0}={1}".format(sysctl, valuestr) cmdret = node.cmd(command) - matches = re.search(r'([^ ]+) = ([^\s]+)', cmdret) + matches = re.search(r"([^ ]+) = ([^\s]+)", cmdret) if matches is None: return cmdret if matches.group(1) != sysctl: @@ -585,6 +978,7 @@ def set_sysctl(node, sysctl, value): return None + def assert_sysctl(node, sysctl, value): "Set and assert that the sysctl is set with the specified value." assert set_sysctl(node, sysctl, value) is None @@ -595,51 +989,83 @@ class Router(Node): def __init__(self, name, **params): super(Router, self).__init__(name, **params) - self.logdir = params.get('logdir') + self.logdir = params.get("logdir") + + # Backward compatibility: + # Load configuration defaults like topogen. + self.config_defaults = configparser.ConfigParser( + { + "verbosity": "info", + "frrdir": "/usr/lib/frr", + "quaggadir": "/usr/lib/quagga", + "routertype": "frr", + "memleak_path": None, + } + ) + self.config_defaults.read( + os.path.join(os.path.dirname(os.path.realpath(__file__)), "../pytest.ini") + ) # If this topology is using old API and doesn't have logdir # specified, then attempt to generate an unique logdir. if self.logdir is None: - cur_test = os.environ['PYTEST_CURRENT_TEST'] - self.logdir = ('/tmp/topotests/' + - cur_test[0:cur_test.find(".py")].replace('/', '.')) + cur_test = os.environ["PYTEST_CURRENT_TEST"] + self.logdir = "/tmp/topotests/" + cur_test[ + 0 : cur_test.find(".py") + ].replace("/", ".") # If the logdir is not created, then create it and set the # appropriated permissions. if not os.path.isdir(self.logdir): - os.system('mkdir -p ' + self.logdir + '/' + name) - os.system('chmod -R go+rw /tmp/topotests') + os.system("mkdir -p " + self.logdir + "/" + name) + os.system("chmod -R go+rw /tmp/topotests") self.daemondir = None self.hasmpls = False - self.routertype = 'frr' - self.daemons = {'zebra': 0, 'ripd': 0, 'ripngd': 0, 'ospfd': 0, - 'ospf6d': 0, 'isisd': 0, 'bgpd': 0, 'pimd': 0, - 'ldpd': 0, 'eigrpd': 0, 'nhrpd': 0, 'staticd': 0, - 'bfdd': 0, 'sharpd': 0} - self.daemons_options = {'zebra': ''} + self.routertype = "frr" + self.daemons = { + "zebra": 0, + "ripd": 0, + "ripngd": 0, + "ospfd": 0, + "ospf6d": 0, + "isisd": 0, + "bgpd": 0, + "pimd": 0, + "ldpd": 0, + "eigrpd": 0, + "nhrpd": 0, + "staticd": 0, + "bfdd": 0, + "sharpd": 0, + "babeld": 0, + "pbrd": 0, + } + self.daemons_options = {"zebra": ""} self.reportCores = True self.version = None def _config_frr(self, **params): "Configure FRR binaries" - self.daemondir = params.get('frrdir') + self.daemondir = params.get("frrdir") if self.daemondir is None: - self.daemondir = '/usr/lib/frr' + self.daemondir = self.config_defaults.get("topogen", "frrdir") - zebra_path = os.path.join(self.daemondir, 'zebra') + zebra_path = os.path.join(self.daemondir, "zebra") if not os.path.isfile(zebra_path): raise Exception("FRR zebra binary doesn't exist at {}".format(zebra_path)) def _config_quagga(self, **params): "Configure Quagga binaries" - self.daemondir = params.get('quaggadir') + self.daemondir = params.get("quaggadir") if self.daemondir is None: - self.daemondir = '/usr/lib/quagga' + self.daemondir = self.config_defaults.get("topogen", "quaggadir") - zebra_path = os.path.join(self.daemondir, 'zebra') + zebra_path = os.path.join(self.daemondir, "zebra") if not os.path.isfile(zebra_path): - raise Exception("Quagga zebra binary doesn't exist at {}".format(zebra_path)) + raise Exception( + "Quagga zebra binary doesn't exist at {}".format(zebra_path) + ) # pylint: disable=W0221 # Some params are only meaningful for the parent class. @@ -647,90 +1073,134 @@ def config(self, **params): super(Router, self).config(**params) # User did not specify the daemons directory, try to autodetect it. - self.daemondir = params.get('daemondir') + self.daemondir = params.get("daemondir") if self.daemondir is None: - self.routertype = params.get('routertype', 'frr') - if self.routertype == 'quagga': + self.routertype = params.get( + "routertype", self.config_defaults.get("topogen", "routertype") + ) + if self.routertype == "quagga": self._config_quagga(**params) else: self._config_frr(**params) else: # Test the provided path - zpath = os.path.join(self.daemondir, 'zebra') + zpath = os.path.join(self.daemondir, "zebra") if not os.path.isfile(zpath): - raise Exception('No zebra binary found in {}'.format(zpath)) + raise Exception("No zebra binary found in {}".format(zpath)) # Allow user to specify routertype when the path was specified. - if params.get('routertype') is not None: - self.routertype = self.params.get('routertype') + if params.get("routertype") is not None: + self.routertype = params.get("routertype") - self.cmd('ulimit -c unlimited') + self.cmd("ulimit -c unlimited") # Set ownership of config files - self.cmd('chown {0}:{0}vty /etc/{0}'.format(self.routertype)) + self.cmd("chown {0}:{0}vty /etc/{0}".format(self.routertype)) def terminate(self): - # Delete Running Quagga or FRR Daemons + # Stop running FRR daemons self.stopRouter() - # rundaemons = self.cmd('ls -1 /var/run/%s/*.pid' % self.routertype) - # for d in StringIO.StringIO(rundaemons): - # self.cmd('kill -7 `cat %s`' % d.rstrip()) - # self.waitOutput() + # Disable forwarding - set_sysctl(self, 'net.ipv4.ip_forward', 0) - set_sysctl(self, 'net.ipv6.conf.all.forwarding', 0) + set_sysctl(self, "net.ipv4.ip_forward", 0) + set_sysctl(self, "net.ipv6.conf.all.forwarding", 0) super(Router, self).terminate() - os.system('chmod -R go+rw /tmp/topotests') + os.system("chmod -R go+rw /tmp/topotests") + + # Return count of running daemons + def listDaemons(self): + ret = [] + rundaemons = self.cmd("ls -1 /var/run/%s/*.pid" % self.routertype) + errors = "" + if re.search(r"No such file or directory", rundaemons): + return 0 + if rundaemons is not None: + bet = rundaemons.split('\n') + for d in bet[:-1]: + daemonpid = self.cmd("cat %s" % d.rstrip()).rstrip() + if daemonpid.isdigit() and pid_exists(int(daemonpid)): + ret.append(os.path.basename(d.rstrip().rsplit(".", 1)[0])) - def stopRouter(self, wait=True, assertOnError=True, minErrorVersion='5.1'): - # Stop Running Quagga or FRR Daemons - rundaemons = self.cmd('ls -1 /var/run/%s/*.pid' % self.routertype) + return ret + + def stopRouter(self, wait=True, assertOnError=True, minErrorVersion="5.1"): + # Stop Running FRR Daemons + rundaemons = self.cmd("ls -1 /var/run/%s/*.pid" % self.routertype) errors = "" if re.search(r"No such file or directory", rundaemons): return errors if rundaemons is not None: - numRunning = 0 - for d in StringIO.StringIO(rundaemons): - daemonpid = self.cmd('cat %s' % d.rstrip()).rstrip() - if (daemonpid.isdigit() and pid_exists(int(daemonpid))): - logger.info('{}: stopping {}'.format( - self.name, - os.path.basename(d.rstrip().rsplit(".", 1)[0]) - )) - self.cmd('kill -TERM %s' % daemonpid) - self.waitOutput() - if pid_exists(int(daemonpid)): - numRunning += 1 - if wait and numRunning > 0: - sleep(2, '{}: waiting for daemons stopping'.format(self.name)) + dmns = rundaemons.split('\n') + # Exclude empty string at end of list + for d in dmns[:-1]: + daemonpid = self.cmd("cat %s" % d.rstrip()).rstrip() + if daemonpid.isdigit() and pid_exists(int(daemonpid)): + daemonname = os.path.basename(d.rstrip().rsplit(".", 1)[0]) + logger.info( + "{}: stopping {}".format( + self.name, daemonname + ) + ) + try: + os.kill(int(daemonpid), signal.SIGTERM) + except OSError as err: + if err.errno == errno.ESRCH: + logger.error("{}: {} left a dead pidfile (pid={})".format(self.name, daemonname, daemonpid)) + else: + logger.info("{}: {} could not kill pid {}: {}".format(self.name, daemonname, daemonpid, str(err))) + + if not wait: + return errors + + running = self.listDaemons() + + if running: + sleep(0.1, "{}: waiting for daemons stopping: {}".format(self.name, ', '.join(running))) + running = self.listDaemons() + + counter = 20 + while counter > 0 and running: + sleep(0.5, "{}: waiting for daemons stopping: {}".format(self.name, ', '.join(running))) + running = self.listDaemons() + counter -= 1 + + if running: # 2nd round of kill if daemons didn't exit - for d in StringIO.StringIO(rundaemons): - daemonpid = self.cmd('cat %s' % d.rstrip()).rstrip() - if (daemonpid.isdigit() and pid_exists(int(daemonpid))): - logger.info('{}: killing {}'.format( - self.name, - os.path.basename(d.rstrip().rsplit(".", 1)[0]) - )) - self.cmd('kill -7 %s' % daemonpid) + dmns = rundaemons.split('\n') + # Exclude empty string at end of list + for d in dmns[:-1]: + daemonpid = self.cmd("cat %s" % d.rstrip()).rstrip() + if daemonpid.isdigit() and pid_exists(int(daemonpid)): + logger.info( + "{}: killing {}".format( + self.name, + os.path.basename(d.rstrip().rsplit(".", 1)[0]), + ) + ) + self.cmd("kill -7 %s" % daemonpid) self.waitOutput() - self.cmd('rm -- {}'.format(d.rstrip())) - if wait: - errors = self.checkRouterCores(reportOnce=True) - if self.checkRouterVersion('<', minErrorVersion): - #ignore errors in old versions - errors = "" - if assertOnError and len(errors) > 0: - assert "Errors found - details follow:" == 0, errors + self.cmd("rm -- {}".format(d.rstrip())) + + if not wait: + return errors + + errors = self.checkRouterCores(reportOnce=True) + if self.checkRouterVersion("<", minErrorVersion): + # ignore errors in old versions + errors = "" + if assertOnError and len(errors) > 0: + assert "Errors found - details follow:" == 0, errors return errors def removeIPs(self): for interface in self.intfNames(): - self.cmd('ip address flush', interface) + self.cmd("ip address flush", interface) def checkCapability(self, daemon, param): if param is not None: daemon_path = os.path.join(self.daemondir, daemon) - daemon_search_option = param.replace('-','') - output = self.cmd('{0} -h | grep {1}'.format( - daemon_path, daemon_search_option)) + daemon_search_option = param.replace("-", "") + output = self.cmd( + "{0} -h | grep {1}".format(daemon_path, daemon_search_option) + ) if daemon_search_option not in output: return False return True @@ -742,130 +1212,267 @@ def loadConf(self, daemon, source=None, param=None): if param is not None: self.daemons_options[daemon] = param if source is None: - self.cmd('touch /etc/%s/%s.conf' % (self.routertype, daemon)) + self.cmd("touch /etc/%s/%s.conf" % (self.routertype, daemon)) self.waitOutput() else: - self.cmd('cp %s /etc/%s/%s.conf' % (source, self.routertype, daemon)) + self.cmd("cp %s /etc/%s/%s.conf" % (source, self.routertype, daemon)) self.waitOutput() - self.cmd('chmod 640 /etc/%s/%s.conf' % (self.routertype, daemon)) + self.cmd("chmod 640 /etc/%s/%s.conf" % (self.routertype, daemon)) self.waitOutput() - self.cmd('chown %s:%s /etc/%s/%s.conf' % (self.routertype, self.routertype, self.routertype, daemon)) + self.cmd( + "chown %s:%s /etc/%s/%s.conf" + % (self.routertype, self.routertype, self.routertype, daemon) + ) self.waitOutput() - if (daemon == 'zebra') and (self.daemons['staticd'] == 0): + if (daemon == "zebra") and (self.daemons["staticd"] == 0): # Add staticd with zebra - if it exists - staticd_path = os.path.join(self.daemondir, 'staticd') + staticd_path = os.path.join(self.daemondir, "staticd") if os.path.isfile(staticd_path): - self.daemons['staticd'] = 1 - self.daemons_options['staticd'] = '' + self.daemons["staticd"] = 1 + self.daemons_options["staticd"] = "" # Auto-Started staticd has no config, so it will read from zebra config else: - logger.info('No daemon {} known'.format(daemon)) + logger.info("No daemon {} known".format(daemon)) # print "Daemons after:", self.daemons def startRouter(self, tgen=None): # Disable integrated-vtysh-config - self.cmd('echo "no service integrated-vtysh-config" >> /etc/%s/vtysh.conf' % self.routertype) - self.cmd('chown %s:%svty /etc/%s/vtysh.conf' % (self.routertype, self.routertype, self.routertype)) + self.cmd( + 'echo "no service integrated-vtysh-config" >> /etc/%s/vtysh.conf' + % self.routertype + ) + self.cmd( + "chown %s:%svty /etc/%s/vtysh.conf" + % (self.routertype, self.routertype, self.routertype) + ) # TODO remove the following lines after all tests are migrated to Topogen. # Try to find relevant old logfiles in /tmp and delete them - map(os.remove, glob.glob('{}/{}/*.log'.format(self.logdir, self.name))) + map(os.remove, glob.glob("{}/{}/*.log".format(self.logdir, self.name))) # Remove old core files - map(os.remove, glob.glob('{}/{}/*.dmp'.format(self.logdir, self.name))) + map(os.remove, glob.glob("{}/{}/*.dmp".format(self.logdir, self.name))) # Remove IP addresses from OS first - we have them in zebra.conf self.removeIPs() # If ldp is used, check for LDP to be compiled and Linux Kernel to be 4.5 or higher # No error - but return message and skip all the tests - if self.daemons['ldpd'] == 1: - ldpd_path = os.path.join(self.daemondir, 'ldpd') + if self.daemons["ldpd"] == 1: + ldpd_path = os.path.join(self.daemondir, "ldpd") if not os.path.isfile(ldpd_path): logger.info("LDP Test, but no ldpd compiled or installed") return "LDP Test, but no ldpd compiled or installed" - if version_cmp(platform.release(), '4.5') < 0: + if version_cmp(platform.release(), "4.5") < 0: logger.info("LDP Test need Linux Kernel 4.5 minimum") return "LDP Test need Linux Kernel 4.5 minimum" # Check if have mpls if tgen != None: self.hasmpls = tgen.hasmpls if self.hasmpls != True: - logger.info("LDP/MPLS Tests will be skipped, platform missing module(s)") + logger.info( + "LDP/MPLS Tests will be skipped, platform missing module(s)" + ) else: # Test for MPLS Kernel modules available self.hasmpls = False - if not module_present('mpls-router'): - logger.info('MPLS tests will not run (missing mpls-router kernel module)') - elif not module_present('mpls-iptunnel'): - logger.info('MPLS tests will not run (missing mpls-iptunnel kernel module)') + if not module_present("mpls-router"): + logger.info( + "MPLS tests will not run (missing mpls-router kernel module)" + ) + elif not module_present("mpls-iptunnel"): + logger.info( + "MPLS tests will not run (missing mpls-iptunnel kernel module)" + ) else: self.hasmpls = True if self.hasmpls != True: return "LDP/MPLS Tests need mpls kernel modules" - self.cmd('echo 100000 > /proc/sys/net/mpls/platform_labels') + self.cmd("echo 100000 > /proc/sys/net/mpls/platform_labels") - if self.daemons['eigrpd'] == 1: - eigrpd_path = os.path.join(self.daemondir, 'eigrpd') + if self.daemons["eigrpd"] == 1: + eigrpd_path = os.path.join(self.daemondir, "eigrpd") if not os.path.isfile(eigrpd_path): logger.info("EIGRP Test, but no eigrpd compiled or installed") return "EIGRP Test, but no eigrpd compiled or installed" - if self.daemons['bfdd'] == 1: - bfdd_path = os.path.join(self.daemondir, 'bfdd') + if self.daemons["bfdd"] == 1: + bfdd_path = os.path.join(self.daemondir, "bfdd") if not os.path.isfile(bfdd_path): logger.info("BFD Test, but no bfdd compiled or installed") return "BFD Test, but no bfdd compiled or installed" - self.restartRouter() - return "" + return self.startRouterDaemons() + + def getStdErr(self, daemon): + return self.getLog("err", daemon) + + def getStdOut(self, daemon): + return self.getLog("out", daemon) + + def getLog(self, log, daemon): + return self.cmd("cat {}/{}/{}.{}".format(self.logdir, self.name, daemon, log)) + + def startRouterDaemons(self, daemons=None): + "Starts all FRR daemons for this router." + + bundle_data = '' + + if os.path.exists('/etc/frr/support_bundle_commands.conf'): + bundle_data = subprocess.check_output( + ["cat /etc/frr/support_bundle_commands.conf"], shell=True) + self.cmd( + "echo '{}' > /etc/frr/support_bundle_commands.conf".format(bundle_data) + ) - def restartRouter(self): # Starts actual daemons without init (ie restart) # cd to per node directory - self.cmd('cd {}/{}'.format(self.logdir, self.name)) - self.cmd('umask 000') - #Re-enable to allow for report per run + self.cmd("cd {}/{}".format(self.logdir, self.name)) + self.cmd("umask 000") + + # Re-enable to allow for report per run self.reportCores = True + + # XXX: glue code forward ported from removed function. if self.version == None: - self.version = self.cmd(os.path.join(self.daemondir, 'bgpd')+' -v').split()[2] - logger.info('{}: running version: {}'.format(self.name,self.version)) + self.version = self.cmd( + os.path.join(self.daemondir, "bgpd") + " -v" + ).split()[2] + logger.info("{}: running version: {}".format(self.name, self.version)) + + # If `daemons` was specified then some upper API called us with + # specific daemons, otherwise just use our own configuration. + daemons_list = [] + if daemons != None: + daemons_list = daemons + else: + # Append all daemons configured. + for daemon in self.daemons: + if self.daemons[daemon] == 1: + daemons_list.append(daemon) + # Start Zebra first - if self.daemons['zebra'] == 1: - zebra_path = os.path.join(self.daemondir, 'zebra') - zebra_option = self.daemons_options['zebra'] - self.cmd('{0} {1} > zebra.out 2> zebra.err &'.format( - zebra_path, zebra_option, self.logdir, self.name - )) - self.waitOutput() - logger.debug('{}: {} zebra started'.format(self, self.routertype)) - sleep(1, '{}: waiting for zebra to start'.format(self.name)) + if "zebra" in daemons_list: + zebra_path = os.path.join(self.daemondir, "zebra") + zebra_option = self.daemons_options["zebra"] + self.cmd( + "{0} {1} --log stdout --log-level debug -s 90000000 -d > zebra.out 2> zebra.err".format( + zebra_path, zebra_option, self.logdir, self.name + ) + ) + logger.debug("{}: {} zebra started".format(self, self.routertype)) + + # Remove `zebra` so we don't attempt to start it again. + while "zebra" in daemons_list: + daemons_list.remove("zebra") + # Start staticd next if required - if self.daemons['staticd'] == 1: - staticd_path = os.path.join(self.daemondir, 'staticd') - staticd_option = self.daemons_options['staticd'] - self.cmd('{0} {1} > staticd.out 2> staticd.err &'.format( - staticd_path, staticd_option, self.logdir, self.name - )) - self.waitOutput() - logger.debug('{}: {} staticd started'.format(self, self.routertype)) - # Fix Link-Local Addresses + if "staticd" in daemons_list: + staticd_path = os.path.join(self.daemondir, "staticd") + staticd_option = self.daemons_options["staticd"] + self.cmd( + "{0} {1} --log stdout --log-level debug -d > staticd.out 2> staticd.err".format( + staticd_path, staticd_option, self.logdir, self.name + ) + ) + logger.debug("{}: {} staticd started".format(self, self.routertype)) + + # Remove `staticd` so we don't attempt to start it again. + while "staticd" in daemons_list: + daemons_list.remove("staticd") + + # Fix Link-Local Addresses # Somehow (on Mininet only), Zebra removes the IPv6 Link-Local addresses on start. Fix this - self.cmd('for i in `ls /sys/class/net/` ; do mac=`cat /sys/class/net/$i/address`; IFS=\':\'; set $mac; unset IFS; ip address add dev $i scope link fe80::$(printf %02x $((0x$1 ^ 2)))$2:${3}ff:fe$4:$5$6/64; done') + self.cmd( + "for i in `ls /sys/class/net/` ; do mac=`cat /sys/class/net/$i/address`; IFS=':'; set $mac; unset IFS; ip address add dev $i scope link fe80::$(printf %02x $((0x$1 ^ 2)))$2:${3}ff:fe$4:$5$6/64; done" + ) + # Now start all the other daemons - for daemon in self.daemons: + for daemon in daemons_list: # Skip disabled daemons and zebra - if self.daemons[daemon] == 0 or daemon == 'zebra' or daemon == 'staticd': + if self.daemons[daemon] == 0: continue + daemon_path = os.path.join(self.daemondir, daemon) - self.cmd('{0} {1} > {2}.out 2> {2}.err &'.format( - daemon_path, self.daemons_options.get(daemon, ''), daemon - )) - self.waitOutput() - logger.debug('{}: {} {} started'.format(self, self.routertype, daemon)) - def getStdErr(self, daemon): - return self.getLog('err', daemon) - def getStdOut(self, daemon): - return self.getLog('out', daemon) - def getLog(self, log, daemon): - return self.cmd('cat {}/{}/{}.{}'.format(self.logdir, self.name, daemon, log)) + self.cmd( + "{0} {1} --log stdout --log-level debug -d > {2}.out 2> {2}.err".format( + daemon_path, self.daemons_options.get(daemon, ""), daemon + ) + ) + logger.debug("{}: {} {} started".format(self, self.routertype, daemon)) + + # Check if daemons are running. + rundaemons = self.cmd("ls -1 /var/run/%s/*.pid" % self.routertype) + if re.search(r"No such file or directory", rundaemons): + return "Daemons are not running" + + return "" + + def killRouterDaemons( + self, daemons, wait=True, assertOnError=True, minErrorVersion="5.1" + ): + # Kill Running Quagga or FRR specific + # Daemons(user specified daemon only) using SIGKILL + rundaemons = self.cmd("ls -1 /var/run/%s/*.pid" % self.routertype) + errors = "" + daemonsNotRunning = [] + if re.search(r"No such file or directory", rundaemons): + return errors + for daemon in daemons: + if rundaemons is not None and daemon in rundaemons: + numRunning = 0 + dmns = rundaemons.split('\n') + # Exclude empty string at end of list + for d in dmns[:-1]: + if re.search(r"%s" % daemon, d): + daemonpid = self.cmd("cat %s" % d.rstrip()).rstrip() + if daemonpid.isdigit() and pid_exists(int(daemonpid)): + logger.info( + "{}: killing {}".format( + self.name, + os.path.basename(d.rstrip().rsplit(".", 1)[0]), + ) + ) + self.cmd("kill -9 %s" % daemonpid) + self.waitOutput() + if pid_exists(int(daemonpid)): + numRunning += 1 + if wait and numRunning > 0: + sleep( + 2, + "{}: waiting for {} daemon to be stopped".format( + self.name, daemon + ), + ) + + # 2nd round of kill if daemons didn't exit + for d in dmns[:-1]: + if re.search(r"%s" % daemon, d): + daemonpid = self.cmd("cat %s" % d.rstrip()).rstrip() + if daemonpid.isdigit() and pid_exists( + int(daemonpid) + ): + logger.info( + "{}: killing {}".format( + self.name, + os.path.basename( + d.rstrip().rsplit(".", 1)[0] + ), + ) + ) + self.cmd("kill -9 %s" % daemonpid) + self.waitOutput() + self.cmd("rm -- {}".format(d.rstrip())) + if wait: + errors = self.checkRouterCores(reportOnce=True) + if self.checkRouterVersion("<", minErrorVersion): + # ignore errors in old versions + errors = "" + if assertOnError and len(errors) > 0: + assert "Errors found - details follow:" == 0, errors + else: + daemonsNotRunning.append(daemon) + if len(daemonsNotRunning) > 0: + errors = errors + "Daemons are not running", daemonsNotRunning + + return errors def checkRouterCores(self, reportLeaks=True, reportOnce=False): if reportOnce and not self.reportCores: @@ -873,33 +1480,49 @@ def checkRouterCores(self, reportLeaks=True, reportOnce=False): reportMade = False traces = "" for daemon in self.daemons: - if (self.daemons[daemon] == 1): + if self.daemons[daemon] == 1: # Look for core file - corefiles = glob.glob('{}/{}/{}_core*.dmp'.format( - self.logdir, self.name, daemon)) - if (len(corefiles) > 0): - daemon_path = os.path.join(self.daemondir, daemon) - backtrace = subprocess.check_output([ - "gdb {} {} --batch -ex bt 2> /dev/null".format(daemon_path, corefiles[0]) - ], shell=True) - sys.stderr.write("\n%s: %s crashed. Core file found - Backtrace follows:\n" % (self.name, daemon)) - sys.stderr.write("%s" % backtrace) - traces = traces + "\n%s: %s crashed. Core file found - Backtrace follows:\n%s" % (self.name, daemon, backtrace) + corefiles = glob.glob( + "{}/{}/{}_core*.dmp".format(self.logdir, self.name, daemon) + ) + if len(corefiles) > 0: + backtrace = gdb_core(self, daemon, corefiles) + traces = ( + traces + + "\n%s: %s crashed. Core file found - Backtrace follows:\n%s" + % (self.name, daemon, backtrace) + ) reportMade = True elif reportLeaks: log = self.getStdErr(daemon) if "memstats" in log: - sys.stderr.write("%s: %s has memory leaks:\n" % (self.name, daemon)) - traces = traces + "\n%s: %s has memory leaks:\n" % (self.name, daemon) + sys.stderr.write( + "%s: %s has memory leaks:\n" % (self.name, daemon) + ) + traces = traces + "\n%s: %s has memory leaks:\n" % ( + self.name, + daemon, + ) log = re.sub("core_handler: ", "", log) - log = re.sub(r"(showing active allocations in memory group [a-zA-Z0-9]+)", r"\n ## \1", log) + log = re.sub( + r"(showing active allocations in memory group [a-zA-Z0-9]+)", + r"\n ## \1", + log, + ) log = re.sub("memstats: ", " ", log) sys.stderr.write(log) reportMade = True # Look for AddressSanitizer Errors and append to /tmp/AddressSanitzer.txt if found - if checkAddressSanitizerError(self.getStdErr(daemon), self.name, daemon): - sys.stderr.write("%s: Daemon %s killed by AddressSanitizer" % (self.name, daemon)) - traces = traces + "\n%s: Daemon %s killed by AddressSanitizer" % (self.name, daemon) + if checkAddressSanitizerError( + self.getStdErr(daemon), self.name, daemon + ): + sys.stderr.write( + "%s: Daemon %s killed by AddressSanitizer" % (self.name, daemon) + ) + traces = traces + "\n%s: Daemon %s killed by AddressSanitizer" % ( + self.name, + daemon, + ) reportMade = True if reportMade: self.reportCores = False @@ -910,7 +1533,9 @@ def checkRouterRunning(self): global fatal_error - daemonsRunning = self.cmd('vtysh -c "show log" | grep "Logging configuration for"') + daemonsRunning = self.cmd( + 'vtysh -c "show logging" | grep "Logging configuration for"' + ) # Look for AddressSanitizer Errors in vtysh output and append to /tmp/AddressSanitzer.txt if found if checkAddressSanitizerError(daemonsRunning, self.name, "vtysh"): return "%s: vtysh killed by AddressSanitizer" % (self.name) @@ -919,32 +1544,46 @@ def checkRouterRunning(self): 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": - sys.stderr.write("You may have a copy of staticd installed but are attempting to test against\n") - sys.stderr.write("a version of FRR that does not have staticd, please cleanup the install dir\n") + sys.stderr.write( + "You may have a copy of staticd installed but are attempting to test against\n" + ) + sys.stderr.write( + "a version of FRR that does not have staticd, please cleanup the install dir\n" + ) # Look for core file - corefiles = glob.glob('{}/{}/{}_core*.dmp'.format( - self.logdir, self.name, daemon)) - if (len(corefiles) > 0): - daemon_path = os.path.join(self.daemondir, daemon) - backtrace = subprocess.check_output([ - "gdb {} {} --batch -ex bt 2> /dev/null".format(daemon_path, corefiles[0]) - ], shell=True) - sys.stderr.write("\n%s: %s crashed. Core file found - Backtrace follows:\n" % (self.name, daemon)) - sys.stderr.write("%s\n" % backtrace) + corefiles = glob.glob( + "{}/{}/{}_core*.dmp".format(self.logdir, self.name, daemon) + ) + if len(corefiles) > 0: + gdb_core(self, daemon, corefiles) else: # No core found - If we find matching logfile in /tmp, then print last 20 lines from it. - if os.path.isfile('{}/{}/{}.log'.format(self.logdir, self.name, daemon)): - log_tail = subprocess.check_output([ - "tail -n20 {}/{}/{}.log 2> /dev/null".format( - self.logdir, self.name, daemon) - ], shell=True) - sys.stderr.write("\nFrom %s %s %s log file:\n" % (self.routertype, self.name, daemon)) + if os.path.isfile( + "{}/{}/{}.log".format(self.logdir, self.name, daemon) + ): + log_tail = subprocess.check_output( + [ + "tail -n20 {}/{}/{}.log 2> /dev/null".format( + self.logdir, self.name, daemon + ) + ], + shell=True, + ) + sys.stderr.write( + "\nFrom %s %s %s log file:\n" + % (self.routertype, self.name, daemon) + ) sys.stderr.write("%s\n" % log_tail) # Look for AddressSanitizer Errors and append to /tmp/AddressSanitzer.txt if found - if checkAddressSanitizerError(self.getStdErr(daemon), self.name, daemon): - return "%s: Daemon %s not running - killed by AddressSanitizer" % (self.name, daemon) + if checkAddressSanitizerError( + self.getStdErr(daemon), self.name, daemon + ): + return "%s: Daemon %s not running - killed by AddressSanitizer" % ( + self.name, + daemon, + ) return "%s: Daemon %s not running" % (self.name, daemon) return "" @@ -964,25 +1603,27 @@ def checkRouterVersion(self, cmpop, version): # Make sure we have version information first if self.version == None: - self.version = self.cmd(os.path.join(self.daemondir, 'bgpd')+' -v').split()[2] - logger.info('{}: running version: {}'.format(self.name,self.version)) + self.version = self.cmd( + os.path.join(self.daemondir, "bgpd") + " -v" + ).split()[2] + logger.info("{}: running version: {}".format(self.name, self.version)) rversion = self.version if rversion is None: return False result = version_cmp(rversion, version) - if cmpop == '>=': + if cmpop == ">=": return result >= 0 - if cmpop == '>': + if cmpop == ">": return result > 0 - if cmpop == '=': + if cmpop == "=": return result == 0 - if cmpop == '<': + if cmpop == "<": return result < 0 - if cmpop == '<': + if cmpop == "<": return result < 0 - if cmpop == '<=': + if cmpop == "<=": return result <= 0 def get_ipv6_linklocal(self): @@ -990,37 +1631,41 @@ def get_ipv6_linklocal(self): linklocal = [] - ifaces = self.cmd('ip -6 address') + ifaces = self.cmd("ip -6 address") # Fix newlines (make them all the same) - ifaces = ('\n'.join(ifaces.splitlines()) + '\n').splitlines() - interface="" - ll_per_if_count=0 + ifaces = ("\n".join(ifaces.splitlines()) + "\n").splitlines() + interface = "" + ll_per_if_count = 0 for line in ifaces: - m = re.search('[0-9]+: ([^:@]+)[@if0-9:]+ <', line) + m = re.search("[0-9]+: ([^:@]+)[@if0-9:]+ <", line) if m: interface = m.group(1) ll_per_if_count = 0 - m = re.search('inet6 (fe80::[0-9a-f]+:[0-9a-f]+:[0-9a-f]+:[0-9a-f]+)[/0-9]* scope link', line) + m = re.search( + "inet6 (fe80::[0-9a-f]+:[0-9a-f]+:[0-9a-f]+:[0-9a-f]+)[/0-9]* scope link", + line, + ) if m: local = m.group(1) ll_per_if_count += 1 - if (ll_per_if_count > 1): + if ll_per_if_count > 1: linklocal += [["%s-%s" % (interface, ll_per_if_count), local]] else: linklocal += [[interface, local]] return linklocal + def daemon_available(self, daemon): "Check if specified daemon is installed (and for ldp if kernel supports MPLS)" daemon_path = os.path.join(self.daemondir, daemon) if not os.path.isfile(daemon_path): return False - if (daemon == 'ldpd'): - if version_cmp(platform.release(), '4.5') < 0: + if daemon == "ldpd": + if version_cmp(platform.release(), "4.5") < 0: return False - if not module_present('mpls-router', load=False): + if not module_present("mpls-router", load=False): return False - if not module_present('mpls-iptunnel', load=False): + if not module_present("mpls-iptunnel", load=False): return False return True @@ -1028,18 +1673,20 @@ def get_routertype(self): "Return the type of Router (frr or quagga)" return self.routertype + def report_memory_leaks(self, filename_prefix, testscript): "Report Memory Leaks to file prefixed with given string" leakfound = False filename = filename_prefix + re.sub(r"\.py", "", testscript) + ".txt" for daemon in self.daemons: - if (self.daemons[daemon] == 1): + if self.daemons[daemon] == 1: log = self.getStdErr(daemon) if "memstats" in log: # Found memory leak - logger.info('\nRouter {} {} StdErr Log:\n{}'.format( - self.name, daemon, log)) + logger.info( + "\nRouter {} {} StdErr Log:\n{}".format(self.name, daemon, log) + ) if not leakfound: leakfound = True # Check if file already exists @@ -1047,17 +1694,25 @@ def report_memory_leaks(self, filename_prefix, testscript): leakfile = open(filename, "a") if not fileexists: # New file - add header - leakfile.write("# Memory Leak Detection for topotest %s\n\n" % testscript) + leakfile.write( + "# Memory Leak Detection for topotest %s\n\n" + % testscript + ) leakfile.write("## Router %s\n" % self.name) leakfile.write("### Process %s\n" % daemon) log = re.sub("core_handler: ", "", log) - log = re.sub(r"(showing active allocations in memory group [a-zA-Z0-9]+)", r"\n#### \1\n", log) + log = re.sub( + r"(showing active allocations in memory group [a-zA-Z0-9]+)", + r"\n#### \1\n", + log, + ) log = re.sub("memstats: ", " ", log) leakfile.write(log) leakfile.write("\n") if leakfound: leakfile.close() + class LinuxRouter(Router): "A Linux Router Node with IPv4/IPv6 forwarding enabled." @@ -1067,25 +1722,26 @@ def __init__(self, name, **params): def config(self, **params): Router.config(self, **params) # Enable forwarding on the router - assert_sysctl(self, 'net.ipv4.ip_forward', 1) - assert_sysctl(self, 'net.ipv6.conf.all.forwarding', 1) + assert_sysctl(self, "net.ipv4.ip_forward", 1) + assert_sysctl(self, "net.ipv6.conf.all.forwarding", 1) # Enable coredumps - assert_sysctl(self, 'kernel.core_uses_pid', 1) - assert_sysctl(self, 'fs.suid_dumpable', 1) - #this applies to the kernel not the namespace... - #original on ubuntu 17.x, but apport won't save as in namespace + assert_sysctl(self, "kernel.core_uses_pid", 1) + assert_sysctl(self, "fs.suid_dumpable", 1) + # this applies to the kernel not the namespace... + # original on ubuntu 17.x, but apport won't save as in namespace # |/usr/share/apport/apport %p %s %c %d %P - corefile = '%e_core-sig_%s-pid_%p.dmp' - assert_sysctl(self, 'kernel.core_pattern', corefile) + corefile = "%e_core-sig_%s-pid_%p.dmp" + assert_sysctl(self, "kernel.core_pattern", corefile) def terminate(self): """ Terminate generic LinuxRouter Mininet instance """ - set_sysctl(self, 'net.ipv4.ip_forward', 0) - set_sysctl(self, 'net.ipv6.conf.all.forwarding', 0) + set_sysctl(self, "net.ipv4.ip_forward", 0) + set_sysctl(self, "net.ipv6.conf.all.forwarding", 0) Router.terminate(self) + class FreeBSDRouter(Router): "A FreeBSD Router Node with IPv4/IPv6 forwarding enabled." @@ -1097,5 +1753,5 @@ class LegacySwitch(OVSSwitch): "A Legacy Switch without OpenFlow" def __init__(self, name, **params): - OVSSwitch.__init__(self, name, failMode='standalone', **params) + OVSSwitch.__init__(self, name, failMode="standalone", **params) self.switchIP = None diff --git a/tests/topotests/lm-proxy-topo1/ce1/bgpd.conf b/tests/topotests/lm-proxy-topo1/ce1/bgpd.conf deleted file mode 100644 index 33cb3d3209..0000000000 --- a/tests/topotests/lm-proxy-topo1/ce1/bgpd.conf +++ /dev/null @@ -1,9 +0,0 @@ -debug bgp vpn label -! -router bgp 9101 - bgp router-id 10.1.1.2 - neighbor 10.1.1.1 remote-as 7777 - address-family ipv4 unicast - redistribute connected - exit-address-family -! diff --git a/tests/topotests/lm-proxy-topo1/ce1/zebra.conf b/tests/topotests/lm-proxy-topo1/ce1/zebra.conf deleted file mode 100644 index 7118cc6b88..0000000000 --- a/tests/topotests/lm-proxy-topo1/ce1/zebra.conf +++ /dev/null @@ -1,11 +0,0 @@ -debug zebra events -debug zebra packet -! -interface lo - ip address 66.1.0.1/32 -! -interface ce1-eth0 - ip address 10.1.1.2/30 -! -ip forwarding -! diff --git a/tests/topotests/lm-proxy-topo1/ce2/bgpd.conf b/tests/topotests/lm-proxy-topo1/ce2/bgpd.conf deleted file mode 100644 index 358be342a4..0000000000 --- a/tests/topotests/lm-proxy-topo1/ce2/bgpd.conf +++ /dev/null @@ -1,9 +0,0 @@ -debug bgp vpn label -! -router bgp 9102 - bgp router-id 10.1.2.2 - neighbor 10.1.2.1 remote-as 7777 - address-family ipv4 unicast - redistribute connected - exit-address-family -! diff --git a/tests/topotests/lm-proxy-topo1/ce2/zebra.conf b/tests/topotests/lm-proxy-topo1/ce2/zebra.conf deleted file mode 100644 index 8b4e3e31d7..0000000000 --- a/tests/topotests/lm-proxy-topo1/ce2/zebra.conf +++ /dev/null @@ -1,11 +0,0 @@ -debug zebra events -debug zebra packet -! -interface lo - ip address 66.1.0.2/32 -! -interface ce2-eth0 - ip address 10.1.2.2/30 -! -ip forwarding -! diff --git a/tests/topotests/lm-proxy-topo1/lm-proxy-topo1.dot b/tests/topotests/lm-proxy-topo1/lm-proxy-topo1.dot deleted file mode 100644 index 71e6f34ba0..0000000000 --- a/tests/topotests/lm-proxy-topo1/lm-proxy-topo1.dot +++ /dev/null @@ -1,102 +0,0 @@ -## Color coding: -######################### -## Main FRR: #f08080 red -## Switches: #d0e0d0 gray -## RIP: #19e3d9 Cyan -## RIPng: #fcb314 dark yellow -## OSPFv2: #32b835 Green -## OSPFv3: #19e3d9 Cyan -## ISIS IPv4 #fcb314 dark yellow -## ISIS IPv6 #9a81ec purple -## BGP IPv4 #eee3d3 beige -## BGP IPv6 #fdff00 yellow -##### Colors (see http://www.color-hex.com/) - -graph template { - label="Test Topology - Label Manager proxy"; - - # Routers - - lm [ shape=doubleoctagon, - label="label manager", - fillcolor="#f08080", - style=filled - ]; - ce1 [ - shape=doubleoctagon, - label="ce1", - fillcolor="#f08080", - style=filled - ]; - ce2 [ - shape=doubleoctagon - label="ce2", - fillcolor="#f08080", - style=filled - ]; - pe1 [ - shape=doubleoctagon, - label="pe1", - fillcolor="#f08080", - style=filled - ]; - pe2 [ - shape=doubleoctagon - label="pe2", - fillcolor="#f08080", - style=filled - ]; - p1 [ - shape=doubleoctagon - label="p1", - fillcolor="#f08080", - style=filled - ]; - - # Switches - - s1 [ - shape=oval, - label="s1\n10.1.1.0/30", - fillcolor="#d0e0d0", - style=filled - ]; - s2 [ - shape=oval, - label="s2\n77.0.1.0/24", - fillcolor="#d0e0d0", - style=filled - ]; - s3 [ - shape=oval, - label="s3\n77.0.2.0/24", - fillcolor="#d0e0d0", - style=filled - ]; - s4 [ - shape=oval, - label="s4\n10.1.2.0/30", - fillcolor="#d0e0d0", - style=filled - ]; - - # Connections - - ce1 -- s1 [label="eth0\n.2"]; - pe1 -- s1 [label="eth0\n.1"]; - - pe1 -- s2 [label="eth1\n.1"]; - p1 -- s2 [label="eth0\n.2"]; - - pe2 -- s3 [label="eth1\n.1"]; - p1 -- s3 [label="eth1\n.2"]; - - ce2 -- s4 [label="eth0\n.2"]; - pe2 -- s4 [label="eth0\n.1"]; - - lm -- ce1; - lm -- pe1; - lm -- p1; - lm -- pe2; - lm -- ce2; -} diff --git a/tests/topotests/lm-proxy-topo1/lm-proxy-topo1.pdf b/tests/topotests/lm-proxy-topo1/lm-proxy-topo1.pdf deleted file mode 100644 index 7f7e09ed09..0000000000 Binary files a/tests/topotests/lm-proxy-topo1/lm-proxy-topo1.pdf and /dev/null differ diff --git a/tests/topotests/lm-proxy-topo1/lm/zebra.conf b/tests/topotests/lm-proxy-topo1/lm/zebra.conf deleted file mode 100644 index 3c14f3bb3a..0000000000 --- a/tests/topotests/lm-proxy-topo1/lm/zebra.conf +++ /dev/null @@ -1,3 +0,0 @@ -debug zebra events -debug zebra packet -! diff --git a/tests/topotests/lm-proxy-topo1/p1/ldpd.conf b/tests/topotests/lm-proxy-topo1/p1/ldpd.conf deleted file mode 100644 index f86c47e6cc..0000000000 --- a/tests/topotests/lm-proxy-topo1/p1/ldpd.conf +++ /dev/null @@ -1,9 +0,0 @@ -! -mpls ldp - router-id 7.0.0.101 - address-family ipv4 - discovery transport-address 7.0.0.101 - interface p1-eth0 - interface p1-eth1 - exit-address-family -! diff --git a/tests/topotests/lm-proxy-topo1/p1/ospfd.conf b/tests/topotests/lm-proxy-topo1/p1/ospfd.conf deleted file mode 100644 index 721426cb15..0000000000 --- a/tests/topotests/lm-proxy-topo1/p1/ospfd.conf +++ /dev/null @@ -1,7 +0,0 @@ -! -router ospf - ospf router-id 7.0.0.101 - network 7.0.0.101/32 area 0 - network 77.0.1.0/24 area 0 - network 77.0.2.0/24 area 0 -! diff --git a/tests/topotests/lm-proxy-topo1/p1/zebra.conf b/tests/topotests/lm-proxy-topo1/p1/zebra.conf deleted file mode 100644 index bb2fc2f96e..0000000000 --- a/tests/topotests/lm-proxy-topo1/p1/zebra.conf +++ /dev/null @@ -1,12 +0,0 @@ -debug zebra events -debug zebra packet -! -interface lo - ip address 7.0.0.101/32 -! -interface p1-eth0 - ip address 77.0.1.2/24 -! -interface p1-eth1 - ip address 77.0.2.2/24 -! diff --git a/tests/topotests/lm-proxy-topo1/pe1/bgpd.conf b/tests/topotests/lm-proxy-topo1/pe1/bgpd.conf deleted file mode 100644 index b9bab30c30..0000000000 --- a/tests/topotests/lm-proxy-topo1/pe1/bgpd.conf +++ /dev/null @@ -1,26 +0,0 @@ -debug bgp vpn label -! -router bgp 7777 - bgp router-id 7.0.0.1 - neighbor 7.0.0.2 remote-as 7777 - neighbor 7.0.0.2 update-source 7.0.0.1 - address-family ipv4 unicast - no neighbor 7.0.0.2 activate - exit-address-family - address-family ipv4 vpn - neighbor 7.0.0.2 activate - exit-address-family -! -router bgp 7777 vrf A - bgp router-id 10.1.1.1 - neighbor 10.1.1.2 remote-as 9101 - address-family ipv4 unicast - label vpn export auto - rd vpn export 110:1 - rt vpn both 152:100 - import vpn - export vpn - redistribute connected - redistribute ospf - exit-address-family -! diff --git a/tests/topotests/lm-proxy-topo1/pe1/ldpd.conf b/tests/topotests/lm-proxy-topo1/pe1/ldpd.conf deleted file mode 100644 index 1b11f19fd3..0000000000 --- a/tests/topotests/lm-proxy-topo1/pe1/ldpd.conf +++ /dev/null @@ -1,8 +0,0 @@ -! -mpls ldp - router-id 7.0.0.1 - address-family ipv4 - discovery transport-address 7.0.0.1 - interface pe1-eth1 - exit-address-family -! diff --git a/tests/topotests/lm-proxy-topo1/pe1/ospfd.conf b/tests/topotests/lm-proxy-topo1/pe1/ospfd.conf deleted file mode 100644 index 334c2547bf..0000000000 --- a/tests/topotests/lm-proxy-topo1/pe1/ospfd.conf +++ /dev/null @@ -1,6 +0,0 @@ -! -router ospf - ospf router-id 7.0.0.1 - network 77.0.0.0/16 area 0 - redistribute connected -! diff --git a/tests/topotests/lm-proxy-topo1/pe1/zebra.conf b/tests/topotests/lm-proxy-topo1/pe1/zebra.conf deleted file mode 100644 index ec1413234b..0000000000 --- a/tests/topotests/lm-proxy-topo1/pe1/zebra.conf +++ /dev/null @@ -1,14 +0,0 @@ -debug zebra events -debug zebra packet -! -interface lo - ip address 7.0.0.1/32 -! -interface pe1-eth0 vrf A - ip address 10.1.1.1/30 -! -interface pe1-eth1 - ip address 77.0.1.1/24 -! -ip forwarding -! diff --git a/tests/topotests/lm-proxy-topo1/pe2/bgpd.conf b/tests/topotests/lm-proxy-topo1/pe2/bgpd.conf deleted file mode 100644 index f1ec2a48e8..0000000000 --- a/tests/topotests/lm-proxy-topo1/pe2/bgpd.conf +++ /dev/null @@ -1,26 +0,0 @@ -debug bgp vpn label -! -router bgp 7777 - bgp router-id 7.0.0.2 - neighbor 7.0.0.1 remote-as 7777 - neighbor 7.0.0.1 update-source 7.0.0.2 - address-family ipv4 unicast - no neighbor 7.0.0.1 activate - exit-address-family - address-family ipv4 vpn - neighbor 7.0.0.1 activate - exit-address-family -! -router bgp 7777 vrf A - bgp router-id 10.1.2.1 - neighbor 10.1.2.2 remote-as 9102 - address-family ipv4 unicast - label vpn export auto - rd vpn export 110:2 - rt vpn both 152:100 - import vpn - export vpn - redistribute connected - redistribute ospf - exit-address-family -! diff --git a/tests/topotests/lm-proxy-topo1/pe2/ldpd.conf b/tests/topotests/lm-proxy-topo1/pe2/ldpd.conf deleted file mode 100644 index 727364fe9f..0000000000 --- a/tests/topotests/lm-proxy-topo1/pe2/ldpd.conf +++ /dev/null @@ -1,8 +0,0 @@ -! -mpls ldp - router-id 7.0.0.2 - address-family ipv4 - discovery transport-address 7.0.0.2 - interface pe2-eth1 - exit-address-family -! diff --git a/tests/topotests/lm-proxy-topo1/pe2/ospfd.conf b/tests/topotests/lm-proxy-topo1/pe2/ospfd.conf deleted file mode 100644 index 068de09a98..0000000000 --- a/tests/topotests/lm-proxy-topo1/pe2/ospfd.conf +++ /dev/null @@ -1,6 +0,0 @@ -! -router ospf - ospf router-id 7.0.0.2 - network 77.0.0.0/16 area 0 - redistribute connected -! diff --git a/tests/topotests/lm-proxy-topo1/pe2/zebra.conf b/tests/topotests/lm-proxy-topo1/pe2/zebra.conf deleted file mode 100644 index 0302fd8018..0000000000 --- a/tests/topotests/lm-proxy-topo1/pe2/zebra.conf +++ /dev/null @@ -1,14 +0,0 @@ -debug zebra events -debug zebra packet -! -interface lo - ip address 7.0.0.2/32 -! -interface pe2-eth1 - ip address 77.0.2.1/24 -! -interface pe2-eth0 vrf A - ip address 10.1.2.1/30 -! -ip forwarding -! diff --git a/tests/topotests/lm-proxy-topo1/test_lm-proxy-topo1.py b/tests/topotests/lm-proxy-topo1/test_lm-proxy-topo1.py deleted file mode 100644 index 7acbb1ee62..0000000000 --- a/tests/topotests/lm-proxy-topo1/test_lm-proxy-topo1.py +++ /dev/null @@ -1,205 +0,0 @@ -#!/usr/bin/env python - -# -# test_lm-proxy-topo1.py -# Part of NetDEF Topology Tests -# -# Copyright (c) 2018 by Volta Networks, Inc. -# -# Requirements, so the test is not skipped: -# - Linux kernel with VRF support -# - 'ip' command with VRF support (e.g. iproute2-ss180129 works) -# - FRR BGP daemon supporting label manager using instance id -# -# Permission to use, copy, modify, and/or distribute this software -# for any purpose with or without fee is hereby granted, provided -# that the above copyright notice and this permission notice appear -# in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY -# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, -# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS -# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE -# OF THIS SOFTWARE. -# - -import os -import sys -import pytest - -from functools import partial - -# Save the Current Working Directory to find configuration files. -CWD = os.path.dirname(os.path.realpath(__file__)) -sys.path.append(os.path.join(CWD, '../')) - -# pylint: disable=C0413 -# Import topogen and topotest helpers -from lib import topotest -from lib.topogen import Topogen, TopoRouter, get_topogen -from lib.topolog import logger - -# Required to instantiate the topology builder class. -from mininet.topo import Topo - -vrf_name = 'A' - -class NetworkTopo(Topo): - "Label Manager proxy topology" - - # Relationship between routers, switches, and hosts - def build(self, *_args, **_opts): - "Build function" - - tgen = get_topogen(self) - - # FRR routers - - for router in ['lm', 'ce1', 'pe1', 'p1', 'pe2', 'ce2']: - tgen.add_router(router); - - # Connections - - switch = tgen.add_switch('s1') - switch.add_link(tgen.gears['ce1'], nodeif='ce1-eth0') - switch.add_link(tgen.gears['pe1'], nodeif='pe1-eth0') - - switch = tgen.add_switch('s2') - switch.add_link(tgen.gears['pe1'], nodeif='pe1-eth1') - switch.add_link(tgen.gears['p1'], nodeif='p1-eth0') - - switch = tgen.add_switch('s3') - switch.add_link(tgen.gears['p1'], nodeif='p1-eth1') - switch.add_link(tgen.gears['pe2'], nodeif='pe2-eth1') - - switch = tgen.add_switch('s4') - switch.add_link(tgen.gears['ce2'], nodeif='ce2-eth0') - switch.add_link(tgen.gears['pe2'], nodeif='pe2-eth0') - -# Test environment handling - -def vrf_destroy(router, vrf): - router.run('ip link delete dev ' + vrf) - -def vrf_setup(router, eth_in, vrf, vrf_table): - cmds = ['ip link set dev lo up', - 'echo 10000 > /proc/sys/net/mpls/platform_labels', - 'ip link add dev ' + vrf + ' type vrf table ' + vrf_table, - 'ip link set ' + vrf + ' up', - 'ip link set ' + eth_in + ' vrf ' + vrf, - 'echo 1 > /proc/sys/net/mpls/conf/' + vrf + '/input' - ] - vrf_destroy(router, vrf) - for cmd in cmds: - logger.info('[vrf_setup] cmd: ' + cmd) - out = router.run(cmd) - if out != None and len(out) > 0: - logger.info('[vrf_setup] "{}" error: out="{}"'.format(cmd, out)) - -def setup_module(mod): - "pytest environment setup" - - tgen = Topogen(NetworkTopo, mod.__name__) - tgen.start_topology() - - router_list = tgen.routers() - - # Load router configuration - - ldp_id = 1 - bgp_id = 101 - lm_sock = '../lm/label_mgr.sock' - - for rname, router in router_list.iteritems(): - if rname == 'lm' : - router.load_config( - TopoRouter.RD_ZEBRA, - os.path.join(CWD, '{}/zebra.conf'.format(rname)), - '-z ' + lm_sock - ) - continue - - rtype = ''.join([i for i in rname if not i.isdigit()]) - - router.load_config( - TopoRouter.RD_ZEBRA, - os.path.join(CWD, '{}/zebra.conf'.format(rname)), - '-l ' + lm_sock - ) - - if router.check_capability(TopoRouter.RD_ZEBRA, '--vrfwnetns') == False: - return pytest.skip('Skipping test: no VRF support') - - if rtype == 'ce' or rtype == 'pe': - if router.check_capability(TopoRouter.RD_BGP, '--int_num') == False: - return pytest.skip('Skipping test: no BGP LM support') - router.load_config( - TopoRouter.RD_BGP, - os.path.join(CWD, '{}/bgpd.conf'.format(rname)), - '-I %d' % bgp_id - ) - bgp_id += 1 - - if rtype == 'pe' or rtype == 'p': - router.load_config( - TopoRouter.RD_OSPF, - os.path.join(CWD, '{}/ospfd.conf'.format(rname)) - ) - router.load_config( - TopoRouter.RD_LDP, - os.path.join(CWD, '{}/ldpd.conf'.format(rname)), - '-n %d' % ldp_id - ) - ldp_id += 1 - - # Prepare VRF's - - router = tgen.gears['pe1'] - out = router.run('ip -h 2>&1 | grep vrf | wc -l') - if int(out) == 0: - return pytest.skip('Skipping test: ip/iproute2 has no VRF support') - - vrf_setup(tgen.gears['pe1'], 'pe1-eth0', vrf_name, '1') - vrf_setup(tgen.gears['pe2'], 'pe2-eth0', vrf_name, '1') - - # Start routers - - tgen.start_router(tgen.gears['lm']) - for rname, router in router_list.iteritems(): - if rname != 'lm': - tgen.start_router(router) - -def teardown_module(mod): - tgen = get_topogen() - for router in ['pe1', 'pe2']: - vrf_destroy(tgen.gears[router], vrf_name) - tgen.stop_topology() - -def test_lm_proxy(): - logger.info('Test: label manager (LDP and BGP)') - tgen = get_topogen() - - # Skip if previous fatal error condition is raised - if tgen.routers_have_failure(): - pytest.skip(tgen.errors) - - cmd = 'show mpls ldp binding' - - router = tgen.gears['p1'] - - def check_labels(router, cmd): - output = router.vtysh_cmd(cmd, isjson=False) - logger.info('chk_labels [' + cmd + ']: ' + output) - return output.count('\n') - - test_func = partial(check_labels, router, cmd) - result, diff = topotest.run_and_expect(test_func, 12, count=6, wait=30) - assert result, 'wrong labels' - -if __name__ == '__main__': - args = ["-s"] + sys.argv[1:] - sys.exit(pytest.main(args)) - diff --git a/tests/topotests/ospf-sr-topo1/r1/ospf_srdb.json b/tests/topotests/ospf-sr-topo1/r1/ospf_srdb.json index 4ffca6f940..952a26ed10 100644 --- a/tests/topotests/ospf-sr-topo1/r1/ospf_srdb.json +++ b/tests/topotests/ospf-sr-topo1/r1/ospf_srdb.json @@ -3,8 +3,10 @@ "srNodes":[ { "routerID":"10.0.255.2", - "srgbSize":20000, - "srgbLabel":8000, + "srgbSize":8000, + "srgbLabel":16000, + "srlbSize":1000, + "srlbLabel":15000, "algorithms":[ { "0":"SPF" @@ -15,9 +17,18 @@ "prefix":"10.0.255.2\/32", "sid":200, "inputLabel":20200, - "outputLabel":"pop", - "interface":"r1-eth0", - "nexthop":"10.0.1.2" + "prefixRoute":[ + { + "outputLabel":3, + "interface":"r1-eth0", + "nexthop":"10.0.0.2" + }, + { + "outputLabel":3, + "interface":"r1-eth1", + "nexthop":"10.0.1.2" + } + ] } ] }, @@ -25,6 +36,8 @@ "routerID":"10.0.255.4", "srgbSize":10000, "srgbLabel":10000, + "srlbSize":1000, + "srlbLabel":5000, "algorithms":[ { "0":"SPF" @@ -36,9 +49,18 @@ "prefix":"10.0.255.4\/32", "sid":400, "inputLabel":20400, - "outputLabel":"8400", - "interface":"r1-eth0", - "nexthop":"10.0.1.2" + "prefixRoute":[ + { + "outputLabel":16400, + "interface":"r1-eth0", + "nexthop":"10.0.0.2" + }, + { + "outputLabel":16400, + "interface":"r1-eth1", + "nexthop":"10.0.1.2" + } + ] } ] }, @@ -46,6 +68,8 @@ "routerID":"10.0.255.3", "srgbSize":10000, "srgbLabel":10000, + "srlbSize":1000, + "srlbLabel":5000, "algorithms":[ { "0":"SPF" @@ -57,9 +81,18 @@ "prefix":"10.0.255.3\/32", "sid":300, "inputLabel":20300, - "outputLabel":"8300", - "interface":"r1-eth0", - "nexthop":"10.0.1.2" + "prefixRoute":[ + { + "outputLabel":16300, + "interface":"r1-eth0", + "nexthop":"10.0.0.2" + }, + { + "outputLabel":16300, + "interface":"r1-eth1", + "nexthop":"10.0.1.2" + } + ] } ] }, @@ -67,6 +100,8 @@ "routerID":"10.0.255.1", "srgbSize":10000, "srgbLabel":20000, + "srlbSize":1000, + "srlbLabel":15000, "algorithms":[ { "0":"SPF" @@ -77,27 +112,47 @@ { "prefix":"10.0.255.1\/32", "sid":100, - "inputLabel":20100, - "outputLabel":"pop", - "interface":"lo", - "nexthop":"10.0.255.1" + "inputLabel":0, + "prefixRoute":[ + { + "outputLabel":0, + "interface":"lo", + "nexthop":"10.0.255.1" + } + ] } ], "extendedLink":[ { - "prefix":"10.0.1.1\/32", - "sid":50001, - "inputLabel":50001, - "outputLabel":"pop", + "prefix":"10.0.0.1\/32", + "sid":"*", + "inputLabel":"*", + "outputLabel":3, "interface":"r1-eth0", + "nexthop":"10.0.0.2" + }, + { + "prefix":"10.0.0.1\/32", + "sid":"*", + "inputLabel":"*", + "outputLabel":3, + "interface":"r1-eth0", + "nexthop":"10.0.0.2" + }, + { + "prefix":"10.0.1.1\/32", + "sid":"*", + "inputLabel":"*", + "outputLabel":3, + "interface":"r1-eth1", "nexthop":"10.0.1.2" }, { "prefix":"10.0.1.1\/32", - "sid":50000, - "inputLabel":50000, - "outputLabel":"pop", - "interface":"r1-eth0", + "sid":"*", + "inputLabel":"*", + "outputLabel":3, + "interface":"r1-eth1", "nexthop":"10.0.1.2" } ] diff --git a/tests/topotests/ospf-sr-topo1/r1/ospfd.conf b/tests/topotests/ospf-sr-topo1/r1/ospfd.conf index e8593d1a1a..0773153a76 100644 --- a/tests/topotests/ospf-sr-topo1/r1/ospfd.conf +++ b/tests/topotests/ospf-sr-topo1/r1/ospfd.conf @@ -1,8 +1,18 @@ +debug ospf sr ! interface lo ip ospf area 0.0.0.0 ! interface r1-eth0 + ip ospf network point-to-point + ip ospf hello-interval 2 + ip ospf dead-interval 10 + ip ospf area 0.0.0.0 +! +interface r1-eth1 + ip ospf network point-to-point + ip ospf hello-interval 2 + ip ospf dead-interval 10 ip ospf area 0.0.0.0 ! router ospf @@ -12,6 +22,6 @@ router ospf segment-routing on segment-routing node-msd 16 segment-routing global-block 20000 29999 - segment-routing prefix 10.0.255.1/32 index 100 no-php-flag + segment-routing prefix 10.0.255.1/32 index 100 explicit-null ! diff --git a/tests/topotests/ospf-sr-topo1/r1/zebra.conf b/tests/topotests/ospf-sr-topo1/r1/zebra.conf index f1fcc7dd3b..faf71db25c 100644 --- a/tests/topotests/ospf-sr-topo1/r1/zebra.conf +++ b/tests/topotests/ospf-sr-topo1/r1/zebra.conf @@ -3,6 +3,9 @@ interface lo ip address 10.0.255.1/32 ! interface r1-eth0 + ip address 10.0.0.1/24 +! +interface r1-eth1 ip address 10.0.1.1/24 ! ip forwarding diff --git a/tests/topotests/ospf-sr-topo1/r1/zebra_mpls.json b/tests/topotests/ospf-sr-topo1/r1/zebra_mpls.json index 254c137acd..6c87596acb 100644 --- a/tests/topotests/ospf-sr-topo1/r1/zebra_mpls.json +++ b/tests/topotests/ospf-sr-topo1/r1/zebra_mpls.json @@ -1,80 +1,115 @@ -{ - "20100":{ - "inLabel":20100, +[ + { + "inLabel":20200, "installed":true, "nexthops":[ { - "type":"SR", + "type":"SR (OSPF)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.0.1.2" + }, + { + "type":"SR (OSPF)", "outLabel":3, "distance":150, "installed":true, - "nexthop":"10.0.255.1" + "nexthop":"10.0.0.2" } ] }, - "20200":{ - "inLabel":20200, + { + "inLabel":20300, "installed":true, "nexthops":[ { - "type":"SR", - "outLabel":3, + "type":"SR (OSPF)", + "outLabel":16300, + "outLabelStack":[ + 16300 + ], "distance":150, "installed":true, "nexthop":"10.0.1.2" } ] }, - "20300":{ - "inLabel":20300, + { + "inLabel":20400, "installed":true, "nexthops":[ { - "type":"SR", - "outLabel":8300, + "type":"SR (OSPF)", + "outLabel":16400, + "outLabelStack":[ + 16400 + ], "distance":150, "installed":true, "nexthop":"10.0.1.2" } ] }, - "20400":{ - "inLabel":20400, + { + "inLabel":"*", "installed":true, "nexthops":[ { - "type":"SR", - "outLabel":8400, + "type":"SR (OSPF)", + "outLabel":3, "distance":150, "installed":true, - "nexthop":"10.0.1.2" + "nexthop":"10.0.0.2" + } + ] + }, + { + "inLabel":"*", + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":3, + "distance":150, + "installed":true, + "nexthop":"10.0.0.2" } ] }, - "50000":{ - "inLabel":50000, + { + "inLabel":"*", "installed":true, "nexthops":[ { - "type":"SR", + "type":"SR (OSPF)", "outLabel":3, + "outLabelStack":[ + 3 + ], "distance":150, "installed":true, "nexthop":"10.0.1.2" } ] }, - "50001":{ - "inLabel":50001, + { + "inLabel":"*", "installed":true, "nexthops":[ { - "type":"SR", + "type":"SR (OSPF)", "outLabel":3, + "outLabelStack":[ + 3 + ], "distance":150, "installed":true, "nexthop":"10.0.1.2" } ] } -} +] diff --git a/tests/topotests/ospf-sr-topo1/r2/ospf_srdb.json b/tests/topotests/ospf-sr-topo1/r2/ospf_srdb.json index 2548299cc7..1de780d84e 100644 --- a/tests/topotests/ospf-sr-topo1/r2/ospf_srdb.json +++ b/tests/topotests/ospf-sr-topo1/r2/ospf_srdb.json @@ -3,8 +3,10 @@ "srNodes":[ { "routerID":"10.0.255.2", - "srgbSize":20000, - "srgbLabel":8000, + "srgbSize":8000, + "srgbLabel":16000, + "srlbSize":1000, + "srlbLabel":15000, "algorithms":[ { "0":"SPF" @@ -15,59 +17,79 @@ "prefix":"10.0.255.2\/32", "sid":200, "inputLabel":0, - "outputLabel":"0", - "interface":"lo", - "nexthop":"10.0.255.2" + "prefixRoute":[ + { + "outputLabel":0, + "interface":"lo", + "nexthop":"10.0.255.2" + } + ] } ], "extendedLink":[ { "prefix":"10.0.4.2\/32", - "sid":50001, - "inputLabel":50001, - "outputLabel":"pop", - "interface":"r2-eth2", + "sid":"*", + "inputLabel":"*", + "outputLabel":3, + "interface":"r2-eth3", "nexthop":"10.0.4.1" }, { "prefix":"10.0.4.2\/32", - "sid":50000, - "inputLabel":50000, - "outputLabel":"pop", - "interface":"r2-eth2", + "sid":"*", + "inputLabel":"*", + "outputLabel":3, + "interface":"r2-eth3", "nexthop":"10.0.4.1" }, { - "prefix":"10.0.3.2\/32", - "sid":50003, - "inputLabel":50003, - "outputLabel":"pop", - "interface":"r2-eth1", - "nexthop":"10.0.3.1" + "prefix":"10.0.0.2\/32", + "sid":"*", + "inputLabel":"*", + "outputLabel":3, + "interface":"r2-eth0", + "nexthop":"10.0.0.1" }, { - "prefix":"10.0.3.2\/32", - "sid":50002, - "inputLabel":50002, - "outputLabel":"pop", - "interface":"r2-eth1", - "nexthop":"10.0.3.1" + "prefix":"10.0.0.2\/32", + "sid":"*", + "inputLabel":"*", + "outputLabel":3, + "interface":"r2-eth0", + "nexthop":"10.0.0.1" }, { "prefix":"10.0.1.2\/32", - "sid":50005, - "inputLabel":50005, - "outputLabel":"pop", - "interface":"r2-eth0", + "sid":"*", + "inputLabel":"*", + "outputLabel":3, + "interface":"r2-eth1", "nexthop":"10.0.1.1" }, { "prefix":"10.0.1.2\/32", - "sid":50004, - "inputLabel":50004, - "outputLabel":"pop", - "interface":"r2-eth0", + "sid":"*", + "inputLabel":"*", + "outputLabel":3, + "interface":"r2-eth1", "nexthop":"10.0.1.1" + }, + { + "prefix":"10.0.3.2\/32", + "sid":"*", + "inputLabel":"*", + "outputLabel":3, + "interface":"r2-eth2", + "nexthop":"10.0.3.1" + }, + { + "prefix":"10.0.3.2\/32", + "sid":"*", + "inputLabel":"*", + "outputLabel":3, + "interface":"r2-eth2", + "nexthop":"10.0.3.1" } ] }, @@ -75,6 +97,8 @@ "routerID":"10.0.255.4", "srgbSize":10000, "srgbLabel":10000, + "srlbSize":1000, + "srlbLabel":5000, "algorithms":[ { "0":"SPF" @@ -85,10 +109,14 @@ { "prefix":"10.0.255.4\/32", "sid":400, - "inputLabel":8400, - "outputLabel":"10400", - "interface":"r2-eth2", - "nexthop":"10.0.4.1" + "inputLabel":16400, + "prefixRoute":[ + { + "outputLabel":10400, + "interface":"r2-eth3", + "nexthop":"10.0.4.1" + } + ] } ] }, @@ -96,6 +124,8 @@ "routerID":"10.0.255.3", "srgbSize":10000, "srgbLabel":10000, + "srlbSize":1000, + "srlbLabel":5000, "algorithms":[ { "0":"SPF" @@ -106,10 +136,14 @@ { "prefix":"10.0.255.3\/32", "sid":300, - "inputLabel":8300, - "outputLabel":"pop", - "interface":"r2-eth1", - "nexthop":"10.0.3.1" + "inputLabel":16300, + "prefixRoute":[ + { + "outputLabel":3, + "interface":"r2-eth2", + "nexthop":"10.0.3.1" + } + ] } ] }, @@ -117,6 +151,8 @@ "routerID":"10.0.255.1", "srgbSize":10000, "srgbLabel":20000, + "srlbSize":1000, + "srlbLabel":15000, "algorithms":[ { "0":"SPF" @@ -127,10 +163,19 @@ { "prefix":"10.0.255.1\/32", "sid":100, - "inputLabel":8100, - "outputLabel":"20100", - "interface":"r2-eth0", - "nexthop":"10.0.1.1" + "inputLabel":16100, + "prefixRoute":[ + { + "outputLabel":0, + "interface":"r2-eth0", + "nexthop":"10.0.0.1" + }, + { + "outputLabel":0, + "interface":"r2-eth1", + "nexthop":"10.0.1.1" + } + ] } ] } diff --git a/tests/topotests/ospf-sr-topo1/r2/ospfd.conf b/tests/topotests/ospf-sr-topo1/r2/ospfd.conf index 8555ea29f1..92dc2f7cd1 100644 --- a/tests/topotests/ospf-sr-topo1/r2/ospfd.conf +++ b/tests/topotests/ospf-sr-topo1/r2/ospfd.conf @@ -1,16 +1,30 @@ ! +debug ospf sr +! interface lo ip ospf area 0.0.0.0 ! interface r2-eth0 + ip ospf network point-to-point + ip ospf hello-interval 2 + ip ospf dead-interval 10 ip ospf area 0.0.0.0 ! interface r2-eth1 ip ospf network point-to-point + ip ospf hello-interval 2 + ip ospf dead-interval 10 ip ospf area 0.0.0.0 ! interface r2-eth2 + ip ospf area 0.0.0.0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +interface r2-eth3 ip ospf network point-to-point + ip ospf hello-interval 2 + ip ospf dead-interval 10 ip ospf area 0.0.0.0 ! router ospf diff --git a/tests/topotests/ospf-sr-topo1/r2/zebra.conf b/tests/topotests/ospf-sr-topo1/r2/zebra.conf index f89548d8c5..ba1d833f50 100644 --- a/tests/topotests/ospf-sr-topo1/r2/zebra.conf +++ b/tests/topotests/ospf-sr-topo1/r2/zebra.conf @@ -3,12 +3,15 @@ interface lo ip address 10.0.255.2/32 ! interface r2-eth0 - ip address 10.0.1.2/24 + ip address 10.0.0.2/24 ! interface r2-eth1 - ip address 10.0.3.2/24 + ip address 10.0.1.2/24 ! interface r2-eth2 + ip address 10.0.3.2/24 +! +interface r2-eth3 ip address 10.0.4.2/24 ! ip forwarding diff --git a/tests/topotests/ospf-sr-topo1/r2/zebra_mpls.json b/tests/topotests/ospf-sr-topo1/r2/zebra_mpls.json index 0d73a409ed..a885e88fc5 100644 --- a/tests/topotests/ospf-sr-topo1/r2/zebra_mpls.json +++ b/tests/topotests/ospf-sr-topo1/r2/zebra_mpls.json @@ -1,119 +1,179 @@ -{ - "8100":{ - "inLabel":8100, +[ + { + "inLabel":16100, "installed":true, "nexthops":[ { - "type":"SR", - "outLabel":20100, + "type":"SR (OSPF)", + "outLabel":0, + "outLabelStack":[ + 0 + ], "distance":150, "installed":true, "nexthop":"10.0.1.1" + }, + { + "type":"SR (OSPF)", + "outLabel":0, + "distance":150, + "installed":true, + "nexthop":"10.0.0.1" } ] }, - "8300":{ - "inLabel":8300, + { + "inLabel":16300, "installed":true, "nexthops":[ { - "type":"SR", + "type":"SR (OSPF)", "outLabel":3, + "outLabelStack":[ + 3 + ], "distance":150, "installed":true, "nexthop":"10.0.3.1" } ] }, - "8400":{ - "inLabel":8400, + { + "inLabel":16400, "installed":true, "nexthops":[ { - "type":"SR", + "type":"SR (OSPF)", "outLabel":10400, + "outLabelStack":[ + 10400 + ], "distance":150, "installed":true, "nexthop":"10.0.4.1" } ] }, - "50000":{ - "inLabel":50000, + { + "inLabel":"*", "installed":true, "nexthops":[ { - "type":"SR", + "type":"SR (OSPF)", "outLabel":3, + "outLabelStack":[ + 3 + ], "distance":150, "installed":true, "nexthop":"10.0.4.1" } ] }, - "50001":{ - "inLabel":50001, + { + "inLabel":"*", "installed":true, "nexthops":[ { - "type":"SR", + "type":"SR (OSPF)", "outLabel":3, + "outLabelStack":[ + 3 + ], "distance":150, "installed":true, "nexthop":"10.0.4.1" } ] }, - "50002":{ - "inLabel":50002, + { + "inLabel":"*", "installed":true, "nexthops":[ { - "type":"SR", + "type":"SR (OSPF)", "outLabel":3, + "outLabelStack":[ + 3 + ], "distance":150, "installed":true, - "nexthop":"10.0.3.1" + "nexthop":"10.0.0.1" } ] }, - "50003":{ - "inLabel":50003, + { + "inLabel":"*", "installed":true, "nexthops":[ { - "type":"SR", + "type":"SR (OSPF)", "outLabel":3, + "outLabelStack":[ + 3 + ], "distance":150, "installed":true, - "nexthop":"10.0.3.1" + "nexthop":"10.0.0.1" } ] }, - "50004":{ - "inLabel":50004, + { + "inLabel":"*", "installed":true, "nexthops":[ { - "type":"SR", + "type":"SR (OSPF)", "outLabel":3, + "outLabelStack":[ + 3 + ], "distance":150, "installed":true, "nexthop":"10.0.1.1" } ] }, - "50005":{ - "inLabel":50005, + { + "inLabel":"*", "installed":true, "nexthops":[ { - "type":"SR", + "type":"SR (OSPF)", "outLabel":3, + "outLabelStack":[ + 3 + ], "distance":150, "installed":true, "nexthop":"10.0.1.1" } ] + }, + { + "inLabel":"*", + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":3, + "distance":150, + "installed":true, + "nexthop":"10.0.3.1" + } + ] + }, + { + "inLabel":"*", + "installed":true, + "nexthops":[ + { + "type":"SR (OSPF)", + "outLabel":3, + "distance":150, + "installed":true, + "nexthop":"10.0.3.1" + } + ] } -} +] diff --git a/tests/topotests/ospf-sr-topo1/r3/ospf_srdb.json b/tests/topotests/ospf-sr-topo1/r3/ospf_srdb.json index 4b618cc7ad..e7371ff593 100644 --- a/tests/topotests/ospf-sr-topo1/r3/ospf_srdb.json +++ b/tests/topotests/ospf-sr-topo1/r3/ospf_srdb.json @@ -3,8 +3,10 @@ "srNodes":[ { "routerID":"10.0.255.2", - "srgbSize":20000, - "srgbLabel":8000, + "srgbSize":8000, + "srgbLabel":16000, + "srlbSize":1000, + "srlbLabel":15000, "algorithms":[ { "0":"SPF" @@ -15,9 +17,13 @@ "prefix":"10.0.255.2\/32", "sid":200, "inputLabel":10200, - "outputLabel":"pop", - "interface":"r3-eth0", - "nexthop":"10.0.3.2" + "prefixRoute":[ + { + "outputLabel":3, + "interface":"r3-eth0", + "nexthop":"10.0.3.2" + } + ] } ] }, @@ -25,6 +31,8 @@ "routerID":"10.0.255.4", "srgbSize":10000, "srgbLabel":10000, + "srlbSize":1000, + "srlbLabel":5000, "algorithms":[ { "0":"SPF" @@ -36,9 +44,13 @@ "prefix":"10.0.255.4\/32", "sid":400, "inputLabel":10400, - "outputLabel":"8400", - "interface":"r3-eth0", - "nexthop":"10.0.3.2" + "prefixRoute":[ + { + "outputLabel":16400, + "interface":"r3-eth0", + "nexthop":"10.0.3.2" + } + ] } ] }, @@ -46,6 +58,8 @@ "routerID":"10.0.255.3", "srgbSize":10000, "srgbLabel":10000, + "srlbSize":1000, + "srlbLabel":5000, "algorithms":[ { "0":"SPF" @@ -57,25 +71,29 @@ "prefix":"10.0.255.3\/32", "sid":300, "inputLabel":0, - "outputLabel":"0", - "interface":"lo", - "nexthop":"10.0.255.3" + "prefixRoute":[ + { + "outputLabel":0, + "interface":"lo", + "nexthop":"10.0.255.3" + } + ] } ], "extendedLink":[ { "prefix":"10.0.3.1\/32", - "sid":50001, - "inputLabel":50001, - "outputLabel":"pop", + "sid":"*", + "inputLabel":"*", + "outputLabel":3, "interface":"r3-eth0", "nexthop":"10.0.3.2" }, { "prefix":"10.0.3.1\/32", - "sid":50000, - "inputLabel":50000, - "outputLabel":"pop", + "sid":"*", + "inputLabel":"*", + "outputLabel":3, "interface":"r3-eth0", "nexthop":"10.0.3.2" } @@ -85,6 +103,8 @@ "routerID":"10.0.255.1", "srgbSize":10000, "srgbLabel":20000, + "srlbSize":1000, + "srlbLabel":15000, "algorithms":[ { "0":"SPF" @@ -96,9 +116,13 @@ "prefix":"10.0.255.1\/32", "sid":100, "inputLabel":10100, - "outputLabel":"8100", - "interface":"r3-eth0", - "nexthop":"10.0.3.2" + "prefixRoute":[ + { + "outputLabel":16100, + "interface":"r3-eth0", + "nexthop":"10.0.3.2" + } + ] } ] } diff --git a/tests/topotests/ospf-sr-topo1/r3/ospfd.conf b/tests/topotests/ospf-sr-topo1/r3/ospfd.conf index 5aaa14fac4..e2766f202d 100644 --- a/tests/topotests/ospf-sr-topo1/r3/ospfd.conf +++ b/tests/topotests/ospf-sr-topo1/r3/ospfd.conf @@ -1,10 +1,13 @@ ! interface lo ip ospf area 0.0.0.0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 ! interface r3-eth0 - ip ospf network point-to-point ip ospf area 0.0.0.0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 ! ! router ospf @@ -12,6 +15,7 @@ router ospf capability opaque router-info area 0.0.0.0 segment-routing on + segment-routing local-block 5000 5999 segment-routing global-block 10000 19999 segment-routing node-msd 8 segment-routing prefix 10.0.255.3/32 index 300 diff --git a/tests/topotests/ospf-sr-topo1/r3/zebra_mpls.json b/tests/topotests/ospf-sr-topo1/r3/zebra_mpls.json index b15f90afd1..1b98ff4756 100644 --- a/tests/topotests/ospf-sr-topo1/r3/zebra_mpls.json +++ b/tests/topotests/ospf-sr-topo1/r3/zebra_mpls.json @@ -1,67 +1,82 @@ -{ - "10100":{ +[ + { "inLabel":10100, "installed":true, "nexthops":[ { - "type":"SR", - "outLabel":8100, + "type":"SR (OSPF)", + "outLabel":16100, + "outLabelStack":[ + 16100 + ], "distance":150, "installed":true, "nexthop":"10.0.3.2" } ] }, - "10200":{ + { "inLabel":10200, "installed":true, "nexthops":[ { - "type":"SR", + "type":"SR (OSPF)", "outLabel":3, + "outLabelStack":[ + 3 + ], "distance":150, "installed":true, "nexthop":"10.0.3.2" } ] }, - "10400":{ + { "inLabel":10400, "installed":true, "nexthops":[ { - "type":"SR", - "outLabel":8400, + "type":"SR (OSPF)", + "outLabel":16400, + "outLabelStack":[ + 16400 + ], "distance":150, "installed":true, "nexthop":"10.0.3.2" } ] }, - "50000":{ - "inLabel":50000, + { + "inLabel":"*", "installed":true, "nexthops":[ { - "type":"SR", + "type":"SR (OSPF)", "outLabel":3, + "outLabelStack":[ + 3 + ], "distance":150, "installed":true, "nexthop":"10.0.3.2" } ] }, - "50001":{ - "inLabel":50001, + { + "inLabel":"*", "installed":true, "nexthops":[ { - "type":"SR", + "type":"SR (OSPF)", "outLabel":3, + "outLabelStack":[ + 3 + ], "distance":150, "installed":true, "nexthop":"10.0.3.2" } ] } -} +] diff --git a/tests/topotests/ospf-sr-topo1/r4/ospf_srdb.json b/tests/topotests/ospf-sr-topo1/r4/ospf_srdb.json index 098e87dc25..a241b32607 100644 --- a/tests/topotests/ospf-sr-topo1/r4/ospf_srdb.json +++ b/tests/topotests/ospf-sr-topo1/r4/ospf_srdb.json @@ -3,8 +3,10 @@ "srNodes":[ { "routerID":"10.0.255.2", - "srgbSize":20000, - "srgbLabel":8000, + "srgbSize":8000, + "srgbLabel":16000, + "srlbSize":1000, + "srlbLabel":15000, "algorithms":[ { "0":"SPF" @@ -15,9 +17,13 @@ "prefix":"10.0.255.2\/32", "sid":200, "inputLabel":10200, - "outputLabel":"pop", - "interface":"r4-eth0", - "nexthop":"10.0.4.2" + "prefixRoute":[ + { + "outputLabel":3, + "interface":"r4-eth0", + "nexthop":"10.0.4.2" + } + ] } ] }, @@ -25,6 +31,8 @@ "routerID":"10.0.255.4", "srgbSize":10000, "srgbLabel":10000, + "srlbSize":1000, + "srlbLabel":5000, "algorithms":[ { "0":"SPF" @@ -36,25 +44,29 @@ "prefix":"10.0.255.4\/32", "sid":400, "inputLabel":10400, - "outputLabel":"pop", - "interface":"lo", - "nexthop":"10.0.255.4" + "prefixRoute":[ + { + "outputLabel":3, + "interface":"lo", + "nexthop":"10.0.255.4" + } + ] } ], "extendedLink":[ { "prefix":"10.0.4.1\/32", - "sid":50001, - "inputLabel":50001, - "outputLabel":"pop", + "sid":"*", + "inputLabel":"*", + "outputLabel":3, "interface":"r4-eth0", "nexthop":"10.0.4.2" }, { "prefix":"10.0.4.1\/32", - "sid":50000, - "inputLabel":50000, - "outputLabel":"pop", + "sid":"*", + "inputLabel":"*", + "outputLabel":3, "interface":"r4-eth0", "nexthop":"10.0.4.2" } @@ -64,6 +76,8 @@ "routerID":"10.0.255.3", "srgbSize":10000, "srgbLabel":10000, + "srlbSize":1000, + "srlbLabel":5000, "algorithms":[ { "0":"SPF" @@ -75,9 +89,13 @@ "prefix":"10.0.255.3\/32", "sid":300, "inputLabel":10300, - "outputLabel":"8300", - "interface":"r4-eth0", - "nexthop":"10.0.4.2" + "prefixRoute":[ + { + "outputLabel":16300, + "interface":"r4-eth0", + "nexthop":"10.0.4.2" + } + ] } ] }, @@ -85,6 +103,8 @@ "routerID":"10.0.255.1", "srgbSize":10000, "srgbLabel":20000, + "srlbSize":1000, + "srlbLabel":15000, "algorithms":[ { "0":"SPF" @@ -96,9 +116,13 @@ "prefix":"10.0.255.1\/32", "sid":100, "inputLabel":10100, - "outputLabel":"8100", - "interface":"r4-eth0", - "nexthop":"10.0.4.2" + "prefixRoute":[ + { + "outputLabel":16100, + "interface":"r4-eth0", + "nexthop":"10.0.4.2" + } + ] } ] } diff --git a/tests/topotests/ospf-sr-topo1/r4/ospfd.conf b/tests/topotests/ospf-sr-topo1/r4/ospfd.conf index 65fdce69f7..e80880af88 100644 --- a/tests/topotests/ospf-sr-topo1/r4/ospfd.conf +++ b/tests/topotests/ospf-sr-topo1/r4/ospfd.conf @@ -1,9 +1,13 @@ ! interface lo ip ospf area 0.0.0.0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 ! interface r4-eth0 ip ospf network point-to-point + ip ospf hello-interval 2 + ip ospf dead-interval 10 ip ospf area 0.0.0.0 ! ! @@ -12,6 +16,7 @@ router ospf capability opaque router-info area 0.0.0.0 segment-routing on + segment-routing local-block 5000 5999 segment-routing global-block 10000 19999 segment-routing node-msd 12 segment-routing prefix 10.0.255.4/32 index 400 no-php-flag diff --git a/tests/topotests/ospf-sr-topo1/r4/zebra_mpls.json b/tests/topotests/ospf-sr-topo1/r4/zebra_mpls.json index d1238517f5..b5758f29a0 100644 --- a/tests/topotests/ospf-sr-topo1/r4/zebra_mpls.json +++ b/tests/topotests/ospf-sr-topo1/r4/zebra_mpls.json @@ -1,80 +1,97 @@ -{ - "10100":{ +[ + { "inLabel":10100, "installed":true, "nexthops":[ { - "type":"SR", - "outLabel":8100, + "type":"SR (OSPF)", + "outLabel":16100, + "outLabelStack":[ + 16100 + ], "distance":150, "installed":true, "nexthop":"10.0.4.2" } ] }, - "10200":{ + { "inLabel":10200, "installed":true, "nexthops":[ { - "type":"SR", + "type":"SR (OSPF)", "outLabel":3, + "outLabelStack":[ + 3 + ], "distance":150, "installed":true, "nexthop":"10.0.4.2" } ] }, - "10300":{ + { "inLabel":10300, "installed":true, "nexthops":[ { - "type":"SR", - "outLabel":8300, + "type":"SR (OSPF)", + "outLabel":16300, + "outLabelStack":[ + 16300 + ], "distance":150, "installed":true, "nexthop":"10.0.4.2" } ] }, - "10400":{ + { "inLabel":10400, "installed":true, "nexthops":[ { - "type":"SR", + "type":"SR (OSPF)", "outLabel":3, + "outLabelStack":[ + 3 + ], "distance":150, - "installed":true, - "nexthop":"10.0.255.4" + "installed":true } ] }, - "50000":{ - "inLabel":50000, + { + "inLabel":"*", "installed":true, "nexthops":[ { - "type":"SR", + "type":"SR (OSPF)", "outLabel":3, + "outLabelStack":[ + 3 + ], "distance":150, "installed":true, "nexthop":"10.0.4.2" } ] }, - "50001":{ - "inLabel":50001, + { + "inLabel":"*", "installed":true, "nexthops":[ { - "type":"SR", + "type":"SR (OSPF)", "outLabel":3, + "outLabelStack":[ + 3 + ], "distance":150, "installed":true, "nexthop":"10.0.4.2" } ] } -} +] diff --git a/tests/topotests/ospf-sr-topo1/test_ospf_sr_topo1.py b/tests/topotests/ospf-sr-topo1/test_ospf_sr_topo1.py index 56cd42ea57..6792c56b3b 100755 --- a/tests/topotests/ospf-sr-topo1/test_ospf_sr_topo1.py +++ b/tests/topotests/ospf-sr-topo1/test_ospf_sr_topo1.py @@ -27,50 +27,58 @@ """ import os +import re import sys +import json from functools import partial # Save the Current Working Directory to find configuration files. CWD = os.path.dirname(os.path.realpath(__file__)) -sys.path.append(os.path.join(CWD, '../')) +sys.path.append(os.path.join(CWD, "../")) # pylint: disable=C0413 # Required to instantiate the topology builder class. from mininet.topo import Topo + # Import topogen and topotest helpers from lib import topotest from lib.topogen import Topogen, TopoRouter, get_topogen from lib.topolog import logger + # and Finally pytest import pytest class OspfSrTopo(Topo): "Test topology builder" + def build(self): "Build function" tgen = get_topogen(self) # Check for mpls if tgen.hasmpls is not True: - tgen.set_error('MPLS not available, tests will be skipped') + tgen.set_error("MPLS not available, tests will be skipped") # Create 4 routers for routern in range(1, 5): - tgen.add_router('r{}'.format(routern)) + tgen.add_router("r{}".format(routern)) - # Interconect router 1 and 2 + # Interconect router 1 and 2 with 2 links switch = tgen.add_switch('s1') switch.add_link(tgen.gears['r1']) switch.add_link(tgen.gears['r2']) + switch = tgen.add_switch('s2') + switch.add_link(tgen.gears['r1']) + switch.add_link(tgen.gears['r2']) # Interconect router 3 and 2 - switch = tgen.add_switch('s2') + switch = tgen.add_switch('s3') switch.add_link(tgen.gears['r3']) switch.add_link(tgen.gears['r2']) # Interconect router 4 and 2 - switch = tgen.add_switch('s3') + switch = tgen.add_switch('s4') switch.add_link(tgen.gears['r4']) switch.add_link(tgen.gears['r2']) @@ -78,7 +86,7 @@ def build(self): def setup_module(mod): "Sets up the pytest environment" - logger.info('\n\n---- Starting OSPF Segment Routing tests ----\n') + logger.info("\n\n---- Starting OSPF Segment Routing tests ----\n") tgen = Topogen(OspfSrTopo, mod.__name__) tgen.start_topology() @@ -87,29 +95,15 @@ def setup_module(mod): for rname, router in router_list.iteritems(): router.load_config( - TopoRouter.RD_ZEBRA, - os.path.join(CWD, '{}/zebra.conf'.format(rname)) + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) ) router.load_config( - TopoRouter.RD_OSPF, - os.path.join(CWD, '{}/ospfd.conf'.format(rname)) + TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname)) ) # Initialize all routers. tgen.start_router() - # Verify that version, MPLS and Segment Routing are OK - for router in router_list.values(): - # Check for Version - if router.has_version('<', '4'): - tgen.set_error('Unsupported FRR version') - break - # Check that Segment Routing is available - output = tgen.gears[router.name].vtysh_cmd( - "show ip ospf database segment-routing json") - if output.find("Unknown") != -1: - tgen.set_error('Segment Routing is not available') - def teardown_module(mod): "Teardown the pytest environment" @@ -117,32 +111,7 @@ def teardown_module(mod): tgen = get_topogen() tgen.stop_topology() - logger.info('\n\n---- OSPF Segment Routing tests End ----\n') - -# Shared test function to validate expected output. -def compare_ospf_srdb(rname, expected): - """ - Calls 'show ip ospf database segment-routing json' for router `rname` - and compare the obtained result with the expected output. - """ - tgen = get_topogen() - current = tgen.gears[rname].vtysh_cmd( - 'show ip ospf database segment-routing json') - return topotest.difflines(current, expected, - title1="Current output", - title2="Expected output") - - -def compare_mpls_table(rname, expected): - """ - Calls 'show mpls table json' for router `rname` and compare the obtained - result with the expected output. - """ - tgen = get_topogen() - current = tgen.gears[rname].vtysh_cmd('show mpls table json') - return topotest.difflines(current, expected, - title1="Current output", - title2="Expected output") + logger.info("\n\n---- OSPF Segment Routing tests End ----\n") def test_ospf_sr(): @@ -151,24 +120,26 @@ def test_ospf_sr(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - logger.info('--- test OSPF Segment Routing Data Base ---') + logger.info("--- test OSPF Segment Routing Data Base ---") for rnum in range(1, 5): - router = 'r{}'.format(rnum) + router = "r{}".format(rnum) logger.info('\tRouter "%s"', router) # Load expected results from the command - reffile = os.path.join(CWD, '{}/ospf_srdb.json'.format(router)) - expected = open(reffile).read() + reffile = os.path.join(CWD, "{}/ospf_srdb.json".format(router)) + expected = json.loads(open(reffile).read()) # Run test function until we get an result. Wait at most 60 seconds. - test_func = partial(compare_ospf_srdb, router, expected) - result, diff = topotest.run_and_expect(test_func, '', - count=25, wait=3) - assert result, ( - 'OSPF did not start Segment Routing on {}:\n{}' - ).format(router, diff) + rt = tgen.gears[router] + test_func = partial( + topotest.router_json_cmp, rt, 'show ip ospf database segment-routing json', expected + ) + rv, diff = topotest.run_and_expect(test_func, None, count=25, wait=3) + assert rv, "OSPF did not start Segment Routing on {}:\n{}".format( + router, diff + ) def test_ospf_kernel_route(): @@ -177,34 +148,62 @@ def test_ospf_kernel_route(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - logger.info('--- test OSPF Segment Routing MPLS tables ---') + logger.info("--- test OSPF Segment Routing MPLS tables ---") + + def show_mpls_table_json_cmp(rt, expected): + """ + Reformat MPLS table output to use a list of labels instead of dict. + + Original: + { + "X": { + inLabel: "X", + # ... + } + } + + List format: + [ + { + inLabel: "X", + } + ] + """ + out = rt.vtysh_cmd('show mpls table json', isjson=True) + + outlist = [] + for key in out.keys(): + outlist.append(out[key]) + + return topotest.json_cmp(outlist, expected) for rnum in range(1, 5): - router = 'r{}'.format(rnum) + router = "r{}".format(rnum) logger.info('\tRouter "%s"', router) # Load expected results from the command - reffile = os.path.join(CWD, '{}/zebra_mpls.json'.format(router)) - expected = open(reffile).read() + reffile = os.path.join(CWD, "{}/zebra_mpls.json".format(router)) + expected = json.loads(open(reffile).read()) # Run test function until we get an result. Wait at most 60 seconds. - test_func = partial(compare_mpls_table, router, expected) - result, diff = topotest.run_and_expect(test_func, '', - count=25, wait=3) - assert result, ( - 'OSPF did not properly instal MPLS table on {}:\n{}' - ).format(router, diff) + rt = tgen.gears[router] + test_func = partial(show_mpls_table_json_cmp, rt, expected) + rv, diff = topotest.run_and_expect(test_func, None, count=25, wait=3) + assert rv, "OSPF did not properly instal MPLS table on {}:\n{}".format( + router, diff + ) def test_memory_leak(): "Run the memory leak test and report results." tgen = get_topogen() if not tgen.is_memleak_enabled(): - pytest.skip('Memory leak test/report is disabled') + pytest.skip("Memory leak test/report is disabled") tgen.report_memory_leaks() -if __name__ == '__main__': + +if __name__ == "__main__": args = ["-s"] + sys.argv[1:] sys.exit(pytest.main(args)) diff --git a/tests/topotests/ospf-topo1-vrf/r1/zebraroute.txt b/tests/topotests/ospf-topo1-vrf/r1/zebraroute.txt index 973db543fa..d72aa3b8e5 100644 --- a/tests/topotests/ospf-topo1-vrf/r1/zebraroute.txt +++ b/tests/topotests/ospf-topo1-vrf/r1/zebraroute.txt @@ -1,8 +1,8 @@ VRF r1-cust1: -O 10.0.1.0/24 [110/10] is directly connected, r1-eth0, XX:XX:XX +O 10.0.1.0/24 [110/10] is directly connected, r1-eth0, weight 1, XX:XX:XX C>* 10.0.1.0/24 is directly connected, r1-eth0, XX:XX:XX -O>* 10.0.2.0/24 [110/20] via 10.0.3.3, r1-eth1, XX:XX:XX -O 10.0.3.0/24 [110/10] is directly connected, r1-eth1, XX:XX:XX +O>* 10.0.2.0/24 [110/20] via 10.0.3.3, r1-eth1, weight 1, XX:XX:XX +O 10.0.3.0/24 [110/10] is directly connected, r1-eth1, weight 1, XX:XX:XX C>* 10.0.3.0/24 is directly connected, r1-eth1, XX:XX:XX -O>* 10.0.10.0/24 [110/20] via 10.0.3.1, r1-eth1, XX:XX:XX +O>* 10.0.10.0/24 [110/20] via 10.0.3.1, r1-eth1, weight 1, XX:XX:XX diff --git a/tests/topotests/ospf-topo1-vrf/r1/zebraroutedown.txt b/tests/topotests/ospf-topo1-vrf/r1/zebraroutedown.txt index 7bdccd0909..5ea6bdc04d 100644 --- a/tests/topotests/ospf-topo1-vrf/r1/zebraroutedown.txt +++ b/tests/topotests/ospf-topo1-vrf/r1/zebraroutedown.txt @@ -1,7 +1,7 @@ VRF r1-cust1: -O 10.0.1.0/24 [110/10] is directly connected, r1-eth0, XX:XX:XX +O 10.0.1.0/24 [110/10] is directly connected, r1-eth0, weight 1, XX:XX:XX C>* 10.0.1.0/24 is directly connected, r1-eth0, XX:XX:XX -O>* 10.0.2.0/24 [110/20] via 10.0.3.3, r1-eth1, XX:XX:XX -O 10.0.3.0/24 [110/10] is directly connected, r1-eth1, XX:XX:XX +O>* 10.0.2.0/24 [110/20] via 10.0.3.3, r1-eth1, weight 1, XX:XX:XX +O 10.0.3.0/24 [110/10] is directly connected, r1-eth1, weight 1, XX:XX:XX C>* 10.0.3.0/24 is directly connected, r1-eth1, XX:XX:XX diff --git a/tests/topotests/ospf-topo1-vrf/r2/zebraroute.txt b/tests/topotests/ospf-topo1-vrf/r2/zebraroute.txt index 2916cb9274..ce5e5f3bab 100644 --- a/tests/topotests/ospf-topo1-vrf/r2/zebraroute.txt +++ b/tests/topotests/ospf-topo1-vrf/r2/zebraroute.txt @@ -1,8 +1,8 @@ VRF r2-cust1: -O>* 10.0.1.0/24 [110/20] via 10.0.3.2, r2-eth1, XX:XX:XX -O 10.0.2.0/24 [110/10] is directly connected, r2-eth0, XX:XX:XX +O>* 10.0.1.0/24 [110/20] via 10.0.3.2, r2-eth1, weight 1, XX:XX:XX +O 10.0.2.0/24 [110/10] is directly connected, r2-eth0, weight 1, XX:XX:XX C>* 10.0.2.0/24 is directly connected, r2-eth0, XX:XX:XX -O 10.0.3.0/24 [110/10] is directly connected, r2-eth1, XX:XX:XX +O 10.0.3.0/24 [110/10] is directly connected, r2-eth1, weight 1, XX:XX:XX C>* 10.0.3.0/24 is directly connected, r2-eth1, XX:XX:XX -O>* 10.0.10.0/24 [110/20] via 10.0.3.1, r2-eth1, XX:XX:XX +O>* 10.0.10.0/24 [110/20] via 10.0.3.1, r2-eth1, weight 1, XX:XX:XX diff --git a/tests/topotests/ospf-topo1-vrf/r2/zebraroutedown.txt b/tests/topotests/ospf-topo1-vrf/r2/zebraroutedown.txt index ccaf9abc31..157811ec77 100644 --- a/tests/topotests/ospf-topo1-vrf/r2/zebraroutedown.txt +++ b/tests/topotests/ospf-topo1-vrf/r2/zebraroutedown.txt @@ -1,7 +1,7 @@ VRF r2-cust1: -O>* 10.0.1.0/24 [110/20] via 10.0.3.2, r2-eth1, XX:XX:XX -O 10.0.2.0/24 [110/10] is directly connected, r2-eth0, XX:XX:XX +O>* 10.0.1.0/24 [110/20] via 10.0.3.2, r2-eth1, weight 1, XX:XX:XX +O 10.0.2.0/24 [110/10] is directly connected, r2-eth0, weight 1, XX:XX:XX C>* 10.0.2.0/24 is directly connected, r2-eth0, XX:XX:XX -O 10.0.3.0/24 [110/10] is directly connected, r2-eth1, XX:XX:XX +O 10.0.3.0/24 [110/10] is directly connected, r2-eth1, weight 1, XX:XX:XX C>* 10.0.3.0/24 is directly connected, r2-eth1, XX:XX:XX diff --git a/tests/topotests/ospf-topo1-vrf/r3/zebraroute.txt b/tests/topotests/ospf-topo1-vrf/r3/zebraroute.txt index 70eae0a9fb..f40b7b09af 100644 --- a/tests/topotests/ospf-topo1-vrf/r3/zebraroute.txt +++ b/tests/topotests/ospf-topo1-vrf/r3/zebraroute.txt @@ -1,8 +1,8 @@ VRF r3-cust1: -O>* 10.0.1.0/24 [110/20] via 10.0.3.2, r3-eth0, XX:XX:XX -O>* 10.0.2.0/24 [110/20] via 10.0.3.3, r3-eth0, XX:XX:XX -O 10.0.3.0/24 [110/10] is directly connected, r3-eth0, XX:XX:XX +O>* 10.0.1.0/24 [110/20] via 10.0.3.2, r3-eth0, weight 1, XX:XX:XX +O>* 10.0.2.0/24 [110/20] via 10.0.3.3, r3-eth0, weight 1, XX:XX:XX +O 10.0.3.0/24 [110/10] is directly connected, r3-eth0, weight 1, XX:XX:XX C>* 10.0.3.0/24 is directly connected, r3-eth0, XX:XX:XX -O 10.0.10.0/24 [110/10] is directly connected, r3-eth1, XX:XX:XX +O 10.0.10.0/24 [110/10] is directly connected, r3-eth1, weight 1, XX:XX:XX C>* 10.0.10.0/24 is directly connected, r3-eth1, XX:XX:XX diff --git a/tests/topotests/ospf-topo1-vrf/r3/zebraroutedown.txt b/tests/topotests/ospf-topo1-vrf/r3/zebraroutedown.txt index 6d54782eff..89cd6f56c4 100644 --- a/tests/topotests/ospf-topo1-vrf/r3/zebraroutedown.txt +++ b/tests/topotests/ospf-topo1-vrf/r3/zebraroutedown.txt @@ -1,4 +1,4 @@ VRF r3-cust1: -O 10.0.10.0/24 [110/10] is directly connected, r3-eth1, XX:XX:XX +O 10.0.10.0/24 [110/10] is directly connected, r3-eth1, weight 1, XX:XX:XX C>* 10.0.10.0/24 is directly connected, r3-eth1, XX:XX:XX diff --git a/tests/topotests/ospf-topo1-vrf/test_ospf_topo1_vrf.py b/tests/topotests/ospf-topo1-vrf/test_ospf_topo1_vrf.py index fc4854454c..130d0c85f9 100755 --- a/tests/topotests/ospf-topo1-vrf/test_ospf_topo1_vrf.py +++ b/tests/topotests/ospf-topo1-vrf/test_ospf_topo1_vrf.py @@ -34,7 +34,7 @@ # Save the Current Working Directory to find configuration files. CWD = os.path.dirname(os.path.realpath(__file__)) -sys.path.append(os.path.join(CWD, '../')) +sys.path.append(os.path.join(CWD, "../")) # pylint: disable=C0413 # Import topogen and topotest helpers @@ -45,33 +45,35 @@ # Required to instantiate the topology builder class. from mininet.topo import Topo + class OSPFTopo(Topo): "Test topology builder" + def build(self, *_args, **_opts): "Build function" tgen = get_topogen(self) # Create 3 routers for routern in range(1, 4): - tgen.add_router('r{}'.format(routern)) + tgen.add_router("r{}".format(routern)) # Create a empty network for router 1 - switch = tgen.add_switch('s1') - switch.add_link(tgen.gears['r1']) + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) # Create a empty network for router 2 - switch = tgen.add_switch('s2') - switch.add_link(tgen.gears['r2']) + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r2"]) # Interconect router 1, 2 and 3 - switch = tgen.add_switch('s3') - switch.add_link(tgen.gears['r1']) - switch.add_link(tgen.gears['r2']) - switch.add_link(tgen.gears['r3']) + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) # Create empty netowrk for router3 - switch = tgen.add_switch('s4') - switch.add_link(tgen.gears['r3']) + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["r3"]) def setup_module(mod): @@ -83,23 +85,26 @@ def setup_module(mod): # check for zebra capability for rname, router in router_list.iteritems(): - if router.check_capability( - TopoRouter.RD_ZEBRA, - '--vrfwnetns' - ) == False: - return pytest.skip('Skipping OSPF VRF NETNS feature. VRF NETNS backend not available on FRR') - - if os.system('ip netns list') != 0: - return pytest.skip('Skipping OSPF VRF NETNS Test. NETNS not available on System') + if router.check_capability(TopoRouter.RD_ZEBRA, "--vrfwnetns") == False: + return pytest.skip( + "Skipping OSPF VRF NETNS feature. VRF NETNS backend not available on FRR" + ) + + if os.system("ip netns list") != 0: + return pytest.skip( + "Skipping OSPF VRF NETNS Test. NETNS not available on System" + ) - logger.info('Testing with VRF Namespace support') + logger.info("Testing with VRF Namespace support") - cmds = ['if [ -e /var/run/netns/{0}-cust1 ] ; then ip netns del {0}-cust1 ; fi', - 'ip netns add {0}-cust1', - 'ip link set dev {0}-eth0 netns {0}-cust1', - 'ip netns exec {0}-cust1 ifconfig {0}-eth0 up', - 'ip link set dev {0}-eth1 netns {0}-cust1', - 'ip netns exec {0}-cust1 ifconfig {0}-eth1 up'] + cmds = [ + "if [ -e /var/run/netns/{0}-cust1 ] ; then ip netns del {0}-cust1 ; fi", + "ip netns add {0}-cust1", + "ip link set dev {0}-eth0 netns {0}-cust1", + "ip netns exec {0}-cust1 ifconfig {0}-eth0 up", + "ip link set dev {0}-eth1 netns {0}-cust1", + "ip netns exec {0}-cust1 ifconfig {0}-eth1 up", + ] for rname, router in router_list.iteritems(): @@ -109,19 +114,18 @@ def setup_module(mod): router.load_config( TopoRouter.RD_ZEBRA, - os.path.join(CWD, '{}/zebra.conf'.format(rname)), - '--vrfwnetns' + os.path.join(CWD, "{}/zebra.conf".format(rname)), + "--vrfwnetns", ) router.load_config( - TopoRouter.RD_OSPF, - os.path.join(CWD, '{}/ospfd.conf'.format(rname)) + TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname)) ) # Initialize all routers. tgen.start_router() for router in router_list.values(): - if router.has_version('<', '4.0'): - tgen.set_error('unsupported version') + if router.has_version("<", "4.0"): + tgen.set_error("unsupported version") def teardown_module(mod): @@ -130,16 +134,19 @@ def teardown_module(mod): # move back rx-eth0 to default VRF # delete rx-vrf - cmds = ['ip netns exec {0}-cust1 ip link set {0}-eth0 netns 1', - 'ip netns exec {0}-cust1 ip link set {0}-eth1 netns 1', - 'ip netns delete {0}-cust1'] - + cmds = [ + "ip netns exec {0}-cust1 ip link set {0}-eth0 netns 1", + "ip netns exec {0}-cust1 ip link set {0}-eth1 netns 1", + "ip netns delete {0}-cust1", + ] + router_list = tgen.routers() for rname, router in router_list.iteritems(): for cmd in cmds: tgen.net[rname].cmd(cmd.format(rname)) tgen.stop_topology() + # Shared test function to validate expected output. def compare_show_ip_route_vrf(rname, expected): """ @@ -147,35 +154,37 @@ def compare_show_ip_route_vrf(rname, expected): result with the expected output. """ tgen = get_topogen() - vrf_name = '{0}-cust1'.format(rname) + vrf_name = "{0}-cust1".format(rname) current = topotest.ip4_route_zebra(tgen.gears[rname], vrf_name) - ret = topotest.difflines(current, expected, - title1="Current output", - title2="Expected output") + ret = topotest.difflines( + current, expected, title1="Current output", title2="Expected output" + ) return ret + def test_ospf_convergence(): "Test OSPF daemon convergence" tgen = get_topogen() if tgen.routers_have_failure(): - pytest.skip('skipped because of router(s) failure') + pytest.skip("skipped because of router(s) failure") for rname, router in tgen.routers().iteritems(): logger.info('Waiting for router "%s" convergence', rname) # Load expected results from the command - reffile = os.path.join(CWD, '{}/ospfroute.txt'.format(rname)) + reffile = os.path.join(CWD, "{}/ospfroute.txt".format(rname)) expected = open(reffile).read() # Run test function until we get an result. Wait at most 60 seconds. - test_func = partial(topotest.router_output_cmp, - router, - 'show ip ospf vrf {0}-cust1 route'.format(rname), - expected) - result, diff = topotest.run_and_expect(test_func, '', - count=160, wait=0.5) - assertmsg = 'OSPF did not converge on {}:\n{}'.format(rname, diff) + test_func = partial( + topotest.router_output_cmp, + router, + "show ip ospf vrf {0}-cust1 route".format(rname), + expected, + ) + result, diff = topotest.run_and_expect(test_func, "", count=160, wait=0.5) + assertmsg = "OSPF did not converge on {}:\n{}".format(rname, diff) assert result, assertmsg @@ -184,19 +193,19 @@ def test_ospf_kernel_route(): tgen = get_topogen() if tgen.routers_have_failure(): - pytest.skip('skipped because of router(s) failure') + pytest.skip("skipped because of router(s) failure") rlist = tgen.routers().values() for router in rlist: logger.info('Checking OSPF IPv4 kernel routes in "%s"', router.name) - reffile = os.path.join(CWD, '{}/zebraroute.txt'.format(router.name)) + reffile = os.path.join(CWD, "{}/zebraroute.txt".format(router.name)) expected = open(reffile).read() # Run test function until we get an result. Wait at most 60 seconds. test_func = partial(compare_show_ip_route_vrf, router.name, expected) - result, diff = topotest.run_and_expect(test_func, '', - count=140, wait=0.5) + result, diff = topotest.run_and_expect(test_func, "", count=140, wait=0.5) assertmsg = 'OSPF IPv4 route mismatch in router "{}": {}'.format( - router.name, diff) + router.name, diff + ) assert result, assertmsg @@ -205,52 +214,57 @@ def test_ospf_json(): tgen = get_topogen() if tgen.routers_have_failure(): - pytest.skip('skipped because of router(s) failure') + pytest.skip("skipped because of router(s) failure") for rname, router in tgen.routers().iteritems(): - logger.info('Comparing router "%s" "show ip ospf vrf %s-cust1 json" output', router.name, router.name) + logger.info( + 'Comparing router "%s" "show ip ospf vrf %s-cust1 json" output', + router.name, + router.name, + ) expected = { - '{}-cust1'.format(router.name) : { - 'vrfName': '{}-cust1'.format(router.name), - 'routerId': '10.0.255.{}'.format(rname[1:]), - 'tosRoutesOnly': True, - 'rfc2328Conform': True, - 'spfScheduleDelayMsecs': 0, - 'holdtimeMinMsecs': 50, - 'holdtimeMaxMsecs': 5000, - 'lsaMinIntervalMsecs': 5000, - 'lsaMinArrivalMsecs': 1000, - 'writeMultiplier': 20, - 'refreshTimerMsecs': 10000, - 'asbrRouter': 'injectingExternalRoutingInformation', - 'attachedAreaCounter': 1, - 'areas': {} - } + "{}-cust1".format(router.name): { + "vrfName": "{}-cust1".format(router.name), + "routerId": "10.0.255.{}".format(rname[1:]), + "tosRoutesOnly": True, + "rfc2328Conform": True, + "spfScheduleDelayMsecs": 0, + "holdtimeMinMsecs": 50, + "holdtimeMaxMsecs": 5000, + "lsaMinIntervalMsecs": 5000, + "lsaMinArrivalMsecs": 1000, + "writeMultiplier": 20, + "refreshTimerMsecs": 10000, + "asbrRouter": "injectingExternalRoutingInformation", + "attachedAreaCounter": 1, + "areas": {}, } + } # Area specific additional checks - if router.name == 'r1' or router.name == 'r2' or router.name == 'r3': - expected['{}-cust1'.format(router.name)]['areas']['0.0.0.0'] = { - 'areaIfActiveCounter': 2, - 'areaIfTotalCounter': 2, - 'authentication': 'authenticationNone', - 'backbone': True, - 'lsaAsbrNumber': 0, - 'lsaNetworkNumber': 1, - 'lsaNssaNumber': 0, - 'lsaNumber': 4, - 'lsaOpaqueAreaNumber': 0, - 'lsaOpaqueLinkNumber': 0, - 'lsaRouterNumber': 3, - 'lsaSummaryNumber': 0, - 'nbrFullAdjacentCounter': 2, + if router.name == "r1" or router.name == "r2" or router.name == "r3": + expected["{}-cust1".format(router.name)]["areas"]["0.0.0.0"] = { + "areaIfActiveCounter": 2, + "areaIfTotalCounter": 2, + "authentication": "authenticationNone", + "backbone": True, + "lsaAsbrNumber": 0, + "lsaNetworkNumber": 1, + "lsaNssaNumber": 0, + "lsaNumber": 4, + "lsaOpaqueAreaNumber": 0, + "lsaOpaqueLinkNumber": 0, + "lsaRouterNumber": 3, + "lsaSummaryNumber": 0, + "nbrFullAdjacentCounter": 2, } - test_func = partial(topotest.router_json_cmp, - router, - 'show ip ospf vrf {0}-cust1 json'.format(rname), - expected) - _, diff = topotest.run_and_expect(test_func, None, - count=10, wait=0.5) + test_func = partial( + topotest.router_json_cmp, + router, + "show ip ospf vrf {0}-cust1 json".format(rname), + expected, + ) + _, diff = topotest.run_and_expect(test_func, None, count=10, wait=0.5) assertmsg = '"{}" JSON output mismatches'.format(rname) assert diff is None, assertmsg @@ -260,27 +274,30 @@ def test_ospf_link_down(): tgen = get_topogen() if tgen.routers_have_failure(): - pytest.skip('skipped because of router(s) failure') + pytest.skip("skipped because of router(s) failure") # Simulate a network down event on router3 switch3 interface. - router3 = tgen.gears['r3'] - topotest.interface_set_status(router3, 'r3-eth0', ifaceaction=False, vrf_name='r3-cust1') + router3 = tgen.gears["r3"] + topotest.interface_set_status( + router3, "r3-eth0", ifaceaction=False, vrf_name="r3-cust1" + ) # Expect convergence on all routers for rname, router in tgen.routers().iteritems(): logger.info('Waiting for router "%s" convergence after link failure', rname) # Load expected results from the command - reffile = os.path.join(CWD, '{}/ospfroute_down.txt'.format(rname)) + reffile = os.path.join(CWD, "{}/ospfroute_down.txt".format(rname)) expected = open(reffile).read() # Run test function until we get an result. Wait at most 60 seconds. - test_func = partial(topotest.router_output_cmp, - router, - 'show ip ospf vrf {0}-cust1 route'.format(rname), - expected) - result, diff = topotest.run_and_expect(test_func, '', - count=140, wait=0.5) - assertmsg = 'OSPF did not converge on {}:\n{}'.format(rname, diff) + test_func = partial( + topotest.router_output_cmp, + router, + "show ip ospf vrf {0}-cust1 route".format(rname), + expected, + ) + result, diff = topotest.run_and_expect(test_func, "", count=140, wait=0.5) + assertmsg = "OSPF did not converge on {}:\n{}".format(rname, diff) assert result, assertmsg @@ -289,21 +306,23 @@ def test_ospf_link_down_kernel_route(): tgen = get_topogen() if tgen.routers_have_failure(): - pytest.skip('skipped because of router(s) failure') + pytest.skip("skipped because of router(s) failure") rlist = tgen.routers().values() for router in rlist: - logger.info('Checking OSPF IPv4 kernel routes in "%s" after link down', router.name) + logger.info( + 'Checking OSPF IPv4 kernel routes in "%s" after link down', router.name + ) - str='{0}-cust1'.format(router.name) - reffile = os.path.join(CWD, '{}/zebraroutedown.txt'.format(router.name)) + str = "{0}-cust1".format(router.name) + reffile = os.path.join(CWD, "{}/zebraroutedown.txt".format(router.name)) expected = open(reffile).read() # Run test function until we get an result. Wait at most 60 seconds. test_func = partial(compare_show_ip_route_vrf, router.name, expected) - result, diff = topotest.run_and_expect(test_func, '', - count=140, wait=0.5) + result, diff = topotest.run_and_expect(test_func, "", count=140, wait=0.5) assertmsg = 'OSPF IPv4 route mismatch in router "{}" after link down: {}'.format( - router.name, diff) + router.name, diff + ) assert result, assertmsg @@ -311,10 +330,11 @@ def test_memory_leak(): "Run the memory leak test and report results." tgen = get_topogen() if not tgen.is_memleak_enabled(): - pytest.skip('Memory leak test/report is disabled') + pytest.skip("Memory leak test/report is disabled") tgen.report_memory_leaks() -if __name__ == '__main__': + +if __name__ == "__main__": args = ["-s"] + sys.argv[1:] sys.exit(pytest.main(args)) diff --git a/tests/topotests/ospf-topo1/r1/ospf6d.conf b/tests/topotests/ospf-topo1/r1/ospf6d.conf index 58c9e292a9..ca3497b4a5 100644 --- a/tests/topotests/ospf-topo1/r1/ospf6d.conf +++ b/tests/topotests/ospf-topo1/r1/ospf6d.conf @@ -7,3 +7,7 @@ router ospf6 interface r1-eth0 area 0.0.0.0 interface r1-eth1 area 0.0.0.0 ! +int r1-eth1 + ipv6 ospf6 dead-interval 10 + ipv6 ospf6 hello-interval 2 +! diff --git a/tests/topotests/ospf-topo1/r1/ospfd.conf b/tests/topotests/ospf-topo1/r1/ospfd.conf index 1226b72f2b..3b5aa192c5 100644 --- a/tests/topotests/ospf-topo1/r1/ospfd.conf +++ b/tests/topotests/ospf-topo1/r1/ospfd.conf @@ -7,3 +7,7 @@ router ospf network 10.0.1.0/24 area 0 network 10.0.3.0/24 area 0 ! +int r1-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/ospf-topo1/r2/ospf6d.conf b/tests/topotests/ospf-topo1/r2/ospf6d.conf index a4ef1469a7..44047e1a4e 100644 --- a/tests/topotests/ospf-topo1/r2/ospf6d.conf +++ b/tests/topotests/ospf-topo1/r2/ospf6d.conf @@ -7,3 +7,11 @@ router ospf6 interface r2-eth0 area 0.0.0.0 interface r2-eth1 area 0.0.0.0 ! +int r2-eth0 + ipv6 ospf6 hello-interval 2 + ipv6 ospf6 dead-interval 10 +! +int r2-eth1 + ipv6 ospf6 hello-interval 2 + ipv6 ospf6 dead-interval 10 +! diff --git a/tests/topotests/ospf-topo1/r2/ospfd.conf b/tests/topotests/ospf-topo1/r2/ospfd.conf index 78d980315d..1a7ccdf728 100644 --- a/tests/topotests/ospf-topo1/r2/ospfd.conf +++ b/tests/topotests/ospf-topo1/r2/ospfd.conf @@ -7,3 +7,7 @@ router ospf network 10.0.2.0/24 area 0 network 10.0.3.0/24 area 0 ! +int r2-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/ospf-topo1/r3/ospf6d.conf b/tests/topotests/ospf-topo1/r3/ospf6d.conf index 4ff53011cb..13ad9a7356 100644 --- a/tests/topotests/ospf-topo1/r3/ospf6d.conf +++ b/tests/topotests/ospf-topo1/r3/ospf6d.conf @@ -8,3 +8,15 @@ router ospf6 interface r3-eth1 area 0.0.0.0 interface r3-eth2 area 0.0.0.1 ! +int r3-eth0 + ipv6 ospf6 hello-interval 2 + ipv6 ospf6 dead-interval 10 +! +int r3-eth1 + ipv6 ospf6 hello-interval 2 + ipv6 ospf6 dead-interval 10 +! +int r3-eth2 + ipv6 ospf6 hello-interval 2 + ipv6 ospf6 dead-interval 10 +! diff --git a/tests/topotests/ospf-topo1/r3/ospfd.conf b/tests/topotests/ospf-topo1/r3/ospfd.conf index dbb7215d10..3b378c0f27 100644 --- a/tests/topotests/ospf-topo1/r3/ospfd.conf +++ b/tests/topotests/ospf-topo1/r3/ospfd.conf @@ -8,3 +8,11 @@ router ospf network 10.0.10.0/24 area 0 network 172.16.0.0/24 area 1 ! +int r3-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +int r3-eth2 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/ospf-topo1/r4/ospf6d.conf b/tests/topotests/ospf-topo1/r4/ospf6d.conf index 32942ea189..f9bde0e83c 100644 --- a/tests/topotests/ospf-topo1/r4/ospf6d.conf +++ b/tests/topotests/ospf-topo1/r4/ospf6d.conf @@ -7,3 +7,11 @@ router ospf6 interface r4-eth0 area 0.0.0.1 interface r4-eth1 area 0.0.0.1 ! +int r4-eth0 + ipv6 ospf6 hello-interval 2 + ipv6 ospf6 dead-interval 10 +! +int r4-eth1 + ipv6 ospf6 hello-interval 2 + ipv6 ospf6 dead-interval 10 +! diff --git a/tests/topotests/ospf-topo1/r4/ospfd.conf b/tests/topotests/ospf-topo1/r4/ospfd.conf index 01b29ca057..52d29322f8 100644 --- a/tests/topotests/ospf-topo1/r4/ospfd.conf +++ b/tests/topotests/ospf-topo1/r4/ospfd.conf @@ -7,3 +7,7 @@ router ospf network 172.16.0.0/24 area 1 network 172.16.1.0/24 area 1 ! +int r4-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/ospf-topo1/test_ospf_topo1.py b/tests/topotests/ospf-topo1/test_ospf_topo1.py index 638e394153..d734f378e7 100755 --- a/tests/topotests/ospf-topo1/test_ospf_topo1.py +++ b/tests/topotests/ospf-topo1/test_ospf_topo1.py @@ -34,7 +34,7 @@ # Save the Current Working Directory to find configuration files. CWD = os.path.dirname(os.path.realpath(__file__)) -sys.path.append(os.path.join(CWD, '../')) +sys.path.append(os.path.join(CWD, "../")) # pylint: disable=C0413 # Import topogen and topotest helpers @@ -45,70 +45,71 @@ # Required to instantiate the topology builder class. from mininet.topo import Topo + class OSPFTopo(Topo): "Test topology builder" + def build(self, *_args, **_opts): "Build function" tgen = get_topogen(self) # Create 4 routers for routern in range(1, 5): - tgen.add_router('r{}'.format(routern)) + tgen.add_router("r{}".format(routern)) # Create a empty network for router 1 - switch = tgen.add_switch('s1') - switch.add_link(tgen.gears['r1']) + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) # Create a empty network for router 2 - switch = tgen.add_switch('s2') - switch.add_link(tgen.gears['r2']) + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r2"]) # Interconect router 1, 2 and 3 - switch = tgen.add_switch('s3') - switch.add_link(tgen.gears['r1']) - switch.add_link(tgen.gears['r2']) - switch.add_link(tgen.gears['r3']) + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) # Create empty netowrk for router3 - switch = tgen.add_switch('s4') - switch.add_link(tgen.gears['r3']) + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["r3"]) # Interconect router 3 and 4 - switch = tgen.add_switch('s5') - switch.add_link(tgen.gears['r3']) - switch.add_link(tgen.gears['r4']) + switch = tgen.add_switch("s5") + switch.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["r4"]) # Create a empty network for router 4 - switch = tgen.add_switch('s6') - switch.add_link(tgen.gears['r4']) + switch = tgen.add_switch("s6") + switch.add_link(tgen.gears["r4"]) + def setup_module(mod): "Sets up the pytest environment" tgen = Topogen(OSPFTopo, mod.__name__) tgen.start_topology() - ospf6_config = 'ospf6d.conf' - if tgen.gears['r1'].has_version('<', '4.0'): - ospf6_config = 'ospf6d.conf-pre-v4' + ospf6_config = "ospf6d.conf" + if tgen.gears["r1"].has_version("<", "4.0"): + ospf6_config = "ospf6d.conf-pre-v4" router_list = tgen.routers() for rname, router in router_list.iteritems(): router.load_config( - TopoRouter.RD_ZEBRA, - os.path.join(CWD, '{}/zebra.conf'.format(rname)) + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) ) router.load_config( - TopoRouter.RD_OSPF, - os.path.join(CWD, '{}/ospfd.conf'.format(rname)) + TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname)) ) router.load_config( - TopoRouter.RD_OSPF6, - os.path.join(CWD, '{}/{}'.format(rname, ospf6_config)) + TopoRouter.RD_OSPF6, os.path.join(CWD, "{}/{}".format(rname, ospf6_config)) ) # Initialize all routers. tgen.start_router() + def teardown_module(mod): "Teardown the pytest environment" tgen = get_topogen() @@ -121,46 +122,50 @@ def compare_show_ipv6_ospf6(rname, expected): result with the expected output. """ tgen = get_topogen() - current = tgen.gears[rname].vtysh_cmd('show ipv6 ospf6 route') + current = tgen.gears[rname].vtysh_cmd("show ipv6 ospf6 route") # Remove the link addresses - current = re.sub(r'fe80::[^ ]+', 'fe80::xxxx:xxxx:xxxx:xxxx', current) - expected = re.sub(r'fe80::[^ ]+', 'fe80::xxxx:xxxx:xxxx:xxxx', expected) + current = re.sub(r"fe80::[^ ]+", "fe80::xxxx:xxxx:xxxx:xxxx", current) + expected = re.sub(r"fe80::[^ ]+", "fe80::xxxx:xxxx:xxxx:xxxx", expected) # Remove the time - current = re.sub(r'\d+:\d{2}:\d{2}', '', current) - expected = re.sub(r'\d+:\d{2}:\d{2}', '', expected) + current = re.sub(r"\d+:\d{2}:\d{2}", "", current) + expected = re.sub(r"\d+:\d{2}:\d{2}", "", expected) + + return topotest.difflines( + topotest.normalize_text(current), + topotest.normalize_text(expected), + title1="Current output", + title2="Expected output", + ) - return topotest.difflines(topotest.normalize_text(current), - topotest.normalize_text(expected), - title1="Current output", - title2="Expected output") def test_ospf_convergence(): "Test OSPF daemon convergence" tgen = get_topogen() if tgen.routers_have_failure(): - pytest.skip('skipped because of router(s) failure') + pytest.skip("skipped because of router(s) failure") for router, rnode in tgen.routers().iteritems(): logger.info('Waiting for router "%s" convergence', router) # Load expected results from the command - reffile = os.path.join(CWD, '{}/ospfroute.txt'.format(router)) + reffile = os.path.join(CWD, "{}/ospfroute.txt".format(router)) expected = open(reffile).read() # Run test function until we get an result. Wait at most 80 seconds. test_func = partial( - topotest.router_output_cmp, rnode, 'show ip ospf route', expected) - result, diff = topotest.run_and_expect(test_func, '', - count=160, wait=0.5) - assert result, 'OSPF did not converge on {}:\n{}'.format(router, diff) + topotest.router_output_cmp, rnode, "show ip ospf route", expected + ) + result, diff = topotest.run_and_expect(test_func, "", count=160, wait=0.5) + assert result, "OSPF did not converge on {}:\n{}".format(router, diff) + def test_ospf_kernel_route(): "Test OSPF kernel route installation" tgen = get_topogen() if tgen.routers_have_failure(): - pytest.skip('skipped because of router(s) failure') + pytest.skip("skipped because of router(s) failure") rlist = tgen.routers().values() for router in rlist: @@ -168,25 +173,26 @@ def test_ospf_kernel_route(): routes = topotest.ip4_route(router) expected = { - '10.0.1.0/24': {}, - '10.0.2.0/24': {}, - '10.0.3.0/24': {}, - '10.0.10.0/24': {}, - '172.16.0.0/24': {}, - '172.16.1.0/24': {}, + "10.0.1.0/24": {}, + "10.0.2.0/24": {}, + "10.0.3.0/24": {}, + "10.0.10.0/24": {}, + "172.16.0.0/24": {}, + "172.16.1.0/24": {}, } assertmsg = 'OSPF IPv4 route mismatch in router "{}"'.format(router.name) assert topotest.json_cmp(routes, expected) is None, assertmsg + def test_ospf6_convergence(): "Test OSPF6 daemon convergence" tgen = get_topogen() if tgen.routers_have_failure(): - pytest.skip('skipped because of router(s) failure') + pytest.skip("skipped because of router(s) failure") - ospf6route_file = '{}/ospf6route_ecmp.txt' + ospf6route_file = "{}/ospf6route_ecmp.txt" for rnum in range(1, 5): - router = 'r{}'.format(rnum) + router = "r{}".format(rnum) logger.info('Waiting for router "%s" IPv6 OSPF convergence', router) @@ -196,39 +202,37 @@ def test_ospf6_convergence(): # Run test function until we get an result. Wait at most 60 seconds. test_func = partial(compare_show_ipv6_ospf6, router, expected) - result, diff = topotest.run_and_expect(test_func, '', - count=25, wait=3) + result, diff = topotest.run_and_expect(test_func, "", count=25, wait=3) if (not result) and (rnum == 1): # Didn't match the new ECMP version - try the old pre-ECMP format - ospf6route_file = '{}/ospf6route.txt' + ospf6route_file = "{}/ospf6route.txt" # Load expected results from the command reffile = os.path.join(CWD, ospf6route_file.format(router)) expected = open(reffile).read() test_func = partial(compare_show_ipv6_ospf6, router, expected) - result, diff = topotest.run_and_expect(test_func, '', - count=1, wait=3) + result, diff = topotest.run_and_expect(test_func, "", count=1, wait=3) if not result: # Didn't match the old version - switch back to new ECMP version # and fail - ospf6route_file = '{}/ospf6route_ecmp.txt' + ospf6route_file = "{}/ospf6route_ecmp.txt" # Load expected results from the command reffile = os.path.join(CWD, ospf6route_file.format(router)) expected = open(reffile).read() test_func = partial(compare_show_ipv6_ospf6, router, expected) - result, diff = topotest.run_and_expect(test_func, '', - count=1, wait=3) + result, diff = topotest.run_and_expect(test_func, "", count=1, wait=3) + + assert result, "OSPF6 did not converge on {}:\n{}".format(router, diff) - assert result, 'OSPF6 did not converge on {}:\n{}'.format(router, diff) def test_ospf6_kernel_route(): "Test OSPF kernel route installation" tgen = get_topogen() if tgen.routers_have_failure(): - pytest.skip('skipped because of router(s) failure') + pytest.skip("skipped because of router(s) failure") rlist = tgen.routers().values() for router in rlist: @@ -236,216 +240,231 @@ def test_ospf6_kernel_route(): routes = topotest.ip6_route(router) expected = { - '2001:db8:1::/64': {}, - '2001:db8:2::/64': {}, - '2001:db8:3::/64': {}, - '2001:db8:100::/64': {}, - '2001:db8:200::/64': {}, - '2001:db8:300::/64': {}, + "2001:db8:1::/64": {}, + "2001:db8:2::/64": {}, + "2001:db8:3::/64": {}, + "2001:db8:100::/64": {}, + "2001:db8:200::/64": {}, + "2001:db8:300::/64": {}, } assertmsg = 'OSPF IPv6 route mismatch in router "{}"'.format(router.name) assert topotest.json_cmp(routes, expected) is None, assertmsg + def test_ospf_json(): "Test 'show ip ospf json' output for coherency." tgen = get_topogen() if tgen.routers_have_failure(): - pytest.skip('skipped because of router(s) failure') + pytest.skip("skipped because of router(s) failure") for rnum in range(1, 5): - router = tgen.gears['r{}'.format(rnum)] + router = tgen.gears["r{}".format(rnum)] logger.info('Comparing router "%s" "show ip ospf json" output', router.name) expected = { - 'routerId': '10.0.255.{}'.format(rnum), - 'tosRoutesOnly': True, - 'rfc2328Conform': True, - 'spfScheduleDelayMsecs': 0, - 'holdtimeMinMsecs': 50, - 'holdtimeMaxMsecs': 5000, - 'lsaMinIntervalMsecs': 5000, - 'lsaMinArrivalMsecs': 1000, - 'writeMultiplier': 20, - 'refreshTimerMsecs': 10000, - 'asbrRouter': 'injectingExternalRoutingInformation', - 'attachedAreaCounter': 1, - 'areas': {} + "routerId": "10.0.255.{}".format(rnum), + "tosRoutesOnly": True, + "rfc2328Conform": True, + "spfScheduleDelayMsecs": 0, + "holdtimeMinMsecs": 50, + "holdtimeMaxMsecs": 5000, + "lsaMinIntervalMsecs": 5000, + "lsaMinArrivalMsecs": 1000, + "writeMultiplier": 20, + "refreshTimerMsecs": 10000, + "asbrRouter": "injectingExternalRoutingInformation", + "attachedAreaCounter": 1, + "areas": {}, } # Area specific additional checks - if router.name == 'r1' or router.name == 'r2' or router.name == 'r3': - expected['areas']['0.0.0.0'] = { - 'areaIfActiveCounter': 2, - 'areaIfTotalCounter': 2, - 'authentication': 'authenticationNone', - 'backbone': True, - 'lsaAsbrNumber': 1, - 'lsaNetworkNumber': 1, - 'lsaNssaNumber': 0, - 'lsaNumber': 7, - 'lsaOpaqueAreaNumber': 0, - 'lsaOpaqueLinkNumber': 0, - 'lsaRouterNumber': 3, - 'lsaSummaryNumber': 2, - 'nbrFullAdjacentCounter': 2, + if router.name == "r1" or router.name == "r2" or router.name == "r3": + expected["areas"]["0.0.0.0"] = { + "areaIfActiveCounter": 2, + "areaIfTotalCounter": 2, + "authentication": "authenticationNone", + "backbone": True, + "lsaAsbrNumber": 1, + "lsaNetworkNumber": 1, + "lsaNssaNumber": 0, + "lsaNumber": 7, + "lsaOpaqueAreaNumber": 0, + "lsaOpaqueLinkNumber": 0, + "lsaRouterNumber": 3, + "lsaSummaryNumber": 2, + "nbrFullAdjacentCounter": 2, } - if router.name == 'r3' or router.name == 'r4': - expected['areas']['0.0.0.1'] = { - 'areaIfActiveCounter': 1, - 'areaIfTotalCounter': 1, - 'authentication': 'authenticationNone', - 'lsaAsbrNumber': 2, - 'lsaNetworkNumber': 1, - 'lsaNssaNumber': 0, - 'lsaNumber': 9, - 'lsaOpaqueAreaNumber': 0, - 'lsaOpaqueLinkNumber': 0, - 'lsaRouterNumber': 2, - 'lsaSummaryNumber': 4, - 'nbrFullAdjacentCounter': 1, + if router.name == "r3" or router.name == "r4": + expected["areas"]["0.0.0.1"] = { + "areaIfActiveCounter": 1, + "areaIfTotalCounter": 1, + "authentication": "authenticationNone", + "lsaAsbrNumber": 2, + "lsaNetworkNumber": 1, + "lsaNssaNumber": 0, + "lsaNumber": 9, + "lsaOpaqueAreaNumber": 0, + "lsaOpaqueLinkNumber": 0, + "lsaRouterNumber": 2, + "lsaSummaryNumber": 4, + "nbrFullAdjacentCounter": 1, } # r4 has more interfaces for area 0.0.0.1 - if router.name == 'r4': - expected['areas']['0.0.0.1'].update({ - 'areaIfActiveCounter': 2, - 'areaIfTotalCounter': 2, - }) + if router.name == "r4": + expected["areas"]["0.0.0.1"].update( + {"areaIfActiveCounter": 2, "areaIfTotalCounter": 2,} + ) # router 3 has an additional area - if router.name == 'r3': - expected['attachedAreaCounter'] = 2 + if router.name == "r3": + expected["attachedAreaCounter"] = 2 - output = router.vtysh_cmd('show ip ospf json', isjson=True) + output = router.vtysh_cmd("show ip ospf json", isjson=True) result = topotest.json_cmp(output, expected) - assert result is None, '"{}" JSON output mismatches the expected result'.format(router.name) + assert result is None, '"{}" JSON output mismatches the expected result'.format( + router.name + ) + def test_ospf_link_down(): "Test OSPF convergence after a link goes down" tgen = get_topogen() if tgen.routers_have_failure(): - pytest.skip('skipped because of router(s) failure') + pytest.skip("skipped because of router(s) failure") # Simulate a network down event on router3 switch3 interface. - router3 = tgen.gears['r3'] - router3.peer_link_enable('r3-eth0', False) + router3 = tgen.gears["r3"] + router3.peer_link_enable("r3-eth0", False) # Expect convergence on all routers for router, rnode in tgen.routers().iteritems(): logger.info('Waiting for router "%s" convergence after link failure', router) # Load expected results from the command - reffile = os.path.join(CWD, '{}/ospfroute_down.txt'.format(router)) + reffile = os.path.join(CWD, "{}/ospfroute_down.txt".format(router)) expected = open(reffile).read() # Run test function until we get an result. Wait at most 80 seconds. test_func = partial( - topotest.router_output_cmp, rnode, 'show ip ospf route', expected) - result, diff = topotest.run_and_expect(test_func, '', - count=140, wait=0.5) - assert result, 'OSPF did not converge on {}:\n{}'.format(router, diff) + topotest.router_output_cmp, rnode, "show ip ospf route", expected + ) + result, diff = topotest.run_and_expect(test_func, "", count=140, wait=0.5) + assert result, "OSPF did not converge on {}:\n{}".format(router, diff) + def test_ospf_link_down_kernel_route(): "Test OSPF kernel route installation" tgen = get_topogen() if tgen.routers_have_failure(): - pytest.skip('skipped because of router(s) failure') + pytest.skip("skipped because of router(s) failure") rlist = tgen.routers().values() for router in rlist: - logger.info('Checking OSPF IPv4 kernel routes in "%s" after link down', router.name) + logger.info( + 'Checking OSPF IPv4 kernel routes in "%s" after link down', router.name + ) routes = topotest.ip4_route(router) expected = { - '10.0.1.0/24': {}, - '10.0.2.0/24': {}, - '10.0.3.0/24': {}, - '10.0.10.0/24': {}, - '172.16.0.0/24': {}, - '172.16.1.0/24': {}, + "10.0.1.0/24": {}, + "10.0.2.0/24": {}, + "10.0.3.0/24": {}, + "10.0.10.0/24": {}, + "172.16.0.0/24": {}, + "172.16.1.0/24": {}, } - if router.name == 'r1' or router.name == 'r2': - expected.update({ - '10.0.10.0/24': None, - '172.16.0.0/24': None, - '172.16.1.0/24': None, - }) - elif router.name == 'r3' or router.name == 'r4': - expected.update({ - '10.0.1.0/24': None, - '10.0.2.0/24': None, - }) + if router.name == "r1" or router.name == "r2": + expected.update( + {"10.0.10.0/24": None, "172.16.0.0/24": None, "172.16.1.0/24": None,} + ) + elif router.name == "r3" or router.name == "r4": + expected.update( + {"10.0.1.0/24": None, "10.0.2.0/24": None,} + ) # Route '10.0.3.0' is no longer available for r4 since it is down. - if router.name == 'r4': - expected.update({ - '10.0.3.0/24': None, - }) - assertmsg = 'OSPF IPv4 route mismatch in router "{}" after link down'.format(router.name) + if router.name == "r4": + expected.update( + {"10.0.3.0/24": None,} + ) + assertmsg = 'OSPF IPv4 route mismatch in router "{}" after link down'.format( + router.name + ) assert topotest.json_cmp(routes, expected) is None, assertmsg + def test_ospf6_link_down(): "Test OSPF6 daemon convergence after link goes down" tgen = get_topogen() if tgen.routers_have_failure(): - pytest.skip('skipped because of router(s) failure') + pytest.skip("skipped because of router(s) failure") for rnum in range(1, 5): - router = 'r{}'.format(rnum) + router = "r{}".format(rnum) - logger.info('Waiting for router "%s" IPv6 OSPF convergence after link down', router) + logger.info( + 'Waiting for router "%s" IPv6 OSPF convergence after link down', router + ) # Load expected results from the command - reffile = os.path.join(CWD, '{}/ospf6route_down.txt'.format(router)) + reffile = os.path.join(CWD, "{}/ospf6route_down.txt".format(router)) expected = open(reffile).read() # Run test function until we get an result. Wait at most 60 seconds. test_func = partial(compare_show_ipv6_ospf6, router, expected) - result, diff = topotest.run_and_expect(test_func, '', - count=25, wait=3) - assert result, 'OSPF6 did not converge on {}:\n{}'.format(router, diff) + result, diff = topotest.run_and_expect(test_func, "", count=25, wait=3) + assert result, "OSPF6 did not converge on {}:\n{}".format(router, diff) + def test_ospf6_link_down_kernel_route(): "Test OSPF kernel route installation" tgen = get_topogen() if tgen.routers_have_failure(): - pytest.skip('skipped because of router(s) failure') + pytest.skip("skipped because of router(s) failure") rlist = tgen.routers().values() for router in rlist: - logger.info('Checking OSPF IPv6 kernel routes in "%s" after link down', router.name) + logger.info( + 'Checking OSPF IPv6 kernel routes in "%s" after link down', router.name + ) routes = topotest.ip6_route(router) expected = { - '2001:db8:1::/64': {}, - '2001:db8:2::/64': {}, - '2001:db8:3::/64': {}, - '2001:db8:100::/64': {}, - '2001:db8:200::/64': {}, - '2001:db8:300::/64': {}, + "2001:db8:1::/64": {}, + "2001:db8:2::/64": {}, + "2001:db8:3::/64": {}, + "2001:db8:100::/64": {}, + "2001:db8:200::/64": {}, + "2001:db8:300::/64": {}, } - if router.name == 'r1' or router.name == 'r2': - expected.update({ - '2001:db8:100::/64': None, - '2001:db8:200::/64': None, - '2001:db8:300::/64': None, - }) - elif router.name == 'r3' or router.name == 'r4': - expected.update({ - '2001:db8:1::/64': None, - '2001:db8:2::/64': None, - }) + if router.name == "r1" or router.name == "r2": + expected.update( + { + "2001:db8:100::/64": None, + "2001:db8:200::/64": None, + "2001:db8:300::/64": None, + } + ) + elif router.name == "r3" or router.name == "r4": + expected.update( + {"2001:db8:1::/64": None, "2001:db8:2::/64": None,} + ) # Route '2001:db8:3::/64' is no longer available for r4 since it is down. - if router.name == 'r4': - expected.update({ - '2001:db8:3::/64': None, - }) - assertmsg = 'OSPF IPv6 route mismatch in router "{}" after link down'.format(router.name) + if router.name == "r4": + expected.update( + {"2001:db8:3::/64": None,} + ) + assertmsg = 'OSPF IPv6 route mismatch in router "{}" after link down'.format( + router.name + ) assert topotest.json_cmp(routes, expected) is None, assertmsg + def test_memory_leak(): "Run the memory leak test and report results." tgen = get_topogen() if not tgen.is_memleak_enabled(): - pytest.skip('Memory leak test/report is disabled') + pytest.skip("Memory leak test/report is disabled") tgen.report_memory_leaks() -if __name__ == '__main__': + +if __name__ == "__main__": args = ["-s"] + sys.argv[1:] sys.exit(pytest.main(args)) diff --git a/tests/topotests/ospf-topo2/r1/ospf-route.json b/tests/topotests/ospf-topo2/r1/ospf-route.json new file mode 100644 index 0000000000..6beb7e9bed --- /dev/null +++ b/tests/topotests/ospf-topo2/r1/ospf-route.json @@ -0,0 +1 @@ +{ "10.0.1.1\/32": { "routeType": "N", "cost": 10, "area": "0.0.0.0", "nexthops": [ { "ip": " ", "directly attached to": "r1-eth0" } ] }, "10.0.20.1\/32": { "routeType": "N", "cost": 20, "area": "0.0.0.0", "nexthops": [ { "ip": "10.0.3.2", "via": "r1-eth1" } ] }, "10.0.255.2": { "routeType": "R ", "cost": 10, "area": "0.0.0.0", "routerType": "asbr", "nexthops": [ { "ip": "10.0.3.2", "via": "r1-eth1" } ] } } diff --git a/tests/topotests/ospf-topo2/r1/ospfd.conf b/tests/topotests/ospf-topo2/r1/ospfd.conf new file mode 100644 index 0000000000..65843cbb83 --- /dev/null +++ b/tests/topotests/ospf-topo2/r1/ospfd.conf @@ -0,0 +1,13 @@ +! +interface r1-eth1 + ip ospf network point-to-point + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +router ospf + ospf router-id 10.0.255.1 + redistribute kernel + redistribute connected + redistribute static + network 0.0.0.0/0 area 0 +! diff --git a/tests/topotests/ospf-topo2/r1/v4_route.json b/tests/topotests/ospf-topo2/r1/v4_route.json new file mode 100644 index 0000000000..76c6396169 --- /dev/null +++ b/tests/topotests/ospf-topo2/r1/v4_route.json @@ -0,0 +1,84 @@ +{ + "10.0.1.1\/32":[ + { + "prefix":"10.0.1.1\/32", + "protocol":"ospf", + "distance":110, + "metric":10, + "table":254, + "nexthops":[ + { + "flags":9, + "ip":"0.0.0.0", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "onLink":true + } + ] + }, + { + "prefix":"10.0.1.1\/32", + "protocol":"connected", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"r1-eth0", + "active":true + } + ] + } + ], + "10.0.3.4\/32":[ + { + "prefix":"10.0.3.4\/32", + "protocol":"connected", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"r1-eth1", + "active":true + } + ] + } + ], + "10.0.20.1\/32":[ + { + "prefix":"10.0.20.1\/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "distance":110, + "metric":20, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":11, + "fib":true, + "ip":"10.0.3.2", + "afi":"ipv4", + "interfaceName":"r1-eth1", + "active":true, + "onLink":true + } + ] + } + ] +} diff --git a/tests/topotests/ospf-topo2/r1/zebra.conf b/tests/topotests/ospf-topo2/r1/zebra.conf new file mode 100644 index 0000000000..d96d9707c1 --- /dev/null +++ b/tests/topotests/ospf-topo2/r1/zebra.conf @@ -0,0 +1,7 @@ +! +interface r1-eth0 + ip address 10.0.1.1/32 +! +interface r1-eth1 + ip address 10.0.3.4/32 +! diff --git a/tests/topotests/ospf-topo2/r2/ospf-route.json b/tests/topotests/ospf-topo2/r2/ospf-route.json new file mode 100644 index 0000000000..3cfd255bfd --- /dev/null +++ b/tests/topotests/ospf-topo2/r2/ospf-route.json @@ -0,0 +1 @@ +{ "10.0.1.1\/32": { "routeType": "N", "cost": 20, "area": "0.0.0.0", "nexthops": [ { "ip": "10.0.3.4", "via": "r2-eth1" } ] }, "10.0.20.1\/32": { "routeType": "N", "cost": 10, "area": "0.0.0.0", "nexthops": [ { "ip": " ", "directly attached to": "r2-eth0" } ] }, "10.0.255.1": { "routeType": "R ", "cost": 10, "area": "0.0.0.0", "routerType": "asbr", "nexthops": [ { "ip": "10.0.3.4", "via": "r2-eth1" } ] } } diff --git a/tests/topotests/ospf-topo2/r2/ospfd.conf b/tests/topotests/ospf-topo2/r2/ospfd.conf new file mode 100644 index 0000000000..b032f1a8ac --- /dev/null +++ b/tests/topotests/ospf-topo2/r2/ospfd.conf @@ -0,0 +1,13 @@ +! +interface r2-eth1 + ip ospf network point-to-point + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +router ospf + ospf router-id 10.0.255.2 + redistribute kernel + redistribute connected + redistribute static + network 0.0.0.0/0 area 0 +! diff --git a/tests/topotests/ospf-topo2/r2/v4_route.json b/tests/topotests/ospf-topo2/r2/v4_route.json new file mode 100644 index 0000000000..1638536388 --- /dev/null +++ b/tests/topotests/ospf-topo2/r2/v4_route.json @@ -0,0 +1,84 @@ +{ + "10.0.1.1\/32":[ + { + "prefix":"10.0.1.1\/32", + "protocol":"ospf", + "selected":true, + "destSelected":true, + "distance":110, + "metric":20, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":11, + "fib":true, + "ip":"10.0.3.4", + "afi":"ipv4", + "interfaceName":"r2-eth1", + "active":true, + "onLink":true + } + ] + } + ], + "10.0.3.2\/32":[ + { + "prefix":"10.0.3.2\/32", + "protocol":"connected", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"r2-eth1", + "active":true + } + ] + } + ], + "10.0.20.1\/32":[ + { + "prefix":"10.0.20.1\/32", + "protocol":"ospf", + "distance":110, + "metric":10, + "table":254, + "nexthops":[ + { + "flags":9, + "ip":"0.0.0.0", + "afi":"ipv4", + "interfaceName":"r2-eth0", + "active":true, + "onLink":true + } + ] + }, + { + "prefix":"10.0.20.1\/32", + "protocol":"connected", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "interfaceName":"r2-eth0", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/ospf-topo2/r2/zebra.conf b/tests/topotests/ospf-topo2/r2/zebra.conf new file mode 100644 index 0000000000..f9dd2c4471 --- /dev/null +++ b/tests/topotests/ospf-topo2/r2/zebra.conf @@ -0,0 +1,7 @@ +! +interface r2-eth0 + ip address 10.0.20.1/32 +! +interface r2-eth1 + ip address 10.0.3.2/32 +! diff --git a/tests/topotests/ospf-topo2/test_ospf_topo2.py b/tests/topotests/ospf-topo2/test_ospf_topo2.py new file mode 100755 index 0000000000..a04d841214 --- /dev/null +++ b/tests/topotests/ospf-topo2/test_ospf_topo2.py @@ -0,0 +1,165 @@ +#!/usr/bin/env python + +# +# test_ospf_topo2.py +# +# Copyright (c) 2019 by +# Cumulus Networks, Inc +# Donald Sharp +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_ospf_topo2.py: Test the OSPF unnumbered. +""" + +import os +import re +import sys +from functools import partial +import pytest +import json + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, '../')) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + +class OSPFTopo(Topo): + "Test topology builder" + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # Create 4 routers + for routern in range(1, 3): + tgen.add_router('r{}'.format(routern)) + + # Create a empty network for router 1 + switch = tgen.add_switch('s1') + switch.add_link(tgen.gears['r1']) + + # Create a empty network for router 2 + switch = tgen.add_switch('s2') + switch.add_link(tgen.gears['r2']) + + # Interconect router 1, 2 + switch = tgen.add_switch('s3') + switch.add_link(tgen.gears['r1']) + switch.add_link(tgen.gears['r2']) + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(OSPFTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + for rname, router in router_list.iteritems(): + router.load_config( + TopoRouter.RD_ZEBRA, + os.path.join(CWD, '{}/zebra.conf'.format(rname)) + ) + router.load_config( + TopoRouter.RD_OSPF, + 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) + 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.all.rp_filter', 0) + + # Initialize all routers. + tgen.start_router() + #tgen.mininet_cli() + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + tgen.stop_topology() + + +def test_ospf_convergence(): + "Test OSPF daemon convergence and that we have received the ospf routes" + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip('skipped because of router(s) failure') + + for router, rnode in tgen.routers().iteritems(): + logger.info('Waiting for router "%s" convergence', router) + + json_file = '{}/{}/ospf-route.json'.format(CWD, router) + expected = json.loads(open(json_file).read()) + + test_func = partial(topotest.router_json_cmp, + rnode, 'show ip ospf route json', expected) + _, result = topotest.run_and_expect(test_func, None, count=160, wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router) + assert result is None, assertmsg + #tgen.mininet_cli() + +def test_ospf_kernel_route(): + "Test OSPF kernel route installation and we have the onlink success" + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip('skipped because of router(s) failure') + + rlist = tgen.routers().values() + for router in rlist: + logger.info('Checking OSPF IPv4 kernel routes in "%s"', router.name) + + json_file = '{}/{}/v4_route.json'.format(CWD, router.name) + expected = json.loads(open(json_file).read()) + + test_func = partial(topotest.router_json_cmp, + router, 'show ip route json', expected) + _, result = topotest.run_and_expect(test_func, None, count=10, wait=.5) + assertmsg = '"{}" JSON output mistmatches'.format(router) + assert result is None, assertmsg + #tgen.mininet_cli() + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip('Memory leak test/report is disabled') + + tgen.report_memory_leaks() + +if __name__ == '__main__': + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/ospf6-topo1/README.md b/tests/topotests/ospf6-topo1/README.md index 28f68e8fa5..526c019c6a 100644 --- a/tests/topotests/ospf6-topo1/README.md +++ b/tests/topotests/ospf6-topo1/README.md @@ -102,7 +102,7 @@ Simplified `R3` config Test is executed by running - vtysh -c "show log" | grep "Logging configuration for" + vtysh -c "show logging" | grep "Logging configuration for" on each FRR router. This should return the logging information for all daemons registered to Zebra and the list of running daemons is compared to the daemons started for this test (`zebra` and `ospf6d`) diff --git a/tests/topotests/ospf6-topo1/r1/ip_6_address.nhg.ref b/tests/topotests/ospf6-topo1/r1/ip_6_address.nhg.ref new file mode 100644 index 0000000000..11fd9fe3c4 --- /dev/null +++ b/tests/topotests/ospf6-topo1/r1/ip_6_address.nhg.ref @@ -0,0 +1,10 @@ +fc00:1111:1111:1111::/64 nhid XXXX via fc00:1:1:1::1234 dev r1-stubnet proto XXXX metric 20 pref medium +fc00:1:1:1::/64 dev r1-stubnet proto XXXX metric 256 pref medium +fc00:2222:2222:2222::/64 nhid XXXX via fe80::__(r2-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium +fc00:2:2:2::/64 nhid XXXX via fe80::__(r2-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium +fc00:3333:3333:3333::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium +fc00:3:3:3::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium +fc00:4444:4444:4444::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium +fc00:4:4:4::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium +fc00:a:a:a::/64 dev r1-sw5 proto XXXX metric 256 pref medium +fc00:b:b:b::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium diff --git a/tests/topotests/ospf6-topo1/r1/ospf6d.conf-pre-v4 b/tests/topotests/ospf6-topo1/r1/ospf6d.conf-pre-v4 deleted file mode 100644 index c4b382171d..0000000000 --- a/tests/topotests/ospf6-topo1/r1/ospf6d.conf-pre-v4 +++ /dev/null @@ -1,27 +0,0 @@ -hostname r1 -log file ospf6d.log -! -debug ospf6 message all -debug ospf6 lsa unknown -debug ospf6 zebra -debug ospf6 interface -debug ospf6 neighbor -debug ospf6 route table -debug ospf6 flooding -! -interface r1-stubnet - ipv6 ospf6 network broadcast -! -interface r1-sw5 - ipv6 ospf6 network broadcast -! -router ospf6 - router-id 10.0.0.1 - log-adjacency-changes detail - redistribute static - interface r1-stubnet area 0.0.0.0 - interface r1-sw5 area 0.0.0.0 -! -line vty - exec-timeout 0 0 -! diff --git a/tests/topotests/ospf6-topo1/r1/show_ipv6_route.ref b/tests/topotests/ospf6-topo1/r1/show_ipv6_route.ref index c961512bd9..a2ddf7c5ae 100644 --- a/tests/topotests/ospf6-topo1/r1/show_ipv6_route.ref +++ b/tests/topotests/ospf6-topo1/r1/show_ipv6_route.ref @@ -1,9 +1,9 @@ -O fc00:1:1:1::/64 [110/10] is directly connected, r1-stubnet -O>* fc00:2:2:2::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5 -O>* fc00:3:3:3::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5 -O>* fc00:4:4:4::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5 -O fc00:a:a:a::/64 [110/10] is directly connected, r1-sw5 -O>* fc00:b:b:b::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5 -O>* fc00:2222:2222:2222::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5 -O>* fc00:3333:3333:3333::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5 -O>* fc00:4444:4444:4444::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5 +O fc00:1:1:1::/64 [110/10] is directly connected, r1-stubnet, weight 1, XX:XX:XX +O>* fc00:2:2:2::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX +O>* fc00:3:3:3::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX +O>* fc00:4:4:4::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX +O fc00:a:a:a::/64 [110/10] is directly connected, r1-sw5, weight 1, XX:XX:XX +O>* fc00:b:b:b::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX +O>* fc00:2222:2222:2222::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX +O>* fc00:3333:3333:3333::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX +O>* fc00:4444:4444:4444::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX diff --git a/tests/topotests/ospf6-topo1/r1/zebra.conf b/tests/topotests/ospf6-topo1/r1/zebra.conf index de298f40e1..dfbcea8d21 100644 --- a/tests/topotests/ospf6-topo1/r1/zebra.conf +++ b/tests/topotests/ospf6-topo1/r1/zebra.conf @@ -2,6 +2,9 @@ hostname r1 log file zebra.log ! +debug zebra events +debug zebra rib +! interface r1-stubnet ipv6 address fc00:1:1:1::1/64 ! diff --git a/tests/topotests/ospf6-topo1/r2/ospf6d.conf-pre-v4 b/tests/topotests/ospf6-topo1/r2/ospf6d.conf-pre-v4 deleted file mode 100644 index bb9958d173..0000000000 --- a/tests/topotests/ospf6-topo1/r2/ospf6d.conf-pre-v4 +++ /dev/null @@ -1,27 +0,0 @@ -hostname r2 -log file ospf6d.log -! -debug ospf6 message all -debug ospf6 lsa unknown -debug ospf6 zebra -debug ospf6 interface -debug ospf6 neighbor -debug ospf6 route table -debug ospf6 flooding -! -interface r2-stubnet - ipv6 ospf6 network broadcast -! -interface r2-sw5 - ipv6 ospf6 network broadcast -! -router ospf6 - router-id 10.0.0.2 - log-adjacency-changes detail - redistribute static - interface r2-stubnet area 0.0.0.0 - interface r2-sw5 area 0.0.0.0 -! -line vty - exec-timeout 0 0 -! diff --git a/tests/topotests/ospf6-topo1/r2/show_ipv6_route.ref b/tests/topotests/ospf6-topo1/r2/show_ipv6_route.ref index 014eddbcb6..1f642b1b22 100644 --- a/tests/topotests/ospf6-topo1/r2/show_ipv6_route.ref +++ b/tests/topotests/ospf6-topo1/r2/show_ipv6_route.ref @@ -1,10 +1,10 @@ -O>* fc00:1:1:1::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5 -O fc00:2:2:2::/64 [110/10] is directly connected, r2-stubnet -O>* fc00:3:3:3::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5 -O>* fc00:4:4:4::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5 -O fc00:a:a:a::/64 [110/10] is directly connected, r2-sw5 -O>* fc00:b:b:b::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5 -O>* fc00:1111:1111:1111::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5 -O>* fc00:3333:3333:3333::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5 -O>* fc00:4444:4444:4444::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5 +O>* fc00:1:1:1::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX +O fc00:2:2:2::/64 [110/10] is directly connected, r2-stubnet, weight 1, XX:XX:XX +O>* fc00:3:3:3::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX +O>* fc00:4:4:4::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX +O fc00:a:a:a::/64 [110/10] is directly connected, r2-sw5, weight 1, XX:XX:XX +O>* fc00:b:b:b::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX +O>* fc00:1111:1111:1111::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX +O>* fc00:3333:3333:3333::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX +O>* fc00:4444:4444:4444::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX diff --git a/tests/topotests/ospf6-topo1/r2/zebra.conf b/tests/topotests/ospf6-topo1/r2/zebra.conf index d5345fede6..f05d1a60ff 100644 --- a/tests/topotests/ospf6-topo1/r2/zebra.conf +++ b/tests/topotests/ospf6-topo1/r2/zebra.conf @@ -2,6 +2,9 @@ hostname r2 log file zebra.log ! +debug zebra events +debug zebra rib +! interface r2-stubnet ipv6 address fc00:2:2:2::2/64 ! diff --git a/tests/topotests/ospf6-topo1/r3/ospf6d.conf-pre-v4 b/tests/topotests/ospf6-topo1/r3/ospf6d.conf-pre-v4 deleted file mode 100644 index d2dbc4a41c..0000000000 --- a/tests/topotests/ospf6-topo1/r3/ospf6d.conf-pre-v4 +++ /dev/null @@ -1,31 +0,0 @@ -hostname r3 -log file ospf6d.log -! -debug ospf6 message all -debug ospf6 lsa unknown -debug ospf6 zebra -debug ospf6 interface -debug ospf6 neighbor -debug ospf6 route table -debug ospf6 flooding -! -interface r3-stubnet - ipv6 ospf6 network broadcast -! -interface r3-sw5 - ipv6 ospf6 network broadcast -! -interface r3-sw6 - ipv6 ospf6 network broadcast -! -router ospf6 - router-id 10.0.0.3 - log-adjacency-changes detail - redistribute static - interface r3-stubnet area 0.0.0.0 - interface r3-sw5 area 0.0.0.0 - interface r3-sw6 area 0.0.0.1 -! -line vty - exec-timeout 0 0 -! diff --git a/tests/topotests/ospf6-topo1/r3/show_ipv6_route.ref b/tests/topotests/ospf6-topo1/r3/show_ipv6_route.ref index 1ac7cbd6b4..8e3afa583a 100644 --- a/tests/topotests/ospf6-topo1/r3/show_ipv6_route.ref +++ b/tests/topotests/ospf6-topo1/r3/show_ipv6_route.ref @@ -1,10 +1,10 @@ -O>* fc00:1:1:1::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5 -O>* fc00:2:2:2::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5 -O fc00:3:3:3::/64 [110/10] is directly connected, r3-stubnet -O>* fc00:4:4:4::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw6 -O fc00:a:a:a::/64 [110/10] is directly connected, r3-sw5 -O fc00:b:b:b::/64 [110/10] is directly connected, r3-sw6 -O>* fc00:1111:1111:1111::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5 -O>* fc00:2222:2222:2222::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5 -O>* fc00:4444:4444:4444::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw6 +O>* fc00:1:1:1::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, weight 1, XX:XX:XX +O>* fc00:2:2:2::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, weight 1, XX:XX:XX +O fc00:3:3:3::/64 [110/10] is directly connected, r3-stubnet, weight 1, XX:XX:XX +O>* fc00:4:4:4::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw6, weight 1, XX:XX:XX +O fc00:a:a:a::/64 [110/10] is directly connected, r3-sw5, weight 1, XX:XX:XX +O fc00:b:b:b::/64 [110/10] is directly connected, r3-sw6, weight 1, XX:XX:XX +O>* fc00:1111:1111:1111::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, weight 1, XX:XX:XX +O>* fc00:2222:2222:2222::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, weight 1, XX:XX:XX +O>* fc00:4444:4444:4444::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw6, weight 1, XX:XX:XX diff --git a/tests/topotests/ospf6-topo1/r3/zebra.conf b/tests/topotests/ospf6-topo1/r3/zebra.conf index 11f1ff59f2..d8051c350d 100644 --- a/tests/topotests/ospf6-topo1/r3/zebra.conf +++ b/tests/topotests/ospf6-topo1/r3/zebra.conf @@ -2,6 +2,9 @@ hostname r3 log file zebra.log ! +debug zebra events +debug zebra rib +! interface r3-stubnet ipv6 address fc00:3:3:3::3/64 ! diff --git a/tests/topotests/ospf6-topo1/r4/ospf6d.conf-pre-v4 b/tests/topotests/ospf6-topo1/r4/ospf6d.conf-pre-v4 deleted file mode 100644 index 6f9c30d75a..0000000000 --- a/tests/topotests/ospf6-topo1/r4/ospf6d.conf-pre-v4 +++ /dev/null @@ -1,27 +0,0 @@ -hostname r4 -log file ospf6d.log -! -debug ospf6 message all -debug ospf6 lsa unknown -debug ospf6 zebra -debug ospf6 interface -debug ospf6 neighbor -debug ospf6 route table -debug ospf6 flooding -! -interface r4-stubnet - ipv6 ospf6 network broadcast -! -interface r4-sw6 - ipv6 ospf6 network broadcast -! -router ospf6 - router-id 10.0.0.4 - log-adjacency-changes detail - redistribute static - interface r4-stubnet area 0.0.0.1 - interface r4-sw6 area 0.0.0.1 -! -line vty - exec-timeout 0 0 -! diff --git a/tests/topotests/ospf6-topo1/r4/show_ipv6_route.ref b/tests/topotests/ospf6-topo1/r4/show_ipv6_route.ref index 698dea6c7b..0df652ffb3 100644 --- a/tests/topotests/ospf6-topo1/r4/show_ipv6_route.ref +++ b/tests/topotests/ospf6-topo1/r4/show_ipv6_route.ref @@ -1,9 +1,9 @@ -O>* fc00:1:1:1::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6 -O>* fc00:2:2:2::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6 -O>* fc00:3:3:3::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6 -O fc00:4:4:4::/64 [110/10] is directly connected, r4-stubnet -O>* fc00:a:a:a::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6 -O fc00:b:b:b::/64 [110/10] is directly connected, r4-sw6 -O>* fc00:1111:1111:1111::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6 -O>* fc00:2222:2222:2222::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6 -O>* fc00:3333:3333:3333::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6 +O>* fc00:1:1:1::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX +O>* fc00:2:2:2::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX +O>* fc00:3:3:3::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX +O fc00:4:4:4::/64 [110/10] is directly connected, r4-stubnet, weight 1, XX:XX:XX +O>* fc00:a:a:a::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX +O fc00:b:b:b::/64 [110/10] is directly connected, r4-sw6, weight 1, XX:XX:XX +O>* fc00:1111:1111:1111::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX +O>* fc00:2222:2222:2222::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX +O>* fc00:3333:3333:3333::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX diff --git a/tests/topotests/ospf6-topo1/r4/zebra.conf b/tests/topotests/ospf6-topo1/r4/zebra.conf index 4b0a8a1f92..cada58bd01 100644 --- a/tests/topotests/ospf6-topo1/r4/zebra.conf +++ b/tests/topotests/ospf6-topo1/r4/zebra.conf @@ -2,6 +2,9 @@ hostname r4 log file zebra.log ! +debug zebra events +debug zebra rib +! interface r4-stubnet ipv6 address fc00:4:4:4::4/64 ! diff --git a/tests/topotests/ospf6-topo1/test_ospf6_topo1.py b/tests/topotests/ospf6-topo1/test_ospf6_topo1.py index 5da04b6449..30c09ea606 100755 --- a/tests/topotests/ospf6-topo1/test_ospf6_topo1.py +++ b/tests/topotests/ospf6-topo1/test_ospf6_topo1.py @@ -76,21 +76,20 @@ import pytest from time import sleep -from mininet.topo import Topo -from mininet.net import Mininet -from mininet.node import Node, OVSSwitch, Host -from mininet.log import setLogLevel, info -from mininet.cli import CLI -from mininet.link import Intf - from functools import partial -sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) -from lib import topotest - +from mininet.topo import Topo -fatal_error = "" +# Save the Current Working Directory to find configuration files later. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +import platform ##################################################### ## @@ -98,334 +97,347 @@ ## ##################################################### + class NetworkTopo(Topo): "OSPFv3 (IPv6) Test Topology 1" def build(self, **_opts): - # - # Define Switches first - # - switch = {} - for i in range(1, 7): - switch[i] = self.addSwitch('SW%s' % i, - dpid=topotest.int2dpid(i), - cls=topotest.LegacySwitch) - # - # Define FRR/Quagga Routers - # - router = {} - for i in range(1, 5): - router[i] = topotest.addRouter(self, 'r%s' % i) + "Build function" + + tgen = get_topogen(self) + + # Create 4 routers + for routern in range(1, 5): + tgen.add_router("r{}".format(routern)) # # Wire up the switches and routers + # Note that we specify the link names so we match the config files # - # Stub nets - for i in range(1, 5): - self.addLink(switch[i], router[i], intfName2='r%s-stubnet' % i) - # Switch 5 - self.addLink(switch[5], router[1], intfName2='r1-sw5') - self.addLink(switch[5], router[2], intfName2='r2-sw5') - self.addLink(switch[5], router[3], intfName2='r3-sw5') - # Switch 6 - self.addLink(switch[6], router[3], intfName2='r3-sw6') - self.addLink(switch[6], router[4], intfName2='r4-sw6') + # Create a empty network for router 1 + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"], nodeif="r1-stubnet") -##################################################### -## -## Tests starting -## -##################################################### + # Create a empty network for router 2 + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r2"], nodeif="r2-stubnet") -def setup_module(module): - global topo, net + # Create a empty network for router 3 + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r3"], nodeif="r3-stubnet") - print("\n\n** %s: Setup Topology" % module.__name__) - print("******************************************\n") + # Create a empty network for router 4 + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["r4"], nodeif="r4-stubnet") - print("Cleanup old Mininet runs") - os.system('sudo mn -c > /dev/null 2>&1') + # Interconnect routers 1, 2, and 3 + switch = tgen.add_switch("s5") + switch.add_link(tgen.gears["r1"], nodeif="r1-sw5") + switch.add_link(tgen.gears["r2"], nodeif="r2-sw5") + switch.add_link(tgen.gears["r3"], nodeif="r3-sw5") - thisDir = os.path.dirname(os.path.realpath(__file__)) - topo = NetworkTopo() + # Interconnect routers 3 and 4 + switch = tgen.add_switch("s6") + switch.add_link(tgen.gears["r3"], nodeif="r3-sw6") + switch.add_link(tgen.gears["r4"], nodeif="r4-sw6") - net = Mininet(controller=None, topo=topo) - net.start() - # For debugging after starting net, but before starting FRR/Quagga, uncomment the next line - # CLI(net) +##################################################### +## +## Tests starting +## +##################################################### - ospf_config = 'ospf6d.conf' - if net['r1'].checkRouterVersion('<', '4.0'): - ospf_config = 'ospf6d.conf-pre-v4' - # Starting Routers - for i in range(1, 5): - net['r%s' % i].loadConf('zebra', '%s/r%s/zebra.conf' % (thisDir, i)) - net['r%s' % i].loadConf('ospf6d', '%s/r%s/%s' % (thisDir, i, ospf_config)) - net['r%s' % i].startRouter() +def setup_module(mod): + "Sets up the pytest environment" - # For debugging after starting FRR/Quagga daemons, uncomment the next line - # CLI(net) + tgen = Topogen(NetworkTopo, mod.__name__) + tgen.start_topology() + logger.info("** %s: Setup Topology" % mod.__name__) + logger.info("******************************************") -def teardown_module(module): - global net + # For debugging after starting net, but before starting FRR, + # uncomment the next line + # tgen.mininet_cli() - print("\n\n** %s: Shutdown Topology" % module.__name__) - print("******************************************\n") + router_list = tgen.routers() + for rname, router in router_list.iteritems(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_OSPF6, os.path.join(CWD, "{}/ospf6d.conf".format(rname)) + ) - # End - Shutdown network - net.stop() + # Initialize all routers. + tgen.start_router() + # For debugging after starting FRR daemons, uncomment the next line + # tgen.mininet_cli() -def test_router_running(): - global fatal_error - global net - # Skip if previous fatal error condition is raised - if (fatal_error != ""): - pytest.skip(fatal_error) +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + tgen.stop_topology() - print("\n\n** Check if FRR/Quagga is running on each Router node") - print("******************************************\n") - sleep(5) - # Make sure that all daemons are running - for i in range(1, 5): - fatal_error = net['r%s' % i].checkRouterRunning() - assert fatal_error == "", fatal_error +def test_ospf6_converged(): - # For debugging after starting FRR/Quagga daemons, uncomment the next line - # CLI(net) + tgen = get_topogen() -def test_ospf6_converged(): - global fatal_error - global net + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) - # Skip if previous fatal error condition is raised - if (fatal_error != ""): - pytest.skip(fatal_error) + # For debugging, uncomment the next line + # tgen.mininet_cli() # Wait for OSPF6 to converge (All Neighbors in either Full or TwoWay State) - print("\n\n** Verify OSPF6 daemons to converge") - print("******************************************\n") + logger.info("Waiting for OSPF6 convergence") + + # Set up for regex + pat1 = re.compile("^[0-9]") + pat2 = re.compile("Full") + timeout = 60 while timeout > 0: - print("Timeout in %s: " % timeout), + logger.info("Timeout in %s: " % timeout), sys.stdout.flush() + # Look for any node not yet converged - for i in range(1, 5): - notConverged = net['r%s' % i].cmd('vtysh -c "show ipv6 ospf neigh" 2> /dev/null | grep ^[0-9] | grep -v Full') - if notConverged: - print('Waiting for r%s' %i) + for router, rnode in tgen.routers().iteritems(): + resStr = rnode.vtysh_cmd("show ipv6 ospf neigh") + + isConverged = False + + for line in resStr.splitlines(): + res1 = pat1.match(line) + if res1: + isConverged = True + res2 = pat2.search(line) + + if res2 == None: + isConverged = False + break + + if isConverged == False: + logger.info("Waiting for {}".format(router)) sys.stdout.flush() break - if notConverged: + + if isConverged: + logger.info("Done") + break + else: sleep(5) timeout -= 5 - else: - print('Done') - print(notConverged) - break - else: + + if timeout == 0: # Bail out with error if a router fails to converge - ospfStatus = net['r%s' % i].cmd('vtysh -c "show ipv6 ospf neigh"') - fatal_error = "OSPFv6 did not converge" - assert False, "OSPFv6 did not converge:\n%s" % ospfStatus + ospfStatus = rnode.vtysh_cmd("show ipv6 ospf neigh") + assert False, "OSPFv6 did not converge:\n{}".format(ospfStatus) - print("OSPFv3 converged.") + logger.info("OSPFv3 converged.") - if timeout < 60: - # Only wait if we actually went through a convergence - print("\nwaiting 15s for routes to populate") - sleep(15) + # For debugging, uncomment the next line + # tgen.mininet_cli() # Make sure that all daemons are still running - for i in range(1, 5): - fatal_error = net['r%s' % i].checkRouterRunning() - assert fatal_error == "", fatal_error + if tgen.routers_have_failure(): + assert tgen.errors == "", tgen.errors -def test_ospfv3_routingTable(): - global fatal_error - global net - # Skip if previous fatal error condition is raised - if (fatal_error != ""): - pytest.skip(fatal_error) +def compare_show_ipv6(rname, expected): + """ + Calls 'show ipv6 route' for router `rname` and compare the obtained + result with the expected output. + """ + tgen = get_topogen() - thisDir = os.path.dirname(os.path.realpath(__file__)) + # Use the vtysh output, with some masking to make comparison easy + current = topotest.ip6_route_zebra(tgen.gears[rname]) - # Verify OSPFv3 Routing Table - print("\n\n** Verifying OSPFv3 Routing Table") - print("******************************************\n") - failures = 0 - for i in range(1, 5): - refTableFile = '%s/r%s/show_ipv6_route.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 ipv6 route" 2> /dev/null | grep "^O"').rstrip() - # Mask out Link-Local mac address portion. They are random... - actual = re.sub(r" fe80::[0-9a-f:]+", " fe80::XXXX:XXXX:XXXX:XXXX", actual) - # Drop timers on end of line (older Quagga Versions) - actual = re.sub(r", [0-2][0-9]:[0-5][0-9]:[0-5][0-9]", "", actual) - # Fix newlines (make them all the same) - actual = ('\n'.join(actual.splitlines()) + '\n').splitlines(1) + # Use just the 'O'spf lines of the output + linearr = [] + for line in current.splitlines(): + if re.match("^O", line): + linearr.append(line) - # Generate Diff - diff = topotest.get_textdiff(actual, expected, - title1="actual OSPFv3 IPv6 routing table", - title2="expected OSPFv3 IPv6 routing table") + current = "\n".join(linearr) - # Empty string if it matches, otherwise diff contains unified diff - if diff: - sys.stderr.write('r%s failed OSPFv3 (IPv6) Routing Table Check:\n%s\n' % (i, diff)) - failures += 1 - else: - print("r%s ok" % i) + return topotest.difflines( + topotest.normalize_text(current), + topotest.normalize_text(expected), + title1="Current output", + title2="Expected output", + ) - assert failures == 0, "OSPFv3 (IPv6) Routing Table verification failed for router r%s:\n%s" % (i, diff) - # Make sure that all daemons are still running - for i in range(1, 5): - fatal_error = net['r%s' % i].checkRouterRunning() - assert fatal_error == "", fatal_error +def test_ospfv3_routingTable(): + + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + # For debugging, uncomment the next line + # tgen.mininet_cli() + + # Verify OSPFv3 Routing Table + for router, rnode in tgen.routers().iteritems(): + logger.info('Waiting for router "%s" convergence', router) + + # Load expected results from the command + reffile = os.path.join(CWD, "{}/show_ipv6_route.ref".format(router)) + expected = open(reffile).read() + + # Run test function until we get an result. Wait at most 60 seconds. + test_func = partial(compare_show_ipv6, router, expected) + result, diff = topotest.run_and_expect(test_func, "", count=120, wait=0.5) + assert result, "OSPFv3 did not converge on {}:\n{}".format(router, diff) - # For debugging after starting FRR/Quagga daemons, uncomment the next line - # CLI(net) def test_linux_ipv6_kernel_routingTable(): - global fatal_error - global net - # Skip if previous fatal error condition is raised - if (fatal_error != ""): - pytest.skip(fatal_error) + tgen = get_topogen() - thisDir = os.path.dirname(os.path.realpath(__file__)) + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") # Verify Linux Kernel Routing Table - print("\n\n** Verifying Linux IPv6 Kernel Routing Table") - print("******************************************\n") + logger.info("Verifying Linux IPv6 Kernel Routing Table") + failures = 0 # Get a list of all current link-local addresses first as they change for # each run and we need to translate them linklocals = [] for i in range(1, 5): - linklocals += net['r%s' % i].get_ipv6_linklocal() + linklocals += tgen.net["r{}".format(i)].get_ipv6_linklocal() + + # Now compare the routing tables (after substituting link-local addresses) - # Now compare the routing tables (after substituting link-local addresses) for i in range(1, 5): - refTableFile = '%s/r%s/ip_6_address.ref' % (thisDir, i) - if os.path.isfile(refTableFile): + # Actual output from router + actual = tgen.gears["r{}".format(i)].run("ip -6 route").rstrip() + if "nhid" in actual: + refTableFile = os.path.join(CWD, "r{}/ip_6_address.nhg.ref".format(i)) + else: + refTableFile = os.path.join(CWD, "r{}/ip_6_address.ref".format(i)) + if os.path.isfile(refTableFile): expected = open(refTableFile).read().rstrip() # Fix newlines (make them all the same) - expected = ('\n'.join(expected.splitlines())).splitlines(1) + expected = ("\n".join(expected.splitlines())).splitlines(1) - # Actual output from router - actual = net['r%s' % i].cmd('ip -6 route').rstrip() # Mask out Link-Local mac addresses for ll in linklocals: actual = actual.replace(ll[1], "fe80::__(%s)__" % ll[0]) # Mask out protocol name or number actual = re.sub(r"[ ]+proto [0-9a-z]+ +", " proto XXXX ", actual) + actual = re.sub(r"[ ]+nhid [0-9]+ +", " nhid XXXX ", actual) # Remove ff00::/8 routes (seen on some kernels - not from FRR) - actual = re.sub(r'ff00::/8.*', '', actual) + actual = re.sub(r"ff00::/8.*", "", actual) # Strip empty lines actual = actual.lstrip() actual = actual.rstrip() - actual = re.sub(r' +', ' ', actual) + actual = re.sub(r" +", " ", actual) filtered_lines = [] for line in sorted(actual.splitlines()): - if line.startswith('fe80::/64 ') \ - or line.startswith('unreachable fe80::/64 '): + if line.startswith("fe80::/64 ") or line.startswith( + "unreachable fe80::/64 " + ): continue filtered_lines.append(line) - actual = '\n'.join(filtered_lines).splitlines(1) + actual = "\n".join(filtered_lines).splitlines(1) # Print Actual table - # print("Router r%s table" % i) + # logger.info("Router r%s table" % i) # for line in actual: - # print(line.rstrip()) + # logger.info(line.rstrip()) # Generate Diff - diff = topotest.get_textdiff(actual, expected, + diff = topotest.get_textdiff( + actual, + expected, title1="actual OSPFv3 IPv6 routing table", - title2="expected OSPFv3 IPv6 routing table") + title2="expected OSPFv3 IPv6 routing table", + ) # Empty string if it matches, otherwise diff contains unified diff if diff: - sys.stderr.write('r%s failed Linux IPv6 Kernel Routing Table Check:\n%s\n' % (i, diff)) + sys.stderr.write( + "r%s failed Linux IPv6 Kernel Routing Table Check:\n%s\n" + % (i, diff) + ) failures += 1 else: - print("r%s ok" % i) + logger.info("r%s ok" % i) - assert failures == 0, "Linux Kernel IPv6 Routing Table verification failed for router r%s:\n%s" % (i, diff) - - # For debugging after starting FRR/Quagga daemons, uncomment the next line - # CLI(net) + assert failures == 0, ( + "Linux Kernel IPv6 Routing Table verification failed for router r%s:\n%s" + % (i, diff) + ) def test_shutdown_check_stderr(): - global fatal_error - global net - # Skip if previous fatal error condition is raised - if (fatal_error != ""): - pytest.skip(fatal_error) + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") - if os.environ.get('TOPOTESTS_CHECK_STDERR') is None: - print("SKIPPED final check on StdErr output: Disabled (TOPOTESTS_CHECK_STDERR undefined)\n") - pytest.skip('Skipping test for Stderr output') + if os.environ.get("TOPOTESTS_CHECK_STDERR") is None: + logger.info( + "SKIPPED final check on StdErr output: Disabled (TOPOTESTS_CHECK_STDERR undefined)\n" + ) + pytest.skip("Skipping test for Stderr output") - thisDir = os.path.dirname(os.path.realpath(__file__)) + net = tgen.net - print("\n\n** Verifying unexpected STDERR output from daemons") - print("******************************************\n") + logger.info("\n\n** Verifying unexpected STDERR output from daemons") + logger.info("******************************************") for i in range(1, 5): - net['r%s' % i].stopRouter() - log = net['r%s' % i].getStdErr('ospf6d') + net["r%s" % i].stopRouter() + log = net["r%s" % i].getStdErr("ospf6d") if log: - print("\nRouter r%s OSPF6d StdErr Log:\n%s" % (i, log)) - log = net['r%s' % i].getStdErr('zebra') + logger.info("\nRouter r%s OSPF6d StdErr Log:\n%s" % (i, log)) + log = net["r%s" % i].getStdErr("zebra") if log: - print("\nRouter r%s Zebra StdErr Log:\n%s" % (i, log)) + logger.info("\nRouter r%s Zebra StdErr Log:\n%s" % (i, log)) def test_shutdown_check_memleak(): - global fatal_error - global net + "Run the memory leak test and report results." - # Skip if previous fatal error condition is raised - if (fatal_error != ""): - pytest.skip(fatal_error) + if os.environ.get("TOPOTESTS_CHECK_MEMLEAK") is None: + logger.info( + "SKIPPED final check on Memory leaks: Disabled (TOPOTESTS_CHECK_MEMLEAK undefined)" + ) + pytest.skip("Skipping test for memory leaks") - if os.environ.get('TOPOTESTS_CHECK_MEMLEAK') is None: - print("SKIPPED final check on Memory leaks: Disabled (TOPOTESTS_CHECK_MEMLEAK undefined)\n") - pytest.skip('Skipping test for memory leaks') - - thisDir = os.path.dirname(os.path.realpath(__file__)) + tgen = get_topogen() + + net = tgen.net for i in range(1, 5): - net['r%s' % i].stopRouter() - net['r%s' % i].report_memory_leaks(os.environ.get('TOPOTESTS_CHECK_MEMLEAK'), os.path.basename(__file__)) + net["r%s" % i].stopRouter() + net["r%s" % i].report_memory_leaks( + os.environ.get("TOPOTESTS_CHECK_MEMLEAK"), os.path.basename(__file__) + ) -if __name__ == '__main__': +if __name__ == "__main__": - setLogLevel('info') - # To suppress tracebacks, either use the following pytest call or add "--tb=no" to cli + # To suppress tracebacks, either use the following pytest call or + # add "--tb=no" to cli # retval = pytest.main(["-s", "--tb=no"]) + retval = pytest.main(["-s"]) sys.exit(retval) diff --git a/tests/topotests/pbr-topo1/__init__.py b/tests/topotests/pbr-topo1/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/pbr-topo1/r1/linux-rules.json b/tests/topotests/pbr-topo1/r1/linux-rules.json new file mode 100644 index 0000000000..5af4363418 --- /dev/null +++ b/tests/topotests/pbr-topo1/r1/linux-rules.json @@ -0,0 +1,19 @@ +[ + { + "iif": "r1-eth1", + "pref": "304", + "from": "4.5.6.7" + }, + { + "to": "3.4.5.0/24", + "iif": "r1-eth2", + "pref": "304", + "from": "1.2.0.0/16" + }, + { + "to": "9.9.9.9", + "iif": "r1-eth1", + "pref": "309", + "from": "all" + } +] diff --git a/tests/topotests/pbr-topo1/r1/pbr-interface.json b/tests/topotests/pbr-topo1/r1/pbr-interface.json new file mode 100644 index 0000000000..e28d9fb149 --- /dev/null +++ b/tests/topotests/pbr-topo1/r1/pbr-interface.json @@ -0,0 +1,27 @@ +[ + { + "name":"r1-eth1", + "policy":"EVA", + "valid":true + }, + { + "name":"r1-eth2", + "policy":"DONNA", + "valid":true + }, + { + "name":"r1-eth3", + "policy":"AKIHABARA", + "valid":true + }, + { + "name":"r1-eth4", + "policy":"ASAKUSA", + "valid":true + }, + { + "name":"r1-noexist", + "policy":"NOEXIST", + "valid":false + } +] diff --git a/tests/topotests/pbr-topo1/r1/pbr-map.json b/tests/topotests/pbr-topo1/r1/pbr-map.json new file mode 100644 index 0000000000..bfa0ecb849 --- /dev/null +++ b/tests/topotests/pbr-topo1/r1/pbr-map.json @@ -0,0 +1,152 @@ +[ + { + "name":"AKIHABARA", + "valid":false, + "policies":[ + { + "sequenceNumber":5, + "vrfUnchanged":false, + "installed":true, + "installedReason":"Valid", + "nexthopGroup": { + "name":"C", + "installed":true, + "installedInternally":1 + }, + "matchDst":"192.168.4.0\/24" + }, + { + "sequenceNumber":10, + "vrfUnchanged":false, + "installed":true, + "installedReason":"Invalid Src or Dst", + "nexthopGroup":{ + "name":"C", + "installed":true, + "installedInternally":1 + } + }, + { + "sequenceNumber":15, + "vrfUnchanged":false, + "installed":false, + "installedReason":"No Nexthops" + } + ] + }, + { + "name":"ASAKUSA", + "valid":true, + "policies":[ + { + "sequenceNumber":5, + "vrfUnchanged":false, + "installed":true, + "installedReason":"Valid", + "matchDst":"c0ff:ee::\/64", + "nexthopGroup":{ + "name":"D", + "installed":true, + "installedInternally":1 + } + }, + { + "sequenceNumber":10, + "vrfUnchanged":false, + "installed":true, + "installedReason":"Valid", + "nexthopGroup":{ + "name":"ASAKUSA10", + "installed":true, + "installedInternally":1 + }, + "matchDst":"dead:beef::\/64", + "matchMark":314159 + }, + { + "sequenceNumber":15, + "vrfUnchanged":false, + "installed":true, + "installedReason":"Valid", + "nexthopGroup":{ + "name":"ASAKUSA15", + "installed":true, + "installedInternally":1 + }, + "matchDst":"dead:beef::/64", + "matchDscp":10 + }, + { + "sequenceNumber":20, + "vrfUnchanged":false, + "installed":true, + "installedReason":"Valid", + "nexthopGroup":{ + "name":"ASAKUSA20", + "installed":true, + "installedInternally":1 + }, + "matchDst":"dead:beef::/64", + "matchEcn":1 + } + ] + }, + { + "name":"DONNA", + "valid":false, + "policies":[ + { + "sequenceNumber":5, + "vrfUnchanged":false, + "installed":false, + "installedReason":"Invalid NH-group", + "nexthopGroup":{ + "name":"B", + "installed":false, + "installedInternally":0 + }, + "matchSrc":"1.2.0.0\/16", + "matchDst":"3.4.5.0\/24" + }, + { + "sequenceNumber":10, + "vrfUnchanged":true, + "installed":false, + "installedReason":"Valid", + "matchSrc":"1.2.0.0\/16", + "matchDst":"3.4.5.0\/24" + } + ] + }, + { + "name":"EVA", + "valid":true, + "policies":[ + { + "sequenceNumber":5, + "vrfUnchanged":false, + "installed":true, + "installedReason":"Valid", + "nexthopGroup":{ + "name":"EVA5", + "installed":true, + "installedInternally":1 + }, + "matchSrc":"4.5.6.7\/32" + }, + { + "sequenceNumber":10, + "ruleNumber":309, + "vrfUnchanged":false, + "installed":true, + "installedReason":"Valid", + "nexthopGroup":{ + "name":"A", + "installed":true, + "installedInternally":1 + }, + "matchDst":"9.9.9.9\/32" + } + ] + } +] diff --git a/tests/topotests/pbr-topo1/r1/pbr-nexthop-groups.json b/tests/topotests/pbr-topo1/r1/pbr-nexthop-groups.json new file mode 100644 index 0000000000..540ea28158 --- /dev/null +++ b/tests/topotests/pbr-topo1/r1/pbr-nexthop-groups.json @@ -0,0 +1,95 @@ +[ + { + "name":"ASAKUSA10", + "valid":true, + "installed":true, + "nexthops":[ + { + "nexthop":"c0ff:ee::1", + "valid":true + } + ] + }, + { + "name":"A", + "valid":true, + "installed":true, + "nexthops":[ + { + "nexthop":"192.168.2.2", + "valid":true + }, + { + "nexthop":"192.168.3.2", + "valid":true + }, + { + "nexthop":"192.168.1.2", + "valid":true + } + ] + }, + { + "name":"D", + "valid":true, + "installed":true, + "nexthops":[ + { + "nexthop":"c0ff:ee::3", + "valid":true + }, + { + "nexthop":"c0ff:ee::2", + "valid":true + }, + { + "nexthop":"c0ff:ee::1", + "valid":true + } + ] + }, + { + "name":"C", + "valid":true, + "installed":true, + "nexthops":[ + { + "nexthop":"192.168.4.3", + "targetVrf":"vrf-chiyoda", + "valid":true + }, + { + "nexthop":"192.168.4.2", + "targetVrf":"vrf-chiyoda", + "valid":true + }, + { + "nexthop":"192.168.4.1", + "targetVrf":"vrf-chiyoda", + "valid":true + } + ] + }, + { + "name":"B", + "valid":false, + "installed":false, + "nexthops":[ + { + "nexthop":"192.168.50.1", + "valid":false + } + ] + }, + { + "name":"EVA5", + "valid":true, + "installed":true, + "nexthops":[ + { + "nexthop":"192.168.1.5", + "valid":true + } + ] + } +] diff --git a/tests/topotests/pbr-topo1/r1/pbrd.conf b/tests/topotests/pbr-topo1/r1/pbrd.conf new file mode 100644 index 0000000000..45cb7656ab --- /dev/null +++ b/tests/topotests/pbr-topo1/r1/pbrd.conf @@ -0,0 +1,100 @@ +debug pbr +debug pbr events +debug pbr nht +debug pbr zebra +# Valid table range +pbr table range 10000 50000 +# Try to set invalid bounds +pbr table range 10000 10001 +pbr table range 50000 10000 +# Reset table range +no pbr table range +! +nexthop-group A + nexthop 192.168.1.2 + nexthop 192.168.2.2 + nexthop 192.168.3.2 +! +# This one is bogus and should +# never work +nexthop-group B + nexthop 192.168.50.1 +! +nexthop-group C + nexthop 192.168.4.1 nexthop-vrf vrf-chiyoda + nexthop 192.168.4.2 nexthop-vrf vrf-chiyoda + nexthop 192.168.4.3 nexthop-vrf vrf-chiyoda +! +nexthop-group D + nexthop c0ff:ee::1 + nexthop c0ff:ee::2 + nexthop c0ff:ee::3 +! +pbr-map EVA seq 5 + match src-ip 4.5.6.7/32 + set nexthop 192.168.1.5 +! +pbr-map EVA seq 10 + match dst-ip 9.9.9.9/32 + set nexthop-group A +! +pbr-map DONNA seq 5 + match dst-ip 3.4.5.0/24 + match src-ip 1.2.0.0/16 + set nexthop-group B +! +pbr-map DONNA seq 10 + match dst-ip 3.4.5.0/24 + match src-ip 1.2.0.0/16 + set vrf unchanged +! +pbr-map AKIHABARA seq 5 + no set vrf unchanged + match dst-ip 192.168.4.0/24 + set nexthop-group C +! +pbr-map AKIHABARA seq 10 + match dst-ip 192.168.4.0/24 + no match dst-ip 192.168.4.0/24 + set nexthop-group C +! +pbr-map AKIHABARA seq 15 + set vrf noexist-vrf + match dst-ip 192.168.4.0/24 + set nexthop-group C + no set nexthop-group C +! +pbr-map ASAKUSA seq 5 + match dst-ip c0ff:ee::/64 + set nexthop-group D +! +pbr-map ASAKUSA seq 10 + match dst-ip dead:beef::/64 + match mark 314159 + set nexthop c0ff:ee::1 +! +pbr-map ASAKUSA seq 15 + match dst-ip dead:beef::/64 + match dscp af11 + set nexthop c0ff:ee::1 +! +pbr-map ASAKUSA seq 20 + match dst-ip dead:beef::/64 + match ecn 1 + set nexthop c0ff:ee::1 +! +# Interface policies +int r1-eth1 + pbr-policy EVA +! +int r1-eth2 + pbr-policy DONNA +! +int r1-eth3 + pbr-policy AKIHABARA +! +int r1-eth4 + pbr-policy ASAKUSA +! +int r1-noexist + pbr-policy NOEXIST diff --git a/tests/topotests/pbr-topo1/r1/zebra.conf b/tests/topotests/pbr-topo1/r1/zebra.conf new file mode 100644 index 0000000000..2ec947c275 --- /dev/null +++ b/tests/topotests/pbr-topo1/r1/zebra.conf @@ -0,0 +1,14 @@ +int r1-eth0 + ip address 192.168.1.1/24 + +int r1-eth1 + ip address 192.168.2.1/24 + +int r1-eth2 + ip address 192.168.3.1/24 + +int r1-eth3 vrf vrf-chiyoda + ip address 192.168.4.1/24 + +int r1-eth4 + ipv6 address c0ff:ee::/64 diff --git a/tests/topotests/pbr-topo1/test_pbr_topo1.py b/tests/topotests/pbr-topo1/test_pbr_topo1.py new file mode 100755 index 0000000000..ffac8e2889 --- /dev/null +++ b/tests/topotests/pbr-topo1/test_pbr_topo1.py @@ -0,0 +1,277 @@ +#!/usr/bin/env python + +# +# test_pbr_topo1.py +# +# Copyright (c) 2020 by +# Cumulus Networks, Inc. +# Donald Sharp +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_pbr_topo1.py: Testing PBR + +""" + +import os +import re +import sys +import pytest +import json +import platform +from functools import partial + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from lib.common_config import shutdown_bringup_interface + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + +##################################################### +## +## Network Topology Definition +## +##################################################### + + +class NetworkTopo(Topo): + "PBR Topology 1" + + def build(self, **_opts): + "Build function" + + tgen = get_topogen(self) + + # Populate routers + for routern in range(1, 2): + tgen.add_router("r{}".format(routern)) + + # Populate switches + for switchn in range(1, 6): + switch = tgen.add_switch("sw{}".format(switchn)) + switch.add_link(tgen.gears["r1"]) + + +##################################################### +## +## Tests starting +## +##################################################### + + +def setup_module(module): + "Setup topology" + tgen = Topogen(NetworkTopo, module.__name__) + tgen.start_topology() + + 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) + + router_list = tgen.routers() + for rname, router in router_list.iteritems(): + # Install vrf into the kernel and slave eth3 + router.run("ip link add vrf-chiyoda type vrf table 1000") + router.run("ip link set dev {}-eth3 master vrf-chiyoda".format(rname)) + router.run("ip link set vrf-chiyoda up") + + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_PBRD, os.path.join(CWD, "{}/pbrd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + + +def test_converge_protocols(): + "Wait for protocol convergence" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + topotest.sleep(5, "Waiting for PBR convergence") + + +def test_pbr_data(): + "Test PBR 'show ip eigrp'" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Verify PBR Status + logger.info("Verifying PBR routes") + + router_list = tgen.routers().values() + for router in router_list: + intf_file = "{}/{}/pbr-interface.json".format(CWD, router.name) + logger.info(intf_file) + + # Read expected result from file + expected = json.loads(open(intf_file).read()) + + # Actual output from router + 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) + assert result is None, assertmsg + + map_file = "{}/{}/pbr-map.json".format(CWD, router.name) + logger.info(map_file) + + # Read expected result from file + expected = json.loads(open(map_file).read()) + + # Actual output from router + 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) + assert result is None, assertmsg + + nexthop_file = "{}/{}/pbr-nexthop-groups.json".format(CWD, router.name) + logger.info(nexthop_file) + + # Read expected result from file + expected = json.loads(open(nexthop_file).read()) + + # Actual output from router + 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) + assert result is None, assertmsg + +def test_pbr_flap(): + "Test PBR interface flapping" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Verify PBR Status + logger.info("Flapping PBR Interfaces") + + router_list = tgen.routers().values() + for router in router_list: + # Flap interface to see if route-map properties are intact + # Shutdown interface + + for i in range(5): + intf = "r1-eth{}".format(i) + + # Down and back again + shutdown_bringup_interface(tgen, router.name, intf, False) + shutdown_bringup_interface(tgen, router.name, intf, True) + + intf_file = "{}/{}/pbr-interface.json".format(CWD, router.name) + logger.info(intf_file) + + # Read expected result from file + expected = json.loads(open(intf_file).read()) + + # Actual output from router + 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) + assert result is None, assertmsg + + +def test_rule_linux_installation(): + "Ensure that rule is installed in the kernel" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Checking for installed PBR rules in OS") + + router_list = tgen.routers().values() + for router in router_list: + rules_file = "{}/{}/linux-rules.json".format(CWD, router.name) + + actual = topotest.ip_rules(router) + expected = json.loads(open(rules_file).read()) + + assertmsg = "Router {} OS rules mismatch".format(router.name) + assert topotest.json_cmp(actual, expected) is None, assertmsg + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) + +# +# EXTRA SAUCE +# +def gather_pbr_data_on_error(router): + logger.info(router.vtysh_cmd("show ip route")) + logger.info(router.vtysh_cmd("show ip route vrf vrf-chiyoda")) + logger.info(router.vtysh_cmd("show ip nht")) + logger.info(router.vtysh_cmd("show pbr interface")) + logger.info(router.vtysh_cmd("show pbr map")) + logger.info(router.vtysh_cmd("show pbr nexthop-groups")) + logger.info(router.vtysh_cmd("show nexthop-group rib singleton ip")) + logger.info(router.vtysh_cmd("show nexthop-group rib singleton ipv6")) + logger.info(router.vtysh_cmd("show nexthop-group rib")) + logger.info(router.run("ip nexthop show")) + logger.info(router.run("ip route show")) + logger.info(router.run("ip route show table 1000")) + logger.info(router.run("ip route show table 10000")) + logger.info(router.run("ip -6 route show table 10000")) + logger.info(router.run("ip route show table 10001")) + logger.info(router.run("ip -6 route show table 10001")) + logger.info(router.run("ip route show table 10002")) + logger.info(router.run("ip -6 route show table 10002")) + logger.info(router.run("ip route show table 10003")) + logger.info(router.run("ip -6 route show table 10003")) + logger.info(router.run("ip route show table 10004")) + logger.info(router.run("ip -6 route show table 10004")) + logger.info(router.run("ip route show table 10005")) + logger.info(router.run("ip -6 route show table 10005")) + logger.info(router.run("ip rule show")) + diff --git a/tests/topotests/pim-basic/mcast-rx.py b/tests/topotests/pim-basic/mcast-rx.py index 9e3484e12a..8a3a44ecb1 100755 --- a/tests/topotests/pim-basic/mcast-rx.py +++ b/tests/topotests/pim-basic/mcast-rx.py @@ -36,8 +36,8 @@ def ifname_to_ifindex(ifname): output = subprocess.check_output("ip link show %s" % ifname, shell=True) - first_line = output.split('\n')[0] - re_index = re.search('^(\d+):', first_line) + first_line = output.split("\n")[0] + re_index = re.search("^(\d+):", first_line) if re_index: return int(re_index.group(1)) @@ -48,24 +48,28 @@ def ifname_to_ifindex(ifname): # Thou shalt be root if os.geteuid() != 0: - sys.stderr.write('ERROR: You must have root privileges\n') + sys.stderr.write("ERROR: You must have root privileges\n") sys.exit(1) -logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(levelname)5s: %(message)s') +logging.basicConfig( + level=logging.DEBUG, format="%(asctime)s %(levelname)5s: %(message)s" +) # Color the errors and warnings in red -logging.addLevelName(logging.ERROR, "\033[91m %s\033[0m" % logging.getLevelName(logging.ERROR)) -logging.addLevelName(logging.WARNING, "\033[91m%s\033[0m" % logging.getLevelName(logging.WARNING)) +logging.addLevelName( + logging.ERROR, "\033[91m %s\033[0m" % logging.getLevelName(logging.ERROR) +) +logging.addLevelName( + logging.WARNING, "\033[91m%s\033[0m" % logging.getLevelName(logging.WARNING) +) log = logging.getLogger(__name__) -parser = argparse.ArgumentParser(description='Multicast RX utility', - version='1.0.0') -parser.add_argument('group', help='Multicast IP') -parser.add_argument('ifname', help='Interface name') -parser.add_argument('--port', help='UDP port', default=1000) -parser.add_argument('--sleep', help='Time to sleep before we stop waiting', - default = 5) +parser = argparse.ArgumentParser(description="Multicast RX utility", version="1.0.0") +parser.add_argument("group", help="Multicast IP") +parser.add_argument("ifname", help="Interface name") +parser.add_argument("--port", help="UDP port", default=1000) +parser.add_argument("--sleep", help="Time to sleep before we stop waiting", default=5) args = parser.parse_args() # Create the datagram socket @@ -77,7 +81,9 @@ def ifname_to_ifindex(ifname): if newpid == 0: ifindex = ifname_to_ifindex(args.ifname) - mreq = struct.pack("=4sLL", socket.inet_aton(args.group), socket.INADDR_ANY, ifindex) + mreq = struct.pack( + "=4sLL", socket.inet_aton(args.group), socket.INADDR_ANY, ifindex + ) sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) time.sleep(float(args.sleep)) sock.close() diff --git a/tests/topotests/pim-basic/mcast-tx.py b/tests/topotests/pim-basic/mcast-tx.py index c469e47d4c..ad6fdc1062 100755 --- a/tests/topotests/pim-basic/mcast-tx.py +++ b/tests/topotests/pim-basic/mcast-tx.py @@ -26,20 +26,28 @@ import time -logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(levelname)5s: %(message)s') +logging.basicConfig( + level=logging.DEBUG, format="%(asctime)s %(levelname)5s: %(message)s" +) # Color the errors and warnings in red -logging.addLevelName(logging.ERROR, "\033[91m %s\033[0m" % logging.getLevelName(logging.ERROR)) -logging.addLevelName(logging.WARNING, "\033[91m%s\033[0m" % logging.getLevelName(logging.WARNING)) +logging.addLevelName( + logging.ERROR, "\033[91m %s\033[0m" % logging.getLevelName(logging.ERROR) +) +logging.addLevelName( + logging.WARNING, "\033[91m%s\033[0m" % logging.getLevelName(logging.WARNING) +) log = logging.getLogger(__name__) -parser = argparse.ArgumentParser(description='Multicast packet generator', version='1.0.0') -parser.add_argument('group', help='Multicast IP') -parser.add_argument('ifname', help='Interface name') -parser.add_argument('--port', type=int, help='UDP port number', default=1000) -parser.add_argument('--ttl', type=int, help='time-to-live', default=20) -parser.add_argument('--count', type=int, help='Packets to send', default=1) -parser.add_argument('--interval', type=int, help='ms between packets', default=100) +parser = argparse.ArgumentParser( + description="Multicast packet generator", version="1.0.0" +) +parser.add_argument("group", help="Multicast IP") +parser.add_argument("ifname", help="Interface name") +parser.add_argument("--port", type=int, help="UDP port number", default=1000) +parser.add_argument("--ttl", type=int, help="time-to-live", default=20) +parser.add_argument("--count", type=int, help="Packets to send", default=1) +parser.add_argument("--interval", type=int, help="ms between packets", default=100) args = parser.parse_args() # Create the datagram socket @@ -49,22 +57,24 @@ # https://github.com/sivel/bonding/issues/10 # # Bind our socket to ifname -sock.setsockopt(socket.SOL_SOCKET, - 25, - struct.pack("%ds" % len(args.ifname), args.ifname)) +sock.setsockopt( + socket.SOL_SOCKET, 25, struct.pack("%ds" % len(args.ifname), args.ifname) +) # We need to make sure our sendto() finishes before we close the socket sock.setblocking(1) # Set the time-to-live -sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, struct.pack('b', args.ttl)) +sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, struct.pack("b", args.ttl)) ms = args.interval / 1000.0 # Send data to the multicast group for x in xrange(args.count): - log.info('TX multicast UDP packet to %s:%d on %s' % (args.group, args.port, args.ifname)) - sent = sock.sendto('foobar %d' % x, (args.group, args.port)) + log.info( + "TX multicast UDP packet to %s:%d on %s" % (args.group, args.port, args.ifname) + ) + sent = sock.sendto("foobar %d" % x, (args.group, args.port)) if args.count > 1 and ms: time.sleep(ms) diff --git a/tests/topotests/pim-basic/r1/bgpd.conf b/tests/topotests/pim-basic/r1/bgpd.conf new file mode 100644 index 0000000000..1ca643f758 --- /dev/null +++ b/tests/topotests/pim-basic/r1/bgpd.conf @@ -0,0 +1,4 @@ +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 10.0.30.3 remote-as external + redistribute connected diff --git a/tests/topotests/pim-basic/r1/pimd.conf b/tests/topotests/pim-basic/r1/pimd.conf index 5740c66e24..f64a46deb3 100644 --- a/tests/topotests/pim-basic/r1/pimd.conf +++ b/tests/topotests/pim-basic/r1/pimd.conf @@ -2,9 +2,16 @@ hostname r1 ! interface r1-eth0 ip igmp - ip pim sm + ip pim +! +interface r1-eth1 + ip pim +! +interface r1-eth2 + ip igmp + ip pim ! interface lo - ip pim sm + ip pim ! -ip pim rp 10.254.0.1 +ip pim rp 10.254.0.3 diff --git a/tests/topotests/pim-basic/r1/rp-info.json b/tests/topotests/pim-basic/r1/rp-info.json new file mode 100644 index 0000000000..1f713c2d28 --- /dev/null +++ b/tests/topotests/pim-basic/r1/rp-info.json @@ -0,0 +1,9 @@ +{ + "10.254.0.3":[ + { + "outboundInterface":"r1-eth1", + "group":"224.0.0.0\/4", + "source":"Static" + } + ] +} diff --git a/tests/topotests/pim-basic/r1/zebra.conf b/tests/topotests/pim-basic/r1/zebra.conf index 2bf71294d0..e43041758b 100644 --- a/tests/topotests/pim-basic/r1/zebra.conf +++ b/tests/topotests/pim-basic/r1/zebra.conf @@ -3,6 +3,12 @@ hostname r1 interface r1-eth0 ip address 10.0.20.1/24 ! +interface r1-eth1 + ip address 10.0.30.1/24 +! +interface r1-eth2 + ip address 10.0.40.1/24 +! interface lo ip address 10.254.0.1/32 ! diff --git a/tests/topotests/pim-basic/r3/pimd.conf b/tests/topotests/pim-basic/r3/pimd.conf new file mode 100644 index 0000000000..f94ee99930 --- /dev/null +++ b/tests/topotests/pim-basic/r3/pimd.conf @@ -0,0 +1 @@ +hostname r3 diff --git a/tests/topotests/pim-basic/r3/zebra.conf b/tests/topotests/pim-basic/r3/zebra.conf new file mode 100644 index 0000000000..8e58e8c66a --- /dev/null +++ b/tests/topotests/pim-basic/r3/zebra.conf @@ -0,0 +1,8 @@ +hostname r3 +! +interface r3-eth0 + ip address 10.0.40.4/24 +! +interface lo + ip address 10.254.0.4/32 +! diff --git a/tests/topotests/pim-basic/rp/bgpd.conf b/tests/topotests/pim-basic/rp/bgpd.conf new file mode 100644 index 0000000000..451799288a --- /dev/null +++ b/tests/topotests/pim-basic/rp/bgpd.conf @@ -0,0 +1,4 @@ +router bgp 65003 + no bgp ebgp-requires-policy + neighbor 10.0.30.1 remote-as external + redistribute connected diff --git a/tests/topotests/pim-basic/rp/pimd.conf b/tests/topotests/pim-basic/rp/pimd.conf new file mode 100644 index 0000000000..6e35c97971 --- /dev/null +++ b/tests/topotests/pim-basic/rp/pimd.conf @@ -0,0 +1,12 @@ +hostname rp +! +interface rp-eth0 + ip pim +! +interface lo + ip pim +! +ip pim rp 10.254.0.3 +ip pim register-accept-list ACCEPT + +ip prefix-list ACCEPT seq 5 permit 10.0.20.0/24 le 32 diff --git a/tests/topotests/pim-basic/rp/upstream.json b/tests/topotests/pim-basic/rp/upstream.json new file mode 100644 index 0000000000..c33dea49e9 --- /dev/null +++ b/tests/topotests/pim-basic/rp/upstream.json @@ -0,0 +1,17 @@ +{ + "229.1.1.1":{ + "10.0.20.2":{ + "sourceStream":true, + "inboundInterface":"rp-eth0", + "rpfAddress":"10.0.20.2", + "source":"10.0.20.2", + "group":"229.1.1.1", + "state":"NotJ", + "joinState":"NotJoined", + "regState":"RegNoInfo", + "resetTimer":"--:--:--", + "refCount":1, + "sptBit":0 + } + } +} diff --git a/tests/topotests/pim-basic/rp/zebra.conf b/tests/topotests/pim-basic/rp/zebra.conf new file mode 100644 index 0000000000..0a1359ecd0 --- /dev/null +++ b/tests/topotests/pim-basic/rp/zebra.conf @@ -0,0 +1,8 @@ +hostname rp +! +interface rp-eth0 + ip address 10.0.30.3/24 +! +interface lo + ip address 10.254.0.3/32 +! diff --git a/tests/topotests/pim-basic/test_pim.py b/tests/topotests/pim-basic/test_pim.py index 6d54b8f2f0..2abee39176 100644 --- a/tests/topotests/pim-basic/test_pim.py +++ b/tests/topotests/pim-basic/test_pim.py @@ -28,9 +28,11 @@ import os import sys import pytest +import json +from functools import partial CWD = os.path.dirname(os.path.realpath(__file__)) -sys.path.append(os.path.join(CWD, '../')) +sys.path.append(os.path.join(CWD, "../")) # pylint: disable=C0413 from lib import topotest @@ -39,18 +41,44 @@ from mininet.topo import Topo + class PIMTopo(Topo): def build(self, *_args, **_opts): "Build function" tgen = get_topogen(self) - for routern in range(1, 3): - tgen.add_router('r{}'.format(routern)) + for routern in range(1, 4): + tgen.add_router("r{}".format(routern)) + + tgen.add_router("rp") + # rp ------ r1 -------- r2 + # \ + # --------- r3 + # r1 -> .1 + # r2 -> .2 + # rp -> .3 + # r3 -> .4 + # loopback network is 10.254.0.X/32 + # # r1 <- sw1 -> r2 - sw = tgen.add_switch('sw1') - sw.add_link(tgen.gears['r1']) - sw.add_link(tgen.gears['r2']) + # r1-eth0 <-> r2-eth0 + # 10.0.20.0/24 + sw = tgen.add_switch("sw1") + sw.add_link(tgen.gears["r1"]) + sw.add_link(tgen.gears["r2"]) + + # r1 <- sw2 -> rp + # r1-eth1 <-> rp-eth0 + # 10.0.30.0/24 + sw = tgen.add_switch("sw2") + sw.add_link(tgen.gears["r1"]) + sw.add_link(tgen.gears["rp"]) + + # 10.0.40.0/24 + sw = tgen.add_switch("sw3") + sw.add_link(tgen.gears["r1"]) + sw.add_link(tgen.gears["r3"]) def setup_module(mod): @@ -61,16 +89,18 @@ def setup_module(mod): # For all registered routers, load the zebra configuration file for rname, router in tgen.routers().iteritems(): router.load_config( - TopoRouter.RD_ZEBRA, - os.path.join(CWD, '{}/zebra.conf'.format(rname)) + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) ) router.load_config( - TopoRouter.RD_PIM, - os.path.join(CWD, '{}/pimd.conf'.format(rname)) + TopoRouter.RD_PIM, os.path.join(CWD, "{}/pimd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) ) # After loading the configurations, this function loads configured daemons. tgen.start_router() + # tgen.mininet_cli() def teardown_module(mod): @@ -81,6 +111,25 @@ def teardown_module(mod): tgen.stop_topology() +def test_pim_rp_setup(): + "Ensure basic routing has come up and the rp has an outgoing interface" + # Ensure rp and r1 establish pim neighbor ship and bgp has come up + # Finally ensure that the rp has an outgoing interface on r1 + tgen = get_topogen() + + r1 = tgen.gears["r1"] + json_file = "{}/{}/rp-info.json".format(CWD, r1.name) + expected = json.loads(open(json_file).read()) + + test_func = partial( + topotest.router_json_cmp, r1, "show ip pim rp-info json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=15, wait=5) + assertmsg = '"{}" JSON output mismatches'.format(r1.name) + assert result is None, assertmsg + # tgen.mininet_cli() + + def test_pim_send_mcast_stream(): "Establish a Multicast stream from r2 -> r1 and then ensure S,G is created as appropriate" logger.info("Establish a Mcast stream from r2->r1 and then ensure S,G created") @@ -90,27 +139,56 @@ def test_pim_send_mcast_stream(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - r2 = tgen.gears['r2'] - r1 = tgen.gears['r1'] + rp = tgen.gears["rp"] + r3 = tgen.gears["r3"] + r2 = tgen.gears["r2"] + r1 = tgen.gears["r1"] # Let's establish a S,G stream from r2 -> r1 CWD = os.path.dirname(os.path.realpath(__file__)) - r2.run("{}/mcast-tx.py --ttl 5 --count 5 --interval 10 229.1.1.1 r2-eth0 > /tmp/bar".format(CWD)) + r2.run( + "{}/mcast-tx.py --ttl 5 --count 5 --interval 10 229.1.1.1 r2-eth0 > /tmp/bar".format( + CWD + ) + ) + # And from r3 -> r1 + r3.run( + "{}/mcast-tx.py --ttl 5 --count 5 --interval 10 229.1.1.1 r3-eth0 > /tmp/bar".format( + CWD + ) + ) # Let's see that it shows up and we have established some basic state out = r1.vtysh_cmd("show ip pim upstream json", isjson=True) expected = { - '229.1.1.1': { - '10.0.20.2': { - 'firstHopRouter': 1, - 'joinState': 'NotJoined', - 'regState': 'RegPrune', - 'inboundInterface': 'r1-eth0', + "229.1.1.1": { + "10.0.20.2": { + "firstHopRouter": 1, + "joinState": "NotJoined", + "regState": "RegPrune", + "inboundInterface": "r1-eth0", } } } - assert topotest.json_cmp(out, expected) is None, 'failed to converge pim' + assert topotest.json_cmp(out, expected) is None, "failed to converge pim" + # tgen.mininet_cli() + + +def test_pim_rp_sees_stream(): + "Ensure that the RP sees the stream and has acted accordingly" + tgen = get_topogen() + + rp = tgen.gears["rp"] + json_file = "{}/{}/upstream.json".format(CWD, rp.name) + expected = json.loads(open(json_file).read()) + + test_func = partial( + topotest.router_json_cmp, rp, "show ip pim upstream json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=20, wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(rp.name) + assert result is None, assertmsg def test_pim_igmp_report(): @@ -122,8 +200,8 @@ def test_pim_igmp_report(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - r2 = tgen.gears['r2'] - r1 = tgen.gears['r1'] + r2 = tgen.gears["r2"] + r1 = tgen.gears["r1"] # Let's send a igmp report from r2->r1 CWD = os.path.dirname(os.path.realpath(__file__)) @@ -131,28 +209,28 @@ def test_pim_igmp_report(): out = r1.vtysh_cmd("show ip pim upstream json", isjson=True) expected = { - '229.1.1.2': { - '*': { - 'sourceIgmp': 1, - 'joinState': 'Joined', - 'regState': 'RegNoInfo', - 'sptBit': 0, + "229.1.1.2": { + "*": { + "sourceIgmp": 1, + "joinState": "Joined", + "regState": "RegNoInfo", + "sptBit": 0, } } } - assert topotest.json_cmp(out, expected) is None, 'failed to converge pim' + assert topotest.json_cmp(out, expected) is None, "failed to converge pim" def test_memory_leak(): "Run the memory leak test and report results." tgen = get_topogen() if not tgen.is_memleak_enabled(): - pytest.skip('Memory leak test/report is disabled') + pytest.skip("Memory leak test/report is disabled") tgen.report_memory_leaks() -if __name__ == '__main__': +if __name__ == "__main__": args = ["-s"] + sys.argv[1:] sys.exit(pytest.main(args)) diff --git a/tests/topotests/pytest.ini b/tests/topotests/pytest.ini index 119ab93857..77b8c2b478 100644 --- a/tests/topotests/pytest.ini +++ b/tests/topotests/pytest.ini @@ -1,6 +1,6 @@ # Skip pytests example directory [pytest] -norecursedirs = .git example-test lib docker +norecursedirs = .git example-test example-topojson-test lib docker evpn_type5_test_topo1 [topogen] # Default configuration values @@ -10,6 +10,13 @@ norecursedirs = .git example-test lib docker # value is 'info', but can be changed to 'debug' to provide more details. #verbosity = info +# Save logs to log file, by default logs will be displayed to console +#frrtest_log_dir = /tmp/topotests/ + +# Display router current configuration during test execution, +# by default configuration will not be shown +# show_router_config = True + # Default daemons binaries path. #frrdir = /usr/lib/frr #quaggadir = /usr/lib/quagga diff --git a/tests/topotests/rip-topo1/r1/rip_status.ref b/tests/topotests/rip-topo1/r1/rip_status.ref index 30c840e508..31ad46ab2e 100644 --- a/tests/topotests/rip-topo1/r1/rip_status.ref +++ b/tests/topotests/rip-topo1/r1/rip_status.ref @@ -1,6 +1,6 @@ Routing Protocol is "rip" - Sending updates every 30 seconds with +/-50%, next due in XX seconds - Timeout after 180 seconds, garbage collect after 120 seconds + Sending updates every 5 seconds with +/-50%, next due in XX seconds + Timeout after 180 seconds, garbage collect after 5 seconds Outgoing update filter list for all interface is not set Incoming update filter list for all interface is not set Default redistribution metric is 1 @@ -8,8 +8,14 @@ Routing Protocol is "rip" Default version control: send version 2, receive version 2 Interface Send Recv Key-chain r1-eth1 2 2 + r1-eth2 2 2 + r1-eth3 2 2 Routing for Networks: 193.1.1.0/26 + r1-eth2 + r1-eth3 + Passive Interface(s): + r1-eth3 Routing Information Sources: Gateway BadPackets BadRoutes Distance Last Update 193.1.1.2 0 0 120 XX:XX:XX diff --git a/tests/topotests/rip-topo1/r1/ripd.conf b/tests/topotests/rip-topo1/r1/ripd.conf index 70e70d3590..54f1774214 100644 --- a/tests/topotests/rip-topo1/r1/ripd.conf +++ b/tests/topotests/rip-topo1/r1/ripd.conf @@ -1,8 +1,12 @@ log file ripd.log ! router rip + timers basic 5 180 5 version 2 network 193.1.1.0/26 + network r1-eth2 + network r1-eth3 + passive-interface r1-eth3 ! line vty ! diff --git a/tests/topotests/rip-topo1/r1/show_ip_rip.ref b/tests/topotests/rip-topo1/r1/show_ip_rip.ref index 561560f230..a0b77c886e 100644 --- a/tests/topotests/rip-topo1/r1/show_ip_rip.ref +++ b/tests/topotests/rip-topo1/r1/show_ip_rip.ref @@ -6,5 +6,7 @@ Sub-codes: Network Next Hop Metric From Tag Time R(n) 192.168.2.0/24 193.1.1.2 3 193.1.1.2 0 XX:XX R(n) 192.168.3.0/24 193.1.1.2 3 193.1.1.2 0 XX:XX +C(i) 192.168.98.0/24 0.0.0.0 1 self 0 +C(i) 192.168.99.0/24 0.0.0.0 1 self 0 C(i) 193.1.1.0/26 0.0.0.0 1 self 0 R(n) 193.1.2.0/24 193.1.1.2 2 193.1.1.2 0 XX:XX diff --git a/tests/topotests/rip-topo1/r1/show_ip_route.ref b/tests/topotests/rip-topo1/r1/show_ip_route.ref index 62d71f0ab6..2ff26045aa 100644 --- a/tests/topotests/rip-topo1/r1/show_ip_route.ref +++ b/tests/topotests/rip-topo1/r1/show_ip_route.ref @@ -1,3 +1,3 @@ -R>* 192.168.2.0/24 [120/3] via 193.1.1.2, r1-eth1 -R>* 192.168.3.0/24 [120/3] via 193.1.1.2, r1-eth1 -R>* 193.1.2.0/24 [120/2] via 193.1.1.2, r1-eth1 +R>* 192.168.2.0/24 [120/3] via 193.1.1.2, r1-eth1, weight 1 +R>* 192.168.3.0/24 [120/3] via 193.1.1.2, r1-eth1, weight 1 +R>* 193.1.2.0/24 [120/2] via 193.1.1.2, r1-eth1, weight 1 diff --git a/tests/topotests/rip-topo1/r1/zebra.conf b/tests/topotests/rip-topo1/r1/zebra.conf index 8537f6dd80..7c8f2c502b 100644 --- a/tests/topotests/rip-topo1/r1/zebra.conf +++ b/tests/topotests/rip-topo1/r1/zebra.conf @@ -5,6 +5,13 @@ hostname r1 interface r1-eth0 ip address 192.168.1.1/24 ! +interface r1-eth2 + ip address 192.168.99.1/24 +! +interface r1-eth3 + ip address 192.168.98.1/24 +! + interface r1-eth1 description to sw2 - RIPv2 interface ip address 193.1.1.1/26 diff --git a/tests/topotests/rip-topo1/r2/rip_status.ref b/tests/topotests/rip-topo1/r2/rip_status.ref index b539d321d5..99841a62b0 100644 --- a/tests/topotests/rip-topo1/r2/rip_status.ref +++ b/tests/topotests/rip-topo1/r2/rip_status.ref @@ -1,6 +1,6 @@ Routing Protocol is "rip" - Sending updates every 30 seconds with +/-50%, next due in XX seconds - Timeout after 180 seconds, garbage collect after 120 seconds + Sending updates every 5 seconds with +/-50%, next due in XX seconds + Timeout after 180 seconds, garbage collect after 5 seconds Outgoing update filter list for all interface is not set Incoming update filter list for all interface is not set Default redistribution metric is 1 @@ -14,5 +14,6 @@ Routing Protocol is "rip" 193.1.2.0/24 Routing Information Sources: Gateway BadPackets BadRoutes Distance Last Update + 193.1.1.1 0 0 120 XX:XX:XX 193.1.2.2 0 0 120 XX:XX:XX Distance: (default is 120) diff --git a/tests/topotests/rip-topo1/r2/ripd.conf b/tests/topotests/rip-topo1/r2/ripd.conf index 179a1ebd0f..2e94cfa262 100644 --- a/tests/topotests/rip-topo1/r2/ripd.conf +++ b/tests/topotests/rip-topo1/r2/ripd.conf @@ -3,6 +3,7 @@ log file ripd.log ! router rip version 2 + timers basic 5 180 5 network 193.1.1.0/26 network 193.1.2.0/24 ! diff --git a/tests/topotests/rip-topo1/r2/show_ip_rip.ref b/tests/topotests/rip-topo1/r2/show_ip_rip.ref index 58ab052160..b61fb45eac 100644 --- a/tests/topotests/rip-topo1/r2/show_ip_rip.ref +++ b/tests/topotests/rip-topo1/r2/show_ip_rip.ref @@ -6,5 +6,7 @@ Sub-codes: Network Next Hop Metric From Tag Time R(n) 192.168.2.0/24 193.1.2.2 2 193.1.2.2 0 XX:XX R(n) 192.168.3.0/24 193.1.2.2 2 193.1.2.2 0 XX:XX +R(n) 192.168.98.0/24 193.1.1.1 2 193.1.1.1 0 XX:XX +R(n) 192.168.99.0/24 193.1.1.1 2 193.1.1.1 0 XX:XX C(i) 193.1.1.0/26 0.0.0.0 1 self 0 C(i) 193.1.2.0/24 0.0.0.0 1 self 0 diff --git a/tests/topotests/rip-topo1/r2/show_ip_route.ref b/tests/topotests/rip-topo1/r2/show_ip_route.ref index 4b34939aa5..80f51a92c7 100644 --- a/tests/topotests/rip-topo1/r2/show_ip_route.ref +++ b/tests/topotests/rip-topo1/r2/show_ip_route.ref @@ -1,2 +1,4 @@ -R>* 192.168.2.0/24 [120/2] via 193.1.2.2, r2-eth1 -R>* 192.168.3.0/24 [120/2] via 193.1.2.2, r2-eth1 +R>* 192.168.2.0/24 [120/2] via 193.1.2.2, r2-eth1, weight 1 +R>* 192.168.3.0/24 [120/2] via 193.1.2.2, r2-eth1, weight 1 +R>* 192.168.98.0/24 [120/2] via 193.1.1.1, r2-eth0, weight 1 +R>* 192.168.99.0/24 [120/2] via 193.1.1.1, r2-eth0, weight 1 diff --git a/tests/topotests/rip-topo1/r3/rip_status.ref b/tests/topotests/rip-topo1/r3/rip_status.ref index 0e3a4be944..040d3c32a1 100644 --- a/tests/topotests/rip-topo1/r3/rip_status.ref +++ b/tests/topotests/rip-topo1/r3/rip_status.ref @@ -1,6 +1,6 @@ Routing Protocol is "rip" - Sending updates every 30 seconds with +/-50%, next due in XX seconds - Timeout after 180 seconds, garbage collect after 120 seconds + Sending updates every 5 seconds with +/-50%, next due in XX seconds + Timeout after 180 seconds, garbage collect after 5 seconds Outgoing update filter list for all interface is not set Incoming update filter list for all interface is not set Default redistribution metric is 1 diff --git a/tests/topotests/rip-topo1/r3/ripd.conf b/tests/topotests/rip-topo1/r3/ripd.conf index 363b91b33a..e27e67503f 100644 --- a/tests/topotests/rip-topo1/r3/ripd.conf +++ b/tests/topotests/rip-topo1/r3/ripd.conf @@ -3,6 +3,7 @@ log file ripd.log ! router rip version 2 + timers basic 5 180 5 redistribute connected redistribute static network 193.1.2.0/24 diff --git a/tests/topotests/rip-topo1/r3/show_ip_rip.ref b/tests/topotests/rip-topo1/r3/show_ip_rip.ref index cf672712a8..1df299b5e6 100644 --- a/tests/topotests/rip-topo1/r3/show_ip_rip.ref +++ b/tests/topotests/rip-topo1/r3/show_ip_rip.ref @@ -6,5 +6,7 @@ Sub-codes: Network Next Hop Metric From Tag Time S(r) 192.168.2.0/24 192.168.3.10 1 self 0 C(r) 192.168.3.0/24 0.0.0.0 1 self 0 +R(n) 192.168.98.0/24 193.1.2.1 3 193.1.2.1 0 XX:XX +R(n) 192.168.99.0/24 193.1.2.1 3 193.1.2.1 0 XX:XX R(n) 193.1.1.0/26 193.1.2.1 2 193.1.2.1 0 XX:XX C(i) 193.1.2.0/24 0.0.0.0 1 self 0 diff --git a/tests/topotests/rip-topo1/r3/show_ip_route.ref b/tests/topotests/rip-topo1/r3/show_ip_route.ref index 835e1229c8..2b739f0489 100644 --- a/tests/topotests/rip-topo1/r3/show_ip_route.ref +++ b/tests/topotests/rip-topo1/r3/show_ip_route.ref @@ -1 +1,3 @@ -R>* 193.1.1.0/26 [120/2] via 193.1.2.1, r3-eth1 +R>* 192.168.98.0/24 [120/3] via 193.1.2.1, r3-eth1, weight 1 +R>* 192.168.99.0/24 [120/3] via 193.1.2.1, r3-eth1, weight 1 +R>* 193.1.1.0/26 [120/2] via 193.1.2.1, r3-eth1, weight 1 diff --git a/tests/topotests/rip-topo1/test_rip_topo1.py b/tests/topotests/rip-topo1/test_rip_topo1.py index 7aaaacacfb..3098812a24 100755 --- a/tests/topotests/rip-topo1/test_rip_topo1.py +++ b/tests/topotests/rip-topo1/test_rip_topo1.py @@ -54,6 +54,7 @@ ## ##################################################### + class NetworkTopo(Topo): "RIP Topology 1" @@ -63,33 +64,38 @@ def build(self, **_opts): router = {} # # Setup Main Router - router[1] = topotest.addRouter(self, 'r1') + router[1] = topotest.addRouter(self, "r1") # # Setup RIP Routers for i in range(2, 4): - router[i] = topotest.addRouter(self, 'r%s' % i) + router[i] = topotest.addRouter(self, "r%s" % i) # # Setup Switches switch = {} # # On main router # First switch is for a dummy interface (for local network) - switch[1] = self.addSwitch('sw1', cls=topotest.LegacySwitch) - self.addLink(switch[1], router[1], intfName2='r1-eth0') + switch[1] = self.addSwitch("sw1", cls=topotest.LegacySwitch) + self.addLink(switch[1], router[1], intfName2="r1-eth0") # # Switches for RIP # switch 2 switch is for connection to RIP router - switch[2] = self.addSwitch('sw2', cls=topotest.LegacySwitch) - self.addLink(switch[2], router[1], intfName2='r1-eth1') - self.addLink(switch[2], router[2], intfName2='r2-eth0') + switch[2] = self.addSwitch("sw2", cls=topotest.LegacySwitch) + self.addLink(switch[2], router[1], intfName2="r1-eth1") + self.addLink(switch[2], router[2], intfName2="r2-eth0") # switch 3 is between RIP routers - switch[3] = self.addSwitch('sw3', cls=topotest.LegacySwitch) - self.addLink(switch[3], router[2], intfName2='r2-eth1') - self.addLink(switch[3], router[3], intfName2='r3-eth1') + switch[3] = self.addSwitch("sw3", cls=topotest.LegacySwitch) + self.addLink(switch[3], router[2], intfName2="r2-eth1") + self.addLink(switch[3], router[3], intfName2="r3-eth1") # switch 4 is stub on remote RIP router - switch[4] = self.addSwitch('sw4', cls=topotest.LegacySwitch) - self.addLink(switch[4], router[3], intfName2='r3-eth0') + 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[6] = self.addSwitch("sw6", cls=topotest.LegacySwitch) + self.addLink(switch[6], router[1], intfName2="r1-eth3") ##################################################### @@ -98,6 +104,7 @@ def build(self, **_opts): ## ##################################################### + def setup_module(module): global topo, net @@ -105,7 +112,7 @@ def setup_module(module): print("******************************************\n") print("Cleanup old Mininet runs") - os.system('sudo mn -c > /dev/null 2>&1') + os.system("sudo mn -c > /dev/null 2>&1") thisDir = os.path.dirname(os.path.realpath(__file__)) topo = NetworkTopo() @@ -116,9 +123,9 @@ def setup_module(module): # Starting Routers # for i in range(1, 4): - net['r%s' % i].loadConf('zebra', '%s/r%s/zebra.conf' % (thisDir, i)) - net['r%s' % i].loadConf('ripd', '%s/r%s/ripd.conf' % (thisDir, i)) - net['r%s' % i].startRouter() + net["r%s" % i].loadConf("zebra", "%s/r%s/zebra.conf" % (thisDir, i)) + net["r%s" % i].loadConf("ripd", "%s/r%s/ripd.conf" % (thisDir, i)) + net["r%s" % i].startRouter() # For debugging after starting Quagga/FRR daemons, uncomment the next line # CLI(net) @@ -139,16 +146,15 @@ def test_router_running(): global net # Skip if previous fatal error condition is raised - if (fatal_error != ""): + if fatal_error != "": pytest.skip(fatal_error) print("\n\n** Check if FRR/Quagga is running on each Router node") print("******************************************\n") - sleep(5) # Make sure that all daemons are running for i in range(1, 4): - fatal_error = net['r%s' % i].checkRouterRunning() + fatal_error = net["r%s" % i].checkRouterRunning() assert fatal_error == "", fatal_error # For debugging after starting FRR/Quagga daemons, uncomment the next line @@ -160,7 +166,7 @@ def test_converge_protocols(): global net # Skip if previous fatal error condition is raised - if (fatal_error != ""): + if fatal_error != "": pytest.skip(fatal_error) thisDir = os.path.dirname(os.path.realpath(__file__)) @@ -168,12 +174,12 @@ def test_converge_protocols(): print("\n\n** Waiting for protocols convergence") print("******************************************\n") - # Not really implemented yet - just sleep 60 secs for now - sleep(60) + # Not really implemented yet - just sleep 11 secs for now + sleep(11) # Make sure that all daemons are still running for i in range(1, 4): - fatal_error = net['r%s' % i].checkRouterRunning() + fatal_error = net["r%s" % i].checkRouterRunning() assert fatal_error == "", fatal_error # For debugging after starting FRR/Quagga daemons, uncomment the next line @@ -185,7 +191,7 @@ def test_rip_status(): global net # Skip if previous fatal error condition is raised - if (fatal_error != ""): + if fatal_error != "": pytest.skip(fatal_error) thisDir = os.path.dirname(os.path.realpath(__file__)) @@ -195,30 +201,37 @@ def test_rip_status(): print("******************************************\n") failures = 0 for i in range(1, 4): - refTableFile = '%s/r%s/rip_status.ref' % (thisDir, i) + refTableFile = "%s/r%s/rip_status.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) + expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1) # Actual output from router - actual = net['r%s' % i].cmd('vtysh -c "show ip rip status" 2> /dev/null').rstrip() - # Drop time in next due + actual = ( + net["r%s" % i] + .cmd('vtysh -c "show ip rip status" 2> /dev/null') + .rstrip() + ) + # Drop time in next due actual = re.sub(r"in [0-9]+ seconds", "in XX seconds", actual) # Drop time in last update actual = re.sub(r" [0-2][0-9]:[0-5][0-9]:[0-5][0-9]", " XX:XX:XX", actual) # Fix newlines (make them all the same) - actual = ('\n'.join(actual.splitlines()) + '\n').splitlines(1) + actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1) # Generate Diff - diff = topotest.get_textdiff(actual, expected, + diff = topotest.get_textdiff( + actual, + expected, title1="actual IP RIP status", - title2="expected IP RIP status") + title2="expected IP RIP status", + ) # Empty string if it matches, otherwise diff contains unified diff if diff: - sys.stderr.write('r%s failed IP RIP status check:\n%s\n' % (i, diff)) + sys.stderr.write("r%s failed IP RIP status check:\n%s\n" % (i, diff)) failures += 1 else: print("r%s ok" % i) @@ -227,7 +240,7 @@ def test_rip_status(): # Make sure that all daemons are still running for i in range(1, 4): - fatal_error = net['r%s' % i].checkRouterRunning() + fatal_error = net["r%s" % i].checkRouterRunning() assert fatal_error == "", fatal_error # For debugging after starting FRR/Quagga daemons, uncomment the next line @@ -239,7 +252,7 @@ def test_rip_routes(): global net # Skip if previous fatal error condition is raised - if (fatal_error != ""): + if fatal_error != "": pytest.skip(fatal_error) thisDir = os.path.dirname(os.path.realpath(__file__)) @@ -249,28 +262,31 @@ def test_rip_routes(): print("******************************************\n") failures = 0 for i in range(1, 4): - refTableFile = '%s/r%s/show_ip_rip.ref' % (thisDir, i) + refTableFile = "%s/r%s/show_ip_rip.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) + expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1) # Actual output from router - actual = net['r%s' % i].cmd('vtysh -c "show ip rip" 2> /dev/null').rstrip() + actual = net["r%s" % i].cmd('vtysh -c "show ip rip" 2> /dev/null').rstrip() # Drop Time actual = re.sub(r"[0-9][0-9]:[0-5][0-9]", "XX:XX", actual) # Fix newlines (make them all the same) - actual = ('\n'.join(actual.splitlines()) + '\n').splitlines(1) + actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1) # Generate Diff - diff = topotest.get_textdiff(actual, expected, + diff = topotest.get_textdiff( + actual, + expected, title1="actual SHOW IP RIP", - title2="expected SHOW IP RIP") + title2="expected SHOW IP RIP", + ) # Empty string if it matches, otherwise diff contains unified diff if diff: - sys.stderr.write('r%s failed SHOW IP RIP check:\n%s\n' % (i, diff)) + sys.stderr.write("r%s failed SHOW IP RIP check:\n%s\n" % (i, diff)) failures += 1 else: print("r%s ok" % i) @@ -279,7 +295,7 @@ def test_rip_routes(): # Make sure that all daemons are still running for i in range(1, 4): - fatal_error = net['r%s' % i].checkRouterRunning() + fatal_error = net["r%s" % i].checkRouterRunning() assert fatal_error == "", fatal_error # For debugging after starting FRR/Quagga daemons, uncomment the next line @@ -291,7 +307,7 @@ def test_zebra_ipv4_routingTable(): global net # Skip if previous fatal error condition is raised - if (fatal_error != ""): + if fatal_error != "": pytest.skip(fatal_error) thisDir = os.path.dirname(os.path.realpath(__file__)) @@ -301,37 +317,49 @@ def test_zebra_ipv4_routingTable(): print("******************************************\n") failures = 0 for i in range(1, 4): - refTableFile = '%s/r%s/show_ip_route.ref' % (thisDir, i) + refTableFile = "%s/r%s/show_ip_route.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) + expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1) # Actual output from router - actual = net['r%s' % i].cmd('vtysh -c "show ip route" 2> /dev/null | grep "^R"').rstrip() + actual = ( + net["r%s" % i] + .cmd('vtysh -c "show ip route" 2> /dev/null | grep "^R"') + .rstrip() + ) # Drop timers on end of line (older Quagga Versions) actual = re.sub(r", [0-2][0-9]:[0-5][0-9]:[0-5][0-9]", "", actual) # Fix newlines (make them all the same) - actual = ('\n'.join(actual.splitlines()) + '\n').splitlines(1) + actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1) # Generate Diff - diff = topotest.get_textdiff(actual, expected, + diff = topotest.get_textdiff( + actual, + expected, title1="actual Zebra IPv4 routing table", - title2="expected Zebra IPv4 routing table") + title2="expected Zebra IPv4 routing table", + ) # Empty string if it matches, otherwise diff contains unified diff if diff: - sys.stderr.write('r%s failed Zebra IPv4 Routing Table Check:\n%s\n' % (i, diff)) + sys.stderr.write( + "r%s failed Zebra IPv4 Routing Table Check:\n%s\n" % (i, diff) + ) failures += 1 else: print("r%s ok" % i) - assert failures == 0, "Zebra IPv4 Routing Table verification failed for router r%s:\n%s" % (i, diff) + assert failures == 0, ( + "Zebra IPv4 Routing Table verification failed for router r%s:\n%s" + % (i, diff) + ) # Make sure that all daemons are still running for i in range(1, 4): - fatal_error = net['r%s' % i].checkRouterRunning() + fatal_error = net["r%s" % i].checkRouterRunning() assert fatal_error == "", fatal_error # For debugging after starting FRR/Quagga daemons, uncomment the next line @@ -343,30 +371,30 @@ def test_shutdown_check_stderr(): global net # Skip if previous fatal error condition is raised - if (fatal_error != ""): + if fatal_error != "": pytest.skip(fatal_error) - if os.environ.get('TOPOTESTS_CHECK_STDERR') is None: - pytest.skip('Skipping test for Stderr output and memory leaks') + if os.environ.get("TOPOTESTS_CHECK_STDERR") is None: + pytest.skip("Skipping test for Stderr output and memory leaks") thisDir = os.path.dirname(os.path.realpath(__file__)) print("\n\n** Verifing unexpected STDERR output from daemons") print("******************************************\n") - net['r1'].stopRouter() + net["r1"].stopRouter() - log = net['r1'].getStdErr('ripd') + log = net["r1"].getStdErr("ripd") if log: print("\nRIPd StdErr Log:\n" + log) - log = net['r1'].getStdErr('zebra') + log = net["r1"].getStdErr("zebra") if log: print("\nZebra StdErr Log:\n" + log) -if __name__ == '__main__': +if __name__ == "__main__": - setLogLevel('info') + setLogLevel("info") # To suppress tracebacks, either use the following pytest call or add "--tb=no" to cli # retval = pytest.main(["-s", "--tb=no"]) retval = pytest.main(["-s"]) diff --git a/tests/topotests/ripng-topo1/r1/ripng_status.ref b/tests/topotests/ripng-topo1/r1/ripng_status.ref index 48816c1a9b..b02cc69d0e 100644 --- a/tests/topotests/ripng-topo1/r1/ripng_status.ref +++ b/tests/topotests/ripng-topo1/r1/ripng_status.ref @@ -1,6 +1,6 @@ Routing Protocol is "RIPng" - Sending updates every 30 seconds with +/-50%, next due in XX seconds - Timeout after 180 seconds, garbage collect after 120 seconds + Sending updates every 5 seconds with +/-50%, next due in XX seconds + Timeout after 180 seconds, garbage collect after 5 seconds Outgoing update filter list for all interface is not set Incoming update filter list for all interface is not set Default redistribution metric is 1 @@ -8,8 +8,12 @@ Routing Protocol is "RIPng" Default version control: send version 1, receive version 1 Interface Send Recv r1-eth1 1 1 + r1-eth2 1 1 + r1-eth3 1 1 Routing for Networks: fc00:5::/64 + r1-eth2 + r1-eth3 Routing Information Sources: Gateway BadPackets BadRoutes Distance Last Update fe80::XXXX:XXXX:XXXX:XXXX diff --git a/tests/topotests/ripng-topo1/r1/ripngd.conf b/tests/topotests/ripng-topo1/r1/ripngd.conf index 5eb78eafe2..07ed7296d9 100644 --- a/tests/topotests/ripng-topo1/r1/ripngd.conf +++ b/tests/topotests/ripng-topo1/r1/ripngd.conf @@ -5,7 +5,11 @@ debug ripng packet debug ripng zebra ! router ripng + timers basic 5 180 5 network fc00:5::/64 + network r1-eth2 + network r1-eth3 + passive-interface r1-eth3 ! line vty ! diff --git a/tests/topotests/ripng-topo1/r1/show_ipv6_ripng.ref b/tests/topotests/ripng-topo1/r1/show_ipv6_ripng.ref index 18d026a8fd..30d0f31e18 100644 --- a/tests/topotests/ripng-topo1/r1/show_ipv6_ripng.ref +++ b/tests/topotests/ripng-topo1/r1/show_ipv6_ripng.ref @@ -12,3 +12,7 @@ R(n) fc00:7::/64 fe80::XXXX:XXXX:XXXX:XXXX r1-eth1 3 0 XX:XX R(n) fc00:7:1111::/64 fe80::XXXX:XXXX:XXXX:XXXX r1-eth1 3 0 XX:XX +C(i) fc00:98:0:1::/64 + :: self 1 0 +C(i) fc00:99:0:1::/64 + :: self 1 0 diff --git a/tests/topotests/ripng-topo1/r1/show_ipv6_route.ref b/tests/topotests/ripng-topo1/r1/show_ipv6_route.ref index 7e5fc3f0f5..55fbbc34f3 100644 --- a/tests/topotests/ripng-topo1/r1/show_ipv6_route.ref +++ b/tests/topotests/ripng-topo1/r1/show_ipv6_route.ref @@ -1,3 +1,3 @@ -R>* fc00:6::/62 [120/2] via fe80::XXXX:XXXX:XXXX:XXXX, r1-eth1 -R>* fc00:7::/64 [120/3] via fe80::XXXX:XXXX:XXXX:XXXX, r1-eth1 -R>* fc00:7:1111::/64 [120/3] via fe80::XXXX:XXXX:XXXX:XXXX, r1-eth1 +R>* fc00:6::/62 [120/2] via fe80::XXXX:XXXX:XXXX:XXXX, r1-eth1, weight 1 +R>* fc00:7::/64 [120/3] via fe80::XXXX:XXXX:XXXX:XXXX, r1-eth1, weight 1 +R>* fc00:7:1111::/64 [120/3] via fe80::XXXX:XXXX:XXXX:XXXX, r1-eth1, weight 1 diff --git a/tests/topotests/ripng-topo1/r1/zebra.conf b/tests/topotests/ripng-topo1/r1/zebra.conf index 1a10343044..11c1cdc5b9 100644 --- a/tests/topotests/ripng-topo1/r1/zebra.conf +++ b/tests/topotests/ripng-topo1/r1/zebra.conf @@ -10,6 +10,12 @@ interface r1-eth1 ipv6 address fc00:5::1/64 no link-detect ! +interface r1-eth2 + ipv6 address fc00:99:0:1::1/64 +! +interface r1-eth3 + ipv6 address fc00:98:0:1::1/64 +! ip forwarding ipv6 forwarding ! diff --git a/tests/topotests/ripng-topo1/r2/ripng_status.ref b/tests/topotests/ripng-topo1/r2/ripng_status.ref index fddcf63e5b..640df9a4a0 100644 --- a/tests/topotests/ripng-topo1/r2/ripng_status.ref +++ b/tests/topotests/ripng-topo1/r2/ripng_status.ref @@ -1,6 +1,6 @@ Routing Protocol is "RIPng" - Sending updates every 30 seconds with +/-50%, next due in XX seconds - Timeout after 180 seconds, garbage collect after 120 seconds + Sending updates every 5 seconds with +/-50%, next due in XX seconds + Timeout after 180 seconds, garbage collect after 5 seconds Outgoing update filter list for all interface is not set Incoming update filter list for all interface is not set Default redistribution metric is 1 diff --git a/tests/topotests/ripng-topo1/r2/ripngd.conf b/tests/topotests/ripng-topo1/r2/ripngd.conf index a25a3cd490..ef2c42195d 100644 --- a/tests/topotests/ripng-topo1/r2/ripngd.conf +++ b/tests/topotests/ripng-topo1/r2/ripngd.conf @@ -5,6 +5,7 @@ debug ripng packet debug ripng zebra ! router ripng + timers basic 5 180 5 network fc00:5::/64 network fc00:6::/62 ! diff --git a/tests/topotests/ripng-topo1/r2/show_ipv6_ripng.ref b/tests/topotests/ripng-topo1/r2/show_ipv6_ripng.ref index 765efd07a2..fe5bcc8b31 100644 --- a/tests/topotests/ripng-topo1/r2/show_ipv6_ripng.ref +++ b/tests/topotests/ripng-topo1/r2/show_ipv6_ripng.ref @@ -12,3 +12,7 @@ R(n) fc00:7::/64 fe80::XXXX:XXXX:XXXX:XXXX r2-eth1 2 0 XX:XX R(n) fc00:7:1111::/64 fe80::XXXX:XXXX:XXXX:XXXX r2-eth1 2 0 XX:XX +R(n) fc00:98:0:1::/64 + fe80::XXXX:XXXX:XXXX:XXXX r2-eth0 2 0 XX:XX +R(n) fc00:99:0:1::/64 + fe80::XXXX:XXXX:XXXX:XXXX r2-eth0 2 0 XX:XX diff --git a/tests/topotests/ripng-topo1/r2/show_ipv6_route.ref b/tests/topotests/ripng-topo1/r2/show_ipv6_route.ref index 688e77e7ed..72e1f926a2 100644 --- a/tests/topotests/ripng-topo1/r2/show_ipv6_route.ref +++ b/tests/topotests/ripng-topo1/r2/show_ipv6_route.ref @@ -1,2 +1,4 @@ -R>* fc00:7::/64 [120/2] via fe80::XXXX:XXXX:XXXX:XXXX, r2-eth1 -R>* fc00:7:1111::/64 [120/2] via fe80::XXXX:XXXX:XXXX:XXXX, r2-eth1 +R>* fc00:7::/64 [120/2] via fe80::XXXX:XXXX:XXXX:XXXX, r2-eth1, weight 1 +R>* fc00:7:1111::/64 [120/2] via fe80::XXXX:XXXX:XXXX:XXXX, r2-eth1, weight 1 +R>* fc00:98:0:1::/64 [120/2] via fe80::XXXX:XXXX:XXXX:XXXX, r2-eth0, weight 1 +R>* fc00:99:0:1::/64 [120/2] via fe80::XXXX:XXXX:XXXX:XXXX, r2-eth0, weight 1 diff --git a/tests/topotests/ripng-topo1/r3/ripng_status.ref b/tests/topotests/ripng-topo1/r3/ripng_status.ref index 1a8dabbf5f..f4bfff0c59 100644 --- a/tests/topotests/ripng-topo1/r3/ripng_status.ref +++ b/tests/topotests/ripng-topo1/r3/ripng_status.ref @@ -1,6 +1,6 @@ Routing Protocol is "RIPng" - Sending updates every 30 seconds with +/-50%, next due in XX seconds - Timeout after 180 seconds, garbage collect after 120 seconds + Sending updates every 5 seconds with +/-50%, next due in XX seconds + Timeout after 180 seconds, garbage collect after 5 seconds Outgoing update filter list for all interface is not set Incoming update filter list for all interface is not set Default redistribution metric is 1 diff --git a/tests/topotests/ripng-topo1/r3/ripngd.conf b/tests/topotests/ripng-topo1/r3/ripngd.conf index dfa5700adb..506eaac442 100644 --- a/tests/topotests/ripng-topo1/r3/ripngd.conf +++ b/tests/topotests/ripng-topo1/r3/ripngd.conf @@ -5,6 +5,7 @@ debug ripng packet debug ripng zebra ! router ripng + timers basic 5 180 5 network fc00:6::/62 redistribute connected redistribute static diff --git a/tests/topotests/ripng-topo1/r3/show_ipv6_ripng.ref b/tests/topotests/ripng-topo1/r3/show_ipv6_ripng.ref index 81e76b97a6..909ad663ba 100644 --- a/tests/topotests/ripng-topo1/r3/show_ipv6_ripng.ref +++ b/tests/topotests/ripng-topo1/r3/show_ipv6_ripng.ref @@ -12,3 +12,7 @@ C(r) fc00:7::/64 :: self 1 0 S(r) fc00:7:1111::/64 :: self 1 0 +R(n) fc00:98:0:1::/64 + fe80::XXXX:XXXX:XXXX:XXXX r3-eth1 3 0 XX:XX +R(n) fc00:99:0:1::/64 + fe80::XXXX:XXXX:XXXX:XXXX r3-eth1 3 0 XX:XX diff --git a/tests/topotests/ripng-topo1/r3/show_ipv6_route.ref b/tests/topotests/ripng-topo1/r3/show_ipv6_route.ref index 8e46e39921..25a7440111 100644 --- a/tests/topotests/ripng-topo1/r3/show_ipv6_route.ref +++ b/tests/topotests/ripng-topo1/r3/show_ipv6_route.ref @@ -1 +1,3 @@ -R>* fc00:5::/64 [120/2] via fe80::XXXX:XXXX:XXXX:XXXX, r3-eth1 +R>* fc00:5::/64 [120/2] via fe80::XXXX:XXXX:XXXX:XXXX, r3-eth1, weight 1 +R>* fc00:98:0:1::/64 [120/3] via fe80::XXXX:XXXX:XXXX:XXXX, r3-eth1, weight 1 +R>* fc00:99:0:1::/64 [120/3] via fe80::XXXX:XXXX:XXXX:XXXX, r3-eth1, weight 1 diff --git a/tests/topotests/ripng-topo1/test_ripng_topo1.py b/tests/topotests/ripng-topo1/test_ripng_topo1.py index 145b1a7efe..23e689235c 100755 --- a/tests/topotests/ripng-topo1/test_ripng_topo1.py +++ b/tests/topotests/ripng-topo1/test_ripng_topo1.py @@ -55,6 +55,7 @@ ## ##################################################### + class NetworkTopo(Topo): "RIPng Topology 1" @@ -64,33 +65,37 @@ def build(self, **_opts): router = {} # # Setup Main Router - router[1] = topotest.addRouter(self, 'r1') + router[1] = topotest.addRouter(self, "r1") # # Setup RIPng Routers for i in range(2, 4): - router[i] = topotest.addRouter(self, 'r%s' % i) + router[i] = topotest.addRouter(self, "r%s" % i) # Setup Switches switch = {} # # On main router # First switch is for a dummy interface (for local network) - switch[1] = self.addSwitch('sw1', cls=topotest.LegacySwitch) - self.addLink(switch[1], router[1], intfName2='r1-eth0') + switch[1] = self.addSwitch("sw1", cls=topotest.LegacySwitch) + self.addLink(switch[1], router[1], intfName2="r1-eth0") # # Switches for RIPng # switch 2 switch is for connection to RIP router - switch[2] = self.addSwitch('sw2', cls=topotest.LegacySwitch) - self.addLink(switch[2], router[1], intfName2='r1-eth1') - self.addLink(switch[2], router[2], intfName2='r2-eth0') + switch[2] = self.addSwitch("sw2", cls=topotest.LegacySwitch) + self.addLink(switch[2], router[1], intfName2="r1-eth1") + self.addLink(switch[2], router[2], intfName2="r2-eth0") # switch 3 is between RIP routers - switch[3] = self.addSwitch('sw3', cls=topotest.LegacySwitch) - self.addLink(switch[3], router[2], intfName2='r2-eth1') - self.addLink(switch[3], router[3], intfName2='r3-eth1') + switch[3] = self.addSwitch("sw3", cls=topotest.LegacySwitch) + self.addLink(switch[3], router[2], intfName2="r2-eth1") + self.addLink(switch[3], router[3], intfName2="r3-eth1") # switch 4 is stub on remote RIP router - switch[4] = self.addSwitch('sw4', cls=topotest.LegacySwitch) - self.addLink(switch[4], router[3], intfName2='r3-eth0') + 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[6] = self.addSwitch("sw6", cls=topotest.LegacySwitch) + self.addLink(switch[6], router[1], intfName2="r1-eth3") ##################################################### @@ -99,6 +104,7 @@ def build(self, **_opts): ## ##################################################### + def setup_module(module): global topo, net @@ -106,7 +112,7 @@ def setup_module(module): print("******************************************\n") print("Cleanup old Mininet runs") - os.system('sudo mn -c > /dev/null 2>&1') + os.system("sudo mn -c > /dev/null 2>&1") thisDir = os.path.dirname(os.path.realpath(__file__)) topo = NetworkTopo() @@ -117,9 +123,9 @@ def setup_module(module): # Starting Routers # for i in range(1, 4): - net['r%s' % i].loadConf('zebra', '%s/r%s/zebra.conf' % (thisDir, i)) - net['r%s' % i].loadConf('ripngd', '%s/r%s/ripngd.conf' % (thisDir, i)) - net['r%s' % i].startRouter() + net["r%s" % i].loadConf("zebra", "%s/r%s/zebra.conf" % (thisDir, i)) + net["r%s" % i].loadConf("ripngd", "%s/r%s/ripngd.conf" % (thisDir, i)) + net["r%s" % i].startRouter() # For debugging after starting Quagga/FRR daemons, uncomment the next line # CLI(net) @@ -140,16 +146,15 @@ def test_router_running(): global net # Skip if previous fatal error condition is raised - if (fatal_error != ""): + if fatal_error != "": pytest.skip(fatal_error) print("\n\n** Check if FRR/Quagga is running on each Router node") print("******************************************\n") - sleep(5) # Starting Routers for i in range(1, 4): - fatal_error = net['r%s' % i].checkRouterRunning() + fatal_error = net["r%s" % i].checkRouterRunning() assert fatal_error == "", fatal_error # For debugging after starting FRR/Quagga daemons, uncomment the next line @@ -161,7 +166,7 @@ def test_converge_protocols(): global net # Skip if previous fatal error condition is raised - if (fatal_error != ""): + if fatal_error != "": pytest.skip(fatal_error) thisDir = os.path.dirname(os.path.realpath(__file__)) @@ -169,16 +174,16 @@ def test_converge_protocols(): print("\n\n** Waiting for protocols convergence") print("******************************************\n") - # Not really implemented yet - just sleep 60 secs for now - sleep(60) + # Not really implemented yet - just sleep 11 secs for now + sleep(11) # Make sure that all daemons are running for i in range(1, 4): - fatal_error = net['r%s' % i].checkRouterRunning() + fatal_error = net["r%s" % i].checkRouterRunning() assert fatal_error == "", fatal_error # For debugging after starting FRR/Quagga daemons, uncomment the next line - #CLI(net) + # CLI(net) def test_ripng_status(): @@ -186,7 +191,7 @@ def test_ripng_status(): global net # Skip if previous fatal error condition is raised - if (fatal_error != ""): + if fatal_error != "": pytest.skip(fatal_error) thisDir = os.path.dirname(os.path.realpath(__file__)) @@ -196,41 +201,53 @@ def test_ripng_status(): print("******************************************\n") failures = 0 for i in range(1, 4): - refTableFile = '%s/r%s/ripng_status.ref' % (thisDir, i) + refTableFile = "%s/r%s/ripng_status.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) + expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1) # Actual output from router - actual = net['r%s' % i].cmd('vtysh -c "show ipv6 ripng status" 2> /dev/null').rstrip() + actual = ( + net["r%s" % i] + .cmd('vtysh -c "show ipv6 ripng status" 2> /dev/null') + .rstrip() + ) # Mask out Link-Local mac address portion. They are random... actual = re.sub(r" fe80::[0-9a-f:]+", " fe80::XXXX:XXXX:XXXX:XXXX", actual) - # Drop time in next due + # Drop time in next due actual = re.sub(r"in [0-9]+ seconds", "in XX seconds", actual) # Drop time in last update actual = re.sub(r" [0-2][0-9]:[0-5][0-9]:[0-5][0-9]", " XX:XX:XX", actual) # Fix newlines (make them all the same) - actual = ('\n'.join(actual.splitlines()) + '\n').splitlines(1) + actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1) # Generate Diff - diff = topotest.get_textdiff(actual, expected, + diff = topotest.get_textdiff( + actual, + expected, title1="actual IPv6 RIPng status", - title2="expected IPv6 RIPng status") + title2="expected IPv6 RIPng status", + ) # Empty string if it matches, otherwise diff contains unified diff if diff: - sys.stderr.write('r%s failed IPv6 RIPng status check:\n%s\n' % (i, diff)) + sys.stderr.write( + "r%s failed IPv6 RIPng status check:\n%s\n" % (i, diff) + ) failures += 1 else: print("r%s ok" % i) - assert failures == 0, "IPv6 RIPng status failed for router r%s:\n%s" % (i, diff) + assert failures == 0, "IPv6 RIPng status failed for router r%s:\n%s" % ( + i, + diff, + ) # Make sure that all daemons are running for i in range(1, 4): - fatal_error = net['r%s' % i].checkRouterRunning() + fatal_error = net["r%s" % i].checkRouterRunning() assert fatal_error == "", fatal_error # For debugging after starting FRR/Quagga daemons, uncomment the next line @@ -242,7 +259,7 @@ def test_ripng_routes(): global net # Skip if previous fatal error condition is raised - if (fatal_error != ""): + if fatal_error != "": pytest.skip(fatal_error) thisDir = os.path.dirname(os.path.realpath(__file__)) @@ -252,42 +269,52 @@ def test_ripng_routes(): print("******************************************\n") failures = 0 for i in range(1, 4): - refTableFile = '%s/r%s/show_ipv6_ripng.ref' % (thisDir, i) + refTableFile = "%s/r%s/show_ipv6_ripng.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) + expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1) # Actual output from router - actual = net['r%s' % i].cmd('vtysh -c "show ipv6 ripng" 2> /dev/null').rstrip() + actual = ( + net["r%s" % i].cmd('vtysh -c "show ipv6 ripng" 2> /dev/null').rstrip() + ) # Drop Time actual = re.sub(r" [0-9][0-9]:[0-5][0-9]", " XX:XX", actual) # Mask out Link-Local mac address portion. They are random... - actual = re.sub(r" fe80::[0-9a-f: ]+", " fe80::XXXX:XXXX:XXXX:XXXX ", actual) + actual = re.sub( + r" fe80::[0-9a-f: ]+", " fe80::XXXX:XXXX:XXXX:XXXX ", actual + ) # Remove trailing spaces on all lines - actual = '\n'.join([line.rstrip() for line in actual.splitlines()]) + actual = "\n".join([line.rstrip() for line in actual.splitlines()]) # Fix newlines (make them all the same) - actual = ('\n'.join(actual.splitlines()) + '\n').splitlines(1) + actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1) # Generate Diff - diff = topotest.get_textdiff(actual, expected, + diff = topotest.get_textdiff( + actual, + expected, title1="actual SHOW IPv6 RIPng", - title2="expected SHOW IPv6 RIPng") + title2="expected SHOW IPv6 RIPng", + ) # Empty string if it matches, otherwise diff contains unified diff if diff: - sys.stderr.write('r%s failed SHOW IPv6 RIPng check:\n%s\n' % (i, diff)) + sys.stderr.write("r%s failed SHOW IPv6 RIPng check:\n%s\n" % (i, diff)) failures += 1 else: print("r%s ok" % i) - assert failures == 0, "SHOW IPv6 RIPng failed for router r%s:\n%s" % (i, diff) + assert failures == 0, "SHOW IPv6 RIPng failed for router r%s:\n%s" % ( + i, + diff, + ) # Make sure that all daemons are running for i in range(1, 4): - fatal_error = net['r%s' % i].checkRouterRunning() + fatal_error = net["r%s" % i].checkRouterRunning() assert fatal_error == "", fatal_error # For debugging after starting FRR/Quagga daemons, uncomment the next line @@ -299,7 +326,7 @@ def test_zebra_ipv6_routingTable(): global net # Skip if previous fatal error condition is raised - if (fatal_error != ""): + if fatal_error != "": pytest.skip(fatal_error) thisDir = os.path.dirname(os.path.realpath(__file__)) @@ -309,39 +336,51 @@ def test_zebra_ipv6_routingTable(): print("******************************************\n") failures = 0 for i in range(1, 4): - refTableFile = '%s/r%s/show_ipv6_route.ref' % (thisDir, i) + refTableFile = "%s/r%s/show_ipv6_route.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) + expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1) # Actual output from router - actual = net['r%s' % i].cmd('vtysh -c "show ipv6 route" 2> /dev/null | grep "^R"').rstrip() + actual = ( + net["r%s" % i] + .cmd('vtysh -c "show ipv6 route" 2> /dev/null | grep "^R"') + .rstrip() + ) # Mask out Link-Local mac address portion. They are random... actual = re.sub(r" fe80::[0-9a-f:]+", " fe80::XXXX:XXXX:XXXX:XXXX", actual) # Drop timers on end of line (older Quagga Versions) actual = re.sub(r", [0-2][0-9]:[0-5][0-9]:[0-5][0-9]", "", actual) # Fix newlines (make them all the same) - actual = ('\n'.join(actual.splitlines()) + '\n').splitlines(1) + actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1) # Generate Diff - diff = topotest.get_textdiff(actual, expected, + diff = topotest.get_textdiff( + actual, + expected, title1="actual Zebra IPv6 routing table", - title2="expected Zebra IPv6 routing table") + title2="expected Zebra IPv6 routing table", + ) # Empty string if it matches, otherwise diff contains unified diff if diff: - sys.stderr.write('r%s failed Zebra IPv6 Routing Table Check:\n%s\n' % (i, diff)) + sys.stderr.write( + "r%s failed Zebra IPv6 Routing Table Check:\n%s\n" % (i, diff) + ) failures += 1 else: print("r%s ok" % i) - assert failures == 0, "Zebra IPv6 Routing Table verification failed for router r%s:\n%s" % (i, diff) + assert failures == 0, ( + "Zebra IPv6 Routing Table verification failed for router r%s:\n%s" + % (i, diff) + ) # Make sure that all daemons are running for i in range(1, 4): - fatal_error = net['r%s' % i].checkRouterRunning() + fatal_error = net["r%s" % i].checkRouterRunning() assert fatal_error == "", fatal_error # For debugging after starting FRR/Quagga daemons, uncomment the next line @@ -353,24 +392,26 @@ def test_shutdown_check_stderr(): global net # Skip if previous fatal error condition is raised - if (fatal_error != ""): + if fatal_error != "": pytest.skip(fatal_error) - if os.environ.get('TOPOTESTS_CHECK_STDERR') is None: - print("SKIPPED final check on StdErr output: Disabled (TOPOTESTS_CHECK_STDERR undefined)\n") - pytest.skip('Skipping test for Stderr output') + if os.environ.get("TOPOTESTS_CHECK_STDERR") is None: + print( + "SKIPPED final check on StdErr output: Disabled (TOPOTESTS_CHECK_STDERR undefined)\n" + ) + pytest.skip("Skipping test for Stderr output") thisDir = os.path.dirname(os.path.realpath(__file__)) print("\n\n** Verifying unexpected STDERR output from daemons") print("******************************************\n") - net['r1'].stopRouter() + net["r1"].stopRouter() - log = net['r1'].getStdErr('ripngd') + log = net["r1"].getStdErr("ripngd") if log: print("\nRIPngd StdErr Log:\n" + log) - log = net['r1'].getStdErr('zebra') + log = net["r1"].getStdErr("zebra") if log: print("\nZebra StdErr Log:\n" + log) @@ -380,22 +421,26 @@ def test_shutdown_check_memleak(): global net # Skip if previous fatal error condition is raised - if (fatal_error != ""): + if fatal_error != "": pytest.skip(fatal_error) - if os.environ.get('TOPOTESTS_CHECK_MEMLEAK') is None: - print("SKIPPED final check on Memory leaks: Disabled (TOPOTESTS_CHECK_MEMLEAK undefined)\n") - pytest.skip('Skipping test for memory leaks') - + if os.environ.get("TOPOTESTS_CHECK_MEMLEAK") is None: + print( + "SKIPPED final check on Memory leaks: Disabled (TOPOTESTS_CHECK_MEMLEAK undefined)\n" + ) + pytest.skip("Skipping test for memory leaks") + thisDir = os.path.dirname(os.path.realpath(__file__)) - net['r1'].stopRouter() - net['r1'].report_memory_leaks(os.environ.get('TOPOTESTS_CHECK_MEMLEAK'), os.path.basename(__file__)) + net["r1"].stopRouter() + net["r1"].report_memory_leaks( + os.environ.get("TOPOTESTS_CHECK_MEMLEAK"), os.path.basename(__file__) + ) -if __name__ == '__main__': +if __name__ == "__main__": - setLogLevel('info') + setLogLevel("info") # To suppress tracebacks, either use the following pytest call or add "--tb=no" to cli # retval = pytest.main(["-s", "--tb=no"]) retval = pytest.main(["-s"]) diff --git a/tests/topotests/route-scale/r1/installed.routes.json b/tests/topotests/route-scale/r1/installed.routes.json new file mode 100644 index 0000000000..25d209f9eb --- /dev/null +++ b/tests/topotests/route-scale/r1/installed.routes.json @@ -0,0 +1,16 @@ +{ + "routes":[ + { + "fib":32, + "rib":32, + "type":"connected" + }, + { + "fib":1000000, + "rib":1000000, + "type":"sharp" + } + ], + "routesTotal":1000032, + "routesTotalFib":1000032 +} diff --git a/tests/topotests/route-scale/r1/no.routes.json b/tests/topotests/route-scale/r1/no.routes.json new file mode 100644 index 0000000000..abebd1b143 --- /dev/null +++ b/tests/topotests/route-scale/r1/no.routes.json @@ -0,0 +1,11 @@ +{ + "routes":[ + { + "fib":32, + "rib":32, + "type":"connected" + } + ], + "routesTotal":32, + "routesTotalFib":32 +} diff --git a/tests/topotests/route-scale/r1/sharpd.conf b/tests/topotests/route-scale/r1/sharpd.conf new file mode 100644 index 0000000000..0848f34ddf --- /dev/null +++ b/tests/topotests/route-scale/r1/sharpd.conf @@ -0,0 +1,76 @@ +! +nexthop-group one + nexthop 192.168.0.1 r1-eth0 +! +nexthop-group two + nexthop 192.168.0.1 r1-eth0 + nexthop 192.168.1.1 r1-eth1 +! +nexthop-group four + nexthop 192.168.0.1 r1-eth0 + nexthop 192.168.1.1 r1-eth1 + nexthop 192.168.2.1 r1-eth2 + nexthop 192.168.3.1 r1-eth3 +! +nexthop-group eight + nexthop 192.168.0.1 r1-eth0 + nexthop 192.168.1.1 r1-eth1 + nexthop 192.168.2.1 r1-eth2 + nexthop 192.168.3.1 r1-eth3 + nexthop 192.168.4.1 r1-eth4 + nexthop 192.168.5.1 r1-eth5 + nexthop 192.168.6.1 r1-eth6 + nexthop 192.168.7.1 r1-eth7 +! +nexthop-group sixteen + nexthop 192.168.0.1 r1-eth0 + nexthop 192.168.1.1 r1-eth1 + nexthop 192.168.2.1 r1-eth2 + nexthop 192.168.3.1 r1-eth3 + nexthop 192.168.4.1 r1-eth4 + nexthop 192.168.5.1 r1-eth5 + nexthop 192.168.6.1 r1-eth6 + nexthop 192.168.7.1 r1-eth7 + nexthop 192.168.8.1 r1-eth8 + nexthop 192.168.9.1 r1-eth9 + nexthop 192.168.10.1 r1-eth10 + nexthop 192.168.11.1 r1-eth11 + nexthop 192.168.12.1 r1-eth12 + nexthop 192.168.13.1 r1-eth13 + nexthop 192.168.14.1 r1-eth14 + nexthop 192.168.15.1 r1-eth15 +! +nexthop-group thirtytwo + nexthop 192.168.0.1 r1-eth0 + nexthop 192.168.1.1 r1-eth1 + nexthop 192.168.2.1 r1-eth2 + nexthop 192.168.3.1 r1-eth3 + nexthop 192.168.4.1 r1-eth4 + nexthop 192.168.5.1 r1-eth5 + nexthop 192.168.6.1 r1-eth6 + nexthop 192.168.7.1 r1-eth7 + nexthop 192.168.8.1 r1-eth8 + nexthop 192.168.9.1 r1-eth9 + nexthop 192.168.10.1 r1-eth10 + nexthop 192.168.11.1 r1-eth11 + nexthop 192.168.12.1 r1-eth12 + nexthop 192.168.13.1 r1-eth13 + nexthop 192.168.14.1 r1-eth14 + nexthop 192.168.15.1 r1-eth15 + nexthop 192.168.16.1 r1-eth16 + nexthop 192.168.17.1 r1-eth17 + nexthop 192.168.18.1 r1-eth18 + nexthop 192.168.19.1 r1-eth19 + nexthop 192.168.20.1 r1-eth20 + nexthop 192.168.21.1 r1-eth21 + nexthop 192.168.22.1 r1-eth22 + nexthop 192.168.23.1 r1-eth23 + nexthop 192.168.24.1 r1-eth24 + nexthop 192.168.25.1 r1-eth25 + nexthop 192.168.26.1 r1-eth26 + nexthop 192.168.27.1 r1-eth27 + nexthop 192.168.28.1 r1-eth28 + nexthop 192.168.29.1 r1-eth29 + nexthop 192.168.30.1 r1-eth30 + nexthop 192.168.31.1 r1-eth31 +! \ No newline at end of file diff --git a/tests/topotests/route-scale/r1/zebra.conf b/tests/topotests/route-scale/r1/zebra.conf new file mode 100644 index 0000000000..48a01f48ab --- /dev/null +++ b/tests/topotests/route-scale/r1/zebra.conf @@ -0,0 +1,96 @@ +int r1-eth0 + ip addr 192.168.0.1/24 +! +int r1-eth1 + ip addr 192.168.1.1/24 +! +int r1-eth2 + ip addr 192.168.2.1/24 +! +int r1-eth3 + ip addr 192.168.3.1/24 +! +int r1-eth4 + ip addr 192.168.4.1/24 +! +int r1-eth5 + ip addr 192.168.5.1/24 +! +int r1-eth6 + ip addr 192.168.6.1/24 +! +int r1-eth7 + ip addr 192.168.7.1/24 +! +int r1-eth8 + ip addr 192.168.8.1/24 +! +int r1-eth9 + ip addr 192.168.9.1/24 +! +int r1-eth10 + ip addr 192.168.10.1/24 +! +int r1-eth11 + ip addr 192.168.11.1/24 +! +int r1-eth12 + ip addr 192.168.12.1/24 +! +int r1-eth13 + ip addr 192.168.13.1/24 +! +int r1-eth14 + ip addr 192.168.14.1/24 +! +int r1-eth15 + ip addr 192.168.15.1/24 +! +int r1-eth16 + ip addr 192.168.16.1/24 +! +int r1-eth17 + ip addr 192.168.17.1/24 +! +int r1-eth18 + ip addr 192.168.18.1/24 +! +int r1-eth19 + ip addr 192.168.19.1/24 +! +int r1-eth20 + ip addr 192.168.20.1/24 +! +int r1-eth21 + ip addr 192.168.21.1/24 +! +int r1-eth22 + ip addr 192.168.22.1/24 +! +int r1-eth23 + ip addr 192.168.23.1/24 +! +int r1-eth24 + ip addr 192.168.24.1/24 +! +int r1-eth25 + ip addr 192.168.25.1/24 +! +int r1-eth26 + ip addr 192.168.26.1/24 +! +int r1-eth27 + ip addr 192.168.27.1/24 +! +int r1-eth28 + ip addr 192.168.28.1/24 +! +int r1-eth29 + ip addr 192.168.29.1/24 +! +int r1-eth30 + ip addr 192.168.30.1/24 +! +int r1-eth31 + ip addr 192.168.31.1/24 +! \ No newline at end of file diff --git a/tests/topotests/route-scale/test_route_scale.py b/tests/topotests/route-scale/test_route_scale.py new file mode 100755 index 0000000000..508d1746b3 --- /dev/null +++ b/tests/topotests/route-scale/test_route_scale.py @@ -0,0 +1,215 @@ +#!/usr/bin/env python + +# +# test_route_scale.py +# +# Copyright (c) 2020 by +# Cumulus Networks, Inc. +# Donald Sharp +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_route_scale.py: Testing route scale + +""" + +import os +import re +import sys +import pytest +import json +from functools import partial + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from lib.common_config import shutdown_bringup_interface + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + +##################################################### +## +## Network Topology Definition +## +##################################################### + + +class NetworkTopo(Topo): + "Route Scale Topology" + + def build(self, **_opts): + "Build function" + + tgen = get_topogen(self) + + # Populate routers + for routern in range(1, 2): + tgen.add_router("r{}".format(routern)) + + # Populate switches + for switchn in range(1, 33): + switch = tgen.add_switch("sw{}".format(switchn)) + switch.add_link(tgen.gears["r1"]) + + +##################################################### +## +## Tests starting +## +##################################################### + + +def setup_module(module): + "Setup topology" + tgen = Topogen(NetworkTopo, module.__name__) + tgen.start_topology() + + router_list = tgen.routers() + for rname, router in router_list.iteritems(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_SHARP, os.path.join(CWD, "{}/sharpd.conf".format(rname)) + ) + + tgen.start_router() + #tgen.mininet_cli() + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + +def test_converge_protocols(): + "Wait for protocol convergence" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + +def run_one_setup(r1, s): + "Run one ecmp config" + + # Extract params + expected_installed = s['expect_in'] + expected_removed = s['expect_rem'] + + count = s['count'] + wait = s['wait'] + + logger.info("Testing 1 million routes X {} ecmp".format(s['ecmp'])) + + r1.vtysh_cmd("sharp install route 1.0.0.0 \ + nexthop-group {} 1000000".format(s['nhg']), + isjson=False) + + test_func = partial(topotest.router_json_cmp, r1, "show ip route summary json", expected_installed) + success, result = topotest.run_and_expect(test_func, None, count, wait) + assert success, "Route scale test install failed:\n{}".format(result) + + output = r1.vtysh_cmd("sharp data route", isjson=False) + logger.info("1 million routes X {} ecmp installed".format(s['ecmp'])) + logger.info(output) + r1.vtysh_cmd("sharp remove route 1.0.0.0 1000000", isjson=False) + test_func = partial(topotest.router_json_cmp, r1, "show ip route summary json", expected_removed) + success, result = topotest.run_and_expect(test_func, None, count, wait) + assert success, "Route scale test remove failed:\n{}".format(result) + + output = r1.vtysh_cmd("sharp data route", isjson=False) + logger.info("1 million routes x {} ecmp removed".format( + s['ecmp'])) + logger.info(output) + + +def test_route_install(): + "Test route install for a variety of ecmp" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + + installed_file = "{}/r1/installed.routes.json".format(CWD) + expected_installed = json.loads(open(installed_file).read()) + + removed_file = "{}/r1/no.routes.json".format(CWD) + expected_removed = json.loads(open(removed_file).read()) + + # dict keys of params: ecmp number, corresponding nhg name, timeout, + # number of times to wait + scale_keys = ['ecmp', 'nhg', 'wait', 'count', 'expect_in', 'expect_rem'] + + # Table of defaults, used for timeout values and 'expected' objects + scale_defaults = dict(zip(scale_keys, [None, None, 7, 30, + expected_installed, + expected_removed])) + + # List of params for each step in the test; note extra time given + # for the highest ecmp steps. Executing 'show' at scale can be costly + # so we widen the interval there too. + scale_steps = [ + [1, 'one'], [2, 'two'], [4, 'four'], + [8, 'eight'], [16, 'sixteen', 10, 40], [32, 'thirtytwo', 10, 40] + ] + + # Build up a list of dicts with params for each step of the test; + # use defaults where the step doesn't supply a value + scale_setups = [] + for s in scale_steps: + d = dict(zip(scale_keys, s)) + for k in scale_keys: + if k not in d: + d[k] = scale_defaults[k] + + scale_setups.append(d) + + # Avoid top ecmp case for runs with < 4G memory + p = os.popen('free') + l = p.readlines()[1].split() + mem = int(l[1]) + if mem < 4000000: + logger.info('Limited memory available: {}, skipping x32 testcase'.format(mem)) + scale_setups = scale_setups[0:-1] + + # Run each step using the dicts we've built + for s in scale_setups: + run_one_setup(r1, s) + +# Mem leak testcase +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + tgen.report_memory_leaks() + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/zebra_netlink/__init__.py b/tests/topotests/zebra_netlink/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/zebra_netlink/r1/sharpd.conf b/tests/topotests/zebra_netlink/r1/sharpd.conf new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/zebra_netlink/r1/v4_route.json b/tests/topotests/zebra_netlink/r1/v4_route.json new file mode 100644 index 0000000000..39041ebc95 --- /dev/null +++ b/tests/topotests/zebra_netlink/r1/v4_route.json @@ -0,0 +1,2302 @@ +{ + "2.1.3.7\/32":[ + { + "prefix":"2.1.3.7\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.8\/32":[ + { + "prefix":"2.1.3.8\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.9\/32":[ + { + "prefix":"2.1.3.9\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.10\/32":[ + { + "prefix":"2.1.3.10\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.11\/32":[ + { + "prefix":"2.1.3.11\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.12\/32":[ + { + "prefix":"2.1.3.12\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.13\/32":[ + { + "prefix":"2.1.3.13\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.14\/32":[ + { + "prefix":"2.1.3.14\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.15\/32":[ + { + "prefix":"2.1.3.15\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.16\/32":[ + { + "prefix":"2.1.3.16\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.17\/32":[ + { + "prefix":"2.1.3.17\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.18\/32":[ + { + "prefix":"2.1.3.18\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.19\/32":[ + { + "prefix":"2.1.3.19\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.20\/32":[ + { + "prefix":"2.1.3.20\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.21\/32":[ + { + "prefix":"2.1.3.21\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.22\/32":[ + { + "prefix":"2.1.3.22\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.23\/32":[ + { + "prefix":"2.1.3.23\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.24\/32":[ + { + "prefix":"2.1.3.24\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.25\/32":[ + { + "prefix":"2.1.3.25\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.26\/32":[ + { + "prefix":"2.1.3.26\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.27\/32":[ + { + "prefix":"2.1.3.27\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.28\/32":[ + { + "prefix":"2.1.3.28\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.29\/32":[ + { + "prefix":"2.1.3.29\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.30\/32":[ + { + "prefix":"2.1.3.30\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.31\/32":[ + { + "prefix":"2.1.3.31\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.32\/32":[ + { + "prefix":"2.1.3.32\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.33\/32":[ + { + "prefix":"2.1.3.33\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.34\/32":[ + { + "prefix":"2.1.3.34\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.35\/32":[ + { + "prefix":"2.1.3.35\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.36\/32":[ + { + "prefix":"2.1.3.36\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.37\/32":[ + { + "prefix":"2.1.3.37\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.38\/32":[ + { + "prefix":"2.1.3.38\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.39\/32":[ + { + "prefix":"2.1.3.39\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.40\/32":[ + { + "prefix":"2.1.3.40\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.41\/32":[ + { + "prefix":"2.1.3.41\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.42\/32":[ + { + "prefix":"2.1.3.42\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.43\/32":[ + { + "prefix":"2.1.3.43\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.44\/32":[ + { + "prefix":"2.1.3.44\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.45\/32":[ + { + "prefix":"2.1.3.45\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.46\/32":[ + { + "prefix":"2.1.3.46\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.47\/32":[ + { + "prefix":"2.1.3.47\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.48\/32":[ + { + "prefix":"2.1.3.48\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.49\/32":[ + { + "prefix":"2.1.3.49\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.50\/32":[ + { + "prefix":"2.1.3.50\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.51\/32":[ + { + "prefix":"2.1.3.51\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.52\/32":[ + { + "prefix":"2.1.3.52\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.53\/32":[ + { + "prefix":"2.1.3.53\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.54\/32":[ + { + "prefix":"2.1.3.54\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.55\/32":[ + { + "prefix":"2.1.3.55\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.56\/32":[ + { + "prefix":"2.1.3.56\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.57\/32":[ + { + "prefix":"2.1.3.57\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.58\/32":[ + { + "prefix":"2.1.3.58\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.59\/32":[ + { + "prefix":"2.1.3.59\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.60\/32":[ + { + "prefix":"2.1.3.60\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.61\/32":[ + { + "prefix":"2.1.3.61\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.62\/32":[ + { + "prefix":"2.1.3.62\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.63\/32":[ + { + "prefix":"2.1.3.63\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.64\/32":[ + { + "prefix":"2.1.3.64\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.65\/32":[ + { + "prefix":"2.1.3.65\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.66\/32":[ + { + "prefix":"2.1.3.66\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.67\/32":[ + { + "prefix":"2.1.3.67\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.68\/32":[ + { + "prefix":"2.1.3.68\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.69\/32":[ + { + "prefix":"2.1.3.69\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.70\/32":[ + { + "prefix":"2.1.3.70\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.71\/32":[ + { + "prefix":"2.1.3.71\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.72\/32":[ + { + "prefix":"2.1.3.72\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.73\/32":[ + { + "prefix":"2.1.3.73\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.74\/32":[ + { + "prefix":"2.1.3.74\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.75\/32":[ + { + "prefix":"2.1.3.75\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.76\/32":[ + { + "prefix":"2.1.3.76\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.77\/32":[ + { + "prefix":"2.1.3.77\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.78\/32":[ + { + "prefix":"2.1.3.78\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.79\/32":[ + { + "prefix":"2.1.3.79\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.80\/32":[ + { + "prefix":"2.1.3.80\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.81\/32":[ + { + "prefix":"2.1.3.81\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.82\/32":[ + { + "prefix":"2.1.3.82\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.83\/32":[ + { + "prefix":"2.1.3.83\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.84\/32":[ + { + "prefix":"2.1.3.84\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.85\/32":[ + { + "prefix":"2.1.3.85\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.86\/32":[ + { + "prefix":"2.1.3.86\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.87\/32":[ + { + "prefix":"2.1.3.87\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.88\/32":[ + { + "prefix":"2.1.3.88\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.89\/32":[ + { + "prefix":"2.1.3.89\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.90\/32":[ + { + "prefix":"2.1.3.90\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.91\/32":[ + { + "prefix":"2.1.3.91\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.92\/32":[ + { + "prefix":"2.1.3.92\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.93\/32":[ + { + "prefix":"2.1.3.93\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.94\/32":[ + { + "prefix":"2.1.3.94\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.95\/32":[ + { + "prefix":"2.1.3.95\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.96\/32":[ + { + "prefix":"2.1.3.96\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.97\/32":[ + { + "prefix":"2.1.3.97\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.98\/32":[ + { + "prefix":"2.1.3.98\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.99\/32":[ + { + "prefix":"2.1.3.99\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.100\/32":[ + { + "prefix":"2.1.3.100\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.101\/32":[ + { + "prefix":"2.1.3.101\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.102\/32":[ + { + "prefix":"2.1.3.102\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.103\/32":[ + { + "prefix":"2.1.3.103\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.104\/32":[ + { + "prefix":"2.1.3.104\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.105\/32":[ + { + "prefix":"2.1.3.105\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.106\/32":[ + { + "prefix":"2.1.3.106\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ] +} diff --git a/tests/topotests/zebra_netlink/r1/zebra.conf b/tests/topotests/zebra_netlink/r1/zebra.conf new file mode 100644 index 0000000000..786be19ad4 --- /dev/null +++ b/tests/topotests/zebra_netlink/r1/zebra.conf @@ -0,0 +1,2 @@ +int r1-eth0 + ip address 192.168.1.1/24 \ No newline at end of file diff --git a/tests/topotests/zebra_netlink/test_zebra_netlink.py b/tests/topotests/zebra_netlink/test_zebra_netlink.py new file mode 100755 index 0000000000..587c3a69d2 --- /dev/null +++ b/tests/topotests/zebra_netlink/test_zebra_netlink.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python + +# +# test_zebra_netlink.py +# +# Copyright (c) 2020 by +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_zebra_netlink.py: Test some basic interactions with kernel using Netlink + +""" + +import os +import re +import sys +import pytest +import json +import platform +from functools import partial + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from lib.common_config import shutdown_bringup_interface + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + +##################################################### +## +## Network Topology Definition +## +##################################################### + + +class ZebraTopo(Topo): + "Test topology builder" + + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + tgen.add_router("r1") + + # Create a empty network for router 1 + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + + +##################################################### +## +## Tests starting +## +##################################################### + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(ZebraTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + for rname, router in router_list.iteritems(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + + router.load_config( + TopoRouter.RD_SHARP, os.path.join(CWD, "{}/sharpd.conf".format(rname)) + ) + + # Initialize all routers. + tgen.start_router() + + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + + +def test_zebra_netlink_batching(): + "Test the situation where dataplane fills netlink send buffer entirely." + logger.info( + "Test the situation where dataplane fills netlink send buffer entirely." + ) + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip("skipped because of previous test failure") + r1 = tgen.gears["r1"] + + # Reduce the size of the buffer to hit the limit. + r1.vtysh_cmd("conf t\nzebra kernel netlink batch-tx-buf 256 256") + + r1.vtysh_cmd("sharp install routes 2.1.3.7 nexthop 192.168.1.1 100") + json_file = "{}/r1/v4_route.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, r1, "show ip route json", expected,) + _, result = topotest.run_and_expect(test_func, None, count=2, wait=0.5) + assertmsg = '"r1" JSON output mismatches' + assert result is None, assertmsg + + r1.vtysh_cmd("sharp remove routes 2.1.3.7 100") + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/zebra_rib/r1/v4_route_1.json b/tests/topotests/zebra_rib/r1/v4_route_1.json new file mode 100644 index 0000000000..a238e52aa7 --- /dev/null +++ b/tests/topotests/zebra_rib/r1/v4_route_1.json @@ -0,0 +1,24 @@ +{ + "4.5.1.0\/24":[ + { + "prefix":"4.5.1.0\/24", + "protocol":"kernel", + "selected":true, + "destSelected":true, + "distance":255, + "metric":8192, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.210.2", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/zebra_rib/r1/v4_route_1_static_override.json b/tests/topotests/zebra_rib/r1/v4_route_1_static_override.json new file mode 100644 index 0000000000..5bc665bea7 --- /dev/null +++ b/tests/topotests/zebra_rib/r1/v4_route_1_static_override.json @@ -0,0 +1,40 @@ +{ + "4.5.1.0\/24":[ + { + "prefix":"4.5.1.0\/24", + "protocol":"static", + "selected":true, + "destSelected":true, + "distance":1, + "metric":0, + "installed":true, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.216.3", + "afi":"ipv4", + "interfaceName":"r1-eth6", + "active":true + } + ] + }, + { + "prefix":"4.5.1.0\/24", + "protocol":"kernel", + "distance":255, + "metric":8192, + "installed":true, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.210.2", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/zebra_rib/r1/v4_route_2.json b/tests/topotests/zebra_rib/r1/v4_route_2.json new file mode 100644 index 0000000000..5454af899b --- /dev/null +++ b/tests/topotests/zebra_rib/r1/v4_route_2.json @@ -0,0 +1,23 @@ +{ + "4.5.2.0\/24":[ + { + "prefix":"4.5.2.0\/24", + "protocol":"kernel", + "selected":true, + "destSelected":true, + "distance":1, + "metric":1, + "installed":true, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.211.2", + "afi":"ipv4", + "interfaceName":"r1-eth1", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/zebra_rib/r1/zebra.conf b/tests/topotests/zebra_rib/r1/zebra.conf new file mode 100644 index 0000000000..b914f2dda2 --- /dev/null +++ b/tests/topotests/zebra_rib/r1/zebra.conf @@ -0,0 +1,26 @@ +hostname r1 +! +interface r1-eth0 + ip address 192.168.210.1/24 +! +interface r1-eth1 + ip address 192.168.211.1/24 +! +interface r1-eth2 + ip address 192.168.212.1/24 +! +interface r1-eth3 + ip address 192.168.213.1/24 +! +interface r1-eth4 + ip address 192.168.214.1/24 +! +interface r1-eth5 + ip address 192.168.215.1/24 +! +interface r1-eth6 + ip address 192.168.216.1/24 +! +interface r1-eth7 + ip address 192.168.217.1/24 +! diff --git a/tests/topotests/zebra_rib/test_zebra_rib.py b/tests/topotests/zebra_rib/test_zebra_rib.py new file mode 100755 index 0000000000..a097a5861c --- /dev/null +++ b/tests/topotests/zebra_rib/test_zebra_rib.py @@ -0,0 +1,172 @@ +#!/usr/bin/env python +# +# test_zebra_rib.py +# +# Copyright (c) 2019 by +# Cumulus Networks, Inc +# Donald Sharp +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_zebra_rib.py: Test some basic zebra <-> kernel interactions +""" + +import os +import re +import sys +from functools import partial +import pytest +import json + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + + +class ZebraTopo(Topo): + "Test topology builder" + + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + tgen.add_router("r1") + + # Create a empty network for router 1 + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r1"]) + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(ZebraTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + for rname, router in router_list.iteritems(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + + # Initialize all routers. + tgen.start_router() + + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + tgen.stop_topology() + + +def test_zebra_kernel_admin_distance(): + "Test some basic kernel routes added that should be accepted" + logger.info("Test some basic kernel routes that should be accepted") + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + r1 = tgen.gears["r1"] + + # Route with 255/8192 metric + r1.run("ip route add 4.5.1.0/24 via 192.168.210.2 dev r1-eth0 metric 4278198272") + # Route with 1/1 metric + r1.run("ip route add 4.5.2.0/24 via 192.168.211.2 dev r1-eth1 metric 16777217") + # Route with 10/1 metric + r1.run("ip route add 4.5.3.0/24 via 192.168.212.2 dev r1-eth2 metric 167772161") + # Same route with a 160/1 metric + r1.run("ip route add 4.5.3.0/24 via 192.168.213.2 dev r1-eth3 metric 2684354561") + + # Currently I believe we have a bug here with the same route and different + # metric. That needs to be properly resolved. Making a note for + # coming back around later and fixing this. + # tgen.mininet_cli() + for i in range(1, 2): + json_file = "{}/r1/v4_route_{}.json".format(CWD, i) + expected = json.loads(open(json_file).read()) + + test_func = partial( + topotest.router_json_cmp, + r1, + "show ip route 4.5.{}.0 json".format(i), + expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=2, wait=0.5) + assertmsg = '"r1" JSON output mismatches' + assert result is None, assertmsg + # tgen.mininet_cli() + + +def test_zebra_kernel_override(): + "Test that a FRR route with a lower admin distance takes over" + logger.info("Test kernel override with a better admin distance") + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip("skipped because of previous test failure") + + r1 = tgen.gears["r1"] + r1.vtysh_cmd("conf\nip route 4.5.1.0/24 192.168.216.3") + json_file = "{}/r1/v4_route_1_static_override.json".format(CWD) + expected = json.loads(open(json_file).read()) + + test_func = partial( + topotest.router_json_cmp, r1, "show ip route 4.5.1.0 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=2, wait=0.5) + assert result is None, '"r1" JSON output mismatches' + + logger.info( + "Test that the removal of the static route allows the kernel to take back over" + ) + r1.vtysh_cmd("conf\nno ip route 4.5.1.0/24 192.168.216.3") + json_file = "{}/r1/v4_route_1.json".format(CWD) + expected = json.loads(open(json_file).read()) + + test_func = partial( + topotest.router_json_cmp, r1, "show ip route 4.5.1.0 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=2, wait=0.5) + assert result is None, '"r1" JSON output mismatches' + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tools/checkpatch.pl b/tools/checkpatch.pl index 77e6e9a330..c0624d933e 100755 --- a/tools/checkpatch.pl +++ b/tools/checkpatch.pl @@ -3351,7 +3351,7 @@ sub process { # if/while/etc brace do not go on next line, unless defining a do while loop, # or if that brace on the next line is for something else - if ($line =~ /(.*)\b((?:if|while|for|switch|(?:[a-z_]+|)for_each[a-z_]+)\s*\(|do\b|else\b)/ && $line !~ /^.\s*\#/) { + if ($line =~ /(.*)\b((?:if|while|for|switch|(?:[a-z_]+|)frr_(each|with)[a-z_]+)\s*\(|do\b|else\b)/ && $line !~ /^.\s*\#/) { my $pre_ctx = "$1$2"; my ($level, @ctx) = ctx_statement_level($linenr, $realcnt, 0); @@ -3397,7 +3397,7 @@ sub process { } # Check relative indent for conditionals and blocks. - if ($line =~ /\b(?:(?:if|while|for|(?:[a-z_]+|)for_each[a-z_]+)\s*\(|(?:do|else)\b)/ && $line !~ /^.\s*#/ && $line !~ /\}\s*while\s*/) { + if ($line =~ /\b(?:(?:if|while|for|(?:[a-z_]+|)frr_(each|with)[a-z_]+)\s*\(|(?:do|else)\b)/ && $line !~ /^.\s*#/ && $line !~ /\}\s*while\s*/) { ($stat, $cond, $line_nr_next, $remain_next, $off_next) = ctx_statement_block($linenr, $realcnt, 0) if (!defined $stat); @@ -5177,6 +5177,31 @@ sub process { } } + if (!defined $suppress_ifbraces{$linenr - 1} && + $line =~ /\b(frr_with_)/) { + my ($level, $endln, @chunks) = + ctx_statement_full($linenr, $realcnt, $-[0]); + + # Check the condition. + my ($cond, $block) = @{$chunks[0]}; + #print "CHECKING<$linenr> cond<$cond> block<$block>\n"; + if (defined $cond) { + substr($block, 0, length($cond), ''); + } + + if ($level == 0 && $block !~ /^\s*\{/) { + my $herectx = $here . "\n"; + my $cnt = statement_rawlines($block); + + for (my $n = 0; $n < $cnt; $n++) { + $herectx .= raw_line($linenr, $n) . "\n"; + } + + WARN("BRACES", + "braces {} are mandatory for frr_with_* blocks\n" . $herectx); + } + } + # check for single line unbalanced braces if ($sline =~ /^.\s*\}\s*else\s*$/ || $sline =~ /^.\s*else\s*\{\s*$/) { @@ -6347,6 +6372,35 @@ sub process { "Please, only use 32 bit atomics.\n" . $herecurr); } +# check for use of strcpy() + if ($line =~ /\bstrcpy\s*\(.*\)/) { + ERROR("STRCPY", + "strcpy() is error-prone; please use strlcpy()" . $herecurr); + } + +# check for use of strncpy() + if ($line =~ /\bstrncpy\s*\(.*\)/) { + WARN("STRNCPY", + "strncpy() is error-prone; please use strlcpy() if possible, or memcpy()" . $herecurr); + } + +# check for use of strcat() + if ($line =~ /\bstrcat\s*\(.*\)/) { + ERROR("STRCAT", + "strcat() is error-prone; please use strlcat() if possible" . $herecurr); + } + +# check for use of strncat() + if ($line =~ /\bstrncat\s*\(.*\)/) { + WARN("STRNCAT", + "strncat() is error-prone; please use strlcat() if possible" . $herecurr); + } + +# check for use of bzero() + if ($line =~ /\bbzero\s*\(.*\)/) { + ERROR("BZERO", + "bzero() is deprecated; use memset()" . $herecurr); + } } # If we have no input at all, then there is nothing to report on diff --git a/tools/cocci.h b/tools/cocci.h index d25fcbbe23..7d6bb4cd7f 100644 --- a/tools/cocci.h +++ b/tools/cocci.h @@ -7,6 +7,18 @@ #define DEFUN_HIDDEN(funcname, cmdname, str, help) \ static int funcname(const struct cmd_element *self, struct vty *vty, \ int argc, struct cmd_token *argv[]) +#define DEFUN_NOSH(funcname, cmdname, str, help) \ + static int funcname(const struct cmd_element *self, struct vty *vty, \ + int argc, struct cmd_token *argv[]) +#define DEFPY(funcname, cmdname, str, help) \ + static int funcname(const struct cmd_element *self, struct vty *vty, \ + int argc, struct cmd_token *argv[]) +#define DEFPY_HIDDEN(funcname, cmdname, str, help) \ + static int funcname(const struct cmd_element *self, struct vty *vty, \ + int argc, struct cmd_token *argv[]) +#define DEFPY_NOSH(funcname, cmdname, str, help) \ + static int funcname(const struct cmd_element *self, struct vty *vty, \ + int argc, struct cmd_token *argv[]) #define ENABLE_BGP_VNC 1 #define ALL_LIST_ELEMENTS_RO(list, node, data) \ @@ -27,3 +39,86 @@ struct type *le_next; /* next element */ \ struct type **le_prev; /* address of previous next element */ \ } + +#define STREAM_GETC(S, P) \ + do { \ + uint8_t _pval; \ + if (!stream_getc2((S), &_pval)) \ + goto stream_failure; \ + (P) = _pval; \ + } while (0) + +#define STREAM_GETW(S, P) \ + do { \ + uint16_t _pval; \ + if (!stream_getw2((S), &_pval)) \ + goto stream_failure; \ + (P) = _pval; \ + } while (0) + +#define STREAM_GETL(S, P) \ + do { \ + uint32_t _pval; \ + if (!stream_getl2((S), &_pval)) \ + goto stream_failure; \ + (P) = _pval; \ + } while (0) + +#define STREAM_GETF(S, P) \ + do { \ + union { \ + float r; \ + uint32_t d; \ + } _pval; \ + if (stream_getl2((S), &_pval.d)) \ + goto stream_failure; \ + (P) = _pval.r; \ + } while (0) + +#define STREAM_GETQ(S, P) \ + do { \ + uint64_t _pval; \ + if (!stream_getq2((S), &_pval)) \ + goto stream_failure; \ + (P) = _pval; \ + } while (0) + +#define STREAM_GET(P, STR, SIZE) \ + do { \ + if (!stream_get2((P), (STR), (SIZE))) \ + goto stream_failure; \ + } while (0) + +#define AF_FOREACH(af) for ((af) = BGP_AF_START; (af) < BGP_AF_MAX; (af)++) + +#define FOREACH_AFI_SAFI(afi, safi) \ + \ + for (afi = AFI_IP; afi < AFI_MAX; afi++) \ + for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) + +#define FOREACH_SAFI(safi) for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) + +#define frr_with_privs(p) \ + for (int x = 1; x; x--) +#define frr_with_mutex(m) \ + for (int x = 1; x; x--) + +#define ALL_LSDB_TYPED_ADVRTR(lsdb, type, adv_router, lsa) \ + const struct route_node *iterend = \ + ospf6_lsdb_head(lsdb, 2, type, adv_router, &lsa); \ + lsa; \ + lsa = ospf6_lsdb_next(iterend, lsa) + +#define ALL_LSDB_TYPED(lsdb, type, lsa) \ + const struct route_node *iterend = \ + ospf6_lsdb_head(lsdb, 1, type, 0, &lsa); \ + lsa; \ + lsa = ospf6_lsdb_next(iterend, lsa) + +#define ALL_LSDB(lsdb, lsa) \ + const struct route_node *iterend = \ + ospf6_lsdb_head(lsdb, 0, 0, 0, &lsa); \ + lsa; \ + lsa = ospf6_lsdb_next(iterend, lsa) + +#define QOBJ_FIELDS struct qobj_node qobj_node; diff --git a/tools/coccinelle/__func__.cocci b/tools/coccinelle/__func__.cocci new file mode 100644 index 0000000000..fb68494d43 --- /dev/null +++ b/tools/coccinelle/__func__.cocci @@ -0,0 +1,10 @@ +@@ +@@ + +( +- __PRETTY_FUNCTION__ ++ __func__ +| +- __FUNCTION__ ++ __func__ +) diff --git a/tools/coccinelle/argv_find.cocci b/tools/coccinelle/argv_find.cocci new file mode 100644 index 0000000000..f13b035d7a --- /dev/null +++ b/tools/coccinelle/argv_find.cocci @@ -0,0 +1,16 @@ +@@ +identifier idx; +identifier argv; +identifier argc; +expression e1; +expression e2; +@@ + +- argv_find(argv, argc, e1, &idx); + if ( +- idx ++ argv_find(argv, argc, e1, &idx) + ) + { + e2; + } diff --git a/tools/coccinelle/bool_assignment.cocci b/tools/coccinelle/bool_assignment.cocci new file mode 100644 index 0000000000..e6146ea310 --- /dev/null +++ b/tools/coccinelle/bool_assignment.cocci @@ -0,0 +1,13 @@ +@@ +bool b; +@@ + +( + b = +- 0 ++ false +| + b = +- 1 ++ true +) diff --git a/tools/coccinelle/bool_expression.cocci b/tools/coccinelle/bool_expression.cocci new file mode 100644 index 0000000000..c0c329cb59 --- /dev/null +++ b/tools/coccinelle/bool_expression.cocci @@ -0,0 +1,29 @@ +@@ +bool t; +@@ + +( +- t == true ++ t +| +- true == t ++ t +| +- t != true ++ !t +| +- true != t ++ !t +| +- t == false ++ !t +| +- false == t ++ !t +| +- t != false ++ t +| +- false != t ++ t +) diff --git a/tools/coccinelle/bool_function.cocci b/tools/coccinelle/bool_function.cocci new file mode 100644 index 0000000000..0328ecfbbe --- /dev/null +++ b/tools/coccinelle/bool_function.cocci @@ -0,0 +1,21 @@ +@@ +identifier fn; +typedef bool; +symbol false; +symbol true; +@@ + +bool fn ( ... ) +{ +<... +return +( +- 0 ++ false +| +- 1 ++ true +) + ; +...> +} diff --git a/tools/coccinelle/bool_function_type.cocci b/tools/coccinelle/bool_function_type.cocci new file mode 100644 index 0000000000..71bf4f53b8 --- /dev/null +++ b/tools/coccinelle/bool_function_type.cocci @@ -0,0 +1,19 @@ +@@ +identifier fn; +typedef bool; +symbol false; +symbol true; +@@ + +- int ++ bool +fn (...) +{ +?... +return +( + true +| + false +); +} diff --git a/tools/coccinelle/cast_to_larger_sizes.cocci b/tools/coccinelle/cast_to_larger_sizes.cocci new file mode 100644 index 0000000000..d97e1f9c33 --- /dev/null +++ b/tools/coccinelle/cast_to_larger_sizes.cocci @@ -0,0 +1,20 @@ +// spatch -sp_file tools/coccinelle/cast_to_larger_sizes.cocci --recursive-includes ./ + +@r@ +typedef uint8_t; +typedef uint16_t; +typedef uint32_t; +typedef uint64_t; +uint8_t *i8; +position p; +@@ + + \( + (uint64_t *) i8@p\|(uint32_t *) i8@p\|(uint16_t *) i8@p + \) + +@script:python@ +p << r.p; +@@ + +coccilib.report.print_report(p[0],"Bad typecast to larger size") diff --git a/tools/coccinelle/ctype_cast.cocci b/tools/coccinelle/ctype_cast.cocci new file mode 100644 index 0000000000..b0b00959da --- /dev/null +++ b/tools/coccinelle/ctype_cast.cocci @@ -0,0 +1,11 @@ + +@@ +identifier func =~ "^(to|is)(alnum|cntrl|print|xdigit|alpha|digit|punct|ascii|graph|space|blank|lower|upper)$"; +expression e; +@@ + + func( +- (int) ++ (unsigned char) + e) + diff --git a/tools/coccinelle/frr_with_mutex.cocci b/tools/coccinelle/frr_with_mutex.cocci new file mode 100644 index 0000000000..ec8b73917c --- /dev/null +++ b/tools/coccinelle/frr_with_mutex.cocci @@ -0,0 +1,23 @@ +@@ +expression E; +iterator name frr_with_mutex; +@@ + +- pthread_mutex_lock(E); ++ frr_with_mutex(E) { +- { + ... +- } +- pthread_mutex_unlock(E); ++ } + + +@@ +expression E; +@@ + +- pthread_mutex_lock(E); ++ frr_with_mutex(E) { + ... +- pthread_mutex_unlock(E); ++ } diff --git a/tools/coccinelle/hash_const.cocci b/tools/coccinelle/hash_const.cocci new file mode 100644 index 0000000000..9c53cb01fb --- /dev/null +++ b/tools/coccinelle/hash_const.cocci @@ -0,0 +1,76 @@ +// +// Transition hash key signatures to take their argument as const. +// Does not handle headers or weirdly named hash functions. +// +@noconst disable optional_qualifier@ +identifier A; +identifier func =~ ".*key$|.*key_make$|.*hash_make$|.*hash_keymake$|.*hash_key$|.*hash_key.*"; +@@ + +- func (void *A) ++ func (const void *A) + { ... } + +@ depends on noconst disable optional_qualifier @ +identifier noconst.A; +identifier noconst.func; +identifier b; +type T; +@@ + +func( ... ) { +<... +- T b = A; ++ const T b = A; +...> + } + +@ depends on noconst disable optional_qualifier @ +identifier noconst.A; +identifier noconst.func; +identifier b; +type T; +@@ + +func(...) + { +<... +- T b = (T) A; ++ const T b = A; +...> + } + +@ depends on noconst disable optional_qualifier @ +identifier noconst.A; +identifier noconst.func; +identifier b; +type T; +@@ + +func(...) + { +<... +- T b; ++ const T b; +... + b = A; +...> + } + +@ depends on noconst disable optional_qualifier @ +identifier noconst.A; +identifier noconst.func; +identifier b; +type T; +@@ + +func(...) + { +<... +- T b; ++ const T b; +... +- b = (T) A; ++ b = A; +...> + } diff --git a/tools/coccinelle/int_to_bool_function.cocci b/tools/coccinelle/int_to_bool_function.cocci new file mode 100644 index 0000000000..f86fe70be2 --- /dev/null +++ b/tools/coccinelle/int_to_bool_function.cocci @@ -0,0 +1,24 @@ +@@ +identifier fn; +typedef bool; +symbol false; +symbol true; +identifier I; +struct thread *thread; +@@ + +- int ++ bool +fn (...) +{ +... when strict + when != I = THREAD_ARG(thread); +( +- return 0; ++ return false; +| +- return 1; ++ return true; +) +?... +} diff --git a/tools/coccinelle/nb-cbs.cocci b/tools/coccinelle/nb-cbs.cocci new file mode 100644 index 0000000000..6e4d32ead4 --- /dev/null +++ b/tools/coccinelle/nb-cbs.cocci @@ -0,0 +1,298 @@ +@@ +identifier func =~ ".*_create$"; +identifier event, dnode, resource; +@@ + +int +- func(enum nb_event event, const struct lyd_node *dnode, union nb_resource *resource) ++ func(struct nb_cb_create_args *args) + { +<... +( +- event ++ args->event +| +- dnode ++ args->dnode +| +- resource ++ args->resource +) +...> + } + +@@ +identifier func =~ ".*_modify$"; +identifier event, dnode, resource; +@@ + +int +- func(enum nb_event event, const struct lyd_node *dnode, union nb_resource *resource) ++ func(struct nb_cb_modify_args *args) + { +<... +( +- event ++ args->event +| +- dnode ++ args->dnode +| +- resource ++ args->resource +) +...> + } + +@@ +identifier func =~ ".*_destroy$"; +identifier event, dnode; +@@ + +int +- func(enum nb_event event, const struct lyd_node *dnode) ++ func(struct nb_cb_destroy_args *args) + { +<... +( +- dnode ++ args->dnode +| +- event ++ args->event +) +...> + } + +@@ +identifier func =~ ".*_pre_validate$"; +identifier dnode; +@@ + +int +- func(const struct lyd_node dnode) ++ func(struct nb_cb_pre_validate_args *args) + { +<... +- dnode ++ args->dnode +...> + } + +@@ +identifier func =~ ".*_apply_finish$"; +identifier dnode; +@@ + +void +- func(const struct lyd_node *dnode) ++ func(struct nb_cb_apply_finish_args *args) + { +<... +- dnode ++ args->dnode +...> + } + +@@ +identifier func =~ ".*_get_elem$"; +identifier xpath, list_entry; +@@ + +struct yang_data * +- func(const char *xpath, const void *list_entry) ++ func(struct nb_cb_get_elem_args *args) + { +<... +( +- xpath ++ args->xpath +| +- list_entry ++ args->list_entry +) +...> + } + +@@ +identifier func =~ ".*_get_next$"; +identifier parent_list_entry, list_entry; +@@ + +const void * +- func(const void *parent_list_entry, const void *list_entry) ++ func(struct nb_cb_get_next_args *args) + { +<... +( +- parent_list_entry ++ args->parent_list_entry +| +- list_entry ++ args->list_entry +) +...> + } + +@@ +identifier func =~ ".*_get_keys$"; +identifier list_entry, keys; +@@ + +int +- func(const void *list_entry, struct yang_list_keys *keys) ++ func(struct nb_cb_get_keys_args *args) + { +<... +( +- list_entry ++ args->list_entry +| +- keys ++ args->keys +) +...> + } + +@@ +identifier func =~ ".*_lookup_entry$"; +identifier parent_list_entry, keys; +@@ + +const void * +- func(const void *parent_list_entry, const struct yang_list_keys *keys) ++ func(struct nb_cb_lookup_entry_args *args) + { +<... +( +- parent_list_entry ++ args->parent_list_entry +| +- keys ++ args->keys +) +...> + } + +@@ +identifier func =~ ".*_rpc$"; +identifier xpath, input, output; +@@ + +int +- func(const char *xpath, const struct list *input, struct list *output) ++ func(struct nb_cb_rpc_args *args) + { +<... +( +- xpath ++ args->xpath +| +- input ++ args->input +| +- output ++ args->output +) +...> + } + +@@ +identifier func =~ ".*_create$"; +identifier event, dnode, resource; +@@ + +int +- func(enum nb_event event, const struct lyd_node *dnode, union nb_resource *resource) ++ func(struct nb_cb_create_args *args) +; + +@@ +identifier func =~ ".*_modify$"; +identifier event, dnode, resource; +@@ + +int +- func(enum nb_event event, const struct lyd_node *dnode, union nb_resource *resource) ++ func(struct nb_cb_modify_args *args) +; + +@@ +identifier func =~ ".*_destroy$"; +identifier event, dnode; +@@ + +int +- func(enum nb_event event, const struct lyd_node *dnode) ++ func(struct nb_cb_destroy_args *args) +; + +@@ +identifier func =~ ".*_pre_validate$"; +identifier dnode; +@@ + +int +- func(const struct lyd_node dnode) ++ func(struct nb_cb_pre_validate_args *args) +; + +@@ +identifier func =~ ".*_apply_finish$"; +identifier dnode; +@@ + +void +- func(const struct lyd_node *dnode) ++ func(struct nb_cb_apply_finish_args *args) +; + +@@ +identifier func =~ ".*_get_elem$"; +identifier xpath, list_entry; +@@ + +struct yang_data * +- func(const char *xpath, const void *list_entry) ++ func(struct nb_cb_get_elem_args *args) +; + +@@ +identifier func =~ ".*_get_next$"; +identifier parent_list_entry, list_entry; +@@ + +const void * +- func(const void *parent_list_entry, const void *list_entry) ++ func(struct nb_cb_get_next_args *args) +; + +@@ +identifier func =~ ".*_get_keys$"; +identifier list_entry, keys; +@@ + +int +- func(const void *list_entry, struct yang_list_keys *keys) ++ func(struct nb_cb_get_keys_args *args) +; + +@@ +identifier func =~ ".*_lookup_entry$"; +identifier parent_list_entry, keys; +@@ + +const void * +- func(const void *parent_list_entry, const struct yang_list_keys *keys) ++ func(struct nb_cb_lookup_entry_args *args) +; + +@@ +identifier func =~ ".*_rpc$"; +identifier xpath, input, output; +@@ + +int +- func(const char *xpath, const struct list *input, struct list *output) ++ func(struct nb_cb_rpc_args *args) +; diff --git a/tools/coccinelle/replace_bgp_flag_functions.cocci b/tools/coccinelle/replace_bgp_flag_functions.cocci new file mode 100644 index 0000000000..3064fc0267 --- /dev/null +++ b/tools/coccinelle/replace_bgp_flag_functions.cocci @@ -0,0 +1,14 @@ +@@ +expression e1, e2; +@@ + +( +- bgp_flag_check(e1, e2) ++ CHECK_FLAG(e1->flags, e2) +| +- bgp_flag_set(e1, e2) ++ SET_FLAG(e1->flags, e2) +| +- bgp_flag_unset(e1, e2) ++ UNSET_FLAG(e1->flags, e2) +) diff --git a/tools/coccinelle/return_without_parenthesis.cocci b/tools/coccinelle/return_without_parenthesis.cocci new file mode 100644 index 0000000000..7097e87ddc --- /dev/null +++ b/tools/coccinelle/return_without_parenthesis.cocci @@ -0,0 +1,9 @@ +// Do not apply only for ldpd daemon since it uses the BSD coding style, +// where parentheses on return is expected. + +@@ +constant c; +@@ + +- return (c); ++ return c; diff --git a/tools/coccinelle/s_addr_0_to_INADDR_ANY.cocci b/tools/coccinelle/s_addr_0_to_INADDR_ANY.cocci new file mode 100644 index 0000000000..bd7f4af4f2 --- /dev/null +++ b/tools/coccinelle/s_addr_0_to_INADDR_ANY.cocci @@ -0,0 +1,14 @@ +@@ +expression e; +@@ + +( +- e.s_addr == 0 ++ e.s_addr == INADDR_ANY +| +- e.s_addr != 0 ++ e.s_addr != INADDR_ANY +| +- e.s_addr = 0 ++ e.s_addr = INADDR_ANY +) diff --git a/tools/coccinelle/same_type_casting.cocci b/tools/coccinelle/same_type_casting.cocci new file mode 100644 index 0000000000..58fd7569af --- /dev/null +++ b/tools/coccinelle/same_type_casting.cocci @@ -0,0 +1,7 @@ +@@ +type T; +T *ptr; +@@ + +- (T *)ptr ++ ptr diff --git a/tools/coccinelle/shorthand_operator.cocci b/tools/coccinelle/shorthand_operator.cocci new file mode 100644 index 0000000000..f7019d4040 --- /dev/null +++ b/tools/coccinelle/shorthand_operator.cocci @@ -0,0 +1,12 @@ +@@ +identifier data; +constant x; +@@ + +( +- data = data + x ++ data += x +| +- data = data - x ++ data -= x +) diff --git a/tools/coccinelle/test_after_assert.cocci b/tools/coccinelle/test_after_assert.cocci new file mode 100644 index 0000000000..30596a89c2 --- /dev/null +++ b/tools/coccinelle/test_after_assert.cocci @@ -0,0 +1,7 @@ +@@ +identifier i; +@@ + +assert(i); +- if (!i) +- return ...; diff --git a/tools/coccinelle/void_no_return.cocci b/tools/coccinelle/void_no_return.cocci new file mode 100644 index 0000000000..7da9e73933 --- /dev/null +++ b/tools/coccinelle/void_no_return.cocci @@ -0,0 +1,9 @@ +@@ +identifier f; +expression e; +@@ +void f(...) { + ... +- return + e; +} diff --git a/tools/coccinelle/zprivs.cocci b/tools/coccinelle/zprivs.cocci index 76d13c3f0d..11628a7eae 100644 --- a/tools/coccinelle/zprivs.cocci +++ b/tools/coccinelle/zprivs.cocci @@ -2,12 +2,12 @@ identifier change; identifier end; expression E, f, g; -iterator name frr_elevate_privs; +iterator name frr_with_privs; @@ - if (E.change(ZPRIVS_RAISE)) - f; -+ frr_elevate_privs(&E) { ++ frr_with_privs(&E) { <+... - goto end; + break; @@ -20,7 +20,7 @@ iterator name frr_elevate_privs; @@ identifier change, errno, safe_strerror, exit; expression E, f1, f2, f3, ret, fn; -iterator name frr_elevate_privs; +iterator name frr_with_privs; @@ if (E.change(ZPRIVS_RAISE)) @@ -44,7 +44,7 @@ iterator name frr_elevate_privs; @@ identifier change; expression E, f1, f2, f3, ret; -iterator name frr_elevate_privs; +iterator name frr_with_privs; @@ if (E.change(ZPRIVS_RAISE)) @@ -64,12 +64,12 @@ iterator name frr_elevate_privs; @@ identifier change; expression E, f, g; -iterator name frr_elevate_privs; +iterator name frr_with_privs; @@ - if (E.change(ZPRIVS_RAISE)) - f; -+ frr_elevate_privs(&E) { ++ frr_with_privs(&E) { ... - if (E.change(ZPRIVS_LOWER)) - g; diff --git a/tools/etc/frr/daemons b/tools/etc/frr/daemons index 2abff422c9..f6d512be72 100644 --- a/tools/etc/frr/daemons +++ b/tools/etc/frr/daemons @@ -12,7 +12,7 @@ # When using "vtysh" such a config file is also needed. It should be owned by # group "frrvty" and set to ug=rw,o= though. Check /etc/pam.d/frr, too. # -# The watchfrr and zebra daemons are always started. +# The watchfrr, zebra and staticd daemons are always started. # bgpd=no ospfd=no @@ -29,6 +29,7 @@ sharpd=no pbrd=no bfdd=no fabricd=no +vrrpd=no # # If this option is set the /etc/init.d/frr script automatically loads @@ -53,10 +54,29 @@ pbrd_options=" -A 127.0.0.1" staticd_options="-A 127.0.0.1" bfdd_options=" -A 127.0.0.1" fabricd_options="-A 127.0.0.1" +vrrpd_options=" -A 127.0.0.1" + +# configuration profile +# +#frr_profile="traditional" +#frr_profile="datacenter" + +# +# This is the maximum number of FD's that will be available. +# Upon startup this is read by the control files and ulimit +# is called. Uncomment and use a reasonable value for your +# setup if you are expecting a large number of peers in +# say BGP. +#MAX_FDS=1024 # The list of daemons to watch is automatically generated by the init script. #watchfrr_options="" +# To make watchfrr create/join the specified netns, use the following option: +#watchfrr_options="--netns" +# This only has an effect in /etc/frr//daemons, and you need to +# start FRR with "/usr/lib/frr/frrinit.sh start ". + # for debugging purposes, you can specify a "wrap" command to start instead # of starting the daemon directly, e.g. to use valgrind on ospfd: # ospfd_wrap="/usr/bin/valgrind" diff --git a/tools/etc/frr/support_bundle_commands.conf b/tools/etc/frr/support_bundle_commands.conf index d52824ff07..11f88e7101 100644 --- a/tools/etc/frr/support_bundle_commands.conf +++ b/tools/etc/frr/support_bundle_commands.conf @@ -28,6 +28,7 @@ show bgp ipv6 update-groups advertised-routes show bgp ipv6 update-groups packet-queue show bgp ipv6 update-groups statistics show ip bgp statistics +show bgp martian next-hop show bgp evpn route CMD_LIST_END @@ -37,14 +38,15 @@ PROC_NAME:zebra CMD_LIST_START show zebra show zebra client summary -show ip route - +show ip nht vrf all show route-map show memory -show interface +show interface vrf all show vrf +show zebra fpm stats show error all show work-queues +show debugging hashtable show running-config show thread cpu show thread poll diff --git a/tools/etc/rsyslog.d/45-frr.conf b/tools/etc/rsyslog.d/45-frr.conf index 4612e8beaf..feeeb13f13 100644 --- a/tools/etc/rsyslog.d/45-frr.conf +++ b/tools/etc/rsyslog.d/45-frr.conf @@ -16,6 +16,7 @@ if $programname == 'babeld' or $programname == 'pimd' or $programname == 'ripd' or $programname == 'ripngd' or + $programname == 'vrrpd' or $programname == 'watchfrr' or $programname == 'zebra' then :omfile:$frr_log @@ -33,6 +34,7 @@ if $programname == 'babeld' or $programname == 'pimd' or $programname == 'ripd' or $programname == 'ripngd' or + $programname == 'vrrpd' or $programname == 'watchfrr' or $programname == 'zebra' then stop diff --git a/tools/frr-llvm-cg.c b/tools/frr-llvm-cg.c new file mode 100644 index 0000000000..84a756a376 --- /dev/null +++ b/tools/frr-llvm-cg.c @@ -0,0 +1,649 @@ +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. +// +// In jurisdictions that recognize copyright laws, the author or authors +// of this software dedicate any and all copyright interest in the +// software to the public domain. We make this dedication for the benefit +// of the public at large and to the detriment of our heirs and +// successors. We intend this dedication to be an overt act of +// relinquishment in perpetuity of all present and future rights to this +// software under copyright law. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +// For more information, please refer to + +/* based on example code: https://github.com/sheredom/llvm_bc_parsing_example + * which came under the above (un-)license. does not depend on any FRR + * pieces, so no reason to change the license. + * + * please note that while included in the FRR sources, this tool is in no way + * supported or maintained by the FRR community. it is provided as a + * "convenience"; while it worked at some point (using LLVM 8 / 9), it may + * easily break with a future LLVM version or any other factors. + * + * 2020-05-04, David Lamparter + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +/* if you want to use this without the special FRRouting defines, + * remove the following #define + */ +#define FRR_SPECIFIC + +static void dbgloc_add(struct json_object *jsobj, LLVMValueRef obj) +{ + unsigned file_len = 0; + const char *file = LLVMGetDebugLocFilename(obj, &file_len); + unsigned line = LLVMGetDebugLocLine(obj); + + if (!file) + file = "???", file_len = 3; + else if (file[0] == '.' && file[1] == '/') + file += 2, file_len -= 2; + + json_object_object_add(jsobj, "filename", + json_object_new_string_len(file, file_len)); + json_object_object_add(jsobj, "line", json_object_new_int64(line)); +} + +static struct json_object *js_get_or_make(struct json_object *parent, + const char *key, + struct json_object *(*maker)(void)) +{ + struct json_object *ret; + + ret = json_object_object_get(parent, key); + if (ret) + return ret; + ret = maker(); + json_object_object_add(parent, key, ret); + return ret; +} + +static bool details_fptr_vars = false; +static bool details_fptr_consts = true; + +enum called_fn { + FN_GENERIC = 0, + FN_NONAME, + FN_INSTALL_ELEMENT, + FN_THREAD_ADD, +}; + +static void walk_const_fptrs(struct json_object *js_call, LLVMValueRef value, + const char *prefix, bool *hdr_written) +{ + LLVMTypeRef type; + LLVMValueKind kind; + + if (LLVMIsAGlobalVariable(value)) { + type = LLVMGlobalGetValueType(value); + value = LLVMGetInitializer(value); + } else { + type = LLVMTypeOf(value); + } + + if (LLVMIsAFunction(value)) { + struct json_object *js_fptrs; + + js_fptrs = js_get_or_make(js_call, "funcptrs", + json_object_new_array); + + size_t fn_len; + const char *fn_name = LLVMGetValueName2(value, &fn_len); + + size_t curlen = json_object_array_length(js_fptrs); + struct json_object *jsobj; + const char *s; + + for (size_t i = 0; i < curlen; i++) { + jsobj = json_object_array_get_idx(js_fptrs, i); + s = json_object_get_string(jsobj); + + if (s && !strcmp(s, fn_name)) + return; + } + + if (details_fptr_consts && !*hdr_written) { + fprintf(stderr, + "%s: calls function pointer from constant or global data\n", + prefix); + *hdr_written = true; + } + if (details_fptr_consts) + fprintf(stderr, "%s- constant: %.*s()\n", + prefix, (int)fn_len, fn_name); + + json_object_array_add(js_fptrs, + json_object_new_string_len(fn_name, + fn_len)); + return; + } + + kind = LLVMGetValueKind(value); + + unsigned len; + char *dump; + + switch (kind) { + case LLVMUndefValueValueKind: + case LLVMConstantAggregateZeroValueKind: + case LLVMConstantPointerNullValueKind: + /* null pointer / array - ignore */ + break; + + case LLVMConstantIntValueKind: + /* integer - ignore */ + break; + + case LLVMConstantStructValueKind: + len = LLVMCountStructElementTypes(type); + for (unsigned i = 0; i < len; i++) + walk_const_fptrs(js_call, LLVMGetOperand(value, i), + prefix, hdr_written); + break; + + case LLVMConstantArrayValueKind: + len = LLVMGetArrayLength(type); + for (unsigned i = 0; i < len; i++) + walk_const_fptrs(js_call, LLVMGetOperand(value, i), + prefix, hdr_written); + return; + + default: + /* to help the user / development */ + if (!*hdr_written) { + fprintf(stderr, + "%s: calls function pointer from constant or global data\n", + prefix); + *hdr_written = true; + } + dump = LLVMPrintValueToString(value); + fprintf(stderr, + "%s- value could not be processed:\n" + "%s- [kind=%d] %s\n", + prefix, prefix, kind, dump); + LLVMDisposeMessage(dump); + return; + } + return; +} + +#ifdef FRR_SPECIFIC +static bool is_thread_sched(const char *name, size_t len) +{ +#define thread_prefix "funcname_" + static const char *const names[] = { + thread_prefix "thread_add_read_write", + thread_prefix "thread_add_timer", + thread_prefix "thread_add_timer_msec", + thread_prefix "thread_add_timer_tv", + thread_prefix "thread_add_event", + thread_prefix "thread_execute", + }; + size_t i; + + for (i = 0; i < sizeof(names) / sizeof(names[0]); i++) { + if (strlen(names[i]) != len) + continue; + if (!memcmp(names[i], name, len)) + return true; + } + return false; +} +#endif + +static void process_call(struct json_object *js_calls, + struct json_object *js_special, + LLVMValueRef instr, + LLVMValueRef function) +{ + struct json_object *js_call, *js_fptrs = NULL; + + LLVMValueRef called = LLVMGetCalledValue(instr); + + if (LLVMIsAConstantExpr(called)) { + LLVMOpcode opcode = LLVMGetConstOpcode(called); + + if (opcode == LLVMBitCast) { + LLVMValueRef op0 = LLVMGetOperand(called, 0); + + if (LLVMIsAFunction(op0)) + called = op0; + } + } + + size_t called_len = 0; + const char *called_name = LLVMGetValueName2(called, &called_len); + unsigned n_args = LLVMGetNumArgOperands(instr); + + bool is_external = LLVMIsDeclaration(called); + enum called_fn called_type = FN_GENERIC; + + js_call = json_object_new_object(); + json_object_array_add(js_calls, js_call); + dbgloc_add(js_call, instr); + json_object_object_add(js_call, "is_external", + json_object_new_boolean(is_external)); + + if (!called_name || called_len == 0) { + called_type = FN_NONAME; + json_object_object_add(js_call, "type", + json_object_new_string("indirect")); + + LLVMValueRef last = called; + + size_t name_len = 0; + const char *name_c = LLVMGetValueName2(function, &name_len); + +#ifdef FRR_SPECIFIC + /* information for FRR hooks is dumped for the registration + * in _hook_typecheck; we can safely ignore the funcptr here + */ + if (strncmp(name_c, "hook_call_", 10) == 0) + return; +#endif + + unsigned file_len = 0; + const char *file = LLVMGetDebugLocFilename(instr, &file_len); + unsigned line = LLVMGetDebugLocLine(instr); + + char prefix[256]; + snprintf(prefix, sizeof(prefix), "%.*s:%d:%.*s()", + (int)file_len, file, line, (int)name_len, name_c); + + while (LLVMIsALoadInst(last) || LLVMIsAGetElementPtrInst(last)) + /* skipping over details for GEP here, but meh. */ + last = LLVMGetOperand(last, 0); + + if (LLVMIsAAllocaInst(last)) { + /* "alloca" is just generically all variables on the + * stack, this does not refer to C alloca() calls + * + * looking at the control flow in the function can + * give better results here, it's just not implemented + * (yet?) + */ + fprintf(stderr, + "%s: call to a function pointer variable\n", + prefix); + + if (details_fptr_vars) { + char *dump = LLVMPrintValueToString(called); + printf("%s- %s\n", prefix, dump); + LLVMDisposeMessage(dump); + } + + json_object_object_add( + js_call, "type", + json_object_new_string("stack_fptr")); + } else if (LLVMIsACallInst(last)) { + /* calling the a function pointer returned from + * another function. + */ + struct json_object *js_indirect; + + js_indirect = js_get_or_make(js_call, "return_of", + json_object_new_array); + + process_call(js_indirect, js_special, last, function); + } else if (LLVMIsAConstant(last)) { + /* function pointer is a constant (includes loading + * from complicated constants like structs or arrays.) + */ + bool hdr_written = false; + walk_const_fptrs(js_call, last, prefix, &hdr_written); + if (details_fptr_consts && !hdr_written) + fprintf(stderr, + "%s: calls function pointer from constant or global data, but no non-NULL function pointers found\n", + prefix); + } else { + char *dump = LLVMPrintValueToString(called); + printf("\t%s\n", dump); + LLVMDisposeMessage(dump); + } + return; +#ifdef FRR_SPECIFIC + } else if (!strcmp(called_name, "install_element")) { + called_type = FN_INSTALL_ELEMENT; + + LLVMValueRef param0 = LLVMGetOperand(instr, 0); + if (!LLVMIsAConstantInt(param0)) + goto out_nonconst; + + long long vty_node = LLVMConstIntGetSExtValue(param0); + json_object_object_add(js_call, "vty_node", + json_object_new_int64(vty_node)); + + LLVMValueRef param1 = LLVMGetOperand(instr, 1); + if (!LLVMIsAGlobalVariable(param1)) + goto out_nonconst; + + LLVMValueRef intlz = LLVMGetInitializer(param1); + assert(intlz && LLVMIsConstant(intlz)); + + LLVMValueKind intlzkind = LLVMGetValueKind(intlz); + assert(intlzkind == LLVMConstantStructValueKind); + + LLVMValueRef funcptr = LLVMGetOperand(intlz, 4); + assert(LLVMIsAFunction(funcptr)); + + size_t target_len = 0; + const char *target; + target = LLVMGetValueName2(funcptr, &target_len); + + json_object_object_add( + js_call, "type", + json_object_new_string("install_element")); + json_object_object_add( + js_call, "target", + json_object_new_string_len(target, target_len)); + return; + + out_nonconst: + json_object_object_add( + js_call, "target", + json_object_new_string("install_element")); + return; + } else if (is_thread_sched(called_name, called_len)) { + called_type = FN_THREAD_ADD; + + json_object_object_add(js_call, "type", + json_object_new_string("thread_sched")); + json_object_object_add( + js_call, "subtype", + json_object_new_string_len(called_name, called_len)); + + LLVMValueRef fparam; + if (strstr(called_name, "_read_")) + fparam = LLVMGetOperand(instr, 2); + else + fparam = LLVMGetOperand(instr, 1); + assert(fparam); + + size_t target_len = 0; + const char *target; + target = LLVMGetValueName2(fparam, &target_len); + + json_object_object_add(js_call, "target", + !target_len ? NULL : + json_object_new_string_len(target, target_len)); + if (!LLVMIsAFunction(fparam)) + json_object_object_add(js_call, "target_unresolved", + json_object_new_boolean(true)); + return; + } else if (!strncmp(called_name, "_hook_typecheck_", + strlen("_hook_typecheck_"))) { + struct json_object *js_hook, *js_this; + const char *hook_name; + + hook_name = called_name + strlen("_hook_typecheck_"); + + json_object_object_add(js_call, "type", + json_object_new_string("hook")); + + LLVMValueRef param0 = LLVMGetOperand(instr, 0); + if (!LLVMIsAFunction(param0)) + return; + + size_t target_len = 0; + const char *target; + target = LLVMGetValueName2(param0, &target_len); + + js_hook = js_get_or_make(js_special, "hooks", + json_object_new_object); + js_hook = js_get_or_make(js_hook, hook_name, + json_object_new_array); + + js_this = json_object_new_object(); + json_object_array_add(js_hook, js_this); + + dbgloc_add(js_this, instr); + json_object_object_add( + js_this, "target", + json_object_new_string_len(target, target_len)); + return; + + /* TODO (FRR specifics): + * - workqueues - not sure we can do much there + * - zclient->* ? + */ +#endif /* FRR_SPECIFIC */ + } else { + json_object_object_add( + js_call, "target", + json_object_new_string_len(called_name, called_len)); + } + + for (unsigned argno = 0; argno < n_args; argno++) { + LLVMValueRef param = LLVMGetOperand(instr, argno); + size_t target_len; + const char *target_name; + + if (LLVMIsAFunction(param)) { + js_fptrs = js_get_or_make(js_call, "funcptrs", + json_object_new_array); + + target_name = LLVMGetValueName2(param, &target_len); + + json_object_array_add(js_fptrs, + json_object_new_string_len( + target_name, target_len)); + } + } +} + +static void process_fn(struct json_object *funcs, + struct json_object *js_special, + LLVMValueRef function) +{ + struct json_object *js_func, *js_calls; + + size_t name_len = 0; + const char *name_c = LLVMGetValueName2(function, &name_len); + char *name; + + name = strndup(name_c, name_len); + + js_func = json_object_object_get(funcs, name); + if (js_func) { + unsigned file_len = 0; + const char *file = LLVMGetDebugLocFilename(function, &file_len); + unsigned line = LLVMGetDebugLocLine(function); + + fprintf(stderr, "%.*s:%d:%s(): duplicate definition!\n", + (int)file_len, file, line, name); + free(name); + return; + } + + js_func = json_object_new_object(); + json_object_object_add(funcs, name, js_func); + free(name); + + js_calls = json_object_new_array(); + json_object_object_add(js_func, "calls", js_calls); + + dbgloc_add(js_func, function); + + for (LLVMBasicBlockRef basicBlock = LLVMGetFirstBasicBlock(function); + basicBlock; basicBlock = LLVMGetNextBasicBlock(basicBlock)) { + + for (LLVMValueRef instr = LLVMGetFirstInstruction(basicBlock); + instr; instr = LLVMGetNextInstruction(instr)) { + + if (LLVMIsAIntrinsicInst(instr)) + continue; + + if (LLVMIsACallInst(instr) || LLVMIsAInvokeInst(instr)) + process_call(js_calls, js_special, instr, + function); + } + } +} + +static void help(int retcode) +{ + fprintf(stderr, + "FRR LLVM bitcode to callgraph analyzer\n" + "\n" + "usage: frr-llvm-cg [-q|-v] [-o ] BITCODEINPUT\n" + "\n" + "\t-o FILENAME\twrite JSON output to file instead of stdout\n" + "\t-v\t\tbe more verbose\n" + "\t-q\t\tbe quiet\n" + "\n" + "BITCODEINPUT must be a LLVM binary bitcode file (not text\n" + "representation.) Use - to read from stdin.\n" + "\n" + "Note it may be necessary to build this binary tool against\n" + "the specific LLVM version that created the bitcode file.\n"); + exit(retcode); +} + +int main(int argc, char **argv) +{ + int opt; + const char *out = NULL; + const char *inp = NULL; + char v_or_q = '\0'; + + while ((opt = getopt(argc, argv, "hvqo:")) != -1) { + switch (opt) { + case 'o': + if (out) + help(1); + out = optarg; + break; + case 'v': + if (v_or_q && v_or_q != 'v') + help(1); + details_fptr_vars = true; + details_fptr_consts = true; + v_or_q = 'v'; + break; + case 'q': + if (v_or_q && v_or_q != 'q') + help(1); + details_fptr_vars = false; + details_fptr_consts = false; + v_or_q = 'q'; + break; + case 'h': + help(0); + return 0; + default: + help(1); + } + } + + if (optind != argc - 1) + help(1); + + inp = argv[optind]; + + LLVMMemoryBufferRef memoryBuffer; + char *message; + int ret; + + // check if we are to read our input file from stdin + if (!strcmp(inp, "-")) { + inp = ""; + ret = LLVMCreateMemoryBufferWithSTDIN(&memoryBuffer, &message); + } else { + ret = LLVMCreateMemoryBufferWithContentsOfFile( + inp, &memoryBuffer, &message); + } + + if (ret) { + fprintf(stderr, "failed to open %s: %s\n", inp, message); + free(message); + return 1; + } + + // now create our module using the memorybuffer + LLVMModuleRef module; + if (LLVMParseBitcode2(memoryBuffer, &module)) { + fprintf(stderr, "%s: invalid bitcode\n", inp); + LLVMDisposeMemoryBuffer(memoryBuffer); + return 1; + } + + // done with the memory buffer now, so dispose of it + LLVMDisposeMemoryBuffer(memoryBuffer); + + struct json_object *js_root, *js_funcs, *js_special; + + js_root = json_object_new_object(); + js_funcs = json_object_new_object(); + json_object_object_add(js_root, "functions", js_funcs); + js_special = json_object_new_object(); + json_object_object_add(js_root, "special", js_special); + + // loop through all the functions in the module + for (LLVMValueRef function = LLVMGetFirstFunction(module); function; + function = LLVMGetNextFunction(function)) { + if (LLVMIsDeclaration(function)) + continue; + + process_fn(js_funcs, js_special, function); + } + + if (out) { + char tmpout[strlen(out) + 5]; + + snprintf(tmpout, sizeof(tmpout), "%s.tmp", out); + ret = json_object_to_file_ext(tmpout, js_root, + JSON_C_TO_STRING_PRETTY | + JSON_C_TO_STRING_PRETTY_TAB | + JSON_C_TO_STRING_NOSLASHESCAPE); + if (ret < 0) { + fprintf(stderr, "could not write JSON to file\n"); + return 1; + } + if (rename(tmpout, out)) { + fprintf(stderr, "could not rename JSON output: %s\n", + strerror(errno)); + unlink(tmpout); + return 1; + } + } else { + ret = json_object_to_fd(1, js_root, + JSON_C_TO_STRING_PRETTY | + JSON_C_TO_STRING_PRETTY_TAB | + JSON_C_TO_STRING_NOSLASHESCAPE); + if (ret < 0) { + fprintf(stderr, "could not write JSON to stdout\n"); + return 1; + } + } + + LLVMDisposeModule(module); + + return 0; +} diff --git a/tools/frr-reload.py b/tools/frr-reload.py index ce1db770d2..a3aed7719c 100755 --- a/tools/frr-reload.py +++ b/tools/frr-reload.py @@ -32,7 +32,7 @@ import argparse import copy import logging -import os +import os, os.path import random import re import string @@ -59,9 +59,110 @@ def iteritems(d): log = logging.getLogger(__name__) -class VtyshMarkException(Exception): +class VtyshException(Exception): pass +class Vtysh(object): + def __init__(self, bindir=None, confdir=None, sockdir=None, pathspace=None): + self.bindir = bindir + self.confdir = confdir + self.pathspace = pathspace + self.common_args = [os.path.join(bindir or '', 'vtysh')] + if confdir: + self.common_args.extend(['--config_dir', confdir]) + if sockdir: + self.common_args.extend(['--vty_socket', sockdir]) + if pathspace: + self.common_args.extend(['-N', pathspace]) + + def _call(self, args, stdin=None, stdout=None, stderr=None): + kwargs = {} + if stdin is not None: + kwargs['stdin'] = stdin + if stdout is not None: + kwargs['stdout'] = stdout + if stderr is not None: + kwargs['stderr'] = stderr + return subprocess.Popen(self.common_args + args, **kwargs) + + def _call_cmd(self, command, stdin=None, stdout=None, stderr=None): + if isinstance(command, list): + args = [item for sub in command for item in ['-c', sub]] + else: + args = ['-c', command] + return self._call(args, stdin, stdout, stderr) + + def __call__(self, command): + """ + Call a CLI command (e.g. "show running-config") + + Output text is automatically redirected, decoded and returned. + Multiple commands may be passed as list. + """ + proc = self._call_cmd(command, stdout=subprocess.PIPE) + stdout, stderr = proc.communicate() + if proc.wait() != 0: + raise VtyshException('vtysh returned status %d for command "%s"' + % (proc.returncode, command)) + return stdout.decode('UTF-8') + + def is_config_available(self): + """ + Return False if no frr daemon is running or some other vtysh session is + in 'configuration terminal' mode which will prevent us from making any + configuration changes. + """ + + output = self('configure') + + if 'VTY configuration is locked by other VTY' in output: + log.error("vtysh 'configure' returned\n%s\n" % (output)) + return False + + return True + + def exec_file(self, filename): + child = self._call(['-f', filename]) + if child.wait() != 0: + raise VtyshException('vtysh (exec file) exited with status %d' + % (child.returncode)) + + def mark_file(self, filename, stdin=None): + child = self._call(['-m', '-f', filename], + stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE) + try: + stdout, stderr = child.communicate() + except subprocess.TimeoutExpired: + child.kill() + stdout, stderr = child.communicate() + raise VtyshException('vtysh call timed out!') + + if child.wait() != 0: + raise VtyshException('vtysh (mark file) exited with status %d:\n%s' + % (child.returncode, stderr)) + + return stdout.decode('UTF-8') + + def mark_show_run(self, daemon = None): + cmd = 'show running-config' + if daemon: + cmd += ' %s' % daemon + cmd += ' no-header' + show_run = self._call_cmd(cmd, stdout=subprocess.PIPE) + mark = self._call(['-m', '-f', '-'], stdin=show_run.stdout, stdout=subprocess.PIPE) + + show_run.wait() + stdout, stderr = mark.communicate() + mark.wait() + + if show_run.returncode != 0: + raise VtyshException('vtysh (show running-config) exited with status %d:' + % (show_run.returncode)) + if mark.returncode != 0: + raise VtyshException('vtysh (mark running-config) exited with status %d' + % (mark.returncode)) + + return stdout.decode('UTF-8') class Context(object): @@ -101,6 +202,26 @@ def add_lines(self, lines): for ligne in lines: self.dlines[ligne] = True +def get_normalized_es_id(line): + """ + The es-id or es-sys-mac need to be converted to lower case + """ + sub_strs = ["evpn mh es-id", "evpn mh es-sys-mac"] + for sub_str in sub_strs: + obj = re.match(sub_str + " (?P\S*)", line) + if obj: + line = "%s %s" % (sub_str, obj.group("esi").lower()) + break + return line + +def get_normalized_mac_ip_line(line): + if line.startswith("evpn mh es"): + return get_normalized_es_id(line) + + if not "ipv6 add" in line: + return get_normalized_ipv6_line(line) + + return line class Config(object): @@ -110,9 +231,10 @@ class Config(object): ('router ospf' for example) are our dictionary key. """ - def __init__(self): + def __init__(self, vtysh): self.lines = [] self.contexts = OrderedDict() + self.vtysh = vtysh def load_from_file(self, filename): """ @@ -122,29 +244,51 @@ def load_from_file(self, filename): """ log.info('Loading Config object from file %s', filename) - try: - file_output = subprocess.check_output(['/usr/bin/vtysh', '-m', '-f', filename], - stderr=subprocess.STDOUT) - except subprocess.CalledProcessError as e: - ve = VtyshMarkException(e) - ve.output = e.output - raise ve - - for line in file_output.decode('utf-8').split('\n'): + file_output = self.vtysh.mark_file(filename) + + for line in file_output.split('\n'): line = line.strip() # Compress duplicate whitespaces line = ' '.join(line.split()) if ":" in line: - qv6_line = get_normalized_ipv6_line(line) - self.lines.append(qv6_line) - else: - self.lines.append(line) + line = get_normalized_mac_ip_line(line) + + """ + vrf static routes can be added in two ways. The old way is: + + "ip route x.x.x.x/x y.y.y.y vrf " + + but it's rendered in the configuration as the new way:: + + vrf + ip route x.x.x.x/x y.y.y.y + exit-vrf + + this difference causes frr-reload to not consider them a + match and delete vrf static routes incorrectly. + fix the old way to match new "show running" output so a + proper match is found. + """ + if ( + line.startswith("ip route ") or line.startswith("ipv6 route ") + ) and " vrf " in line: + newline = line.split(" ") + vrf_index = newline.index("vrf") + vrf_ctx = newline[vrf_index] + " " + newline[vrf_index + 1] + del newline[vrf_index : vrf_index + 2] + newline = " ".join(newline) + self.lines.append(vrf_ctx) + self.lines.append(newline) + self.lines.append("exit-vrf") + line = "end" + + self.lines.append(line) self.load_contexts() - def load_from_show_running(self): + def load_from_show_running(self, daemon): """ Read running configuration and slurp it into internal memory The internal representation has been marked appropriately by passing it @@ -152,16 +296,9 @@ def load_from_show_running(self): """ log.info('Loading Config object from vtysh show running') - try: - config_text = subprocess.check_output( - "/usr/bin/vtysh -c 'show run' | /usr/bin/tail -n +4 | /usr/bin/vtysh -m -f -", - shell=True, stderr=subprocess.STDOUT) - except subprocess.CalledProcessError as e: - ve = VtyshMarkException(e) - ve.output = e.output - raise ve - - for line in config_text.decode('utf-8').split('\n'): + config_text = self.vtysh.mark_show_run(daemon) + + for line in config_text.split('\n'): line = line.strip() if (line == 'Building configuration...' or @@ -306,6 +443,23 @@ def save_contexts(self, key, lines): 'null0' in key[0]): key[0] = re.sub(r'\s+null0(\s*$)', ' Null0', key[0]) + """ + Similar to above, but when the static is in a vrf, it turns into a + blackhole nexthop for both null0 and Null0. Fix it accordingly + """ + if lines and key[0].startswith("vrf "): + newlines = [] + for line in lines: + if line.startswith("ip route ") or line.startswith("ipv6 route "): + if "null0" in line: + line = re.sub(r"\s+null0(\s*$)", " blackhole", line) + elif "Null0" in line: + line = re.sub(r"\s+Null0(\s*$)", " blackhole", line) + newlines.append(line) + else: + newlines.append(line) + lines = newlines + if lines: if tuple(key) not in self.contexts: ctx = Context(tuple(key), lines) @@ -393,8 +547,10 @@ def load_contexts(self): # is not the main router bgp block, but enabling multi-instance oneline_ctx_keywords = ("access-list ", "agentx", + "allow-external-route-update", "bgp ", "debug ", + "domainname ", "dump ", "enable ", "frr ", @@ -402,7 +558,8 @@ def load_contexts(self): "ip ", "ipv6 ", "log ", - "mpls", + "mpls lsp", + "mpls label", "no ", "password ", "ptm-enable", @@ -410,7 +567,9 @@ def load_contexts(self): "service ", "table ", "username ", - "zebra ") + "zebra ", + "vrrp autoconfigure", + "evpn mh") for line in self.lines: @@ -420,8 +579,25 @@ def load_contexts(self): if line.startswith('!') or line.startswith('#'): continue + if (len(ctx_keys) == 2 + and ctx_keys[0].startswith('bfd') + and ctx_keys[1].startswith('profile ') + and line == 'end'): + log.debug('LINE %-50s: popping from sub context, %-50s', line, ctx_keys) + + if main_ctx_key: + self.save_contexts(ctx_keys, current_context_lines) + ctx_keys = copy.deepcopy(main_ctx_key) + current_context_lines = [] + continue + # one line contexts - if new_ctx is True and any(line.startswith(keyword) for keyword in oneline_ctx_keywords): + # there is one exception though: ldpd accepts a 'router-id' clause + # as part of its 'mpls ldp' config context. If we are processing + # ldp configuration and encounter a router-id we should NOT switch + # to a new context + if new_ctx is True and any(line.startswith(keyword) for keyword in oneline_ctx_keywords) and not ( + ctx_keys and ctx_keys[0].startswith("mpls ldp") and line.startswith("router-id ")): self.save_contexts(ctx_keys, current_context_lines) # Start a new context @@ -443,6 +619,16 @@ def load_contexts(self): ctx_keys = [] current_context_lines = [] + elif line == "exit" and ctx_keys[0].startswith("rpki"): + self.save_contexts(ctx_keys, current_context_lines) + log.debug("LINE %-50s: exiting old context, %-50s", line, ctx_keys) + + # Start a new context + new_ctx = True + main_ctx_key = [] + ctx_keys = [] + current_context_lines = [] + elif line == "exit-vrf": self.save_contexts(ctx_keys, current_context_lines) current_context_lines.append(line) @@ -464,7 +650,7 @@ def load_contexts(self): current_context_lines = [] log.debug('LINE %-50s: popping from subcontext to ctx%-50s', line, ctx_keys) - elif line == "exit-vni": + elif line in ["exit-vni", "exit-ldp-if"]: if sub_main_ctx_key: self.save_contexts(ctx_keys, current_context_lines) @@ -486,7 +672,10 @@ def load_contexts(self): elif (line.startswith("address-family ") or line.startswith("vnc defaults") or line.startswith("vnc l2-group") or - line.startswith("vnc nve-group")): + line.startswith("vnc nve-group") or + line.startswith("peer") or + line.startswith("key ") or + line.startswith("member pseudowire")): main_ctx_key = [] # Save old context first @@ -495,9 +684,9 @@ def load_contexts(self): main_ctx_key = copy.deepcopy(ctx_keys) log.debug('LINE %-50s: entering sub-context, append to ctx_keys', line) - if line == "address-family ipv6": + if line == "address-family ipv6" and not ctx_keys[0].startswith("mpls ldp"): ctx_keys.append("address-family ipv6 unicast") - elif line == "address-family ipv4": + elif line == "address-family ipv4" and not ctx_keys[0].startswith("mpls ldp"): ctx_keys.append("address-family ipv4 unicast") elif line == "address-family evpn": ctx_keys.append("address-family l2vpn evpn") @@ -515,6 +704,34 @@ def load_contexts(self): sub_main_ctx_key = copy.deepcopy(ctx_keys) log.debug('LINE %-50s: entering sub-sub-context, append to ctx_keys', line) ctx_keys.append(line) + + elif ((line.startswith("interface ") and + len(ctx_keys) == 2 and + ctx_keys[0].startswith('mpls ldp') and + ctx_keys[1].startswith('address-family'))): + + # Save old context first + self.save_contexts(ctx_keys, current_context_lines) + current_context_lines = [] + sub_main_ctx_key = copy.deepcopy(ctx_keys) + log.debug('LINE %-50s: entering sub-sub-context, append to ctx_keys', line) + ctx_keys.append(line) + + elif ( + line.startswith('profile ') + and len(ctx_keys) == 1 + and ctx_keys[0].startswith('bfd') + ): + + # Save old context first + self.save_contexts(ctx_keys, current_context_lines) + current_context_lines = [] + main_ctx_key = copy.deepcopy(ctx_keys) + log.debug( + "LINE %-50s: entering BFD profile sub-context, append to ctx_keys", + line + ) + ctx_keys.append(line) else: # Continuing in an existing context, add non-commented lines to it @@ -525,58 +742,7 @@ def load_contexts(self): self.save_contexts(ctx_keys, current_context_lines) -def line_to_vtysh_conft(ctx_keys, line, delete): - """ - Return the vtysh command for the specified context line - """ - - cmd = [] - cmd.append('vtysh') - cmd.append('-c') - cmd.append('conf t') - - if line: - for ctx_key in ctx_keys: - cmd.append('-c') - cmd.append(ctx_key) - - line = line.lstrip() - - if delete: - cmd.append('-c') - - if line.startswith('no '): - cmd.append('%s' % line[3:]) - else: - cmd.append('no %s' % line) - - else: - cmd.append('-c') - cmd.append(line) - - # If line is None then we are typically deleting an entire - # context ('no router ospf' for example) - else: - - if delete: - - # Only put the 'no' on the last sub-context - for ctx_key in ctx_keys: - cmd.append('-c') - - if ctx_key == ctx_keys[-1]: - cmd.append('no %s' % ctx_key) - else: - cmd.append('%s' % ctx_key) - else: - for ctx_key in ctx_keys: - cmd.append('-c') - cmd.append(ctx_key) - - return cmd - - -def line_for_vtysh_file(ctx_keys, line, delete): +def lines_to_config(ctx_keys, line, delete): """ Return the command as it would appear in frr.conf """ @@ -589,6 +755,10 @@ def line_for_vtysh_file(ctx_keys, line, delete): line = line.lstrip() indent = len(ctx_keys) * ' ' + # There are some commands that are on by default so their "no" form will be + # displayed in the config. "no bgp default ipv4-unicast" is one of these. + # If we need to remove this line we do so by adding "bgp default ipv4-unicast", + # not by doing a "no no bgp default ipv4-unicast" if delete: if line.startswith('no '): cmd.append('%s%s' % (indent, line[3:])) @@ -601,26 +771,17 @@ def line_for_vtysh_file(ctx_keys, line, delete): # If line is None then we are typically deleting an entire # context ('no router ospf' for example) else: - if delete: - - # Only put the 'no' on the last sub-context - for ctx_key in ctx_keys: + for i, ctx_key in enumerate(ctx_keys[:-1]): + cmd.append('%s%s' % (' ' * i, ctx_key)) - if ctx_key == ctx_keys[-1]: - cmd.append('no %s' % ctx_key) - else: - cmd.append('%s' % ctx_key) + # Only put the 'no' on the last sub-context + if delete: + if ctx_keys[-1].startswith('no '): + cmd.append('%s%s' % (' ' * (len(ctx_keys) - 1), ctx_keys[-1][3:])) + else: + cmd.append('%sno %s' % (' ' * (len(ctx_keys) - 1), ctx_keys[-1])) else: - for ctx_key in ctx_keys: - cmd.append(ctx_key) - - cmd = '\n' + '\n'.join(cmd) - - # There are some commands that are on by default so their "no" form will be - # displayed in the config. "no bgp default ipv4-unicast" is one of these. - # If we need to remove this line we do so by adding "bgp default ipv4-unicast", - # not by doing a "no no bgp default ipv4-unicast" - cmd = cmd.replace('no no ', '') + cmd.append('%s%s' % (' ' * (len(ctx_keys) - 1), ctx_keys[-1])) return cmd @@ -670,6 +831,36 @@ def line_exist(lines, target_ctx_keys, target_line, exact_match=True): return True return False +def check_for_exit_vrf(lines_to_add, lines_to_del): + + # exit-vrf is a bit tricky. If the new config is missing it but we + # have configs under a vrf, we need to add it at the end to do the + # right context changes. If exit-vrf exists in both the running and + # new config, we cannot delete it or it will break context changes. + add_exit_vrf = False + index = 0 + + for (ctx_keys, line) in lines_to_add: + if add_exit_vrf == True: + if ctx_keys[0] != prior_ctx_key: + insert_key=(prior_ctx_key), + lines_to_add.insert(index, ((insert_key, "exit-vrf"))) + add_exit_vrf = False + + if ctx_keys[0].startswith('vrf') and line: + if line is not "exit-vrf": + add_exit_vrf = True + prior_ctx_key = (ctx_keys[0]) + else: + add_exit_vrf = False + index+=1 + + for (ctx_keys, line) in lines_to_del: + if line == "exit-vrf": + if (line_exist(lines_to_add, ctx_keys, line)): + lines_to_del.remove((ctx_keys, line)) + + return (lines_to_add, lines_to_del) def ignore_delete_re_add_lines(lines_to_add, lines_to_del): @@ -752,15 +943,17 @@ def ignore_delete_re_add_lines(lines_to_add, lines_to_del): if re_nbr_bfd_timers: nbr = re_nbr_bfd_timers.group(1) bfd_nbr = "neighbor %s" % nbr + bfd_search_string = bfd_nbr + r' bfd (\S+) (\S+) (\S+)' for (ctx_keys, add_line) in lines_to_add: - re_add_nbr_bfd_timers = re.search(r'neighbor (\S+) bfd (\S+) (\S+) (\S+)', add_line) + if ctx_keys[0].startswith('router bgp'): + re_add_nbr_bfd_timers = re.search(bfd_search_string, add_line) - if re_add_nbr_bfd_timers: - found_add_bfd_nbr = line_exist(lines_to_add, ctx_keys, bfd_nbr, False) + if re_add_nbr_bfd_timers: + found_add_bfd_nbr = line_exist(lines_to_add, ctx_keys, bfd_nbr, False) - if found_add_bfd_nbr: - lines_to_del_to_del.append((ctx_keys, line)) + if found_add_bfd_nbr: + lines_to_del_to_del.append((ctx_keys, line)) ''' We changed how we display the neighbor interface command. Older @@ -909,6 +1102,16 @@ def ignore_delete_re_add_lines(lines_to_add, lines_to_del): lines_to_del_to_del.append((ctx_keys, route_target_export_line)) lines_to_add_to_del.append((ctx_keys, route_target_both_line)) + # Deleting static routes under a vrf can lead to time-outs if each is sent + # as separate vtysh -c commands. Change them from being in lines_to_del and + # put the "no" form in lines_to_add + if ctx_keys[0].startswith('vrf ') and line: + if (line.startswith('ip route') or + line.startswith('ipv6 route')): + add_cmd = ('no ' + line) + lines_to_add.append((ctx_keys, add_cmd)) + lines_to_del_to_del.append((ctx_keys, line)) + if not deleted: found_add_line = line_exist(lines_to_add, ctx_keys, line) @@ -963,6 +1166,7 @@ def ignore_unconfigurable_lines(lines_to_add, lines_to_del): if (ctx_keys[0].startswith('frr version') or ctx_keys[0].startswith('frr defaults') or + ctx_keys[0].startswith('username') or ctx_keys[0].startswith('password') or ctx_keys[0].startswith('line vty') or @@ -971,7 +1175,7 @@ def ignore_unconfigurable_lines(lines_to_add, lines_to_del): # by removing this. ctx_keys[0].startswith('service integrated-vtysh-config')): - log.info("(%s, %s) cannot be removed" % (pformat(ctx_keys), line)) + log.info('"%s" cannot be removed' % (ctx_keys[-1],)) lines_to_del_to_del.append((ctx_keys, line)) for (ctx_keys, line) in lines_to_del_to_del: @@ -1029,6 +1233,32 @@ def compare_context_objects(newconf, running): for line in running_ctx.lines: lines_to_del.append((running_ctx_keys, line)) + # Some commands can happen at higher counts that make + # doing vtysh -c inefficient (and can time out.) For + # these commands, instead of adding them to lines_to_del, + # add the "no " version to lines_to_add. + elif (running_ctx_keys[0].startswith('ip route') or + running_ctx_keys[0].startswith('ipv6 route') or + running_ctx_keys[0].startswith('access-list') or + running_ctx_keys[0].startswith('ipv6 access-list') or + running_ctx_keys[0].startswith('ip prefix-list') or + running_ctx_keys[0].startswith('ipv6 prefix-list')): + add_cmd = ('no ' + running_ctx_keys[0],) + lines_to_add.append((add_cmd, None)) + + # if this an interface sub-subcontext in an address-family block in ldpd and + # we are already deleting the whole context, then ignore this + elif (len(running_ctx_keys) > 2 and running_ctx_keys[0].startswith('mpls ldp') and + running_ctx_keys[1].startswith('address-family') and + (running_ctx_keys[:2], None) in lines_to_del): + continue + + # same thing for a pseudowire sub-context inside an l2vpn context + elif (len(running_ctx_keys) > 1 and running_ctx_keys[0].startswith('l2vpn') and + running_ctx_keys[1].startswith('member pseudowire') and + (running_ctx_keys[:1], None) in lines_to_del): + continue + # Non-global context elif running_ctx_keys and not any("address-family" in key for key in running_ctx_keys): lines_to_del.append((running_ctx_keys, None)) @@ -1064,38 +1294,13 @@ def compare_context_objects(newconf, running): for line in newconf_ctx.lines: lines_to_add.append((newconf_ctx_keys, line)) + (lines_to_add, lines_to_del) = check_for_exit_vrf(lines_to_add, lines_to_del) (lines_to_add, lines_to_del) = ignore_delete_re_add_lines(lines_to_add, lines_to_del) (lines_to_add, lines_to_del) = ignore_unconfigurable_lines(lines_to_add, lines_to_del) return (lines_to_add, lines_to_del) - -def vtysh_config_available(): - """ - Return False if no frr daemon is running or some other vtysh session is - in 'configuration terminal' mode which will prevent us from making any - configuration changes. - """ - - try: - cmd = ['/usr/bin/vtysh', '-c', 'conf t'] - output = subprocess.check_output(cmd, stderr=subprocess.STDOUT).strip() - - if 'VTY configuration is locked by other VTY' in output.decode('utf-8'): - print(output) - log.error("'%s' returned\n%s\n" % (' '.join(cmd), output)) - return False - - except subprocess.CalledProcessError as e: - msg = "vtysh could not connect with any frr daemons" - print(msg) - log.error(msg) - return False - - return True - - if __name__ == '__main__': # Command line options parser = argparse.ArgumentParser(description='Dynamically apply diff in frr configs') @@ -1103,18 +1308,28 @@ def vtysh_config_available(): group = parser.add_mutually_exclusive_group(required=True) group.add_argument('--reload', action='store_true', help='Apply the deltas', default=False) group.add_argument('--test', action='store_true', help='Show the deltas', default=False) - parser.add_argument('--debug', action='store_true', help='Enable debugs', default=False) + level_group = parser.add_mutually_exclusive_group() + level_group.add_argument('--debug', action='store_true', + help='Enable debugs (synonym for --log-level=debug)', default=False) + level_group.add_argument('--log-level', help='Log level', default="info", + choices=("critical", "error", "warning", "info", "debug")) parser.add_argument('--stdout', action='store_true', help='Log to STDOUT', default=False) + parser.add_argument('--pathspace', '-N', metavar='NAME', help='Reload specified path/namespace', default=None) parser.add_argument('filename', help='Location of new frr config file') parser.add_argument('--overwrite', action='store_true', help='Overwrite frr.conf with running config output', default=False) + parser.add_argument('--bindir', help='path to the vtysh executable', default='/usr/bin') + parser.add_argument('--confdir', help='path to the daemon config files', default='/etc/frr') + parser.add_argument('--rundir', help='path for the temp config file', default='/var/run/frr') + parser.add_argument('--vty_socket', help='socket to be used by vtysh to connect to the daemons', default=None) + parser.add_argument('--daemon', help='daemon for which want to replace the config', default='') + args = parser.parse_args() # Logging # For --test log to stdout # For --reload log to /var/log/frr/frr-reload.log if args.test or args.stdout: - logging.basicConfig(level=logging.INFO, - format='%(asctime)s %(levelname)5s: %(message)s') + logging.basicConfig(format='%(asctime)s %(levelname)5s: %(message)s') # Color the errors and warnings in red logging.addLevelName(logging.ERROR, "\033[91m %s\033[0m" % logging.getLevelName(logging.ERROR)) @@ -1125,7 +1340,6 @@ def vtysh_config_available(): os.makedirs('/var/log/frr/') logging.basicConfig(filename='/var/log/frr/frr-reload.log', - level=logging.INFO, format='%(asctime)s %(levelname)5s: %(message)s') # argparse should prevent this from happening but just to be safe... @@ -1133,21 +1347,58 @@ def vtysh_config_available(): raise Exception('Must specify --reload or --test') log = logging.getLogger(__name__) + if args.debug: + log.setLevel(logging.DEBUG) + else: + log.setLevel(args.log_level.upper()) + + if args.reload and not args.stdout: + # Additionally send errors and above to STDOUT, with no metadata, + # when we are logging to a file. This specifically does not follow + # args.log_level, and is analagous to behaviour in earlier versions + # which additionally logged most errors using print(). + + stdout_hdlr = logging.StreamHandler(sys.stdout) + stdout_hdlr.setLevel(logging.ERROR) + stdout_hdlr.setFormatter(logging.Formatter()) + log.addHandler(stdout_hdlr) + # Verify the new config file is valid if not os.path.isfile(args.filename): - msg = "Filename %s does not exist" % args.filename - print(msg) - log.error(msg) + log.error("Filename %s does not exist" % args.filename) sys.exit(1) if not os.path.getsize(args.filename): - msg = "Filename %s is an empty file" % args.filename - print(msg) - log.error(msg) + log.error("Filename %s is an empty file" % args.filename) + sys.exit(1) + + # Verify that confdir is correct + if not os.path.isdir(args.confdir): + log.error("Confdir %s is not a valid path" % args.confdir) + sys.exit(1) + + # Verify that bindir is correct + if not os.path.isdir(args.bindir) or not os.path.isfile(args.bindir + '/vtysh'): + log.error("Bindir %s is not a valid path to vtysh" % args.bindir) + sys.exit(1) + + # verify that the vty_socket, if specified, is valid + if args.vty_socket and not os.path.isdir(args.vty_socket): + log.error('vty_socket %s is not a valid path' % args.vty_socket) + sys.exit(1) + + # verify that the daemon, if specified, is valid + if args.daemon and args.daemon not in ['zebra', 'bgpd', 'fabricd', 'isisd', 'ospf6d', 'ospfd', 'pbrd', 'pimd', 'ripd', 'ripngd', 'sharpd', 'staticd', 'vrrpd', 'ldpd', 'bfdd']: + log.error("Daemon %s is not a valid option for 'show running-config'" % args.daemon) sys.exit(1) + vtysh = Vtysh(args.bindir, args.confdir, args.vty_socket, args.pathspace) + # Verify that 'service integrated-vtysh-config' is configured - vtysh_filename = '/etc/frr/vtysh.conf' + if args.pathspace: + vtysh_filename = args.confdir + '/' + args.pathspace + '/vtysh.conf' + else: + vtysh_filename = args.confdir + '/vtysh.conf' service_integrated_vtysh_config = True if os.path.isfile(vtysh_filename): @@ -1159,31 +1410,30 @@ def vtysh_config_available(): service_integrated_vtysh_config = False break - if not service_integrated_vtysh_config: - msg = "'service integrated-vtysh-config' is not configured, this is required for 'service frr reload'" - print(msg) - log.error(msg) + if not service_integrated_vtysh_config and not args.daemon: + log.error("'service integrated-vtysh-config' is not configured, this is required for 'service frr reload'") sys.exit(1) - if args.debug: - log.setLevel(logging.DEBUG) - log.info('Called via "%s"', str(args)) # Create a Config object from the config generated by newconf - newconf = Config() - newconf.load_from_file(args.filename) - reload_ok = True + newconf = Config(vtysh) + try: + newconf.load_from_file(args.filename) + reload_ok = True + except VtyshException as ve: + log.error("vtysh failed to process new configuration: {}".format(ve)) + reload_ok = False if args.test: # Create a Config object from the running config - running = Config() + running = Config(vtysh) if args.input: running.load_from_file(args.input) else: - running.load_from_show_running() + running.load_from_show_running(args.daemon) (lines_to_add, lines_to_del) = compare_context_objects(newconf, running) lines_to_configure = [] @@ -1197,7 +1447,7 @@ def vtysh_config_available(): if line == '!': continue - cmd = line_for_vtysh_file(ctx_keys, line, True) + cmd = '\n'.join(lines_to_config(ctx_keys, line, True)) lines_to_configure.append(cmd) print(cmd) @@ -1210,14 +1460,14 @@ def vtysh_config_available(): if line == '!': continue - cmd = line_for_vtysh_file(ctx_keys, line, False) + cmd = '\n'.join(lines_to_config(ctx_keys, line, False)) lines_to_configure.append(cmd) print(cmd) elif args.reload: # We will not be able to do anything, go ahead and exit(1) - if not vtysh_config_available(): + if not vtysh.is_config_available(): sys.exit(1) log.debug('New Frr Config\n%s', newconf.get_lines()) @@ -1260,8 +1510,8 @@ def vtysh_config_available(): lines_to_add_first_pass = [] for x in range(2): - running = Config() - running.load_from_show_running() + running = Config(vtysh) + running.load_from_show_running(args.daemon) log.debug('Running Frr Config (Pass #%d)\n%s', x, running.get_lines()) (lines_to_add, lines_to_del) = compare_context_objects(newconf, running) @@ -1293,7 +1543,7 @@ def vtysh_config_available(): # 'no' commands are tricky, we can't just put them in a file and # vtysh -f that file. See the next comment for an explanation # of their quirks - cmd = line_to_vtysh_conft(ctx_keys, line, True) + cmd = lines_to_config(ctx_keys, line, True) original_cmd = cmd # Some commands in frr are picky about taking a "no" of the entire line. @@ -1312,9 +1562,9 @@ def vtysh_config_available(): while True: try: - _ = subprocess.check_output(cmd, stderr=subprocess.STDOUT) + vtysh(['configure'] + cmd) - except subprocess.CalledProcessError: + except VtyshException: # - Pull the last entry from cmd (this would be # 'no ip ospf authentication message-digest 1.1.1.1' in @@ -1324,7 +1574,7 @@ def vtysh_config_available(): last_arg = cmd[-1].split(' ') if len(last_arg) <= 2: - log.error('"%s" we failed to remove this command', original_cmd) + log.error('"%s" we failed to remove this command', ' -- '.join(original_cmd)) break new_last_arg = last_arg[0:-1] @@ -1341,7 +1591,12 @@ def vtysh_config_available(): if line == '!': continue - cmd = line_for_vtysh_file(ctx_keys, line, False) + # Don't run "no" commands twice since they can error + # out the second time due to first deletion + if x == 1 and ctx_keys[0].startswith('no '): + continue + + cmd = '\n'.join(lines_to_config(ctx_keys, line, False)) + '\n' lines_to_configure.append(cmd) if lines_to_configure: @@ -1349,7 +1604,7 @@ def vtysh_config_available(): string.ascii_uppercase + string.digits) for _ in range(6)) - filename = "/var/run/frr/reload-%s.txt" % random_string + filename = args.rundir + "/reload-%s.txt" % random_string log.info("%s content\n%s" % (filename, pformat(lines_to_configure))) with open(filename, 'w') as fh: @@ -1357,15 +1612,16 @@ def vtysh_config_available(): fh.write(line + '\n') try: - subprocess.check_output(['/usr/bin/vtysh', '-f', filename], stderr=subprocess.STDOUT) - except subprocess.CalledProcessError as e: - log.warning("frr-reload.py failed due to\n%s" % e.output) + vtysh.exec_file(filename) + except VtyshException as e: + log.warning("frr-reload.py failed due to\n%s" % e.args) reload_ok = False os.unlink(filename) # Make these changes persistent - if args.overwrite or args.filename != '/etc/frr/frr.conf': - subprocess.call(['/usr/bin/vtysh', '-c', 'write']) + target = str(args.confdir + '/frr.conf') + if args.overwrite or (not args.daemon and args.filename != target): + vtysh('write') if not reload_ok: sys.exit(1) diff --git a/tools/frr.in b/tools/frr.in index 2e3a094589..b860797d5b 100755 --- a/tools/frr.in +++ b/tools/frr.in @@ -21,11 +21,13 @@ VTYSH="@vtysh_bin@" # /usr/bin/vtysh FRR_USER="@enable_user@" # frr FRR_GROUP="@enable_group@" # frr FRR_VTY_GROUP="@enable_vty_group@" # frrvty +FRR_CONFIG_MODE="@enable_configfile_mask@" # 0600 +FRR_DEFAULT_PROFILE="@DFLT_NAME@" # traditional / datacenter # Local Daemon selection may be done by using /etc/frr/daemons. # See /usr/share/doc/frr/README.Debian.gz for further information. # Keep zebra first and do not list watchfrr! -DAEMONS="zebra bgpd ripd ripngd ospfd ospf6d isisd babeld pimd ldpd nhrpd eigrpd sharpd pbrd staticd bfdd fabricd" +DAEMONS="zebra bgpd ripd ripngd ospfd ospf6d isisd babeld pimd ldpd nhrpd eigrpd sharpd pbrd staticd bfdd fabricd vrrpd" MAX_INSTANCES=5 RELOAD_SCRIPT="$D_PATH/frr-reload.py" @@ -55,6 +57,7 @@ chownfrr() { test -n "$FRR_USER" && chown "$FRR_USER" "$1" test -n "$FRR_GROUP" && chgrp "$FRR_GROUP" "$1" + test -n "$FRR_CONFIG_MODE" && chmod "$FRR_CONFIG_MODE" "$1" } # Check if daemon is started by using the pidfile. @@ -74,7 +77,7 @@ vtysh_b () { # Rember, that all variables have been incremented by 1 in convert_daemon_prios() if [ "$vtysh_enable" = 2 -a -f $C_PATH/frr.conf ]; then - $VTYSH -b -n + $VTYSH -b fi } @@ -151,7 +154,7 @@ start() --pidfile=`pidfile $dmn-$inst` \ --exec "$D_PATH/$dmn" \ -- \ - `eval echo "$""$dmn""_options"` -n "$inst" + `eval echo "$""$dmn""_options"` $frr_global_options -n "$inst" else if ! check_daemon $dmn; then echo -n " (binary does not exist)" @@ -164,14 +167,14 @@ start() --pidfile=`pidfile $dmn` \ --exec "$valgrind" \ -- --trace-children=no --leak-check=full --log-file=/var/log/frr/$dmn-valgrind.log $D_PATH/$dmn \ - `eval echo "$""$dmn""_options"` + `eval echo "$""$dmn""_options"` $frr_global_options else ${SSD} \ --start \ --pidfile=`pidfile $dmn` \ --exec "$D_PATH/$dmn" \ -- \ - `eval echo "$""$dmn""_options"` + `eval echo "$""$dmn""_options"` $frr_global_options fi fi @@ -188,7 +191,7 @@ start() --pidfile=`pidfile staticd` \ --exec "$D_PATH/staticd" \ -- \ - `eval echo "$"staticd"_options"` + `eval echo "$"staticd"_options"` $frr_global_options fi } @@ -511,6 +514,18 @@ fi # Read configuration variable file if it is present [ -r /etc/default/frr ] && . /etc/default/frr +if test -z "$frr_profile"; then + # try to autodetect config profile + if test -d /etc/cumulus; then + frr_profile=datacenter + # elif test ...; then + # -- add your distro/system here + elif test -n "$FRR_DEFAULT_PROFILE"; then + frr_profile="$FRR_DEFAULT_PROFILE" + fi +fi +test -n "$frr_profile" && frr_global_options="$frr_global_options -F $frr_profile" + MAX_INSTANCES=${MAX_INSTANCES:=5} # Set priority of un-startable daemons to 'no' and substitute 'yes' to '0' @@ -561,30 +576,7 @@ case "$1" in stop_prio 0 $dmn fi - if [ -z "$dmn" -o "$dmn" = "zebra" ]; then - echo "Removing all routes made by FRR." - # Specific values for each proto can be found - # in /etc/iproute2/rt_protos as well as FRR - # specific ones in /etc/iproute2/rt_protos.d - # Additionally if a new protocol is added - # we need to add it here as well as - # in rt_netlink.h( follow the directions! ) - ip route flush proto 4 - ip route flush proto 11 - ip route flush proto 42 - ip route flush proto 186 - ip route flush proto 187 - ip route flush proto 188 - ip route flush proto 189 - ip route flush proto 190 - ip route flush proto 191 - ip route flush proto 192 - ip route flush proto 193 - ip route flush proto 194 - ip route flush proto 195 - ip route flush proto 196 - ip route flush proto 197 - else + if [ -n "$dmn" -a "$dmn" != "zebra" ]; then [ -n "$dmn" ] && eval "${dmn/-/_}=0" start_watchfrr fi diff --git a/tools/frr.vim b/tools/frr.vim new file mode 100644 index 0000000000..86aa0c0e3f --- /dev/null +++ b/tools/frr.vim @@ -0,0 +1,36 @@ +" settings & syntax hilighting for FRR codebase +" 2019 by David Lamparter, placed in public domain + +let c_gnu=1 + +function! CStyleFRR() + syn clear cFormat + syn match cFormat display "%\(\d\+\$\)\=[-+' #0*]*\(\d*\|\*\|\*\d\+\$\)\(\.\(\d*\|\*\|\*\d\+\$\)\)\=\([hlLjzt]\|ll\|hh\)\=\([aAbiuoxXDOUfFeEgGcCsSn]\|[pd]\([A-Z][A-Z0-9]*[a-z]*\|\)\|\[\^\=.[^]]*\]\)" contained + syn match cFormat display "%%" contained + + syn keyword cIterator frr_each frr_each_safe frr_each_from + syn keyword cMacroOp offsetof container_of container_of_null array_size + + syn keyword cStorageClass atomic + syn keyword cFormatConst PRId64 PRIu64 PRIx64 + syn keyword cFormatConst PRId32 PRIu32 PRIx32 + syn keyword cFormatConst PRId16 PRIu16 PRIx16 + syn keyword cFormatConst PRId8 PRIu8 PRIx8 + + " you can unlink these by just giving them their own hilighting / color + hi link cFormatConst cFormat + hi link cIterator cRepeat + hi link cMacroOp cOperator + + " indentation + setlocal cindent + setlocal cinoptions=:0,(0,u4,w1,W8 + setlocal shiftwidth=8 + setlocal softtabstop=0 + setlocal textwidth=0 + setlocal fo=croql + setlocal noet +endfunction + +" auto-apply the above based on path rules +"autocmd BufRead,BufNewFile /home/.../frr/*.[ch] call CStyleFRR() diff --git a/tools/frr@.service b/tools/frr@.service new file mode 100644 index 0000000000..0fa41c74a3 --- /dev/null +++ b/tools/frr@.service @@ -0,0 +1,25 @@ +[Unit] +Description=FRRouting +Documentation=https://frrouting.readthedocs.io/en/latest/setup.html +Wants=network.target +After=network-pre.target systemd-sysctl.service +Before=network.target +OnFailure=heartbeat-failed@%n.service + +[Service] +Nice=-5 +Type=forking +NotifyAccess=all +StartLimitInterval=3m +StartLimitBurst=3 +TimeoutSec=2m +WatchdogSec=60s +RestartSec=5 +Restart=on-abnormal +LimitNOFILE=1024 +ExecStart=/usr/lib/frr/frrinit.sh start %I +ExecStop=/usr/lib/frr/frrinit.sh stop %I +ExecReload=/usr/lib/frr/frrinit.sh reload %I + +[Install] +WantedBy=multi-user.target diff --git a/tools/frrcommon.sh.in b/tools/frrcommon.sh.in index 897e6d6558..3f7e784ed8 100644 --- a/tools/frrcommon.sh.in +++ b/tools/frrcommon.sh.in @@ -16,20 +16,26 @@ # # This script should be installed in @CFG_SBIN@/frrcommon.sh +# FRR_PATHSPACE is passed in from watchfrr +suffix="${FRR_PATHSPACE:+/${FRR_PATHSPACE}}" +nsopt="${FRR_PATHSPACE:+-N ${FRR_PATHSPACE}}" + PATH=/bin:/usr/bin:/sbin:/usr/sbin D_PATH="@CFG_SBIN@" # /usr/lib/frr -C_PATH="@CFG_SYSCONF@" # /etc/frr -V_PATH="@CFG_STATE@" # /var/run/frr +C_PATH="@CFG_SYSCONF@${suffix}" # /etc/frr +V_PATH="@CFG_STATE@${suffix}" # /var/run/frr VTYSH="@vtysh_bin@" # /usr/bin/vtysh FRR_USER="@enable_user@" # frr FRR_GROUP="@enable_group@" # frr FRR_VTY_GROUP="@enable_vty_group@" # frrvty +FRR_CONFIG_MODE="@enable_configfile_mask@" # 0600 +FRR_DEFAULT_PROFILE="@DFLT_NAME@" # traditional / datacenter # ORDER MATTERS FOR $DAEMONS! # - keep zebra first # - watchfrr does NOT belong in this list -DAEMONS="zebra bgpd ripd ripngd ospfd ospf6d isisd babeld pimd ldpd nhrpd eigrpd sharpd pbrd staticd bfdd fabricd" +DAEMONS="zebra bgpd ripd ripngd ospfd ospf6d isisd babeld pimd ldpd nhrpd eigrpd sharpd pbrd staticd bfdd fabricd vrrpd" RELOAD_SCRIPT="$D_PATH/frr-reload.py" # @@ -52,15 +58,16 @@ debug() { chownfrr() { [ -n "$FRR_USER" ] && chown "$FRR_USER" "$1" [ -n "$FRR_GROUP" ] && chgrp "$FRR_GROUP" "$1" + [ -n "$FRR_CONFIG_MODE" ] && chmod "$FRR_CONFIG_MODE" "$1" } vtysh_b () { [ "$1" = "watchfrr" ] && return 0 [ -r "$C_PATH/frr.conf" ] || return 0 if [ -n "$1" ]; then - "$VTYSH" -b -n -d "$1" + "$VTYSH" `echo $nsopt` -b -d "$1" else - "$VTYSH" -b -n + "$VTYSH" `echo $nsopt` -b fi } @@ -89,7 +96,7 @@ daemon_list() { continue fi debug "$daemon enabled" - enabled="$enabled $daemon" + if [ -n "$inst" ]; then debug "$daemon multi-instance $inst" oldifs="${IFS}" @@ -98,6 +105,8 @@ daemon_list() { enabled="$enabled $daemon-$i" done IFS="${oldifs}" + else + enabled="$enabled $daemon" fi else debug "$daemon disabled" @@ -137,6 +146,10 @@ daemon_prep() { daemon_start() { local dmninst daemon inst args instopt wrap bin + + all=false + [ "$1" = "--all" ] && { all=true; shift; } + daemon_inst "$1" ulimit -n $MAX_FDS > /dev/null 2> /dev/null @@ -151,9 +164,13 @@ daemon_start() { instopt="${inst:+-n $inst}" eval args="\$${daemon}_options" - if eval "$all_wrap $wrap $bin -d $instopt $args"; then + if eval "$all_wrap $wrap $bin $nsopt -d $frr_global_options $instopt $args"; then log_success_msg "Started $dmninst" - vtysh_b "$daemon" + if $all; then + debug "Skipping startup of vtysh until all have started" + else + vtysh_b "$daemon" + fi else log_failure_msg "Failed to start $dmninst!" fi @@ -225,8 +242,9 @@ print_status() { all_start() { daemon_list daemons for dmninst in $daemons; do - daemon_start "$dmninst" + daemon_start --all "$dmninst" done + vtysh_b } all_stop() { @@ -287,9 +305,11 @@ load_old_config() { } . "$C_PATH/daemons" -load_old_config "$C_PATH/daemons.conf" -load_old_config "/etc/default/frr" -load_old_config "/etc/sysconfig/frr" +if [ -z "$FRR_PATHSPACE" ]; then + load_old_config "$C_PATH/daemons.conf" + load_old_config "/etc/default/frr" + load_old_config "/etc/sysconfig/frr" +fi if { declare -p watchfrr_options 2>/dev/null || true; } | grep -q '^declare \-a'; then log_warning_msg "watchfrr_options contains a bash array value." \ @@ -298,6 +318,18 @@ if { declare -p watchfrr_options 2>/dev/null || true; } | grep -q '^declare \-a' unset watchfrr_options fi +if test -z "$frr_profile"; then + # try to autodetect config profile + if test -d /etc/cumulus; then + frr_profile=datacenter + # elif test ...; then + # -- add your distro/system here + elif test -n "$FRR_DEFAULT_PROFILE"; then + frr_profile="$FRR_DEFAULT_PROFILE" + fi +fi +test -n "$frr_profile" && frr_global_options="$frr_global_options -F $frr_profile" + # # other defaults and dispatch # diff --git a/tools/frrinit.sh.in b/tools/frrinit.sh.in index 0f5ed85864..539ab7d816 100644 --- a/tools/frrinit.sh.in +++ b/tools/frrinit.sh.in @@ -30,6 +30,9 @@ else } fi +# "/usr/lib/frr/frrinit.sh start somenamespace" +FRR_PATHSPACE="$2" + self="`dirname $0`" if [ -r "$self/frrcommon.sh" ]; then . "$self/frrcommon.sh" @@ -71,6 +74,16 @@ reload) exit 1 fi + # systemd doesn't set WATCHDOG_USEC for reload commands. + watchfrr_pidfile="$V_PATH/watchfrr.pid" + watchfrr_pid="`cat \"$watchfrr_pidfile\"`" + if [ -d "/proc/$watchfrr_pid" ]; then + wdt="`tr '\0' '\n' < /proc/$watchfrr_pid/environ | grep '^WATCHDOG_USEC='`" + wdt="${wdt#WATCHDOG_USEC=}" + [ -n "$wdt" ] && : ${WATCHDOG_USEC:=$wdt} + [ -n "$WATCHDOG_USEC" ] && export WATCHDOG_USEC + fi + # restart watchfrr to pick up added daemons. # NB: This will NOT cause the other daemons to be restarted. daemon_list daemons @@ -78,9 +91,24 @@ reload) daemon_stop watchfrr && \ daemon_start watchfrr + # make systemd not kill watchfrr after ExecReload completes + # 3 goats were sacrificed to restore sanity after coding this + watchfrr_pid="`cat \"$watchfrr_pidfile\"`" + if [ -f "/proc/$watchfrr_pid/cgroup" -a -d "/sys/fs/cgroup/systemd" ]; then + cg="`egrep '^[0-9]+:name=systemd:' \"/proc/$watchfrr_pid/cgroup\"`" + cg="${cg#*:*:}" + + cgmain="$cg" + cgmain="${cgmain%/.control}" + cgmain="${cgmain%/control}" + + [ -n "$cg" -a "$cg" != "$cgmain" ] && \ + echo "$watchfrr_pid" > "/sys/fs/cgroup/systemd/$cgmain/tasks" + fi + NEW_CONFIG_FILE="${2:-$C_PATH/frr.conf}" [ ! -r $NEW_CONFIG_FILE ] && log_failure_msg "Unable to read new configuration file $NEW_CONFIG_FILE" && exit 1 - "$RELOAD_SCRIPT" --reload "$NEW_CONFIG_FILE" + "$RELOAD_SCRIPT" --reload "$NEW_CONFIG_FILE" `echo $nsopt` exit $? ;; diff --git a/tools/gcc-plugins/.gitignore b/tools/gcc-plugins/.gitignore new file mode 100644 index 0000000000..dd8d0cb7ee --- /dev/null +++ b/tools/gcc-plugins/.gitignore @@ -0,0 +1,7 @@ +*.so +*.o +debian/.debhelper +debian/files +debian/*.substvars +debian/gcc-9-frr-plugin +!gcc-retain-typeinfo.patch diff --git a/tools/gcc-plugins/COPYING.GPLv3 b/tools/gcc-plugins/COPYING.GPLv3 new file mode 100644 index 0000000000..94a9ed024d --- /dev/null +++ b/tools/gcc-plugins/COPYING.GPLv3 @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/tools/gcc-plugins/Makefile b/tools/gcc-plugins/Makefile new file mode 100644 index 0000000000..d6edd745ce --- /dev/null +++ b/tools/gcc-plugins/Makefile @@ -0,0 +1,19 @@ +all: frr-format.so + +CXX=g++-9 + +PLUGBASE=`$(CXX) -print-file-name=plugin` +CPPFLAGS=-I$(PLUGBASE)/include -I$(PLUGBASE)/include/c-family + +frr-format.so: frr-format.o + $(CXX) -g -shared -o $@ $^ + +frr-format.o: frr-format.c gcc-common.h + $(CXX) -g $(CPPFLAGS) -fPIC -Wall -Wextra -Wno-unused-parameter -c -o $@ $< + +install: + install -d $(DESTDIR)$(PLUGBASE) + install frr-format.so $(DESTDIR)$(PLUGBASE) + +clean: + rm -f frr-format.so frr-format.o diff --git a/tools/gcc-plugins/README.md b/tools/gcc-plugins/README.md new file mode 100644 index 0000000000..ab31d0e636 --- /dev/null +++ b/tools/gcc-plugins/README.md @@ -0,0 +1,102 @@ +frr-format GCC plugin +===================== + +Context +------- + +This plugin provides improved type checking for Linux kernel style printf +extensions (i.e. `%pI4` printing `struct in_addr *` as `1.2.3.4`.) + +Other than additional warnings, (non-)usage of this plugin should not affect +the build outcome. It is perfectly fine to build FRR without this plugin. + + +Binary Debian packages +---------------------- + +Can be found at [https://deb.nox.tf/devel/]. + + +GCC requirements +---------------- + +To use this plugin, you need a **patched 9.3.0** or a **patched 10.1.0** +version of GCC using the [gcc-retain-typeinfo.patch] provided in this repo. + +Without this patch, GCC strips type information too early during compilation, +leaving to the plugin being unable to perform more meaningful type checks. +(Specifically, all `typedef` casts will be "cooked down" to their final type.) +If the patch is missing, `format-test.c` will show 4 false negative/positive +warnings marked with `(need retain-typeinfo patch)`. + +(@eqvinox has discussed this one-line diff with some GCC people on their +IRC channel around mid 2019, the consensus was that the line is an "early +optimization" and removing it should not be harmful. However, doing so is +likely to break GCC's unit tests since warnings would print different types.) + +Other versions of gcc are not supported. gcc 8 previously did work but isn't +actively tested/maintained. + + +Usage +----- + +First, all plugin-specific statements should be wrapped by an ifdef: + +``` +#ifdef _FRR_ATTRIBUTE_PRINTFRR +... +#endif +``` + +`_FRR_ATTRIBUTE_PRINTFRR` will be defined to the plugin's version (currently +0x10000) whenever the plugin is loaded. + +Then, annotate extended printf functions with the `frr_format` attribute. +This works exactly like the `format` attribute: + +``` +int printfn(const char *fmt, ...) __attribute__((frr_format("frr_printf", 1, 2))); +``` + +In the FRR codebase, use the `PRINTFRR` macro provided in +[../../lib/compiler.h]. + +Lastly, "declare" extensions with `#pragma FRR printfrr_ext`: +``` +#ifdef _FRR_ATTRIBUTE_PRINTFRR +#pragma FRR printfrr_ext "%pI4" (struct in_addr *) +#pragma FRR printfrr_ext "%pI4" (in_addr_t *) +#endif +``` + +Note that you can use multiple such lines if a particular extended printer +works for more than one type (as seen above.) + +The pragma type "parameter" looks like a C cast but unfortunately due to GCC +not exporting a good interface to proper type parsing, it is "ghetto parsed", +with only `struct`, `union`, `enum` being properly supported. `const` is +ignored if it occurs as the first token. (The plugin always accepts `const` +parameters for printf since printf shouldn't change the passed data it's +printing.) The last token may be zero or more counts of `*`, note that +qualifiers on the intermediate pointers (e.g. `const char * const *`) are not +supported. + + +TODOs and future direction +-------------------------- + +* support two-parameter extension printers that use the precision field + (e.g. `"%.*pI5" (int af, void *addr)` to print an IP address with the + address family in the "precision". + +* port to future GCC versions + +* get the one-liner patch upstreamed + + +License +------- + +This plugin is **derivative of GCC 9.x**. It was created by copying off +`c-format.c`. It must therefore adhere to GCC's GPLv3+ license. diff --git a/tools/gcc-plugins/debian/changelog b/tools/gcc-plugins/debian/changelog new file mode 100644 index 0000000000..a772803b1c --- /dev/null +++ b/tools/gcc-plugins/debian/changelog @@ -0,0 +1,11 @@ +gcc-frr-plugin (9.3.0d15+equi1) unstable; urgency=medium + + * update & rebuild for gcc 9.3.0-15+equi1 + + -- David Lamparter Tue, 14 Jul 2020 19:49:24 +0200 + +gcc-frr-plugin (9.3.0d8+equi2) unstable; urgency=medium + + * package created (+equi1 used during development, never released.) + + -- David Lamparter Sun, 29 Mar 2020 08:32:24 +0200 diff --git a/tools/gcc-plugins/debian/compat b/tools/gcc-plugins/debian/compat new file mode 100644 index 0000000000..48082f72f0 --- /dev/null +++ b/tools/gcc-plugins/debian/compat @@ -0,0 +1 @@ +12 diff --git a/tools/gcc-plugins/debian/control b/tools/gcc-plugins/debian/control new file mode 100644 index 0000000000..b9b5134b46 --- /dev/null +++ b/tools/gcc-plugins/debian/control @@ -0,0 +1,19 @@ +Source: gcc-frr-plugin +Section: devel +Priority: optional +Maintainer: David Lamparter +Build-Depends: + gcc-9-plugin-dev (=9.3.0-15+equi1), + debhelper (>= 12) +Standards-Version: 4.4.1 +Homepage: https://www.frrouting.org/ +Vcs-Browser: https://github.com/FRRouting/frr/ +Vcs-Git: https://github.com/FRRouting/frr.git + +Package: gcc-9-frr-plugin +Architecture: linux-any +Depends: + gcc-9 (=9.3.0-15+equi1), + ${misc:Depends}, + ${shlibs:Depends} +Description: GCC plugin for FRRouting diff --git a/tools/gcc-plugins/debian/copyright b/tools/gcc-plugins/debian/copyright new file mode 100644 index 0000000000..dcd9fa1770 --- /dev/null +++ b/tools/gcc-plugins/debian/copyright @@ -0,0 +1,9 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: FRR +Upstream-Contact: maintainers@frrouting.org, security@frrouting.org +Source: https://www.frrouting.org/ + +Files: * +Copyright: + 2019-2020 by David Lamparter + Code derived from GCC, please refer to gcc package copyright. diff --git a/tools/gcc-plugins/debian/rules b/tools/gcc-plugins/debian/rules new file mode 100755 index 0000000000..f8f42ad337 --- /dev/null +++ b/tools/gcc-plugins/debian/rules @@ -0,0 +1,11 @@ +#!/usr/bin/make -f + +# standard Debian options & profiles + +export DEB_BUILD_MAINT_OPTIONS = hardening=+all + +%: + dh $@ + +override_dh_auto_test: + true diff --git a/tools/gcc-plugins/debian/source/format b/tools/gcc-plugins/debian/source/format new file mode 100644 index 0000000000..89ae9db8f8 --- /dev/null +++ b/tools/gcc-plugins/debian/source/format @@ -0,0 +1 @@ +3.0 (native) diff --git a/tools/gcc-plugins/format-test.c b/tools/gcc-plugins/format-test.c new file mode 100644 index 0000000000..fb7e41c7be --- /dev/null +++ b/tools/gcc-plugins/format-test.c @@ -0,0 +1,113 @@ +#include +#include +#include +#include +#include + +typedef unsigned long mytype; +typedef size_t mysize; + +typedef unsigned int not_in_addr_t; +typedef in_addr_t yes_in_addr_t; +typedef struct in_addr in_addr_s; + +struct other { + int x; +}; + +int testfn(const char *fmt, ...) __attribute__((frr_format("frr_printf", 1, 2))); + +#ifndef _FRR_ATTRIBUTE_PRINTFRR +#error please load the frr-format plugin +#endif + +#pragma FRR printfrr_ext "%pI4" (struct in_addr *) +#pragma FRR printfrr_ext "%pI4" (in_addr_t *) + +int test(unsigned long long ay) +{ + size_t v_size_t = 0; + long v_long = 0; + int v_int = 0; + uint64_t v_uint64_t = 0; + mytype v_mytype = 0; + mysize v_mysize = 0; + pid_t v_pid_t = 0; + + testfn("%zu", v_size_t); // NOWARN + testfn("%zu", v_long); // WARN + testfn("%zu", v_int); // WARN + testfn("%zu", sizeof(v_int)); // NOWARN + testfn("%zu", v_mytype); // WARN + testfn("%zu", v_mysize); // NOWARN + testfn("%zu", v_uint64_t); // WARN + testfn("%zu", v_pid_t); // WARN + + testfn("%lu", v_long); // NOWARN PEDANTIC + testfn("%lu", v_int); // WARN + testfn("%lu", v_size_t); // WARN + testfn("%lu", sizeof(v_int)); // NOWARN (integer constant) + testfn("%lu", v_uint64_t); // WARN + testfn("%lu", v_pid_t); // WARN + + testfn("%ld", v_long); // NOWARN + testfn("%ld", v_int); // WARN + testfn("%ld", v_size_t); // WARN + testfn("%ld", sizeof(v_int)); // NOWARN (integer constant) + testfn("%ld", v_uint64_t); // WARN + testfn("%ld", v_pid_t); // WARN + + testfn("%d", v_int); // NOWARN + testfn("%d", v_long); // WARN + testfn("%d", v_size_t); // WARN + testfn("%d", sizeof(v_int)); // WARN + testfn("%d", v_uint64_t); // WARN + testfn("%d", v_pid_t); // WARN + + testfn("%Lu", v_size_t); // WARN + testfn("%Lu", v_long); // WARN + testfn("%Lu", v_int); // WARN + testfn("%Lu", sizeof(v_int)); // NOWARN (integer constant) + testfn("%Lu", v_mytype); // WARN + testfn("%Lu", v_mysize); // WARN + testfn("%Lu", v_pid_t); // WARN + testfn("%Lu", v_uint64_t); // NOWARN + + testfn("%Ld", v_size_t); // WARN + testfn("%Ld", v_long); // WARN + testfn("%Ld", v_int); // WARN + testfn("%Ld", sizeof(v_int)); // NOWARN (integer constant) + testfn("%Ld", v_mytype); // WARN + testfn("%Ld", v_mysize); // WARN + testfn("%Ld", v_pid_t); // WARN + testfn("%Ld", v_uint64_t); // NOWARN + + /* retain-typeinfo patch */ + testfn("%zu", (size_t)v_pid_t); // NOWARN (need retain-typeinfo patch) + testfn("%lu", (size_t)v_pid_t); // WARN (need retain-typeinfo patch) + testfn("%Lu", (uint64_t)v_pid_t); // NOWARN (need retain-typeinfo patch) + testfn("%lu", (uint64_t)v_pid_t); // WARN (need retain-typeinfo patch) + + testfn("%pI4", &v_long); // WARN + + in_addr_t v_in_addr_t; + yes_in_addr_t v_yes_in_addr_t; + not_in_addr_t v_not_in_addr_t; + void *v_voidp = &v_in_addr_t; + + testfn("%pI4", &v_in_addr_t); // NOWARN + testfn("%pI4", &v_yes_in_addr_t); // NOWARN + testfn("%pI4", &v_not_in_addr_t); // WARN + testfn("%pI4", v_voidp); // WARN + + struct in_addr v_in_addr; + in_addr_s v_in_addr_s; + struct other v_other; + const struct in_addr *v_in_addr_const = &v_in_addr; + + testfn("%pI4", &v_in_addr); // NOWARN + testfn("%pI4", &v_in_addr_s); // NOWARN + testfn("%pI4", &v_other); // WARN + testfn("%pI4", v_in_addr_const); // NOWARN + return 0; +} diff --git a/tools/gcc-plugins/format-test.py b/tools/gcc-plugins/format-test.py new file mode 100644 index 0000000000..df2437d5bc --- /dev/null +++ b/tools/gcc-plugins/format-test.py @@ -0,0 +1,61 @@ +import subprocess +import sys +import shlex +import os +import re + +os.environ['LC_ALL'] = 'C' +os.environ['LANG'] = 'C' +for k in list(os.environ.keys()): + if k.startswith('LC_'): + os.environ.pop(k) + +if len(sys.argv) < 2: + sys.stderr.write('start as format-test.py gcc-123.45 [-options ...]\n') + sys.exit(1) + +c_re = re.compile(r'//\s+(NO)?WARN') +expect = {} +lines = {} + +with open('format-test.c', 'r') as fd: + for lno, line in enumerate(fd.readlines(), 1): + lines[lno] = line.strip() + m = c_re.search(line) + if m is None: + continue + if m.group(1) is None: + expect[lno] = 'warn' + else: + expect[lno] = 'nowarn' + +cmd = shlex.split('-Wall -Wextra -Wno-unused -fplugin=./frr-format.so -fno-diagnostics-show-caret -c -o format-test.o format-test.c') + +gcc = subprocess.Popen(sys.argv[1:] + cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) +sout, serr = gcc.communicate() +gcc.wait() + +gcclines = serr.decode('UTF-8').splitlines() +line_re = re.compile(r'^format-test\.c:(\d+):(.*)$') +gcc_warns = {} + +for line in gcclines: + if line.find('In function') >= 0: + continue + m = line_re.match(line) + if m is None: + sys.stderr.write('cannot process GCC output: %s\n' % line) + continue + + lno = int(m.group(1)) + gcc_warns.setdefault(lno, []).append(line) + +for lno, val in expect.items(): + if val == 'nowarn' and lno in gcc_warns: + sys.stderr.write('unexpected gcc warning on line %d:\n\t%s\n\t%s\n' % (lno, lines[lno], '\n\t'.join(gcc_warns[lno]))) + if val == 'warn' and lno not in gcc_warns: + sys.stderr.write('expected warning on line %d but did not get one\n\t%s\n' % (lno, lines[lno])) + +leftover = set(gcc_warns.keys()) - set(expect.keys()) +for lno in sorted(leftover): + sys.stderr.write('unmarked gcc warning on line %d:\n\t%s\n\t%s\n' % (lno, lines[lno], '\n\t'.join(gcc_warns[lno]))) diff --git a/tools/gcc-plugins/frr-format.c b/tools/gcc-plugins/frr-format.c new file mode 100644 index 0000000000..be56517171 --- /dev/null +++ b/tools/gcc-plugins/frr-format.c @@ -0,0 +1,4447 @@ +/* Check calls to formatted I/O functions (-Wformat). + Copyright (C) 1992-2019 Free Software Foundation, Inc. + + Extended for FRR's printfrr() with Linux kernel style extensions + Copyright (C) 2019-2020 David Lamparter, for NetDEF, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING.GPLv3. If not see +. */ + +#include "gcc-common.h" + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +//include "c-target.h" +#include "c-common.h" +#include "alloc-pool.h" +#include "stringpool.h" +#include "c-tree.h" +#include "c-objc.h" +#include "intl.h" +#include "langhooks.h" +#include "frr-format.h" +#include "diagnostic.h" +#include "substring-locations.h" +#include "selftest.h" +#include "selftest-diagnostic.h" +#ifndef FIRST_PSEUDO_REGISTER +#define FIRST_PSEUDO_REGISTER 0 +#endif +#include "builtins.h" +#include "attribs.h" +#include "gcc-rich-location.h" +#include "c-pretty-print.h" +#include "c-pragma.h" + +extern struct cpp_reader *parse_in; + +#pragma GCC visibility push(hidden) + +/* Handle attributes associated with format checking. */ + +/* This must be in the same order as format_types, except for + format_type_error. Target-specific format types do not have + matching enum values. */ +enum format_type { frr_printf_format_type, + format_type_error = -1}; + +struct function_format_info +{ + int format_type; /* type of format (printf, scanf, etc.) */ + unsigned HOST_WIDE_INT format_num; /* number of format argument */ + unsigned HOST_WIDE_INT first_arg_num; /* number of first arg (zero for varargs) */ +}; + +static GTY(()) tree local_uint64_t_node; +static GTY(()) tree local_int64_t_node; + +static GTY(()) tree local_size_t_node; +static GTY(()) tree local_ssize_t_node; +static GTY(()) tree local_atomic_size_t_node; +static GTY(()) tree local_atomic_ssize_t_node; +static GTY(()) tree local_ptrdiff_t_node; + +static GTY(()) tree local_pid_t_node; +static GTY(()) tree local_uid_t_node; +static GTY(()) tree local_gid_t_node; +static GTY(()) tree local_time_t_node; + +static GTY(()) tree local_socklen_t_node; +static GTY(()) tree local_in_addr_t_node; + +static struct type_special { + tree *match; + tree *replace; + tree *cousin; +} special_types[] = { + { &local_atomic_size_t_node, &local_size_t_node, &local_ssize_t_node, }, + { &local_atomic_ssize_t_node, &local_ssize_t_node, &local_size_t_node, }, + { &local_size_t_node, NULL, &local_ssize_t_node, }, + { &local_ssize_t_node, NULL, &local_size_t_node, }, + { &local_uint64_t_node, NULL, &local_int64_t_node, }, + { &local_int64_t_node, NULL, &local_uint64_t_node, }, + { &local_pid_t_node, NULL, &local_pid_t_node, }, + { &local_uid_t_node, NULL, &local_uid_t_node, }, + { &local_gid_t_node, NULL, &local_gid_t_node, }, + { &local_time_t_node, NULL, &local_time_t_node, }, + { NULL, NULL, NULL, } +}; + +static bool decode_format_attr (tree, function_format_info *, int); +static int decode_format_type (const char *); + +static bool check_format_string (tree argument, + unsigned HOST_WIDE_INT format_num, + int flags, bool *no_add_attrs, + int expected_format_type); +static bool get_constant (tree expr, unsigned HOST_WIDE_INT *value, + int validated_p); +static const char *convert_format_name_to_system_name (const char *attr_name); + +static int first_target_format_type; +static const char *format_name (int format_num); +static int format_flags (int format_num); + +/* Emit a warning as per format_warning_va, but construct the substring_loc + for the character at offset (CHAR_IDX - 1) within a string constant + FORMAT_STRING_CST at FMT_STRING_LOC. */ + +ATTRIBUTE_GCC_DIAG (5,6) +static bool +format_warning_at_char (location_t fmt_string_loc, tree format_string_cst, + int char_idx, int opt, const char *gmsgid, ...) +{ + va_list ap; + va_start (ap, gmsgid); + tree string_type = TREE_TYPE (format_string_cst); + + /* The callers are of the form: + format_warning (format_string_loc, format_string_cst, + format_chars - orig_format_chars, + where format_chars has already been incremented, so that + CHAR_IDX is one character beyond where the warning should + be emitted. Fix it. */ + char_idx -= 1; + + substring_loc fmt_loc (fmt_string_loc, string_type, char_idx, char_idx, + char_idx); +#if BUILDING_GCC_VERSION >= 9000 + format_string_diagnostic_t diag (fmt_loc, NULL, UNKNOWN_LOCATION, NULL, + NULL); + bool warned = diag.emit_warning_va (opt, gmsgid, &ap); +#else + bool warned = format_warning_va (fmt_loc, UNKNOWN_LOCATION, NULL, + opt, gmsgid, &ap); +#endif + va_end (ap); + + return warned; +} + +/* Check that we have a pointer to a string suitable for use as a format. + The default is to check for a char type. + For objective-c dialects, this is extended to include references to string + objects validated by objc_string_ref_type_p (). + Targets may also provide a string object type that can be used within c and + c++ and shared with their respective objective-c dialects. In this case the + reference to a format string is checked for validity via a hook. + + The function returns true if strref points to any string type valid for the + language dialect and target. */ + +static bool +valid_stringptr_type_p (tree strref) +{ + return (strref != NULL + && TREE_CODE (strref) == POINTER_TYPE + && (TYPE_MAIN_VARIANT (TREE_TYPE (strref)) == char_type_node + || objc_string_ref_type_p (strref))); +// || (*targetcm.string_object_ref_type_p) ((const_tree) strref))); +} + +/* Handle a "format_arg" attribute; arguments as in + struct attribute_spec.handler. */ +tree +handle_frr_format_arg_attribute (tree *node, tree ARG_UNUSED (name), + tree args, int flags, bool *no_add_attrs) +{ + tree type = *node; + tree format_num_expr = TREE_VALUE (args); + unsigned HOST_WIDE_INT format_num = 0; + + if (!get_constant (format_num_expr, &format_num, 0)) + { + error ("format string has invalid operand number"); + *no_add_attrs = true; + return NULL_TREE; + } + + if (prototype_p (type)) + { + /* The format arg can be any string reference valid for the language and + target. We cannot be more specific in this case. */ + if (!check_format_string (type, format_num, flags, no_add_attrs, -1)) + return NULL_TREE; + } + + if (!valid_stringptr_type_p (TREE_TYPE (type))) + { + if (!(flags & (int) ATTR_FLAG_BUILT_IN)) + error ("function does not return string type"); + *no_add_attrs = true; + return NULL_TREE; + } + + return NULL_TREE; +} + +/* Verify that the format_num argument is actually a string reference suitable, + for the language dialect and target (in case the format attribute is in + error). When we know the specific reference type expected, this is also + checked. */ +static bool +check_format_string (tree fntype, unsigned HOST_WIDE_INT format_num, + int flags, bool *no_add_attrs, int expected_format_type) +{ + unsigned HOST_WIDE_INT i; + bool is_target_sref, is_char_ref; + tree ref; + int fmt_flags; + function_args_iterator iter; + + i = 1; + FOREACH_FUNCTION_ARGS (fntype, ref, iter) + { + if (i == format_num) + break; + i++; + } + + if (!ref + || !valid_stringptr_type_p (ref)) + { + if (!(flags & (int) ATTR_FLAG_BUILT_IN)) + error ("format string argument is not a string type"); + *no_add_attrs = true; + return false; + } + + /* We only know that we want a suitable string reference. */ + if (expected_format_type < 0) + return true; + + /* Now check that the arg matches the expected type. */ + is_char_ref = + (TYPE_MAIN_VARIANT (TREE_TYPE (ref)) == char_type_node); + + fmt_flags = format_flags (expected_format_type); + is_target_sref = false; + + if (!(fmt_flags & FMT_FLAG_PARSE_ARG_CONVERT_EXTERNAL)) + { + if (is_char_ref) + return true; /* OK, we expected a char and found one. */ + else + { + error ("found a %qT but the format argument should be a string", + ref); + *no_add_attrs = true; + return false; + } + } + + /* We expect a string object type as the format arg. */ + if (is_char_ref) + { + error ("format argument should be a %qs reference but a string was found", format_name (expected_format_type)); + *no_add_attrs = true; + return false; + } + + /* We will allow a target string ref to match only itself. */ + if (first_target_format_type + && expected_format_type >= first_target_format_type + && is_target_sref) + return true; + else + { + error ("format argument should be a %qs reference", + format_name (expected_format_type)); + *no_add_attrs = true; + return false; + } + + gcc_unreachable (); +} + +/* Verify EXPR is a constant, and store its value. + If validated_p is true there should be no errors. + Returns true on success, false otherwise. */ +static bool +get_constant (tree expr, unsigned HOST_WIDE_INT *value, int validated_p) +{ + if (!tree_fits_uhwi_p (expr)) + { + gcc_assert (!validated_p); + return false; + } + + *value = TREE_INT_CST_LOW (expr); + + return true; +} + +/* Decode the arguments to a "format" attribute into a + function_format_info structure. It is already known that the list + is of the right length. If VALIDATED_P is true, then these + attributes have already been validated and must not be erroneous; + if false, it will give an error message. Returns true if the + attributes are successfully decoded, false otherwise. */ + +static bool +decode_format_attr (tree args, function_format_info *info, int validated_p) +{ + tree format_type_id = TREE_VALUE (args); + tree format_num_expr = TREE_VALUE (TREE_CHAIN (args)); + tree first_arg_num_expr + = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (args))); + + if (TREE_CODE (format_type_id) != STRING_CST) + { + gcc_assert (!validated_p); + error ("unrecognized format specifier"); + return false; + } + else + { + const char *p = TREE_STRING_POINTER (format_type_id); + + p = convert_format_name_to_system_name (p); + + info->format_type = decode_format_type (p); + + if (info->format_type == format_type_error) + { + gcc_assert (!validated_p); + warning (OPT_Wformat_, "%qE is an unrecognized format function type", + format_type_id); + return false; + } + } + + if (!get_constant (format_num_expr, &info->format_num, validated_p)) + { + error ("format string has invalid operand number"); + return false; + } + + if (!get_constant (first_arg_num_expr, &info->first_arg_num, validated_p)) + { + error ("%<...%> has invalid operand number"); + return false; + } + + if (info->first_arg_num != 0 && info->first_arg_num <= info->format_num) + { + gcc_assert (!validated_p); + error ("format string argument follows the arguments to be formatted"); + return false; + } + + return true; +} + +/* Check a call to a format function against a parameter list. */ + +/* The C standard version C++ is treated as equivalent to + or inheriting from, for the purpose of format features supported. */ +#define CPLUSPLUS_STD_VER (cxx_dialect < cxx11 ? STD_C94 : STD_C99) +/* The C standard version we are checking formats against when pedantic. */ +#define C_STD_VER ((int) (c_dialect_cxx () \ + ? CPLUSPLUS_STD_VER \ + : (flag_isoc99 \ + ? STD_C99 \ + : (flag_isoc94 ? STD_C94 : STD_C89)))) +/* The name to give to the standard version we are warning about when + pedantic. FEATURE_VER is the version in which the feature warned out + appeared, which is higher than C_STD_VER. */ +#define C_STD_NAME(FEATURE_VER) (c_dialect_cxx () \ + ? (cxx_dialect < cxx11 ? "ISO C++98" \ + : "ISO C++11") \ + : ((FEATURE_VER) == STD_EXT \ + ? "ISO C" \ + : "ISO C90")) +/* Adjust a C standard version, which may be STD_C9L, to account for + -Wno-long-long. Returns other standard versions unchanged. */ +#define ADJ_STD(VER) ((int) ((VER) == STD_C9L \ + ? (warn_long_long ? STD_C99 : STD_C89) \ + : (VER))) + +/* Enum describing the kind of specifiers present in the format and + requiring an argument. */ +enum format_specifier_kind { + CF_KIND_FORMAT, + CF_KIND_FIELD_WIDTH, + CF_KIND_FIELD_PRECISION +}; + +static const char *kind_descriptions[] = { + N_("format"), + N_("field width specifier"), + N_("field precision specifier") +}; + +/* Structure describing details of a type expected in format checking, + and the type to check against it. */ +struct format_wanted_type +{ + /* The type wanted. */ + tree wanted_type; + /* The name of this type to use in diagnostics. */ + const char *wanted_type_name; + /* Should be type checked just for scalar width identity. */ + int scalar_identity_flag; + /* The level of indirection through pointers at which this type occurs. */ + int pointer_count; + /* Whether, when pointer_count is 1, to allow any character type when + pedantic, rather than just the character or void type specified. */ + int char_lenient_flag; + /* Whether the argument, dereferenced once, is written into and so the + argument must not be a pointer to a const-qualified type. */ + int writing_in_flag; + /* Whether the argument, dereferenced once, is read from and so + must not be a NULL pointer. */ + int reading_from_flag; + /* The kind of specifier that this type is used for. */ + enum format_specifier_kind kind; + /* The starting character of the specifier. This never includes the + initial percent sign. */ + const char *format_start; + /* The length of the specifier. */ + int format_length; + /* The actual parameter to check against the wanted type. */ + tree param; + /* The argument number of that parameter. */ + int arg_num; + /* The offset location of this argument with respect to the format + string location. */ + unsigned int offset_loc; + /* The next type to check for this format conversion, or NULL if none. */ + struct format_wanted_type *next; +}; + +/* Convenience macro for format_length_info meaning unused. */ +#define NO_FMT NULL, FMT_LEN_none, STD_C89 + +static const format_length_info printf_length_specs[] = +{ + { "h", FMT_LEN_h, STD_C89, "hh", FMT_LEN_hh, STD_C99, 0 }, + { "l", FMT_LEN_l, STD_C89, "ll", FMT_LEN_ll, STD_C9L, 0 }, + { "q", FMT_LEN_ll, STD_EXT, NO_FMT, 0 }, + { "L", FMT_LEN_L, STD_C89, NO_FMT, 0 }, + { "z", FMT_LEN_z, STD_C99, NO_FMT, 0 }, + { "Z", FMT_LEN_z, STD_EXT, NO_FMT, 0 }, + { "t", FMT_LEN_t, STD_C99, NO_FMT, 0 }, + { "j", FMT_LEN_j, STD_C99, NO_FMT, 0 }, + { "H", FMT_LEN_H, STD_EXT, NO_FMT, 0 }, + { "D", FMT_LEN_D, STD_EXT, "DD", FMT_LEN_DD, STD_EXT, 0 }, + { NO_FMT, NO_FMT, 0 } +}; + +static const format_flag_spec printf_flag_specs[] = +{ + { ' ', 0, 0, 0, N_("' ' flag"), N_("the ' ' printf flag"), STD_C89 }, + { '+', 0, 0, 0, N_("'+' flag"), N_("the '+' printf flag"), STD_C89 }, + { '#', 0, 0, 0, N_("'#' flag"), N_("the '#' printf flag"), STD_C89 }, + { '0', 0, 0, 0, N_("'0' flag"), N_("the '0' printf flag"), STD_C89 }, + { '-', 0, 0, 0, N_("'-' flag"), N_("the '-' printf flag"), STD_C89 }, + { '\'', 0, 0, 0, N_("''' flag"), N_("the ''' printf flag"), STD_EXT }, + { 'I', 0, 0, 0, N_("'I' flag"), N_("the 'I' printf flag"), STD_EXT }, + { 'w', 0, 0, 0, N_("field width"), N_("field width in printf format"), STD_C89 }, + { 'p', 0, 0, 0, N_("precision"), N_("precision in printf format"), STD_C89 }, + { 'L', 0, 0, 0, N_("length modifier"), N_("length modifier in printf format"), STD_C89 }, + { 0, 0, 0, 0, NULL, NULL, STD_C89 } +}; + + +static const format_flag_pair printf_flag_pairs[] = +{ + { ' ', '+', 1, 0 }, + { '0', '-', 1, 0 }, + { '0', 'p', 1, 'i' }, + { 0, 0, 0, 0 } +}; + +#define ETAB_SZ 128 +static kernel_ext_fmt ext_p[ETAB_SZ] = { + { } +}; +static kernel_ext_fmt ext_d[ETAB_SZ] = { + { } +}; + +static const format_char_info print_char_table[] = +{ + /* C89 conversion specifiers. */ + /* none, hh, h, l, ll, L, z, t, j, H, D, DD */ + { "di", 0, STD_C89, { T89_I, T99_SC, T89_S, T89_L, T9L_LL, TEX_S64, T99_SST, T99_PD, T99_IM, BADLEN, BADLEN, BADLEN }, "-wp0 +'I", "i", NULL, ext_d }, + { "oxX", 0, STD_C89, { T89_UI, T99_UC, T89_US, T89_UL, T9L_ULL, TEX_U64, T99_ST, T99_UPD, T99_UIM, BADLEN, BADLEN, BADLEN }, "-wp0#", "i", NULL, NULL }, + { "u", 0, STD_C89, { T89_UI, T99_UC, T89_US, T89_UL, T9L_ULL, TEX_U64, T99_ST, T99_UPD, T99_UIM, BADLEN, BADLEN, BADLEN }, "-wp0'I", "i", NULL, NULL }, + { "fgG", 0, STD_C89, { T89_D, BADLEN, BADLEN, T99_D, BADLEN, T89_LD, BADLEN, BADLEN, BADLEN, TEX_D32, TEX_D64, TEX_D128 }, "-wp0 +#'I", "", NULL, NULL }, + { "eE", 0, STD_C89, { T89_D, BADLEN, BADLEN, T99_D, BADLEN, T89_LD, BADLEN, BADLEN, BADLEN, TEX_D32, TEX_D64, TEX_D128 }, "-wp0 +#I", "", NULL, NULL }, + { "c", 0, STD_C89, { T89_I, BADLEN, BADLEN, T94_WI, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-w", "", NULL, NULL }, + { "s", 1, STD_C89, { T89_C, BADLEN, BADLEN, T94_W, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-wp", "cR", NULL, NULL }, + { "p", 1, STD_C89, { T89_V, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-wp", "c", NULL, ext_p }, + { "n", 1, STD_C89, { T89_I, T99_SC, T89_S, T89_L, T9L_LL, BADLEN, T99_SST, T99_PD, T99_IM, BADLEN, BADLEN, BADLEN }, "", "W", NULL, NULL }, + /* C99 conversion specifiers. */ + { "F", 0, STD_C99, { T99_D, BADLEN, BADLEN, T99_D, BADLEN, T99_LD, BADLEN, BADLEN, BADLEN, TEX_D32, TEX_D64, TEX_D128 }, "-wp0 +#'I", "", NULL, NULL }, + { "aA", 0, STD_C99, { T99_D, BADLEN, BADLEN, T99_D, BADLEN, T99_LD, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-wp0 +#", "", NULL, NULL }, + /* X/Open conversion specifiers. */ + { "C", 0, STD_EXT, { TEX_WI, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-w", "", NULL, NULL }, + { "S", 1, STD_EXT, { TEX_W, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-wp", "R", NULL, NULL }, + /* GNU conversion specifiers. */ + { "m", 0, STD_EXT, { T89_V, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-wp", "", NULL, NULL }, + { NULL, 0, STD_C89, NOLENGTHS, NULL, NULL, NULL, NULL } +}; + +/* This must be in the same order as enum format_type. */ +static const format_kind_info format_types_orig[] = +{ + { "frr_printf", printf_length_specs, print_char_table, " +#0-'I", NULL, + printf_flag_specs, printf_flag_pairs, + FMT_FLAG_ARG_CONVERT|FMT_FLAG_DOLLAR_MULTIPLE|FMT_FLAG_USE_DOLLAR|FMT_FLAG_EMPTY_PREC_OK, + 'w', 0, 'p', 0, 'L', 0, + &integer_type_node, &integer_type_node + }, +}; + +/* This layer of indirection allows GCC to reassign format_types with + new data if necessary, while still allowing the original data to be + const. */ +static const format_kind_info *format_types = format_types_orig; + +static int n_format_types = ARRAY_SIZE (format_types_orig); + +/* Structure detailing the results of checking a format function call + where the format expression may be a conditional expression with + many leaves resulting from nested conditional expressions. */ +struct format_check_results +{ + /* Number of leaves of the format argument that could not be checked + as they were not string literals. */ + int number_non_literal; + /* Number of leaves of the format argument that were null pointers or + string literals, but had extra format arguments. */ + int number_extra_args; + location_t extra_arg_loc; + /* Number of leaves of the format argument that were null pointers or + string literals, but had extra format arguments and used $ operand + numbers. */ + int number_dollar_extra_args; + /* Number of leaves of the format argument that were wide string + literals. */ + int number_wide; + /* Number of leaves of the format argument that are not array of "char". */ + int number_non_char; + /* Number of leaves of the format argument that were empty strings. */ + int number_empty; + /* Number of leaves of the format argument that were unterminated + strings. */ + int number_unterminated; + /* Number of leaves of the format argument that were not counted above. */ + int number_other; + /* Location of the format string. */ + location_t format_string_loc; +}; + +struct format_check_context +{ + format_check_results *res; + function_format_info *info; + tree params; + vec *arglocs; +}; + +/* Return the format name (as specified in the original table) for the format + type indicated by format_num. */ +static const char * +format_name (int format_num) +{ + if (format_num >= 0 && format_num < n_format_types) + return format_types[format_num].name; + gcc_unreachable (); +} + +/* Return the format flags (as specified in the original table) for the format + type indicated by format_num. */ +static int +format_flags (int format_num) +{ + if (format_num >= 0 && format_num < n_format_types) + return format_types[format_num].flags; + gcc_unreachable (); +} + +static void check_format_info (function_format_info *, tree, + vec *); +static void check_format_arg (void *, tree, unsigned HOST_WIDE_INT); +static void check_format_info_main (format_check_results *, + function_format_info *, const char *, + location_t, tree, + int, tree, + unsigned HOST_WIDE_INT, + object_allocator &, + vec *); + +static void init_dollar_format_checking (int, tree); +static int maybe_read_dollar_number (const char **, int, + tree, tree *, const format_kind_info *); +static bool avoid_dollar_number (const char *); +static void finish_dollar_format_checking (format_check_results *, int); + +static const format_flag_spec *get_flag_spec (const format_flag_spec *, + int, const char *); + +static void check_format_types (const substring_loc &fmt_loc, + format_wanted_type *, + const format_kind_info *fki, + int offset_to_type_start, + char conversion_char, + vec *arglocs); +static void format_type_warning (const substring_loc &fmt_loc, + location_t param_loc, + format_wanted_type *, tree, + tree, + const format_kind_info *fki, + int offset_to_type_start, + char conversion_char, + const char *extra = NULL); + +static bool check_kef_type (const substring_loc &fmt_loc, + const struct kernel_ext_fmt *kef, + unsigned arg_num, + tree cur_param, + tree wanted_type, + const format_kind_info *fki, + int offset_to_type_start, + char conversion_char, + vec *arglocs); + +/* Decode a format type from a string, returning the type, or + format_type_error if not valid, in which case the caller should print an + error message. */ +static int +decode_format_type (const char *s) +{ + int i; + int slen; + + s = convert_format_name_to_system_name (s); + slen = strlen (s); + for (i = 0; i < n_format_types; i++) + { + int alen; + if (!strcmp (s, format_types[i].name)) + return i; + alen = strlen (format_types[i].name); + if (slen == alen + 4 && s[0] == '_' && s[1] == '_' + && s[slen - 1] == '_' && s[slen - 2] == '_' + && !strncmp (s + 2, format_types[i].name, alen)) + return i; + } + return format_type_error; +} + + +/* Check the argument list of a call to printf, scanf, etc. + ATTRS are the attributes on the function type. There are NARGS argument + values in the array ARGARRAY. + Also, if -Wsuggest-attribute=format, + warn for calls to vprintf or vscanf in functions with no such format + attribute themselves. */ + +void +check_function_format (tree attrs, int nargs, tree *argarray, + vec *arglocs) +{ + tree a; + + /* See if this function has any format attributes. */ + for (a = attrs; a; a = TREE_CHAIN (a)) + { + if (is_attribute_p ("frr_format", TREE_PURPOSE (a))) + { + /* Yup; check it. */ + function_format_info info; + decode_format_attr (TREE_VALUE (a), &info, /*validated=*/true); + if (warn_format) + { + /* FIXME: Rewrite all the internal functions in this file + to use the ARGARRAY directly instead of constructing this + temporary list. */ + tree params = NULL_TREE; + int i; + for (i = nargs - 1; i >= 0; i--) + params = tree_cons (NULL_TREE, argarray[i], params); + check_format_info (&info, params, arglocs); + } + + /* Attempt to detect whether the current function might benefit + from the format attribute if the called function is decorated + with it. Avoid using calls with string literal formats for + guidance since those are unlikely to be viable candidates. */ + if (warn_suggest_attribute_format + && current_function_decl != NULL_TREE + && info.first_arg_num == 0 + && (format_types[info.format_type].flags + & (int) FMT_FLAG_ARG_CONVERT) + /* c_strlen will fail for a function parameter but succeed + for a literal or constant array. */ + && !c_strlen (argarray[info.format_num - 1], 1)) + { + tree c; + for (c = TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl)); + c; + c = TREE_CHAIN (c)) + if (is_attribute_p ("frr_format", TREE_PURPOSE (c)) + && (decode_format_type (IDENTIFIER_POINTER + (TREE_VALUE (TREE_VALUE (c)))) + == info.format_type)) + break; + if (c == NULL_TREE) + { + /* Check if the current function has a parameter to which + the format attribute could be attached; if not, it + can't be a candidate for a format attribute, despite + the vprintf-like or vscanf-like call. */ + tree args; + for (args = DECL_ARGUMENTS (current_function_decl); + args != 0; + args = DECL_CHAIN (args)) + { + if (TREE_CODE (TREE_TYPE (args)) == POINTER_TYPE + && (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (args))) + == char_type_node)) + break; + } + if (args != 0) + warning (OPT_Wsuggest_attribute_format, + "function %qD might be a candidate for %qs % attribute", + current_function_decl, + format_types[info.format_type].name); + } + } + } + } +} + + +/* Variables used by the checking of $ operand number formats. */ +static char *dollar_arguments_used = NULL; +static char *dollar_arguments_pointer_p = NULL; +static int dollar_arguments_alloc = 0; +static int dollar_arguments_count; +static int dollar_first_arg_num; +static int dollar_max_arg_used; +static int dollar_format_warned; + +/* Initialize the checking for a format string that may contain $ + parameter number specifications; we will need to keep track of whether + each parameter has been used. FIRST_ARG_NUM is the number of the first + argument that is a parameter to the format, or 0 for a vprintf-style + function; PARAMS is the list of arguments starting at this argument. */ + +static void +init_dollar_format_checking (int first_arg_num, tree params) +{ + tree oparams = params; + + dollar_first_arg_num = first_arg_num; + dollar_arguments_count = 0; + dollar_max_arg_used = 0; + dollar_format_warned = 0; + if (first_arg_num > 0) + { + while (params) + { + dollar_arguments_count++; + params = TREE_CHAIN (params); + } + } + if (dollar_arguments_alloc < dollar_arguments_count) + { + free (dollar_arguments_used); + free (dollar_arguments_pointer_p); + dollar_arguments_alloc = dollar_arguments_count; + dollar_arguments_used = XNEWVEC (char, dollar_arguments_alloc); + dollar_arguments_pointer_p = XNEWVEC (char, dollar_arguments_alloc); + } + if (dollar_arguments_alloc) + { + memset (dollar_arguments_used, 0, dollar_arguments_alloc); + if (first_arg_num > 0) + { + int i = 0; + params = oparams; + while (params) + { + dollar_arguments_pointer_p[i] = (TREE_CODE (TREE_TYPE (TREE_VALUE (params))) + == POINTER_TYPE); + params = TREE_CHAIN (params); + i++; + } + } + } +} + + +/* Look for a decimal number followed by a $ in *FORMAT. If DOLLAR_NEEDED + is set, it is an error if one is not found; otherwise, it is OK. If + such a number is found, check whether it is within range and mark that + numbered operand as being used for later checking. Returns the operand + number if found and within range, zero if no such number was found and + this is OK, or -1 on error. PARAMS points to the first operand of the + format; PARAM_PTR is made to point to the parameter referred to. If + a $ format is found, *FORMAT is updated to point just after it. */ + +static int +maybe_read_dollar_number (const char **format, + int dollar_needed, tree params, tree *param_ptr, + const format_kind_info *fki) +{ + int argnum; + int overflow_flag; + const char *fcp = *format; + if (!ISDIGIT (*fcp)) + { + if (dollar_needed) + { + warning (OPT_Wformat_, "missing $ operand number in format"); + return -1; + } + else + return 0; + } + argnum = 0; + overflow_flag = 0; + while (ISDIGIT (*fcp)) + { + int nargnum; + nargnum = 10 * argnum + (*fcp - '0'); + if (nargnum < 0 || nargnum / 10 != argnum) + overflow_flag = 1; + argnum = nargnum; + fcp++; + } + if (*fcp != '$') + { + if (dollar_needed) + { + warning (OPT_Wformat_, "missing $ operand number in format"); + return -1; + } + else + return 0; + } + *format = fcp + 1; + if (pedantic && !dollar_format_warned) + { + warning (OPT_Wformat_, "%s does not support %%n$ operand number formats", + C_STD_NAME (STD_EXT)); + dollar_format_warned = 1; + } + if (overflow_flag || argnum == 0 + || (dollar_first_arg_num && argnum > dollar_arguments_count)) + { + warning (OPT_Wformat_, "operand number out of range in format"); + return -1; + } + if (argnum > dollar_max_arg_used) + dollar_max_arg_used = argnum; + /* For vprintf-style functions we may need to allocate more memory to + track which arguments are used. */ + while (dollar_arguments_alloc < dollar_max_arg_used) + { + int nalloc; + nalloc = 2 * dollar_arguments_alloc + 16; + dollar_arguments_used = XRESIZEVEC (char, dollar_arguments_used, + nalloc); + dollar_arguments_pointer_p = XRESIZEVEC (char, dollar_arguments_pointer_p, + nalloc); + memset (dollar_arguments_used + dollar_arguments_alloc, 0, + nalloc - dollar_arguments_alloc); + dollar_arguments_alloc = nalloc; + } + if (!(fki->flags & (int) FMT_FLAG_DOLLAR_MULTIPLE) + && dollar_arguments_used[argnum - 1] == 1) + { + dollar_arguments_used[argnum - 1] = 2; + warning (OPT_Wformat_, "format argument %d used more than once in %s format", + argnum, fki->name); + } + else + dollar_arguments_used[argnum - 1] = 1; + if (dollar_first_arg_num) + { + int i; + *param_ptr = params; + for (i = 1; i < argnum && *param_ptr != 0; i++) + *param_ptr = TREE_CHAIN (*param_ptr); + + /* This case shouldn't be caught here. */ + gcc_assert (*param_ptr); + } + else + *param_ptr = 0; + return argnum; +} + +/* Ensure that FORMAT does not start with a decimal number followed by + a $; give a diagnostic and return true if it does, false otherwise. */ + +static bool +avoid_dollar_number (const char *format) +{ + if (!ISDIGIT (*format)) + return false; + while (ISDIGIT (*format)) + format++; + if (*format == '$') + { + warning (OPT_Wformat_, "%<$%> operand number used after format without operand number"); + return true; + } + return false; +} + + +/* Finish the checking for a format string that used $ operand number formats + instead of non-$ formats. We check for unused operands before used ones + (a serious error, since the implementation of the format function + can't know what types to pass to va_arg to find the later arguments). + and for unused operands at the end of the format (if we know how many + arguments the format had, so not for vprintf). If there were operand + numbers out of range on a non-vprintf-style format, we won't have reached + here. If POINTER_GAP_OK, unused arguments are OK if all arguments are + pointers. */ + +static void +finish_dollar_format_checking (format_check_results *res, int pointer_gap_ok) +{ + int i; + bool found_pointer_gap = false; + for (i = 0; i < dollar_max_arg_used; i++) + { + if (!dollar_arguments_used[i]) + { + if (pointer_gap_ok && (dollar_first_arg_num == 0 + || dollar_arguments_pointer_p[i])) + found_pointer_gap = true; + else + warning_at (res->format_string_loc, OPT_Wformat_, + "format argument %d unused before used argument %d in %<$%>-style format", + i + 1, dollar_max_arg_used); + } + } + if (found_pointer_gap + || (dollar_first_arg_num + && dollar_max_arg_used < dollar_arguments_count)) + { + res->number_other--; + res->number_dollar_extra_args++; + } +} + + +/* Retrieve the specification for a format flag. SPEC contains the + specifications for format flags for the applicable kind of format. + FLAG is the flag in question. If PREDICATES is NULL, the basic + spec for that flag must be retrieved and must exist. If + PREDICATES is not NULL, it is a string listing possible predicates + for the spec entry; if an entry predicated on any of these is + found, it is returned, otherwise NULL is returned. */ + +static const format_flag_spec * +get_flag_spec (const format_flag_spec *spec, int flag, const char *predicates) +{ + int i; + for (i = 0; spec[i].flag_char != 0; i++) + { + if (spec[i].flag_char != flag) + continue; + if (predicates != NULL) + { + if (spec[i].predicate != 0 + && strchr (predicates, spec[i].predicate) != 0) + return &spec[i]; + } + else if (spec[i].predicate == 0) + return &spec[i]; + } + gcc_assert (predicates); + return NULL; +} + + +/* Check the argument list of a call to printf, scanf, etc. + INFO points to the function_format_info structure. + PARAMS is the list of argument values. */ + +static void +check_format_info (function_format_info *info, tree params, + vec *arglocs) +{ + format_check_context format_ctx; + unsigned HOST_WIDE_INT arg_num; + tree format_tree; + format_check_results res; + /* Skip to format argument. If the argument isn't available, there's + no work for us to do; prototype checking will catch the problem. */ + for (arg_num = 1; ; ++arg_num) + { + if (params == 0) + return; + if (arg_num == info->format_num) + break; + params = TREE_CHAIN (params); + } + format_tree = TREE_VALUE (params); + params = TREE_CHAIN (params); + if (format_tree == 0) + return; + + res.number_non_literal = 0; + res.number_extra_args = 0; + res.extra_arg_loc = UNKNOWN_LOCATION; + res.number_dollar_extra_args = 0; + res.number_wide = 0; + res.number_non_char = 0; + res.number_empty = 0; + res.number_unterminated = 0; + res.number_other = 0; + res.format_string_loc = input_location; + + format_ctx.res = &res; + format_ctx.info = info; + format_ctx.params = params; + format_ctx.arglocs = arglocs; + + check_function_arguments_recurse (check_format_arg, &format_ctx, + format_tree, arg_num); + + location_t loc = format_ctx.res->format_string_loc; + + if (res.number_non_literal > 0) + { + /* Functions taking a va_list normally pass a non-literal format + string. These functions typically are declared with + first_arg_num == 0, so avoid warning in those cases. */ + if (!(format_types[info->format_type].flags & (int) FMT_FLAG_ARG_CONVERT)) + { + /* For strftime-like formats, warn for not checking the format + string; but there are no arguments to check. */ + warning_at (loc, OPT_Wformat_nonliteral, + "format not a string literal, format string not checked"); + } + else if (info->first_arg_num != 0) + { + /* If there are no arguments for the format at all, we may have + printf (foo) which is likely to be a security hole. */ + while (arg_num + 1 < info->first_arg_num) + { + if (params == 0) + break; + params = TREE_CHAIN (params); + ++arg_num; + } + if (params == 0 && warn_format_security) + warning_at (loc, OPT_Wformat_security, + "format not a string literal and no format arguments"); + else if (params == 0 && warn_format_nonliteral) + warning_at (loc, OPT_Wformat_nonliteral, + "format not a string literal and no format arguments"); + else + warning_at (loc, OPT_Wformat_nonliteral, + "format not a string literal, argument types not checked"); + } + } + + /* If there were extra arguments to the format, normally warn. However, + the standard does say extra arguments are ignored, so in the specific + case where we have multiple leaves (conditional expressions or + ngettext) allow extra arguments if at least one leaf didn't have extra + arguments, but was otherwise OK (either non-literal or checked OK). + If the format is an empty string, this should be counted similarly to the + case of extra format arguments. */ + if (res.number_extra_args > 0 && res.number_non_literal == 0 + && res.number_other == 0) + { + if (res.extra_arg_loc == UNKNOWN_LOCATION) + res.extra_arg_loc = loc; + warning_at (res.extra_arg_loc, OPT_Wformat_extra_args, + "too many arguments for format"); + } + if (res.number_dollar_extra_args > 0 && res.number_non_literal == 0 + && res.number_other == 0) + warning_at (loc, OPT_Wformat_extra_args, "unused arguments in %<$%>-style format"); + if (res.number_empty > 0 && res.number_non_literal == 0 + && res.number_other == 0) + warning_at (loc, OPT_Wformat_zero_length, "zero-length %s format string", + format_types[info->format_type].name); + + if (res.number_wide > 0) + warning_at (loc, OPT_Wformat_, "format is a wide character string"); + + if (res.number_non_char > 0) + warning_at (loc, OPT_Wformat_, + "format string is not an array of type %qs", "char"); + + if (res.number_unterminated > 0) + warning_at (loc, OPT_Wformat_, "unterminated format string"); +} + +/* Callback from check_function_arguments_recurse to check a + format string. FORMAT_TREE is the format parameter. ARG_NUM + is the number of the format argument. CTX points to a + format_check_context. */ + +static void +check_format_arg (void *ctx, tree format_tree, + unsigned HOST_WIDE_INT arg_num) +{ + format_check_context *format_ctx = (format_check_context *) ctx; + format_check_results *res = format_ctx->res; + function_format_info *info = format_ctx->info; + tree params = format_ctx->params; + vec *arglocs = format_ctx->arglocs; + + int format_length; + HOST_WIDE_INT offset; + const char *format_chars; + tree array_size = 0; + tree array_init; + + location_t fmt_param_loc = EXPR_LOC_OR_LOC (format_tree, input_location); + + /* Pull out a constant value if the front end didn't, and handle location + wrappers. */ + format_tree = fold_for_warn (format_tree); + STRIP_NOPS (format_tree); + + if (integer_zerop (format_tree)) + { + /* Skip to first argument to check, so we can see if this format + has any arguments (it shouldn't). */ + while (arg_num + 1 < info->first_arg_num) + { + if (params == 0) + return; + params = TREE_CHAIN (params); + ++arg_num; + } + + if (params == 0) + res->number_other++; + else + { + if (res->number_extra_args == 0) + res->extra_arg_loc = EXPR_LOC_OR_LOC (TREE_VALUE (params), + input_location); + res->number_extra_args++; + } + return; + } + + offset = 0; + if (TREE_CODE (format_tree) == POINTER_PLUS_EXPR) + { + tree arg0, arg1; + + arg0 = TREE_OPERAND (format_tree, 0); + arg1 = TREE_OPERAND (format_tree, 1); + STRIP_NOPS (arg0); + STRIP_NOPS (arg1); + if (TREE_CODE (arg1) == INTEGER_CST) + format_tree = arg0; + else + { + res->number_non_literal++; + return; + } + /* POINTER_PLUS_EXPR offsets are to be interpreted signed. */ + if (!cst_and_fits_in_hwi (arg1)) + { + res->number_non_literal++; + return; + } + offset = int_cst_value (arg1); + } + if (TREE_CODE (format_tree) != ADDR_EXPR) + { + res->number_non_literal++; + return; + } + res->format_string_loc = EXPR_LOC_OR_LOC (format_tree, input_location); + format_tree = TREE_OPERAND (format_tree, 0); + if (format_types[info->format_type].flags + & (int) FMT_FLAG_PARSE_ARG_CONVERT_EXTERNAL) + { + /* We cannot examine this string here - but we can check that it is + a valid type. */ + if (TREE_CODE (format_tree) != CONST_DECL) + { + res->number_non_literal++; + return; + } + /* Skip to first argument to check. */ + while (arg_num + 1 < info->first_arg_num) + { + if (params == 0) + return; + params = TREE_CHAIN (params); + ++arg_num; + } + return; + } + if (TREE_CODE (format_tree) == ARRAY_REF + && tree_fits_shwi_p (TREE_OPERAND (format_tree, 1)) + && (offset += tree_to_shwi (TREE_OPERAND (format_tree, 1))) >= 0) + format_tree = TREE_OPERAND (format_tree, 0); + if (offset < 0) + { + res->number_non_literal++; + return; + } + if (VAR_P (format_tree) + && TREE_CODE (TREE_TYPE (format_tree)) == ARRAY_TYPE + && (array_init = decl_constant_value (format_tree)) != format_tree + && TREE_CODE (array_init) == STRING_CST) + { + /* Extract the string constant initializer. Note that this may include + a trailing NUL character that is not in the array (e.g. + const char a[3] = "foo";). */ + array_size = DECL_SIZE_UNIT (format_tree); + format_tree = array_init; + } + if (TREE_CODE (format_tree) != STRING_CST) + { + res->number_non_literal++; + return; + } + tree underlying_type + = TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (format_tree))); + if (underlying_type != char_type_node) + { + if (underlying_type == char16_type_node + || underlying_type == char32_type_node + || underlying_type == wchar_type_node) + res->number_wide++; + else + res->number_non_char++; + return; + } + format_chars = TREE_STRING_POINTER (format_tree); + format_length = TREE_STRING_LENGTH (format_tree); + if (array_size != 0) + { + /* Variable length arrays can't be initialized. */ + gcc_assert (TREE_CODE (array_size) == INTEGER_CST); + + if (tree_fits_shwi_p (array_size)) + { + HOST_WIDE_INT array_size_value = tree_to_shwi (array_size); + if (array_size_value > 0 + && array_size_value == (int) array_size_value + && format_length > array_size_value) + format_length = array_size_value; + } + } + if (offset) + { + if (offset >= format_length) + { + res->number_non_literal++; + return; + } + format_chars += offset; + format_length -= offset; + } + if (format_length < 1 || format_chars[--format_length] != 0) + { + res->number_unterminated++; + return; + } + if (format_length == 0) + { + res->number_empty++; + return; + } + + /* Skip to first argument to check. */ + while (arg_num + 1 < info->first_arg_num) + { + if (params == 0) + return; + params = TREE_CHAIN (params); + ++arg_num; + } + /* Provisionally increment res->number_other; check_format_info_main + will decrement it if it finds there are extra arguments, but this way + need not adjust it for every return. */ + res->number_other++; + object_allocator fwt_pool ("format_wanted_type pool"); + check_format_info_main (res, info, format_chars, fmt_param_loc, format_tree, + format_length, params, arg_num, fwt_pool, arglocs); +} + +/* Support class for argument_parser and check_format_info_main. + Tracks any flag characters that have been applied to the + current argument. */ + +class flag_chars_t +{ + public: + flag_chars_t (); + bool has_char_p (char ch) const; + void add_char (char ch); + void validate (const format_kind_info *fki, + const format_char_info *fci, + const format_flag_spec *flag_specs, + const char * const format_chars, + tree format_string_cst, + location_t format_string_loc, + const char * const orig_format_chars, + char format_char, + bool quoted); + int get_alloc_flag (const format_kind_info *fki); + int assignment_suppression_p (const format_kind_info *fki); + + private: + char m_flag_chars[256]; +}; + +/* Support struct for argument_parser and check_format_info_main. + Encapsulates any length modifier applied to the current argument. */ + +struct length_modifier +{ + length_modifier () + : chars (NULL), val (FMT_LEN_none), std (STD_C89), + scalar_identity_flag (0) + { + } + + length_modifier (const char *chars_, + enum format_lengths val_, + enum format_std_version std_, + int scalar_identity_flag_) + : chars (chars_), val (val_), std (std_), + scalar_identity_flag (scalar_identity_flag_) + { + } + + const char *chars; + enum format_lengths val; + enum format_std_version std; + int scalar_identity_flag; +}; + +/* Parsing one argument within a format string. */ + +class argument_parser +{ + public: + argument_parser (function_format_info *info, const char *&format_chars, + tree format_string_cst, + const char * const orig_format_chars, + location_t format_string_loc, flag_chars_t &flag_chars, + int &has_operand_number, tree first_fillin_param, + object_allocator &fwt_pool_, + vec *arglocs); + + bool read_any_dollar (); + + bool read_format_flags (); + + bool + read_any_format_width (tree ¶ms, + unsigned HOST_WIDE_INT &arg_num); + + void + read_any_format_left_precision (); + + bool + read_any_format_precision (tree ¶ms, + unsigned HOST_WIDE_INT &arg_num); + + void handle_alloc_chars (); + + length_modifier read_any_length_modifier (); + + void read_any_other_modifier (); + + const format_char_info *find_format_char_info (char format_char); + + void + validate_flag_pairs (const format_char_info *fci, + char format_char); + + void + give_y2k_warnings (const format_char_info *fci, + char format_char); + + void parse_any_scan_set (const format_char_info *fci); + + bool handle_conversions (const format_char_info *fci, + const length_modifier &len_modifier, + tree &wanted_type, + const char *&wanted_type_name, + unsigned HOST_WIDE_INT &arg_num, + tree ¶ms, + char format_char); + + bool + check_argument_type (const format_char_info *fci, + const struct kernel_ext_fmt *kef, + const length_modifier &len_modifier, + tree &wanted_type, + const char *&wanted_type_name, + const bool suppressed, + unsigned HOST_WIDE_INT &arg_num, + tree ¶ms, + const int alloc_flag, + const char * const format_start, + const char * const type_start, + location_t fmt_param_loc, + char conversion_char); + + private: + const function_format_info *const info; + const format_kind_info * const fki; + const format_flag_spec * const flag_specs; + const char *start_of_this_format; + const char *&format_chars; + const tree format_string_cst; + const char * const orig_format_chars; + const location_t format_string_loc; + object_allocator &fwt_pool; + flag_chars_t &flag_chars; + int main_arg_num; + tree main_arg_params; + int &has_operand_number; + const tree first_fillin_param; + format_wanted_type width_wanted_type; + format_wanted_type precision_wanted_type; + public: + format_wanted_type main_wanted_type; + private: + format_wanted_type *first_wanted_type; + format_wanted_type *last_wanted_type; + vec *arglocs; +}; + +/* flag_chars_t's constructor. */ + +flag_chars_t::flag_chars_t () +{ + m_flag_chars[0] = 0; +} + +/* Has CH been seen as a flag within the current argument? */ + +bool +flag_chars_t::has_char_p (char ch) const +{ + return strchr (m_flag_chars, ch) != 0; +} + +/* Add CH to the flags seen within the current argument. */ + +void +flag_chars_t::add_char (char ch) +{ + int i = strlen (m_flag_chars); + m_flag_chars[i++] = ch; + m_flag_chars[i] = 0; +} + +/* Validate the individual flags used, removing any that are invalid. */ + +void +flag_chars_t::validate (const format_kind_info *fki, + const format_char_info *fci, + const format_flag_spec *flag_specs, + const char * const format_chars, + tree format_string_cst, + location_t format_string_loc, + const char * const orig_format_chars, + char format_char, + bool quoted) +{ + int i; + int d = 0; + bool quotflag = false; + + for (i = 0; m_flag_chars[i] != 0; i++) + { + const format_flag_spec *s = get_flag_spec (flag_specs, + m_flag_chars[i], NULL); + m_flag_chars[i - d] = m_flag_chars[i]; + if (m_flag_chars[i] == fki->length_code_char) + continue; + + /* Remember if a quoting flag is seen. */ + quotflag |= s->quoting; + + if (strchr (fci->flag_chars, m_flag_chars[i]) == 0) + { + format_warning_at_char (format_string_loc, format_string_cst, + format_chars - orig_format_chars, + OPT_Wformat_, + "%s used with %<%%%c%> %s format", + _(s->name), format_char, fki->name); + d++; + continue; + } + if (pedantic) + { + const format_flag_spec *t; + if (ADJ_STD (s->std) > C_STD_VER) + warning_at (format_string_loc, OPT_Wformat_, + "%s does not support %s", + C_STD_NAME (s->std), _(s->long_name)); + t = get_flag_spec (flag_specs, m_flag_chars[i], fci->flags2); + if (t != NULL && ADJ_STD (t->std) > ADJ_STD (s->std)) + { + const char *long_name = (t->long_name != NULL + ? t->long_name + : s->long_name); + if (ADJ_STD (t->std) > C_STD_VER) + warning_at (format_string_loc, OPT_Wformat_, + "%s does not support %s with the %<%%%c%> %s format", + C_STD_NAME (t->std), _(long_name), + format_char, fki->name); + } + } + + /* Detect quoting directives used within a quoted sequence, such + as GCC's "%<...%qE". */ + if (quoted && s->quoting) + { + format_warning_at_char (format_string_loc, format_string_cst, + format_chars - orig_format_chars - 1, + OPT_Wformat_, + "%s used within a quoted sequence", + _(s->name)); + } + } + m_flag_chars[i - d] = 0; + + if (!quoted + && !quotflag + && strchr (fci->flags2, '\'')) + { + format_warning_at_char (format_string_loc, format_string_cst, + format_chars - orig_format_chars, + OPT_Wformat_, + "%qc conversion used unquoted", + format_char); + } +} + +/* Determine if an assignment-allocation has been set, requiring + an extra char ** for writing back a dynamically-allocated char *. + This is for handling the optional 'm' character in scanf. */ + +int +flag_chars_t::get_alloc_flag (const format_kind_info *fki) +{ + if ((fki->flags & (int) FMT_FLAG_SCANF_A_KLUDGE) + && has_char_p ('a')) + return 1; + if (fki->alloc_char && has_char_p (fki->alloc_char)) + return 1; + return 0; +} + +/* Determine if an assignment-suppression character was seen. + ('*' in scanf, for discarding the converted input). */ + +int +flag_chars_t::assignment_suppression_p (const format_kind_info *fki) +{ + if (fki->suppression_char + && has_char_p (fki->suppression_char)) + return 1; + return 0; +} + +/* Constructor for argument_parser. Initialize for parsing one + argument within a format string. */ + +argument_parser:: +argument_parser (function_format_info *info_, const char *&format_chars_, + tree format_string_cst_, + const char * const orig_format_chars_, + location_t format_string_loc_, + flag_chars_t &flag_chars_, + int &has_operand_number_, + tree first_fillin_param_, + object_allocator &fwt_pool_, + vec *arglocs_) +: info (info_), + fki (&format_types[info->format_type]), + flag_specs (fki->flag_specs), + start_of_this_format (format_chars_), + format_chars (format_chars_), + format_string_cst (format_string_cst_), + orig_format_chars (orig_format_chars_), + format_string_loc (format_string_loc_), + fwt_pool (fwt_pool_), + flag_chars (flag_chars_), + main_arg_num (0), + main_arg_params (NULL), + has_operand_number (has_operand_number_), + first_fillin_param (first_fillin_param_), + first_wanted_type (NULL), + last_wanted_type (NULL), + arglocs (arglocs_) +{ +} + +/* Handle dollars at the start of format arguments, setting up main_arg_params + and main_arg_num. + + Return true if format parsing is to continue, false otherwise. */ + +bool +argument_parser::read_any_dollar () +{ + if ((fki->flags & (int) FMT_FLAG_USE_DOLLAR) && has_operand_number != 0) + { + /* Possibly read a $ operand number at the start of the format. + If one was previously used, one is required here. If one + is not used here, we can't immediately conclude this is a + format without them, since it could be printf %m or scanf %*. */ + int opnum; + opnum = maybe_read_dollar_number (&format_chars, 0, + first_fillin_param, + &main_arg_params, fki); + if (opnum == -1) + return false; + else if (opnum > 0) + { + has_operand_number = 1; + main_arg_num = opnum + info->first_arg_num - 1; + } + } + else if (fki->flags & FMT_FLAG_USE_DOLLAR) + { + if (avoid_dollar_number (format_chars)) + return false; + } + return true; +} + +/* Read any format flags, but do not yet validate them beyond removing + duplicates, since in general validation depends on the rest of + the format. + + Return true if format parsing is to continue, false otherwise. */ + +bool +argument_parser::read_format_flags () +{ + while (*format_chars != 0 + && strchr (fki->flag_chars, *format_chars) != 0) + { + const format_flag_spec *s = get_flag_spec (flag_specs, + *format_chars, NULL); + if (flag_chars.has_char_p (*format_chars)) + { + format_warning_at_char (format_string_loc, format_string_cst, + format_chars + 1 - orig_format_chars, + OPT_Wformat_, + "repeated %s in format", _(s->name)); + } + else + flag_chars.add_char (*format_chars); + + if (s->skip_next_char) + { + ++format_chars; + if (*format_chars == 0) + { + warning_at (format_string_loc, OPT_Wformat_, + "missing fill character at end of strfmon format"); + return false; + } + } + ++format_chars; + } + + return true; +} + +/* Read any format width, possibly * or *m$. + + Return true if format parsing is to continue, false otherwise. */ + +bool +argument_parser:: +read_any_format_width (tree ¶ms, + unsigned HOST_WIDE_INT &arg_num) +{ + if (!fki->width_char) + return true; + + if (fki->width_type != NULL && *format_chars == '*') + { + flag_chars.add_char (fki->width_char); + /* "...a field width...may be indicated by an asterisk. + In this case, an int argument supplies the field width..." */ + ++format_chars; + if (has_operand_number != 0) + { + int opnum; + opnum = maybe_read_dollar_number (&format_chars, + has_operand_number == 1, + first_fillin_param, + ¶ms, fki); + if (opnum == -1) + return false; + else if (opnum > 0) + { + has_operand_number = 1; + arg_num = opnum + info->first_arg_num - 1; + } + else + has_operand_number = 0; + } + else + { + if (avoid_dollar_number (format_chars)) + return false; + } + if (info->first_arg_num != 0) + { + tree cur_param; + if (params == 0) + cur_param = NULL; + else + { + cur_param = TREE_VALUE (params); + if (has_operand_number <= 0) + { + params = TREE_CHAIN (params); + ++arg_num; + } + } + width_wanted_type.wanted_type = *fki->width_type; + width_wanted_type.wanted_type_name = NULL; + width_wanted_type.pointer_count = 0; + width_wanted_type.char_lenient_flag = 0; + width_wanted_type.scalar_identity_flag = 0; + width_wanted_type.writing_in_flag = 0; + width_wanted_type.reading_from_flag = 0; + width_wanted_type.kind = CF_KIND_FIELD_WIDTH; + width_wanted_type.format_start = format_chars - 1; + width_wanted_type.format_length = 1; + width_wanted_type.param = cur_param; + width_wanted_type.arg_num = arg_num; + width_wanted_type.offset_loc = + format_chars - orig_format_chars; + width_wanted_type.next = NULL; + if (last_wanted_type != 0) + last_wanted_type->next = &width_wanted_type; + if (first_wanted_type == 0) + first_wanted_type = &width_wanted_type; + last_wanted_type = &width_wanted_type; + } + } + else + { + /* Possibly read a numeric width. If the width is zero, + we complain if appropriate. */ + int non_zero_width_char = FALSE; + int found_width = FALSE; + while (ISDIGIT (*format_chars)) + { + found_width = TRUE; + if (*format_chars != '0') + non_zero_width_char = TRUE; + ++format_chars; + } + if (found_width && !non_zero_width_char && + (fki->flags & (int) FMT_FLAG_ZERO_WIDTH_BAD)) + warning_at (format_string_loc, OPT_Wformat_, + "zero width in %s format", fki->name); + if (found_width) + flag_chars.add_char (fki->width_char); + } + + return true; +} + +/* Read any format left precision (must be a number, not *). */ +void +argument_parser::read_any_format_left_precision () +{ + if (fki->left_precision_char == 0) + return; + if (*format_chars != '#') + return; + + ++format_chars; + flag_chars.add_char (fki->left_precision_char); + if (!ISDIGIT (*format_chars)) + format_warning_at_char (format_string_loc, format_string_cst, + format_chars - orig_format_chars, + OPT_Wformat_, + "empty left precision in %s format", fki->name); + while (ISDIGIT (*format_chars)) + ++format_chars; +} + +/* Read any format precision, possibly * or *m$. + + Return true if format parsing is to continue, false otherwise. */ + +bool +argument_parser:: +read_any_format_precision (tree ¶ms, + unsigned HOST_WIDE_INT &arg_num) +{ + if (fki->precision_char == 0) + return true; + if (*format_chars != '.') + return true; + + ++format_chars; + flag_chars.add_char (fki->precision_char); + if (fki->precision_type != NULL && *format_chars == '*') + { + /* "...a...precision...may be indicated by an asterisk. + In this case, an int argument supplies the...precision." */ + ++format_chars; + if (has_operand_number != 0) + { + int opnum; + opnum = maybe_read_dollar_number (&format_chars, + has_operand_number == 1, + first_fillin_param, + ¶ms, fki); + if (opnum == -1) + return false; + else if (opnum > 0) + { + has_operand_number = 1; + arg_num = opnum + info->first_arg_num - 1; + } + else + has_operand_number = 0; + } + else + { + if (avoid_dollar_number (format_chars)) + return false; + } + if (info->first_arg_num != 0) + { + tree cur_param; + if (params == 0) + cur_param = NULL; + else + { + cur_param = TREE_VALUE (params); + if (has_operand_number <= 0) + { + params = TREE_CHAIN (params); + ++arg_num; + } + } + precision_wanted_type.wanted_type = *fki->precision_type; + precision_wanted_type.wanted_type_name = NULL; + precision_wanted_type.pointer_count = 0; + precision_wanted_type.char_lenient_flag = 0; + precision_wanted_type.scalar_identity_flag = 0; + precision_wanted_type.writing_in_flag = 0; + precision_wanted_type.reading_from_flag = 0; + precision_wanted_type.kind = CF_KIND_FIELD_PRECISION; + precision_wanted_type.param = cur_param; + precision_wanted_type.format_start = format_chars - 2; + precision_wanted_type.format_length = 2; + precision_wanted_type.arg_num = arg_num; + precision_wanted_type.offset_loc = + format_chars - orig_format_chars; + precision_wanted_type.next = NULL; + if (last_wanted_type != 0) + last_wanted_type->next = &precision_wanted_type; + if (first_wanted_type == 0) + first_wanted_type = &precision_wanted_type; + last_wanted_type = &precision_wanted_type; + } + } + else + { + if (!(fki->flags & (int) FMT_FLAG_EMPTY_PREC_OK) + && !ISDIGIT (*format_chars)) + format_warning_at_char (format_string_loc, format_string_cst, + format_chars - orig_format_chars, + OPT_Wformat_, + "empty precision in %s format", fki->name); + while (ISDIGIT (*format_chars)) + ++format_chars; + } + + return true; +} + +/* Parse any assignment-allocation flags, which request an extra + char ** for writing back a dynamically-allocated char *. + This is for handling the optional 'm' character in scanf, + and, before C99, 'a' (for compatibility with a non-standard + GNU libc extension). */ + +void +argument_parser::handle_alloc_chars () +{ + if (fki->alloc_char && fki->alloc_char == *format_chars) + { + flag_chars.add_char (fki->alloc_char); + format_chars++; + } + + /* Handle the scanf allocation kludge. */ + if (fki->flags & (int) FMT_FLAG_SCANF_A_KLUDGE) + { + if (*format_chars == 'a' && !flag_isoc99) + { + if (format_chars[1] == 's' || format_chars[1] == 'S' + || format_chars[1] == '[') + { + /* 'a' is used as a flag. */ + flag_chars.add_char ('a'); + format_chars++; + } + } + } +} + +/* Look for length modifiers within the current format argument, + returning a length_modifier instance describing it (or the + default if one is not found). + + Issue warnings about non-standard modifiers. */ + +length_modifier +argument_parser::read_any_length_modifier () +{ + length_modifier result; + + const format_length_info *fli = fki->length_char_specs; + if (!fli) + return result; + + while (fli->name != 0 + && strncmp (fli->name, format_chars, strlen (fli->name))) + fli++; + if (fli->name != 0) + { + format_chars += strlen (fli->name); + if (fli->double_name != 0 && fli->name[0] == *format_chars) + { + format_chars++; + result = length_modifier (fli->double_name, fli->double_index, + fli->double_std, 0); + } + else + { + result = length_modifier (fli->name, fli->index, fli->std, + fli->scalar_identity_flag); + } + flag_chars.add_char (fki->length_code_char); + } + if (pedantic) + { + /* Warn if the length modifier is non-standard. */ + if (ADJ_STD (result.std) > C_STD_VER) + warning_at (format_string_loc, OPT_Wformat_, + "%s does not support the %qs %s length modifier", + C_STD_NAME (result.std), result.chars, + fki->name); + } + + return result; +} + +/* Read any other modifier (strftime E/O). */ + +void +argument_parser::read_any_other_modifier () +{ + if (fki->modifier_chars == NULL) + return; + + while (*format_chars != 0 + && strchr (fki->modifier_chars, *format_chars) != 0) + { + if (flag_chars.has_char_p (*format_chars)) + { + const format_flag_spec *s = get_flag_spec (flag_specs, + *format_chars, NULL); + format_warning_at_char (format_string_loc, format_string_cst, + format_chars - orig_format_chars, + OPT_Wformat_, + "repeated %s in format", _(s->name)); + } + else + flag_chars.add_char (*format_chars); + ++format_chars; + } +} + +/* Return the format_char_info corresponding to FORMAT_CHAR, + potentially issuing a warning if the format char is + not supported in the C standard version we are checking + against. + + Issue a warning and return NULL if it is not found. + + Issue warnings about non-standard modifiers. */ + +const format_char_info * +argument_parser::find_format_char_info (char format_char) +{ + const format_char_info *fci = fki->conversion_specs; + + while (fci->format_chars != 0 + && strchr (fci->format_chars, format_char) == 0) + ++fci; + if (fci->format_chars == 0) + { + format_warning_at_char (format_string_loc, format_string_cst, + format_chars - orig_format_chars, + OPT_Wformat_, + "unknown conversion type character %qc in format", + format_char); + return NULL; + } + + if (pedantic) + { + if (ADJ_STD (fci->std) > C_STD_VER) + format_warning_at_char (format_string_loc, format_string_cst, + format_chars - orig_format_chars, + OPT_Wformat_, + "%s does not support the %<%%%c%> %s format", + C_STD_NAME (fci->std), format_char, fki->name); + } + + return fci; +} + +/* Validate the pairs of flags used. + Issue warnings about incompatible combinations of flags. */ + +void +argument_parser::validate_flag_pairs (const format_char_info *fci, + char format_char) +{ + const format_flag_pair * const bad_flag_pairs = fki->bad_flag_pairs; + + for (int i = 0; bad_flag_pairs[i].flag_char1 != 0; i++) + { + const format_flag_spec *s, *t; + if (!flag_chars.has_char_p (bad_flag_pairs[i].flag_char1)) + continue; + if (!flag_chars.has_char_p (bad_flag_pairs[i].flag_char2)) + continue; + if (bad_flag_pairs[i].predicate != 0 + && strchr (fci->flags2, bad_flag_pairs[i].predicate) == 0) + continue; + s = get_flag_spec (flag_specs, bad_flag_pairs[i].flag_char1, NULL); + t = get_flag_spec (flag_specs, bad_flag_pairs[i].flag_char2, NULL); + if (bad_flag_pairs[i].ignored) + { + if (bad_flag_pairs[i].predicate != 0) + warning_at (format_string_loc, OPT_Wformat_, + "%s ignored with %s and %<%%%c%> %s format", + _(s->name), _(t->name), format_char, + fki->name); + else + warning_at (format_string_loc, OPT_Wformat_, + "%s ignored with %s in %s format", + _(s->name), _(t->name), fki->name); + } + else + { + if (bad_flag_pairs[i].predicate != 0) + warning_at (format_string_loc, OPT_Wformat_, + "use of %s and %s together with %<%%%c%> %s format", + _(s->name), _(t->name), format_char, + fki->name); + else + warning_at (format_string_loc, OPT_Wformat_, + "use of %s and %s together in %s format", + _(s->name), _(t->name), fki->name); + } + } +} + +/* Give Y2K warnings. */ + +void +argument_parser::give_y2k_warnings (const format_char_info *fci, + char format_char) +{ + if (!warn_format_y2k) + return; + + int y2k_level = 0; + if (strchr (fci->flags2, '4') != 0) + if (flag_chars.has_char_p ('E')) + y2k_level = 3; + else + y2k_level = 2; + else if (strchr (fci->flags2, '3') != 0) + y2k_level = 3; + else if (strchr (fci->flags2, '2') != 0) + y2k_level = 2; + if (y2k_level == 3) + warning_at (format_string_loc, OPT_Wformat_y2k, + "%<%%%c%> yields only last 2 digits of year in some locales", format_char); + else if (y2k_level == 2) + warning_at (format_string_loc, OPT_Wformat_y2k, + "%<%%%c%> yields only last 2 digits of year", + format_char); +} + +/* Parse any "scan sets" enclosed in square brackets, e.g. + for scanf-style calls. */ + +void +argument_parser::parse_any_scan_set (const format_char_info *fci) +{ + if (strchr (fci->flags2, '[') == NULL) + return; + + /* Skip over scan set, in case it happens to have '%' in it. */ + if (*format_chars == '^') + ++format_chars; + /* Find closing bracket; if one is hit immediately, then + it's part of the scan set rather than a terminator. */ + if (*format_chars == ']') + ++format_chars; + while (*format_chars && *format_chars != ']') + ++format_chars; + if (*format_chars != ']') + /* The end of the format string was reached. */ + format_warning_at_char (format_string_loc, format_string_cst, + format_chars - orig_format_chars, + OPT_Wformat_, + "no closing %<]%> for %<%%[%> format"); +} + +/* Return true if this argument is to be continued to be parsed, + false to skip to next argument. */ + +bool +argument_parser::handle_conversions (const format_char_info *fci, + const length_modifier &len_modifier, + tree &wanted_type, + const char *&wanted_type_name, + unsigned HOST_WIDE_INT &arg_num, + tree ¶ms, + char format_char) +{ + enum format_std_version wanted_type_std; + + if (!(fki->flags & (int) FMT_FLAG_ARG_CONVERT)) + return true; + + wanted_type = (fci->types[len_modifier.val].type + ? *fci->types[len_modifier.val].type : 0); + wanted_type_name = fci->types[len_modifier.val].name; + wanted_type_std = fci->types[len_modifier.val].std; + if (wanted_type == 0) + { + format_warning_at_char (format_string_loc, format_string_cst, + format_chars - orig_format_chars, + OPT_Wformat_, + "use of %qs length modifier with %qc type character has either no effect or undefined behavior", + len_modifier.chars, format_char); + /* Heuristic: skip one argument when an invalid length/type + combination is encountered. */ + arg_num++; + if (params != 0) + params = TREE_CHAIN (params); + return false; + } + else if (pedantic + /* Warn if non-standard, provided it is more non-standard + than the length and type characters that may already + have been warned for. */ + && ADJ_STD (wanted_type_std) > ADJ_STD (len_modifier.std) + && ADJ_STD (wanted_type_std) > ADJ_STD (fci->std)) + { + if (ADJ_STD (wanted_type_std) > C_STD_VER) + format_warning_at_char (format_string_loc, format_string_cst, + format_chars - orig_format_chars, + OPT_Wformat_, + "%s does not support the %<%%%s%c%> %s format", + C_STD_NAME (wanted_type_std), + len_modifier.chars, + format_char, fki->name); + } + + return true; +} + +/* Check type of argument against desired type. + + Return true if format parsing is to continue, false otherwise. */ + +bool +argument_parser:: +check_argument_type (const format_char_info *fci, + const struct kernel_ext_fmt *kef, + const length_modifier &len_modifier, + tree &wanted_type, + const char *&wanted_type_name, + const bool suppressed, + unsigned HOST_WIDE_INT &arg_num, + tree ¶ms, + const int alloc_flag, + const char * const format_start, + const char * const type_start, + location_t fmt_param_loc, + char conversion_char) +{ + if (info->first_arg_num == 0) + return true; + + if ((fci->pointer_count == 0 && wanted_type == void_type_node) + || suppressed) + { + if (main_arg_num != 0) + { + if (suppressed) + warning_at (format_string_loc, OPT_Wformat_, + "operand number specified with suppressed assignment"); + else + warning_at (format_string_loc, OPT_Wformat_, + "operand number specified for format taking no argument"); + } + } + else + { + format_wanted_type *wanted_type_ptr; + + if (main_arg_num != 0) + { + arg_num = main_arg_num; + params = main_arg_params; + } + else + { + ++arg_num; + if (has_operand_number > 0) + { + warning_at (format_string_loc, OPT_Wformat_, + "missing $ operand number in format"); + return false; + } + else + has_operand_number = 0; + } + + wanted_type_ptr = &main_wanted_type; + while (fci) + { + tree cur_param; + if (params == 0) + cur_param = NULL; + else + { + cur_param = TREE_VALUE (params); + params = TREE_CHAIN (params); + } + + wanted_type_ptr->wanted_type = wanted_type; + wanted_type_ptr->wanted_type_name = wanted_type_name; + wanted_type_ptr->pointer_count = fci->pointer_count + alloc_flag; + wanted_type_ptr->char_lenient_flag = 0; + if (strchr (fci->flags2, 'c') != 0) + wanted_type_ptr->char_lenient_flag = 1; + wanted_type_ptr->scalar_identity_flag = 0; + if (len_modifier.scalar_identity_flag) + wanted_type_ptr->scalar_identity_flag = 1; + wanted_type_ptr->writing_in_flag = 0; + wanted_type_ptr->reading_from_flag = 0; + if (alloc_flag) + wanted_type_ptr->writing_in_flag = 1; + else + { + if (strchr (fci->flags2, 'W') != 0) + wanted_type_ptr->writing_in_flag = 1; + if (strchr (fci->flags2, 'R') != 0) + wanted_type_ptr->reading_from_flag = 1; + } + wanted_type_ptr->kind = CF_KIND_FORMAT; + wanted_type_ptr->param = cur_param; + wanted_type_ptr->arg_num = arg_num; + wanted_type_ptr->format_start = format_start; + wanted_type_ptr->format_length = format_chars - format_start; + wanted_type_ptr->offset_loc = format_chars - orig_format_chars; + wanted_type_ptr->next = NULL; + if (last_wanted_type != 0) + last_wanted_type->next = wanted_type_ptr; + if (first_wanted_type == 0) + first_wanted_type = wanted_type_ptr; + last_wanted_type = wanted_type_ptr; + + fci = fci->chain; + if (fci) + { + wanted_type_ptr = fwt_pool.allocate (); + arg_num++; + wanted_type = *fci->types[len_modifier.val].type; + wanted_type_name = fci->types[len_modifier.val].name; + } + } + } + + if (first_wanted_type != 0) + { + ptrdiff_t offset_to_format_start = (start_of_this_format - 1) - orig_format_chars; + ptrdiff_t offset_to_format_end = (format_chars - 1) - orig_format_chars; + /* By default, use the end of the range for the caret location. */ + substring_loc fmt_loc (fmt_param_loc, TREE_TYPE (format_string_cst), + offset_to_format_end, + offset_to_format_start, offset_to_format_end); + ptrdiff_t offset_to_type_start = type_start - orig_format_chars; + check_format_types (fmt_loc, first_wanted_type, fki, + offset_to_type_start, + conversion_char, arglocs); + + /* note printf extension type checks are *additional* - %p must always + * be pointer compatible, %d always int compatible. + */ + if (!kef) + return true; + + const struct kernel_ext_fmt *kef_now; + bool success; + + for (kef_now = kef; kef_now->suffix && !strcmp (kef->suffix, kef_now->suffix); kef_now++) + { + success = check_kef_type (fmt_loc, kef_now, + first_wanted_type->arg_num, + first_wanted_type->param, + kef_now->type, fki, offset_to_type_start, conversion_char, arglocs); + + if (success) + return true; + } + + location_t param_loc; + + if (EXPR_HAS_LOCATION (first_wanted_type->param)) + param_loc = EXPR_LOCATION (first_wanted_type->param); + else if (arglocs) + { + /* arg_num is 1-based. */ + gcc_assert (first_wanted_type->arg_num > 0); + param_loc = (*arglocs)[first_wanted_type->arg_num - 1]; + } + + format_type_warning (fmt_loc, param_loc, first_wanted_type, + kef->type, TREE_TYPE (first_wanted_type->param), + fki, offset_to_type_start, conversion_char); + } + + return true; +} + +/* Do the main part of checking a call to a format function. FORMAT_CHARS + is the NUL-terminated format string (which at this point may contain + internal NUL characters); FORMAT_LENGTH is its length (excluding the + terminating NUL character). ARG_NUM is one less than the number of + the first format argument to check; PARAMS points to that format + argument in the list of arguments. */ + +static void +check_format_info_main (format_check_results *res, + function_format_info *info, const char *format_chars, + location_t fmt_param_loc, tree format_string_cst, + int format_length, tree params, + unsigned HOST_WIDE_INT arg_num, + object_allocator &fwt_pool, + vec *arglocs) +{ + const char * const orig_format_chars = format_chars; + const tree first_fillin_param = params; + + const format_kind_info * const fki = &format_types[info->format_type]; + const format_flag_spec * const flag_specs = fki->flag_specs; + const location_t format_string_loc = res->format_string_loc; + + /* -1 if no conversions taking an operand have been found; 0 if one has + and it didn't use $; 1 if $ formats are in use. */ + int has_operand_number = -1; + + /* Vector of pointers to opening quoting directives (like GCC "%<"). */ + auto_vec quotdirs; + + /* Pointers to the most recent color directives (like GCC's "%r or %R"). + A starting color directive much be terminated before the end of + the format string. A terminating directive makes no sense without + a prior starting directive. */ + const char *color_begin = NULL; + const char *color_end = NULL; + + init_dollar_format_checking (info->first_arg_num, first_fillin_param); + + while (*format_chars != 0) + { + if (*format_chars++ != '%') + continue; + if (*format_chars == 0) + { + format_warning_at_char (format_string_loc, format_string_cst, + format_chars - orig_format_chars, + OPT_Wformat_, + "spurious trailing %<%%%> in format"); + continue; + } + if (*format_chars == '%') + { + ++format_chars; + continue; + } + + flag_chars_t flag_chars; + argument_parser arg_parser (info, format_chars, format_string_cst, + orig_format_chars, format_string_loc, + flag_chars, has_operand_number, + first_fillin_param, fwt_pool, arglocs); + + if (!arg_parser.read_any_dollar ()) + return; + + if (!arg_parser.read_format_flags ()) + return; + + /* Read any format width, possibly * or *m$. */ + if (!arg_parser.read_any_format_width (params, arg_num)) + return; + + /* Read any format left precision (must be a number, not *). */ + arg_parser.read_any_format_left_precision (); + + /* Read any format precision, possibly * or *m$. */ + if (!arg_parser.read_any_format_precision (params, arg_num)) + return; + + const char *format_start = format_chars; + + arg_parser.handle_alloc_chars (); + + /* The rest of the conversion specification is the length modifier + (if any), and the conversion specifier, so this is where the + type information starts. If we need to issue a suggestion + about a type mismatch, then we should preserve everything up + to here. */ + const char *type_start = format_chars; + + /* Read any length modifier, if this kind of format has them. */ + const length_modifier len_modifier + = arg_parser.read_any_length_modifier (); + + /* Read any modifier (strftime E/O). */ + arg_parser.read_any_other_modifier (); + + char format_char = *format_chars; + if (format_char == 0 + || (!(fki->flags & (int) FMT_FLAG_FANCY_PERCENT_OK) + && format_char == '%')) + { + format_warning_at_char (format_string_loc, format_string_cst, + format_chars - orig_format_chars, + OPT_Wformat_, + "conversion lacks type at end of format"); + continue; + } + format_chars++; + + const format_char_info * const fci + = arg_parser.find_format_char_info (format_char); + if (!fci) + continue; + + struct kernel_ext_fmt *etab = fci->kernel_ext; + + if (etab && format_chars[0] >= 'A' && format_chars[0] <= 'Z') + { + struct kernel_ext_fmt *etab_end = etab + ETAB_SZ; + + for (; etab < etab_end && etab->suffix; etab++) + { + if (!strncmp (etab->suffix, format_chars, strlen (etab->suffix))) + break; + } + + if (!etab->suffix || etab == etab_end) + { + format_warning_at_char (format_string_loc, format_string_cst, + format_chars - orig_format_chars + 1, + OPT_Wformat_, + "unrecognized printf extension suffix"); + etab = NULL; + } + else + { + format_chars += strlen (etab->suffix); + } + } + else + etab = NULL; + + flag_chars.validate (fki, fci, flag_specs, format_chars, + format_string_cst, + format_string_loc, orig_format_chars, format_char, + quotdirs.length () > 0); + + const int alloc_flag = flag_chars.get_alloc_flag (fki); + const bool suppressed = flag_chars.assignment_suppression_p (fki); + + /* Diagnose nested or unmatched quoting directives such as GCC's + "%<...%<" and "%>...%>". */ + bool quot_begin_p = strchr (fci->flags2, '<'); + bool quot_end_p = strchr (fci->flags2, '>'); + + if (quot_begin_p && !quot_end_p) + { + if (quotdirs.length ()) + format_warning_at_char (format_string_loc, format_string_cst, + format_chars - orig_format_chars, + OPT_Wformat_, + "nested quoting directive"); + quotdirs.safe_push (format_chars); + } + else if (!quot_begin_p && quot_end_p) + { + if (quotdirs.length ()) + quotdirs.pop (); + else + format_warning_at_char (format_string_loc, format_string_cst, + format_chars - orig_format_chars, + OPT_Wformat_, + "unmatched quoting directive"); + } + + bool color_begin_p = strchr (fci->flags2, '/'); + if (color_begin_p) + { + color_begin = format_chars; + color_end = NULL; + } + else if (strchr (fci->flags2, '\\')) + { + if (color_end) + format_warning_at_char (format_string_loc, format_string_cst, + format_chars - orig_format_chars, + OPT_Wformat_, + "%qc directive redundant after prior occurence of the same", format_char); + else if (!color_begin) + format_warning_at_char (format_string_loc, format_string_cst, + format_chars - orig_format_chars, + OPT_Wformat_, + "unmatched color reset directive"); + color_end = format_chars; + } + + /* Diagnose directives that shouldn't appear in a quoted sequence. + (They are denoted by a double quote in FLAGS2.) */ + if (quotdirs.length ()) + { + if (strchr (fci->flags2, '"')) + format_warning_at_char (format_string_loc, format_string_cst, + format_chars - orig_format_chars, + OPT_Wformat_, + "%qc conversion used within a quoted sequence", + format_char); + } + + /* Validate the pairs of flags used. */ + arg_parser.validate_flag_pairs (fci, format_char); + + arg_parser.give_y2k_warnings (fci, format_char); + + arg_parser.parse_any_scan_set (fci); + + tree wanted_type = NULL; + const char *wanted_type_name = NULL; + + if (!arg_parser.handle_conversions (fci, len_modifier, + wanted_type, wanted_type_name, + arg_num, + params, + format_char)) + continue; + + arg_parser.main_wanted_type.next = NULL; + + /* Finally. . .check type of argument against desired type! */ + if (!arg_parser.check_argument_type (fci, etab, len_modifier, + wanted_type, wanted_type_name, + suppressed, + arg_num, params, + alloc_flag, + format_start, type_start, + fmt_param_loc, + format_char)) + return; + } + + if (format_chars - orig_format_chars != format_length) + format_warning_at_char (format_string_loc, format_string_cst, + format_chars + 1 - orig_format_chars, + OPT_Wformat_contains_nul, + "embedded %<\\0%> in format"); + if (info->first_arg_num != 0 && params != 0 + && has_operand_number <= 0) + { + res->number_other--; + res->number_extra_args++; + } + if (has_operand_number > 0) + finish_dollar_format_checking (res, fki->flags & (int) FMT_FLAG_DOLLAR_GAP_POINTER_OK); + + if (quotdirs.length ()) + format_warning_at_char (format_string_loc, format_string_cst, + quotdirs.pop () - orig_format_chars, + OPT_Wformat_, "unterminated quoting directive"); + if (color_begin && !color_end) + format_warning_at_char (format_string_loc, format_string_cst, + color_begin - orig_format_chars, + OPT_Wformat_, "unterminated color directive"); +} + +/* Check the argument types from a single format conversion (possibly + including width and precision arguments). + + FMT_LOC is the location of the format conversion. + + TYPES is a singly-linked list expressing the parts of the format + conversion that expect argument types, and the arguments they + correspond to. + + OFFSET_TO_TYPE_START is the offset within the execution-charset encoded + format string to where type information begins for the conversion + (the length modifier and conversion specifier). + + CONVERSION_CHAR is the user-provided conversion specifier. + + For example, given: + + sprintf (d, "before %-+*.*lld after", arg3, arg4, arg5); + + then FMT_LOC covers this range: + + sprintf (d, "before %-+*.*lld after", arg3, arg4, arg5); + ^^^^^^^^^ + + and TYPES in this case is a three-entry singly-linked list consisting of: + (1) the check for the field width here: + sprintf (d, "before %-+*.*lld after", arg3, arg4, arg5); + ^ ^^^^ + against arg3, and + (2) the check for the field precision here: + sprintf (d, "before %-+*.*lld after", arg3, arg4, arg5); + ^^ ^^^^ + against arg4, and + (3) the check for the length modifier and conversion char here: + sprintf (d, "before %-+*.*lld after", arg3, arg4, arg5); + ^^^ ^^^^ + against arg5. + + OFFSET_TO_TYPE_START is 13, the offset to the "lld" within the + STRING_CST: + + 0000000000111111111122 + 0123456789012345678901 + sprintf (d, "before %-+*.*lld after", arg3, arg4, arg5); + ^ ^ + | ` CONVERSION_CHAR: 'd' + type starts here. */ +tree type_normalize (tree type, tree *cousin, tree target = NULL) +{ + while (1) + { + if (TREE_CODE (type) == FUNCTION_TYPE || TREE_CODE (type) == POINTER_TYPE) + return type; + if (target) + /* Strip off any "const" etc. */ + type = build_qualified_type (type, 0); + if (TREE_CODE (TYPE_NAME (type)) != TYPE_DECL) + return type; + + if (target && (type == target || TYPE_NAME (type) == target)) + return target; + + struct type_special *t; + for (t = special_types; t->match; t++) + { + if (!*t->match) + continue; + if (TYPE_NAME (type) != *t->match) + continue; + if (t->cousin && *t->cousin) + *cousin = *t->cousin; + if (t->replace) + return *t->replace ? *t->replace : type; + return type; + } + + tree orig = DECL_ORIGINAL_TYPE (TYPE_NAME (type)); + if (!orig) + return type; + + type = orig; + } + return type; +} + +static void +check_format_types (const substring_loc &fmt_loc, + format_wanted_type *types, const format_kind_info *fki, + int offset_to_type_start, + char conversion_char, + vec *arglocs) +{ + for (; types != 0; types = types->next) + { + tree cur_param; + tree cur_type; + tree cur_type_cousin = NULL; + tree orig_cur_type; + tree wanted_type; + int arg_num; + int i; + int char_type_flag; + + wanted_type = types->wanted_type; + arg_num = types->arg_num; + + /* The following should not occur here. */ + gcc_assert (wanted_type); + gcc_assert (wanted_type != void_type_node || types->pointer_count); + + if (types->pointer_count == 0) + wanted_type = lang_hooks.types.type_promotes_to (wanted_type); + + switch (TREE_CODE (wanted_type)) + { + case IDENTIFIER_NODE: + break; + case TYPE_DECL: + wanted_type = TYPE_MAIN_VARIANT (DECL_ORIGINAL_TYPE (wanted_type)); + break; + default: + wanted_type = TYPE_MAIN_VARIANT (wanted_type); + break; + } + + cur_param = types->param; + if (!cur_param) + { + format_type_warning (fmt_loc, UNKNOWN_LOCATION, types, wanted_type, + NULL, fki, offset_to_type_start, + conversion_char); + continue; + } + + cur_type = TREE_TYPE (cur_param); + if (cur_type == error_mark_node) + continue; + orig_cur_type = cur_type; + char_type_flag = 0; + + location_t param_loc = UNKNOWN_LOCATION; + if (EXPR_HAS_LOCATION (cur_param)) + param_loc = EXPR_LOCATION (cur_param); + else if (arglocs) + { + /* arg_num is 1-based. */ + gcc_assert (types->arg_num > 0); + param_loc = (*arglocs)[types->arg_num - 1]; + } + + STRIP_NOPS (cur_param); + + /* Check the types of any additional pointer arguments + that precede the "real" argument. */ + for (i = 0; i < types->pointer_count; ++i) + { + if (TREE_CODE (cur_type) == POINTER_TYPE) + { + cur_type = TREE_TYPE (cur_type); + if (cur_type == error_mark_node) + break; + + /* Check for writing through a NULL pointer. */ + if (types->writing_in_flag + && i == 0 + && cur_param != 0 + && integer_zerop (cur_param)) + warning (OPT_Wformat_, "writing through null pointer (argument %d)", arg_num); + + /* Check for reading through a NULL pointer. */ + if (types->reading_from_flag + && i == 0 + && cur_param != 0 + && integer_zerop (cur_param)) + warning (OPT_Wformat_, "reading through null pointer (argument %d)", arg_num); + + if (cur_param != 0 && TREE_CODE (cur_param) == ADDR_EXPR) + cur_param = TREE_OPERAND (cur_param, 0); + else + cur_param = 0; + + /* See if this is an attempt to write into a const type with + scanf or with printf "%n". Note: the writing in happens + at the first indirection only, if for example + void * const * is passed to scanf %p; passing + const void ** is simply passing an incompatible type. */ + if (types->writing_in_flag + && i == 0 + && (TYPE_READONLY (cur_type) + || (cur_param != 0 + && (CONSTANT_CLASS_P (cur_param) + || (DECL_P (cur_param) + && TREE_READONLY (cur_param)))))) + warning (OPT_Wformat_, "writing into constant object (argument %d)", arg_num); + + /* If there are extra type qualifiers beyond the first + indirection, then this makes the types technically + incompatible. */ + if (i > 0 + && pedantic + && (TYPE_READONLY (cur_type) + || TYPE_VOLATILE (cur_type) + || TYPE_ATOMIC (cur_type) + || TYPE_RESTRICT (cur_type))) + warning (OPT_Wformat_, "extra type qualifiers in format argument (argument %d)", + arg_num); + + } + else + { + format_type_warning (fmt_loc, param_loc, + types, wanted_type, orig_cur_type, fki, + offset_to_type_start, conversion_char); + break; + } + } + + if (i < types->pointer_count) + continue; + + cur_type = type_normalize (cur_type, &cur_type_cousin); + + /* Check whether the argument type is a character type. This leniency + only applies to certain formats, flagged with 'c'. */ + if (types->char_lenient_flag) + char_type_flag = (cur_type == char_type_node + || cur_type == signed_char_type_node + || cur_type == unsigned_char_type_node); + + int compat = lang_hooks.types_compatible_p (wanted_type, cur_type); + /* Check the type of the "real" argument, if there's a type we want. */ + if ((TREE_CODE (wanted_type) != INTEGER_TYPE || types->pointer_count) + && compat) + continue; + if (TREE_CODE (wanted_type) == INTEGER_TYPE && !types->pointer_count + && compat) + { +compat_inner: + if (TREE_CODE (cur_param) == INTEGER_CST) + continue; + + if (TREE_CODE (types->wanted_type) == TYPE_DECL + && TREE_CODE (cur_type) == TYPE_DECL) + { + if (types->wanted_type == cur_type) + continue; + format_type_warning (fmt_loc, param_loc, types, + wanted_type, orig_cur_type, fki, + offset_to_type_start, conversion_char, + " (strict match required [A])"); + continue; + } + else if (TREE_CODE (types->wanted_type) == TYPE_DECL) + { + if (types->wanted_type == TYPE_NAME(cur_type)) + continue; + format_type_warning (fmt_loc, param_loc, types, + wanted_type, orig_cur_type, fki, + offset_to_type_start, conversion_char, + " (strict match required [B])"); + continue; + } + else if (wanted_type == cur_type) + continue; + else if (cur_type_cousin) + { + format_type_warning (fmt_loc, param_loc, types, + wanted_type, orig_cur_type, fki, + offset_to_type_start, conversion_char, + " (strict match required [C])"); + } + + /* + format_type_warning (fmt_loc, param_loc, types, + wanted_type, orig_cur_type, fki, + offset_to_type_start, conversion_char, + " (ultra-pedantic mode)"); + */ + continue; + } + + /* If we want 'void *', allow any pointer type. + (Anything else would already have got a warning.) + With -Wpedantic, only allow pointers to void and to character + types. */ + if (wanted_type == void_type_node + && (!pedantic || (i == 1 && char_type_flag))) + continue; + /* Don't warn about differences merely in signedness, unless + -Wpedantic. With -Wpedantic, warn if the type is a pointer + target and not a character type, and for character types at + a second level of indirection. */ + if (TREE_CODE (wanted_type) == INTEGER_TYPE + && TREE_CODE (cur_type) == INTEGER_TYPE + && ((!pedantic && !warn_format_signedness) + || (i == 0 && !warn_format_signedness) + || (i == 1 && char_type_flag)) + && (TYPE_UNSIGNED (wanted_type) + ? wanted_type == c_common_unsigned_type (cur_type) + : wanted_type == c_common_signed_type (cur_type))) + { + if (cur_type_cousin) + { + if (TREE_CODE (types->wanted_type) == TYPE_DECL + && TREE_CODE (cur_type_cousin) == TYPE_DECL) + { + if (types->wanted_type == cur_type_cousin) + continue; + format_type_warning (fmt_loc, param_loc, types, + wanted_type, orig_cur_type, fki, + offset_to_type_start, conversion_char, + " (strict match required [X])"); + continue; + } + else if (TREE_CODE (types->wanted_type) == TYPE_DECL) + { + if (types->wanted_type == TYPE_NAME(cur_type_cousin)) + continue; + format_type_warning (fmt_loc, param_loc, types, + wanted_type, orig_cur_type, fki, + offset_to_type_start, conversion_char, + " (strict match required [Y])"); + continue; + } + else if (wanted_type == cur_type_cousin) + continue; + else + { + format_type_warning (fmt_loc, param_loc, types, + wanted_type, orig_cur_type, fki, + offset_to_type_start, conversion_char, + " (strict match required [Z])"); + } + } + + goto compat_inner; + } + /* Don't warn about differences merely in signedness if we know + that the current type is integer-promoted and its original type + was unsigned such as that it is in the range of WANTED_TYPE. */ + if (TREE_CODE (wanted_type) == INTEGER_TYPE + && TREE_CODE (cur_type) == INTEGER_TYPE + && warn_format_signedness + && TYPE_UNSIGNED (wanted_type) + && cur_param != NULL_TREE + && TREE_CODE (cur_param) == NOP_EXPR) + { + tree t = TREE_TYPE (TREE_OPERAND (cur_param, 0)); + if (TYPE_UNSIGNED (t) + && cur_type == lang_hooks.types.type_promotes_to (t)) + continue; + } + /* Likewise, "signed char", "unsigned char" and "char" are + equivalent but the above test won't consider them equivalent. */ + if (wanted_type == char_type_node + && (!pedantic || i < 2) + && char_type_flag) + continue; + if (types->scalar_identity_flag + && (TREE_CODE (cur_type) == TREE_CODE (wanted_type) + || (INTEGRAL_TYPE_P (cur_type) + && INTEGRAL_TYPE_P (wanted_type))) + && TYPE_PRECISION (cur_type) == TYPE_PRECISION (wanted_type)) + continue; + /* Now we have a type mismatch. */ + format_type_warning (fmt_loc, param_loc, types, + wanted_type, orig_cur_type, fki, + offset_to_type_start, conversion_char); + } +} + +static bool +check_kef_type (const substring_loc &fmt_loc, + const struct kernel_ext_fmt *kef, + unsigned arg_num, + tree cur_param, + tree wanted_type, + const format_kind_info *fki, + int offset_to_type_start, + char conversion_char, + vec *arglocs) +{ + tree cur_type; + bool ok = true; + int i; + + /* The following should not occur here. */ + gcc_assert (wanted_type); + gcc_assert (wanted_type != void_type_node || kef->ptrlevel); + + if (TREE_CODE (wanted_type) == TYPE_DECL) + wanted_type = DECL_ORIGINAL_TYPE (wanted_type); + + if (!cur_param) + return false; + + cur_type = TREE_TYPE (cur_param); + if (cur_type == error_mark_node) + return false; + + location_t param_loc = UNKNOWN_LOCATION; + if (EXPR_HAS_LOCATION (cur_param)) + param_loc = EXPR_LOCATION (cur_param); + else if (arglocs) + { + /* arg_num is 1-based. */ + gcc_assert (arg_num > 0); + param_loc = (*arglocs)[arg_num - 1]; + } + (void)param_loc; + + STRIP_NOPS (cur_param); + + /* Check the types of any additional pointer arguments + that precede the "real" argument. */ + for (i = 0; i < kef->ptrlevel; ++i) + { + if (TREE_CODE (cur_type) == POINTER_TYPE) + { + cur_type = TREE_TYPE (cur_type); + if (cur_type == error_mark_node) + break; + + if (cur_param != 0 && TREE_CODE (cur_param) == ADDR_EXPR) + cur_param = TREE_OPERAND (cur_param, 0); + else + cur_param = 0; + + /* If there are extra type qualifiers beyond the first + indirection, then this makes the types technically + incompatible. */ + if (i > 0 + && pedantic + && (TYPE_READONLY (cur_type) + || TYPE_VOLATILE (cur_type) + || TYPE_ATOMIC (cur_type) + || TYPE_RESTRICT (cur_type))) + warning (OPT_Wformat_, "extra type qualifiers in format argument (argument %d)", + arg_num); + + } + else + { + ok = false; + break; + } + } + + if (i < kef->ptrlevel) + return ok; + + int compat = lang_hooks.types_compatible_p (wanted_type, cur_type); + + if (!compat) + return false; + + tree cousin; + tree normal_type; + + normal_type = type_normalize (cur_type, &cousin, wanted_type); + + return normal_type == wanted_type; +} + + +/* Given type TYPE, attempt to dereference the type N times + (e.g. from ("int ***", 2) to "int *") + + Return the derefenced type, with any qualifiers + such as "const" stripped from the result, or + NULL if unsuccessful (e.g. TYPE is not a pointer type). */ + +static tree +deref_n_times (tree type, int n) +{ + gcc_assert (type); + + for (int i = n; i > 0; i--) + { + if (TREE_CODE (type) != POINTER_TYPE) + return NULL_TREE; + type = TREE_TYPE (type); + } + /* Strip off any "const" etc. */ + return build_qualified_type (type, 0); +} + +/* Lookup the format code for FORMAT_LEN within FLI, + returning the string code for expressing it, or NULL + if it is not found. */ + +static const char * +get_modifier_for_format_len (const format_length_info *fli, + enum format_lengths format_len) +{ + for (; fli->name; fli++) + { + if (fli->index == format_len) + return fli->name; + if (fli->double_index == format_len) + return fli->double_name; + } + return NULL; +} + +#if CHECKING_P + +namespace selftest { + +static void +test_get_modifier_for_format_len () +{ + ASSERT_STREQ ("h", + get_modifier_for_format_len (printf_length_specs, FMT_LEN_h)); + ASSERT_STREQ ("hh", + get_modifier_for_format_len (printf_length_specs, FMT_LEN_hh)); + ASSERT_STREQ ("L", + get_modifier_for_format_len (printf_length_specs, FMT_LEN_L)); + ASSERT_EQ (NULL, + get_modifier_for_format_len (printf_length_specs, FMT_LEN_none)); +} + +} // namespace selftest + +#endif /* CHECKING_P */ + +/* Determine if SPEC_TYPE and ARG_TYPE are sufficiently similar for a + format_type_detail using SPEC_TYPE to be offered as a suggestion for + Wformat type errors where the argument has type ARG_TYPE. */ + +static bool +matching_type_p (tree spec_type, tree arg_type) +{ + gcc_assert (spec_type); + gcc_assert (arg_type); + + /* If any of the types requires structural equality, we can't compare + their canonical types. */ + if (TYPE_STRUCTURAL_EQUALITY_P (spec_type) + || TYPE_STRUCTURAL_EQUALITY_P (arg_type)) + return false; + + spec_type = TYPE_CANONICAL (spec_type); + arg_type = TYPE_CANONICAL (arg_type); + + if (TREE_CODE (spec_type) == INTEGER_TYPE + && TREE_CODE (arg_type) == INTEGER_TYPE + && (TYPE_UNSIGNED (spec_type) + ? spec_type == c_common_unsigned_type (arg_type) + : spec_type == c_common_signed_type (arg_type))) + return true; + + return spec_type == arg_type; +} + +/* Subroutine of get_format_for_type. + + Generate a string containing the length modifier and conversion specifier + that should be used to format arguments of type ARG_TYPE within FKI + (effectively the inverse of the checking code). + + If CONVERSION_CHAR is not zero (the first pass), the resulting suggestion + is required to use it, for correcting bogus length modifiers. + If CONVERSION_CHAR is zero (the second pass), then allow any suggestion + that matches ARG_TYPE. + + If successful, returns a non-NULL string which should be freed + by the caller. + Otherwise, returns NULL. */ + +static char * +get_format_for_type_1 (const format_kind_info *fki, tree arg_type, + char conversion_char) +{ + gcc_assert (arg_type); + + const format_char_info *spec; + for (spec = &fki->conversion_specs[0]; + spec->format_chars; + spec++) + { + if (conversion_char) + if (!strchr (spec->format_chars, conversion_char)) + continue; + + tree effective_arg_type = deref_n_times (arg_type, + spec->pointer_count); + if (!effective_arg_type) + continue; + for (int i = 0; i < FMT_LEN_MAX; i++) + { + const format_type_detail *ftd = &spec->types[i]; + if (!ftd->type) + continue; + if (matching_type_p (*ftd->type, effective_arg_type)) + { + const char *len_modifier + = get_modifier_for_format_len (fki->length_char_specs, + (enum format_lengths)i); + if (!len_modifier) + len_modifier = ""; + + if (conversion_char) + /* We found a match, using the given conversion char - the + length modifier was incorrect (or absent). + Provide a suggestion using the conversion char with the + correct length modifier for the type. */ + return xasprintf ("%s%c", len_modifier, conversion_char); + else + /* 2nd pass: no match was possible using the user-provided + conversion char, but we do have a match without using it. + Provide a suggestion using the first conversion char + listed for the given type. */ + return xasprintf ("%s%c", len_modifier, spec->format_chars[0]); + } + } + } + + return NULL; +} + +/* Generate a string containing the length modifier and conversion specifier + that should be used to format arguments of type ARG_TYPE within FKI + (effectively the inverse of the checking code). + + If successful, returns a non-NULL string which should be freed + by the caller. + Otherwise, returns NULL. */ + +static char * +get_format_for_type (const format_kind_info *fki, tree arg_type, + char conversion_char) +{ + gcc_assert (arg_type); + gcc_assert (conversion_char); + + /* First pass: look for a format_char_info containing CONVERSION_CHAR + If we find one, then presumably the length modifier was incorrect + (or absent). */ + char *result = get_format_for_type_1 (fki, arg_type, conversion_char); + if (result) + return result; + + /* Second pass: we didn't find a match for CONVERSION_CHAR, so try + matching just on the type. */ + return get_format_for_type_1 (fki, arg_type, '\0'); +} + +/* Attempt to get a string for use as a replacement fix-it hint for the + source range in FMT_LOC. + + Preserve all of the text within the range of FMT_LOC up to + OFFSET_TO_TYPE_START, replacing the rest with an appropriate + length modifier and conversion specifier for ARG_TYPE, attempting + to keep the user-provided CONVERSION_CHAR if possible. + + For example, given a long vs long long mismatch for arg5 here: + + 000000000111111111122222222223333333333| + 123456789012345678901234567890123456789` column numbers + 0000000000111111111122| + 0123456789012345678901` string offsets + V~~~~~~~~ : range of FMT_LOC, from cols 23-31 + sprintf (d, "before %-+*.*lld after", arg3, arg4, arg5); + ^ ^ + | ` CONVERSION_CHAR: 'd' + type starts here + + where OFFSET_TO_TYPE_START is 13 (the offset to the "lld" within the + STRING_CST), where the user provided: + %-+*.*lld + the result (assuming "long" argument 5) should be: + %-+*.*ld + + If successful, returns a non-NULL string which should be freed + by the caller. + Otherwise, returns NULL. */ + +static char * +get_corrected_substring (const substring_loc &fmt_loc, + format_wanted_type *type, tree arg_type, + const format_kind_info *fki, + int offset_to_type_start, char conversion_char) +{ + /* Attempt to provide hints for argument types, but not for field widths + and precisions. */ + if (!arg_type) + return NULL; + if (type->kind != CF_KIND_FORMAT) + return NULL; + + /* Locate the current code within the source range, rejecting + any awkward cases where the format string occupies more than + one line. + Lookup the place where the type starts (including any length + modifiers), getting it as the caret location. */ + substring_loc type_loc (fmt_loc); + type_loc.set_caret_index (offset_to_type_start); + + location_t fmt_substring_loc; + const char *err = type_loc.get_location (&fmt_substring_loc); + if (err) + return NULL; + + source_range fmt_substring_range + = get_range_from_loc (line_table, fmt_substring_loc); + + expanded_location caret + = expand_location_to_spelling_point (fmt_substring_loc); + expanded_location start + = expand_location_to_spelling_point (fmt_substring_range.m_start); + expanded_location finish + = expand_location_to_spelling_point (fmt_substring_range.m_finish); + if (caret.file != start.file) + return NULL; + if (start.file != finish.file) + return NULL; + if (caret.line != start.line) + return NULL; + if (start.line != finish.line) + return NULL; + if (start.column > caret.column) + return NULL; + if (start.column > finish.column) + return NULL; + if (caret.column > finish.column) + return NULL; + +#if BUILDING_GCC_VERSION >= 9000 + char_span line = location_get_source_line (start.file, start.line); + if (!line) + return NULL; + + /* If we got this far, then we have the line containing the + existing conversion specification. + + Generate a trimmed copy, containing the prefix part of the conversion + specification, up to the (but not including) the length modifier. + In the above example, this would be "%-+*.*". */ + int length_up_to_type = caret.column - start.column; + char_span prefix_span = line.subspan (start.column - 1, length_up_to_type); + char *prefix = prefix_span.xstrdup (); +#else + char *prefix = NULL; +#endif + + /* Now attempt to generate a suggestion for the rest of the specification + (length modifier and conversion char), based on ARG_TYPE and + CONVERSION_CHAR. + In the above example, this would be "ld". */ + char *format_for_type = get_format_for_type (fki, arg_type, conversion_char); + if (!format_for_type) + { + free (prefix); + return NULL; + } + + /* Success. Generate the resulting suggestion for the whole range of + FMT_LOC by concatenating the two strings. + In the above example, this would be "%-+*.*ld". */ + char *result = concat (prefix, format_for_type, NULL); + free (format_for_type); + free (prefix); + return result; +} + +/* Helper class for adding zero or more trailing '*' to types. + + The format type and name exclude any '*' for pointers, so those + must be formatted manually. For all the types we currently have, + this is adequate, but formats taking pointers to functions or + arrays would require the full type to be built up in order to + print it with %T. */ + +class indirection_suffix +{ + public: + indirection_suffix (int pointer_count) : m_pointer_count (pointer_count) {} + + /* Determine the size of the buffer (including NUL-terminator). */ + + size_t get_buffer_size () const + { + return m_pointer_count + 2; + } + + /* Write the '*' to DST and add a NUL-terminator. */ + + void fill_buffer (char *dst) const + { + if (m_pointer_count == 0) + dst[0] = 0; + else if (c_dialect_cxx ()) + { + memset (dst, '*', m_pointer_count); + dst[m_pointer_count] = 0; + } + else + { + dst[0] = ' '; + memset (dst + 1, '*', m_pointer_count); + dst[m_pointer_count + 1] = 0; + } + } + + private: + int m_pointer_count; +}; + +#if BUILDING_GCC_VERSION >= 9000 +/* not exported by GCC... need a local copy :( */ +class frr_range_label_for_type_mismatch : public range_label +{ + public: + frr_range_label_for_type_mismatch (tree labelled_type, tree other_type) + : m_labelled_type (labelled_type), m_other_type (other_type) + { + } + + label_text get_text (unsigned range_idx) const OVERRIDE; + + protected: + tree m_labelled_type; + tree m_other_type; +}; + +/* Print T to CPP. */ + +static void +print_type (c_pretty_printer *cpp, tree t, bool *quoted) +{ + gcc_assert (TYPE_P (t)); + struct obstack *ob = pp_buffer (cpp)->obstack; + char *p = (char *) obstack_base (ob); + /* Remember the end of the initial dump. */ + int len = obstack_object_size (ob); + + tree name = TYPE_NAME (t); + if (name && TREE_CODE (name) == TYPE_DECL && DECL_NAME (name)) + pp_identifier (cpp, lang_hooks.decl_printable_name (name, 2)); + else + cpp->type_id (t); + + /* If we're printing a type that involves typedefs, also print the + stripped version. But sometimes the stripped version looks + exactly the same, so we don't want it after all. To avoid + printing it in that case, we play ugly obstack games. */ + if (TYPE_CANONICAL (t) && t != TYPE_CANONICAL (t)) + { + c_pretty_printer cpp2; + /* Print the stripped version into a temporary printer. */ + cpp2.type_id (TYPE_CANONICAL (t)); + struct obstack *ob2 = cpp2.buffer->obstack; + /* Get the stripped version from the temporary printer. */ + const char *aka = (char *) obstack_base (ob2); + int aka_len = obstack_object_size (ob2); + int type1_len = obstack_object_size (ob) - len; + + /* If they are identical, bail out. */ + if (aka_len == type1_len && memcmp (p + len, aka, aka_len) == 0) + return; + + /* They're not, print the stripped version now. */ + if (*quoted) + pp_end_quote (cpp, pp_show_color (cpp)); + pp_c_whitespace (cpp); + pp_left_brace (cpp); + pp_c_ws_string (cpp, _("aka")); + pp_c_whitespace (cpp); + if (*quoted) + pp_begin_quote (cpp, pp_show_color (cpp)); + cpp->type_id (TYPE_CANONICAL (t)); + if (*quoted) + pp_end_quote (cpp, pp_show_color (cpp)); + pp_right_brace (cpp); + /* No further closing quotes are needed. */ + *quoted = false; + } +} + +/* C-specific implementation of range_label::get_text () vfunc for + range_label_for_type_mismatch. */ +#if BUILDING_GCC_VERSION >= 10000 +#define label_borrow(text) label_text::borrow(text) +#define label_take(text) label_text::take(text) +#else +#define label_borrow(text) label_text((char *)text, false) +#define label_take(text) label_text(text, true) +#endif + +label_text +frr_range_label_for_type_mismatch::get_text (unsigned /*range_idx*/) const +{ + if (m_labelled_type == NULL_TREE) + return label_borrow("(null tree)"); + + c_pretty_printer cpp; + bool quoted = false; + print_type (&cpp, m_labelled_type, "ed); + return label_take(xstrdup (pp_formatted_text (&cpp))); +} + +#define range_label_for_type_mismatch frr_range_label_for_type_mismatch +#endif + +/* Subclass of range_label for labelling the range in the format string + with the type in question, adding trailing '*' for pointer_count. */ + +class range_label_for_format_type_mismatch + : public range_label_for_type_mismatch +{ + public: + range_label_for_format_type_mismatch (tree labelled_type, tree other_type, + int pointer_count) + : range_label_for_type_mismatch (labelled_type, other_type), + m_pointer_count (pointer_count) + { + } + + label_text get_text (unsigned range_idx) const FINAL OVERRIDE + { + label_text text = range_label_for_type_mismatch::get_text (range_idx); + if (text.m_buffer == NULL) + return text; + + indirection_suffix suffix (m_pointer_count); + char *p = (char *) alloca (suffix.get_buffer_size ()); + suffix.fill_buffer (p); + + char *result = concat (text.m_buffer, p, NULL); + text.maybe_free (); + return label_take(result); + } + + private: + int m_pointer_count; +}; + +/* Give a warning about a format argument of different type from that expected. + The range of the diagnostic is taken from WHOLE_FMT_LOC; the caret location + is based on the location of the char at TYPE->offset_loc. + PARAM_LOC is the location of the relevant argument, or UNKNOWN_LOCATION + if this is unavailable. + WANTED_TYPE is the type the argument should have, + possibly stripped of pointer dereferences. The description (such as "field + precision"), the placement in the format string, a possibly more + friendly name of WANTED_TYPE, and the number of pointer dereferences + are taken from TYPE. ARG_TYPE is the type of the actual argument, + or NULL if it is missing. + + OFFSET_TO_TYPE_START is the offset within the execution-charset encoded + format string to where type information begins for the conversion + (the length modifier and conversion specifier). + CONVERSION_CHAR is the user-provided conversion specifier. + + For example, given a type mismatch for argument 5 here: + + 00000000011111111112222222222333333333344444444445555555555| + 12345678901234567890123456789012345678901234567890123456789` column numbers + 0000000000111111111122| + 0123456789012345678901` offsets within STRING_CST + V~~~~~~~~ : range of WHOLE_FMT_LOC, from cols 23-31 + sprintf (d, "before %-+*.*lld after", int_expr, int_expr, long_expr); + ^ ^ ^~~~~~~~~ + | ` CONVERSION_CHAR: 'd' PARAM_LOC + type starts here + + OFFSET_TO_TYPE_START is 13, the offset to the "lld" within the + STRING_CST. */ + +static void +format_type_warning (const substring_loc &whole_fmt_loc, + location_t param_loc, + format_wanted_type *type, + tree wanted_type, tree arg_type, + const format_kind_info *fki, + int offset_to_type_start, + char conversion_char, + const char *extra) +{ + enum format_specifier_kind kind = type->kind; + const char *wanted_type_name = type->wanted_type_name; + const char *format_start = type->format_start; + int format_length = type->format_length; + int pointer_count = type->pointer_count; + int arg_num = type->arg_num; + + if (!extra) + extra = ""; + + /* If ARG_TYPE is a typedef with a misleading name (for example, + size_t but not the standard size_t expected by printf %zu), avoid + printing the typedef name. */ + if (wanted_type_name + && arg_type + && TYPE_NAME (arg_type) + && TREE_CODE (TYPE_NAME (arg_type)) == TYPE_DECL + && DECL_NAME (TYPE_NAME (arg_type)) + && !strcmp (wanted_type_name, + lang_hooks.decl_printable_name (TYPE_NAME (arg_type), 2))) + arg_type = TYPE_MAIN_VARIANT (arg_type); + + indirection_suffix suffix (pointer_count); + char *p = (char *) alloca (suffix.get_buffer_size ()); + suffix.fill_buffer (p); + + /* WHOLE_FMT_LOC has the caret at the end of the range. + Set the caret to be at the offset from TYPE. Subtract one + from the offset for the same reason as in format_warning_at_char. */ + substring_loc fmt_loc (whole_fmt_loc); + fmt_loc.set_caret_index (type->offset_loc - 1); + +#if BUILDING_GCC_VERSION >= 9000 + range_label_for_format_type_mismatch fmt_label (wanted_type, arg_type, + pointer_count); + range_label_for_type_mismatch param_label (arg_type, wanted_type); + + /* Get a string for use as a replacement fix-it hint for the range in + fmt_loc, or NULL. */ + char *corrected_substring + = get_corrected_substring (fmt_loc, type, arg_type, fki, + offset_to_type_start, conversion_char); + format_string_diagnostic_t diag (fmt_loc, &fmt_label, param_loc, ¶m_label, + corrected_substring); +# define format_warning_at_substring(a,b,c,d,e,...) \ + diag.emit_warning(__VA_ARGS__) +#else +# define format_warning_at_substring(a,b,c,d,...) \ + format_warning_at_substring(a,c,__VA_ARGS__) + /* Get a string for use as a replacement fix-it hint for the range in + fmt_loc, or NULL. */ + char *corrected_substring + = get_corrected_substring (fmt_loc, type, arg_type, fki, + offset_to_type_start, conversion_char); + +#endif + + if (wanted_type_name) + { + if (arg_type) + format_warning_at_substring + (fmt_loc, &fmt_label, param_loc, ¶m_label, + corrected_substring, OPT_Wformat_, + "%s %<%s%.*s%> expects argument of type %<%s%s%>, but argument %d has type %qT%s", + gettext (kind_descriptions[kind]), + (kind == CF_KIND_FORMAT ? "%" : ""), + format_length, format_start, + wanted_type_name, p, arg_num, arg_type, extra); + else + format_warning_at_substring + (fmt_loc, &fmt_label, param_loc, ¶m_label, + corrected_substring, OPT_Wformat_, + "%s %<%s%.*s%> expects a matching %<%s%s%> argument%s", + gettext (kind_descriptions[kind]), + (kind == CF_KIND_FORMAT ? "%" : ""), + format_length, format_start, wanted_type_name, p, extra); + } + else + { + if (arg_type) + format_warning_at_substring + (fmt_loc, &fmt_label, param_loc, ¶m_label, + corrected_substring, OPT_Wformat_, + "%s %<%s%.*s%> expects argument of type %<%T%s%>, but argument %d has type %qT%s", + gettext (kind_descriptions[kind]), + (kind == CF_KIND_FORMAT ? "%" : ""), + format_length, format_start, + wanted_type, p, arg_num, arg_type, extra); + else + format_warning_at_substring + (fmt_loc, &fmt_label, param_loc, ¶m_label, + corrected_substring, OPT_Wformat_, + "%s %<%s%.*s%> expects a matching %<%T%s%> argument%s", + gettext (kind_descriptions[kind]), + (kind == CF_KIND_FORMAT ? "%" : ""), + format_length, format_start, wanted_type, p, extra); + } + + free (corrected_substring); +} + + +#if 0 +/* Given a format_char_info array FCI, and a character C, this function + returns the index into the conversion_specs where that specifier's + data is located. The character must exist. */ +static unsigned int +find_char_info_specifier_index (const format_char_info *fci, int c) +{ + unsigned i; + + for (i = 0; fci->format_chars; i++, fci++) + if (strchr (fci->format_chars, c)) + return i; + + /* We shouldn't be looking for a non-existent specifier. */ + gcc_unreachable (); +} + +/* Given a format_length_info array FLI, and a character C, this + function returns the index into the conversion_specs where that + modifier's data is located. The character must exist. */ +static unsigned int +find_length_info_modifier_index (const format_length_info *fli, int c) +{ + unsigned i; + + for (i = 0; fli->name; i++, fli++) + if (strchr (fli->name, c)) + return i; + + /* We shouldn't be looking for a non-existent modifier. */ + gcc_unreachable (); +} +#endif + +#ifdef TARGET_FORMAT_TYPES +extern const format_kind_info TARGET_FORMAT_TYPES[]; +#endif + +#ifdef TARGET_OVERRIDES_FORMAT_ATTRIBUTES +extern const target_ovr_attr TARGET_OVERRIDES_FORMAT_ATTRIBUTES[]; +#endif +#ifdef TARGET_OVERRIDES_FORMAT_INIT + extern void TARGET_OVERRIDES_FORMAT_INIT (void); +#endif + +/* Attributes such as "printf" are equivalent to those such as + "gnu_printf" unless this is overridden by a target. */ +static const target_ovr_attr gnu_target_overrides_format_attributes[] = +{ + { NULL, NULL } +}; + +/* Translate to unified attribute name. This is used in decode_format_type and + decode_format_attr. In attr_name the user specified argument is passed. It + returns the unified format name from TARGET_OVERRIDES_FORMAT_ATTRIBUTES + or the attr_name passed to this function, if there is no matching entry. */ +static const char * +convert_format_name_to_system_name (const char *attr_name) +{ + int i; + + if (attr_name == NULL || *attr_name == 0 + || strncmp (attr_name, "gcc_", 4) == 0) + return attr_name; +#ifdef TARGET_OVERRIDES_FORMAT_INIT + TARGET_OVERRIDES_FORMAT_INIT (); +#endif + +#ifdef TARGET_OVERRIDES_FORMAT_ATTRIBUTES + /* Check if format attribute is overridden by target. */ + if (TARGET_OVERRIDES_FORMAT_ATTRIBUTES != NULL + && TARGET_OVERRIDES_FORMAT_ATTRIBUTES_COUNT > 0) + { + for (i = 0; i < TARGET_OVERRIDES_FORMAT_ATTRIBUTES_COUNT; ++i) + { + if (cmp_attribs (TARGET_OVERRIDES_FORMAT_ATTRIBUTES[i].named_attr_src, + attr_name)) + return attr_name; + if (cmp_attribs (TARGET_OVERRIDES_FORMAT_ATTRIBUTES[i].named_attr_dst, + attr_name)) + return TARGET_OVERRIDES_FORMAT_ATTRIBUTES[i].named_attr_src; + } + } +#endif + /* Otherwise default to gnu format. */ + for (i = 0; + gnu_target_overrides_format_attributes[i].named_attr_src != NULL; + ++i) + { + if (cmp_attribs (gnu_target_overrides_format_attributes[i].named_attr_src, + attr_name)) + return attr_name; + if (cmp_attribs (gnu_target_overrides_format_attributes[i].named_attr_dst, + attr_name)) + return gnu_target_overrides_format_attributes[i].named_attr_src; + } + + return attr_name; +} + +/* Handle a "format" attribute; arguments as in + struct attribute_spec.handler. */ +tree +handle_frr_format_attribute (tree *node, tree ARG_UNUSED (name), tree args, + int flags, bool *no_add_attrs) +{ + tree type = *node; + function_format_info info; + + /* Canonicalize name of format function. */ + if (TREE_CODE (TREE_VALUE (args)) == IDENTIFIER_NODE) + TREE_VALUE (args) = canonicalize_attr_name (TREE_VALUE (args)); + + if (!decode_format_attr (args, &info, 0)) + { + *no_add_attrs = true; + return NULL_TREE; + } + + if (prototype_p (type)) + { + if (!check_format_string (type, info.format_num, flags, + no_add_attrs, info.format_type)) + return NULL_TREE; + + if (info.first_arg_num != 0) + { + unsigned HOST_WIDE_INT arg_num = 1; + function_args_iterator iter; + tree arg_type; + + /* Verify that first_arg_num points to the last arg, + the ... */ + FOREACH_FUNCTION_ARGS (type, arg_type, iter) + arg_num++; + + if (arg_num != info.first_arg_num) + { + if (!(flags & (int) ATTR_FLAG_BUILT_IN)) + error ("arguments to be formatted is not %<...%>"); + *no_add_attrs = true; + return NULL_TREE; + } + } + } + + /* Check if this is a strftime variant. Just for this variant + FMT_FLAG_ARG_CONVERT is not set. */ + if ((format_types[info.format_type].flags & (int) FMT_FLAG_ARG_CONVERT) == 0 + && info.first_arg_num != 0) + { + error ("strftime formats cannot format arguments"); + *no_add_attrs = true; + return NULL_TREE; + } + + return NULL_TREE; +} + +#if CHECKING_P + +namespace selftest { + +/* Selftests of location handling. */ + +/* Get the format_kind_info with the given name. */ + +static const format_kind_info * +get_info (const char *name) +{ + int idx = decode_format_type (name); + const format_kind_info *fki = &format_types[idx]; + ASSERT_STREQ (fki->name, name); + return fki; +} + +/* Verify that get_format_for_type (FKI, TYPE, CONVERSION_CHAR) + is EXPECTED_FORMAT. */ + +static void +assert_format_for_type_streq (const location &loc, const format_kind_info *fki, + const char *expected_format, tree type, + char conversion_char) +{ + gcc_assert (fki); + gcc_assert (expected_format); + gcc_assert (type); + + char *actual_format = get_format_for_type (fki, type, conversion_char); + ASSERT_STREQ_AT (loc, expected_format, actual_format); + free (actual_format); +} + +/* Selftests for get_format_for_type. */ + +#define ASSERT_FORMAT_FOR_TYPE_STREQ(EXPECTED_FORMAT, TYPE, CONVERSION_CHAR) \ + assert_format_for_type_streq (SELFTEST_LOCATION, (fki), (EXPECTED_FORMAT), \ + (TYPE), (CONVERSION_CHAR)) + +/* Selftest for get_format_for_type for "printf"-style functions. */ + +static void +test_get_format_for_type_printf () +{ + const format_kind_info *fki = get_info ("gnu_printf"); + ASSERT_NE (fki, NULL); + + ASSERT_FORMAT_FOR_TYPE_STREQ ("f", double_type_node, 'i'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("Lf", long_double_type_node, 'i'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("f", double_type_node, 'o'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("Lf", long_double_type_node, 'o'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("f", double_type_node, 'x'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("Lf", long_double_type_node, 'x'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("f", double_type_node, 'X'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("Lf", long_double_type_node, 'X'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("d", integer_type_node, 'd'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("i", integer_type_node, 'i'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("o", integer_type_node, 'o'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("x", integer_type_node, 'x'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("X", integer_type_node, 'X'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("d", unsigned_type_node, 'd'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("i", unsigned_type_node, 'i'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("o", unsigned_type_node, 'o'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("x", unsigned_type_node, 'x'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("X", unsigned_type_node, 'X'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("ld", long_integer_type_node, 'd'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("li", long_integer_type_node, 'i'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("lx", long_integer_type_node, 'x'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("lo", long_unsigned_type_node, 'o'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("lx", long_unsigned_type_node, 'x'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("lld", long_long_integer_type_node, 'd'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("lli", long_long_integer_type_node, 'i'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("llo", long_long_unsigned_type_node, 'o'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("llx", long_long_unsigned_type_node, 'x'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("s", build_pointer_type (char_type_node), 'i'); +} + +/* Selftest for get_format_for_type for "scanf"-style functions. */ + +static void +test_get_format_for_type_scanf () +{ + const format_kind_info *fki = get_info ("gnu_scanf"); + ASSERT_NE (fki, NULL); + ASSERT_FORMAT_FOR_TYPE_STREQ ("d", build_pointer_type (integer_type_node), 'd'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("u", build_pointer_type (unsigned_type_node), 'u'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("ld", + build_pointer_type (long_integer_type_node), 'd'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("lu", + build_pointer_type (long_unsigned_type_node), 'u'); + ASSERT_FORMAT_FOR_TYPE_STREQ + ("lld", build_pointer_type (long_long_integer_type_node), 'd'); + ASSERT_FORMAT_FOR_TYPE_STREQ + ("llu", build_pointer_type (long_long_unsigned_type_node), 'u'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("e", build_pointer_type (float_type_node), 'e'); + ASSERT_FORMAT_FOR_TYPE_STREQ ("le", build_pointer_type (double_type_node), 'e'); +} + +#undef ASSERT_FORMAT_FOR_TYPE_STREQ + +/* Exercise the type-printing label code, to give some coverage + under "make selftest-valgrind" (in particular, to ensure that + the label-printing machinery doesn't leak). */ + +static void +test_type_mismatch_range_labels () +{ + /* Create a tempfile and write some text to it. + ....................0000000001 11111111 12 22222222 + ....................1234567890 12345678 90 12345678. */ + const char *content = " printf (\"msg: %i\\n\", msg);\n"; + temp_source_file tmp (SELFTEST_LOCATION, ".c", content); + line_table_test ltt; + + linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1); + + location_t c17 = linemap_position_for_column (line_table, 17); + ASSERT_EQ (LOCATION_COLUMN (c17), 17); + location_t c18 = linemap_position_for_column (line_table, 18); + location_t c24 = linemap_position_for_column (line_table, 24); + location_t c26 = linemap_position_for_column (line_table, 26); + + /* Don't attempt to run the tests if column data might be unavailable. */ + if (c26 > LINE_MAP_MAX_LOCATION_WITH_COLS) + return; + + location_t fmt = make_location (c18, c17, c18); + ASSERT_EQ (LOCATION_COLUMN (fmt), 18); + + location_t param = make_location (c24, c24, c26); + ASSERT_EQ (LOCATION_COLUMN (param), 24); + + range_label_for_format_type_mismatch fmt_label (char_type_node, + integer_type_node, 1); + range_label_for_type_mismatch param_label (integer_type_node, + char_type_node); + gcc_rich_location richloc (fmt, &fmt_label); + richloc.add_range (param, SHOW_RANGE_WITHOUT_CARET, ¶m_label); + + test_diagnostic_context dc; + diagnostic_show_locus (&dc, &richloc, DK_ERROR); + if (c_dialect_cxx ()) + /* "char*", without a space. */ + ASSERT_STREQ ("\n" + " printf (\"msg: %i\\n\", msg);\n" + " ~^ ~~~\n" + " | |\n" + " char* int\n", + pp_formatted_text (dc.printer)); + else + /* "char *", with a space. */ + ASSERT_STREQ ("\n" + " printf (\"msg: %i\\n\", msg);\n" + " ~^ ~~~\n" + " | |\n" + " | int\n" + " char *\n", + pp_formatted_text (dc.printer)); +} + +/* Run all of the selftests within this file. */ + +void +c_format_c_tests () +{ + test_get_modifier_for_format_len (); + test_get_format_for_type_printf (); + test_get_format_for_type_scanf (); + test_type_mismatch_range_labels (); +} + +} // namespace selftest + +#endif /* CHECKING_P */ + +// include "gt-c-family-c-format.h" + +static const struct attribute_spec frr_format_attribute_table[] = +{ + /* { name, min_len, max_len, decl_req, type_req, fn_type_req, + affects_type_identity, handler, exclude } */ + { "frr_format", 3, 3, false, true, true, false, + handle_frr_format_attribute, NULL }, + { "frr_format_arg", 1, 1, false, true, true, false, + handle_frr_format_arg_attribute, NULL }, + { NULL, 0, 0, false, false, false, false, NULL, NULL } +}; + +static void +register_attributes (void *event_data, void *data) +{ + // warning (0, G_("Callback to register attributes")); + register_attribute (frr_format_attribute_table); +} + +tree +cb_walk_tree_fn (tree * tp, int * walk_subtrees, void * data ATTRIBUTE_UNUSED) +{ + if (TREE_CODE (*tp) != CALL_EXPR) + return NULL_TREE; + + tree call_expr = *tp; + + int nargs = call_expr_nargs(call_expr); + tree fn = CALL_EXPR_FN(call_expr); + + if (!fn || TREE_CODE (fn) != ADDR_EXPR) + return NULL_TREE; + + tree fndecl = TREE_OPERAND (fn, 0); + if (TREE_CODE (fndecl) != FUNCTION_DECL) + return NULL_TREE; + +#if 0 + warning (0, G_("function call to %s, %d args"), + IDENTIFIER_POINTER (DECL_NAME (fndecl)), + nargs); +#endif + + tree *fargs = (tree *) alloca (nargs * sizeof (tree)); + + for (int j = 0; j < nargs; j++) + { + tree arg = CALL_EXPR_ARG(call_expr, j); + + /* For -Wformat undo the implicit passing by hidden reference + done by convert_arg_to_ellipsis. */ + if (TREE_CODE (arg) == ADDR_EXPR + && TREE_CODE (TREE_TYPE (arg)) == REFERENCE_TYPE) + fargs[j] = TREE_OPERAND (arg, 0); + else + fargs[j] = arg; + } + + check_function_format (TYPE_ATTRIBUTES (TREE_TYPE (fndecl)), nargs, fargs, NULL); + return NULL_TREE; +} + +static void +setup_type (const char *name, tree *dst) +{ + tree tmp; + + if (*dst && *dst != void_type_node) + return; + + *dst = maybe_get_identifier (name); + if (!*dst) + return; + + tmp = identifier_global_value (*dst); + if (tmp && TREE_CODE (tmp) != TYPE_DECL) + { + warning (0, "%qs is not defined as a type", name); + *dst = NULL; + return; + } + if (tmp && TREE_CODE (tmp) == TYPE_DECL) + *dst = tmp; + else + *dst = NULL; +} + +static void +handle_finish_parse (void *event_data, void *data) +{ + tree fndecl = (tree) event_data; + gcc_assert (TREE_CODE (fndecl) == FUNCTION_DECL); + + setup_type ("uint64_t", &local_uint64_t_node); + setup_type ("int64_t", &local_int64_t_node); + + setup_type ("size_t", &local_size_t_node); + setup_type ("ssize_t", &local_ssize_t_node); + setup_type ("atomic_size_t", &local_atomic_size_t_node); + setup_type ("atomic_ssize_t", &local_atomic_ssize_t_node); + setup_type ("ptrdiff_t", &local_ptrdiff_t_node); + + setup_type ("pid_t", &local_pid_t_node); + setup_type ("uid_t", &local_uid_t_node); + setup_type ("gid_t", &local_gid_t_node); + setup_type ("time_t", &local_time_t_node); + + setup_type ("socklen_t", &local_socklen_t_node); + setup_type ("in_addr_t", &local_in_addr_t_node); + + const format_char_info *fci; + + for (fci = print_char_table; fci->format_chars; fci++) + { + if (!fci->kernel_ext) + continue; + + struct kernel_ext_fmt *etab = fci->kernel_ext; + struct kernel_ext_fmt *etab_end = etab + ETAB_SZ; + + for (; etab->suffix && etab < etab_end; etab++) + { + tree identifier, node; + + if (etab->type && etab->type != void_type_node) + continue; + + identifier = maybe_get_identifier (etab->type_str); + + if (!identifier || identifier == error_mark_node) + continue; + + if (etab->type_code) + { + node = identifier_global_tag (identifier); + if (!node) + continue; + + if (node->base.code != etab->type_code) + { + if (!etab->warned) + { + warning (0, "%qs tag category (struct/union/enum) mismatch", etab->type_str); + etab->warned = true; + } + continue; + } + } + else + { + node = identifier_global_value (identifier); + if (!node) + continue; + + if (TREE_CODE (node) != TYPE_DECL) + { + if (!etab->warned) + { + warning (0, "%qs is defined as a non-type", etab->type_str); + etab->warned = true; + } + continue; + } + node = TREE_TYPE (node); + } + + etab->type = node; + } + } + + walk_tree (&DECL_SAVED_TREE (fndecl), cb_walk_tree_fn, NULL, NULL); +} + +static void +handle_pragma_printfrr_ext (cpp_reader *dummy) +{ + tree token = 0; + location_t loc; + enum cpp_ttype ttype; + + ttype = pragma_lex (&token, &loc); + if (ttype != CPP_STRING) + { + error_at (loc, "%<#pragma FRR printfrr_ext%> requires string argument"); + return; + } + + const char *s = TREE_STRING_POINTER (token); + + if (s[0] != '%') + { + error_at (loc, "%<#pragma FRR printfrr_ext%>: invalid format string, needs to start with '%%'"); + return; + } + + switch (s[1]) + { + case 'p': + case 'd': + case 'i': + break; + default: + error_at (loc, "%<#pragma FRR printfrr_ext%>: invalid format string, needs to be %%p, %%d or %%i"); + return; + } + + const format_char_info *fci; + + for (fci = print_char_table; fci->format_chars; fci++) + if (strchr (fci->format_chars, s[1])) + break; + + gcc_assert (fci->format_chars); + gcc_assert (fci->kernel_ext); + + struct kernel_ext_fmt *etab = fci->kernel_ext; + struct kernel_ext_fmt *etab_end = etab + ETAB_SZ; + + switch (s[2]) + { + case 'A' ... 'Z': + break; + + default: + error_at (loc, "%<#pragma FRR printfrr_ext%>: invalid format string, suffix must start with an uppercase letter"); + return; + } + + /* -2 -- need to keep the sentinel at the end */ + if (etab[ETAB_SZ - 2].suffix) + { + error_at (loc, "%<#pragma FRR printfrr_ext%>: out of space for format suffixes"); + return; + } + + for (; etab->suffix && etab < etab_end; etab++) + { + if (!strcmp(s + 2, etab->suffix)) + { + memmove (etab + 1, etab, (etab_end - etab - 1) * sizeof (*etab)); + + if (0) + { + warning_at (loc, OPT_Wformat_, + "%<#pragma FRR printfrr_ext%>: duplicate printf format suffix %qs", s); + warning_at (etab->origin_loc, OPT_Wformat_, + "%<#pragma FRR printfrr_ext%>: previous definition was here"); + return; + } + + break; + } + + if (!strncmp(s + 2, etab->suffix, MIN(strlen(s + 2), strlen(etab->suffix)))) + { + warning_at (loc, OPT_Wformat_, + "%<#pragma FRR printfrr_ext%>: overlapping printf format suffix %qs", s); + warning_at (etab->origin_loc, OPT_Wformat_, + "%<#pragma FRR printfrr_ext%>: previous definition for %<%%%c%s%> was here", s[1], etab->suffix); + return; + } + } + + gcc_assert (etab < etab_end); + + memset (etab, 0, sizeof (*etab)); + etab->suffix = xstrdup(s + 2); + etab->origin_loc = loc; + etab->type = void_type_node; + + ttype = pragma_lex (&token, &loc); + if (ttype != CPP_OPEN_PAREN) + { + error_at (loc, "%<#pragma FRR printfrr_ext%> expected %<(%>"); + goto out_drop; + } + + ttype = pragma_lex (&token, &loc); + + /* qualifiers */ + if (ttype == CPP_NAME && !strcmp (IDENTIFIER_POINTER (token), "const")) + { + etab->t_const = true; + ttype = pragma_lex (&token, &loc); + } + + /* tagged types */ + if (ttype == CPP_NAME && !strcmp (IDENTIFIER_POINTER (token), "struct")) + { + etab->type_code = RECORD_TYPE; + ttype = pragma_lex (&token, &loc); + } + else if (ttype == CPP_NAME && !strcmp (IDENTIFIER_POINTER (token), "union")) + { + etab->type_code = UNION_TYPE; + ttype = pragma_lex (&token, &loc); + } + else if (ttype == CPP_NAME && !strcmp (IDENTIFIER_POINTER (token), "enum")) + { + etab->type_code = ENUMERAL_TYPE; + ttype = pragma_lex (&token, &loc); + } + + /* type name */ + if (ttype != CPP_NAME) + { + error_at (loc, "%<#pragma FRR printfrr_ext%>: expected typename identifier"); + goto out_drop; + } + + etab->type_str = xstrdup (IDENTIFIER_POINTER (token)); + + while ((ttype = pragma_lex (&token, &loc)) != CPP_CLOSE_PAREN) + { + switch (ttype) { + case CPP_NAME: + error_at (loc, "%<#pragma FRR printfrr_ext%>: unexpected identifier. Note the only supported qualifier is %"); + goto out_drop; + + case CPP_MULT: + etab->ptrlevel++; + break; + + case CPP_EOF: + error_at (loc, "%<#pragma FRR printfrr_ext%>: premature end of line, missing %<)%>"); + goto out_drop; + + default: + error_at (loc, "%<#pragma FRR printfrr_ext%>: unsupported token"); + goto out_drop; + } + } + + ttype = pragma_lex (&token, &loc); + if (ttype != CPP_EOF) + warning_at (loc, OPT_Wformat_, + "%<#pragma FRR printfrr_ext%>: garbage at end of line"); + + return; + +out_drop: + memset (etab, 0, sizeof (*etab)); +} + +static void +register_pragma_printfrr_ext (void *event_data, void *data) +{ + c_register_pragma_with_expansion ("FRR", "printfrr_ext", handle_pragma_printfrr_ext); +} + +static void +define_vars (void *gcc_data, void *user_data) +{ + cpp_define (parse_in, "_FRR_ATTRIBUTE_PRINTFRR=0x10000"); +} + +#ifndef __visible +#define __visible __attribute__((visibility("default"))) +#endif + +__visible int plugin_is_GPL_compatible; + +__visible int +plugin_init (struct plugin_name_args *plugin_info, + struct plugin_gcc_version *version) +{ + const char *plugin_name = plugin_info->base_name; + + if (!plugin_default_version_check(version, &gcc_version)) + { + error(G_("incompatible gcc/plugin versions")); + return 1; + } + + memset (ext_p, 0, sizeof (ext_p)); + memset (ext_d, 0, sizeof (ext_d)); + + register_callback (plugin_name, PLUGIN_FINISH_PARSE_FUNCTION, handle_finish_parse, NULL); + register_callback (plugin_name, PLUGIN_ATTRIBUTES, register_attributes, NULL); + register_callback (plugin_name, PLUGIN_START_UNIT, define_vars, NULL); + register_callback (plugin_name, PLUGIN_PRAGMAS, register_pragma_printfrr_ext, NULL); + return 0; +} diff --git a/tools/gcc-plugins/frr-format.h b/tools/gcc-plugins/frr-format.h new file mode 100644 index 0000000000..87d2049ed4 --- /dev/null +++ b/tools/gcc-plugins/frr-format.h @@ -0,0 +1,364 @@ +/* Check calls to formatted I/O functions (-Wformat). + Copyright (C) 1992-2018 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +#ifndef GCC_C_FORMAT_H +#define GCC_C_FORMAT_H + +/* The meaningfully distinct length modifiers for format checking recognized + by GCC. */ +enum format_lengths +{ + FMT_LEN_none, + FMT_LEN_hh, + FMT_LEN_h, + FMT_LEN_l, + FMT_LEN_ll, + FMT_LEN_L, + FMT_LEN_z, + FMT_LEN_t, + FMT_LEN_j, + FMT_LEN_H, + FMT_LEN_D, + FMT_LEN_DD, + FMT_LEN_MAX +}; + + +/* The standard versions in which various format features appeared. */ +enum format_std_version +{ + STD_C89, + STD_C94, + STD_C9L, /* C99, but treat as C89 if -Wno-long-long. */ + STD_C99, + STD_EXT +}; + +/* Flags that may apply to a particular kind of format checked by GCC. */ +enum +{ + /* This format converts arguments of types determined by the + format string. */ + FMT_FLAG_ARG_CONVERT = 1, + /* The scanf allocation 'a' kludge applies to this format kind. */ + FMT_FLAG_SCANF_A_KLUDGE = 2, + /* A % during parsing a specifier is allowed to be a modified % rather + that indicating the format is broken and we are out-of-sync. */ + FMT_FLAG_FANCY_PERCENT_OK = 4, + /* With $ operand numbers, it is OK to reference the same argument more + than once. */ + FMT_FLAG_DOLLAR_MULTIPLE = 8, + /* This format type uses $ operand numbers (strfmon doesn't). */ + FMT_FLAG_USE_DOLLAR = 16, + /* Zero width is bad in this type of format (scanf). */ + FMT_FLAG_ZERO_WIDTH_BAD = 32, + /* Empty precision specification is OK in this type of format (printf). */ + FMT_FLAG_EMPTY_PREC_OK = 64, + /* Gaps are allowed in the arguments with $ operand numbers if all + arguments are pointers (scanf). */ + FMT_FLAG_DOLLAR_GAP_POINTER_OK = 128, + /* The format arg is an opaque object that will be parsed by an external + facility. */ + FMT_FLAG_PARSE_ARG_CONVERT_EXTERNAL = 256 + /* Not included here: details of whether width or precision may occur + (controlled by width_char and precision_char); details of whether + '*' can be used for these (width_type and precision_type); details + of whether length modifiers can occur (length_char_specs). */ +}; + +/* Structure describing a length modifier supported in format checking, and + possibly a doubled version such as "hh". */ +struct format_length_info +{ + /* Name of the single-character length modifier. If prefixed by + a zero character, it describes a multi character length + modifier, like I64, I32, etc. */ + const char *name; + /* Index into a format_char_info.types array. */ + enum format_lengths index; + /* Standard version this length appears in. */ + enum format_std_version std; + /* Same, if the modifier can be repeated, or NULL if it can't. */ + const char *double_name; + enum format_lengths double_index; + enum format_std_version double_std; + + /* If this flag is set, just scalar width identity is checked, and + not the type identity itself. */ + int scalar_identity_flag; +}; + + +struct kernel_ext_fmt +{ + const char *suffix; + + /* RECORD_TYPE, UNION_TYPE, ENUMERAL_TYPE, or NULL for typedef */ + tree_code type_code; + int ptrlevel; + bool t_const; + bool warned; + + const char *type_str; + GTY(()) tree type; + + location_t origin_loc; +}; + + +/* Structure describing the combination of a conversion specifier + (or a set of specifiers which act identically) and a length modifier. */ +struct format_type_detail +{ + /* The standard version this combination of length and type appeared in. + This is only relevant if greater than those for length and type + individually; otherwise it is ignored. */ + enum format_std_version std; + /* The name to use for the type, if different from that generated internally + (e.g., "signed size_t"). */ + const char *name; + /* The type itself. */ + tree *type; +}; + + +/* Macros to fill out tables of these. */ +#define NOARGUMENTS { T89_V, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN } +#define BADLEN { STD_C89, NULL, NULL } +#define NOLENGTHS { BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN } + + +/* Structure describing a format conversion specifier (or a set of specifiers + which act identically), and the length modifiers used with it. */ +struct format_char_info +{ + const char *format_chars; + int pointer_count; + enum format_std_version std; + /* Types accepted for each length modifier. */ + format_type_detail types[FMT_LEN_MAX]; + /* List of other modifier characters allowed with these specifiers. + This lists flags, and additionally "w" for width, "p" for precision + (right precision, for strfmon), "#" for left precision (strfmon), + "a" for scanf "a" allocation extension (not applicable in C99 mode), + "*" for scanf suppression, and "E" and "O" for those strftime + modifiers. */ + const char *flag_chars; + /* List of additional flags describing these conversion specifiers. + "c" for generic character pointers being allowed, "2" for strftime + two digit year formats, "3" for strftime formats giving two digit + years in some locales, "4" for "2" which becomes "3" with an "E" modifier, + "o" if use of strftime "O" is a GNU extension beyond C99, + "W" if the argument is a pointer which is dereferenced and written into, + "R" if the argument is a pointer which is dereferenced and read from, + "i" for printf integer formats where the '0' flag is ignored with + precision, and "[" for the starting character of a scanf scanset, + "<" if the specifier introduces a quoted sequence (such as "%<"), + ">" if the specifier terminates a quoted sequence (such as "%>"), + "[" if the specifier introduces a color sequence (such as "%r"), + "]" if the specifier terminates a color sequence (such as "%R"), + "'" (single quote) if the specifier is expected to be quoted when + it appears outside a quoted sequence and unquoted otherwise (such + as the GCC internal printf format directive "%T"), and + "\"" (double quote) if the specifier is not expected to appear in + a quoted sequence (such as the GCC internal format directive "%K". */ + const char *flags2; + /* If this format conversion character consumes more than one argument, + CHAIN points to information about the next argument. For later + arguments, only POINTER_COUNT, TYPES, and the "c", "R", and "W" flags + in FLAGS2 are used. */ + const struct format_char_info *chain; + + struct kernel_ext_fmt *kernel_ext; +}; + + +/* Structure describing a flag accepted by some kind of format. */ +struct format_flag_spec +{ + /* The flag character in question (0 for end of array). */ + int flag_char; + /* Zero if this entry describes the flag character in general, or a + nonzero character that may be found in flags2 if it describes the + flag when used with certain formats only. If the latter, only + the first such entry found that applies to the current conversion + specifier is used; the values of 'name' and 'long_name' it supplies + will be used, if non-NULL and the standard version is higher than + the unpredicated one, for any pedantic warning. For example, 'o' + for strftime formats (meaning 'O' is an extension over C99). */ + int predicate; + /* Nonzero if the next character after this flag in the format should + be skipped ('=' in strfmon), zero otherwise. */ + int skip_next_char; + /* True if the flag introduces quoting (as in GCC's %qE). */ + bool quoting; + /* The name to use for this flag in diagnostic messages. For example, + N_("'0' flag"), N_("field width"). */ + const char *name; + /* Long name for this flag in diagnostic messages; currently only used for + "ISO C does not support ...". For example, N_("the 'I' printf flag"). */ + const char *long_name; + /* The standard version in which it appeared. */ + enum format_std_version std; +}; + + +/* Structure describing a combination of flags that is bad for some kind + of format. */ +struct format_flag_pair +{ + /* The first flag character in question (0 for end of array). */ + int flag_char1; + /* The second flag character. */ + int flag_char2; + /* Nonzero if the message should say that the first flag is ignored with + the second, zero if the combination should simply be objected to. */ + int ignored; + /* Zero if this entry applies whenever this flag combination occurs, + a nonzero character from flags2 if it only applies in some + circumstances (e.g. 'i' for printf formats ignoring 0 with precision). */ + int predicate; +}; + + +/* Structure describing a particular kind of format processed by GCC. */ +struct format_kind_info +{ + /* The name of this kind of format, for use in diagnostics. Also + the name of the attribute (without preceding and following __). */ + const char *name; + /* Specifications of the length modifiers accepted; possibly NULL. */ + const format_length_info *length_char_specs; + /* Details of the conversion specification characters accepted. */ + const format_char_info *conversion_specs; + /* String listing the flag characters that are accepted. */ + const char *flag_chars; + /* String listing modifier characters (strftime) accepted. May be NULL. */ + const char *modifier_chars; + /* Details of the flag characters, including pseudo-flags. */ + const format_flag_spec *flag_specs; + /* Details of bad combinations of flags. */ + const format_flag_pair *bad_flag_pairs; + /* Flags applicable to this kind of format. */ + int flags; + /* Flag character to treat a width as, or 0 if width not used. */ + int width_char; + /* Flag character to treat a left precision (strfmon) as, + or 0 if left precision not used. */ + int left_precision_char; + /* Flag character to treat a precision (for strfmon, right precision) as, + or 0 if precision not used. */ + int precision_char; + /* If a flag character has the effect of suppressing the conversion of + an argument ('*' in scanf), that flag character, otherwise 0. */ + int suppression_char; + /* Flag character to treat a length modifier as (ignored if length + modifiers not used). Need not be placed in flag_chars for conversion + specifiers, but is used to check for bad combinations such as length + modifier with assignment suppression in scanf. */ + int length_code_char; + /* Assignment-allocation flag character ('m' in scanf), otherwise 0. */ + int alloc_char; + /* Pointer to type of argument expected if '*' is used for a width, + or NULL if '*' not used for widths. */ + tree *width_type; + /* Pointer to type of argument expected if '*' is used for a precision, + or NULL if '*' not used for precisions. */ + tree *precision_type; +}; + +#define T_I &integer_type_node +#define T89_I { STD_C89, NULL, T_I } +#define T_L &long_integer_type_node +#define T89_L { STD_C89, NULL, T_L } +#define T_LL &long_long_integer_type_node +#define T9L_LL { STD_C9L, NULL, T_LL } +#define TEX_LL { STD_EXT, NULL, T_LL } +#define T_U64 &local_uint64_t_node +#define TEX_U64 { STD_EXT, "uint64_t", T_U64 } +#define T_S64 &local_int64_t_node +#define TEX_S64 { STD_EXT, "int64_t", T_S64 } +#define T_S &short_integer_type_node +#define T89_S { STD_C89, NULL, T_S } +#define T_UI &unsigned_type_node +#define T89_UI { STD_C89, NULL, T_UI } +#define T_UL &long_unsigned_type_node +#define T89_UL { STD_C89, NULL, T_UL } +#define T_ULL &long_long_unsigned_type_node +#define T9L_ULL { STD_C9L, NULL, T_ULL } +#define TEX_ULL { STD_EXT, NULL, T_ULL } +#define T_US &short_unsigned_type_node +#define T89_US { STD_C89, NULL, T_US } +#define T_F &float_type_node +#define T89_F { STD_C89, NULL, T_F } +#define T99_F { STD_C99, NULL, T_F } +#define T_D &double_type_node +#define T89_D { STD_C89, NULL, T_D } +#define T99_D { STD_C99, NULL, T_D } +#define T_LD &long_double_type_node +#define T89_LD { STD_C89, NULL, T_LD } +#define T99_LD { STD_C99, NULL, T_LD } +#define T_C &char_type_node +#define T89_C { STD_C89, NULL, T_C } +#define T_SC &signed_char_type_node +#define T99_SC { STD_C99, NULL, T_SC } +#define T_UC &unsigned_char_type_node +#define T99_UC { STD_C99, NULL, T_UC } +#define T_V &void_type_node +#define T89_G { STD_C89, NULL, &local_gimple_ptr_node } +#define T89_T { STD_C89, NULL, &local_tree_type_node } +#define T89_V { STD_C89, NULL, T_V } +#define T_W &wchar_type_node +#define T94_W { STD_C94, "wchar_t", T_W } +#define TEX_W { STD_EXT, "wchar_t", T_W } +#define T_WI &wint_type_node +#define T94_WI { STD_C94, "wint_t", T_WI } +#define TEX_WI { STD_EXT, "wint_t", T_WI } +#define T_ST &local_size_t_node +#define T99_ST { STD_C99, "size_t", T_ST } +#define T_SST &local_ssize_t_node +#define T99_SST { STD_C99, "ssize_t", T_SST } +#define T_PD &ptrdiff_type_node +#define T99_PD { STD_C99, "ptrdiff_t", T_PD } +#define T_UPD &unsigned_ptrdiff_type_node +#define T99_UPD { STD_C99, "unsigned ptrdiff_t", T_UPD } +#define T_IM &intmax_type_node +#define T99_IM { STD_C99, "intmax_t", T_IM } +#define T_UIM &uintmax_type_node +#define T99_UIM { STD_C99, "uintmax_t", T_UIM } +#define T_D32 &dfloat32_type_node +#define TEX_D32 { STD_EXT, "_Decimal32", T_D32 } +#define T_D64 &dfloat64_type_node +#define TEX_D64 { STD_EXT, "_Decimal64", T_D64 } +#define T_D128 &dfloat128_type_node +#define TEX_D128 { STD_EXT, "_Decimal128", T_D128 } + +/* Structure describing how format attributes such as "printf" are + interpreted as "gnu_printf" or "ms_printf" on a particular system. + TARGET_OVERRIDES_FORMAT_ATTRIBUTES is used to specify target-specific + defaults. */ +struct target_ovr_attr +{ + /* The name of the to be copied format attribute. */ + const char *named_attr_src; + /* The name of the to be overridden format attribute. */ + const char *named_attr_dst; +}; + +#endif /* GCC_C_FORMAT_H */ diff --git a/tools/gcc-plugins/gcc-common.h b/tools/gcc-plugins/gcc-common.h new file mode 100644 index 0000000000..ec45de1a53 --- /dev/null +++ b/tools/gcc-plugins/gcc-common.h @@ -0,0 +1,985 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* FRR: imported from Linux kernel on 2020-07-14 */ + +#ifndef GCC_COMMON_H_INCLUDED +#define GCC_COMMON_H_INCLUDED + +#include "bversion.h" +#if BUILDING_GCC_VERSION >= 6000 +#include "gcc-plugin.h" +#else +#include "plugin.h" +#endif +#include "plugin-version.h" +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "line-map.h" +#include "input.h" +#include "tree.h" + +#include "tree-inline.h" +#include "version.h" +#include "rtl.h" +#include "tm_p.h" +#include "flags.h" +#include "hard-reg-set.h" +#include "output.h" +#include "except.h" +#include "function.h" +#include "toplev.h" +#if BUILDING_GCC_VERSION >= 5000 +#include "expr.h" +#endif +#include "basic-block.h" +#include "intl.h" +#include "ggc.h" +#include "timevar.h" + +#if BUILDING_GCC_VERSION < 10000 +#include "params.h" +#endif + +#if BUILDING_GCC_VERSION <= 4009 +#include "pointer-set.h" +#else +#include "hash-map.h" +#endif + +#if BUILDING_GCC_VERSION >= 7000 +#include "memmodel.h" +#endif +#include "emit-rtl.h" +#include "debug.h" +#include "target.h" +#include "langhooks.h" +#include "cfgloop.h" +#include "cgraph.h" +#include "opts.h" + +#if BUILDING_GCC_VERSION == 4005 +#include +#endif + +#if BUILDING_GCC_VERSION >= 4007 +#include "tree-pretty-print.h" +#include "gimple-pretty-print.h" +#endif + +#if BUILDING_GCC_VERSION >= 4006 +/* + * The c-family headers were moved into a subdirectory in GCC version + * 4.7, but most plugin-building users of GCC 4.6 are using the Debian + * or Ubuntu package, which has an out-of-tree patch to move this to the + * same location as found in 4.7 and later: + * https://sources.debian.net/src/gcc-4.6/4.6.3-14/debian/patches/pr45078.diff/ + */ +#include "c-family/c-common.h" +#else +#include "c-common.h" +#endif + +#if BUILDING_GCC_VERSION <= 4008 +#include "tree-flow.h" +#else +#include "tree-cfgcleanup.h" +#include "tree-ssa-operands.h" +#include "tree-into-ssa.h" +#endif + +#if BUILDING_GCC_VERSION >= 4008 +#include "is-a.h" +#endif + +#include "diagnostic.h" +#include "tree-dump.h" +#include "tree-pass.h" +#if BUILDING_GCC_VERSION >= 4009 +#include "pass_manager.h" +#endif +#include "predict.h" +#include "ipa-utils.h" + +#if BUILDING_GCC_VERSION >= 8000 +#include "stringpool.h" +#endif + +#if BUILDING_GCC_VERSION >= 4009 +#include "attribs.h" +#include "varasm.h" +#include "stor-layout.h" +#include "internal-fn.h" +#include "gimple-expr.h" +#include "gimple-fold.h" +#include "context.h" +#include "tree-ssa-alias.h" +#include "tree-ssa.h" +#include "stringpool.h" +#if BUILDING_GCC_VERSION >= 7000 +#include "tree-vrp.h" +#endif +#include "tree-ssanames.h" +#include "print-tree.h" +#include "tree-eh.h" +#include "stmt.h" +#include "gimplify.h" +#endif + +#include "gimple.h" + +#if BUILDING_GCC_VERSION >= 4009 +#include "tree-ssa-operands.h" +#include "tree-phinodes.h" +#include "tree-cfg.h" +#include "gimple-iterator.h" +#include "gimple-ssa.h" +#include "ssa-iterators.h" +#endif + +#if BUILDING_GCC_VERSION >= 5000 +#include "builtins.h" +#endif + +/* missing from basic_block.h... */ +void debug_dominance_info(enum cdi_direction dir); +void debug_dominance_tree(enum cdi_direction dir, basic_block root); + +#if BUILDING_GCC_VERSION == 4006 +void debug_gimple_stmt(gimple); +void debug_gimple_seq(gimple_seq); +void print_gimple_seq(FILE *, gimple_seq, int, int); +void print_gimple_stmt(FILE *, gimple, int, int); +void print_gimple_expr(FILE *, gimple, int, int); +void dump_gimple_stmt(pretty_printer *, gimple, int, int); +#endif + +#ifndef __unused +#define __unused __attribute__((__unused__)) +#endif +#ifndef __visible +#define __visible __attribute__((visibility("default"))) +#endif + +#define DECL_NAME_POINTER(node) IDENTIFIER_POINTER(DECL_NAME(node)) +#define DECL_NAME_LENGTH(node) IDENTIFIER_LENGTH(DECL_NAME(node)) +#define TYPE_NAME_POINTER(node) IDENTIFIER_POINTER(TYPE_NAME(node)) +#define TYPE_NAME_LENGTH(node) IDENTIFIER_LENGTH(TYPE_NAME(node)) + +#if BUILDING_GCC_VERSION < 9000 +/* should come from c-tree.h if only it were installed for gcc 4.5... */ +#define C_TYPE_FIELDS_READONLY(TYPE) TREE_LANG_FLAG_1(TYPE) +#endif + +static inline tree build_const_char_string(int len, const char *str) +{ + tree cstr, elem, index, type; + + cstr = build_string(len, str); + elem = build_type_variant(char_type_node, 1, 0); + index = build_index_type(size_int(len - 1)); + type = build_array_type(elem, index); + TREE_TYPE(cstr) = type; + TREE_CONSTANT(cstr) = 1; + TREE_READONLY(cstr) = 1; + TREE_STATIC(cstr) = 1; + return cstr; +} + +#define PASS_INFO(NAME, REF, ID, POS) \ +struct register_pass_info NAME##_pass_info = { \ + .pass = make_##NAME##_pass(), \ + .reference_pass_name = REF, \ + .ref_pass_instance_number = ID, \ + .pos_op = POS, \ +} + +#if BUILDING_GCC_VERSION == 4005 +#define FOR_EACH_LOCAL_DECL(FUN, I, D) \ + for (tree vars = (FUN)->local_decls, (I) = 0; \ + vars && ((D) = TREE_VALUE(vars)); \ + vars = TREE_CHAIN(vars), (I)++) +#define DECL_CHAIN(NODE) (TREE_CHAIN(DECL_MINIMAL_CHECK(NODE))) +#define FOR_EACH_VEC_ELT(T, V, I, P) \ + for (I = 0; VEC_iterate(T, (V), (I), (P)); ++(I)) +#define TODO_rebuild_cgraph_edges 0 +#define SCOPE_FILE_SCOPE_P(EXP) (!(EXP)) + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +typedef struct varpool_node *varpool_node_ptr; + +static inline bool gimple_call_builtin_p(gimple stmt, enum built_in_function code) +{ + tree fndecl; + + if (!is_gimple_call(stmt)) + return false; + fndecl = gimple_call_fndecl(stmt); + if (!fndecl || DECL_BUILT_IN_CLASS(fndecl) != BUILT_IN_NORMAL) + return false; + return DECL_FUNCTION_CODE(fndecl) == code; +} + +static inline bool is_simple_builtin(tree decl) +{ + if (decl && DECL_BUILT_IN_CLASS(decl) != BUILT_IN_NORMAL) + return false; + + switch (DECL_FUNCTION_CODE(decl)) { + /* Builtins that expand to constants. */ + case BUILT_IN_CONSTANT_P: + case BUILT_IN_EXPECT: + case BUILT_IN_OBJECT_SIZE: + case BUILT_IN_UNREACHABLE: + /* Simple register moves or loads from stack. */ + case BUILT_IN_RETURN_ADDRESS: + case BUILT_IN_EXTRACT_RETURN_ADDR: + case BUILT_IN_FROB_RETURN_ADDR: + case BUILT_IN_RETURN: + case BUILT_IN_AGGREGATE_INCOMING_ADDRESS: + case BUILT_IN_FRAME_ADDRESS: + case BUILT_IN_VA_END: + case BUILT_IN_STACK_SAVE: + case BUILT_IN_STACK_RESTORE: + /* Exception state returns or moves registers around. */ + case BUILT_IN_EH_FILTER: + case BUILT_IN_EH_POINTER: + case BUILT_IN_EH_COPY_VALUES: + return true; + + default: + return false; + } +} + +static inline void add_local_decl(struct function *fun, tree d) +{ + gcc_assert(TREE_CODE(d) == VAR_DECL); + fun->local_decls = tree_cons(NULL_TREE, d, fun->local_decls); +} +#endif + +#if BUILDING_GCC_VERSION <= 4006 +#define ANY_RETURN_P(rtx) (GET_CODE(rtx) == RETURN) +#define C_DECL_REGISTER(EXP) DECL_LANG_FLAG_4(EXP) +#define EDGE_PRESERVE 0ULL +#define HOST_WIDE_INT_PRINT_HEX_PURE "%" HOST_WIDE_INT_PRINT "x" +#define flag_fat_lto_objects true + +#define get_random_seed(noinit) ({ \ + unsigned HOST_WIDE_INT seed; \ + sscanf(get_random_seed(noinit), "%" HOST_WIDE_INT_PRINT "x", &seed); \ + seed * seed; }) + +#define int_const_binop(code, arg1, arg2) \ + int_const_binop((code), (arg1), (arg2), 0) + +static inline bool gimple_clobber_p(gimple s __unused) +{ + return false; +} + +static inline bool gimple_asm_clobbers_memory_p(const_gimple stmt) +{ + unsigned i; + + for (i = 0; i < gimple_asm_nclobbers(stmt); i++) { + tree op = gimple_asm_clobber_op(stmt, i); + + if (!strcmp(TREE_STRING_POINTER(TREE_VALUE(op)), "memory")) + return true; + } + + return false; +} + +static inline tree builtin_decl_implicit(enum built_in_function fncode) +{ + return implicit_built_in_decls[fncode]; +} + +static inline int ipa_reverse_postorder(struct cgraph_node **order) +{ + return cgraph_postorder(order); +} + +static inline struct cgraph_node *cgraph_create_node(tree decl) +{ + return cgraph_node(decl); +} + +static inline struct cgraph_node *cgraph_get_create_node(tree decl) +{ + struct cgraph_node *node = cgraph_get_node(decl); + + return node ? node : cgraph_node(decl); +} + +static inline bool cgraph_function_with_gimple_body_p(struct cgraph_node *node) +{ + return node->analyzed && !node->thunk.thunk_p && !node->alias; +} + +static inline struct cgraph_node *cgraph_first_function_with_gimple_body(void) +{ + struct cgraph_node *node; + + for (node = cgraph_nodes; node; node = node->next) + if (cgraph_function_with_gimple_body_p(node)) + return node; + return NULL; +} + +static inline struct cgraph_node *cgraph_next_function_with_gimple_body(struct cgraph_node *node) +{ + for (node = node->next; node; node = node->next) + if (cgraph_function_with_gimple_body_p(node)) + return node; + return NULL; +} + +static inline bool cgraph_for_node_and_aliases(cgraph_node_ptr node, bool (*callback)(cgraph_node_ptr, void *), void *data, bool include_overwritable) +{ + cgraph_node_ptr alias; + + if (callback(node, data)) + return true; + + for (alias = node->same_body; alias; alias = alias->next) { + if (include_overwritable || cgraph_function_body_availability(alias) > AVAIL_OVERWRITABLE) + if (cgraph_for_node_and_aliases(alias, callback, data, include_overwritable)) + return true; + } + + return false; +} + +#define FOR_EACH_FUNCTION_WITH_GIMPLE_BODY(node) \ + for ((node) = cgraph_first_function_with_gimple_body(); (node); \ + (node) = cgraph_next_function_with_gimple_body(node)) + +static inline void varpool_add_new_variable(tree decl) +{ + varpool_finalize_decl(decl); +} +#endif + +#if BUILDING_GCC_VERSION <= 4007 +#define FOR_EACH_FUNCTION(node) \ + for (node = cgraph_nodes; node; node = node->next) +#define FOR_EACH_VARIABLE(node) \ + for (node = varpool_nodes; node; node = node->next) +#define PROP_loops 0 +#define NODE_SYMBOL(node) (node) +#define NODE_DECL(node) (node)->decl +#define INSN_LOCATION(INSN) RTL_LOCATION(INSN) +#define vNULL NULL + +static inline int bb_loop_depth(const_basic_block bb) +{ + return bb->loop_father ? loop_depth(bb->loop_father) : 0; +} + +static inline bool gimple_store_p(gimple gs) +{ + tree lhs = gimple_get_lhs(gs); + + return lhs && !is_gimple_reg(lhs); +} + +static inline void gimple_init_singleton(gimple g __unused) +{ +} +#endif + +#if BUILDING_GCC_VERSION == 4007 || BUILDING_GCC_VERSION == 4008 +static inline struct cgraph_node *cgraph_alias_target(struct cgraph_node *n) +{ + return cgraph_alias_aliased_node(n); +} +#endif + +#if BUILDING_GCC_VERSION <= 4008 +#define ENTRY_BLOCK_PTR_FOR_FN(FN) ENTRY_BLOCK_PTR_FOR_FUNCTION(FN) +#define EXIT_BLOCK_PTR_FOR_FN(FN) EXIT_BLOCK_PTR_FOR_FUNCTION(FN) +#define basic_block_info_for_fn(FN) ((FN)->cfg->x_basic_block_info) +#define n_basic_blocks_for_fn(FN) ((FN)->cfg->x_n_basic_blocks) +#define n_edges_for_fn(FN) ((FN)->cfg->x_n_edges) +#define last_basic_block_for_fn(FN) ((FN)->cfg->x_last_basic_block) +#define label_to_block_map_for_fn(FN) ((FN)->cfg->x_label_to_block_map) +#define profile_status_for_fn(FN) ((FN)->cfg->x_profile_status) +#define BASIC_BLOCK_FOR_FN(FN, N) BASIC_BLOCK_FOR_FUNCTION((FN), (N)) +#define NODE_IMPLICIT_ALIAS(node) (node)->same_body_alias +#define VAR_P(NODE) (TREE_CODE(NODE) == VAR_DECL) + +static inline bool tree_fits_shwi_p(const_tree t) +{ + if (t == NULL_TREE || TREE_CODE(t) != INTEGER_CST) + return false; + + if (TREE_INT_CST_HIGH(t) == 0 && (HOST_WIDE_INT)TREE_INT_CST_LOW(t) >= 0) + return true; + + if (TREE_INT_CST_HIGH(t) == -1 && (HOST_WIDE_INT)TREE_INT_CST_LOW(t) < 0 && !TYPE_UNSIGNED(TREE_TYPE(t))) + return true; + + return false; +} + +static inline bool tree_fits_uhwi_p(const_tree t) +{ + if (t == NULL_TREE || TREE_CODE(t) != INTEGER_CST) + return false; + + return TREE_INT_CST_HIGH(t) == 0; +} + +static inline HOST_WIDE_INT tree_to_shwi(const_tree t) +{ + gcc_assert(tree_fits_shwi_p(t)); + return TREE_INT_CST_LOW(t); +} + +static inline unsigned HOST_WIDE_INT tree_to_uhwi(const_tree t) +{ + gcc_assert(tree_fits_uhwi_p(t)); + return TREE_INT_CST_LOW(t); +} + +static inline const char *get_tree_code_name(enum tree_code code) +{ + gcc_assert(code < MAX_TREE_CODES); + return tree_code_name[code]; +} + +#define ipa_remove_stmt_references(cnode, stmt) + +typedef union gimple_statement_d gasm; +typedef union gimple_statement_d gassign; +typedef union gimple_statement_d gcall; +typedef union gimple_statement_d gcond; +typedef union gimple_statement_d gdebug; +typedef union gimple_statement_d ggoto; +typedef union gimple_statement_d gphi; +typedef union gimple_statement_d greturn; + +static inline gasm *as_a_gasm(gimple stmt) +{ + return stmt; +} + +static inline const gasm *as_a_const_gasm(const_gimple stmt) +{ + return stmt; +} + +static inline gassign *as_a_gassign(gimple stmt) +{ + return stmt; +} + +static inline const gassign *as_a_const_gassign(const_gimple stmt) +{ + return stmt; +} + +static inline gcall *as_a_gcall(gimple stmt) +{ + return stmt; +} + +static inline const gcall *as_a_const_gcall(const_gimple stmt) +{ + return stmt; +} + +static inline gcond *as_a_gcond(gimple stmt) +{ + return stmt; +} + +static inline const gcond *as_a_const_gcond(const_gimple stmt) +{ + return stmt; +} + +static inline gdebug *as_a_gdebug(gimple stmt) +{ + return stmt; +} + +static inline const gdebug *as_a_const_gdebug(const_gimple stmt) +{ + return stmt; +} + +static inline ggoto *as_a_ggoto(gimple stmt) +{ + return stmt; +} + +static inline const ggoto *as_a_const_ggoto(const_gimple stmt) +{ + return stmt; +} + +static inline gphi *as_a_gphi(gimple stmt) +{ + return stmt; +} + +static inline const gphi *as_a_const_gphi(const_gimple stmt) +{ + return stmt; +} + +static inline greturn *as_a_greturn(gimple stmt) +{ + return stmt; +} + +static inline const greturn *as_a_const_greturn(const_gimple stmt) +{ + return stmt; +} +#endif + +#if BUILDING_GCC_VERSION == 4008 +#define NODE_SYMBOL(node) (&(node)->symbol) +#define NODE_DECL(node) (node)->symbol.decl +#endif + +#if BUILDING_GCC_VERSION >= 4008 +#define add_referenced_var(var) +#define mark_sym_for_renaming(var) +#define varpool_mark_needed_node(node) +#define create_var_ann(var) +#define TODO_dump_func 0 +#define TODO_dump_cgraph 0 +#endif + +#if BUILDING_GCC_VERSION <= 4009 +#define TODO_verify_il 0 +#define AVAIL_INTERPOSABLE AVAIL_OVERWRITABLE + +#define section_name_prefix LTO_SECTION_NAME_PREFIX +#define fatal_error(loc, gmsgid, ...) fatal_error((gmsgid), __VA_ARGS__) + +rtx emit_move_insn(rtx x, rtx y); + +typedef struct rtx_def rtx_insn; + +static inline const char *get_decl_section_name(const_tree decl) +{ + if (DECL_SECTION_NAME(decl) == NULL_TREE) + return NULL; + + return TREE_STRING_POINTER(DECL_SECTION_NAME(decl)); +} + +static inline void set_decl_section_name(tree node, const char *value) +{ + if (value) + DECL_SECTION_NAME(node) = build_string(strlen(value) + 1, value); + else + DECL_SECTION_NAME(node) = NULL; +} +#endif + +#if BUILDING_GCC_VERSION == 4009 +typedef struct gimple_statement_asm gasm; +typedef struct gimple_statement_base gassign; +typedef struct gimple_statement_call gcall; +typedef struct gimple_statement_base gcond; +typedef struct gimple_statement_base gdebug; +typedef struct gimple_statement_base ggoto; +typedef struct gimple_statement_phi gphi; +typedef struct gimple_statement_base greturn; + +static inline gasm *as_a_gasm(gimple stmt) +{ + return as_a(stmt); +} + +static inline const gasm *as_a_const_gasm(const_gimple stmt) +{ + return as_a(stmt); +} + +static inline gassign *as_a_gassign(gimple stmt) +{ + return stmt; +} + +static inline const gassign *as_a_const_gassign(const_gimple stmt) +{ + return stmt; +} + +static inline gcall *as_a_gcall(gimple stmt) +{ + return as_a(stmt); +} + +static inline const gcall *as_a_const_gcall(const_gimple stmt) +{ + return as_a(stmt); +} + +static inline gcond *as_a_gcond(gimple stmt) +{ + return stmt; +} + +static inline const gcond *as_a_const_gcond(const_gimple stmt) +{ + return stmt; +} + +static inline gdebug *as_a_gdebug(gimple stmt) +{ + return stmt; +} + +static inline const gdebug *as_a_const_gdebug(const_gimple stmt) +{ + return stmt; +} + +static inline ggoto *as_a_ggoto(gimple stmt) +{ + return stmt; +} + +static inline const ggoto *as_a_const_ggoto(const_gimple stmt) +{ + return stmt; +} + +static inline gphi *as_a_gphi(gimple stmt) +{ + return as_a(stmt); +} + +static inline const gphi *as_a_const_gphi(const_gimple stmt) +{ + return as_a(stmt); +} + +static inline greturn *as_a_greturn(gimple stmt) +{ + return stmt; +} + +static inline const greturn *as_a_const_greturn(const_gimple stmt) +{ + return stmt; +} +#endif + +#if BUILDING_GCC_VERSION >= 4009 +#define TODO_ggc_collect 0 +#define NODE_SYMBOL(node) (node) +#define NODE_DECL(node) (node)->decl +#define cgraph_node_name(node) (node)->name() +#define NODE_IMPLICIT_ALIAS(node) (node)->cpp_implicit_alias + +static inline opt_pass *get_pass_for_id(int id) +{ + return g->get_passes()->get_pass_for_id(id); +} +#endif + +#if BUILDING_GCC_VERSION >= 5000 && BUILDING_GCC_VERSION < 6000 +/* gimple related */ +template <> +template <> +inline bool is_a_helper::test(const_gimple gs) +{ + return gs->code == GIMPLE_ASSIGN; +} +#endif + +#if BUILDING_GCC_VERSION >= 5000 +#define TODO_verify_ssa TODO_verify_il +#define TODO_verify_flow TODO_verify_il +#define TODO_verify_stmts TODO_verify_il +#define TODO_verify_rtl_sharing TODO_verify_il + +#define INSN_DELETED_P(insn) (insn)->deleted() + +static inline const char *get_decl_section_name(const_tree decl) +{ + return DECL_SECTION_NAME(decl); +} + +/* symtab/cgraph related */ +#define debug_cgraph_node(node) (node)->debug() +#define cgraph_get_node(decl) cgraph_node::get(decl) +#define cgraph_get_create_node(decl) cgraph_node::get_create(decl) +#define cgraph_create_node(decl) cgraph_node::create(decl) +#define cgraph_n_nodes symtab->cgraph_count +#define cgraph_max_uid symtab->cgraph_max_uid +#define varpool_get_node(decl) varpool_node::get(decl) +#define dump_varpool_node(file, node) (node)->dump(file) + +#if BUILDING_GCC_VERSION >= 8000 +#define cgraph_create_edge(caller, callee, call_stmt, count, freq) \ + (caller)->create_edge((callee), (call_stmt), (count)) + +#define cgraph_create_edge_including_clones(caller, callee, \ + old_call_stmt, call_stmt, count, freq, reason) \ + (caller)->create_edge_including_clones((callee), \ + (old_call_stmt), (call_stmt), (count), (reason)) +#else +#define cgraph_create_edge(caller, callee, call_stmt, count, freq) \ + (caller)->create_edge((callee), (call_stmt), (count), (freq)) + +#define cgraph_create_edge_including_clones(caller, callee, \ + old_call_stmt, call_stmt, count, freq, reason) \ + (caller)->create_edge_including_clones((callee), \ + (old_call_stmt), (call_stmt), (count), (freq), (reason)) +#endif + +typedef struct cgraph_node *cgraph_node_ptr; +typedef struct cgraph_edge *cgraph_edge_p; +typedef struct varpool_node *varpool_node_ptr; + +static inline void change_decl_assembler_name(tree decl, tree name) +{ + symtab->change_decl_assembler_name(decl, name); +} + +static inline void varpool_finalize_decl(tree decl) +{ + varpool_node::finalize_decl(decl); +} + +static inline void varpool_add_new_variable(tree decl) +{ + varpool_node::add(decl); +} + +static inline unsigned int rebuild_cgraph_edges(void) +{ + return cgraph_edge::rebuild_edges(); +} + +static inline cgraph_node_ptr cgraph_function_node(cgraph_node_ptr node, enum availability *availability) +{ + return node->function_symbol(availability); +} + +static inline cgraph_node_ptr cgraph_function_or_thunk_node(cgraph_node_ptr node, enum availability *availability = NULL) +{ + return node->ultimate_alias_target(availability); +} + +static inline bool cgraph_only_called_directly_p(cgraph_node_ptr node) +{ + return node->only_called_directly_p(); +} + +static inline enum availability cgraph_function_body_availability(cgraph_node_ptr node) +{ + return node->get_availability(); +} + +static inline cgraph_node_ptr cgraph_alias_target(cgraph_node_ptr node) +{ + return node->get_alias_target(); +} + +static inline bool cgraph_for_node_and_aliases(cgraph_node_ptr node, bool (*callback)(cgraph_node_ptr, void *), void *data, bool include_overwritable) +{ + return node->call_for_symbol_thunks_and_aliases(callback, data, include_overwritable); +} + +static inline struct cgraph_node_hook_list *cgraph_add_function_insertion_hook(cgraph_node_hook hook, void *data) +{ + return symtab->add_cgraph_insertion_hook(hook, data); +} + +static inline void cgraph_remove_function_insertion_hook(struct cgraph_node_hook_list *entry) +{ + symtab->remove_cgraph_insertion_hook(entry); +} + +static inline struct cgraph_node_hook_list *cgraph_add_node_removal_hook(cgraph_node_hook hook, void *data) +{ + return symtab->add_cgraph_removal_hook(hook, data); +} + +static inline void cgraph_remove_node_removal_hook(struct cgraph_node_hook_list *entry) +{ + symtab->remove_cgraph_removal_hook(entry); +} + +static inline struct cgraph_2node_hook_list *cgraph_add_node_duplication_hook(cgraph_2node_hook hook, void *data) +{ + return symtab->add_cgraph_duplication_hook(hook, data); +} + +static inline void cgraph_remove_node_duplication_hook(struct cgraph_2node_hook_list *entry) +{ + symtab->remove_cgraph_duplication_hook(entry); +} + +static inline void cgraph_call_node_duplication_hooks(cgraph_node_ptr node, cgraph_node_ptr node2) +{ + symtab->call_cgraph_duplication_hooks(node, node2); +} + +static inline void cgraph_call_edge_duplication_hooks(cgraph_edge *cs1, cgraph_edge *cs2) +{ + symtab->call_edge_duplication_hooks(cs1, cs2); +} + +#if BUILDING_GCC_VERSION >= 6000 +typedef gimple *gimple_ptr; +typedef const gimple *const_gimple_ptr; +#define gimple gimple_ptr +#define const_gimple const_gimple_ptr +#undef CONST_CAST_GIMPLE +#define CONST_CAST_GIMPLE(X) CONST_CAST(gimple, (X)) +#endif + +/* gimple related */ +static inline gimple gimple_build_assign_with_ops(enum tree_code subcode, tree lhs, tree op1, tree op2 MEM_STAT_DECL) +{ + return gimple_build_assign(lhs, subcode, op1, op2 PASS_MEM_STAT); +} + +#if BUILDING_GCC_VERSION < 10000 +template <> +template <> +inline bool is_a_helper::test(const_gimple gs) +{ + return gs->code == GIMPLE_GOTO; +} + +template <> +template <> +inline bool is_a_helper::test(const_gimple gs) +{ + return gs->code == GIMPLE_RETURN; +} +#endif + +static inline gasm *as_a_gasm(gimple stmt) +{ + return as_a(stmt); +} + +static inline const gasm *as_a_const_gasm(const_gimple stmt) +{ + return as_a(stmt); +} + +static inline gassign *as_a_gassign(gimple stmt) +{ + return as_a(stmt); +} + +static inline const gassign *as_a_const_gassign(const_gimple stmt) +{ + return as_a(stmt); +} + +static inline gcall *as_a_gcall(gimple stmt) +{ + return as_a(stmt); +} + +static inline const gcall *as_a_const_gcall(const_gimple stmt) +{ + return as_a(stmt); +} + +static inline ggoto *as_a_ggoto(gimple stmt) +{ + return as_a(stmt); +} + +static inline const ggoto *as_a_const_ggoto(const_gimple stmt) +{ + return as_a(stmt); +} + +static inline gphi *as_a_gphi(gimple stmt) +{ + return as_a(stmt); +} + +static inline const gphi *as_a_const_gphi(const_gimple stmt) +{ + return as_a(stmt); +} + +static inline greturn *as_a_greturn(gimple stmt) +{ + return as_a(stmt); +} + +static inline const greturn *as_a_const_greturn(const_gimple stmt) +{ + return as_a(stmt); +} + +/* IPA/LTO related */ +#define ipa_ref_list_referring_iterate(L, I, P) \ + (L)->referring.iterate((I), &(P)) +#define ipa_ref_list_reference_iterate(L, I, P) \ + (L)->reference.iterate((I), &(P)) + +static inline cgraph_node_ptr ipa_ref_referring_node(struct ipa_ref *ref) +{ + return dyn_cast(ref->referring); +} + +static inline void ipa_remove_stmt_references(symtab_node *referring_node, gimple stmt) +{ + referring_node->remove_stmt_references(stmt); +} +#endif + +#if BUILDING_GCC_VERSION < 6000 +#define get_inner_reference(exp, pbitsize, pbitpos, poffset, pmode, punsignedp, preversep, pvolatilep, keep_aligning) \ + get_inner_reference(exp, pbitsize, pbitpos, poffset, pmode, punsignedp, pvolatilep, keep_aligning) +#define gen_rtx_set(ARG0, ARG1) gen_rtx_SET(VOIDmode, (ARG0), (ARG1)) +#endif + +#if BUILDING_GCC_VERSION >= 6000 +#define gen_rtx_set(ARG0, ARG1) gen_rtx_SET((ARG0), (ARG1)) +#endif + +#ifdef __cplusplus +static inline void debug_tree(const_tree t) +{ + debug_tree(CONST_CAST_TREE(t)); +} + +static inline void debug_gimple_stmt(const_gimple s) +{ + debug_gimple_stmt(CONST_CAST_GIMPLE(s)); +} +#else +#define debug_tree(t) debug_tree(CONST_CAST_TREE(t)) +#define debug_gimple_stmt(s) debug_gimple_stmt(CONST_CAST_GIMPLE(s)) +#endif + +#if BUILDING_GCC_VERSION >= 7000 +#define get_inner_reference(exp, pbitsize, pbitpos, poffset, pmode, punsignedp, preversep, pvolatilep, keep_aligning) \ + get_inner_reference(exp, pbitsize, pbitpos, poffset, pmode, punsignedp, preversep, pvolatilep) +#endif + +#if BUILDING_GCC_VERSION < 7000 +#define SET_DECL_ALIGN(decl, align) DECL_ALIGN(decl) = (align) +#define SET_DECL_MODE(decl, mode) DECL_MODE(decl) = (mode) +#endif + +#endif diff --git a/tools/gcc-plugins/gcc-retain-typeinfo.patch b/tools/gcc-plugins/gcc-retain-typeinfo.patch new file mode 100644 index 0000000000..ec51f0be6f --- /dev/null +++ b/tools/gcc-plugins/gcc-retain-typeinfo.patch @@ -0,0 +1,11 @@ +--- a/src/gcc/c/c-typeck.c ++++ b/src/gcc/c/c-typeck.c +@@ -5716,8 +5716,6 @@ build_c_cast (location_t loc, tree type, tree expr) + if (objc_is_object_ptr (type) && objc_is_object_ptr (TREE_TYPE (expr))) + return build1 (NOP_EXPR, type, expr); + +- type = TYPE_MAIN_VARIANT (type); +- + if (TREE_CODE (type) == ARRAY_TYPE) + { + error_at (loc, "cast specifies array type"); diff --git a/tools/gen_northbound_callbacks.c b/tools/gen_northbound_callbacks.c index f6c757f5d7..eaab932228 100644 --- a/tools/gen_northbound_callbacks.c +++ b/tools/gen_northbound_callbacks.c @@ -26,9 +26,12 @@ #include "yang.h" #include "northbound.h" +static bool static_cbs; + static void __attribute__((noreturn)) usage(int status) { - fprintf(stderr, "usage: gen_northbound_callbacks [-h] MODULE\n"); + extern const char *__progname; + fprintf(stderr, "usage: %s [-h] [-s] [-p path] MODULE\n", __progname); exit(status); } @@ -43,70 +46,62 @@ static struct nb_callback_info { .operation = NB_OP_CREATE, .return_type = "int ", .return_value = "NB_OK", - .arguments = - "enum nb_event event, const struct lyd_node *dnode, union nb_resource *resource", + .arguments = "struct nb_cb_create_args *args", }, { .operation = NB_OP_MODIFY, .return_type = "int ", .return_value = "NB_OK", - .arguments = - "enum nb_event event, const struct lyd_node *dnode, union nb_resource *resource", + .arguments = "struct nb_cb_modify_args *args", }, { .operation = NB_OP_DESTROY, .return_type = "int ", .return_value = "NB_OK", - .arguments = - "enum nb_event event, const struct lyd_node *dnode", + .arguments = "struct nb_cb_destroy_args *args", }, { .operation = NB_OP_MOVE, .return_type = "int ", .return_value = "NB_OK", - .arguments = - "enum nb_event event, const struct lyd_node *dnode", + .arguments = "struct nb_cb_move_args *args", }, { .operation = NB_OP_APPLY_FINISH, .optional = true, .return_type = "void ", .return_value = "", - .arguments = "const struct lyd_node *dnode", + .arguments = "struct nb_cb_apply_finish_args *args", }, { .operation = NB_OP_GET_ELEM, .return_type = "struct yang_data *", .return_value = "NULL", - .arguments = "const char *xpath, const void *list_entry", + .arguments = "struct nb_cb_get_elem_args *args", }, { .operation = NB_OP_GET_NEXT, .return_type = "const void *", .return_value = "NULL", - .arguments = - "const void *parent_list_entry, const void *list_entry", + .arguments = "struct nb_cb_get_next_args *args", }, { .operation = NB_OP_GET_KEYS, .return_type = "int ", .return_value = "NB_OK", - .arguments = - "const void *list_entry, struct yang_list_keys *keys", + .arguments = "struct nb_cb_get_keys_args *args", }, { .operation = NB_OP_LOOKUP_ENTRY, .return_type = "const void *", .return_value = "NULL", - .arguments = - "const void *parent_list_entry, const struct yang_list_keys *keys", + .arguments = "struct nb_cb_lookup_entry_args *args", }, { .operation = NB_OP_RPC, .return_type = "int ", .return_value = "NB_OK", - .arguments = - "const char *xpath, const struct list *input, struct list *output", + .arguments = "struct nb_cb_rpc_args *args", }, { /* sentinel */ @@ -152,6 +147,72 @@ static void generate_callback_name(struct lys_node *snode, replace_hyphens_by_underscores(buffer); } +static void generate_prototype(const struct nb_callback_info *ncinfo, + const char *cb_name) +{ + printf("%s%s(%s);\n", ncinfo->return_type, cb_name, ncinfo->arguments); +} + +static int generate_prototypes(const struct lys_node *snode, void *arg) +{ + switch (snode->nodetype) { + case LYS_CONTAINER: + case LYS_LEAF: + case LYS_LEAFLIST: + case LYS_LIST: + case LYS_NOTIF: + case LYS_RPC: + break; + default: + return YANG_ITER_CONTINUE; + } + + for (struct nb_callback_info *cb = &nb_callbacks[0]; + cb->operation != -1; cb++) { + char cb_name[BUFSIZ]; + + if (cb->optional + || !nb_operation_is_valid(cb->operation, snode)) + continue; + + generate_callback_name((struct lys_node *)snode, cb->operation, + cb_name, sizeof(cb_name)); + generate_prototype(cb, cb_name); + } + + return YANG_ITER_CONTINUE; +} + +static void generate_callback(const struct nb_callback_info *ncinfo, + const char *cb_name) +{ + printf("%s%s%s(%s)\n{\n", static_cbs ? "static " : "", + ncinfo->return_type, cb_name, ncinfo->arguments); + + switch (ncinfo->operation) { + case NB_OP_CREATE: + case NB_OP_MODIFY: + case NB_OP_DESTROY: + case NB_OP_MOVE: + printf("\tswitch (args->event) {\n" + "\tcase NB_EV_VALIDATE:\n" + "\tcase NB_EV_PREPARE:\n" + "\tcase NB_EV_ABORT:\n" + "\tcase NB_EV_APPLY:\n" + "\t\t/* TODO: implement me. */\n" + "\t\tbreak;\n" + "\t}\n\n" + ); + break; + + default: + printf("\t/* TODO: implement me. */\n"); + break; + } + + printf("\treturn %s;\n}\n\n", ncinfo->return_value); +} + static int generate_callbacks(const struct lys_node *snode, void *arg) { bool first = true; @@ -191,14 +252,7 @@ static int generate_callbacks(const struct lys_node *snode, void *arg) generate_callback_name((struct lys_node *)snode, cb->operation, cb_name, sizeof(cb_name)); - printf("static %s%s(%s)\n" - "{\n" - "\t/* TODO: implement me. */\n" - "\treturn %s;\n" - "}\n\n", - nb_callbacks[cb->operation].return_type, cb_name, - nb_callbacks[cb->operation].arguments, - nb_callbacks[cb->operation].return_value); + generate_callback(cb, cb_name); } return YANG_ITER_CONTINUE; @@ -237,32 +291,55 @@ static int generate_nb_nodes(const struct lys_node *snode, void *arg) printf("\t\t{\n" "\t\t\t.xpath = \"%s\",\n", xpath); + printf("\t\t\t.cbs = {\n"); first = false; } generate_callback_name((struct lys_node *)snode, cb->operation, cb_name, sizeof(cb_name)); - printf("\t\t\t.cbs.%s = %s,\n", - nb_operation_name(cb->operation), cb_name); + printf("\t\t\t\t.%s = %s,\n", nb_operation_name(cb->operation), + cb_name); } - if (!first) + if (!first) { + printf("\t\t\t}\n"); printf("\t\t},\n"); + } return YANG_ITER_CONTINUE; } int main(int argc, char *argv[]) { + const char *search_path = NULL; struct yang_module *module; char module_name_underscores[64]; + struct stat st; int opt; - while ((opt = getopt(argc, argv, "h")) != -1) { + while ((opt = getopt(argc, argv, "hp:s")) != -1) { switch (opt) { case 'h': usage(EXIT_SUCCESS); /* NOTREACHED */ + case 'p': + if (stat(optarg, &st) == -1) { + fprintf(stderr, + "error: invalid search path '%s': %s\n", + optarg, strerror(errno)); + exit(EXIT_FAILURE); + } + if (S_ISDIR(st.st_mode) == 0) { + fprintf(stderr, + "error: search path is not directory"); + exit(EXIT_FAILURE); + } + + search_path = optarg; + break; + case 's': + static_cbs = true; + break; default: usage(EXIT_FAILURE); /* NOTREACHED */ @@ -273,7 +350,10 @@ int main(int argc, char *argv[]) if (argc != 1) usage(EXIT_FAILURE); - yang_init(); + yang_init(false); + + if (search_path) + ly_ctx_set_searchdir(ly_native_ctx, search_path); /* Load all FRR native models to ensure all augmentations are loaded. */ yang_module_load_all(); @@ -285,6 +365,14 @@ int main(int argc, char *argv[]) /* Create a nb_node for all YANG schema nodes. */ nb_nodes_create(); + /* Generate callback prototypes. */ + if (!static_cbs) { + printf("/* prototypes */\n"); + yang_snodes_iterate_module(module->info, generate_prototypes, 0, + NULL); + printf("\n"); + } + /* Generate callback functions. */ yang_snodes_iterate_module(module->info, generate_callbacks, 0, NULL); diff --git a/tools/gen_yang_deviations.c b/tools/gen_yang_deviations.c index f611f1c57e..f908e1fc69 100644 --- a/tools/gen_yang_deviations.c +++ b/tools/gen_yang_deviations.c @@ -65,7 +65,7 @@ int main(int argc, char *argv[]) if (argc != 1) usage(EXIT_FAILURE); - yang_init(); + yang_init(false); /* Load YANG module. */ module = yang_module_load(argv[0]); diff --git a/tools/generate_support_bundle.py b/tools/generate_support_bundle.py old mode 100644 new mode 100755 index 118ca113a5..540b7a1357 --- a/tools/generate_support_bundle.py +++ b/tools/generate_support_bundle.py @@ -1,3 +1,5 @@ +#!/usr/bin/python + ######################################################## ### Python Script to generate the FRR support bundle ### ######################################################## @@ -28,16 +30,16 @@ def createOutputFile(procName): oldFile = LOG_DIR + fileName cpFileCmd = "cp " + oldFile + " " + oldFile + ".prev" rmFileCmd = "rm -rf " + oldFile - print "Making backup of " + oldFile + print("Making backup of " + oldFile) os.system(cpFileCmd) - print "Removing " + oldFile + print("Removing " + oldFile) os.system(rmFileCmd) return fileName # Open the output file for this process def openOutputFile(fileName): crt_file_cmd = LOG_DIR + fileName - print crt_file_cmd + print(crt_file_cmd) try: outputFile = open(crt_file_cmd, "w") return outputFile @@ -65,14 +67,14 @@ def executeCommand(cmd, outputFile): outputFile.write("########################################################\n") outputFile.write('\n') except: - print "Writing to ouptut file Failed" + print("Writing to ouptut file Failed") except subprocess.CalledProcessError as e: dateTime = datetime.datetime.now() outputFile.write(">>[" + str(dateTime) + "]" + cmd + "\n") outputFile.write(e.output) outputFile.write("########################################################\n") outputFile.write('\n') - print "Error:" + e.output + print("Error:" + e.output) # Process the support bundle configuration file @@ -85,26 +87,26 @@ def processConfFile(lines): if cmd_line[0] == "PROC_NAME": outputFileName = createOutputFile(cmd_line[1]) if outputFileName: - print outputFileName, "created for", cmd_line[1] + print(outputFileName, "created for", cmd_line[1]) elif cmd_line[0] == "CMD_LIST_START": outputFile = openOutputFile(outputFileName) if outputFile: - print outputFileName, "opened" + print(outputFileName, "opened") else: - print outputFileName, "open failed" - return FAIL + print(outputFileName, "open failed") + return FAIL elif cmd_line[0] == "CMD_LIST_END": if closeOutputFile(outputFile): - print outputFileName, "closed" + print(outputFileName, "closed") else: - print outputFileName, "close failed" + print(outputFileName, "close failed") else: - print "Execute:" , cmd_line[0] + print("Execute:" , cmd_line[0]) executeCommand(cmd_line[0], outputFile) # Main Function lines = openConfFile(inputFile) if not lines: - print "File support_bundle_commands.conf not present in /etc/frr/ directory" + print("File support_bundle_commands.conf not present in /etc/frr/ directory") else: processConfFile(lines) diff --git a/tools/start-stop-daemon.c b/tools/start-stop-daemon.c index 5903f8732f..0e4cf271f3 100644 --- a/tools/start-stop-daemon.c +++ b/tools/start-stop-daemon.c @@ -235,7 +235,7 @@ static const char *next_dirname(const char *s) { const char *cur; - cur = (const char *)s; + cur = s; if (*cur != '\0') { for (; *cur != '/'; ++cur) @@ -255,7 +255,7 @@ static void add_namespace(const char *path) const char *nsdirname, *nsname, *cur; struct namespace *namespace; - cur = (const char *)path; + cur = path; nsdirname = nsname = ""; while ((cur = next_dirname(cur))[0] != '\0') { @@ -273,7 +273,7 @@ static void add_namespace(const char *path) badusage("invalid namepspace path"); namespace = xmalloc(sizeof(*namespace)); - namespace->path = (const char *)path; + namespace->path = path; namespace->nstype = nstype; LIST_INSERT_HEAD(&namespace_head, namespace, list); } @@ -401,7 +401,7 @@ static void parse_schedule_item(const char *string, struct schedule_item *item) if (!strcmp(string, "forever")) { item->type = sched_forever; - } else if (isdigit((int)string[0])) { + } else if (isdigit((unsigned char)string[0])) { item->type = sched_timeout; if (parse_integer(string, &item->value) != 0) badusage("invalid timeout value in schedule"); @@ -410,8 +410,7 @@ static void parse_schedule_item(const char *string, struct schedule_item *item) item->type = sched_signal; } else { badusage( - "invalid schedule item (must be [-], " - "-, or `forever'"); + "invalid schedule item (must be [-], -, or `forever'"); } } @@ -436,8 +435,7 @@ static void parse_schedule(const char *schedule_str) parse_schedule_item(schedule_str, &schedule[1]); if (schedule[1].type != sched_timeout) { badusage( - "--retry takes timeout, or schedule list" - " of at least two items"); + "--retry takes timeout, or schedule list of at least two items"); } schedule[2].type = sched_signal; schedule[2].value = SIGKILL; @@ -451,8 +449,7 @@ static void parse_schedule(const char *schedule_str) : (ptrdiff_t)strlen(schedule_str); if (str_len >= (ptrdiff_t)sizeof(item_buf)) badusage( - "invalid schedule item: far too long" - " (you must delimit items with slashes)"); + "invalid schedule item: far too long (you must delimit items with slashes)"); memcpy(item_buf, schedule_str, str_len); item_buf[str_len] = 0; schedule_str = slash ? slash + 1 : NULL; @@ -461,8 +458,7 @@ static void parse_schedule(const char *schedule_str) if (schedule[count].type == sched_forever) { if (repeatat >= 0) badusage( - "invalid schedule: `forever'" - " appears more than once"); + "invalid schedule: `forever' appears more than once"); repeatat = count; continue; } @@ -574,8 +570,7 @@ static void parse_options(int argc, char *const *argv) if (signal_str != NULL) { if (parse_signal(signal_str, &signal_nr) != 0) badusage( - "signal value must be numeric or name" - " of signal (KILL, INTR, ...)"); + "signal value must be numeric or name of signal (KILL, INTR, ...)"); } if (schedule_str != NULL) { @@ -605,9 +600,9 @@ static void parse_options(int argc, char *const *argv) static int pid_is_exec(pid_t pid, const struct stat *esb) { struct stat sb; - char buf[32]; + char buf[PATH_MAX]; - sprintf(buf, "/proc/%ld/exe", (long)pid); + snprintf(buf, sizeof(buf), "/proc/%ld/exe", (long)pid); if (stat(buf, &sb) != 0) return 0; return (sb.st_dev == esb->st_dev && sb.st_ino == esb->st_ino); @@ -617,9 +612,9 @@ static int pid_is_exec(pid_t pid, const struct stat *esb) static int pid_is_user(pid_t pid, uid_t uid) { struct stat sb; - char buf[32]; + char buf[PATH_MAX]; - sprintf(buf, "/proc/%ld", (long)pid); + snprintf(buf, sizeof(buf), "/proc/%ld", (long)pid); if (stat(buf, &sb) != 0) return 0; return (sb.st_uid == uid); @@ -628,11 +623,11 @@ static int pid_is_user(pid_t pid, uid_t uid) static int pid_is_cmd(pid_t pid, const char *name) { - char buf[32]; + char buf[PATH_MAX]; FILE *f; int c; - sprintf(buf, "/proc/%ld/stat", (long)pid); + snprintf(buf, sizeof(buf), "/proc/%ld/stat", (long)pid); f = fopen(buf, "r"); if (!f) return 0; diff --git a/tools/stringmangle.py b/tools/stringmangle.py new file mode 100644 index 0000000000..a2eb37336a --- /dev/null +++ b/tools/stringmangle.py @@ -0,0 +1,59 @@ +# 2020 by David Lamparter, placed in the public domain. + +import sys +import os +import re +import argparse + +wrap_res = [ + (re.compile(r'(? 0: + sys.stderr.write('changed: %s\n' % fn) + with open(fn + '.new', 'w') as ofd: + ofd.write(newdata) + os.rename(fn + '.new', fn) + l += 1 + + sys.stderr.write('%d files changed.\n' % (l)) + +main() diff --git a/tools/subdir.am b/tools/subdir.am index 7713bb1ade..e159d82d4c 100644 --- a/tools/subdir.am +++ b/tools/subdir.am @@ -8,6 +8,10 @@ noinst_PROGRAMS += \ tools/gen_yang_deviations \ # end +EXTRA_PROGRAMS += \ + tools/frr-llvm-cg \ + # end + sbin_PROGRAMS += tools/ssd sbin_SCRIPTS += \ tools/frr-reload \ @@ -16,6 +20,7 @@ sbin_SCRIPTS += \ \ tools/frrcommon.sh \ tools/frrinit.sh \ + tools/generate_support_bundle.py \ tools/watchfrr.sh \ # end @@ -30,11 +35,21 @@ tools_gen_yang_deviations_LDADD = lib/libfrr.la $(LIBYANG_LIBS) tools_ssd_SOURCES = tools/start-stop-daemon.c +# don't bother autoconf'ing these for a simple optional tool +llvm_version = $(shell echo __clang_major__ | $(CC) -xc -P -E -) +tools_frr_llvm_cg_CFLAGS = $(AM_CFLAGS) `llvm-config-$(llvm_version) --cflags` +tools_frr_llvm_cg_LDFLAGS = `llvm-config-$(llvm_version) --ldflags --libs` +tools_frr_llvm_cg_SOURCES = \ + tools/frr-llvm-cg.c \ + # end + EXTRA_DIST += \ tools/etc \ tools/frr-reload \ tools/frr-reload.py \ tools/frr.service \ + tools/frr@.service \ + tools/generate_support_bundle.py \ tools/multiple-bgpd.sh \ tools/rrcheck.pl \ tools/rrlookup.pl \ diff --git a/tools/symalyzer.html b/tools/symalyzer.html new file mode 100644 index 0000000000..eefeee3b05 --- /dev/null +++ b/tools/symalyzer.html @@ -0,0 +1,347 @@ + + + + Symalyzer report + + + + + + + + + +
+
    +
  • S means the symbol is not used outside its own file. + It could either be completely unused or used locally. It might be appropriate to make it + static.
  • +
  • Z means the symbol is not used outside its own file, + and it's not visible to the outside of the library or daemon (i.e. ELF hidden linkage.) + It could still be completely unused, or used within the library. It might be appropriate to make it + static.
  • +
  • L means the symbol is used from other files in the library, + but not from outside. It might be appropriate to make it DSO_LOCAL.
  • +
  • A means the symbol is used from some other file, most likely a + loadable module. Note this is only flagged for symbols in executable files, not libraries.
  • +
+
    +
  • T are normal functions ("program Text")
  • +
  • + B (BSS),
    + C (Common),
    + D (Data)
    + are various types of writable global variables
  • +
  • R are read-only global variables ("Rodata")
  • +
+
+
+ {%- for subdir, subreport in dirgroups.items()|sort %} +
{{ subdir }}
+ {%- for obj, reports in subreport.items()|sort %} +
{{ obj }}
+ {%- for report in reports|sort %} + {#-
#} +
{{ report.idshort }}
+
{{ report.sym.klass }}
+
{{ report.sym.name }}
+ {% if report.sym.loc %} +
{{ report.sym.loc }}
+ {% else %} +
unknown
+ {% endif %} + {#- #} + {%- endfor %} + {%- endfor %} + {%- endfor %} +
+ + diff --git a/tools/symalyzer.py b/tools/symalyzer.py new file mode 100755 index 0000000000..cff21f9f93 --- /dev/null +++ b/tools/symalyzer.py @@ -0,0 +1,343 @@ +#!/usr/bin/python3 +# +# 2019 by David Lamparter, placed in public domain +# +# This tool generates a report of possibly unused symbols in the build. It's +# particularly useful for libfrr to find bitrotting functions that aren't even +# used anywhere anymore. +# +# Note that the tool can't distinguish between "a symbol is completely unused" +# and "a symbol is used only in its file" since file-internal references are +# invisible in nm output. However, the compiler will warn you if a static +# symbol is unused. +# +# This tool is only tested on Linux, it probably needs `nm` from GNU binutils +# (as opposed to BSD `nm`). Could use pyelftools instead but that's a lot of +# extra work. +# +# This is a developer tool, please don't put it in any packages :) + +import sys, os, subprocess +import re +from collections import namedtuple + +sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'python')) + +from makevars import MakeVars + +SymRowBase = namedtuple('SymRow', ['target', 'object', 'name', 'address', 'klass', 'typ', 'size', 'line', 'section', 'loc']) +class SymRow(SymRowBase): + ''' + wrapper around a line of `nm` output + ''' + lib_re = re.compile(r'/lib[^/]+\.(so|la)$') + def is_global(self): + return self.klass.isupper() or self.klass in 'uvw' + def scope(self): + if self.lib_re.search(self.target) is None: + return self.target + # "global" + return None + + def is_export(self): + ''' + FRR-specific list of symbols which are considered "externally used" + + e.g. hooks are by design APIs for external use, same for qobj_t_* + frr_inet_ntop is here because it's used through an ELF alias to + "inet_ntop()" + ''' + if self.name in ['main', 'frr_inet_ntop', '_libfrr_version']: + return True + if self.name.startswith('_hook_'): + return True + if self.name.startswith('qobj_t_'): + return True + return False + +class Symbols(dict): + ''' + dict of all symbols in all libs & executables + ''' + + from_re = re.compile(r'^Symbols from (.*?):$') + lt_re = re.compile(r'^(.*/)([^/]+)\.l[oa]$') + + def __init__(self): + super().__init__() + + class ReportSym(object): + def __init__(self, sym): + self.sym = sym + def __repr__(self): + return '<%-25s %-40s [%s]>' % (self.__class__.__name__ + ':', self.sym.name, self.sym.loc) + def __lt__(self, other): + return self.sym.name.__lt__(other.sym.name) + + class ReportSymCouldBeStaticAlreadyLocal(ReportSym): + idshort = 'Z' + idlong = 'extrastatic' + title = "symbol is local to library, but only used in its source file (make static?)" + class ReportSymCouldBeStatic(ReportSym): + idshort = 'S' + idlong = 'static' + title = "symbol is only used in its source file (make static?)" + class ReportSymCouldBeLibLocal(ReportSym): + idshort = 'L' + idlong = 'liblocal' + title = "symbol is only used inside of library" + class ReportSymModuleAPI(ReportSym): + idshort = 'A' + idlong = 'api' + title = "symbol (in executable) is referenced externally from a module" + + class Symbol(object): + def __init__(self, name): + super().__init__() + self.name = name + self.defs = {} + self.refs = [] + + def process(self, row): + scope = row.scope() + if row.section == '*UND*': + self.refs.append(row) + else: + self.defs.setdefault(scope, []).append(row) + + def evaluate(self, out): + ''' + generate output report + + invoked after all object files have been read in, so it can look + at inter-object-file relationships + ''' + if len(self.defs) == 0: + out.extsyms.add(self.name) + return + + for scopename, symdefs in self.defs.items(): + common_defs = [symdef for symdef in symdefs if symdef.section == '*COM*'] + proper_defs = [symdef for symdef in symdefs if symdef.section != '*COM*'] + + if len(proper_defs) > 1: + print(self.name, ' DUPLICATE') + print('\tD: %s %s' % (scopename, '\n\t\t'.join([repr(s) for s in symdefs]))) + for syms in self.refs: + print('\tR: %s' % (syms, )) + return + + if len(proper_defs): + primary_def = proper_defs[0] + elif len(common_defs): + # "common" = global variables without initializer; + # they can occur in multiple .o files and the linker will + # merge them into one variable/storage location. + primary_def = common_defs[0] + else: + # undefined symbol, e.g. libc + continue + + if scopename is not None and len(self.refs) > 0: + for ref in self.refs: + if ref.target != primary_def.target and ref.target.endswith('.la'): + outobj = out.report.setdefault(primary_def.object, []) + outobj.append(out.ReportSymModuleAPI(primary_def)) + break + + if len(self.refs) == 0: + if primary_def.is_export(): + continue + outobj = out.report.setdefault(primary_def.object, []) + if primary_def.visible: + outobj.append(out.ReportSymCouldBeStatic(primary_def)) + else: + outobj.append(out.ReportSymCouldBeStaticAlreadyLocal(primary_def)) + continue + + if scopename is None and primary_def.visible: + # lib symbol + for ref in self.refs: + if ref.target != primary_def.target: + break + else: + outobj = out.report.setdefault(primary_def.object, []) + outobj.append(out.ReportSymCouldBeLibLocal(primary_def)) + + + def evaluate(self): + self.extsyms = set() + self.report = {} + + for sym in self.values(): + sym.evaluate(self) + + def load(self, target, files): + def libtoolmustdie(fn): + m = self.lt_re.match(fn) + if m is None: + return fn + return m.group(1) + '.libs/' + m.group(2) + '.o' + + def libtooltargetmustdie(fn): + m = self.lt_re.match(fn) + if m is None: + a, b = fn.rsplit('/', 1) + return '%s/.libs/%s' % (a, b) + return m.group(1) + '.libs/' + m.group(2) + '.so' + + files = list(set([libtoolmustdie(fn) for fn in files])) + + def parse_nm_output(text): + filename = None + path_rel_to = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + + for line in text.split('\n'): + if line.strip() == '': + continue + m = self.from_re.match(line) + if m is not None: + filename = m.group(1) + continue + if line.startswith('Name'): + continue + + items = [i.strip() for i in line.split('|')] + loc = None + if '\t' in items[-1]: + items[-1], loc = items[-1].split('\t', 1) + fn, lno = loc.rsplit(':', 1) + fn = os.path.relpath(fn, path_rel_to) + loc = '%s:%s' % (fn, lno) + + items[1] = int(items[1] if items[1] != '' else '0', 16) + items[4] = int(items[4] if items[4] != '' else '0', 16) + items.append(loc) + row = SymRow(target, filename, *items) + + if row.section == '.group' or row.name == '_GLOBAL_OFFSET_TABLE_': + continue + if not row.is_global(): + continue + + yield row + + visible_syms = set() + + # the actual symbol report uses output from the individual object files + # (e.g. lib/.libs/foo.o), but we also read the linked binary (e.g. + # lib/.libs/libfrr.so) to determine which symbols are actually visible + # in the linked result (this covers ELF "hidden"/"internal" linkage) + + libfile = libtooltargetmustdie(target) + nmlib = subprocess.Popen(['nm', '-l', '-g', '--defined-only', '-f', 'sysv', libfile], stdout = subprocess.PIPE) + out = nmlib.communicate()[0].decode('US-ASCII') + + for row in parse_nm_output(out): + visible_syms.add(row.name) + + nm = subprocess.Popen(['nm', '-l', '-f', 'sysv'] + files, stdout = subprocess.PIPE) + out = nm.communicate()[0].decode('US-ASCII') + + for row in parse_nm_output(out): + row.visible = row.name in visible_syms + sym = self.setdefault(row.name, self.Symbol(row.name)) + sym.process(row) + + +def write_html_report(syms): + try: + import jinja2 + except ImportError: + sys.stderr.write('jinja2 could not be imported, not writing HTML report!\n') + return + + self_path = os.path.dirname(os.path.abspath(__file__)) + jenv = jinja2.Environment(loader=jinja2.FileSystemLoader(self_path)) + template = jenv.get_template('symalyzer.html') + + dirgroups = {} + for fn, reports in syms.report.items(): + dirname, filename = fn.replace('.libs/', '').rsplit('/', 1) + dirgroups.setdefault(dirname, {})[fn] = reports + + klasses = { + 'T': 'code / plain old regular function (Text)', + 'D': 'global variable, read-write, with nonzero initializer (Data)', + 'B': 'global variable, read-write, with zero initializer (BSS)', + 'C': 'global variable, read-write, with zero initializer (Common)', + 'R': 'global variable, read-only (Rodata)', + } + + with open('symalyzer_report.html.tmp', 'w') as fd: + fd.write(template.render(dirgroups = dirgroups, klasses = klasses)) + os.rename('symalyzer_report.html.tmp', 'symalyzer_report.html') + + if not os.path.exists('jquery-3.4.1.min.js'): + url = 'https://code.jquery.com/jquery-3.4.1.min.js' + sys.stderr.write( + 'trying to grab a copy of jquery from %s\nif this fails, please get it manually (the HTML output is done.)\n' % (url)) + import requests + r = requests.get('https://code.jquery.com/jquery-3.4.1.min.js') + if r.status_code != 200: + sys.stderr.write('failed -- please download jquery-3.4.1.min.js and put it next to the HTML report\n') + else: + with open('jquery-3.4.1.min.js.tmp', 'w') as fd: + fd.write(r.text) + os.rename('jquery-3.4.1.min.js.tmp', 'jquery-3.4.1.min.js') + sys.stderr.write('done.\n') + +def automake_escape(s): + return s.replace('.', '_').replace('/', '_') + +if __name__ == '__main__': + mv = MakeVars() + + if not (os.path.exists('config.version') and os.path.exists('lib/.libs/libfrr.so')): + sys.stderr.write('please execute this script in the root directory of an FRR build tree\n') + sys.stderr.write('./configure && make need to have completed successfully\n') + sys.exit(1) + + amtargets = ['bin_PROGRAMS', 'sbin_PROGRAMS', 'lib_LTLIBRARIES', 'module_LTLIBRARIES'] + targets = [] + + mv.getvars(amtargets) + for amtarget in amtargets: + targets.extend([item for item in mv[amtarget].strip().split() if item != 'tools/ssd']) + + mv.getvars(['%s_LDADD' % automake_escape(t) for t in targets]) + ldobjs = targets[:] + for t in targets: + ldadd = mv['%s_LDADD' % automake_escape(t)].strip().split() + for item in ldadd: + if item.startswith('-'): + continue + if item.endswith('.a'): + ldobjs.append(item) + + mv.getvars(['%s_OBJECTS' % automake_escape(o) for o in ldobjs]) + + syms = Symbols() + + for t in targets: + objs = mv['%s_OBJECTS' % automake_escape(t)].strip().split() + ldadd = mv['%s_LDADD' % automake_escape(t)].strip().split() + for item in ldadd: + if item.startswith('-'): + continue + if item.endswith('.a'): + objs.extend(mv['%s_OBJECTS' % automake_escape(item)].strip().split()) + + sys.stderr.write('processing %s...\n' % t) + sys.stderr.flush() + #print(t, '\n\t', objs) + syms.load(t, objs) + + syms.evaluate() + + for obj, reports in sorted(syms.report.items()): + print('%s:' % obj) + for report in reports: + print('\t%r' % report) + + write_html_report(syms) diff --git a/tools/tarsource.sh b/tools/tarsource.sh index eee2a9739b..4843fe88b0 100755 --- a/tools/tarsource.sh +++ b/tools/tarsource.sh @@ -301,6 +301,11 @@ if $debian; then --format='3.0 (custom)' --target-format='3.0 (quilt)' \ -b . frr_${PACKAGE_VERSION}.orig.tar.$zip frr_${DEBVER}.debian.tar.$zip + dpkg-genchanges -sa -S > ../frr_${DEBVER}_source.changes + + test -n "$keyid" && debsign ../frr_${DEBVER}_source.changes -k"$keyid" + + mv ../frr_${DEBVER}_source.changes "$outdir" || true mv ../frr_${DEBVER}.dsc "$outdir" || true mv ../frr_${DEBVER}.debian.tar.$zip "$outdir" || true if test -h ../frr_${PACKAGE_VERSION}.orig.tar.$zip; then @@ -309,12 +314,12 @@ if $debian; then ln -s frr-${PACKAGE_VERSION}.tar.$zip "$outdir/frr_${PACKAGE_VERSION}.orig.tar.$zip" || true cd "$outdir" - test -n "$keyid" && debsign -k "$keyid" "frr_${DEBVER}.dsc" lsfiles="$lsfiles \ frr_${DEBVER}.dsc \ frr_${DEBVER}.debian.tar.$zip \ - frr_${PACKAGE_VERSION}.orig.tar.$zip" + frr_${PACKAGE_VERSION}.orig.tar.$zip \ + frr_${DEBVER}_source.changes" fi cd "$outdir" diff --git a/vrrpd/.gitignore b/vrrpd/.gitignore new file mode 100644 index 0000000000..e1751b28ac --- /dev/null +++ b/vrrpd/.gitignore @@ -0,0 +1,2 @@ +libvrrp.a +vrrpd diff --git a/vrrpd/Makefile b/vrrpd/Makefile new file mode 100644 index 0000000000..027c6ee1f8 --- /dev/null +++ b/vrrpd/Makefile @@ -0,0 +1,10 @@ +all: ALWAYS + @$(MAKE) -s -C .. vrrp/vrrp +%: ALWAYS + @$(MAKE) -s -C .. vrrp/$@ + +Makefile: + #nothing +ALWAYS: +.PHONY: ALWAYS makefiles +.SUFFIXES: diff --git a/vrrpd/subdir.am b/vrrpd/subdir.am new file mode 100644 index 0000000000..412ee1c3cf --- /dev/null +++ b/vrrpd/subdir.am @@ -0,0 +1,42 @@ +# +# vrrpd +# + +if VRRPD +noinst_LIBRARIES += vrrpd/libvrrp.a +sbin_PROGRAMS += vrrpd/vrrpd +# dist_examples_DATA += staticd/staticd.conf.sample +vtysh_scan += vrrpd/vrrp_vty.c +man8 += $(MANBUILD)/frr-vrrpd.8 +endif + +vrrpd_libvrrp_a_SOURCES = \ + vrrpd/vrrp.c \ + vrrpd/vrrp_arp.c \ + vrrpd/vrrp_debug.c \ + vrrpd/vrrp_ndisc.c \ + vrrpd/vrrp_northbound.c \ + vrrpd/vrrp_packet.c \ + vrrpd/vrrp_vty.c \ + vrrpd/vrrp_zebra.c \ + # end + +noinst_HEADERS += \ + vrrpd/vrrp.h \ + vrrpd/vrrp_arp.h \ + vrrpd/vrrp_debug.h \ + vrrpd/vrrp_ndisc.h \ + vrrpd/vrrp_packet.h \ + vrrpd/vrrp_vty.h \ + vrrpd/vrrp_zebra.h \ + # end + +clippy_scan += \ + vrrpd/vrrp_vty.c \ + # end + +vrrpd_vrrpd_SOURCES = vrrpd/vrrp_main.c +vrrpd_vrrpd_LDADD = vrrpd/libvrrp.a lib/libfrr.la @LIBCAP@ +nodist_vrrpd_vrrpd_SOURCES = \ + yang/frr-vrrpd.yang.c \ + # end diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c new file mode 100644 index 0000000000..d3f9b0c730 --- /dev/null +++ b/vrrpd/vrrp.c @@ -0,0 +1,2428 @@ +/* + * VRRP global definitions and state machine. + * Copyright (C) 2018-2019 Cumulus Networks, Inc. + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include + +#include "lib/hash.h" +#include "lib/hook.h" +#include "lib/if.h" +#include "lib/linklist.h" +#include "lib/memory.h" +#include "lib/network.h" +#include "lib/prefix.h" +#include "lib/sockopt.h" +#include "lib/sockunion.h" +#include "lib/vrf.h" +#include "lib/vty.h" + +#include "vrrp.h" +#include "vrrp_arp.h" +#include "vrrp_debug.h" +#include "vrrp_ndisc.h" +#include "vrrp_packet.h" +#include "vrrp_zebra.h" + +#define VRRP_LOGPFX "[CORE] " + +DEFINE_MTYPE_STATIC(VRRPD, VRRP_IP, "VRRP IP address") +DEFINE_MTYPE_STATIC(VRRPD, VRRP_RTR, "VRRP Router") + +/* statics */ +struct hash *vrrp_vrouters_hash; +bool vrrp_autoconfig_is_on; +int vrrp_autoconfig_version; + +struct vrrp_defaults vd; + +const char *const vrrp_state_names[3] = { + [VRRP_STATE_INITIALIZE] = "Initialize", + [VRRP_STATE_MASTER] = "Master", + [VRRP_STATE_BACKUP] = "Backup", +}; + +static const char *const vrrp_event_names[2] = { + [VRRP_EVENT_STARTUP] = "Startup", + [VRRP_EVENT_SHUTDOWN] = "Shutdown", +}; + + +/* Utility functions ------------------------------------------------------- */ + +/* + * Sets an ethaddr to RFC-defined Virtual Router MAC address. + * + * mac + * ethaddr to set + * + * v6 + * Whether this is a V6 or V4 Virtual Router MAC + * + * vrid + * Virtual Router Identifier + */ +static void vrrp_mac_set(struct ethaddr *mac, bool v6, uint8_t vrid) +{ + /* + * V4: 00-00-5E-00-01-{VRID} + * V6: 00-00-5E-00-02-{VRID} + */ + mac->octet[0] = 0x00; + mac->octet[1] = 0x00; + mac->octet[2] = 0x5E; + mac->octet[3] = 0x00; + mac->octet[4] = v6 ? 0x02 : 0x01; + mac->octet[5] = vrid; +} + +/* + * Recalculates and sets skew_time and master_down_interval based + * values. + * + * r + * VRRP Router to operate on + */ +static void vrrp_recalculate_timers(struct vrrp_router *r) +{ + uint16_t mdiadv = r->vr->version == 3 ? r->master_adver_interval + : r->vr->advertisement_interval; + uint16_t skm = (r->vr->version == 3) ? r->master_adver_interval : 100; + + r->skew_time = ((256 - r->vr->priority) * skm) / 256; + r->master_down_interval = 3 * mdiadv; + r->master_down_interval += r->skew_time; +} + +/* + * Determines if a VRRP router is the owner of the specified address. + * + * The determining factor for whether an interface is the address owner is + * simply whether the address is assigned to the VRRP base interface by someone + * other than vrrpd. + * + * This function should always return the correct answer regardless of + * master/backup status. + * + * ifp + * The interface to check owernship of. This should be the base interface of + * a VRRP router. + * + * vr + * Virtual Router + * + * Returns: + * whether or not vr owns the specified address + */ +static bool vrrp_is_owner(struct interface *ifp, struct ipaddr *addr) +{ + /* + * This code sanity checks implicit ownership configuration. Ideally, + * the way we determine address ownership status for this VRRP router + * is by looking at whether our VIPs are also assigned to the base + * interface, and therefore count as "real" addresses. This frees the + * user from having to manually configure priority 255 to indicate + * address ownership. However, this means one of the VIPs will be used + * as the source address for VRRP advertisements, which in turn means + * that other VRRP routers will be receiving packets with a source + * address they themselves have. This causes lots of different issues + * so for now we're disabling this and forcing the user to configure + * priority 255 to indicate ownership. + */ + + return false; + +#if 0 + struct prefix p; + + p.family = IS_IPADDR_V4(addr) ? AF_INET : AF_INET6; + p.prefixlen = IS_IPADDR_V4(addr) ? IPV4_MAX_BITLEN : IPV6_MAX_BITLEN; + memcpy(&p.u, &addr->ip, sizeof(addr->ip)); + + return !!connected_lookup_prefix_exact(ifp, &p); +#endif +} + +/* + * Whether an interface has a MAC address that matches the VRRP RFC. + * + * ifp + * Interface to check + * + * Returns: + * Whether the interface has a VRRP mac or not + */ +static bool vrrp_ifp_has_vrrp_mac(struct interface *ifp) +{ + struct ethaddr vmac4; + struct ethaddr vmac6; + + vrrp_mac_set(&vmac4, 0, 0x00); + vrrp_mac_set(&vmac6, 1, 0x00); + + return !memcmp(ifp->hw_addr, vmac4.octet, sizeof(vmac4.octet) - 1) + || !memcmp(ifp->hw_addr, vmac6.octet, sizeof(vmac6.octet) - 1); +} + +/* + * Lookup a Virtual Router instance given a macvlan subinterface. + * + * The VRID is extracted from the interface MAC and the 2-tuple (iface, vrid) + * is used to look up any existing instances that match the interface. It does + * not matter whether the instance is already bound to the interface or not. + * + * Note that the interface linkages must be correct for this to work. In other + * words, the macvlan must have a valid VRRP MAC, and its link_ifindex must be + * be equal to the ifindex of another interface in the interface RB trees (its + * parent). If these conditions aren't satisfied we won't find the VR. + * + * mvl_ifp + * Interface pointer to use to lookup. Should be a macvlan device. + * + * Returns: + * Virtual Router, if found + * NULL otherwise + */ +static struct vrrp_vrouter *vrrp_lookup_by_if_mvl(struct interface *mvl_ifp) +{ + struct interface *p; + + if (!mvl_ifp || mvl_ifp->link_ifindex == 0 + || !vrrp_ifp_has_vrrp_mac(mvl_ifp)) { + if (mvl_ifp && mvl_ifp->link_ifindex == 0) + DEBUGD(&vrrp_dbg_zebra, + VRRP_LOGPFX + "Interface %s has no parent ifindex; disregarding", + mvl_ifp->name); + if (mvl_ifp && !vrrp_ifp_has_vrrp_mac(mvl_ifp)) + DEBUGD(&vrrp_dbg_zebra, + VRRP_LOGPFX + "Interface %s has a non-VRRP MAC; disregarding", + mvl_ifp->name); + return NULL; + } + + p = if_lookup_by_index(mvl_ifp->link_ifindex, mvl_ifp->vrf_id); + + if (!p) { + DEBUGD(&vrrp_dbg_zebra, + VRRP_LOGPFX + "Tried to lookup interface %d, parent of %s, but it doesn't exist", + mvl_ifp->link_ifindex, mvl_ifp->name); + return NULL; + } + + uint8_t vrid = mvl_ifp->hw_addr[5]; + + return vrrp_lookup(p, vrid); +} + +/* + * Lookup the Virtual Router instances configured on a particular interface. + * + * ifp + * Interface pointer to use to lookup. Should not be a macvlan device. + * + * Returns: + * List of virtual routers found + */ +static struct list *vrrp_lookup_by_if(struct interface *ifp) +{ + struct list *l = hash_to_list(vrrp_vrouters_hash); + struct listnode *ln, *nn; + struct vrrp_vrouter *vr; + + for (ALL_LIST_ELEMENTS(l, ln, nn, vr)) + if (vr->ifp != ifp) + list_delete_node(l, ln); + + return l; +} + +/* + * Lookup any Virtual Router instances associated with a particular interface. + * This is a combination of the results from vrrp_lookup_by_if_mvl and + * vrrp_lookup_by_if. + * + * Suppose the system interface list looks like the following: + * + * eth0 + * \- eth0-v0 00:00:5e:00:01:01 + * \- eth0-v1 00:00:5e:00:02:01 + * \- eth0-v2 00:00:5e:00:01:0a + * + * Passing eth0-v2 to this function will give you the VRRP instance configured + * on eth0 with VRID 10. Passing eth0-v0 or eth0-v1 will give you the VRRP + * instance configured on eth0 with VRID 1. Passing eth0 will give you both. + * + * ifp + * Interface pointer to use to lookup. Can be any interface. + * + * Returns: + * List of virtual routers found + */ +static struct list *vrrp_lookup_by_if_any(struct interface *ifp) +{ + struct vrrp_vrouter *vr; + struct list *vrs; + + vr = vrrp_lookup_by_if_mvl(ifp); + vrs = vr ? list_new() : vrrp_lookup_by_if(ifp); + + if (vr) + listnode_add(vrs, vr); + + return vrs; +} + +/* Configuration controllers ----------------------------------------------- */ + +void vrrp_check_start(struct vrrp_vrouter *vr) +{ + struct vrrp_router *r; + bool start; + const char *whynot = NULL; + + if (vr->shutdown || vr->ifp == NULL) + return; + + r = vr->v4; + /* Must not already be started */ + start = r->fsm.state == VRRP_STATE_INITIALIZE; + whynot = (!start && !whynot) ? "Already running" : whynot; + /* Must have a parent interface */ + start = start && (vr->ifp != NULL); + whynot = (!start && !whynot) ? "No base interface" : whynot; +#if 0 + /* Parent interface must be up */ + start = start && if_is_operative(vr->ifp); + start = (!start && !whynot) ? "Base interface inoperative" : whynot; +#endif + /* Parent interface must have at least one v4 */ + start = start && connected_count_by_family(vr->ifp, AF_INET) > 0; + whynot = (!start && !whynot) ? "No primary IPv4 address" : whynot; + /* Must have a macvlan interface */ + start = start && (r->mvl_ifp != NULL); + whynot = (!start && !whynot) ? "No VRRP interface" : whynot; +#if 0 + /* Macvlan interface must be admin up */ + start = start && CHECK_FLAG(r->mvl_ifp->flags, IFF_UP); + start = (!start && !whynot) ? "Macvlan device admin down" : whynot; +#endif + /* Must have at least one VIP configured */ + start = start && r->addrs->count > 0; + whynot = (!start && !whynot) ? "No Virtual IP address configured" + : whynot; + if (start) + vrrp_event(r, VRRP_EVENT_STARTUP); + else if (whynot) + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Refusing to start Virtual Router: %s", + vr->vrid, family2str(r->family), whynot); + + whynot = NULL; + + r = vr->v6; + /* Must not already be started */ + start = r->fsm.state == VRRP_STATE_INITIALIZE; + whynot = (!start && !whynot) ? "Already running" : whynot; + /* Must not be v2 */ + start = start && vr->version != 2; + whynot = (!start && !whynot) ? "VRRPv2 does not support v6" : whynot; + /* Must have a parent interface */ + start = start && (vr->ifp != NULL); + whynot = (!start && !whynot) ? "No base interface" : whynot; +#if 0 + /* Parent interface must be up */ + start = start && if_is_operative(vr->ifp); + start = (!start && !whynot) ? "Base interface inoperative" : whynot; +#endif + /* Must have a macvlan interface */ + start = start && (r->mvl_ifp != NULL); + whynot = (!start && !whynot) ? "No VRRP interface" : whynot; +#if 0 + /* Macvlan interface must be admin up */ + start = start && CHECK_FLAG(r->mvl_ifp->flags, IFF_UP); + start = (!start && !whynot) ? "Macvlan device admin down" : whynot; + /* Macvlan interface must have a link local */ + start = start && connected_get_linklocal(r->mvl_ifp); + whynot = + (!start && !whynot) ? "No link local address configured" : whynot; + /* Macvlan interface must have a v6 IP besides the link local */ + start = start && (connected_count_by_family(r->mvl_ifp, AF_INET6) > 1); + whynot = (!start && !whynot) + ? "No Virtual IPv6 address configured on macvlan device" + : whynot; +#endif + /* Must have at least one VIP configured */ + start = start && r->addrs->count > 0; + whynot = + (!start && !whynot) ? "No Virtual IP address configured" : whynot; + if (start) + vrrp_event(r, VRRP_EVENT_STARTUP); + else if (whynot) + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Refusing to start Virtual Router: %s", + vr->vrid, family2str(r->family), whynot); +} + +void vrrp_set_priority(struct vrrp_vrouter *vr, uint8_t priority) +{ + vr->priority = priority; + vr->v4->priority = priority; + vr->v6->priority = priority; +} + +void vrrp_set_advertisement_interval(struct vrrp_vrouter *vr, + uint16_t advertisement_interval) +{ + if (vr->advertisement_interval == advertisement_interval) + return; + + vr->advertisement_interval = advertisement_interval; + vrrp_recalculate_timers(vr->v4); + vrrp_recalculate_timers(vr->v6); +} + +static bool vrrp_has_ip(struct vrrp_vrouter *vr, struct ipaddr *ip) +{ + struct vrrp_router *r = ip->ipa_type == IPADDR_V4 ? vr->v4 : vr->v6; + struct listnode *ln; + struct ipaddr *iter; + + for (ALL_LIST_ELEMENTS_RO(r->addrs, ln, iter)) + if (!memcmp(&iter->ip, &ip->ip, IPADDRSZ(ip))) + return true; + + return false; +} + +int vrrp_add_ip(struct vrrp_vrouter *vr, struct ipaddr *ip) +{ + struct vrrp_router *r = IS_IPADDR_V4(ip) ? vr->v4 : vr->v6; + int af = r->family; + + assert(r->family == af); + assert(!(r->vr->version == 2 && ip->ipa_type == IPADDR_V6)); + + if (vrrp_has_ip(r->vr, ip)) + return 0; + + if (!vrrp_is_owner(r->vr->ifp, ip) && r->is_owner) { + char ipbuf[INET6_ADDRSTRLEN]; + + inet_ntop(r->family, &ip->ip, ipbuf, sizeof(ipbuf)); + zlog_err( + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "This VRRP router is not the address owner of %s, but is the address owner of other addresses; this config is unsupported.", + r->vr->vrid, family2str(r->family), ipbuf); + return -1; + } + + struct ipaddr *new = XCALLOC(MTYPE_VRRP_IP, sizeof(struct ipaddr)); + + *new = *ip; + listnode_add(r->addrs, new); + + if (r->fsm.state == VRRP_STATE_MASTER) { + switch (r->family) { + case AF_INET: + vrrp_garp_send(r, &new->ipaddr_v4); + break; + case AF_INET6: + vrrp_ndisc_una_send(r, new); + break; + } + } + + return 0; +} + +int vrrp_add_ipv4(struct vrrp_vrouter *vr, struct in_addr v4) +{ + struct ipaddr ip; + + ip.ipa_type = IPADDR_V4; + ip.ipaddr_v4 = v4; + return vrrp_add_ip(vr, &ip); +} + +int vrrp_add_ipv6(struct vrrp_vrouter *vr, struct in6_addr v6) +{ + assert(vr->version != 2); + + struct ipaddr ip; + + ip.ipa_type = IPADDR_V6; + ip.ipaddr_v6 = v6; + return vrrp_add_ip(vr, &ip); +} + +int vrrp_del_ip(struct vrrp_vrouter *vr, struct ipaddr *ip) +{ + struct listnode *ln, *nn; + struct ipaddr *iter; + int ret = 0; + + struct vrrp_router *r = IS_IPADDR_V4(ip) ? vr->v4 : vr->v6; + + if (!vrrp_has_ip(r->vr, ip)) + return 0; + + for (ALL_LIST_ELEMENTS(r->addrs, ln, nn, iter)) + if (!memcmp(&iter->ip, &ip->ip, IPADDRSZ(ip))) + list_delete_node(r->addrs, ln); + + /* + * NB: Deleting the last address and then issuing a shutdown will cause + * transmission of a priority 0 VRRP Advertisement - as per the RFC - + * but it will have no addresses. This is not forbidden in the RFC but + * might confuse other implementations. + */ + if (r->addrs->count == 0 && r->fsm.state != VRRP_STATE_INITIALIZE) + ret = vrrp_event(r, VRRP_EVENT_SHUTDOWN); + + return ret; +} + +int vrrp_del_ipv6(struct vrrp_vrouter *vr, struct in6_addr v6) +{ + struct ipaddr ip; + + ip.ipa_type = IPADDR_V6; + ip.ipaddr_v6 = v6; + return vrrp_del_ip(vr, &ip); +} + +int vrrp_del_ipv4(struct vrrp_vrouter *vr, struct in_addr v4) +{ + struct ipaddr ip; + + ip.ipa_type = IPADDR_V4; + ip.ipaddr_v4 = v4; + return vrrp_del_ip(vr, &ip); +} + + +/* Creation and destruction ------------------------------------------------ */ + +static void vrrp_router_addr_list_del_cb(void *val) +{ + struct ipaddr *ip = val; + + XFREE(MTYPE_VRRP_IP, ip); +} + +/* + * Search for a suitable macvlan subinterface we can attach to, and if found, + * attach to it. + * + * r + * Router to attach to interface + * + * Returns: + * Whether an interface was successfully attached + */ +static bool vrrp_attach_interface(struct vrrp_router *r) +{ + /* Search for existing interface with computed MAC address */ + struct interface **ifps; + + size_t ifps_cnt = + if_lookup_by_hwaddr(r->vmac.octet, sizeof(r->vmac.octet), &ifps, + r->vr->ifp->vrf_id); + + /* + * Filter to only those macvlan interfaces whose parent is the base + * interface this VRRP router is configured on. + * + * If there are still multiple interfaces we just select the first one, + * as it should be functionally identical to the others. + */ + unsigned int candidates = 0; + struct interface *selection = NULL; + + for (unsigned int i = 0; i < ifps_cnt; i++) { + if (ifps[i]->link_ifindex != r->vr->ifp->ifindex) + ifps[i] = NULL; + else { + selection = selection ? selection : ifps[i]; + candidates++; + } + } + + if (ifps_cnt) + XFREE(MTYPE_TMP, ifps); + + char ethstr[ETHER_ADDR_STRLEN]; + + prefix_mac2str(&r->vmac, ethstr, sizeof(ethstr)); + + assert(!!selection == !!candidates); + + if (candidates == 0) + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Interface: None (no interface found w/ MAC %s)", + r->vr->vrid, family2str(r->family), ethstr); + else if (candidates > 1) + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Interface: Multiple interfaces found; using %s", + r->vr->vrid, family2str(r->family), selection->name); + else + zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Interface: %s", + r->vr->vrid, family2str(r->family), selection->name); + + r->mvl_ifp = selection; + + return !!r->mvl_ifp; +} + +static struct vrrp_router *vrrp_router_create(struct vrrp_vrouter *vr, + int family) +{ + struct vrrp_router *r = + XCALLOC(MTYPE_VRRP_RTR, sizeof(struct vrrp_router)); + + r->family = family; + r->sock_rx = -1; + r->sock_tx = -1; + r->vr = vr; + r->addrs = list_new(); + r->addrs->del = vrrp_router_addr_list_del_cb; + r->priority = vr->priority; + r->fsm.state = VRRP_STATE_INITIALIZE; + vrrp_mac_set(&r->vmac, family == AF_INET6, vr->vrid); + + vrrp_attach_interface(r); + + return r; +} + +static void vrrp_router_destroy(struct vrrp_router *r) +{ + if (r->is_active) + vrrp_event(r, VRRP_EVENT_SHUTDOWN); + + if (r->sock_rx >= 0) + close(r->sock_rx); + if (r->sock_tx >= 0) + close(r->sock_tx); + + /* FIXME: also delete list elements */ + list_delete(&r->addrs); + XFREE(MTYPE_VRRP_RTR, r); +} + +struct vrrp_vrouter *vrrp_vrouter_create(struct interface *ifp, uint8_t vrid, + uint8_t version) +{ + struct vrrp_vrouter *vr = vrrp_lookup(ifp, vrid); + + if (vr) + return vr; + + if (version != 2 && version != 3) + return NULL; + + vr = XCALLOC(MTYPE_VRRP_RTR, sizeof(struct vrrp_vrouter)); + + vr->ifp = ifp; + vr->version = version; + vr->vrid = vrid; + vr->priority = vd.priority; + vr->preempt_mode = vd.preempt_mode; + vr->accept_mode = vd.accept_mode; + vr->shutdown = vd.shutdown; + + vr->v4 = vrrp_router_create(vr, AF_INET); + vr->v6 = vrrp_router_create(vr, AF_INET6); + + vrrp_set_advertisement_interval(vr, vd.advertisement_interval); + + hash_get(vrrp_vrouters_hash, vr, hash_alloc_intern); + + return vr; +} + +void vrrp_vrouter_destroy(struct vrrp_vrouter *vr) +{ + vrrp_router_destroy(vr->v4); + vrrp_router_destroy(vr->v6); + hash_release(vrrp_vrouters_hash, vr); + XFREE(MTYPE_VRRP_RTR, vr); +} + +struct vrrp_vrouter *vrrp_lookup(const struct interface *ifp, uint8_t vrid) +{ + struct vrrp_vrouter vr; + + vr.vrid = vrid; + vr.ifp = (struct interface *)ifp; + + return hash_lookup(vrrp_vrouters_hash, &vr); +} + +/* Network ----------------------------------------------------------------- */ + +/* Forward decls */ +static void vrrp_change_state(struct vrrp_router *r, int to); +static int vrrp_adver_timer_expire(struct thread *thread); +static int vrrp_master_down_timer_expire(struct thread *thread); + +/* + * Finds the first connected address of the appropriate family on a VRRP + * router's interface and binds the Tx socket of the VRRP router to that + * address. + * + * Also sets src field of vrrp_router. + * + * r + * VRRP router to operate on + * + * Returns: + * 0 on success + * -1 on failure + */ +static int vrrp_bind_to_primary_connected(struct vrrp_router *r) +{ + char ipstr[INET6_ADDRSTRLEN]; + struct interface *ifp; + + /* + * A slight quirk: the RFC specifies that advertisements under IPv6 must + * be transmitted using the link local address of the source interface + */ + ifp = r->family == AF_INET ? r->vr->ifp : r->mvl_ifp; + + struct listnode *ln; + struct connected *c = NULL; + + for (ALL_LIST_ELEMENTS_RO(ifp->connected, ln, c)) + if (c->address->family == r->family) { + if (r->family == AF_INET6 + && IN6_IS_ADDR_LINKLOCAL(&c->address->u.prefix6)) + break; + else if (r->family == AF_INET) + break; + } + + if (c == NULL) { + zlog_err(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Failed to find address to bind on %s", + r->vr->vrid, family2str(r->family), ifp->name); + return -1; + } + + union sockunion su; + + memset(&su, 0x00, sizeof(su)); + + switch (r->family) { + case AF_INET: + r->src.ipa_type = IPADDR_V4; + r->src.ipaddr_v4 = c->address->u.prefix4; + su.sin.sin_family = AF_INET; + su.sin.sin_addr = c->address->u.prefix4; + break; + case AF_INET6: + r->src.ipa_type = IPADDR_V6; + r->src.ipaddr_v6 = c->address->u.prefix6; + su.sin6.sin6_family = AF_INET6; + su.sin6.sin6_scope_id = ifp->ifindex; + su.sin6.sin6_addr = c->address->u.prefix6; + break; + } + + int ret = 0; + + sockopt_reuseaddr(r->sock_tx); + if (bind(r->sock_tx, (const struct sockaddr *)&su, sizeof(su)) < 0) { + zlog_err( + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Failed to bind Tx socket to primary IP address %s: %s", + r->vr->vrid, family2str(r->family), + inet_ntop(r->family, + (const void *)&c->address->u.prefix, ipstr, + sizeof(ipstr)), + safe_strerror(errno)); + ret = -1; + } else { + DEBUGD(&vrrp_dbg_sock, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Bound Tx socket to primary IP address %s", + r->vr->vrid, family2str(r->family), + inet_ntop(r->family, (const void *)&c->address->u.prefix, + ipstr, sizeof(ipstr))); + } + + return ret; +} + + +/* + * Create and multicast a VRRP ADVERTISEMENT message. + * + * r + * VRRP Router for which to send ADVERTISEMENT + */ +static void vrrp_send_advertisement(struct vrrp_router *r) +{ + struct vrrp_pkt *pkt; + ssize_t pktsz; + struct ipaddr *addrs[r->addrs->count]; + union sockunion dest; + + if (r->src.ipa_type == IPADDR_NONE + && vrrp_bind_to_primary_connected(r) < 0) + return; + + list_to_array(r->addrs, (void **)addrs, r->addrs->count); + + pktsz = vrrp_pkt_adver_build(&pkt, &r->src, r->vr->version, r->vr->vrid, + r->priority, r->vr->advertisement_interval, + r->addrs->count, (struct ipaddr **)&addrs); + + if (DEBUG_MODE_CHECK(&vrrp_dbg_pkt, DEBUG_MODE_ALL)) + zlog_hexdump(pkt, (size_t)pktsz); + + const char *group = r->family == AF_INET ? VRRP_MCASTV4_GROUP_STR + : VRRP_MCASTV6_GROUP_STR; + (void)str2sockunion(group, &dest); + + ssize_t sent = sendto(r->sock_tx, pkt, (size_t)pktsz, 0, &dest.sa, + sockunion_sizeof(&dest)); + + vrrp_pkt_free(pkt); + + if (sent < 0) { + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Failed to send VRRP Advertisement: %s", + r->vr->vrid, family2str(r->family), + safe_strerror(errno)); + } else { + ++r->stats.adver_tx_cnt; + } +} + +/* + * Receive and parse VRRP advertisement. + * + * By the time we get here all fields have been validated for basic correctness + * and the packet is a valid VRRP packet. + * + * However, we have not validated whether the VRID is correct for this virtual + * router, nor whether the priority is correct (i.e. is not 255 when we are the + * address owner), nor whether the advertisement interval equals our own + * configured value (this check is only performed in VRRPv2). + * + * r + * VRRP Router associated with the socket this advertisement was received on + * + * src + * Source address of sender + * + * pkt + * The advertisement they sent + * + * pktsize + * Size of advertisement + * + * Returns: + * -1 if advertisement is invalid + * 0 otherwise + */ +static int vrrp_recv_advertisement(struct vrrp_router *r, struct ipaddr *src, + struct vrrp_pkt *pkt, size_t pktsize) +{ + char sipstr[INET6_ADDRSTRLEN]; + char dipstr[INET6_ADDRSTRLEN]; + + ipaddr2str(src, sipstr, sizeof(sipstr)); + ipaddr2str(&r->src, dipstr, sizeof(dipstr)); + + char dumpbuf[BUFSIZ]; + + vrrp_pkt_adver_dump(dumpbuf, sizeof(dumpbuf), pkt); + DEBUGD(&vrrp_dbg_proto, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Received VRRP Advertisement from %s:\n%s", + r->vr->vrid, family2str(r->family), sipstr, dumpbuf); + + /* Check that VRID matches our configured VRID */ + if (pkt->hdr.vrid != r->vr->vrid) { + DEBUGD(&vrrp_dbg_proto, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Datagram invalid: Advertisement contains VRID %hhu which does not match our instance", + r->vr->vrid, family2str(r->family), pkt->hdr.vrid); + return -1; + } + + /* Verify that we are not the IPvX address owner */ + if (r->is_owner) { + DEBUGD(&vrrp_dbg_proto, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Datagram invalid: Received advertisement but we are the address owner", + r->vr->vrid, family2str(r->family)); + return -1; + } + + /* If v2, verify that adver time matches ours */ + bool adveq = (pkt->hdr.v2.adver_int + == MAX(r->vr->advertisement_interval / 100, 1)); + if (r->vr->version == 2 && !adveq) { + DEBUGD(&vrrp_dbg_proto, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Datagram invalid: Received advertisement with advertisement interval %hhu unequal to our configured value %u", + r->vr->vrid, family2str(r->family), + pkt->hdr.v2.adver_int, + MAX(r->vr->advertisement_interval / 100, 1)); + return -1; + } + + + /* Check that # IPs received matches our # configured IPs */ + if (pkt->hdr.naddr != r->addrs->count) + DEBUGD(&vrrp_dbg_proto, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Datagram has %hhu addresses, but this VRRP instance has %u", + r->vr->vrid, family2str(r->family), pkt->hdr.naddr, + r->addrs->count); + + ++r->stats.adver_rx_cnt; + + int addrcmp; + + switch (r->fsm.state) { + case VRRP_STATE_MASTER: + addrcmp = memcmp(&src->ip, &r->src.ip, IPADDRSZ(src)); + + if (pkt->hdr.priority == 0) { + vrrp_send_advertisement(r); + THREAD_OFF(r->t_adver_timer); + thread_add_timer_msec( + master, vrrp_adver_timer_expire, r, + r->vr->advertisement_interval * CS2MS, + &r->t_adver_timer); + } else if (pkt->hdr.priority > r->priority + || ((pkt->hdr.priority == r->priority) + && addrcmp > 0)) { + zlog_info( + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Received advertisement from %s w/ priority %hhu; switching to Backup", + r->vr->vrid, family2str(r->family), sipstr, + pkt->hdr.priority); + THREAD_OFF(r->t_adver_timer); + if (r->vr->version == 3) { + r->master_adver_interval = + htons(pkt->hdr.v3.adver_int); + } + vrrp_recalculate_timers(r); + THREAD_OFF(r->t_master_down_timer); + thread_add_timer_msec(master, + vrrp_master_down_timer_expire, r, + r->master_down_interval * CS2MS, + &r->t_master_down_timer); + vrrp_change_state(r, VRRP_STATE_BACKUP); + } else { + /* Discard advertisement */ + DEBUGD(&vrrp_dbg_proto, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Discarding advertisement from %s (%hhu <= %hhu & %s <= %s)", + r->vr->vrid, family2str(r->family), sipstr, + pkt->hdr.priority, r->priority, sipstr, dipstr); + } + break; + case VRRP_STATE_BACKUP: + if (pkt->hdr.priority == 0) { + THREAD_OFF(r->t_master_down_timer); + thread_add_timer_msec( + master, vrrp_master_down_timer_expire, r, + r->skew_time * CS2MS, &r->t_master_down_timer); + } else if (!r->vr->preempt_mode + || pkt->hdr.priority >= r->priority) { + if (r->vr->version == 3) { + r->master_adver_interval = + ntohs(pkt->hdr.v3.adver_int); + } + vrrp_recalculate_timers(r); + THREAD_OFF(r->t_master_down_timer); + thread_add_timer_msec(master, + vrrp_master_down_timer_expire, r, + r->master_down_interval * CS2MS, + &r->t_master_down_timer); + } else if (r->vr->preempt_mode + && pkt->hdr.priority < r->priority) { + /* Discard advertisement */ + DEBUGD(&vrrp_dbg_proto, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Discarding advertisement from %s (%hhu < %hhu & preempt = true)", + r->vr->vrid, family2str(r->family), sipstr, + pkt->hdr.priority, r->priority); + } + break; + case VRRP_STATE_INITIALIZE: + zlog_err(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Received ADVERTISEMENT in state %s; this is a bug", + r->vr->vrid, family2str(r->family), + vrrp_state_names[r->fsm.state]); + break; + } + + return 0; +} + +/* + * Read and process next IPvX datagram. + */ +static int vrrp_read(struct thread *thread) +{ + struct vrrp_router *r = thread->arg; + + struct vrrp_pkt *pkt; + ssize_t pktsize; + ssize_t nbytes; + bool resched; + char errbuf[BUFSIZ]; + struct sockaddr_storage sa; + uint8_t control[64]; + struct ipaddr src = {}; + + struct msghdr m = {}; + struct iovec iov; + + iov.iov_base = r->ibuf; + iov.iov_len = sizeof(r->ibuf); + m.msg_name = &sa; + m.msg_namelen = sizeof(sa); + m.msg_iov = &iov; + m.msg_iovlen = 1; + m.msg_control = control; + m.msg_controllen = sizeof(control); + + nbytes = recvmsg(r->sock_rx, &m, MSG_DONTWAIT); + + if ((nbytes < 0 && ERRNO_IO_RETRY(errno))) { + resched = true; + goto done; + } else if (nbytes <= 0) { + vrrp_event(r, VRRP_EVENT_SHUTDOWN); + resched = false; + goto done; + } + + if (DEBUG_MODE_CHECK(&vrrp_dbg_pkt, DEBUG_MODE_ALL)) { + DEBUGD(&vrrp_dbg_pkt, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Datagram rx: ", + r->vr->vrid, family2str(r->family)); + zlog_hexdump(r->ibuf, nbytes); + } + + pktsize = vrrp_pkt_parse_datagram(r->family, r->vr->version, &m, nbytes, + &src, &pkt, errbuf, sizeof(errbuf)); + + if (pktsize < 0) + DEBUGD(&vrrp_dbg_pkt, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Datagram invalid: %s", + r->vr->vrid, family2str(r->family), errbuf); + else + vrrp_recv_advertisement(r, &src, pkt, pktsize); + + resched = true; + +done: + memset(r->ibuf, 0x00, sizeof(r->ibuf)); + + if (resched) + thread_add_read(master, vrrp_read, r, r->sock_rx, &r->t_read); + + return 0; +} + +/* + * Creates and configures VRRP router sockets. + * + * This function: + * - Creates two sockets, one for Tx, one for Rx + * - Binds the Tx socket to the macvlan device, if necessary (VRF case) + * - Binds the Rx socket to the base interface + * - Joins the Rx socket to the appropriate VRRP multicast group + * - Sets the Tx socket to set the TTL (v4) or Hop Limit (v6) field to 255 for + * all transmitted IPvX packets + * - Requests the kernel to deliver IPv6 header values needed to validate VRRP + * packets + * + * If any of the above fail, the sockets are closed. The only exception is if + * the TTL / Hop Limit settings fail; these are logged, but configuration + * proceeds. + * + * The first connected address on the Virtual Router's interface is used as the + * interface address. + * + * r + * VRRP Router for which to create listen socket + * + * Returns: + * 0 on success + * -1 on failure + */ +static int vrrp_socket(struct vrrp_router *r) +{ + int ret; + bool failed = false; + + frr_with_privs(&vrrp_privs) { + r->sock_rx = vrf_socket(r->family, SOCK_RAW, IPPROTO_VRRP, + r->vr->ifp->vrf_id, NULL); + r->sock_tx = vrf_socket(r->family, SOCK_RAW, IPPROTO_VRRP, + r->vr->ifp->vrf_id, NULL); + } + + if (r->sock_rx < 0 || r->sock_tx < 0) { + const char *rxtx = r->sock_rx < 0 ? "Rx" : "Tx"; + + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Can't create VRRP %s socket", + r->vr->vrid, family2str(r->family), rxtx); + failed = true; + goto done; + } + + /* + * Bind Tx socket to macvlan device - necessary for VRF support, + * otherwise the kernel will select the vrf device + */ + if (r->vr->ifp->vrf_id != VRF_DEFAULT) { + frr_with_privs (&vrrp_privs) { + ret = setsockopt(r->sock_tx, SOL_SOCKET, + SO_BINDTODEVICE, r->mvl_ifp->name, + strlen(r->mvl_ifp->name)); + } + + if (ret < 0) { + zlog_warn( + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Failed to bind Tx socket to macvlan device '%s'", + r->vr->vrid, family2str(r->family), + r->mvl_ifp->name); + failed = true; + goto done; + } + } + /* Configure sockets */ + if (r->family == AF_INET) { + /* Set Tx socket to always Tx with TTL set to 255 */ + int ttl = 255; + + ret = setsockopt(r->sock_tx, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, + sizeof(ttl)); + if (ret < 0) { + zlog_warn( + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Failed to set outgoing multicast TTL count to 255; RFC 5798 compliant implementations will drop our packets", + r->vr->vrid, family2str(r->family)); + } + + /* Set Tx socket DSCP byte */ + setsockopt_ipv4_tos(r->sock_tx, IPTOS_PREC_INTERNETCONTROL); + + /* Turn off multicast loop on Tx */ + setsockopt_ipv4_multicast_loop(r->sock_tx, 0); + + /* Bind Rx socket to exact interface */ + frr_with_privs(&vrrp_privs) { + ret = setsockopt(r->sock_rx, SOL_SOCKET, + SO_BINDTODEVICE, r->vr->ifp->name, + strlen(r->vr->ifp->name)); + } + if (ret) { + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Failed to bind Rx socket to %s: %s", + r->vr->vrid, family2str(r->family), + r->vr->ifp->name, safe_strerror(errno)); + failed = true; + goto done; + } + DEBUGD(&vrrp_dbg_sock, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Bound Rx socket to %s", + r->vr->vrid, family2str(r->family), r->vr->ifp->name); + + /* Bind Rx socket to v4 multicast address */ + struct sockaddr_in sa = {0}; + + sa.sin_family = AF_INET; + sa.sin_addr.s_addr = htonl(VRRP_MCASTV4_GROUP); + if (bind(r->sock_rx, (struct sockaddr *)&sa, sizeof(sa))) { + zlog_err( + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Failed to bind Rx socket to VRRP multicast group: %s", + r->vr->vrid, family2str(r->family), + safe_strerror(errno)); + failed = true; + goto done; + } + DEBUGD(&vrrp_dbg_sock, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Bound Rx socket to VRRP multicast group", + r->vr->vrid, family2str(r->family)); + + /* Join Rx socket to VRRP IPv4 multicast group */ + assert(listhead(r->vr->ifp->connected)); + struct connected *c = listhead(r->vr->ifp->connected)->data; + struct in_addr v4 = c->address->u.prefix4; + + ret = setsockopt_ipv4_multicast(r->sock_rx, IP_ADD_MEMBERSHIP, + v4, htonl(VRRP_MCASTV4_GROUP), + r->vr->ifp->ifindex); + if (ret < 0) { + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID + "Failed to join VRRP %s multicast group", + r->vr->vrid, family2str(r->family)); + failed = true; + goto done; + } + DEBUGD(&vrrp_dbg_sock, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Joined VRRP multicast group", + r->vr->vrid, family2str(r->family)); + + /* Set outgoing interface for advertisements */ + struct ip_mreqn mreqn = {}; + + mreqn.imr_ifindex = r->mvl_ifp->ifindex; + ret = setsockopt(r->sock_tx, IPPROTO_IP, IP_MULTICAST_IF, + (void *)&mreqn, sizeof(mreqn)); + if (ret < 0) { + zlog_warn( + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Could not set %s as outgoing multicast interface", + r->vr->vrid, family2str(r->family), + r->mvl_ifp->name); + failed = true; + goto done; + } + DEBUGD(&vrrp_dbg_sock, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Set %s as outgoing multicast interface", + r->vr->vrid, family2str(r->family), r->mvl_ifp->name); + + /* Select and bind source address */ + if (vrrp_bind_to_primary_connected(r) < 0) { + failed = true; + goto done; + } + + } else if (r->family == AF_INET6) { + /* Always transmit IPv6 packets with hop limit set to 255 */ + ret = setsockopt_ipv6_multicast_hops(r->sock_tx, 255); + if (ret < 0) { + zlog_warn( + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Failed to set outgoing multicast hop count to 255; RFC 5798 compliant implementations will drop our packets", + r->vr->vrid, family2str(r->family)); + } + + /* Set Tx socket DSCP byte */ + setsockopt_ipv6_tclass(r->sock_tx, IPTOS_PREC_INTERNETCONTROL); + + /* Request hop limit delivery */ + setsockopt_ipv6_hoplimit(r->sock_rx, 1); + if (ret < 0) { + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Failed to request IPv6 Hop Limit delivery", + r->vr->vrid, family2str(r->family)); + failed = true; + goto done; + } + + /* Turn off multicast loop on Tx */ + setsockopt_ipv6_multicast_loop(r->sock_tx, 0); + + /* Bind Rx socket to exact interface */ + frr_with_privs(&vrrp_privs) { + ret = setsockopt(r->sock_rx, SOL_SOCKET, + SO_BINDTODEVICE, r->vr->ifp->name, + strlen(r->vr->ifp->name)); + } + if (ret) { + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Failed to bind Rx socket to %s: %s", + r->vr->vrid, family2str(r->family), + r->vr->ifp->name, safe_strerror(errno)); + failed = true; + goto done; + } + DEBUGD(&vrrp_dbg_sock, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Bound Rx socket to %s", + r->vr->vrid, family2str(r->family), r->vr->ifp->name); + + /* Bind Rx socket to v6 multicast address */ + struct sockaddr_in6 sa = {0}; + + sa.sin6_family = AF_INET6; + inet_pton(AF_INET6, VRRP_MCASTV6_GROUP_STR, &sa.sin6_addr); + if (bind(r->sock_rx, (struct sockaddr *)&sa, sizeof(sa))) { + zlog_err( + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Failed to bind Rx socket to VRRP multicast group: %s", + r->vr->vrid, family2str(r->family), + safe_strerror(errno)); + failed = true; + goto done; + } + DEBUGD(&vrrp_dbg_sock, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Bound Rx socket to VRRP multicast group", + r->vr->vrid, family2str(r->family)); + + /* Join VRRP IPv6 multicast group */ + struct ipv6_mreq mreq; + + inet_pton(AF_INET6, VRRP_MCASTV6_GROUP_STR, + &mreq.ipv6mr_multiaddr); + mreq.ipv6mr_interface = r->vr->ifp->ifindex; + ret = setsockopt(r->sock_rx, IPPROTO_IPV6, IPV6_JOIN_GROUP, + &mreq, sizeof(mreq)); + if (ret < 0) { + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Failed to join VRRP multicast group", + r->vr->vrid, family2str(r->family)); + failed = true; + goto done; + } + DEBUGD(&vrrp_dbg_sock, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Joined VRRP multicast group", + r->vr->vrid, family2str(r->family)); + + /* Set outgoing interface for advertisements */ + ret = setsockopt(r->sock_tx, IPPROTO_IPV6, IPV6_MULTICAST_IF, + &r->mvl_ifp->ifindex, sizeof(ifindex_t)); + if (ret < 0) { + zlog_warn( + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Could not set %s as outgoing multicast interface", + r->vr->vrid, family2str(r->family), + r->mvl_ifp->name); + failed = true; + goto done; + } + DEBUGD(&vrrp_dbg_sock, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Set %s as outgoing multicast interface", + r->vr->vrid, family2str(r->family), r->mvl_ifp->name); + } + +done: + ret = 0; + if (failed) { + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Failed to initialize VRRP router", + r->vr->vrid, family2str(r->family)); + if (r->sock_rx >= 0) { + close(r->sock_rx); + r->sock_rx = -1; + } + if (r->sock_tx >= 0) { + close(r->sock_tx); + r->sock_tx = -1; + } + ret = -1; + } + + return ret; +} + + +/* State machine ----------------------------------------------------------- */ + +DEFINE_HOOK(vrrp_change_state_hook, (struct vrrp_router *r, int to), (r, to)); + +/* + * Handle any necessary actions during state change to MASTER state. + * + * r + * VRRP Router to operate on + */ +static void vrrp_change_state_master(struct vrrp_router *r) +{ + /* Enable ND Router Advertisements */ + if (r->family == AF_INET6) + vrrp_zebra_radv_set(r, true); + + /* Set protodown off */ + vrrp_zclient_send_interface_protodown(r->mvl_ifp, false); + + /* + * If protodown is already off, we can send our stuff, otherwise we + * have to delay until the interface is all the way up + */ + if (if_is_operative(r->mvl_ifp)) { + vrrp_send_advertisement(r); + + if (r->family == AF_INET) + vrrp_garp_send_all(r); + else if (r->family == AF_INET6) + vrrp_ndisc_una_send_all(r); + } else { + DEBUGD(&vrrp_dbg_proto, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Delaying VRRP advertisement until interface is up", + r->vr->vrid, family2str(r->family)); + r->advert_pending = true; + + if (r->family == AF_INET) { + DEBUGD(&vrrp_dbg_proto, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Delaying VRRP gratuitous ARPs until interface is up", + r->vr->vrid, family2str(r->family)); + r->garp_pending = true; + } else if (r->family == AF_INET6) { + DEBUGD(&vrrp_dbg_proto, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Delaying VRRP unsolicited neighbor advertisement until interface is up", + r->vr->vrid, family2str(r->family)); + r->ndisc_pending = true; + } + } +} + +/* + * Handle any necessary actions during state change to BACKUP state. + * + * r + * Virtual Router to operate on + */ +static void vrrp_change_state_backup(struct vrrp_router *r) +{ + /* Disable ND Router Advertisements */ + if (r->family == AF_INET6) + vrrp_zebra_radv_set(r, false); + + /* Disable Adver_Timer */ + THREAD_OFF(r->t_adver_timer); + + r->advert_pending = false; + r->garp_pending = false; + r->ndisc_pending = false; + memset(&r->src, 0x00, sizeof(r->src)); + + vrrp_zclient_send_interface_protodown(r->mvl_ifp, true); +} + +/* + * Handle any necessary actions during state change to INITIALIZE state. + * + * This is not called for initial startup, only when transitioning from MASTER + * or BACKUP. + * + * r + * VRRP Router to operate on + */ +static void vrrp_change_state_initialize(struct vrrp_router *r) +{ + r->master_adver_interval = 0; + vrrp_recalculate_timers(r); + + r->advert_pending = false; + r->garp_pending = false; + r->ndisc_pending = false; + + /* Disable ND Router Advertisements */ + if (r->family == AF_INET6 && r->mvl_ifp) + vrrp_zebra_radv_set(r, false); +} + +void (*const vrrp_change_state_handlers[])(struct vrrp_router *vr) = { + [VRRP_STATE_MASTER] = vrrp_change_state_master, + [VRRP_STATE_BACKUP] = vrrp_change_state_backup, + [VRRP_STATE_INITIALIZE] = vrrp_change_state_initialize, +}; + +/* + * Change Virtual Router FSM position. Handles transitional actions and calls + * any subscribers to the state change hook. + * + * r + * Virtual Router for which to change state + * + * to + * State to change to + */ +static void vrrp_change_state(struct vrrp_router *r, int to) +{ + if (r->fsm.state == to) + return; + + /* Call our handlers, then any subscribers */ + vrrp_change_state_handlers[to](r); + hook_call(vrrp_change_state_hook, r, to); + zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM "%s -> %s", + r->vr->vrid, family2str(r->family), + vrrp_state_names[r->fsm.state], vrrp_state_names[to]); + r->fsm.state = to; + + ++r->stats.trans_cnt; +} + +/* + * Called when Adver_Timer expires. + */ +static int vrrp_adver_timer_expire(struct thread *thread) +{ + struct vrrp_router *r = thread->arg; + + DEBUGD(&vrrp_dbg_proto, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Adver_Timer expired", + r->vr->vrid, family2str(r->family)); + + if (r->fsm.state == VRRP_STATE_MASTER) { + /* Send an ADVERTISEMENT */ + vrrp_send_advertisement(r); + + /* Reset the Adver_Timer to Advertisement_Interval */ + thread_add_timer_msec(master, vrrp_adver_timer_expire, r, + r->vr->advertisement_interval * CS2MS, + &r->t_adver_timer); + } else { + zlog_err(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Adver_Timer expired in state '%s'; this is a bug", + r->vr->vrid, family2str(r->family), + vrrp_state_names[r->fsm.state]); + } + + return 0; +} + +/* + * Called when Master_Down_Timer expires. + */ +static int vrrp_master_down_timer_expire(struct thread *thread) +{ + struct vrrp_router *r = thread->arg; + + zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Master_Down_Timer expired", + r->vr->vrid, family2str(r->family)); + + thread_add_timer_msec(master, vrrp_adver_timer_expire, r, + r->vr->advertisement_interval * CS2MS, + &r->t_adver_timer); + vrrp_change_state(r, VRRP_STATE_MASTER); + + return 0; +} + +/* + * Event handler for Startup event. + * + * Creates sockets, sends advertisements and ARP requests, starts timers, + * and transitions the Virtual Router to either Master or Backup states. + * + * This function will also initialize the program's global ARP subsystem if it + * has not yet been initialized. + * + * r + * VRRP Router on which to apply Startup event + * + * Returns: + * < 0 if the session socket could not be created, or the state is not + * Initialize + * 0 on success + */ +static int vrrp_startup(struct vrrp_router *r) +{ + /* May only be called when the state is Initialize */ + if (r->fsm.state != VRRP_STATE_INITIALIZE) + return -1; + + /* Must have a valid macvlan interface available */ + if (r->mvl_ifp == NULL && !vrrp_attach_interface(r)) { + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "No appropriate interface found", + r->vr->vrid, family2str(r->family)); + return -1; + } + + /* Initialize global gratuitous ARP socket if necessary */ + if (r->family == AF_INET && !vrrp_garp_is_init()) + vrrp_garp_init(); + if (r->family == AF_INET6 && !vrrp_ndisc_is_init()) + vrrp_ndisc_init(); + + /* Create socket */ + if (r->sock_rx < 0 || r->sock_tx < 0) { + int ret = vrrp_socket(r); + + if (ret < 0 || r->sock_tx < 0 || r->sock_rx < 0) + return ret; + } + + /* Schedule listener */ + thread_add_read(master, vrrp_read, r, r->sock_rx, &r->t_read); + + /* Configure effective priority */ + assert(listhead(r->addrs)); + struct ipaddr *primary = (struct ipaddr *)listhead(r->addrs)->data; + char ipbuf[INET6_ADDRSTRLEN]; + + inet_ntop(r->family, &primary->ip.addr, ipbuf, sizeof(ipbuf)); + + if (r->vr->priority == VRRP_PRIO_MASTER + || vrrp_is_owner(r->vr->ifp, primary)) { + r->priority = VRRP_PRIO_MASTER; + vrrp_recalculate_timers(r); + + zlog_info( + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "%s has priority set to 255 or owns primary Virtual Router IP %s; electing self as Master", + r->vr->vrid, family2str(r->family), r->vr->ifp->name, + ipbuf); + } + + if (r->priority == VRRP_PRIO_MASTER) { + thread_add_timer_msec(master, vrrp_adver_timer_expire, r, + r->vr->advertisement_interval * CS2MS, + &r->t_adver_timer); + vrrp_change_state(r, VRRP_STATE_MASTER); + } else { + r->master_adver_interval = r->vr->advertisement_interval; + vrrp_recalculate_timers(r); + thread_add_timer_msec(master, vrrp_master_down_timer_expire, r, + r->master_down_interval * CS2MS, + &r->t_master_down_timer); + vrrp_change_state(r, VRRP_STATE_BACKUP); + } + + r->is_active = true; + + return 0; +} + +/* + * Shuts down a Virtual Router and transitions it to Initialize. + * + * This call must be idempotent; it is safe to call multiple times on the same + * VRRP Router. + */ +static int vrrp_shutdown(struct vrrp_router *r) +{ + uint8_t saved_prio; + + switch (r->fsm.state) { + case VRRP_STATE_MASTER: + /* Send an ADVERTISEMENT with Priority = 0 */ + saved_prio = r->priority; + r->priority = 0; + vrrp_send_advertisement(r); + r->priority = saved_prio; + break; + case VRRP_STATE_BACKUP: + break; + case VRRP_STATE_INITIALIZE: + DEBUGD(&vrrp_dbg_proto, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Received '%s' event in '%s' state; ignoring", + r->vr->vrid, family2str(r->family), + vrrp_event_names[VRRP_EVENT_SHUTDOWN], + vrrp_state_names[VRRP_STATE_INITIALIZE]); + return 0; + } + + /* Cancel all timers */ + THREAD_OFF(r->t_adver_timer); + THREAD_OFF(r->t_master_down_timer); + THREAD_OFF(r->t_read); + THREAD_OFF(r->t_write); + + /* Protodown macvlan */ + if (r->mvl_ifp) + vrrp_zclient_send_interface_protodown(r->mvl_ifp, true); + + /* Throw away our source address */ + memset(&r->src, 0x00, sizeof(r->src)); + + if (r->sock_rx > 0) { + close(r->sock_rx); + r->sock_rx = -1; + } + if (r->sock_tx > 0) { + close(r->sock_tx); + r->sock_tx = -1; + } + + vrrp_change_state(r, VRRP_STATE_INITIALIZE); + + r->is_active = false; + + return 0; +} + +static int (*const vrrp_event_handlers[])(struct vrrp_router *r) = { + [VRRP_EVENT_STARTUP] = vrrp_startup, + [VRRP_EVENT_SHUTDOWN] = vrrp_shutdown, +}; + +/* + * Spawn a VRRP FSM event on a VRRP Router. + * + * vr + * VRRP Router on which to spawn event + * + * event + * The event to spawn + * + * Returns: + * -1 on failure + * 0 otherwise + */ +int vrrp_event(struct vrrp_router *r, int event) +{ + zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM "'%s' event", + r->vr->vrid, family2str(r->family), vrrp_event_names[event]); + return vrrp_event_handlers[event](r); +} + + +/* Autoconfig -------------------------------------------------------------- */ + +/* + * Set the configured addresses for this VRRP instance to exactly the addresses + * present on its macvlan subinterface(s). + * + * vr + * VRRP router to act on + */ +static void vrrp_autoconfig_autoaddrupdate(struct vrrp_router *r) +{ + struct listnode *ln; + struct connected *c = NULL; + bool is_v6_ll; + char ipbuf[INET6_ADDRSTRLEN]; + + if (!r->mvl_ifp) + return; + + DEBUGD(&vrrp_dbg_auto, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Setting Virtual IP list to match IPv4 addresses on %s", + r->vr->vrid, family2str(r->family), r->mvl_ifp->name); + for (ALL_LIST_ELEMENTS_RO(r->mvl_ifp->connected, ln, c)) { + is_v6_ll = (c->address->family == AF_INET6 + && IN6_IS_ADDR_LINKLOCAL(&c->address->u.prefix6)); + if (c->address->family == r->family && !is_v6_ll) { + inet_ntop(r->family, &c->address->u.prefix, ipbuf, + sizeof(ipbuf)); + DEBUGD(&vrrp_dbg_auto, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Adding %s", + r->vr->vrid, family2str(r->family), ipbuf); + if (r->family == AF_INET) + vrrp_add_ipv4(r->vr, c->address->u.prefix4); + else if (r->vr->version == 3) + vrrp_add_ipv6(r->vr, c->address->u.prefix6); + } + } + + vrrp_check_start(r->vr); + + if (r->addrs->count == 0 && r->fsm.state != VRRP_STATE_INITIALIZE) { + DEBUGD(&vrrp_dbg_auto, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Virtual IP list is empty; shutting down", + r->vr->vrid, family2str(r->family)); + vrrp_event(r, VRRP_EVENT_SHUTDOWN); + } +} + +static struct vrrp_vrouter * +vrrp_autoconfig_autocreate(struct interface *mvl_ifp) +{ + struct interface *p; + struct vrrp_vrouter *vr; + + p = if_lookup_by_index(mvl_ifp->link_ifindex, mvl_ifp->vrf_id); + + if (!p) + return NULL; + + uint8_t vrid = mvl_ifp->hw_addr[5]; + uint8_t fam = mvl_ifp->hw_addr[4]; + + DEBUGD(&vrrp_dbg_auto, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Autoconfiguring VRRP on %s", + vrid, family2str(fam), p->name); + + vr = vrrp_vrouter_create(p, vrid, vrrp_autoconfig_version); + + if (!vr) { + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Failed to autoconfigure VRRP on %s", + vrid, family2str(fam), p->name); + return NULL; + } + + vr->autoconf = true; + + /* + * If these interfaces are protodown on, we need to un-protodown them + * in order to get Zebra to send us their addresses so we can + * autoconfigure them. + */ + if (vr->v4->mvl_ifp) + vrrp_zclient_send_interface_protodown(vr->v4->mvl_ifp, false); + if (vr->v6->mvl_ifp) + vrrp_zclient_send_interface_protodown(vr->v6->mvl_ifp, false); + + /* If they're not, we can go ahead and add the addresses we have */ + vrrp_autoconfig_autoaddrupdate(vr->v4); + vrrp_autoconfig_autoaddrupdate(vr->v6); + + return vr; +} + +/* + * Callback to notify autoconfig of interface add. + * + * If the interface is a VRRP-compatible device, and there is no existing VRRP + * router running on it, one is created. All addresses on the interface are + * added to the router. + * + * ifp + * Interface to operate on + * + * Returns: + * -1 on failure + * 0 otherwise + */ +static int vrrp_autoconfig_if_add(struct interface *ifp) +{ + bool created = false; + struct vrrp_vrouter *vr; + + if (!vrrp_autoconfig_is_on) + return 0; + + if (!ifp || !ifp->link_ifindex || !vrrp_ifp_has_vrrp_mac(ifp)) + return -1; + + vr = vrrp_lookup_by_if_mvl(ifp); + + if (!vr) { + vr = vrrp_autoconfig_autocreate(ifp); + created = true; + } + + if (!vr || !vr->autoconf) + return 0; + + if (!created) { + /* + * We didn't create it, but it has already been autoconfigured. + * Try to attach this interface to the existing instance. + */ + if (!vr->v4->mvl_ifp) { + vrrp_attach_interface(vr->v4); + /* If we just attached it, make sure it's turned on */ + if (vr->v4->mvl_ifp) { + vrrp_zclient_send_interface_protodown( + vr->v4->mvl_ifp, false); + /* + * If it's already up, we can go ahead and add + * the addresses we have + */ + vrrp_autoconfig_autoaddrupdate(vr->v4); + } + } + if (!vr->v6->mvl_ifp) { + vrrp_attach_interface(vr->v6); + /* If we just attached it, make sure it's turned on */ + if (vr->v6->mvl_ifp) { + vrrp_zclient_send_interface_protodown( + vr->v6->mvl_ifp, false); + /* + * If it's already up, we can go ahead and add + * the addresses we have + */ + vrrp_autoconfig_autoaddrupdate(vr->v6); + } + } + } + + return 0; +} + +/* + * Callback to notify autoconfig of interface delete. + * + * If the interface is a VRRP-compatible device, and a VRRP router is running + * on it, and that VRRP router was automatically configured, it will be + * deleted. If that was the last router for the corresponding VRID (i.e., if + * this interface was a v4 VRRP interface and no v6 router is configured for + * the same VRID) then the entire virtual router is deleted. + * + * ifp + * Interface to operate on + * + * Returns: + * -1 on failure + * 0 otherwise + */ +static int vrrp_autoconfig_if_del(struct interface *ifp) +{ + if (!vrrp_autoconfig_is_on) + return 0; + + struct vrrp_vrouter *vr; + struct listnode *ln; + struct list *vrs; + + vrs = vrrp_lookup_by_if_any(ifp); + + for (ALL_LIST_ELEMENTS_RO(vrs, ln, vr)) + if (vr->autoconf + && (!vr->ifp || (!vr->v4->mvl_ifp && !vr->v6->mvl_ifp))) { + DEBUGD(&vrrp_dbg_auto, + VRRP_LOGPFX VRRP_LOGPFX_VRID + "All VRRP interfaces for instance deleted; destroying autoconfigured VRRP router", + vr->vrid); + vrrp_vrouter_destroy(vr); + } + + list_delete(&vrs); + + return 0; +} + +/* + * Callback to notify autoconfig of interface up. + * + * Creates VRRP instance on interface if it does not exist. Otherwise does + * nothing. + * + * ifp + * Interface to operate on + * + * Returns: + * -1 on failure + * 0 otherwise + */ +static int vrrp_autoconfig_if_up(struct interface *ifp) +{ + if (!vrrp_autoconfig_is_on) + return 0; + + struct vrrp_vrouter *vr = vrrp_lookup_by_if_mvl(ifp); + + if (vr && !vr->autoconf) + return 0; + + if (!vr) { + vrrp_autoconfig_if_add(ifp); + return 0; + } + + return 0; +} + +/* + * Callback to notify autoconfig of interface down. + * + * Does nothing. An interface down event is accompanied by address deletion + * events for all the addresses on the interface; if an autoconfigured VRRP + * router exists on this interface, then it will have all its addresses deleted + * and end up in Initialize. + * + * ifp + * Interface to operate on + * + * Returns: + * -1 on failure + * 0 otherwise + */ +static int vrrp_autoconfig_if_down(struct interface *ifp) +{ + if (!vrrp_autoconfig_is_on) + return 0; + + return 0; +} + +/* + * Callback to notify autoconfig of a new interface address. + * + * If a VRRP router exists on this interface, its address list is updated to + * match the new address list. If no addresses remain, a Shutdown event is + * issued to the VRRP router. + * + * ifp + * Interface to operate on + * + * Returns: + * -1 on failure + * 0 otherwise + * + */ +static int vrrp_autoconfig_if_address_add(struct interface *ifp) +{ + if (!vrrp_autoconfig_is_on) + return 0; + + struct vrrp_vrouter *vr = vrrp_lookup_by_if_mvl(ifp); + + if (vr && vr->autoconf) { + if (vr->v4->mvl_ifp == ifp) + vrrp_autoconfig_autoaddrupdate(vr->v4); + else if (vr->v6->mvl_ifp == ifp) + vrrp_autoconfig_autoaddrupdate(vr->v6); + } + + return 0; +} + +/* + * Callback to notify autoconfig of a removed interface address. + * + * If a VRRP router exists on this interface, its address list is updated to + * match the new address list. If no addresses remain, a Shutdown event is + * issued to the VRRP router. + * + * ifp + * Interface to operate on + * + * Returns: + * -1 on failure + * 0 otherwise + * + */ +static int vrrp_autoconfig_if_address_del(struct interface *ifp) +{ + if (!vrrp_autoconfig_is_on) + return 0; + + struct vrrp_vrouter *vr = vrrp_lookup_by_if_mvl(ifp); + + if (vr && vr->autoconf) { + if (vr->v4->mvl_ifp == ifp) + vrrp_autoconfig_autoaddrupdate(vr->v4); + else if (vr->v6->mvl_ifp == ifp) + vrrp_autoconfig_autoaddrupdate(vr->v6); + } + + return 0; +} + +int vrrp_autoconfig(void) +{ + if (!vrrp_autoconfig_is_on) + return 0; + + struct vrf *vrf; + struct interface *ifp; + + RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { + FOR_ALL_INTERFACES (vrf, ifp) + vrrp_autoconfig_if_add(ifp); + } + + return 0; +} + +void vrrp_autoconfig_on(int version) +{ + vrrp_autoconfig_is_on = true; + vrrp_autoconfig_version = version; + + vrrp_autoconfig(); +} + +void vrrp_autoconfig_off(void) +{ + vrrp_autoconfig_is_on = false; + + struct list *ll = hash_to_list(vrrp_vrouters_hash); + + struct listnode *ln; + struct vrrp_vrouter *vr; + + for (ALL_LIST_ELEMENTS_RO(ll, ln, vr)) + if (vr->autoconf) + vrrp_vrouter_destroy(vr); + + list_delete(&ll); +} + +/* Interface tracking ------------------------------------------------------ */ + +/* + * Bind any pending interfaces. + * + * mvl_ifp + * macvlan interface that some VRRP instances might want to bind to + */ +static void vrrp_bind_pending(struct interface *mvl_ifp) +{ + struct vrrp_vrouter *vr; + + DEBUGD(&vrrp_dbg_zebra, + VRRP_LOGPFX + "Searching for instances that could use interface %s", + mvl_ifp->name); + + vr = vrrp_lookup_by_if_mvl(mvl_ifp); + + if (vr) { + DEBUGD(&vrrp_dbg_zebra, + VRRP_LOGPFX VRRP_LOGPFX_VRID + "<-- This instance can probably use interface %s", + vr->vrid, mvl_ifp->name); + + if (mvl_ifp->hw_addr[4] == 0x01 && !vr->v4->mvl_ifp) + vrrp_attach_interface(vr->v4); + else if (mvl_ifp->hw_addr[4] == 0x02 && !vr->v6->mvl_ifp) + vrrp_attach_interface(vr->v6); + } +} + +void vrrp_if_up(struct interface *ifp) +{ + struct vrrp_vrouter *vr; + struct listnode *ln; + struct list *vrs; + + vrrp_bind_pending(ifp); + + vrs = vrrp_lookup_by_if_any(ifp); + + for (ALL_LIST_ELEMENTS_RO(vrs, ln, vr)) { + vrrp_check_start(vr); + + if (!if_is_operative(ifp)) + continue; + + /* + * Handle the situation in which we performed a state + * transition on this VRRP router but needed to wait for the + * macvlan interface to come up to perform some actions + */ + if (ifp == vr->v4->mvl_ifp) { + if (vr->v4->advert_pending) { + DEBUGD(&vrrp_dbg_proto, + VRRP_LOGPFX VRRP_LOGPFX_VRID + VRRP_LOGPFX_FAM + "Interface up; sending pending advertisement", + vr->vrid, family2str(vr->v4->family)); + vrrp_send_advertisement(vr->v4); + vr->v4->advert_pending = false; + } + if (vr->v4->garp_pending) { + DEBUGD(&vrrp_dbg_proto, + VRRP_LOGPFX VRRP_LOGPFX_VRID + VRRP_LOGPFX_FAM + "Interface up; sending pending gratuitous ARP", + vr->vrid, family2str(vr->v4->family)); + vrrp_garp_send_all(vr->v4); + vr->v4->garp_pending = false; + } + } + if (ifp == vr->v6->mvl_ifp) { + if (vr->v6->advert_pending) { + DEBUGD(&vrrp_dbg_proto, + VRRP_LOGPFX VRRP_LOGPFX_VRID + VRRP_LOGPFX_FAM + "Interface up; sending pending advertisement", + vr->vrid, family2str(vr->v6->family)); + vrrp_send_advertisement(vr->v6); + vr->v6->advert_pending = false; + } + if (vr->v6->ndisc_pending) { + DEBUGD(&vrrp_dbg_proto, + VRRP_LOGPFX VRRP_LOGPFX_VRID + VRRP_LOGPFX_FAM + "Interface up; sending pending Unsolicited Neighbor Advertisement", + vr->vrid, family2str(vr->v6->family)); + vrrp_ndisc_una_send_all(vr->v6); + vr->v6->ndisc_pending = false; + } + } + } + + list_delete(&vrs); + + vrrp_autoconfig_if_up(ifp); +} + +void vrrp_if_down(struct interface *ifp) +{ + struct vrrp_vrouter *vr; + struct listnode *ln; + struct list *vrs; + + vrrp_bind_pending(ifp); + + vrs = vrrp_lookup_by_if_any(ifp); + + for (ALL_LIST_ELEMENTS_RO(vrs, ln, vr)) { + vrrp_check_start(vr); + + if (vr->ifp == ifp || vr->v4->mvl_ifp == ifp + || vr->v6->mvl_ifp == ifp) { + DEBUGD(&vrrp_dbg_auto, + VRRP_LOGPFX VRRP_LOGPFX_VRID "Interface %s down", + vr->vrid, ifp->name); + } + } + + list_delete(&vrs); + + vrrp_autoconfig_if_down(ifp); +} + +void vrrp_if_add(struct interface *ifp) +{ + vrrp_bind_pending(ifp); + + /* thanks, zebra */ + if (CHECK_FLAG(ifp->flags, IFF_UP)) + vrrp_if_up(ifp); + + vrrp_autoconfig_if_add(ifp); +} + +void vrrp_if_del(struct interface *ifp) +{ + struct listnode *ln; + struct vrrp_vrouter *vr; + + vrrp_if_down(ifp); + + /* + * You think we'd be able use vrrp_lookup_by_if_any to find interfaces? + * Nah. FRR's interface management is insane. There are no ordering + * guarantees about what interfaces are deleted when. Maybe this is a + * macvlan and its parent was already deleted, in which case its + * ifindex is now IFINDEX_INTERNAL, so ifp->link_ifindex - while still + * valid - doesn't match any interface on the system, meaning we can't + * use any of the vrrp_lookup* functions since they rely on finding the + * base interface of what they're given by following link_ifindex. + * + * Since we need to actually NULL out pointers in this function to + * avoid a UAF - since the caller will (might) free ifp after we return + * - we need to look up based on pointers. + */ + struct list *vrs = hash_to_list(vrrp_vrouters_hash); + + for (ALL_LIST_ELEMENTS_RO(vrs, ln, vr)) { + if (ifp == vr->ifp) { + vrrp_event(vr->v4, VRRP_EVENT_SHUTDOWN); + vrrp_event(vr->v6, VRRP_EVENT_SHUTDOWN); + /* + * Stands to reason if the base was deleted, so were + * (or will be) its children + */ + vr->v4->mvl_ifp = NULL; + vr->v6->mvl_ifp = NULL; + /* + * We shouldn't need to lose the reference if it's the + * primary interface, because that was configured + * explicitly in our config, and thus will be kept as a + * stub; to avoid stupid bugs, double check that + */ + assert(ifp->configured); + } else if (ifp == vr->v4->mvl_ifp) { + vrrp_event(vr->v4, VRRP_EVENT_SHUTDOWN); + /* + * If this is a macvlan, then it wasn't explicitly + * configured and will be deleted when we return from + * this function, so we need to lose the reference + */ + vr->v4->mvl_ifp = NULL; + } else if (ifp == vr->v6->mvl_ifp) { + vrrp_event(vr->v6, VRRP_EVENT_SHUTDOWN); + /* + * If this is a macvlan, then it wasn't explicitly + * configured and will be deleted when we return from + * this function, so we need to lose the reference + */ + vr->v6->mvl_ifp = NULL; + } + } + + list_delete(&vrs); + + vrrp_autoconfig_if_del(ifp); +} + +void vrrp_if_address_add(struct interface *ifp) +{ + struct vrrp_vrouter *vr; + struct listnode *ln; + struct list *vrs; + + /* + * We have to do a wide search here, because we need to know when a v6 + * macvlan device gets a new address. This is because the macvlan link + * local is used as the source address for v6 advertisements, and hence + * "do I have a link local" constitutes an activation condition for v6 + * virtual routers. + */ + vrs = vrrp_lookup_by_if_any(ifp); + + for (ALL_LIST_ELEMENTS_RO(vrs, ln, vr)) + vrrp_check_start(vr); + + list_delete(&vrs); + + vrrp_autoconfig_if_address_add(ifp); +} + +void vrrp_if_address_del(struct interface *ifp) +{ + /* + * Zebra is stupid and sends us address deletion notifications + * when any of the following condition sets are met: + * + * - if_is_operative && address deleted + * - if_is_operative -> !if_is_operative + * + * Note that the second one is nonsense, because Zebra behaves as + * though an interface going down means all the addresses on that + * interface got deleted. Which is a problem for autoconfig because all + * the addresses on an interface going away means the VRRP session goes + * to Initialize. However interfaces go down whenever we transition to + * Backup, so this effectively means that for autoconfigured instances + * we actually end up in Initialize whenever we try to go into Backup. + * + * Also, Zebra does NOT send us notifications when: + * - !if_is_operative && address deleted + * + * Which means if we're in backup and an address is deleted out from + * under us, we won't even know. + * + * The only solution here is to only resynchronize our address list + * when: + * + * - An interfaces comes up + * - An interface address is added + * - An interface address is deleted AND the interface is up + * + * Even though this is only a problem with autoconfig at the moment I'm + * papering over Zebra's braindead semantics here. Every piece of code + * in this function should be protected by a check that the interface + * is up. + */ + if (if_is_operative(ifp)) + vrrp_autoconfig_if_address_del(ifp); +} + +/* Other ------------------------------------------------------------------- */ + +int vrrp_config_write_global(struct vty *vty) +{ + unsigned int writes = 0; + + if (vrrp_autoconfig_is_on && ++writes) + vty_out(vty, "vrrp autoconfigure%s\n", + vrrp_autoconfig_version == 2 ? " version 2" : ""); + + /* FIXME: needs to be udpated for full YANG conversion. */ + if (vd.priority != VRRP_DEFAULT_PRIORITY && ++writes) + vty_out(vty, "vrrp default priority %hhu\n", vd.priority); + + if (vd.advertisement_interval != VRRP_DEFAULT_ADVINT && ++writes) + vty_out(vty, + "vrrp default advertisement-interval %u\n", + vd.advertisement_interval * CS2MS); + + if (vd.preempt_mode != VRRP_DEFAULT_PREEMPT && ++writes) + vty_out(vty, "%svrrp default preempt\n", + !vd.preempt_mode ? "no " : ""); + + if (vd.accept_mode != VRRP_DEFAULT_ACCEPT && ++writes) + vty_out(vty, "%svrrp default accept\n", + !vd.accept_mode ? "no " : ""); + + if (vd.shutdown != VRRP_DEFAULT_SHUTDOWN && ++writes) + vty_out(vty, "%svrrp default shutdown\n", + !vd.shutdown ? "no " : ""); + + return writes; +} + +static unsigned int vrrp_hash_key(const void *arg) +{ + const struct vrrp_vrouter *vr = arg; + char key[IFNAMSIZ + 64]; + + snprintf(key, sizeof(key), "%s@%u", vr->ifp->name, vr->vrid); + + return string_hash_make(key); +} + +static bool vrrp_hash_cmp(const void *arg1, const void *arg2) +{ + const struct vrrp_vrouter *vr1 = arg1; + const struct vrrp_vrouter *vr2 = arg2; + + if (vr1->ifp != vr2->ifp) + return false; + if (vr1->vrid != vr2->vrid) + return false; + + return true; +} + +void vrrp_init(void) +{ + /* Set default defaults */ + vd.version = yang_get_default_uint8("%s/version", VRRP_XPATH_FULL); + vd.priority = yang_get_default_uint8("%s/priority", VRRP_XPATH_FULL); + vd.advertisement_interval = yang_get_default_uint16( + "%s/advertisement-interval", VRRP_XPATH_FULL); + vd.preempt_mode = yang_get_default_bool("%s/preempt", VRRP_XPATH_FULL); + vd.accept_mode = + yang_get_default_bool("%s/accept-mode", VRRP_XPATH_FULL); + vd.shutdown = VRRP_DEFAULT_SHUTDOWN; + + vrrp_autoconfig_version = 3; + vrrp_vrouters_hash = hash_create(&vrrp_hash_key, vrrp_hash_cmp, + "VRRP virtual router hash"); + vrf_init(NULL, NULL, NULL, NULL, NULL); +} + +void vrrp_fini(void) +{ + /* Destroy all instances */ + struct list *vrs = hash_to_list(vrrp_vrouters_hash); + + struct listnode *ln; + struct vrrp_vrouter *vr; + + for (ALL_LIST_ELEMENTS_RO(vrs, ln, vr)) + vrrp_vrouter_destroy(vr); + + list_delete(&vrs); + + hash_clean(vrrp_vrouters_hash, NULL); + hash_free(vrrp_vrouters_hash); +} diff --git a/vrrpd/vrrp.h b/vrrpd/vrrp.h new file mode 100644 index 0000000000..502d7b82b6 --- /dev/null +++ b/vrrpd/vrrp.h @@ -0,0 +1,568 @@ +/* + * VRRP global definitions and state machine. + * Copyright (C) 2018-2019 Cumulus Networks, Inc. + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef __VRRP_H__ +#define __VRRP_H__ + +#include +#include + +#include "lib/memory.h" +#include "lib/hash.h" +#include "lib/hook.h" +#include "lib/if.h" +#include "lib/linklist.h" +#include "lib/northbound.h" +#include "lib/privs.h" +#include "lib/stream.h" +#include "lib/thread.h" +#include "lib/vty.h" + +/* Global definitions */ +#define VRRP_RADV_INT 16 +#define VRRP_PRIO_MASTER 255 +#define VRRP_MCASTV4_GROUP_STR "224.0.0.18" +#define VRRP_MCASTV6_GROUP_STR "ff02:0:0:0:0:0:0:12" +#define VRRP_MCASTV4_GROUP 0xe0000012 +#define VRRP_MCASTV6_GROUP 0xff020000000000000000000000000012 +#define IPPROTO_VRRP 112 + +#define VRRP_LOGPFX_VRID "[VRID %u] " +#define VRRP_LOGPFX_FAM "[%s] " + +/* Default defaults */ +#define VRRP_XPATH_FULL "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group" +#define VRRP_XPATH "./frr-vrrpd:vrrp/vrrp-group" +#define VRRP_DEFAULT_PRIORITY 100 +#define VRRP_DEFAULT_ADVINT 100 +#define VRRP_DEFAULT_PREEMPT true +#define VRRP_DEFAULT_ACCEPT true +#define VRRP_DEFAULT_SHUTDOWN false + +/* User compatibility constant */ +#define CS2MS 10 + +DECLARE_MGROUP(VRRPD) + +/* Northbound */ +extern const struct frr_yang_module_info frr_vrrpd_info; + +/* Configured defaults */ +struct vrrp_defaults { + uint8_t version; + uint8_t priority; + uint16_t advertisement_interval; + bool preempt_mode; + bool accept_mode; + bool shutdown; +}; + +extern struct vrrp_defaults vd; + +/* threadmaster */ +extern struct thread_master *master; + +/* privileges */ +extern struct zebra_privs_t vrrp_privs; + +/* Global hash of all Virtual Routers */ +extern struct hash *vrrp_vrouters_hash; + +/* + * VRRP Router. + * + * This struct contains all state for a particular VRRP Router operating + * in a Virtual Router for either IPv4 or IPv6. + */ +struct vrrp_router { + /* + * Whether this VRRP Router is active. + */ + bool is_active; + + /* Whether we are the address owner */ + bool is_owner; + + /* Rx socket: Rx from parent of mvl_ifp */ + int sock_rx; + /* Tx socket; Tx from mvl_ifp */ + int sock_tx; + + /* macvlan interface */ + struct interface *mvl_ifp; + + /* Source address for advertisements */ + struct ipaddr src; + + /* Socket read buffer */ + uint8_t ibuf[IP_MAXPACKET]; + + /* + * Address family of this Virtual Router. + * Either AF_INET or AF_INET6. + */ + int family; + + /* + * Virtual Router this VRRP Router is participating in. + */ + struct vrrp_vrouter *vr; + + /* + * One or more IPvX addresses associated with this Virtual + * Router. The first address must be the "primary" address this + * Virtual Router is backing up in the case of IPv4. In the case of + * IPv6 it must be the link-local address of vr->ifp. + * + * Type: struct ipaddr * + */ + struct list *addrs; + + /* + * This flag says whether we are waiting on an interface up + * notification from Zebra before we send an ADVERTISEMENT. + */ + bool advert_pending; + + /* + * If this is an IPv4 VRRP router, this flag says whether we are + * waiting on an interface up notification from Zebra before we send + * gratuitous ARP packets for all our addresses. Should never be true + * if family == AF_INET6. + */ + bool garp_pending; + /* + * If this is an IPv6 VRRP router, this flag says whether we are + * waiting on an interface up notification from Zebra before we send + * Unsolicited Neighbor Advertisement packets for all our addresses. + * Should never be true if family == AF_INET. + */ + bool ndisc_pending; + + /* + * Effective priority + * => vr->priority if we are Backup + * => 255 if we are Master + */ + uint8_t priority; + + /* + * Advertisement interval contained in ADVERTISEMENTS received from the + * Master (centiseconds) + */ + uint16_t master_adver_interval; + + /* + * Time to skew Master_Down_Interval in centiseconds. Calculated as: + * (((256 - priority) * Master_Adver_Interval) / 256) + */ + uint16_t skew_time; + + /* + * Time interval for Backup to declare Master down (centiseconds). + * Calculated as: + * (3 * Master_Adver_Interval) + Skew_time + */ + uint16_t master_down_interval; + + /* + * The MAC address used for the source MAC address in VRRP + * advertisements, advertised in ARP requests/responses, and advertised + * in ND Neighbor Advertisements. + */ + struct ethaddr vmac; + + struct { + int state; + } fsm; + + struct { + /* Total number of advertisements sent and received */ + uint32_t adver_tx_cnt; + uint32_t adver_rx_cnt; + /* Total number of gratuitous ARPs sent */ + uint32_t garp_tx_cnt; + /* Total number of unsolicited Neighbor Advertisements sent */ + uint32_t una_tx_cnt; + /* Total number of state transitions */ + uint32_t trans_cnt; + } stats; + + struct thread *t_master_down_timer; + struct thread *t_adver_timer; + struct thread *t_read; + struct thread *t_write; +}; + +/* + * VRRP Virtual Router. + * + * This struct contains all state and configuration for a given Virtual Router + * Identifier on a given interface, both v4 and v6. + * + * RFC5798 s. 1 states: + * "Within a VRRP router, the virtual routers in each of the IPv4 and IPv6 + * address families are a domain unto themselves and do not overlap." + * + * This implementation has chosen the tuple (interface, VRID) as the key for a + * particular VRRP Router, and the rest of the program is designed around this + * assumption. Additionally, base protocol configuration parameters such as the + * advertisement interval and (configured) priority are shared between v4 and + * v6 instances. This corresponds to the choice made by other industrial + * implementations. + */ +struct vrrp_vrouter { + /* Whether this instance was automatically configured */ + bool autoconf; + + /* Whether this VRRP router is in administrative shutdown */ + bool shutdown; + + /* Interface */ + struct interface *ifp; + + /* Version */ + uint8_t version; + + /* Virtual Router Identifier */ + uint32_t vrid; + + /* Configured priority */ + uint8_t priority; + + /* + * Time interval between ADVERTISEMENTS (centiseconds). Default is 100 + * centiseconds (1 second). + */ + uint16_t advertisement_interval; + + /* + * Controls whether a (starting or restarting) higher-priority Backup + * router preempts a lower-priority Master router. Values are True to + * allow preemption and False to prohibit preemption. Default is True. + */ + bool preempt_mode; + + /* + * Controls whether a virtual router in Master state will accept + * packets addressed to the address owner's IPvX address as its own if + * it is not the IPvX address owner. The default is False. + */ + bool accept_mode; + + struct vrrp_router *v4; + struct vrrp_router *v6; +}; + +/* + * Initialize VRRP global datastructures. + */ +void vrrp_init(void); + +/* + * Destroy all VRRP instances and gracefully shutdown. + * + * For instances in Master state, VRRP advertisements with 0 priority will be + * sent if possible to notify Backup routers that we are going away. + */ +void vrrp_fini(void); + + +/* Creation and destruction ------------------------------------------------ */ + +/* + * Create and register a new VRRP Virtual Router. + * + * ifp + * Base interface to configure VRRP on + * + * vrid + * Virtual Router Identifier + */ +struct vrrp_vrouter *vrrp_vrouter_create(struct interface *ifp, uint8_t vrid, + uint8_t version); + +/* + * Destroy a VRRP Virtual Router, freeing all its resources. + * + * If there are any running VRRP instances, these are stopped and destroyed. + */ +void vrrp_vrouter_destroy(struct vrrp_vrouter *vr); + + +/* Configuration controllers ----------------------------------------------- */ + +/* + * Check if a Virtual Router ought to be started, and if so, start it. + * + * vr + * Virtual Router to checkstart + */ +void vrrp_check_start(struct vrrp_vrouter *vr); + +/* + * Change the configured priority of a VRRP Virtual Router. + * + * Note that this only changes the configured priority of the Virtual Router. + * The currently effective priority will not be changed; to change the + * effective priority, the Virtual Router must be restarted by issuing a + * VRRP_EVENT_SHUTDOWN followed by a VRRP_EVENT_STARTUP. + * + * vr + * Virtual Router to change priority of + * + * priority + * New priority + */ +void vrrp_set_priority(struct vrrp_vrouter *vr, uint8_t priority); + +/* + * Set Advertisement Interval on this Virtual Router. + * + * vr + * Virtual Router to change priority of + * + * advertisement_interval + * New advertisement interval + */ +void vrrp_set_advertisement_interval(struct vrrp_vrouter *vr, + uint16_t advertisement_interval); + +/* + * Add an IPvX address to a VRRP Virtual Router. + * + * vr + * Virtual Router to add IPvx address to + * + * ip + * Address to add + * + * activate + * Whether to automatically start the VRRP router if this is the first IP + * address added. + * + * Returns: + * -1 on error + * 0 otherwise + */ +int vrrp_add_ip(struct vrrp_vrouter *vr, struct ipaddr *ip); + +/* + * Add an IPv4 address to a VRRP Virtual Router. + * + * vr + * Virtual Router to add IPv4 address to + * + * v4 + * Address to add + * + * activate + * Whether to automatically start the VRRP router if this is the first IP + * address added. + * + * Returns: + * -1 on error + * 0 otherwise + */ +int vrrp_add_ipv4(struct vrrp_vrouter *vr, struct in_addr v4); + +/* + * Add an IPv6 address to a VRRP Virtual Router. + * + * vr + * Virtual Router to add IPv6 address to + * + * v6 + * Address to add + * + * activate + * Whether to automatically start the VRRP router if this is the first IP + * address added. + * + * Returns: + * -1 on error + * 0 otherwise + */ +int vrrp_add_ipv6(struct vrrp_vrouter *vr, struct in6_addr v6); + +/* + * Remove an IP address from a VRRP Virtual Router. + * + * vr + * Virtual Router to remove IP address from + * + * ip + * Address to remove + * + * deactivate + * Whether to automatically stop the VRRP router if removing v4 would leave + * us with an empty address list. If this is not true and ip is the only IP + * address backed up by this virtual router, this function will not remove + * the address and return failure. + * + * Returns: + * -1 on error + * 0 otherwise + */ +int vrrp_del_ip(struct vrrp_vrouter *vr, struct ipaddr *ip); + +/* + * Remove an IPv4 address from a VRRP Virtual Router. + * + * vr + * Virtual Router to remove IPv4 address from + * + * v4 + * Address to remove + * + * deactivate + * Whether to automatically stop the VRRP router if removing v4 would leave + * us with an empty address list. If this is not true and v4 is the only + * IPv4 address backed up by this virtual router, this function will not + * remove the address and return failure. + * + * Returns: + * -1 on error + * 0 otherwise + */ +int vrrp_del_ipv4(struct vrrp_vrouter *vr, struct in_addr v4); + +/* + * Remove an IPv6 address from a VRRP Virtual Router. + * + * vr + * Virtual Router to remove IPv6 address from + * + * v6 + * Address to remove + * + * deactivate + * Whether to automatically stop the VRRP router if removing v5 would leave + * us with an empty address list. If this is not true and v4 is the only + * IPv6 address backed up by this virtual router, this function will not + * remove the address and return failure. + * + * Returns: + * -1 on error + * 0 otherwise + */ +int vrrp_del_ipv6(struct vrrp_vrouter *vr, struct in6_addr v6); + +/* State machine ----------------------------------------------------------- */ + +#define VRRP_STATE_INITIALIZE 0 +#define VRRP_STATE_MASTER 1 +#define VRRP_STATE_BACKUP 2 +#define VRRP_EVENT_STARTUP 0 +#define VRRP_EVENT_SHUTDOWN 1 + +extern const char *const vrrp_state_names[3]; + +/* + * This hook called whenever the state of a Virtual Router changes, after the + * specific internal state handlers have run. + * + * Use this if you need to react to state changes to perform non-critical + * tasks. Critical tasks should go in the internal state change handlers. + */ +DECLARE_HOOK(vrrp_change_state_hook, (struct vrrp_router *r, int to), (r, to)); + +/* + * Trigger a VRRP event on a given Virtual Router.. + * + * vr + * Virtual Router to operate on + * + * event + * Event to kick off. All event related processing will have completed upon + * return of this function. + * + * Returns: + * < 0 if the event created an error + * 0 otherwise + */ +int vrrp_event(struct vrrp_router *r, int event); + +/* Autoconfig -------------------------------------------------------------- */ + +/* + * Search for and automatically configure VRRP instances on interfaces. + * + * ifp + * Interface to autoconfig. If it is a macvlan interface and has a VRRP MAC, + * a VRRP instance corresponding to VMAC assigned to macvlan will be created + * on the parent interface and all addresses on the macvlan interface except + * the v6 link local will be configured as VRRP addresses. If NULL, this + * treatment will be applied to all existing interfaces matching the above + * criterion. + * + * Returns: + * -1 on failure + * 0 otherwise + */ +int vrrp_autoconfig(void); + +/* + * Enable autoconfiguration. + * + * Calling this function will cause vrrpd to automatically configure VRRP + * instances on existing compatible macvlan interfaces. These instances will + * react to interface up/down and address add/delete events to keep themselves + * in sync with the available interfaces. + * + * version + * VRRP version to use for autoconfigured instances. Must be 2 or 3. + */ +void vrrp_autoconfig_on(int version); + +/* + * Disable autoconfiguration. + * + * Calling this function will delete all existing autoconfigured VRRP instances. + */ +void vrrp_autoconfig_off(void); + +/* Interface Tracking ------------------------------------------------------ */ + +void vrrp_if_add(struct interface *ifp); +void vrrp_if_del(struct interface *ifp); +void vrrp_if_up(struct interface *ifp); +void vrrp_if_down(struct interface *ifp); +void vrrp_if_address_add(struct interface *ifp); +void vrrp_if_address_del(struct interface *ifp); + +/* Other ------------------------------------------------------------------- */ + +/* + * Write global level configuration to vty. + * + * vty + * vty to write config to + * + * Returns: + * # of lines written + */ +int vrrp_config_write_global(struct vty *vty); + +/* + * Find VRRP Virtual Router by Virtual Router ID + */ +struct vrrp_vrouter *vrrp_lookup(const struct interface *ifp, uint8_t vrid); + +#endif /* __VRRP_H__ */ diff --git a/vrrpd/vrrp_arp.c b/vrrpd/vrrp_arp.c new file mode 100644 index 0000000000..750050e8c3 --- /dev/null +++ b/vrrpd/vrrp_arp.c @@ -0,0 +1,219 @@ +/* + * VRRP ARP handling. + * Copyright (C) 2001-2017 Alexandre Cassen + * Portions: + * Copyright (C) 2018-2019 Cumulus Networks, Inc. + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include + +#include +#include +#include + +#include "lib/if.h" +#include "lib/linklist.h" +#include "lib/log.h" +#include "lib/memory.h" +#include "lib/prefix.h" + +#include "vrrp.h" +#include "vrrp_arp.h" +#include "vrrp_debug.h" + +#define VRRP_LOGPFX "[ARP] " + +/* + * The size of the garp packet buffer should be the large enough to hold the + * largest arp packet to be sent + the size of the link layer header for the + * corresponding protocol. In this case we hardcode for Ethernet. + */ +#define GARP_BUFFER_SIZE \ + sizeof(struct ether_header) + sizeof(struct arphdr) + 2 * ETH_ALEN \ + + 2 * sizeof(struct in_addr) + +/* static vars */ +static int garp_fd = -1; + +/* Send the gratuitous ARP message */ +static ssize_t vrrp_send_garp(struct interface *ifp, uint8_t *buf, + ssize_t pack_len) +{ + struct sockaddr_ll sll; + ssize_t len; + + /* Build the dst device */ + memset(&sll, 0, sizeof(sll)); + sll.sll_family = AF_PACKET; + sll.sll_protocol = ETH_P_ARP; + sll.sll_ifindex = (int)ifp->ifindex; + sll.sll_halen = ifp->hw_addr_len; + memset(sll.sll_addr, 0xFF, ETH_ALEN); + + /* Send packet */ + len = sendto(garp_fd, buf, pack_len, 0, (struct sockaddr *)&sll, + sizeof(sll)); + + return len; +} + +/* Build a gratuitous ARP message over a specific interface */ +static ssize_t vrrp_build_garp(uint8_t *buf, struct interface *ifp, + struct in_addr *v4) +{ + uint8_t *arp_ptr; + + if (ifp->hw_addr_len == 0) + return -1; + + /* Build Ethernet header */ + struct ether_header *eth = (struct ether_header *)buf; + + memset(eth->ether_dhost, 0xFF, ETH_ALEN); + memcpy(eth->ether_shost, ifp->hw_addr, ETH_ALEN); + eth->ether_type = htons(ETHERTYPE_ARP); + + /* Build ARP payload */ + struct arphdr *arph = (struct arphdr *)(buf + ETHER_HDR_LEN); + + arph->ar_hrd = htons(HWTYPE_ETHER); + arph->ar_pro = htons(ETHERTYPE_IP); + arph->ar_hln = ifp->hw_addr_len; + arph->ar_pln = sizeof(struct in_addr); + arph->ar_op = htons(ARPOP_REQUEST); + arp_ptr = (uint8_t *)(arph + 1); + /* Source MAC: us */ + memcpy(arp_ptr, ifp->hw_addr, ifp->hw_addr_len); + arp_ptr += ifp->hw_addr_len; + /* Source IP: us */ + memcpy(arp_ptr, v4, sizeof(struct in_addr)); + arp_ptr += sizeof(struct in_addr); + /* Dest MAC: broadcast */ + memset(arp_ptr, 0xFF, ETH_ALEN); + arp_ptr += ifp->hw_addr_len; + /* Dest IP: us */ + memcpy(arp_ptr, v4, sizeof(struct in_addr)); + arp_ptr += sizeof(struct in_addr); + + return arp_ptr - buf; +} + +void vrrp_garp_send(struct vrrp_router *r, struct in_addr *v4) +{ + struct interface *ifp = r->mvl_ifp; + uint8_t garpbuf[GARP_BUFFER_SIZE]; + ssize_t garpbuf_len; + ssize_t sent_len; + char astr[INET_ADDRSTRLEN]; + + /* If the interface doesn't support ARP, don't try sending */ + if (ifp->flags & IFF_NOARP) { + zlog_warn( + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Unable to send gratuitous ARP on %s; has IFF_NOARP", + r->vr->vrid, family2str(r->family), ifp->name); + return; + } + + /* Build garp */ + garpbuf_len = vrrp_build_garp(garpbuf, ifp, v4); + + if (garpbuf_len < 0) { + zlog_warn( + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Unable to send gratuitous ARP on %s; MAC address unknown", + r->vr->vrid, family2str(r->family), ifp->name); + return; + }; + + /* Send garp */ + inet_ntop(AF_INET, v4, astr, sizeof(astr)); + + DEBUGD(&vrrp_dbg_arp, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Sending gratuitous ARP on %s for %s", + r->vr->vrid, family2str(r->family), ifp->name, astr); + if (DEBUG_MODE_CHECK(&vrrp_dbg_arp, DEBUG_MODE_ALL)) + zlog_hexdump(garpbuf, garpbuf_len); + + sent_len = vrrp_send_garp(ifp, garpbuf, garpbuf_len); + + if (sent_len < 0) + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Error sending gratuitous ARP on %s for %s", + r->vr->vrid, family2str(r->family), ifp->name, astr); + else + ++r->stats.garp_tx_cnt; +} + +void vrrp_garp_send_all(struct vrrp_router *r) +{ + assert(r->family == AF_INET); + + struct interface *ifp = r->mvl_ifp; + + /* If the interface doesn't support ARP, don't try sending */ + if (ifp->flags & IFF_NOARP) { + zlog_warn( + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Unable to send gratuitous ARP on %s; has IFF_NOARP\n", + r->vr->vrid, family2str(r->family), ifp->name); + return; + } + + struct listnode *ln; + struct ipaddr *ip; + + for (ALL_LIST_ELEMENTS_RO(r->addrs, ln, ip)) + vrrp_garp_send(r, &ip->ipaddr_v4); +} + + +void vrrp_garp_init(void) +{ + /* Create the socket descriptor */ + /* FIXME: why ETH_P_RARP? */ + errno = 0; + frr_with_privs(&vrrp_privs) { + garp_fd = socket(PF_PACKET, SOCK_RAW | SOCK_CLOEXEC, + htons(ETH_P_RARP)); + } + + if (garp_fd > 0) { + DEBUGD(&vrrp_dbg_sock, + VRRP_LOGPFX "Initialized gratuitous ARP socket"); + DEBUGD(&vrrp_dbg_arp, + VRRP_LOGPFX "Initialized gratuitous ARP subsystem"); + } else { + zlog_err(VRRP_LOGPFX + "Error initializing gratuitous ARP subsystem"); + } +} + +void vrrp_garp_fini(void) +{ + close(garp_fd); + garp_fd = -1; + + DEBUGD(&vrrp_dbg_arp, + VRRP_LOGPFX "Deinitialized gratuitous ARP subsystem"); +} + +bool vrrp_garp_is_init(void) +{ + return garp_fd > 0; +} diff --git a/vrrpd/vrrp_arp.h b/vrrpd/vrrp_arp.h new file mode 100644 index 0000000000..21f2c4edd1 --- /dev/null +++ b/vrrpd/vrrp_arp.h @@ -0,0 +1,36 @@ +/* + * VRRP ARP handling. + * Copyright (C) 2018-2019 Cumulus Networks, Inc. + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef __VRRP_ARP_H__ +#define __VRRP_ARP_H__ + +#include + +#include "vrrp.h" + +/* FIXME: Use the kernel define for this */ +#define HWTYPE_ETHER 1 + +extern void vrrp_garp_init(void); +extern void vrrp_garp_fini(void); +extern bool vrrp_garp_is_init(void); +extern void vrrp_garp_send(struct vrrp_router *vr, struct in_addr *v4); +extern void vrrp_garp_send_all(struct vrrp_router *vr); + +#endif /* __VRRP_ARP_H__ */ diff --git a/vrrpd/vrrp_debug.c b/vrrpd/vrrp_debug.c new file mode 100644 index 0000000000..09d5e780cf --- /dev/null +++ b/vrrpd/vrrp_debug.c @@ -0,0 +1,131 @@ +/* + * VRRP debugging. + * Copyright (C) 2019 Cumulus Networks, Inc. + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include + +#include "lib/command.h" +#include "lib/debug.h" +#include "lib/vector.h" + +#include "vrrp_debug.h" + +/* clang-format off */ +struct debug vrrp_dbg_arp = {0, "VRRP ARP"}; +struct debug vrrp_dbg_auto = {0, "VRRP autoconfiguration events"}; +struct debug vrrp_dbg_ndisc = {0, "VRRP Neighbor Discovery"}; +struct debug vrrp_dbg_pkt = {0, "VRRP packets"}; +struct debug vrrp_dbg_proto = {0, "VRRP protocol events"}; +struct debug vrrp_dbg_sock = {0, "VRRP sockets"}; +struct debug vrrp_dbg_zebra = {0, "VRRP Zebra events"}; + +struct debug *vrrp_debugs[] = { + &vrrp_dbg_arp, + &vrrp_dbg_auto, + &vrrp_dbg_ndisc, + &vrrp_dbg_pkt, + &vrrp_dbg_proto, + &vrrp_dbg_sock, + &vrrp_dbg_zebra +}; + +const char *vrrp_debugs_conflines[] = { + "debug vrrp arp", + "debug vrrp autoconfigure", + "debug vrrp ndisc", + "debug vrrp packets", + "debug vrrp protocol", + "debug vrrp sockets", + "debug vrrp zebra", +}; +/* clang-format on */ + +/* + * Set or unset flags on all debugs for vrrpd. + * + * flags + * The flags to set + * + * set + * Whether to set or unset the specified flags + */ +static void vrrp_debug_set_all(uint32_t flags, bool set) +{ + for (unsigned int i = 0; i < array_size(vrrp_debugs); i++) { + DEBUG_FLAGS_SET(vrrp_debugs[i], flags, set); + + /* if all modes have been turned off, don't preserve options */ + if (!DEBUG_MODE_CHECK(vrrp_debugs[i], DEBUG_MODE_ALL)) + DEBUG_CLEAR(vrrp_debugs[i]); + } +} + +static int vrrp_debug_config_write_helper(struct vty *vty, bool config) +{ + uint32_t mode = DEBUG_MODE_ALL; + + if (config) + mode = DEBUG_MODE_CONF; + + for (unsigned int i = 0; i < array_size(vrrp_debugs); i++) + if (DEBUG_MODE_CHECK(vrrp_debugs[i], mode)) + vty_out(vty, "%s\n", vrrp_debugs_conflines[i]); + + return 0; +} + +int vrrp_config_write_debug(struct vty *vty) +{ + return vrrp_debug_config_write_helper(vty, true); +} + +int vrrp_debug_status_write(struct vty *vty) +{ + return vrrp_debug_config_write_helper(vty, false); +} + +void vrrp_debug_set(struct interface *ifp, uint8_t vrid, int vtynode, + bool onoff, bool proto, bool autoconf, bool pkt, bool sock, + bool ndisc, bool arp, bool zebra) +{ + uint32_t mode = DEBUG_NODE2MODE(vtynode); + + if (proto) + DEBUG_MODE_SET(&vrrp_dbg_proto, mode, onoff); + if (autoconf) + DEBUG_MODE_SET(&vrrp_dbg_auto, mode, onoff); + if (pkt) + DEBUG_MODE_SET(&vrrp_dbg_pkt, mode, onoff); + if (sock) + DEBUG_MODE_SET(&vrrp_dbg_sock, mode, onoff); + if (ndisc) + DEBUG_MODE_SET(&vrrp_dbg_ndisc, mode, onoff); + if (arp) + DEBUG_MODE_SET(&vrrp_dbg_arp, mode, onoff); + if (zebra) + DEBUG_MODE_SET(&vrrp_dbg_zebra, mode, onoff); +} + +/* ------------------------------------------------------------------------- */ + +struct debug_callbacks vrrp_dbg_cbs = {.debug_set_all = vrrp_debug_set_all}; + +void vrrp_debug_init(void) +{ + debug_init(&vrrp_dbg_cbs); +} diff --git a/vrrpd/vrrp_debug.h b/vrrpd/vrrp_debug.h new file mode 100644 index 0000000000..459d4d01aa --- /dev/null +++ b/vrrpd/vrrp_debug.h @@ -0,0 +1,87 @@ +/* + * VRRP debugging. + * Copyright (C) 2019 Cumulus Networks, Inc. + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef __VRRP_DEBUG_H__ +#define __VRRP_DEBUG_H__ + +#include + +#include "lib/debug.h" + +/* VRRP debugging records */ +extern struct debug vrrp_dbg_arp; +extern struct debug vrrp_dbg_auto; +extern struct debug vrrp_dbg_ndisc; +extern struct debug vrrp_dbg_pkt; +extern struct debug vrrp_dbg_proto; +extern struct debug vrrp_dbg_sock; +extern struct debug vrrp_dbg_zebra; + +/* + * Initialize VRRP debugging. + * + * Installs VTY commands and registers callbacks. + */ +void vrrp_debug_init(void); + +/* + * Print VRRP debugging configuration. + * + * vty + * VTY to print debugging configuration to. + */ +int vrrp_config_write_debug(struct vty *vty); + +/* + * Print VRRP debugging configuration, human readable form. + * + * vty + * VTY to print debugging configuration to. + */ +int vrrp_debug_status_write(struct vty *vty); + +/* + * Set debugging status. + * + * ifp + * Interface to set status on + * + * vrid + * VRID of instance to set status on + * + * vtynode + * vty->node + * + * onoff + * Whether to turn the specified debugs on or off + * + * proto + * Turn protocol debugging on or off + * + * autoconf + * Turn autoconfiguration debugging on or off + * + * pkt + * Turn packet debugging on or off + */ +void vrrp_debug_set(struct interface *ifp, uint8_t vrid, int vtynode, + bool onoff, bool proto, bool autoconf, bool pkt, bool sock, + bool ndisc, bool arp, bool zebra); + +#endif /* __VRRP_DEBUG_H__ */ diff --git a/vrrpd/vrrp_main.c b/vrrpd/vrrp_main.c new file mode 100644 index 0000000000..94f10757a2 --- /dev/null +++ b/vrrpd/vrrp_main.c @@ -0,0 +1,172 @@ +/* + * VRRP entry point. + * Copyright (C) 2018-2019 Cumulus Networks, Inc. + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include + +#include + +#include "lib/command.h" +#include "lib/filter.h" +#include "lib/getopt.h" +#include "lib/if.h" +#include "lib/libfrr.h" +#include "lib/log.h" +#include "lib/memory.h" +#include "lib/nexthop.h" +#include "lib/privs.h" +#include "lib/sigevent.h" +#include "lib/thread.h" +#include "lib/vrf.h" +#include "lib/vty.h" + +#include "vrrp.h" +#include "vrrp_debug.h" +#include "vrrp_vty.h" +#include "vrrp_zebra.h" + +DEFINE_MGROUP(VRRPD, "vrrpd") + +char backup_config_file[256]; + +zebra_capabilities_t _caps_p[] = { + ZCAP_NET_RAW, +}; + +struct zebra_privs_t vrrp_privs = { +#if defined(FRR_USER) && defined(FRR_GROUP) + .user = FRR_USER, + .group = FRR_GROUP, +#endif +#if defined(VTY_GROUP) + .vty_group = VTY_GROUP, +#endif + .caps_p = _caps_p, + .cap_num_p = array_size(_caps_p), + .cap_num_i = 0}; + +struct option longopts[] = { {0} }; + +/* Master of threads. */ +struct thread_master *master; + +static struct frr_daemon_info vrrpd_di; + +/* SIGHUP handler. */ +static void sighup(void) +{ + zlog_info("SIGHUP received"); + + vty_read_config(NULL, vrrpd_di.config_file, config_default); +} + +/* SIGINT / SIGTERM handler. */ +static void __attribute__((noreturn)) sigint(void) +{ + zlog_notice("Terminating on signal"); + + vrrp_fini(); + + frr_fini(); + + exit(0); +} + +/* SIGUSR1 handler. */ +static void sigusr1(void) +{ + zlog_rotate(); +} + +struct quagga_signal_t vrrp_signals[] = { + { + .signal = SIGHUP, + .handler = &sighup, + }, + { + .signal = SIGUSR1, + .handler = &sigusr1, + }, + { + .signal = SIGINT, + .handler = &sigint, + }, + { + .signal = SIGTERM, + .handler = &sigint, + }, +}; + +static const struct frr_yang_module_info *const vrrp_yang_modules[] = { + &frr_filter_info, + &frr_vrf_info, + &frr_interface_info, + &frr_vrrpd_info, +}; + +#define VRRP_VTY_PORT 2619 + +FRR_DAEMON_INFO(vrrpd, VRRP, .vty_port = VRRP_VTY_PORT, + .proghelp = "Virtual Router Redundancy Protocol", + .signals = vrrp_signals, + .n_signals = array_size(vrrp_signals), + .privs = &vrrp_privs, + .yang_modules = vrrp_yang_modules, + .n_yang_modules = array_size(vrrp_yang_modules), +) + +int main(int argc, char **argv, char **envp) +{ + frr_preinit(&vrrpd_di, argc, argv); + frr_opt_add("", longopts, ""); + + while (1) { + int opt; + + opt = frr_getopt(argc, argv, NULL); + + if (opt == EOF) + break; + + switch (opt) { + case 0: + break; + default: + frr_help_exit(1); + break; + } + } + + master = frr_init(); + + access_list_init(); + vrrp_debug_init(); + vrrp_zebra_init(); + vrrp_vty_init(); + vrrp_init(); + + snprintf(backup_config_file, sizeof(backup_config_file), + "%s/vrrpd.conf", frr_sysconfdir); + vrrpd_di.backup_config_file = backup_config_file; + + frr_config_fork(); + frr_run(master); + + /* Not reached. */ + return 0; +} diff --git a/vrrpd/vrrp_ndisc.c b/vrrpd/vrrp_ndisc.c new file mode 100644 index 0000000000..b989e66f60 --- /dev/null +++ b/vrrpd/vrrp_ndisc.c @@ -0,0 +1,243 @@ +/* + * VRRP Neighbor Discovery. + * Copyright (C) 2019 Cumulus Networks, Inc. + * Quentin Young + * + * Portions: + * Copyright (C) 2001-2017 Alexandre Cassen + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include + +#include +#include +#include +#include + +#include "lib/checksum.h" +#include "lib/if.h" +#include "lib/ipaddr.h" +#include "lib/log.h" + +#include "vrrp_debug.h" +#include "vrrp_ndisc.h" + +#define VRRP_LOGPFX "[NDISC] " + +#define VRRP_NDISC_HOPLIMIT 255 +#define VRRP_NDISC_SIZE \ + ETHER_HDR_LEN + sizeof(struct ip6_hdr) \ + + sizeof(struct nd_neighbor_advert) \ + + sizeof(struct nd_opt_hdr) + ETH_ALEN + +/* static vars */ +static int ndisc_fd = -1; + +/* + * Build an unsolicited Neighbour Advertisement. + * + * ifp + * Interface to send Neighbor Advertisement on + * + * ip + * IP address to send Neighbor Advertisement for + * + * buf + * Buffer to fill with IPv6 Neighbor Advertisement message. Includes + * Ethernet header. + * + * bufsiz + * Size of buf. + * + * Returns; + * -1 if bufsiz is too small + * 0 otherwise + */ +static int vrrp_ndisc_una_build(struct interface *ifp, struct ipaddr *ip, + uint8_t *buf, size_t bufsiz) +{ + if (bufsiz < VRRP_NDISC_SIZE) + return -1; + + memset(buf, 0x00, bufsiz); + + struct ether_header *eth = (struct ether_header *)buf; + struct ip6_hdr *ip6h = (struct ip6_hdr *)((char *)eth + ETHER_HDR_LEN); + struct nd_neighbor_advert *ndh = + (struct nd_neighbor_advert *)((char *)ip6h + + sizeof(struct ip6_hdr)); + struct icmp6_hdr *icmp6h = &ndh->nd_na_hdr; + struct nd_opt_hdr *nd_opt_h = + (struct nd_opt_hdr *)((char *)ndh + + sizeof(struct nd_neighbor_advert)); + char *nd_opt_lladdr = ((char *)nd_opt_h + sizeof(struct nd_opt_hdr)); + char *lladdr = (char *)ifp->hw_addr; + + /* + * An IPv6 packet with a multicast destination address DST, consisting + * of the sixteen octets DST[1] through DST[16], is transmitted to the + * Ethernet multicast address whose first two octets are the value 3333 + * hexadecimal and whose last four octets are the last four octets of + * DST. + * - RFC2464.7 + * + * In this case we are sending to the all nodes multicast address, so + * the last four octets are 0x00 0x00 0x00 0x01. + */ + memset(eth->ether_dhost, 0, ETH_ALEN); + eth->ether_dhost[0] = 0x33; + eth->ether_dhost[1] = 0x33; + eth->ether_dhost[5] = 1; + + /* Set source Ethernet address to interface link layer address */ + memcpy(eth->ether_shost, lladdr, ETH_ALEN); + eth->ether_type = htons(ETHERTYPE_IPV6); + + /* IPv6 Header */ + ip6h->ip6_vfc = 6 << 4; + ip6h->ip6_plen = htons(sizeof(struct nd_neighbor_advert) + + sizeof(struct nd_opt_hdr) + ETH_ALEN); + ip6h->ip6_nxt = IPPROTO_ICMPV6; + ip6h->ip6_hlim = VRRP_NDISC_HOPLIMIT; + memcpy(&ip6h->ip6_src, &ip->ipaddr_v6, sizeof(struct in6_addr)); + /* All nodes multicast address */ + ip6h->ip6_dst.s6_addr[0] = 0xFF; + ip6h->ip6_dst.s6_addr[1] = 0x02; + ip6h->ip6_dst.s6_addr[15] = 0x01; + + /* ICMPv6 Header */ + ndh->nd_na_type = ND_NEIGHBOR_ADVERT; + ndh->nd_na_flags_reserved |= ND_NA_FLAG_ROUTER; + ndh->nd_na_flags_reserved |= ND_NA_FLAG_OVERRIDE; + memcpy(&ndh->nd_na_target, &ip->ipaddr_v6, sizeof(struct in6_addr)); + + /* NDISC Option header */ + nd_opt_h->nd_opt_type = ND_OPT_TARGET_LINKADDR; + nd_opt_h->nd_opt_len = 1; + memcpy(nd_opt_lladdr, lladdr, ETH_ALEN); + + /* Compute checksum */ + uint32_t len = sizeof(struct nd_neighbor_advert) + + sizeof(struct nd_opt_hdr) + ETH_ALEN; + struct ipv6_ph ph = {}; + + ph.src = ip6h->ip6_src; + ph.dst = ip6h->ip6_dst; + ph.ulpl = htonl(len); + ph.next_hdr = IPPROTO_ICMPV6; + + /* Suppress static analysis warnings about accessing icmp6 oob */ + void *offset = icmp6h; + icmp6h->icmp6_cksum = in_cksum_with_ph6(&ph, offset, len); + + return 0; +} + +int vrrp_ndisc_una_send(struct vrrp_router *r, struct ipaddr *ip) +{ + assert(r->family == AF_INET6); + + int ret = 0; + struct interface *ifp = r->mvl_ifp; + uint8_t buf[VRRP_NDISC_SIZE]; + + ret = vrrp_ndisc_una_build(ifp, ip, buf, sizeof(buf)); + + if (ret == -1) + return ret; + + struct sockaddr_ll sll; + ssize_t len; + + /* Build the dst device */ + memset(&sll, 0, sizeof(sll)); + sll.sll_family = AF_PACKET; + memcpy(sll.sll_addr, ifp->hw_addr, ETH_ALEN); + sll.sll_halen = ETH_ALEN; + sll.sll_ifindex = (int)ifp->ifindex; + + char ipbuf[INET6_ADDRSTRLEN]; + + ipaddr2str(ip, ipbuf, sizeof(ipbuf)); + + DEBUGD(&vrrp_dbg_ndisc, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Sending unsolicited Neighbor Advertisement on %s for %s", + r->vr->vrid, family2str(r->family), ifp->name, ipbuf); + + if (DEBUG_MODE_CHECK(&vrrp_dbg_ndisc, DEBUG_MODE_ALL) + && DEBUG_MODE_CHECK(&vrrp_dbg_pkt, DEBUG_MODE_ALL)) + zlog_hexdump(buf, VRRP_NDISC_SIZE); + + len = sendto(ndisc_fd, buf, VRRP_NDISC_SIZE, 0, (struct sockaddr *)&sll, + sizeof(sll)); + + if (len < 0) { + zlog_err( + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Error sending unsolicited Neighbor Advertisement on %s for %s", + r->vr->vrid, family2str(r->family), ifp->name, ipbuf); + ret = -1; + } else { + ++r->stats.una_tx_cnt; + } + + return ret; +} + +int vrrp_ndisc_una_send_all(struct vrrp_router *r) +{ + assert(r->family == AF_INET6); + + struct listnode *ln; + struct ipaddr *ip; + + for (ALL_LIST_ELEMENTS_RO(r->addrs, ln, ip)) + vrrp_ndisc_una_send(r, ip); + + return 0; +} + +void vrrp_ndisc_init(void) +{ + frr_with_privs(&vrrp_privs) { + ndisc_fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_IPV6)); + } + + if (ndisc_fd > 0) { + DEBUGD(&vrrp_dbg_sock, + VRRP_LOGPFX "Initialized Neighbor Discovery socket"); + DEBUGD(&vrrp_dbg_ndisc, + VRRP_LOGPFX "Initialized Neighbor Discovery subsystem"); + } else { + zlog_err(VRRP_LOGPFX + "Error initializing Neighbor Discovery socket"); + } +} + +void vrrp_ndisc_fini(void) +{ + close(ndisc_fd); + ndisc_fd = -1; + + DEBUGD(&vrrp_dbg_ndisc, + VRRP_LOGPFX "Deinitialized Neighbor Discovery subsystem"); +} + +bool vrrp_ndisc_is_init(void) +{ + return ndisc_fd > 0; +} diff --git a/vrrpd/vrrp_ndisc.h b/vrrpd/vrrp_ndisc.h new file mode 100644 index 0000000000..efbef348d0 --- /dev/null +++ b/vrrpd/vrrp_ndisc.h @@ -0,0 +1,74 @@ +/* + * VRRP Neighbor Discovery. + * Copyright (C) 2019 Cumulus Networks, Inc. + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef __VRRP_NDISC_H__ +#define __VRRP_NDISC_H__ + +#include +#include +#include + +#include "vrrp.h" + +/* + * Initialize VRRP neighbor discovery. + */ +extern void vrrp_ndisc_init(void); + +/* + * Check whether VRRP Neighbor Discovery is initialized. + * + * Returns: + * True if initialized, false otherwise + */ +extern bool vrrp_ndisc_is_init(void); + +/* + * Finish VRRP Neighbor Discovery. + */ +extern void vrrp_ndisc_fini(void); + +/* + * Send VRRP Neighbor Advertisement. + * + * ifp + * Interface to transmit on + * + * ip + * IPv6 address to send Neighbor Advertisement for + * + * Returns: + * -1 on failure + * 0 otherwise + */ +extern int vrrp_ndisc_una_send(struct vrrp_router *r, struct ipaddr *ip); + +/* + * Send VRRP Neighbor Advertisements for all virtual IPs. + * + * r + * Virtual Router to send NA's for + * + * Returns: + * -1 on failure + * 0 otherwise + */ +extern int vrrp_ndisc_una_send_all(struct vrrp_router *r); + +#endif /* __VRRP_NDISC_H__ */ diff --git a/vrrpd/vrrp_northbound.c b/vrrpd/vrrp_northbound.c new file mode 100644 index 0000000000..f814963fe5 --- /dev/null +++ b/vrrpd/vrrp_northbound.c @@ -0,0 +1,803 @@ +/* + * VRRP northbound bindings. + * Copyright (C) 2019 Cumulus Networks, Inc. + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "if.h" +#include "log.h" +#include "prefix.h" +#include "table.h" +#include "command.h" +#include "northbound.h" +#include "libfrr.h" +#include "vrrp.h" +#include "vrrp_vty.h" + +/* + * XPath: /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group + */ +static int lib_interface_vrrp_vrrp_group_create(struct nb_cb_create_args *args) +{ + struct interface *ifp; + uint8_t vrid; + uint8_t version = 3; + struct vrrp_vrouter *vr; + + vrid = yang_dnode_get_uint8(args->dnode, "./virtual-router-id"); + version = yang_dnode_get_enum(args->dnode, "./version"); + + switch (args->event) { + case NB_EV_VALIDATE: + ifp = nb_running_get_entry(args->dnode, NULL, false); + if (ifp) { + vr = vrrp_lookup(ifp, vrid); + if (vr && vr->autoconf) { + snprintf( + args->errmsg, args->errmsg_len, + "Virtual Router with ID %d already exists on interface '%s'; created by VRRP autoconfiguration", + vrid, ifp->name); + return NB_ERR_VALIDATION; + } + } + return NB_OK; + case NB_EV_PREPARE: + case NB_EV_ABORT: + return NB_OK; + case NB_EV_APPLY: + break; + } + + ifp = nb_running_get_entry(args->dnode, NULL, true); + vr = vrrp_vrouter_create(ifp, vrid, version); + nb_running_set_entry(args->dnode, vr); + + return NB_OK; +} + +static int +lib_interface_vrrp_vrrp_group_destroy(struct nb_cb_destroy_args *args) +{ + struct vrrp_vrouter *vr; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + vr = nb_running_unset_entry(args->dnode); + vrrp_vrouter_destroy(vr); + + return NB_OK; +} + +static const void * +lib_interface_vrrp_vrrp_group_get_next(struct nb_cb_get_next_args *args) +{ + struct list *l = hash_to_list(vrrp_vrouters_hash); + struct listnode *ln; + const struct vrrp_vrouter *curr; + const struct interface *ifp = args->parent_list_entry; + + /* + * If list_entry is null, we return the first vrrp instance with a + * matching interface + */ + bool nextone = args->list_entry ? false : true; + + for (ALL_LIST_ELEMENTS_RO(l, ln, curr)) { + if (curr == args->list_entry) { + nextone = true; + continue; + } + + if (nextone && curr->ifp == ifp) + goto done; + } + + curr = NULL; + +done: + list_delete(&l); + return curr; +} + +static int +lib_interface_vrrp_vrrp_group_get_keys(struct nb_cb_get_keys_args *args) +{ + const struct vrrp_vrouter *vr = args->list_entry; + + args->keys->num = 1; + snprintf(args->keys->key[0], sizeof(args->keys->key[0]), "%u", + vr->vrid); + + return NB_OK; +} + +static const void * +lib_interface_vrrp_vrrp_group_lookup_entry(struct nb_cb_lookup_entry_args *args) +{ + uint32_t vrid = strtoul(args->keys->key[0], NULL, 10); + const struct interface *ifp = args->parent_list_entry; + + return vrrp_lookup(ifp, vrid); +} + +/* + * XPath: /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/version + */ +static int +lib_interface_vrrp_vrrp_group_version_modify(struct nb_cb_modify_args *args) +{ + if (args->event != NB_EV_APPLY) + return NB_OK; + + struct vrrp_vrouter *vr; + uint8_t version; + + vr = nb_running_get_entry(args->dnode, NULL, true); + vrrp_event(vr->v4, VRRP_EVENT_SHUTDOWN); + vrrp_event(vr->v6, VRRP_EVENT_SHUTDOWN); + version = yang_dnode_get_enum(args->dnode, NULL); + vr->version = version; + + vrrp_check_start(vr); + + return NB_OK; +} + +/* + * Helper function for address list OP_MODIFY callbacks. + */ +static void vrrp_yang_add_del_virtual_address(const struct lyd_node *dnode, + bool add) +{ + struct vrrp_vrouter *vr; + struct ipaddr ip; + + vr = nb_running_get_entry(dnode, NULL, true); + yang_dnode_get_ip(&ip, dnode, NULL); + if (add) + vrrp_add_ip(vr, &ip); + else + vrrp_del_ip(vr, &ip); + + vrrp_check_start(vr); +} + +/* + * XPath: + * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/virtual-address + */ +static int lib_interface_vrrp_vrrp_group_v4_virtual_address_create( + struct nb_cb_create_args *args) +{ + if (args->event != NB_EV_APPLY) + return NB_OK; + + vrrp_yang_add_del_virtual_address(args->dnode, true); + + return NB_OK; +} + +static int lib_interface_vrrp_vrrp_group_v4_virtual_address_destroy( + struct nb_cb_destroy_args *args) +{ + if (args->event != NB_EV_APPLY) + return NB_OK; + + vrrp_yang_add_del_virtual_address(args->dnode, false); + + return NB_OK; +} + +/* + * XPath: + * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/current-priority + */ +static struct yang_data * +lib_interface_vrrp_vrrp_group_v4_current_priority_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct vrrp_vrouter *vr = args->list_entry; + + return yang_data_new_uint8(args->xpath, vr->v4->priority); +} + +/* + * XPath: + * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/vrrp-interface + */ +static struct yang_data * +lib_interface_vrrp_vrrp_group_v4_vrrp_interface_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct vrrp_vrouter *vr = args->list_entry; + + struct yang_data *val = NULL; + + if (vr->v4->mvl_ifp) + val = yang_data_new_string(args->xpath, vr->v4->mvl_ifp->name); + + return val; +} + +/* + * XPath: + * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/source-address + */ +static struct yang_data * +lib_interface_vrrp_vrrp_group_v4_source_address_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct vrrp_vrouter *vr = args->list_entry; + struct yang_data *val = NULL; + struct ipaddr ip; + + memset(&ip, 0x00, sizeof(ip)); + + if (memcmp(&vr->v4->src.ipaddr_v4, &ip.ipaddr_v4, sizeof(ip.ipaddr_v4))) + val = yang_data_new_ip(args->xpath, &vr->v4->src); + + return val; +} + +/* + * XPath: /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/state + */ +static struct yang_data *lib_interface_vrrp_vrrp_group_v4_state_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct vrrp_vrouter *vr = args->list_entry; + + return yang_data_new_enum(args->xpath, vr->v4->fsm.state); +} + +/* + * XPath: + * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/master-advertisement-interval + */ +static struct yang_data * +lib_interface_vrrp_vrrp_group_v4_master_advertisement_interval_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct vrrp_vrouter *vr = args->list_entry; + + return yang_data_new_uint16(args->xpath, vr->v4->master_adver_interval); +} + +/* + * XPath: /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/skew-time + */ +static struct yang_data *lib_interface_vrrp_vrrp_group_v4_skew_time_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct vrrp_vrouter *vr = args->list_entry; + + return yang_data_new_uint16(args->xpath, vr->v4->skew_time); +} + +/* + * XPath: + * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/counter/state-transition + */ +static struct yang_data * +lib_interface_vrrp_vrrp_group_v4_counter_state_transition_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct vrrp_vrouter *vr = args->list_entry; + + return yang_data_new_uint32(args->xpath, vr->v4->stats.trans_cnt); +} + +/* + * XPath: + * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/counter/tx/advertisement + */ +static struct yang_data * +lib_interface_vrrp_vrrp_group_v4_counter_tx_advertisement_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct vrrp_vrouter *vr = args->list_entry; + + return yang_data_new_uint32(args->xpath, vr->v4->stats.adver_tx_cnt); +} + +/* + * XPath: + * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/counter/tx/gratuitous-arp + */ +static struct yang_data * +lib_interface_vrrp_vrrp_group_v4_counter_tx_gratuitous_arp_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct vrrp_vrouter *vr = args->list_entry; + + return yang_data_new_uint32(args->xpath, vr->v4->stats.garp_tx_cnt); +} + +/* + * XPath: + * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/counter/rx/advertisement + */ +static struct yang_data * +lib_interface_vrrp_vrrp_group_v4_counter_rx_advertisement_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct vrrp_vrouter *vr = args->list_entry; + + return yang_data_new_uint32(args->xpath, vr->v4->stats.adver_rx_cnt); +} + +/* + * XPath: + * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/virtual-address + */ +static int lib_interface_vrrp_vrrp_group_v6_virtual_address_create( + struct nb_cb_create_args *args) +{ + if (args->event != NB_EV_APPLY) + return NB_OK; + + vrrp_yang_add_del_virtual_address(args->dnode, true); + + return NB_OK; +} + +static int lib_interface_vrrp_vrrp_group_v6_virtual_address_destroy( + struct nb_cb_destroy_args *args) +{ + if (args->event != NB_EV_APPLY) + return NB_OK; + + vrrp_yang_add_del_virtual_address(args->dnode, false); + + return NB_OK; +} + +/* + * XPath: + * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/current-priority + */ +static struct yang_data * +lib_interface_vrrp_vrrp_group_v6_current_priority_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct vrrp_vrouter *vr = args->list_entry; + + return yang_data_new_uint8(args->xpath, vr->v6->priority); +} + +/* + * XPath: + * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/vrrp-interface + */ +static struct yang_data * +lib_interface_vrrp_vrrp_group_v6_vrrp_interface_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct vrrp_vrouter *vr = args->list_entry; + struct yang_data *val = NULL; + + if (vr->v6->mvl_ifp) + val = yang_data_new_string(args->xpath, vr->v6->mvl_ifp->name); + + return val; +} + +/* + * XPath: + * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/source-address + */ +static struct yang_data * +lib_interface_vrrp_vrrp_group_v6_source_address_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct vrrp_vrouter *vr = args->list_entry; + struct yang_data *val = NULL; + + if (ipaddr_isset(&vr->v6->src)) + val = yang_data_new_ip(args->xpath, &vr->v6->src); + + return val; +} + +/* + * XPath: /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/state + */ +static struct yang_data *lib_interface_vrrp_vrrp_group_v6_state_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct vrrp_vrouter *vr = args->list_entry; + + return yang_data_new_enum(args->xpath, vr->v6->fsm.state); +} + +/* + * XPath: + * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/master-advertisement-interval + */ +static struct yang_data * +lib_interface_vrrp_vrrp_group_v6_master_advertisement_interval_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct vrrp_vrouter *vr = args->list_entry; + + return yang_data_new_uint16(args->xpath, vr->v6->master_adver_interval); +} + +/* + * XPath: /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/skew-time + */ +static struct yang_data *lib_interface_vrrp_vrrp_group_v6_skew_time_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct vrrp_vrouter *vr = args->list_entry; + + return yang_data_new_uint16(args->xpath, vr->v6->skew_time); +} + +/* + * XPath: + * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/counter/state-transition + */ +static struct yang_data * +lib_interface_vrrp_vrrp_group_v6_counter_state_transition_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct vrrp_vrouter *vr = args->list_entry; + + return yang_data_new_uint32(args->xpath, vr->v6->stats.trans_cnt); +} + +/* + * XPath: + * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/counter/tx/advertisement + */ +static struct yang_data * +lib_interface_vrrp_vrrp_group_v6_counter_tx_advertisement_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct vrrp_vrouter *vr = args->list_entry; + + return yang_data_new_uint32(args->xpath, vr->v6->stats.adver_tx_cnt); +} + +/* + * XPath: + * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/counter/tx/neighbor-advertisement + */ +static struct yang_data * +lib_interface_vrrp_vrrp_group_v6_counter_tx_neighbor_advertisement_get_elem( + struct nb_cb_get_elem_args *args) +{ + /* TODO: implement me. */ + return NULL; +} + +/* + * XPath: + * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/counter/rx/advertisement + */ +static struct yang_data * +lib_interface_vrrp_vrrp_group_v6_counter_rx_advertisement_get_elem( + struct nb_cb_get_elem_args *args) +{ + /* TODO: implement me. */ + return NULL; +} + +/* + * XPath: /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/priority + */ +static int +lib_interface_vrrp_vrrp_group_priority_modify(struct nb_cb_modify_args *args) +{ + if (args->event != NB_EV_APPLY) + return NB_OK; + + struct vrrp_vrouter *vr; + uint8_t priority; + + vr = nb_running_get_entry(args->dnode, NULL, true); + priority = yang_dnode_get_uint8(args->dnode, NULL); + vrrp_set_priority(vr, priority); + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/preempt + */ +static int +lib_interface_vrrp_vrrp_group_preempt_modify(struct nb_cb_modify_args *args) +{ + if (args->event != NB_EV_APPLY) + return NB_OK; + + struct vrrp_vrouter *vr; + bool preempt; + + vr = nb_running_get_entry(args->dnode, NULL, true); + preempt = yang_dnode_get_bool(args->dnode, NULL); + vr->preempt_mode = preempt; + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/accept-mode + */ +static int +lib_interface_vrrp_vrrp_group_accept_mode_modify(struct nb_cb_modify_args *args) +{ + if (args->event != NB_EV_APPLY) + return NB_OK; + + struct vrrp_vrouter *vr; + bool accept; + + vr = nb_running_get_entry(args->dnode, NULL, true); + accept = yang_dnode_get_bool(args->dnode, NULL); + vr->accept_mode = accept; + + return NB_OK; +} + +/* + * XPath: + * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/advertisement-interval + */ +static int lib_interface_vrrp_vrrp_group_advertisement_interval_modify( + struct nb_cb_modify_args *args) +{ + if (args->event != NB_EV_APPLY) + return NB_OK; + + struct vrrp_vrouter *vr; + uint16_t advert_int; + + vr = nb_running_get_entry(args->dnode, NULL, true); + advert_int = yang_dnode_get_uint16(args->dnode, NULL); + vrrp_set_advertisement_interval(vr, advert_int); + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/shutdown + */ +static int +lib_interface_vrrp_vrrp_group_shutdown_modify(struct nb_cb_modify_args *args) +{ + if (args->event != NB_EV_APPLY) + return NB_OK; + + struct vrrp_vrouter *vr; + bool shutdown; + + vr = nb_running_get_entry(args->dnode, NULL, true); + shutdown = yang_dnode_get_bool(args->dnode, NULL); + + vr->shutdown = shutdown; + + if (shutdown) { + vrrp_event(vr->v4, VRRP_EVENT_SHUTDOWN); + vrrp_event(vr->v6, VRRP_EVENT_SHUTDOWN); + } else { + vrrp_check_start(vr); + } + + return NB_OK; +} + +/* clang-format off */ +const struct frr_yang_module_info frr_vrrpd_info = { + .name = "frr-vrrpd", + .nodes = { + { + .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group", + .cbs = { + .create = lib_interface_vrrp_vrrp_group_create, + .destroy = lib_interface_vrrp_vrrp_group_destroy, + .get_next = lib_interface_vrrp_vrrp_group_get_next, + .get_keys = lib_interface_vrrp_vrrp_group_get_keys, + .lookup_entry = lib_interface_vrrp_vrrp_group_lookup_entry, + .cli_show = cli_show_vrrp, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/version", + .cbs = { + .modify = lib_interface_vrrp_vrrp_group_version_modify, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/priority", + .cbs = { + .modify = lib_interface_vrrp_vrrp_group_priority_modify, + .cli_show = cli_show_priority, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/preempt", + .cbs = { + .modify = lib_interface_vrrp_vrrp_group_preempt_modify, + .cli_show = cli_show_preempt, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/accept-mode", + .cbs = { + .modify = lib_interface_vrrp_vrrp_group_accept_mode_modify, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/advertisement-interval", + .cbs = { + .modify = lib_interface_vrrp_vrrp_group_advertisement_interval_modify, + .cli_show = cli_show_advertisement_interval, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/shutdown", + .cbs = { + .modify = lib_interface_vrrp_vrrp_group_shutdown_modify, + .cli_show = cli_show_shutdown, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/virtual-address", + .cbs = { + .create = lib_interface_vrrp_vrrp_group_v4_virtual_address_create, + .destroy = lib_interface_vrrp_vrrp_group_v4_virtual_address_destroy, + .cli_show = cli_show_ip, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/current-priority", + .cbs = { + .get_elem = lib_interface_vrrp_vrrp_group_v4_current_priority_get_elem, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/vrrp-interface", + .cbs = { + .get_elem = lib_interface_vrrp_vrrp_group_v4_vrrp_interface_get_elem, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/source-address", + .cbs = { + .get_elem = lib_interface_vrrp_vrrp_group_v4_source_address_get_elem, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/state", + .cbs = { + .get_elem = lib_interface_vrrp_vrrp_group_v4_state_get_elem, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/master-advertisement-interval", + .cbs = { + .get_elem = lib_interface_vrrp_vrrp_group_v4_master_advertisement_interval_get_elem, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/skew-time", + .cbs = { + .get_elem = lib_interface_vrrp_vrrp_group_v4_skew_time_get_elem, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/counter/state-transition", + .cbs = { + .get_elem = lib_interface_vrrp_vrrp_group_v4_counter_state_transition_get_elem, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/counter/tx/advertisement", + .cbs = { + .get_elem = lib_interface_vrrp_vrrp_group_v4_counter_tx_advertisement_get_elem, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/counter/tx/gratuitous-arp", + .cbs = { + .get_elem = lib_interface_vrrp_vrrp_group_v4_counter_tx_gratuitous_arp_get_elem, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/counter/rx/advertisement", + .cbs = { + .get_elem = lib_interface_vrrp_vrrp_group_v4_counter_rx_advertisement_get_elem, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/virtual-address", + .cbs = { + .create = lib_interface_vrrp_vrrp_group_v6_virtual_address_create, + .destroy = lib_interface_vrrp_vrrp_group_v6_virtual_address_destroy, + .cli_show = cli_show_ipv6, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/current-priority", + .cbs = { + .get_elem = lib_interface_vrrp_vrrp_group_v6_current_priority_get_elem, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/vrrp-interface", + .cbs = { + .get_elem = lib_interface_vrrp_vrrp_group_v6_vrrp_interface_get_elem, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/source-address", + .cbs = { + .get_elem = lib_interface_vrrp_vrrp_group_v6_source_address_get_elem, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/state", + .cbs = { + .get_elem = lib_interface_vrrp_vrrp_group_v6_state_get_elem, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/master-advertisement-interval", + .cbs = { + .get_elem = lib_interface_vrrp_vrrp_group_v6_master_advertisement_interval_get_elem, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/skew-time", + .cbs = { + .get_elem = lib_interface_vrrp_vrrp_group_v6_skew_time_get_elem, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/counter/state-transition", + .cbs = { + .get_elem = lib_interface_vrrp_vrrp_group_v6_counter_state_transition_get_elem, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/counter/tx/advertisement", + .cbs = { + .get_elem = lib_interface_vrrp_vrrp_group_v6_counter_tx_advertisement_get_elem, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/counter/tx/neighbor-advertisement", + .cbs = { + .get_elem = lib_interface_vrrp_vrrp_group_v6_counter_tx_neighbor_advertisement_get_elem, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/counter/rx/advertisement", + .cbs = { + .get_elem = lib_interface_vrrp_vrrp_group_v6_counter_rx_advertisement_get_elem, + } + }, + { + .xpath = NULL, + }, + } +}; diff --git a/vrrpd/vrrp_packet.c b/vrrpd/vrrp_packet.c new file mode 100644 index 0000000000..3cb13bd71b --- /dev/null +++ b/vrrpd/vrrp_packet.c @@ -0,0 +1,326 @@ +/* + * VRRP packet crafting. + * Copyright (C) 2018-2019 Cumulus Networks, Inc. + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include +#include +#include +#include + +#include "lib/checksum.h" +#include "lib/ipaddr.h" +#include "lib/memory.h" + +#include "vrrp.h" +#include "vrrp_debug.h" +#include "vrrp_packet.h" + +DEFINE_MTYPE_STATIC(VRRPD, VRRP_PKT, "VRRP packet") + +/* clang-format off */ +static const char *const vrrp_packet_names[16] = { + [0] = "Unknown", + [VRRP_TYPE_ADVERTISEMENT] = "ADVERTISEMENT", + [2] = "Unknown", + [3] = "Unknown", + [4] = "Unknown", + [5] = "Unknown", + [6] = "Unknown", + [7] = "Unknown", + [8] = "Unknown", + [9] = "Unknown", + [10] = "Unknown", + [11] = "Unknown", + [12] = "Unknown", + [13] = "Unknown", + [14] = "Unknown", + [15] = "Unknown", +}; +/* clang-format on */ + +/* + * Compute the VRRP checksum. + * + * Checksum is not set in the packet, just computed. + * + * pkt + * VRRP packet, fully filled out except for checksum field. + * + * pktsize + * sizeof(*pkt) + * + * src + * IP address that pkt will be transmitted from. + * + * Returns: + * VRRP checksum in network byte order. + */ +static uint16_t vrrp_pkt_checksum(struct vrrp_pkt *pkt, size_t pktsize, + struct ipaddr *src) +{ + uint16_t chksum; + bool v6 = (src->ipa_type == IPADDR_V6); + + uint16_t chksum_pre = pkt->hdr.chksum; + + pkt->hdr.chksum = 0; + + if (v6) { + struct ipv6_ph ph = {}; + + ph.src = src->ipaddr_v6; + inet_pton(AF_INET6, VRRP_MCASTV6_GROUP_STR, &ph.dst); + ph.ulpl = htons(pktsize); + ph.next_hdr = IPPROTO_VRRP; + chksum = in_cksum_with_ph6(&ph, pkt, pktsize); + } else if (!v6 && ((pkt->hdr.vertype >> 4) == 3)) { + struct ipv4_ph ph = {}; + + ph.src = src->ipaddr_v4; + inet_pton(AF_INET, VRRP_MCASTV4_GROUP_STR, &ph.dst); + ph.proto = IPPROTO_VRRP; + ph.len = htons(pktsize); + chksum = in_cksum_with_ph4(&ph, pkt, pktsize); + } else if (!v6 && ((pkt->hdr.vertype >> 4) == 2)) { + chksum = in_cksum(pkt, pktsize); + } else { + assert(!"Invalid VRRP protocol version"); + } + + pkt->hdr.chksum = chksum_pre; + + return chksum; +} + +ssize_t vrrp_pkt_adver_build(struct vrrp_pkt **pkt, struct ipaddr *src, + uint8_t version, uint8_t vrid, uint8_t prio, + uint16_t max_adver_int, uint8_t numip, + struct ipaddr **ips) +{ + bool v6 = false; + size_t addrsz = 0; + + assert(version >= 2 && version <= 3); + + if (numip > 0) { + v6 = IS_IPADDR_V6(ips[0]); + addrsz = IPADDRSZ(ips[0]); + } + + assert(!(version == 2 && v6)); + + size_t pktsize = VRRP_PKT_SIZE(v6 ? AF_INET6 : AF_INET, version, numip); + + *pkt = XCALLOC(MTYPE_VRRP_PKT, pktsize); + + (*pkt)->hdr.vertype |= version << 4; + (*pkt)->hdr.vertype |= VRRP_TYPE_ADVERTISEMENT; + (*pkt)->hdr.vrid = vrid; + (*pkt)->hdr.priority = prio; + (*pkt)->hdr.naddr = numip; + if (version == 3) + (*pkt)->hdr.v3.adver_int = htons(max_adver_int); + else if (version == 2) { + (*pkt)->hdr.v2.auth_type = 0; + (*pkt)->hdr.v2.adver_int = MAX(max_adver_int / 100, 1); + } + + uint8_t *aptr = (void *)(*pkt)->addrs; + + for (int i = 0; i < numip; i++) { + memcpy(aptr, &ips[i]->ip.addr, addrsz); + aptr += addrsz; + } + + (*pkt)->hdr.chksum = vrrp_pkt_checksum(*pkt, pktsize, src); + + return pktsize; +} + +void vrrp_pkt_free(struct vrrp_pkt *pkt) +{ + XFREE(MTYPE_VRRP_PKT, pkt); +} + +size_t vrrp_pkt_adver_dump(char *buf, size_t buflen, struct vrrp_pkt *pkt) +{ + if (buflen < 1) + return 0; + + char tmpbuf[BUFSIZ]; + size_t rs = 0; + struct vrrp_hdr *hdr = &pkt->hdr; + + buf[0] = 0x00; + snprintf(tmpbuf, sizeof(tmpbuf), "version %u, ", (hdr->vertype >> 4)); + rs += strlcat(buf, tmpbuf, buflen); + snprintf(tmpbuf, sizeof(tmpbuf), "type %u (%s), ", + (hdr->vertype & 0x0F), + vrrp_packet_names[(hdr->vertype & 0x0F)]); + rs += strlcat(buf, tmpbuf, buflen); + snprintf(tmpbuf, sizeof(tmpbuf), "vrid %u, ", hdr->vrid); + rs += strlcat(buf, tmpbuf, buflen); + snprintf(tmpbuf, sizeof(tmpbuf), "priority %u, ", hdr->priority); + rs += strlcat(buf, tmpbuf, buflen); + snprintf(tmpbuf, sizeof(tmpbuf), "#%u addresses, ", hdr->naddr); + rs += strlcat(buf, tmpbuf, buflen); + snprintf(tmpbuf, sizeof(tmpbuf), "max adver int %u, ", + ntohs(hdr->v3.adver_int)); + rs += strlcat(buf, tmpbuf, buflen); + snprintf(tmpbuf, sizeof(tmpbuf), "checksum %x", ntohs(hdr->chksum)); + rs += strlcat(buf, tmpbuf, buflen); + + return rs; +} + +ssize_t vrrp_pkt_parse_datagram(int family, int version, struct msghdr *m, + size_t read, struct ipaddr *src, + struct vrrp_pkt **pkt, char *errmsg, + size_t errmsg_len) +{ + /* Source (MAC & IP), Dest (MAC & IP) TTL validation done by kernel */ + size_t addrsz = (family == AF_INET) ? sizeof(struct in_addr) + : sizeof(struct in6_addr); + + size_t pktsize; + uint8_t *buf = m->msg_iov->iov_base; + +#define VRRP_PKT_VCHECK(cond, _f, ...) \ + do { \ + if (!(cond)) { \ + if (errmsg) \ + snprintf(errmsg, errmsg_len, (_f), \ + ##__VA_ARGS__); \ + return -1; \ + } \ + } while (0) + + /* IPvX header check */ + + if (family == AF_INET) { + VRRP_PKT_VCHECK( + read >= sizeof(struct ip), + "Datagram not large enough to contain IP header"); + + struct ip *ip = (struct ip *)buf; + + /* IP total length check */ + VRRP_PKT_VCHECK( + ntohs(ip->ip_len) == read, + "IPv4 packet length field does not match # received bytes; %hu!= %zu", + ntohs(ip->ip_len), read); + + /* TTL check */ + VRRP_PKT_VCHECK(ip->ip_ttl == 255, + "IPv4 TTL is %hhu; should be 255", + ip->ip_ttl); + + *pkt = (struct vrrp_pkt *)(buf + (ip->ip_hl << 2)); + pktsize = read - (ip->ip_hl << 2); + + /* IP empty packet check */ + VRRP_PKT_VCHECK(pktsize > 0, "IPv4 packet has no payload"); + + /* Extract source address */ + struct sockaddr_in *sa = m->msg_name; + + src->ipa_type = IPADDR_V4; + src->ipaddr_v4 = sa->sin_addr; + } else if (family == AF_INET6) { + struct cmsghdr *c; + + for (c = CMSG_FIRSTHDR(m); c != NULL; CMSG_NXTHDR(m, c)) { + if (c->cmsg_level == IPPROTO_IPV6 + && c->cmsg_type == IPV6_HOPLIMIT) + break; + } + + VRRP_PKT_VCHECK(!!c, "IPv6 Hop Limit not received"); + + uint8_t *hoplimit = CMSG_DATA(c); + + VRRP_PKT_VCHECK(*hoplimit == 255, + "IPv6 Hop Limit is %hhu; should be 255", + *hoplimit); + + *pkt = (struct vrrp_pkt *)buf; + pktsize = read; + + /* Extract source address */ + struct sockaddr_in6 *sa = m->msg_name; + + src->ipa_type = IPADDR_V6; + memcpy(&src->ipaddr_v6, &sa->sin6_addr, + sizeof(struct in6_addr)); + } else { + assert(!"Unknown address family"); + } + + /* Size check */ + size_t minsize = (family == AF_INET) ? VRRP_MIN_PKT_SIZE_V4 + : VRRP_MIN_PKT_SIZE_V6; + size_t maxsize = (family == AF_INET) ? VRRP_MAX_PKT_SIZE_V4 + : VRRP_MAX_PKT_SIZE_V6; + VRRP_PKT_VCHECK(pktsize >= minsize, + "VRRP packet is undersized (%zu < %zu)", pktsize, + minsize); + VRRP_PKT_VCHECK(pktsize <= maxsize, + "VRRP packet is oversized (%zu > %zu)", pktsize, + maxsize); + + /* Version check */ + uint8_t pktver = (*pkt)->hdr.vertype >> 4; + + VRRP_PKT_VCHECK(pktver == version, "Bad version %u", pktver); + + /* Checksum check */ + uint16_t chksum = vrrp_pkt_checksum(*pkt, pktsize, src); + + VRRP_PKT_VCHECK((*pkt)->hdr.chksum == chksum, + "Bad VRRP checksum %hx; should be %hx", + (*pkt)->hdr.chksum, chksum); + + /* Type check */ + VRRP_PKT_VCHECK(((*pkt)->hdr.vertype & 0x0F) == 1, "Bad type %u", + (*pkt)->hdr.vertype & 0x0f); + + /* Exact size check */ + size_t ves = VRRP_PKT_SIZE(family, pktver, (*pkt)->hdr.naddr); + + VRRP_PKT_VCHECK(pktsize == ves, "Packet has incorrect # addresses%s", + pktver == 2 ? " or missing auth fields" : ""); + + /* auth type check */ + if (version == 2) + VRRP_PKT_VCHECK((*pkt)->hdr.v2.auth_type == 0, + "Bad authentication type %hhu", + (*pkt)->hdr.v2.auth_type); + + /* Addresses check */ + char vbuf[INET6_ADDRSTRLEN]; + uint8_t *p = (uint8_t *)(*pkt)->addrs; + + for (uint8_t i = 0; i < (*pkt)->hdr.naddr; i++) { + VRRP_PKT_VCHECK(inet_ntop(family, p, vbuf, sizeof(vbuf)), + "Bad IP address, #%hhu", i); + p += addrsz; + } + + /* Everything checks out */ + return pktsize; +} diff --git a/vrrpd/vrrp_packet.h b/vrrpd/vrrp_packet.h new file mode 100644 index 0000000000..082935f080 --- /dev/null +++ b/vrrpd/vrrp_packet.h @@ -0,0 +1,203 @@ +/* + * VRRP packet crafting. + * Copyright (C) 2018-2019 Cumulus Networks, Inc. + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef __VRRP_PACKET_H__ +#define __VRRP_PACKET_H__ + +#include + +#include "lib/ipaddr.h" +#include "lib/memory.h" +#include "lib/prefix.h" + +#define VRRP_TYPE_ADVERTISEMENT 1 + +/* + * Shared header for VRRPv2/v3 packets. + */ +struct vrrp_hdr { + /* + * H L H L + * 0000 0000 + * ver type + */ + uint8_t vertype; + uint8_t vrid; + uint8_t priority; + uint8_t naddr; + union { + struct { + uint8_t auth_type; + /* advertisement interval (in sec) */ + uint8_t adver_int; + } v2; + struct { + /* + * advertisement interval (in centiseconds) + * H L H L + * 0000 000000000000 + * rsvd adver_int + */ + uint16_t adver_int; + } v3; + }; + uint16_t chksum; +} __attribute__((packed)); + +#define VRRP_HDR_SIZE sizeof(struct vrrp_hdr) + +struct vrrp_pkt { + struct vrrp_hdr hdr; + /* + * When used, this is actually an array of one or the other, not an + * array of union. If N v4 addresses are stored then + * sizeof(addrs) == N * sizeof(struct in_addr). + * + * Under v2, the last 2 entries in this array are the authentication + * data fields. We don't support auth in v2 so these are always just 8 + * bytes of 0x00. + */ + union { + struct in_addr v4; + struct in6_addr v6; + } addrs[]; +} __attribute__((packed)); + +#define VRRP_PKT_SIZE(_f, _ver, _naddr) \ + ({ \ + size_t _asz = ((_f) == AF_INET) ? sizeof(struct in_addr) \ + : sizeof(struct in6_addr); \ + size_t _auth = 2 * sizeof(uint32_t) * (3 - (_ver)); \ + sizeof(struct vrrp_hdr) + (_asz * (_naddr)) + _auth; \ + }) + +#define VRRP_MIN_PKT_SIZE_V4 VRRP_PKT_SIZE(AF_INET, 3, 1) +#define VRRP_MAX_PKT_SIZE_V4 VRRP_PKT_SIZE(AF_INET, 2, 255) +#define VRRP_MIN_PKT_SIZE_V6 VRRP_PKT_SIZE(AF_INET6, 3, 1) +#define VRRP_MAX_PKT_SIZE_V6 VRRP_PKT_SIZE(AF_INET6, 3, 255) + +#define VRRP_MIN_PKT_SIZE VRRP_MIN_PKT_SIZE_V4 +#define VRRP_MAX_PKT_SIZE VRRP_MAX_PKT_SIZE_V6 + +/* + * Builds a VRRP ADVERTISEMENT packet. + * + * pkt + * Pointer to store pointer to result buffer in + * + * src + * Source address packet will be transmitted from. This is needed to compute + * the VRRP checksum. The returned packet must be sent in an IP datagram with + * the source address equal to this field, or the checksum will be invalid. + * + * version + * VRRP version; must be 2 or 3 + * + * vrid + * Virtual Router Identifier + * + * prio + * Virtual Router Priority + * + * max_adver_int + * time between ADVERTISEMENTs + * + * v6 + * whether 'ips' is an array of v4 or v6 addresses + * + * numip + * number of IPvX addresses in 'ips' + * + * ips + * array of pointer to either struct in_addr (v6 = false) or struct in6_addr + * (v6 = true) + */ +ssize_t vrrp_pkt_adver_build(struct vrrp_pkt **pkt, struct ipaddr *src, + uint8_t version, uint8_t vrid, uint8_t prio, + uint16_t max_adver_int, uint8_t numip, + struct ipaddr **ips); + +/* free memory allocated by vrrp_pkt_adver_build's pkt arg */ +void vrrp_pkt_free(struct vrrp_pkt *pkt); + +/* + * Dumps a VRRP ADVERTISEMENT packet to a string. + * + * Currently only dumps the header. + * + * buf + * Buffer to store string representation + * + * buflen + * Size of buf + * + * pkt + * Packet to dump to a string + * + * Returns: + * # bytes written to buf + */ +size_t vrrp_pkt_adver_dump(char *buf, size_t buflen, struct vrrp_pkt *pkt); + + +/* + * Parses a VRRP packet, checking for illegal or invalid data. + * + * This function parses both VRRPv2 and VRRPv3 packets. Which version is + * expected is determined by the version argument. For example, if version is 3 + * and the received packet has version field 2 it will fail to parse. + * + * Note that this function only checks whether the packet itself is a valid + * VRRP packet. It is up to the caller to validate whether the VRID is correct, + * priority and timer values are correct, etc. + * + * family + * Address family of received packet + * + * version + * VRRP version to use for validation + * + * m + * msghdr containing results of recvmsg() on VRRP router socket + * + * read + * Return value of recvmsg() on VRRP router socket; must be non-negative + * + * src + * Pointer to struct ipaddr to store address of datagram sender + * + * pkt + * Pointer to pointer to set to location of VRRP packet within buf + * + * errmsg + * Buffer to store human-readable error message in case of error; may be + * NULL, in which case no message will be stored + * + * errmsg_len + * Size of errmsg + * + * Returns: + * Size of VRRP packet, or -1 upon error + */ +ssize_t vrrp_pkt_parse_datagram(int family, int version, struct msghdr *m, + size_t read, struct ipaddr *src, + struct vrrp_pkt **pkt, char *errmsg, + size_t errmsg_len); + +#endif /* __VRRP_PACKET_H__ */ diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c new file mode 100644 index 0000000000..7d9cea3adc --- /dev/null +++ b/vrrpd/vrrp_vty.c @@ -0,0 +1,792 @@ +/* + * VRRP CLI commands. + * Copyright (C) 2018-2019 Cumulus Networks, Inc. + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include + +#include "lib/command.h" +#include "lib/if.h" +#include "lib/ipaddr.h" +#include "lib/json.h" +#include "lib/northbound_cli.h" +#include "lib/prefix.h" +#include "lib/termtable.h" +#include "lib/vty.h" +#include "lib/vrf.h" + +#include "vrrp.h" +#include "vrrp_debug.h" +#include "vrrp_vty.h" +#include "vrrp_zebra.h" +#ifndef VTYSH_EXTRACT_PL +#include "vrrpd/vrrp_vty_clippy.c" +#endif + + +#define VRRP_STR "Virtual Router Redundancy Protocol\n" +#define VRRP_VRID_STR "Virtual Router ID\n" +#define VRRP_PRIORITY_STR "Virtual Router Priority\n" +#define VRRP_ADVINT_STR "Virtual Router Advertisement Interval\n" +#define VRRP_IP_STR "Virtual Router IP address\n" +#define VRRP_VERSION_STR "VRRP protocol version\n" + +#define VRRP_XPATH_ENTRY VRRP_XPATH "[virtual-router-id='%ld']" + +/* clang-format off */ + +/* + * XPath: /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group + */ +DEFPY_YANG(vrrp_vrid, + vrrp_vrid_cmd, + "[no] vrrp (1-255)$vrid [version (2-3)]", + NO_STR + VRRP_STR + VRRP_VRID_STR + VRRP_VERSION_STR + VRRP_VERSION_STR) +{ + char valbuf[20]; + + snprintf(valbuf, sizeof(valbuf), "%ld", version ? version : vd.version); + + if (no) + nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, NULL); + else { + nb_cli_enqueue_change(vty, ".", NB_OP_CREATE, NULL); + nb_cli_enqueue_change(vty, "./version", NB_OP_MODIFY, valbuf); + } + + return nb_cli_apply_changes(vty, VRRP_XPATH_ENTRY, vrid); +} + +void cli_show_vrrp(struct vty *vty, struct lyd_node *dnode, bool show_defaults) +{ + const char *vrid = yang_dnode_get_string(dnode, "./virtual-router-id"); + const char *ver = yang_dnode_get_string(dnode, "./version"); + + vty_out(vty, " vrrp %s", vrid); + if (show_defaults || !yang_dnode_is_default(dnode, "./version")) + vty_out(vty, " version %s", ver); + vty_out(vty, "\n"); +} + +/* + * XPath: /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/shutdown + */ +DEFPY_YANG(vrrp_shutdown, + vrrp_shutdown_cmd, + "[no] vrrp (1-255)$vrid shutdown", + NO_STR + VRRP_STR + VRRP_VRID_STR + "Force VRRP router into administrative shutdown\n") +{ + nb_cli_enqueue_change(vty, "./shutdown", NB_OP_MODIFY, + no ? "false" : "true"); + + return nb_cli_apply_changes(vty, VRRP_XPATH_ENTRY, vrid); +} + +void cli_show_shutdown(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + const char *vrid = yang_dnode_get_string(dnode, "../virtual-router-id"); + const bool shut = yang_dnode_get_bool(dnode, NULL); + + vty_out(vty, " %svrrp %s shutdown\n", shut ? "" : "no ", vrid); +} + +/* + * XPath: /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/priority + */ +DEFPY_YANG(vrrp_priority, + vrrp_priority_cmd, + "vrrp (1-255)$vrid priority (1-254)", + VRRP_STR + VRRP_VRID_STR + VRRP_PRIORITY_STR + "Priority value") +{ + nb_cli_enqueue_change(vty, "./priority", NB_OP_MODIFY, priority_str); + + return nb_cli_apply_changes(vty, VRRP_XPATH_ENTRY, vrid); +} + +/* + * XPath: /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/priority + */ +DEFPY_YANG(no_vrrp_priority, + no_vrrp_priority_cmd, + "no vrrp (1-255)$vrid priority [(1-254)]", + NO_STR + VRRP_STR + VRRP_VRID_STR + VRRP_PRIORITY_STR + "Priority value") +{ + nb_cli_enqueue_change(vty, "./priority", NB_OP_MODIFY, NULL); + + return nb_cli_apply_changes(vty, VRRP_XPATH_ENTRY, vrid); +} + +void cli_show_priority(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + const char *vrid = yang_dnode_get_string(dnode, "../virtual-router-id"); + const char *prio = yang_dnode_get_string(dnode, NULL); + + vty_out(vty, " vrrp %s priority %s\n", vrid, prio); +} + +/* + * XPath: + * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/advertisement-interval + */ +DEFPY_YANG(vrrp_advertisement_interval, + vrrp_advertisement_interval_cmd, + "vrrp (1-255)$vrid advertisement-interval (10-40950)", + VRRP_STR VRRP_VRID_STR VRRP_ADVINT_STR + "Advertisement interval in milliseconds; must be multiple of 10") +{ + char val[20]; + + /* all internal computations are in centiseconds */ + advertisement_interval /= CS2MS; + snprintf(val, sizeof(val), "%ld", advertisement_interval); + nb_cli_enqueue_change(vty, "./advertisement-interval", NB_OP_MODIFY, + val); + + return nb_cli_apply_changes(vty, VRRP_XPATH_ENTRY, vrid); +} + +/* + * XPath: + * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/advertisement-interval + */ +DEFPY_YANG(no_vrrp_advertisement_interval, + no_vrrp_advertisement_interval_cmd, + "no vrrp (1-255)$vrid advertisement-interval [(10-40950)]", + NO_STR VRRP_STR VRRP_VRID_STR VRRP_ADVINT_STR + "Advertisement interval in milliseconds; must be multiple of 10") +{ + nb_cli_enqueue_change(vty, "./advertisement-interval", NB_OP_MODIFY, + NULL); + + return nb_cli_apply_changes(vty, VRRP_XPATH_ENTRY, vrid); +} + +void cli_show_advertisement_interval(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + const char *vrid = yang_dnode_get_string(dnode, "../virtual-router-id"); + uint16_t advint = yang_dnode_get_uint16(dnode, NULL); + + vty_out(vty, " vrrp %s advertisement-interval %u\n", vrid, + advint * CS2MS); +} + +/* + * XPath: + * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/virtual-address + */ +DEFPY_YANG(vrrp_ip, + vrrp_ip_cmd, + "[no] vrrp (1-255)$vrid ip A.B.C.D", + NO_STR + VRRP_STR + VRRP_VRID_STR + "Add IPv4 address\n" + VRRP_IP_STR) +{ + int op = no ? NB_OP_DESTROY : NB_OP_CREATE; + nb_cli_enqueue_change(vty, "./v4/virtual-address", op, ip_str); + + return nb_cli_apply_changes(vty, VRRP_XPATH_ENTRY, vrid); +} + +void cli_show_ip(struct vty *vty, struct lyd_node *dnode, bool show_defaults) +{ + const char *vrid = + yang_dnode_get_string(dnode, "../../virtual-router-id"); + const char *ipv4 = yang_dnode_get_string(dnode, NULL); + + vty_out(vty, " vrrp %s ip %s\n", vrid, ipv4); +} + +/* + * XPath: + * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/virtual-address + */ +DEFPY_YANG(vrrp_ip6, + vrrp_ip6_cmd, + "[no] vrrp (1-255)$vrid ipv6 X:X::X:X", + NO_STR + VRRP_STR + VRRP_VRID_STR + "Add IPv6 address\n" + VRRP_IP_STR) +{ + int op = no ? NB_OP_DESTROY : NB_OP_CREATE; + nb_cli_enqueue_change(vty, "./v6/virtual-address", op, ipv6_str); + + return nb_cli_apply_changes(vty, VRRP_XPATH_ENTRY, vrid); +} + +void cli_show_ipv6(struct vty *vty, struct lyd_node *dnode, bool show_defaults) +{ + const char *vrid = + yang_dnode_get_string(dnode, "../../virtual-router-id"); + const char *ipv6 = yang_dnode_get_string(dnode, NULL); + + vty_out(vty, " vrrp %s ipv6 %s\n", vrid, ipv6); +} + +/* + * XPath: /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/preempt + */ +DEFPY_YANG(vrrp_preempt, + vrrp_preempt_cmd, + "[no] vrrp (1-255)$vrid preempt", + NO_STR + VRRP_STR + VRRP_VRID_STR + "Preempt mode\n") +{ + nb_cli_enqueue_change(vty, "./preempt", NB_OP_MODIFY, + no ? "false" : "true"); + + return nb_cli_apply_changes(vty, VRRP_XPATH_ENTRY, vrid); +} + +void cli_show_preempt(struct vty *vty, struct lyd_node *dnode, + bool show_defaults) +{ + const char *vrid = yang_dnode_get_string(dnode, "../virtual-router-id"); + const bool pre = yang_dnode_get_bool(dnode, NULL); + + vty_out(vty, " %svrrp %s preempt\n", pre ? "" : "no ", vrid); +} + +/* XXX: yang conversion */ +DEFPY_YANG(vrrp_autoconfigure, + vrrp_autoconfigure_cmd, + "[no] vrrp autoconfigure [version (2-3)]", + NO_STR + VRRP_STR + "Automatically set up VRRP instances on VRRP-compatible interfaces\n" + "Version for automatically configured instances\n" + VRRP_VERSION_STR) +{ + version = version ? version : 3; + + if (!no) + vrrp_autoconfig_on(version); + else + vrrp_autoconfig_off(); + + return CMD_SUCCESS; +} + +/* XXX: yang conversion */ +DEFPY_YANG(vrrp_default, + vrrp_default_cmd, + "[no] vrrp default ", + NO_STR + VRRP_STR + "Configure defaults for new VRRP instances\n" + VRRP_ADVINT_STR + "Advertisement interval in milliseconds\n" + "Preempt mode\n" + VRRP_PRIORITY_STR + "Priority value\n" + "Force VRRP router into administrative shutdown\n") +{ + if (adv) { + if (advint % CS2MS != 0) { + vty_out(vty, "%% Value must be a multiple of %u\n", + (unsigned int)CS2MS); + return CMD_WARNING_CONFIG_FAILED; + } + /* all internal computations are in centiseconds */ + advint /= CS2MS; + vd.advertisement_interval = no ? VRRP_DEFAULT_ADVINT : advint; + } + if (p) + vd.preempt_mode = !no; + if (prio) + vd.priority = no ? VRRP_DEFAULT_PRIORITY : prioval; + if (s) + vd.shutdown = !no; + + return CMD_SUCCESS; +} + +/* clang-format on */ + +/* + * Build JSON representation of VRRP instance. + * + * vr + * VRRP router to build json object from + * + * Returns: + * JSON representation of VRRP instance. Must be freed by caller. + */ +static struct json_object *vrrp_build_json(struct vrrp_vrouter *vr) +{ + char ethstr4[ETHER_ADDR_STRLEN]; + char ethstr6[ETHER_ADDR_STRLEN]; + char ipstr[INET6_ADDRSTRLEN]; + const char *stastr4 = vrrp_state_names[vr->v4->fsm.state]; + const char *stastr6 = vrrp_state_names[vr->v6->fsm.state]; + char sipstr4[INET6_ADDRSTRLEN] = {}; + char sipstr6[INET6_ADDRSTRLEN] = {}; + struct listnode *ln; + struct ipaddr *ip; + struct json_object *j = json_object_new_object(); + struct json_object *v4 = json_object_new_object(); + struct json_object *v4_stats = json_object_new_object(); + struct json_object *v4_addrs = json_object_new_array(); + struct json_object *v6 = json_object_new_object(); + struct json_object *v6_stats = json_object_new_object(); + struct json_object *v6_addrs = json_object_new_array(); + + prefix_mac2str(&vr->v4->vmac, ethstr4, sizeof(ethstr4)); + prefix_mac2str(&vr->v6->vmac, ethstr6, sizeof(ethstr6)); + + json_object_int_add(j, "vrid", vr->vrid); + json_object_int_add(j, "version", vr->version); + json_object_boolean_add(j, "autoconfigured", vr->autoconf); + json_object_boolean_add(j, "shutdown", vr->shutdown); + json_object_boolean_add(j, "preemptMode", vr->preempt_mode); + json_object_boolean_add(j, "acceptMode", vr->accept_mode); + json_object_string_add(j, "interface", vr->ifp->name); + json_object_int_add(j, "advertisementInterval", + vr->advertisement_interval * CS2MS); + /* v4 */ + json_object_string_add(v4, "interface", + vr->v4->mvl_ifp ? vr->v4->mvl_ifp->name : ""); + json_object_string_add(v4, "vmac", ethstr4); + ipaddr2str(&vr->v4->src, sipstr4, sizeof(sipstr4)); + json_object_string_add(v4, "primaryAddress", sipstr4); + json_object_string_add(v4, "status", stastr4); + json_object_int_add(v4, "effectivePriority", vr->v4->priority); + json_object_int_add(v4, "masterAdverInterval", + vr->v4->master_adver_interval * CS2MS); + json_object_int_add(v4, "skewTime", vr->v4->skew_time * CS2MS); + json_object_int_add(v4, "masterDownInterval", + vr->v4->master_down_interval * CS2MS); + /* v4 stats */ + json_object_int_add(v4_stats, "adverTx", vr->v4->stats.adver_tx_cnt); + json_object_int_add(v4_stats, "adverRx", vr->v4->stats.adver_rx_cnt); + json_object_int_add(v4_stats, "garpTx", vr->v4->stats.garp_tx_cnt); + json_object_int_add(v4_stats, "transitions", vr->v4->stats.trans_cnt); + json_object_object_add(v4, "stats", v4_stats); + /* v4 addrs */ + if (vr->v4->addrs->count) { + for (ALL_LIST_ELEMENTS_RO(vr->v4->addrs, ln, ip)) { + inet_ntop(vr->v4->family, &ip->ipaddr_v4, ipstr, + sizeof(ipstr)); + json_object_array_add(v4_addrs, + json_object_new_string(ipstr)); + } + } + json_object_object_add(v4, "addresses", v4_addrs); + json_object_object_add(j, "v4", v4); + + /* v6 */ + json_object_string_add(v6, "interface", + vr->v6->mvl_ifp ? vr->v6->mvl_ifp->name : ""); + json_object_string_add(v6, "vmac", ethstr6); + ipaddr2str(&vr->v6->src, sipstr6, sizeof(sipstr6)); + if (strlen(sipstr6) == 0 && vr->v6->src.ip.addr == 0x00) + strlcat(sipstr6, "::", sizeof(sipstr6)); + json_object_string_add(v6, "primaryAddress", sipstr6); + json_object_string_add(v6, "status", stastr6); + json_object_int_add(v6, "effectivePriority", vr->v6->priority); + json_object_int_add(v6, "masterAdverInterval", + vr->v6->master_adver_interval * CS2MS); + json_object_int_add(v6, "skewTime", vr->v6->skew_time * CS2MS); + json_object_int_add(v6, "masterDownInterval", + vr->v6->master_down_interval * CS2MS); + /* v6 stats */ + json_object_int_add(v6_stats, "adverTx", vr->v6->stats.adver_tx_cnt); + json_object_int_add(v6_stats, "adverRx", vr->v6->stats.adver_rx_cnt); + json_object_int_add(v6_stats, "neighborAdverTx", + vr->v6->stats.una_tx_cnt); + json_object_int_add(v6_stats, "transitions", vr->v6->stats.trans_cnt); + json_object_object_add(v6, "stats", v6_stats); + /* v6 addrs */ + if (vr->v6->addrs->count) { + for (ALL_LIST_ELEMENTS_RO(vr->v6->addrs, ln, ip)) { + inet_ntop(vr->v6->family, &ip->ipaddr_v6, ipstr, + sizeof(ipstr)); + json_object_array_add(v6_addrs, + json_object_new_string(ipstr)); + } + } + json_object_object_add(v6, "addresses", v6_addrs); + json_object_object_add(j, "v6", v6); + + return j; +} + +/* + * Dump VRRP instance status to VTY. + * + * vty + * vty to dump to + * + * vr + * VRRP router to dump + */ +static void vrrp_show(struct vty *vty, struct vrrp_vrouter *vr) +{ + char ethstr4[ETHER_ADDR_STRLEN]; + char ethstr6[ETHER_ADDR_STRLEN]; + char ipstr[INET6_ADDRSTRLEN]; + const char *stastr4 = vrrp_state_names[vr->v4->fsm.state]; + const char *stastr6 = vrrp_state_names[vr->v6->fsm.state]; + char sipstr4[INET6_ADDRSTRLEN] = {}; + char sipstr6[INET6_ADDRSTRLEN] = {}; + struct listnode *ln; + struct ipaddr *ip; + + struct ttable *tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]); + + ttable_add_row(tt, "%s|%u", "Virtual Router ID", vr->vrid); + ttable_add_row(tt, "%s|%hhu", "Protocol Version", vr->version); + ttable_add_row(tt, "%s|%s", "Autoconfigured", + vr->autoconf ? "Yes" : "No"); + ttable_add_row(tt, "%s|%s", "Shutdown", vr->shutdown ? "Yes" : "No"); + ttable_add_row(tt, "%s|%s", "Interface", vr->ifp->name); + prefix_mac2str(&vr->v4->vmac, ethstr4, sizeof(ethstr4)); + prefix_mac2str(&vr->v6->vmac, ethstr6, sizeof(ethstr6)); + ttable_add_row(tt, "%s|%s", "VRRP interface (v4)", + vr->v4->mvl_ifp ? vr->v4->mvl_ifp->name : "None"); + ttable_add_row(tt, "%s|%s", "VRRP interface (v6)", + vr->v6->mvl_ifp ? vr->v6->mvl_ifp->name : "None"); + ipaddr2str(&vr->v4->src, sipstr4, sizeof(sipstr4)); + ipaddr2str(&vr->v6->src, sipstr6, sizeof(sipstr6)); + if (strlen(sipstr6) == 0 && vr->v6->src.ip.addr == 0x00) + strlcat(sipstr6, "::", sizeof(sipstr6)); + ttable_add_row(tt, "%s|%s", "Primary IP (v4)", sipstr4); + ttable_add_row(tt, "%s|%s", "Primary IP (v6)", sipstr6); + ttable_add_row(tt, "%s|%s", "Virtual MAC (v4)", ethstr4); + ttable_add_row(tt, "%s|%s", "Virtual MAC (v6)", ethstr6); + ttable_add_row(tt, "%s|%s", "Status (v4)", stastr4); + ttable_add_row(tt, "%s|%s", "Status (v6)", stastr6); + ttable_add_row(tt, "%s|%hhu", "Priority", vr->priority); + ttable_add_row(tt, "%s|%hhu", "Effective Priority (v4)", + vr->v4->priority); + ttable_add_row(tt, "%s|%hhu", "Effective Priority (v6)", + vr->v6->priority); + ttable_add_row(tt, "%s|%s", "Preempt Mode", + vr->preempt_mode ? "Yes" : "No"); + ttable_add_row(tt, "%s|%s", "Accept Mode", + vr->accept_mode ? "Yes" : "No"); + ttable_add_row(tt, "%s|%d ms", "Advertisement Interval", + vr->advertisement_interval * CS2MS); + ttable_add_row(tt, "%s|%d ms", + "Master Advertisement Interval (v4)", + vr->v4->master_adver_interval * CS2MS); + ttable_add_row(tt, "%s|%d ms", + "Master Advertisement Interval (v6)", + vr->v6->master_adver_interval * CS2MS); + ttable_add_row(tt, "%s|%u", "Advertisements Tx (v4)", + vr->v4->stats.adver_tx_cnt); + ttable_add_row(tt, "%s|%u", "Advertisements Tx (v6)", + vr->v6->stats.adver_tx_cnt); + ttable_add_row(tt, "%s|%u", "Advertisements Rx (v4)", + vr->v4->stats.adver_rx_cnt); + ttable_add_row(tt, "%s|%u", "Advertisements Rx (v6)", + vr->v6->stats.adver_rx_cnt); + ttable_add_row(tt, "%s|%u", "Gratuitous ARP Tx (v4)", + vr->v4->stats.garp_tx_cnt); + ttable_add_row(tt, "%s|%u", "Neigh. Adverts Tx (v6)", + vr->v6->stats.una_tx_cnt); + ttable_add_row(tt, "%s|%u", "State transitions (v4)", + vr->v4->stats.trans_cnt); + ttable_add_row(tt, "%s|%u", "State transitions (v6)", + vr->v6->stats.trans_cnt); + ttable_add_row(tt, "%s|%d ms", "Skew Time (v4)", + vr->v4->skew_time * CS2MS); + ttable_add_row(tt, "%s|%d ms", "Skew Time (v6)", + vr->v6->skew_time * CS2MS); + ttable_add_row(tt, "%s|%d ms", "Master Down Interval (v4)", + vr->v4->master_down_interval * CS2MS); + ttable_add_row(tt, "%s|%d ms", "Master Down Interval (v6)", + vr->v6->master_down_interval * CS2MS); + ttable_add_row(tt, "%s|%u", "IPv4 Addresses", vr->v4->addrs->count); + + char fill[35]; + + memset(fill, '.', sizeof(fill)); + fill[sizeof(fill) - 1] = 0x00; + if (vr->v4->addrs->count) { + for (ALL_LIST_ELEMENTS_RO(vr->v4->addrs, ln, ip)) { + inet_ntop(vr->v4->family, &ip->ipaddr_v4, ipstr, + sizeof(ipstr)); + ttable_add_row(tt, "%s|%s", fill, ipstr); + } + } + + ttable_add_row(tt, "%s|%u", "IPv6 Addresses", vr->v6->addrs->count); + + if (vr->v6->addrs->count) { + for (ALL_LIST_ELEMENTS_RO(vr->v6->addrs, ln, ip)) { + inet_ntop(vr->v6->family, &ip->ipaddr_v6, ipstr, + sizeof(ipstr)); + ttable_add_row(tt, "%s|%s", fill, ipstr); + } + } + + char *table = ttable_dump(tt, "\n"); + + vty_out(vty, "\n%s\n", table); + XFREE(MTYPE_TMP, table); + ttable_del(tt); +} + +/* + * Sort comparator, used when sorting VRRP instances for display purposes. + * + * Sorts by interface name first, then by VRID ascending. + */ +static int vrrp_instance_display_sort_cmp(const void **d1, const void **d2) +{ + const struct vrrp_vrouter *vr1 = *d1; + const struct vrrp_vrouter *vr2 = *d2; + int result; + + result = strcmp(vr1->ifp->name, vr2->ifp->name); + result += !result * (vr1->vrid - vr2->vrid); + + return result; +} + +/* clang-format off */ + +DEFPY_YANG(vrrp_vrid_show, + vrrp_vrid_show_cmd, + "show vrrp [interface INTERFACE$ifn] [(1-255)$vrid] [json$json]", + SHOW_STR + VRRP_STR + INTERFACE_STR + "Only show VRRP instances on this interface\n" + VRRP_VRID_STR + JSON_STR) +{ + struct vrrp_vrouter *vr; + struct listnode *ln; + struct list *ll = hash_to_list(vrrp_vrouters_hash); + struct json_object *j = json_object_new_array(); + + list_sort(ll, vrrp_instance_display_sort_cmp); + + for (ALL_LIST_ELEMENTS_RO(ll, ln, vr)) { + if (ifn && !strmatch(ifn, vr->ifp->name)) + continue; + if (vrid && ((uint8_t) vrid) != vr->vrid) + continue; + + if (!json) + vrrp_show(vty, vr); + else + json_object_array_add(j, vrrp_build_json(vr)); + } + + if (json) + vty_out(vty, "%s\n", + json_object_to_json_string_ext( + j, JSON_C_TO_STRING_PRETTY)); + + json_object_free(j); + + list_delete(&ll); + + return CMD_SUCCESS; +} + +DEFPY_YANG(vrrp_vrid_show_summary, + vrrp_vrid_show_summary_cmd, + "show vrrp [interface INTERFACE$ifn] [(1-255)$vrid] summary", + SHOW_STR + VRRP_STR + INTERFACE_STR + "Only show VRRP instances on this interface\n" + VRRP_VRID_STR + "Summarize all VRRP instances\n") +{ + struct vrrp_vrouter *vr; + struct listnode *ln; + struct list *ll = hash_to_list(vrrp_vrouters_hash); + + list_sort(ll, vrrp_instance_display_sort_cmp); + + struct ttable *tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]); + + ttable_add_row( + tt, "Interface|VRID|Priority|IPv4|IPv6|State (v4)|State (v6)"); + ttable_rowseps(tt, 0, BOTTOM, true, '-'); + + for (ALL_LIST_ELEMENTS_RO(ll, ln, vr)) { + if (ifn && !strmatch(ifn, vr->ifp->name)) + continue; + if (vrid && ((uint8_t)vrid) != vr->vrid) + continue; + + ttable_add_row( + tt, "%s|%u|%hhu|%d|%d|%s|%s", + vr->ifp->name, vr->vrid, vr->priority, + vr->v4->addrs->count, vr->v6->addrs->count, + vr->v4->fsm.state == VRRP_STATE_MASTER ? "Master" + : "Backup", + vr->v6->fsm.state == VRRP_STATE_MASTER ? "Master" + : "Backup"); + } + + char *table = ttable_dump(tt, "\n"); + + vty_out(vty, "\n%s\n", table); + XFREE(MTYPE_TMP, table); + ttable_del(tt); + + list_delete(&ll); + + return CMD_SUCCESS; +} + + +DEFPY_YANG(debug_vrrp, + debug_vrrp_cmd, + "[no] debug vrrp [{protocol$proto|autoconfigure$ac|packets$pkt|sockets$sock|ndisc$ndisc|arp$arp|zebra$zebra}]", + NO_STR + DEBUG_STR + VRRP_STR + "Debug protocol state\n" + "Debug autoconfiguration\n" + "Debug sent and received packets\n" + "Debug socket creation and configuration\n" + "Debug Neighbor Discovery\n" + "Debug ARP\n" + "Debug Zebra events\n") +{ + /* If no specific are given on/off them all */ + if (strmatch(argv[argc - 1]->text, "vrrp")) + vrrp_debug_set(NULL, 0, vty->node, !no, true, true, true, true, + true, true, true); + else + vrrp_debug_set(NULL, 0, vty->node, !no, !!proto, !!ac, !!pkt, + !!sock, !!ndisc, !!arp, !!zebra); + + return CMD_SUCCESS; +} + +DEFUN_NOSH (show_debugging_vrrp, + show_debugging_vrrp_cmd, + "show debugging [vrrp]", + SHOW_STR + DEBUG_STR + "VRRP information\n") +{ + vty_out(vty, "VRRP debugging status:\n"); + + vrrp_debug_status_write(vty); + + return CMD_SUCCESS; +} + +/* clang-format on */ + +/* + * Write per interface VRRP config. + */ +static int vrrp_config_write_interface(struct vty *vty) +{ + struct vrf *vrf; + int write = 0; + + RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { + struct interface *ifp; + + FOR_ALL_INTERFACES (vrf, ifp) { + struct lyd_node *dnode; + + dnode = yang_dnode_get( + running_config->dnode, + "/frr-interface:lib/interface[name='%s'][vrf='%s']", + ifp->name, vrf->name); + if (dnode == NULL) + continue; + + write = 1; + nb_cli_show_dnode_cmds(vty, dnode, false); + } + } + + return write; +} + +static struct cmd_node interface_node = { + .name = "interface", + .node = INTERFACE_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-if)# ", + .config_write = vrrp_config_write_interface, +}; + +static struct cmd_node debug_node = { + .name = "debug", + .node = DEBUG_NODE, + .prompt = "", + .config_write = vrrp_config_write_debug, +}; + +static struct cmd_node vrrp_node = { + .name = "vrrp", + .node = VRRP_NODE, + .prompt = "", + .config_write = vrrp_config_write_global, +}; + +void vrrp_vty_init(void) +{ + install_node(&debug_node); + install_node(&interface_node); + install_node(&vrrp_node); + if_cmd_init(); + + install_element(VIEW_NODE, &vrrp_vrid_show_cmd); + install_element(VIEW_NODE, &vrrp_vrid_show_summary_cmd); + install_element(ENABLE_NODE, &show_debugging_vrrp_cmd); + install_element(ENABLE_NODE, &debug_vrrp_cmd); + install_element(CONFIG_NODE, &debug_vrrp_cmd); + install_element(CONFIG_NODE, &vrrp_autoconfigure_cmd); + install_element(CONFIG_NODE, &vrrp_default_cmd); + install_element(INTERFACE_NODE, &vrrp_vrid_cmd); + install_element(INTERFACE_NODE, &vrrp_shutdown_cmd); + install_element(INTERFACE_NODE, &vrrp_priority_cmd); + install_element(INTERFACE_NODE, &no_vrrp_priority_cmd); + install_element(INTERFACE_NODE, &vrrp_advertisement_interval_cmd); + install_element(INTERFACE_NODE, &no_vrrp_advertisement_interval_cmd); + install_element(INTERFACE_NODE, &vrrp_ip_cmd); + install_element(INTERFACE_NODE, &vrrp_ip6_cmd); + install_element(INTERFACE_NODE, &vrrp_preempt_cmd); +} diff --git a/vrrpd/vrrp_vty.h b/vrrpd/vrrp_vty.h new file mode 100644 index 0000000000..6c6eef0327 --- /dev/null +++ b/vrrpd/vrrp_vty.h @@ -0,0 +1,40 @@ +/* + * VRRP CLI commands. + * Copyright (C) 2018-2019 Cumulus Networks, Inc. + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef __VRRP_VTY_H__ +#define __VRRP_VTY_H__ + +#include "lib/northbound.h" + +void vrrp_vty_init(void); + +/* Northbound callbacks */ +void cli_show_vrrp(struct vty *vty, struct lyd_node *dnode, bool show_defaults); +void cli_show_shutdown(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_priority(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_advertisement_interval(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void cli_show_ip(struct vty *vty, struct lyd_node *dnode, bool show_defaults); +void cli_show_ipv6(struct vty *vty, struct lyd_node *dnode, bool show_defaults); +void cli_show_preempt(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); + +#endif /* __VRRP_VTY_H__ */ diff --git a/vrrpd/vrrp_zebra.c b/vrrpd/vrrp_zebra.c new file mode 100644 index 0000000000..a578921df6 --- /dev/null +++ b/vrrpd/vrrp_zebra.c @@ -0,0 +1,208 @@ +/* + * VRRP Zebra interfacing. + * Copyright (C) 2018-2019 Cumulus Networks, Inc. + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include + +#include "lib/if.h" +#include "lib/linklist.h" +#include "lib/log.h" +#include "lib/prefix.h" +#include "lib/vty.h" +#include "lib/zclient.h" + +#include "vrrp.h" +#include "vrrp_debug.h" +#include "vrrp_zebra.h" + +#define VRRP_LOGPFX "[ZEBRA] " + +static struct zclient *zclient; + +static void vrrp_zebra_debug_if_state(struct interface *ifp, vrf_id_t vrf_id, + const char *func) +{ + DEBUGD(&vrrp_dbg_zebra, + "%s: %s index %d(%u) parent %d mac %02x:%02x:%02x:%02x:%02x:%02x flags %ld metric %d mtu %d operative %d", + func, ifp->name, vrf_id, ifp->link_ifindex, ifp->ifindex, + ifp->hw_addr[0], ifp->hw_addr[1], ifp->hw_addr[2], + ifp->hw_addr[3], ifp->hw_addr[4], ifp->hw_addr[5], + (long)ifp->flags, ifp->metric, ifp->mtu, if_is_operative(ifp)); +} + +static void vrrp_zebra_debug_if_dump_address(struct interface *ifp, + const char *func) +{ + struct connected *ifc; + struct listnode *node; + + DEBUGD(&vrrp_dbg_zebra, "%s: interface %s addresses:", func, ifp->name); + + for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) { + struct prefix *p = ifc->address; + + DEBUGD(&vrrp_dbg_zebra, "%s: interface %s address %s %s", func, + ifp->name, inet_ntoa(p->u.prefix4), + CHECK_FLAG(ifc->flags, ZEBRA_IFA_SECONDARY) ? "secondary" + : "primary"); + } +} + + +static void vrrp_zebra_connected(struct zclient *zclient) +{ + zclient_send_reg_requests(zclient, VRF_DEFAULT); +} + +/* Router-id update message from zebra. */ +static int vrrp_router_id_update_zebra(int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + struct prefix router_id; + + zebra_router_id_update_read(zclient->ibuf, &router_id); + + return 0; +} + +int vrrp_ifp_create(struct interface *ifp) +{ + vrrp_zebra_debug_if_state(ifp, ifp->vrf_id, __func__); + + vrrp_if_add(ifp); + + return 0; +} + +int vrrp_ifp_destroy(struct interface *ifp) +{ + vrrp_zebra_debug_if_state(ifp, ifp->vrf_id, __func__); + + vrrp_if_del(ifp); + + return 0; +} + +int vrrp_ifp_up(struct interface *ifp) +{ + vrrp_zebra_debug_if_state(ifp, ifp->vrf_id, __func__); + + vrrp_if_up(ifp); + + return 0; +} + +int vrrp_ifp_down(struct interface *ifp) +{ + vrrp_zebra_debug_if_state(ifp, ifp->vrf_id, __func__); + + vrrp_if_down(ifp); + + return 0; +} + +static int vrrp_zebra_if_address_add(int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + struct connected *c; + + /* + * zebra api notifies address adds/dels events by using the same call + * interface_add_read below, see comments in lib/zclient.c + * + * zebra_interface_address_read(ZEBRA_INTERFACE_ADDRESS_ADD, ...) + * will add address to interface list by calling + * connected_add_by_prefix() + */ + c = zebra_interface_address_read(command, zclient->ibuf, vrf_id); + + if (!c) + return 0; + + vrrp_zebra_debug_if_state(c->ifp, vrf_id, __func__); + vrrp_zebra_debug_if_dump_address(c->ifp, __func__); + + vrrp_if_address_add(c->ifp); + + return 0; +} + +static int vrrp_zebra_if_address_del(int command, struct zclient *client, + zebra_size_t length, vrf_id_t vrf_id) +{ + struct connected *c; + + /* + * zebra api notifies address adds/dels events by using the same call + * interface_add_read below, see comments in lib/zclient.c + * + * zebra_interface_address_read(ZEBRA_INTERFACE_ADDRESS_DELETE, ...) + * will remove address from interface list by calling + * connected_delete_by_prefix() + */ + c = zebra_interface_address_read(command, client->ibuf, vrf_id); + + if (!c) + return 0; + + vrrp_zebra_debug_if_state(c->ifp, vrf_id, __func__); + vrrp_zebra_debug_if_dump_address(c->ifp, __func__); + + vrrp_if_address_del(c->ifp); + + return 0; +} + +void vrrp_zebra_radv_set(struct vrrp_router *r, bool enable) +{ + DEBUGD(&vrrp_dbg_zebra, + VRRP_LOGPFX VRRP_LOGPFX_VRID + "Requesting Zebra to turn router advertisements %s for %s", + r->vr->vrid, enable ? "on" : "off", r->mvl_ifp->name); + + zclient_send_interface_radv_req(zclient, r->mvl_ifp->vrf_id, r->mvl_ifp, + enable, VRRP_RADV_INT); +} + +int vrrp_zclient_send_interface_protodown(struct interface *ifp, bool down) +{ + DEBUGD(&vrrp_dbg_zebra, + VRRP_LOGPFX "Requesting Zebra to set %s protodown %s", ifp->name, + down ? "on" : "off"); + + return zclient_send_interface_protodown(zclient, ifp->vrf_id, ifp, + down); +} + +void vrrp_zebra_init(void) +{ + if_zapi_callbacks(vrrp_ifp_create, vrrp_ifp_up, + vrrp_ifp_down, vrrp_ifp_destroy); + + /* Socket for receiving updates from Zebra daemon */ + zclient = zclient_new(master, &zclient_options_default); + + zclient->zebra_connected = vrrp_zebra_connected; + zclient->router_id_update = vrrp_router_id_update_zebra; + zclient->interface_address_add = vrrp_zebra_if_address_add; + zclient->interface_address_delete = vrrp_zebra_if_address_del; + + zclient_init(zclient, ZEBRA_ROUTE_VRRP, 0, &vrrp_privs); + + zlog_notice("%s: zclient socket initialized", __func__); +} diff --git a/vrrpd/vrrp_zebra.h b/vrrpd/vrrp_zebra.h new file mode 100644 index 0000000000..02d7055b86 --- /dev/null +++ b/vrrpd/vrrp_zebra.h @@ -0,0 +1,37 @@ +/* + * VRRP Zebra interfacing. + * Copyright (C) 2018-2019 Cumulus Networks, Inc. + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef __VRRP_ZEBRA_H__ +#define __VRRP_ZEBRA_H__ + +#include + +#include "lib/if.h" + +extern void vrrp_zebra_init(void); +extern void vrrp_zebra_radv_set(struct vrrp_router *r, bool enable); +extern int vrrp_zclient_send_interface_protodown(struct interface *ifp, + bool down); + +extern int vrrp_ifp_create(struct interface *ifp); +extern int vrrp_ifp_up(struct interface *ifp); +extern int vrrp_ifp_down(struct interface *ifp); +extern int vrrp_ifp_destroy(struct interface *ifp); + +#endif /* __VRRP_ZEBRA_H__ */ diff --git a/vtysh/extract.pl.in b/vtysh/extract.pl.in index 332fd248ca..60c31bd847 100755 --- a/vtysh/extract.pl.in +++ b/vtysh/extract.pl.in @@ -40,13 +40,15 @@ sub scan_file { $cppadd = $fabricd ? "-DFABRICD=1" : ""; - open (FH, "@CPP@ -DHAVE_CONFIG_H -DVTYSH_EXTRACT_PL -Ivtysh/@top_builddir@ -Ivtysh/@top_srcdir@ -Ivtysh/@top_srcdir@/lib -Ivtysh/@top_builddir@/lib -Ivtysh/@top_srcdir@/bgpd -Ivtysh/@top_srcdir@/bgpd/rfapi @CPPFLAGS@ $cppadd $file |"); + open (FH, "@CPP@ -P -DHAVE_CONFIG_H -DVTYSH_EXTRACT_PL -Ivtysh/@top_builddir@ -Ivtysh/@top_srcdir@ -Ivtysh/@top_srcdir@/lib -Ivtysh/@top_builddir@/lib -Ivtysh/@top_srcdir@/bgpd -Ivtysh/@top_srcdir@/bgpd/rfapi @LUA_INCLUDE@ @CPPFLAGS@ $cppadd $file |"); local $/; undef $/; $line = ; - close (FH); + if (!close (FH)) { + printf "File: $file failed to compile, when extracting cli from it please inspect\n" + } # ?: makes a group non-capturing - @defun = ($line =~ /((?:DEFUN|DEFUN_HIDDEN|ALIAS|ALIAS_HIDDEN)\s*\(.+?\));?\s?\s?\n/sg); + @defun = ($line =~ /((?:DEFUN|DEFUN_HIDDEN|DEFUN_YANG|ALIAS|ALIAS_HIDDEN|ALIAS_YANG|DEFPY|DEFPY_HIDDEN|DEFPY_YANG)\s*\(.+?\));?\s?\s?\n/sg); @install = ($line =~ /install_element\s*\(\s*[0-9A-Z_]+,\s*&[^;]*;\s*\n/sg); # DEFUN process @@ -87,7 +89,7 @@ sub scan_file { if ($file =~ /lib\/keychain\.c$/) { $protocol = "VTYSH_RIPD|VTYSH_EIGRPD"; } - elsif ($file =~ /lib\/routemap\.c$/) { + elsif ($file =~ /lib\/routemap\.c$/ || $file =~ /lib\/routemap_cli\.c$/) { $protocol = "VTYSH_RMAP"; } elsif ($file =~ /lib\/vrf\.c$/) { @@ -96,20 +98,17 @@ sub scan_file { elsif ($file =~ /lib\/if\.c$/) { $protocol = "VTYSH_INTERFACE"; } - elsif ($file =~ /lib\/logicalrouter\.c$/) { - $protocol = "VTYSH_ALL"; + elsif ($file =~ /lib\/(filter|filter_cli)\.c$/) { + $protocol = "VTYSH_ACL"; } - elsif ($file =~ /lib\/filter\.c$/) { + elsif ($file =~ /lib\/lib_vty\.c$/) { $protocol = "VTYSH_ALL"; } elsif ($file =~ /lib\/agentx\.c$/) { $protocol = "VTYSH_RIPD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ZEBRA"; } - elsif ($file =~ /lib\/ns\.c$/) { - $protocol = "VTYSH_ZEBRA"; - } elsif ($file =~ /lib\/nexthop_group\.c$/) { - $protocol = "VTYSH_PBRD | VTYSH_SHARPD"; + $protocol = "VTYSH_NH_GROUP"; } elsif ($file =~ /lib\/plist\.c$/) { if ($defun_array[1] =~ m/ipv6/) { @@ -142,7 +141,7 @@ sub scan_file { $protocol = "VTYSH_FABRICD"; } else { - ($protocol) = ($file =~ /^.*\/([a-z0-9]+)\/[a-zA-Z0-9_\-]+\.c$/); + ($protocol) = ($file =~ /^(?:.*\/)?([a-z0-9]+)\/[a-zA-Z0-9_\-]+\.c$/); $protocol = "VTYSH_" . uc $protocol; } @@ -201,7 +200,7 @@ sub scan_file { } foreach (@ARGV) { - if (/\/isisd\//) { + if (/(^|\/)isisd\//) { # We scan all the IS-IS files twice, once for isisd, # once for fabricd. Exceptions are made for the files # that are not shared between the two. diff --git a/vtysh/subdir.am b/vtysh/subdir.am index 74595788b0..3e9b8a3dc9 100644 --- a/vtysh/subdir.am +++ b/vtysh/subdir.am @@ -31,5 +31,5 @@ am__v_EXTRACT_ = $(am__v_EXTRACT_$(AM_DEFAULT_VERBOSITY)) am__v_EXTRACT_0 = @echo " EXTRACT " $@; am__v_EXTRACT_1 = -vtysh/vtysh_cmd.c: $(vtysh_scan) vtysh/extract.pl - $(AM_V_EXTRACT) vtysh/extract.pl $(vtysh_scan) > vtysh/vtysh_cmd.c +vtysh/vtysh_cmd.c: vtysh/extract.pl $(vtysh_scan) + $(AM_V_EXTRACT) $^ > vtysh/vtysh_cmd.c diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index eff1e996ed..cf1811bb1f 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -39,13 +39,13 @@ #include "filter.h" #include "vtysh/vtysh.h" #include "log.h" -#include "bgpd/bgp_vty.h" #include "ns.h" #include "vrf.h" #include "libfrr.h" #include "command_graph.h" #include "frrstr.h" #include "json.h" +#include "ferr.h" DEFINE_MTYPE_STATIC(MVTYSH, VTYSH_CMD, "Vtysh cmd copy") @@ -137,8 +137,24 @@ struct vtysh_client vtysh_client[] = { {.fd = -1, .name = "pbrd", .flag = VTYSH_PBRD, .next = NULL}, {.fd = -1, .name = "staticd", .flag = VTYSH_STATICD, .next = NULL}, {.fd = -1, .name = "bfdd", .flag = VTYSH_BFDD, .next = NULL}, + {.fd = -1, .name = "vrrpd", .flag = VTYSH_VRRPD, .next = NULL}, }; +/* Searches for client by name, returns index */ +static int vtysh_client_lookup(const char *name) +{ + int idx = -1; + + for (unsigned int i = 0; i < array_size(vtysh_client); i++) { + if (strmatch(vtysh_client[i].name, name)) { + idx = i; + break; + } + } + + return idx; +} + enum vtysh_write_integrated vtysh_write_integrated = WRITE_INTEGRATED_UNSPECIFIED; @@ -147,9 +163,10 @@ static int vtysh_reconnect(struct vtysh_client *vclient); static void vclient_close(struct vtysh_client *vclient) { if (vclient->fd >= 0) { - vty_out(vty, - "Warning: closing connection to %s because of an I/O error!\n", - vclient->name); + if (vty->of) + vty_out(vty, + "Warning: closing connection to %s because of an I/O error!\n", + vclient->name); close(vclient->fd); /* indicate as candidate for reconnect */ vclient->fd = VTYSH_WAS_ACTIVE; @@ -220,8 +237,11 @@ static int vtysh_client_run(struct vtysh_client *vclient, const char *line, continue; if (nread <= 0) { - vty_out(vty, "vtysh: error reading from %s: %s (%d)", - vclient->name, safe_strerror(errno), errno); + if (vty->of) + vty_out(vty, + "vtysh: error reading from %s: %s (%d)", + vclient->name, safe_strerror(errno), + errno); goto out_err; } @@ -366,7 +386,7 @@ static int vtysh_client_run_all(struct vtysh_client *head_client, rc_all = rc; } } - if (wrong_instance && !correct_instance) { + if (wrong_instance && !correct_instance && vty->of) { vty_out(vty, "%% [%s]: command ignored as it targets an instance that is not running\n", head_client->name); @@ -393,6 +413,23 @@ static int vtysh_client_execute(struct vtysh_client *head_client, return vtysh_client_run_all(head_client, line, 0, NULL, NULL); } +/* Execute by name */ +static int vtysh_client_execute_name(const char *name, const char *line) +{ + int ret = CMD_SUCCESS; + int idx_client = -1; + + idx_client = vtysh_client_lookup(name); + if (idx_client != -1) + ret = vtysh_client_execute(&vtysh_client[idx_client], line); + else { + vty_out(vty, "Client not found\n"); + ret = CMD_WARNING; + } + + return ret; +} + /* * Retrieve all running config from daemons and parse it with the vtysh config * parser. Returned output is not displayed to the user. @@ -502,7 +539,7 @@ static int vtysh_execute_func(const char *line, int pager) vtysh_execute("exit"); } else if (tried) { vtysh_execute("end"); - vtysh_execute("configure terminal"); + vtysh_execute("configure"); } } /* @@ -540,7 +577,7 @@ static int vtysh_execute_func(const char *line, int pager) if (pager && strncmp(line, "exit", 4)) vty_open_pager(vty); - if (!strcmp(cmd->string, "configure terminal")) { + if (!strcmp(cmd->string, "configure")) { for (i = 0; i < array_size(vtysh_client); i++) { cmd_stat = vtysh_client_execute( &vtysh_client[i], line); @@ -634,11 +671,11 @@ static char *trim(char *s) return s; end = s + size - 1; - while (end >= s && isspace((int)*end)) + while (end >= s && isspace((unsigned char)*end)) end--; *(end + 1) = '\0'; - while (*s && isspace((int)*s)) + while (*s && isspace((unsigned char)*s)) s++; return s; @@ -665,40 +702,40 @@ int vtysh_mark_file(const char *filename) if (confp == NULL) { fprintf(stderr, "%% Can't open config file %s due to '%s'.\n", filename, safe_strerror(errno)); - return (CMD_ERR_NO_FILE); + return CMD_ERR_NO_FILE; } vty = vty_new(); - vty->wfd = STDERR_FILENO; + vty->wfd = STDOUT_FILENO; vty->type = VTY_TERM; vty->node = CONFIG_NODE; vtysh_execute_no_pager("enable"); - vtysh_execute_no_pager("configure terminal"); + vtysh_execute_no_pager("configure"); vty_buf_copy = XCALLOC(MTYPE_VTYSH_CMD, VTY_BUFSIZ); while (fgets(vty->buf, VTY_BUFSIZ, confp)) { lineno++; tried = 0; - strcpy(vty_buf_copy, vty->buf); + strlcpy(vty_buf_copy, vty->buf, VTY_BUFSIZ); vty_buf_trimmed = trim(vty_buf_copy); switch (vty->node) { case LDP_IPV4_IFACE_NODE: if (strncmp(vty_buf_copy, " ", 3)) { - vty_out(vty, " end\n"); + vty_out(vty, " exit-ldp-if\n"); vty->node = LDP_IPV4_NODE; } break; case LDP_IPV6_IFACE_NODE: if (strncmp(vty_buf_copy, " ", 3)) { - vty_out(vty, " end\n"); + vty_out(vty, " exit-ldp-if\n"); vty->node = LDP_IPV6_NODE; } break; case LDP_PSEUDOWIRE_NODE: if (strncmp(vty_buf_copy, " ", 2)) { - vty_out(vty, " end\n"); + vty_out(vty, " exit\n"); vty->node = LDP_L2VPN_NODE; } break; @@ -771,6 +808,9 @@ int vtysh_mark_file(const char *filename) } else if ((prev_node == KEYCHAIN_KEY_NODE) && (tried == 1)) { vty_out(vty, "exit\n"); + } else if ((prev_node == BFD_PEER_NODE) + && (tried == 1)) { + vty_out(vty, "exit\n"); } else if (tried) { vty_out(vty, "end\n"); } @@ -818,11 +858,15 @@ int vtysh_mark_file(const char *filename) return CMD_ERR_INCOMPLETE; case CMD_SUCCESS: vty_out(vty, "%s", vty->buf); + if (strmatch(vty_buf_trimmed, "exit-vrf")) + vty_out(vty, "end\n"); break; case CMD_SUCCESS_DAEMON: { int cmd_stat; vty_out(vty, "%s", vty->buf); + if (strmatch(vty_buf_trimmed, "exit-vrf")) + vty_out(vty, "end\n"); cmd_stat = vtysh_client_execute(&vtysh_client[0], vty->buf); if (cmd_stat != CMD_SUCCESS) @@ -841,7 +885,7 @@ int vtysh_mark_file(const char *filename) if (confp != stdin) fclose(confp); - return (0); + return 0; } /* Configration make from file. */ @@ -945,7 +989,7 @@ static int vtysh_process_questionmark(const char *input, int input_len) if (vline == NULL) { vline = vector_init(1); vector_set(vline, NULL); - } else if (input_len && isspace((int)input[input_len - 1])) + } else if (input_len && isspace((unsigned char)input[input_len - 1])) vector_set(vline, NULL); describe = cmd_describe_command(vline, vty, &ret); @@ -958,7 +1002,6 @@ static int vtysh_process_questionmark(const char *input, int input_len) vty_out(vty, "%% Ambiguous command.\n"); rl_on_new_line(); return 0; - break; case CMD_ERR_NO_MATCH: cmd_free_strvec(vline); if (describe) @@ -966,7 +1009,6 @@ static int vtysh_process_questionmark(const char *input, int input_len) vty_out(vty, "%% There is no matched command.\n"); rl_on_new_line(); return 0; - break; } /* Get width of command string. */ @@ -1093,7 +1135,8 @@ static char *command_generator(const char *text, int state) if (vline == NULL) return NULL; - if (rl_end && isspace((int)rl_line_buffer[rl_end - 1])) + if (rl_end && + isspace((unsigned char)rl_line_buffer[rl_end - 1])) vector_set(vline, NULL); matched = cmd_complete_command(vline, vty, &complete_status); @@ -1108,7 +1151,6 @@ static char *command_generator(const char *text, int state) return matched[index++]; XFREE(MTYPE_TMP, matched); - matched = NULL; return NULL; } @@ -1131,143 +1173,333 @@ static char **new_completion(const char *text, int start, int end) /* Vty node structures. */ static struct cmd_node bgp_node = { - BGP_NODE, "%s(config-router)# ", + .name = "bgp", + .node = BGP_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-router)# ", }; static struct cmd_node rip_node = { - RIP_NODE, "%s(config-router)# ", + .name = "rip", + .node = RIP_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-router)# ", }; static struct cmd_node isis_node = { - ISIS_NODE, "%s(config-router)# ", + .name = "isis", + .node = ISIS_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-router)# ", }; static struct cmd_node openfabric_node = { - OPENFABRIC_NODE, "%s(config-router)# ", + .name = "openfabric", + .node = OPENFABRIC_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-router)# ", }; static struct cmd_node interface_node = { - INTERFACE_NODE, "%s(config-if)# ", + .name = "interface", + .node = INTERFACE_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-if)# ", }; static struct cmd_node pw_node = { - PW_NODE, "%s(config-pw)# ", -}; - -static struct cmd_node logicalrouter_node = { - LOGICALROUTER_NODE, "%s(config-logical-router)# ", + .name = "pw", + .node = PW_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-pw)# ", }; static struct cmd_node vrf_node = { - VRF_NODE, "%s(config-vrf)# ", + .name = "vrf", + .node = VRF_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-vrf)# ", }; static struct cmd_node nh_group_node = { - NH_GROUP_NODE, - "%s(config-nh-group)# ", + .name = "nexthop-group", + .node = NH_GROUP_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-nh-group)# ", }; -static struct cmd_node rmap_node = {RMAP_NODE, "%s(config-route-map)# "}; +static struct cmd_node rmap_node = { + .name = "routemap", + .node = RMAP_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-route-map)# ", +}; -static struct cmd_node pbr_map_node = {PBRMAP_NODE, "%s(config-pbr-map)# "}; +static struct cmd_node pbr_map_node = { + .name = "pbr-map", + .node = PBRMAP_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-pbr-map)# ", +}; -static struct cmd_node zebra_node = {ZEBRA_NODE, "%s(config-router)# "}; +static struct cmd_node zebra_node = { + .name = "zebra", + .node = ZEBRA_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-router)# ", +}; -static struct cmd_node bgp_vpnv4_node = {BGP_VPNV4_NODE, - "%s(config-router-af)# "}; +static struct cmd_node bgp_vpnv4_node = { + .name = "bgp vpnv4", + .node = BGP_VPNV4_NODE, + .parent_node = BGP_NODE, + .prompt = "%s(config-router-af)# ", +}; -static struct cmd_node bgp_vpnv6_node = {BGP_VPNV6_NODE, - "%s(config-router-af)# "}; +static struct cmd_node bgp_vpnv6_node = { + .name = "bgp vpnv6", + .node = BGP_VPNV6_NODE, + .parent_node = BGP_NODE, + .prompt = "%s(config-router-af)# ", +}; -static struct cmd_node bgp_flowspecv4_node = {BGP_FLOWSPECV4_NODE, - "%s(config-router-af)# "}; +static struct cmd_node bgp_flowspecv4_node = { + .name = "bgp ipv4 flowspec", + .node = BGP_FLOWSPECV4_NODE, + .parent_node = BGP_NODE, + .prompt = "%s(config-router-af)# ", +}; -static struct cmd_node bgp_flowspecv6_node = {BGP_FLOWSPECV6_NODE, - "%s(config-router-af)# "}; +static struct cmd_node bgp_flowspecv6_node = { + .name = "bgp ipv6 flowspec", + .node = BGP_FLOWSPECV6_NODE, + .parent_node = BGP_NODE, + .prompt = "%s(config-router-af)# ", +}; -static struct cmd_node bgp_ipv4_node = {BGP_IPV4_NODE, - "%s(config-router-af)# "}; +static struct cmd_node bgp_ipv4_node = { + .name = "bgp ipv4 unicast", + .node = BGP_IPV4_NODE, + .parent_node = BGP_NODE, + .prompt = "%s(config-router-af)# ", +}; -static struct cmd_node bgp_ipv4m_node = {BGP_IPV4M_NODE, - "%s(config-router-af)# "}; +static struct cmd_node bgp_ipv4m_node = { + .name = "bgp ipv4 multicast", + .node = BGP_IPV4M_NODE, + .parent_node = BGP_NODE, + .prompt = "%s(config-router-af)# ", +}; -static struct cmd_node bgp_ipv4l_node = {BGP_IPV4L_NODE, - "%s(config-router-af)# "}; +static struct cmd_node bgp_ipv4l_node = { + .name = "bgp ipv4 labeled unicast", + .node = BGP_IPV4L_NODE, + .parent_node = BGP_NODE, + .prompt = "%s(config-router-af)# ", +}; -static struct cmd_node bgp_ipv6_node = {BGP_IPV6_NODE, - "%s(config-router-af)# "}; +static struct cmd_node bgp_ipv6_node = { + .name = "bgp ipv6", + .node = BGP_IPV6_NODE, + .parent_node = BGP_NODE, + .prompt = "%s(config-router-af)# ", +}; -static struct cmd_node bgp_ipv6m_node = {BGP_IPV6M_NODE, - "%s(config-router-af)# "}; +static struct cmd_node bgp_ipv6m_node = { + .name = "bgp ipv6 multicast", + .node = BGP_IPV6M_NODE, + .parent_node = BGP_NODE, + .prompt = "%s(config-router-af)# ", +}; -static struct cmd_node bgp_evpn_node = {BGP_EVPN_NODE, - "%s(config-router-af)# "}; +static struct cmd_node bgp_evpn_node = { + .name = "bgp evpn", + .node = BGP_EVPN_NODE, + .parent_node = BGP_NODE, + .prompt = "%s(config-router-af)# ", +}; -static struct cmd_node bgp_evpn_vni_node = {BGP_EVPN_VNI_NODE, - "%s(config-router-af-vni)# "}; +static struct cmd_node bgp_evpn_vni_node = { + .name = "bgp evpn vni", + .node = BGP_EVPN_VNI_NODE, + .parent_node = BGP_EVPN_NODE, + .prompt = "%s(config-router-af-vni)# ", +}; -static struct cmd_node bgp_ipv6l_node = {BGP_IPV6L_NODE, - "%s(config-router-af)# "}; +static struct cmd_node bgp_ipv6l_node = { + .name = "bgp ipv6 labeled unicast", + .node = BGP_IPV6L_NODE, + .parent_node = BGP_NODE, + .prompt = "%s(config-router-af)# ", +}; static struct cmd_node bgp_vnc_defaults_node = { - BGP_VNC_DEFAULTS_NODE, "%s(config-router-vnc-defaults)# "}; + .name = "bgp vnc defaults", + .node = BGP_VNC_DEFAULTS_NODE, + .parent_node = BGP_NODE, + .prompt = "%s(config-router-vnc-defaults)# ", +}; static struct cmd_node bgp_vnc_nve_group_node = { - BGP_VNC_NVE_GROUP_NODE, "%s(config-router-vnc-nve-group)# "}; + .name = "bgp vnc nve", + .node = BGP_VNC_NVE_GROUP_NODE, + .parent_node = BGP_NODE, + .prompt = "%s(config-router-vnc-nve-group)# ", +}; -static struct cmd_node bgp_vrf_policy_node = {BGP_VRF_POLICY_NODE, - "%s(config-router-vrf-policy)# "}; +static struct cmd_node bgp_vrf_policy_node = { + .name = "bgp vrf policy", + .node = BGP_VRF_POLICY_NODE, + .parent_node = BGP_NODE, + .prompt = "%s(config-router-vrf-policy)# ", +}; static struct cmd_node bgp_vnc_l2_group_node = { - BGP_VNC_L2_GROUP_NODE, "%s(config-router-vnc-l2-group)# "}; + .name = "bgp vnc l2", + .node = BGP_VNC_L2_GROUP_NODE, + .parent_node = BGP_NODE, + .prompt = "%s(config-router-vnc-l2-group)# ", +}; + +static struct cmd_node bmp_node = { + .name = "bmp", + .node = BMP_NODE, + .parent_node = BGP_NODE, + .prompt = "%s(config-bgp-bmp)# " +}; -static struct cmd_node ospf_node = {OSPF_NODE, "%s(config-router)# "}; +static struct cmd_node ospf_node = { + .name = "ospf", + .node = OSPF_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-router)# ", +}; -static struct cmd_node eigrp_node = {EIGRP_NODE, "%s(config-router)# "}; +static struct cmd_node eigrp_node = { + .name = "eigrp", + .node = EIGRP_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-router)# ", +}; -static struct cmd_node babel_node = {BABEL_NODE, "%s(config-router)# "}; +static struct cmd_node babel_node = { + .name = "babel", + .node = BABEL_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-router)# ", +}; -static struct cmd_node ripng_node = {RIPNG_NODE, "%s(config-router)# "}; +static struct cmd_node ripng_node = { + .name = "ripng", + .node = RIPNG_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-router)# ", +}; -static struct cmd_node ospf6_node = {OSPF6_NODE, "%s(config-ospf6)# "}; +static struct cmd_node ospf6_node = { + .name = "ospf6", + .node = OSPF6_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-ospf6)# ", +}; -static struct cmd_node ldp_node = {LDP_NODE, "%s(config-ldp)# "}; +static struct cmd_node ldp_node = { + .name = "ldp", + .node = LDP_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-ldp)# ", +}; -static struct cmd_node ldp_ipv4_node = {LDP_IPV4_NODE, "%s(config-ldp-af)# "}; +static struct cmd_node ldp_ipv4_node = { + .name = "ldp ipv4", + .node = LDP_IPV4_NODE, + .parent_node = LDP_NODE, + .prompt = "%s(config-ldp-af)# ", +}; -static struct cmd_node ldp_ipv6_node = {LDP_IPV6_NODE, "%s(config-ldp-af)# "}; +static struct cmd_node ldp_ipv6_node = { + .name = "ldp ipv6", + .node = LDP_IPV6_NODE, + .parent_node = LDP_NODE, + .prompt = "%s(config-ldp-af)# ", +}; -static struct cmd_node ldp_ipv4_iface_node = {LDP_IPV4_IFACE_NODE, - "%s(config-ldp-af-if)# "}; +static struct cmd_node ldp_ipv4_iface_node = { + .name = "ldp ipv4 interface", + .node = LDP_IPV4_IFACE_NODE, + .parent_node = LDP_IPV4_NODE, + .prompt = "%s(config-ldp-af-if)# ", +}; -static struct cmd_node ldp_ipv6_iface_node = {LDP_IPV6_IFACE_NODE, - "%s(config-ldp-af-if)# "}; +static struct cmd_node ldp_ipv6_iface_node = { + .name = "ldp ipv6 interface", + .node = LDP_IPV6_IFACE_NODE, + .parent_node = LDP_IPV6_NODE, + .prompt = "%s(config-ldp-af-if)# ", +}; -static struct cmd_node ldp_l2vpn_node = {LDP_L2VPN_NODE, "%s(config-l2vpn)# "}; +static struct cmd_node ldp_l2vpn_node = { + .name = "ldp l2vpn", + .node = LDP_L2VPN_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-l2vpn)# ", +}; -static struct cmd_node ldp_pseudowire_node = {LDP_PSEUDOWIRE_NODE, - "%s(config-l2vpn-pw)# "}; +static struct cmd_node ldp_pseudowire_node = { + .name = "ldp", + .node = LDP_PSEUDOWIRE_NODE, + .parent_node = LDP_L2VPN_NODE, + .prompt = "%s(config-l2vpn-pw)# ", +}; -static struct cmd_node keychain_node = {KEYCHAIN_NODE, "%s(config-keychain)# "}; +static struct cmd_node keychain_node = { + .name = "keychain", + .node = KEYCHAIN_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-keychain)# ", +}; -static struct cmd_node keychain_key_node = {KEYCHAIN_KEY_NODE, - "%s(config-keychain-key)# "}; +static struct cmd_node keychain_key_node = { + .name = "keychain key", + .node = KEYCHAIN_KEY_NODE, + .parent_node = KEYCHAIN_NODE, + .prompt = "%s(config-keychain-key)# ", +}; struct cmd_node link_params_node = { - LINK_PARAMS_NODE, "%s(config-link-params)# ", + .name = "link-params", + .node = LINK_PARAMS_NODE, + .parent_node = INTERFACE_NODE, + .prompt = "%s(config-link-params)# ", }; -static struct cmd_node rpki_node = {RPKI_NODE, "%s(config-rpki)# ", 1}; +static struct cmd_node rpki_node = { + .name = "rpki", + .node = RPKI_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-rpki)# ", +}; #if HAVE_BFDD > 0 static struct cmd_node bfd_node = { - BFD_NODE, - "%s(config-bfd)# ", + .name = "bfd", + .node = BFD_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-bfd)# ", }; static struct cmd_node bfd_peer_node = { - BFD_PEER_NODE, - "%s(config-bfd-peer)# ", + .name = "bfd peer", + .node = BFD_PEER_NODE, + .parent_node = BFD_NODE, + .prompt = "%s(config-bfd-peer)# ", +}; + +static struct cmd_node bfd_profile_node = { + .name = "bfd profile", + .node = BFD_PROFILE_NODE, + .parent_node = BFD_NODE, + .prompt = "%s(config-bfd-profile)# ", }; #endif /* HAVE_BFDD */ @@ -1439,6 +1671,18 @@ DEFUNSH(VTYSH_BGPD, return CMD_SUCCESS; } +DEFUNSH(VTYSH_BGPD, + bmp_targets, + bmp_targets_cmd, + "bmp targets BMPTARGETS", + "BGP Monitoring Protocol\n" + "Create BMP target group\n" + "Name of the BMP target group\n") +{ + vty->node = BMP_NODE; + return CMD_SUCCESS; +} + DEFUNSH(VTYSH_BGPD, address_family_evpn, address_family_evpn_cmd, "address-family ", "Enter Address Family command mode\n" @@ -1546,10 +1790,11 @@ DEFUNSH(VTYSH_OSPFD, router_ospf, router_ospf_cmd, return CMD_SUCCESS; } -DEFUNSH(VTYSH_EIGRPD, router_eigrp, router_eigrp_cmd, "router eigrp (1-65535)", +DEFUNSH(VTYSH_EIGRPD, router_eigrp, router_eigrp_cmd, "router eigrp (1-65535) [vrf NAME]", "Enable a routing process\n" "Start EIGRP configuration\n" - "AS number to use\n") + "AS number to use\n" + VRF_CMD_HELP_STR) { vty->node = EIGRP_NODE; return CMD_SUCCESS; @@ -1646,10 +1891,11 @@ DEFUNSH(VTYSH_LDPD, ldp_member_pseudowire_ifname, } #endif -DEFUNSH(VTYSH_ISISD, router_isis, router_isis_cmd, "router isis WORD", +DEFUNSH(VTYSH_ISISD, router_isis, router_isis_cmd, + "router isis WORD [vrf NAME]", ROUTER_STR "ISO IS-IS\n" - "ISO Routing area tag\n") + "ISO Routing area tag\n" VRF_CMD_HELP_STR) { vty->node = ISIS_NODE; return CMD_SUCCESS; @@ -1677,7 +1923,7 @@ DEFUNSH(VTYSH_RMAP, vtysh_route_map, vtysh_route_map_cmd, } DEFUNSH(VTYSH_PBRD, vtysh_pbr_map, vtysh_pbr_map_cmd, - "pbr-map NAME seq (1-700)", + "pbr-map PBRMAP seq (1-700)", "Create pbr-map or enter pbr-map command mode\n" "The name of the PBR MAP\n" "Sequence to insert to/delete from existing pbr-map entry\n" @@ -1711,9 +1957,18 @@ DEFUNSH(VTYSH_BFDD, bfd_peer_enter, bfd_peer_enter_cmd, vty->node = BFD_PEER_NODE; return CMD_SUCCESS; } + +DEFUNSH(VTYSH_BFDD, bfd_profile_enter, bfd_profile_enter_cmd, + "profile BFDPROF", + BFD_PROFILE_STR + BFD_PROFILE_NAME_STR) +{ + vty->node = BFD_PROFILE_NODE; + return CMD_SUCCESS; +} #endif /* HAVE_BFDD */ -DEFSH(VTYSH_PBRD, vtysh_no_pbr_map_cmd, "no pbr-map WORD [seq (1-700)]", +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" @@ -1744,7 +1999,7 @@ DEFUNSH(VTYSH_REALLYALL, vtysh_disable, vtysh_disable_cmd, "disable", } DEFUNSH(VTYSH_REALLYALL, vtysh_config_terminal, vtysh_config_terminal_cmd, - "configure terminal", + "configure [terminal]", "Configuration from vty interface\n" "Configuration terminal\n") { @@ -1754,85 +2009,19 @@ DEFUNSH(VTYSH_REALLYALL, vtysh_config_terminal, vtysh_config_terminal_cmd, static int vtysh_exit(struct vty *vty) { - switch (vty->node) { - case VIEW_NODE: - case ENABLE_NODE: + struct cmd_node *cnode = vector_lookup(cmdvec, vty->node); + + if (vty->node == VIEW_NODE || vty->node == ENABLE_NODE) exit(0); - break; - case CONFIG_NODE: - vty->node = ENABLE_NODE; - break; - case INTERFACE_NODE: - case PW_NODE: - case LOGICALROUTER_NODE: - case VRF_NODE: - case NH_GROUP_NODE: - case ZEBRA_NODE: - case BGP_NODE: - case RIP_NODE: - case RIPNG_NODE: - case OSPF_NODE: - case OSPF6_NODE: - case EIGRP_NODE: - case BABEL_NODE: - case LDP_NODE: - case LDP_L2VPN_NODE: - case ISIS_NODE: - case OPENFABRIC_NODE: - case RMAP_NODE: - case PBRMAP_NODE: - case VTY_NODE: - case KEYCHAIN_NODE: - case BFD_NODE: - case RPKI_NODE: + if (cnode->node_exit) + cnode->node_exit(vty); + if (cnode->parent_node) + vty->node = cnode->parent_node; + + if (vty->node == CONFIG_NODE) { + /* resync in case one of the daemons is somewhere else */ vtysh_execute("end"); - vtysh_execute("configure terminal"); - vty->node = CONFIG_NODE; - break; - case BGP_VPNV4_NODE: - case BGP_VPNV6_NODE: - case BGP_IPV4_NODE: - case BGP_IPV4M_NODE: - case BGP_IPV4L_NODE: - case BGP_IPV6_NODE: - case BGP_IPV6M_NODE: - case BGP_IPV6L_NODE: - case BGP_FLOWSPECV4_NODE: - case BGP_FLOWSPECV6_NODE: - case BGP_VRF_POLICY_NODE: - case BGP_EVPN_NODE: - case BGP_VNC_DEFAULTS_NODE: - case BGP_VNC_NVE_GROUP_NODE: - case BGP_VNC_L2_GROUP_NODE: - vty->node = BGP_NODE; - break; - case BGP_EVPN_VNI_NODE: - vty->node = BGP_EVPN_NODE; - break; - case LDP_IPV4_NODE: - case LDP_IPV6_NODE: - vty->node = LDP_NODE; - break; - case LDP_IPV4_IFACE_NODE: - vty->node = LDP_IPV4_NODE; - break; - case LDP_IPV6_IFACE_NODE: - vty->node = LDP_IPV6_NODE; - break; - case LDP_PSEUDOWIRE_NODE: - vty->node = LDP_L2VPN_NODE; - break; - case KEYCHAIN_KEY_NODE: - vty->node = KEYCHAIN_NODE; - break; - case LINK_PARAMS_NODE: - vty->node = INTERFACE_NODE; - break; - case BFD_PEER_NODE: - vty->node = BFD_NODE; - break; - default: - break; + vtysh_execute("configure"); } return CMD_SUCCESS; } @@ -1843,7 +2032,7 @@ DEFUNSH(VTYSH_REALLYALL, vtysh_exit_all, vtysh_exit_all_cmd, "exit", return vtysh_exit(vty); } -DEFUNSH(VTYSH_ALL, vtysh_quit_all, vtysh_quit_all_cmd, "quit", +DEFUNSH(VTYSH_REALLYALL, vtysh_quit_all, vtysh_quit_all_cmd, "quit", "Exit current mode and down to previous mode\n") { return vtysh_exit_all(self, vty, argc, argv); @@ -1894,7 +2083,20 @@ DEFUNSH(VTYSH_BGPD, rpki_quit, rpki_quit_cmd, "quit", return rpki_exit(self, vty, argc, argv); } -DEFUNSH(VTYSH_PIMD|VTYSH_ZEBRA, exit_vrf_config, exit_vrf_config_cmd, "exit-vrf", +DEFUNSH(VTYSH_BGPD, bmp_exit, bmp_exit_cmd, "exit", + "Exit current mode and down to previous mode\n") +{ + vtysh_exit(vty); + return CMD_SUCCESS; +} + +DEFUNSH(VTYSH_BGPD, bmp_quit, bmp_quit_cmd, "quit", + "Exit current mode and down to previous mode\n") +{ + 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) @@ -1994,7 +2196,7 @@ DEFUNSH(VTYSH_EIGRPD, vtysh_quit_eigrpd, vtysh_quit_eigrpd_cmd, "quit", return vtysh_exit(vty); } -DEFUNSH(VTYSH_EIGRPD, vtysh_exit_babeld, vtysh_exit_babeld_cmd, "exit", +DEFUNSH(VTYSH_BABELD, vtysh_exit_babeld, vtysh_exit_babeld_cmd, "exit", "Exit current mode and down to previous mode\n") { return vtysh_exit(vty); @@ -2064,6 +2266,18 @@ DEFUNSH(VTYSH_FABRICD, vtysh_quit_fabricd, vtysh_quit_fabricd_cmd, "quit", return vtysh_exit_fabricd(self, vty, argc, argv); } +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") { @@ -2094,26 +2308,9 @@ DEFUNSH(VTYSH_ZEBRA, vtysh_pseudowire, vtysh_pseudowire_cmd, return CMD_SUCCESS; } -DEFUNSH(VTYSH_ZEBRA, vtysh_logicalrouter, vtysh_logicalrouter_cmd, - "logical-router (1-65535) ns NAME", - "Enable a logical-router\n" - "Specify the logical-router indentifier\n" - "The Name Space\n" - "The file name in " NS_RUN_DIR ", or a full pathname\n") -{ - vty->node = LOGICALROUTER_NODE; - return CMD_SUCCESS; -} - -DEFSH(VTYSH_ZEBRA, vtysh_no_logicalrouter_cmd, - "no logical-router (1-65535) ns NAME", NO_STR - "Enable a Logical-Router\n" - "Specify the Logical-Router identifier\n" - "The Name Space\n" - "The file name in " NS_RUN_DIR ", or a full pathname\n") - -DEFUNSH(VTYSH_PBRD | VTYSH_SHARPD, vtysh_nexthop_group, vtysh_nexthop_group_cmd, - "nexthop-group NAME", +DEFUNSH(VTYSH_NH_GROUP, + vtysh_nexthop_group, vtysh_nexthop_group_cmd, + "nexthop-group NHGNAME", "Nexthop Group configuration\n" "Name of the Nexthop Group\n") { @@ -2121,8 +2318,8 @@ DEFUNSH(VTYSH_PBRD | VTYSH_SHARPD, vtysh_nexthop_group, vtysh_nexthop_group_cmd, return CMD_SUCCESS; } -DEFSH(VTYSH_PBRD | VTYSH_SHARPD, vtysh_no_nexthop_group_cmd, - "no nexthop-group NAME", +DEFSH(VTYSH_NH_GROUP, vtysh_no_nexthop_group_cmd, + "no nexthop-group NHGNAME", NO_STR "Nexthop Group Configuration\n" "Name of the Nexthop Group\n") @@ -2146,20 +2343,6 @@ DEFSH(VTYSH_ZEBRA, vtysh_no_vrf_netns_cmd, "Detach VRF from a Namespace\n" "The file name in " NS_RUN_DIR ", or a full pathname\n") -DEFUNSH(VTYSH_NS, vtysh_exit_logicalrouter, - vtysh_exit_logicalrouter_cmd, "exit", - "Exit current mode and down to previous mode\n") -{ - return vtysh_exit(vty); -} - -DEFUNSH(VTYSH_NS, vtysh_quit_logicalrouter, - vtysh_quit_logicalrouter_cmd, "quit", - "Exit current mode and down to previous mode\n") -{ - return vtysh_exit_logicalrouter(self, vty, argc, argv); -} - DEFUNSH(VTYSH_VRF, vtysh_exit_vrf, vtysh_exit_vrf_cmd, "exit", "Exit current mode and down to previous mode\n") { @@ -2172,13 +2355,15 @@ DEFUNSH(VTYSH_VRF, vtysh_quit_vrf, vtysh_quit_vrf_cmd, "quit", return vtysh_exit_vrf(self, vty, argc, argv); } -DEFUNSH(VTYSH_PBRD | VTYSH_SHARPD, vtysh_exit_nexthop_group, vtysh_exit_nexthop_group_cmd, +DEFUNSH(VTYSH_NH_GROUP, + vtysh_exit_nexthop_group, vtysh_exit_nexthop_group_cmd, "exit", "Exit current mode and down to previous mode\n") { return vtysh_exit(vty); } -DEFUNSH(VTYSH_VRF, vtysh_quit_nexthop_group, vtysh_quit_nexthop_group_cmd, +DEFUNSH(VTYSH_NH_GROUP, + vtysh_quit_nexthop_group, vtysh_quit_nexthop_group_cmd, "quit", "Exit current mode and down to previous mode\n") { return vtysh_exit_nexthop_group(self, vty, argc, argv); @@ -2218,6 +2403,7 @@ DEFUN (vtysh_show_poll, return ret; } +#ifndef EXCLUDE_CPU_TIME DEFUN (vtysh_show_thread, vtysh_show_thread_cmd, "show thread cpu [FILTER]", @@ -2244,6 +2430,7 @@ DEFUN (vtysh_show_thread, } return ret; } +#endif DEFUN (vtysh_show_work_queues, vtysh_show_work_queues_cmd, @@ -2268,33 +2455,15 @@ DEFUN (vtysh_show_work_queues, DEFUN (vtysh_show_work_queues_daemon, vtysh_show_work_queues_daemon_cmd, - "show work-queues ", + "show work-queues " DAEMONS_LIST, SHOW_STR "Work Queue information\n" - "For the zebra daemon\n" - "For the rip daemon\n" - "For the ripng daemon\n" - "For the ospf daemon\n" - "For the ospfv6 daemon\n" - "For the bgp daemon\n" - "For the isis daemon\n" - "For the pbr daemon\n" - "For the fabricd daemon\n" - "For the pim daemon\n" - "For the static daemon\n") + DAEMONS_STR) { int idx_protocol = 2; - unsigned int i; - int ret = CMD_SUCCESS; - - for (i = 0; i < array_size(vtysh_client); i++) { - if (strmatch(vtysh_client[i].name, argv[idx_protocol]->text)) - break; - } - - ret = vtysh_client_execute(&vtysh_client[i], "show work-queues\n"); - return ret; + return vtysh_client_execute_name(argv[idx_protocol]->text, + "show work-queues\n"); } DEFUNSH(VTYSH_ZEBRA, vtysh_link_params, vtysh_link_params_cmd, "link-params", @@ -2379,17 +2548,79 @@ DEFUN (vtysh_show_error_code, "Information on all errors\n" JSON_STR) { - char *fcmd = argv_concat(argv, argc, 0); - char cmd[256]; - int rv; + uint32_t arg = 0; - snprintf(cmd, sizeof(cmd), "do %s", fcmd); + if (!strmatch(argv[2]->text, "all")) + arg = strtoul(argv[2]->arg, NULL, 10); - /* FIXME: Needs to determine which daemon to send to via code ranges */ - rv = show_per_daemon(cmd, ""); + /* If it's not a shared code, send it to all the daemons */ + if (arg < LIB_FERR_START || arg > LIB_FERR_END) { + char *fcmd = argv_concat(argv, argc, 0); + char cmd[256]; + snprintf(cmd, sizeof(cmd), "do %s", fcmd); + show_per_daemon(cmd, ""); + XFREE(MTYPE_TMP, fcmd); + /* Otherwise, print it ourselves to avoid duplication */ + } else { + bool json = strmatch(argv[argc - 1]->text, "json"); + + if (!strmatch(argv[2]->text, "all")) + arg = strtoul(argv[2]->arg, NULL, 10); + + log_ref_display(vty, arg, json); + } + + return CMD_SUCCESS; +} + +/* Northbound. */ +DEFUN (show_yang_operational_data, + show_yang_operational_data_cmd, + "show yang operational-data XPATH\ + [{\ + format \ + |translate WORD\ + }]" DAEMONS_LIST, + SHOW_STR + "YANG information\n" + "Show YANG operational data\n" + "XPath expression specifying the YANG data path\n" + "Set the output format\n" + "JavaScript Object Notation\n" + "Extensible Markup Language\n" + "Translate operational data\n" + "YANG module translator\n" + DAEMONS_STR) +{ + int idx_protocol = argc - 1; + char *fcmd = argv_concat(argv, argc - 1, 0); + int ret = vtysh_client_execute_name(argv[idx_protocol]->text, fcmd); XFREE(MTYPE_TMP, fcmd); - return rv; + return ret; +} + +DEFUNSH(VTYSH_ALL, debug_nb, + debug_nb_cmd, + "[no] debug northbound\ + [<\ + callbacks [{configuration|state|rpc}]\ + |notifications\ + |events\ + |libyang\ + >]", + NO_STR + DEBUG_STR + "Northbound debugging\n" + "Callbacks\n" + "Configuration\n" + "State\n" + "RPC\n" + "Notifications\n" + "Events\n" + "libyang debugging\n") +{ + return CMD_SUCCESS; } /* Memory */ @@ -2518,10 +2749,11 @@ DEFUNSH(VTYSH_ALL, vtysh_log_facility, vtysh_log_facility_cmd, } DEFUNSH(VTYSH_ALL, no_vtysh_log_facility, no_vtysh_log_facility_cmd, - "no log facility [FACILITY]", NO_STR + "no log facility []", + NO_STR "Logging control\n" "Reset syslog facility to default (daemon)\n" - "Syslog facility\n") + LOG_FACILITY_DESC) { return CMD_SUCCESS; } @@ -2552,6 +2784,15 @@ DEFUNSH(VTYSH_ALL, vtysh_log_timestamp_precision, return CMD_SUCCESS; } +DEFUNSH(VTYSH_ALL, vtysh_debug_memstats, + vtysh_debug_memstats_cmd, "[no] debug memstats-at-exit", + NO_STR + "Debug\n" + "Print memory statistics at exit\n") +{ + return CMD_SUCCESS; +} + DEFUNSH(VTYSH_ALL, no_vtysh_log_timestamp_precision, no_vtysh_log_timestamp_precision_cmd, "no log timestamp precision", NO_STR @@ -2619,27 +2860,22 @@ DEFUNSH(VTYSH_ALL, no_vtysh_config_enable_password, DEFUN (vtysh_write_terminal, vtysh_write_terminal_cmd, - "write terminal []", + "write terminal ["DAEMONS_LIST"] [no-header]", "Write running configuration to memory, network, or terminal\n" "Write to terminal\n" - "For the zebra daemon\n" - "For the rip daemon\n" - "For the ripng daemon\n" - "For the ospf daemon\n" - "For the ospfv6 daemon\n" - "For the ldpd daemon\n" - "For the bgp daemon\n" - "For the isis daemon\n" - "For the fabricd daemon\n" - "For the pim daemon\n" - "For the static daemon\n") + DAEMONS_STR + "Skip \"Building configuration...\" header\n") { unsigned int i; char line[] = "do write terminal\n"; - vty_out(vty, "Building configuration...\n"); - vty_out(vty, "\nCurrent configuration:\n"); - vty_out(vty, "!\n"); + if (!strcmp(argv[argc - 1]->arg, "no-header")) + argc--; + else { + vty_out(vty, "Building configuration...\n"); + vty_out(vty, "\nCurrent configuration:\n"); + vty_out(vty, "!\n"); + } for (i = 0; i < array_size(vtysh_client); i++) if ((argc < 3) @@ -2658,20 +2894,11 @@ DEFUN (vtysh_write_terminal, DEFUN (vtysh_show_running_config, vtysh_show_running_config_cmd, - "show running-config []", + "show running-config ["DAEMONS_LIST"] [no-header]", SHOW_STR "Current operating configuration\n" - "For the zebra daemon\n" - "For the rip daemon\n" - "For the ripng daemon\n" - "For the ospf daemon\n" - "For the ospfv6 daemon\n" - "For the ldp daemon\n" - "For the bgp daemon\n" - "For the isis daemon\n" - "For the fabricd daemon\n" - "For the pim daemon\n" - "For the static daemon\n") + DAEMONS_STR + "Skip \"Building configuration...\" header\n") { return vtysh_write_terminal(self, vty, argc, argv); } @@ -2701,18 +2928,18 @@ static void backup_config_file(const char *fbackup) { char *integrate_sav = NULL; - integrate_sav = malloc(strlen(fbackup) + strlen(CONF_BACKUP_EXT) + 1); - strcpy(integrate_sav, fbackup); - strcat(integrate_sav, CONF_BACKUP_EXT); + size_t integrate_sav_sz = strlen(fbackup) + strlen(CONF_BACKUP_EXT) + 1; + integrate_sav = malloc(integrate_sav_sz); + strlcpy(integrate_sav, fbackup, integrate_sav_sz); + strlcat(integrate_sav, CONF_BACKUP_EXT, integrate_sav_sz); /* Move current configuration file to backup config file. */ - if (unlink(integrate_sav) != 0) { - vty_out(vty, "Warning: %s unlink failed\n", integrate_sav); - } - if (rename(fbackup, integrate_sav) != 0) { - vty_out(vty, "Error renaming %s to %s\n", fbackup, - integrate_sav); - } + if (unlink(integrate_sav) != 0 && errno != ENOENT) + vty_out(vty, "Unlink failed for %s: %s\n", integrate_sav, + strerror(errno)); + if (rename(fbackup, integrate_sav) != 0 && errno != ENOENT) + vty_out(vty, "Error renaming %s to %s: %s\n", fbackup, + integrate_sav, strerror(errno)); free(integrate_sav); } @@ -2858,9 +3085,7 @@ DEFUN (vtysh_write_memory, * ourselves */ if (!used_watchfrr) { - printf("\nWarning: attempting direct configuration write without " - "watchfrr.\nFile permissions and ownership may be " - "incorrect, or write may fail.\n\n"); + printf("\nWarning: attempting direct configuration write without watchfrr.\nFile permissions and ownership may be incorrect, or write may fail.\n\n"); ret = vtysh_write_config_integrated(); } return ret; @@ -2884,6 +3109,24 @@ DEFUN (vtysh_copy_running_config, return vtysh_write_memory(self, vty, argc, argv); } +DEFUN (vtysh_copy_to_running, + vtysh_copy_to_running_cmd, + "copy FILENAME running-config", + "Apply a configuration file\n" + "Configuration file to read\n" + "Apply to current configuration\n") +{ + int ret; + const char *fname = argv[1]->arg; + + ret = vtysh_read_config(fname); + + /* Return to enable mode - the 'read_config' api leaves us up a level */ + vtysh_execute_no_pager("enable"); + + return ret; +} + DEFUN (vtysh_terminal_paginate, vtysh_terminal_paginate_cmd, "[no] terminal paginate", @@ -3171,15 +3414,66 @@ DEFUN (no_vtysh_output_file, DEFUN(find, find_cmd, - "find COMMAND...", - "Find CLI command containing text\n" - "Text to search for\n") + "find REGEX", + "Find CLI command matching a regular expression\n" + "Search pattern (POSIX regex)\n") { - char *text = argv_concat(argv, argc, 1); + char *pattern = argv[1]->arg; const struct cmd_node *node; const struct cmd_element *cli; vector clis; + regex_t exp = {}; + + int cr = regcomp(&exp, pattern, REG_NOSUB | REG_EXTENDED); + + if (cr != 0) { + switch (cr) { + case REG_BADBR: + vty_out(vty, "%% Invalid \\{...\\} expression\n"); + break; + case REG_BADRPT: + vty_out(vty, "%% Bad repetition operator\n"); + break; + case REG_BADPAT: + vty_out(vty, "%% Regex syntax error\n"); + break; + case REG_ECOLLATE: + vty_out(vty, "%% Invalid collating element\n"); + break; + case REG_ECTYPE: + vty_out(vty, "%% Invalid character class name\n"); + break; + case REG_EESCAPE: + vty_out(vty, + "%% Regex ended with escape character (\\)\n"); + break; + case REG_ESUBREG: + vty_out(vty, + "%% Invalid number in \\digit construction\n"); + break; + case REG_EBRACK: + vty_out(vty, "%% Unbalanced square brackets\n"); + break; + case REG_EPAREN: + vty_out(vty, "%% Unbalanced parentheses\n"); + break; + case REG_EBRACE: + vty_out(vty, "%% Unbalanced braces\n"); + break; + case REG_ERANGE: + vty_out(vty, + "%% Invalid endpoint in range expression\n"); + break; + case REG_ESPACE: + vty_out(vty, "%% Failed to compile (out of memory)\n"); + break; + } + + goto done; + } + + for (unsigned int i = 0; i < vector_active(cmdvec); i++) { node = vector_slot(cmdvec, i); if (!node) @@ -3187,14 +3481,15 @@ DEFUN(find, clis = node->cmd_vector; for (unsigned int j = 0; j < vector_active(clis); j++) { cli = vector_slot(clis, j); - if (strcasestr(cli->string, text)) + + if (regexec(&exp, cli->string, 0, NULL, 0) == 0) vty_out(vty, " (%s) %s\n", - node_names[node->node], cli->string); + node->name, cli->string); } } - XFREE(MTYPE_TMP, text); - +done: + regfree(&exp); return CMD_SUCCESS; } @@ -3346,7 +3641,7 @@ static void vtysh_update_all_instances(struct vtysh_client *head_client) dir = opendir(vtydir); if (dir) { while ((file = readdir(dir)) != NULL) { - if (begins_with(file->d_name, "ospfd-") + if (frrstr_startswith(file->d_name, "ospfd-") && ends_with(file->d_name, ".vty")) { if (n == MAXIMUM_INSTANCES) { fprintf(stderr, @@ -3419,17 +3714,23 @@ static char *vtysh_completion_entry_function(const char *ignore, void vtysh_readline_init(void) { /* readline related settings. */ + char *disable_bracketed_paste = + XSTRDUP(MTYPE_TMP, "set enable-bracketed-paste off"); + rl_initialize(); + rl_parse_and_bind(disable_bracketed_paste); rl_bind_key('?', (rl_command_func_t *)vtysh_rl_describe); rl_completion_entry_function = vtysh_completion_entry_function; rl_attempted_completion_function = new_completion; + + XFREE(MTYPE_TMP, disable_bracketed_paste); } char *vtysh_prompt(void) { - static char buf[100]; + static char buf[512]; - snprintf(buf, sizeof buf, cmd_prompt(vty->node), cmd_hostname_get()); + snprintf(buf, sizeof(buf), cmd_prompt(vty->node), cmd_hostname_get()); return buf; } @@ -3488,54 +3789,55 @@ void vtysh_init_vty(void) cmd_variable_handler_register(vtysh_var_handler); /* Install nodes. */ - install_node(&bgp_node, NULL); - install_node(&rip_node, NULL); - install_node(&interface_node, NULL); - install_node(&pw_node, NULL); - install_node(&link_params_node, NULL); - install_node(&logicalrouter_node, NULL); - install_node(&vrf_node, NULL); - install_node(&nh_group_node, NULL); - install_node(&rmap_node, NULL); - install_node(&pbr_map_node, NULL); - install_node(&zebra_node, NULL); - install_node(&bgp_vpnv4_node, NULL); - install_node(&bgp_vpnv6_node, NULL); - install_node(&bgp_flowspecv4_node, NULL); - install_node(&bgp_flowspecv6_node, NULL); - install_node(&bgp_ipv4_node, NULL); - install_node(&bgp_ipv4m_node, NULL); - install_node(&bgp_ipv4l_node, NULL); - install_node(&bgp_ipv6_node, NULL); - install_node(&bgp_ipv6m_node, NULL); - install_node(&bgp_ipv6l_node, NULL); - install_node(&bgp_vrf_policy_node, NULL); - install_node(&bgp_evpn_node, NULL); - install_node(&bgp_evpn_vni_node, NULL); - install_node(&bgp_vnc_defaults_node, NULL); - install_node(&bgp_vnc_nve_group_node, NULL); - install_node(&bgp_vnc_l2_group_node, NULL); - install_node(&ospf_node, NULL); - install_node(&eigrp_node, NULL); - install_node(&babel_node, NULL); - install_node(&ripng_node, NULL); - install_node(&ospf6_node, NULL); - install_node(&ldp_node, NULL); - install_node(&ldp_ipv4_node, NULL); - install_node(&ldp_ipv6_node, NULL); - install_node(&ldp_ipv4_iface_node, NULL); - install_node(&ldp_ipv6_iface_node, NULL); - install_node(&ldp_l2vpn_node, NULL); - install_node(&ldp_pseudowire_node, NULL); - install_node(&keychain_node, NULL); - install_node(&keychain_key_node, NULL); - install_node(&isis_node, NULL); - install_node(&openfabric_node, NULL); - install_node(&vty_node, NULL); - install_node(&rpki_node, NULL); + 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_node(&bgp_vpnv4_node); + install_node(&bgp_vpnv6_node); + install_node(&bgp_flowspecv4_node); + install_node(&bgp_flowspecv6_node); + install_node(&bgp_ipv4_node); + install_node(&bgp_ipv4m_node); + install_node(&bgp_ipv4l_node); + install_node(&bgp_ipv6_node); + install_node(&bgp_ipv6m_node); + install_node(&bgp_ipv6l_node); + install_node(&bgp_vrf_policy_node); + install_node(&bgp_evpn_node); + install_node(&bgp_evpn_vni_node); + install_node(&bgp_vnc_defaults_node); + install_node(&bgp_vnc_nve_group_node); + 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, NULL); - install_node(&bfd_peer_node, NULL); + install_node(&bfd_node); + install_node(&bfd_peer_node); + install_node(&bfd_profile_node); #endif /* HAVE_BFDD */ struct cmd_node *node; @@ -3625,10 +3927,10 @@ void vtysh_init_vty(void) install_element(ISIS_NODE, &vtysh_quit_isisd_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(KEYCHAIN_NODE, &vtysh_exit_keys_cmd); + install_element(KEYCHAIN_NODE, &vtysh_quit_keys_cmd); + install_element(KEYCHAIN_KEY_NODE, &vtysh_exit_keys_cmd); + install_element(KEYCHAIN_KEY_NODE, &vtysh_quit_keys_cmd); install_element(RMAP_NODE, &vtysh_exit_rmap_cmd); install_element(RMAP_NODE, &vtysh_quit_rmap_cmd); install_element(PBRMAP_NODE, &vtysh_exit_pbr_map_cmd); @@ -3637,16 +3939,20 @@ void vtysh_init_vty(void) /* Enter 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_PEER_NODE, &vtysh_exit_bfdd_cmd); install_element(BFD_PEER_NODE, &vtysh_quit_bfdd_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); @@ -3703,13 +4009,6 @@ void vtysh_init_vty(void) install_element(PW_NODE, &vtysh_exit_interface_cmd); install_element(PW_NODE, &vtysh_quit_interface_cmd); - install_element(LOGICALROUTER_NODE, &vtysh_end_all_cmd); - - install_element(CONFIG_NODE, &vtysh_logicalrouter_cmd); - install_element(CONFIG_NODE, &vtysh_no_logicalrouter_cmd); - install_element(LOGICALROUTER_NODE, &vtysh_exit_logicalrouter_cmd); - install_element(LOGICALROUTER_NODE, &vtysh_quit_logicalrouter_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); @@ -3773,6 +4072,11 @@ void vtysh_init_vty(void) install_element(BGP_FLOWSPECV4_NODE, &exit_address_family_cmd); install_element(BGP_FLOWSPECV6_NODE, &exit_address_family_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); @@ -3800,6 +4104,7 @@ void vtysh_init_vty(void) 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); @@ -3842,18 +4147,27 @@ void vtysh_init_vty(void) #endif /* debugging */ - install_element(VIEW_NODE, &vtysh_show_debugging_cmd); install_element(VIEW_NODE, &vtysh_show_error_code_cmd); - install_element(VIEW_NODE, &vtysh_show_debugging_hashtable_cmd); + install_element(ENABLE_NODE, &vtysh_show_debugging_cmd); + install_element(ENABLE_NODE, &vtysh_show_debugging_hashtable_cmd); install_element(ENABLE_NODE, &vtysh_debug_all_cmd); install_element(CONFIG_NODE, &vtysh_debug_all_cmd); + install_element(ENABLE_NODE, &vtysh_debug_memstats_cmd); + install_element(CONFIG_NODE, &vtysh_debug_memstats_cmd); + + /* northbound */ + install_element(VIEW_NODE, &show_yang_operational_data_cmd); + install_element(ENABLE_NODE, &debug_nb_cmd); + install_element(CONFIG_NODE, &debug_nb_cmd); /* misc lib show commands */ install_element(VIEW_NODE, &vtysh_show_memory_cmd); install_element(VIEW_NODE, &vtysh_show_modules_cmd); install_element(VIEW_NODE, &vtysh_show_work_queues_cmd); install_element(VIEW_NODE, &vtysh_show_work_queues_daemon_cmd); +#ifndef EXCLUDE_CPU_TIME install_element(VIEW_NODE, &vtysh_show_thread_cmd); +#endif install_element(VIEW_NODE, &vtysh_show_poll_cmd); /* Logging */ diff --git a/vtysh/vtysh.h b/vtysh/vtysh.h index eb69a20b83..d2675a81b9 100644 --- a/vtysh/vtysh.h +++ b/vtysh/vtysh.h @@ -42,6 +42,7 @@ DECLARE_MGROUP(MVTYSH) #define VTYSH_STATICD 0x08000 #define VTYSH_BFDD 0x10000 #define VTYSH_FABRICD 0x20000 +#define VTYSH_VRRPD 0x40000 #define VTYSH_WAS_ACTIVE (-2) @@ -50,12 +51,14 @@ DECLARE_MGROUP(MVTYSH) /* watchfrr is not in ALL since library CLI functions should not be * run on it (logging & co. should stay in a fixed/frozen config, and * things like prefix lists are not even initialised) */ -#define VTYSH_ALL VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_LDPD|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_SHARPD|VTYSH_PBRD|VTYSH_STATICD|VTYSH_BFDD|VTYSH_FABRICD -#define VTYSH_RMAP VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_EIGRPD|VTYSH_SHARPD|VTYSH_FABRICD -#define VTYSH_INTERFACE VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_PBRD|VTYSH_FABRICD -#define VTYSH_NS VTYSH_ZEBRA +#define VTYSH_ALL VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_LDPD|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_SHARPD|VTYSH_PBRD|VTYSH_STATICD|VTYSH_BFDD|VTYSH_FABRICD|VTYSH_VRRPD +#define VTYSH_ACL VTYSH_BFDD|VTYSH_BABELD|VTYSH_BGPD|VTYSH_EIGRPD|VTYSH_ISISD|VTYSH_FABRICD|VTYSH_LDPD|VTYSH_NHRPD|VTYSH_OSPF6D|VTYSH_OSPFD|VTYSH_PBRD|VTYSH_PIMD|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_VRRPD|VTYSH_ZEBRA +#define VTYSH_RMAP VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_EIGRPD|VTYSH_FABRICD +#define VTYSH_INTERFACE VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_PBRD|VTYSH_FABRICD|VTYSH_VRRPD #define VTYSH_VRF VTYSH_ZEBRA|VTYSH_PIMD|VTYSH_STATICD #define VTYSH_KEYS VTYSH_RIPD|VTYSH_EIGRPD +/* Daemons who can process nexthop-group configs */ +#define VTYSH_NH_GROUP VTYSH_PBRD|VTYSH_SHARPD enum vtysh_write_integrated { WRITE_INTEGRATED_UNSPECIFIED, diff --git a/vtysh/vtysh_config.c b/vtysh/vtysh_config.c index 7ca3ed9c5e..f0416257d2 100644 --- a/vtysh/vtysh_config.c +++ b/vtysh/vtysh_config.c @@ -23,6 +23,7 @@ #include "command.h" #include "linklist.h" #include "memory.h" +#include "typesafe.h" #include "vtysh/vtysh.h" #include "vtysh/vtysh_user.h" @@ -33,6 +34,9 @@ DEFINE_MTYPE_STATIC(MVTYSH, VTYSH_CONFIG_LINE, "Vtysh configuration line") vector configvec; +PREDECL_LIST(config_master); +PREDECL_HASH(config_master_hash); + struct config { /* Configuration node name. */ char *name; @@ -45,6 +49,10 @@ struct config { /* Index of this config. */ uint32_t index; + + /* Node entry for the typed Red-black tree */ + struct config_master_item rbt_item; + struct config_master_hash_item hash_item; }; struct list *config_top; @@ -66,11 +74,6 @@ static struct config *config_new(void) return config; } -static int config_cmp(struct config *c1, struct config *c2) -{ - return strcmp(c1->name, c2->name); -} - static void config_del(struct config *config) { list_delete(&config->line); @@ -78,28 +81,49 @@ static void config_del(struct config *config) XFREE(MTYPE_VTYSH_CONFIG, config); } +static int config_cmp(const struct config *c1, const struct config *c2) +{ + return strcmp(c1->name, c2->name); +} + +static uint32_t config_hash(const struct config *c) +{ + return string_hash_make(c->name); +} + +DECLARE_LIST(config_master, struct config, rbt_item) +DECLARE_HASH(config_master_hash, struct config, hash_item, config_cmp, + config_hash) + +/* + * The config_master_head is a list for order of receipt + * The hash is for quick lookup under this NODE + */ +struct configuration { + struct config_master_head master; + struct config_master_hash_head hash_master; +}; + static struct config *config_get(int index, const char *line) { - struct config *config; - struct config *config_loop; - struct list *master; - struct listnode *node, *nnode; + struct config *config, *config_loop; + struct configuration *configuration; + struct config lookup; config = config_loop = NULL; - master = vector_lookup_ensure(configvec, index); + configuration = vector_lookup_ensure(configvec, index); - if (!master) { - master = list_new(); - master->del = (void (*)(void *))config_del; - master->cmp = (int (*)(void *, void *))config_cmp; - vector_set_index(configvec, index, master); + if (!configuration) { + configuration = XMALLOC(MTYPE_VTYSH_CONFIG, + sizeof(struct configuration)); + config_master_init(&configuration->master); + config_master_hash_init(&configuration->hash_master); + vector_set_index(configvec, index, configuration); } - for (ALL_LIST_ELEMENTS(master, node, nnode, config_loop)) { - if (strcmp(config_loop->name, line) == 0) - config = config_loop; - } + lookup.name = (char *)line; + config = config_master_hash_find(&configuration->hash_master, &lookup); if (!config) { config = config_new(); @@ -108,7 +132,8 @@ static struct config *config_get(int index, const char *line) config->line->cmp = (int (*)(void *, void *))line_cmp; config->name = XSTRDUP(MTYPE_VTYSH_CONFIG_LINE, line); config->index = index; - listnode_add(master, config); + config_master_add_tail(&configuration->master, config); + config_master_hash_add(&configuration->hash_master, config); } return config; } @@ -257,11 +282,17 @@ void vtysh_config_parse_line(void *arg, const char *line) strlen(" exit-vrf")) == 0) { config_add_line_uniq_end(config->line, line); + } else if (!strncmp(line, " vrrp", strlen(" vrrp")) + || !strncmp(line, " no vrrp", + strlen(" no vrrp"))) { + config_add_line(config->line, line); + } else if (!strncmp(line, " ip mroute", strlen(" ip mroute"))) { + config_add_line_uniq_end(config->line, line); } else if (config->index == RMAP_NODE || config->index == INTERFACE_NODE - || config->index == LOGICALROUTER_NODE || config->index == VTY_NODE - || config->index == VRF_NODE) + || config->index == VRF_NODE + || config->index == NH_GROUP_NODE) config_add_line_uniq(config->line, line); else config_add_line(config->line, line); @@ -273,8 +304,6 @@ void vtysh_config_parse_line(void *arg, const char *line) config = config_get(INTERFACE_NODE, line); else if (strncmp(line, "pseudowire", strlen("pseudowire")) == 0) config = config_get(PW_NODE, line); - else if (strncmp(line, "logical-router", strlen("logical-router")) == 0) - config = config_get(LOGICALROUTER_NODE, line); else if (strncmp(line, "vrf", strlen("vrf")) == 0) config = config_get(VRF_NODE, line); else if (strncmp(line, "nexthop-group", strlen("nexthop-group")) @@ -369,6 +398,13 @@ void vtysh_config_parse_line(void *arg, const char *line) strlen("debug northbound")) == 0) config = config_get(NORTHBOUND_DEBUG_NODE, line); + else if (strncmp(line, "debug route-map", + strlen("debug route-map")) + == 0) + config = config_get(RMAP_DEBUG_NODE, line); + else if (strncmp(line, "debug resolver", + strlen("debug resolver")) == 0) + config = config_get(RESOLVER_DEBUG_NODE, line); else if (strncmp(line, "debug", strlen("debug")) == 0) config = config_get(DEBUG_NODE, line); else if (strncmp(line, "password", strlen("password")) == 0 @@ -392,11 +428,13 @@ void vtysh_config_parse_line(void *arg, const char *line) config = config_get(BFD_NODE, line); else { if (strncmp(line, "log", strlen("log")) == 0 - || strncmp(line, "hostname", strlen("hostname")) - == 0 + || strncmp(line, "hostname", strlen("hostname")) == 0 + || strncmp(line, "domainname", strlen("domainname")) == 0 || strncmp(line, "frr", strlen("frr")) == 0 || strncmp(line, "agentx", strlen("agentx")) == 0 - || strncmp(line, "no log", strlen("no log")) == 0) + || strncmp(line, "no log", strlen("no log")) == 0 + || strncmp(line, "no ip prefix-list", strlen("no ip prefix-list")) == 0 + || strncmp(line, "no ipv6 prefix-list", strlen("no ipv6 prefix-list")) == 0) config_add_line_uniq(config_top, line); else config_add_line(config_top, line); @@ -414,7 +452,8 @@ void vtysh_config_parse_line(void *arg, const char *line) || (I) == ACCESS_IPV6_NODE || (I) == ACCESS_MAC_NODE \ || (I) == PREFIX_IPV6_NODE || (I) == FORWARDING_NODE \ || (I) == DEBUG_NODE || (I) == AAA_NODE || (I) == VRF_DEBUG_NODE \ - || (I) == NORTHBOUND_DEBUG_NODE || (I) == MPLS_NODE) + || (I) == NORTHBOUND_DEBUG_NODE || (I) == RMAP_DEBUG_NODE \ + || (I) == RESOLVER_DEBUG_NODE || (I) == MPLS_NODE) /* Display configuration to file pointer. */ void vtysh_config_dump(void) @@ -422,7 +461,7 @@ void vtysh_config_dump(void) struct listnode *node, *nnode; struct listnode *mnode, *mnnode; struct config *config; - struct list *master; + struct configuration *configuration; char *line; unsigned int i; @@ -432,8 +471,11 @@ void vtysh_config_dump(void) vty_out(vty, "!\n"); for (i = 0; i < vector_active(configvec); i++) - if ((master = vector_slot(configvec, i)) != NULL) { - for (ALL_LIST_ELEMENTS(master, node, nnode, config)) { + if ((configuration = vector_slot(configvec, i)) != NULL) { + while ((config = config_master_pop( + &configuration->master))) { + config_master_hash_del( + &configuration->hash_master, config); /* Don't print empty sections for interface. * Route maps on the * other hand could have a legitimate empty @@ -453,27 +495,31 @@ void vtysh_config_dump(void) vty_out(vty, "%s\n", line); if (!NO_DELIMITER(i)) vty_out(vty, "!\n"); + + config_del(config); } if (NO_DELIMITER(i)) vty_out(vty, "!\n"); } for (i = 0; i < vector_active(configvec); i++) - if ((master = vector_slot(configvec, i)) != NULL) { - list_delete(&master); + if ((configuration = vector_slot(configvec, i)) != NULL) { + config_master_fini(&configuration->master); + config_master_hash_fini(&configuration->hash_master); + XFREE(MTYPE_VTYSH_CONFIG, configuration); vector_slot(configvec, i) = NULL; } list_delete_all_node(config_top); } /* Read up configuration file from file_name. */ -static int vtysh_read_file(FILE *confp) +static int vtysh_read_file(FILE *confp, int output_fileno) { struct vty *vty; int ret; vty = vty_new(); - vty->wfd = STDERR_FILENO; + vty->wfd = output_fileno; vty->type = VTY_TERM; vty->node = CONFIG_NODE; @@ -497,16 +543,22 @@ int vtysh_read_config(const char *config_default_dir) FILE *confp = NULL; int ret; - confp = fopen(config_default_dir, "r"); + int is_real_file = strcmp(config_default_dir,"-") ; + if ( is_real_file == 0 ) { + confp = stdin ; + } + else { + confp = fopen(config_default_dir, "r"); + } if (confp == NULL) { fprintf(stderr, "%% Can't open configuration file %s due to '%s'.\n", config_default_dir, safe_strerror(errno)); - return (CMD_ERR_NO_FILE); + return CMD_ERR_NO_FILE; } - ret = vtysh_read_file(confp); - fclose(confp); + ret = vtysh_read_file(confp, is_real_file ? STDERR_FILENO : STDOUT_FILENO ); + if ( is_real_file ) fclose(confp); return (ret); } @@ -517,15 +569,16 @@ int vtysh_read_config(const char *config_default_dir) */ void vtysh_config_write(void) { - char line[81]; + char line[512]; if (cmd_hostname_get()) { - sprintf(line, "hostname %s", cmd_hostname_get()); + snprintf(line, sizeof(line), "hostname %s", cmd_hostname_get()); vtysh_config_parse_line(NULL, line); } if (cmd_domainname_get()) { - sprintf(line, "domainname %s", cmd_domainname_get()); + snprintf(line, sizeof(line), "domainname %s", + cmd_domainname_get()); vtysh_config_parse_line(NULL, line); } if (vtysh_write_integrated == WRITE_INTEGRATED_NO) diff --git a/vtysh/vtysh_main.c b/vtysh/vtysh_main.c index 2e4510a45a..8baa0d8d61 100644 --- a/vtysh/vtysh_main.c +++ b/vtysh/vtysh_main.c @@ -45,8 +45,9 @@ #include "command.h" #include "memory.h" #include "linklist.h" -#include "memory_vty.h" #include "libfrr.h" +#include "ferr.h" +#include "lib_errors.h" #include "vtysh/vtysh.h" #include "vtysh/vtysh_user.h" @@ -228,14 +229,15 @@ static char *vtysh_rl_gets(void) static void log_it(const char *line) { time_t t = time(NULL); - struct tm *tmp = localtime(&t); + struct tm tmp; const char *user = getenv("USER"); char tod[64]; + localtime_r(&t, &tmp); if (!user) user = "boot"; - strftime(tod, sizeof tod, "%Y%m%d-%H:%M.%S", tmp); + strftime(tod, sizeof(tod), "%Y%m%d-%H:%M.%S", &tmp); fprintf(logfile, "%s:%s %s\n", tod, user, line); } @@ -332,6 +334,8 @@ int main(int argc, char **argv, char **env) progname = ((p = strrchr(argv[0], '/')) ? ++p : argv[0]); strlcpy(sysconfdir, frr_sysconfdir, sizeof(sysconfdir)); + + frr_init_vtydir(); strlcpy(vtydir, frr_vtydir, sizeof(vtydir)); /* Option handling. */ @@ -417,14 +421,12 @@ int main(int argc, char **argv, char **env) if (markfile + writeconfig + dryrun + boot_flag > 1) { fprintf(stderr, - "Invalid combination of arguments. Please specify at " - "most one of:\n\t-b, -C, -m, -w\n"); + "Invalid combination of arguments. Please specify at most one of:\n\t-b, -C, -m, -w\n"); return 1; } if (inputfile && (writeconfig || boot_flag)) { fprintf(stderr, - "WARNING: Combinining the -f option with -b or -w is " - "NOT SUPPORTED since its\nresults are inconsistent!\n"); + "WARNING: Combinining the -f option with -b or -w is NOT SUPPORTED since its\nresults are inconsistent!\n"); } snprintf(vtysh_config, sizeof(vtysh_config), "%s%s%s", sysconfdir, @@ -459,12 +461,15 @@ int main(int argc, char **argv, char **env) vtysh_read_config(vtysh_config); suid_off(); } + /* Error code library system */ + log_ref_init(); + lib_error_init(); if (markfile) { if (!inputfile) { fprintf(stderr, "-f option MUST be specified with -m option\n"); - return (1); + return 1; } return (vtysh_mark_file(inputfile)); } @@ -528,6 +533,9 @@ int main(int argc, char **argv, char **env) /* Do not connect until we have passed authentication. */ if (vtysh_connect_all(daemon_name) <= 0) { fprintf(stderr, "Exiting: failed to connect to any daemons.\n"); + if (geteuid() != 0) + fprintf(stderr, + "Hint: if this seems wrong, try running me as a privileged user!\n"); if (no_error) exit(0); else @@ -551,9 +559,10 @@ int main(int argc, char **argv, char **env) } if (inputfile) { - vtysh_flock_config(inputfile); + int is_real_file = ( strcmp(inputfile, "-") ) ; + if ( is_real_file ) vtysh_flock_config(inputfile); ret = vtysh_read_config(inputfile); - vtysh_unflock_config(); + if ( is_real_file ) vtysh_unflock_config(); exit(ret); } diff --git a/vtysh/vtysh_user.c b/vtysh/vtysh_user.c index aaf70ab08b..665e6ca90d 100644 --- a/vtysh/vtysh_user.c +++ b/vtysh/vtysh_user.c @@ -62,35 +62,20 @@ static int vtysh_pam(const char *user) /* Start PAM. */ ret = pam_start(FRR_PAM_NAME, user, &conv, &pamh); - /* printf ("ret %d\n", ret); */ /* Is user really user? */ if (ret == PAM_SUCCESS) ret = pam_authenticate(pamh, 0); -/* printf ("ret %d\n", ret); */ -#if 0 - /* Permitted access? */ - if (ret == PAM_SUCCESS) - ret = pam_acct_mgmt (pamh, 0); - printf ("ret %d\n", ret); - - if (ret == PAM_AUTHINFO_UNAVAIL) - ret = PAM_SUCCESS; -#endif /* 0 */ - -/* This is where we have been authorized or not. */ -#ifdef DEBUG - if (ret == PAM_SUCCESS) - printf("Authenticated\n"); - else - printf("Not Authenticated\n"); -#endif /* DEBUG */ + if (ret != PAM_SUCCESS) + fprintf(stderr, "vtysh_pam: Failure to initialize pam: %s(%d)", + pam_strerror(pamh, ret), ret); /* close Linux-PAM */ if (pam_end(pamh, ret) != PAM_SUCCESS) { pamh = NULL; - fprintf(stderr, "vtysh_pam: failed to release authenticator\n"); + fprintf(stderr, "vtysh_pam: failed to release authenticator: %s(%d)\n", + pam_strerror(pamh, ret), ret); exit(1); } @@ -130,7 +115,8 @@ void user_config_write(void) for (ALL_LIST_ELEMENTS(userlist, node, nnode, user)) { if (user->nopassword) { - sprintf(line, "username %s nopassword", user->name); + snprintf(line, sizeof(line), "username %s nopassword", + user->name); config_add_line(config_top, line); } } @@ -162,6 +148,26 @@ DEFUN (vtysh_banner_motd_file, return cmd_banner_motd_file(argv[idx_file]->arg); } +DEFUN (vtysh_banner_motd_line, + vtysh_banner_motd_line_cmd, + "banner motd line LINE...", + "Set banner\n" + "Banner for motd\n" + "Banner from an input\n" + "Text\n") +{ + int idx = 0; + char *motd; + + argv_find(argv, argc, "LINE", &idx); + motd = argv_concat(argv, argc, idx); + + cmd_banner_motd_line(motd); + XFREE(MTYPE_TMP, motd); + + return CMD_SUCCESS; +} + DEFUN (username_nopassword, username_nopassword_cmd, "username WORD nopassword", @@ -218,4 +224,5 @@ void vtysh_user_init(void) userlist = list_new(); install_element(CONFIG_NODE, &username_nopassword_cmd); install_element(CONFIG_NODE, &vtysh_banner_motd_file_cmd); + install_element(CONFIG_NODE, &vtysh_banner_motd_line_cmd); } diff --git a/watchfrr/subdir.am b/watchfrr/subdir.am index c27491e55c..677f85efcb 100644 --- a/watchfrr/subdir.am +++ b/watchfrr/subdir.am @@ -4,8 +4,8 @@ if WATCHFRR sbin_PROGRAMS += watchfrr/watchfrr -vtysh_scan += $(top_srcdir)/watchfrr/watchfrr_vty.c -man8 += $(MANBUILD)/watchfrr.8 +vtysh_scan += watchfrr/watchfrr_vty.c +man8 += $(MANBUILD)/frr-watchfrr.8 endif noinst_HEADERS += \ @@ -19,3 +19,7 @@ watchfrr_watchfrr_SOURCES = \ watchfrr/watchfrr_errors.c \ watchfrr/watchfrr_vty.c \ # end + +clippy_scan += \ + watchfrr/watchfrr_vty.c \ + # end diff --git a/watchfrr/watchfrr.c b/watchfrr/watchfrr.c index 34f8dabdf1..34ef1a45a6 100644 --- a/watchfrr/watchfrr.c +++ b/watchfrr/watchfrr.c @@ -25,9 +25,11 @@ #include #include #include "command.h" -#include "memory_vty.h" #include "libfrr.h" #include "lib_errors.h" +#include "zlog_targets.h" +#include "network.h" +#include "printfrr.h" #include #include @@ -43,7 +45,7 @@ #endif /* Macros to help randomize timers. */ -#define JITTER(X) ((random() % ((X)+1))-((X)/2)) +#define JITTER(X) ((frr_weak_random() % ((X)+1))-((X)/2)) #define FUZZY(X) ((X)+JITTER((X)/20)) #define DEFAULT_PERIOD 5 @@ -76,7 +78,7 @@ typedef enum { PHASE_WAITING_ZEBRA_UP } restart_phase_t; -static const char *phase_str[] = { +static const char *const phase_str[] = { "Idle", "Startup", "Stop jobs running", @@ -144,7 +146,7 @@ typedef enum { #define IS_UP(DMN) \ (((DMN)->state == DAEMON_UP) || ((DMN)->state == DAEMON_UNRESPONSIVE)) -static const char *state_str[] = { +static const char *const state_str[] = { "Init", "Down", "Connecting", "Up", "Unresponsive", }; @@ -159,11 +161,21 @@ struct daemon { struct thread *t_write; struct daemon *next; struct restart_info restart; + + /* + * For a given daemon, if we've turned on ignore timeouts + * ignore the timeout value and assume everything is ok + * This is for daemon debugging w/ gdb after we have started + * FRR and realize we have something that needs to be looked + * at + */ + bool ignore_timeout; }; #define OPTION_MINRESTART 2000 #define OPTION_MAXRESTART 2001 #define OPTION_DRY 2002 +#define OPTION_NETNS 2003 static const struct option longopts[] = { {"daemon", no_argument, NULL, 'd'}, @@ -180,6 +192,9 @@ static const struct option longopts[] = { {"max-restart-interval", required_argument, NULL, OPTION_MAXRESTART}, {"pid-file", required_argument, NULL, 'p'}, {"blank-string", required_argument, NULL, 'b'}, +#ifdef GNU_LINUX + {"netns", optional_argument, NULL, OPTION_NETNS}, +#endif {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'v'}, {NULL, 0, NULL, 0}}; @@ -191,6 +206,25 @@ static void phase_check(void); static void restart_done(struct daemon *dmn); static const char *progname; + +void watchfrr_set_ignore_daemon(struct vty *vty, const char *dname, bool ignore) +{ + struct daemon *dmn; + + for (dmn = gs.daemons; dmn; dmn = dmn->next) { + if (strncmp(dmn->name, dname, strlen(dmn->name)) == 0) + break; + } + + if (dmn) { + dmn->ignore_timeout = ignore; + vty_out(vty, "%s switching to %s\n", dmn->name, + ignore ? "ignore" : "watch"); + } else + vty_out(vty, "%s is not configured for running at the moment", + dname); +} + static void printhelp(FILE *target) { fprintf(target, @@ -215,7 +249,12 @@ Otherwise, the interval is doubled (but capped at the -M value).\n\n", -d, --daemon Run in daemon mode. In this mode, error messages are sent\n\ to syslog instead of stdout.\n\ -S, --statedir Set the vty socket directory (default is %s)\n\ --l, --loglevel Set the logging level (default is %d).\n\ +-N, --pathspace Insert prefix into config & socket paths\n" +#ifdef GNU_LINUX +" --netns Create and/or use Linux network namespace. If no name is\n" +" given, uses the value from `-N`.\n" +#endif +"-l, --loglevel Set the logging level (default is %d).\n\ The value should range from %d (LOG_EMERG) to %d (LOG_DEBUG),\n\ but it can be set higher than %d if extra-verbose debugging\n\ messages are desired.\n\ @@ -291,9 +330,8 @@ static pid_t run_background(char *shell_cmd) } default: /* Parent process: we will reap the child later. */ - flog_err_sys(EC_LIB_SYSTEM_CALL, - "Forked background command [pid %d]: %s", - (int)child, shell_cmd); + zlog_info("Forked background command [pid %d]: %s", (int)child, + shell_cmd); return child; } } @@ -318,8 +356,7 @@ static int restart_kill(struct thread *t_kill) time_elapsed(&delay, &restart->time); zlog_warn( - "Warning: %s %s child process %d still running after " - "%ld seconds, sending signal %d", + "Warning: %s %s child process %d still running after %ld seconds, sending signal %d", restart->what, restart->name, (int)restart->pid, (long)delay.tv_sec, (restart->kills ? SIGKILL : SIGTERM)); kill(-restart->pid, (restart->kills ? SIGKILL : SIGTERM)); @@ -432,16 +469,23 @@ static int run_job(struct restart_info *restart, const char *cmdtype, return -1; } +#if defined HAVE_SYSTEMD + char buffer[512]; + + snprintf(buffer, sizeof(buffer), "restarting %s", restart->name); + systemd_send_status(buffer); +#endif + /* Note: time_elapsed test must come before the force test, since we need to make sure that delay is initialized for use below in updating the restart interval. */ if ((time_elapsed(&delay, &restart->time)->tv_sec < restart->interval) && !force) { + if (gs.loglevel > LOG_DEBUG + 1) zlog_debug( - "postponing %s %s: " - "elapsed time %ld < retry interval %ld", + "postponing %s %s: elapsed time %ld < retry interval %ld", cmdtype, restart->name, (long)delay.tv_sec, restart->interval); return -1; @@ -462,6 +506,9 @@ static int run_job(struct restart_info *restart, const char *cmdtype, restart->pid = 0; } +#if defined HAVE_SYSTEMD + systemd_send_status("FRR Operational"); +#endif /* Calculate the new restart interval. */ if (update_interval) { if (delay.tv_sec > 2 * gs.max_restart_interval) @@ -521,9 +568,9 @@ static int wakeup_init(struct thread *t_wakeup) dmn->t_wakeup = NULL; if (try_connect(dmn) < 0) { - flog_err(EC_WATCHFRR_CONNECTION, - "%s state -> down : initial connection attempt failed", - dmn->name); + zlog_info( + "%s state -> down : initial connection attempt failed", + dmn->name); dmn->state = DAEMON_DOWN; } phase_check(); @@ -533,7 +580,9 @@ static int wakeup_init(struct thread *t_wakeup) static void restart_done(struct daemon *dmn) { if (dmn->state != DAEMON_DOWN) { - zlog_warn("wtf?"); + zlog_warn( + "Daemon: %s: is in %s state but expected it to be in DAEMON_DOWN state", + dmn->name, state_str[dmn->state]); return; } if (dmn->t_wakeup) @@ -605,8 +654,7 @@ static int handle_read(struct thread *t_read) if ((rc != sizeof(resp)) || memcmp(buf, resp, sizeof(resp))) { char why[100 + sizeof(buf)]; snprintf(why, sizeof(why), - "read returned bad echo response of %d bytes " - "(expecting %u): %.*s", + "read returned bad echo response of %d bytes (expecting %u): %.*s", (int)rc, (unsigned int)sizeof(resp), (int)rc, buf); daemon_down(dmn, why); return 0; @@ -618,14 +666,12 @@ static int handle_read(struct thread *t_read) if (delay.tv_sec < gs.timeout) { dmn->state = DAEMON_UP; zlog_warn( - "%s state -> up : echo response received after %ld.%06ld " - "seconds", + "%s state -> up : echo response received after %ld.%06ld seconds", dmn->name, (long)delay.tv_sec, (long)delay.tv_usec); } else zlog_warn( - "%s: slow echo response finally received after %ld.%06ld " - "seconds", + "%s: slow echo response finally received after %ld.%06ld seconds", dmn->name, (long)delay.tv_sec, (long)delay.tv_usec); } else if (gs.loglevel > LOG_DEBUG + 1) @@ -648,6 +694,7 @@ static void daemon_send_ready(int exitcode) { FILE *fp; static int sent = 0; + char started[1024]; if (sent) return; @@ -656,24 +703,25 @@ static void daemon_send_ready(int exitcode) zlog_notice("all daemons up, doing startup-complete notify"); else if (gs.numdown < gs.numdaemons) flog_err(EC_WATCHFRR_CONNECTION, - "startup did not complete within timeout" - " (%d/%d daemons running)", + "startup did not complete within timeout (%d/%d daemons running)", gs.numdaemons - gs.numdown, gs.numdaemons); else { flog_err(EC_WATCHFRR_CONNECTION, - "all configured daemons failed to start" - " -- exiting watchfrr"); + "all configured daemons failed to start -- exiting watchfrr"); exit(exitcode); } frr_detach(); - fp = fopen(DAEMON_VTY_DIR "/watchfrr.started", "w"); + snprintf(started, sizeof(started), "%s/%s", frr_vtydir, + "watchfrr.started"); + fp = fopen(started, "w"); if (fp) fclose(fp); #if defined HAVE_SYSTEMD systemd_send_started(master, 0); + systemd_send_status("FRR Operational"); #endif sent = 1; } @@ -903,8 +951,7 @@ static void try_restart(struct daemon *dmn) 1); else zlog_debug( - "%s: postponing restart attempt because master %s daemon " - "not up [%s], or phased restart in progress", + "%s: postponing restart attempt because master %s daemon not up [%s], or phased restart in progress", dmn->name, gs.special->name, state_str[gs.special->state]); return; @@ -913,8 +960,7 @@ static void try_restart(struct daemon *dmn) if ((gs.phase != PHASE_NONE) || gs.numpids) { if (gs.loglevel > LOG_DEBUG + 1) zlog_debug( - "postponing phased global restart: restart already in " - "progress [%s], or outstanding child processes [%d]", + "postponing phased global restart: restart already in progress [%s], or outstanding child processes [%d]", phase_str[gs.phase], gs.numpids); return; } @@ -925,8 +971,7 @@ static void try_restart(struct daemon *dmn) < gs.special->restart.interval) { if (gs.loglevel > LOG_DEBUG + 1) zlog_debug( - "postponing phased global restart: " - "elapsed time %ld < retry interval %ld", + "postponing phased global restart: elapsed time %ld < retry interval %ld", (long)delay.tv_sec, gs.special->restart.interval); return; @@ -942,8 +987,7 @@ static int wakeup_unresponsive(struct thread *t_wakeup) dmn->t_wakeup = NULL; if (dmn->state != DAEMON_UNRESPONSIVE) flog_err(EC_WATCHFRR_CONNECTION, - "%s: no longer unresponsive (now %s), " - "wakeup should have been cancelled!", + "%s: no longer unresponsive (now %s), wakeup should have been cancelled!", dmn->name, state_str[dmn->state]); else { SET_WAKEUP_UNRESPONSIVE(dmn); @@ -958,9 +1002,10 @@ static int wakeup_no_answer(struct thread *t_wakeup) dmn->t_wakeup = NULL; dmn->state = DAEMON_UNRESPONSIVE; + if (dmn->ignore_timeout) + return 0; flog_err(EC_WATCHFRR_CONNECTION, - "%s state -> unresponsive : no response yet to ping " - "sent %ld seconds ago", + "%s state -> unresponsive : no response yet to ping sent %ld seconds ago", dmn->name, gs.timeout); SET_WAKEUP_UNRESPONSIVE(dmn); try_restart(dmn); @@ -1011,17 +1056,18 @@ void watchfrr_status(struct vty *vty) (long)gs.restart.pid); for (dmn = gs.daemons; dmn; dmn = dmn->next) { - vty_out(vty, " %-20s %s\n", dmn->name, state_str[dmn->state]); + vty_out(vty, " %-20s %s%s", dmn->name, state_str[dmn->state], + dmn->ignore_timeout ? "/Ignoring Timeout\n" : "\n"); if (dmn->restart.pid) vty_out(vty, " restart running, pid %ld\n", (long)dmn->restart.pid); else if (dmn->state == DAEMON_DOWN && time_elapsed(&delay, &dmn->restart.time)->tv_sec < dmn->restart.interval) - vty_out(vty, " restarting in %ld seconds" - " (%lds backoff interval)\n", - dmn->restart.interval - delay.tv_sec, - dmn->restart.interval); + vty_out(vty, " restarting in %jd seconds (%jds backoff interval)\n", + (intmax_t)dmn->restart.interval + - (intmax_t)delay.tv_sec, + (intmax_t)dmn->restart.interval); } } @@ -1036,6 +1082,9 @@ static int valid_command(const char *cmd) { char *p; + if (cmd == NULL) + return 0; + return ((p = strchr(cmd, '%')) != NULL) && (*(p + 1) == 's') && !strchr(p + 1, '%'); } @@ -1066,6 +1115,148 @@ static int startup_timeout(struct thread *t_wakeup) return 0; } +#ifdef GNU_LINUX + +#include +#include + +#define NETNS_RUN_DIR "/var/run/netns" + +static void netns_create(int dirfd, const char *nsname) +{ + /* make /var/run/netns shared between mount namespaces + * just like iproute2 sets it up + */ + if (mount("", NETNS_RUN_DIR, "none", MS_SHARED | MS_REC, NULL)) { + if (errno != EINVAL) { + perror("mount"); + exit(1); + } + + if (mount(NETNS_RUN_DIR, NETNS_RUN_DIR, "none", + MS_BIND | MS_REC, NULL)) { + perror("mount"); + exit(1); + } + + if (mount("", NETNS_RUN_DIR, "none", MS_SHARED | MS_REC, + NULL)) { + perror("mount"); + exit(1); + } + } + + /* need an empty file to mount on top of */ + int nsfd = openat(dirfd, nsname, O_CREAT | O_RDONLY | O_EXCL, 0); + + if (nsfd < 0) { + fprintf(stderr, "failed to create \"%s/%s\": %s\n", + NETNS_RUN_DIR, nsname, strerror(errno)); + exit(1); + } + close(nsfd); + + if (unshare(CLONE_NEWNET)) { + perror("unshare"); + unlinkat(dirfd, nsname, 0); + exit(1); + } + + char *dstpath = asprintfrr(MTYPE_TMP, "%s/%s", NETNS_RUN_DIR, nsname); + + /* bind-mount so the namespace has a name and is persistent */ + if (mount("/proc/self/ns/net", dstpath, "none", MS_BIND, NULL) < 0) { + fprintf(stderr, "failed to bind-mount netns to \"%s\": %s\n", + dstpath, strerror(errno)); + unlinkat(dirfd, nsname, 0); + exit(1); + } + + XFREE(MTYPE_TMP, dstpath); +} + +static void netns_setup(const char *nsname) +{ + int dirfd, nsfd; + + dirfd = open(NETNS_RUN_DIR, O_DIRECTORY | O_RDONLY); + if (dirfd < 0) { + if (errno == ENOTDIR) { + fprintf(stderr, "error: \"%s\" is not a directory!\n", + NETNS_RUN_DIR); + exit(1); + } else if (errno == ENOENT) { + if (mkdir(NETNS_RUN_DIR, 0755)) { + fprintf(stderr, "error: \"%s\": mkdir: %s\n", + NETNS_RUN_DIR, strerror(errno)); + exit(1); + } + dirfd = open(NETNS_RUN_DIR, O_DIRECTORY | O_RDONLY); + if (dirfd < 0) { + fprintf(stderr, "error: \"%s\": opendir: %s\n", + NETNS_RUN_DIR, strerror(errno)); + exit(1); + } + } else { + fprintf(stderr, "error: \"%s\": %s\n", + NETNS_RUN_DIR, strerror(errno)); + exit(1); + } + } + + nsfd = openat(dirfd, nsname, O_RDONLY); + if (nsfd < 0 && errno != ENOENT) { + fprintf(stderr, "error: \"%s/%s\": %s\n", + NETNS_RUN_DIR, nsname, strerror(errno)); + exit(1); + } + if (nsfd < 0) + netns_create(dirfd, nsname); + else { + if (setns(nsfd, CLONE_NEWNET)) { + perror("setns"); + exit(1); + } + close(nsfd); + } + close(dirfd); + + /* make sure loopback is up... weird things happen otherwise. + * ioctl is perfectly fine for this, don't need netlink... + */ + int sockfd; + struct ifreq ifr = { }; + + strlcpy(ifr.ifr_name, "lo", sizeof(ifr.ifr_name)); + + sockfd = socket(AF_INET, SOCK_DGRAM, 0); + if (sockfd < 0) { + perror("socket"); + exit(1); + } + if (ioctl(sockfd, SIOCGIFFLAGS, &ifr)) { + perror("ioctl(SIOCGIFFLAGS, \"lo\")"); + exit(1); + } + if (!(ifr.ifr_flags & IFF_UP)) { + ifr.ifr_flags |= IFF_UP; + if (ioctl(sockfd, SIOCSIFFLAGS, &ifr)) { + perror("ioctl(SIOCSIFFLAGS, \"lo\")"); + exit(1); + } + } + close(sockfd); +} + +#else /* !GNU_LINUX */ + +static void netns_setup(const char *nsname) +{ + fprintf(stderr, "network namespaces are only available on Linux\n"); + exit(1); +} +#endif + static void watchfrr_init(int argc, char **argv) { const char *special = "zebra"; @@ -1155,11 +1346,13 @@ int main(int argc, char **argv) { int opt; const char *blankstr = NULL; + const char *netns = NULL; + bool netns_en = false; frr_preinit(&watchfrr_di, argc, argv); progname = watchfrr_di.progname; - frr_opt_add("b:dk:l:i:p:r:S:s:t:T:" DEPRECATED_OPTIONS, longopts, ""); + frr_opt_add("b:di:k:l:N:p:r:S:s:t:T:" DEPRECATED_OPTIONS, longopts, ""); gs.restart.name = "all"; while ((opt = frr_getopt(argc, argv, NULL)) != EOF) { @@ -1224,6 +1417,16 @@ int main(int argc, char **argv) frr_help_exit(1); } } break; + case OPTION_NETNS: + netns_en = true; + if (optarg && strchr(optarg, '/')) { + fprintf(stderr, + "invalid network namespace name \"%s\" (may not contain slashes)\n", + optarg); + frr_help_exit(1); + } + netns = optarg; + break; case 'i': { char garbage[3]; int period; @@ -1315,6 +1518,17 @@ int main(int argc, char **argv) gs.restart.interval = gs.min_restart_interval; + /* env variable for the processes that we start */ + if (watchfrr_di.pathspace) + setenv("FRR_PATHSPACE", watchfrr_di.pathspace, 1); + else + unsetenv("FRR_PATHSPACE"); + + if (netns_en && !netns) + netns = watchfrr_di.pathspace; + if (netns_en && netns && netns[0]) + netns_setup(netns); + master = frr_init(); watchfrr_error_init(); watchfrr_init(argc, argv); @@ -1322,11 +1536,10 @@ int main(int argc, char **argv) frr_config_fork(); - zlog_set_level(ZLOG_DEST_MONITOR, ZLOG_DISABLED); if (watchfrr_di.daemon_mode) - zlog_set_level(ZLOG_DEST_SYSLOG, MIN(gs.loglevel, LOG_DEBUG)); + zlog_syslog_set_prio_min(MIN(gs.loglevel, LOG_DEBUG)); else - zlog_set_level(ZLOG_DEST_STDOUT, MIN(gs.loglevel, LOG_DEBUG)); + zlog_aux_init(NULL, MIN(gs.loglevel, LOG_DEBUG)); frr_run(master); diff --git a/watchfrr/watchfrr.h b/watchfrr/watchfrr.h index c5f54769bd..ba6e94960f 100644 --- a/watchfrr/watchfrr.h +++ b/watchfrr/watchfrr.h @@ -41,4 +41,6 @@ extern void watchfrr_status(struct vty *vty); */ extern bool check_all_up(void); +extern void watchfrr_set_ignore_daemon(struct vty *vty, const char *dname, + bool ignore); #endif /* FRR_WATCHFRR_H */ diff --git a/watchfrr/watchfrr_vty.c b/watchfrr/watchfrr_vty.c index 9b844d67f2..eda4f5d516 100644 --- a/watchfrr/watchfrr_vty.c +++ b/watchfrr/watchfrr_vty.c @@ -23,6 +23,7 @@ #include "memory.h" #include "log.h" +#include "log_vty.h" #include "vty.h" #include "command.h" @@ -134,6 +135,36 @@ DEFUN (show_watchfrr, return CMD_SUCCESS; } +/* we don't have the other logging commands since watchfrr only accepts + * log config through command line options + */ +DEFUN_NOSH (show_logging, + show_logging_cmd, + "show logging", + SHOW_STR + "Show current logging configuration\n") +{ + log_show_syslog(vty); + return CMD_SUCCESS; +} + +#ifndef VTYSH_EXTRACT_PL +#include "watchfrr/watchfrr_vty_clippy.c" +#endif + +DEFPY (watchfrr_ignore_daemon, + watchfrr_ignore_daemon_cmd, + "[no] watchfrr ignore DAEMON$dname", + NO_STR + "Watchfrr Specific sub-command\n" + "Ignore a specified daemon when it does not respond to echo request\n" + "The daemon to ignore\n") +{ + watchfrr_set_ignore_daemon(vty, dname, no ? false : true ); + + return CMD_SUCCESS; +} + void integrated_write_sigchld(int status) { uint8_t reply[4] = {0, 0, 0, CMD_WARNING}; @@ -168,6 +199,10 @@ void watchfrr_vty_init(void) integrated_write_pid = -1; install_element(ENABLE_NODE, &config_write_integrated_cmd); install_element(ENABLE_NODE, &show_debugging_watchfrr_cmd); + + install_element(ENABLE_NODE, &watchfrr_ignore_daemon_cmd); + install_element(CONFIG_NODE, &show_debugging_watchfrr_cmd); install_element(VIEW_NODE, &show_watchfrr_cmd); + install_element(VIEW_NODE, &show_logging_cmd); } diff --git a/yang/LICENSE b/yang/LICENSE new file mode 100644 index 0000000000..a7cec5de91 --- /dev/null +++ b/yang/LICENSE @@ -0,0 +1,22 @@ +Copyright 2020 FRRouting + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/yang/embedmodel.py b/yang/embedmodel.py index 52671f99a8..0a25c93da7 100644 --- a/yang/embedmodel.py +++ b/yang/embedmodel.py @@ -3,16 +3,25 @@ # YANG module to C wrapper # written 2018 by David Lamparter, placed in Public Domain. -import sys, string, re +import sys +import os +import string +import re inname = sys.argv[1] outname = sys.argv[2] +outdir = os.path.dirname(os.path.abspath(outname)) +if not os.path.isdir(outdir): + os.makedirs(outdir) + # these are regexes to avoid a compile-time/host dependency on yang-tools # or python-yang. Cross-compiling FRR is already somewhat involved, no need # to make it even harder. re_name = re.compile(r'\bmodule\s+([^\s]+)\s+\{') +re_subname = re.compile(r'\bsubmodule\s+([^\s]+)\s+\{') +re_mainname = re.compile(r'\bbelongs-to\s+([^\s]+)\s+\{') re_rev = re.compile(r'\brevision\s+([\d-]+)\s+\{') @@ -27,6 +36,8 @@ static struct yang_module_embed embed = { \t.mod_name = "%s", \t.mod_rev = "%s", +\t.sub_mod_name = "%s", +\t.sub_mod_rev = "%s", \t.data = model, \t.format = %s, }; @@ -55,6 +66,10 @@ def escape(line): with open(inname, 'r') as fd: data = fd.read() +sub_name = "" +rev = "" +sub_rev = "" + # XML support isn't actively used currently, but it's here in case the need # arises. It does avoid the regex'ing. if ' + FRR Development List: "; + description + "This module defines a model for managing FRR bfdd daemon. + + Copyright 2020 FRRouting + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."; + + revision 2019-05-09 { + description "Initial revision."; + reference + "RFC 5880: Bidirectional Forwarding Detection (BFD). + RFC 5881: Bidirectional Forwarding Detection (BFD) + for IPv4 and IPv6 (Single Hop). + RFC 5883: Bidirectional Forwarding Detection (BFD) for Multihop Paths."; + } + + + /* + * BFD types declaration. + */ + typedef multiplier { + description "Detection multiplier"; + type uint8 { + range 2..255; + } + } + + typedef discriminator { + description "BFD session identification"; + type uint32 { + range 1..4294967295; + } + } + + typedef state { + description "BFD session state"; + type enumeration { + enum admin-down { + value 0; + description "Administratively down"; + } + enum down { + value 1; + description "Down"; + } + enum init { + value 2; + description "Initializing"; + } + enum up { + value 3; + description "Up"; + } + } + } + + typedef diagnostic { + description "BFD session diagnostic"; + type enumeration { + enum ok { + value 0; + description "Ok"; + } + enum control-expired { + value 1; + description "Control timer expired"; + } + enum echo-failed { + value 2; + description "Echo function failed"; + } + enum neighbor-down { + value 3; + description "Neighbor signaled session down"; + } + enum forwarding-reset { + value 4; + description "Forwarding plane reset"; + } + enum path-down { + value 5; + description "Path down"; + } + enum concatenated-path-down { + value 6; + description "Concatenated path down"; + } + enum administratively-down { + value 7; + description "Administratively down"; + } + enum reverse-concat-path-down { + value 8; + description "Reverse concatenated path down"; + } + } + } + + typedef profile-name { + type string { + length "1..64"; + } + description "Profile name format"; + } + + typedef profile-ref { + type leafref { + path "/frr-bfdd:bfdd/frr-bfdd:bfd/frr-bfdd:profile/frr-bfdd:name"; + require-instance false; + } + description "Reference to a BFD profile"; + } + + /* + * Shared BFD items. + */ + grouping session-common { + description "Common BFD session settings"; + + leaf detection-multiplier { + type multiplier; + default 3; + description "Local session detection multiplier"; + } + + leaf desired-transmission-interval { + type uint32; + units microseconds; + default 300000; + description "Minimum desired control packet transmission interval"; + } + + leaf required-receive-interval { + type uint32; + units microseconds; + default 300000; + description "Minimum required control packet receive interval"; + } + + leaf administrative-down { + type boolean; + default false; + description "Disables or enables the session administratively"; + } + + leaf passive-mode { + type boolean; + default false; + description + "Don't attempt to start session establishment."; + } + } + + grouping session-echo { + description "BFD session echo settings"; + + leaf echo-mode { + type boolean; + default false; + description "Use echo packets to detect failures"; + } + + leaf desired-echo-transmission-interval { + type uint32; + units microseconds; + default 50000; + description "Minimum desired control packet transmission interval"; + } + } + + grouping session-multi-hop { + description "BFD session multi hop settings."; + + leaf minimum-ttl { + type uint8 { + range 1..254; + } + description + "Minimum expected TTL on received packets."; + } + } + + grouping session-states { + /* + * Local settings. + */ + leaf local-discriminator { + type discriminator; + description "Local session identifier"; + } + + leaf local-state { + type state; + description "Local session state"; + } + + leaf local-diagnostic { + type diagnostic; + description "Local session diagnostic"; + } + + leaf local-multiplier { + type multiplier; + description "Local session current multiplier"; + } + + /* + * Remote settings. + */ + leaf remote-discriminator { + type discriminator; + description "Remote session identifier"; + } + + leaf remote-state { + type state; + description "Remote session state"; + } + + leaf remote-diagnostic { + type diagnostic; + description "Local session diagnostic"; + } + + leaf remote-multiplier { + type multiplier; + description "Remote session detection multiplier"; + } + + /* + * Negotiated settings. + */ + leaf negotiated-transmission-interval { + description "Negotiated transmit interval"; + type uint32; + units microseconds; + } + + leaf negotiated-receive-interval { + description "Negotiated receive interval"; + type uint32; + units microseconds; + } + + leaf detection-mode { + description "Detection mode"; + + type enumeration { + enum async-with-echo { + value "1"; + description "Async with echo"; + } + enum async-without-echo { + value "2"; + description "Async without echo"; + } + enum demand-with-echo { + value "3"; + description "Demand with echo"; + } + enum demand-without-echo { + value "4"; + description "Demand without echo"; + } + } + } + + /* + * Statistics. + */ + leaf last-down-time { + type yang:date-and-time; + description "Time and date of the last time session was down"; + } + + leaf last-up-time { + type yang:date-and-time; + description "Time and date of the last time session was up"; + } + + leaf session-down-count { + type uint32; + description "Number of times the session went down"; + } + + leaf session-up-count { + type uint32; + description "Number of times the session went up"; + } + + leaf control-packet-input-count { + type uint64; + description "Number of control packets received"; + } + + leaf control-packet-output-count { + type uint64; + description "Number of control packets sent"; + } + + /* + * Echo mode operational data. + */ + leaf negotiated-echo-transmission-interval { + type uint32; + units microseconds; + description "Negotiated echo transmit interval"; + } + + /* + * Statistics. + */ + leaf echo-packet-input-count { + type uint64; + description "Number of echo packets received"; + } + + leaf echo-packet-output-count { + type uint64; + description "Number of echo packets sent"; + } + } + + /* + * BFD operational. + */ + container bfdd { + container bfd { + presence "Present if the BFD protocol is enabled"; + + list profile { + key "name"; + description "BFD pre configuration profiles"; + + leaf name { + type profile-name; + description "Profile name"; + } + + uses session-common; + uses session-echo; + uses session-multi-hop; + } + + container sessions { + list single-hop { + key "dest-addr interface vrf"; + description "List of single hop sessions"; + + leaf dest-addr { + type inet:ip-address; + description "IP address of the peer"; + } + + leaf interface { + type string { + length "0..16"; + } + description "Interface to use to contact peer"; + } + + leaf vrf { + type string; + description "Virtual Routing Domain name"; + } + + leaf source-addr { + type inet:ip-address; + description "Local IP address"; + } + + leaf profile { + type profile-ref; + description "Override defaults with profile."; + } + + uses session-common; + uses session-echo; + + container stats { + uses session-states; + config false; + } + } + + list multi-hop { + key "source-addr dest-addr interface vrf"; + description "List of multi hop sessions"; + + leaf source-addr { + type inet:ip-address; + description "Local IP address"; + } + + leaf dest-addr { + type inet:ip-address; + description "IP address of the peer"; + } + + leaf interface { + type string { + length "0..16"; + } + description "Interface to use to contact peer"; + } + + leaf vrf { + type string; + description "Virtual Routing Domain name"; + } + + leaf profile { + type profile-ref; + description "Override defaults with profile."; + } + + uses session-common; + uses session-multi-hop; + + container stats { + uses session-states; + config false; + } + } + } + } + } +} diff --git a/yang/frr-bgp-bmp.yang b/yang/frr-bgp-bmp.yang new file mode 100644 index 0000000000..344448f104 --- /dev/null +++ b/yang/frr-bgp-bmp.yang @@ -0,0 +1,203 @@ +submodule frr-bgp-bmp { + yang-version 1.1; + + belongs-to frr-bgp { + prefix "bgp"; + } + + import ietf-inet-types { + prefix inet; + } + + import frr-bgp-types { + prefix frr-bt; + } + + include frr-bgp-common-multiprotocol; + + organization + "FRRouting"; + contact + "FRR Users List: FRR Development + List: "; + description + "This submodule defines a model for managing FRR BGP BMP. + + Copyright 2020 FRRouting + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."; + + revision 2019-12-03 { + description + "Initial revision."; + } + + grouping bmp-incoming-session { + container incoming-session { + list session-list { + key "address tcp-port"; + leaf address { + type inet:ip-address; + description + "IPv4 address to listen on."; + } + + leaf tcp-port { + type uint32; + } + } + } + } + + grouping bmp-outgoing-session { + container outgoing-session { + list session-list { + key "hostname tcp-port"; + leaf hostname { + type string; + } + + leaf tcp-port { + type uint32; + } + + leaf min-retry-time { + type uint32 { + range "100..86400000"; + } + units "miliseconds"; + default "30000"; + description + "Minimum connection retry interval."; + } + + leaf max-retry-time { + type uint32 { + range "100..86400000"; + } + units "miliseconds"; + default "720000"; + description + "Maximum connection retry interval."; + } + } + } + } + + grouping bmp-afi-safis { + container afi-safis { + description + "List of address-families associated with the BGP + instance."; + list afi-safi { + key "afi-safi-name"; + description + "AFI, SAFI configuration available for the + neighbour or group."; + uses mp-afi-safi-config; + + uses mp-all-afi-safi-list-contents; + } + } + } + + grouping bmp-afi-safi-common-config { + container common-config { + leaf pre-policy { + type boolean; + default "false"; + description + "Send state before policy and filter processing."; + } + + leaf post-policy { + type boolean; + default "false"; + description + "Send state after policy and filter processing."; + } + } + } + + grouping global-bmp-config { + description + "Structural grouping used to include filter + configuration for BMP."; + container bmp-config { + description + "BMP related parameters."; + list target-list { + key "target-name"; + leaf target-name { + type string; + description + "Targets group name."; + } + + uses bmp-incoming-session; + + uses bmp-outgoing-session; + + leaf mirror { + type boolean; + default "false"; + description + "When set to 'TRUE' it send BMP route mirroring messages."; + } + + leaf stats-time { + type uint32 { + range "100..86400000"; + } + units "miliseconds"; + description + "Interval to send BMP Stats."; + } + + leaf ipv4-access-list { + type frr-bt:access-list-ref; + description + "Access list to restrict BMP sessions."; + } + + leaf ipv6-access-list { + type frr-bt:access-list-ref; + description + "Access list to restrict BMP sessions."; + } + + uses bmp-afi-safis; + } + + leaf mirror-buffer-limit { + type uint32 { + range "0..4294967294"; + } + units "bytes"; + description + "Maximum memory used for buffered mirroring messages."; + } + } + } +} diff --git a/yang/frr-bgp-common-multiprotocol.yang b/yang/frr-bgp-common-multiprotocol.yang new file mode 100644 index 0000000000..aefdf02ba6 --- /dev/null +++ b/yang/frr-bgp-common-multiprotocol.yang @@ -0,0 +1,209 @@ +submodule frr-bgp-common-multiprotocol { + yang-version 1.1; + + belongs-to frr-bgp { + prefix "bgp"; + } + + import frr-routing { + prefix frr-rt; + } + + include frr-bgp-common; + + organization + "FRRouting"; + contact + "FRR Users List: FRR Development + List: "; + description + "This module contains general data definitions for use in BGP + Multiprotocol. + + Copyright 2020 FRRouting + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."; + + revision 2019-12-03 { + description + "Initial revision."; + } + + grouping mp-afi-safi-config { + description + "Configuration parameters used for all BGP AFI-SAFIs."; + leaf afi-safi-name { + type identityref { + base frr-rt:afi-safi-type; + } + description + "AFI, SAFI."; + } + } + + grouping mp-all-afi-safi-list-contents { + description + "A common grouping used for contents of the list that is used + for AFI-SAFI entries."; + container ipv4-unicast { + when "derived-from-or-self(../afi-safi-name, 'ipv4-unicast')" { + description + "Include this container for IPv4 Unicast specific + configuration."; + } + description + "IPv4 unicast configuration options."; + } + + container ipv6-unicast { + when "derived-from-or-self(../afi-safi-name, 'ipv6-unicast')" { + description + "Include this container for IPv6 Unicast specific + configuration."; + } + description + "IPv6 unicast configuration options."; + } + + container ipv4-labeled-unicast { + when "derived-from-or-self(../afi-safi-name, 'ipv4-labeled-unicast')" { + description + "Include this container for IPv4 Labeled Unicast specific + configuration."; + } + description + "IPv4 Labeled Unicast configuration options."; + } + + container ipv6-labeled-unicast { + when "derived-from-or-self(../afi-safi-name, 'ipv6-labeled-unicast')" { + description + "Include this container for IPv6 Labeled Unicast specific + configuration."; + } + description + "IPv6 Labeled Unicast configuration options."; + } + + container l3vpn-ipv4-unicast { + when "derived-from-or-self(../afi-safi-name, 'l3vpn-ipv4-unicast')" { + description + "Include this container for IPv4 Unicast L3VPN specific + configuration."; + } + description + "Unicast IPv4 L3VPN configuration options."; + } + + container l3vpn-ipv6-unicast { + when "derived-from-or-self(../afi-safi-name, 'l3vpn-ipv6-unicast')" { + description + "Include this container for unicast IPv6 L3VPN specific + configuration."; + } + description + "Unicast IPv6 L3VPN configuration options."; + } + + container l3vpn-ipv4-multicast { + when "derived-from-or-self(../afi-safi-name, 'l3vpn-ipv4-multicast')" { + description + "Include this container for multicast IPv4 L3VPN specific + configuration."; + } + description + "Multicast IPv4 L3VPN configuration options."; + } + + container l3vpn-ipv6-multicast { + when "derived-from-or-self(../afi-safi-name, 'l3vpn-ipv6-multicast')" { + description + "Include this container for multicast IPv6 L3VPN specific + configuration."; + } + description + "Multicast IPv6 L3VPN configuration options."; + } + + container l2vpn-vpls { + when "derived-from-or-self(../afi-safi-name, 'l2vpn-vpls')" { + description + "Include this container for BGP-signalled VPLS specific + configuration."; + } + description + "BGP-signalled VPLS configuration options."; + } + + container l2vpn-evpn { + when "derived-from-or-self(../afi-safi-name, 'l2vpn-evpn')" { + description + "Include this container for BGP EVPN specific + configuration."; + } + description + "BGP EVPN configuration options."; + } + + container ipv4-multicast { + when "derived-from-or-self(../afi-safi-name, 'ipv4-multicast')" { + description + "Include this container for IPv4 multicast specific + configuration."; + } + description + "IPv4 multicast configuration options."; + } + + container ipv6-multicast { + when "derived-from-or-self(../afi-safi-name, 'ipv6-multicast')" { + description + "Include this container for IPv6 multicast specific + configuration."; + } + description + "IPv6 multicast configuration options."; + } + + container ipv4-flowspec { + when "derived-from-or-self(../afi-safi-name, 'ipv4-flowspec')" { + description + "Include this container for IPv4 flowspec specific + configuration."; + } + description + "IPv4 flowspec configuration options."; + } + + container ipv6-flowspec { + when "derived-from-or-self(../afi-safi-name, 'ipv6-flowspec')" { + description + "Include this container for IPv6 flowspec specific + configuration."; + } + description + "IPv6 flowspec configuration options."; + } + } +} diff --git a/yang/frr-bgp-common-structure.yang b/yang/frr-bgp-common-structure.yang new file mode 100644 index 0000000000..8162527e90 --- /dev/null +++ b/yang/frr-bgp-common-structure.yang @@ -0,0 +1,807 @@ +submodule frr-bgp-common-structure { + yang-version 1.1; + + belongs-to frr-bgp { + prefix "bgp"; + } + + import ietf-inet-types { + prefix inet; + } + + import frr-interface { + prefix frr-interface; + } + + import ietf-bgp-types { + prefix bt; + } + + import frr-bgp-types { + prefix frr-bt; + } + + include frr-bgp-common; + + organization + "FRRouting"; + contact + "FRR Users List: FRR Development + List: "; + description + "This submodule contains general data definitions for use in BGP. + + Copyright 2020 FRRouting + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."; + + revision 2019-12-03 { + description + "Initial revision."; + } + + grouping structure-neighbor-group-ebgp-multihop { + description + "Structural grouping used to include EBGP multi-hop + configuration for both BGP neighbors and peer groups."; + container ebgp-multihop { + description + "EBGP multi-hop parameters for the BGP group."; + choice hop-count-choice { + case default-hop-count { + leaf enabled { + type boolean; + default "false"; + description + "When enabled the referenced group or neighbors are + permitted to be indirectly connected - including cases + where the TTL can be decremented between the BGP peers."; + } + } + + case max-hop-count { + leaf multihop-ttl { + type uint8 { + range "1..255"; + } + description + "Time-to-live value to use when packets are sent to the + referenced group or neighbors and ebgp-multihop is + enabled."; + } + } + } + + leaf disable-connected-check { + type boolean; + default "false"; + description + "When set to 'true' it enforces EBGP neighbors perform multihop."; + } + } + } + + grouping neighbor-local-as-options { + container local-as { + leaf local-as { + type inet:as-number; + mandatory true; + description + "The local autonomous system number that is to be used when + establishing sessions with the remote peer or peer group, if + this differs from the global BGP router autonomous system + number."; + } + + leaf no-prepend { + when "../local-as != 0"; + type boolean; + default "false"; + description + "Do not prepend local-as to updates from EBGP peers. When + set to 'true' it will not prepend local-as to updates. When + set to 'false' it will prepend local-as to updates."; + } + + leaf no-replace-as { + type boolean; + default "false"; + description + "Do not prepend local-as to updates from IBGP peers."; + } + } + } + + grouping neighbor-bfd-options { + container bfd-options { + leaf enable { + type boolean; + default "false"; + description + "BFD support."; + } + + leaf detect-multiplier { + when "../enable = 'true'"; + type uint8 { + range "2..255"; + } + default "3"; + description + "Detect multiplier."; + } + + leaf required-min-rx { + when "../enable = 'true'"; + type uint16 { + range "50..60000"; + } + units "miliseconds"; + default "300"; + description + "Required min receive interval."; + } + + leaf desired-min-tx { + when "../enable = 'true'"; + type uint16 { + range "50..60000"; + } + units "miliseconds"; + default "300"; + description + "Desired min transmit interval."; + } + + leaf session-type { + when "../enable = 'true'"; + type frr-bt:bfd-session-type; + default "not-configured"; + description + "BFD session type."; + } + + leaf check-cp-failure { + when "../enable = 'true'"; + type boolean; + default "false"; + description + "Link dataplane status with BGP control plane."; + } + } + } + + grouping neighbor-remote-as { + container neighbor-remote-as { + leaf remote-as-type { + type frr-bt:as-type; + mandatory true; + description + "Remote AS type."; + } + + leaf remote-as { + when "../remote-as-type = 'as-specified'"; + type inet:as-number; + description + "The remote autonomous system number received in + the BGP OPEN message."; + reference + "RFC 4271"; + } + } + } + + grouping neighbor-update-source { + description + "Source of routing updates."; + container update-source { + description + "Source of routing updates config."; + choice source { + case ip-based { + leaf ip { + type inet:ip-address; + description + "IPv4 address/IPv6 address."; + } + } + + case interface-based { + leaf interface { + type frr-interface:interface-ref { + require-instance false; + } + description + "The local interface."; + } + } + } + } + } + + grouping structure-neighbor-group-add-paths { + description + "Structural grouping used to include ADD-PATHs configuration + and state for both BGP neighbors and peer groups."; + container add-paths { + description + "Parameters relating to the advertisement and receipt of + multiple paths for a single NLRI (add-paths)."; + reference + "RFC 7911: ADD-PATH."; + leaf path-type { + type frr-bt:add-path-type; + default "none"; + description + "Enable ability to receive multiple path advertisements for + an NLRI from the neighbor or group."; + } + } + } + + grouping structure-neighbor-group-as-path-options { + description + "Structural grouping used to include AS_PATH manipulation + configuration both BGP neighbors and peer groups."; + container as-path-options { + description + "AS_PATH manipulation parameters for the BGP neighbor or + group."; + choice allowas-in { + case occurence-based { + leaf allow-own-as { + type uint8 { + range "1..10"; + } + description + "Specify the number of occurrences of the local BGP + speaker's AS that can occur within the AS_PATH before it + is rejected."; + } + } + + case origin-based { + leaf allow-own-origin-as { + type boolean; + default "false"; + description + "When set to 'true' only accept my AS in the as-path + if the route was originated in my AS."; + } + } + } + + leaf replace-peer-as { + type boolean; + default "false"; + description + "Replace occurrences of the peer's AS in the AS_PATH with + the local autonomous system number. This is same as override + ASN CLI."; + } + } + } + + grouping structure-neighbor-group-capability-options { + description + "Structural grouping used to include capability + configuration for both BGP neighbors and peer groups."; + container capability-options { + description + "Capability manipulation parameters for the BGP neighbor or + group."; + leaf dynamic-capability { + type boolean; + default "false"; + description + "When set to 'true' dynamic capability is advertise to this peer."; + } + + leaf strict-capability { + type boolean; + default "false"; + description + "Strict capability negotiation match. When set to 'true' + remote and local capabilities are strictly compared + if capabilities are different, send Unsupported Capability + error then reset connection."; + } + + leaf extended-nexthop-capability { + type boolean; + default "false"; + description + "When set to 'true' extended next-hop capability is advertise + to this peer."; + } + + leaf capability-negotiate { + type boolean; + default "true"; + description + "When set to 'true' sending Capability Negotiation in the open + message is supressed to this peer."; + } + + leaf override-capability { + type boolean; + default "false"; + description + "Overrides the result of Capability Negotiation, ignoring remote + peer's capability value, when set to 'true'."; + } + } + } + + grouping structure-neighbor-default-originate-options { + description + "Structural grouping used to include default-originate + configuration for both BGP neighbors and peer groups."; + container default-originate-options { + description + "default originate parameters for the BGP neighbor or + group."; + leaf send-default-route { + type boolean; + default "false"; + description + "If set to 'true', send the default-route to the neighbour(s)."; + } + + leaf rmap-policy-export { + type frr-bt:rmap-ref; + description + "Route-map to specify criteria to originate default."; + } + } + } + + grouping structure-neighbor-prefix-limit { + container prefix-limit { + description + "Parameters relating to the prefix limit for the AFI-SAFI."; + list direction-list { + key "direction"; + leaf direction { + type frr-bt:direction; + description + "Prefix limit applied on Tx route-updates or Rx route-updates."; + } + + leaf max-prefixes { + type uint32; + mandatory true; + description + "Maximum number of prefixes that will be accepted from the + neighbour."; + } + + container prefix-limit-options { + when "../direction = 'in'"; + choice options { + case warning { + leaf warning-only { + type boolean; + default "false"; + description + "When set to 'true' only give warning message when limit + is exceeded."; + } + } + + case restart { + leaf restart-timer { + type uint16; + units "minutes"; + description + "Time interval in seconds after which the BGP session is + re-established after being torn down due to exceeding the + max-prefix limit."; + } + } + + case threshold { + leaf shutdown-threshold-pct { + type bt:percentage; + description + "Threshold on number of prefixes that can be received from + a neighbour before generation of warning messages or log + entries. Expressed as a percentage of max-prefixes."; + } + } + + case threshold-restart { + leaf tr-shutdown-threshold-pct { + type bt:percentage; + description + "Threshold on number of prefixes that can be received from + a neighbour before generation of warning messages or log + entries. Expressed as a percentage of max-prefixes."; + } + + leaf tr-restart-timer { + type uint32; + units "minutes"; + description + "Time interval in seconds after which the BGP session is + re-established after being torn down due to exceeding the + max-prefix limit."; + } + } + + case threshold-warning { + leaf tw-shutdown-threshold-pct { + type bt:percentage; + description + "Threshold on number of prefixes that can be received from + a neighbour before generation of warning messages or log + entries. Expressed as a percentage of max-prefixes."; + } + + leaf tw-warning-only { + type boolean; + default "false"; + description + "When set to 'true' only give warning message when limit + is exceeded."; + } + } + } + } + } + } + } + + grouping structure-neighbor-nexthop-self { + container nexthop-self { + description + "Parameters relating to the nexthop-self for the AFI-SAFI."; + leaf next-hop-self { + type boolean; + default "false"; + description + "When set to 'true', EBGP learned routes are announced with the + local speaker's nexthop."; + } + + leaf next-hop-self-force { + type boolean; + default "false"; + description + "When set to 'true', EBGP learned routes are announced with the + local speaker's nexthop."; + } + } + } + + grouping structure-neighbor-private-as { + container private-as { + description + "Parameters relating to the private-as for the AFI-SAFI."; + leaf remove-private-as-all { + type boolean; + default "false"; + description + "When set to 'true', private ASNs are removed from outbound updates; + applies to all AS numbers."; + } + + leaf remove-private-as-all-replace { + type boolean; + default "false"; + description + "When set to 'true', private ASNs are replaced by the local + speaker's ASN in all outbound updates; applies to all AS numbers."; + } + + leaf remove-private-as { + type boolean; + default "false"; + description + "When set to 'true', removes private ASNs in outbound updates; + applies to all AS numbers."; + } + + leaf remove-private-as-replace { + type boolean; + default "false"; + description + "When set to 'true', private ASNs are replaced with the local + speaker's ASN in all outbound updates; applies to all AS numbers."; + } + } + } + + grouping structure-neighbor-weight { + container weight { + description + "Parameters relating to the weight for the AFI-SAFI."; + leaf weight-attribute { + type uint16 { + range "0..65535"; + } + description + "Set default weight for routes from this neighbor."; + } + } + } + + grouping structure-neighbor-route-reflector { + container route-reflector { + description + "Parameters relating to the route-reflector for the AFI-SAFI."; + leaf route-reflector-client { + type boolean; + default "false"; + description + "Configure a neighbor as route reflector client."; + } + } + } + + grouping structure-neighbor-route-server { + container route-server { + description + "Parameters relating to the route-server for the AFI-SAFI."; + leaf route-server-client { + type boolean; + default "false"; + description + "Configure a neighbor as route server client."; + } + } + } + + grouping structure-neighbor-send-community { + container send-community { + description + "Parameters relating to the send-community for the AFI-SAFI."; + leaf send-community { + type boolean; + default "true"; + description + "Send standard community attribute to this neighbor."; + } + + leaf send-ext-community { + type boolean; + default "true"; + description + "Send extended community attribute to this neighbor."; + } + + leaf send-large-community { + type boolean; + default "false"; + description + "Send large community attribute to this neighbor."; + } + } + } + + grouping structure-neighbor-group-admin-shutdown { + description + "Structural grouping used to include admin-shutdown + configuration for both BGP neighbors and peer groups."; + container admin-shutdown { + description + "BGP Administrative Shutdown Communication."; + leaf enable { + type boolean; + mandatory true; + description + "When set to 'true', BGP shutdown communication is enabled."; + } + + leaf message { + type string; + description + "Shutdown message."; + reference + "draft-ietf-idr-shutdown-06"; + } + } + } + + grouping structure-neighbor-group-graceful-restart { + description + "Structural grouping used to include graceful-restart + configuration for both BGP neighbors and peer groups."; + container graceful-restart { + description + "BGP Graceful restart feature."; + choice mode { + case graceful-restart-mode { + leaf enable { + type boolean; + default "false"; + description + "Enable or disable the graceful-restart capability. + Setting this value to 'true' enables the graceful-restart + and helper both at peer level. Setting this value to 'false' + disables graceful restart and helper mode. The peer will inherit + global configuration."; + } + } + + case graceful-restart-helper-mode { + leaf graceful-restart-helper { + type boolean; + default "false"; + description + "Setting this value to 'true' enables helper mode for the peer + Setting this value to 'false' disables the helper mode. The + peer will inherit global configuration."; + } + } + + case graceful-restart-disable-mode { + leaf graceful-restart-disable { + type boolean; + default "false"; + description + "Setting this value to 'true' disables the graceful-restart + and helper Mode. Setting this value to 'false' causes the peer + to inherit global configuration."; + } + } + } + } + } + + grouping structure-neighbor-group-soft-reconfiguration { + description + "Structural grouping used to include soft-reconfiguration + configuration for both BGP neighbors and peer groups."; + leaf soft-reconfiguration { + type boolean; + default "false"; + description + "Allow inbound soft reconfiguration for this neighbor."; + } + } + + grouping structure-neighbor-group-attr-unchanged { + description + "Structural grouping used to include BGP route propagation + rules configuration for both BGP neighbors and peer groups."; + container attr-unchanged { + description + "BGP route propagation rules configuration."; + leaf as-path-unchanged { + type boolean; + default "false"; + description + "When set to 'true' as-path attribute is propagated unchanged."; + } + + leaf next-hop-unchanged { + type boolean; + default "false"; + description + "When set to 'true' next-hop attribute is propagated unchanged."; + } + + leaf med-unchanged { + type boolean; + default "false"; + description + "When set to 'true' med attribute is propagated unchanged."; + } + } + } + + grouping structure-neighbor-group-orf-capability { + description + "Structural grouping used to include orf + configuration for both BGP neighbors and peer groups."; + container orf-capability { + choice orf-update { + case send { + leaf orf-send { + type boolean; + default "false"; + description + "Setting to 'true' advertises the ORF capability."; + } + } + + case receive { + leaf orf-receive { + type boolean; + default "false"; + description + "When set to 'true' it receives the orf capability."; + } + } + + case both { + leaf orf-both { + type boolean; + default "false"; + description + "When set to 'true' it advertises/receives the orf capability."; + } + } + } + } + } + + grouping structure-neighbor-config-timers { + description + "Structural grouping used to include per neighbor timers + configuration for both BGP neighbors and peer groups."; + container timers { + leaf advertise-interval { + type uint16 { + range "0..600"; + } + units "seconds"; + description + "Minimum interval between sending BGP routing updates."; + } + + leaf connect-time { + type uint16 { + range "1..65535"; + } + units "seconds"; + description + "BGP connect timer."; + } + + uses neighbor-timers; + } + } + + grouping structure-neighbor-group-filter-config { + description + "Structural grouping used to include filter + configuration for both BGP neighbors and peer groups."; + container filter-config { + description + "BGP Policy configuration for both BGP neighbors and groups."; + uses rmap-policy-import; + + uses rmap-policy-export; + + uses plist-policy-import; + + uses plist-policy-export; + + uses access-list-policy-import; + + uses access-list-policy-export; + + uses as-path-filter-list-policy-import; + + uses as-path-filter-list-policy-export; + + uses unsupress-map-policy-import; + + uses unsupress-map-policy-export; + } + } +} diff --git a/yang/frr-bgp-common.yang b/yang/frr-bgp-common.yang new file mode 100644 index 0000000000..188cf856db --- /dev/null +++ b/yang/frr-bgp-common.yang @@ -0,0 +1,1108 @@ +submodule frr-bgp-common { + yang-version 1.1; + + belongs-to frr-bgp { + prefix "bgp"; + } + + import ietf-bgp-types { + prefix bt; + } + + import ietf-inet-types { + prefix inet; + } + + import frr-bgp-types { + prefix frr-bt; + } + + import frr-route-types { + prefix frr-rt-type; + } + + import frr-vrf { + prefix frr-vrf; + } + + import ietf-routing-types { + prefix rt-types; + } + + import frr-interface { + prefix frr-interface; + } + + organization + "FRRouting"; + contact + "FRR Users List: FRR Development + List: "; + description + "This submodule contains general data definitions for use in BGP. + + Copyright 2020 FRRouting + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."; + + revision 2019-12-03 { + description + "Initial revision."; + } + + grouping rmap-policy-import { + leaf rmap-import { + type frr-bt:rmap-ref; + } + } + + grouping rmap-policy-export { + leaf rmap-export { + type frr-bt:rmap-ref; + } + } + + grouping unsupress-map-policy-import { + leaf unsupress-map-import { + type frr-bt:rmap-ref; + } + } + + grouping unsupress-map-policy-export { + leaf unsupress-map-export { + type frr-bt:rmap-ref; + } + } + + grouping plist-policy-import { + leaf plist-import { + type frr-bt:plist-ref; + } + } + + grouping plist-policy-export { + leaf plist-export { + type frr-bt:plist-ref; + } + } + + grouping access-list-policy-import { + leaf access-list-import { + type frr-bt:access-list-ref; + } + } + + grouping access-list-policy-export { + leaf access-list-export { + type frr-bt:access-list-ref; + } + } + + grouping as-path-filter-list-policy-import { + leaf as-path-filter-list-import { + type frr-bt:as-path-filter-ref; + } + } + + grouping as-path-filter-list-policy-export { + leaf as-path-filter-list-export { + type frr-bt:as-path-filter-ref; + } + } + + grouping route-selection-options { + description + "Configuration relating to route selection options."; + container route-selection-options { + description + "Parameters relating to options for route selection."; + leaf always-compare-med { + type boolean; + default "false"; + description + "Compare multi-exit discriminator (MED) value from + different ASes when selecting the best route. The + default behaviour is to only compare MEDs for paths + received from the same AS."; + } + + leaf deterministic-med { + type boolean; + default "false"; + description + "Compare multi-exit discriminator (MED) value from + Same ASes when selecting the best route."; + } + + leaf confed-med { + type boolean; + default "false"; + description + "Compare multi-exit discriminator (MED) value from + different Sub ASes when selecting the best route."; + } + + leaf missing-as-worst-med { + type boolean; + default "false"; + description + "Missing multi-exit discriminator (MED) value is + considered as value of infinity, making the path + least desirable when selecting the best route."; + } + + leaf aspath-confed { + type boolean; + default "false"; + description + "Compare path lengths including confederation sets + and sequences in selecting a route."; + } + + leaf ignore-as-path-length { + type boolean; + default "false"; + description + "Ignore the AS path length when selecting the best path. + The default is to use the AS path length and prefer paths + with shorter length."; + } + + leaf external-compare-router-id { + type boolean; + default "true"; + description + "When comparing similar routes received from external BGP + peers, use the router-id as a criterion to select the + active path."; + } + + leaf allow-multiple-as { + type boolean; + default "false"; + description + "Allow multi-path to use paths from different neighbouring + ASes. The default is to only consider multiple paths + from the same neighbouring AS."; + } + + leaf multi-path-as-set { + when "../allow-multiple-as = 'true'"; + type boolean; + default "false"; + description + "Multi-path with AS-SET, When set to 'true' it adds AS set + information for aggregate routes, When set to 'false' it + prevents AS set generation."; + } + } + } + + grouping med-config { + description + "Configuration relating to MED."; + container med-config { + leaf enable-med-admin { + type boolean; + default "false"; + description + "Flag to enable receiving of MED metric attribute + in routing updates."; + } + leaf max-med-admin { + type uint32 { + range "0..4294967295"; + } + default "4294967294"; + description + "Tells the router to announce routes with this MED value + This MED value is applicable for indefinite time."; + } + + leaf max-med-onstart-up-time { + type uint32 { + range "5..86400"; + } + units "seconds"; + description + "Tells the router to announce routes with MED value, + This MED value is applicable for this duration during + start-up."; + } + + leaf max-med-onstart-up-value { + type uint32 { + range "0..4294967295"; + } + default "4294967294"; + description + "Tells the router to announce routes with this MED value + This MED value is applicable for start-up time."; + } + } + } + + grouping route-reflector-config { + description + "Grouping used to include route reflector + configuration for BGP global."; + container route-reflector { + description + "Route reflector parameters for the BGP global."; + leaf route-reflector-cluster-id { + type bt:rr-cluster-id-type; + description + "Route Reflector cluster ID to use when local router is + configured as a route reflector. Commonly set at the + group level, but allows a different cluster ID to be set + for each neighbor."; + reference + "RFC 4456: BGP Route Reflection: An Alternative to + Full Mesh."; + } + + leaf no-client-reflect { + type boolean; + default "false"; + description + "When set to 'true', this disables route redistribution + by the Route Reflector. It is set 'true' when the client is + fully meshed to prevent sending of redundant route + advertisements."; + reference + "TODO: Add reference when IETF writes a draft describing + this."; + } + + leaf allow-outbound-policy { + type boolean; + default "false"; + description + "When set to 'true', this allow RR to modify the attributes of the + reflected IBGP routes as per the out route-map. It is set 'false' + RR will not modify the attributes of the reflected IBGP routes as + per the out route-map rules."; + } + } + } + + grouping global-bgp-config { + leaf instance-type-view { + type boolean; + default "false"; + description + "When set to 'true' BGP instance type is view. + When set to 'false' BGP instance type is regular."; + } + + leaf ebgp-multihop-connected-route-check { + type boolean; + default "false"; + description + "When set to 'true' it will disable checking if nexthop is connected + on EBGP sessions. When set to 'false' it will enable checking if + nexthop is connected on EBGP sessions."; + } + + leaf fast-external-failover { + type boolean; + default "true"; + description + "It's an interface tracking mechanism. When set to 'true' don't + immediately reset session if a link to a directly connected + external peer goes down. When set to 'false' it will immediately + reset session if a link to a directly connected external peer goes down."; + } + + leaf local-pref { + type uint32; + default "100"; + description + "BGP local preference attribute sent to internal peers to + indicate the degree of preference for externally learned + routes. The route with the highest local preference + value is preferred."; + reference + "RFC 4271."; + } + + leaf default-shutdown { + type boolean; + default "false"; + description + "Apply administrative shutdown to newly configured peers."; + } + + leaf ebgp-requires-policy { + type boolean; + default "true"; + description + "When set to 'true' BGP speaker requires in and out policy + for EBGP peers (RFC8212)."; + } + + leaf show-hostname { + type boolean; + default "false"; + description + "When set to 'true' BGP show hostname in certain command outputs."; + } + + leaf show-nexthop-hostname { + type boolean; + default "false"; + description + "When set to 'true' BGP show hostname for nexthop in certain + command outputs."; + } + } + + grouping global-neighbor-config { + container global-neighbor-config { + leaf dynamic-neighbors-limit { + type uint32 { + range "1..5000"; + } + description + "Maximum number of BGP Dynamic Neighbors that can be created."; + } + + leaf log-neighbor-changes { + type boolean; + default "false"; + description + "When set to 'true' BGP will start logging neighbor up/down and reset reason. + When set to 'false' BGP will stop logging neighbor up/down and reset reason."; + } + + container packet-quanta-config { + leaf wpkt-quanta { + type uint32 { + range "1..64"; + } + default "64"; + description + "How many packets to write to peer socket per run."; + } + + leaf rpkt-quanta { + type uint32 { + range "1..10"; + } + default "10"; + description + "How many packets to read from peer socket per I/O cycle."; + } + } + } + } + + grouping global-update-group-config { + container global-update-group-config { + leaf subgroup-pkt-queue-size { + type uint32 { + range "20..100"; + } + default "40"; + description + "Subgroup packet queue size."; + } + + leaf coalesce-time { + type uint32 { + range "0..4294967295"; + } + units "miliseconds"; + default "1000"; + description + "Configures the Subgroup coalesce timer."; + } + } + } + + grouping global-network-config { + leaf import-check { + type boolean; + default "false"; + description + "When set to 'true' bgp creates entries for network statements + if a matching prefix exists in the rib. When set to 'false' bgp + creates entries for networks that the router cannot reach."; + } + } + + grouping neighbor-timers { + leaf hold-time { + type uint16 { + range "0 | 3..65535"; + } + units "seconds"; + default "180"; + description + "Time interval (in seconds) for the HoldTimer established + with the peer. When read as operational data (ro), the + value of this object is calculated by this BGP speaker, + using the smaller of the values in hold-time that was + configured (rw) in the running datastore and the Hold Time + received in the OPEN message. + + This value must be at least three seconds + if it is not zero (0). + + If the Hold Timer has not been established + with the peer this object MUST have a value + of zero (0). + + If the configured value of hold-time object was + a value of (0), then when read this object MUST have a + value of (0) also."; + reference + "RFC 4271"; + } + + leaf keepalive { + type uint16 { + range "0..65535"; + } + units "seconds"; + default "60"; + description + "When used as a configuration (rw) value, this Time interval + (in seconds) for the KeepAlive timer configured for this BGP + speaker with this peer. The value of this object will only + determine the KEEPALIVE messages' frequency relative to + the value specified in configured value for hold-time. + + If the value of this object is zero (0), no periodical + KEEPALIVE messages are sent to the peer after the BGP + connection has been established. The suggested value for + this timer is 30 seconds.; + + The actual time interval for the KEEPALIVE messages is + indicated by operational value of keepalive. That value + of this object is calculated by this BGP speaker such that, + when compared with hold-time, it has the same proportion + that keepalive has, compared with hold-time. A + reasonable maximum value for this timer would be one third + of that of hold-time."; + reference + "RFC 4271"; + } + } + + grouping global-config-timers { + container global-config-timers { + leaf rmap-delay-time { + type uint16 { + range "0..600"; + } + units "seconds"; + default "5"; + description + "Time to wait before processing route-map changes."; + } + + leaf update-delay-time { + type uint16 { + range "0..3600"; + } + units "seconds"; + description + "Time to force initial delay for best-path and updates."; + } + + leaf establish-wait-time { + type uint16 { + range "1..3600"; + } + units "seconds"; + description + "Time to force initial delay for updates."; + } + + leaf connect-retry-interval { + type uint16 { + range "1..max"; + } + units "seconds"; + default "120"; + description + "Time interval (in seconds) for the ConnectRetryTimer. The + suggested value for this timer is 120 seconds."; + reference + "RFC 4271, This is the value used + to initialize the 'ConnectRetryTimer'."; + } + + uses neighbor-timers; + } + } + + grouping graceful-restart-config { + description + "Configuration parameters relating to BGP graceful restart."; + choice mode { + case graceful-restart-mode { + leaf enabled { + type boolean; + default "false"; + description + "Enable or disable the graceful-restart capability. When set to 'true' + it will enable graceful restart and helper both globally. When set + to 'false' it will enable the default behaviour global helper mode."; + } + } + + case graceful-restart-disable-mode { + leaf graceful-restart-disable { + type boolean; + default "false"; + description + "When set to 'true' it will disable graceful restart and helper both + globally. when set to 'false' it will enable the default behaviour + global helper mode."; + } + } + } + + leaf preserve-fw-entry { + type boolean; + default "false"; + description + "Sets F-bit indication that fib is preserved while doing Graceful Restart. + When set to 'true' Zebra would preserve the FIB entry on the restarting + node."; + } + + leaf restart-time { + type uint16 { + range "1..3600"; + } + units "seconds"; + default "120"; + description + "Estimated time (in seconds) for the local BGP speaker to + restart a session. This value is advertise in the graceful + restart BGP capability. This is a 12-bit value, referred to + as Restart Time in RFC4724. Per RFC4724, the suggested + default value is <= the hold-time value. This timer is + applicable for helper node."; + reference + "RFC 4724: Graceful Restart Mechanism for BGP."; + } + + leaf stale-routes-time { + type uint16 { + range "1..3600"; + } + units "seconds"; + default "360"; + description + "An upper-bound on the time that stale routes will be + retained by a router after a session is restarted. If an + End-of-RIB (EOR) marker is received prior to this timer + expiring stale-routes will be flushed upon its receipt - if + no EOR is received, then when this timer expires stale paths + will be purged. This timer is applicable for restarting node."; + reference + "RFC 4724: Graceful Restart Mechanism for BGP."; + } + + leaf selection-deferral-time { + type uint16 { + range "0..3600"; + } + units "seconds"; + default "360"; + description + "An upper-bound on the time that restarting router defers + the route selection process after restart."; + reference + "RFC 4724: Graceful Restart Mechanism for BGP."; + } + + leaf rib-stale-time { + type uint16 { + range "1..3600"; + } + units "seconds"; + default "500"; + description + "An upper-bound on the time that helper router holds the + stale routes in Zebra, When this timer gets expired Zebra + removes the stale routes."; + } + } + + grouping global-group-use-multiple-paths { + description + "Common grouping used for both global and groups which provides + configuration parameters relating to use of multiple paths."; + container use-multiple-paths { + description + "Parameters related to the use of multiple paths for the + same NLRI."; + container ebgp { + description + "Multi-Path parameters for EBGP."; + leaf maximum-paths { + type uint16; + default "64"; + description + "Maximum number of parallel paths to consider when using + BGP multi-path. The default is use a single path."; + } + } + + container ibgp { + description + "Multi-Path parameters for IBGP."; + leaf maximum-paths { + type uint16; + default "64"; + description + "Maximum number of parallel paths to consider when using + IBGP multi-path. The default is to use a single path."; + } + + leaf cluster-length-list { + when "../maximum-paths != 0"; + type boolean; + default "false"; + description + "When set to 'true' route with the shortest cluster-list + length is used. The cluster-list reflects the IBGP + reflection path the route has taken. It's the part + of route selection algo."; + } + } + } + } + + grouping global-redistribute { + description + "List of route redistribution per AFI."; + list redistribution-list { + key "route-type route-instance"; + leaf route-type { + type frr-rt-type:frr-route-types; + description + "Protocol route type."; + } + + leaf route-instance { + type uint16 { + range "0..65535"; + } + description + "Protocol Instance."; + } + + leaf metric { + type uint32 { + range "0..4294967295"; + } + description + "Metric for redistributed routes."; + } + + leaf rmap-policy-import { + type frr-bt:rmap-ref; + description + "Route-map to be applied for redistributed routes into the bgp."; + } + } + } + + grouping mp-afi-safi-network-config { + leaf label-index { + type rt-types:mpls-label; + description + "Label index to associate with the prefix."; + } + + leaf rmap-policy-export { + type frr-bt:rmap-ref; + description + "Route-map to modify the attributes for Routes going out + via BGP updates."; + } + } + + grouping mp-afi-safi-agg-route-config { + leaf as-set { + type boolean; + default "false"; + description + "When set to 'true' AS set path information is generated + for aggregate address. When set to 'false' AS set path + information is not generated."; + } + + leaf summary-only { + type boolean; + default "false"; + description + "When set to 'true' it advertise only the aggregate route + and suppress the advertisement of all the component routes. + When set to 'false' all more-specific routes summarized + by the aggregate route are advertised."; + } + + leaf rmap-policy-export { + type frr-bt:rmap-ref; + description + "Apply route map to aggregate network."; + } + } + + grouping admin-distance { + container admin-distance { + description + "Administrative distance (or preference) assigned to + routes received from different sources + (external, internal, and local)."; + leaf external { + type uint8 { + range "1..255"; + } + description + "Administrative distance for routes learned from + external BGP (EBGP)."; + } + + leaf internal { + type uint8 { + range "1..255"; + } + description + "Administrative distance for routes learned from + internal BGP (IBGP)."; + } + + leaf local { + type uint8 { + range "1..255"; + } + description + "Administrative distance for routes learned from + local."; + } + } + } + + grouping distance-per-route-config { + leaf distance { + type uint8 { + range "1..255"; + } + mandatory true; + description + "Administrative distance for route."; + } + + leaf access-list-policy-export { + type frr-bt:access-list-ref; + description + "Access-list policy applied on routes going from BGP to Zebra."; + } + } + + grouping route-flap-dampening { + container route-flap-dampening { + description + "Dampening feature"; + leaf enable { + type boolean; + default "false"; + description + "Enable route flap dampening."; + } + + leaf reach-decay { + when "../enable = 'true'"; + type uint8 { + range "1..45"; + } + units "seconds"; + default "15"; + description + "This value specifies the time desired for the instability + metric value to reach one-half of its current value when + the route is reachable. This half-life value determines + the rate at which the metric value is decayed. A smaller + half-life value makes a suppressed route reusable sooner + than a larger value. The accumulated penalty will be reduced + to half after this duration."; + } + + leaf reuse-above { + when "../enable = 'true'"; + type uint16 { + range "1..20000"; + } + default "750"; + description + "This is the value of the instability metric at which a + suppressed route becomes unsuppressed if it is reachable + but currently suppressed. The value assigned to + reuse-below must be less than suppress-above."; + } + + leaf suppress-above { + when "../enable = 'true'"; + type uint16 { + range "1..20000"; + } + default "2000"; + description + "This is the value of the instability metric at which + route suppression takes place. A route is not installed + in the forwarding information base (FIB), or announced + even if it is reachable during the period that it is + suppressed."; + } + + leaf unreach-decay { + when "../enable = 'true'"; + type uint8 { + range "1..255"; + } + units "seconds"; + default "60"; + description + "This value acts the same as reach-decay except that it + specifies the rate at which the instability metric is + decayed when a route is unreachable. It should have a + value greater than or equal to reach-decay."; + } + } + } + + grouping flow-spec-config { + container flow-spec-config { + description + "Flow spec feature."; + leaf interface { + type frr-interface:interface-ref { + require-instance false; + } + description + "The local interface."; + } + } + } + + grouping global-graceful-shutdown { + description + "Structural grouping used to include graceful-shutdown + configuration for both BGP neighbors and peer groups."; + container graceful-shutdown { + description + "BGP Graceful shutdown feature."; + leaf enable { + type boolean; + default "false"; + description + "Enable graceful-shutdown feature."; + } + } + } + + grouping global-filter-config { + description + "Structural grouping used to include filter + configuration for BGP RIB table."; + container filter-config { + description + "BGP table to RIB route download filter."; + uses rmap-policy-export; + } + } + + grouping route-distinguisher-params { + description + "Route distinguisher value as per RFC4364."; + leaf rd { + type rt-types:route-distinguisher; + description + "Route distinguisher value as per RFC4364."; + } + } + + grouping vpn-label-params { + description + "Label value for VRF."; + choice label-allocation-mode { + case manual { + leaf label { + type rt-types:mpls-label; + description + "Label index to associate with the prefix."; + } + } + + case auto { + leaf label-auto { + type boolean; + default "false"; + description + "Automatically assign a label."; + } + } + } + } + + grouping vpn-nexthop-params { + description + "Specify next hop to use for VRF advertised prefixes."; + leaf nexthop { + type inet:ip-address; + description + "Nexthop IP address."; + } + } + + grouping rt-list { + description + "Route Target list"; + leaf-list import-rt-list { + type rt-types:route-target; + description + "For routes leaked from vpn to current address-family: match any."; + } + + leaf-list export-rt-list { + type rt-types:route-target; + description + "For routes leaked from current address-family to vpn: set."; + } + } + + grouping vpn-route-target-params { + description + "Route Target value."; + leaf redirect-rt { + type rt-types:route-target; + description + "Flow-spec redirect type route target."; + } + + choice rt-direction { + case import-export { + uses rt-list; + } + case both { + leaf-list rt-list { + type rt-types:route-target; + description + "Both import: match any and export: set."; + } + } + } + } + + grouping vpn-import-params { + description + "VPN route leaking parameters."; + leaf import-vpn { + type boolean; + default "false"; + description + "Import routes from default instance VPN RIB."; + } + + leaf export-vpn { + type boolean; + default "false"; + description + "Export routes to default instance VPN RIB."; + } + + list import-vrf-list { + key "vrf"; + description + "List of VRFs to import routes from."; + leaf vrf { + type frr-vrf:vrf-ref { + require-instance false; + } + description + "Routing instance."; + } + } + + uses rmap-policy-import; + + uses rmap-policy-export; + } + + grouping global-afi-safi-vpn-config { + container vpn-config { + uses route-distinguisher-params; + + uses vpn-label-params; + + uses vpn-nexthop-params; + + uses vpn-import-params; + + uses vpn-route-target-params; + } + } + + grouping global-afi-safi-vpn-network-config { + list network-config { + key "rd"; + description + "A list of rd."; + uses route-distinguisher-params; + + list prefix-list { + key "prefix"; + description + "A list of prefix."; + leaf prefix { + type inet:ip-prefix; + description + "IP destination prefix."; + } + + leaf label-index { + type uint32; + mandatory true; + description + "Label index to associate with the prefix."; + } + + leaf rmap-policy-export { + type frr-bt:rmap-ref; + description + "Route-map to modify the attributes for Routes going out + via BGP updates."; + } + } + } + } +} diff --git a/yang/frr-bgp-neighbor.yang b/yang/frr-bgp-neighbor.yang new file mode 100644 index 0000000000..3b8d63c447 --- /dev/null +++ b/yang/frr-bgp-neighbor.yang @@ -0,0 +1,137 @@ +submodule frr-bgp-neighbor { + yang-version 1.1; + + belongs-to frr-bgp { + prefix "bgp"; + } + + include frr-bgp-common-structure; + include frr-bgp-common-multiprotocol; + + organization + "FRRouting"; + contact + "FRR Users List: FRR Development + List: "; + description + "This submodule contains general data definitions for use in BGP neighbor. + + Copyright 2020 FRRouting + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."; + + revision 2019-12-03 { + description + "Initial revision."; + } + + grouping neighbor-parameters { + leaf password { + type string { + length "1..254"; + } + description + "Actual password."; + } + + leaf ttl-security { + type uint8; + description + "BGP Time To Live (TTL) security check."; + reference + "RFC 5082: The Generalized TTL Security Mechanism + (GTSM), + RFC 7454: BGP Operations and Security."; + } + + leaf solo { + type boolean; + default "false"; + description + "Solo peer - part of its own update group."; + } + + leaf enforce-first-as { + type boolean; + default "false"; + description + "When set to 'true' it will enforce the first AS for EBGP routes."; + } + + leaf description { + type string; + description + "An optional textual description (intended primarily for use + with a peer or group."; + } + + leaf passive-mode { + type boolean; + default "false"; + description + "Don't send open messages to this neighbor."; + } + + uses structure-neighbor-group-capability-options; + + uses neighbor-update-source; + + uses neighbor-remote-as; + + uses structure-neighbor-group-ebgp-multihop; + + uses neighbor-local-as-options; + + uses neighbor-bfd-options; + + uses structure-neighbor-group-admin-shutdown; + + uses structure-neighbor-group-graceful-restart; + + uses structure-neighbor-config-timers; + + container afi-safis { + description + "List of address-families associated with the BGP + instance."; + list afi-safi { + key "afi-safi-name"; + description + "AFI, SAFI configuration available for the + neighbour or group."; + uses mp-afi-safi-config; + + leaf enabled { + type boolean; + default "false"; + description + "This leaf indicates whether the IPv4 Unicast AFI, SAFI is + enabled for the neighbour or group."; + } + + uses mp-all-afi-safi-list-contents; + } + } + } +} diff --git a/yang/frr-bgp-peer-group.yang b/yang/frr-bgp-peer-group.yang new file mode 100644 index 0000000000..3ce628d2b7 --- /dev/null +++ b/yang/frr-bgp-peer-group.yang @@ -0,0 +1,89 @@ +submodule frr-bgp-peer-group { + yang-version 1.1; + + belongs-to frr-bgp { + prefix "bgp"; + } + + import ietf-inet-types { + prefix inet; + } + + include frr-bgp-common-structure; + include frr-bgp-neighbor; + + organization + "FRRouting"; + contact + "FRR Users List: FRR Development + List: "; + description + "This submodule contains general data definitions for use in BGP + peer group. + + Copyright 2020 FRRouting + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."; + + revision 2019-12-03 { + description + "Initial revision."; + } + + grouping bgp-peer-group-base { + description + "Parameters related to a BGP group."; + leaf peer-group-name { + type string; + description + "Name of the BGP peer-group."; + } + + leaf-list ipv4-listen-range { + type inet:ipv4-address; + description + "Configure BGP dynamic neighbors listen range."; + } + + leaf-list ipv6-listen-range { + type inet:ipv6-address; + description + "Configure BGP dynamic neighbors listen range."; + } + + uses neighbor-parameters; + } + + grouping bgp-peer-group-list { + description + "The list of BGP peer groups."; + list peer-group { + key "peer-group-name"; + description + "List of BGP peer-groups configured on the local system - + uniquely identified by peer-group name."; + uses bgp-peer-group-base; + } + } +} diff --git a/yang/frr-bgp-rpki.yang b/yang/frr-bgp-rpki.yang new file mode 100644 index 0000000000..e9b6752f26 --- /dev/null +++ b/yang/frr-bgp-rpki.yang @@ -0,0 +1,209 @@ +module frr-bgp-rpki { + yang-version 1.1; + namespace "http://frrouting.org/yang/frr-bgp-rpki"; + prefix frr-bgp-rpki; + + import ietf-inet-types { + prefix inet; + } + + import frr-vrf { + prefix frr-vrf; + } + + organization + "FRRouting"; + contact + "FRR Users List: FRR Development + List: "; + description + "This module defines a model for managing FRR BGP RPKI. + + Copyright 2020 FRRouting + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."; + + revision 2019-12-03 { + description + "Initial revision."; + } + + typedef transport-type { + type enumeration { + enum "TCP" { + value 1; + description + "Connection to server is TCP based."; + } + enum "SSH" { + value 2; + description + "Connection to server is SSH based."; + } + } + } + + grouping bgp-rpki-timers { + container rpki-timers { + description + "RPKI timers config."; + leaf polling-time { + type uint32 { + range "1..86400"; + } + units "seconds"; + default "3600"; + description + "Set the number of seconds the router waits until the + router asks the cache again for updated data."; + } + + leaf expire-time { + type uint32 { + range "600..172800"; + } + units "seconds"; + default "7200"; + description + "Set the expire interval."; + } + + leaf retry-time { + type uint16 { + range "1..7200"; + } + units "seconds"; + default "600"; + description + "Set the retry interval."; + } + } + } + + grouping bgp-rpki-cache-server { + container rpki-cache-server { + description + "Add a cache server to the socket."; + list cache-list { + key "preference"; + leaf preference { + type uint8 { + range "1..255"; + } + description + "Preference of the cache server."; + } + + leaf cache-type { + type transport-type; + mandatory true; + description + "Specifies a transport method for the RPKI cache."; + } + + choice server { + case ip-address { + leaf ip-address { + type inet:ip-address; + mandatory true; + } + } + + case host-name { + leaf ip-host-address { + type inet:host; + mandatory true; + } + } + } + + container transport { + container tcp { + when "../../cache-type = 'TCP'"; + description + "TCP server details."; + leaf tcp-port { + type uint32; + } + } + + container ssh { + when "../../cache-type = 'SSH'"; + description + "SSH login details"; + leaf ssh-port { + type uint32 { + range "1..65535"; + } + description + "SSH port on which session gets opened."; + } + + leaf user-name { + type string; + description + "SSH username to establish an SSH connection to the + cache server."; + } + + leaf private-key { + type string; + description + "Local path that includes the private key file of the router."; + } + + leaf public-key { + type string; + description + "Local path that includes the public key file of the router."; + } + + leaf server-public-ley { + type string; + description + "Server public key."; + } + } + } + } + } + } + + augment "/frr-vrf:lib/frr-vrf:vrf" { + container bgp-rpki { + description + "RPKI configuration parameters."; + leaf enable { + type boolean; + default "false"; + description + "When set to 'true' it enables the RPKI."; + } + + uses bgp-rpki-timers; + + uses bgp-rpki-cache-server; + } + } +} diff --git a/yang/frr-bgp-types.yang b/yang/frr-bgp-types.yang new file mode 100644 index 0000000000..0afdea1ba6 --- /dev/null +++ b/yang/frr-bgp-types.yang @@ -0,0 +1,154 @@ +module frr-bgp-types { + yang-version 1.1; + namespace "http://frrouting.org/yang/bgp-types"; + prefix frr-bt; + + organization + "FRRouting"; + contact + "FRR Users List: FRR Development + List: "; + description + "This module contains general data definitions for use in BGP. + + Copyright 2020 FRRouting + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."; + + revision 2019-12-03 { + description + "Initial revision."; + } + + typedef rmap-ref { + type string; + } + + typedef plist-ref { + type string; + } + + typedef access-list-ref { + type string; + } + + typedef as-path-filter-ref { + type string; + } + + typedef bgp-instance-type { + type enumeration { + enum "default" { + value 1; + description + "BGP instance default."; + } + enum "vrf" { + value 2; + description + "BGP instance vrf."; + } + enum "view" { + value 3; + description + "BGP instance view."; + } + } + } + + typedef as-type { + type enumeration { + enum "as-specified" { + value 1; + description + "AS has explicitly specified value."; + } + enum "internal" { + value 2; + description + "Internal BGP peer."; + } + enum "external" { + value 3; + description + "External BGP peer."; + } + } + } + + typedef add-path-type { + type enumeration { + enum "all" { + value 1; + description + "To advertise all paths to a neighbor."; + } + enum "per-as" { + value 2; + description + "To advertise the best path per each neighboring AS."; + } + enum "none" { + value 3; + description + "Add path feature is disabled."; + } + } + } + + typedef bfd-session-type { + type enumeration { + enum "single-hop" { + value 1; + description + "Single hop session."; + } + enum "multi-hop" { + value 2; + description + "Multiple hop session."; + } + enum "not-configured" { + value 3; + description + "Not Configured."; + } + } + } + + typedef direction { + type enumeration { + enum "in" { + value 1; + description + "IN, ingress, Rx."; + } + enum "out" { + value 2; + description + "OUT, egress, Tx."; + } + } + } +} diff --git a/yang/frr-bgp.yang b/yang/frr-bgp.yang new file mode 100644 index 0000000000..3e5b1da7d3 --- /dev/null +++ b/yang/frr-bgp.yang @@ -0,0 +1,1239 @@ +module frr-bgp { + yang-version 1.1; + namespace "http://frrouting.org/yang/bgp"; + prefix frr-bgp; + + import frr-routing { + prefix frr-rt; + } + + import ietf-inet-types { + prefix inet; + } + + import ietf-routing-types { + prefix rt-types; + } + + import frr-interface { + prefix frr-interface; + } + + include "frr-bgp-common-structure"; + + include "frr-bgp-common"; + + include "frr-bgp-common-multiprotocol"; + + include "frr-bgp-neighbor"; + + include "frr-bgp-peer-group"; + + include "frr-bgp-bmp"; + + organization + "FRRouting"; + contact + "FRR Users List: FRR Development + List: "; + description + "This module defines a model for managing FRR bgpd daemon. + + Copyright 2020 FRRouting + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."; + + revision 2019-12-03 { + description + "Initial revision."; + } + + identity bgp { + base frr-rt:routing-protocol; + description + "BGP protocol."; + } + + grouping mp-afi-unicast-common { + uses global-group-use-multiple-paths; + + uses global-redistribute; + + uses admin-distance; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol" { + container bgp { + when "../frr-rt:type = 'frr-bgp:bgp'" { + description + "BGP protocol augmentation of ietf-routing module + control-plane-protocol."; + } + description + "Top-level configuration for the BGP router."; + container global { + presence "Enables global configuration of BGP"; + description + "Global configuration for the BGP router."; + leaf local-as { + type inet:as-number; + mandatory true; + description + "Local autonomous system number of the router. Uses + the 32-bit as-number type from the model in RFC 6991."; + } + + uses frr-rt:router-id; + + container confederation { + description + "Configuration options specifying parameters when the + local router is within an autonomous system which is + part of a BGP confederation."; + leaf identifier { + type inet:as-number; + description + "Confederation identifier for the autonomous system."; + } + + leaf-list member-as { + type inet:as-number; + description + "Remote autonomous systems that are to be treated + as part of the local confederation."; + } + } + + uses med-config; + + uses route-reflector-config; + + uses route-selection-options; + + uses global-neighbor-config; + + container graceful-restart { + description + "Parameters relating the graceful restart mechanism for + BGP."; + uses graceful-restart-config; + } + + uses global-update-group-config; + + uses global-config-timers; + + uses global-bgp-config; + + uses global-network-config; + + uses global-graceful-shutdown; + + uses global-bmp-config; + + container afi-safis { + description + "List of address-families associated with the BGP + instance."; + list afi-safi { + key "afi-safi-name"; + description + "AFI, SAFI configuration available for the + neighbour or group."; + uses mp-afi-safi-config; + + uses mp-all-afi-safi-list-contents; + } + } + } + + container neighbors { + description + "Configuration for BGP neighbors."; + list neighbor { + key "remote-address"; + description + "List of BGP neighbors configured on the local system, + uniquely identified by remote IPv[46] address."; + leaf remote-address { + type inet:ip-address; + description + "The remote IP address of this entry's BGP peer."; + } + + leaf local-interface { + type frr-interface:interface-ref { + require-instance false; + } + description + "Neighbor's interface name."; + } + + leaf local-port { + type inet:port-number { + range "0..65535"; + } + description + "Neighbor's BGP TCP port number."; + } + + leaf peer-group { + type leafref { + path "../../../peer-groups/peer-group/peer-group-name"; + } + description + "The peer-group with which this neighbor is associated."; + } + + uses neighbor-parameters; + } + + list unnumbered-neighbor { + key "interface"; + description + "List of BGP neighbors configured on the local system, + uniquely identified by interfaces."; + leaf interface { + type frr-interface:interface-ref { + require-instance false; + } + description + "The local interface of this entry's BGP peer."; + } + + leaf v6only { + type boolean; + default "false"; + description + "When set to 'true' it will create a neighbor with v6 + link local only."; + } + + leaf peer-group { + type leafref { + path "../../../peer-groups/peer-group/peer-group-name"; + } + description + "The peer-group with which this neighbor is associated."; + } + + uses neighbor-parameters; + } + } + + container peer-groups { + description + "Configuration for BGP peer-groups."; + uses bgp-peer-group-list; + } + } + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/global/afi-safis/afi-safi/ipv4-unicast" { + list network-config { + key "prefix"; + description + "A list of network routes."; + leaf prefix { + type inet:ipv4-prefix; + description + "IPv4 destination prefix."; + } + + leaf backdoor { + type boolean; + default "false"; + description + "Specify a BGP backdoor route."; + } + + uses mp-afi-safi-network-config; + } + + list aggregate-route { + key "prefix"; + description + "A list of aggregated routes."; + leaf prefix { + type inet:ipv4-prefix; + description + "IPv4 destination prefix."; + } + + uses mp-afi-safi-agg-route-config; + } + + list admin-distance-route { + key "prefix"; + description + "A list of routes with a particular admin distance."; + leaf prefix { + type inet:ipv4-prefix; + description + "IPv4 destination prefix."; + } + + uses distance-per-route-config; + } + + uses route-flap-dampening; + + uses mp-afi-unicast-common; + + uses global-filter-config; + + uses global-afi-safi-vpn-config; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/global/afi-safis/afi-safi/ipv6-unicast" { + list network-config { + key "prefix"; + description + "A list of network routes."; + leaf prefix { + type inet:ipv6-prefix; + description + "IPv6 destination prefix."; + } + + leaf backdoor { + type boolean; + default "false"; + description + "Specify a BGP backdoor route."; + } + + uses mp-afi-safi-network-config; + } + + list aggregate-route { + key "prefix"; + description + "A list of aggregated routes."; + leaf prefix { + type inet:ipv6-prefix; + description + "IPv6 destination prefix."; + } + + uses mp-afi-safi-agg-route-config; + } + + list admin-distance-route { + key "prefix"; + description + "A list of routes with a particular admin distance."; + leaf prefix { + type inet:ipv6-prefix; + description + "IPv6 destination prefix."; + } + + uses distance-per-route-config; + } + + uses mp-afi-unicast-common; + + uses global-filter-config; + + uses global-afi-safi-vpn-config; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/global/afi-safis/afi-safi/ipv4-labeled-unicast" { + uses global-group-use-multiple-paths; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/global/afi-safis/afi-safi/ipv6-labeled-unicast" { + uses global-group-use-multiple-paths; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/global/afi-safis/afi-safi/ipv4-multicast" { + list network-config { + key "prefix"; + description + "A list of network routes."; + leaf prefix { + type rt-types:ipv4-multicast-group-address; + description + "IPv4 multicast destination prefix."; + } + + leaf backdoor { + type boolean; + default "false"; + description + "Specify a BGP backdoor route."; + } + + uses mp-afi-safi-network-config; + } + + list aggregate-route { + key "prefix"; + description + "A list of aggregated routes."; + leaf prefix { + type rt-types:ipv4-multicast-group-address; + description + "IPv4 multicast destination prefix."; + } + + uses mp-afi-safi-agg-route-config; + } + + list admin-distance-route { + key "prefix"; + description + "A list of routes with a particular admin distance."; + leaf prefix { + type rt-types:ipv4-multicast-group-address; + description + "IPv4 multicast destination prefix."; + } + } + + uses admin-distance; + + uses route-flap-dampening; + + uses global-filter-config; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/global/afi-safis/afi-safi/ipv6-multicast" { + list network-config { + key "prefix"; + description + "A list of network routes."; + leaf prefix { + type rt-types:ipv6-multicast-group-address; + description + "IPv6 multicast destination prefix."; + } + + leaf backdoor { + type boolean; + default "false"; + description + "Specify a BGP backdoor route."; + } + + uses mp-afi-safi-network-config; + } + + list aggregate-route { + key "prefix"; + description + "A list of aggregated routes."; + leaf prefix { + type rt-types:ipv6-multicast-group-address; + description + "IPv6 multicast destination prefix."; + } + + uses mp-afi-safi-agg-route-config; + } + + list admin-distance-route { + key "prefix"; + description + "A list of routes with a particular admin distance."; + leaf prefix { + type rt-types:ipv6-multicast-group-address; + description + "IPv6 multicast destination prefix."; + } + } + + uses admin-distance; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/global/afi-safis/afi-safi/ipv4-flowspec" { + uses flow-spec-config; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/global/afi-safis/afi-safi/l3vpn-ipv4-unicast" { + uses global-afi-safi-vpn-network-config; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/global/afi-safis/afi-safi/l3vpn-ipv6-unicast" { + uses global-afi-safi-vpn-network-config; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/global/bmp-config/target-list/afi-safis/afi-safi/ipv4-unicast" { + uses bmp-afi-safi-common-config; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/global/bmp-config/target-list/afi-safis/afi-safi/ipv4-multicast" { + uses bmp-afi-safi-common-config; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/global/bmp-config/target-list/afi-safis/afi-safi/ipv6-unicast" { + uses bmp-afi-safi-common-config; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/global/bmp-config/target-list/afi-safis/afi-safi/ipv6-multicast" { + uses bmp-afi-safi-common-config; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/ipv4-unicast" { + uses structure-neighbor-group-add-paths; + + uses structure-neighbor-group-as-path-options; + + uses structure-neighbor-default-originate-options; + + uses structure-neighbor-prefix-limit; + + uses structure-neighbor-nexthop-self; + + uses structure-neighbor-private-as; + + uses structure-neighbor-weight; + + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-send-community; + + uses structure-neighbor-group-soft-reconfiguration; + + uses structure-neighbor-group-attr-unchanged; + + uses structure-neighbor-group-orf-capability; + + uses structure-neighbor-group-filter-config; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/ipv6-unicast" { + leaf nexthop-local-unchanged { + type boolean; + default "false"; + description + "Configure treatment of outgoing link-local nexthop attribute. + When set to 'true' it leaves link-local nexthop unchanged + for this peer."; + } + + uses structure-neighbor-group-add-paths; + + uses structure-neighbor-group-as-path-options; + + uses structure-neighbor-group-attr-unchanged; + + uses structure-neighbor-group-orf-capability; + + uses structure-neighbor-prefix-limit; + + uses structure-neighbor-nexthop-self; + + uses structure-neighbor-private-as; + + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-send-community; + + uses structure-neighbor-group-soft-reconfiguration; + + uses structure-neighbor-weight; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/ipv4-multicast" { + uses structure-neighbor-group-add-paths; + + uses structure-neighbor-group-as-path-options; + + uses structure-neighbor-group-attr-unchanged; + + uses structure-neighbor-group-orf-capability; + + uses structure-neighbor-prefix-limit; + + uses structure-neighbor-nexthop-self; + + uses structure-neighbor-private-as; + + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-send-community; + + uses structure-neighbor-group-soft-reconfiguration; + + uses structure-neighbor-weight; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/ipv6-multicast" { + uses structure-neighbor-group-add-paths; + + uses structure-neighbor-group-as-path-options; + + uses structure-neighbor-group-attr-unchanged; + + uses structure-neighbor-group-orf-capability; + + uses structure-neighbor-prefix-limit; + + uses structure-neighbor-nexthop-self; + + uses structure-neighbor-private-as; + + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-send-community; + + uses structure-neighbor-group-soft-reconfiguration; + + uses structure-neighbor-weight; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/ipv4-labeled-unicast" { + uses structure-neighbor-group-add-paths; + + uses structure-neighbor-group-as-path-options; + + uses structure-neighbor-group-attr-unchanged; + + uses structure-neighbor-group-orf-capability; + + uses structure-neighbor-prefix-limit; + + uses structure-neighbor-nexthop-self; + + uses structure-neighbor-private-as; + + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-send-community; + + uses structure-neighbor-group-soft-reconfiguration; + + uses structure-neighbor-weight; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/ipv6-labeled-unicast" { + uses structure-neighbor-group-add-paths; + + uses structure-neighbor-group-as-path-options; + + uses structure-neighbor-group-attr-unchanged; + + uses structure-neighbor-group-orf-capability; + + uses structure-neighbor-prefix-limit; + + uses structure-neighbor-nexthop-self; + + uses structure-neighbor-private-as; + + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-send-community; + + uses structure-neighbor-group-soft-reconfiguration; + + uses structure-neighbor-weight; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/l3vpn-ipv4-unicast" { + uses structure-neighbor-group-add-paths; + + uses structure-neighbor-group-as-path-options; + + uses structure-neighbor-group-attr-unchanged; + + uses structure-neighbor-prefix-limit; + + uses structure-neighbor-nexthop-self; + + uses structure-neighbor-private-as; + + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-send-community; + + uses structure-neighbor-group-soft-reconfiguration; + + uses structure-neighbor-weight; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/l3vpn-ipv6-unicast" { + uses structure-neighbor-group-add-paths; + + uses structure-neighbor-group-as-path-options; + + uses structure-neighbor-group-attr-unchanged; + + uses structure-neighbor-prefix-limit; + + uses structure-neighbor-nexthop-self; + + uses structure-neighbor-private-as; + + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-send-community; + + uses structure-neighbor-group-soft-reconfiguration; + + uses structure-neighbor-weight; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/l2vpn-evpn" { + uses structure-neighbor-group-as-path-options; + + uses structure-neighbor-group-attr-unchanged; + + uses structure-neighbor-nexthop-self; + + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-group-soft-reconfiguration; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/ipv4-flowspec" { + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-group-soft-reconfiguration; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/ipv6-flowspec" { + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-group-soft-reconfiguration; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/unnumbered-neighbor/afi-safis/afi-safi/ipv4-unicast" { + uses structure-neighbor-group-add-paths; + + uses structure-neighbor-group-as-path-options; + + uses structure-neighbor-default-originate-options; + + uses structure-neighbor-prefix-limit; + + uses structure-neighbor-nexthop-self; + + uses structure-neighbor-private-as; + + uses structure-neighbor-weight; + + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-send-community; + + uses structure-neighbor-group-soft-reconfiguration; + + uses structure-neighbor-group-attr-unchanged; + + uses structure-neighbor-group-orf-capability; + + uses structure-neighbor-group-filter-config; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/unnumbered-neighbor/afi-safis/afi-safi/ipv6-unicast" { + leaf nexthop-local-unchanged { + type boolean; + default "false"; + description + "Configure treatment of outgoing link-local nexthop attribute. + When set to 'true' it leaves link-local nexthop unchanged + for this peer."; + } + + uses structure-neighbor-group-add-paths; + + uses structure-neighbor-group-as-path-options; + + uses structure-neighbor-group-attr-unchanged; + + uses structure-neighbor-group-orf-capability; + + uses structure-neighbor-prefix-limit; + + uses structure-neighbor-nexthop-self; + + uses structure-neighbor-private-as; + + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-send-community; + + uses structure-neighbor-group-soft-reconfiguration; + + uses structure-neighbor-weight; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/unnumbered-neighbor/afi-safis/afi-safi/ipv4-multicast" { + uses structure-neighbor-group-add-paths; + + uses structure-neighbor-group-as-path-options; + + uses structure-neighbor-group-attr-unchanged; + + uses structure-neighbor-group-orf-capability; + + uses structure-neighbor-prefix-limit; + + uses structure-neighbor-nexthop-self; + + uses structure-neighbor-private-as; + + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-send-community; + + uses structure-neighbor-group-soft-reconfiguration; + + uses structure-neighbor-weight; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/unnumbered-neighbor/afi-safis/afi-safi/ipv6-multicast" { + uses structure-neighbor-group-add-paths; + + uses structure-neighbor-group-as-path-options; + + uses structure-neighbor-group-attr-unchanged; + + uses structure-neighbor-group-orf-capability; + + uses structure-neighbor-prefix-limit; + + uses structure-neighbor-nexthop-self; + + uses structure-neighbor-private-as; + + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-send-community; + + uses structure-neighbor-group-soft-reconfiguration; + + uses structure-neighbor-weight; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/unnumbered-neighbor/afi-safis/afi-safi/ipv4-labeled-unicast" { + uses structure-neighbor-group-add-paths; + + uses structure-neighbor-group-as-path-options; + + uses structure-neighbor-group-attr-unchanged; + + uses structure-neighbor-group-orf-capability; + + uses structure-neighbor-prefix-limit; + + uses structure-neighbor-nexthop-self; + + uses structure-neighbor-private-as; + + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-send-community; + + uses structure-neighbor-group-soft-reconfiguration; + + uses structure-neighbor-weight; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/unnumbered-neighbor/afi-safis/afi-safi/ipv6-labeled-unicast" { + uses structure-neighbor-group-add-paths; + + uses structure-neighbor-group-as-path-options; + + uses structure-neighbor-group-attr-unchanged; + + uses structure-neighbor-group-orf-capability; + + uses structure-neighbor-prefix-limit; + + uses structure-neighbor-nexthop-self; + + uses structure-neighbor-private-as; + + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-send-community; + + uses structure-neighbor-group-soft-reconfiguration; + + uses structure-neighbor-weight; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/unnumbered-neighbor/afi-safis/afi-safi/l3vpn-ipv4-unicast" { + uses structure-neighbor-group-add-paths; + + uses structure-neighbor-group-as-path-options; + + uses structure-neighbor-group-attr-unchanged; + + uses structure-neighbor-prefix-limit; + + uses structure-neighbor-nexthop-self; + + uses structure-neighbor-private-as; + + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-send-community; + + uses structure-neighbor-group-soft-reconfiguration; + + uses structure-neighbor-weight; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/unnumbered-neighbor/afi-safis/afi-safi/l3vpn-ipv6-unicast" { + uses structure-neighbor-group-add-paths; + + uses structure-neighbor-group-as-path-options; + + uses structure-neighbor-group-attr-unchanged; + + uses structure-neighbor-prefix-limit; + + uses structure-neighbor-nexthop-self; + + uses structure-neighbor-private-as; + + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-send-community; + + uses structure-neighbor-group-soft-reconfiguration; + + uses structure-neighbor-weight; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/unnumbered-neighbor/afi-safis/afi-safi/l2vpn-evpn" { + uses structure-neighbor-group-as-path-options; + + uses structure-neighbor-group-attr-unchanged; + + uses structure-neighbor-nexthop-self; + + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-group-soft-reconfiguration; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/unnumbered-neighbor/afi-safis/afi-safi/ipv4-flowspec" { + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-group-soft-reconfiguration; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/unnumbered-neighbor/afi-safis/afi-safi/ipv6-flowspec" { + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-group-soft-reconfiguration; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/peer-groups/peer-group/afi-safis/afi-safi/ipv4-unicast" { + uses structure-neighbor-group-add-paths; + + uses structure-neighbor-group-as-path-options; + + uses structure-neighbor-default-originate-options; + + uses structure-neighbor-prefix-limit; + + uses structure-neighbor-nexthop-self; + + uses structure-neighbor-private-as; + + uses structure-neighbor-weight; + + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-send-community; + + uses structure-neighbor-group-soft-reconfiguration; + + uses structure-neighbor-group-attr-unchanged; + + uses structure-neighbor-group-orf-capability; + + uses structure-neighbor-group-filter-config; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/peer-groups/peer-group/afi-safis/afi-safi/ipv6-unicast" { + leaf nexthop-local-unchanged { + type boolean; + default "false"; + description + "Configure treatment of outgoing link-local nexthop attribute. + When set to 'true' it leaves link-local nexthop unchanged + for this peer."; + } + + uses structure-neighbor-group-add-paths; + + uses structure-neighbor-group-as-path-options; + + uses structure-neighbor-group-attr-unchanged; + + uses structure-neighbor-group-orf-capability; + + uses structure-neighbor-prefix-limit; + + uses structure-neighbor-nexthop-self; + + uses structure-neighbor-private-as; + + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-send-community; + + uses structure-neighbor-group-soft-reconfiguration; + + uses structure-neighbor-weight; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/peer-groups/peer-group/afi-safis/afi-safi/ipv4-multicast" { + uses structure-neighbor-group-add-paths; + + uses structure-neighbor-group-as-path-options; + + uses structure-neighbor-group-attr-unchanged; + + uses structure-neighbor-group-orf-capability; + + uses structure-neighbor-prefix-limit; + + uses structure-neighbor-nexthop-self; + + uses structure-neighbor-private-as; + + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-send-community; + + uses structure-neighbor-group-soft-reconfiguration; + + uses structure-neighbor-weight; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/peer-groups/peer-group/afi-safis/afi-safi/ipv6-multicast" { + uses structure-neighbor-group-add-paths; + + uses structure-neighbor-group-as-path-options; + + uses structure-neighbor-group-attr-unchanged; + + uses structure-neighbor-group-orf-capability; + + uses structure-neighbor-prefix-limit; + + uses structure-neighbor-nexthop-self; + + uses structure-neighbor-private-as; + + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-send-community; + + uses structure-neighbor-group-soft-reconfiguration; + + uses structure-neighbor-weight; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/peer-groups/peer-group/afi-safis/afi-safi/ipv4-labeled-unicast" { + uses structure-neighbor-group-add-paths; + + uses structure-neighbor-group-as-path-options; + + uses structure-neighbor-group-attr-unchanged; + + uses structure-neighbor-group-orf-capability; + + uses structure-neighbor-prefix-limit; + + uses structure-neighbor-nexthop-self; + + uses structure-neighbor-private-as; + + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-send-community; + + uses structure-neighbor-group-soft-reconfiguration; + + uses structure-neighbor-weight; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/peer-groups/peer-group/afi-safis/afi-safi/ipv6-labeled-unicast" { + uses structure-neighbor-group-add-paths; + + uses structure-neighbor-group-as-path-options; + + uses structure-neighbor-group-attr-unchanged; + + uses structure-neighbor-group-orf-capability; + + uses structure-neighbor-prefix-limit; + + uses structure-neighbor-nexthop-self; + + uses structure-neighbor-private-as; + + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-send-community; + + uses structure-neighbor-group-soft-reconfiguration; + + uses structure-neighbor-weight; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/peer-groups/peer-group/afi-safis/afi-safi/l3vpn-ipv4-unicast" { + uses structure-neighbor-group-add-paths; + + uses structure-neighbor-group-as-path-options; + + uses structure-neighbor-group-attr-unchanged; + + uses structure-neighbor-prefix-limit; + + uses structure-neighbor-nexthop-self; + + uses structure-neighbor-private-as; + + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-send-community; + + uses structure-neighbor-group-soft-reconfiguration; + + uses structure-neighbor-weight; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/peer-groups/peer-group/afi-safis/afi-safi/l3vpn-ipv6-unicast" { + uses structure-neighbor-group-add-paths; + + uses structure-neighbor-group-as-path-options; + + uses structure-neighbor-group-attr-unchanged; + + uses structure-neighbor-prefix-limit; + + uses structure-neighbor-nexthop-self; + + uses structure-neighbor-private-as; + + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-send-community; + + uses structure-neighbor-group-soft-reconfiguration; + + uses structure-neighbor-weight; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/peer-groups/peer-group/afi-safis/afi-safi/l2vpn-evpn" { + uses structure-neighbor-group-as-path-options; + + uses structure-neighbor-group-attr-unchanged; + + uses structure-neighbor-nexthop-self; + + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-group-soft-reconfiguration; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/peer-groups/peer-group/afi-safis/afi-safi/ipv4-flowspec" { + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-group-soft-reconfiguration; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/peer-groups/peer-group/afi-safis/afi-safi/ipv6-flowspec" { + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-group-soft-reconfiguration; + } +} diff --git a/yang/frr-deviations-bgp-datacenter.yang b/yang/frr-deviations-bgp-datacenter.yang new file mode 100644 index 0000000000..9d2725b253 --- /dev/null +++ b/yang/frr-deviations-bgp-datacenter.yang @@ -0,0 +1,106 @@ +module frr-deviations-bgp-datacenter { + yang-version 1.1; + namespace "http://frrouting.org/yang/frr-deviations-bgp-datacenter"; + prefix frr-deviations-bgp-dc; + + import frr-routing { + prefix frr-rt; + } + + import frr-bgp { + prefix frr-bgp; + } + + organization + "FRRouting"; + contact + "FRR Users List: FRR Development + List: "; + description + "This module defines deviations for the frr-bgp module with + datacenter profile. + + Copyright 2020 FRRouting + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."; + + revision 2019-12-03 { + description + "Initial revision."; + } + + deviation "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/frr-bgp:bgp/frr-bgp:global/frr-bgp:global-config-timers/frr-bgp:connect-retry-interval" { + deviate replace { + default "10"; + } + } + + deviation "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/frr-bgp:bgp/frr-bgp:global/frr-bgp:global-config-timers/frr-bgp:hold-time" { + deviate replace { + default "9"; + } + } + + deviation "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/frr-bgp:bgp/frr-bgp:global/frr-bgp:global-config-timers/frr-bgp:keepalive" { + deviate replace { + default "3"; + } + } + + deviation "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/frr-bgp:bgp/frr-bgp:global/frr-bgp:route-selection-options/frr-bgp:deterministic-med" { + deviate replace { + default "false"; + } + } + + deviation "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/frr-bgp:bgp/frr-bgp:global/frr-bgp:import-check" { + deviate replace { + default "true"; + } + } + + deviation "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/frr-bgp:bgp/frr-bgp:global/frr-bgp:show-hostname" { + deviate replace { + default "true"; + } + } + + deviation "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/frr-bgp:bgp/frr-bgp:global/frr-bgp:show-nexthop-hostname" { + deviate replace { + default "true"; + } + } + + deviation "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/frr-bgp:bgp/frr-bgp:global/frr-bgp:ebgp-requires-policy" { + deviate replace { + default "false"; + } + } + + deviation "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/frr-bgp:bgp/frr-bgp:global/frr-bgp:global-neighbor-config/frr-bgp:log-neighbor-changes" { + deviate replace { + default "true"; + } + } +} diff --git a/yang/frr-eigrpd.yang b/yang/frr-eigrpd.yang new file mode 100644 index 0000000000..3d1bf3baa5 --- /dev/null +++ b/yang/frr-eigrpd.yang @@ -0,0 +1,364 @@ +module frr-eigrpd { + yang-version 1.1; + namespace "http://frrouting.org/yang/eigrpd"; + prefix frr-eigrpd; + + import ietf-inet-types { + prefix inet; + } + import ietf-yang-types { + prefix yang; + } + import frr-interface { + prefix frr-interface; + } + import frr-route-types { + prefix frr-route-types; + } + + organization "FRRouting"; + contact + "FRR Users List: + FRR Development List: "; + description + "This module defines a model for managing FRR eigrpd daemon. + + Copyright 2020 FRRouting + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."; + + revision 2019-09-09 { + description + "Changed interface references to use + frr-interface:interface-ref typedef"; + } + revision 2019-06-19 { + description "Initial revision."; + reference + "RFC 7868: Cisco's Enhanced Interior Gateway Routing Protocol (EIGRP)."; + } + + /* + * Types specification. + */ + typedef autonomous-system { + description "Administrative domain identification for a network"; + type uint16 { + range 1..65535; + } + } + + typedef authentication-type { + description "Authentication types"; + type enumeration { + enum none { + description "Don't authenticate"; + value 0; + } + + enum text { + description "User defined text"; + value 1; + } + + enum md5 { + description "MD5 algorithm"; + value 2; + } + + enum hmac-sha-256 { + description "HMAC SHA256 algorithm"; + value 3; + } + } + } + + /* + * EIGRP operational data. + */ + container eigrpd { + list instance { + key "asn vrf"; + description "EIGRP autonomous system instance"; + + leaf asn { + description "Autonomous System Number"; + type autonomous-system; + } + + leaf vrf { + description "Virtual Routing Domain name"; + type string { + length "0..16"; + } + } + + /* + * Configurations. + */ + leaf router-id { + description "Router identification"; + type inet:ipv4-address; + } + + leaf-list passive-interface { + description "List of suppressed interfaces"; + type frr-interface:interface-ref; + } + + leaf active-time { + description "ACTIVE time limit in seconds (0 disables limit)"; + type uint16 { + range "0..65535"; + } + units seconds; + default 180; + } + + leaf variance { + description "Control load balance variance"; + type uint8 { + range "1..128"; + } + } + + leaf maximum-paths { + description "Most number of paths to forward packets to"; + type uint8 { + range "1..32"; + } + } + + container metric-weights { + description + "Metrics and parameters for advertisement. + + EIGRP calculates the composite metric with the following formula: + + metric = 256 * ({(K1*BW) + [(K2*BW)/(256-LOAD)] + (K3*DELAY)} * + (K5/(REL+K4))) + + Composite calculation: + K5 + metric =[(K1*Net-Throughput) + Latency)+(K6*ExtAttr)] * ------ + K4+Rel + + RFC 7868 Sections 5.5 and 5.6.2.5."; + + leaf K1 { + description "Bandwidth coefficient."; + type uint8 { + range "0..255"; + } + } + + leaf K2 { + description "Bandwidth on load coefficient."; + type uint8 { + range "0..255"; + } + } + + leaf K3 { + description "Delay or latency-based coefficient."; + type uint8 { + range "0..255"; + } + } + + leaf K4 { + description "Link quality coefficient."; + type uint8 { + range "0..255"; + } + } + + leaf K5 { + description "Packet loss coefficient."; + type uint8 { + range "0..255"; + } + } + + leaf K6 { + description "Jitter coefficient."; + type uint8 { + range "0..255"; + } + } + } + + leaf-list network { + description "Enable EIGRP on the specific network"; + type inet:ipv4-prefix; + } + + leaf-list neighbor { + description "Specific EIGRP neighbor"; + type inet:ipv4-address; + } + + list redistribute { + description "Redistribute routes learned from other routing protocols"; + + key "protocol"; + + leaf protocol { + description "Routing protocol"; + type frr-route-types:frr-route-types-v4; + must '. != "eigrp"'; + } + + leaf route-map { + description + "Applies the conditions of the specified route-map to + routes that are redistributed into the EIGRP routing + instance"; + type string { + length "1..max"; + } + } + + container metrics { + description "Metric for the redistributed routes"; + + leaf bandwidth { + description "Bandwidth metric in Kbits per second"; + type uint32 { + range "1..4294967295"; + } + } + + leaf delay { + description "Delay metric"; + units microseconds; + type uint32 { + range "0..4294967295"; + } + } + + leaf reliability { + description "Reliability metric"; + type uint32 { + range "0..255"; + } + } + + leaf load { + description "Effective bandwidth usage"; + type uint32 { + range "1..255"; + } + } + + leaf mtu { + description "Path Maximum Transmission Unit"; + type uint32 { + range "1..65535"; + } + } + } + } + } + } + + /* + * EIGRP interface configurations. + */ + augment "/frr-interface:lib/frr-interface:interface" { + container eigrp { + description "EIGRP interface parameters"; + + leaf delay { + description "Throughput delay"; + type uint32 { + range "1..16777215"; + } + default 10; + } + + leaf bandwidth { + description "Interface bandwidth value"; + type uint32 { + range "1..10000000"; + } + default 100000; + } + + leaf hello-interval { + description "Hello packet interval"; + type uint16 { + range "1..65535"; + } + units seconds; + default 5; + } + + leaf hold-time { + description "Timeout amount to consider neighbor down"; + type uint16 { + range "1..65535"; + } + units seconds; + default 15; + } + + leaf split-horizon { + description "Perform split horizon loop preventing technique"; + type boolean; + default true; + } + + /* + * Per AS configuration. + */ + list instance { + description "Autonomous System specific configuration"; + + key "asn"; + + leaf asn { + description "Autonomous System Number"; + type autonomous-system; + } + + leaf-list summarize-addresses { + description "Peform address summarization"; + type inet:ipv4-prefix; + } + + leaf authentication { + description "Authentication digest algorithm"; + type authentication-type; + default "none"; + } + + leaf keychain { + description "FRR key chain name to use with authentication"; + type string; + } + } + } + } +} diff --git a/yang/frr-filter.yang b/yang/frr-filter.yang new file mode 100644 index 0000000000..eb84dd7460 --- /dev/null +++ b/yang/frr-filter.yang @@ -0,0 +1,356 @@ +module frr-filter { + yang-version 1.1; + namespace "http://frrouting.org/yang/filter"; + prefix frr-filter; + + import ietf-inet-types { + prefix inet; + } + import ietf-yang-types { + prefix yang; + } + + organization "FRRouting"; + contact + "FRR Users List: + FRR Development List: "; + description + "This module defines filter settings + + Copyright 2020 FRRouting + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."; + + revision 2019-07-04 { + description "Initial revision"; + } + + /* + * Types. + */ + typedef access-list-name { + description "Access list name formatting"; + type string { + length 1..128; + } + } + + typedef access-list-sequence { + description "Access list sequence number"; + type uint32 { + range "1..4294967295"; + } + } + + typedef access-list-action { + description "Access list return action on match"; + type enumeration { + enum deny { + description "Deny an entry"; + value 0; + } + enum permit { + description "Accept an entry"; + value 1; + } + } + } + + /* + * Configuration data. + */ + container lib { + list access-list { + description "Access list instance"; + + key "type name"; + + leaf type { + description "Access list content type"; + type enumeration { + enum ipv4 { + description "Internet Protocol address version 4"; + value 0; + } + enum ipv6 { + description "Internet Protocol address version 6"; + value 1; + } + enum mac { + description "Media Access Control address"; + value 2; + } + } + } + + leaf name { + description "Access list name"; + type access-list-name; + } + + leaf remark { + description "Access list remark"; + type string; + } + + list entry { + description "Access list entry"; + + key "sequence"; + + leaf sequence { + description "Access list sequence value"; + type access-list-sequence; + } + + leaf action { + description "Access list action on match"; + type access-list-action; + mandatory true; + } + + choice value { + description "Access list value to match"; + mandatory true; + + case ipv4-prefix { + when "../type = 'ipv4'"; + + choice style { + description "Access list entry style selection: zebra or cisco."; + mandatory true; + + case zebra { + leaf ipv4-prefix { + description "Configure IPv4 prefix to match"; + type inet:ipv4-prefix; + } + + leaf ipv4-exact-match { + description "Exact match of prefix"; + type boolean; + default false; + } + } + case cisco { + choice standard-value { + description "Source value to match"; + + leaf host { + description "Host to match"; + type inet:ipv4-address; + } + container network { + leaf address { + mandatory true; + description "Network address part."; + type inet:ipv4-address; + } + leaf mask { + mandatory true; + description "Network mask/wildcard part."; + type inet:ipv4-address; + } + } + leaf source-any { + /* + * Was `any`, however it conflicts with `any` leaf + * outside this choice. + */ + description "Match any"; + type empty; + } + } + + choice extended-value { + description "Destination value to match"; + + leaf destination-host { + description "Host to match"; + type inet:ipv4-address; + } + container destination-network { + leaf address { + mandatory true; + description "Network address part."; + type inet:ipv4-address; + } + leaf mask { + mandatory true; + description "Network mask/wildcard part."; + type inet:ipv4-address; + } + } + leaf destination-any { + description "Match any"; + type empty; + } + } + } + } + } + case ipv6-prefix { + when "../type = 'ipv6'"; + + leaf ipv6-prefix { + description "Configure IPv6 prefix to match"; + type inet:ipv6-prefix; + } + + leaf ipv6-exact-match { + description "Exact match of prefix"; + type boolean; + default false; + } + } + case mac { + when "../type = 'mac'"; + + leaf mac { + description "Configure MAC address to match"; + type yang:mac-address; + } + } + case any { + leaf any { + description "Match anything"; + type empty; + } + } + } + } + } + + list prefix-list { + description "Prefix list instance"; + + key "type name"; + + leaf type { + description "Prefix list type"; + type enumeration { + enum ipv4 { + description "Internet Protocol address version 4"; + value 0; + } + enum ipv6 { + description "Internet Protocol address version 6"; + value 1; + } + } + } + + leaf name { + description "Prefix list name"; + type access-list-name; + } + + leaf remark { + description "Prefix list user description"; + type string; + } + + list entry { + description "Prefix list entry"; + + key "sequence"; + + leaf sequence { + description "Access list sequence value"; + type access-list-sequence; + } + + leaf action { + description "Prefix list action on match"; + type access-list-action; + mandatory true; + } + + choice value { + description "Prefix list value to match"; + mandatory true; + + case ipv4-prefix { + when "../type = 'ipv4'"; + + leaf ipv4-prefix { + description "Configure IPv4 prefix to match"; + type inet:ipv4-prefix; + } + + leaf ipv4-prefix-length-greater-or-equal { + description + "Specifies if matching prefixes with length greater than + or equal to value"; + type uint8 { + range "0..32"; + } + } + + leaf ipv4-prefix-length-lesser-or-equal { + description + "Specifies if matching prefixes with length lesser than + or equal to value"; + type uint8 { + range "0..32"; + } + } + } + case ipv6-prefix { + when "../type = 'ipv6'"; + + leaf ipv6-prefix { + description "Configure IPv6 prefix to match"; + type inet:ipv6-prefix; + } + + leaf ipv6-prefix-length-greater-or-equal { + description + "Specifies if matching prefixes with length greater than + or equal to value"; + type uint8 { + range "0..128"; + } + } + + leaf ipv6-prefix-length-lesser-or-equal { + description + "Specifies if matching prefixes with length lesser than + or equal to value"; + type uint8 { + range "0..128"; + } + } + } + case any { + leaf any { + description "Match anything"; + type empty; + } + } + } + } + } + } +} diff --git a/yang/frr-igmp.yang b/yang/frr-igmp.yang new file mode 100644 index 0000000000..b63d0f97ec --- /dev/null +++ b/yang/frr-igmp.yang @@ -0,0 +1,172 @@ +module frr-igmp { + yang-version "1.1"; + namespace "http://frrouting.org/yang/igmp"; + + prefix frr-igmp; + + import frr-routing { + prefix "frr-rt"; + } + + import ietf-routing-types { + prefix "rt-types"; + } + + import ietf-inet-types { + prefix "inet"; + } + + import frr-interface { + prefix frr-interface; + } + + organization + "FRRouting"; + + contact + "FRR Users List: + FRR Development List: "; + + description + "This module defines a model for managing FRR pimd daemon. + + Copyright 2020 FRRouting + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."; + + revision 2019-11-06 { + description + "Initial revision."; + reference + "RFC 2236: IGMP v2. + RFC 3376: IGMP v3."; + } + + grouping interface-config-attributes { + description + "Configuration attributes applied to the interface level."; + + leaf igmp-enable { + type boolean; + default "false"; + description + "Enable IGMP protocol on the interface."; + } + + leaf version { + type uint8 { + range "2..3"; + } + default "3"; + description + "IGMP version."; + } + + leaf query-interval { + type uint16 { + range "1..1800"; + } + units seconds; + default "125"; + description + "The Query Interval is the interval between General Queries + sent by the Querier."; + } + + leaf query-max-response-time { + type uint8 { + range "10..250"; + } + units deciseconds; + default "100"; + description + "Query maximum response time specifies the maximum time + allowed before sending a responding report."; + } + + leaf last-member-query-interval { + type uint8 { + range "1..255"; + } + units deciseconds; + default "10"; + description + "Last Member Query Interval, which may be tuned to modify + the leave latency of the network."; + } + + leaf robustness-variable { + type uint8 { + range "1..7"; + } + default "2"; + description + "Querier's Robustness Variable allows tuning for the + expected packet loss on a network."; + } + } + + grouping per-af-interface-config-attributes { + description + "Configuration attributes applied to the interface level per address family."; + + list static-group { + key "group-addr source-addr"; + description + "A static multicast route, (*,G) or (S,G). + The version of IGMP must be 3 to support (S,G)."; + + leaf group-addr { + type rt-types:ip-multicast-group-address; + description + "Multicast group address."; + } + leaf source-addr { + type inet:ip-address; + description + "Multicast source address."; + } + } + + } // per-af-interface-config-attributes + + /* + * Per-interface configuration data + */ + augment "/frr-interface:lib/frr-interface:interface" { + container igmp { + description + "IGMP interface parameters."; + uses interface-config-attributes; + list address-family { + key "address-family"; + description + "Each list entry for one address family."; + uses frr-rt:address-family; + uses per-af-interface-config-attributes; + + } //address-family + } + } +} diff --git a/yang/frr-interface.yang b/yang/frr-interface.yang index d3cc66dfaa..46c03a1d1f 100644 --- a/yang/frr-interface.yang +++ b/yang/frr-interface.yang @@ -3,25 +3,294 @@ module frr-interface { namespace "http://frrouting.org/yang/interface"; prefix frr-interface; + import frr-vrf { + prefix frr-vrf; + } + + import ietf-interfaces { + prefix ietf-if; + } + + import ietf-yang-types { + prefix yang; + } + organization - "Free Range Routing"; + "FRRouting"; contact "FRR Users List: FRR Development List: "; description - "This module defines a model for managing FRR interfaces."; + "This module defines a model for managing FRR interfaces. + + Copyright 2020 FRRouting + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."; + + revision 2020-02-05 { + description + "Added operational data"; + } + revision 2019-09-09 { + description + "Added interface-ref typedef"; + } revision 2018-03-28 { description "Initial revision."; } + identity other { + base ietf-if:interface-type; + description + "Other type"; + } + + identity unknown { + base ietf-if:interface-type; + description + "Unknown type"; + } + + identity ethernet { + base ietf-if:interface-type; + description + "Ethernet type"; + } + + identity exper-ethernet { + base ietf-if:interface-type; + description + "Experimental Ethernet type"; + } + + identity loopback { + base ietf-if:interface-type; + description + "Loopback type"; + } + + identity pimreg { + base ietf-if:interface-type; + description + "PIMSM Registration."; + } + + identity ipip { + base ietf-if:interface-type; + description + "IPIP Tunnel."; + } + + identity ipip6 { + base ietf-if:interface-type; + description + "IPIP6 Tunnel."; + } + + identity ipgre { + base ietf-if:interface-type; + description + "GRE over IP."; + } + + typedef interface-ref { + type leafref { + path "/frr-interface:lib/frr-interface:interface/frr-interface:name"; + require-instance false; + } + description + "Reference to an interface"; + } + + typedef if-flags-type { + type enumeration { + enum "up" { + value 1; + description + "Active and ready to transfer packets."; + } + enum "broadcast" { + value 2; + description + "Broadcast enabled."; + } + enum "debug" { + value 3; + description + "Debug mode."; + } + enum "loopback" { + value 4; + description + "Loopback interface."; + } + enum "point-to-point" { + value 5; + description + "Point-to-Point link."; + } + enum "notrailers" { + value 6; + description + "This flag is unused in Linux, but it exists for BSD compatibility. + Avoid use of trailers"; + } + enum "running" { + value 7; + description + "Up and Running."; + } + enum "noarp" { + value 8; + description + "Can't perform address resolution protocol."; + } + enum "promisc" { + value 9; + description + "Promiscuous mode. Receive all packets."; + } + enum "allmulti" { + value 10; + description + "Receive all multicast packets."; + } + enum "simplex" { + value 11; + description + "Does not Rx or Tx at the sametime."; + } + enum "link0" { + value 12; + description + "Link0."; + } + enum "link1" { + value 13; + description + "Link1."; + } + enum "link2" { + value 14; + description + "Link2."; + } + enum "multicast" { + value 15; + description + "Supports multicast transmission."; + } + enum "notransmit" { + value 16; + description + "Interface is no transmit mode."; + } + enum "nortexch" { + value 17; + description + "No routing info exchange."; + } + enum "virtual" { + value 18; + description + "Virtual interface."; + } + enum "ipv4" { + value 19; + description + "IPv4 enabled."; + } + enum "ipv6" { + value 20; + description + "IPv6 enabled."; + } + } + } + + grouping if-common-operational { + leaf if-index { + type int32 { + range "0..2147483647"; + } + description + "Interface index."; + } + + leaf mtu { + type uint16; + description + "The size of the largest IPV4 packet that the interface + will send and receive."; + } + + leaf mtu6 { + type uint32; + description + "The size of the largest IPV6 packet that the interface + will send and receive."; + } + + leaf speed { + type uint32; + description + "Interface speed."; + } + + leaf metric { + type uint32; + description + "Interface metric."; + } + + leaf flags { + type if-flags-type; + description + "Interface flags."; + } + + leaf type { + type identityref { + base ietf-if:interface-type; + } + description + "The link type of the interface."; + } + + leaf phy-address { + type yang:mac-address; + description + "The interface's MAC address."; + } + } + container lib { list interface { key "name vrf"; description "Interface."; - leaf name { type string { length "1..16"; @@ -29,18 +298,23 @@ module frr-interface { description "Interface name."; } + leaf vrf { - type string { - length "1..36"; - } + type frr-vrf:vrf-ref; description "VRF this interface is associated with."; } + leaf description { type string; description "Interface description."; } + + container state { + config false; + uses if-common-operational; + } } } } diff --git a/yang/frr-isisd.yang b/yang/frr-isisd.yang index 7b132cb61e..00296516ef 100644 --- a/yang/frr-isisd.yang +++ b/yang/frr-isisd.yang @@ -20,13 +20,52 @@ module frr-isisd { } organization - "Free Range Routing"; + "FRRouting"; contact "FRR Users List: FRR Development List: "; description - "This module defines a model for managing FRR isisd daemon."; - + "This module defines a model for managing FRR isisd daemon. + + Copyright 2020 FRRouting + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."; + + revision 2020-04-06 { + description + "Group LSP timers in a container so that they can be displayed and + configured together"; + } + revision 2019-12-17 { + description + "Changed default area is-type to level-1-2"; + } + revision 2019-09-09 { + description + "Changed interface references to use + frr-interface:interface-ref typedef"; + } revision 2018-07-26 { description "Initial revision."; @@ -56,6 +95,13 @@ module frr-isisd { "This type defines IS-IS level of an object."; } + typedef extended-circuit-id { + type uint32; + description + "This type defines the extended circuit ID + associated with an interface."; + } + typedef network-type { type enumeration { enum "unknown" { @@ -90,6 +136,20 @@ module frr-isisd { pattern, An example LSP ID is 0143.0438.AeF0.02-01"; } + typedef snpa { + type string { + length "0 .. 20"; + } + description + "This type defines the Subnetwork Point + of Attachment (SNPA) format. + The SNPA should be encoded according to the rules + specified for the particular type of subnetwork + being used. As an example, for an ethernet subnetwork, + the SNPA is encoded as a MAC address like + '00aa.bbcc.ddee'."; + } + typedef system-id { type string { pattern "[0-9A-Fa-f]{4}\\.[0-9A-Fa-f]{4}\\.[0-9A-Fa-f]{4}"; @@ -270,6 +330,428 @@ module frr-isisd { } } + grouping interface-config { + description + "Interface configuration grouping"; + leaf area-tag { + type string; + mandatory true; + description + "Area-tag associated to this circuit."; + } + + leaf vrf { + type string; + default "default"; + description + "VRF NAME."; + } + + leaf ipv4-routing { + type boolean; + default "false"; + description + "Routing IS-IS IPv4 traffic over this circuit."; + } + + leaf ipv6-routing { + type boolean; + default "false"; + description + "Routing IS-IS IPv6 traffic over this circuit."; + } + + leaf circuit-type { + type level; + default "level-1-2"; + description + "IS-type of this circuit."; + } + + container bfd-monitoring { + leaf enabled { + type boolean; + default "false"; + description + "Monitor IS-IS peers on this circuit."; + } + leaf profile { + type string; + description + "Let BFD use a pre-configured profile."; + } + } + + container csnp-interval { + description + "Complete Sequence Number PDU (CSNP) generation interval."; + leaf level-1 { + type uint16 { + range "1..600"; + } + units "seconds"; + default "10"; + description + "CNSP interval for level-1"; + } + + leaf level-2 { + type uint16 { + range "1..600"; + } + units "seconds"; + default "10"; + description + "CNSP interval for level-2"; + } + } + + container psnp-interval { + description + "Partial Sequence Number PDU (PSNP) generation interval."; + leaf level-1 { + type uint16 { + range "1..120"; + } + units "seconds"; + default "2"; + description + "PNSP interval for level-1"; + } + + leaf level-2 { + type uint16 { + range "1..120"; + } + units "seconds"; + default "2"; + description + "PCNSP interval for level-2"; + } + } + + container hello { + description + "Parameters related to IS-IS hello PDUs."; + leaf padding { + type boolean; + default "true"; + description + "Add padding to IS-IS hello PDUs."; + } + + container interval { + description + "Interval between consecutive hello messages."; + leaf level-1 { + type uint32 { + range "1..600"; + } + units "seconds"; + default "3"; + description + "Holding time for level-1; interval will depend on multiplier."; + } + + leaf level-2 { + type uint32 { + range "1..600"; + } + units "seconds"; + default "3"; + description + "Holding time for level-2; interval will depend on multiplier."; + } + } + + container multiplier { + description + "Multiplier for the hello messages holding time."; + leaf level-1 { + type uint16 { + range "2..100"; + } + default "10"; + description + "Multiplier for the hello holding time."; + } + + leaf level-2 { + type uint16 { + range "2..100"; + } + default "10"; + description + "Multiplier for the hello holding time."; + } + } + } + + container metric { + description + "Default metric for this IS-IS circuit."; + leaf level-1 { + type uint32 { + range "0..16777215"; + } + must ". < 64 or /frr-isisd:isis/instance[area-tag = current()/../../area-tag]/metric-style = 'wide'"; + default "10"; + description + "Default level-1 metric for this IS-IS circuit."; + } + + leaf level-2 { + type uint32 { + range "0..16777215"; + } + must ". < 64 or /frr-isisd:isis/instance[area-tag = current()/../../area-tag]/metric-style = 'wide'"; + default "10"; + description + "Default level-2 metric for this IS-IS circuit."; + } + } + + container priority { + description + "Priority for Designated Router election."; + leaf level-1 { + type uint8 { + range "0..127"; + } + default "64"; + description + "Level-1 priority for this IS-IS circuit."; + } + + leaf level-2 { + type uint8 { + range "0..127"; + } + default "64"; + description + "Level-2 priority for this IS-IS circuit."; + } + } + + leaf network-type { + type network-type; + must "(. = \"point-to-point\") or (. = \"broadcast\")"; + default "broadcast"; + description + "Explicitly configured type of IS-IS circuit (broadcast or point-to-point)."; + } + + leaf passive { + type boolean; + default "false"; + description + "Interface is in passive mode."; + } + + container password { + presence "Present if a password is set for this IS interface."; + uses isis-password; + } + + leaf disable-three-way-handshake { + type boolean; + default "false"; + description + "Disables three-way handshake when creating new adjacencies."; + } + + container multi-topology { + description + "IS-IS topologies configured on this circuit."; + leaf ipv4-unicast { + type boolean; + default "true"; + description + "IPv4 unicast topology."; + } + + leaf ipv4-multicast { + type boolean; + default "true"; + description + "IPv4 multicast topology."; + } + + leaf ipv4-management { + type boolean; + default "true"; + description + "IPv4 management topology."; + } + + leaf ipv6-unicast { + type boolean; + default "true"; + description + "IPv6 unicast topology."; + } + + leaf ipv6-multicast { + type boolean; + default "true"; + description + "IPv6 multicast topology."; + } + + leaf ipv6-management { + type boolean; + default "true"; + description + "IPv6 management topology."; + } + + leaf ipv6-dstsrc { + type boolean; + default "true"; + description + "IPv6 destination-source topology."; + } + } + } + + grouping adjacency-state { + description + "Adjacency state"; + container adjacencies { + config false; + description + "This container lists the adjacencies of + the local node."; + list adjacency { + description + "List of operational adjacencies."; + leaf neighbor-sys-type { + type level; + description + "Level capability of neighboring system"; + } + + leaf neighbor-sysid { + type system-id; + description + "The system-id of the neighbor"; + } + + leaf neighbor-extended-circuit-id { + type extended-circuit-id; + description + "Circuit ID of the neighbor"; + } + + leaf neighbor-snpa { + type snpa; + description + "SNPA of the neighbor"; + } + + leaf hold-timer { + type uint16; + units "seconds"; + description + "The holding time in seconds for this + adjacency. This value is based on + received hello PDUs and the elapsed + time since receipt."; + } + + leaf neighbor-priority { + type uint8 { + range "0 .. 127"; + } + description + "Priority of the neighboring IS for becoming + the DIS."; + } + + leaf state { + type adj-state-type; + description + "This leaf describes the state of the interface."; + } + } + } + } + + grouping event-counters { + description + "Grouping for IS-IS interface event counters"; + container event-counters { + config false; + description + "IS-IS interface event counters."; + leaf adjacency-changes { + type uint32; + description + "The number of times an adjacency state change has + occurred on this interface."; + } + + leaf adjacency-number { + type uint32; + description + "The number of adjacencies on this interface."; + } + + leaf init-fails { + type uint32; + description + "The number of times initialization of this + interface has failed. This counts events such + as PPP NCP failures. Failures to form an + adjacency are counted by adjacency-rejects."; + } + + leaf adjacency-rejects { + type uint32; + description + "The number of times an adjacency has been + rejected on this interface."; + } + + leaf id-len-mismatch { + type uint32; + description + "The number of times an IS-IS PDU with an ID + field length different from that for this + system has been received on this interface."; + } + + leaf max-area-addresses-mismatch { + type uint32; + description + "The number of times an IS-IS PDU has been + received on this interface with the + max area address field differing from that of + this system."; + } + + leaf authentication-type-fails { + type uint32; + description + "Number of authentication type mismatches."; + } + + leaf authentication-fails { + type uint32; + description + "Number of authentication key failures."; + } + } + } + + grouping interface-state { + description + "IS-IS interface operational state."; + uses adjacency-state; + + uses event-counters; + } + grouping notification-instance-hdr { description "Instance specific IS-IS notification data grouping"; @@ -296,7 +778,7 @@ module frr-isisd { description "Interface specific IS-IS notification data grouping"; leaf interface-name { - type string; + type frr-interface:interface-ref; description "IS-IS interface name"; } @@ -308,7 +790,7 @@ module frr-isisd { } leaf extended-circuit-id { - type uint32; + type extended-circuit-id; description "Eextended circuit-id of the interface."; } @@ -318,7 +800,7 @@ module frr-isisd { description "Configuration of the IS-IS routing daemon."; list instance { - key "area-tag"; + key "area-tag vrf"; description "IS-IS routing instance."; leaf area-tag { @@ -327,9 +809,15 @@ module frr-isisd { "Area-tag associated to this routing instance."; } + leaf vrf { + type string; + description + "VRF NAME."; + } + leaf is-type { type level; - default "level-1"; + default "level-1-2"; description "Level of the IS-IS routing instance (OSI only)."; } @@ -391,75 +879,75 @@ module frr-isisd { "MTU of an LSP."; } - container refresh-interval { + container timers { description - ""; - leaf level-1 { - type uint16; - units "seconds"; - default "900"; + "LSP-related timers"; + container level-1 { description - "LSP refresh interval for level-1."; - } + "Level-1 LSP-related timers"; + leaf refresh-interval { + type uint16; + units "seconds"; + default "900"; + description + "LSP refresh interval for level-1."; + } - leaf level-2 { - type uint16; - units "seconds"; - default "900"; - description - "LSP refresh interval for level-2."; - } - } + leaf maximum-lifetime { + type uint16 { + range "350..65535"; + } + units "seconds"; + must ". >= ../refresh-interval + 300"; + default "1200"; + description + "Maximum LSP lifetime for level-1."; + } - container maximum-lifetime { - description - "Maximum LSP lifetime."; - leaf level-1 { - type uint16 { - range "350..65535"; + leaf generation-interval { + type uint16 { + range "1..120"; + } + units "seconds"; + must ". < ../refresh-interval"; + default "30"; + description + "Minimum time allowed before level-1 LSP retransmissions."; } - units "seconds"; - must ". >= ../../refresh-interval/level-1 + 300"; - default "1200"; - description - "Maximum LSP lifetime for level-1."; } - leaf level-2 { - type uint16 { - range "350..65535"; - } - units "seconds"; - must ". >= ../../refresh-interval/level-2 + 300"; - default "1200"; + container level-2 { description - "Maximum LSP lifetime for level-2."; - } - } + "Level-2 LSP-related timers"; + leaf refresh-interval { + type uint16; + units "seconds"; + default "900"; + description + "LSP refresh interval for level-2."; + } - container generation-interval { - description - "Minimum LSP regeneration interval."; - leaf level-1 { - type uint16 { - range "1..120"; + leaf maximum-lifetime { + type uint16 { + range "350..65535"; + } + units "seconds"; + must ". >= ../refresh-interval + 300"; + default "1200"; + description + "Maximum LSP lifetime for level-2."; } - units "seconds"; - must ". < ../../refresh-interval/level-1"; - default "30"; - description - "Minimum time allowed before level-1 LSP retransmissions."; - } - leaf level-2 { - type uint16 { - range "1..120"; + leaf generation-interval { + type uint16 { + range "1..120"; + } + units "seconds"; + must ". < ../refresh-interval"; + default "30"; + description + "Minimum time allowed before level-2 LSP retransmissions."; } - units "seconds"; - must ". < ../../refresh-interval/level-2"; - default "30"; - description - "Minimum time allowed before level-2 LSP retransmissions."; } } } @@ -718,274 +1206,147 @@ module frr-isisd { "Stable IP address of the advertising router."; } } - } - } - - augment "/frr-interface:lib/frr-interface:interface" { - description - "Extends interface model with IS-IS related parameters."; - container isis { - presence "Present if an IS-IS circuit is defined for this interface."; - description - "IS-IS interface parameters."; - leaf area-tag { - type string; - mandatory true; - description - "Area-tag associated to this circuit."; - } - - leaf ipv4-routing { - type boolean; - default "false"; - description - "Routing IS-IS IPv4 traffic over this circuit."; - } - - leaf ipv6-routing { - type boolean; - default "false"; - description - "Routing IS-IS IPv6 traffic over this circuit."; - } - - leaf circuit-type { - type level; - default "level-1-2"; - description - "IS-type of this circuit."; - } - - container csnp-interval { - description - "Complete Sequence Number PDU (CSNP) generation interval."; - leaf level-1 { - type uint16 { - range "1..600"; - } - units "seconds"; - default "10"; - description - "CNSP interval for level-1"; - } - - leaf level-2 { - type uint16 { - range "1..600"; - } - units "seconds"; - default "10"; - description - "CNSP interval for level-2"; - } - } - - container psnp-interval { - description - "Partial Sequence Number PDU (PSNP) generation interval."; - leaf level-1 { - type uint16 { - range "1..120"; - } - units "seconds"; - default "2"; - description - "PNSP interval for level-1"; - } - - leaf level-2 { - type uint16 { - range "1..120"; - } - units "seconds"; - default "2"; - description - "PCNSP interval for level-2"; - } - } - container hello { + container segment-routing { description - "Parameters related to IS-IS hello PDUs."; - leaf padding { + "Segment Routing global configuration."; + leaf enabled { type boolean; - default "true"; + default "false"; description - "Add padding to IS-IS hello PDUs."; + "Enables segment-routing protocol extensions."; } - - container interval { + container srgb { description - "Interval between consecutive hello messages."; - leaf level-1 { - type uint32 { - range "1..600"; - } - units "seconds"; - default "3"; - description - "Holding time for level-1; interval will depend on multiplier."; + "Global blocks to be advertised."; + must "./upper-bound > ./lower-bound"; + leaf lower-bound { + type uint32; + default "16000"; + description + "Lower value in the label range."; } - - leaf level-2 { - type uint32 { - range "1..600"; - } - units "seconds"; - default "3"; - description - "Holding time for level-2; interval will depend on multiplier."; + leaf upper-bound { + type uint32; + default "23999"; + description + "Upper value in the label range."; } } - - container multiplier { + container srlb { description - "Multiplier for the hello messages holding time."; - leaf level-1 { - type uint16 { - range "2..100"; - } - default "10"; - description - "Multiplier for the hello holding time."; + "Local blocks to be advertised."; + must "./upper-bound > ./lower-bound"; + leaf lower-bound { + type uint32; + default "15000"; + description + "Lower value in the label range."; } - - leaf level-2 { - type uint16 { - range "2..100"; - } - default "10"; - description - "Multiplier for the hello holding time."; + leaf upper-bound { + type uint32; + default "15999"; + description + "Upper value in the label range."; } } - } - - container metric { - description - "Default metric for this IS-IS circuit."; - leaf level-1 { - type uint32 { - range "0..16777215"; - } - must ". < 64 or /frr-isisd:isis/instance[area-tag = current()/../../area-tag]/metric-style = 'wide'"; - default "10"; + container msd { description - "Default level-1 metric for this IS-IS circuit."; - } - - leaf level-2 { - type uint32 { - range "0..16777215"; + "MSD configuration."; + leaf node-msd { + type uint8; + description + "Node MSD is the lowest MSD supported by the node."; } - must ". < 64 or /frr-isisd:isis/instance[area-tag = current()/../../area-tag]/metric-style = 'wide'"; - default "10"; - description - "Default level-2 metric for this IS-IS circuit."; } - } - - container priority { - description - "Priority for Designated Router election."; - leaf level-1 { - type uint8 { - range "0..127"; - } - default "64"; + container prefix-sid-map { description - "Level-1 priority for this IS-IS circuit."; - } - - leaf level-2 { - type uint8 { - range "0..127"; + "Prefix SID configuration."; + list prefix-sid { + key "prefix"; + unique "sid-value-type sid-value"; + description + "List of prefix SID mapped to IPv4/IPv6 + local prefixes."; + leaf prefix { + type inet:ip-prefix; + description + "Connected prefix sid."; + } + leaf sid-value-type { + type enumeration { + enum "index" { + value 0; + description + "The value will be interpreted as an index."; + } + enum "absolute" { + value 1; + description + "The value will become interpreted as an absolute + value."; + } + } + default "index"; + description + "This leaf defines how value must be interpreted."; + } + leaf sid-value { + type uint32; + mandatory true; + description + "Value associated with prefix. The value must be + interpreted in the context of sid-value-type."; + } + leaf last-hop-behavior { + type enumeration { + enum "explicit-null" { + value 0; + description + "Use explicit-null for the SID."; + } + enum "no-php" { + value 1; + description + "Do not use Penultimate Hop Popping (PHP) + for the SID."; + } + enum "php" { + value 2; + description + "Use PHP for the SID."; + } + } + default "php"; + description + "Configure last hop behavior."; + } } - default "64"; - description - "Level-2 priority for this IS-IS circuit."; } } + } + } - leaf network-type { - type network-type; - default "broadcast"; - must "(. = \"point-to-point\") or (. = \"broadcast\")"; - description - "Explicitly configured type of IS-IS circuit (broadcast or point-to-point)."; - } - - leaf passive { - type boolean; - default "false"; - description - "Interface is in passive mode."; - } - - container password { - presence "Present if a password is set for this IS interface."; - uses isis-password; - } - - leaf disable-three-way-handshake { - type boolean; - default "false"; - description - "Disables three-way handshake when creating new adjacencies."; - } - - container multi-topology { - description - "IS-IS topologies configured on this circuit."; - leaf ipv4-unicast { - type boolean; - default "true"; - description - "IPv4 unicast topology."; - } - - leaf ipv4-multicast { - type boolean; - default "true"; - description - "IPv4 multicast topology."; - } - - leaf ipv4-management { - type boolean; - default "true"; - description - "IPv4 management topology."; - } - - leaf ipv6-unicast { - type boolean; - default "true"; - description - "IPv6 unicast topology."; - } - - leaf ipv6-multicast { - type boolean; - default "true"; - description - "IPv6 multicast topology."; - } + augment "/frr-interface:lib/frr-interface:interface" { + description + "Extends interface model with IS-IS related parameters."; + container isis { + presence "Present if an IS-IS circuit is defined for this interface."; + description + "IS-IS interface parameters."; + uses interface-config; + } + } - leaf ipv6-management { - type boolean; - default "true"; - description - "IPv6 management topology."; - } + augment "/frr-interface:lib/frr-interface:interface/frr-interface:state" { + description + "Extends interface model with IS-IS operational data."; + container isis { + presence "Present if an IS-IS circuit is defined for this interface."; + description + "IS-IS interface operational data."; - leaf ipv6-dstsrc { - type boolean; - default "true"; - description - "IPv6 destination-source topology."; - } - } + uses interface-state; } } @@ -1018,7 +1379,7 @@ module frr-isisd { "This notification is sent when we attempt to propagate an LSP that is larger than the dataLinkBlockSize for the circuit. The notification generation must be throttled - with at least 5 seconds betweeen successive + with at least 5 seconds between successive notifications."; uses notification-instance-hdr; @@ -1084,7 +1445,7 @@ module frr-isisd { "This notification is sent when we receive a PDU with a different value for the System ID length. The notification generation must be throttled - with at least 5 seconds betweeen successive + with at least 5 seconds between successive notifications."; uses notification-instance-hdr; @@ -1108,7 +1469,7 @@ module frr-isisd { "This notification is sent when we receive a PDU with a different value for the Maximum Area Addresses. The notification generation must be throttled - with at least 5 seconds betweeen successive + with at least 5 seconds between successive notifications."; uses notification-instance-hdr; @@ -1164,7 +1525,7 @@ module frr-isisd { "This notification is sent when the system receives a PDU with the wrong authentication type field. The notification generation must be throttled - with at least 5 seconds betweeen successive + with at least 5 seconds between successive notifications."; uses notification-instance-hdr; @@ -1182,7 +1543,7 @@ module frr-isisd { "This notification is sent when the system receives a PDU with the wrong authentication information. The notification generation must be throttled with - with at least 5 seconds betweeen successive + with at least 5 seconds between successive notifications."; uses notification-instance-hdr; @@ -1200,7 +1561,7 @@ module frr-isisd { "This notification is sent when the system receives a PDU with a different protocol version number. The notification generation must be throttled - with at least 5 seconds betweeen successive + with at least 5 seconds between successive notifications."; uses notification-instance-hdr; @@ -1224,7 +1585,7 @@ module frr-isisd { "This notification is sent when the system receives a Hello PDU from an IS that does not share any area address. The notification generation must be throttled - with at least 5 seconds betweeen successive + with at least 5 seconds between successive notifications."; uses notification-instance-hdr; @@ -1242,7 +1603,7 @@ module frr-isisd { "This notification is sent when the system receives a Hello PDU from an IS but does not establish an adjacency for some reason. The notification generation must be - throttled with at least 5 seconds betweeen successive + throttled with at least 5 seconds between successive notifications."; uses notification-instance-hdr; @@ -1267,7 +1628,7 @@ module frr-isisd { description "This notification is sent when the system receives an LSP with a parse error. The notification generation must - be throttled with at least 5 seconds betweeen successive + be throttled with at least 5 seconds between successive notifications."; uses notification-instance-hdr; @@ -1344,7 +1705,7 @@ module frr-isisd { description "This notification is sent when an LSP is received. The notification generation must be throttled with at - least 5 seconds betweeen successive notifications."; + least 5 seconds between successive notifications."; uses notification-instance-hdr; uses notification-interface-hdr; @@ -1378,7 +1739,7 @@ module frr-isisd { description "This notification is sent when an LSP is regenerated. The notification generation must be throttled with at - least 5 seconds betweeen successive notifications."; + least 5 seconds between successive notifications."; uses notification-instance-hdr; leaf lsp-id { diff --git a/yang/frr-module-translator.yang b/yang/frr-module-translator.yang index 3d64ec5399..90d3cc8601 100644 --- a/yang/frr-module-translator.yang +++ b/yang/frr-module-translator.yang @@ -4,12 +4,37 @@ module frr-module-translator { prefix frr-module-translator; organization - "Free Range Routing"; + "FRRouting"; contact "FRR Users List: FRR Development List: "; description - "A model for FRR YANG module translators."; + "A model for FRR YANG module translators. + + Copyright 2020 FRRouting + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."; revision 2018-07-31 { description diff --git a/yang/frr-nexthop.yang b/yang/frr-nexthop.yang new file mode 100644 index 0000000000..0cb0f93ee4 --- /dev/null +++ b/yang/frr-nexthop.yang @@ -0,0 +1,318 @@ +module frr-nexthop { + yang-version 1.1; + namespace "http://frrouting.org/yang/nexthop"; + prefix frr-nexthop; + + import ietf-inet-types { + prefix inet; + } + + import ietf-routing-types { + prefix rt-types; + } + + import frr-interface { + prefix frr-interface; + } + + import frr-vrf { + prefix frr-vrf; + } + + organization + "FRRouting"; + contact + "FRR Users List: + FRR Development List: "; + description + "This module defines a model for managing FRR nexthop information. + + Copyright 2020 FRRouting + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."; + + revision 2019-08-15 { + description + "Initial revision."; + } + + typedef optional-ip-address { + type union { + type inet:ip-address; + type string { + pattern ''; + } + } + } + + /* + * Nexthop types. + */ + typedef nexthop-type { + type enumeration { + enum "ifindex" { + value 1; + description + "Specific interface."; + } + enum "ip4" { + value 2; + description + "IPv4 address."; + } + enum "ip4-ifindex" { + value 3; + description + "IPv4 address and interface."; + } + enum "ip6" { + value 4; + description + "IPv6 address."; + } + enum "ip6-ifindex" { + value 5; + description + "IPv6 address and interface."; + } + enum "blackhole" { + value 6; + description + "Unreachable or prohibited."; + } + } + description + "Nexthop types."; + } + + /* + * Blockhole nexthop type. + */ + typedef blackhole-type { + type enumeration { + enum "unspec" { + value 0; + description + "Generic unreachable."; + } + enum "null" { + value 1; + description + "Null type."; + } + enum "reject" { + value 2; + description + "ICMP unreachable."; + } + enum "prohibited" { + value 3; + description + "ICMP admin-prohibited."; + } + } + default "null"; + description + "Nexthop blackhole types."; + } + + typedef nexthop-group-ref { + type leafref { + path "/frr-nexthop:frr-nexthop-group/frr-nexthop:nexthop-groups/frr-nexthop:name"; + require-instance false; + } + } + + /* + * Common nexthop attributes grouping. + */ + grouping frr-nexthop-attributes { + leaf nh-type { + type nexthop-type; + mandatory true; + description + "The nexthop type."; + } + + leaf vrf { + type string; + description + "The nexthop vrf name, if different from the route."; + } + leaf gateway { + type frr-nexthop:optional-ip-address; + description + "The nexthop gateway address."; + } + + leaf interface { + type string; + description + "The nexthop egress interface."; + } + + leaf bh-type { + when "../nh-type = 'blackhole'"; + type blackhole-type; + description + "A blackhole sub-type, if the nexthop is a blackhole type."; + } + + leaf onlink { + when "../nh-type = 'ip4-ifindex' or + ../nh-type = 'ip6-ifindex'"; + type boolean; + default "false"; + description + "Nexthop is directly connected."; + } + + leaf srte-color { + when "../nh-type = 'ip4' or + ../nh-type = 'ip6' or + ../nh-type = 'ip4-ifindex' or + ../nh-type = 'ip6-ifindex'"; + type uint32; + description + "The nexthop SR-TE color"; + } + + uses rt-types:mpls-label-stack { + description + "Nexthop's MPLS label stack."; + } + } + + /* + * operational common attributes for nexthop + */ + grouping frr-nexthop-operational { + leaf duplicate { + type empty; + config false; + description + "Duplicate nexthop"; + } + + leaf recursive { + type empty; + config false; + description + "Nexthop resolved through another gateway."; + } + + leaf active { + type empty; + config false; + description + "Nexthop is active."; + } + + leaf fib { + type empty; + config false; + description + "Nexthop is installed in fib."; + } + + leaf weight { + type uint8; + config false; + description + "Weight to be used by the nexthop for purposes of ECMP"; + } + } + + grouping nexthop-grouping { + list nexthop { + key "nh-type vrf gateway interface"; + min-elements 1; + description + "A list of nexthop objects."; + uses frr-nexthop-attributes; + } + } + + /* + * Single nexthop grouping. + */ + grouping frr-nexthop { + container frr-nexthops { + description + "FRR nexthop object."; + uses nexthop-grouping; + } + } + + + /* + * Container for FRR nexthop group. + */ + grouping frr-nexthop-grouping { + list nexthop-groups { + key "name"; + description + "List of nexthop groups, each contains group of nexthops"; + leaf name { + type string; + description + "The nexthop-group name."; + } + + uses frr-nexthop; + } + } + + /* Operational nexthop-group */ + grouping frr-nexthop-group-operational { + container nexthop-group { + description + "A group of nexthops."; + leaf id { + type uint32; + description + "The nexthop-group id."; + } + + uses nexthop-grouping; + } + } + + container frr-nexthop-group { + description + "A nexthop-group, represented as a list of nexthop objects."; + uses frr-nexthop-grouping; + } + + /* + * Augment weight attributes to nexthop group. + */ + augment "/frr-nexthop-group/nexthop-groups/frr-nexthops/nexthop" { + leaf weight { + type uint8; + description + "Weight to be used by the nexthop for purposes of ECMP"; + } + } +} diff --git a/yang/frr-ospfd.yang b/yang/frr-ospfd.yang new file mode 100644 index 0000000000..466dd42ce4 --- /dev/null +++ b/yang/frr-ospfd.yang @@ -0,0 +1,1002 @@ +module frr-ospfd { + yang-version 1.1; + namespace "http://frrouting.org/yang/ospfd"; + prefix frr-ospfd; + + import frr-routing { + prefix frr-rt; + } + + import ietf-inet-types { + prefix inet; + } + + import frr-interface { + prefix frr-interface; + } + + import frr-route-types { + prefix frr-route-types; + } + + organization + "FRRouting"; + contact + "FRR Users List: + FRR Development List: "; + description + "This module defines a model for managing FRR OSPFd information. + This YANG module augments the basic routing with additional + OSPF information"; + + revision 2020-07-21 { + description + "Initial revision."; + } + + identity ospf { + base frr-rt:routing-protocol; + description + "'OSPF' routing ospf-protocol."; + } + + /* Policy types to be removed later, once policy Yang finalized */ + typedef rmap-ref { + type string; + } + + typedef plist-ref { + type string; + } + + typedef access-list-ref { + type string; + } + + typedef ospf-area-id { + type union { + type inet:ipv4-address; + type uint32; + } + description "OSPF Area ID."; + } + + grouping nssa-grouping { + container nssa { + presence "Present if the nssa is enabled"; + leaf no-summary { + type boolean; + default "false"; + description + "Do not inject inter-area routes into nssa"; + } + + leaf translate-always { + type boolean; + default "false"; + description + "NSSA-ABR to always translate"; + } + + leaf translate-candidate { + type boolean; + default "false"; + description + "NSSA-ABR for translate election"; + } + + leaf translate-never { + type boolean; + default "false"; + description + "NSSA-ABR to never translate"; + } + } + } + + grouping range-grouping { + container ranges { + list range { + key "prefix"; + description + "A list of range objects"; + leaf prefix { + type inet:ipv4-prefix; + description + "Area range prefix"; + } + + leaf advertise { + type boolean; + description + "Advertise this range"; + } + + leaf not-advertise { + type boolean; + default "false"; + description + "Do not advertise this range"; + } + + leaf cost { + type uint32 { + range "0..16777215"; + } + description + "Metric for this range"; + } + + leaf substitute { + type inet:ipv4-prefix; + description + "Network prefix to be announced instead of range"; + } + } + } + } + + grouping stub-grouping { + container stub { + presence "Present when area is stub"; + leaf no-summary { + type boolean; + default "false"; + description + "Do not inject inter-area routes into stub"; + } + } + } + + grouping shortcut-grouping { + container shortcut { + leaf default { + type boolean; + default "false"; + description + "Default shortcutting behavior"; + } + + leaf disable { + type boolean; + description + "Disable shortcutting through the area"; + } + + leaf enable { + type boolean; + description + "Enable shortcutting through the area"; + } + } + } + + grouping authentication-group { + container authentication { + presence "Enable authentication."; + description + "Enable authentication on this virtual link."; + leaf message-digest { + type boolean; + description + "Use message-digest authentication."; + } + + leaf null { + type boolean; + description + "Use null authentication."; + } + } + + list message-digest-key { + key "key-id"; + leaf key-id { + type uint8; + description + "Key id"; + } + + leaf mds-key { + type string; + description + "The OSPF password."; + } + } + + leaf authentication-key { + type string; + description + "The OSPF password."; + } + } + + grouping virtual-link-group { + list virtual-link { + key "neighbor area-id"; + description + "Virtual link parameters."; + leaf neighbor { + type inet:ipv4-address; + description + "Router ID of the remote ABR."; + } + + leaf area-id { + mandatory true; + type ospf-area-id; + } + + uses authentication-group; + + container timers { + leaf dead-interval { + type uint16; + units "seconds"; + description + "Interval time after which a neighbor is declared down."; + } + + leaf hello-interval { + type uint16 { + range "1..65535"; + } + units "seconds"; + description + "Time between HELLO packets."; + } + + leaf retransmit-interval { + type uint16 { + range "1..65535"; + } + units "seconds"; + description + "Time between retransmitting lost link state advertisements."; + } + + leaf transmit-delay { + type uint16 { + range "1..65535"; + } + units "seconds"; + description + "Link state transmit delay."; + } + } + } + } + + grouping area-groupings { + container areas { + list area { + key "area-id"; + description + "A list of area objects"; + leaf area-id { + type ospf-area-id; + } + + container authentication { + presence "Enable authentication"; + leaf message-digest { + type boolean; + description + "Use message-digest authentication"; + } + } + + leaf default-cost { + type uint32 { + range "0..16777215"; + } + description + "Advertised default summary cost"; + } + + leaf export-list { + type access-list-ref; + description + "Filter for networks announced to other areas."; + } + + leaf import-list { + type access-list-ref; + description + "Filter for networks from other areas announced to the specified one."; + } + + container filter-list { + leaf prefix { + type plist-ref; + description + "Filter networks between OSPF areas."; + } + + leaf in { + type boolean; + } + + leaf out { + type boolean; + } + } + + uses nssa-grouping; + + uses range-grouping; + + uses stub-grouping; + + uses shortcut-grouping; + + uses virtual-link-group; + } + } + } + + /* router ospf attributes */ + grouping route-ospf-leaf-attributes { + leaf auto-cost-reference-bandwidth { + type uint32 { + range "1..4294967"; + } + units "Mbits"; + description + "The reference bandwidth in terms of Mbits per second."; + } + + leaf use-arp { + type boolean; + default "true"; + description + "ARP for neighbor table entry."; + } + + leaf capability-opaque { + type boolean; + default "false"; + description + "Opaque LSA."; + } + + leaf compatible-rfc1583 { + type boolean; + description + "Compatible with RFC 1583."; + } + + leaf default-metric { + type uint32 { + range "0..16777214"; + } + description + "Metric of redistributed routes."; + } + + leaf write-multiplier { + type uint8 { + range "1..100"; + } + description + "Maximum number of interfaces serviced per write."; + } + + container router-info { + choice router-info { + case as { + leaf as { + type boolean; + description + "Enable the Router Information functionality with AS flooding scope."; + } + } + case area { + leaf area { + type ospf-area-id; + description + "Enable the Router Information functionality with Area flooding scope."; + } + } + } + } + } + + grouping metric-common-group { + leaf metric { + type uint32 { + range "0..16777214"; + } + description + "OSPF default metric."; + } + + leaf metric-type { + type enumeration { + enum "type-1" { + value 1; + } + enum "type-2" { + value 2; + } + } + description + "Metric type (1/2)."; + } + + leaf route-map { + type rmap-ref; + description + "Route map reference."; + } + } + + grouping default-info-group { + container default-information { + leaf originate { + type boolean; + description + "Advertise a default route"; + } + + leaf always { + type boolean; + description + "Always advertise default route."; + } + + uses metric-common-group; + } + } + + grouping redistribute-group { + list redistribute { + key "protocol"; + leaf protocol { + type frr-route-types:frr-route-types-v4; + description + "Protocol."; + } + + uses metric-common-group; + } + } + + grouping distance-group { + container distance { + leaf admin-value { + type uint8 { + range "1..255"; + } + description + "Admin value."; + } + + container ospf { + leaf external { + type uint8 { + range "1..255"; + } + description + "Distance for external routes."; + } + + leaf inter-area { + type uint8 { + range "1..255"; + } + description + "Distance for inter-area routes."; + } + + leaf intra-area { + type uint8 { + range "1..255"; + } + description + "Distance for intra-area routes."; + } + } + } + } + + grouping distribute-list-group { + container distribute-list { + list dlist { + key "name protocol"; + leaf name { + type string; + description + "Filter networks in routing updates."; + } + + leaf protocol { + type frr-route-types:frr-route-types-v4; + description + "Out protocol."; + } + } + } + } + + grouping max-metric-group { + container max-metric { + container router-lsa { + description + "Advertise own Router-LSA with infinite distance (stub router)."; + leaf administrative { + type boolean; + description + "Administratively applied, for an indefinite period."; + } + + leaf on-shutdown { + type uint8 { + range "5..100"; + } + description + "Advertise stub-router prior to full shutdown of OSPF."; + } + + leaf on-startup { + type uint32 { + range "5..86400"; + } + description + "Automatically advertise stub Router-LSA on startup of OSPF."; + } + } + } + } + + grouping mpls-te-group { + container mpls-te { + leaf on { + type boolean; + description + "Enable the MPLS-TE functionality."; + } + + leaf router-address { + type inet:ipv4-address; + description + "Stable IP address of the advertising router."; + } + + container inter-as { + leaf as { + type boolean; + description + "AS native mode self originate INTER-AS LSA with Type 11 (as flooding scope)."; + } + + leaf area { + type ospf-area-id; + description + "AREA native mode self originate INTER-AS LSA with Type 10 (area flooding scope)."; + } + } + } + } + + grouping ospf-group { + container ospf { + leaf abr-type { + type enumeration { + enum "cisco" { + value 1; + description + "Alternative ABR, Cisco implementation."; + } + enum "ibm" { + value 2; + description + "Alternative ABR, IBM implementation."; + } + enum "shortcut" { + value 3; + description + "Shortcut ABR."; + } + enum "standard" { + value 4; + description + "Standard behavior (RFC2328)."; + } + } + } + + leaf opaque-lsa { + type boolean; + description + "Enable the Opaque-LSA capability (RFC2370)"; + } + + leaf rfc1583compatibility { + type boolean; + description + "Enable the RFC 1583 compatibility flag."; + } + + leaf router-id { + type inet:ipv4-address; + description + "Router-id for the OSPF process."; + } + + leaf write-multiplier { + type uint8 { + range "1..100"; + } + description + "Write multiplier."; + } + } + } + + grouping timer-group { + container timers { + leaf refresh-interval { + type uint16 { + range "10..1800"; + } + units "seconds"; + description + "The maximum time between distinct originations of any particular + LSA, value in units seconds."; + } + + leaf lsa-min-arrival { + type uint32 { + range "0..600000"; + } + units "milliseconds"; + description + "Minimum delay in receiving new version of an LSA."; + } + + container throttle { + leaf lsa-all { + type uint16 { + range "0..5000"; + } + units "milliseconds"; + description + "LSA delay between transmissions."; + } + + leaf spf { + type uint32 { + range "0..600000"; + } + units "milliseconds"; + description + "Delay from first change received till SPF calculation."; + } + } + } + } + + grouping segment-routing-group { + container segment-routing { + container global-block { + description + "Segment Routing Global Block label range."; + must "./upper-bound > ./lower-bound"; + leaf lower-bound { + type uint32 { + range "0..1048575"; + } + default "16000"; + } + + leaf upper-bound { + type uint32 { + range "0..1048575"; + } + default "23999"; + } + } + + container srlb { + description + "Local blocks to be advertised."; + must "./upper-bound > ./lower-bound"; + leaf lower-bound { + type uint32; + default "15000"; + description + "Lower value in the label range."; + } + leaf upper-bound { + type uint32; + default "15999"; + description + "Upper value in the label range."; + } + } + + leaf node-msd { + type uint8 { + range "1..16"; + } + description + "Maximum Stack Depth for this router."; + } + + leaf on { + type boolean; + description + "Enable Segment Routing."; + } + + list prefix-sid { + key "index"; + leaf index { + type uint16; + description + "SID index for this prefix."; + } + + leaf prefix { + type inet:ipv4-prefix; + description + "Prefix SID."; + } + + leaf last-hop-behavior { + type enumeration { + enum "explicit-null" { + value 0; + description + "Use explicit-null for the SID."; + } + enum "no-php" { + value 1; + description + "Do not use Penultimate Hop Popping (PHP) + for the SID."; + } + enum "php" { + value 2; + description + "Use PHP for the SID."; + } + } + default "php"; + description + "Configure last hop behavior."; + } + } + } + } + + grouping ospf-list-group { + list neighbor { + key "ip"; + description + "Neighbor list."; + leaf ip { + type inet:ipv4-address; + description + "Neighbor IP address."; + } + + leaf priority { + type uint8; + description + "Neighbor Priority."; + } + + leaf poll-interval { + type uint16 { + range "1..65535"; + } + units "seconds"; + description + "Dead Neighbor Polling interval."; + } + } + + list network { + key "prefix"; + description + "Enable routing on list of IP network."; + leaf prefix { + type inet:ipv4-prefix; + description + "Prefix on which routing needs to be enabled."; + } + + leaf area { + type ospf-area-id; + description + "Area ID for this network."; + } + } + + list passive-interface { + key "interface"; + description + "Suppress routing updates on list interface."; + leaf interface { + type frr-interface:interface-ref; + description + "Suppress routing updates on an interface."; + } + + leaf address { + type inet:ipv4-address; + description + "Interface address."; + } + } + } + + grouping interface-ospf-attribute-group { + leaf area { + type ospf-area-id; + description + "OSPF area ID."; + } + + uses authentication-group; + + leaf cost { + type uint16 { + range "1..65535"; + } + description + "Interface cost"; + } + + container dead-interval { + leaf interval { + type uint16 { + range "1..65535"; + } + units "seconds"; + } + + container minimal { + leaf hello-multiplier { + type uint8 { + range "1..10"; + } + } + } + } + + leaf hello-interval { + type uint16 { + range "1..65535"; + } + units "seconds"; + description + "Time between HELLO packets."; + } + + leaf retransmit-interval { + type uint16 { + range "1..65535"; + } + units "seconds"; + description + "Time between retransmitting lost link state advertisements."; + } + + leaf transmit-delay { + type uint16 { + range "1..65535"; + } + units "seconds"; + description + "Link state transmit delay."; + } + + leaf mtu-ignore { + type boolean; + description + "Disable MTU mismatch detection on this interface."; + } + + leaf priority { + type uint8; + description + "Router priority."; + } + } + + grouping interface-ospf-group { + list instance { + key "id"; + leaf id { + type uint16; + description + "OSPF instance ID."; + } + + leaf bfd { + type boolean; + default "false"; + description + "BFD support."; + } + + leaf network { + type enumeration { + enum "broadcast" { + value 1; + } + enum "non-broadcast" { + value 2; + } + enum "point-to-multipoint" { + value 3; + } + enum "point-to-point" { + value 4; + } + } + } + + uses interface-ospf-attribute-group; + + list interface-address { + key "address"; + leaf address { + type inet:ipv4-address; + description + "Address of interface"; + } + + uses interface-ospf-attribute-group; + } + } + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol" { + container ospf { + when "../frr-rt:type = 'frr-ospfd:ospf'" { + description + "This container is only valid for the 'OSPF' routing + protocol."; + } + + uses route-ospf-leaf-attributes; + + uses default-info-group; + + uses redistribute-group; + + uses distance-group; + + uses distribute-list-group; + + uses max-metric-group; + + uses mpls-te-group; + + uses ospf-group; + + uses timer-group; + + uses segment-routing-group; + + uses ospf-list-group; + + uses area-groupings; + } + } + + /* + * Per-interface configuration data + */ + augment "/frr-interface:lib/frr-interface:interface" { + container ospf { + description + "OSPF interface parameters."; + uses interface-ospf-group; + } + } +} diff --git a/yang/frr-pim-rp.yang b/yang/frr-pim-rp.yang new file mode 100644 index 0000000000..a2eca5100a --- /dev/null +++ b/yang/frr-pim-rp.yang @@ -0,0 +1,158 @@ +module frr-pim-rp { + yang-version "1.1"; + namespace "http://frrouting.org/yang/pim-rp"; + + prefix frr-pim-rp; + + import ietf-inet-types { + prefix "inet"; + } + + import ietf-routing-types { + prefix "rt-types"; + } + + import frr-routing { + prefix "frr-rt"; + } + + import frr-pim { + prefix "frr-pim"; + } + + organization + "FRRouting"; + + contact + "FRR Users List: + FRR Development List: "; + + description + "The module defines a collection of YANG definitions common for + all PIM (Protocol Independent Multicast) RP (Rendezvous Point) model. + + Copyright 2020 FRRouting + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."; + + revision 2017-03-09 { + description + "Initial revision."; + reference + "RFC XXXX: A YANG Data Model for PIM RP"; + } + + typedef ipv4-multicast-group-address-prefix { + type inet:ipv4-prefix{ + pattern '(2((2[4-9])|(3[0-9]))\.)(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){2}([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(/(([4-9])|([1-2][0-9])|(3[0-2])))'; + } + description + "This type represents an IPv4 multicast group prefix, + which is in the range from 224.0.0.0 to 239.255.255.255."; + } + + typedef ipv6-multicast-group-address-prefix { + type inet:ipv6-prefix { + pattern + '(((FF|ff)[0-9a-fA-F]{2}):)([0-9a-fA-F]{0,4}:){0,5}((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))(/((1[6-9])|([2-9][0-9])|(1[0-1][0-9])|(12[0-8])))'; + pattern + '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)(/.+)'; + } + description + "This type represents an IPv6 multicast group prefix, + which is in the range of FF00::/8."; + } + + typedef ip-multicast-group-address-prefix { + description "The IP-Multicast-Group-Address-Prefix type represents an IP multicast address + prefix and is IP version neutral. The format of the textual representations implies the IP + version. It includes a prefix-length, separated by a '/' sign."; + type union { + type ipv4-multicast-group-address-prefix; + type ipv6-multicast-group-address-prefix; + } + } // typedef ip-multicast-group-address-prefix + + typedef plist-ref { + type string; + } + + /* + * Groupings + */ + grouping static-rp-container { + description + "Grouping of static RP container."; + + container static-rp { + description + "Containing static RP attributes."; + + list rp-list { + key "rp-address"; + description + "A list of RP addresses."; + + leaf rp-address { + type inet:ip-address; + description + "Specifies a static RP address."; + } + + choice group-list-or-prefix-list { + description "Use group-list or prefix-list"; + case group-list { + leaf-list group-list{ + type ip-multicast-group-address-prefix; + description + "List of multicast group address."; + } + } + case prefix-list { + leaf prefix-list { + type plist-ref; + description + "Group prefix-list filter"; + } + } + } + } + } // static-rp + } // static-rp-container + + /* + * Configuration data nodes + */ + augment "/frr-rt:routing/frr-rt:control-plane-protocols/" + + "frr-rt:control-plane-protocol/frr-pim:pim/" + + "frr-pim:address-family" { + description "PIM RP augmentation."; + + container rp { + description + "PIM RP configuration data."; + uses static-rp-container; + } // rp + } // augment +} diff --git a/yang/frr-pim.yang b/yang/frr-pim.yang new file mode 100644 index 0000000000..2135d22f67 --- /dev/null +++ b/yang/frr-pim.yang @@ -0,0 +1,471 @@ +module frr-pim { + yang-version "1.1"; + namespace "http://frrouting.org/yang/pim"; + + prefix frr-pim; + + import frr-interface { + prefix frr-interface; + } + + import frr-routing { + prefix "frr-rt"; + } + + import ietf-routing-types { + prefix "rt-types"; + } + + import ietf-inet-types { + prefix "inet"; + } + + organization + "FRRouting"; + + contact + "FRR Users List: + FRR Development List: "; + + description + "The module defines a collection of YANG definitions common for + PIM (Protocol Independent Multicast) model. + + Copyright 2020 FRRouting + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."; + + revision 2017-03-09 { + description + "Initial revision."; + reference + "RFC XXXX: A YANG Data Model for PIM"; + } + + identity pimd { + base frr-rt:routing-protocol; + description + "'Pim' routing pseudo-protocol."; + } + + typedef plist-ref { + type string; + } + + /* + * Groupings + */ + + grouping global-pim-config-attributes { + description + "A grouping defining pim global attributes."; + + leaf ecmp { + type empty; + description + "Enable PIM ECMP."; + } + + leaf ecmp-rebalance { + type empty; + description + "Enable PIM ECMP Rebalance."; + } + + leaf join-prune-interval { + type uint16 { + range "60..600"; + } + default "60"; + description + "Join Prune Send Interval in seconds."; + } + + leaf keep-alive-timer { + type uint16 { + range "31..60000"; + } + default "210"; + description + "Keep alive Timer in seconds."; + } + + leaf rp-keep-alive-timer { + type uint16 { + range "31..60000"; + } + default "210"; + description + "RP keep alive Timer in seconds."; + } + + leaf packets { + type uint8 { + range "1..100"; + } + default "3"; + description + "Number of packets to process at one time per fd."; + } + + leaf register-suppress-time { + type uint16 { + range "5..60000"; + } + default "60"; + description + "Register Suppress Timer."; + } + } + + grouping per-af-global-pim-config-attributes { + description + "A grouping defining per address family pim global attributes"; + + leaf send-v6-secondary { + when "../frr-pim:address-family = 'frr-rt:ipv4'" { + description + "Only applicable to IPv4 address family."; + } + type empty; + description + "Send v6 secondary addresses."; + } + + container spt-switchover { + description + "SPT-Switchover."; + + leaf spt-action { + type enumeration { + enum "PIM_SPT_IMMEDIATE" { + value 0; + description + "Immediate switch to SPT Tree."; + } + enum "PIM_SPT_INFINITY" { + value 1; + description + "Never switch to SPT Tree."; + } + } + default "PIM_SPT_IMMEDIATE"; + description + "SPT-Switchover action"; + } + + leaf spt-infinity-prefix-list { + when "../spt-action = 'PIM_SPT_INFINITY'" { + description + "This leaf is only valid when the spt action + is PIM_SPT_INFINITY."; + } + type plist-ref; + description + "Prefix-List to control which groups to switch."; + } + } + + leaf ssm-prefix-list { + type plist-ref; + description + "Prefix-list used to define Source-Specific Multicast address range."; + } + + leaf-list ssm-pingd-source-ip { + type inet:ip-address; + description + "Enable ssmpingd operation."; + } + + container msdp-mesh-group { + presence + "Configure MSDP mesh-group."; + + leaf mesh-group-name { + type string; + description + "MSDP mesh group name."; + } + + leaf-list member-ip { + type inet:ip-address; + description + "Peer ip address."; + } + + leaf source-ip { + type inet:ip-address; + description + "Source ip address for the TCP connection."; + } + } + + list msdp-peer { + key "peer-ip"; + description + "Configure MSDP peer."; + + leaf peer-ip { + type inet:ip-address; + description + "MSDP peer IP address."; + } + + leaf source-ip { + type inet:ip-address; + description + "MSDP source IP address."; + } + } + + container mlag { + description + "Multi-chassis link aggregation."; + + leaf peerlink-rif { + type frr-interface:interface-ref; + description + "Outgoing interface name."; + } + + leaf reg-address { + type inet:ip-address; + description + "reg address."; + } + + leaf my-role { + type enumeration { + enum "MLAG_ROLE_NONE" { + value 0; + description + "MLAG role none."; + } + enum "MLAG_ROLE_PRIMARY" { + value 1; + description + "MLAG role primary."; + } + + enum "MLAG_ROLE_SECONDARY" { + value 2; + description + "MLAG role secondary."; + } + } + default "MLAG_ROLE_NONE"; + description + "Mlag role."; + } + + leaf peer-state { + type boolean; + default "false"; + description + "Peer state"; + } + } + + leaf register-accept-list { + type plist-ref; + description + "Only accept registers from a specific source prefix list."; + } + } // per-af-global-pim-config-attributes + + grouping interface-pim-config-attributes { + description + "A grouping defining pim interface attributes."; + + leaf pim-enable { + type empty; + description + "Enable PIM flag on the interface."; + } + + leaf dr-priority { + type uint32 { + range "1..4294967295"; + } + default 1; + description + "DR (Designated Router) priority"; + } + + leaf hello-interval { + type uint16 { + range "1..180"; + } + default "30"; + description + "Hello interval"; + } + + leaf hello-holdtime { + type uint16 { + range "1..180"; + } + description + "Hello holdtime"; + } + + container bfd { + presence + "Enable BFD support on the interface."; + + leaf min-rx-interval { + type uint16 { + range "50..60000"; + } + default "300"; + description + "Required min receive interval"; + } + + leaf min-tx-interval { + type uint16 { + range "50..60000"; + } + default "300"; + description + "Desired min transmit interval"; + } + + leaf detect_mult { + type uint8 { + range "2..255"; + } + default "3"; + description + "Detect Multiplier"; + } + } + + leaf bsm { + type empty; + description + "Enables BSM support on the interface."; + } + + leaf unicast-bsm { + type empty; + description + "Accept/Send unicast BSM on the interface."; + } + + leaf active-active { + type empty; + description + "Mark interface as Active-Active for MLAG operations."; + } + } // interface-pim-config-attributes + + grouping per-af-interface-pim-config-attributes { + description + "A grouping defining pim interface attributes per address family."; + + leaf use-source { + type inet:ip-address; + description + "Primary address of the interface set by user."; + } + + leaf multicast-boundary-oil { + type plist-ref; + description + "Prefix-List to define multicast boundary"; + } + + list mroute { + key "source-addr group-addr"; + description + "Add multicast route."; + + leaf oif { + type frr-interface:interface-ref; + description + "Outgoing interface name."; + } + + leaf source-addr { + type inet:ip-address; + description + "Multicast source address."; + } + + leaf group-addr { + type rt-types:ip-multicast-group-address; + description + "Multicast group address."; + } + } + } // per-af-interface-pim-config-attributes + + /* + * Global Configuration data nodes + */ + augment "/frr-rt:routing/frr-rt:control-plane-protocols/" + + "frr-rt:control-plane-protocol" { + container pim { + when "../frr-rt:type = 'frr-pim:pimd'" { + description + "This container is only valid for the 'pim' routing + protocol."; + } + description + "PIM configuration data."; + + uses global-pim-config-attributes; + + list address-family { + key "address-family"; + description + "Each list entry for one address family."; + uses frr-rt:address-family; + uses per-af-global-pim-config-attributes; + + } //address-family + } // pim + } // augment + + /* + * Per-interface configuration data + */ + augment "/frr-interface:lib/frr-interface:interface" { + container pim { + description + "PIM interface parameters."; + uses interface-pim-config-attributes; + list address-family { + key "address-family"; + description + "Each list entry for one address family."; + uses frr-rt:address-family; + uses per-af-interface-pim-config-attributes; + } + } + } +} + diff --git a/yang/frr-ripd.yang b/yang/frr-ripd.yang index 07690793f0..f5775ab968 100644 --- a/yang/frr-ripd.yang +++ b/yang/frr-ripd.yang @@ -17,13 +17,43 @@ module frr-ripd { } organization - "Free Range Routing"; + "FRRouting"; contact "FRR Users List: FRR Development List: "; description - "This module defines a model for managing FRR ripd daemon."; + "This module defines a model for managing FRR ripd daemon. + Copyright 2020 FRRouting + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."; + + revision 2019-09-09 { + description + "Changed interface references to use + frr-interface:interface-ref typedef"; + } revision 2017-12-06 { description "Initial revision."; @@ -113,9 +143,7 @@ module frr-ripd { "Enable RIP on the specified IP network."; } leaf-list interface { - type string { - length "1..16"; - } + type frr-interface:interface-ref; description "Enable RIP on the specified interface."; } @@ -124,7 +152,15 @@ module frr-ripd { description "Offset-list to modify route metric."; leaf interface { - type string; + type union { + type frr-interface:interface-ref; + type enumeration { + enum '*' { + description + "Match all interfaces."; + } + } + } description "Interface to match. Use '*' to match all interfaces."; } @@ -168,18 +204,14 @@ module frr-ripd { } leaf-list passive-interface { when "../passive-default = 'false'"; - type string { - length "1..16"; - } + type frr-interface:interface-ref; description "A list of interfaces where the sending of RIP packets is disabled."; } leaf-list non-passive-interface { when "../passive-default = 'true'"; - type string { - length "1..16"; - } + type frr-interface:interface-ref; description "A list of interfaces where the sending of RIP packets is enabled."; diff --git a/yang/frr-ripngd.yang b/yang/frr-ripngd.yang index b341b438a4..52e208b2c9 100644 --- a/yang/frr-ripngd.yang +++ b/yang/frr-ripngd.yang @@ -17,13 +17,43 @@ module frr-ripngd { } organization - "Free Range Routing"; + "FRRouting"; contact "FRR Users List: FRR Development List: "; description - "This module defines a model for managing FRR ripngd daemon."; + "This module defines a model for managing FRR ripngd daemon. + Copyright 2020 FRRouting + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."; + + revision 2019-09-09 { + description + "Changed interface references to use + frr-interface:interface-ref typedef"; + } revision 2018-11-27 { description "Initial revision."; @@ -71,9 +101,7 @@ module frr-ripngd { "Enable RIPng on the specified IPv6 network."; } leaf-list interface { - type string { - length "1..16"; - } + type frr-interface:interface-ref; description "Enable RIPng on the specified interface."; } @@ -82,7 +110,15 @@ module frr-ripngd { description "Offset-list to modify route metric."; leaf interface { - type string; + type union { + type frr-interface:interface-ref; + type enumeration { + enum '*' { + description + "Match all interfaces."; + } + } + } description "Interface to match. Use '*' to match all interfaces."; } @@ -118,9 +154,7 @@ module frr-ripngd { } } leaf-list passive-interface { - type string { - length "1..16"; - } + type frr-interface:interface-ref; description "A list of interfaces where the sending of RIPng packets is disabled."; diff --git a/yang/frr-route-map.yang b/yang/frr-route-map.yang new file mode 100644 index 0000000000..b22a96a740 --- /dev/null +++ b/yang/frr-route-map.yang @@ -0,0 +1,429 @@ +module frr-route-map { + yang-version 1.1; + namespace "http://frrouting.org/yang/route-map"; + prefix frr-route-map; + + import ietf-inet-types { + prefix inet; + } + + import frr-filter { + prefix filter; + } + + import frr-interface { + prefix frr-interface; + } + + organization + "FRRouting"; + contact + "FRR Users List: + FRR Development List: "; + description + "This module defines route map settings + + Copyright 2020 FRRouting + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."; + + revision 2019-07-01 { + description + "Initial revision"; + } + + /* + * Types. + */ + typedef route-map-sequence { + type uint16 { + range "1..65535"; + } + description + "Route map valid sequence numbers."; + } + + typedef route-map-name { + type string; + description + "Route map name format."; + } + + typedef route-map-ref { + type leafref { + path "/frr-route-map:lib/frr-route-map:route-map/frr-route-map:name"; + require-instance false; + } + description + "Reference to a route-map."; + } + + /* + * Operational data. + */ + container lib { + list route-map { + key "name"; + description + "Route map instance."; + leaf name { + type route-map-name; + description + "Route map instance name."; + } + + list entry { + key "sequence"; + description + "Route map entry."; + leaf sequence { + description + "Route map instance priority (low number means higher priority)."; + type route-map-sequence; + } + + leaf description { + description "Route map description."; + type string; + } + + leaf action { + description + "Route map actions: permit (executes action), deny (quits evaluation)."; + mandatory true; + type enumeration { + enum permit { + description + "Executes configured action and permits the prefix/route + if the conditions matched. An alternative exit action can + be configured to continue processing the route map list + or jump to process another route map."; + value 0; + } + enum deny { + description + "If all conditions are met the prefix/route is denied and + route map processing stops."; + value 1; + } + } + } + + leaf call { + description + "Call another route map before calling `exit-policy`. If the + called route map returns deny then this route map will also + return deny."; + type route-map-name; + } + + leaf exit-policy { + description "What do to after route map successful match, set and call."; + type enumeration { + enum permit-or-deny { + description "End route map evaluation and return."; + value 0; + } + enum next { + description + "Proceed evaluating next route map entry per sequence."; + value 1; + } + enum goto { + description + "Go to route map entry with the provided sequence number."; + value 2; + } + } + default "permit-or-deny"; + } + + leaf goto-value { + when "../exit-policy = 'goto'"; + description + "Sequence number to jump (when using `goto` exit policy)."; + mandatory true; + type route-map-sequence; + } + + list match-condition { + key "condition"; + description + "Route map match conditions."; + leaf condition { + description "Match condition."; + type enumeration { + enum interface { + description "Match interface."; + value 0; + } + enum ipv4-address-list { + description "Match an IPv4 access-list."; + value 1; + } + enum ipv4-prefix-list { + description "Match an IPv4 prefix-list."; + value 2; + } + enum ipv4-next-hop-list { + description "Match an IPv4 next-hop."; + value 3; + } + enum ipv4-next-hop-prefix-list { + description "Match an IPv4 next-hop prefix list."; + value 4; + } + enum ipv4-next-hop-type { + description "Match an IPv4 next-hop type."; + value 5; + } + enum ipv6-address-list { + description "Match an IPv6 access-list."; + value 6; + } + enum ipv6-prefix-list { + description "Match an IPv6 prefix-list."; + value 7; + } + enum ipv6-next-hop-type { + description "Match an IPv6 next-hop type."; + value 8; + } + enum metric { + description "Match a route metric."; + value 9; + } + enum tag { + description "Match a route tag."; + value 10; + } + /* zebra specific conditions. */ + enum ipv4-prefix-length { + description "Match IPv4 prefix length."; + value 100; + } + enum ipv6-prefix-length { + description "Match IPv6 prefix length."; + value 101; + } + enum ipv4-next-hop-prefix-length { + description "Match next-hop prefix length."; + value 102; + } + enum source-protocol { + description "Match source protocol."; + value 103; + } + enum source-instance { + description "Match source protocol instance."; + value 104; + } + } + } + + choice condition-value { + description + "Value to match (interpretation depends on condition type)."; + mandatory true; + case interface { + when "./condition = 'interface'"; + leaf interface { + type frr-interface:interface-ref; + } + } + + case list-name { + when "./condition = 'ipv4-address-list' or + ./condition = 'ipv4-prefix-list' or + ./condition = 'ipv4-next-hop-list' or + ./condition = 'ipv4-next-hop-prefix-list' or + ./condition = 'ipv6-address-list' or + ./condition = 'ipv6-prefix-list'"; + leaf list-name { + type filter:access-list-name; + } + } + + case ipv4-next-hop-type { + when "./condition = 'ipv4-next-hop-type'"; + leaf ipv4-next-hop-type { + type enumeration { + enum blackhole { + value 0; + } + } + } + } + + case ipv6-next-hop-type { + when "./condition = 'ipv6-next-hop-type'"; + leaf ipv6-next-hop-type { + type enumeration { + enum blackhole { + value 0; + } + } + } + } + + case metric { + when "./condition = 'metric'"; + leaf metric { + type uint32 { + range "1..4294967295"; + } + } + } + + case tag { + when "./condition = 'tag'"; + leaf tag { + type uint32 { + range "1..4294967295"; + } + } + } + } + } + + list set-action { + description "Route map set actions."; + + key "action"; + + leaf action { + description "Action to do when the route map matches."; + type enumeration { + enum ipv4-next-hop { + description "Set IPv4 address of the next hop."; + value 0; + } + enum ipv6-next-hop { + description "Set IPv6 address of the next hop."; + value 1; + } + enum metric { + description "Set prefix/route metric."; + value 2; + } + enum tag { + description "Set tag."; + value 3; + } + /* zebra specific conditions. */ + enum source { + description "Set source address for route."; + value 100; + } + } + } + + choice action-value { + description + "Value to set (interpretation depends on action-type)."; + case ipv4-address { + when "./action = 'ipv4-next-hop'"; + leaf ipv4-address { + description "IPv4 address."; + type inet:ipv4-address; + } + } + + case ipv6-address { + when "./action = 'ipv6-next-hop'"; + leaf ipv6-address { + description "IPv6 address."; + type inet:ipv6-address; + } + } + + case metric { + when "./action = 'metric'"; + choice metric-value { + description "Metric to set or use."; + case value { + leaf value { + description "Use the following metric value."; + type uint32 { + range "0..4294967295"; + } + } + } + + case add-metric { + leaf add-metric { + description "Add value to metric."; + type uint32 { + range "0..4294967295"; + } + } + } + + case subtract-metric { + leaf subtract-metric { + description "Subtract value from metric."; + type uint32 { + range "0..4294967295"; + } + } + } + + case use-round-trip-time { + leaf use-round-trip-time { + description "Use the round trip time as metric."; + type boolean; + } + } + + case add-round-trip-time { + leaf add-round-trip-time { + description "Add round trip time to metric."; + type boolean; + } + } + + case subtract-round-trip-time { + leaf subtract-round-trip-time { + description "Subtract round trip time to metric."; + type boolean; + } + } + } + } + + case tag { + when "./action = 'tag'"; + leaf tag { + description "Tag value."; + type uint32 { + range "0..4294967295"; + } + } + } + } + } + } + } + } +} diff --git a/yang/frr-route-types.yang b/yang/frr-route-types.yang index f22c5ef890..057c32a7e7 100644 --- a/yang/frr-route-types.yang +++ b/yang/frr-route-types.yang @@ -4,12 +4,37 @@ module frr-route-types { prefix frr-route-types; organization - "Free Range Routing"; + "FRRouting"; contact "FRR Users List: FRR Development List: "; description - "This module defines typedefs for route types."; + "This module defines typedefs for route types. + + Copyright 2020 FRRouting + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."; revision 2018-03-28 { description @@ -106,4 +131,12 @@ module frr-route-types { } } } + + typedef frr-route-types { + description "Route types as enumerated in `lib/route_types.txt`"; + type union { + type frr-route-types-v4; + type frr-route-types-v6; + } + } } diff --git a/yang/frr-routing.yang b/yang/frr-routing.yang new file mode 100644 index 0000000000..52607f9ad0 --- /dev/null +++ b/yang/frr-routing.yang @@ -0,0 +1,259 @@ +module frr-routing { + yang-version "1.1"; + namespace "http://frrouting.org/yang/routing"; + prefix "rt"; + + import ietf-yang-types { + prefix "yang"; + } + + import frr-vrf { + prefix frr-vrf; + } + + organization + "FRRouting"; + + contact + "FRR Users List: + FRR Development List: "; + + description + "This YANG module defines essential components for the management + of a routing subsystem. + + Copyright 2020 FRRouting + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."; + + revision 2019-08-15 { + description + "Initial revision."; + } + + /* Identities */ + + identity address-family { + description + "Base identity from which identities describing address + families are derived."; + } + + identity ipv4 { + base address-family; + description + "This identity represents an IPv4 address family."; + } + + identity ipv6 { + base address-family; + description + "This identity represents an IPv6 address family."; + } + + identity afi-safi-type { + description + "Base identity type (AFI,SAFI) tuples for RIB"; + } + + identity ipv4-unicast { + base afi-safi-type; + description + "This identity represents the IPv4 unicast address family."; + } + + identity ipv6-unicast { + base afi-safi-type; + description + "This identity represents the IPv6 unicast address family."; + } + + identity ipv4-multicast { + base afi-safi-type; + description + "This identity represents the IPv4 multicast address family."; + } + + identity ipv6-multicast { + base afi-safi-type; + description + "This identity represents the IPv6 multicast address family."; + } + + identity ipv4-labeled-unicast { + base afi-safi-type; + description + "This identity represents the IPv4 labeled unicast address family."; + } + + + identity ipv6-labeled-unicast { + base afi-safi-type; + description + "This identity represents the IPv6 labeled unicast address family."; + } + + + identity l3vpn-ipv4-unicast { + base afi-safi-type; + description + "This identity represents the L3vpn IPv4 unicast address family."; + } + + + identity l3vpn-ipv6-unicast { + base afi-safi-type; + description + "This identity represents the L3vpn IPv6 unicast address family."; + } + + + identity l3vpn-ipv4-multicast { + base afi-safi-type; + description + "This identity represents the L3vpn IPv4 multicast address family."; + } + + + identity l3vpn-ipv6-multicast { + base afi-safi-type; + description + "This identity represents the L3vpn IPv6 multicast address family."; + } + + + identity l2vpn-vpls { + base afi-safi-type; + description + "This identity represents the L2vpn VPLS address family."; + } + + + identity l2vpn-evpn { + base afi-safi-type; + description + "This identity represents the L2vpn EVPN address family."; + } + + + identity ipv4-flowspec { + base afi-safi-type; + description + "This identity represents the IPv4 flowspec address family."; + } + + + identity ipv6-flowspec { + base afi-safi-type; + description + "This identity represents the IPv6 flowspec address family."; + } + + + identity control-plane-protocol { + description + "Base identity from which control-plane protocol identities are + derived."; + } + + identity routing-protocol { + base control-plane-protocol; + description + "Identity from which Layer 3 routing protocol identities are + derived."; + } + + /* Type Definitions */ + + typedef administrative-distance { + type uint8 { + range "1..255"; + } + description + "Admin distance associated with the route."; + } + + /* Groupings */ + + grouping address-family { + description + "This grouping provides a leaf identifying an address + family."; + leaf address-family { + type identityref { + base address-family; + } + mandatory true; + description + "Address family."; + } + } + + grouping router-id { + description + "This grouping provides a router ID."; + leaf router-id { + type yang:dotted-quad; + description + "A 32-bit number in the form of a dotted quad that is used by + some routing protocols identifying a router."; + reference + "RFC 2328: OSPF Version 2"; + } + } + + /* Data nodes */ + + container routing { + description + "Configuration parameters for the routing subsystem."; + container control-plane-protocols { + description + "Support for control-plane protocol instances."; + list control-plane-protocol { + key "type name vrf"; + description + "Each entry contains a control-plane protocol instance."; + leaf type { + type identityref { + base control-plane-protocol; + } + description + "Type of the control-plane protocol"; + } + leaf name { + type string; + description + "An arbitrary name of the control-plane protocol + instance."; + } + leaf vrf { + type string; + description + "vrf for control-plane protocol"; + } + } + } + } +} diff --git a/yang/frr-staticd.yang b/yang/frr-staticd.yang new file mode 100644 index 0000000000..98ff3a83c6 --- /dev/null +++ b/yang/frr-staticd.yang @@ -0,0 +1,132 @@ +module frr-staticd { + yang-version 1.1; + namespace "http://frrouting.org/yang/staticd"; + prefix frr-staticd; + + import frr-routing { + prefix frr-rt; + } + + import frr-nexthop { + prefix frr-nexthop; + } + + import ietf-inet-types { + prefix inet; + } + + organization + "FRRouting"; + contact + "FRR Users List: + FRR Development List: "; + description + "This module defines a model for managing FRR staticd information. + This YANG module augments the ietf-routing with additional + nexthop information + + Copyright 2020 FRRouting + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."; + + revision 2019-12-03 { + description + "Initial revision."; + } + + identity staticd { + base frr-rt:routing-protocol; + description + "'Staticd' routing pseudo-protocol."; + } + + grouping staticd-prefix-attributes { + list path-list { + key "table-id distance"; + leaf table-id { + type uint32; + description + "Table-id"; + } + + leaf distance { + type frr-rt:administrative-distance; + description + "Admin distance associated with this route."; + } + + leaf tag { + type uint32; + default "0"; + description + "Route tag"; + } + + uses frr-nexthop:frr-nexthop; + } + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol" { + container staticd { + when "../frr-rt:type = 'frr-staticd:staticd'" { + description + "This container is only valid for the 'staticd' routing + protocol."; + } + description + "Support for a 'staticd' pseudo-protocol instance + consists of a list of routes."; + list route-list { + key "prefix afi-safi"; + description + "List of staticd IP routes."; + leaf prefix { + type inet:ip-prefix; + description + "IP prefix."; + } + leaf afi-safi { + type identityref { + base frr-rt:afi-safi-type; + } + description + "AFI-SAFI type."; + } + + uses staticd-prefix-attributes; + + list src-list { + key "src-prefix"; + leaf src-prefix { + type inet:ipv6-prefix; + description + "IPv6 source prefix"; + } + + uses staticd-prefix-attributes; + } + } + } + } +} diff --git a/yang/frr-test-module.yang b/yang/frr-test-module.yang index d85b12ea06..61915b1349 100644 --- a/yang/frr-test-module.yang +++ b/yang/frr-test-module.yang @@ -13,6 +13,34 @@ module frr-test-module { prefix frr-interface; } + description + "FRRouting internal testing module. + + Copyright 2020 FRRouting + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."; + revision 2018-11-26 { description "Initial revision."; diff --git a/yang/frr-vrf.yang b/yang/frr-vrf.yang new file mode 100644 index 0000000000..bb17bfaddb --- /dev/null +++ b/yang/frr-vrf.yang @@ -0,0 +1,85 @@ +module frr-vrf { + yang-version 1.1; + namespace "http://frrouting.org/yang/vrf"; + prefix frr-vrf; + + organization + "Free Range Routing"; + contact + "FRR Users List: + FRR Development List: "; + description + "This module defines a model for managing FRR VRF. + + Copyright 2020 FRRouting + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."; + + revision 2019-12-06 { + description + "Initial revision."; + } + + typedef vrf-ref { + type leafref { + path "/frr-vrf:lib/frr-vrf:vrf/frr-vrf:name"; + require-instance false; + } + description + "Reference to a VRF"; + } + + container lib { + list vrf { + key "name"; + description + "VRF."; + leaf name { + type string { + length "1..36"; + } + description + "VRF name."; + } + + container state { + config false; + leaf id { + type uint32 { + range "0..4294967295"; + } + description + "VRF Id."; + } + + leaf active { + type boolean; + default "false"; + description + "VRF active in kernel."; + } + } + } + } +} diff --git a/yang/frr-vrrpd.yang b/yang/frr-vrrpd.yang new file mode 100644 index 0000000000..200eaeb0b5 --- /dev/null +++ b/yang/frr-vrrpd.yang @@ -0,0 +1,285 @@ +module frr-vrrpd { + yang-version 1.1; + namespace "http://frrouting.org/yang/vrrpd"; + prefix frr-vrrpd; + + import ietf-inet-types { + prefix inet; + } + + import ietf-yang-types { + prefix yang; + } + + import frr-interface { + prefix frr-interface; + } + + organization + "FRRouting"; + contact + "FRR Users List: + FRR Development List: "; + description + "This module defines a model for managing FRR vrrpd daemon. + + Copyright 2020 FRRouting + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."; + + revision 2019-09-09 { + description + "Initial revision."; + } + + grouping ip-vrrp-config { + description + "Configuration data for VRRP on IP interfaces"; + leaf virtual-router-id { + type uint8 { + range "1..255"; + } + description + "Set the virtual router id for use by the VRRP group. This + usually also determines the virtual MAC address that is + generated for the VRRP group"; + } + + leaf version { + type enumeration { + enum "2" { + value 2; + description + "VRRP version 2."; + } + enum "3" { + value 3; + description + "VRRP version 3."; + } + } + default "3"; + } + + leaf priority { + type uint8 { + range "1..254"; + } + default "100"; + description + "Specifies the sending VRRP interface's priority + for the virtual router. Higher values equal higher + priority"; + } + + leaf preempt { + type boolean; + default "true"; + description + "When set to true, enables preemption by a higher + priority backup router of a lower priority master router"; + } + + leaf accept-mode { + type boolean; + default "true"; + description + "Configure whether packets destined for + virtual addresses are accepted even when the virtual + address is not owned by the router interface"; + } + + leaf advertisement-interval { + type uint16 { + range "1..4095"; + } + units "centiseconds"; + default "100"; + description + "Sets the interval between successive VRRP + advertisements -- RFC 5798 defines this as a 12-bit + value expressed as 0.1 seconds, with default 100, i.e., + 1 second. Several implementation express this in units of + seconds"; + } + + leaf shutdown { + type boolean; + default "false"; + description + "Administrative shutdown for this VRRP group."; + } + } + + grouping ip-vrrp-state { + description + "Grouping for operational state data for a virtual router"; + leaf current-priority { + type uint8; + config false; + description + "Operational value of the priority for the + interface in the VRRP group."; + } + + leaf vrrp-interface { + type frr-interface:interface-ref; + config false; + description + "The interface used to transmit VRRP traffic."; + } + + leaf source-address { + type inet:ip-address; + config false; + description + "The source IP address used for VRRP advertisements."; + } + + leaf state { + type enumeration { + enum "Initialize" { + description + "State when virtual router is waiting for a Startup event."; + } + enum "Master" { + description + "State when virtual router is functioning as the forwarding router + for the virtual addresses."; + } + enum "Backup" { + description + "State when virtual router is monitoring the availability and state + of the Master router."; + } + } + config false; + } + + leaf master-advertisement-interval { + type uint16 { + range "0..4095"; + } + units "centiseconds"; + config false; + description + "Advertisement interval contained in advertisements received from the Master."; + } + + leaf skew-time { + type uint16; + units "centiseconds"; + config false; + description + "Time to skew Master_Down_Interval."; + } + + container counter { + config false; + leaf state-transition { + type yang:zero-based-counter32; + description + "Number of state transitions the virtual router has experienced."; + } + + container tx { + leaf advertisement { + type yang:zero-based-counter32; + description + "Number of sent VRRP advertisements."; + } + } + + container rx { + leaf advertisement { + type yang:zero-based-counter32; + description + "Number of received VRRP advertisements."; + } + } + } + } + + grouping ip-vrrp-top { + description + "Top-level grouping for Virtual Router Redundancy Protocol"; + container vrrp { + description + "Enclosing container for VRRP groups handled by this + IP interface"; + reference + "RFC 5798 - Virtual Router Redundancy Protocol + (VRRP) Version 3 for IPv4 and IPv6"; + list vrrp-group { + key "virtual-router-id"; + description + "List of VRRP groups, keyed by virtual router id"; + uses ip-vrrp-config; + + container v4 { + leaf-list virtual-address { + type inet:ipv4-address; + description + "Configure one or more IPv4 virtual addresses for the + VRRP group"; + } + + uses ip-vrrp-state { + augment "counter/tx" { + leaf gratuitous-arp { + type yang:zero-based-counter32; + description + "Number of sent gratuitous ARP requests."; + } + } + } + } + + container v6 { + when "../version = 3"; + leaf-list virtual-address { + type inet:ipv6-address; + description + "Configure one or more IPv6 virtual addresses for the + VRRP group"; + } + + uses ip-vrrp-state { + augment "counter/tx" { + leaf neighbor-advertisement { + type yang:zero-based-counter32; + description + "Number of sent unsolicited Neighbor Advertisements."; + } + } + } + } + } + } + } + + augment "/frr-interface:lib/frr-interface:interface" { + uses ip-vrrp-top; + } +} diff --git a/yang/frr-zebra.yang b/yang/frr-zebra.yang new file mode 100644 index 0000000000..6b4be65915 --- /dev/null +++ b/yang/frr-zebra.yang @@ -0,0 +1,2239 @@ +module frr-zebra { + yang-version 1.1; + namespace "http://frrouting.org/yang/zebra"; + prefix frr-zebra; + + import ietf-yang-types { + prefix yang; + } + + import ietf-inet-types { + prefix inet; + } + + import frr-route-map { + prefix frr-route-map; + } + + import frr-route-types { + prefix frr-route-types; + } + + import ietf-routing-types { + prefix rt-types; + } + + import frr-nexthop { + prefix frr-nh; + } + + import frr-routing { + prefix frr-rt; + } + + import frr-interface { + prefix frr-interface; + } + + import frr-vrf { + prefix frr-vrf; + } + + organization + "FRRouting"; + contact + "FRR Users List: + FRR Development List: "; + description + "This module defines a model for managing the FRR zebra daemon. + + Copyright 2020 FRRouting + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."; + + revision 2019-06-01 { + description + "Initial revision."; + } + + typedef unix-timestamp { + type uint32; + units "seconds"; + description + "An absolute time in seconds since the unix epoch."; + } + + identity zebra-interface-type { + description + "zebra interface type."; + } + + identity zif-other { + base zebra-interface-type; + description + "Zebra interface type other."; + } + + identity zif-bridge { + base zebra-interface-type; + description + "Zebra interface type bridge."; + } + + identity zif-vlan { + base zebra-interface-type; + description + "Zebra interface type vlan."; + } + + identity zif-vxlan { + base zebra-interface-type; + description + "Zebra interface type vxlan."; + } + + identity zif-vrf { + base zebra-interface-type; + description + "Zebra interface type vrf."; + } + + identity zif-veth { + base zebra-interface-type; + description + "Zebra interface type veth."; + } + + identity zif-bond { + base zebra-interface-type; + description + "Zebra interface type bond."; + } + + identity zif-bond-slave { + base zebra-interface-type; + description + "Zebra interface type bond slave."; + } + + identity zif-macvlan { + base zebra-interface-type; + description + "Zebra interface type macvlan."; + } + + /* + * Multicast RPF mode configurable type + */ + + typedef mcast-rpf-lookup-mode { + type enumeration { + enum "none" { + value 0; + description + "No mode set."; + } + enum "mrib-only" { + value 1; + description + "Lookup in unicast RIB only."; + } + enum "urib-only" { + value 2; + description + "Lookup in multicast RIB only."; + } + enum "mrib-then-urib" { + value 3; + description + "Try multicast RIB first, fall back to unicast RIB."; + } + enum "lower-distance" { + value 4; + description + "Lookup both unicast and mcast, use entry with lower distance."; + } + enum "longer-prefix" { + value 5; + description + "Lookup both unicast and mcast, use entry with longer prefix."; + } + } + description + "Multicast RPF lookup behavior"; + } + + // End of ip6-route + /* + * VxLAN Network Identifier type + */ + + typedef vni-id-type { + type uint32 { + range "0..16777215"; + } + description + "A VxLAN network identifier value."; + } + + typedef vni-vtep-flood-type { + type enumeration { + enum "head-end-repl" { + value 0; + description + "Head-end replication."; + } + enum "disabled" { + value 1; + description + "Flooding disabled."; + } + enum "pim-sm" { + value 2; + description + "Multicast PIM-SM."; + } + } + } + + /* + * Common route data, shared by v4 and v6 routes. + */ + + grouping route-common { + description + "Common information about a route."; + + leaf distance { + type uint8; + description + "Admin distance based on routing protocol."; + } + + leaf metric { + type uint32; + description + "Route metric value."; + } + + leaf tag { + type uint32 { + range "1..4294967295"; + } + description + "Route tag value."; + } + + leaf selected { + type empty; + description + "Route is the selected or preferred route for the prefix."; + } + + leaf installed { + type empty; + description + "Route is installed in the FIB."; + } + + leaf failed { + type empty; + description + "Route installation in FIB has failed."; + } + + leaf queued { + type empty; + description + "Route has a pending FIB operation that has not completed."; + } + + leaf internal-flags { + type int32; + description + "Internal flags for the route."; + } + + leaf internal-status { + type int32; + description + "Internal status for the route."; + } + + leaf uptime { + type yang:date-and-time; + description + "Uptime for the route."; + } + + uses frr-nh:frr-nexthop-group-operational; + } + + // End of route-common + /* + * IPv4 Route object. + */ + + grouping ip4-route { + description + "An IPv4 route."; + leaf prefix { + type inet:ipv4-prefix; + description + "IP address (in the form A.B.C.D) and prefix length, + separated by the slash (/) character. The range of + values for the prefix-length is 0 to 32."; + } + + leaf protocol { + type frr-route-types:frr-route-types-v4; + description + "The protocol owning the route."; + } + + uses route-common; + } + + // End of ip4-route + /* + * IPv6 Route object. + */ + + grouping ip6-route { + description + "An IPv6 route."; + leaf prefix { + type inet:ipv6-prefix; + description + "The route's IPv6 prefix."; + } + + leaf protocol { + type frr-route-types:frr-route-types-v6; + description + "The protocol owning the route."; + } + + uses route-common; + } + // End of ip6-route + + /* + * Information about EVPN VNIs + */ + + grouping vni-information { + choice type-choice { + case l2 { + leaf is-layer2 { + type empty; + description + "Information about an L2 VNI."; + } + + leaf vtep-count { + type uint32; + description + "Number of VTEPs."; + } + } + + case l3 { + leaf is-layer3 { + type empty; + description + "Information about an L3 VNI."; + } + } + } + + leaf vni-id { + type vni-id-type; + description + "The VNI identifier."; + } + + leaf vxlan-ifname { + type frr-interface:interface-ref; + description + "The VxLAN interface name."; + } + + leaf mac-count { + type uint32; + description + "Number of valid MACs."; + } + + leaf neighbor-count { + type uint32; + description + "Number of neighbors."; + } + + leaf vrf { + type frr-vrf:vrf-ref; + description + "The tenant VRF."; + } + + leaf local-vtep-addr { + type inet:ipv4-address; + description + "The local VTEP IP address."; + } + } + + /* + * Detailed EVPN VNI information for L2. + */ + + grouping vni-l2-detail { + leaf if-index { + type uint32; + description + "The VxLAN ifindex."; + } + + leaf advertise-gw { + type empty; + description + "The gateway MAC-IP is being advertised."; + } + + leaf mcast-group { + type rt-types:ipv4-multicast-group-address; + description + "The VNI multicast group for BUM traffic."; + } + + list remote-vtep-list { + leaf remote-vtep { + type inet:ipv4-address; + description + "The remote VTEP IP address."; + } + + leaf vtep-flood { + type vni-vtep-flood-type; + } + } + } + + /* + * Detailed EVPN VNI information for L3. + */ + + grouping vni-l3-detail { + leaf svi-interface { + type frr-interface:interface-ref; + description + "The SVI interface."; + } + + leaf is-up { + type empty; + description + "The state is active."; + } + + leaf prefix-only { + type empty; + description + "Prefix routes only"; + } + + leaf router-mac { + type yang:mac-address; + description + "The VNI Router MAC address."; + } + + list vni-list { + description + "A list of the associated L2 VNIs."; + leaf vni-id { + type vni-id-type; + description + "An L2 VNI identifier."; + } + } + } + + /* + * Debug options + */ + + grouping zebra-debugs { + leaf debug-events { + type boolean; + description + "Debug ZAPI events."; + } + + leaf debug-zapi-send { + type boolean; + description + "Debug ZAPI messages sent."; + } + + leaf debug-zapi-recv { + type boolean; + description + "Debug ZAPI messages received."; + } + + leaf debug-zapi-detail { + type boolean; + description + "Debug ZAPI details."; + } + + leaf debug-kernel { + type boolean; + description + "Debug kernel events."; + } + + leaf debug-kernel-msg-send { + type boolean; + description + "Debug kernel messages sent."; + } + + leaf debug-kernel-msg-recv { + type boolean; + description + "Debug kernel messages received."; + } + + leaf debug-rib { + type boolean; + description + "Debug RIB processing."; + } + + leaf debug-rib-detail { + type boolean; + description + "Debug RIB processing details."; + } + + leaf debug-fpm { + type boolean; + description + "Debug the FIB Push Interface subsystem."; + } + + leaf debug-nht { + type boolean; + description + "Debug Nexthop-tracking."; + } + + leaf debug-nht-detail { + type boolean; + description + "Debug Nexthop-tracking details."; + } + + leaf debug-mpls { + type boolean; + description + "Debug MPLS."; + } + + leaf debug-vxlan { + type boolean; + description + "Debug VxLAN."; + } + + leaf debug-pw { + type boolean; + description + "Debug pseudowires."; + } + + leaf debug-dplane { + type boolean; + description + "Debug the dataplane subsystem."; + } + + leaf debug-dplane-detail { + type boolean; + description + "Debug dataplane subsystem details."; + } + + leaf debug-mlag { + type boolean; + description + "Debug MLAG."; + } + } + + grouping ribs { + container ribs { + description + "RIBs supported by FRR."; + list rib { + key "afi-safi-name table-id"; + leaf afi-safi-name { + type identityref { + base frr-rt:afi-safi-type; + } + description + "AFI, SAFI name."; + } + + leaf table-id { + type uint32; + description + "Routing Table id (default id - 254)."; + } + + list route { + key "prefix"; + config false; + leaf prefix { + type inet:ip-prefix; + description + "The route's prefix."; + } + + list route-entry { + key "protocol"; + leaf protocol { + type frr-route-types:frr-route-types; + description + "The protocol owning the route."; + } + + leaf instance { + type uint16; + must "../protocol = \"ospf\""; + description + "Retrieve routes from a specific OSPF instance."; + } + + uses route-common; + } + } + } + } + } + + grouping vrf-vni-mapping { + description + "EVPN L3-VNI mapping corresponding to a VRF."; + leaf l3vni-id { + type vni-id-type; + description + "EVPN L3-VNI id to map to the VRF."; + } + + leaf prefix-only { + type boolean; + default "false"; + description + "EVPN asymmetric mode advertise prefix routes only."; + } + } + + // End of zebra container + /* + * RPCs + */ + + rpc get-route-information { + description + "Retrieve IPv4 or IPv6 unicast routes."; + input { + choice ip-type { + case v4 { + leaf ipv4 { + type empty; + mandatory true; + description + "Retrieve IPv4 routes."; + } + + leaf prefix-v4 { + type inet:ipv4-prefix; + description + "Retrieve routes matching a specific prefix."; + } + + leaf supernets-only { + type empty; + description + "Skip routes that are subnets of classful prefix sizes."; + } + } + + case v6 { + leaf ipv6 { + type empty; + mandatory true; + description + "Retrieve IPv6 routes."; + } + + leaf prefix-v6 { + type inet:ipv6-prefix; + description + "Retrieve routes matching a specific prefix."; + } + } + } + + choice vrf-choice { + case single { + leaf vrf { + type frr-vrf:vrf-ref; + description + "Retrieve routes in a non-default vrf."; + } + } + + case all { + leaf all-vrfs { + type empty; + description + "Retrieve routes from all vrfs."; + } + } + } + + leaf fib-routes { + type empty; + description + "Retrieve FIB routes rather than RIB routes."; + } + + leaf table-id { + type uint32 { + range "1..4294967295"; + } + description + "Routing table id to retrieve."; + } + + leaf protocol { + type frr-route-types:frr-route-types-v4; + description + "Retrieve routes from a specific protocol daemon."; + } + + leaf ospf-instance { + type uint32 { + range "1..65535"; + } + must "../protocol = \"ospf\""; + description + "Retrieve routes from a specific OSPF instance."; + } + + choice detail { + case det { + leaf include-detail { + type empty; + description + "Include detailed information."; + } + } + + case summ { + leaf summary { + type empty; + description + "Include summary information only."; + } + } + } + } + // End of input + output { + choice route-list { + case v4 { + container routes-v4 { + description + "IPv4 route information."; + list route { + uses ip4-route; + } + } + } + + case v6 { + container routes-v6 { + description + "IPv6 route information."; + list route { + uses ip6-route; + } + } + } + } + } + // End of output + } + + // End get-route-information + + rpc get-v6-mroute-info { + description + "Retrieve IPv6 multicast routes."; + input { + choice vrf-choice { + case single { + leaf vrf { + type frr-vrf:vrf-ref; + description + "Retrieve routes in a non-default vrf."; + } + } + + case all { + leaf all-vrfs { + type empty; + description + "Retrieve routes from all vrfs."; + } + } + } + } + + output { + container routes { + description + "IPv6 mcast route information."; + list route { + uses ip6-route; + } + } + } + } + + // End get-v6-mroute-info + + rpc get-vrf-info { + description + "Retrieve VRF information; the default VRF is elided."; + // Note: no input clause. + output { + list vrf-list { + leaf name { + type frr-vrf:vrf-ref; + description + "The VRF name"; + } + + leaf is-user-config { + type empty; + description + "The VRF was configured by an admin."; + } + + leaf vrf-id { + type uint32; + description + "The VRF id."; + } + + choice vrf-type { + case inactive { + leaf is-inactive { + type empty; + description + "The VRF is inactive."; + } + } + + case netns { + leaf netns-name { + type string; + description + "The net namespace name associated with the VRF."; + } + } + + case table { + leaf table-id { + type uint32; + description + "The table-id associated with the VRF."; + } + } + } + } + } + } + + // End get-vrf-info + + rpc get-vrf-vni-info { + description + "Retrieve mappings between EVPN VNI and VRF."; + // Note: no input clause. + output { + list vrf-vni-list { + leaf vrf-name { + type frr-vrf:vrf-ref; + description + "The VRF name."; + } + + leaf vni-id { + type vni-id-type; + description + "The EVPN VNI."; + } + + leaf vxlan-if-name { + type frr-interface:interface-ref; + description + "The VxLAN interface name."; + } + + leaf svi-if-name { + type frr-interface:interface-ref; + description + "The SVI interface name."; + } + + leaf router-mac-addr { + type yang:mac-address; + description + "Router MAC address."; + } + + leaf is-up { + type empty; + description + "The state is active."; + } + } + } + } + + // End get-vrf-vni-info + + rpc get-evpn-info { + description + "Retrieve global information about EVPN."; + // Note: No input clause. + output { + leaf l2vni-count { + type uint32; + description + "Number of L2 VNIs."; + } + + leaf l3vni-count { + type uint32; + description + "Number of L3 VNIs."; + } + + leaf advertise-gateway { + type empty; + description + "Advertise the gateway MAC-IP."; + } + + leaf advertise-svi { + type empty; + description + "Advertise SVI MAC-IP."; + } + + leaf dup-detect { + type empty; + description + "Duplicate address detection is enabled."; + } + + leaf dad-max-moves { + type uint32; + description + "Maximum moves allowed before address is considered duplicate."; + } + + leaf dad-timeout { + type uint32; + units "seconds"; + description + "Duplicate address detection timeout."; + } + + leaf dad-freeze { + type empty; + description + "Duplicate address detection freeze enabled."; + } + + choice dad-freeze-choice { + case freeze-permanent { + leaf dad-freeze-perm { + type empty; + description + "Duplicate address detection freeze is permanent."; + } + } + + case freeze-time { + leaf dad-freeze-time { + type uint32; + units "seconds"; + description + "Duplicate address detection freeze timer."; + } + } + } + } + } + + // End get-evpn-info + + rpc get-vni-info { + // If no vni is specified, retrieve global list. + input { + choice vni-choice { + default "all-vnis"; + case all-vnis { + leaf all-vnis { + type empty; + description + "Retrieve information about all VNIs."; + } + } + + case single-vni { + leaf vni-id { + type vni-id-type; + description + "Retrieve information about a specific EVPN VNI."; + } + } + } + + leaf detailed-info { + type empty; + description + "Retrieve detailed information."; + } + } + + output { + list vni-list { + description + "Information about EVPN VNI objects."; + uses vni-information; + + choice detail-choice { + case l2 { + description + "Detailed L2 information."; + uses vni-l2-detail; + } + + case l3 { + description + "Detailed L3 information."; + uses vni-l3-detail; + } + } + } + } + } + + // End get-vni-info + + rpc get-evpn-vni-rmac { + description + "Retrieve information about VxLAN VNI RMACs."; + input { + choice vni-choice { + default "all-vnis"; + case all-vnis { + leaf all-vnis { + type empty; + description + "Retrieve information about all VNIs."; + } + } + + case single-vni { + leaf vni-id { + type vni-id-type; + description + "Retrieve information about a specific EVPN VNI."; + } + + leaf vni-rmac { + type yang:mac-address; + description + "A single RMAC address."; + } + } + } + } + + output { + list rmac-info-list { + leaf rmac { + type yang:mac-address; + description + "The RMAC address."; + } + + leaf remote-vtep { + type inet:ipv4-address; + description + "The remote VTEP IP address."; + } + + leaf refcount { + type uint32; + description + "The refcount of the RMAC."; + } + + list prefix-list { + leaf prefix-item { + type inet:ip-prefix; + description + "IP prefixes associated with the RMAC."; + } + } + } + } + } + + // End get-evpn-vni-rmac + + rpc get-evpn-vni-nexthops { + description + "Retrieve information about EVPN nexthops."; + input { + choice vni-choice { + default "all-vnis"; + case all-vnis { + leaf all-vnis { + type empty; + description + "Retrieve information about all VNIs."; + } + } + + case single-vni { + leaf vni-id { + type vni-id-type; + description + "Retrieve information about a specific EVPN VNI."; + } + + leaf vni-ipaddr { + type inet:ip-address; + description + "A single host IP address (v4 or v6)."; + } + } + } + } + + output { + list nh-info-list { + leaf ip-addr { + type inet:ip-address; + description + "The nexthop IP address."; + } + + leaf mac-addr { + type yang:mac-address; + description + "The nexthop MAC address."; + } + + leaf refcount { + type uint32; + description + "The refcount of the RMAC."; + } + + list prefix-list { + leaf prefix-item { + type inet:ip-prefix; + description + "IP prefixes associated with the RMAC."; + } + } + } + } + } + + // End get-evpn-vni-vteps + + rpc clear-evpn-dup-addr { + description + "Clear duplicate address detection state for one or all VNIs."; + input { + choice clear-dup-choice { + case all-case { + leaf all-vnis { + type empty; + description + "Clear all VNIs."; + } + } + + case single-case { + leaf vni-id { + type vni-id-type; + description + "Clear state for a single EVPN VNI."; + } + + choice ip-mac-choice { + description + "Clear state for a specific MAC or IP address."; + case ip-case { + leaf vni-ipaddr { + type inet:ip-address; + description + "A specific IP address (v4 or v6)."; + } + } + + case mac-case { + leaf mac-addr { + type yang:mac-address; + description + "A specific MAC address."; + } + } + } + } + } + } + } + + // End clear-evpn-dup-addr + + rpc get-evpn-macs { + description + "Retrieve information about EVPN MAC addresses."; + input { + choice all-choice { + default "all-vni"; + case all-vni { + leaf all-vnis { + type empty; + description + "Retrieve information for all VNIs."; + } + + choice all-choices { + case detail-case { + leaf all-detail { + type empty; + description + "Include detailed results."; + } + } + + case vtep-case { + leaf all-vtep-addr { + type inet:ipv4-address; + description + "A single VTEP address."; + } + } + + case dup-case { + leaf all-dup { + type empty; + description + "Show duplicate addresses."; + } + } + } + } + + case single-vni { + leaf vni-id { + type vni-id-type; + description + "Retrieve information for a single VNI."; + } + + choice single-choices { + case detail-case { + leaf single-detail { + type empty; + description + "Include detailed results."; + } + } + + case mac-case { + leaf single-mac { + type yang:mac-address; + description + "A specific MAC address."; + } + } + + case vtep-case { + leaf single-vtep { + type inet:ipv4-address; + description + "A single VTEP address."; + } + } + + case dup-case { + leaf single-dup { + type empty; + description + "Show duplicate addresses."; + } + } + } + } + } + } + // End of input section + output { + list mac-list { + leaf mac-addr { + type yang:mac-address; + description + "The MAC address."; + } + + leaf vni { + type vni-id-type; + description + "The VNI value."; + } + + leaf local-sequence { + type uint32; + description + "Local sequence number."; + } + + leaf remote-sequence { + type uint32; + description + "Remote sequence number."; + } + + leaf dad-count { + type uint32; + description + "Duplicate detection counter."; + } + + leaf is-duplicate { + type empty; + description + "Duplicate MAC detected."; + } + + leaf dup-detect-time { + type unix-timestamp; + description + "If a duplicate, the detection time."; + } + + container dup-detect-started { + leaf dup-detect-start { + type unix-timestamp; + description + "Duplicate detection process start time."; + } + + leaf dup-count { + type uint32; + description + "Duplicate detection count."; + } + } + + leaf is-auto { + type empty; + description + "This is an Auto MAC."; + } + + leaf is-sticky { + type empty; + description + "This is a sticky MAC."; + } + + leaf is-default-gw { + type empty; + description + "This is a default-gateway MAC."; + } + + leaf is-remote-gw { + type empty; + description + "This is a remote-gateway MAC."; + } + + list neighbor-list { + leaf neighbor-addr { + type inet:ip-address; + description + "Neighbor address."; + } + + leaf is-active { + type empty; + description + "Neighbor is active."; + } + } + + leaf mac-count { + type uint32; + description + "Number of MACs (local and remote)."; + } + + choice local-rem-choice { + case local-case { + leaf intf { + type frr-interface:interface-ref; + description + "The local interface name."; + } + + leaf vlan { + type uint32; + description + "A VLAN id."; + } + } + + case remote-case { + leaf vtep-addr { + type inet:ipv4-address; + description + "The remote VTEP IP address."; + } + } + } + } + } + } + + // End get-evpn-macs + + rpc get-evpn-arp-cache { + description + "Retrieve information about EVPN neighbor cache entries."; + input { + choice all-choice { + default "all-vni"; + case all-vni { + leaf all-vnis { + type empty; + description + "Retrieve information for all VNIs."; + } + + choice all-choices { + case detail-case { + leaf all-detail { + type empty; + description + "Include detailed results."; + } + } + + case dup-case { + leaf all-dup { + type empty; + description + "Show duplicates."; + } + } + } + } + + case single-vni { + leaf vni-id { + type vni-id-type; + description + "Retrieve information for a single VNI."; + } + + choice single-choices { + case vtep-case { + leaf single-vtep { + type inet:ipv4-address; + description + "A single VTEP address."; + } + } + + case neighbor-case { + leaf neighbor-addr { + type inet:ip-address; + description + "A single neighbor address."; + } + } + + case dup-case { + leaf single-dup { + type empty; + description + "Show duplicates."; + } + } + } + } + } + } + // End input section + output { + list vni-list { + container vni-container { + description + "Information for one VNI."; + leaf vni-id { + type vni-id-type; + description + "The VNI id."; + } + + list neigh-list { + description + "Information about a VNI's neighbor cache."; + leaf mac-addr { + type yang:mac-address; + description + "A neighbor MAC address."; + } + + leaf ip-addr { + type inet:ip-address; + description + "A neighbor IP address."; + } + + leaf state-active { + type empty; + description + "Indicates whether the entry is active."; + } + + choice local-remote-choice { + case local-case { + leaf is-local { + type empty; + description + "The entry is local."; + } + } + + case remote-case { + leaf is-remote { + type empty; + description + "The entry is remote."; + } + } + } + + leaf is-dup { + type empty; + description + "The entry is a detected duplicate."; + } + + leaf is-default-gw { + type empty; + description + "The entry is a default gateway."; + } + + leaf is-router { + type empty; + description + "The entry is a router."; + } + + leaf local-sequence { + type uint32; + description + "The local sequence number."; + } + + leaf remote-sequence { + type uint32; + description + "The remote sequence number."; + } + + leaf remote-vtep { + type inet:ipv4-address; + description + "The remote VTEP address."; + } + } + } + } + } + } + + // End get-evpn-arp-cache + + rpc get-pbr-ipset { + input { + leaf name { + type string { + length "1..32"; + } + description + "An optional specific IPset name."; + } + } + + output { + list ipset-list { + leaf name { + type string { + length "1..32"; + } + description + "The IPset name."; + } + + leaf ipset-type { + type enumeration { + enum "net-net" { + value 1; + description + ""; + } + enum "net-port-net" { + value 2; + description + ""; + } + enum "net-port" { + value 3; + description + ""; + } + enum "net" { + value 4; + description + ""; + } + } + } + + leaf src-prefix { + type inet:ip-prefix; + description + ""; + } + + leaf dest-prefix { + type inet:ip-prefix; + description + ""; + } + + leaf src-port { + type inet:port-number; + description + ""; + } + + leaf dest-port { + type inet:port-number; + description + ""; + } + + choice proto-choice { + description + "Filter UDP/TCP only, or a specific protocol number."; + case udp-tcp-case { + leaf is-udp-tcp { + type empty; + description + "Filter TCP/UDP ports only."; + } + } + + case proto-case { + leaf proto { + type uint32; + description + "Filter a specific protocol number."; + } + } + } + + container icmp-info { + description + "Additional information for ICMP filters."; + leaf type-min { + type uint8; + description + ""; + } + + leaf type-max { + type uint8; + description + ""; + } + + leaf code-min { + type uint8; + description + ""; + } + + leaf code-max { + type uint8; + description + ""; + } + } + + container ipset-stats { + leaf is-unique { + type empty; + description + ""; + } + + leaf packet-counter { + type uint64; + description + ""; + } + + leaf bytes-counter { + type uint64; + description + ""; + } + } + } + } + } + + // End get-pbr-ipset + + rpc get-pbr-iptable { + input { + leaf name { + type string { + length "1..32"; + } + description + "An optional single IPtable name."; + } + } + + output { + list iptable-list { + leaf name { + type string { + length "1..32"; + } + description + "The IPtable name."; + } + + leaf unique-val { + type uint32; + description + ""; + } + + choice action-choice { + description + "The table action."; + case drop-case { + leaf action-drop { + type empty; + description + ""; + } + } + + case redirect-case { + leaf action-redirect { + type empty; + description + ""; + } + } + } + + leaf min-packet { + type uint32; + description + ""; + } + + leaf max-packet { + type uint32; + description + ""; + } + + leaf lookup-src-port { + type empty; + description + ""; + } + + leaf lookup-dst-port { + type empty; + description + ""; + } + + leaf tcp-flags { + type uint16; + description + ""; + } + + leaf tcp-flags-mask { + type uint16; + description + ""; + } + + leaf protocol-val { + type uint32; + description + "An IP protocol number."; + } + + container dscp-info { + leaf dscp-value { + type uint32; + description + "A DSCP value to match."; + } + + leaf invert-match { + type empty; + description + "If set, exclude the specified value"; + } + } + + container fragment-info { + leaf fragment-val { + type uint32; + description + "An IP fragment value."; + } + + leaf invert-match { + type empty; + description + "If set, exclude the specified value."; + } + } + + container iptable-stats { + leaf packet-counter { + type uint64; + description + ""; + } + + leaf bytes-counter { + type uint64; + description + ""; + } + } + + container rule-info { + description + "Information about a rule, for redirect tables."; + leaf table-id { + type uint32; + description + "The rule table id."; + } + + leaf table-fwmark { + type uint32; + description + "The firewall mark for the rule."; + } + } + } + } + } + + // End get-pbr-iptable + /* + * Handy 'all-at-once' api to retrieve debugs + */ + + rpc get-debugs { + output { + uses zebra-debugs; + } + } + + // End get-debugs + + augment "/frr-interface:lib/frr-interface:interface" { + description + "Extends interface model with Zebra-related parameters."; + container zebra { + list ip-addrs { + key "address-family ip-prefix"; + description + "IP prefixes for an interface."; + uses frr-rt:address-family { + description + "Address family of the RIB."; + } + + leaf ip-prefix { + type inet:ip-prefix; + description + "IP address prefix."; + } + + leaf label { + type string; + description + "Optional string label for the address."; + } + + leaf ip4-peer { + when "derived-from-or-self(../address-family, 'ipv4')"; + type inet:ipv4-prefix; + description + "Peer prefix, for peer-to-peer interfaces."; + } + } + + leaf multicast { + type boolean; + description + "Multicast flag for the interface."; + } + + leaf link-detect { + type boolean; + description + "Link-detection for the interface."; + } + + leaf shutdown { + type boolean; + description + "Interface admin status."; + } + + leaf bandwidth { + type uint32 { + range "1..100000"; + } + description + "Link bandwidth informational parameter, in megabits."; + } + // TODO -- link-params for (experimental/partial TE use in IGP extensions) + container state { + config false; + description + "Operational data."; + leaf up-count { + type uint16; + description + "Interface Up count."; + } + + leaf down-count { + type uint16; + description + "Interface Down count."; + } + + leaf zif-type { + type identityref { + base zebra-interface-type; + } + description + "zebra interface type."; + } + + leaf ptm-status { + type string; + default "disabled"; + description + "Interface PTM status."; + } + + leaf vlan-id { + type uint16 { + range "1..4094"; + } + description + "A VLAN id."; + } + + leaf vni-id { + type vni-id-type; + } + + leaf remote-vtep { + type inet:ipv4-address; + description + "The remote VTEP IP address."; + } + + leaf mcast-group { + type rt-types:ipv4-multicast-group-address; + description + "The VNI multicast group for BUM traffic."; + } + } + } + } + + augment "/frr-vrf:lib/frr-vrf:vrf" { + description + "Extends VRF model with Zebra-related parameters."; + container zebra { + description + "Zebra's vrf specific configuration and operational model."; + uses ribs; + + uses vrf-vni-mapping; + } + } + + augment "/frr-vrf:lib/frr-vrf:vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop" { + uses frr-nh:frr-nexthop-operational; + } + + /* + * Main zebra container + */ + + container zebra { + description + "Data model for the Zebra daemon."; + leaf mcast-rpf-lookup { + type frr-zebra:mcast-rpf-lookup-mode; + default "mrib-then-urib"; + description + "Multicast RPF lookup behavior."; + } + leaf ip-forwarding { + type boolean; + description + "IP forwarding status."; + } + leaf ipv6-forwarding { + type enumeration { + enum unknown { + value -1; + description + "Unknown state."; + } + enum off { + value 0; + description + "IPv6 forwarding disabled."; + } + enum on { + value 1; + description + "IPv6 forwarding enabled."; + } + } + description + "IPv6 forwarding status."; + } + leaf workqueue-hold-timer { + type uint32 { + range "0..10000"; + } + units "milliseconds"; + default "10"; + description + "Work-queue processing hold timer, in milliseconds."; + } + leaf zapi-packets { + type uint32 { + range "1..10000"; + } + default "1000"; + description + "Number of ZAPI packets to process before relinquishing + the main thread."; + } + container import-kernel-table { + description + "Parameters to use when importing IPv4 routes from a non-main kernel + routing table."; + leaf table-id { + type uint32 { + range "1..252"; + } + description + "The kernel table id."; + } + leaf distance { + type uint32 { + range "1..255"; + } + default "15"; + description + "The admin distance to use for imported routes."; + } + leaf route-map { + type string; + description + "A route-map to filter imported routes."; + } + } + leaf allow-external-route-update { + type empty; + description + "Allow FRR-controlled routes to be overwritten by external processes"; + } + leaf dplane-queue-limit { + type uint32 { + range "0..10000"; + } + default "200"; + description + "Limit on the number of updates queued to the dataplane subsystem."; + } + /* + * Debug options + */ + container debugs { + uses zebra-debugs; + } + /* End of debugs */ + /* + * End of configuration attributes + */ + /* + * Operational data. + */ + container state { + config false; + description + "Operational data."; + } + // End of operational / state container + } + + // End interface model augmentation + + augment "/frr-route-map:lib" + + "/frr-route-map:route-map" + + "/frr-route-map:entry" + + "/frr-route-map:match-condition" + + "/frr-route-map:condition-value" { + case ipv4-prefix-length { + when "./frr-route-map:condition = 'ipv4-prefix-length' or + ./frr-route-map:condition = 'ipv4-next-hop-prefix-length'"; + leaf ipv4-prefix-length { + type uint8 { + range "0..32"; + } + } + } + case ipv6-prefix-length { + when "./frr-route-map:condition = 'ipv6-prefix-length'"; + leaf ipv6-prefix-length { + type uint8 { + range "0..128"; + } + } + } + case source-protocol { + when "./frr-route-map:condition = 'source-protocol'"; + leaf source-protocol { + type frr-route-types:frr-route-types; + } + } + case source-instance { + when "./frr-route-map:condition = 'source-instance'"; + leaf source-instance { + type uint8 { + range "0..255"; + } + } + } + } + + augment "/frr-route-map:lib" + + "/frr-route-map:route-map" + + "/frr-route-map:entry" + + "/frr-route-map:set-action" + + "/frr-route-map:action-value" { + case source-v4 { + when "./frr-route-map:action = 'source'"; + leaf source-v4 { + description "IPv4 address"; + type inet:ipv4-address; + } + } + case source-v6 { + when "./frr-route-map:action = 'source'"; + leaf source-v6 { + description "IPv6 address"; + type inet:ipv6-address; + } + } + } +} diff --git a/yang/ietf/frr-deviations-ietf-interfaces.yang b/yang/ietf/frr-deviations-ietf-interfaces.yang index 6528d66d22..704839fb60 100644 --- a/yang/ietf/frr-deviations-ietf-interfaces.yang +++ b/yang/ietf/frr-deviations-ietf-interfaces.yang @@ -8,7 +8,7 @@ module frr-deviations-ietf-interfaces { } organization - "Free Range Routing"; + "FRRouting"; contact "FRR Users List: diff --git a/yang/ietf/frr-deviations-ietf-rip.yang b/yang/ietf/frr-deviations-ietf-rip.yang index 42ed8e3c09..39a1d7e71d 100644 --- a/yang/ietf/frr-deviations-ietf-rip.yang +++ b/yang/ietf/frr-deviations-ietf-rip.yang @@ -12,7 +12,7 @@ module frr-deviations-ietf-rip { } organization - "Free Range Routing"; + "FRRouting"; contact "FRR Users List: diff --git a/yang/ietf/frr-deviations-ietf-routing.yang b/yang/ietf/frr-deviations-ietf-routing.yang index 62787e782c..15ceb6b929 100644 --- a/yang/ietf/frr-deviations-ietf-routing.yang +++ b/yang/ietf/frr-deviations-ietf-routing.yang @@ -8,7 +8,7 @@ module frr-deviations-ietf-routing { } organization - "Free Range Routing"; + "FRRouting"; contact "FRR Users List: diff --git a/yang/ietf/ietf-bgp-types.yang b/yang/ietf/ietf-bgp-types.yang new file mode 100644 index 0000000000..9c7a6af76c --- /dev/null +++ b/yang/ietf/ietf-bgp-types.yang @@ -0,0 +1,525 @@ +module ietf-bgp-types { + yang-version "1.1"; + namespace "urn:ietf:params:xml:ns:yang:ietf-bgp-types"; + + prefix "bt"; + + import ietf-inet-types { + prefix inet; + } + + // meta + organization + "IETF IDR Working Group"; + + contact + "WG Web: + WG List: + + Authors: Mahesh Jethanandani (mjethanandani at gmail.com), + Keyur Patel (keyur at arrcus.com), + Susan Hares (shares at ndzh.com), + Jeffrey Haas (jhaas at pfrc.org)."; + description + "This module contains general data definitions for use in BGP + policy. It can be imported by modules that make use of BGP + attributes"; + + revision 2019-10-03 { + description + "Initial Version"; + reference + "RFC XXX, BGP Model for Service Provider Network."; + } + + identity bgp-capability { + description "Base identity for a BGP capability"; + } + + identity mp-bgp { + base bgp-capability; + description + "Multi-protocol extensions to BGP"; + reference + "RFC 4760"; + } + + identity route-refresh { + base bgp-capability; + description + "The BGP route-refresh functionality"; + reference + "RFC2918"; + } + + identity asn32 { + base bgp-capability; + description + "4-byte (32-bit) AS number functionality"; + reference + "RFC6793"; + } + + identity graceful-restart { + base bgp-capability; + description + "Graceful restart functionality"; + reference + "RFC4724"; + } + + identity add-paths { + base bgp-capability; + description + "BGP add-paths"; + reference + "RFC 7911."; + } + + identity afi-safi-type { + description + "Base identity type for AFI,SAFI tuples for BGP-4"; + reference + "RFC4760 - multi-protocol extensions for BGP-4"; + } + + identity ipv4-unicast { + base afi-safi-type; + description + "IPv4 unicast (AFI,SAFI = 1,1)"; + reference + "RFC4760"; + } + + identity ipv6-unicast { + base afi-safi-type; + description + "IPv6 unicast (AFI,SAFI = 2,1)"; + reference + "RFC4760"; + } + + identity ipv4-labeled-unicast { + base afi-safi-type; + description + "Labeled IPv4 unicast (AFI,SAFI = 1,4)"; + reference + "RFC3107"; + } + + identity ipv6-labeled-unicast { + base afi-safi-type; + description + "Labeled IPv6 unicast (AFI,SAFI = 2,4)"; + reference + "RFC3107"; + } + + identity l3vpn-ipv4-unicast { + base afi-safi-type; + description + "Unicast IPv4 MPLS L3VPN (AFI,SAFI = 1,128)"; + reference + "RFC4364"; + } + + identity l3vpn-ipv6-unicast { + base afi-safi-type; + description + "Unicast IPv6 MPLS L3VPN (AFI,SAFI = 2,128)"; + reference + "RFC4659"; + } + + identity l3vpn-ipv4-multicast { + base afi-safi-type; + description + "Multicast IPv4 MPLS L3VPN (AFI,SAFI = 1,129)"; + reference + "RFC6514"; + } + + identity l3vpn-ipv6-multicast { + base afi-safi-type; + description + "Multicast IPv6 MPLS L3VPN (AFI,SAFI = 2,129)"; + reference + "RFC6514"; + } + + identity l2vpn-vpls { + base afi-safi-type; + description + "BGP-signalled VPLS (AFI,SAFI = 25,65)"; + reference + "RFC4761"; + } + + identity l2vpn-evpn { + base afi-safi-type; + description + "BGP MPLS Based Ethernet VPN (AFI,SAFI = 25,70)"; + } + + identity bgp-well-known-std-community { + description + "Base identity for reserved communities within the standard + community space defined by RFC1997. These communities must + fall within the range 0xFFFF0000 to 0xFFFFFFFF"; + reference + "RFC 1997: BGP Communities Attribute."; + } + + identity no-export { + base bgp-well-known-std-community; + description + "Do not export NLRI received carrying this community outside + the bounds of this autonomous system, or this confederation if + the local autonomous system is a confederation member AS. This + community has a value of 0xFFFFFF01."; + reference + "RFC 1997: BGP Communities Attribute."; + } + + identity no-advertise { + base bgp-well-known-std-community; + description + "All NLRI received carrying this community must not be + advertised to other BGP peers. This community has a value of + 0xFFFFFF02."; + reference + "RFC 1997: BGP Communities Attribute."; + } + + identity no-export-subconfed { + base bgp-well-known-std-community; + description + "All NLRI received carrying this community must not be + advertised to external BGP peers - including over confederation + sub-AS boundaries. This community has a value of 0xFFFFFF03."; + reference + "RFC 1997: BGP Communities Attribute."; + } + + identity no-peer { + base bgp-well-known-std-community; + description + "An autonomous system receiving NLRI tagged with this community + is advised not to re-advertise the NLRI to external bi-lateral + peer autonomous systems. An AS may also filter received NLRI + from bilateral peer sessions when they are tagged with this + community value"; + reference + "RFC 3765: NOPEER Community for BGP."; + } + identity as-path-segment-type { + description + "Base AS Path Segment Type. In [BGP-4], the path segment type + is a 1-octet field with the following values defined."; + reference + "RFC 4271: A Border Gateway Protocol 4 (BGP-4), Section 4.3."; + } + + identity as-set { + base as-path-segment-type; + description + "Unordered set of autonomous systems that a route in the UPDATE + message has traversed."; + reference + "RFC 4271: A Border Gateway Protocol 4 (BGP-4), Section 4.3."; + } + + identity as-sequence { + base as-path-segment-type; + description + "Ordered set of autonomous systems that a route in the UPDATE + message has traversed."; + reference + "RFC 4271: A Border Gateway Protocol 4 (BGP-4), Section 4.3."; + } + + identity as-confed-sequence { + base as-path-segment-type; + description + "Ordered set of Member Autonomous Systems in the local + confederation that the UPDATE message has traversed."; + reference + "RFC 5065, Autonomous System Configuration for BGP."; + } + + identity as-confed-set { + base as-path-segment-type; + description + "Unordered set of Member Autonomous Systems in the local + confederation that the UPDATE message has traversed."; + reference + "RFC 5065, Autonomous System Configuration for BGP."; + } + + /* + * Features. + */ + feature send-communities { + description + "Enable the propogation of communities."; + } + + feature ttl-security { + description + "BGP Time To Live (TTL) security check support."; + reference + "RFC 5082, The Generalized TTL Security Mechanism (GTSM)"; + } + + feature bfd { + description + "Support for BFD detection of BGP neighbor reachability."; + reference + "RFC 5880, Bidirectional Forward Detection (BFD), + RFC 5881, Bidirectional Forward Detection for IPv4 and IPv6 + (Single Hop). + RFC 5883, Bidirectional Forwarding Detection (BFD) for Multihop + Paths"; + } + + typedef bgp-session-direction { + type enumeration { + enum INBOUND { + description + "Refers to all NLRI received from the BGP peer"; + } + enum OUTBOUND { + description + "Refers to all NLRI advertised to the BGP peer"; + } + } + description + "Type to describe the direction of NLRI transmission"; + } + + typedef bgp-well-known-community-type { + type identityref { + base bgp-well-known-std-community; + } + description + "Type definition for well-known IETF community attribute + values"; + reference + "IANA Border Gateway Protocol (BGP) Well Known Communities"; + } + + typedef bgp-std-community-type { + // TODO: further refine restrictions and allowed patterns + // 4-octet value: + // 2 octets + // 2 octets + type union { + type uint32 { + // per RFC 1997, 0x00000000 - 0x0000FFFF and 0xFFFF0000 - + // 0xFFFFFFFF are reserved + range "65536..4294901759"; // 0x00010000..0xFFFEFFFF + } + type string { + pattern '([0-9]+:[0-9]+)'; + } + } + description + "Type definition for standard community attributes"; + reference + "RFC 1997 - BGP Communities Attribute"; + } + + typedef bgp-ext-community-type { + // TODO: needs more work to make this more precise given the + // variability of extended community attribute specifications + // 8-octet value: + // 2 octects + // 6 octets + + type union { + type string { + // Type 1: 2-octet global and 4-octet local + // (AS number) (Integer) + pattern '(6[0-5][0-5][0-3][0-5]|[1-5][0-9]{4}|' + + '[1-9][0-9]{1,4}|[0-9]):' + + '(4[0-2][0-9][0-4][0-9][0-6][0-7][0-2][0-9][0-6]|' + + '[1-3][0-9]{9}|[1-9]([0-9]{1,7})?[0-9]|[1-9])'; + } + type string { + // Type 2: 4-octet global and 2-octet local + // (ipv4-address) (integer) + pattern '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|' + + '25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9][0-9]|' + + '2[0-4][0-9]|25[0-5]):' + + '(6[0-5][0-5][0-3][0-5]|[1-5][0-9]{4}|' + + '[1-9][0-9]{1,4}|[0-9])'; + } + type string { + // route-target with Type 1 + // route-target:(ASN):(local-part) + pattern 'route\-target:(6[0-5][0-5][0-3][0-5]|' + + '[1-5][0-9]{4}|[1-9][0-9]{1,4}|[0-9]):' + + '(4[0-2][0-9][0-4][0-9][0-6][0-7][0-2][0-9][0-6]|' + + '[1-3][0-9]{9}|[1-9]([0-9]{1,7})?[0-9]|[1-9])'; + } + type string { + // route-target with Type 2 + // route-target:(IPv4):(local-part) + pattern 'route\-target:' + + '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|' + + '25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9][0-9]|' + + '2[0-4][0-9]|25[0-5]):' + + '(6[0-5][0-5][0-3][0-5]|[1-5][0-9]{4}|' + + '[1-9][0-9]{1,4}|[0-9])'; + } + type string { + // route-origin with Type 1 + pattern 'route\-origin:(6[0-5][0-5][0-3][0-5]|' + + '[1-5][0-9]{4}|[1-9][0-9]{1,4}|[0-9]):' + + '(4[0-2][0-9][0-4][0-9][0-6][0-7][0-2][0-9][0-6]|' + + '[1-3][0-9]{9}|[1-9]([0-9]{1,7})?[0-9]|[1-9])'; + } + type string { + // route-origin with Type 2 + pattern 'route\-origin:' + + '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|' + + '25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9][0-9]|' + + '2[0-4][0-9]|25[0-5]):' + + '(6[0-5][0-5][0-3][0-5]|[1-5][0-9]{4}|' + + '[1-9][0-9]{1,4}|[0-9])'; + } + } + description + "Type definition for extended community attributes"; + reference + "RFC 4360 - BGP Extended Communities Attribute"; + } + + typedef bgp-community-regexp-type { + // TODO: needs more work to decide what format these regexps can + // take. + type string; + description + "Type definition for communities specified as regular + expression patterns"; + } + + typedef bgp-origin-attr-type { + type enumeration { + enum igp { + description "Origin of the NLRI is internal"; + } + enum egp { + description "Origin of the NLRI is EGP"; + } + enum incomplete { + description "Origin of the NLRI is neither IGP or EGP"; + } + } + description + "Type definition for standard BGP origin attribute"; + reference + "RFC 4271 - A Border Gateway Protocol 4 (BGP-4), Sec 4.3"; + } + + typedef peer-type { + type enumeration { + enum internal { + description + "internal (iBGP) peer"; + } + enum external { + description + "external (eBGP) peer"; + } + enum confederation { + description + "Confederation as peer"; + } + } + description + "Labels a peer or peer group as explicitly internal, + external or confederation."; + } + + identity REMOVE_PRIVATE_AS_OPTION { + description + "Base identity for options for removing private autonomous + system numbers from the AS_PATH attribute"; + } + + identity PRIVATE_AS_REMOVE_ALL { + base REMOVE_PRIVATE_AS_OPTION; + description + "Strip all private autonomous system numbers from the AS_PATH. + This action is performed regardless of the other content of the + AS_PATH attribute, and for all instances of private AS numbers + within that attribute."; + } + + identity PRIVATE_AS_REPLACE_ALL { + base REMOVE_PRIVATE_AS_OPTION; + description + "Replace all instances of private autonomous system numbers in + the AS_PATH with the local BGP speaker's autonomous system + number. This action is performed regardless of the other + content of the AS_PATH attribute, and for all instances of + private AS number within that attribute."; + } + + typedef remove-private-as-option { + type identityref { + base REMOVE_PRIVATE_AS_OPTION; + } + description + "Set of options for configuring how private AS path numbers + are removed from advertisements"; + } + + typedef percentage { + type uint8 { + range "0..100"; + } + description + "Integer indicating a percentage value"; + } + + typedef rr-cluster-id-type { + type union { + type uint32; + type inet:ipv4-address; + } + description + "Union type for route reflector cluster ids: + option 1: 4-byte number + option 2: IP address"; + } + + typedef community-type { + type bits { + bit standard { + position 0; + description + "Send only standard communities."; + reference + "RFC 1997: BGP Communities Attribute."; + } + bit extended { + description + "Send only extended communities."; + reference + "RFC 4360: BGP Extended Communities Attribute."; + } + bit large { + description + "Send only large communities."; + reference + "RFC 8092: BGP Large Communities Attribute."; + } + } + description + "Type describing variations of community attributes. + The community types can be combined and a value of 0 + implies 'none'"; + } +} diff --git a/yang/ietf/ietf-interfaces.yang b/yang/ietf/ietf-interfaces.yang new file mode 100644 index 0000000000..f66c205ce0 --- /dev/null +++ b/yang/ietf/ietf-interfaces.yang @@ -0,0 +1,1123 @@ +module ietf-interfaces { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:ietf-interfaces"; + prefix if; + + import ietf-yang-types { + prefix yang; + } + + organization + "IETF NETMOD (Network Modeling) Working Group"; + + contact + "WG Web: + WG List: + + Editor: Martin Bjorklund + "; + + description + "This module contains a collection of YANG definitions for + managing network interfaces. + + Copyright (c) 2018 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (https://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 8343; see + the RFC itself for full legal notices."; + + revision 2018-02-20 { + description + "Updated to support NMDA."; + reference + "RFC 8343: A YANG Data Model for Interface Management"; + } + + revision 2014-05-08 { + description + "Initial revision."; + reference + "RFC 7223: A YANG Data Model for Interface Management"; + } + + /* + * Typedefs + */ + + typedef interface-ref { + type leafref { + path "/if:interfaces/if:interface/if:name"; + } + description + "This type is used by data models that need to reference + interfaces."; + } + + /* + * Identities + */ + + identity interface-type { + description + "Base identity from which specific interface types are + derived."; + } + + /* + * Features + */ + + feature arbitrary-names { + description + "This feature indicates that the device allows user-controlled + interfaces to be named arbitrarily."; + } + feature pre-provisioning { + description + "This feature indicates that the device supports + pre-provisioning of interface configuration, i.e., it is + possible to configure an interface whose physical interface + hardware is not present on the device."; + } + feature if-mib { + description + "This feature indicates that the device implements + the IF-MIB."; + reference + "RFC 2863: The Interfaces Group MIB"; + } + + /* + * Data nodes + */ + + container interfaces { + description + "Interface parameters."; + + list interface { + key "name"; + + description + "The list of interfaces on the device. + + The status of an interface is available in this list in the + operational state. If the configuration of a + system-controlled interface cannot be used by the system + (e.g., the interface hardware present does not match the + interface type), then the configuration is not applied to + the system-controlled interface shown in the operational + state. If the configuration of a user-controlled interface + cannot be used by the system, the configured interface is + not instantiated in the operational state. + + System-controlled interfaces created by the system are + always present in this list in the operational state, + whether or not they are configured."; + + leaf name { + type string; + description + "The name of the interface. + + A device MAY restrict the allowed values for this leaf, + possibly depending on the type of the interface. + For system-controlled interfaces, this leaf is the + device-specific name of the interface. + + If a client tries to create configuration for a + system-controlled interface that is not present in the + operational state, the server MAY reject the request if + the implementation does not support pre-provisioning of + interfaces or if the name refers to an interface that can + never exist in the system. A Network Configuration + Protocol (NETCONF) server MUST reply with an rpc-error + with the error-tag 'invalid-value' in this case. + + If the device supports pre-provisioning of interface + configuration, the 'pre-provisioning' feature is + advertised. + + If the device allows arbitrarily named user-controlled + interfaces, the 'arbitrary-names' feature is advertised. + + When a configured user-controlled interface is created by + the system, it is instantiated with the same name in the + operational state. + + A server implementation MAY map this leaf to the ifName + MIB object. Such an implementation needs to use some + mechanism to handle the differences in size and characters + allowed between this leaf and ifName. The definition of + such a mechanism is outside the scope of this document."; + reference + "RFC 2863: The Interfaces Group MIB - ifName"; + } + + leaf description { + type string; + description + "A textual description of the interface. + + A server implementation MAY map this leaf to the ifAlias + MIB object. Such an implementation needs to use some + mechanism to handle the differences in size and characters + allowed between this leaf and ifAlias. The definition of + such a mechanism is outside the scope of this document. + + Since ifAlias is defined to be stored in non-volatile + storage, the MIB implementation MUST map ifAlias to the + value of 'description' in the persistently stored + configuration."; + reference + "RFC 2863: The Interfaces Group MIB - ifAlias"; + } + + leaf type { + type identityref { + base interface-type; + } + mandatory true; + description + "The type of the interface. + + When an interface entry is created, a server MAY + initialize the type leaf with a valid value, e.g., if it + is possible to derive the type from the name of the + interface. + + If a client tries to set the type of an interface to a + value that can never be used by the system, e.g., if the + type is not supported or if the type does not match the + name of the interface, the server MUST reject the request. + A NETCONF server MUST reply with an rpc-error with the + error-tag 'invalid-value' in this case."; + reference + "RFC 2863: The Interfaces Group MIB - ifType"; + } + + leaf enabled { + type boolean; + default "true"; + description + "This leaf contains the configured, desired state of the + interface. + + Systems that implement the IF-MIB use the value of this + leaf in the intended configuration to set + IF-MIB.ifAdminStatus to 'up' or 'down' after an ifEntry + has been initialized, as described in RFC 2863. + + Changes in this leaf in the intended configuration are + reflected in ifAdminStatus."; + reference + "RFC 2863: The Interfaces Group MIB - ifAdminStatus"; + } + + leaf link-up-down-trap-enable { + if-feature if-mib; + type enumeration { + enum enabled { + value 1; + description + "The device will generate linkUp/linkDown SNMP + notifications for this interface."; + } + enum disabled { + value 2; + description + "The device will not generate linkUp/linkDown SNMP + notifications for this interface."; + } + } + description + "Controls whether linkUp/linkDown SNMP notifications + should be generated for this interface. + + If this node is not configured, the value 'enabled' is + operationally used by the server for interfaces that do + not operate on top of any other interface (i.e., there are + no 'lower-layer-if' entries), and 'disabled' otherwise."; + reference + "RFC 2863: The Interfaces Group MIB - + ifLinkUpDownTrapEnable"; + } + + leaf admin-status { + if-feature if-mib; + type enumeration { + enum up { + value 1; + description + "Ready to pass packets."; + } + enum down { + value 2; + description + "Not ready to pass packets and not in some test mode."; + } + enum testing { + value 3; + description + "In some test mode."; + } + } + config false; + mandatory true; + description + "The desired state of the interface. + + This leaf has the same read semantics as ifAdminStatus."; + reference + "RFC 2863: The Interfaces Group MIB - ifAdminStatus"; + } + + leaf oper-status { + type enumeration { + enum up { + value 1; + description + "Ready to pass packets."; + } + enum down { + value 2; + + description + "The interface does not pass any packets."; + } + enum testing { + value 3; + description + "In some test mode. No operational packets can + be passed."; + } + enum unknown { + value 4; + description + "Status cannot be determined for some reason."; + } + enum dormant { + value 5; + description + "Waiting for some external event."; + } + enum not-present { + value 6; + description + "Some component (typically hardware) is missing."; + } + enum lower-layer-down { + value 7; + description + "Down due to state of lower-layer interface(s)."; + } + } + config false; + mandatory true; + description + "The current operational state of the interface. + + This leaf has the same semantics as ifOperStatus."; + reference + "RFC 2863: The Interfaces Group MIB - ifOperStatus"; + } + + leaf last-change { + type yang:date-and-time; + config false; + description + "The time the interface entered its current operational + state. If the current state was entered prior to the + last re-initialization of the local network management + subsystem, then this node is not present."; + reference + "RFC 2863: The Interfaces Group MIB - ifLastChange"; + } + + leaf if-index { + if-feature if-mib; + type int32 { + range "1..2147483647"; + } + config false; + mandatory true; + description + "The ifIndex value for the ifEntry represented by this + interface."; + reference + "RFC 2863: The Interfaces Group MIB - ifIndex"; + } + + leaf phys-address { + type yang:phys-address; + config false; + description + "The interface's address at its protocol sub-layer. For + example, for an 802.x interface, this object normally + contains a Media Access Control (MAC) address. The + interface's media-specific modules must define the bit + and byte ordering and the format of the value of this + object. For interfaces that do not have such an address + (e.g., a serial line), this node is not present."; + reference + "RFC 2863: The Interfaces Group MIB - ifPhysAddress"; + } + + leaf-list higher-layer-if { + type interface-ref; + config false; + description + "A list of references to interfaces layered on top of this + interface."; + reference + "RFC 2863: The Interfaces Group MIB - ifStackTable"; + } + + leaf-list lower-layer-if { + type interface-ref; + config false; + + description + "A list of references to interfaces layered underneath this + interface."; + reference + "RFC 2863: The Interfaces Group MIB - ifStackTable"; + } + + leaf speed { + type yang:gauge64; + units "bits/second"; + config false; + description + "An estimate of the interface's current bandwidth in bits + per second. For interfaces that do not vary in + bandwidth or for those where no accurate estimation can + be made, this node should contain the nominal bandwidth. + For interfaces that have no concept of bandwidth, this + node is not present."; + reference + "RFC 2863: The Interfaces Group MIB - + ifSpeed, ifHighSpeed"; + } + + container statistics { + config false; + description + "A collection of interface-related statistics objects."; + + leaf discontinuity-time { + type yang:date-and-time; + mandatory true; + description + "The time on the most recent occasion at which any one or + more of this interface's counters suffered a + discontinuity. If no such discontinuities have occurred + since the last re-initialization of the local management + subsystem, then this node contains the time the local + management subsystem re-initialized itself."; + } + + leaf in-octets { + type yang:counter64; + description + "The total number of octets received on the interface, + including framing characters. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCInOctets"; + } + + leaf in-unicast-pkts { + type yang:counter64; + description + "The number of packets, delivered by this sub-layer to a + higher (sub-)layer, that were not addressed to a + multicast or broadcast address at this sub-layer. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCInUcastPkts"; + } + + leaf in-broadcast-pkts { + type yang:counter64; + description + "The number of packets, delivered by this sub-layer to a + higher (sub-)layer, that were addressed to a broadcast + address at this sub-layer. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCInBroadcastPkts"; + } + + leaf in-multicast-pkts { + type yang:counter64; + description + "The number of packets, delivered by this sub-layer to a + higher (sub-)layer, that were addressed to a multicast + address at this sub-layer. For a MAC-layer protocol, + this includes both Group and Functional addresses. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCInMulticastPkts"; + } + + leaf in-discards { + type yang:counter32; + description + "The number of inbound packets that were chosen to be + discarded even though no errors had been detected to + prevent their being deliverable to a higher-layer + protocol. One possible reason for discarding such a + packet could be to free up buffer space. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifInDiscards"; + } + + leaf in-errors { + type yang:counter32; + description + "For packet-oriented interfaces, the number of inbound + packets that contained errors preventing them from being + deliverable to a higher-layer protocol. For character- + oriented or fixed-length interfaces, the number of + inbound transmission units that contained errors + preventing them from being deliverable to a higher-layer + protocol. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifInErrors"; + } + + leaf in-unknown-protos { + type yang:counter32; + + description + "For packet-oriented interfaces, the number of packets + received via the interface that were discarded because + of an unknown or unsupported protocol. For + character-oriented or fixed-length interfaces that + support protocol multiplexing, the number of + transmission units received via the interface that were + discarded because of an unknown or unsupported protocol. + For any interface that does not support protocol + multiplexing, this counter is not present. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifInUnknownProtos"; + } + + leaf out-octets { + type yang:counter64; + description + "The total number of octets transmitted out of the + interface, including framing characters. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCOutOctets"; + } + + leaf out-unicast-pkts { + type yang:counter64; + description + "The total number of packets that higher-level protocols + requested be transmitted and that were not addressed + to a multicast or broadcast address at this sub-layer, + including those that were discarded or not sent. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCOutUcastPkts"; + } + + leaf out-broadcast-pkts { + type yang:counter64; + description + "The total number of packets that higher-level protocols + requested be transmitted and that were addressed to a + broadcast address at this sub-layer, including those + that were discarded or not sent. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCOutBroadcastPkts"; + } + + leaf out-multicast-pkts { + type yang:counter64; + description + "The total number of packets that higher-level protocols + requested be transmitted and that were addressed to a + multicast address at this sub-layer, including those + that were discarded or not sent. For a MAC-layer + protocol, this includes both Group and Functional + addresses. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCOutMulticastPkts"; + } + + leaf out-discards { + type yang:counter32; + description + "The number of outbound packets that were chosen to be + discarded even though no errors had been detected to + prevent their being transmitted. One possible reason + for discarding such a packet could be to free up buffer + space. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifOutDiscards"; + } + + leaf out-errors { + type yang:counter32; + description + "For packet-oriented interfaces, the number of outbound + packets that could not be transmitted because of errors. + For character-oriented or fixed-length interfaces, the + number of outbound transmission units that could not be + transmitted because of errors. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifOutErrors"; + } + } + + } + } + + /* + * Legacy typedefs + */ + + typedef interface-state-ref { + type leafref { + path "/if:interfaces-state/if:interface/if:name"; + } + status deprecated; + description + "This type is used by data models that need to reference + the operationally present interfaces."; + } + + /* + * Legacy operational state data nodes + */ + + container interfaces-state { + config false; + status deprecated; + description + "Data nodes for the operational state of interfaces."; + + list interface { + key "name"; + status deprecated; + + description + "The list of interfaces on the device. + + System-controlled interfaces created by the system are + always present in this list, whether or not they are + configured."; + + leaf name { + type string; + status deprecated; + description + "The name of the interface. + + A server implementation MAY map this leaf to the ifName + MIB object. Such an implementation needs to use some + mechanism to handle the differences in size and characters + allowed between this leaf and ifName. The definition of + such a mechanism is outside the scope of this document."; + reference + "RFC 2863: The Interfaces Group MIB - ifName"; + } + + leaf type { + type identityref { + base interface-type; + } + mandatory true; + status deprecated; + description + "The type of the interface."; + reference + "RFC 2863: The Interfaces Group MIB - ifType"; + } + + leaf admin-status { + if-feature if-mib; + type enumeration { + enum up { + value 1; + description + "Ready to pass packets."; + } + enum down { + value 2; + description + "Not ready to pass packets and not in some test mode."; + } + enum testing { + value 3; + description + "In some test mode."; + } + } + mandatory true; + status deprecated; + description + "The desired state of the interface. + + This leaf has the same read semantics as ifAdminStatus."; + reference + "RFC 2863: The Interfaces Group MIB - ifAdminStatus"; + } + + leaf oper-status { + type enumeration { + enum up { + value 1; + description + "Ready to pass packets."; + } + enum down { + value 2; + description + "The interface does not pass any packets."; + } + enum testing { + value 3; + description + "In some test mode. No operational packets can + be passed."; + } + enum unknown { + value 4; + description + "Status cannot be determined for some reason."; + } + enum dormant { + value 5; + description + "Waiting for some external event."; + } + enum not-present { + value 6; + description + "Some component (typically hardware) is missing."; + } + enum lower-layer-down { + value 7; + description + "Down due to state of lower-layer interface(s)."; + } + } + mandatory true; + status deprecated; + description + "The current operational state of the interface. + + This leaf has the same semantics as ifOperStatus."; + reference + "RFC 2863: The Interfaces Group MIB - ifOperStatus"; + } + + leaf last-change { + type yang:date-and-time; + status deprecated; + description + "The time the interface entered its current operational + state. If the current state was entered prior to the + last re-initialization of the local network management + subsystem, then this node is not present."; + reference + "RFC 2863: The Interfaces Group MIB - ifLastChange"; + } + + leaf if-index { + if-feature if-mib; + type int32 { + range "1..2147483647"; + } + mandatory true; + status deprecated; + description + "The ifIndex value for the ifEntry represented by this + interface."; + + reference + "RFC 2863: The Interfaces Group MIB - ifIndex"; + } + + leaf phys-address { + type yang:phys-address; + status deprecated; + description + "The interface's address at its protocol sub-layer. For + example, for an 802.x interface, this object normally + contains a Media Access Control (MAC) address. The + interface's media-specific modules must define the bit + and byte ordering and the format of the value of this + object. For interfaces that do not have such an address + (e.g., a serial line), this node is not present."; + reference + "RFC 2863: The Interfaces Group MIB - ifPhysAddress"; + } + + leaf-list higher-layer-if { + type interface-state-ref; + status deprecated; + description + "A list of references to interfaces layered on top of this + interface."; + reference + "RFC 2863: The Interfaces Group MIB - ifStackTable"; + } + + leaf-list lower-layer-if { + type interface-state-ref; + status deprecated; + description + "A list of references to interfaces layered underneath this + interface."; + reference + "RFC 2863: The Interfaces Group MIB - ifStackTable"; + } + + leaf speed { + type yang:gauge64; + units "bits/second"; + status deprecated; + description + "An estimate of the interface's current bandwidth in bits + per second. For interfaces that do not vary in + bandwidth or for those where no accurate estimation can + + be made, this node should contain the nominal bandwidth. + For interfaces that have no concept of bandwidth, this + node is not present."; + reference + "RFC 2863: The Interfaces Group MIB - + ifSpeed, ifHighSpeed"; + } + + container statistics { + status deprecated; + description + "A collection of interface-related statistics objects."; + + leaf discontinuity-time { + type yang:date-and-time; + mandatory true; + status deprecated; + description + "The time on the most recent occasion at which any one or + more of this interface's counters suffered a + discontinuity. If no such discontinuities have occurred + since the last re-initialization of the local management + subsystem, then this node contains the time the local + management subsystem re-initialized itself."; + } + + leaf in-octets { + type yang:counter64; + status deprecated; + description + "The total number of octets received on the interface, + including framing characters. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCInOctets"; + } + + leaf in-unicast-pkts { + type yang:counter64; + status deprecated; + description + "The number of packets, delivered by this sub-layer to a + higher (sub-)layer, that were not addressed to a + multicast or broadcast address at this sub-layer. + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCInUcastPkts"; + } + + leaf in-broadcast-pkts { + type yang:counter64; + status deprecated; + description + "The number of packets, delivered by this sub-layer to a + higher (sub-)layer, that were addressed to a broadcast + address at this sub-layer. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCInBroadcastPkts"; + } + + leaf in-multicast-pkts { + type yang:counter64; + status deprecated; + description + "The number of packets, delivered by this sub-layer to a + higher (sub-)layer, that were addressed to a multicast + address at this sub-layer. For a MAC-layer protocol, + this includes both Group and Functional addresses. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCInMulticastPkts"; + } + + leaf in-discards { + type yang:counter32; + status deprecated; + + description + "The number of inbound packets that were chosen to be + discarded even though no errors had been detected to + prevent their being deliverable to a higher-layer + protocol. One possible reason for discarding such a + packet could be to free up buffer space. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifInDiscards"; + } + + leaf in-errors { + type yang:counter32; + status deprecated; + description + "For packet-oriented interfaces, the number of inbound + packets that contained errors preventing them from being + deliverable to a higher-layer protocol. For character- + oriented or fixed-length interfaces, the number of + inbound transmission units that contained errors + preventing them from being deliverable to a higher-layer + protocol. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifInErrors"; + } + + leaf in-unknown-protos { + type yang:counter32; + status deprecated; + description + "For packet-oriented interfaces, the number of packets + received via the interface that were discarded because + of an unknown or unsupported protocol. For + character-oriented or fixed-length interfaces that + support protocol multiplexing, the number of + transmission units received via the interface that were + discarded because of an unknown or unsupported protocol. + For any interface that does not support protocol + multiplexing, this counter is not present. + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifInUnknownProtos"; + } + + leaf out-octets { + type yang:counter64; + status deprecated; + description + "The total number of octets transmitted out of the + interface, including framing characters. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCOutOctets"; + } + + leaf out-unicast-pkts { + type yang:counter64; + status deprecated; + description + "The total number of packets that higher-level protocols + requested be transmitted and that were not addressed + to a multicast or broadcast address at this sub-layer, + including those that were discarded or not sent. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCOutUcastPkts"; + } + + leaf out-broadcast-pkts { + type yang:counter64; + status deprecated; + + description + "The total number of packets that higher-level protocols + requested be transmitted and that were addressed to a + broadcast address at this sub-layer, including those + that were discarded or not sent. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCOutBroadcastPkts"; + } + + leaf out-multicast-pkts { + type yang:counter64; + status deprecated; + description + "The total number of packets that higher-level protocols + requested be transmitted and that were addressed to a + multicast address at this sub-layer, including those + that were discarded or not sent. For a MAC-layer + protocol, this includes both Group and Functional + addresses. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCOutMulticastPkts"; + } + + leaf out-discards { + type yang:counter32; + status deprecated; + description + "The number of outbound packets that were chosen to be + discarded even though no errors had been detected to + prevent their being transmitted. One possible reason + for discarding such a packet could be to free up buffer + space. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifOutDiscards"; + } + + leaf out-errors { + type yang:counter32; + status deprecated; + description + "For packet-oriented interfaces, the number of outbound + packets that could not be transmitted because of errors. + For character-oriented or fixed-length interfaces, the + number of outbound transmission units that could not be + transmitted because of errors. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifOutErrors"; + } + } + } + } +} diff --git a/yang/ietf/ietf-routing-types.yang b/yang/ietf/ietf-routing-types.yang new file mode 100644 index 0000000000..a30427eb10 --- /dev/null +++ b/yang/ietf/ietf-routing-types.yang @@ -0,0 +1,751 @@ +module ietf-routing-types { + namespace "urn:ietf:params:xml:ns:yang:ietf-routing-types"; + prefix rt-types; + + import ietf-yang-types { + prefix yang; + } + import ietf-inet-types { + prefix inet; + } + + organization + "IETF RTGWG - Routing Area Working Group"; + contact + "WG Web: + WG List: + Editors: Xufeng Liu + + Yingzhen Qu + + Acee Lindem + + Christian Hopps + + Lou Berger + "; + + description + "This module contains a collection of YANG data types + considered generally useful for routing protocols. + Copyright (c) 2017 IETF Trust and the persons + identified as authors of the code. All rights reserved. + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (https://trustee.ietf.org/license-info). + This version of this YANG module is part of RFC 8294; see + the RFC itself for full legal notices."; + revision 2017-12-04 { + description "Initial revision."; + reference + "RFC 8294: Common YANG Data Types for the Routing Area. + Section 3."; + } + + /*** Identities related to MPLS/GMPLS ***/ + + identity mpls-label-special-purpose-value { + description + "Base identity for deriving identities describing + special-purpose Multiprotocol Label Switching (MPLS) label + values."; + reference + "RFC 7274: Allocating and Retiring Special-Purpose MPLS + Labels."; + } + + identity ipv4-explicit-null-label { + base mpls-label-special-purpose-value; + description + "This identity represents the IPv4 Explicit NULL Label."; + reference + "RFC 3032: MPLS Label Stack Encoding. Section 2.1."; + } + + identity router-alert-label { + base mpls-label-special-purpose-value; + description + "This identity represents the Router Alert Label."; + reference + "RFC 3032: MPLS Label Stack Encoding. Section 2.1."; + } + + identity ipv6-explicit-null-label { + base mpls-label-special-purpose-value; + description + "This identity represents the IPv6 Explicit NULL Label."; + reference + "RFC 3032: MPLS Label Stack Encoding. Section 2.1."; + } + + identity implicit-null-label { + base mpls-label-special-purpose-value; + description + "This identity represents the Implicit NULL Label."; + reference + "RFC 3032: MPLS Label Stack Encoding. Section 2.1."; + } + + identity entropy-label-indicator { + base mpls-label-special-purpose-value; + description + "This identity represents the Entropy Label Indicator."; + reference + "RFC 6790: The Use of Entropy Labels in MPLS Forwarding. + Sections 3 and 10.1."; + } + + identity gal-label { + base mpls-label-special-purpose-value; + description + "This identity represents the Generic Associated Channel + (G-ACh) Label (GAL)."; + reference + "RFC 5586: MPLS Generic Associated Channel. + Sections 4 and 10."; + } + + identity oam-alert-label { + base mpls-label-special-purpose-value; + description + "This identity represents the OAM Alert Label."; + reference + "RFC 3429: Assignment of the 'OAM Alert Label' for + Multiprotocol Label Switching Architecture (MPLS) + Operation and Maintenance (OAM) Functions. + Sections 3 and 6."; + } + + identity extension-label { + base mpls-label-special-purpose-value; + description + "This identity represents the Extension Label."; + reference + "RFC 7274: Allocating and Retiring Special-Purpose MPLS + Labels. Sections 3.1 and 5."; + } + + /*** Collection of types related to routing ***/ + + typedef router-id { + type yang:dotted-quad; + description + "A 32-bit number in the dotted-quad format assigned to each + router. This number uniquely identifies the router within + an Autonomous System."; + } + + /*** Collection of types related to VPNs ***/ + + typedef route-target { + type string { + pattern + '(0:(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|' + + '6[0-4][0-9]{3}|' + + '[1-5][0-9]{4}|[1-9][0-9]{0,3}|0):(429496729[0-5]|' + + '42949672[0-8][0-9]|' + + '4294967[01][0-9]{2}|429496[0-6][0-9]{3}|' + + '42949[0-5][0-9]{4}|' + + '4294[0-8][0-9]{5}|429[0-3][0-9]{6}|' + + '42[0-8][0-9]{7}|4[01][0-9]{8}|' + + '[1-3][0-9]{9}|[1-9][0-9]{0,8}|0))|' + + '(1:((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|' + + '25[0-5])\.){3}([0-9]|[1-9][0-9]|' + + '1[0-9]{2}|2[0-4][0-9]|25[0-5])):(6553[0-5]|' + + '655[0-2][0-9]|' + + '65[0-4][0-9]{2}|6[0-4][0-9]{3}|' + + '[1-5][0-9]{4}|[1-9][0-9]{0,3}|0))|' + + '(2:(429496729[0-5]|42949672[0-8][0-9]|' + + '4294967[01][0-9]{2}|' + + '429496[0-6][0-9]{3}|42949[0-5][0-9]{4}|' + + '4294[0-8][0-9]{5}|' + + '429[0-3][0-9]{6}|42[0-8][0-9]{7}|4[01][0-9]{8}|' + + '[1-3][0-9]{9}|[1-9][0-9]{0,8}|0):' + + '(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|' + + '6[0-4][0-9]{3}|' + + '[1-5][0-9]{4}|[1-9][0-9]{0,3}|0))|' + + '(6(:[a-fA-F0-9]{2}){6})|' + + '(([3-57-9a-fA-F]|[1-9a-fA-F][0-9a-fA-F]{1,3}):' + + '[0-9a-fA-F]{1,12})'; + } + + description + "A Route Target is an 8-octet BGP extended community + initially identifying a set of sites in a BGP VPN + (RFC 4364). However, it has since taken on a more general + role in BGP route filtering. A Route Target consists of two + or three fields: a 2-octet Type field, an administrator + field, and, optionally, an assigned number field. + According to the data formats for types 0, 1, 2, and 6 as + defined in RFC 4360, RFC 5668, and RFC 7432, the encoding + pattern is defined as: + 0:2-octet-asn:4-octet-number + 1:4-octet-ipv4addr:2-octet-number + 2:4-octet-asn:2-octet-number + 6:6-octet-mac-address + Additionally, a generic pattern is defined for future + Route Target types: + 2-octet-other-hex-number:6-octet-hex-number + Some valid examples are 0:100:100, 1:1.1.1.1:100, + 2:1234567890:203, and 6:26:00:08:92:78:00."; + reference + "RFC 4360: BGP Extended Communities Attribute. + RFC 4364: BGP/MPLS IP Virtual Private Networks (VPNs). + RFC 5668: 4-Octet AS Specific BGP Extended Community. + RFC 7432: BGP MPLS-Based Ethernet VPN."; + } + + typedef ipv6-route-target { + type string { + pattern + '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}' + + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|' + + '(((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\.){3}' + + '(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])))' + + ':' + + '(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|' + + '6[0-4][0-9]{3}|' + + '[1-5][0-9]{4}|[1-9][0-9]{0,3}|0)'; + pattern '((([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|' + + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?))' + + ':' + + '(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|' + + '6[0-4][0-9]{3}|' + + '[1-5][0-9]{4}|[1-9][0-9]{0,3}|0)'; + } + description + "An IPv6 Route Target is a 20-octet BGP IPv6 Address + Specific Extended Community serving the same function + as a standard 8-octet Route Target, except that it only + allows an IPv6 address as the global administrator. + The format is . + Two valid examples are 2001:db8::1:6544 and + 2001:db8::5eb1:791:6b37:17958."; + reference + "RFC 5701: IPv6 Address Specific BGP Extended Community + Attribute."; + } + + typedef route-target-type { + type enumeration { + enum import { + value 0; + description + "The Route Target applies to route import."; + } + enum export { + value 1; + description + "The Route Target applies to route export."; + } + + enum both { + value 2; + description + "The Route Target applies to both route import and + route export."; + } + } + description + "Indicates the role a Route Target takes in route filtering."; + reference + "RFC 4364: BGP/MPLS IP Virtual Private Networks (VPNs)."; + } + + typedef route-distinguisher { + type string { + pattern + '(0:(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|' + + '6[0-4][0-9]{3}|' + + '[1-5][0-9]{4}|[1-9][0-9]{0,3}|0):(429496729[0-5]|' + + '42949672[0-8][0-9]|' + + '4294967[01][0-9]{2}|429496[0-6][0-9]{3}|' + + '42949[0-5][0-9]{4}|' + + '4294[0-8][0-9]{5}|429[0-3][0-9]{6}|' + + '42[0-8][0-9]{7}|4[01][0-9]{8}|' + + '[1-3][0-9]{9}|[1-9][0-9]{0,8}|0))|' + + '(1:((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|' + + '25[0-5])\.){3}([0-9]|[1-9][0-9]|' + + '1[0-9]{2}|2[0-4][0-9]|25[0-5])):(6553[0-5]|' + + '655[0-2][0-9]|' + + '65[0-4][0-9]{2}|6[0-4][0-9]{3}|' + + '[1-5][0-9]{4}|[1-9][0-9]{0,3}|0))|' + + '(2:(429496729[0-5]|42949672[0-8][0-9]|' + + '4294967[01][0-9]{2}|' + + '429496[0-6][0-9]{3}|42949[0-5][0-9]{4}|' + + '4294[0-8][0-9]{5}|' + + '429[0-3][0-9]{6}|42[0-8][0-9]{7}|4[01][0-9]{8}|' + + '[1-3][0-9]{9}|[1-9][0-9]{0,8}|0):' + + '(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|' + + '6[0-4][0-9]{3}|' + + '[1-5][0-9]{4}|[1-9][0-9]{0,3}|0))|' + + '(6(:[a-fA-F0-9]{2}){6})|' + + '(([3-57-9a-fA-F]|[1-9a-fA-F][0-9a-fA-F]{1,3}):' + + '[0-9a-fA-F]{1,12})'; + } + + description + "A Route Distinguisher is an 8-octet value used to + distinguish routes from different BGP VPNs (RFC 4364). + A Route Distinguisher will have the same format as a + Route Target as per RFC 4360 and will consist of + two or three fields: a 2-octet Type field, an administrator + field, and, optionally, an assigned number field. + According to the data formats for types 0, 1, 2, and 6 as + defined in RFC 4360, RFC 5668, and RFC 7432, the encoding + pattern is defined as: + 0:2-octet-asn:4-octet-number + 1:4-octet-ipv4addr:2-octet-number + 2:4-octet-asn:2-octet-number + 6:6-octet-mac-address + Additionally, a generic pattern is defined for future + route discriminator types: + 2-octet-other-hex-number:6-octet-hex-number + Some valid examples are 0:100:100, 1:1.1.1.1:100, + 2:1234567890:203, and 6:26:00:08:92:78:00."; + reference + "RFC 4360: BGP Extended Communities Attribute. + RFC 4364: BGP/MPLS IP Virtual Private Networks (VPNs). + RFC 5668: 4-Octet AS Specific BGP Extended Community. + RFC 7432: BGP MPLS-Based Ethernet VPN."; + } + + typedef route-origin { + type string { + pattern + '(0:(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|' + + '6[0-4][0-9]{3}|' + + '[1-5][0-9]{4}|[1-9][0-9]{0,3}|0):(429496729[0-5]|' + + '42949672[0-8][0-9]|' + + '4294967[01][0-9]{2}|429496[0-6][0-9]{3}|' + + '42949[0-5][0-9]{4}|' + + '4294[0-8][0-9]{5}|429[0-3][0-9]{6}|' + + '42[0-8][0-9]{7}|4[01][0-9]{8}|' + + '[1-3][0-9]{9}|[1-9][0-9]{0,8}|0))|' + + '(1:((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|' + + '25[0-5])\.){3}([0-9]|[1-9][0-9]|' + + '1[0-9]{2}|2[0-4][0-9]|25[0-5])):(6553[0-5]|' + + '655[0-2][0-9]|' + + '65[0-4][0-9]{2}|6[0-4][0-9]{3}|' + + '[1-5][0-9]{4}|[1-9][0-9]{0,3}|0))|' + + '(2:(429496729[0-5]|42949672[0-8][0-9]|' + + '4294967[01][0-9]{2}|' + + '429496[0-6][0-9]{3}|42949[0-5][0-9]{4}|' + + '4294[0-8][0-9]{5}|' + + '429[0-3][0-9]{6}|42[0-8][0-9]{7}|4[01][0-9]{8}|' + + '[1-3][0-9]{9}|[1-9][0-9]{0,8}|0):' + + '(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|' + + '6[0-4][0-9]{3}|' + + '[1-5][0-9]{4}|[1-9][0-9]{0,3}|0))|' + + '(6(:[a-fA-F0-9]{2}){6})|' + + '(([3-57-9a-fA-F]|[1-9a-fA-F][0-9a-fA-F]{1,3}):' + + '[0-9a-fA-F]{1,12})'; + } + description + "A Route Origin is an 8-octet BGP extended community + identifying the set of sites where the BGP route + originated (RFC 4364). A Route Origin will have the same + format as a Route Target as per RFC 4360 and will consist + of two or three fields: a 2-octet Type field, an + administrator field, and, optionally, an assigned number + field. + According to the data formats for types 0, 1, 2, and 6 as + defined in RFC 4360, RFC 5668, and RFC 7432, the encoding + pattern is defined as: + 0:2-octet-asn:4-octet-number + 1:4-octet-ipv4addr:2-octet-number + 2:4-octet-asn:2-octet-number + 6:6-octet-mac-address + Additionally, a generic pattern is defined for future + Route Origin types: + 2-octet-other-hex-number:6-octet-hex-number + Some valid examples are 0:100:100, 1:1.1.1.1:100, + 2:1234567890:203, and 6:26:00:08:92:78:00."; + reference + "RFC 4360: BGP Extended Communities Attribute. + RFC 4364: BGP/MPLS IP Virtual Private Networks (VPNs). + RFC 5668: 4-Octet AS Specific BGP Extended Community. + RFC 7432: BGP MPLS-Based Ethernet VPN."; + } + + typedef ipv6-route-origin { + type string { + pattern + '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}' + + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|' + + '(((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\.){3}' + + '(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])))' + + ':' + + '(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|' + + '6[0-4][0-9]{3}|' + + '[1-5][0-9]{4}|[1-9][0-9]{0,3}|0)'; + pattern '((([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|' + + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?))' + + ':' + + '(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|' + + '6[0-4][0-9]{3}|' + + '[1-5][0-9]{4}|[1-9][0-9]{0,3}|0)'; + } + description + "An IPv6 Route Origin is a 20-octet BGP IPv6 Address + Specific Extended Community serving the same function + as a standard 8-octet route, except that it only allows + an IPv6 address as the global administrator. The format + is . + Two valid examples are 2001:db8::1:6544 and + 2001:db8::5eb1:791:6b37:17958."; + reference + "RFC 5701: IPv6 Address Specific BGP Extended Community + Attribute."; + } + + /*** Collection of types common to multicast ***/ + + typedef ipv4-multicast-group-address { + type inet:ipv4-address { + pattern '(2((2[4-9])|(3[0-9]))\.).*'; + } + description + "This type represents an IPv4 multicast group address, + which is in the range of 224.0.0.0 to 239.255.255.255."; + reference + "RFC 1112: Host Extensions for IP Multicasting."; + } + + typedef ipv6-multicast-group-address { + type inet:ipv6-address { + pattern '(([fF]{2}[0-9a-fA-F]{2}):).*'; + } + description + "This type represents an IPv6 multicast group address, + which is in the range of ff00::/8."; + reference + "RFC 4291: IP Version 6 Addressing Architecture. Section 2.7. + RFC 7346: IPv6 Multicast Address Scopes."; + } + + typedef ip-multicast-group-address { + type union { + type ipv4-multicast-group-address; + type ipv6-multicast-group-address; + } + description + "This type represents a version-neutral IP multicast group + address. The format of the textual representation implies + the IP version."; + } + + typedef ipv4-multicast-source-address { + type union { + type enumeration { + enum * { + description + "Any source address."; + } + } + type inet:ipv4-address; + } + description + "Multicast source IPv4 address type."; + } + + typedef ipv6-multicast-source-address { + type union { + type enumeration { + enum * { + description + "Any source address."; + } + } + type inet:ipv6-address; + } + description + "Multicast source IPv6 address type."; + } + + /*** Collection of types common to protocols ***/ + + typedef bandwidth-ieee-float32 { + type string { + pattern + '0[xX](0((\.0?)?[pP](\+)?0?|(\.0?))|' + + '1(\.([0-9a-fA-F]{0,5}[02468aAcCeE]?)?)?[pP](\+)?(12[0-7]|' + + '1[01][0-9]|0?[0-9]?[0-9])?)'; + } + description + "Bandwidth in IEEE 754 floating-point 32-bit binary format: + (-1)**(S) * 2**(Exponent-127) * (1 + Fraction), + where Exponent uses 8 bits and Fraction uses 23 bits. + The units are octets per second. + The encoding format is the external hexadecimal-significant + character sequences specified in IEEE 754 and ISO/IEC C99. + The format is restricted to be normalized, non-negative, and + non-fraction: 0x1.hhhhhhp{+}d, 0X1.HHHHHHP{+}D, or 0x0p0, + where 'h' and 'H' are hexadecimal digits and 'd' and 'D' are + integers in the range of [0..127]. + When six hexadecimal digits are used for 'hhhhhh' or + 'HHHHHH', the least significant digit must be an even + number. 'x' and 'X' indicate hexadecimal; 'p' and 'P' + indicate a power of two. Some examples are 0x0p0, 0x1p10, + and 0x1.abcde2p+20."; + reference + "IEEE Std 754-2008: IEEE Standard for Floating-Point + Arithmetic. + ISO/IEC C99: Information technology - Programming + Languages - C."; + } + + typedef link-access-type { + type enumeration { + enum broadcast { + description + "Specify broadcast multi-access network."; + } + enum non-broadcast-multiaccess { + description + "Specify Non-Broadcast Multi-Access (NBMA) network."; + } + enum point-to-multipoint { + description + "Specify point-to-multipoint network."; + } + enum point-to-point { + description + "Specify point-to-point network."; + } + } + description + "Link access type."; + } + + typedef timer-multiplier { + type uint8; + description + "The number of timer value intervals that should be + interpreted as a failure."; + } + + typedef timer-value-seconds16 { + type union { + type uint16 { + range "1..65535"; + } + type enumeration { + enum infinity { + description + "The timer is set to infinity."; + } + enum not-set { + description + "The timer is not set."; + } + } + } + units "seconds"; + description + "Timer value type, in seconds (16-bit range)."; + } + + typedef timer-value-seconds32 { + type union { + type uint32 { + range "1..4294967295"; + } + type enumeration { + enum infinity { + description + "The timer is set to infinity."; + } + enum not-set { + description + "The timer is not set."; + } + } + } + units "seconds"; + description + "Timer value type, in seconds (32-bit range)."; + } + + typedef timer-value-milliseconds { + type union { + type uint32 { + range "1..4294967295"; + } + type enumeration { + enum infinity { + description + "The timer is set to infinity."; + } + enum not-set { + description + "The timer is not set."; + } + } + } + units "milliseconds"; + description + "Timer value type, in milliseconds."; + } + + typedef percentage { + type uint8 { + range "0..100"; + } + description + "Integer indicating a percentage value."; + } + + typedef timeticks64 { + type uint64; + description + "This type is based on the timeticks type defined in + RFC 6991, but with 64-bit width. It represents the time, + modulo 2^64, in hundredths of a second between two epochs."; + reference + "RFC 6991: Common YANG Data Types."; + } + + typedef uint24 { + type uint32 { + range "0..16777215"; + } + description + "24-bit unsigned integer."; + } + + /*** Collection of types related to MPLS/GMPLS ***/ + + typedef generalized-label { + type binary; + description + "Generalized Label. Nodes sending and receiving the + Generalized Label are aware of the link-specific + label context and type."; + reference + "RFC 3471: Generalized Multi-Protocol Label Switching (GMPLS) + Signaling Functional Description. Section 3.2."; + } + + typedef mpls-label-special-purpose { + type identityref { + base mpls-label-special-purpose-value; + } + description + "This type represents the special-purpose MPLS label values."; + reference + "RFC 3032: MPLS Label Stack Encoding. + RFC 7274: Allocating and Retiring Special-Purpose MPLS + Labels."; + } + + typedef mpls-label-general-use { + type uint32 { + range "16..1048575"; + } + description + "The 20-bit label value in an MPLS label stack as specified + in RFC 3032. This label value does not include the + encodings of Traffic Class and TTL (Time to Live). + The label range specified by this type is for general use, + with special-purpose MPLS label values excluded."; + reference + "RFC 3032: MPLS Label Stack Encoding."; + } + + typedef mpls-label { + type union { + type mpls-label-special-purpose; + type mpls-label-general-use; + } + description + "The 20-bit label value in an MPLS label stack as specified + in RFC 3032. This label value does not include the + encodings of Traffic Class and TTL."; + reference + "RFC 3032: MPLS Label Stack Encoding."; + } + + /*** Groupings **/ + + grouping mpls-label-stack { + description + "This grouping specifies an MPLS label stack. The label + stack is encoded as a list of label stack entries. The + list key is an identifier that indicates the relative + ordering of each entry, with the lowest-value identifier + corresponding to the top of the label stack."; + container mpls-label-stack { + description + "Container for a list of MPLS label stack entries."; + list entry { + key "id"; + description + "List of MPLS label stack entries."; + leaf id { + type uint8; + description + "Identifies the entry in a sequence of MPLS label + stack entries. An entry with a smaller identifier + value precedes an entry with a larger identifier + value in the label stack. The value of this ID has + no semantic meaning other than relative ordering + and referencing the entry."; + } + leaf label { + type rt-types:mpls-label; + description + "Label value."; + } + + leaf ttl { + type uint8; + description + "Time to Live (TTL)."; + reference + "RFC 3032: MPLS Label Stack Encoding."; + } + leaf traffic-class { + type uint8 { + range "0..7"; + } + description + "Traffic Class (TC)."; + reference + "RFC 5462: Multiprotocol Label Switching (MPLS) Label + Stack Entry: 'EXP' Field Renamed to 'Traffic Class' + Field."; + } + } + } + } + + grouping vpn-route-targets { + description + "A grouping that specifies Route Target import-export rules + used in BGP-enabled VPNs."; + reference + "RFC 4364: BGP/MPLS IP Virtual Private Networks (VPNs). + RFC 4664: Framework for Layer 2 Virtual Private Networks + (L2VPNs)."; + list vpn-target { + key "route-target"; + description + "List of Route Targets."; + leaf route-target { + type rt-types:route-target; + description + "Route Target value."; + } + leaf route-target-type { + type rt-types:route-target-type; + mandatory true; + description + "Import/export type of the Route Target."; + } + } + } +} diff --git a/yang/libyang_plugins/frr_user_types.c b/yang/libyang_plugins/frr_user_types.c index 4814f5bc1d..48cdceccf4 100644 --- a/yang/libyang_plugins/frr_user_types.c +++ b/yang/libyang_plugins/frr_user_types.c @@ -20,6 +20,7 @@ #include #include "prefix.h" +#include "ipaddr.h" #include @@ -53,6 +54,21 @@ static int ipv6_address_store_clb(const char *type_name, const char *value_str, return 0; } +static int ip_address_store_clb(const char *type_name, const char *value_str, + lyd_val *value, char **err_msg) +{ + value->ptr = malloc(sizeof(struct ipaddr)); + if (!value->ptr) + return 1; + + if (str2ipaddr(value_str, value->ptr)) { + free(value->ptr); + return 1; + } + + return 0; +} + static int ipv4_prefix_store_clb(const char *type_name, const char *value_str, lyd_val *value, char **err_msg) { @@ -83,6 +99,21 @@ static int ipv6_prefix_store_clb(const char *type_name, const char *value_str, return 0; } +static int ip_prefix_store_clb(const char *type_name, const char *value_str, + lyd_val *value, char **err_msg) +{ + value->ptr = malloc(sizeof(struct prefix)); + if (!value->ptr) + return 1; + + if (str2prefix(value_str, value->ptr) == 0) { + free(value->ptr); + return 1; + } + + return 0; +} + struct lytype_plugin_list frr_user_types[] = { {"ietf-inet-types", "2013-07-15", "ipv4-address", ipv4_address_store_clb, free}, @@ -92,9 +123,13 @@ struct lytype_plugin_list frr_user_types[] = { ipv6_address_store_clb, free}, {"ietf-inet-types", "2013-07-15", "ipv6-address-no-zone", ipv6_address_store_clb, free}, + {"ietf-inet-types", "2013-07-15", "ip-address", ip_address_store_clb, + free}, {"ietf-inet-types", "2013-07-15", "ipv4-prefix", ipv4_prefix_store_clb, free}, {"ietf-inet-types", "2013-07-15", "ipv6-prefix", ipv6_prefix_store_clb, free}, + {"ietf-inet-types", "2013-07-15", "ip-prefix", ip_prefix_store_clb, + free}, {NULL, NULL, NULL, NULL, NULL} /* terminating item */ }; diff --git a/yang/libyang_plugins/subdir.am b/yang/libyang_plugins/subdir.am index fe5f34a28a..837908a1b3 100644 --- a/yang/libyang_plugins/subdir.am +++ b/yang/libyang_plugins/subdir.am @@ -2,13 +2,6 @@ # libyang user types # -if LIBYANG_EXT_BUILTIN -lib_libfrr_la_SOURCES += yang/libyang_plugins/frr_user_types.c -else -libyang_plugins_LTLIBRARIES += yang/libyang_plugins/frr_user_types.la -endif - -yang_libyang_plugins_frr_user_types_la_CFLAGS = $(WERROR) $(LIBYANG_CFLAGS) -yang_libyang_plugins_frr_user_types_la_LDFLAGS = -avoid-version -module -shared -export-dynamic -yang_libyang_plugins_frr_user_types_la_LIBADD = -yang_libyang_plugins_frr_user_types_la_SOURCES = yang/libyang_plugins/frr_user_types.c +# XXX: disable support for libyang custom user types temporarily to facilitate +# the transition from libyang 0.x to libyang 1.x. +#lib_libfrr_la_SOURCES += yang/libyang_plugins/frr_user_types.c diff --git a/yang/subdir.am b/yang/subdir.am index c95ec4dbff..6aae0e4701 100644 --- a/yang/subdir.am +++ b/yang/subdir.am @@ -19,10 +19,25 @@ EXTRA_DIST += yang/embedmodel.py # global symbols :(. Just put it in the daemon. Dynamic libraries.so work # without problems, as seen in libfrr. +dist_yangmodels_DATA += yang/frr-filter.yang dist_yangmodels_DATA += yang/frr-module-translator.yang +dist_yangmodels_DATA += yang/frr-nexthop.yang dist_yangmodels_DATA += yang/frr-test-module.yang dist_yangmodels_DATA += yang/frr-interface.yang +dist_yangmodels_DATA += yang/frr-route-map.yang +dist_yangmodels_DATA += yang/frr-vrf.yang dist_yangmodels_DATA += yang/frr-route-types.yang +dist_yangmodels_DATA += yang/frr-routing.yang +dist_yangmodels_DATA += yang/ietf/ietf-routing-types.yang +dist_yangmodels_DATA += yang/ietf/ietf-interfaces.yang + +if BFDD +dist_yangmodels_DATA += yang/frr-bfdd.yang +endif + +if EIGRPD +dist_yangmodels_DATA += yang/frr-eigrpd.yang +endif if RIPD dist_yangmodels_DATA += yang/frr-ripd.yang @@ -35,3 +50,21 @@ endif if ISISD dist_yangmodels_DATA += yang/frr-isisd.yang endif + +if VRRPD +dist_yangmodels_DATA += yang/frr-vrrpd.yang +endif + +if STATICD +dist_yangmodels_DATA += yang/frr-staticd.yang +endif + +if ZEBRA +dist_yangmodels_DATA += yang/frr-zebra.yang +endif + +if PIMD +dist_yangmodels_DATA += yang/frr-igmp.yang +dist_yangmodels_DATA += yang/frr-pim.yang +dist_yangmodels_DATA += yang/frr-pim-rp.yang +endif diff --git a/zebra/connected.c b/zebra/connected.c index 7114a3286b..8c4ba163bd 100644 --- a/zebra/connected.c +++ b/zebra/connected.c @@ -65,7 +65,7 @@ static void connected_withdraw(struct connected *ifc) if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_CONFIGURED)) { listnode_delete(ifc->ifp->connected, ifc); - connected_free(ifc); + connected_free(&ifc); } } @@ -120,10 +120,6 @@ struct connected *connected_check_ptp(struct interface *ifp, struct connected *ifc; struct listnode *node; - /* ignore broadcast addresses */ - if (p->prefixlen != IPV4_MAX_PREFIXLEN) - d = NULL; - for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) { if (!prefix_same(ifc->address, p)) continue; @@ -143,6 +139,12 @@ static int connected_same(struct connected *ifc1, struct connected *ifc2) if (ifc1->ifp != ifc2->ifp) return 0; + if (ifc1->flags != ifc2->flags) + return 0; + + if (ifc1->conf != ifc2->conf) + return 0; + if (ifc1->destination) if (!ifc2->destination) return 0; @@ -154,12 +156,6 @@ static int connected_same(struct connected *ifc1, struct connected *ifc2) if (!prefix_same(ifc1->destination, ifc2->destination)) return 0; - if (ifc1->flags != ifc2->flags) - return 0; - - if (ifc1->conf != ifc2->conf) - return 0; - return 1; } @@ -181,7 +177,7 @@ static void connected_update(struct interface *ifp, struct connected *ifc) */ if (connected_same(current, ifc)) { /* nothing to do */ - connected_free(ifc); + connected_free(&ifc); return; } @@ -203,14 +199,23 @@ static void connected_update(struct interface *ifp, struct connected *ifc) void connected_up(struct interface *ifp, struct connected *ifc) { afi_t afi; - struct prefix p; + struct prefix p = {0}; struct nexthop nh = { .type = NEXTHOP_TYPE_IFINDEX, .ifindex = ifp->ifindex, .vrf_id = ifp->vrf_id, }; + struct zebra_vrf *zvrf; uint32_t metric; + zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id); + if (!zvrf) { + flog_err( + EC_ZEBRA_VRF_NOT_FOUND, + "%s: Received Up for interface but no associated zvrf: %d", + __func__, ifp->vrf_id); + return; + } if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_REAL)) return; @@ -231,7 +236,7 @@ void connected_up(struct interface *ifp, struct connected *ifc) return; break; case AFI_IP6: -#ifndef LINUX +#ifndef GNU_LINUX /* XXX: It is already done by rib_bogus_ipv6 within rib_add */ if (IN6_IS_ADDR_UNSPECIFIED(&p.u.prefix6)) return; @@ -246,39 +251,29 @@ void connected_up(struct interface *ifp, struct connected *ifc) metric = (ifc->metric < (uint32_t)METRIC_MAX) ? ifc->metric : ifp->metric; - rib_add(afi, SAFI_UNICAST, ifp->vrf_id, ZEBRA_ROUTE_CONNECT, 0, 0, &p, - NULL, &nh, RT_TABLE_MAIN, metric, 0, 0, 0); - - rib_add(afi, SAFI_MULTICAST, ifp->vrf_id, ZEBRA_ROUTE_CONNECT, 0, 0, &p, - NULL, &nh, RT_TABLE_MAIN, metric, 0, 0, 0); + rib_add(afi, SAFI_UNICAST, zvrf->vrf->vrf_id, ZEBRA_ROUTE_CONNECT, + 0, 0, &p, NULL, &nh, 0, zvrf->table_id, metric, 0, 0, 0); - if (IS_ZEBRA_DEBUG_RIB_DETAILED) { - char buf[PREFIX_STRLEN]; - - zlog_debug( - "%u: IF %s address %s add/up, scheduling RIB processing", - ifp->vrf_id, ifp->name, - prefix2str(&p, buf, sizeof(buf))); - } - rib_update(ifp->vrf_id, RIB_UPDATE_IF_CHANGE); + rib_add(afi, SAFI_MULTICAST, zvrf->vrf->vrf_id, ZEBRA_ROUTE_CONNECT, + 0, 0, &p, NULL, &nh, 0, zvrf->table_id, metric, 0, 0, 0); /* Schedule LSP forwarding entries for processing, if appropriate. */ - if (ifp->vrf_id == VRF_DEFAULT) { + if (zvrf->vrf->vrf_id == VRF_DEFAULT) { if (IS_ZEBRA_DEBUG_MPLS) { char buf[PREFIX_STRLEN]; zlog_debug( "%u: IF %s IP %s address add/up, scheduling MPLS processing", - ifp->vrf_id, ifp->name, + zvrf->vrf->vrf_id, ifp->name, prefix2str(&p, buf, sizeof(buf))); } - mpls_mark_lsps_for_processing(vrf_info_lookup(ifp->vrf_id), &p); + mpls_mark_lsps_for_processing(zvrf, &p); } } /* Add connected IPv4 route to the interface. */ void connected_add_ipv4(struct interface *ifp, int flags, struct in_addr *addr, - uint16_t prefixlen, struct in_addr *broad, + uint16_t prefixlen, struct in_addr *dest, const char *label, uint32_t metric) { struct prefix_ipv4 *p; @@ -304,59 +299,36 @@ void connected_add_ipv4(struct interface *ifp, int flags, struct in_addr *addr, : prefixlen; ifc->address = (struct prefix *)p; - /* If there is broadcast or peer address. */ - if (broad) { - p = prefix_ipv4_new(); - p->family = AF_INET; - p->prefix = *broad; - p->prefixlen = prefixlen; - ifc->destination = (struct prefix *)p; - + /* If there is a peer address. */ + if (CONNECTED_PEER(ifc)) { /* validate the destination address */ - if (CONNECTED_PEER(ifc)) { - if (IPV4_ADDR_SAME(addr, broad)) + if (dest) { + p = prefix_ipv4_new(); + p->family = AF_INET; + p->prefix = *dest; + p->prefixlen = prefixlen; + ifc->destination = (struct prefix *)p; + + if (IPV4_ADDR_SAME(addr, dest)) flog_warn( EC_ZEBRA_IFACE_SAME_LOCAL_AS_PEER, - "warning: interface %s has same local and peer " - "address %s, routing protocols may malfunction", + "warning: interface %s has same local and peer address %s, routing protocols may malfunction", ifp->name, inet_ntoa(*addr)); } else { - if (broad->s_addr - != ipv4_broadcast_addr(addr->s_addr, prefixlen)) { - char buf[2][INET_ADDRSTRLEN]; - struct in_addr bcalc; - bcalc.s_addr = ipv4_broadcast_addr(addr->s_addr, - prefixlen); - flog_warn( - EC_ZEBRA_BCAST_ADDR_MISMATCH, - "warning: interface %s broadcast addr %s/%d != " - "calculated %s, routing protocols may malfunction", - ifp->name, - inet_ntop(AF_INET, broad, buf[0], - sizeof(buf[0])), - prefixlen, - inet_ntop(AF_INET, &bcalc, buf[1], - sizeof(buf[1]))); - } - } - - } else { - if (CHECK_FLAG(ifc->flags, ZEBRA_IFA_PEER)) { zlog_debug( - "warning: %s called for interface %s " - "with peer flag set, but no peer address supplied", + "warning: %s called for interface %s with peer flag set, but no peer address supplied", __func__, ifp->name); UNSET_FLAG(ifc->flags, ZEBRA_IFA_PEER); } - - /* no broadcast or destination address was supplied */ - if ((prefixlen == IPV4_MAX_PREFIXLEN) && if_is_pointopoint(ifp)) - zlog_debug( - "warning: PtP interface %s with addr %s/%d needs a " - "peer address", - ifp->name, inet_ntoa(*addr), prefixlen); } + /* no destination address was supplied */ + if (!dest && (prefixlen == IPV4_MAX_PREFIXLEN) + && if_is_pointopoint(ifp)) + zlog_debug( + "warning: PtP interface %s with addr %s/%d needs a peer address", + ifp->name, inet_ntoa(*addr), prefixlen); + /* Label of this address. */ if (label) ifc->label = XSTRDUP(MTYPE_CONNECTED_LABEL, label); @@ -377,6 +349,16 @@ void connected_down(struct interface *ifp, struct connected *ifc) .ifindex = ifp->ifindex, .vrf_id = ifp->vrf_id, }; + struct zebra_vrf *zvrf; + + zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id); + if (!zvrf) { + flog_err( + EC_ZEBRA_VRF_NOT_FOUND, + "%s: Received Up for interface but no associated zvrf: %d", + __func__, ifp->vrf_id); + return; + } if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_REAL)) return; @@ -402,7 +384,7 @@ void connected_down(struct interface *ifp, struct connected *ifc) return; break; default: - zlog_info("Unknown AFI: %s", afi2str(afi)); + zlog_warn("Unknown AFI: %s", afi2str(afi)); break; } @@ -410,34 +392,23 @@ void connected_down(struct interface *ifp, struct connected *ifc) * Same logic as for connected_up(): push the changes into the * head. */ - rib_delete(afi, SAFI_UNICAST, ifp->vrf_id, ZEBRA_ROUTE_CONNECT, 0, 0, - &p, NULL, &nh, 0, 0, 0, false); - - rib_delete(afi, SAFI_MULTICAST, ifp->vrf_id, ZEBRA_ROUTE_CONNECT, 0, 0, - &p, NULL, &nh, 0, 0, 0, false); + rib_delete(afi, SAFI_UNICAST, zvrf->vrf->vrf_id, ZEBRA_ROUTE_CONNECT, 0, + 0, &p, NULL, &nh, 0, zvrf->table_id, 0, 0, false, true); - if (IS_ZEBRA_DEBUG_RIB_DETAILED) { - char buf[PREFIX_STRLEN]; - - zlog_debug( - "%u: IF %s IP %s address down, scheduling RIB processing", - ifp->vrf_id, ifp->name, - prefix2str(&p, buf, sizeof(buf))); - } - - rib_update(ifp->vrf_id, RIB_UPDATE_IF_CHANGE); + rib_delete(afi, SAFI_MULTICAST, zvrf->vrf->vrf_id, ZEBRA_ROUTE_CONNECT, + 0, 0, &p, NULL, &nh, 0, zvrf->table_id, 0, 0, false, true); /* Schedule LSP forwarding entries for processing, if appropriate. */ - if (ifp->vrf_id == VRF_DEFAULT) { + if (zvrf->vrf->vrf_id == VRF_DEFAULT) { if (IS_ZEBRA_DEBUG_MPLS) { char buf[PREFIX_STRLEN]; zlog_debug( "%u: IF %s IP %s address down, scheduling MPLS processing", - ifp->vrf_id, ifp->name, + zvrf->vrf->vrf_id, ifp->name, prefix2str(&p, buf, sizeof(buf))); } - mpls_mark_lsps_for_processing(vrf_info_lookup(ifp->vrf_id), &p); + mpls_mark_lsps_for_processing(zvrf, &p); } } @@ -451,16 +422,6 @@ static void connected_delete_helper(struct connected *ifc, struct prefix *p) connected_withdraw(ifc); - if (IS_ZEBRA_DEBUG_RIB_DETAILED) { - char buf[PREFIX_STRLEN]; - - zlog_debug( - "%u: IF %s IP %s address del, scheduling RIB processing", - ifp->vrf_id, ifp->name, - prefix2str(p, buf, sizeof(buf))); - } - rib_update(ifp->vrf_id, RIB_UPDATE_IF_CHANGE); - /* Schedule LSP forwarding entries for processing, if appropriate. */ if (ifp->vrf_id == VRF_DEFAULT) { if (IS_ZEBRA_DEBUG_MPLS) { @@ -478,7 +439,7 @@ static void connected_delete_helper(struct connected *ifc, struct prefix *p) /* Delete connected IPv4 route to the interface. */ void connected_delete_ipv4(struct interface *ifp, int flags, struct in_addr *addr, uint16_t prefixlen, - struct in_addr *broad) + struct in_addr *dest) { struct prefix p, d; struct connected *ifc; @@ -489,10 +450,10 @@ void connected_delete_ipv4(struct interface *ifp, int flags, p.prefixlen = CHECK_FLAG(flags, ZEBRA_IFA_PEER) ? IPV4_MAX_PREFIXLEN : prefixlen; - if (broad) { + if (dest) { memset(&d, 0, sizeof(struct prefix)); d.family = AF_INET; - d.u.prefix4 = *broad; + d.u.prefix4 = *dest; d.prefixlen = prefixlen; ifc = connected_check_ptp(ifp, &p, &d); } else @@ -503,7 +464,7 @@ void connected_delete_ipv4(struct interface *ifp, int flags, /* Add connected IPv6 route to the interface. */ void connected_add_ipv6(struct interface *ifp, int flags, struct in6_addr *addr, - struct in6_addr *broad, uint16_t prefixlen, + struct in6_addr *dest, uint16_t prefixlen, const char *label, uint32_t metric) { struct prefix_ipv6 *p; @@ -528,10 +489,14 @@ void connected_add_ipv6(struct interface *ifp, int flags, struct in6_addr *addr, p->prefixlen = prefixlen; ifc->address = (struct prefix *)p; - if (broad) { + /* Add global ipv6 address to the RA prefix list */ + if (!IN6_IS_ADDR_LINKLOCAL(&p->prefix)) + rtadv_add_prefix(ifp->info, p); + + if (dest) { p = prefix_ipv6_new(); p->family = AF_INET6; - IPV6_ADDR_COPY(&p->prefix, broad); + IPV6_ADDR_COPY(&p->prefix, dest); p->prefixlen = prefixlen; ifc->destination = (struct prefix *)p; } else { @@ -561,7 +526,7 @@ void connected_add_ipv6(struct interface *ifp, int flags, struct in6_addr *addr, } void connected_delete_ipv6(struct interface *ifp, struct in6_addr *address, - struct in6_addr *broad, uint16_t prefixlen) + struct in6_addr *dest, uint16_t prefixlen) { struct prefix p, d; struct connected *ifc; @@ -571,10 +536,14 @@ void connected_delete_ipv6(struct interface *ifp, struct in6_addr *address, memcpy(&p.u.prefix6, address, sizeof(struct in6_addr)); p.prefixlen = prefixlen; - if (broad) { + /* Delete global ipv6 address from RA prefix list */ + if (!IN6_IS_ADDR_LINKLOCAL(&p.u.prefix6)) + rtadv_delete_prefix(ifp->info, &p); + + if (dest) { memset(&d, 0, sizeof(struct prefix)); d.family = AF_INET6; - IPV6_ADDR_COPY(&d.u.prefix6, broad); + IPV6_ADDR_COPY(&d.u.prefix6, dest); d.prefixlen = prefixlen; ifc = connected_check_ptp(ifp, &p, &d); } else @@ -594,5 +563,5 @@ int connected_is_unnumbered(struct interface *ifp) return CHECK_FLAG(connected->flags, ZEBRA_IFA_UNNUMBERED); } - return 1; + return 0; } diff --git a/zebra/connected.h b/zebra/connected.h index 7672bec006..14f6cb2db0 100644 --- a/zebra/connected.h +++ b/zebra/connected.h @@ -40,12 +40,12 @@ extern struct connected *connected_check_ptp(struct interface *ifp, extern void connected_add_ipv4(struct interface *ifp, int flags, struct in_addr *addr, uint16_t prefixlen, - struct in_addr *broad, const char *label, + struct in_addr *dest, const char *label, uint32_t metric); extern void connected_delete_ipv4(struct interface *ifp, int flags, struct in_addr *addr, uint16_t prefixlen, - struct in_addr *broad); + struct in_addr *dest); extern void connected_delete_ipv4_unnumbered(struct connected *ifc); @@ -53,12 +53,12 @@ extern void connected_up(struct interface *ifp, struct connected *ifc); extern void connected_down(struct interface *ifp, struct connected *ifc); extern void connected_add_ipv6(struct interface *ifp, int flags, - struct in6_addr *address, struct in6_addr *broad, + struct in6_addr *address, struct in6_addr *dest, uint16_t prefixlen, const char *label, uint32_t metric); extern void connected_delete_ipv6(struct interface *ifp, struct in6_addr *address, - struct in6_addr *broad, uint16_t prefixlen); + struct in6_addr *dest, uint16_t prefixlen); extern int connected_is_unnumbered(struct interface *); diff --git a/zebra/debug.c b/zebra/debug.c index 8e5fb0ea10..87a10ea65d 100644 --- a/zebra/debug.c +++ b/zebra/debug.c @@ -39,6 +39,8 @@ unsigned long zebra_debug_vxlan; unsigned long zebra_debug_pw; unsigned long zebra_debug_dplane; unsigned long zebra_debug_mlag; +unsigned long zebra_debug_nexthop; +unsigned long zebra_debug_evpn_mh; DEFINE_HOOK(zebra_debug_show_debugging, (struct vty *vty), (vty)); @@ -103,6 +105,22 @@ DEFUN_NOSH (show_debugging_zebra, vty_out(vty, " Zebra dataplane debugging is on\n"); if (IS_ZEBRA_DEBUG_MLAG) vty_out(vty, " Zebra mlag debugging is on\n"); + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + vty_out(vty, " Zebra detailed nexthop debugging is on\n"); + else if (IS_ZEBRA_DEBUG_NHG) + vty_out(vty, " Zebra nexthop debugging is on\n"); + + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + vty_out(vty, " Zebra EVPN-MH ethernet segment debugging is on\n"); + + if (IS_ZEBRA_DEBUG_EVPN_MH_NH) + vty_out(vty, " Zebra EVPN-MH nexthop debugging is on\n"); + + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) + vty_out(vty, " Zebra EVPN-MH MAC debugging is on\n"); + + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + vty_out(vty, " Zebra EVPN-MH Neigh debugging is on\n"); hook_call(zebra_debug_show_debugging, vty); return CMD_SUCCESS; @@ -315,6 +333,53 @@ DEFPY (debug_zebra_mlag, return CMD_SUCCESS; } +DEFPY (debug_zebra_evpn_mh, + debug_zebra_evpn_mh_cmd, + "[no$no] debug zebra evpn mh ", + NO_STR + DEBUG_STR + "Zebra configuration\n" + "EVPN\n" + "Multihoming\n" + "Ethernet Segment Debugging\n" + "MAC Debugging\n" + "Neigh Debugging\n" + "Nexthop Debugging\n") +{ + if (es) { + if (no) + UNSET_FLAG(zebra_debug_evpn_mh, ZEBRA_DEBUG_EVPN_MH_ES); + else + SET_FLAG(zebra_debug_evpn_mh, ZEBRA_DEBUG_EVPN_MH_ES); + } + + if (mac) { + if (no) + UNSET_FLAG(zebra_debug_evpn_mh, + ZEBRA_DEBUG_EVPN_MH_MAC); + else + SET_FLAG(zebra_debug_evpn_mh, ZEBRA_DEBUG_EVPN_MH_MAC); + } + + if (neigh) { + if (no) + UNSET_FLAG(zebra_debug_evpn_mh, + ZEBRA_DEBUG_EVPN_MH_NEIGH); + else + SET_FLAG(zebra_debug_evpn_mh, + ZEBRA_DEBUG_EVPN_MH_NEIGH); + } + + if (nh) { + if (no) + UNSET_FLAG(zebra_debug_evpn_mh, ZEBRA_DEBUG_EVPN_MH_NH); + else + SET_FLAG(zebra_debug_evpn_mh, ZEBRA_DEBUG_EVPN_MH_NH); + } + + return CMD_SUCCESS; +} + DEFUN (no_debug_zebra_events, no_debug_zebra_events_cmd, "no debug zebra events", @@ -443,9 +508,36 @@ DEFUN (no_debug_zebra_dplane, return CMD_SUCCESS; } +DEFPY (debug_zebra_nexthop, + debug_zebra_nexthop_cmd, + "[no$no] debug zebra nexthop [detail$detail]", + NO_STR + DEBUG_STR + "Zebra configuration\n" + "Debug zebra nexthop events\n" + "Detailed information\n") +{ + if (no) + zebra_debug_nexthop = 0; + else { + SET_FLAG(zebra_debug_nexthop, ZEBRA_DEBUG_NHG); + + if (detail) + SET_FLAG(zebra_debug_nexthop, + ZEBRA_DEBUG_NHG_DETAILED); + } + + return CMD_SUCCESS; +} + /* Debug node. */ -struct cmd_node debug_node = {DEBUG_NODE, "", /* Debug node has no interface. */ - 1}; +static int config_write_debug(struct vty *vty); +struct cmd_node debug_node = { + .name = "debug", + .node = DEBUG_NODE, + .prompt = "", + .config_write = config_write_debug, +}; static int config_write_debug(struct vty *vty) { @@ -517,6 +609,26 @@ static int config_write_debug(struct vty *vty) vty_out(vty, "debug zebra vxlan\n"); write++; } + if (IS_ZEBRA_DEBUG_MLAG) { + vty_out(vty, "debug zebra mlag\n"); + write++; + } + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) { + vty_out(vty, "debug zebra evpn mh es\n"); + write++; + } + if (IS_ZEBRA_DEBUG_EVPN_MH_NH) { + vty_out(vty, "debug zebra evpn mh nh\n"); + write++; + } + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) { + vty_out(vty, "debug zebra evpn mh mac\n"); + write++; + } + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) { + vty_out(vty, "debug zebra evpn mh neigh\n"); + write++; + } if (IS_ZEBRA_DEBUG_PW) { vty_out(vty, "debug zebra pseudowires\n"); write++; @@ -530,6 +642,14 @@ static int config_write_debug(struct vty *vty) write++; } + if (CHECK_FLAG(zebra_debug_nexthop, ZEBRA_DEBUG_NHG_DETAILED)) { + vty_out(vty, "debug zebra nexthop detail\n"); + write++; + } else if (CHECK_FLAG(zebra_debug_nexthop, ZEBRA_DEBUG_NHG)) { + vty_out(vty, "debug zebra nexthop\n"); + write++; + } + return write; } @@ -545,11 +665,13 @@ void zebra_debug_init(void) zebra_debug_pw = 0; zebra_debug_dplane = 0; zebra_debug_mlag = 0; + zebra_debug_evpn_mh = 0; zebra_debug_nht = 0; + zebra_debug_nexthop = 0; - install_node(&debug_node, config_write_debug); + install_node(&debug_node); - install_element(VIEW_NODE, &show_debugging_zebra_cmd); + install_element(ENABLE_NODE, &show_debugging_zebra_cmd); install_element(ENABLE_NODE, &debug_zebra_events_cmd); install_element(ENABLE_NODE, &debug_zebra_nht_cmd); @@ -563,6 +685,7 @@ void zebra_debug_init(void) install_element(ENABLE_NODE, &debug_zebra_fpm_cmd); install_element(ENABLE_NODE, &debug_zebra_dplane_cmd); install_element(ENABLE_NODE, &debug_zebra_mlag_cmd); + install_element(ENABLE_NODE, &debug_zebra_nexthop_cmd); install_element(ENABLE_NODE, &no_debug_zebra_events_cmd); install_element(ENABLE_NODE, &no_debug_zebra_nht_cmd); install_element(ENABLE_NODE, &no_debug_zebra_mpls_cmd); @@ -573,6 +696,7 @@ void zebra_debug_init(void) install_element(ENABLE_NODE, &no_debug_zebra_rib_cmd); install_element(ENABLE_NODE, &no_debug_zebra_fpm_cmd); install_element(ENABLE_NODE, &no_debug_zebra_dplane_cmd); + install_element(ENABLE_NODE, &debug_zebra_evpn_mh_cmd); install_element(CONFIG_NODE, &debug_zebra_events_cmd); install_element(CONFIG_NODE, &debug_zebra_nht_cmd); @@ -585,6 +709,7 @@ void zebra_debug_init(void) install_element(CONFIG_NODE, &debug_zebra_rib_cmd); install_element(CONFIG_NODE, &debug_zebra_fpm_cmd); install_element(CONFIG_NODE, &debug_zebra_dplane_cmd); + install_element(CONFIG_NODE, &debug_zebra_nexthop_cmd); install_element(CONFIG_NODE, &no_debug_zebra_events_cmd); install_element(CONFIG_NODE, &no_debug_zebra_nht_cmd); install_element(CONFIG_NODE, &no_debug_zebra_mpls_cmd); @@ -595,4 +720,6 @@ void zebra_debug_init(void) install_element(CONFIG_NODE, &no_debug_zebra_rib_cmd); install_element(CONFIG_NODE, &no_debug_zebra_fpm_cmd); install_element(CONFIG_NODE, &no_debug_zebra_dplane_cmd); + install_element(CONFIG_NODE, &debug_zebra_mlag_cmd); + install_element(CONFIG_NODE, &debug_zebra_evpn_mh_cmd); } diff --git a/zebra/debug.h b/zebra/debug.h index 176226f7ae..8402224f19 100644 --- a/zebra/debug.h +++ b/zebra/debug.h @@ -59,6 +59,14 @@ extern "C" { #define ZEBRA_DEBUG_MLAG 0x01 +#define ZEBRA_DEBUG_NHG 0x01 +#define ZEBRA_DEBUG_NHG_DETAILED 0x02 + +#define ZEBRA_DEBUG_EVPN_MH_ES 0x01 +#define ZEBRA_DEBUG_EVPN_MH_NH 0x02 +#define ZEBRA_DEBUG_EVPN_MH_MAC 0x04 +#define ZEBRA_DEBUG_EVPN_MH_NEIGH 0x08 + /* Debug related macro. */ #define IS_ZEBRA_DEBUG_EVENT (zebra_debug_event & ZEBRA_DEBUG_EVENT) @@ -92,6 +100,20 @@ extern "C" { #define IS_ZEBRA_DEBUG_MLAG (zebra_debug_mlag & ZEBRA_DEBUG_MLAG) +#define IS_ZEBRA_DEBUG_NHG (zebra_debug_nexthop & ZEBRA_DEBUG_NHG) + +#define IS_ZEBRA_DEBUG_NHG_DETAIL \ + (zebra_debug_nexthop & ZEBRA_DEBUG_NHG_DETAILED) + +#define IS_ZEBRA_DEBUG_EVPN_MH_ES \ + (zebra_debug_evpn_mh & ZEBRA_DEBUG_EVPN_MH_ES) +#define IS_ZEBRA_DEBUG_EVPN_MH_NH \ + (zebra_debug_evpn_mh & ZEBRA_DEBUG_EVPN_MH_NH) +#define IS_ZEBRA_DEBUG_EVPN_MH_MAC \ + (zebra_debug_evpn_mh & ZEBRA_DEBUG_EVPN_MH_MAC) +#define IS_ZEBRA_DEBUG_EVPN_MH_NEIGH \ + (zebra_debug_evpn_mh & ZEBRA_DEBUG_EVPN_MH_NEIGH) + extern unsigned long zebra_debug_event; extern unsigned long zebra_debug_packet; extern unsigned long zebra_debug_kernel; @@ -103,6 +125,8 @@ extern unsigned long zebra_debug_vxlan; extern unsigned long zebra_debug_pw; extern unsigned long zebra_debug_dplane; extern unsigned long zebra_debug_mlag; +extern unsigned long zebra_debug_nexthop; +extern unsigned long zebra_debug_evpn_mh; extern void zebra_debug_init(void); diff --git a/zebra/dplane_fpm_nl.c b/zebra/dplane_fpm_nl.c new file mode 100644 index 0000000000..a816f10b8d --- /dev/null +++ b/zebra/dplane_fpm_nl.c @@ -0,0 +1,1421 @@ +/* + * Zebra dataplane plugin for Forwarding Plane Manager (FPM) using netlink. + * + * Copyright (C) 2019 Network Device Education Foundation, Inc. ("NetDEF") + * Rafael Zalamena + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" /* Include this explicitly */ +#endif + +#include + +#include +#include + +#include +#include + +#include "lib/zebra.h" +#include "lib/json.h" +#include "lib/libfrr.h" +#include "lib/frratomic.h" +#include "lib/command.h" +#include "lib/memory.h" +#include "lib/network.h" +#include "lib/ns.h" +#include "lib/frr_pthread.h" +#include "zebra/debug.h" +#include "zebra/interface.h" +#include "zebra/zebra_dplane.h" +#include "zebra/zebra_router.h" +#include "zebra/zebra_evpn.h" +#include "zebra/zebra_evpn_mac.h" +#include "zebra/zebra_vxlan_private.h" +#include "zebra/kernel_netlink.h" +#include "zebra/rt_netlink.h" +#include "zebra/debug.h" + +#define SOUTHBOUND_DEFAULT_ADDR INADDR_LOOPBACK +#define SOUTHBOUND_DEFAULT_PORT 2620 + +/** + * FPM header: + * { + * version: 1 byte (always 1), + * type: 1 byte (1 for netlink, 2 protobuf), + * len: 2 bytes (network order), + * } + * + * This header is used with any format to tell the users how many bytes to + * expect. + */ +#define FPM_HEADER_SIZE 4 + +static const char *prov_name = "dplane_fpm_nl"; + +struct fpm_nl_ctx { + /* data plane connection. */ + int socket; + bool disabled; + bool connecting; + bool nhg_complete; + bool rib_complete; + bool rmac_complete; + bool use_nhg; + struct sockaddr_storage addr; + + /* data plane buffers. */ + struct stream *ibuf; + struct stream *obuf; + pthread_mutex_t obuf_mutex; + + /* + * data plane context queue: + * When a FPM server connection becomes a bottleneck, we must keep the + * data plane contexts until we get a chance to process them. + */ + struct dplane_ctx_q ctxqueue; + pthread_mutex_t ctxqueue_mutex; + + /* data plane events. */ + struct zebra_dplane_provider *prov; + struct frr_pthread *fthread; + struct thread *t_connect; + struct thread *t_read; + struct thread *t_write; + struct thread *t_event; + struct thread *t_dequeue; + + /* zebra events. */ + struct thread *t_nhgreset; + struct thread *t_nhgwalk; + struct thread *t_ribreset; + struct thread *t_ribwalk; + struct thread *t_rmacreset; + struct thread *t_rmacwalk; + + /* Statistic counters. */ + struct { + /* Amount of bytes read into ibuf. */ + _Atomic uint32_t bytes_read; + /* Amount of bytes written from obuf. */ + _Atomic uint32_t bytes_sent; + /* Output buffer current usage. */ + _Atomic uint32_t obuf_bytes; + /* Output buffer peak usage. */ + _Atomic uint32_t obuf_peak; + + /* Amount of connection closes. */ + _Atomic uint32_t connection_closes; + /* Amount of connection errors. */ + _Atomic uint32_t connection_errors; + + /* Amount of user configurations: FNE_RECONNECT. */ + _Atomic uint32_t user_configures; + /* Amount of user disable requests: FNE_DISABLE. */ + _Atomic uint32_t user_disables; + + /* Amount of data plane context processed. */ + _Atomic uint32_t dplane_contexts; + /* Amount of data plane contexts enqueued. */ + _Atomic uint32_t ctxqueue_len; + /* Peak amount of data plane contexts enqueued. */ + _Atomic uint32_t ctxqueue_len_peak; + + /* Amount of buffer full events. */ + _Atomic uint32_t buffer_full; + } counters; +} *gfnc; + +enum fpm_nl_events { + /* Ask for FPM to reconnect the external server. */ + FNE_RECONNECT, + /* Disable FPM. */ + FNE_DISABLE, + /* Reset counters. */ + FNE_RESET_COUNTERS, + /* Toggle next hop group feature. */ + FNE_TOGGLE_NHG, + /* Reconnect request by our own code to avoid races. */ + FNE_INTERNAL_RECONNECT, + + /* Next hop groups walk finished. */ + FNE_NHG_FINISHED, + /* RIB walk finished. */ + FNE_RIB_FINISHED, + /* RMAC walk finished. */ + FNE_RMAC_FINISHED, +}; + +#define FPM_RECONNECT(fnc) \ + thread_add_event((fnc)->fthread->master, fpm_process_event, (fnc), \ + FNE_INTERNAL_RECONNECT, &(fnc)->t_event) + +#define WALK_FINISH(fnc, ev) \ + thread_add_event((fnc)->fthread->master, fpm_process_event, (fnc), \ + (ev), NULL) + +/* + * Prototypes. + */ +static int fpm_process_event(struct thread *t); +static int fpm_nl_enqueue(struct fpm_nl_ctx *fnc, struct zebra_dplane_ctx *ctx); +static int fpm_nhg_send(struct thread *t); +static int fpm_nhg_reset(struct thread *t); +static int fpm_rib_send(struct thread *t); +static int fpm_rib_reset(struct thread *t); +static int fpm_rmac_send(struct thread *t); +static int fpm_rmac_reset(struct thread *t); + +/* + * Helper functions. + */ + +/** + * Reorganizes the data on the buffer so it can fit more data. + * + * @param s stream pointer. + */ +static void stream_pulldown(struct stream *s) +{ + size_t rlen = STREAM_READABLE(s); + + /* No more data, so just move the pointers. */ + if (rlen == 0) { + stream_reset(s); + return; + } + + /* Move the available data to the beginning. */ + memmove(s->data, &s->data[s->getp], rlen); + s->getp = 0; + s->endp = rlen; +} + +/* + * CLI. + */ +#define FPM_STR "Forwarding Plane Manager configuration\n" + +DEFUN(fpm_set_address, fpm_set_address_cmd, + "fpm address [port (1-65535)]", + FPM_STR + "FPM remote listening server address\n" + "Remote IPv4 FPM server\n" + "Remote IPv6 FPM server\n" + "FPM remote listening server port\n" + "Remote FPM server port\n") +{ + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + uint16_t port = 0; + uint8_t naddr[INET6_BUFSIZ]; + + if (argc == 5) + port = strtol(argv[4]->arg, NULL, 10); + + /* Handle IPv4 addresses. */ + if (inet_pton(AF_INET, argv[2]->arg, naddr) == 1) { + sin = (struct sockaddr_in *)&gfnc->addr; + + memset(sin, 0, sizeof(*sin)); + sin->sin_family = AF_INET; + sin->sin_port = + port ? htons(port) : htons(SOUTHBOUND_DEFAULT_PORT); +#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN + sin->sin_len = sizeof(*sin); +#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */ + memcpy(&sin->sin_addr, naddr, sizeof(sin->sin_addr)); + + goto ask_reconnect; + } + + /* Handle IPv6 addresses. */ + if (inet_pton(AF_INET6, argv[2]->arg, naddr) != 1) { + vty_out(vty, "%% Invalid address: %s\n", argv[2]->arg); + return CMD_WARNING; + } + + sin6 = (struct sockaddr_in6 *)&gfnc->addr; + memset(sin6, 0, sizeof(*sin6)); + sin6->sin6_family = AF_INET6; + sin6->sin6_port = port ? htons(port) : htons(SOUTHBOUND_DEFAULT_PORT); +#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN + sin6->sin6_len = sizeof(*sin6); +#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */ + memcpy(&sin6->sin6_addr, naddr, sizeof(sin6->sin6_addr)); + +ask_reconnect: + thread_add_event(gfnc->fthread->master, fpm_process_event, gfnc, + FNE_RECONNECT, &gfnc->t_event); + return CMD_SUCCESS; +} + +DEFUN(no_fpm_set_address, no_fpm_set_address_cmd, + "no fpm address [ [port <1-65535>]]", + NO_STR + FPM_STR + "FPM remote listening server address\n" + "Remote IPv4 FPM server\n" + "Remote IPv6 FPM server\n" + "FPM remote listening server port\n" + "Remote FPM server port\n") +{ + thread_add_event(gfnc->fthread->master, fpm_process_event, gfnc, + FNE_DISABLE, &gfnc->t_event); + return CMD_SUCCESS; +} + +DEFUN(fpm_use_nhg, fpm_use_nhg_cmd, + "fpm use-next-hop-groups", + FPM_STR + "Use netlink next hop groups feature.\n") +{ + /* Already enabled. */ + if (gfnc->use_nhg) + return CMD_SUCCESS; + + thread_add_event(gfnc->fthread->master, fpm_process_event, gfnc, + FNE_TOGGLE_NHG, &gfnc->t_event); + + return CMD_SUCCESS; +} + +DEFUN(no_fpm_use_nhg, no_fpm_use_nhg_cmd, + "no fpm use-next-hop-groups", + NO_STR + FPM_STR + "Use netlink next hop groups feature.\n") +{ + /* Already disabled. */ + if (!gfnc->use_nhg) + return CMD_SUCCESS; + + thread_add_event(gfnc->fthread->master, fpm_process_event, gfnc, + FNE_TOGGLE_NHG, &gfnc->t_event); + + return CMD_SUCCESS; +} + +DEFUN(fpm_reset_counters, fpm_reset_counters_cmd, + "clear fpm counters", + CLEAR_STR + FPM_STR + "FPM statistic counters\n") +{ + thread_add_event(gfnc->fthread->master, fpm_process_event, gfnc, + FNE_RESET_COUNTERS, &gfnc->t_event); + return CMD_SUCCESS; +} + +DEFUN(fpm_show_counters, fpm_show_counters_cmd, + "show fpm counters", + SHOW_STR + FPM_STR + "FPM statistic counters\n") +{ + vty_out(vty, "%30s\n%30s\n", "FPM counters", "============"); + +#define SHOW_COUNTER(label, counter) \ + vty_out(vty, "%28s: %u\n", (label), (counter)) + + SHOW_COUNTER("Input bytes", gfnc->counters.bytes_read); + SHOW_COUNTER("Output bytes", gfnc->counters.bytes_sent); + SHOW_COUNTER("Output buffer current size", gfnc->counters.obuf_bytes); + SHOW_COUNTER("Output buffer peak size", gfnc->counters.obuf_peak); + SHOW_COUNTER("Connection closes", gfnc->counters.connection_closes); + SHOW_COUNTER("Connection errors", gfnc->counters.connection_errors); + SHOW_COUNTER("Data plane items processed", + gfnc->counters.dplane_contexts); + SHOW_COUNTER("Data plane items enqueued", + gfnc->counters.ctxqueue_len); + SHOW_COUNTER("Data plane items queue peak", + gfnc->counters.ctxqueue_len_peak); + SHOW_COUNTER("Buffer full hits", gfnc->counters.buffer_full); + SHOW_COUNTER("User FPM configurations", gfnc->counters.user_configures); + SHOW_COUNTER("User FPM disable requests", gfnc->counters.user_disables); + +#undef SHOW_COUNTER + + return CMD_SUCCESS; +} + +DEFUN(fpm_show_counters_json, fpm_show_counters_json_cmd, + "show fpm counters json", + SHOW_STR + FPM_STR + "FPM statistic counters\n" + JSON_STR) +{ + struct json_object *jo; + + jo = json_object_new_object(); + json_object_int_add(jo, "bytes-read", gfnc->counters.bytes_read); + json_object_int_add(jo, "bytes-sent", gfnc->counters.bytes_sent); + json_object_int_add(jo, "obuf-bytes", gfnc->counters.obuf_bytes); + json_object_int_add(jo, "obuf-bytes-peak", gfnc->counters.obuf_peak); + json_object_int_add(jo, "connection-closes", + gfnc->counters.connection_closes); + json_object_int_add(jo, "connection-errors", + gfnc->counters.connection_errors); + json_object_int_add(jo, "data-plane-contexts", + gfnc->counters.dplane_contexts); + json_object_int_add(jo, "data-plane-contexts-queue", + gfnc->counters.ctxqueue_len); + json_object_int_add(jo, "data-plane-contexts-queue-peak", + gfnc->counters.ctxqueue_len_peak); + json_object_int_add(jo, "buffer-full-hits", gfnc->counters.buffer_full); + json_object_int_add(jo, "user-configures", + gfnc->counters.user_configures); + json_object_int_add(jo, "user-disables", gfnc->counters.user_disables); + vty_out(vty, "%s\n", json_object_to_json_string_ext(jo, 0)); + json_object_free(jo); + + return CMD_SUCCESS; +} + +static int fpm_write_config(struct vty *vty) +{ + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + int written = 0; + char addrstr[INET6_ADDRSTRLEN]; + + if (gfnc->disabled) + return written; + + switch (gfnc->addr.ss_family) { + case AF_INET: + written = 1; + sin = (struct sockaddr_in *)&gfnc->addr; + inet_ntop(AF_INET, &sin->sin_addr, addrstr, sizeof(addrstr)); + vty_out(vty, "fpm address %s", addrstr); + if (sin->sin_port != htons(SOUTHBOUND_DEFAULT_PORT)) + vty_out(vty, " port %d", ntohs(sin->sin_port)); + + vty_out(vty, "\n"); + break; + case AF_INET6: + written = 1; + sin6 = (struct sockaddr_in6 *)&gfnc->addr; + inet_ntop(AF_INET, &sin6->sin6_addr, addrstr, sizeof(addrstr)); + vty_out(vty, "fpm address %s", addrstr); + if (sin6->sin6_port != htons(SOUTHBOUND_DEFAULT_PORT)) + vty_out(vty, " port %d", ntohs(sin6->sin6_port)); + + vty_out(vty, "\n"); + break; + + default: + break; + } + + if (!gfnc->use_nhg) { + vty_out(vty, "no fpm use-next-hop-groups\n"); + written = 1; + } + + return written; +} + +static struct cmd_node fpm_node = { + .name = "fpm", + .node = FPM_NODE, + .prompt = "", + .config_write = fpm_write_config, +}; + +/* + * FPM functions. + */ +static int fpm_connect(struct thread *t); + +static void fpm_reconnect(struct fpm_nl_ctx *fnc) +{ + /* Cancel all zebra threads first. */ + thread_cancel_async(zrouter.master, &fnc->t_nhgreset, NULL); + thread_cancel_async(zrouter.master, &fnc->t_nhgwalk, NULL); + thread_cancel_async(zrouter.master, &fnc->t_ribreset, NULL); + thread_cancel_async(zrouter.master, &fnc->t_ribwalk, NULL); + thread_cancel_async(zrouter.master, &fnc->t_rmacreset, NULL); + thread_cancel_async(zrouter.master, &fnc->t_rmacwalk, NULL); + + /* + * Grab the lock to empty the streams (data plane might try to + * enqueue updates while we are closing). + */ + frr_mutex_lock_autounlock(&fnc->obuf_mutex); + + /* Avoid calling close on `-1`. */ + if (fnc->socket != -1) { + close(fnc->socket); + fnc->socket = -1; + } + + stream_reset(fnc->ibuf); + stream_reset(fnc->obuf); + THREAD_OFF(fnc->t_read); + THREAD_OFF(fnc->t_write); + + /* FPM is disabled, don't attempt to connect. */ + if (fnc->disabled) + return; + + thread_add_timer(fnc->fthread->master, fpm_connect, fnc, 3, + &fnc->t_connect); +} + +static int fpm_read(struct thread *t) +{ + struct fpm_nl_ctx *fnc = THREAD_ARG(t); + ssize_t rv; + + /* Let's ignore the input at the moment. */ + rv = stream_read_try(fnc->ibuf, fnc->socket, + STREAM_WRITEABLE(fnc->ibuf)); + /* We've got an interruption. */ + if (rv == -2) { + /* Schedule next read. */ + thread_add_read(fnc->fthread->master, fpm_read, fnc, + fnc->socket, &fnc->t_read); + return 0; + } + if (rv == 0) { + atomic_fetch_add_explicit(&fnc->counters.connection_closes, 1, + memory_order_relaxed); + + if (IS_ZEBRA_DEBUG_FPM) + zlog_debug("%s: connection closed", __func__); + + FPM_RECONNECT(fnc); + return 0; + } + if (rv == -1) { + atomic_fetch_add_explicit(&fnc->counters.connection_errors, 1, + memory_order_relaxed); + zlog_warn("%s: connection failure: %s", __func__, + strerror(errno)); + FPM_RECONNECT(fnc); + return 0; + } + stream_reset(fnc->ibuf); + + /* Account all bytes read. */ + atomic_fetch_add_explicit(&fnc->counters.bytes_read, rv, + memory_order_relaxed); + + thread_add_read(fnc->fthread->master, fpm_read, fnc, fnc->socket, + &fnc->t_read); + + return 0; +} + +static int fpm_write(struct thread *t) +{ + struct fpm_nl_ctx *fnc = THREAD_ARG(t); + socklen_t statuslen; + ssize_t bwritten; + int rv, status; + size_t btotal; + + if (fnc->connecting == true) { + status = 0; + statuslen = sizeof(status); + + rv = getsockopt(fnc->socket, SOL_SOCKET, SO_ERROR, &status, + &statuslen); + if (rv == -1 || status != 0) { + if (rv != -1) + zlog_warn("%s: connection failed: %s", __func__, + strerror(status)); + else + zlog_warn("%s: SO_ERROR failed: %s", __func__, + strerror(status)); + + atomic_fetch_add_explicit( + &fnc->counters.connection_errors, 1, + memory_order_relaxed); + + FPM_RECONNECT(fnc); + return 0; + } + + fnc->connecting = false; + + /* Permit receiving messages now. */ + thread_add_read(fnc->fthread->master, fpm_read, fnc, + fnc->socket, &fnc->t_read); + } + + frr_mutex_lock_autounlock(&fnc->obuf_mutex); + + while (true) { + /* Stream is empty: reset pointers and return. */ + if (STREAM_READABLE(fnc->obuf) == 0) { + stream_reset(fnc->obuf); + break; + } + + /* Try to write all at once. */ + btotal = stream_get_endp(fnc->obuf) - + stream_get_getp(fnc->obuf); + bwritten = write(fnc->socket, stream_pnt(fnc->obuf), btotal); + if (bwritten == 0) { + atomic_fetch_add_explicit( + &fnc->counters.connection_closes, 1, + memory_order_relaxed); + + if (IS_ZEBRA_DEBUG_FPM) + zlog_debug("%s: connection closed", __func__); + break; + } + if (bwritten == -1) { + /* Attempt to continue if blocked by a signal. */ + if (errno == EINTR) + continue; + /* Receiver is probably slow, lets give it some time. */ + if (errno == EAGAIN || errno == EWOULDBLOCK) + break; + + atomic_fetch_add_explicit( + &fnc->counters.connection_errors, 1, + memory_order_relaxed); + zlog_warn("%s: connection failure: %s", __func__, + strerror(errno)); + + FPM_RECONNECT(fnc); + return 0; + } + + /* Account all bytes sent. */ + atomic_fetch_add_explicit(&fnc->counters.bytes_sent, bwritten, + memory_order_relaxed); + + /* Account number of bytes free. */ + atomic_fetch_sub_explicit(&fnc->counters.obuf_bytes, bwritten, + memory_order_relaxed); + + stream_forward_getp(fnc->obuf, (size_t)bwritten); + } + + /* Stream is not empty yet, we must schedule more writes. */ + if (STREAM_READABLE(fnc->obuf)) { + stream_pulldown(fnc->obuf); + thread_add_write(fnc->fthread->master, fpm_write, fnc, + fnc->socket, &fnc->t_write); + return 0; + } + + return 0; +} + +static int fpm_connect(struct thread *t) +{ + struct fpm_nl_ctx *fnc = THREAD_ARG(t); + struct sockaddr_in *sin = (struct sockaddr_in *)&fnc->addr; + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&fnc->addr; + socklen_t slen; + int rv, sock; + char addrstr[INET6_ADDRSTRLEN]; + + sock = socket(fnc->addr.ss_family, SOCK_STREAM, 0); + if (sock == -1) { + zlog_err("%s: fpm socket failed: %s", __func__, + strerror(errno)); + thread_add_timer(fnc->fthread->master, fpm_connect, fnc, 3, + &fnc->t_connect); + return 0; + } + + set_nonblocking(sock); + + if (fnc->addr.ss_family == AF_INET) { + inet_ntop(AF_INET, &sin->sin_addr, addrstr, sizeof(addrstr)); + slen = sizeof(*sin); + } else { + inet_ntop(AF_INET6, &sin6->sin6_addr, addrstr, sizeof(addrstr)); + slen = sizeof(*sin6); + } + + if (IS_ZEBRA_DEBUG_FPM) + zlog_debug("%s: attempting to connect to %s:%d", __func__, + addrstr, ntohs(sin->sin_port)); + + rv = connect(sock, (struct sockaddr *)&fnc->addr, slen); + if (rv == -1 && errno != EINPROGRESS) { + atomic_fetch_add_explicit(&fnc->counters.connection_errors, 1, + memory_order_relaxed); + close(sock); + zlog_warn("%s: fpm connection failed: %s", __func__, + strerror(errno)); + thread_add_timer(fnc->fthread->master, fpm_connect, fnc, 3, + &fnc->t_connect); + return 0; + } + + fnc->connecting = (errno == EINPROGRESS); + fnc->socket = sock; + if (!fnc->connecting) + thread_add_read(fnc->fthread->master, fpm_read, fnc, sock, + &fnc->t_read); + thread_add_write(fnc->fthread->master, fpm_write, fnc, sock, + &fnc->t_write); + + /* Mark all routes as unsent. */ + if (fnc->use_nhg) + thread_add_timer(zrouter.master, fpm_nhg_reset, fnc, 0, + &fnc->t_nhgreset); + else + thread_add_timer(zrouter.master, fpm_rib_reset, fnc, 0, + &fnc->t_ribreset); + + return 0; +} + +/** + * Encode data plane operation context into netlink and enqueue it in the FPM + * output buffer. + * + * @param fnc the netlink FPM context. + * @param ctx the data plane operation context data. + * @return 0 on success or -1 on not enough space. + */ +static int fpm_nl_enqueue(struct fpm_nl_ctx *fnc, struct zebra_dplane_ctx *ctx) +{ + uint8_t nl_buf[NL_PKT_BUF_SIZE]; + size_t nl_buf_len; + ssize_t rv; + uint64_t obytes, obytes_peak; + enum dplane_op_e op = dplane_ctx_get_op(ctx); + + /* + * If we were configured to not use next hop groups, then quit as soon + * as possible. + */ + if ((!fnc->use_nhg) + && (op == DPLANE_OP_NH_DELETE || op == DPLANE_OP_NH_INSTALL + || op == DPLANE_OP_NH_UPDATE)) + return 0; + + nl_buf_len = 0; + + frr_mutex_lock_autounlock(&fnc->obuf_mutex); + + switch (op) { + case DPLANE_OP_ROUTE_UPDATE: + case DPLANE_OP_ROUTE_DELETE: + rv = netlink_route_multipath_msg_encode(RTM_DELROUTE, ctx, + nl_buf, sizeof(nl_buf), + true, fnc->use_nhg); + if (rv <= 0) { + zlog_err( + "%s: netlink_route_multipath_msg_encode failed", + __func__); + return 0; + } + + nl_buf_len = (size_t)rv; + + /* UPDATE operations need a INSTALL, otherwise just quit. */ + if (op == DPLANE_OP_ROUTE_DELETE) + break; + + /* FALL THROUGH */ + case DPLANE_OP_ROUTE_INSTALL: + rv = netlink_route_multipath_msg_encode( + RTM_NEWROUTE, ctx, &nl_buf[nl_buf_len], + sizeof(nl_buf) - nl_buf_len, true, fnc->use_nhg); + if (rv <= 0) { + zlog_err( + "%s: netlink_route_multipath_msg_encode failed", + __func__); + return 0; + } + + nl_buf_len += (size_t)rv; + break; + + case DPLANE_OP_MAC_INSTALL: + case DPLANE_OP_MAC_DELETE: + rv = netlink_macfdb_update_ctx(ctx, nl_buf, sizeof(nl_buf)); + if (rv <= 0) { + zlog_err("%s: netlink_macfdb_update_ctx failed", + __func__); + return 0; + } + + nl_buf_len = (size_t)rv; + break; + + case DPLANE_OP_NH_DELETE: + rv = netlink_nexthop_msg_encode(RTM_DELNEXTHOP, ctx, nl_buf, + sizeof(nl_buf)); + if (rv <= 0) { + zlog_err("%s: netlink_nexthop_msg_encode failed", + __func__); + return 0; + } + + nl_buf_len = (size_t)rv; + break; + case DPLANE_OP_NH_INSTALL: + case DPLANE_OP_NH_UPDATE: + rv = netlink_nexthop_msg_encode(RTM_NEWNEXTHOP, ctx, nl_buf, + sizeof(nl_buf)); + if (rv <= 0) { + zlog_err("%s: netlink_nexthop_msg_encode failed", + __func__); + return 0; + } + + nl_buf_len = (size_t)rv; + break; + + case DPLANE_OP_LSP_INSTALL: + case DPLANE_OP_LSP_UPDATE: + case DPLANE_OP_LSP_DELETE: + case DPLANE_OP_PW_INSTALL: + case DPLANE_OP_PW_UNINSTALL: + case DPLANE_OP_ADDR_INSTALL: + case DPLANE_OP_ADDR_UNINSTALL: + case DPLANE_OP_NEIGH_INSTALL: + case DPLANE_OP_NEIGH_UPDATE: + case DPLANE_OP_NEIGH_DELETE: + case DPLANE_OP_VTEP_ADD: + case DPLANE_OP_VTEP_DELETE: + case DPLANE_OP_SYS_ROUTE_ADD: + case DPLANE_OP_SYS_ROUTE_DELETE: + case DPLANE_OP_ROUTE_NOTIFY: + case DPLANE_OP_LSP_NOTIFY: + case DPLANE_OP_NONE: + break; + + default: + if (IS_ZEBRA_DEBUG_FPM) + zlog_debug("%s: unhandled data plane message (%d) %s", + __func__, dplane_ctx_get_op(ctx), + dplane_op2str(dplane_ctx_get_op(ctx))); + break; + } + + /* Skip empty enqueues. */ + if (nl_buf_len == 0) + return 0; + + /* We must know if someday a message goes beyond 65KiB. */ + assert((nl_buf_len + FPM_HEADER_SIZE) <= UINT16_MAX); + + /* Check if we have enough buffer space. */ + if (STREAM_WRITEABLE(fnc->obuf) < (nl_buf_len + FPM_HEADER_SIZE)) { + atomic_fetch_add_explicit(&fnc->counters.buffer_full, 1, + memory_order_relaxed); + + if (IS_ZEBRA_DEBUG_FPM) + zlog_debug( + "%s: buffer full: wants to write %zu but has %zu", + __func__, nl_buf_len + FPM_HEADER_SIZE, + STREAM_WRITEABLE(fnc->obuf)); + + return -1; + } + + /* + * Fill in the FPM header information. + * + * See FPM_HEADER_SIZE definition for more information. + */ + stream_putc(fnc->obuf, 1); + stream_putc(fnc->obuf, 1); + stream_putw(fnc->obuf, nl_buf_len + FPM_HEADER_SIZE); + + /* Write current data. */ + stream_write(fnc->obuf, nl_buf, (size_t)nl_buf_len); + + /* Account number of bytes waiting to be written. */ + atomic_fetch_add_explicit(&fnc->counters.obuf_bytes, + nl_buf_len + FPM_HEADER_SIZE, + memory_order_relaxed); + obytes = atomic_load_explicit(&fnc->counters.obuf_bytes, + memory_order_relaxed); + obytes_peak = atomic_load_explicit(&fnc->counters.obuf_peak, + memory_order_relaxed); + if (obytes_peak < obytes) + atomic_store_explicit(&fnc->counters.obuf_peak, obytes, + memory_order_relaxed); + + /* Tell the thread to start writing. */ + thread_add_write(fnc->fthread->master, fpm_write, fnc, fnc->socket, + &fnc->t_write); + + return 0; +} + +/* + * Next hop walk/send functions. + */ +struct fpm_nhg_arg { + struct zebra_dplane_ctx *ctx; + struct fpm_nl_ctx *fnc; + bool complete; +}; + +static int fpm_nhg_send_cb(struct hash_bucket *bucket, void *arg) +{ + struct nhg_hash_entry *nhe = bucket->data; + struct fpm_nhg_arg *fna = arg; + + /* This entry was already sent, skip it. */ + if (CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_FPM)) + return HASHWALK_CONTINUE; + + /* Reset ctx to reuse allocated memory, take a snapshot and send it. */ + dplane_ctx_reset(fna->ctx); + dplane_ctx_nexthop_init(fna->ctx, DPLANE_OP_NH_INSTALL, nhe); + if (fpm_nl_enqueue(fna->fnc, fna->ctx) == -1) { + /* Our buffers are full, lets give it some cycles. */ + fna->complete = false; + return HASHWALK_ABORT; + } + + /* Mark group as sent, so it doesn't get sent again. */ + SET_FLAG(nhe->flags, NEXTHOP_GROUP_FPM); + + return HASHWALK_CONTINUE; +} + +static int fpm_nhg_send(struct thread *t) +{ + struct fpm_nl_ctx *fnc = THREAD_ARG(t); + struct fpm_nhg_arg fna; + + fna.fnc = fnc; + fna.ctx = dplane_ctx_alloc(); + fna.complete = true; + + /* Send next hops. */ + hash_walk(zrouter.nhgs_id, fpm_nhg_send_cb, &fna); + + /* `free()` allocated memory. */ + dplane_ctx_fini(&fna.ctx); + + /* We are done sending next hops, lets install the routes now. */ + if (fna.complete) { + WALK_FINISH(fnc, FNE_NHG_FINISHED); + thread_add_timer(zrouter.master, fpm_rib_reset, fnc, 0, + &fnc->t_ribreset); + } else /* Otherwise reschedule next hop group again. */ + thread_add_timer(zrouter.master, fpm_nhg_send, fnc, 0, + &fnc->t_nhgwalk); + + return 0; +} + +/** + * Send all RIB installed routes to the connected data plane. + */ +static int fpm_rib_send(struct thread *t) +{ + struct fpm_nl_ctx *fnc = THREAD_ARG(t); + rib_dest_t *dest; + struct route_node *rn; + struct route_table *rt; + struct zebra_dplane_ctx *ctx; + rib_tables_iter_t rt_iter; + + /* Allocate temporary context for all transactions. */ + ctx = dplane_ctx_alloc(); + + rt_iter.state = RIB_TABLES_ITER_S_INIT; + while ((rt = rib_tables_iter_next(&rt_iter))) { + for (rn = route_top(rt); rn; rn = srcdest_route_next(rn)) { + dest = rib_dest_from_rnode(rn); + /* Skip bad route entries. */ + if (dest == NULL || dest->selected_fib == NULL) + continue; + + /* Check for already sent routes. */ + if (CHECK_FLAG(dest->flags, RIB_DEST_UPDATE_FPM)) + continue; + + /* Enqueue route install. */ + dplane_ctx_reset(ctx); + dplane_ctx_route_init(ctx, DPLANE_OP_ROUTE_INSTALL, rn, + dest->selected_fib); + if (fpm_nl_enqueue(fnc, ctx) == -1) { + /* Free the temporary allocated context. */ + dplane_ctx_fini(&ctx); + + thread_add_timer(zrouter.master, fpm_rib_send, + fnc, 1, &fnc->t_ribwalk); + return 0; + } + + /* Mark as sent. */ + SET_FLAG(dest->flags, RIB_DEST_UPDATE_FPM); + } + } + + /* Free the temporary allocated context. */ + dplane_ctx_fini(&ctx); + + /* All RIB routes sent! */ + WALK_FINISH(fnc, FNE_RIB_FINISHED); + + /* Schedule next event: RMAC reset. */ + thread_add_event(zrouter.master, fpm_rmac_reset, fnc, 0, + &fnc->t_rmacreset); + + return 0; +} + +/* + * The next three functions will handle RMAC enqueue. + */ +struct fpm_rmac_arg { + struct zebra_dplane_ctx *ctx; + struct fpm_nl_ctx *fnc; + zebra_l3vni_t *zl3vni; + bool complete; +}; + +static void fpm_enqueue_rmac_table(struct hash_bucket *backet, void *arg) +{ + struct fpm_rmac_arg *fra = arg; + zebra_mac_t *zrmac = backet->data; + struct zebra_if *zif = fra->zl3vni->vxlan_if->info; + const struct zebra_l2info_vxlan *vxl = &zif->l2info.vxl; + struct zebra_if *br_zif; + vlanid_t vid; + bool sticky; + + /* Entry already sent. */ + if (CHECK_FLAG(zrmac->flags, ZEBRA_MAC_FPM_SENT) || !fra->complete) + return; + + sticky = !!CHECK_FLAG(zrmac->flags, + (ZEBRA_MAC_STICKY | ZEBRA_MAC_REMOTE_DEF_GW)); + br_zif = (struct zebra_if *)(zif->brslave_info.br_if->info); + vid = IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(br_zif) ? vxl->access_vlan : 0; + + dplane_ctx_reset(fra->ctx); + dplane_ctx_set_op(fra->ctx, DPLANE_OP_MAC_INSTALL); + dplane_mac_init(fra->ctx, fra->zl3vni->vxlan_if, + zif->brslave_info.br_if, vid, + &zrmac->macaddr, zrmac->fwd_info.r_vtep_ip, sticky, + 0 /*nhg*/, 0 /*update_flags*/); + if (fpm_nl_enqueue(fra->fnc, fra->ctx) == -1) { + thread_add_timer(zrouter.master, fpm_rmac_send, + fra->fnc, 1, &fra->fnc->t_rmacwalk); + fra->complete = false; + } +} + +static void fpm_enqueue_l3vni_table(struct hash_bucket *backet, void *arg) +{ + struct fpm_rmac_arg *fra = arg; + zebra_l3vni_t *zl3vni = backet->data; + + fra->zl3vni = zl3vni; + hash_iterate(zl3vni->rmac_table, fpm_enqueue_rmac_table, zl3vni); +} + +static int fpm_rmac_send(struct thread *t) +{ + struct fpm_rmac_arg fra; + + fra.fnc = THREAD_ARG(t); + fra.ctx = dplane_ctx_alloc(); + fra.complete = true; + hash_iterate(zrouter.l3vni_table, fpm_enqueue_l3vni_table, &fra); + dplane_ctx_fini(&fra.ctx); + + /* RMAC walk completed. */ + if (fra.complete) + WALK_FINISH(fra.fnc, FNE_RMAC_FINISHED); + + return 0; +} + +/* + * Resets the next hop FPM flags so we send all next hops again. + */ +static void fpm_nhg_reset_cb(struct hash_bucket *bucket, void *arg) +{ + struct nhg_hash_entry *nhe = bucket->data; + + /* Unset FPM installation flag so it gets installed again. */ + UNSET_FLAG(nhe->flags, NEXTHOP_GROUP_FPM); +} + +static int fpm_nhg_reset(struct thread *t) +{ + struct fpm_nl_ctx *fnc = THREAD_ARG(t); + + fnc->nhg_complete = false; + hash_iterate(zrouter.nhgs_id, fpm_nhg_reset_cb, NULL); + + /* Schedule next step: send next hop groups. */ + thread_add_event(zrouter.master, fpm_nhg_send, fnc, 0, &fnc->t_nhgwalk); + + return 0; +} + +/** + * Resets the RIB FPM flags so we send all routes again. + */ +static int fpm_rib_reset(struct thread *t) +{ + struct fpm_nl_ctx *fnc = THREAD_ARG(t); + rib_dest_t *dest; + struct route_node *rn; + struct route_table *rt; + rib_tables_iter_t rt_iter; + + fnc->rib_complete = false; + + rt_iter.state = RIB_TABLES_ITER_S_INIT; + while ((rt = rib_tables_iter_next(&rt_iter))) { + for (rn = route_top(rt); rn; rn = srcdest_route_next(rn)) { + dest = rib_dest_from_rnode(rn); + /* Skip bad route entries. */ + if (dest == NULL) + continue; + + UNSET_FLAG(dest->flags, RIB_DEST_UPDATE_FPM); + } + } + + /* Schedule next step: send RIB routes. */ + thread_add_event(zrouter.master, fpm_rib_send, fnc, 0, &fnc->t_ribwalk); + + return 0; +} + +/* + * The next three function will handle RMAC table reset. + */ +static void fpm_unset_rmac_table(struct hash_bucket *backet, void *arg) +{ + zebra_mac_t *zrmac = backet->data; + + UNSET_FLAG(zrmac->flags, ZEBRA_MAC_FPM_SENT); +} + +static void fpm_unset_l3vni_table(struct hash_bucket *backet, void *arg) +{ + zebra_l3vni_t *zl3vni = backet->data; + + hash_iterate(zl3vni->rmac_table, fpm_unset_rmac_table, zl3vni); +} + +static int fpm_rmac_reset(struct thread *t) +{ + struct fpm_nl_ctx *fnc = THREAD_ARG(t); + + fnc->rmac_complete = false; + hash_iterate(zrouter.l3vni_table, fpm_unset_l3vni_table, NULL); + + /* Schedule next event: send RMAC entries. */ + thread_add_event(zrouter.master, fpm_rmac_send, fnc, 0, + &fnc->t_rmacwalk); + + return 0; +} + +static int fpm_process_queue(struct thread *t) +{ + struct fpm_nl_ctx *fnc = THREAD_ARG(t); + struct zebra_dplane_ctx *ctx; + + frr_mutex_lock_autounlock(&fnc->ctxqueue_mutex); + + while (true) { + /* No space available yet. */ + if (STREAM_WRITEABLE(fnc->obuf) < NL_PKT_BUF_SIZE) + break; + + /* Dequeue next item or quit processing. */ + ctx = dplane_ctx_dequeue(&fnc->ctxqueue); + if (ctx == NULL) + break; + + fpm_nl_enqueue(fnc, ctx); + + /* Account the processed entries. */ + atomic_fetch_add_explicit(&fnc->counters.dplane_contexts, 1, + memory_order_relaxed); + atomic_fetch_sub_explicit(&fnc->counters.ctxqueue_len, 1, + memory_order_relaxed); + + dplane_ctx_set_status(ctx, ZEBRA_DPLANE_REQUEST_SUCCESS); + dplane_provider_enqueue_out_ctx(fnc->prov, ctx); + } + + /* Check for more items in the queue. */ + if (atomic_load_explicit(&fnc->counters.ctxqueue_len, + memory_order_relaxed) + > 0) + thread_add_timer(fnc->fthread->master, fpm_process_queue, + fnc, 0, &fnc->t_dequeue); + + /* + * Let the dataplane thread know if there are items in the + * output queue to be processed. Otherwise they may sit + * until the dataplane thread gets scheduled for new, + * unrelated work. + */ + if (dplane_provider_out_ctx_queue_len(fnc->prov) > 0) + dplane_provider_work_ready(); + + return 0; +} + +/** + * Handles external (e.g. CLI, data plane or others) events. + */ +static int fpm_process_event(struct thread *t) +{ + struct fpm_nl_ctx *fnc = THREAD_ARG(t); + int event = THREAD_VAL(t); + + switch (event) { + case FNE_DISABLE: + zlog_info("%s: manual FPM disable event", __func__); + fnc->disabled = true; + atomic_fetch_add_explicit(&fnc->counters.user_disables, 1, + memory_order_relaxed); + + /* Call reconnect to disable timers and clean up context. */ + fpm_reconnect(fnc); + break; + + case FNE_RECONNECT: + zlog_info("%s: manual FPM reconnect event", __func__); + fnc->disabled = false; + atomic_fetch_add_explicit(&fnc->counters.user_configures, 1, + memory_order_relaxed); + fpm_reconnect(fnc); + break; + + case FNE_RESET_COUNTERS: + zlog_info("%s: manual FPM counters reset event", __func__); + memset(&fnc->counters, 0, sizeof(fnc->counters)); + break; + + case FNE_TOGGLE_NHG: + zlog_info("%s: toggle next hop groups support", __func__); + fnc->use_nhg = !fnc->use_nhg; + fpm_reconnect(fnc); + break; + + case FNE_INTERNAL_RECONNECT: + fpm_reconnect(fnc); + break; + + case FNE_NHG_FINISHED: + if (IS_ZEBRA_DEBUG_FPM) + zlog_debug("%s: next hop groups walk finished", + __func__); + + fnc->nhg_complete = true; + break; + case FNE_RIB_FINISHED: + if (IS_ZEBRA_DEBUG_FPM) + zlog_debug("%s: RIB walk finished", __func__); + + fnc->rib_complete = true; + break; + case FNE_RMAC_FINISHED: + if (IS_ZEBRA_DEBUG_FPM) + zlog_debug("%s: RMAC walk finished", __func__); + + fnc->rmac_complete = true; + break; + + default: + if (IS_ZEBRA_DEBUG_FPM) + zlog_debug("%s: unhandled event %d", __func__, event); + break; + } + + return 0; +} + +/* + * Data plane functions. + */ +static int fpm_nl_start(struct zebra_dplane_provider *prov) +{ + struct fpm_nl_ctx *fnc; + + fnc = dplane_provider_get_data(prov); + fnc->fthread = frr_pthread_new(NULL, prov_name, prov_name); + assert(frr_pthread_run(fnc->fthread, NULL) == 0); + fnc->ibuf = stream_new(NL_PKT_BUF_SIZE); + fnc->obuf = stream_new(NL_PKT_BUF_SIZE * 128); + pthread_mutex_init(&fnc->obuf_mutex, NULL); + fnc->socket = -1; + fnc->disabled = true; + fnc->prov = prov; + TAILQ_INIT(&fnc->ctxqueue); + pthread_mutex_init(&fnc->ctxqueue_mutex, NULL); + + /* Set default values. */ + fnc->use_nhg = true; + + return 0; +} + +static int fpm_nl_finish_early(struct fpm_nl_ctx *fnc) +{ + /* Disable all events and close socket. */ + THREAD_OFF(fnc->t_nhgreset); + THREAD_OFF(fnc->t_nhgwalk); + THREAD_OFF(fnc->t_ribreset); + THREAD_OFF(fnc->t_ribwalk); + THREAD_OFF(fnc->t_rmacreset); + THREAD_OFF(fnc->t_rmacwalk); + thread_cancel_async(fnc->fthread->master, &fnc->t_read, NULL); + thread_cancel_async(fnc->fthread->master, &fnc->t_write, NULL); + thread_cancel_async(fnc->fthread->master, &fnc->t_connect, NULL); + + if (fnc->socket != -1) { + close(fnc->socket); + fnc->socket = -1; + } + + return 0; +} + +static int fpm_nl_finish_late(struct fpm_nl_ctx *fnc) +{ + /* Stop the running thread. */ + frr_pthread_stop(fnc->fthread, NULL); + + /* Free all allocated resources. */ + pthread_mutex_destroy(&fnc->obuf_mutex); + pthread_mutex_destroy(&fnc->ctxqueue_mutex); + stream_free(fnc->ibuf); + stream_free(fnc->obuf); + free(gfnc); + gfnc = NULL; + + return 0; +} + +static int fpm_nl_finish(struct zebra_dplane_provider *prov, bool early) +{ + struct fpm_nl_ctx *fnc; + + fnc = dplane_provider_get_data(prov); + if (early) + return fpm_nl_finish_early(fnc); + + return fpm_nl_finish_late(fnc); +} + +static int fpm_nl_process(struct zebra_dplane_provider *prov) +{ + struct zebra_dplane_ctx *ctx; + struct fpm_nl_ctx *fnc; + int counter, limit; + uint64_t cur_queue, peak_queue; + + fnc = dplane_provider_get_data(prov); + limit = dplane_provider_get_work_limit(prov); + for (counter = 0; counter < limit; counter++) { + ctx = dplane_provider_dequeue_in_ctx(prov); + if (ctx == NULL) + break; + + /* + * Skip all notifications if not connected, we'll walk the RIB + * anyway. + */ + if (fnc->socket != -1 && fnc->connecting == false) { + frr_mutex_lock_autounlock(&fnc->ctxqueue_mutex); + dplane_ctx_enqueue_tail(&fnc->ctxqueue, ctx); + + /* Account the number of contexts. */ + atomic_fetch_add_explicit(&fnc->counters.ctxqueue_len, + 1, memory_order_relaxed); + cur_queue = atomic_load_explicit( + &fnc->counters.ctxqueue_len, + memory_order_relaxed); + peak_queue = atomic_load_explicit( + &fnc->counters.ctxqueue_len_peak, + memory_order_relaxed); + if (peak_queue < cur_queue) + atomic_store_explicit( + &fnc->counters.ctxqueue_len_peak, + cur_queue, memory_order_relaxed); + continue; + } + + dplane_ctx_set_status(ctx, ZEBRA_DPLANE_REQUEST_SUCCESS); + dplane_provider_enqueue_out_ctx(prov, ctx); + } + + if (atomic_load_explicit(&fnc->counters.ctxqueue_len, + memory_order_relaxed) + > 0) + thread_add_timer(fnc->fthread->master, fpm_process_queue, + fnc, 0, &fnc->t_dequeue); + + return 0; +} + +static int fpm_nl_new(struct thread_master *tm) +{ + struct zebra_dplane_provider *prov = NULL; + int rv; + + gfnc = calloc(1, sizeof(*gfnc)); + rv = dplane_provider_register(prov_name, DPLANE_PRIO_POSTPROCESS, + DPLANE_PROV_FLAG_THREADED, fpm_nl_start, + fpm_nl_process, fpm_nl_finish, gfnc, + &prov); + + if (IS_ZEBRA_DEBUG_DPLANE) + zlog_debug("%s register status: %d", prov_name, rv); + + install_node(&fpm_node); + install_element(ENABLE_NODE, &fpm_show_counters_cmd); + install_element(ENABLE_NODE, &fpm_show_counters_json_cmd); + install_element(ENABLE_NODE, &fpm_reset_counters_cmd); + install_element(CONFIG_NODE, &fpm_set_address_cmd); + install_element(CONFIG_NODE, &no_fpm_set_address_cmd); + install_element(CONFIG_NODE, &fpm_use_nhg_cmd); + install_element(CONFIG_NODE, &no_fpm_use_nhg_cmd); + + return 0; +} + +static int fpm_nl_init(void) +{ + hook_register(frr_late_init, fpm_nl_new); + return 0; +} + +FRR_MODULE_SETUP( + .name = "dplane_fpm_nl", + .version = "0.0.1", + .description = "Data plane plugin for FPM using netlink.", + .init = fpm_nl_init, + ) diff --git a/zebra/if_ioctl_solaris.c b/zebra/if_ioctl_solaris.c index 8b539a9049..2a2504ebf8 100644 --- a/zebra/if_ioctl_solaris.c +++ b/zebra/if_ioctl_solaris.c @@ -60,7 +60,7 @@ static int interface_list_ioctl(int af) size_t needed, lastneeded = 0; char *buf = NULL; - frr_elevate_privs(&zserv_privs) { + frr_with_privs(&zserv_privs) { sock = socket(af, SOCK_DGRAM, 0); } @@ -72,7 +72,7 @@ static int interface_list_ioctl(int af) } calculate_lifc_len: - frr_elevate_privs(&zserv_privs) { + frr_with_privs(&zserv_privs) { lifn.lifn_family = af; lifn.lifn_flags = LIFC_NOXMIT; /* we want NOXMIT interfaces too */ @@ -107,7 +107,7 @@ static int interface_list_ioctl(int af) lifconf.lifc_len = needed; lifconf.lifc_buf = buf; - frr_elevate_privs(&zserv_privs) { + frr_with_privs(&zserv_privs) { ret = ioctl(sock, SIOCGLIFCONF, &lifconf); } diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index ba518ea576..ce3ed47f94 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -66,9 +66,11 @@ #include "zebra/zebra_ptm.h" #include "zebra/zebra_mpls.h" #include "zebra/kernel_netlink.h" +#include "zebra/rt_netlink.h" #include "zebra/if_netlink.h" #include "zebra/zebra_errors.h" #include "zebra/zebra_vxlan.h" +#include "zebra/zebra_evpn_mh.h" extern struct zebra_privs_t zserv_privs; @@ -244,6 +246,26 @@ static enum zebra_link_type netlink_to_zebra_link_type(unsigned int hwt) } } +static inline void zebra_if_set_ziftype(struct interface *ifp, + zebra_iftype_t zif_type, + zebra_slave_iftype_t zif_slave_type) +{ + struct zebra_if *zif; + + zif = (struct zebra_if *)ifp->info; + zif->zif_slave_type = zif_slave_type; + + if (zif->zif_type != zif_type) { + zif->zif_type = zif_type; + /* If the if_type has been set to bond initialize ES info + * against it. XXX - note that we don't handle the case where + * a zif changes from bond to non-bond; it is really + * an unexpected/error condition. + */ + zebra_evpn_if_init(zif); + } +} + static void netlink_determine_zebra_iftype(const char *kind, zebra_iftype_t *zif_type) { @@ -274,7 +296,7 @@ static void netlink_determine_zebra_iftype(const char *kind, netlink_parse_rtattr((tb), (max), RTA_DATA(rta), RTA_PAYLOAD(rta)) static void netlink_vrf_change(struct nlmsghdr *h, struct rtattr *tb, - const char *name) + uint32_t ns_id, const char *name) { struct ifinfomsg *ifi; struct rtattr *linkinfo[IFLA_INFO_MAX + 1]; @@ -285,7 +307,7 @@ static void netlink_vrf_change(struct nlmsghdr *h, struct rtattr *tb, ifi = NLMSG_DATA(h); - memset(linkinfo, 0, sizeof linkinfo); + memset(linkinfo, 0, sizeof(linkinfo)); parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, tb); if (!linkinfo[IFLA_INFO_DATA]) { @@ -296,7 +318,7 @@ static void netlink_vrf_change(struct nlmsghdr *h, struct rtattr *tb, return; } - memset(attr, 0, sizeof attr); + memset(attr, 0, sizeof(attr)); parse_rtattr_nested(attr, IFLA_VRF_MAX, linkinfo[IFLA_INFO_DATA]); if (!attr[IFLA_VRF_TABLE]) { if (IS_ZEBRA_DEBUG_KERNEL) @@ -313,6 +335,21 @@ static void netlink_vrf_change(struct nlmsghdr *h, struct rtattr *tb, zlog_debug("RTM_NEWLINK for VRF %s(%u) table %u", name, ifi->ifi_index, nl_table_id); + if (!vrf_lookup_by_id((vrf_id_t)ifi->ifi_index)) { + vrf_id_t exist_id; + + exist_id = vrf_lookup_by_table(nl_table_id, ns_id); + if (exist_id != VRF_DEFAULT) { + vrf = vrf_lookup_by_id(exist_id); + + flog_err( + EC_ZEBRA_VRF_MISCONFIGURED, + "VRF %s id %u table id overlaps existing vrf %s, misconfiguration exiting", + name, ifi->ifi_index, vrf->name); + exit(-1); + } + } + /* * vrf_get is implied creation if it does not exist */ @@ -365,7 +402,7 @@ static void netlink_vrf_change(struct nlmsghdr *h, struct rtattr *tb, } } -static int get_iflink_speed(struct interface *interface) +static uint32_t get_iflink_speed(struct interface *interface, int *error) { struct ifreq ifdata; struct ethtool_cmd ecmd; @@ -373,6 +410,8 @@ static int get_iflink_speed(struct interface *interface) int rc; const char *ifname = interface->name; + if (error) + *error = 0; /* initialize struct */ memset(&ifdata, 0, sizeof(ifdata)); @@ -385,7 +424,7 @@ static int get_iflink_speed(struct interface *interface) ifdata.ifr_data = (caddr_t)&ecmd; /* use ioctl to get IP address of an interface */ - frr_elevate_privs(&zserv_privs) { + frr_with_privs(&zserv_privs) { sd = vrf_socket(PF_INET, SOCK_DGRAM, IPPROTO_IP, interface->vrf_id, NULL); @@ -393,6 +432,9 @@ static int get_iflink_speed(struct interface *interface) if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug("Failure to read interface %s speed: %d %s", ifname, errno, safe_strerror(errno)); + /* no vrf socket creation may probably mean vrf issue */ + if (error) + *error = -1; return 0; } /* Get the current link state for the interface */ @@ -404,18 +446,21 @@ static int get_iflink_speed(struct interface *interface) zlog_debug( "IOCTL failure to read interface %s speed: %d %s", ifname, errno, safe_strerror(errno)); + /* no device means interface unreachable */ + if (errno == ENODEV && error) + *error = -1; ecmd.speed_hi = 0; ecmd.speed = 0; } close(sd); - return (ecmd.speed_hi << 16) | ecmd.speed; + return ((uint32_t)ecmd.speed_hi << 16) | ecmd.speed; } -uint32_t kernel_get_speed(struct interface *ifp) +uint32_t kernel_get_speed(struct interface *ifp, int *error) { - return get_iflink_speed(ifp); + return get_iflink_speed(ifp, error); } static int netlink_extract_bridge_info(struct rtattr *link_data, @@ -424,7 +469,7 @@ static int netlink_extract_bridge_info(struct rtattr *link_data, struct rtattr *attr[IFLA_BR_MAX + 1]; memset(bridge_info, 0, sizeof(*bridge_info)); - memset(attr, 0, sizeof attr); + memset(attr, 0, sizeof(attr)); parse_rtattr_nested(attr, IFLA_BR_MAX, link_data); if (attr[IFLA_BR_VLAN_FILTERING]) bridge_info->vlan_aware = @@ -439,7 +484,7 @@ static int netlink_extract_vlan_info(struct rtattr *link_data, vlanid_t vid_in_msg; memset(vlan_info, 0, sizeof(*vlan_info)); - memset(attr, 0, sizeof attr); + memset(attr, 0, sizeof(attr)); parse_rtattr_nested(attr, IFLA_VLAN_MAX, link_data); if (!attr[IFLA_VLAN_ID]) { if (IS_ZEBRA_DEBUG_KERNEL) @@ -458,9 +503,10 @@ static int netlink_extract_vxlan_info(struct rtattr *link_data, struct rtattr *attr[IFLA_VXLAN_MAX + 1]; vni_t vni_in_msg; struct in_addr vtep_ip_in_msg; + ifindex_t ifindex_link; memset(vxl_info, 0, sizeof(*vxl_info)); - memset(attr, 0, sizeof attr); + memset(attr, 0, sizeof(attr)); parse_rtattr_nested(attr, IFLA_VXLAN_MAX, link_data); if (!attr[IFLA_VXLAN_ID]) { if (IS_ZEBRA_DEBUG_KERNEL) @@ -486,6 +532,14 @@ static int netlink_extract_vxlan_info(struct rtattr *link_data, *(struct in_addr *)RTA_DATA(attr[IFLA_VXLAN_GROUP]); } + if (!attr[IFLA_VXLAN_LINK]) { + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("IFLA_VXLAN_LINK missing from VXLAN IF message"); + } else { + ifindex_link = + *(ifindex_t *)RTA_DATA(attr[IFLA_VXLAN_LINK]); + vxl_info->ifindex_link = ifindex_link; + } return 0; } @@ -495,7 +549,8 @@ static int netlink_extract_vxlan_info(struct rtattr *link_data, * its members. Likewise, for VxLAN interface. */ static void netlink_interface_update_l2info(struct interface *ifp, - struct rtattr *link_data, int add) + struct rtattr *link_data, int add, + ns_id_t link_nsid) { if (!link_data) return; @@ -514,10 +569,83 @@ static void netlink_interface_update_l2info(struct interface *ifp, struct zebra_l2info_vxlan vxlan_info; netlink_extract_vxlan_info(link_data, &vxlan_info); + vxlan_info.link_nsid = link_nsid; zebra_l2_vxlanif_add_update(ifp, &vxlan_info, add); + if (link_nsid != NS_UNKNOWN && + vxlan_info.ifindex_link) + zebra_if_update_link(ifp, vxlan_info.ifindex_link, + link_nsid); } } +static int netlink_bridge_vxlan_update(struct interface *ifp, + struct rtattr *af_spec) +{ + struct rtattr *aftb[IFLA_BRIDGE_MAX + 1]; + struct bridge_vlan_info *vinfo; + vlanid_t access_vlan; + + /* There is a 1-to-1 mapping of VLAN to VxLAN - hence + * only 1 access VLAN is accepted. + */ + memset(aftb, 0, sizeof(aftb)); + parse_rtattr_nested(aftb, IFLA_BRIDGE_MAX, af_spec); + if (!aftb[IFLA_BRIDGE_VLAN_INFO]) + return 0; + + vinfo = RTA_DATA(aftb[IFLA_BRIDGE_VLAN_INFO]); + if (!(vinfo->flags & BRIDGE_VLAN_INFO_PVID)) + return 0; + + access_vlan = (vlanid_t)vinfo->vid; + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("Access VLAN %u for VxLAN IF %s(%u)", access_vlan, + ifp->name, ifp->ifindex); + zebra_l2_vxlanif_update_access_vlan(ifp, access_vlan); + return 0; +} + +static void netlink_bridge_vlan_update(struct interface *ifp, + struct rtattr *af_spec) +{ + struct rtattr *i; + int rem; + uint16_t vid_range_start = 0; + struct zebra_if *zif; + bitfield_t old_vlan_bitmap; + struct bridge_vlan_info *vinfo; + + zif = (struct zebra_if *)ifp->info; + + /* cache the old bitmap addrs */ + old_vlan_bitmap = zif->vlan_bitmap; + /* create a new bitmap space for re-eval */ + bf_init(zif->vlan_bitmap, IF_VLAN_BITMAP_MAX); + + for (i = RTA_DATA(af_spec), rem = RTA_PAYLOAD(af_spec); + RTA_OK(i, rem); i = RTA_NEXT(i, rem)) { + + if (i->rta_type != IFLA_BRIDGE_VLAN_INFO) + continue; + + vinfo = RTA_DATA(i); + + if (vinfo->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) { + vid_range_start = vinfo->vid; + continue; + } + + if (!(vinfo->flags & BRIDGE_VLAN_INFO_RANGE_END)) + vid_range_start = vinfo->vid; + + zebra_vlan_bitmap_compute(ifp, vid_range_start, vinfo->vid); + } + + zebra_vlan_mbr_re_eval(ifp, old_vlan_bitmap); + + bf_free(old_vlan_bitmap); +} + static int netlink_bridge_interface(struct nlmsghdr *h, int len, ns_id_t ns_id, int startup) { @@ -525,16 +653,12 @@ static int netlink_bridge_interface(struct nlmsghdr *h, int len, ns_id_t ns_id, struct ifinfomsg *ifi; struct rtattr *tb[IFLA_MAX + 1]; struct interface *ifp; - struct rtattr *aftb[IFLA_BRIDGE_MAX + 1]; - struct { - uint16_t flags; - uint16_t vid; - } * vinfo; - vlanid_t access_vlan; + struct zebra_if *zif; + struct rtattr *af_spec; /* Fetch name and ifindex */ ifi = NLMSG_DATA(h); - memset(tb, 0, sizeof tb); + memset(tb, 0, sizeof(tb)); netlink_parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len); if (tb[IFLA_IFNAME] == NULL) @@ -548,30 +672,22 @@ static int netlink_bridge_interface(struct nlmsghdr *h, int len, ns_id_t ns_id, ifi->ifi_index); return 0; } - if (!IS_ZEBRA_IF_VXLAN(ifp)) - return 0; /* We are only interested in the access VLAN i.e., AF_SPEC */ - if (!tb[IFLA_AF_SPEC]) - return 0; + af_spec = tb[IFLA_AF_SPEC]; + if (!af_spec) + return 0; - /* There is a 1-to-1 mapping of VLAN to VxLAN - hence - * only 1 access VLAN is accepted. - */ - memset(aftb, 0, sizeof aftb); - parse_rtattr_nested(aftb, IFLA_BRIDGE_MAX, tb[IFLA_AF_SPEC]); - if (!aftb[IFLA_BRIDGE_VLAN_INFO]) - return 0; + if (IS_ZEBRA_IF_VXLAN(ifp)) + return netlink_bridge_vxlan_update(ifp, af_spec); - vinfo = RTA_DATA(aftb[IFLA_BRIDGE_VLAN_INFO]); - if (!(vinfo->flags & BRIDGE_VLAN_INFO_PVID)) - return 0; + /* build vlan bitmap associated with this interface if that + * device type is interested in the vlans + */ + zif = (struct zebra_if *)ifp->info; + if (bf_is_inited(zif->vlan_bitmap)) + netlink_bridge_vlan_update(ifp, af_spec); - access_vlan = (vlanid_t)vinfo->vid; - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("Access VLAN %u for VxLAN IF %s(%u)", access_vlan, - name, ifi->ifi_index); - zebra_l2_vxlanif_update_access_vlan(ifp, access_vlan); return 0; } @@ -590,7 +706,7 @@ static int netlink_interface(struct nlmsghdr *h, ns_id_t ns_id, int startup) char *kind = NULL; char *desc = NULL; char *slave_kind = NULL; - struct zebra_ns *zns; + struct zebra_ns *zns = NULL; vrf_id_t vrf_id = VRF_DEFAULT; zebra_iftype_t zif_type = ZEBRA_IF_OTHER; zebra_slave_iftype_t zif_slave_type = ZEBRA_IF_SLAVE_NONE; @@ -598,6 +714,7 @@ static int netlink_interface(struct nlmsghdr *h, ns_id_t ns_id, int startup) ifindex_t link_ifindex = IFINDEX_INTERNAL; ifindex_t bond_ifindex = IFINDEX_INTERNAL; struct zebra_if *zif; + ns_id_t link_nsid = ns_id; zns = zebra_ns_lookup(ns_id); ifi = NLMSG_DATA(h); @@ -607,10 +724,10 @@ static int netlink_interface(struct nlmsghdr *h, ns_id_t ns_id, int startup) len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct ifinfomsg)); if (len < 0) { - zlog_err("%s: Message received from netlink is of a broken size: %d %zu", - __PRETTY_FUNCTION__, - h->nlmsg_len, - (size_t)NLMSG_LENGTH(sizeof(struct ifinfomsg))); + zlog_err( + "%s: Message received from netlink is of a broken size: %d %zu", + __func__, h->nlmsg_len, + (size_t)NLMSG_LENGTH(sizeof(struct ifinfomsg))); return -1; } @@ -619,8 +736,8 @@ static int netlink_interface(struct nlmsghdr *h, ns_id_t ns_id, int startup) return netlink_bridge_interface(h, len, ns_id, startup); /* Looking up interface name. */ - memset(tb, 0, sizeof tb); - memset(linkinfo, 0, sizeof linkinfo); + memset(tb, 0, sizeof(tb)); + memset(linkinfo, 0, sizeof(linkinfo)); netlink_parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len); /* check for wireless messages to ignore */ @@ -655,7 +772,7 @@ static int netlink_interface(struct nlmsghdr *h, ns_id_t ns_id, int startup) /* If VRF, create the VRF structure itself. */ if (zif_type == ZEBRA_IF_VRF && !vrf_is_backend_netns()) { - netlink_vrf_change(h, tb[IFLA_LINKINFO], name); + netlink_vrf_change(h, tb[IFLA_LINKINFO], ns_id, name); vrf_id = (vrf_id_t)ifi->ifi_index; } @@ -681,18 +798,28 @@ static int netlink_interface(struct nlmsghdr *h, ns_id_t ns_id, int startup) if (tb[IFLA_LINK]) link_ifindex = *(ifindex_t *)RTA_DATA(tb[IFLA_LINK]); - /* Add interface. */ - ifp = if_get_by_name(name, vrf_id); - set_ifindex(ifp, ifi->ifi_index, zns); + if (tb[IFLA_LINK_NETNSID]) { + link_nsid = *(ns_id_t *)RTA_DATA(tb[IFLA_LINK_NETNSID]); + link_nsid = ns_id_get_absolute(ns_id, link_nsid); + } + + /* Add interface. + * We add by index first because in some cases such as the master + * interface, we have the index before we have the name. Fixing + * back references on the slave interfaces is painful if not done + * this way, i.e. by creating by ifindex. + */ + ifp = if_get_by_ifindex(ifi->ifi_index, vrf_id); + set_ifindex(ifp, ifi->ifi_index, zns); /* add it to ns struct */ + + if_set_name(ifp, name); + ifp->flags = ifi->ifi_flags & 0x0000fffff; ifp->mtu6 = ifp->mtu = *(uint32_t *)RTA_DATA(tb[IFLA_MTU]); ifp->metric = 0; - ifp->speed = get_iflink_speed(ifp); + ifp->speed = get_iflink_speed(ifp, NULL); ifp->ptm_status = ZEBRA_PTM_STATUS_UNKNOWN; - if (desc) - ifp->desc = XSTRDUP(MTYPE_TMP, desc); - /* Set zebra interface type */ zebra_if_set_ziftype(ifp, zif_type, zif_slave_type); if (IS_ZEBRA_IF_VRF(ifp)) @@ -707,6 +834,11 @@ static int netlink_interface(struct nlmsghdr *h, ns_id_t ns_id, int startup) zif = (struct zebra_if *)ifp->info; zif->link_ifindex = link_ifindex; + if (desc) { + XFREE(MTYPE_TMP, zif->desc); + zif->desc = XSTRDUP(MTYPE_TMP, desc); + } + /* Hardware type and address. */ ifp->ll_type = netlink_to_zebra_link_type(ifi->ifi_type); netlink_interface_update_hw_addr(tb, ifp); @@ -715,9 +847,10 @@ static int netlink_interface(struct nlmsghdr *h, ns_id_t ns_id, int startup) /* Extract and save L2 interface information, take additional actions. */ - netlink_interface_update_l2info(ifp, linkinfo[IFLA_INFO_DATA], 1); + netlink_interface_update_l2info(ifp, linkinfo[IFLA_INFO_DATA], + 1, link_nsid); if (IS_ZEBRA_IF_BRIDGE_SLAVE(ifp)) - zebra_l2if_update_bridge_slave(ifp, bridge_ifindex); + zebra_l2if_update_bridge_slave(ifp, bridge_ifindex, ns_id); else if (IS_ZEBRA_IF_BOND_SLAVE(ifp)) zebra_l2if_update_bond_slave(ifp, bond_ifindex); @@ -743,9 +876,9 @@ static int netlink_request_intf_addr(struct nlsock *netlink_cmd, int family, /* Include filter, if specified. */ if (filter_mask) - addattr32(&req.n, sizeof(req), IFLA_EXT_MASK, filter_mask); + nl_attr_put32(&req.n, sizeof(req), IFLA_EXT_MASK, filter_mask); - return netlink_request(netlink_cmd, &req.n); + return netlink_request(netlink_cmd, &req); } /* Interface lookup by netlink socket. */ @@ -789,6 +922,23 @@ int interface_lookup_netlink(struct zebra_ns *zns) /* fixup linkages */ zebra_if_update_all_links(); + return 0; +} + +/** + * interface_addr_lookup_netlink() - Look up interface addresses + * + * @zns: Zebra netlink socket + * Return: Result status + */ +static int interface_addr_lookup_netlink(struct zebra_ns *zns) +{ + int ret; + struct zebra_dplane_info dp_info; + struct nlsock *netlink_cmd = &zns->netlink_cmd; + + /* Capture key info from ns struct */ + zebra_dplane_info_from_zns(&dp_info, zns, true /*is_cmd*/); /* Get IPv4 address of the interfaces. */ ret = netlink_request_intf_addr(netlink_cmd, AF_INET, RTM_GETADDR, 0); @@ -822,7 +972,7 @@ int kernel_interface_set_master(struct interface *master, char buf[NL_PKT_BUF_SIZE]; } req; - memset(&req, 0, sizeof req); + memset(&req, 0, sizeof(req)); req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); req.n.nlmsg_flags = NLM_F_REQUEST; @@ -831,15 +981,16 @@ int kernel_interface_set_master(struct interface *master, req.ifa.ifi_index = slave->ifindex; - addattr_l(&req.n, sizeof req, IFLA_MASTER, &master->ifindex, 4); - addattr_l(&req.n, sizeof req, IFLA_LINK, &slave->ifindex, 4); + nl_attr_put32(&req.n, sizeof(req), IFLA_MASTER, master->ifindex); + nl_attr_put32(&req.n, sizeof(req), IFLA_LINK, slave->ifindex); return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns, 0); } /* Interface address modification. */ -static int netlink_address_ctx(const struct zebra_dplane_ctx *ctx) +static ssize_t netlink_address_msg_encoder(struct zebra_dplane_ctx *ctx, + void *buf, size_t buflen) { int bytelen; const struct prefix *p; @@ -849,62 +1000,72 @@ static int netlink_address_ctx(const struct zebra_dplane_ctx *ctx) struct { struct nlmsghdr n; struct ifaddrmsg ifa; - char buf[NL_PKT_BUF_SIZE]; - } req; + char buf[0]; + } *req = buf; + + if (buflen < sizeof(*req)) + return 0; p = dplane_ctx_get_intf_addr(ctx); - memset(&req, 0, sizeof(req) - NL_PKT_BUF_SIZE); + memset(req, 0, sizeof(*req)); bytelen = (p->family == AF_INET ? 4 : 16); - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); - req.n.nlmsg_flags = NLM_F_REQUEST; + req->n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); + req->n.nlmsg_flags = NLM_F_REQUEST; if (dplane_ctx_get_op(ctx) == DPLANE_OP_ADDR_INSTALL) cmd = RTM_NEWADDR; else cmd = RTM_DELADDR; - req.n.nlmsg_type = cmd; - req.ifa.ifa_family = p->family; + req->n.nlmsg_type = cmd; + req->ifa.ifa_family = p->family; - req.ifa.ifa_index = dplane_ctx_get_ifindex(ctx); + req->ifa.ifa_index = dplane_ctx_get_ifindex(ctx); - addattr_l(&req.n, sizeof(req), IFA_LOCAL, &p->u.prefix, bytelen); + if (!nl_attr_put(&req->n, buflen, IFA_LOCAL, &p->u.prefix, bytelen)) + return 0; if (p->family == AF_INET) { if (dplane_ctx_intf_is_connected(ctx)) { p = dplane_ctx_get_intf_dest(ctx); - addattr_l(&req.n, sizeof(req), IFA_ADDRESS, - &p->u.prefix, bytelen); - } else if (cmd == RTM_NEWADDR && - dplane_ctx_intf_has_dest(ctx)) { - p = dplane_ctx_get_intf_dest(ctx); - addattr_l(&req.n, sizeof(req), IFA_BROADCAST, - &p->u.prefix, bytelen); + if (!nl_attr_put(&req->n, buflen, IFA_ADDRESS, + &p->u.prefix, bytelen)) + return 0; + } else if (cmd == RTM_NEWADDR) { + struct in_addr broad = { + .s_addr = ipv4_broadcast_addr(p->u.prefix4.s_addr, + p->prefixlen) + }; + if (!nl_attr_put(&req->n, buflen, IFA_BROADCAST, &broad, + bytelen)) + return 0; } } /* p is now either address or destination/bcast addr */ - req.ifa.ifa_prefixlen = p->prefixlen; + req->ifa.ifa_prefixlen = p->prefixlen; if (dplane_ctx_intf_is_secondary(ctx)) - SET_FLAG(req.ifa.ifa_flags, IFA_F_SECONDARY); + SET_FLAG(req->ifa.ifa_flags, IFA_F_SECONDARY); if (dplane_ctx_intf_has_label(ctx)) { label = dplane_ctx_get_intf_label(ctx); - addattr_l(&req.n, sizeof(req), IFA_LABEL, label, - strlen(label) + 1); + if (!nl_attr_put(&req->n, buflen, IFA_LABEL, label, + strlen(label) + 1)) + return 0; } - return netlink_talk_info(netlink_talk_filter, &req.n, - dplane_ctx_get_ns(ctx), 0); + return NLMSG_ALIGN(req->n.nlmsg_len); } -enum zebra_dplane_result kernel_address_update_ctx(struct zebra_dplane_ctx *ctx) +enum netlink_msg_status +netlink_put_address_update_msg(struct nl_batch *bth, + struct zebra_dplane_ctx *ctx) { - return (netlink_address_ctx(ctx) == 0 ? - ZEBRA_DPLANE_REQUEST_SUCCESS : ZEBRA_DPLANE_REQUEST_FAILURE); + return netlink_batch_add_msg(bth, ctx, netlink_address_msg_encoder, + false); } int netlink_interface_addr(struct nlmsghdr *h, ns_id_t ns_id, int startup) @@ -937,14 +1098,14 @@ int netlink_interface_addr(struct nlmsghdr *h, ns_id_t ns_id, int startup) len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct ifaddrmsg)); if (len < 0) { - zlog_err("%s: Message received from netlink is of a broken size: %d %zu", - __PRETTY_FUNCTION__, - h->nlmsg_len, - (size_t)NLMSG_LENGTH(sizeof(struct ifaddrmsg))); + zlog_err( + "%s: Message received from netlink is of a broken size: %d %zu", + __func__, h->nlmsg_len, + (size_t)NLMSG_LENGTH(sizeof(struct ifaddrmsg))); return -1; } - memset(tb, 0, sizeof tb); + memset(tb, 0, sizeof(tb)); netlink_parse_rtattr(tb, IFA_MAX, IFA_RTA(ifa), len); ifp = if_lookup_by_index_per_ns(zns, ifa->ifa_index); @@ -1019,7 +1180,8 @@ int netlink_interface_addr(struct nlmsghdr *h, ns_id_t ns_id, int startup) /* addr is primary key, SOL if we don't have one */ if (addr == NULL) { - zlog_debug("%s: NULL address", __func__); + zlog_debug("%s: Local Interface Address is NULL for %s", + __func__, ifp->name); return -1; } @@ -1046,16 +1208,23 @@ int netlink_interface_addr(struct nlmsghdr *h, ns_id_t ns_id, int startup) nl_msg_type_to_str(h->nlmsg_type)); return -1; } + if (h->nlmsg_type == RTM_NEWADDR) connected_add_ipv4(ifp, flags, (struct in_addr *)addr, ifa->ifa_prefixlen, (struct in_addr *)broad, label, metric); - else + else if (CHECK_FLAG(flags, ZEBRA_IFA_PEER)) { + /* Delete with a peer address */ connected_delete_ipv4( ifp, flags, (struct in_addr *)addr, - ifa->ifa_prefixlen, (struct in_addr *)broad); + ifa->ifa_prefixlen, broad); + } else + connected_delete_ipv4( + ifp, flags, (struct in_addr *)addr, + ifa->ifa_prefixlen, NULL); } + if (ifa->ifa_family == AF_INET6) { if (ifa->ifa_prefixlen > IPV6_MAX_BITLEN) { zlog_err( @@ -1080,10 +1249,17 @@ int netlink_interface_addr(struct nlmsghdr *h, ns_id_t ns_id, int startup) metric); } else connected_delete_ipv6(ifp, (struct in6_addr *)addr, - (struct in6_addr *)broad, - ifa->ifa_prefixlen); + NULL, ifa->ifa_prefixlen); } + + /* + * Linux kernel does not send route delete on interface down/addr del + * so we have to re-process routes it owns (i.e. kernel routes) + */ + if (h->nlmsg_type != RTM_NEWADDR) + rib_update(RIB_UPDATE_KERNEL); + return 0; } @@ -1106,7 +1282,8 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) ifindex_t bond_ifindex = IFINDEX_INTERNAL; ifindex_t link_ifindex = IFINDEX_INTERNAL; uint8_t old_hw_addr[INTERFACE_HWADDR_MAX]; - + struct zebra_if *zif; + ns_id_t link_nsid = ns_id; zns = zebra_ns_lookup(ns_id); ifi = NLMSG_DATA(h); @@ -1130,9 +1307,10 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct ifinfomsg)); if (len < 0) { - zlog_err("%s: Message received from netlink is of a broken size %d %zu", - __PRETTY_FUNCTION__, h->nlmsg_len, - (size_t)NLMSG_LENGTH(sizeof(struct ifinfomsg))); + zlog_err( + "%s: Message received from netlink is of a broken size %d %zu", + __func__, h->nlmsg_len, + (size_t)NLMSG_LENGTH(sizeof(struct ifinfomsg))); return -1; } @@ -1141,8 +1319,8 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) return netlink_bridge_interface(h, len, ns_id, startup); /* Looking up interface name. */ - memset(tb, 0, sizeof tb); - memset(linkinfo, 0, sizeof linkinfo); + memset(tb, 0, sizeof(tb)); + memset(linkinfo, 0, sizeof(linkinfo)); netlink_parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len); /* check for wireless messages to ignore */ @@ -1173,25 +1351,23 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) if (tb[IFLA_LINK]) link_ifindex = *(ifindex_t *)RTA_DATA(tb[IFLA_LINK]); + if (tb[IFLA_LINK_NETNSID]) { + link_nsid = *(ns_id_t *)RTA_DATA(tb[IFLA_LINK_NETNSID]); + link_nsid = ns_id_get_absolute(ns_id, link_nsid); + } if (tb[IFLA_IFALIAS]) { desc = (char *)RTA_DATA(tb[IFLA_IFALIAS]); } /* If VRF, create or update the VRF structure itself. */ if (zif_type == ZEBRA_IF_VRF && !vrf_is_backend_netns()) { - netlink_vrf_change(h, tb[IFLA_LINKINFO], name); + netlink_vrf_change(h, tb[IFLA_LINKINFO], ns_id, name); vrf_id = (vrf_id_t)ifi->ifi_index; } /* See if interface is present. */ ifp = if_lookup_by_name_per_ns(zns, name); - if (ifp) { - XFREE(MTYPE_TMP, ifp->desc); - if (desc) - ifp->desc = XSTRDUP(MTYPE_TMP, desc); - } - if (h->nlmsg_type == RTM_NEWLINK) { if (tb[IFLA_MASTER]) { if (slave_kind && (strcmp(slave_kind, "vrf") == 0) @@ -1218,8 +1394,7 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) /* Add interface notification from kernel */ if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug( - "RTM_NEWLINK ADD for %s(%u) vrf_id %u type %d " - "sl_type %d master %u flags 0x%x", + "RTM_NEWLINK ADD for %s(%u) vrf_id %u type %d sl_type %d master %u flags 0x%x", name, ifi->ifi_index, vrf_id, zif_type, zif_slave_type, bridge_ifindex, ifi->ifi_flags); @@ -1263,18 +1438,19 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) /* Extract and save L2 interface information, take * additional actions. */ netlink_interface_update_l2info( - ifp, linkinfo[IFLA_INFO_DATA], 1); + ifp, linkinfo[IFLA_INFO_DATA], + 1, link_nsid); if (IS_ZEBRA_IF_BRIDGE_SLAVE(ifp)) zebra_l2if_update_bridge_slave(ifp, - bridge_ifindex); + bridge_ifindex, + ns_id); else if (IS_ZEBRA_IF_BOND_SLAVE(ifp)) zebra_l2if_update_bond_slave(ifp, bond_ifindex); } else if (ifp->vrf_id != vrf_id) { /* VRF change for an interface. */ if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug( - "RTM_NEWLINK vrf-change for %s(%u) " - "vrf_id %u -> %u flags 0x%x", + "RTM_NEWLINK vrf-change for %s(%u) vrf_id %u -> %u flags 0x%x", name, ifp->ifindex, ifp->vrf_id, vrf_id, ifi->ifi_flags); @@ -1285,8 +1461,7 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) /* Interface update. */ if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug( - "RTM_NEWLINK update for %s(%u) " - "sl_type %d master %u flags 0x%x", + "RTM_NEWLINK update for %s(%u) sl_type %d master %u flags 0x%x", name, ifp->ifindex, zif_slave_type, bridge_ifindex, ifi->ifi_flags); @@ -1318,6 +1493,7 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) "Intf %s(%u) has gone DOWN", name, ifp->ifindex); if_down(ifp); + rib_update(RIB_UPDATE_KERNEL); } else if (if_is_operative(ifp)) { /* Must notify client daemons of new * interface status. */ @@ -1351,19 +1527,35 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) "Intf %s(%u) has come UP", name, ifp->ifindex); if_up(ifp); + } else { + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug( + "Intf %s(%u) has gone DOWN", + name, ifp->ifindex); + if_down(ifp); + rib_update(RIB_UPDATE_KERNEL); } } /* Extract and save L2 interface information, take * additional actions. */ netlink_interface_update_l2info( - ifp, linkinfo[IFLA_INFO_DATA], 0); + ifp, linkinfo[IFLA_INFO_DATA], + 0, link_nsid); if (IS_ZEBRA_IF_BRIDGE_SLAVE(ifp) || was_bridge_slave) zebra_l2if_update_bridge_slave(ifp, - bridge_ifindex); + bridge_ifindex, + ns_id); else if (IS_ZEBRA_IF_BOND_SLAVE(ifp) || was_bond_slave) zebra_l2if_update_bond_slave(ifp, bond_ifindex); } + + zif = ifp->info; + if (zif) { + XFREE(MTYPE_TMP, zif->desc); + if (desc) + zif->desc = XSTRDUP(MTYPE_TMP, desc); + } } else { /* Delete interface notification from kernel */ if (ifp == NULL) { @@ -1386,17 +1578,49 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) else if (IS_ZEBRA_IF_VXLAN(ifp)) zebra_l2_vxlanif_del(ifp); - if (!IS_ZEBRA_IF_VRF(ifp)) - if_delete_update(ifp); + if_delete_update(ifp); } return 0; } +int netlink_protodown(struct interface *ifp, bool down) +{ + struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT); + + struct { + struct nlmsghdr n; + struct ifinfomsg ifa; + char buf[NL_PKT_BUF_SIZE]; + } req; + + memset(&req, 0, sizeof(req)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_type = RTM_SETLINK; + req.n.nlmsg_pid = zns->netlink_cmd.snl.nl_pid; + + req.ifa.ifi_index = ifp->ifindex; + + nl_attr_put(&req.n, sizeof(req), IFLA_PROTO_DOWN, &down, sizeof(down)); + nl_attr_put32(&req.n, sizeof(req), IFLA_LINK, ifp->ifindex); + + return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns, + 0); +} + /* Interface information read by netlink. */ void interface_list(struct zebra_ns *zns) { interface_lookup_netlink(zns); + /* We add routes for interface address, + * so we need to get the nexthop info + * from the kernel before we can do that + */ + netlink_nexthop_read(zns); + + interface_addr_lookup_netlink(zns); } #endif /* GNU_LINUX */ diff --git a/zebra/if_netlink.h b/zebra/if_netlink.h index 710fd52558..0bbba81ca6 100644 --- a/zebra/if_netlink.h +++ b/zebra/if_netlink.h @@ -32,6 +32,24 @@ extern int netlink_interface_addr(struct nlmsghdr *h, ns_id_t ns_id, extern int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup); extern int interface_lookup_netlink(struct zebra_ns *zns); +extern enum netlink_msg_status +netlink_put_address_update_msg(struct nl_batch *bth, + struct zebra_dplane_ctx *ctx); + +/* + * Set protodown status of interface. + * + * ifp + * Interface to set protodown on. + * + * down + * If true, set protodown on. If false, set protodown off. + * + * Returns: + * 0 + */ +int netlink_protodown(struct interface *ifp, bool down); + #ifdef __cplusplus } #endif diff --git a/zebra/interface.c b/zebra/interface.c index 10f1f92100..d29f61450e 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -47,9 +47,11 @@ #include "zebra/irdp.h" #include "zebra/zebra_ptm.h" #include "zebra/rt_netlink.h" +#include "zebra/if_netlink.h" #include "zebra/interface.h" #include "zebra/zebra_vxlan.h" #include "zebra/zebra_errors.h" +#include "zebra/zebra_evpn_mh.h" DEFINE_MTYPE_STATIC(ZEBRA, ZINFO, "Zebra Interface Information") @@ -68,18 +70,31 @@ static int if_zebra_speed_update(struct thread *thread) struct interface *ifp = THREAD_ARG(thread); struct zebra_if *zif = ifp->info; uint32_t new_speed; + bool changed = false; + int error = 0; zif->speed_update = NULL; - new_speed = kernel_get_speed(ifp); + new_speed = kernel_get_speed(ifp, &error); + + /* error may indicate vrf not available or + * interfaces not available. + * note that loopback & virtual interfaces can return 0 as speed + */ + if (error < 0) + return 1; + if (new_speed != ifp->speed) { - zlog_info("%s: %s old speed: %u new speed: %u", - __PRETTY_FUNCTION__, ifp->name, ifp->speed, - new_speed); + zlog_info("%s: %s old speed: %u new speed: %u", __func__, + ifp->name, ifp->speed, new_speed); ifp->speed = new_speed; if_add_update(ifp); + changed = true; } + if (changed || new_speed == UINT32_MAX) + thread_add_timer(zrouter.master, if_zebra_speed_update, ifp, 5, + &zif->speed_update); return 1; } @@ -92,6 +107,17 @@ static void zebra_if_node_destroy(route_table_delegate_t *delegate, route_node_destroy(delegate, table, node); } +static void zebra_if_nhg_dependents_free(struct zebra_if *zebra_if) +{ + nhg_connected_tree_free(&zebra_if->nhg_dependents); +} + +static void zebra_if_nhg_dependents_init(struct zebra_if *zebra_if) +{ + nhg_connected_tree_init(&zebra_if->nhg_dependents); +} + + route_table_delegate_t zebra_if_table_delegate = { .create_node = route_node_create, .destroy_node = zebra_if_node_destroy}; @@ -102,9 +128,13 @@ static int if_zebra_new_hook(struct interface *ifp) struct zebra_if *zebra_if; zebra_if = XCALLOC(MTYPE_ZINFO, sizeof(struct zebra_if)); + zebra_if->ifp = ifp; zebra_if->multicast = IF_ZEBRA_MULTICAST_UNSPEC; zebra_if->shutdown = IF_ZEBRA_SHUTDOWN_OFF; + + zebra_if_nhg_dependents_init(zebra_if); + zebra_ptm_if_init(zebra_if); ifp->ptm_enable = zebra_ptm_get_enable_state(); @@ -125,13 +155,14 @@ static int if_zebra_new_hook(struct interface *ifp) rtadv->AdvLinkMTU = 0; rtadv->AdvReachableTime = 0; rtadv->AdvRetransTimer = 0; - rtadv->AdvCurHopLimit = 0; + rtadv->AdvCurHopLimit = RTADV_DEFAULT_HOPLIMIT; rtadv->AdvDefaultLifetime = -1; /* derive from MaxRtrAdvInterval */ rtadv->HomeAgentPreference = 0; rtadv->HomeAgentLifetime = -1; /* derive from AdvDefaultLifetime */ rtadv->AdvIntervalOption = 0; + rtadv->UseFastRexmit = true; rtadv->DefaultPreference = RTADV_PREF_MEDIUM; rtadv->AdvPrefixList = list_new(); @@ -160,6 +191,34 @@ static int if_zebra_new_hook(struct interface *ifp) return 0; } +static void if_nhg_dependents_check_valid(struct nhg_hash_entry *nhe) +{ + zebra_nhg_check_valid(nhe); + if (!CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_VALID)) + /* Assuming uninstalled as well here */ + UNSET_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED); +} + +static void if_down_nhg_dependents(const struct interface *ifp) +{ + struct nhg_connected *rb_node_dep = NULL; + struct zebra_if *zif = (struct zebra_if *)ifp->info; + + frr_each(nhg_connected_tree, &zif->nhg_dependents, rb_node_dep) + if_nhg_dependents_check_valid(rb_node_dep->nhe); +} + +static void if_nhg_dependents_release(const struct interface *ifp) +{ + struct nhg_connected *rb_node_dep = NULL; + struct zebra_if *zif = (struct zebra_if *)ifp->info; + + frr_each(nhg_connected_tree, &zif->nhg_dependents, rb_node_dep) { + rb_node_dep->nhe->ifp = NULL; /* Null it out */ + if_nhg_dependents_check_valid(rb_node_dep->nhe); + } +} + /* Called when interface is deleted. */ static int if_zebra_delete_hook(struct interface *ifp) { @@ -181,6 +240,13 @@ static int if_zebra_delete_hook(struct interface *ifp) list_delete(&rtadv->AdvDNSSLList); #endif /* HAVE_RTADV */ + zebra_evpn_if_cleanup(zebra_if); + + if_nhg_dependents_release(ifp); + zebra_if_nhg_dependents_free(zebra_if); + + XFREE(MTYPE_TMP, zebra_if->desc); + THREAD_OFF(zebra_if->speed_update); XFREE(MTYPE_ZINFO, zebra_if); @@ -254,8 +320,10 @@ struct interface *if_lookup_by_name_per_ns(struct zebra_ns *ns, for (rn = route_top(ns->if_table); rn; rn = route_next(rn)) { ifp = (struct interface *)rn->info; - if (ifp && strcmp(ifp->name, ifname) == 0) + if (ifp && strcmp(ifp->name, ifname) == 0) { + route_unlock_node(rn); return (ifp); + } } return NULL; @@ -321,8 +389,7 @@ int if_subnet_delete(struct interface *ifp, struct connected *ifc) rn = route_node_lookup(zebra_if->ipv4_subnets, &cp); if (!(rn && rn->info)) { flog_warn(EC_ZEBRA_REMOVE_ADDR_UNKNOWN_SUBNET, - "Trying to remove an address from an unknown subnet." - " (please report this bug)"); + "Trying to remove an address from an unknown subnet. (please report this bug)"); return -1; } route_unlock_node(rn); @@ -336,8 +403,7 @@ int if_subnet_delete(struct interface *ifp, struct connected *ifc) if (!listnode_lookup(addr_list, ifc)) { flog_warn( EC_ZEBRA_REMOVE_UNREGISTERED_ADDR, - "Trying to remove an address from a subnet where it is not" - " currently registered. (please report this bug)"); + "Trying to remove an address from a subnet where it is not currently registered. (please report this bug)"); return -1; } @@ -436,7 +502,7 @@ void if_flags_update(struct interface *ifp, uint64_t newflags) /* Wake up configured address if it is not in current kernel address. */ -static void if_addr_wakeup(struct interface *ifp) +void if_addr_wakeup(struct interface *ifp) { struct listnode *node, *nnode; struct connected *ifc; @@ -552,11 +618,13 @@ void if_add_update(struct interface *ifp) SET_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE); if (if_data->shutdown == IF_ZEBRA_SHUTDOWN_ON) { - if (IS_ZEBRA_DEBUG_KERNEL) + if (IS_ZEBRA_DEBUG_KERNEL) { zlog_debug( - "interface %s vrf %u index %d is shutdown. " - "Won't wake it up.", - ifp->name, ifp->vrf_id, ifp->ifindex); + "interface %s vrf %s(%u) index %d is shutdown. Won't wake it up.", + ifp->name, VRF_LOGNAME(zvrf->vrf), + ifp->vrf_id, ifp->ifindex); + } + return; } @@ -564,13 +632,15 @@ void if_add_update(struct interface *ifp) if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug( - "interface %s vrf %u index %d becomes active.", - ifp->name, ifp->vrf_id, ifp->ifindex); + "interface %s vrf %s(%u) index %d becomes active.", + ifp->name, VRF_LOGNAME(zvrf->vrf), ifp->vrf_id, + ifp->ifindex); } else { if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("interface %s vrf %u index %d is added.", - ifp->name, ifp->vrf_id, ifp->ifindex); + zlog_debug("interface %s vrf %s(%u) index %d is added.", + ifp->name, VRF_LOGNAME(zvrf->vrf), + ifp->vrf_id, ifp->ifindex); } } @@ -674,7 +744,7 @@ static void if_delete_connected(struct interface *ifp) ZEBRA_IFC_CONFIGURED)) { listnode_delete(ifp->connected, ifc); - connected_free(ifc); + connected_free(&ifc); } else last = node; } @@ -695,7 +765,7 @@ static void if_delete_connected(struct interface *ifp) last = node; else { listnode_delete(ifp->connected, ifc); - connected_free(ifc); + connected_free(&ifc); } } else { last = node; @@ -709,10 +779,12 @@ void if_delete_update(struct interface *ifp) struct zebra_if *zif; if (if_is_up(ifp)) { + struct vrf *vrf = vrf_lookup_by_id(ifp->vrf_id); + flog_err( EC_LIB_INTERFACE, - "interface %s vrf %u index %d is still up while being deleted.", - ifp->name, ifp->vrf_id, ifp->ifindex); + "interface %s vrf %s(%u) index %d is still up while being deleted.", + ifp->name, VRF_LOGNAME(vrf), ifp->vrf_id, ifp->ifindex); return; } @@ -722,9 +794,13 @@ void if_delete_update(struct interface *ifp) /* Mark interface as inactive */ UNSET_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE); - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("interface %s vrf %u index %d is now inactive.", - ifp->name, ifp->vrf_id, ifp->ifindex); + if (IS_ZEBRA_DEBUG_KERNEL) { + struct vrf *vrf = vrf_lookup_by_id(ifp->vrf_id); + + zlog_debug("interface %s vrf %s(%u) index %d is now inactive.", + ifp->name, VRF_LOGNAME(vrf), ifp->vrf_id, + ifp->ifindex); + } /* Delete connected routes from the kernel. */ if_delete_connected(ifp); @@ -759,6 +835,14 @@ void if_delete_update(struct interface *ifp) memset(&zif->l2info, 0, sizeof(union zebra_l2if_info)); memset(&zif->brslave_info, 0, sizeof(struct zebra_l2info_brslave)); + zebra_evpn_if_cleanup(zif); + } + + if (!ifp->configured) { + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("interface %s is being deleted from the system", + ifp->name); + if_delete(&ifp); } } @@ -792,15 +876,6 @@ void if_handle_vrf_change(struct interface *ifp, vrf_id_t vrf_id) /* Install connected routes (in new VRF). */ if (if_is_operative(ifp)) if_install_connected(ifp); - - /* Due to connected route change, schedule RIB processing for both old - * and new VRF. - */ - if (IS_ZEBRA_DEBUG_RIB_DETAILED) - zlog_debug("%u: IF %s VRF change, scheduling RIB processing", - ifp->vrf_id, ifp->name); - rib_update(old_vrf_id, RIB_UPDATE_IF_CHANGE); - rib_update(ifp->vrf_id, RIB_UPDATE_IF_CHANGE); } static void ipv6_ll_address_to_mac(struct in6_addr *address, uint8_t *mac) @@ -909,6 +984,47 @@ static void if_down_del_nbr_connected(struct interface *ifp) } } +void if_nhg_dependents_add(struct interface *ifp, struct nhg_hash_entry *nhe) +{ + if (ifp->info) { + struct zebra_if *zif = (struct zebra_if *)ifp->info; + + nhg_connected_tree_add_nhe(&zif->nhg_dependents, nhe); + } +} + +void if_nhg_dependents_del(struct interface *ifp, struct nhg_hash_entry *nhe) +{ + if (ifp->info) { + struct zebra_if *zif = (struct zebra_if *)ifp->info; + + nhg_connected_tree_del_nhe(&zif->nhg_dependents, nhe); + } +} + +unsigned int if_nhg_dependents_count(const struct interface *ifp) +{ + if (ifp->info) { + struct zebra_if *zif = (struct zebra_if *)ifp->info; + + return nhg_connected_tree_count(&zif->nhg_dependents); + } + + return 0; +} + + +bool if_nhg_dependents_is_empty(const struct interface *ifp) +{ + if (ifp->info) { + struct zebra_if *zif = (struct zebra_if *)ifp->info; + + return nhg_connected_tree_is_empty(&zif->nhg_dependents); + } + + return false; +} + /* Interface is up. */ void if_up(struct interface *ifp) { @@ -934,7 +1050,8 @@ void if_up(struct interface *ifp) #if defined(HAVE_RTADV) /* Enable fast tx of RA if enabled && RA interval is not in msecs */ if (zif->rtadv.AdvSendAdvertisements - && (zif->rtadv.MaxRtrAdvInterval >= 1000)) { + && (zif->rtadv.MaxRtrAdvInterval >= 1000) + && zif->rtadv.UseFastRexmit) { zif->rtadv.inFastRexmit = 1; zif->rtadv.NumFastReXmitsRemain = RTADV_NUM_FAST_REXMITS; } @@ -943,11 +1060,6 @@ void if_up(struct interface *ifp) /* Install connected routes to the kernel. */ if_install_connected(ifp); - if (IS_ZEBRA_DEBUG_RIB_DETAILED) - zlog_debug("%u: IF %s up, scheduling RIB processing", - ifp->vrf_id, ifp->name); - rib_update(ifp->vrf_id, RIB_UPDATE_IF_CHANGE); - /* Handle interface up for specific types for EVPN. Non-VxLAN interfaces * are checked to see if (remote) neighbor entries need to be installed * on them for ARP suppression. @@ -962,7 +1074,11 @@ void if_up(struct interface *ifp) zif->link_ifindex); if (link_if) zebra_vxlan_svi_up(ifp, link_if); - } + } else if (IS_ZEBRA_IF_MACVLAN(ifp)) + zebra_vxlan_macvlan_up(ifp); + + if (zif->es_info.es) + zebra_evpn_es_if_oper_state_change(zif, true /*up*/); } /* Interface goes down. We have to manage different behavior of based @@ -977,6 +1093,8 @@ void if_down(struct interface *ifp) zif->down_count++; quagga_timestamp(2, zif->down_last, sizeof(zif->down_last)); + if_down_nhg_dependents(ifp); + /* Handle interface down for specific types for EVPN. Non-VxLAN * interfaces * are checked to see if (remote) neighbor entries need to be purged @@ -992,8 +1110,11 @@ void if_down(struct interface *ifp) zif->link_ifindex); if (link_if) zebra_vxlan_svi_down(ifp, link_if); - } + } else if (IS_ZEBRA_IF_MACVLAN(ifp)) + zebra_vxlan_macvlan_down(ifp); + if (zif->es_info.es) + zebra_evpn_es_if_oper_state_change(zif, false /*up*/); /* Notify to the protocol daemons. */ zebra_interface_down_update(ifp); @@ -1001,11 +1122,6 @@ void if_down(struct interface *ifp) /* Uninstall connected routes from the kernel. */ if_uninstall_connected(ifp); - if (IS_ZEBRA_DEBUG_RIB_DETAILED) - zlog_debug("%u: IF %s down, scheduling RIB processing", - ifp->vrf_id, ifp->name); - rib_update(ifp->vrf_id, RIB_UPDATE_IF_CHANGE); - if_nbr_ipv6ll_to_ipv4ll_neigh_del_all(ifp); /* Delete all neighbor addresses learnt through IPv6 RA */ @@ -1062,7 +1178,14 @@ void zebra_if_update_all_links(void) } } - +void zebra_if_set_protodown(struct interface *ifp, bool down) +{ +#ifdef HAVE_NETLINK + netlink_protodown(ifp, down); +#else + zlog_warn("Protodown is not supported on this platform"); +#endif +} /* Output prefix string to vty. */ static int prefix_vty_out(struct vty *vty, struct prefix *p) @@ -1086,12 +1209,10 @@ static void connected_dump_vty(struct vty *vty, struct connected *connected) vty_out(vty, "/%d", p->prefixlen); /* If there is destination address, print it. */ - if (connected->destination) { - vty_out(vty, - (CONNECTED_PEER(connected) ? " peer " : " broadcast ")); + if (CONNECTED_PEER(connected) && connected->destination) { + vty_out(vty, " peer "); prefix_vty_out(vty, connected->destination); - if (CONNECTED_PEER(connected)) - vty_out(vty, "/%d", connected->destination->prefixlen); + vty_out(vty, "/%d", connected->destination->prefixlen); } if (CHECK_FLAG(connected->flags, ZEBRA_IFA_SECONDARY)) @@ -1121,32 +1242,43 @@ static void nbr_connected_dump_vty(struct vty *vty, vty_out(vty, "\n"); } +static const char *zebra_zifslavetype_2str(zebra_slave_iftype_t zif_slave_type) +{ + switch (zif_slave_type) { + case ZEBRA_IF_SLAVE_BRIDGE: + return "Bridge"; + case ZEBRA_IF_SLAVE_VRF: + return "Vrf"; + case ZEBRA_IF_SLAVE_BOND: + return "Bond"; + case ZEBRA_IF_SLAVE_OTHER: + return "Other"; + case ZEBRA_IF_SLAVE_NONE: + return "None"; + } + return "None"; +} + static const char *zebra_ziftype_2str(zebra_iftype_t zif_type) { switch (zif_type) { case ZEBRA_IF_OTHER: return "Other"; - break; case ZEBRA_IF_BRIDGE: return "Bridge"; - break; case ZEBRA_IF_VLAN: return "Vlan"; - break; case ZEBRA_IF_VXLAN: return "Vxlan"; - break; case ZEBRA_IF_VRF: return "VRF"; - break; case ZEBRA_IF_VETH: return "VETH"; - break; case ZEBRA_IF_BOND: return "bond"; @@ -1159,7 +1291,6 @@ static const char *zebra_ziftype_2str(zebra_iftype_t zif_type) default: return "Unknown"; - break; } } @@ -1303,6 +1434,9 @@ static void if_dump_vty(struct vty *vty, struct interface *ifp) if (ifp->desc) vty_out(vty, " Description: %s\n", ifp->desc); + if (zebra_if->desc) + vty_out(vty, " OS Description: %s\n", zebra_if->desc); + if (ifp->ifindex == IFINDEX_INTERNAL) { vty_out(vty, " pseudo interface\n"); return; @@ -1352,6 +1486,9 @@ static void if_dump_vty(struct vty *vty, struct interface *ifp) vty_out(vty, " Interface Type %s\n", zebra_ziftype_2str(zebra_if->zif_type)); + vty_out(vty, " Interface Slave Type %s\n", + zebra_zifslavetype_2str(zebra_if->zif_slave_type)); + if (IS_ZEBRA_IF_BRIDGE(ifp)) { struct zebra_l2info_bridge *bridge_info; @@ -1377,6 +1514,17 @@ static void if_dump_vty(struct vty *vty, struct interface *ifp) if (vxlan_info->mcast_grp.s_addr != INADDR_ANY) vty_out(vty, " Mcast Group %s", inet_ntoa(vxlan_info->mcast_grp)); + if (vxlan_info->ifindex_link && + (vxlan_info->link_nsid != NS_UNKNOWN)) { + struct interface *ifp; + + ifp = if_lookup_by_index_per_ns( + zebra_ns_lookup(vxlan_info->link_nsid), + vxlan_info->ifindex_link); + vty_out(vty, " Link Interface %s", + ifp == NULL ? "Unknown" : + ifp->name); + } vty_out(vty, "\n"); } @@ -1384,26 +1532,37 @@ static void if_dump_vty(struct vty *vty, struct interface *ifp) struct zebra_l2info_brslave *br_slave; br_slave = &zebra_if->brslave_info; - if (br_slave->bridge_ifindex != IFINDEX_INTERNAL) - vty_out(vty, " Master (bridge) ifindex %u\n", - br_slave->bridge_ifindex); + if (br_slave->bridge_ifindex != IFINDEX_INTERNAL) { + if (br_slave->br_if) + vty_out(vty, " Master interface: %s\n", + br_slave->br_if->name); + else + vty_out(vty, " Master ifindex: %u\n", + br_slave->bridge_ifindex); + } } if (IS_ZEBRA_IF_BOND_SLAVE(ifp)) { struct zebra_l2info_bondslave *bond_slave; bond_slave = &zebra_if->bondslave_info; - if (bond_slave->bond_ifindex != IFINDEX_INTERNAL) - vty_out(vty, " Master (bond) ifindex %u\n", - bond_slave->bond_ifindex); + if (bond_slave->bond_ifindex != IFINDEX_INTERNAL) { + if (bond_slave->bond_if) + vty_out(vty, " Master interface: %s\n", + bond_slave->bond_if->name); + else + vty_out(vty, " Master ifindex: %u\n", + bond_slave->bond_ifindex); + } } + zebra_evpn_if_es_print(vty, zebra_if); + if (zebra_if->link_ifindex != IFINDEX_INTERNAL) { - vty_out(vty, " Link ifindex %u", zebra_if->link_ifindex); if (zebra_if->link) - vty_out(vty, "(%s)\n", zebra_if->link->name); + vty_out(vty, " Parent interface: %s\n", zebra_if->link->name); else - vty_out(vty, "(Unknown)\n"); + vty_out(vty, " Parent ifindex: %d\n", zebra_if->link_ifindex); } if (HAS_LINK_PARAMS(ifp)) { @@ -1474,14 +1633,12 @@ static void if_dump_vty(struct vty *vty, struct interface *ifp) #ifdef HAVE_PROC_NET_DEV /* Statistics print out using proc file system. */ vty_out(vty, - " %lu input packets (%lu multicast), %lu bytes, " - "%lu dropped\n", + " %lu input packets (%lu multicast), %lu bytes, %lu dropped\n", ifp->stats.rx_packets, ifp->stats.rx_multicast, ifp->stats.rx_bytes, ifp->stats.rx_dropped); vty_out(vty, - " %lu input errors, %lu length, %lu overrun," - " %lu CRC, %lu frame\n", + " %lu input errors, %lu length, %lu overrun, %lu CRC, %lu frame\n", ifp->stats.rx_errors, ifp->stats.rx_length_errors, ifp->stats.rx_over_errors, ifp->stats.rx_crc_errors, ifp->stats.rx_frame_errors); @@ -1494,8 +1651,7 @@ static void if_dump_vty(struct vty *vty, struct interface *ifp) ifp->stats.tx_dropped); vty_out(vty, - " %lu output errors, %lu aborted, %lu carrier," - " %lu fifo, %lu heartbeat\n", + " %lu output errors, %lu aborted, %lu carrier, %lu fifo, %lu heartbeat\n", ifp->stats.tx_errors, ifp->stats.tx_aborted_errors, ifp->stats.tx_carrier_errors, ifp->stats.tx_fifo_errors, ifp->stats.tx_heartbeat_errors); @@ -1505,11 +1661,9 @@ static void if_dump_vty(struct vty *vty, struct interface *ifp) #endif /* HAVE_PROC_NET_DEV */ #ifdef HAVE_NET_RT_IFLIST -#if defined(__bsdi__) || defined(__NetBSD__) /* Statistics print out using sysctl (). */ vty_out(vty, - " input packets %llu, bytes %llu, dropped %llu," - " multicast packets %llu\n", + " input packets %llu, bytes %llu, dropped %llu, multicast packets %llu\n", (unsigned long long)ifp->stats.ifi_ipackets, (unsigned long long)ifp->stats.ifi_ibytes, (unsigned long long)ifp->stats.ifi_iqdrops, @@ -1519,8 +1673,7 @@ static void if_dump_vty(struct vty *vty, struct interface *ifp) (unsigned long long)ifp->stats.ifi_ierrors); vty_out(vty, - " output packets %llu, bytes %llu," - " multicast packets %llu\n", + " output packets %llu, bytes %llu, multicast packets %llu\n", (unsigned long long)ifp->stats.ifi_opackets, (unsigned long long)ifp->stats.ifi_obytes, (unsigned long long)ifp->stats.ifi_omcasts); @@ -1530,25 +1683,6 @@ static void if_dump_vty(struct vty *vty, struct interface *ifp) vty_out(vty, " collisions %llu\n", (unsigned long long)ifp->stats.ifi_collisions); -#else - /* Statistics print out using sysctl (). */ - vty_out(vty, - " input packets %lu, bytes %lu, dropped %lu," - " multicast packets %lu\n", - ifp->stats.ifi_ipackets, ifp->stats.ifi_ibytes, - ifp->stats.ifi_iqdrops, ifp->stats.ifi_imcasts); - - vty_out(vty, " input errors %lu\n", ifp->stats.ifi_ierrors); - - vty_out(vty, - " output packets %lu, bytes %lu, multicast packets %lu\n", - ifp->stats.ifi_opackets, ifp->stats.ifi_obytes, - ifp->stats.ifi_omcasts); - - vty_out(vty, " output errors %lu\n", ifp->stats.ifi_oerrors); - - vty_out(vty, " collisions %lu\n", ifp->stats.ifi_collisions); -#endif /* __bsdi__ || __NetBSD__ */ #endif /* HAVE_NET_RT_IFLIST */ } @@ -1564,14 +1698,21 @@ static void interface_update_stats(void) #endif /* HAVE_NET_RT_IFLIST */ } -struct cmd_node interface_node = {INTERFACE_NODE, "%s(config-if)# ", 1}; +static int if_config_write(struct vty *vty); +struct cmd_node interface_node = { + .name = "interface", + .node = INTERFACE_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-if)# ", + .config_write = if_config_write, +}; #ifndef VTYSH_EXTRACT_PL #include "zebra/interface_clippy.c" #endif /* Show all interfaces to vty. */ DEFPY(show_interface, show_interface_cmd, - "show interface [vrf NAME$name] [brief$brief]", + "show interface vrf NAME$vrf_name [brief$brief]", SHOW_STR "Interface status and configuration\n" VRF_CMD_HELP_STR @@ -1579,15 +1720,15 @@ DEFPY(show_interface, show_interface_cmd, { struct vrf *vrf; struct interface *ifp; - vrf_id_t vrf_id = VRF_DEFAULT; interface_update_stats(); - if (name) - VRF_GET_ID(vrf_id, name, false); + vrf = vrf_lookup_by_name(vrf_name); + if (!vrf) { + vty_out(vty, "%% VRF %s not found\n", vrf_name); + return CMD_WARNING; + } - /* All interface print. */ - vrf = vrf_lookup_by_id(vrf_id); if (brief) { ifs_dump_brief_vty(vty, vrf); } else { @@ -1601,12 +1742,13 @@ DEFPY(show_interface, show_interface_cmd, /* Show all interfaces to vty. */ -DEFUN (show_interface_vrf_all, +DEFPY (show_interface_vrf_all, show_interface_vrf_all_cmd, - "show interface vrf all", + "show interface [vrf all] [brief$brief]", SHOW_STR "Interface status and configuration\n" - VRF_ALL_CMD_HELP_STR) + VRF_ALL_CMD_HELP_STR + "Interface status and configuration summary\n") { struct vrf *vrf; struct interface *ifp; @@ -1614,9 +1756,14 @@ DEFUN (show_interface_vrf_all, interface_update_stats(); /* All interface print. */ - RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) - FOR_ALL_INTERFACES (vrf, ifp) - if_dump_vty(vty, ifp); + RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { + if (brief) { + ifs_dump_brief_vty(vty, vrf); + } else { + FOR_ALL_INTERFACES (vrf, ifp) + if_dump_vty(vty, ifp); + } + } return CMD_SUCCESS; } @@ -1634,14 +1781,17 @@ DEFUN (show_interface_name_vrf, int idx_ifname = 2; int idx_name = 4; struct interface *ifp; - vrf_id_t vrf_id; + struct vrf *vrf; interface_update_stats(); - VRF_GET_ID(vrf_id, argv[idx_name]->arg, false); + vrf = vrf_lookup_by_name(argv[idx_name]->arg); + if (!vrf) { + vty_out(vty, "%% VRF %s not found\n", argv[idx_name]->arg); + return CMD_WARNING; + } - /* Specified interface print. */ - ifp = if_lookup_by_name(argv[idx_ifname]->arg, vrf_id); + ifp = if_lookup_by_name_vrf(argv[idx_ifname]->arg, vrf); if (ifp == NULL) { vty_out(vty, "%% Can't find interface %s\n", argv[idx_ifname]->arg); @@ -1662,40 +1812,32 @@ DEFUN (show_interface_name_vrf_all, VRF_ALL_CMD_HELP_STR) { int idx_ifname = 2; - struct vrf *vrf; struct interface *ifp; - int found = 0; interface_update_stats(); - /* All interface print. */ - RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { - /* Specified interface print. */ - ifp = if_lookup_by_name(argv[idx_ifname]->arg, vrf->vrf_id); - if (ifp) { - if_dump_vty(vty, ifp); - found++; - } - } - - if (!found) { + ifp = if_lookup_by_name_all_vrf(argv[idx_ifname]->arg); + if (ifp == NULL) { vty_out(vty, "%% Can't find interface %s\n", argv[idx_ifname]->arg); return CMD_WARNING; } + if_dump_vty(vty, ifp); return CMD_SUCCESS; } - -static void if_show_description(struct vty *vty, vrf_id_t vrf_id) +static void if_show_description(struct vty *vty, struct vrf *vrf) { - struct vrf *vrf = vrf_lookup_by_id(vrf_id); struct interface *ifp; vty_out(vty, "Interface Status Protocol Description\n"); FOR_ALL_INTERFACES (vrf, ifp) { int len; + struct zebra_if *zif; + bool intf_desc; + + intf_desc = false; len = vty_out(vty, "%s", ifp->name); vty_out(vty, "%*s", (16 - len), " "); @@ -1715,26 +1857,40 @@ static void if_show_description(struct vty *vty, vrf_id_t vrf_id) vty_out(vty, "down down "); } - if (ifp->desc) + if (ifp->desc) { + intf_desc = true; vty_out(vty, "%s", ifp->desc); + } + zif = ifp->info; + if (zif && zif->desc) { + vty_out(vty, "%s%s", + intf_desc + ? "\n " + : "", + zif->desc); + } + vty_out(vty, "\n"); } } DEFUN (show_interface_desc, show_interface_desc_cmd, - "show interface description [vrf NAME]", + "show interface description vrf NAME", SHOW_STR "Interface status and configuration\n" "Interface description\n" VRF_CMD_HELP_STR) { - vrf_id_t vrf_id = VRF_DEFAULT; + struct vrf *vrf; - if (argc > 3) - VRF_GET_ID(vrf_id, argv[4]->arg, false); + vrf = vrf_lookup_by_name(argv[4]->arg); + if (!vrf) { + vty_out(vty, "%% VRF %s not found\n", argv[4]->arg); + return CMD_WARNING; + } - if_show_description(vty, vrf_id); + if_show_description(vty, vrf); return CMD_SUCCESS; } @@ -1742,7 +1898,7 @@ DEFUN (show_interface_desc, DEFUN (show_interface_desc_vrf_all, show_interface_desc_vrf_all_cmd, - "show interface description vrf all", + "show interface description [vrf all]", SHOW_STR "Interface status and configuration\n" "Interface description\n" @@ -1752,13 +1908,32 @@ DEFUN (show_interface_desc_vrf_all, RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) if (!RB_EMPTY(if_name_head, &vrf->ifaces_by_name)) { - vty_out(vty, "\n\tVRF %u\n\n", vrf->vrf_id); - if_show_description(vty, vrf->vrf_id); + vty_out(vty, "\n\tVRF %s(%u)\n\n", VRF_LOGNAME(vrf), + vrf->vrf_id); + if_show_description(vty, vrf); } return CMD_SUCCESS; } +int if_multicast_set(struct interface *ifp) +{ + struct zebra_if *if_data; + + if (CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE)) { + if (if_set_flags(ifp, IFF_MULTICAST) < 0) { + zlog_debug("Can't set multicast flag on interface %s", + ifp->name); + return -1; + } + if_refresh(ifp); + } + if_data = ifp->info; + if_data->multicast = IF_ZEBRA_MULTICAST_ON; + + return 0; +} + DEFUN (multicast, multicast_cmd, "multicast", @@ -1782,6 +1957,24 @@ DEFUN (multicast, return CMD_SUCCESS; } +int if_multicast_unset(struct interface *ifp) +{ + struct zebra_if *if_data; + + if (CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE)) { + if (if_unset_flags(ifp, IFF_MULTICAST) < 0) { + zlog_debug("Can't unset multicast flag on interface %s", + ifp->name); + return -1; + } + if_refresh(ifp); + } + if_data = ifp->info; + if_data->multicast = IF_ZEBRA_MULTICAST_OFF; + + return 0; +} + DEFUN (no_multicast, no_multicast_cmd, "no multicast", @@ -1806,23 +1999,35 @@ DEFUN (no_multicast, return CMD_SUCCESS; } -DEFUN (linkdetect, - linkdetect_cmd, - "link-detect", - "Enable link detection on interface\n") +int if_linkdetect(struct interface *ifp, bool detect) { - VTY_DECLVAR_CONTEXT(interface, ifp); int if_was_operative; if_was_operative = if_is_no_ptm_operative(ifp); - SET_FLAG(ifp->status, ZEBRA_INTERFACE_LINKDETECTION); + if (detect) { + SET_FLAG(ifp->status, ZEBRA_INTERFACE_LINKDETECTION); - /* When linkdetection is enabled, if might come down */ - if (!if_is_no_ptm_operative(ifp) && if_was_operative) - if_down(ifp); + /* When linkdetection is enabled, if might come down */ + if (!if_is_no_ptm_operative(ifp) && if_was_operative) + if_down(ifp); + } else { + UNSET_FLAG(ifp->status, ZEBRA_INTERFACE_LINKDETECTION); + /* Interface may come up after disabling link detection */ + if (if_is_operative(ifp) && !if_was_operative) + if_up(ifp); + } /* FIXME: Will defer status change forwarding if interface does not come down! */ + return 0; +} + +DEFUN(linkdetect, linkdetect_cmd, "link-detect", + "Enable link detection on interface\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + + if_linkdetect(ifp, true); return CMD_SUCCESS; } @@ -1835,18 +2040,29 @@ DEFUN (no_linkdetect, "Disable link detection on interface\n") { VTY_DECLVAR_CONTEXT(interface, ifp); - int if_was_operative; - if_was_operative = if_is_no_ptm_operative(ifp); - UNSET_FLAG(ifp->status, ZEBRA_INTERFACE_LINKDETECTION); + if_linkdetect(ifp, false); - /* Interface may come up after disabling link detection */ - if (if_is_operative(ifp) && !if_was_operative) - if_up(ifp); + return CMD_SUCCESS; +} - /* FIXME: see linkdetect_cmd */ +int if_shutdown(struct interface *ifp) +{ + struct zebra_if *if_data; - return CMD_SUCCESS; + if (ifp->ifindex != IFINDEX_INTERNAL) { + /* send RA lifetime of 0 before stopping. rfc4861/6.2.5 */ + rtadv_stop_ra(ifp); + if (if_unset_flags(ifp, IFF_UP) < 0) { + zlog_debug("Can't shutdown interface %s", ifp->name); + return -1; + } + if_refresh(ifp); + } + if_data = ifp->info; + if_data->shutdown = IF_ZEBRA_SHUTDOWN_ON; + + return 0; } DEFUN (shutdown_if, @@ -1859,6 +2075,8 @@ DEFUN (shutdown_if, struct zebra_if *if_data; if (ifp->ifindex != IFINDEX_INTERNAL) { + /* send RA lifetime of 0 before stopping. rfc4861/6.2.5 */ + rtadv_stop_ra(ifp); ret = if_unset_flags(ifp, IFF_UP); if (ret < 0) { vty_out(vty, "Can't shutdown interface\n"); @@ -1872,6 +2090,30 @@ DEFUN (shutdown_if, return CMD_SUCCESS; } +int if_no_shutdown(struct interface *ifp) +{ + struct zebra_if *if_data; + + if (ifp->ifindex != IFINDEX_INTERNAL) { + if (if_set_flags(ifp, IFF_UP | IFF_RUNNING) < 0) { + zlog_debug("Can't up interface %s", ifp->name); + return -1; + } + if_refresh(ifp); + + /* Some addresses (in particular, IPv6 addresses on Linux) get + * removed when the interface goes down. They need to be + * readded. + */ + if_addr_wakeup(ifp); + } + + if_data = ifp->info; + if_data->shutdown = IF_ZEBRA_SHUTDOWN_OFF; + + return 0; +} + DEFUN (no_shutdown_if, no_shutdown_if_cmd, "no shutdown", @@ -1950,7 +2192,10 @@ DEFUN (no_bandwidth_if, struct cmd_node link_params_node = { - LINK_PARAMS_NODE, "%s(config-link-params)# ", 1, + .name = "link-params", + .node = LINK_PARAMS_NODE, + .parent_node = INTERFACE_NODE, + .prompt = "%s(config-link-params)# ", }; static void link_param_cmd_set_uint32(struct interface *ifp, uint32_t *field, @@ -2028,13 +2273,13 @@ DEFUN (link_params_enable, /* This command could be issue at startup, when activate MPLS TE */ /* on a new interface or after a ON / OFF / ON toggle */ /* In all case, TE parameters are reset to their default factory */ - if (IS_ZEBRA_DEBUG_EVENT) + if (IS_ZEBRA_DEBUG_EVENT || IS_ZEBRA_DEBUG_MPLS) zlog_debug( "Link-params: enable TE link parameters on interface %s", ifp->name); if (!if_link_params_get(ifp)) { - if (IS_ZEBRA_DEBUG_EVENT) + if (IS_ZEBRA_DEBUG_EVENT || IS_ZEBRA_DEBUG_MPLS) zlog_debug( "Link-params: failed to init TE link parameters %s", ifp->name); @@ -2057,8 +2302,9 @@ DEFUN (no_link_params_enable, { VTY_DECLVAR_CONTEXT(interface, ifp); - zlog_debug("MPLS-TE: disable TE link parameters on interface %s", - ifp->name); + if (IS_ZEBRA_DEBUG_EVENT || IS_ZEBRA_DEBUG_MPLS) + zlog_debug("MPLS-TE: disable TE link parameters on interface %s", + ifp->name); if_link_params_free(ifp); @@ -2623,6 +2869,79 @@ DEFUN (no_link_params_use_bw, return CMD_SUCCESS; } +int if_ip_address_install(struct interface *ifp, struct prefix *prefix, + const char *label, struct prefix *pp) +{ + struct zebra_if *if_data; + struct prefix_ipv4 lp; + struct prefix_ipv4 *p; + struct connected *ifc; + enum zebra_dplane_result dplane_res; + + if_data = ifp->info; + + lp.family = prefix->family; + lp.prefix = prefix->u.prefix4; + lp.prefixlen = prefix->prefixlen; + apply_mask_ipv4(&lp); + + ifc = connected_check_ptp(ifp, &lp, pp ? pp : NULL); + if (!ifc) { + ifc = connected_new(); + ifc->ifp = ifp; + + /* Address. */ + p = prefix_ipv4_new(); + *p = lp; + ifc->address = (struct prefix *)p; + + if (pp) { + SET_FLAG(ifc->flags, ZEBRA_IFA_PEER); + p = prefix_ipv4_new(); + *p = *(struct prefix_ipv4 *)pp; + ifc->destination = (struct prefix *)p; + } + + /* Label. */ + if (label) + ifc->label = XSTRDUP(MTYPE_CONNECTED_LABEL, label); + + /* Add to linked list. */ + listnode_add(ifp->connected, ifc); + } + + /* This address is configured from zebra. */ + if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_CONFIGURED)) + SET_FLAG(ifc->conf, ZEBRA_IFC_CONFIGURED); + + /* In case of this route need to install kernel. */ + if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_QUEUED) + && CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE) + && !(if_data && if_data->shutdown == IF_ZEBRA_SHUTDOWN_ON)) { + /* Some system need to up the interface to set IP address. */ + if (!if_is_up(ifp)) { + if_set_flags(ifp, IFF_UP | IFF_RUNNING); + if_refresh(ifp); + } + + dplane_res = dplane_intf_addr_set(ifp, ifc); + if (dplane_res == ZEBRA_DPLANE_REQUEST_FAILURE) { + zlog_debug( + "dplane can't set interface IP address: %s.\n", + dplane_res2str(dplane_res)); + return NB_ERR; + } + + SET_FLAG(ifc->conf, ZEBRA_IFC_QUEUED); + /* The address will be advertised to zebra clients when the + * notification + * from the kernel has been received. + * It will also be added to the subnet chain list, then. */ + } + + return 0; +} + static int ip_address_install(struct vty *vty, struct interface *ifp, const char *addr_str, const char *peer_str, const char *label) @@ -2676,12 +2995,6 @@ static int ip_address_install(struct vty *vty, struct interface *ifp, p = prefix_ipv4_new(); *p = pp; ifc->destination = (struct prefix *)p; - } else if (p->prefixlen <= IPV4_MAX_PREFIXLEN - 2) { - p = prefix_ipv4_new(); - *p = lp; - p->prefix.s_addr = ipv4_broadcast_addr(p->prefix.s_addr, - p->prefixlen); - ifc->destination = (struct prefix *)p; } /* Label. */ @@ -2723,6 +3036,51 @@ static int ip_address_install(struct vty *vty, struct interface *ifp, return CMD_SUCCESS; } +int if_ip_address_uinstall(struct interface *ifp, struct prefix *prefix) +{ + struct connected *ifc = NULL; + enum zebra_dplane_result dplane_res; + + if (prefix->family == AF_INET) { + /* Check current interface address. */ + ifc = connected_check_ptp(ifp, prefix, NULL); + if (!ifc) { + zlog_debug("interface %s Can't find address\n", + ifp->name); + return -1; + } + + } else if (prefix->family == AF_INET6) { + /* Check current interface address. */ + ifc = connected_check(ifp, prefix); + } + + if (!ifc) { + zlog_debug("interface %s Can't find address\n", ifp->name); + return -1; + } + UNSET_FLAG(ifc->conf, ZEBRA_IFC_CONFIGURED); + + /* This is not real address or interface is not active. */ + if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_QUEUED) + || !CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE)) { + listnode_delete(ifp->connected, ifc); + connected_free(&ifc); + return CMD_WARNING_CONFIG_FAILED; + } + + /* This is real route. */ + dplane_res = dplane_intf_addr_unset(ifp, ifc); + if (dplane_res == ZEBRA_DPLANE_REQUEST_FAILURE) { + zlog_debug("Can't unset interface IP address: %s.\n", + dplane_res2str(dplane_res)); + return -1; + } + UNSET_FLAG(ifc->conf, ZEBRA_IFC_QUEUED); + + return 0; +} + static int ip_address_uninstall(struct vty *vty, struct interface *ifp, const char *addr_str, const char *peer_str, const char *label) @@ -2770,7 +3128,7 @@ static int ip_address_uninstall(struct vty *vty, struct interface *ifp, if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_QUEUED) || !CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE)) { listnode_delete(ifp->connected, ifc); - connected_free(ifc); + connected_free(&ifc); return CMD_WARNING_CONFIG_FAILED; } @@ -2876,6 +3234,71 @@ DEFUN (no_ip_address_label, } #endif /* HAVE_NETLINK */ +int if_ipv6_address_install(struct interface *ifp, struct prefix *prefix, + const char *label) +{ + struct zebra_if *if_data; + struct prefix_ipv6 cp; + struct connected *ifc; + struct prefix_ipv6 *p; + enum zebra_dplane_result dplane_res; + + if_data = ifp->info; + + cp.family = prefix->family; + cp.prefixlen = prefix->prefixlen; + cp.prefix = prefix->u.prefix6; + apply_mask_ipv6(&cp); + + ifc = connected_check(ifp, (struct prefix *)&cp); + if (!ifc) { + ifc = connected_new(); + ifc->ifp = ifp; + + /* Address. */ + p = prefix_ipv6_new(); + *p = cp; + ifc->address = (struct prefix *)p; + + /* Label. */ + if (label) + ifc->label = XSTRDUP(MTYPE_CONNECTED_LABEL, label); + + /* Add to linked list. */ + listnode_add(ifp->connected, ifc); + } + + /* This address is configured from zebra. */ + if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_CONFIGURED)) + SET_FLAG(ifc->conf, ZEBRA_IFC_CONFIGURED); + + /* In case of this route need to install kernel. */ + if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_QUEUED) + && CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE) + && !(if_data && if_data->shutdown == IF_ZEBRA_SHUTDOWN_ON)) { + /* Some system need to up the interface to set IP address. */ + if (!if_is_up(ifp)) { + if_set_flags(ifp, IFF_UP | IFF_RUNNING); + if_refresh(ifp); + } + + dplane_res = dplane_intf_addr_set(ifp, ifc); + if (dplane_res == ZEBRA_DPLANE_REQUEST_FAILURE) { + zlog_debug( + "dplane can't set interface IP address: %s.\n", + dplane_res2str(dplane_res)); + return NB_ERR; + } + + SET_FLAG(ifc->conf, ZEBRA_IFC_QUEUED); + /* The address will be advertised to zebra clients when the + * notification + * from the kernel has been received. */ + } + + return 0; +} + static int ipv6_address_install(struct vty *vty, struct interface *ifp, const char *addr_str, const char *peer_str, const char *label) @@ -2995,7 +3418,7 @@ static int ipv6_address_uninstall(struct vty *vty, struct interface *ifp, if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_QUEUED) || !CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE)) { listnode_delete(ifp->connected, ifc); - connected_free(ifc); + connected_free(&ifc); return CMD_WARNING_CONFIG_FAILED; } @@ -3176,7 +3599,7 @@ static int if_config_write(struct vty *vty) } hook_call(zebra_if_config_wr, vty, ifp); - + zebra_evpn_mh_if_write(vty, ifp); link_params_config_write(vty, ifp); vty_endframe(vty, "!\n"); @@ -3192,9 +3615,14 @@ void zebra_if_init(void) hook_register_prio(if_del, 0, if_zebra_delete_hook); /* Install configuration write function. */ - install_node(&interface_node, if_config_write); - install_node(&link_params_node, NULL); + install_node(&interface_node); + install_node(&link_params_node); if_cmd_init(); + /* + * This is *intentionally* setting this to NULL, signaling + * that interface creation for zebra acts differently + */ + if_zapi_callbacks(NULL, NULL, NULL, NULL); install_element(VIEW_NODE, &show_interface_cmd); install_element(VIEW_NODE, &show_interface_vrf_all_cmd); @@ -3247,4 +3675,7 @@ void zebra_if_init(void) install_element(LINK_PARAMS_NODE, &link_params_use_bw_cmd); install_element(LINK_PARAMS_NODE, &no_link_params_use_bw_cmd); install_element(LINK_PARAMS_NODE, &exit_link_params_cmd); + + /* setup EVPN MH elements */ + zebra_evpn_interface_init(); } diff --git a/zebra/interface.h b/zebra/interface.h index ce404e8253..1a8e3caed5 100644 --- a/zebra/interface.h +++ b/zebra/interface.h @@ -25,8 +25,10 @@ #include "redistribute.h" #include "vrf.h" #include "hook.h" +#include "bitfield.h" #include "zebra/zebra_l2.h" +#include "zebra/zebra_nhg_private.h" #ifdef __cplusplus extern "C" { @@ -41,12 +43,14 @@ extern "C" { #define IF_ZEBRA_SHUTDOWN_OFF 0 #define IF_ZEBRA_SHUTDOWN_ON 1 +#define IF_VLAN_BITMAP_MAX 4096 + #if defined(HAVE_RTADV) /* Router advertisement parameter. From RFC4861, RFC6275 and RFC4191. */ struct rtadvconf { /* A flag indicating whether or not the router sends periodic Router Advertisements and responds to Router Solicitations. - Default: FALSE */ + Default: false */ int AdvSendAdvertisements; /* The maximum time allowed between sending unsolicited multicast @@ -70,19 +74,19 @@ struct rtadvconf { /* Unsolicited Router Advertisements' interval timer. */ int AdvIntervalTimer; - /* The TRUE/FALSE value to be placed in the "Managed address + /* The true/false value to be placed in the "Managed address configuration" flag field in the Router Advertisement. See [ADDRCONF]. - Default: FALSE */ + Default: false */ int AdvManagedFlag; - /* The TRUE/FALSE value to be placed in the "Other stateful + /* The true/false value to be placed in the "Other stateful configuration" flag field in the Router Advertisement. See [ADDRCONF]. - Default: FALSE */ + Default: false */ int AdvOtherConfigFlag; /* The value to be placed in MTU options sent by the router. A @@ -116,6 +120,7 @@ struct rtadvconf { Default: The value specified in the "Assigned Numbers" RFC [ASSIGNED] that was in effect at the time of implementation. */ int AdvCurHopLimit; +#define RTADV_DEFAULT_HOPLIMIT 64 /* 64 hops */ /* The value to be placed in the Router Lifetime field of Router Advertisements sent from the interface, in seconds. MUST be @@ -136,10 +141,10 @@ struct rtadvconf { included in the list of advertised prefixes. */ struct list *AdvPrefixList; - /* The TRUE/FALSE value to be placed in the "Home agent" + /* The true/false value to be placed in the "Home agent" flag field in the Router Advertisement. See [RFC6275 7.1]. - Default: FALSE */ + Default: false */ int AdvHomeAgentFlag; #ifndef ND_RA_FLAG_HOME_AGENT #define ND_RA_FLAG_HOME_AGENT 0x20 @@ -159,10 +164,10 @@ struct rtadvconf { int HomeAgentLifetime; #define RTADV_MAX_HALIFETIME 65520 /* 18.2 hours */ - /* The TRUE/FALSE value to insert or not an Advertisement Interval + /* The true/false value to insert or not an Advertisement Interval option. See [RFC 6275 7.3] - Default: FALSE */ + Default: false */ int AdvIntervalOption; /* The value to be placed in the Default Router Preference field of @@ -188,6 +193,13 @@ struct rtadvconf { */ struct list *AdvDNSSLList; + /* + * rfc4861 states RAs must be sent at least 3 seconds apart. + * We allow faster retransmits to speed up convergence but can + * turn that capability off to meet the rfc if needed. + */ + bool UseFastRexmit; /* True if fast rexmits are enabled */ + uint8_t inFastRexmit; /* True if we're rexmits faster than usual */ /* Track if RA was configured by BGP or by the Operator or both */ @@ -263,8 +275,19 @@ typedef enum { struct irdp_interface; +/* Ethernet segment info used for setting up EVPN multihoming */ +struct zebra_evpn_es; +struct zebra_es_if_info { + struct ethaddr sysmac; + uint32_t lid; /* local-id; has to be unique per-ES-sysmac */ + struct zebra_evpn_es *es; /* local ES */ +}; + /* `zebra' daemon local interface structure. */ struct zebra_if { + /* back pointer to the interface */ + struct interface *ifp; + /* Shutdown configuration. */ uint8_t shutdown; @@ -277,6 +300,15 @@ struct zebra_if { /* Installed addresses chains tree. */ struct route_table *ipv4_subnets; + /* Nexthops pointing to this interface */ + /** + * Any nexthop that we get should have an + * interface. When an interface goes down, + * we will use this list to update the nexthops + * pointing to it with that info. + */ + struct nhg_connected_tree_head nhg_dependents; + /* Information about up/down changes */ unsigned int up_count; char up_last[QUAGGA_TIMESTAMP_LEN]; @@ -329,6 +361,12 @@ struct zebra_if { struct zebra_l2info_bondslave bondslave_info; + /* ethernet segment */ + struct zebra_es_if_info es_info; + + /* bitmap of vlans associated with this interface */ + bitfield_t vlan_bitmap; + /* Link fields - for sub-interfaces. */ ifindex_t link_ifindex; struct interface *link; @@ -342,6 +380,9 @@ struct zebra_if { bool v6_2_v4_ll_neigh_entry; char neigh_mac[6]; struct in6_addr v6_2_v4_ll_addr6; + + /* The description of the interface */ + char *desc; }; DECLARE_HOOK(zebra_if_extra_info, (struct vty * vty, struct interface *ifp), @@ -349,17 +390,6 @@ DECLARE_HOOK(zebra_if_extra_info, (struct vty * vty, struct interface *ifp), DECLARE_HOOK(zebra_if_config_wr, (struct vty * vty, struct interface *ifp), (vty, ifp)) -static inline void zebra_if_set_ziftype(struct interface *ifp, - zebra_iftype_t zif_type, - zebra_slave_iftype_t zif_slave_type) -{ - struct zebra_if *zif; - - zif = (struct zebra_if *)ifp->info; - zif->zif_type = zif_type; - zif->zif_slave_type = zif_slave_type; -} - #define IS_ZEBRA_IF_VRF(ifp) \ (((struct zebra_if *)(ifp->info))->zif_type == ZEBRA_IF_VRF) @@ -419,6 +449,26 @@ extern void if_handle_vrf_change(struct interface *ifp, vrf_id_t vrf_id); extern void zebra_if_update_link(struct interface *ifp, ifindex_t link_ifindex, ns_id_t ns_id); extern void zebra_if_update_all_links(void); +extern void zebra_if_set_protodown(struct interface *ifp, bool down); +extern int if_ip_address_install(struct interface *ifp, struct prefix *prefix, + const char *label, struct prefix *pp); +extern int if_ipv6_address_install(struct interface *ifp, struct prefix *prefix, + const char *label); +extern int if_ip_address_uinstall(struct interface *ifp, struct prefix *prefix); +extern int if_shutdown(struct interface *ifp); +extern int if_no_shutdown(struct interface *ifp); +extern int if_multicast_set(struct interface *ifp); +extern int if_multicast_unset(struct interface *ifp); +extern int if_linkdetect(struct interface *ifp, bool detect); +extern void if_addr_wakeup(struct interface *ifp); + +/* Nexthop group connected functions */ +extern void if_nhg_dependents_add(struct interface *ifp, + struct nhg_hash_entry *nhe); +extern void if_nhg_dependents_del(struct interface *ifp, + struct nhg_hash_entry *nhe); +extern unsigned int if_nhg_dependents_count(const struct interface *ifp); +extern bool if_nhg_dependents_is_empty(const struct interface *ifp); extern void vrf_add_update(struct vrf *vrfp); diff --git a/zebra/ioctl.c b/zebra/ioctl.c index 322527015b..b461a08881 100644 --- a/zebra/ioctl.c +++ b/zebra/ioctl.c @@ -57,7 +57,7 @@ int if_ioctl(unsigned long request, caddr_t buffer) int ret; int err = 0; - frr_elevate_privs(&zserv_privs) { + frr_with_privs(&zserv_privs) { sock = socket(AF_INET, SOCK_DGRAM, 0); if (sock < 0) { zlog_err("Cannot create UDP socket: %s", @@ -83,7 +83,7 @@ int vrf_if_ioctl(unsigned long request, caddr_t buffer, vrf_id_t vrf_id) int ret; int err = 0; - frr_elevate_privs(&zserv_privs) { + frr_with_privs(&zserv_privs) { sock = vrf_socket(AF_INET, SOCK_DGRAM, 0, vrf_id, NULL); if (sock < 0) { zlog_err("Cannot create UDP socket: %s", @@ -110,7 +110,7 @@ static int if_ioctl_ipv6(unsigned long request, caddr_t buffer) int ret; int err = 0; - frr_elevate_privs(&zserv_privs) { + frr_with_privs(&zserv_privs) { sock = socket(AF_INET6, SOCK_DGRAM, 0); if (sock < 0) { zlog_err("Cannot create IPv6 datagram socket: %s", @@ -245,7 +245,7 @@ static int if_set_prefix_ctx(const struct zebra_dplane_ctx *ctx) p = (struct prefix_ipv4 *)dplane_ctx_get_intf_addr(ctx); memset(&addreq, 0, sizeof(addreq)); - strncpy((char *)&addreq.ifra_name, dplane_ctx_get_ifname(ctx), + strlcpy((char *)&addreq.ifra_name, dplane_ctx_get_ifname(ctx), sizeof(addreq.ifra_name)); memset(&addr, 0, sizeof(struct sockaddr_in)); @@ -296,7 +296,7 @@ static int if_unset_prefix_ctx(const struct zebra_dplane_ctx *ctx) p = (struct prefix_ipv4 *)dplane_ctx_get_intf_addr(ctx); memset(&addreq, 0, sizeof(addreq)); - strncpy((char *)&addreq.ifra_name, dplane_ctx_get_ifname(ctx), + strlcpy((char *)&addreq.ifra_name, dplane_ctx_get_ifname(ctx), sizeof(addreq.ifra_name)); memset(&addr, 0, sizeof(struct sockaddr_in)); diff --git a/zebra/ioctl_solaris.c b/zebra/ioctl_solaris.c index ccfa7a4a4c..2c71d949f7 100644 --- a/zebra/ioctl_solaris.c +++ b/zebra/ioctl_solaris.c @@ -66,7 +66,7 @@ int if_ioctl(unsigned long request, caddr_t buffer) int ret; int err; - frr_elevate_privs(&zserv_privs) { + frr_with_privs(&zserv_privs) { sock = socket(AF_INET, SOCK_DGRAM, 0); if (sock < 0) { @@ -96,7 +96,7 @@ int if_ioctl_ipv6(unsigned long request, caddr_t buffer) int ret; int err; - frr_elevate_privs(&zserv_privs) { + frr_with_privs(&zserv_privs) { sock = socket(AF_INET6, SOCK_DGRAM, 0); if (sock < 0) { @@ -286,7 +286,7 @@ static int if_unset_prefix_ctx(const struct zebra_dplane_ctx *ctx) p = (struct prefix_ipv4 *)dplane_ctx_get_intf_addr(ctx); - strncpy(ifreq.ifr_name, dplane_ctx_get_ifname(ctx), + strlcpy(ifreq.ifr_name, dplane_ctx_get_ifname(ctx), sizeof(ifreq.ifr_name)); memset(&addr, 0, sizeof(struct sockaddr_in)); diff --git a/zebra/ipforward_proc.c b/zebra/ipforward_proc.c index 8f44c377b3..4bd160ddbc 100644 --- a/zebra/ipforward_proc.c +++ b/zebra/ipforward_proc.c @@ -30,13 +30,11 @@ extern struct zebra_privs_t zserv_privs; -char proc_net_snmp[] = "/proc/net/snmp"; +static const char proc_net_snmp[] = "/proc/net/snmp"; static void dropline(FILE *fp) { - int c; - - while ((c = getc(fp)) != '\n') + while (getc(fp) != '\n') ; } @@ -70,13 +68,13 @@ int ipforward(void) } /* char proc_ipv4_forwarding[] = "/proc/sys/net/ipv4/conf/all/forwarding"; */ -char proc_ipv4_forwarding[] = "/proc/sys/net/ipv4/ip_forward"; +static const char proc_ipv4_forwarding[] = "/proc/sys/net/ipv4/ip_forward"; int ipforward_on(void) { FILE *fp; - frr_elevate_privs(&zserv_privs) { + frr_with_privs(&zserv_privs) { fp = fopen(proc_ipv4_forwarding, "w"); @@ -97,7 +95,7 @@ int ipforward_off(void) { FILE *fp; - frr_elevate_privs(&zserv_privs) { + frr_with_privs(&zserv_privs) { fp = fopen(proc_ipv4_forwarding, "w"); @@ -114,7 +112,8 @@ int ipforward_off(void) return ipforward(); } -char proc_ipv6_forwarding[] = "/proc/sys/net/ipv6/conf/all/forwarding"; +static const char proc_ipv6_forwarding[] = + "/proc/sys/net/ipv6/conf/all/forwarding"; int ipforward_ipv6(void) { @@ -143,7 +142,7 @@ int ipforward_ipv6_on(void) { FILE *fp; - frr_elevate_privs(&zserv_privs) { + frr_with_privs(&zserv_privs) { fp = fopen(proc_ipv6_forwarding, "w"); @@ -165,7 +164,7 @@ int ipforward_ipv6_off(void) { FILE *fp; - frr_elevate_privs(&zserv_privs) { + frr_with_privs(&zserv_privs) { fp = fopen(proc_ipv6_forwarding, "w"); diff --git a/zebra/ipforward_solaris.c b/zebra/ipforward_solaris.c index 1bb743059c..3d6a29bec6 100644 --- a/zebra/ipforward_solaris.c +++ b/zebra/ipforward_solaris.c @@ -72,8 +72,7 @@ static int solaris_nd(const int cmd, const char *parameter, const int value) snprintf(nd_buf, ND_BUFFER_SIZE, "%s", parameter); else { flog_err_sys(EC_LIB_SYSTEM_CALL, - "internal error - inappropriate command given to " - "solaris_nd()%s:%d", + "internal error - inappropriate command given to solaris_nd()%s:%d", __FILE__, __LINE__); return -1; } @@ -83,7 +82,7 @@ static int solaris_nd(const int cmd, const char *parameter, const int value) strioctl.ic_len = ND_BUFFER_SIZE; strioctl.ic_dp = nd_buf; - frr_elevate_privs(&zserv_privs) { + frr_with_privs(&zserv_privs) { if ((fd = open(device, O_RDWR)) < 0) { flog_err_sys(EC_LIB_SYSTEM_CALL, "failed to open device %s - %s", device, diff --git a/zebra/ipforward_sysctl.c b/zebra/ipforward_sysctl.c index cc9421c275..48ab95d1ba 100644 --- a/zebra/ipforward_sysctl.c +++ b/zebra/ipforward_sysctl.c @@ -41,7 +41,7 @@ int ipforward(void) size_t len; int ipforwarding = 0; - len = sizeof ipforwarding; + len = sizeof(ipforwarding); if (sysctl(mib, MIB_SIZ, &ipforwarding, &len, 0, 0) < 0) { flog_err_sys(EC_LIB_SYSTEM_CALL, "Can't get ipforwarding value"); @@ -55,8 +55,8 @@ int ipforward_on(void) size_t len; int ipforwarding = 1; - len = sizeof ipforwarding; - frr_elevate_privs(&zserv_privs) { + len = sizeof(ipforwarding); + frr_with_privs(&zserv_privs) { if (sysctl(mib, MIB_SIZ, NULL, NULL, &ipforwarding, len) < 0) { flog_err_sys(EC_LIB_SYSTEM_CALL, "Can't set ipforwarding on"); @@ -71,8 +71,8 @@ int ipforward_off(void) size_t len; int ipforwarding = 0; - len = sizeof ipforwarding; - frr_elevate_privs(&zserv_privs) { + len = sizeof(ipforwarding); + frr_with_privs(&zserv_privs) { if (sysctl(mib, MIB_SIZ, NULL, NULL, &ipforwarding, len) < 0) { flog_err_sys(EC_LIB_SYSTEM_CALL, "Can't set ipforwarding on"); @@ -96,8 +96,8 @@ int ipforward_ipv6(void) size_t len; int ip6forwarding = 0; - len = sizeof ip6forwarding; - frr_elevate_privs(&zserv_privs) { + len = sizeof(ip6forwarding); + frr_with_privs(&zserv_privs) { if (sysctl(mib_ipv6, MIB_SIZ, &ip6forwarding, &len, 0, 0) < 0) { flog_err_sys(EC_LIB_SYSTEM_CALL, "can't get ip6forwarding value"); @@ -112,8 +112,8 @@ int ipforward_ipv6_on(void) size_t len; int ip6forwarding = 1; - len = sizeof ip6forwarding; - frr_elevate_privs(&zserv_privs) { + len = sizeof(ip6forwarding); + frr_with_privs(&zserv_privs) { if (sysctl(mib_ipv6, MIB_SIZ, NULL, NULL, &ip6forwarding, len) < 0) { flog_err_sys(EC_LIB_SYSTEM_CALL, @@ -129,8 +129,8 @@ int ipforward_ipv6_off(void) size_t len; int ip6forwarding = 0; - len = sizeof ip6forwarding; - frr_elevate_privs(&zserv_privs) { + len = sizeof(ip6forwarding); + frr_with_privs(&zserv_privs) { if (sysctl(mib_ipv6, MIB_SIZ, NULL, NULL, &ip6forwarding, len) < 0) { flog_err_sys(EC_LIB_SYSTEM_CALL, diff --git a/zebra/irdp.h b/zebra/irdp.h index 3f4fa93460..c1ea34f3aa 100644 --- a/zebra/irdp.h +++ b/zebra/irdp.h @@ -32,9 +32,6 @@ extern "C" { #endif -#define TRUE 1 -#define FALSE 0 - /* ICMP Messages */ #ifndef ICMP_ROUTERADVERT #define ICMP_ROUTERADVERT 9 @@ -54,7 +51,7 @@ extern "C" { #endif /* INADDR_ALLRTRS_GROUP */ /* Default irdp packet interval */ -#define IRDP_DEFAULT_INTERVAL 300 +#define IRDP_DEFAULT_INTERVAL 300 /* Router constants from RFC1256 */ #define MAX_INITIAL_ADVERT_INTERVAL 16 @@ -126,10 +123,10 @@ struct irdp_interface { #define IF_ACTIVE (1<<0) /* ICMP Active */ #define IF_BROADCAST (1<<1) /* 255.255.255.255 */ #define IF_SOLICIT (1<<2) /* Solicit active */ -#define IF_DEBUG_MESSAGES (1<<3) -#define IF_DEBUG_PACKET (1<<4) -#define IF_DEBUG_MISC (1<<5) -#define IF_SHUTDOWN (1<<6) +#define IF_DEBUG_MESSAGES (1<<3) +#define IF_DEBUG_PACKET (1<<4) +#define IF_DEBUG_MISC (1<<5) +#define IF_SHUTDOWN (1<<6) struct interface *ifp; struct thread *t_advertise; diff --git a/zebra/irdp_interface.c b/zebra/irdp_interface.c index c0b772cd01..2ab5fd3a4c 100644 --- a/zebra/irdp_interface.c +++ b/zebra/irdp_interface.c @@ -53,6 +53,7 @@ #include "if.h" #include "sockunion.h" #include "log.h" +#include "network.h" extern int irdp_sock; @@ -223,8 +224,7 @@ static void irdp_if_start(struct interface *ifp, int multicast, } if ((irdp_sock < 0) && ((irdp_sock = irdp_sock_init()) < 0)) { flog_warn(EC_ZEBRA_IRDP_CANNOT_ACTIVATE_IFACE, - "IRDP: Cannot activate interface %s (cannot create " - "IRDP socket)", + "IRDP: Cannot activate interface %s (cannot create IRDP socket)", ifp->name); return; } @@ -267,7 +267,7 @@ static void irdp_if_start(struct interface *ifp, int multicast, } srandom(seed); - timer = (random() % IRDP_DEFAULT_INTERVAL) + 1; + timer = (frr_weak_random() % IRDP_DEFAULT_INTERVAL) + 1; irdp->AdvPrefList = list_new(); irdp->AdvPrefList->del = (void (*)(void *))Adv_free; /* Destructor */ @@ -352,7 +352,7 @@ static void irdp_if_no_shutdown(struct interface *ifp) irdp->flags &= ~IF_SHUTDOWN; - irdp_if_start(ifp, irdp->flags & IF_BROADCAST ? FALSE : TRUE, FALSE); + irdp_if_start(ifp, irdp->flags & IF_BROADCAST ? false : true, false); } @@ -407,7 +407,7 @@ DEFUN (ip_irdp_multicast, VTY_DECLVAR_CONTEXT(interface, ifp); irdp_if_get(ifp); - irdp_if_start(ifp, TRUE, TRUE); + irdp_if_start(ifp, true, true); return CMD_SUCCESS; } @@ -421,7 +421,7 @@ DEFUN (ip_irdp_broadcast, VTY_DECLVAR_CONTEXT(interface, ifp); irdp_if_get(ifp); - irdp_if_start(ifp, FALSE, TRUE); + irdp_if_start(ifp, false, true); return CMD_SUCCESS; } @@ -502,8 +502,7 @@ DEFUN (ip_irdp_minadvertinterval, return CMD_SUCCESS; } else { vty_out(vty, - "%% MinAdvertInterval must be less than or equal to " - "MaxAdvertInterval\n"); + "%% MinAdvertInterval must be less than or equal to MaxAdvertInterval\n"); return CMD_WARNING_CONFIG_FAILED; } } @@ -527,8 +526,7 @@ DEFUN (ip_irdp_maxadvertinterval, return CMD_SUCCESS; } else { vty_out(vty, - "%% MaxAdvertInterval must be greater than or equal to " - "MinAdvertInterval\n"); + "%% MaxAdvertInterval must be greater than or equal to MinAdvertInterval\n"); return CMD_WARNING_CONFIG_FAILED; } } diff --git a/zebra/irdp_main.c b/zebra/irdp_main.c index 38d241eaa5..b868d23a94 100644 --- a/zebra/irdp_main.c +++ b/zebra/irdp_main.c @@ -66,6 +66,7 @@ #include "if.h" #include "sockunion.h" #include "log.h" +#include "network.h" /* GLOBAL VARS */ @@ -82,7 +83,7 @@ int irdp_sock_init(void) int save_errno; int sock; - frr_elevate_privs(&zserv_privs) { + frr_with_privs(&zserv_privs) { sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); save_errno = errno; @@ -190,7 +191,7 @@ static void irdp_send(struct interface *ifp, struct prefix *p, struct stream *s) if (irdp->flags & IF_DEBUG_MESSAGES) zlog_debug("IRDP: TX Advert on %s %s Holdtime=%d Preference=%d", - ifp->name, prefix2str(p, buf, sizeof buf), + ifp->name, prefix2str(p, buf, sizeof(buf)), irdp->flags & IF_SHUTDOWN ? 0 : irdp->Lifetime, get_pref(irdp, p)); @@ -233,7 +234,7 @@ int irdp_send_thread(struct thread *t_advert) } tmp = irdp->MaxAdvertInterval - irdp->MinAdvertInterval; - timer = random() % (tmp + 1); + timer = frr_weak_random() % (tmp + 1); timer = irdp->MinAdvertInterval + timer; if (irdp->irdp_sent < MAX_INITIAL_ADVERTISEMENTS @@ -303,7 +304,7 @@ void process_solicit(struct interface *ifp) thread_cancel(irdp->t_advertise); irdp->t_advertise = NULL; - timer = (random() % MAX_RESPONSE_DELAY) + 1; + timer = (frr_weak_random() % MAX_RESPONSE_DELAY) + 1; irdp->t_advertise = NULL; thread_add_timer(zrouter.master, irdp_send_thread, ifp, timer, diff --git a/zebra/irdp_packet.c b/zebra/irdp_packet.c index 2804787620..502a2f277c 100644 --- a/zebra/irdp_packet.c +++ b/zebra/irdp_packet.c @@ -78,6 +78,7 @@ static void parse_irdp_packet(char *p, int len, struct interface *ifp) int ip_hlen, iplen, datalen; struct zebra_if *zi; struct irdp_interface *irdp; + uint16_t saved_chksum; zi = ifp->info; if (!zi) @@ -121,8 +122,10 @@ static void parse_irdp_packet(char *p, int len, struct interface *ifp) icmp = (struct icmphdr *)(p + ip_hlen); + saved_chksum = icmp->checksum; + icmp->checksum = 0; /* check icmp checksum */ - if (in_cksum(icmp, datalen) != icmp->checksum) { + if (in_cksum(icmp, datalen) != saved_chksum) { flog_warn( EC_ZEBRA_IRDP_BAD_CHECKSUM, "IRDP: RX ICMP packet from %s. Bad checksum, silently ignored", @@ -136,10 +139,10 @@ static void parse_irdp_packet(char *p, int len, struct interface *ifp) return; if (icmp->code != 0) { - flog_warn(EC_ZEBRA_IRDP_BAD_TYPE_CODE, - "IRDP: RX packet type %d from %s. Bad ICMP type code," - " silently ignored", - icmp->type, inet_ntoa(src)); + flog_warn( + EC_ZEBRA_IRDP_BAD_TYPE_CODE, + "IRDP: RX packet type %d from %s. Bad ICMP type code, silently ignored", + icmp->type, inet_ntoa(src)); return; } @@ -174,8 +177,8 @@ static void parse_irdp_packet(char *p, int len, struct interface *ifp) default: flog_warn( - EC_ZEBRA_IRDP_BAD_TYPE, - "IRDP: RX type %d from %s. Bad ICMP type, silently ignored", + EC_ZEBRA_IRDP_BAD_TYPE_CODE, + "IRDP: RX packet type %d from %s. Bad ICMP type code, silently ignored", icmp->type, inet_ntoa(src)); } } @@ -193,7 +196,7 @@ static int irdp_recvmsg(int sock, uint8_t *buf, int size, int *ifindex) msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = (void *)adata; - msg.msg_controllen = sizeof adata; + msg.msg_controllen = sizeof(adata); iov.iov_base = buf; iov.iov_len = size; @@ -315,15 +318,20 @@ void send_packet(struct interface *ifp, struct stream *s, uint32_t dst, if (setsockopt(irdp_sock, IPPROTO_IP, IP_HDRINCL, (char *)&on, sizeof(on)) < 0) - zlog_debug("sendto %s", safe_strerror(errno)); + flog_err(EC_LIB_SOCKET, + "IRDP: Cannot set IP_HDRINCLU %s(%d) on %s", + safe_strerror(errno), errno, ifp->name); if (dst == INADDR_BROADCAST) { - on = 1; - if (setsockopt(irdp_sock, SOL_SOCKET, SO_BROADCAST, (char *)&on, - sizeof(on)) + uint32_t bon = 1; + + if (setsockopt(irdp_sock, SOL_SOCKET, SO_BROADCAST, &bon, + sizeof(bon)) < 0) - zlog_debug("sendto %s", safe_strerror(errno)); + flog_err(EC_LIB_SOCKET, + "IRDP: Cannot set SO_BROADCAST %s(%d) on %s", + safe_strerror(errno), errno, ifp->name); } if (dst != INADDR_BROADCAST) @@ -354,8 +362,8 @@ void send_packet(struct interface *ifp, struct stream *s, uint32_t dst, sockopt_iphdrincl_swab_htosys(ip); - if (sendmsg(irdp_sock, msg, 0) < 0) { - zlog_debug("sendto %s", safe_strerror(errno)); - } - /* printf("TX on %s idx %d\n", ifp->name, ifp->ifindex); */ + if (sendmsg(irdp_sock, msg, 0) < 0) + flog_err(EC_LIB_SOCKET, + "IRDP: sendmsg send failure %s(%d) on %s", + safe_strerror(errno), errno, ifp->name); } diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c index fe37a33358..d9fb6d6d91 100644 --- a/zebra/kernel_netlink.c +++ b/zebra/kernel_netlink.c @@ -20,11 +20,6 @@ #include -#if defined(HANDLE_NETLINK_FUZZING) -#include -#include -#endif /* HANDLE_NETLINK_FUZZING */ - #ifdef HAVE_NETLINK #include "linklist.h" @@ -83,6 +78,20 @@ #define RTPROT_MROUTED 17 #endif +#define NL_DEFAULT_BATCH_BUFSIZE (16 * NL_PKT_BUF_SIZE) + +/* + * We limit the batch's size to a number smaller than the length of the + * underlying buffer since the last message that wouldn't fit the batch would go + * over the upper boundary and then it would have to be encoded again into a new + * buffer. If the difference between the limit and the length of the buffer is + * big enough (bigger than the biggest Netlink message) then this situation + * won't occur. + */ +#define NL_DEFAULT_BATCH_SEND_THRESHOLD (15 * NL_PKT_BUF_SIZE) + +#define NL_BATCH_RX_BUFSIZE NL_RCV_PKT_BUF_SIZE + static const struct message nlmsg_str[] = {{RTM_NEWROUTE, "RTM_NEWROUTE"}, {RTM_DELROUTE, "RTM_DELROUTE"}, {RTM_GETROUTE, "RTM_GETROUTE"}, @@ -98,6 +107,9 @@ static const struct message nlmsg_str[] = {{RTM_NEWROUTE, "RTM_NEWROUTE"}, {RTM_NEWRULE, "RTM_NEWRULE"}, {RTM_DELRULE, "RTM_DELRULE"}, {RTM_GETRULE, "RTM_GETRULE"}, + {RTM_NEWNEXTHOP, "RTM_NEWNEXTHOP"}, + {RTM_DELNEXTHOP, "RTM_DELNEXTHOP"}, + {RTM_GETNEXTHOP, "RTM_GETNEXTHOP"}, {0}}; static const struct message rtproto_str[] = { @@ -128,8 +140,18 @@ static const struct message family_str[] = {{AF_INET, "ipv4"}, {RTNL_FAMILY_IP6MR, "ipv6MR"}, {0}}; -static const struct message rttype_str[] = {{RTN_UNICAST, "unicast"}, +static const struct message rttype_str[] = {{RTN_UNSPEC, "none"}, + {RTN_UNICAST, "unicast"}, + {RTN_LOCAL, "local"}, + {RTN_BROADCAST, "broadcast"}, + {RTN_ANYCAST, "anycast"}, {RTN_MULTICAST, "multicast"}, + {RTN_BLACKHOLE, "blackhole"}, + {RTN_UNREACHABLE, "unreachable"}, + {RTN_PROHIBIT, "prohibited"}, + {RTN_THROW, "throw"}, + {RTN_NAT, "nat"}, + {RTN_XRESOLVE, "resolver"}, {0}}; extern struct thread_master *master; @@ -137,6 +159,62 @@ extern uint32_t nl_rcvbufsize; extern struct zebra_privs_t zserv_privs; +DEFINE_MTYPE_STATIC(ZEBRA, NL_BUF, "Zebra Netlink buffers") + +size_t nl_batch_tx_bufsize; +char *nl_batch_tx_buf; + +char nl_batch_rx_buf[NL_BATCH_RX_BUFSIZE]; + +_Atomic uint32_t nl_batch_bufsize = NL_DEFAULT_BATCH_BUFSIZE; +_Atomic uint32_t nl_batch_send_threshold = NL_DEFAULT_BATCH_SEND_THRESHOLD; + +struct nl_batch { + void *buf; + size_t bufsiz; + size_t limit; + + void *buf_head; + size_t curlen; + size_t msgcnt; + + const struct zebra_dplane_info *zns; + + struct dplane_ctx_q ctx_list; + + /* + * Pointer to the queue of completed contexts outbound back + * towards the dataplane module. + */ + struct dplane_ctx_q *ctx_out_q; +}; + +int netlink_config_write_helper(struct vty *vty) +{ + uint32_t size = + atomic_load_explicit(&nl_batch_bufsize, memory_order_relaxed); + uint32_t threshold = atomic_load_explicit(&nl_batch_send_threshold, + memory_order_relaxed); + + if (size != NL_DEFAULT_BATCH_BUFSIZE + || threshold != NL_DEFAULT_BATCH_SEND_THRESHOLD) + vty_out(vty, "zebra kernel netlink batch-tx-buf %u %u\n", size, + threshold); + + return 0; +} + +void netlink_set_batch_buffer_size(uint32_t size, uint32_t threshold, bool set) +{ + if (!set) { + size = NL_DEFAULT_BATCH_BUFSIZE; + threshold = NL_DEFAULT_BATCH_SEND_THRESHOLD; + } + + atomic_store_explicit(&nl_batch_bufsize, size, memory_order_relaxed); + atomic_store_explicit(&nl_batch_send_threshold, threshold, + memory_order_relaxed); +} int netlink_talk_filter(struct nlmsghdr *h, ns_id_t ns_id, int startup) { @@ -172,7 +250,7 @@ static int netlink_recvbuf(struct nlsock *nl, uint32_t newsize) } /* Try force option (linux >= 2.6.14) and fall back to normal set */ - frr_elevate_privs(&zserv_privs) { + frr_with_privs(&zserv_privs) { ret = setsockopt(nl->sock, SOL_SOCKET, SO_RCVBUFFORCE, &nl_rcvbufsize, sizeof(nl_rcvbufsize)); @@ -209,7 +287,7 @@ static int netlink_socket(struct nlsock *nl, unsigned long groups, int sock; int namelen; - frr_elevate_privs(&zserv_privs) { + frr_with_privs(&zserv_privs) { sock = ns_socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE, ns_id); if (sock < 0) { zlog_err("Can't open %s socket: %s", nl->name, @@ -217,12 +295,12 @@ static int netlink_socket(struct nlsock *nl, unsigned long groups, return -1; } - memset(&snl, 0, sizeof snl); + memset(&snl, 0, sizeof(snl)); snl.nl_family = AF_NETLINK; snl.nl_groups = groups; /* Bind the socket to the netlink structure for anything. */ - ret = bind(sock, (struct sockaddr *)&snl, sizeof snl); + ret = bind(sock, (struct sockaddr *)&snl, sizeof(snl)); } if (ret < 0) { @@ -233,9 +311,9 @@ static int netlink_socket(struct nlsock *nl, unsigned long groups, } /* multiple netlink sockets will have different nl_pid */ - namelen = sizeof snl; + namelen = sizeof(snl); ret = getsockname(sock, (struct sockaddr *)&snl, (socklen_t *)&namelen); - if (ret < 0 || namelen != sizeof snl) { + if (ret < 0 || namelen != sizeof(snl)) { flog_err_sys(EC_LIB_SOCKET, "Can't get %s socket name: %s", nl->name, safe_strerror(errno)); close(sock); @@ -276,10 +354,26 @@ static int netlink_information_fetch(struct nlmsghdr *h, ns_id_t ns_id, return netlink_neigh_change(h, ns_id); case RTM_DELNEIGH: return netlink_neigh_change(h, ns_id); + case RTM_GETNEIGH: + /* + * Kernel in some situations when it expects + * user space to resolve arp entries, we will + * receive this notification. As we don't + * need this notification and as that + * we don't want to spam the log file with + * below messages, just ignore. + */ + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("Received RTM_GETNEIGH, ignoring"); + break; case RTM_NEWRULE: return netlink_rule_change(h, ns_id, startup); case RTM_DELRULE: return netlink_rule_change(h, ns_id, startup); + case RTM_NEWNEXTHOP: + return netlink_nexthop_change(h, ns_id, startup); + case RTM_DELNEXTHOP: + return netlink_nexthop_change(h, ns_id, startup); default: /* * If we have received this message then @@ -289,7 +383,7 @@ static int netlink_information_fetch(struct nlmsghdr *h, ns_id_t ns_id, * it to be sent up to us */ flog_err(EC_ZEBRA_UNKNOWN_NLMSG, - "Unknown netlink nlmsg_type %s(%d) vrf %u\n", + "Unknown netlink nlmsg_type %s(%d) vrf %u", nl_msg_type_to_str(h->nlmsg_type), h->nlmsg_type, ns_id); break; @@ -297,87 +391,6 @@ static int netlink_information_fetch(struct nlmsghdr *h, ns_id_t ns_id, return 0; } -#if defined(HANDLE_NETLINK_FUZZING) -/* Using globals here to avoid adding function parameters */ - -/* Keep distinct filenames for netlink fuzzy collection */ -static unsigned int netlink_file_counter = 1; - -/* File name to read fuzzed netlink from */ -static char netlink_fuzz_file[MAXPATHLEN] = ""; - -/* Flag for whether to read from file or not */ -bool netlink_read; - -/** - * netlink_read_init() - Starts the message parser - * @fname: Filename to read. - */ -void netlink_read_init(const char *fname) -{ - struct zebra_dplane_info dp_info; - - snprintf(netlink_fuzz_file, MAXPATHLEN, "%s", fname); - /* Creating this fake socket for testing purposes */ - struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT); - - /* Capture key info from zns struct */ - zebra_dplane_info_from_zns(&dp_info, zns, false); - - netlink_parse_info(netlink_information_fetch, &zns->netlink, - &dp_info, 1, 0); -} - -/** - * netlink_write_incoming() - Writes all data received from netlink to a file - * @buf: Data from netlink. - * @size: Size of data. - * @counter: Counter for keeping filenames distinct. - */ -static void netlink_write_incoming(const char *buf, const unsigned int size, - unsigned int counter) -{ - char fname[MAXPATHLEN]; - FILE *f; - - snprintf(fname, MAXPATHLEN, "%s/%s_%u", DAEMON_VTY_DIR, "netlink", - counter); - frr_elevate_privs(&zserv_privs) { - f = fopen(fname, "w"); - } - if (f) { - fwrite(buf, 1, size, f); - fclose(f); - } -} - -/** - * netlink_read_file() - Reads netlink data from file - * @buf: Netlink buffer being overwritten. - * @fname: File name to read from. - * - * Return: Size of file. - */ -static long netlink_read_file(char *buf, const char *fname) -{ - FILE *f; - long file_bytes = -1; - - frr_elevate_privs(&zserv_privs) { - f = fopen(fname, "r"); - } - if (f) { - fseek(f, 0, SEEK_END); - file_bytes = ftell(f); - rewind(f); - fread(buf, NL_RCV_PKT_BUF_SIZE, 1, f); - fclose(f); - } - return file_bytes; -} - -#endif /* HANDLE_NETLINK_FUZZING */ - static int kernel_read(struct thread *thread) { struct zebra_ns *zns = (struct zebra_ns *)THREAD_ARG(thread); @@ -472,7 +485,7 @@ static void netlink_install_filter(int sock, __u32 pid, __u32 dplane_pid) if (setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &prog, sizeof(prog)) < 0) - flog_err_sys(EC_LIB_SOCKET, "Can't install socket filter: %s\n", + flog_err_sys(EC_LIB_SOCKET, "Can't install socket filter: %s", safe_strerror(errno)); } @@ -498,8 +511,8 @@ void netlink_parse_rtattr_nested(struct rtattr **tb, int max, netlink_parse_rtattr(tb, max, RTA_DATA(rta), RTA_PAYLOAD(rta)); } -int addattr_l(struct nlmsghdr *n, unsigned int maxlen, int type, - const void *data, unsigned int alen) +bool nl_attr_put(struct nlmsghdr *n, unsigned int maxlen, int type, + const void *data, unsigned int alen) { int len; struct rtattr *rta; @@ -507,7 +520,7 @@ int addattr_l(struct nlmsghdr *n, unsigned int maxlen, int type, len = RTA_LENGTH(alen); if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen) - return -1; + return false; rta = (struct rtattr *)(((char *)n) + NLMSG_ALIGN(n->nlmsg_len)); rta->rta_type = type; @@ -520,70 +533,56 @@ int addattr_l(struct nlmsghdr *n, unsigned int maxlen, int type, n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len); - return 0; -} - -int rta_addattr_l(struct rtattr *rta, unsigned int maxlen, int type, - const void *data, unsigned int alen) -{ - unsigned int len; - struct rtattr *subrta; - - len = RTA_LENGTH(alen); - - if (RTA_ALIGN(rta->rta_len) + RTA_ALIGN(len) > maxlen) - return -1; - - subrta = (struct rtattr *)(((char *)rta) + RTA_ALIGN(rta->rta_len)); - subrta->rta_type = type; - subrta->rta_len = len; - - if (data) - memcpy(RTA_DATA(subrta), data, alen); - else - assert(alen == 0); - - rta->rta_len = NLMSG_ALIGN(rta->rta_len) + RTA_ALIGN(len); - - return 0; + return true; } -int addattr16(struct nlmsghdr *n, unsigned int maxlen, int type, uint16_t data) +bool nl_attr_put16(struct nlmsghdr *n, unsigned int maxlen, int type, + uint16_t data) { - return addattr_l(n, maxlen, type, &data, sizeof(uint16_t)); + return nl_attr_put(n, maxlen, type, &data, sizeof(uint16_t)); } -int addattr32(struct nlmsghdr *n, unsigned int maxlen, int type, int data) +bool nl_attr_put32(struct nlmsghdr *n, unsigned int maxlen, int type, + uint32_t data) { - return addattr_l(n, maxlen, type, &data, sizeof(uint32_t)); + return nl_attr_put(n, maxlen, type, &data, sizeof(uint32_t)); } -struct rtattr *addattr_nest(struct nlmsghdr *n, int maxlen, int type) +struct rtattr *nl_attr_nest(struct nlmsghdr *n, unsigned int maxlen, int type) { struct rtattr *nest = NLMSG_TAIL(n); - addattr_l(n, maxlen, type, NULL, 0); + if (!nl_attr_put(n, maxlen, type, NULL, 0)) + return NULL; + + nest->rta_type |= NLA_F_NESTED; return nest; } -int addattr_nest_end(struct nlmsghdr *n, struct rtattr *nest) +int nl_attr_nest_end(struct nlmsghdr *n, struct rtattr *nest) { nest->rta_len = (uint8_t *)NLMSG_TAIL(n) - (uint8_t *)nest; return n->nlmsg_len; } -struct rtattr *rta_nest(struct rtattr *rta, int maxlen, int type) +struct rtnexthop *nl_attr_rtnh(struct nlmsghdr *n, unsigned int maxlen) { - struct rtattr *nest = RTA_TAIL(rta); + struct rtnexthop *rtnh = (struct rtnexthop *)NLMSG_TAIL(n); - rta_addattr_l(rta, maxlen, type, NULL, 0); - return nest; + if (NLMSG_ALIGN(n->nlmsg_len) + RTNH_ALIGN(sizeof(struct rtnexthop)) + > maxlen) + return NULL; + + memset(rtnh, 0, sizeof(struct rtnexthop)); + n->nlmsg_len = + NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(sizeof(struct rtnexthop)); + + return rtnh; } -int rta_nest_end(struct rtattr *rta, struct rtattr *nest) +void nl_attr_rtnh_end(struct nlmsghdr *n, struct rtnexthop *rtnh) { - nest->rta_len = (uint8_t *)RTA_TAIL(rta) - (uint8_t *)nest; - return rta->rta_len; + rtnh->rtnh_len = (uint8_t *)NLMSG_TAIL(n) - (uint8_t *)rtnh; } const char *nl_msg_type_to_str(uint16_t msg_type) @@ -666,8 +665,7 @@ static void netlink_parse_extended_ack(struct nlmsghdr *h) * but noticing it for later. */ err_nlh = &err->msg; - zlog_debug("%s: Received %s extended Ack", - __PRETTY_FUNCTION__, + zlog_debug("%s: Received %s extended Ack", __func__, nl_msg_type_to_str(err_nlh->nlmsg_type)); } } @@ -683,6 +681,184 @@ static void netlink_parse_extended_ack(struct nlmsghdr *h) } } +/* + * netlink_send_msg - send a netlink message of a certain size. + * + * Returns -1 on error. Otherwise, it returns the number of bytes sent. + */ +static ssize_t netlink_send_msg(const struct nlsock *nl, void *buf, + size_t buflen) +{ + struct sockaddr_nl snl = {}; + struct iovec iov = {}; + struct msghdr msg = {}; + ssize_t status; + int save_errno = 0; + + iov.iov_base = buf; + iov.iov_len = buflen; + msg.msg_name = &snl; + msg.msg_namelen = sizeof(snl); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + snl.nl_family = AF_NETLINK; + + /* Send message to netlink interface. */ + frr_with_privs(&zserv_privs) { + status = sendmsg(nl->sock, &msg, 0); + save_errno = errno; + } + + if (IS_ZEBRA_DEBUG_KERNEL_MSGDUMP_SEND) { + zlog_debug("%s: >> netlink message dump [sent]", __func__); + zlog_hexdump(buf, buflen); + } + + if (status == -1) { + flog_err_sys(EC_LIB_SOCKET, "%s error: %s", __func__, + safe_strerror(save_errno)); + return -1; + } + + return status; +} + +/* + * netlink_recv_msg - receive a netlink message. + * + * Returns -1 on error, 0 if read would block or the number of bytes received. + */ +static int netlink_recv_msg(const struct nlsock *nl, struct msghdr msg, + void *buf, size_t buflen) +{ + struct iovec iov; + int status; + + iov.iov_base = buf; + iov.iov_len = buflen; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + do { + status = recvmsg(nl->sock, &msg, 0); + } while (status == -1 && errno == EINTR); + + if (status == -1) { + if (errno == EWOULDBLOCK || errno == EAGAIN) + return 0; + flog_err(EC_ZEBRA_RECVMSG_OVERRUN, "%s recvmsg overrun: %s", + nl->name, safe_strerror(errno)); + /* + * In this case we are screwed. There is no good way to recover + * zebra at this point. + */ + exit(-1); + } + + if (status == 0) { + flog_err_sys(EC_LIB_SOCKET, "%s EOF", nl->name); + return -1; + } + + if (msg.msg_namelen != sizeof(struct sockaddr_nl)) { + flog_err(EC_ZEBRA_NETLINK_LENGTH_ERROR, + "%s sender address length error: length %d", nl->name, + msg.msg_namelen); + return -1; + } + + if (IS_ZEBRA_DEBUG_KERNEL_MSGDUMP_RECV) { + zlog_debug("%s: << netlink message dump [recv]", __func__); + zlog_hexdump(buf, status); + } + + return status; +} + +/* + * netlink_parse_error - parse a netlink error message + * + * Returns 1 if this message is acknowledgement, 0 if this error should be + * ignored, -1 otherwise. + */ +static int netlink_parse_error(const struct nlsock *nl, struct nlmsghdr *h, + const struct zebra_dplane_info *zns, + bool startup) +{ + struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(h); + int errnum = err->error; + int msg_type = err->msg.nlmsg_type; + + if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) { + flog_err(EC_ZEBRA_NETLINK_LENGTH_ERROR, + "%s error: message truncated", nl->name); + return -1; + } + + /* + * Parse the extended information before we actually handle it. At this + * point in time we do not do anything other than report the issue. + */ + if (h->nlmsg_flags & NLM_F_ACK_TLVS) + netlink_parse_extended_ack(h); + + /* If the error field is zero, then this is an ACK. */ + if (err->error == 0) { + if (IS_ZEBRA_DEBUG_KERNEL) { + zlog_debug("%s: %s ACK: type=%s(%u), seq=%u, pid=%u", + __func__, nl->name, + nl_msg_type_to_str(err->msg.nlmsg_type), + err->msg.nlmsg_type, err->msg.nlmsg_seq, + err->msg.nlmsg_pid); + } + + return 1; + } + + /* Deal with errors that occur because of races in link handling. */ + if (zns->is_cmd + && ((msg_type == RTM_DELROUTE + && (-errnum == ENODEV || -errnum == ESRCH)) + || (msg_type == RTM_NEWROUTE + && (-errnum == ENETDOWN || -errnum == EEXIST)))) { + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("%s: error: %s type=%s(%u), seq=%u, pid=%u", + nl->name, safe_strerror(-errnum), + nl_msg_type_to_str(msg_type), msg_type, + err->msg.nlmsg_seq, err->msg.nlmsg_pid); + return 0; + } + + /* + * We see RTM_DELNEIGH when shutting down an interface with an IPv4 + * link-local. The kernel should have already deleted the neighbor so + * do not log these as an error. + */ + if (msg_type == RTM_DELNEIGH + || (zns->is_cmd && msg_type == RTM_NEWROUTE + && (-errnum == ESRCH || -errnum == ENETUNREACH))) { + /* + * This is known to happen in some situations, don't log as + * error. + */ + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("%s error: %s, type=%s(%u), seq=%u, pid=%u", + nl->name, safe_strerror(-errnum), + nl_msg_type_to_str(msg_type), msg_type, + err->msg.nlmsg_seq, err->msg.nlmsg_pid); + } else { + if ((msg_type != RTM_GETNEXTHOP) || !startup) + flog_err(EC_ZEBRA_UNEXPECTED_MESSAGE, + "%s error: %s, type=%s(%u), seq=%u, pid=%u", + nl->name, safe_strerror(-errnum), + nl_msg_type_to_str(msg_type), msg_type, + err->msg.nlmsg_seq, err->msg.nlmsg_pid); + } + + return -1; +} + /* * netlink_parse_info * @@ -708,71 +884,19 @@ int netlink_parse_info(int (*filter)(struct nlmsghdr *, ns_id_t, int), while (1) { char buf[NL_RCV_PKT_BUF_SIZE]; - struct iovec iov = {.iov_base = buf, .iov_len = sizeof buf}; struct sockaddr_nl snl; struct msghdr msg = {.msg_name = (void *)&snl, - .msg_namelen = sizeof snl, - .msg_iov = &iov, - .msg_iovlen = 1}; + .msg_namelen = sizeof(snl)}; struct nlmsghdr *h; if (count && read_in >= count) return 0; -#if defined(HANDLE_NETLINK_FUZZING) - /* Check if reading and filename is set */ - if (netlink_read && '\0' != netlink_fuzz_file[0]) { - zlog_debug("Reading netlink fuzz file"); - status = netlink_read_file(buf, netlink_fuzz_file); - snl.nl_pid = 0; - } else { - status = recvmsg(nl->sock, &msg, 0); - } -#else - status = recvmsg(nl->sock, &msg, 0); -#endif /* HANDLE_NETLINK_FUZZING */ - if (status < 0) { - if (errno == EINTR) - continue; - if (errno == EWOULDBLOCK || errno == EAGAIN) - break; - flog_err(EC_ZEBRA_RECVMSG_OVERRUN, - "%s recvmsg overrun: %s", nl->name, - safe_strerror(errno)); - /* - * In this case we are screwed. - * There is no good way to - * recover zebra at this point. - */ - exit(-1); - continue; - } - - if (status == 0) { - flog_err_sys(EC_LIB_SOCKET, "%s EOF", nl->name); + status = netlink_recv_msg(nl, msg, buf, sizeof(buf)); + if (status == -1) return -1; - } - - if (msg.msg_namelen != sizeof snl) { - flog_err(EC_ZEBRA_NETLINK_LENGTH_ERROR, - "%s sender address length error: length %d", - nl->name, msg.msg_namelen); - return -1; - } - - if (IS_ZEBRA_DEBUG_KERNEL_MSGDUMP_RECV) { - zlog_debug("%s: << netlink message dump [recv]", - __func__); - zlog_hexdump(buf, status); - } - -#if defined(HANDLE_NETLINK_FUZZING) - if (!netlink_read) { - zlog_debug("Writing incoming netlink message"); - netlink_write_incoming(buf, status, - netlink_file_counter++); - } -#endif /* HANDLE_NETLINK_FUZZING */ + else if (status == 0) + break; read_in++; for (h = (struct nlmsghdr *)buf; @@ -784,107 +908,14 @@ int netlink_parse_info(int (*filter)(struct nlmsghdr *, ns_id_t, int), /* Error handling. */ if (h->nlmsg_type == NLMSG_ERROR) { - struct nlmsgerr *err = - (struct nlmsgerr *)NLMSG_DATA(h); - int errnum = err->error; - int msg_type = err->msg.nlmsg_type; - - if (h->nlmsg_len - < NLMSG_LENGTH(sizeof(struct nlmsgerr))) { - flog_err(EC_ZEBRA_NETLINK_LENGTH_ERROR, - "%s error: message truncated", - nl->name); - return -1; - } - - /* - * Parse the extended information before - * we actually handle it. - * At this point in time we do not - * do anything other than report the - * issue. - */ - if (h->nlmsg_flags & NLM_F_ACK_TLVS) - netlink_parse_extended_ack(h); - - /* If the error field is zero, then this is an - * ACK */ - if (err->error == 0) { - if (IS_ZEBRA_DEBUG_KERNEL) { - zlog_debug( - "%s: %s ACK: type=%s(%u), seq=%u, pid=%u", - __FUNCTION__, nl->name, - nl_msg_type_to_str( - err->msg.nlmsg_type), - err->msg.nlmsg_type, - err->msg.nlmsg_seq, - err->msg.nlmsg_pid); - } - - /* return if not a multipart message, - * otherwise continue */ + int err = netlink_parse_error(nl, h, zns, + startup); + if (err == 1) { if (!(h->nlmsg_flags & NLM_F_MULTI)) return 0; continue; - } - - /* Deal with errors that occur because of races - * in link handling */ - if (zns->is_cmd - && ((msg_type == RTM_DELROUTE - && (-errnum == ENODEV - || -errnum == ESRCH)) - || (msg_type == RTM_NEWROUTE - && (-errnum == ENETDOWN - || -errnum == EEXIST)))) { - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug( - "%s: error: %s type=%s(%u), seq=%u, pid=%u", - nl->name, - safe_strerror(-errnum), - nl_msg_type_to_str( - msg_type), - msg_type, - err->msg.nlmsg_seq, - err->msg.nlmsg_pid); - return 0; - } - - /* We see RTM_DELNEIGH when shutting down an - * interface with an IPv4 - * link-local. The kernel should have already - * deleted the neighbor - * so do not log these as an error. - */ - if (msg_type == RTM_DELNEIGH - || (zns->is_cmd && msg_type == RTM_NEWROUTE - && (-errnum == ESRCH - || -errnum == ENETUNREACH))) { - /* This is known to happen in some - * situations, don't log - * as error. - */ - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug( - "%s error: %s, type=%s(%u), seq=%u, pid=%u", - nl->name, - safe_strerror(-errnum), - nl_msg_type_to_str( - msg_type), - msg_type, - err->msg.nlmsg_seq, - err->msg.nlmsg_pid); } else - flog_err( - EC_ZEBRA_UNEXPECTED_MESSAGE, - "%s error: %s, type=%s(%u), seq=%u, pid=%u", - nl->name, - safe_strerror(-errnum), - nl_msg_type_to_str(msg_type), - msg_type, err->msg.nlmsg_seq, - err->msg.nlmsg_pid); - - return -1; + return err; } /* OK we got netlink message. */ @@ -943,30 +974,13 @@ int netlink_parse_info(int (*filter)(struct nlmsghdr *, ns_id_t, int), * startup -> Are we reading in under startup conditions * This is passed through eventually to filter. */ -int netlink_talk_info(int (*filter)(struct nlmsghdr *, ns_id_t, int startup), - struct nlmsghdr *n, - const struct zebra_dplane_info *dp_info, int startup) +static int +netlink_talk_info(int (*filter)(struct nlmsghdr *, ns_id_t, int startup), + struct nlmsghdr *n, const struct zebra_dplane_info *dp_info, + int startup) { - int status = 0; - struct sockaddr_nl snl; - struct iovec iov; - struct msghdr msg; - int save_errno = 0; const struct nlsock *nl; - memset(&snl, 0, sizeof snl); - memset(&iov, 0, sizeof iov); - memset(&msg, 0, sizeof msg); - - iov.iov_base = n; - iov.iov_len = n->nlmsg_len; - msg.msg_name = (void *)&snl; - msg.msg_namelen = sizeof snl; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - snl.nl_family = AF_NETLINK; - nl = &(dp_info->nls); n->nlmsg_seq = nl->seq; n->nlmsg_pid = nl->snl.nl_pid; @@ -978,22 +992,8 @@ int netlink_talk_info(int (*filter)(struct nlmsghdr *, ns_id_t, int startup), n->nlmsg_type, n->nlmsg_len, n->nlmsg_seq, n->nlmsg_flags); - /* Send message to netlink interface. */ - frr_elevate_privs(&zserv_privs) { - status = sendmsg(nl->sock, &msg, 0); - save_errno = errno; - } - - if (IS_ZEBRA_DEBUG_KERNEL_MSGDUMP_SEND) { - zlog_debug("%s: >> netlink message dump [sent]", __func__); - zlog_hexdump(n, n->nlmsg_len); - } - - if (status < 0) { - flog_err_sys(EC_LIB_SOCKET, "netlink_talk sendmsg() error: %s", - safe_strerror(save_errno)); + if (netlink_send_msg(nl, n, n->nlmsg_len) == -1) return -1; - } /* * Get reply from netlink socket. @@ -1026,10 +1026,9 @@ int netlink_talk(int (*filter)(struct nlmsghdr *, ns_id_t, int startup), /* Issue request message to kernel via netlink socket. GET messages * are issued through this interface. */ -int netlink_request(struct nlsock *nl, struct nlmsghdr *n) +int netlink_request(struct nlsock *nl, void *req) { - int ret; - struct sockaddr_nl snl; + struct nlmsghdr *n = (struct nlmsghdr *)req; /* Check netlink socket. */ if (nl->sock < 0) { @@ -1042,29 +1041,347 @@ int netlink_request(struct nlsock *nl, struct nlmsghdr *n) n->nlmsg_pid = nl->snl.nl_pid; n->nlmsg_seq = ++nl->seq; - memset(&snl, 0, sizeof snl); - snl.nl_family = AF_NETLINK; + if (netlink_send_msg(nl, req, n->nlmsg_len) == -1) + return -1; - /* Raise capabilities and send message, then lower capabilities. */ - frr_elevate_privs(&zserv_privs) { - ret = sendto(nl->sock, (void *)n, n->nlmsg_len, 0, - (struct sockaddr *)&snl, sizeof snl); - } + return 0; +} - if (ret < 0) { - zlog_err("%s sendto failed: %s", nl->name, - safe_strerror(errno)); - return -1; +static int nl_batch_read_resp(struct nl_batch *bth) +{ + struct nlmsghdr *h; + struct sockaddr_nl snl; + struct msghdr msg = {}; + int status, seq; + const struct nlsock *nl; + struct zebra_dplane_ctx *ctx; + bool ignore_msg; + + nl = &(bth->zns->nls); + + msg.msg_name = (void *)&snl; + msg.msg_namelen = sizeof(snl); + + /* + * The responses are not batched, so we need to read and process one + * message at a time. + */ + while (true) { + status = netlink_recv_msg(nl, msg, nl_batch_rx_buf, + sizeof(nl_batch_rx_buf)); + if (status == -1 || status == 0) + return status; + + h = (struct nlmsghdr *)nl_batch_rx_buf; + ignore_msg = false; + seq = h->nlmsg_seq; + /* + * Find the corresponding context object. Received responses are + * in the same order as requests we sent, so we can simply + * iterate over the context list and match responses with + * requests at same time. + */ + while (true) { + ctx = dplane_ctx_dequeue(&(bth->ctx_list)); + if (ctx == NULL) + break; + + dplane_ctx_enqueue_tail(bth->ctx_out_q, ctx); + + /* We have found corresponding context object. */ + if (dplane_ctx_get_ns(ctx)->nls.seq == seq) + break; + + /* + * 'update' context objects take two consecutive + * sequence numbers. + */ + if (dplane_ctx_is_update(ctx) + && dplane_ctx_get_ns(ctx)->nls.seq + 1 == seq) { + /* + * This is the situation where we get a response + * to a message that should be ignored. + */ + ignore_msg = true; + break; + } + } + + if (ignore_msg) + continue; + + /* + * We received a message with the sequence number that isn't + * associated with any dplane context object. + */ + if (ctx == NULL) { + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug( + "%s: skipping unassociated response, seq number %d NS %u", + __func__, h->nlmsg_seq, + bth->zns->ns_id); + continue; + } + + if (h->nlmsg_type == NLMSG_ERROR) { + int err = netlink_parse_error(nl, h, bth->zns, 0); + + if (err == -1) + dplane_ctx_set_status( + ctx, ZEBRA_DPLANE_REQUEST_FAILURE); + + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("%s: netlink error message seq=%d", + __func__, h->nlmsg_seq); + continue; + } + + /* + * If we get here then we did not receive neither the ack nor + * the error and instead received some other message in an + * unexpected way. + */ + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("%s: ignoring message type 0x%04x(%s) NS %u", + __func__, h->nlmsg_type, + nl_msg_type_to_str(h->nlmsg_type), + bth->zns->ns_id); } return 0; } +static void nl_batch_reset(struct nl_batch *bth) +{ + bth->buf_head = bth->buf; + bth->curlen = 0; + bth->msgcnt = 0; + bth->zns = NULL; + + TAILQ_INIT(&(bth->ctx_list)); +} + +static void nl_batch_init(struct nl_batch *bth, struct dplane_ctx_q *ctx_out_q) +{ + /* + * If the size of the buffer has changed, free and then allocate a new + * one. + */ + size_t bufsize = + atomic_load_explicit(&nl_batch_bufsize, memory_order_relaxed); + if (bufsize != nl_batch_tx_bufsize) { + if (nl_batch_tx_buf) + XFREE(MTYPE_NL_BUF, nl_batch_tx_buf); + + nl_batch_tx_buf = XCALLOC(MTYPE_NL_BUF, bufsize); + nl_batch_tx_bufsize = bufsize; + } + + bth->buf = nl_batch_tx_buf; + bth->bufsiz = bufsize; + bth->limit = atomic_load_explicit(&nl_batch_send_threshold, + memory_order_relaxed); + + bth->ctx_out_q = ctx_out_q; + + nl_batch_reset(bth); +} + +static void nl_batch_send(struct nl_batch *bth) +{ + struct zebra_dplane_ctx *ctx; + bool err = false; + + if (bth->curlen != 0 && bth->zns != NULL) { + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("%s: %s, batch size=%zu, msg cnt=%zu", + __func__, bth->zns->nls.name, bth->curlen, + bth->msgcnt); + + if (netlink_send_msg(&(bth->zns->nls), bth->buf, bth->curlen) + == -1) + err = true; + + if (!err) { + if (nl_batch_read_resp(bth) == -1) + err = true; + } + } + + /* Move remaining contexts to the outbound queue. */ + while (true) { + ctx = dplane_ctx_dequeue(&(bth->ctx_list)); + if (ctx == NULL) + break; + + if (err) + dplane_ctx_set_status(ctx, + ZEBRA_DPLANE_REQUEST_FAILURE); + + dplane_ctx_enqueue_tail(bth->ctx_out_q, ctx); + } + + nl_batch_reset(bth); +} + +enum netlink_msg_status netlink_batch_add_msg( + struct nl_batch *bth, struct zebra_dplane_ctx *ctx, + ssize_t (*msg_encoder)(struct zebra_dplane_ctx *, void *, size_t), + bool ignore_res) +{ + int seq; + ssize_t size; + struct nlmsghdr *msgh; + + size = (*msg_encoder)(ctx, bth->buf_head, bth->bufsiz - bth->curlen); + + /* + * If there was an error while encoding the message (other than buffer + * overflow) then return an error. + */ + if (size < 0) + return FRR_NETLINK_ERROR; + + /* + * If the message doesn't fit entirely in the buffer then send the batch + * and retry. + */ + if (size == 0) { + nl_batch_send(bth); + size = (*msg_encoder)(ctx, bth->buf_head, + bth->bufsiz - bth->curlen); + /* + * If the message doesn't fit in the empty buffer then just + * return an error. + */ + if (size <= 0) + return FRR_NETLINK_ERROR; + } + + seq = dplane_ctx_get_ns(ctx)->nls.seq; + if (ignore_res) + seq++; + + msgh = (struct nlmsghdr *)bth->buf_head; + msgh->nlmsg_seq = seq; + msgh->nlmsg_pid = dplane_ctx_get_ns(ctx)->nls.snl.nl_pid; + + bth->zns = dplane_ctx_get_ns(ctx); + bth->buf_head = ((char *)bth->buf_head) + size; + bth->curlen += size; + bth->msgcnt++; + + return FRR_NETLINK_QUEUED; +} + +static enum netlink_msg_status nl_put_msg(struct nl_batch *bth, + struct zebra_dplane_ctx *ctx) +{ + if (dplane_ctx_is_skip_kernel(ctx)) + return FRR_NETLINK_SUCCESS; + + switch (dplane_ctx_get_op(ctx)) { + + case DPLANE_OP_ROUTE_INSTALL: + case DPLANE_OP_ROUTE_UPDATE: + case DPLANE_OP_ROUTE_DELETE: + return netlink_put_route_update_msg(bth, ctx); + + case DPLANE_OP_NH_INSTALL: + case DPLANE_OP_NH_UPDATE: + case DPLANE_OP_NH_DELETE: + return netlink_put_nexthop_update_msg(bth, ctx); + + case DPLANE_OP_LSP_INSTALL: + case DPLANE_OP_LSP_UPDATE: + case DPLANE_OP_LSP_DELETE: + return netlink_put_lsp_update_msg(bth, ctx); + + case DPLANE_OP_PW_INSTALL: + case DPLANE_OP_PW_UNINSTALL: + return netlink_put_pw_update_msg(bth, ctx); + + case DPLANE_OP_ADDR_INSTALL: + case DPLANE_OP_ADDR_UNINSTALL: + return netlink_put_address_update_msg(bth, ctx); + + case DPLANE_OP_MAC_INSTALL: + case DPLANE_OP_MAC_DELETE: + return netlink_put_mac_update_msg(bth, ctx); + + case DPLANE_OP_NEIGH_INSTALL: + case DPLANE_OP_NEIGH_UPDATE: + case DPLANE_OP_NEIGH_DELETE: + case DPLANE_OP_VTEP_ADD: + case DPLANE_OP_VTEP_DELETE: + case DPLANE_OP_NEIGH_DISCOVER: + return netlink_put_neigh_update_msg(bth, ctx); + + case DPLANE_OP_RULE_ADD: + case DPLANE_OP_RULE_DELETE: + case DPLANE_OP_RULE_UPDATE: + return netlink_put_rule_update_msg(bth, ctx); + + case DPLANE_OP_SYS_ROUTE_ADD: + case DPLANE_OP_SYS_ROUTE_DELETE: + case DPLANE_OP_ROUTE_NOTIFY: + case DPLANE_OP_LSP_NOTIFY: + return FRR_NETLINK_SUCCESS; + + case DPLANE_OP_NONE: + return FRR_NETLINK_ERROR; + } + + return FRR_NETLINK_ERROR; +} + +void kernel_update_multi(struct dplane_ctx_q *ctx_list) +{ + struct nl_batch batch; + struct zebra_dplane_ctx *ctx; + struct dplane_ctx_q handled_list; + enum netlink_msg_status res; + + TAILQ_INIT(&handled_list); + nl_batch_init(&batch, &handled_list); + + while (true) { + ctx = dplane_ctx_dequeue(ctx_list); + if (ctx == NULL) + break; + + if (batch.zns != NULL + && batch.zns->ns_id != dplane_ctx_get_ns(ctx)->ns_id) + nl_batch_send(&batch); + + /* + * Assume all messages will succeed and then mark only the ones + * that failed. + */ + dplane_ctx_set_status(ctx, ZEBRA_DPLANE_REQUEST_SUCCESS); + + res = nl_put_msg(&batch, ctx); + + dplane_ctx_enqueue_tail(&(batch.ctx_list), ctx); + if (res == FRR_NETLINK_ERROR) + dplane_ctx_set_status(ctx, + ZEBRA_DPLANE_REQUEST_FAILURE); + + if (batch.curlen > batch.limit) + nl_batch_send(&batch); + } + + nl_batch_send(&batch); + + TAILQ_INIT(ctx_list); + dplane_ctx_list_append(ctx_list, &handled_list); +} + /* Exported interface function. This function simply calls netlink_socket (). */ void kernel_init(struct zebra_ns *zns) { - unsigned long groups; + uint32_t groups; #if defined SOL_NETLINK int one, ret; #endif @@ -1085,8 +1402,9 @@ void kernel_init(struct zebra_ns *zns) RTMGRP_IPV6_IFADDR | RTMGRP_IPV4_MROUTE | RTMGRP_NEIGH | - (1 << (RTNLGRP_IPV4_RULE - 1)) | - (1 << (RTNLGRP_IPV6_RULE - 1)); + ((uint32_t) 1 << (RTNLGRP_IPV4_RULE - 1)) | + ((uint32_t) 1 << (RTNLGRP_IPV6_RULE - 1)) | + ((uint32_t) 1 << (RTNLGRP_NEXTHOP - 1)); snprintf(zns->netlink.name, sizeof(zns->netlink.name), "netlink-listen (NS %u)", zns->ns_id); @@ -1140,6 +1458,18 @@ void kernel_init(struct zebra_ns *zns) if (ret < 0) zlog_notice("Registration for extended dp ACK failed : %d %s", errno, safe_strerror(errno)); + + /* + * Trim off the payload of the original netlink message in the + * acknowledgment. This option is available since Linux 4.2, so if + * setsockopt fails, ignore the error. + */ + one = 1; + ret = setsockopt(zns->netlink_dplane.sock, SOL_NETLINK, NETLINK_CAP_ACK, + &one, sizeof(one)); + if (ret < 0) + zlog_notice( + "Registration for reduced ACK packet size failed, probably running an early kernel"); #endif /* Register kernel socket. */ @@ -1156,8 +1486,11 @@ void kernel_init(struct zebra_ns *zns) zns->netlink_dplane.name, safe_strerror(errno), errno); /* Set receive buffer size if it's set from command line */ - if (nl_rcvbufsize) + if (nl_rcvbufsize) { netlink_recvbuf(&zns->netlink, nl_rcvbufsize); + netlink_recvbuf(&zns->netlink_cmd, nl_rcvbufsize); + netlink_recvbuf(&zns->netlink_dplane, nl_rcvbufsize); + } netlink_install_filter(zns->netlink.sock, zns->netlink_cmd.snl.nl_pid, @@ -1173,7 +1506,7 @@ void kernel_init(struct zebra_ns *zns) void kernel_terminate(struct zebra_ns *zns, bool complete) { - THREAD_READ_OFF(zns->t_netlink); + THREAD_OFF(zns->t_netlink); if (zns->netlink.sock >= 0) { close(zns->netlink.sock); diff --git a/zebra/kernel_netlink.h b/zebra/kernel_netlink.h index 076ca5c5c7..696f9be4f6 100644 --- a/zebra/kernel_netlink.h +++ b/zebra/kernel_netlink.h @@ -30,31 +30,62 @@ extern "C" { #define NL_RCV_PKT_BUF_SIZE 32768 #define NL_PKT_BUF_SIZE 8192 +/* + * nl_attr_put - add an attribute to the Netlink message. + * + * Returns true if the attribute could be added to the message (fits into the + * buffer), otherwise false is returned. + */ +extern bool nl_attr_put(struct nlmsghdr *n, unsigned int maxlen, int type, + const void *data, unsigned int alen); +extern bool nl_attr_put16(struct nlmsghdr *n, unsigned int maxlen, int type, + uint16_t data); +extern bool nl_attr_put32(struct nlmsghdr *n, unsigned int maxlen, int type, + uint32_t data); + +/* + * nl_attr_nest - start an attribute nest. + * + * Returns a valid pointer to the beginning of the nest if the attribute + * describing the nest could be added to the message (fits into the buffer), + * otherwise NULL is returned. + */ +extern struct rtattr *nl_attr_nest(struct nlmsghdr *n, unsigned int maxlen, + int type); + +/* + * nl_attr_nest_end - finalize nesting of attributes. + * + * Updates the length field of the attribute header to include the appeneded + * attributes. Returns a total length of the Netlink message. + */ +extern int nl_attr_nest_end(struct nlmsghdr *n, struct rtattr *nest); + +/* + * nl_attr_rtnh - append a rtnexthop record to the Netlink message. + * + * Returns a valid pointer to the rtnexthop struct if it could be added to + * the message (fits into the buffer), otherwise NULL is returned. + */ +extern struct rtnexthop *nl_attr_rtnh(struct nlmsghdr *n, unsigned int maxlen); + +/* + * nl_attr_rtnh_end - finalize adding a rtnexthop record. + * + * Updates the length field of the rtnexthop to include the appeneded + * attributes. + */ +extern void nl_attr_rtnh_end(struct nlmsghdr *n, struct rtnexthop *rtnh); + extern void netlink_parse_rtattr(struct rtattr **tb, int max, struct rtattr *rta, int len); extern void netlink_parse_rtattr_nested(struct rtattr **tb, int max, struct rtattr *rta); -extern int addattr_l(struct nlmsghdr *n, unsigned int maxlen, int type, - const void *data, unsigned int alen); -extern int rta_addattr_l(struct rtattr *rta, unsigned int maxlen, int type, - const void *data, unsigned int alen); -extern int addattr16(struct nlmsghdr *n, unsigned int maxlen, int type, - uint16_t data); -extern int addattr32(struct nlmsghdr *n, unsigned int maxlen, int type, - int data); -extern struct rtattr *addattr_nest(struct nlmsghdr *n, int maxlen, int type); -extern int addattr_nest_end(struct nlmsghdr *n, struct rtattr *nest); -extern struct rtattr *rta_nest(struct rtattr *rta, int maxlen, int type); -extern int rta_nest_end(struct rtattr *rta, struct rtattr *nest); extern const char *nl_msg_type_to_str(uint16_t msg_type); extern const char *nl_rtproto_to_str(uint8_t rtproto); extern const char *nl_family_to_str(uint8_t family); extern const char *nl_rttype_to_str(uint8_t rttype); -#if defined(HANDLE_NETLINK_FUZZING) -extern bool netlink_read; -extern void netlink_read_init(const char *fname); -#endif /* HANDLE_NETLINK_FUZZING */ extern int netlink_parse_info(int (*filter)(struct nlmsghdr *, ns_id_t, int), const struct nlsock *nl, const struct zebra_dplane_info *dp_info, @@ -63,12 +94,48 @@ extern int netlink_talk_filter(struct nlmsghdr *h, ns_id_t ns, int startup); extern int netlink_talk(int (*filter)(struct nlmsghdr *, ns_id_t, int startup), struct nlmsghdr *n, struct nlsock *nl, struct zebra_ns *zns, int startup); -/* Version with 'info' struct only */ -int netlink_talk_info(int (*filter)(struct nlmsghdr *, ns_id_t, int startup), - struct nlmsghdr *n, - const struct zebra_dplane_info *dp_info, int startup); +extern int netlink_request(struct nlsock *nl, void *req); + +enum netlink_msg_status { + FRR_NETLINK_SUCCESS, + FRR_NETLINK_ERROR, + FRR_NETLINK_QUEUED, +}; + +struct nl_batch; + +/* + * netlink_batch_add_msg - add message to the netlink batch using dplane + * context object. + * + * @ctx: Dataplane context + * @msg_encoder: A function that encodes dplane context object into + * netlink message. Should take dplane context object, + * pointer to a buffer and buffer's length as parameters + * and should return -1 on error, 0 on buffer overflow or + * size of the encoded message. + * @ignore_res: Whether the result of this message should be ignored. + * This should be used in some 'update' cases where we + * need to send two messages for one context object. + * + * Return: Status of the message. + */ +extern enum netlink_msg_status netlink_batch_add_msg( + struct nl_batch *bth, struct zebra_dplane_ctx *ctx, + ssize_t (*msg_encoder)(struct zebra_dplane_ctx *, void *, size_t), + bool ignore_res); -extern int netlink_request(struct nlsock *nl, struct nlmsghdr *n); +/* + * Vty/cli apis + */ +extern int netlink_config_write_helper(struct vty *vty); + +/* + * Configure size of the batch buffer and sending threshold. If 'unset', reset + * to default value. + */ +extern void netlink_set_batch_buffer_size(uint32_t size, uint32_t threshold, + bool set); #endif /* HAVE_NETLINK */ diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c index 13d2185b0f..2b6caace8e 100644 --- a/zebra/kernel_socket.c +++ b/zebra/kernel_socket.c @@ -304,12 +304,13 @@ size_t rta_getattr(caddr_t sap, void *destp, size_t destlen) size_t rta_getsdlname(caddr_t sap, void *destp, short *destlen) { struct sockaddr_dl *sdl = (struct sockaddr_dl *)sap; - struct sockaddr *sa = (struct sockaddr *)sap; uint8_t *dest = destp; size_t tlen, copylen; copylen = sdl->sdl_nlen; #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN + struct sockaddr *sa = (struct sockaddr *)sap; + tlen = (sa->sa_len == 0) ? sizeof(ROUNDUP_TYPE) : ROUNDUP(sa->sa_len); #else /* !HAVE_STRUCT_SOCKADDR_SA_LEN */ tlen = SAROUNDUP(sap); @@ -522,7 +523,7 @@ static enum zebra_link_type sdl_to_zebra_link_type(unsigned int sdlt) int ifm_read(struct if_msghdr *ifm) { struct interface *ifp = NULL; - struct sockaddr_dl *sdl; + struct sockaddr_dl *sdl = NULL; char ifname[IFNAMSIZ]; short ifnlen = 0; int maskbit; @@ -555,7 +556,7 @@ int ifm_read(struct if_msghdr *ifm) * is 12 bytes larger than the 32 bit version. */ if (((struct sockaddr *)cp)->sa_family == AF_UNSPEC) - cp = cp + 12; + cp += 12; #endif /* Look up for RTA_IFP and skip others. */ @@ -642,7 +643,7 @@ int ifm_read(struct if_msghdr *ifm) if (ifp == NULL) { /* Interface that zebra was not previously aware of, so * create. */ - ifp = if_create(ifname, VRF_DEFAULT); + ifp = if_create_name(ifname, VRF_DEFAULT); if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug("%s: creating ifp for ifindex %d", __func__, ifm->ifm_index); @@ -711,8 +712,7 @@ int ifm_read(struct if_msghdr *ifm) { if (ifp->ifindex != ifm->ifm_index) { zlog_debug( - "%s: index mismatch, ifname %s, ifp index %d, " - "ifm index %d", + "%s: index mismatch, ifname %s, ifp index %d, ifm index %d", __func__, ifp->name, ifp->ifindex, ifm->ifm_index); return -1; @@ -832,9 +832,7 @@ static void ifam_read_mesg(struct ifa_msghdr *ifm, union sockunion *addr, ? ip_masklen(mask->sin.sin_addr) : ip6_masklen(mask->sin6.sin6_addr); zlog_debug( - "%s: ifindex %d, ifname %s, ifam_addrs {%s}, " - "ifam_flags 0x%x, addr %s/%d broad %s dst %s " - "gateway %s", + "%s: ifindex %d, ifname %s, ifam_addrs {%s}, ifam_flags 0x%x, addr %s/%d broad %s dst %s gateway %s", __func__, ifm->ifam_index, (ifnlen ? ifname : "(nil)"), rtatostr(ifm->ifam_addrs, fbuf, sizeof(fbuf)), @@ -867,6 +865,7 @@ int ifam_read(struct ifa_msghdr *ifam) { struct interface *ifp = NULL; union sockunion addr, mask, brd; + bool dest_same = false; char ifname[INTERFACE_NAMSIZ]; short ifnlen = 0; char isalias = 0; @@ -893,6 +892,10 @@ int ifam_read(struct ifa_msghdr *ifam) rely upon the interface type. */ if (if_is_pointopoint(ifp)) SET_FLAG(flags, ZEBRA_IFA_PEER); + else { + if (memcmp(&addr, &brd, sizeof(addr)) == 0) + dest_same = true; + } #if 0 /* it might seem cute to grab the interface metric here, however @@ -909,13 +912,14 @@ int ifam_read(struct ifa_msghdr *ifam) if (ifam->ifam_type == RTM_NEWADDR) connected_add_ipv4(ifp, flags, &addr.sin.sin_addr, ip_masklen(mask.sin.sin_addr), - &brd.sin.sin_addr, + dest_same ? NULL : &brd.sin.sin_addr, (isalias ? ifname : NULL), METRIC_MAX); else connected_delete_ipv4(ifp, flags, &addr.sin.sin_addr, ip_masklen(mask.sin.sin_addr), - &brd.sin.sin_addr); + dest_same ? NULL + : &brd.sin.sin_addr); break; case AF_INET6: /* Unset interface index from link-local address when IPv6 stack @@ -977,8 +981,7 @@ static int rtm_read_mesg(struct rt_msghdr *rtm, union sockunion *dest, /* rt_msghdr version check. */ if (rtm->rtm_version != RTM_VERSION) flog_warn(EC_ZEBRA_RTM_VERSION_MISMATCH, - "Routing message version different %d should be %d." - "This may cause problem\n", + "Routing message version different %d should be %d.This may cause problem\n", rtm->rtm_version, RTM_VERSION); /* Be sure structure is cleared */ @@ -1138,14 +1141,17 @@ void rtm_read(struct rt_msghdr *rtm) */ if (rtm->rtm_type == RTM_CHANGE) rib_delete(afi, SAFI_UNICAST, VRF_DEFAULT, ZEBRA_ROUTE_KERNEL, - 0, zebra_flags, &p, NULL, NULL, 0, 0, 0, true); + 0, zebra_flags, &p, NULL, NULL, 0, RT_TABLE_MAIN, 0, + 0, true, false); if (rtm->rtm_type == RTM_GET || rtm->rtm_type == RTM_ADD || rtm->rtm_type == RTM_CHANGE) rib_add(afi, SAFI_UNICAST, VRF_DEFAULT, ZEBRA_ROUTE_KERNEL, 0, - zebra_flags, &p, NULL, &nh, 0, 0, 0, 0, 0); + zebra_flags, &p, NULL, &nh, 0, RT_TABLE_MAIN, + 0, 0, 0, 0); else rib_delete(afi, SAFI_UNICAST, VRF_DEFAULT, ZEBRA_ROUTE_KERNEL, - 0, zebra_flags, &p, NULL, &nh, 0, 0, 0, true); + 0, zebra_flags, &p, NULL, &nh, 0, RT_TABLE_MAIN, 0, + 0, true, false); } /* Interface function for the kernel routing table updates. Support @@ -1367,7 +1373,7 @@ static int kernel_read(struct thread *thread) /* Fetch routing socket. */ sock = THREAD_FD(thread); - nbytes = read(sock, &buf, sizeof buf); + nbytes = read(sock, &buf, sizeof(buf)); if (nbytes <= 0) { if (nbytes < 0 && errno != EWOULDBLOCK && errno != EAGAIN) @@ -1389,7 +1395,7 @@ static int kernel_read(struct thread *thread) */ if (rtm->rtm_msglen != nbytes) { zlog_debug( - "kernel_read: rtm->rtm_msglen %d, nbytes %d, type %d\n", + "kernel_read: rtm->rtm_msglen %d, nbytes %d, type %d", rtm->rtm_msglen, nbytes, rtm->rtm_type); return -1; } @@ -1423,7 +1429,7 @@ static int kernel_read(struct thread *thread) /* Make routing socket. */ static void routing_socket(struct zebra_ns *zns) { - frr_elevate_privs(&zserv_privs) { + frr_with_privs(&zserv_privs) { routing_sock = ns_socket(AF_ROUTE, SOCK_RAW, 0, zns->ns_id); dplane_routing_sock = @@ -1464,4 +1470,99 @@ void kernel_terminate(struct zebra_ns *zns, bool complete) return; } +void kernel_update_multi(struct dplane_ctx_q *ctx_list) +{ + struct zebra_dplane_ctx *ctx; + struct dplane_ctx_q handled_list; + enum zebra_dplane_result res; + + TAILQ_INIT(&handled_list); + + while (true) { + ctx = dplane_ctx_dequeue(ctx_list); + if (ctx == NULL) + break; + + /* + * A previous provider plugin may have asked to skip the + * kernel update. + */ + if (dplane_ctx_is_skip_kernel(ctx)) { + res = ZEBRA_DPLANE_REQUEST_SUCCESS; + goto skip_one; + } + + switch (dplane_ctx_get_op(ctx)) { + + case DPLANE_OP_ROUTE_INSTALL: + case DPLANE_OP_ROUTE_UPDATE: + case DPLANE_OP_ROUTE_DELETE: + res = kernel_route_update(ctx); + break; + + case DPLANE_OP_NH_INSTALL: + case DPLANE_OP_NH_UPDATE: + case DPLANE_OP_NH_DELETE: + res = kernel_nexthop_update(ctx); + break; + + case DPLANE_OP_LSP_INSTALL: + case DPLANE_OP_LSP_UPDATE: + case DPLANE_OP_LSP_DELETE: + res = kernel_lsp_update(ctx); + break; + + case DPLANE_OP_PW_INSTALL: + case DPLANE_OP_PW_UNINSTALL: + res = kernel_pw_update(ctx); + break; + + case DPLANE_OP_ADDR_INSTALL: + case DPLANE_OP_ADDR_UNINSTALL: + res = kernel_address_update_ctx(ctx); + break; + + case DPLANE_OP_MAC_INSTALL: + case DPLANE_OP_MAC_DELETE: + res = kernel_mac_update_ctx(ctx); + break; + + case DPLANE_OP_NEIGH_INSTALL: + case DPLANE_OP_NEIGH_UPDATE: + case DPLANE_OP_NEIGH_DELETE: + case DPLANE_OP_VTEP_ADD: + case DPLANE_OP_VTEP_DELETE: + case DPLANE_OP_NEIGH_DISCOVER: + res = kernel_neigh_update_ctx(ctx); + break; + + case DPLANE_OP_RULE_ADD: + case DPLANE_OP_RULE_DELETE: + case DPLANE_OP_RULE_UPDATE: + res = kernel_pbr_rule_update(ctx); + break; + + /* Ignore 'notifications' - no-op */ + case DPLANE_OP_SYS_ROUTE_ADD: + case DPLANE_OP_SYS_ROUTE_DELETE: + case DPLANE_OP_ROUTE_NOTIFY: + case DPLANE_OP_LSP_NOTIFY: + res = ZEBRA_DPLANE_REQUEST_SUCCESS; + break; + + default: + res = ZEBRA_DPLANE_REQUEST_FAILURE; + break; + } + + skip_one: + dplane_ctx_set_status(ctx, res); + + dplane_ctx_enqueue_tail(&handled_list, ctx); + } + + TAILQ_INIT(ctx_list); + dplane_ctx_list_append(ctx_list, &handled_list); +} + #endif /* !HAVE_NETLINK */ diff --git a/zebra/label_manager.c b/zebra/label_manager.c index 16d45836e0..d312a661f3 100644 --- a/zebra/label_manager.c +++ b/zebra/label_manager.c @@ -4,7 +4,7 @@ * Copyright (C) 2017 by Bingen Eguzkitza, * Volta Networks Inc. * - * This file is part of FreeRangeRouting (FRR) + * This file is part of FRRouting (FRR) * * FRR is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -38,290 +38,63 @@ #include "zebra/zebra_router.h" #include "zebra/label_manager.h" #include "zebra/zebra_errors.h" +#include "zebra/zapi_msg.h" +#include "zebra/debug.h" #define CONNECTION_DELAY 5 struct label_manager lbl_mgr; -extern struct zebra_privs_t zserv_privs; - DEFINE_MGROUP(LBL_MGR, "Label Manager"); DEFINE_MTYPE_STATIC(LBL_MGR, LM_CHUNK, "Label Manager Chunk"); -/* In case this zebra daemon is not acting as label manager, - * it will be a proxy to relay messages to external label manager - * This zclient thus is to connect to it +/* define hooks for the basic API, so that it can be specialized or served + * externally */ -static struct stream *obuf; -static struct zclient *zclient; -bool lm_is_external; - -static void delete_label_chunk(void *val) -{ - XFREE(MTYPE_LM_CHUNK, val); -} - -static int relay_response_back(void) -{ - int ret = 0; - struct stream *src, *dst; - uint16_t size = 0; - uint8_t marker; - uint8_t version; - vrf_id_t vrf_id; - uint16_t resp_cmd; - uint8_t proto; - const char *proto_str; - unsigned short instance; - struct zserv *zserv; - - /* input buffer with msg from label manager */ - src = zclient->ibuf; - - stream_reset(src); - - /* parse header */ - ret = zclient_read_header(src, zclient->sock, &size, &marker, &version, - &vrf_id, &resp_cmd); - if (ret < 0 && errno != EAGAIN) { - flog_err(EC_ZEBRA_LM_RESPONSE, - "Error reading Label Manager response: %s", - strerror(errno)); - return -1; - } - - /* do not relay a msg that has nothing to do with LM */ - switch (resp_cmd) { - case ZEBRA_LABEL_MANAGER_CONNECT: - case ZEBRA_LABEL_MANAGER_CONNECT_ASYNC: /* should not be seen */ - case ZEBRA_GET_LABEL_CHUNK: - case ZEBRA_RELEASE_LABEL_CHUNK: - break; - default: - zlog_debug("Not relaying '%s' response (size %d) from LM", - zserv_command_string(resp_cmd), size); - return -1; - } - - zlog_debug("Received '%s' response (size %d) from LM", - zserv_command_string(resp_cmd), size); - - if (size == 0) - return -1; - - /* Get the 'proto' field of the message */ - proto = stream_getc(src); - - /* Get the 'instance' field of the message */ - instance = stream_getw(src); - - proto_str = zebra_route_string(proto); - - /* lookup the client to relay the msg to */ - zserv = zserv_find_client(proto, instance); - if (!zserv) { - flog_err( - EC_ZEBRA_LM_NO_SUCH_CLIENT, - "Error relaying LM response: can't find client %s, instance %u", - proto_str, instance); - return -1; - } - zlog_debug("Found client to relay LM response to client %s instance %u", - proto_str, instance); - - /* copy msg into output buffer */ - dst = obuf; - stream_copy(dst, src); - - /* send response back */ - ret = writen(zserv->sock, dst->data, stream_get_endp(dst)); - if (ret <= 0) { - flog_err(EC_ZEBRA_LM_RELAY_FAILED, - "Error relaying LM response to %s instance %u: %s", - proto_str, instance, strerror(errno)); - return -1; - } - zlog_debug("Relayed LM response (%d bytes) to %s instance %u", ret, - proto_str, instance); - return 0; -} - -static int lm_zclient_read(struct thread *t) -{ - int ret; - - zclient->t_read = NULL; - - /* read response and send it back */ - ret = relay_response_back(); - - /* re-arm read */ - thread_add_read(zclient->master, lm_zclient_read, NULL, - zclient->sock, &zclient->t_read); - return ret; -} - -static int reply_error(int cmd, struct zserv *zserv, vrf_id_t vrf_id) +DEFINE_HOOK(lm_client_connect, (struct zserv *client, vrf_id_t vrf_id), + (client, vrf_id)); +DEFINE_HOOK(lm_client_disconnect, (struct zserv *client), (client)); +DEFINE_HOOK(lm_get_chunk, + (struct label_manager_chunk * *lmc, struct zserv *client, + uint8_t keep, uint32_t size, uint32_t base, vrf_id_t vrf_id), + (lmc, client, keep, size, base, vrf_id)); +DEFINE_HOOK(lm_release_chunk, + (struct zserv *client, uint32_t start, uint32_t end), + (client, start, end)); +DEFINE_HOOK(lm_cbs_inited, (), ()); + +/* define wrappers to be called in zapi_msg.c (as hooks must be called in + * source file where they were defined) + */ +void lm_client_connect_call(struct zserv *client, vrf_id_t vrf_id) { - int ret; - struct stream *s; - - s = stream_new(ZEBRA_MAX_PACKET_SIZ); - - zclient_create_header(s, cmd, vrf_id); - - /* proto */ - stream_putc(s, zserv->proto); - /* instance */ - stream_putw(s, zserv->instance); - /* result */ - stream_putc(s, 1); - - /* Write packet size. */ - stream_putw_at(s, 0, stream_get_endp(s)); - - ret = writen(zserv->sock, s->data, stream_get_endp(s)); - - stream_free(s); - return ret; + hook_call(lm_client_connect, client, vrf_id); } -/** - * Receive a request to get or release a label chunk and forward it to external - * label manager. - * - * It's called from zserv in case it's not an actual label manager, but just a - * proxy. - * - * @param cmd Type of request (connect, get or release) - * @param zserv - * @return 0 on success, -1 otherwise - */ -int zread_relay_label_manager_request(int cmd, struct zserv *zserv, - struct stream *msg, vrf_id_t vrf_id) +void lm_get_chunk_call(struct label_manager_chunk **lmc, struct zserv *client, + uint8_t keep, uint32_t size, uint32_t base, + vrf_id_t vrf_id) { - struct stream *dst; - int ret = 0; - uint8_t proto; - const char *proto_str; - unsigned short instance; - - if (zclient->sock < 0) { - flog_err(EC_ZEBRA_LM_NO_SOCKET, - "Unable to relay LM request: no socket"); - reply_error(cmd, zserv, vrf_id); - return -1; - } - - /* peek msg to get proto and instance id. This zebra, which acts as - * a proxy needs to have such values for each client in order to - * relay responses back to it. - */ - - /* Get the 'proto' field of incoming msg */ - proto = stream_getc(msg); - - /* Get the 'instance' field of incoming msg */ - instance = stream_getw(msg); - - /* stringify proto */ - proto_str = zebra_route_string(proto); - - /* check & set client proto if unset */ - if (zserv->proto && zserv->proto != proto) { - flog_warn(EC_ZEBRAING_LM_PROTO_MISMATCH, - "Client proto(%u) != msg proto(%u)", zserv->proto, - proto); - return -1; - } - - /* check & set client instance if unset */ - if (zserv->instance && zserv->instance != instance) { - flog_err(EC_ZEBRA_LM_BAD_INSTANCE, - "Client instance(%u) != msg instance(%u)", - zserv->instance, instance); - return -1; - } - - /* recall proto and instance */ - zserv->instance = instance; - zserv->proto = proto; - - /* in case there's any incoming message enqueued, read and forward it */ - if (zserv->is_synchronous) - while (ret == 0) - ret = relay_response_back(); - - /* get the msg buffer used toward the 'master' Label Manager */ - dst = zclient->obuf; - - /* copy the message */ - stream_copy(dst, msg); - - /* Send request to external label manager */ - ret = writen(zclient->sock, dst->data, stream_get_endp(dst)); - if (ret <= 0) { - flog_err(EC_ZEBRA_LM_RELAY_FAILED, - "Error relaying LM request from %s instance %u: %s", - proto_str, instance, strerror(errno)); - reply_error(cmd, zserv, vrf_id); - return -1; - } - zlog_debug("Relayed LM request (%d bytes) from %s instance %u", ret, - proto_str, instance); - - - /* Release label chunk has no response */ - if (cmd == ZEBRA_RELEASE_LABEL_CHUNK) - return 0; - - /* make sure we listen to the response */ - if (!zclient->t_read) - thread_add_read(zclient->master, lm_zclient_read, NULL, - zclient->sock, &zclient->t_read); - - return 0; + hook_call(lm_get_chunk, lmc, client, keep, size, base, vrf_id); } - -static int lm_zclient_connect(struct thread *t) +void lm_release_chunk_call(struct zserv *client, uint32_t start, uint32_t end) { - zclient->t_connect = NULL; - - if (zclient->sock >= 0) - return 0; - - if (zclient_socket_connect(zclient) < 0) { - flog_err(EC_ZEBRA_LM_CLIENT_CONNECTION_FAILED, - "Error connecting synchronous zclient!"); - thread_add_timer(zrouter.master, lm_zclient_connect, zclient, - CONNECTION_DELAY, &zclient->t_connect); - return -1; - } - - /* make socket non-blocking */ - (void)set_nonblocking(zclient->sock); - - return 0; + hook_call(lm_release_chunk, client, start, end); } -/** - * Function to initialize zclient in case this is not an actual - * label manager, but just a proxy to an external one. - * - * @param lm_zserv_path Path to zserv socket of external label manager - */ -static void lm_zclient_init(char *lm_zserv_path) +/* forward declarations of the static functions to be used for some hooks */ +static int label_manager_connect(struct zserv *client, vrf_id_t vrf_id); +static int label_manager_disconnect(struct zserv *client); +static int label_manager_get_chunk(struct label_manager_chunk **lmc, + struct zserv *client, uint8_t keep, + uint32_t size, uint32_t base, + vrf_id_t vrf_id); +static int label_manager_release_label_chunk(struct zserv *client, + uint32_t start, uint32_t end); + +void delete_label_chunk(void *val) { - if (lm_zserv_path) - frr_zclient_addr(&zclient_addr, &zclient_addr_len, - lm_zserv_path); - - /* Set default values. */ - zclient = zclient_new(zrouter.master, &zclient_options_default); - zclient->privs = &zserv_privs; - zclient->sock = -1; - zclient->t_connect = NULL; - lm_zclient_connect(NULL); + XFREE(MTYPE_LM_CHUNK, val); } /** @@ -336,53 +109,188 @@ static void lm_zclient_init(char *lm_zserv_path) */ int release_daemon_label_chunks(struct zserv *client) { - uint8_t proto = client->proto; - uint16_t instance = client->instance; struct listnode *node; struct label_manager_chunk *lmc; int count = 0; int ret; + if (IS_ZEBRA_DEBUG_PACKET) + zlog_debug("%s: Releasing chunks for client proto %s, instance %d, session %u", + __func__, zebra_route_string(client->proto), + client->instance, client->session_id); + for (ALL_LIST_ELEMENTS_RO(lbl_mgr.lc_list, node, lmc)) { - if (lmc->proto == proto && lmc->instance == instance - && lmc->keep == 0) { + if (lmc->proto == client->proto && + lmc->instance == client->instance && + lmc->session_id == client->session_id && lmc->keep == 0) { ret = release_label_chunk(lmc->proto, lmc->instance, + lmc->session_id, lmc->start, lmc->end); if (ret == 0) count++; } } - zlog_debug("%s: Released %d label chunks", __func__, count); + if (IS_ZEBRA_DEBUG_PACKET) + zlog_debug("%s: Released %d label chunks", __func__, count); return count; } +int lm_client_disconnect_cb(struct zserv *client) +{ + hook_call(lm_client_disconnect, client); + return 0; +} + +void lm_hooks_register(void) +{ + hook_register(lm_client_connect, label_manager_connect); + hook_register(lm_client_disconnect, label_manager_disconnect); + hook_register(lm_get_chunk, label_manager_get_chunk); + hook_register(lm_release_chunk, label_manager_release_label_chunk); +} +void lm_hooks_unregister(void) +{ + hook_unregister(lm_client_connect, label_manager_connect); + hook_unregister(lm_client_disconnect, label_manager_disconnect); + hook_unregister(lm_get_chunk, label_manager_get_chunk); + hook_unregister(lm_release_chunk, label_manager_release_label_chunk); +} + /** * Init label manager (or proxy to an external one) */ -void label_manager_init(char *lm_zserv_path) +void label_manager_init(void) +{ + lbl_mgr.lc_list = list_new(); + lbl_mgr.lc_list->del = delete_label_chunk; + hook_register(zserv_client_close, lm_client_disconnect_cb); + + /* register default hooks for the label manager actions */ + lm_hooks_register(); + + /* notify any external module that we are done */ + hook_call(lm_cbs_inited); +} + +/* alloc and fill a label chunk */ +struct label_manager_chunk *create_label_chunk(uint8_t proto, + unsigned short instance, + uint32_t session_id, + uint8_t keep, uint32_t start, + uint32_t end) +{ + /* alloc chunk, fill it and return it */ + struct label_manager_chunk *lmc = + XCALLOC(MTYPE_LM_CHUNK, sizeof(struct label_manager_chunk)); + + lmc->start = start; + lmc->end = end; + lmc->proto = proto; + lmc->instance = instance; + lmc->session_id = session_id; + lmc->keep = keep; + + return lmc; +} + +/* attempt to get a specific label chunk */ +static struct label_manager_chunk * +assign_specific_label_chunk(uint8_t proto, unsigned short instance, + uint32_t session_id, uint8_t keep, uint32_t size, + uint32_t base) { - /* this is an actual label manager */ - if (!lm_zserv_path) { - zlog_debug("Initializing internal label manager"); - lm_is_external = false; - lbl_mgr.lc_list = list_new(); - lbl_mgr.lc_list->del = delete_label_chunk; - } else { /* it's acting just as a proxy */ - zlog_debug("Initializing external label manager at %s", - lm_zserv_path); - lm_is_external = true; - lm_zclient_init(lm_zserv_path); + struct label_manager_chunk *lmc; + struct listnode *node, *next = NULL; + struct listnode *first_node = NULL; + struct listnode *last_node = NULL; + struct listnode *insert_node = NULL; + + /* precompute last label from base and size */ + uint32_t end = base + size - 1; + + /* sanities */ + if ((base < MPLS_LABEL_UNRESERVED_MIN) + || (end > MPLS_LABEL_UNRESERVED_MAX)) { + zlog_err("Invalid LM request arguments: base: %u, size: %u", + base, size); + return NULL; } - obuf = stream_new(ZEBRA_MAX_PACKET_SIZ); + /* Scan the existing chunks to see if the requested range of labels + * falls inside any of such chunks */ + for (ALL_LIST_ELEMENTS_RO(lbl_mgr.lc_list, node, lmc)) { + + /* skip chunks for labels < base */ + if (base > lmc->end) + continue; + + /* requested range is not covered by any existing, free chunk. + * Therefore, need to insert a chunk */ + if ((end < lmc->start) && !first_node) { + insert_node = node; + break; + } + + if (!first_node) + first_node = node; - hook_register(zserv_client_close, release_daemon_label_chunks); + /* if chunk is used, cannot honor request */ + if (lmc->proto != NO_PROTO) + return NULL; + + if (end <= lmc->end) { + last_node = node; + break; + } + } + + /* insert chunk between existing chunks */ + if (insert_node) { + lmc = create_label_chunk(proto, instance, session_id, keep, + base, end); + listnode_add_before(lbl_mgr.lc_list, insert_node, lmc); + return lmc; + } + + if (first_node) { + /* get node past the last one, if there */ + if (last_node) + last_node = listnextnode(last_node); + + /* delete node coming after the above chunk whose labels are + * included in the previous one */ + for (node = first_node; node && (node != last_node); + node = next) { + struct label_manager_chunk *death; + + next = listnextnode(node); + death = listgetdata(node); + list_delete_node(lbl_mgr.lc_list, node); + delete_label_chunk(death); + } + + lmc = create_label_chunk(proto, instance, session_id, keep, + base, end); + if (last_node) + listnode_add_before(lbl_mgr.lc_list, last_node, lmc); + else + listnode_add(lbl_mgr.lc_list, lmc); + + return lmc; + } else { + /* create a new chunk past all the existing ones and link at + * tail */ + lmc = create_label_chunk(proto, instance, session_id, keep, + base, end); + listnode_add(lbl_mgr.lc_list, lmc); + return lmc; + } } /** - * Core function, assigns label cunks + * Core function, assigns label chunks * * It first searches through the list to check if there's one available * (previously released). Otherwise it creates and assigns a new one @@ -390,15 +298,27 @@ void label_manager_init(char *lm_zserv_path) * @param proto Daemon protocol of client, to identify the owner * @param instance Instance, to identify the owner * @param keep If set, avoid garbage collection - * @para size Size of the label chunk - * @return Pointer to the assigned label chunk + * @param size Size of the label chunk + * @param base Desired starting label of the chunk; if MPLS_LABEL_BASE_ANY it does not apply + * @return Pointer to the assigned label chunk, or NULL if the request could not be satisfied */ struct label_manager_chunk *assign_label_chunk(uint8_t proto, unsigned short instance, - uint8_t keep, uint32_t size) + uint32_t session_id, + uint8_t keep, uint32_t size, + uint32_t base) { struct label_manager_chunk *lmc; struct listnode *node; + uint32_t prev_end = 0; + + /* handle chunks request with a specific base label */ + if (base != MPLS_LABEL_BASE_ANY) + return assign_specific_label_chunk(proto, instance, session_id, + keep, size, base); + + /* appease scan-build, who gets confused by the use of macros */ + assert(lbl_mgr.lc_list); /* first check if there's one available */ for (ALL_LIST_ELEMENTS_RO(lbl_mgr.lc_list, node, lmc)) { @@ -406,38 +326,67 @@ struct label_manager_chunk *assign_label_chunk(uint8_t proto, && lmc->end - lmc->start + 1 == size) { lmc->proto = proto; lmc->instance = instance; + lmc->session_id = session_id; lmc->keep = keep; return lmc; } + /* check if we hadve a "hole" behind us that we can squeeze into + */ + if ((lmc->start > prev_end) + && (lmc->start - prev_end >= size)) { + lmc = create_label_chunk(proto, instance, session_id, + keep, prev_end + 1, + prev_end + size); + listnode_add_before(lbl_mgr.lc_list, node, lmc); + return lmc; + } + prev_end = lmc->end; } /* otherwise create a new one */ - lmc = XCALLOC(MTYPE_LM_CHUNK, sizeof(struct label_manager_chunk)); + uint32_t start_free; if (list_isempty(lbl_mgr.lc_list)) - lmc->start = MPLS_LABEL_UNRESERVED_MIN; + start_free = MPLS_LABEL_UNRESERVED_MIN; else - lmc->start = ((struct label_manager_chunk *)listgetdata( + start_free = ((struct label_manager_chunk *)listgetdata( listtail(lbl_mgr.lc_list))) ->end + 1; - if (lmc->start > MPLS_LABEL_UNRESERVED_MAX - size + 1) { + + if (start_free > MPLS_LABEL_UNRESERVED_MAX - size + 1) { flog_err(EC_ZEBRA_LM_EXHAUSTED_LABELS, - "Reached max labels. Start: %u, size: %u", lmc->start, + "Reached max labels. Start: %u, size: %u", start_free, size); - XFREE(MTYPE_LM_CHUNK, lmc); return NULL; } - lmc->end = lmc->start + size - 1; - lmc->proto = proto; - lmc->instance = instance; - lmc->keep = keep; - listnode_add(lbl_mgr.lc_list, lmc); + /* create chunk and link at tail */ + lmc = create_label_chunk(proto, instance, session_id, keep, start_free, + start_free + size - 1); + listnode_add(lbl_mgr.lc_list, lmc); return lmc; } /** - * Core function, release no longer used label cunks + * Release label chunks from a client. + * + * Called on client disconnection or reconnection. It only releases chunks + * with empty keep value. + * + * @param client Client zapi session + * @param start First label of the chunk + * @param end Last label of the chunk + * @return 0 on success + */ +static int label_manager_release_label_chunk(struct zserv *client, + uint32_t start, uint32_t end) +{ + return release_label_chunk(client->proto, client->instance, + client->session_id, start, end); +} + +/** + * Core function, release no longer used label chunks * * @param proto Daemon protocol of client, to identify the owner * @param instance Instance, to identify the owner @@ -445,28 +394,31 @@ struct label_manager_chunk *assign_label_chunk(uint8_t proto, * @param end Last label of the chunk * @return 0 on success, -1 otherwise */ -int release_label_chunk(uint8_t proto, unsigned short instance, uint32_t start, - uint32_t end) +int release_label_chunk(uint8_t proto, unsigned short instance, + uint32_t session_id, uint32_t start, uint32_t end) { struct listnode *node; struct label_manager_chunk *lmc; int ret = -1; /* check that size matches */ - zlog_debug("Releasing label chunk: %u - %u", start, end); + if (IS_ZEBRA_DEBUG_PACKET) + zlog_debug("Releasing label chunk: %u - %u", start, end); /* find chunk and disown */ for (ALL_LIST_ELEMENTS_RO(lbl_mgr.lc_list, node, lmc)) { if (lmc->start != start) continue; if (lmc->end != end) continue; - if (lmc->proto != proto || lmc->instance != instance) { + if (lmc->proto != proto || lmc->instance != instance || + lmc->session_id != session_id) { flog_err(EC_ZEBRA_LM_DAEMON_MISMATCH, "%s: Daemon mismatch!!", __func__); continue; } lmc->proto = NO_PROTO; lmc->instance = 0; + lmc->session_id = 0; lmc->keep = 0; ret = 0; break; @@ -478,9 +430,64 @@ int release_label_chunk(uint8_t proto, unsigned short instance, uint32_t start, return ret; } +/* default functions to be called on hooks */ +static int label_manager_connect(struct zserv *client, vrf_id_t vrf_id) +{ + /* + * Release previous labels of same protocol and instance. + * This is done in case it restarted from an unexpected shutdown. + */ + release_daemon_label_chunks(client); + return zsend_label_manager_connect_response(client, vrf_id, 0); +} +static int label_manager_disconnect(struct zserv *client) +{ + release_daemon_label_chunks(client); + return 0; +} +static int label_manager_get_chunk(struct label_manager_chunk **lmc, + struct zserv *client, uint8_t keep, + uint32_t size, uint32_t base, + vrf_id_t vrf_id) +{ + *lmc = assign_label_chunk(client->proto, client->instance, + client->session_id, keep, size, base); + return lm_get_chunk_response(*lmc, client, vrf_id); +} + +/* Respond to a connect request */ +int lm_client_connect_response(uint8_t proto, uint16_t instance, + uint32_t session_id, vrf_id_t vrf_id, + uint8_t result) +{ + struct zserv *client = zserv_find_client_session(proto, instance, + session_id); + if (!client) { + zlog_err("%s: could not find client for daemon %s instance %u session %u", + __func__, zebra_route_string(proto), instance, + session_id); + return 1; + } + return zsend_label_manager_connect_response(client, vrf_id, result); +} + +/* Respond to a get_chunk request */ +int lm_get_chunk_response(struct label_manager_chunk *lmc, struct zserv *client, + vrf_id_t vrf_id) +{ + if (!lmc) + flog_err(EC_ZEBRA_LM_CANNOT_ASSIGN_CHUNK, + "Unable to assign Label Chunk to %s instance %u", + zebra_route_string(client->proto), client->instance); + else if (IS_ZEBRA_DEBUG_PACKET) + zlog_debug("Assigned Label Chunk %u - %u to %s instance %u", + lmc->start, lmc->end, + zebra_route_string(client->proto), client->instance); + + return zsend_assign_label_chunk_response(client, vrf_id, lmc); +} void label_manager_close(void) { list_delete(&lbl_mgr.lc_list); - stream_free(obuf); } diff --git a/zebra/label_manager.h b/zebra/label_manager.h index 3ea89fbfc3..82154982c2 100644 --- a/zebra/label_manager.h +++ b/zebra/label_manager.h @@ -4,7 +4,7 @@ * Copyright (C) 2017 by Bingen Eguzkitza, * Volta Networks Inc. * - * This file is part of FreeRangeRouting (FRR) + * This file is part of FRRouting (FRR) * * FRR is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -28,6 +28,7 @@ #include "lib/linklist.h" #include "lib/thread.h" +#include "lib/hook.h" #include "zebra/zserv.h" @@ -39,24 +40,72 @@ extern "C" { /* * Label chunk struct - * Client daemon which the chunk belongs to can be identified by either - * proto (daemon protocol) + instance. + * Client daemon which the chunk belongs to can be identified by a tuple of: + * proto (daemon protocol) + instance + zapi session_id * If the client then passes a non-empty value to keep field when it requests * for chunks, the chunks won't be garbage collected and the client will be - * responsible of its release. + * responsible for releasing them. * Otherwise, if the keep field is not set (value 0) for the chunk, it will be * automatically released when the client disconnects or when it reconnects * (in case it died unexpectedly, we can know it's the same because it will have - * the same proto and instance values) + * the same proto+instance+session values) */ struct label_manager_chunk { uint8_t proto; unsigned short instance; + uint32_t session_id; uint8_t keep; uint32_t start; /* First label of the chunk */ uint32_t end; /* Last label of the chunk */ }; +/* declare hooks for the basic API, so that it can be specialized or served + * externally. Also declare a hook when those functions have been registered, + * so that any external module wanting to replace those can react + */ + +DECLARE_HOOK(lm_client_connect, (struct zserv *client, vrf_id_t vrf_id), + (client, vrf_id)); +DECLARE_HOOK(lm_client_disconnect, (struct zserv *client), (client)); +DECLARE_HOOK(lm_get_chunk, + (struct label_manager_chunk * *lmc, struct zserv *client, + uint8_t keep, uint32_t size, uint32_t base, vrf_id_t vrf_id), + (lmc, client, keep, size, base, vrf_id)); +DECLARE_HOOK(lm_release_chunk, + (struct zserv *client, uint32_t start, uint32_t end), + (client, start, end)); +DECLARE_HOOK(lm_cbs_inited, (), ()); + + +/* declare wrappers to be called in zapi_msg.c (as hooks must be called in + * source file where they were defined) + */ +void lm_client_connect_call(struct zserv *client, vrf_id_t vrf_id); +void lm_get_chunk_call(struct label_manager_chunk **lmc, struct zserv *client, + uint8_t keep, uint32_t size, uint32_t base, + vrf_id_t vrf_id); +void lm_release_chunk_call(struct zserv *client, uint32_t start, + uint32_t end); + +/* API for an external LM to return responses for requests */ +int lm_client_connect_response(uint8_t proto, uint16_t instance, + uint32_t session_id, vrf_id_t vrf_id, + uint8_t result); +int lm_get_chunk_response(struct label_manager_chunk *lmc, struct zserv *client, + vrf_id_t vrf_id); + +/* convenience function to allocate an lmc to be consumed by the above API */ +struct label_manager_chunk *create_label_chunk(uint8_t proto, + unsigned short instance, + uint32_t session_id, + uint8_t keep, uint32_t start, + uint32_t end); +void delete_label_chunk(void *val); + +/* register/unregister callbacks for hooks */ +void lm_hooks_register(void); +void lm_hooks_unregister(void); + /* * Main label manager struct * Holds a linked list of label chunks. @@ -65,16 +114,15 @@ struct label_manager { struct list *lc_list; }; -bool lm_is_external; - -int zread_relay_label_manager_request(int cmd, struct zserv *zserv, - struct stream *msg, vrf_id_t vrf_id); -void label_manager_init(char *lm_zserv_path); +void label_manager_init(void); struct label_manager_chunk *assign_label_chunk(uint8_t proto, unsigned short instance, - uint8_t keep, uint32_t size); -int release_label_chunk(uint8_t proto, unsigned short instance, uint32_t start, - uint32_t end); + uint32_t session_id, + uint8_t keep, uint32_t size, + uint32_t base); +int release_label_chunk(uint8_t proto, unsigned short instance, + uint32_t session_id, uint32_t start, uint32_t end); +int lm_client_disconnect_cb(struct zserv *client); int release_daemon_label_chunks(struct zserv *client); void label_manager_close(void); diff --git a/zebra/main.c b/zebra/main.c index 184e798bd0..f320c445c7 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -27,17 +27,15 @@ #include "filter.h" #include "memory.h" #include "zebra_memory.h" -#include "memory_vty.h" #include "prefix.h" #include "log.h" #include "plist.h" #include "privs.h" #include "sigevent.h" #include "vrf.h" -#include "logicalrouter.h" #include "libfrr.h" #include "routemap.h" -#include "frr_pthread.h" +#include "routing_nb.h" #include "zebra/zebra_router.h" #include "zebra/zebra_errors.h" @@ -55,10 +53,11 @@ #include "zebra/zebra_netns_notify.h" #include "zebra/zebra_rnh.h" #include "zebra/zebra_pbr.h" - -#if defined(HANDLE_NETLINK_FUZZING) -#include "zebra/kernel_netlink.h" -#endif /* HANDLE_NETLINK_FUZZING */ +#include "zebra/zebra_vxlan.h" +#include "zebra/zebra_routemap.h" +#include "zebra/zebra_nb.h" +#include "zebra/zebra_opaque.h" +#include "zebra/zebra_srte.h" #define ZEBRA_PTM_SUPPORT @@ -74,8 +73,7 @@ int retain_mode = 0; /* Allow non-quagga entities to delete quagga routes */ int allow_delete = 0; -/* Don't delete kernel route. */ -int keep_kernel_mode = 0; +int graceful_restart; bool v6_rr_semantics = false; @@ -86,15 +84,15 @@ uint32_t nl_rcvbufsize = 4194304; #define OPTION_V6_RR_SEMANTICS 2000 /* Command line options. */ -struct option longopts[] = { +const struct option longopts[] = { {"batch", no_argument, NULL, 'b'}, {"allow_delete", no_argument, NULL, 'a'}, {"keep_kernel", no_argument, NULL, 'k'}, {"socket", required_argument, NULL, 'z'}, {"ecmp", required_argument, NULL, 'e'}, - {"label_socket", no_argument, NULL, 'l'}, {"retain", no_argument, NULL, 'r'}, {"vrfdefaultname", required_argument, NULL, 'o'}, + {"graceful_restart", required_argument, NULL, 'K'}, #ifdef HAVE_NETLINK {"vrfwnetns", no_argument, NULL, 'n'}, {"nl-bufsize", required_argument, NULL, 's'}, @@ -119,8 +117,6 @@ struct zebra_privs_t zserv_privs = { .cap_num_p = array_size(_caps_p), .cap_num_i = 0}; -unsigned int multipath_num = MULTIPATH_NUM; - /* SIGHUP handler. */ static void sighup(void) { @@ -146,34 +142,61 @@ static void sigint(void) zlog_notice("Terminating on signal"); + atomic_store_explicit(&zrouter.in_shutdown, true, + memory_order_relaxed); + + /* send RA lifetime of 0 before stopping. rfc4861/6.2.5 */ + rtadv_stop_ra_all(); + frr_early_fini(); + /* Stop the opaque module pthread */ + zebra_opaque_stop(); + zebra_dplane_pre_finish(); + /* Clean up GR related info. */ + zebra_gr_stale_client_cleanup(zrouter.stale_client_list); + list_delete_all_node(zrouter.stale_client_list); + + /* Clean up zapi clients and server module */ for (ALL_LIST_ELEMENTS(zrouter.client_list, ln, nn, client)) zserv_close_client(client); zserv_close(); list_delete_all_node(zrouter.client_list); + /* Once all the zclients are cleaned up, clean up the opaque module */ + zebra_opaque_finish(); + zebra_ptm_finish(); - if (retain_mode) + if (retain_mode) { + zebra_nhg_mark_keep(); RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { zvrf = vrf->info; if (zvrf) SET_FLAG(zvrf->flags, ZEBRA_VRF_RETAIN); } + } if (zrouter.lsp_process_q) work_queue_free_and_null(&zrouter.lsp_process_q); + vrf_terminate(); + rtadv_terminate(); - ns_walk_func(zebra_ns_early_shutdown); + ns_walk_func(zebra_ns_early_shutdown, NULL, NULL); zebra_ns_notify_close(); access_list_reset(); prefix_list_reset(); - route_map_finish(); + /* + * zebra_routemap_finish will + * 1 set rmap upd timer to 0 so that rmap update wont be scheduled again + * 2 Put off the rmap update thread + * 3 route_map_finish + */ + zebra_routemap_finish(); list_delete(&zrouter.client_list); @@ -193,7 +216,7 @@ int zebra_finalize(struct thread *dummy) zlog_info("Zebra final shutdown"); /* Final shutdown of ns resources */ - ns_walk_func(zebra_ns_final_shutdown); + ns_walk_func(zebra_ns_final_shutdown, NULL, NULL); /* Stop dplane thread and finish any cleanup */ zebra_dplane_shutdown(); @@ -229,16 +252,20 @@ struct quagga_signal_t zebra_signals[] = { }, }; -static const struct frr_yang_module_info *zebra_yang_modules[] = { +static const struct frr_yang_module_info *const zebra_yang_modules[] = { + &frr_filter_info, &frr_interface_info, + &frr_route_map_info, + &frr_zebra_info, + &frr_vrf_info, + &frr_routing_info, }; FRR_DAEMON_INFO( zebra, ZEBRA, .vty_port = ZEBRA_VTY_PORT, .flags = FRR_NO_ZCLIENT, .proghelp = - "Daemon which manages kernel routing table management " - "and\nredistribution between different routing protocols.", + "Daemon which manages kernel routing table management and\nredistribution between different routing protocols.", .signals = zebra_signals, .n_signals = array_size(zebra_signals), @@ -253,54 +280,33 @@ int main(int argc, char **argv) // int batch_mode = 0; char *zserv_path = NULL; char *vrf_default_name_configured = NULL; - /* Socket to external label manager */ - char *lblmgr_path = NULL; struct sockaddr_storage dummy; socklen_t dummylen; -#if defined(HANDLE_ZAPI_FUZZING) - char *zapi_fuzzing = NULL; -#endif /* HANDLE_ZAPI_FUZZING */ -#if defined(HANDLE_NETLINK_FUZZING) - char *netlink_fuzzing = NULL; -#endif /* HANDLE_NETLINK_FUZZING */ + graceful_restart = 0; vrf_configure_backend(VRF_BACKEND_VRF_LITE); - logicalrouter_configure_backend(LOGICALROUTER_BACKEND_NETNS); frr_preinit(&zebra_di, argc, argv); frr_opt_add( - "bakz:e:l:o:r" + "baz:e:o:rK:" #ifdef HAVE_NETLINK "s:n" #endif -#if defined(HANDLE_ZAPI_FUZZING) - "c:" -#endif /* HANDLE_ZAPI_FUZZING */ -#if defined(HANDLE_NETLINK_FUZZING) - "w:" -#endif /* HANDLE_NETLINK_FUZZING */ , longopts, - " -b, --batch Runs in batch mode\n" - " -a, --allow_delete Allow other processes to delete zebra routes\n" - " -z, --socket Set path of zebra socket\n" - " -e, --ecmp Specify ECMP to use.\n" - " -l, --label_socket Socket to external label manager\n" - " -k, --keep_kernel Don't delete old routes which were installed by zebra.\n" - " -r, --retain When program terminates, retain added route by zebra.\n" - " -o, --vrfdefaultname Set default VRF name.\n" + " -b, --batch Runs in batch mode\n" + " -a, --allow_delete Allow other processes to delete zebra routes\n" + " -z, --socket Set path of zebra socket\n" + " -e, --ecmp Specify ECMP to use.\n" + " -r, --retain When program terminates, retain added route by zebra.\n" + " -o, --vrfdefaultname Set default VRF name.\n" + " -K, --graceful_restart Graceful restart at the kernel level, timer in seconds for expiration\n" #ifdef HAVE_NETLINK - " -n, --vrfwnetns Use NetNS as VRF backend\n" - " -s, --nl-bufsize Set netlink receive buffer size\n" - " --v6-rr-semantics Use v6 RR semantics\n" + " -n, --vrfwnetns Use NetNS as VRF backend\n" + " -s, --nl-bufsize Set netlink receive buffer size\n" + " --v6-rr-semantics Use v6 RR semantics\n" #endif /* HAVE_NETLINK */ -#if defined(HANDLE_ZAPI_FUZZING) - " -c Bypass normal startup and use this file for testing of zapi\n" -#endif /* HANDLE_ZAPI_FUZZING */ -#if defined(HANDLE_NETLINK_FUZZING) - " -w Bypass normal startup and use this file for testing of netlink input\n" -#endif /* HANDLE_NETLINK_FUZZING */ ); while (1) { @@ -318,20 +324,21 @@ int main(int argc, char **argv) case 'a': allow_delete = 1; break; - case 'k': - keep_kernel_mode = 1; - break; - case 'e': - multipath_num = atoi(optarg); - if (multipath_num > MULTIPATH_NUM - || multipath_num <= 0) { + case 'e': { + unsigned long int parsed_multipath = + strtoul(optarg, NULL, 10); + if (parsed_multipath == 0 + || parsed_multipath > MULTIPATH_NUM + || parsed_multipath > UINT32_MAX) { flog_err( EC_ZEBRA_BAD_MULTIPATH_NUM, - "Multipath Number specified must be less than %d and greater than 0", + "Multipath Number specified must be less than %u and greater than 0", MULTIPATH_NUM); return 1; } + zrouter.multipath_num = parsed_multipath; break; + } case 'o': vrf_default_name_configured = optarg; break; @@ -344,40 +351,23 @@ int main(int argc, char **argv) exit(1); } break; - case 'l': - lblmgr_path = optarg; - break; case 'r': retain_mode = 1; break; + case 'K': + graceful_restart = atoi(optarg); + break; #ifdef HAVE_NETLINK case 's': nl_rcvbufsize = atoi(optarg); break; case 'n': vrf_configure_backend(VRF_BACKEND_NETNS); - logicalrouter_configure_backend( - LOGICALROUTER_BACKEND_OFF); break; case OPTION_V6_RR_SEMANTICS: v6_rr_semantics = true; break; #endif /* HAVE_NETLINK */ -#if defined(HANDLE_ZAPI_FUZZING) - case 'c': - zapi_fuzzing = optarg; - break; -#endif /* HANDLE_ZAPI_FUZZING */ -#if defined(HANDLE_NETLINK_FUZZING) - case 'w': - netlink_fuzzing = optarg; - /* This ensures we are aren't writing any of the - * startup netlink messages that happen when we - * just want to read. - */ - netlink_read = true; - break; -#endif /* HANDLE_NETLINK_FUZZING */ default: frr_help_exit(1); break; @@ -386,21 +376,18 @@ int main(int argc, char **argv) zrouter.master = frr_init(); - /* Initialize pthread library */ - frr_pthread_init(); - /* Zebra related initialize. */ zebra_router_init(); zserv_init(); rib_init(); zebra_if_init(); zebra_debug_init(); - router_id_cmd_init(); /* * Initialize NS( and implicitly the VRF module), and make kernel * routing socket. */ zebra_ns_init((const char *)vrf_default_name_configured); + router_id_cmd_init(); zebra_vty_init(); access_list_init(); prefix_list_init(); @@ -416,9 +403,11 @@ int main(int argc, char **argv) zebra_mpls_vty_init(); zebra_pw_vty_init(); zebra_pbr_init(); + zebra_opaque_init(); + zebra_srte_init(); -/* For debug purpose. */ -/* SET_FLAG (zebra_debug_event, ZEBRA_DEBUG_EVENT); */ + /* For debug purpose. */ + /* SET_FLAG (zebra_debug_event, ZEBRA_DEBUG_EVENT); */ /* Process the configuration file. Among other configuration * directives we can meet those installing static routes. Such @@ -437,8 +426,9 @@ int main(int argc, char **argv) * will be equal to the current getpid(). To know about such routes, * we have to have route_read() called before. */ - if (!keep_kernel_mode) - rib_sweep_route(); + zrouter.startup_time = monotime(NULL); + thread_add_timer(zrouter.master, rib_sweep_route, + NULL, graceful_restart, NULL); /* Needed for BSD routing socket. */ pid = getpid(); @@ -446,32 +436,24 @@ int main(int argc, char **argv) /* Start dataplane system */ zebra_dplane_start(); + /* Start the ted module, before zserv */ + zebra_opaque_start(); + /* Start Zebra API server */ zserv_start(zserv_path); /* Init label manager */ - label_manager_init(lblmgr_path); + label_manager_init(); /* RNH init */ zebra_rnh_init(); + /* Config handler Init */ + zebra_evpn_init(); + /* Error init */ zebra_error_init(); -#if defined(HANDLE_ZAPI_FUZZING) - if (zapi_fuzzing) { - zserv_read_file(zapi_fuzzing); - exit(0); - } -#endif /* HANDLE_ZAPI_FUZZING */ -#if defined(HANDLE_NETLINK_FUZZING) - if (netlink_fuzzing) { - netlink_read_init(netlink_fuzzing); - exit(0); - } -#endif /* HANDLE_NETLINK_FUZZING */ - - frr_run(zrouter.master); /* Not reached... */ diff --git a/zebra/redistribute.c b/zebra/redistribute.c index f98a4c02c3..c0f89e6afe 100644 --- a/zebra/redistribute.c +++ b/zebra/redistribute.c @@ -52,7 +52,7 @@ static int zebra_import_table_used[AFI_MAX][ZEBRA_KERNEL_TABLE_MAX]; static uint32_t zebra_import_table_distance[AFI_MAX][ZEBRA_KERNEL_TABLE_MAX]; -int is_zebra_import_table_enabled(afi_t afi, uint32_t table_id) +int is_zebra_import_table_enabled(afi_t afi, vrf_id_t vrf_id, uint32_t table_id) { /* * Make sure that what we are called with actualy makes sense @@ -75,6 +75,10 @@ static void zebra_redistribute_default(struct zserv *client, vrf_id_t vrf_id) struct route_entry *newre; for (afi = AFI_IP; afi <= AFI_IP6; afi++) { + + if (!vrf_bitmap_check(client->redist_default[afi], vrf_id)) + continue; + /* Lookup table. */ table = zebra_vrf_table(afi, SAFI_UNICAST, vrf_id); if (!table) @@ -119,7 +123,7 @@ static void zebra_redistribute(struct zserv *client, int type, srcdest_rnode_prefixes(rn, &dst_p, &src_p); - if (IS_ZEBRA_DEBUG_EVENT) + if (IS_ZEBRA_DEBUG_RIB) zlog_debug( "%s: client %s %s(%u) checking: selected=%d, type=%d, distance=%d, metric=%d zebra_check_addr=%d", __func__, @@ -146,21 +150,58 @@ static void zebra_redistribute(struct zserv *client, int type, } } +/* + * Function to check if prefix is candidate for + * redistribute. + */ +static bool zebra_redistribute_check(const struct route_entry *re, + struct zserv *client, + const struct prefix *p, int afi) +{ + /* Process only if there is valid re */ + if (!re) + return false; + + /* If default route and redistributed */ + if (is_default_prefix(p) + && vrf_bitmap_check(client->redist_default[afi], re->vrf_id)) + return true; + + /* If redistribute in enabled for zebra route all */ + if (vrf_bitmap_check(client->redist[afi][ZEBRA_ROUTE_ALL], re->vrf_id)) + return true; + + /* + * If multi-instance then check for route + * redistribution for given instance. + */ + if (re->instance + && redist_check_instance(&client->mi_redist[afi][re->type], + re->instance)) + return true; + + /* If redistribution is enabled for give route type. */ + if (vrf_bitmap_check(client->redist[afi][re->type], re->vrf_id)) + return true; + + return false; +} + /* Either advertise a route for redistribution to registered clients or */ /* withdraw redistribution if add cannot be done for client */ void redistribute_update(const struct prefix *p, const struct prefix *src_p, - struct route_entry *re, struct route_entry *prev_re) + const struct route_entry *re, + const struct route_entry *prev_re) { struct listnode *node, *nnode; struct zserv *client; - int send_redistribute; int afi; char buf[PREFIX_STRLEN]; if (IS_ZEBRA_DEBUG_RIB) { zlog_debug( - "%u:%s: Redist update re %p (%s), old %p (%s)", - re->vrf_id, prefix2str(p, buf, sizeof(buf)), + "(%u:%u):%s: Redist update re %p (%s), old %p (%s)", + re->vrf_id, re->table, prefix2str(p, buf, sizeof(buf)), re, zebra_route_string(re->type), prev_re, prev_re ? zebra_route_string(prev_re->type) : "None"); } @@ -168,102 +209,115 @@ void redistribute_update(const struct prefix *p, const struct prefix *src_p, afi = family2afi(p->family); if (!afi) { flog_warn(EC_ZEBRA_REDISTRIBUTE_UNKNOWN_AF, - "%s: Unknown AFI/SAFI prefix received\n", - __FUNCTION__); + "%s: Unknown AFI/SAFI prefix received\n", __func__); + return; + } + if (!zebra_check_addr(p)) { + if (IS_ZEBRA_DEBUG_RIB) + zlog_debug("Redist update filter prefix %s", + prefix2str(p, buf, sizeof(buf))); return; } + for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) { - send_redistribute = 0; - - if (is_default_prefix(p) - && vrf_bitmap_check(client->redist_default[afi], - re->vrf_id)) - send_redistribute = 1; - else if (vrf_bitmap_check(client->redist[afi][ZEBRA_ROUTE_ALL], - re->vrf_id)) - send_redistribute = 1; - else if (re->instance - && redist_check_instance( - &client->mi_redist[afi][re->type], - re->instance)) - send_redistribute = 1; - else if (vrf_bitmap_check(client->redist[afi][re->type], - re->vrf_id)) - send_redistribute = 1; - - if (send_redistribute) { - if (IS_ZEBRA_DEBUG_EVENT) { + if (zebra_redistribute_check(re, client, p, afi)) { + if (IS_ZEBRA_DEBUG_RIB) { zlog_debug( - "%s: client %s %s(%u), type=%d, distance=%d, metric=%d", - __func__, - zebra_route_string(client->proto), - prefix2str(p, buf, sizeof(buf)), - re->vrf_id, re->type, - re->distance, re->metric); + "%s: client %s %s(%u:%u), type=%d, distance=%d, metric=%d", + __func__, + zebra_route_string(client->proto), + prefix2str(p, buf, sizeof(buf)), + re->vrf_id, re->table, re->type, + re->distance, re->metric); } zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_ADD, client, p, src_p, re); - } else if (prev_re - && ((re->instance - && redist_check_instance( - &client->mi_redist[afi] - [prev_re->type], - re->instance)) - || vrf_bitmap_check( - client->redist[afi][prev_re->type], - re->vrf_id))) { + } else if (zebra_redistribute_check(prev_re, client, p, afi)) zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_DEL, client, p, src_p, prev_re); - } } } +/* + * During a route delete, where 'new_re' is NULL, redist a delete to all + * clients registered for the type of 'old_re'. + * During a route update, redist a delete to any clients who will not see + * an update when the new route is installed. There are cases when a client + * may have seen a redist for 'old_re', but will not see + * the redist for 'new_re'. + */ void redistribute_delete(const struct prefix *p, const struct prefix *src_p, - struct route_entry *re) + const struct route_entry *old_re, + const struct route_entry *new_re) { struct listnode *node, *nnode; struct zserv *client; - char buf[INET6_ADDRSTRLEN]; int afi; + char buf[PREFIX_STRLEN]; + vrf_id_t vrfid; + + if (old_re) + vrfid = old_re->vrf_id; + else if (new_re) + vrfid = new_re->vrf_id; + else + return; if (IS_ZEBRA_DEBUG_RIB) { - inet_ntop(p->family, &p->u.prefix, buf, INET6_ADDRSTRLEN); - zlog_debug("%u:%s/%d: Redist delete re %p (%s)", - re->vrf_id, buf, p->prefixlen, re, - zebra_route_string(re->type)); + zlog_debug( + "%u:%s: Redist del: re %p (%s), new re %p (%s)", + vrfid, prefix2str(p, buf, sizeof(buf)), + old_re, + old_re ? zebra_route_string(old_re->type) : "None", + new_re, + new_re ? zebra_route_string(new_re->type) : "None"); } /* Add DISTANCE_INFINITY check. */ - if (re->distance == DISTANCE_INFINITY) + if (old_re && (old_re->distance == DISTANCE_INFINITY)) { + if (IS_ZEBRA_DEBUG_RIB) + zlog_debug(" Skipping due to Infinite Distance"); return; + } afi = family2afi(p->family); if (!afi) { flog_warn(EC_ZEBRA_REDISTRIBUTE_UNKNOWN_AF, "%s: Unknown AFI/SAFI prefix received\n", - __FUNCTION__); + __func__); + return; + } + + /* Skip invalid (e.g. linklocal) prefix */ + if (!zebra_check_addr(p)) { + if (IS_ZEBRA_DEBUG_RIB) { + zlog_debug( + "%u:%s: Redist del old: skipping invalid prefix", + vrfid, prefix2str(p, buf, sizeof(buf))); + } return; } for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) { - if ((is_default_prefix(p) - && vrf_bitmap_check(client->redist_default[afi], - re->vrf_id)) - || vrf_bitmap_check(client->redist[afi][ZEBRA_ROUTE_ALL], - re->vrf_id) - || (re->instance - && redist_check_instance( - &client->mi_redist[afi][re->type], - re->instance)) - || vrf_bitmap_check(client->redist[afi][re->type], - re->vrf_id)) { + /* Do not send unsolicited messages to synchronous clients. */ + if (client->synchronous) + continue; + /* + * Skip this client if it will receive an update for the + * 'new' re + */ + if (zebra_redistribute_check(new_re, client, p, afi)) + continue; + + /* Send a delete for the 'old' re to any subscribed client. */ + if (zebra_redistribute_check(old_re, client, p, afi)) zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_DEL, - client, p, src_p, re); - } + client, p, src_p, old_re); } } + void zebra_redistribute_add(ZAPI_HANDLER_ARGS) { afi_t afi = 0; @@ -276,20 +330,20 @@ void zebra_redistribute_add(ZAPI_HANDLER_ARGS) if (IS_ZEBRA_DEBUG_EVENT) zlog_debug( - "%s: client proto %s afi=%d, wants %s, vrf %u, instance=%d", + "%s: client proto %s afi=%d, wants %s, vrf %s(%u), instance=%d", __func__, zebra_route_string(client->proto), afi, - zebra_route_string(type), zvrf_id(zvrf), instance); + zebra_route_string(type), VRF_LOGNAME(zvrf->vrf), + zvrf_id(zvrf), instance); if (afi == 0 || afi >= AFI_MAX) { flog_warn(EC_ZEBRA_REDISTRIBUTE_UNKNOWN_AF, - "%s: Specified afi %d does not exist", - __PRETTY_FUNCTION__, afi); + "%s: Specified afi %d does not exist", __func__, afi); return; } if (type == 0 || type >= ZEBRA_ROUTE_MAX) { zlog_debug("%s: Specified Route Type %d does not exist", - __PRETTY_FUNCTION__, type); + __func__, type); return; } @@ -305,8 +359,10 @@ void zebra_redistribute_add(ZAPI_HANDLER_ARGS) if (!vrf_bitmap_check(client->redist[afi][type], zvrf_id(zvrf))) { if (IS_ZEBRA_DEBUG_EVENT) - zlog_debug("%s: setting vrf %u redist bitmap", - __func__, zvrf_id(zvrf)); + zlog_debug( + "%s: setting vrf %s(%u) redist bitmap", + __func__, VRF_LOGNAME(zvrf->vrf), + zvrf_id(zvrf)); vrf_bitmap_set(client->redist[afi][type], zvrf_id(zvrf)); zebra_redistribute(client, type, 0, zvrf_id(zvrf), afi); @@ -329,14 +385,13 @@ void zebra_redistribute_delete(ZAPI_HANDLER_ARGS) if (afi == 0 || afi >= AFI_MAX) { flog_warn(EC_ZEBRA_REDISTRIBUTE_UNKNOWN_AF, - "%s: Specified afi %d does not exist", - __PRETTY_FUNCTION__, afi); + "%s: Specified afi %d does not exist", __func__, afi); return; } if (type == 0 || type >= ZEBRA_ROUTE_MAX) { zlog_debug("%s: Specified Route Type %d does not exist", - __PRETTY_FUNCTION__, type); + __func__, type); return; } @@ -363,8 +418,7 @@ void zebra_redistribute_default_add(ZAPI_HANDLER_ARGS) if (afi == 0 || afi >= AFI_MAX) { flog_warn(EC_ZEBRA_REDISTRIBUTE_UNKNOWN_AF, - "%s: Specified afi %u does not exist", - __PRETTY_FUNCTION__, afi); + "%s: Specified afi %u does not exist", __func__, afi); return; } @@ -383,8 +437,7 @@ void zebra_redistribute_default_delete(ZAPI_HANDLER_ARGS) if (afi == 0 || afi >= AFI_MAX) { flog_warn(EC_ZEBRA_REDISTRIBUTE_UNKNOWN_AF, - "%s: Specified afi %u does not exist", - __PRETTY_FUNCTION__, afi); + "%s: Specified afi %u does not exist", __func__, afi); return; } @@ -407,6 +460,12 @@ void zebra_interface_up_update(struct interface *ifp) if (ifp->ptm_status || !ifp->ptm_enable) { for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) { + /* Do not send unsolicited messages to synchronous + * clients. + */ + if (client->synchronous) + continue; + zsend_interface_update(ZEBRA_INTERFACE_UP, client, ifp); zsend_interface_link_params(client, ifp); @@ -425,6 +484,10 @@ void zebra_interface_down_update(struct interface *ifp) ifp->name, ifp->vrf_id); for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) { + /* Do not send unsolicited messages to synchronous clients. */ + if (client->synchronous) + continue; + zsend_interface_update(ZEBRA_INTERFACE_DOWN, client, ifp); } } @@ -440,6 +503,10 @@ void zebra_interface_add_update(struct interface *ifp) ifp->vrf_id); for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) { + /* Do not send unsolicited messages to synchronous clients. */ + if (client->synchronous) + continue; + client->ifadd_cnt++; zsend_interface_add(client, ifp); zsend_interface_link_params(client, ifp); @@ -456,6 +523,10 @@ void zebra_interface_delete_update(struct interface *ifp) ifp->name, ifp->vrf_id); for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) { + /* Do not send unsolicited messages to synchronous clients. */ + if (client->synchronous) + continue; + client->ifdel_cnt++; zsend_interface_delete(client, ifp); } @@ -487,12 +558,17 @@ void zebra_interface_address_add_update(struct interface *ifp, router_id_add_address(ifc); - for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) + for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) { + /* Do not send unsolicited messages to synchronous clients. */ + if (client->synchronous) + continue; + if (CHECK_FLAG(ifc->conf, ZEBRA_IFC_REAL)) { client->connected_rt_add_cnt++; zsend_interface_address(ZEBRA_INTERFACE_ADDRESS_ADD, client, ifp, ifc); } + } } /* Interface address deletion. */ @@ -516,12 +592,17 @@ void zebra_interface_address_delete_update(struct interface *ifp, router_id_del_address(ifc); - for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) + for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) { + /* Do not send unsolicited messages to synchronous clients. */ + if (client->synchronous) + continue; + if (CHECK_FLAG(ifc->conf, ZEBRA_IFC_REAL)) { client->connected_rt_del_cnt++; zsend_interface_address(ZEBRA_INTERFACE_ADDRESS_DELETE, client, ifp, ifc); } + } } /* Interface VRF change. May need to delete from clients not interested in @@ -538,6 +619,10 @@ void zebra_interface_vrf_update_del(struct interface *ifp, vrf_id_t new_vrf_id) ifp->name, ifp->vrf_id, new_vrf_id); for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) { + /* Do not send unsolicited messages to synchronous clients. */ + if (client->synchronous) + continue; + /* Need to delete if the client is not interested in the new * VRF. */ zsend_interface_update(ZEBRA_INTERFACE_DOWN, client, ifp); @@ -561,6 +646,10 @@ void zebra_interface_vrf_update_add(struct interface *ifp, vrf_id_t old_vrf_id) ifp->name, old_vrf_id, ifp->vrf_id); for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) { + /* Do not send unsolicited messages to synchronous clients. */ + if (client->synchronous) + continue; + /* Need to add if the client is interested in the new VRF. */ client->ifadd_cnt++; zsend_interface_add(client, ifp); @@ -568,24 +657,26 @@ void zebra_interface_vrf_update_add(struct interface *ifp, vrf_id_t old_vrf_id) } } -int zebra_add_import_table_entry(struct route_node *rn, struct route_entry *re, - const char *rmap_name) +int zebra_add_import_table_entry(struct zebra_vrf *zvrf, struct route_node *rn, + struct route_entry *re, const char *rmap_name) { struct route_entry *newre; struct route_entry *same; struct prefix p; - route_map_result_t ret = RMAP_MATCH; + struct nexthop_group *ng; + route_map_result_t ret = RMAP_PERMITMATCH; afi_t afi; afi = family2afi(rn->p.family); if (rmap_name) ret = zebra_import_table_route_map_check( - afi, re->type, re->instance, &rn->p, re->ng.nexthop, - re->vrf_id, re->tag, rmap_name); + afi, re->type, re->instance, &rn->p, + re->nhe->nhg.nexthop, + zvrf->vrf->vrf_id, re->tag, rmap_name); - if (ret != RMAP_MATCH) { + if (ret != RMAP_PERMITMATCH) { UNSET_FLAG(re->flags, ZEBRA_FLAG_SELECTED); - zebra_del_import_table_entry(rn, re); + zebra_del_import_table_entry(zvrf, rn, re); return 0; } @@ -603,7 +694,7 @@ int zebra_add_import_table_entry(struct route_node *rn, struct route_entry *re, if (same) { UNSET_FLAG(same->flags, ZEBRA_FLAG_SELECTED); - zebra_del_import_table_entry(rn, same); + zebra_del_import_table_entry(zvrf, rn, same); } newre = XCALLOC(MTYPE_RE, sizeof(struct route_entry)); @@ -612,18 +703,20 @@ int zebra_add_import_table_entry(struct route_node *rn, struct route_entry *re, newre->flags = re->flags; newre->metric = re->metric; newre->mtu = re->mtu; - newre->table = zrouter.rtm_table_default; - newre->nexthop_num = 0; - newre->uptime = time(NULL); + newre->table = zvrf->table_id; + newre->uptime = monotime(NULL); newre->instance = re->table; - route_entry_copy_nexthops(newre, re->ng.nexthop); - rib_add_multipath(afi, SAFI_UNICAST, &p, NULL, newre); + ng = nexthop_group_new(); + copy_nexthops(&ng->nexthop, re->nhe->nhg.nexthop, NULL); + + rib_add_multipath(afi, SAFI_UNICAST, &p, NULL, newre, ng); return 0; } -int zebra_del_import_table_entry(struct route_node *rn, struct route_entry *re) +int zebra_del_import_table_entry(struct zebra_vrf *zvrf, struct route_node *rn, + struct route_entry *re) { struct prefix p; afi_t afi; @@ -631,30 +724,32 @@ int zebra_del_import_table_entry(struct route_node *rn, struct route_entry *re) afi = family2afi(rn->p.family); prefix_copy(&p, &rn->p); - rib_delete(afi, SAFI_UNICAST, re->vrf_id, ZEBRA_ROUTE_TABLE, re->table, - re->flags, &p, NULL, re->ng.nexthop, - zrouter.rtm_table_default, re->metric, re->distance, false); + rib_delete(afi, SAFI_UNICAST, zvrf->vrf->vrf_id, ZEBRA_ROUTE_TABLE, + re->table, re->flags, &p, NULL, re->nhe->nhg.nexthop, + re->nhe_id, zvrf->table_id, re->metric, re->distance, + false, false); return 0; } /* Assuming no one calls this with the main routing table */ -int zebra_import_table(afi_t afi, uint32_t table_id, uint32_t distance, - const char *rmap_name, int add) +int zebra_import_table(afi_t afi, vrf_id_t vrf_id, uint32_t table_id, + uint32_t distance, const char *rmap_name, int add) { struct route_table *table; struct route_entry *re; struct route_node *rn; + struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(vrf_id); if (!is_zebra_valid_kernel_table(table_id) - || ((table_id == RT_TABLE_MAIN) - || (table_id == zrouter.rtm_table_default))) - return (-1); + || (table_id == RT_TABLE_MAIN)) + return -1; if (afi >= AFI_MAX) - return (-1); + return -1; - table = zebra_vrf_other_route_table(afi, table_id, VRF_DEFAULT); + table = zebra_vrf_get_table_with_table_id(afi, SAFI_UNICAST, vrf_id, + table_id); if (table == NULL) { return 0; } else if (IS_ZEBRA_DEBUG_RIB) { @@ -708,15 +803,16 @@ int zebra_import_table(afi_t afi, uint32_t table_id, uint32_t distance, if (((afi == AFI_IP) && (rn->p.family == AF_INET)) || ((afi == AFI_IP6) && (rn->p.family == AF_INET6))) { if (add) - zebra_add_import_table_entry(rn, re, rmap_name); + zebra_add_import_table_entry(zvrf, rn, re, + rmap_name); else - zebra_del_import_table_entry(rn, re); + zebra_del_import_table_entry(zvrf, rn, re); } } return 0; } -int zebra_import_table_config(struct vty *vty) +int zebra_import_table_config(struct vty *vty, vrf_id_t vrf_id) { int i; afi_t afi; @@ -726,7 +822,7 @@ int zebra_import_table_config(struct vty *vty) for (afi = AFI_IP; afi < AFI_MAX; afi++) { for (i = 1; i < ZEBRA_KERNEL_TABLE_MAX; i++) { - if (!is_zebra_import_table_enabled(afi, i)) + if (!is_zebra_import_table_enabled(afi, vrf_id, i)) continue; if (zebra_import_table_distance[afi][i] @@ -751,54 +847,84 @@ int zebra_import_table_config(struct vty *vty) return write; } -void zebra_import_table_rm_update(const char *rmap) +static void zebra_import_table_rm_update_vrf_afi(struct zebra_vrf *zvrf, + afi_t afi, int table_id, + const char *rmap) { - afi_t afi; - int i; struct route_table *table; struct route_entry *re; struct route_node *rn; const char *rmap_name; + rmap_name = zebra_get_import_table_route_map(afi, table_id); + if ((!rmap_name) || (strcmp(rmap_name, rmap) != 0)) + return; + + table = zebra_vrf_get_table_with_table_id(afi, SAFI_UNICAST, + zvrf->vrf->vrf_id, table_id); + if (!table) { + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug("%s: Table id=%d not found", __func__, + table_id); + return; + } + + for (rn = route_top(table); rn; rn = route_next(rn)) { + /* + * For each entry in the non-default routing table, + * add the entry in the main table + */ + if (!rn->info) + continue; + + RNODE_FOREACH_RE (rn, re) { + if (CHECK_FLAG(re->status, ROUTE_ENTRY_REMOVED)) + continue; + break; + } + + if (!re) + continue; + + if (((afi == AFI_IP) && (rn->p.family == AF_INET)) + || ((afi == AFI_IP6) && (rn->p.family == AF_INET6))) + zebra_add_import_table_entry(zvrf, rn, re, rmap_name); + } + + return; +} + +static void zebra_import_table_rm_update_vrf(struct zebra_vrf *zvrf, + const char *rmap) +{ + afi_t afi; + int i; + for (afi = AFI_IP; afi < AFI_MAX; afi++) { for (i = 1; i < ZEBRA_KERNEL_TABLE_MAX; i++) { - if (!is_zebra_import_table_enabled(afi, i)) + if (!is_zebra_import_table_enabled( + afi, zvrf->vrf->vrf_id, i)) continue; - rmap_name = zebra_get_import_table_route_map(afi, i); - if ((!rmap_name) || (strcmp(rmap_name, rmap) != 0)) - continue; - table = zebra_vrf_other_route_table(afi, i, - VRF_DEFAULT); - for (rn = route_top(table); rn; rn = route_next(rn)) { - /* For each entry in the non-default - * routing table, - * add the entry in the main table - */ - if (!rn->info) - continue; - - RNODE_FOREACH_RE (rn, re) { - if (CHECK_FLAG(re->status, - ROUTE_ENTRY_REMOVED)) - continue; - break; - } - - if (!re) - continue; - - if (((afi == AFI_IP) - && (rn->p.family == AF_INET)) - || ((afi == AFI_IP6) - && (rn->p.family == AF_INET6))) - zebra_add_import_table_entry(rn, re, - rmap_name); - } + zebra_import_table_rm_update_vrf_afi(zvrf, afi, i, + rmap); } } +} - return; +void zebra_import_table_rm_update(const char *rmap) +{ + struct vrf *vrf; + struct zebra_vrf *zvrf; + + RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { + zvrf = vrf->info; + + if (!zvrf) + continue; + + zebra_import_table_rm_update_vrf(zvrf, rmap); + } } /* Interface parameters update */ @@ -811,6 +937,11 @@ void zebra_interface_parameters_update(struct interface *ifp) zlog_debug("MESSAGE: ZEBRA_INTERFACE_LINK_PARAMS %s(%u)", ifp->name, ifp->vrf_id); - for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) + for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) { + /* Do not send unsolicited messages to synchronous clients. */ + if (client->synchronous) + continue; + zsend_interface_link_params(client, ifp); + } } diff --git a/zebra/redistribute.h b/zebra/redistribute.h index 74a593b240..2685458f96 100644 --- a/zebra/redistribute.h +++ b/zebra/redistribute.h @@ -42,11 +42,19 @@ extern void zebra_redistribute_default_delete(ZAPI_HANDLER_ARGS); extern void redistribute_update(const struct prefix *p, const struct prefix *src_p, - struct route_entry *re, - struct route_entry *prev_re); -extern void redistribute_delete(const struct prefix *p, - const struct prefix *src_p, - struct route_entry *re); + const struct route_entry *re, + const struct route_entry *prev_re); +/* + * During a route delete, where 'new_re' is NULL, redist a delete to all + * clients registered for the type of 'old_re'. + * During a route update, redist a delete to any clients who will not see + * an update when the new route is installed. There are cases when a client + * may have seen a redist for 'old_re', but will not see + * the redist for 'new_re'. + */ +void redistribute_delete(const struct prefix *p, const struct prefix *src_p, + const struct route_entry *old_re, + const struct route_entry *new_re); extern void zebra_interface_up_update(struct interface *); extern void zebra_interface_down_update(struct interface *); @@ -64,17 +72,21 @@ extern void zebra_interface_vrf_update_del(struct interface *, extern void zebra_interface_vrf_update_add(struct interface *, vrf_id_t old_vrf_id); -extern int zebra_import_table(afi_t afi, uint32_t table_id, uint32_t distance, +extern int zebra_import_table(afi_t afi, vrf_id_t vrf_id, + uint32_t table_id, uint32_t distance, const char *rmap_name, int add); -extern int zebra_add_import_table_entry(struct route_node *rn, +extern int zebra_add_import_table_entry(struct zebra_vrf *zvrf, + struct route_node *rn, struct route_entry *re, const char *rmap_name); -extern int zebra_del_import_table_entry(struct route_node *rn, +extern int zebra_del_import_table_entry(struct zebra_vrf *zvrf, + struct route_node *rn, struct route_entry *re); -extern int is_zebra_import_table_enabled(afi_t, uint32_t table_id); +extern int is_zebra_import_table_enabled(afi_t, vrf_id_t vrf_id, + uint32_t table_id); -extern int zebra_import_table_config(struct vty *); +extern int zebra_import_table_config(struct vty *, vrf_id_t vrf_id); extern void zebra_import_table_rm_update(const char *rmap); diff --git a/zebra/rib.h b/zebra/rib.h index e26831e1a6..38f8029791 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -24,6 +24,7 @@ #include "zebra.h" #include "hook.h" +#include "typesafe.h" #include "linklist.h" #include "prefix.h" #include "table.h" @@ -34,21 +35,73 @@ #include "if.h" #include "mpls.h" #include "srcdest_table.h" +#include "zebra/zebra_nhg.h" #ifdef __cplusplus extern "C" { #endif +enum rnh_type { RNH_NEXTHOP_TYPE, RNH_IMPORT_CHECK_TYPE }; + +PREDECL_LIST(rnh_list) + +/* Nexthop structure. */ +struct rnh { + uint8_t flags; + +#define ZEBRA_NHT_CONNECTED 0x1 +#define ZEBRA_NHT_DELETED 0x2 +#define ZEBRA_NHT_EXACT_MATCH 0x4 + + /* VRF identifier. */ + vrf_id_t vrf_id; + + afi_t afi; + + enum rnh_type type; + + uint32_t seqno; + + struct route_entry *state; + struct prefix resolved_route; + struct list *client_list; + + /* pseudowires dependent on this nh */ + struct list *zebra_pseudowire_list; + + struct route_node *node; + + /* + * if this has been filtered for the client + */ + int filtered[ZEBRA_ROUTE_MAX]; + + struct rnh_list_item rnh_list_item; +}; + #define DISTANCE_INFINITY 255 #define ZEBRA_KERNEL_TABLE_MAX 252 /* support for no more than this rt tables */ +PREDECL_LIST(re_list) + struct route_entry { /* Link list. */ - struct route_entry *next; - struct route_entry *prev; + struct re_list_item next; - /* Nexthop structure */ - struct nexthop_group ng; + /* Nexthop group, shared/refcounted, based on the nexthop(s) + * provided by the owner of the route + */ + struct nhg_hash_entry *nhe; + + /* Nexthop group from FIB (optional), reflecting what is actually + * installed in the FIB if that differs. The 'backup' group is used + * when backup nexthops are present in the route's nhg. + */ + struct nexthop_group fib_ng; + struct nexthop_group fib_backup_ng; + + /* Nexthop group hash entry ID */ + uint32_t nhe_id; /* Tag */ route_tag_t tag; @@ -56,12 +109,9 @@ struct route_entry { /* Uptime. */ time_t uptime; - /* Type fo this route. */ + /* Type of this route. */ int type; - /* Source protocol instance */ - unsigned short instance; - /* VRF identifier. */ vrf_id_t vrf_id; @@ -75,9 +125,6 @@ struct route_entry { uint32_t mtu; uint32_t nexthop_mtu; - /* Distance. */ - uint8_t distance; - /* Flags of this route. * This flag's definition is in lib/zebra.h ZEBRA_FLAG_* and is exposed * to clients via Zserv @@ -87,35 +134,46 @@ struct route_entry { /* RIB internal status */ uint32_t status; #define ROUTE_ENTRY_REMOVED 0x1 -/* to simplify NHT logic when NHs change, instead of doing a NH by NH cmp */ -#define ROUTE_ENTRY_NEXTHOPS_CHANGED 0x2 /* The Route Entry has changed */ -#define ROUTE_ENTRY_CHANGED 0x4 +#define ROUTE_ENTRY_CHANGED 0x2 /* The Label has changed on the Route entry */ -#define ROUTE_ENTRY_LABELS_CHANGED 0x8 +#define ROUTE_ENTRY_LABELS_CHANGED 0x4 /* Route is queued for Installation into the Data Plane */ -#define ROUTE_ENTRY_QUEUED 0x10 +#define ROUTE_ENTRY_QUEUED 0x8 /* Route is installed into the Data Plane */ -#define ROUTE_ENTRY_INSTALLED 0x20 +#define ROUTE_ENTRY_INSTALLED 0x10 /* Route has Failed installation into the Data Plane in some manner */ -#define ROUTE_ENTRY_FAILED 0x40 - - /* Nexthop information. */ - uint8_t nexthop_num; - uint8_t nexthop_active_num; +#define ROUTE_ENTRY_FAILED 0x20 +/* Route has a 'fib' set of nexthops, probably because the installed set + * differs from the rib/normal set of nexthops. + */ +#define ROUTE_ENTRY_USE_FIB_NHG 0x40 /* Sequence value incremented for each dataplane operation */ uint32_t dplane_sequence; + + /* Source protocol instance */ + uint16_t instance; + + /* Distance. */ + uint8_t distance; }; +#define RIB_SYSTEM_ROUTE(R) RSYSTEM_ROUTE((R)->type) + +#define RIB_KERNEL_ROUTE(R) RKERNEL_ROUTE((R)->type) + /* meta-queue structure: - * sub-queue 0: connected, kernel - * sub-queue 1: static - * sub-queue 2: RIP, RIPng, OSPF, OSPF6, IS-IS, EIGRP, NHRP - * sub-queue 3: iBGP, eBGP - * sub-queue 4: any other origin (if any) + * sub-queue 0: nexthop group objects + * sub-queue 1: connected + * sub-queue 2: kernel + * sub-queue 3: static + * sub-queue 4: RIP, RIPng, OSPF, OSPF6, IS-IS, EIGRP, NHRP + * sub-queue 5: iBGP, eBGP + * sub-queue 6: any other origin (if any) typically those that + * don't generate routes */ -#define MQ_SIZE 5 +#define MQ_SIZE 7 struct meta_queue { struct list *subq[MQ_SIZE]; uint32_t size; /* sum of lengths of all subqueues */ @@ -135,7 +193,7 @@ typedef struct rib_dest_t_ { /* * Doubly-linked list of routes for this prefix. */ - struct route_entry *routes; + struct re_list_head routes; struct route_entry *selected_fib; @@ -151,7 +209,7 @@ typedef struct rib_dest_t_ { * the data plane we will run evaluate_rnh * on these prefixes. */ - struct list *nht; + struct rnh_list_head nht; /* * Linkage to put dest on the FPM processing queue. @@ -160,9 +218,12 @@ typedef struct rib_dest_t_ { } rib_dest_t; +DECLARE_LIST(rnh_list, struct rnh, rnh_list_item); +DECLARE_LIST(re_list, struct route_entry, next); + #define RIB_ROUTE_QUEUED(x) (1 << (x)) // If MQ_SIZE is modified this value needs to be updated. -#define RIB_ROUTE_ANY_QUEUED 0x1F +#define RIB_ROUTE_ANY_QUEUED 0x3F /* * The maximum qindex that can be used. @@ -187,14 +248,22 @@ typedef struct rib_dest_t_ { * Macro to iterate over each route for a destination (prefix). */ #define RE_DEST_FOREACH_ROUTE(dest, re) \ - for ((re) = (dest) ? (dest)->routes : NULL; (re); (re) = (re)->next) + for ((re) = (dest) ? re_list_first(&((dest)->routes)) : NULL; (re); \ + (re) = re_list_next(&((dest)->routes), (re))) /* * Same as above, but allows the current node to be unlinked. */ #define RE_DEST_FOREACH_ROUTE_SAFE(dest, re, next) \ - for ((re) = (dest) ? (dest)->routes : NULL; \ - (re) && ((next) = (re)->next, 1); (re) = (next)) + for ((re) = (dest) ? re_list_first(&((dest)->routes)) : NULL; \ + (re) && ((next) = re_list_next(&((dest)->routes), (re)), 1); \ + (re) = (next)) + +#define RE_DEST_FIRST_ROUTE(dest, re) \ + ((re) = (dest) ? re_list_first(&((dest)->routes)) : NULL) + +#define RE_DEST_NEXT_ROUTE(dest, re) \ + ((re) = (dest) ? re_list_next(&((dest)->routes), (re)) : NULL) #define RNODE_FOREACH_RE(rn, re) \ RE_DEST_FOREACH_ROUTE (rib_dest_from_rnode(rn), re) @@ -202,6 +271,10 @@ typedef struct rib_dest_t_ { #define RNODE_FOREACH_RE_SAFE(rn, re, next) \ RE_DEST_FOREACH_ROUTE_SAFE (rib_dest_from_rnode(rn), re, next) +#define RNODE_FIRST_RE(rn, re) RE_DEST_FIRST_ROUTE(rib_dest_from_rnode(rn), re) + +#define RNODE_NEXT_RE(rn, re) RE_DEST_NEXT_ROUTE(rib_dest_from_rnode(rn), re) + #if defined(HAVE_RTADV) /* Structure which hold status of router advertisement. */ struct rtadv { @@ -221,7 +294,7 @@ struct rtadv { * Structure that is hung off of a route_table that holds information about * the table. */ -typedef struct rib_table_info_t_ { +struct rib_table_info { /* * Back pointer to zebra_vrf. @@ -229,14 +302,14 @@ typedef struct rib_table_info_t_ { struct zebra_vrf *zvrf; afi_t afi; safi_t safi; + uint32_t table_id; +}; -} rib_table_info_t; - -typedef enum { +enum rib_tables_iter_state { RIB_TABLES_ITER_S_INIT, RIB_TABLES_ITER_S_ITERATING, RIB_TABLES_ITER_S_DONE -} rib_tables_iter_state_t; +}; /* * Structure that holds state for iterating over all tables in the @@ -246,61 +319,26 @@ typedef struct rib_tables_iter_t_ { vrf_id_t vrf_id; int afi_safi_ix; - rib_tables_iter_state_t state; + enum rib_tables_iter_state state; } rib_tables_iter_t; /* Events/reasons triggering a RIB update. */ -typedef enum { - RIB_UPDATE_IF_CHANGE, +enum rib_update_event { + RIB_UPDATE_KERNEL, RIB_UPDATE_RMAP_CHANGE, - RIB_UPDATE_OTHER -} rib_update_event_t; - -extern struct nexthop *route_entry_nexthop_ifindex_add(struct route_entry *re, - ifindex_t ifindex, - vrf_id_t nh_vrf_id); -extern struct nexthop * -route_entry_nexthop_blackhole_add(struct route_entry *re, - enum blackhole_type bh_type); -extern struct nexthop *route_entry_nexthop_ipv4_add(struct route_entry *re, - struct in_addr *ipv4, - struct in_addr *src, - vrf_id_t nh_vrf_id); -extern struct nexthop * -route_entry_nexthop_ipv4_ifindex_add(struct route_entry *re, - struct in_addr *ipv4, struct in_addr *src, - ifindex_t ifindex, vrf_id_t nh_vrf_id); -extern void route_entry_nexthop_delete(struct route_entry *re, - struct nexthop *nexthop); -extern struct nexthop *route_entry_nexthop_ipv6_add(struct route_entry *re, - struct in6_addr *ipv6, - vrf_id_t nh_vrf_id); -extern struct nexthop * -route_entry_nexthop_ipv6_ifindex_add(struct route_entry *re, - struct in6_addr *ipv6, ifindex_t ifindex, - vrf_id_t nh_vrf_id); -extern void route_entry_nexthop_add(struct route_entry *re, - struct nexthop *nexthop); + RIB_UPDATE_OTHER, + RIB_UPDATE_MAX +}; + extern void route_entry_copy_nexthops(struct route_entry *re, struct nexthop *nh); +int route_entry_update_nhe(struct route_entry *re, + struct nhg_hash_entry *new_nhghe); #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, const struct route_entry *re); -/* RPF lookup behaviour */ -enum multicast_mode { - MCAST_NO_CONFIG = 0, /* MIX_MRIB_FIRST, but no show in config write */ - MCAST_MRIB_ONLY, /* MRIB only */ - MCAST_URIB_ONLY, /* URIB only */ - MCAST_MIX_MRIB_FIRST, /* MRIB, if nothing at all then URIB */ - MCAST_MIX_DISTANCE, /* MRIB & URIB, lower distance wins */ - MCAST_MIX_PFXLEN, /* MRIB & URIB, longer prefix wins */ - /* on equal value, MRIB wins for last 2 */ -}; - -extern void multicast_mode_ipv4_set(enum multicast_mode mode); -extern enum multicast_mode multicast_mode_ipv4_get(void); extern void rib_lookup_and_dump(struct prefix_ipv4 *p, vrf_id_t vrf_id); extern void rib_lookup_and_pushup(struct prefix_ipv4 *p, vrf_id_t vrf_id); @@ -326,17 +364,24 @@ extern void rib_uninstall_kernel(struct route_node *rn, struct route_entry *re); extern int rib_add(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, unsigned short instance, int flags, struct prefix *p, struct prefix_ipv6 *src_p, const struct nexthop *nh, - uint32_t table_id, uint32_t metric, uint32_t mtu, - uint8_t distance, route_tag_t tag); - + uint32_t nhe_id, uint32_t table_id, uint32_t metric, + uint32_t mtu, uint8_t distance, route_tag_t tag); +/* + * Multipath route apis. + */ extern int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *p, - struct prefix_ipv6 *src_p, struct route_entry *re); + struct prefix_ipv6 *src_p, struct route_entry *re, + struct nexthop_group *ng); +extern int rib_add_multipath_nhe(afi_t afi, safi_t safi, struct prefix *p, + struct prefix_ipv6 *src_p, + struct route_entry *re, + struct nhg_hash_entry *nhe); extern void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, unsigned short instance, int flags, struct prefix *p, struct prefix_ipv6 *src_p, const struct nexthop *nh, - uint32_t table_id, uint32_t metric, uint8_t distance, - bool fromkernel); + uint32_t nhe_id, uint32_t table_id, uint32_t metric, + uint8_t distance, bool fromkernel, bool connected_down); extern struct route_entry *rib_match(afi_t afi, safi_t safi, vrf_id_t vrf_id, union g_addr *addr, @@ -348,10 +393,11 @@ extern struct route_entry *rib_match_ipv4_multicast(vrf_id_t vrf_id, extern struct route_entry *rib_lookup_ipv4(struct prefix_ipv4 *p, vrf_id_t vrf_id); -extern void rib_update(vrf_id_t vrf_id, rib_update_event_t event); +extern void rib_update(enum rib_update_event event); +extern void rib_update_vrf(vrf_id_t vrf_id, enum rib_update_event event); extern void rib_update_table(struct route_table *table, - rib_update_event_t event); -extern void rib_sweep_route(void); + enum rib_update_event event); +extern int rib_sweep_route(struct thread *t); extern void rib_sweep_table(struct route_table *table); extern void rib_close_table(struct route_table *table); extern void rib_init(void); @@ -359,7 +405,13 @@ extern unsigned long rib_score_proto(uint8_t proto, unsigned short instance); extern unsigned long rib_score_proto_table(uint8_t proto, unsigned short instance, struct route_table *table); -extern void rib_queue_add(struct route_node *rn); + +extern int rib_queue_add(struct route_node *rn); + +struct nhg_ctx; /* Forward declaration */ + +extern int rib_queue_nhg_add(struct nhg_ctx *ctx); + extern void meta_queue_free(struct meta_queue *mq); extern int zebra_rib_labeled_unicast(struct route_entry *re); extern struct route_table *rib_table_ipv6; @@ -379,9 +431,9 @@ extern void zebra_rib_evaluate_rn_nexthops(struct route_node *rn, uint32_t seq); /* * rib_table_info */ -static inline rib_table_info_t *rib_table_info(struct route_table *table) +static inline struct rib_table_info *rib_table_info(struct route_table *table) { - return (rib_table_info_t *)route_table_get_info(table); + return (struct rib_table_info *)route_table_get_info(table); } /* @@ -406,7 +458,7 @@ static inline struct route_entry *rnode_to_ribs(struct route_node *rn) if (!dest) return NULL; - return dest->routes; + return re_list_first(&dest->routes); } /* @@ -461,7 +513,7 @@ static inline void rib_tables_iter_init(rib_tables_iter_t *iter) /* * rib_tables_iter_started * - * Returns TRUE if this iterator has started iterating over the set of + * Returns true if this iterator has started iterating over the set of * tables. */ static inline int rib_tables_iter_started(rib_tables_iter_t *iter) @@ -480,6 +532,30 @@ static inline void rib_tables_iter_cleanup(rib_tables_iter_t *iter) DECLARE_HOOK(rib_update, (struct route_node * rn, const char *reason), (rn, reason)) +/* + * Access installed/fib nexthops, which may be a subset of the + * rib nexthops. + */ +static inline struct nexthop_group *rib_get_fib_nhg(struct route_entry *re) +{ + /* If the fib set is a subset of the active rib set, + * use the dedicated fib list. + */ + if (CHECK_FLAG(re->status, ROUTE_ENTRY_USE_FIB_NHG)) + return &(re->fib_ng); + else + return &(re->nhe->nhg); +} + +/* + * Access backup nexthop-group that represents the installed backup nexthops; + * any installed backup will be on the fib list. + */ +static inline struct nexthop_group *rib_get_fib_backup_nhg( + struct route_entry *re) +{ + return &(re->fib_backup_ng); +} extern void zebra_vty_init(void); diff --git a/zebra/router-id.c b/zebra/router-id.c index 569ffbab41..7af60a389b 100644 --- a/zebra/router-id.c +++ b/zebra/router-id.c @@ -59,9 +59,6 @@ static struct connected *router_id_find_node(struct list *l, static int router_id_bad_address(struct connected *ifc) { - if (ifc->address->family != AF_INET) - return 1; - /* non-redistributable addresses shouldn't be used for RIDs either */ if (!zebra_check_addr(ifc->address)) return 1; @@ -69,50 +66,82 @@ static int router_id_bad_address(struct connected *ifc) return 0; } -void router_id_get(struct prefix *p, vrf_id_t vrf_id) +static bool router_id_v6_is_any(struct prefix *p) +{ + return memcmp(&p->u.prefix6, &in6addr_any, sizeof(struct in6_addr)) + == 0; +} + +int router_id_get(afi_t afi, struct prefix *p, struct zebra_vrf *zvrf) { struct listnode *node; struct connected *c; - struct zebra_vrf *zvrf = vrf_info_get(vrf_id); - - p->u.prefix4.s_addr = 0; - p->family = AF_INET; - p->prefixlen = 32; - - if (zvrf->rid_user_assigned.u.prefix4.s_addr) - p->u.prefix4.s_addr = zvrf->rid_user_assigned.u.prefix4.s_addr; - else if (!list_isempty(zvrf->rid_lo_sorted_list)) { - node = listtail(zvrf->rid_lo_sorted_list); - c = listgetdata(node); - p->u.prefix4.s_addr = c->address->u.prefix4.s_addr; - } else if (!list_isempty(zvrf->rid_all_sorted_list)) { - node = listtail(zvrf->rid_all_sorted_list); - c = listgetdata(node); - p->u.prefix4.s_addr = c->address->u.prefix4.s_addr; + struct in6_addr *addr = NULL; + + switch (afi) { + case AFI_IP: + p->u.prefix4.s_addr = INADDR_ANY; + p->family = AF_INET; + p->prefixlen = 32; + if (zvrf->rid_user_assigned.u.prefix4.s_addr != INADDR_ANY) + p->u.prefix4.s_addr = + zvrf->rid_user_assigned.u.prefix4.s_addr; + else if (!list_isempty(zvrf->rid_lo_sorted_list)) { + node = listtail(zvrf->rid_lo_sorted_list); + c = listgetdata(node); + p->u.prefix4.s_addr = c->address->u.prefix4.s_addr; + } else if (!list_isempty(zvrf->rid_all_sorted_list)) { + node = listtail(zvrf->rid_all_sorted_list); + c = listgetdata(node); + p->u.prefix4.s_addr = c->address->u.prefix4.s_addr; + } + return 0; + case AFI_IP6: + p->u.prefix6 = in6addr_any; + p->family = AF_INET6; + p->prefixlen = 128; + if (!router_id_v6_is_any(&zvrf->rid6_user_assigned)) + addr = &zvrf->rid6_user_assigned.u.prefix6; + else if (!list_isempty(zvrf->rid6_lo_sorted_list)) { + node = listtail(zvrf->rid6_lo_sorted_list); + c = listgetdata(node); + addr = &c->address->u.prefix6; + } else if (!list_isempty(zvrf->rid6_all_sorted_list)) { + node = listtail(zvrf->rid6_all_sorted_list); + c = listgetdata(node); + addr = &c->address->u.prefix6; + } + if (addr) + memcpy(&p->u.prefix6, addr, sizeof(struct in6_addr)); + return 0; + default: + return -1; } } -static void router_id_set(struct prefix *p, vrf_id_t vrf_id) +static int router_id_set(afi_t afi, struct prefix *p, struct zebra_vrf *zvrf) { struct prefix p2; struct listnode *node; struct zserv *client; - struct zebra_vrf *zvrf; - - if (p->u.prefix4.s_addr == 0) /* unset */ - { - zvrf = vrf_info_lookup(vrf_id); - if (!zvrf) - return; - } else /* set */ - zvrf = vrf_info_get(vrf_id); - zvrf->rid_user_assigned.u.prefix4.s_addr = p->u.prefix4.s_addr; + switch (afi) { + case AFI_IP: + zvrf->rid_user_assigned.u.prefix4.s_addr = p->u.prefix4.s_addr; + break; + case AFI_IP6: + zvrf->rid6_user_assigned.u.prefix6 = p->u.prefix6; + break; + default: + return -1; + } - router_id_get(&p2, vrf_id); + router_id_get(afi, &p2, zvrf); for (ALL_LIST_ELEMENTS_RO(zrouter.client_list, node, client)) - zsend_router_id_update(client, &p2, vrf_id); + zsend_router_id_update(client, afi, &p2, zvrf->vrf->vrf_id); + + return 0; } void router_id_add_address(struct connected *ifc) @@ -123,27 +152,42 @@ void router_id_add_address(struct connected *ifc) struct prefix after; struct zserv *client; struct zebra_vrf *zvrf = vrf_info_get(ifc->ifp->vrf_id); + afi_t afi; + struct list *rid_lo; + struct list *rid_all; if (router_id_bad_address(ifc)) return; - router_id_get(&before, zvrf_id(zvrf)); + switch (ifc->address->family) { + case AF_INET: + afi = AFI_IP; + rid_lo = zvrf->rid_lo_sorted_list; + rid_all = zvrf->rid_all_sorted_list; + break; + case AF_INET6: + afi = AFI_IP6; + rid_lo = zvrf->rid6_lo_sorted_list; + rid_all = zvrf->rid6_all_sorted_list; + break; + default: + return; + } - if (if_is_loopback(ifc->ifp)) - l = zvrf->rid_lo_sorted_list; - else - l = zvrf->rid_all_sorted_list; + router_id_get(afi, &before, zvrf); + + l = if_is_loopback(ifc->ifp) ? rid_lo : rid_all; if (!router_id_find_node(l, ifc)) listnode_add_sort(l, ifc); - router_id_get(&after, zvrf_id(zvrf)); + router_id_get(afi, &after, zvrf); if (prefix_same(&before, &after)) return; for (ALL_LIST_ELEMENTS_RO(zrouter.client_list, node, client)) - zsend_router_id_update(client, &after, zvrf_id(zvrf)); + zsend_router_id_update(client, afi, &after, zvrf_id(zvrf)); } void router_id_del_address(struct connected *ifc) @@ -155,104 +199,341 @@ void router_id_del_address(struct connected *ifc) struct listnode *node; struct zserv *client; struct zebra_vrf *zvrf = vrf_info_get(ifc->ifp->vrf_id); + afi_t afi; + struct list *rid_lo; + struct list *rid_all; if (router_id_bad_address(ifc)) return; - router_id_get(&before, zvrf_id(zvrf)); + switch (ifc->address->family) { + case AF_INET: + afi = AFI_IP; + rid_lo = zvrf->rid_lo_sorted_list; + rid_all = zvrf->rid_all_sorted_list; + break; + case AF_INET6: + afi = AFI_IP6; + rid_lo = zvrf->rid6_lo_sorted_list; + rid_all = zvrf->rid6_all_sorted_list; + break; + default: + return; + } + + router_id_get(afi, &before, zvrf); if (if_is_loopback(ifc->ifp)) - l = zvrf->rid_lo_sorted_list; + l = rid_lo; else - l = zvrf->rid_all_sorted_list; + l = rid_all; if ((c = router_id_find_node(l, ifc))) listnode_delete(l, c); - router_id_get(&after, zvrf_id(zvrf)); + router_id_get(afi, &after, zvrf); if (prefix_same(&before, &after)) return; for (ALL_LIST_ELEMENTS_RO(zrouter.client_list, node, client)) - zsend_router_id_update(client, &after, zvrf_id(zvrf)); + zsend_router_id_update(client, afi, &after, zvrf_id(zvrf)); } -void router_id_write(struct vty *vty) +void router_id_write(struct vty *vty, struct zebra_vrf *zvrf) { - struct vrf *vrf; + char space[2]; + + memset(space, 0, sizeof(space)); + + if (zvrf_id(zvrf) != VRF_DEFAULT) + snprintf(space, sizeof(space), "%s", " "); + + if (zvrf->rid_user_assigned.u.prefix4.s_addr != INADDR_ANY) { + vty_out(vty, "%sip router-id %pI4\n", space, + &zvrf->rid_user_assigned.u.prefix4); + } + if (!router_id_v6_is_any(&zvrf->rid6_user_assigned)) { + vty_out(vty, "%sipv6 router-id %pI6\n", space, + &zvrf->rid_user_assigned.u.prefix6); + } +} + +DEFUN (ip_router_id, + ip_router_id_cmd, + "ip router-id A.B.C.D vrf NAME", + IP_STR + "Manually set the router-id\n" + "IP address to use for router-id\n" + VRF_CMD_HELP_STR) +{ + int idx = 0; + struct prefix rid; + vrf_id_t vrf_id; struct zebra_vrf *zvrf; - RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) - if ((zvrf = vrf->info) != NULL) - if (zvrf->rid_user_assigned.u.prefix4.s_addr) { - if (zvrf_id(zvrf) == VRF_DEFAULT) - vty_out(vty, "router-id %s\n", - inet_ntoa( - zvrf->rid_user_assigned - .u.prefix4)); - else - vty_out(vty, "router-id %s vrf %s\n", - inet_ntoa( - zvrf->rid_user_assigned - .u.prefix4), - zvrf_name(zvrf)); - } + argv_find(argv, argc, "A.B.C.D", &idx); + + if (!inet_pton(AF_INET, argv[idx]->arg, &rid.u.prefix4)) + return CMD_WARNING_CONFIG_FAILED; + + rid.prefixlen = 32; + rid.family = AF_INET; + + argv_find(argv, argc, "NAME", &idx); + VRF_GET_ID(vrf_id, argv[idx]->arg, false); + + zvrf = vrf_info_lookup(vrf_id); + router_id_set(AFI_IP, &rid, zvrf); + + return CMD_SUCCESS; } -DEFUN (router_id, +ALIAS (ip_router_id, router_id_cmd, - "router-id A.B.C.D [vrf NAME]", + "router-id A.B.C.D vrf NAME", "Manually set the router-id\n" "IP address to use for router-id\n" + VRF_CMD_HELP_STR); + +DEFUN (ipv6_router_id, + ipv6_router_id_cmd, + "ipv6 router-id X:X::X:X vrf NAME", + IPV6_STR + "Manually set the router-id\n" + "IPv6 address to use for router-id\n" VRF_CMD_HELP_STR) { - int idx_ipv4 = 1; - int idx_name = 3; + int idx = 0; + struct prefix rid; + vrf_id_t vrf_id; + struct zebra_vrf *zvrf; + argv_find(argv, argc, "X:X::X:X", &idx); + + if (!inet_pton(AF_INET6, argv[idx]->arg, &rid.u.prefix6)) + return CMD_WARNING_CONFIG_FAILED; + + rid.prefixlen = 128; + rid.family = AF_INET6; + + argv_find(argv, argc, "NAME", &idx); + VRF_GET_ID(vrf_id, argv[idx]->arg, false); + + zvrf = vrf_info_lookup(vrf_id); + router_id_set(AFI_IP6, &rid, zvrf); + + return CMD_SUCCESS; +} + + +DEFUN (ip_router_id_in_vrf, + ip_router_id_in_vrf_cmd, + "ip router-id A.B.C.D", + IP_STR + "Manually set the router-id\n" + "IP address to use for router-id\n") +{ + ZEBRA_DECLVAR_CONTEXT(vrf, zvrf); + int idx = 0; struct prefix rid; - vrf_id_t vrf_id = VRF_DEFAULT; - rid.u.prefix4.s_addr = inet_addr(argv[idx_ipv4]->arg); - if (!rid.u.prefix4.s_addr) + argv_find(argv, argc, "A.B.C.D", &idx); + + if (!inet_pton(AF_INET, argv[idx]->arg, &rid.u.prefix4)) return CMD_WARNING_CONFIG_FAILED; rid.prefixlen = 32; rid.family = AF_INET; - if (argc > 2) - VRF_GET_ID(vrf_id, argv[idx_name]->arg, false); + router_id_set(AFI_IP, &rid, zvrf); + + return CMD_SUCCESS; +} + +ALIAS (ip_router_id_in_vrf, + router_id_in_vrf_cmd, + "router-id A.B.C.D", + "Manually set the router-id\n" + "IP address to use for router-id\n"); + +DEFUN (ipv6_router_id_in_vrf, + ipv6_router_id_in_vrf_cmd, + "ipv6 router-id X:X::X:X", + IP6_STR + "Manually set the IPv6 router-id\n" + "IPV6 address to use for router-id\n") +{ + ZEBRA_DECLVAR_CONTEXT(vrf, zvrf); + int idx = 0; + struct prefix rid; + + argv_find(argv, argc, "X:X::X:X", &idx); + + if (!inet_pton(AF_INET6, argv[idx]->arg, &rid.u.prefix6)) + return CMD_WARNING_CONFIG_FAILED; + + rid.prefixlen = 128; + rid.family = AF_INET6; + + router_id_set(AFI_IP6, &rid, zvrf); + + return CMD_SUCCESS; +} + +DEFUN (no_ip_router_id, + no_ip_router_id_cmd, + "no ip router-id [A.B.C.D vrf NAME]", + NO_STR + IP_STR + "Remove the manually configured router-id\n" + "IP address to use for router-id\n" + VRF_CMD_HELP_STR) +{ + int idx = 0; + struct prefix rid; + vrf_id_t vrf_id = VRF_DEFAULT; + struct zebra_vrf *zvrf; + + rid.u.prefix4.s_addr = 0; + rid.prefixlen = 0; + rid.family = AF_INET; + + if (argv_find(argv, argc, "NAME", &idx)) + VRF_GET_ID(vrf_id, argv[idx]->arg, false); - router_id_set(&rid, vrf_id); + zvrf = vrf_info_lookup(vrf_id); + router_id_set(AFI_IP, &rid, zvrf); return CMD_SUCCESS; } -DEFUN (no_router_id, +ALIAS (no_ip_router_id, no_router_id_cmd, - "no router-id [A.B.C.D [vrf NAME]]", + "no router-id [A.B.C.D vrf NAME]", NO_STR "Remove the manually configured router-id\n" "IP address to use for router-id\n" + VRF_CMD_HELP_STR); + +DEFUN (no_ipv6_router_id, + no_ipv6_router_id_cmd, + "no ipv6 router-id [X:X::X:X vrf NAME]", + NO_STR + IPV6_STR + "Remove the manually configured IPv6 router-id\n" + "IPv6 address to use for router-id\n" VRF_CMD_HELP_STR) { - int idx_name = 4; - + int idx = 0; struct prefix rid; vrf_id_t vrf_id = VRF_DEFAULT; + struct zebra_vrf *zvrf; + + memset(&rid, 0, sizeof(rid)); + rid.family = AF_INET; + + if (argv_find(argv, argc, "NAME", &idx)) + VRF_GET_ID(vrf_id, argv[idx]->arg, false); + + zvrf = vrf_info_lookup(vrf_id); + router_id_set(AFI_IP6, &rid, zvrf); + + return CMD_SUCCESS; +} + +DEFUN (no_ip_router_id_in_vrf, + no_ip_router_id_in_vrf_cmd, + "no ip router-id [A.B.C.D]", + NO_STR + IP_STR + "Remove the manually configured router-id\n" + "IP address to use for router-id\n") +{ + ZEBRA_DECLVAR_CONTEXT(vrf, zvrf); + + struct prefix rid; rid.u.prefix4.s_addr = 0; rid.prefixlen = 0; rid.family = AF_INET; - if (argc > 3) - VRF_GET_ID(vrf_id, argv[idx_name]->arg, false); + router_id_set(AFI_IP, &rid, zvrf); - router_id_set(&rid, vrf_id); + return CMD_SUCCESS; +} + +ALIAS (no_ip_router_id_in_vrf, + no_router_id_in_vrf_cmd, + "no router-id [A.B.C.D]", + NO_STR + "Remove the manually configured router-id\n" + "IP address to use for router-id\n"); + +DEFUN (no_ipv6_router_id_in_vrf, + no_ipv6_router_id_in_vrf_cmd, + "no ipv6 router-id [X:X::X:X]", + NO_STR + IP6_STR + "Remove the manually configured IPv6 router-id\n" + "IPv6 address to use for router-id\n") +{ + ZEBRA_DECLVAR_CONTEXT(vrf, zvrf); + + struct prefix rid; + + memset(&rid, 0, sizeof(rid)); + rid.family = AF_INET; + + router_id_set(AFI_IP6, &rid, zvrf); return CMD_SUCCESS; } +DEFUN (show_ip_router_id, + show_ip_router_id_cmd, + "show [ip|ipv6] router-id [vrf NAME]", + SHOW_STR + IP_STR + IPV6_STR + "Show the configured router-id\n" + VRF_CMD_HELP_STR) +{ + int idx = 0; + vrf_id_t vrf_id = VRF_DEFAULT; + struct zebra_vrf *zvrf; + const char *vrf_name = "default"; + char addr_name[INET6_ADDRSTRLEN]; + int is_ipv6 = 0; + + is_ipv6 = argv_find(argv, argc, "ipv6", &idx); + + if (argv_find(argv, argc, "NAME", &idx)) { + VRF_GET_ID(vrf_id, argv[idx]->arg, false); + vrf_name = argv[idx]->arg; + } + + zvrf = vrf_info_get(vrf_id); + + if (zvrf != NULL) { + if (is_ipv6) { + if (router_id_v6_is_any(&zvrf->rid6_user_assigned)) + return CMD_SUCCESS; + inet_ntop(AF_INET6, &zvrf->rid6_user_assigned.u.prefix6, + addr_name, sizeof(addr_name)); + } else { + if (zvrf->rid_user_assigned.u.prefix4.s_addr == 0) + return CMD_SUCCESS; + inet_ntop(AF_INET, &zvrf->rid_user_assigned.u.prefix4, + addr_name, sizeof(addr_name)); + } + + vty_out(vty, "zebra:\n"); + vty_out(vty, " router-id %s vrf %s\n", addr_name, vrf_name); + } + + return CMD_SUCCESS; +} static int router_id_cmp(void *a, void *b) { @@ -263,25 +544,62 @@ static int router_id_cmp(void *a, void *b) &ifb->address->u.prefix4.s_addr); } +static int router_id_v6_cmp(void *a, void *b) +{ + const struct connected *ifa = (const struct connected *)a; + const struct connected *ifb = (const struct connected *)b; + + return IPV6_ADDR_CMP(&ifa->address->u.prefix6, + &ifb->address->u.prefix6); +} + void router_id_cmd_init(void) { + install_element(CONFIG_NODE, &ip_router_id_cmd); install_element(CONFIG_NODE, &router_id_cmd); + install_element(CONFIG_NODE, &ipv6_router_id_cmd); + install_element(CONFIG_NODE, &no_ip_router_id_cmd); install_element(CONFIG_NODE, &no_router_id_cmd); + install_element(CONFIG_NODE, &ip_router_id_in_vrf_cmd); + install_element(VRF_NODE, &ip_router_id_in_vrf_cmd); + install_element(CONFIG_NODE, &router_id_in_vrf_cmd); + install_element(VRF_NODE, &router_id_in_vrf_cmd); + install_element(CONFIG_NODE, &ipv6_router_id_in_vrf_cmd); + install_element(VRF_NODE, &ipv6_router_id_in_vrf_cmd); + install_element(CONFIG_NODE, &no_ipv6_router_id_cmd); + install_element(CONFIG_NODE, &no_ip_router_id_in_vrf_cmd); + install_element(VRF_NODE, &no_ip_router_id_in_vrf_cmd); + install_element(CONFIG_NODE, &no_router_id_in_vrf_cmd); + install_element(VRF_NODE, &no_router_id_in_vrf_cmd); + install_element(CONFIG_NODE, &no_ipv6_router_id_in_vrf_cmd); + install_element(VRF_NODE, &no_ipv6_router_id_in_vrf_cmd); + install_element(VIEW_NODE, &show_ip_router_id_cmd); } void router_id_init(struct zebra_vrf *zvrf) { zvrf->rid_all_sorted_list = &zvrf->_rid_all_sorted_list; zvrf->rid_lo_sorted_list = &zvrf->_rid_lo_sorted_list; + zvrf->rid6_all_sorted_list = &zvrf->_rid6_all_sorted_list; + zvrf->rid6_lo_sorted_list = &zvrf->_rid6_lo_sorted_list; memset(zvrf->rid_all_sorted_list, 0, sizeof(zvrf->_rid_all_sorted_list)); memset(zvrf->rid_lo_sorted_list, 0, sizeof(zvrf->_rid_lo_sorted_list)); memset(&zvrf->rid_user_assigned, 0, sizeof(zvrf->rid_user_assigned)); + memset(zvrf->rid6_all_sorted_list, 0, + sizeof(zvrf->_rid6_all_sorted_list)); + memset(zvrf->rid6_lo_sorted_list, 0, + sizeof(zvrf->_rid6_lo_sorted_list)); + memset(&zvrf->rid6_user_assigned, 0, sizeof(zvrf->rid6_user_assigned)); zvrf->rid_all_sorted_list->cmp = router_id_cmp; zvrf->rid_lo_sorted_list->cmp = router_id_cmp; + zvrf->rid6_all_sorted_list->cmp = router_id_v6_cmp; + zvrf->rid6_lo_sorted_list->cmp = router_id_v6_cmp; zvrf->rid_user_assigned.family = AF_INET; zvrf->rid_user_assigned.prefixlen = 32; + zvrf->rid6_user_assigned.family = AF_INET6; + zvrf->rid6_user_assigned.prefixlen = 128; } diff --git a/zebra/router-id.h b/zebra/router-id.h index f7d16853f1..4a35f6605b 100644 --- a/zebra/router-id.h +++ b/zebra/router-id.h @@ -34,12 +34,12 @@ extern "C" { #endif -extern void router_id_add_address(struct connected *); -extern void router_id_del_address(struct connected *); -extern void router_id_init(struct zebra_vrf *); +extern void router_id_add_address(struct connected *c); +extern void router_id_del_address(struct connected *c); +extern void router_id_init(struct zebra_vrf *zvrf); extern void router_id_cmd_init(void); -extern void router_id_write(struct vty *); -extern void router_id_get(struct prefix *, vrf_id_t); +extern void router_id_write(struct vty *vty, struct zebra_vrf *zvrf); +extern int router_id_get(afi_t afi, struct prefix *p, struct zebra_vrf *zvrf); #ifdef __cplusplus } diff --git a/zebra/rt.h b/zebra/rt.h index 08b51fcc0b..48f1df2868 100644 --- a/zebra/rt.h +++ b/zebra/rt.h @@ -35,16 +35,22 @@ extern "C" { #endif -#define RSYSTEM_ROUTE(type) \ - ((type) == ZEBRA_ROUTE_KERNEL || (type) == ZEBRA_ROUTE_CONNECT) +#define RKERNEL_ROUTE(type) ((type) == ZEBRA_ROUTE_KERNEL) +#define RSYSTEM_ROUTE(type) \ + ((RKERNEL_ROUTE(type)) || (type) == ZEBRA_ROUTE_CONNECT) + +#ifndef HAVE_NETLINK /* - * Update or delete a route, LSP, or pseudowire from the kernel, - * using info from a dataplane context. + * Update or delete a route, nexthop, LSP, pseudowire, or vxlan MAC from the + * kernel, using info from a dataplane context. */ extern enum zebra_dplane_result kernel_route_update( struct zebra_dplane_ctx *ctx); +extern enum zebra_dplane_result +kernel_nexthop_update(struct zebra_dplane_ctx *ctx); + extern enum zebra_dplane_result kernel_lsp_update( struct zebra_dplane_ctx *ctx); @@ -53,6 +59,15 @@ enum zebra_dplane_result kernel_pw_update(struct zebra_dplane_ctx *ctx); enum zebra_dplane_result kernel_address_update_ctx( struct zebra_dplane_ctx *ctx); +enum zebra_dplane_result kernel_mac_update_ctx(struct zebra_dplane_ctx *ctx); + +enum zebra_dplane_result kernel_neigh_update_ctx(struct zebra_dplane_ctx *ctx); + +extern enum zebra_dplane_result +kernel_pbr_rule_update(struct zebra_dplane_ctx *ctx); + +#endif /* !HAVE_NETLINK */ + extern int kernel_neigh_update(int cmd, int ifindex, uint32_t addr, char *lla, int llalen, ns_id_t ns_id); extern int kernel_interface_set_master(struct interface *master, @@ -60,23 +75,8 @@ extern int kernel_interface_set_master(struct interface *master, extern int mpls_kernel_init(void); -extern uint32_t kernel_get_speed(struct interface *ifp); +extern uint32_t kernel_get_speed(struct interface *ifp, int *error); extern int kernel_get_ipmr_sg_stats(struct zebra_vrf *zvrf, void *mroute); -extern int kernel_add_vtep(vni_t vni, struct interface *ifp, - struct in_addr *vtep_ip); -extern int kernel_del_vtep(vni_t vni, struct interface *ifp, - struct in_addr *vtep_ip); -extern int kernel_add_mac(struct interface *ifp, vlanid_t vid, - struct ethaddr *mac, struct in_addr vtep_ip, - bool sticky); -extern int kernel_del_mac(struct interface *ifp, vlanid_t vid, - struct ethaddr *mac, struct in_addr vtep_ip); - -extern int kernel_add_neigh(struct interface *ifp, struct ipaddr *ip, - struct ethaddr *mac, uint8_t flags); -extern int kernel_del_neigh(struct interface *ifp, struct ipaddr *ip); -extern int kernel_upd_neigh(struct interface *ifp, struct ipaddr *ip, - struct ethaddr *mac, uint8_t flags, uint16_t state); /* * Southbound Initialization routines to get initial starting @@ -96,6 +96,16 @@ extern void neigh_read_for_vlan(struct zebra_ns *zns, struct interface *ifp); extern void neigh_read_specific_ip(struct ipaddr *ip, struct interface *vlan_if); extern void route_read(struct zebra_ns *zns); +extern int kernel_upd_mac_nh(uint32_t nh_id, struct in_addr vtep_ip); +extern int kernel_del_mac_nh(uint32_t nh_id); +extern int kernel_upd_mac_nhg(uint32_t nhg_id, uint32_t nh_cnt, + struct nh_grp *nh_ids); +extern int kernel_del_mac_nhg(uint32_t nhg_id); + +/* + * Message batching interface. + */ +extern void kernel_update_multi(struct dplane_ctx_q *ctx_list); #ifdef __cplusplus } diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 289ed5a15b..770feb52bd 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -27,6 +27,7 @@ #include #include #include +#include /* Hack for GNU libc version 2. */ #ifndef MSG_TRUNC @@ -49,6 +50,7 @@ #include "vty.h" #include "mpls.h" #include "vxlan.h" +#include "printfrr.h" #include "zebra/zapi_msg.h" #include "zebra/zebra_ns.h" @@ -62,24 +64,66 @@ #include "zebra/zebra_mpls.h" #include "zebra/kernel_netlink.h" #include "zebra/rt_netlink.h" +#include "zebra/zebra_nhg.h" #include "zebra/zebra_mroute.h" #include "zebra/zebra_vxlan.h" #include "zebra/zebra_errors.h" +#include "zebra/zebra_evpn_mh.h" #ifndef AF_MPLS #define AF_MPLS 28 #endif +/* Re-defining as I am unable to include which has the + * UAPI for MAC sync. */ +#ifndef _UAPI_LINUX_IF_BRIDGE_H +/* FDB notification bits for NDA_NOTIFY: + * - BR_FDB_NFY_STATIC - notify on activity/expire even for a static entry + * - BR_FDB_NFY_INACTIVE - mark as inactive to avoid double notification, + * used with BR_FDB_NFY_STATIC (kernel controlled) + */ +enum { + BR_FDB_NFY_STATIC, + BR_FDB_NFY_INACTIVE, + BR_FDB_NFY_MAX +}; +#endif + static vlanid_t filter_vlan = 0; +/* We capture whether the current kernel supports nexthop ids; by + * default, we'll use them if possible. There's also a configuration + * available to _disable_ use of kernel nexthops. + */ +static bool supports_nh; + struct gw_family_t { uint16_t filler; uint16_t family; union g_addr gate; }; -char ipv4_ll_buf[16] = "169.254.0.1"; -struct in_addr ipv4_ll; +static const char ipv4_ll_buf[16] = "169.254.0.1"; +static struct in_addr ipv4_ll; + +/* Is this a ipv4 over ipv6 route? */ +static bool is_route_v4_over_v6(unsigned char rtm_family, + enum nexthop_types_t nexthop_type) +{ + if (rtm_family == AF_INET + && (nexthop_type == NEXTHOP_TYPE_IPV6 + || nexthop_type == NEXTHOP_TYPE_IPV6_IFINDEX)) + return true; + + return false; +} + +/* Helper to control use of kernel-level nexthop ids */ +static bool kernel_nexthops_supported(void) +{ + return (supports_nh && !vrf_is_backend_netns() + && zebra_nhg_kernel_nexthops_enabled()); +} /* * The ipv4_ll data structure is used for all 5549 @@ -92,7 +136,46 @@ void rt_netlink_init(void) inet_pton(AF_INET, ipv4_ll_buf, &ipv4_ll); } -static inline int is_selfroute(int proto) +/* + * Mapping from dataplane neighbor flags to netlink flags + */ +static uint8_t neigh_flags_to_netlink(uint8_t dplane_flags) +{ + uint8_t flags = 0; + + if (dplane_flags & DPLANE_NTF_EXT_LEARNED) + flags |= NTF_EXT_LEARNED; + if (dplane_flags & DPLANE_NTF_ROUTER) + flags |= NTF_ROUTER; + if (dplane_flags & DPLANE_NTF_USE) + flags |= NTF_USE; + + return flags; +} + +/* + * Mapping from dataplane neighbor state to netlink state + */ +static uint16_t neigh_state_to_netlink(uint16_t dplane_state) +{ + uint16_t state = 0; + + if (dplane_state & DPLANE_NUD_REACHABLE) + state |= NUD_REACHABLE; + if (dplane_state & DPLANE_NUD_STALE) + state |= NUD_STALE; + if (dplane_state & DPLANE_NUD_NOARP) + state |= NUD_NOARP; + if (dplane_state & DPLANE_NUD_PROBE) + state |= NUD_PROBE; + if (dplane_state & DPLANE_NUD_INCOMPLETE) + state |= NUD_INCOMPLETE; + + return state; +} + + +static inline bool is_selfroute(int proto) { if ((proto == RTPROT_BGP) || (proto == RTPROT_OSPF) || (proto == RTPROT_ZSTATIC) || (proto == RTPROT_ZEBRA) @@ -100,11 +183,12 @@ static inline int is_selfroute(int proto) || (proto == RTPROT_NHRP) || (proto == RTPROT_EIGRP) || (proto == RTPROT_LDP) || (proto == RTPROT_BABEL) || (proto == RTPROT_RIP) || (proto == RTPROT_SHARP) - || (proto == RTPROT_PBR) || (proto == RTPROT_OPENFABRIC)) { - return 1; + || (proto == RTPROT_PBR) || (proto == RTPROT_OPENFABRIC) + || (proto == RTPROT_SRTE)) { + return true; } - return 0; + return false; } static inline int zebra2proto(int proto) @@ -150,6 +234,13 @@ static inline int zebra2proto(int proto) case ZEBRA_ROUTE_OPENFABRIC: proto = RTPROT_OPENFABRIC; break; + case ZEBRA_ROUTE_SRTE: + proto = RTPROT_SRTE; + break; + case ZEBRA_ROUTE_TABLE: + case ZEBRA_ROUTE_NHG: + proto = RTPROT_ZEBRA; + break; default: /* * When a user adds a new protocol this will show up @@ -159,7 +250,7 @@ static inline int zebra2proto(int proto) */ zlog_debug( "%s: Please add this protocol(%d) to proper rt_netlink.c handling", - __PRETTY_FUNCTION__, proto); + __func__, proto); proto = RTPROT_ZEBRA; break; } @@ -167,7 +258,7 @@ static inline int zebra2proto(int proto) return proto; } -static inline int proto2zebra(int proto, int family) +static inline int proto2zebra(int proto, int family, bool is_nexthop) { switch (proto) { case RTPROT_BABEL: @@ -177,8 +268,8 @@ static inline int proto2zebra(int proto, int family) proto = ZEBRA_ROUTE_BGP; break; case RTPROT_OSPF: - proto = (family == AFI_IP) ? ZEBRA_ROUTE_OSPF - : ZEBRA_ROUTE_OSPF6; + proto = (family == AF_INET) ? ZEBRA_ROUTE_OSPF + : ZEBRA_ROUTE_OSPF6; break; case RTPROT_ISIS: proto = ZEBRA_ROUTE_ISIS; @@ -211,6 +302,15 @@ static inline int proto2zebra(int proto, int family) case RTPROT_OPENFABRIC: proto = ZEBRA_ROUTE_OPENFABRIC; break; + case RTPROT_SRTE: + proto = ZEBRA_ROUTE_SRTE; + break; + case RTPROT_ZEBRA: + if (is_nexthop) { + proto = ZEBRA_ROUTE_NHG; + break; + } + /* Intentional fall thru */ default: /* * When a user adds a new protocol this will show up @@ -220,7 +320,7 @@ static inline int proto2zebra(int proto, int family) */ zlog_debug( "%s: Please add this protocol(%d) to proper rt_netlink.c handling", - __PRETTY_FUNCTION__, proto); + __func__, proto); proto = ZEBRA_ROUTE_KERNEL; break; } @@ -230,7 +330,7 @@ static inline int proto2zebra(int proto, int family) /* Pending: create an efficient table_id (in a tree/hash) based lookup) */ -static vrf_id_t vrf_lookup_by_table(uint32_t table_id, ns_id_t ns_id) +vrf_id_t vrf_lookup_by_table(uint32_t table_id, ns_id_t ns_id) { struct vrf *vrf; struct zebra_vrf *zvrf; @@ -281,6 +381,167 @@ static int parse_encap_mpls(struct rtattr *tb, mpls_label_t *labels) return num_labels; } +static struct nexthop +parse_nexthop_unicast(ns_id_t ns_id, struct rtmsg *rtm, struct rtattr **tb, + enum blackhole_type bh_type, int index, void *prefsrc, + void *gate, afi_t afi, vrf_id_t vrf_id) +{ + struct interface *ifp = NULL; + struct nexthop nh = {0}; + mpls_label_t labels[MPLS_MAX_LABELS] = {0}; + int num_labels = 0; + + vrf_id_t nh_vrf_id = vrf_id; + size_t sz = (afi == AFI_IP) ? 4 : 16; + + if (bh_type == BLACKHOLE_UNSPEC) { + if (index && !gate) + nh.type = NEXTHOP_TYPE_IFINDEX; + else if (index && gate) + nh.type = (afi == AFI_IP) ? NEXTHOP_TYPE_IPV4_IFINDEX + : NEXTHOP_TYPE_IPV6_IFINDEX; + else if (!index && gate) + nh.type = (afi == AFI_IP) ? NEXTHOP_TYPE_IPV4 + : NEXTHOP_TYPE_IPV6; + else { + nh.type = NEXTHOP_TYPE_BLACKHOLE; + nh.bh_type = bh_type; + } + } else { + nh.type = NEXTHOP_TYPE_BLACKHOLE; + nh.bh_type = bh_type; + } + nh.ifindex = index; + if (prefsrc) + memcpy(&nh.src, prefsrc, sz); + if (gate) + memcpy(&nh.gate, gate, sz); + + if (index) { + ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(ns_id), index); + if (ifp) + nh_vrf_id = ifp->vrf_id; + } + nh.vrf_id = nh_vrf_id; + + if (tb[RTA_ENCAP] && tb[RTA_ENCAP_TYPE] + && *(uint16_t *)RTA_DATA(tb[RTA_ENCAP_TYPE]) + == LWTUNNEL_ENCAP_MPLS) { + num_labels = parse_encap_mpls(tb[RTA_ENCAP], labels); + } + + if (rtm->rtm_flags & RTNH_F_ONLINK) + SET_FLAG(nh.flags, NEXTHOP_FLAG_ONLINK); + + if (num_labels) + nexthop_add_labels(&nh, ZEBRA_LSP_STATIC, num_labels, labels); + + return nh; +} + +static uint8_t parse_multipath_nexthops_unicast(ns_id_t ns_id, + struct nexthop_group *ng, + struct rtmsg *rtm, + struct rtnexthop *rtnh, + struct rtattr **tb, + void *prefsrc, vrf_id_t vrf_id) +{ + void *gate = NULL; + struct interface *ifp = NULL; + int index = 0; + /* MPLS labels */ + mpls_label_t labels[MPLS_MAX_LABELS] = {0}; + int num_labels = 0; + struct rtattr *rtnh_tb[RTA_MAX + 1] = {}; + + int len = RTA_PAYLOAD(tb[RTA_MULTIPATH]); + vrf_id_t nh_vrf_id = vrf_id; + + for (;;) { + struct nexthop *nh = NULL; + + if (len < (int)sizeof(*rtnh) || rtnh->rtnh_len > len) + break; + + index = rtnh->rtnh_ifindex; + if (index) { + /* + * Yes we are looking this up + * for every nexthop and just + * using the last one looked + * up right now + */ + ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(ns_id), + index); + if (ifp) + nh_vrf_id = ifp->vrf_id; + else { + flog_warn( + EC_ZEBRA_UNKNOWN_INTERFACE, + "%s: Unknown interface %u specified, defaulting to VRF_DEFAULT", + __func__, index); + nh_vrf_id = VRF_DEFAULT; + } + } else + nh_vrf_id = vrf_id; + + if (rtnh->rtnh_len > sizeof(*rtnh)) { + memset(rtnh_tb, 0, sizeof(rtnh_tb)); + + netlink_parse_rtattr(rtnh_tb, RTA_MAX, RTNH_DATA(rtnh), + rtnh->rtnh_len - sizeof(*rtnh)); + if (rtnh_tb[RTA_GATEWAY]) + gate = RTA_DATA(rtnh_tb[RTA_GATEWAY]); + if (rtnh_tb[RTA_ENCAP] && rtnh_tb[RTA_ENCAP_TYPE] + && *(uint16_t *)RTA_DATA(rtnh_tb[RTA_ENCAP_TYPE]) + == LWTUNNEL_ENCAP_MPLS) { + num_labels = parse_encap_mpls( + rtnh_tb[RTA_ENCAP], labels); + } + } + + if (gate && rtm->rtm_family == AF_INET) { + if (index) + nh = nexthop_from_ipv4_ifindex( + gate, prefsrc, index, nh_vrf_id); + else + nh = nexthop_from_ipv4(gate, prefsrc, + nh_vrf_id); + } else if (gate && rtm->rtm_family == AF_INET6) { + if (index) + nh = nexthop_from_ipv6_ifindex( + gate, index, nh_vrf_id); + else + nh = nexthop_from_ipv6(gate, nh_vrf_id); + } else + nh = nexthop_from_ifindex(index, nh_vrf_id); + + if (nh) { + nh->weight = rtnh->rtnh_hops + 1; + + if (num_labels) + nexthop_add_labels(nh, ZEBRA_LSP_STATIC, + num_labels, labels); + + if (rtnh->rtnh_flags & RTNH_F_ONLINK) + SET_FLAG(nh->flags, NEXTHOP_FLAG_ONLINK); + + /* Add to temporary list */ + nexthop_group_add_sorted(ng, nh); + } + + if (rtnh->rtnh_len == 0) + break; + + len -= NLMSG_ALIGN(rtnh->rtnh_len); + rtnh = RTNH_NEXT(rtnh); + } + + uint8_t nhop_num = nexthop_group_nexthop_num(ng); + + return nhop_num; +} + /* Looking up routing table by netlink interface. */ static int netlink_route_change_read_unicast(struct nlmsghdr *h, ns_id_t ns_id, int startup) @@ -292,6 +553,7 @@ static int netlink_route_change_read_unicast(struct nlmsghdr *h, ns_id_t ns_id, struct prefix p; struct prefix_ipv6 src_p = {}; vrf_id_t vrf_id; + bool selfroute; char anyaddr[16] = {0}; @@ -302,6 +564,7 @@ static int netlink_route_change_read_unicast(struct nlmsghdr *h, ns_id_t ns_id, uint32_t mtu = 0; uint8_t distance = 0; route_tag_t tag = 0; + uint32_t nhe_id = 0; void *dest = NULL; void *gate = NULL; @@ -309,10 +572,6 @@ static int netlink_route_change_read_unicast(struct nlmsghdr *h, ns_id_t ns_id, void *src = NULL; /* IPv6 srcdest source prefix */ enum blackhole_type bh_type = BLACKHOLE_UNSPEC; - /* MPLS labels */ - mpls_label_t labels[MPLS_MAX_LABELS] = {0}; - int num_labels = 0; - rtm = NLMSG_DATA(h); if (startup && h->nlmsg_type != RTM_NEWROUTE) @@ -330,18 +589,23 @@ static int netlink_route_change_read_unicast(struct nlmsghdr *h, ns_id_t ns_id, bh_type = BLACKHOLE_ADMINPROHIB; break; default: + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("Route rtm_type: %s(%d) intentionally ignoring", + nl_rttype_to_str(rtm->rtm_type), + rtm->rtm_type); return 0; } len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct rtmsg)); if (len < 0) { - zlog_err("%s: Message received from netlink is of a broken size %d %zu", - __PRETTY_FUNCTION__, h->nlmsg_len, - (size_t)NLMSG_LENGTH(sizeof(struct rtmsg))); + zlog_err( + "%s: Message received from netlink is of a broken size %d %zu", + __func__, h->nlmsg_len, + (size_t)NLMSG_LENGTH(sizeof(struct rtmsg))); return -1; } - memset(tb, 0, sizeof tb); + memset(tb, 0, sizeof(tb)); netlink_parse_rtattr(tb, RTA_MAX, RTM_RTA(rtm), len); if (rtm->rtm_flags & RTM_F_CLONED) @@ -351,8 +615,9 @@ static int netlink_route_change_read_unicast(struct nlmsghdr *h, ns_id_t ns_id, if (rtm->rtm_protocol == RTPROT_KERNEL) return 0; - if (!startup && is_selfroute(rtm->rtm_protocol) - && h->nlmsg_type == RTM_NEWROUTE) { + selfroute = is_selfroute(rtm->rtm_protocol); + + if (!startup && selfroute && h->nlmsg_type == RTM_NEWROUTE) { if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug("Route type: %d Received that we think we have originated, ignoring", rtm->rtm_protocol); @@ -379,9 +644,9 @@ static int netlink_route_change_read_unicast(struct nlmsghdr *h, ns_id_t ns_id, } /* Route which inserted by Zebra. */ - if (is_selfroute(rtm->rtm_protocol)) { + if (selfroute) { flags |= ZEBRA_FLAG_SELFROUTE; - proto = proto2zebra(rtm->rtm_protocol, rtm->rtm_family); + proto = proto2zebra(rtm->rtm_protocol, rtm->rtm_family, false); } if (tb[RTA_OIF]) index = *(int *)RTA_DATA(tb[RTA_OIF]); @@ -402,6 +667,9 @@ static int netlink_route_change_read_unicast(struct nlmsghdr *h, ns_id_t ns_id, if (tb[RTA_GATEWAY]) gate = RTA_DATA(tb[RTA_GATEWAY]); + if (tb[RTA_NH_ID]) + nhe_id = *(uint32_t *)RTA_DATA(tb[RTA_NH_ID]); + if (tb[RTA_PRIORITY]) metric = *(int *)RTA_DATA(tb[RTA_PRIORITY]); @@ -413,7 +681,7 @@ static int netlink_route_change_read_unicast(struct nlmsghdr *h, ns_id_t ns_id, if (tb[RTA_METRICS]) { struct rtattr *mxrta[RTAX_MAX + 1]; - memset(mxrta, 0, sizeof mxrta); + memset(mxrta, 0, sizeof(mxrta)); netlink_parse_rtattr(mxrta, RTAX_MAX, RTA_DATA(tb[RTA_METRICS]), RTA_PAYLOAD(tb[RTA_METRICS])); @@ -490,14 +758,15 @@ static int netlink_route_change_read_unicast(struct nlmsghdr *h, ns_id_t ns_id, if (IS_ZEBRA_DEBUG_KERNEL) { char buf[PREFIX_STRLEN]; char buf2[PREFIX_STRLEN]; - zlog_debug("%s %s%s%s vrf %u(%u) metric: %d Admin Distance: %d", - nl_msg_type_to_str(h->nlmsg_type), - prefix2str(&p, buf, sizeof(buf)), - src_p.prefixlen ? " from " : "", - src_p.prefixlen - ? prefix2str(&src_p, buf2, sizeof(buf2)) - : "", - vrf_id, table, metric, distance); + zlog_debug( + "%s %s%s%s vrf %s(%u) table_id: %u metric: %d Admin Distance: %d", + nl_msg_type_to_str(h->nlmsg_type), + prefix2str(&p, buf, sizeof(buf)), + src_p.prefixlen ? " from " : "", + src_p.prefixlen ? prefix2str(&src_p, buf2, sizeof(buf2)) + : "", + vrf_id_to_name(vrf_id), vrf_id, table, metric, + distance); } afi_t afi = AFI_IP; @@ -505,75 +774,25 @@ static int netlink_route_change_read_unicast(struct nlmsghdr *h, ns_id_t ns_id, afi = AFI_IP6; if (h->nlmsg_type == RTM_NEWROUTE) { - struct interface *ifp; - vrf_id_t nh_vrf_id = vrf_id; if (!tb[RTA_MULTIPATH]) { - struct nexthop nh; - size_t sz = (afi == AFI_IP) ? 4 : 16; - - memset(&nh, 0, sizeof(nh)); - - if (bh_type == BLACKHOLE_UNSPEC) { - if (index && !gate) - nh.type = NEXTHOP_TYPE_IFINDEX; - else if (index && gate) - nh.type = - (afi == AFI_IP) - ? NEXTHOP_TYPE_IPV4_IFINDEX - : NEXTHOP_TYPE_IPV6_IFINDEX; - else if (!index && gate) - nh.type = (afi == AFI_IP) - ? NEXTHOP_TYPE_IPV4 - : NEXTHOP_TYPE_IPV6; - else { - nh.type = NEXTHOP_TYPE_BLACKHOLE; - nh.bh_type = bh_type; - } - } else { - nh.type = NEXTHOP_TYPE_BLACKHOLE; - nh.bh_type = bh_type; - } - nh.ifindex = index; - if (prefsrc) - memcpy(&nh.src, prefsrc, sz); - if (gate) - memcpy(&nh.gate, gate, sz); - - if (index) { - ifp = if_lookup_by_index_per_ns( - zebra_ns_lookup(ns_id), - index); - if (ifp) - nh_vrf_id = ifp->vrf_id; - } - nh.vrf_id = nh_vrf_id; + struct nexthop nh = {0}; - if (tb[RTA_ENCAP] && tb[RTA_ENCAP_TYPE] - && *(uint16_t *)RTA_DATA(tb[RTA_ENCAP_TYPE]) - == LWTUNNEL_ENCAP_MPLS) { - num_labels = - parse_encap_mpls(tb[RTA_ENCAP], labels); + if (!nhe_id) { + nh = parse_nexthop_unicast( + ns_id, rtm, tb, bh_type, index, prefsrc, + gate, afi, vrf_id); } - - if (rtm->rtm_flags & RTNH_F_ONLINK) - SET_FLAG(nh.flags, NEXTHOP_FLAG_ONLINK); - - if (num_labels) - nexthop_add_labels(&nh, ZEBRA_LSP_STATIC, - num_labels, labels); - rib_add(afi, SAFI_UNICAST, vrf_id, proto, 0, flags, &p, - &src_p, &nh, table, metric, mtu, distance, tag); + &src_p, &nh, nhe_id, table, metric, mtu, + distance, tag); } else { /* This is a multipath route */ - struct route_entry *re; + struct nexthop_group *ng = NULL; struct rtnexthop *rtnh = (struct rtnexthop *)RTA_DATA(tb[RTA_MULTIPATH]); - len = RTA_PAYLOAD(tb[RTA_MULTIPATH]); - re = XCALLOC(MTYPE_RE, sizeof(struct route_entry)); re->type = proto; re->distance = distance; @@ -582,148 +801,60 @@ static int netlink_route_change_read_unicast(struct nlmsghdr *h, ns_id_t ns_id, re->mtu = mtu; re->vrf_id = vrf_id; re->table = table; - re->nexthop_num = 0; - re->uptime = time(NULL); + re->uptime = monotime(NULL); re->tag = tag; - - for (;;) { - struct nexthop *nh = NULL; - - if (len < (int)sizeof(*rtnh) - || rtnh->rtnh_len > len) - break; - - index = rtnh->rtnh_ifindex; - if (index) { - /* - * Yes we are looking this up - * for every nexthop and just - * using the last one looked - * up right now - */ - ifp = if_lookup_by_index_per_ns( - zebra_ns_lookup(ns_id), - index); - if (ifp) - nh_vrf_id = ifp->vrf_id; - else { - flog_warn( - EC_ZEBRA_UNKNOWN_INTERFACE, - "%s: Unknown interface %u specified, defaulting to VRF_DEFAULT", - __PRETTY_FUNCTION__, - index); - nh_vrf_id = VRF_DEFAULT; - } - } else - nh_vrf_id = vrf_id; - - gate = 0; - if (rtnh->rtnh_len > sizeof(*rtnh)) { - memset(tb, 0, sizeof(tb)); - netlink_parse_rtattr( - tb, RTA_MAX, RTNH_DATA(rtnh), - rtnh->rtnh_len - sizeof(*rtnh)); - if (tb[RTA_GATEWAY]) - gate = RTA_DATA( - tb[RTA_GATEWAY]); - if (tb[RTA_ENCAP] && tb[RTA_ENCAP_TYPE] - && *(uint16_t *)RTA_DATA( - tb[RTA_ENCAP_TYPE]) - == LWTUNNEL_ENCAP_MPLS) { - num_labels = parse_encap_mpls( - tb[RTA_ENCAP], labels); - } + re->nhe_id = nhe_id; + + if (!nhe_id) { + uint8_t nhop_num; + + /* Use temporary list of nexthops; parse + * message payload's nexthops. + */ + ng = nexthop_group_new(); + nhop_num = + parse_multipath_nexthops_unicast( + ns_id, ng, rtm, rtnh, tb, + prefsrc, vrf_id); + + zserv_nexthop_num_warn( + __func__, (const struct prefix *)&p, + nhop_num); + + if (nhop_num == 0) { + nexthop_group_delete(&ng); + ng = NULL; } - - if (gate) { - if (rtm->rtm_family == AF_INET) { - if (index) - nh = route_entry_nexthop_ipv4_ifindex_add( - re, gate, - prefsrc, index, - nh_vrf_id); - else - nh = route_entry_nexthop_ipv4_add( - re, gate, - prefsrc, - nh_vrf_id); - } else if (rtm->rtm_family - == AF_INET6) { - if (index) - nh = route_entry_nexthop_ipv6_ifindex_add( - re, gate, index, - nh_vrf_id); - else - nh = route_entry_nexthop_ipv6_add( - re, gate, - nh_vrf_id); - } - } else - nh = route_entry_nexthop_ifindex_add( - re, index, nh_vrf_id); - - if (nh && num_labels) - nexthop_add_labels(nh, ZEBRA_LSP_STATIC, - num_labels, labels); - - if (nh && (rtnh->rtnh_flags & RTNH_F_ONLINK)) - SET_FLAG(nh->flags, - NEXTHOP_FLAG_ONLINK); - - if (rtnh->rtnh_len == 0) - break; - - len -= NLMSG_ALIGN(rtnh->rtnh_len); - rtnh = RTNH_NEXT(rtnh); } - zserv_nexthop_num_warn(__func__, - (const struct prefix *)&p, - re->nexthop_num); - if (re->nexthop_num == 0) - XFREE(MTYPE_RE, re); - else + if (nhe_id || ng) rib_add_multipath(afi, SAFI_UNICAST, &p, - &src_p, re); + &src_p, re, ng); + else + XFREE(MTYPE_RE, re); } } else { - if (!tb[RTA_MULTIPATH]) { - struct nexthop nh; - size_t sz = (afi == AFI_IP) ? 4 : 16; - - memset(&nh, 0, sizeof(nh)); - if (bh_type == BLACKHOLE_UNSPEC) { - if (index && !gate) - nh.type = NEXTHOP_TYPE_IFINDEX; - else if (index && gate) - nh.type = - (afi == AFI_IP) - ? NEXTHOP_TYPE_IPV4_IFINDEX - : NEXTHOP_TYPE_IPV6_IFINDEX; - else if (!index && gate) - nh.type = (afi == AFI_IP) - ? NEXTHOP_TYPE_IPV4 - : NEXTHOP_TYPE_IPV6; - else { - nh.type = NEXTHOP_TYPE_BLACKHOLE; - nh.bh_type = BLACKHOLE_UNSPEC; - } - } else { - nh.type = NEXTHOP_TYPE_BLACKHOLE; - nh.bh_type = bh_type; - } - nh.ifindex = index; - if (gate) - memcpy(&nh.gate, gate, sz); + if (nhe_id) { rib_delete(afi, SAFI_UNICAST, vrf_id, proto, 0, flags, - &p, &src_p, &nh, table, metric, distance, - true); + &p, &src_p, NULL, nhe_id, table, metric, + distance, true, false); } else { - /* XXX: need to compare the entire list of nexthops - * here for NLM_F_APPEND stupidity */ - rib_delete(afi, SAFI_UNICAST, vrf_id, proto, 0, flags, - &p, &src_p, NULL, table, metric, distance, - true); + if (!tb[RTA_MULTIPATH]) { + struct nexthop nh; + + nh = parse_nexthop_unicast( + ns_id, rtm, tb, bh_type, index, prefsrc, + gate, afi, vrf_id); + rib_delete(afi, SAFI_UNICAST, vrf_id, proto, 0, + flags, &p, &src_p, &nh, 0, table, + metric, distance, true, false); + } else { + /* XXX: need to compare the entire list of + * nexthops here for NLM_F_APPEND stupidity */ + rib_delete(afi, SAFI_UNICAST, vrf_id, proto, 0, + flags, &p, &src_p, NULL, 0, table, + metric, distance, true, false); + } } } @@ -761,7 +892,7 @@ static int netlink_route_change_read_multicast(struct nlmsghdr *h, len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct rtmsg)); - memset(tb, 0, sizeof tb); + memset(tb, 0, sizeof(tb)); netlink_parse_rtattr(tb, RTA_MAX, RTM_RTA(rtm), len); if (tb[RTA_TABLE]) @@ -804,24 +935,26 @@ static int netlink_route_change_read_multicast(struct nlmsghdr *h, } if (IS_ZEBRA_DEBUG_KERNEL) { - struct interface *ifp; + struct interface *ifp = NULL; + struct zebra_vrf *zvrf = NULL; + strlcpy(sbuf, inet_ntoa(m->sg.src), sizeof(sbuf)); strlcpy(gbuf, inet_ntoa(m->sg.grp), sizeof(gbuf)); for (count = 0; count < oif_count; count++) { ifp = if_lookup_by_index(oif[count], vrf); char temp[256]; - sprintf(temp, "%s(%d) ", ifp ? ifp->name : "Unknown", - oif[count]); - strcat(oif_list, temp); + snprintf(temp, sizeof(temp), "%s(%d) ", + ifp ? ifp->name : "Unknown", oif[count]); + strlcat(oif_list, temp, sizeof(oif_list)); } - struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(vrf); + zvrf = zebra_vrf_lookup_by_id(vrf); ifp = if_lookup_by_index(iif, vrf); - zlog_debug("MCAST VRF: %s(%d) %s (%s,%s) IIF: %s(%d) OIF: %s jiffies: %lld", - zvrf->vrf->name, vrf, - nl_msg_type_to_str(h->nlmsg_type), - sbuf, gbuf, ifp ? ifp->name : "Unknown", iif, - oif_list, m->lastused); + zlog_debug( + "MCAST VRF: %s(%d) %s (%s,%s) IIF: %s(%d) OIF: %s jiffies: %lld", + zvrf_name(zvrf), vrf, nl_msg_type_to_str(h->nlmsg_type), + sbuf, gbuf, ifp ? ifp->name : "Unknown", iif, oif_list, + m->lastused); } return 0; } @@ -861,10 +994,10 @@ int netlink_route_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct rtmsg)); if (len < 0) { - zlog_err("%s: Message received from netlink is of a broken size: %d %zu", - __PRETTY_FUNCTION__, - h->nlmsg_len, - (size_t)NLMSG_LENGTH(sizeof(struct rtmsg))); + zlog_err( + "%s: Message received from netlink is of a broken size: %d %zu", + __func__, h->nlmsg_len, + (size_t)NLMSG_LENGTH(sizeof(struct rtmsg))); return -1; } @@ -890,7 +1023,7 @@ static int netlink_request_route(struct zebra_ns *zns, int family, int type) req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); req.rtm.rtm_family = family; - return netlink_request(&zns->netlink_cmd, &req.n); + return netlink_request(&zns->netlink_cmd, &req); } /* Routing table read function using netlink interface. Only called @@ -923,11 +1056,15 @@ int netlink_route_read(struct zebra_ns *zns) return 0; } -static void _netlink_route_nl_add_gateway_info(uint8_t route_family, - uint8_t gw_family, - struct nlmsghdr *nlmsg, - size_t req_size, int bytelen, - const struct nexthop *nexthop) +/* + * The function returns true if the gateway info could be added + * to the message, otherwise false is returned. + */ +static bool _netlink_route_add_gateway_info(uint8_t route_family, + uint8_t gw_family, + struct nlmsghdr *nlmsg, + size_t req_size, int bytelen, + const struct nexthop *nexthop) { if (route_family == AF_MPLS) { struct gw_family_t gw_fam; @@ -937,68 +1074,65 @@ static void _netlink_route_nl_add_gateway_info(uint8_t route_family, memcpy(&gw_fam.gate.ipv4, &nexthop->gate.ipv4, bytelen); else memcpy(&gw_fam.gate.ipv6, &nexthop->gate.ipv6, bytelen); - addattr_l(nlmsg, req_size, RTA_VIA, &gw_fam.family, - bytelen + 2); + if (!nl_attr_put(nlmsg, req_size, RTA_VIA, &gw_fam.family, + bytelen + 2)) + return false; } else { - if (gw_family == AF_INET) - addattr_l(nlmsg, req_size, RTA_GATEWAY, - &nexthop->gate.ipv4, bytelen); - else - addattr_l(nlmsg, req_size, RTA_GATEWAY, - &nexthop->gate.ipv6, bytelen); + if (!(nexthop->rparent + && IS_MAPPED_IPV6(&nexthop->rparent->gate.ipv6))) { + if (gw_family == AF_INET) { + if (!nl_attr_put(nlmsg, req_size, RTA_GATEWAY, + &nexthop->gate.ipv4, bytelen)) + return false; + } else { + if (!nl_attr_put(nlmsg, req_size, RTA_GATEWAY, + &nexthop->gate.ipv6, bytelen)) + return false; + } + } } + + return true; } -static void _netlink_route_rta_add_gateway_info(uint8_t route_family, - uint8_t gw_family, - struct rtattr *rta, - struct rtnexthop *rtnh, - size_t req_size, int bytelen, - const struct nexthop *nexthop) +static int build_label_stack(struct mpls_label_stack *nh_label, + mpls_lse_t *out_lse, char *label_buf, + size_t label_buf_size) { - if (route_family == AF_MPLS) { - struct gw_family_t gw_fam; + char label_buf1[20]; + int num_labels = 0; - gw_fam.family = gw_family; - if (gw_family == AF_INET) - memcpy(&gw_fam.gate.ipv4, &nexthop->gate.ipv4, bytelen); - else - memcpy(&gw_fam.gate.ipv6, &nexthop->gate.ipv6, bytelen); - rta_addattr_l(rta, req_size, RTA_VIA, &gw_fam.family, - bytelen + 2); - rtnh->rtnh_len += RTA_LENGTH(bytelen + 2); - } else { - if (gw_family == AF_INET) - rta_addattr_l(rta, req_size, RTA_GATEWAY, - &nexthop->gate.ipv4, bytelen); - else - rta_addattr_l(rta, req_size, RTA_GATEWAY, - &nexthop->gate.ipv6, bytelen); - rtnh->rtnh_len += sizeof(struct rtattr) + bytelen; + for (int i = 0; nh_label && i < nh_label->num_labels; i++) { + if (nh_label->label[i] == MPLS_LABEL_IMPLICIT_NULL) + continue; + + if (IS_ZEBRA_DEBUG_KERNEL) { + if (!num_labels) + sprintf(label_buf, "label %u", + nh_label->label[i]); + else { + snprintf(label_buf1, sizeof(label_buf1), "/%u", + nh_label->label[i]); + strlcat(label_buf, label_buf1, label_buf_size); + } + } + + out_lse[num_labels] = + mpls_lse_encode(nh_label->label[i], 0, 0, 0); + num_labels++; } + + return num_labels; } -/* This function takes a nexthop as argument and adds - * the appropriate netlink attributes to an existing - * netlink message. - * - * @param routedesc: Human readable description of route type - * (direct/recursive, single-/multipath) - * @param bytelen: Length of addresses in bytes. - * @param nexthop: Nexthop information - * @param nlmsg: nlmsghdr structure to fill in. - * @param req_size: The size allocated for the message. - */ -static void _netlink_route_build_singlepath(const char *routedesc, int bytelen, - const struct nexthop *nexthop, - struct nlmsghdr *nlmsg, - struct rtmsg *rtmsg, - size_t req_size, int cmd) +static bool _netlink_route_encode_label_info(struct mpls_label_stack *nh_label, + struct nlmsghdr *nlmsg, + size_t buflen, struct rtmsg *rtmsg, + char *label_buf, + size_t label_buf_size) { - struct mpls_label_stack *nh_label; mpls_lse_t out_lse[MPLS_MAX_LABELS]; - int num_labels = 0; - char label_buf[256]; + int num_labels; /* * label_buf is *only* currently used within debugging. @@ -1008,128 +1142,176 @@ static void _netlink_route_build_singlepath(const char *routedesc, int bytelen, */ label_buf[0] = '\0'; - assert(nexthop); - for (const struct nexthop *nh = nexthop; nh; nh = nh->rparent) { - char label_buf1[20]; + num_labels = + build_label_stack(nh_label, out_lse, label_buf, label_buf_size); - nh_label = nh->nh_label; - if (!nh_label || !nh_label->num_labels) - continue; + if (num_labels) { + /* Set the BoS bit */ + out_lse[num_labels - 1] |= htonl(1 << MPLS_LS_S_SHIFT); - for (int i = 0; i < nh_label->num_labels; i++) { - if (nh_label->label[i] == MPLS_LABEL_IMPLICIT_NULL) - continue; + if (rtmsg->rtm_family == AF_MPLS) { + if (!nl_attr_put(nlmsg, buflen, RTA_NEWDST, &out_lse, + num_labels * sizeof(mpls_lse_t))) + return false; + } else { + struct rtattr *nest; - if (IS_ZEBRA_DEBUG_KERNEL) { - if (!num_labels) - sprintf(label_buf, "label %u", - nh_label->label[i]); - else { - sprintf(label_buf1, "/%u", - nh_label->label[i]); - strlcat(label_buf, label_buf1, - sizeof(label_buf)); - } - } + if (!nl_attr_put16(nlmsg, buflen, RTA_ENCAP_TYPE, + LWTUNNEL_ENCAP_MPLS)) + return false; - out_lse[num_labels] = - mpls_lse_encode(nh_label->label[i], 0, 0, 0); - num_labels++; + nest = nl_attr_nest(nlmsg, buflen, RTA_ENCAP); + if (!nest) + return false; + + if (!nl_attr_put(nlmsg, buflen, MPLS_IPTUNNEL_DST, + &out_lse, + num_labels * sizeof(mpls_lse_t))) + return false; + nl_attr_nest_end(nlmsg, nest); } } - if (num_labels) { - /* Set the BoS bit */ - out_lse[num_labels - 1] |= htonl(1 << MPLS_LS_S_SHIFT); + return true; +} - if (rtmsg->rtm_family == AF_MPLS) - addattr_l(nlmsg, req_size, RTA_NEWDST, &out_lse, - num_labels * sizeof(mpls_lse_t)); - else { - struct rtattr *nest; - uint16_t encap = LWTUNNEL_ENCAP_MPLS; - - addattr_l(nlmsg, req_size, RTA_ENCAP_TYPE, &encap, - sizeof(uint16_t)); - nest = addattr_nest(nlmsg, req_size, RTA_ENCAP); - addattr_l(nlmsg, req_size, MPLS_IPTUNNEL_DST, &out_lse, - num_labels * sizeof(mpls_lse_t)); - addattr_nest_end(nlmsg, nest); +static bool _netlink_route_encode_nexthop_src(const struct nexthop *nexthop, + int family, + struct nlmsghdr *nlmsg, + size_t buflen, int bytelen) +{ + if (family == AF_INET) { + if (nexthop->rmap_src.ipv4.s_addr != INADDR_ANY) { + if (!nl_attr_put(nlmsg, buflen, RTA_PREFSRC, + &nexthop->rmap_src.ipv4, bytelen)) + return false; + } else if (nexthop->src.ipv4.s_addr != INADDR_ANY) { + if (!nl_attr_put(nlmsg, buflen, RTA_PREFSRC, + &nexthop->src.ipv4, bytelen)) + return false; + } + } else if (family == AF_INET6) { + if (!IN6_IS_ADDR_UNSPECIFIED(&nexthop->rmap_src.ipv6)) { + if (!nl_attr_put(nlmsg, buflen, RTA_PREFSRC, + &nexthop->rmap_src.ipv6, bytelen)) + return false; + } else if (!IN6_IS_ADDR_UNSPECIFIED(&nexthop->src.ipv6)) { + if (!nl_attr_put(nlmsg, buflen, RTA_PREFSRC, + &nexthop->src.ipv6, bytelen)) + return false; } } + return true; +} + +/* This function takes a nexthop as argument and adds + * the appropriate netlink attributes to an existing + * netlink message. + * + * @param routedesc: Human readable description of route type + * (direct/recursive, single-/multipath) + * @param bytelen: Length of addresses in bytes. + * @param nexthop: Nexthop information + * @param nlmsg: nlmsghdr structure to fill in. + * @param req_size: The size allocated for the message. + * + * The function returns true if the nexthop could be added + * to the message, otherwise false is returned. + */ +static bool _netlink_route_build_singlepath(const struct prefix *p, + const char *routedesc, int bytelen, + const struct nexthop *nexthop, + struct nlmsghdr *nlmsg, + struct rtmsg *rtmsg, + size_t req_size, int cmd) +{ + + char label_buf[256]; + struct vrf *vrf; + char addrstr[INET6_ADDRSTRLEN]; + + assert(nexthop); + + vrf = vrf_lookup_by_id(nexthop->vrf_id); + + if (!_netlink_route_encode_label_info(nexthop->nh_label, nlmsg, + req_size, rtmsg, label_buf, + sizeof(label_buf))) + return false; + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK)) rtmsg->rtm_flags |= RTNH_F_ONLINK; - if (rtmsg->rtm_family == AF_INET - && (nexthop->type == NEXTHOP_TYPE_IPV6 - || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX)) { + if (is_route_v4_over_v6(rtmsg->rtm_family, nexthop->type)) { rtmsg->rtm_flags |= RTNH_F_ONLINK; - addattr_l(nlmsg, req_size, RTA_GATEWAY, &ipv4_ll, 4); - addattr32(nlmsg, req_size, RTA_OIF, nexthop->ifindex); + if (!nl_attr_put(nlmsg, req_size, RTA_GATEWAY, &ipv4_ll, 4)) + return false; + if (!nl_attr_put32(nlmsg, req_size, RTA_OIF, nexthop->ifindex)) + return false; - if (nexthop->rmap_src.ipv4.s_addr && (cmd == RTM_NEWROUTE)) - addattr_l(nlmsg, req_size, RTA_PREFSRC, - &nexthop->rmap_src.ipv4, bytelen); - else if (nexthop->src.ipv4.s_addr && (cmd == RTM_NEWROUTE)) - addattr_l(nlmsg, req_size, RTA_PREFSRC, - &nexthop->src.ipv4, bytelen); + if (cmd == RTM_NEWROUTE) { + if (!_netlink_route_encode_nexthop_src( + nexthop, AF_INET, nlmsg, req_size, bytelen)) + return false; + } if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug( - " 5549: _netlink_route_build_singlepath() (%s): " - "nexthop via %s %s if %u(%u)", - routedesc, ipv4_ll_buf, label_buf, - nexthop->ifindex, nexthop->vrf_id); - return; + zlog_debug("%s: 5549 (%s): %pFX nexthop via %s %s if %u vrf %s(%u)", + __func__, routedesc, p, ipv4_ll_buf, + label_buf, nexthop->ifindex, + VRF_LOGNAME(vrf), nexthop->vrf_id); + return true; } if (nexthop->type == NEXTHOP_TYPE_IPV4 || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) { /* Send deletes to the kernel without specifying the next-hop */ - if (cmd != RTM_DELROUTE) - _netlink_route_nl_add_gateway_info( - rtmsg->rtm_family, AF_INET, nlmsg, req_size, - bytelen, nexthop); + if (cmd != RTM_DELROUTE) { + if (!_netlink_route_add_gateway_info( + rtmsg->rtm_family, AF_INET, nlmsg, req_size, + bytelen, nexthop)) + return false; + } if (cmd == RTM_NEWROUTE) { - if (nexthop->rmap_src.ipv4.s_addr) - addattr_l(nlmsg, req_size, RTA_PREFSRC, - &nexthop->rmap_src.ipv4, bytelen); - else if (nexthop->src.ipv4.s_addr) - addattr_l(nlmsg, req_size, RTA_PREFSRC, - &nexthop->src.ipv4, bytelen); + if (!_netlink_route_encode_nexthop_src( + nexthop, AF_INET, nlmsg, req_size, bytelen)) + return false; } - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug( - "netlink_route_multipath() (%s): " - "nexthop via %s %s if %u(%u)", - routedesc, inet_ntoa(nexthop->gate.ipv4), - label_buf, nexthop->ifindex, nexthop->vrf_id); + if (IS_ZEBRA_DEBUG_KERNEL) { + inet_ntop(AF_INET, &nexthop->gate.ipv4, addrstr, + sizeof(addrstr)); + zlog_debug("%s: (%s): %pFX nexthop via %s %s if %u vrf %s(%u)", + __func__, routedesc, p, addrstr, label_buf, + nexthop->ifindex, VRF_LOGNAME(vrf), + nexthop->vrf_id); + } } if (nexthop->type == NEXTHOP_TYPE_IPV6 || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) { - _netlink_route_nl_add_gateway_info(rtmsg->rtm_family, AF_INET6, - nlmsg, req_size, bytelen, - nexthop); + if (!_netlink_route_add_gateway_info(rtmsg->rtm_family, + AF_INET6, nlmsg, req_size, + bytelen, nexthop)) + return false; if (cmd == RTM_NEWROUTE) { - if (!IN6_IS_ADDR_UNSPECIFIED(&nexthop->rmap_src.ipv6)) - addattr_l(nlmsg, req_size, RTA_PREFSRC, - &nexthop->rmap_src.ipv6, bytelen); - else if (!IN6_IS_ADDR_UNSPECIFIED(&nexthop->src.ipv6)) - addattr_l(nlmsg, req_size, RTA_PREFSRC, - &nexthop->src.ipv6, bytelen); + if (!_netlink_route_encode_nexthop_src( + nexthop, AF_INET6, nlmsg, req_size, + bytelen)) + return false; } - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug( - "netlink_route_multipath() (%s): " - "nexthop via %s %s if %u(%u)", - routedesc, inet6_ntoa(nexthop->gate.ipv6), - label_buf, nexthop->ifindex, nexthop->vrf_id); + if (IS_ZEBRA_DEBUG_KERNEL) { + inet_ntop(AF_INET6, &nexthop->gate.ipv6, addrstr, + sizeof(addrstr)); + zlog_debug("%s: (%s): %pFX nexthop via %s %s if %u vrf %s(%u)", + __func__, routedesc, p, addrstr, label_buf, + nexthop->ifindex, VRF_LOGNAME(vrf), + nexthop->vrf_id); + } } /* @@ -1137,30 +1319,29 @@ static void _netlink_route_build_singlepath(const char *routedesc, int bytelen, * This is especially useful if we are doing route * leaking. */ - if (nexthop->type != NEXTHOP_TYPE_BLACKHOLE) - addattr32(nlmsg, req_size, RTA_OIF, nexthop->ifindex); + if (nexthop->type != NEXTHOP_TYPE_BLACKHOLE) { + if (!nl_attr_put32(nlmsg, req_size, RTA_OIF, nexthop->ifindex)) + return false; + } if (nexthop->type == NEXTHOP_TYPE_IFINDEX) { if (cmd == RTM_NEWROUTE) { - if (nexthop->rmap_src.ipv4.s_addr) - addattr_l(nlmsg, req_size, RTA_PREFSRC, - &nexthop->rmap_src.ipv4, bytelen); - else if (nexthop->src.ipv4.s_addr) - addattr_l(nlmsg, req_size, RTA_PREFSRC, - &nexthop->src.ipv4, bytelen); + if (!_netlink_route_encode_nexthop_src( + nexthop, AF_INET, nlmsg, req_size, bytelen)) + return false; } if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug( - "netlink_route_multipath() (%s): " - "nexthop via if %u(%u)", - routedesc, nexthop->ifindex, nexthop->vrf_id); + zlog_debug("%s: (%s): %pFX nexthop via if %u vrf %s(%u)", + __func__, routedesc, p, nexthop->ifindex, + VRF_LOGNAME(vrf), nexthop->vrf_id); } + + return true; } /* This function takes a nexthop as argument and - * appends to the given rtattr/rtnexthop pair the - * representation of the nexthop. If the nexthop + * appends to the given netlink msg. If the nexthop * defines a preferred source, the src parameter * will be modified to point to that src, otherwise * it will be kept unmodified. @@ -1169,141 +1350,88 @@ static void _netlink_route_build_singlepath(const char *routedesc, int bytelen, * (direct/recursive, single-/multipath) * @param bytelen: Length of addresses in bytes. * @param nexthop: Nexthop information - * @param rta: rtnetlink attribute structure - * @param rtnh: pointer to an rtnetlink nexthop structure + * @param nlmsg: nlmsghdr structure to fill in. + * @param req_size: The size allocated for the message. * @param src: pointer pointing to a location where * the prefsrc should be stored. + * + * The function returns true if the nexthop could be added + * to the message, otherwise false is returned. */ -static void _netlink_route_build_multipath(const char *routedesc, int bytelen, +static bool _netlink_route_build_multipath(const struct prefix *p, + const char *routedesc, int bytelen, const struct nexthop *nexthop, - struct rtattr *rta, - struct rtnexthop *rtnh, - struct rtmsg *rtmsg, + struct nlmsghdr *nlmsg, + size_t req_size, struct rtmsg *rtmsg, const union g_addr **src) { - struct mpls_label_stack *nh_label; - mpls_lse_t out_lse[MPLS_MAX_LABELS]; - int num_labels = 0; char label_buf[256]; + struct vrf *vrf; + struct rtnexthop *rtnh; - rtnh->rtnh_len = sizeof(*rtnh); - rtnh->rtnh_flags = 0; - rtnh->rtnh_hops = 0; - rta->rta_len += rtnh->rtnh_len; - - /* - * label_buf is *only* currently used within debugging. - * As such when we assign it we are guarding it inside - * a debug test. If you want to change this make sure - * you fix this assumption - */ - label_buf[0] = '\0'; + rtnh = nl_attr_rtnh(nlmsg, req_size); + if (rtnh == NULL) + return false; assert(nexthop); - for (const struct nexthop *nh = nexthop; nh; nh = nh->rparent) { - char label_buf1[20]; - nh_label = nh->nh_label; - if (!nh_label || !nh_label->num_labels) - continue; + vrf = vrf_lookup_by_id(nexthop->vrf_id); - for (int i = 0; i < nh_label->num_labels; i++) { - if (nh_label->label[i] == MPLS_LABEL_IMPLICIT_NULL) - continue; - - if (IS_ZEBRA_DEBUG_KERNEL) { - if (!num_labels) - sprintf(label_buf, "label %u", - nh_label->label[i]); - else { - sprintf(label_buf1, "/%u", - nh_label->label[i]); - strlcat(label_buf, label_buf1, - sizeof(label_buf)); - } - } - - out_lse[num_labels] = - mpls_lse_encode(nh_label->label[i], 0, 0, 0); - num_labels++; - } - } - - if (num_labels) { - /* Set the BoS bit */ - out_lse[num_labels - 1] |= htonl(1 << MPLS_LS_S_SHIFT); - - if (rtmsg->rtm_family == AF_MPLS) { - rta_addattr_l(rta, NL_PKT_BUF_SIZE, RTA_NEWDST, - &out_lse, - num_labels * sizeof(mpls_lse_t)); - rtnh->rtnh_len += - RTA_LENGTH(num_labels * sizeof(mpls_lse_t)); - } else { - struct rtattr *nest; - uint16_t encap = LWTUNNEL_ENCAP_MPLS; - int len = rta->rta_len; - - rta_addattr_l(rta, NL_PKT_BUF_SIZE, RTA_ENCAP_TYPE, - &encap, sizeof(uint16_t)); - nest = rta_nest(rta, NL_PKT_BUF_SIZE, RTA_ENCAP); - rta_addattr_l(rta, NL_PKT_BUF_SIZE, MPLS_IPTUNNEL_DST, - &out_lse, - num_labels * sizeof(mpls_lse_t)); - rta_nest_end(rta, nest); - rtnh->rtnh_len += rta->rta_len - len; - } - } + if (!_netlink_route_encode_label_info(nexthop->nh_label, nlmsg, + req_size, rtmsg, label_buf, + sizeof(label_buf))) + return false; if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK)) rtnh->rtnh_flags |= RTNH_F_ONLINK; - if (rtmsg->rtm_family == AF_INET - && (nexthop->type == NEXTHOP_TYPE_IPV6 - || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX)) { - bytelen = 4; + if (is_route_v4_over_v6(rtmsg->rtm_family, nexthop->type)) { rtnh->rtnh_flags |= RTNH_F_ONLINK; - rta_addattr_l(rta, NL_PKT_BUF_SIZE, RTA_GATEWAY, &ipv4_ll, - bytelen); - rtnh->rtnh_len += sizeof(struct rtattr) + bytelen; + if (!nl_attr_put(nlmsg, req_size, RTA_GATEWAY, &ipv4_ll, 4)) + return false; rtnh->rtnh_ifindex = nexthop->ifindex; + if (nexthop->weight) + rtnh->rtnh_hops = nexthop->weight - 1; - if (nexthop->rmap_src.ipv4.s_addr) + if (nexthop->rmap_src.ipv4.s_addr != INADDR_ANY) *src = &nexthop->rmap_src; - else if (nexthop->src.ipv4.s_addr) + else if (nexthop->src.ipv4.s_addr != INADDR_ANY) *src = &nexthop->src; if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug( - " 5549: netlink_route_build_multipath() (%s): " - "nexthop via %s %s if %u", - routedesc, ipv4_ll_buf, label_buf, - nexthop->ifindex); - return; + "%s: 5549 (%s): %pFX nexthop via %s %s if %u vrf %s(%u)", + __func__, routedesc, p, ipv4_ll_buf, label_buf, + nexthop->ifindex, VRF_LOGNAME(vrf), + nexthop->vrf_id); + nl_attr_rtnh_end(nlmsg, rtnh); + return true; } if (nexthop->type == NEXTHOP_TYPE_IPV4 || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) { - _netlink_route_rta_add_gateway_info(rtmsg->rtm_family, AF_INET, - rta, rtnh, NL_PKT_BUF_SIZE, - bytelen, nexthop); - if (nexthop->rmap_src.ipv4.s_addr) + if (!_netlink_route_add_gateway_info(rtmsg->rtm_family, AF_INET, + nlmsg, req_size, bytelen, + nexthop)) + return false; + + if (nexthop->rmap_src.ipv4.s_addr != INADDR_ANY) *src = &nexthop->rmap_src; - else if (nexthop->src.ipv4.s_addr) + else if (nexthop->src.ipv4.s_addr != INADDR_ANY) *src = &nexthop->src; if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug( - "netlink_route_multipath() (%s): " - "nexthop via %s %s if %u", - routedesc, inet_ntoa(nexthop->gate.ipv4), - label_buf, nexthop->ifindex); + zlog_debug("%s: (%s): %pFX nexthop via %pI4 %s if %u vrf %s(%u)", + __func__, routedesc, p, &nexthop->gate.ipv4, + label_buf, nexthop->ifindex, + VRF_LOGNAME(vrf), nexthop->vrf_id); } if (nexthop->type == NEXTHOP_TYPE_IPV6 || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) { - _netlink_route_rta_add_gateway_info(rtmsg->rtm_family, AF_INET6, - rta, rtnh, NL_PKT_BUF_SIZE, - bytelen, nexthop); + if (!_netlink_route_add_gateway_info(rtmsg->rtm_family, + AF_INET6, nlmsg, req_size, + bytelen, nexthop)) + return false; if (!IN6_IS_ADDR_UNSPECIFIED(&nexthop->rmap_src.ipv6)) *src = &nexthop->rmap_src; @@ -1311,11 +1439,10 @@ static void _netlink_route_build_multipath(const char *routedesc, int bytelen, *src = &nexthop->src; if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug( - "netlink_route_multipath() (%s): " - "nexthop via %s %s if %u", - routedesc, inet6_ntoa(nexthop->gate.ipv6), - label_buf, nexthop->ifindex); + zlog_debug("%s: (%s): %pFX nexthop via %pI6 %s if %u vrf %s(%u)", + __func__, routedesc, p, &nexthop->gate.ipv6, + label_buf, nexthop->ifindex, + VRF_LOGNAME(vrf), nexthop->vrf_id); } /* @@ -1328,20 +1455,26 @@ static void _netlink_route_build_multipath(const char *routedesc, int bytelen, /* ifindex */ if (nexthop->type == NEXTHOP_TYPE_IFINDEX) { - if (nexthop->rmap_src.ipv4.s_addr) + if (nexthop->rmap_src.ipv4.s_addr != INADDR_ANY) *src = &nexthop->rmap_src; - else if (nexthop->src.ipv4.s_addr) + else if (nexthop->src.ipv4.s_addr != INADDR_ANY) *src = &nexthop->src; if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug( - "netlink_route_multipath() (%s): " - "nexthop via if %u", - routedesc, nexthop->ifindex); + zlog_debug("%s: (%s): %pFX nexthop via if %u vrf %s(%u)", + __func__, routedesc, p, nexthop->ifindex, + VRF_LOGNAME(vrf), nexthop->vrf_id); } + + if (nexthop->weight) + rtnh->rtnh_hops = nexthop->weight - 1; + + nl_attr_rtnh_end(nlmsg, rtnh); + return true; } -static inline void _netlink_mpls_build_singlepath(const char *routedesc, +static inline bool _netlink_mpls_build_singlepath(const struct prefix *p, + const char *routedesc, const zebra_nhlfe_t *nhlfe, struct nlmsghdr *nlmsg, struct rtmsg *rtmsg, @@ -1352,14 +1485,16 @@ static inline void _netlink_mpls_build_singlepath(const char *routedesc, family = NHLFE_FAMILY(nhlfe); bytelen = (family == AF_INET ? 4 : 16); - _netlink_route_build_singlepath(routedesc, bytelen, nhlfe->nexthop, - nlmsg, rtmsg, req_size, cmd); + return _netlink_route_build_singlepath(p, routedesc, bytelen, + nhlfe->nexthop, nlmsg, rtmsg, + req_size, cmd); } -static inline void -_netlink_mpls_build_multipath(const char *routedesc, const zebra_nhlfe_t *nhlfe, - struct rtattr *rta, struct rtnexthop *rtnh, +static inline bool +_netlink_mpls_build_multipath(const struct prefix *p, const char *routedesc, + const zebra_nhlfe_t *nhlfe, + struct nlmsghdr *nlmsg, size_t req_size, struct rtmsg *rtmsg, const union g_addr **src) { int bytelen; @@ -1367,44 +1502,22 @@ _netlink_mpls_build_multipath(const char *routedesc, const zebra_nhlfe_t *nhlfe, family = NHLFE_FAMILY(nhlfe); bytelen = (family == AF_INET ? 4 : 16); - _netlink_route_build_multipath(routedesc, bytelen, nhlfe->nexthop, rta, - rtnh, rtmsg, src); -} - - -/* Log debug information for netlink_route_multipath - * if debug logging is enabled. - * - * @param cmd: Netlink command which is to be processed - * @param p: Prefix for which the change is due - * @param family: Address family which the change concerns - * @param zvrf: The vrf we are in - * @param tableid: The table we are working on - */ -static void _netlink_route_debug(int cmd, const struct prefix *p, - int family, vrf_id_t vrfid, - uint32_t tableid) -{ - if (IS_ZEBRA_DEBUG_KERNEL) { - char buf[PREFIX_STRLEN]; - zlog_debug( - "netlink_route_multipath(): %s %s vrf %u(%u)", - nl_msg_type_to_str(cmd), - prefix2str(p, buf, sizeof(buf)), - vrfid, tableid); - } + return _netlink_route_build_multipath(p, routedesc, bytelen, + nhlfe->nexthop, nlmsg, req_size, + rtmsg, src); } static void _netlink_mpls_debug(int cmd, uint32_t label, const char *routedesc) { if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("netlink_mpls_multipath() (%s): %s %u/20", routedesc, - nl_msg_type_to_str(cmd), label); + zlog_debug("netlink_mpls_multipath_msg_encode() (%s): %s %u/20", + routedesc, nl_msg_type_to_str(cmd), label); } static int netlink_neigh_update(int cmd, int ifindex, uint32_t addr, char *lla, int llalen, ns_id_t ns_id) { + uint8_t protocol = RTPROT_ZEBRA; struct { struct nlmsghdr n; struct ndmsg ndm; @@ -1425,24 +1538,83 @@ static int netlink_neigh_update(int cmd, int ifindex, uint32_t addr, char *lla, req.ndm.ndm_ifindex = ifindex; req.ndm.ndm_type = RTN_UNICAST; - addattr_l(&req.n, sizeof(req), NDA_DST, &addr, 4); - addattr_l(&req.n, sizeof(req), NDA_LLADDR, lla, llalen); + nl_attr_put(&req.n, sizeof(req), NDA_PROTOCOL, &protocol, + sizeof(protocol)); + nl_attr_put32(&req.n, sizeof(req), NDA_DST, addr); + nl_attr_put(&req.n, sizeof(req), NDA_LLADDR, lla, llalen); return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns, 0); } +static bool nexthop_set_src(const struct nexthop *nexthop, int family, + union g_addr *src) +{ + if (family == AF_INET) { + if (nexthop->rmap_src.ipv4.s_addr != INADDR_ANY) { + src->ipv4 = nexthop->rmap_src.ipv4; + return true; + } else if (nexthop->src.ipv4.s_addr != INADDR_ANY) { + src->ipv4 = nexthop->src.ipv4; + return true; + } + } else if (family == AF_INET6) { + if (!IN6_IS_ADDR_UNSPECIFIED(&nexthop->rmap_src.ipv6)) { + src->ipv6 = nexthop->rmap_src.ipv6; + return true; + } else if (!IN6_IS_ADDR_UNSPECIFIED(&nexthop->src.ipv6)) { + src->ipv6 = nexthop->src.ipv6; + return true; + } + } + + return false; +} + +/* + * The function returns true if the attribute could be added + * to the message, otherwise false is returned. + */ +static int netlink_route_nexthop_encap(struct nlmsghdr *n, size_t nlen, + struct nexthop *nh) +{ + struct rtattr *nest; + + switch (nh->nh_encap_type) { + case NET_VXLAN: + if (!nl_attr_put16(n, nlen, RTA_ENCAP_TYPE, nh->nh_encap_type)) + return false; + + nest = nl_attr_nest(n, nlen, RTA_ENCAP); + if (!nest) + return false; + + if (!nl_attr_put32(n, nlen, 0 /* VXLAN_VNI */, + nh->nh_encap.vni)) + return false; + nl_attr_nest_end(n, nest); + break; + } + + return true; +} + /* * Routing table change via netlink interface, using a dataplane context object + * + * Returns -1 on failure, 0 when the msg doesn't fit entirely in the buffer + * otherwise the number of bytes written to buf. */ -static int netlink_route_multipath(int cmd, struct zebra_dplane_ctx *ctx) +ssize_t netlink_route_multipath_msg_encode(int cmd, + struct zebra_dplane_ctx *ctx, + uint8_t *data, size_t datalen, + bool fpm, bool force_nhg) { int bytelen; struct nexthop *nexthop = NULL; unsigned int nexthop_num; - int family; const char *routedesc; - int setsrc = 0; + bool setsrc = false; union g_addr src; const struct prefix *p, *src_p; uint32_t table_id; @@ -1450,38 +1622,39 @@ static int netlink_route_multipath(int cmd, struct zebra_dplane_ctx *ctx) struct { struct nlmsghdr n; struct rtmsg r; - char buf[NL_PKT_BUF_SIZE]; - } req; + char buf[]; + } *req = (void *)data; p = dplane_ctx_get_dest(ctx); src_p = dplane_ctx_get_src(ctx); - family = PREFIX_FAMILY(p); + if (datalen < sizeof(*req)) + return 0; - memset(&req, 0, sizeof(req) - NL_PKT_BUF_SIZE); + memset(req, 0, sizeof(*req)); - bytelen = (family == AF_INET ? 4 : 16); + bytelen = (p->family == AF_INET ? 4 : 16); - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); - req.n.nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST; + req->n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + req->n.nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST; if ((cmd == RTM_NEWROUTE) && ((p->family == AF_INET) || v6_rr_semantics)) - req.n.nlmsg_flags |= NLM_F_REPLACE; + req->n.nlmsg_flags |= NLM_F_REPLACE; - req.n.nlmsg_type = cmd; + req->n.nlmsg_type = cmd; - req.n.nlmsg_pid = dplane_ctx_get_ns(ctx)->nls.snl.nl_pid; + req->n.nlmsg_pid = dplane_ctx_get_ns(ctx)->nls.snl.nl_pid; - req.r.rtm_family = family; - req.r.rtm_dst_len = p->prefixlen; - req.r.rtm_src_len = src_p ? src_p->prefixlen : 0; - req.r.rtm_scope = RT_SCOPE_UNIVERSE; + req->r.rtm_family = p->family; + req->r.rtm_dst_len = p->prefixlen; + req->r.rtm_src_len = src_p ? src_p->prefixlen : 0; + req->r.rtm_scope = RT_SCOPE_UNIVERSE; if (cmd == RTM_DELROUTE) - req.r.rtm_protocol = zebra2proto(dplane_ctx_get_old_type(ctx)); + req->r.rtm_protocol = zebra2proto(dplane_ctx_get_old_type(ctx)); else - req.r.rtm_protocol = zebra2proto(dplane_ctx_get_type(ctx)); + req->r.rtm_protocol = zebra2proto(dplane_ctx_get_type(ctx)); /* * blackhole routes are not RTN_UNICAST, they are @@ -1492,12 +1665,15 @@ static int netlink_route_multipath(int cmd, struct zebra_dplane_ctx *ctx) * the RTM_DELROUTE case */ if (cmd != RTM_DELROUTE) - req.r.rtm_type = RTN_UNICAST; + req->r.rtm_type = RTN_UNICAST; - addattr_l(&req.n, sizeof(req), RTA_DST, &p->u.prefix, bytelen); - if (src_p) - addattr_l(&req.n, sizeof(req), RTA_SRC, &src_p->u.prefix, - bytelen); + if (!nl_attr_put(&req->n, datalen, RTA_DST, &p->u.prefix, bytelen)) + return 0; + if (src_p) { + if (!nl_attr_put(&req->n, datalen, RTA_SRC, &src_p->u.prefix, + bytelen)) + return 0; + } /* Metric. */ /* Hardcode the metric for all routes coming from zebra. Metric isn't @@ -1506,7 +1682,9 @@ static int netlink_route_multipath(int cmd, struct zebra_dplane_ctx *ctx) * path(s) * by the routing protocol and for communicating with protocol peers. */ - addattr32(&req.n, sizeof(req), RTA_PRIORITY, NL_DEFAULT_ROUTE_METRIC); + if (!nl_attr_put32(&req->n, datalen, RTA_PRIORITY, + NL_DEFAULT_ROUTE_METRIC)) + return 0; #if defined(SUPPORT_REALMS) { @@ -1517,20 +1695,27 @@ static int netlink_route_multipath(int cmd, struct zebra_dplane_ctx *ctx) else tag = dplane_ctx_get_tag(ctx); - if (tag > 0 && tag <= 255) - addattr32(&req.n, sizeof(req), RTA_FLOW, tag); + if (tag > 0 && tag <= 255) { + if (!nl_attr_put32(&req->n, datalen, RTA_FLOW, tag)) + return 0; + } } #endif /* Table corresponding to this route. */ table_id = dplane_ctx_get_table(ctx); if (table_id < 256) - req.r.rtm_table = table_id; + req->r.rtm_table = table_id; else { - req.r.rtm_table = RT_TABLE_UNSPEC; - addattr32(&req.n, sizeof(req), RTA_TABLE, table_id); + req->r.rtm_table = RT_TABLE_UNSPEC; + if (!nl_attr_put32(&req->n, datalen, RTA_TABLE, table_id)) + return 0; } - _netlink_route_debug(cmd, p, family, dplane_ctx_get_vrf(ctx), table_id); + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug( + "%s: %s %pFX vrf %u(%u)", __func__, + nl_msg_type_to_str(cmd), p, dplane_ctx_get_vrf(ctx), + table_id); /* * If we are not updating the route and we have received @@ -1539,22 +1724,83 @@ static int netlink_route_multipath(int cmd, struct zebra_dplane_ctx *ctx) * it. */ if (cmd == RTM_DELROUTE) - goto skip; + return NLMSG_ALIGN(req->n.nlmsg_len); if (dplane_ctx_get_mtu(ctx) || dplane_ctx_get_nh_mtu(ctx)) { - char buf[NL_PKT_BUF_SIZE]; - struct rtattr *rta = (void *)buf; + struct rtattr *nest; uint32_t mtu = dplane_ctx_get_mtu(ctx); uint32_t nexthop_mtu = dplane_ctx_get_nh_mtu(ctx); if (!mtu || (nexthop_mtu && nexthop_mtu < mtu)) mtu = nexthop_mtu; - rta->rta_type = RTA_METRICS; - rta->rta_len = RTA_LENGTH(0); - rta_addattr_l(rta, NL_PKT_BUF_SIZE, - RTAX_MTU, &mtu, sizeof(mtu)); - addattr_l(&req.n, NL_PKT_BUF_SIZE, RTA_METRICS, RTA_DATA(rta), - RTA_PAYLOAD(rta)); + + nest = nl_attr_nest(&req->n, datalen, RTA_METRICS); + if (nest == NULL) + return 0; + + if (!nl_attr_put(&req->n, datalen, RTAX_MTU, &mtu, sizeof(mtu))) + return 0; + nl_attr_nest_end(&req->n, nest); + } + + /* + * Always install blackhole routes without using nexthops, because of + * the following kernel problems: + * 1. Kernel nexthops don't suport unreachable/prohibit route types. + * 2. Blackhole kernel nexthops are deleted when loopback is down. + */ + nexthop = dplane_ctx_get_ng(ctx)->nexthop; + if (nexthop) { + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + nexthop = nexthop->resolved; + + if (nexthop->type == NEXTHOP_TYPE_BLACKHOLE) { + switch (nexthop->bh_type) { + case BLACKHOLE_ADMINPROHIB: + req->r.rtm_type = RTN_PROHIBIT; + break; + case BLACKHOLE_REJECT: + req->r.rtm_type = RTN_UNREACHABLE; + break; + default: + req->r.rtm_type = RTN_BLACKHOLE; + break; + } + return NLMSG_ALIGN(req->n.nlmsg_len); + } + } + + if ((!fpm && kernel_nexthops_supported()) || (fpm && force_nhg)) { + /* Kernel supports nexthop objects */ + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("%s: %pFX nhg_id is %u", __func__, p, + dplane_ctx_get_nhe_id(ctx)); + + if (!nl_attr_put32(&req->n, datalen, RTA_NH_ID, + dplane_ctx_get_nhe_id(ctx))) + return 0; + + /* Have to determine src still */ + for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx), nexthop)) { + if (setsrc) + break; + + setsrc = nexthop_set_src(nexthop, p->family, &src); + } + + if (setsrc) { + if (p->family == AF_INET) { + if (!nl_attr_put(&req->n, datalen, RTA_PREFSRC, + &src.ipv4, bytelen)) + return 0; + } else if (p->family == AF_INET6) { + if (!nl_attr_put(&req->n, datalen, RTA_PREFSRC, + &src.ipv6, bytelen)) + return 0; + } + } + + return NLMSG_ALIGN(req->n.nlmsg_len); } /* Count overall nexthops so we can decide whether to use singlepath @@ -1564,7 +1810,7 @@ static int netlink_route_multipath(int cmd, struct zebra_dplane_ctx *ctx) for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx), nexthop)) { if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) continue; - if (cmd == RTM_NEWROUTE && !NEXTHOP_IS_ACTIVE(nexthop->flags)) + if (!NEXTHOP_IS_ACTIVE(nexthop->flags)) continue; nexthop_num++; @@ -1574,92 +1820,59 @@ static int netlink_route_multipath(int cmd, struct zebra_dplane_ctx *ctx) if (nexthop_num == 1) { nexthop_num = 0; for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx), nexthop)) { - /* - * So we want to cover 2 types of blackhole - * routes here: - * 1) A normal blackhole route( ala from a static - * install. - * 2) A recursively resolved blackhole route - */ - if (nexthop->type == NEXTHOP_TYPE_BLACKHOLE) { - switch (nexthop->bh_type) { - case BLACKHOLE_ADMINPROHIB: - req.r.rtm_type = RTN_PROHIBIT; - break; - case BLACKHOLE_REJECT: - req.r.rtm_type = RTN_UNREACHABLE; - break; - default: - req.r.rtm_type = RTN_BLACKHOLE; - break; - } - goto skip; - } if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) { if (setsrc) continue; - if (family == AF_INET) { - if (nexthop->rmap_src.ipv4.s_addr - != 0) { - src.ipv4 = - nexthop->rmap_src.ipv4; - setsrc = 1; - } else if (nexthop->src.ipv4.s_addr - != 0) { - src.ipv4 = - nexthop->src.ipv4; - setsrc = 1; - } - } else if (family == AF_INET6) { - if (!IN6_IS_ADDR_UNSPECIFIED( - &nexthop->rmap_src.ipv6)) { - src.ipv6 = - nexthop->rmap_src.ipv6; - setsrc = 1; - } else if ( - !IN6_IS_ADDR_UNSPECIFIED( - &nexthop->src.ipv6)) { - src.ipv6 = - nexthop->src.ipv6; - setsrc = 1; - } - } + setsrc = nexthop_set_src(nexthop, p->family, + &src); continue; } - if ((cmd == RTM_NEWROUTE - && NEXTHOP_IS_ACTIVE(nexthop->flags))) { + if (NEXTHOP_IS_ACTIVE(nexthop->flags)) { routedesc = nexthop->rparent ? "recursive, single-path" : "single-path"; - _netlink_route_build_singlepath( - routedesc, bytelen, nexthop, &req.n, - &req.r, sizeof(req), cmd); + if (!_netlink_route_build_singlepath( + p, routedesc, bytelen, nexthop, + &req->n, &req->r, datalen, cmd)) + return 0; nexthop_num++; break; } + + /* + * Add encapsulation information when installing via + * FPM. + */ + if (fpm) { + if (!netlink_route_nexthop_encap( + &req->n, datalen, nexthop)) + return 0; + } } - if (setsrc && (cmd == RTM_NEWROUTE)) { - if (family == AF_INET) - addattr_l(&req.n, sizeof(req), RTA_PREFSRC, - &src.ipv4, bytelen); - else if (family == AF_INET6) - addattr_l(&req.n, sizeof(req), RTA_PREFSRC, - &src.ipv6, bytelen); + + if (setsrc) { + if (p->family == AF_INET) { + if (!nl_attr_put(&req->n, datalen, RTA_PREFSRC, + &src.ipv4, bytelen)) + return 0; + } else if (p->family == AF_INET6) { + if (!nl_attr_put(&req->n, datalen, RTA_PREFSRC, + &src.ipv6, bytelen)) + return 0; + } } } else { /* Multipath case */ - char buf[NL_PKT_BUF_SIZE]; - struct rtattr *rta = (void *)buf; - struct rtnexthop *rtnh; + struct rtattr *nest; const union g_addr *src1 = NULL; - rta->rta_type = RTA_MULTIPATH; - rta->rta_len = RTA_LENGTH(0); - rtnh = RTA_DATA(rta); + nest = nl_attr_nest(&req->n, datalen, RTA_MULTIPATH); + if (nest == NULL) + return 0; nexthop_num = 0; for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx), nexthop)) { @@ -1669,86 +1882,74 @@ static int netlink_route_multipath(int cmd, struct zebra_dplane_ctx *ctx) if (setsrc) continue; - if (family == AF_INET) { - if (nexthop->rmap_src.ipv4.s_addr - != 0) { - src.ipv4 = - nexthop->rmap_src.ipv4; - setsrc = 1; - } else if (nexthop->src.ipv4.s_addr - != 0) { - src.ipv4 = - nexthop->src.ipv4; - setsrc = 1; - } - } else if (family == AF_INET6) { - if (!IN6_IS_ADDR_UNSPECIFIED( - &nexthop->rmap_src.ipv6)) { - src.ipv6 = - nexthop->rmap_src.ipv6; - setsrc = 1; - } else if ( - !IN6_IS_ADDR_UNSPECIFIED( - &nexthop->src.ipv6)) { - src.ipv6 = - nexthop->src.ipv6; - setsrc = 1; - } - } - + setsrc = nexthop_set_src(nexthop, p->family, + &src); continue; } - if ((cmd == RTM_NEWROUTE - && NEXTHOP_IS_ACTIVE(nexthop->flags))) { + if (NEXTHOP_IS_ACTIVE(nexthop->flags)) { routedesc = nexthop->rparent ? "recursive, multipath" : "multipath"; nexthop_num++; - _netlink_route_build_multipath( - routedesc, bytelen, nexthop, rta, rtnh, - &req.r, &src1); - rtnh = RTNH_NEXT(rtnh); + if (!_netlink_route_build_multipath( + p, routedesc, bytelen, nexthop, + &req->n, datalen, &req->r, &src1)) + return 0; if (!setsrc && src1) { - if (family == AF_INET) + if (p->family == AF_INET) src.ipv4 = src1->ipv4; - else if (family == AF_INET6) + else if (p->family == AF_INET6) src.ipv6 = src1->ipv6; setsrc = 1; } } } - if (setsrc && (cmd == RTM_NEWROUTE)) { - if (family == AF_INET) - addattr_l(&req.n, sizeof(req), RTA_PREFSRC, - &src.ipv4, bytelen); - else if (family == AF_INET6) - addattr_l(&req.n, sizeof(req), RTA_PREFSRC, - &src.ipv6, bytelen); + + nl_attr_nest_end(&req->n, nest); + + /* + * Add encapsulation information when installing via + * FPM. + */ + if (fpm) { + for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx), + nexthop)) { + if (CHECK_FLAG(nexthop->flags, + NEXTHOP_FLAG_RECURSIVE)) + continue; + if (!netlink_route_nexthop_encap( + &req->n, datalen, nexthop)) + return 0; + } + } + + + if (setsrc) { + if (p->family == AF_INET) { + if (!nl_attr_put(&req->n, datalen, RTA_PREFSRC, + &src.ipv4, bytelen)) + return 0; + } else if (p->family == AF_INET6) { + if (!nl_attr_put(&req->n, datalen, RTA_PREFSRC, + &src.ipv6, bytelen)) + return 0; + } if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug("Setting source"); } - - if (rta->rta_len > RTA_LENGTH(0)) - addattr_l(&req.n, NL_PKT_BUF_SIZE, RTA_MULTIPATH, - RTA_DATA(rta), RTA_PAYLOAD(rta)); } /* If there is no useful nexthop then return. */ if (nexthop_num == 0) { if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug( - "netlink_route_multipath(): No useful nexthop."); - return 0; + zlog_debug("%s: No useful nexthop.", __func__); } -skip: - /* Talk to netlink socket. */ - return netlink_talk_info(netlink_talk_filter, &req.n, - dplane_ctx_get_ns(ctx), 0); + return NLMSG_ALIGN(req->n.nlmsg_len); } int kernel_get_ipmr_sg_stats(struct zebra_vrf *zvrf, void *in) @@ -1775,10 +1976,10 @@ int kernel_get_ipmr_sg_stats(struct zebra_vrf *zvrf, void *in) req.ndm.ndm_family = RTNL_FAMILY_IPMR; req.n.nlmsg_type = RTM_GETROUTE; - addattr_l(&req.n, sizeof(req), RTA_IIF, &mroute->ifindex, 4); - addattr_l(&req.n, sizeof(req), RTA_OIF, &mroute->ifindex, 4); - addattr_l(&req.n, sizeof(req), RTA_SRC, &mroute->sg.src.s_addr, 4); - addattr_l(&req.n, sizeof(req), RTA_DST, &mroute->sg.grp.s_addr, 4); + nl_attr_put32(&req.n, sizeof(req), RTA_IIF, mroute->ifindex); + nl_attr_put32(&req.n, sizeof(req), RTA_OIF, mroute->ifindex); + nl_attr_put32(&req.n, sizeof(req), RTA_SRC, mroute->sg.src.s_addr); + nl_attr_put32(&req.n, sizeof(req), RTA_DST, mroute->sg.grp.s_addr); /* * What? * @@ -1795,7 +1996,7 @@ int kernel_get_ipmr_sg_stats(struct zebra_vrf *zvrf, void *in) */ actual_table = (zvrf->table_id == RT_TABLE_MAIN) ? RT_TABLE_DEFAULT : zvrf->table_id; - addattr_l(&req.n, sizeof(req), RTA_TABLE, &actual_table, 4); + nl_attr_put32(&req.n, sizeof(req), RTA_TABLE, actual_table); suc = netlink_talk(netlink_route_change_read_multicast, &req.n, &zns->netlink_cmd, zns, 0); @@ -1804,15 +2005,293 @@ int kernel_get_ipmr_sg_stats(struct zebra_vrf *zvrf, void *in) return suc; } -/* - * Update or delete a prefix from the kernel, - * using info from a dataplane context. +/* Char length to debug ID with */ +#define ID_LENGTH 10 + +static bool _netlink_nexthop_build_group(struct nlmsghdr *n, size_t req_size, + uint32_t id, + const struct nh_grp *z_grp, + const uint8_t count) +{ + struct nexthop_grp grp[count]; + /* Need space for max group size, "/", and null term */ + char buf[(MULTIPATH_NUM * (ID_LENGTH + 1)) + 1]; + char buf1[ID_LENGTH + 2]; + + buf[0] = '\0'; + + memset(grp, 0, sizeof(grp)); + + if (count) { + for (int i = 0; i < count; i++) { + grp[i].id = z_grp[i].id; + grp[i].weight = z_grp[i].weight - 1; + + if (IS_ZEBRA_DEBUG_KERNEL) { + if (i == 0) + snprintf(buf, sizeof(buf1), "group %u", + grp[i].id); + else { + snprintf(buf1, sizeof(buf1), "/%u", + grp[i].id); + strlcat(buf, buf1, sizeof(buf)); + } + } + } + if (!nl_attr_put(n, req_size, NHA_GROUP, grp, + count * sizeof(*grp))) + return false; + } + + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("%s: ID (%u): %s", __func__, id, buf); + + return true; +} + +/** + * Next hop packet encoding helper function. + * + * \param[in] cmd netlink command. + * \param[in] ctx dataplane context (information snapshot). + * \param[out] buf buffer to hold the packet. + * \param[in] buflen amount of buffer bytes. + * + * \returns -1 on failure, 0 when the msg doesn't fit entirely in the buffer + * otherwise the number of bytes written to buf. */ -enum zebra_dplane_result kernel_route_update(struct zebra_dplane_ctx *ctx) +ssize_t netlink_nexthop_msg_encode(uint16_t cmd, + const struct zebra_dplane_ctx *ctx, + void *buf, size_t buflen) +{ + struct { + struct nlmsghdr n; + struct nhmsg nhm; + char buf[]; + } *req = buf; + + mpls_lse_t out_lse[MPLS_MAX_LABELS]; + char label_buf[256]; + int num_labels = 0; + + label_buf[0] = '\0'; + + if (buflen < sizeof(*req)) + return 0; + + memset(req, 0, sizeof(*req)); + + req->n.nlmsg_len = NLMSG_LENGTH(sizeof(struct nhmsg)); + req->n.nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST; + + if (cmd == RTM_NEWNEXTHOP) + req->n.nlmsg_flags |= NLM_F_REPLACE; + + req->n.nlmsg_type = cmd; + req->n.nlmsg_pid = dplane_ctx_get_ns(ctx)->nls.snl.nl_pid; + + 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; + + if (cmd == RTM_NEWNEXTHOP) { + /* + * We distinguish between a "group", which is a collection + * of ids, and a singleton nexthop with an id. The + * group is installed as an id that just refers to a list of + * other ids. + */ + if (dplane_ctx_get_nhe_nh_grp_count(ctx)) { + if (!_netlink_nexthop_build_group( + &req->n, buflen, id, + dplane_ctx_get_nhe_nh_grp(ctx), + dplane_ctx_get_nhe_nh_grp_count(ctx))) + return 0; + } else { + const struct nexthop *nh = + dplane_ctx_get_nhe_ng(ctx)->nexthop; + afi_t afi = dplane_ctx_get_nhe_afi(ctx); + + if (afi == AFI_IP) + req->nhm.nh_family = AF_INET; + else if (afi == AFI_IP6) + req->nhm.nh_family = AF_INET6; + + switch (nh->type) { + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + if (!nl_attr_put(&req->n, buflen, NHA_GATEWAY, + &nh->gate.ipv4, + IPV4_MAX_BYTELEN)) + return 0; + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + if (!nl_attr_put(&req->n, buflen, NHA_GATEWAY, + &nh->gate.ipv6, + IPV6_MAX_BYTELEN)) + return 0; + break; + case NEXTHOP_TYPE_BLACKHOLE: + if (!nl_attr_put(&req->n, buflen, NHA_BLACKHOLE, + NULL, 0)) + return 0; + /* Blackhole shouldn't have anymore attributes + */ + goto nexthop_done; + case NEXTHOP_TYPE_IFINDEX: + /* Don't need anymore info for this */ + break; + } + + if (!nh->ifindex) { + flog_err( + EC_ZEBRA_NHG_FIB_UPDATE, + "Context received for kernel nexthop update without an interface"); + return -1; + } + + if (!nl_attr_put32(&req->n, buflen, NHA_OIF, + nh->ifindex)) + return 0; + + if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_ONLINK)) + req->nhm.nh_flags |= RTNH_F_ONLINK; + + num_labels = + build_label_stack(nh->nh_label, out_lse, + label_buf, sizeof(label_buf)); + + if (num_labels) { + /* Set the BoS bit */ + out_lse[num_labels - 1] |= + htonl(1 << MPLS_LS_S_SHIFT); + + /* + * TODO: MPLS unsupported for now in kernel. + */ + if (req->nhm.nh_family == AF_MPLS) + goto nexthop_done; +#if 0 + if (!nl_attr_put(&req->n, buflen, NHA_NEWDST, + &out_lse, + num_labels + * sizeof(mpls_lse_t))) + return 0; +#endif + else { + struct rtattr *nest; + uint16_t encap = LWTUNNEL_ENCAP_MPLS; + + if (!nl_attr_put16(&req->n, buflen, + NHA_ENCAP_TYPE, + encap)) + return 0; + nest = nl_attr_nest(&req->n, buflen, + NHA_ENCAP); + if (!nest) + return 0; + if (!nl_attr_put( + &req->n, buflen, + MPLS_IPTUNNEL_DST, &out_lse, + num_labels + * sizeof( + mpls_lse_t))) + return 0; + nl_attr_nest_end(&req->n, nest); + } + } + +nexthop_done: + + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("%s: ID (%u): %pNHv(%d) vrf %s(%u) %s ", + __func__, id, nh, nh->ifindex, + vrf_id_to_name(nh->vrf_id), + nh->vrf_id, label_buf); +} + + req->nhm.nh_protocol = + zebra2proto(dplane_ctx_get_nhe_type(ctx)); + + } else if (cmd != RTM_DELNEXTHOP) { + flog_err( + EC_ZEBRA_NHG_FIB_UPDATE, + "Nexthop group kernel update command (%d) does not exist", + cmd); + return -1; + } + + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("%s: %s, id=%u", __func__, nl_msg_type_to_str(cmd), + id); + + return NLMSG_ALIGN(req->n.nlmsg_len); +} + +static ssize_t netlink_nexthop_msg_encoder(struct zebra_dplane_ctx *ctx, + void *buf, size_t buflen) { - int cmd, ret; + enum dplane_op_e op; + int cmd = 0; + + op = dplane_ctx_get_op(ctx); + if (op == DPLANE_OP_NH_INSTALL || op == DPLANE_OP_NH_UPDATE) + cmd = RTM_NEWNEXTHOP; + else if (op == DPLANE_OP_NH_DELETE) + cmd = RTM_DELNEXTHOP; + else { + flog_err(EC_ZEBRA_NHG_FIB_UPDATE, + "Context received for kernel nexthop update with incorrect OP code (%u)", + op); + return -1; + } + + return netlink_nexthop_msg_encode(cmd, ctx, buf, buflen); +} + +enum netlink_msg_status +netlink_put_nexthop_update_msg(struct nl_batch *bth, + struct zebra_dplane_ctx *ctx) +{ + /* Nothing to do if the kernel doesn't support nexthop objects */ + if (!kernel_nexthops_supported()) + return FRR_NETLINK_SUCCESS; + + return netlink_batch_add_msg(bth, ctx, netlink_nexthop_msg_encoder, + false); +} + +static ssize_t netlink_newroute_msg_encoder(struct zebra_dplane_ctx *ctx, + void *buf, size_t buflen) +{ + return netlink_route_multipath_msg_encode(RTM_NEWROUTE, ctx, buf, + buflen, false, false); +} + +static ssize_t netlink_delroute_msg_encoder(struct zebra_dplane_ctx *ctx, + void *buf, size_t buflen) +{ + return netlink_route_multipath_msg_encode(RTM_DELROUTE, ctx, buf, + buflen, false, false); +} + +enum netlink_msg_status +netlink_put_route_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx) +{ + int cmd; const struct prefix *p = dplane_ctx_get_dest(ctx); - struct nexthop *nexthop; if (dplane_ctx_get_op(ctx) == DPLANE_OP_ROUTE_DELETE) { cmd = RTM_DELROUTE; @@ -1822,7 +2301,20 @@ enum zebra_dplane_result kernel_route_update(struct zebra_dplane_ctx *ctx) if (p->family == AF_INET || v6_rr_semantics) { /* Single 'replace' operation */ - cmd = RTM_NEWROUTE; + + /* + * With route replace semantics in place + * for v4 routes and the new route is a system + * route we do not install anything. + * The problem here is that the new system + * route should cause us to withdraw from + * the kernel the old non-system route + */ + if (RSYSTEM_ROUTE(dplane_ctx_get_type(ctx)) + && !RSYSTEM_ROUTE(dplane_ctx_get_old_type(ctx))) + netlink_batch_add_msg( + bth, ctx, netlink_delroute_msg_encoder, + true); } else { /* * So v6 route replace semantics are not in @@ -1837,37 +2329,329 @@ enum zebra_dplane_result kernel_route_update(struct zebra_dplane_ctx *ctx) * screwed. */ if (!RSYSTEM_ROUTE(dplane_ctx_get_old_type(ctx))) - (void)netlink_route_multipath(RTM_DELROUTE, - ctx); - cmd = RTM_NEWROUTE; + netlink_batch_add_msg( + bth, ctx, netlink_delroute_msg_encoder, + true); } - } else { - return ZEBRA_DPLANE_REQUEST_FAILURE; + cmd = RTM_NEWROUTE; + } else + return FRR_NETLINK_ERROR; + + if (RSYSTEM_ROUTE(dplane_ctx_get_type(ctx))) + return FRR_NETLINK_SUCCESS; + + return netlink_batch_add_msg(bth, ctx, + cmd == RTM_NEWROUTE + ? netlink_newroute_msg_encoder + : netlink_delroute_msg_encoder, + false); +} + +/** + * netlink_nexthop_process_nh() - Parse the gatway/if info from a new nexthop + * + * @tb: Netlink RTA data + * @family: Address family in the nhmsg + * @ifp: Interface connected - this should be NULL, we fill it in + * @ns_id: Namspace id + * + * Return: New nexthop + */ +static struct nexthop netlink_nexthop_process_nh(struct rtattr **tb, + unsigned char family, + struct interface **ifp, + ns_id_t ns_id) +{ + struct nexthop nh = {}; + void *gate = NULL; + enum nexthop_types_t type = 0; + int if_index = 0; + size_t sz = 0; + struct interface *ifp_lookup; + + if_index = *(int *)RTA_DATA(tb[NHA_OIF]); + + + if (tb[NHA_GATEWAY]) { + switch (family) { + case AF_INET: + type = NEXTHOP_TYPE_IPV4_IFINDEX; + sz = 4; + break; + case AF_INET6: + type = NEXTHOP_TYPE_IPV6_IFINDEX; + sz = 16; + break; + default: + flog_warn( + EC_ZEBRA_BAD_NHG_MESSAGE, + "Nexthop gateway with bad address family (%d) received from kernel", + family); + return nh; + } + gate = RTA_DATA(tb[NHA_GATEWAY]); + } else + type = NEXTHOP_TYPE_IFINDEX; + + if (type) + nh.type = type; + + if (gate) + memcpy(&(nh.gate), gate, sz); + + if (if_index) + nh.ifindex = if_index; + + ifp_lookup = + if_lookup_by_index_per_ns(zebra_ns_lookup(ns_id), nh.ifindex); + + if (ifp) + *ifp = ifp_lookup; + if (ifp_lookup) + nh.vrf_id = ifp_lookup->vrf_id; + else { + flog_warn( + EC_ZEBRA_UNKNOWN_INTERFACE, + "%s: Unknown nexthop interface %u received, defaulting to VRF_DEFAULT", + __func__, nh.ifindex); + + nh.vrf_id = VRF_DEFAULT; } - if (!RSYSTEM_ROUTE(dplane_ctx_get_type(ctx))) - ret = netlink_route_multipath(cmd, ctx); - else - ret = 0; - if ((cmd == RTM_NEWROUTE) && (ret == 0)) { - /* Update installed nexthops to signal which have been - * installed. - */ - for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx), nexthop)) { - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) - continue; + if (tb[NHA_ENCAP] && tb[NHA_ENCAP_TYPE]) { + uint16_t encap_type = *(uint16_t *)RTA_DATA(tb[NHA_ENCAP_TYPE]); + int num_labels = 0; - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) { - SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); - } + mpls_label_t labels[MPLS_MAX_LABELS] = {0}; + + if (encap_type == LWTUNNEL_ENCAP_MPLS) + num_labels = parse_encap_mpls(tb[NHA_ENCAP], labels); + + if (num_labels) + nexthop_add_labels(&nh, ZEBRA_LSP_STATIC, num_labels, + labels); + } + + return nh; +} + +static int netlink_nexthop_process_group(struct rtattr **tb, + struct nh_grp *z_grp, int z_grp_size) +{ + uint8_t count = 0; + /* linux/nexthop.h group struct */ + struct nexthop_grp *n_grp = NULL; + + n_grp = (struct nexthop_grp *)RTA_DATA(tb[NHA_GROUP]); + count = (RTA_PAYLOAD(tb[NHA_GROUP]) / sizeof(*n_grp)); + + if (!count || (count * sizeof(*n_grp)) != RTA_PAYLOAD(tb[NHA_GROUP])) { + flog_warn(EC_ZEBRA_BAD_NHG_MESSAGE, + "Invalid nexthop group received from the kernel"); + return count; + } + +#if 0 + // TODO: Need type for something? + zlog_debug("Nexthop group type: %d", + *((uint16_t *)RTA_DATA(tb[NHA_GROUP_TYPE]))); + +#endif + + for (int i = 0; ((i < count) && (i < z_grp_size)); i++) { + z_grp[i].id = n_grp[i].id; + z_grp[i].weight = n_grp[i].weight + 1; + } + return count; +} + +/** + * netlink_nexthop_change() - Read in change about nexthops from the kernel + * + * @h: Netlink message header + * @ns_id: Namspace id + * @startup: Are we reading under startup conditions? + * + * Return: Result status + */ +int netlink_nexthop_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) +{ + int len; + /* nexthop group id */ + uint32_t id; + unsigned char family; + int type; + afi_t afi = AFI_UNSPEC; + vrf_id_t vrf_id = VRF_DEFAULT; + struct interface *ifp = NULL; + struct nhmsg *nhm = NULL; + struct nexthop nh = {}; + struct nh_grp grp[MULTIPATH_NUM] = {}; + /* Count of nexthops in group array */ + uint8_t grp_count = 0; + struct rtattr *tb[NHA_MAX + 1] = {}; + + nhm = NLMSG_DATA(h); + + if (ns_id) + vrf_id = ns_id; + + if (startup && h->nlmsg_type != RTM_NEWNEXTHOP) + return 0; + + len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct nhmsg)); + if (len < 0) { + zlog_warn( + "%s: Message received from netlink is of a broken size %d %zu", + __func__, h->nlmsg_len, + (size_t)NLMSG_LENGTH(sizeof(struct nhmsg))); + return -1; + } + + netlink_parse_rtattr(tb, NHA_MAX, RTM_NHA(nhm), len); + + + if (!tb[NHA_ID]) { + flog_warn( + EC_ZEBRA_BAD_NHG_MESSAGE, + "Nexthop group without an ID received from the kernel"); + return -1; + } + + /* We use the ID key'd nhg table for kernel updates */ + id = *((uint32_t *)RTA_DATA(tb[NHA_ID])); + + if (zebra_evpn_mh_is_fdb_nh(id)) { + /* If this is a L2 NH just ignore it */ + if (IS_ZEBRA_DEBUG_KERNEL || IS_ZEBRA_DEBUG_EVPN_MH_NH) { + zlog_debug("Ignore kernel update (%u) for fdb-nh 0x%x", + h->nlmsg_type, id); } + return 0; } - return (ret == 0 ? - ZEBRA_DPLANE_REQUEST_SUCCESS : ZEBRA_DPLANE_REQUEST_FAILURE); + family = nhm->nh_family; + afi = family2afi(family); + + type = proto2zebra(nhm->nh_protocol, 0, true); + + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("%s ID (%u) %s NS %u", + nl_msg_type_to_str(h->nlmsg_type), id, + nl_family_to_str(family), ns_id); + + + if (h->nlmsg_type == RTM_NEWNEXTHOP) { + if (tb[NHA_GROUP]) { + /** + * If this is a group message its only going to have + * an array of nexthop IDs associated with it + */ + grp_count = netlink_nexthop_process_group( + tb, grp, array_size(grp)); + } else { + if (tb[NHA_BLACKHOLE]) { + /** + * This nexthop is just for blackhole-ing + * traffic, it should not have an OIF, GATEWAY, + * or ENCAP + */ + nh.type = NEXTHOP_TYPE_BLACKHOLE; + nh.bh_type = BLACKHOLE_UNSPEC; + } else if (tb[NHA_OIF]) + /** + * This is a true new nexthop, so we need + * to parse the gateway and device info + */ + nh = netlink_nexthop_process_nh(tb, family, + &ifp, ns_id); + else { + + flog_warn( + EC_ZEBRA_BAD_NHG_MESSAGE, + "Invalid Nexthop message received from the kernel with ID (%u)", + id); + return -1; + } + SET_FLAG(nh.flags, NEXTHOP_FLAG_ACTIVE); + if (nhm->nh_flags & RTNH_F_ONLINK) + SET_FLAG(nh.flags, NEXTHOP_FLAG_ONLINK); + vrf_id = nh.vrf_id; + } + + if (zebra_nhg_kernel_find(id, &nh, grp, grp_count, vrf_id, afi, + type, startup)) + return -1; + + } else if (h->nlmsg_type == RTM_DELNEXTHOP) + zebra_nhg_kernel_del(id, vrf_id); + + return 0; } +/** + * netlink_request_nexthop() - Request nextop information from the kernel + * @zns: Zebra namespace + * @family: AF_* netlink family + * @type: RTM_* route type + * + * Return: Result status + */ +static int netlink_request_nexthop(struct zebra_ns *zns, int family, int type) +{ + struct { + struct nlmsghdr n; + struct nhmsg nhm; + } req; + + /* Form the request, specifying filter (rtattr) if needed. */ + memset(&req, 0, sizeof(req)); + req.n.nlmsg_type = type; + req.n.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST; + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct nhmsg)); + req.nhm.nh_family = family; + + return netlink_request(&zns->netlink_cmd, &req); +} + + +/** + * netlink_nexthop_read() - Nexthop read function using netlink interface + * + * @zns: Zebra name space + * + * Return: Result status + * Only called at bootstrap time. + */ +int netlink_nexthop_read(struct zebra_ns *zns) +{ + int ret; + struct zebra_dplane_info dp_info; + + zebra_dplane_info_from_zns(&dp_info, zns, true /*is_cmd*/); + + /* Get nexthop objects */ + ret = netlink_request_nexthop(zns, AF_UNSPEC, RTM_GETNEXTHOP); + if (ret < 0) + return ret; + ret = netlink_parse_info(netlink_nexthop_change, &zns->netlink_cmd, + &dp_info, 0, 1); + + if (!ret) + /* If we succesfully read in nexthop objects, + * this kernel must support them. + */ + supports_nh = true; + + if (IS_ZEBRA_DEBUG_KERNEL || IS_ZEBRA_DEBUG_NHG) + zlog_debug("Nexthop objects %ssupported on this kernel", + supports_nh ? "" : "not "); + + return ret; +} + + int kernel_neigh_update(int add, int ifindex, uint32_t addr, char *lla, int llalen, ns_id_t ns_id) { @@ -1875,69 +2659,118 @@ int kernel_neigh_update(int add, int ifindex, uint32_t addr, char *lla, addr, lla, llalen, ns_id); } -/* - * Add remote VTEP to the flood list for this VxLAN interface (VNI). This - * is done by adding an FDB entry with a MAC of 00:00:00:00:00:00. +/** + * netlink_neigh_update_msg_encode() - Common helper api for encoding + * evpn neighbor update as netlink messages using dataplane context object. + * Here, a neighbor refers to a bridge forwarding database entry for + * either unicast forwarding or head-end replication or an IP neighbor + * entry. + * @ctx: Dataplane context + * @cmd: Netlink command (RTM_NEWNEIGH or RTM_DELNEIGH) + * @mac: A neighbor cache link layer address + * @ip: A neighbor cache n/w layer destination address + * In the case of bridge FDB, this represnts the remote + * VTEP IP. + * @replace_obj: Whether NEW request should replace existing object or + * add to the end of the list + * @family: AF_* netlink family + * @type: RTN_* route type + * @flags: NTF_* flags + * @state: NUD_* states + * @data: data buffer pointer + * @datalen: total amount of data buffer space + * + * Return: 0 when the msg doesn't fit entirely in the buffer + * otherwise the number of bytes written to buf. */ -static int netlink_vxlan_flood_list_update(struct interface *ifp, - struct in_addr *vtep_ip, int cmd) +static ssize_t netlink_neigh_update_msg_encode( + const struct zebra_dplane_ctx *ctx, int cmd, const struct ethaddr *mac, + const struct ipaddr *ip, bool replace_obj, uint8_t family, uint8_t type, + uint8_t flags, uint16_t state, uint32_t nhg_id, + bool nfy, uint8_t nfy_flags, + void *data, size_t datalen) { - struct zebra_ns *zns; + uint8_t protocol = RTPROT_ZEBRA; struct { struct nlmsghdr n; struct ndmsg ndm; - char buf[256]; - } req; - uint8_t dst_mac[6] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0}; - struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id); + char buf[]; + } *req = data; + int ipa_len; + enum dplane_op_e op; - zns = zvrf->zns; - memset(&req, 0, sizeof(req)); + if (datalen < sizeof(*req)) + return 0; + memset(req, 0, sizeof(*req)); - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)); - req.n.nlmsg_flags = NLM_F_REQUEST; + op = dplane_ctx_get_op(ctx); + + req->n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)); + req->n.nlmsg_flags = NLM_F_REQUEST; if (cmd == RTM_NEWNEIGH) - req.n.nlmsg_flags |= (NLM_F_CREATE | NLM_F_APPEND); - req.n.nlmsg_type = cmd; - req.ndm.ndm_family = PF_BRIDGE; - req.ndm.ndm_state = NUD_NOARP | NUD_PERMANENT; - req.ndm.ndm_flags |= NTF_SELF; // Handle by "self", not "master" + req->n.nlmsg_flags |= + NLM_F_CREATE + | (replace_obj ? NLM_F_REPLACE : NLM_F_APPEND); + req->n.nlmsg_type = cmd; + req->ndm.ndm_family = family; + req->ndm.ndm_type = type; + req->ndm.ndm_state = state; + req->ndm.ndm_flags = flags; + req->ndm.ndm_ifindex = dplane_ctx_get_ifindex(ctx); + + if (!nl_attr_put(&req->n, datalen, NDA_PROTOCOL, &protocol, + sizeof(protocol))) + return 0; + if (mac) { + if (!nl_attr_put(&req->n, datalen, NDA_LLADDR, mac, 6)) + return 0; + } - addattr_l(&req.n, sizeof(req), NDA_LLADDR, &dst_mac, 6); - req.ndm.ndm_ifindex = ifp->ifindex; - addattr_l(&req.n, sizeof(req), NDA_DST, &vtep_ip->s_addr, 4); + if (nhg_id) { + if (!nl_attr_put32(&req->n, datalen, NDA_NH_ID, nhg_id)) + return 0; + } + if (nfy) { + if (!nl_attr_put(&req->n, datalen, NDA_NOTIFY, + &nfy_flags, sizeof(nfy_flags))) + return 0; + } - return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns, - 0); -} + ipa_len = IS_IPADDR_V4(ip) ? IPV4_MAX_BYTELEN : IPV6_MAX_BYTELEN; + if (!nl_attr_put(&req->n, datalen, NDA_DST, &ip->ip.addr, ipa_len)) + return 0; -/* - * Add remote VTEP for this VxLAN interface (VNI). In Linux, this involves - * adding - * a "flood" MAC FDB entry. - */ -int kernel_add_vtep(vni_t vni, struct interface *ifp, struct in_addr *vtep_ip) -{ - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Install %s into flood list for VNI %u intf %s(%u)", - inet_ntoa(*vtep_ip), vni, ifp->name, ifp->ifindex); + if (op == DPLANE_OP_MAC_INSTALL || op == DPLANE_OP_MAC_DELETE) { + vlanid_t vid = dplane_ctx_mac_get_vlan(ctx); + + if (vid > 0) { + if (!nl_attr_put16(&req->n, datalen, NDA_VLAN, vid)) + return 0; + } + + if (!nl_attr_put32(&req->n, datalen, NDA_MASTER, + dplane_ctx_mac_get_br_ifindex(ctx))) + return 0; + } - return netlink_vxlan_flood_list_update(ifp, vtep_ip, RTM_NEWNEIGH); + return NLMSG_ALIGN(req->n.nlmsg_len); } /* - * Remove remote VTEP for this VxLAN interface (VNI). In Linux, this involves - * deleting the "flood" MAC FDB entry. + * Add remote VTEP to the flood list for this VxLAN interface (VNI). This + * is done by adding an FDB entry with a MAC of 00:00:00:00:00:00. */ -int kernel_del_vtep(vni_t vni, struct interface *ifp, struct in_addr *vtep_ip) +static ssize_t +netlink_vxlan_flood_update_ctx(const struct zebra_dplane_ctx *ctx, int cmd, + void *buf, size_t buflen) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "Uninstall %s from flood list for VNI %u intf %s(%u)", - inet_ntoa(*vtep_ip), vni, ifp->name, ifp->ifindex); + struct ethaddr dst_mac = {.octet = {0}}; - return netlink_vxlan_flood_list_update(ifp, vtep_ip, RTM_DELNEIGH); + return netlink_neigh_update_msg_encode( + ctx, cmd, &dst_mac, dplane_ctx_neigh_get_ipaddr(ctx), false, + PF_BRIDGE, 0, NTF_SELF, (NUD_NOARP | NUD_PERMANENT), 0 /*nhg*/, + false /*nfy*/, 0 /*nfy_flags*/, buf, buflen); } #ifndef NDA_RTA @@ -1953,80 +2786,42 @@ static int netlink_macfdb_change(struct nlmsghdr *h, int len, ns_id_t ns_id) struct rtattr *tb[NDA_MAX + 1]; struct interface *br_if; struct ethaddr mac; - vlanid_t vid = 0; - struct prefix vtep_ip; - int vid_present = 0, dst_present = 0; - char buf[ETHER_ADDR_STRLEN]; - char vid_buf[20]; - char dst_buf[30]; - bool sticky; - - ndm = NLMSG_DATA(h); - - /* We only process macfdb notifications if EVPN is enabled */ - if (!is_evpn_enabled()) - return 0; - - /* The interface should exist. */ - ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(ns_id), - ndm->ndm_ifindex); - if (!ifp || !ifp->info) { - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("\t%s without associated interface: %u", - __PRETTY_FUNCTION__, ndm->ndm_ifindex); - return 0; - } - - /* The interface should be something we're interested in. */ - if (!IS_ZEBRA_IF_BRIDGE_SLAVE(ifp)) { - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("\t%s Not interested in %s, not a slave", - __PRETTY_FUNCTION__, ifp->name); - return 0; - } + vlanid_t vid = 0; + struct in_addr vtep_ip; + int vid_present = 0, dst_present = 0; + char buf[ETHER_ADDR_STRLEN]; + char vid_buf[20]; + char dst_buf[30]; + bool sticky; + bool local_inactive = false; + bool dp_static = false; + uint32_t nhg_id = 0; - /* Drop "permanent" entries. */ - if (ndm->ndm_state & NUD_PERMANENT) { - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("\t%s Entry is PERMANENT, dropping", - __PRETTY_FUNCTION__); - return 0; - } + ndm = NLMSG_DATA(h); - zif = (struct zebra_if *)ifp->info; - if ((br_if = zif->brslave_info.br_if) == NULL) { - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug( - "%s family %s IF %s(%u) brIF %u - no bridge master", - nl_msg_type_to_str(h->nlmsg_type), - nl_family_to_str(ndm->ndm_family), ifp->name, - ndm->ndm_ifindex, - zif->brslave_info.bridge_ifindex); + /* We only process macfdb notifications if EVPN is enabled */ + if (!is_evpn_enabled()) return 0; - } - /* Parse attributes and extract fields of interest. */ + /* Parse attributes and extract fields of interest. Do basic + * validation of the fields. + */ memset(tb, 0, sizeof tb); netlink_parse_rtattr(tb, NDA_MAX, NDA_RTA(ndm), len); if (!tb[NDA_LLADDR]) { if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("%s family %s IF %s(%u) brIF %u - no LLADDR", + zlog_debug("%s AF_BRIDGE IF %u - no LLADDR", nl_msg_type_to_str(h->nlmsg_type), - nl_family_to_str(ndm->ndm_family), ifp->name, - ndm->ndm_ifindex, - zif->brslave_info.bridge_ifindex); + ndm->ndm_ifindex); return 0; } if (RTA_PAYLOAD(tb[NDA_LLADDR]) != ETH_ALEN) { if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug( - "%s family %s IF %s(%u) brIF %u - LLADDR is not MAC, len %lu", - nl_msg_type_to_str(h->nlmsg_type), - nl_family_to_str(ndm->ndm_family), ifp->name, - ndm->ndm_ifindex, - zif->brslave_info.bridge_ifindex, + "%s AF_BRIDGE IF %u - LLADDR is not MAC, len %lu", + nl_msg_type_to_str(h->nlmsg_type), ndm->ndm_ifindex, (unsigned long)RTA_PAYLOAD(tb[NDA_LLADDR])); return 0; } @@ -2036,33 +2831,68 @@ static int netlink_macfdb_change(struct nlmsghdr *h, int len, ns_id_t ns_id) if ((NDA_VLAN <= NDA_MAX) && tb[NDA_VLAN]) { vid_present = 1; vid = *(uint16_t *)RTA_DATA(tb[NDA_VLAN]); - sprintf(vid_buf, " VLAN %u", vid); + snprintf(vid_buf, sizeof(vid_buf), " VLAN %u", vid); } if (tb[NDA_DST]) { /* TODO: Only IPv4 supported now. */ dst_present = 1; - vtep_ip.family = AF_INET; - vtep_ip.prefixlen = IPV4_MAX_BITLEN; - memcpy(&(vtep_ip.u.prefix4.s_addr), RTA_DATA(tb[NDA_DST]), + memcpy(&vtep_ip.s_addr, RTA_DATA(tb[NDA_DST]), IPV4_MAX_BYTELEN); - sprintf(dst_buf, " dst %s", inet_ntoa(vtep_ip.u.prefix4)); + snprintf(dst_buf, sizeof(dst_buf), " dst %s", + inet_ntoa(vtep_ip)); } - sticky = !!(ndm->ndm_state & NUD_NOARP); + if (tb[NDA_NH_ID]) + nhg_id = *(uint32_t *)RTA_DATA(tb[NDA_NH_ID]); + + if (ndm->ndm_state & NUD_STALE) + local_inactive = true; + + if (tb[NDA_NOTIFY]) { + uint8_t nfy_flags; + + dp_static = true; + nfy_flags = *(uint8_t *)RTA_DATA(tb[NDA_NOTIFY]); + /* local activity has not been detected on the entry */ + if (nfy_flags & (1 << BR_FDB_NFY_INACTIVE)) + local_inactive = true; + } if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("Rx %s family %s IF %s(%u)%s %sMAC %s%s", + zlog_debug("Rx %s AF_BRIDGE IF %u%s st 0x%x fl 0x%x MAC %s%s nhg %d", nl_msg_type_to_str(h->nlmsg_type), - nl_family_to_str(ndm->ndm_family), ifp->name, ndm->ndm_ifindex, vid_present ? vid_buf : "", - sticky ? "sticky " : "", + ndm->ndm_state, ndm->ndm_flags, prefix_mac2str(&mac, buf, sizeof(buf)), - dst_present ? dst_buf : ""); + dst_present ? dst_buf : "", nhg_id); + + /* The interface should exist. */ + ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(ns_id), + ndm->ndm_ifindex); + if (!ifp || !ifp->info) + return 0; + + /* The interface should be something we're interested in. */ + if (!IS_ZEBRA_IF_BRIDGE_SLAVE(ifp)) + return 0; + + zif = (struct zebra_if *)ifp->info; + if ((br_if = zif->brslave_info.br_if) == NULL) { + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug( + "%s AF_BRIDGE IF %s(%u) brIF %u - no bridge master", + nl_msg_type_to_str(h->nlmsg_type), ifp->name, + ndm->ndm_ifindex, + zif->brslave_info.bridge_ifindex); + return 0; + } + + sticky = !!(ndm->ndm_flags & NTF_STICKY); if (filter_vlan && vid != filter_vlan) { if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("\tFiltered due to filter vlan: %d", + zlog_debug(" Filtered due to filter vlan: %d", filter_vlan); return 0; } @@ -2073,25 +2903,40 @@ static int netlink_macfdb_change(struct nlmsghdr *h, int len, ns_id_t ns_id) * so perform an implicit delete of any local entry (if it exists). */ if (h->nlmsg_type == RTM_NEWNEIGH) { + /* Drop "permanent" entries. */ + if (ndm->ndm_state & NUD_PERMANENT) { + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug( + " Dropping entry because of NUD_PERMANENT"); + return 0; + } + if (IS_ZEBRA_IF_VXLAN(ifp)) return zebra_vxlan_check_del_local_mac(ifp, br_if, &mac, vid); return zebra_vxlan_local_mac_add_update(ifp, br_if, &mac, vid, - sticky); + sticky, local_inactive, dp_static); } /* This is a delete notification. + * Ignore the notification with IP dest as it may just signify that the + * MAC has moved from remote to local. The exception is the special + * all-zeros MAC that represents the BUM flooding entry; we may have + * to readd it. Otherwise, * 1. For a MAC over VxLan, check if it needs to be refreshed(readded) * 2. For a MAC over "local" interface, delete the mac * Note: We will get notifications from both bridge driver and VxLAN * driver. - * Ignore the notification from VxLan driver as it is also generated - * when mac moves from remote to local. */ + if (nhg_id) + return 0; + if (dst_present) { - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("\tNo Destination Present"); + u_char zero_mac[6] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0}; + + if (!memcmp(zero_mac, mac.octet, ETH_ALEN)) + return zebra_vxlan_check_readd_vtep(ifp, vtep_ip); return 0; } @@ -2140,9 +2985,9 @@ static int netlink_request_macs(struct nlsock *netlink_cmd, int family, req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); req.ifm.ifi_family = family; if (master_ifindex) - addattr32(&req.n, sizeof(req), IFLA_MASTER, master_ifindex); + nl_attr_put32(&req.n, sizeof(req), IFLA_MASTER, master_ifindex); - return netlink_request(netlink_cmd, &req.n); + return netlink_request(netlink_cmd, &req); } /* @@ -2229,22 +3074,23 @@ static int netlink_request_specific_mac_in_bridge(struct zebra_ns *zns, req.ndm.ndm_family = family; /* AF_BRIDGE */ /* req.ndm.ndm_state = NUD_REACHABLE; */ - addattr_l(&req.n, sizeof(req), NDA_LLADDR, mac, 6); + nl_attr_put(&req.n, sizeof(req), NDA_LLADDR, mac, 6); br_zif = (struct zebra_if *)br_if->info; if (IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(br_zif) && vid > 0) - addattr16(&req.n, sizeof(req), NDA_VLAN, vid); + nl_attr_put16(&req.n, sizeof(req), NDA_VLAN, vid); - addattr32(&req.n, sizeof(req), NDA_MASTER, br_if->ifindex); + nl_attr_put32(&req.n, sizeof(req), NDA_MASTER, br_if->ifindex); if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("%s: Tx family %s IF %s(%u) MAC %s vid %u", - __PRETTY_FUNCTION__, - nl_family_to_str(req.ndm.ndm_family), br_if->name, - br_if->ifindex, - prefix_mac2str(mac, buf, sizeof(buf)), vid); + zlog_debug( + "%s: Tx family %s IF %s(%u) vrf %s(%u) MAC %s vid %u", + __func__, nl_family_to_str(req.ndm.ndm_family), + br_if->name, br_if->ifindex, + vrf_id_to_name(br_if->vrf_id), br_if->vrf_id, + prefix_mac2str(mac, buf, sizeof(buf)), vid); - return netlink_request(&zns->netlink_cmd, &req.n); + return netlink_request(&zns->netlink_cmd, &req); } int netlink_macfdb_read_specific_mac(struct zebra_ns *zns, @@ -2269,74 +3115,101 @@ int netlink_macfdb_read_specific_mac(struct zebra_ns *zns, return ret; } -static int netlink_macfdb_update(struct interface *ifp, vlanid_t vid, - struct ethaddr *mac, struct in_addr vtep_ip, - int cmd, bool sticky) -{ - struct zebra_ns *zns; - struct { - struct nlmsghdr n; - struct ndmsg ndm; - char buf[256]; - } req; - int dst_alen; - struct zebra_if *zif; - struct interface *br_if; - struct zebra_if *br_zif; - char buf[ETHER_ADDR_STRLEN]; - int vid_present = 0; - char vid_buf[20]; - char dst_buf[30]; - struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id); - zns = zvrf->zns; - zif = ifp->info; - if ((br_if = zif->brslave_info.br_if) == NULL) { - zlog_debug("MAC %s on IF %s(%u) - no mapping to bridge", - (cmd == RTM_NEWNEIGH) ? "add" : "del", ifp->name, - ifp->ifindex); - return -1; - } - - memset(&req, 0, sizeof(req)); - - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)); - req.n.nlmsg_flags = NLM_F_REQUEST; - if (cmd == RTM_NEWNEIGH) - req.n.nlmsg_flags |= (NLM_F_CREATE | NLM_F_REPLACE); - req.n.nlmsg_type = cmd; - req.ndm.ndm_family = AF_BRIDGE; - req.ndm.ndm_flags |= NTF_SELF | NTF_MASTER; - req.ndm.ndm_state = NUD_REACHABLE; +/* + * Netlink-specific handler for MAC updates using dataplane context object. + */ +ssize_t netlink_macfdb_update_ctx(struct zebra_dplane_ctx *ctx, void *data, + size_t datalen) +{ + struct ipaddr vtep_ip; + vlanid_t vid; + ssize_t total; + int cmd; + uint8_t flags; + uint16_t state; + uint32_t nhg_id; + uint32_t update_flags; + bool nfy = false; + uint8_t nfy_flags = 0; + + cmd = dplane_ctx_get_op(ctx) == DPLANE_OP_MAC_INSTALL + ? RTM_NEWNEIGH : RTM_DELNEIGH; + + flags = NTF_MASTER; + state = NUD_REACHABLE; + + update_flags = dplane_ctx_mac_get_update_flags(ctx); + if (update_flags & DPLANE_MAC_REMOTE) { + flags |= NTF_SELF; + if (dplane_ctx_mac_is_sticky(ctx)) { + /* NUD_NOARP prevents the entry from expiring */ + state |= NUD_NOARP; + /* sticky the entry from moving */ + flags |= NTF_STICKY; + } else { + flags |= NTF_EXT_LEARNED; + } + /* if it was static-local previously we need to clear the + * notify flags on replace with remote + */ + if (update_flags & DPLANE_MAC_WAS_STATIC) + nfy = true; + } else { + /* local mac */ + if (update_flags & DPLANE_MAC_SET_STATIC) { + nfy_flags |= (1 << BR_FDB_NFY_STATIC); + state |= NUD_NOARP; + } - if (sticky) - req.ndm.ndm_state |= NUD_NOARP; - else - req.ndm.ndm_flags |= NTF_EXT_LEARNED; + if (update_flags & DPLANE_MAC_SET_INACTIVE) + nfy_flags |= (1 << BR_FDB_NFY_INACTIVE); - addattr_l(&req.n, sizeof(req), NDA_LLADDR, mac, 6); - req.ndm.ndm_ifindex = ifp->ifindex; - dst_alen = 4; // TODO: hardcoded - addattr_l(&req.n, sizeof(req), NDA_DST, &vtep_ip, dst_alen); - sprintf(dst_buf, " dst %s", inet_ntoa(vtep_ip)); - br_zif = (struct zebra_if *)br_if->info; - if (IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(br_zif) && vid > 0) { - addattr16(&req.n, sizeof(req), NDA_VLAN, vid); - vid_present = 1; - sprintf(vid_buf, " VLAN %u", vid); + nfy = true; } - addattr32(&req.n, sizeof(req), NDA_MASTER, br_if->ifindex); - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("Tx %s family %s IF %s(%u)%s %sMAC %s%s", - nl_msg_type_to_str(cmd), - nl_family_to_str(req.ndm.ndm_family), ifp->name, - ifp->ifindex, vid_present ? vid_buf : "", - sticky ? "sticky " : "", - prefix_mac2str(mac, buf, sizeof(buf)), dst_buf); + nhg_id = dplane_ctx_mac_get_nhg_id(ctx); + vtep_ip.ipaddr_v4 = *(dplane_ctx_mac_get_vtep_ip(ctx)); + SET_IPADDR_V4(&vtep_ip); - return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns, - 0); + if (IS_ZEBRA_DEBUG_KERNEL) { + char ipbuf[PREFIX_STRLEN]; + char buf[ETHER_ADDR_STRLEN]; + char vid_buf[20]; + const struct ethaddr *mac = dplane_ctx_mac_get_addr(ctx); + + vid = dplane_ctx_mac_get_vlan(ctx); + if (vid > 0) + snprintf(vid_buf, sizeof(vid_buf), " VLAN %u", vid); + else + vid_buf[0] = '\0'; + + zlog_debug("Tx %s family %s IF %s(%u)%s %sMAC %s dst %s nhg %u%s%s%s%s%s", + nl_msg_type_to_str(cmd), nl_family_to_str(AF_BRIDGE), + dplane_ctx_get_ifname(ctx), + dplane_ctx_get_ifindex(ctx), vid_buf, + dplane_ctx_mac_is_sticky(ctx) ? "sticky " : "", + prefix_mac2str(mac, buf, sizeof(buf)), + ipaddr2str(&vtep_ip, ipbuf, sizeof(ipbuf)), + nhg_id, + (update_flags & + DPLANE_MAC_REMOTE) ? " rem" : "", + (update_flags & + DPLANE_MAC_WAS_STATIC) ? " clr_sync" : "", + (update_flags & + DPLANE_MAC_SET_STATIC) ? " static" : "", + (update_flags & + DPLANE_MAC_SET_INACTIVE) ? " inactive" : "", + (nfy & + DPLANE_MAC_SET_INACTIVE) ? " nfy" : ""); + } + + total = netlink_neigh_update_msg_encode( + ctx, cmd, dplane_ctx_mac_get_addr(ctx), &vtep_ip, true, + AF_BRIDGE, 0, flags, state, nhg_id, nfy, nfy_flags, + data, datalen); + + return total; } /* @@ -2344,7 +3217,8 @@ static int netlink_macfdb_update(struct interface *ifp, vlanid_t vid, * 5549 support, re-install them. */ static void netlink_handle_5549(struct ndmsg *ndm, struct zebra_if *zif, - struct interface *ifp, struct ipaddr *ip) + struct interface *ifp, struct ipaddr *ip, + bool handle_failed) { if (ndm->ndm_family != AF_INET) return; @@ -2355,12 +3229,20 @@ static void netlink_handle_5549(struct ndmsg *ndm, struct zebra_if *zif, if (ipv4_ll.s_addr != ip->ip._v4_addr.s_addr) return; + if (handle_failed && ndm->ndm_state & NUD_FAILED) { + zlog_info("Neighbor Entry for %s has entered a failed state, not reinstalling", + ifp->name); + return; + } + if_nbr_ipv6ll_to_ipv4ll_neigh_update(ifp, &zif->v6_2_v4_ll_addr6, true); } #define NUD_VALID \ (NUD_PERMANENT | NUD_NOARP | NUD_REACHABLE | NUD_PROBE | NUD_STALE \ | NUD_DELAY) +#define NUD_LOCAL_ACTIVE \ + (NUD_PERMANENT | NUD_NOARP | NUD_REACHABLE) static int netlink_ipneigh_change(struct nlmsghdr *h, int len, ns_id_t ns_id) { @@ -2371,11 +3253,13 @@ static int netlink_ipneigh_change(struct nlmsghdr *h, int len, ns_id_t ns_id) struct interface *link_if; struct ethaddr mac; struct ipaddr ip; + struct vrf *vrf; char buf[ETHER_ADDR_STRLEN]; char buf2[INET6_ADDRSTRLEN]; int mac_present = 0; bool is_ext; bool is_router; + bool local_inactive; ndm = NLMSG_DATA(h); @@ -2385,17 +3269,18 @@ static int netlink_ipneigh_change(struct nlmsghdr *h, int len, ns_id_t ns_id) if (!ifp || !ifp->info) return 0; + vrf = vrf_lookup_by_id(ifp->vrf_id); zif = (struct zebra_if *)ifp->info; /* Parse attributes and extract fields of interest. */ - memset(tb, 0, sizeof tb); + memset(tb, 0, sizeof(tb)); netlink_parse_rtattr(tb, NDA_MAX, NDA_RTA(ndm), len); if (!tb[NDA_DST]) { - zlog_debug("%s family %s IF %s(%u) - no DST", + zlog_debug("%s family %s IF %s(%u) vrf %s(%u) - no DST", nl_msg_type_to_str(h->nlmsg_type), nl_family_to_str(ndm->ndm_family), ifp->name, - ndm->ndm_ifindex); + ndm->ndm_ifindex, VRF_LOGNAME(vrf), ifp->vrf_id); return 0; } @@ -2405,7 +3290,7 @@ static int netlink_ipneigh_change(struct nlmsghdr *h, int len, ns_id_t ns_id) /* if kernel deletes our rfc5549 neighbor entry, re-install it */ if (h->nlmsg_type == RTM_DELNEIGH && (ndm->ndm_state & NUD_PERMANENT)) { - netlink_handle_5549(ndm, zif, ifp, &ip); + netlink_handle_5549(ndm, zif, ifp, &ip, false); if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug( "\tNeighbor Entry Received is a 5549 entry, finished"); @@ -2414,7 +3299,7 @@ static int netlink_ipneigh_change(struct nlmsghdr *h, int len, ns_id_t ns_id) /* if kernel marks our rfc5549 neighbor entry invalid, re-install it */ if (h->nlmsg_type == RTM_NEWNEIGH && !(ndm->ndm_state & NUD_VALID)) - netlink_handle_5549(ndm, zif, ifp, &ip); + netlink_handle_5549(ndm, zif, ifp, &ip, true); /* The neighbor is present on an SVI. From this, we locate the * underlying @@ -2447,12 +3332,13 @@ static int netlink_ipneigh_change(struct nlmsghdr *h, int len, ns_id_t ns_id) if (RTA_PAYLOAD(tb[NDA_LLADDR]) != ETH_ALEN) { if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug( - "%s family %s IF %s(%u) - LLADDR is not MAC, len %lu", + "%s family %s IF %s(%u) vrf %s(%u) - LLADDR is not MAC, len %lu", nl_msg_type_to_str( h->nlmsg_type), nl_family_to_str( ndm->ndm_family), ifp->name, ndm->ndm_ifindex, + VRF_LOGNAME(vrf), ifp->vrf_id, (unsigned long)RTA_PAYLOAD( tb[NDA_LLADDR])); return 0; @@ -2467,10 +3353,10 @@ static int netlink_ipneigh_change(struct nlmsghdr *h, int len, ns_id_t ns_id) if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug( - "Rx %s family %s IF %s(%u) IP %s MAC %s state 0x%x flags 0x%x", + "Rx %s family %s IF %s(%u) vrf %s(%u) IP %s MAC %s state 0x%x flags 0x%x", nl_msg_type_to_str(h->nlmsg_type), nl_family_to_str(ndm->ndm_family), ifp->name, - ndm->ndm_ifindex, + ndm->ndm_ifindex, VRF_LOGNAME(vrf), ifp->vrf_id, ipaddr2str(&ip, buf2, sizeof(buf2)), mac_present ? prefix_mac2str(&mac, buf, sizeof(buf)) @@ -2483,19 +3369,26 @@ static int netlink_ipneigh_change(struct nlmsghdr *h, int len, ns_id_t ns_id) * result * in re-adding the neighbor if it is a valid "remote" neighbor. */ - if (ndm->ndm_state & NUD_VALID) + if (ndm->ndm_state & NUD_VALID) { + local_inactive = !(ndm->ndm_state & NUD_LOCAL_ACTIVE); + + /* XXX - populate dp-static based on the sync flags + * in the kernel + */ return zebra_vxlan_handle_kernel_neigh_update( ifp, link_if, &ip, &mac, ndm->ndm_state, - is_ext, is_router); + is_ext, is_router, local_inactive, + false /* dp_static */); + } return zebra_vxlan_handle_kernel_neigh_del(ifp, link_if, &ip); } if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("Rx %s family %s IF %s(%u) IP %s", + zlog_debug("Rx %s family %s IF %s(%u) vrf %s(%u) IP %s", nl_msg_type_to_str(h->nlmsg_type), nl_family_to_str(ndm->ndm_family), ifp->name, - ndm->ndm_ifindex, + ndm->ndm_ifindex, VRF_LOGNAME(vrf), ifp->vrf_id, ipaddr2str(&ip, buf2, sizeof(buf2))); /* Process the delete - it may result in re-adding the neighbor if it is @@ -2542,9 +3435,9 @@ static int netlink_request_neigh(struct nlsock *netlink_cmd, int family, req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)); req.ndm.ndm_family = family; if (ifindex) - addattr32(&req.n, sizeof(req), NDA_IFINDEX, ifindex); + nl_attr_put32(&req.n, sizeof(req), NDA_IFINDEX, ifindex); - return netlink_request(netlink_cmd, &req.n); + return netlink_request(netlink_cmd, &req); } /* @@ -2621,9 +3514,18 @@ static int netlink_request_specific_neigh_in_vlan(struct zebra_ns *zns, req.ndm.ndm_family = AF_INET6; } - addattr_l(&req.n, sizeof(req), NDA_DST, &ip->ip.addr, ipa_len); + nl_attr_put(&req.n, sizeof(req), NDA_DST, &ip->ip.addr, ipa_len); + + if (IS_ZEBRA_DEBUG_KERNEL) { + char buf[INET6_ADDRSTRLEN]; + + zlog_debug("%s: Tx %s family %s IF %u IP %s flags 0x%x", + __func__, nl_msg_type_to_str(type), + nl_family_to_str(req.ndm.ndm_family), ifindex, + ipaddr2str(ip, buf, sizeof(buf)), req.n.nlmsg_flags); + } - return netlink_request(&zns->netlink_cmd, &req.n); + return netlink_request(&zns->netlink_cmd, &req); } int netlink_neigh_read_specific_ip(struct ipaddr *ip, @@ -2640,11 +3542,10 @@ int netlink_neigh_read_specific_ip(struct ipaddr *ip, zebra_dplane_info_from_zns(&dp_info, zns, true /*is_cmd*/); if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("%s: neigh request IF %s(%u) IP %s vrf_id %u", - __PRETTY_FUNCTION__, vlan_if->name, - vlan_if->ifindex, + zlog_debug("%s: neigh request IF %s(%u) IP %s vrf %s(%u)", + __func__, vlan_if->name, vlan_if->ifindex, ipaddr2str(ip, buf, sizeof(buf)), - vlan_if->vrf_id); + vrf_id_to_name(vlan_if->vrf_id), vlan_if->vrf_id); ret = netlink_request_specific_neigh_in_vlan(zns, RTM_GETNEIGH, ip, vlan_if->ifindex); @@ -2668,9 +3569,10 @@ int netlink_neigh_change(struct nlmsghdr *h, ns_id_t ns_id) /* Length validity. */ len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct ndmsg)); if (len < 0) { - zlog_err("%s: Message received from netlink is of a broken size %d %zu", - __PRETTY_FUNCTION__, h->nlmsg_len, - (size_t)NLMSG_LENGTH(sizeof(struct ndmsg))); + zlog_err( + "%s: Message received from netlink is of a broken size %d %zu", + __func__, h->nlmsg_len, + (size_t)NLMSG_LENGTH(sizeof(struct ndmsg))); return -1; } @@ -2695,112 +3597,127 @@ int netlink_neigh_change(struct nlmsghdr *h, ns_id_t ns_id) return 0; } -static int netlink_neigh_update2(struct interface *ifp, struct ipaddr *ip, - struct ethaddr *mac, uint8_t flags, - uint16_t state, int cmd) +/* + * Utility neighbor-update function, using info from dplane context. + */ +static ssize_t netlink_neigh_update_ctx(const struct zebra_dplane_ctx *ctx, + int cmd, void *buf, size_t buflen) { - struct { - struct nlmsghdr n; - struct ndmsg ndm; - char buf[256]; - } req; - int ipa_len; + const struct ipaddr *ip; + const struct ethaddr *mac; + uint8_t flags; + uint16_t state; + uint8_t family; - struct zebra_ns *zns; - char buf[INET6_ADDRSTRLEN]; - char buf2[ETHER_ADDR_STRLEN]; - struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id); + ip = dplane_ctx_neigh_get_ipaddr(ctx); + mac = dplane_ctx_neigh_get_mac(ctx); + if (is_zero_mac(mac)) + mac = NULL; - zns = zvrf->zns; - memset(&req, 0, sizeof(req)); + flags = neigh_flags_to_netlink(dplane_ctx_neigh_get_flags(ctx)); + state = neigh_state_to_netlink(dplane_ctx_neigh_get_state(ctx)); - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)); - req.n.nlmsg_flags = NLM_F_REQUEST; - if (cmd == RTM_NEWNEIGH) - req.n.nlmsg_flags |= (NLM_F_CREATE | NLM_F_REPLACE); - req.n.nlmsg_type = cmd; // RTM_NEWNEIGH or RTM_DELNEIGH - req.ndm.ndm_family = IS_IPADDR_V4(ip) ? AF_INET : AF_INET6; - req.ndm.ndm_state = state; - req.ndm.ndm_ifindex = ifp->ifindex; - req.ndm.ndm_type = RTN_UNICAST; - req.ndm.ndm_flags = flags; + family = IS_IPADDR_V4(ip) ? AF_INET : AF_INET6; - ipa_len = IS_IPADDR_V4(ip) ? IPV4_MAX_BYTELEN : IPV6_MAX_BYTELEN; - addattr_l(&req.n, sizeof(req), NDA_DST, &ip->ip.addr, ipa_len); - if (mac) - addattr_l(&req.n, sizeof(req), NDA_LLADDR, mac, 6); + if (IS_ZEBRA_DEBUG_KERNEL) { + char buf[INET6_ADDRSTRLEN]; + char buf2[ETHER_ADDR_STRLEN]; - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("Tx %s family %s IF %s(%u) Neigh %s MAC %s flags 0x%x state 0x%x", - nl_msg_type_to_str(cmd), - nl_family_to_str(req.ndm.ndm_family), ifp->name, - ifp->ifindex, ipaddr2str(ip, buf, sizeof(buf)), - mac ? prefix_mac2str(mac, buf2, sizeof(buf2)) - : "null", flags, state); + zlog_debug( + "Tx %s family %s IF %s(%u) Neigh %s MAC %s flags 0x%x state 0x%x", + nl_msg_type_to_str(cmd), nl_family_to_str(family), + dplane_ctx_get_ifname(ctx), dplane_ctx_get_ifindex(ctx), + ipaddr2str(ip, buf, sizeof(buf)), + mac ? prefix_mac2str(mac, buf2, sizeof(buf2)) : "null", + flags, state); + } - return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns, - 0); + return netlink_neigh_update_msg_encode( + ctx, cmd, mac, ip, true, family, RTN_UNICAST, flags, state, + 0 /*nhg*/, false /*nfy*/, 0 /*nfy_flags*/, buf, buflen); } -int kernel_add_mac(struct interface *ifp, vlanid_t vid, struct ethaddr *mac, - struct in_addr vtep_ip, bool sticky) +static ssize_t netlink_neigh_msg_encoder(struct zebra_dplane_ctx *ctx, + void *buf, size_t buflen) { - return netlink_macfdb_update(ifp, vid, mac, vtep_ip, RTM_NEWNEIGH, - sticky); -} + ssize_t ret; -int kernel_del_mac(struct interface *ifp, vlanid_t vid, struct ethaddr *mac, - struct in_addr vtep_ip) -{ - return netlink_macfdb_update(ifp, vid, mac, vtep_ip, RTM_DELNEIGH, 0); -} + switch (dplane_ctx_get_op(ctx)) { + case DPLANE_OP_NEIGH_INSTALL: + case DPLANE_OP_NEIGH_UPDATE: + case DPLANE_OP_NEIGH_DISCOVER: + ret = netlink_neigh_update_ctx(ctx, RTM_NEWNEIGH, buf, buflen); + break; + case DPLANE_OP_NEIGH_DELETE: + ret = netlink_neigh_update_ctx(ctx, RTM_DELNEIGH, buf, buflen); + break; + case DPLANE_OP_VTEP_ADD: + ret = netlink_vxlan_flood_update_ctx(ctx, RTM_NEWNEIGH, buf, + buflen); + break; + case DPLANE_OP_VTEP_DELETE: + ret = netlink_vxlan_flood_update_ctx(ctx, RTM_DELNEIGH, buf, + buflen); + break; + default: + ret = -1; + } -int kernel_add_neigh(struct interface *ifp, struct ipaddr *ip, - struct ethaddr *mac, uint8_t flags) -{ - return netlink_neigh_update2(ifp, ip, mac, flags, - NUD_NOARP, RTM_NEWNEIGH); + return ret; } -int kernel_del_neigh(struct interface *ifp, struct ipaddr *ip) +/* + * Update MAC, using dataplane context object. + */ + +enum netlink_msg_status netlink_put_mac_update_msg(struct nl_batch *bth, + struct zebra_dplane_ctx *ctx) { - return netlink_neigh_update2(ifp, ip, NULL, 0, 0, RTM_DELNEIGH); + return netlink_batch_add_msg(bth, ctx, netlink_macfdb_update_ctx, + false); } -int kernel_upd_neigh(struct interface *ifp, struct ipaddr *ip, - struct ethaddr *mac, uint8_t flags, uint16_t state) +enum netlink_msg_status +netlink_put_neigh_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx) { - return netlink_neigh_update2(ifp, ip, mac, flags, - state, RTM_NEWNEIGH); + return netlink_batch_add_msg(bth, ctx, netlink_neigh_msg_encoder, + false); } /* * MPLS label forwarding table change via netlink interface, using dataplane * context information. */ -int netlink_mpls_multipath(int cmd, struct zebra_dplane_ctx *ctx) +ssize_t netlink_mpls_multipath_msg_encode(int cmd, struct zebra_dplane_ctx *ctx, + void *buf, size_t buflen) { mpls_lse_t lse; + const struct nhlfe_list_head *head; const zebra_nhlfe_t *nhlfe; struct nexthop *nexthop = NULL; unsigned int nexthop_num; const char *routedesc; int route_type; + struct prefix p = {0}; struct { struct nlmsghdr n; struct rtmsg r; - char buf[NL_PKT_BUF_SIZE]; - } req; + char buf[0]; + } *req = buf; - memset(&req, 0, sizeof(req) - NL_PKT_BUF_SIZE); + if (buflen < sizeof(*req)) + return 0; + + memset(req, 0, sizeof(*req)); /* * Count # nexthops so we can decide whether to use singlepath * or multipath case. */ nexthop_num = 0; - for (nhlfe = dplane_ctx_get_nhlfe(ctx); nhlfe; nhlfe = nhlfe->next) { + head = dplane_ctx_get_nhlfe_list(ctx); + frr_each(nhlfe_list_const, head, nhlfe) { nexthop = nhlfe->nexthop; if (!nexthop) continue; @@ -2821,30 +3738,31 @@ int netlink_mpls_multipath(int cmd, struct zebra_dplane_ctx *ctx) (!dplane_ctx_get_best_nhlfe(ctx) && (cmd != RTM_DELROUTE))) return 0; - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); - req.n.nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST; - req.n.nlmsg_type = cmd; - req.n.nlmsg_pid = dplane_ctx_get_ns(ctx)->nls.snl.nl_pid; + req->n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + req->n.nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST; + req->n.nlmsg_type = cmd; + req->n.nlmsg_pid = dplane_ctx_get_ns(ctx)->nls.snl.nl_pid; - req.r.rtm_family = AF_MPLS; - req.r.rtm_table = RT_TABLE_MAIN; - req.r.rtm_dst_len = MPLS_LABEL_LEN_BITS; - req.r.rtm_scope = RT_SCOPE_UNIVERSE; - req.r.rtm_type = RTN_UNICAST; + req->r.rtm_family = AF_MPLS; + req->r.rtm_table = RT_TABLE_MAIN; + req->r.rtm_dst_len = MPLS_LABEL_LEN_BITS; + req->r.rtm_scope = RT_SCOPE_UNIVERSE; + req->r.rtm_type = RTN_UNICAST; if (cmd == RTM_NEWROUTE) { /* We do a replace to handle update. */ - req.n.nlmsg_flags |= NLM_F_REPLACE; + req->n.nlmsg_flags |= NLM_F_REPLACE; /* set the protocol value if installing */ route_type = re_type_from_lsp_type( dplane_ctx_get_best_nhlfe(ctx)->type); - req.r.rtm_protocol = zebra2proto(route_type); + req->r.rtm_protocol = zebra2proto(route_type); } /* Fill destination */ lse = mpls_lse_encode(dplane_ctx_get_in_label(ctx), 0, 0, 1); - addattr_l(&req.n, sizeof(req), RTA_DST, &lse, sizeof(mpls_lse_t)); + if (!nl_attr_put(&req->n, buflen, RTA_DST, &lse, sizeof(mpls_lse_t))) + return 0; /* Fill nexthops (paths) based on single-path or multipath. The paths * chosen depend on the operation. @@ -2855,8 +3773,7 @@ int netlink_mpls_multipath(int cmd, struct zebra_dplane_ctx *ctx) routedesc); nexthop_num = 0; - for (nhlfe = dplane_ctx_get_nhlfe(ctx); - nhlfe; nhlfe = nhlfe->next) { + frr_each(nhlfe_list_const, head, nhlfe) { nexthop = nhlfe->nexthop; if (!nexthop) continue; @@ -2871,32 +3788,29 @@ int netlink_mpls_multipath(int cmd, struct zebra_dplane_ctx *ctx) && CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)))) { /* Add the gateway */ - _netlink_mpls_build_singlepath( - routedesc, nhlfe, - &req.n, &req.r, - sizeof(req), cmd); + if (!_netlink_mpls_build_singlepath( + &p, routedesc, nhlfe, &req->n, + &req->r, buflen, cmd)) + return false; nexthop_num++; break; } } } else { /* Multipath case */ - char buf[NL_PKT_BUF_SIZE]; - struct rtattr *rta = (void *)buf; - struct rtnexthop *rtnh; + struct rtattr *nest; const union g_addr *src1 = NULL; - rta->rta_type = RTA_MULTIPATH; - rta->rta_len = RTA_LENGTH(0); - rtnh = RTA_DATA(rta); + nest = nl_attr_nest(&req->n, buflen, RTA_MULTIPATH); + if (!nest) + return 0; routedesc = "multipath"; _netlink_mpls_debug(cmd, dplane_ctx_get_in_label(ctx), routedesc); nexthop_num = 0; - for (nhlfe = dplane_ctx_get_nhlfe(ctx); - nhlfe; nhlfe = nhlfe->next) { + frr_each(nhlfe_list_const, head, nhlfe) { nexthop = nhlfe->nexthop; if (!nexthop) continue; @@ -2913,21 +3827,185 @@ int netlink_mpls_multipath(int cmd, struct zebra_dplane_ctx *ctx) nexthop_num++; /* Build the multipath */ - _netlink_mpls_build_multipath(routedesc, nhlfe, - rta, rtnh, &req.r, - &src1); - rtnh = RTNH_NEXT(rtnh); + if (!_netlink_mpls_build_multipath( + &p, routedesc, nhlfe, &req->n, + buflen, &req->r, &src1)) + return 0; } } /* Add the multipath */ - if (rta->rta_len > RTA_LENGTH(0)) - addattr_l(&req.n, NL_PKT_BUF_SIZE, RTA_MULTIPATH, - RTA_DATA(rta), RTA_PAYLOAD(rta)); + nl_attr_nest_end(&req->n, nest); + } + + return NLMSG_ALIGN(req->n.nlmsg_len); +} + +/**************************************************************************** +* This code was developed in a branch that didn't have dplane APIs for +* MAC updates. Hence the use of the legacy style. It will be moved to +* the new dplane style pre-merge to master. XXX +*/ +static int netlink_fdb_nh_update(uint32_t nh_id, struct in_addr vtep_ip) +{ + struct { + struct nlmsghdr n; + struct nhmsg nhm; + char buf[256]; + } req; + int cmd = RTM_NEWNEXTHOP; + struct zebra_vrf *zvrf; + struct zebra_ns *zns; + + zvrf = zebra_vrf_get_evpn(); + if (!zvrf) + return -1; + zns = zvrf->zns; + + memset(&req, 0, sizeof(req)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct nhmsg)); + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_flags |= (NLM_F_CREATE | NLM_F_REPLACE); + req.n.nlmsg_type = cmd; + req.nhm.nh_family = AF_INET; + + if (!nl_attr_put32(&req.n, sizeof(req), NHA_ID, nh_id)) + return -1; + if (!nl_attr_put(&req.n, sizeof(req), NHA_FDB, NULL, 0)) + return -1; + if (!nl_attr_put(&req.n, sizeof(req), NHA_GATEWAY, + &vtep_ip, IPV4_MAX_BYTELEN)) + return -1; + + if (IS_ZEBRA_DEBUG_KERNEL || IS_ZEBRA_DEBUG_EVPN_MH_NH) { + zlog_debug("Tx %s fdb-nh 0x%x %s", + nl_msg_type_to_str(cmd), nh_id, inet_ntoa(vtep_ip)); + } + + return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns, + 0); +} + +static int netlink_fdb_nh_del(uint32_t nh_id) +{ + struct { + struct nlmsghdr n; + struct nhmsg nhm; + char buf[256]; + } req; + int cmd = RTM_DELNEXTHOP; + struct zebra_vrf *zvrf; + struct zebra_ns *zns; + + zvrf = zebra_vrf_get_evpn(); + if (!zvrf) + return -1; + zns = zvrf->zns; + + memset(&req, 0, sizeof(req)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct nhmsg)); + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_type = cmd; + req.nhm.nh_family = AF_UNSPEC; + + if (!nl_attr_put32(&req.n, sizeof(req), NHA_ID, nh_id)) + return -1; + + if (IS_ZEBRA_DEBUG_KERNEL || IS_ZEBRA_DEBUG_EVPN_MH_NH) { + zlog_debug("Tx %s fdb-nh 0x%x", + nl_msg_type_to_str(cmd), nh_id); + } + + return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns, + 0); +} + +static int netlink_fdb_nhg_update(uint32_t nhg_id, uint32_t nh_cnt, + struct nh_grp *nh_ids) +{ + struct { + struct nlmsghdr n; + struct nhmsg nhm; + char buf[256]; + } req; + int cmd = RTM_NEWNEXTHOP; + struct zebra_vrf *zvrf; + struct zebra_ns *zns; + struct nexthop_grp grp[nh_cnt]; + uint32_t i; + + zvrf = zebra_vrf_get_evpn(); + if (!zvrf) + return -1; + zns = zvrf->zns; + + memset(&req, 0, sizeof(req)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct nhmsg)); + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_flags |= (NLM_F_CREATE | NLM_F_REPLACE); + req.n.nlmsg_type = cmd; + req.nhm.nh_family = AF_UNSPEC; + + if (!nl_attr_put32(&req.n, sizeof(req), NHA_ID, nhg_id)) + return -1; + if (!nl_attr_put(&req.n, sizeof(req), NHA_FDB, NULL, 0)) + return -1; + memset(&grp, 0, sizeof(grp)); + for (i = 0; i < nh_cnt; ++i) { + grp[i].id = nh_ids[i].id; + grp[i].weight = nh_ids[i].weight; + } + if (!nl_attr_put(&req.n, sizeof(req), NHA_GROUP, + grp, nh_cnt * sizeof(struct nexthop_grp))) + return -1; + + + if (IS_ZEBRA_DEBUG_KERNEL || IS_ZEBRA_DEBUG_EVPN_MH_NH) { + char vtep_str[ES_VTEP_LIST_STR_SZ]; + char nh_buf[16]; + + vtep_str[0] = '\0'; + for (i = 0; i < nh_cnt; ++i) { + snprintf(nh_buf, sizeof(nh_buf), "%u ", + grp[i].id); + strlcat(vtep_str, nh_buf, sizeof(vtep_str)); + } + + zlog_debug("Tx %s fdb-nhg 0x%x %s", + nl_msg_type_to_str(cmd), nhg_id, vtep_str); } - /* Talk to netlink socket. */ - return netlink_talk_info(netlink_talk_filter, &req.n, - dplane_ctx_get_ns(ctx), 0); + return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns, + 0); +} + +static int netlink_fdb_nhg_del(uint32_t nhg_id) +{ + return netlink_fdb_nh_del(nhg_id); +} + +int kernel_upd_mac_nh(uint32_t nh_id, struct in_addr vtep_ip) +{ + return netlink_fdb_nh_update(nh_id, vtep_ip); +} + +int kernel_del_mac_nh(uint32_t nh_id) +{ + return netlink_fdb_nh_del(nh_id); +} + +int kernel_upd_mac_nhg(uint32_t nhg_id, uint32_t nh_cnt, + struct nh_grp *nh_ids) +{ + return netlink_fdb_nhg_update(nhg_id, nh_cnt, nh_ids); } + +int kernel_del_mac_nhg(uint32_t nhg_id) +{ + return netlink_fdb_nhg_del(nhg_id); +} + #endif /* HAVE_NETLINK */ diff --git a/zebra/rt_netlink.h b/zebra/rt_netlink.h index 29e0152bb2..e1bb844785 100644 --- a/zebra/rt_netlink.h +++ b/zebra/rt_netlink.h @@ -60,15 +60,32 @@ extern "C" { #define RTPROT_PBR 195 #define RTPROT_ZSTATIC 196 #define RTPROT_OPENFABRIC 197 +#define RTPROT_SRTE 198 void rt_netlink_init(void); /* MPLS label forwarding table change, using dataplane context information. */ -extern int netlink_mpls_multipath(int cmd, struct zebra_dplane_ctx *ctx); +extern ssize_t netlink_mpls_multipath_msg_encode(int cmd, + struct zebra_dplane_ctx *ctx, + void *buf, size_t buflen); + +extern ssize_t netlink_route_multipath_msg_encode(int cmd, + struct zebra_dplane_ctx *ctx, + uint8_t *data, size_t datalen, + bool fpm, bool force_nhg); +extern ssize_t netlink_macfdb_update_ctx(struct zebra_dplane_ctx *ctx, + void *data, size_t datalen); extern int netlink_route_change(struct nlmsghdr *h, ns_id_t ns_id, int startup); extern int netlink_route_read(struct zebra_ns *zns); +extern int netlink_nexthop_change(struct nlmsghdr *h, ns_id_t ns_id, + int startup); +extern int netlink_nexthop_read(struct zebra_ns *zns); +extern ssize_t netlink_nexthop_msg_encode(uint16_t cmd, + const struct zebra_dplane_ctx *ctx, + void *buf, size_t buflen); + extern int netlink_neigh_change(struct nlmsghdr *h, ns_id_t ns_id); extern int netlink_macfdb_read(struct zebra_ns *zns); extern int netlink_macfdb_read_for_bridge(struct zebra_ns *zns, @@ -82,6 +99,24 @@ extern int netlink_macfdb_read_specific_mac(struct zebra_ns *zns, struct ethaddr *mac, uint16_t vid); extern int netlink_neigh_read_specific_ip(struct ipaddr *ip, struct interface *vlan_if); +extern vrf_id_t vrf_lookup_by_table(uint32_t table_id, ns_id_t ns_id); + +struct nl_batch; +extern enum netlink_msg_status +netlink_put_route_update_msg(struct nl_batch *bth, + struct zebra_dplane_ctx *ctx); +extern enum netlink_msg_status +netlink_put_nexthop_update_msg(struct nl_batch *bth, + struct zebra_dplane_ctx *ctx); +extern enum netlink_msg_status +netlink_put_mac_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx); +extern enum netlink_msg_status +netlink_put_neigh_update_msg(struct nl_batch *bth, + struct zebra_dplane_ctx *ctx); +extern enum netlink_msg_status +netlink_put_lsp_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx); +extern enum netlink_msg_status +netlink_put_pw_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx); #ifdef __cplusplus } diff --git a/zebra/rt_socket.c b/zebra/rt_socket.c index 8d8bdd0a6d..a2e15cbd2b 100644 --- a/zebra/rt_socket.c +++ b/zebra/rt_socket.c @@ -50,8 +50,7 @@ static int kernel_rtm_add_labels(struct mpls_label_stack *nh_label, { if (nh_label->num_labels > 1) { flog_warn(EC_ZEBRA_MAX_LABELS_PUSH, - "%s: can't push %u labels at " - "once (maximum is 1)", + "%s: can't push %u labels at once (maximum is 1)", __func__, nh_label->num_labels); return -1; } @@ -178,8 +177,9 @@ static int kernel_rtm(int cmd, const struct prefix *p, case NEXTHOP_TYPE_BLACKHOLE: bh_type = nexthop->bh_type; switch (p->family) { - case AFI_IP: { + case AF_INET: { struct in_addr loopback; + loopback.s_addr = htonl(INADDR_LOOPBACK); sin_gate.sin.sin_addr = loopback; #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN @@ -187,10 +187,21 @@ static int kernel_rtm(int cmd, const struct prefix *p, sizeof(struct sockaddr_in); #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */ gate = true; - } - break; - case AFI_IP6: - break; + } break; + case AF_INET6: { + struct in6_addr loopback; + + inet_pton(AF_INET6, "::1", &loopback); + + sin_gate.sin6.sin6_addr = loopback; + sin_gate.sin6.sin6_family = AF_INET6; + +#ifdef HAVE_STRUCTSOCKADDR_SA_LEN + sin_gate.sin6.sin6_len = + sizeof(struct sockaddr_in6); +#endif /* HAVE_STRUCTSOCKADDR_SA_LEN */ + gate = true; + } break; } } @@ -230,13 +241,13 @@ static int kernel_rtm(int cmd, const struct prefix *p, __func__, prefix_buf); } else { switch (p->family) { - case AFI_IP: + case AF_INET: inet_ntop(AF_INET, &sin_gate.sin.sin_addr, gate_buf, sizeof(gate_buf)); break; - case AFI_IP6: + case AF_INET6: inet_ntop(AF_INET6, &sin_gate.sin6.sin6_addr, gate_buf, sizeof(gate_buf)); @@ -314,7 +325,7 @@ enum zebra_dplane_result kernel_route_update(struct zebra_dplane_ctx *ctx) type = dplane_ctx_get_type(ctx); old_type = dplane_ctx_get_old_type(ctx); - frr_elevate_privs(&zserv_privs) { + frr_with_privs(&zserv_privs) { if (dplane_ctx_get_op(ctx) == DPLANE_OP_ROUTE_DELETE) { if (!RSYSTEM_ROUTE(type)) @@ -347,23 +358,14 @@ enum zebra_dplane_result kernel_route_update(struct zebra_dplane_ctx *ctx) } } /* Elevated privs */ - if (RSYSTEM_ROUTE(type) - && dplane_ctx_get_op(ctx) != DPLANE_OP_ROUTE_DELETE) { - struct nexthop *nexthop; - - 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); - } - } - } - return res; } +enum zebra_dplane_result kernel_nexthop_update(struct zebra_dplane_ctx *ctx) +{ + return ZEBRA_DPLANE_REQUEST_SUCCESS; +} + int kernel_neigh_update(int add, int ifindex, uint32_t addr, char *lla, int llalen, ns_id_t ns_id) { @@ -371,53 +373,55 @@ int kernel_neigh_update(int add, int ifindex, uint32_t addr, char *lla, return 0; } -extern int kernel_get_ipmr_sg_stats(struct zebra_vrf *zvrf, void *mroute) +/* NYI on routing-socket platforms, but we've always returned 'success'... */ +enum zebra_dplane_result kernel_neigh_update_ctx(struct zebra_dplane_ctx *ctx) { - return 0; + return ZEBRA_DPLANE_REQUEST_SUCCESS; } -int kernel_add_vtep(vni_t vni, struct interface *ifp, struct in_addr *vtep_ip) +extern int kernel_get_ipmr_sg_stats(struct zebra_vrf *zvrf, void *mroute) { return 0; } -int kernel_del_vtep(vni_t vni, struct interface *ifp, struct in_addr *vtep_ip) +/* + * Update MAC, using dataplane context object. No-op here for now. + */ +enum zebra_dplane_result kernel_mac_update_ctx(struct zebra_dplane_ctx *ctx) { - return 0; + return ZEBRA_DPLANE_REQUEST_SUCCESS; } -int kernel_add_mac(struct interface *ifp, vlanid_t vid, struct ethaddr *mac, - struct in_addr vtep_ip, bool sticky) +extern int kernel_interface_set_master(struct interface *master, + struct interface *slave) { return 0; } -int kernel_del_mac(struct interface *ifp, vlanid_t vid, struct ethaddr *mac, - struct in_addr vtep_ip) +uint32_t kernel_get_speed(struct interface *ifp, int *error) { - return 0; + return ifp->speed; } -int kernel_add_neigh(struct interface *ifp, struct ipaddr *ip, - struct ethaddr *mac, uint8_t flags) +int kernel_upd_mac_nh(uint32_t nh_id, struct in_addr vtep_ip) { return 0; } -int kernel_del_neigh(struct interface *ifp, struct ipaddr *ip) +int kernel_del_mac_nh(uint32_t nh_id) { return 0; } -extern int kernel_interface_set_master(struct interface *master, - struct interface *slave) +int kernel_upd_mac_nhg(uint32_t nhg_id, uint32_t nh_cnt, + struct nh_grp *nh_ids) { return 0; } -uint32_t kernel_get_speed(struct interface *ifp) +int kernel_del_mac_nhg(uint32_t nhg_id) { - return ifp->speed; + return 0; } #endif /* !HAVE_NETLINK */ diff --git a/zebra/rtadv.c b/zebra/rtadv.c index 5088e2e8e1..13b7c150a3 100644 --- a/zebra/rtadv.c +++ b/zebra/rtadv.c @@ -42,7 +42,6 @@ #include "zebra/debug.h" #include "zebra/rib.h" #include "zebra/zapi_msg.h" -#include "zebra/zebra_ns.h" #include "zebra/zebra_vrf.h" #include "zebra/zebra_errors.h" #include "zebra/zebra_router.h" @@ -51,16 +50,22 @@ extern struct zebra_privs_t zserv_privs; #if defined(HAVE_RTADV) +#ifndef VTYSH_EXTRACT_PL +#include "zebra/rtadv_clippy.c" +#endif + +DEFINE_MTYPE_STATIC(ZEBRA, RTADV_PREFIX, "Router Advertisement Prefix") + #ifdef OPEN_BSD #include #endif /* If RFC2133 definition is used. */ #ifndef IPV6_JOIN_GROUP -#define IPV6_JOIN_GROUP IPV6_ADD_MEMBERSHIP +#define IPV6_JOIN_GROUP IPV6_ADD_MEMBERSHIP #endif #ifndef IPV6_LEAVE_GROUP -#define IPV6_LEAVE_GROUP IPV6_DROP_MEMBERSHIP +#define IPV6_LEAVE_GROUP IPV6_DROP_MEMBERSHIP #endif #define ALLNODE "ff02::1" @@ -71,7 +76,9 @@ DEFINE_MTYPE_STATIC(ZEBRA, RTADV_DNSSL, "Router Advertisement DNSSL") /* Order is intentional. Matches RFC4191. This array is also used for command matching, so only modify with care. */ -const char *rtadv_pref_strs[] = {"medium", "high", "INVALID", "low", 0}; +static const char *const rtadv_pref_strs[] = { + "medium", "high", "INVALID", "low", 0 +}; enum rtadv_event { RTADV_START, @@ -81,18 +88,25 @@ enum rtadv_event { RTADV_READ }; -static void rtadv_event(struct zebra_ns *, enum rtadv_event, int); +static void rtadv_event(struct zebra_vrf *, enum rtadv_event, int); static int if_join_all_router(int, struct interface *); static int if_leave_all_router(int, struct interface *); -static int rtadv_increment_received(struct zebra_ns *zns, ifindex_t *ifindex) +static int rtadv_get_socket(struct zebra_vrf *zvrf) +{ + if (zvrf->rtadv.sock > 0) + return zvrf->rtadv.sock; + return zrouter.rtadv_sock; +} + +static int rtadv_increment_received(struct zebra_vrf *zvrf, ifindex_t *ifindex) { int ret = -1; struct interface *iface; struct zebra_if *zif; - iface = if_lookup_by_index_per_ns(zns, *ifindex); + iface = if_lookup_by_index(*ifindex, zvrf->vrf->vrf_id); if (iface && iface->info) { zif = iface->info; zif->ra_rcvd++; @@ -101,7 +115,7 @@ static int rtadv_increment_received(struct zebra_ns *zns, ifindex_t *ifindex) return ret; } -static int rtadv_recv_packet(struct zebra_ns *zns, int sock, uint8_t *buf, +static int rtadv_recv_packet(struct zebra_vrf *zvrf, int sock, uint8_t *buf, int buflen, struct sockaddr_in6 *from, ifindex_t *ifindex, int *hoplimit) { @@ -120,7 +134,7 @@ static int rtadv_recv_packet(struct zebra_ns *zns, int sock, uint8_t *buf, msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = (void *)adata; - msg.msg_controllen = sizeof adata; + msg.msg_controllen = sizeof(adata); iov.iov_base = buf; iov.iov_len = buflen; @@ -149,14 +163,15 @@ static int rtadv_recv_packet(struct zebra_ns *zns, int sock, uint8_t *buf, } } - rtadv_increment_received(zns, ifindex); + rtadv_increment_received(zvrf, ifindex); return ret; } #define RTADV_MSG_SIZE 4096 /* Send router advertisement packet. */ -static void rtadv_send_packet(int sock, struct interface *ifp) +static void rtadv_send_packet(int sock, struct interface *ifp, + enum ipv6_nd_suppress_ra_status stop) { struct msghdr msg; struct iovec iov; @@ -193,9 +208,12 @@ static void rtadv_send_packet(int sock, struct interface *ifp) } /* Logging of packet. */ - if (IS_ZEBRA_DEBUG_PACKET) - zlog_debug("%s(%u): Tx RA, socket %u", ifp->name, ifp->ifindex, - sock); + if (IS_ZEBRA_DEBUG_PACKET) { + struct vrf *vrf = vrf_lookup_by_id(ifp->vrf_id); + + zlog_debug("%s(%s:%u): Tx RA, socket %u", ifp->name, + VRF_LOGNAME(vrf), ifp->ifindex, sock); + } /* Fill in sockaddr_in6. */ memset(&addr, 0, sizeof(struct sockaddr_in6)); @@ -216,7 +234,7 @@ static void rtadv_send_packet(int sock, struct interface *ifp) rtadv->nd_ra_code = 0; rtadv->nd_ra_cksum = 0; - rtadv->nd_ra_curhoplimit = 64; + rtadv->nd_ra_curhoplimit = zif->rtadv.AdvCurHopLimit; /* RFC4191: Default Router Preference is 0 if Router Lifetime is 0. */ rtadv->nd_ra_flags_reserved = zif->rtadv.AdvDefaultLifetime == 0 @@ -242,9 +260,12 @@ static void rtadv_send_packet(int sock, struct interface *ifp) zif->rtadv.AdvDefaultLifetime != -1 ? zif->rtadv.AdvDefaultLifetime : MAX(1, 0.003 * zif->rtadv.MaxRtrAdvInterval); - rtadv->nd_ra_router_lifetime = htons(pkt_RouterLifetime); + + /* send RA lifetime of 0 before stopping. rfc4861/6.2.5 */ + rtadv->nd_ra_router_lifetime = + (stop == RA_SUPPRESS) ? htons(0) : htons(pkt_RouterLifetime); rtadv->nd_ra_reachable = htonl(zif->rtadv.AdvReachableTime); - rtadv->nd_ra_retransmit = htonl(0); + rtadv->nd_ra_retransmit = htonl(zif->rtadv.AdvRetransTimer); len = sizeof(struct nd_router_advert); @@ -319,16 +340,6 @@ static void rtadv_send_packet(int sock, struct interface *ifp) IPV6_ADDR_COPY(&pinfo->nd_opt_pi_prefix, &rprefix->prefix.prefix); -#ifdef DEBUG - { - uint8_t buf[INET6_ADDRSTRLEN]; - - zlog_debug("DEBUG %s", - inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, - buf, INET6_ADDRSTRLEN)); - } -#endif /* DEBUG */ - len += sizeof(struct nd_opt_prefix_info); } @@ -374,9 +385,11 @@ static void rtadv_send_packet(int sock, struct interface *ifp) sizeof(struct nd_opt_rdnss) + sizeof(struct in6_addr); if (len + opt_len > max_len) { + struct vrf *vrf = vrf_lookup_by_id(ifp->vrf_id); + zlog_warn( - "%s(%u): Tx RA: RDNSS option would exceed MTU, omitting it", - ifp->name, ifp->ifindex); + "%s(%s:%u): Tx RA: RDNSS option would exceed MTU, omitting it", + ifp->name, VRF_LOGNAME(vrf), ifp->ifindex); goto no_more_opts; } struct nd_opt_rdnss *opt = (struct nd_opt_rdnss *)(buf + len); @@ -461,19 +474,19 @@ static void rtadv_send_packet(int sock, struct interface *ifp) static int rtadv_timer(struct thread *thread) { - struct zebra_ns *zns = THREAD_ARG(thread); + struct zebra_vrf *zvrf = THREAD_ARG(thread); struct vrf *vrf; struct interface *ifp; struct zebra_if *zif; int period; - zrouter.rtadv.ra_timer = NULL; - if (zrouter.rtadv.adv_msec_if_count == 0) { + zvrf->rtadv.ra_timer = NULL; + if (zvrf->rtadv.adv_msec_if_count == 0) { period = 1000; /* 1 s */ - rtadv_event(zns, RTADV_TIMER, 1 /* 1 s */); + rtadv_event(zvrf, RTADV_TIMER, 1 /* 1 s */); } else { period = 10; /* 10 ms */ - rtadv_event(zns, RTADV_TIMER_MSEC, 10 /* 10 ms */); + rtadv_event(zvrf, RTADV_TIMER_MSEC, 10 /* 10 ms */); } RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) @@ -487,7 +500,8 @@ static int rtadv_timer(struct thread *thread) zif = ifp->info; if (zif->rtadv.AdvSendAdvertisements) { - if (zif->rtadv.inFastRexmit) { + if (zif->rtadv.inFastRexmit + && zif->rtadv.UseFastRexmit) { /* We assume we fast rexmit every sec so * no * additional vars */ @@ -495,13 +509,20 @@ static int rtadv_timer(struct thread *thread) <= 0) zif->rtadv.inFastRexmit = 0; - if (IS_ZEBRA_DEBUG_SEND) + if (IS_ZEBRA_DEBUG_SEND) { + struct vrf *vrf = + vrf_lookup_by_id( + ifp->vrf_id); + zlog_debug( - "Fast RA Rexmit on interface %s", - ifp->name); + "Fast RA Rexmit on interface %s(%s:%u)", + ifp->name, + VRF_LOGNAME(vrf), + ifp->ifindex); + } - rtadv_send_packet(zrouter.rtadv.sock, - ifp); + rtadv_send_packet(rtadv_get_socket(zvrf), + ifp, RA_ENABLE); } else { zif->rtadv.AdvIntervalTimer -= period; if (zif->rtadv.AdvIntervalTimer <= 0) { @@ -514,8 +535,8 @@ static int rtadv_timer(struct thread *thread) zif->rtadv .MaxRtrAdvInterval; rtadv_send_packet( - zrouter.rtadv.sock, - ifp); + rtadv_get_socket(zvrf), + ifp, RA_ENABLE); } } } @@ -527,10 +548,28 @@ static int rtadv_timer(struct thread *thread) static void rtadv_process_solicit(struct interface *ifp) { struct zebra_vrf *zvrf = vrf_info_lookup(ifp->vrf_id); - struct zebra_ns *zns = zvrf->zns; + struct zebra_if *zif; - assert(zns); - rtadv_send_packet(zrouter.rtadv.sock, ifp); + assert(zvrf); + zif = ifp->info; + + /* + * If FastRetransmit is enabled, send the RA immediately. + * If not enabled but it has been more than MIN_DELAY_BETWEEN_RAS + * (3 seconds) since the last RA was sent, send it now and reset + * the timer to start at the max (configured) again. + * If not enabled and it is less than 3 seconds since the last + * RA packet was sent, set the timer for 3 seconds so the next + * one will be sent with a minimum of 3 seconds between RAs. + * RFC4861 sec 6.2.6 + */ + if ((zif->rtadv.UseFastRexmit) + || (zif->rtadv.AdvIntervalTimer <= + (zif->rtadv.MaxRtrAdvInterval - MIN_DELAY_BETWEEN_RAS))) { + rtadv_send_packet(rtadv_get_socket(zvrf), ifp, RA_ENABLE); + zif->rtadv.AdvIntervalTimer = zif->rtadv.MaxRtrAdvInterval; + } else + zif->rtadv.AdvIntervalTimer = MIN_DELAY_BETWEEN_RAS; } /* @@ -579,9 +618,14 @@ static void rtadv_process_advert(uint8_t *msg, unsigned int len, inet_ntop(AF_INET6, &addr->sin6_addr, addr_str, INET6_ADDRSTRLEN); if (len < sizeof(struct nd_router_advert)) { - if (IS_ZEBRA_DEBUG_PACKET) - zlog_debug("%s(%u): Rx RA with invalid length %d from %s", - ifp->name, ifp->ifindex, len, addr_str); + if (IS_ZEBRA_DEBUG_PACKET) { + struct vrf *vrf = vrf_lookup_by_id(ifp->vrf_id); + + zlog_debug( + "%s(%s:%u): Rx RA with invalid length %d from %s", + ifp->name, VRF_LOGNAME(vrf), ifp->ifindex, len, + addr_str); + } return; } @@ -589,9 +633,14 @@ static void rtadv_process_advert(uint8_t *msg, unsigned int len, rtadv_process_optional(msg + sizeof(struct nd_router_advert), len - sizeof(struct nd_router_advert), ifp, addr); - if (IS_ZEBRA_DEBUG_PACKET) - zlog_debug("%s(%u): Rx RA with non-linklocal source address from %s", - ifp->name, ifp->ifindex, addr_str); + if (IS_ZEBRA_DEBUG_PACKET) { + struct vrf *vrf = vrf_lookup_by_id(ifp->vrf_id); + + zlog_debug( + "%s(%s:%u): Rx RA with non-linklocal source address from %s", + ifp->name, VRF_LOGNAME(vrf), ifp->ifindex, + addr_str); + } return; } @@ -630,9 +679,8 @@ static void rtadv_process_advert(uint8_t *msg, unsigned int len, ifp->name, ifp->ifindex, addr_str); } - if ((radvert->nd_ra_retransmit && zif->rtadv.AdvRetransTimer) - && (ntohl(radvert->nd_ra_retransmit) - != (unsigned int)zif->rtadv.AdvRetransTimer)) { + if ((ntohl(radvert->nd_ra_retransmit) + != (unsigned int)zif->rtadv.AdvRetransTimer)) { flog_warn( EC_ZEBRA_RA_PARAM_MISMATCH, "%s(%u): Rx RA - our AdvRetransTimer doesn't agree with %s", @@ -652,7 +700,7 @@ static void rtadv_process_advert(uint8_t *msg, unsigned int len, static void rtadv_process_packet(uint8_t *buf, unsigned int len, ifindex_t ifindex, int hoplimit, struct sockaddr_in6 *from, - struct zebra_ns *zns) + struct zebra_vrf *zvrf) { struct icmp6_hdr *icmph; struct interface *ifp; @@ -662,7 +710,7 @@ static void rtadv_process_packet(uint8_t *buf, unsigned int len, inet_ntop(AF_INET6, &from->sin6_addr, addr_str, INET6_ADDRSTRLEN); /* Interface search. */ - ifp = if_lookup_by_index_per_ns(zns, ifindex); + ifp = if_lookup_by_index(ifindex, zvrf->vrf->vrf_id); if (ifp == NULL) { flog_warn(EC_ZEBRA_UNKNOWN_INTERFACE, "RA/RS received on unknown IF %u from %s", ifindex, @@ -670,9 +718,12 @@ static void rtadv_process_packet(uint8_t *buf, unsigned int len, return; } - if (IS_ZEBRA_DEBUG_PACKET) - zlog_debug("%s(%u): Rx RA/RS len %d from %s", ifp->name, - ifp->ifindex, len, addr_str); + if (IS_ZEBRA_DEBUG_PACKET) { + struct vrf *vrf = vrf_lookup_by_id(ifp->vrf_id); + + zlog_debug("%s(%s:%u): Rx RA/RS len %d from %s", ifp->name, + VRF_LOGNAME(vrf), ifp->ifindex, len, addr_str); + } if (if_is_loopback(ifp) || CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK)) @@ -685,8 +736,11 @@ static void rtadv_process_packet(uint8_t *buf, unsigned int len, /* ICMP message length check. */ if (len < sizeof(struct icmp6_hdr)) { - zlog_debug("%s(%u): Rx RA with Invalid ICMPV6 packet length %d", - ifp->name, ifp->ifindex, len); + struct vrf *vrf = vrf_lookup_by_id(ifp->vrf_id); + + zlog_debug( + "%s(%s:%u): Rx RA with Invalid ICMPV6 packet length %d", + ifp->name, VRF_LOGNAME(vrf), ifp->ifindex, len); return; } @@ -695,15 +749,20 @@ static void rtadv_process_packet(uint8_t *buf, unsigned int len, /* ICMP message type check. */ if (icmph->icmp6_type != ND_ROUTER_SOLICIT && icmph->icmp6_type != ND_ROUTER_ADVERT) { - zlog_debug("%s(%u): Rx RA - Unwanted ICMPV6 message type %d", - ifp->name, ifp->ifindex, icmph->icmp6_type); + struct vrf *vrf = vrf_lookup_by_id(ifp->vrf_id); + + zlog_debug("%s(%s:%u): Rx RA - Unwanted ICMPV6 message type %d", + ifp->name, VRF_LOGNAME(vrf), ifp->ifindex, + icmph->icmp6_type); return; } /* Hoplimit check. */ if (hoplimit >= 0 && hoplimit != 255) { - zlog_debug("%s(%u): Rx RA - Invalid hoplimit %d", ifp->name, - ifp->ifindex, hoplimit); + struct vrf *vrf = vrf_lookup_by_id(ifp->vrf_id); + + zlog_debug("%s(%s:%u): Rx RA - Invalid hoplimit %d", ifp->name, + VRF_LOGNAME(vrf), ifp->ifindex, hoplimit); return; } @@ -724,15 +783,15 @@ static int rtadv_read(struct thread *thread) struct sockaddr_in6 from; ifindex_t ifindex = 0; int hoplimit = -1; - struct zebra_ns *zns = THREAD_ARG(thread); + struct zebra_vrf *zvrf = THREAD_ARG(thread); sock = THREAD_FD(thread); - zrouter.rtadv.ra_read = NULL; + zvrf->rtadv.ra_read = NULL; /* Register myself. */ - rtadv_event(zns, RTADV_READ, sock); + rtadv_event(zvrf, RTADV_READ, sock); - len = rtadv_recv_packet(zns, sock, buf, sizeof(buf), &from, &ifindex, + len = rtadv_recv_packet(zvrf, sock, buf, sizeof(buf), &from, &ifindex, &hoplimit); if (len < 0) { @@ -742,7 +801,7 @@ static int rtadv_read(struct thread *thread) return len; } - rtadv_process_packet(buf, (unsigned)len, ifindex, hoplimit, &from, zns); + rtadv_process_packet(buf, (unsigned)len, ifindex, hoplimit, &from, zvrf); return 0; } @@ -753,7 +812,7 @@ static int rtadv_make_socket(ns_id_t ns_id) int ret = 0; struct icmp6_filter filter; - frr_elevate_privs(&zserv_privs) { + frr_with_privs(&zserv_privs) { sock = ns_socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6, ns_id); @@ -843,18 +902,48 @@ static struct rtadv_prefix *rtadv_prefix_get(struct list *rplist, return rprefix; } +static void rtadv_prefix_set_defaults(struct rtadv_prefix *rp) +{ + rp->AdvAutonomousFlag = 1; + rp->AdvOnLinkFlag = 1; + rp->AdvRouterAddressFlag = 0; + rp->AdvPreferredLifetime = RTADV_PREFERRED_LIFETIME; + rp->AdvValidLifetime = RTADV_VALID_LIFETIME; +} + static void rtadv_prefix_set(struct zebra_if *zif, struct rtadv_prefix *rp) { struct rtadv_prefix *rprefix; rprefix = rtadv_prefix_get(zif->rtadv.AdvPrefixList, &rp->prefix); - /* Set parameters. */ - rprefix->AdvValidLifetime = rp->AdvValidLifetime; - rprefix->AdvPreferredLifetime = rp->AdvPreferredLifetime; - rprefix->AdvOnLinkFlag = rp->AdvOnLinkFlag; - rprefix->AdvAutonomousFlag = rp->AdvAutonomousFlag; - rprefix->AdvRouterAddressFlag = rp->AdvRouterAddressFlag; + /* + * Set parameters based on where the prefix is created. + * If auto-created based on kernel address addition, set the + * default values. If created from a manual "ipv6 nd prefix" + * command, take the parameters from the manual command. Note + * that if the manual command exists, the default values will + * not overwrite the manual values. + */ + if (rp->AdvPrefixCreate == PREFIX_SRC_MANUAL) { + if (rprefix->AdvPrefixCreate == PREFIX_SRC_AUTO) + rprefix->AdvPrefixCreate = PREFIX_SRC_BOTH; + else + rprefix->AdvPrefixCreate = PREFIX_SRC_MANUAL; + + rprefix->AdvAutonomousFlag = rp->AdvAutonomousFlag; + rprefix->AdvOnLinkFlag = rp->AdvOnLinkFlag; + rprefix->AdvRouterAddressFlag = rp->AdvRouterAddressFlag; + rprefix->AdvPreferredLifetime = rp->AdvPreferredLifetime; + rprefix->AdvValidLifetime = rp->AdvValidLifetime; + } else if (rp->AdvPrefixCreate == PREFIX_SRC_AUTO) { + if (rprefix->AdvPrefixCreate == PREFIX_SRC_MANUAL) + rprefix->AdvPrefixCreate = PREFIX_SRC_BOTH; + else { + rprefix->AdvPrefixCreate = PREFIX_SRC_AUTO; + rtadv_prefix_set_defaults(rprefix); + } + } } static int rtadv_prefix_reset(struct zebra_if *zif, struct rtadv_prefix *rp) @@ -863,6 +952,27 @@ static int rtadv_prefix_reset(struct zebra_if *zif, struct rtadv_prefix *rp) rprefix = rtadv_prefix_lookup(zif->rtadv.AdvPrefixList, &rp->prefix); if (rprefix != NULL) { + + /* + * When deleting an address from the list, need to take care + * it wasn't defined both automatically via kernel + * address addition as well as manually by vtysh cli. If both, + * we don't actually delete but may change the parameters + * back to default if a manually defined entry is deleted. + */ + if (rp->AdvPrefixCreate == PREFIX_SRC_MANUAL) { + if (rprefix->AdvPrefixCreate == PREFIX_SRC_BOTH) { + rprefix->AdvPrefixCreate = PREFIX_SRC_AUTO; + rtadv_prefix_set_defaults(rprefix); + return 1; + } + } else if (rp->AdvPrefixCreate == PREFIX_SRC_AUTO) { + if (rprefix->AdvPrefixCreate == PREFIX_SRC_BOTH) { + rprefix->AdvPrefixCreate = PREFIX_SRC_MANUAL; + return 1; + } + } + listnode_delete(zif->rtadv.AdvPrefixList, (void *)rprefix); rtadv_prefix_free(rprefix); return 1; @@ -870,48 +980,73 @@ static int rtadv_prefix_reset(struct zebra_if *zif, struct rtadv_prefix *rp) return 0; } +/* Add IPv6 prefixes learned from the kernel to the RA prefix list */ +void rtadv_add_prefix(struct zebra_if *zif, const struct prefix_ipv6 *p) +{ + struct rtadv_prefix rp; + + rp.prefix = *p; + apply_mask_ipv6(&rp.prefix); + rp.AdvPrefixCreate = PREFIX_SRC_AUTO; + rtadv_prefix_set(zif, &rp); +} + +/* Delete IPv6 prefixes removed by the kernel from the RA prefix list */ +void rtadv_delete_prefix(struct zebra_if *zif, const struct prefix *p) +{ + struct rtadv_prefix rp; + + rp.prefix = *((struct prefix_ipv6 *)p); + apply_mask_ipv6(&rp.prefix); + rp.AdvPrefixCreate = PREFIX_SRC_AUTO; + rtadv_prefix_reset(zif, &rp); +} + static void ipv6_nd_suppress_ra_set(struct interface *ifp, - ipv6_nd_suppress_ra_status status) + enum ipv6_nd_suppress_ra_status status) { struct zebra_if *zif; struct zebra_vrf *zvrf; - struct zebra_ns *zns; zif = ifp->info; zvrf = vrf_info_lookup(ifp->vrf_id); - zns = zvrf->zns; if (status == RA_SUPPRESS) { /* RA is currently enabled */ if (zif->rtadv.AdvSendAdvertisements) { + rtadv_send_packet(rtadv_get_socket(zvrf), ifp, + RA_SUPPRESS); zif->rtadv.AdvSendAdvertisements = 0; zif->rtadv.AdvIntervalTimer = 0; - zrouter.rtadv.adv_if_count--; + zvrf->rtadv.adv_if_count--; - if_leave_all_router(zrouter.rtadv.sock, ifp); + if_leave_all_router(rtadv_get_socket(zvrf), ifp); - if (zrouter.rtadv.adv_if_count == 0) - rtadv_event(zns, RTADV_STOP, 0); + if (zvrf->rtadv.adv_if_count == 0) + rtadv_event(zvrf, RTADV_STOP, 0); } } else { if (!zif->rtadv.AdvSendAdvertisements) { zif->rtadv.AdvSendAdvertisements = 1; zif->rtadv.AdvIntervalTimer = 0; - zrouter.rtadv.adv_if_count++; - - if (zif->rtadv.MaxRtrAdvInterval >= 1000) { - /* Enable Fast RA only when RA interval is in - * secs */ + zvrf->rtadv.adv_if_count++; + + if ((zif->rtadv.MaxRtrAdvInterval >= 1000) + && zif->rtadv.UseFastRexmit) { + /* + * Enable Fast RA only when RA interval is in + * secs and Fast RA retransmit is enabled + */ zif->rtadv.inFastRexmit = 1; zif->rtadv.NumFastReXmitsRemain = RTADV_NUM_FAST_REXMITS; } - if_join_all_router(zrouter.rtadv.sock, ifp); + if_join_all_router(rtadv_get_socket(zvrf), ifp); - if (zrouter.rtadv.adv_if_count == 1) - rtadv_event(zns, RTADV_START, - zrouter.rtadv.sock); + if (zvrf->rtadv.adv_if_count == 1) + rtadv_event(zvrf, RTADV_START, + rtadv_get_socket(zvrf)); } } } @@ -929,33 +1064,51 @@ static void zebra_interface_radv_set(ZAPI_HANDLER_ARGS, int enable) ifindex_t ifindex; struct interface *ifp; struct zebra_if *zif; - int ra_interval; + int ra_interval_rxd; s = msg; /* Get interface index and RA interval. */ STREAM_GETL(s, ifindex); - STREAM_GETL(s, ra_interval); + STREAM_GETL(s, ra_interval_rxd); + + if (ra_interval_rxd < 0) { + zlog_warn( + "Requested RA interval %d is garbage; ignoring request", + ra_interval_rxd); + return; + } + + unsigned int ra_interval = ra_interval_rxd; + + if (IS_ZEBRA_DEBUG_EVENT) { + struct vrf *vrf = zvrf->vrf; - if (IS_ZEBRA_DEBUG_EVENT) - zlog_debug("%u: IF %u RA %s from client %s, interval %ds", - zvrf_id(zvrf), ifindex, + zlog_debug("%s:%u: IF %u RA %s from client %s, interval %ums", + VRF_LOGNAME(vrf), zvrf_id(zvrf), ifindex, enable ? "enable" : "disable", zebra_route_string(client->proto), ra_interval); + } /* Locate interface and check VRF match. */ - ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(NS_DEFAULT), ifindex); + ifp = if_lookup_by_index(ifindex, zvrf->vrf->vrf_id); if (!ifp) { + struct vrf *vrf = zvrf->vrf; + flog_warn(EC_ZEBRA_UNKNOWN_INTERFACE, - "%u: IF %u RA %s client %s - interface unknown", - zvrf_id(zvrf), ifindex, enable ? "enable" : "disable", + "%s:%u: IF %u RA %s client %s - interface unknown", + VRF_LOGNAME(vrf), zvrf_id(zvrf), ifindex, + enable ? "enable" : "disable", zebra_route_string(client->proto)); return; } if (ifp->vrf_id != zvrf_id(zvrf)) { + struct vrf *vrf = zvrf->vrf; + zlog_debug( - "%u: IF %u RA %s client %s - VRF mismatch, IF VRF %u", - zvrf_id(zvrf), ifindex, enable ? "enable" : "disable", + "%s:%u: IF %u RA %s client %s - VRF mismatch, IF VRF %u", + VRF_LOGNAME(vrf), zvrf_id(zvrf), ifindex, + enable ? "enable" : "disable", zebra_route_string(client->proto), ifp->vrf_id); return; } @@ -965,7 +1118,7 @@ static void zebra_interface_radv_set(ZAPI_HANDLER_ARGS, int enable) SET_FLAG(zif->rtadv.ra_configured, BGP_RA_CONFIGURED); ipv6_nd_suppress_ra_set(ifp, RA_ENABLE); if (ra_interval - && (ra_interval * 1000) < zif->rtadv.MaxRtrAdvInterval + && (ra_interval * 1000) < (unsigned int) zif->rtadv.MaxRtrAdvInterval && !CHECK_FLAG(zif->rtadv.ra_configured, VTY_RA_INTERVAL_CONFIGURED)) zif->rtadv.MaxRtrAdvInterval = ra_interval * 1000; @@ -982,6 +1135,51 @@ static void zebra_interface_radv_set(ZAPI_HANDLER_ARGS, int enable) return; } +/* + * send router lifetime value of zero in RAs on this interface since we're + * ceasing to advertise and want to let our neighbors know. + * RFC 4861 secion 6.2.5 + */ +void rtadv_stop_ra(struct interface *ifp) +{ + struct zebra_if *zif; + struct zebra_vrf *zvrf; + + zif = ifp->info; + zvrf = vrf_info_lookup(ifp->vrf_id); + + if (zif->rtadv.AdvSendAdvertisements) + rtadv_send_packet(rtadv_get_socket(zvrf), ifp, RA_SUPPRESS); +} + +/* + * Send router lifetime value of zero in RAs on all interfaces since we're + * ceasing to advertise globally and want to let all of our neighbors know + * RFC 4861 secion 6.2.5 + * + * Delete all ipv6 global prefixes added to the router advertisement prefix + * lists prior to ceasing. + */ +void rtadv_stop_ra_all(void) +{ + struct vrf *vrf; + struct interface *ifp; + struct listnode *node, *nnode; + struct zebra_if *zif; + struct rtadv_prefix *rprefix; + + RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) + FOR_ALL_INTERFACES (vrf, ifp) { + zif = ifp->info; + + for (ALL_LIST_ELEMENTS(zif->rtadv.AdvPrefixList, + node, nnode, rprefix)) + rtadv_prefix_reset(zif, rprefix); + + rtadv_stop_ra(ifp); + } +} + void zebra_interface_radv_disable(ZAPI_HANDLER_ARGS) { zebra_interface_radv_set(client, hdr, msg, zvrf, 0); @@ -991,6 +1189,145 @@ void zebra_interface_radv_enable(ZAPI_HANDLER_ARGS) zebra_interface_radv_set(client, hdr, msg, zvrf, 1); } +DEFUN (ipv6_nd_ra_fast_retrans, + ipv6_nd_ra_fast_retrans_cmd, + "ipv6 nd ra-fast-retrans", + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Fast retransmit of RA packets\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct zebra_if *zif = ifp->info; + + if (if_is_loopback(ifp) + || CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK)) { + vty_out(vty, + "Cannot configure IPv6 Router Advertisements on this interface\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + zif->rtadv.UseFastRexmit = true; + + return CMD_SUCCESS; +} + +DEFUN (no_ipv6_nd_ra_fast_retrans, + no_ipv6_nd_ra_fast_retrans_cmd, + "no ipv6 nd ra-fast-retrans", + NO_STR + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Fast retransmit of RA packets\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct zebra_if *zif = ifp->info; + + if (if_is_loopback(ifp) + || CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK)) { + vty_out(vty, + "Cannot configure IPv6 Router Advertisements on this interface\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + zif->rtadv.UseFastRexmit = false; + + return CMD_SUCCESS; +} + +DEFPY (ipv6_nd_ra_hop_limit, + ipv6_nd_ra_hop_limit_cmd, + "ipv6 nd ra-hop-limit (0-255)$hopcount", + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Advertisement Hop Limit\n" + "Advertisement Hop Limit in hops (default:64)\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct zebra_if *zif = ifp->info; + + if (if_is_loopback(ifp) + || CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK)) { + vty_out(vty, + "Cannot configure IPv6 Router Advertisements on this interface\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + zif->rtadv.AdvCurHopLimit = hopcount; + + return CMD_SUCCESS; +} + +DEFPY (no_ipv6_nd_ra_hop_limit, + no_ipv6_nd_ra_hop_limit_cmd, + "no ipv6 nd ra-hop-limit [(0-255)]", + NO_STR + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Advertisement Hop Limit\n" + "Advertisement Hop Limit in hops\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct zebra_if *zif = ifp->info; + + if (if_is_loopback(ifp) + || CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK)) { + vty_out(vty, + "Cannot configure IPv6 Router Advertisements on this interface\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + zif->rtadv.AdvCurHopLimit = RTADV_DEFAULT_HOPLIMIT; + + return CMD_SUCCESS; +} + +DEFPY (ipv6_nd_ra_retrans_interval, + ipv6_nd_ra_retrans_interval_cmd, + "ipv6 nd ra-retrans-interval (0-4294967295)$interval", + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Advertisement Retransmit Interval\n" + "Advertisement Retransmit Interval in msec\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct zebra_if *zif = ifp->info; + + if (if_is_loopback(ifp) + || CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK)) { + vty_out(vty, + "Cannot configure IPv6 Router Advertisements on loopback interface\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + zif->rtadv.AdvRetransTimer = interval; + + return CMD_SUCCESS; +} + +DEFPY (no_ipv6_nd_ra_retrans_interval, + no_ipv6_nd_ra_retrans_interval_cmd, + "no ipv6 nd ra-retrans-interval [(0-4294967295)]", + NO_STR + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Advertisement Retransmit Interval\n" + "Advertisement Retransmit Interval in msec\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct zebra_if *zif = ifp->info; + + if (if_is_loopback(ifp) + || CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK)) { + vty_out(vty, + "Cannot remove IPv6 Router Advertisements on loopback interface\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + zif->rtadv.AdvRetransTimer = 0; + + return CMD_SUCCESS; +} + DEFUN (ipv6_nd_suppress_ra, ipv6_nd_suppress_ra_cmd, "ipv6 nd suppress-ra", @@ -1051,6 +1388,9 @@ DEFUN (ipv6_nd_ra_interval_msec, VTY_DECLVAR_CONTEXT(interface, ifp); unsigned interval; struct zebra_if *zif = ifp->info; + struct zebra_vrf *zvrf; + + zvrf = vrf_info_lookup(ifp->vrf_id); interval = strtoul(argv[idx_number]->arg, NULL, 10); if ((zif->rtadv.AdvDefaultLifetime != -1 @@ -1061,10 +1401,10 @@ DEFUN (ipv6_nd_ra_interval_msec, } if (zif->rtadv.MaxRtrAdvInterval % 1000) - zrouter.rtadv.adv_msec_if_count--; + zvrf->rtadv.adv_msec_if_count--; if (interval % 1000) - zrouter.rtadv.adv_msec_if_count++; + zvrf->rtadv.adv_msec_if_count++; SET_FLAG(zif->rtadv.ra_configured, VTY_RA_INTERVAL_CONFIGURED); zif->rtadv.MaxRtrAdvInterval = interval; @@ -1086,6 +1426,9 @@ DEFUN (ipv6_nd_ra_interval, VTY_DECLVAR_CONTEXT(interface, ifp); unsigned interval; struct zebra_if *zif = ifp->info; + struct zebra_vrf *zvrf; + + zvrf = vrf_info_lookup(ifp->vrf_id); interval = strtoul(argv[idx_number]->arg, NULL, 10); if ((zif->rtadv.AdvDefaultLifetime != -1 @@ -1096,7 +1439,7 @@ DEFUN (ipv6_nd_ra_interval, } if (zif->rtadv.MaxRtrAdvInterval % 1000) - zrouter.rtadv.adv_msec_if_count--; + zvrf->rtadv.adv_msec_if_count--; /* convert to milliseconds */ interval = interval * 1000; @@ -1122,9 +1465,12 @@ DEFUN (no_ipv6_nd_ra_interval, { VTY_DECLVAR_CONTEXT(interface, ifp); struct zebra_if *zif = ifp->info; + struct zebra_vrf *zvrf = NULL; + + zvrf = vrf_info_lookup(ifp->vrf_id); if (zif->rtadv.MaxRtrAdvInterval % 1000) - zrouter.rtadv.adv_msec_if_count--; + zvrf->rtadv.adv_msec_if_count--; UNSET_FLAG(zif->rtadv.ra_configured, VTY_RA_INTERVAL_CONFIGURED); @@ -1470,6 +1816,7 @@ DEFUN (ipv6_nd_prefix, rp.AdvRouterAddressFlag = routeraddr; rp.AdvValidLifetime = RTADV_VALID_LIFETIME; rp.AdvPreferredLifetime = RTADV_PREFERRED_LIFETIME; + rp.AdvPrefixCreate = PREFIX_SRC_MANUAL; if (lifetimes) { rp.AdvValidLifetime = strmatch(lifetime, "infinite") @@ -1520,6 +1867,7 @@ DEFUN (no_ipv6_nd_prefix, return CMD_WARNING_CONFIG_FAILED; } apply_mask_ipv6(&rp.prefix); /* RFC4861 4.6.2 */ + rp.AdvPrefixCreate = PREFIX_SRC_MANUAL; ret = rtadv_prefix_reset(zebra_if, &rp); if (!ret) { @@ -1925,21 +2273,25 @@ static int nd_dump_vty(struct vty *vty, struct interface *ifp) " ND advertised reachable time is %d milliseconds\n", rtadv->AdvReachableTime); vty_out(vty, - " ND advertised retransmit interval is %d milliseconds\n", + " ND advertised retransmit interval is %u milliseconds\n", rtadv->AdvRetransTimer); + vty_out(vty, " ND advertised hop-count limit is %d hops\n", + rtadv->AdvCurHopLimit); vty_out(vty, " ND router advertisements sent: %d rcvd: %d\n", zif->ra_sent, zif->ra_rcvd); interval = rtadv->MaxRtrAdvInterval; if (interval % 1000) vty_out(vty, - " ND router advertisements are sent every " - "%d milliseconds\n", + " ND router advertisements are sent every %d milliseconds\n", interval); else vty_out(vty, - " ND router advertisements are sent every " - "%d seconds\n", + " ND router advertisements are sent every %d seconds\n", interval / 1000); + if (!rtadv->UseFastRexmit) + vty_out(vty, + " ND router advertisements do not use fast retransmit\n"); + if (rtadv->AdvDefaultLifetime != -1) vty_out(vty, " ND router advertisements live for %d seconds\n", @@ -1948,8 +2300,7 @@ static int nd_dump_vty(struct vty *vty, struct interface *ifp) vty_out(vty, " ND router advertisements lifetime tracks ra-interval\n"); vty_out(vty, - " ND router advertisement default router preference is " - "%s\n", + " ND router advertisement default router preference is %s\n", rtadv_pref_strs[rtadv->DefaultPreference]); if (rtadv->AdvManagedFlag) vty_out(vty, @@ -2011,6 +2362,17 @@ static int rtadv_config_write(struct vty *vty, struct interface *ifp) if (zif->rtadv.AdvIntervalOption) vty_out(vty, " ipv6 nd adv-interval-option\n"); + if (!zif->rtadv.UseFastRexmit) + vty_out(vty, " no ipv6 nd ra-fast-retrans\n"); + + if (zif->rtadv.AdvRetransTimer != 0) + vty_out(vty, " ipv6 nd ra-retrans-interval %u\n", + zif->rtadv.AdvRetransTimer); + + if (zif->rtadv.AdvCurHopLimit != RTADV_DEFAULT_HOPLIMIT) + vty_out(vty, " ipv6 nd ra-hop-limit %d\n", + zif->rtadv.AdvCurHopLimit); + if (zif->rtadv.AdvDefaultLifetime != -1) vty_out(vty, " ipv6 nd ra-lifetime %d\n", zif->rtadv.AdvDefaultLifetime); @@ -2044,29 +2406,34 @@ static int rtadv_config_write(struct vty *vty, struct interface *ifp) vty_out(vty, " ipv6 nd mtu %d\n", zif->rtadv.AdvLinkMTU); for (ALL_LIST_ELEMENTS_RO(zif->rtadv.AdvPrefixList, node, rprefix)) { - vty_out(vty, " ipv6 nd prefix %s", - prefix2str(&rprefix->prefix, buf, sizeof(buf))); - if ((rprefix->AdvValidLifetime != RTADV_VALID_LIFETIME) - || (rprefix->AdvPreferredLifetime - != RTADV_PREFERRED_LIFETIME)) { - if (rprefix->AdvValidLifetime == UINT32_MAX) - vty_out(vty, " infinite"); - else - vty_out(vty, " %u", rprefix->AdvValidLifetime); - if (rprefix->AdvPreferredLifetime == UINT32_MAX) - vty_out(vty, " infinite"); - else - vty_out(vty, " %u", - rprefix->AdvPreferredLifetime); + if ((rprefix->AdvPrefixCreate == PREFIX_SRC_MANUAL) + || (rprefix->AdvPrefixCreate == PREFIX_SRC_BOTH)) { + vty_out(vty, " ipv6 nd prefix %s", + prefix2str(&rprefix->prefix, buf, sizeof(buf))); + if ((rprefix->AdvValidLifetime != RTADV_VALID_LIFETIME) + || (rprefix->AdvPreferredLifetime + != RTADV_PREFERRED_LIFETIME)) { + if (rprefix->AdvValidLifetime == UINT32_MAX) + vty_out(vty, " infinite"); + else + vty_out(vty, " %u", + rprefix->AdvValidLifetime); + if (rprefix->AdvPreferredLifetime == UINT32_MAX) + vty_out(vty, " infinite"); + else + vty_out(vty, " %u", + rprefix->AdvPreferredLifetime); + } + if (!rprefix->AdvOnLinkFlag) + vty_out(vty, " off-link"); + if (!rprefix->AdvAutonomousFlag) + vty_out(vty, " no-autoconfig"); + if (rprefix->AdvRouterAddressFlag) + vty_out(vty, " router-address"); + vty_out(vty, "\n"); } - if (!rprefix->AdvOnLinkFlag) - vty_out(vty, " off-link"); - if (!rprefix->AdvAutonomousFlag) - vty_out(vty, " no-autoconfig"); - if (rprefix->AdvRouterAddressFlag) - vty_out(vty, " router-address"); - vty_out(vty, "\n"); } + for (ALL_LIST_ELEMENTS_RO(zif->rtadv.AdvRDNSSList, node, rdnss)) { char buf[INET6_ADDRSTRLEN]; @@ -2094,37 +2461,38 @@ static int rtadv_config_write(struct vty *vty, struct interface *ifp) } -static void rtadv_event(struct zebra_ns *zns, enum rtadv_event event, int val) +static void rtadv_event(struct zebra_vrf *zvrf, enum rtadv_event event, int val) { - struct rtadv *rtadv = &zrouter.rtadv; + struct rtadv *rtadv = &zvrf->rtadv; + + if (IS_ZEBRA_DEBUG_EVENT) { + struct vrf *vrf = zvrf->vrf; + + zlog_debug("%s(%s) with event: %d and val: %d", __func__, + VRF_LOGNAME(vrf), event, val); + } switch (event) { case RTADV_START: - thread_add_read(zrouter.master, rtadv_read, zns, val, + thread_add_read(zrouter.master, rtadv_read, zvrf, val, &rtadv->ra_read); - thread_add_event(zrouter.master, rtadv_timer, zns, 0, + thread_add_event(zrouter.master, rtadv_timer, zvrf, 0, &rtadv->ra_timer); break; case RTADV_STOP: - if (rtadv->ra_timer) { - thread_cancel(rtadv->ra_timer); - rtadv->ra_timer = NULL; - } - if (rtadv->ra_read) { - thread_cancel(rtadv->ra_read); - rtadv->ra_read = NULL; - } + THREAD_OFF(rtadv->ra_timer); + THREAD_OFF(rtadv->ra_read); break; case RTADV_TIMER: - thread_add_timer(zrouter.master, rtadv_timer, zns, val, + thread_add_timer(zrouter.master, rtadv_timer, zvrf, val, &rtadv->ra_timer); break; case RTADV_TIMER_MSEC: - thread_add_timer_msec(zrouter.master, rtadv_timer, zns, val, + thread_add_timer_msec(zrouter.master, rtadv_timer, zvrf, val, &rtadv->ra_timer); break; case RTADV_READ: - thread_add_read(zrouter.master, rtadv_read, zns, val, + thread_add_read(zrouter.master, rtadv_read, zvrf, val, &rtadv->ra_read); break; default: @@ -2133,21 +2501,37 @@ static void rtadv_event(struct zebra_ns *zns, enum rtadv_event event, int val) return; } -void rtadv_init(struct zebra_ns *zns) +void rtadv_init(struct zebra_vrf *zvrf) { - zrouter.rtadv.sock = rtadv_make_socket(zns->ns_id); + if (vrf_is_backend_netns()) { + zvrf->rtadv.sock = rtadv_make_socket(zvrf->zns->ns_id); + zrouter.rtadv_sock = -1; + } else { + zvrf->rtadv.sock = -1; + if (zrouter.rtadv_sock < 0) + zrouter.rtadv_sock = + rtadv_make_socket(zvrf->zns->ns_id); + } } -void rtadv_terminate(struct zebra_ns *zns) +void rtadv_vrf_terminate(struct zebra_vrf *zvrf) { - rtadv_event(zns, RTADV_STOP, 0); - if (zrouter.rtadv.sock >= 0) { - close(zrouter.rtadv.sock); - zrouter.rtadv.sock = -1; + rtadv_event(zvrf, RTADV_STOP, 0); + if (zvrf->rtadv.sock >= 0) { + close(zvrf->rtadv.sock); + zvrf->rtadv.sock = -1; } - zrouter.rtadv.adv_if_count = 0; - zrouter.rtadv.adv_msec_if_count = 0; + zvrf->rtadv.adv_if_count = 0; + zvrf->rtadv.adv_msec_if_count = 0; +} + +void rtadv_terminate(void) +{ + if (zrouter.rtadv_sock >= 0) { + close(zrouter.rtadv_sock); + zrouter.rtadv_sock = -1; + } } void rtadv_cmd_init(void) @@ -2155,6 +2539,12 @@ void rtadv_cmd_init(void) hook_register(zebra_if_extra_info, nd_dump_vty); hook_register(zebra_if_config_wr, rtadv_config_write); + install_element(INTERFACE_NODE, &ipv6_nd_ra_fast_retrans_cmd); + install_element(INTERFACE_NODE, &no_ipv6_nd_ra_fast_retrans_cmd); + install_element(INTERFACE_NODE, &ipv6_nd_ra_retrans_interval_cmd); + install_element(INTERFACE_NODE, &no_ipv6_nd_ra_retrans_interval_cmd); + install_element(INTERFACE_NODE, &ipv6_nd_ra_hop_limit_cmd); + install_element(INTERFACE_NODE, &no_ipv6_nd_ra_hop_limit_cmd); install_element(INTERFACE_NODE, &ipv6_nd_suppress_ra_cmd); install_element(INTERFACE_NODE, &no_ipv6_nd_suppress_ra_cmd); install_element(INTERFACE_NODE, &ipv6_nd_ra_interval_cmd); @@ -2201,17 +2591,20 @@ static int if_join_all_router(int sock, struct interface *ifp) mreq.ipv6mr_interface = ifp->ifindex; ret = setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, (char *)&mreq, - sizeof mreq); + sizeof(mreq)); if (ret < 0) flog_err_sys(EC_LIB_SOCKET, "%s(%u): Failed to join group, socket %u error %s", ifp->name, ifp->ifindex, sock, safe_strerror(errno)); - if (IS_ZEBRA_DEBUG_EVENT) + if (IS_ZEBRA_DEBUG_EVENT) { + struct vrf *vrf = vrf_lookup_by_id(ifp->vrf_id); + zlog_debug( - "%s(%u): Join All-Routers multicast group, socket %u", - ifp->name, ifp->ifindex, sock); + "%s(%s:%u): Join All-Routers multicast group, socket %u", + ifp->name, VRF_LOGNAME(vrf), ifp->ifindex, sock); + } return 0; } @@ -2227,27 +2620,32 @@ static int if_leave_all_router(int sock, struct interface *ifp) mreq.ipv6mr_interface = ifp->ifindex; ret = setsockopt(sock, IPPROTO_IPV6, IPV6_LEAVE_GROUP, (char *)&mreq, - sizeof mreq); - if (ret < 0) + sizeof(mreq)); + if (ret < 0) { + struct vrf *vrf = vrf_lookup_by_id(ifp->vrf_id); + flog_err_sys( EC_LIB_SOCKET, - "%s(%u): Failed to leave group, socket %u error %s", - ifp->name, ifp->ifindex, sock, safe_strerror(errno)); + "%s(%s:%u): Failed to leave group, socket %u error %s", + ifp->name, VRF_LOGNAME(vrf), ifp->ifindex, sock, + safe_strerror(errno)); + } + if (IS_ZEBRA_DEBUG_EVENT) { + struct vrf *vrf = vrf_lookup_by_id(ifp->vrf_id); - if (IS_ZEBRA_DEBUG_EVENT) zlog_debug( - "%s(%u): Leave All-Routers multicast group, socket %u", - ifp->name, ifp->ifindex, sock); - + "%s(%s:%u): Leave All-Routers multicast group, socket %u", + ifp->name, VRF_LOGNAME(vrf), ifp->ifindex, sock); + } return 0; } #else -void rtadv_init(struct zebra_ns *zns) +void rtadv_init(struct zebra_vrf *zvrf) { /* Empty.*/; } -void rtadv_terminate(struct zebra_ns *zns) +void rtadv_terminate(void) { /* Empty.*/; } @@ -2255,4 +2653,49 @@ void rtadv_cmd_init(void) { /* Empty.*/; } + +void rtadv_add_prefix(struct zebra_if *zif, const struct prefix_ipv6 *p) +{ + /* Empty.*/; +} + +void rtadv_delete_prefix(struct zebra_if *zif, const struct prefix *p) +{ + /* Empty.*/; +} + +void rtadv_stop_ra(struct interface *ifp) +{ + /* Empty.*/; +} + +void rtadv_stop_ra_all(void) +{ + /* Empty.*/; +} + +/* + * If the end user does not have RADV enabled we should + * handle this better + */ +void zebra_interface_radv_disable(ZAPI_HANDLER_ARGS) +{ + if (IS_ZEBRA_DEBUG_PACKET) + zlog_debug( + "Received %s command, but ZEBRA is not compiled with Router Advertisements on", + zserv_command_string(hdr->command)); + + return; +} + +void zebra_interface_radv_enable(ZAPI_HANDLER_ARGS) +{ + if (IS_ZEBRA_DEBUG_PACKET) + zlog_debug( + "Received %s command, but ZEBRA is not compiled with Router Advertisements on", + zserv_command_string(hdr->command)); + + return; +} + #endif /* HAVE_RTADV */ diff --git a/zebra/rtadv.h b/zebra/rtadv.h index 53c497fc09..d7a1ccfb29 100644 --- a/zebra/rtadv.h +++ b/zebra/rtadv.h @@ -37,6 +37,9 @@ struct rtadv_prefix { /* Prefix to be advertised. */ struct prefix_ipv6 prefix; + /* The prefix was manually/automatically defined. */ + int AdvPrefixCreate; + /* The value to be placed in the Valid Lifetime in the Prefix */ uint32_t AdvValidLifetime; #define RTADV_VALID_LIFETIME 2592000 @@ -59,6 +62,11 @@ struct rtadv_prefix { #endif }; +/* RFC4861 minimum delay between RAs */ +#ifndef MIN_DELAY_BETWEEN_RAS +#define MIN_DELAY_BETWEEN_RAS 3000 +#endif + /* RFC4584 Extension to Sockets API for Mobile IPv6 */ #ifndef ND_OPT_ADV_INTERVAL @@ -126,20 +134,34 @@ struct nd_opt_dnssl { /* DNS search list option [RFC8106 5.2] */ } __attribute__((__packed__)); #endif -extern const char *rtadv_pref_strs[]; - #endif /* HAVE_RTADV */ -typedef enum { +/* + * ipv6 nd prefixes can be manually defined, derived from the kernel interface + * configs or both. If both, manual flag/timer settings are used. + */ +enum ipv6_nd_prefix_source { + PREFIX_SRC_NONE = 0, + PREFIX_SRC_MANUAL, + PREFIX_SRC_AUTO, + PREFIX_SRC_BOTH, +}; + +enum ipv6_nd_suppress_ra_status { RA_ENABLE = 0, RA_SUPPRESS, -} ipv6_nd_suppress_ra_status; +}; -extern void rtadv_init(struct zebra_ns *); -extern void rtadv_terminate(struct zebra_ns *); +extern void rtadv_init(struct zebra_vrf *zvrf); +extern void rtadv_vrf_terminate(struct zebra_vrf *zvrf); +extern void rtadv_terminate(void); +extern void rtadv_stop_ra(struct interface *ifp); +extern void rtadv_stop_ra_all(void); extern void rtadv_cmd_init(void); extern void zebra_interface_radv_disable(ZAPI_HANDLER_ARGS); extern void zebra_interface_radv_enable(ZAPI_HANDLER_ARGS); +extern void rtadv_add_prefix(struct zebra_if *zif, const struct prefix_ipv6 *p); +extern void rtadv_delete_prefix(struct zebra_if *zif, const struct prefix *p); #ifdef __cplusplus } diff --git a/zebra/rtread_getmsg.c b/zebra/rtread_getmsg.c index 725bb63a0d..0a1fb82d95 100644 --- a/zebra/rtread_getmsg.c +++ b/zebra/rtread_getmsg.c @@ -102,7 +102,7 @@ static void handle_route_entry(mib2_ipRouteEntry_t *routeEntry) nh.gate.ipv4.s_addr = routeEntry->ipRouteNextHop; rib_add(AFI_IP, SAFI_UNICAST, VRF_DEFAULT, ZEBRA_ROUTE_KERNEL, 0, - zebra_flags, &prefix, NULL, &nh, 0, 0, 0, 0, 0); + zebra_flags, &prefix, NULL, &nh, 0, 0, 0, 0, 0, 0); } void route_read(struct zebra_ns *zns) @@ -230,8 +230,7 @@ void route_read(struct zebra_ns *zns) if (msgdata.len % sizeof(mib2_ipRouteEntry_t) != 0) { zlog_debug( - "getmsg(data) returned " - "msgdata.len = %d (%% sizeof (mib2_ipRouteEntry_t) != 0)", + "getmsg(data) returned msgdata.len = %d (%% sizeof(mib2_ipRouteEntry_t) != 0)", msgdata.len); goto exit; } diff --git a/zebra/rule_netlink.c b/zebra/rule_netlink.c index 8cc5b52b34..d6a34327a6 100644 --- a/zebra/rule_netlink.c +++ b/zebra/rule_netlink.c @@ -41,6 +41,7 @@ #include "zebra/rule_netlink.h" #include "zebra/zebra_pbr.h" #include "zebra/zebra_errors.h" +#include "zebra/zebra_dplane.h" /* definitions */ @@ -48,127 +49,167 @@ /* Private functions */ -/* Install or uninstall specified rule for a specific interface. - * Form netlink message and ship it. Currently, notify status after - * waiting for netlink status. + +/* + * netlink_rule_msg_encode + * + * Encodes netlink RTM_ADDRULE/RTM_DELRULE message to buffer buf of size buflen. + * + * Returns -1 on failure, 0 when the msg doesn't fit entirely in the buffer + * or the number of bytes written to buf. */ -static int netlink_rule_update(int cmd, struct zebra_pbr_rule *rule) +static ssize_t +netlink_rule_msg_encode(int cmd, const struct zebra_dplane_ctx *ctx, + uint32_t filter_bm, uint32_t priority, uint32_t table, + const struct prefix *src_ip, + const struct prefix *dst_ip, uint32_t fwmark, + uint8_t dsfield, void *buf, size_t buflen) { + uint8_t protocol = RTPROT_ZEBRA; int family; int bytelen; struct { struct nlmsghdr n; struct fib_rule_hdr frh; - char buf[NL_PKT_BUF_SIZE]; - } req; - struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT); - struct sockaddr_nl snl; + char buf[]; + } *req = buf; + + const char *ifname = dplane_ctx_rule_get_ifname(ctx); char buf1[PREFIX_STRLEN]; char buf2[PREFIX_STRLEN]; - memset(&req, 0, sizeof(req) - NL_PKT_BUF_SIZE); - family = PREFIX_FAMILY(&rule->rule.filter.src_ip); + if (buflen < sizeof(*req)) + return 0; + memset(req, 0, sizeof(*req)); + family = PREFIX_FAMILY(src_ip); bytelen = (family == AF_INET ? 4 : 16); - req.n.nlmsg_type = cmd; - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); - req.n.nlmsg_flags = NLM_F_REQUEST; - req.n.nlmsg_pid = zns->netlink_cmd.snl.nl_pid; + req->n.nlmsg_type = cmd; + req->n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + req->n.nlmsg_flags = NLM_F_REQUEST; - req.frh.family = family; - req.frh.action = FR_ACT_TO_TBL; + req->frh.family = family; + req->frh.action = FR_ACT_TO_TBL; + + if (!nl_attr_put(&req->n, buflen, FRA_PROTOCOL, &protocol, + sizeof(protocol))) + return 0; /* rule's pref # */ - addattr32(&req.n, sizeof(req), FRA_PRIORITY, rule->rule.priority); + if (!nl_attr_put32(&req->n, buflen, FRA_PRIORITY, priority)) + return 0; /* interface on which applied */ - if (rule->ifp) - addattr_l(&req.n, sizeof(req), FRA_IFNAME, rule->ifp->name, - strlen(rule->ifp->name) + 1); + if (!nl_attr_put(&req->n, buflen, FRA_IFNAME, ifname, + strlen(ifname) + 1)) + return 0; /* source IP, if specified */ - if (IS_RULE_FILTERING_ON_SRC_IP(rule)) { - req.frh.src_len = rule->rule.filter.src_ip.prefixlen; - addattr_l(&req.n, sizeof(req), FRA_SRC, - &rule->rule.filter.src_ip.u.prefix, bytelen); + if (filter_bm & PBR_FILTER_SRC_IP) { + req->frh.src_len = src_ip->prefixlen; + if (!nl_attr_put(&req->n, buflen, FRA_SRC, &src_ip->u.prefix, + bytelen)) + return 0; } + /* destination IP, if specified */ - if (IS_RULE_FILTERING_ON_DST_IP(rule)) { - req.frh.dst_len = rule->rule.filter.dst_ip.prefixlen; - addattr_l(&req.n, sizeof(req), FRA_DST, - &rule->rule.filter.dst_ip.u.prefix, bytelen); + if (filter_bm & PBR_FILTER_DST_IP) { + req->frh.dst_len = dst_ip->prefixlen; + if (!nl_attr_put(&req->n, buflen, FRA_DST, &dst_ip->u.prefix, + bytelen)) + return 0; } /* fwmark, if specified */ - if (IS_RULE_FILTERING_ON_FWMARK(rule)) { - addattr32(&req.n, sizeof(req), FRA_FWMARK, - rule->rule.filter.fwmark); + if (filter_bm & PBR_FILTER_FWMARK) { + if (!nl_attr_put32(&req->n, buflen, FRA_FWMARK, fwmark)) + return 0; } + /* dsfield, if specified */ + if (filter_bm & PBR_FILTER_DSFIELD) + req->frh.tos = dsfield; + /* Route table to use to forward, if filter criteria matches. */ - if (rule->rule.action.table < 256) - req.frh.table = rule->rule.action.table; + if (table < 256) + req->frh.table = table; else { - req.frh.table = RT_TABLE_UNSPEC; - addattr32(&req.n, sizeof(req), FRA_TABLE, - rule->rule.action.table); + req->frh.table = RT_TABLE_UNSPEC; + if (!nl_attr_put32(&req->n, buflen, FRA_TABLE, table)) + return 0; } if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug( - "Tx %s family %s IF %s(%u) Pref %u Fwmark %u Src %s Dst %s Table %u", + "Tx %s family %s IF %s Pref %u Fwmark %u Src %s Dst %s Table %u", nl_msg_type_to_str(cmd), nl_family_to_str(family), - rule->ifp ? rule->ifp->name : "Unknown", - rule->ifp ? rule->ifp->ifindex : 0, rule->rule.priority, - rule->rule.filter.fwmark, - prefix2str(&rule->rule.filter.src_ip, buf1, - sizeof(buf1)), - prefix2str(&rule->rule.filter.dst_ip, buf2, - sizeof(buf2)), - rule->rule.action.table); + ifname, priority, fwmark, + prefix2str(src_ip, buf1, sizeof(buf1)), + prefix2str(dst_ip, buf2, sizeof(buf2)), table); - /* Ship off the message. - * Note: Currently, netlink_talk() is a blocking call which returns - * back the status. - */ - memset(&snl, 0, sizeof(snl)); - snl.nl_family = AF_NETLINK; - return netlink_talk(netlink_talk_filter, &req.n, - &zns->netlink_cmd, zns, 0); + return NLMSG_ALIGN(req->n.nlmsg_len); } - -/* Public functions */ -/* - * Install specified rule for a specific interface. The preference is what - * goes in the rule to denote relative ordering; it may or may not be the - * same as the rule's user-defined sequence number. - */ -enum zebra_dplane_result kernel_add_pbr_rule(struct zebra_pbr_rule *rule) +static ssize_t netlink_rule_msg_encoder(struct zebra_dplane_ctx *ctx, void *buf, + size_t buflen) { - int ret = 0; - - ret = netlink_rule_update(RTM_NEWRULE, rule); - kernel_pbr_rule_add_del_status(rule, - (!ret) ? ZEBRA_DPLANE_INSTALL_SUCCESS - : ZEBRA_DPLANE_INSTALL_FAILURE); + int cmd = RTM_NEWRULE; + + if (dplane_ctx_get_op(ctx) == DPLANE_OP_RULE_DELETE) + cmd = RTM_DELRULE; + + return netlink_rule_msg_encode( + cmd, ctx, dplane_ctx_rule_get_filter_bm(ctx), + dplane_ctx_rule_get_priority(ctx), + dplane_ctx_rule_get_table(ctx), dplane_ctx_rule_get_src_ip(ctx), + dplane_ctx_rule_get_dst_ip(ctx), + dplane_ctx_rule_get_fwmark(ctx), + dplane_ctx_rule_get_dsfield(ctx), buf, buflen); +} - return ZEBRA_DPLANE_REQUEST_SUCCESS; +static ssize_t netlink_oldrule_msg_encoder(struct zebra_dplane_ctx *ctx, + void *buf, size_t buflen) +{ + return netlink_rule_msg_encode( + RTM_DELRULE, ctx, dplane_ctx_rule_get_old_filter_bm(ctx), + dplane_ctx_rule_get_old_priority(ctx), + dplane_ctx_rule_get_old_table(ctx), + dplane_ctx_rule_get_old_src_ip(ctx), + dplane_ctx_rule_get_old_dst_ip(ctx), + dplane_ctx_rule_get_old_fwmark(ctx), + dplane_ctx_rule_get_old_dsfield(ctx), buf, buflen); } -/* - * Uninstall specified rule for a specific interface. - */ -enum zebra_dplane_result kernel_del_pbr_rule(struct zebra_pbr_rule *rule) +/* Public functions */ + +enum netlink_msg_status +netlink_put_rule_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx) { - int ret = 0; + enum dplane_op_e op; + enum netlink_msg_status ret; + + op = dplane_ctx_get_op(ctx); + if (!(op == DPLANE_OP_RULE_ADD || op == DPLANE_OP_RULE_UPDATE + || op == DPLANE_OP_RULE_DELETE)) { + flog_err( + EC_ZEBRA_PBR_RULE_UPDATE, + "Context received for kernel rule update with incorrect OP code (%u)", + op); + return FRR_NETLINK_ERROR; + } - ret = netlink_rule_update(RTM_DELRULE, rule); - kernel_pbr_rule_add_del_status(rule, - (!ret) ? ZEBRA_DPLANE_DELETE_SUCCESS - : ZEBRA_DPLANE_DELETE_FAILURE); + ret = netlink_batch_add_msg(bth, ctx, netlink_rule_msg_encoder, false); - return ZEBRA_DPLANE_REQUEST_SUCCESS; + /** + * Delete the old one. + * + * Don't care about this result right? + */ + if (op == DPLANE_OP_RULE_UPDATE) + netlink_batch_add_msg(bth, ctx, netlink_oldrule_msg_encoder, + true); + + return ret; } /* @@ -177,6 +218,10 @@ enum zebra_dplane_result kernel_del_pbr_rule(struct zebra_pbr_rule *rule) * DELs are notified up, if other attributes indicate it may be a * notification of interest. The expectation is that if this corresponds * to a PBR rule added by FRR, it will be readded. + * + * If startup and we see a rule we created, delete it as its leftover + * from a previous instance and should have been removed on shutdown. + * */ int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) { @@ -188,25 +233,32 @@ int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) struct zebra_pbr_rule rule = {}; char buf1[PREFIX_STRLEN]; char buf2[PREFIX_STRLEN]; + uint8_t proto = 0; /* Basic validation followed by extracting attributes. */ if (h->nlmsg_type != RTM_NEWRULE && h->nlmsg_type != RTM_DELRULE) return 0; - /* TBD */ - if (h->nlmsg_type == RTM_NEWRULE) - return 0; - len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct fib_rule_hdr)); if (len < 0) { - zlog_err("%s: Message received from netlink is of a broken size: %d %zu", - __PRETTY_FUNCTION__, h->nlmsg_len, - (size_t)NLMSG_LENGTH(sizeof(struct fib_rule_hdr))); + zlog_err( + "%s: Message received from netlink is of a broken size: %d %zu", + __func__, h->nlmsg_len, + (size_t)NLMSG_LENGTH(sizeof(struct fib_rule_hdr))); return -1; } frh = NLMSG_DATA(h); + if (frh->family != AF_INET && frh->family != AF_INET6) { + if (frh->family == RTNL_FAMILY_IPMR + || frh->family == RTNL_FAMILY_IP6MR) { + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug( + "Received rule netlink that we are ignoring for family %u, rule change: %u", + frh->family, h->nlmsg_type); + return 0; + } flog_warn( EC_ZEBRA_NETLINK_INVALID_AF, "Invalid address family: %u received from kernel rule change: %u", @@ -219,17 +271,6 @@ int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) memset(tb, 0, sizeof(tb)); netlink_parse_rtattr(tb, FRA_MAX, RTM_RTA(frh), len); - /* TBD: We don't care about rules not specifying an IIF. */ - if (tb[FRA_IFNAME] == NULL) - return 0; - - /* If we don't know the interface, we don't care. */ - ifname = (char *)RTA_DATA(tb[FRA_IFNAME]); - zns = zebra_ns_lookup(ns_id); - rule.ifp = if_lookup_by_name_per_ns(zns, ifname); - if (!rule.ifp) - return 0; - if (tb[FRA_PRIORITY]) rule.rule.priority = *(uint32_t *)RTA_DATA(tb[FRA_PRIORITY]); @@ -241,6 +282,7 @@ int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) memcpy(&rule.rule.filter.src_ip.u.prefix6, RTA_DATA(tb[FRA_SRC]), 16); rule.rule.filter.src_ip.prefixlen = frh->src_len; + rule.rule.filter.src_ip.family = frh->family; rule.rule.filter.filter_bm |= PBR_FILTER_SRC_IP; } @@ -252,6 +294,7 @@ int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) memcpy(&rule.rule.filter.dst_ip.u.prefix6, RTA_DATA(tb[FRA_DST]), 16); rule.rule.filter.dst_ip.prefixlen = frh->dst_len; + rule.rule.filter.dst_ip.family = frh->family; rule.rule.filter.filter_bm |= PBR_FILTER_DST_IP; } @@ -260,12 +303,57 @@ int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) else rule.rule.action.table = frh->table; + /* TBD: We don't care about rules not specifying an IIF. */ + if (tb[FRA_IFNAME] == NULL) + return 0; + + if (tb[FRA_PROTOCOL]) + proto = *(uint8_t *)RTA_DATA(tb[FRA_PROTOCOL]); + + ifname = (char *)RTA_DATA(tb[FRA_IFNAME]); + strlcpy(rule.ifname, ifname, sizeof(rule.ifname)); + + if (h->nlmsg_type == RTM_NEWRULE) { + /* + * If we see a rule at startup we created, delete it now. + * It should have been flushed on a previous shutdown. + */ + if (startup && proto == RTPROT_ZEBRA) { + enum zebra_dplane_result ret; + + ret = dplane_pbr_rule_delete(&rule); + + zlog_debug( + "%s: %s leftover rule: family %s IF %s Pref %u Src %s Dst %s Table %u", + __func__, + ((ret == ZEBRA_DPLANE_REQUEST_FAILURE) + ? "Failed to remove" + : "Removed"), + nl_family_to_str(frh->family), rule.ifname, + rule.rule.priority, + prefix2str(&rule.rule.filter.src_ip, buf1, + sizeof(buf1)), + prefix2str(&rule.rule.filter.dst_ip, buf2, + sizeof(buf2)), + rule.rule.action.table); + } + + /* TBD */ + return 0; + } + + zns = zebra_ns_lookup(ns_id); + + /* If we don't know the interface, we don't care. */ + if (!if_lookup_by_name_per_ns(zns, ifname)) + return 0; + if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug( - "Rx %s family %s IF %s(%u) Pref %u Src %s Dst %s Table %u", + "Rx %s family %s IF %s Pref %u Src %s Dst %s Table %u", nl_msg_type_to_str(h->nlmsg_type), - nl_family_to_str(frh->family), rule.ifp->name, - rule.ifp->ifindex, rule.rule.priority, + nl_family_to_str(frh->family), rule.ifname, + rule.rule.priority, prefix2str(&rule.rule.filter.src_ip, buf1, sizeof(buf1)), prefix2str(&rule.rule.filter.dst_ip, buf2, @@ -275,13 +363,52 @@ int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) return kernel_pbr_rule_del(&rule); } +/* + * Request rules from the kernel + */ +static int netlink_request_rules(struct zebra_ns *zns, int family, int type) +{ + struct { + struct nlmsghdr n; + struct fib_rule_hdr frh; + char buf[NL_PKT_BUF_SIZE]; + } req; + + memset(&req, 0, sizeof(req)); + req.n.nlmsg_type = type; + req.n.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST; + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct fib_rule_hdr)); + req.frh.family = family; + + return netlink_request(&zns->netlink_cmd, &req); +} + /* * Get to know existing PBR rules in the kernel - typically called at startup. - * TBD. */ int netlink_rules_read(struct zebra_ns *zns) { - return 0; + int ret; + struct zebra_dplane_info dp_info; + + zebra_dplane_info_from_zns(&dp_info, zns, true); + + ret = netlink_request_rules(zns, AF_INET, RTM_GETRULE); + if (ret < 0) + return ret; + + ret = netlink_parse_info(netlink_rule_change, &zns->netlink_cmd, + &dp_info, 0, 1); + if (ret < 0) + return ret; + + ret = netlink_request_rules(zns, AF_INET6, RTM_GETRULE); + if (ret < 0) + return ret; + + ret = netlink_parse_info(netlink_rule_change, &zns->netlink_cmd, + &dp_info, 0, 1); + return ret; } #endif /* HAVE_NETLINK */ diff --git a/zebra/rule_netlink.h b/zebra/rule_netlink.h index 8c4741dc06..cf4d978e78 100644 --- a/zebra/rule_netlink.h +++ b/zebra/rule_netlink.h @@ -40,6 +40,9 @@ extern int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup); */ extern int netlink_rules_read(struct zebra_ns *zns); +extern enum netlink_msg_status +netlink_put_rule_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx); + #ifdef __cplusplus } #endif diff --git a/zebra/rule_socket.c b/zebra/rule_socket.c index c5660abf3a..e629017bdf 100644 --- a/zebra/rule_socket.c +++ b/zebra/rule_socket.c @@ -43,17 +43,10 @@ #include "zebra/zebra_pbr.h" #include "zebra/zebra_errors.h" -enum zebra_dplane_result kernel_add_pbr_rule(struct zebra_pbr_rule *rule) +enum zebra_dplane_result kernel_pbr_rule_update(struct zebra_dplane_ctx *ctx) { flog_err(EC_LIB_UNAVAILABLE, "%s not Implemented for this platform", - __PRETTY_FUNCTION__); - return ZEBRA_DPLANE_REQUEST_FAILURE; -} - -enum zebra_dplane_result kernel_del_pbr_rule(struct zebra_pbr_rule *rule) -{ - flog_err(EC_LIB_UNAVAILABLE, "%s not Implemented for this platform", - __PRETTY_FUNCTION__); + __func__); return ZEBRA_DPLANE_REQUEST_FAILURE; } diff --git a/zebra/sample_plugin.c b/zebra/sample_plugin.c new file mode 100644 index 0000000000..c96a86cc73 --- /dev/null +++ b/zebra/sample_plugin.c @@ -0,0 +1,134 @@ +/* + * Sample plugin for the FRR zebra dataplane. + * + * Copyright (c) 2019 Volta Networks, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * Should be possible to build this plugin using this sort of command: + * + * gcc -I ~/work/frr/ -I ~/work/frr/lib -I ~/work/frr/zebra \ + * -g -O0 -o sample_plugin.so -shared -fPIC sample_plugin.c + * + * where 'frr' is a configured and built frr sandbox. + * + * Run zebra with '-M /path/to/sample_plugin.so' to load the module. + */ + +#include "config.h" /* Include this explicitly */ +#include "lib/zebra.h" +#include "lib/libfrr.h" +#include "zebra/zebra_dplane.h" +#include "zebra/debug.h" + +static const char *plugin_name = "SAMPLE"; + +static struct zebra_dplane_provider *prov_p; + +/* + * Startup/init callback, called from the dataplane. + */ +static int sample_start(struct zebra_dplane_provider *prov) +{ + /* Nothing special to do - we don't allocate anything. */ + return 0; +} + + +/* + * Shutdown/cleanup callback, called from the dataplane pthread. + */ +static int sample_fini(struct zebra_dplane_provider *prov, bool early) +{ + /* Nothing special to do. */ + return 0; +} + +/* + * Callback from the dataplane to process incoming work; this runs in the + * dplane pthread. + */ +static int sample_process(struct zebra_dplane_provider *prov) +{ + int counter, limit; + struct zebra_dplane_ctx *ctx; + + limit = dplane_provider_get_work_limit(prov_p); + + /* Respect the configured limit on the amount of work to do in + * any one call. + */ + for (counter = 0; counter < limit; counter++) { + ctx = dplane_provider_dequeue_in_ctx(prov_p); + if (!ctx) + break; + + /* Just set 'success' status and return to the dataplane */ + dplane_ctx_set_status(ctx, ZEBRA_DPLANE_REQUEST_SUCCESS); + dplane_provider_enqueue_out_ctx(prov_p, ctx); + } + + return 0; +} + +/* + * Init entry point called during zebra startup. This is registered during + * module init. + */ +static int init_sample_plugin(struct thread_master *tm) +{ + int ret; + struct zebra_dplane_provider *prov = NULL; + + /* Note that we don't use or store the thread_master 'tm'. We + * don't use the zebra main pthread: our plugin code will run in + * the zebra dataplane pthread context. + */ + + /* Register the plugin with the dataplane infrastructure. We + * register to be called before the kernel, and we register + * our init, process work, and shutdown callbacks. + */ + ret = dplane_provider_register(plugin_name, DPLANE_PRIO_PRE_KERNEL, + DPLANE_PROV_FLAGS_DEFAULT, + sample_start, + sample_process, + sample_fini, + NULL, + &prov_p); + + if (IS_ZEBRA_DEBUG_DPLANE) + zlog_debug("sample plugin register => %d", ret); + + return 0; +} + +/* + * Base FRR loadable module info: basic info including module entry-point. + */ +static int module_init(void) +{ + hook_register(frr_late_init, init_sample_plugin); + return 0; +} + +FRR_MODULE_SETUP( + .name = "dplane_sample", + .version = "0.0.1", + .description = "Dataplane Sample Plugin", + .init = module_init, + ) diff --git a/zebra/subdir.am b/zebra/subdir.am index 1e36d020a3..a6ef1537c0 100644 --- a/zebra/subdir.am +++ b/zebra/subdir.am @@ -6,22 +6,24 @@ if ZEBRA sbin_PROGRAMS += zebra/zebra dist_examples_DATA += zebra/zebra.conf.sample vtysh_scan += \ - $(top_srcdir)/zebra/debug.c \ - $(top_srcdir)/zebra/interface.c \ - $(top_srcdir)/zebra/router-id.c \ - $(top_srcdir)/zebra/rtadv.c \ - $(top_srcdir)/zebra/zebra_mlag.c \ - $(top_srcdir)/zebra/zebra_mpls_vty.c \ - $(top_srcdir)/zebra/zebra_ptm.c \ - $(top_srcdir)/zebra/zebra_pw.c \ - $(top_srcdir)/zebra/zebra_routemap.c \ - $(top_srcdir)/zebra/zebra_vty.c \ - $(top_srcdir)/zebra/zserv.c \ + zebra/debug.c \ + zebra/interface.c \ + zebra/router-id.c \ + zebra/rtadv.c \ + zebra/zebra_gr.c \ + zebra/zebra_mlag_vty.c \ + zebra/zebra_evpn_mh.c \ + zebra/zebra_mpls_vty.c \ + zebra/zebra_ptm.c \ + zebra/zebra_pw.c \ + zebra/zebra_routemap.c \ + zebra/zebra_vty.c \ + zebra/zserv.c \ # end # can be loaded as DSO - always include for vtysh -vtysh_scan += $(top_srcdir)/zebra/irdp_interface.c -vtysh_scan += $(top_srcdir)/zebra/zebra_fpm.c +vtysh_scan += zebra/irdp_interface.c +vtysh_scan += zebra/zebra_fpm.c if IRDP module_LTLIBRARIES += zebra/zebra_irdp.la @@ -32,12 +34,19 @@ endif if FPM module_LTLIBRARIES += zebra/zebra_fpm.la endif +if LINUX +module_LTLIBRARIES += zebra/zebra_cumulus_mlag.la +endif -man8 += $(MANBUILD)/zebra.8 +man8 += $(MANBUILD)/frr-zebra.8 ## endif ZEBRA endif zebra_zebra_LDADD = lib/libfrr.la $(LIBCAP) +if HAVE_PROTOBUF3 +zebra_zebra_LDADD += mlag/libmlag_pb.la $(PROTOBUF_C_LIBS) +zebra/zebra_mlag.$(OBJEXT): mlag/mlag.pb-c.h +endif zebra_zebra_SOURCES = \ zebra/connected.c \ zebra/debug.c \ @@ -65,17 +74,33 @@ zebra_zebra_SOURCES = \ zebra/rtread_sysctl.c \ zebra/rule_netlink.c \ zebra/rule_socket.c \ - zebra/zebra_mlag.c \ + zebra/table_manager.c \ + zebra/zapi_msg.c \ + zebra/zebra_dplane.c \ + zebra/zebra_errors.c \ + zebra/zebra_gr.c \ zebra/zebra_l2.c \ + zebra/zebra_evpn.c \ + zebra/zebra_evpn_mac.c \ + zebra/zebra_evpn_neigh.c \ + zebra/zebra_mlag.c \ + zebra/zebra_mlag_vty.c \ zebra/zebra_memory.c \ - zebra/zebra_dplane.c \ zebra/zebra_mpls.c \ zebra/zebra_mpls_netlink.c \ zebra/zebra_mpls_openbsd.c \ zebra/zebra_mpls_null.c \ zebra/zebra_mpls_vty.c \ zebra/zebra_mroute.c \ + zebra/zebra_nb.c \ + zebra/zebra_nb_config.c \ + zebra/zebra_nb_rpcs.c \ + zebra/zebra_nb_state.c \ + zebra/zebra_netns_id.c \ + zebra/zebra_netns_notify.c \ + zebra/zebra_nhg.c \ zebra/zebra_ns.c \ + zebra/zebra_opaque.c \ zebra/zebra_pbr.c \ zebra/zebra_ptm.c \ zebra/zebra_ptm_redistribute.c \ @@ -84,30 +109,23 @@ zebra_zebra_SOURCES = \ zebra/zebra_router.c \ zebra/zebra_rnh.c \ zebra/zebra_routemap.c \ + zebra/zebra_srte.c \ zebra/zebra_vrf.c \ zebra/zebra_vty.c \ zebra/zebra_vxlan.c \ + zebra/zebra_evpn_mh.c \ zebra/zserv.c \ - zebra/zebra_netns_id.c \ - zebra/zebra_netns_notify.c \ - zebra/table_manager.c \ - zebra/zapi_msg.c \ - zebra/zebra_errors.c \ # end -zebra/debug_clippy.c: $(CLIPPY_DEPS) -zebra/debug.$(OBJEXT): zebra/debug_clippy.c - -zebra/zebra_mlag_clippy.c: $(CLIPPY_DEPS) -zebra/zebra_mlag.$(OBJEXT): zebra/zebra_mlag_clippy.c - -zebra/zebra_vty_clippy.c: $(CLIPPY_DEPS) -zebra/interface_clippy.c: $(CLIPPY_DEPS) -zebra/interface.$(OBJEXT): zebra/interface_clippy.c -zebra/zebra_vty.$(OBJEXT): zebra/zebra_vty_clippy.c - -zebra/zebra_routemap_clippy.c: $(CLIPPY_DEPS) -zebra/zebra_routemap.$(OBJEXT): zebra/zebra_routemap_clippy.c +clippy_scan += \ + zebra/debug.c \ + zebra/interface.c \ + zebra/rtadv.c \ + zebra/zebra_evpn_mh.c \ + zebra/zebra_mlag_vty.c \ + zebra/zebra_routemap.c \ + zebra/zebra_vty.c \ + # end noinst_HEADERS += \ zebra/connected.h \ @@ -128,14 +146,28 @@ noinst_HEADERS += \ zebra/rt_netlink.h \ zebra/rtadv.h \ zebra/rule_netlink.h \ - zebra/zebra_mlag.h \ + zebra/table_manager.h \ + zebra/zapi_msg.h \ + zebra/zebra_dplane.h \ + zebra/zebra_errors.h \ + zebra/zebra_evpn.h \ + zebra/zebra_evpn_mac.h \ + zebra/zebra_evpn_neigh.h \ + zebra/zebra_evpn_vxlan.h \ zebra/zebra_fpm_private.h \ zebra/zebra_l2.h \ - zebra/zebra_dplane.h \ zebra/zebra_memory.h \ + zebra/zebra_mlag.h \ + zebra/zebra_mlag_vty.h \ zebra/zebra_mpls.h \ zebra/zebra_mroute.h \ + zebra/zebra_nb.h \ + zebra/zebra_netns_id.h \ + zebra/zebra_netns_notify.h \ + zebra/zebra_nhg.h \ + zebra/zebra_nhg_private.h \ zebra/zebra_ns.h \ + zebra/zebra_opaque.h \ zebra/zebra_pbr.h \ zebra/zebra_ptm.h \ zebra/zebra_ptm_redistribute.h \ @@ -143,15 +175,12 @@ noinst_HEADERS += \ zebra/zebra_rnh.h \ zebra/zebra_routemap.h \ zebra/zebra_router.h \ + zebra/zebra_srte.h \ zebra/zebra_vrf.h \ zebra/zebra_vxlan.h \ zebra/zebra_vxlan_private.h \ + zebra/zebra_evpn_mh.h \ zebra/zserv.h \ - zebra/zebra_netns_id.h \ - zebra/zebra_netns_notify.h \ - zebra/table_manager.h \ - zebra/zapi_msg.h \ - zebra/zebra_errors.h \ # end zebra_zebra_irdp_la_SOURCES = \ @@ -173,7 +202,26 @@ zebra_zebra_fpm_la_SOURCES += zebra/zebra_fpm_netlink.c if HAVE_PROTOBUF zebra_zebra_fpm_la_LIBADD += fpm/libfrrfpm_pb.la qpb/libfrr_pb.la $(PROTOBUF_C_LIBS) zebra_zebra_fpm_la_SOURCES += zebra/zebra_fpm_protobuf.c +zebra/zebra_fpm_protobuf.lo: fpm/fpm.pb-c.h qpb/qpb.pb-c.h if DEV_BUILD zebra_zebra_fpm_la_SOURCES += zebra/zebra_fpm_dt.c +zebra/zebra_fpm_dt.lo: fpm/fpm.pb-c.h qpb/qpb.pb-c.h endif endif + +nodist_zebra_zebra_SOURCES = \ + yang/frr-zebra.yang.c \ + # end + +zebra_zebra_cumulus_mlag_la_SOURCES = zebra/zebra_mlag_private.c +zebra_zebra_cumulus_mlag_la_LDFLAGS = -avoid-version -module -shared -export-dynamic + +if LINUX +module_LTLIBRARIES += zebra/dplane_fpm_nl.la + +zebra_dplane_fpm_nl_la_SOURCES = zebra/dplane_fpm_nl.c +zebra_dplane_fpm_nl_la_LDFLAGS = -avoid-version -module -shared -export-dynamic +zebra_dplane_fpm_nl_la_LIBADD = + +vtysh_scan += $(top_srcdir)/zebra/dplane_fpm_nl.c +endif diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index f31fb53a34..b4127ae1ec 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -24,23 +24,17 @@ #include #include "lib/prefix.h" -#include "lib/command.h" -#include "lib/if.h" -#include "lib/thread.h" #include "lib/stream.h" #include "lib/memory.h" #include "lib/table.h" #include "lib/network.h" -#include "lib/sockunion.h" #include "lib/log.h" #include "lib/zclient.h" #include "lib/privs.h" -#include "lib/network.h" -#include "lib/buffer.h" #include "lib/nexthop.h" #include "lib/vrf.h" #include "lib/libfrr.h" -#include "lib/sockopt.h" +#include "lib/lib_errors.h" #include "zebra/zebra_router.h" #include "zebra/rib.h" @@ -51,26 +45,30 @@ #include "zebra/redistribute.h" #include "zebra/debug.h" #include "zebra/zebra_rnh.h" -#include "zebra/rt_netlink.h" #include "zebra/interface.h" #include "zebra/zebra_ptm.h" #include "zebra/rtadv.h" #include "zebra/zebra_mpls.h" #include "zebra/zebra_mroute.h" -#include "zebra/label_manager.h" #include "zebra/zebra_vxlan.h" +#include "zebra/zebra_evpn_mh.h" #include "zebra/rt.h" #include "zebra/zebra_pbr.h" #include "zebra/table_manager.h" #include "zebra/zapi_msg.h" #include "zebra/zebra_errors.h" #include "zebra/zebra_mlag.h" +#include "zebra/connected.h" +#include "zebra/zebra_opaque.h" +#include "zebra/zebra_srte.h" /* Encoding helpers -------------------------------------------------------- */ static void zserv_encode_interface(struct stream *s, struct interface *ifp) { /* Interface information. */ + struct zebra_if *zif = ifp->info; + stream_put(s, ifp->name, INTERFACE_NAMSIZ); stream_putl(s, ifp->ifindex); stream_putc(s, ifp->status); @@ -82,6 +80,7 @@ static void zserv_encode_interface(struct stream *s, struct interface *ifp) stream_putl(s, ifp->mtu); stream_putl(s, ifp->mtu6); stream_putl(s, ifp->bandwidth); + stream_putl(s, zif->link_ifindex); stream_putl(s, ifp->ll_type); stream_putl(s, ifp->hw_addr_len); if (ifp->hw_addr_len) @@ -103,6 +102,7 @@ static void zserv_encode_vrf(struct stream *s, struct zebra_vrf *zvrf) struct vrf_data data; const char *netns_name = zvrf_ns_name(zvrf); + memset(&data, 0, sizeof(data)); data.l.table_id = zvrf->table_id; if (netns_name) @@ -145,6 +145,25 @@ static int zserv_encode_nexthop(struct stream *s, struct nexthop *nexthop) return 1; } +/* + * Zebra error addition adds error type. + * + * + * 0 1 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | enum zebra_error_types | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + */ +static void zserv_encode_error(struct stream *s, enum zebra_error_types error) +{ + stream_put(s, &error, sizeof(error)); + + /* Write packet size. */ + stream_putw_at(s, 0, stream_get_endp(s)); +} + /* Send handlers ----------------------------------------------------------- */ /* Interface is added. Send ZEBRA_INTERFACE_ADD to client. */ @@ -360,9 +379,14 @@ static void zebra_interface_nbr_address_add_update(struct interface *ifp, p->prefixlen, ifc->ifp->name); } - for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) + for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) { + /* Do not send unsolicited messages to synchronous clients. */ + if (client->synchronous) + continue; + zsend_interface_nbr_address(ZEBRA_INTERFACE_NBR_ADDRESS_ADD, client, ifp, ifc); + } } /* Interface address deletion. */ @@ -384,9 +408,14 @@ static void zebra_interface_nbr_address_delete_update(struct interface *ifp, p->prefixlen, ifc->ifp->name); } - for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) + for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) { + /* Do not send unsolicited messages to synchronous clients. */ + if (client->synchronous) + continue; + zsend_interface_nbr_address(ZEBRA_INTERFACE_NBR_ADDRESS_DELETE, client, ifp, ifc); + } } /* Send addresses on interface to client */ @@ -514,13 +543,16 @@ int zsend_interface_update(int cmd, struct zserv *client, struct interface *ifp) int zsend_redistribute_route(int cmd, struct zserv *client, const struct prefix *p, - const struct prefix *src_p, struct route_entry *re) + const struct prefix *src_p, + const struct route_entry *re) { struct zapi_route api; struct zapi_nexthop *api_nh; struct nexthop *nexthop; - int count = 0; + uint8_t count = 0; afi_t afi; + size_t stream_size = + MAX(ZEBRA_MAX_PACKET_SIZ, sizeof(struct zapi_route)); memset(&api, 0, sizeof(api)); api.vrf_id = re->vrf_id; @@ -554,18 +586,15 @@ int zsend_redistribute_route(int cmd, struct zserv *client, memcpy(&api.src_prefix, src_p, sizeof(api.src_prefix)); } - /* Nexthops. */ - if (re->nexthop_active_num) { - SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP); - api.nexthop_num = re->nexthop_active_num; - } - for (nexthop = re->ng.nexthop; nexthop; nexthop = nexthop->next) { + for (nexthop = re->nhe->nhg.nexthop; + nexthop; nexthop = nexthop->next) { if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) continue; api_nh = &api.nexthops[count]; api_nh->vrf_id = nexthop->vrf_id; api_nh->type = nexthop->type; + api_nh->weight = nexthop->weight; switch (nexthop->type) { case NEXTHOP_TYPE_BLACKHOLE: api_nh->bh_type = nexthop->bh_type; @@ -590,6 +619,12 @@ int zsend_redistribute_route(int cmd, struct zserv *client, count++; } + /* Nexthops. */ + if (count) { + SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP); + api.nexthop_num = count; + } + /* Attributes. */ SET_FLAG(api.message, ZAPI_MESSAGE_DISTANCE); api.distance = re->distance; @@ -602,7 +637,7 @@ int zsend_redistribute_route(int cmd, struct zserv *client, SET_FLAG(api.message, ZAPI_MESSAGE_MTU); api.mtu = re->mtu; - struct stream *s = stream_new(ZEBRA_MAX_PACKET_SIZ); + struct stream *s = stream_new(stream_size); /* Encode route and send. */ if (zapi_route_encode(cmd, s, &api) < 0) { @@ -660,7 +695,8 @@ static int zsend_ipv4_nexthop_lookup_mrib(struct zserv *client, * nexthop we are looking up. Therefore, we will just iterate * over the top chain of nexthops. */ - for (nexthop = re->ng.nexthop; nexthop; nexthop = nexthop->next) + for (nexthop = re->nhe->nhg.nexthop; nexthop; + nexthop = nexthop->next) if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) num += zserv_encode_nexthop(s, nexthop); @@ -696,9 +732,10 @@ static int route_notify_internal(const struct prefix *p, int type, char buff[PREFIX_STRLEN]; zlog_debug( - "Not Notifying Owner: %u about prefix %s(%u) %d vrf: %u", - type, prefix2str(p, buff, sizeof(buff)), - table_id, note, vrf_id); + "Not Notifying Owner: %s about prefix %s(%u) %d vrf: %u", + zebra_route_string(type), + prefix2str(p, buff, sizeof(buff)), table_id, + note, vrf_id); } return 0; } @@ -706,9 +743,10 @@ static int route_notify_internal(const struct prefix *p, int type, if (IS_ZEBRA_DEBUG_PACKET) { char buff[PREFIX_STRLEN]; - zlog_debug("Notifying Owner: %u about prefix %s(%u) %d vrf: %u", - type, prefix2str(p, buff, sizeof(buff)), - table_id, note, vrf_id); + zlog_debug("Notifying Owner: %s about prefix %s(%u) %d vrf: %u", + zebra_route_string(type), + prefix2str(p, buff, sizeof(buff)), table_id, note, + vrf_id); } s = stream_new(ZEBRA_MAX_PACKET_SIZ); @@ -752,7 +790,7 @@ int zsend_route_notify_owner_ctx(const struct zebra_dplane_ctx *ctx, note)); } -void zsend_rule_notify_owner(struct zebra_pbr_rule *rule, +void zsend_rule_notify_owner(const struct zebra_dplane_ctx *ctx, enum zapi_rule_notify_owner note) { struct listnode *node; @@ -760,11 +798,11 @@ void zsend_rule_notify_owner(struct zebra_pbr_rule *rule, struct stream *s; if (IS_ZEBRA_DEBUG_PACKET) - zlog_debug("%s: Notifying %u", __PRETTY_FUNCTION__, - rule->rule.unique); + zlog_debug("%s: Notifying %u", __func__, + dplane_ctx_rule_get_unique(ctx)); for (ALL_LIST_ELEMENTS_RO(zrouter.client_list, node, client)) { - if (rule->sock == client->sock) + if (dplane_ctx_rule_get_sock(ctx) == client->sock) break; } @@ -775,13 +813,10 @@ void zsend_rule_notify_owner(struct zebra_pbr_rule *rule, zclient_create_header(s, ZEBRA_RULE_NOTIFY_OWNER, VRF_DEFAULT); stream_put(s, ¬e, sizeof(note)); - stream_putl(s, rule->rule.seq); - stream_putl(s, rule->rule.priority); - stream_putl(s, rule->rule.unique); - if (rule->ifp) - stream_putl(s, rule->ifp->ifindex); - else - stream_putl(s, 0); + stream_putl(s, dplane_ctx_rule_get_seq(ctx)); + stream_putl(s, dplane_ctx_rule_get_priority(ctx)); + stream_putl(s, dplane_ctx_rule_get_unique(ctx)); + stream_put(s, dplane_ctx_rule_get_ifname(ctx), INTERFACE_NAMSIZ); stream_putw_at(s, 0, stream_get_endp(s)); @@ -796,8 +831,7 @@ void zsend_ipset_notify_owner(struct zebra_pbr_ipset *ipset, struct stream *s; if (IS_ZEBRA_DEBUG_PACKET) - zlog_debug("%s: Notifying %u", __PRETTY_FUNCTION__, - ipset->unique); + zlog_debug("%s: Notifying %u", __func__, ipset->unique); for (ALL_LIST_ELEMENTS_RO(zrouter.client_list, node, client)) { if (ipset->sock == client->sock) @@ -826,8 +860,7 @@ void zsend_ipset_entry_notify_owner(struct zebra_pbr_ipset_entry *ipset, struct stream *s; if (IS_ZEBRA_DEBUG_PACKET) - zlog_debug("%s: Notifying %u", __PRETTY_FUNCTION__, - ipset->unique); + zlog_debug("%s: Notifying %u", __func__, ipset->unique); for (ALL_LIST_ELEMENTS_RO(zrouter.client_list, node, client)) { if (ipset->sock == client->sock) @@ -856,8 +889,7 @@ void zsend_iptable_notify_owner(struct zebra_pbr_iptable *iptable, struct stream *s; if (IS_ZEBRA_DEBUG_PACKET) - zlog_debug("%s: Notifying %u", __PRETTY_FUNCTION__, - iptable->unique); + zlog_debug("%s: Notifying %u", __func__, iptable->unique); for (ALL_LIST_ELEMENTS_RO(zrouter.client_list, node, client)) { if (iptable->sock == client->sock) @@ -877,17 +909,18 @@ void zsend_iptable_notify_owner(struct zebra_pbr_iptable *iptable, zserv_send_message(client, s); } -/* Router-id is updated. Send ZEBRA_ROUTER_ID_ADD to client. */ -int zsend_router_id_update(struct zserv *client, struct prefix *p, +/* Router-id is updated. Send ZEBRA_ROUTER_ID_UPDATE to client. */ +int zsend_router_id_update(struct zserv *client, afi_t afi, struct prefix *p, vrf_id_t vrf_id) { int blen; + struct stream *s; /* Check this client need interface information. */ - if (!vrf_bitmap_check(client->ridinfo, vrf_id)) + if (!vrf_bitmap_check(client->ridinfo[afi], vrf_id)) return 0; - struct stream *s = stream_new(ZEBRA_MAX_PACKET_SIZ); + s = stream_new(ZEBRA_MAX_PACKET_SIZ); /* Message type. */ zclient_create_header(s, ZEBRA_ROUTER_ID_UPDATE, vrf_id); @@ -923,20 +956,18 @@ int zsend_pw_update(struct zserv *client, struct zebra_pw *pw) } /* Send response to a get label chunk request to client */ -static int zsend_assign_label_chunk_response(struct zserv *client, - vrf_id_t vrf_id, - struct label_manager_chunk *lmc) +int zsend_assign_label_chunk_response(struct zserv *client, vrf_id_t vrf_id, + struct label_manager_chunk *lmc) { - int ret; struct stream *s = stream_new(ZEBRA_MAX_PACKET_SIZ); zclient_create_header(s, ZEBRA_GET_LABEL_CHUNK, vrf_id); + /* proto */ + stream_putc(s, client->proto); + /* instance */ + stream_putw(s, client->instance); if (lmc) { - /* proto */ - stream_putc(s, lmc->proto); - /* instance */ - stream_putw(s, lmc->instance); /* keep */ stream_putc(s, lmc->keep); /* start and end labels */ @@ -947,17 +978,13 @@ static int zsend_assign_label_chunk_response(struct zserv *client, /* Write packet size. */ stream_putw_at(s, 0, stream_get_endp(s)); - ret = writen(client->sock, s->data, stream_get_endp(s)); - stream_free(s); - return ret; + return zserv_send_message(client, s); } /* Send response to a label manager connect request to client */ -static int zsend_label_manager_connect_response(struct zserv *client, - vrf_id_t vrf_id, - unsigned short result) +int zsend_label_manager_connect_response(struct zserv *client, vrf_id_t vrf_id, + unsigned short result) { - int ret; struct stream *s = stream_new(ZEBRA_MAX_PACKET_SIZ); zclient_create_header(s, ZEBRA_LABEL_MANAGER_CONNECT, vrf_id); @@ -974,10 +1001,7 @@ static int zsend_label_manager_connect_response(struct zserv *client, /* Write packet size. */ stream_putw_at(s, 0, stream_get_endp(s)); - ret = writen(client->sock, s->data, stream_get_endp(s)); - stream_free(s); - - return ret; + return zserv_send_message(client, s); } /* Send response to a get table chunk request to client */ @@ -1019,7 +1043,7 @@ static int zsend_table_manager_connect_response(struct zserv *client, /* Inbound message handling ------------------------------------------------ */ -int cmd2type[] = { +const int cmd2type[] = { [ZEBRA_NEXTHOP_REGISTER] = RNH_NEXTHOP_TYPE, [ZEBRA_NEXTHOP_UNREGISTER] = RNH_NEXTHOP_TYPE, [ZEBRA_IMPORT_ROUTE_REGISTER] = RNH_IMPORT_CHECK_TYPE, @@ -1041,14 +1065,15 @@ static void zread_rnh_register(ZAPI_HANDLER_ARGS) if (IS_ZEBRA_DEBUG_NHT) zlog_debug( - "rnh_register msg from client %s: hdr->length=%d, type=%s vrf=%u\n", + "rnh_register msg from client %s: hdr->length=%d, type=%s vrf=%u", zebra_route_string(client->proto), hdr->length, (type == RNH_NEXTHOP_TYPE) ? "nexthop" : "route", zvrf->vrf->vrf_id); s = msg; - client->nh_reg_time = monotime(NULL); + if (!client->nh_reg_time) + client->nh_reg_time = monotime(NULL); while (l < hdr->length) { STREAM_GETC(s, flags); @@ -1060,7 +1085,7 @@ static void zread_rnh_register(ZAPI_HANDLER_ARGS) if (p.prefixlen > IPV4_MAX_BITLEN) { zlog_debug( "%s: Specified prefix hdr->length %d is too large for a v4 address", - __PRETTY_FUNCTION__, p.prefixlen); + __func__, p.prefixlen); return; } STREAM_GET(&p.u.prefix4.s_addr, s, IPV4_MAX_BYTELEN); @@ -1070,7 +1095,7 @@ static void zread_rnh_register(ZAPI_HANDLER_ARGS) if (p.prefixlen > IPV6_MAX_BITLEN) { zlog_debug( "%s: Specified prefix hdr->length %d is to large for a v6 address", - __PRETTY_FUNCTION__, p.prefixlen); + __func__, p.prefixlen); return; } STREAM_GET(&p.u.prefix6, s, IPV6_MAX_BYTELEN); @@ -1130,7 +1155,7 @@ static void zread_rnh_unregister(ZAPI_HANDLER_ARGS) if (IS_ZEBRA_DEBUG_NHT) zlog_debug( - "rnh_unregister msg from client %s: hdr->length=%d vrf: %u\n", + "rnh_unregister msg from client %s: hdr->length=%d vrf: %u", zebra_route_string(client->proto), hdr->length, zvrf->vrf->vrf_id); @@ -1151,7 +1176,7 @@ static void zread_rnh_unregister(ZAPI_HANDLER_ARGS) if (p.prefixlen > IPV4_MAX_BITLEN) { zlog_debug( "%s: Specified prefix hdr->length %d is to large for a v4 address", - __PRETTY_FUNCTION__, p.prefixlen); + __func__, p.prefixlen); return; } STREAM_GET(&p.u.prefix4.s_addr, s, IPV4_MAX_BYTELEN); @@ -1161,7 +1186,7 @@ static void zread_rnh_unregister(ZAPI_HANDLER_ARGS) if (p.prefixlen > IPV6_MAX_BITLEN) { zlog_debug( "%s: Specified prefix hdr->length %d is to large for a v6 address", - __PRETTY_FUNCTION__, p.prefixlen); + __func__, p.prefixlen); return; } STREAM_GET(&p.u.prefix6, s, IPV6_MAX_BYTELEN); @@ -1229,7 +1254,7 @@ static void zread_fec_register(ZAPI_HANDLER_ARGS) && p.prefixlen > IPV6_MAX_BITLEN)) { zlog_debug( "%s: Specified prefix hdr->length: %d is to long for %d", - __PRETTY_FUNCTION__, p.prefixlen, p.family); + __func__, p.prefixlen, p.family); return; } l += 5; @@ -1295,7 +1320,7 @@ static void zread_fec_unregister(ZAPI_HANDLER_ARGS) && p.prefixlen > IPV6_MAX_BITLEN)) { zlog_debug( "%s: Received prefix hdr->length %d which is greater than %d can support", - __PRETTY_FUNCTION__, p.prefixlen, p.family); + __func__, p.prefixlen, p.family); return; } l += 5; @@ -1318,6 +1343,20 @@ static void zread_interface_add(ZAPI_HANDLER_ARGS) struct vrf *vrf; struct interface *ifp; + vrf_id_t vrf_id = zvrf_id(zvrf); + if (vrf_id != VRF_DEFAULT && vrf_id != VRF_UNKNOWN) { + FOR_ALL_INTERFACES (zvrf->vrf, ifp) { + /* Skip pseudo interface. */ + if (!CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE)) + continue; + + zsend_interface_add(client, ifp); + zsend_interface_link_params(client, ifp); + zsend_interface_addresses(client, ifp); + } + return; + } + RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) { FOR_ALL_INTERFACES (vrf, ifp) { /* Skip pseudo interface. */ @@ -1336,20 +1375,201 @@ static void zread_interface_delete(ZAPI_HANDLER_ARGS) { } +/* + * Handle message requesting interface be set up or down. + */ +static void zread_interface_set_protodown(ZAPI_HANDLER_ARGS) +{ + ifindex_t ifindex; + struct interface *ifp; + char down; + + STREAM_GETL(msg, ifindex); + STREAM_GETC(msg, down); + + /* set ifdown */ + ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(NS_DEFAULT), ifindex); + + if (ifp) { + zlog_info("Setting interface %s (%u): protodown %s", ifp->name, + ifindex, down ? "on" : "off"); + zebra_if_set_protodown(ifp, down); + } else { + zlog_warn( + "Cannot set protodown %s for interface %u; does not exist", + down ? "on" : "off", ifindex); + } + + +stream_failure: + return; +} + + void zserv_nexthop_num_warn(const char *caller, const struct prefix *p, const unsigned int nexthop_num) { - if (nexthop_num > multipath_num) { + if (nexthop_num > zrouter.multipath_num) { char buff[PREFIX2STR_BUFFER]; 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, multipath_num); + caller, buff, nexthop_num, zrouter.multipath_num); } } +/* + * 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) +{ + struct nexthop *nexthop = NULL; + struct ipaddr vtep_ip; + struct interface *ifp; + int i; + char nhbuf[INET6_ADDRSTRLEN] = ""; + + switch (api_nh->type) { + case NEXTHOP_TYPE_IFINDEX: + nexthop = nexthop_from_ifindex(api_nh->ifindex, api_nh->vrf_id); + break; + case NEXTHOP_TYPE_IPV4: + if (IS_ZEBRA_DEBUG_RECV) { + inet_ntop(AF_INET, &api_nh->gate.ipv4, nhbuf, + sizeof(nhbuf)); + zlog_debug("%s: nh=%s, vrf_id=%d", __func__, + nhbuf, api_nh->vrf_id); + } + nexthop = nexthop_from_ipv4(&api_nh->gate.ipv4, NULL, + api_nh->vrf_id); + break; + case NEXTHOP_TYPE_IPV4_IFINDEX: + if (IS_ZEBRA_DEBUG_RECV) { + inet_ntop(AF_INET, &api_nh->gate.ipv4, nhbuf, + sizeof(nhbuf)); + zlog_debug("%s: nh=%s, vrf_id=%d, ifindex=%d", + __func__, nhbuf, api_nh->vrf_id, + api_nh->ifindex); + } + + nexthop = nexthop_from_ipv4_ifindex( + &api_nh->gate.ipv4, NULL, api_nh->ifindex, + api_nh->vrf_id); + + /* 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)) { + 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); + } + break; + case NEXTHOP_TYPE_IPV6: + if (IS_ZEBRA_DEBUG_RECV) { + inet_ntop(AF_INET6, &api_nh->gate.ipv6, nhbuf, + sizeof(nhbuf)); + zlog_debug("%s: nh=%s, vrf_id=%d", __func__, + nhbuf, api_nh->vrf_id); + } + nexthop = nexthop_from_ipv6(&api_nh->gate.ipv6, api_nh->vrf_id); + break; + case NEXTHOP_TYPE_IPV6_IFINDEX: + if (IS_ZEBRA_DEBUG_RECV) { + inet_ntop(AF_INET6, &api_nh->gate.ipv6, nhbuf, + sizeof(nhbuf)); + zlog_debug("%s: nh=%s, vrf_id=%d, ifindex=%d", + __func__, nhbuf, api_nh->vrf_id, + api_nh->ifindex); + } + nexthop = nexthop_from_ipv6_ifindex(&api_nh->gate.ipv6, + api_nh->ifindex, + api_nh->vrf_id); + + /* 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)) { + 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); + } + break; + case NEXTHOP_TYPE_BLACKHOLE: + if (IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: nh blackhole %d", + __func__, api_nh->bh_type); + + nexthop = nexthop_from_blackhole(api_nh->bh_type); + break; + } + + /* Return early if we couldn't process the zapi nexthop */ + if (nexthop == NULL) { + goto done; + } + + /* Mark nexthop as onlink either if client has explicitly told us + * to or if the nexthop is on an 'unnumbered' interface. + */ + if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_ONLINK)) + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK); + else if (api_nh->type == NEXTHOP_TYPE_IPV4_IFINDEX) { + ifp = if_lookup_by_index(api_nh->ifindex, api_nh->vrf_id); + if (ifp && connected_is_unnumbered(ifp)) + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK); + } + + if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_WEIGHT)) + nexthop->weight = api_nh->weight; + + if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP)) { + /* Validate count */ + if (api_nh->backup_num > NEXTHOP_MAX_BACKUPS) { + if (IS_ZEBRA_DEBUG_RECV || IS_ZEBRA_DEBUG_EVENT) + zlog_debug("%s: invalid backup nh count %d", + __func__, api_nh->backup_num); + nexthop_free(nexthop); + nexthop = NULL; + goto done; + } + + /* Copy backup info */ + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP); + nexthop->backup_num = api_nh->backup_num; + + for (i = 0; i < api_nh->backup_num; i++) { + /* Validate backup index */ + if (api_nh->backup_idx[i] < api->backup_nexthop_num) { + nexthop->backup_idx[i] = api_nh->backup_idx[i]; + } else { + if (IS_ZEBRA_DEBUG_RECV || IS_ZEBRA_DEBUG_EVENT) + zlog_debug("%s: invalid backup nh idx %d", + __func__, + api_nh->backup_idx[i]); + nexthop_free(nexthop); + nexthop = NULL; + goto done; + } + } + } + +done: + return nexthop; +} + static void zread_route_add(ZAPI_HANDLER_ARGS) { struct stream *s; @@ -1358,55 +1578,70 @@ static void zread_route_add(ZAPI_HANDLER_ARGS) afi_t afi; struct prefix_ipv6 *src_p = NULL; struct route_entry *re; - struct nexthop *nexthop = NULL; + 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 = 0; - struct ipaddr vtep_ip; + 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", - __PRETTY_FUNCTION__); + __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=%s, ZAPI_MESSAGE_LABEL: %sset, flags=0x%x", - __func__, buf_prefix, - (CHECK_FLAG(api.message, ZAPI_MESSAGE_LABEL) ? "" - : "un"), - api.flags); + 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. */ - vrf_id = zvrf_id(zvrf); re = XCALLOC(MTYPE_RE, sizeof(struct route_entry)); re->type = api.type; re->instance = api.instance; re->flags = api.flags; - re->uptime = time(NULL); + re->uptime = monotime(NULL); re->vrf_id = vrf_id; - if (api.tableid && vrf_id == VRF_DEFAULT) + + if (api.tableid) re->table = api.tableid; else re->table = zvrf->table_id; if (!CHECK_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP) || api.nexthop_num == 0) { - char buf_prefix[PREFIX_STRLEN]; - - prefix2str(&api.prefix, buf_prefix, sizeof(buf_prefix)); flog_warn(EC_ZEBRA_RX_ROUTE_NO_NEXTHOPS, - "%s: received a route without nexthops for prefix %s", - __func__, buf_prefix); + "%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); + } + + /* 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 @@ -1414,119 +1649,148 @@ static void zread_route_add(ZAPI_HANDLER_ARGS) */ for (i = 0; i < api.nexthop_num; i++) { api_nh = &api.nexthops[i]; - ifindex_t ifindex = 0; - if (IS_ZEBRA_DEBUG_RECV) - zlog_debug("nh type %d", api_nh->type); + /* Convert zapi nexthop */ + nexthop = nexthop_from_zapi(re, api_nh, &api); + 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; + } - switch (api_nh->type) { - case NEXTHOP_TYPE_IFINDEX: - nexthop = route_entry_nexthop_ifindex_add( - re, api_nh->ifindex, api_nh->vrf_id); - break; - case NEXTHOP_TYPE_IPV4: - if (IS_ZEBRA_DEBUG_RECV) { - char nhbuf[INET6_ADDRSTRLEN] = {0}; + if (CHECK_FLAG(api.message, ZAPI_MESSAGE_SRTE)) { + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_SRTE); + nexthop->srte_color = api_nh->srte_color; + } - inet_ntop(AF_INET, &api_nh->gate.ipv4, nhbuf, - INET6_ADDRSTRLEN); - zlog_debug("%s: nh=%s, vrf_id=%d", __func__, - nhbuf, api_nh->vrf_id); - } - nexthop = route_entry_nexthop_ipv4_add( - re, &api_nh->gate.ipv4, NULL, api_nh->vrf_id); - break; - case NEXTHOP_TYPE_IPV4_IFINDEX: + /* 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) { - memset(&vtep_ip, 0, sizeof(struct ipaddr)); - ifindex = api_nh->ifindex; - if (IS_ZEBRA_DEBUG_RECV) { - char nhbuf[INET6_ADDRSTRLEN] = {0}; + label_type = lsp_type_from_re_type(client->proto); + nexthop_add_labels(nexthop, label_type, + api_nh->label_num, + &api_nh->labels[0]); + } - inet_ntop(AF_INET, &api_nh->gate.ipv4, nhbuf, - INET6_ADDRSTRLEN); - zlog_debug( - "%s: nh=%s, vrf_id=%d (re->vrf_id=%d), ifindex=%d", - __func__, nhbuf, api_nh->vrf_id, - re->vrf_id, ifindex); - } - nexthop = route_entry_nexthop_ipv4_ifindex_add( - re, &api_nh->gate.ipv4, NULL, ifindex, - api_nh->vrf_id); - - /* 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)) { - 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); - } - break; - case NEXTHOP_TYPE_IPV6: - nexthop = route_entry_nexthop_ipv6_add( - re, &api_nh->gate.ipv6, api_nh->vrf_id); - break; - case NEXTHOP_TYPE_IPV6_IFINDEX: - memset(&vtep_ip, 0, sizeof(struct ipaddr)); - ifindex = api_nh->ifindex; - nexthop = route_entry_nexthop_ipv6_ifindex_add( - re, &api_nh->gate.ipv6, ifindex, - api_nh->vrf_id); - - /* 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)) { - 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); + if (IS_ZEBRA_DEBUG_RECV) { + labelbuf[0] = '\0'; + nhbuf[0] = '\0'; + + nexthop2str(nexthop, nhbuf, sizeof(nhbuf)); + + 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); } - break; - case NEXTHOP_TYPE_BLACKHOLE: - nexthop = route_entry_nexthop_blackhole_add( - re, api_nh->bh_type); - break; + + zlog_debug("%s: nh=%s, vrf_id=%d %s", + __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); + } + + /* 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; + } + + /* Copy backup nexthops also, if present */ + for (i = 0; i < api.backup_nexthop_num; i++) { + api_nh = &api.backup_nexthops[i]; + + /* Convert zapi backup nexthop */ + nexthop = nexthop_from_zapi(re, api_nh, &api); if (!nexthop) { flog_warn( EC_ZEBRA_NEXTHOP_CREATION_FAILED, - "%s: Nexthops Specified: %d but we failed to properly create one", - __PRETTY_FUNCTION__, api.nexthop_num); - nexthops_free(re->ng.nexthop); + "%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 (api_nh->onlink) - SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK); + + /* 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; + } + + if (CHECK_FLAG(api.message, ZAPI_MESSAGE_SRTE)) { + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_SRTE); + nexthop->srte_color = api_nh->srte_color; + } /* MPLS labels for BGP-LU or Segment Routing */ - if (CHECK_FLAG(api.message, ZAPI_MESSAGE_LABEL) + if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_LABEL) && api_nh->type != NEXTHOP_TYPE_IFINDEX - && api_nh->type != NEXTHOP_TYPE_BLACKHOLE) { - enum lsp_types_t label_type; + && api_nh->type != NEXTHOP_TYPE_BLACKHOLE + && api_nh->label_num > 0) { label_type = lsp_type_from_re_type(client->proto); - - if (IS_ZEBRA_DEBUG_RECV) { - zlog_debug( - "%s: adding %d labels of type %d (1st=%u)", - __func__, api_nh->label_num, label_type, - api_nh->labels[0]); - } - nexthop_add_labels(nexthop, label_type, api_nh->label_num, &api_nh->labels[0]); } + + if (IS_ZEBRA_DEBUG_RECV) { + labelbuf[0] = '\0'; + nhbuf[0] = '\0'; + + nexthop2str(nexthop, nhbuf, sizeof(nhbuf)); + + 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); + } + + zlog_debug("%s: backup nh=%s, vrf_id=%d %s", + __func__, nhbuf, api_nh->vrf_id, labelbuf); + } + + /* 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; } if (CHECK_FLAG(api.message, ZAPI_MESSAGE_DISTANCE)) @@ -1542,15 +1806,42 @@ static void zread_route_add(ZAPI_HANDLER_ARGS) if (afi != AFI_IP6 && CHECK_FLAG(api.message, ZAPI_MESSAGE_SRCPFX)) { flog_warn(EC_ZEBRA_RX_SRCDEST_WRONG_AFI, "%s: Received SRC Prefix but afi is not v6", - __PRETTY_FUNCTION__); - nexthops_free(re->ng.nexthop); + __func__); + nexthop_group_delete(&ng); + zebra_nhg_backup_free(&bnhg); XFREE(MTYPE_RE, re); return; } if (CHECK_FLAG(api.message, ZAPI_MESSAGE_SRCPFX)) src_p = &api.src_prefix; - ret = rib_add_multipath(afi, api.safi, &api.prefix, src_p, re); + if (api.safi != SAFI_UNICAST && api.safi != SAFI_MULTICAST) { + flog_warn(EC_LIB_ZAPI_MISSMATCH, + "%s: Received safi: %d but we can only accept UNICAST or MULTICAST", + __func__, api.safi); + nexthop_group_delete(&ng); + zebra_nhg_backup_free(&bnhg); + XFREE(MTYPE_RE, re); + return; + } + + /* 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; + ret = rib_add_multipath_nhe(afi, api.safi, &api.prefix, src_p, + re, &nhe); + + /* At this point, these allocations are not needed: 're' has been + * retained or freed, and if 're' still exists, it is using + * a reference to a shared group object. + */ + nexthop_group_delete(&ng); + if (bnhg) + zebra_nhg_backup_free(&bnhg); /* Stats */ switch (api.prefix.family) { @@ -1585,20 +1876,29 @@ static void zread_route_del(ZAPI_HANDLER_ARGS) if (afi != AFI_IP6 && CHECK_FLAG(api.message, ZAPI_MESSAGE_SRCPFX)) { flog_warn(EC_ZEBRA_RX_SRCDEST_WRONG_AFI, "%s: Received a src prefix while afi is not v6", - __PRETTY_FUNCTION__); + __func__); return; } if (CHECK_FLAG(api.message, ZAPI_MESSAGE_SRCPFX)) src_p = &api.src_prefix; - if (api.vrf_id == VRF_DEFAULT && api.tableid != 0) + if (api.tableid) table_id = api.tableid; else table_id = zvrf->table_id; + 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__, zvrf_id(zvrf), table_id, buf_prefix, + (int)api.message, api.flags); + } + rib_delete(afi, api.safi, zvrf_id(zvrf), api.type, api.instance, - api.flags, &api.prefix, src_p, NULL, table_id, api.metric, - api.distance, false); + api.flags, &api.prefix, src_p, NULL, 0, table_id, api.metric, + api.distance, false, false); /* Stats */ switch (api.prefix.family) { @@ -1628,20 +1928,48 @@ static void zread_ipv4_nexthop_lookup_mrib(ZAPI_HANDLER_ARGS) /* Register zebra server router-id information. Send current router-id */ static void zread_router_id_add(ZAPI_HANDLER_ARGS) { + afi_t afi; + struct prefix p; + STREAM_GETW(msg, afi); + + if (afi <= AFI_UNSPEC || afi >= AFI_MAX) { + zlog_warn( + "Invalid AFI %u while registering for router ID notifications", + afi); + goto stream_failure; + } + /* Router-id information is needed. */ - vrf_bitmap_set(client->ridinfo, zvrf_id(zvrf)); + vrf_bitmap_set(client->ridinfo[afi], zvrf_id(zvrf)); - router_id_get(&p, zvrf_id(zvrf)); + router_id_get(afi, &p, zvrf); - zsend_router_id_update(client, &p, zvrf_id(zvrf)); + zsend_router_id_update(client, afi, &p, zvrf_id(zvrf)); + +stream_failure: + return; } /* Unregister zebra server router-id information. */ static void zread_router_id_delete(ZAPI_HANDLER_ARGS) { - vrf_bitmap_unset(client->ridinfo, zvrf_id(zvrf)); + afi_t afi; + + STREAM_GETW(msg, afi); + + if (afi <= AFI_UNSPEC || afi >= AFI_MAX) { + zlog_warn( + "Invalid AFI %u while unregistering from router ID notifications", + afi); + goto stream_failure; + } + + vrf_bitmap_unset(client->ridinfo[afi], zvrf_id(zvrf)); + +stream_failure: + return; } static void zsend_capabilities(struct zserv *client, struct zebra_vrf *zvrf) @@ -1651,7 +1979,7 @@ static void zsend_capabilities(struct zserv *client, struct zebra_vrf *zvrf) zclient_create_header(s, ZEBRA_CAPABILITIES, zvrf->vrf->vrf_id); stream_putl(s, vrf_get_backend()); stream_putc(s, mpls_enabled); - stream_putl(s, multipath_num); + stream_putl(s, zrouter.multipath_num); stream_putc(s, zebra_mlag_get_role()); stream_putw_at(s, 0, stream_get_endp(s)); @@ -1666,6 +1994,10 @@ void zsend_capabilities_all_clients(void) zvrf = vrf_info_lookup(VRF_DEFAULT); for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) { + /* Do not send unsolicited messages to synchronous clients. */ + if (client->synchronous) + continue; + zsend_capabilities(client, zvrf); } } @@ -1677,13 +2009,20 @@ static void zread_hello(ZAPI_HANDLER_ARGS) uint8_t proto; unsigned short instance; uint8_t notify; + uint8_t synchronous; + uint32_t session_id; STREAM_GETC(msg, proto); STREAM_GETW(msg, instance); + STREAM_GETL(msg, session_id); STREAM_GETC(msg, notify); + STREAM_GETC(msg, synchronous); if (notify) client->notify_owner = true; + if (synchronous) + client->synchronous = true; + /* accept only dynamic routing protocols */ if ((proto < ZEBRA_ROUTE_MAX) && (proto > ZEBRA_ROUTE_CONNECT)) { zlog_notice( @@ -1695,10 +2034,16 @@ static void zread_hello(ZAPI_HANDLER_ARGS) client->proto = proto; client->instance = instance; + client->session_id = session_id; + + /* Graceful restart processing for client connect */ + zebra_gr_client_reconnect(client); } - zsend_capabilities(client, zvrf); - zebra_vrf_update_all(client); + if (!client->synchronous) { + zsend_capabilities(client, zvrf); + zebra_vrf_update_all(client); + } stream_failure: return; } @@ -1713,92 +2058,286 @@ static void zread_vrf_unregister(ZAPI_HANDLER_ARGS) for (i = 0; i < ZEBRA_ROUTE_MAX; i++) vrf_bitmap_unset(client->redist[afi][i], zvrf_id(zvrf)); vrf_bitmap_unset(client->redist_default[afi], zvrf_id(zvrf)); + vrf_bitmap_unset(client->ridinfo[afi], zvrf_id(zvrf)); + } +} + +/* + * Validate incoming zapi mpls lsp / labels message + */ +static int zapi_labels_validate(const struct zapi_labels *zl) +{ + int ret = -1; + int i, j, idx; + uint32_t bits[8]; + uint32_t ival; + const struct zapi_nexthop *znh; + + /* Validate backup info: no duplicates for a single primary */ + if (zl->backup_nexthop_num == 0) { + ret = 0; + goto done; + } + + for (j = 0; j < zl->nexthop_num; j++) { + znh = &zl->nexthops[j]; + + memset(bits, 0, sizeof(bits)); + + for (i = 0; i < znh->backup_num; i++) { + idx = znh->backup_idx[i] / 32; + + ival = 1 << znh->backup_idx[i] % 32; + + /* Check whether value is already used */ + if (ival & bits[idx]) { + /* Fail */ + + if (IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: invalid zapi mpls message: duplicate backup nexthop index %d", + __func__, + znh->backup_idx[i]); + goto done; + } + + /* Mark index value */ + bits[idx] |= ival; + } } - vrf_bitmap_unset(client->ridinfo, zvrf_id(zvrf)); + + ret = 0; + +done: + + return ret; } -static void zread_mpls_labels(ZAPI_HANDLER_ARGS) +/* + * Handle request to create an MPLS LSP. + * + * A single message can fully specify an LSP with multiple nexthops. + * + * When the optional ZAPI_LABELS_FTN flag is set, the specified FEC (route) is + * updated to use the received label(s). + */ +static void zread_mpls_labels_add(ZAPI_HANDLER_ARGS) { struct stream *s; - enum lsp_types_t type; - struct prefix prefix; - enum nexthop_types_t gtype; - union g_addr gate; - ifindex_t ifindex; - mpls_label_t in_label, out_label; - uint8_t distance; + struct zapi_labels zl; + int ret; /* Get input stream. */ s = msg; + if (zapi_labels_decode(s, &zl) < 0) { + if (IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: Unable to decode zapi_labels sent", + __func__); + return; + } - /* Get data. */ - STREAM_GETC(s, type); - STREAM_GETL(s, prefix.family); - switch (prefix.family) { - case AF_INET: - STREAM_GET(&prefix.u.prefix4.s_addr, s, IPV4_MAX_BYTELEN); - STREAM_GETC(s, prefix.prefixlen); - if (prefix.prefixlen > IPV4_MAX_BITLEN) { - zlog_debug( - "%s: Specified prefix length %d is greater than a v4 address can support", - __PRETTY_FUNCTION__, prefix.prefixlen); - return; + if (!mpls_enabled) + return; + + /* Validate; will debug on failure */ + if (zapi_labels_validate(&zl) < 0) + return; + + ret = mpls_zapi_labels_process(true, zvrf, &zl); + if (ret < 0) { + if (IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: Error processing zapi request", + __func__); + } +} + +/* + * Handle request to delete an MPLS LSP. + * + * An LSP is identified by its type and local label. When the received message + * doesn't contain any nexthop, the whole LSP is deleted. Otherwise, only the + * listed LSP nexthops (aka NHLFEs) are deleted. + * + * When the optional ZAPI_LABELS_FTN flag is set, the labels of the specified + * FEC (route) nexthops are deleted. + */ +static void zread_mpls_labels_delete(ZAPI_HANDLER_ARGS) +{ + struct stream *s; + struct zapi_labels zl; + int ret; + + /* Get input stream. */ + s = msg; + if (zapi_labels_decode(s, &zl) < 0) { + if (IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: Unable to decode zapi_labels sent", + __func__); + return; + } + + if (!mpls_enabled) + return; + + if (zl.nexthop_num > 0) { + ret = mpls_zapi_labels_process(false /*delete*/, zvrf, &zl); + if (ret < 0) { + if (IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: Error processing zapi request", + __func__); } - STREAM_GET(&gate.ipv4.s_addr, s, IPV4_MAX_BYTELEN); - break; - case AF_INET6: - STREAM_GET(&prefix.u.prefix6, s, 16); - STREAM_GETC(s, prefix.prefixlen); - if (prefix.prefixlen > IPV6_MAX_BITLEN) { + } else { + mpls_lsp_uninstall_all_vrf(zvrf, zl.type, zl.local_label); + + if (CHECK_FLAG(zl.message, ZAPI_LABELS_FTN)) + mpls_ftn_uninstall(zvrf, zl.type, &zl.route.prefix, + zl.route.type, zl.route.instance); + } +} + +/* + * Handle request to add an MPLS LSP or change an existing one. + * + * A single message can fully specify an LSP with multiple nexthops. + * + * When the optional ZAPI_LABELS_FTN flag is set, the specified FEC (route) is + * updated to use the received label(s). + * + * NOTE: zebra will use route replace semantics (make-before-break) to update + * the LSP in the forwarding plane if that's supported by the underlying + * platform. + */ +static void zread_mpls_labels_replace(ZAPI_HANDLER_ARGS) +{ + struct stream *s; + struct zapi_labels zl; + + /* Get input stream. */ + s = msg; + if (zapi_labels_decode(s, &zl) < 0) { + if (IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: Unable to decode zapi_labels sent", + __func__); + return; + } + + if (!mpls_enabled) + return; + + /* Validate; will debug on failure */ + if (zapi_labels_validate(&zl) < 0) + return; + + /* This removes everything, then re-adds from the client's + * zapi message. Since the LSP will be processed later, on this + * this same pthread, all of the changes will 'appear' at once. + */ + mpls_lsp_uninstall_all_vrf(zvrf, zl.type, zl.local_label); + if (CHECK_FLAG(zl.message, ZAPI_LABELS_FTN)) + mpls_ftn_uninstall(zvrf, zl.type, &zl.route.prefix, + zl.route.type, zl.route.instance); + + mpls_zapi_labels_process(true, zvrf, &zl); +} + +static void zread_sr_policy_set(ZAPI_HANDLER_ARGS) +{ + struct stream *s; + struct zapi_sr_policy zp; + struct zapi_srte_tunnel *zt; + struct zebra_sr_policy *policy; + + /* Get input stream. */ + s = msg; + if (zapi_sr_policy_decode(s, &zp) < 0) { + if (IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: Unable to decode zapi_sr_policy sent", + __PRETTY_FUNCTION__); + return; + } + zt = &zp.segment_list; + if (zt->label_num < 1) { + if (IS_ZEBRA_DEBUG_RECV) zlog_debug( - "%s: Specified prefix length %d is greater than a v6 address can support", - __PRETTY_FUNCTION__, prefix.prefixlen); - return; - } - STREAM_GET(&gate.ipv6, s, 16); - break; - default: - zlog_debug("%s: Specified AF %d is not supported for this call", - __PRETTY_FUNCTION__, prefix.family); + "%s: SR-TE tunnel must contain at least one label", + __PRETTY_FUNCTION__); return; } - STREAM_GETL(s, ifindex); - STREAM_GETC(s, distance); - STREAM_GETL(s, in_label); - STREAM_GETL(s, out_label); - switch (prefix.family) { - case AF_INET: - if (ifindex) - gtype = NEXTHOP_TYPE_IPV4_IFINDEX; - else - gtype = NEXTHOP_TYPE_IPV4; - break; - case AF_INET6: - if (ifindex) - gtype = NEXTHOP_TYPE_IPV6_IFINDEX; - else - gtype = NEXTHOP_TYPE_IPV6; - break; - default: + if (!mpls_enabled) + return; + + policy = zebra_sr_policy_find(zp.color, &zp.endpoint); + if (!policy) + policy = zebra_sr_policy_add(zp.color, &zp.endpoint, zp.name); + /* TODO: per-VRF list of SR-TE policies. */ + policy->zvrf = zvrf; + + zebra_sr_policy_validate(policy, &zp.segment_list); +} + +static void zread_sr_policy_delete(ZAPI_HANDLER_ARGS) +{ + struct stream *s; + struct zapi_sr_policy zp; + struct zebra_sr_policy *policy; + + /* Get input stream. */ + s = msg; + if (zapi_sr_policy_decode(s, &zp) < 0) { + if (IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: Unable to decode zapi_sr_policy sent", + __PRETTY_FUNCTION__); return; } if (!mpls_enabled) return; - if (hdr->command == ZEBRA_MPLS_LABELS_ADD) { - mpls_lsp_install(zvrf, type, in_label, out_label, gtype, &gate, - ifindex); - mpls_ftn_update(1, zvrf, type, &prefix, gtype, &gate, ifindex, - distance, out_label); - } else if (hdr->command == ZEBRA_MPLS_LABELS_DELETE) { - mpls_lsp_uninstall(zvrf, type, in_label, gtype, &gate, ifindex); - mpls_ftn_update(0, zvrf, type, &prefix, gtype, &gate, ifindex, - distance, out_label); + policy = zebra_sr_policy_find(zp.color, &zp.endpoint); + if (!policy) { + if (IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: Unable to find SR-TE policy", + __PRETTY_FUNCTION__); + return; } -stream_failure: - return; + + zebra_sr_policy_del(policy); +} + +int zsend_sr_policy_notify_status(uint32_t color, struct ipaddr *endpoint, + char *name, int status) +{ + struct zserv *client; + struct stream *s; + + client = zserv_find_client(ZEBRA_ROUTE_SRTE, 0); + if (!client) { + if (IS_ZEBRA_DEBUG_PACKET) + zlog_debug( + "Not notifying pathd about policy %s" + " status change to %d", + name, status); + return 0; + } + + if (IS_ZEBRA_DEBUG_PACKET) + zlog_debug( + "Notifying pathd about policy %s status change" + " to %d", + name, status); + + s = stream_new(ZEBRA_MAX_PACKET_SIZ); + stream_reset(s); + + zclient_create_header(s, ZEBRA_SR_POLICY_NOTIFY_STATUS, VRF_DEFAULT); + stream_putl(s, color); + stream_put_ipaddr(s, endpoint); + stream_write(s, name, SRTE_POLICY_NAME_MAX_LENGTH); + stream_putl(s, status); + + stream_putw_at(s, 0, stream_get_endp(s)); + + return zserv_send_message(client, s); } /* Send response to a table manager connect request to client */ @@ -1808,6 +2347,7 @@ static void zread_table_manager_connect(struct zserv *client, struct stream *s; uint8_t proto; uint16_t instance; + struct vrf *vrf = vrf_lookup_by_id(vrf_id); s = msg; @@ -1823,8 +2363,9 @@ static void zread_table_manager_connect(struct zserv *client, zsend_table_manager_connect_response(client, vrf_id, 1); return; } - zlog_notice("client %d with vrf %u instance %u connected as %s", - client->sock, vrf_id, instance, zebra_route_string(proto)); + zlog_notice("client %d with vrf %s(%u) instance %u connected as %s", + client->sock, VRF_LOGNAME(vrf), vrf_id, instance, + zebra_route_string(proto)); client->proto = proto; client->instance = instance; @@ -1860,27 +2401,16 @@ static void zread_label_manager_connect(struct zserv *client, flog_err(EC_ZEBRA_TM_WRONG_PROTO, "client %d has wrong protocol %s", client->sock, zebra_route_string(proto)); - if (client->is_synchronous) - zsend_label_manager_connect_response(client, vrf_id, 1); + zsend_label_manager_connect_response(client, vrf_id, 1); return; } - zlog_notice("client %d with vrf %u instance %u connected as %s", - client->sock, vrf_id, instance, zebra_route_string(proto)); + + /* recall proto and instance in this socket */ client->proto = proto; client->instance = instance; - /* - * Release previous labels of same protocol and instance. - * This is done in case it restarted from an unexpected shutdown. - */ - release_daemon_label_chunks(client); - - zlog_debug( - " Label Manager client connected: sock %d, proto %s, vrf %u instance %u", - client->sock, zebra_route_string(proto), vrf_id, instance); - /* send response back */ - if (client->is_synchronous) - zsend_label_manager_connect_response(client, vrf_id, 0); + /* call hook for connection using wrapper */ + lm_client_connect_call(client, vrf_id); stream_failure: return; @@ -1891,8 +2421,8 @@ static void zread_get_label_chunk(struct zserv *client, struct stream *msg, { struct stream *s; uint8_t keep; - uint32_t size; - struct label_manager_chunk *lmc; + uint32_t size, base; + struct label_manager_chunk *lmc = NULL; uint8_t proto; unsigned short instance; @@ -1904,19 +2434,12 @@ static void zread_get_label_chunk(struct zserv *client, struct stream *msg, STREAM_GETW(s, instance); STREAM_GETC(s, keep); STREAM_GETL(s, size); + STREAM_GETL(s, base); - lmc = assign_label_chunk(proto, instance, keep, size); - if (!lmc) - flog_err( - EC_ZEBRA_LM_CANNOT_ASSIGN_CHUNK, - "Unable to assign Label Chunk of size %u to %s instance %u", - size, zebra_route_string(proto), instance); - else - zlog_debug("Assigned Label Chunk %u - %u to %s instance %u", - lmc->start, lmc->end, - zebra_route_string(proto), instance); - /* send response back */ - zsend_assign_label_chunk_response(client, vrf_id, lmc); + assert(proto == client->proto && instance == client->instance); + + /* call hook to get a chunk using wrapper */ + lm_get_chunk_call(&lmc, client, keep, size, base, vrf_id); stream_failure: return; @@ -1938,33 +2461,25 @@ static void zread_release_label_chunk(struct zserv *client, struct stream *msg) STREAM_GETL(s, start); STREAM_GETL(s, end); - release_label_chunk(proto, instance, start, end); + assert(proto == client->proto && instance == client->instance); + + /* call hook to release a chunk using wrapper */ + lm_release_chunk_call(client, start, end); stream_failure: return; } + static void zread_label_manager_request(ZAPI_HANDLER_ARGS) { - /* to avoid sending other messages like ZERBA_INTERFACE_UP */ - client->is_synchronous = hdr->command == - ZEBRA_LABEL_MANAGER_CONNECT; - - /* external label manager */ - if (lm_is_external) - zread_relay_label_manager_request(hdr->command, client, msg, - zvrf_id(zvrf)); - /* this is a label manager */ + if (hdr->command == ZEBRA_LABEL_MANAGER_CONNECT + || hdr->command == ZEBRA_LABEL_MANAGER_CONNECT_ASYNC) + zread_label_manager_connect(client, msg, zvrf_id(zvrf)); else { - if (hdr->command == ZEBRA_LABEL_MANAGER_CONNECT || - hdr->command == ZEBRA_LABEL_MANAGER_CONNECT_ASYNC) - zread_label_manager_connect(client, msg, zvrf_id(zvrf)); - else { - if (hdr->command == ZEBRA_GET_LABEL_CHUNK) - zread_get_label_chunk(client, msg, - zvrf_id(zvrf)); - else if (hdr->command == ZEBRA_RELEASE_LABEL_CHUNK) - zread_release_label_chunk(client, msg); - } + if (hdr->command == ZEBRA_GET_LABEL_CHUNK) + zread_get_label_chunk(client, msg, zvrf_id(zvrf)); + else if (hdr->command == ZEBRA_RELEASE_LABEL_CHUNK) + zread_release_label_chunk(client, msg); } } @@ -2054,6 +2569,7 @@ static void zread_pseudowire(ZAPI_HANDLER_ARGS) /* Get data. */ STREAM_GET(ifname, s, IF_NAMESIZE); + ifname[IF_NAMESIZE - 1] = '\0'; STREAM_GETL(s, ifindex); STREAM_GETL(s, type); STREAM_GETL(s, af); @@ -2161,6 +2677,12 @@ static void zread_vrf_label(ZAPI_HANDLER_ARGS) s = msg; STREAM_GETL(s, nlabel); STREAM_GETC(s, afi); + + if (!(IS_VALID_AFI(afi))) { + zlog_warn("Invalid AFI for VRF label: %u", afi); + return; + } + if (nlabel == zvrf->label[afi]) { /* * Nothing to do here move along @@ -2204,13 +2726,14 @@ static void zread_vrf_label(ZAPI_HANDLER_ARGS) if (really_remove) mpls_lsp_uninstall(def_zvrf, ltype, zvrf->label[afi], NEXTHOP_TYPE_IFINDEX, NULL, - ifp->ifindex); + ifp->ifindex, false /*backup*/); } - if (nlabel != MPLS_LABEL_NONE) - mpls_lsp_install(def_zvrf, ltype, nlabel, - MPLS_LABEL_IMPLICIT_NULL, NEXTHOP_TYPE_IFINDEX, - NULL, ifp->ifindex); + if (nlabel != MPLS_LABEL_NONE) { + mpls_label_t out_label = MPLS_LABEL_IMPLICIT_NULL; + mpls_lsp_install(def_zvrf, ltype, nlabel, 1, &out_label, + NEXTHOP_TYPE_IFINDEX, NULL, ifp->ifindex); + } zvrf->label[afi] = nlabel; stream_failure: @@ -2222,7 +2745,7 @@ static inline void zread_rule(ZAPI_HANDLER_ARGS) struct zebra_pbr_rule zpr; struct stream *s; uint32_t total, i; - ifindex_t ifindex; + char ifname[INTERFACE_NAMSIZ + 1] = {}; s = msg; STREAM_GETL(s, total); @@ -2245,20 +2768,13 @@ static inline void zread_rule(ZAPI_HANDLER_ARGS) STREAM_GET(&zpr.rule.filter.dst_ip.u.prefix, s, prefix_blen(&zpr.rule.filter.dst_ip)); STREAM_GETW(s, zpr.rule.filter.dst_port); + STREAM_GETC(s, zpr.rule.filter.dsfield); STREAM_GETL(s, zpr.rule.filter.fwmark); STREAM_GETL(s, zpr.rule.action.table); - STREAM_GETL(s, ifindex); - - if (ifindex) { - zpr.ifp = if_lookup_by_index_per_ns( - zvrf->zns, - ifindex); - if (!zpr.ifp) { - zlog_debug("Failed to lookup ifindex: %u", - ifindex); - return; - } - } + STREAM_GET(ifname, s, INTERFACE_NAMSIZ); + + strlcpy(zpr.ifname, ifname, sizeof(zpr.ifname)); + strlcpy(zpr.rule.ifname, ifname, sizeof(zpr.rule.ifname)); if (!is_default_prefix(&zpr.rule.filter.src_ip)) zpr.rule.filter.filter_bm |= PBR_FILTER_SRC_IP; @@ -2272,9 +2788,30 @@ static inline void zread_rule(ZAPI_HANDLER_ARGS) if (zpr.rule.filter.dst_port) zpr.rule.filter.filter_bm |= PBR_FILTER_DST_PORT; + if (zpr.rule.filter.dsfield) + zpr.rule.filter.filter_bm |= PBR_FILTER_DSFIELD; + if (zpr.rule.filter.fwmark) zpr.rule.filter.filter_bm |= PBR_FILTER_FWMARK; + if (!(zpr.rule.filter.src_ip.family == AF_INET + || zpr.rule.filter.src_ip.family == AF_INET6)) { + zlog_warn( + "Unsupported PBR source IP family: %s (%hhu)", + family2str(zpr.rule.filter.src_ip.family), + zpr.rule.filter.src_ip.family); + return; + } + if (!(zpr.rule.filter.dst_ip.family == AF_INET + || zpr.rule.filter.dst_ip.family == AF_INET6)) { + zlog_warn( + "Unsupported PBR destination IP family: %s (%hhu)", + family2str(zpr.rule.filter.dst_ip.family), + zpr.rule.filter.dst_ip.family); + return; + } + + zpr.vrf_id = zvrf->vrf->vrf_id; if (hdr->command == ZEBRA_RULE_ADD) zebra_pbr_add_rule(&zpr); @@ -2302,6 +2839,7 @@ static inline void zread_ipset(ZAPI_HANDLER_ARGS) zpi.vrf_id = zvrf->vrf->vrf_id; STREAM_GETL(s, zpi.unique); STREAM_GETL(s, zpi.type); + STREAM_GETC(s, zpi.family); STREAM_GET(&zpi.ipset_name, s, ZEBRA_IPSET_NAME_SIZE); if (hdr->command == ZEBRA_IPSET_CREATE) @@ -2331,6 +2869,7 @@ static inline void zread_ipset_entry(ZAPI_HANDLER_ARGS) zpi.sock = client->sock; STREAM_GETL(s, zpi.unique); STREAM_GET(&ipset.ipset_name, s, ZEBRA_IPSET_NAME_SIZE); + ipset.ipset_name[ZEBRA_IPSET_NAME_SIZE - 1] = '\0'; STREAM_GETC(s, zpi.src.family); STREAM_GETC(s, zpi.src.prefixlen); STREAM_GET(&zpi.src.u.prefix, s, prefix_blen(&zpi.src)); @@ -2359,9 +2898,31 @@ static inline void zread_ipset_entry(ZAPI_HANDLER_ARGS) if (zpi.proto != 0) zpi.filter_bm |= PBR_FILTER_PROTO; + if (!(zpi.dst.family == AF_INET + || zpi.dst.family == AF_INET6)) { + zlog_warn( + "Unsupported PBR destination IP family: %s (%hhu)", + family2str(zpi.dst.family), zpi.dst.family); + goto stream_failure; + } + if (!(zpi.src.family == AF_INET + || zpi.src.family == AF_INET6)) { + zlog_warn( + "Unsupported PBR source IP family: %s (%hhu)", + family2str(zpi.src.family), zpi.src.family); + goto stream_failure; + } + /* calculate backpointer */ zpi.backpointer = zebra_pbr_lookup_ipset_pername(ipset.ipset_name); + + if (!zpi.backpointer) { + zlog_warn("ipset name specified: %s does not exist", + ipset.ipset_name); + goto stream_failure; + } + if (hdr->command == ZEBRA_IPSET_ENTRY_ADD) zebra_pbr_add_ipset_entry(&zpi); else @@ -2374,44 +2935,115 @@ static inline void zread_ipset_entry(ZAPI_HANDLER_ARGS) static inline void zread_iptable(ZAPI_HANDLER_ARGS) { - struct zebra_pbr_iptable zpi; + struct zebra_pbr_iptable *zpi = + XCALLOC(MTYPE_TMP, sizeof(struct zebra_pbr_iptable)); struct stream *s; s = msg; - memset(&zpi, 0, sizeof(zpi)); - - zpi.interface_name_list = list_new(); - zpi.sock = client->sock; - zpi.vrf_id = zvrf->vrf->vrf_id; - STREAM_GETL(s, zpi.unique); - STREAM_GETL(s, zpi.type); - STREAM_GETL(s, zpi.filter_bm); - STREAM_GETL(s, zpi.action); - STREAM_GETL(s, zpi.fwmark); - STREAM_GET(&zpi.ipset_name, s, ZEBRA_IPSET_NAME_SIZE); - STREAM_GETW(s, zpi.pkt_len_min); - STREAM_GETW(s, zpi.pkt_len_max); - STREAM_GETW(s, zpi.tcp_flags); - STREAM_GETW(s, zpi.tcp_mask_flags); - STREAM_GETC(s, zpi.dscp_value); - STREAM_GETC(s, zpi.fragment); - STREAM_GETL(s, zpi.nb_interface); - zebra_pbr_iptable_update_interfacelist(s, &zpi); + zpi->interface_name_list = list_new(); + zpi->sock = client->sock; + zpi->vrf_id = zvrf->vrf->vrf_id; + STREAM_GETL(s, zpi->unique); + STREAM_GETL(s, zpi->type); + STREAM_GETL(s, zpi->filter_bm); + STREAM_GETL(s, zpi->action); + STREAM_GETL(s, zpi->fwmark); + STREAM_GET(&zpi->ipset_name, s, ZEBRA_IPSET_NAME_SIZE); + STREAM_GETC(s, zpi->family); + STREAM_GETW(s, zpi->pkt_len_min); + STREAM_GETW(s, zpi->pkt_len_max); + STREAM_GETW(s, zpi->tcp_flags); + STREAM_GETW(s, zpi->tcp_mask_flags); + STREAM_GETC(s, zpi->dscp_value); + STREAM_GETC(s, zpi->fragment); + STREAM_GETC(s, zpi->protocol); + STREAM_GETW(s, zpi->flow_label); + STREAM_GETL(s, zpi->nb_interface); + zebra_pbr_iptable_update_interfacelist(s, zpi); if (hdr->command == ZEBRA_IPTABLE_ADD) - zebra_pbr_add_iptable(&zpi); + zebra_pbr_add_iptable(zpi); else - zebra_pbr_del_iptable(&zpi); + zebra_pbr_del_iptable(zpi); + stream_failure: + zebra_pbr_iptable_free(zpi); + zpi = NULL; return; } -void (*zserv_handlers[])(ZAPI_HANDLER_ARGS) = { +static inline void zread_neigh_discover(ZAPI_HANDLER_ARGS) +{ + struct stream *s; + ifindex_t ifindex; + struct interface *ifp; + struct prefix p; + struct ipaddr ip; + + s = msg; + + STREAM_GETL(s, ifindex); + + ifp = if_lookup_by_index_per_ns(zvrf->zns, ifindex); + if (!ifp) { + zlog_debug("Failed to lookup ifindex: %u", ifindex); + return; + } + + STREAM_GETC(s, p.family); + STREAM_GETC(s, p.prefixlen); + STREAM_GET(&p.u.prefix, s, prefix_blen(&p)); + + if (p.family == AF_INET) + SET_IPADDR_V4(&ip); + else + SET_IPADDR_V6(&ip); + + memcpy(&ip.ip.addr, &p.u.prefix, prefix_blen(&p)); + + dplane_neigh_discover(ifp, &ip); + +stream_failure: + return; +} + +static void zsend_error_msg(struct zserv *client, enum zebra_error_types error, + struct zmsghdr *bad_hdr) +{ + + struct stream *s = stream_new(ZEBRA_MAX_PACKET_SIZ); + + zclient_create_header(s, ZEBRA_ERROR, bad_hdr->vrf_id); + + zserv_encode_error(s, error); + + client->error_cnt++; + zserv_send_message(client, s); +} + +static void zserv_error_no_vrf(ZAPI_HANDLER_ARGS) +{ + if (IS_ZEBRA_DEBUG_PACKET && IS_ZEBRA_DEBUG_RECV) + zlog_debug("ZAPI message specifies unknown VRF: %d", + hdr->vrf_id); + + zsend_error_msg(client, ZEBRA_NO_VRF, hdr); +} + +static void zserv_error_invalid_msg_type(ZAPI_HANDLER_ARGS) +{ + zlog_info("Zebra received unknown command %d", hdr->command); + + zsend_error_msg(client, ZEBRA_INVALID_MSG_TYPE, hdr); +} + +void (*const zserv_handlers[])(ZAPI_HANDLER_ARGS) = { [ZEBRA_ROUTER_ID_ADD] = zread_router_id_add, [ZEBRA_ROUTER_ID_DELETE] = zread_router_id_delete, [ZEBRA_INTERFACE_ADD] = zread_interface_add, [ZEBRA_INTERFACE_DELETE] = zread_interface_delete, + [ZEBRA_INTERFACE_SET_PROTODOWN] = zread_interface_set_protodown, [ZEBRA_ROUTE_ADD] = zread_route_add, [ZEBRA_ROUTE_DELETE] = zread_route_del, [ZEBRA_REDISTRIBUTE_ADD] = zebra_redistribute_add, @@ -2433,15 +3065,13 @@ void (*zserv_handlers[])(ZAPI_HANDLER_ARGS) = { [ZEBRA_VRF_UNREGISTER] = zread_vrf_unregister, [ZEBRA_VRF_LABEL] = zread_vrf_label, [ZEBRA_BFD_CLIENT_REGISTER] = zebra_ptm_bfd_client_register, -#if defined(HAVE_RTADV) [ZEBRA_INTERFACE_ENABLE_RADV] = zebra_interface_radv_enable, [ZEBRA_INTERFACE_DISABLE_RADV] = zebra_interface_radv_disable, -#else - [ZEBRA_INTERFACE_ENABLE_RADV] = NULL, - [ZEBRA_INTERFACE_DISABLE_RADV] = NULL, -#endif - [ZEBRA_MPLS_LABELS_ADD] = zread_mpls_labels, - [ZEBRA_MPLS_LABELS_DELETE] = zread_mpls_labels, + [ZEBRA_SR_POLICY_SET] = zread_sr_policy_set, + [ZEBRA_SR_POLICY_DELETE] = zread_sr_policy_delete, + [ZEBRA_MPLS_LABELS_ADD] = zread_mpls_labels_add, + [ZEBRA_MPLS_LABELS_DELETE] = zread_mpls_labels_delete, + [ZEBRA_MPLS_LABELS_REPLACE] = zread_mpls_labels_replace, [ZEBRA_IPMR_ROUTE_STATS] = zebra_ipmr_route_stats, [ZEBRA_LABEL_MANAGER_CONNECT] = zread_label_manager_request, [ZEBRA_LABEL_MANAGER_CONNECT_ASYNC] = zread_label_manager_request, @@ -2453,6 +3083,8 @@ void (*zserv_handlers[])(ZAPI_HANDLER_ARGS) = { [ZEBRA_ADVERTISE_SVI_MACIP] = zebra_vxlan_advertise_svi_macip, [ZEBRA_ADVERTISE_SUBNET] = zebra_vxlan_advertise_subnet, [ZEBRA_ADVERTISE_ALL_VNI] = zebra_vxlan_advertise_all_vni, + [ZEBRA_REMOTE_ES_VTEP_ADD] = zebra_evpn_proc_remote_es, + [ZEBRA_REMOTE_ES_VTEP_DEL] = zebra_evpn_proc_remote_es, [ZEBRA_REMOTE_VTEP_ADD] = zebra_vxlan_remote_vtep_add, [ZEBRA_REMOTE_VTEP_DEL] = zebra_vxlan_remote_vtep_del, [ZEBRA_REMOTE_MACIP_ADD] = zebra_vxlan_remote_macip_add, @@ -2475,56 +3107,82 @@ void (*zserv_handlers[])(ZAPI_HANDLER_ARGS) = { [ZEBRA_IPTABLE_ADD] = zread_iptable, [ZEBRA_IPTABLE_DELETE] = zread_iptable, [ZEBRA_VXLAN_FLOOD_CONTROL] = zebra_vxlan_flood_control, -}; - -#if defined(HANDLE_ZAPI_FUZZING) -extern struct zebra_privs_t zserv_privs; + [ZEBRA_VXLAN_SG_REPLAY] = zebra_vxlan_sg_replay, + [ZEBRA_MLAG_CLIENT_REGISTER] = zebra_mlag_client_register, + [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}; -static void zserv_write_incoming(struct stream *orig, uint16_t command) +/* + * Process a batch of zapi messages. + */ +void zserv_handle_commands(struct zserv *client, struct stream_fifo *fifo) { - char fname[MAXPATHLEN]; - struct stream *copy; - int fd = -1; + struct zmsghdr hdr; + struct zebra_vrf *zvrf; + struct stream *msg; + struct stream_fifo temp_fifo; - copy = stream_dup(orig); - stream_set_getp(copy, 0); + stream_fifo_init(&temp_fifo); - snprintf(fname, MAXPATHLEN, "%s/%u", DAEMON_VTY_DIR, command); + while (stream_fifo_head(fifo)) { + msg = stream_fifo_pop(fifo); - frr_elevate_privs(&zserv_privs) { - fd = open(fname, O_CREAT | O_WRONLY | O_EXCL, 0644); - } - stream_flush(copy, fd); - close(fd); - stream_free(copy); -} -#endif + if (STREAM_READABLE(msg) > ZEBRA_MAX_PACKET_SIZ) { + if (IS_ZEBRA_DEBUG_PACKET && IS_ZEBRA_DEBUG_RECV) + zlog_debug( + "ZAPI message is %zu bytes long but the maximum packet size is %u; dropping", + STREAM_READABLE(msg), + ZEBRA_MAX_PACKET_SIZ); + goto continue_loop; + } -void zserv_handle_commands(struct zserv *client, struct stream *msg) -{ - struct zmsghdr hdr; - struct zebra_vrf *zvrf; + zapi_parse_header(msg, &hdr); - zapi_parse_header(msg, &hdr); + if (IS_ZEBRA_DEBUG_PACKET && IS_ZEBRA_DEBUG_RECV + && IS_ZEBRA_DEBUG_DETAIL) + zserv_log_message(NULL, msg, &hdr); -#if defined(HANDLE_ZAPI_FUZZING) - zserv_write_incoming(msg, hdr.command); -#endif + hdr.length -= ZEBRA_HEADER_SIZE; - hdr.length -= ZEBRA_HEADER_SIZE; + /* Before checking for a handler function, check for + * special messages that are handled in another module; + * we'll treat these as opaque. + */ + if (zebra_opaque_handles_msgid(hdr.command)) { + /* Reset message buffer */ + stream_set_getp(msg, 0); - /* lookup vrf */ - zvrf = zebra_vrf_lookup_by_id(hdr.vrf_id); - if (!zvrf) { - if (IS_ZEBRA_DEBUG_PACKET && IS_ZEBRA_DEBUG_RECV) - zlog_debug("ZAPI message specifies unknown VRF: %d", - hdr.vrf_id); - return; - } + stream_fifo_push(&temp_fifo, msg); + + /* Continue without freeing the message */ + msg = NULL; + goto continue_loop; + } + + /* lookup vrf */ + zvrf = zebra_vrf_lookup_by_id(hdr.vrf_id); + if (!zvrf) { + zserv_error_no_vrf(client, &hdr, msg, zvrf); + goto continue_loop; + } + + if (hdr.command >= array_size(zserv_handlers) + || zserv_handlers[hdr.command] == NULL) { + zserv_error_invalid_msg_type(client, &hdr, msg, zvrf); + goto continue_loop; + } - if (hdr.command >= array_size(zserv_handlers) - || zserv_handlers[hdr.command] == NULL) - zlog_info("Zebra received unknown command %d", hdr.command); - else zserv_handlers[hdr.command](client, &hdr, msg, zvrf); + +continue_loop: + stream_free(msg); + } + + /* Dispatch any special messages from the temp fifo */ + if (stream_fifo_head(&temp_fifo) != NULL) + zebra_opaque_enqueue_batch(&temp_fifo); + + stream_fifo_deinit(&temp_fifo); } diff --git a/zebra/zapi_msg.h b/zebra/zapi_msg.h index d30fa2d0ef..e7c755c2bf 100644 --- a/zebra/zapi_msg.h +++ b/zebra/zapi_msg.h @@ -29,6 +29,8 @@ #include "zebra/zserv.h" #include "zebra/zebra_pbr.h" #include "zebra/zebra_errors.h" +#include "zebra/label_manager.h" + #ifdef __cplusplus extern "C" { @@ -40,10 +42,11 @@ extern "C" { * client * the client datastructure * - * msg - * the message + * fifo + * a batch of messages */ -extern void zserv_handle_commands(struct zserv *client, struct stream *msg); +extern void zserv_handle_commands(struct zserv *client, + struct stream_fifo *fifo); extern int zsend_vrf_add(struct zserv *zclient, struct zebra_vrf *zvrf); extern int zsend_vrf_delete(struct zserv *zclient, struct zebra_vrf *zvrf); @@ -63,9 +66,10 @@ extern int zsend_interface_update(int cmd, struct zserv *client, extern int zsend_redistribute_route(int cmd, struct zserv *zclient, const struct prefix *p, const struct prefix *src_p, - struct route_entry *re); -extern int zsend_router_id_update(struct zserv *zclient, struct prefix *p, - vrf_id_t vrf_id); + const struct route_entry *re); + +extern int zsend_router_id_update(struct zserv *zclient, afi_t afi, + struct prefix *p, vrf_id_t vrf_id); extern int zsend_interface_vrf_update(struct zserv *zclient, struct interface *ifp, vrf_id_t vrf_id); extern int zsend_interface_link_params(struct zserv *zclient, @@ -77,7 +81,7 @@ extern int zsend_route_notify_owner(struct route_entry *re, extern int zsend_route_notify_owner_ctx(const struct zebra_dplane_ctx *ctx, enum zapi_route_notify_owner note); -extern void zsend_rule_notify_owner(struct zebra_pbr_rule *rule, +extern void zsend_rule_notify_owner(const struct zebra_dplane_ctx *ctx, enum zapi_rule_notify_owner note); extern void zsend_ipset_notify_owner(struct zebra_pbr_ipset *ipset, enum zapi_ipset_notify_owner note); @@ -90,6 +94,15 @@ extern void zserv_nexthop_num_warn(const char *caller, const struct prefix *p, const unsigned int nexthop_num); extern void zsend_capabilities_all_clients(void); +extern int zsend_assign_label_chunk_response(struct zserv *client, + vrf_id_t vrf_id, + struct label_manager_chunk *lmc); +extern int zsend_label_manager_connect_response(struct zserv *client, + vrf_id_t vrf_id, + unsigned short result); +extern int zsend_sr_policy_notify_status(uint32_t color, + struct ipaddr *endpoint, char *name, + int status); #ifdef __cplusplus } diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index d1b28227c3..c094a8e3f7 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -17,6 +17,10 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include "lib/libfrr.h" #include "lib/debug.h" #include "lib/frratomic.h" @@ -24,16 +28,18 @@ #include "lib/memory.h" #include "lib/queue.h" #include "lib/zebra.h" -#include "zebra/zebra_router.h" #include "zebra/zebra_memory.h" #include "zebra/zebra_router.h" #include "zebra/zebra_dplane.h" +#include "zebra/zebra_vxlan_private.h" +#include "zebra/zebra_mpls.h" #include "zebra/rt.h" #include "zebra/debug.h" +#include "zebra/zebra_pbr.h" /* Memory type for context blocks */ -DEFINE_MTYPE(ZEBRA, DP_CTX, "Zebra DPlane Ctx") -DEFINE_MTYPE(ZEBRA, DP_PROV, "Zebra DPlane Provider") +DEFINE_MTYPE_STATIC(ZEBRA, DP_CTX, "Zebra DPlane Ctx") +DEFINE_MTYPE_STATIC(ZEBRA, DP_PROV, "Zebra DPlane Provider") #ifndef AOK # define AOK 0 @@ -62,6 +68,20 @@ const uint32_t DPLANE_DEFAULT_NEW_WORK = 100; #endif /* DPLANE_DEBUG */ +/* + * Nexthop information captured for nexthop/nexthop group updates + */ +struct dplane_nexthop_info { + uint32_t id; + afi_t afi; + vrf_id_t vrf_id; + int type; + + struct nexthop_group ng; + struct nh_grp nh_grp[MULTIPATH_NUM]; + uint8_t nh_grp_count; +}; + /* * Route information captured for route updates. */ @@ -91,11 +111,19 @@ struct dplane_route_info { uint32_t zd_mtu; uint32_t zd_nexthop_mtu; + /* Nexthop hash entry info */ + struct dplane_nexthop_info nhe; + /* Nexthops */ + uint32_t zd_nhg_id; struct nexthop_group zd_ng; + /* Backup nexthops (if present) */ + struct nexthop_group backup_ng; + /* "Previous" nexthops, used only in route updates without netlink */ struct nexthop_group zd_old_ng; + struct nexthop_group old_backup_ng; /* TODO -- use fixed array of nexthops, to avoid mallocs? */ @@ -105,8 +133,6 @@ struct dplane_route_info { * Pseudowire info for the dataplane */ struct dplane_pw_info { - char ifname[IF_NAMESIZE]; - ifindex_t ifindex; int type; int af; int status; @@ -126,16 +152,13 @@ struct dplane_pw_info { */ struct dplane_intf_info { - char ifname[INTERFACE_NAMSIZ]; - ifindex_t ifindex; - uint32_t metric; uint32_t flags; #define DPLANE_INTF_CONNECTED (1 << 0) /* Connected peer, p2p */ #define DPLANE_INTF_SECONDARY (1 << 1) #define DPLANE_INTF_BROADCAST (1 << 2) -#define DPLANE_INTF_HAS_DEST (1 << 3) +#define DPLANE_INTF_HAS_DEST DPLANE_INTF_CONNECTED #define DPLANE_INTF_HAS_LABEL (1 << 4) /* Interface address/prefix */ @@ -148,6 +171,62 @@ struct dplane_intf_info { char label_buf[32]; }; +/* + * EVPN MAC address info for the dataplane. + */ +struct dplane_mac_info { + vlanid_t vid; + ifindex_t br_ifindex; + struct ethaddr mac; + struct in_addr vtep_ip; + bool is_sticky; + uint32_t nhg_id; + uint32_t update_flags; +}; + +/* + * Neighbor info for the dataplane + */ +struct dplane_neigh_info { + struct ipaddr ip_addr; + struct ethaddr mac; + uint32_t flags; + uint16_t state; + uint32_t update_flags; +}; + +/* + * Policy based routing rule info for the dataplane + */ +struct dplane_ctx_rule { + uint32_t priority; + + /* The route table pointed by this rule */ + uint32_t table; + + /* Filter criteria */ + uint32_t filter_bm; + uint32_t fwmark; + uint8_t dsfield; + struct prefix src_ip; + struct prefix dst_ip; + char ifname[INTERFACE_NAMSIZ + 1]; +}; + +struct dplane_rule_info { + /* + * Originating zclient sock fd, so we can know who to send + * back to. + */ + int sock; + + int unique; + int seq; + + struct dplane_ctx_rule new; + struct dplane_ctx_rule old; +}; + /* * The context block used to exchange info about route updates across * the boundary between the zebra main context (and pthread) and the @@ -172,6 +251,11 @@ struct zebra_dplane_ctx { uint32_t zd_seq; uint32_t zd_old_seq; + /* Some updates may be generated by notifications: allow the + * plugin to notice and ignore results from its own notifications. + */ + uint32_t zd_notif_provider; + /* TODO -- internal/sub-operation status? */ enum zebra_dplane_result zd_remote_status; enum zebra_dplane_result zd_kernel_status; @@ -179,12 +263,18 @@ struct zebra_dplane_ctx { vrf_id_t zd_vrf_id; uint32_t zd_table_id; + char zd_ifname[INTERFACE_NAMSIZ]; + ifindex_t zd_ifindex; + /* Support info for different kinds of updates */ union { struct dplane_route_info rinfo; zebra_lsp_t lsp; struct dplane_pw_info pw; struct dplane_intf_info intf; + struct dplane_mac_info macinfo; + struct dplane_neigh_info neigh; + struct dplane_rule_info rule; } u; /* Namespace info, used especially for netlink kernel communication */ @@ -222,6 +312,8 @@ struct zebra_dplane_provider { /* Flags */ int dp_flags; + int (*dp_start)(struct zebra_dplane_provider *prov); + int (*dp_fp)(struct zebra_dplane_provider *prov); int (*dp_fini)(struct zebra_dplane_provider *prov, bool early_p); @@ -262,8 +354,8 @@ static struct zebra_dplane_globals { /* Sentinel for end of shutdown */ volatile bool dg_run; - /* Route-update context queue inbound to the dataplane */ - TAILQ_HEAD(zdg_ctx_q, zebra_dplane_ctx) dg_route_ctx_q; + /* Update context queue inbound to the dataplane */ + TAILQ_HEAD(zdg_ctx_q, zebra_dplane_ctx) dg_update_ctx_q; /* Ordered list of providers */ TAILQ_HEAD(zdg_prov_q, zebra_dplane_provider) dg_providers_q; @@ -288,6 +380,9 @@ static struct zebra_dplane_globals { _Atomic uint32_t dg_route_errors; _Atomic uint32_t dg_other_errors; + _Atomic uint32_t dg_nexthops_in; + _Atomic uint32_t dg_nexthop_errors; + _Atomic uint32_t dg_lsps_in; _Atomic uint32_t dg_lsp_errors; @@ -297,6 +392,15 @@ static struct zebra_dplane_globals { _Atomic uint32_t dg_intf_addrs_in; _Atomic uint32_t dg_intf_addr_errors; + _Atomic uint32_t dg_macs_in; + _Atomic uint32_t dg_mac_errors; + + _Atomic uint32_t dg_neighs_in; + _Atomic uint32_t dg_neigh_errors; + + _Atomic uint32_t dg_rules_in; + _Atomic uint32_t dg_rule_errors; + _Atomic uint32_t dg_update_yields; /* Dataplane pthread */ @@ -337,6 +441,18 @@ static enum zebra_dplane_result pw_update_internal(struct zebra_pw *pw, static enum zebra_dplane_result intf_addr_update_internal( const struct interface *ifp, const struct connected *ifc, enum dplane_op_e op); +static enum zebra_dplane_result mac_update_common( + enum dplane_op_e op, const struct interface *ifp, + const struct interface *br_ifp, + vlanid_t vid, const struct ethaddr *mac, + struct in_addr vtep_ip, bool sticky, uint32_t nhg_id, + uint32_t update_flags); +static enum zebra_dplane_result neigh_update_internal( + enum dplane_op_e op, + const struct interface *ifp, + const struct ethaddr *mac, + const struct ipaddr *ip, + uint32_t flags, uint16_t state, uint32_t update_flags); /* * Public APIs @@ -351,7 +467,7 @@ struct thread_master *dplane_get_thread_master(void) /* * Allocate a dataplane update context */ -static struct zebra_dplane_ctx *dplane_ctx_alloc(void) +struct zebra_dplane_ctx *dplane_ctx_alloc(void) { struct zebra_dplane_ctx *p; @@ -370,64 +486,93 @@ void dplane_enable_sys_route_notifs(void) } /* - * Free a dataplane results context. + * Clean up dependent/internal allocations inside a context object */ -static void dplane_ctx_free(struct zebra_dplane_ctx **pctx) +static void dplane_ctx_free_internal(struct zebra_dplane_ctx *ctx) { - if (pctx == NULL) - return; - - DPLANE_CTX_VALID(*pctx); - - /* TODO -- just freeing memory, but would like to maintain - * a pool - */ - - /* Some internal allocations may need to be freed, depending on + /* + * Some internal allocations may need to be freed, depending on * the type of info captured in the ctx. */ - switch ((*pctx)->zd_op) { + switch (ctx->zd_op) { case DPLANE_OP_ROUTE_INSTALL: case DPLANE_OP_ROUTE_UPDATE: case DPLANE_OP_ROUTE_DELETE: case DPLANE_OP_SYS_ROUTE_ADD: case DPLANE_OP_SYS_ROUTE_DELETE: + case DPLANE_OP_ROUTE_NOTIFY: /* Free allocated nexthops */ - if ((*pctx)->u.rinfo.zd_ng.nexthop) { + if (ctx->u.rinfo.zd_ng.nexthop) { + /* This deals with recursive nexthops too */ + nexthops_free(ctx->u.rinfo.zd_ng.nexthop); + + ctx->u.rinfo.zd_ng.nexthop = NULL; + } + + /* Free backup info also (if present) */ + if (ctx->u.rinfo.backup_ng.nexthop) { + /* This deals with recursive nexthops too */ + nexthops_free(ctx->u.rinfo.backup_ng.nexthop); + + ctx->u.rinfo.backup_ng.nexthop = NULL; + } + + if (ctx->u.rinfo.zd_old_ng.nexthop) { /* This deals with recursive nexthops too */ - nexthops_free((*pctx)->u.rinfo.zd_ng.nexthop); + nexthops_free(ctx->u.rinfo.zd_old_ng.nexthop); - (*pctx)->u.rinfo.zd_ng.nexthop = NULL; + ctx->u.rinfo.zd_old_ng.nexthop = NULL; } - if ((*pctx)->u.rinfo.zd_old_ng.nexthop) { + if (ctx->u.rinfo.old_backup_ng.nexthop) { /* This deals with recursive nexthops too */ - nexthops_free((*pctx)->u.rinfo.zd_old_ng.nexthop); + nexthops_free(ctx->u.rinfo.old_backup_ng.nexthop); - (*pctx)->u.rinfo.zd_old_ng.nexthop = NULL; + ctx->u.rinfo.old_backup_ng.nexthop = NULL; } break; + case DPLANE_OP_NH_INSTALL: + case DPLANE_OP_NH_UPDATE: + case DPLANE_OP_NH_DELETE: { + if (ctx->u.rinfo.nhe.ng.nexthop) { + /* This deals with recursive nexthops too */ + nexthops_free(ctx->u.rinfo.nhe.ng.nexthop); + + ctx->u.rinfo.nhe.ng.nexthop = NULL; + } + break; + } + case DPLANE_OP_LSP_INSTALL: case DPLANE_OP_LSP_UPDATE: case DPLANE_OP_LSP_DELETE: + case DPLANE_OP_LSP_NOTIFY: { - zebra_nhlfe_t *nhlfe, *next; + zebra_nhlfe_t *nhlfe; - /* Free allocated NHLFEs */ - for (nhlfe = (*pctx)->u.lsp.nhlfe_list; nhlfe; nhlfe = next) { - next = nhlfe->next; + /* Unlink and free allocated NHLFEs */ + frr_each_safe(nhlfe_list, &ctx->u.lsp.nhlfe_list, nhlfe) { + nhlfe_list_del(&ctx->u.lsp.nhlfe_list, nhlfe); + zebra_mpls_nhlfe_free(nhlfe); + } - zebra_mpls_nhlfe_del(nhlfe); + /* Unlink and free allocated backup NHLFEs, if present */ + frr_each_safe(nhlfe_list, + &(ctx->u.lsp.backup_nhlfe_list), nhlfe) { + nhlfe_list_del(&ctx->u.lsp.backup_nhlfe_list, + nhlfe); + zebra_mpls_nhlfe_free(nhlfe); } - /* Clear pointers in lsp struct, in case we're cacheing + /* Clear pointers in lsp struct, in case we're caching * free context structs. */ - (*pctx)->u.lsp.nhlfe_list = NULL; - (*pctx)->u.lsp.best_nhlfe = NULL; + nhlfe_list_init(&ctx->u.lsp.nhlfe_list); + ctx->u.lsp.best_nhlfe = NULL; + nhlfe_list_init(&ctx->u.lsp.backup_nhlfe_list); break; } @@ -435,30 +580,70 @@ static void dplane_ctx_free(struct zebra_dplane_ctx **pctx) case DPLANE_OP_PW_INSTALL: case DPLANE_OP_PW_UNINSTALL: /* Free allocated nexthops */ - if ((*pctx)->u.pw.nhg.nexthop) { + if (ctx->u.pw.nhg.nexthop) { /* This deals with recursive nexthops too */ - nexthops_free((*pctx)->u.pw.nhg.nexthop); + nexthops_free(ctx->u.pw.nhg.nexthop); - (*pctx)->u.pw.nhg.nexthop = NULL; + ctx->u.pw.nhg.nexthop = NULL; } break; case DPLANE_OP_ADDR_INSTALL: case DPLANE_OP_ADDR_UNINSTALL: /* Maybe free label string, if allocated */ - if ((*pctx)->u.intf.label != NULL && - (*pctx)->u.intf.label != (*pctx)->u.intf.label_buf) { - free((*pctx)->u.intf.label); - (*pctx)->u.intf.label = NULL; + if (ctx->u.intf.label != NULL && + ctx->u.intf.label != ctx->u.intf.label_buf) { + free(ctx->u.intf.label); + ctx->u.intf.label = NULL; } break; + case DPLANE_OP_MAC_INSTALL: + case DPLANE_OP_MAC_DELETE: + case DPLANE_OP_NEIGH_INSTALL: + case DPLANE_OP_NEIGH_UPDATE: + case DPLANE_OP_NEIGH_DELETE: + case DPLANE_OP_VTEP_ADD: + case DPLANE_OP_VTEP_DELETE: + case DPLANE_OP_RULE_ADD: + case DPLANE_OP_RULE_DELETE: + case DPLANE_OP_RULE_UPDATE: + case DPLANE_OP_NEIGH_DISCOVER: case DPLANE_OP_NONE: break; } +} + +/* + * Free a dataplane results context. + */ +static void dplane_ctx_free(struct zebra_dplane_ctx **pctx) +{ + if (pctx == NULL) + return; + + DPLANE_CTX_VALID(*pctx); + + /* TODO -- just freeing memory, but would like to maintain + * a pool + */ + + /* Some internal allocations may need to be freed, depending on + * the type of info captured in the ctx. + */ + dplane_ctx_free_internal(*pctx); XFREE(MTYPE_DP_CTX, *pctx); - *pctx = NULL; +} + +/* + * Reset an allocated context object for re-use. All internal allocations are + * freed and the context is memset. + */ +void dplane_ctx_reset(struct zebra_dplane_ctx *ctx) +{ + dplane_ctx_free_internal(ctx); + memset(ctx, 0, sizeof(*ctx)); } /* @@ -543,6 +728,12 @@ bool dplane_ctx_is_skip_kernel(const struct zebra_dplane_ctx *ctx) return CHECK_FLAG(ctx->zd_flags, DPLANE_CTX_FLAG_NO_KERNEL); } +void dplane_ctx_set_op(struct zebra_dplane_ctx *ctx, enum dplane_op_e op) +{ + DPLANE_CTX_VALID(ctx); + ctx->zd_op = op; +} + enum dplane_op_e dplane_ctx_get_op(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); @@ -569,6 +760,20 @@ const char *dplane_op2str(enum dplane_op_e op) case DPLANE_OP_ROUTE_DELETE: ret = "ROUTE_DELETE"; break; + case DPLANE_OP_ROUTE_NOTIFY: + ret = "ROUTE_NOTIFY"; + break; + + /* Nexthop update */ + case DPLANE_OP_NH_INSTALL: + ret = "NH_INSTALL"; + break; + case DPLANE_OP_NH_UPDATE: + ret = "NH_UPDATE"; + break; + case DPLANE_OP_NH_DELETE: + ret = "NH_DELETE"; + break; case DPLANE_OP_LSP_INSTALL: ret = "LSP_INSTALL"; @@ -579,6 +784,9 @@ const char *dplane_op2str(enum dplane_op_e op) case DPLANE_OP_LSP_DELETE: ret = "LSP_DELETE"; break; + case DPLANE_OP_LSP_NOTIFY: + ret = "LSP_NOTIFY"; + break; case DPLANE_OP_PW_INSTALL: ret = "PW_INSTALL"; @@ -601,6 +809,42 @@ const char *dplane_op2str(enum dplane_op_e op) ret = "ADDR_UNINSTALL"; break; + case DPLANE_OP_MAC_INSTALL: + ret = "MAC_INSTALL"; + break; + case DPLANE_OP_MAC_DELETE: + ret = "MAC_DELETE"; + break; + + case DPLANE_OP_NEIGH_INSTALL: + ret = "NEIGH_INSTALL"; + break; + case DPLANE_OP_NEIGH_UPDATE: + ret = "NEIGH_UPDATE"; + break; + case DPLANE_OP_NEIGH_DELETE: + ret = "NEIGH_DELETE"; + break; + case DPLANE_OP_VTEP_ADD: + ret = "VTEP_ADD"; + break; + case DPLANE_OP_VTEP_DELETE: + ret = "VTEP_DELETE"; + break; + + case DPLANE_OP_RULE_ADD: + ret = "RULE_ADD"; + break; + case DPLANE_OP_RULE_DELETE: + ret = "RULE_DELETE"; + break; + case DPLANE_OP_RULE_UPDATE: + ret = "RULE_UPDATE"; + break; + + case DPLANE_OP_NEIGH_DISCOVER: + ret = "NEIGH_DISCOVER"; + break; } return ret; @@ -625,6 +869,14 @@ const char *dplane_res2str(enum zebra_dplane_result res) return ret; } +void dplane_ctx_set_dest(struct zebra_dplane_ctx *ctx, + const struct prefix *dest) +{ + DPLANE_CTX_VALID(ctx); + + prefix_copy(&(ctx->u.rinfo.zd_dest), dest); +} + const struct prefix *dplane_ctx_get_dest(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); @@ -632,6 +884,16 @@ const struct prefix *dplane_ctx_get_dest(const struct zebra_dplane_ctx *ctx) return &(ctx->u.rinfo.zd_dest); } +void dplane_ctx_set_src(struct zebra_dplane_ctx *ctx, const struct prefix *src) +{ + DPLANE_CTX_VALID(ctx); + + if (src) + prefix_copy(&(ctx->u.rinfo.zd_src), src); + else + memset(&(ctx->u.rinfo.zd_src), 0, sizeof(struct prefix)); +} + /* Source prefix is a little special - return NULL for "no src prefix" */ const struct prefix *dplane_ctx_get_src(const struct zebra_dplane_ctx *ctx) { @@ -666,342 +928,832 @@ uint32_t dplane_ctx_get_old_seq(const struct zebra_dplane_ctx *ctx) return ctx->zd_old_seq; } -vrf_id_t dplane_ctx_get_vrf(const struct zebra_dplane_ctx *ctx) +void dplane_ctx_set_vrf(struct zebra_dplane_ctx *ctx, vrf_id_t vrf) { DPLANE_CTX_VALID(ctx); - return ctx->zd_vrf_id; + ctx->zd_vrf_id = vrf; } -int dplane_ctx_get_type(const struct zebra_dplane_ctx *ctx) +vrf_id_t dplane_ctx_get_vrf(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); - return ctx->u.rinfo.zd_type; + return ctx->zd_vrf_id; } -int dplane_ctx_get_old_type(const struct zebra_dplane_ctx *ctx) +bool dplane_ctx_is_from_notif(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); - return ctx->u.rinfo.zd_old_type; + return (ctx->zd_notif_provider != 0); } -afi_t dplane_ctx_get_afi(const struct zebra_dplane_ctx *ctx) +uint32_t dplane_ctx_get_notif_provider(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); - return ctx->u.rinfo.zd_afi; + return ctx->zd_notif_provider; } -safi_t dplane_ctx_get_safi(const struct zebra_dplane_ctx *ctx) +void dplane_ctx_set_notif_provider(struct zebra_dplane_ctx *ctx, + uint32_t id) { DPLANE_CTX_VALID(ctx); - return ctx->u.rinfo.zd_safi; + ctx->zd_notif_provider = id; } -uint32_t dplane_ctx_get_table(const struct zebra_dplane_ctx *ctx) +const char *dplane_ctx_get_ifname(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); - return ctx->zd_table_id; + return ctx->zd_ifname; } -route_tag_t dplane_ctx_get_tag(const struct zebra_dplane_ctx *ctx) +void dplane_ctx_set_ifname(struct zebra_dplane_ctx *ctx, const char *ifname) { DPLANE_CTX_VALID(ctx); - return ctx->u.rinfo.zd_tag; + if (!ifname) + return; + + strlcpy(ctx->zd_ifname, ifname, sizeof(ctx->zd_ifname)); } -route_tag_t dplane_ctx_get_old_tag(const struct zebra_dplane_ctx *ctx) +ifindex_t dplane_ctx_get_ifindex(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); - return ctx->u.rinfo.zd_old_tag; + return ctx->zd_ifindex; } -uint16_t dplane_ctx_get_instance(const struct zebra_dplane_ctx *ctx) +void dplane_ctx_set_type(struct zebra_dplane_ctx *ctx, int type) { DPLANE_CTX_VALID(ctx); - return ctx->u.rinfo.zd_instance; + ctx->u.rinfo.zd_type = type; } -uint16_t dplane_ctx_get_old_instance(const struct zebra_dplane_ctx *ctx) +int dplane_ctx_get_type(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); - return ctx->u.rinfo.zd_old_instance; + return ctx->u.rinfo.zd_type; } -uint32_t dplane_ctx_get_metric(const struct zebra_dplane_ctx *ctx) +int dplane_ctx_get_old_type(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); - return ctx->u.rinfo.zd_metric; + return ctx->u.rinfo.zd_old_type; } -uint32_t dplane_ctx_get_old_metric(const struct zebra_dplane_ctx *ctx) +void dplane_ctx_set_afi(struct zebra_dplane_ctx *ctx, afi_t afi) { DPLANE_CTX_VALID(ctx); - return ctx->u.rinfo.zd_old_metric; + ctx->u.rinfo.zd_afi = afi; } -uint32_t dplane_ctx_get_mtu(const struct zebra_dplane_ctx *ctx) +afi_t dplane_ctx_get_afi(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); - return ctx->u.rinfo.zd_mtu; + return ctx->u.rinfo.zd_afi; } -uint32_t dplane_ctx_get_nh_mtu(const struct zebra_dplane_ctx *ctx) +void dplane_ctx_set_safi(struct zebra_dplane_ctx *ctx, safi_t safi) { DPLANE_CTX_VALID(ctx); - return ctx->u.rinfo.zd_nexthop_mtu; + ctx->u.rinfo.zd_safi = safi; } -uint8_t dplane_ctx_get_distance(const struct zebra_dplane_ctx *ctx) +safi_t dplane_ctx_get_safi(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); - return ctx->u.rinfo.zd_distance; + return ctx->u.rinfo.zd_safi; } -uint8_t dplane_ctx_get_old_distance(const struct zebra_dplane_ctx *ctx) +void dplane_ctx_set_table(struct zebra_dplane_ctx *ctx, uint32_t table) { DPLANE_CTX_VALID(ctx); - return ctx->u.rinfo.zd_old_distance; + ctx->zd_table_id = table; } -const struct nexthop_group *dplane_ctx_get_ng( - const struct zebra_dplane_ctx *ctx) +uint32_t dplane_ctx_get_table(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); - return &(ctx->u.rinfo.zd_ng); + return ctx->zd_table_id; } -const struct nexthop_group *dplane_ctx_get_old_ng( - const struct zebra_dplane_ctx *ctx) +route_tag_t dplane_ctx_get_tag(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); - return &(ctx->u.rinfo.zd_old_ng); + return ctx->u.rinfo.zd_tag; } -const struct zebra_dplane_info *dplane_ctx_get_ns( - const struct zebra_dplane_ctx *ctx) +void dplane_ctx_set_tag(struct zebra_dplane_ctx *ctx, route_tag_t tag) { DPLANE_CTX_VALID(ctx); - return &(ctx->zd_ns_info); + ctx->u.rinfo.zd_tag = tag; } -/* Accessors for LSP information */ - -mpls_label_t dplane_ctx_get_in_label(const struct zebra_dplane_ctx *ctx) +route_tag_t dplane_ctx_get_old_tag(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); - return ctx->u.lsp.ile.in_label; + return ctx->u.rinfo.zd_old_tag; } -uint8_t dplane_ctx_get_addr_family(const struct zebra_dplane_ctx *ctx) +uint16_t dplane_ctx_get_instance(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); - return ctx->u.lsp.addr_family; + return ctx->u.rinfo.zd_instance; } -uint32_t dplane_ctx_get_lsp_flags(const struct zebra_dplane_ctx *ctx) +void dplane_ctx_set_instance(struct zebra_dplane_ctx *ctx, uint16_t instance) { DPLANE_CTX_VALID(ctx); - return ctx->u.lsp.flags; + ctx->u.rinfo.zd_instance = instance; } -const zebra_nhlfe_t *dplane_ctx_get_nhlfe(const struct zebra_dplane_ctx *ctx) +uint16_t dplane_ctx_get_old_instance(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); - return ctx->u.lsp.nhlfe_list; + return ctx->u.rinfo.zd_old_instance; } -const zebra_nhlfe_t * -dplane_ctx_get_best_nhlfe(const struct zebra_dplane_ctx *ctx) +uint32_t dplane_ctx_get_metric(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); - return ctx->u.lsp.best_nhlfe; + return ctx->u.rinfo.zd_metric; } -uint32_t dplane_ctx_get_lsp_num_ecmp(const struct zebra_dplane_ctx *ctx) +uint32_t dplane_ctx_get_old_metric(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); - return ctx->u.lsp.num_ecmp; + return ctx->u.rinfo.zd_old_metric; } -const char *dplane_ctx_get_pw_ifname(const struct zebra_dplane_ctx *ctx) +uint32_t dplane_ctx_get_mtu(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); - return ctx->u.pw.ifname; + return ctx->u.rinfo.zd_mtu; } -mpls_label_t dplane_ctx_get_pw_local_label(const struct zebra_dplane_ctx *ctx) +uint32_t dplane_ctx_get_nh_mtu(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); - return ctx->u.pw.local_label; + return ctx->u.rinfo.zd_nexthop_mtu; } -mpls_label_t dplane_ctx_get_pw_remote_label(const struct zebra_dplane_ctx *ctx) +uint8_t dplane_ctx_get_distance(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); - return ctx->u.pw.remote_label; + return ctx->u.rinfo.zd_distance; } -int dplane_ctx_get_pw_type(const struct zebra_dplane_ctx *ctx) +void dplane_ctx_set_distance(struct zebra_dplane_ctx *ctx, uint8_t distance) { DPLANE_CTX_VALID(ctx); - return ctx->u.pw.type; + ctx->u.rinfo.zd_distance = distance; } -int dplane_ctx_get_pw_af(const struct zebra_dplane_ctx *ctx) +uint8_t dplane_ctx_get_old_distance(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); - return ctx->u.pw.af; + return ctx->u.rinfo.zd_old_distance; } -uint32_t dplane_ctx_get_pw_flags(const struct zebra_dplane_ctx *ctx) +/* + * Set the nexthops associated with a context: note that processing code + * may well expect that nexthops are in canonical (sorted) order, so we + * will enforce that here. + */ +void dplane_ctx_set_nexthops(struct zebra_dplane_ctx *ctx, struct nexthop *nh) { DPLANE_CTX_VALID(ctx); - return ctx->u.pw.flags; + if (ctx->u.rinfo.zd_ng.nexthop) { + nexthops_free(ctx->u.rinfo.zd_ng.nexthop); + ctx->u.rinfo.zd_ng.nexthop = NULL; + } + nexthop_group_copy_nh_sorted(&(ctx->u.rinfo.zd_ng), nh); } -int dplane_ctx_get_pw_status(const struct zebra_dplane_ctx *ctx) +/* + * Set the list of backup nexthops; their ordering is preserved (they're not + * re-sorted.) + */ +void dplane_ctx_set_backup_nhg(struct zebra_dplane_ctx *ctx, + const struct nexthop_group *nhg) { + struct nexthop *nh, *last_nh, *nexthop; + DPLANE_CTX_VALID(ctx); - return ctx->u.pw.status; + if (ctx->u.rinfo.backup_ng.nexthop) { + nexthops_free(ctx->u.rinfo.backup_ng.nexthop); + ctx->u.rinfo.backup_ng.nexthop = NULL; + } + + last_nh = NULL; + + /* Be careful to preserve the order of the backup list */ + for (nh = nhg->nexthop; nh; nh = nh->next) { + nexthop = nexthop_dup(nh, NULL); + + if (last_nh) + NEXTHOP_APPEND(last_nh, nexthop); + else + ctx->u.rinfo.backup_ng.nexthop = nexthop; + + last_nh = nexthop; + } } -const union g_addr *dplane_ctx_get_pw_dest( - const struct zebra_dplane_ctx *ctx) +uint32_t dplane_ctx_get_nhg_id(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); - - return &(ctx->u.pw.dest); + return ctx->u.rinfo.zd_nhg_id; } -const union pw_protocol_fields *dplane_ctx_get_pw_proto( +const struct nexthop_group *dplane_ctx_get_ng( const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); - return &(ctx->u.pw.fields); + return &(ctx->u.rinfo.zd_ng); } const struct nexthop_group * -dplane_ctx_get_pw_nhg(const struct zebra_dplane_ctx *ctx) +dplane_ctx_get_backup_ng(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); - return &(ctx->u.pw.nhg); + return &(ctx->u.rinfo.backup_ng); } -/* Accessors for interface information */ -const char *dplane_ctx_get_ifname(const struct zebra_dplane_ctx *ctx) +const struct nexthop_group * +dplane_ctx_get_old_ng(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); - return ctx->u.intf.ifname; + return &(ctx->u.rinfo.zd_old_ng); } -ifindex_t dplane_ctx_get_ifindex(const struct zebra_dplane_ctx *ctx) +const struct nexthop_group * +dplane_ctx_get_old_backup_ng(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return &(ctx->u.rinfo.old_backup_ng); +} + +const struct zebra_dplane_info *dplane_ctx_get_ns( + const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return &(ctx->zd_ns_info); +} + +/* Accessors for nexthop information */ +uint32_t dplane_ctx_get_nhe_id(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + return ctx->u.rinfo.nhe.id; +} + +afi_t dplane_ctx_get_nhe_afi(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + return ctx->u.rinfo.nhe.afi; +} + +vrf_id_t dplane_ctx_get_nhe_vrf_id(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + return ctx->u.rinfo.nhe.vrf_id; +} + +int dplane_ctx_get_nhe_type(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + return ctx->u.rinfo.nhe.type; +} + +const struct nexthop_group * +dplane_ctx_get_nhe_ng(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + return &(ctx->u.rinfo.nhe.ng); +} + +const struct nh_grp * +dplane_ctx_get_nhe_nh_grp(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + return ctx->u.rinfo.nhe.nh_grp; +} + +uint8_t dplane_ctx_get_nhe_nh_grp_count(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + return ctx->u.rinfo.nhe.nh_grp_count; +} + +/* Accessors for LSP information */ + +mpls_label_t dplane_ctx_get_in_label(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.lsp.ile.in_label; +} + +void dplane_ctx_set_in_label(struct zebra_dplane_ctx *ctx, mpls_label_t label) +{ + DPLANE_CTX_VALID(ctx); + + ctx->u.lsp.ile.in_label = label; +} + +uint8_t dplane_ctx_get_addr_family(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.lsp.addr_family; +} + +void dplane_ctx_set_addr_family(struct zebra_dplane_ctx *ctx, + uint8_t family) +{ + DPLANE_CTX_VALID(ctx); + + ctx->u.lsp.addr_family = family; +} + +uint32_t dplane_ctx_get_lsp_flags(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.lsp.flags; +} + +void dplane_ctx_set_lsp_flags(struct zebra_dplane_ctx *ctx, + uint32_t flags) +{ + DPLANE_CTX_VALID(ctx); + + ctx->u.lsp.flags = flags; +} + +const struct nhlfe_list_head *dplane_ctx_get_nhlfe_list( + const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + return &(ctx->u.lsp.nhlfe_list); +} + +const struct nhlfe_list_head *dplane_ctx_get_backup_nhlfe_list( + const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + return &(ctx->u.lsp.backup_nhlfe_list); +} + +zebra_nhlfe_t *dplane_ctx_add_nhlfe(struct zebra_dplane_ctx *ctx, + enum lsp_types_t lsp_type, + enum nexthop_types_t nh_type, + const union g_addr *gate, + ifindex_t ifindex, + uint8_t num_labels, + mpls_label_t *out_labels) +{ + zebra_nhlfe_t *nhlfe; + + DPLANE_CTX_VALID(ctx); + + nhlfe = zebra_mpls_lsp_add_nhlfe(&(ctx->u.lsp), + lsp_type, nh_type, gate, + ifindex, num_labels, out_labels); + + return nhlfe; +} + +zebra_nhlfe_t *dplane_ctx_add_backup_nhlfe(struct zebra_dplane_ctx *ctx, + enum lsp_types_t lsp_type, + enum nexthop_types_t nh_type, + const union g_addr *gate, + ifindex_t ifindex, + uint8_t num_labels, + mpls_label_t *out_labels) +{ + zebra_nhlfe_t *nhlfe; + + DPLANE_CTX_VALID(ctx); + + nhlfe = zebra_mpls_lsp_add_backup_nhlfe(&(ctx->u.lsp), + lsp_type, nh_type, gate, + ifindex, num_labels, + out_labels); + + return nhlfe; +} + +const zebra_nhlfe_t * +dplane_ctx_get_best_nhlfe(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.lsp.best_nhlfe; +} + +const zebra_nhlfe_t * +dplane_ctx_set_best_nhlfe(struct zebra_dplane_ctx *ctx, + zebra_nhlfe_t *nhlfe) +{ + DPLANE_CTX_VALID(ctx); + + ctx->u.lsp.best_nhlfe = nhlfe; + return ctx->u.lsp.best_nhlfe; +} + +uint32_t dplane_ctx_get_lsp_num_ecmp(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.lsp.num_ecmp; +} + +mpls_label_t dplane_ctx_get_pw_local_label(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.pw.local_label; +} + +mpls_label_t dplane_ctx_get_pw_remote_label(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.pw.remote_label; +} + +int dplane_ctx_get_pw_type(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.pw.type; +} + +int dplane_ctx_get_pw_af(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.pw.af; +} + +uint32_t dplane_ctx_get_pw_flags(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.pw.flags; +} + +int dplane_ctx_get_pw_status(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.pw.status; +} + +void dplane_ctx_set_pw_status(struct zebra_dplane_ctx *ctx, int status) +{ + DPLANE_CTX_VALID(ctx); + + ctx->u.pw.status = status; +} + +const union g_addr *dplane_ctx_get_pw_dest( + const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return &(ctx->u.pw.dest); +} + +const union pw_protocol_fields *dplane_ctx_get_pw_proto( + const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return &(ctx->u.pw.fields); +} + +const struct nexthop_group * +dplane_ctx_get_pw_nhg(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return &(ctx->u.pw.nhg); +} + +/* Accessors for interface information */ +uint32_t dplane_ctx_get_intf_metric(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.intf.metric; +} + +/* Is interface addr p2p? */ +bool dplane_ctx_intf_is_connected(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return (ctx->u.intf.flags & DPLANE_INTF_CONNECTED); +} + +bool dplane_ctx_intf_is_secondary(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return (ctx->u.intf.flags & DPLANE_INTF_SECONDARY); +} + +bool dplane_ctx_intf_is_broadcast(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return (ctx->u.intf.flags & DPLANE_INTF_BROADCAST); +} + +const struct prefix *dplane_ctx_get_intf_addr( + const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return &(ctx->u.intf.prefix); +} + +bool dplane_ctx_intf_has_dest(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return (ctx->u.intf.flags & DPLANE_INTF_HAS_DEST); +} + +const struct prefix *dplane_ctx_get_intf_dest( + const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + if (ctx->u.intf.flags & DPLANE_INTF_HAS_DEST) + return &(ctx->u.intf.dest_prefix); + else + return NULL; +} + +bool dplane_ctx_intf_has_label(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return (ctx->u.intf.flags & DPLANE_INTF_HAS_LABEL); +} + +const char *dplane_ctx_get_intf_label(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.intf.label; +} + +/* Accessors for MAC information */ +vlanid_t dplane_ctx_mac_get_vlan(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + return ctx->u.macinfo.vid; +} + +bool dplane_ctx_mac_is_sticky(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + return ctx->u.macinfo.is_sticky; +} + +uint32_t dplane_ctx_mac_get_nhg_id(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + return ctx->u.macinfo.nhg_id; +} + +uint32_t dplane_ctx_mac_get_update_flags(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + return ctx->u.macinfo.update_flags; +} + +const struct ethaddr *dplane_ctx_mac_get_addr( + const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + return &(ctx->u.macinfo.mac); +} + +const struct in_addr *dplane_ctx_mac_get_vtep_ip( + const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + return &(ctx->u.macinfo.vtep_ip); +} + +ifindex_t dplane_ctx_mac_get_br_ifindex(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + return ctx->u.macinfo.br_ifindex; +} + +/* Accessors for neighbor information */ +const struct ipaddr *dplane_ctx_neigh_get_ipaddr( + const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + return &(ctx->u.neigh.ip_addr); +} + +const struct ethaddr *dplane_ctx_neigh_get_mac( + const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + return &(ctx->u.neigh.mac); +} + +uint32_t dplane_ctx_neigh_get_flags(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + return ctx->u.neigh.flags; +} + +uint16_t dplane_ctx_neigh_get_state(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + return ctx->u.neigh.state; +} + +uint32_t dplane_ctx_neigh_get_update_flags(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + return ctx->u.neigh.update_flags; +} + +/* Accessors for PBR rule information */ +int dplane_ctx_rule_get_sock(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.rule.sock; +} + +const char *dplane_ctx_rule_get_ifname(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.rule.new.ifname; +} + +int dplane_ctx_rule_get_unique(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.rule.unique; +} + +int dplane_ctx_rule_get_seq(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); - return ctx->u.intf.ifindex; + return ctx->u.rule.seq; } -uint32_t dplane_ctx_get_intf_metric(const struct zebra_dplane_ctx *ctx) +uint32_t dplane_ctx_rule_get_priority(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); - return ctx->u.intf.metric; + return ctx->u.rule.new.priority; } -/* Is interface addr p2p? */ -bool dplane_ctx_intf_is_connected(const struct zebra_dplane_ctx *ctx) +uint32_t dplane_ctx_rule_get_old_priority(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); - return (ctx->u.intf.flags & DPLANE_INTF_CONNECTED); + return ctx->u.rule.old.priority; } -bool dplane_ctx_intf_is_secondary(const struct zebra_dplane_ctx *ctx) +uint32_t dplane_ctx_rule_get_table(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); - return (ctx->u.intf.flags & DPLANE_INTF_SECONDARY); + return ctx->u.rule.new.table; } -bool dplane_ctx_intf_is_broadcast(const struct zebra_dplane_ctx *ctx) +uint32_t dplane_ctx_rule_get_old_table(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); - return (ctx->u.intf.flags & DPLANE_INTF_BROADCAST); + return ctx->u.rule.old.table; } -const struct prefix *dplane_ctx_get_intf_addr( - const struct zebra_dplane_ctx *ctx) +uint32_t dplane_ctx_rule_get_filter_bm(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); - return &(ctx->u.intf.prefix); + return ctx->u.rule.new.filter_bm; } -bool dplane_ctx_intf_has_dest(const struct zebra_dplane_ctx *ctx) +uint32_t dplane_ctx_rule_get_old_filter_bm(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); - return (ctx->u.intf.flags & DPLANE_INTF_HAS_DEST); + return ctx->u.rule.old.filter_bm; } -const struct prefix *dplane_ctx_get_intf_dest( - const struct zebra_dplane_ctx *ctx) +uint32_t dplane_ctx_rule_get_fwmark(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); - if (ctx->u.intf.flags & DPLANE_INTF_HAS_DEST) - return &(ctx->u.intf.dest_prefix); - else - return NULL; + return ctx->u.rule.new.fwmark; } -bool dplane_ctx_intf_has_label(const struct zebra_dplane_ctx *ctx) +uint32_t dplane_ctx_rule_get_old_fwmark(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); - return (ctx->u.intf.flags & DPLANE_INTF_HAS_LABEL); + return ctx->u.rule.old.fwmark; } -const char *dplane_ctx_get_intf_label(const struct zebra_dplane_ctx *ctx) +uint8_t dplane_ctx_rule_get_dsfield(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); - return ctx->u.intf.label; + return ctx->u.rule.new.dsfield; +} + +uint8_t dplane_ctx_rule_get_old_dsfield(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.rule.old.dsfield; +} + +const struct prefix * +dplane_ctx_rule_get_src_ip(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return &(ctx->u.rule.new.src_ip); +} + +const struct prefix * +dplane_ctx_rule_get_old_src_ip(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return &(ctx->u.rule.old.src_ip); +} + +const struct prefix * +dplane_ctx_rule_get_dst_ip(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return &(ctx->u.rule.new.dst_ip); +} + +const struct prefix * +dplane_ctx_rule_get_old_dst_ip(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return &(ctx->u.rule.old.dst_ip); } /* @@ -1065,18 +1817,17 @@ static int dplane_ctx_ns_init(struct zebra_dplane_ctx *ctx, /* * Initialize a context block for a route update from zebra data structs. */ -static int dplane_ctx_route_init(struct zebra_dplane_ctx *ctx, - enum dplane_op_e op, - struct route_node *rn, - struct route_entry *re) +int dplane_ctx_route_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op, + struct route_node *rn, struct route_entry *re) { int ret = EINVAL; const struct route_table *table = NULL; - const rib_table_info_t *info; + const struct rib_table_info *info; const struct prefix *p, *src_p; struct zebra_ns *zns; struct zebra_vrf *zvrf; struct nexthop *nexthop; + zebra_l3vni_t *zl3vni; if (!ctx || !rn || !re) goto done; @@ -1116,12 +1867,34 @@ static int dplane_ctx_route_init(struct zebra_dplane_ctx *ctx, ctx->u.rinfo.zd_safi = info->safi; /* Copy nexthops; recursive info is included too */ - copy_nexthops(&(ctx->u.rinfo.zd_ng.nexthop), re->ng.nexthop, NULL); + copy_nexthops(&(ctx->u.rinfo.zd_ng.nexthop), + re->nhe->nhg.nexthop, NULL); + ctx->u.rinfo.zd_nhg_id = re->nhe->id; + + /* Copy backup nexthop info, if present */ + if (re->nhe->backup_info && re->nhe->backup_info->nhe) { + copy_nexthops(&(ctx->u.rinfo.backup_ng.nexthop), + re->nhe->backup_info->nhe->nhg.nexthop, NULL); + } - /* Ensure that the dplane's nexthops flags are clear. */ - for (ALL_NEXTHOPS(ctx->u.rinfo.zd_ng, nexthop)) + /* + * Ensure that the dplane nexthops' flags are clear and copy + * encapsulation information. + */ + for (ALL_NEXTHOPS(ctx->u.rinfo.zd_ng, nexthop)) { UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); + /* Check for available encapsulations. */ + if (!CHECK_FLAG(re->flags, ZEBRA_FLAG_EVPN_ROUTE)) + continue; + + zl3vni = zl3vni_from_vrf(nexthop->vrf_id); + if (zl3vni && is_l3vni_oper_up(zl3vni)) { + nexthop->nh_encap_type = NET_VXLAN; + nexthop->nh_encap.vni = zl3vni->vni; + } + } + /* Don't need some info when capturing a system notification */ if (op == DPLANE_OP_SYS_ROUTE_ADD || op == DPLANE_OP_SYS_ROUTE_DELETE) { @@ -1134,6 +1907,29 @@ static int dplane_ctx_route_init(struct zebra_dplane_ctx *ctx, zns = zvrf->zns; dplane_ctx_ns_init(ctx, zns, (op == DPLANE_OP_ROUTE_UPDATE)); +#ifdef HAVE_NETLINK + { + struct nhg_hash_entry *nhe = zebra_nhg_resolve(re->nhe); + + ctx->u.rinfo.nhe.id = nhe->id; + /* + * Check if the nhe is installed/queued before doing anything + * with this route. + * + * If its a delete we only use the prefix anyway, so this only + * matters for INSTALL/UPDATE. + */ + if (zebra_nhg_kernel_nexthops_enabled() + && (((op == DPLANE_OP_ROUTE_INSTALL) + || (op == DPLANE_OP_ROUTE_UPDATE)) + && !CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED) + && !CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_QUEUED))) { + ret = ENOENT; + goto done; + } + } +#endif /* HAVE_NETLINK */ + /* Trying out the sequence number idea, so we can try to detect * when a result is stale. */ @@ -1146,66 +1942,159 @@ static int dplane_ctx_route_init(struct zebra_dplane_ctx *ctx, return ret; } +/** + * dplane_ctx_nexthop_init() - Initialize a context block for a nexthop update + * + * @ctx: Dataplane context to init + * @op: Operation being performed + * @nhe: Nexthop group hash entry + * + * Return: Result status + */ +int dplane_ctx_nexthop_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op, + struct nhg_hash_entry *nhe) +{ + struct zebra_vrf *zvrf = NULL; + struct zebra_ns *zns = NULL; + int ret = EINVAL; + + if (!ctx || !nhe) + goto done; + + ctx->zd_op = op; + ctx->zd_status = ZEBRA_DPLANE_REQUEST_SUCCESS; + + /* Copy over nhe info */ + ctx->u.rinfo.nhe.id = nhe->id; + ctx->u.rinfo.nhe.afi = nhe->afi; + ctx->u.rinfo.nhe.vrf_id = nhe->vrf_id; + ctx->u.rinfo.nhe.type = nhe->type; + + nexthop_group_copy(&(ctx->u.rinfo.nhe.ng), &(nhe->nhg)); + + /* If this is a group, convert it to a grp array of ids */ + if (!zebra_nhg_depends_is_empty(nhe) + && !CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_RECURSIVE)) + ctx->u.rinfo.nhe.nh_grp_count = zebra_nhg_nhe2grp( + ctx->u.rinfo.nhe.nh_grp, nhe, MULTIPATH_NUM); + + zvrf = vrf_info_lookup(nhe->vrf_id); + + /* + * Fallback to default namespace if the vrf got ripped out from under + * us. + */ + zns = zvrf ? zvrf->zns : zebra_ns_lookup(NS_DEFAULT); + + /* + * TODO: Might not need to mark this as an update, since + * it probably won't require two messages + */ + dplane_ctx_ns_init(ctx, zns, (op == DPLANE_OP_NH_UPDATE)); + ctx->zd_is_update = (op == DPLANE_OP_NH_UPDATE); + + ret = AOK; + +done: + return ret; +} + /* * Capture information for an LSP update in a dplane context. */ -static int dplane_ctx_lsp_init(struct zebra_dplane_ctx *ctx, - enum dplane_op_e op, - zebra_lsp_t *lsp) +int dplane_ctx_lsp_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op, + zebra_lsp_t *lsp) { int ret = AOK; zebra_nhlfe_t *nhlfe, *new_nhlfe; - if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) - zlog_debug("init dplane ctx %s: in-label %u ecmp# %d", - dplane_op2str(op), lsp->ile.in_label, - lsp->num_ecmp); - ctx->zd_op = op; ctx->zd_status = ZEBRA_DPLANE_REQUEST_SUCCESS; /* Capture namespace info */ dplane_ctx_ns_init(ctx, zebra_ns_lookup(NS_DEFAULT), (op == DPLANE_OP_LSP_UPDATE)); + ctx->zd_is_update = (op == DPLANE_OP_LSP_UPDATE); memset(&ctx->u.lsp, 0, sizeof(ctx->u.lsp)); + nhlfe_list_init(&(ctx->u.lsp.nhlfe_list)); + nhlfe_list_init(&(ctx->u.lsp.backup_nhlfe_list)); + + /* This may be called to create/init a dplane context, not necessarily + * to copy an lsp object. + */ + if (lsp == NULL) { + ret = AOK; + goto done; + } + + if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) + zlog_debug("init dplane ctx %s: in-label %u ecmp# %d", + dplane_op2str(op), lsp->ile.in_label, + lsp->num_ecmp); + ctx->u.lsp.ile = lsp->ile; ctx->u.lsp.addr_family = lsp->addr_family; ctx->u.lsp.num_ecmp = lsp->num_ecmp; ctx->u.lsp.flags = lsp->flags; /* Copy source LSP's nhlfes, and capture 'best' nhlfe */ - for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next) { + frr_each(nhlfe_list, &lsp->nhlfe_list, nhlfe) { /* Not sure if this is meaningful... */ if (nhlfe->nexthop == NULL) continue; - new_nhlfe = - zebra_mpls_lsp_add_nhlfe( - &(ctx->u.lsp), - nhlfe->type, - nhlfe->nexthop->type, - &(nhlfe->nexthop->gate), - nhlfe->nexthop->ifindex, - nhlfe->nexthop->nh_label->label[0]); - + new_nhlfe = zebra_mpls_lsp_add_nh(&(ctx->u.lsp), nhlfe->type, + nhlfe->nexthop); if (new_nhlfe == NULL || new_nhlfe->nexthop == NULL) { ret = ENOMEM; break; } - /* Need to copy flags too */ + /* Need to copy flags and backup info too */ new_nhlfe->flags = nhlfe->flags; new_nhlfe->nexthop->flags = nhlfe->nexthop->flags; + if (CHECK_FLAG(new_nhlfe->nexthop->flags, + NEXTHOP_FLAG_HAS_BACKUP)) { + new_nhlfe->nexthop->backup_num = + nhlfe->nexthop->backup_num; + memcpy(new_nhlfe->nexthop->backup_idx, + nhlfe->nexthop->backup_idx, + new_nhlfe->nexthop->backup_num); + } + if (nhlfe == lsp->best_nhlfe) ctx->u.lsp.best_nhlfe = new_nhlfe; } + if (ret != AOK) + goto done; + + /* Capture backup nhlfes/nexthops */ + frr_each(nhlfe_list, &lsp->backup_nhlfe_list, nhlfe) { + /* Not sure if this is meaningful... */ + if (nhlfe->nexthop == NULL) + continue; + + new_nhlfe = zebra_mpls_lsp_add_backup_nh(&(ctx->u.lsp), + nhlfe->type, + nhlfe->nexthop); + if (new_nhlfe == NULL || new_nhlfe->nexthop == NULL) { + ret = ENOMEM; + break; + } + + /* Need to copy flags too */ + new_nhlfe->flags = nhlfe->flags; + new_nhlfe->nexthop->flags = nhlfe->nexthop->flags; + } + /* On error the ctx will be cleaned-up, so we don't need to * deal with any allocated nhlfe or nexthop structs here. */ +done: return ret; } @@ -1222,6 +2111,7 @@ static int dplane_ctx_pw_init(struct zebra_dplane_ctx *ctx, struct route_table *table; struct route_node *rn; struct route_entry *re; + const struct nexthop_group *nhg; if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) zlog_debug("init dplane ctx %s: pw '%s', loc %u, rem %u", @@ -1239,10 +2129,10 @@ static int dplane_ctx_pw_init(struct zebra_dplane_ctx *ctx, memset(&ctx->u.pw, 0, sizeof(ctx->u.pw)); /* This name appears to be c-string, so we use string copy. */ - strlcpy(ctx->u.pw.ifname, pw->ifname, sizeof(ctx->u.pw.ifname)); + strlcpy(ctx->zd_ifname, pw->ifname, sizeof(ctx->zd_ifname)); ctx->zd_vrf_id = pw->vrf_id; - ctx->u.pw.ifindex = pw->ifindex; + ctx->zd_ifindex = pw->ifindex; ctx->u.pw.type = pw->type; ctx->u.pw.af = pw->af; ctx->u.pw.local_label = pw->local_label; @@ -1272,10 +2162,18 @@ static int dplane_ctx_pw_init(struct zebra_dplane_ctx *ctx, break; } - if (re) - copy_nexthops(&(ctx->u.pw.nhg.nexthop), - re->ng.nexthop, NULL); - + if (re) { + nhg = rib_get_fib_nhg(re); + if (nhg && nhg->nexthop) + copy_nexthops(&(ctx->u.pw.nhg.nexthop), + nhg->nexthop, NULL); + + /* Include any installed backup nexthops */ + nhg = rib_get_fib_backup_nhg(re); + if (nhg && nhg->nexthop) + copy_nexthops(&(ctx->u.pw.nhg.nexthop), + nhg->nexthop, NULL); + } route_unlock_node(rn); } } @@ -1283,11 +2181,82 @@ static int dplane_ctx_pw_init(struct zebra_dplane_ctx *ctx, return AOK; } +/** + * dplane_ctx_rule_init_single() - Initialize a dataplane representation of a + * PBR rule. + * + * @dplane_rule: Dataplane internal representation of a rule + * @rule: PBR rule + */ +static void dplane_ctx_rule_init_single(struct dplane_ctx_rule *dplane_rule, + struct zebra_pbr_rule *rule) +{ + dplane_rule->priority = rule->rule.priority; + dplane_rule->table = rule->rule.action.table; + + dplane_rule->filter_bm = rule->rule.filter.filter_bm; + dplane_rule->fwmark = rule->rule.filter.fwmark; + dplane_rule->dsfield = rule->rule.filter.dsfield; + prefix_copy(&(dplane_rule->dst_ip), &rule->rule.filter.dst_ip); + prefix_copy(&(dplane_rule->src_ip), &rule->rule.filter.src_ip); + strlcpy(dplane_rule->ifname, rule->ifname, INTERFACE_NAMSIZ); +} + +/** + * dplane_ctx_rule_init() - Initialize a context block for a PBR rule update. + * + * @ctx: Dataplane context to init + * @op: Operation being performed + * @new_rule: PBR rule + * + * Return: Result status + */ +static int dplane_ctx_rule_init(struct zebra_dplane_ctx *ctx, + enum dplane_op_e op, + struct zebra_pbr_rule *new_rule, + struct zebra_pbr_rule *old_rule) +{ + if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) { + char buf1[PREFIX_STRLEN]; + char buf2[PREFIX_STRLEN]; + + zlog_debug( + "init dplane ctx %s: IF %s Prio %u Fwmark %u Src %s Dst %s Table %u", + dplane_op2str(op), new_rule->ifname, + new_rule->rule.priority, new_rule->rule.filter.fwmark, + prefix2str(&new_rule->rule.filter.src_ip, buf1, + sizeof(buf1)), + prefix2str(&new_rule->rule.filter.dst_ip, buf2, + sizeof(buf2)), + new_rule->rule.action.table); + } + + ctx->zd_op = op; + ctx->zd_status = ZEBRA_DPLANE_REQUEST_SUCCESS; + + dplane_ctx_ns_init(ctx, zebra_ns_lookup(NS_DEFAULT), + op == DPLANE_OP_RULE_UPDATE); + ctx->zd_is_update = (op == DPLANE_OP_RULE_UPDATE); + + ctx->zd_vrf_id = new_rule->vrf_id; + memcpy(ctx->zd_ifname, new_rule->ifname, sizeof(new_rule->ifname)); + + ctx->u.rule.sock = new_rule->sock; + ctx->u.rule.unique = new_rule->rule.unique; + ctx->u.rule.seq = new_rule->rule.seq; + + dplane_ctx_rule_init_single(&ctx->u.rule.new, new_rule); + if (op == DPLANE_OP_RULE_UPDATE) + dplane_ctx_rule_init_single(&ctx->u.rule.old, old_rule); + + return AOK; +} + /* - * Enqueue a new route update, + * Enqueue a new update, * and ensure an event is active for the dataplane pthread. */ -static int dplane_route_enqueue(struct zebra_dplane_ctx *ctx) +static int dplane_update_enqueue(struct zebra_dplane_ctx *ctx) { int ret = EINVAL; uint32_t high, curr; @@ -1295,22 +2264,17 @@ static int dplane_route_enqueue(struct zebra_dplane_ctx *ctx) /* Enqueue for processing by the dataplane pthread */ DPLANE_LOCK(); { - TAILQ_INSERT_TAIL(&zdplane_info.dg_route_ctx_q, ctx, + TAILQ_INSERT_TAIL(&zdplane_info.dg_update_ctx_q, ctx, zd_q_entries); } DPLANE_UNLOCK(); - curr = atomic_add_fetch_explicit( -#ifdef __clang__ - /* TODO -- issue with the clang atomic/intrinsics currently; - * casting away the 'Atomic'-ness of the variable works. - */ - (uint32_t *)&(zdplane_info.dg_routes_queued), -#else + curr = atomic_fetch_add_explicit( &(zdplane_info.dg_routes_queued), -#endif 1, memory_order_seq_cst); + curr++; /* We got the pre-incremented value */ + /* Maybe update high-water counter also */ high = atomic_load_explicit(&zdplane_info.dg_routes_queued_max, memory_order_seq_cst); @@ -1344,10 +2308,6 @@ dplane_route_update_internal(struct route_node *rn, /* Obtain context block */ ctx = dplane_ctx_alloc(); - if (ctx == NULL) { - ret = ENOMEM; - goto done; - } /* Init context with info from zebra data structs */ ret = dplane_ctx_route_init(ctx, op, rn, re); @@ -1374,15 +2334,25 @@ dplane_route_update_internal(struct route_node *rn, * We'll need these to do per-nexthop deletes. */ copy_nexthops(&(ctx->u.rinfo.zd_old_ng.nexthop), - old_re->ng.nexthop, NULL); + old_re->nhe->nhg.nexthop, NULL); + + if (zebra_nhg_get_backup_nhg(old_re->nhe) != NULL) { + struct nexthop_group *nhg; + struct nexthop **nh; + + nhg = zebra_nhg_get_backup_nhg(old_re->nhe); + nh = &(ctx->u.rinfo.old_backup_ng.nexthop); + + if (nhg->nexthop) + copy_nexthops(nh, nhg->nexthop, NULL); + } #endif /* !HAVE_NETLINK */ } /* Enqueue context for processing */ - ret = dplane_route_enqueue(ctx); + ret = dplane_update_enqueue(ctx); } -done: /* Update counter */ atomic_fetch_add_explicit(&zdplane_info.dg_routes_in, 1, memory_order_relaxed); @@ -1399,6 +2369,49 @@ dplane_route_update_internal(struct route_node *rn, return result; } +/** + * dplane_nexthop_update_internal() - Helper for enqueuing nexthop changes + * + * @nhe: Nexthop group hash entry where the change occured + * @op: The operation to be enqued + * + * Return: Result of the change + */ +static enum zebra_dplane_result +dplane_nexthop_update_internal(struct nhg_hash_entry *nhe, enum dplane_op_e op) +{ + enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE; + int ret = EINVAL; + struct zebra_dplane_ctx *ctx = NULL; + + /* Obtain context block */ + ctx = dplane_ctx_alloc(); + if (!ctx) { + ret = ENOMEM; + goto done; + } + + ret = dplane_ctx_nexthop_init(ctx, op, nhe); + if (ret == AOK) + ret = dplane_update_enqueue(ctx); + +done: + /* Update counter */ + atomic_fetch_add_explicit(&zdplane_info.dg_nexthops_in, 1, + memory_order_relaxed); + + if (ret == AOK) + result = ZEBRA_DPLANE_REQUEST_QUEUED; + else { + atomic_fetch_add_explicit(&zdplane_info.dg_nexthop_errors, 1, + memory_order_relaxed); + if (ctx) + dplane_ctx_free(&ctx); + } + + return result; +} + /* * Enqueue a route 'add' for the dataplane. */ @@ -1501,6 +2514,112 @@ enum zebra_dplane_result dplane_sys_route_del(struct route_node *rn, return ret; } +/* + * Update from an async notification, to bring other fibs up-to-date. + */ +enum zebra_dplane_result +dplane_route_notif_update(struct route_node *rn, + struct route_entry *re, + enum dplane_op_e op, + struct zebra_dplane_ctx *ctx) +{ + enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE; + int ret = EINVAL; + struct zebra_dplane_ctx *new_ctx = NULL; + struct nexthop *nexthop; + struct nexthop_group *nhg; + + if (rn == NULL || re == NULL) + goto done; + + new_ctx = dplane_ctx_alloc(); + if (new_ctx == NULL) + goto done; + + /* Init context with info from zebra data structs */ + dplane_ctx_route_init(new_ctx, op, rn, re); + + /* For add/update, need to adjust the nexthops so that we match + * the notification state, which may not be the route-entry/RIB + * state. + */ + if (op == DPLANE_OP_ROUTE_UPDATE || + op == DPLANE_OP_ROUTE_INSTALL) { + + nexthops_free(new_ctx->u.rinfo.zd_ng.nexthop); + new_ctx->u.rinfo.zd_ng.nexthop = NULL; + + nhg = rib_get_fib_nhg(re); + if (nhg && nhg->nexthop) + copy_nexthops(&(new_ctx->u.rinfo.zd_ng.nexthop), + nhg->nexthop, NULL); + + /* Check for installed backup nexthops also */ + nhg = rib_get_fib_backup_nhg(re); + if (nhg && nhg->nexthop) { + copy_nexthops(&(new_ctx->u.rinfo.zd_ng.nexthop), + nhg->nexthop, NULL); + } + + for (ALL_NEXTHOPS(new_ctx->u.rinfo.zd_ng, nexthop)) + UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); + + } + + /* Capture info about the source of the notification, in 'ctx' */ + dplane_ctx_set_notif_provider(new_ctx, + dplane_ctx_get_notif_provider(ctx)); + + ret = dplane_update_enqueue(new_ctx); + +done: + if (ret == AOK) + result = ZEBRA_DPLANE_REQUEST_QUEUED; + else if (new_ctx) + dplane_ctx_free(&new_ctx); + + return result; +} + +/* + * Enqueue a nexthop add for the dataplane. + */ +enum zebra_dplane_result dplane_nexthop_add(struct nhg_hash_entry *nhe) +{ + enum zebra_dplane_result ret = ZEBRA_DPLANE_REQUEST_FAILURE; + + if (nhe) + ret = dplane_nexthop_update_internal(nhe, DPLANE_OP_NH_INSTALL); + return ret; +} + +/* + * Enqueue a nexthop update for the dataplane. + * + * Might not need this func since zebra's nexthop objects should be immutable? + */ +enum zebra_dplane_result dplane_nexthop_update(struct nhg_hash_entry *nhe) +{ + enum zebra_dplane_result ret = ZEBRA_DPLANE_REQUEST_FAILURE; + + if (nhe) + ret = dplane_nexthop_update_internal(nhe, DPLANE_OP_NH_UPDATE); + return ret; +} + +/* + * Enqueue a nexthop removal for the dataplane. + */ +enum zebra_dplane_result dplane_nexthop_delete(struct nhg_hash_entry *nhe) +{ + enum zebra_dplane_result ret = ZEBRA_DPLANE_REQUEST_FAILURE; + + if (nhe) + ret = dplane_nexthop_update_internal(nhe, DPLANE_OP_NH_DELETE); + + return ret; +} + /* * Enqueue LSP add for the dataplane. */ @@ -1534,6 +2653,69 @@ enum zebra_dplane_result dplane_lsp_delete(zebra_lsp_t *lsp) return ret; } +/* Update or un-install resulting from an async notification */ +enum zebra_dplane_result +dplane_lsp_notif_update(zebra_lsp_t *lsp, + enum dplane_op_e op, + struct zebra_dplane_ctx *notif_ctx) +{ + enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE; + int ret = EINVAL; + struct zebra_dplane_ctx *ctx = NULL; + struct nhlfe_list_head *head; + zebra_nhlfe_t *nhlfe, *new_nhlfe; + + /* Obtain context block */ + ctx = dplane_ctx_alloc(); + if (ctx == NULL) { + ret = ENOMEM; + goto done; + } + + /* Copy info from zebra LSP */ + ret = dplane_ctx_lsp_init(ctx, op, lsp); + if (ret != AOK) + goto done; + + /* Add any installed backup nhlfes */ + head = &(ctx->u.lsp.backup_nhlfe_list); + frr_each(nhlfe_list, head, nhlfe) { + + if (CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED) && + CHECK_FLAG(nhlfe->nexthop->flags, NEXTHOP_FLAG_FIB)) { + new_nhlfe = zebra_mpls_lsp_add_nh(&(ctx->u.lsp), + nhlfe->type, + nhlfe->nexthop); + + /* Need to copy flags too */ + new_nhlfe->flags = nhlfe->flags; + new_nhlfe->nexthop->flags = nhlfe->nexthop->flags; + } + } + + /* Capture info about the source of the notification */ + dplane_ctx_set_notif_provider( + ctx, + dplane_ctx_get_notif_provider(notif_ctx)); + + ret = dplane_update_enqueue(ctx); + +done: + /* Update counter */ + atomic_fetch_add_explicit(&zdplane_info.dg_lsps_in, 1, + memory_order_relaxed); + + if (ret == AOK) + result = ZEBRA_DPLANE_REQUEST_QUEUED; + else { + atomic_fetch_add_explicit(&zdplane_info.dg_lsp_errors, 1, + memory_order_relaxed); + if (ctx) + dplane_ctx_free(&ctx); + } + return result; +} + /* * Enqueue pseudowire install for the dataplane. */ @@ -1562,16 +2744,12 @@ static enum zebra_dplane_result lsp_update_internal(zebra_lsp_t *lsp, /* Obtain context block */ ctx = dplane_ctx_alloc(); - if (ctx == NULL) { - ret = ENOMEM; - goto done; - } ret = dplane_ctx_lsp_init(ctx, op, lsp); if (ret != AOK) goto done; - ret = dplane_route_enqueue(ctx); + ret = dplane_update_enqueue(ctx); done: /* Update counter */ @@ -1583,8 +2761,7 @@ static enum zebra_dplane_result lsp_update_internal(zebra_lsp_t *lsp, else { atomic_fetch_add_explicit(&zdplane_info.dg_lsp_errors, 1, memory_order_relaxed); - if (ctx) - dplane_ctx_free(&ctx); + dplane_ctx_free(&ctx); } return result; @@ -1601,16 +2778,12 @@ static enum zebra_dplane_result pw_update_internal(struct zebra_pw *pw, struct zebra_dplane_ctx *ctx = NULL; ctx = dplane_ctx_alloc(); - if (ctx == NULL) { - ret = ENOMEM; - goto done; - } ret = dplane_ctx_pw_init(ctx, op, pw); if (ret != AOK) goto done; - ret = dplane_route_enqueue(ctx); + ret = dplane_update_enqueue(ctx); done: /* Update counter */ @@ -1622,8 +2795,7 @@ static enum zebra_dplane_result pw_update_internal(struct zebra_pw *pw, else { atomic_fetch_add_explicit(&zdplane_info.dg_pw_errors, 1, memory_order_relaxed); - if (ctx) - dplane_ctx_free(&ctx); + dplane_ctx_free(&ctx); } return result; @@ -1685,16 +2857,400 @@ static enum zebra_dplane_result intf_addr_update_internal( prefix2str(ifc->address, addr_str, sizeof(addr_str)); - zlog_debug("init intf ctx %s: idx %d, addr %u:%s", - dplane_op2str(op), ifp->ifindex, ifp->vrf_id, - addr_str); + zlog_debug("init intf ctx %s: idx %d, addr %u:%s", + dplane_op2str(op), ifp->ifindex, ifp->vrf_id, + addr_str); + } + + ctx = dplane_ctx_alloc(); + + ctx->zd_op = op; + ctx->zd_status = ZEBRA_DPLANE_REQUEST_SUCCESS; + ctx->zd_vrf_id = ifp->vrf_id; + + zns = zebra_ns_lookup(ifp->vrf_id); + dplane_ctx_ns_init(ctx, zns, false); + + /* Init the interface-addr-specific area */ + memset(&ctx->u.intf, 0, sizeof(ctx->u.intf)); + + strlcpy(ctx->zd_ifname, ifp->name, sizeof(ctx->zd_ifname)); + ctx->zd_ifindex = ifp->ifindex; + ctx->u.intf.prefix = *(ifc->address); + + if (if_is_broadcast(ifp)) + ctx->u.intf.flags |= DPLANE_INTF_BROADCAST; + + if (CONNECTED_PEER(ifc)) { + ctx->u.intf.dest_prefix = *(ifc->destination); + ctx->u.intf.flags |= + (DPLANE_INTF_CONNECTED | DPLANE_INTF_HAS_DEST); + } + + if (CHECK_FLAG(ifc->flags, ZEBRA_IFA_SECONDARY)) + ctx->u.intf.flags |= DPLANE_INTF_SECONDARY; + + if (ifc->label) { + size_t len; + + ctx->u.intf.flags |= DPLANE_INTF_HAS_LABEL; + + /* Use embedded buffer if it's adequate; else allocate. */ + len = strlen(ifc->label); + + if (len < sizeof(ctx->u.intf.label_buf)) { + strlcpy(ctx->u.intf.label_buf, ifc->label, + sizeof(ctx->u.intf.label_buf)); + ctx->u.intf.label = ctx->u.intf.label_buf; + } else { + ctx->u.intf.label = strdup(ifc->label); + } + } + + ret = dplane_update_enqueue(ctx); + + /* Increment counter */ + atomic_fetch_add_explicit(&zdplane_info.dg_intf_addrs_in, 1, + memory_order_relaxed); + + if (ret == AOK) + result = ZEBRA_DPLANE_REQUEST_QUEUED; + else { + /* Error counter */ + atomic_fetch_add_explicit(&zdplane_info.dg_intf_addr_errors, + 1, memory_order_relaxed); + dplane_ctx_free(&ctx); + } + + return result; +} + +/* + * Enqueue vxlan/evpn mac add (or update). + */ +enum zebra_dplane_result dplane_rem_mac_add(const struct interface *ifp, + const struct interface *bridge_ifp, + vlanid_t vid, + const struct ethaddr *mac, + struct in_addr vtep_ip, + bool sticky, + uint32_t nhg_id, + bool was_static) +{ + enum zebra_dplane_result result; + uint32_t update_flags = 0; + + update_flags |= DPLANE_MAC_REMOTE; + if (was_static) + update_flags |= DPLANE_MAC_WAS_STATIC; + + /* Use common helper api */ + result = mac_update_common(DPLANE_OP_MAC_INSTALL, ifp, bridge_ifp, + vid, mac, vtep_ip, sticky, nhg_id, update_flags); + return result; +} + +/* + * Enqueue vxlan/evpn mac delete. + */ +enum zebra_dplane_result dplane_rem_mac_del(const struct interface *ifp, + const struct interface *bridge_ifp, + vlanid_t vid, + const struct ethaddr *mac, + struct in_addr vtep_ip) +{ + enum zebra_dplane_result result; + uint32_t update_flags = 0; + + update_flags |= DPLANE_MAC_REMOTE; + + /* Use common helper api */ + result = mac_update_common(DPLANE_OP_MAC_DELETE, ifp, bridge_ifp, + vid, mac, vtep_ip, false, 0, update_flags); + return result; +} + +/* + * Enqueue local mac add (or update). + */ +enum zebra_dplane_result dplane_local_mac_add(const struct interface *ifp, + const struct interface *bridge_ifp, + vlanid_t vid, + const struct ethaddr *mac, + bool sticky, + uint32_t set_static, + uint32_t set_inactive) +{ + enum zebra_dplane_result result; + uint32_t update_flags = 0; + struct in_addr vtep_ip; + + if (set_static) + update_flags |= DPLANE_MAC_SET_STATIC; + + if (set_inactive) + update_flags |= DPLANE_MAC_SET_INACTIVE; + + vtep_ip.s_addr = 0; + + /* Use common helper api */ + result = mac_update_common(DPLANE_OP_MAC_INSTALL, ifp, bridge_ifp, + vid, mac, vtep_ip, sticky, 0, + update_flags); + return result; +} + +/* + * Public api to init an empty context - either newly-allocated or + * reset/cleared - for a MAC update. + */ +void dplane_mac_init(struct zebra_dplane_ctx *ctx, + const struct interface *ifp, + const struct interface *br_ifp, + vlanid_t vid, + const struct ethaddr *mac, + struct in_addr vtep_ip, + bool sticky, + uint32_t nhg_id, + uint32_t update_flags) +{ + struct zebra_ns *zns; + + ctx->zd_status = ZEBRA_DPLANE_REQUEST_SUCCESS; + ctx->zd_vrf_id = ifp->vrf_id; + + zns = zebra_ns_lookup(ifp->vrf_id); + dplane_ctx_ns_init(ctx, zns, false); + + strlcpy(ctx->zd_ifname, ifp->name, sizeof(ctx->zd_ifname)); + ctx->zd_ifindex = ifp->ifindex; + + /* Init the mac-specific data area */ + memset(&ctx->u.macinfo, 0, sizeof(ctx->u.macinfo)); + + ctx->u.macinfo.br_ifindex = br_ifp->ifindex; + ctx->u.macinfo.vtep_ip = vtep_ip; + ctx->u.macinfo.mac = *mac; + ctx->u.macinfo.vid = vid; + ctx->u.macinfo.is_sticky = sticky; + ctx->u.macinfo.nhg_id = nhg_id; + ctx->u.macinfo.update_flags = update_flags; +} + +/* + * Common helper api for MAC address/vxlan updates + */ +static enum zebra_dplane_result +mac_update_common(enum dplane_op_e op, + const struct interface *ifp, + const struct interface *br_ifp, + vlanid_t vid, + const struct ethaddr *mac, + struct in_addr vtep_ip, + bool sticky, + uint32_t nhg_id, + uint32_t update_flags) +{ + enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE; + int ret; + struct zebra_dplane_ctx *ctx = NULL; + + if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) { + char buf1[ETHER_ADDR_STRLEN], buf2[PREFIX_STRLEN]; + + zlog_debug("init mac ctx %s: mac %s, ifp %s, vtep %s", + dplane_op2str(op), + prefix_mac2str(mac, buf1, sizeof(buf1)), + ifp->name, + inet_ntop(AF_INET, &vtep_ip, buf2, sizeof(buf2))); + } + + ctx = dplane_ctx_alloc(); + ctx->zd_op = op; + + /* Common init for the ctx */ + dplane_mac_init(ctx, ifp, br_ifp, vid, mac, vtep_ip, sticky, + nhg_id, update_flags); + + /* Enqueue for processing on the dplane pthread */ + ret = dplane_update_enqueue(ctx); + + /* Increment counter */ + atomic_fetch_add_explicit(&zdplane_info.dg_macs_in, 1, + memory_order_relaxed); + + if (ret == AOK) + result = ZEBRA_DPLANE_REQUEST_QUEUED; + else { + /* Error counter */ + atomic_fetch_add_explicit(&zdplane_info.dg_mac_errors, 1, + memory_order_relaxed); + dplane_ctx_free(&ctx); + } + + return result; +} + +/* + * Enqueue evpn neighbor add for the dataplane. + */ +enum zebra_dplane_result dplane_rem_neigh_add(const struct interface *ifp, + const struct ipaddr *ip, + const struct ethaddr *mac, + uint32_t flags, bool was_static) +{ + enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE; + uint32_t update_flags = 0; + + update_flags |= DPLANE_NEIGH_REMOTE; + + if (was_static) + update_flags |= DPLANE_NEIGH_WAS_STATIC; + + result = neigh_update_internal(DPLANE_OP_NEIGH_INSTALL, + ifp, mac, ip, flags, DPLANE_NUD_NOARP, + update_flags); + + return result; +} + +/* + * Enqueue local neighbor add for the dataplane. + */ +enum zebra_dplane_result dplane_local_neigh_add(const struct interface *ifp, + const struct ipaddr *ip, + const struct ethaddr *mac, + bool set_router, bool set_static, + bool set_inactive) +{ + enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE; + uint32_t update_flags = 0; + uint32_t ntf = 0; + uint16_t state; + + if (set_static) + update_flags |= DPLANE_NEIGH_SET_STATIC; + + if (set_inactive) { + update_flags |= DPLANE_NEIGH_SET_INACTIVE; + state = DPLANE_NUD_STALE; + } else { + state = DPLANE_NUD_REACHABLE; + } + + if (set_router) + ntf |= DPLANE_NTF_ROUTER; + + result = neigh_update_internal(DPLANE_OP_NEIGH_INSTALL, + ifp, mac, ip, ntf, + state, update_flags); + + return result; +} + +/* + * Enqueue evpn neighbor delete for the dataplane. + */ +enum zebra_dplane_result dplane_rem_neigh_delete(const struct interface *ifp, + const struct ipaddr *ip) +{ + enum zebra_dplane_result result; + uint32_t update_flags = 0; + + update_flags |= DPLANE_NEIGH_REMOTE; + + result = neigh_update_internal(DPLANE_OP_NEIGH_DELETE, + ifp, NULL, ip, 0, 0, update_flags); + + return result; +} + +/* + * Enqueue evpn VTEP add for the dataplane. + */ +enum zebra_dplane_result dplane_vtep_add(const struct interface *ifp, + const struct in_addr *ip, + vni_t vni) +{ + enum zebra_dplane_result result; + struct ethaddr mac = { {0, 0, 0, 0, 0, 0} }; + struct ipaddr addr; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Install %s into flood list for VNI %u intf %s(%u)", + inet_ntoa(*ip), vni, ifp->name, ifp->ifindex); + + SET_IPADDR_V4(&addr); + addr.ipaddr_v4 = *ip; + + result = neigh_update_internal(DPLANE_OP_VTEP_ADD, + ifp, &mac, &addr, 0, 0, 0); + + return result; +} + +/* + * Enqueue evpn VTEP add for the dataplane. + */ +enum zebra_dplane_result dplane_vtep_delete(const struct interface *ifp, + const struct in_addr *ip, + vni_t vni) +{ + enum zebra_dplane_result result; + struct ethaddr mac = { {0, 0, 0, 0, 0, 0} }; + struct ipaddr addr; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Uninstall %s from flood list for VNI %u intf %s(%u)", + inet_ntoa(*ip), vni, ifp->name, ifp->ifindex); + + SET_IPADDR_V4(&addr); + addr.ipaddr_v4 = *ip; + + result = neigh_update_internal(DPLANE_OP_VTEP_DELETE, + ifp, &mac, &addr, 0, 0, 0); + + return result; +} + +enum zebra_dplane_result dplane_neigh_discover(const struct interface *ifp, + const struct ipaddr *ip) +{ + enum zebra_dplane_result result; + + result = neigh_update_internal(DPLANE_OP_NEIGH_DISCOVER, ifp, NULL, ip, + DPLANE_NTF_USE, DPLANE_NUD_INCOMPLETE, 0); + + return result; +} + +/* + * Common helper api for neighbor updates + */ +static enum zebra_dplane_result +neigh_update_internal(enum dplane_op_e op, + const struct interface *ifp, + const struct ethaddr *mac, + const struct ipaddr *ip, + uint32_t flags, uint16_t state, + uint32_t update_flags) +{ + enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE; + int ret; + struct zebra_dplane_ctx *ctx = NULL; + struct zebra_ns *zns; + + if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) { + char buf1[ETHER_ADDR_STRLEN], buf2[PREFIX_STRLEN]; + + zlog_debug("init neigh ctx %s: ifp %s, mac %s, ip %s", + dplane_op2str(op), ifp->name, + prefix_mac2str(mac, buf1, sizeof(buf1)), + ipaddr2str(ip, buf2, sizeof(buf2))); } ctx = dplane_ctx_alloc(); - if (ctx == NULL) { - ret = ENOMEM; - goto done; - } ctx->zd_op = op; ctx->zd_status = ZEBRA_DPLANE_REQUEST_SUCCESS; @@ -1703,66 +3259,88 @@ static enum zebra_dplane_result intf_addr_update_internal( zns = zebra_ns_lookup(ifp->vrf_id); dplane_ctx_ns_init(ctx, zns, false); - /* Init the interface-addr-specific area */ - memset(&ctx->u.intf, 0, sizeof(ctx->u.intf)); + strlcpy(ctx->zd_ifname, ifp->name, sizeof(ctx->zd_ifname)); + ctx->zd_ifindex = ifp->ifindex; - strncpy(ctx->u.intf.ifname, ifp->name, sizeof(ctx->u.intf.ifname)); - ctx->u.intf.ifindex = ifp->ifindex; - ctx->u.intf.prefix = *(ifc->address); + /* Init the neighbor-specific data area */ + memset(&ctx->u.neigh, 0, sizeof(ctx->u.neigh)); - if (if_is_broadcast(ifp)) - ctx->u.intf.flags |= DPLANE_INTF_BROADCAST; + ctx->u.neigh.ip_addr = *ip; + if (mac) + ctx->u.neigh.mac = *mac; + ctx->u.neigh.flags = flags; + ctx->u.neigh.state = state; + ctx->u.neigh.update_flags = update_flags; - if (CONNECTED_PEER(ifc)) { - ctx->u.intf.dest_prefix = *(ifc->destination); - ctx->u.intf.flags |= - (DPLANE_INTF_CONNECTED | DPLANE_INTF_HAS_DEST); - } else if (ifc->destination) { - ctx->u.intf.dest_prefix = *(ifc->destination); - ctx->u.intf.flags |= DPLANE_INTF_HAS_DEST; - } + /* Enqueue for processing on the dplane pthread */ + ret = dplane_update_enqueue(ctx); - if (CHECK_FLAG(ifc->flags, ZEBRA_IFA_SECONDARY)) - ctx->u.intf.flags |= DPLANE_INTF_SECONDARY; + /* Increment counter */ + atomic_fetch_add_explicit(&zdplane_info.dg_neighs_in, 1, + memory_order_relaxed); - if (ifc->label) { - size_t len; + if (ret == AOK) + result = ZEBRA_DPLANE_REQUEST_QUEUED; + else { + /* Error counter */ + atomic_fetch_add_explicit(&zdplane_info.dg_neigh_errors, 1, + memory_order_relaxed); + dplane_ctx_free(&ctx); + } - ctx->u.intf.flags |= DPLANE_INTF_HAS_LABEL; + return result; +} - /* Use embedded buffer if it's adequate; else allocate. */ - len = strlen(ifc->label); +/* + * Common helper api for PBR rule updates + */ +static enum zebra_dplane_result +rule_update_internal(enum dplane_op_e op, struct zebra_pbr_rule *new_rule, + struct zebra_pbr_rule *old_rule) +{ + enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE; + struct zebra_dplane_ctx *ctx; + int ret; - if (len < sizeof(ctx->u.intf.label_buf)) { - strncpy(ctx->u.intf.label_buf, ifc->label, - sizeof(ctx->u.intf.label_buf)); - ctx->u.intf.label = ctx->u.intf.label_buf; - } else { - ctx->u.intf.label = strdup(ifc->label); - } - } + ctx = dplane_ctx_alloc(); - ret = dplane_route_enqueue(ctx); + ret = dplane_ctx_rule_init(ctx, op, new_rule, old_rule); + if (ret != AOK) + goto done; -done: + ret = dplane_update_enqueue(ctx); - /* Increment counter */ - atomic_fetch_add_explicit(&zdplane_info.dg_intf_addrs_in, 1, +done: + atomic_fetch_add_explicit(&zdplane_info.dg_rules_in, 1, memory_order_relaxed); if (ret == AOK) result = ZEBRA_DPLANE_REQUEST_QUEUED; else { - /* Error counter */ - atomic_fetch_add_explicit(&zdplane_info.dg_intf_addr_errors, - 1, memory_order_relaxed); - if (ctx) - dplane_ctx_free(&ctx); + atomic_fetch_add_explicit(&zdplane_info.dg_rule_errors, 1, + memory_order_relaxed); + dplane_ctx_free(&ctx); } return result; } +enum zebra_dplane_result dplane_pbr_rule_add(struct zebra_pbr_rule *rule) +{ + return rule_update_internal(DPLANE_OP_RULE_ADD, rule, NULL); +} + +enum zebra_dplane_result dplane_pbr_rule_delete(struct zebra_pbr_rule *rule) +{ + return rule_update_internal(DPLANE_OP_RULE_DELETE, rule, NULL); +} + +enum zebra_dplane_result dplane_pbr_rule_update(struct zebra_pbr_rule *old_rule, + struct zebra_pbr_rule *new_rule) +{ + return rule_update_internal(DPLANE_OP_RULE_UPDATE, new_rule, old_rule); +} + /* * Handler for 'show dplane' */ @@ -1796,7 +3374,49 @@ int dplane_show_helper(struct vty *vty, bool detailed) vty_out(vty, "Route update queue limit: %"PRIu64"\n", limit); vty_out(vty, "Route update queue depth: %"PRIu64"\n", queued); vty_out(vty, "Route update queue max: %"PRIu64"\n", queue_max); - vty_out(vty, "Dplane update yields: %"PRIu64"\n", yields); + vty_out(vty, "Dplane update yields: %"PRIu64"\n", yields); + + incoming = atomic_load_explicit(&zdplane_info.dg_lsps_in, + memory_order_relaxed); + errs = atomic_load_explicit(&zdplane_info.dg_lsp_errors, + memory_order_relaxed); + vty_out(vty, "LSP updates: %"PRIu64"\n", incoming); + vty_out(vty, "LSP update errors: %"PRIu64"\n", errs); + + incoming = atomic_load_explicit(&zdplane_info.dg_pws_in, + memory_order_relaxed); + errs = atomic_load_explicit(&zdplane_info.dg_pw_errors, + memory_order_relaxed); + vty_out(vty, "PW updates: %"PRIu64"\n", incoming); + vty_out(vty, "PW update errors: %"PRIu64"\n", errs); + + incoming = atomic_load_explicit(&zdplane_info.dg_intf_addrs_in, + memory_order_relaxed); + errs = atomic_load_explicit(&zdplane_info.dg_intf_addr_errors, + memory_order_relaxed); + vty_out(vty, "Intf addr updates: %"PRIu64"\n", incoming); + vty_out(vty, "Intf addr errors: %"PRIu64"\n", errs); + + incoming = atomic_load_explicit(&zdplane_info.dg_macs_in, + memory_order_relaxed); + errs = atomic_load_explicit(&zdplane_info.dg_mac_errors, + memory_order_relaxed); + vty_out(vty, "EVPN MAC updates: %"PRIu64"\n", incoming); + vty_out(vty, "EVPN MAC errors: %"PRIu64"\n", errs); + + incoming = atomic_load_explicit(&zdplane_info.dg_neighs_in, + memory_order_relaxed); + errs = atomic_load_explicit(&zdplane_info.dg_neigh_errors, + memory_order_relaxed); + vty_out(vty, "EVPN neigh updates: %"PRIu64"\n", incoming); + vty_out(vty, "EVPN neigh errors: %"PRIu64"\n", errs); + + incoming = atomic_load_explicit(&zdplane_info.dg_rules_in, + memory_order_relaxed); + errs = atomic_load_explicit(&zdplane_info.dg_rule_errors, + memory_order_relaxed); + vty_out(vty, "Rule updates: %" PRIu64 "\n", incoming); + vty_out(vty, "Rule errors: %" PRIu64 "\n", errs); return CMD_SUCCESS; } @@ -1827,8 +3447,9 @@ int dplane_show_provs_helper(struct vty *vty, bool detailed) out_max = atomic_load_explicit(&prov->dp_out_max, memory_order_relaxed); - vty_out(vty, "%s (%u): in: %"PRIu64", q_max: %"PRIu64", " - "out: %"PRIu64", q_max: %"PRIu64"\n", + vty_out(vty, + "%s (%u): in: %" PRIu64 ", q_max: %" PRIu64 + ", out: %" PRIu64 ", q_max: %" PRIu64 "\n", prov->dp_name, prov->dp_id, in, in_max, out, out_max); DPLANE_LOCK(); @@ -1839,12 +3460,25 @@ int dplane_show_provs_helper(struct vty *vty, bool detailed) return CMD_SUCCESS; } +/* + * Helper for 'show run' etc. + */ +int dplane_config_write_helper(struct vty *vty) +{ + if (zdplane_info.dg_max_queued_updates != DPLANE_DEFAULT_MAX_QUEUED) + vty_out(vty, "zebra dplane limit %u\n", + zdplane_info.dg_max_queued_updates); + + return 0; +} + /* * Provider registration */ int dplane_provider_register(const char *name, enum dplane_provider_prio prio, int flags, + int (*start_fp)(struct zebra_dplane_provider *), int (*fp)(struct zebra_dplane_provider *), int (*fini_fp)(struct zebra_dplane_provider *, bool early), @@ -1873,8 +3507,10 @@ int dplane_provider_register(const char *name, TAILQ_INIT(&(p->dp_ctx_in_q)); TAILQ_INIT(&(p->dp_ctx_out_q)); + p->dp_flags = flags; p->dp_priority = prio; p->dp_fp = fp; + p->dp_start = start_fp; p->dp_fini = fini_fp; p->dp_data = data; @@ -2007,6 +3643,12 @@ int dplane_provider_dequeue_in_list(struct zebra_dplane_provider *prov, return ret; } +uint32_t dplane_provider_out_ctx_queue_len(struct zebra_dplane_provider *prov) +{ + return atomic_load_explicit(&(prov->dp_out_counter), + memory_order_relaxed); +} + /* * Enqueue and maintain associated counter */ @@ -2068,111 +3710,219 @@ int dplane_provider_work_ready(void) } /* - * Kernel dataplane provider - */ - -/* - * Handler for kernel LSP updates + * Enqueue a context directly to zebra main. */ -static enum zebra_dplane_result -kernel_dplane_lsp_update(struct zebra_dplane_ctx *ctx) +void dplane_provider_enqueue_to_zebra(struct zebra_dplane_ctx *ctx) { - enum zebra_dplane_result res; + struct dplane_ctx_q temp_list; - /* Call into the synchronous kernel-facing code here */ - res = kernel_lsp_update(ctx); + /* Zebra's api takes a list, so we need to use a temporary list */ + TAILQ_INIT(&temp_list); - if (res != ZEBRA_DPLANE_REQUEST_SUCCESS) - atomic_fetch_add_explicit( - &zdplane_info.dg_lsp_errors, 1, - memory_order_relaxed); - - return res; + TAILQ_INSERT_TAIL(&temp_list, ctx, zd_q_entries); + (zdplane_info.dg_results_cb)(&temp_list); } /* - * Handler for kernel pseudowire updates + * Kernel dataplane provider */ -static enum zebra_dplane_result -kernel_dplane_pw_update(struct zebra_dplane_ctx *ctx) + +static void kernel_dplane_log_detail(struct zebra_dplane_ctx *ctx) { - enum zebra_dplane_result res; + char buf[PREFIX_STRLEN]; - if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) + switch (dplane_ctx_get_op(ctx)) { + + case DPLANE_OP_ROUTE_INSTALL: + case DPLANE_OP_ROUTE_UPDATE: + case DPLANE_OP_ROUTE_DELETE: + prefix2str(dplane_ctx_get_dest(ctx), buf, sizeof(buf)); + + zlog_debug("%u:%s Dplane route update ctx %p op %s", + dplane_ctx_get_vrf(ctx), buf, ctx, + dplane_op2str(dplane_ctx_get_op(ctx))); + break; + + case DPLANE_OP_NH_INSTALL: + case DPLANE_OP_NH_UPDATE: + case DPLANE_OP_NH_DELETE: + zlog_debug("ID (%u) Dplane nexthop update ctx %p op %s", + dplane_ctx_get_nhe_id(ctx), ctx, + dplane_op2str(dplane_ctx_get_op(ctx))); + break; + + case DPLANE_OP_LSP_INSTALL: + case DPLANE_OP_LSP_UPDATE: + case DPLANE_OP_LSP_DELETE: + break; + + case DPLANE_OP_PW_INSTALL: + case DPLANE_OP_PW_UNINSTALL: zlog_debug("Dplane pw %s: op %s af %d loc: %u rem: %u", - dplane_ctx_get_pw_ifname(ctx), - dplane_op2str(ctx->zd_op), - dplane_ctx_get_pw_af(ctx), + dplane_ctx_get_ifname(ctx), + dplane_op2str(ctx->zd_op), dplane_ctx_get_pw_af(ctx), dplane_ctx_get_pw_local_label(ctx), dplane_ctx_get_pw_remote_label(ctx)); + break; - res = kernel_pw_update(ctx); - - if (res != ZEBRA_DPLANE_REQUEST_SUCCESS) - atomic_fetch_add_explicit( - &zdplane_info.dg_pw_errors, 1, - memory_order_relaxed); + case DPLANE_OP_ADDR_INSTALL: + case DPLANE_OP_ADDR_UNINSTALL: + prefix2str(dplane_ctx_get_intf_addr(ctx), buf, sizeof(buf)); - return res; -} + zlog_debug("Dplane intf %s, idx %u, addr %s", + dplane_op2str(dplane_ctx_get_op(ctx)), + dplane_ctx_get_ifindex(ctx), buf); + break; -/* - * Handler for kernel route updates - */ -static enum zebra_dplane_result -kernel_dplane_route_update(struct zebra_dplane_ctx *ctx) -{ - enum zebra_dplane_result res; + case DPLANE_OP_MAC_INSTALL: + case DPLANE_OP_MAC_DELETE: + prefix_mac2str(dplane_ctx_mac_get_addr(ctx), buf, + sizeof(buf)); - if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) { - char dest_str[PREFIX_STRLEN]; + zlog_debug("Dplane %s, mac %s, ifindex %u", + dplane_op2str(dplane_ctx_get_op(ctx)), + buf, dplane_ctx_get_ifindex(ctx)); + break; - prefix2str(dplane_ctx_get_dest(ctx), - dest_str, sizeof(dest_str)); + case DPLANE_OP_NEIGH_INSTALL: + case DPLANE_OP_NEIGH_UPDATE: + case DPLANE_OP_NEIGH_DELETE: + case DPLANE_OP_VTEP_ADD: + case DPLANE_OP_VTEP_DELETE: + case DPLANE_OP_NEIGH_DISCOVER: + ipaddr2str(dplane_ctx_neigh_get_ipaddr(ctx), buf, + sizeof(buf)); - zlog_debug("%u:%s Dplane route update ctx %p op %s", - dplane_ctx_get_vrf(ctx), dest_str, - ctx, dplane_op2str(dplane_ctx_get_op(ctx))); - } + zlog_debug("Dplane %s, ip %s, ifindex %u", + dplane_op2str(dplane_ctx_get_op(ctx)), + buf, dplane_ctx_get_ifindex(ctx)); + break; - /* Call into the synchronous kernel-facing code here */ - res = kernel_route_update(ctx); + case DPLANE_OP_RULE_ADD: + case DPLANE_OP_RULE_DELETE: + case DPLANE_OP_RULE_UPDATE: + zlog_debug("Dplane rule update op %s, if %s(%u), ctx %p", + dplane_op2str(dplane_ctx_get_op(ctx)), + dplane_ctx_get_ifname(ctx), + dplane_ctx_get_ifindex(ctx), ctx); + break; - if (res != ZEBRA_DPLANE_REQUEST_SUCCESS) - atomic_fetch_add_explicit( - &zdplane_info.dg_route_errors, 1, - memory_order_relaxed); + case DPLANE_OP_SYS_ROUTE_ADD: + case DPLANE_OP_SYS_ROUTE_DELETE: + case DPLANE_OP_ROUTE_NOTIFY: + case DPLANE_OP_LSP_NOTIFY: - return res; + case DPLANE_OP_NONE: + break; + } } -/* - * Handler for kernel-facing interface address updates - */ -static enum zebra_dplane_result -kernel_dplane_address_update(struct zebra_dplane_ctx *ctx) +static void kernel_dplane_handle_result(struct zebra_dplane_ctx *ctx) { - enum zebra_dplane_result res; + enum zebra_dplane_result res = dplane_ctx_get_status(ctx); + switch (dplane_ctx_get_op(ctx)) { - if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) { - char dest_str[PREFIX_STRLEN]; + case DPLANE_OP_ROUTE_INSTALL: + case DPLANE_OP_ROUTE_UPDATE: + case DPLANE_OP_ROUTE_DELETE: + if (res != ZEBRA_DPLANE_REQUEST_SUCCESS) + atomic_fetch_add_explicit(&zdplane_info.dg_route_errors, + 1, memory_order_relaxed); - prefix2str(dplane_ctx_get_intf_addr(ctx), dest_str, - sizeof(dest_str)); + if ((dplane_ctx_get_op(ctx) != DPLANE_OP_ROUTE_DELETE) + && (res == ZEBRA_DPLANE_REQUEST_SUCCESS)) { + struct nexthop *nexthop; - zlog_debug("Dplane intf %s, idx %u, addr %s", - dplane_op2str(dplane_ctx_get_op(ctx)), - dplane_ctx_get_ifindex(ctx), dest_str); - } + /* Update installed nexthops to signal which have been + * installed. + */ + 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); + } + } + } + break; - res = kernel_address_update_ctx(ctx); + case DPLANE_OP_NH_INSTALL: + case DPLANE_OP_NH_UPDATE: + case DPLANE_OP_NH_DELETE: + if (res != ZEBRA_DPLANE_REQUEST_SUCCESS) + atomic_fetch_add_explicit( + &zdplane_info.dg_nexthop_errors, 1, + memory_order_relaxed); + break; - if (res != ZEBRA_DPLANE_REQUEST_SUCCESS) - atomic_fetch_add_explicit(&zdplane_info.dg_intf_addr_errors, - 1, memory_order_relaxed); + case DPLANE_OP_LSP_INSTALL: + case DPLANE_OP_LSP_UPDATE: + case DPLANE_OP_LSP_DELETE: + if (res != ZEBRA_DPLANE_REQUEST_SUCCESS) + atomic_fetch_add_explicit(&zdplane_info.dg_lsp_errors, + 1, memory_order_relaxed); + break; + + case DPLANE_OP_PW_INSTALL: + case DPLANE_OP_PW_UNINSTALL: + if (res != ZEBRA_DPLANE_REQUEST_SUCCESS) + atomic_fetch_add_explicit(&zdplane_info.dg_pw_errors, 1, + memory_order_relaxed); + break; + + case DPLANE_OP_ADDR_INSTALL: + case DPLANE_OP_ADDR_UNINSTALL: + if (res != ZEBRA_DPLANE_REQUEST_SUCCESS) + atomic_fetch_add_explicit( + &zdplane_info.dg_intf_addr_errors, 1, + memory_order_relaxed); + break; + + case DPLANE_OP_MAC_INSTALL: + case DPLANE_OP_MAC_DELETE: + if (res != ZEBRA_DPLANE_REQUEST_SUCCESS) + atomic_fetch_add_explicit(&zdplane_info.dg_mac_errors, + 1, memory_order_relaxed); + break; + + case DPLANE_OP_NEIGH_INSTALL: + case DPLANE_OP_NEIGH_UPDATE: + case DPLANE_OP_NEIGH_DELETE: + case DPLANE_OP_VTEP_ADD: + case DPLANE_OP_VTEP_DELETE: + case DPLANE_OP_NEIGH_DISCOVER: + if (res != ZEBRA_DPLANE_REQUEST_SUCCESS) + atomic_fetch_add_explicit(&zdplane_info.dg_neigh_errors, + 1, memory_order_relaxed); + break; + + case DPLANE_OP_RULE_ADD: + case DPLANE_OP_RULE_DELETE: + case DPLANE_OP_RULE_UPDATE: + if (res != ZEBRA_DPLANE_REQUEST_SUCCESS) + atomic_fetch_add_explicit(&zdplane_info.dg_rule_errors, + 1, memory_order_relaxed); + break; + + /* Ignore 'notifications' - no-op */ + case DPLANE_OP_SYS_ROUTE_ADD: + case DPLANE_OP_SYS_ROUTE_DELETE: + case DPLANE_OP_ROUTE_NOTIFY: + case DPLANE_OP_LSP_NOTIFY: + break; - return res; + case DPLANE_OP_NONE: + if (res != ZEBRA_DPLANE_REQUEST_SUCCESS) + atomic_fetch_add_explicit(&zdplane_info.dg_other_errors, + 1, memory_order_relaxed); + break; + } } /* @@ -2180,10 +3930,12 @@ kernel_dplane_address_update(struct zebra_dplane_ctx *ctx) */ static int kernel_dplane_process_func(struct zebra_dplane_provider *prov) { - enum zebra_dplane_result res; - struct zebra_dplane_ctx *ctx; + struct zebra_dplane_ctx *ctx, *tctx; + struct dplane_ctx_q work_list; int counter, limit; + TAILQ_INIT(&work_list); + limit = dplane_provider_get_work_limit(prov); if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) @@ -2191,62 +3943,22 @@ static int kernel_dplane_process_func(struct zebra_dplane_provider *prov) dplane_provider_get_name(prov)); for (counter = 0; counter < limit; counter++) { - ctx = dplane_provider_dequeue_in_ctx(prov); if (ctx == NULL) break; - /* A previous provider plugin may have asked to skip the - * kernel update. - */ - if (dplane_ctx_is_skip_kernel(ctx)) { - res = ZEBRA_DPLANE_REQUEST_SUCCESS; - goto skip_one; - } - - /* Dispatch to appropriate kernel-facing apis */ - switch (dplane_ctx_get_op(ctx)) { - - case DPLANE_OP_ROUTE_INSTALL: - case DPLANE_OP_ROUTE_UPDATE: - case DPLANE_OP_ROUTE_DELETE: - res = kernel_dplane_route_update(ctx); - break; - - case DPLANE_OP_LSP_INSTALL: - case DPLANE_OP_LSP_UPDATE: - case DPLANE_OP_LSP_DELETE: - res = kernel_dplane_lsp_update(ctx); - break; - - case DPLANE_OP_PW_INSTALL: - case DPLANE_OP_PW_UNINSTALL: - res = kernel_dplane_pw_update(ctx); - break; - - case DPLANE_OP_ADDR_INSTALL: - case DPLANE_OP_ADDR_UNINSTALL: - res = kernel_dplane_address_update(ctx); - break; - - /* Ignore system 'notifications' - the kernel already knows */ - case DPLANE_OP_SYS_ROUTE_ADD: - case DPLANE_OP_SYS_ROUTE_DELETE: - res = ZEBRA_DPLANE_REQUEST_SUCCESS; - break; + if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) + kernel_dplane_log_detail(ctx); - default: - atomic_fetch_add_explicit( - &zdplane_info.dg_other_errors, 1, - memory_order_relaxed); + TAILQ_INSERT_TAIL(&work_list, ctx, zd_q_entries); + } - res = ZEBRA_DPLANE_REQUEST_FAILURE; - break; - } + kernel_update_multi(&work_list); -skip_one: - dplane_ctx_set_status(ctx, res); + TAILQ_FOREACH_SAFE (ctx, &work_list, zd_q_entries, tctx) { + kernel_dplane_handle_result(ctx); + TAILQ_REMOVE(&work_list, ctx, zd_q_entries); dplane_provider_enqueue_out_ctx(prov, ctx); } @@ -2267,7 +3979,7 @@ static int kernel_dplane_process_func(struct zebra_dplane_provider *prov) return 0; } -#if DPLANE_TEST_PROVIDER +#ifdef DPLANE_TEST_PROVIDER /* * Test dataplane provider plugin @@ -2290,7 +4002,6 @@ static int test_dplane_process_func(struct zebra_dplane_provider *prov) limit = dplane_provider_get_work_limit(prov); for (counter = 0; counter < limit; counter++) { - ctx = dplane_provider_dequeue_in_ctx(prov); if (ctx == NULL) break; @@ -2342,7 +4053,7 @@ static void dplane_provider_init(void) ret = dplane_provider_register("Kernel", DPLANE_PRIO_KERNEL, - DPLANE_PROV_FLAGS_DEFAULT, + DPLANE_PROV_FLAGS_DEFAULT, NULL, kernel_dplane_process_func, NULL, NULL, NULL); @@ -2351,11 +4062,11 @@ static void dplane_provider_init(void) zlog_err("Unable to register kernel dplane provider: %d", ret); -#if DPLANE_TEST_PROVIDER +#ifdef DPLANE_TEST_PROVIDER /* Optional test provider ... */ ret = dplane_provider_register("Test", DPLANE_PRIO_PRE_KERNEL, - DPLANE_PROV_FLAGS_DEFAULT, + DPLANE_PROV_FLAGS_DEFAULT, NULL, test_dplane_process_func, test_dplane_shutdown_func, NULL /* data */, NULL); @@ -2383,12 +4094,20 @@ bool dplane_is_in_shutdown(void) */ void zebra_dplane_pre_finish(void) { + struct zebra_dplane_provider *prov; + if (IS_ZEBRA_DEBUG_DPLANE) - zlog_debug("Zebra dataplane pre-fini called"); + zlog_debug("Zebra dataplane pre-finish called"); zdplane_info.dg_is_shutdown = true; - /* TODO -- Notify provider(s) of pending shutdown */ + /* Notify provider(s) of pending shutdown. */ + TAILQ_FOREACH(prov, &zdplane_info.dg_providers_q, dp_prov_link) { + if (prov->dp_fini == NULL) + continue; + + prov->dp_fini(prov, true /* early */); + } } /* @@ -2406,7 +4125,7 @@ static bool dplane_work_pending(void) */ DPLANE_LOCK(); { - ctx = TAILQ_FIRST(&zdplane_info.dg_route_ctx_q); + ctx = TAILQ_FIRST(&zdplane_info.dg_update_ctx_q); prov = TAILQ_FIRST(&zdplane_info.dg_providers_q); } DPLANE_UNLOCK(); @@ -2534,9 +4253,9 @@ static int dplane_thread_loop(struct thread *event) /* Move new work from incoming list to temp list */ for (counter = 0; counter < limit; counter++) { - ctx = TAILQ_FIRST(&zdplane_info.dg_route_ctx_q); + ctx = TAILQ_FIRST(&zdplane_info.dg_update_ctx_q); if (ctx) { - TAILQ_REMOVE(&zdplane_info.dg_route_ctx_q, ctx, + TAILQ_REMOVE(&zdplane_info.dg_update_ctx_q, ctx, zd_q_entries); ctx->zd_provider = prov->dp_id; @@ -2674,7 +4393,6 @@ static int dplane_thread_loop(struct thread *event) TAILQ_INIT(&error_list); - /* Call through to zebra main */ (zdplane_info.dg_results_cb)(&work_list); @@ -2690,6 +4408,8 @@ static int dplane_thread_loop(struct thread *event) */ void zebra_dplane_shutdown(void) { + struct zebra_dplane_provider *dp; + if (IS_ZEBRA_DEBUG_DPLANE) zlog_debug("Zebra dataplane shutdown called"); @@ -2697,7 +4417,9 @@ void zebra_dplane_shutdown(void) zdplane_info.dg_run = false; - THREAD_OFF(zdplane_info.dg_t_update); + if (zdplane_info.dg_t_update) + thread_cancel_async(zdplane_info.dg_t_update->master, + &zdplane_info.dg_t_update, NULL); frr_pthread_stop(zdplane_info.dg_pthread, NULL); @@ -2706,7 +4428,16 @@ void zebra_dplane_shutdown(void) zdplane_info.dg_pthread = NULL; zdplane_info.dg_master = NULL; - /* TODO -- Notify provider(s) of final shutdown */ + /* Notify provider(s) of final shutdown. + * Note that this call is in the main pthread, so providers must + * be prepared for that. + */ + TAILQ_FOREACH(dp, &zdplane_info.dg_providers_q, dp_prov_link) { + if (dp->dp_fini == NULL) + continue; + + dp->dp_fini(dp, false); + } /* TODO -- Clean-up provider objects */ @@ -2722,7 +4453,7 @@ static void zebra_dplane_init_internal(void) pthread_mutex_init(&zdplane_info.dg_mutex, NULL); - TAILQ_INIT(&zdplane_info.dg_route_ctx_q); + TAILQ_INIT(&zdplane_info.dg_update_ctx_q); TAILQ_INIT(&zdplane_info.dg_providers_q); zdplane_info.dg_updates_per_cycle = DPLANE_DEFAULT_NEW_WORK; @@ -2739,15 +4470,16 @@ static void zebra_dplane_init_internal(void) */ void zebra_dplane_start(void) { - /* Start dataplane pthread */ - + struct zebra_dplane_provider *prov; struct frr_pthread_attr pattr = { .start = frr_pthread_attr_default.start, .stop = frr_pthread_attr_default.stop }; + /* Start dataplane pthread */ + zdplane_info.dg_pthread = frr_pthread_new(&pattr, "Zebra dplane thread", - "Zebra dplane"); + "zebra_dplane"); zdplane_info.dg_master = zdplane_info.dg_pthread->master; @@ -2757,6 +4489,23 @@ void zebra_dplane_start(void) thread_add_event(zdplane_info.dg_master, dplane_thread_loop, NULL, 0, &zdplane_info.dg_t_update); + /* Call start callbacks for registered providers */ + + DPLANE_LOCK(); + prov = TAILQ_FIRST(&zdplane_info.dg_providers_q); + DPLANE_UNLOCK(); + + while (prov) { + + if (prov->dp_start) + (prov->dp_start)(prov); + + /* Locate next provider */ + DPLANE_LOCK(); + prov = TAILQ_NEXT(prov, dp_prov_link); + DPLANE_UNLOCK(); + } + frr_pthread_run(zdplane_info.dg_pthread, NULL); } diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h index d45628fdd0..55e2d47b75 100644 --- a/zebra/zebra_dplane.h +++ b/zebra/zebra_dplane.h @@ -25,10 +25,12 @@ #include "lib/nexthop.h" #include "lib/nexthop_group.h" #include "lib/queue.h" +#include "lib/vlan.h" #include "zebra/zebra_ns.h" #include "zebra/rib.h" #include "zebra/zserv.h" #include "zebra/zebra_mpls.h" +#include "zebra/zebra_nhg.h" #ifdef __cplusplus extern "C" { @@ -105,11 +107,18 @@ enum dplane_op_e { DPLANE_OP_ROUTE_INSTALL, DPLANE_OP_ROUTE_UPDATE, DPLANE_OP_ROUTE_DELETE, + DPLANE_OP_ROUTE_NOTIFY, + + /* Nexthop update */ + DPLANE_OP_NH_INSTALL, + DPLANE_OP_NH_UPDATE, + DPLANE_OP_NH_DELETE, /* LSP update */ DPLANE_OP_LSP_INSTALL, DPLANE_OP_LSP_UPDATE, DPLANE_OP_LSP_DELETE, + DPLANE_OP_LSP_NOTIFY, /* Pseudowire update */ DPLANE_OP_PW_INSTALL, @@ -122,8 +131,59 @@ enum dplane_op_e { /* Interface address update */ DPLANE_OP_ADDR_INSTALL, DPLANE_OP_ADDR_UNINSTALL, + + /* MAC address update */ + DPLANE_OP_MAC_INSTALL, + DPLANE_OP_MAC_DELETE, + + /* EVPN neighbor updates */ + DPLANE_OP_NEIGH_INSTALL, + DPLANE_OP_NEIGH_UPDATE, + DPLANE_OP_NEIGH_DELETE, + + /* EVPN VTEP updates */ + DPLANE_OP_VTEP_ADD, + DPLANE_OP_VTEP_DELETE, + + /* Policy based routing rule update */ + DPLANE_OP_RULE_ADD, + DPLANE_OP_RULE_DELETE, + DPLANE_OP_RULE_UPDATE, + + /* Link layer address discovery */ + DPLANE_OP_NEIGH_DISCOVER, }; +/* + * The vxlan/evpn neighbor management code needs some values to use + * when programming neighbor changes. Offer some platform-neutral values + * here for use within the dplane apis and plugins. + */ + +/* Neighbor cache flags */ +#define DPLANE_NTF_EXT_LEARNED 0x01 +#define DPLANE_NTF_ROUTER 0x02 +#define DPLANE_NTF_USE 0x04 + +/* Neighbor cache states */ +#define DPLANE_NUD_REACHABLE 0x01 +#define DPLANE_NUD_STALE 0x02 +#define DPLANE_NUD_NOARP 0x04 +#define DPLANE_NUD_PROBE 0x08 +#define DPLANE_NUD_INCOMPLETE 0x10 + +/* MAC update flags - dplane_mac_info.update_flags */ +#define DPLANE_MAC_REMOTE (1 << 0) +#define DPLANE_MAC_WAS_STATIC (1 << 1) +#define DPLANE_MAC_SET_STATIC (1 << 2) +#define DPLANE_MAC_SET_INACTIVE (1 << 3) + +/* Neigh update flags - dplane_neigh_info.update_flags */ +#define DPLANE_NEIGH_REMOTE (1 << 0) +#define DPLANE_NEIGH_WAS_STATIC (1 << 1) +#define DPLANE_NEIGH_SET_STATIC (1 << 2) +#define DPLANE_NEIGH_SET_INACTIVE (1 << 3) + /* Enable system route notifications */ void dplane_enable_sys_route_notifs(void); @@ -139,6 +199,15 @@ void dplane_enable_sys_route_notifs(void); */ TAILQ_HEAD(dplane_ctx_q, zebra_dplane_ctx); +/* Allocate a context object */ +struct zebra_dplane_ctx *dplane_ctx_alloc(void); + +/* + * Reset an allocated context object for re-use. All internal allocations are + * freed. + */ +void dplane_ctx_reset(struct zebra_dplane_ctx *ctx); + /* Return a dataplane results context block after use; the caller's pointer will * be cleared. */ @@ -169,9 +238,15 @@ void dplane_ctx_set_status(struct zebra_dplane_ctx *ctx, const char *dplane_res2str(enum zebra_dplane_result res); enum dplane_op_e dplane_ctx_get_op(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_set_op(struct zebra_dplane_ctx *ctx, enum dplane_op_e op); const char *dplane_op2str(enum dplane_op_e op); const struct prefix *dplane_ctx_get_dest(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_set_dest(struct zebra_dplane_ctx *ctx, + const struct prefix *dest); +const char *dplane_ctx_get_ifname(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_set_ifname(struct zebra_dplane_ctx *ctx, const char *ifname); +ifindex_t dplane_ctx_get_ifindex(const struct zebra_dplane_ctx *ctx); /* Retrieve last/current provider id */ uint32_t dplane_ctx_get_provider(const struct zebra_dplane_ctx *ctx); @@ -186,51 +261,123 @@ bool dplane_ctx_is_skip_kernel(const struct zebra_dplane_ctx *ctx); * to mean "no src prefix" */ const struct prefix *dplane_ctx_get_src(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_set_src(struct zebra_dplane_ctx *ctx, const struct prefix *src); bool dplane_ctx_is_update(const struct zebra_dplane_ctx *ctx); uint32_t dplane_ctx_get_seq(const struct zebra_dplane_ctx *ctx); uint32_t dplane_ctx_get_old_seq(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_set_vrf(struct zebra_dplane_ctx *ctx, vrf_id_t vrf); vrf_id_t dplane_ctx_get_vrf(const struct zebra_dplane_ctx *ctx); +bool dplane_ctx_is_from_notif(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_set_notif_provider(struct zebra_dplane_ctx *ctx, + uint32_t id); +uint32_t dplane_ctx_get_notif_provider(const struct zebra_dplane_ctx *ctx); + /* Accessors for route update information */ +void dplane_ctx_set_type(struct zebra_dplane_ctx *ctx, int type); int dplane_ctx_get_type(const struct zebra_dplane_ctx *ctx); int dplane_ctx_get_old_type(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_set_afi(struct zebra_dplane_ctx *ctx, afi_t afi); afi_t dplane_ctx_get_afi(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_set_safi(struct zebra_dplane_ctx *ctx, safi_t safi); safi_t dplane_ctx_get_safi(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_set_table(struct zebra_dplane_ctx *ctx, uint32_t table); uint32_t dplane_ctx_get_table(const struct zebra_dplane_ctx *ctx); route_tag_t dplane_ctx_get_tag(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_set_tag(struct zebra_dplane_ctx *ctx, route_tag_t tag); route_tag_t dplane_ctx_get_old_tag(const struct zebra_dplane_ctx *ctx); uint16_t dplane_ctx_get_instance(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_set_instance(struct zebra_dplane_ctx *ctx, uint16_t instance); uint16_t dplane_ctx_get_old_instance(const struct zebra_dplane_ctx *ctx); uint32_t dplane_ctx_get_metric(const struct zebra_dplane_ctx *ctx); uint32_t dplane_ctx_get_old_metric(const struct zebra_dplane_ctx *ctx); uint32_t dplane_ctx_get_mtu(const struct zebra_dplane_ctx *ctx); uint32_t dplane_ctx_get_nh_mtu(const struct zebra_dplane_ctx *ctx); uint8_t dplane_ctx_get_distance(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_set_distance(struct zebra_dplane_ctx *ctx, uint8_t distance); uint8_t dplane_ctx_get_old_distance(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_set_nexthops(struct zebra_dplane_ctx *ctx, struct nexthop *nh); +void dplane_ctx_set_backup_nhg(struct zebra_dplane_ctx *ctx, + const struct nexthop_group *nhg); + +uint32_t dplane_ctx_get_nhg_id(const struct zebra_dplane_ctx *ctx); const struct nexthop_group *dplane_ctx_get_ng( const struct zebra_dplane_ctx *ctx); const struct nexthop_group *dplane_ctx_get_old_ng( const struct zebra_dplane_ctx *ctx); +/* Backup nexthop information (list of nexthops) if present. */ +const struct nexthop_group * +dplane_ctx_get_backup_ng(const struct zebra_dplane_ctx *ctx); +const struct nexthop_group * +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); +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); +const struct nexthop_group * +dplane_ctx_get_nhe_ng(const struct zebra_dplane_ctx *ctx); +const struct nh_grp * +dplane_ctx_get_nhe_nh_grp(const struct zebra_dplane_ctx *ctx); +uint8_t dplane_ctx_get_nhe_nh_grp_count(const struct zebra_dplane_ctx *ctx); + /* Accessors for LSP information */ + +/* Init the internal LSP data struct - necessary before adding to it. + * If 'lsp' is non-NULL, info will be copied from it to the internal + * context data area. + */ +int dplane_ctx_lsp_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op, + zebra_lsp_t *lsp); + mpls_label_t dplane_ctx_get_in_label(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_set_in_label(struct zebra_dplane_ctx *ctx, + mpls_label_t label); uint8_t dplane_ctx_get_addr_family(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_set_addr_family(struct zebra_dplane_ctx *ctx, + uint8_t family); uint32_t dplane_ctx_get_lsp_flags(const struct zebra_dplane_ctx *ctx); -const zebra_nhlfe_t *dplane_ctx_get_nhlfe(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_set_lsp_flags(struct zebra_dplane_ctx *ctx, + uint32_t flags); +const struct nhlfe_list_head *dplane_ctx_get_nhlfe_list( + const struct zebra_dplane_ctx *ctx); +const struct nhlfe_list_head *dplane_ctx_get_backup_nhlfe_list( + const struct zebra_dplane_ctx *ctx); + +zebra_nhlfe_t *dplane_ctx_add_nhlfe(struct zebra_dplane_ctx *ctx, + enum lsp_types_t lsp_type, + enum nexthop_types_t nh_type, + const union g_addr *gate, + ifindex_t ifindex, + uint8_t num_labels, + mpls_label_t *out_labels); + +zebra_nhlfe_t *dplane_ctx_add_backup_nhlfe(struct zebra_dplane_ctx *ctx, + enum lsp_types_t lsp_type, + enum nexthop_types_t nh_type, + const union g_addr *gate, + ifindex_t ifindex, + uint8_t num_labels, + mpls_label_t *out_labels); + const zebra_nhlfe_t *dplane_ctx_get_best_nhlfe( const struct zebra_dplane_ctx *ctx); +const zebra_nhlfe_t *dplane_ctx_set_best_nhlfe(struct zebra_dplane_ctx *ctx, + zebra_nhlfe_t *nhlfe); uint32_t dplane_ctx_get_lsp_num_ecmp(const struct zebra_dplane_ctx *ctx); /* Accessors for pseudowire information */ -const char *dplane_ctx_get_pw_ifname(const struct zebra_dplane_ctx *ctx); mpls_label_t dplane_ctx_get_pw_local_label(const struct zebra_dplane_ctx *ctx); mpls_label_t dplane_ctx_get_pw_remote_label(const struct zebra_dplane_ctx *ctx); int dplane_ctx_get_pw_type(const struct zebra_dplane_ctx *ctx); int dplane_ctx_get_pw_af(const struct zebra_dplane_ctx *ctx); uint32_t dplane_ctx_get_pw_flags(const struct zebra_dplane_ctx *ctx); int dplane_ctx_get_pw_status(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_set_pw_status(struct zebra_dplane_ctx *ctx, int status); const union g_addr *dplane_ctx_get_pw_dest( const struct zebra_dplane_ctx *ctx); const union pw_protocol_fields *dplane_ctx_get_pw_proto( @@ -239,8 +386,6 @@ const struct nexthop_group *dplane_ctx_get_pw_nhg( const struct zebra_dplane_ctx *ctx); /* Accessors for interface information */ -const char *dplane_ctx_get_ifname(const struct zebra_dplane_ctx *ctx); -ifindex_t dplane_ctx_get_ifindex(const struct zebra_dplane_ctx *ctx); uint32_t dplane_ctx_get_intf_metric(const struct zebra_dplane_ctx *ctx); /* Is interface addr p2p? */ bool dplane_ctx_intf_is_connected(const struct zebra_dplane_ctx *ctx); @@ -254,6 +399,50 @@ const struct prefix *dplane_ctx_get_intf_dest( bool dplane_ctx_intf_has_label(const struct zebra_dplane_ctx *ctx); const char *dplane_ctx_get_intf_label(const struct zebra_dplane_ctx *ctx); +/* Accessors for MAC information */ +vlanid_t dplane_ctx_mac_get_vlan(const struct zebra_dplane_ctx *ctx); +bool dplane_ctx_mac_is_sticky(const struct zebra_dplane_ctx *ctx); +uint32_t dplane_ctx_mac_get_update_flags(const struct zebra_dplane_ctx *ctx); +uint32_t dplane_ctx_mac_get_nhg_id(const struct zebra_dplane_ctx *ctx); +const struct ethaddr *dplane_ctx_mac_get_addr( + const struct zebra_dplane_ctx *ctx); +const struct in_addr *dplane_ctx_mac_get_vtep_ip( + const struct zebra_dplane_ctx *ctx); +ifindex_t dplane_ctx_mac_get_br_ifindex(const struct zebra_dplane_ctx *ctx); + +/* Accessors for neighbor information */ +const struct ipaddr *dplane_ctx_neigh_get_ipaddr( + const struct zebra_dplane_ctx *ctx); +const struct ethaddr *dplane_ctx_neigh_get_mac( + const struct zebra_dplane_ctx *ctx); +uint32_t dplane_ctx_neigh_get_flags(const struct zebra_dplane_ctx *ctx); +uint16_t dplane_ctx_neigh_get_state(const struct zebra_dplane_ctx *ctx); +uint32_t dplane_ctx_neigh_get_update_flags(const struct zebra_dplane_ctx *ctx); + +/* Accessors for policy based routing rule information */ +int dplane_ctx_rule_get_sock(const struct zebra_dplane_ctx *ctx); +int dplane_ctx_rule_get_unique(const struct zebra_dplane_ctx *ctx); +int dplane_ctx_rule_get_seq(const struct zebra_dplane_ctx *ctx); +const char *dplane_ctx_rule_get_ifname(const struct zebra_dplane_ctx *ctx); +uint32_t dplane_ctx_rule_get_priority(const struct zebra_dplane_ctx *ctx); +uint32_t dplane_ctx_rule_get_old_priority(const struct zebra_dplane_ctx *ctx); +uint32_t dplane_ctx_rule_get_table(const struct zebra_dplane_ctx *ctx); +uint32_t dplane_ctx_rule_get_old_table(const struct zebra_dplane_ctx *ctx); +uint32_t dplane_ctx_rule_get_filter_bm(const struct zebra_dplane_ctx *ctx); +uint32_t dplane_ctx_rule_get_old_filter_bm(const struct zebra_dplane_ctx *ctx); +uint32_t dplane_ctx_rule_get_fwmark(const struct zebra_dplane_ctx *ctx); +uint32_t dplane_ctx_rule_get_old_fwmark(const struct zebra_dplane_ctx *ctx); +uint8_t dplane_ctx_rule_get_dsfield(const struct zebra_dplane_ctx *ctx); +uint8_t dplane_ctx_rule_get_old_dsfield(const struct zebra_dplane_ctx *ctx); +const struct prefix * +dplane_ctx_rule_get_src_ip(const struct zebra_dplane_ctx *ctx); +const struct prefix * +dplane_ctx_rule_get_old_src_ip(const struct zebra_dplane_ctx *ctx); +const struct prefix * +dplane_ctx_rule_get_dst_ip(const struct zebra_dplane_ctx *ctx); +const struct prefix * +dplane_ctx_rule_get_old_dst_ip(const struct zebra_dplane_ctx *ctx); + /* Namespace info - esp. for netlink communication */ const struct zebra_dplane_info *dplane_ctx_get_ns( const struct zebra_dplane_ctx *ctx); @@ -282,6 +471,23 @@ enum zebra_dplane_result dplane_sys_route_add(struct route_node *rn, enum zebra_dplane_result dplane_sys_route_del(struct route_node *rn, struct route_entry *re); +/* Update from an async notification, to bring other fibs up-to-date */ +enum zebra_dplane_result dplane_route_notif_update( + struct route_node *rn, + struct route_entry *re, + enum dplane_op_e op, + struct zebra_dplane_ctx *ctx); + + +/* Forward ref of nhg_hash_entry */ +struct nhg_hash_entry; +/* + * Enqueue a nexthop change operation for the dataplane. + */ +enum zebra_dplane_result dplane_nexthop_add(struct nhg_hash_entry *nhe); +enum zebra_dplane_result dplane_nexthop_update(struct nhg_hash_entry *nhe); +enum zebra_dplane_result dplane_nexthop_delete(struct nhg_hash_entry *nhe); + /* * Enqueue LSP change operations for the dataplane. */ @@ -289,6 +495,11 @@ enum zebra_dplane_result dplane_lsp_add(zebra_lsp_t *lsp); enum zebra_dplane_result dplane_lsp_update(zebra_lsp_t *lsp); enum zebra_dplane_result dplane_lsp_delete(zebra_lsp_t *lsp); +/* Update or un-install resulting from an async notification */ +enum zebra_dplane_result dplane_lsp_notif_update(zebra_lsp_t *lsp, + enum dplane_op_e op, + struct zebra_dplane_ctx *ctx); + /* * Enqueue pseudowire operations for the dataplane. */ @@ -303,6 +514,95 @@ enum zebra_dplane_result dplane_intf_addr_set(const struct interface *ifp, enum zebra_dplane_result dplane_intf_addr_unset(const struct interface *ifp, const struct connected *ifc); +/* + * Enqueue evpn mac operations for the dataplane. + */ +enum zebra_dplane_result dplane_rem_mac_add(const struct interface *ifp, + const struct interface *bridge_ifp, + vlanid_t vid, + const struct ethaddr *mac, + struct in_addr vtep_ip, + bool sticky, + uint32_t nhg_id, + bool was_static); + +enum zebra_dplane_result dplane_local_mac_add(const struct interface *ifp, + const struct interface *bridge_ifp, + vlanid_t vid, + const struct ethaddr *mac, + bool sticky, + uint32_t set_static, + uint32_t set_inactive); + +enum zebra_dplane_result dplane_rem_mac_del(const struct interface *ifp, + const struct interface *bridge_ifp, + vlanid_t vid, + const struct ethaddr *mac, + struct in_addr vtep_ip); + +/* Helper api to init an empty or new context for a MAC update */ +void dplane_mac_init(struct zebra_dplane_ctx *ctx, + const struct interface *ifp, + const struct interface *br_ifp, + vlanid_t vid, + const struct ethaddr *mac, + struct in_addr vtep_ip, + bool sticky, + uint32_t nhg_id, uint32_t update_flags); + +/* + * Enqueue evpn neighbor updates for the dataplane. + */ +enum zebra_dplane_result dplane_rem_neigh_add(const struct interface *ifp, + const struct ipaddr *ip, + const struct ethaddr *mac, + uint32_t flags, bool was_static); +enum zebra_dplane_result dplane_local_neigh_add(const struct interface *ifp, + const struct ipaddr *ip, + const struct ethaddr *mac, + bool set_router, bool set_static, + bool set_inactive); +enum zebra_dplane_result dplane_rem_neigh_delete(const struct interface *ifp, + const struct ipaddr *ip); + +/* + * Enqueue evpn VTEP updates for the dataplane. + */ +enum zebra_dplane_result dplane_vtep_add(const struct interface *ifp, + const struct in_addr *ip, + vni_t vni); +enum zebra_dplane_result dplane_vtep_delete(const struct interface *ifp, + const struct in_addr *ip, + vni_t vni); + +/* + * Enqueue a neighbour discovery request for the dataplane. + */ +enum zebra_dplane_result dplane_neigh_discover(const struct interface *ifp, + const struct ipaddr *ip); + +/* Forward ref of zebra_pbr_rule */ +struct zebra_pbr_rule; + +/* + * Enqueue policy based routing rule for the dataplane. + * It is possible that the user-defined sequence number and the one in the + * forwarding plane may not coincide, hence the API requires a separate + * rule priority - maps to preference/FRA_PRIORITY on Linux. + */ +enum zebra_dplane_result dplane_pbr_rule_add(struct zebra_pbr_rule *rule); +enum zebra_dplane_result dplane_pbr_rule_delete(struct zebra_pbr_rule *rule); +enum zebra_dplane_result +dplane_pbr_rule_update(struct zebra_pbr_rule *old_rule, + struct zebra_pbr_rule *new_rule); + +/* Encode route information into data plane context. */ +int dplane_ctx_route_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op, + struct route_node *rn, struct route_entry *re); + +/* Encode next hop information into data plane context. */ +int dplane_ctx_nexthop_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op, + struct nhg_hash_entry *nhe); /* Retrieve the limit on the number of pending, unprocessed updates. */ uint32_t dplane_get_in_queue_limit(void); @@ -320,7 +620,7 @@ uint32_t dplane_get_in_queue_len(void); */ int dplane_show_helper(struct vty *vty, bool detailed); int dplane_show_provs_helper(struct vty *vty, bool detailed); - +int dplane_config_write_helper(struct vty *vty); /* * Dataplane providers: modules that process or consume dataplane events. @@ -351,7 +651,6 @@ enum dplane_provider_prio { /* Provider will be spawning its own worker thread */ #define DPLANE_PROV_FLAG_THREADED 0x1 - /* Provider registration: ordering or priority value, callbacks, and optional * opaque data value. If 'prov_p', return the newly-allocated provider object * on success. @@ -363,7 +662,13 @@ enum dplane_provider_prio { * then checks the provider's outbound queue for completed work. */ -/* Providers offer an entry-point for shutdown and cleanup. This is called +/* + * Providers can offer a 'start' callback; if present, the dataplane will + * call it when it is starting - when its pthread and event-scheduling + * thread_master are available. + */ + +/* Providers can offer an entry-point for shutdown and cleanup. This is called * with 'early' during shutdown, to indicate that the dataplane subsystem * is allowing work to move through the providers and finish. * When called without 'early', the provider should release @@ -372,6 +677,7 @@ enum dplane_provider_prio { int dplane_provider_register(const char *name, enum dplane_provider_prio prio, int flags, + int (*start_fp)(struct zebra_dplane_provider *), int (*fp)(struct zebra_dplane_provider *), int (*fini_fp)(struct zebra_dplane_provider *, bool early), @@ -409,10 +715,16 @@ struct zebra_dplane_ctx *dplane_provider_dequeue_in_ctx( int dplane_provider_dequeue_in_list(struct zebra_dplane_provider *prov, struct dplane_ctx_q *listp); -/* Enqueue, maintain associated counter and locking */ +/* Current completed work queue length */ +uint32_t dplane_provider_out_ctx_queue_len(struct zebra_dplane_provider *prov); + +/* Enqueue completed work, maintain associated counter and locking */ void dplane_provider_enqueue_out_ctx(struct zebra_dplane_provider *prov, struct zebra_dplane_ctx *ctx); +/* Enqueue a context directly to zebra main. */ +void dplane_provider_enqueue_to_zebra(struct zebra_dplane_ctx *ctx); + /* * Initialize the dataplane modules at zebra startup. This is currently called * by the rib module. Zebra registers a results callback with the dataplane. diff --git a/zebra/zebra_errors.c b/zebra/zebra_errors.c index cb5f30df1f..b75708031e 100644 --- a/zebra/zebra_errors.c +++ b/zebra/zebra_errors.c @@ -283,6 +283,45 @@ static struct log_ref ferr_zebra_err[] = { .description = "Zebra received an event from inotify, but failed to read what it was.", .suggestion = "Notify a developer.", }, + { + .code = EC_ZEBRA_NHG_TABLE_INSERT_FAILED, + .title = + "Nexthop Group Hash Table Insert Failure", + .description = + "Zebra failed in inserting a Nexthop Group into its hash tables.", + .suggestion = + "Check to see if the entry already exists or if the netlink message was parsed incorrectly." + }, + { + .code = EC_ZEBRA_NHG_SYNC, + .title = + "Zebra's Nexthop Groups are out of sync", + .description = + "Zebra's nexthop group tables are out of sync with the nexthop groups in the fib.", + .suggestion = + "Check the current status of the kernels nexthop groups and compare it to Zebra's." + }, + { + .code = EC_ZEBRA_NHG_FIB_UPDATE, + .title = + "Zebra failed updating the fib with Nexthop Group", + .description = + "Zebra was not able to successfully install a new nexthop group into the fib", + .suggestion = + "Check to see if the nexthop group on the route you tried to install is valid." + }, + { + .code = EC_ZEBRA_IF_LOOKUP_FAILED, + .title = "Zebra interface lookup failed", + .description = "Zebra attempted to look up a interface for a particular vrf_id and interface index, but didn't find anything.", + .suggestion = "If you entered a command to trigger this error, make sure you entered the arguments correctly. Check your config file for any potential errors. If these look correct, seek help.", + }, + { + .code = EC_ZEBRA_NS_NO_DEFAULT, + .title = "Zebra NameSpace failed to find Default", + .description = "Zebra NameSpace subsystem failed to find a Default namespace during initialization.", + .suggestion = "Open an Issue with all relevant log files and restart FRR", + }, /* Warnings */ { .code = EC_ZEBRAING_LM_PROTO_MISMATCH, @@ -380,14 +419,6 @@ static struct log_ref ferr_zebra_err[] = { .suggestion = "If you wish to receive the messages, change your IRDP settings accordingly.", }, - { - .code = EC_ZEBRA_IRDP_BAD_TYPE, - .title = - "Zebra received IRDP packet with bad type", - .description = - "THIS IS BULLSHIT REMOVE ME", - .suggestion = "asdf", - }, { .code = EC_ZEBRA_RNH_NO_TABLE, .title = @@ -667,7 +698,7 @@ static struct log_ref ferr_zebra_err[] = { { .code = EC_ZEBRA_RTM_VERSION_MISMATCH, .title = - "Zebra received kernel message with uknown version", + "Zebra received kernel message with unknown version", .description = "Zebra received a message from the kernel with a message version that does not match Zebra's internal version. Depending on version compatibility, this may cause issues sending and receiving messages to the kernel.", .suggestion = @@ -736,6 +767,30 @@ static struct log_ref ferr_zebra_err[] = { .suggestion = "Check network topology to detect duplicate host IP for correctness.", }, + { + .code = EC_ZEBRA_BAD_NHG_MESSAGE, + .title = + "Bad Nexthop Group Message", + .description = + "Zebra received Nexthop Group message from the kernel that it cannot process.", + .suggestion = + "Check the kernel's link states and routing table to see how it matches ours." + }, + { + .code = EC_ZEBRA_DUPLICATE_NHG_MESSAGE, + .title = + "Duplicate Nexthop Group Message", + .description = + "Zebra received Nexthop Group message from the kernel that it is identical to one it/we already have but with a different ID.", + .suggestion = + "See if the nexthop you are trying to add is already present in the fib." + }, + { + .code = EC_ZEBRA_VRF_MISCONFIGURED, + .title = "Duplicate VRF table id detected", + .description = "Zebra has detected a situation where there are two vrf devices with the exact same tableid. This is considered a complete misconfiguration of VRF devices and breaks a fundamental assumption in FRR about how VRF's work", + .suggestion = "Use different table id's for the VRF's in question" + }, { .code = END_FERR, } diff --git a/zebra/zebra_errors.h b/zebra/zebra_errors.h index 2b7831a408..03953ed17f 100644 --- a/zebra/zebra_errors.h +++ b/zebra/zebra_errors.h @@ -72,6 +72,12 @@ enum zebra_log_refs { EC_ZEBRA_VNI_DEL_FAILED, EC_ZEBRA_VTEP_ADD_FAILED, EC_ZEBRA_VNI_ADD_FAILED, + EC_ZEBRA_NHG_TABLE_INSERT_FAILED, + EC_ZEBRA_NHG_SYNC, + EC_ZEBRA_NHG_FIB_UPDATE, + EC_ZEBRA_IF_LOOKUP_FAILED, + EC_ZEBRA_NS_NO_DEFAULT, + EC_ZEBRA_PBR_RULE_UPDATE, /* warnings */ EC_ZEBRA_NS_NOTIFY_READ, EC_ZEBRAING_LM_PROTO_MISMATCH, @@ -85,7 +91,6 @@ enum zebra_log_refs { EC_ZEBRA_IRDP_BAD_CHECKSUM, EC_ZEBRA_IRDP_BAD_TYPE_CODE, EC_ZEBRA_IRDP_BAD_RX_FLAGS, - EC_ZEBRA_IRDP_BAD_TYPE, EC_ZEBRA_RNH_NO_TABLE, EC_ZEBRA_IFLIST_FAILED, EC_ZEBRA_FPM_FORMAT_UNKNOWN, @@ -126,6 +131,10 @@ enum zebra_log_refs { EC_ZEBRA_DUP_MAC_DETECTED, EC_ZEBRA_DUP_IP_INHERIT_DETECTED, EC_ZEBRA_DUP_IP_DETECTED, + EC_ZEBRA_BAD_NHG_MESSAGE, + EC_ZEBRA_DUPLICATE_NHG_MESSAGE, + EC_ZEBRA_VRF_MISCONFIGURED, + EC_ZEBRA_ES_CREATE, }; void zebra_error_init(void); diff --git a/zebra/zebra_evpn.c b/zebra/zebra_evpn.c new file mode 100644 index 0000000000..80124f92b3 --- /dev/null +++ b/zebra/zebra_evpn.c @@ -0,0 +1,1538 @@ +/* + * Zebra EVPN for VxLAN code + * Copyright (C) 2016, 2017 Cumulus Networks, Inc. + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ +#include + +#include "hash.h" +#include "if.h" +#include "jhash.h" +#include "linklist.h" +#include "log.h" +#include "memory.h" +#include "prefix.h" +#include "stream.h" +#include "table.h" +#include "vlan.h" +#include "vxlan.h" +#ifdef GNU_LINUX +#include +#endif + +#include "zebra/zebra_router.h" +#include "zebra/debug.h" +#include "zebra/interface.h" +#include "zebra/rib.h" +#include "zebra/rt.h" +#include "zebra/rt_netlink.h" +#include "zebra/zebra_errors.h" +#include "zebra/zebra_l2.h" +#include "zebra/zebra_memory.h" +#include "zebra/zebra_ns.h" +#include "zebra/zebra_vrf.h" +#include "zebra/zebra_vxlan.h" +#include "zebra/zebra_evpn.h" +#include "zebra/zebra_evpn_mac.h" +#include "zebra/zebra_evpn_neigh.h" +#include "zebra/zebra_vxlan_private.h" +#include "zebra/zebra_evpn_mh.h" +#include "zebra/zebra_evpn_vxlan.h" +#include "zebra/zebra_router.h" + +DEFINE_MTYPE_STATIC(ZEBRA, ZEVPN, "VNI hash"); +DEFINE_MTYPE_STATIC(ZEBRA, ZEVPN_VTEP, "VNI remote VTEP"); + +/* PMSI strings. */ +#define VXLAN_FLOOD_STR_NO_INFO "-" +#define VXLAN_FLOOD_STR_DEFAULT VXLAN_FLOOD_STR_NO_INFO +static const struct message zvtep_flood_str[] = { + {VXLAN_FLOOD_DISABLED, VXLAN_FLOOD_STR_NO_INFO}, + {VXLAN_FLOOD_PIM_SM, "PIM-SM"}, + {VXLAN_FLOOD_HEAD_END_REPL, "HER"}, + {0} +}; + +int advertise_gw_macip_enabled(zebra_evpn_t *zevpn) +{ + struct zebra_vrf *zvrf; + + zvrf = zebra_vrf_get_evpn(); + if (zvrf && zvrf->advertise_gw_macip) + return 1; + + if (zevpn && zevpn->advertise_gw_macip) + return 1; + + return 0; +} + +int advertise_svi_macip_enabled(zebra_evpn_t *zevpn) +{ + struct zebra_vrf *zvrf; + + zvrf = zebra_vrf_get_evpn(); + if (zvrf && zvrf->advertise_svi_macip) + return 1; + + if (zevpn && zevpn->advertise_svi_macip) + return 1; + + return 0; +} + +/* + * Print a specific EVPN entry. + */ +void zebra_evpn_print(zebra_evpn_t *zevpn, void **ctxt) +{ + struct vty *vty; + zebra_vtep_t *zvtep; + uint32_t num_macs; + uint32_t num_neigh; + json_object *json = NULL; + json_object *json_vtep_list = NULL; + json_object *json_ip_str = NULL; + + vty = ctxt[0]; + json = ctxt[1]; + + if (json == NULL) { + vty_out(vty, "VNI: %u\n", zevpn->vni); + vty_out(vty, " Type: %s\n", "L2"); + vty_out(vty, " Tenant VRF: %s\n", vrf_id_to_name(zevpn->vrf_id)); + } else { + json_object_int_add(json, "vni", zevpn->vni); + json_object_string_add(json, "type", "L2"); + json_object_string_add(json, "vrf", + vrf_id_to_name(zevpn->vrf_id)); + } + + if (!zevpn->vxlan_if) { // unexpected + if (json == NULL) + vty_out(vty, " VxLAN interface: unknown\n"); + return; + } + num_macs = num_valid_macs(zevpn); + num_neigh = hashcount(zevpn->neigh_table); + if (json == NULL) { + vty_out(vty, " VxLAN interface: %s\n", zevpn->vxlan_if->name); + vty_out(vty, " VxLAN ifIndex: %u\n", zevpn->vxlan_if->ifindex); + vty_out(vty, " Local VTEP IP: %s\n", + inet_ntoa(zevpn->local_vtep_ip)); + vty_out(vty, " Mcast group: %s\n", + inet_ntoa(zevpn->mcast_grp)); + } else { + json_object_string_add(json, "vxlanInterface", + zevpn->vxlan_if->name); + json_object_int_add(json, "ifindex", zevpn->vxlan_if->ifindex); + json_object_string_add(json, "vtepIp", + inet_ntoa(zevpn->local_vtep_ip)); + json_object_string_add(json, "mcastGroup", + inet_ntoa(zevpn->mcast_grp)); + json_object_string_add(json, "advertiseGatewayMacip", + zevpn->advertise_gw_macip ? "Yes" : "No"); + json_object_int_add(json, "numMacs", num_macs); + json_object_int_add(json, "numArpNd", num_neigh); + } + if (!zevpn->vteps) { + if (json == NULL) + vty_out(vty, " No remote VTEPs known for this VNI\n"); + } else { + if (json == NULL) + vty_out(vty, " Remote VTEPs for this VNI:\n"); + else + json_vtep_list = json_object_new_array(); + for (zvtep = zevpn->vteps; zvtep; zvtep = zvtep->next) { + const char *flood_str = lookup_msg(zvtep_flood_str, + zvtep->flood_control, + VXLAN_FLOOD_STR_DEFAULT); + + if (json == NULL) { + vty_out(vty, " %s flood: %s\n", + inet_ntoa(zvtep->vtep_ip), + flood_str); + } else { + json_ip_str = json_object_new_string( + inet_ntoa(zvtep->vtep_ip)); + json_object_array_add(json_vtep_list, + json_ip_str); + } + } + if (json) + json_object_object_add(json, "numRemoteVteps", + json_vtep_list); + } + if (json == NULL) { + vty_out(vty, + " Number of MACs (local and remote) known for this VNI: %u\n", + num_macs); + vty_out(vty, + " Number of ARPs (IPv4 and IPv6, local and remote) " + "known for this VNI: %u\n", + num_neigh); + vty_out(vty, " Advertise-gw-macip: %s\n", + zevpn->advertise_gw_macip ? "Yes" : "No"); + } +} + +/* + * Print an EVPN hash entry - called for display of all VNIs. + */ +void zebra_evpn_print_hash(struct hash_bucket *bucket, void *ctxt[]) +{ + struct vty *vty; + zebra_evpn_t *zevpn; + zebra_vtep_t *zvtep; + uint32_t num_vteps = 0; + uint32_t num_macs = 0; + uint32_t num_neigh = 0; + json_object *json = NULL; + json_object *json_evpn = NULL; + json_object *json_ip_str = NULL; + json_object *json_vtep_list = NULL; + + vty = ctxt[0]; + json = ctxt[1]; + + zevpn = (zebra_evpn_t *)bucket->data; + + zvtep = zevpn->vteps; + while (zvtep) { + num_vteps++; + zvtep = zvtep->next; + } + + num_macs = num_valid_macs(zevpn); + num_neigh = hashcount(zevpn->neigh_table); + if (json == NULL) + vty_out(vty, "%-10u %-4s %-21s %-8u %-8u %-15u %-37s\n", + zevpn->vni, "L2", + zevpn->vxlan_if ? zevpn->vxlan_if->name : "unknown", + num_macs, num_neigh, num_vteps, + vrf_id_to_name(zevpn->vrf_id)); + else { + char vni_str[VNI_STR_LEN]; + snprintf(vni_str, VNI_STR_LEN, "%u", zevpn->vni); + json_evpn = json_object_new_object(); + json_object_int_add(json_evpn, "vni", zevpn->vni); + json_object_string_add(json_evpn, "type", "L2"); + json_object_string_add(json_evpn, "vxlanIf", + zevpn->vxlan_if ? zevpn->vxlan_if->name + : "unknown"); + json_object_int_add(json_evpn, "numMacs", num_macs); + json_object_int_add(json_evpn, "numArpNd", num_neigh); + json_object_int_add(json_evpn, "numRemoteVteps", num_vteps); + json_object_string_add(json_evpn, "tenantVrf", + vrf_id_to_name(zevpn->vrf_id)); + if (num_vteps) { + json_vtep_list = json_object_new_array(); + for (zvtep = zevpn->vteps; zvtep; zvtep = zvtep->next) { + json_ip_str = json_object_new_string( + inet_ntoa(zvtep->vtep_ip)); + json_object_array_add(json_vtep_list, + json_ip_str); + } + json_object_object_add(json_evpn, "remoteVteps", + json_vtep_list); + } + json_object_object_add(json, vni_str, json_evpn); + } +} + +/* + * Print an EVPN hash entry in detail - called for display of all EVPNs. + */ +void zebra_evpn_print_hash_detail(struct hash_bucket *bucket, void *data) +{ + struct vty *vty; + zebra_evpn_t *zevpn; + json_object *json_array = NULL; + bool use_json = false; + struct zebra_evpn_show *zes = data; + + vty = zes->vty; + json_array = zes->json; + use_json = zes->use_json; + + zevpn = (zebra_evpn_t *)bucket->data; + + zebra_vxlan_print_vni(vty, zes->zvrf, zevpn->vni, use_json, json_array); + + if (!use_json) + vty_out(vty, "\n"); +} + +int zebra_evpn_del_macip_for_intf(struct interface *ifp, zebra_evpn_t *zevpn) +{ + struct listnode *cnode = NULL, *cnnode = NULL; + struct connected *c = NULL; + struct ethaddr macaddr; + + memcpy(&macaddr.octet, ifp->hw_addr, ETH_ALEN); + + for (ALL_LIST_ELEMENTS(ifp->connected, cnode, cnnode, c)) { + struct ipaddr ip; + + memset(&ip, 0, sizeof(struct ipaddr)); + if (!CHECK_FLAG(c->conf, ZEBRA_IFC_REAL)) + continue; + + if (c->address->family == AF_INET) { + ip.ipa_type = IPADDR_V4; + memcpy(&(ip.ipaddr_v4), &(c->address->u.prefix4), + sizeof(struct in_addr)); + } else if (c->address->family == AF_INET6) { + ip.ipa_type = IPADDR_V6; + memcpy(&(ip.ipaddr_v6), &(c->address->u.prefix6), + sizeof(struct in6_addr)); + } else { + continue; + } + + zebra_evpn_gw_macip_del(ifp, zevpn, &ip); + } + + return 0; +} + +int zebra_evpn_add_macip_for_intf(struct interface *ifp, zebra_evpn_t *zevpn) +{ + struct listnode *cnode = NULL, *cnnode = NULL; + struct connected *c = NULL; + struct ethaddr macaddr; + + memcpy(&macaddr.octet, ifp->hw_addr, ETH_ALEN); + + for (ALL_LIST_ELEMENTS(ifp->connected, cnode, cnnode, c)) { + struct ipaddr ip; + + memset(&ip, 0, sizeof(struct ipaddr)); + if (!CHECK_FLAG(c->conf, ZEBRA_IFC_REAL)) + continue; + + if (c->address->family == AF_INET) { + ip.ipa_type = IPADDR_V4; + memcpy(&(ip.ipaddr_v4), &(c->address->u.prefix4), + sizeof(struct in_addr)); + } else if (c->address->family == AF_INET6) { + ip.ipa_type = IPADDR_V6; + memcpy(&(ip.ipaddr_v6), &(c->address->u.prefix6), + sizeof(struct in6_addr)); + } else { + continue; + } + + zebra_evpn_gw_macip_add(ifp, zevpn, &macaddr, &ip); + } + return 0; +} + +static int ip_prefix_send_to_client(vrf_id_t vrf_id, struct prefix *p, + uint16_t cmd) +{ + struct zserv *client = NULL; + struct stream *s = NULL; + char buf[PREFIX_STRLEN]; + + client = zserv_find_client(ZEBRA_ROUTE_BGP, 0); + /* BGP may not be running. */ + if (!client) + return 0; + + s = stream_new(ZEBRA_MAX_PACKET_SIZ); + + zclient_create_header(s, cmd, vrf_id); + stream_put(s, p, sizeof(struct prefix)); + + /* Write packet size. */ + stream_putw_at(s, 0, stream_get_endp(s)); + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Send ip prefix %s %s on vrf %s", + prefix2str(p, buf, sizeof(buf)), + (cmd == ZEBRA_IP_PREFIX_ROUTE_ADD) ? "ADD" : "DEL", + vrf_id_to_name(vrf_id)); + + if (cmd == ZEBRA_IP_PREFIX_ROUTE_ADD) + client->prefixadd_cnt++; + else + client->prefixdel_cnt++; + + return zserv_send_message(client, s); +} + +int zebra_evpn_advertise_subnet(zebra_evpn_t *zevpn, struct interface *ifp, + int advertise) +{ + struct listnode *cnode = NULL, *cnnode = NULL; + struct connected *c = NULL; + struct ethaddr macaddr; + + memcpy(&macaddr.octet, ifp->hw_addr, ETH_ALEN); + + for (ALL_LIST_ELEMENTS(ifp->connected, cnode, cnnode, c)) { + struct prefix p; + + memcpy(&p, c->address, sizeof(struct prefix)); + + /* skip link local address */ + if (IN6_IS_ADDR_LINKLOCAL(&p.u.prefix6)) + continue; + + apply_mask(&p); + if (advertise) + ip_prefix_send_to_client(ifp->vrf_id, &p, + ZEBRA_IP_PREFIX_ROUTE_ADD); + else + ip_prefix_send_to_client(ifp->vrf_id, &p, + ZEBRA_IP_PREFIX_ROUTE_DEL); + } + return 0; +} + +/* + * zebra_evpn_gw_macip_add_to_client + */ +int zebra_evpn_gw_macip_add(struct interface *ifp, zebra_evpn_t *zevpn, + struct ethaddr *macaddr, struct ipaddr *ip) +{ + zebra_mac_t *mac = NULL; + struct zebra_if *zif = NULL; + struct zebra_l2info_vxlan *vxl = NULL; + + zif = zevpn->vxlan_if->info; + if (!zif) + return -1; + + vxl = &zif->l2info.vxl; + + if (zebra_evpn_mac_gw_macip_add(ifp, zevpn, ip, &mac, macaddr, + vxl->access_vlan) + != 0) + return -1; + + return zebra_evpn_neigh_gw_macip_add(ifp, zevpn, ip, mac); +} + +/* + * zebra_evpn_gw_macip_del_from_client + */ +int zebra_evpn_gw_macip_del(struct interface *ifp, zebra_evpn_t *zevpn, + struct ipaddr *ip) +{ + char buf1[ETHER_ADDR_STRLEN]; + char buf2[INET6_ADDRSTRLEN]; + zebra_neigh_t *n = NULL; + zebra_mac_t *mac = NULL; + + /* If the neigh entry is not present nothing to do*/ + n = zebra_evpn_neigh_lookup(zevpn, ip); + if (!n) + return 0; + + /* mac entry should be present */ + mac = zebra_evpn_mac_lookup(zevpn, &n->emac); + if (!mac) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("MAC %s doesn't exist for neigh %s on VNI %u", + prefix_mac2str(&n->emac, + buf1, sizeof(buf1)), + ipaddr2str(ip, buf2, sizeof(buf2)), + zevpn->vni); + return -1; + } + + /* If the entry is not local nothing to do*/ + if (!CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) + return -1; + + /* only need to delete the entry from bgp if we sent it before */ + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%u:SVI %s(%u) VNI %u, sending GW MAC %s IP %s del to BGP", + ifp->vrf_id, ifp->name, ifp->ifindex, zevpn->vni, + prefix_mac2str(&(n->emac), buf1, sizeof(buf1)), + ipaddr2str(ip, buf2, sizeof(buf2))); + + /* Remove neighbor from BGP. */ + zebra_evpn_neigh_send_del_to_client(zevpn->vni, &n->ip, &n->emac, + n->flags, ZEBRA_NEIGH_ACTIVE, + false /*force*/); + + /* Delete this neighbor entry. */ + zebra_evpn_neigh_del(zevpn, n); + + /* see if the mac needs to be deleted as well*/ + if (mac) + zebra_evpn_deref_ip2mac(zevpn, mac); + + return 0; +} + +void zebra_evpn_gw_macip_del_for_evpn_hash(struct hash_bucket *bucket, + void *ctxt) +{ + zebra_evpn_t *zevpn = NULL; + struct zebra_if *zif = NULL; + struct zebra_l2info_vxlan zl2_info; + struct interface *vlan_if = NULL; + struct interface *vrr_if = NULL; + struct interface *ifp; + + /* Add primary SVI MAC*/ + zevpn = (zebra_evpn_t *)bucket->data; + + /* Global (Zvrf) advertise-default-gw is disabled, + * but zevpn advertise-default-gw is enabled + */ + if (zevpn->advertise_gw_macip) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("VNI: %u GW-MACIP enabled, retain gw-macip", + zevpn->vni); + return; + } + + ifp = zevpn->vxlan_if; + if (!ifp) + return; + zif = ifp->info; + + /* If down or not mapped to a bridge, we're done. */ + if (!if_is_operative(ifp) || !zif->brslave_info.br_if) + return; + + zl2_info = zif->l2info.vxl; + + vlan_if = + zvni_map_to_svi(zl2_info.access_vlan, zif->brslave_info.br_if); + if (!vlan_if) + return; + + /* Del primary MAC-IP */ + zebra_evpn_del_macip_for_intf(vlan_if, zevpn); + + /* Del VRR MAC-IP - if any*/ + vrr_if = zebra_get_vrr_intf_for_svi(vlan_if); + if (vrr_if) + zebra_evpn_del_macip_for_intf(vrr_if, zevpn); + + return; +} + +void zebra_evpn_gw_macip_add_for_evpn_hash(struct hash_bucket *bucket, + void *ctxt) +{ + zebra_evpn_t *zevpn = NULL; + struct zebra_if *zif = NULL; + struct zebra_l2info_vxlan zl2_info; + struct interface *vlan_if = NULL; + struct interface *vrr_if = NULL; + struct interface *ifp = NULL; + + zevpn = (zebra_evpn_t *)bucket->data; + + ifp = zevpn->vxlan_if; + if (!ifp) + return; + zif = ifp->info; + + /* If down or not mapped to a bridge, we're done. */ + if (!if_is_operative(ifp) || !zif->brslave_info.br_if) + return; + zl2_info = zif->l2info.vxl; + + vlan_if = + zvni_map_to_svi(zl2_info.access_vlan, zif->brslave_info.br_if); + if (!vlan_if) + return; + + /* Add primary SVI MAC-IP */ + zebra_evpn_add_macip_for_intf(vlan_if, zevpn); + + if (advertise_gw_macip_enabled(zevpn)) { + /* Add VRR MAC-IP - if any*/ + vrr_if = zebra_get_vrr_intf_for_svi(vlan_if); + if (vrr_if) + zebra_evpn_add_macip_for_intf(vrr_if, zevpn); + } + + return; +} + +void zebra_evpn_svi_macip_del_for_evpn_hash(struct hash_bucket *bucket, + void *ctxt) +{ + zebra_evpn_t *zevpn = NULL; + struct zebra_if *zif = NULL; + struct zebra_l2info_vxlan zl2_info; + struct interface *vlan_if = NULL; + struct interface *ifp; + + /* Add primary SVI MAC*/ + zevpn = (zebra_evpn_t *)bucket->data; + if (!zevpn) + return; + + /* Global(vrf) advertise-svi-ip disabled, but zevpn advertise-svi-ip + * enabled + */ + if (zevpn->advertise_svi_macip) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("VNI: %u SVI-MACIP enabled, retain svi-macip", + zevpn->vni); + return; + } + + ifp = zevpn->vxlan_if; + if (!ifp) + return; + zif = ifp->info; + + /* If down or not mapped to a bridge, we're done. */ + if (!if_is_operative(ifp) || !zif->brslave_info.br_if) + return; + + zl2_info = zif->l2info.vxl; + + vlan_if = + zvni_map_to_svi(zl2_info.access_vlan, zif->brslave_info.br_if); + if (!vlan_if) + return; + + /* Del primary MAC-IP */ + zebra_evpn_del_macip_for_intf(vlan_if, zevpn); + + return; +} + +static int zebra_evpn_map_vlan_ns(struct ns *ns, + void *_in_param, + void **_p_zevpn) +{ + struct zebra_ns *zns = ns->info; + struct route_node *rn; + struct interface *br_if; + zebra_evpn_t **p_zevpn = (zebra_evpn_t **)_p_zevpn; + zebra_evpn_t *zevpn; + struct interface *tmp_if = NULL; + struct zebra_if *zif; + struct zebra_l2info_vxlan *vxl = NULL; + struct zebra_from_svi_param *in_param = + (struct zebra_from_svi_param *)_in_param; + int found = 0; + + if (!in_param) + return NS_WALK_STOP; + br_if = in_param->br_if; + zif = in_param->zif; + assert(zif); + assert(br_if); + + /* See if this interface (or interface plus VLAN Id) maps to a VxLAN */ + /* TODO: Optimize with a hash. */ + for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { + tmp_if = (struct interface *)rn->info; + if (!tmp_if) + continue; + zif = tmp_if->info; + if (!zif || zif->zif_type != ZEBRA_IF_VXLAN) + continue; + if (!if_is_operative(tmp_if)) + continue; + vxl = &zif->l2info.vxl; + + if (zif->brslave_info.br_if != br_if) + continue; + + if (!in_param->bridge_vlan_aware + || vxl->access_vlan == in_param->vid) { + found = 1; + break; + } + } + if (!found) + return NS_WALK_CONTINUE; + + zevpn = zebra_evpn_lookup(vxl->vni); + if (p_zevpn) + *p_zevpn = zevpn; + return NS_WALK_STOP; +} + +/* + * Map port or (port, VLAN) to an EVPN. This is invoked upon getting MAC + * notifications, to see if they are of interest. + */ +zebra_evpn_t *zebra_evpn_map_vlan(struct interface *ifp, + struct interface *br_if, vlanid_t vid) +{ + struct zebra_if *zif; + struct zebra_l2info_bridge *br; + zebra_evpn_t **p_zevpn; + zebra_evpn_t *zevpn = NULL; + struct zebra_from_svi_param in_param; + + /* Determine if bridge is VLAN-aware or not */ + zif = br_if->info; + assert(zif); + br = &zif->l2info.br; + in_param.bridge_vlan_aware = br->vlan_aware; + in_param.vid = vid; + in_param.br_if = br_if; + in_param.zif = zif; + p_zevpn = &zevpn; + + ns_walk_func(zebra_evpn_map_vlan_ns, + (void *)&in_param, + (void **)p_zevpn); + return zevpn; +} + +static int zebra_evpn_from_svi_ns(struct ns *ns, + void *_in_param, + void **_p_zevpn) +{ + struct zebra_ns *zns = ns->info; + struct route_node *rn; + struct interface *br_if; + zebra_evpn_t **p_zevpn = (zebra_evpn_t **)_p_zevpn; + zebra_evpn_t *zevpn; + struct interface *tmp_if = NULL; + struct zebra_if *zif; + struct zebra_l2info_vxlan *vxl = NULL; + struct zebra_from_svi_param *in_param = + (struct zebra_from_svi_param *)_in_param; + int found = 0; + + if (!in_param) + return NS_WALK_STOP; + br_if = in_param->br_if; + zif = in_param->zif; + assert(zif); + + /* TODO: Optimize with a hash. */ + for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { + tmp_if = (struct interface *)rn->info; + if (!tmp_if) + continue; + zif = tmp_if->info; + if (!zif || zif->zif_type != ZEBRA_IF_VXLAN) + continue; + if (!if_is_operative(tmp_if)) + continue; + vxl = &zif->l2info.vxl; + + if (zif->brslave_info.br_if != br_if) + continue; + + if (!in_param->bridge_vlan_aware + || vxl->access_vlan == in_param->vid) { + found = 1; + break; + } + } + + if (!found) + return NS_WALK_CONTINUE; + + zevpn = zebra_evpn_lookup(vxl->vni); + if (p_zevpn) + *p_zevpn = zevpn; + return NS_WALK_STOP; +} + +/* + * Map SVI and associated bridge to an EVPN. This is invoked upon getting + * neighbor notifications, to see if they are of interest. + */ +zebra_evpn_t *zebra_evpn_from_svi(struct interface *ifp, + struct interface *br_if) +{ + struct zebra_l2info_bridge *br; + zebra_evpn_t *zevpn = NULL; + zebra_evpn_t **p_zevpn; + struct zebra_if *zif; + struct zebra_from_svi_param in_param; + + if (!br_if) + return NULL; + + /* Make sure the linked interface is a bridge. */ + if (!IS_ZEBRA_IF_BRIDGE(br_if)) + return NULL; + + /* Determine if bridge is VLAN-aware or not */ + zif = br_if->info; + assert(zif); + br = &zif->l2info.br; + in_param.bridge_vlan_aware = br->vlan_aware; + in_param.vid = 0; + + if (in_param.bridge_vlan_aware) { + struct zebra_l2info_vlan *vl; + + if (!IS_ZEBRA_IF_VLAN(ifp)) + return NULL; + + zif = ifp->info; + assert(zif); + vl = &zif->l2info.vl; + in_param.vid = vl->vid; + } + + in_param.br_if = br_if; + in_param.zif = zif; + p_zevpn = &zevpn; + /* See if this interface (or interface plus VLAN Id) maps to a VxLAN */ + ns_walk_func(zebra_evpn_from_svi_ns, (void *)&in_param, + (void **)p_zevpn); + return zevpn; +} + +static int zvni_map_to_macvlan_ns(struct ns *ns, + void *_in_param, + void **_p_ifp) +{ + struct zebra_ns *zns = ns->info; + struct zebra_from_svi_param *in_param = + (struct zebra_from_svi_param *)_in_param; + struct interface **p_ifp = (struct interface **)_p_ifp; + struct route_node *rn; + struct interface *tmp_if = NULL; + struct zebra_if *zif; + + if (!in_param) + return NS_WALK_STOP; + + /* Identify corresponding VLAN interface. */ + for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { + tmp_if = (struct interface *)rn->info; + /* Check oper status of the SVI. */ + if (!tmp_if || !if_is_operative(tmp_if)) + continue; + zif = tmp_if->info; + + if (!zif || zif->zif_type != ZEBRA_IF_MACVLAN) + continue; + + if (zif->link == in_param->svi_if) { + if (p_ifp) + *p_ifp = tmp_if; + return NS_WALK_STOP; + } + } + + return NS_WALK_CONTINUE; +} + +/* Map to MAC-VLAN interface corresponding to specified SVI interface. + */ +struct interface *zebra_evpn_map_to_macvlan(struct interface *br_if, + struct interface *svi_if) +{ + struct interface *tmp_if = NULL; + struct zebra_if *zif; + struct interface **p_ifp; + struct zebra_from_svi_param in_param; + + /* Defensive check, caller expected to invoke only with valid bridge. */ + if (!br_if) + return NULL; + + if (!svi_if) { + zlog_debug("svi_if is not passed."); + return NULL; + } + + /* Determine if bridge is VLAN-aware or not */ + zif = br_if->info; + assert(zif); + + in_param.vid = 0; + in_param.br_if = br_if; + in_param.zif = NULL; + in_param.svi_if = svi_if; + p_ifp = &tmp_if; + + /* Identify corresponding VLAN interface. */ + ns_walk_func(zvni_map_to_macvlan_ns, + (void *)&in_param, + (void **)p_ifp); + return tmp_if; +} + +/* + * Install MAC hash entry - called upon access VLAN change. + */ +void zebra_evpn_install_mac_hash(struct hash_bucket *bucket, void *ctxt) +{ + zebra_mac_t *mac; + struct mac_walk_ctx *wctx = ctxt; + + mac = (zebra_mac_t *)bucket->data; + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) + zebra_evpn_rem_mac_install(wctx->zevpn, mac, false); +} + +/* + * Read and populate local MACs and neighbors corresponding to this EVPN. + */ +void zebra_evpn_read_mac_neigh(zebra_evpn_t *zevpn, struct interface *ifp) +{ + struct zebra_ns *zns; + struct zebra_vrf *zvrf; + struct zebra_if *zif; + struct interface *vlan_if; + struct zebra_l2info_vxlan *vxl; + struct interface *vrr_if; + + zif = ifp->info; + vxl = &zif->l2info.vxl; + zvrf = zebra_vrf_lookup_by_id(zevpn->vrf_id); + if (!zvrf || !zvrf->zns) + return; + zns = zvrf->zns; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Reading MAC FDB and Neighbors for intf %s(%u) VNI %u master %u", + ifp->name, ifp->ifindex, zevpn->vni, + zif->brslave_info.bridge_ifindex); + + macfdb_read_for_bridge(zns, ifp, zif->brslave_info.br_if); + vlan_if = zvni_map_to_svi(vxl->access_vlan, zif->brslave_info.br_if); + if (vlan_if) { + + /* Add SVI MAC-IP */ + zebra_evpn_add_macip_for_intf(vlan_if, zevpn); + + /* Add VRR MAC-IP - if any*/ + vrr_if = zebra_get_vrr_intf_for_svi(vlan_if); + if (vrr_if) + zebra_evpn_add_macip_for_intf(vrr_if, zevpn); + + neigh_read_for_vlan(zns, vlan_if); + } +} + +/* + * Hash function for EVPN. + */ +unsigned int zebra_evpn_hash_keymake(const void *p) +{ + const zebra_evpn_t *zevpn = p; + + return (jhash_1word(zevpn->vni, 0)); +} + +/* + * Compare 2 evpn hash entries. + */ +bool zebra_evpn_hash_cmp(const void *p1, const void *p2) +{ + const zebra_evpn_t *zevpn1 = p1; + const zebra_evpn_t *zevpn2 = p2; + + return (zevpn1->vni == zevpn2->vni); +} + +int zebra_evpn_list_cmp(void *p1, void *p2) +{ + const zebra_evpn_t *zevpn1 = p1; + const zebra_evpn_t *zevpn2 = p2; + + if (zevpn1->vni == zevpn2->vni) + return 0; + return (zevpn1->vni < zevpn2->vni) ? -1 : 1; +} + +/* + * Callback to allocate VNI hash entry. + */ +void *zebra_evpn_alloc(void *p) +{ + const zebra_evpn_t *tmp_vni = p; + zebra_evpn_t *zevpn; + + zevpn = XCALLOC(MTYPE_ZEVPN, sizeof(zebra_evpn_t)); + zevpn->vni = tmp_vni->vni; + return ((void *)zevpn); +} + +/* + * Look up EVPN hash entry. + */ +zebra_evpn_t *zebra_evpn_lookup(vni_t vni) +{ + struct zebra_vrf *zvrf; + zebra_evpn_t tmp_vni; + zebra_evpn_t *zevpn = NULL; + + zvrf = zebra_vrf_get_evpn(); + assert(zvrf); + memset(&tmp_vni, 0, sizeof(zebra_evpn_t)); + tmp_vni.vni = vni; + zevpn = hash_lookup(zvrf->evpn_table, &tmp_vni); + + return zevpn; +} + +/* + * Add EVPN hash entry. + */ +zebra_evpn_t *zebra_evpn_add(vni_t vni) +{ + struct zebra_vrf *zvrf; + zebra_evpn_t tmp_zevpn; + zebra_evpn_t *zevpn = NULL; + + zvrf = zebra_vrf_get_evpn(); + assert(zvrf); + memset(&tmp_zevpn, 0, sizeof(zebra_evpn_t)); + tmp_zevpn.vni = vni; + zevpn = hash_get(zvrf->evpn_table, &tmp_zevpn, zebra_evpn_alloc); + assert(zevpn); + + zebra_evpn_evpn_es_init(zevpn); + + /* Create hash table for MAC */ + zevpn->mac_table = zebra_mac_db_create("Zebra EVPN MAC Table"); + + /* Create hash table for neighbors */ + zevpn->neigh_table = zebra_neigh_db_create("Zebra EVPN Neighbor Table"); + + return zevpn; +} + +/* + * Delete EVPN hash entry. + */ +int zebra_evpn_del(zebra_evpn_t *zevpn) +{ + struct zebra_vrf *zvrf; + zebra_evpn_t *tmp_zevpn; + + zvrf = zebra_vrf_get_evpn(); + assert(zvrf); + + /* Free the neighbor hash table. */ + hash_free(zevpn->neigh_table); + zevpn->neigh_table = NULL; + + /* Free the MAC hash table. */ + hash_free(zevpn->mac_table); + zevpn->mac_table = NULL; + + zebra_evpn_evpn_es_cleanup(zevpn); + + /* Free the EVPN hash entry and allocated memory. */ + tmp_zevpn = hash_release(zvrf->evpn_table, zevpn); + XFREE(MTYPE_ZEVPN, tmp_zevpn); + + return 0; +} + +/* + * Inform BGP about local EVPN addition. + */ +int zebra_evpn_send_add_to_client(zebra_evpn_t *zevpn) +{ + struct zserv *client; + struct stream *s; + int rc; + + client = zserv_find_client(ZEBRA_ROUTE_BGP, 0); + /* BGP may not be running. */ + if (!client) + return 0; + + s = stream_new(ZEBRA_MAX_PACKET_SIZ); + + zclient_create_header(s, ZEBRA_VNI_ADD, zebra_vrf_get_evpn_id()); + stream_putl(s, zevpn->vni); + stream_put_in_addr(s, &zevpn->local_vtep_ip); + stream_put(s, &zevpn->vrf_id, sizeof(vrf_id_t)); /* tenant vrf */ + stream_put_in_addr(s, &zevpn->mcast_grp); + + /* Write packet size. */ + stream_putw_at(s, 0, stream_get_endp(s)); + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Send EVPN_ADD %u %s tenant vrf %s to %s", zevpn->vni, + inet_ntoa(zevpn->local_vtep_ip), + vrf_id_to_name(zevpn->vrf_id), + zebra_route_string(client->proto)); + + client->vniadd_cnt++; + rc = zserv_send_message(client, s); + + if (!(zevpn->flags & ZEVPN_READY_FOR_BGP)) { + zevpn->flags |= ZEVPN_READY_FOR_BGP; + /* once the EVPN is sent the ES-EVIs can also be replayed + * to BGP + */ + zebra_evpn_update_all_es(zevpn); + } + return rc; +} + +/* + * Inform BGP about local EVPN deletion. + */ +int zebra_evpn_send_del_to_client(zebra_evpn_t *zevpn) +{ + struct zserv *client; + struct stream *s; + + client = zserv_find_client(ZEBRA_ROUTE_BGP, 0); + /* BGP may not be running. */ + if (!client) + return 0; + + if (zevpn->flags & ZEVPN_READY_FOR_BGP) { + zevpn->flags &= ~ZEVPN_READY_FOR_BGP; + /* the ES-EVIs must be removed from BGP before the EVPN is */ + zebra_evpn_update_all_es(zevpn); + } + + s = stream_new(ZEBRA_MAX_PACKET_SIZ); + stream_reset(s); + + zclient_create_header(s, ZEBRA_VNI_DEL, zebra_vrf_get_evpn_id()); + stream_putl(s, zevpn->vni); + + /* Write packet size. */ + stream_putw_at(s, 0, stream_get_endp(s)); + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Send EVPN_DEL %u to %s", zevpn->vni, + zebra_route_string(client->proto)); + + client->vnidel_cnt++; + return zserv_send_message(client, s); +} + +/* + * See if remote VTEP matches with prefix. + */ +static int zebra_evpn_vtep_match(struct in_addr *vtep_ip, zebra_vtep_t *zvtep) +{ + return (IPV4_ADDR_SAME(vtep_ip, &zvtep->vtep_ip)); +} + +/* + * Locate remote VTEP in EVPN hash table. + */ +zebra_vtep_t *zebra_evpn_vtep_find(zebra_evpn_t *zevpn, struct in_addr *vtep_ip) +{ + zebra_vtep_t *zvtep; + + if (!zevpn) + return NULL; + + for (zvtep = zevpn->vteps; zvtep; zvtep = zvtep->next) { + if (zebra_evpn_vtep_match(vtep_ip, zvtep)) + break; + } + + return zvtep; +} + +/* + * Add remote VTEP to EVPN hash table. + */ +zebra_vtep_t *zebra_evpn_vtep_add(zebra_evpn_t *zevpn, struct in_addr *vtep_ip, + int flood_control) + +{ + zebra_vtep_t *zvtep; + + zvtep = XCALLOC(MTYPE_ZEVPN_VTEP, sizeof(zebra_vtep_t)); + + zvtep->vtep_ip = *vtep_ip; + zvtep->flood_control = flood_control; + + if (zevpn->vteps) + zevpn->vteps->prev = zvtep; + zvtep->next = zevpn->vteps; + zevpn->vteps = zvtep; + + return zvtep; +} + +/* + * Remove remote VTEP from EVPN hash table. + */ +int zebra_evpn_vtep_del(zebra_evpn_t *zevpn, zebra_vtep_t *zvtep) +{ + if (zvtep->next) + zvtep->next->prev = zvtep->prev; + if (zvtep->prev) + zvtep->prev->next = zvtep->next; + else + zevpn->vteps = zvtep->next; + + zvtep->prev = zvtep->next = NULL; + XFREE(MTYPE_ZEVPN_VTEP, zvtep); + + return 0; +} + +/* + * Delete all remote VTEPs for this EVPN (upon VNI delete). Also + * uninstall from kernel if asked to. + */ +int zebra_evpn_vtep_del_all(zebra_evpn_t *zevpn, int uninstall) +{ + zebra_vtep_t *zvtep, *zvtep_next; + + if (!zevpn) + return -1; + + for (zvtep = zevpn->vteps; zvtep; zvtep = zvtep_next) { + zvtep_next = zvtep->next; + if (uninstall) + zebra_evpn_vtep_uninstall(zevpn, &zvtep->vtep_ip); + zebra_evpn_vtep_del(zevpn, zvtep); + } + + return 0; +} + +/* + * Install remote VTEP into the kernel if the remote VTEP has asked + * for head-end-replication. + */ +int zebra_evpn_vtep_install(zebra_evpn_t *zevpn, zebra_vtep_t *zvtep) +{ + if (is_vxlan_flooding_head_end() && + (zvtep->flood_control == VXLAN_FLOOD_HEAD_END_REPL)) { + if (ZEBRA_DPLANE_REQUEST_FAILURE == + dplane_vtep_add(zevpn->vxlan_if, + &zvtep->vtep_ip, zevpn->vni)) + return -1; + } + + return 0; +} + +/* + * Uninstall remote VTEP from the kernel. + */ +int zebra_evpn_vtep_uninstall(zebra_evpn_t *zevpn, struct in_addr *vtep_ip) +{ + if (!zevpn->vxlan_if) { + zlog_debug("VNI %u hash %p couldn't be uninstalled - no intf", + zevpn->vni, zevpn); + return -1; + } + + if (ZEBRA_DPLANE_REQUEST_FAILURE == + dplane_vtep_delete(zevpn->vxlan_if, vtep_ip, zevpn->vni)) + return -1; + + return 0; +} + +/* + * Install or uninstall flood entries in the kernel corresponding to + * remote VTEPs. This is invoked upon change to BUM handling. + */ +void zebra_evpn_handle_flooding_remote_vteps(struct hash_bucket *bucket, + void *zvrf) +{ + zebra_evpn_t *zevpn; + zebra_vtep_t *zvtep; + + zevpn = (zebra_evpn_t *)bucket->data; + if (!zevpn) + return; + + for (zvtep = zevpn->vteps; zvtep; zvtep = zvtep->next) { + if (is_vxlan_flooding_head_end()) + zebra_evpn_vtep_install(zevpn, zvtep); + else + zebra_evpn_vtep_uninstall(zevpn, &zvtep->vtep_ip); + } +} + +/* + * Cleanup EVPN/VTEP and update kernel + */ +void zebra_evpn_cleanup_all(struct hash_bucket *bucket, void *arg) +{ + zebra_evpn_t *zevpn = NULL; + + zevpn = (zebra_evpn_t *)bucket->data; + + /* Free up all neighbors and MACs, if any. */ + zebra_evpn_neigh_del_all(zevpn, 1, 0, DEL_ALL_NEIGH); + zebra_evpn_mac_del_all(zevpn, 1, 0, DEL_ALL_MAC); + + /* Free up all remote VTEPs, if any. */ + zebra_evpn_vtep_del_all(zevpn, 1); + + /* Delete the hash entry. */ + zebra_evpn_del(zevpn); +} + +static void +zebra_evpn_process_sync_macip_add(zebra_evpn_t *zevpn, struct ethaddr *macaddr, + uint16_t ipa_len, struct ipaddr *ipaddr, + uint8_t flags, uint32_t seq, esi_t *esi) +{ + struct sync_mac_ip_ctx ctx; + char macbuf[ETHER_ADDR_STRLEN]; + char ipbuf[INET6_ADDRSTRLEN]; + bool sticky; + bool remote_gw; + zebra_neigh_t *n = NULL; + + sticky = !!CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY); + remote_gw = !!CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_GW); + /* if sticky or remote-gw ignore updates from the peer */ + if (sticky || remote_gw) { + if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_NEIGH + || IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug( + "Ignore sync-macip vni %u mac %s%s%s%s%s", + zevpn->vni, + prefix_mac2str(macaddr, macbuf, sizeof(macbuf)), + ipa_len ? " IP " : "", + ipa_len ? ipaddr2str(ipaddr, ipbuf, + sizeof(ipbuf)) + : "", + sticky ? " sticky" : "", + remote_gw ? " remote_gw" : ""); + return; + } + + if (ipa_len) { + n = zebra_evpn_neigh_lookup(zevpn, ipaddr); + if (n + && !zebra_evpn_neigh_is_bgp_seq_ok(zevpn, n, macaddr, seq)) + return; + } + + memset(&ctx, 0, sizeof(ctx)); + ctx.mac = zebra_evpn_proc_sync_mac_update( + zevpn, macaddr, ipa_len, ipaddr, flags, seq, esi, &ctx); + if (ctx.ignore_macip || !ctx.mac || !ipa_len) + return; + + zebra_evpn_proc_sync_neigh_update(zevpn, n, ipa_len, ipaddr, flags, seq, + esi, &ctx); +} + +/************************** remote mac-ip handling **************************/ +/* Process a remote MACIP add from BGP. */ +void process_remote_macip_add(vni_t vni, struct ethaddr *macaddr, + uint16_t ipa_len, struct ipaddr *ipaddr, + uint8_t flags, uint32_t seq, + struct in_addr vtep_ip, esi_t *esi) +{ + zebra_evpn_t *zevpn; + zebra_vtep_t *zvtep; + zebra_mac_t *mac = NULL; + struct interface *ifp = NULL; + struct zebra_if *zif = NULL; + struct zebra_vrf *zvrf; + + /* Locate EVPN hash entry - expected to exist. */ + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { + zlog_warn("Unknown VNI %u upon remote MACIP ADD", vni); + return; + } + + ifp = zevpn->vxlan_if; + if (ifp) + zif = ifp->info; + if (!ifp || !if_is_operative(ifp) || !zif || !zif->brslave_info.br_if) { + zlog_warn( + "Ignoring remote MACIP ADD VNI %u, invalid interface state or info", + vni); + return; + } + + /* Type-2 routes from another PE can be interpreted as remote or + * SYNC based on the destination ES - + * SYNC - if ES is local + * REMOTE - if ES is not local + */ + if (flags & ZEBRA_MACIP_TYPE_SYNC_PATH) { + zebra_evpn_process_sync_macip_add(zevpn, macaddr, ipa_len, + ipaddr, flags, seq, esi); + return; + } + + /* The remote VTEP specified should normally exist, but it is + * possible that when peering comes up, peer may advertise MACIP + * routes before advertising type-3 routes. + */ + if (vtep_ip.s_addr) { + zvtep = zebra_evpn_vtep_find(zevpn, &vtep_ip); + if (!zvtep) { + zvtep = zebra_evpn_vtep_add(zevpn, &vtep_ip, + VXLAN_FLOOD_DISABLED); + if (!zvtep) { + flog_err( + EC_ZEBRA_VTEP_ADD_FAILED, + "Failed to add remote VTEP, VNI %u zevpn %p upon remote MACIP ADD", + vni, zevpn); + return; + } + + zebra_evpn_vtep_install(zevpn, zvtep); + } + } + + zvrf = zebra_vrf_get_evpn(); + if (!zvrf) + return; + + + if (process_mac_remote_macip_add(zevpn, zvrf, macaddr, ipa_len, ipaddr, + &mac, vtep_ip, flags, seq, esi) + != 0) + return; + + process_neigh_remote_macip_add(zevpn, zvrf, ipaddr, mac, vtep_ip, flags, + seq); +} + +/* Process a remote MACIP delete from BGP. */ +void process_remote_macip_del(vni_t vni, struct ethaddr *macaddr, + uint16_t ipa_len, struct ipaddr *ipaddr, + struct in_addr vtep_ip) +{ + zebra_evpn_t *zevpn; + zebra_mac_t *mac = NULL; + zebra_neigh_t *n = NULL; + struct interface *ifp = NULL; + struct zebra_if *zif = NULL; + struct zebra_ns *zns; + struct zebra_l2info_vxlan *vxl; + struct zebra_vrf *zvrf; + char buf[ETHER_ADDR_STRLEN]; + char buf1[INET6_ADDRSTRLEN]; + + /* Locate EVPN hash entry - expected to exist. */ + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Unknown VNI %u upon remote MACIP DEL", vni); + return; + } + + ifp = zevpn->vxlan_if; + if (ifp) + zif = ifp->info; + if (!ifp || !if_is_operative(ifp) || !zif || !zif->brslave_info.br_if) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Ignoring remote MACIP DEL VNI %u, invalid interface state or info", + vni); + return; + } + zns = zebra_ns_lookup(NS_DEFAULT); + vxl = &zif->l2info.vxl; + + mac = zebra_evpn_mac_lookup(zevpn, macaddr); + if (ipa_len) + n = zebra_evpn_neigh_lookup(zevpn, ipaddr); + + if (n && !mac) { + zlog_warn( + "Failed to locate MAC %s for neigh %s VNI %u upon remote MACIP DEL", + prefix_mac2str(macaddr, buf, sizeof(buf)), + ipaddr2str(ipaddr, buf1, sizeof(buf1)), vni); + return; + } + + /* If the remote mac or neighbor doesn't exist there is nothing + * more to do. Otherwise, uninstall the entry and then remove it. + */ + if (!mac && !n) + return; + + zvrf = vrf_info_lookup(zevpn->vxlan_if->vrf_id); + + /* Ignore the delete if this mac is a gateway mac-ip */ + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL) + && CHECK_FLAG(mac->flags, ZEBRA_MAC_DEF_GW)) { + zlog_warn( + "Ignore remote MACIP DEL VNI %u MAC %s%s%s as MAC is already configured as gateway MAC", + vni, prefix_mac2str(macaddr, buf, sizeof(buf)), + ipa_len ? " IP " : "", + ipa_len ? ipaddr2str(ipaddr, buf1, sizeof(buf1)) : ""); + return; + } + + /* Uninstall remote neighbor or MAC. */ + if (n) + zebra_evpn_neigh_remote_uninstall(zevpn, zvrf, n, mac, ipaddr); + else { + /* DAD: when MAC is freeze state as remote learn event, + * remote mac-ip delete event is received will result in freeze + * entry removal, first fetch kernel for the same entry present + * as LOCAL and reachable, avoid deleting this entry instead + * use kerenel local entry to update during unfreeze time. + */ + if (zvrf->dad_freeze + && CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE) + && CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%s: MAC %s (flags 0x%x) is remote and duplicate, read kernel for local entry", + __func__, + prefix_mac2str(macaddr, buf, + sizeof(buf)), + mac->flags); + macfdb_read_specific_mac(zns, zif->brslave_info.br_if, + macaddr, vxl->access_vlan); + } + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) { + if (!ipa_len) + zebra_evpn_sync_mac_del(mac); + } else if (CHECK_FLAG(mac->flags, ZEBRA_NEIGH_REMOTE)) { + zebra_evpn_rem_mac_del(zevpn, mac); + } + } +} + +/************************** EVPN BGP config management ************************/ +void zebra_evpn_cfg_cleanup(struct hash_bucket *bucket, void *ctxt) +{ + zebra_evpn_t *zevpn = NULL; + + zevpn = (zebra_evpn_t *)bucket->data; + zevpn->advertise_gw_macip = 0; + zevpn->advertise_svi_macip = 0; + zevpn->advertise_subnet = 0; + + zebra_evpn_neigh_del_all(zevpn, 1, 0, + DEL_REMOTE_NEIGH | DEL_REMOTE_NEIGH_FROM_VTEP); + zebra_evpn_mac_del_all(zevpn, 1, 0, + DEL_REMOTE_MAC | DEL_REMOTE_MAC_FROM_VTEP); + zebra_evpn_vtep_del_all(zevpn, 1); +} diff --git a/zebra/zebra_evpn.h b/zebra/zebra_evpn.h new file mode 100644 index 0000000000..27392ec85c --- /dev/null +++ b/zebra/zebra_evpn.h @@ -0,0 +1,217 @@ +/* + * Zebra EVPN Data structures and definitions + * These are "internal" to this function. + * Copyright (C) 2016, 2017 Cumulus Networks, Inc. + * Copyright (C) 2020 Volta Networks. + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef _ZEBRA_EVPN_H +#define _ZEBRA_EVPN_H + +#include + +#include "if.h" +#include "linklist.h" +#include "bitfield.h" + +#include "zebra/zebra_l2.h" +#include "zebra/interface.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct zebra_evpn_t_ zebra_evpn_t; +typedef struct zebra_vtep_t_ zebra_vtep_t; + +RB_HEAD(zebra_es_evi_rb_head, zebra_evpn_es_evi); +RB_PROTOTYPE(zebra_es_evi_rb_head, zebra_evpn_es_evi, rb_node, + zebra_es_evi_rb_cmp); + +/* Private Structure to pass callback data for hash iterator */ +struct zebra_evpn_show { + struct vty *vty; + json_object *json; + struct zebra_vrf *zvrf; + bool use_json; +}; + +/* + * VTEP info + * + * Right now, this just has each remote VTEP's IP address. + */ +struct zebra_vtep_t_ { + /* Remote IP. */ + /* NOTE: Can only be IPv4 right now. */ + struct in_addr vtep_ip; + /* Flood mode (one of enum vxlan_flood_control) based on the PMSI + * tunnel type advertised by the remote VTEP + */ + int flood_control; + + /* Links. */ + struct zebra_vtep_t_ *next; + struct zebra_vtep_t_ *prev; +}; + +/* + * VNI hash table + * + * Contains information pertaining to a VNI: + * - the list of remote VTEPs (with this VNI) + */ +struct zebra_evpn_t_ { + /* VNI - key */ + vni_t vni; + + /* ES flags */ + uint32_t flags; +#define ZEVPN_READY_FOR_BGP (1 << 0) /* ready to be sent to BGP */ + + /* Flag for advertising gw macip */ + uint8_t advertise_gw_macip; + + /* Flag for advertising svi macip */ + uint8_t advertise_svi_macip; + + /* Flag for advertising gw macip */ + uint8_t advertise_subnet; + + /* Corresponding VxLAN interface. */ + struct interface *vxlan_if; + + /* List of remote VTEPs */ + zebra_vtep_t *vteps; + + /* Local IP */ + struct in_addr local_vtep_ip; + + /* PIM-SM MDT group for BUM flooding */ + struct in_addr mcast_grp; + + /* tenant VRF, if any */ + vrf_id_t vrf_id; + + /* List of local or remote MAC */ + struct hash *mac_table; + + /* List of local or remote neighbors (MAC+IP) */ + struct hash *neigh_table; + + /* RB tree of ES-EVIs */ + struct zebra_es_evi_rb_head es_evi_rb_tree; + + /* List of local ESs */ + struct list *local_es_evi_list; +}; + +/* for parsing evpn and vni contexts */ +struct zebra_from_svi_param { + struct interface *br_if; + struct interface *svi_if; + struct zebra_if *zif; + uint8_t bridge_vlan_aware; + vlanid_t vid; +}; + +struct interface *zvni_map_to_svi(vlanid_t vid, struct interface *br_if); + +static inline struct interface *zevpn_map_to_svi(zebra_evpn_t *zevpn) +{ + struct interface *ifp; + struct zebra_if *zif = NULL; + struct zebra_l2info_vxlan zl2_info; + + ifp = zevpn->vxlan_if; + if (!ifp) + return NULL; + zif = ifp->info; + if (!zif) + return NULL; + + /* If down or not mapped to a bridge, we're done. */ + if (!if_is_operative(ifp) || !zif->brslave_info.br_if) + return NULL; + zl2_info = zif->l2info.vxl; + return zvni_map_to_svi(zl2_info.access_vlan, zif->brslave_info.br_if); +} + +int advertise_gw_macip_enabled(zebra_evpn_t *zevpn); +int advertise_svi_macip_enabled(zebra_evpn_t *zevpn); +void zebra_evpn_print(zebra_evpn_t *zevpn, void **ctxt); +void zebra_evpn_print_hash(struct hash_bucket *bucket, void *ctxt[]); +void zebra_evpn_print_hash_detail(struct hash_bucket *bucket, void *data); +int zebra_evpn_add_macip_for_intf(struct interface *ifp, zebra_evpn_t *zevpn); +int zebra_evpn_del_macip_for_intf(struct interface *ifp, zebra_evpn_t *zevpn); +int zebra_evpn_advertise_subnet(zebra_evpn_t *zevpn, struct interface *ifp, + int advertise); +int zebra_evpn_gw_macip_add(struct interface *ifp, zebra_evpn_t *zevpn, + struct ethaddr *macaddr, struct ipaddr *ip); +int zebra_evpn_gw_macip_del(struct interface *ifp, zebra_evpn_t *zevpn, + struct ipaddr *ip); +void zebra_evpn_gw_macip_del_for_evpn_hash(struct hash_bucket *bucket, + void *ctxt); +void zebra_evpn_gw_macip_add_for_evpn_hash(struct hash_bucket *bucket, + void *ctxt); +void zebra_evpn_svi_macip_del_for_evpn_hash(struct hash_bucket *bucket, + void *ctxt); +zebra_evpn_t *zebra_evpn_map_vlan(struct interface *ifp, + struct interface *br_if, vlanid_t vid); +zebra_evpn_t *zebra_evpn_from_svi(struct interface *ifp, + struct interface *br_if); +struct interface *zebra_evpn_map_to_macvlan(struct interface *br_if, + struct interface *svi_if); +void zebra_evpn_install_mac_hash(struct hash_bucket *bucket, void *ctxt); +void zebra_evpn_read_mac_neigh(zebra_evpn_t *zevpn, struct interface *ifp); +unsigned int zebra_evpn_hash_keymake(const void *p); +bool zebra_evpn_hash_cmp(const void *p1, const void *p2); +int zebra_evpn_list_cmp(void *p1, void *p2); +void *zebra_evpn_alloc(void *p); +zebra_evpn_t *zebra_evpn_lookup(vni_t vni); +zebra_evpn_t *zebra_evpn_add(vni_t vni); +int zebra_evpn_del(zebra_evpn_t *zevpn); +int zebra_evpn_send_add_to_client(zebra_evpn_t *zevpn); +int zebra_evpn_send_del_to_client(zebra_evpn_t *zevpn); +zebra_vtep_t *zebra_evpn_vtep_find(zebra_evpn_t *zevpn, + struct in_addr *vtep_ip); +zebra_vtep_t *zebra_evpn_vtep_add(zebra_evpn_t *zevpn, struct in_addr *vtep_ip, + int flood_control); +int zebra_evpn_vtep_del(zebra_evpn_t *zevpn, zebra_vtep_t *zvtep); +int zebra_evpn_vtep_del_all(zebra_evpn_t *zevpn, int uninstall); +int zebra_evpn_vtep_install(zebra_evpn_t *zevpn, zebra_vtep_t *zvtep); +int zebra_evpn_vtep_uninstall(zebra_evpn_t *zevpn, struct in_addr *vtep_ip); +void zebra_evpn_handle_flooding_remote_vteps(struct hash_bucket *bucket, + void *zvrf); +void zebra_evpn_cleanup_all(struct hash_bucket *bucket, void *arg); +void process_remote_macip_add(vni_t vni, struct ethaddr *macaddr, + uint16_t ipa_len, struct ipaddr *ipaddr, + uint8_t flags, uint32_t seq, + struct in_addr vtep_ip, esi_t *esi); +void process_remote_macip_del(vni_t vni, struct ethaddr *macaddr, + uint16_t ipa_len, struct ipaddr *ipaddr, + struct in_addr vtep_ip); +void zebra_evpn_cfg_cleanup(struct hash_bucket *bucket, void *ctxt); + +#ifdef __cplusplus +} +#endif + +#endif /*_ZEBRA_EVPN_H */ diff --git a/zebra/zebra_evpn_mac.c b/zebra/zebra_evpn_mac.c new file mode 100644 index 0000000000..75031ddba6 --- /dev/null +++ b/zebra/zebra_evpn_mac.c @@ -0,0 +1,2247 @@ +/* + * Zebra EVPN for VxLAN code + * Copyright (C) 2016, 2017 Cumulus Networks, Inc. + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include + +#include "hash.h" +#include "interface.h" +#include "jhash.h" +#include "memory.h" +#include "prefix.h" +#include "vlan.h" +#include "json.h" + +#include "zebra/zserv.h" +#include "zebra/debug.h" +#include "zebra/zebra_router.h" +#include "zebra/zebra_memory.h" +#include "zebra/zebra_errors.h" +#include "zebra/zebra_vrf.h" +#include "zebra/zebra_evpn.h" +#include "zebra/zebra_evpn_mh.h" +#include "zebra/zebra_evpn_mac.h" +#include "zebra/zebra_evpn_neigh.h" + +DEFINE_MTYPE_STATIC(ZEBRA, MAC, "EVPN MAC"); + +/* + * Return number of valid MACs in an EVPN's MAC hash table - all + * remote MACs and non-internal (auto) local MACs count. + */ +uint32_t num_valid_macs(zebra_evpn_t *zevpn) +{ + unsigned int i; + uint32_t num_macs = 0; + struct hash *hash; + struct hash_bucket *hb; + zebra_mac_t *mac; + + hash = zevpn->mac_table; + if (!hash) + return num_macs; + for (i = 0; i < hash->size; i++) { + for (hb = hash->index[i]; hb; hb = hb->next) { + mac = (zebra_mac_t *)hb->data; + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE) + || CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL) + || !CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO)) + num_macs++; + } + } + + return num_macs; +} + +uint32_t num_dup_detected_macs(zebra_evpn_t *zevpn) +{ + unsigned int i; + uint32_t num_macs = 0; + struct hash *hash; + struct hash_bucket *hb; + zebra_mac_t *mac; + + hash = zevpn->mac_table; + if (!hash) + return num_macs; + for (i = 0; i < hash->size; i++) { + for (hb = hash->index[i]; hb; hb = hb->next) { + mac = (zebra_mac_t *)hb->data; + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) + num_macs++; + } + } + + return num_macs; +} + +/* + * Install remote MAC into the forwarding plane. + */ +int zebra_evpn_rem_mac_install(zebra_evpn_t *zevpn, zebra_mac_t *mac, + bool was_static) +{ + const struct zebra_if *zif, *br_zif; + const struct zebra_l2info_vxlan *vxl; + bool sticky; + enum zebra_dplane_result res; + const struct interface *br_ifp; + vlanid_t vid; + uint32_t nhg_id; + struct in_addr vtep_ip; + + if (!(mac->flags & ZEBRA_MAC_REMOTE)) + return 0; + + zif = zevpn->vxlan_if->info; + if (!zif) + return -1; + + br_ifp = zif->brslave_info.br_if; + if (br_ifp == NULL) + return -1; + + vxl = &zif->l2info.vxl; + + sticky = !!CHECK_FLAG(mac->flags, + (ZEBRA_MAC_STICKY | ZEBRA_MAC_REMOTE_DEF_GW)); + + /* If nexthop group for the FDB entry is inactive (not programmed in + * the dataplane) the MAC entry cannot be installed + */ + if (mac->es) { + if (!(mac->es->flags & ZEBRA_EVPNES_NHG_ACTIVE)) + return -1; + nhg_id = mac->es->nhg_id; + vtep_ip.s_addr = 0; + } else { + nhg_id = 0; + vtep_ip = mac->fwd_info.r_vtep_ip; + } + + br_zif = (const struct zebra_if *)(br_ifp->info); + + if (IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(br_zif)) + vid = vxl->access_vlan; + else + vid = 0; + + res = dplane_rem_mac_add(zevpn->vxlan_if, br_ifp, vid, &mac->macaddr, + vtep_ip, sticky, nhg_id, was_static); + if (res != ZEBRA_DPLANE_REQUEST_FAILURE) + return 0; + else + return -1; +} + +/* + * Uninstall remote MAC from the forwarding plane. + */ +int zebra_evpn_rem_mac_uninstall(zebra_evpn_t *zevpn, zebra_mac_t *mac) +{ + const struct zebra_if *zif, *br_zif; + const struct zebra_l2info_vxlan *vxl; + struct in_addr vtep_ip; + const struct interface *ifp, *br_ifp; + vlanid_t vid; + enum zebra_dplane_result res; + + if (!(mac->flags & ZEBRA_MAC_REMOTE)) + return 0; + + if (!zevpn->vxlan_if) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "VNI %u hash %p couldn't be uninstalled - no intf", + zevpn->vni, zevpn); + return -1; + } + + zif = zevpn->vxlan_if->info; + if (!zif) + return -1; + + br_ifp = zif->brslave_info.br_if; + if (br_ifp == NULL) + return -1; + + vxl = &zif->l2info.vxl; + + br_zif = (const struct zebra_if *)br_ifp->info; + + if (IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(br_zif)) + vid = vxl->access_vlan; + else + vid = 0; + + ifp = zevpn->vxlan_if; + vtep_ip = mac->fwd_info.r_vtep_ip; + + res = dplane_rem_mac_del(ifp, br_ifp, vid, &mac->macaddr, vtep_ip); + if (res != ZEBRA_DPLANE_REQUEST_FAILURE) + return 0; + else + return -1; +} + +/* + * Decrement neighbor refcount of MAC; uninstall and free it if + * appropriate. + */ +void zebra_evpn_deref_ip2mac(zebra_evpn_t *zevpn, zebra_mac_t *mac) +{ + if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO)) + return; + + /* If all remote neighbors referencing a remote MAC go away, + * we need to uninstall the MAC. + */ + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE) + && remote_neigh_count(mac) == 0) { + zebra_evpn_rem_mac_uninstall(zevpn, mac); + zebra_evpn_es_mac_deref_entry(mac); + UNSET_FLAG(mac->flags, ZEBRA_MAC_REMOTE); + } + + /* If no neighbors, delete the MAC. */ + if (list_isempty(mac->neigh_list)) + zebra_evpn_mac_del(zevpn, mac); +} + +static void zebra_evpn_mac_get_access_info(zebra_mac_t *mac, + struct interface **ifpP, + vlanid_t *vid) +{ + /* if the mac is associated with an ES we must get the access + * info from the ES + */ + if (mac->es) { + struct zebra_if *zif; + + /* get the access port from the es */ + *ifpP = mac->es->zif ? mac->es->zif->ifp : NULL; + /* get the vlan from the EVPN */ + if (mac->zevpn->vxlan_if) { + zif = mac->zevpn->vxlan_if->info; + *vid = zif->l2info.vxl.access_vlan; + } else { + *vid = 0; + } + } else { + struct zebra_ns *zns; + + *vid = mac->fwd_info.local.vid; + zns = zebra_ns_lookup(mac->fwd_info.local.ns_id); + *ifpP = if_lookup_by_index_per_ns(zns, + mac->fwd_info.local.ifindex); + } +} + +static int zebra_evpn_dad_mac_auto_recovery_exp(struct thread *t) +{ + struct zebra_vrf *zvrf = NULL; + zebra_mac_t *mac = NULL; + zebra_evpn_t *zevpn = NULL; + struct listnode *node = NULL; + zebra_neigh_t *nbr = NULL; + char buf[ETHER_ADDR_STRLEN]; + + mac = THREAD_ARG(t); + + /* since this is asynchronous we need sanity checks*/ + zvrf = vrf_info_lookup(mac->zevpn->vrf_id); + if (!zvrf) + return 0; + + zevpn = zebra_evpn_lookup(mac->zevpn->vni); + if (!zevpn) + return 0; + + mac = zebra_evpn_mac_lookup(zevpn, &mac->macaddr); + if (!mac) + return 0; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%s: duplicate addr mac %s flags 0x%x learn count %u host count %u auto recovery expired", + __func__, + prefix_mac2str(&mac->macaddr, buf, sizeof(buf)), + mac->flags, mac->dad_count, listcount(mac->neigh_list)); + + /* Remove all IPs as duplicate associcated with this MAC */ + for (ALL_LIST_ELEMENTS_RO(mac->neigh_list, node, nbr)) { + if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) { + if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL)) + ZEBRA_NEIGH_SET_INACTIVE(nbr); + else if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_REMOTE)) + zebra_evpn_rem_neigh_install( + zevpn, nbr, false /*was_static*/); + } + + UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); + nbr->dad_count = 0; + nbr->detect_start_time.tv_sec = 0; + nbr->dad_dup_detect_time = 0; + } + + UNSET_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE); + mac->dad_count = 0; + mac->detect_start_time.tv_sec = 0; + mac->detect_start_time.tv_usec = 0; + mac->dad_dup_detect_time = 0; + mac->dad_mac_auto_recovery_timer = NULL; + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) { + /* Inform to BGP */ + if (zebra_evpn_mac_send_add_to_client(zevpn->vni, &mac->macaddr, + mac->flags, mac->loc_seq, + mac->es)) + return -1; + + /* Process all neighbors associated with this MAC. */ + zebra_evpn_process_neigh_on_local_mac_change(zevpn, mac, 0, + 0 /*es_change*/); + + } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { + zebra_evpn_process_neigh_on_remote_mac_add(zevpn, mac); + + /* Install the entry. */ + zebra_evpn_rem_mac_install(zevpn, mac, false /* was_static */); + } + + return 0; +} + +static void zebra_evpn_dup_addr_detect_for_mac(struct zebra_vrf *zvrf, + zebra_mac_t *mac, + struct in_addr vtep_ip, + bool do_dad, bool *is_dup_detect, + bool is_local) +{ + zebra_neigh_t *nbr; + struct listnode *node = NULL; + struct timeval elapsed = {0, 0}; + char buf[ETHER_ADDR_STRLEN]; + char buf1[INET6_ADDRSTRLEN]; + bool reset_params = false; + + if (!(zvrf->dup_addr_detect && do_dad)) + return; + + /* MAC is detected as duplicate, + * Local MAC event -> hold on advertising to BGP. + * Remote MAC event -> hold on installing it. + */ + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%s: duplicate addr MAC %s flags 0x%x skip update to client, learn count %u recover time %u", + __func__, + prefix_mac2str(&mac->macaddr, buf, sizeof(buf)), + mac->flags, mac->dad_count, + zvrf->dad_freeze_time); + + /* For duplicate MAC do not update + * client but update neigh due to + * this MAC update. + */ + if (zvrf->dad_freeze) + *is_dup_detect = true; + + return; + } + + /* Check if detection time (M-secs) expired. + * Reset learn count and detection start time. + */ + monotime_since(&mac->detect_start_time, &elapsed); + reset_params = (elapsed.tv_sec > zvrf->dad_time); + if (is_local && !reset_params) { + /* RFC-7432: A PE/VTEP that detects a MAC mobility + * event via LOCAL learning starts an M-second timer. + * + * NOTE: This is the START of the probe with count is + * 0 during LOCAL learn event. + * (mac->dad_count == 0 || elapsed.tv_sec >= zvrf->dad_time) + */ + reset_params = !mac->dad_count; + } + + if (reset_params) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%s: duplicate addr MAC %s flags 0x%x detection time passed, reset learn count %u", + __func__, + prefix_mac2str(&mac->macaddr, buf, sizeof(buf)), + mac->flags, mac->dad_count); + + mac->dad_count = 0; + /* Start dup. addr detection (DAD) start time, + * ONLY during LOCAL learn. + */ + if (is_local) + monotime(&mac->detect_start_time); + + } else if (!is_local) { + /* For REMOTE MAC, increment detection count + * ONLY while in probe window, once window passed, + * next local learn event should trigger DAD. + */ + mac->dad_count++; + } + + /* For LOCAL MAC learn event, once count is reset above via either + * initial/start detection time or passed the probe time, the count + * needs to be incremented. + */ + if (is_local) + mac->dad_count++; + + if (mac->dad_count >= zvrf->dad_max_moves) { + flog_warn(EC_ZEBRA_DUP_MAC_DETECTED, + "VNI %u: MAC %s detected as duplicate during %s VTEP %s", + mac->zevpn->vni, + prefix_mac2str(&mac->macaddr, buf, sizeof(buf)), + is_local ? "local update, last" : + "remote update, from", inet_ntoa(vtep_ip)); + + SET_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE); + + /* Capture Duplicate detection time */ + mac->dad_dup_detect_time = monotime(NULL); + + /* Mark all IPs/Neighs as duplicate + * associcated with this MAC + */ + for (ALL_LIST_ELEMENTS_RO(mac->neigh_list, node, nbr)) { + + /* Ony Mark IPs which are Local */ + if (!CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL)) + continue; + + SET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); + + nbr->dad_dup_detect_time = monotime(NULL); + + flog_warn(EC_ZEBRA_DUP_IP_INHERIT_DETECTED, + "VNI %u: MAC %s IP %s detected as duplicate during %s update, inherit duplicate from MAC", + mac->zevpn->vni, + prefix_mac2str(&mac->macaddr, + buf, sizeof(buf)), + ipaddr2str(&nbr->ip, buf1, sizeof(buf1)), + is_local ? "local" : "remote"); + } + + /* Start auto recovery timer for this MAC */ + THREAD_OFF(mac->dad_mac_auto_recovery_timer); + if (zvrf->dad_freeze && zvrf->dad_freeze_time) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%s: duplicate addr MAC %s flags 0x%x auto recovery time %u start", + __func__, + prefix_mac2str(&mac->macaddr, buf, + sizeof(buf)), + mac->flags, zvrf->dad_freeze_time); + + thread_add_timer(zrouter.master, + zebra_evpn_dad_mac_auto_recovery_exp, + mac, zvrf->dad_freeze_time, + &mac->dad_mac_auto_recovery_timer); + } + + /* In case of local update, do not inform to client (BGPd), + * upd_neigh for neigh sequence change. + */ + if (zvrf->dad_freeze) + *is_dup_detect = true; + } +} + +/* + * Print a specific MAC entry. + */ +void zebra_evpn_print_mac(zebra_mac_t *mac, void *ctxt, json_object *json) +{ + struct vty *vty; + zebra_neigh_t *n = NULL; + struct listnode *node = NULL; + char buf1[ETHER_ADDR_STRLEN]; + char buf2[INET6_ADDRSTRLEN]; + struct zebra_vrf *zvrf; + struct timeval detect_start_time = {0, 0}; + char timebuf[MONOTIME_STRLEN]; + char thread_buf[THREAD_TIMER_STRLEN]; + + zvrf = zebra_vrf_get_evpn(); + if (!zvrf) + return; + + vty = (struct vty *)ctxt; + prefix_mac2str(&mac->macaddr, buf1, sizeof(buf1)); + + if (json) { + json_object *json_mac = json_object_new_object(); + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) { + struct interface *ifp; + vlanid_t vid; + + zebra_evpn_mac_get_access_info(mac, &ifp, &vid); + json_object_string_add(json_mac, "type", "local"); + if (ifp) { + json_object_string_add(json_mac, "intf", + ifp->name); + json_object_int_add(json_mac, "ifindex", + ifp->ifindex); + } + if (vid) + json_object_int_add(json_mac, "vlan", vid); + } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { + json_object_string_add(json_mac, "type", "remote"); + json_object_string_add( + json_mac, "remoteVtep", + inet_ntoa(mac->fwd_info.r_vtep_ip)); + } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO)) + json_object_string_add(json_mac, "type", "auto"); + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY)) + json_object_boolean_true_add(json_mac, "stickyMac"); + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DEF_GW)) + json_object_boolean_true_add(json_mac, + "defaultGateway"); + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE_DEF_GW)) + json_object_boolean_true_add(json_mac, + "remoteGatewayMac"); + + json_object_int_add(json_mac, "localSequence", mac->loc_seq); + json_object_int_add(json_mac, "remoteSequence", mac->rem_seq); + + json_object_int_add(json_mac, "detectionCount", mac->dad_count); + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) + json_object_boolean_true_add(json_mac, "isDuplicate"); + else + json_object_boolean_false_add(json_mac, "isDuplicate"); + + json_object_int_add(json_mac, "syncNeighCount", + mac->sync_neigh_cnt); + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL_INACTIVE)) + json_object_boolean_true_add(json_mac, "localInactive"); + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_PROXY)) + json_object_boolean_true_add(json_mac, "peerProxy"); + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_ACTIVE)) + json_object_boolean_true_add(json_mac, "peerActive"); + if (mac->hold_timer) + json_object_string_add( + json_mac, "peerActiveHold", + thread_timer_to_hhmmss(thread_buf, + sizeof(thread_buf), + mac->hold_timer)); + if (mac->es) + json_object_string_add(json_mac, "esi", + mac->es->esi_str); + /* print all the associated neigh */ + if (!listcount(mac->neigh_list)) + json_object_string_add(json_mac, "neighbors", "none"); + else { + json_object *json_active_nbrs = json_object_new_array(); + json_object *json_inactive_nbrs = + json_object_new_array(); + json_object *json_nbrs = json_object_new_object(); + + for (ALL_LIST_ELEMENTS_RO(mac->neigh_list, node, n)) { + if (IS_ZEBRA_NEIGH_ACTIVE(n)) + json_object_array_add( + json_active_nbrs, + json_object_new_string( + ipaddr2str( + &n->ip, buf2, + sizeof(buf2)))); + else + json_object_array_add( + json_inactive_nbrs, + json_object_new_string( + ipaddr2str( + &n->ip, buf2, + sizeof(buf2)))); + } + + json_object_object_add(json_nbrs, "active", + json_active_nbrs); + json_object_object_add(json_nbrs, "inactive", + json_inactive_nbrs); + json_object_object_add(json_mac, "neighbors", + json_nbrs); + } + + json_object_object_add(json, buf1, json_mac); + } else { + vty_out(vty, "MAC: %s\n", buf1); + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) { + struct interface *ifp; + vlanid_t vid; + + zebra_evpn_mac_get_access_info(mac, &ifp, &vid); + + if (mac->es) + vty_out(vty, " ESI: %s\n", mac->es->esi_str); + + if (ifp) + vty_out(vty, " Intf: %s(%u)", ifp->name, + ifp->ifindex); + else + vty_out(vty, " Intf: -"); + vty_out(vty, " VLAN: %u", vid); + } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { + if (mac->es) + vty_out(vty, " Remote ES: %s", + mac->es->esi_str); + else + vty_out(vty, " Remote VTEP: %s", + inet_ntoa(mac->fwd_info.r_vtep_ip)); + } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO)) { + vty_out(vty, " Auto Mac "); + } + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY)) + vty_out(vty, " Sticky Mac "); + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DEF_GW)) + vty_out(vty, " Default-gateway Mac "); + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE_DEF_GW)) + vty_out(vty, " Remote-gateway Mac "); + + vty_out(vty, "\n"); + vty_out(vty, " Sync-info: neigh#: %u", mac->sync_neigh_cnt); + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL_INACTIVE)) + vty_out(vty, " local-inactive"); + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_PROXY)) + vty_out(vty, " peer-proxy"); + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_ACTIVE)) + vty_out(vty, " peer-active"); + if (mac->hold_timer) + vty_out(vty, " (ht: %s)", + thread_timer_to_hhmmss(thread_buf, + sizeof(thread_buf), + mac->hold_timer)); + vty_out(vty, "\n"); + vty_out(vty, " Local Seq: %u Remote Seq: %u", mac->loc_seq, + mac->rem_seq); + vty_out(vty, "\n"); + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) { + vty_out(vty, " Duplicate, detected at %s", + time_to_string(mac->dad_dup_detect_time, + timebuf)); + } else if (mac->dad_count) { + monotime_since(&mac->detect_start_time, + &detect_start_time); + if (detect_start_time.tv_sec <= zvrf->dad_time) { + time_to_string(mac->detect_start_time.tv_sec, + timebuf); + vty_out(vty, + " Duplicate detection started at %s, detection count %u\n", + timebuf, mac->dad_count); + } + } + + /* print all the associated neigh */ + vty_out(vty, " Neighbors:\n"); + if (!listcount(mac->neigh_list)) + vty_out(vty, " No Neighbors\n"); + else { + for (ALL_LIST_ELEMENTS_RO(mac->neigh_list, node, n)) { + vty_out(vty, " %s %s\n", + ipaddr2str(&n->ip, buf2, sizeof(buf2)), + (IS_ZEBRA_NEIGH_ACTIVE(n) + ? "Active" + : "Inactive")); + } + } + + vty_out(vty, "\n"); + } +} + +static char *zebra_evpn_print_mac_flags(zebra_mac_t *mac, char *flags_buf, + uint32_t flags_buf_sz) +{ + snprintf(flags_buf, flags_buf_sz, "%s%s%s%s", + mac->sync_neigh_cnt ? + "N" : "", + (mac->flags & ZEBRA_MAC_ES_PEER_ACTIVE) ? + "P" : "", + (mac->flags & ZEBRA_MAC_ES_PEER_PROXY) ? + "X" : "", + (mac->flags & ZEBRA_MAC_LOCAL_INACTIVE) ? + "I" : ""); + + return flags_buf; +} + +/* + * Print MAC hash entry - called for display of all MACs. + */ +void zebra_evpn_print_mac_hash(struct hash_bucket *bucket, void *ctxt) +{ + struct vty *vty; + json_object *json_mac_hdr = NULL, *json_mac = NULL; + zebra_mac_t *mac; + char buf1[ETHER_ADDR_STRLEN]; + struct mac_walk_ctx *wctx = ctxt; + char flags_buf[6]; + + vty = wctx->vty; + json_mac_hdr = wctx->json; + mac = (zebra_mac_t *)bucket->data; + + prefix_mac2str(&mac->macaddr, buf1, sizeof(buf1)); + + if (json_mac_hdr) + json_mac = json_object_new_object(); + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) { + struct interface *ifp; + vlanid_t vid; + + if (wctx->flags & SHOW_REMOTE_MAC_FROM_VTEP) + return; + + zebra_evpn_mac_get_access_info(mac, &ifp, &vid); + if (json_mac_hdr == NULL) { + vty_out(vty, "%-17s %-6s %-5s %-30s", buf1, "local", + zebra_evpn_print_mac_flags(mac, flags_buf, + sizeof(flags_buf)), + ifp ? ifp->name : "-"); + } else { + json_object_string_add(json_mac, "type", "local"); + if (ifp) + json_object_string_add(json_mac, "intf", + ifp->name); + } + if (vid) { + if (json_mac_hdr == NULL) + vty_out(vty, " %-5u", vid); + else + json_object_int_add(json_mac, "vlan", vid); + } else /* No vid? fill out the space */ + if (json_mac_hdr == NULL) + vty_out(vty, " %-5s", ""); + if (json_mac_hdr == NULL) { + vty_out(vty, " %u/%u", mac->loc_seq, mac->rem_seq); + vty_out(vty, "\n"); + } else { + json_object_int_add(json_mac, "localSequence", + mac->loc_seq); + json_object_int_add(json_mac, "remoteSequence", + mac->rem_seq); + json_object_int_add(json_mac, "detectionCount", + mac->dad_count); + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) + json_object_boolean_true_add(json_mac, + "isDuplicate"); + else + json_object_boolean_false_add(json_mac, + "isDuplicate"); + json_object_object_add(json_mac_hdr, buf1, json_mac); + } + + wctx->count++; + + } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { + + if ((wctx->flags & SHOW_REMOTE_MAC_FROM_VTEP) + && !IPV4_ADDR_SAME(&mac->fwd_info.r_vtep_ip, + &wctx->r_vtep_ip)) + return; + + if (json_mac_hdr == NULL) { + if ((wctx->flags & SHOW_REMOTE_MAC_FROM_VTEP) + && (wctx->count == 0)) { + vty_out(vty, "\nVNI %u\n\n", wctx->zevpn->vni); + vty_out(vty, "%-17s %-6s %-5s%-30s %-5s %s\n", + "MAC", "Type", "Flags", + "Intf/Remote ES/VTEP", "VLAN", + "Seq #'s"); + } + vty_out(vty, "%-17s %-6s %-5s %-30s %-5s %u/%u\n", buf1, + "remote", + zebra_evpn_print_mac_flags(mac, flags_buf, + sizeof(flags_buf)), + mac->es ? mac->es->esi_str + : inet_ntoa(mac->fwd_info.r_vtep_ip), + "", mac->loc_seq, mac->rem_seq); + } else { + json_object_string_add(json_mac, "type", "remote"); + json_object_string_add( + json_mac, "remoteVtep", + inet_ntoa(mac->fwd_info.r_vtep_ip)); + json_object_object_add(json_mac_hdr, buf1, json_mac); + json_object_int_add(json_mac, "localSequence", + mac->loc_seq); + json_object_int_add(json_mac, "remoteSequence", + mac->rem_seq); + json_object_int_add(json_mac, "detectionCount", + mac->dad_count); + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) + json_object_boolean_true_add(json_mac, + "isDuplicate"); + else + json_object_boolean_false_add(json_mac, + "isDuplicate"); + } + + wctx->count++; + } +} + +/* + * Print MAC hash entry in detail - called for display of all MACs. + */ +void zebra_evpn_print_mac_hash_detail(struct hash_bucket *bucket, void *ctxt) +{ + struct vty *vty; + json_object *json_mac_hdr = NULL; + zebra_mac_t *mac; + struct mac_walk_ctx *wctx = ctxt; + char buf1[ETHER_ADDR_STRLEN]; + + vty = wctx->vty; + json_mac_hdr = wctx->json; + mac = (zebra_mac_t *)bucket->data; + if (!mac) + return; + + wctx->count++; + prefix_mac2str(&mac->macaddr, buf1, sizeof(buf1)); + + zebra_evpn_print_mac(mac, vty, json_mac_hdr); +} + +/* + * Inform BGP about local MACIP. + */ +int zebra_evpn_macip_send_msg_to_client(vni_t vni, struct ethaddr *macaddr, + struct ipaddr *ip, uint8_t flags, + uint32_t seq, int state, + struct zebra_evpn_es *es, uint16_t cmd) +{ + char buf[ETHER_ADDR_STRLEN]; + char buf2[INET6_ADDRSTRLEN]; + int ipa_len; + struct zserv *client = NULL; + struct stream *s = NULL; + esi_t *esi = es ? &es->esi : zero_esi; + + client = zserv_find_client(ZEBRA_ROUTE_BGP, 0); + /* BGP may not be running. */ + if (!client) + return 0; + + s = stream_new(ZEBRA_MAX_PACKET_SIZ); + + zclient_create_header(s, cmd, zebra_vrf_get_evpn_id()); + stream_putl(s, vni); + stream_put(s, macaddr->octet, ETH_ALEN); + if (ip) { + ipa_len = 0; + if (IS_IPADDR_V4(ip)) + ipa_len = IPV4_MAX_BYTELEN; + else if (IS_IPADDR_V6(ip)) + ipa_len = IPV6_MAX_BYTELEN; + + stream_putl(s, ipa_len); /* IP address length */ + if (ipa_len) + stream_put(s, &ip->ip.addr, ipa_len); /* IP address */ + } else + stream_putl(s, 0); /* Just MAC. */ + + if (cmd == ZEBRA_MACIP_ADD) { + stream_putc(s, flags); /* sticky mac/gateway mac */ + stream_putl(s, seq); /* sequence number */ + stream_put(s, esi, sizeof(esi_t)); + } else { + stream_putl(s, state); /* state - active/inactive */ + } + + + /* Write packet size. */ + stream_putw_at(s, 0, stream_get_endp(s)); + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Send MACIP %s f 0x%x MAC %s IP %s seq %u L2-VNI %u ESI %s to %s", + (cmd == ZEBRA_MACIP_ADD) ? "Add" : "Del", flags, + prefix_mac2str(macaddr, buf, sizeof(buf)), + ipaddr2str(ip, buf2, sizeof(buf2)), seq, vni, + es ? es->esi_str : "-", + zebra_route_string(client->proto)); + + if (cmd == ZEBRA_MACIP_ADD) + client->macipadd_cnt++; + else + client->macipdel_cnt++; + + return zserv_send_message(client, s); +} + +static unsigned int mac_hash_keymake(const void *p) +{ + const zebra_mac_t *pmac = p; + const void *pnt = (void *)pmac->macaddr.octet; + + return jhash(pnt, ETH_ALEN, 0xa5a5a55a); +} + +/* + * Compare two MAC addresses. + */ +static bool mac_cmp(const void *p1, const void *p2) +{ + const zebra_mac_t *pmac1 = p1; + const zebra_mac_t *pmac2 = p2; + + if (pmac1 == NULL && pmac2 == NULL) + return true; + + if (pmac1 == NULL || pmac2 == NULL) + return false; + + return (memcmp(pmac1->macaddr.octet, pmac2->macaddr.octet, ETH_ALEN) + == 0); +} + +/* + * Callback to allocate MAC hash entry. + */ +static void *zebra_evpn_mac_alloc(void *p) +{ + const zebra_mac_t *tmp_mac = p; + zebra_mac_t *mac; + + mac = XCALLOC(MTYPE_MAC, sizeof(zebra_mac_t)); + *mac = *tmp_mac; + + return ((void *)mac); +} + +/* + * Add MAC entry. + */ +zebra_mac_t *zebra_evpn_mac_add(zebra_evpn_t *zevpn, struct ethaddr *macaddr) +{ + zebra_mac_t tmp_mac; + zebra_mac_t *mac = NULL; + + memset(&tmp_mac, 0, sizeof(zebra_mac_t)); + memcpy(&tmp_mac.macaddr, macaddr, ETH_ALEN); + mac = hash_get(zevpn->mac_table, &tmp_mac, zebra_evpn_mac_alloc); + assert(mac); + + mac->zevpn = zevpn; + mac->dad_mac_auto_recovery_timer = NULL; + + mac->neigh_list = list_new(); + mac->neigh_list->cmp = neigh_list_cmp; + + if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_MAC) { + char buf[ETHER_ADDR_STRLEN]; + + zlog_debug("%s: MAC %s flags 0x%x", __func__, + prefix_mac2str(&mac->macaddr, buf, sizeof(buf)), + mac->flags); + } + return mac; +} + +/* + * Delete MAC entry. + */ +int zebra_evpn_mac_del(zebra_evpn_t *zevpn, zebra_mac_t *mac) +{ + zebra_mac_t *tmp_mac; + + if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_MAC) { + char buf[ETHER_ADDR_STRLEN]; + + zlog_debug("%s: MAC %s flags 0x%x", __func__, + prefix_mac2str(&mac->macaddr, buf, sizeof(buf)), + mac->flags); + } + + /* force de-ref any ES entry linked to the MAC */ + zebra_evpn_es_mac_deref_entry(mac); + + /* Cancel proxy hold timer */ + zebra_evpn_mac_stop_hold_timer(mac); + + /* Cancel auto recovery */ + THREAD_OFF(mac->dad_mac_auto_recovery_timer); + + list_delete(&mac->neigh_list); + + /* Free the VNI hash entry and allocated memory. */ + tmp_mac = hash_release(zevpn->mac_table, mac); + XFREE(MTYPE_MAC, tmp_mac); + + return 0; +} + +static bool zebra_evpn_check_mac_del_from_db(struct mac_walk_ctx *wctx, + zebra_mac_t *mac) +{ + if ((wctx->flags & DEL_LOCAL_MAC) && (mac->flags & ZEBRA_MAC_LOCAL)) + return true; + else if ((wctx->flags & DEL_REMOTE_MAC) + && (mac->flags & ZEBRA_MAC_REMOTE)) + return true; + else if ((wctx->flags & DEL_REMOTE_MAC_FROM_VTEP) + && (mac->flags & ZEBRA_MAC_REMOTE) + && IPV4_ADDR_SAME(&mac->fwd_info.r_vtep_ip, &wctx->r_vtep_ip)) + return true; + else if ((wctx->flags & DEL_LOCAL_MAC) && (mac->flags & ZEBRA_MAC_AUTO) + && !listcount(mac->neigh_list)) { + if (IS_ZEBRA_DEBUG_VXLAN) { + char buf[ETHER_ADDR_STRLEN]; + + zlog_debug( + "%s: Del MAC %s flags 0x%x", __func__, + prefix_mac2str(&mac->macaddr, buf, sizeof(buf)), + mac->flags); + } + wctx->uninstall = 0; + + return true; + } + + return false; +} + +/* + * Free MAC hash entry (callback) + */ +static void zebra_evpn_mac_del_hash_entry(struct hash_bucket *bucket, void *arg) +{ + struct mac_walk_ctx *wctx = arg; + zebra_mac_t *mac = bucket->data; + + if (zebra_evpn_check_mac_del_from_db(wctx, mac)) { + if (wctx->upd_client && (mac->flags & ZEBRA_MAC_LOCAL)) { + zebra_evpn_mac_send_del_to_client(wctx->zevpn->vni, + &mac->macaddr, + mac->flags, false); + } + if (wctx->uninstall) { + if (zebra_evpn_mac_is_static(mac)) + zebra_evpn_sync_mac_dp_install( + mac, false /* set_inactive */, + true /* force_clear_static */, + __func__); + + if (mac->flags & ZEBRA_MAC_REMOTE) + zebra_evpn_rem_mac_uninstall(wctx->zevpn, mac); + } + + zebra_evpn_mac_del(wctx->zevpn, mac); + } + + return; +} + +/* + * Delete all MAC entries for this EVPN. + */ +void zebra_evpn_mac_del_all(zebra_evpn_t *zevpn, int uninstall, int upd_client, + uint32_t flags) +{ + struct mac_walk_ctx wctx; + + if (!zevpn->mac_table) + return; + + memset(&wctx, 0, sizeof(struct mac_walk_ctx)); + wctx.zevpn = zevpn; + wctx.uninstall = uninstall; + wctx.upd_client = upd_client; + wctx.flags = flags; + + hash_iterate(zevpn->mac_table, zebra_evpn_mac_del_hash_entry, &wctx); +} + +/* + * Look up MAC hash entry. + */ +zebra_mac_t *zebra_evpn_mac_lookup(zebra_evpn_t *zevpn, struct ethaddr *mac) +{ + zebra_mac_t tmp; + zebra_mac_t *pmac; + + memset(&tmp, 0, sizeof(tmp)); + memcpy(&tmp.macaddr, mac, ETH_ALEN); + pmac = hash_lookup(zevpn->mac_table, &tmp); + + return pmac; +} + +/* + * Inform BGP about local MAC addition. + */ +int zebra_evpn_mac_send_add_to_client(vni_t vni, struct ethaddr *macaddr, + uint32_t mac_flags, uint32_t seq, + struct zebra_evpn_es *es) +{ + uint8_t flags = 0; + + if (CHECK_FLAG(mac_flags, ZEBRA_MAC_LOCAL_INACTIVE)) { + /* host reachability has not been verified locally */ + + /* if no ES peer is claiming reachability we can't advertise the + * entry + */ + if (!CHECK_FLAG(mac_flags, ZEBRA_MAC_ES_PEER_ACTIVE)) + return 0; + + /* ES peers are claiming reachability; we will + * advertise the entry but with a proxy flag + */ + SET_FLAG(flags, ZEBRA_MACIP_TYPE_PROXY_ADVERT); + } + + if (CHECK_FLAG(mac_flags, ZEBRA_MAC_STICKY)) + SET_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY); + if (CHECK_FLAG(mac_flags, ZEBRA_MAC_DEF_GW)) + SET_FLAG(flags, ZEBRA_MACIP_TYPE_GW); + + return zebra_evpn_macip_send_msg_to_client(vni, macaddr, NULL, flags, + seq, ZEBRA_NEIGH_ACTIVE, es, + ZEBRA_MACIP_ADD); +} + +/* + * Inform BGP about local MAC deletion. + */ +int zebra_evpn_mac_send_del_to_client(vni_t vni, struct ethaddr *macaddr, + uint32_t flags, bool force) +{ + if (!force) { + if (CHECK_FLAG(flags, ZEBRA_MAC_LOCAL_INACTIVE) + && !CHECK_FLAG(flags, ZEBRA_MAC_ES_PEER_ACTIVE)) + /* the host was not advertised - nothing to delete */ + return 0; + } + + return zebra_evpn_macip_send_msg_to_client( + vni, macaddr, NULL, 0 /* flags */, 0 /* seq */, + ZEBRA_NEIGH_ACTIVE, NULL, ZEBRA_MACIP_DEL); +} + +/* + * wrapper to create a MAC hash table + */ +struct hash *zebra_mac_db_create(const char *desc) +{ + return hash_create(mac_hash_keymake, mac_cmp, desc); +} + +/* program sync mac flags in the dataplane */ +void zebra_evpn_sync_mac_dp_install(zebra_mac_t *mac, bool set_inactive, + bool force_clear_static, const char *caller) +{ + char macbuf[ETHER_ADDR_STRLEN]; + struct interface *ifp; + bool sticky; + bool set_static; + zebra_evpn_t *zevpn = mac->zevpn; + vlanid_t vid; + struct zebra_if *zif; + struct interface *br_ifp; + + /* get the access vlan from the vxlan_device */ + zebra_evpn_mac_get_access_info(mac, &ifp, &vid); + + if (!ifp) { + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug( + "%s: dp-install sync-mac vni %u mac %s es %s 0x%x %sskipped, no access-port", + caller, zevpn->vni, + prefix_mac2str(&mac->macaddr, macbuf, + sizeof(macbuf)), + mac->es ? mac->es->esi_str : "-", mac->flags, + set_inactive ? "inactive " : ""); + return; + } + + zif = ifp->info; + br_ifp = zif->brslave_info.br_if; + if (!br_ifp) { + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug( + "%s: dp-install sync-mac vni %u mac %s es %s 0x%x %sskipped, no br", + caller, zevpn->vni, + prefix_mac2str(&mac->macaddr, macbuf, + sizeof(macbuf)), + mac->es ? mac->es->esi_str : "-", mac->flags, + set_inactive ? "inactive " : ""); + return; + } + + sticky = !!CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY); + if (force_clear_static) + set_static = false; + else + set_static = zebra_evpn_mac_is_static(mac); + + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug( + "dp-install sync-mac vni %u mac %s es %s 0x%x %s%s", + zevpn->vni, + prefix_mac2str(&mac->macaddr, macbuf, sizeof(macbuf)), + mac->es ? mac->es->esi_str : "-", mac->flags, + set_static ? "static " : "", + set_inactive ? "inactive " : ""); + + dplane_local_mac_add(ifp, br_ifp, vid, &mac->macaddr, sticky, + set_static, set_inactive); +} + +void zebra_evpn_mac_send_add_del_to_client(zebra_mac_t *mac, bool old_bgp_ready, + bool new_bgp_ready) +{ + if (new_bgp_ready) + zebra_evpn_mac_send_add_to_client(mac->zevpn->vni, + &mac->macaddr, mac->flags, + mac->loc_seq, mac->es); + else if (old_bgp_ready) + zebra_evpn_mac_send_del_to_client(mac->zevpn->vni, + &mac->macaddr, mac->flags, + true /* force */); +} + +/* MAC hold timer is used to age out peer-active flag. + * + * During this wait time we expect the dataplane component or an + * external neighmgr daemon to probe existing hosts to independently + * establish their presence on the ES. + */ +static int zebra_evpn_mac_hold_exp_cb(struct thread *t) +{ + zebra_mac_t *mac; + bool old_bgp_ready; + bool new_bgp_ready; + bool old_static; + bool new_static; + char macbuf[ETHER_ADDR_STRLEN]; + + mac = THREAD_ARG(t); + /* the purpose of the hold timer is to age out the peer-active + * flag + */ + if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_ACTIVE)) + return 0; + + old_bgp_ready = zebra_evpn_mac_is_ready_for_bgp(mac->flags); + old_static = zebra_evpn_mac_is_static(mac); + UNSET_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_ACTIVE); + new_bgp_ready = zebra_evpn_mac_is_ready_for_bgp(mac->flags); + new_static = zebra_evpn_mac_is_static(mac); + + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug( + "sync-mac vni %u mac %s es %s 0x%x hold expired", + mac->zevpn->vni, + prefix_mac2str(&mac->macaddr, macbuf, sizeof(macbuf)), + mac->es ? mac->es->esi_str : "-", mac->flags); + + /* re-program the local mac in the dataplane if the mac is no + * longer static + */ + if (old_static != new_static) + zebra_evpn_sync_mac_dp_install(mac, false /* set_inactive */, + false /* force_clear_static */, + __func__); + + /* inform bgp if needed */ + if (old_bgp_ready != new_bgp_ready) + zebra_evpn_mac_send_add_del_to_client(mac, old_bgp_ready, + new_bgp_ready); + + return 0; +} + +static inline void zebra_evpn_mac_start_hold_timer(zebra_mac_t *mac) +{ + char macbuf[ETHER_ADDR_STRLEN]; + + if (mac->hold_timer) + return; + + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug( + "sync-mac vni %u mac %s es %s 0x%x hold started", + mac->zevpn->vni, + prefix_mac2str(&mac->macaddr, macbuf, sizeof(macbuf)), + mac->es ? mac->es->esi_str : "-", mac->flags); + thread_add_timer(zrouter.master, zebra_evpn_mac_hold_exp_cb, mac, + zmh_info->mac_hold_time, &mac->hold_timer); +} + +void zebra_evpn_mac_stop_hold_timer(zebra_mac_t *mac) +{ + char macbuf[ETHER_ADDR_STRLEN]; + + if (!mac->hold_timer) + return; + + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug( + "sync-mac vni %u mac %s es %s 0x%x hold stopped", + mac->zevpn->vni, + prefix_mac2str(&mac->macaddr, macbuf, sizeof(macbuf)), + mac->es ? mac->es->esi_str : "-", mac->flags); + THREAD_OFF(mac->hold_timer); +} + +void zebra_evpn_sync_mac_del(zebra_mac_t *mac) +{ + char macbuf[ETHER_ADDR_STRLEN]; + bool old_static; + bool new_static; + + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug( + "sync-mac del vni %u mac %s es %s seq %d f 0x%x", + mac->zevpn->vni, + prefix_mac2str(&mac->macaddr, macbuf, sizeof(macbuf)), + mac->es ? mac->es->esi_str : "-", mac->loc_seq, + mac->flags); + old_static = zebra_evpn_mac_is_static(mac); + UNSET_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_PROXY); + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_ACTIVE)) + zebra_evpn_mac_start_hold_timer(mac); + new_static = zebra_evpn_mac_is_static(mac); + + if (old_static != new_static) + /* program the local mac in the kernel */ + zebra_evpn_sync_mac_dp_install(mac, false /* set_inactive */, + false /* force_clear_static */, + __func__); +} + +static inline bool zebra_evpn_mac_is_bgp_seq_ok(zebra_evpn_t *zevpn, + zebra_mac_t *mac, uint32_t seq, + uint16_t ipa_len, + struct ipaddr *ipaddr) +{ + char macbuf[ETHER_ADDR_STRLEN]; + char ipbuf[INET6_ADDRSTRLEN]; + uint32_t tmp_seq; + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) + tmp_seq = mac->loc_seq; + else + tmp_seq = mac->rem_seq; + + if (seq < tmp_seq) { + /* if the mac was never advertised to bgp we must accept + * whatever sequence number bgp sends + * XXX - check with Vivek + */ + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL) + && !zebra_evpn_mac_is_ready_for_bgp(mac->flags)) { + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug( + "sync-macip accept vni %u mac %s%s%s lower seq %u f 0x%x", + zevpn->vni, + prefix_mac2str(&mac->macaddr, macbuf, + sizeof(macbuf)), + ipa_len ? " IP " : "", + ipa_len ? ipaddr2str(ipaddr, ipbuf, + sizeof(ipbuf)) + : "", + tmp_seq, mac->flags); + return true; + } + + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug( + "sync-macip ignore vni %u mac %s%s%s as existing has higher seq %u f 0x%x", + zevpn->vni, + prefix_mac2str(&mac->macaddr, macbuf, + sizeof(macbuf)), + ipa_len ? " IP " : "", + ipa_len ? ipaddr2str(ipaddr, ipbuf, + sizeof(ipbuf)) + : "", + tmp_seq, mac->flags); + return false; + } + + return true; +} + +zebra_mac_t * +zebra_evpn_proc_sync_mac_update(zebra_evpn_t *zevpn, struct ethaddr *macaddr, + uint16_t ipa_len, struct ipaddr *ipaddr, + uint8_t flags, uint32_t seq, esi_t *esi, + struct sync_mac_ip_ctx *ctx) +{ + zebra_mac_t *mac; + bool inform_bgp = false; + bool inform_dataplane = false; + bool seq_change = false; + bool es_change = false; + uint32_t tmp_seq; + char macbuf[ETHER_ADDR_STRLEN]; + char ipbuf[INET6_ADDRSTRLEN]; + bool old_local = false; + bool old_bgp_ready; + bool new_bgp_ready; + + mac = zebra_evpn_mac_lookup(zevpn, macaddr); + if (!mac) { + /* if it is a new local path we need to inform both + * the control protocol and the data-plane + */ + inform_bgp = true; + inform_dataplane = true; + ctx->mac_created = true; + ctx->mac_inactive = true; + + /* create the MAC and associate it with the dest ES */ + mac = zebra_evpn_mac_add(zevpn, macaddr); + zebra_evpn_es_mac_ref(mac, esi); + + /* local mac activated by an ES peer */ + SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL); + /* if mac-only route setup peer flags */ + if (!ipa_len) { + if (CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_PROXY_ADVERT)) + SET_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_PROXY); + else + SET_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_ACTIVE); + } + SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL_INACTIVE); + old_bgp_ready = false; + new_bgp_ready = zebra_evpn_mac_is_ready_for_bgp(mac->flags); + } else { + uint32_t old_flags; + uint32_t new_flags; + bool old_static; + bool new_static; + bool sticky; + bool remote_gw; + + old_flags = mac->flags; + sticky = !!CHECK_FLAG(old_flags, ZEBRA_MAC_STICKY); + remote_gw = !!CHECK_FLAG(old_flags, ZEBRA_MAC_REMOTE_DEF_GW); + if (sticky || remote_gw) { + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug( + "Ignore sync-macip vni %u mac %s%s%s%s%s", + zevpn->vni, + prefix_mac2str(macaddr, macbuf, + sizeof(macbuf)), + ipa_len ? " IP " : "", + ipa_len ? ipaddr2str(ipaddr, ipbuf, + sizeof(ipbuf)) + : "", + sticky ? " sticky" : "", + remote_gw ? " remote_gw" : ""); + ctx->ignore_macip = true; + return NULL; + } + if (!zebra_evpn_mac_is_bgp_seq_ok(zevpn, mac, seq, ipa_len, + ipaddr)) { + ctx->ignore_macip = true; + return NULL; + } + + old_local = !!CHECK_FLAG(old_flags, ZEBRA_MAC_LOCAL); + old_static = zebra_evpn_mac_is_static(mac); + + /* re-build the mac flags */ + new_flags = 0; + SET_FLAG(new_flags, ZEBRA_MAC_LOCAL); + /* retain old local activity flag */ + if (old_flags & ZEBRA_MAC_LOCAL) { + new_flags |= (old_flags & ZEBRA_MAC_LOCAL_INACTIVE); + } else { + new_flags |= ZEBRA_MAC_LOCAL_INACTIVE; + ctx->mac_inactive = true; + } + if (ipa_len) { + /* if mac-ip route do NOT update the peer flags + * i.e. retain only flags as is + */ + new_flags |= (old_flags & ZEBRA_MAC_ALL_PEER_FLAGS); + } else { + /* if mac-only route update peer flags */ + if (CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_PROXY_ADVERT)) { + SET_FLAG(new_flags, ZEBRA_MAC_ES_PEER_PROXY); + /* if the mac was peer-active previously we + * need to keep the flag and start the + * holdtimer on it. the peer-active flag is + * cleared on holdtimer expiry. + */ + if (CHECK_FLAG(old_flags, + ZEBRA_MAC_ES_PEER_ACTIVE)) { + SET_FLAG(new_flags, + ZEBRA_MAC_ES_PEER_ACTIVE); + zebra_evpn_mac_start_hold_timer(mac); + } + } else { + SET_FLAG(new_flags, ZEBRA_MAC_ES_PEER_ACTIVE); + /* stop hold timer if a peer has verified + * reachability + */ + zebra_evpn_mac_stop_hold_timer(mac); + } + } + mac->rem_seq = 0; + memset(&mac->fwd_info, 0, sizeof(mac->fwd_info)); + mac->flags = new_flags; + + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC && (old_flags != new_flags)) + zlog_debug( + "sync-mac vni %u mac %s old_f 0x%x new_f 0x%x", + zevpn->vni, + prefix_mac2str(macaddr, macbuf, sizeof(macbuf)), + old_flags, mac->flags); + + /* update es */ + es_change = zebra_evpn_es_mac_ref(mac, esi); + /* if mac dest change - inform both sides */ + if (es_change) { + inform_bgp = true; + inform_dataplane = true; + ctx->mac_inactive = true; + } + /* if peer-flag is being set notify dataplane that the + * entry must not be expired because of local inactivity + */ + new_static = zebra_evpn_mac_is_static(mac); + if (old_static != new_static) + inform_dataplane = true; + + old_bgp_ready = zebra_evpn_mac_is_ready_for_bgp(old_flags); + new_bgp_ready = zebra_evpn_mac_is_ready_for_bgp(mac->flags); + if (old_bgp_ready != new_bgp_ready) + inform_bgp = true; + } + + + /* update sequence number; if that results in a new local sequence + * inform bgp + */ + tmp_seq = MAX(mac->loc_seq, seq); + if (tmp_seq != mac->loc_seq) { + mac->loc_seq = tmp_seq; + seq_change = true; + inform_bgp = true; + } + + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug("sync-mac %s vni %u mac %s es %s seq %d f 0x%x%s%s", + ctx->mac_created ? "created" : "updated", zevpn->vni, + prefix_mac2str(macaddr, macbuf, sizeof(macbuf)), + mac->es ? mac->es->esi_str : "-", mac->loc_seq, + mac->flags, inform_bgp ? " inform_bgp" : "", + inform_dataplane ? " inform_dp" : ""); + + if (inform_bgp) + zebra_evpn_mac_send_add_del_to_client(mac, old_bgp_ready, + new_bgp_ready); + + /* neighs using the mac may need to be re-sent to + * bgp with updated info + */ + if (seq_change || es_change || !old_local) + zebra_evpn_process_neigh_on_local_mac_change( + zevpn, mac, seq_change, es_change); + + if (inform_dataplane) { + if (ipa_len) + /* if the mac is being created as a part of MAC-IP + * route wait for the neigh to be updated or + * created before programming the mac + */ + ctx->mac_dp_update_deferred = true; + else + /* program the local mac in the kernel. when the ES + * change we need to force the dataplane to reset + * the activity as we are yet to establish activity + * locally + */ + zebra_evpn_sync_mac_dp_install( + mac, ctx->mac_inactive, + false /* force_clear_static */, __func__); + } + + return mac; +} + +/* update local fowarding info. return true if a dest-ES change + * is detected + */ +static bool zebra_evpn_local_mac_update_fwd_info(zebra_mac_t *mac, + struct interface *ifp, + vlanid_t vid) +{ + struct zebra_if *zif = ifp->info; + bool es_change; + ns_id_t local_ns_id = NS_DEFAULT; + struct zebra_vrf *zvrf; + + zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id); + if (zvrf && zvrf->zns) + local_ns_id = zvrf->zns->ns_id; + + memset(&mac->fwd_info, 0, sizeof(mac->fwd_info)); + + es_change = zebra_evpn_es_mac_ref_entry(mac, zif->es_info.es); + + if (!mac->es) { + /* if es is set fwd_info is not-relevant/taped-out */ + mac->fwd_info.local.ifindex = ifp->ifindex; + mac->fwd_info.local.ns_id = local_ns_id; + mac->fwd_info.local.vid = vid; + } + + return es_change; +} + +/* Notify Local MACs to the clienti, skips GW MAC */ +static void zebra_evpn_send_mac_hash_entry_to_client(struct hash_bucket *bucket, + void *arg) +{ + struct mac_walk_ctx *wctx = arg; + zebra_mac_t *zmac = bucket->data; + + if (CHECK_FLAG(zmac->flags, ZEBRA_MAC_DEF_GW)) + return; + + if (CHECK_FLAG(zmac->flags, ZEBRA_MAC_LOCAL)) + zebra_evpn_mac_send_add_to_client(wctx->zevpn->vni, + &zmac->macaddr, zmac->flags, + zmac->loc_seq, zmac->es); +} + +/* Iterator to Notify Local MACs of a EVPN */ +void zebra_evpn_send_mac_list_to_client(zebra_evpn_t *zevpn) +{ + struct mac_walk_ctx wctx; + + if (!zevpn->mac_table) + return; + + memset(&wctx, 0, sizeof(struct mac_walk_ctx)); + wctx.zevpn = zevpn; + + hash_iterate(zevpn->mac_table, zebra_evpn_send_mac_hash_entry_to_client, + &wctx); +} + +void zebra_evpn_rem_mac_del(zebra_evpn_t *zevpn, zebra_mac_t *mac) +{ + zebra_evpn_process_neigh_on_remote_mac_del(zevpn, mac); + /* the remote sequence number in the auto mac entry + * needs to be reset to 0 as the mac entry may have + * been removed on all VTEPs (including + * the originating one) + */ + mac->rem_seq = 0; + + /* If all remote neighbors referencing a remote MAC + * go away, we need to uninstall the MAC. + */ + if (remote_neigh_count(mac) == 0) { + zebra_evpn_rem_mac_uninstall(zevpn, mac); + zebra_evpn_es_mac_deref_entry(mac); + UNSET_FLAG(mac->flags, ZEBRA_MAC_REMOTE); + } + + if (list_isempty(mac->neigh_list)) + zebra_evpn_mac_del(zevpn, mac); + else + SET_FLAG(mac->flags, ZEBRA_MAC_AUTO); +} + +/* Print Duplicate MAC */ +void zebra_evpn_print_dad_mac_hash(struct hash_bucket *bucket, void *ctxt) +{ + zebra_mac_t *mac; + + mac = (zebra_mac_t *)bucket->data; + if (!mac) + return; + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) + zebra_evpn_print_mac_hash(bucket, ctxt); +} + +/* Print Duplicate MAC in detail */ +void zebra_evpn_print_dad_mac_hash_detail(struct hash_bucket *bucket, + void *ctxt) +{ + zebra_mac_t *mac; + + mac = (zebra_mac_t *)bucket->data; + if (!mac) + return; + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) + zebra_evpn_print_mac_hash_detail(bucket, ctxt); +} + +int process_mac_remote_macip_add(zebra_evpn_t *zevpn, struct zebra_vrf *zvrf, + struct ethaddr *macaddr, uint16_t ipa_len, + struct ipaddr *ipaddr, zebra_mac_t **macp, + struct in_addr vtep_ip, uint8_t flags, + uint32_t seq, esi_t *esi) +{ + char buf[ETHER_ADDR_STRLEN]; + char buf1[INET6_ADDRSTRLEN]; + uint32_t tmp_seq; + bool sticky; + bool remote_gw; + int update_mac = 0; + bool do_dad = false; + bool is_dup_detect = false; + esi_t *old_esi; + bool old_static = false; + zebra_mac_t *mac; + + sticky = !!CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY); + remote_gw = !!CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_GW); + + mac = zebra_evpn_mac_lookup(zevpn, macaddr); + + /* Ignore if the mac is already present as a gateway mac */ + if (mac && CHECK_FLAG(mac->flags, ZEBRA_MAC_DEF_GW) + && CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_GW)) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Ignore remote MACIP ADD VNI %u MAC %s%s%s as MAC is already configured as gateway MAC", + zevpn->vni, + prefix_mac2str(macaddr, buf, sizeof(buf)), + ipa_len ? " IP " : "", + ipa_len ? ipaddr2str(ipaddr, buf1, sizeof(buf1)) + : ""); + return -1; + } + + old_esi = (mac && mac->es) ? &mac->es->esi : zero_esi; + + /* check if the remote MAC is unknown or has a change. + * If so, that needs to be updated first. Note that client could + * install MAC and MACIP separately or just install the latter. + */ + if (!mac || !CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE) + || sticky != !!CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY) + || remote_gw != !!CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE_DEF_GW) + || !IPV4_ADDR_SAME(&mac->fwd_info.r_vtep_ip, &vtep_ip) + || memcmp(old_esi, esi, sizeof(esi_t)) || seq != mac->rem_seq) + update_mac = 1; + + if (update_mac) { + if (!mac) { + mac = zebra_evpn_mac_add(zevpn, macaddr); + if (!mac) { + zlog_warn( + "Failed to add MAC %s VNI %u Remote VTEP %s", + prefix_mac2str(macaddr, buf, + sizeof(buf)), + zevpn->vni, inet_ntoa(vtep_ip)); + return -1; + } + + zebra_evpn_es_mac_ref(mac, esi); + + /* Is this MAC created for a MACIP? */ + if (ipa_len) + SET_FLAG(mac->flags, ZEBRA_MAC_AUTO); + } else { + zebra_evpn_es_mac_ref(mac, esi); + + /* When host moves but changes its (MAC,IP) + * binding, BGP may install a MACIP entry that + * corresponds to "older" location of the host + * in transient situations (because {IP1,M1} + * is a different route from {IP1,M2}). Check + * the sequence number and ignore this update + * if appropriate. + */ + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) + tmp_seq = mac->loc_seq; + else + tmp_seq = mac->rem_seq; + + if (seq < tmp_seq) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Ignore remote MACIP ADD VNI %u MAC %s%s%s as existing MAC has higher seq %u flags 0x%x", + zevpn->vni, + prefix_mac2str(macaddr, buf, + sizeof(buf)), + ipa_len ? " IP " : "", + ipa_len ? ipaddr2str( + ipaddr, buf1, + sizeof(buf1)) + : "", + tmp_seq, mac->flags); + return -1; + } + } + + /* Check MAC's curent state is local (this is the case + * where MAC has moved from L->R) and check previous + * detection started via local learning. + * RFC-7432: A PE/VTEP that detects a MAC mobility + * event via local learning starts an M-second timer. + * + * VTEP-IP or seq. change alone is not considered + * for dup. detection. + * + * MAC is already marked duplicate set dad, then + * is_dup_detect will be set to not install the entry. + */ + if ((!CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE) + && mac->dad_count) + || CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) + do_dad = true; + + /* Remove local MAC from BGP. */ + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) { + /* force drop the sync flags */ + old_static = zebra_evpn_mac_is_static(mac); + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug( + "sync-mac->remote vni %u mac %s es %s seq %d f 0x%x", + zevpn->vni, + prefix_mac2str(macaddr, buf, + sizeof(buf)), + mac->es ? mac->es->esi_str : "-", + mac->loc_seq, mac->flags); + zebra_evpn_mac_clear_sync_info(mac); + zebra_evpn_mac_send_del_to_client(zevpn->vni, macaddr, + mac->flags, + false /* force */); + } + + /* Set "auto" and "remote" forwarding info. */ + UNSET_FLAG(mac->flags, ZEBRA_MAC_ALL_LOCAL_FLAGS); + memset(&mac->fwd_info, 0, sizeof(mac->fwd_info)); + SET_FLAG(mac->flags, ZEBRA_MAC_REMOTE); + mac->fwd_info.r_vtep_ip = vtep_ip; + + if (sticky) + SET_FLAG(mac->flags, ZEBRA_MAC_STICKY); + else + UNSET_FLAG(mac->flags, ZEBRA_MAC_STICKY); + + if (remote_gw) + SET_FLAG(mac->flags, ZEBRA_MAC_REMOTE_DEF_GW); + else + UNSET_FLAG(mac->flags, ZEBRA_MAC_REMOTE_DEF_GW); + + zebra_evpn_dup_addr_detect_for_mac( + zvrf, mac, mac->fwd_info.r_vtep_ip, do_dad, + &is_dup_detect, false); + + if (!is_dup_detect) { + zebra_evpn_process_neigh_on_remote_mac_add(zevpn, mac); + /* Install the entry. */ + zebra_evpn_rem_mac_install(zevpn, mac, old_static); + } + } + + /* Update seq number. */ + mac->rem_seq = seq; + + /* If there is no IP, return after clearing AUTO flag of MAC. */ + if (!ipa_len) { + UNSET_FLAG(mac->flags, ZEBRA_MAC_AUTO); + return -1; + } + *macp = mac; + return 0; +} + +int zebra_evpn_add_update_local_mac(struct zebra_vrf *zvrf, zebra_evpn_t *zevpn, + struct interface *ifp, + struct ethaddr *macaddr, vlanid_t vid, + bool sticky, bool local_inactive, + bool dp_static) +{ + zebra_mac_t *mac; + char buf[ETHER_ADDR_STRLEN]; + bool mac_sticky = false; + bool inform_client = false; + bool upd_neigh = false; + bool is_dup_detect = false; + struct in_addr vtep_ip = {.s_addr = 0}; + bool es_change = false; + bool new_bgp_ready; + /* assume inactive if not present or if not local */ + bool old_local_inactive = true; + bool old_bgp_ready = false; + bool inform_dataplane = false; + bool new_static = false; + + assert(ifp); + /* Check if we need to create or update or it is a NO-OP. */ + mac = zebra_evpn_mac_lookup(zevpn, macaddr); + if (!mac) { + if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug( + "ADD %sMAC %s intf %s(%u) VID %u -> VNI %u%s", + sticky ? "sticky " : "", + prefix_mac2str(macaddr, buf, sizeof(buf)), + ifp->name, ifp->ifindex, vid, zevpn->vni, + local_inactive ? " local-inactive" : ""); + + mac = zebra_evpn_mac_add(zevpn, macaddr); + if (!mac) { + flog_err( + EC_ZEBRA_MAC_ADD_FAILED, + "Failed to add MAC %s intf %s(%u) VID %u VNI %u", + prefix_mac2str(macaddr, buf, sizeof(buf)), + ifp->name, ifp->ifindex, vid, zevpn->vni); + return -1; + } + SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL); + es_change = zebra_evpn_local_mac_update_fwd_info(mac, ifp, vid); + if (sticky) + SET_FLAG(mac->flags, ZEBRA_MAC_STICKY); + inform_client = true; + } else { + if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug( + "UPD %sMAC %s intf %s(%u) VID %u -> VNI %u %scurFlags 0x%x", + sticky ? "sticky " : "", + prefix_mac2str(macaddr, buf, sizeof(buf)), + ifp->name, ifp->ifindex, vid, zevpn->vni, + local_inactive ? "local-inactive " : "", + mac->flags); + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) { + struct interface *old_ifp; + vlanid_t old_vid; + bool old_static; + + zebra_evpn_mac_get_access_info(mac, &old_ifp, &old_vid); + old_bgp_ready = + zebra_evpn_mac_is_ready_for_bgp(mac->flags); + old_local_inactive = + !!(mac->flags & ZEBRA_MAC_LOCAL_INACTIVE); + old_static = zebra_evpn_mac_is_static(mac); + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY)) + mac_sticky = true; + + /* + * Update any changes and if changes are relevant to + * BGP, note it. + */ + if (mac_sticky == sticky && old_ifp == ifp + && old_vid == vid + && old_local_inactive == local_inactive + && dp_static == old_static) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + " Add/Update %sMAC %s intf %s(%u) VID %u -> VNI %u%s, " + "entry exists and has not changed ", + sticky ? "sticky " : "", + prefix_mac2str(macaddr, buf, + sizeof(buf)), + ifp->name, ifp->ifindex, vid, + zevpn->vni, + local_inactive + ? " local_inactive" + : ""); + return 0; + } + if (mac_sticky != sticky) { + if (sticky) + SET_FLAG(mac->flags, ZEBRA_MAC_STICKY); + else + UNSET_FLAG(mac->flags, + ZEBRA_MAC_STICKY); + inform_client = true; + } + + es_change = zebra_evpn_local_mac_update_fwd_info( + mac, ifp, vid); + /* If an es_change is detected we need to advertise + * the route with a sequence that is one + * greater. This is need to indicate a mac-move + * to the ES peers + */ + if (es_change) { + mac->loc_seq = mac->loc_seq + 1; + /* force drop the peer/sync info as it is + * simply no longer relevant + */ + if (CHECK_FLAG(mac->flags, + ZEBRA_MAC_ALL_PEER_FLAGS)) { + zebra_evpn_mac_clear_sync_info(mac); + new_static = + zebra_evpn_mac_is_static(mac); + /* if we clear peer-flags we + * also need to notify the dataplane + * to drop the static flag + */ + if (old_static != new_static) + inform_dataplane = true; + } + } + } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE) + || CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO)) { + bool do_dad = false; + + /* + * MAC has either moved or was "internally" created due + * to a neighbor learn and is now actually learnt. If + * it was learnt as a remote sticky MAC, this is an + * operator error. + */ + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY)) { + flog_warn( + EC_ZEBRA_STICKY_MAC_ALREADY_LEARNT, + "MAC %s already learnt as remote sticky MAC behind VTEP %s VNI %u", + prefix_mac2str(macaddr, buf, + sizeof(buf)), + inet_ntoa(mac->fwd_info.r_vtep_ip), + zevpn->vni); + return 0; + } + + /* If an actual move, compute MAC's seq number */ + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { + mac->loc_seq = + MAX(mac->rem_seq + 1, mac->loc_seq); + vtep_ip = mac->fwd_info.r_vtep_ip; + /* Trigger DAD for remote MAC */ + do_dad = true; + } + + UNSET_FLAG(mac->flags, ZEBRA_MAC_REMOTE); + UNSET_FLAG(mac->flags, ZEBRA_MAC_AUTO); + SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL); + es_change = zebra_evpn_local_mac_update_fwd_info( + mac, ifp, vid); + if (sticky) + SET_FLAG(mac->flags, ZEBRA_MAC_STICKY); + else + UNSET_FLAG(mac->flags, ZEBRA_MAC_STICKY); + /* + * We have to inform BGP of this MAC as well as process + * all neighbors. + */ + inform_client = true; + upd_neigh = true; + + zebra_evpn_dup_addr_detect_for_mac( + zvrf, mac, vtep_ip, do_dad, &is_dup_detect, + true); + if (is_dup_detect) { + inform_client = false; + upd_neigh = false; + es_change = false; + } + } + } + + /* if the dataplane thinks the entry is sync but it is + * not sync in zebra we need to re-install to fixup + */ + if (dp_static) { + new_static = zebra_evpn_mac_is_static(mac); + if (!new_static) + inform_dataplane = true; + } + + if (local_inactive) + SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL_INACTIVE); + else + UNSET_FLAG(mac->flags, ZEBRA_MAC_LOCAL_INACTIVE); + + new_bgp_ready = zebra_evpn_mac_is_ready_for_bgp(mac->flags); + /* if local-activity has changed we need update bgp + * even if bgp already knows about the mac + */ + if ((old_local_inactive != local_inactive) + || (new_bgp_ready != old_bgp_ready)) { + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug( + "local mac vni %u mac %s es %s seq %d f 0x%x%s", + zevpn->vni, + prefix_mac2str(macaddr, buf, sizeof(buf)), + mac->es ? mac->es->esi_str : "", mac->loc_seq, + mac->flags, + local_inactive ? " local-inactive" : ""); + if (!is_dup_detect) + inform_client = true; + } + + if (es_change) { + inform_client = true; + upd_neigh = true; + } + + /* Inform dataplane if required. */ + if (inform_dataplane) + zebra_evpn_sync_mac_dp_install(mac, false /* set_inactive */, + false /* force_clear_static */, + __func__); + + /* Inform BGP if required. */ + if (inform_client) + zebra_evpn_mac_send_add_del_to_client(mac, old_bgp_ready, + new_bgp_ready); + + /* Process all neighbors associated with this MAC, if required. */ + if (upd_neigh) + zebra_evpn_process_neigh_on_local_mac_change(zevpn, mac, 0, + es_change); + + return 0; +} + +int zebra_evpn_del_local_mac(zebra_evpn_t *zevpn, struct ethaddr *macaddr, + struct interface *ifp) +{ + zebra_mac_t *mac; + char buf[ETHER_ADDR_STRLEN]; + bool old_bgp_ready; + bool new_bgp_ready; + /* If entry doesn't exist, nothing to do. */ + mac = zebra_evpn_mac_lookup(zevpn, macaddr); + if (!mac) + return 0; + + /* Is it a local entry? */ + if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) + return 0; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "DEL MAC %s intf %s(%u) VID %u -> VNI %u seq %u flags 0x%x nbr count %u", + prefix_mac2str(macaddr, buf, sizeof(buf)), ifp->name, + ifp->ifindex, mac->fwd_info.local.vid, zevpn->vni, + mac->loc_seq, mac->flags, listcount(mac->neigh_list)); + + old_bgp_ready = zebra_evpn_mac_is_ready_for_bgp(mac->flags); + if (zebra_evpn_mac_is_static(mac)) { + /* this is a synced entry and can only be removed when the + * es-peers stop advertising it. + */ + memset(&mac->fwd_info, 0, sizeof(mac->fwd_info)); + + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug( + "re-add sync-mac vni %u mac %s es %s seq %d f 0x%x", + zevpn->vni, + prefix_mac2str(macaddr, buf, sizeof(buf)), + mac->es ? mac->es->esi_str : "-", mac->loc_seq, + mac->flags); + + /* inform-bgp about change in local-activity if any */ + if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL_INACTIVE)) { + SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL_INACTIVE); + new_bgp_ready = + zebra_evpn_mac_is_ready_for_bgp(mac->flags); + zebra_evpn_mac_send_add_del_to_client( + mac, old_bgp_ready, new_bgp_ready); + } + + /* re-install the entry in the kernel */ + zebra_evpn_sync_mac_dp_install(mac, false /* set_inactive */, + false /* force_clear_static */, + __func__); + + return 0; + } + + /* Update all the neigh entries associated with this mac */ + zebra_evpn_process_neigh_on_local_mac_del(zevpn, mac); + + /* Remove MAC from BGP. */ + zebra_evpn_mac_send_del_to_client(zevpn->vni, macaddr, mac->flags, + false /* force */); + + zebra_evpn_es_mac_deref_entry(mac); + + /* + * If there are no neigh associated with the mac delete the mac + * else mark it as AUTO for forward reference + */ + if (!listcount(mac->neigh_list)) { + zebra_evpn_mac_del(zevpn, mac); + } else { + UNSET_FLAG(mac->flags, ZEBRA_MAC_ALL_LOCAL_FLAGS); + UNSET_FLAG(mac->flags, ZEBRA_MAC_STICKY); + SET_FLAG(mac->flags, ZEBRA_MAC_AUTO); + } + + return 0; +} + +int zebra_evpn_mac_gw_macip_add(struct interface *ifp, zebra_evpn_t *zevpn, + struct ipaddr *ip, zebra_mac_t **macp, + struct ethaddr *macaddr, vlanid_t vlan_id) +{ + char buf[ETHER_ADDR_STRLEN]; + zebra_mac_t *mac; + ns_id_t local_ns_id = NS_DEFAULT; + struct zebra_vrf *zvrf; + + zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id); + if (zvrf && zvrf->zns) + local_ns_id = zvrf->zns->ns_id; + + mac = zebra_evpn_mac_lookup(zevpn, macaddr); + if (!mac) { + mac = zebra_evpn_mac_add(zevpn, macaddr); + if (!mac) { + flog_err(EC_ZEBRA_MAC_ADD_FAILED, + "Failed to add MAC %s intf %s(%u) VID %u", + prefix_mac2str(macaddr, buf, sizeof(buf)), + ifp->name, ifp->ifindex, vlan_id); + return -1; + } + } + + /* Set "local" forwarding info. */ + SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL); + SET_FLAG(mac->flags, ZEBRA_MAC_AUTO); + SET_FLAG(mac->flags, ZEBRA_MAC_DEF_GW); + memset(&mac->fwd_info, 0, sizeof(mac->fwd_info)); + mac->fwd_info.local.ifindex = ifp->ifindex; + mac->fwd_info.local.ns_id = local_ns_id; + mac->fwd_info.local.vid = vlan_id; + + *macp = mac; + + return 0; +} diff --git a/zebra/zebra_evpn_mac.h b/zebra/zebra_evpn_mac.h new file mode 100644 index 0000000000..f9ca81445f --- /dev/null +++ b/zebra/zebra_evpn_mac.h @@ -0,0 +1,264 @@ +/* + * Zebra EVPN MAC Data structures and definitions + * These are "internal" to this function. + * Copyright (C) 2016, 2017 Cumulus Networks, Inc. + * Copyright (C) 2020 Volta Networks. + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef _ZEBRA_EVPN_MAC_H +#define _ZEBRA_EVPN_MAC_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct zebra_mac_t_ zebra_mac_t; + +struct host_rb_entry { + RB_ENTRY(host_rb_entry) hl_entry; + + struct prefix p; +}; + +RB_HEAD(host_rb_tree_entry, host_rb_entry); +RB_PROTOTYPE(host_rb_tree_entry, host_rb_entry, hl_entry, + host_rb_entry_compare); +/* + * MAC hash table. + * + * This table contains the MAC addresses pertaining to this VNI. + * This includes local MACs learnt on an attached VLAN that maps + * to this VNI as well as remote MACs learnt and installed by BGP. + * Local MACs will be known either on a VLAN sub-interface or + * on (port, VLAN); however, it is sufficient for zebra to maintain + * against the VNI i.e., it does not need to retain the local "port" + * information. The correct VNI will be obtained as zebra maintains + * the mapping (of VLAN to VNI). + */ +struct zebra_mac_t_ { + /* MAC address. */ + struct ethaddr macaddr; + + uint32_t flags; +#define ZEBRA_MAC_LOCAL 0x01 +#define ZEBRA_MAC_REMOTE 0x02 +#define ZEBRA_MAC_AUTO 0x04 /* Auto created for neighbor. */ +#define ZEBRA_MAC_STICKY 0x08 /* Static MAC */ +#define ZEBRA_MAC_REMOTE_RMAC 0x10 /* remote router mac */ +#define ZEBRA_MAC_DEF_GW 0x20 +/* remote VTEP advertised MAC as default GW */ +#define ZEBRA_MAC_REMOTE_DEF_GW 0x40 +#define ZEBRA_MAC_DUPLICATE 0x80 +#define ZEBRA_MAC_FPM_SENT 0x100 /* whether or not this entry was sent. */ +/* MAC is locally active on an ethernet segment peer */ +#define ZEBRA_MAC_ES_PEER_ACTIVE 0x200 +/* MAC has been proxy-advertised by peers. This means we need to + * keep the entry for forwarding but cannot advertise it + */ +#define ZEBRA_MAC_ES_PEER_PROXY 0x400 +/* We have not been able to independently establish that the host is + * local connected but one or more ES peers claims it is. + * We will maintain the entry for forwarding purposes and continue + * to advertise it as locally attached but with a "proxy" flag + */ +#define ZEBRA_MAC_LOCAL_INACTIVE 0x800 + +#define ZEBRA_MAC_ALL_LOCAL_FLAGS (ZEBRA_MAC_LOCAL | ZEBRA_MAC_LOCAL_INACTIVE) +#define ZEBRA_MAC_ALL_PEER_FLAGS \ + (ZEBRA_MAC_ES_PEER_PROXY | ZEBRA_MAC_ES_PEER_ACTIVE) + + /* back pointer to zevpn */ + zebra_evpn_t *zevpn; + + /* Local or remote info. */ + union { + struct { + ifindex_t ifindex; + ns_id_t ns_id; + vlanid_t vid; + } local; + + struct in_addr r_vtep_ip; + } fwd_info; + + /* Local or remote ES */ + struct zebra_evpn_es *es; + /* memory used to link the mac to the es */ + struct listnode es_listnode; + + /* Mobility sequence numbers associated with this entry. */ + uint32_t rem_seq; + uint32_t loc_seq; + + /* List of neigh associated with this mac */ + struct list *neigh_list; + + /* list of hosts pointing to this remote RMAC */ + struct host_rb_tree_entry host_rb; + + /* Duplicate mac detection */ + uint32_t dad_count; + + struct thread *dad_mac_auto_recovery_timer; + + struct timeval detect_start_time; + + time_t dad_dup_detect_time; + + /* used for ageing out the PEER_ACTIVE flag */ + struct thread *hold_timer; + + /* number of neigh entries (using this mac) that have + * ZEBRA_MAC_ES_PEER_ACTIVE or ZEBRA_NEIGH_ES_PEER_PROXY + */ + uint32_t sync_neigh_cnt; +}; + +/* + * Context for MAC hash walk - used by callbacks. + */ +struct mac_walk_ctx { + zebra_evpn_t *zevpn; /* EVPN hash */ + struct zebra_vrf *zvrf; /* VRF - for client notification. */ + int uninstall; /* uninstall from kernel? */ + int upd_client; /* uninstall from client? */ + + uint32_t flags; +#define DEL_LOCAL_MAC 0x1 +#define DEL_REMOTE_MAC 0x2 +#define DEL_ALL_MAC (DEL_LOCAL_MAC | DEL_REMOTE_MAC) +#define DEL_REMOTE_MAC_FROM_VTEP 0x4 +#define SHOW_REMOTE_MAC_FROM_VTEP 0x8 + + struct in_addr r_vtep_ip; /* To walk MACs from specific VTEP */ + + struct vty *vty; /* Used by VTY handlers */ + uint32_t count; /* Used by VTY handlers */ + struct json_object *json; /* Used for JSON Output */ + bool print_dup; /* Used to print dup addr list */ +}; + +struct rmac_walk_ctx { + struct vty *vty; + struct json_object *json; +}; + +/* temporary datastruct to pass info between the mac-update and + * neigh-update while handling mac-ip routes + */ +struct sync_mac_ip_ctx { + bool ignore_macip; + bool mac_created; + bool mac_inactive; + bool mac_dp_update_deferred; + zebra_mac_t *mac; +}; + +/**************************** SYNC MAC handling *****************************/ +/**************************** SYNC MAC handling *****************************/ +/* if the mac has been added of a mac-route from the peer + * or if it is being referenced by a neigh added by the + * peer we cannot let it age out i.e. we set the static bit + * in the dataplane + */ +static inline bool zebra_evpn_mac_is_static(zebra_mac_t *mac) +{ + return ((mac->flags & ZEBRA_MAC_ALL_PEER_FLAGS) || mac->sync_neigh_cnt); +} + +/* mac needs to be locally active or active on an ES peer */ +static inline bool zebra_evpn_mac_is_ready_for_bgp(uint32_t flags) +{ + return (flags & ZEBRA_MAC_LOCAL) + && (!(flags & ZEBRA_MAC_LOCAL_INACTIVE) + || (flags & ZEBRA_MAC_ES_PEER_ACTIVE)); +} + +void zebra_evpn_mac_stop_hold_timer(zebra_mac_t *mac); + +static inline void zebra_evpn_mac_clear_sync_info(zebra_mac_t *mac) +{ + UNSET_FLAG(mac->flags, ZEBRA_MAC_ALL_PEER_FLAGS); + zebra_evpn_mac_stop_hold_timer(mac); +} + +struct hash *zebra_mac_db_create(const char *desc); +uint32_t num_valid_macs(zebra_evpn_t *zevi); +uint32_t num_dup_detected_macs(zebra_evpn_t *zevi); +int zebra_evpn_rem_mac_uninstall(zebra_evpn_t *zevi, zebra_mac_t *mac); +int zebra_evpn_rem_mac_install(zebra_evpn_t *zevi, zebra_mac_t *mac, + bool was_static); +void zebra_evpn_deref_ip2mac(zebra_evpn_t *zevi, zebra_mac_t *mac); +zebra_mac_t *zebra_evpn_mac_lookup(zebra_evpn_t *zevi, struct ethaddr *mac); +zebra_mac_t *zebra_evpn_mac_add(zebra_evpn_t *zevi, struct ethaddr *macaddr); +int zebra_evpn_mac_del(zebra_evpn_t *zevi, zebra_mac_t *mac); +int zebra_evpn_macip_send_msg_to_client(uint32_t id, struct ethaddr *macaddr, + struct ipaddr *ip, uint8_t flags, + uint32_t seq, int state, + struct zebra_evpn_es *es, uint16_t cmd); +void zebra_evpn_print_mac(zebra_mac_t *mac, void *ctxt, json_object *json); +void zebra_evpn_print_mac_hash(struct hash_bucket *bucket, void *ctxt); +void zebra_evpn_print_mac_hash_detail(struct hash_bucket *bucket, void *ctxt); +void zebra_evpn_sync_mac_dp_install(zebra_mac_t *mac, bool set_inactive, + bool force_clear_static, + const char *caller); +void zebra_evpn_mac_send_add_del_to_client(zebra_mac_t *mac, bool old_bgp_ready, + bool new_bgp_ready); + +void zebra_evpn_mac_del_all(zebra_evpn_t *zevi, int uninstall, int upd_client, + uint32_t flags); +int zebra_evpn_mac_send_add_to_client(vni_t vni, struct ethaddr *macaddr, + uint32_t mac_flags, uint32_t seq, + struct zebra_evpn_es *es); +int zebra_evpn_mac_send_del_to_client(vni_t vni, struct ethaddr *macaddr, + uint32_t flags, bool force); +void zebra_evpn_send_mac_list_to_client(zebra_evpn_t *zevi); +zebra_mac_t * +zebra_evpn_proc_sync_mac_update(zebra_evpn_t *zevi, struct ethaddr *macaddr, + uint16_t ipa_len, struct ipaddr *ipaddr, + uint8_t flags, uint32_t seq, esi_t *esi, + struct sync_mac_ip_ctx *ctx); +void zebra_evpn_sync_mac_del(zebra_mac_t *mac); +void zebra_evpn_rem_mac_del(zebra_evpn_t *zevi, zebra_mac_t *mac); +void zebra_evpn_print_dad_mac_hash(struct hash_bucket *bucket, void *ctxt); +void zebra_evpn_print_dad_mac_hash_detail(struct hash_bucket *bucket, + void *ctxt); +int process_mac_remote_macip_add(zebra_evpn_t *zevpn, struct zebra_vrf *zvrf, + struct ethaddr *macaddr, uint16_t ipa_len, + struct ipaddr *ipaddr, zebra_mac_t **macp, + struct in_addr vtep_ip, uint8_t flags, + uint32_t seq, esi_t *esi); + +int zebra_evpn_add_update_local_mac(struct zebra_vrf *zvrf, zebra_evpn_t *zevpn, + struct interface *ifp, + struct ethaddr *macaddr, vlanid_t vid, + bool sticky, bool local_inactive, + bool dp_static); +int zebra_evpn_del_local_mac(zebra_evpn_t *zevpn, struct ethaddr *macaddr, + struct interface *ifp); +int zebra_evpn_mac_gw_macip_add(struct interface *ifp, zebra_evpn_t *zevpn, + struct ipaddr *ip, zebra_mac_t **macp, + struct ethaddr *macaddr, vlanid_t vlan_id); + +#ifdef __cplusplus +} +#endif + +#endif /*_ZEBRA_EVPN_MAC_H */ diff --git a/zebra/zebra_evpn_mh.c b/zebra/zebra_evpn_mh.c new file mode 100644 index 0000000000..2206ce0832 --- /dev/null +++ b/zebra/zebra_evpn_mh.c @@ -0,0 +1,2164 @@ +/* + * Zebra EVPN multihoming code + * + * Copyright (C) 2019 Cumulus Networks, Inc. + * Anuradha Karuppiah + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include + +#include "command.h" +#include "hash.h" +#include "if.h" +#include "jhash.h" +#include "linklist.h" +#include "log.h" +#include "memory.h" +#include "prefix.h" +#include "stream.h" +#include "table.h" +#include "vlan.h" +#include "vxlan.h" + +#include "zebra/zebra_router.h" +#include "zebra/debug.h" +#include "zebra/interface.h" +#include "zebra/rib.h" +#include "zebra/rt.h" +#include "zebra/rt_netlink.h" +#include "zebra/zebra_errors.h" +#include "zebra/zebra_l2.h" +#include "zebra/zebra_memory.h" +#include "zebra/zebra_ns.h" +#include "zebra/zebra_vrf.h" +#include "zebra/zebra_vxlan.h" +#include "zebra/zebra_evpn.h" +#include "zebra/zebra_evpn_mac.h" +#include "zebra/zebra_vxlan_private.h" +#include "zebra/zebra_router.h" +#include "zebra/zebra_evpn_mh.h" +#include "zebra/zebra_nhg.h" + +DEFINE_MTYPE_STATIC(ZEBRA, ZACC_BD, "Access Broadcast Domain"); +DEFINE_MTYPE_STATIC(ZEBRA, ZES, "Ethernet Segment"); +DEFINE_MTYPE_STATIC(ZEBRA, ZES_EVI, "ES info per-EVI"); +DEFINE_MTYPE_STATIC(ZEBRA, ZMH_INFO, "MH global info"); +DEFINE_MTYPE_STATIC(ZEBRA, ZES_VTEP, "VTEP attached to the ES"); + +static void zebra_evpn_es_get_one_base_evpn(void); +static int zebra_evpn_es_evi_send_to_client(struct zebra_evpn_es *es, + zebra_evpn_t *zevpn, bool add); +static void zebra_evpn_local_es_del(struct zebra_evpn_es **esp); +static int zebra_evpn_local_es_update(struct zebra_if *zif, uint32_t lid, + struct ethaddr *sysmac); + +esi_t zero_esi_buf, *zero_esi = &zero_esi_buf; + +/*****************************************************************************/ +/* Ethernet Segment to EVI association - + * 1. The ES-EVI entry is maintained as a RB tree per L2-VNI + * (zebra_evpn_t.es_evi_rb_tree). + * 2. Each local ES-EVI entry is sent to BGP which advertises it as an + * EAD-EVI (Type-1 EVPN) route + * 3. Local ES-EVI setup is re-evaluated on the following triggers - + * a. When an ESI is set or cleared on an access port. + * b. When an access port associated with an ESI is deleted. + * c. When VLAN member ship changes on an access port. + * d. When a VXLAN_IF is set or cleared on an access broadcast domain. + * e. When a L2-VNI is added or deleted for a VxLAN_IF. + * 4. Currently zebra doesn't remote ES-EVIs. Those are managed and maintained + * entirely in BGP which consolidates them into a remote ES. The remote ES + * is then sent to zebra which allocates a NHG for it. + */ + +/* compare ES-IDs for the ES-EVI RB tree maintained per-EVPN */ +static int zebra_es_evi_rb_cmp(const struct zebra_evpn_es_evi *es_evi1, + const struct zebra_evpn_es_evi *es_evi2) +{ + return memcmp(&es_evi1->es->esi, &es_evi2->es->esi, ESI_BYTES); +} +RB_GENERATE(zebra_es_evi_rb_head, zebra_evpn_es_evi, + rb_node, zebra_es_evi_rb_cmp); + +/* allocate a new ES-EVI and insert it into the per-L2-VNI and per-ES + * tables. + */ +static struct zebra_evpn_es_evi *zebra_evpn_es_evi_new(struct zebra_evpn_es *es, + zebra_evpn_t *zevpn) +{ + struct zebra_evpn_es_evi *es_evi; + + es_evi = XCALLOC(MTYPE_ZES_EVI, sizeof(struct zebra_evpn_es_evi)); + + es_evi->es = es; + es_evi->zevpn = zevpn; + + /* insert into the EVPN-ESI rb tree */ + if (RB_INSERT(zebra_es_evi_rb_head, &zevpn->es_evi_rb_tree, es_evi)) { + XFREE(MTYPE_ZES_EVI, es_evi); + return NULL; + } + + /* add to the ES's VNI list */ + listnode_init(&es_evi->es_listnode, es_evi); + listnode_add(es->es_evi_list, &es_evi->es_listnode); + + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("es %s evi %d new", + es_evi->es->esi_str, es_evi->zevpn->vni); + + return es_evi; +} + +/* returns TRUE if the EVPN is ready to be sent to BGP */ +static inline bool zebra_evpn_send_to_client_ok(zebra_evpn_t *zevpn) +{ + return !!(zevpn->flags & ZEVPN_READY_FOR_BGP); +} + +/* Evaluate if the es_evi is ready to be sent BGP - + * 1. If it is ready an add is sent to BGP + * 2. If it is not ready a del is sent (if the ES had been previously added + * to BGP). + */ +static void zebra_evpn_es_evi_re_eval_send_to_client( + struct zebra_evpn_es_evi *es_evi) +{ + bool old_ready; + bool new_ready; + + old_ready = !!(es_evi->flags & ZEBRA_EVPNES_EVI_READY_FOR_BGP); + + /* ES and L2-VNI have to be individually ready for BGP */ + if ((es_evi->flags & ZEBRA_EVPNES_EVI_LOCAL) && + (es_evi->es->flags & ZEBRA_EVPNES_READY_FOR_BGP) && + zebra_evpn_send_to_client_ok(es_evi->zevpn)) + es_evi->flags |= ZEBRA_EVPNES_EVI_READY_FOR_BGP; + else + es_evi->flags &= ~ZEBRA_EVPNES_EVI_READY_FOR_BGP; + + new_ready = !!(es_evi->flags & ZEBRA_EVPNES_EVI_READY_FOR_BGP); + + if (old_ready == new_ready) + return; + + if (new_ready) + zebra_evpn_es_evi_send_to_client(es_evi->es, es_evi->zevpn, + true /* add */); + else + zebra_evpn_es_evi_send_to_client(es_evi->es, es_evi->zevpn, + false /* add */); +} + +/* remove the ES-EVI from the per-L2-VNI and per-ES tables and free + * up the memory. + */ +static void zebra_evpn_es_evi_free(struct zebra_evpn_es_evi *es_evi) +{ + struct zebra_evpn_es *es = es_evi->es; + zebra_evpn_t *zevpn = es_evi->zevpn; + + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("es %s evi %d free", + es_evi->es->esi_str, es_evi->zevpn->vni); + + /* remove from the ES's VNI list */ + list_delete_node(es->es_evi_list, &es_evi->es_listnode); + + /* remove from the VNI-ESI rb tree */ + RB_REMOVE(zebra_es_evi_rb_head, &zevpn->es_evi_rb_tree, es_evi); + + /* remove from the VNI-ESI rb tree */ + XFREE(MTYPE_ZES_EVI, es_evi); +} + +/* find the ES-EVI in the per-L2-VNI RB tree */ +static struct zebra_evpn_es_evi *zebra_evpn_es_evi_find( + struct zebra_evpn_es *es, zebra_evpn_t *zevpn) +{ + struct zebra_evpn_es_evi es_evi; + + es_evi.es = es; + + return RB_FIND(zebra_es_evi_rb_head, &zevpn->es_evi_rb_tree, &es_evi); +} + +/* Tell BGP about an ES-EVI deletion and then delete it */ +static void zebra_evpn_local_es_evi_do_del(struct zebra_evpn_es_evi *es_evi) +{ + if (!(es_evi->flags & ZEBRA_EVPNES_EVI_LOCAL)) + return; + + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("local es %s evi %d del", + es_evi->es->esi_str, es_evi->zevpn->vni); + + if (es_evi->flags & ZEBRA_EVPNES_EVI_READY_FOR_BGP) { + /* send a del only if add was sent for it earlier */ + zebra_evpn_es_evi_send_to_client(es_evi->es, + es_evi->zevpn, false /* add */); + } + + /* delete it from the EVPN's local list */ + list_delete_node(es_evi->zevpn->local_es_evi_list, + &es_evi->l2vni_listnode); + + es_evi->flags &= ~ZEBRA_EVPNES_EVI_LOCAL; + zebra_evpn_es_evi_free(es_evi); +} +static void zebra_evpn_local_es_evi_del(struct zebra_evpn_es *es, + zebra_evpn_t *zevpn) +{ + struct zebra_evpn_es_evi *es_evi; + + es_evi = zebra_evpn_es_evi_find(es, zevpn); + if (es_evi) + zebra_evpn_local_es_evi_do_del(es_evi); +} + +/* Create an ES-EVI if it doesn't already exist and tell BGP */ +static void zebra_evpn_local_es_evi_add(struct zebra_evpn_es *es, + zebra_evpn_t *zevpn) +{ + struct zebra_evpn_es_evi *es_evi; + + es_evi = zebra_evpn_es_evi_find(es, zevpn); + if (!es_evi) { + es_evi = zebra_evpn_es_evi_new(es, zevpn); + if (!es_evi) + return; + + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("local es %s evi %d add", + es_evi->es->esi_str, es_evi->zevpn->vni); + es_evi->flags |= ZEBRA_EVPNES_EVI_LOCAL; + /* add to the EVPN's local list */ + listnode_init(&es_evi->l2vni_listnode, es_evi); + listnode_add(zevpn->local_es_evi_list, &es_evi->l2vni_listnode); + + zebra_evpn_es_evi_re_eval_send_to_client(es_evi); + } +} + +static void zebra_evpn_es_evi_show_entry(struct vty *vty, + struct zebra_evpn_es_evi *es_evi, json_object *json) +{ + char type_str[4]; + + if (json) { + /* XXX */ + } else { + type_str[0] = '\0'; + if (es_evi->flags & ZEBRA_EVPNES_EVI_LOCAL) + strlcat(type_str, "L", sizeof(type_str)); + + vty_out(vty, "%-8d %-30s %-4s\n", + es_evi->zevpn->vni, es_evi->es->esi_str, + type_str); + } +} + +static void zebra_evpn_es_evi_show_entry_detail(struct vty *vty, + struct zebra_evpn_es_evi *es_evi, json_object *json) +{ + char type_str[4]; + + if (json) { + /* XXX */ + } else { + type_str[0] = '\0'; + if (es_evi->flags & ZEBRA_EVPNES_EVI_LOCAL) + strlcat(type_str, "L", sizeof(type_str)); + + vty_out(vty, "VNI %d ESI: %s\n", + es_evi->zevpn->vni, es_evi->es->esi_str); + vty_out(vty, " Type: %s\n", type_str); + vty_out(vty, " Ready for BGP: %s\n", + (es_evi->flags & + ZEBRA_EVPNES_EVI_READY_FOR_BGP) ? + "yes" : "no"); + vty_out(vty, "\n"); + } +} + +static void zebra_evpn_es_evi_show_one_evpn(zebra_evpn_t *zevpn, + struct vty *vty, json_object *json, int detail) +{ + struct zebra_evpn_es_evi *es_evi; + + RB_FOREACH(es_evi, zebra_es_evi_rb_head, &zevpn->es_evi_rb_tree) { + if (detail) + zebra_evpn_es_evi_show_entry_detail(vty, es_evi, json); + else + zebra_evpn_es_evi_show_entry(vty, es_evi, json); + } +} + +struct evpn_mh_show_ctx { + struct vty *vty; + json_object *json; + int detail; +}; + +static void zebra_evpn_es_evi_show_one_evpn_hash_cb(struct hash_bucket *bucket, + void *ctxt) +{ + zebra_evpn_t *zevpn = (zebra_evpn_t *)bucket->data; + struct evpn_mh_show_ctx *wctx = (struct evpn_mh_show_ctx *)ctxt; + + zebra_evpn_es_evi_show_one_evpn(zevpn, wctx->vty, + wctx->json, wctx->detail); +} + +void zebra_evpn_es_evi_show(struct vty *vty, bool uj, int detail) +{ + json_object *json = NULL; + struct zebra_vrf *zvrf; + struct evpn_mh_show_ctx wctx; + + zvrf = zebra_vrf_get_evpn(); + + memset(&wctx, 0, sizeof(wctx)); + wctx.vty = vty; + wctx.json = json; + wctx.detail = detail; + + if (!detail && !json) { + vty_out(vty, "Type: L local, R remote\n"); + vty_out(vty, "%-8s %-30s %-4s\n", "VNI", "ESI", "Type"); + } + /* Display all L2-VNIs */ + hash_iterate(zvrf->evpn_table, zebra_evpn_es_evi_show_one_evpn_hash_cb, + &wctx); +} + +void zebra_evpn_es_evi_show_vni(struct vty *vty, bool uj, vni_t vni, int detail) +{ + json_object *json = NULL; + zebra_evpn_t *zevpn; + + zevpn = zebra_evpn_lookup(vni); + if (zevpn) { + if (!detail && !json) { + vty_out(vty, "Type: L local, R remote\n"); + vty_out(vty, "%-8s %-30s %-4s\n", "VNI", "ESI", "Type"); + } + zebra_evpn_es_evi_show_one_evpn(zevpn, vty, json, detail); + } else { + if (!uj) + vty_out(vty, "VNI %d doesn't exist\n", vni); + } +} + +/* Initialize the ES tables maintained per-L2_VNI */ +void zebra_evpn_evpn_es_init(zebra_evpn_t *zevpn) +{ + /* Initialize the ES-EVI RB tree */ + RB_INIT(zebra_es_evi_rb_head, &zevpn->es_evi_rb_tree); + + /* Initialize the local and remote ES lists maintained for quick + * walks by type + */ + zevpn->local_es_evi_list = list_new(); + listset_app_node_mem(zevpn->local_es_evi_list); +} + +/* Cleanup the ES info maintained per- EVPN */ +void zebra_evpn_evpn_es_cleanup(zebra_evpn_t *zevpn) +{ + struct zebra_evpn_es_evi *es_evi; + struct zebra_evpn_es_evi *es_evi_next; + + RB_FOREACH_SAFE(es_evi, zebra_es_evi_rb_head, + &zevpn->es_evi_rb_tree, es_evi_next) { + zebra_evpn_local_es_evi_do_del(es_evi); + } + + list_delete(&zevpn->local_es_evi_list); + zebra_evpn_es_clear_base_evpn(zevpn); +} + +/* called when the oper state or bridge membership changes for the + * vxlan device + */ +void zebra_evpn_update_all_es(zebra_evpn_t *zevpn) +{ + struct zebra_evpn_es_evi *es_evi; + struct listnode *node; + + /* the EVPN is now elgible as a base for EVPN-MH */ + if (zebra_evpn_send_to_client_ok(zevpn)) + zebra_evpn_es_set_base_evpn(zevpn); + else + zebra_evpn_es_clear_base_evpn(zevpn); + + for (ALL_LIST_ELEMENTS_RO(zevpn->local_es_evi_list, node, es_evi)) + zebra_evpn_es_evi_re_eval_send_to_client(es_evi); +} + +/*****************************************************************************/ +/* Access broadcast domains (BD) + * 1. These broadcast domains can be VLAN aware (in which case + * the key is VID) or VLAN unaware (in which case the key is + * 2. A VID-BD is created when a VLAN is associated with an access port or + * when the VLAN is associated with VXLAN_IF + * 3. A BD is translated into ES-EVI entries when a VNI is associated + * with the broadcast domain + */ +/* Hash key for VLAN based broadcast domains */ +static unsigned int zebra_evpn_acc_vl_hash_keymake(const void *p) +{ + const struct zebra_evpn_access_bd *acc_bd = p; + + return jhash_1word(acc_bd->vid, 0); +} + +/* Compare two VLAN based broadcast domains */ +static bool zebra_evpn_acc_vl_cmp(const void *p1, const void *p2) +{ + const struct zebra_evpn_access_bd *acc_bd1 = p1; + const struct zebra_evpn_access_bd *acc_bd2 = p2; + + if (acc_bd1 == NULL && acc_bd2 == NULL) + return true; + + if (acc_bd1 == NULL || acc_bd2 == NULL) + return false; + + return (acc_bd1->vid == acc_bd2->vid); +} + +/* Lookup VLAN based broadcast domain */ +static struct zebra_evpn_access_bd *zebra_evpn_acc_vl_find(vlanid_t vid) +{ + struct zebra_evpn_access_bd *acc_bd; + struct zebra_evpn_access_bd tmp; + + tmp.vid = vid; + acc_bd = hash_lookup(zmh_info->evpn_vlan_table, &tmp); + + return acc_bd; +} + +/* A new broadcast domain can be created when a VLAN member or VLAN<=>VxLAN_IF + * mapping is added. + */ +static struct zebra_evpn_access_bd *zebra_evpn_acc_vl_new(vlanid_t vid) +{ + struct zebra_evpn_access_bd *acc_bd; + + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("access vlan %d add", vid); + + acc_bd = XCALLOC(MTYPE_ZACC_BD, sizeof(struct zebra_evpn_access_bd)); + + acc_bd->vid = vid; + + /* Initialize the mbr list */ + acc_bd->mbr_zifs = list_new(); + + /* Add to hash */ + if (!hash_get(zmh_info->evpn_vlan_table, acc_bd, hash_alloc_intern)) { + XFREE(MTYPE_ZACC_BD, acc_bd); + return NULL; + } + + return acc_bd; +} + +/* Free VLAN based broadcast domain - + * This just frees appropriate memory, caller should have taken other + * needed actions. + */ +static void zebra_evpn_acc_vl_free(struct zebra_evpn_access_bd *acc_bd) +{ + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("access vlan %d del", acc_bd->vid); + + /* cleanup resources maintained against the ES */ + list_delete(&acc_bd->mbr_zifs); + + /* remove EVI from various tables */ + hash_release(zmh_info->evpn_vlan_table, acc_bd); + + XFREE(MTYPE_ZACC_BD, acc_bd); +} + +static void zebra_evpn_acc_vl_cleanup_all(struct hash_bucket *bucket, void *arg) +{ + struct zebra_evpn_access_bd *acc_bd = bucket->data; + + zebra_evpn_acc_vl_free(acc_bd); +} + +/* called when a bd mbr is removed or VxLAN_IF is diassociated from the access + * VLAN + */ +static void zebra_evpn_acc_bd_free_on_deref(struct zebra_evpn_access_bd *acc_bd) +{ + if (!list_isempty(acc_bd->mbr_zifs) || acc_bd->vxlan_zif) + return; + + /* if there are no references free the EVI */ + zebra_evpn_acc_vl_free(acc_bd); +} + +/* called when a EVPN-L2VNI is set or cleared against a BD */ +static void zebra_evpn_acc_bd_evpn_set(struct zebra_evpn_access_bd *acc_bd, + zebra_evpn_t *zevpn, zebra_evpn_t *old_zevpn) +{ + struct zebra_if *zif; + struct listnode *node; + + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("access vlan %d l2-vni %u set", + acc_bd->vid, zevpn ? zevpn->vni : 0); + + for (ALL_LIST_ELEMENTS_RO(acc_bd->mbr_zifs, node, zif)) { + if (!zif->es_info.es) + continue; + + if (zevpn) + zebra_evpn_local_es_evi_add(zif->es_info.es, zevpn); + else if (old_zevpn) + zebra_evpn_local_es_evi_del(zif->es_info.es, old_zevpn); + } +} + +/* handle VLAN->VxLAN_IF association */ +void zebra_evpn_vl_vxl_ref(uint16_t vid, struct zebra_if *vxlan_zif) +{ + struct zebra_evpn_access_bd *acc_bd; + struct zebra_if *old_vxlan_zif; + zebra_evpn_t *old_zevpn; + + if (!vid) + return; + + acc_bd = zebra_evpn_acc_vl_find(vid); + if (!acc_bd) + acc_bd = zebra_evpn_acc_vl_new(vid); + + old_vxlan_zif = acc_bd->vxlan_zif; + acc_bd->vxlan_zif = vxlan_zif; + if (vxlan_zif == old_vxlan_zif) + return; + + old_zevpn = acc_bd->zevpn; + acc_bd->zevpn = zebra_evpn_lookup(vxlan_zif->l2info.vxl.vni); + if (acc_bd->zevpn == old_zevpn) + return; + + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("access vlan %d vni %u ref", + acc_bd->vid, vxlan_zif->l2info.vxl.vni); + + if (old_zevpn) + zebra_evpn_acc_bd_evpn_set(acc_bd, NULL, old_zevpn); + + if (acc_bd->zevpn) + zebra_evpn_acc_bd_evpn_set(acc_bd, acc_bd->zevpn, NULL); +} + +/* handle VLAN->VxLAN_IF deref */ +void zebra_evpn_vl_vxl_deref(uint16_t vid, struct zebra_if *vxlan_zif) +{ + struct zebra_evpn_access_bd *acc_bd; + + if (!vid) + return; + + acc_bd = zebra_evpn_acc_vl_find(vid); + if (!acc_bd) + return; + + /* clear vxlan_if only if it matches */ + if (acc_bd->vxlan_zif != vxlan_zif) + return; + + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("access vlan %d vni %u deref", + acc_bd->vid, vxlan_zif->l2info.vxl.vni); + + if (acc_bd->zevpn) + zebra_evpn_acc_bd_evpn_set(acc_bd, NULL, acc_bd->zevpn); + + acc_bd->zevpn = NULL; + acc_bd->vxlan_zif = NULL; + + /* if there are no other references the access_bd can be freed */ + zebra_evpn_acc_bd_free_on_deref(acc_bd); +} + +/* handle EVPN add/del */ +void zebra_evpn_vxl_evpn_set(struct zebra_if *zif, zebra_evpn_t *zevpn, + bool set) +{ + struct zebra_l2info_vxlan *vxl; + struct zebra_evpn_access_bd *acc_bd; + + if (!zif) + return; + + /* locate access_bd associated with the vxlan device */ + vxl = &zif->l2info.vxl; + acc_bd = zebra_evpn_acc_vl_find(vxl->access_vlan); + if (!acc_bd) + return; + + if (set) { + zebra_evpn_es_set_base_evpn(zevpn); + if (acc_bd->zevpn != zevpn) { + acc_bd->zevpn = zevpn; + zebra_evpn_acc_bd_evpn_set(acc_bd, zevpn, NULL); + } + } else { + if (acc_bd->zevpn) { + zebra_evpn_t *old_zevpn = acc_bd->zevpn; + acc_bd->zevpn = NULL; + zebra_evpn_acc_bd_evpn_set(acc_bd, NULL, old_zevpn); + } + } +} + +/* handle addition of new VLAN members */ +void zebra_evpn_vl_mbr_ref(uint16_t vid, struct zebra_if *zif) +{ + struct zebra_evpn_access_bd *acc_bd; + + if (!vid) + return; + + acc_bd = zebra_evpn_acc_vl_find(vid); + if (!acc_bd) + acc_bd = zebra_evpn_acc_vl_new(vid); + + if (listnode_lookup(acc_bd->mbr_zifs, zif)) + return; + + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("access vlan %d mbr %s ref", + vid, zif->ifp->name); + + listnode_add(acc_bd->mbr_zifs, zif); + if (acc_bd->zevpn && zif->es_info.es) + zebra_evpn_local_es_evi_add(zif->es_info.es, acc_bd->zevpn); +} + +/* handle deletion of VLAN members */ +void zebra_evpn_vl_mbr_deref(uint16_t vid, struct zebra_if *zif) +{ + struct zebra_evpn_access_bd *acc_bd; + struct listnode *node; + + if (!vid) + return; + + acc_bd = zebra_evpn_acc_vl_find(vid); + if (!acc_bd) + return; + + node = listnode_lookup(acc_bd->mbr_zifs, zif); + if (!node) + return; + + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("access vlan %d mbr %s deref", + vid, zif->ifp->name); + + list_delete_node(acc_bd->mbr_zifs, node); + + if (acc_bd->zevpn && zif->es_info.es) + zebra_evpn_local_es_evi_del(zif->es_info.es, acc_bd->zevpn); + + /* if there are no other references the access_bd can be freed */ + zebra_evpn_acc_bd_free_on_deref(acc_bd); +} + +static void zebra_evpn_acc_vl_show_entry_detail(struct vty *vty, + struct zebra_evpn_access_bd *acc_bd, json_object *json) +{ + struct zebra_if *zif; + struct listnode *node; + + if (json) { + /* XXX */ + } else { + vty_out(vty, "VLAN: %u\n", acc_bd->vid); + vty_out(vty, " VxLAN Interface: %s\n", + acc_bd->vxlan_zif ? + acc_bd->vxlan_zif->ifp->name : "-"); + vty_out(vty, " L2-VNI: %d\n", + acc_bd->zevpn ? acc_bd->zevpn->vni : 0); + vty_out(vty, " Member Count: %d\n", + listcount(acc_bd->mbr_zifs)); + vty_out(vty, " Members: \n"); + for (ALL_LIST_ELEMENTS_RO(acc_bd->mbr_zifs, node, zif)) + vty_out(vty, " %s\n", zif->ifp->name); + vty_out(vty, "\n"); + } +} + +static void zebra_evpn_acc_vl_show_entry(struct vty *vty, + struct zebra_evpn_access_bd *acc_bd, json_object *json) +{ + if (!json) + vty_out(vty, "%-5u %21s %-8d %u\n", + acc_bd->vid, + acc_bd->vxlan_zif ? + acc_bd->vxlan_zif->ifp->name : "-", + acc_bd->zevpn ? acc_bd->zevpn->vni : 0, + listcount(acc_bd->mbr_zifs)); +} + +static void zebra_evpn_acc_vl_show_hash(struct hash_bucket *bucket, void *ctxt) +{ + struct evpn_mh_show_ctx *wctx = ctxt; + struct zebra_evpn_access_bd *acc_bd = bucket->data; + + if (wctx->detail) + zebra_evpn_acc_vl_show_entry_detail(wctx->vty, + acc_bd, wctx->json); + else + zebra_evpn_acc_vl_show_entry(wctx->vty, + acc_bd, wctx->json); +} + +void zebra_evpn_acc_vl_show(struct vty *vty, bool uj) +{ + json_object *json = NULL; + struct evpn_mh_show_ctx wctx; + + memset(&wctx, 0, sizeof(wctx)); + wctx.vty = vty; + wctx.json = json; + wctx.detail = false; + + if (!json) + vty_out(vty, "%-5s %21s %-8s %s\n", + "VLAN", "VxLAN-IF", "L2-VNI", "# Members"); + + hash_iterate(zmh_info->evpn_vlan_table, zebra_evpn_acc_vl_show_hash, + &wctx); +} + +void zebra_evpn_acc_vl_show_detail(struct vty *vty, bool uj) +{ + json_object *json = NULL; + struct evpn_mh_show_ctx wctx; + + memset(&wctx, 0, sizeof(wctx)); + wctx.vty = vty; + wctx.json = json; + wctx.detail = true; + + hash_iterate(zmh_info->evpn_vlan_table, zebra_evpn_acc_vl_show_hash, + &wctx); +} + +void zebra_evpn_acc_vl_show_vid(struct vty *vty, bool uj, vlanid_t vid) +{ + json_object *json = NULL; + struct zebra_evpn_access_bd *acc_bd; + + acc_bd = zebra_evpn_acc_vl_find(vid); + if (!acc_bd) { + if (!json) { + vty_out(vty, "VLAN %u not present\n", vid); + return; + } + } + zebra_evpn_acc_vl_show_entry_detail(vty, acc_bd, json); +} + +/* Initialize VLAN member bitmap on an interface. Although VLAN membership + * is independent of EVPN we only process it if its of interest to EVPN-MH + * i.e. on access ports that can be setup as Ethernet Segments. And that is + * intended as an optimization. + */ +void zebra_evpn_if_init(struct zebra_if *zif) +{ + if (!zebra_evpn_is_if_es_capable(zif)) + return; + + if (!bf_is_inited(zif->vlan_bitmap)) + bf_init(zif->vlan_bitmap, IF_VLAN_BITMAP_MAX); + + /* if an es_id and sysmac are already present against the interface + * activate it + */ + zebra_evpn_local_es_update(zif, zif->es_info.lid, &zif->es_info.sysmac); +} + +/* handle deletion of an access port by removing it from all associated + * broadcast domains. + */ +void zebra_evpn_if_cleanup(struct zebra_if *zif) +{ + vlanid_t vid; + struct zebra_evpn_es *es; + + if (!bf_is_inited(zif->vlan_bitmap)) + return; + + bf_for_each_set_bit(zif->vlan_bitmap, vid, IF_VLAN_BITMAP_MAX) { + zebra_evpn_vl_mbr_deref(vid, zif); + } + + bf_free(zif->vlan_bitmap); + + /* Delete associated Ethernet Segment */ + es = zif->es_info.es; + if (es) + zebra_evpn_local_es_del(&es); +} + +/***************************************************************************** + * L2 NH/NHG Management + * A L2 NH entry is programmed in the kernel for every ES-VTEP entry. This + * NH is then added to the L2-ECMP-NHG associated with the ES. + */ +static uint32_t zebra_evpn_nhid_alloc(bool is_nhg) +{ + uint32_t id; + int type; + + bf_assign_index(zmh_info->nh_id_bitmap, id); + + if (!id) + return 0; + + type = is_nhg ? EVPN_NHG_ID_TYPE_BIT : EVPN_NH_ID_TYPE_BIT; + return (id | type); +} + +static void zebra_evpn_nhid_free(uint32_t nh_id) +{ + uint32_t id = (nh_id & EVPN_NH_ID_VAL_MASK); + + if (!id) + return; + + bf_release_index(zmh_info->nh_id_bitmap, id); +} + +/* The MAC ECMP group is activated on the first VTEP */ +static void zebra_evpn_nhg_update(struct zebra_evpn_es *es) +{ + uint32_t nh_cnt = 0; + struct nh_grp nh_ids[ES_VTEP_MAX_CNT]; + struct zebra_evpn_es_vtep *es_vtep; + struct listnode *node; + + if (!es->nhg_id) + return; + + for (ALL_LIST_ELEMENTS_RO(es->es_vtep_list, node, es_vtep)) { + if (!es_vtep->nh_id) + continue; + + if (nh_cnt >= ES_VTEP_MAX_CNT) + break; + + memset(&nh_ids[nh_cnt], 0, sizeof(struct nh_grp)); + nh_ids[nh_cnt].id = es_vtep->nh_id; + ++nh_cnt; + } + + if (nh_cnt) { + if (IS_ZEBRA_DEBUG_EVPN_MH_NH) { + char nh_str[ES_VTEP_LIST_STR_SZ]; + uint32_t i; + char nh_buf[16]; + + nh_str[0] = '\0'; + for (i = 0; i < nh_cnt; ++i) { + snprintf(nh_buf, sizeof(nh_buf), "%u ", + nh_ids[i].id); + strlcat(nh_str, nh_buf, sizeof(nh_str)); + } + zlog_debug("es %s nhg 0x%x add %s", + es->esi_str, es->nhg_id, nh_str); + } + + es->flags |= ZEBRA_EVPNES_NHG_ACTIVE; + kernel_upd_mac_nhg(es->nhg_id, nh_cnt, nh_ids); + } else { + if (es->flags & ZEBRA_EVPNES_NHG_ACTIVE) { + if (IS_ZEBRA_DEBUG_EVPN_MH_NH) + zlog_debug("es %s nhg 0x%x del", + es->esi_str, es->nhg_id); + es->flags &= ~ZEBRA_EVPNES_NHG_ACTIVE; + kernel_del_mac_nhg(es->nhg_id); + } + } + + /* XXX - update remote macs associated with the ES */ +} + +static void zebra_evpn_nh_add(struct zebra_evpn_es_vtep *es_vtep) +{ + if (es_vtep->nh_id) + return; + + es_vtep->nh_id = zebra_evpn_nhid_alloc(false); + + if (!es_vtep->nh_id) + return; + + if (IS_ZEBRA_DEBUG_EVPN_MH_NH) + zlog_debug("es %s vtep %s nh 0x%x add", + es_vtep->es->esi_str, + inet_ntoa(es_vtep->vtep_ip), es_vtep->nh_id); + /* install the NH */ + kernel_upd_mac_nh(es_vtep->nh_id, es_vtep->vtep_ip); + /* add the NH to the parent NHG */ + zebra_evpn_nhg_update(es_vtep->es); +} + +static void zebra_evpn_nh_del(struct zebra_evpn_es_vtep *es_vtep) +{ + uint32_t nh_id; + + if (!es_vtep->nh_id) + return; + + if (IS_ZEBRA_DEBUG_EVPN_MH_NH) + zlog_debug("es %s vtep %s nh 0x%x del", + es_vtep->es->esi_str, + inet_ntoa(es_vtep->vtep_ip), es_vtep->nh_id); + + nh_id = es_vtep->nh_id; + es_vtep->nh_id = 0; + + /* remove the NH from the parent NHG */ + zebra_evpn_nhg_update(es_vtep->es); + /* uninstall the NH */ + kernel_del_mac_nh(nh_id); + zebra_evpn_nhid_free(nh_id); + +} + +/*****************************************************************************/ +/* Ethernet Segment Management + * 1. Ethernet Segment is a collection of links attached to the same + * server (MHD) or switch (MHN) + * 2. An Ethernet Segment can span multiple PEs and is identified by the + * 10-byte ES-ID. + * 3. Zebra manages the local ESI configuration. + * 4. It also maintains the aliasing that maps an ESI (local or remote) + * to one or more PEs/VTEPs. + * 5. remote ESs are added by BGP (on rxing EAD Type-1 routes) + */ +/* A list of remote VTEPs is maintained for each ES. This list includes - + * 1. VTEPs for which we have imported the ESR i.e. ES-peers + * 2. VTEPs that have an "active" ES-EVI VTEP i.e. EAD-per-ES and EAD-per-EVI + * have been imported into one or more EVPNs + */ +static int zebra_evpn_es_vtep_cmp(void *p1, void *p2) +{ + const struct zebra_evpn_es_vtep *es_vtep1 = p1; + const struct zebra_evpn_es_vtep *es_vtep2 = p2; + + return es_vtep1->vtep_ip.s_addr - es_vtep2->vtep_ip.s_addr; +} + +static struct zebra_evpn_es_vtep *zebra_evpn_es_vtep_new( + struct zebra_evpn_es *es, struct in_addr vtep_ip) +{ + struct zebra_evpn_es_vtep *es_vtep; + + es_vtep = XCALLOC(MTYPE_ZES_VTEP, sizeof(*es_vtep)); + + es_vtep->es = es; + es_vtep->vtep_ip.s_addr = vtep_ip.s_addr; + listnode_init(&es_vtep->es_listnode, es_vtep); + listnode_add_sort(es->es_vtep_list, &es_vtep->es_listnode); + + return es_vtep; +} + +static void zebra_evpn_es_vtep_free(struct zebra_evpn_es_vtep *es_vtep) +{ + struct zebra_evpn_es *es = es_vtep->es; + + list_delete_node(es->es_vtep_list, &es_vtep->es_listnode); + /* update the L2-NHG associated with the ES */ + zebra_evpn_nh_del(es_vtep); + XFREE(MTYPE_ZES_VTEP, es_vtep); +} + + +/* check if VTEP is already part of the list */ +static struct zebra_evpn_es_vtep *zebra_evpn_es_vtep_find( + struct zebra_evpn_es *es, struct in_addr vtep_ip) +{ + struct listnode *node = NULL; + struct zebra_evpn_es_vtep *es_vtep; + + for (ALL_LIST_ELEMENTS_RO(es->es_vtep_list, node, es_vtep)) { + if (es_vtep->vtep_ip.s_addr == vtep_ip.s_addr) + return es_vtep; + } + return NULL; +} + +static void zebra_evpn_es_vtep_add(struct zebra_evpn_es *es, + struct in_addr vtep_ip) +{ + struct zebra_evpn_es_vtep *es_vtep; + + es_vtep = zebra_evpn_es_vtep_find(es, vtep_ip); + + if (!es_vtep) { + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("es %s vtep %s add", + es->esi_str, inet_ntoa(vtep_ip)); + es_vtep = zebra_evpn_es_vtep_new(es, vtep_ip); + /* update the L2-NHG associated with the ES */ + zebra_evpn_nh_add(es_vtep); + } +} + +static void zebra_evpn_es_vtep_del(struct zebra_evpn_es *es, + struct in_addr vtep_ip) +{ + struct zebra_evpn_es_vtep *es_vtep; + + es_vtep = zebra_evpn_es_vtep_find(es, vtep_ip); + + if (es_vtep) { + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("es %s vtep %s del", + es->esi_str, inet_ntoa(vtep_ip)); + zebra_evpn_es_vtep_free(es_vtep); + } +} + +/* compare ES-IDs for the global ES RB tree */ +static int zebra_es_rb_cmp(const struct zebra_evpn_es *es1, + const struct zebra_evpn_es *es2) +{ + return memcmp(&es1->esi, &es2->esi, ESI_BYTES); +} +RB_GENERATE(zebra_es_rb_head, zebra_evpn_es, rb_node, zebra_es_rb_cmp); + +/* Lookup ES */ +struct zebra_evpn_es *zebra_evpn_es_find(esi_t *esi) +{ + struct zebra_evpn_es tmp; + + memcpy(&tmp.esi, esi, sizeof(esi_t)); + return RB_FIND(zebra_es_rb_head, &zmh_info->es_rb_tree, &tmp); +} + +/* A new local es is created when a local-es-id and sysmac is configured + * against an interface. + */ +static struct zebra_evpn_es *zebra_evpn_es_new(esi_t *esi) +{ + struct zebra_evpn_es *es; + + es = XCALLOC(MTYPE_ZES, sizeof(struct zebra_evpn_es)); + + /* fill in ESI */ + memcpy(&es->esi, esi, sizeof(esi_t)); + esi_to_str(&es->esi, es->esi_str, sizeof(es->esi_str)); + + /* Add to rb_tree */ + if (RB_INSERT(zebra_es_rb_head, &zmh_info->es_rb_tree, es)) { + XFREE(MTYPE_ZES, es); + return NULL; + } + + /* Initialise the ES-EVI list */ + es->es_evi_list = list_new(); + listset_app_node_mem(es->es_evi_list); + + /* Initialise the VTEP list */ + es->es_vtep_list = list_new(); + listset_app_node_mem(es->es_vtep_list); + es->es_vtep_list->cmp = zebra_evpn_es_vtep_cmp; + + /* mac entries associated with the ES */ + es->mac_list = list_new(); + listset_app_node_mem(es->mac_list); + + /* reserve a NHG */ + es->nhg_id = zebra_evpn_nhid_alloc(true); + + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("es %s nhg 0x%x new", es->esi_str, es->nhg_id); + + return es; +} + +/* Free a given ES - + * This just frees appropriate memory, caller should have taken other + * needed actions. + */ +static void zebra_evpn_es_free(struct zebra_evpn_es **esp) +{ + struct zebra_evpn_es *es = *esp; + + /* If the ES has a local or remote reference it cannot be freed. + * Free is also prevented if there are MAC entries referencing + * it. + */ + if ((es->flags & (ZEBRA_EVPNES_LOCAL | ZEBRA_EVPNES_REMOTE)) || + listcount(es->mac_list)) + return; + + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("es %s free", es->esi_str); + + /* If the NHG is still installed uninstall it and free the id */ + if (es->flags & ZEBRA_EVPNES_NHG_ACTIVE) { + es->flags &= ~ZEBRA_EVPNES_NHG_ACTIVE; + kernel_del_mac_nhg(es->nhg_id); + } + zebra_evpn_nhid_free(es->nhg_id); + + /* cleanup resources maintained against the ES */ + list_delete(&es->es_evi_list); + list_delete(&es->es_vtep_list); + list_delete(&es->mac_list); + + /* remove from the VNI-ESI rb tree */ + RB_REMOVE(zebra_es_rb_head, &zmh_info->es_rb_tree, es); + + XFREE(MTYPE_ZES, es); + + *esp = NULL; +} + +/* Inform BGP about local ES addition */ +static int zebra_evpn_es_send_add_to_client(struct zebra_evpn_es *es) +{ + struct zserv *client; + struct stream *s; + uint8_t oper_up; + + client = zserv_find_client(ZEBRA_ROUTE_BGP, 0); + /* BGP may not be running. */ + if (!client) + return 0; + + s = stream_new(ZEBRA_MAX_PACKET_SIZ); + + zclient_create_header(s, ZEBRA_LOCAL_ES_ADD, zebra_vrf_get_evpn_id()); + stream_put(s, &es->esi, sizeof(esi_t)); + stream_put_ipv4(s, zmh_info->es_originator_ip.s_addr); + oper_up = !!(es->flags & ZEBRA_EVPNES_OPER_UP); + stream_putc(s, oper_up); + + /* Write packet size. */ + stream_putw_at(s, 0, stream_get_endp(s)); + + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("send add local es %s %s to %s", + es->esi_str, + inet_ntoa(zmh_info->es_originator_ip), + zebra_route_string(client->proto)); + + client->local_es_add_cnt++; + return zserv_send_message(client, s); +} + +/* Inform BGP about local ES deletion */ +static int zebra_evpn_es_send_del_to_client(struct zebra_evpn_es *es) +{ + struct zserv *client; + struct stream *s; + + client = zserv_find_client(ZEBRA_ROUTE_BGP, 0); + /* BGP may not be running. */ + if (!client) + return 0; + + s = stream_new(ZEBRA_MAX_PACKET_SIZ); + stream_reset(s); + + zclient_create_header(s, ZEBRA_LOCAL_ES_DEL, zebra_vrf_get_evpn_id()); + stream_put(s, &es->esi, sizeof(esi_t)); + + /* Write packet size. */ + stream_putw_at(s, 0, stream_get_endp(s)); + + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("send del local es %s to %s", es->esi_str, + zebra_route_string(client->proto)); + + client->local_es_del_cnt++; + return zserv_send_message(client, s); +} + +static void zebra_evpn_es_re_eval_send_to_client(struct zebra_evpn_es *es, + bool es_evi_re_reval) +{ + bool old_ready; + bool new_ready; + struct listnode *node; + struct zebra_evpn_es_evi *es_evi; + + old_ready = !!(es->flags & ZEBRA_EVPNES_READY_FOR_BGP); + + if ((es->flags & ZEBRA_EVPNES_LOCAL) && + zmh_info->es_originator_ip.s_addr) + es->flags |= ZEBRA_EVPNES_READY_FOR_BGP; + else + es->flags &= ~ZEBRA_EVPNES_READY_FOR_BGP; + + new_ready = !!(es->flags & ZEBRA_EVPNES_READY_FOR_BGP); + if (old_ready == new_ready) + return; + + if (new_ready) + zebra_evpn_es_send_add_to_client(es); + else + zebra_evpn_es_send_del_to_client(es); + + /* re-eval associated EVIs */ + if (es_evi_re_reval) { + for (ALL_LIST_ELEMENTS_RO(es->es_evi_list, node, es_evi)) { + if (!(es_evi->flags & ZEBRA_EVPNES_EVI_LOCAL)) + continue; + zebra_evpn_es_evi_re_eval_send_to_client(es_evi); + } + } +} + +void zebra_evpn_es_send_all_to_client(bool add) +{ + struct listnode *es_node; + struct listnode *evi_node; + struct zebra_evpn_es *es; + struct zebra_evpn_es_evi *es_evi; + + if (!zmh_info) + return; + + for (ALL_LIST_ELEMENTS_RO(zmh_info->local_es_list, es_node, es)) { + if (es->flags & ZEBRA_EVPNES_READY_FOR_BGP) { + if (add) + zebra_evpn_es_send_add_to_client(es); + for (ALL_LIST_ELEMENTS_RO(es->es_evi_list, + evi_node, es_evi)) { + if (!(es_evi->flags & + ZEBRA_EVPNES_EVI_READY_FOR_BGP)) + continue; + + if (add) + zebra_evpn_es_evi_send_to_client( + es, es_evi->zevpn, + true /* add */); + else + zebra_evpn_es_evi_send_to_client( + es, es_evi->zevpn, + false /* add */); + } + if (!add) + zebra_evpn_es_send_del_to_client(es); + } + } +} + +/* walk the vlan bitmap associated with the zif and create or delete + * es_evis for all vlans associated with a VNI. + * XXX: This API is really expensive. optimize later if possible. + */ +static void zebra_evpn_es_setup_evis(struct zebra_evpn_es *es) +{ + struct zebra_if *zif = es->zif; + uint16_t vid; + struct zebra_evpn_access_bd *acc_bd; + + + bf_for_each_set_bit(zif->vlan_bitmap, vid, IF_VLAN_BITMAP_MAX) { + acc_bd = zebra_evpn_acc_vl_find(vid); + if (acc_bd->zevpn) + zebra_evpn_local_es_evi_add(es, acc_bd->zevpn); + } +} + +static void zebra_evpn_es_local_mac_update(struct zebra_evpn_es *es, + bool force_clear_static) +{ + zebra_mac_t *mac; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(es->mac_list, node, mac)) { + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_ACTIVE)) { + zebra_evpn_sync_mac_dp_install( + mac, false /* set_inactive */, + force_clear_static, __func__); + } + } +} + +static void zebra_evpn_es_local_info_set(struct zebra_evpn_es *es, + struct zebra_if *zif) +{ + if (es->flags & ZEBRA_EVPNES_LOCAL) + return; + + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("local es %s add; nhg 0x%x if %s", + es->esi_str, es->nhg_id, zif->ifp->name); + + es->flags |= ZEBRA_EVPNES_LOCAL; + listnode_init(&es->local_es_listnode, es); + listnode_add(zmh_info->local_es_list, &es->local_es_listnode); + + /* attach es to interface */ + zif->es_info.es = es; + + /* attach interface to es */ + es->zif = zif; + if (if_is_operative(zif->ifp)) + es->flags |= ZEBRA_EVPNES_OPER_UP; + + /* setup base-vni if one doesn't already exist; the ES will get sent + * to BGP as a part of that process + */ + if (!zmh_info->es_base_evpn) + zebra_evpn_es_get_one_base_evpn(); + else + /* send notification to bgp */ + zebra_evpn_es_re_eval_send_to_client(es, + false /* es_evi_re_reval */); + + /* Setup ES-EVIs for all VxLAN stretched VLANs associated with + * the zif + */ + zebra_evpn_es_setup_evis(es); + /* if there any local macs referring to the ES as dest we + * need to set the static reference on them if the MAC is + * synced from an ES peer + */ + zebra_evpn_es_local_mac_update(es, + false /* force_clear_static */); +} + +static void zebra_evpn_es_local_info_clear(struct zebra_evpn_es **esp) +{ + struct zebra_if *zif; + struct zebra_evpn_es *es = *esp; + + if (!(es->flags & ZEBRA_EVPNES_LOCAL)) + return; + + es->flags &= ~(ZEBRA_EVPNES_LOCAL | ZEBRA_EVPNES_READY_FOR_BGP); + + /* if there any local macs referring to the ES as dest we + * need to clear the static reference on them + */ + zebra_evpn_es_local_mac_update(es, + true /* force_clear_static */); + + /* clear the es from the parent interface */ + zif = es->zif; + zif->es_info.es = NULL; + es->zif = NULL; + + /* remove from the ES list */ + list_delete_node(zmh_info->local_es_list, &es->local_es_listnode); + + /* free up the ES if there is no remote reference */ + zebra_evpn_es_free(esp); +} + +/* Delete an ethernet segment and inform BGP */ +static void zebra_evpn_local_es_del(struct zebra_evpn_es **esp) +{ + struct zebra_evpn_es_evi *es_evi; + struct listnode *node = NULL; + struct listnode *nnode = NULL; + struct zebra_if *zif; + struct zebra_evpn_es *es = *esp; + + if (!CHECK_FLAG(es->flags, ZEBRA_EVPNES_LOCAL)) + return; + + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) { + zif = es->zif; + zlog_debug("local es %s del; nhg 0x%x if %s", + es->esi_str, es->nhg_id, + zif ? zif->ifp->name : "-"); + } + + /* remove all ES-EVIs associated with the ES */ + for (ALL_LIST_ELEMENTS(es->es_evi_list, node, nnode, es_evi)) + zebra_evpn_local_es_evi_do_del(es_evi); + + /* send a del if the ES had been sent to BGP earlier */ + if (es->flags & ZEBRA_EVPNES_READY_FOR_BGP) + zebra_evpn_es_send_del_to_client(es); + + zebra_evpn_es_local_info_clear(esp); +} + +/* eval remote info associated with the ES */ +static void zebra_evpn_es_remote_info_re_eval(struct zebra_evpn_es **esp) +{ + struct zebra_evpn_es *es = *esp; + + /* if there are remote VTEPs the ES-EVI is classified as "remote" */ + if (listcount(es->es_vtep_list)) { + if (!(es->flags & ZEBRA_EVPNES_REMOTE)) { + es->flags |= ZEBRA_EVPNES_REMOTE; + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("remote es %s add; nhg 0x%x", + es->esi_str, es->nhg_id); + } + } else { + if (es->flags & ZEBRA_EVPNES_REMOTE) { + es->flags &= ~ZEBRA_EVPNES_REMOTE; + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("remote es %s del; nhg 0x%x", + es->esi_str, es->nhg_id); + zebra_evpn_es_free(esp); + } + } +} + +/* A new local es is created when a local-es-id and sysmac is configured + * against an interface. + */ +static int zebra_evpn_local_es_update(struct zebra_if *zif, uint32_t lid, + struct ethaddr *sysmac) +{ + struct zebra_evpn_es *old_es = zif->es_info.es; + struct zebra_evpn_es *es; + esi_t esi; + int offset = 0; + int field_bytes = 0; + + /* Complete config of the ES-ID bootstraps the ES */ + if (!lid || is_zero_mac(sysmac)) { + /* if in ES is attached to zif delete it */ + if (old_es) + zebra_evpn_local_es_del(&old_es); + return 0; + } + + /* build 10-byte type-3-ESI - + * Type(1-byte), MAC(6-bytes), ES-LID (3-bytes) + */ + field_bytes = 1; + esi.val[offset] = ESI_TYPE_MAC; + offset += field_bytes; + + field_bytes = ETH_ALEN; + memcpy(&esi.val[offset], (uint8_t *)sysmac, field_bytes); + offset += field_bytes; + + esi.val[offset++] = (uint8_t)(lid >> 16); + esi.val[offset++] = (uint8_t)(lid >> 8); + esi.val[offset++] = (uint8_t)lid; + + if (old_es && !memcmp(&old_es->esi, &esi, sizeof(esi_t))) + /* dup - nothing to be done */ + return 0; + + /* release the old_es against the zif */ + if (old_es) + zebra_evpn_local_es_del(&old_es); + + es = zebra_evpn_es_find(&esi); + if (es) { + /* if it exists against another interface flag an error */ + if (es->zif && es->zif != zif) + return -1; + } else { + /* create new es */ + es = zebra_evpn_es_new(&esi); + } + + zebra_evpn_es_local_info_set(es, zif); + + return 0; +} + +static int zebra_evpn_remote_es_del(esi_t *esi, struct in_addr vtep_ip) +{ + char buf[ESI_STR_LEN]; + struct zebra_evpn_es *es; + + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("remote es %s vtep %s del", + esi_to_str(esi, buf, sizeof(buf)), + inet_ntoa(vtep_ip)); + + es = zebra_evpn_es_find(esi); + if (!es) { + zlog_warn("remote es %s vtep %pI4 del failed, es missing", + esi_to_str(esi, buf, sizeof(buf)), &vtep_ip); + return -1; + } + + zebra_evpn_es_vtep_del(es, vtep_ip); + zebra_evpn_es_remote_info_re_eval(&es); + + return 0; +} + +/* force delete a remote ES on the way down */ +static void zebra_evpn_remote_es_flush(struct zebra_evpn_es **esp) +{ + struct zebra_evpn_es_vtep *es_vtep; + struct listnode *node; + struct listnode *nnode; + struct zebra_evpn_es *es = *esp; + + for (ALL_LIST_ELEMENTS(es->es_vtep_list, node, nnode, es_vtep)) { + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("es %s vtep %s flush", + es->esi_str, + inet_ntoa(es_vtep->vtep_ip)); + zebra_evpn_es_vtep_free(es_vtep); + } + zebra_evpn_es_remote_info_re_eval(esp); +} + +static int zebra_evpn_remote_es_add(esi_t *esi, struct in_addr vtep_ip) +{ + char buf[ESI_STR_LEN]; + struct zebra_evpn_es *es; + + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("remote es %s vtep %s add", + esi_to_str(esi, buf, sizeof(buf)), + inet_ntoa(vtep_ip)); + + es = zebra_evpn_es_find(esi); + if (!es) { + es = zebra_evpn_es_new(esi); + if (!es) { + zlog_warn( + "remote es %s vtep %pI4 add failed, es missing", + esi_to_str(esi, buf, sizeof(buf)), &vtep_ip); + return -1; + } + } + + zebra_evpn_es_vtep_add(es, vtep_ip); + zebra_evpn_es_remote_info_re_eval(&es); + + return 0; +} + +void zebra_evpn_proc_remote_es(ZAPI_HANDLER_ARGS) +{ + struct stream *s; + struct in_addr vtep_ip; + esi_t esi; + + if (!is_evpn_enabled()) { + zlog_debug( + "%s: EVPN not enabled yet we received a es_add zapi call", + __PRETTY_FUNCTION__); + return; + } + + memset(&esi, 0, sizeof(esi_t)); + s = msg; + + stream_get(&esi, s, sizeof(esi_t)); + vtep_ip.s_addr = stream_get_ipv4(s); + + if (hdr->command == ZEBRA_REMOTE_ES_VTEP_ADD) + zebra_evpn_remote_es_add(&esi, vtep_ip); + else + zebra_evpn_remote_es_del(&esi, vtep_ip); +} + +void zebra_evpn_es_mac_deref_entry(zebra_mac_t *mac) +{ + struct zebra_evpn_es *es = mac->es; + + mac->es = NULL; + if (!es) + return; + + list_delete_node(es->mac_list, &mac->es_listnode); + if (!listcount(es->mac_list)) + zebra_evpn_es_free(&es); +} + +/* Associate a MAC entry with a local or remote ES. Returns false if there + * was no ES change. + */ +bool zebra_evpn_es_mac_ref_entry(zebra_mac_t *mac, struct zebra_evpn_es *es) +{ + if (mac->es == es) + return false; + + if (mac->es) + zebra_evpn_es_mac_deref_entry(mac); + + if (!es) + return true; + + mac->es = es; + listnode_init(&mac->es_listnode, mac); + listnode_add(es->mac_list, &mac->es_listnode); + + return true; +} + +bool zebra_evpn_es_mac_ref(zebra_mac_t *mac, esi_t *esi) +{ + struct zebra_evpn_es *es; + + es = zebra_evpn_es_find(esi); + if (!es) { + /* If non-zero esi implicitly create a new ES */ + if (memcmp(esi, zero_esi, sizeof(esi_t))) { + es = zebra_evpn_es_new(esi); + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("auto es %s add on mac ref", + es->esi_str); + } + } + + return zebra_evpn_es_mac_ref_entry(mac, es); +} + +/* Inform BGP about local ES-EVI add or del */ +static int zebra_evpn_es_evi_send_to_client(struct zebra_evpn_es *es, + zebra_evpn_t *zevpn, bool add) +{ + struct zserv *client; + struct stream *s; + + client = zserv_find_client(ZEBRA_ROUTE_BGP, 0); + /* BGP may not be running. */ + if (!client) + return 0; + + s = stream_new(ZEBRA_MAX_PACKET_SIZ); + + zclient_create_header(s, + add ? ZEBRA_LOCAL_ES_EVI_ADD : ZEBRA_LOCAL_ES_EVI_DEL, + zebra_vrf_get_evpn_id()); + stream_put(s, &es->esi, sizeof(esi_t)); + stream_putl(s, zevpn->vni); + + /* Write packet size. */ + stream_putw_at(s, 0, stream_get_endp(s)); + + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("send %s local es %s evi %u to %s", + add ? "add" : "del", + es->esi_str, zevpn->vni, + zebra_route_string(client->proto)); + + client->local_es_add_cnt++; + return zserv_send_message(client, s); +} + +/* sysmac part of a local ESI has changed */ +static int zebra_evpn_es_sys_mac_update(struct zebra_if *zif, + struct ethaddr *sysmac) +{ + int rv; + + rv = zebra_evpn_local_es_update(zif, zif->es_info.lid, sysmac); + if (!rv) + memcpy(&zif->es_info.sysmac, sysmac, sizeof(struct ethaddr)); + + return rv; +} + +/* local-ID part of ESI has changed */ +static int zebra_evpn_es_lid_update(struct zebra_if *zif, uint32_t lid) +{ + int rv; + + rv = zebra_evpn_local_es_update(zif, lid, &zif->es_info.sysmac); + if (!rv) + zif->es_info.lid = lid; + + return rv; +} + +void zebra_evpn_es_cleanup(void) +{ + struct zebra_evpn_es *es; + struct zebra_evpn_es *es_next; + + RB_FOREACH_SAFE(es, zebra_es_rb_head, + &zmh_info->es_rb_tree, es_next) { + zebra_evpn_local_es_del(&es); + if (es) + zebra_evpn_remote_es_flush(&es); + } +} + +/* Only certain types of access ports can be setup as an Ethernet Segment */ +bool zebra_evpn_is_if_es_capable(struct zebra_if *zif) +{ + if (zif->zif_type == ZEBRA_IF_BOND) + return true; + + /* XXX: allow swpX i.e. a regular ethernet port to be an ES link too */ + return false; +} + +void zebra_evpn_if_es_print(struct vty *vty, struct zebra_if *zif) +{ + char buf[ETHER_ADDR_STRLEN]; + + if (zif->es_info.lid || !is_zero_mac(&zif->es_info.sysmac)) + vty_out(vty, " EVPN MH: ES id %u ES sysmac %s\n", + zif->es_info.lid, + prefix_mac2str(&zif->es_info.sysmac, + buf, sizeof(buf))); +} + +void zebra_evpn_es_if_oper_state_change(struct zebra_if *zif, bool up) +{ + struct zebra_evpn_es *es = zif->es_info.es; + bool old_up = !!(es->flags & ZEBRA_EVPNES_OPER_UP); + + if (old_up == up) + return; + + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("es %s state changed to %s ", + es->esi_str, + up ? "up" : "down"); + if (up) + es->flags |= ZEBRA_EVPNES_OPER_UP; + else + es->flags &= ~ZEBRA_EVPNES_OPER_UP; + + /* inform BGP of the ES oper state change */ + if (es->flags & ZEBRA_EVPNES_READY_FOR_BGP) + zebra_evpn_es_send_add_to_client(es); +} + +static char *zebra_evpn_es_vtep_str(char *vtep_str, struct zebra_evpn_es *es, + uint8_t vtep_str_size) +{ + struct zebra_evpn_es_vtep *zvtep; + struct listnode *node; + bool first = true; + + vtep_str[0] = '\0'; + for (ALL_LIST_ELEMENTS_RO(es->es_vtep_list, node, zvtep)) { + if (first) { + first = false; + strlcat(vtep_str, inet_ntoa(zvtep->vtep_ip), + vtep_str_size); + } else { + strlcat(vtep_str, ",", vtep_str_size); + strlcat(vtep_str, inet_ntoa(zvtep->vtep_ip), + vtep_str_size); + } + } + return vtep_str; +} + +static void zebra_evpn_es_show_entry(struct vty *vty, + struct zebra_evpn_es *es, json_object *json) +{ + char type_str[4]; + char vtep_str[ES_VTEP_LIST_STR_SZ]; + + if (json) { + /* XXX */ + } else { + type_str[0] = '\0'; + if (es->flags & ZEBRA_EVPNES_LOCAL) + strlcat(type_str, "L", sizeof(type_str)); + if (es->flags & ZEBRA_EVPNES_REMOTE) + strlcat(type_str, "R", sizeof(type_str)); + + zebra_evpn_es_vtep_str(vtep_str, es, sizeof(vtep_str)); + + vty_out(vty, "%-30s %-4s %-21s %s\n", + es->esi_str, type_str, + es->zif ? es->zif->ifp->name : "-", + vtep_str); + } +} + +static void zebra_evpn_es_show_entry_detail(struct vty *vty, + struct zebra_evpn_es *es, json_object *json) +{ + char type_str[80]; + struct zebra_evpn_es_vtep *zvtep; + struct listnode *node; + + if (json) { + /* XXX */ + } else { + type_str[0] = '\0'; + if (es->flags & ZEBRA_EVPNES_LOCAL) + strlcat(type_str, "Local", sizeof(type_str)); + if (es->flags & ZEBRA_EVPNES_REMOTE) { + if (strnlen(type_str, sizeof(type_str))) + strlcat(type_str, ",", sizeof(type_str)); + strlcat(type_str, "Remote", sizeof(type_str)); + } + + vty_out(vty, "ESI: %s\n", es->esi_str); + vty_out(vty, " Type: %s\n", type_str); + vty_out(vty, " Interface: %s\n", + (es->zif) ? + es->zif->ifp->name : "-"); + vty_out(vty, " State: %s\n", + (es->flags & ZEBRA_EVPNES_OPER_UP) ? + "up" : "down"); + vty_out(vty, " Ready for BGP: %s\n", + (es->flags & ZEBRA_EVPNES_READY_FOR_BGP) ? + "yes" : "no"); + vty_out(vty, " VNI Count: %d\n", listcount(es->es_evi_list)); + vty_out(vty, " MAC Count: %d\n", listcount(es->mac_list)); + vty_out(vty, " Nexthop group: 0x%x\n", es->nhg_id); + vty_out(vty, " VTEPs:\n"); + for (ALL_LIST_ELEMENTS_RO(es->es_vtep_list, node, zvtep)) + vty_out(vty, " %s nh: 0x%x\n", + inet_ntoa(zvtep->vtep_ip), + zvtep->nh_id); + + vty_out(vty, "\n"); + } +} + +void zebra_evpn_es_show(struct vty *vty, bool uj) +{ + struct zebra_evpn_es *es; + json_object *json = NULL; + + if (uj) { + /* XXX */ + } else { + vty_out(vty, "Type: L local, R remote\n"); + vty_out(vty, "%-30s %-4s %-21s %s\n", + "ESI", "Type", "ES-IF", "VTEPs"); + } + + RB_FOREACH(es, zebra_es_rb_head, &zmh_info->es_rb_tree) + zebra_evpn_es_show_entry(vty, es, json); +} + +void zebra_evpn_es_show_detail(struct vty *vty, bool uj) +{ + struct zebra_evpn_es *es; + json_object *json = NULL; + + RB_FOREACH(es, zebra_es_rb_head, &zmh_info->es_rb_tree) + zebra_evpn_es_show_entry_detail(vty, es, json); +} + +void zebra_evpn_es_show_esi(struct vty *vty, bool uj, esi_t *esi) +{ + struct zebra_evpn_es *es; + char esi_str[ESI_STR_LEN]; + json_object *json = NULL; + + es = zebra_evpn_es_find(esi); + + if (!es) { + esi_to_str(esi, esi_str, sizeof(esi_str)); + vty_out(vty, "ESI %s does not exist\n", esi_str); + return; + } + + zebra_evpn_es_show_entry_detail(vty, es, json); +} + +int zebra_evpn_mh_if_write(struct vty *vty, struct interface *ifp) +{ + struct zebra_if *zif = ifp->info; + char buf[ETHER_ADDR_STRLEN]; + + if (zif->es_info.lid) + vty_out(vty, " evpn mh es-id %u\n", zif->es_info.lid); + + if (!is_zero_mac(&zif->es_info.sysmac)) + vty_out(vty, " evpn mh es-sys-mac %s\n", + prefix_mac2str(&zif->es_info.sysmac, + buf, sizeof(buf))); + return 0; +} + +#ifndef VTYSH_EXTRACT_PL +#include "zebra/zebra_evpn_mh_clippy.c" +#endif +/* CLI for setting up sysmac part of ESI on an access port */ +DEFPY(zebra_evpn_es_sys_mac, + zebra_evpn_es_sys_mac_cmd, + "[no$no] evpn mh es-sys-mac [X:X:X:X:X:X$mac]", + NO_STR + "EVPN\n" + EVPN_MH_VTY_STR + "Ethernet segment system MAC\n" + MAC_STR +) +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct zebra_if *zif; + int ret = 0; + + zif = ifp->info; + + if (no) { + static struct ethaddr zero_mac; + + ret = zebra_evpn_es_sys_mac_update(zif, &zero_mac); + if (ret == -1) { + vty_out(vty, "%%Failed to clear ES sysmac\n"); + return CMD_WARNING; + } + } else { + + if (!zebra_evpn_is_if_es_capable(zif)) { + vty_out(vty, + "%%ESI cannot be associated with this interface type\n"); + return CMD_WARNING; + } + + if (!mac || is_zero_mac(&mac->eth_addr)) { + vty_out(vty, "%%ES sysmac value is invalid\n"); + return CMD_WARNING; + } + + ret = zebra_evpn_es_sys_mac_update(zif, &mac->eth_addr); + if (ret == -1) { + vty_out(vty, "%%ESI already exists on a different interface\n"); + return CMD_WARNING; + } + } + return CMD_SUCCESS; +} + +/* CLI for setting up local-ID part of ESI on an access port */ +DEFPY(zebra_evpn_es_id, + zebra_evpn_es_id_cmd, + "[no$no] evpn mh es-id [(1-16777215)$es_lid]", + NO_STR + "EVPN\n" + EVPN_MH_VTY_STR + "Ethernet segment local identifier\n" + "ID\n" +) +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct zebra_if *zif; + int ret; + + zif = ifp->info; + + if (no) { + ret = zebra_evpn_es_lid_update(zif, 0); + if (ret == -1) { + vty_out(vty, "%%Failed to clear ES local id\n"); + return CMD_WARNING; + } + } else { + if (!zebra_evpn_is_if_es_capable(zif)) { + vty_out(vty, + "%%ESI cannot be associated with this interface type\n"); + return CMD_WARNING; + } + + if (!es_lid) { + vty_out(vty, "%%Specify local ES ID\n"); + return CMD_WARNING; + } + ret = zebra_evpn_es_lid_update(zif, es_lid); + if (ret == -1) { + vty_out(vty, + "%%ESI already exists on a different interface\n"); + return CMD_WARNING; + } + } + return CMD_SUCCESS; +} + +/*****************************************************************************/ +/* A base L2-VNI is maintained to derive parameters such as ES originator-IP. + * XXX: once single vxlan device model becomes available this will not be + * necessary + */ +/* called when a new vni is added or becomes oper up or becomes a bridge port */ +void zebra_evpn_es_set_base_evpn(zebra_evpn_t *zevpn) +{ + struct listnode *node; + struct zebra_evpn_es *es; + + if (zmh_info->es_base_evpn) { + if (zmh_info->es_base_evpn != zevpn) { + /* unrelated EVPN; ignore it */ + return; + } + /* check if the local vtep-ip has changed */ + } else { + /* check if the EVPN can be used as base EVPN */ + if (!zebra_evpn_send_to_client_ok(zevpn)) + return; + + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("es base vni set to %d", + zevpn->vni); + zmh_info->es_base_evpn = zevpn; + } + + /* update local VTEP-IP */ + if (zmh_info->es_originator_ip.s_addr == + zmh_info->es_base_evpn->local_vtep_ip.s_addr) + return; + + zmh_info->es_originator_ip.s_addr = + zmh_info->es_base_evpn->local_vtep_ip.s_addr; + + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("es originator ip set to %s", + inet_ntoa(zmh_info->es_base_evpn->local_vtep_ip)); + + /* if originator ip changes we need to update bgp */ + for (ALL_LIST_ELEMENTS_RO(zmh_info->local_es_list, node, es)) { + if (es->flags & ZEBRA_EVPNES_READY_FOR_BGP) + zebra_evpn_es_send_add_to_client(es); + else + zebra_evpn_es_re_eval_send_to_client(es, + true /* es_evi_re_reval */); + } +} + +/* called when a vni is removed or becomes oper down or is removed from a + * bridge + */ +void zebra_evpn_es_clear_base_evpn(zebra_evpn_t *zevpn) +{ + struct listnode *node; + struct zebra_evpn_es *es; + + if (zmh_info->es_base_evpn != zevpn) + return; + + zmh_info->es_base_evpn = NULL; + /* lost current base EVPN; try to find a new one */ + zebra_evpn_es_get_one_base_evpn(); + + /* couldn't locate an eligible base evpn */ + if (!zmh_info->es_base_evpn && zmh_info->es_originator_ip.s_addr) { + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("es originator ip cleared"); + + zmh_info->es_originator_ip.s_addr = 0; + /* lost originator ip */ + for (ALL_LIST_ELEMENTS_RO(zmh_info->local_es_list, node, es)) { + zebra_evpn_es_re_eval_send_to_client(es, + true /* es_evi_re_reval */); + } + } +} + +/* Locate an "eligible" L2-VNI to follow */ +static int zebra_evpn_es_get_one_base_evpn_cb(struct hash_bucket *b, void *data) +{ + zebra_evpn_t *zevpn = b->data; + + zebra_evpn_es_set_base_evpn(zevpn); + + if (zmh_info->es_base_evpn) + return HASHWALK_ABORT; + + return HASHWALK_CONTINUE; +} + +/* locate a base_evpn to follow for the purposes of common params like + * originator IP + */ +static void zebra_evpn_es_get_one_base_evpn(void) +{ + struct zebra_vrf *zvrf; + + zvrf = zebra_vrf_get_evpn(); + hash_walk(zvrf->evpn_table, zebra_evpn_es_get_one_base_evpn_cb, NULL); +} + +/*****************************************************************************/ +void zebra_evpn_mh_config_write(struct vty *vty) +{ + if (zmh_info->mac_hold_time != EVPN_MH_MAC_HOLD_TIME_DEF) + vty_out(vty, "evpn mh mac-holdtime %ld\n", + zmh_info->mac_hold_time); + + if (zmh_info->neigh_hold_time != EVPN_MH_NEIGH_HOLD_TIME_DEF) + vty_out(vty, "evpn mh neigh-holdtime %ld\n", + zmh_info->neigh_hold_time); +} + +int zebra_evpn_mh_neigh_holdtime_update(struct vty *vty, + uint32_t duration, bool set_default) +{ + if (set_default) + duration = EVPN_MH_NEIGH_HOLD_TIME_DEF; + + zmh_info->neigh_hold_time = duration; + + return 0; +} + +int zebra_evpn_mh_mac_holdtime_update(struct vty *vty, + uint32_t duration, bool set_default) +{ + if (set_default) + duration = EVPN_MH_MAC_HOLD_TIME_DEF; + + zmh_info->mac_hold_time = duration; + + return 0; +} + +void zebra_evpn_interface_init(void) +{ + install_element(INTERFACE_NODE, &zebra_evpn_es_id_cmd); + install_element(INTERFACE_NODE, &zebra_evpn_es_sys_mac_cmd); +} + +void zebra_evpn_mh_init(void) +{ + zrouter.mh_info = XCALLOC(MTYPE_ZMH_INFO, sizeof(*zrouter.mh_info)); + + zmh_info->mac_hold_time = EVPN_MH_MAC_HOLD_TIME_DEF; + zmh_info->neigh_hold_time = EVPN_MH_NEIGH_HOLD_TIME_DEF; + /* setup ES tables */ + RB_INIT(zebra_es_rb_head, &zmh_info->es_rb_tree); + zmh_info->local_es_list = list_new(); + listset_app_node_mem(zmh_info->local_es_list); + + bf_init(zmh_info->nh_id_bitmap, EVPN_NH_ID_MAX); + bf_assign_zero_index(zmh_info->nh_id_bitmap); + + /* setup broadcast domain tables */ + zmh_info->evpn_vlan_table = hash_create(zebra_evpn_acc_vl_hash_keymake, + zebra_evpn_acc_vl_cmp, "access VLAN hash table"); +} + +void zebra_evpn_mh_terminate(void) +{ + list_delete(&zmh_info->local_es_list); + + hash_iterate(zmh_info->evpn_vlan_table, + zebra_evpn_acc_vl_cleanup_all, NULL); + hash_free(zmh_info->evpn_vlan_table); +} diff --git a/zebra/zebra_evpn_mh.h b/zebra/zebra_evpn_mh.h new file mode 100644 index 0000000000..ed62677e3b --- /dev/null +++ b/zebra/zebra_evpn_mh.h @@ -0,0 +1,239 @@ +/* + * Zebra EVPN MH Data structures and definitions + * + * Copyright (C) 2019 Cumulus Networks, Inc. + * Anuradha Karuppiah + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef _ZEBRA_EVPN_MH_H +#define _ZEBRA_EVPN_MH_H + +#include + +#include "if.h" +#include "linklist.h" +#include "bitfield.h" +#include "zebra_vxlan.h" +#include "zebra_vxlan_private.h" + +#define EVPN_MH_VTY_STR "Multihoming\n" + +/* Ethernet Segment entry - + * - Local and remote ESs are maintained in a global RB tree, + * zmh_info->es_rb_tree using ESI as key + * - Local ESs are added via zebra config (ZEBRA_EVPNES_LOCAL) when an + * access port is associated with an ES-ID + * - Remotes ESs are added by BGP based on received/remote EAD/Type-1 routes + * (ZEBRA_EVPNES_REMOTE) + * - An ES can be simulatenously LOCAL and REMOTE; infact all LOCAL ESs are + * expected to have REMOTE ES peers. + */ +struct zebra_evpn_es { + esi_t esi; + char esi_str[ESI_STR_LEN]; + + /* ES flags */ + uint32_t flags; +#define ZEBRA_EVPNES_LOCAL (1 << 0) /* configured in zebra */ +#define ZEBRA_EVPNES_REMOTE (1 << 1) /* added by bgp */ +#define ZEBRA_EVPNES_OPER_UP (1 << 2) /* es->ifp is oper-up */ +#define ZEBRA_EVPNES_READY_FOR_BGP (1 << 3) /* ready to be sent to BGP */ +#define ZEBRA_EVPNES_NHG_ACTIVE (1 << 4) /* NHG has been installed */ + + /* memory used for adding the es to zmh_info->es_rb_tree */ + RB_ENTRY(zebra_evpn_es) rb_node; + + /* [EVPNES_LOCAL] memory used for linking the es to + * zmh_info->local_es_list + */ + struct listnode local_es_listnode; + + /* [EVPNES_LOCAL] corresponding interface */ + struct zebra_if *zif; + + /* list of ES-EVIs associated with the ES */ + struct list *es_evi_list; + + /* [!EVPNES_LOCAL] List of remote VTEPs (zebra_evpn_es_vtep) */ + struct list *es_vtep_list; + + /* list of zebra_mac entries using this ES as destination */ + struct list *mac_list; + + /* Nexthop group id */ + uint32_t nhg_id; +}; +RB_HEAD(zebra_es_rb_head, zebra_evpn_es); +RB_PROTOTYPE(zebra_es_rb_head, zebra_evpn_es, rb_node, zebra_es_rb_cmp); + +/* ES per-EVI info + * - ES-EVIs are maintained per-EVPN (vni->es_evi_rb_tree) + * - Local ES-EVIs are linked to per-EVPN list for quick access + * - Although some infrastucture is present for remote ES-EVIs, currently + * BGP does NOT send remote ES-EVIs to zebra. This may change in the + * future (but must be changed thoughtfully and only if needed as ES-EVI + * can get prolific and come in the way of rapid failovers) + */ +struct zebra_evpn_es_evi { + struct zebra_evpn_es *es; + zebra_evpn_t *zevpn; + + /* ES-EVI flags */ + uint32_t flags; + /* local ES-EVI */ +#define ZEBRA_EVPNES_EVI_LOCAL (1 << 0) /* created by zebra */ +#define ZEBRA_EVPNES_EVI_READY_FOR_BGP (1 << 1) /* ready to be sent to BGP */ + + /* memory used for adding the es_evi to + * es_evi->zevpn->es_evi_rb_tree + */ + RB_ENTRY(zebra_evpn_es_evi) rb_node; + /* memory used for linking the es_evi to + * es_evi->zevpn->local_es_evi_list + */ + struct listnode l2vni_listnode; + /* memory used for linking the es_evi to + * es_evi->es->es_evi_list + */ + struct listnode es_listnode; +}; + +/* PE attached to an ES */ +struct zebra_evpn_es_vtep { + struct zebra_evpn_es *es; /* parent ES */ + struct in_addr vtep_ip; + + /* memory used for adding the entry to es->es_vtep_list */ + struct listnode es_listnode; + + /* MAC nexthop */ + uint32_t nh_id; + + /* XXX - maintain a backpointer to zebra_vtep_t */ +}; + +/* Local/access-side broadcast domain - zebra_evpn_access_bd is added to - + * zrouter->evpn_vlan_table (for VLAN aware bridges) OR + * zrouter->evpn_bridge_table (for VLAN unaware bridges) + * XXX - support for VLAN unaware bridges is yet to be flushed out + */ +struct zebra_evpn_access_bd { + vlanid_t vid; + + struct zebra_if *vxlan_zif; /* vxlan device */ + /* list of members associated with the BD i.e. (potential) ESs */ + struct list *mbr_zifs; + /* presence of zevpn activates the EVI on all the ESs in mbr_zifs */ + zebra_evpn_t *zevpn; +}; + +/* multihoming information stored in zrouter */ +#define zmh_info (zrouter.mh_info) +struct zebra_evpn_mh_info { + /* RB tree of Ethernet segments (used for EVPN-MH) */ + struct zebra_es_rb_head es_rb_tree; + /* List of local ESs */ + struct list *local_es_list; + + /* EVPN MH broadcast domains indexed by the VID */ + struct hash *evpn_vlan_table; + + /* A base L2-VNI is maintained to derive parameters such as + * ES originator-IP. + * XXX: once single vxlan device model becomes available this will + * not be necessary + */ + zebra_evpn_t *es_base_evpn; + struct in_addr es_originator_ip; + + /* L2 NH and NHG ids - + * Most significant 8 bits is type. Lower 24 bits is the value + * allocated from the nh_id_bitmap. + */ + bitfield_t nh_id_bitmap; +#define EVPN_NH_ID_MAX (16*1024) +#define EVPN_NH_ID_VAL_MASK 0xffffff +#define EVPN_NH_ID_TYPE_POS 24 +/* The purpose of using different types for NHG and NH is NOT to manage the + * id space separately. It is simply to make debugging easier. + */ +#define EVPN_NH_ID_TYPE_BIT (1 << EVPN_NH_ID_TYPE_POS) +#define EVPN_NHG_ID_TYPE_BIT (2 << EVPN_NH_ID_TYPE_POS) + + /* XXX - re-visit the default hold timer value */ +#define EVPN_MH_MAC_HOLD_TIME_DEF (18 * 60) + long mac_hold_time; +#define EVPN_MH_NEIGH_HOLD_TIME_DEF (18 * 60) + long neigh_hold_time; +}; + +static inline bool zebra_evpn_mac_is_es_local(zebra_mac_t *mac) +{ + return mac->es && (mac->es->flags & ZEBRA_EVPNES_LOCAL); +} + +/* Returns true if the id is of L2-NHG or L2-NH type */ +static inline bool zebra_evpn_mh_is_fdb_nh(uint32_t id) +{ + return ((id & EVPN_NHG_ID_TYPE_BIT) || + (id & EVPN_NH_ID_TYPE_BIT)); +} + +/*****************************************************************************/ +extern esi_t *zero_esi; +extern void zebra_evpn_mh_init(void); +extern void zebra_evpn_mh_terminate(void); +extern bool zebra_evpn_is_if_es_capable(struct zebra_if *zif); +extern void zebra_evpn_if_init(struct zebra_if *zif); +extern void zebra_evpn_if_cleanup(struct zebra_if *zif); +extern void zebra_evpn_evpn_es_init(zebra_evpn_t *zevpn); +extern void zebra_evpn_evpn_es_cleanup(zebra_evpn_t *zevpn); +extern void zebra_evpn_vxl_evpn_set(struct zebra_if *zif, zebra_evpn_t *zevpn, + bool set); +extern void zebra_evpn_es_set_base_evpn(zebra_evpn_t *zevpn); +extern void zebra_evpn_es_clear_base_evpn(zebra_evpn_t *zevpn); +extern void zebra_evpn_vl_vxl_ref(uint16_t vid, struct zebra_if *vxlan_zif); +extern void zebra_evpn_vl_vxl_deref(uint16_t vid, struct zebra_if *vxlan_zif); +extern void zebra_evpn_vl_mbr_ref(uint16_t vid, struct zebra_if *zif); +extern void zebra_evpn_vl_mbr_deref(uint16_t vid, struct zebra_if *zif); +extern void zebra_evpn_es_send_all_to_client(bool add); +extern void zebra_evpn_es_if_oper_state_change(struct zebra_if *zif, bool up); +extern void zebra_evpn_es_show(struct vty *vty, bool uj); +extern void zebra_evpn_es_show_detail(struct vty *vty, bool uj); +extern void zebra_evpn_es_show_esi(struct vty *vty, bool uj, esi_t *esi); +extern void zebra_evpn_update_all_es(zebra_evpn_t *zevpn); +extern void zebra_evpn_proc_remote_es(ZAPI_HANDLER_ARGS); +extern void zebra_evpn_es_evi_show(struct vty *vty, bool uj, int detail); +extern void zebra_evpn_es_evi_show_vni(struct vty *vty, bool uj, + vni_t vni, int detail); +extern void zebra_evpn_es_mac_deref_entry(zebra_mac_t *mac); +extern bool zebra_evpn_es_mac_ref_entry(zebra_mac_t *mac, + struct zebra_evpn_es *es); +extern bool zebra_evpn_es_mac_ref(zebra_mac_t *mac, esi_t *esi); +extern struct zebra_evpn_es *zebra_evpn_es_find(esi_t *esi); +extern void zebra_evpn_interface_init(void); +extern int zebra_evpn_mh_if_write(struct vty *vty, struct interface *ifp); +extern void zebra_evpn_acc_vl_show(struct vty *vty, bool uj); +extern void zebra_evpn_acc_vl_show_detail(struct vty *vty, bool uj); +extern void zebra_evpn_acc_vl_show_vid(struct vty *vty, bool uj, vlanid_t vid); +extern void zebra_evpn_if_es_print(struct vty *vty, struct zebra_if *zif); +extern void zebra_evpn_es_cleanup(void); +extern int zebra_evpn_mh_mac_holdtime_update(struct vty *vty, + uint32_t duration, bool set_default); +void zebra_evpn_mh_config_write(struct vty *vty); +int zebra_evpn_mh_neigh_holdtime_update(struct vty *vty, + uint32_t duration, bool set_default); + +#endif /* _ZEBRA_EVPN_MH_H */ diff --git a/zebra/zebra_evpn_neigh.c b/zebra/zebra_evpn_neigh.c new file mode 100644 index 0000000000..6a76a475e6 --- /dev/null +++ b/zebra/zebra_evpn_neigh.c @@ -0,0 +1,2457 @@ +/* + * Zebra EVPN Neighbor code + * Copyright (C) 2016, 2017 Cumulus Networks, Inc. + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include + +#include "hash.h" +#include "interface.h" +#include "jhash.h" +#include "memory.h" +#include "prefix.h" +#include "vlan.h" +#include "json.h" + +#include "zebra/zserv.h" +#include "zebra/debug.h" +#include "zebra/zebra_router.h" +#include "zebra/rt.h" +#include "zebra/zebra_memory.h" +#include "zebra/zebra_errors.h" +#include "zebra/zebra_vrf.h" +#include "zebra/zebra_evpn.h" +#include "zebra/zebra_evpn_mh.h" +#include "zebra/zebra_evpn_neigh.h" +#include "zebra/zebra_evpn_mac.h" + +DEFINE_MTYPE_STATIC(ZEBRA, NEIGH, "EVI Neighbor"); + +/* + * Make hash key for neighbors. + */ +static unsigned int neigh_hash_keymake(const void *p) +{ + const zebra_neigh_t *n = p; + const struct ipaddr *ip = &n->ip; + + if (IS_IPADDR_V4(ip)) + return jhash_1word(ip->ipaddr_v4.s_addr, 0); + + return jhash2(ip->ipaddr_v6.s6_addr32, + array_size(ip->ipaddr_v6.s6_addr32), 0); +} + +/* + * Compare two neighbor hash structures. + */ +static bool neigh_cmp(const void *p1, const void *p2) +{ + const zebra_neigh_t *n1 = p1; + const zebra_neigh_t *n2 = p2; + + if (n1 == NULL && n2 == NULL) + return true; + + if (n1 == NULL || n2 == NULL) + return false; + + return (memcmp(&n1->ip, &n2->ip, sizeof(struct ipaddr)) == 0); +} + +int neigh_list_cmp(void *p1, void *p2) +{ + const zebra_neigh_t *n1 = p1; + const zebra_neigh_t *n2 = p2; + + return memcmp(&n1->ip, &n2->ip, sizeof(struct ipaddr)); +} + +struct hash *zebra_neigh_db_create(const char *desc) +{ + return hash_create(neigh_hash_keymake, neigh_cmp, desc); +} + +uint32_t num_dup_detected_neighs(zebra_evpn_t *zevpn) +{ + unsigned int i; + uint32_t num_neighs = 0; + struct hash *hash; + struct hash_bucket *hb; + zebra_neigh_t *nbr; + + hash = zevpn->neigh_table; + if (!hash) + return num_neighs; + for (i = 0; i < hash->size; i++) { + for (hb = hash->index[i]; hb; hb = hb->next) { + nbr = (zebra_neigh_t *)hb->data; + if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) + num_neighs++; + } + } + + return num_neighs; +} + +/* + * Helper function to determine maximum width of neighbor IP address for + * display - just because we're dealing with IPv6 addresses that can + * widely vary. + */ +void zebra_evpn_find_neigh_addr_width(struct hash_bucket *bucket, void *ctxt) +{ + zebra_neigh_t *n; + char buf[INET6_ADDRSTRLEN]; + struct neigh_walk_ctx *wctx = ctxt; + int width; + + n = (zebra_neigh_t *)bucket->data; + + ipaddr2str(&n->ip, buf, sizeof(buf)); + width = strlen(buf); + if (width > wctx->addr_width) + wctx->addr_width = width; +} + +/* + * Count of remote neighbors referencing this MAC. + */ +int remote_neigh_count(zebra_mac_t *zmac) +{ + zebra_neigh_t *n = NULL; + struct listnode *node = NULL; + int count = 0; + + for (ALL_LIST_ELEMENTS_RO(zmac->neigh_list, node, n)) { + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) + count++; + } + + return count; +} + +/* + * Install remote neighbor into the kernel. + */ +int zebra_evpn_rem_neigh_install(zebra_evpn_t *zevpn, zebra_neigh_t *n, + bool was_static) +{ + struct interface *vlan_if; + int flags; + int ret = 0; + + if (!(n->flags & ZEBRA_NEIGH_REMOTE)) + return 0; + + vlan_if = zevpn_map_to_svi(zevpn); + if (!vlan_if) + return -1; + + flags = DPLANE_NTF_EXT_LEARNED; + if (n->flags & ZEBRA_NEIGH_ROUTER_FLAG) + flags |= DPLANE_NTF_ROUTER; + ZEBRA_NEIGH_SET_ACTIVE(n); + + dplane_rem_neigh_add(vlan_if, &n->ip, &n->emac, flags, was_static); + + return ret; +} + +/* + * Install neighbor hash entry - called upon access VLAN change. + */ +void zebra_evpn_install_neigh_hash(struct hash_bucket *bucket, void *ctxt) +{ + zebra_neigh_t *n; + struct neigh_walk_ctx *wctx = ctxt; + + n = (zebra_neigh_t *)bucket->data; + + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) + zebra_evpn_rem_neigh_install(wctx->zevpn, n, + false /*was_static*/); +} + +/* + * Callback to allocate neighbor hash entry. + */ +static void *zebra_evpn_neigh_alloc(void *p) +{ + const zebra_neigh_t *tmp_n = p; + zebra_neigh_t *n; + + n = XCALLOC(MTYPE_NEIGH, sizeof(zebra_neigh_t)); + *n = *tmp_n; + + return ((void *)n); +} + +static void zebra_evpn_local_neigh_ref_mac(zebra_neigh_t *n, + struct ethaddr *macaddr, + zebra_mac_t *mac, + bool send_mac_update) +{ + char macbuf[ETHER_ADDR_STRLEN]; + char ipbuf[INET6_ADDRSTRLEN]; + bool old_static; + bool new_static; + + memcpy(&n->emac, macaddr, ETH_ALEN); + n->mac = mac; + + /* Link to new MAC */ + if (!mac) + return; + + listnode_add_sort(mac->neigh_list, n); + if (n->flags & ZEBRA_NEIGH_ALL_PEER_FLAGS) { + old_static = zebra_evpn_mac_is_static(mac); + ++mac->sync_neigh_cnt; + new_static = zebra_evpn_mac_is_static(mac); + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug( + "sync-neigh ref mac vni %u ip %s mac %s ref %d", + n->zevpn->vni, + ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)), + prefix_mac2str(&n->emac, macbuf, + sizeof(macbuf)), + mac->sync_neigh_cnt); + if ((old_static != new_static) && send_mac_update) + /* program the local mac in the kernel */ + zebra_evpn_sync_mac_dp_install( + mac, false /*set_inactive*/, + false /*force_clear_static*/, __func__); + } +} + +/* sync-path that is active on an ES peer */ +static void zebra_evpn_sync_neigh_dp_install(zebra_neigh_t *n, + bool set_inactive, + bool force_clear_static, + const char *caller) +{ + char macbuf[ETHER_ADDR_STRLEN]; + char ipbuf[INET6_ADDRSTRLEN]; + struct zebra_ns *zns; + struct interface *ifp; + bool set_static; + bool set_router; + + zns = zebra_ns_lookup(NS_DEFAULT); + ifp = if_lookup_by_index_per_ns(zns, n->ifindex); + if (!ifp) { + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug( + "%s: dp-install sync-neigh vni %u ip %s mac %s if %d f 0x%x skipped", + caller, n->zevpn->vni, + ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)), + prefix_mac2str(&n->emac, macbuf, + sizeof(macbuf)), + n->ifindex, n->flags); + return; + } + + if (force_clear_static) + set_static = false; + else + set_static = zebra_evpn_neigh_is_static(n); + + set_router = !!CHECK_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); + + /* XXX - this will change post integration with the new kernel */ + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL_INACTIVE)) + set_inactive = true; + + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug( + "%s: dp-install sync-neigh vni %u ip %s mac %s if %s(%d) f 0x%x%s%s%s", + caller, n->zevpn->vni, + ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)), + prefix_mac2str(&n->emac, macbuf, sizeof(macbuf)), + ifp->name, n->ifindex, n->flags, + set_router ? " router" : "", + set_static ? " static" : "", + set_inactive ? " inactive" : ""); + dplane_local_neigh_add(ifp, &n->ip, &n->emac, set_router, set_static, + set_inactive); +} + +/* + * Inform BGP about local neighbor addition. + */ +int zebra_evpn_neigh_send_add_to_client(vni_t vni, struct ipaddr *ip, + struct ethaddr *macaddr, + zebra_mac_t *zmac, uint32_t neigh_flags, + uint32_t seq) +{ + uint8_t flags = 0; + + if (CHECK_FLAG(neigh_flags, ZEBRA_NEIGH_LOCAL_INACTIVE)) { + /* host reachability has not been verified locally */ + + /* if no ES peer is claiming reachability we can't advertise + * the entry + */ + if (!CHECK_FLAG(neigh_flags, ZEBRA_NEIGH_ES_PEER_ACTIVE)) + return 0; + + /* ES peers are claiming reachability; we will + * advertise the entry but with a proxy flag + */ + SET_FLAG(flags, ZEBRA_MACIP_TYPE_PROXY_ADVERT); + } + + if (CHECK_FLAG(neigh_flags, ZEBRA_NEIGH_DEF_GW)) + SET_FLAG(flags, ZEBRA_MACIP_TYPE_GW); + /* Set router flag (R-bit) based on local neigh entry add */ + if (CHECK_FLAG(neigh_flags, ZEBRA_NEIGH_ROUTER_FLAG)) + SET_FLAG(flags, ZEBRA_MACIP_TYPE_ROUTER_FLAG); + if (CHECK_FLAG(neigh_flags, ZEBRA_NEIGH_SVI_IP)) + SET_FLAG(flags, ZEBRA_MACIP_TYPE_SVI_IP); + + return zebra_evpn_macip_send_msg_to_client( + vni, macaddr, ip, flags, seq, ZEBRA_NEIGH_ACTIVE, + zmac ? zmac->es : NULL, ZEBRA_MACIP_ADD); +} + +/* + * Inform BGP about local neighbor deletion. + */ +int zebra_evpn_neigh_send_del_to_client(vni_t vni, struct ipaddr *ip, + struct ethaddr *macaddr, uint32_t flags, + int state, bool force) +{ + if (!force) { + if (CHECK_FLAG(flags, ZEBRA_NEIGH_LOCAL_INACTIVE) + && !CHECK_FLAG(flags, ZEBRA_NEIGH_ES_PEER_ACTIVE)) + /* the neigh was not advertised - nothing to delete */ + return 0; + } + + return zebra_evpn_macip_send_msg_to_client( + vni, macaddr, ip, flags, 0, state, NULL, ZEBRA_MACIP_DEL); +} + +static void zebra_evpn_neigh_send_add_del_to_client(zebra_neigh_t *n, + bool old_bgp_ready, + bool new_bgp_ready) +{ + if (new_bgp_ready) + zebra_evpn_neigh_send_add_to_client(n->zevpn->vni, &n->ip, + &n->emac, n->mac, n->flags, + n->loc_seq); + else if (old_bgp_ready) + zebra_evpn_neigh_send_del_to_client(n->zevpn->vni, &n->ip, + &n->emac, n->flags, + n->state, true /*force*/); +} + +/* if the static flag associated with the neigh changes we need + * to update the sync-neigh references against the MAC + * and inform the dataplane about the static flag changes. + */ +void zebra_evpn_sync_neigh_static_chg(zebra_neigh_t *n, bool old_n_static, + bool new_n_static, bool defer_n_dp, + bool defer_mac_dp, const char *caller) +{ + zebra_mac_t *mac = n->mac; + bool old_mac_static; + bool new_mac_static; + char macbuf[ETHER_ADDR_STRLEN]; + char ipbuf[INET6_ADDRSTRLEN]; + + if (old_n_static == new_n_static) + return; + + /* update the neigh sync references in the dataplane. if + * the neigh is in the middle of updates the caller can + * request for a defer + */ + if (!defer_n_dp) + zebra_evpn_sync_neigh_dp_install(n, false /* set_inactive */, + false /* force_clear_static */, + __func__); + + if (!mac) + return; + + /* update the mac sync ref cnt */ + old_mac_static = zebra_evpn_mac_is_static(mac); + if (new_n_static) { + ++mac->sync_neigh_cnt; + } else if (old_n_static) { + if (mac->sync_neigh_cnt) + --mac->sync_neigh_cnt; + } + new_mac_static = zebra_evpn_mac_is_static(mac); + + /* update the mac sync references in the dataplane */ + if ((old_mac_static != new_mac_static) && !defer_mac_dp) + zebra_evpn_sync_mac_dp_install(mac, false /* set_inactive */, + false /* force_clear_static */, + __func__); + + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug( + "sync-neigh ref-chg vni %u ip %s mac %s f 0x%x %d%s%s%s%s by %s", + n->zevpn->vni, ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)), + prefix_mac2str(&n->emac, macbuf, sizeof(macbuf)), + n->flags, mac->sync_neigh_cnt, + old_n_static ? " old_n_static" : "", + new_n_static ? " new_n_static" : "", + old_mac_static ? " old_mac_static" : "", + new_mac_static ? " new_mac_static" : "", caller); +} + +/* Neigh hold timer is used to age out peer-active flag. + * + * During this wait time we expect the dataplane component or an + * external neighmgr daemon to probe existing hosts to independently + * establish their presence on the ES. + */ +static int zebra_evpn_neigh_hold_exp_cb(struct thread *t) +{ + zebra_neigh_t *n; + bool old_bgp_ready; + bool new_bgp_ready; + bool old_n_static; + bool new_n_static; + char macbuf[ETHER_ADDR_STRLEN]; + char ipbuf[INET6_ADDRSTRLEN]; + + n = THREAD_ARG(t); + /* the purpose of the hold timer is to age out the peer-active + * flag + */ + if (!CHECK_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_ACTIVE)) + return 0; + + old_bgp_ready = zebra_evpn_neigh_is_ready_for_bgp(n); + old_n_static = zebra_evpn_neigh_is_static(n); + UNSET_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_ACTIVE); + new_bgp_ready = zebra_evpn_neigh_is_ready_for_bgp(n); + new_n_static = zebra_evpn_neigh_is_static(n); + + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug("sync-neigh vni %u ip %s mac %s 0x%x hold expired", + n->zevpn->vni, + ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)), + prefix_mac2str(&n->emac, macbuf, sizeof(macbuf)), + n->flags); + + /* re-program the local neigh in the dataplane if the neigh is no + * longer static + */ + if (old_n_static != new_n_static) + zebra_evpn_sync_neigh_static_chg( + n, old_n_static, new_n_static, false /*defer_n_dp*/, + false /*defer_mac_dp*/, __func__); + + /* inform bgp if needed */ + if (old_bgp_ready != new_bgp_ready) + zebra_evpn_neigh_send_add_del_to_client(n, old_bgp_ready, + new_bgp_ready); + + return 0; +} + +static inline void zebra_evpn_neigh_start_hold_timer(zebra_neigh_t *n) +{ + char macbuf[ETHER_ADDR_STRLEN]; + char ipbuf[INET6_ADDRSTRLEN]; + + if (n->hold_timer) + return; + + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug("sync-neigh vni %u ip %s mac %s 0x%x hold start", + n->zevpn->vni, + ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)), + prefix_mac2str(&n->emac, macbuf, sizeof(macbuf)), + n->flags); + thread_add_timer(zrouter.master, zebra_evpn_neigh_hold_exp_cb, n, + zmh_info->neigh_hold_time, &n->hold_timer); +} + +static void zebra_evpn_local_neigh_deref_mac(zebra_neigh_t *n, + bool send_mac_update) +{ + zebra_mac_t *mac = n->mac; + zebra_evpn_t *zevpn = n->zevpn; + char macbuf[ETHER_ADDR_STRLEN]; + char ipbuf[INET6_ADDRSTRLEN]; + bool old_static; + bool new_static; + + n->mac = NULL; + if (!mac) + return; + + if ((n->flags & ZEBRA_NEIGH_ALL_PEER_FLAGS) && mac->sync_neigh_cnt) { + old_static = zebra_evpn_mac_is_static(mac); + --mac->sync_neigh_cnt; + new_static = zebra_evpn_mac_is_static(mac); + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug( + "sync-neigh deref mac vni %u ip %s mac %s ref %d", + n->zevpn->vni, + ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)), + prefix_mac2str(&n->emac, macbuf, + sizeof(macbuf)), + mac->sync_neigh_cnt); + if ((old_static != new_static) && send_mac_update) + /* program the local mac in the kernel */ + zebra_evpn_sync_mac_dp_install( + mac, false /* set_inactive */, + false /* force_clear_static */, __func__); + } + + listnode_delete(mac->neigh_list, n); + zebra_evpn_deref_ip2mac(zevpn, mac); +} + +bool zebra_evpn_neigh_is_bgp_seq_ok(zebra_evpn_t *zevpn, zebra_neigh_t *n, + struct ethaddr *macaddr, uint32_t seq) +{ + char macbuf[ETHER_ADDR_STRLEN]; + char ipbuf[INET6_ADDRSTRLEN]; + uint32_t tmp_seq; + + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) + tmp_seq = n->loc_seq; + else + tmp_seq = n->rem_seq; + + if (seq < tmp_seq) { + /* if the neigh was never advertised to bgp we must accept + * whatever sequence number bgp sends + * XXX - check with Vivek + */ + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL) + && !zebra_evpn_neigh_is_ready_for_bgp(n)) { + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug( + "sync-macip accept vni %u mac %s IP %s lower seq %u f 0x%x", + zevpn->vni, + prefix_mac2str(macaddr, macbuf, + sizeof(macbuf)), + ipaddr2str(&n->ip, ipbuf, + sizeof(ipbuf)), + tmp_seq, n->flags); + return true; + } + + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug( + "sync-macip ignore vni %u mac %s IP %s as existing has higher seq %u f 0x%x", + zevpn->vni, + prefix_mac2str(macaddr, macbuf, sizeof(macbuf)), + ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)), + tmp_seq, n->flags); + return false; + } + + return true; +} + +/* + * Add neighbor entry. + */ +static zebra_neigh_t *zebra_evpn_neigh_add(zebra_evpn_t *zevpn, + struct ipaddr *ip, + struct ethaddr *mac, + zebra_mac_t *zmac, uint32_t n_flags) +{ + zebra_neigh_t tmp_n; + zebra_neigh_t *n = NULL; + + memset(&tmp_n, 0, sizeof(zebra_neigh_t)); + memcpy(&tmp_n.ip, ip, sizeof(struct ipaddr)); + n = hash_get(zevpn->neigh_table, &tmp_n, zebra_evpn_neigh_alloc); + assert(n); + + n->state = ZEBRA_NEIGH_INACTIVE; + n->zevpn = zevpn; + n->dad_ip_auto_recovery_timer = NULL; + n->flags = n_flags; + + if (!zmac) + zmac = zebra_evpn_mac_lookup(zevpn, mac); + zebra_evpn_local_neigh_ref_mac(n, mac, zmac, + false /* send_mac_update */); + + return n; +} + +/* + * Delete neighbor entry. + */ +int zebra_evpn_neigh_del(zebra_evpn_t *zevpn, zebra_neigh_t *n) +{ + zebra_neigh_t *tmp_n; + + if (n->mac) + listnode_delete(n->mac->neigh_list, n); + + /* Cancel auto recovery */ + THREAD_OFF(n->dad_ip_auto_recovery_timer); + + /* Cancel proxy hold timer */ + zebra_evpn_neigh_stop_hold_timer(n); + + /* Free the VNI hash entry and allocated memory. */ + tmp_n = hash_release(zevpn->neigh_table, n); + XFREE(MTYPE_NEIGH, tmp_n); + + return 0; +} + +void zebra_evpn_sync_neigh_del(zebra_neigh_t *n) +{ + bool old_n_static; + bool new_n_static; + char macbuf[ETHER_ADDR_STRLEN]; + char ipbuf[INET6_ADDRSTRLEN]; + + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug("sync-neigh del vni %u ip %s mac %s f 0x%x", + n->zevpn->vni, + ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)), + prefix_mac2str(&n->emac, macbuf, sizeof(macbuf)), + n->flags); + + old_n_static = zebra_evpn_neigh_is_static(n); + UNSET_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_PROXY); + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_ACTIVE)) + zebra_evpn_neigh_start_hold_timer(n); + new_n_static = zebra_evpn_neigh_is_static(n); + + if (old_n_static != new_n_static) + zebra_evpn_sync_neigh_static_chg( + n, old_n_static, new_n_static, false /*defer-dp*/, + false /*defer_mac_dp*/, __func__); +} + +zebra_neigh_t * +zebra_evpn_proc_sync_neigh_update(zebra_evpn_t *zevpn, zebra_neigh_t *n, + uint16_t ipa_len, struct ipaddr *ipaddr, + uint8_t flags, uint32_t seq, esi_t *esi, + struct sync_mac_ip_ctx *ctx) +{ + struct interface *ifp = NULL; + bool is_router; + zebra_mac_t *mac = ctx->mac; + uint32_t tmp_seq; + bool old_router = false; + bool old_bgp_ready = false; + bool new_bgp_ready; + bool inform_dataplane = false; + bool inform_bgp = false; + bool old_mac_static; + bool new_mac_static; + bool set_dp_inactive = false; + char macbuf[ETHER_ADDR_STRLEN]; + char ipbuf[INET6_ADDRSTRLEN]; + bool created; + ifindex_t ifindex = 0; + + /* locate l3-svi */ + ifp = zevpn_map_to_svi(zevpn); + if (ifp) + ifindex = ifp->ifindex; + + is_router = !!CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_ROUTER_FLAG); + old_mac_static = zebra_evpn_mac_is_static(mac); + + if (!n) { + uint32_t n_flags = 0; + + /* New neighbor - create */ + SET_FLAG(n_flags, ZEBRA_NEIGH_LOCAL); + if (CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_PROXY_ADVERT)) + SET_FLAG(n_flags, ZEBRA_NEIGH_ES_PEER_PROXY); + else + SET_FLAG(n_flags, ZEBRA_NEIGH_ES_PEER_ACTIVE); + SET_FLAG(n_flags, ZEBRA_NEIGH_LOCAL_INACTIVE); + + n = zebra_evpn_neigh_add(zevpn, ipaddr, &mac->macaddr, mac, + n_flags); + n->ifindex = ifindex; + ZEBRA_NEIGH_SET_ACTIVE(n); + + created = true; + inform_dataplane = true; + inform_bgp = true; + set_dp_inactive = true; + } else { + bool mac_change; + uint32_t old_flags = n->flags; + bool old_n_static; + bool new_n_static; + + created = false; + old_n_static = zebra_evpn_neigh_is_static(n); + old_bgp_ready = zebra_evpn_neigh_is_ready_for_bgp(n); + old_router = !!CHECK_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); + + mac_change = !!memcmp(&n->emac, &mac->macaddr, ETH_ALEN); + + /* deref and clear old info */ + if (mac_change) { + if (old_bgp_ready) { + zebra_evpn_neigh_send_del_to_client( + zevpn->vni, &n->ip, &n->emac, n->flags, + n->state, false /*force*/); + old_bgp_ready = false; + } + if (n->mac) + zebra_evpn_local_neigh_deref_mac( + n, false /*send_mac_update*/); + } + /* clear old fwd info */ + n->rem_seq = 0; + n->r_vtep_ip.s_addr = 0; + + /* setup new flags */ + n->flags = 0; + SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL); + /* retain activity flag if the neigh was + * previously local + */ + if (old_flags & ZEBRA_NEIGH_LOCAL) { + n->flags |= (old_flags & ZEBRA_NEIGH_LOCAL_INACTIVE); + } else { + inform_dataplane = true; + set_dp_inactive = true; + n->flags |= ZEBRA_NEIGH_LOCAL_INACTIVE; + } + + if (CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_PROXY_ADVERT)) + SET_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_PROXY); + else + SET_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_ACTIVE); + + if (CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_PROXY_ADVERT)) { + SET_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_PROXY); + /* if the neigh was peer-active previously we + * need to keep the flag and start the + * holdtimer on it. the peer-active flag is + * cleared on holdtimer expiry. + */ + if (CHECK_FLAG(old_flags, ZEBRA_NEIGH_ES_PEER_ACTIVE)) { + SET_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_ACTIVE); + zebra_evpn_neigh_start_hold_timer(n); + } + } else { + SET_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_ACTIVE); + /* stop hold timer if a peer has verified + * reachability + */ + zebra_evpn_neigh_stop_hold_timer(n); + } + ZEBRA_NEIGH_SET_ACTIVE(n); + + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH && (old_flags != n->flags)) + zlog_debug( + "sync-neigh vni %u ip %s mac %s old_f 0x%x new_f 0x%x", + n->zevpn->vni, + ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)), + prefix_mac2str(&n->emac, macbuf, + sizeof(macbuf)), + old_flags, n->flags); + + new_n_static = zebra_evpn_neigh_is_static(n); + if (mac_change) { + set_dp_inactive = true; + n->flags |= ZEBRA_NEIGH_LOCAL_INACTIVE; + inform_dataplane = true; + zebra_evpn_local_neigh_ref_mac( + n, &mac->macaddr, mac, + false /*send_mac_update*/); + } else if (old_n_static != new_n_static) { + inform_dataplane = true; + /* if static flags have changed without a mac change + * we need to create the correct sync-refs against + * the existing mac + */ + zebra_evpn_sync_neigh_static_chg( + n, old_n_static, new_n_static, + true /*defer_dp*/, true /*defer_mac_dp*/, + __func__); + } + + /* Update the forwarding info. */ + if (n->ifindex != ifindex) { + n->ifindex = ifindex; + inform_dataplane = true; + } + } + + /* update the neigh seq. we don't bother with the mac seq as + * sync_mac_update already took care of that + */ + tmp_seq = MAX(n->loc_seq, seq); + if (tmp_seq != n->loc_seq) { + n->loc_seq = tmp_seq; + inform_bgp = true; + } + + /* Mark Router flag (R-bit) */ + if (is_router) + SET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); + else + UNSET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); + + if (old_router != is_router) + inform_dataplane = true; + + new_bgp_ready = zebra_evpn_neigh_is_ready_for_bgp(n); + if (old_bgp_ready != new_bgp_ready) + inform_bgp = true; + + new_mac_static = zebra_evpn_mac_is_static(mac); + if ((old_mac_static != new_mac_static) || ctx->mac_dp_update_deferred) + zebra_evpn_sync_mac_dp_install(mac, ctx->mac_inactive, + false /* force_clear_static */, + __func__); + + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug( + "sync-neigh %s vni %u ip %s mac %s if %s(%d) seq %d f 0x%x%s%s", + created ? "created" : "updated", n->zevpn->vni, + ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)), + prefix_mac2str(&n->emac, macbuf, sizeof(macbuf)), + ifp ? ifp->name : "", ifindex, n->loc_seq, n->flags, + inform_bgp ? " inform_bgp" : "", + inform_dataplane ? " inform_dp" : ""); + + if (inform_dataplane) + zebra_evpn_sync_neigh_dp_install(n, set_dp_inactive, + false /* force_clear_static */, + __func__); + + if (inform_bgp) + zebra_evpn_neigh_send_add_del_to_client(n, old_bgp_ready, + new_bgp_ready); + + return n; +} + +/* + * Uninstall remote neighbor from the kernel. + */ +static int zebra_evpn_neigh_uninstall(zebra_evpn_t *zevpn, zebra_neigh_t *n) +{ + struct interface *vlan_if; + + if (!(n->flags & ZEBRA_NEIGH_REMOTE)) + return 0; + + vlan_if = zevpn_map_to_svi(zevpn); + if (!vlan_if) + return -1; + + ZEBRA_NEIGH_SET_INACTIVE(n); + n->loc_seq = 0; + + dplane_rem_neigh_delete(vlan_if, &n->ip); + + return 0; +} + +/* + * Free neighbor hash entry (callback) + */ +static void zebra_evpn_neigh_del_hash_entry(struct hash_bucket *bucket, + void *arg) +{ + struct neigh_walk_ctx *wctx = arg; + zebra_neigh_t *n = bucket->data; + + if (((wctx->flags & DEL_LOCAL_NEIGH) && (n->flags & ZEBRA_NEIGH_LOCAL)) + || ((wctx->flags & DEL_REMOTE_NEIGH) + && (n->flags & ZEBRA_NEIGH_REMOTE)) + || ((wctx->flags & DEL_REMOTE_NEIGH_FROM_VTEP) + && (n->flags & ZEBRA_NEIGH_REMOTE) + && IPV4_ADDR_SAME(&n->r_vtep_ip, &wctx->r_vtep_ip))) { + if (wctx->upd_client && (n->flags & ZEBRA_NEIGH_LOCAL)) + zebra_evpn_neigh_send_del_to_client( + wctx->zevpn->vni, &n->ip, &n->emac, n->flags, + n->state, false /*force*/); + + if (wctx->uninstall) { + if (zebra_evpn_neigh_is_static(n)) + zebra_evpn_sync_neigh_dp_install( + n, false /* set_inactive */, + true /* force_clear_static */, + __func__); + if ((n->flags & ZEBRA_NEIGH_REMOTE)) + zebra_evpn_neigh_uninstall(wctx->zevpn, n); + } + + zebra_evpn_neigh_del(wctx->zevpn, n); + } + + return; +} + +/* + * Delete all neighbor entries for this EVPN. + */ +void zebra_evpn_neigh_del_all(zebra_evpn_t *zevpn, int uninstall, + int upd_client, uint32_t flags) +{ + struct neigh_walk_ctx wctx; + + if (!zevpn->neigh_table) + return; + + memset(&wctx, 0, sizeof(struct neigh_walk_ctx)); + wctx.zevpn = zevpn; + wctx.uninstall = uninstall; + wctx.upd_client = upd_client; + wctx.flags = flags; + + hash_iterate(zevpn->neigh_table, zebra_evpn_neigh_del_hash_entry, + &wctx); +} + +/* + * Look up neighbor hash entry. + */ +zebra_neigh_t *zebra_evpn_neigh_lookup(zebra_evpn_t *zevpn, struct ipaddr *ip) +{ + zebra_neigh_t tmp; + zebra_neigh_t *n; + + memset(&tmp, 0, sizeof(tmp)); + memcpy(&tmp.ip, ip, sizeof(struct ipaddr)); + n = hash_lookup(zevpn->neigh_table, &tmp); + + return n; +} + +/* + * Process all neighbors associated with a MAC upon the MAC being learnt + * locally or undergoing any other change (such as sequence number). + */ +void zebra_evpn_process_neigh_on_local_mac_change(zebra_evpn_t *zevpn, + zebra_mac_t *zmac, + bool seq_change, + bool es_change) +{ + zebra_neigh_t *n = NULL; + struct listnode *node = NULL; + struct zebra_vrf *zvrf = NULL; + char buf[ETHER_ADDR_STRLEN]; + + zvrf = vrf_info_lookup(zevpn->vxlan_if->vrf_id); + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Processing neighbors on local MAC %s %s, VNI %u", + prefix_mac2str(&zmac->macaddr, buf, sizeof(buf)), + seq_change ? "CHANGE" : "ADD", zevpn->vni); + + /* Walk all neighbors and mark any inactive local neighbors as + * active and/or update sequence number upon a move, and inform BGP. + * The action for remote neighbors is TBD. + * NOTE: We can't simply uninstall remote neighbors as the kernel may + * accidentally end up deleting a just-learnt local neighbor. + */ + for (ALL_LIST_ELEMENTS_RO(zmac->neigh_list, node, n)) { + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { + if (IS_ZEBRA_NEIGH_INACTIVE(n) || seq_change + || es_change) { + ZEBRA_NEIGH_SET_ACTIVE(n); + n->loc_seq = zmac->loc_seq; + if (!(zvrf->dup_addr_detect && zvrf->dad_freeze + && !!CHECK_FLAG(n->flags, + ZEBRA_NEIGH_DUPLICATE))) + zebra_evpn_neigh_send_add_to_client( + zevpn->vni, &n->ip, &n->emac, + n->mac, n->flags, n->loc_seq); + } + } + } +} + +/* + * Process all neighbors associated with a local MAC upon the MAC being + * deleted. + */ +void zebra_evpn_process_neigh_on_local_mac_del(zebra_evpn_t *zevpn, + zebra_mac_t *zmac) +{ + zebra_neigh_t *n = NULL; + struct listnode *node = NULL; + char buf[ETHER_ADDR_STRLEN]; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Processing neighbors on local MAC %s DEL, VNI %u", + prefix_mac2str(&zmac->macaddr, buf, sizeof(buf)), + zevpn->vni); + + /* Walk all local neighbors and mark as inactive and inform + * BGP, if needed. + * TBD: There is currently no handling for remote neighbors. We + * don't expect them to exist, if they do, do we install the MAC + * as a remote MAC and the neighbor as remote? + */ + for (ALL_LIST_ELEMENTS_RO(zmac->neigh_list, node, n)) { + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { + if (IS_ZEBRA_NEIGH_ACTIVE(n)) { + ZEBRA_NEIGH_SET_INACTIVE(n); + n->loc_seq = 0; + zebra_evpn_neigh_send_del_to_client( + zevpn->vni, &n->ip, &n->emac, n->flags, + ZEBRA_NEIGH_ACTIVE, false /*force*/); + } + } + } +} + +/* + * Process all neighbors associated with a MAC upon the MAC being remotely + * learnt. + */ +void zebra_evpn_process_neigh_on_remote_mac_add(zebra_evpn_t *zevpn, + zebra_mac_t *zmac) +{ + zebra_neigh_t *n = NULL; + struct listnode *node = NULL; + char buf[ETHER_ADDR_STRLEN]; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Processing neighbors on remote MAC %s ADD, VNI %u", + prefix_mac2str(&zmac->macaddr, buf, sizeof(buf)), + zevpn->vni); + + /* Walk all local neighbors and mark as inactive and inform + * BGP, if needed. + */ + for (ALL_LIST_ELEMENTS_RO(zmac->neigh_list, node, n)) { + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { + if (IS_ZEBRA_NEIGH_ACTIVE(n)) { + ZEBRA_NEIGH_SET_INACTIVE(n); + n->loc_seq = 0; + zebra_evpn_neigh_send_del_to_client( + zevpn->vni, &n->ip, &n->emac, n->flags, + ZEBRA_NEIGH_ACTIVE, false /* force */); + } + } + } +} + +/* + * Process all neighbors associated with a remote MAC upon the MAC being + * deleted. + */ +void zebra_evpn_process_neigh_on_remote_mac_del(zebra_evpn_t *zevpn, + zebra_mac_t *zmac) +{ + /* NOTE: Currently a NO-OP. */ +} + +static inline void zebra_evpn_local_neigh_update_log( + const char *pfx, zebra_neigh_t *n, bool is_router, bool local_inactive, + bool old_bgp_ready, bool new_bgp_ready, bool inform_dataplane, + bool inform_bgp, const char *sfx) +{ + char macbuf[ETHER_ADDR_STRLEN]; + char ipbuf[INET6_ADDRSTRLEN]; + + if (!IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + return; + + zlog_debug("%s neigh vni %u ip %s mac %s f 0x%x%s%s%s%s%s%s %s", pfx, + n->zevpn->vni, ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)), + prefix_mac2str(&n->emac, macbuf, sizeof(macbuf)), n->flags, + is_router ? " router" : "", + local_inactive ? " local-inactive" : "", + old_bgp_ready ? " old_bgp_ready" : "", + new_bgp_ready ? " new_bgp_ready" : "", + inform_dataplane ? " inform_dp" : "", + inform_bgp ? " inform_bgp" : "", sfx); +} + +/* As part Duplicate Address Detection (DAD) for IP mobility + * MAC binding changes, ensure to inherit duplicate flag + * from MAC. + */ +static int zebra_evpn_ip_inherit_dad_from_mac(struct zebra_vrf *zvrf, + zebra_mac_t *old_zmac, + zebra_mac_t *new_zmac, + zebra_neigh_t *nbr) +{ + bool is_old_mac_dup = false; + bool is_new_mac_dup = false; + + if (!zvrf->dup_addr_detect) + return 0; + /* Check old or new MAC is detected as duplicate + * mark this neigh as duplicate + */ + if (old_zmac) + is_old_mac_dup = + CHECK_FLAG(old_zmac->flags, ZEBRA_MAC_DUPLICATE); + if (new_zmac) + is_new_mac_dup = + CHECK_FLAG(new_zmac->flags, ZEBRA_MAC_DUPLICATE); + /* Old and/or new MAC can be in duplicate state, + * based on that IP/Neigh Inherits the flag. + * If New MAC is marked duplicate, inherit to the IP. + * If old MAC is duplicate but new MAC is not, clear + * duplicate flag for IP and reset detection params + * and let IP DAD retrigger. + */ + if (is_new_mac_dup && !CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) { + SET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); + /* Capture Duplicate detection time */ + nbr->dad_dup_detect_time = monotime(NULL); + /* Mark neigh inactive */ + ZEBRA_NEIGH_SET_INACTIVE(nbr); + + return 1; + } else if (is_old_mac_dup && !is_new_mac_dup) { + UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); + nbr->dad_count = 0; + nbr->detect_start_time.tv_sec = 0; + nbr->detect_start_time.tv_usec = 0; + } + return 0; +} + +static int zebra_evpn_dad_ip_auto_recovery_exp(struct thread *t) +{ + struct zebra_vrf *zvrf = NULL; + zebra_neigh_t *nbr = NULL; + zebra_evpn_t *zevpn = NULL; + char buf1[INET6_ADDRSTRLEN]; + char buf2[ETHER_ADDR_STRLEN]; + + nbr = THREAD_ARG(t); + + /* since this is asynchronous we need sanity checks*/ + zvrf = vrf_info_lookup(nbr->zevpn->vrf_id); + if (!zvrf) + return 0; + + zevpn = zebra_evpn_lookup(nbr->zevpn->vni); + if (!zevpn) + return 0; + + nbr = zebra_evpn_neigh_lookup(zevpn, &nbr->ip); + if (!nbr) + return 0; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%s: duplicate addr MAC %s IP %s flags 0x%x learn count %u vni %u auto recovery expired", + __func__, + prefix_mac2str(&nbr->emac, buf2, sizeof(buf2)), + ipaddr2str(&nbr->ip, buf1, sizeof(buf1)), nbr->flags, + nbr->dad_count, zevpn->vni); + + UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); + nbr->dad_count = 0; + nbr->detect_start_time.tv_sec = 0; + nbr->detect_start_time.tv_usec = 0; + nbr->dad_dup_detect_time = 0; + nbr->dad_ip_auto_recovery_timer = NULL; + ZEBRA_NEIGH_SET_ACTIVE(nbr); + + /* Send to BGP */ + if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL)) { + zebra_evpn_neigh_send_add_to_client(zevpn->vni, &nbr->ip, + &nbr->emac, nbr->mac, + nbr->flags, nbr->loc_seq); + } else if (!!CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_REMOTE)) { + zebra_evpn_rem_neigh_install(zevpn, nbr, false /*was_static*/); + } + + return 0; +} + +static void +zebra_evpn_dup_addr_detect_for_neigh(struct zebra_vrf *zvrf, zebra_neigh_t *nbr, + struct in_addr vtep_ip, bool do_dad, + bool *is_dup_detect, bool is_local) +{ + + struct timeval elapsed = {0, 0}; + char buf[ETHER_ADDR_STRLEN]; + char buf1[INET6_ADDRSTRLEN]; + bool reset_params = false; + + if (!zvrf->dup_addr_detect) + return; + + /* IP is detected as duplicate or inherit dup + * state, hold on to install as remote entry + * only if freeze is enabled. + */ + if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%s: duplicate addr MAC %s IP %s flags 0x%x skip installing, learn count %u recover time %u", + __func__, + prefix_mac2str(&nbr->emac, buf, sizeof(buf)), + ipaddr2str(&nbr->ip, buf1, sizeof(buf1)), + nbr->flags, nbr->dad_count, + zvrf->dad_freeze_time); + + if (zvrf->dad_freeze) + *is_dup_detect = true; + + /* warn-only action, neigh will be installed. + * freeze action, it wil not be installed. + */ + return; + } + + if (!do_dad) + return; + + /* Check if detection time (M-secs) expired. + * Reset learn count and detection start time. + * During remote mac add, count should already be 1 + * via local learning. + */ + monotime_since(&nbr->detect_start_time, &elapsed); + reset_params = (elapsed.tv_sec > zvrf->dad_time); + + if (is_local && !reset_params) { + /* RFC-7432: A PE/VTEP that detects a MAC mobility + * event via LOCAL learning starts an M-second timer. + * + * NOTE: This is the START of the probe with count is + * 0 during LOCAL learn event. + */ + reset_params = !nbr->dad_count; + } + + if (reset_params) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%s: duplicate addr MAC %s IP %s flags 0x%x detection time passed, reset learn count %u", + __func__, + prefix_mac2str(&nbr->emac, buf, sizeof(buf)), + ipaddr2str(&nbr->ip, buf1, sizeof(buf1)), + nbr->flags, nbr->dad_count); + /* Reset learn count but do not start detection + * during REMOTE learn event. + */ + nbr->dad_count = 0; + /* Start dup. addr detection (DAD) start time, + * ONLY during LOCAL learn. + */ + if (is_local) + monotime(&nbr->detect_start_time); + + } else if (!is_local) { + /* For REMOTE IP/Neigh, increment detection count + * ONLY while in probe window, once window passed, + * next local learn event should trigger DAD. + */ + nbr->dad_count++; + } + + /* For LOCAL IP/Neigh learn event, once count is reset above via either + * initial/start detection time or passed the probe time, the count + * needs to be incremented. + */ + if (is_local) + nbr->dad_count++; + + if (nbr->dad_count >= zvrf->dad_max_moves) { + flog_warn( + EC_ZEBRA_DUP_IP_DETECTED, + "VNI %u: MAC %s IP %s detected as duplicate during %s VTEP %s", + nbr->zevpn->vni, + prefix_mac2str(&nbr->emac, buf, sizeof(buf)), + ipaddr2str(&nbr->ip, buf1, sizeof(buf1)), + is_local ? "local update, last" : "remote update, from", + inet_ntoa(vtep_ip)); + + SET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); + + /* Capture Duplicate detection time */ + nbr->dad_dup_detect_time = monotime(NULL); + + /* Start auto recovery timer for this IP */ + THREAD_OFF(nbr->dad_ip_auto_recovery_timer); + if (zvrf->dad_freeze && zvrf->dad_freeze_time) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%s: duplicate addr MAC %s IP %s flags 0x%x auto recovery time %u start", + __func__, + prefix_mac2str(&nbr->emac, buf, + sizeof(buf)), + ipaddr2str(&nbr->ip, buf1, + sizeof(buf1)), + nbr->flags, zvrf->dad_freeze_time); + + thread_add_timer(zrouter.master, + zebra_evpn_dad_ip_auto_recovery_exp, + nbr, zvrf->dad_freeze_time, + &nbr->dad_ip_auto_recovery_timer); + } + if (zvrf->dad_freeze) + *is_dup_detect = true; + } +} + +int zebra_evpn_local_neigh_update(zebra_evpn_t *zevpn, struct interface *ifp, + struct ipaddr *ip, struct ethaddr *macaddr, + bool is_router, bool local_inactive, + bool dp_static) +{ + char buf[ETHER_ADDR_STRLEN]; + char buf2[INET6_ADDRSTRLEN]; + struct zebra_vrf *zvrf; + zebra_neigh_t *n = NULL; + zebra_mac_t *zmac = NULL, *old_zmac = NULL; + uint32_t old_mac_seq = 0, mac_new_seq = 0; + bool upd_mac_seq = false; + bool neigh_mac_change = false; + bool neigh_on_hold = false; + bool neigh_was_remote = false; + bool do_dad = false; + struct in_addr vtep_ip = {.s_addr = 0}; + bool inform_dataplane = false; + bool created = false; + bool new_static = false; + bool old_bgp_ready = false; + bool new_bgp_ready; + + /* Check if the MAC exists. */ + zmac = zebra_evpn_mac_lookup(zevpn, macaddr); + if (!zmac) { + /* create a dummy MAC if the MAC is not already present */ + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("AUTO MAC %s created for neigh %s on VNI %u", + prefix_mac2str(macaddr, buf, sizeof(buf)), + ipaddr2str(ip, buf2, sizeof(buf2)), + zevpn->vni); + + zmac = zebra_evpn_mac_add(zevpn, macaddr); + if (!zmac) { + zlog_debug("Failed to add MAC %s VNI %u", + prefix_mac2str(macaddr, buf, sizeof(buf)), + zevpn->vni); + return -1; + } + + memset(&zmac->fwd_info, 0, sizeof(zmac->fwd_info)); + memset(&zmac->flags, 0, sizeof(uint32_t)); + SET_FLAG(zmac->flags, ZEBRA_MAC_AUTO); + } else { + if (CHECK_FLAG(zmac->flags, ZEBRA_MAC_REMOTE)) { + /* + * We don't change the MAC to local upon a neighbor + * learn event, we wait for the explicit local MAC + * learn. However, we have to compute its sequence + * number in preparation for when it actually turns + * local. + */ + upd_mac_seq = true; + } + } + + zvrf = vrf_info_lookup(zevpn->vxlan_if->vrf_id); + if (!zvrf) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug(" Unable to find vrf for: %d", + zevpn->vxlan_if->vrf_id); + return -1; + } + + /* Check if the neighbor exists. */ + n = zebra_evpn_neigh_lookup(zevpn, ip); + if (!n) { + /* New neighbor - create */ + n = zebra_evpn_neigh_add(zevpn, ip, macaddr, zmac, 0); + if (!n) { + flog_err( + EC_ZEBRA_MAC_ADD_FAILED, + "Failed to add neighbor %s MAC %s intf %s(%u) -> VNI %u", + ipaddr2str(ip, buf2, sizeof(buf2)), + prefix_mac2str(macaddr, buf, sizeof(buf)), + ifp->name, ifp->ifindex, zevpn->vni); + return -1; + } + /* Set "local" forwarding info. */ + SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL); + n->ifindex = ifp->ifindex; + created = true; + } else { + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { + bool mac_different; + bool cur_is_router; + bool old_local_inactive; + + old_local_inactive = !!CHECK_FLAG( + n->flags, ZEBRA_NEIGH_LOCAL_INACTIVE); + + old_bgp_ready = zebra_evpn_neigh_is_ready_for_bgp(n); + + /* Note any changes and see if of interest to BGP. */ + mac_different = !!memcmp(&n->emac, macaddr, ETH_ALEN); + cur_is_router = + !!CHECK_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); + new_static = zebra_evpn_neigh_is_static(n); + if (!mac_different && is_router == cur_is_router + && old_local_inactive == local_inactive + && dp_static != new_static) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + " Ignoring entry mac is the same and is_router == cur_is_router"); + n->ifindex = ifp->ifindex; + return 0; + } + + old_zmac = n->mac; + if (!mac_different) { + /* XXX - cleanup this code duplication */ + bool is_neigh_freezed = false; + + /* Only the router flag has changed. */ + if (is_router) + SET_FLAG(n->flags, + ZEBRA_NEIGH_ROUTER_FLAG); + else + UNSET_FLAG(n->flags, + ZEBRA_NEIGH_ROUTER_FLAG); + + if (local_inactive) + SET_FLAG(n->flags, + ZEBRA_NEIGH_LOCAL_INACTIVE); + else + UNSET_FLAG(n->flags, + ZEBRA_NEIGH_LOCAL_INACTIVE); + new_bgp_ready = + zebra_evpn_neigh_is_ready_for_bgp(n); + + /* Neigh is in freeze state and freeze action + * is enabled, do not send update to client. + */ + is_neigh_freezed = + (zvrf->dup_addr_detect + && zvrf->dad_freeze + && CHECK_FLAG(n->flags, + ZEBRA_NEIGH_DUPLICATE)); + + zebra_evpn_local_neigh_update_log( + "local", n, is_router, local_inactive, + old_bgp_ready, new_bgp_ready, false, + false, "flag-update"); + + /* if the neigh can no longer be advertised + * remove it from bgp + */ + if (!is_neigh_freezed) { + zebra_evpn_neigh_send_add_del_to_client( + n, old_bgp_ready, + new_bgp_ready); + } else { + if (IS_ZEBRA_DEBUG_VXLAN + && IS_ZEBRA_NEIGH_ACTIVE(n)) + zlog_debug( + " Neighbor active and frozen"); + } + return 0; + } + + /* The MAC has changed, need to issue a delete + * first as this means a different MACIP route. + * Also, need to do some unlinking/relinking. + * We also need to update the MAC's sequence number + * in different situations. + */ + if (old_bgp_ready) { + zebra_evpn_neigh_send_del_to_client( + zevpn->vni, &n->ip, &n->emac, n->flags, + n->state, false /*force*/); + old_bgp_ready = false; + } + if (old_zmac) { + old_mac_seq = CHECK_FLAG(old_zmac->flags, + ZEBRA_MAC_REMOTE) + ? old_zmac->rem_seq + : old_zmac->loc_seq; + neigh_mac_change = upd_mac_seq = true; + zebra_evpn_local_neigh_deref_mac( + n, true /* send_mac_update */); + } + + /* if mac changes abandon peer flags and tell + * dataplane to clear the static flag + */ + if (zebra_evpn_neigh_clear_sync_info(n)) + inform_dataplane = true; + /* Update the forwarding info. */ + n->ifindex = ifp->ifindex; + + /* Link to new MAC */ + zebra_evpn_local_neigh_ref_mac( + n, macaddr, zmac, true /* send_mac_update */); + } else if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) { + /* + * Neighbor has moved from remote to local. Its + * MAC could have also changed as part of the move. + */ + if (memcmp(n->emac.octet, macaddr->octet, ETH_ALEN) + != 0) { + old_zmac = n->mac; + if (old_zmac) { + old_mac_seq = + CHECK_FLAG(old_zmac->flags, + ZEBRA_MAC_REMOTE) + ? old_zmac->rem_seq + : old_zmac->loc_seq; + neigh_mac_change = upd_mac_seq = true; + zebra_evpn_local_neigh_deref_mac( + n, true /* send_update */); + } + + /* Link to new MAC */ + zebra_evpn_local_neigh_ref_mac( + n, macaddr, zmac, true /*send_update*/); + } + /* Based on Mobility event Scenario-B from the + * draft, neigh's previous state was remote treat this + * event for DAD. + */ + neigh_was_remote = true; + vtep_ip = n->r_vtep_ip; + /* Mark appropriately */ + UNSET_FLAG(n->flags, ZEBRA_NEIGH_REMOTE); + n->r_vtep_ip.s_addr = INADDR_ANY; + SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL); + n->ifindex = ifp->ifindex; + } + } + + /* If MAC was previously remote, or the neighbor had a different + * MAC earlier, recompute the sequence number. + */ + if (upd_mac_seq) { + uint32_t seq1, seq2; + + seq1 = CHECK_FLAG(zmac->flags, ZEBRA_MAC_REMOTE) + ? zmac->rem_seq + 1 + : zmac->loc_seq; + seq2 = neigh_mac_change ? old_mac_seq + 1 : 0; + mac_new_seq = zmac->loc_seq < MAX(seq1, seq2) ? MAX(seq1, seq2) + : zmac->loc_seq; + } + + if (local_inactive) + SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL_INACTIVE); + else + UNSET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL_INACTIVE); + + /* Mark Router flag (R-bit) */ + if (is_router) + SET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); + else + UNSET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); + + /* if the dataplane thinks that this is a sync entry but + * zebra doesn't we need to re-concile the diff + * by re-installing the dataplane entry + */ + if (dp_static) { + new_static = zebra_evpn_neigh_is_static(n); + if (!new_static) + inform_dataplane = true; + } + + /* Check old and/or new MAC detected as duplicate mark + * the neigh as duplicate + */ + if (zebra_evpn_ip_inherit_dad_from_mac(zvrf, old_zmac, zmac, n)) { + flog_warn( + EC_ZEBRA_DUP_IP_INHERIT_DETECTED, + "VNI %u: MAC %s IP %s detected as duplicate during local update, inherit duplicate from MAC", + zevpn->vni, prefix_mac2str(macaddr, buf, sizeof(buf)), + ipaddr2str(&n->ip, buf2, sizeof(buf2))); + } + + /* For IP Duplicate Address Detection (DAD) is trigger, + * when the event is extended mobility based on scenario-B + * from the draft, IP/Neigh's MAC binding changed and + * neigh's previous state was remote. + */ + if (neigh_mac_change && neigh_was_remote) + do_dad = true; + + zebra_evpn_dup_addr_detect_for_neigh(zvrf, n, vtep_ip, do_dad, + &neigh_on_hold, true); + + if (inform_dataplane) + zebra_evpn_sync_neigh_dp_install(n, false /* set_inactive */, + false /* force_clear_static */, + __func__); + + /* Before we program this in BGP, we need to check if MAC is locally + * learnt. If not, force neighbor to be inactive and reset its seq. + */ + if (!CHECK_FLAG(zmac->flags, ZEBRA_MAC_LOCAL)) { + zebra_evpn_local_neigh_update_log( + "local", n, is_router, local_inactive, false, false, + inform_dataplane, false, "auto-mac"); + ZEBRA_NEIGH_SET_INACTIVE(n); + n->loc_seq = 0; + zmac->loc_seq = mac_new_seq; + return 0; + } + + zebra_evpn_local_neigh_update_log("local", n, is_router, local_inactive, + false, false, inform_dataplane, true, + created ? "created" : "updated"); + + /* If the MAC's sequence number has changed, inform the MAC and all + * neighbors associated with the MAC to BGP, else just inform this + * neighbor. + */ + if (upd_mac_seq && zmac->loc_seq != mac_new_seq) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Seq changed for MAC %s VNI %u - old %u new %u", + prefix_mac2str(macaddr, buf, sizeof(buf)), + zevpn->vni, zmac->loc_seq, mac_new_seq); + zmac->loc_seq = mac_new_seq; + if (zebra_evpn_mac_send_add_to_client(zevpn->vni, macaddr, + zmac->flags, + zmac->loc_seq, zmac->es)) + return -1; + zebra_evpn_process_neigh_on_local_mac_change(zevpn, zmac, 1, + 0 /*es_change*/); + return 0; + } + + n->loc_seq = zmac->loc_seq; + + if (!neigh_on_hold) { + ZEBRA_NEIGH_SET_ACTIVE(n); + new_bgp_ready = zebra_evpn_neigh_is_ready_for_bgp(n); + zebra_evpn_neigh_send_add_del_to_client(n, old_bgp_ready, + new_bgp_ready); + } else { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug(" Neighbor on hold not sending"); + } + return 0; +} + +int zebra_evpn_remote_neigh_update(zebra_evpn_t *zevpn, struct interface *ifp, + struct ipaddr *ip, struct ethaddr *macaddr, + uint16_t state) +{ + char buf[ETHER_ADDR_STRLEN]; + char buf2[INET6_ADDRSTRLEN]; + zebra_neigh_t *n = NULL; + zebra_mac_t *zmac = NULL; + + /* If the neighbor is unknown, there is no further action. */ + n = zebra_evpn_neigh_lookup(zevpn, ip); + if (!n) + return 0; + + /* If a remote entry, see if it needs to be refreshed */ + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) { +#ifdef GNU_LINUX + if (state & NUD_STALE) + zebra_evpn_rem_neigh_install(zevpn, n, + false /*was_static*/); +#endif + } else { + /* We got a "remote" neighbor notification for an entry + * we think is local. This can happen in a multihoming + * scenario - but only if the MAC is already "remote". + * Just mark our entry as "remote". + */ + zmac = zebra_evpn_mac_lookup(zevpn, macaddr); + if (!zmac || !CHECK_FLAG(zmac->flags, ZEBRA_MAC_REMOTE)) { + zlog_debug( + "Ignore remote neigh %s (MAC %s) on L2-VNI %u - MAC unknown or local", + ipaddr2str(&n->ip, buf2, sizeof(buf2)), + prefix_mac2str(macaddr, buf, sizeof(buf)), + zevpn->vni); + return -1; + } + + UNSET_FLAG(n->flags, ZEBRA_NEIGH_ALL_LOCAL_FLAGS); + SET_FLAG(n->flags, ZEBRA_NEIGH_REMOTE); + ZEBRA_NEIGH_SET_ACTIVE(n); + n->r_vtep_ip = zmac->fwd_info.r_vtep_ip; + } + + return 0; +} + +/* Notify Neighbor entries to the Client, skips the GW entry */ +static void +zebra_evpn_send_neigh_hash_entry_to_client(struct hash_bucket *bucket, + void *arg) +{ + struct mac_walk_ctx *wctx = arg; + zebra_neigh_t *zn = bucket->data; + zebra_mac_t *zmac = NULL; + + if (CHECK_FLAG(zn->flags, ZEBRA_NEIGH_DEF_GW)) + return; + + if (CHECK_FLAG(zn->flags, ZEBRA_NEIGH_LOCAL) + && IS_ZEBRA_NEIGH_ACTIVE(zn)) { + zmac = zebra_evpn_mac_lookup(wctx->zevpn, &zn->emac); + if (!zmac) + return; + + zebra_evpn_neigh_send_add_to_client(wctx->zevpn->vni, &zn->ip, + &zn->emac, zn->mac, + zn->flags, zn->loc_seq); + } +} + +/* Iterator of a specific EVPN */ +void zebra_evpn_send_neigh_to_client(zebra_evpn_t *zevpn) +{ + struct neigh_walk_ctx wctx; + + memset(&wctx, 0, sizeof(struct neigh_walk_ctx)); + wctx.zevpn = zevpn; + + hash_iterate(zevpn->neigh_table, + zebra_evpn_send_neigh_hash_entry_to_client, &wctx); +} + +void zebra_evpn_clear_dup_neigh_hash(struct hash_bucket *bucket, void *ctxt) +{ + struct neigh_walk_ctx *wctx = ctxt; + zebra_neigh_t *nbr; + zebra_evpn_t *zevpn; + char buf[INET6_ADDRSTRLEN]; + + nbr = (zebra_neigh_t *)bucket->data; + if (!nbr) + return; + + zevpn = wctx->zevpn; + + if (!CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) + return; + + if (IS_ZEBRA_DEBUG_VXLAN) { + ipaddr2str(&nbr->ip, buf, sizeof(buf)); + zlog_debug("%s: clear neigh %s dup state, flags 0x%x seq %u", + __func__, buf, nbr->flags, nbr->loc_seq); + } + + UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); + nbr->dad_count = 0; + nbr->detect_start_time.tv_sec = 0; + nbr->detect_start_time.tv_usec = 0; + nbr->dad_dup_detect_time = 0; + THREAD_OFF(nbr->dad_ip_auto_recovery_timer); + + if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL)) { + zebra_evpn_neigh_send_add_to_client(zevpn->vni, &nbr->ip, + &nbr->emac, nbr->mac, + nbr->flags, nbr->loc_seq); + } else if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_REMOTE)) { + zebra_evpn_rem_neigh_install(zevpn, nbr, false /*was_static*/); + } +} + +/* + * Print a specific neighbor entry. + */ +void zebra_evpn_print_neigh(zebra_neigh_t *n, void *ctxt, json_object *json) +{ + struct vty *vty; + char buf1[ETHER_ADDR_STRLEN]; + char buf2[INET6_ADDRSTRLEN]; + const char *type_str; + const char *state_str; + bool flags_present = false; + struct zebra_vrf *zvrf = NULL; + struct timeval detect_start_time = {0, 0}; + char timebuf[MONOTIME_STRLEN]; + char thread_buf[THREAD_TIMER_STRLEN]; + + zvrf = zebra_vrf_get_evpn(); + if (!zvrf) + return; + + ipaddr2str(&n->ip, buf2, sizeof(buf2)); + prefix_mac2str(&n->emac, buf1, sizeof(buf1)); + type_str = CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL) ? "local" : "remote"; + state_str = IS_ZEBRA_NEIGH_ACTIVE(n) ? "active" : "inactive"; + vty = (struct vty *)ctxt; + if (json == NULL) { + bool sync_info = false; + + vty_out(vty, "IP: %s\n", + ipaddr2str(&n->ip, buf2, sizeof(buf2))); + vty_out(vty, " Type: %s\n", type_str); + vty_out(vty, " State: %s\n", state_str); + vty_out(vty, " MAC: %s\n", + prefix_mac2str(&n->emac, buf1, sizeof(buf1))); + vty_out(vty, " Sync-info:"); + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL_INACTIVE)) { + vty_out(vty, " local-inactive"); + sync_info = true; + } + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_PROXY)) { + vty_out(vty, " peer-proxy"); + sync_info = true; + } + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_ACTIVE)) { + vty_out(vty, " peer-active"); + sync_info = true; + } + if (n->hold_timer) { + vty_out(vty, " (ht: %s)", + thread_timer_to_hhmmss(thread_buf, + sizeof(thread_buf), + n->hold_timer)); + sync_info = true; + } + if (!sync_info) + vty_out(vty, " -"); + vty_out(vty, "\n"); + } else { + json_object_string_add(json, "ip", buf2); + json_object_string_add(json, "type", type_str); + json_object_string_add(json, "state", state_str); + json_object_string_add(json, "mac", buf1); + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL_INACTIVE)) + json_object_boolean_true_add(json, "localInactive"); + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_PROXY)) + json_object_boolean_true_add(json, "peerProxy"); + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_ACTIVE)) + json_object_boolean_true_add(json, "peerActive"); + if (n->hold_timer) + json_object_string_add( + json, "peerActiveHold", + thread_timer_to_hhmmss(thread_buf, + sizeof(thread_buf), + n->hold_timer)); + } + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) { + if (n->mac->es) { + if (json) + json_object_string_add(json, "remoteEs", + n->mac->es->esi_str); + else + vty_out(vty, " Remote ES: %s\n", + n->mac->es->esi_str); + } else { + if (json) + json_object_string_add(json, "remoteVtep", + inet_ntoa(n->r_vtep_ip)); + else + vty_out(vty, " Remote VTEP: %s\n", + inet_ntoa(n->r_vtep_ip)); + } + } + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DEF_GW)) { + if (!json) { + vty_out(vty, " Flags: Default-gateway"); + flags_present = true; + } else + json_object_boolean_true_add(json, "defaultGateway"); + } + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG)) { + if (!json) { + vty_out(vty, + flags_present ? " ,Router" : " Flags: Router"); + flags_present = true; + } + } + if (json == NULL) { + if (flags_present) + vty_out(vty, "\n"); + vty_out(vty, " Local Seq: %u Remote Seq: %u\n", n->loc_seq, + n->rem_seq); + + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE)) { + vty_out(vty, " Duplicate, detected at %s", + time_to_string(n->dad_dup_detect_time, + timebuf)); + } else if (n->dad_count) { + monotime_since(&n->detect_start_time, + &detect_start_time); + if (detect_start_time.tv_sec <= zvrf->dad_time) { + time_to_string(n->detect_start_time.tv_sec, + timebuf); + vty_out(vty, + " Duplicate detection started at %s, detection count %u\n", + timebuf, n->dad_count); + } + } + } else { + json_object_int_add(json, "localSequence", n->loc_seq); + json_object_int_add(json, "remoteSequence", n->rem_seq); + json_object_int_add(json, "detectionCount", n->dad_count); + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE)) + json_object_boolean_true_add(json, "isDuplicate"); + else + json_object_boolean_false_add(json, "isDuplicate"); + } +} + +void zebra_evpn_print_neigh_hdr(struct vty *vty, struct neigh_walk_ctx *wctx) +{ + vty_out(vty, "Flags: I=local-inactive, P=peer-active, X=peer-proxy\n"); + vty_out(vty, "%*s %-6s %-5s %-8s %-17s %-30s %s\n", -wctx->addr_width, + "Neighbor", "Type", "Flags", "State", "MAC", "Remote ES/VTEP", + "Seq #'s"); +} + +static char *zebra_evpn_print_neigh_flags(zebra_neigh_t *n, char *flags_buf, + uint32_t flags_buf_sz) +{ + snprintf(flags_buf, flags_buf_sz, "%s%s%s", + (n->flags & ZEBRA_NEIGH_ES_PEER_ACTIVE) ? + "P" : "", + (n->flags & ZEBRA_NEIGH_ES_PEER_PROXY) ? + "X" : "", + (n->flags & ZEBRA_NEIGH_LOCAL_INACTIVE) ? + "I" : ""); + + return flags_buf; +} + +/* + * Print neighbor hash entry - called for display of all neighbors. + */ +void zebra_evpn_print_neigh_hash(struct hash_bucket *bucket, void *ctxt) +{ + struct vty *vty; + json_object *json_evpn = NULL, *json_row = NULL; + zebra_neigh_t *n; + char buf1[ETHER_ADDR_STRLEN]; + char buf2[INET6_ADDRSTRLEN]; + struct neigh_walk_ctx *wctx = ctxt; + const char *state_str; + char flags_buf[6]; + + vty = wctx->vty; + json_evpn = wctx->json; + n = (zebra_neigh_t *)bucket->data; + + if (json_evpn) + json_row = json_object_new_object(); + + prefix_mac2str(&n->emac, buf1, sizeof(buf1)); + ipaddr2str(&n->ip, buf2, sizeof(buf2)); + state_str = IS_ZEBRA_NEIGH_ACTIVE(n) ? "active" : "inactive"; + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { + if (wctx->flags & SHOW_REMOTE_NEIGH_FROM_VTEP) + return; + + if (json_evpn == NULL) { + vty_out(vty, "%*s %-6s %-5s %-8s %-17s %-30s %u/%u\n", + -wctx->addr_width, buf2, "local", + zebra_evpn_print_neigh_flags(n, flags_buf, + sizeof(flags_buf)), state_str, buf1, + "", n->loc_seq, n->rem_seq); + } else { + json_object_string_add(json_row, "type", "local"); + json_object_string_add(json_row, "state", state_str); + json_object_string_add(json_row, "mac", buf1); + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DEF_GW)) + json_object_boolean_true_add(json_row, + "defaultGateway"); + json_object_int_add(json_row, "localSequence", + n->loc_seq); + json_object_int_add(json_row, "remoteSequence", + n->rem_seq); + json_object_int_add(json_row, "detectionCount", + n->dad_count); + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE)) + json_object_boolean_true_add(json_row, + "isDuplicate"); + else + json_object_boolean_false_add(json_row, + "isDuplicate"); + } + wctx->count++; + } else if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) { + if ((wctx->flags & SHOW_REMOTE_NEIGH_FROM_VTEP) + && !IPV4_ADDR_SAME(&n->r_vtep_ip, &wctx->r_vtep_ip)) + return; + + if (json_evpn == NULL) { + if ((wctx->flags & SHOW_REMOTE_NEIGH_FROM_VTEP) + && (wctx->count == 0)) + zebra_evpn_print_neigh_hdr(vty, wctx); + vty_out(vty, "%*s %-6s %-5s %-8s %-17s %-30s %u/%u\n", + -wctx->addr_width, buf2, "remote", + zebra_evpn_print_neigh_flags(n, flags_buf, + sizeof(flags_buf)), state_str, buf1, + n->mac->es ? n->mac->es->esi_str + : inet_ntoa(n->r_vtep_ip), + n->loc_seq, n->rem_seq); + } else { + json_object_string_add(json_row, "type", "remote"); + json_object_string_add(json_row, "state", state_str); + json_object_string_add(json_row, "mac", buf1); + if (n->mac->es) + json_object_string_add(json_row, "remoteEs", + n->mac->es->esi_str); + else + json_object_string_add(json_row, "remoteVtep", + inet_ntoa(n->r_vtep_ip)); + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DEF_GW)) + json_object_boolean_true_add(json_row, + "defaultGateway"); + json_object_int_add(json_row, "localSequence", + n->loc_seq); + json_object_int_add(json_row, "remoteSequence", + n->rem_seq); + json_object_int_add(json_row, "detectionCount", + n->dad_count); + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE)) + json_object_boolean_true_add(json_row, + "isDuplicate"); + else + json_object_boolean_false_add(json_row, + "isDuplicate"); + } + wctx->count++; + } + + if (json_evpn) + json_object_object_add(json_evpn, buf2, json_row); +} + +/* + * Print neighbor hash entry in detail - called for display of all neighbors. + */ +void zebra_evpn_print_neigh_hash_detail(struct hash_bucket *bucket, void *ctxt) +{ + struct vty *vty; + json_object *json_evpn = NULL, *json_row = NULL; + zebra_neigh_t *n; + char buf[INET6_ADDRSTRLEN]; + struct neigh_walk_ctx *wctx = ctxt; + + vty = wctx->vty; + json_evpn = wctx->json; + n = (zebra_neigh_t *)bucket->data; + if (!n) + return; + + ipaddr2str(&n->ip, buf, sizeof(buf)); + if (json_evpn) + json_row = json_object_new_object(); + + zebra_evpn_print_neigh(n, vty, json_row); + + if (json_evpn) + json_object_object_add(json_evpn, buf, json_row); +} + +void zebra_evpn_print_dad_neigh_hash(struct hash_bucket *bucket, void *ctxt) +{ + zebra_neigh_t *nbr; + + nbr = (zebra_neigh_t *)bucket->data; + if (!nbr) + return; + + if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) + zebra_evpn_print_neigh_hash(bucket, ctxt); +} + +void zebra_evpn_print_dad_neigh_hash_detail(struct hash_bucket *bucket, + void *ctxt) +{ + zebra_neigh_t *nbr; + + nbr = (zebra_neigh_t *)bucket->data; + if (!nbr) + return; + + if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) + zebra_evpn_print_neigh_hash_detail(bucket, ctxt); +} + +void process_neigh_remote_macip_add(zebra_evpn_t *zevpn, struct zebra_vrf *zvrf, + struct ipaddr *ipaddr, zebra_mac_t *mac, + struct in_addr vtep_ip, uint8_t flags, + uint32_t seq) +{ + zebra_neigh_t *n; + int update_neigh = 0; + uint32_t tmp_seq; + char buf[ETHER_ADDR_STRLEN]; + char buf1[INET6_ADDRSTRLEN]; + zebra_mac_t *old_mac = NULL; + bool old_static = false; + bool do_dad = false; + bool is_dup_detect = false; + bool is_router; + + assert(mac); + is_router = !!CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_ROUTER_FLAG); + + /* Check if the remote neighbor itself is unknown or has a + * change. If so, create or update and then install the entry. + */ + n = zebra_evpn_neigh_lookup(zevpn, ipaddr); + if (!n || !CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE) + || is_router != !!CHECK_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG) + || (memcmp(&n->emac, &mac->macaddr, sizeof(struct ethaddr)) != 0) + || !IPV4_ADDR_SAME(&n->r_vtep_ip, &vtep_ip) || seq != n->rem_seq) + update_neigh = 1; + + if (update_neigh) { + if (!n) { + n = zebra_evpn_neigh_add(zevpn, ipaddr, &mac->macaddr, + mac, 0); + if (!n) { + zlog_warn( + "Failed to add Neigh %s MAC %s VNI %u Remote VTEP %s", + ipaddr2str(ipaddr, buf1, sizeof(buf1)), + prefix_mac2str(&mac->macaddr, buf, + sizeof(buf)), + zevpn->vni, inet_ntoa(vtep_ip)); + return; + } + + } else { + const char *n_type; + + /* When host moves but changes its (MAC,IP) + * binding, BGP may install a MACIP entry that + * corresponds to "older" location of the host + * in transient situations (because {IP1,M1} + * is a different route from {IP1,M2}). Check + * the sequence number and ignore this update + * if appropriate. + */ + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { + tmp_seq = n->loc_seq; + n_type = "local"; + } else { + tmp_seq = n->rem_seq; + n_type = "remote"; + } + if (seq < tmp_seq) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Ignore remote MACIP ADD VNI %u MAC %s%s%s as existing %s Neigh has higher seq %u", + zevpn->vni, + prefix_mac2str(&mac->macaddr, + buf, + sizeof(buf)), + " IP ", + ipaddr2str(ipaddr, buf1, + sizeof(buf1)), + n_type, tmp_seq); + return; + } + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { + old_static = zebra_evpn_neigh_is_static(n); + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug( + "sync->remote neigh vni %u ip %s mac %s seq %d f0x%x", + n->zevpn->vni, + ipaddr2str(&n->ip, buf1, + sizeof(buf1)), + prefix_mac2str(&n->emac, buf, + sizeof(buf)), + seq, n->flags); + zebra_evpn_neigh_clear_sync_info(n); + if (IS_ZEBRA_NEIGH_ACTIVE(n)) + zebra_evpn_neigh_send_del_to_client( + zevpn->vni, &n->ip, &n->emac, + n->flags, n->state, + false /*force*/); + } + if (memcmp(&n->emac, &mac->macaddr, + sizeof(struct ethaddr)) + != 0) { + /* update neigh list for macs */ + old_mac = + zebra_evpn_mac_lookup(zevpn, &n->emac); + if (old_mac) { + listnode_delete(old_mac->neigh_list, n); + n->mac = NULL; + zebra_evpn_deref_ip2mac(zevpn, old_mac); + } + n->mac = mac; + listnode_add_sort(mac->neigh_list, n); + memcpy(&n->emac, &mac->macaddr, ETH_ALEN); + + /* Check Neigh's curent state is local + * (this is the case where neigh/host has moved + * from L->R) and check previous detction + * started via local learning. + * + * RFC-7432: A PE/VTEP that detects a MAC + * mobilit event via local learning starts + * an M-second timer. + * VTEP-IP or seq. change along is not + * considered for dup. detection. + * + * Mobilty event scenario-B IP-MAC binding + * changed. + */ + if ((!CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) + && n->dad_count) + do_dad = true; + } + } + + /* Set "remote" forwarding info. */ + UNSET_FLAG(n->flags, ZEBRA_NEIGH_ALL_LOCAL_FLAGS); + n->r_vtep_ip = vtep_ip; + SET_FLAG(n->flags, ZEBRA_NEIGH_REMOTE); + + /* Set router flag (R-bit) to this Neighbor entry */ + if (CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_ROUTER_FLAG)) + SET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); + else + UNSET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); + + /* Check old or new MAC detected as duplicate, + * inherit duplicate flag to this neigh. + */ + if (zebra_evpn_ip_inherit_dad_from_mac(zvrf, old_mac, mac, n)) { + flog_warn( + EC_ZEBRA_DUP_IP_INHERIT_DETECTED, + "VNI %u: MAC %s IP %s detected as duplicate during remote update, inherit duplicate from MAC", + zevpn->vni, + prefix_mac2str(&mac->macaddr, buf, sizeof(buf)), + ipaddr2str(&n->ip, buf1, sizeof(buf1))); + } + + /* Check duplicate address detection for IP */ + zebra_evpn_dup_addr_detect_for_neigh( + zvrf, n, n->r_vtep_ip, do_dad, &is_dup_detect, false); + /* Install the entry. */ + if (!is_dup_detect) + zebra_evpn_rem_neigh_install(zevpn, n, old_static); + } + + /* Update seq number. */ + n->rem_seq = seq; +} + +int zebra_evpn_neigh_gw_macip_add(struct interface *ifp, zebra_evpn_t *zevpn, + struct ipaddr *ip, zebra_mac_t *mac) +{ + zebra_neigh_t *n; + char buf[ETHER_ADDR_STRLEN]; + char buf2[INET6_ADDRSTRLEN]; + + assert(mac); + + n = zebra_evpn_neigh_lookup(zevpn, ip); + if (!n) { + n = zebra_evpn_neigh_add(zevpn, ip, &mac->macaddr, mac, 0); + if (!n) { + flog_err( + EC_ZEBRA_MAC_ADD_FAILED, + "Failed to add neighbor %s MAC %s intf %s(%u) -> VNI %u", + ipaddr2str(ip, buf2, sizeof(buf2)), + prefix_mac2str(&mac->macaddr, buf, sizeof(buf)), + ifp->name, ifp->ifindex, zevpn->vni); + return -1; + } + } + + /* Set "local" forwarding info. */ + SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL); + ZEBRA_NEIGH_SET_ACTIVE(n); + memcpy(&n->emac, &mac->macaddr, ETH_ALEN); + n->ifindex = ifp->ifindex; + + /* Only advertise in BGP if the knob is enabled */ + if (advertise_gw_macip_enabled(zevpn)) { + + SET_FLAG(mac->flags, ZEBRA_MAC_DEF_GW); + SET_FLAG(n->flags, ZEBRA_NEIGH_DEF_GW); + /* Set Router flag (R-bit) */ + if (ip->ipa_type == IPADDR_V6) + SET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "SVI %s(%u) L2-VNI %u, sending GW MAC %s IP %s add to BGP with flags 0x%x", + ifp->name, ifp->ifindex, zevpn->vni, + prefix_mac2str(&mac->macaddr, buf, sizeof(buf)), + ipaddr2str(ip, buf2, sizeof(buf2)), n->flags); + + zebra_evpn_neigh_send_add_to_client( + zevpn->vni, ip, &n->emac, n->mac, n->flags, n->loc_seq); + } else if (advertise_svi_macip_enabled(zevpn)) { + + SET_FLAG(n->flags, ZEBRA_NEIGH_SVI_IP); + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "SVI %s(%u) L2-VNI %u, sending SVI MAC %s IP %s add to BGP with flags 0x%x", + ifp->name, ifp->ifindex, zevpn->vni, + prefix_mac2str(&mac->macaddr, buf, sizeof(buf)), + ipaddr2str(ip, buf2, sizeof(buf2)), n->flags); + + zebra_evpn_neigh_send_add_to_client( + zevpn->vni, ip, &n->emac, n->mac, n->flags, n->loc_seq); + } + + return 0; +} + +void zebra_evpn_neigh_remote_uninstall(zebra_evpn_t *zevpn, + struct zebra_vrf *zvrf, zebra_neigh_t *n, + zebra_mac_t *mac, struct ipaddr *ipaddr) +{ + char buf1[INET6_ADDRSTRLEN]; + + if (zvrf->dad_freeze && CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE) + && CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE) + && (memcmp(n->emac.octet, mac->macaddr.octet, ETH_ALEN) == 0)) { + struct interface *vlan_if; + + vlan_if = zevpn_map_to_svi(zevpn); + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%s: IP %s (flags 0x%x intf %s) is remote and duplicate, read kernel for local entry", + __func__, + ipaddr2str(ipaddr, buf1, sizeof(buf1)), + n->flags, vlan_if ? vlan_if->name : "Unknown"); + if (vlan_if) + neigh_read_specific_ip(ipaddr, vlan_if); + } + + /* When the MAC changes for an IP, it is possible the + * client may update the new MAC before trying to delete the + * "old" neighbor (as these are two different MACIP routes). + * Do the delete only if the MAC matches. + */ + if (!memcmp(n->emac.octet, mac->macaddr.octet, ETH_ALEN)) { + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { + zebra_evpn_sync_neigh_del(n); + } else if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) { + zebra_evpn_neigh_uninstall(zevpn, n); + zebra_evpn_neigh_del(zevpn, n); + zebra_evpn_deref_ip2mac(zevpn, mac); + } + } +} + +int zebra_evpn_neigh_del_ip(zebra_evpn_t *zevpn, struct ipaddr *ip) +{ + zebra_neigh_t *n; + zebra_mac_t *zmac; + bool old_bgp_ready; + bool new_bgp_ready; + char buf[INET6_ADDRSTRLEN]; + char buf2[ETHER_ADDR_STRLEN]; + struct zebra_vrf *zvrf; + + /* If entry doesn't exist, nothing to do. */ + n = zebra_evpn_neigh_lookup(zevpn, ip); + if (!n) + return 0; + + zmac = zebra_evpn_mac_lookup(zevpn, &n->emac); + if (!zmac) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Trying to del a neigh %s without a mac %s on VNI %u", + ipaddr2str(ip, buf, sizeof(buf)), + prefix_mac2str(&n->emac, buf2, sizeof(buf2)), + zevpn->vni); + + return 0; + } + + /* If it is a remote entry, the kernel has aged this out or someone has + * deleted it, it needs to be re-installed as Quagga is the owner. + */ + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) { + zebra_evpn_rem_neigh_install(zevpn, n, false /*was_static*/); + return 0; + } + + /* if this is a sync entry it cannot be dropped re-install it in + * the dataplane + */ + old_bgp_ready = zebra_evpn_neigh_is_ready_for_bgp(n); + if (zebra_evpn_neigh_is_static(n)) { + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug("re-add sync neigh vni %u ip %s mac %s 0x%x", + n->zevpn->vni, + ipaddr2str(&n->ip, buf, sizeof(buf)), + prefix_mac2str(&n->emac, buf2, sizeof(buf2)), + n->flags); + + if (!CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL_INACTIVE)) + SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL_INACTIVE); + /* inform-bgp about change in local-activity if any */ + new_bgp_ready = zebra_evpn_neigh_is_ready_for_bgp(n); + zebra_evpn_neigh_send_add_del_to_client(n, old_bgp_ready, + new_bgp_ready); + + /* re-install the entry in the kernel */ + zebra_evpn_sync_neigh_dp_install(n, false /* set_inactive */, + false /* force_clear_static */, + __func__); + + return 0; + } + + zvrf = vrf_info_lookup(zevpn->vxlan_if->vrf_id); + if (!zvrf) { + zlog_debug("%s: VNI %u vrf lookup failed.", __func__, + zevpn->vni); + return -1; + } + + /* In case of feeze action, if local neigh is in duplicate state, + * Mark the Neigh as inactive before sending delete request to BGPd, + * If BGPd has remote entry, it will re-install + */ + if (zvrf->dad_freeze && CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE)) + ZEBRA_NEIGH_SET_INACTIVE(n); + + /* Remove neighbor from BGP. */ + zebra_evpn_neigh_send_del_to_client(zevpn->vni, &n->ip, &n->emac, + n->flags, n->state, + false /* force */); + + /* Delete this neighbor entry. */ + zebra_evpn_neigh_del(zevpn, n); + + /* see if the AUTO mac needs to be deleted */ + if (CHECK_FLAG(zmac->flags, ZEBRA_MAC_AUTO) + && !listcount(zmac->neigh_list)) + zebra_evpn_mac_del(zevpn, zmac); + + return 0; +} diff --git a/zebra/zebra_evpn_neigh.h b/zebra/zebra_evpn_neigh.h new file mode 100644 index 0000000000..4b98266c86 --- /dev/null +++ b/zebra/zebra_evpn_neigh.h @@ -0,0 +1,294 @@ +/* + * Zebra EVPN Neighbor Data structures and definitions + * These are "internal" to this function. + * Copyright (C) 2016, 2017 Cumulus Networks, Inc. + * Copyright (C) 2020 Volta Networks. + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef _ZEBRA_EVPN_NEIGH_H +#define _ZEBRA_EVPN_NEIGH_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct zebra_neigh_t_ zebra_neigh_t; + +#define IS_ZEBRA_NEIGH_ACTIVE(n) (n->state == ZEBRA_NEIGH_ACTIVE) + +#define IS_ZEBRA_NEIGH_INACTIVE(n) (n->state == ZEBRA_NEIGH_INACTIVE) + +#define ZEBRA_NEIGH_SET_ACTIVE(n) n->state = ZEBRA_NEIGH_ACTIVE + +#define ZEBRA_NEIGH_SET_INACTIVE(n) n->state = ZEBRA_NEIGH_INACTIVE + +/* + * Neighbor hash table. + * + * This table contains the neighbors (IP to MAC bindings) pertaining to + * this VNI. This includes local neighbors learnt on the attached VLAN + * device that maps to this VNI as well as remote neighbors learnt and + * installed by BGP. + * Local neighbors will be known against the VLAN device (SVI); however, + * it is sufficient for zebra to maintain against the VNI. The correct + * VNI will be obtained as zebra maintains the mapping (of VLAN to VNI). + */ +struct zebra_neigh_t_ { + /* IP address. */ + struct ipaddr ip; + + /* MAC address. */ + struct ethaddr emac; + + /* Back pointer to MAC. Only applicable to hosts in a L2-VNI. */ + zebra_mac_t *mac; + + /* Underlying interface. */ + ifindex_t ifindex; + + zebra_evpn_t *zevpn; + + uint32_t flags; +#define ZEBRA_NEIGH_LOCAL 0x01 +#define ZEBRA_NEIGH_REMOTE 0x02 +#define ZEBRA_NEIGH_REMOTE_NH 0x04 /* neigh entry for remote vtep */ +#define ZEBRA_NEIGH_DEF_GW 0x08 +#define ZEBRA_NEIGH_ROUTER_FLAG 0x10 +#define ZEBRA_NEIGH_DUPLICATE 0x20 +#define ZEBRA_NEIGH_SVI_IP 0x40 +/* rxed from an ES peer */ +#define ZEBRA_NEIGH_ES_PEER_ACTIVE 0x80 +/* rxed from an ES peer as a proxy advertisement */ +#define ZEBRA_NEIGH_ES_PEER_PROXY 0x100 +/* We have not been able to independently establish that the host + * is local connected + */ +#define ZEBRA_NEIGH_LOCAL_INACTIVE 0x200 +#define ZEBRA_NEIGH_ALL_LOCAL_FLAGS \ + (ZEBRA_NEIGH_LOCAL | ZEBRA_NEIGH_LOCAL_INACTIVE) +#define ZEBRA_NEIGH_ALL_PEER_FLAGS \ + (ZEBRA_NEIGH_ES_PEER_PROXY | ZEBRA_NEIGH_ES_PEER_ACTIVE) + + enum zebra_neigh_state state; + + /* Remote VTEP IP - applicable only for remote neighbors. */ + struct in_addr r_vtep_ip; + + /* + * Mobility sequence numbers associated with this entry. The rem_seq + * represents the sequence number from the client (BGP) for the most + * recent add or update of this entry while the loc_seq represents + * the sequence number informed (or to be informed) by zebra to BGP + * for this entry. + */ + uint32_t rem_seq; + uint32_t loc_seq; + + /* list of hosts pointing to this remote NH entry */ + struct host_rb_tree_entry host_rb; + + /* Duplicate ip detection */ + uint32_t dad_count; + + struct thread *dad_ip_auto_recovery_timer; + + struct timeval detect_start_time; + + time_t dad_dup_detect_time; + + /* used for ageing out the PEER_ACTIVE flag */ + struct thread *hold_timer; +}; + +/* + * Context for neighbor hash walk - used by callbacks. + */ +struct neigh_walk_ctx { + zebra_evpn_t *zevpn; /* VNI hash */ + struct zebra_vrf *zvrf; /* VRF - for client notification. */ + int uninstall; /* uninstall from kernel? */ + int upd_client; /* uninstall from client? */ + + uint32_t flags; +#define DEL_LOCAL_NEIGH 0x1 +#define DEL_REMOTE_NEIGH 0x2 +#define DEL_ALL_NEIGH (DEL_LOCAL_NEIGH | DEL_REMOTE_NEIGH) +#define DEL_REMOTE_NEIGH_FROM_VTEP 0x4 +#define SHOW_REMOTE_NEIGH_FROM_VTEP 0x8 + + struct in_addr r_vtep_ip; /* To walk neighbors from specific VTEP */ + + struct vty *vty; /* Used by VTY handlers */ + uint32_t count; /* Used by VTY handlers */ + uint8_t addr_width; /* Used by VTY handlers */ + struct json_object *json; /* Used for JSON Output */ +}; + +/**************************** SYNC neigh handling **************************/ +static inline bool zebra_evpn_neigh_is_static(zebra_neigh_t *neigh) +{ + return !!(neigh->flags & ZEBRA_NEIGH_ALL_PEER_FLAGS); +} + +static inline bool zebra_evpn_neigh_is_ready_for_bgp(zebra_neigh_t *n) +{ + bool mac_ready; + bool neigh_ready; + + mac_ready = !!(n->mac->flags & ZEBRA_MAC_LOCAL); + neigh_ready = + ((n->flags & ZEBRA_NEIGH_LOCAL) && IS_ZEBRA_NEIGH_ACTIVE(n) + && (!(n->flags & ZEBRA_NEIGH_LOCAL_INACTIVE) + || (n->flags & ZEBRA_NEIGH_ES_PEER_ACTIVE))) + ? true + : false; + + return mac_ready && neigh_ready; +} + +static inline void zebra_evpn_neigh_stop_hold_timer(zebra_neigh_t *n) +{ + char macbuf[ETHER_ADDR_STRLEN]; + char ipbuf[INET6_ADDRSTRLEN]; + + if (!n->hold_timer) + return; + + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug("sync-neigh vni %u ip %s mac %s 0x%x hold stop", + n->zevpn->vni, + ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)), + prefix_mac2str(&n->emac, macbuf, sizeof(macbuf)), + n->flags); + THREAD_OFF(n->hold_timer); +} + +void zebra_evpn_sync_neigh_static_chg(zebra_neigh_t *n, bool old_n_static, + bool new_n_static, bool defer_n_dp, + bool defer_mac_dp, const char *caller); + +static inline bool zebra_evpn_neigh_clear_sync_info(zebra_neigh_t *n) +{ + char macbuf[ETHER_ADDR_STRLEN]; + char ipbuf[INET6_ADDRSTRLEN]; + bool old_n_static = false; + bool new_n_static = false; + + if (n->flags & ZEBRA_NEIGH_ALL_PEER_FLAGS) { + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug("sync-neigh vni %u ip %s mac %s 0x%x clear", + n->zevpn->vni, + ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)), + prefix_mac2str(&n->emac, macbuf, + sizeof(macbuf)), + n->flags); + + old_n_static = zebra_evpn_neigh_is_static(n); + UNSET_FLAG(n->flags, ZEBRA_NEIGH_ALL_PEER_FLAGS); + new_n_static = zebra_evpn_neigh_is_static(n); + if (old_n_static != new_n_static) + zebra_evpn_sync_neigh_static_chg( + n, old_n_static, new_n_static, + true /*defer_dp)*/, false /*defer_mac_dp*/, + __func__); + } + zebra_evpn_neigh_stop_hold_timer(n); + + /* if the neigh static flag changed inform that a dp + * re-install maybe needed + */ + return old_n_static != new_n_static; +} + +int remote_neigh_count(zebra_mac_t *zmac); + +int neigh_list_cmp(void *p1, void *p2); +struct hash *zebra_neigh_db_create(const char *desc); +uint32_t num_dup_detected_neighs(zebra_evpn_t *zevpn); +void zebra_evpn_find_neigh_addr_width(struct hash_bucket *bucket, void *ctxt); +int remote_neigh_count(zebra_mac_t *zmac); +int zebra_evpn_rem_neigh_install(zebra_evpn_t *zevpn, zebra_neigh_t *n, + bool was_static); +void zebra_evpn_install_neigh_hash(struct hash_bucket *bucket, void *ctxt); +int zebra_evpn_neigh_send_add_to_client(vni_t vni, struct ipaddr *ip, + struct ethaddr *macaddr, + zebra_mac_t *zmac, uint32_t neigh_flags, + uint32_t seq); +int zebra_evpn_neigh_send_del_to_client(vni_t vni, struct ipaddr *ip, + struct ethaddr *macaddr, uint32_t flags, + int state, bool force); +bool zebra_evpn_neigh_is_bgp_seq_ok(zebra_evpn_t *zevpn, zebra_neigh_t *n, + struct ethaddr *macaddr, uint32_t seq); +int zebra_evpn_neigh_del(zebra_evpn_t *zevpn, zebra_neigh_t *n); +void zebra_evpn_sync_neigh_del(zebra_neigh_t *n); +zebra_neigh_t * +zebra_evpn_proc_sync_neigh_update(zebra_evpn_t *zevpn, zebra_neigh_t *n, + uint16_t ipa_len, struct ipaddr *ipaddr, + uint8_t flags, uint32_t seq, esi_t *esi, + struct sync_mac_ip_ctx *ctx); +void zebra_evpn_neigh_del_all(zebra_evpn_t *zevpn, int uninstall, + int upd_client, uint32_t flags); +zebra_neigh_t *zebra_evpn_neigh_lookup(zebra_evpn_t *zevpn, struct ipaddr *ip); + +int zebra_evpn_rem_neigh_install(zebra_evpn_t *zevpn, zebra_neigh_t *n, + bool was_static); +void zebra_evpn_process_neigh_on_remote_mac_add(zebra_evpn_t *zevpn, + zebra_mac_t *zmac); +void zebra_evpn_process_neigh_on_local_mac_del(zebra_evpn_t *zevpn, + zebra_mac_t *zmac); +void zebra_evpn_process_neigh_on_local_mac_change(zebra_evpn_t *zevpn, + zebra_mac_t *zmac, + bool seq_change, + bool es_change); +void zebra_evpn_process_neigh_on_remote_mac_del(zebra_evpn_t *zevpn, + zebra_mac_t *zmac); +int zebra_evpn_local_neigh_update(zebra_evpn_t *zevpn, struct interface *ifp, + struct ipaddr *ip, struct ethaddr *macaddr, + bool is_router, bool local_inactive, + bool dp_static); +int zebra_evpn_remote_neigh_update(zebra_evpn_t *zevpn, struct interface *ifp, + struct ipaddr *ip, struct ethaddr *macaddr, + uint16_t state); +void zebra_evpn_send_neigh_to_client(zebra_evpn_t *zevpn); +void zebra_evpn_clear_dup_neigh_hash(struct hash_bucket *bucket, void *ctxt); +void zebra_evpn_print_neigh(zebra_neigh_t *n, void *ctxt, json_object *json); +void zebra_evpn_print_neigh_hash(struct hash_bucket *bucket, void *ctxt); +void zebra_evpn_print_neigh_hdr(struct vty *vty, struct neigh_walk_ctx *wctx); +void zebra_evpn_print_neigh_hash_detail(struct hash_bucket *bucket, void *ctxt); +void zebra_evpn_print_dad_neigh_hash(struct hash_bucket *bucket, void *ctxt); +void zebra_evpn_print_dad_neigh_hash_detail(struct hash_bucket *bucket, + void *ctxt); +void process_neigh_remote_macip_add(zebra_evpn_t *zevpn, struct zebra_vrf *zvrf, + struct ipaddr *ipaddr, zebra_mac_t *mac, + struct in_addr vtep_ip, uint8_t flags, + uint32_t seq); +int zebra_evpn_neigh_gw_macip_add(struct interface *ifp, zebra_evpn_t *zevpn, + struct ipaddr *ip, zebra_mac_t *mac); +void zebra_evpn_neigh_remote_uninstall(zebra_evpn_t *zevpn, + struct zebra_vrf *zvrf, zebra_neigh_t *n, + zebra_mac_t *mac, struct ipaddr *ipaddr); +int zebra_evpn_neigh_del_ip(zebra_evpn_t *zevpn, struct ipaddr *ip); + + +#ifdef __cplusplus +} +#endif + +#endif /*_ZEBRA_EVPN_NEIGH_H */ diff --git a/zebra/zebra_evpn_vxlan.h b/zebra/zebra_evpn_vxlan.h new file mode 100644 index 0000000000..bf8904d492 --- /dev/null +++ b/zebra/zebra_evpn_vxlan.h @@ -0,0 +1,71 @@ +/* + * Zebra EVPN for VxLAN code + * Copyright (C) 2016, 2017 Cumulus Networks, Inc. + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +/* Get the VRR interface for SVI if any */ +static inline struct interface * +zebra_get_vrr_intf_for_svi(struct interface *ifp) +{ + struct zebra_vrf *zvrf = NULL; + struct interface *tmp_if = NULL; + struct zebra_if *zif = NULL; + + zvrf = vrf_info_lookup(ifp->vrf_id); + assert(zvrf); + + FOR_ALL_INTERFACES (zvrf->vrf, tmp_if) { + zif = tmp_if->info; + if (!zif) + continue; + + if (!IS_ZEBRA_IF_MACVLAN(tmp_if)) + continue; + + if (zif->link == ifp) + return tmp_if; + } + + return NULL; +} + +/* EVPN<=>vxlan_zif association */ +static inline void zevpn_vxlan_if_set(zebra_evpn_t *zevpn, + struct interface *ifp, bool set) +{ + struct zebra_if *zif; + + if (set) { + if (zevpn->vxlan_if == ifp) + return; + zevpn->vxlan_if = ifp; + } else { + if (!zevpn->vxlan_if) + return; + zevpn->vxlan_if = NULL; + } + + if (ifp) + zif = ifp->info; + else + zif = NULL; + + zebra_evpn_vxl_evpn_set(zif, zevpn, set); +} diff --git a/zebra/zebra_fpm.c b/zebra/zebra_fpm.c index 35a5d69ee3..7b0611bf93 100644 --- a/zebra/zebra_fpm.c +++ b/zebra/zebra_fpm.c @@ -30,15 +30,21 @@ #include "network.h" #include "command.h" #include "version.h" +#include "jhash.h" #include "zebra/rib.h" #include "zebra/zserv.h" #include "zebra/zebra_ns.h" #include "zebra/zebra_vrf.h" #include "zebra/zebra_errors.h" +#include "zebra/zebra_memory.h" #include "fpm/fpm.h" #include "zebra_fpm_private.h" +#include "zebra/zebra_router.h" +#include "zebra_vxlan_private.h" + +DEFINE_MTYPE_STATIC(ZEBRA, FPM_MAC_INFO, "FPM_MAC_INFO"); /* * Interval at which we attempt to connect to the FPM. @@ -62,20 +68,23 @@ * Interval over which we collect statistics. */ #define ZFPM_STATS_IVL_SECS 10 +#define FPM_MAX_MAC_MSG_LEN 512 + +static void zfpm_iterate_rmac_table(struct hash_bucket *backet, void *args); /* * Structure that holds state for iterating over all route_node * structures that are candidates for being communicated to the FPM. */ -typedef struct zfpm_rnodes_iter_t_ { +struct zfpm_rnodes_iter { rib_tables_iter_t tables_iter; route_table_iter_t iter; -} zfpm_rnodes_iter_t; +}; /* * Statistics. */ -typedef struct zfpm_stats_t_ { +struct zfpm_stats { unsigned long connect_calls; unsigned long connect_no_sock; @@ -106,13 +115,12 @@ typedef struct zfpm_stats_t_ { unsigned long t_conn_up_yields; unsigned long t_conn_up_aborts; unsigned long t_conn_up_finishes; - -} zfpm_stats_t; +}; /* * States for the FPM state machine. */ -typedef enum { +enum zfpm_state { /* * In this state we are not yet ready to connect to the FPM. This @@ -138,20 +146,21 @@ typedef enum { */ ZFPM_STATE_ESTABLISHED -} zfpm_state_t; +}; /* * Message format to be used to communicate with the FPM. */ -typedef enum { +enum zfpm_msg_format { ZFPM_MSG_FORMAT_NONE, ZFPM_MSG_FORMAT_NETLINK, ZFPM_MSG_FORMAT_PROTOBUF, -} zfpm_msg_format_e; +}; + /* * Globals. */ -typedef struct zfpm_glob_t_ { +struct zfpm_glob { /* * True if the FPM module has been enabled. @@ -161,11 +170,11 @@ typedef struct zfpm_glob_t_ { /* * Message format to be used to communicate with the fpm. */ - zfpm_msg_format_e message_format; + enum zfpm_msg_format message_format; struct thread_master *master; - zfpm_state_t state; + enum zfpm_state state; in_addr_t fpm_server; /* @@ -178,6 +187,25 @@ typedef struct zfpm_glob_t_ { */ TAILQ_HEAD(zfpm_dest_q, rib_dest_t_) dest_q; + /* + * List of fpm_mac_info structures to be processed + */ + TAILQ_HEAD(zfpm_mac_q, fpm_mac_info_t) mac_q; + + /* + * Hash table of fpm_mac_info_t entries + * + * While adding fpm_mac_info_t for a MAC to the mac_q, + * it is possible that another fpm_mac_info_t node for the this MAC + * is already present in the queue. + * This is possible in the case of consecutive add->delete operations. + * To avoid such duplicate insertions in the mac_q, + * define a hash table for fpm_mac_info_t which can be looked up + * to see if an fpm_mac_info_t node for a MAC is already present + * in the mac_q. + */ + struct hash *fpm_mac_info_table; + /* * Stream socket to the FPM. */ @@ -203,7 +231,7 @@ typedef struct zfpm_glob_t_ { struct thread *t_conn_down; struct { - zfpm_rnodes_iter_t iter; + struct zfpm_rnodes_iter iter; } t_conn_down_state; /* @@ -213,7 +241,7 @@ typedef struct zfpm_glob_t_ { struct thread *t_conn_up; struct { - zfpm_rnodes_iter_t iter; + struct zfpm_rnodes_iter iter; } t_conn_up_state; unsigned long connect_calls; @@ -223,18 +251,18 @@ typedef struct zfpm_glob_t_ { * Stats from the start of the current statistics interval up to * now. These are the counters we typically update in the code. */ - zfpm_stats_t stats; + struct zfpm_stats stats; /* * Statistics that were gathered in the last collection interval. */ - zfpm_stats_t last_ivl_stats; + struct zfpm_stats last_ivl_stats; /* * Cumulative stats from the last clear to the start of the current * statistics interval. */ - zfpm_stats_t cumulative_stats; + struct zfpm_stats cumulative_stats; /* * Stats interval timer. @@ -245,20 +273,20 @@ typedef struct zfpm_glob_t_ { * If non-zero, the last time when statistics were cleared. */ time_t last_stats_clear_time; +}; -} zfpm_glob_t; - -static zfpm_glob_t zfpm_glob_space; -static zfpm_glob_t *zfpm_g = &zfpm_glob_space; +static struct zfpm_glob zfpm_glob_space; +static struct zfpm_glob *zfpm_g = &zfpm_glob_space; static int zfpm_trigger_update(struct route_node *rn, const char *reason); static int zfpm_read_cb(struct thread *thread); static int zfpm_write_cb(struct thread *thread); -static void zfpm_set_state(zfpm_state_t state, const char *reason); +static void zfpm_set_state(enum zfpm_state state, const char *reason); static void zfpm_start_connect_timer(const char *reason); static void zfpm_start_stats_timer(void); +static void zfpm_mac_info_del(struct fpm_mac_info_t *fpm_mac); /* * zfpm_thread_should_yield @@ -271,7 +299,7 @@ static inline int zfpm_thread_should_yield(struct thread *t) /* * zfpm_state_to_str */ -static const char *zfpm_state_to_str(zfpm_state_t state) +static const char *zfpm_state_to_str(enum zfpm_state state) { switch (state) { @@ -314,7 +342,7 @@ static time_t zfpm_get_elapsed_time(time_t reference) /* * zfpm_rnodes_iter_init */ -static inline void zfpm_rnodes_iter_init(zfpm_rnodes_iter_t *iter) +static inline void zfpm_rnodes_iter_init(struct zfpm_rnodes_iter *iter) { memset(iter, 0, sizeof(*iter)); rib_tables_iter_init(&iter->tables_iter); @@ -331,7 +359,8 @@ static inline void zfpm_rnodes_iter_init(zfpm_rnodes_iter_t *iter) /* * zfpm_rnodes_iter_next */ -static inline struct route_node *zfpm_rnodes_iter_next(zfpm_rnodes_iter_t *iter) +static inline struct route_node * +zfpm_rnodes_iter_next(struct zfpm_rnodes_iter *iter) { struct route_node *rn; struct route_table *table; @@ -360,7 +389,7 @@ static inline struct route_node *zfpm_rnodes_iter_next(zfpm_rnodes_iter_t *iter) /* * zfpm_rnodes_iter_pause */ -static inline void zfpm_rnodes_iter_pause(zfpm_rnodes_iter_t *iter) +static inline void zfpm_rnodes_iter_pause(struct zfpm_rnodes_iter *iter) { route_table_iter_pause(&iter->iter); } @@ -368,7 +397,7 @@ static inline void zfpm_rnodes_iter_pause(zfpm_rnodes_iter_t *iter) /* * zfpm_rnodes_iter_cleanup */ -static inline void zfpm_rnodes_iter_cleanup(zfpm_rnodes_iter_t *iter) +static inline void zfpm_rnodes_iter_cleanup(struct zfpm_rnodes_iter *iter) { route_table_iter_cleanup(&iter->iter); rib_tables_iter_cleanup(&iter->tables_iter); @@ -379,7 +408,7 @@ static inline void zfpm_rnodes_iter_cleanup(zfpm_rnodes_iter_t *iter) * * Initialize a statistics block. */ -static inline void zfpm_stats_init(zfpm_stats_t *stats) +static inline void zfpm_stats_init(struct zfpm_stats *stats) { memset(stats, 0, sizeof(*stats)); } @@ -387,7 +416,7 @@ static inline void zfpm_stats_init(zfpm_stats_t *stats) /* * zfpm_stats_reset */ -static inline void zfpm_stats_reset(zfpm_stats_t *stats) +static inline void zfpm_stats_reset(struct zfpm_stats *stats) { zfpm_stats_init(stats); } @@ -395,7 +424,8 @@ static inline void zfpm_stats_reset(zfpm_stats_t *stats) /* * zfpm_stats_copy */ -static inline void zfpm_stats_copy(const zfpm_stats_t *src, zfpm_stats_t *dest) +static inline void zfpm_stats_copy(const struct zfpm_stats *src, + struct zfpm_stats *dest) { memcpy(dest, src, sizeof(*dest)); } @@ -411,8 +441,9 @@ static inline void zfpm_stats_copy(const zfpm_stats_t *src, zfpm_stats_t *dest) * structure is composed entirely of counters. This can easily be * changed when necessary. */ -static void zfpm_stats_compose(const zfpm_stats_t *s1, const zfpm_stats_t *s2, - zfpm_stats_t *result) +static void zfpm_stats_compose(const struct zfpm_stats *s1, + const struct zfpm_stats *s2, + struct zfpm_stats *result) { const unsigned long *p1, *p2; unsigned long *result_p; @@ -422,7 +453,7 @@ static void zfpm_stats_compose(const zfpm_stats_t *s1, const zfpm_stats_t *s2, p2 = (const unsigned long *)s2; result_p = (unsigned long *)result; - num_counters = (sizeof(zfpm_stats_t) / sizeof(unsigned long)); + num_counters = (sizeof(struct zfpm_stats) / sizeof(unsigned long)); for (i = 0; i < num_counters; i++) { result_p[i] = p1[i] + p2[i]; @@ -469,6 +500,11 @@ static inline void zfpm_write_off(void) THREAD_WRITE_OFF(zfpm_g->t_write); } +static inline void zfpm_connect_off(void) +{ + THREAD_TIMER_OFF(zfpm_g->t_connect); +} + /* * zfpm_conn_up_thread_cb * @@ -478,7 +514,7 @@ static inline void zfpm_write_off(void) static int zfpm_conn_up_thread_cb(struct thread *thread) { struct route_node *rnode; - zfpm_rnodes_iter_t *iter; + struct zfpm_rnodes_iter *iter; rib_dest_t *dest; zfpm_g->t_conn_up = NULL; @@ -492,6 +528,9 @@ static int zfpm_conn_up_thread_cb(struct thread *thread) goto done; } + /* Enqueue FPM updates for all the RMAC entries */ + hash_iterate(zrouter.l3vni_table, zfpm_iterate_rmac_table, NULL); + while ((rnode = zfpm_rnodes_iter_next(iter))) { dest = rib_dest_from_rnode(rnode); @@ -589,11 +628,19 @@ static void zfpm_connect_check(void) static int zfpm_conn_down_thread_cb(struct thread *thread) { struct route_node *rnode; - zfpm_rnodes_iter_t *iter; + struct zfpm_rnodes_iter *iter; rib_dest_t *dest; + struct fpm_mac_info_t *mac = NULL; assert(zfpm_g->state == ZFPM_STATE_IDLE); + /* + * Delink and free all fpm_mac_info_t nodes + * in the mac_q and fpm_mac_info_hash + */ + while ((mac = TAILQ_FIRST(&zfpm_g->mac_q)) != NULL) + zfpm_mac_info_del(mac); + zfpm_g->t_conn_down = NULL; iter = &zfpm_g->t_conn_down_state.iter; @@ -671,7 +718,6 @@ static void zfpm_connection_down(const char *detail) * Start thread to clean up state after the connection goes down. */ assert(!zfpm_g->t_conn_down); - zfpm_debug("Starting conn_down thread"); zfpm_rnodes_iter_init(&zfpm_g->t_conn_down_state.iter); zfpm_g->t_conn_down = NULL; thread_add_timer_msec(zfpm_g->master, zfpm_conn_down_thread_cb, NULL, 0, @@ -692,7 +738,6 @@ static int zfpm_read_cb(struct thread *thread) fpm_msg_hdr_t *hdr; zfpm_g->stats.read_cb_calls++; - zfpm_g->t_read = NULL; /* * Check if async connect is now done. @@ -717,8 +762,9 @@ static int zfpm_read_cb(struct thread *thread) if (nbyte == -1) { char buffer[1024]; - sprintf(buffer, "closed socket in read(%d): %s", - errno, safe_strerror(errno)); + snprintf(buffer, sizeof(buffer), + "closed socket in read(%d): %s", errno, + safe_strerror(errno)); zfpm_connection_down(buffer); } else zfpm_connection_down("closed socket in read"); @@ -754,8 +800,9 @@ static int zfpm_read_cb(struct thread *thread) if (nbyte == -1) { char buffer[1024]; - sprintf(buffer, "failed to read message(%d) %s", - errno, safe_strerror(errno)); + snprintf(buffer, sizeof(buffer), + "failed to read message(%d) %s", errno, + safe_strerror(errno)); zfpm_connection_down(buffer); } else zfpm_connection_down("failed to read message"); @@ -766,8 +813,6 @@ static int zfpm_read_cb(struct thread *thread) goto done; } - zfpm_debug("Read out a full fpm message"); - /* * Just throw it away for now. */ @@ -778,10 +823,18 @@ static int zfpm_read_cb(struct thread *thread) return 0; } +static bool zfpm_updates_pending(void) +{ + if (!(TAILQ_EMPTY(&zfpm_g->dest_q)) || !(TAILQ_EMPTY(&zfpm_g->mac_q))) + return true; + + return false; +} + /* * zfpm_writes_pending * - * Returns TRUE if we may have something to write to the FPM. + * Returns true if we may have something to write to the FPM. */ static int zfpm_writes_pending(void) { @@ -794,9 +847,9 @@ static int zfpm_writes_pending(void) return 1; /* - * Check if there are any prefixes on the outbound queue. + * Check if there are any updates scheduled on the outbound queues. */ - if (!TAILQ_EMPTY(&zfpm_g->dest_q)) + if (zfpm_updates_pending()) return 1; return 0; @@ -861,12 +914,29 @@ struct route_entry *zfpm_route_for_update(rib_dest_t *dest) } /* - * zfpm_build_updates + * Define an enum for return codes for queue processing functions * - * Process the outgoing queue and write messages to the outbound - * buffer. + * FPM_WRITE_STOP: This return code indicates that the write buffer is full. + * Stop processing all the queues and empty the buffer by writing its content + * to the socket. + * + * FPM_GOTO_NEXT_Q: This return code indicates that either this queue is + * empty or we have processed enough updates from this queue. + * So, move on to the next queue. */ -static void zfpm_build_updates(void) +enum { + FPM_WRITE_STOP = 0, + FPM_GOTO_NEXT_Q = 1 +}; + +#define FPM_QUEUE_PROCESS_LIMIT 10000 + +/* + * zfpm_build_route_updates + * + * Process the dest_q queue and write FPM messages to the outbound buffer. + */ +static int zfpm_build_route_updates(void) { struct stream *s; rib_dest_t *dest; @@ -877,25 +947,27 @@ static void zfpm_build_updates(void) struct route_entry *re; int is_add, write_msg; fpm_msg_type_e msg_type; + uint16_t q_limit; - s = zfpm_g->obuf; + if (TAILQ_EMPTY(&zfpm_g->dest_q)) + return FPM_GOTO_NEXT_Q; - assert(stream_empty(s)); - - do { + s = zfpm_g->obuf; + q_limit = FPM_QUEUE_PROCESS_LIMIT; + do { /* * Make sure there is enough space to write another message. */ if (STREAM_WRITEABLE(s) < FPM_MAX_MSG_LEN) - break; + return FPM_WRITE_STOP; buf = STREAM_DATA(s) + stream_get_endp(s); buf_end = buf + STREAM_WRITEABLE(s); dest = TAILQ_FIRST(&zfpm_g->dest_q); if (!dest) - break; + return FPM_GOTO_NEXT_Q; assert(CHECK_FLAG(dest->flags, RIB_DEST_UPDATE_FPM)); @@ -923,7 +995,6 @@ static void zfpm_build_updates(void) data_len = zfpm_encode_route(dest, re, (char *)data, buf_end - data, &msg_type); - assert(data_len); if (data_len) { hdr->msg_type = msg_type; msg_len = fpm_data_len_to_msg_len(data_len); @@ -934,6 +1005,9 @@ static void zfpm_build_updates(void) zfpm_g->stats.route_adds++; else zfpm_g->stats.route_dels++; + } else { + zlog_err("%s: Encoding Prefix: %pRN No valid nexthops", + __func__, dest->rnode); } } @@ -955,9 +1029,135 @@ static void zfpm_build_updates(void) if (rib_gc_dest(dest->rnode)) zfpm_g->stats.dests_del_after_update++; + q_limit--; + if (q_limit == 0) { + /* + * We have processed enough updates in this queue. + * Now yield for other queues. + */ + return FPM_GOTO_NEXT_Q; + } + } while (true); +} + +/* + * zfpm_encode_mac + * + * Encode a message to FPM with information about the given MAC. + * + * Returns the number of bytes written to the buffer. + */ +static inline int zfpm_encode_mac(struct fpm_mac_info_t *mac, char *in_buf, + size_t in_buf_len, fpm_msg_type_e *msg_type) +{ + size_t len = 0; + + *msg_type = FPM_MSG_TYPE_NONE; + + switch (zfpm_g->message_format) { + + case ZFPM_MSG_FORMAT_NONE: + break; + case ZFPM_MSG_FORMAT_NETLINK: +#ifdef HAVE_NETLINK + len = zfpm_netlink_encode_mac(mac, in_buf, in_buf_len); + assert(fpm_msg_align(len) == len); + *msg_type = FPM_MSG_TYPE_NETLINK; +#endif /* HAVE_NETLINK */ + break; + case ZFPM_MSG_FORMAT_PROTOBUF: + break; + } + return len; +} + +static int zfpm_build_mac_updates(void) +{ + struct stream *s; + struct fpm_mac_info_t *mac; + unsigned char *buf, *data, *buf_end; + fpm_msg_hdr_t *hdr; + size_t data_len, msg_len; + fpm_msg_type_e msg_type; + uint16_t q_limit; + + if (TAILQ_EMPTY(&zfpm_g->mac_q)) + return FPM_GOTO_NEXT_Q; + + s = zfpm_g->obuf; + q_limit = FPM_QUEUE_PROCESS_LIMIT; + + do { + /* Make sure there is enough space to write another message. */ + if (STREAM_WRITEABLE(s) < FPM_MAX_MAC_MSG_LEN) + return FPM_WRITE_STOP; + + buf = STREAM_DATA(s) + stream_get_endp(s); + buf_end = buf + STREAM_WRITEABLE(s); + + mac = TAILQ_FIRST(&zfpm_g->mac_q); + if (!mac) + return FPM_GOTO_NEXT_Q; + + /* Check for no-op */ + if (!CHECK_FLAG(mac->fpm_flags, ZEBRA_MAC_UPDATE_FPM)) { + zfpm_g->stats.nop_deletes_skipped++; + zfpm_mac_info_del(mac); + continue; + } + + hdr = (fpm_msg_hdr_t *)buf; + hdr->version = FPM_PROTO_VERSION; + + data = fpm_msg_data(hdr); + data_len = zfpm_encode_mac(mac, (char *)data, buf_end - data, + &msg_type); + assert(data_len); + + hdr->msg_type = msg_type; + msg_len = fpm_data_len_to_msg_len(data_len); + hdr->msg_len = htons(msg_len); + stream_forward_endp(s, msg_len); + + /* Remove the MAC from the queue, and delete it. */ + zfpm_mac_info_del(mac); + + q_limit--; + if (q_limit == 0) { + /* + * We have processed enough updates in this queue. + * Now yield for other queues. + */ + return FPM_GOTO_NEXT_Q; + } } while (1); } +/* + * zfpm_build_updates + * + * Process the outgoing queues and write messages to the outbound + * buffer. + */ +static void zfpm_build_updates(void) +{ + struct stream *s; + + s = zfpm_g->obuf; + assert(stream_empty(s)); + + do { + /* + * Stop processing the queues if zfpm_g->obuf is full + * or we do not have more updates to process + */ + if (zfpm_build_mac_updates() == FPM_WRITE_STOP) + break; + if (zfpm_build_route_updates() == FPM_WRITE_STOP) + break; + } while (zfpm_updates_pending()); +} + /* * zfpm_write_cb */ @@ -967,7 +1167,6 @@ static int zfpm_write_cb(struct thread *thread) int num_writes; zfpm_g->stats.write_cb_calls++; - zfpm_g->t_write = NULL; /* * Check if async connect is now done. @@ -1051,12 +1250,11 @@ static int zfpm_connect_cb(struct thread *t) int sock, ret; struct sockaddr_in serv; - zfpm_g->t_connect = NULL; assert(zfpm_g->state == ZFPM_STATE_ACTIVE); sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0) { - zfpm_debug("Failed to create socket for connect(): %s", + zlog_err("Failed to create socket for connect(): %s", strerror(errno)); zfpm_g->stats.connect_no_sock++; return 0; @@ -1114,9 +1312,9 @@ static int zfpm_connect_cb(struct thread *t) * * Move state machine into the given state. */ -static void zfpm_set_state(zfpm_state_t state, const char *reason) +static void zfpm_set_state(enum zfpm_state state, const char *reason) { - zfpm_state_t cur_state = zfpm_g->state; + enum zfpm_state cur_state = zfpm_g->state; if (!reason) reason = "Unknown"; @@ -1210,7 +1408,7 @@ static void zfpm_start_connect_timer(const char *reason) /* * zfpm_is_enabled * - * Returns TRUE if the zebra FPM module has been enabled. + * Returns true if the zebra FPM module has been enabled. */ static inline int zfpm_is_enabled(void) { @@ -1220,7 +1418,7 @@ static inline int zfpm_is_enabled(void) /* * zfpm_conn_is_up * - * Returns TRUE if the connection to the FPM is up. + * Returns true if the connection to the FPM is up. */ static inline int zfpm_conn_is_up(void) { @@ -1277,7 +1475,185 @@ static int zfpm_trigger_update(struct route_node *rn, const char *reason) } /* - * zfpm_stats_timer_cb + * Generate Key for FPM MAC info hash entry + */ +static unsigned int zfpm_mac_info_hash_keymake(const void *p) +{ + struct fpm_mac_info_t *fpm_mac = (struct fpm_mac_info_t *)p; + uint32_t mac_key; + + mac_key = jhash(fpm_mac->macaddr.octet, ETH_ALEN, 0xa5a5a55a); + + return jhash_2words(mac_key, fpm_mac->vni, 0); +} + +/* + * Compare function for FPM MAC info hash lookup + */ +static bool zfpm_mac_info_cmp(const void *p1, const void *p2) +{ + const struct fpm_mac_info_t *fpm_mac1 = p1; + const struct fpm_mac_info_t *fpm_mac2 = p2; + + if (memcmp(fpm_mac1->macaddr.octet, fpm_mac2->macaddr.octet, ETH_ALEN) + != 0) + return false; + if (fpm_mac1->vni != fpm_mac2->vni) + return false; + + return true; +} + +/* + * Lookup FPM MAC info hash entry. + */ +static struct fpm_mac_info_t *zfpm_mac_info_lookup(struct fpm_mac_info_t *key) +{ + return hash_lookup(zfpm_g->fpm_mac_info_table, key); +} + +/* + * Callback to allocate fpm_mac_info_t structure. + */ +static void *zfpm_mac_info_alloc(void *p) +{ + const struct fpm_mac_info_t *key = p; + struct fpm_mac_info_t *fpm_mac; + + fpm_mac = XCALLOC(MTYPE_FPM_MAC_INFO, sizeof(struct fpm_mac_info_t)); + + memcpy(&fpm_mac->macaddr, &key->macaddr, ETH_ALEN); + fpm_mac->vni = key->vni; + + return (void *)fpm_mac; +} + +/* + * Delink and free fpm_mac_info_t. + */ +static void zfpm_mac_info_del(struct fpm_mac_info_t *fpm_mac) +{ + hash_release(zfpm_g->fpm_mac_info_table, fpm_mac); + TAILQ_REMOVE(&zfpm_g->mac_q, fpm_mac, fpm_mac_q_entries); + XFREE(MTYPE_FPM_MAC_INFO, fpm_mac); +} + +/* + * zfpm_trigger_rmac_update + * + * Zebra code invokes this function to indicate that we should + * send an update to FPM for given MAC entry. + * + * This function checks if we already have enqueued an update for this RMAC, + * If yes, update the same fpm_mac_info_t. Else, create and enqueue an update. + */ +static int zfpm_trigger_rmac_update(zebra_mac_t *rmac, zebra_l3vni_t *zl3vni, + bool delete, const char *reason) +{ + char buf[ETHER_ADDR_STRLEN]; + struct fpm_mac_info_t *fpm_mac, key; + struct interface *vxlan_if, *svi_if; + bool mac_found = false; + + /* + * Ignore if the connection is down. We will update the FPM about + * all destinations once the connection comes up. + */ + if (!zfpm_conn_is_up()) + return 0; + + if (reason) { + zfpm_debug("triggering update to FPM - Reason: %s - %s", + reason, + prefix_mac2str(&rmac->macaddr, buf, sizeof(buf))); + } + + vxlan_if = zl3vni_map_to_vxlan_if(zl3vni); + svi_if = zl3vni_map_to_svi_if(zl3vni); + + memset(&key, 0, sizeof(struct fpm_mac_info_t)); + + memcpy(&key.macaddr, &rmac->macaddr, ETH_ALEN); + key.vni = zl3vni->vni; + + /* Check if this MAC is already present in the queue. */ + fpm_mac = zfpm_mac_info_lookup(&key); + + if (fpm_mac) { + mac_found = true; + + /* + * If the enqueued op is "add" and current op is "delete", + * this is a noop. So, Unset ZEBRA_MAC_UPDATE_FPM flag. + * While processing FPM queue, we will silently delete this + * MAC entry without sending any update for this MAC. + */ + if (!CHECK_FLAG(fpm_mac->fpm_flags, ZEBRA_MAC_DELETE_FPM) && + delete == 1) { + SET_FLAG(fpm_mac->fpm_flags, ZEBRA_MAC_DELETE_FPM); + UNSET_FLAG(fpm_mac->fpm_flags, ZEBRA_MAC_UPDATE_FPM); + return 0; + } + } else { + fpm_mac = hash_get(zfpm_g->fpm_mac_info_table, &key, + zfpm_mac_info_alloc); + if (!fpm_mac) + return 0; + } + + fpm_mac->r_vtep_ip.s_addr = rmac->fwd_info.r_vtep_ip.s_addr; + fpm_mac->zebra_flags = rmac->flags; + fpm_mac->vxlan_if = vxlan_if ? vxlan_if->ifindex : 0; + fpm_mac->svi_if = svi_if ? svi_if->ifindex : 0; + + SET_FLAG(fpm_mac->fpm_flags, ZEBRA_MAC_UPDATE_FPM); + if (delete) + SET_FLAG(fpm_mac->fpm_flags, ZEBRA_MAC_DELETE_FPM); + else + UNSET_FLAG(fpm_mac->fpm_flags, ZEBRA_MAC_DELETE_FPM); + + if (!mac_found) + TAILQ_INSERT_TAIL(&zfpm_g->mac_q, fpm_mac, fpm_mac_q_entries); + + zfpm_g->stats.updates_triggered++; + + /* If writes are already enabled, return. */ + if (zfpm_g->t_write) + return 0; + + zfpm_write_on(); + return 0; +} + +/* + * This function is called when the FPM connections is established. + * Iterate over all the RMAC entries for the given L3VNI + * and enqueue the RMAC for FPM processing. + */ +static void zfpm_trigger_rmac_update_wrapper(struct hash_bucket *backet, + void *args) +{ + zebra_mac_t *zrmac = (zebra_mac_t *)backet->data; + zebra_l3vni_t *zl3vni = (zebra_l3vni_t *)args; + + zfpm_trigger_rmac_update(zrmac, zl3vni, false, "RMAC added"); +} + +/* + * This function is called when the FPM connections is established. + * This function iterates over all the L3VNIs to trigger + * FPM updates for RMACs currently available. + */ +static void zfpm_iterate_rmac_table(struct hash_bucket *backet, void *args) +{ + zebra_l3vni_t *zl3vni = (zebra_l3vni_t *)backet->data; + + hash_iterate(zl3vni->rmac_table, zfpm_trigger_rmac_update_wrapper, + (void *)zl3vni); +} + +/* + * struct zfpm_statsimer_cb */ static int zfpm_stats_timer_cb(struct thread *t) { @@ -1342,7 +1718,7 @@ void zfpm_start_stats_timer(void) */ static void zfpm_show_stats(struct vty *vty) { - zfpm_stats_t total_stats; + struct zfpm_stats total_stats; time_t elapsed; vty_out(vty, "\n%-40s %10s Last %2d secs\n\n", "Counter", "Total", @@ -1533,6 +1909,8 @@ static inline void zfpm_init_message_format(const char *format) "FPM protobuf message format is not available"); return; } + flog_warn(EC_ZEBRA_PROTOBUF_NOT_AVAILABLE, + "FPM protobuf message format is deprecated and scheduled to be removed. Please convert to using netlink format or contact dev@lists.frrouting.org with your use case."); zfpm_g->message_format = ZFPM_MSG_FORMAT_PROTOBUF; return; } @@ -1565,8 +1943,15 @@ static int fpm_remote_srv_write(struct vty *vty) } +static int fpm_remote_srv_write(struct vty *vty); /* Zebra node */ -static struct cmd_node zebra_node = {ZEBRA_NODE, "", 1}; +static struct cmd_node zebra_node = { + .name = "zebra", + .node = ZEBRA_NODE, + .parent_node = CONFIG_NODE, + .prompt = "", + .config_write = fpm_remote_srv_write, +}; /** @@ -1575,10 +1960,10 @@ static struct cmd_node zebra_node = {ZEBRA_NODE, "", 1}; * One-time initialization of the Zebra FPM module. * * @param[in] port port at which FPM is running. - * @param[in] enable TRUE if the zebra FPM module should be enabled + * @param[in] enable true if the zebra FPM module should be enabled * @param[in] format to use to talk to the FPM. Can be 'netink' or 'protobuf'. * - * Returns TRUE on success. + * Returns true on success. */ static int zfpm_init(struct thread_master *master) { @@ -1589,6 +1974,13 @@ static int zfpm_init(struct thread_master *master) memset(zfpm_g, 0, sizeof(*zfpm_g)); zfpm_g->master = master; TAILQ_INIT(&zfpm_g->dest_q); + TAILQ_INIT(&zfpm_g->mac_q); + + /* Create hash table for fpm_mac_info_t enties */ + zfpm_g->fpm_mac_info_table = hash_create(zfpm_mac_info_hash_keymake, + zfpm_mac_info_cmp, + "FPM MAC info hash table"); + zfpm_g->sock = -1; zfpm_g->state = ZFPM_STATE_IDLE; @@ -1596,7 +1988,7 @@ static int zfpm_init(struct thread_master *master) zfpm_stats_init(&zfpm_g->last_ivl_stats); zfpm_stats_init(&zfpm_g->cumulative_stats); - install_node(&zebra_node, fpm_remote_srv_write); + install_node(&zebra_node); install_element(ENABLE_NODE, &show_zebra_fpm_stats_cmd); install_element(ENABLE_NODE, &clear_zebra_fpm_stats_cmd); install_element(CONFIG_NODE, &fpm_remote_ip_cmd); @@ -1628,10 +2020,24 @@ static int zfpm_init(struct thread_master *master) return 0; } +static int zfpm_fini(void) +{ + zfpm_write_off(); + zfpm_read_off(); + zfpm_connect_off(); + + zfpm_stop_stats_timer(); + + hook_unregister(rib_update, zfpm_trigger_update); + return 0; +} + static int zebra_fpm_module_init(void) { hook_register(rib_update, zfpm_trigger_update); + hook_register(zebra_rmac_update, zfpm_trigger_rmac_update); hook_register(frr_late_init, zfpm_init); + hook_register(frr_early_fini, zfpm_fini); return 0; } diff --git a/zebra/zebra_fpm_dt.c b/zebra/zebra_fpm_dt.c index e87fa0ad71..81437e72f5 100644 --- a/zebra/zebra_fpm_dt.c +++ b/zebra/zebra_fpm_dt.c @@ -90,7 +90,7 @@ static int zfpm_dt_find_route(rib_dest_t **dest_p, struct route_entry **re_p) if (!re) continue; - if (re->nexthop_active_num <= 0) + if (nexthop_group_active_nexthop_num(&(re->nhe->nhg)) == 0) continue; *dest_p = dest; diff --git a/zebra/zebra_fpm_netlink.c b/zebra/zebra_fpm_netlink.c index bdc1dcdff3..2c07413638 100644 --- a/zebra/zebra_fpm_netlink.c +++ b/zebra/zebra_fpm_netlink.c @@ -32,6 +32,7 @@ #include "prefix.h" #include "zebra/zserv.h" +#include "zebra/zebra_router.h" #include "zebra/zebra_dplane.h" #include "zebra/zebra_ns.h" #include "zebra/zebra_vrf.h" @@ -40,6 +41,7 @@ #include "nexthop.h" #include "zebra/zebra_fpm_private.h" +#include "zebra/zebra_vxlan_private.h" /* * addr_to_a @@ -55,13 +57,10 @@ static inline const char *addr_to_a(uint8_t af, void *addr) case AF_INET: return inet_ntoa(*((struct in_addr *)addr)); - break; case AF_INET6: return inet6_ntoa(*((struct in6_addr *)addr)); - break; default: return ""; - break; } } @@ -90,10 +89,8 @@ static size_t af_addr_size(uint8_t af) case AF_INET: return 4; - break; case AF_INET6: return 16; - break; default: assert(0); return 16; @@ -101,13 +98,58 @@ static size_t af_addr_size(uint8_t af) } /* - * netlink_nh_info_t + * We plan to use RTA_ENCAP_TYPE attribute for VxLAN encap as well. + * Currently, values 0 to 8 for this attribute are used by lwtunnel_encap_types + * So, we cannot use these values for VxLAN encap. + */ +enum fpm_nh_encap_type_t { + FPM_NH_ENCAP_NONE = 0, + FPM_NH_ENCAP_VXLAN = 100, + FPM_NH_ENCAP_MAX, +}; + +/* + * fpm_nh_encap_type_to_str + */ +static const char *fpm_nh_encap_type_to_str(enum fpm_nh_encap_type_t encap_type) +{ + switch (encap_type) { + case FPM_NH_ENCAP_NONE: + return "none"; + + case FPM_NH_ENCAP_VXLAN: + return "VxLAN"; + + case FPM_NH_ENCAP_MAX: + return "invalid"; + } + + return "invalid"; +} + +struct vxlan_encap_info_t { + vni_t vni; +}; + +enum vxlan_encap_info_type_t { + VXLAN_VNI = 0, +}; + +struct fpm_nh_encap_info_t { + enum fpm_nh_encap_type_t encap_type; + union { + struct vxlan_encap_info_t vxlan_encap; + }; +}; + +/* + * netlink_nh_info * * Holds information about a single nexthop for netlink. These info * structures are transient and may contain pointers into rib * data structures for convenience. */ -typedef struct netlink_nh_info_t_ { +struct netlink_nh_info { uint32_t if_index; union g_addr *gateway; @@ -117,14 +159,15 @@ typedef struct netlink_nh_info_t_ { */ int recursive; enum nexthop_types_t type; -} netlink_nh_info_t; + struct fpm_nh_encap_info_t encap_info; +}; /* - * netlink_route_info_t + * netlink_route_info * * A structure for holding information for a netlink route message. */ -typedef struct netlink_route_info_t_ { +struct netlink_route_info { uint16_t nlmsg_type; uint8_t rtm_type; uint32_t rtm_table; @@ -137,9 +180,9 @@ typedef struct netlink_route_info_t_ { /* * Nexthop structures */ - netlink_nh_info_t nhs[MULTIPATH_NUM]; + struct netlink_nh_info nhs[MULTIPATH_NUM]; union g_addr *pref_src; -} netlink_route_info_t; +}; /* * netlink_route_info_add_nh @@ -147,13 +190,15 @@ typedef struct netlink_route_info_t_ { * Add information about the given nexthop to the given route info * structure. * - * Returns TRUE if a nexthop was added, FALSE otherwise. + * Returns true if a nexthop was added, false otherwise. */ -static int netlink_route_info_add_nh(netlink_route_info_t *ri, - struct nexthop *nexthop) +static int netlink_route_info_add_nh(struct netlink_route_info *ri, + struct nexthop *nexthop, + struct route_entry *re) { - netlink_nh_info_t nhi; + struct netlink_nh_info nhi; union g_addr *src; + zebra_l3vni_t *zl3vni = NULL; memset(&nhi, 0, sizeof(nhi)); src = NULL; @@ -168,7 +213,7 @@ static int netlink_route_info_add_nh(netlink_route_info_t *ri, if (nexthop->type == NEXTHOP_TYPE_IPV4 || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) { nhi.gateway = &nexthop->gate; - if (nexthop->src.ipv4.s_addr) + if (nexthop->src.ipv4.s_addr != INADDR_ANY) src = &nexthop->src; } @@ -178,13 +223,24 @@ static int netlink_route_info_add_nh(netlink_route_info_t *ri, } if (nexthop->type == NEXTHOP_TYPE_IFINDEX) { - if (nexthop->src.ipv4.s_addr) + if (nexthop->src.ipv4.s_addr != INADDR_ANY) src = &nexthop->src; } if (!nhi.gateway && nhi.if_index == 0) return 0; + if (re && CHECK_FLAG(re->flags, ZEBRA_FLAG_EVPN_ROUTE)) { + nhi.encap_info.encap_type = FPM_NH_ENCAP_VXLAN; + + zl3vni = zl3vni_from_vrf(nexthop->vrf_id); + if (zl3vni && is_l3vni_oper_up(zl3vni)) { + + /* Add VNI to VxLAN encap info */ + nhi.encap_info.vxlan_encap.vni = zl3vni->vni; + } + } + /* * We have a valid nhi. Copy the structure over to the route_info. */ @@ -217,9 +273,9 @@ static uint8_t netlink_proto_from_route_type(int type) * * Fill out the route information object from the given route. * - * Returns TRUE on success and FALSE on failure. + * Returns true on success and false on failure. */ -static int netlink_route_info_fill(netlink_route_info_t *ri, int cmd, +static int netlink_route_info_fill(struct netlink_route_info *ri, int cmd, rib_dest_t *dest, struct route_entry *re) { struct nexthop *nexthop; @@ -230,7 +286,7 @@ static int netlink_route_info_fill(netlink_route_info_t *ri, int cmd, ri->af = rib_dest_af(dest); ri->nlmsg_type = cmd; - ri->rtm_table = zvrf_id(rib_dest_vrf(dest)); + ri->rtm_table = rib_table_info(rib_dest_table(dest))->table_id; ri->rtm_protocol = RTPROT_UNSPEC; /* @@ -241,8 +297,7 @@ static int netlink_route_info_fill(netlink_route_info_t *ri, int cmd, return 1; if (!re) { - zfpm_debug("%s: Expected non-NULL re pointer", - __PRETTY_FUNCTION__); + zfpm_debug("%s: Expected non-NULL re pointer", __func__); return 0; } @@ -250,8 +305,8 @@ static int netlink_route_info_fill(netlink_route_info_t *ri, int cmd, ri->rtm_type = RTN_UNICAST; ri->metric = &re->metric; - for (ALL_NEXTHOPS(re->ng, nexthop)) { - if (ri->num_nhs >= multipath_num) + for (ALL_NEXTHOPS(re->nhe->nhg, nexthop)) { + if (ri->num_nhs >= zrouter.multipath_num) break; if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) @@ -270,21 +325,28 @@ static int netlink_route_info_fill(netlink_route_info_t *ri, int cmd, ri->rtm_type = RTN_BLACKHOLE; break; } - return 1; } if ((cmd == RTM_NEWROUTE && CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) || (cmd == RTM_DELROUTE && CHECK_FLAG(re->status, ROUTE_ENTRY_INSTALLED))) { - netlink_route_info_add_nh(ri, nexthop); + netlink_route_info_add_nh(ri, nexthop, re); } } - /* If there is no useful nexthop then return. */ if (ri->num_nhs == 0) { - zfpm_debug("netlink_encode_route(): No useful nexthop."); - return 0; + switch (ri->rtm_type) { + case RTN_PROHIBIT: + case RTN_UNREACHABLE: + case RTN_BLACKHOLE: + break; + default: + /* If there is no useful nexthop then return. */ + zfpm_debug( + "netlink_encode_route(): No useful nexthop."); + return 0; + } } return 1; @@ -296,13 +358,18 @@ static int netlink_route_info_fill(netlink_route_info_t *ri, int cmd, * Returns the number of bytes written to the buffer. 0 or a negative * value indicates an error. */ -static int netlink_route_info_encode(netlink_route_info_t *ri, char *in_buf, - size_t in_buf_len) +static int netlink_route_info_encode(struct netlink_route_info *ri, + char *in_buf, size_t in_buf_len) { size_t bytelen; unsigned int nexthop_num = 0; size_t buf_offset; - netlink_nh_info_t *nhi; + struct netlink_nh_info *nhi; + enum fpm_nh_encap_type_t encap; + struct rtattr *nest, *inner_nest; + struct rtnexthop *rtnh; + struct vxlan_encap_info_t *vxlan; + struct in6_addr ipv6; struct { struct nlmsghdr n; @@ -327,18 +394,33 @@ static int netlink_route_info_encode(netlink_route_info_t *ri, char *in_buf, req->n.nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST; req->n.nlmsg_type = ri->nlmsg_type; req->r.rtm_family = ri->af; - req->r.rtm_table = ri->rtm_table; + + /* + * rtm_table field is a uchar field which can accomodate table_id less + * than 256. + * To support table id greater than 255, if the table_id is greater than + * 255, set rtm_table to RT_TABLE_UNSPEC and add RTA_TABLE attribute + * with 32 bit value as the table_id. + */ + if (ri->rtm_table < 256) + req->r.rtm_table = ri->rtm_table; + else { + req->r.rtm_table = RT_TABLE_UNSPEC; + nl_attr_put32(&req->n, in_buf_len, RTA_TABLE, ri->rtm_table); + } + req->r.rtm_dst_len = ri->prefix->prefixlen; req->r.rtm_protocol = ri->rtm_protocol; req->r.rtm_scope = RT_SCOPE_UNIVERSE; - addattr_l(&req->n, in_buf_len, RTA_DST, &ri->prefix->u.prefix, bytelen); + nl_attr_put(&req->n, in_buf_len, RTA_DST, &ri->prefix->u.prefix, + bytelen); req->r.rtm_type = ri->rtm_type; /* Metric. */ if (ri->metric) - addattr32(&req->n, in_buf_len, RTA_PRIORITY, *ri->metric); + nl_attr_put32(&req->n, in_buf_len, RTA_PRIORITY, *ri->metric); if (ri->num_nhs == 0) goto done; @@ -347,12 +429,36 @@ static int netlink_route_info_encode(netlink_route_info_t *ri, char *in_buf, nhi = &ri->nhs[0]; if (nhi->gateway) { - addattr_l(&req->n, in_buf_len, RTA_GATEWAY, - nhi->gateway, bytelen); + if (nhi->type == NEXTHOP_TYPE_IPV4_IFINDEX + && ri->af == AF_INET6) { + ipv4_to_ipv4_mapped_ipv6(&ipv6, + nhi->gateway->ipv4); + nl_attr_put(&req->n, in_buf_len, RTA_GATEWAY, + &ipv6, bytelen); + } else + nl_attr_put(&req->n, in_buf_len, RTA_GATEWAY, + nhi->gateway, bytelen); } if (nhi->if_index) { - addattr32(&req->n, in_buf_len, RTA_OIF, nhi->if_index); + nl_attr_put32(&req->n, in_buf_len, RTA_OIF, + nhi->if_index); + } + + encap = nhi->encap_info.encap_type; + switch (encap) { + case FPM_NH_ENCAP_NONE: + case FPM_NH_ENCAP_MAX: + break; + case FPM_NH_ENCAP_VXLAN: + nl_attr_put16(&req->n, in_buf_len, RTA_ENCAP_TYPE, + encap); + vxlan = &nhi->encap_info.vxlan_encap; + nest = nl_attr_nest(&req->n, in_buf_len, RTA_ENCAP); + nl_attr_put32(&req->n, in_buf_len, VXLAN_VNI, + vxlan->vni); + nl_attr_nest_end(&req->n, nest); + break; } goto done; @@ -361,45 +467,48 @@ static int netlink_route_info_encode(netlink_route_info_t *ri, char *in_buf, /* * Multipath case. */ - char buf[NL_PKT_BUF_SIZE]; - struct rtattr *rta = (void *)buf; - struct rtnexthop *rtnh; - - rta->rta_type = RTA_MULTIPATH; - rta->rta_len = RTA_LENGTH(0); - rtnh = RTA_DATA(rta); + nest = nl_attr_nest(&req->n, in_buf_len, RTA_MULTIPATH); for (nexthop_num = 0; nexthop_num < ri->num_nhs; nexthop_num++) { + rtnh = nl_attr_rtnh(&req->n, in_buf_len); nhi = &ri->nhs[nexthop_num]; - rtnh->rtnh_len = sizeof(*rtnh); - rtnh->rtnh_flags = 0; - rtnh->rtnh_hops = 0; - rtnh->rtnh_ifindex = 0; - rta->rta_len += rtnh->rtnh_len; - - if (nhi->gateway) { - rta_addattr_l(rta, sizeof(buf), RTA_GATEWAY, - nhi->gateway, bytelen); - rtnh->rtnh_len += sizeof(struct rtattr) + bytelen; - } + if (nhi->gateway) + nl_attr_put(&req->n, in_buf_len, RTA_GATEWAY, + nhi->gateway, bytelen); if (nhi->if_index) { rtnh->rtnh_ifindex = nhi->if_index; } - rtnh = RTNH_NEXT(rtnh); + encap = nhi->encap_info.encap_type; + switch (encap) { + case FPM_NH_ENCAP_NONE: + case FPM_NH_ENCAP_MAX: + break; + case FPM_NH_ENCAP_VXLAN: + nl_attr_put16(&req->n, in_buf_len, RTA_ENCAP_TYPE, + encap); + vxlan = &nhi->encap_info.vxlan_encap; + inner_nest = + nl_attr_nest(&req->n, in_buf_len, RTA_ENCAP); + nl_attr_put32(&req->n, in_buf_len, VXLAN_VNI, + vxlan->vni); + nl_attr_nest_end(&req->n, inner_nest); + break; + } + + nl_attr_rtnh_end(&req->n, rtnh); } - assert(rta->rta_len > RTA_LENGTH(0)); - addattr_l(&req->n, in_buf_len, RTA_MULTIPATH, RTA_DATA(rta), - RTA_PAYLOAD(rta)); + nl_attr_nest_end(&req->n, nest); + assert(nest->rta_len > RTA_LENGTH(0)); done: if (ri->pref_src) { - addattr_l(&req->n, in_buf_len, RTA_PREFSRC, &ri->pref_src, - bytelen); + nl_attr_put(&req->n, in_buf_len, RTA_PREFSRC, &ri->pref_src, + bytelen); } assert(req->n.nlmsg_len < in_buf_len); @@ -411,9 +520,10 @@ static int netlink_route_info_encode(netlink_route_info_t *ri, char *in_buf, * * Helper function to log the information in a route_info structure. */ -static void zfpm_log_route_info(netlink_route_info_t *ri, const char *label) +static void zfpm_log_route_info(struct netlink_route_info *ri, + const char *label) { - netlink_nh_info_t *nhi; + struct netlink_nh_info *nhi; unsigned int i; zfpm_debug("%s : %s %s/%d, Proto: %s, Metric: %u", label, @@ -424,10 +534,12 @@ static void zfpm_log_route_info(netlink_route_info_t *ri, const char *label) for (i = 0; i < ri->num_nhs; i++) { nhi = &ri->nhs[i]; - zfpm_debug(" Intf: %u, Gateway: %s, Recursive: %s, Type: %s", + zfpm_debug(" Intf: %u, Gateway: %s, Recursive: %s, Type: %s, Encap type: %s", nhi->if_index, addr_to_a(ri->af, nhi->gateway), nhi->recursive ? "yes" : "no", - nexthop_type_to_str(nhi->type)); + nexthop_type_to_str(nhi->type), + fpm_nh_encap_type_to_str(nhi->encap_info.encap_type) + ); } } @@ -443,16 +555,79 @@ static void zfpm_log_route_info(netlink_route_info_t *ri, const char *label) int zfpm_netlink_encode_route(int cmd, rib_dest_t *dest, struct route_entry *re, char *in_buf, size_t in_buf_len) { - netlink_route_info_t ri_space, *ri; + struct netlink_route_info ri_space, *ri; ri = &ri_space; if (!netlink_route_info_fill(ri, cmd, dest, re)) return 0; - zfpm_log_route_info(ri, __FUNCTION__); + zfpm_log_route_info(ri, __func__); return netlink_route_info_encode(ri, in_buf, in_buf_len); } +/* + * zfpm_netlink_encode_mac + * + * Create a netlink message corresponding to the given MAC. + * + * Returns the number of bytes written to the buffer. 0 or a negative + * value indicates an error. + */ +int zfpm_netlink_encode_mac(struct fpm_mac_info_t *mac, char *in_buf, + size_t in_buf_len) +{ + char buf1[ETHER_ADDR_STRLEN]; + size_t buf_offset; + + struct macmsg { + struct nlmsghdr hdr; + struct ndmsg ndm; + char buf[0]; + } *req; + req = (void *)in_buf; + + buf_offset = offsetof(struct macmsg, buf); + if (in_buf_len < buf_offset) + return 0; + memset(req, 0, buf_offset); + + /* Construct nlmsg header */ + req->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)); + req->hdr.nlmsg_type = CHECK_FLAG(mac->fpm_flags, ZEBRA_MAC_DELETE_FPM) ? + RTM_DELNEIGH : RTM_NEWNEIGH; + req->hdr.nlmsg_flags = NLM_F_REQUEST; + if (req->hdr.nlmsg_type == RTM_NEWNEIGH) + req->hdr.nlmsg_flags |= (NLM_F_CREATE | NLM_F_REPLACE); + + /* Construct ndmsg */ + req->ndm.ndm_family = AF_BRIDGE; + req->ndm.ndm_ifindex = mac->vxlan_if; + + req->ndm.ndm_state = NUD_REACHABLE; + req->ndm.ndm_flags |= NTF_SELF | NTF_MASTER; + if (CHECK_FLAG(mac->zebra_flags, + (ZEBRA_MAC_STICKY | ZEBRA_MAC_REMOTE_DEF_GW))) + req->ndm.ndm_state |= NUD_NOARP; + else + req->ndm.ndm_flags |= NTF_EXT_LEARNED; + + /* Add attributes */ + nl_attr_put(&req->hdr, in_buf_len, NDA_LLADDR, &mac->macaddr, 6); + nl_attr_put(&req->hdr, in_buf_len, NDA_DST, &mac->r_vtep_ip, 4); + nl_attr_put32(&req->hdr, in_buf_len, NDA_MASTER, mac->svi_if); + nl_attr_put32(&req->hdr, in_buf_len, NDA_VNI, mac->vni); + + assert(req->hdr.nlmsg_len < in_buf_len); + + zfpm_debug("Tx %s family %s ifindex %u MAC %s DEST %s", + nl_msg_type_to_str(req->hdr.nlmsg_type), + nl_family_to_str(req->ndm.ndm_family), req->ndm.ndm_ifindex, + prefix_mac2str(&mac->macaddr, buf1, sizeof(buf1)), + inet_ntoa(mac->r_vtep_ip)); + + return req->hdr.nlmsg_len; +} + #endif /* HAVE_NETLINK */ diff --git a/zebra/zebra_fpm_private.h b/zebra/zebra_fpm_private.h index 943aad9864..c169ee8c22 100644 --- a/zebra/zebra_fpm_private.h +++ b/zebra/zebra_fpm_private.h @@ -53,6 +53,34 @@ static inline void zfpm_debug(const char *format, ...) } #endif +/* This structure contains the MAC addresses enqueued for FPM processing. */ +struct fpm_mac_info_t { + struct ethaddr macaddr; + uint32_t zebra_flags; /* Could be used to build FPM messages */ + vni_t vni; + ifindex_t vxlan_if; + ifindex_t svi_if; /* L2 or L3 Bridge interface */ + struct in_addr r_vtep_ip; /* Remote VTEP IP */ + + /* Linkage to put MAC on the FPM processing queue. */ + TAILQ_ENTRY(fpm_mac_info_t) fpm_mac_q_entries; + + uint8_t fpm_flags; + +#define ZEBRA_MAC_UPDATE_FPM 0x1 /* This flag indicates if we want to upadte + * data plane for this MAC. If a MAC is added + * and then deleted immediately, we do not want + * to update data plane for such operation. + * Unset the ZEBRA_MAC_UPDATE_FPM flag in this + * case. FPM thread while processing the queue + * node will check this flag and dequeue the + * node silently without sending any update to + * the data plane. + */ +#define ZEBRA_MAC_DELETE_FPM 0x2 /* This flag is set if it is a delete operation + * for the MAC. + */ +}; /* * Externs @@ -64,6 +92,9 @@ extern int zfpm_netlink_encode_route(int cmd, rib_dest_t *dest, extern int zfpm_protobuf_encode_route(rib_dest_t *dest, struct route_entry *re, uint8_t *in_buf, size_t in_buf_len); +extern int zfpm_netlink_encode_mac(struct fpm_mac_info_t *mac, char *in_buf, + size_t in_buf_len); + extern struct route_entry *zfpm_route_for_update(rib_dest_t *dest); #ifdef __cplusplus diff --git a/zebra/zebra_fpm_protobuf.c b/zebra/zebra_fpm_protobuf.c index 0f95c9ba8b..4b31cc0281 100644 --- a/zebra/zebra_fpm_protobuf.c +++ b/zebra/zebra_fpm_protobuf.c @@ -34,6 +34,7 @@ #include "qpb/linear_allocator.h" #include "fpm/fpm_pb.h" +#include "zebra_router.h" #include "zebra_fpm_private.h" /* @@ -85,7 +86,7 @@ static inline int add_nexthop(qpb_allocator_t *allocator, Fpm__AddRoute *msg, if (nexthop->type == NEXTHOP_TYPE_IPV4 || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) { gateway = &nexthop->gate; - if (nexthop->src.ipv4.s_addr) + if (nexthop->src.ipv4.s_addr != INADDR_ANY) src = &nexthop->src; } @@ -95,7 +96,7 @@ static inline int add_nexthop(qpb_allocator_t *allocator, Fpm__AddRoute *msg, } if (nexthop->type == NEXTHOP_TYPE_IFINDEX) { - if (nexthop->src.ipv4.s_addr) + if (nexthop->src.ipv4.s_addr != INADDR_ANY) src = &nexthop->src; } @@ -172,8 +173,8 @@ static Fpm__AddRoute *create_add_route_message(qpb_allocator_t *allocator, * Figure out the set of nexthops to be added to the message. */ num_nhs = 0; - for (ALL_NEXTHOPS(re->ng, nexthop)) { - if (num_nhs >= multipath_num) + for (ALL_NEXTHOPS(re->nhe->nhg, nexthop)) { + if (num_nhs >= zrouter.multipath_num) break; if (num_nhs >= array_size(nexthops)) @@ -293,7 +294,7 @@ int zfpm_protobuf_encode_route(rib_dest_t *dest, struct route_entry *re, return 0; } - len = fpm__message__pack(msg, (uint8_t *)in_buf); + len = fpm__message__pack(msg, in_buf); assert(len <= in_buf_len); QPB_RESET_STACK_ALLOCATOR(allocator); diff --git a/zebra/zebra_gr.c b/zebra/zebra_gr.c new file mode 100644 index 0000000000..a4f32dbf39 --- /dev/null +++ b/zebra/zebra_gr.c @@ -0,0 +1,683 @@ +/* + * Zebra GR related helper functions. + * + * Portions: + * Copyright (C) 2019 VMware, Inc. + * et al. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include + +#include "lib/prefix.h" +#include "lib/command.h" +#include "lib/if.h" +#include "lib/thread.h" +#include "lib/stream.h" +#include "lib/memory.h" +#include "lib/table.h" +#include "lib/network.h" +#include "lib/sockunion.h" +#include "lib/log.h" +#include "lib/zclient.h" +#include "lib/privs.h" +#include "lib/network.h" +#include "lib/buffer.h" +#include "lib/nexthop.h" +#include "lib/vrf.h" +#include "lib/libfrr.h" +#include "lib/sockopt.h" + +#include "zebra/zebra_router.h" +#include "zebra/debug.h" +#include "zebra/zapi_msg.h" + + +/* + * Forward declaration. + */ +static struct zserv *zebra_gr_find_stale_client(struct zserv *client); +static int32_t zebra_gr_route_stale_delete_timer_expiry(struct thread *thread); +static int32_t zebra_gr_delete_stale_routes(struct client_gr_info *info); +static void zebra_gr_process_client_stale_routes(struct zserv *client, + vrf_id_t vrf_id); + +/* + * Debug macros. + */ +#define LOG_GR(msg, ...) \ + do { \ + if (IS_ZEBRA_DEBUG_EVENT) \ + zlog_debug(msg, ##__VA_ARGS__); \ + } while (0) + + +/* + * Client connection functions + */ + +/* + * Function to clean all the stale clients, + * function will also clean up all per instance + * capabilities that are exchanged. + */ +void zebra_gr_stale_client_cleanup(struct list *client_list) +{ + struct listnode *node, *nnode; + struct zserv *s_client = NULL; + struct client_gr_info *info, *ninfo; + + /* Find the stale client */ + for (ALL_LIST_ELEMENTS(client_list, node, nnode, s_client)) { + + LOG_GR("%s: Stale client %s is being deleted", __func__, + zebra_route_string(s_client->proto)); + + TAILQ_FOREACH_SAFE (info, &s_client->gr_info_queue, gr_info, + ninfo) { + + /* Cancel the stale timer */ + if (info->t_stale_removal != NULL) { + THREAD_OFF(info->t_stale_removal); + info->t_stale_removal = NULL; + /* Process the stale routes */ + thread_execute( + zrouter.master, + zebra_gr_route_stale_delete_timer_expiry, + info, 1); + } + } + } +} + +/* + * A helper function to create client info. + */ +static struct client_gr_info *zebra_gr_client_info_create(struct zserv *client) +{ + struct client_gr_info *info; + + info = XCALLOC(MTYPE_TMP, sizeof(struct client_gr_info)); + + TAILQ_INSERT_TAIL(&(client->gr_info_queue), info, gr_info); + return info; +} + +/* + * A helper function to delte and destory client info. + */ +static void zebra_gr_client_info_delte(struct zserv *client, + struct client_gr_info *info) +{ + TAILQ_REMOVE(&(client->gr_info_queue), info, gr_info); + + THREAD_OFF(info->t_stale_removal); + + XFREE(MTYPE_TMP, info->current_prefix); + + LOG_GR("%s: Instance info is being deleted for client %s", __func__, + zebra_route_string(client->proto)); + + /* Delete all the stale routes. */ + info->do_delete = true; + zebra_gr_delete_stale_routes(info); + + XFREE(MTYPE_TMP, info); +} + +/* + * Function to handle client when it disconnect. + */ +int32_t zebra_gr_client_disconnect(struct zserv *client) +{ + struct zserv *stale_client; + struct timeval tv; + struct client_gr_info *info = NULL; + + /* Find the stale client */ + stale_client = zebra_gr_find_stale_client(client); + + /* + * We should never be here. + */ + if (stale_client) { + LOG_GR("%s: Stale client %s exist, we should not be here!", + __func__, zebra_route_string(client->proto)); + assert(0); + } + + client->restart_time = monotime(&tv); + + /* For all the GR instance start the starle removal timer. */ + TAILQ_FOREACH (info, &client->gr_info_queue, gr_info) { + if (ZEBRA_CLIENT_GR_ENABLED(info->capabilities) + && (info->t_stale_removal == NULL)) { + thread_add_timer( + zrouter.master, + zebra_gr_route_stale_delete_timer_expiry, info, + info->stale_removal_time, + &info->t_stale_removal); + info->current_afi = AFI_IP; + info->stale_client_ptr = client; + info->stale_client = true; + LOG_GR("%s: Client %s Stale timer update to %d", + __func__, zebra_route_string(client->proto), + info->stale_removal_time); + } + } + + listnode_add(zrouter.stale_client_list, client); + + return 0; +} + +/* + * Function to delete stale client + */ +static void zebra_gr_delete_stale_client(struct client_gr_info *info) +{ + struct client_gr_info *bgp_info; + struct zserv *s_client = NULL; + + s_client = info->stale_client_ptr; + + if (!s_client || !info->stale_client) + return; + + /* + * If there are bgp instances with the stale delete timer pending + * then stale client is not deleted + */ + if ((s_client->gr_instance_count > 0) && info->gr_enable) + s_client->gr_instance_count--; + + TAILQ_REMOVE(&(s_client->gr_info_queue), info, gr_info); + + LOG_GR("%s: Client %s gr count %d", __func__, + zebra_route_string(s_client->proto), + s_client->gr_instance_count); + + TAILQ_FOREACH (bgp_info, &s_client->gr_info_queue, gr_info) { + if (bgp_info->t_stale_removal != NULL) + return; + } + + LOG_GR("%s: Client %s is being deleted", __func__, + zebra_route_string(s_client->proto)); + + TAILQ_INIT(&(s_client->gr_info_queue)); + listnode_delete(zrouter.stale_client_list, s_client); + if (info->stale_client) + XFREE(MTYPE_TMP, s_client); + XFREE(MTYPE_TMP, info); +} + +/* + * Function to find stale client. + */ +static struct zserv *zebra_gr_find_stale_client(struct zserv *client) +{ + struct listnode *node, *nnode; + struct zserv *stale_client; + + /* Find the stale client */ + for (ALL_LIST_ELEMENTS(zrouter.stale_client_list, node, nnode, + stale_client)) { + if (client->proto == stale_client->proto + && client->instance == stale_client->instance) { + return stale_client; + } + } + + return NULL; +} + +/* + * Function to handle reconnect of client post restart. + */ +void zebra_gr_client_reconnect(struct zserv *client) +{ + struct listnode *node, *nnode; + struct zserv *old_client = NULL; + struct client_gr_info *info = NULL; + + /* Find the stale client */ + for (ALL_LIST_ELEMENTS(zrouter.stale_client_list, node, nnode, + old_client)) { + if (client->proto == old_client->proto + && client->instance == old_client->instance) + break; + } + + /* Copy the timers */ + if (!old_client) + return; + + client->gr_instance_count = old_client->gr_instance_count; + client->restart_time = old_client->restart_time; + + LOG_GR("%s : old client %s, gr_instance_count %d", __func__, + zebra_route_string(old_client->proto), + old_client->gr_instance_count); + + if (TAILQ_FIRST(&old_client->gr_info_queue)) { + TAILQ_CONCAT(&client->gr_info_queue, &old_client->gr_info_queue, + gr_info); + TAILQ_INIT(&old_client->gr_info_queue); + } + + TAILQ_FOREACH (info, &client->gr_info_queue, gr_info) { + info->stale_client_ptr = client; + info->stale_client = false; + } + + /* Delete the stale client */ + listnode_delete(zrouter.stale_client_list, old_client); + /* Delete old client */ + XFREE(MTYPE_TMP, old_client); +} + +/* + * Functions to deal with capabilities + */ + +/* + * Update the graceful restart information + * for the client instance. + * This function handles all the capabilties that are received. + */ +static void zebra_client_update_info(struct zserv *client, struct zapi_cap *api) +{ + struct client_gr_info *info = NULL; + + /* Find the bgp information for the specified vrf id */ + TAILQ_FOREACH (info, &client->gr_info_queue, gr_info) { + if (info->vrf_id == api->vrf_id) + break; + } + + + /* + * If the command is delete, then cancel the stale timer and + * delete the bgp info + */ + switch (api->cap) { + case ZEBRA_CLIENT_GR_DISABLE: + if (!info) + return; + + LOG_GR("%s: Client %s instance GR disabled count %d", __func__, + zebra_route_string(client->proto), + client->gr_instance_count); + + if ((info->gr_enable) && (client->gr_instance_count > 0)) + client->gr_instance_count--; + + zebra_gr_client_info_delte(client, info); + break; + case ZEBRA_CLIENT_GR_CAPABILITIES: + /* Allocate bgp info */ + if (!info) + info = zebra_gr_client_info_create(client); + + /* Udpate other parameters */ + if (!info->gr_enable) { + client->gr_instance_count++; + + LOG_GR("%s: Cient %s GR enabled count %d", __func__, + zebra_route_string(client->proto), + client->gr_instance_count); + + info->capabilities = api->cap; + info->stale_removal_time = api->stale_removal_time; + info->vrf_id = api->vrf_id; + info->gr_enable = true; + } + break; + case ZEBRA_CLIENT_RIB_STALE_TIME: + LOG_GR("%s: Client %s stale time update event", __func__, + zebra_route_string(client->proto)); + + /* Update the stale removal timer */ + if (info && info->t_stale_removal == NULL) { + + LOG_GR("%s: Stale time: %d is now update to: %d", + __func__, info->stale_removal_time, + api->stale_removal_time); + + info->stale_removal_time = api->stale_removal_time; + } + + break; + case ZEBRA_CLIENT_ROUTE_UPDATE_COMPLETE: + LOG_GR( + "%s: Client %s route update complete for AFI %d, SAFI %d", + __func__, zebra_route_string(client->proto), api->afi, + api->safi); + if (info) + info->route_sync[api->afi][api->safi] = true; + break; + case ZEBRA_CLIENT_ROUTE_UPDATE_PENDING: + LOG_GR("%s: Client %s route update pending for AFI %d, SAFI %d", + __func__, zebra_route_string(client->proto), api->afi, + api->safi); + if (info) + info->af_enabled[api->afi][api->safi] = true; + break; + } +} + +/* + * Handler for capabilities that are received from client. + */ +static void zebra_client_capabilities_handler(struct zserv *client, + struct zapi_cap *api) +{ + switch (api->cap) { + case ZEBRA_CLIENT_GR_CAPABILITIES: + case ZEBRA_CLIENT_ROUTE_UPDATE_PENDING: + case ZEBRA_CLIENT_GR_DISABLE: + case ZEBRA_CLIENT_RIB_STALE_TIME: + /* + * For all the cases we need to update the client info. + */ + zebra_client_update_info(client, api); + break; + case ZEBRA_CLIENT_ROUTE_UPDATE_COMPLETE: + /* + * After client info has been updated delete all + * stale routes + */ + zebra_client_update_info(client, api); + zebra_gr_process_client_stale_routes(client, api->vrf_id); + break; + } +} + +/* + * Function to decode and call appropriate functions + * to handle client capabilities. + */ +void zread_client_capabilities(ZAPI_HANDLER_ARGS) +{ + struct zapi_cap api; + struct stream *s; + + s = msg; + + if (zapi_capabilities_decode(s, &api)) { + LOG_GR("%s: Error in reading capabilities for client %s", + __func__, zebra_route_string(client->proto)); + return; + } + + /* GR only for dynamic clients */ + if (client->proto <= ZEBRA_ROUTE_CONNECT) { + LOG_GR("%s: GR capabilities for client %s not supported", + __func__, zebra_route_string(client->proto)); + return; + } + /* Call the capabilities handler */ + zebra_client_capabilities_handler(client, &api); +} + + +/* + * Stale route handling + */ + +/* + * Delete all the stale routes that have not been refreshed + * post restart. + */ +static int32_t zebra_gr_route_stale_delete_timer_expiry(struct thread *thread) +{ + struct client_gr_info *info; + int32_t cnt = 0; + struct zserv *client; + + info = THREAD_ARG(thread); + info->t_stale_removal = NULL; + client = (struct zserv *)info->stale_client_ptr; + + /* Set the flag to indicate all stale route deletion */ + if (thread->u.val == 1) + info->do_delete = true; + + cnt = zebra_gr_delete_stale_routes(info); + + /* Retsart the timer */ + if (cnt > 0) { + LOG_GR("%s: Client %s processed %d routes. Start timer again", + __func__, zebra_route_string(client->proto), cnt); + + thread_add_timer(zrouter.master, + zebra_gr_route_stale_delete_timer_expiry, info, + ZEBRA_DEFAULT_STALE_UPDATE_DELAY, + &info->t_stale_removal); + } else { + /* No routes to delete for the VRF */ + LOG_GR("%s: Client %s all starle routes processed", __func__, + zebra_route_string(client->proto)); + + XFREE(MTYPE_TMP, info->current_prefix); + info->current_afi = 0; + zebra_gr_delete_stale_client(info); + } + return 0; +} + + +/* + * Function to process to check if route entry is stale + * or has been updated. + */ +static void zebra_gr_process_route_entry(struct zserv *client, + struct route_node *rn, + struct route_entry *re) +{ + char buf[PREFIX2STR_BUFFER]; + + if ((client == NULL) || (rn == NULL) || (re == NULL)) + return; + + /* If the route is not refreshed after restart, delete the entry */ + if (re->uptime < client->restart_time) { + if (IS_ZEBRA_DEBUG_RIB) { + prefix2str(&rn->p, buf, sizeof(buf)); + zlog_debug("%s: Client %s stale route %s is deleted", + __func__, zebra_route_string(client->proto), + buf); + } + rib_delnode(rn, re); + } +} + +/* + * This function walks through the route table for all vrf and deletes + * the stale routes for the restarted client specified by the protocol + * type + */ +static int32_t zebra_gr_delete_stale_route(struct client_gr_info *info, + struct zebra_vrf *zvrf) +{ + struct route_node *rn, *curr; + struct route_entry *re; + struct route_entry *next; + struct route_table *table; + int32_t n = 0; + afi_t afi, curr_afi; + uint8_t proto; + uint16_t instance; + struct zserv *s_client; + + if ((info == NULL) || (zvrf == NULL)) + return -1; + + s_client = info->stale_client_ptr; + if (s_client == NULL) { + LOG_GR("%s: Stale client not present", __func__); + return -1; + } + + proto = s_client->proto; + instance = s_client->instance; + curr_afi = info->current_afi; + + LOG_GR("%s: Client %s stale routes are being deleted", __func__, + zebra_route_string(proto)); + + /* Process routes for all AFI */ + for (afi = curr_afi; afi < AFI_MAX; afi++) { + table = zvrf->table[afi][SAFI_UNICAST]; + + if (table) { + /* + * If the current prefix is NULL then get the first + * route entry in the table + */ + if (info->current_prefix == NULL) { + rn = route_top(table); + if (rn == NULL) + continue; + curr = rn; + } else + /* Get the next route entry */ + curr = route_table_get_next( + table, info->current_prefix); + + for (rn = curr; rn; rn = srcdest_route_next(rn)) { + RNODE_FOREACH_RE_SAFE (rn, re, next) { + if (CHECK_FLAG(re->status, + ROUTE_ENTRY_REMOVED)) + continue; + /* If the route refresh is received + * after restart then do not delete + * the route + */ + if (re->type == proto + && re->instance == instance) { + zebra_gr_process_route_entry( + s_client, rn, re); + n++; + } + + /* If the max route count is reached + * then timer thread will be restarted + * Store the current prefix and afi + */ + if ((n >= ZEBRA_MAX_STALE_ROUTE_COUNT) + && (info->do_delete == false)) { + info->current_afi = afi; + info->current_prefix = XCALLOC( + MTYPE_TMP, + sizeof(struct prefix)); + prefix_copy( + info->current_prefix, + &rn->p); + return n; + } + } + } + } + /* + * Reset the current prefix to indicate processing completion + * of the current AFI + */ + XFREE(MTYPE_TMP, info->current_prefix); + } + return 0; +} + +/* + * Delete the stale routes when client is restarted and routes are not + * refreshed within the stale timeout + */ +static int32_t zebra_gr_delete_stale_routes(struct client_gr_info *info) +{ + struct vrf *vrf; + struct zebra_vrf *zvrf; + uint64_t cnt = 0; + + if (info == NULL) + return -1; + + /* Get the current VRF */ + vrf = vrf_lookup_by_id(info->vrf_id); + if (vrf == NULL) { + LOG_GR("%s: Invalid VRF %d", __func__, info->vrf_id); + return -1; + } + + zvrf = vrf->info; + if (zvrf == NULL) { + LOG_GR("%s: Invalid VRF entry %d", __func__, info->vrf_id); + return -1; + } + + cnt = zebra_gr_delete_stale_route(info, zvrf); + return cnt; +} + +/* + * This function checks if route update for all AFI, SAFI is completed + * and cancels the stale timer + */ +static void zebra_gr_process_client_stale_routes(struct zserv *client, + vrf_id_t vrf_id) +{ + struct client_gr_info *info = NULL; + afi_t afi; + safi_t safi; + + TAILQ_FOREACH (info, &client->gr_info_queue, gr_info) { + if (info->vrf_id == vrf_id) + break; + } + + if (info == NULL) + return; + + /* Check if route update completed for all AFI, SAFI */ + for (afi = AFI_IP; afi < AFI_MAX; afi++) + for (safi = SAFI_UNICAST; safi <= SAFI_MPLS_VPN; safi++) { + if (info->af_enabled[afi][safi]) { + if (!info->route_sync[afi][safi]) { + LOG_GR( + "%s: Client %s route update not completed for AFI %d, SAFI %d", + __func__, zebra_route_string( + client->proto), + afi, safi); + return; + } + } + } + + /* + * Route update completed for all AFI, SAFI + * Cancel the stale timer and process the routes + */ + if (info->t_stale_removal) { + LOG_GR("%s: Client %s cancled stale delete timer vrf %d", + __func__, zebra_route_string(client->proto), + info->vrf_id); + THREAD_OFF(info->t_stale_removal); + thread_execute(zrouter.master, + zebra_gr_route_stale_delete_timer_expiry, info, + 0); + } +} diff --git a/zebra/zebra_l2.c b/zebra/zebra_l2.c index ca37dd748e..417056ecb0 100644 --- a/zebra/zebra_l2.c +++ b/zebra/zebra_l2.c @@ -43,6 +43,7 @@ #include "zebra/rt_netlink.h" #include "zebra/zebra_l2.h" #include "zebra/zebra_vxlan.h" +#include "zebra/zebra_evpn_mh.h" /* definitions */ @@ -53,7 +54,13 @@ static void map_slaves_to_bridge(struct interface *br_if, int link) { struct vrf *vrf; struct interface *ifp; + struct zebra_vrf *zvrf; + struct zebra_ns *zns; + zvrf = zebra_vrf_lookup_by_id(br_if->vrf_id); + assert(zvrf); + zns = zvrf->zns; + assert(zns); RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { FOR_ALL_INTERFACES (vrf, ifp) { struct zebra_if *zif; @@ -72,7 +79,8 @@ static void map_slaves_to_bridge(struct interface *br_if, int link) br_slave = &zif->brslave_info; if (link) { - if (br_slave->bridge_ifindex == br_if->ifindex) + if (br_slave->bridge_ifindex == br_if->ifindex && + br_slave->ns_id == zns->ns_id) br_slave->br_if = br_if; } else { if (br_slave->br_if == br_if) @@ -83,12 +91,14 @@ static void map_slaves_to_bridge(struct interface *br_if, int link) } /* Public functions */ -void zebra_l2_map_slave_to_bridge(struct zebra_l2info_brslave *br_slave) +void zebra_l2_map_slave_to_bridge(struct zebra_l2info_brslave *br_slave, + struct zebra_ns *zns) { struct interface *br_if; /* TODO: Handle change of master */ - br_if = if_lookup_by_index_per_ns(zebra_ns_lookup(NS_DEFAULT), + assert(zns); + br_if = if_lookup_by_index_per_ns(zebra_ns_lookup(zns->ns_id), br_slave->bridge_ifindex); if (br_if) br_slave->br_if = br_if; @@ -99,15 +109,18 @@ void zebra_l2_unmap_slave_from_bridge(struct zebra_l2info_brslave *br_slave) br_slave->br_if = NULL; } -void zebra_l2_map_slave_to_bond(struct zebra_l2info_bondslave *bond_slave) +void zebra_l2_map_slave_to_bond(struct zebra_l2info_bondslave *bond_slave, + vrf_id_t vrf_id) { struct interface *bond_if; /* TODO: Handle change of master */ - bond_if = if_lookup_by_index_per_ns(zebra_ns_lookup(NS_DEFAULT), - bond_slave->bond_ifindex); + bond_if = if_lookup_by_index_all_vrf(bond_slave->bond_ifindex); if (bond_if) bond_slave->bond_if = bond_if; + else + bond_slave->bond_if = if_create_ifindex(bond_slave->bond_ifindex, + vrf_id); } void zebra_l2_unmap_slave_from_bond(struct zebra_l2info_bondslave *bond_slave) @@ -179,6 +192,7 @@ void zebra_l2_vxlanif_add_update(struct interface *ifp, if (add) { memcpy(&zif->l2info.vxl, vxlan_info, sizeof(*vxlan_info)); + zebra_evpn_vl_vxl_ref(zif->l2info.vxl.access_vlan, zif); zebra_vxlan_if_add(ifp); return; } @@ -217,6 +231,9 @@ void zebra_l2_vxlanif_update_access_vlan(struct interface *ifp, return; zif->l2info.vxl.access_vlan = access_vlan; + + zebra_evpn_vl_vxl_deref(old_access_vlan, zif); + zebra_evpn_vl_vxl_ref(zif->l2info.vxl.access_vlan, zif); zebra_vxlan_if_update(ifp, ZEBRA_VXLIF_VLAN_CHANGE); } @@ -225,6 +242,12 @@ void zebra_l2_vxlanif_update_access_vlan(struct interface *ifp, */ void zebra_l2_vxlanif_del(struct interface *ifp) { + struct zebra_if *zif; + + zif = ifp->info; + assert(zif); + + zebra_evpn_vl_vxl_deref(zif->l2info.vxl.access_vlan, zif); zebra_vxlan_if_del(ifp); } @@ -234,23 +257,32 @@ void zebra_l2_vxlanif_del(struct interface *ifp) * from a bridge before it can be mapped to another bridge. */ void zebra_l2if_update_bridge_slave(struct interface *ifp, - ifindex_t bridge_ifindex) + ifindex_t bridge_ifindex, + ns_id_t ns_id) { struct zebra_if *zif; ifindex_t old_bridge_ifindex; + ns_id_t old_ns_id; + struct zebra_vrf *zvrf; zif = ifp->info; assert(zif); + zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id); + if (!zvrf) + return; + old_bridge_ifindex = zif->brslave_info.bridge_ifindex; - if (old_bridge_ifindex == bridge_ifindex) + old_ns_id = zif->brslave_info.ns_id; + if (old_bridge_ifindex == bridge_ifindex && + old_ns_id == zif->brslave_info.ns_id) return; + zif->brslave_info.ns_id = ns_id; zif->brslave_info.bridge_ifindex = bridge_ifindex; - /* Set up or remove link with master */ if (bridge_ifindex != IFINDEX_INTERNAL) { - zebra_l2_map_slave_to_bridge(&zif->brslave_info); + zebra_l2_map_slave_to_bridge(&zif->brslave_info, zvrf->zns); /* In the case of VxLAN, invoke the handler for EVPN. */ if (zif->zif_type == ZEBRA_IF_VXLAN) zebra_vxlan_if_update(ifp, ZEBRA_VXLIF_MASTER_CHANGE); @@ -282,7 +314,47 @@ void zebra_l2if_update_bond_slave(struct interface *ifp, ifindex_t bond_ifindex) /* Set up or remove link with master */ if (bond_ifindex != IFINDEX_INTERNAL) - zebra_l2_map_slave_to_bond(&zif->bondslave_info); + zebra_l2_map_slave_to_bond(&zif->bondslave_info, ifp->vrf_id); else if (old_bond_ifindex != IFINDEX_INTERNAL) zebra_l2_unmap_slave_from_bond(&zif->bondslave_info); } + +void zebra_vlan_bitmap_compute(struct interface *ifp, + uint32_t vid_start, uint16_t vid_end) +{ + uint32_t vid; + struct zebra_if *zif; + + zif = (struct zebra_if *)ifp->info; + assert(zif); + + for (vid = vid_start; vid <= vid_end; ++vid) + bf_set_bit(zif->vlan_bitmap, vid); +} + +void zebra_vlan_mbr_re_eval(struct interface *ifp, bitfield_t old_vlan_bitmap) +{ + uint32_t vid; + struct zebra_if *zif; + + zif = (struct zebra_if *)ifp->info; + assert(zif); + + if (!bf_cmp(zif->vlan_bitmap, old_vlan_bitmap)) + /* no change */ + return; + + bf_for_each_set_bit(zif->vlan_bitmap, vid, IF_VLAN_BITMAP_MAX) { + /* if not already set create new reference */ + if (!bf_test_index(old_vlan_bitmap, vid)) + zebra_evpn_vl_mbr_ref(vid, zif); + + /* also clear from the old vlan bitmap */ + bf_release_index(old_vlan_bitmap, vid); + } + + /* any bits remaining in the old vlan bitmap are stale references */ + bf_for_each_set_bit(old_vlan_bitmap, vid, IF_VLAN_BITMAP_MAX) { + zebra_evpn_vl_mbr_deref(vid, zif); + } +} diff --git a/zebra/zebra_l2.h b/zebra/zebra_l2.h index 33aa2e3746..f3b15c7770 100644 --- a/zebra/zebra_l2.h +++ b/zebra/zebra_l2.h @@ -37,6 +37,7 @@ extern "C" { struct zebra_l2info_brslave { ifindex_t bridge_ifindex; /* Bridge Master */ struct interface *br_if; /* Pointer to master */ + ns_id_t ns_id; /* network namespace where bridge is */ }; /* zebra L2 interface information - bridge interface */ @@ -55,6 +56,10 @@ struct zebra_l2info_vxlan { struct in_addr vtep_ip; /* Local tunnel IP */ vlanid_t access_vlan; /* Access VLAN - for VLAN-aware bridge. */ struct in_addr mcast_grp; + ifindex_t ifindex_link; /* Interface index of interface + * linked with VXLAN + */ + ns_id_t link_nsid; }; struct zebra_l2info_bondslave { @@ -77,11 +82,12 @@ union zebra_l2if_info { #define IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(zif) ((zif)->l2info.br.vlan_aware == 1) -extern void zebra_l2_map_slave_to_bridge(struct zebra_l2info_brslave *br_slave); +extern void zebra_l2_map_slave_to_bridge(struct zebra_l2info_brslave *br_slave, + struct zebra_ns *zns); extern void zebra_l2_unmap_slave_from_bridge(struct zebra_l2info_brslave *br_slave); extern void -zebra_l2_map_slave_to_bond(struct zebra_l2info_bondslave *bond_slave); +zebra_l2_map_slave_to_bond(struct zebra_l2info_bondslave *bond_slave, vrf_id_t); extern void zebra_l2_unmap_slave_from_bond(struct zebra_l2info_bondslave *bond_slave); extern void zebra_l2_bridge_add_update(struct interface *ifp, @@ -97,10 +103,15 @@ extern void zebra_l2_vxlanif_update_access_vlan(struct interface *ifp, vlanid_t access_vlan); extern void zebra_l2_vxlanif_del(struct interface *ifp); extern void zebra_l2if_update_bridge_slave(struct interface *ifp, - ifindex_t bridge_ifindex); + ifindex_t bridge_ifindex, + ns_id_t ns_id); extern void zebra_l2if_update_bond_slave(struct interface *ifp, ifindex_t bond_ifindex); +extern void zebra_vlan_bitmap_compute(struct interface *ifp, + uint32_t vid_start, uint16_t vid_end); +extern void zebra_vlan_mbr_re_eval(struct interface *ifp, + bitfield_t vlan_bitmap); #ifdef __cplusplus } diff --git a/zebra/zebra_memory.c b/zebra/zebra_memory.c index ee041b1c3d..da8121774e 100644 --- a/zebra/zebra_memory.c +++ b/zebra/zebra_memory.c @@ -26,11 +26,7 @@ #include "zebra_memory.h" DEFINE_MGROUP(ZEBRA, "zebra") -DEFINE_MTYPE(ZEBRA, RTADV_PREFIX, "Router Advertisement Prefix") -DEFINE_MTYPE(ZEBRA, ZEBRA_VRF, "ZEBRA VRF") DEFINE_MTYPE(ZEBRA, RE, "Route Entry") -DEFINE_MTYPE(ZEBRA, RIB_QUEUE, "RIB process work queue") -DEFINE_MTYPE(ZEBRA, STATIC_ROUTE, "Static route") DEFINE_MTYPE(ZEBRA, RIB_DEST, "RIB destination") -DEFINE_MTYPE(ZEBRA, RIB_TABLE_INFO, "RIB table info") -DEFINE_MTYPE(ZEBRA, RNH, "Nexthop tracking object") +DEFINE_MTYPE(ZEBRA, ZVLAN, "VLAN") +DEFINE_MTYPE(ZEBRA, ZVLAN_BITMAP, "VLAN bitmap") diff --git a/zebra/zebra_memory.h b/zebra/zebra_memory.h index 667c73b227..e15f972493 100644 --- a/zebra/zebra_memory.h +++ b/zebra/zebra_memory.h @@ -29,17 +29,9 @@ extern "C" { #endif DECLARE_MGROUP(ZEBRA) -DECLARE_MTYPE(RTADV_PREFIX) DECLARE_MTYPE(ZEBRA_NS) -DECLARE_MTYPE(ZEBRA_VRF) DECLARE_MTYPE(RE) -DECLARE_MTYPE(RIB_QUEUE) -DECLARE_MTYPE(STATIC_ROUTE) DECLARE_MTYPE(RIB_DEST) -DECLARE_MTYPE(RIB_TABLE_INFO) -DECLARE_MTYPE(RNH) -DECLARE_MTYPE(DP_CTX) -DECLARE_MTYPE(DP_PROV) #ifdef __cplusplus } diff --git a/zebra/zebra_mlag.c b/zebra/zebra_mlag.c index 5012cc2a49..fb8798ebd9 100644 --- a/zebra/zebra_mlag.c +++ b/zebra/zebra_mlag.c @@ -23,48 +23,575 @@ #include "command.h" #include "hook.h" +#include "frr_pthread.h" +#include "mlag.h" #include "zebra/zebra_mlag.h" +#include "zebra/zebra_mlag_vty.h" #include "zebra/zebra_router.h" +#include "zebra/zebra_memory.h" #include "zebra/zapi_msg.h" #include "zebra/debug.h" -#ifndef VTYSH_EXTRACT_PL -#include "zebra/zebra_mlag_clippy.c" +#ifdef HAVE_PROTOBUF_VERSION_3 +#include "mlag/mlag.pb-c.h" #endif -enum mlag_role zebra_mlag_get_role(void) +DEFINE_HOOK(zebra_mlag_private_write_data, + (uint8_t *data, uint32_t len), (data, len)) +DEFINE_HOOK(zebra_mlag_private_monitor_state, (), ()) +DEFINE_HOOK(zebra_mlag_private_open_channel, (), ()) +DEFINE_HOOK(zebra_mlag_private_close_channel, (), ()) +DEFINE_HOOK(zebra_mlag_private_cleanup_data, (), ()) + +#define ZEBRA_MLAG_METADATA_LEN 4 +#define ZEBRA_MLAG_MSG_BCAST 0xFFFFFFFF + +uint8_t mlag_wr_buffer[ZEBRA_MLAG_BUF_LIMIT]; +uint8_t mlag_rd_buffer[ZEBRA_MLAG_BUF_LIMIT]; +uint32_t mlag_rd_buf_offset; + +static bool test_mlag_in_progress; + +static int zebra_mlag_signal_write_thread(void); +static int zebra_mlag_terminate_pthread(struct thread *event); +static int zebra_mlag_post_data_from_main_thread(struct thread *thread); +static void zebra_mlag_publish_process_state(struct zserv *client, + zebra_message_types_t msg_type); + +/**********************MLAG Interaction***************************************/ + +/* + * API to post the Registration to MLAGD + * MLAG will not process any messages with out the registration + */ +void zebra_mlag_send_register(void) { - return zrouter.mlag_info.role; + struct stream *s = NULL; + + s = stream_new(sizeof(struct mlag_msg)); + + stream_putl(s, MLAG_REGISTER); + stream_putw(s, MLAG_MSG_NULL_PAYLOAD); + stream_putw(s, MLAG_MSG_NO_BATCH); + stream_fifo_push_safe(zrouter.mlag_info.mlag_fifo, s); + zebra_mlag_signal_write_thread(); + + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug("%s: Enqueued MLAG Register to MLAG Thread ", + __func__); } -DEFUN_HIDDEN (show_mlag, - show_mlag_cmd, - "show zebra mlag", - SHOW_STR - ZEBRA_STR - "The mlag role on this machine\n") +/* + * API to post the De-Registration to MLAGD + * MLAG will not process any messages after the de-registration + */ +void zebra_mlag_send_deregister(void) { - char buf[80]; + struct stream *s = NULL; - vty_out(vty, "MLag is configured to: %s\n", - mlag_role2str(zrouter.mlag_info.role, buf, sizeof(buf))); + s = stream_new(sizeof(struct mlag_msg)); - return CMD_SUCCESS; + stream_putl(s, MLAG_DEREGISTER); + stream_putw(s, MLAG_MSG_NULL_PAYLOAD); + stream_putw(s, MLAG_MSG_NO_BATCH); + stream_fifo_push_safe(zrouter.mlag_info.mlag_fifo, s); + zebra_mlag_signal_write_thread(); + + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug("%s: Enqueued MLAG De-Register to MLAG Thread ", + __func__); +} + +/* + * API To handle MLAG Received data + * Decodes the data using protobuf and enqueue to main thread + * main thread publish this to clients based on client subscription + */ +void zebra_mlag_process_mlag_data(uint8_t *data, uint32_t len) +{ + struct stream *s = NULL; + int msg_type = 0; + + s = stream_new(ZEBRA_MAX_PACKET_SIZ); + /* + * Place holder we need the message type first + */ + stream_putl(s, msg_type); + msg_type = zebra_mlag_protobuf_decode_message(s, data, len); + + if (msg_type <= 0) { + /* Something went wrong in decoding */ + stream_free(s); + zlog_err("%s: failed to process mlag data-%d, %u", __func__, + msg_type, len); + return; + } + + /* + * additional four bytes are for message type + */ + stream_putl_at(s, 0, msg_type); + thread_add_event(zrouter.master, zebra_mlag_post_data_from_main_thread, + s, 0, NULL); +} + +/**********************End of MLAG Interaction********************************/ + +/************************MLAG Thread Processing*******************************/ + +/* + * after posting every 'ZEBRA_MLAG_POST_LIMIT' packets, MLAG Thread will be + * yielded to give CPU for other threads + */ +#define ZEBRA_MLAG_POST_LIMIT 100 + +/* + * This thread reads the clients data from the Global queue and encodes with + * protobuf and pass on to the MLAG socket. + */ +static int zebra_mlag_client_msg_handler(struct thread *event) +{ + struct stream *s; + uint32_t wr_count = 0; + uint32_t msg_type = 0; + uint32_t max_count = 0; + int len = 0; + + wr_count = stream_fifo_count_safe(zrouter.mlag_info.mlag_fifo); + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug(":%s: Processing MLAG write, %u messages in queue", + __func__, wr_count); + + max_count = MIN(wr_count, ZEBRA_MLAG_POST_LIMIT); + + for (wr_count = 0; wr_count < max_count; wr_count++) { + s = stream_fifo_pop_safe(zrouter.mlag_info.mlag_fifo); + if (!s) { + zlog_debug(":%s: Got a NULL Messages, some thing wrong", + __func__); + break; + } + + /* + * Encode the data now + */ + len = zebra_mlag_protobuf_encode_client_data(s, &msg_type); + + /* + * write to MCLAGD + */ + if (len > 0) { + hook_call(zebra_mlag_private_write_data, + mlag_wr_buffer, len); + + /* + * If message type is De-register, send a signal to main + * thread, so that necessary cleanup will be done by + * main thread. + */ + if (msg_type == MLAG_DEREGISTER) { + thread_add_event(zrouter.master, + zebra_mlag_terminate_pthread, + NULL, 0, NULL); + } + } + + stream_free(s); + } + + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug(":%s: Posted %d messages to MLAGD", __func__, + wr_count); + /* + * Currently there is only message write task is enqueued to this + * thread, yielding was added for future purpose, so that this thread + * can server other tasks also and in case FIFO is empty, this task will + * be schedule when main thread adds some messages + */ + if (wr_count >= ZEBRA_MLAG_POST_LIMIT) + zebra_mlag_signal_write_thread(); + return 0; +} + +/* + * API to handle the process state. + * In case of Down, Zebra keep monitoring the MLAG state. + * all the state Notifications will be published to clients + */ +void zebra_mlag_handle_process_state(enum zebra_mlag_state state) +{ + if (state == MLAG_UP) { + zrouter.mlag_info.connected = true; + zebra_mlag_publish_process_state(NULL, ZEBRA_MLAG_PROCESS_UP); + zebra_mlag_send_register(); + } else if (state == MLAG_DOWN) { + zrouter.mlag_info.connected = false; + zebra_mlag_publish_process_state(NULL, ZEBRA_MLAG_PROCESS_DOWN); + hook_call(zebra_mlag_private_monitor_state); + } +} + +/***********************End of MLAG Thread processing*************************/ + +/*************************Multi-entratnt Api's********************************/ + +/* + * Provider api to signal that work/events are available + * for the Zebra MLAG Write pthread. + * This API is called from 2 pthreads.. + * 1) by main thread when client posts a MLAG Message + * 2) by MLAG Thread, in case of yield + * though this api, is called from two threads we don't need any locking + * because Thread task enqueue is thread safe means internally it had + * necessary protection + */ +static int zebra_mlag_signal_write_thread(void) +{ + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug(":%s: Scheduling MLAG write", __func__); + /* + * This api will be called from Both main & MLAG Threads. + * main thread writes, "zrouter.mlag_info.th_master" only + * during Zebra Init/after MLAG thread is destroyed. + * so it is safe to use without any locking + */ + thread_add_event(zrouter.mlag_info.th_master, + zebra_mlag_client_msg_handler, NULL, 0, + &zrouter.mlag_info.t_write); + return 0; +} + +/* + * API will be used to publish the MLAG state to interested clients + * In case client is passed, state is posted only for that client, + * otherwise to all interested clients + * this api can be called from two threads. + * 1) from main thread: when client is passed + * 2) from MLAG Thread: when client is NULL + * + * In second case, to avoid global data access data will be post to Main + * thread, so that actual posting to clients will happen from Main thread. + */ +static void zebra_mlag_publish_process_state(struct zserv *client, + zebra_message_types_t msg_type) +{ + struct stream *s; + + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug("%s: Publishing MLAG process state:%s to %s Client", + __func__, + (msg_type == ZEBRA_MLAG_PROCESS_UP) ? "UP" : "DOWN", + (client) ? "one" : "all"); + + if (client) { + s = stream_new(ZEBRA_HEADER_SIZE); + zclient_create_header(s, msg_type, VRF_DEFAULT); + zserv_send_message(client, s); + return; + } + + + /* + * additional four bytes are for mesasge type + */ + s = stream_new(ZEBRA_HEADER_SIZE + ZEBRA_MLAG_METADATA_LEN); + stream_putl(s, ZEBRA_MLAG_MSG_BCAST); + zclient_create_header(s, msg_type, VRF_DEFAULT); + thread_add_event(zrouter.master, zebra_mlag_post_data_from_main_thread, + s, 0, NULL); +} + +/**************************End of Multi-entrant Apis**************************/ + +/***********************Zebra Main thread processing**************************/ + +/* + * To avoid data corruption, messages will be post to clients only from + * main thread, because for that access was needed for clients list. + * so instead of forcing the locks, messages will be posted from main thread. + */ +static int zebra_mlag_post_data_from_main_thread(struct thread *thread) +{ + struct stream *s = THREAD_ARG(thread); + struct stream *zebra_s = NULL; + struct listnode *node; + struct zserv *client; + uint32_t msg_type = 0; + uint32_t msg_len = 0; + + if (!s) + return -1; + + STREAM_GETL(s, msg_type); + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug( + "%s: Posting MLAG data for msg_type:0x%x to interested clients", + __func__, msg_type); + + msg_len = s->endp - ZEBRA_MLAG_METADATA_LEN; + for (ALL_LIST_ELEMENTS_RO(zrouter.client_list, node, client)) { + if (client->mlag_updates_interested == true) { + if (msg_type != ZEBRA_MLAG_MSG_BCAST + && !CHECK_FLAG(client->mlag_reg_mask1, + (1 << msg_type))) { + continue; + } + + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug( + "%s: Posting MLAG data of length-%d to client:%d ", + __func__, msg_len, client->proto); + + zebra_s = stream_new(msg_len); + STREAM_GET(zebra_s->data, s, msg_len); + zebra_s->endp = msg_len; + stream_putw_at(zebra_s, 0, msg_len); + + /* + * This stream will be enqueued to client_obuf, it will + * be freed after posting to client socket. + */ + zserv_send_message(client, zebra_s); + zebra_s = NULL; + } + } + + stream_free(s); + return 0; +stream_failure: + stream_free(s); + if (zebra_s) + stream_free(zebra_s); + return 0; +} + +/* + * Start the MLAG Thread, this will be used to write client data on to + * MLAG Process and to read the data from MLAG and post to clients. + * when all clients are un-registered, this Thread will be + * suspended. + */ +static void zebra_mlag_spawn_pthread(void) +{ + /* Start MLAG write pthread */ + + struct frr_pthread_attr pattr = {.start = + frr_pthread_attr_default.start, + .stop = frr_pthread_attr_default.stop}; + + zrouter.mlag_info.zebra_pth_mlag = + frr_pthread_new(&pattr, "Zebra MLAG thread", "Zebra MLAG"); + + zrouter.mlag_info.th_master = zrouter.mlag_info.zebra_pth_mlag->master; + + + /* Enqueue an initial event to the Newly spawn MLAG pthread */ + zebra_mlag_signal_write_thread(); + + frr_pthread_run(zrouter.mlag_info.zebra_pth_mlag, NULL); +} + +/* + * all clients are un-registered for MLAG Updates, terminate the + * MLAG write thread + */ +static int zebra_mlag_terminate_pthread(struct thread *event) +{ + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug("Zebra MLAG write thread terminate called"); + + if (zrouter.mlag_info.clients_interested_cnt) { + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug( + "Zebra MLAG: still some clients are interested"); + return 0; + } + + frr_pthread_stop(zrouter.mlag_info.zebra_pth_mlag, NULL); + + /* Destroy pthread */ + frr_pthread_destroy(zrouter.mlag_info.zebra_pth_mlag); + zrouter.mlag_info.zebra_pth_mlag = NULL; + zrouter.mlag_info.th_master = NULL; + zrouter.mlag_info.t_read = NULL; + zrouter.mlag_info.t_write = NULL; + + /* + * Send Notification to clean private data + */ + hook_call(zebra_mlag_private_cleanup_data); + return 0; +} + +/* + * API to register zebra client for MLAG Updates + */ +void zebra_mlag_client_register(ZAPI_HANDLER_ARGS) +{ + struct stream *s; + uint32_t reg_mask = 0; + int rc = 0; + + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug("Received MLAG Registration from client-proto:%d", + client->proto); + + + /* Get input stream. */ + s = msg; + + /* Get data. */ + STREAM_GETL(s, reg_mask); + + if (client->mlag_updates_interested == true) { + + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug( + "Client is registered, existing mask: 0x%x, new mask: 0x%x", + client->mlag_reg_mask1, reg_mask); + if (client->mlag_reg_mask1 != reg_mask) + client->mlag_reg_mask1 = reg_mask; + /* + * Client might missed MLAG-UP Notification, post-it again + */ + zebra_mlag_publish_process_state(client, ZEBRA_MLAG_PROCESS_UP); + return; + } + + + client->mlag_updates_interested = true; + client->mlag_reg_mask1 = reg_mask; + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug("Registering for MLAG Updates with mask: 0x%x, ", + client->mlag_reg_mask1); + + zrouter.mlag_info.clients_interested_cnt++; + + if (zrouter.mlag_info.clients_interested_cnt == 1) { + /* + * First-client for MLAG Updates,open the communication channel + * with MLAG + */ + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug( + "First client, opening the channel with MLAG"); + + zebra_mlag_spawn_pthread(); + rc = hook_call(zebra_mlag_private_open_channel); + if (rc < 0) { + /* + * For some reason, zebra not able to open the + * comm-channel with MLAG, so post MLAG-DOWN to client. + * later when the channel is open, zebra will send + * MLAG-UP + */ + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug( + "Fail to open channel with MLAG,rc:%d, post Proto-down", + rc); + zebra_mlag_publish_process_state( + client, ZEBRA_MLAG_PROCESS_DOWN); + } + } + + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug("Client Registered successfully for MLAG Updates"); + + if (zrouter.mlag_info.connected == true) + zebra_mlag_publish_process_state(client, ZEBRA_MLAG_PROCESS_UP); +stream_failure: + return; } -DEFPY_HIDDEN (test_mlag, - test_mlag_cmd, - "test zebra mlag ", - "Test code\n" - ZEBRA_STR - "Modify the Mlag state\n" - "Mlag is not setup on the machine\n" - "Mlag is setup to be primary\n" - "Mlag is setup to be the secondary\n") +/* + * API to un-register for MLAG Updates + */ +void zebra_mlag_client_unregister(ZAPI_HANDLER_ARGS) +{ + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug("Received MLAG De-Registration from client-proto:%d", + client->proto); + + if (client->mlag_updates_interested == false) + /* Unexpected */ + return; + + client->mlag_updates_interested = false; + client->mlag_reg_mask1 = 0; + zrouter.mlag_info.clients_interested_cnt--; + + if (zrouter.mlag_info.clients_interested_cnt == 0) { + /* + * No-client is interested for MLAG Updates,close the + * communication channel with MLAG + */ + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug("Last client for MLAG, close the channel "); + + /* + * Clean up flow: + * ============= + * 1) main thread calls socket close which posts De-register + * to MLAG write thread + * 2) after MLAG write thread posts De-register it sends a + * signal back to main thread to do the thread cleanup + * this was mainly to make sure De-register is posted to MCLAGD. + */ + hook_call(zebra_mlag_private_close_channel); + } + + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug( + "Client De-Registered successfully for MLAG Updates"); +} + +/* + * Does following things. + * 1) allocated new local stream, and copies the client data and enqueue + * to MLAG Thread + * 2) MLAG Thread after dequeing, encode the client data using protobuf + * and write on to MLAG + */ +void zebra_mlag_forward_client_msg(ZAPI_HANDLER_ARGS) +{ + struct stream *zebra_s; + struct stream *mlag_s; + + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug("Received Client MLAG Data from client-proto:%d", + client->proto); + + /* Get input stream. */ + zebra_s = msg; + mlag_s = stream_new(zebra_s->endp); + + /* + * Client data is | Zebra Header + MLAG Data | + * we need to enqueue only the MLAG data, skipping Zebra Header + */ + stream_put(mlag_s, zebra_s->data + zebra_s->getp, + STREAM_READABLE(zebra_s)); + stream_fifo_push_safe(zrouter.mlag_info.mlag_fifo, mlag_s); + zebra_mlag_signal_write_thread(); + + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug("%s: Enqueued Client:%d data to MLAG Thread ", + __func__, client->proto); +} + +/***********************End of Zebra Main thread processing*************/ + +enum mlag_role zebra_mlag_get_role(void) +{ + return zrouter.mlag_info.role; +} + +int32_t zebra_mlag_test_mlag_internal(const char *none, const char *primary, + const char *secondary) { enum mlag_role orig = zrouter.mlag_info.role; - char buf1[80], buf2[80]; + char buf1[MLAG_ROLE_STRSIZE], buf2[MLAG_ROLE_STRSIZE]; if (none) zrouter.mlag_info.role = MLAG_ROLE_NONE; @@ -78,18 +605,595 @@ DEFPY_HIDDEN (test_mlag, mlag_role2str(orig, buf1, sizeof(buf1)), mlag_role2str(orig, buf2, sizeof(buf2))); - if (orig != zrouter.mlag_info.role) + if (orig != zrouter.mlag_info.role) { zsend_capabilities_all_clients(); + if (zrouter.mlag_info.role != MLAG_ROLE_NONE) { + if (zrouter.mlag_info.clients_interested_cnt == 0 + && !test_mlag_in_progress) { + if (zrouter.mlag_info.zebra_pth_mlag == NULL) + zebra_mlag_spawn_pthread(); + zrouter.mlag_info.clients_interested_cnt++; + test_mlag_in_progress = true; + hook_call(zebra_mlag_private_open_channel); + } + } else { + if (test_mlag_in_progress) { + test_mlag_in_progress = false; + zrouter.mlag_info.clients_interested_cnt--; + hook_call(zebra_mlag_private_close_channel); + } + } + } return CMD_SUCCESS; } void zebra_mlag_init(void) { - install_element(VIEW_NODE, &show_mlag_cmd); - install_element(ENABLE_NODE, &test_mlag_cmd); + zebra_mlag_vty_init(); + + /* + * Intialiaze the MLAG Global variables + * write thread will be created during actual registration with MCLAG + */ + zrouter.mlag_info.clients_interested_cnt = 0; + zrouter.mlag_info.connected = false; + zrouter.mlag_info.timer_running = false; + zrouter.mlag_info.mlag_fifo = stream_fifo_new(); + zrouter.mlag_info.zebra_pth_mlag = NULL; + zrouter.mlag_info.th_master = NULL; + zrouter.mlag_info.t_read = NULL; + zrouter.mlag_info.t_write = NULL; + test_mlag_in_progress = false; + zebra_mlag_reset_read_buffer(); } void zebra_mlag_terminate(void) { } + + +/* + * + * ProtoBuf Encoding APIs + */ + +#ifdef HAVE_PROTOBUF_VERSION_3 + +DEFINE_MTYPE_STATIC(ZEBRA, MLAG_PBUF, "ZEBRA MLAG PROTOBUF") + +int zebra_mlag_protobuf_encode_client_data(struct stream *s, uint32_t *msg_type) +{ + ZebraMlagHeader hdr = ZEBRA_MLAG__HEADER__INIT; + struct mlag_msg mlag_msg; + uint8_t tmp_buf[ZEBRA_MLAG_BUF_LIMIT]; + int len = 0; + int n_len = 0; + int rc = 0; + char buf[ZLOG_FILTER_LENGTH_MAX]; + size_t length; + + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug("%s: Entering..", __func__); + + rc = mlag_lib_decode_mlag_hdr(s, &mlag_msg, &length); + if (rc) + return rc; + + memset(tmp_buf, 0, ZEBRA_MLAG_BUF_LIMIT); + + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug("%s: Mlag ProtoBuf encoding of message:%s, len:%d", + __func__, + mlag_lib_msgid_to_str(mlag_msg.msg_type, buf, + sizeof(buf)), + mlag_msg.data_len); + *msg_type = mlag_msg.msg_type; + switch (mlag_msg.msg_type) { + case MLAG_MROUTE_ADD: { + struct mlag_mroute_add msg; + ZebraMlagMrouteAdd pay_load = ZEBRA_MLAG_MROUTE_ADD__INIT; + uint32_t vrf_name_len = 0; + + rc = mlag_lib_decode_mroute_add(s, &msg, &length); + if (rc) + return rc; + + vrf_name_len = strlen(msg.vrf_name) + 1; + pay_load.vrf_name = XMALLOC(MTYPE_MLAG_PBUF, vrf_name_len); + strlcpy(pay_load.vrf_name, msg.vrf_name, vrf_name_len); + pay_load.source_ip = msg.source_ip; + pay_load.group_ip = msg.group_ip; + pay_load.cost_to_rp = msg.cost_to_rp; + pay_load.owner_id = msg.owner_id; + pay_load.am_i_dr = msg.am_i_dr; + pay_load.am_i_dual_active = msg.am_i_dual_active; + pay_load.vrf_id = msg.vrf_id; + + if (msg.owner_id == MLAG_OWNER_INTERFACE) { + vrf_name_len = strlen(msg.intf_name) + 1; + pay_load.intf_name = + XMALLOC(MTYPE_MLAG_PBUF, vrf_name_len); + strlcpy(pay_load.intf_name, msg.intf_name, + vrf_name_len); + } + + len = zebra_mlag_mroute_add__pack(&pay_load, tmp_buf); + XFREE(MTYPE_MLAG_PBUF, pay_load.vrf_name); + if (msg.owner_id == MLAG_OWNER_INTERFACE) + XFREE(MTYPE_MLAG_PBUF, pay_load.intf_name); + } break; + case MLAG_MROUTE_DEL: { + struct mlag_mroute_del msg; + ZebraMlagMrouteDel pay_load = ZEBRA_MLAG_MROUTE_DEL__INIT; + uint32_t vrf_name_len = 0; + + rc = mlag_lib_decode_mroute_del(s, &msg, &length); + if (rc) + return rc; + vrf_name_len = strlen(msg.vrf_name) + 1; + pay_load.vrf_name = XMALLOC(MTYPE_MLAG_PBUF, vrf_name_len); + strlcpy(pay_load.vrf_name, msg.vrf_name, vrf_name_len); + pay_load.source_ip = msg.source_ip; + pay_load.group_ip = msg.group_ip; + pay_load.owner_id = msg.owner_id; + pay_load.vrf_id = msg.vrf_id; + + if (msg.owner_id == MLAG_OWNER_INTERFACE) { + vrf_name_len = strlen(msg.intf_name) + 1; + pay_load.intf_name = + XMALLOC(MTYPE_MLAG_PBUF, vrf_name_len); + strlcpy(pay_load.intf_name, msg.intf_name, + vrf_name_len); + } + + len = zebra_mlag_mroute_del__pack(&pay_load, tmp_buf); + XFREE(MTYPE_MLAG_PBUF, pay_load.vrf_name); + if (msg.owner_id == MLAG_OWNER_INTERFACE) + XFREE(MTYPE_MLAG_PBUF, pay_load.intf_name); + } break; + case MLAG_MROUTE_ADD_BULK: { + struct mlag_mroute_add msg; + ZebraMlagMrouteAddBulk Bulk_msg = + ZEBRA_MLAG_MROUTE_ADD_BULK__INIT; + ZebraMlagMrouteAdd **pay_load = NULL; + bool cleanup = false; + uint32_t i, actual; + + Bulk_msg.n_mroute_add = mlag_msg.msg_cnt; + pay_load = XMALLOC(MTYPE_MLAG_PBUF, sizeof(ZebraMlagMrouteAdd *) + * mlag_msg.msg_cnt); + + for (i = 0, actual = 0; i < mlag_msg.msg_cnt; i++, actual++) { + + uint32_t vrf_name_len = 0; + + rc = mlag_lib_decode_mroute_add(s, &msg, &length); + if (rc) { + cleanup = true; + break; + } + pay_load[i] = XMALLOC(MTYPE_MLAG_PBUF, + sizeof(ZebraMlagMrouteAdd)); + zebra_mlag_mroute_add__init(pay_load[i]); + + vrf_name_len = strlen(msg.vrf_name) + 1; + pay_load[i]->vrf_name = + XMALLOC(MTYPE_MLAG_PBUF, vrf_name_len); + strlcpy(pay_load[i]->vrf_name, msg.vrf_name, + vrf_name_len); + pay_load[i]->source_ip = msg.source_ip; + pay_load[i]->group_ip = msg.group_ip; + pay_load[i]->cost_to_rp = msg.cost_to_rp; + pay_load[i]->owner_id = msg.owner_id; + pay_load[i]->am_i_dr = msg.am_i_dr; + pay_load[i]->am_i_dual_active = msg.am_i_dual_active; + pay_load[i]->vrf_id = msg.vrf_id; + if (msg.owner_id == MLAG_OWNER_INTERFACE) { + vrf_name_len = strlen(msg.intf_name) + 1; + pay_load[i]->intf_name = + XMALLOC(MTYPE_MLAG_PBUF, vrf_name_len); + + strlcpy(pay_load[i]->intf_name, msg.intf_name, + vrf_name_len); + } + } + if (!cleanup) { + Bulk_msg.mroute_add = pay_load; + len = zebra_mlag_mroute_add_bulk__pack(&Bulk_msg, + tmp_buf); + } + + for (i = 0; i < actual; i++) { + /* + * The mlag_lib_decode_mroute_add can + * fail to properly decode and cause nothing + * to be allocated. Prevent a crash + */ + if (!pay_load[i]) + continue; + + XFREE(MTYPE_MLAG_PBUF, pay_load[i]->vrf_name); + if (pay_load[i]->owner_id == MLAG_OWNER_INTERFACE + && pay_load[i]->intf_name) + XFREE(MTYPE_MLAG_PBUF, pay_load[i]->intf_name); + XFREE(MTYPE_MLAG_PBUF, pay_load[i]); + } + XFREE(MTYPE_MLAG_PBUF, pay_load); + if (cleanup) + return -1; + } break; + case MLAG_MROUTE_DEL_BULK: { + struct mlag_mroute_del msg; + ZebraMlagMrouteDelBulk Bulk_msg = + ZEBRA_MLAG_MROUTE_DEL_BULK__INIT; + ZebraMlagMrouteDel **pay_load = NULL; + bool cleanup = false; + uint32_t i, actual; + + Bulk_msg.n_mroute_del = mlag_msg.msg_cnt; + pay_load = XMALLOC(MTYPE_MLAG_PBUF, sizeof(ZebraMlagMrouteDel *) + * mlag_msg.msg_cnt); + + for (i = 0, actual = 0; i < mlag_msg.msg_cnt; i++, actual++) { + + uint32_t vrf_name_len = 0; + + rc = mlag_lib_decode_mroute_del(s, &msg, &length); + if (rc) { + cleanup = true; + break; + } + + pay_load[i] = XMALLOC(MTYPE_MLAG_PBUF, + sizeof(ZebraMlagMrouteDel)); + zebra_mlag_mroute_del__init(pay_load[i]); + + vrf_name_len = strlen(msg.vrf_name) + 1; + pay_load[i]->vrf_name = + XMALLOC(MTYPE_MLAG_PBUF, vrf_name_len); + + strlcpy(pay_load[i]->vrf_name, msg.vrf_name, + vrf_name_len); + pay_load[i]->source_ip = msg.source_ip; + pay_load[i]->group_ip = msg.group_ip; + pay_load[i]->owner_id = msg.owner_id; + pay_load[i]->vrf_id = msg.vrf_id; + if (msg.owner_id == MLAG_OWNER_INTERFACE) { + vrf_name_len = strlen(msg.intf_name) + 1; + pay_load[i]->intf_name = + XMALLOC(MTYPE_MLAG_PBUF, vrf_name_len); + + strlcpy(pay_load[i]->intf_name, msg.intf_name, + vrf_name_len); + } + } + if (!cleanup) { + Bulk_msg.mroute_del = pay_load; + len = zebra_mlag_mroute_del_bulk__pack(&Bulk_msg, + tmp_buf); + } + + for (i = 0; i < actual; i++) { + /* + * The mlag_lib_decode_mroute_add can + * fail to properly decode and cause nothing + * to be allocated. Prevent a crash + */ + if (!pay_load[i]) + continue; + + XFREE(MTYPE_MLAG_PBUF, pay_load[i]->vrf_name); + if (pay_load[i]->owner_id == MLAG_OWNER_INTERFACE + && pay_load[i]->intf_name) + XFREE(MTYPE_MLAG_PBUF, pay_load[i]->intf_name); + XFREE(MTYPE_MLAG_PBUF, pay_load[i]); + } + XFREE(MTYPE_MLAG_PBUF, pay_load); + if (cleanup) + return -1; + } break; + default: + break; + } + + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug("%s: length of Mlag ProtoBuf encoded message:%s, %d", + __func__, + mlag_lib_msgid_to_str(mlag_msg.msg_type, buf, + sizeof(buf)), + len); + hdr.type = (ZebraMlagHeader__MessageType)mlag_msg.msg_type; + if (len != 0) { + hdr.data.len = len; + hdr.data.data = XMALLOC(MTYPE_MLAG_PBUF, len); + memcpy(hdr.data.data, tmp_buf, len); + } + + /* + * ProtoBuf Infra will not support to demarc the pointers whem multiple + * messages are posted inside a single Buffer. + * 2 -solutions exist to solve this + * 1. add Unenoced length at the beginning of every message, this will + * be used to point to next message in the buffer + * 2. another solution is defining all messages insides another message + * But this will permit only 32 messages. this can be extended with + * multiple levels. + * for simplicity we are going with solution-1. + */ + len = zebra_mlag__header__pack(&hdr, + (mlag_wr_buffer + ZEBRA_MLAG_LEN_SIZE)); + n_len = htonl(len); + memcpy(mlag_wr_buffer, &n_len, ZEBRA_MLAG_LEN_SIZE); + len += ZEBRA_MLAG_LEN_SIZE; + + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug( + "%s: length of Mlag ProtoBuf message:%s with Header %d", + __func__, + mlag_lib_msgid_to_str(mlag_msg.msg_type, buf, + sizeof(buf)), + len); + XFREE(MTYPE_MLAG_PBUF, hdr.data.data); + + return len; +} + +static void zebra_fill_protobuf_msg(struct stream *s, char *name, int len) +{ + int str_len = strlen(name) + 1; + + stream_put(s, name, str_len); + /* Fill the rest with Null Character for aligning */ + stream_put(s, NULL, len - str_len); +} + +int zebra_mlag_protobuf_decode_message(struct stream *s, uint8_t *data, + uint32_t len) +{ + uint32_t msg_type; + ZebraMlagHeader *hdr; + char buf[80]; + + hdr = zebra_mlag__header__unpack(NULL, len, data); + if (hdr == NULL) + return -1; + + /* + * ADD The MLAG Header + */ + zclient_create_header(s, ZEBRA_MLAG_FORWARD_MSG, VRF_DEFAULT); + + msg_type = hdr->type; + + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug("%s: Mlag ProtoBuf decoding of message:%s", __func__, + mlag_lib_msgid_to_str(msg_type, buf, 80)); + + /* + * Internal MLAG Message-types & MLAG.proto message types should + * always match, otherwise there can be decoding errors + * To avoid exposing clients with Protobuf flags, using internal + * message-types + */ + stream_putl(s, hdr->type); + + if (hdr->data.len == 0) { + /* NULL Payload */ + stream_putw(s, MLAG_MSG_NULL_PAYLOAD); + /* No Batching */ + stream_putw(s, MLAG_MSG_NO_BATCH); + } else { + switch (msg_type) { + case ZEBRA_MLAG__HEADER__MESSAGE_TYPE__ZEBRA_MLAG_STATUS_UPDATE: { + ZebraMlagStatusUpdate *msg = NULL; + + msg = zebra_mlag_status_update__unpack( + NULL, hdr->data.len, hdr->data.data); + if (msg == NULL) { + zebra_mlag__header__free_unpacked(hdr, NULL); + return -1; + } + /* Payload len */ + stream_putw(s, sizeof(struct mlag_status)); + /* No Batching */ + stream_putw(s, MLAG_MSG_NO_BATCH); + /* Actual Data */ + zebra_fill_protobuf_msg(s, msg->peerlink, + INTERFACE_NAMSIZ); + stream_putl(s, msg->my_role); + stream_putl(s, msg->peer_state); + zebra_mlag_status_update__free_unpacked(msg, NULL); + } break; + case ZEBRA_MLAG__HEADER__MESSAGE_TYPE__ZEBRA_MLAG_VXLAN_UPDATE: { + ZebraMlagVxlanUpdate *msg = NULL; + + msg = zebra_mlag_vxlan_update__unpack( + NULL, hdr->data.len, hdr->data.data); + if (msg == NULL) { + zebra_mlag__header__free_unpacked(hdr, NULL); + return -1; + } + /* Payload len */ + stream_putw(s, sizeof(struct mlag_vxlan)); + /* No Batching */ + stream_putw(s, MLAG_MSG_NO_BATCH); + /* Actual Data */ + stream_putl(s, msg->anycast_ip); + stream_putl(s, msg->local_ip); + zebra_mlag_vxlan_update__free_unpacked(msg, NULL); + } break; + case ZEBRA_MLAG__HEADER__MESSAGE_TYPE__ZEBRA_MLAG_MROUTE_ADD: { + ZebraMlagMrouteAdd *msg = NULL; + + msg = zebra_mlag_mroute_add__unpack(NULL, hdr->data.len, + hdr->data.data); + if (msg == NULL) { + zebra_mlag__header__free_unpacked(hdr, NULL); + return -1; + } + /* Payload len */ + stream_putw(s, sizeof(struct mlag_mroute_add)); + /* No Batching */ + stream_putw(s, MLAG_MSG_NO_BATCH); + /* Actual Data */ + zebra_fill_protobuf_msg(s, msg->vrf_name, VRF_NAMSIZ); + + stream_putl(s, msg->source_ip); + stream_putl(s, msg->group_ip); + stream_putl(s, msg->cost_to_rp); + stream_putl(s, msg->owner_id); + stream_putc(s, msg->am_i_dr); + stream_putc(s, msg->am_i_dual_active); + stream_putl(s, msg->vrf_id); + if (msg->owner_id == MLAG_OWNER_INTERFACE) + zebra_fill_protobuf_msg(s, msg->intf_name, + INTERFACE_NAMSIZ); + else + stream_put(s, NULL, INTERFACE_NAMSIZ); + zebra_mlag_mroute_add__free_unpacked(msg, NULL); + } break; + case ZEBRA_MLAG__HEADER__MESSAGE_TYPE__ZEBRA_MLAG_MROUTE_DEL: { + ZebraMlagMrouteDel *msg = NULL; + + msg = zebra_mlag_mroute_del__unpack(NULL, hdr->data.len, + hdr->data.data); + if (msg == NULL) { + zebra_mlag__header__free_unpacked(hdr, NULL); + return -1; + } + /* Payload len */ + stream_putw(s, sizeof(struct mlag_mroute_del)); + /* No Batching */ + stream_putw(s, MLAG_MSG_NO_BATCH); + /* Actual Data */ + zebra_fill_protobuf_msg(s, msg->vrf_name, VRF_NAMSIZ); + + stream_putl(s, msg->source_ip); + stream_putl(s, msg->group_ip); + stream_putl(s, msg->owner_id); + stream_putl(s, msg->vrf_id); + if (msg->owner_id == MLAG_OWNER_INTERFACE) + zebra_fill_protobuf_msg(s, msg->intf_name, + INTERFACE_NAMSIZ); + else + stream_put(s, NULL, INTERFACE_NAMSIZ); + zebra_mlag_mroute_del__free_unpacked(msg, NULL); + } break; + case ZEBRA_MLAG__HEADER__MESSAGE_TYPE__ZEBRA_MLAG_MROUTE_ADD_BULK: { + ZebraMlagMrouteAddBulk *Bulk_msg = NULL; + ZebraMlagMrouteAdd *msg = NULL; + size_t i; + + Bulk_msg = zebra_mlag_mroute_add_bulk__unpack( + NULL, hdr->data.len, hdr->data.data); + if (Bulk_msg == NULL) { + zebra_mlag__header__free_unpacked(hdr, NULL); + return -1; + } + /* Payload len */ + stream_putw(s, (Bulk_msg->n_mroute_add + * sizeof(struct mlag_mroute_add))); + /* No. of msgs in Batch */ + stream_putw(s, Bulk_msg->n_mroute_add); + + /* Actual Data */ + for (i = 0; i < Bulk_msg->n_mroute_add; i++) { + + msg = Bulk_msg->mroute_add[i]; + + zebra_fill_protobuf_msg(s, msg->vrf_name, + VRF_NAMSIZ); + stream_putl(s, msg->source_ip); + stream_putl(s, msg->group_ip); + stream_putl(s, msg->cost_to_rp); + stream_putl(s, msg->owner_id); + stream_putc(s, msg->am_i_dr); + stream_putc(s, msg->am_i_dual_active); + stream_putl(s, msg->vrf_id); + if (msg->owner_id == MLAG_OWNER_INTERFACE) + zebra_fill_protobuf_msg( + s, msg->intf_name, + INTERFACE_NAMSIZ); + else + stream_put(s, NULL, INTERFACE_NAMSIZ); + } + zebra_mlag_mroute_add_bulk__free_unpacked(Bulk_msg, + NULL); + } break; + case ZEBRA_MLAG__HEADER__MESSAGE_TYPE__ZEBRA_MLAG_MROUTE_DEL_BULK: { + ZebraMlagMrouteDelBulk *Bulk_msg = NULL; + ZebraMlagMrouteDel *msg = NULL; + size_t i; + + Bulk_msg = zebra_mlag_mroute_del_bulk__unpack( + NULL, hdr->data.len, hdr->data.data); + if (Bulk_msg == NULL) { + zebra_mlag__header__free_unpacked(hdr, NULL); + return -1; + } + /* Payload len */ + stream_putw(s, (Bulk_msg->n_mroute_del + * sizeof(struct mlag_mroute_del))); + /* No. of msgs in Batch */ + stream_putw(s, Bulk_msg->n_mroute_del); + + /* Actual Data */ + for (i = 0; i < Bulk_msg->n_mroute_del; i++) { + + msg = Bulk_msg->mroute_del[i]; + + zebra_fill_protobuf_msg(s, msg->vrf_name, + VRF_NAMSIZ); + stream_putl(s, msg->source_ip); + stream_putl(s, msg->group_ip); + stream_putl(s, msg->owner_id); + stream_putl(s, msg->vrf_id); + if (msg->owner_id == MLAG_OWNER_INTERFACE) + zebra_fill_protobuf_msg( + s, msg->intf_name, + INTERFACE_NAMSIZ); + else + stream_put(s, NULL, INTERFACE_NAMSIZ); + } + zebra_mlag_mroute_del_bulk__free_unpacked(Bulk_msg, + NULL); + } break; + case ZEBRA_MLAG__HEADER__MESSAGE_TYPE__ZEBRA_MLAG_ZEBRA_STATUS_UPDATE: { + ZebraMlagZebraStatusUpdate *msg = NULL; + + msg = zebra_mlag_zebra_status_update__unpack( + NULL, hdr->data.len, hdr->data.data); + if (msg == NULL) { + zebra_mlag__header__free_unpacked(hdr, NULL); + return -1; + } + /* Payload len */ + stream_putw(s, sizeof(struct mlag_frr_status)); + /* No Batching */ + stream_putw(s, MLAG_MSG_NO_BATCH); + /* Actual Data */ + stream_putl(s, msg->peer_frrstate); + zebra_mlag_zebra_status_update__free_unpacked(msg, + NULL); + } break; + default: + break; + } + } + zebra_mlag__header__free_unpacked(hdr, NULL); + return msg_type; +} + +#else +int zebra_mlag_protobuf_encode_client_data(struct stream *s, uint32_t *msg_type) +{ + return 0; +} + +int zebra_mlag_protobuf_decode_message(struct stream *s, uint8_t *data, + uint32_t len) +{ + return 0; +} +#endif diff --git a/zebra/zebra_mlag.h b/zebra/zebra_mlag.h index 90a5a41fa4..85028d2774 100644 --- a/zebra/zebra_mlag.h +++ b/zebra/zebra_mlag.h @@ -23,16 +23,56 @@ #define __ZEBRA_MLAG_H__ #include "mlag.h" +#include "zclient.h" +#include "zebra/zserv.h" #ifdef __cplusplus extern "C" { #endif +#define ZEBRA_MLAG_BUF_LIMIT 32768 +#define ZEBRA_MLAG_LEN_SIZE 4 + +DECLARE_HOOK(zebra_mlag_private_write_data, + (uint8_t *data, uint32_t len), (data, len)) +DECLARE_HOOK(zebra_mlag_private_monitor_state, (), ()) +DECLARE_HOOK(zebra_mlag_private_open_channel, (), ()) +DECLARE_HOOK(zebra_mlag_private_close_channel, (), ()) +DECLARE_HOOK(zebra_mlag_private_cleanup_data, (), ()) + +extern uint8_t mlag_wr_buffer[ZEBRA_MLAG_BUF_LIMIT]; +extern uint8_t mlag_rd_buffer[ZEBRA_MLAG_BUF_LIMIT]; +extern uint32_t mlag_rd_buf_offset; + +static inline void zebra_mlag_reset_read_buffer(void) +{ + memset(mlag_wr_buffer, 0, ZEBRA_MLAG_BUF_LIMIT); + mlag_rd_buf_offset = 0; +} + +enum zebra_mlag_state { + MLAG_UP = 1, + MLAG_DOWN = 2, +}; + void zebra_mlag_init(void); void zebra_mlag_terminate(void); - enum mlag_role zebra_mlag_get_role(void); +void zebra_mlag_client_register(ZAPI_HANDLER_ARGS); +void zebra_mlag_client_unregister(ZAPI_HANDLER_ARGS); +void zebra_mlag_forward_client_msg(ZAPI_HANDLER_ARGS); +void zebra_mlag_send_register(void); +void zebra_mlag_send_deregister(void); +void zebra_mlag_handle_process_state(enum zebra_mlag_state state); +void zebra_mlag_process_mlag_data(uint8_t *data, uint32_t len); +/* + * ProtoBuffer Api's + */ +int zebra_mlag_protobuf_encode_client_data(struct stream *s, + uint32_t *msg_type); +int zebra_mlag_protobuf_decode_message(struct stream *s, uint8_t *data, + uint32_t len); #ifdef __cplusplus } #endif diff --git a/zebra/zebra_mlag_private.c b/zebra/zebra_mlag_private.c new file mode 100644 index 0000000000..d39b0c0aee --- /dev/null +++ b/zebra/zebra_mlag_private.c @@ -0,0 +1,298 @@ +/* + * This is an implementation of MLAG Functionality + * + * Module name: Zebra MLAG + * + * Author: sathesh Kumar karra + * + * Copyright (C) 2019 Cumulus Networks http://www.cumulusnetworks.com + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include "zebra.h" + +#include "hook.h" +#include "module.h" +#include "thread.h" +#include "frr_pthread.h" +#include "libfrr.h" +#include "version.h" +#include "network.h" + +#include "lib/stream.h" + +#include "zebra/debug.h" +#include "zebra/zebra_router.h" +#include "zebra/zebra_mlag.h" + +#include + + +/* + * This file will have platform specific apis to communicate with MCLAG. + * + */ + +static struct thread_master *zmlag_master; +static int mlag_socket; + +static int zebra_mlag_connect(struct thread *thread); +static int zebra_mlag_read(struct thread *thread); + +/* + * Write the data to MLAGD + */ +static int zebra_mlag_private_write_data(uint8_t *data, uint32_t len) +{ + int rc = 0; + + if (IS_ZEBRA_DEBUG_MLAG) { + zlog_debug("%s: Writing %d length Data to clag", __func__, len); + zlog_hexdump(data, len); + } + rc = write(mlag_socket, data, len); + return rc; +} + +static void zebra_mlag_sched_read(void) +{ + thread_add_read(zmlag_master, zebra_mlag_read, NULL, mlag_socket, + &zrouter.mlag_info.t_read); +} + +static int zebra_mlag_read(struct thread *thread) +{ + uint32_t *msglen; + uint32_t h_msglen; + uint32_t tot_len, curr_len = mlag_rd_buf_offset; + + /* + * Received message in sock_stream looks like below + * | len-1 (4 Bytes) | payload-1 (len-1) | + * len-2 (4 Bytes) | payload-2 (len-2) | .. + * + * Idea is read one message completely, then process, until message is + * read completely, keep on reading from the socket + */ + if (curr_len < ZEBRA_MLAG_LEN_SIZE) { + ssize_t data_len; + + data_len = read(mlag_socket, mlag_rd_buffer + curr_len, + ZEBRA_MLAG_LEN_SIZE - curr_len); + if (data_len == 0 || data_len == -1) { + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug("MLAG connection closed socket : %d", + mlag_socket); + close(mlag_socket); + zebra_mlag_handle_process_state(MLAG_DOWN); + return -1; + } + mlag_rd_buf_offset += data_len; + if (data_len != (ssize_t)(ZEBRA_MLAG_LEN_SIZE - curr_len)) { + /* Try again later */ + zebra_mlag_sched_read(); + return 0; + } + curr_len = ZEBRA_MLAG_LEN_SIZE; + } + + /* Get the actual packet length */ + msglen = (uint32_t *)mlag_rd_buffer; + h_msglen = ntohl(*msglen); + + /* This will be the actual length of the packet */ + tot_len = h_msglen + ZEBRA_MLAG_LEN_SIZE; + + /* + * If the buffer read we are about to do is too large + * we are really really really not double plus good + * + * I'm not sure what to do here other than to bail + * We'll need to revisit this in the future. + */ + assert(tot_len < ZEBRA_MLAG_BUF_LIMIT); + + if (curr_len < tot_len) { + ssize_t data_len; + + data_len = read(mlag_socket, mlag_rd_buffer + curr_len, + tot_len - curr_len); + if (data_len == 0 || data_len == -1) { + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug("MLAG connection closed socket : %d", + mlag_socket); + close(mlag_socket); + zebra_mlag_handle_process_state(MLAG_DOWN); + return -1; + } + mlag_rd_buf_offset += data_len; + if (data_len != (ssize_t)(tot_len - curr_len)) { + /* Try again later */ + zebra_mlag_sched_read(); + return 0; + } + } + + if (IS_ZEBRA_DEBUG_MLAG) { + zlog_debug("Received a MLAG Message from socket: %d, len:%u ", + mlag_socket, tot_len); + zlog_hexdump(mlag_rd_buffer, tot_len); + } + + tot_len -= ZEBRA_MLAG_LEN_SIZE; + + /* Process the packet */ + zebra_mlag_process_mlag_data(mlag_rd_buffer + ZEBRA_MLAG_LEN_SIZE, + tot_len); + + /* Register read thread. */ + zebra_mlag_reset_read_buffer(); + zebra_mlag_sched_read(); + return 0; +} + +static int zebra_mlag_connect(struct thread *thread) +{ + struct sockaddr_un svr = {0}; + + /* Reset the Timer-running flag */ + zrouter.mlag_info.timer_running = false; + + svr.sun_family = AF_UNIX; +#define MLAG_SOCK_NAME "/var/run/clag-zebra.socket" + strlcpy(svr.sun_path, MLAG_SOCK_NAME, sizeof(MLAG_SOCK_NAME) + 1); + + mlag_socket = socket(svr.sun_family, SOCK_STREAM, 0); + if (mlag_socket < 0) + return -1; + + if (connect(mlag_socket, (struct sockaddr *)&svr, sizeof(svr)) == -1) { + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug( + "Unable to connect to %s try again in 10 secs", + svr.sun_path); + close(mlag_socket); + zrouter.mlag_info.timer_running = true; + thread_add_timer(zmlag_master, zebra_mlag_connect, NULL, 10, + &zrouter.mlag_info.t_read); + return 0; + } + + set_nonblocking(mlag_socket); + + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug("%s: Connection with MLAG is established ", + __func__); + + thread_add_read(zmlag_master, zebra_mlag_read, NULL, mlag_socket, + &zrouter.mlag_info.t_read); + /* + * Connection is established with MLAGD, post to clients + */ + zebra_mlag_handle_process_state(MLAG_UP); + return 0; +} + +/* + * Currently we are doing polling later we will look for better options + */ +static int zebra_mlag_private_monitor_state(void) +{ + thread_add_event(zmlag_master, zebra_mlag_connect, NULL, 0, + &zrouter.mlag_info.t_read); + return 0; +} + +static int zebra_mlag_private_open_channel(void) +{ + zmlag_master = zrouter.mlag_info.th_master; + + if (zrouter.mlag_info.connected == true) { + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug("%s: Zebra already connected to MLAGD", + __func__); + return 0; + } + + if (zrouter.mlag_info.timer_running == true) { + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug( + "%s: Connection retry is in progress for MLAGD", + __func__); + return 0; + } + + if (zrouter.mlag_info.clients_interested_cnt) { + /* + * Connect only if any clients are showing interest + */ + thread_add_event(zmlag_master, zebra_mlag_connect, NULL, 0, + &zrouter.mlag_info.t_read); + } + return 0; +} + +static int zebra_mlag_private_close_channel(void) +{ + if (zmlag_master == NULL) + return -1; + + if (zrouter.mlag_info.clients_interested_cnt) { + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug("%s: still %d clients are connected, skip", + __func__, + zrouter.mlag_info.clients_interested_cnt); + return -1; + } + + /* + * Post the De-register to MLAG, so that it can do necesasry cleanup + */ + zebra_mlag_send_deregister(); + + return 0; +} + +static int zebra_mlag_private_cleanup_data(void) +{ + zmlag_master = NULL; + zrouter.mlag_info.connected = false; + zrouter.mlag_info.timer_running = false; + + close(mlag_socket); + return 0; +} + +static int zebra_mlag_module_init(void) +{ + hook_register(zebra_mlag_private_write_data, + zebra_mlag_private_write_data); + hook_register(zebra_mlag_private_monitor_state, + zebra_mlag_private_monitor_state); + hook_register(zebra_mlag_private_open_channel, + zebra_mlag_private_open_channel); + hook_register(zebra_mlag_private_close_channel, + zebra_mlag_private_close_channel); + hook_register(zebra_mlag_private_cleanup_data, + zebra_mlag_private_cleanup_data); + return 0; +} + +FRR_MODULE_SETUP( + .name = "zebra_cumulus_mlag", + .version = FRR_VERSION, + .description = "zebra Cumulus MLAG interface", + .init = zebra_mlag_module_init, +) diff --git a/zebra/zebra_mlag_vty.c b/zebra/zebra_mlag_vty.c new file mode 100644 index 0000000000..ebaaf03dab --- /dev/null +++ b/zebra/zebra_mlag_vty.c @@ -0,0 +1,67 @@ +/* Zebra Mlag vty Code. + * Copyright (C) 2019 Cumulus Networks, Inc. + * Donald Sharp + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ +#include + +#include "vty.h" +#include "command.h" + +#include "zebra_router.h" +#include "zebra_mlag_vty.h" +#include "debug.h" +#include "zapi_msg.h" + +#ifndef VTYSH_EXTRACT_PL +#include "zebra/zebra_mlag_vty_clippy.c" +#endif + +DEFUN_HIDDEN (show_mlag, + show_mlag_cmd, + "show zebra mlag", + SHOW_STR + ZEBRA_STR + "The mlag role on this machine\n") +{ + char buf[MLAG_ROLE_STRSIZE]; + + vty_out(vty, "MLag is configured to: %s\n", + mlag_role2str(zrouter.mlag_info.role, buf, sizeof(buf))); + + return CMD_SUCCESS; +} + +DEFPY_HIDDEN(test_mlag, test_mlag_cmd, + "test zebra mlag ", + "Test code\n" + ZEBRA_STR + "Modify the Mlag state\n" + "Mlag is not setup on the machine\n" + "Mlag is setup to be primary\n" + "Mlag is setup to be the secondary\n") +{ + return zebra_mlag_test_mlag_internal(none, primary, secondary); +} + +void zebra_mlag_vty_init(void) +{ + install_element(VIEW_NODE, &show_mlag_cmd); + install_element(ENABLE_NODE, &test_mlag_cmd); +} diff --git a/babeld/babel_memory.h b/zebra/zebra_mlag_vty.h similarity index 64% rename from babeld/babel_memory.h rename to zebra/zebra_mlag_vty.h index 4283498891..789154d684 100644 --- a/babeld/babel_memory.h +++ b/zebra/zebra_mlag_vty.h @@ -1,6 +1,6 @@ -/* babel memory type declarations - * - * Copyright (C) 2017 Donald Sharp +/* Zebra Mlag vty Code. + * Copyright (C) 2019 Cumulus Networks, Inc. + * Donald Sharp * * This file is part of FRR. * @@ -19,14 +19,21 @@ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ +#ifndef __ZEBRA_MLAG_VTY_CODE__ +#define __ZEBRA_MLAG_VTY_CODE__ + +#ifdef __cplusplus +extern "C" { +#endif -#ifndef _FRR_BABEL_MEMORY_H -#define _FRR_BABEL_MEMORY_H +extern int32_t zebra_mlag_test_mlag_internal(const char *none, + const char *primary, + const char *secondary); -#include "memory.h" +extern void zebra_mlag_vty_init(void); -DECLARE_MGROUP(BABELD) -DECLARE_MTYPE(BABEL) -DECLARE_MTYPE(BABEL_IF) +#ifdef __cplusplus +} +#endif -#endif /* _FRR_BABELD_MEMORY_H */ +#endif diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c index 5c375a6bef..03b8c8de1f 100644 --- a/zebra/zebra_mpls.c +++ b/zebra/zebra_mpls.c @@ -34,6 +34,7 @@ #include "routemap.h" #include "stream.h" #include "nexthop.h" +#include "termtable.h" #include "lib/json.h" #include "zebra/rib.h" @@ -46,6 +47,7 @@ #include "zebra/zebra_memory.h" #include "zebra/zebra_vrf.h" #include "zebra/zebra_mpls.h" +#include "zebra/zebra_srte.h" #include "zebra/zebra_errors.h" DEFINE_MTYPE_STATIC(ZEBRA, LSP, "MPLS LSP object") @@ -76,7 +78,7 @@ static zebra_fec_t *fec_add(struct route_table *table, struct prefix *p, uint32_t label_index); static int fec_del(zebra_fec_t *fec); -static unsigned int label_hash(void *p); +static unsigned int label_hash(const void *p); static bool label_cmp(const void *p1, const void *p2); static int nhlfe_nexthop_active_ipv4(zebra_nhlfe_t *nhlfe, struct nexthop *nexthop); @@ -93,38 +95,58 @@ static void lsp_processq_complete(struct work_queue *wq); static int lsp_processq_add(zebra_lsp_t *lsp); static void *lsp_alloc(void *p); +/* Check whether lsp can be freed - no nhlfes, e.g., and call free api */ +static void lsp_check_free(struct hash *lsp_table, zebra_lsp_t **plsp); + +/* Free lsp; sets caller's pointer to NULL */ +static void lsp_free(struct hash *lsp_table, zebra_lsp_t **plsp); + static char *nhlfe2str(zebra_nhlfe_t *nhlfe, char *buf, int size); static int nhlfe_nhop_match(zebra_nhlfe_t *nhlfe, enum nexthop_types_t gtype, - union g_addr *gate, ifindex_t ifindex); -static zebra_nhlfe_t *nhlfe_find(zebra_lsp_t *lsp, enum lsp_types_t lsp_type, - enum nexthop_types_t gtype, union g_addr *gate, - ifindex_t ifindex); + const union g_addr *gate, ifindex_t ifindex); +static zebra_nhlfe_t *nhlfe_find(struct nhlfe_list_head *list, + enum lsp_types_t lsp_type, + enum nexthop_types_t gtype, + const union g_addr *gate, ifindex_t ifindex); static zebra_nhlfe_t *nhlfe_add(zebra_lsp_t *lsp, enum lsp_types_t lsp_type, - enum nexthop_types_t gtype, union g_addr *gate, - ifindex_t ifindex, mpls_label_t out_label); -static int nhlfe_del(zebra_nhlfe_t *snhlfe); + enum nexthop_types_t gtype, + const union g_addr *gate, ifindex_t ifindex, + uint8_t num_labels, const mpls_label_t *labels, + bool is_backup); +static int nhlfe_del(zebra_nhlfe_t *nhlfe); +static void nhlfe_free(zebra_nhlfe_t *nhlfe); static void nhlfe_out_label_update(zebra_nhlfe_t *nhlfe, struct mpls_label_stack *nh_label); static int mpls_lsp_uninstall_all(struct hash *lsp_table, zebra_lsp_t *lsp, enum lsp_types_t type); static int mpls_static_lsp_uninstall_all(struct zebra_vrf *zvrf, mpls_label_t in_label); -static void nhlfe_print(zebra_nhlfe_t *nhlfe, struct vty *vty); -static void lsp_print(zebra_lsp_t *lsp, void *ctxt); +static void nhlfe_print(zebra_nhlfe_t *nhlfe, struct vty *vty, + const char *indent); +static void lsp_print(struct vty *vty, zebra_lsp_t *lsp); static void *slsp_alloc(void *p); static int snhlfe_match(zebra_snhlfe_t *snhlfe, enum nexthop_types_t gtype, - union g_addr *gate, ifindex_t ifindex); + const union g_addr *gate, ifindex_t ifindex); static zebra_snhlfe_t *snhlfe_find(zebra_slsp_t *slsp, enum nexthop_types_t gtype, - union g_addr *gate, ifindex_t ifindex); + const union g_addr *gate, ifindex_t ifindex); static zebra_snhlfe_t *snhlfe_add(zebra_slsp_t *slsp, enum nexthop_types_t gtype, - union g_addr *gate, ifindex_t ifindex, + const union g_addr *gate, ifindex_t ifindex, mpls_label_t out_label); static int snhlfe_del(zebra_snhlfe_t *snhlfe); static int snhlfe_del_all(zebra_slsp_t *slsp); static char *snhlfe2str(zebra_snhlfe_t *snhlfe, char *buf, int size); +static void mpls_lsp_uninstall_all_type(struct hash_bucket *bucket, void *ctxt); +static void mpls_ftn_uninstall_all(struct zebra_vrf *zvrf, + int afi, enum lsp_types_t lsp_type); +static int lsp_znh_install(zebra_lsp_t *lsp, enum lsp_types_t type, + const struct zapi_nexthop *znh); +static int lsp_backup_znh_install(zebra_lsp_t *lsp, enum lsp_types_t type, + const struct zapi_nexthop *znh); +/* List implementations - declare internal linkage */ +DECLARE_DLIST(snhlfe_list, struct zebra_snhlfe_t_, list); /* Static functions */ @@ -136,7 +158,16 @@ static void clear_nhlfe_installed(zebra_lsp_t *lsp) zebra_nhlfe_t *nhlfe; struct nexthop *nexthop; - for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next) { + frr_each_safe(nhlfe_list, &lsp->nhlfe_list, nhlfe) { + nexthop = nhlfe->nexthop; + if (!nexthop) + continue; + + UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED); + UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); + } + + frr_each_safe(nhlfe_list, &lsp->backup_nhlfe_list, nhlfe) { nexthop = nhlfe->nexthop; if (!nexthop) continue; @@ -181,14 +212,16 @@ static int lsp_install(struct zebra_vrf *zvrf, mpls_label_t label, * the label advertised by the recursive nexthop (plus we don't have the * logic yet to push multiple labels). */ - for (nexthop = re->ng.nexthop; nexthop; nexthop = nexthop->next) { + for (nexthop = re->nhe->nhg.nexthop; + nexthop; nexthop = nexthop->next) { /* Skip inactive and recursive entries. */ if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) continue; if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) continue; - nhlfe = nhlfe_find(lsp, lsp_type, nexthop->type, &nexthop->gate, + nhlfe = nhlfe_find(&lsp->nhlfe_list, lsp_type, + nexthop->type, &nexthop->gate, nexthop->ifindex); if (nhlfe) { /* Clear deleted flag (in case it was set) */ @@ -201,8 +234,7 @@ static int lsp_install(struct zebra_vrf *zvrf, mpls_label_t label, if (IS_ZEBRA_DEBUG_MPLS) { nhlfe2str(nhlfe, buf, BUFSIZ); zlog_debug( - "LSP in-label %u type %d nexthop %s " - "out-label changed", + "LSP in-label %u type %d nexthop %s out-label changed", lsp->ile.in_label, lsp_type, buf); } @@ -214,15 +246,16 @@ static int lsp_install(struct zebra_vrf *zvrf, mpls_label_t label, /* Add LSP entry to this nexthop */ nhlfe = nhlfe_add(lsp, lsp_type, nexthop->type, &nexthop->gate, nexthop->ifindex, - nexthop->nh_label->label[0]); + nexthop->nh_label->num_labels, + nexthop->nh_label->label, + false /*backup*/); if (!nhlfe) return -1; if (IS_ZEBRA_DEBUG_MPLS) { nhlfe2str(nhlfe, buf, BUFSIZ); zlog_debug( - "Add LSP in-label %u type %d nexthop %s " - "out-label %u", + "Add LSP in-label %u type %d nexthop %s out-label %u", lsp->ile.in_label, lsp_type, buf, nexthop->nh_label->label[0]); } @@ -242,14 +275,8 @@ static int lsp_install(struct zebra_vrf *zvrf, mpls_label_t label, if (added || changed) { if (lsp_processq_add(lsp)) return -1; - } else if (!lsp->nhlfe_list - && !CHECK_FLAG(lsp->flags, LSP_FLAG_SCHEDULED)) { - if (IS_ZEBRA_DEBUG_MPLS) - zlog_debug("Free LSP in-label %u flags 0x%x", - lsp->ile.in_label, lsp->flags); - - lsp = hash_release(lsp_table, &lsp->ile); - XFREE(MTYPE_LSP, lsp); + } else { + lsp_check_free(lsp_table, &lsp); } return 0; @@ -264,7 +291,7 @@ static int lsp_uninstall(struct zebra_vrf *zvrf, mpls_label_t label) struct hash *lsp_table; zebra_ile_t tmp_ile; zebra_lsp_t *lsp; - zebra_nhlfe_t *nhlfe, *nhlfe_next; + zebra_nhlfe_t *nhlfe; char buf[BUFSIZ]; /* Lookup table. */ @@ -275,12 +302,11 @@ static int lsp_uninstall(struct zebra_vrf *zvrf, mpls_label_t label) /* If entry is not present, exit. */ tmp_ile.in_label = label; lsp = hash_lookup(lsp_table, &tmp_ile); - if (!lsp || !lsp->nhlfe_list) + if (!lsp || (nhlfe_list_first(&lsp->nhlfe_list) == NULL)) return 0; /* Mark NHLFEs for delete or directly delete, as appropriate. */ - for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe_next) { - nhlfe_next = nhlfe->next; + frr_each_safe(nhlfe_list, &lsp->nhlfe_list, nhlfe) { /* Skip static NHLFEs */ if (nhlfe->type == ZEBRA_LSP_STATIC) @@ -305,14 +331,8 @@ static int lsp_uninstall(struct zebra_vrf *zvrf, mpls_label_t label) if (CHECK_FLAG(lsp->flags, LSP_FLAG_INSTALLED)) { if (lsp_processq_add(lsp)) return -1; - } else if (!lsp->nhlfe_list - && !CHECK_FLAG(lsp->flags, LSP_FLAG_SCHEDULED)) { - if (IS_ZEBRA_DEBUG_MPLS) - zlog_debug("Del LSP in-label %u flags 0x%x", - lsp->ile.in_label, lsp->flags); - - lsp = hash_release(lsp_table, &lsp->ile); - XFREE(MTYPE_LSP, lsp); + } else { + lsp_check_free(lsp_table, &lsp); } return 0; @@ -577,7 +597,7 @@ static int fec_del(zebra_fec_t *fec) /* * Hash function for label. */ -static unsigned int label_hash(void *p) +static unsigned int label_hash(const void *p) { const zebra_ile_t *ile = p; @@ -631,7 +651,7 @@ static int nhlfe_nexthop_active_ipv4(zebra_nhlfe_t *nhlfe, || !CHECK_FLAG(match->flags, ZEBRA_FLAG_SELECTED)) continue; - for (match_nh = match->ng.nexthop; match_nh; + for (match_nh = match->nhe->nhg.nexthop; match_nh; match_nh = match_nh->next) { if (match->type == ZEBRA_ROUTE_CONNECT || nexthop->ifindex == match_nh->ifindex) { @@ -682,10 +702,10 @@ static int nhlfe_nexthop_active_ipv6(zebra_nhlfe_t *nhlfe, break; } - if (!match || !match->ng.nexthop) + if (!match || !match->nhe->nhg.nexthop) return 0; - nexthop->ifindex = match->ng.nexthop->ifindex; + nexthop->ifindex = match->nhe->nhg.nexthop->ifindex; return 1; } @@ -784,10 +804,9 @@ static void lsp_select_best_nhlfe(zebra_lsp_t *lsp) /* * First compute the best path, after checking nexthop status. We are - * only - * concerned with non-deleted NHLFEs. + * only concerned with non-deleted NHLFEs. */ - for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next) { + frr_each_safe(nhlfe_list, &lsp->nhlfe_list, nhlfe) { /* Clear selection flags. */ UNSET_FLAG(nhlfe->flags, (NHLFE_FLAG_SELECTED | NHLFE_FLAG_MULTIPATH)); @@ -803,6 +822,14 @@ static void lsp_select_best_nhlfe(zebra_lsp_t *lsp) if (!lsp->best_nhlfe) return; + /* + * Check the active status of backup nhlfes also + */ + frr_each_safe(nhlfe_list, &lsp->backup_nhlfe_list, nhlfe) { + if (!CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_DELETED)) + (void)nhlfe_nexthop_active(nhlfe); + } + /* Mark best NHLFE as selected. */ SET_FLAG(lsp->best_nhlfe->flags, NHLFE_FLAG_SELECTED); @@ -812,7 +839,7 @@ static void lsp_select_best_nhlfe(zebra_lsp_t *lsp) * new (uninstalled) NHLFE has been selected, an installed entry that is * still selected has a change or an installed entry is to be removed. */ - for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next) { + frr_each(nhlfe_list, &lsp->nhlfe_list, nhlfe) { int nh_chg, nh_sel, nh_inst; nexthop = nhlfe->nexthop; @@ -897,12 +924,11 @@ static wq_item_status lsp_process(struct work_queue *wq, void *data) if (IS_ZEBRA_DEBUG_MPLS) { if (oldbest) - nhlfe2str(oldbest, buf, BUFSIZ); + nhlfe2str(oldbest, buf, sizeof(buf)); if (newbest) - nhlfe2str(newbest, buf2, BUFSIZ); + nhlfe2str(newbest, buf2, sizeof(buf2)); zlog_debug( - "Process LSP in-label %u oldbest %s newbest %s " - "flags 0x%x ecmp# %d", + "Process LSP in-label %u oldbest %s newbest %s flags 0x%x ecmp# %d", lsp->ile.in_label, oldbest ? buf : "NULL", newbest ? buf2 : "NULL", lsp->flags, lsp->num_ecmp); } @@ -963,15 +989,14 @@ static wq_item_status lsp_process(struct work_queue *wq, void *data) UNSET_FLAG(lsp->flags, LSP_FLAG_CHANGED); /* We leave the INSTALLED flag set here - * so we know an update in in-flight. + * so we know an update is in-flight. */ /* * Any NHLFE that was installed but is not * selected now needs to have its flags updated. */ - for (nhlfe = lsp->nhlfe_list; nhlfe; - nhlfe = nhlfe->next) { + frr_each_safe(nhlfe_list, &lsp->nhlfe_list, nhlfe) { nexthop = nhlfe->nexthop; if (!nexthop) continue; @@ -1015,7 +1040,7 @@ static void lsp_processq_del(struct work_queue *wq, void *data) struct zebra_vrf *zvrf; zebra_lsp_t *lsp; struct hash *lsp_table; - zebra_nhlfe_t *nhlfe, *nhlfe_next; + zebra_nhlfe_t *nhlfe; zvrf = vrf_info_lookup(VRF_DEFAULT); assert(zvrf); @@ -1034,20 +1059,17 @@ static void lsp_processq_del(struct work_queue *wq, void *data) */ UNSET_FLAG(lsp->flags, LSP_FLAG_SCHEDULED); - for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe_next) { - nhlfe_next = nhlfe->next; + frr_each_safe(nhlfe_list, &lsp->nhlfe_list, nhlfe) { if (CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_DELETED)) nhlfe_del(nhlfe); } - if (!lsp->nhlfe_list) { - if (IS_ZEBRA_DEBUG_MPLS) - zlog_debug("Free LSP in-label %u flags 0x%x", - lsp->ile.in_label, lsp->flags); - - lsp = hash_release(lsp_table, &lsp->ile); - XFREE(MTYPE_LSP, lsp); + frr_each_safe(nhlfe_list, &lsp->backup_nhlfe_list, nhlfe) { + if (CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_DELETED)) + nhlfe_del(nhlfe); } + + lsp_check_free(lsp_table, &lsp); } /* @@ -1089,6 +1111,8 @@ static void *lsp_alloc(void *p) lsp = XCALLOC(MTYPE_LSP, sizeof(zebra_lsp_t)); lsp->ile = *ile; + nhlfe_list_init(&lsp->nhlfe_list); + nhlfe_list_init(&lsp->backup_nhlfe_list); if (IS_ZEBRA_DEBUG_MPLS) zlog_debug("Alloc LSP in-label %u", lsp->ile.in_label); @@ -1096,6 +1120,56 @@ static void *lsp_alloc(void *p) return ((void *)lsp); } +/* + * Check whether lsp can be freed - no nhlfes, e.g., and call free api + */ +static void lsp_check_free(struct hash *lsp_table, zebra_lsp_t **plsp) +{ + zebra_lsp_t *lsp; + + if (plsp == NULL || *plsp == NULL) + return; + + lsp = *plsp; + + if ((nhlfe_list_first(&lsp->nhlfe_list) == NULL) && + (nhlfe_list_first(&lsp->backup_nhlfe_list) == NULL) && + !CHECK_FLAG(lsp->flags, LSP_FLAG_SCHEDULED)) + lsp_free(lsp_table, plsp); +} + +/* + * Dtor for an LSP: remove from ile hash, release any internal allocations, + * free LSP object. + */ +static void lsp_free(struct hash *lsp_table, zebra_lsp_t **plsp) +{ + zebra_lsp_t *lsp; + zebra_nhlfe_t *nhlfe; + + if (plsp == NULL || *plsp == NULL) + return; + + lsp = *plsp; + + if (IS_ZEBRA_DEBUG_MPLS) + zlog_debug("Free LSP in-label %u flags 0x%x", + lsp->ile.in_label, lsp->flags); + + /* Free nhlfes, if any. */ + frr_each_safe(nhlfe_list, &lsp->nhlfe_list, nhlfe) + nhlfe_del(nhlfe); + + /* Free backup nhlfes, if any. */ + frr_each_safe(nhlfe_list, &lsp->backup_nhlfe_list, nhlfe) + nhlfe_del(nhlfe); + + hash_release(lsp_table, &lsp->ile); + XFREE(MTYPE_LSP, lsp); + + *plsp = NULL; +} + /* * Create printable string for NHLFE entry. */ @@ -1111,6 +1185,7 @@ static char *nhlfe2str(zebra_nhlfe_t *nhlfe, char *buf, int size) inet_ntop(AF_INET, &nexthop->gate.ipv4, buf, size); break; case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: inet_ntop(AF_INET6, &nexthop->gate.ipv6, buf, size); break; case NEXTHOP_TYPE_IFINDEX: @@ -1126,7 +1201,7 @@ static char *nhlfe2str(zebra_nhlfe_t *nhlfe, char *buf, int size) * Check if NHLFE matches with search info passed. */ static int nhlfe_nhop_match(zebra_nhlfe_t *nhlfe, enum nexthop_types_t gtype, - union g_addr *gate, ifindex_t ifindex) + const union g_addr *gate, ifindex_t ifindex) { struct nexthop *nhop; int cmp = 1; @@ -1167,16 +1242,14 @@ static int nhlfe_nhop_match(zebra_nhlfe_t *nhlfe, enum nexthop_types_t gtype, /* * Locate NHLFE that matches with passed info. */ -static zebra_nhlfe_t *nhlfe_find(zebra_lsp_t *lsp, enum lsp_types_t lsp_type, - enum nexthop_types_t gtype, union g_addr *gate, - ifindex_t ifindex) +static zebra_nhlfe_t *nhlfe_find(struct nhlfe_list_head *list, + enum lsp_types_t lsp_type, + enum nexthop_types_t gtype, + const union g_addr *gate, ifindex_t ifindex) { zebra_nhlfe_t *nhlfe; - if (!lsp) - return NULL; - - for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next) { + frr_each_safe(nhlfe_list, list, nhlfe) { if (nhlfe->type != lsp_type) continue; if (!nhlfe_nhop_match(nhlfe, gtype, gate, ifindex)) @@ -1187,18 +1260,18 @@ static zebra_nhlfe_t *nhlfe_find(zebra_lsp_t *lsp, enum lsp_types_t lsp_type, } /* - * Add NHLFE. Base entry must have been created and duplicate - * check done. + * Allocate and init new NHLFE. */ -static zebra_nhlfe_t *nhlfe_add(zebra_lsp_t *lsp, enum lsp_types_t lsp_type, - enum nexthop_types_t gtype, union g_addr *gate, - ifindex_t ifindex, mpls_label_t out_label) +static zebra_nhlfe_t *nhlfe_alloc(zebra_lsp_t *lsp, enum lsp_types_t lsp_type, + enum nexthop_types_t gtype, + const union g_addr *gate, ifindex_t ifindex, + uint8_t num_labels, + const mpls_label_t *labels) { zebra_nhlfe_t *nhlfe; struct nexthop *nexthop; - if (!lsp) - return NULL; + assert(lsp); nhlfe = XCALLOC(MTYPE_NHLFE, sizeof(zebra_nhlfe_t)); @@ -1207,11 +1280,8 @@ static zebra_nhlfe_t *nhlfe_add(zebra_lsp_t *lsp, enum lsp_types_t lsp_type, nhlfe->distance = lsp_distance(lsp_type); nexthop = nexthop_new(); - if (!nexthop) { - XFREE(MTYPE_NHLFE, nhlfe); - return NULL; - } - nexthop_add_labels(nexthop, lsp_type, 1, &out_label); + + nexthop_add_labels(nexthop, lsp_type, num_labels, labels); nexthop->vrf_id = VRF_DEFAULT; nexthop->type = gtype; @@ -1235,20 +1305,61 @@ static zebra_nhlfe_t *nhlfe_add(zebra_lsp_t *lsp, enum lsp_types_t lsp_type, nexthop_free(nexthop); XFREE(MTYPE_NHLFE, nhlfe); return NULL; - break; } - nhlfe->nexthop = nexthop; - if (lsp->nhlfe_list) - lsp->nhlfe_list->prev = nhlfe; - nhlfe->next = lsp->nhlfe_list; - lsp->nhlfe_list = nhlfe; return nhlfe; } /* - * Delete NHLFE. Entry must be present on list. + * Add primary or backup NHLFE. Base entry must have been created and + * duplicate check done. + */ +static zebra_nhlfe_t *nhlfe_add(zebra_lsp_t *lsp, enum lsp_types_t lsp_type, + enum nexthop_types_t gtype, + const union g_addr *gate, ifindex_t ifindex, + uint8_t num_labels, const mpls_label_t *labels, + bool is_backup) +{ + zebra_nhlfe_t *nhlfe; + + if (!lsp) + return NULL; + + /* Allocate new object */ + nhlfe = nhlfe_alloc(lsp, lsp_type, gtype, gate, ifindex, num_labels, + labels); + + /* Enqueue to LSP: primaries at head of list, backups at tail */ + if (is_backup) { + SET_FLAG(nhlfe->flags, NHLFE_FLAG_IS_BACKUP); + nhlfe_list_add_tail(&lsp->backup_nhlfe_list, nhlfe); + } else + nhlfe_list_add_head(&lsp->nhlfe_list, nhlfe); + + return nhlfe; +} + +/* + * Common delete for NHLFEs. + */ +static void nhlfe_free(zebra_nhlfe_t *nhlfe) +{ + if (!nhlfe) + return; + + /* Free nexthop. */ + if (nhlfe->nexthop) + nexthop_free(nhlfe->nexthop); + + nhlfe->nexthop = NULL; + + XFREE(MTYPE_NHLFE, nhlfe); +} + + +/* + * Disconnect NHLFE from LSP, and free. Entry must be present on LSP's list. */ static int nhlfe_del(zebra_nhlfe_t *nhlfe) { @@ -1261,22 +1372,18 @@ static int nhlfe_del(zebra_nhlfe_t *nhlfe) if (!lsp) return -1; - /* Free nexthop. */ - if (nhlfe->nexthop) - nexthop_free(nhlfe->nexthop); + if (nhlfe == lsp->best_nhlfe) + lsp->best_nhlfe = NULL; /* Unlink from LSP */ - if (nhlfe->next) - nhlfe->next->prev = nhlfe->prev; - if (nhlfe->prev) - nhlfe->prev->next = nhlfe->next; + if (CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_IS_BACKUP)) + nhlfe_list_del(&lsp->backup_nhlfe_list, nhlfe); else - lsp->nhlfe_list = nhlfe->next; + nhlfe_list_del(&lsp->nhlfe_list, nhlfe); - if (nhlfe == lsp->best_nhlfe) - lsp->best_nhlfe = NULL; + nhlfe->lsp = NULL; - XFREE(MTYPE_NHLFE, nhlfe); + nhlfe_free(nhlfe); return 0; } @@ -1293,14 +1400,15 @@ static void nhlfe_out_label_update(zebra_nhlfe_t *nhlfe, static int mpls_lsp_uninstall_all(struct hash *lsp_table, zebra_lsp_t *lsp, enum lsp_types_t type) { - zebra_nhlfe_t *nhlfe, *nhlfe_next; + zebra_nhlfe_t *nhlfe; int schedule_lsp = 0; char buf[BUFSIZ]; - /* Mark NHLFEs for delete or directly delete, as appropriate. */ - for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe_next) { - nhlfe_next = nhlfe->next; + if (CHECK_FLAG(lsp->flags, LSP_FLAG_INSTALLED)) + schedule_lsp = 1; + /* Mark NHLFEs for delete or directly delete, as appropriate. */ + frr_each_safe(nhlfe_list, &lsp->nhlfe_list, nhlfe) { /* Skip non-static NHLFEs */ if (nhlfe->type != type) continue; @@ -1321,18 +1429,37 @@ static int mpls_lsp_uninstall_all(struct hash *lsp_table, zebra_lsp_t *lsp, } } + frr_each_safe(nhlfe_list, &lsp->backup_nhlfe_list, nhlfe) { + /* Skip non-static NHLFEs */ + if (nhlfe->type != type) + continue; + + if (IS_ZEBRA_DEBUG_MPLS) { + nhlfe2str(nhlfe, buf, BUFSIZ); + zlog_debug( + "Del backup LSP in-label %u type %d nexthop %s flags 0x%x", + lsp->ile.in_label, type, buf, nhlfe->flags); + } + + if (CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED)) { + UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_CHANGED); + SET_FLAG(nhlfe->flags, NHLFE_FLAG_DELETED); + schedule_lsp = 1; + } else { + nhlfe_del(nhlfe); + } + } + /* Queue LSP for processing, if needed, else delete. */ if (schedule_lsp) { + if (IS_ZEBRA_DEBUG_MPLS) { + zlog_debug("Schedule LSP in-label %u flags 0x%x", + lsp->ile.in_label, lsp->flags); + } if (lsp_processq_add(lsp)) return -1; - } else if (!lsp->nhlfe_list - && !CHECK_FLAG(lsp->flags, LSP_FLAG_SCHEDULED)) { - if (IS_ZEBRA_DEBUG_MPLS) - zlog_debug("Free LSP in-label %u flags 0x%x", - lsp->ile.in_label, lsp->flags); - - lsp = hash_release(lsp_table, &lsp->ile); - XFREE(MTYPE_LSP, lsp); + } else { + lsp_check_free(lsp_table, &lsp); } return 0; @@ -1357,7 +1484,7 @@ static int mpls_static_lsp_uninstall_all(struct zebra_vrf *zvrf, /* If entry is not present, exit. */ tmp_ile.in_label = in_label; lsp = hash_lookup(lsp_table, &tmp_ile); - if (!lsp || !lsp->nhlfe_list) + if (!lsp || (nhlfe_list_first(&lsp->nhlfe_list) == NULL)) return 0; return mpls_lsp_uninstall_all(lsp_table, lsp, ZEBRA_LSP_STATIC); @@ -1367,12 +1494,23 @@ static json_object *nhlfe_json(zebra_nhlfe_t *nhlfe) { char buf[BUFSIZ]; json_object *json_nhlfe = NULL; + json_object *json_backups = NULL; + json_object *json_label_stack; struct nexthop *nexthop = nhlfe->nexthop; + int i; json_nhlfe = json_object_new_object(); json_object_string_add(json_nhlfe, "type", nhlfe_type2str(nhlfe->type)); json_object_int_add(json_nhlfe, "outLabel", nexthop->nh_label->label[0]); + + json_label_stack = json_object_new_array(); + json_object_object_add(json_nhlfe, "outLabelStack", json_label_stack); + for (i = 0; i < nexthop->nh_label->num_labels; i++) + json_object_array_add( + json_label_stack, + json_object_new_int(nexthop->nh_label->label[i])); + json_object_int_add(json_nhlfe, "distance", nhlfe->distance); if (CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED)) @@ -1398,16 +1536,30 @@ static json_object *nhlfe_json(zebra_nhlfe_t *nhlfe) default: break; } + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) { + json_backups = json_object_new_array(); + for (i = 0; i < nexthop->backup_num; i++) { + json_object_array_add( + json_backups, + json_object_new_int(nexthop->backup_idx[i])); + } + + json_object_object_add(json_nhlfe, "backupIndex", + json_backups); + } + return json_nhlfe; } /* * Print the NHLFE for a LSP forwarding entry. */ -static void nhlfe_print(zebra_nhlfe_t *nhlfe, struct vty *vty) +static void nhlfe_print(zebra_nhlfe_t *nhlfe, struct vty *vty, + const char *indent) { struct nexthop *nexthop; - char buf[BUFSIZ]; + char buf[MPLS_LABEL_STRLEN]; nexthop = nhlfe->nexthop; if (!nexthop || !nexthop->nh_label) // unexpected @@ -1415,8 +1567,14 @@ static void nhlfe_print(zebra_nhlfe_t *nhlfe, struct vty *vty) vty_out(vty, " type: %s remote label: %s distance: %d\n", nhlfe_type2str(nhlfe->type), - label2str(nexthop->nh_label->label[0], buf, BUFSIZ), + mpls_label2str(nexthop->nh_label->num_labels, + nexthop->nh_label->label, + buf, sizeof(buf), 0), nhlfe->distance); + + if (indent) + vty_out(vty, "%s", indent); + switch (nexthop->type) { case NEXTHOP_TYPE_IPV4: case NEXTHOP_TYPE_IPV4_IFINDEX: @@ -1429,7 +1587,8 @@ static void nhlfe_print(zebra_nhlfe_t *nhlfe, struct vty *vty) case NEXTHOP_TYPE_IPV6: case NEXTHOP_TYPE_IPV6_IFINDEX: vty_out(vty, " via %s", - inet_ntop(AF_INET6, &nexthop->gate.ipv6, buf, BUFSIZ)); + inet_ntop(AF_INET6, &nexthop->gate.ipv6, buf, + sizeof(buf))); if (nexthop->ifindex) vty_out(vty, " dev %s", ifindex2ifname(nexthop->ifindex, @@ -1438,6 +1597,9 @@ static void nhlfe_print(zebra_nhlfe_t *nhlfe, struct vty *vty) default: break; } + vty_out(vty, "%s", + CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_IS_BACKUP) ? " (backup)" + : ""); vty_out(vty, "%s", CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED) ? " (installed)" : ""); @@ -1447,19 +1609,40 @@ static void nhlfe_print(zebra_nhlfe_t *nhlfe, struct vty *vty) /* * Print an LSP forwarding entry. */ -static void lsp_print(zebra_lsp_t *lsp, void *ctxt) +static void lsp_print(struct vty *vty, zebra_lsp_t *lsp) { - zebra_nhlfe_t *nhlfe; - struct vty *vty; - - vty = (struct vty *)ctxt; + zebra_nhlfe_t *nhlfe, *backup; + int i, j; vty_out(vty, "Local label: %u%s\n", lsp->ile.in_label, CHECK_FLAG(lsp->flags, LSP_FLAG_INSTALLED) ? " (installed)" : ""); - for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next) - nhlfe_print(nhlfe, vty); + frr_each(nhlfe_list, &lsp->nhlfe_list, nhlfe) { + nhlfe_print(nhlfe, vty, NULL); + + if (nhlfe->nexthop == NULL || + !CHECK_FLAG(nhlfe->nexthop->flags, + NEXTHOP_FLAG_HAS_BACKUP)) + continue; + + /* Backup nhlfes: find backups in backup list */ + + for (j = 0; j < nhlfe->nexthop->backup_num; j++) { + i = 0; + backup = NULL; + frr_each(nhlfe_list, &lsp->backup_nhlfe_list, backup) { + if (i == nhlfe->nexthop->backup_idx[j]) + break; + i++; + } + + if (backup) { + vty_out(vty, " [backup %d]", i); + nhlfe_print(backup, vty, " "); + } + } + } } /* @@ -1476,10 +1659,23 @@ static json_object *lsp_json(zebra_lsp_t *lsp) if (CHECK_FLAG(lsp->flags, LSP_FLAG_INSTALLED)) json_object_boolean_true_add(json, "installed"); - for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next) + frr_each(nhlfe_list, &lsp->nhlfe_list, nhlfe) json_object_array_add(json_nhlfe_list, nhlfe_json(nhlfe)); json_object_object_add(json, "nexthops", json_nhlfe_list); + json_nhlfe_list = NULL; + + + frr_each(nhlfe_list, &lsp->backup_nhlfe_list, nhlfe) { + if (json_nhlfe_list == NULL) + json_nhlfe_list = json_object_new_array(); + + json_object_array_add(json_nhlfe_list, nhlfe_json(nhlfe)); + } + + if (json_nhlfe_list) + json_object_object_add(json, "backupNexthops", json_nhlfe_list); + return json; } @@ -1503,7 +1699,7 @@ static struct list *hash_get_sorted_list(struct hash *hash, void *cmp) /* * Compare two LSPs based on their label values. */ -static int lsp_cmp(zebra_lsp_t *lsp1, zebra_lsp_t *lsp2) +static int lsp_cmp(const zebra_lsp_t *lsp1, const zebra_lsp_t *lsp2) { if (lsp1->ile.in_label < lsp2->ile.in_label) return -1; @@ -1524,13 +1720,15 @@ static void *slsp_alloc(void *p) slsp = XCALLOC(MTYPE_SLSP, sizeof(zebra_slsp_t)); slsp->ile = *ile; + snhlfe_list_init(&slsp->snhlfe_list); + return ((void *)slsp); } /* * Compare two static LSPs based on their label values. */ -static int slsp_cmp(zebra_slsp_t *slsp1, zebra_slsp_t *slsp2) +static int slsp_cmp(const zebra_slsp_t *slsp1, const zebra_slsp_t *slsp2) { if (slsp1->ile.in_label < slsp2->ile.in_label) return -1; @@ -1545,7 +1743,7 @@ static int slsp_cmp(zebra_slsp_t *slsp1, zebra_slsp_t *slsp2) * Check if static NHLFE matches with search info passed. */ static int snhlfe_match(zebra_snhlfe_t *snhlfe, enum nexthop_types_t gtype, - union g_addr *gate, ifindex_t ifindex) + const union g_addr *gate, ifindex_t ifindex) { int cmp = 1; @@ -1576,14 +1774,14 @@ static int snhlfe_match(zebra_snhlfe_t *snhlfe, enum nexthop_types_t gtype, */ static zebra_snhlfe_t *snhlfe_find(zebra_slsp_t *slsp, enum nexthop_types_t gtype, - union g_addr *gate, ifindex_t ifindex) + const union g_addr *gate, ifindex_t ifindex) { zebra_snhlfe_t *snhlfe; if (!slsp) return NULL; - for (snhlfe = slsp->snhlfe_list; snhlfe; snhlfe = snhlfe->next) { + frr_each_safe(snhlfe_list, &slsp->snhlfe_list, snhlfe) { if (!snhlfe_match(snhlfe, gtype, gate, ifindex)) break; } @@ -1598,7 +1796,7 @@ static zebra_snhlfe_t *snhlfe_find(zebra_slsp_t *slsp, */ static zebra_snhlfe_t *snhlfe_add(zebra_slsp_t *slsp, enum nexthop_types_t gtype, - union g_addr *gate, ifindex_t ifindex, + const union g_addr *gate, ifindex_t ifindex, mpls_label_t out_label) { zebra_snhlfe_t *snhlfe; @@ -1625,10 +1823,7 @@ static zebra_snhlfe_t *snhlfe_add(zebra_slsp_t *slsp, return NULL; } - if (slsp->snhlfe_list) - slsp->snhlfe_list->prev = snhlfe; - snhlfe->next = slsp->snhlfe_list; - slsp->snhlfe_list = snhlfe; + snhlfe_list_add_head(&slsp->snhlfe_list, snhlfe); return snhlfe; } @@ -1647,14 +1842,8 @@ static int snhlfe_del(zebra_snhlfe_t *snhlfe) if (!slsp) return -1; - if (snhlfe->next) - snhlfe->next->prev = snhlfe->prev; - if (snhlfe->prev) - snhlfe->prev->next = snhlfe->next; - else - slsp->snhlfe_list = snhlfe->next; + snhlfe_list_del(&slsp->snhlfe_list, snhlfe); - snhlfe->prev = snhlfe->next = NULL; XFREE(MTYPE_SNHLFE_IFNAME, snhlfe->ifname); XFREE(MTYPE_SNHLFE, snhlfe); @@ -1666,13 +1855,12 @@ static int snhlfe_del(zebra_snhlfe_t *snhlfe) */ static int snhlfe_del_all(zebra_slsp_t *slsp) { - zebra_snhlfe_t *snhlfe, *snhlfe_next; + zebra_snhlfe_t *snhlfe; if (!slsp) return -1; - for (snhlfe = slsp->snhlfe_list; snhlfe; snhlfe = snhlfe_next) { - snhlfe_next = snhlfe->next; + frr_each_safe(snhlfe_list, &slsp->snhlfe_list, snhlfe) { snhlfe_del(snhlfe); } @@ -1693,8 +1881,9 @@ static char *snhlfe2str(zebra_snhlfe_t *snhlfe, char *buf, int size) case NEXTHOP_TYPE_IPV6_IFINDEX: inet_ntop(AF_INET6, &snhlfe->gate.ipv6, buf, size); if (snhlfe->ifindex) - strcat(buf, - ifindex2ifname(snhlfe->ifindex, VRF_DEFAULT)); + strlcat(buf, + ifindex2ifname(snhlfe->ifindex, VRF_DEFAULT), + size); break; default: break; @@ -1726,14 +1915,13 @@ static int mpls_processq_init(void) } -/* Public functions */ - /* * Process LSP update results from zebra dataplane. */ void zebra_mpls_lsp_dplane_result(struct zebra_dplane_ctx *ctx) { struct zebra_vrf *zvrf; + mpls_label_t label; zebra_ile_t tmp_ile; struct hash *lsp_table; zebra_lsp_t *lsp; @@ -1741,6 +1929,7 @@ void zebra_mpls_lsp_dplane_result(struct zebra_dplane_ctx *ctx) struct nexthop *nexthop; enum dplane_op_e op; enum zebra_dplane_result status; + enum zebra_sr_policy_update_label_mode update_mode; op = dplane_ctx_get_op(ctx); status = dplane_ctx_get_status(ctx); @@ -1751,6 +1940,8 @@ void zebra_mpls_lsp_dplane_result(struct zebra_dplane_ctx *ctx) dplane_ctx_get_in_label(ctx), dplane_res2str(status)); + label = dplane_ctx_get_in_label(ctx); + switch (op) { case DPLANE_OP_LSP_INSTALL: case DPLANE_OP_LSP_UPDATE: @@ -1761,7 +1952,7 @@ void zebra_mpls_lsp_dplane_result(struct zebra_dplane_ctx *ctx) lsp_table = zvrf->lsp_table; - tmp_ile.in_label = dplane_ctx_get_in_label(ctx); + tmp_ile.in_label = label; lsp = hash_lookup(lsp_table, &tmp_ile); if (lsp == NULL) { if (IS_ZEBRA_DEBUG_DPLANE) @@ -1772,33 +1963,44 @@ void zebra_mpls_lsp_dplane_result(struct zebra_dplane_ctx *ctx) /* TODO -- Confirm that this result is still 'current' */ - if (status == ZEBRA_DPLANE_REQUEST_SUCCESS) { - /* Update zebra object */ - SET_FLAG(lsp->flags, LSP_FLAG_INSTALLED); - for (nhlfe = lsp->nhlfe_list; nhlfe; - nhlfe = nhlfe->next) { - nexthop = nhlfe->nexthop; - if (!nexthop) - continue; - - SET_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED); - SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); - } - } else { + if (status != ZEBRA_DPLANE_REQUEST_SUCCESS) { UNSET_FLAG(lsp->flags, LSP_FLAG_INSTALLED); clear_nhlfe_installed(lsp); flog_warn(EC_ZEBRA_LSP_INSTALL_FAILURE, "LSP Install Failure: in-label %u", lsp->ile.in_label); + break; + } + + /* Update zebra object */ + SET_FLAG(lsp->flags, LSP_FLAG_INSTALLED); + frr_each(nhlfe_list, &lsp->nhlfe_list, nhlfe) { + nexthop = nhlfe->nexthop; + if (!nexthop) + continue; + + if (CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_SELECTED) && + CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) { + SET_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED); + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); + } } + update_mode = (op == DPLANE_OP_LSP_INSTALL) + ? ZEBRA_SR_POLICY_LABEL_CREATED + : ZEBRA_SR_POLICY_LABEL_UPDATED; + zebra_sr_policy_label_update(label, update_mode); break; case DPLANE_OP_LSP_DELETE: - if (status != ZEBRA_DPLANE_REQUEST_SUCCESS) + if (status != ZEBRA_DPLANE_REQUEST_SUCCESS) { flog_warn(EC_ZEBRA_LSP_DELETE_FAILURE, "LSP Deletion Failure: in-label %u", dplane_ctx_get_in_label(ctx)); + break; + } + zebra_sr_policy_label_update(label, + ZEBRA_SR_POLICY_LABEL_REMOVED); break; default: @@ -1810,50 +2012,331 @@ void zebra_mpls_lsp_dplane_result(struct zebra_dplane_ctx *ctx) } /* - * Install dynamic LSP entry. + * Process LSP installation info from two sets of nhlfes: a set from + * a dplane notification, and a set from the zebra LSP object. Update + * counters of installed nexthops, and return whether the LSP has changed. */ -int zebra_mpls_lsp_install(struct zebra_vrf *zvrf, struct route_node *rn, - struct route_entry *re) +static bool compare_notif_nhlfes(const struct nhlfe_list_head *ctx_head, + struct nhlfe_list_head *nhlfe_head, + int *start_counter, int *end_counter) { - struct route_table *table; - zebra_fec_t *fec; + zebra_nhlfe_t *nhlfe; + const zebra_nhlfe_t *ctx_nhlfe; + struct nexthop *nexthop; + const struct nexthop *ctx_nexthop; + int start_count = 0, end_count = 0; + bool changed_p = false; + bool is_debug = (IS_ZEBRA_DEBUG_DPLANE | IS_ZEBRA_DEBUG_MPLS); - table = zvrf->fec_table[family2afi(PREFIX_FAMILY(&rn->p))]; - if (!table) - return -1; + frr_each_safe(nhlfe_list, nhlfe_head, nhlfe) { + char buf[NEXTHOP_STRLEN]; - /* See if there is a configured label binding for this FEC. */ - fec = fec_find(table, &rn->p); - if (!fec || fec->label == MPLS_INVALID_LABEL) - return 0; + nexthop = nhlfe->nexthop; + if (!nexthop) + continue; - /* We cannot install a label forwarding entry if local label is the - * implicit-null label. - */ - if (fec->label == MPLS_LABEL_IMPLICIT_NULL) - return 0; + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) + start_count++; - if (lsp_install(zvrf, fec->label, rn, re)) - return -1; + ctx_nhlfe = NULL; + ctx_nexthop = NULL; + frr_each(nhlfe_list_const, ctx_head, ctx_nhlfe) { + ctx_nexthop = ctx_nhlfe->nexthop; + if (!ctx_nexthop) + continue; - return 0; + if ((ctx_nexthop->type == nexthop->type) && + nexthop_same(ctx_nexthop, nexthop)) { + /* Matched */ + break; + } + } + + if (is_debug) + nexthop2str(nexthop, buf, sizeof(buf)); + + if (ctx_nhlfe && ctx_nexthop) { + if (is_debug) { + const char *tstr = ""; + + if (!CHECK_FLAG(ctx_nhlfe->flags, + NHLFE_FLAG_INSTALLED)) + tstr = "not "; + + zlog_debug("LSP dplane notif: matched nh %s (%sinstalled)", + buf, tstr); + } + + /* Test zebra nhlfe install state */ + if (CHECK_FLAG(ctx_nhlfe->flags, + NHLFE_FLAG_INSTALLED)) { + + if (!CHECK_FLAG(nhlfe->flags, + NHLFE_FLAG_INSTALLED)) + changed_p = true; + + /* Update counter */ + end_count++; + } else { + + if (CHECK_FLAG(nhlfe->flags, + NHLFE_FLAG_INSTALLED)) + changed_p = true; + } + + } else { + /* Not mentioned in lfib set -> uninstalled */ + if (CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED) || + CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE) || + CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) { + changed_p = true; + } + + if (is_debug) + zlog_debug("LSP dplane notif: no match, nh %s", + buf); + } + } + + if (start_counter) + *start_counter += start_count; + if (end_counter) + *end_counter += end_count; + + return changed_p; } /* - * Uninstall dynamic LSP entry, if any. + * Update an lsp nhlfe list from a dplane context, typically an async + * notification context. Update the LSP list to match the installed + * status from the context's list. */ -int zebra_mpls_lsp_uninstall(struct zebra_vrf *zvrf, struct route_node *rn, - struct route_entry *re) +static int update_nhlfes_from_ctx(struct nhlfe_list_head *nhlfe_head, + const struct nhlfe_list_head *ctx_head) { - struct route_table *table; - zebra_fec_t *fec; + int ret = 0; + zebra_nhlfe_t *nhlfe; + const zebra_nhlfe_t *ctx_nhlfe; + struct nexthop *nexthop; + const struct nexthop *ctx_nexthop; + bool is_debug = (IS_ZEBRA_DEBUG_DPLANE | IS_ZEBRA_DEBUG_MPLS); - table = zvrf->fec_table[family2afi(PREFIX_FAMILY(&rn->p))]; - if (!table) - return -1; + frr_each_safe(nhlfe_list, nhlfe_head, nhlfe) { + char buf[NEXTHOP_STRLEN]; - /* See if there is a configured label binding for this FEC. */ - fec = fec_find(table, &rn->p); + nexthop = nhlfe->nexthop; + if (!nexthop) + continue; + + ctx_nhlfe = NULL; + ctx_nexthop = NULL; + frr_each(nhlfe_list_const, ctx_head, ctx_nhlfe) { + ctx_nexthop = ctx_nhlfe->nexthop; + if (!ctx_nexthop) + continue; + + if ((ctx_nexthop->type == nexthop->type) && + nexthop_same(ctx_nexthop, nexthop)) { + /* Matched */ + break; + } + } + + if (is_debug) + nexthop2str(nexthop, buf, sizeof(buf)); + + if (ctx_nhlfe && ctx_nexthop) { + + /* Bring zebra nhlfe install state into sync */ + if (CHECK_FLAG(ctx_nhlfe->flags, + NHLFE_FLAG_INSTALLED)) { + if (is_debug) + zlog_debug("%s: matched lsp nhlfe %s (installed)", + __func__, buf); + + SET_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED); + SET_FLAG(nhlfe->flags, NHLFE_FLAG_SELECTED); + + } else { + if (is_debug) + zlog_debug("%s: matched lsp nhlfe %s (not installed)", + __func__, buf); + + UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED); + UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_SELECTED); + } + + if (CHECK_FLAG(ctx_nhlfe->nexthop->flags, + NEXTHOP_FLAG_FIB)) { + SET_FLAG(nhlfe->nexthop->flags, + NEXTHOP_FLAG_ACTIVE); + SET_FLAG(nhlfe->nexthop->flags, + NEXTHOP_FLAG_FIB); + } else { + UNSET_FLAG(nhlfe->nexthop->flags, + NEXTHOP_FLAG_ACTIVE); + UNSET_FLAG(nhlfe->nexthop->flags, + NEXTHOP_FLAG_FIB); + } + + } else { + /* Not mentioned in lfib set -> uninstalled */ + if (is_debug) + zlog_debug("%s: no match for lsp nhlfe %s", + __func__, buf); + UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED); + UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_SELECTED); + UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); + UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); + } + } + + return ret; +} + +/* + * Process async dplane notifications. + */ +void zebra_mpls_process_dplane_notify(struct zebra_dplane_ctx *ctx) +{ + struct zebra_vrf *zvrf; + zebra_ile_t tmp_ile; + struct hash *lsp_table; + zebra_lsp_t *lsp; + const struct nhlfe_list_head *ctx_list; + int start_count = 0, end_count = 0; /* Installed counts */ + bool changed_p = false; + bool is_debug = (IS_ZEBRA_DEBUG_DPLANE | IS_ZEBRA_DEBUG_MPLS); + + if (is_debug) + zlog_debug("LSP dplane notif, in-label %u", + dplane_ctx_get_in_label(ctx)); + + /* Look for zebra LSP object */ + zvrf = vrf_info_lookup(VRF_DEFAULT); + if (zvrf == NULL) + goto done; + + lsp_table = zvrf->lsp_table; + + tmp_ile.in_label = dplane_ctx_get_in_label(ctx); + lsp = hash_lookup(lsp_table, &tmp_ile); + if (lsp == NULL) { + if (is_debug) + zlog_debug("dplane LSP notif: in-label %u not found", + dplane_ctx_get_in_label(ctx)); + goto done; + } + + /* + * The dataplane/forwarding plane is notifying zebra about the state + * of the nexthops associated with this LSP. First, we take a + * pre-scan pass to determine whether the LSP has transitioned + * from installed -> uninstalled. In that case, we need to have + * the existing state of the LSP objects available before making + * any changes. + */ + ctx_list = dplane_ctx_get_nhlfe_list(ctx); + + changed_p = compare_notif_nhlfes(ctx_list, &lsp->nhlfe_list, + &start_count, &end_count); + + if (is_debug) + zlog_debug("LSP dplane notif: lfib start_count %d, end_count %d%s", + start_count, end_count, + changed_p ? ", changed" : ""); + + ctx_list = dplane_ctx_get_backup_nhlfe_list(ctx); + + if (compare_notif_nhlfes(ctx_list, &lsp->backup_nhlfe_list, + &start_count, &end_count)) + /* Avoid accidentally setting back to 'false' */ + changed_p = true; + + if (is_debug) + zlog_debug("LSP dplane notif: lfib backups, start_count %d, end_count %d%s", + start_count, end_count, + changed_p ? ", changed" : ""); + + /* + * Has the LSP become uninstalled? We need the existing state of the + * nexthops/nhlfes at this point so we know what to delete. + */ + if (start_count > 0 && end_count == 0) { + /* Inform other lfibs */ + dplane_lsp_notif_update(lsp, DPLANE_OP_LSP_DELETE, ctx); + } + + /* + * Now we take a second pass and bring the zebra + * nexthop state into sync with the forwarding-plane state. + */ + ctx_list = dplane_ctx_get_nhlfe_list(ctx); + update_nhlfes_from_ctx(&lsp->nhlfe_list, ctx_list); + + ctx_list = dplane_ctx_get_backup_nhlfe_list(ctx); + update_nhlfes_from_ctx(&lsp->backup_nhlfe_list, ctx_list); + + if (end_count > 0) { + SET_FLAG(lsp->flags, LSP_FLAG_INSTALLED); + + if (changed_p) + dplane_lsp_notif_update(lsp, DPLANE_OP_LSP_UPDATE, ctx); + + } else { + UNSET_FLAG(lsp->flags, LSP_FLAG_INSTALLED); + clear_nhlfe_installed(lsp); + } + +done: + dplane_ctx_fini(&ctx); +} + +/* + * Install dynamic LSP entry. + */ +int zebra_mpls_lsp_install(struct zebra_vrf *zvrf, struct route_node *rn, + struct route_entry *re) +{ + struct route_table *table; + zebra_fec_t *fec; + + table = zvrf->fec_table[family2afi(PREFIX_FAMILY(&rn->p))]; + if (!table) + return -1; + + /* See if there is a configured label binding for this FEC. */ + fec = fec_find(table, &rn->p); + if (!fec || fec->label == MPLS_INVALID_LABEL) + return 0; + + /* We cannot install a label forwarding entry if local label is the + * implicit-null label. + */ + if (fec->label == MPLS_LABEL_IMPLICIT_NULL) + return 0; + + if (lsp_install(zvrf, fec->label, rn, re)) + return -1; + + return 0; +} + +/* + * Uninstall dynamic LSP entry, if any. + */ +int zebra_mpls_lsp_uninstall(struct zebra_vrf *zvrf, struct route_node *rn, + struct route_entry *re) +{ + struct route_table *table; + zebra_fec_t *fec; + + table = zvrf->fec_table[family2afi(PREFIX_FAMILY(&rn->p))]; + if (!table) + return -1; + + /* See if there is a configured label binding for this FEC. */ + fec = fec_find(table, &rn->p); if (!fec || fec->label == MPLS_INVALID_LABEL) return 0; @@ -1862,26 +2345,86 @@ int zebra_mpls_lsp_uninstall(struct zebra_vrf *zvrf, struct route_node *rn, } /* - * Add an NHLFE to an LSP, return the newly-added object + * Add an NHLFE to an LSP, return the newly-added object. This path only changes + * the LSP object - nothing is scheduled for processing, for example. */ zebra_nhlfe_t *zebra_mpls_lsp_add_nhlfe(zebra_lsp_t *lsp, enum lsp_types_t lsp_type, enum nexthop_types_t gtype, - union g_addr *gate, + const union g_addr *gate, ifindex_t ifindex, - mpls_label_t out_label) + uint8_t num_labels, + const mpls_label_t *out_labels) +{ + /* Just a public pass-through to the internal implementation */ + return nhlfe_add(lsp, lsp_type, gtype, gate, ifindex, num_labels, + out_labels, false /*backup*/); +} + +/* + * Add a backup NHLFE to an LSP, return the newly-added object. + * This path only changes the LSP object - nothing is scheduled for + * processing, for example. + */ +zebra_nhlfe_t *zebra_mpls_lsp_add_backup_nhlfe(zebra_lsp_t *lsp, + enum lsp_types_t lsp_type, + enum nexthop_types_t gtype, + const union g_addr *gate, + ifindex_t ifindex, + uint8_t num_labels, + const mpls_label_t *out_labels) { /* Just a public pass-through to the internal implementation */ - return nhlfe_add(lsp, lsp_type, gtype, gate, ifindex, out_label); + return nhlfe_add(lsp, lsp_type, gtype, gate, ifindex, num_labels, + out_labels, true); +} + +/* + * Add an NHLFE to an LSP based on a nexthop; return the newly-added object + */ +zebra_nhlfe_t *zebra_mpls_lsp_add_nh(zebra_lsp_t *lsp, + enum lsp_types_t lsp_type, + const struct nexthop *nh) +{ + zebra_nhlfe_t *nhlfe; + + if (nh->nh_label == NULL || nh->nh_label->num_labels == 0) + return NULL; + + nhlfe = nhlfe_add(lsp, lsp_type, nh->type, &nh->gate, nh->ifindex, + nh->nh_label->num_labels, nh->nh_label->label, + false /*backup*/); + + return nhlfe; +} + +/* + * Add a backup NHLFE to an LSP based on a nexthop; + * return the newly-added object. + */ +zebra_nhlfe_t *zebra_mpls_lsp_add_backup_nh(zebra_lsp_t *lsp, + enum lsp_types_t lsp_type, + const struct nexthop *nh) +{ + zebra_nhlfe_t *nhlfe; + + if (nh->nh_label == NULL || nh->nh_label->num_labels == 0) + return NULL; + + nhlfe = nhlfe_add(lsp, lsp_type, nh->type, &nh->gate, + nh->ifindex, nh->nh_label->num_labels, + nh->nh_label->label, true); + + return nhlfe; } /* * Free an allocated NHLFE */ -void zebra_mpls_nhlfe_del(zebra_nhlfe_t *nhlfe) +void zebra_mpls_nhlfe_free(zebra_nhlfe_t *nhlfe) { /* Just a pass-through to the internal implementation */ - nhlfe_del(nhlfe); + nhlfe_free(nhlfe); } /* @@ -2085,6 +2628,42 @@ static int zebra_mpls_cleanup_fecs_for_client(struct zserv *client) return 0; } +struct lsp_uninstall_args { + struct hash *lsp_table; + enum lsp_types_t type; +}; + +/* + * Cleanup MPLS labels registered by this client. + */ +static int zebra_mpls_cleanup_zclient_labels(struct zserv *client) +{ + struct vrf *vrf; + struct zebra_vrf *zvrf; + + RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) { + struct lsp_uninstall_args args; + + zvrf = vrf->info; + if (!zvrf) + continue; + + /* Cleanup LSPs. */ + args.lsp_table = zvrf->lsp_table; + args.type = lsp_type_from_re_type(client->proto); + hash_iterate(zvrf->lsp_table, mpls_lsp_uninstall_all_type, + &args); + + /* Cleanup FTNs. */ + mpls_ftn_uninstall_all(zvrf, AFI_IP, + lsp_type_from_re_type(client->proto)); + mpls_ftn_uninstall_all(zvrf, AFI_IP6, + lsp_type_from_re_type(client->proto)); + } + + return 0; +} + /* * Return FEC (if any) to which this label is bound. * Note: Only works for per-prefix binding and when the label is not @@ -2315,12 +2894,23 @@ void zebra_mpls_print_fec(struct vty *vty, struct zebra_vrf *zvrf, fec_print(rn->info, vty); } -static bool mpls_ftn_update_nexthop(int add, struct nexthop *nexthop, - enum lsp_types_t type, mpls_label_t label) +static void mpls_zebra_nhe_update(struct route_entry *re, afi_t afi, + struct nhg_hash_entry *new_nhe) { - if (add && nexthop->nh_label_type == ZEBRA_LSP_NONE) - nexthop_add_labels(nexthop, type, 1, &label); - else if (!add && nexthop->nh_label_type == type) + struct nhg_hash_entry *nhe; + + nhe = zebra_nhg_rib_find_nhe(new_nhe, afi); + + route_entry_update_nhe(re, nhe); +} + +static bool ftn_update_nexthop(bool add_p, struct nexthop *nexthop, + enum lsp_types_t type, + const struct zapi_nexthop *znh) +{ + if (add_p && nexthop->nh_label_type == ZEBRA_LSP_NONE) + nexthop_add_labels(nexthop, type, znh->label_num, znh->labels); + else if (!add_p && nexthop->nh_label_type == type) nexthop_del_labels(nexthop); else return false; @@ -2328,23 +2918,19 @@ static bool mpls_ftn_update_nexthop(int add, struct nexthop *nexthop, return true; } -/* - * Install/uninstall a FEC-To-NHLFE (FTN) binding. - */ -int mpls_ftn_update(int add, struct zebra_vrf *zvrf, enum lsp_types_t type, - struct prefix *prefix, enum nexthop_types_t gtype, - union g_addr *gate, ifindex_t ifindex, uint8_t distance, - mpls_label_t out_label) +int mpls_ftn_uninstall(struct zebra_vrf *zvrf, enum lsp_types_t type, + struct prefix *prefix, uint8_t route_type, + unsigned short route_instance) { struct route_table *table; struct route_node *rn; struct route_entry *re; struct nexthop *nexthop; - bool found; + struct nhg_hash_entry *new_nhe; + afi_t afi = family2afi(prefix->family); /* Lookup table. */ - table = zebra_vrf_table(family2afi(prefix->family), SAFI_UNICAST, - zvrf_id(zvrf)); + table = zebra_vrf_table(afi, SAFI_UNICAST, zvrf_id(zvrf)); if (!table) return -1; @@ -2353,59 +2939,301 @@ int mpls_ftn_update(int add, struct zebra_vrf *zvrf, enum lsp_types_t type, RNODE_FOREACH_RE (rn, re) { if (CHECK_FLAG(re->status, ROUTE_ENTRY_REMOVED)) continue; - if (re->distance == distance) + if (re->type == route_type && re->instance == route_instance) break; } - if (re == NULL) return -1; - found = false; - for (nexthop = re->ng.nexthop; nexthop; nexthop = nexthop->next) { + /* + * Nexthops are now shared by multiple routes, so we have to make + * a local copy, modify the copy, then update the route. + */ + new_nhe = zebra_nhe_copy(re->nhe, 0); + + for (nexthop = new_nhe->nhg.nexthop; nexthop; nexthop = nexthop->next) + nexthop_del_labels(nexthop); + + /* Update backup routes/nexthops also, if present. */ + if (zebra_nhg_get_backup_nhg(new_nhe) != NULL) { + for (nexthop = new_nhe->backup_info->nhe->nhg.nexthop; nexthop; + nexthop = nexthop->next) + nexthop_del_labels(nexthop); + } + + SET_FLAG(re->status, ROUTE_ENTRY_CHANGED); + SET_FLAG(re->status, ROUTE_ENTRY_LABELS_CHANGED); + + /* This will create (or ref) a new nhe, so we will discard the local + * temporary nhe + */ + mpls_zebra_nhe_update(re, afi, new_nhe); + + zebra_nhg_free(new_nhe); + + rib_queue_add(rn); + + return 0; +} + +/* + * Iterate through a list of nexthops, for a match for 'znh'. If found, + * update its labels according to 'add_p', and return 'true' if successful. + */ +static bool ftn_update_znh(bool add_p, enum lsp_types_t type, + struct nexthop *head, const struct zapi_nexthop *znh) +{ + bool found = false, success = false; + struct nexthop *nexthop; + + for (nexthop = head; nexthop; nexthop = nexthop->next) { switch (nexthop->type) { case NEXTHOP_TYPE_IPV4: case NEXTHOP_TYPE_IPV4_IFINDEX: - if (gtype != NEXTHOP_TYPE_IPV4 - && gtype != NEXTHOP_TYPE_IPV4_IFINDEX) + if (znh->type != NEXTHOP_TYPE_IPV4 + && znh->type != NEXTHOP_TYPE_IPV4_IFINDEX) continue; - if (!IPV4_ADDR_SAME(&nexthop->gate.ipv4, &gate->ipv4)) + if (!IPV4_ADDR_SAME(&nexthop->gate.ipv4, + &znh->gate.ipv4)) continue; if (nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX - && nexthop->ifindex != ifindex) + && nexthop->ifindex != znh->ifindex) continue; - if (!mpls_ftn_update_nexthop(add, nexthop, type, - out_label)) - return 0; + found = true; + + if (!ftn_update_nexthop(add_p, nexthop, type, znh)) + break; + + success = true; break; case NEXTHOP_TYPE_IPV6: case NEXTHOP_TYPE_IPV6_IFINDEX: - if (gtype != NEXTHOP_TYPE_IPV6 - && gtype != NEXTHOP_TYPE_IPV6_IFINDEX) + if (znh->type != NEXTHOP_TYPE_IPV6 + && znh->type != NEXTHOP_TYPE_IPV6_IFINDEX) continue; - if (!IPV6_ADDR_SAME(&nexthop->gate.ipv6, &gate->ipv6)) + if (!IPV6_ADDR_SAME(&nexthop->gate.ipv6, + &znh->gate.ipv6)) continue; if (nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX - && nexthop->ifindex != ifindex) + && nexthop->ifindex != znh->ifindex) continue; - if (!mpls_ftn_update_nexthop(add, nexthop, type, - out_label)) - return 0; + found = true; + + if (!ftn_update_nexthop(add_p, nexthop, type, znh)) + break; + success = true; break; default: break; } + + if (found) + break; } - if (!found) - return -1; + return success; +} - SET_FLAG(re->status, ROUTE_ENTRY_CHANGED); - SET_FLAG(re->status, ROUTE_ENTRY_LABELS_CHANGED); - rib_queue_add(rn); +/* + * Install/uninstall LSP and (optionally) FEC-To-NHLFE (FTN) bindings, + * using zapi message info. + * There are several changes that need to be made, in several zebra + * data structures, so we want to do all the work required at once. + */ +int mpls_zapi_labels_process(bool add_p, struct zebra_vrf *zvrf, + const struct zapi_labels *zl) +{ + int i, counter, ret = 0; + char buf[NEXTHOP_STRLEN], prefix_buf[PREFIX_STRLEN]; + const struct zapi_nexthop *znh; + struct route_table *table; + struct route_node *rn = NULL; + struct route_entry *re = NULL; + struct nhg_hash_entry *new_nhe = NULL; + bool found; + afi_t afi = AFI_IP; + const struct prefix *prefix = NULL; + struct hash *lsp_table; + zebra_ile_t tmp_ile; + zebra_lsp_t *lsp = NULL; - return 0; + /* Prep LSP for add case */ + if (add_p) { + /* Lookup table. */ + lsp_table = zvrf->lsp_table; + if (!lsp_table) + return -1; + + /* Find or create LSP object */ + tmp_ile.in_label = zl->local_label; + lsp = hash_get(lsp_table, &tmp_ile, lsp_alloc); + if (!lsp) + return -1; + } + + /* Prep for route/FEC update if requested */ + if (CHECK_FLAG(zl->message, ZAPI_LABELS_FTN)) { + prefix = &zl->route.prefix; + + afi = family2afi(prefix->family); + + /* Lookup table. */ + table = zebra_vrf_table(afi, SAFI_UNICAST, zvrf_id(zvrf)); + if (table) { + /* Lookup existing route */ + rn = route_node_get(table, prefix); + RNODE_FOREACH_RE(rn, re) { + if (CHECK_FLAG(re->status, ROUTE_ENTRY_REMOVED)) + continue; + if (re->type == zl->route.type && + re->instance == zl->route.instance) + break; + } + } + + if (re) { + /* + * Copy over current nexthops into a temporary group. + * We can't just change the values here since the nhgs + * are shared and if the labels change, we'll need + * to find or create a new nhg. We need to create + * a whole temporary group, make changes to it, + * then attach that to the route. + */ + new_nhe = zebra_nhe_copy(re->nhe, 0); + + } else { + /* + * The old version of the zapi code + * attempted to manage LSPs before trying to + * find a route/FEC, so we'll continue that way. + */ + if (IS_ZEBRA_DEBUG_RECV || IS_ZEBRA_DEBUG_MPLS) { + prefix2str(prefix, prefix_buf, + sizeof(prefix_buf)); + zlog_debug("%s: FTN update requested: no route for prefix %s", + __func__, prefix_buf); + } + } + } + + /* + * Use info from the zapi nexthops to add/replace/remove LSP/FECs + */ + + counter = 0; + for (i = 0; i < zl->nexthop_num; i++) { + + znh = &zl->nexthops[i]; + + /* Attempt LSP update */ + if (add_p) + ret = lsp_znh_install(lsp, zl->type, znh); + else + ret = mpls_lsp_uninstall(zvrf, zl->type, + zl->local_label, znh->type, + &znh->gate, znh->ifindex, + false); + if (ret < 0) { + if (IS_ZEBRA_DEBUG_RECV || IS_ZEBRA_DEBUG_MPLS) { + zapi_nexthop2str(znh, buf, sizeof(buf)); + zlog_debug("%s: Unable to %sinstall LSP: label %u, znh %s", + __func__, (add_p ? "" : "un"), + zl->local_label, buf); + } + continue; + } + + /* Attempt route/FEC update if requested */ + if (re == NULL) + continue; + + /* Search the route's nexthops for a match, and update it. */ + found = ftn_update_znh(add_p, zl->type, new_nhe->nhg.nexthop, + znh); + if (found) { + counter++; + } else if (IS_ZEBRA_DEBUG_RECV | IS_ZEBRA_DEBUG_MPLS) { + zapi_nexthop2str(znh, buf, sizeof(buf)); + prefix2str(prefix, prefix_buf, sizeof(prefix_buf)); + zlog_debug("%s: Unable to update FEC: prefix %s, label %u, znh %s", + __func__, prefix_buf, zl->local_label, buf); + } + } + + /* + * Process backup LSPs/nexthop entries also. We associate backup + * LSP info with backup nexthops. + */ + if (!CHECK_FLAG(zl->message, ZAPI_LABELS_HAS_BACKUPS)) + goto znh_done; + + for (i = 0; i < zl->backup_nexthop_num; i++) { + + znh = &zl->backup_nexthops[i]; + + if (add_p) + ret = lsp_backup_znh_install(lsp, zl->type, znh); + else + ret = mpls_lsp_uninstall(zvrf, zl->type, + zl->local_label, + znh->type, &znh->gate, + znh->ifindex, true); + + if (ret < 0) { + if (IS_ZEBRA_DEBUG_RECV || + IS_ZEBRA_DEBUG_MPLS) { + zapi_nexthop2str(znh, buf, sizeof(buf)); + zlog_debug("%s: Unable to %sinstall backup LSP: label %u, znh %s", + __func__, (add_p ? "" : "un"), + zl->local_label, buf); + } + continue; + } + + /* Attempt backup nexthop/FEC update if requested */ + if (re == NULL || zebra_nhg_get_backup_nhg(new_nhe) == NULL) + continue; + + /* Search the route's backup nexthops for a match + * and update it. + */ + found = ftn_update_znh(add_p, zl->type, + new_nhe->backup_info->nhe->nhg.nexthop, + znh); + if (found) { + counter++; + } else if (IS_ZEBRA_DEBUG_RECV | IS_ZEBRA_DEBUG_MPLS) { + zapi_nexthop2str(znh, buf, sizeof(buf)); + prefix2str(prefix, prefix_buf, sizeof(prefix_buf)); + zlog_debug("%s: Unable to update backup FEC: prefix %s, label %u, znh %s", + __func__, prefix_buf, zl->local_label, buf); + } + } + +znh_done: + + /* + * If we made changes, update the route, and schedule it + * for rib processing + */ + if (re != NULL && counter > 0) { + assert(rn != NULL); + + SET_FLAG(re->status, ROUTE_ENTRY_CHANGED); + SET_FLAG(re->status, ROUTE_ENTRY_LABELS_CHANGED); + + mpls_zebra_nhe_update(re, afi, new_nhe); + + rib_queue_add(rn); + } + + if (new_nhe) + zebra_nhg_free(new_nhe); + + return ret; } /* @@ -2413,28 +3241,26 @@ int mpls_ftn_update(int add, struct zebra_vrf *zvrf, enum lsp_types_t type, * a new LSP entry or a new NHLFE for an existing in-label or an update of * the out-label for an existing NHLFE (update case). */ -int mpls_lsp_install(struct zebra_vrf *zvrf, enum lsp_types_t type, - mpls_label_t in_label, mpls_label_t out_label, - enum nexthop_types_t gtype, union g_addr *gate, - ifindex_t ifindex) +static zebra_nhlfe_t * +lsp_add_nhlfe(zebra_lsp_t *lsp, enum lsp_types_t type, + uint8_t num_out_labels, const mpls_label_t *out_labels, + enum nexthop_types_t gtype, const union g_addr *gate, + ifindex_t ifindex, bool is_backup) { - struct hash *lsp_table; - zebra_ile_t tmp_ile; - zebra_lsp_t *lsp; zebra_nhlfe_t *nhlfe; - char buf[BUFSIZ]; + char buf[MPLS_LABEL_STRLEN]; + const char *backup_str; - /* Lookup table. */ - lsp_table = zvrf->lsp_table; - if (!lsp_table) - return -1; + if (is_backup) { + nhlfe = nhlfe_find(&lsp->backup_nhlfe_list, type, gtype, + gate, ifindex); + backup_str = "backup "; + } else { + nhlfe = nhlfe_find(&lsp->nhlfe_list, type, gtype, gate, + ifindex); + backup_str = ""; + } - /* If entry is present, exit. */ - tmp_ile.in_label = in_label; - lsp = hash_get(lsp_table, &tmp_ile, lsp_alloc); - if (!lsp) - return -1; - nhlfe = nhlfe_find(lsp, type, gtype, gate, ifindex); if (nhlfe) { struct nexthop *nh = nhlfe->nexthop; @@ -2443,33 +3269,54 @@ int mpls_lsp_install(struct zebra_vrf *zvrf, enum lsp_types_t type, /* Clear deleted flag (in case it was set) */ UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_DELETED); - if (nh->nh_label->label[0] == out_label) + if (nh->nh_label->num_labels == num_out_labels + && !memcmp(nh->nh_label->label, out_labels, + sizeof(mpls_label_t) * num_out_labels)) /* No change */ - return 0; + return nhlfe; if (IS_ZEBRA_DEBUG_MPLS) { - nhlfe2str(nhlfe, buf, BUFSIZ); - zlog_debug( - "LSP in-label %u type %d nexthop %s " - "out-label changed to %u (old %u)", - in_label, type, buf, out_label, - nh->nh_label->label[0]); + char buf2[MPLS_LABEL_STRLEN]; + char buf3[MPLS_LABEL_STRLEN]; + + nhlfe2str(nhlfe, buf, sizeof(buf)); + mpls_label2str(num_out_labels, out_labels, buf2, + sizeof(buf2), 0); + mpls_label2str(nh->nh_label->num_labels, + nh->nh_label->label, buf3, sizeof(buf3), + 0); + + zlog_debug("LSP in-label %u type %d %snexthop %s out-label(s) changed to %s (old %s)", + lsp->ile.in_label, type, backup_str, buf, + buf2, buf3); } - /* Update out label, trigger processing. */ - nh->nh_label->label[0] = out_label; + /* Update out label(s), trigger processing. */ + if (nh->nh_label->num_labels == num_out_labels) + memcpy(nh->nh_label->label, out_labels, + sizeof(mpls_label_t) * num_out_labels); + else { + nexthop_del_labels(nh); + nexthop_add_labels(nh, type, num_out_labels, + out_labels); + } } else { /* Add LSP entry to this nexthop */ - nhlfe = nhlfe_add(lsp, type, gtype, gate, ifindex, out_label); + nhlfe = nhlfe_add(lsp, type, gtype, gate, ifindex, + num_out_labels, out_labels, is_backup); if (!nhlfe) - return -1; + return NULL; if (IS_ZEBRA_DEBUG_MPLS) { - nhlfe2str(nhlfe, buf, BUFSIZ); - zlog_debug( - "Add LSP in-label %u type %d nexthop %s " - "out-label %u", - in_label, type, buf, out_label); + char buf2[MPLS_LABEL_STRLEN]; + + nhlfe2str(nhlfe, buf, sizeof(buf)); + mpls_label2str(num_out_labels, out_labels, buf2, + sizeof(buf2), 0); + + zlog_debug("Add LSP in-label %u type %d %snexthop %s out-label(s) %s", + lsp->ile.in_label, type, backup_str, buf, + buf2); } lsp->addr_family = NHLFE_FAMILY(nhlfe); @@ -2477,25 +3324,140 @@ int mpls_lsp_install(struct zebra_vrf *zvrf, enum lsp_types_t type, /* Mark NHLFE, queue LSP for processing. */ SET_FLAG(nhlfe->flags, NHLFE_FLAG_CHANGED); + + return nhlfe; +} + +/* + * Install an LSP and forwarding entry; used primarily + * from vrf zapi message processing. + */ +int mpls_lsp_install(struct zebra_vrf *zvrf, enum lsp_types_t type, + mpls_label_t in_label, uint8_t num_out_labels, + const mpls_label_t *out_labels, enum nexthop_types_t gtype, + const union g_addr *gate, ifindex_t ifindex) +{ + struct hash *lsp_table; + zebra_ile_t tmp_ile; + zebra_lsp_t *lsp; + zebra_nhlfe_t *nhlfe; + + /* Lookup table. */ + lsp_table = zvrf->lsp_table; + if (!lsp_table) + return -1; + + /* Find or create LSP object */ + tmp_ile.in_label = in_label; + lsp = hash_get(lsp_table, &tmp_ile, lsp_alloc); + if (!lsp) + return -1; + + nhlfe = lsp_add_nhlfe(lsp, type, num_out_labels, out_labels, gtype, + gate, ifindex, false /*backup*/); + if (nhlfe == NULL) + return -1; + + /* Queue LSP for processing. */ if (lsp_processq_add(lsp)) return -1; return 0; } +/* + * Install or replace NHLFE, using info from zapi nexthop + */ +static int lsp_znh_install(zebra_lsp_t *lsp, enum lsp_types_t type, + const struct zapi_nexthop *znh) +{ + zebra_nhlfe_t *nhlfe; + + nhlfe = lsp_add_nhlfe(lsp, type, znh->label_num, znh->labels, + znh->type, &znh->gate, znh->ifindex, + false /*backup*/); + if (nhlfe == NULL) + return -1; + + /* Update backup info if present */ + if (CHECK_FLAG(znh->flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP)) { + if (znh->backup_num > NEXTHOP_MAX_BACKUPS) { + nhlfe_del(nhlfe); + return -1; + } + + nhlfe->nexthop->backup_num = znh->backup_num; + memcpy(nhlfe->nexthop->backup_idx, znh->backup_idx, + znh->backup_num); + SET_FLAG(nhlfe->nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP); + } else { + /* Ensure there's no stale backup info */ + UNSET_FLAG(nhlfe->nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP); + nhlfe->nexthop->backup_num = 0; + } + + /* Queue LSP for processing. */ + if (lsp_processq_add(lsp)) + return -1; + + return 0; +} + +/* + * Install/update backup NHLFE for an LSP, using info from a zapi message. + */ +static int lsp_backup_znh_install(zebra_lsp_t *lsp, enum lsp_types_t type, + const struct zapi_nexthop *znh) +{ + zebra_nhlfe_t *nhlfe; + + nhlfe = lsp_add_nhlfe(lsp, type, znh->label_num, + znh->labels, znh->type, &znh->gate, + znh->ifindex, true /*backup*/); + if (nhlfe == NULL) { + if (IS_ZEBRA_DEBUG_MPLS) + zlog_debug("%s: unable to add backup nhlfe, label: %u", + __func__, lsp->ile.in_label); + return -1; + } + + /* Queue LSP for processing. */ + if (lsp_processq_add(lsp)) + return -1; + + return 0; +} + +zebra_lsp_t *mpls_lsp_find(struct zebra_vrf *zvrf, mpls_label_t in_label) +{ + struct hash *lsp_table; + zebra_ile_t tmp_ile; + + /* Lookup table. */ + lsp_table = zvrf->lsp_table; + if (!lsp_table) + return NULL; + + /* If entry is not present, exit. */ + tmp_ile.in_label = in_label; + return hash_lookup(lsp_table, &tmp_ile); +} + /* * Uninstall a particular NHLFE in the forwarding table. If this is * the only NHLFE, the entire LSP forwarding entry has to be deleted. */ int mpls_lsp_uninstall(struct zebra_vrf *zvrf, enum lsp_types_t type, mpls_label_t in_label, enum nexthop_types_t gtype, - union g_addr *gate, ifindex_t ifindex) + const union g_addr *gate, ifindex_t ifindex, + bool backup_p) { struct hash *lsp_table; zebra_ile_t tmp_ile; zebra_lsp_t *lsp; zebra_nhlfe_t *nhlfe; - char buf[BUFSIZ]; + char buf[NEXTHOP_STRLEN]; + bool schedule_lsp = false; /* Lookup table. */ lsp_table = zvrf->lsp_table; @@ -2507,69 +3469,100 @@ int mpls_lsp_uninstall(struct zebra_vrf *zvrf, enum lsp_types_t type, lsp = hash_lookup(lsp_table, &tmp_ile); if (!lsp) return 0; - nhlfe = nhlfe_find(lsp, type, gtype, gate, ifindex); + + if (backup_p) + nhlfe = nhlfe_find(&lsp->backup_nhlfe_list, type, gtype, + gate, ifindex); + else + nhlfe = nhlfe_find(&lsp->nhlfe_list, type, gtype, gate, + ifindex); if (!nhlfe) return 0; if (IS_ZEBRA_DEBUG_MPLS) { - nhlfe2str(nhlfe, buf, BUFSIZ); + nhlfe2str(nhlfe, buf, sizeof(buf)); zlog_debug("Del LSP in-label %u type %d nexthop %s flags 0x%x", in_label, type, buf, nhlfe->flags); } + if (CHECK_FLAG(lsp->flags, LSP_FLAG_INSTALLED) || + CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED)) + schedule_lsp = true; + /* Mark NHLFE for delete or directly delete, as appropriate. */ - if (CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED)) { - UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_CHANGED); + if (schedule_lsp) { SET_FLAG(nhlfe->flags, NHLFE_FLAG_DELETED); + UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_CHANGED); + + if (IS_ZEBRA_DEBUG_MPLS) + zlog_debug("Schedule LSP in-label %u flags 0x%x", + lsp->ile.in_label, lsp->flags); if (lsp_processq_add(lsp)) return -1; } else { nhlfe_del(nhlfe); /* Free LSP entry if no other NHLFEs and not scheduled. */ - if (!lsp->nhlfe_list - && !CHECK_FLAG(lsp->flags, LSP_FLAG_SCHEDULED)) { - if (IS_ZEBRA_DEBUG_MPLS) - zlog_debug("Free LSP in-label %u flags 0x%x", - lsp->ile.in_label, lsp->flags); - - lsp = hash_release(lsp_table, &lsp->ile); - XFREE(MTYPE_LSP, lsp); - } + lsp_check_free(lsp_table, &lsp); } return 0; } +int mpls_lsp_uninstall_all_vrf(struct zebra_vrf *zvrf, enum lsp_types_t type, + mpls_label_t in_label) +{ + struct hash *lsp_table; + zebra_ile_t tmp_ile; + zebra_lsp_t *lsp; + + /* Lookup table. */ + lsp_table = zvrf->lsp_table; + if (!lsp_table) + return -1; + + /* If entry is not present, exit. */ + tmp_ile.in_label = in_label; + lsp = hash_lookup(lsp_table, &tmp_ile); + if (!lsp) + return 0; + + return mpls_lsp_uninstall_all(lsp_table, lsp, type); +} + /* - * Uninstall all LDP NHLFEs for a particular LSP forwarding entry. + * Uninstall all NHLFEs for a particular LSP forwarding entry. * If no other NHLFEs exist, the entry would be deleted. */ -void mpls_ldp_lsp_uninstall_all(struct hash_bucket *bucket, void *ctxt) +static void mpls_lsp_uninstall_all_type(struct hash_bucket *bucket, void *ctxt) { + struct lsp_uninstall_args *args = ctxt; zebra_lsp_t *lsp; struct hash *lsp_table; lsp = (zebra_lsp_t *)bucket->data; - if (!lsp->nhlfe_list) + if (nhlfe_list_first(&lsp->nhlfe_list) == NULL) return; - lsp_table = ctxt; + lsp_table = args->lsp_table; if (!lsp_table) return; - mpls_lsp_uninstall_all(lsp_table, lsp, ZEBRA_LSP_LDP); + mpls_lsp_uninstall_all(lsp_table, lsp, args->type); } /* - * Uninstall all LDP FEC-To-NHLFE (FTN) bindings of the given address-family. + * Uninstall all FEC-To-NHLFE (FTN) bindings of the given address-family and + * LSP type. */ -void mpls_ldp_ftn_uninstall_all(struct zebra_vrf *zvrf, int afi) +static void mpls_ftn_uninstall_all(struct zebra_vrf *zvrf, + int afi, enum lsp_types_t lsp_type) { struct route_table *table; struct route_node *rn; struct route_entry *re; struct nexthop *nexthop; - int update; + struct nexthop_group *nhg; + bool update; /* Process routes of interested address-families. */ table = zebra_vrf_table(afi, SAFI_UNICAST, zvrf_id(zvrf)); @@ -2577,19 +3570,47 @@ void mpls_ldp_ftn_uninstall_all(struct zebra_vrf *zvrf, int afi) return; for (rn = route_top(table); rn; rn = route_next(rn)) { - update = 0; + update = false; + RNODE_FOREACH_RE (rn, re) { - for (nexthop = re->ng.nexthop; nexthop; + struct nhg_hash_entry *new_nhe; + + new_nhe = zebra_nhe_copy(re->nhe, 0); + + nhg = &new_nhe->nhg; + for (nexthop = nhg->nexthop; nexthop; nexthop = nexthop->next) { - if (nexthop->nh_label_type != ZEBRA_LSP_LDP) + if (nexthop->nh_label_type != lsp_type) continue; nexthop_del_labels(nexthop); SET_FLAG(re->status, ROUTE_ENTRY_CHANGED); SET_FLAG(re->status, ROUTE_ENTRY_LABELS_CHANGED); - update = 1; + update = true; + } + + /* Check for backup info and update that also */ + nhg = zebra_nhg_get_backup_nhg(new_nhe); + if (nhg != NULL) { + for (nexthop = nhg->nexthop; nexthop; + nexthop = nexthop->next) { + if (nexthop->nh_label_type != lsp_type) + continue; + + nexthop_del_labels(nexthop); + SET_FLAG(re->status, + ROUTE_ENTRY_CHANGED); + SET_FLAG(re->status, + ROUTE_ENTRY_LABELS_CHANGED); + update = true; + } } + + if (CHECK_FLAG(re->status, ROUTE_ENTRY_LABELS_CHANGED)) + mpls_zebra_nhe_update(re, afi, new_nhe); + + zebra_nhg_free(new_nhe); } if (update) @@ -2632,15 +3653,17 @@ int zebra_mpls_lsp_label_consistent(struct zebra_vrf *zvrf, return 1; /* If not only NHLFE, cannot allow label change. */ - if (snhlfe != slsp->snhlfe_list || snhlfe->next) + if (snhlfe != snhlfe_list_first(&slsp->snhlfe_list) || + snhlfe_list_next(&slsp->snhlfe_list, snhlfe) != NULL) return 0; } else { /* If other NHLFEs exist, label operation must match. */ - if (slsp->snhlfe_list) { + snhlfe = snhlfe_list_first(&slsp->snhlfe_list); + if (snhlfe != NULL) { int cur_op, new_op; - cur_op = (slsp->snhlfe_list->out_label - == MPLS_LABEL_IMPLICIT_NULL); + cur_op = (snhlfe->out_label == + MPLS_LABEL_IMPLICIT_NULL); new_op = (out_label == MPLS_LABEL_IMPLICIT_NULL); if (cur_op != new_op) return 0; @@ -2675,11 +3698,12 @@ int zebra_mpls_static_lsp_add(struct zebra_vrf *zvrf, mpls_label_t in_label, if (!slsp_table) return -1; - /* If entry is present, exit. */ + /* Find or create LSP. */ tmp_ile.in_label = in_label; slsp = hash_get(slsp_table, &tmp_ile, slsp_alloc); if (!slsp) return -1; + snhlfe = snhlfe_find(slsp, gtype, gate, ifindex); if (snhlfe) { if (snhlfe->out_label == out_label) @@ -2687,10 +3711,9 @@ int zebra_mpls_static_lsp_add(struct zebra_vrf *zvrf, mpls_label_t in_label, return 0; if (IS_ZEBRA_DEBUG_MPLS) { - snhlfe2str(snhlfe, buf, BUFSIZ); + snhlfe2str(snhlfe, buf, sizeof(buf)); zlog_debug( - "Upd static LSP in-label %u nexthop %s " - "out-label %u (old %u)", + "Upd static LSP in-label %u nexthop %s out-label %u (old %u)", in_label, buf, out_label, snhlfe->out_label); } snhlfe->out_label = out_label; @@ -2701,7 +3724,7 @@ int zebra_mpls_static_lsp_add(struct zebra_vrf *zvrf, mpls_label_t in_label, return -1; if (IS_ZEBRA_DEBUG_MPLS) { - snhlfe2str(snhlfe, buf, BUFSIZ); + snhlfe2str(snhlfe, buf, sizeof(buf)); zlog_debug( "Add static LSP in-label %u nexthop %s out-label %u", in_label, buf, out_label); @@ -2709,8 +3732,8 @@ int zebra_mpls_static_lsp_add(struct zebra_vrf *zvrf, mpls_label_t in_label, } /* (Re)Install LSP in the main table. */ - if (mpls_lsp_install(zvrf, ZEBRA_LSP_STATIC, in_label, out_label, gtype, - gate, ifindex)) + if (mpls_lsp_install(zvrf, ZEBRA_LSP_STATIC, in_label, 1, &out_label, + gtype, gate, ifindex)) return -1; return 0; @@ -2768,7 +3791,7 @@ int zebra_mpls_static_lsp_del(struct zebra_vrf *zvrf, mpls_label_t in_label, /* Uninstall LSP from the main table. */ mpls_lsp_uninstall(zvrf, ZEBRA_LSP_STATIC, in_label, gtype, - gate, ifindex); + gate, ifindex, false); /* Delete static LSP NHLFE */ snhlfe_del(snhlfe); @@ -2776,7 +3799,7 @@ int zebra_mpls_static_lsp_del(struct zebra_vrf *zvrf, mpls_label_t in_label, /* Remove entire static LSP entry if no NHLFE - valid in either case * above. */ - if (!slsp->snhlfe_list) { + if (snhlfe_list_first(&slsp->snhlfe_list) == NULL) { slsp = hash_release(slsp_table, &tmp_ile); XFREE(MTYPE_SLSP, slsp); } @@ -2825,7 +3848,7 @@ void zebra_mpls_print_lsp(struct vty *vty, struct zebra_vrf *zvrf, json, JSON_C_TO_STRING_PRETTY)); json_object_free(json); } else - lsp_print(lsp, (void *)vty); + lsp_print(vty, lsp); } /* @@ -2838,7 +3861,6 @@ void zebra_mpls_print_lsp_table(struct vty *vty, struct zebra_vrf *zvrf, json_object *json = NULL; zebra_lsp_t *lsp = NULL; zebra_nhlfe_t *nhlfe = NULL; - struct nexthop *nexthop = NULL; struct listnode *node = NULL; struct list *lsp_list = hash_get_sorted_list(zvrf->lsp_table, lsp_cmp); @@ -2847,22 +3869,30 @@ void zebra_mpls_print_lsp_table(struct vty *vty, struct zebra_vrf *zvrf, for (ALL_LIST_ELEMENTS_RO(lsp_list, node, lsp)) json_object_object_add( - json, label2str(lsp->ile.in_label, buf, BUFSIZ), + json, label2str(lsp->ile.in_label, buf, + sizeof(buf)), lsp_json(lsp)); vty_out(vty, "%s\n", json_object_to_json_string_ext( json, JSON_C_TO_STRING_PRETTY)); json_object_free(json); } else { - vty_out(vty, " Inbound Outbound\n"); - vty_out(vty, " Label Type Nexthop Label\n"); - vty_out(vty, "-------- ------- --------------- --------\n"); + struct ttable *tt; + + /* Prepare table. */ + tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]); + ttable_add_row(tt, "Inbound Label|Type|Nexthop|Outbound Label"); + tt->style.cell.rpad = 2; + tt->style.corner = '+'; + ttable_restyle(tt); + ttable_rowseps(tt, 0, BOTTOM, true, '-'); for (ALL_LIST_ELEMENTS_RO(lsp_list, node, lsp)) { - for (nhlfe = lsp->nhlfe_list; nhlfe; - nhlfe = nhlfe->next) { - vty_out(vty, "%8d %7s ", lsp->ile.in_label, - nhlfe_type2str(nhlfe->type)); + frr_each_safe(nhlfe_list, &lsp->nhlfe_list, nhlfe) { + struct nexthop *nexthop; + const char *out_label_str; + char nh_buf[NEXTHOP_STRLEN]; + nexthop = nhlfe->nexthop; switch (nexthop->type) { @@ -2872,41 +3902,47 @@ void zebra_mpls_print_lsp_table(struct vty *vty, struct zebra_vrf *zvrf, zns = zebra_ns_lookup(NS_DEFAULT); ifp = if_lookup_by_index_per_ns( - zns, - nexthop->ifindex); - vty_out(vty, "%15s", ifp->name); + zns, nexthop->ifindex); + snprintf(nh_buf, sizeof(nh_buf), "%s", + ifp ? ifp->name : "Null"); break; } case NEXTHOP_TYPE_IPV4: case NEXTHOP_TYPE_IPV4_IFINDEX: - vty_out(vty, "%15s", - inet_ntoa(nexthop->gate.ipv4)); + inet_ntop(AF_INET, &nexthop->gate.ipv4, + nh_buf, sizeof(nh_buf)); break; case NEXTHOP_TYPE_IPV6: case NEXTHOP_TYPE_IPV6_IFINDEX: - vty_out(vty, "%15s", - inet_ntop(AF_INET6, - &nexthop->gate.ipv6, - buf, BUFSIZ)); + inet_ntop(AF_INET6, &nexthop->gate.ipv6, + nh_buf, sizeof(nh_buf)); break; default: break; } if (nexthop->type != NEXTHOP_TYPE_IFINDEX) - vty_out(vty, " %8s\n", - mpls_label2str( - nexthop->nh_label - ->num_labels, - &nexthop->nh_label - ->label[0], - buf, BUFSIZ, 1)); + out_label_str = mpls_label2str( + nexthop->nh_label->num_labels, + &nexthop->nh_label->label[0], + buf, sizeof(buf), 1); else - vty_out(vty, "\n"); + out_label_str = "-"; + + ttable_add_row(tt, "%u|%s|%s|%s", + lsp->ile.in_label, + nhlfe_type2str(nhlfe->type), + nh_buf, out_label_str); } } - vty_out(vty, "\n"); + /* Dump the generated table. */ + if (tt->nrows > 1) { + char *table = ttable_dump(tt, "\n"); + vty_out(vty, "%s\n", table); + XFREE(MTYPE_TMP, table); + } + ttable_del(tt); } list_delete(&lsp_list); @@ -2924,8 +3960,7 @@ int zebra_mpls_write_lsp_config(struct vty *vty, struct zebra_vrf *zvrf) hash_get_sorted_list(zvrf->slsp_table, slsp_cmp); for (ALL_LIST_ELEMENTS_RO(slsp_list, node, slsp)) { - for (snhlfe = slsp->snhlfe_list; snhlfe; - snhlfe = snhlfe->next) { + frr_each(snhlfe_list, &slsp->snhlfe_list, snhlfe) { char buf[BUFSIZ]; char lstr[30]; @@ -2939,7 +3974,8 @@ int zebra_mpls_write_lsp_config(struct vty *vty, struct zebra_vrf *zvrf) strlcpy(lstr, "implicit-null", sizeof(lstr)); break; default: - sprintf(lstr, "%u", snhlfe->out_label); + snprintf(lstr, sizeof(lstr), "%u", + snhlfe->out_label); break; } @@ -2999,11 +4035,30 @@ int zebra_mpls_write_label_block_config(struct vty *vty, struct zebra_vrf *zvrf) /* * Called when VRF becomes inactive, cleans up information but keeps * the table itself. - * NOTE: Currently supported only for default VRF. */ void zebra_mpls_cleanup_tables(struct zebra_vrf *zvrf) { - hash_iterate(zvrf->lsp_table, lsp_uninstall_from_kernel, NULL); + struct zebra_vrf *def_zvrf; + afi_t afi; + + if (zvrf_id(zvrf) == VRF_DEFAULT) + hash_iterate(zvrf->lsp_table, lsp_uninstall_from_kernel, NULL); + else { + /* + * For other vrfs, we try to remove associated LSPs; we locate + * the LSPs in the default vrf. + */ + def_zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT); + + /* At shutdown, the default may be gone already */ + if (def_zvrf == NULL) + return; + + for (afi = AFI_IP; afi < AFI_MAX; afi++) { + if (zvrf->label[afi] != MPLS_LABEL_NONE) + lsp_uninstall(def_zvrf, zvrf->label[afi]); + } + } } /* @@ -3057,4 +4112,5 @@ void zebra_mpls_init(void) mpls_enabled = 1; hook_register(zserv_client_close, zebra_mpls_cleanup_fecs_for_client); + hook_register(zserv_client_close, zebra_mpls_cleanup_zclient_labels); } diff --git a/zebra/zebra_mpls.h b/zebra/zebra_mpls.h index 3a131e1aaf..c0e58c44e3 100644 --- a/zebra/zebra_mpls.h +++ b/zebra/zebra_mpls.h @@ -33,6 +33,7 @@ #include "mpls.h" #include "zebra/zserv.h" #include "zebra/zebra_vrf.h" +#include "hook.h" #ifdef __cplusplus extern "C" { @@ -55,6 +56,10 @@ typedef struct zebra_nhlfe_t_ zebra_nhlfe_t; typedef struct zebra_lsp_t_ zebra_lsp_t; typedef struct zebra_fec_t_ zebra_fec_t; +/* Declare LSP nexthop list types */ +PREDECL_DLIST(snhlfe_list); +PREDECL_DLIST(nhlfe_list); + /* * (Outgoing) nexthop label forwarding entry configuration */ @@ -71,9 +76,8 @@ struct zebra_snhlfe_t_ { /* Backpointer to base entry. */ zebra_slsp_t *slsp; - /* Pointers to more outgoing information for same in-label */ - zebra_snhlfe_t *next; - zebra_snhlfe_t *prev; + /* Linkage for LSPs' lists */ + struct snhlfe_list_item list; }; /* @@ -96,10 +100,12 @@ struct zebra_nhlfe_t_ { #define NHLFE_FLAG_MULTIPATH (1 << 2) #define NHLFE_FLAG_DELETED (1 << 3) #define NHLFE_FLAG_INSTALLED (1 << 4) +#define NHLFE_FLAG_IS_BACKUP (1 << 5) - zebra_nhlfe_t *next; - zebra_nhlfe_t *prev; uint8_t distance; + + /* Linkage for LSPs' lists */ + struct nhlfe_list_item list; }; /* @@ -117,7 +123,7 @@ struct zebra_slsp_t_ { zebra_ile_t ile; /* List of outgoing nexthop static configuration */ - zebra_snhlfe_t *snhlfe_list; + struct snhlfe_list_head snhlfe_list; }; /* @@ -127,11 +133,18 @@ struct zebra_lsp_t_ { /* Incoming label */ zebra_ile_t ile; - /* List of NHLFE, pointer to best and num equal-cost. */ - zebra_nhlfe_t *nhlfe_list; + /* List of NHLFEs, pointer to best, and num equal-cost. */ + struct nhlfe_list_head nhlfe_list; + zebra_nhlfe_t *best_nhlfe; uint32_t num_ecmp; + /* Backup nhlfes, if present. The nexthop in a primary/active nhlfe + * refers to its backup (if any) by index, so the order of this list + * is significant. + */ + struct nhlfe_list_head backup_nhlfe_list; + /* Flags */ uint32_t flags; #define LSP_FLAG_SCHEDULED (1 << 0) @@ -164,6 +177,9 @@ struct zebra_fec_t_ { struct list *client_list; }; +/* Declare typesafe list apis/macros */ +DECLARE_DLIST(nhlfe_list, struct zebra_nhlfe_t_, list); + /* Function declarations. */ /* @@ -198,12 +214,34 @@ int zebra_mpls_lsp_uninstall(struct zebra_vrf *zvrf, struct route_node *rn, zebra_nhlfe_t *zebra_mpls_lsp_add_nhlfe(zebra_lsp_t *lsp, enum lsp_types_t lsp_type, enum nexthop_types_t gtype, - union g_addr *gate, + const union g_addr *gate, ifindex_t ifindex, - mpls_label_t out_label); + uint8_t num_labels, + const mpls_label_t *out_labels); + +/* Add or update a backup NHLFE for an LSP; return the object */ +zebra_nhlfe_t *zebra_mpls_lsp_add_backup_nhlfe(zebra_lsp_t *lsp, + enum lsp_types_t lsp_type, + enum nexthop_types_t gtype, + const union g_addr *gate, + ifindex_t ifindex, + uint8_t num_labels, + const mpls_label_t *out_labels); + +/* + * Add NHLFE or backup NHLFE to an LSP based on a nexthop. These just maintain + * the LSP and NHLFE objects; nothing is scheduled for processing. + * Return: the newly-added object + */ +zebra_nhlfe_t *zebra_mpls_lsp_add_nh(zebra_lsp_t *lsp, + enum lsp_types_t lsp_type, + const struct nexthop *nh); +zebra_nhlfe_t *zebra_mpls_lsp_add_backup_nh(zebra_lsp_t *lsp, + enum lsp_types_t lsp_type, + const struct nexthop *nh); /* Free an allocated NHLFE */ -void zebra_mpls_nhlfe_del(zebra_nhlfe_t *nhlfe); +void zebra_mpls_nhlfe_free(zebra_nhlfe_t *nhlfe); int zebra_mpls_fec_register(struct zebra_vrf *zvrf, struct prefix *p, uint32_t label, uint32_t label_index, @@ -265,22 +303,33 @@ void zebra_mpls_print_fec(struct vty *vty, struct zebra_vrf *zvrf, struct prefix *p); /* - * Install/uninstall a FEC-To-NHLFE (FTN) binding. + * Handle zapi request to install/uninstall LSP and + * (optionally) FEC-To-NHLFE (FTN) bindings. */ -int mpls_ftn_update(int add, struct zebra_vrf *zvrf, enum lsp_types_t type, - struct prefix *prefix, enum nexthop_types_t gtype, - union g_addr *gate, ifindex_t ifindex, uint8_t distance, - mpls_label_t out_label); +int mpls_zapi_labels_process(bool add_p, struct zebra_vrf *zvrf, + const struct zapi_labels *zl); + +/* + * Uninstall all NHLFEs bound to a single FEC. + */ +int mpls_ftn_uninstall(struct zebra_vrf *zvrf, enum lsp_types_t type, + struct prefix *prefix, uint8_t route_type, + unsigned short route_instance); /* * Install/update a NHLFE for an LSP in the forwarding table. This may be * a new LSP entry or a new NHLFE for an existing in-label or an update of - * the out-label for an existing NHLFE (update case). + * the out-label(s) for an existing NHLFE (update case). */ int mpls_lsp_install(struct zebra_vrf *zvrf, enum lsp_types_t type, - mpls_label_t in_label, mpls_label_t out_label, - enum nexthop_types_t gtype, union g_addr *gate, - ifindex_t ifindex); + mpls_label_t in_label, uint8_t num_out_labels, + const mpls_label_t *out_labels, enum nexthop_types_t gtype, + const union g_addr *gate, ifindex_t ifindex); + +/* + * Lookup LSP by its input label. + */ +zebra_lsp_t *mpls_lsp_find(struct zebra_vrf *zvrf, mpls_label_t in_label); /* * Uninstall a particular NHLFE in the forwarding table. If this is @@ -288,24 +337,14 @@ int mpls_lsp_install(struct zebra_vrf *zvrf, enum lsp_types_t type, */ int mpls_lsp_uninstall(struct zebra_vrf *zvrf, enum lsp_types_t type, mpls_label_t in_label, enum nexthop_types_t gtype, - union g_addr *gate, ifindex_t ifindex); - -/* - * Uninstall all LDP NHLFEs for a particular LSP forwarding entry. - * If no other NHLFEs exist, the entry would be deleted. - */ -void mpls_ldp_lsp_uninstall_all(struct hash_bucket *bucket, void *ctxt); + const union g_addr *gate, ifindex_t ifindex, + bool backup_p); /* - * Uninstall all LDP FEC-To-NHLFE (FTN) bindings of the given address-family. + * Uninstall all NHLFEs for a particular LSP forwarding entry. */ -void mpls_ldp_ftn_uninstall_all(struct zebra_vrf *zvrf, int afi); - -/* - * Uninstall all Segment Routing NHLFEs for a particular LSP forwarding entry. - * If no other NHLFEs exist, the entry would be deleted. - */ -void mpls_sr_lsp_uninstall_all(struct hash_bucket *bucket, void *ctxt); +int mpls_lsp_uninstall_all_vrf(struct zebra_vrf *zvrf, enum lsp_types_t type, + mpls_label_t in_label); #if defined(HAVE_CUMULUS) /* @@ -352,6 +391,9 @@ struct zebra_dplane_ctx; void zebra_mpls_lsp_dplane_result(struct zebra_dplane_ctx *ctx); +/* Process async dplane notifications. */ +void zebra_mpls_process_dplane_notify(struct zebra_dplane_ctx *ctx); + /* * Schedule all MPLS label forwarding entries for processing. * Called upon changes that may affect one or more of them such as @@ -423,7 +465,9 @@ static inline uint8_t lsp_distance(enum lsp_types_t type) return (route_distance(ZEBRA_ROUTE_BGP)); case ZEBRA_LSP_NONE: case ZEBRA_LSP_SHARP: - case ZEBRA_LSP_SR: + case ZEBRA_LSP_OSPF_SR: + case ZEBRA_LSP_ISIS_SR: + case ZEBRA_LSP_SRTE: return 150; } @@ -445,10 +489,18 @@ static inline enum lsp_types_t lsp_type_from_re_type(int re_type) switch (re_type) { case ZEBRA_ROUTE_STATIC: return ZEBRA_LSP_STATIC; + case ZEBRA_ROUTE_LDP: + return ZEBRA_LSP_LDP; case ZEBRA_ROUTE_BGP: return ZEBRA_LSP_BGP; + case ZEBRA_ROUTE_OSPF: + return ZEBRA_LSP_OSPF_SR; + case ZEBRA_ROUTE_ISIS: + return ZEBRA_LSP_ISIS_SR; case ZEBRA_ROUTE_SHARP: return ZEBRA_LSP_SHARP; + case ZEBRA_ROUTE_SRTE: + return ZEBRA_LSP_SRTE; default: return ZEBRA_LSP_NONE; } @@ -466,12 +518,16 @@ static inline int re_type_from_lsp_type(enum lsp_types_t lsp_type) return ZEBRA_ROUTE_LDP; case ZEBRA_LSP_BGP: return ZEBRA_ROUTE_BGP; - case ZEBRA_LSP_SR: + case ZEBRA_LSP_OSPF_SR: return ZEBRA_ROUTE_OSPF; + case ZEBRA_LSP_ISIS_SR: + return ZEBRA_ROUTE_ISIS; case ZEBRA_LSP_NONE: return ZEBRA_ROUTE_KERNEL; case ZEBRA_LSP_SHARP: return ZEBRA_ROUTE_SHARP; + case ZEBRA_LSP_SRTE: + return ZEBRA_ROUTE_SRTE; } /* @@ -493,10 +549,14 @@ static inline const char *nhlfe_type2str(enum lsp_types_t lsp_type) return "LDP"; case ZEBRA_LSP_BGP: return "BGP"; - case ZEBRA_LSP_SR: - return "SR"; + case ZEBRA_LSP_OSPF_SR: + return "SR (OSPF)"; + case ZEBRA_LSP_ISIS_SR: + return "SR (IS-IS)"; case ZEBRA_LSP_SHARP: return "SHARP"; + case ZEBRA_LSP_SRTE: + return "SR-TE"; case ZEBRA_LSP_NONE: return "Unknown"; } diff --git a/zebra/zebra_mpls_netlink.c b/zebra/zebra_mpls_netlink.c index a9233530dc..3b2279c66c 100644 --- a/zebra/zebra_mpls_netlink.c +++ b/zebra/zebra_mpls_netlink.c @@ -26,13 +26,12 @@ #include "zebra/rt.h" #include "zebra/rt_netlink.h" #include "zebra/zebra_mpls.h" +#include "zebra/kernel_netlink.h" -/* - * LSP forwarding update using dataplane context information. - */ -enum zebra_dplane_result kernel_lsp_update(struct zebra_dplane_ctx *ctx) +static ssize_t netlink_lsp_msg_encoder(struct zebra_dplane_ctx *ctx, void *buf, + size_t buflen) { - int cmd, ret = -1; + int cmd; /* Call to netlink layer based on type of update */ if (dplane_ctx_get_op(ctx) == DPLANE_OP_LSP_DELETE) { @@ -45,20 +44,21 @@ enum zebra_dplane_result kernel_lsp_update(struct zebra_dplane_ctx *ctx) if (IS_ZEBRA_DEBUG_KERNEL || IS_ZEBRA_DEBUG_MPLS) zlog_debug("LSP in-label %u: update fails, no best NHLFE", dplane_ctx_get_in_label(ctx)); - goto done; + return -1; } cmd = RTM_NEWROUTE; } else /* Invalid op? */ - goto done; - - ret = netlink_mpls_multipath(cmd, ctx); + return -1; -done: + return netlink_mpls_multipath_msg_encode(cmd, ctx, buf, buflen); +} - return (ret == 0 ? - ZEBRA_DPLANE_REQUEST_SUCCESS : ZEBRA_DPLANE_REQUEST_FAILURE); +enum netlink_msg_status netlink_put_lsp_update_msg(struct nl_batch *bth, + struct zebra_dplane_ctx *ctx) +{ + return netlink_batch_add_msg(bth, ctx, netlink_lsp_msg_encoder, false); } /* @@ -66,9 +66,10 @@ enum zebra_dplane_result kernel_lsp_update(struct zebra_dplane_ctx *ctx) * but note that the default has been to report 'success' for pw updates * on unsupported platforms. */ -enum zebra_dplane_result kernel_pw_update(struct zebra_dplane_ctx *ctx) +enum netlink_msg_status netlink_put_pw_update_msg(struct nl_batch *bth, + struct zebra_dplane_ctx *ctx) { - return ZEBRA_DPLANE_REQUEST_SUCCESS; + return FRR_NETLINK_SUCCESS; } int mpls_kernel_init(void) diff --git a/zebra/zebra_mpls_openbsd.c b/zebra/zebra_mpls_openbsd.c index 977a8eaf3c..b767929dc0 100644 --- a/zebra/zebra_mpls_openbsd.c +++ b/zebra/zebra_mpls_openbsd.c @@ -27,6 +27,7 @@ #include "zebra/zebra_mpls.h" #include "zebra/debug.h" #include "zebra/zebra_errors.h" +#include "zebra/zebra_router.h" #include "privs.h" #include "prefix.h" @@ -118,7 +119,7 @@ static int kernel_send_rtmsg_v4(int action, mpls_label_t in_label, hdr.rtm_mpls = MPLS_OP_SWAP; } - frr_elevate_privs(&zserv_privs) { + frr_with_privs(&zserv_privs) { ret = writev(kr_state.fd, iov, iovcnt); } @@ -225,7 +226,7 @@ static int kernel_send_rtmsg_v6(int action, mpls_label_t in_label, hdr.rtm_mpls = MPLS_OP_SWAP; } - frr_elevate_privs(&zserv_privs) { + frr_with_privs(&zserv_privs) { ret = writev(kr_state.fd, iov, iovcnt); } @@ -238,8 +239,9 @@ static int kernel_send_rtmsg_v6(int action, mpls_label_t in_label, static int kernel_lsp_cmd(struct zebra_dplane_ctx *ctx) { + const struct nhlfe_list_head *head; const zebra_nhlfe_t *nhlfe; - struct nexthop *nexthop = NULL; + const struct nexthop *nexthop = NULL; unsigned int nexthop_num = 0; int action; @@ -257,12 +259,13 @@ static int kernel_lsp_cmd(struct zebra_dplane_ctx *ctx) return -1; } - for (nhlfe = dplane_ctx_get_nhlfe(ctx); nhlfe; nhlfe = nhlfe->next) { + head = dplane_ctx_get_nhlfe_list(ctx); + frr_each(nhlfe_list_const, head, nhlfe) { nexthop = nhlfe->nexthop; if (!nexthop) continue; - if (nexthop_num >= multipath_num) + if (nexthop_num >= zrouter.multipath_num) break; if (((action == RTM_ADD || action == RTM_CHANGE) @@ -273,8 +276,7 @@ static int kernel_lsp_cmd(struct zebra_dplane_ctx *ctx) && CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)))) { if (nhlfe->nexthop->nh_label->num_labels > 1) { flog_warn(EC_ZEBRA_MAX_LABELS_PUSH, - "%s: can't push %u labels at once " - "(maximum is 1)", + "%s: can't push %u labels at once (maximum is 1)", __func__, nhlfe->nexthop->nh_label->num_labels); continue; @@ -301,7 +303,7 @@ static int kernel_lsp_cmd(struct zebra_dplane_ctx *ctx) } } - return (0); + return 0; } enum zebra_dplane_result kernel_lsp_update(struct zebra_dplane_ctx *ctx) @@ -368,7 +370,7 @@ static enum zebra_dplane_result kmpw_install(struct zebra_dplane_ctx *ctx) /* ioctl */ memset(&ifr, 0, sizeof(ifr)); - strlcpy(ifr.ifr_name, dplane_ctx_get_pw_ifname(ctx), + strlcpy(ifr.ifr_name, dplane_ctx_get_ifname(ctx), sizeof(ifr.ifr_name)); ifr.ifr_data = (caddr_t)&imr; if (ioctl(kr_state.ioctl_fd, SIOCSETMPWCFG, &ifr) == -1) { @@ -387,7 +389,7 @@ static enum zebra_dplane_result kmpw_uninstall(struct zebra_dplane_ctx *ctx) memset(&ifr, 0, sizeof(ifr)); memset(&imr, 0, sizeof(imr)); - strlcpy(ifr.ifr_name, dplane_ctx_get_pw_ifname(ctx), + strlcpy(ifr.ifr_name, dplane_ctx_get_ifname(ctx), sizeof(ifr.ifr_name)); ifr.ifr_data = (caddr_t)&imr; if (ioctl(kr_state.ioctl_fd, SIOCSETMPWCFG, &ifr) == -1) { diff --git a/zebra/zebra_mpls_vty.c b/zebra/zebra_mpls_vty.c index 796aa3f666..d789f20071 100644 --- a/zebra/zebra_mpls_vty.c +++ b/zebra/zebra_mpls_vty.c @@ -449,15 +449,21 @@ DEFUN (no_mpls_label_global_block, return zebra_mpls_global_block(vty, 0, NULL, NULL); } +static int zebra_mpls_config(struct vty *vty); /* MPLS node for MPLS LSP. */ -static struct cmd_node mpls_node = {MPLS_NODE, "", 1}; +static struct cmd_node mpls_node = { + .name = "mpls", + .node = MPLS_NODE, + .prompt = "", + .config_write = zebra_mpls_config, +}; /* MPLS VTY. */ void zebra_mpls_vty_init(void) { install_element(VIEW_NODE, &show_mpls_status_cmd); - install_node(&mpls_node, zebra_mpls_config); + install_node(&mpls_node); install_element(CONFIG_NODE, &mpls_transit_lsp_cmd); install_element(CONFIG_NODE, &no_mpls_transit_lsp_cmd); diff --git a/zebra/zebra_nb.c b/zebra/zebra_nb.c new file mode 100644 index 0000000000..1fc1faff67 --- /dev/null +++ b/zebra/zebra_nb.c @@ -0,0 +1,682 @@ +/* + * Copyright (C) 2020 Cumulus Networks, Inc. + * Chirag Shah + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include "northbound.h" +#include "libfrr.h" +#include "zebra_nb.h" + +/* clang-format off */ +const struct frr_yang_module_info frr_zebra_info = { + .name = "frr-zebra", + .nodes = { + { + .xpath = "/frr-zebra:zebra/mcast-rpf-lookup", + .cbs = { + .modify = zebra_mcast_rpf_lookup_modify, + } + }, + { + .xpath = "/frr-zebra:zebra/ip-forwarding", + .cbs = { + .modify = zebra_ip_forwarding_modify, + .destroy = zebra_ip_forwarding_destroy, + } + }, + { + .xpath = "/frr-zebra:zebra/ipv6-forwarding", + .cbs = { + .modify = zebra_ipv6_forwarding_modify, + .destroy = zebra_ipv6_forwarding_destroy, + } + }, + { + .xpath = "/frr-zebra:zebra/workqueue-hold-timer", + .cbs = { + .modify = zebra_workqueue_hold_timer_modify, + } + }, + { + .xpath = "/frr-zebra:zebra/zapi-packets", + .cbs = { + .modify = zebra_zapi_packets_modify, + } + }, + { + .xpath = "/frr-zebra:zebra/import-kernel-table/table-id", + .cbs = { + .modify = zebra_import_kernel_table_table_id_modify, + .destroy = zebra_import_kernel_table_table_id_destroy, + } + }, + { + .xpath = "/frr-zebra:zebra/import-kernel-table/distance", + .cbs = { + .modify = zebra_import_kernel_table_distance_modify, + } + }, + { + .xpath = "/frr-zebra:zebra/import-kernel-table/route-map", + .cbs = { + .modify = zebra_import_kernel_table_route_map_modify, + .destroy = zebra_import_kernel_table_route_map_destroy, + } + }, + { + .xpath = "/frr-zebra:zebra/allow-external-route-update", + .cbs = { + .create = zebra_allow_external_route_update_create, + .destroy = zebra_allow_external_route_update_destroy, + } + }, + { + .xpath = "/frr-zebra:zebra/dplane-queue-limit", + .cbs = { + .modify = zebra_dplane_queue_limit_modify, + } + }, + { + .xpath = "/frr-zebra:zebra/debugs/debug-events", + .cbs = { + .modify = zebra_debugs_debug_events_modify, + .destroy = zebra_debugs_debug_events_destroy, + } + }, + { + .xpath = "/frr-zebra:zebra/debugs/debug-zapi-send", + .cbs = { + .modify = zebra_debugs_debug_zapi_send_modify, + .destroy = zebra_debugs_debug_zapi_send_destroy, + } + }, + { + .xpath = "/frr-zebra:zebra/debugs/debug-zapi-recv", + .cbs = { + .modify = zebra_debugs_debug_zapi_recv_modify, + .destroy = zebra_debugs_debug_zapi_recv_destroy, + } + }, + { + .xpath = "/frr-zebra:zebra/debugs/debug-zapi-detail", + .cbs = { + .modify = zebra_debugs_debug_zapi_detail_modify, + .destroy = zebra_debugs_debug_zapi_detail_destroy, + } + }, + { + .xpath = "/frr-zebra:zebra/debugs/debug-kernel", + .cbs = { + .modify = zebra_debugs_debug_kernel_modify, + .destroy = zebra_debugs_debug_kernel_destroy, + } + }, + { + .xpath = "/frr-zebra:zebra/debugs/debug-kernel-msg-send", + .cbs = { + .modify = zebra_debugs_debug_kernel_msg_send_modify, + .destroy = zebra_debugs_debug_kernel_msg_send_destroy, + } + }, + { + .xpath = "/frr-zebra:zebra/debugs/debug-kernel-msg-recv", + .cbs = { + .modify = zebra_debugs_debug_kernel_msg_recv_modify, + .destroy = zebra_debugs_debug_kernel_msg_recv_destroy, + } + }, + { + .xpath = "/frr-zebra:zebra/debugs/debug-rib", + .cbs = { + .modify = zebra_debugs_debug_rib_modify, + .destroy = zebra_debugs_debug_rib_destroy, + } + }, + { + .xpath = "/frr-zebra:zebra/debugs/debug-rib-detail", + .cbs = { + .modify = zebra_debugs_debug_rib_detail_modify, + .destroy = zebra_debugs_debug_rib_detail_destroy, + } + }, + { + .xpath = "/frr-zebra:zebra/debugs/debug-fpm", + .cbs = { + .modify = zebra_debugs_debug_fpm_modify, + .destroy = zebra_debugs_debug_fpm_destroy, + } + }, + { + .xpath = "/frr-zebra:zebra/debugs/debug-nht", + .cbs = { + .modify = zebra_debugs_debug_nht_modify, + .destroy = zebra_debugs_debug_nht_destroy, + } + }, + { + .xpath = "/frr-zebra:zebra/debugs/debug-nht-detail", + .cbs = { + .modify = zebra_debugs_debug_nht_detail_modify, + .destroy = zebra_debugs_debug_nht_detail_destroy, + } + }, + { + .xpath = "/frr-zebra:zebra/debugs/debug-mpls", + .cbs = { + .modify = zebra_debugs_debug_mpls_modify, + .destroy = zebra_debugs_debug_mpls_destroy, + } + }, + { + .xpath = "/frr-zebra:zebra/debugs/debug-vxlan", + .cbs = { + .modify = zebra_debugs_debug_vxlan_modify, + .destroy = zebra_debugs_debug_vxlan_destroy, + } + }, + { + .xpath = "/frr-zebra:zebra/debugs/debug-pw", + .cbs = { + .modify = zebra_debugs_debug_pw_modify, + .destroy = zebra_debugs_debug_pw_destroy, + } + }, + { + .xpath = "/frr-zebra:zebra/debugs/debug-dplane", + .cbs = { + .modify = zebra_debugs_debug_dplane_modify, + .destroy = zebra_debugs_debug_dplane_destroy, + } + }, + { + .xpath = "/frr-zebra:zebra/debugs/debug-dplane-detail", + .cbs = { + .modify = zebra_debugs_debug_dplane_detail_modify, + .destroy = zebra_debugs_debug_dplane_detail_destroy, + } + }, + { + .xpath = "/frr-zebra:zebra/debugs/debug-mlag", + .cbs = { + .modify = zebra_debugs_debug_mlag_modify, + .destroy = zebra_debugs_debug_mlag_destroy, + } + }, + { + .xpath = "/frr-zebra:get-route-information", + .cbs = { + .rpc = get_route_information_rpc, + } + }, + { + .xpath = "/frr-zebra:get-v6-mroute-info", + .cbs = { + .rpc = get_v6_mroute_info_rpc, + } + }, + { + .xpath = "/frr-zebra:get-vrf-info", + .cbs = { + .rpc = get_vrf_info_rpc, + } + }, + { + .xpath = "/frr-zebra:get-vrf-vni-info", + .cbs = { + .rpc = get_vrf_vni_info_rpc, + } + }, + { + .xpath = "/frr-zebra:get-evpn-info", + .cbs = { + .rpc = get_evpn_info_rpc, + } + }, + { + .xpath = "/frr-zebra:get-vni-info", + .cbs = { + .rpc = get_vni_info_rpc, + } + }, + { + .xpath = "/frr-zebra:get-evpn-vni-rmac", + .cbs = { + .rpc = get_evpn_vni_rmac_rpc, + } + }, + { + .xpath = "/frr-zebra:get-evpn-vni-nexthops", + .cbs = { + .rpc = get_evpn_vni_nexthops_rpc, + } + }, + { + .xpath = "/frr-zebra:clear-evpn-dup-addr", + .cbs = { + .rpc = clear_evpn_dup_addr_rpc, + } + }, + { + .xpath = "/frr-zebra:get-evpn-macs", + .cbs = { + .rpc = get_evpn_macs_rpc, + } + }, + { + .xpath = "/frr-zebra:get-evpn-arp-cache", + .cbs = { + .rpc = get_evpn_arp_cache_rpc, + } + }, + { + .xpath = "/frr-zebra:get-pbr-ipset", + .cbs = { + .rpc = get_pbr_ipset_rpc, + } + }, + { + .xpath = "/frr-zebra:get-pbr-iptable", + .cbs = { + .rpc = get_pbr_iptable_rpc, + } + }, + { + .xpath = "/frr-zebra:get-debugs", + .cbs = { + .rpc = get_debugs_rpc, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ip-addrs", + .cbs = { + .create = lib_interface_zebra_ip_addrs_create, + .destroy = lib_interface_zebra_ip_addrs_destroy, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ip-addrs/label", + .cbs = { + .modify = lib_interface_zebra_ip_addrs_label_modify, + .destroy = lib_interface_zebra_ip_addrs_label_destroy, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/ip-addrs/ip4-peer", + .cbs = { + .modify = lib_interface_zebra_ip_addrs_ip4_peer_modify, + .destroy = lib_interface_zebra_ip_addrs_ip4_peer_destroy, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/multicast", + .cbs = { + .modify = lib_interface_zebra_multicast_modify, + .destroy = lib_interface_zebra_multicast_destroy, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/link-detect", + .cbs = { + .modify = lib_interface_zebra_link_detect_modify, + .destroy = lib_interface_zebra_link_detect_destroy, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/shutdown", + .cbs = { + .modify = lib_interface_zebra_shutdown_modify, + .destroy = lib_interface_zebra_shutdown_destroy, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/bandwidth", + .cbs = { + .modify = lib_interface_zebra_bandwidth_modify, + .destroy = lib_interface_zebra_bandwidth_destroy, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/state/up-count", + .cbs = { + .get_elem = lib_interface_zebra_state_up_count_get_elem, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/state/down-count", + .cbs = { + .get_elem = lib_interface_zebra_state_down_count_get_elem, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/state/zif-type", + .cbs = { + .get_elem = lib_interface_zebra_state_zif_type_get_elem, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/state/ptm-status", + .cbs = { + .get_elem = lib_interface_zebra_state_ptm_status_get_elem, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/state/vlan-id", + .cbs = { + .get_elem = lib_interface_zebra_state_vlan_id_get_elem, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/state/vni-id", + .cbs = { + .get_elem = lib_interface_zebra_state_vni_id_get_elem, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/state/remote-vtep", + .cbs = { + .get_elem = lib_interface_zebra_state_remote_vtep_get_elem, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/state/mcast-group", + .cbs = { + .get_elem = lib_interface_zebra_state_mcast_group_get_elem, + } + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib", + .cbs = { + .create = lib_vrf_zebra_ribs_rib_create, + .destroy = lib_vrf_zebra_ribs_rib_destroy, + .get_next = lib_vrf_zebra_ribs_rib_get_next, + .get_keys = lib_vrf_zebra_ribs_rib_get_keys, + .lookup_entry = lib_vrf_zebra_ribs_rib_lookup_entry, + } + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route", + .cbs = { + .get_next = lib_vrf_zebra_ribs_rib_route_get_next, + .get_keys = lib_vrf_zebra_ribs_rib_route_get_keys, + .lookup_entry = lib_vrf_zebra_ribs_rib_route_lookup_entry, + } + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/prefix", + .cbs = { + .get_elem = lib_vrf_zebra_ribs_rib_route_prefix_get_elem, + } + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry", + .cbs = { + .get_next = lib_vrf_zebra_ribs_rib_route_route_entry_get_next, + .get_keys = lib_vrf_zebra_ribs_rib_route_route_entry_get_keys, + .lookup_entry = lib_vrf_zebra_ribs_rib_route_route_entry_lookup_entry, + } + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/protocol", + .cbs = { + .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_protocol_get_elem, + } + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/instance", + .cbs = { + .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_instance_get_elem, + } + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/distance", + .cbs = { + .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_distance_get_elem, + } + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/metric", + .cbs = { + .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_metric_get_elem, + } + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/tag", + .cbs = { + .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_tag_get_elem, + } + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/selected", + .cbs = { + .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_selected_get_elem, + } + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/installed", + .cbs = { + .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_installed_get_elem, + } + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/failed", + .cbs = { + .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_failed_get_elem, + } + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/queued", + .cbs = { + .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_queued_get_elem, + } + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/internal-flags", + .cbs = { + .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_internal_flags_get_elem, + } + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/internal-status", + .cbs = { + .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_internal_status_get_elem, + } + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/uptime", + .cbs = { + .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_uptime_get_elem, + } + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/id", + .cbs = { + .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_id_get_elem, + } + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop", + .cbs = { + .get_next = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_get_next, + .get_keys = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_get_keys, + .lookup_entry = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_lookup_entry, + } + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/nh-type", + .cbs = { + .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_nh_type_get_elem, + } + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/vrf", + .cbs = { + .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_vrf_get_elem, + } + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/gateway", + .cbs = { + .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_gateway_get_elem, + } + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/interface", + .cbs = { + .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_interface_get_elem, + } + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/bh-type", + .cbs = { + .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_bh_type_get_elem, + } + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/onlink", + .cbs = { + .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_onlink_get_elem, + } + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/srte-color", + .cbs = { + .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_color_get_elem, + } + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/mpls-label-stack/entry", + .cbs = { + .get_next = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_get_next, + .get_keys = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_get_keys, + .lookup_entry = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_lookup_entry, + } + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/mpls-label-stack/entry/id", + .cbs = { + .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_id_get_elem, + } + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/mpls-label-stack/entry/label", + .cbs = { + .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_label_get_elem, + } + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/mpls-label-stack/entry/ttl", + .cbs = { + .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_ttl_get_elem, + } + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/mpls-label-stack/entry/traffic-class", + .cbs = { + .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_traffic_class_get_elem, + } + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/duplicate", + .cbs = { + .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_duplicate_get_elem, + } + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/recursive", + .cbs = { + .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_recursive_get_elem, + } + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/active", + .cbs = { + .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_active_get_elem, + } + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/fib", + .cbs = { + .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_fib_get_elem, + } + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/weight", + .cbs = { + .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_weight_get_elem, + } + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/l3vni-id", + .cbs = { + .modify = lib_vrf_zebra_l3vni_id_modify, + .destroy = lib_vrf_zebra_l3vni_id_destroy, + } + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/prefix-only", + .cbs = { + .modify = lib_vrf_zebra_prefix_only_modify, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/match-condition/frr-zebra:ipv4-prefix-length", + .cbs = { + .modify = lib_route_map_entry_match_condition_ipv4_prefix_length_modify, + .destroy = lib_route_map_entry_match_condition_ipv4_prefix_length_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/match-condition/frr-zebra:ipv6-prefix-length", + .cbs = { + .modify = lib_route_map_entry_match_condition_ipv6_prefix_length_modify, + .destroy = lib_route_map_entry_match_condition_ipv6_prefix_length_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/match-condition/frr-zebra:source-protocol", + .cbs = { + .modify = lib_route_map_entry_match_condition_source_protocol_modify, + .destroy = lib_route_map_entry_match_condition_source_protocol_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/match-condition/frr-zebra:source-instance", + .cbs = { + .modify = lib_route_map_entry_match_condition_source_instance_modify, + .destroy = lib_route_map_entry_match_condition_source_instance_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/frr-zebra:source-v4", + .cbs = { + .modify = lib_route_map_entry_set_action_source_v4_modify, + .destroy = lib_route_map_entry_set_action_source_v4_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/frr-zebra:source-v6", + .cbs = { + .modify = lib_route_map_entry_set_action_source_v6_modify, + .destroy = lib_route_map_entry_set_action_source_v6_destroy, + } + }, + { + .xpath = NULL, + }, + } +}; diff --git a/zebra/zebra_nb.h b/zebra/zebra_nb.h new file mode 100644 index 0000000000..82529663ba --- /dev/null +++ b/zebra/zebra_nb.h @@ -0,0 +1,311 @@ +/* + * Copyright (C) 2020 Cumulus Networks, Inc. + * Chirag Shah + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef ZEBRA_ZEBRA_NB_H_ +#define ZEBRA_ZEBRA_NB_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +extern const struct frr_yang_module_info frr_zebra_info; + +/* prototypes */ +int get_route_information_rpc(struct nb_cb_rpc_args *args); +int get_v6_mroute_info_rpc(struct nb_cb_rpc_args *args); +int get_vrf_info_rpc(struct nb_cb_rpc_args *args); +int get_vrf_vni_info_rpc(struct nb_cb_rpc_args *args); +int get_evpn_info_rpc(struct nb_cb_rpc_args *args); +int get_vni_info_rpc(struct nb_cb_rpc_args *args); +int get_evpn_vni_rmac_rpc(struct nb_cb_rpc_args *args); +int get_evpn_vni_nexthops_rpc(struct nb_cb_rpc_args *args); +int clear_evpn_dup_addr_rpc(struct nb_cb_rpc_args *args); +int get_evpn_macs_rpc(struct nb_cb_rpc_args *args); +int get_evpn_arp_cache_rpc(struct nb_cb_rpc_args *args); +int get_pbr_ipset_rpc(struct nb_cb_rpc_args *args); +int get_pbr_iptable_rpc(struct nb_cb_rpc_args *args); +int get_debugs_rpc(struct nb_cb_rpc_args *args); +int zebra_mcast_rpf_lookup_modify(struct nb_cb_modify_args *args); +int zebra_ip_forwarding_modify(struct nb_cb_modify_args *args); +int zebra_ip_forwarding_destroy(struct nb_cb_destroy_args *args); +int zebra_ipv6_forwarding_modify(struct nb_cb_modify_args *args); +int zebra_ipv6_forwarding_destroy(struct nb_cb_destroy_args *args); +int zebra_workqueue_hold_timer_modify(struct nb_cb_modify_args *args); +int zebra_zapi_packets_modify(struct nb_cb_modify_args *args); +int zebra_import_kernel_table_table_id_modify(struct nb_cb_modify_args *args); +int zebra_import_kernel_table_table_id_destroy(struct nb_cb_destroy_args *args); +int zebra_import_kernel_table_distance_modify(struct nb_cb_modify_args *args); +int zebra_import_kernel_table_route_map_modify(struct nb_cb_modify_args *args); +int zebra_import_kernel_table_route_map_destroy( + struct nb_cb_destroy_args *args); +int zebra_allow_external_route_update_create(struct nb_cb_create_args *args); +int zebra_allow_external_route_update_destroy(struct nb_cb_destroy_args *args); +int zebra_dplane_queue_limit_modify(struct nb_cb_modify_args *args); +int zebra_debugs_debug_events_modify(struct nb_cb_modify_args *args); +int zebra_debugs_debug_events_destroy(struct nb_cb_destroy_args *args); +int zebra_debugs_debug_zapi_send_modify(struct nb_cb_modify_args *args); +int zebra_debugs_debug_zapi_send_destroy(struct nb_cb_destroy_args *args); +int zebra_debugs_debug_zapi_recv_modify(struct nb_cb_modify_args *args); +int zebra_debugs_debug_zapi_recv_destroy(struct nb_cb_destroy_args *args); +int zebra_debugs_debug_zapi_detail_modify(struct nb_cb_modify_args *args); +int zebra_debugs_debug_zapi_detail_destroy(struct nb_cb_destroy_args *args); +int zebra_debugs_debug_kernel_modify(struct nb_cb_modify_args *args); +int zebra_debugs_debug_kernel_destroy(struct nb_cb_destroy_args *args); +int zebra_debugs_debug_kernel_msg_send_modify(struct nb_cb_modify_args *args); +int zebra_debugs_debug_kernel_msg_send_destroy(struct nb_cb_destroy_args *args); +int zebra_debugs_debug_kernel_msg_recv_modify(struct nb_cb_modify_args *args); +int zebra_debugs_debug_kernel_msg_recv_destroy(struct nb_cb_destroy_args *args); +int zebra_debugs_debug_rib_modify(struct nb_cb_modify_args *args); +int zebra_debugs_debug_rib_destroy(struct nb_cb_destroy_args *args); +int zebra_debugs_debug_rib_detail_modify(struct nb_cb_modify_args *args); +int zebra_debugs_debug_rib_detail_destroy(struct nb_cb_destroy_args *args); +int zebra_debugs_debug_fpm_modify(struct nb_cb_modify_args *args); +int zebra_debugs_debug_fpm_destroy(struct nb_cb_destroy_args *args); +int zebra_debugs_debug_nht_modify(struct nb_cb_modify_args *args); +int zebra_debugs_debug_nht_destroy(struct nb_cb_destroy_args *args); +int zebra_debugs_debug_nht_detail_modify(struct nb_cb_modify_args *args); +int zebra_debugs_debug_nht_detail_destroy(struct nb_cb_destroy_args *args); +int zebra_debugs_debug_mpls_modify(struct nb_cb_modify_args *args); +int zebra_debugs_debug_mpls_destroy(struct nb_cb_destroy_args *args); +int zebra_debugs_debug_vxlan_modify(struct nb_cb_modify_args *args); +int zebra_debugs_debug_vxlan_destroy(struct nb_cb_destroy_args *args); +int zebra_debugs_debug_pw_modify(struct nb_cb_modify_args *args); +int zebra_debugs_debug_pw_destroy(struct nb_cb_destroy_args *args); +int zebra_debugs_debug_dplane_modify(struct nb_cb_modify_args *args); +int zebra_debugs_debug_dplane_destroy(struct nb_cb_destroy_args *args); +int zebra_debugs_debug_dplane_detail_modify(struct nb_cb_modify_args *args); +int zebra_debugs_debug_dplane_detail_destroy(struct nb_cb_destroy_args *args); +int zebra_debugs_debug_mlag_modify(struct nb_cb_modify_args *args); +int zebra_debugs_debug_mlag_destroy(struct nb_cb_destroy_args *args); +int lib_interface_zebra_ip_addrs_create(struct nb_cb_create_args *args); +int lib_interface_zebra_ip_addrs_destroy(struct nb_cb_destroy_args *args); +int lib_interface_zebra_ip_addrs_label_modify(struct nb_cb_modify_args *args); +int lib_interface_zebra_ip_addrs_label_destroy(struct nb_cb_destroy_args *args); +int lib_interface_zebra_ip_addrs_ip4_peer_modify( + struct nb_cb_modify_args *args); +int lib_interface_zebra_ip_addrs_ip4_peer_destroy( + struct nb_cb_destroy_args *args); +int lib_interface_zebra_multicast_modify(struct nb_cb_modify_args *args); +int lib_interface_zebra_multicast_destroy(struct nb_cb_destroy_args *args); +int lib_interface_zebra_link_detect_modify(struct nb_cb_modify_args *args); +int lib_interface_zebra_link_detect_destroy(struct nb_cb_destroy_args *args); +int lib_interface_zebra_shutdown_modify(struct nb_cb_modify_args *args); +int lib_interface_zebra_shutdown_destroy(struct nb_cb_destroy_args *args); +int lib_interface_zebra_bandwidth_modify(struct nb_cb_modify_args *args); +int lib_interface_zebra_bandwidth_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_match_condition_ipv4_prefix_length_modify( + struct nb_cb_modify_args *args); +int lib_route_map_entry_match_condition_ipv4_prefix_length_destroy( + struct nb_cb_destroy_args *args); +int lib_route_map_entry_match_condition_ipv6_prefix_length_modify( + struct nb_cb_modify_args *args); +int lib_route_map_entry_match_condition_ipv6_prefix_length_destroy( + struct nb_cb_destroy_args *args); +int lib_route_map_entry_match_condition_source_protocol_modify( + struct nb_cb_modify_args *args); +int lib_route_map_entry_match_condition_source_protocol_destroy( + struct nb_cb_destroy_args *args); +int lib_route_map_entry_match_condition_source_instance_modify( + struct nb_cb_modify_args *args); +int lib_route_map_entry_match_condition_source_instance_destroy( + struct nb_cb_destroy_args *args); +int lib_route_map_entry_set_action_source_v4_modify( + struct nb_cb_modify_args *args); +int lib_route_map_entry_set_action_source_v4_destroy( + struct nb_cb_destroy_args *args); +int lib_route_map_entry_set_action_source_v6_modify( + struct nb_cb_modify_args *args); +int lib_route_map_entry_set_action_source_v6_destroy( + struct nb_cb_destroy_args *args); +struct yang_data * +lib_interface_zebra_state_up_count_get_elem(struct nb_cb_get_elem_args *args); +struct yang_data * +lib_interface_zebra_state_down_count_get_elem(struct nb_cb_get_elem_args *args); +struct yang_data * +lib_interface_zebra_state_zif_type_get_elem(struct nb_cb_get_elem_args *args); +struct yang_data * +lib_interface_zebra_state_ptm_status_get_elem(struct nb_cb_get_elem_args *args); +struct yang_data * +lib_interface_zebra_state_vlan_id_get_elem(struct nb_cb_get_elem_args *args); +struct yang_data * +lib_interface_zebra_state_vni_id_get_elem(struct nb_cb_get_elem_args *args); +struct yang_data *lib_interface_zebra_state_remote_vtep_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data *lib_interface_zebra_state_mcast_group_get_elem( + struct nb_cb_get_elem_args *args); +int lib_vrf_zebra_ribs_rib_create(struct nb_cb_create_args *args); +int lib_vrf_zebra_ribs_rib_destroy(struct nb_cb_destroy_args *args); +const void *lib_vrf_zebra_ribs_rib_get_next(struct nb_cb_get_next_args *args); +int lib_vrf_zebra_ribs_rib_get_keys(struct nb_cb_get_keys_args *args); +const void * +lib_vrf_zebra_ribs_rib_lookup_entry(struct nb_cb_lookup_entry_args *args); +const void * +lib_vrf_zebra_ribs_rib_route_get_next(struct nb_cb_get_next_args *args); +int lib_vrf_zebra_ribs_rib_route_get_keys(struct nb_cb_get_keys_args *args); +const void * +lib_vrf_zebra_ribs_rib_route_lookup_entry(struct nb_cb_lookup_entry_args *args); +struct yang_data * +lib_vrf_zebra_ribs_rib_route_prefix_get_elem(struct nb_cb_get_elem_args *args); +struct yang_data *lib_vrf_zebra_ribs_rib_route_protocol_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data *lib_vrf_zebra_ribs_rib_route_protocol_v6_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +lib_vrf_zebra_ribs_rib_route_vrf_get_elem(struct nb_cb_get_elem_args *args); +struct yang_data *lib_vrf_zebra_ribs_rib_route_distance_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +lib_vrf_zebra_ribs_rib_route_metric_get_elem(struct nb_cb_get_elem_args *args); +struct yang_data * +lib_vrf_zebra_ribs_rib_route_tag_get_elem(struct nb_cb_get_elem_args *args); +struct yang_data *lib_vrf_zebra_ribs_rib_route_selected_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data *lib_vrf_zebra_ribs_rib_route_installed_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +lib_vrf_zebra_ribs_rib_route_failed_get_elem(struct nb_cb_get_elem_args *args); +struct yang_data * +lib_vrf_zebra_ribs_rib_route_queued_get_elem(struct nb_cb_get_elem_args *args); +struct yang_data *lib_vrf_zebra_ribs_rib_route_internal_flags_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data *lib_vrf_zebra_ribs_rib_route_internal_status_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +lib_vrf_zebra_ribs_rib_route_uptime_get_elem(struct nb_cb_get_elem_args *args); +const void *lib_vrf_zebra_ribs_rib_route_nexthop_group_get_next( + struct nb_cb_get_next_args *args); +int lib_vrf_zebra_ribs_rib_route_nexthop_group_get_keys( + struct nb_cb_get_keys_args *args); +const void *lib_vrf_zebra_ribs_rib_route_nexthop_group_lookup_entry( + struct nb_cb_lookup_entry_args *args); +struct yang_data *lib_vrf_zebra_ribs_rib_route_nexthop_group_name_get_elem( + struct nb_cb_get_elem_args *args); +const void * +lib_vrf_zebra_ribs_rib_route_nexthop_group_frr_nexthops_nexthop_get_next( + struct nb_cb_get_next_args *args); +int lib_vrf_zebra_ribs_rib_route_nexthop_group_frr_nexthops_nexthop_get_keys( + struct nb_cb_get_keys_args *args); +const void *lib_vrf_zebra_ribs_rib_route_route_entry_get_next( + struct nb_cb_get_next_args *args); +int lib_vrf_zebra_ribs_rib_route_route_entry_get_keys( + struct nb_cb_get_keys_args *args); +const void *lib_vrf_zebra_ribs_rib_route_route_entry_lookup_entry( + struct nb_cb_lookup_entry_args *args); +struct yang_data *lib_vrf_zebra_ribs_rib_route_route_entry_protocol_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data *lib_vrf_zebra_ribs_rib_route_route_entry_instance_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data *lib_vrf_zebra_ribs_rib_route_route_entry_distance_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data *lib_vrf_zebra_ribs_rib_route_route_entry_metric_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data *lib_vrf_zebra_ribs_rib_route_route_entry_tag_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data *lib_vrf_zebra_ribs_rib_route_route_entry_selected_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data *lib_vrf_zebra_ribs_rib_route_route_entry_installed_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data *lib_vrf_zebra_ribs_rib_route_route_entry_failed_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data *lib_vrf_zebra_ribs_rib_route_route_entry_queued_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +lib_vrf_zebra_ribs_rib_route_route_entry_internal_flags_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +lib_vrf_zebra_ribs_rib_route_route_entry_internal_status_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data *lib_vrf_zebra_ribs_rib_route_route_entry_uptime_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_id_get_elem( + struct nb_cb_get_elem_args *args); +const void * +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_get_next( + struct nb_cb_get_next_args *args); +int lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_get_keys( + struct nb_cb_get_keys_args *args); +const void * +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_lookup_entry( + struct nb_cb_lookup_entry_args *args); +struct yang_data * +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_nh_type_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_vrf_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_gateway_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_interface_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_bh_type_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_onlink_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_color_get_elem( + struct nb_cb_get_elem_args *args); +const void * +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_get_next( + struct nb_cb_get_next_args *args); +int lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_get_keys( + struct nb_cb_get_keys_args *args); +const void * +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_lookup_entry( + struct nb_cb_lookup_entry_args *args); +struct yang_data * +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_id_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_label_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_ttl_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_traffic_class_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_duplicate_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_recursive_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_active_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_fib_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_weight_get_elem( + struct nb_cb_get_elem_args *args); +int lib_vrf_zebra_l3vni_id_modify(struct nb_cb_modify_args *args); +int lib_vrf_zebra_l3vni_id_destroy(struct nb_cb_destroy_args *args); +int lib_vrf_zebra_prefix_only_modify(struct nb_cb_modify_args *args); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/zebra/zebra_nb_config.c b/zebra/zebra_nb_config.c new file mode 100644 index 0000000000..932825159b --- /dev/null +++ b/zebra/zebra_nb_config.c @@ -0,0 +1,1656 @@ +/* + * Copyright (C) 2019 Cumulus Networks, Inc. + * Chirag Shah + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "lib/log.h" +#include "lib/northbound.h" +#include "libfrr.h" +#include "lib/command.h" +#include "lib/routemap.h" +#include "zebra/zebra_nb.h" +#include "zebra/rib.h" +#include "zebra_nb.h" +#include "zebra/interface.h" +#include "zebra/connected.h" +#include "zebra/zebra_router.h" +#include "zebra/debug.h" +#include "zebra/zebra_vxlan_private.h" +#include "zebra/zebra_vxlan.h" + +/* + * XPath: /frr-zebra:zebra/mcast-rpf-lookup + */ +int zebra_mcast_rpf_lookup_modify(struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-zebra:zebra/ip-forwarding + */ +int zebra_ip_forwarding_modify(struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +int zebra_ip_forwarding_destroy(struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-zebra:zebra/ipv6-forwarding + */ +int zebra_ipv6_forwarding_modify(struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +int zebra_ipv6_forwarding_destroy(struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-zebra:zebra/workqueue-hold-timer + */ +int zebra_workqueue_hold_timer_modify(struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-zebra:zebra/zapi-packets + */ +int zebra_zapi_packets_modify(struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-zebra:zebra/import-kernel-table/table-id + */ +int zebra_import_kernel_table_table_id_modify(struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +int zebra_import_kernel_table_table_id_destroy(struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-zebra:zebra/import-kernel-table/distance + */ +int zebra_import_kernel_table_distance_modify(struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-zebra:zebra/import-kernel-table/route-map + */ +int zebra_import_kernel_table_route_map_modify(struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +int zebra_import_kernel_table_route_map_destroy(struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-zebra:zebra/allow-external-route-update + */ +int zebra_allow_external_route_update_create(struct nb_cb_create_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +int zebra_allow_external_route_update_destroy(struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-zebra:zebra/dplane-queue-limit + */ +int zebra_dplane_queue_limit_modify(struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-zebra:zebra/debugs/debug-events + */ +int zebra_debugs_debug_events_modify(struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +int zebra_debugs_debug_events_destroy(struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-zebra:zebra/debugs/debug-zapi-send + */ +int zebra_debugs_debug_zapi_send_modify(struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +int zebra_debugs_debug_zapi_send_destroy(struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-zebra:zebra/debugs/debug-zapi-recv + */ +int zebra_debugs_debug_zapi_recv_modify(struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +int zebra_debugs_debug_zapi_recv_destroy(struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-zebra:zebra/debugs/debug-zapi-detail + */ +int zebra_debugs_debug_zapi_detail_modify(struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +int zebra_debugs_debug_zapi_detail_destroy(struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-zebra:zebra/debugs/debug-kernel + */ +int zebra_debugs_debug_kernel_modify(struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +int zebra_debugs_debug_kernel_destroy(struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-zebra:zebra/debugs/debug-kernel-msg-send + */ +int zebra_debugs_debug_kernel_msg_send_modify(struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +int zebra_debugs_debug_kernel_msg_send_destroy(struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-zebra:zebra/debugs/debug-kernel-msg-recv + */ +int zebra_debugs_debug_kernel_msg_recv_modify(struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +int zebra_debugs_debug_kernel_msg_recv_destroy(struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-zebra:zebra/debugs/debug-rib + */ +int zebra_debugs_debug_rib_modify(struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +int zebra_debugs_debug_rib_destroy(struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-zebra:zebra/debugs/debug-rib-detail + */ +int zebra_debugs_debug_rib_detail_modify(struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +int zebra_debugs_debug_rib_detail_destroy(struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-zebra:zebra/debugs/debug-fpm + */ +int zebra_debugs_debug_fpm_modify(struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +int zebra_debugs_debug_fpm_destroy(struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-zebra:zebra/debugs/debug-nht + */ +int zebra_debugs_debug_nht_modify(struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +int zebra_debugs_debug_nht_destroy(struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-zebra:zebra/debugs/debug-nht-detail + */ +int zebra_debugs_debug_nht_detail_modify(struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +int zebra_debugs_debug_nht_detail_destroy(struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-zebra:zebra/debugs/debug-mpls + */ +int zebra_debugs_debug_mpls_modify(struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +int zebra_debugs_debug_mpls_destroy(struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-zebra:zebra/debugs/debug-vxlan + */ +int zebra_debugs_debug_vxlan_modify(struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +int zebra_debugs_debug_vxlan_destroy(struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-zebra:zebra/debugs/debug-pw + */ +int zebra_debugs_debug_pw_modify(struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +int zebra_debugs_debug_pw_destroy(struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-zebra:zebra/debugs/debug-dplane + */ +int zebra_debugs_debug_dplane_modify(struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +int zebra_debugs_debug_dplane_destroy(struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-zebra:zebra/debugs/debug-dplane-detail + */ +int zebra_debugs_debug_dplane_detail_modify(struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +int zebra_debugs_debug_dplane_detail_destroy(struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-zebra:zebra/debugs/debug-mlag + */ +int zebra_debugs_debug_mlag_modify(struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +int zebra_debugs_debug_mlag_destroy(struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/ip-addrs + */ +int lib_interface_zebra_ip_addrs_create(struct nb_cb_create_args *args) +{ + struct interface *ifp; + struct prefix prefix; + char buf[PREFIX_STRLEN] = {0}; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + // addr_family = yang_dnode_get_enum(dnode, "./address-family"); + yang_dnode_get_prefix(&prefix, args->dnode, "./ip-prefix"); + apply_mask(&prefix); + + switch (args->event) { + case NB_EV_VALIDATE: + if (prefix.family == AF_INET + && ipv4_martian(&prefix.u.prefix4)) { + snprintf(args->errmsg, args->errmsg_len, + "invalid address %s", + prefix2str(&prefix, buf, sizeof(buf))); + return NB_ERR_VALIDATION; + } else if (prefix.family == AF_INET6 + && ipv6_martian(&prefix.u.prefix6)) { + snprintf(args->errmsg, args->errmsg_len, + "invalid address %s", + prefix2str(&prefix, buf, sizeof(buf))); + return NB_ERR_VALIDATION; + } + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + if (prefix.family == AF_INET) + if_ip_address_install(ifp, &prefix, NULL, NULL); + else if (prefix.family == AF_INET6) + if_ipv6_address_install(ifp, &prefix, NULL); + + break; + } + + return NB_OK; +} + +int lib_interface_zebra_ip_addrs_destroy(struct nb_cb_destroy_args *args) +{ + struct interface *ifp; + struct prefix prefix; + struct connected *ifc; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + yang_dnode_get_prefix(&prefix, args->dnode, "./ip-prefix"); + apply_mask(&prefix); + + switch (args->event) { + case NB_EV_VALIDATE: + if (prefix.family == AF_INET) { + /* Check current interface address. */ + ifc = connected_check_ptp(ifp, &prefix, NULL); + if (!ifc) { + snprintf(args->errmsg, args->errmsg_len, + "interface %s Can't find address\n", + ifp->name); + return NB_ERR_VALIDATION; + } + } else if (prefix.family == AF_INET6) { + /* Check current interface address. */ + ifc = connected_check(ifp, &prefix); + if (!ifc) { + snprintf(args->errmsg, args->errmsg_len, + "interface can't find address %s", + ifp->name); + return NB_ERR_VALIDATION; + } + } else + return NB_ERR_VALIDATION; + + /* This is not configured address. */ + if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_CONFIGURED)) { + snprintf(args->errmsg, args->errmsg_len, + "interface %s not configured", ifp->name); + return NB_ERR_VALIDATION; + } + + /* This is not real address or interface is not active. */ + if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_QUEUED) + || !CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE)) { + listnode_delete(ifp->connected, ifc); + connected_free(&ifc); + return NB_ERR_VALIDATION; + } + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + if_ip_address_uinstall(ifp, &prefix); + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/ip-addrs/label + */ +int lib_interface_zebra_ip_addrs_label_modify(struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +int lib_interface_zebra_ip_addrs_label_destroy(struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/ip-addrs/ip4-peer + */ +int lib_interface_zebra_ip_addrs_ip4_peer_modify(struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +int lib_interface_zebra_ip_addrs_ip4_peer_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/multicast + */ +int lib_interface_zebra_multicast_modify(struct nb_cb_modify_args *args) +{ + if (args->event != NB_EV_APPLY) + return NB_OK; + + struct interface *ifp; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + + if_multicast_set(ifp); + + return NB_OK; +} + +int lib_interface_zebra_multicast_destroy(struct nb_cb_destroy_args *args) +{ + if (args->event != NB_EV_APPLY) + return NB_OK; + + struct interface *ifp; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + + if_multicast_unset(ifp); + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/link-detect + */ +int lib_interface_zebra_link_detect_modify(struct nb_cb_modify_args *args) +{ + if (args->event != NB_EV_APPLY) + return NB_OK; + + struct interface *ifp; + bool link_detect; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + link_detect = yang_dnode_get_bool(args->dnode, "./link-detect"); + + if_linkdetect(ifp, link_detect); + + return NB_OK; +} + +int lib_interface_zebra_link_detect_destroy(struct nb_cb_destroy_args *args) +{ + if (args->event != NB_EV_APPLY) + return NB_OK; + + struct interface *ifp; + bool link_detect; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + link_detect = yang_dnode_get_bool(args->dnode, "./link-detect"); + + if_linkdetect(ifp, link_detect); + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/shutdown + */ +int lib_interface_zebra_shutdown_modify(struct nb_cb_modify_args *args) +{ + struct interface *ifp; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + + if_shutdown(ifp); + + return NB_OK; +} + +int lib_interface_zebra_shutdown_destroy(struct nb_cb_destroy_args *args) +{ + struct interface *ifp; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + + if_no_shutdown(ifp); + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/bandwidth + */ +int lib_interface_zebra_bandwidth_modify(struct nb_cb_modify_args *args) +{ + if (args->event != NB_EV_APPLY) + return NB_OK; + + struct interface *ifp; + uint32_t bandwidth; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + bandwidth = yang_dnode_get_uint32(args->dnode, "./bandwidth"); + + ifp->bandwidth = bandwidth; + + /* force protocols to recalculate routes due to cost change */ + if (if_is_operative(ifp)) + zebra_interface_up_update(ifp); + + return NB_OK; +} + +int lib_interface_zebra_bandwidth_destroy(struct nb_cb_destroy_args *args) +{ + if (args->event != NB_EV_APPLY) + return NB_OK; + + struct interface *ifp; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + + ifp->bandwidth = 0; + + /* force protocols to recalculate routes due to cost change */ + if (if_is_operative(ifp)) + zebra_interface_up_update(ifp); + + return NB_OK; +} + +/* + * XPath: /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib + */ +int lib_vrf_zebra_ribs_rib_create(struct nb_cb_create_args *args) +{ + struct vrf *vrf; + afi_t afi; + safi_t safi; + struct zebra_vrf *zvrf; + struct zebra_router_table *zrt; + uint32_t table_id; + const char *afi_safi_name; + + vrf = nb_running_get_entry(args->dnode, NULL, false); + zvrf = vrf_info_lookup(vrf->vrf_id); + table_id = yang_dnode_get_uint32(args->dnode, "./table-id"); + if (!table_id) + table_id = zvrf->table_id; + + afi_safi_name = yang_dnode_get_string(args->dnode, "./afi-safi-name"); + yang_afi_safi_identity2value(afi_safi_name, &afi, &safi); + + zrt = zebra_router_find_zrt(zvrf, table_id, afi, safi); + + switch (args->event) { + case NB_EV_VALIDATE: + if (!zrt) { + snprintf(args->errmsg, args->errmsg_len, + "vrf %s table is not found.", vrf->name); + return NB_ERR_VALIDATION; + } + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + + nb_running_set_entry(args->dnode, zrt); + + break; + } + + return NB_OK; +} + +int lib_vrf_zebra_ribs_rib_destroy(struct nb_cb_destroy_args *args) +{ + if (args->event != NB_EV_APPLY) + return NB_OK; + + nb_running_unset_entry(args->dnode); + + return NB_OK; +} + +/* + * XPath: /frr-vrf:lib/vrf/frr-zebra:zebra/l3vni-id + */ +int lib_vrf_zebra_l3vni_id_modify(struct nb_cb_modify_args *args) +{ + struct vrf *vrf; + struct zebra_vrf *zvrf; + vni_t vni = 0; + zebra_l3vni_t *zl3vni = NULL; + struct zebra_vrf *zvrf_evpn = NULL; + char err[ERR_STR_SZ]; + bool pfx_only = false; + const struct lyd_node *pn_dnode; + const char *vrfname; + + switch (args->event) { + case NB_EV_PREPARE: + case NB_EV_ABORT: + return NB_OK; + case NB_EV_VALIDATE: + zvrf_evpn = zebra_vrf_get_evpn(); + if (!zvrf_evpn) { + snprintf(args->errmsg, args->errmsg_len, + "evpn vrf is not present."); + return NB_ERR_VALIDATION; + } + vni = yang_dnode_get_uint32(args->dnode, NULL); + /* Get vrf info from parent node, reject configuration + * if zebra vrf already mapped to different vni id. + */ + pn_dnode = yang_dnode_get_parent(args->dnode, "vrf"); + if (pn_dnode) { + vrfname = yang_dnode_get_string(pn_dnode, "./name"); + zvrf = zebra_vrf_lookup_by_name(vrfname); + if (!zvrf) { + snprintf(args->errmsg, args->errmsg_len, + "zebra vrf info not found for vrf:%s.", + vrfname); + return NB_ERR_VALIDATION; + } + if (zvrf->l3vni && zvrf->l3vni != vni) { + snprintf( + args->errmsg, args->errmsg_len, + "vni %u cannot be configured as vni %u is already configured under the vrf", + vni, zvrf->l3vni); + return NB_ERR_VALIDATION; + } + } + + /* Check if this VNI is already present in the system */ + zl3vni = zl3vni_lookup(vni); + if (zl3vni) { + snprintf(args->errmsg, args->errmsg_len, + "VNI %u is already configured as L3-VNI", vni); + return NB_ERR_VALIDATION; + } + + break; + case NB_EV_APPLY: + + vrf = nb_running_get_entry(args->dnode, NULL, true); + zvrf = zebra_vrf_lookup_by_name(vrf->name); + vni = yang_dnode_get_uint32(args->dnode, NULL); + /* Note: This covers lib_vrf_zebra_prefix_only_modify() config + * along with l3vni config + */ + pfx_only = yang_dnode_get_bool(args->dnode, "../prefix-only"); + + if (zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, + pfx_only ? 1 : 0, 1) + != 0) { + if (IS_ZEBRA_DEBUG_VXLAN) + snprintf( + args->errmsg, args->errmsg_len, + "vrf vni %u mapping failed with error: %s", + vni, err); + return NB_ERR; + } + + break; + } + + return NB_OK; +} + +int lib_vrf_zebra_l3vni_id_destroy(struct nb_cb_destroy_args *args) +{ + struct vrf *vrf; + struct zebra_vrf *zvrf; + vni_t vni = 0; + char err[ERR_STR_SZ]; + uint8_t filter = 0; + + switch (args->event) { + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_VALIDATE: + return NB_OK; + case NB_EV_APPLY: + vrf = nb_running_get_entry(args->dnode, NULL, true); + zvrf = zebra_vrf_lookup_by_name(vrf->name); + vni = yang_dnode_get_uint32(args->dnode, NULL); + + if (!zl3vni_lookup(vni)) + return NB_OK; + + if (zvrf->l3vni != vni) { + snprintf(args->errmsg, args->errmsg_len, + "vrf %s has different vni %u mapped", + vrf->name, zvrf->l3vni); + return NB_ERR; + } + + if (is_l3vni_for_prefix_routes_only(zvrf->l3vni)) + filter = 1; + + if (zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, + filter, 0) + != 0) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "vrf vni %u unmapping failed with error: %s", + vni, err); + return NB_ERR; + } + + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-vrf:lib/vrf/frr-zebra:zebra/prefix-only + */ +int lib_vrf_zebra_prefix_only_modify(struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +/* + * XPath: + * /frr-route-map:lib/route-map/entry/match-condition/frr-zebra:ipv4-prefix-length + */ +int lib_route_map_entry_match_condition_ipv4_prefix_length_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *length; + int condition, rv; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + length = yang_dnode_get_string(args->dnode, NULL); + condition = + yang_dnode_get_enum(args->dnode, "../frr-route-map:condition"); + + /* Set destroy information. */ + switch (condition) { + case 100: /* ipv4-prefix-length */ + rhc->rhc_rule = "ip address prefix-len"; + break; + + case 102: /* ipv4-next-hop-prefix-length */ + rhc->rhc_rule = "ip next-hop prefix-len"; + break; + } + rhc->rhc_mhook = generic_match_delete; + rhc->rhc_event = RMAP_EVENT_MATCH_DELETED; + + rv = generic_match_add(NULL, rhc->rhc_rmi, rhc->rhc_rule, length, + RMAP_EVENT_MATCH_ADDED); + if (rv != CMD_SUCCESS) { + rhc->rhc_mhook = NULL; + return NB_ERR_INCONSISTENCY; + } + + return NB_OK; +} + +int lib_route_map_entry_match_condition_ipv4_prefix_length_destroy( + struct nb_cb_destroy_args *args) +{ + return lib_route_map_entry_match_destroy(args); +} + +/* + * XPath: + * /frr-route-map:lib/route-map/entry/match-condition/frr-zebra:ipv6-prefix-length + */ +int lib_route_map_entry_match_condition_ipv6_prefix_length_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *length; + int rv; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + length = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_mhook = generic_match_delete; + rhc->rhc_rule = "ipv6 address prefix-len"; + rhc->rhc_event = RMAP_EVENT_MATCH_DELETED; + + rv = generic_match_add(NULL, rhc->rhc_rmi, "ipv6 address prefix-len", + length, RMAP_EVENT_MATCH_ADDED); + if (rv != CMD_SUCCESS) { + rhc->rhc_mhook = NULL; + return NB_ERR_INCONSISTENCY; + } + + return NB_OK; +} + +int lib_route_map_entry_match_condition_ipv6_prefix_length_destroy( + struct nb_cb_destroy_args *args) +{ + return lib_route_map_entry_match_destroy(args); +} + +/* + * XPath: + * /frr-route-map:lib/route-map/entry/match-condition/frr-zebra:source-protocol + */ +int lib_route_map_entry_match_condition_source_protocol_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *type; + int rv; + + switch (args->event) { + case NB_EV_VALIDATE: + type = yang_dnode_get_string(args->dnode, NULL); + if (proto_name2num(type) == -1) { + snprintf(args->errmsg, args->errmsg_len, + "invalid protocol: %s", type); + return NB_ERR_VALIDATION; + } + return NB_OK; + case NB_EV_PREPARE: + case NB_EV_ABORT: + return NB_OK; + case NB_EV_APPLY: + /* NOTHING */ + break; + } + + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + type = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_mhook = generic_match_delete; + rhc->rhc_rule = "source-protocol"; + rhc->rhc_event = RMAP_EVENT_MATCH_DELETED; + + rv = generic_match_add(NULL, rhc->rhc_rmi, "source-protocol", type, + RMAP_EVENT_MATCH_ADDED); + if (rv != CMD_SUCCESS) { + rhc->rhc_mhook = NULL; + return NB_ERR_INCONSISTENCY; + } + + return NB_OK; +} + +int lib_route_map_entry_match_condition_source_protocol_destroy( + struct nb_cb_destroy_args *args) +{ + return lib_route_map_entry_match_destroy(args); +} + +/* + * XPath: + * /frr-route-map:lib/route-map/entry/match-condition/frr-zebra:source-instance + */ +int lib_route_map_entry_match_condition_source_instance_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *type; + int rv; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + type = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_mhook = generic_match_delete; + rhc->rhc_rule = "source-instance"; + rhc->rhc_event = RMAP_EVENT_MATCH_DELETED; + + rv = generic_match_add(NULL, rhc->rhc_rmi, "source-instance", type, + RMAP_EVENT_MATCH_ADDED); + if (rv != CMD_SUCCESS) { + rhc->rhc_mhook = NULL; + return NB_ERR_INCONSISTENCY; + } + + return NB_OK; +} + +int lib_route_map_entry_match_condition_source_instance_destroy( + struct nb_cb_destroy_args *args) +{ + return lib_route_map_entry_match_destroy(args); +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/set-action/frr-zebra:source-v4 + */ +int lib_route_map_entry_set_action_source_v4_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + struct interface *pif = NULL; + const char *source; + struct vrf *vrf; + struct prefix p; + int rv; + + switch (args->event) { + case NB_EV_VALIDATE: + memset(&p, 0, sizeof(p)); + yang_dnode_get_ipv4p(&p, args->dnode, NULL); + if (zebra_check_addr(&p) == 0) { + snprintf(args->errmsg, args->errmsg_len, + "invalid IPv4 address: %s", + yang_dnode_get_string(args->dnode, NULL)); + return NB_ERR_VALIDATION; + } + + RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) { + pif = if_lookup_exact_address(&p.u.prefix4, AF_INET, + vrf->vrf_id); + if (pif != NULL) + break; + } + /* + * On startup the local address *may* not have come up + * yet. We need to allow startup configuration of + * set src or we are fudged. Log it for future fun + */ + if (pif == NULL) + zlog_warn("set src %pI4 is not a local address", + &p.u.prefix4); + return NB_OK; + case NB_EV_PREPARE: + case NB_EV_ABORT: + return NB_OK; + case NB_EV_APPLY: + /* NOTHING */ + break; + } + + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + source = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_shook = generic_set_delete; + rhc->rhc_rule = "src"; + + rv = generic_set_add(NULL, rhc->rhc_rmi, "src", source); + if (rv != CMD_SUCCESS) { + rhc->rhc_shook = NULL; + return NB_ERR_INCONSISTENCY; + } + + return NB_OK; +} + +int lib_route_map_entry_set_action_source_v4_destroy( + struct nb_cb_destroy_args *args) +{ + return lib_route_map_entry_set_destroy(args); +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/set-action/frr-zebra:source-v6 + */ +int lib_route_map_entry_set_action_source_v6_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + struct interface *pif = NULL; + const char *source; + struct vrf *vrf; + struct prefix p; + int rv; + + switch (args->event) { + case NB_EV_VALIDATE: + memset(&p, 0, sizeof(p)); + yang_dnode_get_ipv6p(&p, args->dnode, NULL); + if (zebra_check_addr(&p) == 0) { + snprintf(args->errmsg, args->errmsg_len, + "invalid IPv6 address: %s", + yang_dnode_get_string(args->dnode, NULL)); + return NB_ERR_VALIDATION; + } + + RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) { + pif = if_lookup_exact_address(&p.u.prefix6, AF_INET6, + vrf->vrf_id); + if (pif != NULL) + break; + } + /* + * On startup the local address *may* not have come up + * yet. We need to allow startup configuration of + * set src or we are fudged. Log it for future fun + */ + if (pif == NULL) + zlog_warn("set src %pI6 is not a local address", + &p.u.prefix6); + return NB_OK; + case NB_EV_PREPARE: + case NB_EV_ABORT: + return NB_OK; + case NB_EV_APPLY: + /* NOTHING */ + break; + } + + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + source = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_shook = generic_set_delete; + rhc->rhc_rule = "src"; + + rv = generic_set_add(NULL, rhc->rhc_rmi, "src", source); + if (rv != CMD_SUCCESS) { + rhc->rhc_shook = NULL; + return NB_ERR_INCONSISTENCY; + } + + return NB_OK; +} + +int lib_route_map_entry_set_action_source_v6_destroy( + struct nb_cb_destroy_args *args) +{ + return lib_route_map_entry_set_destroy(args); +} diff --git a/zebra/zebra_nb_rpcs.c b/zebra/zebra_nb_rpcs.c new file mode 100644 index 0000000000..204cc5da6d --- /dev/null +++ b/zebra/zebra_nb_rpcs.c @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2020 Cumulus Networks, Inc. + * Chirag Shah + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include "northbound.h" +#include "libfrr.h" + +#include "zebra/zebra_nb.h" +#include "zebra/zebra_router.h" +#include "zebra/zebra_vrf.h" +#include "zebra/zebra_vxlan.h" + +/* + * XPath: /frr-zebra:clear-evpn-dup-addr + */ +int clear_evpn_dup_addr_rpc(struct nb_cb_rpc_args *args) +{ + struct zebra_vrf *zvrf; + int ret = NB_OK; + struct yang_data *yang_dup_choice = NULL, *yang_dup_vni = NULL, + *yang_dup_ip = NULL, *yang_dup_mac = NULL; + + yang_dup_choice = yang_data_list_find(args->input, "%s/%s", args->xpath, + "input/clear-dup-choice"); + + zvrf = zebra_vrf_get_evpn(); + + if (yang_dup_choice + && strcmp(yang_dup_choice->value, "all-case") == 0) { + zebra_vxlan_clear_dup_detect_vni_all(zvrf); + } else { + vni_t vni; + struct ipaddr host_ip = {.ipa_type = IPADDR_NONE}; + struct ethaddr mac; + + yang_dup_vni = yang_data_list_find( + args->input, "%s/%s", args->xpath, + "input/clear-dup-choice/single-case/vni-id"); + if (yang_dup_vni) { + vni = yang_str2uint32(yang_dup_vni->value); + + yang_dup_mac = yang_data_list_find( + args->input, "%s/%s", args->xpath, + "input/clear-dup-choice/single-case/vni-id/mac-addr"); + yang_dup_ip = yang_data_list_find( + args->input, "%s/%s", args->xpath, + "input/clear-dup-choice/single-case/vni-id/vni-ipaddr"); + + if (yang_dup_mac) { + yang_str2mac(yang_dup_mac->value, &mac); + ret = zebra_vxlan_clear_dup_detect_vni_mac( + zvrf, vni, &mac); + } else if (yang_dup_ip) { + yang_str2ip(yang_dup_ip->value, &host_ip); + ret = zebra_vxlan_clear_dup_detect_vni_ip( + zvrf, vni, &host_ip); + } else + ret = zebra_vxlan_clear_dup_detect_vni(zvrf, + vni); + } + } + ret = (ret != CMD_SUCCESS) ? NB_ERR : NB_OK; + + return ret; +} + +/* + * XPath: /frr-zebra:get-route-information + */ +int get_route_information_rpc(struct nb_cb_rpc_args *args) +{ + /* TODO: implement me. */ + return NB_ERR_NOT_FOUND; +} + +/* + * XPath: /frr-zebra:get-v6-mroute-info + */ +int get_v6_mroute_info_rpc(struct nb_cb_rpc_args *args) +{ + /* TODO: implement me. */ + return NB_ERR_NOT_FOUND; +} + +/* + * XPath: /frr-zebra:get-vrf-info + */ +int get_vrf_info_rpc(struct nb_cb_rpc_args *args) +{ + /* TODO: implement me. */ + return NB_ERR_NOT_FOUND; +} + +/* + * XPath: /frr-zebra:get-vrf-vni-info + */ +int get_vrf_vni_info_rpc(struct nb_cb_rpc_args *args) +{ + /* TODO: implement me. */ + return NB_ERR_NOT_FOUND; +} + +/* + * XPath: /frr-zebra:get-evpn-info + */ +int get_evpn_info_rpc(struct nb_cb_rpc_args *args) +{ + /* TODO: implement me. */ + return NB_ERR_NOT_FOUND; +} + +/* + * XPath: /frr-zebra:get-vni-info + */ +int get_vni_info_rpc(struct nb_cb_rpc_args *args) +{ + /* TODO: implement me. */ + return NB_ERR_NOT_FOUND; +} + +/* + * XPath: /frr-zebra:get-evpn-vni-rmac + */ +int get_evpn_vni_rmac_rpc(struct nb_cb_rpc_args *args) +{ + /* TODO: implement me. */ + return NB_ERR_NOT_FOUND; +} + +/* + * XPath: /frr-zebra:get-evpn-vni-nexthops + */ +int get_evpn_vni_nexthops_rpc(struct nb_cb_rpc_args *args) +{ + /* TODO: implement me. */ + return NB_ERR_NOT_FOUND; +} + +/* + * XPath: /frr-zebra:get-evpn-macs + */ +int get_evpn_macs_rpc(struct nb_cb_rpc_args *args) +{ + /* TODO: implement me. */ + return NB_ERR_NOT_FOUND; +} + +/* + * XPath: /frr-zebra:get-evpn-arp-cache + */ +int get_evpn_arp_cache_rpc(struct nb_cb_rpc_args *args) +{ + /* TODO: implement me. */ + return NB_ERR_NOT_FOUND; +} + +/* + * XPath: /frr-zebra:get-pbr-ipset + */ +int get_pbr_ipset_rpc(struct nb_cb_rpc_args *args) +{ + /* TODO: implement me. */ + return NB_ERR_NOT_FOUND; +} + +/* + * XPath: /frr-zebra:get-pbr-iptable + */ +int get_pbr_iptable_rpc(struct nb_cb_rpc_args *args) +{ + /* TODO: implement me. */ + return NB_ERR_NOT_FOUND; +} + +/* + * XPath: /frr-zebra:get-debugs + */ +int get_debugs_rpc(struct nb_cb_rpc_args *args) +{ + /* TODO: implement me. */ + return NB_ERR_NOT_FOUND; +} diff --git a/zebra/zebra_nb_state.c b/zebra/zebra_nb_state.c new file mode 100644 index 0000000000..21c89f64ed --- /dev/null +++ b/zebra/zebra_nb_state.c @@ -0,0 +1,978 @@ +/* + * Copyright (C) 2020 Cumulus Networks, Inc. + * Chirag Shah + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include "northbound.h" +#include "libfrr.h" +#include "zebra_nb.h" +#include "zebra/interface.h" +#include "zebra/zebra_router.h" +#include "zebra/debug.h" +#include "printfrr.h" + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/state/up-count + */ +struct yang_data * +lib_interface_zebra_state_up_count_get_elem(struct nb_cb_get_elem_args *args) +{ + const struct interface *ifp = args->list_entry; + struct zebra_if *zebra_if; + + zebra_if = ifp->info; + + return yang_data_new_uint16(args->xpath, zebra_if->up_count); +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/state/down-count + */ +struct yang_data * +lib_interface_zebra_state_down_count_get_elem(struct nb_cb_get_elem_args *args) +{ + const struct interface *ifp = args->list_entry; + struct zebra_if *zebra_if; + + zebra_if = ifp->info; + + return yang_data_new_uint16(args->xpath, zebra_if->down_count); +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/state/zif-type + */ +struct yang_data * +lib_interface_zebra_state_zif_type_get_elem(struct nb_cb_get_elem_args *args) +{ + /* TODO: implement me. */ + return NULL; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/state/ptm-status + */ +struct yang_data * +lib_interface_zebra_state_ptm_status_get_elem(struct nb_cb_get_elem_args *args) +{ + /* TODO: implement me. */ + return NULL; +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/state/vlan-id + */ +struct yang_data * +lib_interface_zebra_state_vlan_id_get_elem(struct nb_cb_get_elem_args *args) +{ + const struct interface *ifp = args->list_entry; + struct zebra_if *zebra_if; + struct zebra_l2info_vlan *vlan_info; + + if (!IS_ZEBRA_IF_VLAN(ifp)) + return NULL; + + zebra_if = ifp->info; + vlan_info = &zebra_if->l2info.vl; + + return yang_data_new_uint16(args->xpath, vlan_info->vid); +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/state/vni-id + */ +struct yang_data * +lib_interface_zebra_state_vni_id_get_elem(struct nb_cb_get_elem_args *args) +{ + const struct interface *ifp = args->list_entry; + struct zebra_if *zebra_if; + struct zebra_l2info_vxlan *vxlan_info; + + if (!IS_ZEBRA_IF_VXLAN(ifp)) + return NULL; + + zebra_if = ifp->info; + vxlan_info = &zebra_if->l2info.vxl; + + return yang_data_new_uint32(args->xpath, vxlan_info->vni); +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/state/remote-vtep + */ +struct yang_data * +lib_interface_zebra_state_remote_vtep_get_elem(struct nb_cb_get_elem_args *args) +{ + const struct interface *ifp = args->list_entry; + struct zebra_if *zebra_if; + struct zebra_l2info_vxlan *vxlan_info; + + if (!IS_ZEBRA_IF_VXLAN(ifp)) + return NULL; + + zebra_if = ifp->info; + vxlan_info = &zebra_if->l2info.vxl; + + return yang_data_new_ipv4(args->xpath, &vxlan_info->vtep_ip); +} + +/* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/state/mcast-group + */ +struct yang_data * +lib_interface_zebra_state_mcast_group_get_elem(struct nb_cb_get_elem_args *args) +{ + const struct interface *ifp = args->list_entry; + struct zebra_if *zebra_if; + struct zebra_l2info_vxlan *vxlan_info; + + if (!IS_ZEBRA_IF_VXLAN(ifp)) + return NULL; + + zebra_if = ifp->info; + vxlan_info = &zebra_if->l2info.vxl; + + return yang_data_new_ipv4(args->xpath, &vxlan_info->mcast_grp); +} + +const void *lib_vrf_zebra_ribs_rib_get_next(struct nb_cb_get_next_args *args) +{ + struct vrf *vrf = (struct vrf *)args->parent_list_entry; + struct zebra_router_table *zrt = + (struct zebra_router_table *)args->list_entry; + + struct zebra_vrf *zvrf; + afi_t afi; + safi_t safi; + + zvrf = zebra_vrf_lookup_by_id(vrf->vrf_id); + + if (args->list_entry == NULL) { + afi = AFI_IP; + safi = SAFI_UNICAST; + + zrt = zebra_router_find_zrt(zvrf, zvrf->table_id, afi, safi); + if (zrt == NULL) + return NULL; + } else { + zrt = RB_NEXT(zebra_router_table_head, zrt); + /* vrf_id/ns_id do not match, only walk for the given VRF */ + while (zrt && zrt->ns_id != zvrf->zns->ns_id) + zrt = RB_NEXT(zebra_router_table_head, zrt); + } + + return zrt; +} + +int lib_vrf_zebra_ribs_rib_get_keys(struct nb_cb_get_keys_args *args) +{ + const struct zebra_router_table *zrt = args->list_entry; + + args->keys->num = 2; + + snprintfrr(args->keys->key[0], sizeof(args->keys->key[0]), "%s", + yang_afi_safi_value2identity(zrt->afi, zrt->safi)); + snprintfrr(args->keys->key[1], sizeof(args->keys->key[1]), "%u", + zrt->tableid); + + return NB_OK; +} + +const void * +lib_vrf_zebra_ribs_rib_lookup_entry(struct nb_cb_lookup_entry_args *args) +{ + struct vrf *vrf = (struct vrf *)args->parent_list_entry; + struct zebra_vrf *zvrf; + afi_t afi; + safi_t safi; + uint32_t table_id = 0; + + zvrf = zebra_vrf_lookup_by_id(vrf->vrf_id); + + yang_afi_safi_identity2value(args->keys->key[0], &afi, &safi); + table_id = yang_str2uint32(args->keys->key[1]); + /* table_id 0 assume vrf's table_id. */ + if (!table_id) + table_id = zvrf->table_id; + + return zebra_router_find_zrt(zvrf, table_id, afi, safi); +} + +/* + * XPath: /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route + */ +const void * +lib_vrf_zebra_ribs_rib_route_get_next(struct nb_cb_get_next_args *args) +{ + const struct zebra_router_table *zrt = args->parent_list_entry; + struct route_node *rn = (struct route_node *)args->list_entry; + + if (args->list_entry == NULL) + rn = route_top(zrt->table); + else + rn = srcdest_route_next((struct route_node *)rn); + /* Optimization: skip empty route nodes. */ + while (rn && rn->info == NULL) + rn = route_next(rn); + + /* Skip link-local routes. */ + if (rn && rn->p.family == AF_INET6 + && IN6_IS_ADDR_LINKLOCAL(&rn->p.u.prefix6)) + return NULL; + + return rn; +} + +int lib_vrf_zebra_ribs_rib_route_get_keys(struct nb_cb_get_keys_args *args) +{ + const struct route_node *rn = args->list_entry; + + args->keys->num = 1; + prefix2str(&rn->p, args->keys->key[0], sizeof(args->keys->key[0])); + + return NB_OK; +} + +const void * +lib_vrf_zebra_ribs_rib_route_lookup_entry(struct nb_cb_lookup_entry_args *args) +{ + const struct zebra_router_table *zrt = args->parent_list_entry; + struct prefix p; + struct route_node *rn; + + yang_str2prefix(args->keys->key[0], &p); + + rn = route_node_lookup(zrt->table, &p); + + if (!rn) + return NULL; + + route_unlock_node(rn); + + return rn; +} + +/* + * XPath: /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/prefix + */ +struct yang_data * +lib_vrf_zebra_ribs_rib_route_prefix_get_elem(struct nb_cb_get_elem_args *args) +{ + const struct route_node *rn = args->list_entry; + + return yang_data_new_prefix(args->xpath, &rn->p); +} + +/* + * XPath: /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry + */ +const void *lib_vrf_zebra_ribs_rib_route_route_entry_get_next( + struct nb_cb_get_next_args *args) +{ + struct route_entry *re = (struct route_entry *)args->list_entry; + struct route_node *rn = (struct route_node *)args->parent_list_entry; + + if (args->list_entry == NULL) + RNODE_FIRST_RE(rn, re); + else + RNODE_NEXT_RE(rn, re); + + return re; +} + +int lib_vrf_zebra_ribs_rib_route_route_entry_get_keys( + struct nb_cb_get_keys_args *args) +{ + struct route_entry *re = (struct route_entry *)args->list_entry; + + args->keys->num = 1; + + strlcpy(args->keys->key[0], zebra_route_string(re->type), + sizeof(args->keys->key[0])); + + return NB_OK; +} + +const void *lib_vrf_zebra_ribs_rib_route_route_entry_lookup_entry( + struct nb_cb_lookup_entry_args *args) +{ + struct route_node *rn = (struct route_node *)args->parent_list_entry; + struct route_entry *re = NULL; + int proto_type = 0; + afi_t afi; + + afi = family2afi(rn->p.family); + proto_type = proto_redistnum(afi, args->keys->key[0]); + + RNODE_FOREACH_RE (rn, re) { + if (proto_type == re->type) + return re; + } + + return NULL; +} + +/* + * XPath: /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/protocol + */ +struct yang_data *lib_vrf_zebra_ribs_rib_route_route_entry_protocol_get_elem( + struct nb_cb_get_elem_args *args) +{ + struct route_entry *re = (struct route_entry *)args->list_entry; + + return yang_data_new_enum(args->xpath, re->type); +} + +/* + * XPath: /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/instance + */ +struct yang_data *lib_vrf_zebra_ribs_rib_route_route_entry_instance_get_elem( + struct nb_cb_get_elem_args *args) +{ + struct route_entry *re = (struct route_entry *)args->list_entry; + + if (re->instance) + return yang_data_new_uint16(args->xpath, re->instance); + + return NULL; +} + +/* + * XPath: /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/distance + */ +struct yang_data *lib_vrf_zebra_ribs_rib_route_route_entry_distance_get_elem( + struct nb_cb_get_elem_args *args) +{ + struct route_entry *re = (struct route_entry *)args->list_entry; + + return yang_data_new_uint8(args->xpath, re->distance); +} + +/* + * XPath: /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/metric + */ +struct yang_data *lib_vrf_zebra_ribs_rib_route_route_entry_metric_get_elem( + struct nb_cb_get_elem_args *args) +{ + struct route_entry *re = (struct route_entry *)args->list_entry; + + return yang_data_new_uint32(args->xpath, re->metric); +} + +/* + * XPath: /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/tag + */ +struct yang_data *lib_vrf_zebra_ribs_rib_route_route_entry_tag_get_elem( + struct nb_cb_get_elem_args *args) +{ + struct route_entry *re = (struct route_entry *)args->list_entry; + + if (re->tag) + return yang_data_new_uint32(args->xpath, re->tag); + + return NULL; +} + +/* + * XPath: /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/selected + */ +struct yang_data *lib_vrf_zebra_ribs_rib_route_route_entry_selected_get_elem( + struct nb_cb_get_elem_args *args) +{ + struct route_entry *re = (struct route_entry *)args->list_entry; + + if (CHECK_FLAG(re->flags, ZEBRA_FLAG_SELECTED)) + return yang_data_new_empty(args->xpath); + + return NULL; +} + +/* + * XPath: /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/installed + */ +struct yang_data *lib_vrf_zebra_ribs_rib_route_route_entry_installed_get_elem( + struct nb_cb_get_elem_args *args) +{ + struct route_entry *re = (struct route_entry *)args->list_entry; + + if (CHECK_FLAG(re->status, ROUTE_ENTRY_INSTALLED)) + return yang_data_new_empty(args->xpath); + + return NULL; +} + +/* + * XPath: /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/failed + */ +struct yang_data *lib_vrf_zebra_ribs_rib_route_route_entry_failed_get_elem( + struct nb_cb_get_elem_args *args) +{ + struct route_entry *re = (struct route_entry *)args->list_entry; + + if (CHECK_FLAG(re->status, ROUTE_ENTRY_FAILED)) + return yang_data_new_empty(args->xpath); + + return NULL; +} + +/* + * XPath: /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/queued + */ +struct yang_data *lib_vrf_zebra_ribs_rib_route_route_entry_queued_get_elem( + struct nb_cb_get_elem_args *args) +{ + struct route_entry *re = (struct route_entry *)args->list_entry; + + if (CHECK_FLAG(re->status, ROUTE_ENTRY_QUEUED)) + return yang_data_new_empty(args->xpath); + + return NULL; +} + +/* + * XPath: + * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/internal-flags + */ +struct yang_data * +lib_vrf_zebra_ribs_rib_route_route_entry_internal_flags_get_elem( + struct nb_cb_get_elem_args *args) +{ + struct route_entry *re = (struct route_entry *)args->list_entry; + + if (re->flags) + return yang_data_new_int32(args->xpath, re->flags); + + return NULL; +} + +/* + * XPath: + * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/internal-status + */ +struct yang_data * +lib_vrf_zebra_ribs_rib_route_route_entry_internal_status_get_elem( + struct nb_cb_get_elem_args *args) +{ + struct route_entry *re = (struct route_entry *)args->list_entry; + + if (re->status) + return yang_data_new_int32(args->xpath, re->status); + + return NULL; +} + +/* + * XPath: /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/uptime + */ +struct yang_data *lib_vrf_zebra_ribs_rib_route_route_entry_uptime_get_elem( + struct nb_cb_get_elem_args *args) +{ + struct route_entry *re = (struct route_entry *)args->list_entry; + + return yang_data_new_date_and_time(args->xpath, re->uptime); +} + +/* + * XPath: + * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/id + */ +struct yang_data * +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_id_get_elem( + struct nb_cb_get_elem_args *args) +{ + struct route_entry *re = (struct route_entry *)args->list_entry; + + return yang_data_new_uint32(args->xpath, re->nhe->id); +} + +/* + * XPath: + * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop + */ +const void * +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_get_next( + struct nb_cb_get_next_args *args) +{ + struct nexthop *nexthop = (struct nexthop *)args->list_entry; + struct route_entry *re = (struct route_entry *)args->parent_list_entry; + struct nhg_hash_entry *nhe = re->nhe; + + if (args->list_entry == NULL) { + nexthop = nhe->nhg.nexthop; + } else + nexthop = nexthop_next(nexthop); + + return nexthop; +} + +int lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_get_keys( + struct nb_cb_get_keys_args *args) +{ + struct nexthop *nexthop = (struct nexthop *)args->list_entry; + + args->keys->num = 4; + + strlcpy(args->keys->key[0], yang_nexthop_type2str(nexthop->type), + sizeof(args->keys->key[0])); + + snprintfrr(args->keys->key[1], sizeof(args->keys->key[1]), "%" PRIu32, + nexthop->vrf_id); + + switch (nexthop->type) { + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + snprintfrr(args->keys->key[2], sizeof(args->keys->key[2]), + "%pI4", &nexthop->gate.ipv4); + if (nexthop->ifindex) + strlcpy(args->keys->key[3], + ifindex2ifname(nexthop->ifindex, + nexthop->vrf_id), + sizeof(args->keys->key[3])); + else + /* no ifindex */ + strlcpy(args->keys->key[3], " ", + sizeof(args->keys->key[3])); + + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + snprintfrr(args->keys->key[2], sizeof(args->keys->key[2]), + "%pI6", &nexthop->gate.ipv6); + + if (nexthop->ifindex) + strlcpy(args->keys->key[3], + ifindex2ifname(nexthop->ifindex, + nexthop->vrf_id), + sizeof(args->keys->key[3])); + else + /* no ifindex */ + strlcpy(args->keys->key[3], " ", + sizeof(args->keys->key[3])); + + break; + case NEXTHOP_TYPE_IFINDEX: + strlcpy(args->keys->key[2], "", sizeof(args->keys->key[2])); + strlcpy(args->keys->key[3], + ifindex2ifname(nexthop->ifindex, nexthop->vrf_id), + sizeof(args->keys->key[3])); + + break; + case NEXTHOP_TYPE_BLACKHOLE: + /* Gateway IP */ + strlcpy(args->keys->key[2], "", sizeof(args->keys->key[2])); + strlcpy(args->keys->key[3], " ", sizeof(args->keys->key[3])); + break; + default: + break; + } + + return NB_OK; +} + +const void * +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_lookup_entry( + struct nb_cb_lookup_entry_args *args) +{ + struct nhg_hash_entry *nhe; + struct nexthop nexthop_lookup = {}; + struct nexthop *nexthop; + const char *nh_type_str; + + nhe = (struct nhg_hash_entry *)args->parent_list_entry; + nexthop_lookup.vrf_id = nhe->vrf_id; + + /* + * Get nexthop type. + * TODO: use yang_str2enum() instead. + */ + nh_type_str = args->keys->key[0]; + if (strmatch(nh_type_str, "ifindex")) + nexthop_lookup.type = NEXTHOP_TYPE_IFINDEX; + else if (strmatch(nh_type_str, "ip4")) + nexthop_lookup.type = NEXTHOP_TYPE_IPV4; + else if (strmatch(nh_type_str, "ip4-ifindex")) + nexthop_lookup.type = NEXTHOP_TYPE_IPV4_IFINDEX; + else if (strmatch(nh_type_str, "ip6")) + nexthop_lookup.type = NEXTHOP_TYPE_IPV6; + else if (strmatch(nh_type_str, "ip6-ifindex")) + nexthop_lookup.type = NEXTHOP_TYPE_IPV6_IFINDEX; + else if (strmatch(nh_type_str, "blackhole")) + nexthop_lookup.type = NEXTHOP_TYPE_BLACKHOLE; + else + /* unexpected */ + return NULL; + + /* Get nexthop address. */ + switch (nexthop_lookup.type) { + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + yang_str2ipv4(args->keys->key[1], &nexthop_lookup.gate.ipv4); + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + yang_str2ipv6(args->keys->key[1], &nexthop_lookup.gate.ipv6); + break; + case NEXTHOP_TYPE_IFINDEX: + case NEXTHOP_TYPE_BLACKHOLE: + break; + } + + /* Get nexthop interface. */ + switch (nexthop_lookup.type) { + case NEXTHOP_TYPE_IPV4_IFINDEX: + case NEXTHOP_TYPE_IPV6_IFINDEX: + case NEXTHOP_TYPE_IFINDEX: + nexthop_lookup.ifindex = + ifname2ifindex(args->keys->key[2], nhe->vrf_id); + break; + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_BLACKHOLE: + break; + } + + /* Lookup requested nexthop (ignore weight and metric). */ + for (ALL_NEXTHOPS(nhe->nhg, nexthop)) { + nexthop_lookup.weight = nexthop->weight; + nexthop_lookup.src = nexthop->src; + if (nexthop_same_no_labels(&nexthop_lookup, nexthop)) + return nexthop; + } + + return NULL; +} + +/* + * XPath: + * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/nh-type + */ +struct yang_data * +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_nh_type_get_elem( + struct nb_cb_get_elem_args *args) +{ + struct nexthop *nexthop = (struct nexthop *)args->list_entry; + + switch (nexthop->type) { + case NEXTHOP_TYPE_IFINDEX: + return yang_data_new_string(args->xpath, "ifindex"); + break; + case NEXTHOP_TYPE_IPV4: + return yang_data_new_string(args->xpath, "ip4"); + break; + case NEXTHOP_TYPE_IPV4_IFINDEX: + return yang_data_new_string(args->xpath, "ip4-ifindex"); + break; + case NEXTHOP_TYPE_IPV6: + return yang_data_new_string(args->xpath, "ip6"); + break; + case NEXTHOP_TYPE_IPV6_IFINDEX: + return yang_data_new_string(args->xpath, "ip6-ifindex"); + break; + default: + break; + } + + return NULL; +} + +/* + * XPath: + * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/vrf + */ +struct yang_data * +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_vrf_get_elem( + struct nb_cb_get_elem_args *args) +{ + struct nexthop *nexthop = (struct nexthop *)args->list_entry; + + return yang_data_new_string(args->xpath, + vrf_id_to_name(nexthop->vrf_id)); +} + +/* + * XPath: + * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/gateway + */ +struct yang_data * +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_gateway_get_elem( + struct nb_cb_get_elem_args *args) +{ + struct nexthop *nexthop = (struct nexthop *)args->list_entry; + struct ipaddr addr; + + switch (nexthop->type) { + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + addr.ipa_type = IPADDR_V4; + memcpy(&addr.ipaddr_v4, &(nexthop->gate.ipv4), + sizeof(struct in_addr)); + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + addr.ipa_type = IPADDR_V6; + memcpy(&addr.ipaddr_v6, &(nexthop->gate.ipv6), + sizeof(struct in6_addr)); + break; + case NEXTHOP_TYPE_BLACKHOLE: + case NEXTHOP_TYPE_IFINDEX: + /* No addr here */ + return yang_data_new_string(args->xpath, ""); + break; + default: + break; + } + + return yang_data_new_ip(args->xpath, &addr); +} + +/* + * XPath: + * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/interface + */ +struct yang_data * +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_interface_get_elem( + struct nb_cb_get_elem_args *args) +{ + struct nexthop *nexthop = (struct nexthop *)args->list_entry; + + if (nexthop->ifindex) + return yang_data_new_string( + args->xpath, + ifindex2ifname(nexthop->ifindex, nexthop->vrf_id)); + + return NULL; +} + +/* + * XPath: + * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/bh-type + */ +struct yang_data * +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_bh_type_get_elem( + struct nb_cb_get_elem_args *args) +{ + struct nexthop *nexthop = (struct nexthop *)args->list_entry; + const char *type_str = ""; + + if (nexthop->type != NEXTHOP_TYPE_BLACKHOLE) + return NULL; + + switch (nexthop->bh_type) { + case BLACKHOLE_NULL: + type_str = "null"; + break; + case BLACKHOLE_REJECT: + type_str = "reject"; + break; + case BLACKHOLE_ADMINPROHIB: + type_str = "prohibited"; + break; + case BLACKHOLE_UNSPEC: + type_str = "unspec"; + break; + } + + return yang_data_new_string(args->xpath, type_str); +} + +/* + * XPath: + * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/onlink + */ +struct yang_data * +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_onlink_get_elem( + struct nb_cb_get_elem_args *args) +{ + struct nexthop *nexthop = (struct nexthop *)args->list_entry; + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK)) + return yang_data_new_bool(args->xpath, true); + + return NULL; +} + +/* + * XPath: + * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/srte-color + */ +struct yang_data * +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_color_get_elem( + struct nb_cb_get_elem_args *args) +{ + struct nexthop *nexthop = (struct nexthop *)args->list_entry; + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_SRTE)) + return yang_data_new_uint32(args->xpath, nexthop->srte_color); + + return NULL; +} + +/* + * XPath: + * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/mpls-label-stack/entry + */ +const void * +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_get_next( + struct nb_cb_get_next_args *args) +{ + /* TODO: implement me. */ + return NULL; +} + +int lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_get_keys( + struct nb_cb_get_keys_args *args) +{ + /* TODO: implement me. */ + return NB_OK; +} + +const void * +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_lookup_entry( + struct nb_cb_lookup_entry_args *args) +{ + /* TODO: implement me. */ + return NULL; +} + +/* + * XPath: + * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/mpls-label-stack/entry/id + */ +struct yang_data * +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_id_get_elem( + struct nb_cb_get_elem_args *args) +{ + /* TODO: implement me. */ + return NULL; +} + +/* + * XPath: + * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/mpls-label-stack/entry/label + */ +struct yang_data * +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_label_get_elem( + struct nb_cb_get_elem_args *args) +{ + /* TODO: implement me. */ + return NULL; +} + +/* + * XPath: + * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/mpls-label-stack/entry/ttl + */ +struct yang_data * +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_ttl_get_elem( + struct nb_cb_get_elem_args *args) +{ + /* TODO: implement me. */ + return NULL; +} + +/* + * XPath: + * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/mpls-label-stack/entry/traffic-class + */ +struct yang_data * +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_traffic_class_get_elem( + struct nb_cb_get_elem_args *args) +{ + /* TODO: implement me. */ + return NULL; +} + +/* + * XPath: + * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/duplicate + */ +struct yang_data * +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_duplicate_get_elem( + struct nb_cb_get_elem_args *args) +{ + struct nexthop *nexthop = (struct nexthop *)args->list_entry; + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE)) + return yang_data_new_empty(args->xpath); + + return NULL; +} + +/* + * XPath: + * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/recursive + */ +struct yang_data * +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_recursive_get_elem( + struct nb_cb_get_elem_args *args) +{ + struct nexthop *nexthop = (struct nexthop *)args->list_entry; + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + return yang_data_new_empty(args->xpath); + + return NULL; +} + +/* + * XPath: + * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/active + */ +struct yang_data * +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_active_get_elem( + struct nb_cb_get_elem_args *args) +{ + struct nexthop *nexthop = (struct nexthop *)args->list_entry; + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) + return yang_data_new_empty(args->xpath); + + return NULL; +} + +/* + * XPath: + * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/fib + */ +struct yang_data * +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_fib_get_elem( + struct nb_cb_get_elem_args *args) +{ + struct nexthop *nexthop = (struct nexthop *)args->list_entry; + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) + return yang_data_new_empty(args->xpath); + + return NULL; +} + +/* + * XPath: + * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/weight + */ +struct yang_data * +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_weight_get_elem( + struct nb_cb_get_elem_args *args) +{ + struct nexthop *nexthop = (struct nexthop *)args->list_entry; + + if (nexthop->weight) + return yang_data_new_uint8(args->xpath, nexthop->weight); + + return NULL; +} diff --git a/zebra/zebra_netns_id.c b/zebra/zebra_netns_id.c index ea4b07a87d..81d610940d 100644 --- a/zebra/zebra_netns_id.c +++ b/zebra/zebra_netns_id.c @@ -39,9 +39,6 @@ #include "zebra/zebra_netns_id.h" #include "zebra/zebra_errors.h" -/* default NS ID value used when VRF backend is not NETNS */ -#define NS_DEFAULT_INTERNAL 0 - /* in case NEWNSID not available, the NSID will be locally obtained */ #define NS_BASE_NSID 0 @@ -143,7 +140,7 @@ static ns_id_t extract_nsid(struct nlmsghdr *nlh, char *buf) void *tail = (void *)((char *)nlh + NETLINK_ALIGN(nlh->nlmsg_len)); struct nlattr *attr; - for (attr = (struct nlattr *)((char *)buf + offset); + for (attr = (struct nlattr *)(buf + offset); NETLINK_NLATTR_LEN(tail, attr) >= sizeof(struct nlattr) && attr->nla_len >= sizeof(struct nlattr) && attr->nla_len <= NETLINK_NLATTR_LEN(tail, attr); @@ -159,27 +156,34 @@ static ns_id_t extract_nsid(struct nlmsghdr *nlh, char *buf) return ns_id; } -ns_id_t zebra_ns_id_get(const char *netnspath) +/* fd_param = -1 is ignored. + * netnspath set to null is ignored. + * one of the 2 params is mandatory. netnspath is looked in priority + */ +ns_id_t zebra_ns_id_get(const char *netnspath, int fd_param) { int ns_id = -1; struct sockaddr_nl snl; - int fd, sock, ret; + int fd = -1, sock, ret; unsigned int seq; ns_id_t return_nsid = NS_UNKNOWN; /* netns path check */ - if (!netnspath) + if (!netnspath && fd_param == -1) return NS_UNKNOWN; - fd = open(netnspath, O_RDONLY); - if (fd == -1) - return NS_UNKNOWN; - + if (netnspath) { + fd = open(netnspath, O_RDONLY); + if (fd == -1) + return NS_UNKNOWN; + } else if (fd_param != -1) + fd = fd_param; /* netlink socket */ sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (sock < 0) { flog_err_sys(EC_LIB_SOCKET, "netlink( %u) socket() error: %s", sock, safe_strerror(errno)); - close(fd); + if (netnspath) + close(fd); return NS_UNKNOWN; } memset(&snl, 0, sizeof(snl)); @@ -192,7 +196,8 @@ ns_id_t zebra_ns_id_get(const char *netnspath) "netlink( %u) socket() bind error: %s", sock, safe_strerror(errno)); close(sock); - close(fd); + if (netnspath) + close(fd); return NS_UNKNOWN; } @@ -208,13 +213,14 @@ ns_id_t zebra_ns_id_get(const char *netnspath) nlh->nlmsg_len += NETLINK_ALIGN(sizeof(struct rtgenmsg)); rt->rtgen_family = AF_UNSPEC; - addattr32(nlh, NETLINK_SOCKET_BUFFER_SIZE, NETNSA_FD, fd); - addattr32(nlh, NETLINK_SOCKET_BUFFER_SIZE, NETNSA_NSID, ns_id); + nl_attr_put32(nlh, NETLINK_SOCKET_BUFFER_SIZE, NETNSA_FD, fd); + nl_attr_put32(nlh, NETLINK_SOCKET_BUFFER_SIZE, NETNSA_NSID, ns_id); ret = send_receive(sock, nlh, seq, buf); if (ret < 0) { close(sock); - close(fd); + if (netnspath) + close(fd); return NS_UNKNOWN; } nlh = (struct nlmsghdr *)buf; @@ -258,7 +264,8 @@ ns_id_t zebra_ns_id_get(const char *netnspath) "netlink( %u) recvfrom() error 2 when reading: %s", fd, safe_strerror(errno)); close(sock); - close(fd); + if (netnspath) + close(fd); if (errno == ENOTSUP) { zlog_debug("NEWNSID locally generated"); return zebra_ns_id_get_fallback(netnspath); @@ -272,13 +279,15 @@ ns_id_t zebra_ns_id_get(const char *netnspath) nlh->nlmsg_len += NETLINK_ALIGN(sizeof(struct rtgenmsg)); rt->rtgen_family = AF_UNSPEC; - addattr32(nlh, NETLINK_SOCKET_BUFFER_SIZE, NETNSA_FD, fd); - addattr32(nlh, NETLINK_SOCKET_BUFFER_SIZE, NETNSA_NSID, ns_id); + nl_attr_put32(nlh, NETLINK_SOCKET_BUFFER_SIZE, NETNSA_FD, fd); + nl_attr_put32(nlh, NETLINK_SOCKET_BUFFER_SIZE, NETNSA_NSID, + ns_id); ret = send_receive(sock, nlh, seq, buf); if (ret < 0) { close(sock); - close(fd); + if (netnspath) + close(fd); return NS_UNKNOWN; } nlh = (struct nlmsghdr *)buf; @@ -309,16 +318,18 @@ ns_id_t zebra_ns_id_get(const char *netnspath) } while (len != 0 && ret == 0); } - close(fd); + if (netnspath) + close(fd); close(sock); return return_nsid; } #else -ns_id_t zebra_ns_id_get(const char *netnspath) +ns_id_t zebra_ns_id_get(const char *netnspath, int fd __attribute__ ((unused))) { return zebra_ns_id_get_fallback(netnspath); } + #endif /* ! defined(HAVE_NETLINK) */ #ifdef HAVE_NETNS @@ -348,14 +359,14 @@ ns_id_t zebra_ns_id_get_default(void) fd = open(NS_DEFAULT_NAME, O_RDONLY); if (fd == -1) - return NS_DEFAULT_INTERNAL; + return NS_DEFAULT; if (!vrf_is_backend_netns()) { close(fd); - return NS_DEFAULT_INTERNAL; + return NS_DEFAULT; } close(fd); - return zebra_ns_id_get((char *)NS_DEFAULT_NAME); + return zebra_ns_id_get((char *)NS_DEFAULT_NAME, -1); #else /* HAVE_NETNS */ - return NS_DEFAULT_INTERNAL; + return NS_DEFAULT; #endif /* !HAVE_NETNS */ } diff --git a/zebra/zebra_netns_id.h b/zebra/zebra_netns_id.h index 7a5f6851f4..dd9eab18e0 100644 --- a/zebra/zebra_netns_id.h +++ b/zebra/zebra_netns_id.h @@ -24,7 +24,7 @@ extern "C" { #endif -extern ns_id_t zebra_ns_id_get(const char *netnspath); +extern ns_id_t zebra_ns_id_get(const char *netnspath, int fd); extern ns_id_t zebra_ns_id_get_default(void); #ifdef __cplusplus diff --git a/zebra/zebra_netns_notify.c b/zebra/zebra_netns_notify.c index 476638591b..995fa6fb5a 100644 --- a/zebra/zebra_netns_notify.c +++ b/zebra/zebra_netns_notify.c @@ -72,13 +72,14 @@ static void zebra_ns_notify_create_context_from_entry_name(const char *name) char *netnspath = ns_netns_pathname(NULL, name); struct vrf *vrf; int ret; - ns_id_t ns_id, ns_id_external; + ns_id_t ns_id, ns_id_external, ns_id_relative = NS_UNKNOWN; + struct ns *default_ns; if (netnspath == NULL) return; - frr_elevate_privs(&zserv_privs) { - ns_id = zebra_ns_id_get(netnspath); + frr_with_privs(&zserv_privs) { + ns_id = zebra_ns_id_get(netnspath, -1); } if (ns_id == NS_UNKNOWN) return; @@ -97,9 +98,21 @@ static void zebra_ns_notify_create_context_from_entry_name(const char *name) ns_map_nsid_with_external(ns_id, false); return; } - frr_elevate_privs(&zserv_privs) { + + default_ns = ns_get_default(); + + /* force kernel ns_id creation in that new vrf */ + frr_with_privs(&zserv_privs) { + ns_switch_to_netns(netnspath); + ns_id_relative = zebra_ns_id_get(NULL, default_ns->fd); + ns_switchback_to_initial(); + } + + frr_with_privs(&zserv_privs) { ret = vrf_netns_handler_create(NULL, vrf, netnspath, - ns_id_external, ns_id); + ns_id_external, + ns_id, + ns_id_relative); } if (ret != CMD_SUCCESS) { flog_warn(EC_ZEBRA_NS_VRF_CREATION_FAILED, @@ -153,10 +166,10 @@ static int zebra_ns_delete(char *name) static int zebra_ns_notify_self_identify(struct stat *netst) { - char net_path[64]; + char net_path[PATH_MAX]; int netns; - sprintf(net_path, "/proc/self/ns/net"); + snprintf(net_path, sizeof(net_path), "/proc/self/ns/net"); netns = open(net_path, O_RDONLY); if (netns < 0) return -1; @@ -172,13 +185,13 @@ static bool zebra_ns_notify_is_default_netns(const char *name) { struct stat default_netns_stat; struct stat st; - char netnspath[64]; + char netnspath[PATH_MAX]; if (zebra_ns_notify_self_identify(&default_netns_stat)) return false; memset(&st, 0, sizeof(struct stat)); - snprintf(netnspath, 64, "%s/%s", NS_RUN_DIR, name); + snprintf(netnspath, sizeof(netnspath), "%s/%s", NS_RUN_DIR, name); /* compare with local stat */ if (stat(netnspath, &st) == 0 && (st.st_dev == default_netns_stat.st_dev) && @@ -202,14 +215,14 @@ static int zebra_ns_ready_read(struct thread *t) netnspath = zns_info->netnspath; if (--zns_info->retries == 0) stop_retry = 1; - frr_elevate_privs(&zserv_privs) { + frr_with_privs(&zserv_privs) { err = ns_switch_to_netns(netnspath); } if (err < 0) return zebra_ns_continue_read(zns_info, stop_retry); /* go back to default ns */ - frr_elevate_privs(&zserv_privs) { + frr_with_privs(&zserv_privs) { err = ns_switchback_to_initial(); } if (err < 0) @@ -217,14 +230,12 @@ static int zebra_ns_ready_read(struct thread *t) /* check default name is not already set */ if (strmatch(VRF_DEFAULT_NAME, basename(netnspath))) { - zlog_warn("NS notify : NS %s is already default VRF." - "Cancel VRF Creation", basename(netnspath)); + zlog_warn("NS notify : NS %s is already default VRF.Cancel VRF Creation", basename(netnspath)); return zebra_ns_continue_read(zns_info, 1); } if (zebra_ns_notify_is_default_netns(basename(netnspath))) { zlog_warn( - "NS notify : NS %s is default VRF." - " Updating VRF Name", basename(netnspath)); + "NS notify : NS %s is default VRF. Updating VRF Name", basename(netnspath)); vrf_set_default_name(basename(netnspath), false); return zebra_ns_continue_read(zns_info, 1); } @@ -320,14 +331,12 @@ void zebra_ns_notify_parse(void) } /* check default name is not already set */ if (strmatch(VRF_DEFAULT_NAME, basename(dent->d_name))) { - zlog_warn("NS notify : NS %s is already default VRF." - "Cancel VRF Creation", dent->d_name); + zlog_warn("NS notify : NS %s is already default VRF.Cancel VRF Creation", dent->d_name); continue; } if (zebra_ns_notify_is_default_netns(dent->d_name)) { zlog_warn( - "NS notify : NS %s is default VRF." - " Updating VRF Name", dent->d_name); + "NS notify : NS %s is default VRF. Updating VRF Name", dent->d_name); vrf_set_default_name(dent->d_name, false); continue; } diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c new file mode 100644 index 0000000000..cfc31eeca6 --- /dev/null +++ b/zebra/zebra_nhg.c @@ -0,0 +1,2735 @@ +/* Zebra Nexthop Group Code. + * Copyright (C) 2019 Cumulus Networks, Inc. + * Donald Sharp + * Stephen Worley + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ +#include + +#include "lib/nexthop.h" +#include "lib/nexthop_group_private.h" +#include "lib/routemap.h" +#include "lib/mpls.h" +#include "lib/jhash.h" +#include "lib/debug.h" +#include "lib/lib_errors.h" + +#include "zebra/connected.h" +#include "zebra/debug.h" +#include "zebra/zebra_router.h" +#include "zebra/zebra_nhg_private.h" +#include "zebra/zebra_rnh.h" +#include "zebra/zebra_routemap.h" +#include "zebra/zebra_memory.h" +#include "zebra/zebra_srte.h" +#include "zebra/zserv.h" +#include "zebra/rt.h" +#include "zebra_errors.h" +#include "zebra_dplane.h" +#include "zebra/interface.h" + +DEFINE_MTYPE_STATIC(ZEBRA, NHG, "Nexthop Group Entry"); +DEFINE_MTYPE_STATIC(ZEBRA, NHG_CONNECTED, "Nexthop Group Connected"); +DEFINE_MTYPE_STATIC(ZEBRA, NHG_CTX, "Nexthop Group Context"); + +/* id counter to keep in sync with kernel */ +uint32_t id_counter; + +/* */ +static bool g_nexthops_enabled = true; + +static struct nhg_hash_entry *depends_find(const struct nexthop *nh, + afi_t afi); +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); +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); + +static struct nhg_backup_info * +nhg_backup_copy(const struct nhg_backup_info *orig); + + +static void nhg_connected_free(struct nhg_connected *dep) +{ + XFREE(MTYPE_NHG_CONNECTED, dep); +} + +static struct nhg_connected *nhg_connected_new(struct nhg_hash_entry *nhe) +{ + struct nhg_connected *new = NULL; + + new = XCALLOC(MTYPE_NHG_CONNECTED, sizeof(struct nhg_connected)); + new->nhe = nhe; + + return new; +} + +void nhg_connected_tree_free(struct nhg_connected_tree_head *head) +{ + struct nhg_connected *rb_node_dep = NULL; + + if (!nhg_connected_tree_is_empty(head)) { + frr_each_safe(nhg_connected_tree, head, rb_node_dep) { + nhg_connected_tree_del(head, rb_node_dep); + nhg_connected_free(rb_node_dep); + } + } +} + +bool nhg_connected_tree_is_empty(const struct nhg_connected_tree_head *head) +{ + return nhg_connected_tree_count(head) ? false : true; +} + +struct nhg_connected * +nhg_connected_tree_root(struct nhg_connected_tree_head *head) +{ + return nhg_connected_tree_first(head); +} + +struct nhg_hash_entry * +nhg_connected_tree_del_nhe(struct nhg_connected_tree_head *head, + struct nhg_hash_entry *depend) +{ + struct nhg_connected lookup = {}; + struct nhg_connected *remove = NULL; + struct nhg_hash_entry *removed_nhe; + + lookup.nhe = depend; + + /* Lookup to find the element, then remove it */ + remove = nhg_connected_tree_find(head, &lookup); + if (remove) + /* Re-returning here just in case this API changes.. + * the _del list api's are a bit undefined at the moment. + * + * So hopefully returning here will make it fail if the api + * changes to something different than currently expected. + */ + remove = nhg_connected_tree_del(head, remove); + + /* If the entry was sucessfully removed, free the 'connected` struct */ + if (remove) { + removed_nhe = remove->nhe; + nhg_connected_free(remove); + return removed_nhe; + } + + return NULL; +} + +/* Assuming UNIQUE RB tree. If this changes, assumptions here about + * insertion need to change. + */ +struct nhg_hash_entry * +nhg_connected_tree_add_nhe(struct nhg_connected_tree_head *head, + struct nhg_hash_entry *depend) +{ + struct nhg_connected *new = NULL; + + new = nhg_connected_new(depend); + + /* On success, NULL will be returned from the + * RB code. + */ + if (new && (nhg_connected_tree_add(head, new) == NULL)) + return NULL; + + /* If it wasn't successful, it must be a duplicate. We enforce the + * unique property for the `nhg_connected` tree. + */ + nhg_connected_free(new); + + return depend; +} + +static void +nhg_connected_tree_decrement_ref(struct nhg_connected_tree_head *head) +{ + struct nhg_connected *rb_node_dep = NULL; + + frr_each_safe(nhg_connected_tree, head, rb_node_dep) { + zebra_nhg_decrement_ref(rb_node_dep->nhe); + } +} + +static void +nhg_connected_tree_increment_ref(struct nhg_connected_tree_head *head) +{ + struct nhg_connected *rb_node_dep = NULL; + + frr_each(nhg_connected_tree, head, rb_node_dep) { + zebra_nhg_increment_ref(rb_node_dep->nhe); + } +} + +struct nhg_hash_entry *zebra_nhg_resolve(struct nhg_hash_entry *nhe) +{ + if (CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_RECURSIVE) + && !zebra_nhg_depends_is_empty(nhe)) { + nhe = nhg_connected_tree_root(&nhe->nhg_depends)->nhe; + return zebra_nhg_resolve(nhe); + } + + return nhe; +} + +unsigned int zebra_nhg_depends_count(const struct nhg_hash_entry *nhe) +{ + return nhg_connected_tree_count(&nhe->nhg_depends); +} + +bool zebra_nhg_depends_is_empty(const struct nhg_hash_entry *nhe) +{ + return nhg_connected_tree_is_empty(&nhe->nhg_depends); +} + +static void zebra_nhg_depends_del(struct nhg_hash_entry *from, + struct nhg_hash_entry *depend) +{ + nhg_connected_tree_del_nhe(&from->nhg_depends, depend); +} + +static void zebra_nhg_depends_init(struct nhg_hash_entry *nhe) +{ + nhg_connected_tree_init(&nhe->nhg_depends); +} + +unsigned int zebra_nhg_dependents_count(const struct nhg_hash_entry *nhe) +{ + return nhg_connected_tree_count(&nhe->nhg_dependents); +} + + +bool zebra_nhg_dependents_is_empty(const struct nhg_hash_entry *nhe) +{ + return nhg_connected_tree_is_empty(&nhe->nhg_dependents); +} + +static void zebra_nhg_dependents_del(struct nhg_hash_entry *from, + struct nhg_hash_entry *dependent) +{ + nhg_connected_tree_del_nhe(&from->nhg_dependents, dependent); +} + +static void zebra_nhg_dependents_add(struct nhg_hash_entry *to, + struct nhg_hash_entry *dependent) +{ + nhg_connected_tree_add_nhe(&to->nhg_dependents, dependent); +} + +static void zebra_nhg_dependents_init(struct nhg_hash_entry *nhe) +{ + nhg_connected_tree_init(&nhe->nhg_dependents); +} + +/* Release this nhe from anything depending on it */ +static void zebra_nhg_dependents_release(struct nhg_hash_entry *nhe) +{ + struct nhg_connected *rb_node_dep = NULL; + + frr_each_safe(nhg_connected_tree, &nhe->nhg_dependents, rb_node_dep) { + zebra_nhg_depends_del(rb_node_dep->nhe, nhe); + /* recheck validity of the dependent */ + zebra_nhg_check_valid(rb_node_dep->nhe); + } +} + +/* Release this nhe from anything that it depends on */ +static void zebra_nhg_depends_release(struct nhg_hash_entry *nhe) +{ + if (!zebra_nhg_depends_is_empty(nhe)) { + struct nhg_connected *rb_node_dep = NULL; + + frr_each_safe(nhg_connected_tree, &nhe->nhg_depends, + rb_node_dep) { + zebra_nhg_dependents_del(rb_node_dep->nhe, nhe); + } + } +} + + +struct nhg_hash_entry *zebra_nhg_lookup_id(uint32_t id) +{ + struct nhg_hash_entry lookup = {}; + + lookup.id = id; + return hash_lookup(zrouter.nhgs_id, &lookup); +} + +static int zebra_nhg_insert_id(struct nhg_hash_entry *nhe) +{ + if (hash_lookup(zrouter.nhgs_id, nhe)) { + flog_err( + EC_ZEBRA_NHG_TABLE_INSERT_FAILED, + "Failed inserting NHG id=%u into the ID hash table, entry already exists", + nhe->id); + return -1; + } + + hash_get(zrouter.nhgs_id, nhe, hash_alloc_intern); + + return 0; +} + +static void zebra_nhg_set_if(struct nhg_hash_entry *nhe, struct interface *ifp) +{ + nhe->ifp = ifp; + if_nhg_dependents_add(ifp, nhe); +} + +static void +zebra_nhg_connect_depends(struct nhg_hash_entry *nhe, + struct nhg_connected_tree_head *nhg_depends) +{ + struct nhg_connected *rb_node_dep = NULL; + + /* This has been allocated higher above in the stack. Could probably + * re-allocate and free the old stuff but just using the same memory + * for now. Otherwise, their might be a time trade-off for repeated + * alloc/frees as startup. + */ + nhe->nhg_depends = *nhg_depends; + + /* Attach backpointer to anything that it depends on */ + zebra_nhg_dependents_init(nhe); + if (!zebra_nhg_depends_is_empty(nhe)) { + frr_each(nhg_connected_tree, &nhe->nhg_depends, rb_node_dep) { + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: nhe %p (%u), dep %p (%u)", + __func__, nhe, nhe->id, + rb_node_dep->nhe, + rb_node_dep->nhe->id); + + zebra_nhg_dependents_add(rb_node_dep->nhe, nhe); + } + } +} + +/* Init an nhe, for use in a hash lookup for example */ +void zebra_nhe_init(struct nhg_hash_entry *nhe, afi_t afi, + const struct nexthop *nh) +{ + memset(nhe, 0, sizeof(struct nhg_hash_entry)); + nhe->vrf_id = VRF_DEFAULT; + nhe->type = ZEBRA_ROUTE_NHG; + nhe->afi = AFI_UNSPEC; + + /* There are some special rules that apply to groups representing + * a single nexthop. + */ + if (nh && (nh->next == NULL)) { + switch (nh->type) { + case (NEXTHOP_TYPE_IFINDEX): + case (NEXTHOP_TYPE_BLACKHOLE): + /* + * This switch case handles setting the afi different + * for ipv4/v6 routes. Ifindex/blackhole nexthop + * objects cannot be ambiguous, they must be Address + * Family specific. If we get here, we will either use + * the AF of the route, or the one we got passed from + * here from the kernel. + */ + nhe->afi = afi; + break; + case (NEXTHOP_TYPE_IPV4_IFINDEX): + case (NEXTHOP_TYPE_IPV4): + nhe->afi = AFI_IP; + break; + case (NEXTHOP_TYPE_IPV6_IFINDEX): + case (NEXTHOP_TYPE_IPV6): + nhe->afi = AFI_IP6; + break; + } + } +} + +struct nhg_hash_entry *zebra_nhg_alloc(void) +{ + struct nhg_hash_entry *nhe; + + nhe = XCALLOC(MTYPE_NHG, sizeof(struct nhg_hash_entry)); + + return nhe; +} + +/* + * Allocate new nhe and make shallow copy of 'orig'; no + * recursive info is copied. + */ +struct nhg_hash_entry *zebra_nhe_copy(const struct nhg_hash_entry *orig, + uint32_t id) +{ + struct nhg_hash_entry *nhe; + + nhe = zebra_nhg_alloc(); + + nhe->id = id; + + nexthop_group_copy(&(nhe->nhg), &(orig->nhg)); + + nhe->vrf_id = orig->vrf_id; + nhe->afi = orig->afi; + nhe->type = orig->type ? orig->type : ZEBRA_ROUTE_NHG; + nhe->refcnt = 0; + nhe->dplane_ref = zebra_router_get_next_sequence(); + + /* Copy backup info also, if present */ + if (orig->backup_info) + nhe->backup_info = nhg_backup_copy(orig->backup_info); + + return nhe; +} + +/* Allocation via hash handler */ +static void *zebra_nhg_hash_alloc(void *arg) +{ + struct nhg_hash_entry *nhe = NULL; + struct nhg_hash_entry *copy = arg; + + nhe = zebra_nhe_copy(copy, copy->id); + + /* Mark duplicate nexthops in a group at creation time. */ + nexthop_group_mark_duplicates(&(nhe->nhg)); + + zebra_nhg_connect_depends(nhe, &(copy->nhg_depends)); + + /* Add the ifp now if it's not a group or recursive and has ifindex */ + if (zebra_nhg_depends_is_empty(nhe) && nhe->nhg.nexthop + && nhe->nhg.nexthop->ifindex) { + struct interface *ifp = NULL; + + ifp = if_lookup_by_index(nhe->nhg.nexthop->ifindex, + nhe->nhg.nexthop->vrf_id); + if (ifp) + zebra_nhg_set_if(nhe, ifp); + else + flog_err( + EC_ZEBRA_IF_LOOKUP_FAILED, + "Zebra failed to lookup an interface with ifindex=%d in vrf=%u for NHE id=%u", + nhe->nhg.nexthop->ifindex, + nhe->nhg.nexthop->vrf_id, nhe->id); + } + + zebra_nhg_insert_id(nhe); + + return nhe; +} + +uint32_t zebra_nhg_hash_key(const void *arg) +{ + const struct nhg_hash_entry *nhe = arg; + uint32_t val, key = 0x5a351234; + + val = nexthop_group_hash(&(nhe->nhg)); + if (nhe->backup_info) { + val = jhash_2words(val, + nexthop_group_hash( + &(nhe->backup_info->nhe->nhg)), + key); + } + + key = jhash_3words(nhe->vrf_id, nhe->afi, val, key); + + return key; +} + +uint32_t zebra_nhg_id_key(const void *arg) +{ + const struct nhg_hash_entry *nhe = arg; + + return nhe->id; +} + +/* Helper with common nhg/nhe nexthop comparison logic */ +static bool nhg_compare_nexthops(const struct nexthop *nh1, + const struct nexthop *nh2) +{ + assert(nh1 != NULL && nh2 != NULL); + + /* + * We have to check the active flag of each individual one, + * not just the overall active_num. This solves the special case + * issue of a route with a nexthop group with one nexthop + * resolving to itself and thus marking it inactive. If we + * have two different routes each wanting to mark a different + * nexthop inactive, they need to hash to two different groups. + * + * If we just hashed on num_active, they would hash the same + * which is incorrect. + * + * ex) + * 1.1.1.0/24 + * -> 1.1.1.1 dummy1 (inactive) + * -> 1.1.2.1 dummy2 + * + * 1.1.2.0/24 + * -> 1.1.1.1 dummy1 + * -> 1.1.2.1 dummy2 (inactive) + * + * Without checking each individual one, they would hash to + * the same group and both have 1.1.1.1 dummy1 marked inactive. + * + */ + if (CHECK_FLAG(nh1->flags, NEXTHOP_FLAG_ACTIVE) + != CHECK_FLAG(nh2->flags, NEXTHOP_FLAG_ACTIVE)) + return false; + + if (!nexthop_same(nh1, nh2)) + return false; + + return true; +} + +bool zebra_nhg_hash_equal(const void *arg1, const void *arg2) +{ + const struct nhg_hash_entry *nhe1 = arg1; + const struct nhg_hash_entry *nhe2 = arg2; + struct nexthop *nexthop1; + struct nexthop *nexthop2; + + /* No matter what if they equal IDs, assume equal */ + if (nhe1->id && nhe2->id && (nhe1->id == nhe2->id)) + return true; + + if (nhe1->vrf_id != nhe2->vrf_id) + return false; + + if (nhe1->afi != nhe2->afi) + return false; + + /* Nexthops should be in-order, so we simply compare them in-place */ + for (nexthop1 = nhe1->nhg.nexthop, nexthop2 = nhe2->nhg.nexthop; + nexthop1 && nexthop2; + nexthop1 = nexthop1->next, nexthop2 = nexthop2->next) { + + if (!nhg_compare_nexthops(nexthop1, nexthop2)) + return false; + } + + /* Check for unequal list lengths */ + if (nexthop1 || nexthop2) + return false; + + /* If there's no backup info, comparison is done. */ + if ((nhe1->backup_info == NULL) && (nhe2->backup_info == NULL)) + return true; + + /* Compare backup info also - test the easy things first */ + if (nhe1->backup_info && (nhe2->backup_info == NULL)) + return false; + if (nhe2->backup_info && (nhe1->backup_info == NULL)) + return false; + + /* Compare number of backups before actually comparing any */ + for (nexthop1 = nhe1->backup_info->nhe->nhg.nexthop, + nexthop2 = nhe2->backup_info->nhe->nhg.nexthop; + nexthop1 && nexthop2; + nexthop1 = nexthop1->next, nexthop2 = nexthop2->next) { + ; + } + + /* Did we find the end of one list before the other? */ + if (nexthop1 || nexthop2) + return false; + + /* Have to compare the backup nexthops */ + for (nexthop1 = nhe1->backup_info->nhe->nhg.nexthop, + nexthop2 = nhe2->backup_info->nhe->nhg.nexthop; + nexthop1 && nexthop2; + nexthop1 = nexthop1->next, nexthop2 = nexthop2->next) { + + if (!nhg_compare_nexthops(nexthop1, nexthop2)) + return false; + } + + return true; +} + +bool zebra_nhg_hash_id_equal(const void *arg1, const void *arg2) +{ + const struct nhg_hash_entry *nhe1 = arg1; + const struct nhg_hash_entry *nhe2 = arg2; + + return nhe1->id == nhe2->id; +} + +static int zebra_nhg_process_grp(struct nexthop_group *nhg, + struct nhg_connected_tree_head *depends, + struct nh_grp *grp, uint8_t count) +{ + nhg_connected_tree_init(depends); + + for (int i = 0; i < count; i++) { + struct nhg_hash_entry *depend = NULL; + /* We do not care about nexthop_grp.weight at + * this time. But we should figure out + * how to adapt this to our code in + * the future. + */ + depend = depends_find_id_add(depends, grp[i].id); + + if (!depend) { + flog_err( + EC_ZEBRA_NHG_SYNC, + "Received Nexthop Group from the kernel with a dependent Nexthop ID (%u) which we do not have in our table", + grp[i].id); + return -1; + } + + /* + * If this is a nexthop with its own group + * dependencies, add them as well. Not sure its + * even possible to have a group within a group + * in the kernel. + */ + + copy_nexthops(&nhg->nexthop, depend->nhg.nexthop, NULL); + } + + return 0; +} + +static void handle_recursive_depend(struct nhg_connected_tree_head *nhg_depends, + struct nexthop *nh, afi_t afi) +{ + struct nhg_hash_entry *depend = NULL; + struct nexthop_group resolved_ng = {}; + + resolved_ng.nexthop = nh; + + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: head %p, nh %pNHv", + __func__, nhg_depends, nh); + + depend = zebra_nhg_rib_find(0, &resolved_ng, afi); + + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: nh %pNHv => %p (%u)", + __func__, nh, depend, + depend ? depend->id : 0); + + if (depend) + depends_add(nhg_depends, depend); +} + +/* + * Lookup an nhe in the global hash, using data from another nhe. If 'lookup' + * has an id value, that's used. Create a new global/shared nhe if not found. + */ +static bool zebra_nhe_find(struct nhg_hash_entry **nhe, /* return value */ + struct nhg_hash_entry *lookup, + struct nhg_connected_tree_head *nhg_depends, + afi_t afi) +{ + bool created = false; + bool recursive = false; + struct nhg_hash_entry *newnhe, *backup_nhe; + struct nexthop *nh = NULL; + + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: id %u, lookup %p, vrf %d, type %d, depends %p", + __func__, lookup->id, lookup, + lookup->vrf_id, lookup->type, + nhg_depends); + + if (lookup->id) + (*nhe) = zebra_nhg_lookup_id(lookup->id); + else + (*nhe) = hash_lookup(zrouter.nhgs, lookup); + + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: lookup => %p (%u)", + __func__, (*nhe), + (*nhe) ? (*nhe)->id : 0); + + /* If we found an existing object, we're done */ + if (*nhe) + goto done; + + /* We're going to create/insert a new nhe: + * 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); + created = true; + + /* Mail back the new object */ + *nhe = newnhe; + + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: => created %p (%u)", __func__, newnhe, + newnhe->id); + + /* Only hash/lookup the depends if the first lookup + * fails to find something. This should hopefully save a + * lot of cycles for larger ecmp sizes. + */ + if (nhg_depends) { + /* If you don't want to hash on each nexthop in the + * nexthop group struct you can pass the depends + * directly. Kernel-side we do this since it just looks + * them up via IDs. + */ + zebra_nhg_connect_depends(newnhe, nhg_depends); + goto done; + } + + /* Prepare dependency relationships if this is not a + * singleton nexthop. There are two cases: a single + * recursive nexthop, where we need a relationship to the + * resolving nexthop; or a group of nexthops, where we need + * relationships with the corresponding singletons. + */ + zebra_nhg_depends_init(lookup); + + nh = newnhe->nhg.nexthop; + + if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_ACTIVE)) + SET_FLAG(newnhe->flags, NEXTHOP_GROUP_VALID); + + if (nh->next == NULL) { + if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_RECURSIVE)) { + /* Single recursive nexthop */ + handle_recursive_depend(&newnhe->nhg_depends, + nh->resolved, afi); + recursive = true; + } + } else { + /* List of nexthops */ + for (nh = newnhe->nhg.nexthop; nh; nh = nh->next) { + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: depends NH %pNHv %s", + __func__, nh, + CHECK_FLAG(nh->flags, + NEXTHOP_FLAG_RECURSIVE) ? + "(R)" : ""); + + depends_find_add(&newnhe->nhg_depends, nh, afi); + } + } + + if (recursive) + SET_FLAG((*nhe)->flags, NEXTHOP_GROUP_RECURSIVE); + + if (zebra_nhg_get_backup_nhg(newnhe) == NULL || + zebra_nhg_get_backup_nhg(newnhe)->nexthop == NULL) + goto done; + + /* If there are backup nexthops, add them to the backup + * depends tree. The rules here are a little different. + */ + recursive = false; + backup_nhe = newnhe->backup_info->nhe; + + nh = backup_nhe->nhg.nexthop; + + /* Singleton recursive NH */ + if (nh->next == NULL && + CHECK_FLAG(nh->flags, NEXTHOP_FLAG_RECURSIVE)) { + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: backup depend NH %pNHv (R)", + __func__, nh); + + /* Single recursive nexthop */ + handle_recursive_depend(&backup_nhe->nhg_depends, + nh->resolved, afi); + recursive = true; + } else { + /* One or more backup NHs */ + for (; nh; nh = nh->next) { + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: backup depend NH %pNHv %s", + __func__, nh, + CHECK_FLAG(nh->flags, + NEXTHOP_FLAG_RECURSIVE) ? + "(R)" : ""); + + depends_find_add(&backup_nhe->nhg_depends, + nh, afi); + } + } + + if (recursive) + SET_FLAG(backup_nhe->flags, NEXTHOP_GROUP_RECURSIVE); + +done: + + return created; +} + +/* + * Lookup or create an nhe, based on an nhg or an nhe id. + */ +static bool zebra_nhg_find(struct nhg_hash_entry **nhe, uint32_t id, + struct nexthop_group *nhg, + struct nhg_connected_tree_head *nhg_depends, + vrf_id_t vrf_id, afi_t afi, int type) +{ + struct nhg_hash_entry lookup = {}; + bool created = false; + + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: id %u, nhg %p, vrf %d, type %d, depends %p", + __func__, id, nhg, vrf_id, type, + nhg_depends); + + /* Use a temporary nhe and call into the superset/common code */ + lookup.id = id; + lookup.type = type ? type : ZEBRA_ROUTE_NHG; + lookup.nhg = *nhg; + + lookup.vrf_id = vrf_id; + if (lookup.nhg.nexthop->next) { + /* Groups can have all vrfs and AF's in them */ + lookup.afi = AFI_UNSPEC; + } else { + switch (lookup.nhg.nexthop->type) { + case (NEXTHOP_TYPE_IFINDEX): + case (NEXTHOP_TYPE_BLACKHOLE): + /* + * This switch case handles setting the afi different + * for ipv4/v6 routes. Ifindex/blackhole nexthop + * objects cannot be ambiguous, they must be Address + * Family specific. If we get here, we will either use + * the AF of the route, or the one we got passed from + * here from the kernel. + */ + lookup.afi = afi; + break; + case (NEXTHOP_TYPE_IPV4_IFINDEX): + case (NEXTHOP_TYPE_IPV4): + lookup.afi = AFI_IP; + break; + case (NEXTHOP_TYPE_IPV6_IFINDEX): + case (NEXTHOP_TYPE_IPV6): + lookup.afi = AFI_IP6; + break; + } + } + + created = zebra_nhe_find(nhe, &lookup, nhg_depends, afi); + + return created; +} + +/* Find/create a single nexthop */ +static struct nhg_hash_entry * +zebra_nhg_find_nexthop(uint32_t id, struct nexthop *nh, afi_t afi, int type) +{ + struct nhg_hash_entry *nhe = NULL; + struct nexthop_group nhg = {}; + vrf_id_t vrf_id = !vrf_is_backend_netns() ? VRF_DEFAULT : nh->vrf_id; + + nexthop_group_add_sorted(&nhg, nh); + + zebra_nhg_find(&nhe, id, &nhg, NULL, vrf_id, afi, type); + + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: nh %pNHv => %p (%u)", + __func__, nh, nhe, nhe ? nhe->id : 0); + + return nhe; +} + +static uint32_t nhg_ctx_get_id(const struct nhg_ctx *ctx) +{ + return ctx->id; +} + +static void nhg_ctx_set_status(struct nhg_ctx *ctx, enum nhg_ctx_status status) +{ + ctx->status = status; +} + +static enum nhg_ctx_status nhg_ctx_get_status(const struct nhg_ctx *ctx) +{ + return ctx->status; +} + +static void nhg_ctx_set_op(struct nhg_ctx *ctx, enum nhg_ctx_op_e op) +{ + ctx->op = op; +} + +static enum nhg_ctx_op_e nhg_ctx_get_op(const struct nhg_ctx *ctx) +{ + return ctx->op; +} + +static vrf_id_t nhg_ctx_get_vrf_id(const struct nhg_ctx *ctx) +{ + return ctx->vrf_id; +} + +static int nhg_ctx_get_type(const struct nhg_ctx *ctx) +{ + return ctx->type; +} + +static int nhg_ctx_get_afi(const struct nhg_ctx *ctx) +{ + return ctx->afi; +} + +static struct nexthop *nhg_ctx_get_nh(struct nhg_ctx *ctx) +{ + return &ctx->u.nh; +} + +static uint8_t nhg_ctx_get_count(const struct nhg_ctx *ctx) +{ + return ctx->count; +} + +static struct nh_grp *nhg_ctx_get_grp(struct nhg_ctx *ctx) +{ + return ctx->u.grp; +} + +static struct nhg_ctx *nhg_ctx_new(void) +{ + struct nhg_ctx *new; + + new = XCALLOC(MTYPE_NHG_CTX, sizeof(struct nhg_ctx)); + + return new; +} + +static void nhg_ctx_free(struct nhg_ctx **ctx) +{ + struct nexthop *nh; + + if (ctx == NULL) + return; + + assert((*ctx) != NULL); + + if (nhg_ctx_get_count(*ctx)) + goto done; + + nh = nhg_ctx_get_nh(*ctx); + + nexthop_del_labels(nh); + +done: + XFREE(MTYPE_NHG_CTX, *ctx); +} + +static struct nhg_ctx *nhg_ctx_init(uint32_t id, struct nexthop *nh, + struct nh_grp *grp, vrf_id_t vrf_id, + afi_t afi, int type, uint8_t count) +{ + struct nhg_ctx *ctx = NULL; + + ctx = nhg_ctx_new(); + + ctx->id = id; + ctx->vrf_id = vrf_id; + ctx->afi = afi; + ctx->type = type; + ctx->count = count; + + if (count) + /* Copy over the array */ + memcpy(&ctx->u.grp, grp, count * sizeof(struct nh_grp)); + else if (nh) + ctx->u.nh = *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; + + SET_FLAG(nhe->flags, NEXTHOP_GROUP_VALID); + + frr_each(nhg_connected_tree, &nhe->nhg_dependents, rb_node_dep) + zebra_nhg_set_valid(rb_node_dep->nhe); +} + +static void zebra_nhg_set_invalid(struct nhg_hash_entry *nhe) +{ + struct nhg_connected *rb_node_dep; + + UNSET_FLAG(nhe->flags, NEXTHOP_GROUP_VALID); + + /* Update validity of nexthops depending on it */ + frr_each(nhg_connected_tree, &nhe->nhg_dependents, rb_node_dep) + zebra_nhg_check_valid(rb_node_dep->nhe); +} + +void zebra_nhg_check_valid(struct nhg_hash_entry *nhe) +{ + struct nhg_connected *rb_node_dep = NULL; + bool valid = false; + + /* If anthing else in the group is valid, the group is valid */ + frr_each(nhg_connected_tree, &nhe->nhg_depends, rb_node_dep) { + if (CHECK_FLAG(rb_node_dep->nhe->flags, NEXTHOP_GROUP_VALID)) { + valid = true; + goto done; + } + } + +done: + if (valid) + zebra_nhg_set_valid(nhe); + else + zebra_nhg_set_invalid(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); + + /* 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); + + /* + * If its unhashable, 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)) + hash_release(zrouter.nhgs, nhe); + + hash_release(zrouter.nhgs_id, nhe); +} + +static void zebra_nhg_handle_uninstall(struct nhg_hash_entry *nhe) +{ + zebra_nhg_release(nhe); + zebra_nhg_free(nhe); +} + +static void zebra_nhg_handle_install(struct nhg_hash_entry *nhe) +{ + /* Update validity of groups depending on it */ + struct nhg_connected *rb_node_dep; + + frr_each_safe(nhg_connected_tree, &nhe->nhg_dependents, rb_node_dep) + zebra_nhg_set_valid(rb_node_dep->nhe); +} + +/* + * The kernel/other program has changed the state of a nexthop object we are + * using. + */ +static void zebra_nhg_handle_kernel_state_change(struct nhg_hash_entry *nhe, + bool is_delete) +{ + if (nhe->refcnt) { + flog_err( + EC_ZEBRA_NHG_SYNC, + "Kernel %s a nexthop group with ID (%u) that we are still using for a route, sending it back down", + (is_delete ? "deleted" : "updated"), nhe->id); + + UNSET_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED); + zebra_nhg_install_kernel(nhe); + } else + zebra_nhg_handle_uninstall(nhe); +} + +static int nhg_ctx_process_new(struct nhg_ctx *ctx) +{ + struct nexthop_group *nhg = NULL; + struct nhg_connected_tree_head nhg_depends = {}; + struct nhg_hash_entry *lookup = NULL; + struct nhg_hash_entry *nhe = NULL; + + uint32_t id = nhg_ctx_get_id(ctx); + uint8_t count = nhg_ctx_get_count(ctx); + vrf_id_t vrf_id = nhg_ctx_get_vrf_id(ctx); + int type = nhg_ctx_get_type(ctx); + afi_t afi = nhg_ctx_get_afi(ctx); + + lookup = zebra_nhg_lookup_id(id); + + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: id %u, count %d, lookup => %p", + __func__, id, count, lookup); + + if (lookup) { + /* This is already present in our table, hence an update + * that we did not initate. + */ + zebra_nhg_handle_kernel_state_change(lookup, false); + return 0; + } + + if (nhg_ctx_get_count(ctx)) { + nhg = nexthop_group_new(); + if (zebra_nhg_process_grp(nhg, &nhg_depends, + nhg_ctx_get_grp(ctx), count)) { + depends_decrement_free(&nhg_depends); + nexthop_group_delete(&nhg); + return -ENOENT; + } + + if (!zebra_nhg_find(&nhe, id, nhg, &nhg_depends, vrf_id, type, + afi)) + depends_decrement_free(&nhg_depends); + + /* These got copied over in zebra_nhg_alloc() */ + nexthop_group_delete(&nhg); + } else + 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 { + flog_err( + EC_ZEBRA_TABLE_LOOKUP_FAILED, + "Zebra failed to find or create a nexthop hash entry for ID (%u)", + id); + return -1; + } + + return 0; +} + +static int nhg_ctx_process_del(struct nhg_ctx *ctx) +{ + struct nhg_hash_entry *nhe = NULL; + uint32_t id = nhg_ctx_get_id(ctx); + + nhe = zebra_nhg_lookup_id(id); + + if (!nhe) { + flog_warn( + EC_ZEBRA_BAD_NHG_MESSAGE, + "Kernel delete message received for nexthop group ID (%u) that we do not have in our ID table", + id); + return -1; + } + + zebra_nhg_handle_kernel_state_change(nhe, true); + + return 0; +} + +static void nhg_ctx_fini(struct nhg_ctx **ctx) +{ + /* + * Just freeing for now, maybe do something more in the future + * based on flag. + */ + + nhg_ctx_free(ctx); +} + +static int queue_add(struct nhg_ctx *ctx) +{ + /* If its queued or already processed do nothing */ + if (nhg_ctx_get_status(ctx) == NHG_CTX_QUEUED) + return 0; + + if (rib_queue_nhg_add(ctx)) { + nhg_ctx_set_status(ctx, NHG_CTX_FAILURE); + return -1; + } + + nhg_ctx_set_status(ctx, NHG_CTX_QUEUED); + + return 0; +} + +int nhg_ctx_process(struct nhg_ctx *ctx) +{ + int ret = 0; + + switch (nhg_ctx_get_op(ctx)) { + case NHG_CTX_OP_NEW: + ret = nhg_ctx_process_new(ctx); + if (nhg_ctx_get_count(ctx) && ret == -ENOENT + && nhg_ctx_get_status(ctx) != NHG_CTX_REQUEUED) { + /** + * We have entered a situation where we are + * processing a group from the kernel + * that has a contained nexthop which + * we have not yet processed. + * + * Re-enqueue this ctx to be handled exactly one + * more time (indicated by the flag). + * + * By the time we get back to it, we + * should have processed its depends. + */ + nhg_ctx_set_status(ctx, NHG_CTX_NONE); + if (queue_add(ctx) == 0) { + nhg_ctx_set_status(ctx, NHG_CTX_REQUEUED); + return 0; + } + } + break; + case NHG_CTX_OP_DEL: + ret = nhg_ctx_process_del(ctx); + case NHG_CTX_OP_NONE: + break; + } + + nhg_ctx_set_status(ctx, (ret ? NHG_CTX_FAILURE : NHG_CTX_SUCCESS)); + + nhg_ctx_fini(&ctx); + + return ret; +} + +/* Kernel-side, you either get a single new nexthop or a array of ID's */ +int zebra_nhg_kernel_find(uint32_t id, struct nexthop *nh, struct nh_grp *grp, + uint8_t count, vrf_id_t vrf_id, afi_t afi, int type, + int startup) +{ + struct nhg_ctx *ctx = NULL; + + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: nh %pNHv, id %u, count %d", + __func__, nh, id, (int)count); + + if (id > id_counter) + /* Increase our counter so we don't try to create + * an ID that already exists + */ + id_counter = id; + + ctx = nhg_ctx_init(id, nh, grp, vrf_id, afi, type, count); + nhg_ctx_set_op(ctx, NHG_CTX_OP_NEW); + + /* Under statup conditions, we need to handle them immediately + * like we do for routes. Otherwise, we are going to get a route + * with a nhe_id that we have not handled. + */ + if (startup) + return nhg_ctx_process(ctx); + + if (queue_add(ctx)) { + nhg_ctx_fini(&ctx); + return -1; + } + + return 0; +} + +/* Kernel-side, received delete message */ +int zebra_nhg_kernel_del(uint32_t id, vrf_id_t vrf_id) +{ + struct nhg_ctx *ctx = NULL; + + ctx = nhg_ctx_init(id, NULL, NULL, vrf_id, 0, 0, 0); + + nhg_ctx_set_op(ctx, NHG_CTX_OP_DEL); + + if (queue_add(ctx)) { + nhg_ctx_fini(&ctx); + return -1; + } + + return 0; +} + +/* Some dependency helper functions */ +static struct nhg_hash_entry *depends_find_recursive(const struct nexthop *nh, + afi_t afi) +{ + struct nhg_hash_entry *nhe; + struct nexthop *lookup = NULL; + + lookup = nexthop_dup(nh, NULL); + + nhe = zebra_nhg_find_nexthop(0, lookup, afi, 0); + + nexthops_free(lookup); + + return nhe; +} + +static struct nhg_hash_entry *depends_find_singleton(const struct nexthop *nh, + afi_t afi) +{ + struct nhg_hash_entry *nhe; + struct nexthop lookup = {}; + + /* Capture a snapshot of this single nh; it might be part of a list, + * so we need to make a standalone copy. + */ + nexthop_copy_no_recurse(&lookup, nh, NULL); + + nhe = zebra_nhg_find_nexthop(0, &lookup, afi, 0); + + /* The copy may have allocated labels; free them if necessary. */ + nexthop_del_labels(&lookup); + + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: nh %pNHv => %p (%u)", + __func__, nh, nhe, nhe ? nhe->id : 0); + + return nhe; +} + +static struct nhg_hash_entry *depends_find(const struct nexthop *nh, afi_t afi) +{ + struct nhg_hash_entry *nhe = NULL; + + if (!nh) + goto done; + + /* We are separating these functions out to increase handling speed + * in the non-recursive case (by not alloc/freeing) + */ + if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_RECURSIVE)) + nhe = depends_find_recursive(nh, afi); + else + nhe = depends_find_singleton(nh, afi); + + + if (IS_ZEBRA_DEBUG_NHG_DETAIL) { + zlog_debug("%s: nh %pNHv %s => %p (%u)", __func__, nh, + CHECK_FLAG(nh->flags, NEXTHOP_FLAG_RECURSIVE) ? "(R)" + : "", + nhe, nhe ? nhe->id : 0); + } + +done: + return nhe; +} + +static void depends_add(struct nhg_connected_tree_head *head, + struct nhg_hash_entry *depend) +{ + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: head %p nh %pNHv", + __func__, head, depend->nhg.nexthop); + + /* If NULL is returned, it was successfully added and + * needs to have its refcnt incremented. + * + * Else the NHE is already present in the tree and doesn't + * need to increment the refcnt. + */ + if (nhg_connected_tree_add_nhe(head, depend) == NULL) + zebra_nhg_increment_ref(depend); +} + +static struct nhg_hash_entry * +depends_find_add(struct nhg_connected_tree_head *head, struct nexthop *nh, + afi_t afi) +{ + struct nhg_hash_entry *depend = NULL; + + depend = depends_find(nh, afi); + + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: nh %pNHv => %p", + __func__, nh, depend); + + if (depend) + depends_add(head, depend); + + return depend; +} + +static struct nhg_hash_entry * +depends_find_id_add(struct nhg_connected_tree_head *head, uint32_t id) +{ + struct nhg_hash_entry *depend = NULL; + + depend = zebra_nhg_lookup_id(id); + + if (depend) + depends_add(head, depend); + + return depend; +} + +static void depends_decrement_free(struct nhg_connected_tree_head *head) +{ + nhg_connected_tree_decrement_ref(head); + nhg_connected_tree_free(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 *nhe = NULL; + vrf_id_t vrf_id; + + /* + * CLANG SA is complaining that nexthop may be NULL + * Make it happy but this is ridonc + */ + 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); + + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: => nhe %p (%u)", + __func__, nhe, nhe ? nhe->id : 0); + + return nhe; +} + +/* Find an nhe based on a route's nhe */ +struct nhg_hash_entry * +zebra_nhg_rib_find_nhe(struct nhg_hash_entry *rt_nhe, afi_t rt_afi) +{ + struct nhg_hash_entry *nhe = NULL; + + if (!(rt_nhe && rt_nhe->nhg.nexthop)) { + flog_err(EC_ZEBRA_TABLE_LOOKUP_FAILED, + "No nexthop passed to %s", __func__); + return NULL; + } + + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: rt_nhe %p (%u)", __func__, rt_nhe, rt_nhe->id); + + zebra_nhe_find(&nhe, rt_nhe, NULL, rt_afi); + + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: => nhe %p (%u)", + __func__, nhe, nhe ? nhe->id : 0); + + return nhe; +} + +/* + * Allocate backup nexthop info object. Typically these are embedded in + * nhg_hash_entry objects. + */ +struct nhg_backup_info *zebra_nhg_backup_alloc(void) +{ + struct nhg_backup_info *p; + + p = XCALLOC(MTYPE_NHG, sizeof(struct nhg_backup_info)); + + p->nhe = zebra_nhg_alloc(); + + /* Identify the embedded group used to hold the list of backups */ + SET_FLAG(p->nhe->flags, NEXTHOP_GROUP_BACKUP); + + return p; +} + +/* + * Free backup nexthop info object, deal with any embedded allocations + */ +void zebra_nhg_backup_free(struct nhg_backup_info **p) +{ + if (p && *p) { + if ((*p)->nhe) + zebra_nhg_free((*p)->nhe); + + XFREE(MTYPE_NHG, (*p)); + } +} + +/* Accessor for backup nexthop group */ +struct nexthop_group *zebra_nhg_get_backup_nhg(struct nhg_hash_entry *nhe) +{ + struct nexthop_group *p = NULL; + + if (nhe) { + if (nhe->backup_info && nhe->backup_info->nhe) + p = &(nhe->backup_info->nhe->nhg); + } + + return p; +} + +/* + * Helper to return a copy of a backup_info - note that this is a shallow + * copy, meant to be used when creating a new nhe from info passed in with + * a route e.g. + */ +static struct nhg_backup_info * +nhg_backup_copy(const struct nhg_backup_info *orig) +{ + struct nhg_backup_info *b; + + b = zebra_nhg_backup_alloc(); + + /* Copy list of nexthops */ + nexthop_group_copy(&(b->nhe->nhg), &(orig->nhe->nhg)); + + return b; +} + +static void zebra_nhg_free_members(struct nhg_hash_entry *nhe) +{ + nexthops_free(nhe->nhg.nexthop); + + zebra_nhg_backup_free(&nhe->backup_info); + + /* Decrement to remove connection ref */ + nhg_connected_tree_decrement_ref(&nhe->nhg_depends); + nhg_connected_tree_free(&nhe->nhg_depends); + nhg_connected_tree_free(&nhe->nhg_dependents); +} + +void zebra_nhg_free(struct nhg_hash_entry *nhe) +{ + if (IS_ZEBRA_DEBUG_NHG_DETAIL) { + /* Group or singleton? */ + if (nhe->nhg.nexthop && nhe->nhg.nexthop->next) + zlog_debug("%s: nhe %p (%u), refcnt %d", + __func__, nhe, nhe->id, nhe->refcnt); + else + zlog_debug("%s: nhe %p (%u), refcnt %d, NH %pNHv", + __func__, nhe, nhe->id, nhe->refcnt, + nhe->nhg.nexthop); + } + + if (nhe->refcnt) + zlog_debug("nhe_id=%u hash refcnt=%d", nhe->id, nhe->refcnt); + + zebra_nhg_free_members(nhe); + + XFREE(MTYPE_NHG, nhe); +} + +void zebra_nhg_hash_free(void *p) +{ + zebra_nhg_free((struct nhg_hash_entry *)p); +} + +void zebra_nhg_decrement_ref(struct nhg_hash_entry *nhe) +{ + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: nhe %p (%u) %d => %d", + __func__, nhe, nhe->id, nhe->refcnt, + nhe->refcnt - 1); + + nhe->refcnt--; + + if (!zebra_nhg_depends_is_empty(nhe)) + nhg_connected_tree_decrement_ref(&nhe->nhg_depends); + + if (ZEBRA_NHG_CREATED(nhe) && nhe->refcnt <= 0) + zebra_nhg_uninstall_kernel(nhe); +} + +void zebra_nhg_increment_ref(struct nhg_hash_entry *nhe) +{ + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: nhe %p (%u) %d => %d", + __func__, nhe, nhe->id, nhe->refcnt, + nhe->refcnt + 1); + + nhe->refcnt++; + + if (!zebra_nhg_depends_is_empty(nhe)) + nhg_connected_tree_increment_ref(&nhe->nhg_depends); +} + +static void nexthop_set_resolved(afi_t afi, const struct nexthop *newhop, + struct nexthop *nexthop, + struct zebra_sr_policy *policy) +{ + struct nexthop *resolved_hop; + uint8_t num_labels = 0; + mpls_label_t labels[MPLS_MAX_LABELS]; + enum lsp_types_t label_type = ZEBRA_LSP_NONE; + int i = 0; + + resolved_hop = nexthop_new(); + SET_FLAG(resolved_hop->flags, NEXTHOP_FLAG_ACTIVE); + + resolved_hop->vrf_id = nexthop->vrf_id; + switch (newhop->type) { + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + /* If the resolving route specifies a gateway, use it */ + resolved_hop->type = newhop->type; + resolved_hop->gate.ipv4 = newhop->gate.ipv4; + + if (newhop->ifindex) { + resolved_hop->type = NEXTHOP_TYPE_IPV4_IFINDEX; + resolved_hop->ifindex = newhop->ifindex; + } + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + resolved_hop->type = newhop->type; + resolved_hop->gate.ipv6 = newhop->gate.ipv6; + + if (newhop->ifindex) { + resolved_hop->type = NEXTHOP_TYPE_IPV6_IFINDEX; + resolved_hop->ifindex = newhop->ifindex; + } + break; + case NEXTHOP_TYPE_IFINDEX: + /* If the resolving route is an interface route, + * it means the gateway we are looking up is connected + * to that interface. (The actual network is _not_ onlink). + * Therefore, the resolved route should have the original + * gateway as nexthop as it is directly connected. + * + * On Linux, we have to set the onlink netlink flag because + * otherwise, the kernel won't accept the route. + */ + resolved_hop->flags |= NEXTHOP_FLAG_ONLINK; + if (afi == AFI_IP) { + resolved_hop->type = NEXTHOP_TYPE_IPV4_IFINDEX; + resolved_hop->gate.ipv4 = nexthop->gate.ipv4; + } else if (afi == AFI_IP6) { + resolved_hop->type = NEXTHOP_TYPE_IPV6_IFINDEX; + resolved_hop->gate.ipv6 = nexthop->gate.ipv6; + } + resolved_hop->ifindex = newhop->ifindex; + break; + case NEXTHOP_TYPE_BLACKHOLE: + resolved_hop->type = NEXTHOP_TYPE_BLACKHOLE; + resolved_hop->bh_type = newhop->bh_type; + break; + } + + if (newhop->flags & NEXTHOP_FLAG_ONLINK) + resolved_hop->flags |= NEXTHOP_FLAG_ONLINK; + + /* Copy labels of the resolved route and the parent resolving to it */ + if (policy) { + int i = 0; + + /* + * Don't push the first SID if the corresponding action in the + * LFIB is POP. + */ + if (!newhop->nh_label || !newhop->nh_label->num_labels + || newhop->nh_label->label[0] == MPLS_LABEL_IMPLICIT_NULL) + i = 1; + + for (; i < policy->segment_list.label_num; i++) + labels[num_labels++] = policy->segment_list.labels[i]; + label_type = policy->segment_list.type; + } else if (newhop->nh_label) { + for (i = 0; i < newhop->nh_label->num_labels; i++) { + /* Be a bit picky about overrunning the local array */ + if (num_labels >= MPLS_MAX_LABELS) { + if (IS_ZEBRA_DEBUG_NHG || IS_ZEBRA_DEBUG_RIB) + zlog_debug("%s: too many labels in newhop %pNHv", + __func__, newhop); + break; + } + labels[num_labels++] = newhop->nh_label->label[i]; + } + /* Use the "outer" type */ + label_type = newhop->nh_label_type; + } + + if (nexthop->nh_label) { + for (i = 0; i < nexthop->nh_label->num_labels; i++) { + /* Be a bit picky about overrunning the local array */ + if (num_labels >= MPLS_MAX_LABELS) { + if (IS_ZEBRA_DEBUG_NHG || IS_ZEBRA_DEBUG_RIB) + zlog_debug("%s: too many labels in nexthop %pNHv", + __func__, nexthop); + break; + } + labels[num_labels++] = nexthop->nh_label->label[i]; + } + + /* If the parent has labels, use its type if + * we don't already have one. + */ + if (label_type == ZEBRA_LSP_NONE) + label_type = nexthop->nh_label_type; + } + + if (num_labels) + nexthop_add_labels(resolved_hop, label_type, num_labels, + labels); + + resolved_hop->rparent = nexthop; + _nexthop_add(&nexthop->resolved, resolved_hop); +} + +/* Checks if nexthop we are trying to resolve to is valid */ +static bool nexthop_valid_resolve(const struct nexthop *nexthop, + const struct nexthop *resolved) +{ + /* Can't resolve to a recursive nexthop */ + if (CHECK_FLAG(resolved->flags, NEXTHOP_FLAG_RECURSIVE)) + return false; + + /* Must be ACTIVE */ + if (!CHECK_FLAG(resolved->flags, NEXTHOP_FLAG_ACTIVE)) + return false; + + /* Must not be duplicate */ + if (CHECK_FLAG(resolved->flags, NEXTHOP_FLAG_DUPLICATE)) + return false; + + switch (nexthop->type) { + case NEXTHOP_TYPE_IPV4_IFINDEX: + case NEXTHOP_TYPE_IPV6_IFINDEX: + /* If the nexthop we are resolving to does not match the + * ifindex for the nexthop the route wanted, its not valid. + */ + if (nexthop->ifindex != resolved->ifindex) + return false; + break; + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IFINDEX: + case NEXTHOP_TYPE_BLACKHOLE: + break; + } + + return true; +} + +/* + * Given a nexthop we need to properly recursively resolve + * the route. As such, do a table lookup to find and match + * if at all possible. Set the nexthop->ifindex and resolved_id + * as appropriate + */ +static int nexthop_active(afi_t afi, struct route_entry *re, + struct nexthop *nexthop, struct route_node *top) +{ + struct prefix p; + struct route_table *table; + struct route_node *rn; + struct route_entry *match = NULL; + int resolved; + zebra_nhlfe_t *nhlfe; + struct nexthop *newhop; + struct interface *ifp; + rib_dest_t *dest; + struct zebra_vrf *zvrf; + struct in_addr local_ipv4; + struct in_addr *ipv4; + + if ((nexthop->type == NEXTHOP_TYPE_IPV4) + || nexthop->type == NEXTHOP_TYPE_IPV6) + nexthop->ifindex = 0; + + + UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE); + nexthops_free(nexthop->resolved); + nexthop->resolved = NULL; + re->nexthop_mtu = 0; + + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: re %p, nexthop %pNHv", + __func__, re, nexthop); + + /* + * If the kernel has sent us a NEW route, then + * by golly gee whiz it's a good route. + * + * If its an already INSTALLED route we have already handled, then the + * kernel route's nexthop might have became unreachable + * and we have to handle that. + */ + if (!CHECK_FLAG(re->status, ROUTE_ENTRY_INSTALLED) + && (re->type == ZEBRA_ROUTE_KERNEL + || re->type == ZEBRA_ROUTE_SYSTEM)) + return 1; + + /* + * If the nexthop has been marked as 'onlink' we just need to make + * sure the nexthop's interface is known and is operational. + */ + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK)) { + ifp = if_lookup_by_index(nexthop->ifindex, nexthop->vrf_id); + if (!ifp) { + 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_RIB_DETAILED) + zlog_debug("nexthop %pNHv marked onlink but nhif %s is not operational", + nexthop, ifp->name); + return 0; + } + return 1; + } + + if ((top->p.family == AF_INET && top->p.prefixlen == 32 + && nexthop->gate.ipv4.s_addr == top->p.u.prefix4.s_addr) + || (top->p.family == AF_INET6 && top->p.prefixlen == 128 + && memcmp(&nexthop->gate.ipv6, &top->p.u.prefix6, 16) == 0)) { + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug( + " :%s: Attempting to install a max prefixlength route through itself", + __func__); + return 0; + } + + /* Validation for ipv4 mapped ipv6 nexthop. */ + if (IS_MAPPED_IPV6(&nexthop->gate.ipv6)) { + afi = AFI_IP; + ipv4 = &local_ipv4; + ipv4_mapped_ipv6_to_ipv4(&nexthop->gate.ipv6, ipv4); + } else { + ipv4 = &nexthop->gate.ipv4; + } + + if (nexthop->srte_color) { + struct ipaddr endpoint = {0}; + struct zebra_sr_policy *policy; + + switch (afi) { + case AFI_IP: + endpoint.ipa_type = IPADDR_V4; + endpoint.ipaddr_v4 = *ipv4; + break; + case AFI_IP6: + endpoint.ipa_type = IPADDR_V6; + endpoint.ipaddr_v6 = nexthop->gate.ipv6; + break; + default: + flog_err(EC_LIB_DEVELOPMENT, + "%s: unknown address-family: %u", __func__, + afi); + exit(1); + } + + policy = zebra_sr_policy_find(nexthop->srte_color, &endpoint); + if (policy && policy->status == ZEBRA_SR_POLICY_UP) { + resolved = 0; + frr_each_safe (nhlfe_list, &policy->lsp->nhlfe_list, + nhlfe) { + if (!CHECK_FLAG(nhlfe->flags, + NHLFE_FLAG_SELECTED) + || CHECK_FLAG(nhlfe->flags, + NHLFE_FLAG_DELETED)) + continue; + SET_FLAG(nexthop->flags, + NEXTHOP_FLAG_RECURSIVE); + nexthop_set_resolved(afi, nhlfe->nexthop, + nexthop, policy); + resolved = 1; + } + if (resolved) + return 1; + } + } + + /* Make lookup prefix. */ + memset(&p, 0, sizeof(struct prefix)); + switch (afi) { + case AFI_IP: + p.family = AF_INET; + p.prefixlen = IPV4_MAX_PREFIXLEN; + p.u.prefix4 = *ipv4; + break; + case AFI_IP6: + p.family = AF_INET6; + p.prefixlen = IPV6_MAX_PREFIXLEN; + p.u.prefix6 = nexthop->gate.ipv6; + break; + default: + assert(afi != AFI_IP && afi != AFI_IP6); + break; + } + /* Lookup table. */ + table = zebra_vrf_table(afi, SAFI_UNICAST, nexthop->vrf_id); + /* get zvrf */ + zvrf = zebra_vrf_lookup_by_id(nexthop->vrf_id); + if (!table || !zvrf) { + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug(" %s: Table not found", __func__); + return 0; + } + + rn = route_node_match(table, (struct prefix *)&p); + while (rn) { + route_unlock_node(rn); + + /* Lookup should halt if we've matched against ourselves ('top', + * if specified) - i.e., we cannot have a nexthop NH1 is + * resolved by a route NH1. The exception is if the route is a + * host route. + */ + if (rn == top) + if (((afi == AFI_IP) && (rn->p.prefixlen != 32)) + || ((afi == AFI_IP6) && (rn->p.prefixlen != 128))) { + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug( + " %s: Matched against ourself and prefix length is not max bit length", + __func__); + return 0; + } + + /* Pick up selected route. */ + /* However, do not resolve over default route unless explicitly + * allowed. + */ + if (is_default_prefix(&rn->p) + && !rnh_resolve_via_default(zvrf, p.family)) { + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug( + " :%s: Resolved against default route", + __func__); + return 0; + } + + dest = rib_dest_from_rnode(rn); + if (dest && dest->selected_fib + && !CHECK_FLAG(dest->selected_fib->status, + ROUTE_ENTRY_REMOVED) + && dest->selected_fib->type != ZEBRA_ROUTE_TABLE) + match = dest->selected_fib; + + /* If there is no selected route or matched route is EGP, go up + * tree. + */ + if (!match) { + do { + rn = rn->parent; + } while (rn && rn->info == NULL); + if (rn) + route_lock_node(rn); + + continue; + } + + if (match->type == ZEBRA_ROUTE_CONNECT) { + /* Directly point connected route. */ + newhop = match->nhe->nhg.nexthop; + if (newhop) { + if (nexthop->type == NEXTHOP_TYPE_IPV4 + || 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. + */ + return 0; + } + } + + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: CONNECT match %p (%u), newhop %pNHv", + __func__, match, + match->nhe->id, newhop); + + return 1; + } else if (CHECK_FLAG(re->flags, ZEBRA_FLAG_ALLOW_RECURSION)) { + struct nexthop_group *nhg; + + resolved = 0; + + /* Only useful if installed */ + if (!CHECK_FLAG(match->status, ROUTE_ENTRY_INSTALLED)) { + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug("%s: match %p (%u) not installed", + __func__, match, + match->nhe->id); + + goto done_with_match; + } + + /* Examine installed nexthops; note that there + * may not be any installed primary nexthops if + * only backups are installed. + */ + nhg = rib_get_fib_nhg(match); + for (ALL_NEXTHOPS_PTR(nhg, newhop)) { + if (!nexthop_valid_resolve(nexthop, newhop)) + continue; + + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: RECURSIVE match %p (%u), newhop %pNHv", + __func__, match, + match->nhe->id, newhop); + + SET_FLAG(nexthop->flags, + NEXTHOP_FLAG_RECURSIVE); + nexthop_set_resolved(afi, newhop, nexthop, + NULL); + resolved = 1; + } + + /* Examine installed backup nexthops, if any. There + * are only installed backups *if* there is a + * dedicated fib list. + */ + nhg = rib_get_fib_backup_nhg(match); + if (nhg == NULL || nhg->nexthop == NULL) + goto done_with_match; + + for (ALL_NEXTHOPS_PTR(nhg, newhop)) { + if (!nexthop_valid_resolve(nexthop, newhop)) + continue; + + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: RECURSIVE match backup %p (%u), newhop %pNHv", + __func__, match, + match->nhe->id, newhop); + + SET_FLAG(nexthop->flags, + NEXTHOP_FLAG_RECURSIVE); + nexthop_set_resolved(afi, newhop, nexthop, + NULL); + resolved = 1; + } +done_with_match: + if (resolved) + re->nexthop_mtu = match->mtu; + else if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug( + " %s: Recursion failed to find", + __func__); + + return resolved; + } else { + if (IS_ZEBRA_DEBUG_RIB_DETAILED) { + zlog_debug( + " %s: Route Type %s has not turned on recursion", + __func__, zebra_route_string(re->type)); + if (re->type == ZEBRA_ROUTE_BGP + && !CHECK_FLAG(re->flags, ZEBRA_FLAG_IBGP)) + zlog_debug( + " EBGP: see \"disable-ebgp-connected-route-check\" or \"disable-connected-check\""); + } + return 0; + } + } + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug(" %s: Nexthop did not lookup in table", + __func__); + return 0; +} + +/* This function verifies reachability of one given nexthop, which can be + * numbered or unnumbered, IPv4 or IPv6. The result is unconditionally stored + * in nexthop->flags field. The nexthop->ifindex will be updated + * appropriately as well. An existing route map can turn + * (otherwise active) nexthop into inactive, but not vice versa. + * + * If it finds a nexthop recursivedly, set the resolved_id + * to match that nexthop's nhg_hash_entry ID; + * + * The return value is the final value of 'ACTIVE' flag. + */ +static unsigned nexthop_active_check(struct route_node *rn, + struct route_entry *re, + struct nexthop *nexthop) +{ + struct interface *ifp; + route_map_result_t ret = RMAP_PERMITMATCH; + int family; + char buf[SRCDEST2STR_BUFFER]; + const struct prefix *p, *src_p; + struct zebra_vrf *zvrf; + + srcdest_rnode_prefixes(rn, &p, &src_p); + + if (rn->p.family == AF_INET) + family = AFI_IP; + else if (rn->p.family == AF_INET6) + family = AFI_IP6; + else + family = 0; + switch (nexthop->type) { + case NEXTHOP_TYPE_IFINDEX: + ifp = if_lookup_by_index(nexthop->ifindex, nexthop->vrf_id); + /* + * If the interface exists and its operative or its a kernel + * route and interface is up, its active. We trust kernel routes + * to be good. + */ + if (ifp + && (if_is_operative(ifp) + || (if_is_up(ifp) + && (re->type == ZEBRA_ROUTE_KERNEL + || re->type == ZEBRA_ROUTE_SYSTEM)))) + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); + else + UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); + break; + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + family = AFI_IP; + if (nexthop_active(AFI_IP, re, nexthop, rn)) + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); + else + UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); + break; + case NEXTHOP_TYPE_IPV6: + family = AFI_IP6; + if (nexthop_active(AFI_IP6, re, nexthop, rn)) + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); + else + UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); + break; + case NEXTHOP_TYPE_IPV6_IFINDEX: + /* RFC 5549, v4 prefix with v6 NH */ + if (rn->p.family != AF_INET) + family = AFI_IP6; + if (IN6_IS_ADDR_LINKLOCAL(&nexthop->gate.ipv6)) { + ifp = if_lookup_by_index(nexthop->ifindex, + nexthop->vrf_id); + if (ifp && if_is_operative(ifp)) + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); + else + UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); + } else { + if (nexthop_active(AFI_IP6, re, nexthop, rn)) + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); + else + UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); + } + break; + case NEXTHOP_TYPE_BLACKHOLE: + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); + break; + default: + break; + } + + if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) { + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug(" %s: Unable to find active nexthop", + __func__); + return 0; + } + + /* XXX: What exactly do those checks do? Do we support + * e.g. IPv4 routes with IPv6 nexthops or vice versa? + */ + if (RIB_SYSTEM_ROUTE(re) || (family == AFI_IP && p->family != AF_INET) + || (family == AFI_IP6 && p->family != AF_INET6)) + return CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); + + /* The original code didn't determine the family correctly + * e.g. for NEXTHOP_TYPE_IFINDEX. Retrieve the correct afi + * from the rib_table_info in those cases. + * Possibly it may be better to use only the rib_table_info + * in every case. + */ + if (!family) { + struct rib_table_info *info; + + info = srcdest_rnode_table_info(rn); + family = info->afi; + } + + memset(&nexthop->rmap_src.ipv6, 0, sizeof(union g_addr)); + + zvrf = zebra_vrf_lookup_by_id(nexthop->vrf_id); + if (!zvrf) { + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug(" %s: zvrf is NULL", __func__); + return CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); + } + + /* It'll get set if required inside */ + ret = zebra_route_map_check(family, re->type, re->instance, p, nexthop, + zvrf, re->tag); + if (ret == RMAP_DENYMATCH) { + if (IS_ZEBRA_DEBUG_RIB) { + srcdest_rnode2str(rn, buf, sizeof(buf)); + zlog_debug( + "%u:%s: Filtering out with NH out %s due to route map", + re->vrf_id, buf, + ifindex2ifname(nexthop->ifindex, + nexthop->vrf_id)); + } + UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); + } + return CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); +} + +/* Helper function called after resolution to walk nhg rb trees + * and toggle the NEXTHOP_GROUP_VALID flag if the nexthop + * is active on singleton NHEs. + */ +static bool zebra_nhg_set_valid_if_active(struct nhg_hash_entry *nhe) +{ + struct nhg_connected *rb_node_dep = NULL; + bool valid = false; + + if (!zebra_nhg_depends_is_empty(nhe)) { + /* Is at least one depend valid? */ + frr_each(nhg_connected_tree, &nhe->nhg_depends, rb_node_dep) { + if (zebra_nhg_set_valid_if_active(rb_node_dep->nhe)) + valid = true; + } + + goto done; + } + + /* should be fully resolved singleton at this point */ + if (CHECK_FLAG(nhe->nhg.nexthop->flags, NEXTHOP_FLAG_ACTIVE)) + valid = true; + +done: + if (valid) + SET_FLAG(nhe->flags, NEXTHOP_GROUP_VALID); + + return valid; +} + +/* + * Process a list of nexthops, given an nhg, determining + * whether each one is ACTIVE/installable at this time. + */ +static uint32_t nexthop_list_active_update(struct route_node *rn, + struct route_entry *re, + struct nexthop_group *nhg) +{ + union g_addr prev_src; + unsigned int prev_active, new_active; + ifindex_t prev_index; + uint32_t counter = 0; + struct nexthop *nexthop; + + nexthop = nhg->nexthop; + + /* Process nexthops one-by-one */ + for ( ; nexthop; nexthop = nexthop->next) { + + /* No protocol daemon provides src and so we're skipping + * tracking it + */ + prev_src = nexthop->rmap_src; + prev_active = CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); + prev_index = nexthop->ifindex; + /* + * We need to respect the multipath_num here + * as that what we should be able to install from + * a multipath perspective should not be a data plane + * decision point. + */ + new_active = + nexthop_active_check(rn, re, nexthop); + + if (new_active && counter >= zrouter.multipath_num) { + struct nexthop *nh; + + /* Set it and its resolved nexthop as inactive. */ + for (nh = nexthop; nh; nh = nh->resolved) + UNSET_FLAG(nh->flags, NEXTHOP_FLAG_ACTIVE); + + new_active = 0; + } + + if (new_active) + counter++; + + /* Don't allow src setting on IPv6 addr for now */ + if (prev_active != new_active || prev_index != nexthop->ifindex + || ((nexthop->type >= NEXTHOP_TYPE_IFINDEX + && nexthop->type < NEXTHOP_TYPE_IPV6) + && prev_src.ipv4.s_addr + != nexthop->rmap_src.ipv4.s_addr) + || ((nexthop->type >= NEXTHOP_TYPE_IPV6 + && nexthop->type < NEXTHOP_TYPE_BLACKHOLE) + && !(IPV6_ADDR_SAME(&prev_src.ipv6, + &nexthop->rmap_src.ipv6))) + || CHECK_FLAG(re->status, ROUTE_ENTRY_LABELS_CHANGED)) + SET_FLAG(re->status, ROUTE_ENTRY_CHANGED); + } + + return counter; +} + +/* + * Iterate over all nexthops of the given RIB entry and refresh their + * ACTIVE flag. If any nexthop is found to toggle the ACTIVE flag, + * the whole re structure is flagged with ROUTE_ENTRY_CHANGED. + * + * Return value is the new number of active nexthops. + */ +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; + + afi_t rt_afi = family2afi(rn->p.family); + + UNSET_FLAG(re->status, ROUTE_ENTRY_CHANGED); + + /* Make a local copy of the existing nhe, so we don't work on/modify + * the shared nhe. + */ + curr_nhe = zebra_nhe_copy(re->nhe, re->nhe->id); + + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: re %p nhe %p (%u), curr_nhe %p", + __func__, re, re->nhe, re->nhe->id, + curr_nhe); + + /* Clear the existing id, if any: this will avoid any confusion + * if the id exists, and will also force the creation + * of a new nhe reflecting the changes we may make in this local copy. + */ + curr_nhe->id = 0; + + /* Process nexthops */ + curr_active = nexthop_list_active_update(rn, re, &curr_nhe->nhg); + + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: re %p curr_active %u", __func__, re, + curr_active); + + /* If there are no backup nexthops, we are done */ + if (zebra_nhg_get_backup_nhg(curr_nhe) == NULL) + goto backups_done; + + backup_active = nexthop_list_active_update( + rn, re, zebra_nhg_get_backup_nhg(curr_nhe)); + + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: re %p backup_active %u", __func__, re, + backup_active); + +backups_done: + + /* + * Ref or create an nhe that matches the current state of the + * nexthop(s). + */ + if (CHECK_FLAG(re->status, ROUTE_ENTRY_CHANGED)) { + struct nhg_hash_entry *new_nhe = NULL; + + new_nhe = zebra_nhg_rib_find_nhe(curr_nhe, rt_afi); + + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: re %p CHANGED: nhe %p (%u) => new_nhe %p (%u)", + __func__, re, re->nhe, + re->nhe->id, new_nhe, new_nhe->id); + + route_entry_update_nhe(re, new_nhe); + } + + + /* Walk the NHE depends tree and toggle NEXTHOP_GROUP_VALID + * flag where appropriate. + */ + if (curr_active) + zebra_nhg_set_valid_if_active(re->nhe); + + /* + * Do not need the old / copied nhe anymore since it + * was either copied over into a new nhe or not + * used at all. + */ + zebra_nhg_free(curr_nhe); + return curr_active; +} + +/* Recursively construct a grp array of fully resolved IDs. + * + * This function allows us to account for groups within groups, + * by converting them into a flat array of IDs. + * + * nh_grp is modified at every level of recursion to append + * to it the next unique, fully resolved ID from the entire tree. + * + * + * Note: + * I'm pretty sure we only allow ONE level of group within group currently. + * But making this recursive just in case that ever changes. + */ +static uint8_t zebra_nhg_nhe2grp_internal(struct nh_grp *grp, + uint8_t curr_index, + struct nhg_hash_entry *nhe, + int max_num) +{ + struct nhg_connected *rb_node_dep = NULL; + struct nhg_hash_entry *depend = NULL; + uint8_t i = curr_index; + + frr_each(nhg_connected_tree, &nhe->nhg_depends, rb_node_dep) { + bool duplicate = false; + + if (i >= max_num) + goto done; + + depend = rb_node_dep->nhe; + + /* + * If its recursive, use its resolved nhe in the group + */ + if (CHECK_FLAG(depend->flags, NEXTHOP_GROUP_RECURSIVE)) { + depend = zebra_nhg_resolve(depend); + if (!depend) { + flog_err( + EC_ZEBRA_NHG_FIB_UPDATE, + "Failed to recursively resolve Nexthop Hash Entry in the group id=%u", + nhe->id); + continue; + } + } + + if (!zebra_nhg_depends_is_empty(depend)) { + /* This is a group within a group */ + i = zebra_nhg_nhe2grp_internal(grp, i, depend, max_num); + } else { + if (!CHECK_FLAG(depend->flags, NEXTHOP_GROUP_VALID)) { + if (IS_ZEBRA_DEBUG_RIB_DETAILED + || IS_ZEBRA_DEBUG_NHG) + zlog_debug( + "%s: Nexthop ID (%u) not valid, not appending to dataplane install group", + __func__, depend->id); + continue; + } + + /* If the nexthop not installed/queued for install don't + * put in the ID array. + */ + if (!(CHECK_FLAG(depend->flags, NEXTHOP_GROUP_INSTALLED) + || CHECK_FLAG(depend->flags, + NEXTHOP_GROUP_QUEUED))) { + if (IS_ZEBRA_DEBUG_RIB_DETAILED + || IS_ZEBRA_DEBUG_NHG) + zlog_debug( + "%s: Nexthop ID (%u) not installed or queued for install, not appending to dataplane install group", + __func__, depend->id); + continue; + } + + /* Check for duplicate IDs, ignore if found. */ + for (int j = 0; j < i; j++) { + if (depend->id == grp[j].id) { + duplicate = true; + break; + } + } + + if (duplicate) { + if (IS_ZEBRA_DEBUG_RIB_DETAILED + || IS_ZEBRA_DEBUG_NHG) + zlog_debug( + "%s: Nexthop ID (%u) is duplicate, not appending to dataplane install group", + __func__, depend->id); + continue; + } + + grp[i].id = depend->id; + grp[i].weight = depend->nhg.nexthop->weight; + i++; + } + } + + if (nhe->backup_info == NULL || nhe->backup_info->nhe == NULL) + goto done; + + /* TODO -- For now, we are not trying to use or install any + * backup info in this nexthop-id path: we aren't prepared + * to use the backups here yet. We're just debugging what we find. + */ + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: skipping backup nhe", __func__); + +done: + return i; +} + +/* Convert a nhe into a group array */ +uint8_t zebra_nhg_nhe2grp(struct nh_grp *grp, struct nhg_hash_entry *nhe, + int max_num) +{ + /* Call into the recursive function */ + return zebra_nhg_nhe2grp_internal(grp, 0, nhe, max_num); +} + +void zebra_nhg_install_kernel(struct nhg_hash_entry *nhe) +{ + struct nhg_connected *rb_node_dep = NULL; + + /* Resolve it first */ + nhe = zebra_nhg_resolve(nhe); + + /* Make sure all depends are installed/queued */ + frr_each(nhg_connected_tree, &nhe->nhg_depends, rb_node_dep) { + zebra_nhg_install_kernel(rb_node_dep->nhe); + } + + if (CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_VALID) + && !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; + + int ret = dplane_nexthop_add(nhe); + + switch (ret) { + case ZEBRA_DPLANE_REQUEST_QUEUED: + SET_FLAG(nhe->flags, NEXTHOP_GROUP_QUEUED); + break; + case ZEBRA_DPLANE_REQUEST_FAILURE: + flog_err( + EC_ZEBRA_DP_INSTALL_FAIL, + "Failed to install Nexthop ID (%u) into the kernel", + nhe->id); + break; + case ZEBRA_DPLANE_REQUEST_SUCCESS: + SET_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED); + zebra_nhg_handle_install(nhe); + break; + } + } +} + +void zebra_nhg_uninstall_kernel(struct nhg_hash_entry *nhe) +{ + if (CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED)) { + int ret = dplane_nexthop_delete(nhe); + + switch (ret) { + case ZEBRA_DPLANE_REQUEST_QUEUED: + SET_FLAG(nhe->flags, NEXTHOP_GROUP_QUEUED); + break; + case ZEBRA_DPLANE_REQUEST_FAILURE: + flog_err( + EC_ZEBRA_DP_DELETE_FAIL, + "Failed to uninstall Nexthop ID (%u) from the kernel", + nhe->id); + break; + case ZEBRA_DPLANE_REQUEST_SUCCESS: + UNSET_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED); + break; + } + } + + zebra_nhg_handle_uninstall(nhe); +} + +void zebra_nhg_dplane_result(struct zebra_dplane_ctx *ctx) +{ + enum dplane_op_e op; + enum zebra_dplane_result status; + uint32_t id = 0; + struct nhg_hash_entry *nhe = NULL; + + op = dplane_ctx_get_op(ctx); + status = dplane_ctx_get_status(ctx); + + id = dplane_ctx_get_nhe_id(ctx); + + if (IS_ZEBRA_DEBUG_DPLANE_DETAIL || IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug( + "Nexthop dplane ctx %p, op %s, nexthop ID (%u), result %s", + ctx, dplane_op2str(op), id, dplane_res2str(status)); + + switch (op) { + case DPLANE_OP_NH_DELETE: + if (status != ZEBRA_DPLANE_REQUEST_SUCCESS) + flog_err( + EC_ZEBRA_DP_DELETE_FAIL, + "Failed to uninstall Nexthop ID (%u) from the kernel", + id); + /* We already free'd the data, nothing to do */ + break; + case DPLANE_OP_NH_INSTALL: + case DPLANE_OP_NH_UPDATE: + nhe = zebra_nhg_lookup_id(id); + + if (!nhe) { + flog_err( + EC_ZEBRA_NHG_SYNC, + "%s operation preformed on Nexthop ID (%u) in the kernel, that we no longer have in our table", + dplane_op2str(op), id); + break; + } + + UNSET_FLAG(nhe->flags, NEXTHOP_GROUP_QUEUED); + if (status == ZEBRA_DPLANE_REQUEST_SUCCESS) { + SET_FLAG(nhe->flags, NEXTHOP_GROUP_VALID); + SET_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED); + zebra_nhg_handle_install(nhe); + } else + flog_err( + EC_ZEBRA_DP_INSTALL_FAIL, + "Failed to install Nexthop ID (%u) into the kernel", + nhe->id); + break; + case DPLANE_OP_ROUTE_INSTALL: + case DPLANE_OP_ROUTE_UPDATE: + case DPLANE_OP_ROUTE_DELETE: + case DPLANE_OP_ROUTE_NOTIFY: + case DPLANE_OP_LSP_INSTALL: + case DPLANE_OP_LSP_UPDATE: + case DPLANE_OP_LSP_DELETE: + case DPLANE_OP_LSP_NOTIFY: + case DPLANE_OP_PW_INSTALL: + case DPLANE_OP_PW_UNINSTALL: + case DPLANE_OP_SYS_ROUTE_ADD: + case DPLANE_OP_SYS_ROUTE_DELETE: + case DPLANE_OP_ADDR_INSTALL: + case DPLANE_OP_ADDR_UNINSTALL: + case DPLANE_OP_MAC_INSTALL: + case DPLANE_OP_MAC_DELETE: + case DPLANE_OP_NEIGH_INSTALL: + case DPLANE_OP_NEIGH_UPDATE: + case DPLANE_OP_NEIGH_DELETE: + case DPLANE_OP_VTEP_ADD: + case DPLANE_OP_VTEP_DELETE: + case DPLANE_OP_RULE_ADD: + case DPLANE_OP_RULE_DELETE: + case DPLANE_OP_RULE_UPDATE: + case DPLANE_OP_NEIGH_DISCOVER: + case DPLANE_OP_NONE: + break; + } + + dplane_ctx_fini(&ctx); +} + +static void zebra_nhg_sweep_entry(struct hash_bucket *bucket, void *arg) +{ + struct nhg_hash_entry *nhe = NULL; + + nhe = (struct nhg_hash_entry *)bucket->data; + + /* If its being ref'd, just let it be uninstalled via a route removal */ + if (ZEBRA_NHG_CREATED(nhe) && nhe->refcnt <= 0) + zebra_nhg_uninstall_kernel(nhe); +} + +void zebra_nhg_sweep_table(struct hash *hash) +{ + hash_iterate(hash, zebra_nhg_sweep_entry, NULL); +} + +static void zebra_nhg_mark_keep_entry(struct hash_bucket *bucket, void *arg) +{ + struct nhg_hash_entry *nhe = bucket->data; + + UNSET_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED); +} + +/* + * When we are shutting down and we have retain mode enabled + * in zebra the process is to mark each vrf that it's + * routes should not be deleted. The problem with that + * is that shutdown actually free's up memory which + * causes the nexthop group's ref counts to go to zero + * we need a way to subtly tell the system to not remove + * the nexthop groups from the kernel at the same time. + * The easiest just looks like that we should not mark + * the nhg's as installed any more and when the ref count + * goes to zero we'll attempt to delete and do nothing + */ +void zebra_nhg_mark_keep(void) +{ + hash_iterate(zrouter.nhgs_id, zebra_nhg_mark_keep_entry, NULL); +} + +/* Global control to disable use of kernel nexthops, if available. We can't + * force the kernel to support nexthop ids, of course, but we can disable + * zebra's use of them, for testing e.g. By default, if the kernel supports + * nexthop ids, zebra uses them. + */ +void zebra_nhg_enable_kernel_nexthops(bool set) +{ + g_nexthops_enabled = set; +} + +bool zebra_nhg_kernel_nexthops_enabled(void) +{ + return g_nexthops_enabled; +} diff --git a/zebra/zebra_nhg.h b/zebra/zebra_nhg.h new file mode 100644 index 0000000000..649a6caa08 --- /dev/null +++ b/zebra/zebra_nhg.h @@ -0,0 +1,297 @@ +/* Zebra Nexthop Group header. + * Copyright (C) 2019 Cumulus Networks, Inc. + * Donald Sharp + * Stephen Worley + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ +#ifndef __ZEBRA_NHG_H__ +#define __ZEBRA_NHG_H__ + +#include "lib/nexthop.h" +#include "lib/nexthop_group.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* This struct is used exclusively for dataplane + * interaction via a dataplane context. + * + * It is designed to mimic the netlink nexthop_grp + * struct in include/linux/nexthop.h + */ +struct nh_grp { + uint32_t id; + uint8_t weight; +}; + +PREDECL_RBTREE_UNIQ(nhg_connected_tree); + +/* + * Hashtables containing nhg entries is in `zebra_router`. + */ +struct nhg_hash_entry { + uint32_t id; + afi_t afi; + vrf_id_t vrf_id; + int type; + + struct nexthop_group nhg; + + /* If supported, a mapping of backup nexthops. */ + struct nhg_backup_info *backup_info; + + /* If this is not a group, it + * will be a single nexthop + * and must have an interface + * associated with it. + * Otherwise, this will be null. + */ + struct interface *ifp; + + uint32_t refcnt; + uint32_t dplane_ref; + + uint32_t flags; + + /* Dependency tree for other entries. + * For instance a group with two + * nexthops will have two dependencies + * pointing to those nhg_hash_entries. + * + * Using a rb tree here to make lookups + * faster with ID's. + */ + struct nhg_connected_tree_head nhg_depends, nhg_dependents; + +/* + * Is this nexthop group valid, ie all nexthops are fully resolved. + * What is fully resolved? It's a nexthop that is either self contained + * and correct( ie no recursive pointer ) or a nexthop that is recursively + * resolved and correct. + */ +#define NEXTHOP_GROUP_VALID (1 << 0) +/* + * Has this nexthop group been installed? At this point in time, this + * means that the data-plane has been told about this nexthop group + * and it's possible usage by a route entry. + */ +#define NEXTHOP_GROUP_INSTALLED (1 << 1) +/* + * Has the nexthop group been queued to be send to the FIB? + * The NEXTHOP_GROUP_VALID flag should also be set by this point. + */ +#define NEXTHOP_GROUP_QUEUED (1 << 2) +/* + * 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) + +/* + * Track FPM installation status.. + */ +#define NEXTHOP_GROUP_FPM (1 << 6) +}; + +/* Was this one we created, either this session or previously? */ +#define ZEBRA_NHG_CREATED(NHE) ((NHE->type) == ZEBRA_ROUTE_NHG) + +/* + * Backup nexthops: this is a group object itself, so + * that the backup nexthops can use the same code as a normal object. + */ +struct nhg_backup_info { + struct nhg_hash_entry *nhe; +}; + +enum nhg_ctx_op_e { + NHG_CTX_OP_NONE = 0, + NHG_CTX_OP_NEW, + NHG_CTX_OP_DEL, +}; + +enum nhg_ctx_status { + NHG_CTX_NONE = 0, + NHG_CTX_QUEUED, + NHG_CTX_REQUEUED, + NHG_CTX_SUCCESS, + NHG_CTX_FAILURE, +}; + +/* + * Context needed to queue nhg updates on the + * work queue. + */ +struct nhg_ctx { + + /* Unique ID */ + uint32_t id; + + vrf_id_t vrf_id; + afi_t afi; + /* + * This should only every be ZEBRA_ROUTE_NHG unless we get a a kernel + * created nexthop not made by us. + */ + int type; + + /* If its a group array, how many? */ + uint8_t count; + + /* Its either a single nexthop or an array of ID's */ + union { + struct nexthop nh; + struct nh_grp grp[MULTIPATH_NUM]; + } u; + + enum nhg_ctx_op_e op; + enum nhg_ctx_status status; +}; + +/* Global control to disable use of kernel nexthops, if available. We can't + * force the kernel to support nexthop ids, of course, but we can disable + * zebra's use of them, for testing e.g. By default, if the kernel supports + * nexthop ids, zebra uses them. + */ +void zebra_nhg_enable_kernel_nexthops(bool set); +bool zebra_nhg_kernel_nexthops_enabled(void); + +/** + * NHE abstracted tree functions. + * Use these where possible instead of direct access. + */ +struct nhg_hash_entry *zebra_nhg_alloc(void); +void zebra_nhg_free(struct nhg_hash_entry *nhe); +/* In order to clear a generic hash, we need a generic api, sigh. */ +void zebra_nhg_hash_free(void *p); + +/* Init an nhe, for use in a hash lookup for example. There's some fuzziness + * if the nhe represents only a single nexthop, so we try to capture that + * variant also. + */ +void zebra_nhe_init(struct nhg_hash_entry *nhe, afi_t afi, + const struct nexthop *nh); + +/* + * Shallow copy of 'orig', into new/allocated nhe. + */ +struct nhg_hash_entry *zebra_nhe_copy(const struct nhg_hash_entry *orig, + uint32_t id); + +/* Allocate, free backup nexthop info objects */ +struct nhg_backup_info *zebra_nhg_backup_alloc(void); +void zebra_nhg_backup_free(struct nhg_backup_info **p); + +struct nexthop_group *zebra_nhg_get_backup_nhg(struct nhg_hash_entry *nhe); + +extern struct nhg_hash_entry *zebra_nhg_resolve(struct nhg_hash_entry *nhe); + +extern unsigned int zebra_nhg_depends_count(const struct nhg_hash_entry *nhe); +extern bool zebra_nhg_depends_is_empty(const struct nhg_hash_entry *nhe); + +extern unsigned int +zebra_nhg_dependents_count(const struct nhg_hash_entry *nhe); +extern bool zebra_nhg_dependents_is_empty(const struct nhg_hash_entry *nhe); + +/* Lookup ID, doesn't create */ +extern struct nhg_hash_entry *zebra_nhg_lookup_id(uint32_t id); + +/* Hash functions */ +extern uint32_t zebra_nhg_hash_key(const void *arg); +extern uint32_t zebra_nhg_id_key(const void *arg); + +extern bool zebra_nhg_hash_equal(const void *arg1, const void *arg2); +extern bool zebra_nhg_hash_id_equal(const void *arg1, const void *arg2); + +/* + * Process a context off of a queue. + * Specifically this should be from + * the rib meta queue. + */ +extern int nhg_ctx_process(struct nhg_ctx *ctx); + +/* Find via kernel nh creation */ +extern int zebra_nhg_kernel_find(uint32_t id, struct nexthop *nh, + struct nh_grp *grp, uint8_t count, + vrf_id_t vrf_id, afi_t afi, int type, + int startup); +/* Del via kernel */ +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); + +/* 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); + +/* 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); + +/* Check validity of nhe, if invalid will update dependents as well */ +extern void zebra_nhg_check_valid(struct nhg_hash_entry *nhe); + +/* Convert nhe depends to a grp context that can be passed around safely */ +extern uint8_t zebra_nhg_nhe2grp(struct nh_grp *grp, struct nhg_hash_entry *nhe, + int size); + +/* Dataplane install/uninstall */ +extern void zebra_nhg_install_kernel(struct nhg_hash_entry *nhe); +extern void zebra_nhg_uninstall_kernel(struct nhg_hash_entry *nhe); + +/* Forward ref of dplane update context type */ +struct zebra_dplane_ctx; +extern void zebra_nhg_dplane_result(struct zebra_dplane_ctx *ctx); + + +/* Sweep the nhg hash tables for old entries on restart */ +extern void zebra_nhg_sweep_table(struct hash *hash); + +/* + * We are shutting down but the nexthops should be kept + * as that -r has been specified and we don't want to delete + * the routes unintentionally + */ +extern void zebra_nhg_mark_keep(void); + +/* Nexthop resolution processing */ +struct route_entry; /* Forward ref to avoid circular includes */ +extern int nexthop_active_update(struct route_node *rn, struct route_entry *re); + +#ifdef __cplusplus +} +#endif + +#endif /* __ZEBRA_NHG_H__ */ diff --git a/zebra/zebra_nhg_private.h b/zebra/zebra_nhg_private.h new file mode 100644 index 0000000000..25048258d5 --- /dev/null +++ b/zebra/zebra_nhg_private.h @@ -0,0 +1,81 @@ +/* + * Nexthop Group Private Functions. + * Copyright (C) 2019 Cumulus Networks, Inc. + * Stephen Worley + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * These functions should only be used internally for nhg_hash_entry + * manipulation and in certain special cases. + * + * Please use `zebra/zebra_nhg.h` for any general nhg_hash_entry api needs. + */ + +#ifndef __ZEBRA_NHG_PRIVATE_H__ +#define __ZEBRA_NHG_PRIVATE_H__ + +#include "zebra/zebra_nhg.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Abstraction for connected trees */ +struct nhg_connected { + struct nhg_connected_tree_item tree_item; + struct nhg_hash_entry *nhe; +}; + +static int nhg_connected_cmp(const struct nhg_connected *con1, + const struct nhg_connected *con2) +{ + return (con1->nhe->id - con2->nhe->id); +} + +DECLARE_RBTREE_UNIQ(nhg_connected_tree, struct nhg_connected, tree_item, + nhg_connected_cmp); + +/* nhg connected tree direct access functions */ +extern void nhg_connected_tree_init(struct nhg_connected_tree_head *head); +extern void nhg_connected_tree_free(struct nhg_connected_tree_head *head); +extern bool +nhg_connected_tree_is_empty(const struct nhg_connected_tree_head *head); +extern struct nhg_connected * +nhg_connected_tree_root(struct nhg_connected_tree_head *head); + +/* I realize _add/_del returns are backwords. + * + * Currently the list APIs are not standardized for what happens in + * the _del() function when the item isn't present. + * + * We are choosing to return NULL if not found in the _del case for now. + */ + +/* Delete NHE from the tree. On success, return the NHE, otherwise NULL. */ +extern struct nhg_hash_entry * +nhg_connected_tree_del_nhe(struct nhg_connected_tree_head *head, + struct nhg_hash_entry *nhe); +/* ADD NHE to the tree. On success, return NULL, otherwise return the NHE. */ +extern struct nhg_hash_entry * +nhg_connected_tree_add_nhe(struct nhg_connected_tree_head *head, + struct nhg_hash_entry *nhe); + +#ifdef __cplusplus +} +#endif + +#endif /* __ZEBRA_NHG_PRIVATE_H__ */ diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c index 0c743d8678..e9ff3fcc08 100644 --- a/zebra/zebra_ns.c +++ b/zebra/zebra_ns.c @@ -23,11 +23,9 @@ #include "lib/ns.h" #include "lib/vrf.h" -#include "lib/logicalrouter.h" #include "lib/prefix.h" #include "lib/memory.h" -#include "rtadv.h" #include "zebra_ns.h" #include "zebra_vrf.h" #include "zebra_memory.h" @@ -39,6 +37,7 @@ #include "zebra_pbr.h" #include "rib.h" #include "table_manager.h" +#include "zebra_errors.h" extern struct zebra_privs_t zserv_privs; @@ -46,7 +45,6 @@ DEFINE_MTYPE(ZEBRA, ZEBRA_NS, "Zebra Name Space") static struct zebra_ns *dzns; -static int logicalrouter_config_write(struct vty *vty); static int zebra_ns_disable_internal(struct zebra_ns *zns, bool complete); struct zebra_ns *zebra_ns_lookup(ns_id_t ns_id) @@ -67,6 +65,9 @@ static int zebra_ns_new(struct ns *ns) { struct zebra_ns *zns; + if (!ns) + return -1; + if (IS_ZEBRA_DEBUG_EVENT) zlog_info("ZNS %s with id %u (created)", ns->name, ns->ns_id); @@ -89,7 +90,7 @@ static int zebra_ns_delete(struct ns *ns) zlog_info("ZNS %s with id %u (deleted)", ns->name, ns->ns_id); if (!zns) return 0; - XFREE(MTYPE_ZEBRA_NS, zns); + XFREE(MTYPE_ZEBRA_NS, ns->info); return 0; } @@ -122,13 +123,10 @@ int zebra_ns_enable(ns_id_t ns_id, void **info) zns->ns_id = ns_id; -#if defined(HAVE_RTADV) - rtadv_init(zns); -#endif - kernel_init(zns); interface_list(zns); route_read(zns); + kernel_read_pbr_rules(zns); /* Initiate Table Manager per ZNS */ table_manager_enable(ns_id); @@ -142,9 +140,6 @@ int zebra_ns_enable(ns_id_t ns_id, void **info) static int zebra_ns_disable_internal(struct zebra_ns *zns, bool complete) { route_table_finish(zns->if_table); -#if defined(HAVE_RTADV) - rtadv_terminate(zns); -#endif kernel_terminate(zns, complete); @@ -158,20 +153,25 @@ static int zebra_ns_disable_internal(struct zebra_ns *zns, bool complete) /* During zebra shutdown, do partial cleanup while the async dataplane * is still running. */ -int zebra_ns_early_shutdown(struct ns *ns) +int zebra_ns_early_shutdown(struct ns *ns, + void *param_in __attribute__((unused)), + void **param_out __attribute__((unused))) { struct zebra_ns *zns = ns->info; if (zns == NULL) return 0; - return zebra_ns_disable_internal(zns, false); + zebra_ns_disable_internal(zns, false); + return NS_WALK_CONTINUE; } /* During zebra shutdown, do final cleanup * after all dataplane work is complete. */ -int zebra_ns_final_shutdown(struct ns *ns) +int zebra_ns_final_shutdown(struct ns *ns, + void *param_in __attribute__((unused)), + void **param_out __attribute__((unused))) { struct zebra_ns *zns = ns->info; @@ -180,26 +180,35 @@ int zebra_ns_final_shutdown(struct ns *ns) kernel_terminate(zns, true); - return 0; + return NS_WALK_CONTINUE; } int zebra_ns_init(const char *optional_default_name) { + struct ns *default_ns; ns_id_t ns_id; ns_id_t ns_id_external; + struct ns *ns; - dzns = zebra_ns_alloc(); - - frr_elevate_privs(&zserv_privs) { + frr_with_privs(&zserv_privs) { ns_id = zebra_ns_id_get_default(); } ns_id_external = ns_map_nsid_with_external(ns_id, true); ns_init_management(ns_id_external, ns_id); - - logicalrouter_init(logicalrouter_config_write); + ns = ns_get_default(); + if (ns) + ns->relative_default_ns = ns_id; + + default_ns = ns_lookup(NS_DEFAULT); + if (!default_ns) { + flog_err(EC_ZEBRA_NS_NO_DEFAULT, + "%s: failed to find default ns", __func__); + exit(EXIT_FAILURE); /* This is non-recoverable */ + } /* Do any needed per-NS data structure allocation. */ - dzns->if_table = route_table_init(); + zebra_ns_new(default_ns); + dzns = default_ns->info; /* Register zebra VRF callbacks, create and activate default VRF. */ zebra_vrf_init(); @@ -223,21 +232,6 @@ int zebra_ns_init(const char *optional_default_name) return 0; } -static int logicalrouter_config_write(struct vty *vty) -{ - struct ns *ns; - int write = 0; - - RB_FOREACH (ns, ns_head, &ns_tree) { - if (ns->ns_id == NS_DEFAULT || ns->name == NULL) - continue; - vty_out(vty, "logical-router %u netns %s\n", ns->ns_id, - ns->name); - write = 1; - } - return write; -} - int zebra_ns_config_write(struct vty *vty, struct ns *ns) { if (ns && ns->name != NULL) diff --git a/zebra/zebra_ns.h b/zebra/zebra_ns.h index dc79a83db0..f7d1f40782 100644 --- a/zebra/zebra_ns.h +++ b/zebra/zebra_ns.h @@ -67,9 +67,12 @@ struct zebra_ns *zebra_ns_lookup(ns_id_t ns_id); int zebra_ns_init(const char *optional_default_name); int zebra_ns_enable(ns_id_t ns_id, void **info); int zebra_ns_disabled(struct ns *ns); -int zebra_ns_early_shutdown(struct ns *ns); -int zebra_ns_final_shutdown(struct ns *ns); - +int zebra_ns_early_shutdown(struct ns *ns, + void *param_in __attribute__((unused)), + void **param_out __attribute__((unused))); +int zebra_ns_final_shutdown(struct ns *ns, + void *param_in __attribute__((unused)), + void **param_out __attribute__((unused))); int zebra_ns_config_write(struct vty *vty, struct ns *ns); #ifdef __cplusplus diff --git a/zebra/zebra_opaque.c b/zebra/zebra_opaque.c new file mode 100644 index 0000000000..41e278f71b --- /dev/null +++ b/zebra/zebra_opaque.c @@ -0,0 +1,722 @@ +/* + * Zebra opaque message handler module + * Copyright (c) 2020 Volta Networks, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#include +#include "lib/debug.h" +#include "lib/frr_pthread.h" +#include "lib/stream.h" +#include "zebra/debug.h" +#include "zebra/zserv.h" +#include "zebra/zebra_memory.h" +#include "zebra/zebra_opaque.h" + +/* Mem type */ +DEFINE_MTYPE_STATIC(ZEBRA, OPQ, "ZAPI Opaque Information"); + +/* Hash to hold message registration info from zapi clients */ +PREDECL_HASH(opq_regh); + +/* Registered client info */ +struct opq_client_reg { + int proto; + int instance; + uint32_t session_id; + + struct opq_client_reg *next; + struct opq_client_reg *prev; +}; + +/* Opaque message registration info */ +struct opq_msg_reg { + struct opq_regh_item item; + + /* Message type */ + uint32_t type; + + struct opq_client_reg *clients; +}; + +/* Registration helper prototypes */ +static uint32_t registration_hash(const struct opq_msg_reg *reg); +static int registration_compare(const struct opq_msg_reg *reg1, + const struct opq_msg_reg *reg2); + +DECLARE_HASH(opq_regh, struct opq_msg_reg, item, registration_compare, + registration_hash); + +static struct opq_regh_head opq_reg_hash; + +/* + * Globals + */ +static struct zebra_opaque_globals { + + /* Sentinel for run or start of shutdown */ + _Atomic uint32_t run; + + /* Limit number of pending, unprocessed updates */ + _Atomic uint32_t max_queued_updates; + + /* Limit number of new messages dequeued at once, to pace an + * incoming burst. + */ + uint32_t msgs_per_cycle; + + /* Stats: counters of incoming messages, errors, and yields (when + * the limit has been reached.) + */ + _Atomic uint32_t msgs_in; + _Atomic uint32_t msg_errors; + _Atomic uint32_t yields; + + /* pthread */ + struct frr_pthread *pthread; + + /* Event-delivery context 'master' for the module */ + struct thread_master *master; + + /* Event/'thread' pointer for queued zapi messages */ + struct thread *t_msgs; + + /* Input fifo queue to the module, and lock to protect it. */ + pthread_mutex_t mutex; + struct stream_fifo in_fifo; + +} zo_info; + +/* Name string for debugs/logs */ +static const char LOG_NAME[] = "Zebra Opaque"; + +/* Prototypes */ + +/* Main event loop, processing incoming message queue */ +static int process_messages(struct thread *event); +static int handle_opq_registration(const struct zmsghdr *hdr, + struct stream *msg); +static int handle_opq_unregistration(const struct zmsghdr *hdr, + struct stream *msg); +static int dispatch_opq_messages(struct stream_fifo *msg_fifo); +static struct opq_msg_reg *opq_reg_lookup(uint32_t type); +static bool opq_client_match(const struct opq_client_reg *client, + const struct zapi_opaque_reg_info *info); +static struct opq_msg_reg *opq_reg_alloc(uint32_t type); +static void opq_reg_free(struct opq_msg_reg **reg); +static struct opq_client_reg *opq_client_alloc( + const struct zapi_opaque_reg_info *info); +static void opq_client_free(struct opq_client_reg **client); +static const char *opq_client2str(char *buf, size_t buflen, + const struct opq_client_reg *client); + +/* + * Initialize the module at startup + */ +void zebra_opaque_init(void) +{ + memset(&zo_info, 0, sizeof(zo_info)); + + pthread_mutex_init(&zo_info.mutex, NULL); + stream_fifo_init(&zo_info.in_fifo); + + zo_info.msgs_per_cycle = ZEBRA_OPAQUE_MSG_LIMIT; +} + +/* + * Start the module pthread. This step is run later than the + * 'init' step, in case zebra has fork-ed. + */ +void zebra_opaque_start(void) +{ + struct frr_pthread_attr pattr = { + .start = frr_pthread_attr_default.start, + .stop = frr_pthread_attr_default.stop + }; + + if (IS_ZEBRA_DEBUG_EVENT) + zlog_debug("%s module starting", LOG_NAME); + + /* Start pthread */ + zo_info.pthread = frr_pthread_new(&pattr, "Zebra Opaque thread", + "zebra_opaque"); + + /* Associate event 'master' */ + zo_info.master = zo_info.pthread->master; + + atomic_store_explicit(&zo_info.run, 1, memory_order_relaxed); + + /* Enqueue an initial event for the pthread */ + thread_add_event(zo_info.master, process_messages, NULL, 0, + &zo_info.t_msgs); + + /* And start the pthread */ + frr_pthread_run(zo_info.pthread, NULL); +} + +/* + * Module stop, halting the dedicated pthread; called from the main pthread. + */ +void zebra_opaque_stop(void) +{ + if (IS_ZEBRA_DEBUG_EVENT) + zlog_debug("%s module stop", LOG_NAME); + + atomic_store_explicit(&zo_info.run, 0, memory_order_relaxed); + + frr_pthread_stop(zo_info.pthread, NULL); + + frr_pthread_destroy(zo_info.pthread); + + if (IS_ZEBRA_DEBUG_EVENT) + zlog_debug("%s module stop complete", LOG_NAME); +} + +/* + * Module final cleanup, called from the zebra main pthread. + */ +void zebra_opaque_finish(void) +{ + struct opq_msg_reg *reg; + struct opq_client_reg *client; + + if (IS_ZEBRA_DEBUG_EVENT) + zlog_debug("%s module shutdown", LOG_NAME); + + /* Clear out registration info */ + while ((reg = opq_regh_pop(&opq_reg_hash)) != NULL) { + client = reg->clients; + while (client) { + reg->clients = client->next; + opq_client_free(&client); + client = reg->clients; + } + + opq_reg_free(®); + } + + opq_regh_fini(&opq_reg_hash); + + pthread_mutex_destroy(&zo_info.mutex); + stream_fifo_deinit(&zo_info.in_fifo); +} + +/* + * Does this module handle (intercept) the specified zapi message type? + */ +bool zebra_opaque_handles_msgid(uint16_t id) +{ + bool ret = false; + + switch (id) { + case ZEBRA_OPAQUE_MESSAGE: + case ZEBRA_OPAQUE_REGISTER: + case ZEBRA_OPAQUE_UNREGISTER: + ret = true; + break; + default: + break; + } + + return ret; +} + +/* + * Enqueue a batch of messages for processing - this is the public api + * used from the zapi processing threads. + */ +uint32_t zebra_opaque_enqueue_batch(struct stream_fifo *batch) +{ + uint32_t counter = 0; + struct stream *msg; + + /* Dequeue messages from the incoming batch, and save them + * on the module fifo. + */ + frr_with_mutex(&zo_info.mutex) { + msg = stream_fifo_pop(batch); + while (msg) { + stream_fifo_push(&zo_info.in_fifo, msg); + counter++; + msg = stream_fifo_pop(batch); + } + } + + /* Schedule module pthread to process the batch */ + if (counter > 0) { + if (IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: received %u messages", + __func__, counter); + thread_add_event(zo_info.master, process_messages, NULL, 0, + &zo_info.t_msgs); + } + + return counter; +} + +/* + * Pthread event loop, process the incoming message queue. + */ +static int process_messages(struct thread *event) +{ + struct stream_fifo fifo; + struct stream *msg; + uint32_t i; + bool need_resched = false; + + stream_fifo_init(&fifo); + + /* Check for zebra shutdown */ + if (atomic_load_explicit(&zo_info.run, memory_order_relaxed) == 0) + goto done; + + /* + * Dequeue some messages from the incoming queue, temporarily + * save them on the local fifo + */ + frr_with_mutex(&zo_info.mutex) { + + for (i = 0; i < zo_info.msgs_per_cycle; i++) { + msg = stream_fifo_pop(&zo_info.in_fifo); + if (msg == NULL) + break; + + stream_fifo_push(&fifo, msg); + } + + /* + * We may need to reschedule, if there are still + * queued messages + */ + if (stream_fifo_head(&zo_info.in_fifo) != NULL) + need_resched = true; + } + + /* Update stats */ + atomic_fetch_add_explicit(&zo_info.msgs_in, i, memory_order_relaxed); + + /* Check for zebra shutdown */ + if (atomic_load_explicit(&zo_info.run, memory_order_relaxed) == 0) { + need_resched = false; + goto done; + } + + if (IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: processing %u messages", __func__, i); + + /* + * Process the messages from the temporary fifo. We send the whole + * fifo so that we can take advantage of batching internally. Note + * that registration/deregistration messages are handled here also. + */ + dispatch_opq_messages(&fifo); + +done: + + if (need_resched) { + atomic_fetch_add_explicit(&zo_info.yields, 1, + memory_order_relaxed); + thread_add_event(zo_info.master, process_messages, NULL, 0, + &zo_info.t_msgs); + } + + /* This will also free any leftover messages, in the shutdown case */ + stream_fifo_deinit(&fifo); + + return 0; +} + +/* + * Process (dispatch) or drop opaque messages. + */ +static int dispatch_opq_messages(struct stream_fifo *msg_fifo) +{ + struct stream *msg, *dup; + struct zmsghdr hdr; + struct zapi_opaque_msg info; + struct opq_msg_reg *reg; + int ret; + struct opq_client_reg *client; + struct zserv *zclient; + char buf[50]; + + while ((msg = stream_fifo_pop(msg_fifo)) != NULL) { + zapi_parse_header(msg, &hdr); + hdr.length -= ZEBRA_HEADER_SIZE; + + /* Handle client registration messages */ + if (hdr.command == ZEBRA_OPAQUE_REGISTER) { + handle_opq_registration(&hdr, msg); + continue; + } else if (hdr.command == ZEBRA_OPAQUE_UNREGISTER) { + handle_opq_unregistration(&hdr, msg); + continue; + } + + /* We only process OPAQUE messages - drop anything else */ + if (hdr.command != ZEBRA_OPAQUE_MESSAGE) + goto drop_it; + + /* Dispatch to any registered ZAPI client(s) */ + + /* Extract subtype and flags */ + ret = zclient_opaque_decode(msg, &info); + if (ret != 0) + goto drop_it; + + /* Look up registered ZAPI client(s) */ + reg = opq_reg_lookup(info.type); + if (reg == NULL) { + if (IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: no registrations for opaque type %u, flags %#x", + __func__, info.type, info.flags); + goto drop_it; + } + + /* Reset read pointer, since we'll be re-sending message */ + stream_set_getp(msg, 0); + + /* Send a copy of the message to all registered clients */ + for (client = reg->clients; client; client = client->next) { + dup = NULL; + + if (CHECK_FLAG(info.flags, ZAPI_OPAQUE_FLAG_UNICAST)) { + + if (client->proto != info.proto || + client->instance != info.instance || + client->session_id != info.session_id) + continue; + + if (IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: found matching unicast client %s", + __func__, + opq_client2str(buf, + sizeof(buf), + client)); + + } else { + /* Copy message if more clients */ + if (client->next) + dup = stream_dup(msg); + } + + /* + * TODO -- this isn't ideal: we're going through an + * acquire/release cycle for each client for each + * message. Replace this with a batching version. + */ + zclient = zserv_acquire_client(client->proto, + client->instance, + client->session_id); + if (zclient) { + if (IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: sending %s to client %s", + __func__, + (dup ? "dup" : "msg"), + opq_client2str(buf, + sizeof(buf), + client)); + + /* + * Sending a message actually means enqueuing + * it for a zapi io pthread to send - so we + * don't touch the message after this call. + */ + zserv_send_message(zclient, dup ? dup : msg); + if (dup) + dup = NULL; + else + msg = NULL; + + zserv_release_client(zclient); + } else { + if (IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: type %u: no zclient for %s", + __func__, info.type, + opq_client2str(buf, + sizeof(buf), + client)); + /* Registered but gone? */ + if (dup) + stream_free(dup); + } + + /* If unicast, we're done */ + if (CHECK_FLAG(info.flags, ZAPI_OPAQUE_FLAG_UNICAST)) + break; + } + +drop_it: + + if (msg) + stream_free(msg); + } + + return 0; +} + +/* + * Process a register/unregister message + */ +static int handle_opq_registration(const struct zmsghdr *hdr, + struct stream *msg) +{ + int ret = 0; + struct zapi_opaque_reg_info info; + struct opq_client_reg *client; + struct opq_msg_reg key, *reg; + char buf[50]; + + memset(&info, 0, sizeof(info)); + + if (zapi_opaque_reg_decode(msg, &info) < 0) { + ret = -1; + goto done; + } + + memset(&key, 0, sizeof(key)); + + key.type = info.type; + + reg = opq_regh_find(&opq_reg_hash, &key); + if (reg) { + /* Look for dup client */ + for (client = reg->clients; client != NULL; + client = client->next) { + if (opq_client_match(client, &info)) + break; + } + + if (client) { + /* Oops - duplicate registration? */ + if (IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: duplicate opq reg for client %s", + __func__, + opq_client2str(buf, sizeof(buf), + client)); + goto done; + } + + client = opq_client_alloc(&info); + + if (IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: client %s registers for %u", + __func__, + opq_client2str(buf, sizeof(buf), client), + info.type); + + /* Link client into registration */ + client->next = reg->clients; + if (reg->clients) + reg->clients->prev = client; + reg->clients = client; + } else { + /* + * No existing registrations - create one, add the + * client, and add registration to hash. + */ + reg = opq_reg_alloc(info.type); + client = opq_client_alloc(&info); + + if (IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: client %s registers for new reg %u", + __func__, + opq_client2str(buf, sizeof(buf), client), + info.type); + + reg->clients = client; + + opq_regh_add(&opq_reg_hash, reg); + } + +done: + + stream_free(msg); + return ret; +} + +/* + * Process a register/unregister message + */ +static int handle_opq_unregistration(const struct zmsghdr *hdr, + struct stream *msg) +{ + int ret = 0; + struct zapi_opaque_reg_info info; + struct opq_client_reg *client; + struct opq_msg_reg key, *reg; + char buf[50]; + + memset(&info, 0, sizeof(info)); + + if (zapi_opaque_reg_decode(msg, &info) < 0) { + ret = -1; + goto done; + } + + memset(&key, 0, sizeof(key)); + + key.type = info.type; + + reg = opq_regh_find(&opq_reg_hash, &key); + if (reg == NULL) { + /* Weird: unregister for unknown message? */ + if (IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: unknown client %s/%u/%u unregisters for unknown type %u", + __func__, + zebra_route_string(info.proto), + info.instance, info.session_id, info.type); + goto done; + } + + /* Look for client */ + for (client = reg->clients; client != NULL; + client = client->next) { + if (opq_client_match(client, &info)) + break; + } + + if (client == NULL) { + /* Oops - unregister for unknown client? */ + if (IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: unknown client %s/%u/%u unregisters for %u", + __func__, zebra_route_string(info.proto), + info.instance, info.session_id, info.type); + goto done; + } + + if (IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: client %s unregisters for %u", + __func__, opq_client2str(buf, sizeof(buf), client), + info.type); + + if (client->prev) + client->prev->next = client->next; + if (client->next) + client->next->prev = client->prev; + if (reg->clients == client) + reg->clients = client->next; + + opq_client_free(&client); + + /* Is registration empty now? */ + if (reg->clients == NULL) { + if (IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: free empty reg %u", __func__, + reg->type); + + opq_regh_del(&opq_reg_hash, reg); + opq_reg_free(®); + } + +done: + + stream_free(msg); + return ret; +} + +/* Compare utility for registered clients */ +static bool opq_client_match(const struct opq_client_reg *client, + const struct zapi_opaque_reg_info *info) +{ + if (client->proto == info->proto && + client->instance == info->instance && + client->session_id == info->session_id) + return true; + else + return false; +} + +static struct opq_msg_reg *opq_reg_lookup(uint32_t type) +{ + struct opq_msg_reg key, *reg; + + memset(&key, 0, sizeof(key)); + + key.type = type; + + reg = opq_regh_find(&opq_reg_hash, &key); + + return reg; +} + +static struct opq_msg_reg *opq_reg_alloc(uint32_t type) +{ + struct opq_msg_reg *reg; + + reg = XCALLOC(MTYPE_OPQ, sizeof(struct opq_msg_reg)); + + reg->type = type; + INIT_HASH(®->item); + + return reg; +} + +static void opq_reg_free(struct opq_msg_reg **reg) +{ + XFREE(MTYPE_OPQ, (*reg)); +} + +static struct opq_client_reg *opq_client_alloc( + const struct zapi_opaque_reg_info *info) +{ + struct opq_client_reg *client; + + client = XCALLOC(MTYPE_OPQ, sizeof(struct opq_client_reg)); + + client->proto = info->proto; + client->instance = info->instance; + client->session_id = info->session_id; + + return client; +} + +static void opq_client_free(struct opq_client_reg **client) +{ + XFREE(MTYPE_OPQ, (*client)); +} + +static const char *opq_client2str(char *buf, size_t buflen, + const struct opq_client_reg *client) +{ + char sbuf[20]; + + snprintf(buf, buflen, "%s/%u", zebra_route_string(client->proto), + client->instance); + if (client->session_id > 0) { + snprintf(sbuf, sizeof(sbuf), "/%u", client->session_id); + strlcat(buf, sbuf, buflen); + } + + return buf; +} + +/* Hash function for clients registered for messages */ +static uint32_t registration_hash(const struct opq_msg_reg *reg) +{ + return reg->type; +} + +/* Comparison function for client registrations */ +static int registration_compare(const struct opq_msg_reg *reg1, + const struct opq_msg_reg *reg2) +{ + if (reg1->type == reg2->type) + return 0; + else + return -1; +} diff --git a/zebra/zebra_opaque.h b/zebra/zebra_opaque.h new file mode 100644 index 0000000000..a9610bfef5 --- /dev/null +++ b/zebra/zebra_opaque.h @@ -0,0 +1,63 @@ +/* + * Zebra opaque message zapi message handler + * Copyright (c) 2020 Volta Networks, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#ifndef _ZEBRA_OPAQUE_H +#define _ZEBRA_OPAQUE_H 1 + +/* Default for number of messages to dequeue per lock cycle */ +#define ZEBRA_OPAQUE_MSG_LIMIT 1000 + +/* + * Initialize the module at startup + */ +void zebra_opaque_init(void); + +/* + * Start the module pthread. This step is run later than the + * 'init' step, in case zebra has fork-ed. + */ +void zebra_opaque_start(void); + +/* + * Does this module handle (intercept) the specified zapi message type? + */ +bool zebra_opaque_handles_msgid(uint16_t id); + +/* + * Module stop, called from the main pthread. This is synchronous: + * once it returns, the pthread has stopped and exited. + */ +void zebra_opaque_stop(void); + +/* + * Module cleanup, called from the zebra main pthread. When it returns, + * all module cleanup is complete. + */ +void zebra_opaque_finish(void); + +/* + * Enqueue a batch of messages for processing. Returns the number dequeued + * from the batch fifo. + */ +uint32_t zebra_opaque_enqueue_batch(struct stream_fifo *batch); + + +#endif /* _ZEBRA_OPAQUE_H */ diff --git a/zebra/zebra_pbr.c b/zebra/zebra_pbr.c index 73db567eac..c244d2a955 100644 --- a/zebra/zebra_pbr.c +++ b/zebra/zebra_pbr.c @@ -83,6 +83,27 @@ const struct message icmp_typecode_str[] = { {0} }; +const struct message icmpv6_typecode_str[] = { + { 128 << 8, "echo-request"}, + { 129 << 8, "echo-reply"}, + { 1 << 8, "no-route"}, + { (1 << 8) + 1, "communication-prohibited"}, + { (1 << 8) + 3, "address-unreachable"}, + { (1 << 8) + 4, "port-unreachable"}, + { (2 << 8), "packet-too-big"}, + { 3 << 0, "ttl-zero-during-transit"}, + { (3 << 8) + 1, "ttl-zero-during-reassembly"}, + { 4 << 0, "bad-header"}, + { (4 << 0) + 1, "unknown-header-type"}, + { (4 << 0) + 2, "unknown-option"}, + { 133 << 8, "router-solicitation"}, + { 134 << 8, "router-advertisement"}, + { 135 << 8, "neighbor-solicitation"}, + { 136 << 8, "neighbor-advertisement"}, + { 137 << 8, "redirect"}, + {0} +}; + /* definitions */ static const struct message tcp_value_str[] = { {TCP_HEADER_FIN, "FIN"}, @@ -131,30 +152,26 @@ void zebra_pbr_rules_free(void *arg) rule = (struct zebra_pbr_rule *)arg; - (void)kernel_del_pbr_rule(rule); + (void)dplane_pbr_rule_delete(rule); XFREE(MTYPE_TMP, rule); } -uint32_t zebra_pbr_rules_hash_key(void *arg) +uint32_t zebra_pbr_rules_hash_key(const void *arg) { - struct zebra_pbr_rule *rule; + const struct zebra_pbr_rule *rule; uint32_t key; - rule = (struct zebra_pbr_rule *)arg; + rule = arg; key = jhash_3words(rule->rule.seq, rule->rule.priority, rule->rule.action.table, prefix_hash_key(&rule->rule.filter.src_ip)); - if (rule->ifp) - key = jhash_1word(rule->ifp->ifindex, key); - else - key = jhash_1word(0, key); if (rule->rule.filter.fwmark) - key = jhash_1word(rule->rule.filter.fwmark, key); + key = jhash_2words(rule->rule.filter.fwmark, rule->vrf_id, key); else - key = jhash_1word(0, key); + key = jhash_1word(rule->vrf_id, key); - key = jhash_1word(rule->vrf_id, key); + key = jhash(rule->ifname, strlen(rule->ifname), key); return jhash_3words(rule->rule.filter.src_port, rule->rule.filter.dst_port, @@ -196,7 +213,7 @@ bool zebra_pbr_rules_hash_equal(const void *arg1, const void *arg2) if (!prefix_same(&r1->rule.filter.dst_ip, &r2->rule.filter.dst_ip)) return false; - if (r1->ifp != r2->ifp) + if (strcmp(r1->rule.ifname, r2->rule.ifname) != 0) return false; if (r1->vrf_id != r2->vrf_id) @@ -208,7 +225,7 @@ bool zebra_pbr_rules_hash_equal(const void *arg1, const void *arg2) struct pbr_rule_unique_lookup { struct zebra_pbr_rule *rule; uint32_t unique; - struct interface *ifp; + char ifname[INTERFACE_NAMSIZ + 1]; vrf_id_t vrf_id; }; @@ -218,7 +235,7 @@ static int pbr_rule_lookup_unique_walker(struct hash_bucket *b, void *data) struct zebra_pbr_rule *rule = b->data; if (pul->unique == rule->rule.unique - && pul->ifp == rule->ifp + && strncmp(pul->ifname, rule->rule.ifname, INTERFACE_NAMSIZ) == 0 && pul->vrf_id == rule->vrf_id) { pul->rule = rule; return HASHWALK_ABORT; @@ -233,7 +250,7 @@ pbr_rule_lookup_unique(struct zebra_pbr_rule *zrule) struct pbr_rule_unique_lookup pul; pul.unique = zrule->rule.unique; - pul.ifp = zrule->ifp; + strlcpy(pul.ifname, zrule->rule.ifname, INTERFACE_NAMSIZ); pul.rule = NULL; pul.vrf_id = zrule->vrf_id; hash_walk(zrouter.rules_hash, &pbr_rule_lookup_unique_walker, &pul); @@ -250,12 +267,14 @@ void zebra_pbr_ipset_free(void *arg) XFREE(MTYPE_TMP, ipset); } -uint32_t zebra_pbr_ipset_hash_key(void *arg) +uint32_t zebra_pbr_ipset_hash_key(const void *arg) { - struct zebra_pbr_ipset *ipset = (struct zebra_pbr_ipset *)arg; + const struct zebra_pbr_ipset *ipset = arg; uint32_t *pnt = (uint32_t *)&ipset->ipset_name; uint32_t key = jhash_1word(ipset->vrf_id, 0x63ab42de); + key = jhash_1word(ipset->family, key); + return jhash2(pnt, ZEBRA_IPSET_NAME_HASH_SIZE, key); } @@ -272,6 +291,8 @@ bool zebra_pbr_ipset_hash_equal(const void *arg1, const void *arg2) return false; if (r1->vrf_id != r2->vrf_id) return false; + if (r1->family != r2->family) + return false; if (strncmp(r1->ipset_name, r2->ipset_name, ZEBRA_IPSET_NAME_SIZE)) @@ -290,12 +311,12 @@ void zebra_pbr_ipset_entry_free(void *arg) XFREE(MTYPE_TMP, ipset); } -uint32_t zebra_pbr_ipset_entry_hash_key(void *arg) +uint32_t zebra_pbr_ipset_entry_hash_key(const void *arg) { - struct zebra_pbr_ipset_entry *ipset; + const struct zebra_pbr_ipset_entry *ipset; uint32_t key; - ipset = (struct zebra_pbr_ipset_entry *)arg; + ipset = arg; key = prefix_hash_key(&ipset->src); key = jhash_1word(ipset->unique, key); key = jhash_1word(prefix_hash_key(&ipset->dst), key); @@ -341,38 +362,54 @@ bool zebra_pbr_ipset_entry_hash_equal(const void *arg1, const void *arg2) return true; } -void zebra_pbr_iptable_free(void *arg) +/* this function gives option to flush plugin memory contexts + * with all parameter. set it to true to flush all + * set it to false to flush only passed arg argument + */ +static void _zebra_pbr_iptable_free_all(void *arg, bool all) { struct zebra_pbr_iptable *iptable; struct listnode *node, *nnode; char *name; iptable = (struct zebra_pbr_iptable *)arg; - hook_call(zebra_pbr_iptable_update, 0, iptable); - for (ALL_LIST_ELEMENTS(iptable->interface_name_list, - node, nnode, name)) { - XFREE(MTYPE_PBR_IPTABLE_IFNAME, name); - list_delete_node(iptable->interface_name_list, - node); + if (all) + hook_call(zebra_pbr_iptable_update, 0, iptable); + + if (iptable->interface_name_list) { + for (ALL_LIST_ELEMENTS(iptable->interface_name_list, node, + nnode, name)) { + XFREE(MTYPE_PBR_IPTABLE_IFNAME, name); + list_delete_node(iptable->interface_name_list, node); + } + list_delete(&iptable->interface_name_list); } XFREE(MTYPE_TMP, iptable); } -uint32_t zebra_pbr_iptable_hash_key(void *arg) +void zebra_pbr_iptable_free(void *arg) +{ + _zebra_pbr_iptable_free_all(arg, false); +} + +uint32_t zebra_pbr_iptable_hash_key(const void *arg) { - struct zebra_pbr_iptable *iptable = (struct zebra_pbr_iptable *)arg; + const struct zebra_pbr_iptable *iptable = arg; uint32_t *pnt = (uint32_t *)&(iptable->ipset_name); uint32_t key; key = jhash2(pnt, ZEBRA_IPSET_NAME_HASH_SIZE, 0x63ab42de); key = jhash_1word(iptable->fwmark, key); + key = jhash_1word(iptable->family, key); + key = jhash_1word(iptable->flow_label, key); key = jhash_1word(iptable->pkt_len_min, key); key = jhash_1word(iptable->pkt_len_max, key); key = jhash_1word(iptable->tcp_flags, key); key = jhash_1word(iptable->tcp_mask_flags, key); key = jhash_1word(iptable->dscp_value, key); + key = jhash_1word(iptable->protocol, key); key = jhash_1word(iptable->fragment, key); key = jhash_1word(iptable->vrf_id, key); @@ -402,6 +439,10 @@ bool zebra_pbr_iptable_hash_equal(const void *arg1, const void *arg2) if (strncmp(r1->ipset_name, r2->ipset_name, ZEBRA_IPSET_NAME_SIZE)) return false; + if (r1->family != r2->family) + return false; + if (r1->flow_label != r2->flow_label) + return false; if (r1->pkt_len_min != r2->pkt_len_min) return false; if (r1->pkt_len_max != r2->pkt_len_max) @@ -414,6 +455,8 @@ bool zebra_pbr_iptable_hash_equal(const void *arg1, const void *arg2) return false; if (r1->fragment != r2->fragment) return false; + if (r1->protocol != r2->protocol) + return false; return true; } @@ -431,34 +474,53 @@ static void *pbr_rule_alloc_intern(void *arg) return new; } +static int pbr_rule_release(struct zebra_pbr_rule *rule) +{ + struct zebra_pbr_rule *lookup; + + lookup = hash_lookup(zrouter.rules_hash, rule); + + if (!lookup) + return -ENOENT; + + hash_release(zrouter.rules_hash, lookup); + XFREE(MTYPE_TMP, lookup); + + return 0; +} + void zebra_pbr_add_rule(struct zebra_pbr_rule *rule) { - struct zebra_pbr_rule *unique = - pbr_rule_lookup_unique(rule); + struct zebra_pbr_rule *found; - (void)hash_get(zrouter.rules_hash, rule, pbr_rule_alloc_intern); - (void)kernel_add_pbr_rule(rule); - /* - * Rule Replace semantics, if we have an old, install the - * new rule, look above, and then delete the old + /** + * Check if we already have it (this checks via a unique ID, walking + * over the hash table, not via a hash operation). */ - if (unique) - zebra_pbr_del_rule(unique); + found = pbr_rule_lookup_unique(rule); + + (void)hash_get(zrouter.rules_hash, rule, pbr_rule_alloc_intern); + + /* If found, this is an update */ + if (found) { + (void)dplane_pbr_rule_update(found, rule); + + if (pbr_rule_release(found)) + zlog_debug( + "%s: Rule being updated we know nothing about", + __PRETTY_FUNCTION__); + + } else + (void)dplane_pbr_rule_add(rule); } void zebra_pbr_del_rule(struct zebra_pbr_rule *rule) { - struct zebra_pbr_rule *lookup; + (void)dplane_pbr_rule_delete(rule); - lookup = hash_lookup(zrouter.rules_hash, rule); - (void)kernel_del_pbr_rule(rule); - - if (lookup) { - hash_release(zrouter.rules_hash, lookup); - XFREE(MTYPE_TMP, lookup); - } else + if (pbr_rule_release(rule)) zlog_debug("%s: Rule being deleted we know nothing about", - __PRETTY_FUNCTION__); + __func__); } static void zebra_pbr_cleanup_rules(struct hash_bucket *b, void *data) @@ -467,9 +529,13 @@ static void zebra_pbr_cleanup_rules(struct hash_bucket *b, void *data) int *sock = data; if (rule->sock == *sock) { - (void)kernel_del_pbr_rule(rule); - hash_release(zrouter.rules_hash, rule); - XFREE(MTYPE_TMP, rule); + (void)dplane_pbr_rule_delete(rule); + if (hash_release(zrouter.rules_hash, rule)) + XFREE(MTYPE_TMP, rule); + else + zlog_debug( + "%s: Rule seq: %u is being cleaned but we can't find it in our tables", + __func__, rule->rule.seq); } } @@ -479,8 +545,10 @@ static void zebra_pbr_cleanup_ipset(struct hash_bucket *b, void *data) int *sock = data; if (ipset->sock == *sock) { - hook_call(zebra_pbr_ipset_update, 0, ipset); - hash_release(zrouter.ipset_hash, ipset); + if (hash_release(zrouter.ipset_hash, ipset)) + zebra_pbr_ipset_free(ipset); + else + hook_call(zebra_pbr_ipset_update, 0, ipset); } } @@ -490,8 +558,10 @@ static void zebra_pbr_cleanup_ipset_entry(struct hash_bucket *b, void *data) int *sock = data; if (ipset->sock == *sock) { - hook_call(zebra_pbr_ipset_entry_update, 0, ipset); - hash_release(zrouter.ipset_entry_hash, ipset); + if (hash_release(zrouter.ipset_entry_hash, ipset)) + zebra_pbr_ipset_entry_free(ipset); + else + hook_call(zebra_pbr_ipset_entry_update, 0, ipset); } } @@ -501,8 +571,10 @@ static void zebra_pbr_cleanup_iptable(struct hash_bucket *b, void *data) int *sock = data; if (iptable->sock == *sock) { - hook_call(zebra_pbr_iptable_update, 0, iptable); - hash_release(zrouter.iptable_hash, iptable); + if (hash_release(zrouter.iptable_hash, iptable)) + _zebra_pbr_iptable_free_all(iptable, true); + else + hook_call(zebra_pbr_iptable_update, 0, iptable); } } @@ -562,7 +634,7 @@ void zebra_pbr_destroy_ipset(struct zebra_pbr_ipset *ipset) } else zlog_debug( "%s: IPSet Entry being deleted we know nothing about", - __PRETTY_FUNCTION__); + __func__); } struct pbr_ipset_name_lookup { @@ -641,19 +713,29 @@ void zebra_pbr_del_ipset_entry(struct zebra_pbr_ipset_entry *ipset) XFREE(MTYPE_TMP, lookup); } else zlog_debug("%s: IPSet being deleted we know nothing about", - __PRETTY_FUNCTION__); + __func__); } static void *pbr_iptable_alloc_intern(void *arg) { struct zebra_pbr_iptable *zpi; struct zebra_pbr_iptable *new; + struct listnode *ln; + char *ifname; zpi = (struct zebra_pbr_iptable *)arg; new = XCALLOC(MTYPE_TMP, sizeof(struct zebra_pbr_iptable)); + /* Deep structure copy */ memcpy(new, zpi, sizeof(*zpi)); + new->interface_name_list = list_new(); + + if (zpi->interface_name_list) { + for (ALL_LIST_ELEMENTS_RO(zpi->interface_name_list, ln, ifname)) + listnode_add(new->interface_name_list, + XSTRDUP(MTYPE_PBR_IPTABLE_IFNAME, ifname)); + } return new; } @@ -686,34 +768,39 @@ void zebra_pbr_del_iptable(struct zebra_pbr_iptable *iptable) list_delete_node(iptable->interface_name_list, node); } + list_delete(&iptable->interface_name_list); XFREE(MTYPE_TMP, lookup); } else zlog_debug("%s: IPTable being deleted we know nothing about", - __PRETTY_FUNCTION__); + __func__); } /* * Handle success or failure of rule (un)install in the kernel. */ -void kernel_pbr_rule_add_del_status(struct zebra_pbr_rule *rule, - enum zebra_dplane_status res) +void zebra_pbr_dplane_result(struct zebra_dplane_ctx *ctx) { - switch (res) { - case ZEBRA_DPLANE_INSTALL_SUCCESS: - zsend_rule_notify_owner(rule, ZAPI_RULE_INSTALLED); - break; - case ZEBRA_DPLANE_INSTALL_FAILURE: - zsend_rule_notify_owner(rule, ZAPI_RULE_FAIL_INSTALL); - break; - case ZEBRA_DPLANE_DELETE_SUCCESS: - zsend_rule_notify_owner(rule, ZAPI_RULE_REMOVED); - break; - case ZEBRA_DPLANE_DELETE_FAILURE: - zsend_rule_notify_owner(rule, ZAPI_RULE_FAIL_REMOVE); - break; - case ZEBRA_DPLANE_STATUS_NONE: - break; - } + enum zebra_dplane_result res; + enum dplane_op_e op; + + res = dplane_ctx_get_status(ctx); + op = dplane_ctx_get_op(ctx); + if (op == DPLANE_OP_RULE_ADD || op == DPLANE_OP_RULE_UPDATE) + zsend_rule_notify_owner(ctx, res == ZEBRA_DPLANE_REQUEST_SUCCESS + ? ZAPI_RULE_INSTALLED + : ZAPI_RULE_FAIL_INSTALL); + else if (op == DPLANE_OP_RULE_DELETE) + zsend_rule_notify_owner(ctx, res == ZEBRA_DPLANE_REQUEST_SUCCESS + ? ZAPI_RULE_REMOVED + : ZAPI_RULE_FAIL_REMOVE); + else + flog_err( + EC_ZEBRA_PBR_RULE_UPDATE, + "Context received in pbr rule dplane result handler with incorrect OP code (%u)", + op); + + + dplane_ctx_fini(&ctx); } /* @@ -821,7 +908,8 @@ static const char *zebra_pbr_prefix2str(union prefixconstptr pu, const struct prefix *p = pu.p; char buf[PREFIX2STR_BUFFER]; - if (p->family == AF_INET && p->prefixlen == IPV4_MAX_PREFIXLEN) { + if ((p->family == AF_INET && p->prefixlen == IPV4_MAX_PREFIXLEN) || + (p->family == AF_INET6 && p->prefixlen == IPV6_MAX_PREFIXLEN)) { snprintf(str, size, "%s", inet_ntop(p->family, &p->u.prefix, buf, PREFIX2STR_BUFFER)); return str; @@ -834,21 +922,25 @@ static void zebra_pbr_display_icmp(struct vty *vty, { char decoded_str[20]; uint16_t port; + struct zebra_pbr_ipset *zpi; + + zpi = zpie->backpointer; /* range icmp type */ if (zpie->src_port_max || zpie->dst_port_max) { - vty_out(vty, ":icmp:[type <%d:%d>;code <%d:%d>", + vty_out(vty, ":icmp:[type <%u:%u>;code <%u:%u>", zpie->src_port_min, zpie->src_port_max, zpie->dst_port_min, zpie->dst_port_max); } else { port = ((zpie->src_port_min << 8) & 0xff00) + (zpie->dst_port_min & 0xff); memset(decoded_str, 0, sizeof(decoded_str)); - sprintf(decoded_str, "%d/%d", - zpie->src_port_min, - zpie->dst_port_min); - vty_out(vty, ":icmp:%s", - lookup_msg(icmp_typecode_str, + snprintf(decoded_str, sizeof(decoded_str), "%u/%u", + zpie->src_port_min, zpie->dst_port_min); + vty_out(vty, ":%s:%s", + zpi->family == AF_INET6 ? "ipv6-icmp" : "icmp", + lookup_msg(zpi->family == AF_INET6 ? + icmpv6_typecode_str : icmp_typecode_str, port, decoded_str)); } } @@ -958,8 +1050,9 @@ static int zebra_pbr_show_ipset_walkcb(struct hash_bucket *bucket, void *arg) struct vty *vty = uniqueipset->vty; struct zebra_ns *zns = uniqueipset->zns; - vty_out(vty, "IPset %s type %s\n", zpi->ipset_name, - zebra_pbr_ipset_type2str(zpi->type)); + vty_out(vty, "IPset %s type %s family %s\n", zpi->ipset_name, + zebra_pbr_ipset_type2str(zpi->type), + family2str(zpi->family)); unique.vty = vty; unique.zpi = zpi; unique.zns = zns; @@ -1004,9 +1097,9 @@ void zebra_pbr_show_ipset_list(struct vty *vty, char *ipsetname) vty_out(vty, "No IPset %s found\n", ipsetname); return; } - vty_out(vty, "IPset %s type %s\n", ipsetname, - zebra_pbr_ipset_type2str(zpi->type)); - + vty_out(vty, "IPset %s type %s family %s\n", ipsetname, + zebra_pbr_ipset_type2str(zpi->type), + family2str(zpi->family)); unique.vty = vty; unique.zpi = zpi; unique.zns = zns; @@ -1047,7 +1140,9 @@ static void zebra_pbr_show_iptable_unit(struct zebra_pbr_iptable *iptable, int ret; uint64_t pkts = 0, bytes = 0; - vty_out(vty, "IPtable %s action %s (%u)\n", iptable->ipset_name, + vty_out(vty, "IPtable %s family %s action %s (%u)\n", + iptable->ipset_name, + family2str(iptable->family), iptable->action == ZEBRA_IPTABLES_DROP ? "drop" : "redirect", iptable->unique); if (iptable->type == IPSET_NET_PORT || @@ -1086,15 +1181,25 @@ static void zebra_pbr_show_iptable_unit(struct zebra_pbr_iptable *iptable, iptable->filter_bm & MATCH_DSCP_INVERSE_SET ? "not" : "", iptable->dscp_value); } + if (iptable->filter_bm & (MATCH_FLOW_LABEL_SET | + MATCH_FLOW_LABEL_INVERSE_SET)) { + vty_out(vty, "\t flowlabel %s %d\n", + iptable->filter_bm & MATCH_FLOW_LABEL_INVERSE_SET ? + "not" : "", iptable->flow_label); + } if (iptable->fragment) { char val_str[10]; - sprintf(val_str, "%d", iptable->fragment); + snprintf(val_str, sizeof(val_str), "%d", iptable->fragment); vty_out(vty, "\t fragment%s %s\n", iptable->filter_bm & MATCH_FRAGMENT_INVERSE_SET ? " not" : "", lookup_msg(fragment_value_str, iptable->fragment, val_str)); } + if (iptable->protocol) { + vty_out(vty, "\t protocol %d\n", + iptable->protocol); + } ret = hook_call(zebra_pbr_iptable_get_stat, iptable, &pkts, &bytes); if (ret && pkts > 0) diff --git a/zebra/zebra_pbr.h b/zebra/zebra_pbr.h index 0d55491107..e7504a3547 100644 --- a/zebra/zebra_pbr.h +++ b/zebra/zebra_pbr.h @@ -41,7 +41,7 @@ struct zebra_pbr_rule { struct pbr_rule rule; - struct interface *ifp; + char ifname[INTERFACE_NAMSIZ]; vrf_id_t vrf_id; }; @@ -54,6 +54,8 @@ struct zebra_pbr_rule { (r->rule.filter.filter_bm & PBR_FILTER_SRC_PORT) #define IS_RULE_FILTERING_ON_DST_PORT(r) \ (r->rule.filter.filter_bm & PBR_FILTER_DST_PORT) +#define IS_RULE_FILTERING_ON_DSFIELD(r) \ + (r->rule.filter.filter_bm & PBR_FILTER_DSFIELD) #define IS_RULE_FILTERING_ON_FWMARK(r) \ (r->rule.filter.filter_bm & PBR_FILTER_FWMARK) @@ -77,6 +79,9 @@ struct zebra_pbr_ipset { * but value is an enum ipset_type */ uint32_t type; + + uint8_t family; + char ipset_name[ZEBRA_IPSET_NAME_SIZE]; }; @@ -145,8 +150,12 @@ struct zebra_pbr_iptable { uint16_t tcp_mask_flags; uint8_t dscp_value; uint8_t fragment; + uint8_t protocol; uint32_t nb_interface; + uint16_t flow_label; + + uint8_t family; struct list *interface_name_list; @@ -154,6 +163,7 @@ struct zebra_pbr_iptable { }; extern const struct message icmp_typecode_str[]; +extern const struct message icmpv6_typecode_str[]; const char *zebra_pbr_ipset_type2str(uint32_t type); @@ -168,19 +178,6 @@ void zebra_pbr_del_ipset_entry(struct zebra_pbr_ipset_entry *ipset); void zebra_pbr_add_iptable(struct zebra_pbr_iptable *iptable); void zebra_pbr_del_iptable(struct zebra_pbr_iptable *iptable); -/* - * Install specified rule for a specific interface. - * It is possible that the user-defined sequence number and the one in the - * forwarding plane may not coincide, hence the API requires a separate - * rule priority - maps to preference/FRA_PRIORITY on Linux. - */ -extern enum zebra_dplane_result kernel_add_pbr_rule(struct zebra_pbr_rule *rule); - -/* - * Uninstall specified rule for a specific interface. - */ -extern enum zebra_dplane_result kernel_del_pbr_rule(struct zebra_pbr_rule *rule); - /* * Get to know existing PBR rules in the kernel - typically called at startup. */ @@ -189,8 +186,7 @@ extern void kernel_read_pbr_rules(struct zebra_ns *zns); /* * Handle success or failure of rule (un)install in the kernel. */ -extern void kernel_pbr_rule_add_del_status(struct zebra_pbr_rule *rule, - enum zebra_dplane_status res); +extern void zebra_pbr_dplane_result(struct zebra_dplane_ctx *ctx); /* * Handle success or failure of ipset kinds (un)install in the kernel. @@ -211,7 +207,7 @@ extern void kernel_pbr_iptable_add_del_status(struct zebra_pbr_iptable *iptable, extern int kernel_pbr_rule_del(struct zebra_pbr_rule *rule); extern void zebra_pbr_rules_free(void *arg); -extern uint32_t zebra_pbr_rules_hash_key(void *arg); +extern uint32_t zebra_pbr_rules_hash_key(const void *arg); extern bool zebra_pbr_rules_hash_equal(const void *arg1, const void *arg2); /* has operates on 32bit pointer @@ -220,16 +216,16 @@ extern bool zebra_pbr_rules_hash_equal(const void *arg1, const void *arg2); #define ZEBRA_IPSET_NAME_HASH_SIZE (ZEBRA_IPSET_NAME_SIZE / 4) extern void zebra_pbr_ipset_free(void *arg); -extern uint32_t zebra_pbr_ipset_hash_key(void *arg); +extern uint32_t zebra_pbr_ipset_hash_key(const void *arg); extern bool zebra_pbr_ipset_hash_equal(const void *arg1, const void *arg2); extern void zebra_pbr_ipset_entry_free(void *arg); -extern uint32_t zebra_pbr_ipset_entry_hash_key(void *arg); +extern uint32_t zebra_pbr_ipset_entry_hash_key(const void *arg); extern bool zebra_pbr_ipset_entry_hash_equal(const void *arg1, const void *arg2); extern void zebra_pbr_iptable_free(void *arg); -extern uint32_t zebra_pbr_iptable_hash_key(void *arg); +extern uint32_t zebra_pbr_iptable_hash_key(const void *arg); extern bool zebra_pbr_iptable_hash_equal(const void *arg1, const void *arg2); extern void zebra_pbr_init(void); diff --git a/zebra/zebra_ptm.c b/zebra/zebra_ptm.c index bb352dc2ff..9a3b567b5a 100644 --- a/zebra/zebra_ptm.c +++ b/zebra/zebra_ptm.c @@ -93,6 +93,7 @@ const char ZEBRA_PTM_BFD_IFNAME_FIELD[] = "ifName"; const char ZEBRA_PTM_BFD_MAX_HOP_CNT_FIELD[] = "maxHopCnt"; const char ZEBRA_PTM_BFD_SEND_EVENT[] = "sendEvent"; const char ZEBRA_PTM_BFD_VRF_NAME_FIELD[] = "vrfName"; +const char ZEBRA_PTM_BFD_CBIT_FIELD[] = "bfdcbit"; static ptm_lib_handle_t *ptm_hdl; @@ -131,7 +132,7 @@ void zebra_ptm_init(void) ptm_cb.pid = getpid(); zebra_ptm_install_commands(); - sprintf(buf, "%s", FRR_PTM_NAME); + snprintf(buf, sizeof(buf), "%s", FRR_PTM_NAME); ptm_hdl = ptm_lib_register(buf, NULL, zebra_ptm_handle_msg_cb, zebra_ptm_handle_msg_cb); ptm_cb.wb = buffer_new(0); @@ -189,7 +190,7 @@ static int zebra_ptm_flush_messages(struct thread *thread) ptm_cb.t_timer = NULL; thread_add_timer(zrouter.master, zebra_ptm_connect, NULL, ptm_cb.reconnect_time, &ptm_cb.t_timer); - return (-1); + return -1; case BUFFER_PENDING: ptm_cb.t_write = NULL; thread_add_write(zrouter.master, zebra_ptm_flush_messages, NULL, @@ -199,7 +200,7 @@ static int zebra_ptm_flush_messages(struct thread *thread) break; } - return (0); + return 0; } static int zebra_ptm_send_message(char *data, int size) @@ -389,7 +390,7 @@ static int zebra_ptm_socket_init(void) if (set_nonblocking(sock) < 0) { if (IS_ZEBRA_DEBUG_EVENT) zlog_debug("%s: Unable to set socket non blocking[%s]", - __PRETTY_FUNCTION__, safe_strerror(errno)); + __func__, safe_strerror(errno)); close(sock); return -1; } @@ -433,22 +434,22 @@ static void if_bfd_session_update(struct interface *ifp, struct prefix *dp, if (ifp) { zlog_debug( - "MESSAGE: ZEBRA_INTERFACE_BFD_DEST_UPDATE %s/%d on %s" - " %s event", + "MESSAGE: ZEBRA_INTERFACE_BFD_DEST_UPDATE %s/%d on %s %s event", inet_ntop(dp->family, &dp->u.prefix, buf[0], INET6_ADDRSTRLEN), dp->prefixlen, ifp->name, bfd_get_status_str(status)); } else { + struct vrf *vrf = vrf_lookup_by_id(vrf_id); + zlog_debug( - "MESSAGE: ZEBRA_INTERFACE_BFD_DEST_UPDATE %s/%d " - "with src %s/%d and vrf %u %s event", + "MESSAGE: ZEBRA_INTERFACE_BFD_DEST_UPDATE %s/%d with src %s/%d and vrf %s(%u) %s event", inet_ntop(dp->family, &dp->u.prefix, buf[0], INET6_ADDRSTRLEN), dp->prefixlen, inet_ntop(sp->family, &sp->u.prefix, buf[1], INET6_ADDRSTRLEN), - sp->prefixlen, vrf_id, + sp->prefixlen, VRF_LOGNAME(vrf), vrf_id, bfd_get_status_str(status)); } } @@ -499,8 +500,7 @@ static int zebra_ptm_handle_bfd_msg(void *arg, void *in_ctxt, if (IS_ZEBRA_DEBUG_EVENT) zlog_debug( - "%s: Recv Port [%s] bfd status [%s] vrf [%s]" - " peer [%s] local [%s]", + "%s: Recv Port [%s] bfd status [%s] vrf [%s] peer [%s] local [%s]", __func__, ifp ? ifp->name : "N/A", bfdst_str, vrf_str, dest_str, src_str); @@ -660,7 +660,7 @@ int zebra_ptm_sock_read(struct thread *thread) thread_add_timer(zrouter.master, zebra_ptm_connect, NULL, ptm_cb.reconnect_time, &ptm_cb.t_timer); - return (-1); + return -1; } ptm_cb.t_read = NULL; @@ -688,6 +688,7 @@ void zebra_ptm_bfd_dst_register(ZAPI_HANDLER_ARGS) char tmp_buf[64]; int data_len = ZEBRA_PTM_SEND_MAX_SOCKBUF; unsigned int pid; + uint8_t cbit_set; if (hdr->command == ZEBRA_BFD_DEST_UPDATE) client->bfd_peer_upd8_cnt++; @@ -706,16 +707,17 @@ void zebra_ptm_bfd_dst_register(ZAPI_HANDLER_ARGS) } ptm_lib_init_msg(ptm_hdl, 0, PTMLIB_MSG_TYPE_CMD, NULL, &out_ctxt); - sprintf(tmp_buf, "%s", ZEBRA_PTM_BFD_START_CMD); + snprintf(tmp_buf, sizeof(tmp_buf), "%s", ZEBRA_PTM_BFD_START_CMD); ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_CMD_STR, tmp_buf); - sprintf(tmp_buf, "%s", zebra_route_string(client->proto)); + snprintf(tmp_buf, sizeof(tmp_buf), "%s", + zebra_route_string(client->proto)); ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_BFD_CLIENT_FIELD, tmp_buf); s = msg; STREAM_GETL(s, pid); - sprintf(tmp_buf, "%d", pid); + snprintf(tmp_buf, sizeof(tmp_buf), "%d", pid); ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_BFD_SEQID_FIELD, tmp_buf); @@ -738,21 +740,21 @@ void zebra_ptm_bfd_dst_register(ZAPI_HANDLER_ARGS) } STREAM_GETL(s, min_rx_timer); - sprintf(tmp_buf, "%d", min_rx_timer); + snprintf(tmp_buf, sizeof(tmp_buf), "%d", min_rx_timer); ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_BFD_MIN_RX_FIELD, tmp_buf); STREAM_GETL(s, min_tx_timer); - sprintf(tmp_buf, "%d", min_tx_timer); + snprintf(tmp_buf, sizeof(tmp_buf), "%d", min_tx_timer); ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_BFD_MIN_TX_FIELD, tmp_buf); STREAM_GETC(s, detect_mul); - sprintf(tmp_buf, "%d", detect_mul); + snprintf(tmp_buf, sizeof(tmp_buf), "%d", detect_mul); ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_BFD_DETECT_MULT_FIELD, tmp_buf); STREAM_GETC(s, multi_hop); if (multi_hop) { - sprintf(tmp_buf, "%d", 1); + snprintf(tmp_buf, sizeof(tmp_buf), "%d", 1); ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_BFD_MULTI_HOP_FIELD, tmp_buf); STREAM_GETW(s, src_p.family); @@ -774,7 +776,7 @@ void zebra_ptm_bfd_dst_register(ZAPI_HANDLER_ARGS) } STREAM_GETC(s, multi_hop_cnt); - sprintf(tmp_buf, "%d", multi_hop_cnt); + snprintf(tmp_buf, sizeof(tmp_buf), "%d", multi_hop_cnt); ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_BFD_MAX_HOP_CNT_FIELD, tmp_buf); @@ -813,8 +815,12 @@ void zebra_ptm_bfd_dst_register(ZAPI_HANDLER_ARGS) ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_BFD_IFNAME_FIELD, if_name); } + STREAM_GETC(s, cbit_set); + snprintf(tmp_buf, sizeof(tmp_buf), "%d", cbit_set); + ptm_lib_append_msg(ptm_hdl, out_ctxt, + ZEBRA_PTM_BFD_CBIT_FIELD, tmp_buf); - sprintf(tmp_buf, "%d", 1); + snprintf(tmp_buf, sizeof(tmp_buf), "%d", 1); ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_BFD_SEND_EVENT, tmp_buf); @@ -861,17 +867,18 @@ void zebra_ptm_bfd_dst_deregister(ZAPI_HANDLER_ARGS) ptm_lib_init_msg(ptm_hdl, 0, PTMLIB_MSG_TYPE_CMD, NULL, &out_ctxt); - sprintf(tmp_buf, "%s", ZEBRA_PTM_BFD_STOP_CMD); + snprintf(tmp_buf, sizeof(tmp_buf), "%s", ZEBRA_PTM_BFD_STOP_CMD); ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_CMD_STR, tmp_buf); - sprintf(tmp_buf, "%s", zebra_route_string(client->proto)); + snprintf(tmp_buf, sizeof(tmp_buf), "%s", + zebra_route_string(client->proto)); ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_BFD_CLIENT_FIELD, tmp_buf); s = msg; STREAM_GETL(s, pid); - sprintf(tmp_buf, "%d", pid); + snprintf(tmp_buf, sizeof(tmp_buf), "%d", pid); ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_BFD_SEQID_FIELD, tmp_buf); @@ -892,7 +899,7 @@ void zebra_ptm_bfd_dst_deregister(ZAPI_HANDLER_ARGS) STREAM_GETC(s, multi_hop); if (multi_hop) { - sprintf(tmp_buf, "%d", 1); + snprintf(tmp_buf, sizeof(tmp_buf), "%d", 1); ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_BFD_MULTI_HOP_FIELD, tmp_buf); @@ -988,14 +995,15 @@ void zebra_ptm_bfd_client_register(ZAPI_HANDLER_ARGS) ptm_lib_init_msg(ptm_hdl, 0, PTMLIB_MSG_TYPE_CMD, NULL, &out_ctxt); - sprintf(tmp_buf, "%s", ZEBRA_PTM_BFD_CLIENT_REG_CMD); + snprintf(tmp_buf, sizeof(tmp_buf), "%s", ZEBRA_PTM_BFD_CLIENT_REG_CMD); ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_CMD_STR, tmp_buf); - sprintf(tmp_buf, "%s", zebra_route_string(client->proto)); + snprintf(tmp_buf, sizeof(tmp_buf), "%s", + zebra_route_string(client->proto)); ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_BFD_CLIENT_FIELD, tmp_buf); - sprintf(tmp_buf, "%d", pid); + snprintf(tmp_buf, sizeof(tmp_buf), "%d", pid); ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_BFD_SEQID_FIELD, tmp_buf); @@ -1046,10 +1054,11 @@ int zebra_ptm_bfd_client_deregister(struct zserv *client) ptm_lib_init_msg(ptm_hdl, 0, PTMLIB_MSG_TYPE_CMD, NULL, &out_ctxt); - sprintf(tmp_buf, "%s", ZEBRA_PTM_BFD_CLIENT_DEREG_CMD); + snprintf(tmp_buf, sizeof(tmp_buf), "%s", + ZEBRA_PTM_BFD_CLIENT_DEREG_CMD); ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_CMD_STR, tmp_buf); - sprintf(tmp_buf, "%s", zebra_route_string(proto)); + snprintf(tmp_buf, sizeof(tmp_buf), "%s", zebra_route_string(proto)); ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_BFD_CLIENT_FIELD, tmp_buf); @@ -1188,8 +1197,8 @@ static void pp_free_all(void); static void zebra_ptm_send_bfdd(struct stream *msg); static void zebra_ptm_send_clients(struct stream *msg); static int _zebra_ptm_bfd_client_deregister(struct zserv *zs); -static void _zebra_ptm_reroute(struct zserv *zs, struct stream *msg, - uint32_t command); +static void _zebra_ptm_reroute(struct zserv *zs, struct zebra_vrf *zvrf, + struct stream *msg, uint32_t command); /* @@ -1392,43 +1401,32 @@ void zebra_ptm_finish(void) /* * Message handling. */ -static void _zebra_ptm_reroute(struct zserv *zs, struct stream *msg, - uint32_t command) +static void _zebra_ptm_reroute(struct zserv *zs, struct zebra_vrf *zvrf, + struct stream *msg, uint32_t command) { struct stream *msgc; - size_t zmsglen, zhdrlen; + char buf[ZEBRA_MAX_PACKET_SIZ]; pid_t ppid; - /* - * Don't modify message in the zebra API. In order to do that we - * need to allocate a new message stream and copy the message - * provided by zebra. - */ + /* Create BFD header */ msgc = stream_new(ZEBRA_MAX_PACKET_SIZ); - if (msgc == NULL) { - zlog_debug("%s: not enough memory", __func__); - return; - } + zclient_create_header(msgc, ZEBRA_BFD_DEST_REPLAY, zvrf->vrf->vrf_id); + stream_putl(msgc, command); - /* Calculate our header size plus the message contents. */ - zhdrlen = ZEBRA_HEADER_SIZE + sizeof(uint32_t); - zmsglen = msg->endp - msg->getp; - memcpy(msgc->data + zhdrlen, msg->data + msg->getp, zmsglen); + if (STREAM_READABLE(msg) > STREAM_WRITEABLE(msgc)) { + zlog_warn("Cannot fit extended BFD header plus original message contents into ZAPI packet; dropping message"); + goto stream_failure; + } - /* - * The message type will be BFD_DEST_REPLY so we can use only - * one callback at the `bfdd` side, however the real command - * number will be included right after the zebra header. - */ - zclient_create_header(msgc, ZEBRA_BFD_DEST_REPLAY, 0); - stream_putl(msgc, command); + /* Copy original message, excluding header, into new message */ + stream_get_from(buf, msg, stream_get_getp(msg), STREAM_READABLE(msg)); + stream_put(msgc, buf, STREAM_READABLE(msg)); - /* Update the data pointers. */ - msgc->getp = 0; - msgc->endp = zhdrlen + zmsglen; - stream_putw_at(msgc, 0, stream_get_endp(msgc)); + /* Update length field */ + stream_putw_at(msgc, 0, STREAM_READABLE(msgc)); zebra_ptm_send_bfdd(msgc); + msgc = NULL; /* Registrate process PID for shutdown hook. */ STREAM_GETL(msg, ppid); @@ -1437,6 +1435,8 @@ static void _zebra_ptm_reroute(struct zserv *zs, struct stream *msg, return; stream_failure: + if (msgc) + stream_free(msgc); zlog_err("%s:%d failed to registrate client pid", __FILE__, __LINE__); } @@ -1446,7 +1446,7 @@ void zebra_ptm_bfd_dst_register(ZAPI_HANDLER_ARGS) zlog_debug("bfd_dst_register msg from client %s: length=%d", zebra_route_string(client->proto), hdr->length); - _zebra_ptm_reroute(client, msg, ZEBRA_BFD_DEST_REGISTER); + _zebra_ptm_reroute(client, zvrf, msg, ZEBRA_BFD_DEST_REGISTER); } void zebra_ptm_bfd_dst_deregister(ZAPI_HANDLER_ARGS) @@ -1455,7 +1455,7 @@ void zebra_ptm_bfd_dst_deregister(ZAPI_HANDLER_ARGS) zlog_debug("bfd_dst_deregister msg from client %s: length=%d", zebra_route_string(client->proto), hdr->length); - _zebra_ptm_reroute(client, msg, ZEBRA_BFD_DEST_DEREGISTER); + _zebra_ptm_reroute(client, zvrf, msg, ZEBRA_BFD_DEST_DEREGISTER); } void zebra_ptm_bfd_client_register(ZAPI_HANDLER_ARGS) @@ -1464,7 +1464,7 @@ void zebra_ptm_bfd_client_register(ZAPI_HANDLER_ARGS) zlog_debug("bfd_client_register msg from client %s: length=%d", zebra_route_string(client->proto), hdr->length); - _zebra_ptm_reroute(client, msg, ZEBRA_BFD_CLIENT_REGISTER); + _zebra_ptm_reroute(client, zvrf, msg, ZEBRA_BFD_CLIENT_REGISTER); } void zebra_ptm_bfd_dst_replay(ZAPI_HANDLER_ARGS) @@ -1488,7 +1488,7 @@ void zebra_ptm_bfd_dst_replay(ZAPI_HANDLER_ARGS) * special treatment. */ if (client->proto != ZEBRA_ROUTE_BFD) { - _zebra_ptm_reroute(client, msg, ZEBRA_BFD_DEST_REPLAY); + _zebra_ptm_reroute(client, zvrf, msg, ZEBRA_BFD_DEST_REPLAY); return; } diff --git a/zebra/zebra_ptm_redistribute.c b/zebra/zebra_ptm_redistribute.c index 01d5114b9f..eabc2e005e 100644 --- a/zebra/zebra_ptm_redistribute.c +++ b/zebra/zebra_ptm_redistribute.c @@ -59,6 +59,9 @@ static int zsend_interface_bfd_update(int cmd, struct zserv *client, stream_put(s, &sp->u.prefix, blen); stream_putc(s, sp->prefixlen); + /* c-bit bullshit */ + stream_putc(s, 0); + /* Write packet size. */ stream_putw_at(s, 0, stream_get_endp(s)); diff --git a/zebra/zebra_pw.c b/zebra/zebra_pw.c index 09edbc9a68..cdcca1e930 100644 --- a/zebra/zebra_pw.c +++ b/zebra/zebra_pw.c @@ -24,6 +24,8 @@ #include "thread.h" #include "command.h" #include "vrf.h" +#include "lib/json.h" +#include "printfrr.h" #include "zebra/debug.h" #include "zebra/rib.h" @@ -72,7 +74,7 @@ struct zebra_pw *zebra_pw_add(struct zebra_vrf *zvrf, const char *ifname, pw->protocol = protocol; pw->vrf_id = zvrf_id(zvrf); pw->client = client; - pw->status = PW_STATUS_DOWN; + pw->status = PW_NOT_FORWARDING; pw->local_label = MPLS_NO_LABEL; pw->remote_label = MPLS_NO_LABEL; pw->flags = F_PSEUDOWIRE_CWORD; @@ -96,7 +98,7 @@ void zebra_pw_del(struct zebra_vrf *zvrf, struct zebra_pw *pw) zebra_deregister_rnh_pseudowire(pw->vrf_id, pw); /* uninstall */ - if (pw->status == PW_STATUS_UP) { + if (pw->status == PW_FORWARDING) { hook_call(pw_uninstall, pw); dplane_pw_uninstall(pw); } else if (pw->install_retry_timer) @@ -114,8 +116,6 @@ void zebra_pw_change(struct zebra_pw *pw, ifindex_t ifindex, int type, int af, uint32_t remote_label, uint8_t flags, union pw_protocol_fields *data) { - zebra_deregister_rnh_pseudowire(pw->vrf_id, pw); - pw->ifindex = ifindex; pw->type = type; pw->af = af; @@ -125,10 +125,16 @@ void zebra_pw_change(struct zebra_pw *pw, ifindex_t ifindex, int type, int af, pw->flags = flags; pw->data = *data; - if (zebra_pw_enabled(pw)) - zebra_register_rnh_pseudowire(pw->vrf_id, pw); - else + if (zebra_pw_enabled(pw)) { + bool nht_exists; + zebra_register_rnh_pseudowire(pw->vrf_id, pw, &nht_exists); + if (nht_exists) + zebra_pw_update(pw); + } else { + if (pw->protocol == ZEBRA_ROUTE_STATIC) + zebra_deregister_rnh_pseudowire(pw->vrf_id, pw); zebra_pw_uninstall(pw); + } } struct zebra_pw *zebra_pw_find(struct zebra_vrf *zvrf, const char *ifname) @@ -153,7 +159,7 @@ void zebra_pw_update(struct zebra_pw *pw) { if (zebra_pw_check_reachability(pw) < 0) { zebra_pw_uninstall(pw); - zebra_pw_install_failure(pw); + zebra_pw_install_failure(pw, PW_NOT_FORWARDING); /* wait for NHT and try again later */ } else { /* @@ -173,17 +179,17 @@ static void zebra_pw_install(struct zebra_pw *pw) hook_call(pw_install, pw); if (dplane_pw_install(pw) == ZEBRA_DPLANE_REQUEST_FAILURE) { - zebra_pw_install_failure(pw); + zebra_pw_install_failure(pw, PW_NOT_FORWARDING); return; } - if (pw->status == PW_STATUS_DOWN) - zebra_pw_update_status(pw, PW_STATUS_UP); + if (pw->status != PW_FORWARDING) + zebra_pw_update_status(pw, PW_FORWARDING); } static void zebra_pw_uninstall(struct zebra_pw *pw) { - if (pw->status == PW_STATUS_DOWN) + if (pw->status != PW_FORWARDING) return; if (IS_ZEBRA_DEBUG_PW) @@ -196,7 +202,7 @@ static void zebra_pw_uninstall(struct zebra_pw *pw) dplane_pw_uninstall(pw); if (zebra_pw_enabled(pw)) - zebra_pw_update_status(pw, PW_STATUS_DOWN); + zebra_pw_update_status(pw, PW_NOT_FORWARDING); } /* @@ -205,12 +211,11 @@ static void zebra_pw_uninstall(struct zebra_pw *pw) * to retry the installation later. This function can be called by an external * agent that performs the pseudowire installation in an asynchronous way. */ -void zebra_pw_install_failure(struct zebra_pw *pw) +void zebra_pw_install_failure(struct zebra_pw *pw, int pwstatus) { if (IS_ZEBRA_DEBUG_PW) zlog_debug( - "%u: failed installing pseudowire %s, " - "scheduling retry in %u seconds", + "%u: failed installing pseudowire %s, scheduling retry in %u seconds", pw->vrf_id, pw->ifname, PW_INSTALL_RETRY_INTERVAL); /* schedule to retry later */ @@ -218,7 +223,7 @@ void zebra_pw_install_failure(struct zebra_pw *pw) thread_add_timer(zrouter.master, zebra_pw_install_retry, pw, PW_INSTALL_RETRY_INTERVAL, &pw->install_retry_timer); - zebra_pw_update_status(pw, PW_STATUS_DOWN); + zebra_pw_update_status(pw, pwstatus); } static int zebra_pw_install_retry(struct thread *thread) @@ -259,7 +264,7 @@ static int zebra_pw_check_reachability(struct zebra_pw *pw) * Need to ensure that there's a label binding for all nexthops. * Otherwise, ECMP for this route could render the pseudowire unusable. */ - for (ALL_NEXTHOPS(re->ng, nexthop)) { + for (ALL_NEXTHOPS(re->nhe->nhg, nexthop)) { if (!nexthop->nh_label) { if (IS_ZEBRA_DEBUG_PW) zlog_debug("%s: unlabeled route for %s", @@ -498,7 +503,7 @@ DEFUN (show_pseudowires, vty_out(vty, "%-16s %-24s %-12s %-8s %-10s\n", pw->ifname, (pw->af != AF_UNSPEC) ? buf_nbr : "-", buf_labels, zebra_route_string(pw->protocol), - (zebra_pw_enabled(pw) && pw->status == PW_STATUS_UP) + (zebra_pw_enabled(pw) && pw->status == PW_FORWARDING) ? "UP" : "DOWN"); } @@ -506,6 +511,201 @@ DEFUN (show_pseudowires, return CMD_SUCCESS; } +static void vty_show_mpls_pseudowire_detail(struct vty *vty) +{ + struct zebra_vrf *zvrf; + struct zebra_pw *pw; + struct route_entry *re; + struct nexthop *nexthop; + struct nexthop_group *nhg; + + zvrf = vrf_info_lookup(VRF_DEFAULT); + if (!zvrf) + return; + + RB_FOREACH (pw, zebra_pw_head, &zvrf->pseudowires) { + char buf_nbr[INET6_ADDRSTRLEN]; + char buf_nh[100]; + + vty_out(vty, "Interface: %s\n", pw->ifname); + inet_ntop(pw->af, &pw->nexthop, buf_nbr, sizeof(buf_nbr)); + vty_out(vty, " Neighbor: %s\n", + (pw->af != AF_UNSPEC) ? buf_nbr : "-"); + if (pw->local_label != MPLS_NO_LABEL) + vty_out(vty, " Local Label: %u\n", pw->local_label); + else + vty_out(vty, " Local Label: %s\n", "-"); + if (pw->remote_label != MPLS_NO_LABEL) + vty_out(vty, " Remote Label: %u\n", pw->remote_label); + else + vty_out(vty, " Remote Label: %s\n", "-"); + vty_out(vty, " Protocol: %s\n", + zebra_route_string(pw->protocol)); + if (pw->protocol == ZEBRA_ROUTE_LDP) + vty_out(vty, " VC-ID: %u\n", pw->data.ldp.pwid); + vty_out(vty, " Status: %s \n", + (zebra_pw_enabled(pw) && pw->status == PW_FORWARDING) + ? "Up" + : "Down"); + re = rib_match(family2afi(pw->af), SAFI_UNICAST, pw->vrf_id, + &pw->nexthop, NULL); + if (re == NULL) + continue; + + nhg = rib_get_fib_nhg(re); + for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { + snprintfrr(buf_nh, sizeof(buf_nh), "%pNHv", + nexthop); + vty_out(vty, " Next Hop: %s\n", buf_nh); + if (nexthop->nh_label) + vty_out(vty, " Next Hop label: %u\n", + nexthop->nh_label->label[0]); + else + vty_out(vty, " Next Hop label: %s\n", + "-"); + } + + /* Include any installed backups */ + nhg = rib_get_fib_backup_nhg(re); + if (nhg == NULL) + continue; + + for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { + snprintfrr(buf_nh, sizeof(buf_nh), "%pNHv", + nexthop); + vty_out(vty, " Next Hop: %s\n", buf_nh); + if (nexthop->nh_label) + vty_out(vty, " Next Hop label: %u\n", + nexthop->nh_label->label[0]); + else + vty_out(vty, " Next Hop label: %s\n", + "-"); + } + } +} + +static void vty_show_mpls_pseudowire(struct zebra_pw *pw, json_object *json_pws) +{ + struct route_entry *re; + struct nexthop *nexthop; + struct nexthop_group *nhg; + char buf_nbr[INET6_ADDRSTRLEN]; + char buf_nh[100]; + json_object *json_pw = NULL; + json_object *json_nexthop = NULL; + json_object *json_nexthops = NULL; + + json_nexthops = json_object_new_array(); + json_pw = json_object_new_object(); + + json_object_string_add(json_pw, "interface", pw->ifname); + if (pw->af == AF_UNSPEC) + json_object_string_add(json_pw, "neighbor", "-"); + else { + inet_ntop(pw->af, &pw->nexthop, buf_nbr, sizeof(buf_nbr)); + json_object_string_add(json_pw, "neighbor", buf_nbr); + } + if (pw->local_label != MPLS_NO_LABEL) + json_object_int_add(json_pw, "localLabel", pw->local_label); + else + json_object_string_add(json_pw, "localLabel", "-"); + if (pw->remote_label != MPLS_NO_LABEL) + json_object_int_add(json_pw, "remoteLabel", pw->remote_label); + else + json_object_string_add(json_pw, "remoteLabel", "-"); + json_object_string_add(json_pw, "protocol", + zebra_route_string(pw->protocol)); + if (pw->protocol == ZEBRA_ROUTE_LDP) + json_object_int_add(json_pw, "vcId", pw->data.ldp.pwid); + json_object_string_add( + json_pw, "Status", + (zebra_pw_enabled(pw) && pw->status == PW_FORWARDING) ? "Up" + : "Down"); + re = rib_match(family2afi(pw->af), SAFI_UNICAST, pw->vrf_id, + &pw->nexthop, NULL); + if (re == NULL) + goto done; + + nhg = rib_get_fib_nhg(re); + for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { + json_nexthop = json_object_new_object(); + snprintfrr(buf_nh, sizeof(buf_nh), "%pNHv", nexthop); + json_object_string_add(json_nexthop, "nexthop", buf_nh); + if (nexthop->nh_label) + json_object_int_add( + json_nexthop, "nhLabel", + nexthop->nh_label->label[0]); + else + json_object_string_add(json_nexthop, "nhLabel", + "-"); + + json_object_array_add(json_nexthops, json_nexthop); + } + + /* Include installed backup nexthops also */ + nhg = rib_get_fib_backup_nhg(re); + if (nhg == NULL) + goto done; + + for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { + json_nexthop = json_object_new_object(); + snprintfrr(buf_nh, sizeof(buf_nh), "%pNHv", nexthop); + json_object_string_add(json_nexthop, "nexthop", buf_nh); + if (nexthop->nh_label) + json_object_int_add( + json_nexthop, "nhLabel", + nexthop->nh_label->label[0]); + else + json_object_string_add(json_nexthop, "nhLabel", + "-"); + + json_object_array_add(json_nexthops, json_nexthop); + } + +done: + + json_object_object_add(json_pw, "nexthops", json_nexthops); + json_object_array_add(json_pws, json_pw); +} + +static void vty_show_mpls_pseudowire_detail_json(struct vty *vty) +{ + json_object *json = NULL; + json_object *json_pws = NULL; + struct zebra_vrf *zvrf; + struct zebra_pw *pw; + + zvrf = vrf_info_lookup(VRF_DEFAULT); + if (!zvrf) + return; + + json = json_object_new_object(); + json_pws = json_object_new_array(); + RB_FOREACH (pw, zebra_pw_head, &zvrf->pseudowires) { + vty_show_mpls_pseudowire(pw, json_pws); + } + json_object_object_add(json, "pw", json_pws); + vty_out(vty, "%s\n", + json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); +} + +DEFUN(show_pseudowires_detail, show_pseudowires_detail_cmd, + "show mpls pseudowires detail [json]$json", + SHOW_STR MPLS_STR + "Pseudowires\n" + "Detailed output\n" JSON_STR) +{ + bool uj = use_json(argc, argv); + + if (uj) + vty_show_mpls_pseudowire_detail_json(vty); + else + vty_show_mpls_pseudowire_detail(vty); + + return CMD_SUCCESS; +} + /* Pseudowire configuration write function. */ static int zebra_pw_config(struct vty *vty) { @@ -525,8 +725,7 @@ static int zebra_pw_config(struct vty *vty) pw->local_label, pw->remote_label); else vty_out(vty, - " ! Incomplete config, specify the static " - "MPLS labels\n"); + " ! Incomplete config, specify the static MPLS labels\n"); if (pw->af != AF_UNSPEC) { char buf[INET6_ADDRSTRLEN]; @@ -534,8 +733,7 @@ static int zebra_pw_config(struct vty *vty) vty_out(vty, " neighbor %s\n", buf); } else vty_out(vty, - " ! Incomplete config, specify a neighbor " - "address\n"); + " ! Incomplete config, specify a neighbor address\n"); if (!(pw->flags & F_PSEUDOWIRE_CWORD)) vty_out(vty, " control-word exclude\n"); @@ -547,13 +745,18 @@ static int zebra_pw_config(struct vty *vty) return write; } +static int zebra_pw_config(struct vty *vty); static struct cmd_node pw_node = { - PW_NODE, "%s(config-pw)# ", 1, + .name = "pw", + .node = PW_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-pw)# ", + .config_write = zebra_pw_config, }; void zebra_pw_vty_init(void) { - install_node(&pw_node, zebra_pw_config); + install_node(&pw_node); install_default(PW_NODE); install_element(CONFIG_NODE, &pseudowire_if_cmd); @@ -563,4 +766,5 @@ void zebra_pw_vty_init(void) install_element(PW_NODE, &pseudowire_control_word_cmd); install_element(VIEW_NODE, &show_pseudowires_cmd); + install_element(VIEW_NODE, &show_pseudowires_detail_cmd); } diff --git a/zebra/zebra_pw.h b/zebra/zebra_pw.h index bbb3776725..7b13c7e16b 100644 --- a/zebra/zebra_pw.h +++ b/zebra/zebra_pw.h @@ -73,7 +73,7 @@ void zebra_pw_change(struct zebra_pw *, ifindex_t, int, int, union g_addr *, uint32_t, uint32_t, uint8_t, union pw_protocol_fields *); struct zebra_pw *zebra_pw_find(struct zebra_vrf *, const char *); void zebra_pw_update(struct zebra_pw *); -void zebra_pw_install_failure(struct zebra_pw *); +void zebra_pw_install_failure(struct zebra_pw *pw, int pwstatus); void zebra_pw_init(struct zebra_vrf *); void zebra_pw_exit(struct zebra_vrf *); void zebra_pw_vty_init(void); diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 8eb309b4b2..215188ac12 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -36,6 +36,8 @@ #include "thread.h" #include "vrf.h" #include "workqueue.h" +#include "nexthop_group_private.h" +#include "frr_pthread.h" #include "zebra/zebra_router.h" #include "zebra/connected.h" @@ -55,6 +57,8 @@ #include "zebra/zapi_msg.h" #include "zebra/zebra_dplane.h" +DEFINE_MTYPE_STATIC(ZEBRA, RIB_UPDATE_CTX, "Rib update context object"); + /* * Event, list, and mutex for delivery of dataplane results */ @@ -71,68 +75,77 @@ extern int allow_delete; /* Each route type's string and default distance value. */ static const struct { int key; - int distance; + uint8_t distance; uint8_t meta_q_map; } route_info[ZEBRA_ROUTE_MAX] = { - [ZEBRA_ROUTE_SYSTEM] = {ZEBRA_ROUTE_SYSTEM, 0, 4}, - [ZEBRA_ROUTE_KERNEL] = {ZEBRA_ROUTE_KERNEL, 0, 0}, - [ZEBRA_ROUTE_CONNECT] = {ZEBRA_ROUTE_CONNECT, 0, 0}, - [ZEBRA_ROUTE_STATIC] = {ZEBRA_ROUTE_STATIC, 1, 1}, - [ZEBRA_ROUTE_RIP] = {ZEBRA_ROUTE_RIP, 120, 2}, - [ZEBRA_ROUTE_RIPNG] = {ZEBRA_ROUTE_RIPNG, 120, 2}, - [ZEBRA_ROUTE_OSPF] = {ZEBRA_ROUTE_OSPF, 110, 2}, - [ZEBRA_ROUTE_OSPF6] = {ZEBRA_ROUTE_OSPF6, 110, 2}, - [ZEBRA_ROUTE_ISIS] = {ZEBRA_ROUTE_ISIS, 115, 2}, - [ZEBRA_ROUTE_BGP] = {ZEBRA_ROUTE_BGP, 20 /* IBGP is 200. */, 3}, - [ZEBRA_ROUTE_PIM] = {ZEBRA_ROUTE_PIM, 255, 4}, - [ZEBRA_ROUTE_EIGRP] = {ZEBRA_ROUTE_EIGRP, 90, 2}, - [ZEBRA_ROUTE_NHRP] = {ZEBRA_ROUTE_NHRP, 10, 2}, - [ZEBRA_ROUTE_HSLS] = {ZEBRA_ROUTE_HSLS, 255, 4}, - [ZEBRA_ROUTE_OLSR] = {ZEBRA_ROUTE_OLSR, 255, 4}, - [ZEBRA_ROUTE_TABLE] = {ZEBRA_ROUTE_TABLE, 150, 1}, - [ZEBRA_ROUTE_LDP] = {ZEBRA_ROUTE_LDP, 150, 4}, - [ZEBRA_ROUTE_VNC] = {ZEBRA_ROUTE_VNC, 20, 3}, - [ZEBRA_ROUTE_VNC_DIRECT] = {ZEBRA_ROUTE_VNC_DIRECT, 20, 3}, - [ZEBRA_ROUTE_VNC_DIRECT_RH] = {ZEBRA_ROUTE_VNC_DIRECT_RH, 20, 3}, - [ZEBRA_ROUTE_BGP_DIRECT] = {ZEBRA_ROUTE_BGP_DIRECT, 20, 3}, - [ZEBRA_ROUTE_BGP_DIRECT_EXT] = {ZEBRA_ROUTE_BGP_DIRECT_EXT, 20, 3}, - [ZEBRA_ROUTE_BABEL] = {ZEBRA_ROUTE_BABEL, 100, 2}, - [ZEBRA_ROUTE_SHARP] = {ZEBRA_ROUTE_SHARP, 150, 4}, - [ZEBRA_ROUTE_PBR] = {ZEBRA_ROUTE_PBR, 200, 4}, - [ZEBRA_ROUTE_BFD] = {ZEBRA_ROUTE_BFD, 255, 4}, - [ZEBRA_ROUTE_OPENFABRIC] = {ZEBRA_ROUTE_OPENFABRIC, 115, 2}, + [ZEBRA_ROUTE_NHG] = {ZEBRA_ROUTE_NHG, 255 /* Uneeded for nhg's */, 0}, + [ZEBRA_ROUTE_SYSTEM] = {ZEBRA_ROUTE_SYSTEM, 0, 6}, + [ZEBRA_ROUTE_KERNEL] = {ZEBRA_ROUTE_KERNEL, 0, 2}, + [ZEBRA_ROUTE_CONNECT] = {ZEBRA_ROUTE_CONNECT, 0, 1}, + [ZEBRA_ROUTE_STATIC] = {ZEBRA_ROUTE_STATIC, 1, 3}, + [ZEBRA_ROUTE_RIP] = {ZEBRA_ROUTE_RIP, 120, 4}, + [ZEBRA_ROUTE_RIPNG] = {ZEBRA_ROUTE_RIPNG, 120, 4}, + [ZEBRA_ROUTE_OSPF] = {ZEBRA_ROUTE_OSPF, 110, 4}, + [ZEBRA_ROUTE_OSPF6] = {ZEBRA_ROUTE_OSPF6, 110, 4}, + [ZEBRA_ROUTE_ISIS] = {ZEBRA_ROUTE_ISIS, 115, 4}, + [ZEBRA_ROUTE_BGP] = {ZEBRA_ROUTE_BGP, 20 /* IBGP is 200. */, 5}, + [ZEBRA_ROUTE_PIM] = {ZEBRA_ROUTE_PIM, 255, 6}, + [ZEBRA_ROUTE_EIGRP] = {ZEBRA_ROUTE_EIGRP, 90, 4}, + [ZEBRA_ROUTE_NHRP] = {ZEBRA_ROUTE_NHRP, 10, 4}, + [ZEBRA_ROUTE_HSLS] = {ZEBRA_ROUTE_HSLS, 255, 6}, + [ZEBRA_ROUTE_OLSR] = {ZEBRA_ROUTE_OLSR, 255, 6}, + [ZEBRA_ROUTE_TABLE] = {ZEBRA_ROUTE_TABLE, 150, 3}, + [ZEBRA_ROUTE_LDP] = {ZEBRA_ROUTE_LDP, 150, 6}, + [ZEBRA_ROUTE_VNC] = {ZEBRA_ROUTE_VNC, 20, 5}, + [ZEBRA_ROUTE_VNC_DIRECT] = {ZEBRA_ROUTE_VNC_DIRECT, 20, 5}, + [ZEBRA_ROUTE_VNC_DIRECT_RH] = {ZEBRA_ROUTE_VNC_DIRECT_RH, 20, 5}, + [ZEBRA_ROUTE_BGP_DIRECT] = {ZEBRA_ROUTE_BGP_DIRECT, 20, 5}, + [ZEBRA_ROUTE_BGP_DIRECT_EXT] = {ZEBRA_ROUTE_BGP_DIRECT_EXT, 20, 5}, + [ZEBRA_ROUTE_BABEL] = {ZEBRA_ROUTE_BABEL, 100, 4}, + [ZEBRA_ROUTE_SHARP] = {ZEBRA_ROUTE_SHARP, 150, 6}, + [ZEBRA_ROUTE_PBR] = {ZEBRA_ROUTE_PBR, 200, 6}, + [ZEBRA_ROUTE_BFD] = {ZEBRA_ROUTE_BFD, 255, 6}, + [ZEBRA_ROUTE_OPENFABRIC] = {ZEBRA_ROUTE_OPENFABRIC, 115, 4}, + [ZEBRA_ROUTE_VRRP] = {ZEBRA_ROUTE_VRRP, 255, 6}, + [ZEBRA_ROUTE_SRTE] = {ZEBRA_ROUTE_SRTE, 255, 6}, /* Any new route type added to zebra, should be mirrored here */ /* no entry/default: 150 */ }; -/* RPF lookup behaviour */ -static enum multicast_mode ipv4_multicast_mode = MCAST_NO_CONFIG; - - -static void __attribute__((format(printf, 5, 6))) +static void PRINTFRR(5, 6) _rnode_zlog(const char *_func, vrf_id_t vrf_id, struct route_node *rn, int priority, const char *msgfmt, ...) { char buf[SRCDEST2STR_BUFFER + sizeof(" (MRIB)")]; char msgbuf[512]; va_list ap; + uint32_t table = 0; va_start(ap, msgfmt); vsnprintf(msgbuf, sizeof(msgbuf), msgfmt, ap); va_end(ap); if (rn) { - rib_table_info_t *info = srcdest_rnode_table_info(rn); + struct rib_table_info *info = srcdest_rnode_table_info(rn); + rib_dest_t *dest = NULL; + struct route_entry *re = NULL; + srcdest_rnode2str(rn, buf, sizeof(buf)); if (info->safi == SAFI_MULTICAST) - strcat(buf, " (MRIB)"); + strlcat(buf, " (MRIB)", sizeof(buf)); + + dest = rib_dest_from_rnode(rn); + if (dest) + re = re_list_first(&dest->routes); + if (re) + table = re->table; } else { snprintf(buf, sizeof(buf), "{(route_node *) NULL}"); } - zlog(priority, "%s: %d:%s: %s", _func, vrf_id, buf, msgbuf); + zlog(priority, "%s: (%u:%u):%s: %s", _func, vrf_id, table, buf, msgbuf); } #define rnode_debug(node, vrf_id, ...) \ @@ -165,8 +178,7 @@ int is_zebra_valid_kernel_table(uint32_t table_id) int is_zebra_main_routing_table(uint32_t table_id) { - if ((table_id == RT_TABLE_MAIN) - || (table_id == zrouter.rtm_table_default)) + if (table_id == RT_TABLE_MAIN) return 1; return 0; } @@ -192,441 +204,50 @@ int zebra_check_addr(const struct prefix *p) return 1; } -/* Add nexthop to the end of a rib node's nexthop list */ -void route_entry_nexthop_add(struct route_entry *re, struct nexthop *nexthop) -{ - nexthop_add(&re->ng.nexthop, nexthop); - re->nexthop_num++; -} - - /** * copy_nexthop - copy a nexthop to the rib structure. */ void route_entry_copy_nexthops(struct route_entry *re, struct nexthop *nh) { - assert(!re->ng.nexthop); - copy_nexthops(&re->ng.nexthop, nh, NULL); - for (struct nexthop *nexthop = nh; nexthop; nexthop = nexthop->next) - re->nexthop_num++; -} - -/* Delete specified nexthop from the list. */ -void route_entry_nexthop_delete(struct route_entry *re, struct nexthop *nexthop) -{ - if (nexthop->next) - nexthop->next->prev = nexthop->prev; - if (nexthop->prev) - nexthop->prev->next = nexthop->next; - else - re->ng.nexthop = nexthop->next; - re->nexthop_num--; -} - - -struct nexthop *route_entry_nexthop_ifindex_add(struct route_entry *re, - ifindex_t ifindex, - vrf_id_t nh_vrf_id) -{ - struct nexthop *nexthop; - - nexthop = nexthop_new(); - nexthop->type = NEXTHOP_TYPE_IFINDEX; - nexthop->ifindex = ifindex; - nexthop->vrf_id = nh_vrf_id; - - route_entry_nexthop_add(re, nexthop); - - return nexthop; -} - -struct nexthop *route_entry_nexthop_ipv4_add(struct route_entry *re, - struct in_addr *ipv4, - struct in_addr *src, - vrf_id_t nh_vrf_id) -{ - struct nexthop *nexthop; - - nexthop = nexthop_new(); - nexthop->type = NEXTHOP_TYPE_IPV4; - nexthop->vrf_id = nh_vrf_id; - nexthop->gate.ipv4 = *ipv4; - if (src) - nexthop->src.ipv4 = *src; - - route_entry_nexthop_add(re, nexthop); - - return nexthop; -} - -struct nexthop *route_entry_nexthop_ipv4_ifindex_add(struct route_entry *re, - struct in_addr *ipv4, - struct in_addr *src, - ifindex_t ifindex, - vrf_id_t nh_vrf_id) -{ - struct nexthop *nexthop; - struct interface *ifp; - - nexthop = nexthop_new(); - nexthop->vrf_id = nh_vrf_id; - nexthop->type = NEXTHOP_TYPE_IPV4_IFINDEX; - nexthop->gate.ipv4 = *ipv4; - if (src) - nexthop->src.ipv4 = *src; - nexthop->ifindex = ifindex; - ifp = if_lookup_by_index(nexthop->ifindex, nh_vrf_id); - /*Pending: need to think if null ifp here is ok during bootup? - There was a crash because ifp here was coming to be NULL */ - if (ifp) - if (connected_is_unnumbered(ifp)) - SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK); - - route_entry_nexthop_add(re, nexthop); - - return nexthop; -} - -struct nexthop *route_entry_nexthop_ipv6_add(struct route_entry *re, - struct in6_addr *ipv6, - vrf_id_t nh_vrf_id) -{ - struct nexthop *nexthop; - - nexthop = nexthop_new(); - nexthop->vrf_id = nh_vrf_id; - nexthop->type = NEXTHOP_TYPE_IPV6; - nexthop->gate.ipv6 = *ipv6; - - route_entry_nexthop_add(re, nexthop); - - return nexthop; + assert(!re->nhe->nhg.nexthop); + copy_nexthops(&re->nhe->nhg.nexthop, nh, NULL); } -struct nexthop *route_entry_nexthop_ipv6_ifindex_add(struct route_entry *re, - struct in6_addr *ipv6, - ifindex_t ifindex, - vrf_id_t nh_vrf_id) +static void route_entry_attach_ref(struct route_entry *re, + struct nhg_hash_entry *new) { - struct nexthop *nexthop; - - nexthop = nexthop_new(); - nexthop->vrf_id = nh_vrf_id; - nexthop->type = NEXTHOP_TYPE_IPV6_IFINDEX; - nexthop->gate.ipv6 = *ipv6; - nexthop->ifindex = ifindex; - - route_entry_nexthop_add(re, nexthop); - - return nexthop; -} - -struct nexthop *route_entry_nexthop_blackhole_add(struct route_entry *re, - enum blackhole_type bh_type) -{ - struct nexthop *nexthop; - - nexthop = nexthop_new(); - nexthop->vrf_id = VRF_DEFAULT; - nexthop->type = NEXTHOP_TYPE_BLACKHOLE; - nexthop->bh_type = bh_type; - - route_entry_nexthop_add(re, nexthop); - - return nexthop; -} - -static void nexthop_set_resolved(afi_t afi, const struct nexthop *newhop, - struct nexthop *nexthop) -{ - struct nexthop *resolved_hop; - - resolved_hop = nexthop_new(); - SET_FLAG(resolved_hop->flags, NEXTHOP_FLAG_ACTIVE); - - resolved_hop->vrf_id = nexthop->vrf_id; - switch (newhop->type) { - case NEXTHOP_TYPE_IPV4: - case NEXTHOP_TYPE_IPV4_IFINDEX: - /* If the resolving route specifies a gateway, use it */ - resolved_hop->type = newhop->type; - resolved_hop->gate.ipv4 = newhop->gate.ipv4; - - if (newhop->ifindex) { - resolved_hop->type = NEXTHOP_TYPE_IPV4_IFINDEX; - resolved_hop->ifindex = newhop->ifindex; - } - break; - case NEXTHOP_TYPE_IPV6: - case NEXTHOP_TYPE_IPV6_IFINDEX: - resolved_hop->type = newhop->type; - resolved_hop->gate.ipv6 = newhop->gate.ipv6; + re->nhe = new; + re->nhe_id = new->id; - if (newhop->ifindex) { - resolved_hop->type = NEXTHOP_TYPE_IPV6_IFINDEX; - resolved_hop->ifindex = newhop->ifindex; - } - break; - case NEXTHOP_TYPE_IFINDEX: - /* If the resolving route is an interface route, - * it means the gateway we are looking up is connected - * to that interface. (The actual network is _not_ onlink). - * Therefore, the resolved route should have the original - * gateway as nexthop as it is directly connected. - * - * On Linux, we have to set the onlink netlink flag because - * otherwise, the kernel won't accept the route. - */ - resolved_hop->flags |= NEXTHOP_FLAG_ONLINK; - if (afi == AFI_IP) { - resolved_hop->type = NEXTHOP_TYPE_IPV4_IFINDEX; - resolved_hop->gate.ipv4 = nexthop->gate.ipv4; - } else if (afi == AFI_IP6) { - resolved_hop->type = NEXTHOP_TYPE_IPV6_IFINDEX; - resolved_hop->gate.ipv6 = nexthop->gate.ipv6; - } - resolved_hop->ifindex = newhop->ifindex; - break; - case NEXTHOP_TYPE_BLACKHOLE: - resolved_hop->type = NEXTHOP_TYPE_BLACKHOLE; - resolved_hop->bh_type = nexthop->bh_type; - break; - } - - if (newhop->flags & NEXTHOP_FLAG_ONLINK) - resolved_hop->flags |= NEXTHOP_FLAG_ONLINK; - - /* Copy labels of the resolved route */ - if (newhop->nh_label) - nexthop_add_labels(resolved_hop, newhop->nh_label_type, - newhop->nh_label->num_labels, - &newhop->nh_label->label[0]); - - resolved_hop->rparent = nexthop; - nexthop_add(&nexthop->resolved, resolved_hop); + zebra_nhg_increment_ref(new); } -/* - * Given a nexthop we need to properly recursively resolve - * the route. As such, do a table lookup to find and match - * if at all possible. Set the nexthop->ifindex as appropriate - */ -static int nexthop_active(afi_t afi, struct route_entry *re, - struct nexthop *nexthop, - struct route_node *top) +int route_entry_update_nhe(struct route_entry *re, + struct nhg_hash_entry *new_nhghe) { - struct prefix p; - struct route_table *table; - struct route_node *rn; - struct route_entry *match = NULL; - int resolved; - struct nexthop *newhop; - struct interface *ifp; - rib_dest_t *dest; - - if ((nexthop->type == NEXTHOP_TYPE_IPV4) - || nexthop->type == NEXTHOP_TYPE_IPV6) - nexthop->ifindex = 0; - - UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE); - nexthops_free(nexthop->resolved); - nexthop->resolved = NULL; - re->nexthop_mtu = 0; - - /* - * If the kernel has sent us a route, then - * by golly gee whiz it's a good route. - */ - if (re->type == ZEBRA_ROUTE_KERNEL || - re->type == ZEBRA_ROUTE_SYSTEM) - return 1; - - /* - * Check to see if we should trust the passed in information - * for UNNUMBERED interfaces as that we won't find the GW - * address in the routing table. - * This check should suffice to handle IPv4 or IPv6 routes - * sourced from EVPN routes which are installed with the - * next hop as the remote VTEP IP. - */ - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK)) { - ifp = if_lookup_by_index(nexthop->ifindex, nexthop->vrf_id); - if (!ifp) { - if (IS_ZEBRA_DEBUG_RIB_DETAILED) - zlog_debug( - "\t%s: Onlink and interface: %u[%u] does not exist", - __PRETTY_FUNCTION__, nexthop->ifindex, - nexthop->vrf_id); - return 0; - } - if (connected_is_unnumbered(ifp)) { - if (if_is_operative(ifp)) - return 1; - else { - if (IS_ZEBRA_DEBUG_RIB_DETAILED) - zlog_debug( - "\t%s: Onlink and interface %s is not operative", - __PRETTY_FUNCTION__, ifp->name); - return 0; - } - } - if (!if_is_operative(ifp)) { - if (IS_ZEBRA_DEBUG_RIB_DETAILED) - zlog_debug( - "\t%s: Interface %s is not unnumbered", - __PRETTY_FUNCTION__, - ifp ? ifp->name : "Unknown"); - return 0; - } - } + struct nhg_hash_entry *old; + int ret = 0; - /* Make lookup prefix. */ - memset(&p, 0, sizeof(struct prefix)); - switch (afi) { - case AFI_IP: - p.family = AF_INET; - p.prefixlen = IPV4_MAX_PREFIXLEN; - p.u.prefix4 = nexthop->gate.ipv4; - break; - case AFI_IP6: - p.family = AF_INET6; - p.prefixlen = IPV6_MAX_PREFIXLEN; - p.u.prefix6 = nexthop->gate.ipv6; - break; - default: - assert(afi != AFI_IP && afi != AFI_IP6); - break; - } - /* Lookup table. */ - table = zebra_vrf_table(afi, SAFI_UNICAST, nexthop->vrf_id); - if (!table) { - if (IS_ZEBRA_DEBUG_RIB_DETAILED) - zlog_debug("\t%s: Table not found", - __PRETTY_FUNCTION__); - return 0; + if (new_nhghe == NULL) { + if (re->nhe) + zebra_nhg_decrement_ref(re->nhe); + re->nhe = NULL; + goto done; } - rn = route_node_match(table, (struct prefix *)&p); - while (rn) { - route_unlock_node(rn); - - /* Lookup should halt if we've matched against ourselves ('top', - * if specified) - i.e., we cannot have a nexthop NH1 is - * resolved by a route NH1. The exception is if the route is a - * host route. - */ - if (top && rn == top) - if (((afi == AFI_IP) && (rn->p.prefixlen != 32)) - || ((afi == AFI_IP6) && (rn->p.prefixlen != 128))) { - if (IS_ZEBRA_DEBUG_RIB_DETAILED) - zlog_debug( - "\t%s: Matched against ourself and prefix length is not max bit length", - __PRETTY_FUNCTION__); - return 0; - } - - /* Pick up selected route. */ - /* However, do not resolve over default route unless explicitly - * allowed. */ - if (is_default_prefix(&rn->p) - && !rnh_resolve_via_default(p.family)) { - if (IS_ZEBRA_DEBUG_RIB_DETAILED) - zlog_debug( - "\t:%s: Resolved against default route", - __PRETTY_FUNCTION__); - return 0; - } - - dest = rib_dest_from_rnode(rn); - if (dest && dest->selected_fib - && !CHECK_FLAG(dest->selected_fib->status, - ROUTE_ENTRY_REMOVED) - && dest->selected_fib->type != ZEBRA_ROUTE_TABLE) - match = dest->selected_fib; - - /* If there is no selected route or matched route is EGP, go up - tree. */ - if (!match) { - do { - rn = rn->parent; - } while (rn && rn->info == NULL); - if (rn) - route_lock_node(rn); - - continue; - } - - if (match->type == ZEBRA_ROUTE_CONNECT) { - /* Directly point connected route. */ - newhop = match->ng.nexthop; - if (newhop) { - if (nexthop->type == NEXTHOP_TYPE_IPV4 - || nexthop->type == NEXTHOP_TYPE_IPV6) - nexthop->ifindex = newhop->ifindex; - } - return 1; - } else if (CHECK_FLAG(re->flags, ZEBRA_FLAG_ALLOW_RECURSION)) { - resolved = 0; - for (ALL_NEXTHOPS(match->ng, newhop)) { - if (!CHECK_FLAG(match->status, - ROUTE_ENTRY_INSTALLED)) - continue; - if (CHECK_FLAG(newhop->flags, - NEXTHOP_FLAG_RECURSIVE)) - continue; + if ((re->nhe_id != 0) && (re->nhe_id != new_nhghe->id)) { + old = re->nhe; - SET_FLAG(nexthop->flags, - NEXTHOP_FLAG_RECURSIVE); - SET_FLAG(re->status, - ROUTE_ENTRY_NEXTHOPS_CHANGED); - nexthop_set_resolved(afi, newhop, nexthop); - resolved = 1; - } - if (resolved) - re->nexthop_mtu = match->mtu; - if (!resolved && IS_ZEBRA_DEBUG_RIB_DETAILED) - zlog_debug("\t%s: Recursion failed to find", - __PRETTY_FUNCTION__); - return resolved; - } else if (re->type == ZEBRA_ROUTE_STATIC) { - resolved = 0; - for (ALL_NEXTHOPS(match->ng, newhop)) { - if (!CHECK_FLAG(match->status, - ROUTE_ENTRY_INSTALLED)) - continue; - if (CHECK_FLAG(newhop->flags, - NEXTHOP_FLAG_RECURSIVE)) - continue; + route_entry_attach_ref(re, new_nhghe); - SET_FLAG(nexthop->flags, - NEXTHOP_FLAG_RECURSIVE); - nexthop_set_resolved(afi, newhop, nexthop); - resolved = 1; - } - if (resolved) - re->nexthop_mtu = match->mtu; + if (old) + zebra_nhg_decrement_ref(old); + } else if (!re->nhe) + /* This is the first time it's being attached */ + route_entry_attach_ref(re, new_nhghe); - if (!resolved && IS_ZEBRA_DEBUG_RIB_DETAILED) - zlog_debug( - "\t%s: Static route unable to resolve", - __PRETTY_FUNCTION__); - return resolved; - } else { - if (IS_ZEBRA_DEBUG_RIB_DETAILED) { - zlog_debug("\t%s: Route Type %s has not turned on recursion", - __PRETTY_FUNCTION__, - zebra_route_string(re->type)); - if (re->type == ZEBRA_ROUTE_BGP && - !CHECK_FLAG(re->flags, ZEBRA_FLAG_IBGP)) - zlog_debug("\tEBGP: see \"disable-ebgp-connected-route-check\" or \"disable-connected-check\""); - } - return 0; - } - } - if (IS_ZEBRA_DEBUG_RIB_DETAILED) - zlog_debug("\t%s: Nexthop did not lookup in table", - __PRETTY_FUNCTION__); - return 0; +done: + return ret; } struct route_entry *rib_match(afi_t afi, safi_t safi, vrf_id_t vrf_id, @@ -652,7 +273,7 @@ struct route_entry *rib_match(afi_t afi, safi_t safi, vrf_id_t vrf_id, p.prefixlen = IPV6_MAX_PREFIXLEN; } - rn = route_node_match(table, (struct prefix *)&p); + rn = route_node_match(table, &p); while (rn) { rib_dest_t *dest; @@ -696,7 +317,7 @@ struct route_entry *rib_match_ipv4_multicast(vrf_id_t vrf_id, struct route_node *m_rn = NULL, *u_rn = NULL; union g_addr gaddr = {.ipv4 = addr}; - switch (ipv4_multicast_mode) { + switch (zrouter.ipv4_multicast_mode) { case MCAST_MRIB_ONLY: return rib_match(AFI_IP, SAFI_MULTICAST, vrf_id, &gaddr, rn_out); @@ -739,8 +360,8 @@ struct route_entry *rib_match_ipv4_multicast(vrf_id_t vrf_id, char buf[BUFSIZ]; inet_ntop(AF_INET, &addr, buf, BUFSIZ); - zlog_debug("%s: %s: vrf: %u found %s, using %s", - __func__, buf, vrf_id, + zlog_debug("%s: %s: vrf: %s(%u) found %s, using %s", __func__, + buf, vrf_id_to_name(vrf_id), vrf_id, mre ? (ure ? "MRIB+URIB" : "MRIB") : ure ? "URIB" : "nothing", re == ure ? "URIB" : re == mre ? "MRIB" : "none"); @@ -748,19 +369,6 @@ struct route_entry *rib_match_ipv4_multicast(vrf_id_t vrf_id, return re; } -void multicast_mode_ipv4_set(enum multicast_mode mode) -{ - if (IS_ZEBRA_DEBUG_RIB) - zlog_debug("%s: multicast lookup mode set (%d)", __func__, - mode); - ipv4_multicast_mode = mode; -} - -enum multicast_mode multicast_mode_ipv4_get(void) -{ - return ipv4_multicast_mode; -} - struct route_entry *rib_lookup_ipv4(struct prefix_ipv4 *p, vrf_id_t vrf_id) { struct route_table *table; @@ -799,195 +407,6 @@ struct route_entry *rib_lookup_ipv4(struct prefix_ipv4 *p, vrf_id_t vrf_id) return NULL; } -#define RIB_SYSTEM_ROUTE(R) \ - ((R)->type == ZEBRA_ROUTE_KERNEL || (R)->type == ZEBRA_ROUTE_CONNECT) - -#define RIB_KERNEL_ROUTE(R) \ - ((R)->type == ZEBRA_ROUTE_KERNEL) - -/* This function verifies reachability of one given nexthop, which can be - * numbered or unnumbered, IPv4 or IPv6. The result is unconditionally stored - * in nexthop->flags field. The nexthop->ifindex will be updated - * appropriately as well. An existing route map can turn - * (otherwise active) nexthop into inactive, but not vice versa. - * - * The return value is the final value of 'ACTIVE' flag. - */ -static unsigned nexthop_active_check(struct route_node *rn, - struct route_entry *re, - struct nexthop *nexthop) -{ - struct interface *ifp; - route_map_result_t ret = RMAP_MATCH; - int family; - char buf[SRCDEST2STR_BUFFER]; - const struct prefix *p, *src_p; - struct zebra_vrf *zvrf; - - srcdest_rnode_prefixes(rn, &p, &src_p); - - if (rn->p.family == AF_INET) - family = AFI_IP; - else if (rn->p.family == AF_INET6) - family = AFI_IP6; - else - family = 0; - switch (nexthop->type) { - case NEXTHOP_TYPE_IFINDEX: - ifp = if_lookup_by_index(nexthop->ifindex, nexthop->vrf_id); - if (ifp && if_is_operative(ifp)) - SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); - else - UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); - break; - case NEXTHOP_TYPE_IPV4: - case NEXTHOP_TYPE_IPV4_IFINDEX: - family = AFI_IP; - if (nexthop_active(AFI_IP, re, nexthop, rn)) - SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); - else - UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); - break; - case NEXTHOP_TYPE_IPV6: - family = AFI_IP6; - if (nexthop_active(AFI_IP6, re, nexthop, rn)) - SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); - else - UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); - break; - case NEXTHOP_TYPE_IPV6_IFINDEX: - /* RFC 5549, v4 prefix with v6 NH */ - if (rn->p.family != AF_INET) - family = AFI_IP6; - if (IN6_IS_ADDR_LINKLOCAL(&nexthop->gate.ipv6)) { - ifp = if_lookup_by_index(nexthop->ifindex, - nexthop->vrf_id); - if (ifp && if_is_operative(ifp)) - SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); - else - UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); - } else { - if (nexthop_active(AFI_IP6, re, nexthop, rn)) - SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); - else - UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); - } - break; - case NEXTHOP_TYPE_BLACKHOLE: - SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); - break; - default: - break; - } - if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) { - if (IS_ZEBRA_DEBUG_RIB_DETAILED) - zlog_debug("\t%s: Unable to find a active nexthop", - __PRETTY_FUNCTION__); - return 0; - } - - /* XXX: What exactly do those checks do? Do we support - * e.g. IPv4 routes with IPv6 nexthops or vice versa? - */ - if (RIB_SYSTEM_ROUTE(re) || (family == AFI_IP && p->family != AF_INET) - || (family == AFI_IP6 && p->family != AF_INET6)) - return CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); - - /* The original code didn't determine the family correctly - * e.g. for NEXTHOP_TYPE_IFINDEX. Retrieve the correct afi - * from the rib_table_info in those cases. - * Possibly it may be better to use only the rib_table_info - * in every case. - */ - if (!family) { - rib_table_info_t *info; - - info = srcdest_rnode_table_info(rn); - family = info->afi; - } - - memset(&nexthop->rmap_src.ipv6, 0, sizeof(union g_addr)); - - zvrf = zebra_vrf_lookup_by_id(nexthop->vrf_id); - if (!zvrf) { - if (IS_ZEBRA_DEBUG_RIB_DETAILED) - zlog_debug("\t%s: zvrf is NULL", __PRETTY_FUNCTION__); - return CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); - } - - /* It'll get set if required inside */ - ret = zebra_route_map_check(family, re->type, re->instance, p, - nexthop, zvrf, re->tag); - if (ret == RMAP_DENYMATCH) { - if (IS_ZEBRA_DEBUG_RIB) { - srcdest_rnode2str(rn, buf, sizeof(buf)); - zlog_debug( - "%u:%s: Filtering out with NH out %s due to route map", - re->vrf_id, buf, - ifindex2ifname(nexthop->ifindex, - nexthop->vrf_id)); - } - UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); - } - return CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); -} - -/* - * Iterate over all nexthops of the given RIB entry and refresh their - * ACTIVE flag. re->nexthop_active_num is updated accordingly. If any - * nexthop is found to toggle the ACTIVE flag, the whole re structure - * is flagged with ROUTE_ENTRY_CHANGED. - * - * Return value is the new number of active nexthops. - */ -static int nexthop_active_update(struct route_node *rn, struct route_entry *re) -{ - struct nexthop *nexthop; - union g_addr prev_src; - unsigned int prev_active, new_active; - ifindex_t prev_index; - - re->nexthop_active_num = 0; - UNSET_FLAG(re->status, ROUTE_ENTRY_CHANGED); - - for (nexthop = re->ng.nexthop; nexthop; nexthop = nexthop->next) { - /* No protocol daemon provides src and so we're skipping - * tracking it */ - prev_src = nexthop->rmap_src; - prev_active = CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); - prev_index = nexthop->ifindex; - /* - * We need to respect the multipath_num here - * as that what we should be able to install from - * a multipath perpsective should not be a data plane - * decision point. - */ - new_active = nexthop_active_check(rn, re, nexthop); - if (new_active && re->nexthop_active_num >= multipath_num) { - UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); - new_active = 0; - } - if (new_active) - re->nexthop_active_num++; - /* Don't allow src setting on IPv6 addr for now */ - if (prev_active != new_active || prev_index != nexthop->ifindex - || ((nexthop->type >= NEXTHOP_TYPE_IFINDEX - && nexthop->type < NEXTHOP_TYPE_IPV6) - && prev_src.ipv4.s_addr - != nexthop->rmap_src.ipv4.s_addr) - || ((nexthop->type >= NEXTHOP_TYPE_IPV6 - && nexthop->type < NEXTHOP_TYPE_BLACKHOLE) - && !(IPV6_ADDR_SAME(&prev_src.ipv6, - &nexthop->rmap_src.ipv6))) - || CHECK_FLAG(re->status, ROUTE_ENTRY_LABELS_CHANGED)) { - SET_FLAG(re->status, ROUTE_ENTRY_CHANGED); - SET_FLAG(re->status, ROUTE_ENTRY_NEXTHOPS_CHANGED); - } - } - - return re->nexthop_active_num; -} - /* * Is this RIB labeled-unicast? It must be of type BGP and all paths * (nexthops) must have a label. @@ -999,7 +418,7 @@ int zebra_rib_labeled_unicast(struct route_entry *re) if (re->type != ZEBRA_ROUTE_BGP) return 0; - for (ALL_NEXTHOPS(re->ng, nexthop)) + for (ALL_NEXTHOPS(re->nhe->nhg, nexthop)) if (!nexthop->nh_label || !nexthop->nh_label->num_labels) return 0; @@ -1013,7 +432,7 @@ void rib_install_kernel(struct route_node *rn, struct route_entry *re, struct route_entry *old) { struct nexthop *nexthop; - rib_table_info_t *info = srcdest_rnode_table_info(rn); + struct rib_table_info *info = srcdest_rnode_table_info(rn); struct zebra_vrf *zvrf = vrf_info_lookup(re->vrf_id); const struct prefix *p, *src_p; enum zebra_dplane_result ret; @@ -1023,26 +442,17 @@ void rib_install_kernel(struct route_node *rn, struct route_entry *re, srcdest_rnode_prefixes(rn, &p, &src_p); if (info->safi != SAFI_UNICAST) { - for (ALL_NEXTHOPS(re->ng, nexthop)) + for (ALL_NEXTHOPS(re->nhe->nhg, nexthop)) SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); return; - } else { - struct nexthop *prev; - - for (ALL_NEXTHOPS(re->ng, nexthop)) { - UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_DUPLICATE); - for (ALL_NEXTHOPS(re->ng, prev)) { - if (prev == nexthop) - break; - if (nexthop_same_firsthop(nexthop, prev)) { - SET_FLAG(nexthop->flags, - NEXTHOP_FLAG_DUPLICATE); - break; - } - } - } } + + /* + * Install the resolved nexthop object first. + */ + zebra_nhg_install_kernel(re->nhe); + /* * If this is a replace to a new RE let the originator of the RE * know that they've lost @@ -1068,8 +478,18 @@ void rib_install_kernel(struct route_node *rn, struct route_entry *re, switch (ret) { case ZEBRA_DPLANE_REQUEST_QUEUED: SET_FLAG(re->status, ROUTE_ENTRY_QUEUED); - if (old) + + if (old) { SET_FLAG(old->status, ROUTE_ENTRY_QUEUED); + + /* Free old FIB nexthop group */ + UNSET_FLAG(old->status, ROUTE_ENTRY_USE_FIB_NHG); + if (old->fib_ng.nexthop) { + nexthops_free(old->fib_ng.nexthop); + old->fib_ng.nexthop = NULL; + } + } + if (zvrf) zvrf->installs_queued++; break; @@ -1079,8 +499,8 @@ void rib_install_kernel(struct route_node *rn, struct route_entry *re, srcdest_rnode2str(rn, str, sizeof(str)); flog_err(EC_ZEBRA_DP_INSTALL_FAIL, - "%u:%s: Failed to enqueue dataplane install", - re->vrf_id, str); + "%u:%u:%s: Failed to enqueue dataplane install", + re->vrf_id, re->table, str); break; } case ZEBRA_DPLANE_REQUEST_SUCCESS: @@ -1096,12 +516,12 @@ void rib_install_kernel(struct route_node *rn, struct route_entry *re, void rib_uninstall_kernel(struct route_node *rn, struct route_entry *re) { struct nexthop *nexthop; - rib_table_info_t *info = srcdest_rnode_table_info(rn); + struct rib_table_info *info = srcdest_rnode_table_info(rn); struct zebra_vrf *zvrf = vrf_info_lookup(re->vrf_id); if (info->safi != SAFI_UNICAST) { UNSET_FLAG(re->status, ROUTE_ENTRY_INSTALLED); - for (ALL_NEXTHOPS(re->ng, nexthop)) + for (ALL_NEXTHOPS(re->nhe->nhg, nexthop)) UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); return; } @@ -1139,7 +559,7 @@ void rib_uninstall_kernel(struct route_node *rn, struct route_entry *re) /* Uninstall the route from kernel. */ static void rib_uninstall(struct route_node *rn, struct route_entry *re) { - rib_table_info_t *info = srcdest_rnode_table_info(rn); + struct rib_table_info *info = srcdest_rnode_table_info(rn); rib_dest_t *dest = rib_dest_from_rnode(rn); struct nexthop *nexthop; @@ -1155,7 +575,14 @@ static void rib_uninstall(struct route_node *rn, struct route_entry *re) dest->selected_fib = NULL; - for (ALL_NEXTHOPS(re->ng, nexthop)) + /* Free FIB nexthop group, if present */ + if (re->fib_ng.nexthop) { + nexthops_free(re->fib_ng.nexthop); + re->fib_ng.nexthop = NULL; + } + UNSET_FLAG(re->status, ROUTE_ENTRY_USE_FIB_NHG); + + for (ALL_NEXTHOPS(re->nhe->nhg, nexthop)) UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); } @@ -1164,7 +591,7 @@ static void rib_uninstall(struct route_node *rn, struct route_entry *re) srcdest_rnode_prefixes(rn, &p, &src_p); - redistribute_delete(p, src_p, re); + redistribute_delete(p, src_p, re, NULL); UNSET_FLAG(re->flags, ZEBRA_FLAG_SELECTED); } } @@ -1172,11 +599,11 @@ static void rib_uninstall(struct route_node *rn, struct route_entry *re) /* * rib_can_delete_dest * - * Returns TRUE if the given dest can be deleted from the table. + * Returns true if the given dest can be deleted from the table. */ static int rib_can_delete_dest(rib_dest_t *dest) { - if (dest->routes) { + if (re_list_first(&dest->routes)) { return 0; } @@ -1204,7 +631,6 @@ static int rib_can_delete_dest(rib_dest_t *dest) void zebra_rib_evaluate_rn_nexthops(struct route_node *rn, uint32_t seq) { rib_dest_t *dest = rib_dest_from_rnode(rn); - struct listnode *node, *nnode; struct rnh *rnh; /* @@ -1220,9 +646,11 @@ void zebra_rib_evaluate_rn_nexthops(struct route_node *rn, uint32_t seq) if (IS_ZEBRA_DEBUG_NHT_DETAILED) { char buf[PREFIX_STRLEN]; - zlog_debug("%s: %s Being examined for Nexthop Tracking", - __PRETTY_FUNCTION__, - srcdest_rnode2str(rn, buf, sizeof(buf))); + zlog_debug( + "%s: %s Being examined for Nexthop Tracking Count: %zd", + __func__, + srcdest_rnode2str(rn, buf, sizeof(buf)), + dest ? rnh_list_count(&dest->nht) : 0); } if (!dest) { rn = rn->parent; @@ -1236,7 +664,7 @@ void zebra_rib_evaluate_rn_nexthops(struct route_node *rn, uint32_t seq) * nht resolution and as such we need to call the * nexthop tracking evaluation code */ - for (ALL_LIST_ELEMENTS(dest->nht, node, nnode, rnh)) { + frr_each_safe(rnh_list, &dest->nht, rnh) { struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(rnh->vrf_id); struct prefix *p = &rnh->node->p; @@ -1245,12 +673,14 @@ void zebra_rib_evaluate_rn_nexthops(struct route_node *rn, uint32_t seq) char buf1[PREFIX_STRLEN]; char buf2[PREFIX_STRLEN]; - zlog_debug("%u:%s has Nexthop(%s) depending on it, evaluating %u:%u", - zvrf->vrf->vrf_id, - srcdest_rnode2str(rn, buf1, - sizeof(buf1)), - prefix2str(p, buf2, sizeof(buf2)), - seq, rnh->seqno); + zlog_debug( + "%s(%u):%s has Nexthop(%s) Type: %s depending on it, evaluating %u:%u", + zvrf_name(zvrf), zvrf_id(zvrf), + srcdest_rnode2str(rn, buf1, + sizeof(buf1)), + prefix2str(p, buf2, sizeof(buf2)), + rnh_type2str(rnh->type), seq, + rnh->seqno); } /* @@ -1289,7 +719,7 @@ void zebra_rib_evaluate_rn_nexthops(struct route_node *rn, uint32_t seq) * Garbage collect the rib dest corresponding to the given route node * if appropriate. * - * Returns TRUE if the dest was deleted, FALSE otherwise. + * Returns true if the dest was deleted, false otherwise. */ int rib_gc_dest(struct route_node *rn) { @@ -1312,7 +742,7 @@ int rib_gc_dest(struct route_node *rn) zebra_rib_evaluate_rn_nexthops(rn, zebra_router_get_next_sequence()); dest->rnode = NULL; - list_delete(&dest->nht); + rnh_list_fini(&dest->nht); XFREE(MTYPE_RIB_DEST, dest); rn->info = NULL; @@ -1330,7 +760,7 @@ static void rib_process_add_fib(struct zebra_vrf *zvrf, struct route_node *rn, /* Update real nexthop. This may actually determine if nexthop is active * or not. */ - if (!nexthop_group_active_nexthop_num(&new->ng)) { + if (!nexthop_group_active_nexthop_num(&(new->nhe->nhg))) { UNSET_FLAG(new->status, ROUTE_ENTRY_CHANGED); return; } @@ -1338,9 +768,9 @@ static void rib_process_add_fib(struct zebra_vrf *zvrf, struct route_node *rn, if (IS_ZEBRA_DEBUG_RIB) { char buf[SRCDEST2STR_BUFFER]; srcdest_rnode2str(rn, buf, sizeof(buf)); - zlog_debug("%u:%s: Adding route rn %p, re %p (%s)", - zvrf_id(zvrf), buf, rn, new, - zebra_route_string(new->type)); + zlog_debug("%s(%u:%u):%s: Adding route rn %p, re %p (%s)", + zvrf_name(zvrf), zvrf_id(zvrf), new->table, buf, rn, + new, zebra_route_string(new->type)); } /* If labeled-unicast route, install transit LSP. */ @@ -1361,9 +791,9 @@ static void rib_process_del_fib(struct zebra_vrf *zvrf, struct route_node *rn, if (IS_ZEBRA_DEBUG_RIB) { char buf[SRCDEST2STR_BUFFER]; srcdest_rnode2str(rn, buf, sizeof(buf)); - zlog_debug("%u:%s: Deleting route rn %p, re %p (%s)", - zvrf_id(zvrf), buf, rn, old, - zebra_route_string(old->type)); + zlog_debug("%s(%u:%u):%s: Deleting route rn %p, re %p (%s)", + zvrf_name(zvrf), zvrf_id(zvrf), old->table, buf, rn, + old, zebra_route_string(old->type)); } /* If labeled-unicast route, uninstall transit LSP. */ @@ -1399,7 +829,7 @@ static void rib_process_update_fib(struct zebra_vrf *zvrf, /* Update the nexthop; we could determine here that nexthop is * inactive. */ - if (nexthop_group_active_nexthop_num(&new->ng)) + if (nexthop_group_active_nexthop_num(&(new->nhe->nhg))) nh_active = 1; /* If nexthop is active, install the selected route, if @@ -1414,15 +844,17 @@ static void rib_process_update_fib(struct zebra_vrf *zvrf, srcdest_rnode2str(rn, buf, sizeof(buf)); if (new != old) zlog_debug( - "%u:%s: Updating route rn %p, re %p (%s) old %p (%s)", - zvrf_id(zvrf), buf, rn, new, + "%s(%u:%u):%s: Updating route rn %p, re %p (%s) old %p (%s)", + zvrf_name(zvrf), zvrf_id(zvrf), + new->table, buf, rn, new, zebra_route_string(new->type), old, zebra_route_string(old->type)); else zlog_debug( - "%u:%s: Updating route rn %p, re %p (%s)", - zvrf_id(zvrf), buf, rn, new, + "%s(%u:%u):%s: Updating route rn %p, re %p (%s)", + zvrf_name(zvrf), zvrf_id(zvrf), + new->table, buf, rn, new, zebra_route_string(new->type)); } @@ -1452,23 +884,36 @@ static void rib_process_update_fib(struct zebra_vrf *zvrf, srcdest_rnode2str(rn, buf, sizeof(buf)); if (new != old) zlog_debug( - "%u:%s: Deleting route rn %p, re %p (%s) old %p (%s) - nexthop inactive", - zvrf_id(zvrf), buf, rn, new, + "%s(%u:%u):%s: Deleting route rn %p, re %p (%s) old %p (%s) - nexthop inactive", + zvrf_name(zvrf), zvrf_id(zvrf), + new->table, buf, rn, new, zebra_route_string(new->type), old, zebra_route_string(old->type)); else zlog_debug( - "%u:%s: Deleting route rn %p, re %p (%s) - nexthop inactive", - zvrf_id(zvrf), buf, rn, new, + "%s(%u:%u):%s: Deleting route rn %p, re %p (%s) - nexthop inactive", + zvrf_name(zvrf), zvrf_id(zvrf), + new->table, buf, rn, new, zebra_route_string(new->type)); } - /* If labeled-unicast route, uninstall transit LSP. */ - if (zebra_rib_labeled_unicast(old)) - zebra_mpls_lsp_uninstall(zvrf, rn, old); + /* + * When we have gotten to this point + * the new route entry has no nexthops + * that are usable and as such we need + * to remove the old route, but only + * if we were the one who installed + * the old route + */ + if (!RIB_SYSTEM_ROUTE(old)) { + /* If labeled-unicast route, uninstall transit + * LSP. */ + if (zebra_rib_labeled_unicast(old)) + zebra_mpls_lsp_uninstall(zvrf, rn, old); - rib_uninstall_kernel(rn, old); + rib_uninstall_kernel(rn, old); + } } } else { /* @@ -1517,15 +962,19 @@ static struct route_entry *rib_choose_best(struct route_entry *current, /* both are connected. are either loop or vrf? */ struct nexthop *nexthop = NULL; - for (ALL_NEXTHOPS(alternate->ng, nexthop)) { - if (if_is_loopback_or_vrf(if_lookup_by_index( - nexthop->ifindex, alternate->vrf_id))) + for (ALL_NEXTHOPS(alternate->nhe->nhg, nexthop)) { + struct interface *ifp = if_lookup_by_index( + nexthop->ifindex, alternate->vrf_id); + + if (ifp && if_is_loopback_or_vrf(ifp)) return alternate; } - for (ALL_NEXTHOPS(current->ng, nexthop)) { - if (if_is_loopback_or_vrf(if_lookup_by_index( - nexthop->ifindex, current->vrf_id))) + for (ALL_NEXTHOPS(current->nhe->nhg, nexthop)) { + struct interface *ifp = if_lookup_by_index( + nexthop->ifindex, current->vrf_id); + + if (ifp && if_is_loopback_or_vrf(ifp)) return current; } @@ -1552,6 +1001,12 @@ static struct route_entry *rib_choose_best(struct route_entry *current, return current; } +/* Core function for processing nexthop group contexts's off metaq */ +static void rib_nhg_process(struct nhg_ctx *ctx) +{ + nhg_ctx_process(ctx); +} + /* Core function for processing routing information base. */ static void rib_process(struct route_node *rn) { @@ -1565,6 +1020,7 @@ static void rib_process(struct route_node *rn) char buf[SRCDEST2STR_BUFFER]; rib_dest_t *dest; struct zebra_vrf *zvrf = NULL; + struct vrf *vrf; const struct prefix *p, *src_p; srcdest_rnode_prefixes(rn, &p, &src_p); @@ -1578,30 +1034,36 @@ static void rib_process(struct route_node *rn) vrf_id = zvrf_id(zvrf); } + vrf = vrf_lookup_by_id(vrf_id); + if (IS_ZEBRA_DEBUG_RIB) srcdest_rnode2str(rn, buf, sizeof(buf)); - if (IS_ZEBRA_DEBUG_RIB_DETAILED) - zlog_debug("%u:%s: Processing rn %p", vrf_id, buf, rn); - /* * we can have rn's that have a NULL info pointer * (dest). As such let's not let the deref happen * additionally we know RNODE_FOREACH_RE_SAFE * will not iterate so we are ok. */ - if (dest) + if (dest) { + if (IS_ZEBRA_DEBUG_RIB_DETAILED) { + struct route_entry *re = re_list_first(&dest->routes); + + zlog_debug("%s(%u:%u):%s: Processing rn %p", + VRF_LOGNAME(vrf), vrf_id, re->table, buf, + rn); + } + old_fib = dest->selected_fib; + } RNODE_FOREACH_RE_SAFE (rn, re, next) { if (IS_ZEBRA_DEBUG_RIB_DETAILED) zlog_debug( - "%u:%s: Examine re %p (%s) status %x flags %x dist %d metric %d", - vrf_id, buf, re, zebra_route_string(re->type), - re->status, re->flags, re->distance, - re->metric); - - UNSET_FLAG(re->status, ROUTE_ENTRY_NEXTHOPS_CHANGED); + "%s(%u:%u):%s: Examine re %p (%s) status %x flags %x dist %d metric %d", + VRF_LOGNAME(vrf), vrf_id, re->table, buf, re, + zebra_route_string(re->type), re->status, + re->flags, re->distance, re->metric); /* Currently selected re. */ if (CHECK_FLAG(re->flags, ZEBRA_FLAG_SELECTED)) { @@ -1613,49 +1075,61 @@ static void rib_process(struct route_node *rn) if (CHECK_FLAG(re->status, ROUTE_ENTRY_REMOVED)) continue; - /* Skip unreachable nexthop. */ - /* This first call to nexthop_active_update is merely to - * determine if there's any change to nexthops associated - * with this RIB entry. Now, rib_process() can be invoked due - * to an external event such as link down or due to - * next-hop-tracking evaluation. In the latter case, - * a decision has already been made that the NHs have changed. - * So, no need to invoke a potentially expensive call again. - * Further, since the change might be in a recursive NH which - * is not caught in the nexthop_active_update() code. Thus, we - * might miss changes to recursive NHs. + /* + * If the route entry has changed, verify/resolve + * the nexthops associated with the entry. + * + * In any event if we have nexthops that are not active + * then we cannot use this particular route entry so + * skip it. */ - if (CHECK_FLAG(re->status, ROUTE_ENTRY_CHANGED) - && !nexthop_active_update(rn, re)) { - if (re->type == ZEBRA_ROUTE_TABLE) { - /* XXX: HERE BE DRAGONS!!!!! - * In all honesty, I have not yet figured out - * what this part does or why the - * ROUTE_ENTRY_CHANGED test above is correct - * or why we need to delete a route here, and - * also not whether this concerns both selected - * and fib route, or only selected - * or only fib - * - * This entry was denied by the 'ip protocol - * table' route-map, we need to delete it */ - if (re != old_selected) { - if (IS_ZEBRA_DEBUG_RIB) - zlog_debug( - "%s: %u:%s: imported via import-table but denied " - "by the ip protocol table route-map", - __func__, vrf_id, buf); - rib_unlink(rn, re); - } else - SET_FLAG(re->status, - ROUTE_ENTRY_REMOVED); - } + if (CHECK_FLAG(re->status, ROUTE_ENTRY_CHANGED)) { + if (!nexthop_active_update(rn, re)) { + if (re->type == ZEBRA_ROUTE_TABLE) { + /* XXX: HERE BE DRAGONS!!!!! + * In all honesty, I have not yet + * figured out what this part does or + * why the ROUTE_ENTRY_CHANGED test + * above is correct or why we need to + * delete a route here, and also not + * whether this concerns both selected + * and fib route, or only selected + * or only fib + * + * This entry was denied by the 'ip + * protocol + * table' route-map, we need to delete + * it */ + if (re != old_selected) { + if (IS_ZEBRA_DEBUG_RIB) + zlog_debug( + "%s: %s(%u):%s: imported via import-table but denied by the ip protocol table route-map", + __func__, + VRF_LOGNAME( + vrf), + vrf_id, buf); + rib_unlink(rn, re); + } else + SET_FLAG(re->status, + ROUTE_ENTRY_REMOVED); + } - continue; + continue; + } + } else { + /* + * If the re has not changed and the nhg we have is + * not usable, then we cannot use this route entry + * for consideration, as that the route will just + * not install if it is selected. + */ + if (!nexthop_group_active_nexthop_num(&re->nhe->nhg)) + continue; } /* Infinite distance. */ - if (re->distance == DISTANCE_INFINITY) { + if (re->distance == DISTANCE_INFINITY && + re->type != ZEBRA_ROUTE_KERNEL) { UNSET_FLAG(re->status, ROUTE_ENTRY_CHANGED); continue; } @@ -1693,9 +1167,19 @@ static void rib_process(struct route_node *rn) */ if (IS_ZEBRA_DEBUG_RIB_DETAILED) { + struct route_entry *entry; + + entry = old_selected + ? old_selected + : new_selected + ? new_selected + : old_fib ? old_fib + : new_fib ? new_fib : NULL; + zlog_debug( - "%u:%s: After processing: old_selected %p new_selected %p old_fib %p new_fib %p", - vrf_id, buf, (void *)old_selected, (void *)new_selected, + "%s(%u:%u):%s: After processing: old_selected %p new_selected %p old_fib %p new_fib %p", + VRF_LOGNAME(vrf), vrf_id, entry ? entry->table : 0, buf, + (void *)old_selected, (void *)new_selected, (void *)old_fib, (void *)new_fib); } @@ -1722,8 +1206,17 @@ static void rib_process(struct route_node *rn) SET_FLAG(new_selected->flags, ZEBRA_FLAG_SELECTED); if (old_selected) { - if (!new_selected) - redistribute_delete(p, src_p, old_selected); + /* + * If we're removing the old entry, we should tell + * redist subscribers about that *if* they aren't + * going to see a redist for the new entry. + */ + if (!new_selected || CHECK_FLAG(old_selected->status, + ROUTE_ENTRY_REMOVED)) + redistribute_delete(p, src_p, + old_selected, + new_selected); + if (old_selected != new_selected) UNSET_FLAG(old_selected->flags, ZEBRA_FLAG_SELECTED); @@ -1758,8 +1251,8 @@ static void zebra_rib_evaluate_mpls(struct route_node *rn) if (CHECK_FLAG(dest->flags, RIB_DEST_UPDATE_LSPS)) { if (IS_ZEBRA_DEBUG_MPLS) zlog_debug( - "%u: Scheduling all LSPs upon RIB completion", - zvrf_id(zvrf)); + "%s(%u): Scheduling all LSPs upon RIB completion", + zvrf_name(zvrf), zvrf_id(zvrf)); zebra_mpls_lsp_schedule(zvrf); mpls_unmark_lsps_for_processing(rn); } @@ -1783,13 +1276,17 @@ static bool rib_route_match_ctx(const struct route_entry *re, (re->instance == dplane_ctx_get_old_instance(ctx))) { result = true; - /* TODO -- we're using this extra test, but it's not - * exactly clear why. + /* We use an extra test for statics, and another for + * kernel routes. */ if (re->type == ZEBRA_ROUTE_STATIC && (re->distance != dplane_ctx_get_old_distance(ctx) || re->tag != dplane_ctx_get_old_tag(ctx))) { result = false; + } else if (re->type == ZEBRA_ROUTE_KERNEL && + re->metric != + dplane_ctx_get_old_metric(ctx)) { + result = false; } } @@ -1807,13 +1304,19 @@ static bool rib_route_match_ctx(const struct route_entry *re, (re->instance == dplane_ctx_get_instance(ctx))) { result = true; - /* TODO -- we're using this extra test, but it's not - * exactly clear why. + /* We use an extra test for statics, and another for + * kernel routes. */ if (re->type == ZEBRA_ROUTE_STATIC && (re->distance != dplane_ctx_get_distance(ctx) || re->tag != dplane_ctx_get_tag(ctx))) { result = false; + } else if (re->type == ZEBRA_ROUTE_KERNEL && + re->metric != dplane_ctx_get_metric(ctx)) { + result = false; + } else if (re->type == ZEBRA_ROUTE_CONNECT) { + result = nexthop_group_equal_no_recurse( + &re->nhe->nhg, dplane_ctx_get_ng(ctx)); } } } @@ -1837,8 +1340,9 @@ static void zebra_rib_fixup_system(struct route_node *rn) continue; SET_FLAG(re->status, ROUTE_ENTRY_INSTALLED); + UNSET_FLAG(re->status, ROUTE_ENTRY_QUEUED); - for (ALL_NEXTHOPS(re->ng, nhop)) { + for (ALL_NEXTHOPS(re->nhe->nhg, nhop)) { if (CHECK_FLAG(nhop->flags, NEXTHOP_FLAG_RECURSIVE)) continue; @@ -1847,41 +1351,393 @@ static void zebra_rib_fixup_system(struct route_node *rn) } } +/* Route comparison logic, with various special cases. */ +static bool rib_compare_routes(const struct route_entry *re1, + const struct route_entry *re2) +{ + if (re1->type != re2->type) + return false; + + if (re1->instance != re2->instance) + return false; + + if (re1->type == ZEBRA_ROUTE_KERNEL && re1->metric != re2->metric) + return false; + + if (CHECK_FLAG(re1->flags, ZEBRA_FLAG_RR_USE_DISTANCE) && + re1->distance != re2->distance) + return false; + + /* We support multiple connected routes: this supports multiple + * v6 link-locals, and we also support multiple addresses in the same + * subnet on a single interface. + */ + if (re1->type != ZEBRA_ROUTE_CONNECT) + return true; + + return false; +} + /* - * Route-update results processing after async dataplane update. + * Compare nexthop lists from a route and a dplane context; test whether + * the list installed in the FIB matches the route's list. + * Set 'changed_p' to 'true' if there were changes to the route's + * installed nexthops. + * + * Return 'false' if any ACTIVE route nexthops are not mentioned in the FIB + * list. */ -static void rib_process_result(struct zebra_dplane_ctx *ctx) +static bool rib_update_nhg_from_ctx(struct nexthop_group *re_nhg, + const struct nexthop_group *ctx_nhg, + bool *changed_p) { - struct route_table *table = NULL; - struct zebra_vrf *zvrf = NULL; - struct route_node *rn = NULL; - struct route_entry *re = NULL, *old_re = NULL, *rib; - bool is_update = false; + bool matched_p = true; struct nexthop *nexthop, *ctx_nexthop; + + /* Get the first `installed` one to check against. + * If the dataplane doesn't set these to be what was actually installed, + * it will just be whatever was in re->nhe->nhg? + */ + ctx_nexthop = ctx_nhg->nexthop; + + if (CHECK_FLAG(ctx_nexthop->flags, NEXTHOP_FLAG_RECURSIVE) + || !CHECK_FLAG(ctx_nexthop->flags, NEXTHOP_FLAG_ACTIVE)) + ctx_nexthop = nexthop_next_active_resolved(ctx_nexthop); + + for (ALL_NEXTHOPS_PTR(re_nhg, nexthop)) { + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + continue; + + if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) + continue; + + /* Check for a FIB nexthop corresponding to the RIB nexthop */ + if (!nexthop_same(ctx_nexthop, nexthop)) { + /* If the FIB doesn't know about the nexthop, + * it's not installed + */ + if (IS_ZEBRA_DEBUG_RIB_DETAILED || + IS_ZEBRA_DEBUG_NHG_DETAIL) { + zlog_debug("%s: no ctx match for rib nh %pNHv %s", + __func__, nexthop, + (CHECK_FLAG(nexthop->flags, + NEXTHOP_FLAG_FIB) ? + "(FIB)":"")); + } + matched_p = false; + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) + *changed_p = true; + + UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); + + /* Keep checking nexthops */ + continue; + } + + if (CHECK_FLAG(ctx_nexthop->flags, NEXTHOP_FLAG_FIB)) { + if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) { + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: rib nh %pNHv -> installed", + __func__, nexthop); + + *changed_p = true; + } + + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); + } else { + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) { + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: rib nh %pNHv -> uninstalled", + __func__, nexthop); + + *changed_p = true; + } + + UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); + } + + ctx_nexthop = nexthop_next_active_resolved(ctx_nexthop); + } + + return matched_p; +} + +/* + * Update a route from a dplane context. This consolidates common code + * that can be used in processing of results from FIB updates, and in + * async notification processing. + * The return is 'true' if the installed nexthops changed; 'false' otherwise. + */ +static bool rib_update_re_from_ctx(struct route_entry *re, + struct route_node *rn, + struct zebra_dplane_ctx *ctx) +{ char dest_str[PREFIX_STRLEN] = ""; - enum dplane_op_e op; - enum zebra_dplane_result status; + struct nexthop *nexthop; + bool matched; + const struct nexthop_group *ctxnhg; + struct nexthop_group *re_nhg; + bool is_selected = false; /* Is 're' currently the selected re? */ + bool changed_p = false; /* Change to nexthops? */ + rib_dest_t *dest; + struct vrf *vrf; + + vrf = vrf_lookup_by_id(re->vrf_id); + + /* Note well: only capturing the prefix string if debug is enabled here; + * unconditional log messages will have to generate the string. + */ + if (IS_ZEBRA_DEBUG_RIB) + prefix2str(&(rn->p), dest_str, sizeof(dest_str)); + + dest = rib_dest_from_rnode(rn); + if (dest) + is_selected = (re == dest->selected_fib); + + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug("update_from_ctx: %s(%u:%u):%s: %sSELECTED, re %p", + VRF_LOGNAME(vrf), re->vrf_id, re->table, dest_str, + (is_selected ? "" : "NOT "), re); + + /* Update zebra's nexthop FIB flag for each nexthop that was installed. + * If the installed set differs from the set requested by the rib/owner, + * we use the fib-specific nexthop-group to record the actual FIB + * status. + */ + matched = false; + ctxnhg = dplane_ctx_get_ng(ctx); + + /* Check route's fib group and incoming notif group for equivalence. + * + * Let's assume the nexthops are ordered here to save time. + */ + /* TODO -- this isn't testing or comparing the FIB flags; we should + * do a more explicit loop, checking the incoming notification's flags. + */ + if (re->fib_ng.nexthop && ctxnhg->nexthop && + nexthop_group_equal(&re->fib_ng, ctxnhg)) + matched = true; + + /* If the new FIB set matches the existing FIB set, we're done. */ + if (matched) { + if (IS_ZEBRA_DEBUG_RIB) + zlog_debug( + "%s(%u:%u):%s update_from_ctx(): existing fib nhg, no change", + VRF_LOGNAME(vrf), re->vrf_id, re->table, + dest_str); + goto check_backups; + + } else if (CHECK_FLAG(re->status, ROUTE_ENTRY_USE_FIB_NHG)) { + /* + * Free stale fib list and move on to check the rib nhg. + */ + if (IS_ZEBRA_DEBUG_RIB) + zlog_debug( + "%s(%u:%u):%s update_from_ctx(): replacing fib nhg", + VRF_LOGNAME(vrf), re->vrf_id, re->table, + dest_str); + nexthops_free(re->fib_ng.nexthop); + re->fib_ng.nexthop = NULL; + + UNSET_FLAG(re->status, ROUTE_ENTRY_USE_FIB_NHG); + + /* Note that the installed nexthops have changed */ + changed_p = true; + } else { + if (IS_ZEBRA_DEBUG_RIB) + zlog_debug("%s(%u:%u):%s update_from_ctx(): no fib nhg", + VRF_LOGNAME(vrf), re->vrf_id, re->table, + dest_str); + } + + /* + * Compare with the rib nexthop group. The comparison here is different: + * the RIB group may be a superset of the list installed in the FIB. We + * walk the RIB group, looking for the 'installable' candidate + * nexthops, and then check those against the set + * that is actually installed. + * + * Assume nexthops are ordered here as well. + */ + + /* If nothing is installed, we can skip some of the checking/comparison + * of nexthops. + */ + if (ctxnhg->nexthop == NULL) { + changed_p = true; + goto no_nexthops; + } + + matched = rib_update_nhg_from_ctx(&(re->nhe->nhg), ctxnhg, &changed_p); + + /* If all nexthops were processed, we're done */ + if (matched) { + if (IS_ZEBRA_DEBUG_RIB) + zlog_debug( + "%s(%u:%u):%s update_from_ctx(): rib nhg matched, changed '%s'", + VRF_LOGNAME(vrf), re->vrf_id, re->table, + dest_str, (changed_p ? "true" : "false")); + goto check_backups; + } + +no_nexthops: + + /* FIB nexthop set differs from the RIB set: + * create a fib-specific nexthop-group + */ + if (IS_ZEBRA_DEBUG_RIB) + zlog_debug( + "%s(%u:%u):%s update_from_ctx(): changed %s, adding new fib nhg%s", + VRF_LOGNAME(vrf), re->vrf_id, re->table, dest_str, + (changed_p ? "true" : "false"), + ctxnhg->nexthop != NULL ? "" : " (empty)"); + + /* Set the flag about the dedicated fib list */ + SET_FLAG(re->status, ROUTE_ENTRY_USE_FIB_NHG); + if (ctxnhg->nexthop) + copy_nexthops(&(re->fib_ng.nexthop), ctxnhg->nexthop, NULL); + +check_backups: + + /* + * Check the status of the route's backup nexthops, if any. + * The logic for backups is somewhat different: if any backup is + * installed, a new fib nhg will be attached to the route. + */ + re_nhg = zebra_nhg_get_backup_nhg(re->nhe); + if (re_nhg == NULL) + goto done; /* No backup nexthops */ + + /* First check the route's 'fib' list of backups, if it's present + * from some previous event. + */ + re_nhg = &re->fib_backup_ng; + ctxnhg = dplane_ctx_get_backup_ng(ctx); + + matched = false; + if (re_nhg->nexthop && ctxnhg && nexthop_group_equal(re_nhg, ctxnhg)) + matched = true; + + /* If the new FIB set matches an existing FIB set, we're done. */ + if (matched) { + if (IS_ZEBRA_DEBUG_RIB) + zlog_debug( + "%s(%u):%s update_from_ctx(): existing fib backup nhg, no change", + VRF_LOGNAME(vrf), re->vrf_id, dest_str); + goto done; + + } else if (re->fib_backup_ng.nexthop) { + /* + * Free stale fib backup list and move on to check + * the route's backups. + */ + if (IS_ZEBRA_DEBUG_RIB) + zlog_debug( + "%s(%u):%s update_from_ctx(): replacing fib backup nhg", + VRF_LOGNAME(vrf), re->vrf_id, dest_str); + nexthops_free(re->fib_backup_ng.nexthop); + re->fib_backup_ng.nexthop = NULL; + + /* Note that the installed nexthops have changed */ + changed_p = true; + } else { + if (IS_ZEBRA_DEBUG_RIB) + zlog_debug("%s(%u):%s update_from_ctx(): no fib backup nhg", + VRF_LOGNAME(vrf), re->vrf_id, dest_str); + } + + /* + * If a FIB backup nexthop set exists, attach a copy + * to the route if any backup is installed + */ + if (ctxnhg && ctxnhg->nexthop) { + + for (ALL_NEXTHOPS_PTR(ctxnhg, nexthop)) { + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) + break; + } + + /* If no installed backups, we're done */ + if (nexthop == NULL) + goto done; + + if (IS_ZEBRA_DEBUG_RIB) + zlog_debug("%s(%u):%s update_from_ctx(): changed %s, adding new backup fib nhg", + VRF_LOGNAME(vrf), re->vrf_id, dest_str, + (changed_p ? "true" : "false")); + + copy_nexthops(&(re->fib_backup_ng.nexthop), ctxnhg->nexthop, + NULL); + } + +done: + + return changed_p; +} + +/* + * Helper to locate a zebra route-node from a dplane context. This is used + * when processing dplane results, e.g. Note well: the route-node is returned + * with a ref held - route_unlock_node() must be called eventually. + */ +static struct route_node * +rib_find_rn_from_ctx(const struct zebra_dplane_ctx *ctx) +{ + struct route_table *table = NULL; + struct route_node *rn = NULL; const struct prefix *dest_pfx, *src_pfx; - uint32_t seq; /* Locate rn and re(s) from ctx */ - table = zebra_vrf_table_with_table_id(dplane_ctx_get_afi(ctx), - dplane_ctx_get_safi(ctx), - dplane_ctx_get_vrf(ctx), - dplane_ctx_get_table(ctx)); + table = zebra_vrf_lookup_table_with_table_id( + dplane_ctx_get_afi(ctx), dplane_ctx_get_safi(ctx), + dplane_ctx_get_vrf(ctx), dplane_ctx_get_table(ctx)); if (table == NULL) { if (IS_ZEBRA_DEBUG_DPLANE) { - zlog_debug("Failed to process dplane results: no table for afi %d, safi %d, vrf %u", - dplane_ctx_get_afi(ctx), - dplane_ctx_get_safi(ctx), - dplane_ctx_get_vrf(ctx)); + zlog_debug( + "Failed to find route for ctx: no table for afi %d, safi %d, vrf %s(%u)", + dplane_ctx_get_afi(ctx), + dplane_ctx_get_safi(ctx), + vrf_id_to_name(dplane_ctx_get_vrf(ctx)), + dplane_ctx_get_vrf(ctx)); } goto done; } - zvrf = vrf_info_lookup(dplane_ctx_get_vrf(ctx)); + dest_pfx = dplane_ctx_get_dest(ctx); + src_pfx = dplane_ctx_get_src(ctx); + + rn = srcdest_rnode_get(table, dest_pfx, + src_pfx ? (struct prefix_ipv6 *)src_pfx : NULL); + +done: + return rn; +} + + +/* + * Route-update results processing after async dataplane update. + */ +static void rib_process_result(struct zebra_dplane_ctx *ctx) +{ + struct zebra_vrf *zvrf = NULL; + struct vrf *vrf; + struct route_node *rn = NULL; + struct route_entry *re = NULL, *old_re = NULL, *rib; + bool is_update = false; + char dest_str[PREFIX_STRLEN] = ""; + enum dplane_op_e op; + enum zebra_dplane_result status; + const struct prefix *dest_pfx, *src_pfx; + uint32_t seq; + rib_dest_t *dest; + bool fib_changed = false; + + zvrf = vrf_info_lookup(dplane_ctx_get_vrf(ctx)); + vrf = vrf_lookup_by_id(dplane_ctx_get_vrf(ctx)); dest_pfx = dplane_ctx_get_dest(ctx); /* Note well: only capturing the prefix string if debug is enabled here; @@ -1890,26 +1746,30 @@ static void rib_process_result(struct zebra_dplane_ctx *ctx) if (IS_ZEBRA_DEBUG_DPLANE) prefix2str(dest_pfx, dest_str, sizeof(dest_str)); - src_pfx = dplane_ctx_get_src(ctx); - rn = srcdest_rnode_get(table, dplane_ctx_get_dest(ctx), - src_pfx ? (struct prefix_ipv6 *)src_pfx : NULL); + /* Locate rn and re(s) from ctx */ + rn = rib_find_rn_from_ctx(ctx); if (rn == NULL) { if (IS_ZEBRA_DEBUG_DPLANE) { - zlog_debug("Failed to process dplane results: no route for %u:%s", - dplane_ctx_get_vrf(ctx), dest_str); + zlog_debug( + "Failed to process dplane results: no route for %s(%u):%s", + VRF_LOGNAME(vrf), dplane_ctx_get_vrf(ctx), + dest_str); } goto done; } + dest = rib_dest_from_rnode(rn); srcdest_rnode_prefixes(rn, &dest_pfx, &src_pfx); op = dplane_ctx_get_op(ctx); status = dplane_ctx_get_status(ctx); if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) - zlog_debug("%u:%s Processing dplane ctx %p, op %s result %s", - dplane_ctx_get_vrf(ctx), dest_str, ctx, - dplane_op2str(op), dplane_res2str(status)); + zlog_debug( + "%s(%u:%u):%s Processing dplane result ctx %p, op %s result %s", + VRF_LOGNAME(vrf), dplane_ctx_get_vrf(ctx), + dplane_ctx_get_table(ctx), dest_str, ctx, + dplane_op2str(op), dplane_res2str(status)); /* * Update is a bit of a special case, where we may have both old and new @@ -1947,9 +1807,10 @@ static void rib_process_result(struct zebra_dplane_ctx *ctx) if (re) { if (re->dplane_sequence != seq) { if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) - zlog_debug("%u:%s Stale dplane result for re %p", - dplane_ctx_get_vrf(ctx), - dest_str, re); + zlog_debug( + "%s(%u):%s Stale dplane result for re %p", + VRF_LOGNAME(vrf), + dplane_ctx_get_vrf(ctx), dest_str, re); } else UNSET_FLAG(re->status, ROUTE_ENTRY_QUEUED); } @@ -1957,9 +1818,11 @@ static void rib_process_result(struct zebra_dplane_ctx *ctx) if (old_re) { if (old_re->dplane_sequence != dplane_ctx_get_old_seq(ctx)) { if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) - zlog_debug("%u:%s Stale dplane result for old_re %p", - dplane_ctx_get_vrf(ctx), - dest_str, old_re); + zlog_debug( + "%s(%u:%u):%s Stale dplane result for old_re %p", + VRF_LOGNAME(vrf), + dplane_ctx_get_vrf(ctx), old_re->table, + dest_str, old_re); } else UNSET_FLAG(old_re->status, ROUTE_ENTRY_QUEUED); } @@ -1986,34 +1849,29 @@ static void rib_process_result(struct zebra_dplane_ctx *ctx) UNSET_FLAG(old_re->status, ROUTE_ENTRY_INSTALLED); } - /* Update zebra nexthop FIB flag for each - * nexthop that was installed. - */ - for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx), - ctx_nexthop)) { - if (!re) - continue; + /* Update zebra route based on the results in + * the context struct. + */ + if (re) { + fib_changed = + rib_update_re_from_ctx(re, rn, ctx); - for (ALL_NEXTHOPS(re->ng, nexthop)) { - if (nexthop_same(ctx_nexthop, nexthop)) - break; + if (!fib_changed) { + if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) + zlog_debug( + "%s(%u:%u):%s no fib change for re", + VRF_LOGNAME(vrf), + dplane_ctx_get_vrf(ctx), + dplane_ctx_get_table( + ctx), + dest_str); } - if (nexthop == NULL) - continue; - - if (CHECK_FLAG(nexthop->flags, - NEXTHOP_FLAG_RECURSIVE)) - continue; - - if (CHECK_FLAG(ctx_nexthop->flags, - NEXTHOP_FLAG_FIB)) - SET_FLAG(nexthop->flags, - NEXTHOP_FLAG_FIB); - else - UNSET_FLAG(nexthop->flags, - NEXTHOP_FLAG_FIB); + /* Redistribute if this is the selected re */ + if (dest && re == dest->selected_fib) + redistribute_update(dest_pfx, src_pfx, + re, old_re); } /* @@ -2030,19 +1888,6 @@ static void rib_process_result(struct zebra_dplane_ctx *ctx) if (zvrf) zvrf->installs++; - /* Redistribute */ - /* - * TODO -- still calling the redist api using the - * route_entries, and there's a corner-case here: - * if there's no client for the 'new' route, a redist - * deleting the 'old' route will be sent. But if the - * 'old' context info was stale, 'old_re' will be - * NULL here and that delete will not be sent. - */ - if (re) - redistribute_update(dest_pfx, src_pfx, - re, old_re); - /* Notify route owner */ zsend_route_notify_owner_ctx(ctx, ZAPI_ROUTE_INSTALLED); @@ -2056,10 +1901,11 @@ static void rib_process_result(struct zebra_dplane_ctx *ctx) zsend_route_notify_owner(re, dest_pfx, ZAPI_ROUTE_FAIL_INSTALL); - zlog_warn("%u:%s: Route install failed", - dplane_ctx_get_vrf(ctx), - prefix2str(dest_pfx, - dest_str, sizeof(dest_str))); + zlog_warn("%s(%u:%u):%s: Route install failed", + VRF_LOGNAME(vrf), dplane_ctx_get_vrf(ctx), + dplane_ctx_get_table(ctx), + prefix2str(dest_pfx, dest_str, + sizeof(dest_str))); } break; case DPLANE_OP_ROUTE_DELETE: @@ -2085,10 +1931,11 @@ static void rib_process_result(struct zebra_dplane_ctx *ctx) zsend_route_notify_owner_ctx(ctx, ZAPI_ROUTE_REMOVE_FAIL); - zlog_warn("%u:%s: Route Deletion failure", - dplane_ctx_get_vrf(ctx), - prefix2str(dest_pfx, - dest_str, sizeof(dest_str))); + zlog_warn("%s(%u:%u):%s: Route Deletion failure", + VRF_LOGNAME(vrf), dplane_ctx_get_vrf(ctx), + dplane_ctx_get_table(ctx), + prefix2str(dest_pfx, dest_str, + sizeof(dest_str))); } /* @@ -2117,32 +1964,298 @@ static void rib_process_result(struct zebra_dplane_ctx *ctx) dplane_ctx_fini(&ctx); } -/* Take a list of route_node structs and return 1, if there was a record - * picked from it and processed by rib_process(). Don't process more, - * than one RN record; operate only in the specified sub-queue. +/* + * Count installed/FIB nexthops */ -static unsigned int process_subq(struct list *subq, uint8_t qindex) +static int rib_count_installed_nh(struct route_entry *re) { - struct listnode *lnode = listhead(subq); - struct route_node *rnode; + int count = 0; + struct nexthop *nexthop; + struct nexthop_group *nhg; + + nhg = rib_get_fib_nhg(re); + + for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { + /* The meaningful flag depends on where the installed + * nexthops reside. + */ + if (nhg == &(re->fib_ng)) { + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) + count++; + } else { + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) + count++; + } + } + + nhg = rib_get_fib_backup_nhg(re); + if (nhg) { + for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) + count++; + } + } + + return count; +} + +/* + * Handle notification from async dataplane: the dataplane has detected + * some change to a route, and notifies zebra so that the control plane + * can reflect that change. + */ +static void rib_process_dplane_notify(struct zebra_dplane_ctx *ctx) +{ + struct route_node *rn = NULL; + struct route_entry *re = NULL; + struct vrf *vrf; + struct nexthop *nexthop; + char dest_str[PREFIX_STRLEN] = ""; + const struct prefix *dest_pfx, *src_pfx; rib_dest_t *dest; - struct zebra_vrf *zvrf = NULL; + bool fib_changed = false; + bool debug_p = IS_ZEBRA_DEBUG_DPLANE | IS_ZEBRA_DEBUG_RIB; + int start_count, end_count; + dest_pfx = dplane_ctx_get_dest(ctx); + vrf = vrf_lookup_by_id(dplane_ctx_get_vrf(ctx)); - if (!lnode) - return 0; + /* Note well: only capturing the prefix string if debug is enabled here; + * unconditional log messages will have to generate the string. + */ + if (debug_p) + prefix2str(dest_pfx, dest_str, sizeof(dest_str)); + + /* Locate rn and re(s) from ctx */ + rn = rib_find_rn_from_ctx(ctx); + if (rn == NULL) { + if (debug_p) { + zlog_debug( + "Failed to process dplane notification: no routes for %s(%u:%u):%s", + VRF_LOGNAME(vrf), dplane_ctx_get_vrf(ctx), + dplane_ctx_get_table(ctx), dest_str); + } + goto done; + } + + dest = rib_dest_from_rnode(rn); + srcdest_rnode_prefixes(rn, &dest_pfx, &src_pfx); + + if (debug_p) + zlog_debug("%s(%u:%u):%s Processing dplane notif ctx %p", + VRF_LOGNAME(vrf), dplane_ctx_get_vrf(ctx), + dplane_ctx_get_table(ctx), dest_str, ctx); + + /* + * Take a pass through the routes, look for matches with the context + * info. + */ + RNODE_FOREACH_RE(rn, re) { + if (rib_route_match_ctx(re, ctx, false /*!update*/)) + break; + } + + /* No match? Nothing we can do */ + if (re == NULL) { + if (debug_p) + zlog_debug( + "%s(%u:%u):%s Unable to process dplane notification: no entry for type %s", + VRF_LOGNAME(vrf), dplane_ctx_get_vrf(ctx), + dplane_ctx_get_table(ctx), dest_str, + zebra_route_string(dplane_ctx_get_type(ctx))); + + goto done; + } + + /* Ensure we clear the QUEUED flag */ + UNSET_FLAG(re->status, ROUTE_ENTRY_QUEUED); + + /* Is this a notification that ... matters? We mostly care about + * the route that is currently selected for installation; we may also + * get an un-install notification, and handle that too. + */ + if (re != dest->selected_fib) { + /* + * If we need to, clean up after a delete that was part of + * an update operation. + */ + end_count = 0; + for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx), nexthop)) { + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) + end_count++; + } + + /* If no nexthops or none installed, ensure that this re + * gets its 'installed' flag cleared. + */ + if (end_count == 0) { + if (CHECK_FLAG(re->status, ROUTE_ENTRY_INSTALLED)) + UNSET_FLAG(re->status, ROUTE_ENTRY_INSTALLED); + if (debug_p) + zlog_debug( + "%s(%u:%u):%s dplane notif, uninstalled type %s route", + VRF_LOGNAME(vrf), + dplane_ctx_get_vrf(ctx), + dplane_ctx_get_table(ctx), dest_str, + zebra_route_string( + dplane_ctx_get_type(ctx))); + } else { + /* At least report on the event. */ + if (debug_p) + zlog_debug( + "%s(%u:%u):%s dplane notif, but type %s not selected_fib", + VRF_LOGNAME(vrf), + dplane_ctx_get_vrf(ctx), + dplane_ctx_get_table(ctx), dest_str, + zebra_route_string( + dplane_ctx_get_type(ctx))); + } + goto done; + } + + /* We'll want to determine whether the installation status of the + * route has changed: we'll check the status before processing, + * and then again if there's been a change. + */ + start_count = 0; + + if (CHECK_FLAG(re->status, ROUTE_ENTRY_INSTALLED)) + start_count = rib_count_installed_nh(re); + + /* Update zebra's nexthop FIB flags based on the context struct's + * nexthops. + */ + fib_changed = rib_update_re_from_ctx(re, rn, ctx); + + if (!fib_changed) { + if (debug_p) + zlog_debug( + "%s(%u:%u):%s dplane notification: rib_update returns FALSE", + VRF_LOGNAME(vrf), dplane_ctx_get_vrf(ctx), + dplane_ctx_get_table(ctx), dest_str); + } + + /* + * Perform follow-up work if the actual status of the prefix + * changed. + */ + end_count = rib_count_installed_nh(re); + + /* Various fib transitions: changed nexthops; from installed to + * not-installed; or not-installed to installed. + */ + if (start_count > 0 && end_count > 0) { + if (debug_p) + zlog_debug( + "%s(%u:%u):%s applied nexthop changes from dplane notification", + VRF_LOGNAME(vrf), dplane_ctx_get_vrf(ctx), + dplane_ctx_get_table(ctx), dest_str); + + /* Changed nexthops - update kernel/others */ + dplane_route_notif_update(rn, re, + DPLANE_OP_ROUTE_UPDATE, ctx); + + } else if (start_count == 0 && end_count > 0) { + if (debug_p) + zlog_debug( + "%s(%u:%u):%s installed transition from dplane notification", + VRF_LOGNAME(vrf), dplane_ctx_get_vrf(ctx), + dplane_ctx_get_table(ctx), dest_str); + + /* We expect this to be the selected route, so we want + * to tell others about this transition. + */ + SET_FLAG(re->status, ROUTE_ENTRY_INSTALLED); + + /* Changed nexthops - update kernel/others */ + dplane_route_notif_update(rn, re, DPLANE_OP_ROUTE_UPDATE, ctx); + + /* Redistribute, lsp, and nht update */ + redistribute_update(dest_pfx, src_pfx, re, NULL); + + } else if (start_count > 0 && end_count == 0) { + if (debug_p) + zlog_debug( + "%s(%u:%u):%s un-installed transition from dplane notification", + VRF_LOGNAME(vrf), dplane_ctx_get_vrf(ctx), + dplane_ctx_get_table(ctx), dest_str); + + /* Transition from _something_ installed to _nothing_ + * installed. + */ + /* We expect this to be the selected route, so we want + * to tell others about this transistion. + */ + UNSET_FLAG(re->status, ROUTE_ENTRY_INSTALLED); + + /* Changed nexthops - update kernel/others */ + dplane_route_notif_update(rn, re, DPLANE_OP_ROUTE_DELETE, ctx); + + /* Redistribute, lsp, and nht update */ + redistribute_delete(dest_pfx, src_pfx, re, NULL); + } + + /* Make any changes visible for lsp and nexthop-tracking processing */ + zebra_rib_evaluate_rn_nexthops( + rn, zebra_router_get_next_sequence()); + + zebra_rib_evaluate_mpls(rn); + +done: + if (rn) + route_unlock_node(rn); + + /* Return context to dataplane module */ + dplane_ctx_fini(&ctx); +} + +static void process_subq_nhg(struct listnode *lnode) +{ + struct nhg_ctx *ctx = NULL; + uint8_t qindex = route_info[ZEBRA_ROUTE_NHG].meta_q_map; + + ctx = listgetdata(lnode); + + if (!ctx) + return; + + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug("NHG Context id=%u dequeued from sub-queue %u", + ctx->id, qindex); + + rib_nhg_process(ctx); +} + +static void process_subq_route(struct listnode *lnode, uint8_t qindex) +{ + struct route_node *rnode = NULL; + rib_dest_t *dest = NULL; + struct zebra_vrf *zvrf = NULL; rnode = listgetdata(lnode); dest = rib_dest_from_rnode(rnode); - if (dest) - zvrf = rib_dest_vrf(dest); + assert(dest); + + zvrf = rib_dest_vrf(dest); rib_process(rnode); if (IS_ZEBRA_DEBUG_RIB_DETAILED) { + struct route_entry *re = NULL; char buf[SRCDEST2STR_BUFFER]; + + /* + * rib_process may have freed the dest + * as part of the garbage collection. Let's + * prevent stupidity from happening. + */ + dest = rib_dest_from_rnode(rnode); + if (dest) + re = re_list_first(&dest->routes); + srcdest_rnode2str(rnode, buf, sizeof(buf)); - zlog_debug("%u:%s: rn %p dequeued from sub-queue %u", - zvrf ? zvrf_id(zvrf) : 0, buf, rnode, qindex); + zlog_debug("%s(%u:%u):%s: rn %p dequeued from sub-queue %u", + zvrf_name(zvrf), zvrf_id(zvrf), re ? re->table : 0, buf, + rnode, qindex); } if (rnode->info) @@ -2158,16 +2271,27 @@ static unsigned int process_subq(struct list *subq, uint8_t qindex) } #endif route_unlock_node(rnode); - list_delete_node(subq, lnode); - return 1; } - -/* - * Perform next-hop tracking processing after RIB updates. +/* Take a list of route_node structs and return 1, if there was a record + * picked from it and processed by rib_process(). Don't process more, + * than one RN record; operate only in the specified sub-queue. */ -static void do_nht_processing(void) +static unsigned int process_subq(struct list *subq, uint8_t qindex) { + struct listnode *lnode = listhead(subq); + + if (!lnode) + return 0; + + if (qindex == route_info[ZEBRA_ROUTE_NHG].meta_q_map) + process_subq_nhg(lnode); + else + process_subq_route(lnode, qindex); + + list_delete_node(subq, lnode); + + return 1; } /* Dispatch the meta queue by picking, processing and unlocking the next RN from @@ -2224,11 +2348,14 @@ static wq_item_status meta_queue_process(struct work_queue *dummy, void *data) * original metaqueue index value will win and we'll end up with * the route node enqueued once. */ -static void rib_meta_queue_add(struct meta_queue *mq, struct route_node *rn) +static int rib_meta_queue_add(struct meta_queue *mq, void *data) { + struct route_node *rn = NULL; struct route_entry *re = NULL, *curr_re = NULL; uint8_t qindex = MQ_SIZE, curr_qindex = MQ_SIZE; + rn = (struct route_node *)data; + RNODE_FOREACH_RE (rn, curr_re) { curr_qindex = route_info[curr_re->type].meta_q_map; @@ -2239,7 +2366,7 @@ static void rib_meta_queue_add(struct meta_queue *mq, struct route_node *rn) } if (!re) - return; + return -1; /* Invariant: at this point we always have rn->info set. */ if (CHECK_FLAG(rib_dest_from_rnode(rn)->flags, @@ -2248,7 +2375,7 @@ static void rib_meta_queue_add(struct meta_queue *mq, struct route_node *rn) rnode_debug(rn, re->vrf_id, "rn %p is already queued in sub-queue %u", (void *)rn, qindex); - return; + return -1; } SET_FLAG(rib_dest_from_rnode(rn)->flags, RIB_ROUTE_QUEUED(qindex)); @@ -2259,26 +2386,37 @@ static void rib_meta_queue_add(struct meta_queue *mq, struct route_node *rn) if (IS_ZEBRA_DEBUG_RIB_DETAILED) rnode_debug(rn, re->vrf_id, "queued rn %p into sub-queue %u", (void *)rn, qindex); + + return 0; } -/* Add route_node to work queue and schedule processing */ -void rib_queue_add(struct route_node *rn) +static int rib_meta_queue_nhg_add(struct meta_queue *mq, void *data) { - assert(rn); + struct nhg_ctx *ctx = NULL; + uint8_t qindex = route_info[ZEBRA_ROUTE_NHG].meta_q_map; - /* Pointless to queue a route_node with no RIB entries to add or remove - */ - if (!rnode_to_ribs(rn)) { - zlog_debug("%s: called for route_node (%p, %d) with no ribs", - __func__, (void *)rn, rn->lock); - zlog_backtrace(LOG_DEBUG); - return; - } + ctx = (struct nhg_ctx *)data; + + if (!ctx) + return -1; + + listnode_add(mq->subq[qindex], ctx); + mq->size++; + + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug("NHG Context id=%u queued into sub-queue %u", + ctx->id, qindex); + + return 0; +} +static int mq_add_handler(void *data, + int (*mq_add_func)(struct meta_queue *mq, void *data)) +{ if (zrouter.ribq == NULL) { flog_err(EC_ZEBRA_WQ_NONEXISTENT, "%s: work_queue does not exist!", __func__); - return; + return -1; } /* @@ -2292,9 +2430,31 @@ void rib_queue_add(struct route_node *rn) if (work_queue_empty(zrouter.ribq)) work_queue_add(zrouter.ribq, zrouter.mq); - rib_meta_queue_add(zrouter.mq, rn); + return mq_add_func(zrouter.mq, data); +} - return; +/* Add route_node to work queue and schedule processing */ +int rib_queue_add(struct route_node *rn) +{ + assert(rn); + + /* Pointless to queue a route_node with no RIB entries to add or remove + */ + if (!rnode_to_ribs(rn)) { + zlog_debug("%s: called for route_node (%p, %d) with no ribs", + __func__, (void *)rn, rn->lock); + zlog_backtrace(LOG_DEBUG); + return -1; + } + + return mq_add_handler(rn, &rib_meta_queue_add); +} + +int rib_queue_nhg_add(struct nhg_ctx *ctx) +{ + assert(ctx); + + return mq_add_handler(ctx, &rib_meta_queue_nhg_add); } /* Create new meta queue. @@ -2357,7 +2517,8 @@ rib_dest_t *zebra_rib_create_dest(struct route_node *rn) rib_dest_t *dest; dest = XCALLOC(MTYPE_RIB_DEST, sizeof(rib_dest_t)); - dest->nht = list_new(); + rnh_list_init(&dest->nht); + re_list_init(&dest->routes); route_lock_node(rn); /* rn route table reference */ rn->info = dest; dest->rnode = rn; @@ -2405,7 +2566,6 @@ rib_dest_t *zebra_rib_create_dest(struct route_node *rn) /* Add RE to head of the route node. */ static void rib_link(struct route_node *rn, struct route_entry *re, int process) { - struct route_entry *head; rib_dest_t *dest; afi_t afi; const char *rmap_name; @@ -2420,19 +2580,16 @@ static void rib_link(struct route_node *rn, struct route_entry *re, int process) dest = zebra_rib_create_dest(rn); } - head = dest->routes; - if (head) { - head->prev = re; - } - re->next = head; - dest->routes = re; + re_list_add_head(&dest->routes, re); afi = (rn->p.family == AF_INET) ? AFI_IP : (rn->p.family == AF_INET6) ? AFI_IP6 : AFI_MAX; - if (is_zebra_import_table_enabled(afi, re->table)) { + if (is_zebra_import_table_enabled(afi, re->vrf_id, re->table)) { + struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(re->vrf_id); + rmap_name = zebra_get_import_table_route_map(afi, re->table); - zebra_add_import_table_entry(rn, re, rmap_name); + zebra_add_import_table_entry(zvrf, rn, re, rmap_name); } else if (process) rib_queue_add(rn); } @@ -2475,19 +2632,19 @@ void rib_unlink(struct route_node *rn, struct route_entry *re) dest = rib_dest_from_rnode(rn); - if (re->next) - re->next->prev = re->prev; - - if (re->prev) - re->prev->next = re->next; - else { - dest->routes = re->next; - } + re_list_del(&dest->routes, re); if (dest->selected_fib == re) dest->selected_fib = NULL; - nexthops_free(re->ng.nexthop); + if (re->nhe && re->nhe_id) { + assert(re->nhe->id == re->nhe_id); + zebra_nhg_decrement_ref(re->nhe); + } else if (re->nhe && re->nhe->nhg.nexthop) + nexthops_free(re->nhe->nhg.nexthop); + + nexthops_free(re->fib_ng.nexthop); + XFREE(MTYPE_RE, re); } @@ -2500,31 +2657,104 @@ void rib_delnode(struct route_node *rn, struct route_entry *re) (void *)rn, (void *)re); SET_FLAG(re->status, ROUTE_ENTRY_REMOVED); - afi = (rn->p.family == AF_INET) - ? AFI_IP - : (rn->p.family == AF_INET6) ? AFI_IP6 : AFI_MAX; - if (is_zebra_import_table_enabled(afi, re->table)) { - zebra_del_import_table_entry(rn, re); - /* Just clean up if non main table */ - if (IS_ZEBRA_DEBUG_RIB) { - char buf[SRCDEST2STR_BUFFER]; - srcdest_rnode2str(rn, buf, sizeof(buf)); - zlog_debug("%u:%s: Freeing route rn %p, re %p (%s)", - re->vrf_id, buf, rn, re, - zebra_route_string(re->type)); + afi = (rn->p.family == AF_INET) + ? AFI_IP + : (rn->p.family == AF_INET6) ? AFI_IP6 : AFI_MAX; + if (is_zebra_import_table_enabled(afi, re->vrf_id, re->table)) { + struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(re->vrf_id); + + zebra_del_import_table_entry(zvrf, rn, re); + /* Just clean up if non main table */ + if (IS_ZEBRA_DEBUG_RIB) { + char buf[SRCDEST2STR_BUFFER]; + srcdest_rnode2str(rn, buf, sizeof(buf)); + zlog_debug("%s(%u):%s: Freeing route rn %p, re %p (%s)", + vrf_id_to_name(re->vrf_id), re->vrf_id, buf, + rn, re, zebra_route_string(re->type)); + } + + rib_unlink(rn, re); + } else { + rib_queue_add(rn); + } +} + +/* + * Helper that debugs a single nexthop within a route-entry + */ +static void _route_entry_dump_nh(const struct route_entry *re, + const char *straddr, + const struct nexthop *nexthop) +{ + char nhname[PREFIX_STRLEN]; + char backup_str[50]; + char wgt_str[50]; + char temp_str[10]; + int i; + struct interface *ifp; + struct vrf *vrf = vrf_lookup_by_id(nexthop->vrf_id); + + switch (nexthop->type) { + case NEXTHOP_TYPE_BLACKHOLE: + snprintf(nhname, sizeof(nhname), "Blackhole"); + break; + case NEXTHOP_TYPE_IFINDEX: + ifp = if_lookup_by_index(nexthop->ifindex, nexthop->vrf_id); + snprintf(nhname, sizeof(nhname), "%s", + ifp ? ifp->name : "Unknown"); + break; + case NEXTHOP_TYPE_IPV4: + /* fallthrough */ + case NEXTHOP_TYPE_IPV4_IFINDEX: + inet_ntop(AF_INET, &nexthop->gate, nhname, INET6_ADDRSTRLEN); + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + inet_ntop(AF_INET6, &nexthop->gate, nhname, INET6_ADDRSTRLEN); + break; + } + + backup_str[0] = '\0'; + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) { + snprintf(backup_str, sizeof(backup_str), "backup "); + for (i = 0; i < nexthop->backup_num; i++) { + snprintf(temp_str, sizeof(temp_str), "%d, ", + nexthop->backup_idx[i]); + strlcat(backup_str, temp_str, sizeof(backup_str)); } - - rib_unlink(rn, re); - } else { - rib_queue_add(rn); } + + wgt_str[0] = '\0'; + if (nexthop->weight) + snprintf(wgt_str, sizeof(wgt_str), "wgt %d,", nexthop->weight); + + zlog_debug("%s: %s %s[%u] vrf %s(%u) %s%s with flags %s%s%s%s%s", + straddr, (nexthop->rparent ? " NH" : "NH"), nhname, + nexthop->ifindex, vrf ? vrf->name : "Unknown", + nexthop->vrf_id, + wgt_str, backup_str, + (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE) + ? "ACTIVE " + : ""), + (CHECK_FLAG(re->status, ROUTE_ENTRY_INSTALLED) + ? "FIB " + : ""), + (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE) + ? "RECURSIVE " + : ""), + (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK) + ? "ONLINK " + : ""), + (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE) + ? "DUPLICATE " + : "")); + } /* This function dumps the contents of a given RE entry into * standard debug log. Calling function name and IP prefix in * question are passed as 1st and 2nd arguments. */ - void _route_entry_dump(const char *func, union prefixconstptr pp, union prefixconstptr src_pp, const struct route_entry *re) @@ -2534,90 +2764,60 @@ void _route_entry_dump(const char *func, union prefixconstptr pp, char straddr[PREFIX_STRLEN]; char srcaddr[PREFIX_STRLEN]; struct nexthop *nexthop; + struct vrf *vrf = vrf_lookup_by_id(re->vrf_id); + struct nexthop_group *nhg; - zlog_debug("%s: dumping RE entry %p for %s%s%s vrf %u", func, + zlog_debug("%s: dumping RE entry %p for %s%s%s vrf %s(%u)", func, (const void *)re, prefix2str(pp, straddr, sizeof(straddr)), is_srcdst ? " from " : "", is_srcdst ? prefix2str(src_pp, srcaddr, sizeof(srcaddr)) : "", - re->vrf_id); + VRF_LOGNAME(vrf), re->vrf_id); zlog_debug("%s: uptime == %lu, type == %u, instance == %d, table == %d", - func, (unsigned long)re->uptime, re->type, re->instance, + straddr, (unsigned long)re->uptime, re->type, re->instance, re->table); - zlog_debug( - "%s: metric == %u, mtu == %u, distance == %u, flags == %u, status == %u", - func, re->metric, re->mtu, re->distance, re->flags, re->status); - zlog_debug("%s: nexthop_num == %u, nexthop_active_num == %u", func, - re->nexthop_num, re->nexthop_active_num); - - for (ALL_NEXTHOPS(re->ng, nexthop)) { - struct interface *ifp; - struct vrf *vrf = vrf_lookup_by_id(nexthop->vrf_id); - - switch (nexthop->type) { - case NEXTHOP_TYPE_BLACKHOLE: - sprintf(straddr, "Blackhole"); - break; - case NEXTHOP_TYPE_IFINDEX: - ifp = if_lookup_by_index(nexthop->ifindex, - nexthop->vrf_id); - sprintf(straddr, "%s", ifp ? ifp->name : "Unknown"); - break; - case NEXTHOP_TYPE_IPV4: - /* fallthrough */ - case NEXTHOP_TYPE_IPV4_IFINDEX: - inet_ntop(AF_INET, &nexthop->gate, straddr, - INET6_ADDRSTRLEN); - break; - case NEXTHOP_TYPE_IPV6: - case NEXTHOP_TYPE_IPV6_IFINDEX: - inet_ntop(AF_INET6, &nexthop->gate, straddr, - INET6_ADDRSTRLEN); - break; - } - zlog_debug("%s: %s %s[%u] vrf %s(%u) with flags %s%s%s%s%s%s", - func, (nexthop->rparent ? " NH" : "NH"), straddr, - nexthop->ifindex, vrf ? vrf->name : "Unknown", - nexthop->vrf_id, - (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE) - ? "ACTIVE " - : ""), - (CHECK_FLAG(re->status, ROUTE_ENTRY_INSTALLED) - ? "FIB " - : ""), - (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE) - ? "RECURSIVE " - : ""), - (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK) - ? "ONLINK " - : ""), - (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_MATCHED) - ? "MATCHED " - : ""), - (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE) - ? "DUPLICATE " - : "")); - } - zlog_debug("%s: dump complete", func); -} - -/* This is an exported helper to rtm_read() to dump the strange + zlog_debug("%s: metric == %u, mtu == %u, distance == %u, flags == %u, status == %u", + straddr, re->metric, re->mtu, re->distance, re->flags, + re->status); + zlog_debug("%s: nexthop_num == %u, nexthop_active_num == %u", straddr, + nexthop_group_nexthop_num(&(re->nhe->nhg)), + nexthop_group_active_nexthop_num(&(re->nhe->nhg))); + + /* Dump nexthops */ + for (ALL_NEXTHOPS(re->nhe->nhg, nexthop)) + _route_entry_dump_nh(re, straddr, nexthop); + + if (zebra_nhg_get_backup_nhg(re->nhe)) { + zlog_debug("%s: backup nexthops:", straddr); + + nhg = zebra_nhg_get_backup_nhg(re->nhe); + for (ALL_NEXTHOPS_PTR(nhg, nexthop)) + _route_entry_dump_nh(re, straddr, nexthop); + } + + zlog_debug("%s: dump complete", straddr); +} + +/* + * This is an exported helper to rtm_read() to dump the strange * RE entry found by rib_lookup_ipv4_route() */ - void rib_lookup_and_dump(struct prefix_ipv4 *p, vrf_id_t vrf_id) { struct route_table *table; struct route_node *rn; struct route_entry *re; + struct vrf *vrf; char prefix_buf[INET_ADDRSTRLEN]; + vrf = vrf_lookup_by_id(vrf_id); + /* Lookup table. */ table = zebra_vrf_table(AFI_IP, SAFI_UNICAST, vrf_id); if (!table) { flog_err(EC_ZEBRA_TABLE_LOOKUP_FAILED, - "%s:%u zebra_vrf_table() returned NULL", __func__, - vrf_id); + "%s:%s(%u) zebra_vrf_table() returned NULL", __func__, + VRF_LOGNAME(vrf), vrf_id); return; } @@ -2626,7 +2826,8 @@ void rib_lookup_and_dump(struct prefix_ipv4 *p, vrf_id_t vrf_id) /* No route for this prefix. */ if (!rn) { - zlog_debug("%s:%u lookup failed for %s", __func__, vrf_id, + zlog_debug("%s:%s(%u) lookup failed for %s", __func__, + VRF_LOGNAME(vrf), vrf_id, prefix2str((struct prefix *)p, prefix_buf, sizeof(prefix_buf))); return; @@ -2637,9 +2838,8 @@ void rib_lookup_and_dump(struct prefix_ipv4 *p, vrf_id_t vrf_id) /* let's go */ RNODE_FOREACH_RE (rn, re) { - zlog_debug("%s:%u rn %p, re %p: %s, %s", - __func__, vrf_id, - (void *)rn, (void *)re, + zlog_debug("%s:%s(%u) rn %p, re %p: %s, %s", __func__, + VRF_LOGNAME(vrf), vrf_id, (void *)rn, (void *)re, (CHECK_FLAG(re->status, ROUTE_ENTRY_REMOVED) ? "removed" : "NOT removed"), @@ -2662,9 +2862,11 @@ void rib_lookup_and_pushup(struct prefix_ipv4 *p, vrf_id_t vrf_id) rib_dest_t *dest; if (NULL == (table = zebra_vrf_table(AFI_IP, SAFI_UNICAST, vrf_id))) { + struct vrf *vrf = vrf_lookup_by_id(vrf_id); + flog_err(EC_ZEBRA_TABLE_LOOKUP_FAILED, - "%s:%u zebra_vrf_table() returned NULL", __func__, - vrf_id); + "%s:%s(%u) zebra_vrf_table() returned NULL", __func__, + VRF_LOGNAME(vrf), vrf_id); return; } @@ -2687,10 +2889,13 @@ void rib_lookup_and_pushup(struct prefix_ipv4 *p, vrf_id_t vrf_id) if (dest->selected_fib) { if (IS_ZEBRA_DEBUG_RIB) { char buf[PREFIX_STRLEN]; + struct vrf *vrf = + vrf_lookup_by_id(dest->selected_fib->vrf_id); - zlog_debug("%u:%s: freeing way for connected prefix", - dest->selected_fib->vrf_id, - prefix2str(&rn->p, buf, sizeof(buf))); + zlog_debug( + "%s(%u):%s: freeing way for connected prefix", + VRF_LOGNAME(vrf), dest->selected_fib->vrf_id, + prefix2str(&rn->p, buf, sizeof(buf))); route_entry_dump(&rn->p, NULL, dest->selected_fib); } rib_uninstall(rn, dest->selected_fib); @@ -2698,72 +2903,96 @@ void rib_lookup_and_pushup(struct prefix_ipv4 *p, vrf_id_t vrf_id) } } -int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *p, - struct prefix_ipv6 *src_p, struct route_entry *re) +/* + * Internal route-add implementation; there are a couple of different public + * signatures. Callers in this path are responsible for the memory they + * allocate: if they allocate a nexthop_group or backup nexthop info, they + * must free those objects. If this returns < 0, an error has occurred and the + * route_entry 're' has not been captured; the caller should free that also. + */ +int rib_add_multipath_nhe(afi_t afi, safi_t safi, struct prefix *p, + struct prefix_ipv6 *src_p, struct route_entry *re, + struct nhg_hash_entry *re_nhe) { + struct nhg_hash_entry *nhe = NULL; struct route_table *table; struct route_node *rn; struct route_entry *same = NULL; int ret = 0; - if (!re) - return 0; + if (!re || !re_nhe) + return -1; assert(!src_p || !src_p->prefixlen || afi == AFI_IP6); /* Lookup table. */ - table = zebra_vrf_table_with_table_id(afi, safi, re->vrf_id, re->table); - if (!table) { - XFREE(MTYPE_RE, re); - return 0; + table = zebra_vrf_get_table_with_table_id(afi, safi, re->vrf_id, + re->table); + if (!table) + return -1; + + 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); + + return -1; + } + } else { + /* Lookup nhe from route information */ + nhe = zebra_nhg_rib_find_nhe(re_nhe, afi); + if (!nhe) { + char buf[PREFIX_STRLEN] = ""; + char buf2[PREFIX_STRLEN] = ""; + + flog_err( + EC_ZEBRA_TABLE_LOOKUP_FAILED, + "Zebra failed to find or create a nexthop hash entry for %s%s%s", + prefix2str(p, buf, sizeof(buf)), + src_p ? " from " : "", + src_p ? prefix2str(src_p, buf2, sizeof(buf2)) + : ""); + + return -1; + } } + /* + * Attach the re to the nhe's nexthop group. + * + * TODO: This will need to change when we start getting IDs from upper + * level protocols, as the refcnt might be wrong, since it checks + * if old_id != new_id. + */ + route_entry_update_nhe(re, nhe); + /* Make it sure prefixlen is applied to the prefix. */ apply_mask(p); if (src_p) apply_mask_ipv6(src_p); /* Set default distance by route type. */ - if (re->distance == 0) { + if (re->distance == 0) re->distance = route_distance(re->type); - /* iBGP distance is 200. */ - if (re->type == ZEBRA_ROUTE_BGP - && CHECK_FLAG(re->flags, ZEBRA_FLAG_IBGP)) - re->distance = 200; - } - /* Lookup route node.*/ rn = srcdest_rnode_get(table, p, src_p); /* * If same type of route are installed, treat it as a implicit - * withdraw. - * If the user has specified the No route replace semantics + * withdraw. If the user has specified the No route replace semantics * for the install don't do a route replace. */ RNODE_FOREACH_RE (rn, same) { if (CHECK_FLAG(same->status, ROUTE_ENTRY_REMOVED)) continue; - if (same->type != re->type) - continue; - if (same->instance != re->instance) - continue; - if (same->type == ZEBRA_ROUTE_KERNEL - && same->metric != re->metric) - continue; - - if (CHECK_FLAG(re->flags, ZEBRA_FLAG_RR_USE_DISTANCE) && - same->distance != re->distance) - continue; - - /* - * We should allow duplicate connected routes - * because of IPv6 link-local routes and unnumbered - * interfaces on Linux. - */ - if (same->type != ZEBRA_ROUTE_CONNECT) + /* Compare various route_entry properties */ + if (rib_compare_routes(re, same)) break; } @@ -2788,20 +3017,56 @@ int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *p, ret = 1; /* Free implicit route.*/ - if (same) { + if (same) rib_delnode(rn, same); - ret = -1; - } route_unlock_node(rn); return ret; } +/* + * Add a single route. + */ +int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *p, + struct prefix_ipv6 *src_p, struct route_entry *re, + struct nexthop_group *ng) +{ + int ret; + struct nhg_hash_entry nhe; + + if (!re) + return -1; + + /* We either need nexthop(s) or an existing nexthop id */ + if (ng == NULL && re->nhe_id == 0) + return -1; + + /* + * Use a temporary nhe to convey info to the common/main api. + */ + zebra_nhe_init(&nhe, afi, (ng ? ng->nexthop : NULL)); + if (ng) + nhe.nhg.nexthop = ng->nexthop; + else if (re->nhe_id > 0) + nhe.id = re->nhe_id; + + ret = rib_add_multipath_nhe(afi, safi, p, src_p, re, &nhe); + + /* In this path, the callers expect memory to be freed. */ + nexthop_group_delete(&ng); + + /* In error cases, free the route also */ + if (ret < 0) + XFREE(MTYPE_RE, re); + + return ret; +} + void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, unsigned short instance, int flags, struct prefix *p, struct prefix_ipv6 *src_p, const struct nexthop *nh, - uint32_t table_id, uint32_t metric, uint8_t distance, - bool fromkernel) + uint32_t nhe_id, uint32_t table_id, uint32_t metric, + uint8_t distance, bool fromkernel, bool connected_down) { struct route_table *table; struct route_node *rn; @@ -2815,7 +3080,8 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, assert(!src_p || !src_p->prefixlen || afi == AFI_IP6); /* Lookup table. */ - table = zebra_vrf_table_with_table_id(afi, safi, vrf_id, table_id); + table = zebra_vrf_lookup_table_with_table_id(afi, safi, vrf_id, + table_id); if (!table) return; @@ -2835,11 +3101,14 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, else src_buf[0] = '\0'; - if (IS_ZEBRA_DEBUG_RIB) - zlog_debug("%u:%s%s%s doesn't exist in rib", vrf_id, - dst_buf, + if (IS_ZEBRA_DEBUG_RIB) { + struct vrf *vrf = vrf_lookup_by_id(vrf_id); + + zlog_debug("%s[%d]:%s%s%s doesn't exist in rib", + vrf->name, table_id, dst_buf, (src_buf[0] != '\0') ? " from " : "", src_buf); + } return; } @@ -2861,27 +3130,38 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, if (re->type == ZEBRA_ROUTE_KERNEL && re->metric != metric) continue; - if (re->type == ZEBRA_ROUTE_CONNECT && (rtnh = re->ng.nexthop) + if (re->type == ZEBRA_ROUTE_CONNECT && + (rtnh = re->nhe->nhg.nexthop) && rtnh->type == NEXTHOP_TYPE_IFINDEX && nh) { if (rtnh->ifindex != nh->ifindex) continue; same = re; break; } + /* Make sure that the route found has the same gateway. */ - else { - if (nh == NULL) { + if (nhe_id && re->nhe_id == nhe_id) { + same = re; + break; + } + + if (nh == NULL) { + same = re; + break; + } + for (ALL_NEXTHOPS(re->nhe->nhg, rtnh)) { + /* + * No guarantee all kernel send nh with labels + * on delete. + */ + if (nexthop_same_no_labels(rtnh, nh)) { same = re; break; } - for (ALL_NEXTHOPS(re->ng, rtnh)) - if (nexthop_same_no_recurse(rtnh, nh)) { - same = re; - break; - } - if (same) - break; } + + if (same) + break; } /* If same type of route can't be found and this message is from kernel. */ @@ -2908,10 +3188,11 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, rn, fib, zebra_route_string(fib->type)); } - if (allow_delete) { + if (allow_delete + || CHECK_FLAG(dest->flags, RIB_ROUTE_ANY_QUEUED)) { UNSET_FLAG(fib->status, ROUTE_ENTRY_INSTALLED); /* Unset flags. */ - for (rtnh = fib->ng.nexthop; rtnh; + for (rtnh = fib->nhe->nhg.nexthop; rtnh; rtnh = rtnh->next) UNSET_FLAG(rtnh->flags, NEXTHOP_FLAG_FIB); @@ -2934,8 +3215,7 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, if (nh) rnode_debug( rn, vrf_id, - "via %s ifindex %d type %d " - "doesn't exist in rib", + "via %s ifindex %d type %d doesn't exist in rib", inet_ntop(afi2family(afi), &nh->gate, buf2, sizeof(buf2)), @@ -2967,7 +3247,7 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, if (CHECK_FLAG(flags, ZEBRA_FLAG_EVPN_ROUTE)) { struct nexthop *tmp_nh; - for (ALL_NEXTHOPS(re->ng, tmp_nh)) { + for (ALL_NEXTHOPS(re->nhe->nhg, tmp_nh)) { struct ipaddr vtep_ip; memset(&vtep_ip, 0, sizeof(struct ipaddr)); @@ -2994,6 +3274,19 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, rib_delnode(rn, same); } + /* + * This is to force an immediate re-eval of this particular + * node via nexthop tracking. Why? Because there are scenarios + * where the interface is flapping and the normal queuing methodology + * will cause down/up events to very very rarely be combined into + * a non-event from nexthop tracking perspective. Leading + * to some fun timing situations with upper level routing protocol + * trying to and failing to install routes during this blip. Especially + * when zebra is under load. + */ + if (connected_down) + zebra_rib_evaluate_rn_nexthops(rn, + zebra_router_get_next_sequence()); route_unlock_node(rn); return; } @@ -3002,11 +3295,12 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, int rib_add(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, unsigned short instance, int flags, struct prefix *p, struct prefix_ipv6 *src_p, const struct nexthop *nh, - uint32_t table_id, uint32_t metric, uint32_t mtu, uint8_t distance, - route_tag_t tag) + uint32_t nhe_id, uint32_t table_id, uint32_t metric, uint32_t mtu, + uint8_t distance, route_tag_t tag) { - struct route_entry *re; - struct nexthop *nexthop; + struct route_entry *re = NULL; + struct nexthop *nexthop = NULL; + struct nexthop_group *ng = NULL; /* Allocate new route_entry structure. */ re = XCALLOC(MTYPE_RE, sizeof(struct route_entry)); @@ -3018,23 +3312,85 @@ int rib_add(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, re->mtu = mtu; re->table = table_id; re->vrf_id = vrf_id; - re->nexthop_num = 0; - re->uptime = time(NULL); + re->uptime = monotime(NULL); re->tag = tag; + re->nhe_id = nhe_id; + + /* If the owner of the route supplies a shared nexthop-group id, + * we'll use that. Otherwise, pass the nexthop along directly. + */ + if (!nhe_id) { + ng = nexthop_group_new(); + + /* Add nexthop. */ + nexthop = nexthop_new(); + *nexthop = *nh; + nexthop_group_add_sorted(ng, nexthop); + } + + return rib_add_multipath(afi, safi, p, src_p, re, ng); +} + +static const char *rib_update_event2str(enum rib_update_event event) +{ + const char *ret = "UNKNOWN"; + + switch (event) { + case RIB_UPDATE_KERNEL: + ret = "RIB_UPDATE_KERNEL"; + break; + case RIB_UPDATE_RMAP_CHANGE: + ret = "RIB_UPDATE_RMAP_CHANGE"; + break; + case RIB_UPDATE_OTHER: + ret = "RIB_UPDATE_OTHER"; + break; + case RIB_UPDATE_MAX: + break; + } + + return ret; +} + + +/* Schedule route nodes to be processed if they match the type */ +static void rib_update_route_node(struct route_node *rn, int type) +{ + struct route_entry *re, *next; + bool re_changed = false; - /* Add nexthop. */ - nexthop = nexthop_new(); - *nexthop = *nh; - route_entry_nexthop_add(re, nexthop); + RNODE_FOREACH_RE_SAFE (rn, re, next) { + if (type == ZEBRA_ROUTE_ALL || type == re->type) { + SET_FLAG(re->status, ROUTE_ENTRY_CHANGED); + re_changed = true; + } + } - return rib_add_multipath(afi, safi, p, src_p, re); + if (re_changed) + rib_queue_add(rn); } /* Schedule routes of a particular table (address-family) based on event. */ -void rib_update_table(struct route_table *table, rib_update_event_t event) +void rib_update_table(struct route_table *table, enum rib_update_event event) { struct route_node *rn; - struct route_entry *re, *next; + + if (IS_ZEBRA_DEBUG_EVENT) { + struct zebra_vrf *zvrf; + struct vrf *vrf; + + zvrf = table->info + ? ((struct rib_table_info *)table->info)->zvrf + : NULL; + vrf = zvrf ? zvrf->vrf : NULL; + + zlog_debug("%s: %s VRF %s Table %u event %s", __func__, + table->info ? afi2str( + ((struct rib_table_info *)table->info)->afi) + : "Unknown", + VRF_LOGNAME(vrf), zvrf ? zvrf->table_id : 0, + rib_update_event2str(event)); + } /* Walk all routes and queue for processing, if appropriate for * the trigger event. @@ -3045,93 +3401,137 @@ void rib_update_table(struct route_table *table, rib_update_event_t event) * has already been queued we don't * need to queue it up again */ - if (rn->info && CHECK_FLAG(rib_dest_from_rnode(rn)->flags, - RIB_ROUTE_ANY_QUEUED)) + if (rn->info + && CHECK_FLAG(rib_dest_from_rnode(rn)->flags, + RIB_ROUTE_ANY_QUEUED)) continue; - switch (event) { - case RIB_UPDATE_IF_CHANGE: - /* Examine all routes that won't get processed by the - * protocol or - * triggered by nexthop evaluation (NHT). This would be - * system, - * kernel and certain static routes. Note that NHT will - * get - * triggered upon an interface event as connected routes - * always - * get queued for processing. - */ - RNODE_FOREACH_RE_SAFE (rn, re, next) { - struct nexthop *nh; - - if (re->type != ZEBRA_ROUTE_SYSTEM - && re->type != ZEBRA_ROUTE_KERNEL - && re->type != ZEBRA_ROUTE_CONNECT - && re->type != ZEBRA_ROUTE_STATIC) - continue; - if (re->type != ZEBRA_ROUTE_STATIC) { - SET_FLAG(re->status, - ROUTE_ENTRY_CHANGED); - rib_queue_add(rn); - continue; - } - - for (nh = re->ng.nexthop; nh; nh = nh->next) - if (!(nh->type == NEXTHOP_TYPE_IPV4 - || nh->type == NEXTHOP_TYPE_IPV6)) - break; - - /* If we only have nexthops to a - * gateway, NHT will - * take care. - */ - if (nh) { - SET_FLAG(re->status, - ROUTE_ENTRY_CHANGED); - rib_queue_add(rn); - } - } + switch (event) { + case RIB_UPDATE_KERNEL: + rib_update_route_node(rn, ZEBRA_ROUTE_KERNEL); break; - case RIB_UPDATE_RMAP_CHANGE: case RIB_UPDATE_OTHER: - /* Right now, examine all routes. Can restrict to a - * protocol in - * some cases (TODO). - */ - if (rnode_to_ribs(rn)) { - RNODE_FOREACH_RE_SAFE (rn, re, next) - SET_FLAG(re->status, - ROUTE_ENTRY_CHANGED); - rib_queue_add(rn); - } + rib_update_route_node(rn, ZEBRA_ROUTE_ALL); break; - default: break; } } } -/* RIB update function. */ -void rib_update(vrf_id_t vrf_id, rib_update_event_t event) +static void rib_update_handle_vrf(vrf_id_t vrf_id, enum rib_update_event event) { struct route_table *table; + if (IS_ZEBRA_DEBUG_EVENT) + zlog_debug("%s: Handling VRF %s event %s", __func__, + vrf_id_to_name(vrf_id), rib_update_event2str(event)); + /* Process routes of interested address-families. */ table = zebra_vrf_table(AFI_IP, SAFI_UNICAST, vrf_id); - if (table) { - if (IS_ZEBRA_DEBUG_EVENT) - zlog_debug("%s : AFI_IP event %d", __func__, event); + if (table) rib_update_table(table, event); - } table = zebra_vrf_table(AFI_IP6, SAFI_UNICAST, vrf_id); - if (table) { - if (IS_ZEBRA_DEBUG_EVENT) - zlog_debug("%s : AFI_IP6 event %d", __func__, event); + if (table) rib_update_table(table, event); - } +} + +static void rib_update_handle_vrf_all(enum rib_update_event event) +{ + struct zebra_router_table *zrt; + + if (IS_ZEBRA_DEBUG_EVENT) + zlog_debug("%s: Handling VRF (ALL) event %s", __func__, + rib_update_event2str(event)); + + /* Just iterate over all the route tables, rather than vrf lookups */ + RB_FOREACH (zrt, zebra_router_table_head, &zrouter.tables) + rib_update_table(zrt->table, event); +} + +struct rib_update_ctx { + enum rib_update_event event; + bool vrf_all; + vrf_id_t vrf_id; +}; + +static struct rib_update_ctx *rib_update_ctx_init(vrf_id_t vrf_id, + enum rib_update_event event) +{ + struct rib_update_ctx *ctx; + + ctx = XCALLOC(MTYPE_RIB_UPDATE_CTX, sizeof(struct rib_update_ctx)); + + ctx->event = event; + ctx->vrf_id = vrf_id; + + return ctx; +} + +static void rib_update_ctx_fini(struct rib_update_ctx **ctx) +{ + XFREE(MTYPE_RIB_UPDATE_CTX, *ctx); +} + +static int rib_update_handler(struct thread *thread) +{ + struct rib_update_ctx *ctx; + + ctx = THREAD_ARG(thread); + + if (ctx->vrf_all) + rib_update_handle_vrf_all(ctx->event); + else + rib_update_handle_vrf(ctx->vrf_id, ctx->event); + + rib_update_ctx_fini(&ctx); + + return 0; +} + +/* + * Thread list to ensure we don't schedule a ton of events + * if interfaces are flapping for instance. + */ +static struct thread *t_rib_update_threads[RIB_UPDATE_MAX]; + +/* Schedule a RIB update event for specific vrf */ +void rib_update_vrf(vrf_id_t vrf_id, enum rib_update_event event) +{ + struct rib_update_ctx *ctx; + + ctx = rib_update_ctx_init(vrf_id, event); + + /* Don't worry about making sure multiple rib updates for specific vrf + * are scheduled at once for now. If it becomes a problem, we can use a + * lookup of some sort to keep track of running threads via t_vrf_id + * like how we are doing it in t_rib_update_threads[]. + */ + thread_add_event(zrouter.master, rib_update_handler, ctx, 0, NULL); + + if (IS_ZEBRA_DEBUG_EVENT) + zlog_debug("%s: Scheduled VRF %s, event %s", __func__, + vrf_id_to_name(ctx->vrf_id), + rib_update_event2str(event)); +} + +/* Schedule a RIB update event for all vrfs */ +void rib_update(enum rib_update_event event) +{ + struct rib_update_ctx *ctx; + + ctx = rib_update_ctx_init(0, event); + + ctx->vrf_all = true; + + if (!thread_add_event(zrouter.master, rib_update_handler, ctx, 0, + &t_rib_update_threads[event])) + rib_update_ctx_fini(&ctx); /* Already scheduled */ + else if (IS_ZEBRA_DEBUG_EVENT) + zlog_debug("%s: Scheduled VRF (ALL), event %s", __func__, + rib_update_event2str(event)); } /* Delete self installed routes after zebra is relaunched. */ @@ -3145,8 +3545,12 @@ void rib_sweep_table(struct route_table *table) if (!table) return; + if (IS_ZEBRA_DEBUG_RIB) + zlog_debug("%s: starting", __func__); + for (rn = route_top(table); rn; rn = srcdest_route_next(rn)) { RNODE_FOREACH_RE_SAFE (rn, re, next) { + if (IS_ZEBRA_DEBUG_RIB) route_entry_dump(&rn->p, NULL, re); @@ -3156,6 +3560,14 @@ void rib_sweep_table(struct route_table *table) if (!CHECK_FLAG(re->flags, ZEBRA_FLAG_SELFROUTE)) continue; + /* + * If routes are older than startup_time then + * we know we read them in from the kernel. + * As such we can safely remove them. + */ + if (zrouter.startup_time < re->uptime) + continue; + /* * So we are starting up and have received * routes from the kernel that we have installed @@ -3175,17 +3587,20 @@ void rib_sweep_table(struct route_table *table) * this decision needs to be revisited */ SET_FLAG(re->status, ROUTE_ENTRY_INSTALLED); - for (ALL_NEXTHOPS(re->ng, nexthop)) + for (ALL_NEXTHOPS(re->nhe->nhg, nexthop)) SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); rib_uninstall_kernel(rn, re); rib_delnode(rn, re); } } + + if (IS_ZEBRA_DEBUG_RIB) + zlog_debug("%s: ends", __func__); } /* Sweep all RIB tables. */ -void rib_sweep_route(void) +int rib_sweep_route(struct thread *t) { struct vrf *vrf; struct zebra_vrf *zvrf; @@ -3199,6 +3614,9 @@ void rib_sweep_route(void) } zebra_router_sweep_route(); + zebra_router_sweep_nhgs(); + + return 0; } /* Remove specific by protocol routes from 'table'. */ @@ -3229,18 +3647,23 @@ unsigned long rib_score_proto(uint8_t proto, unsigned short instance) { struct vrf *vrf; struct zebra_vrf *zvrf; + struct other_route_table *ort; unsigned long cnt = 0; - RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) - if ((zvrf = vrf->info) != NULL) - cnt += rib_score_proto_table( - proto, instance, - zvrf->table[AFI_IP][SAFI_UNICAST]) - + rib_score_proto_table( - proto, instance, - zvrf->table[AFI_IP6][SAFI_UNICAST]); + RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) { + zvrf = vrf->info; + if (!zvrf) + continue; + + cnt += rib_score_proto_table(proto, instance, + zvrf->table[AFI_IP][SAFI_UNICAST]) + + rib_score_proto_table( + proto, instance, + zvrf->table[AFI_IP6][SAFI_UNICAST]); - cnt += zebra_router_score_proto(proto, instance); + frr_each(otable, &zvrf->other_tables, ort) cnt += + rib_score_proto_table(proto, instance, ort->table); + } return cnt; } @@ -3249,7 +3672,7 @@ unsigned long rib_score_proto(uint8_t proto, unsigned short instance) void rib_close_table(struct route_table *table) { struct route_node *rn; - rib_table_info_t *info; + struct rib_table_info *info; rib_dest_t *dest; if (!table) @@ -3286,9 +3709,10 @@ static int handle_pw_result(struct zebra_dplane_ctx *ctx) if (dplane_ctx_get_status(ctx) != ZEBRA_DPLANE_REQUEST_SUCCESS) { vrf = zebra_vrf_lookup_by_id(dplane_ctx_get_vrf(ctx)); - pw = zebra_pw_find(vrf, dplane_ctx_get_pw_ifname(ctx)); + pw = zebra_pw_find(vrf, dplane_ctx_get_ifname(ctx)); if (pw) - zebra_pw_install_failure(pw); + zebra_pw_install_failure(pw, + dplane_ctx_get_pw_status(ctx)); } done: @@ -3305,6 +3729,7 @@ static int rib_process_dplane_results(struct thread *thread) { struct zebra_dplane_ctx *ctx; struct dplane_ctx_q ctxlist; + bool shut_p = false; /* Dequeue a list of completed updates with one lock/unlock cycle */ @@ -3312,12 +3737,10 @@ static int rib_process_dplane_results(struct thread *thread) TAILQ_INIT(&ctxlist); /* Take lock controlling queue of results */ - pthread_mutex_lock(&dplane_mutex); - { + frr_with_mutex(&dplane_mutex) { /* Dequeue list of context structs */ dplane_ctx_list_append(&ctxlist, &rib_dplane_q); } - pthread_mutex_unlock(&dplane_mutex); /* Dequeue context block */ ctx = dplane_ctx_dequeue(&ctxlist); @@ -3326,18 +3749,66 @@ static int rib_process_dplane_results(struct thread *thread) if (ctx == NULL) break; + /* If zebra is shutting down, avoid processing results, + * just drain the results queue. + */ + shut_p = atomic_load_explicit(&zrouter.in_shutdown, + memory_order_relaxed); + if (shut_p) { + while (ctx) { + dplane_ctx_fini(&ctx); + + ctx = dplane_ctx_dequeue(&ctxlist); + } + + continue; + } + while (ctx) { switch (dplane_ctx_get_op(ctx)) { case DPLANE_OP_ROUTE_INSTALL: case DPLANE_OP_ROUTE_UPDATE: case DPLANE_OP_ROUTE_DELETE: - rib_process_result(ctx); + { + /* Bit of special case for route updates + * that were generated by async notifications: + * we don't want to continue processing these + * in the rib. + */ + if (dplane_ctx_get_notif_provider(ctx) == 0) + rib_process_result(ctx); + else + dplane_ctx_fini(&ctx); + } + break; + + case DPLANE_OP_ROUTE_NOTIFY: + rib_process_dplane_notify(ctx); + break; + + case DPLANE_OP_NH_INSTALL: + case DPLANE_OP_NH_UPDATE: + case DPLANE_OP_NH_DELETE: + zebra_nhg_dplane_result(ctx); break; case DPLANE_OP_LSP_INSTALL: case DPLANE_OP_LSP_UPDATE: case DPLANE_OP_LSP_DELETE: - zebra_mpls_lsp_dplane_result(ctx); + { + /* Bit of special case for LSP updates + * that were generated by async notifications: + * we don't want to continue processing these. + */ + if (dplane_ctx_get_notif_provider(ctx) == 0) + zebra_mpls_lsp_dplane_result(ctx); + else + dplane_ctx_fini(&ctx); + } + break; + + case DPLANE_OP_LSP_NOTIFY: + zebra_mpls_process_dplane_notify(ctx); break; case DPLANE_OP_PW_INSTALL: @@ -3351,10 +3822,31 @@ static int rib_process_dplane_results(struct thread *thread) dplane_ctx_fini(&ctx); break; - default: + case DPLANE_OP_MAC_INSTALL: + case DPLANE_OP_MAC_DELETE: + zebra_vxlan_handle_result(ctx); + break; + + case DPLANE_OP_RULE_ADD: + case DPLANE_OP_RULE_DELETE: + case DPLANE_OP_RULE_UPDATE: + zebra_pbr_dplane_result(ctx); + break; + + /* Some op codes not handled here */ + case DPLANE_OP_ADDR_INSTALL: + case DPLANE_OP_ADDR_UNINSTALL: + case DPLANE_OP_NEIGH_INSTALL: + case DPLANE_OP_NEIGH_UPDATE: + case DPLANE_OP_NEIGH_DELETE: + case DPLANE_OP_VTEP_ADD: + case DPLANE_OP_VTEP_DELETE: + case DPLANE_OP_NEIGH_DISCOVER: + case DPLANE_OP_NONE: /* Don't expect this: just return the struct? */ dplane_ctx_fini(&ctx); break; + } /* Dispatch by op code */ ctx = dplane_ctx_dequeue(&ctxlist); @@ -3362,9 +3854,6 @@ static int rib_process_dplane_results(struct thread *thread) } while (1); - /* Check for nexthop tracking processing after finishing with results */ - do_nht_processing(); - return 0; } @@ -3376,12 +3865,10 @@ static int rib_process_dplane_results(struct thread *thread) static int rib_dplane_results(struct dplane_ctx_q *ctxlist) { /* Take lock controlling queue of results */ - pthread_mutex_lock(&dplane_mutex); - { + frr_with_mutex(&dplane_mutex) { /* Enqueue context blocks */ dplane_ctx_list_append(&rib_dplane_q, ctxlist); } - pthread_mutex_unlock(&dplane_mutex); /* Ensure event is signalled to zebra main pthread */ thread_add_event(zrouter.master, rib_process_dplane_results, NULL, 0, @@ -3390,9 +3877,33 @@ static int rib_dplane_results(struct dplane_ctx_q *ctxlist) return 0; } +/* + * Ensure there are no empty slots in the route_info array. + * Every route type in zebra should be present there. + */ +static void check_route_info(void) +{ + int len = array_size(route_info); + + /* + * ZEBRA_ROUTE_SYSTEM is special cased since + * its key is 0 anyway. + * + * ZEBRA_ROUTE_ALL is also ignored. + */ + for (int i = 0; i < len; i++) { + if (i == ZEBRA_ROUTE_SYSTEM || i == ZEBRA_ROUTE_ALL) + continue; + assert(route_info[i].key); + assert(route_info[i].meta_q_map < MQ_SIZE); + } +} + /* Routing information base initialize. */ void rib_init(void) { + check_route_info(); + rib_queue_init(); /* Init dataplane, and register for results */ @@ -3406,7 +3917,7 @@ void rib_init(void) * * Get the first vrf id that is greater than the given vrf id if any. * - * Returns TRUE if a vrf id was found, FALSE otherwise. + * Returns true if a vrf id was found, false otherwise. */ static inline int vrf_id_get_next(vrf_id_t vrf_id, vrf_id_t *next_id_p) { @@ -3437,7 +3948,7 @@ struct route_table *rib_tables_iter_next(rib_tables_iter_t *iter) * Array that helps us go over all AFI/SAFI combinations via one * index. */ - static struct { + static const struct { afi_t afi; safi_t safi; } afi_safis[] = { diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c index 220a8006d0..db4199a85d 100644 --- a/zebra/zebra_rnh.c +++ b/zebra/zebra_rnh.c @@ -46,30 +46,28 @@ #include "zebra/debug.h" #include "zebra/zebra_rnh.h" #include "zebra/zebra_routemap.h" +#include "zebra/zebra_srte.h" #include "zebra/interface.h" #include "zebra/zebra_memory.h" #include "zebra/zebra_errors.h" +DEFINE_MTYPE_STATIC(ZEBRA, RNH, "Nexthop tracking object") + static void free_state(vrf_id_t vrf_id, struct route_entry *re, struct route_node *rn); -static void copy_state(struct rnh *rnh, struct route_entry *re, +static void copy_state(struct rnh *rnh, const struct route_entry *re, struct route_node *rn); static int compare_state(struct route_entry *r1, struct route_entry *r2); -static int send_client(struct rnh *rnh, struct zserv *client, rnh_type_t type, - vrf_id_t vrf_id); static void print_rnh(struct route_node *rn, struct vty *vty); static int zebra_client_cleanup_rnh(struct zserv *client); -int zebra_rnh_ip_default_route = 0; -int zebra_rnh_ipv6_default_route = 0; - void zebra_rnh_init(void) { hook_register(zserv_client_close, zebra_client_cleanup_rnh); } static inline struct route_table *get_rnh_table(vrf_id_t vrfid, afi_t afi, - rnh_type_t type) + enum rnh_type type) { struct zebra_vrf *zvrf; struct route_table *t = NULL; @@ -112,14 +110,14 @@ static void zebra_rnh_remove_from_routing_table(struct rnh *rnh) char buf[PREFIX_STRLEN]; char buf1[PREFIX_STRLEN]; - zlog_debug("%s: %u:%s removed from tracking on %s", - __PRETTY_FUNCTION__, rnh->vrf_id, + zlog_debug("%s: %u:%s removed from tracking on %s", __func__, + rnh->vrf_id, prefix2str(&rnh->node->p, buf, sizeof(buf)), srcdest_rnode2str(rn, buf1, sizeof(buf))); } dest = rib_dest_from_rnode(rn); - listnode_delete(dest->nht, rnh); + rnh_list_del(&dest->nht, rnh); route_unlock_node(rn); } @@ -138,18 +136,18 @@ static void zebra_rnh_store_in_routing_table(struct rnh *rnh) char buf[PREFIX_STRLEN]; char buf1[PREFIX_STRLEN]; - zlog_debug("%s: %u:%s added for tracking on %s", - __PRETTY_FUNCTION__, rnh->vrf_id, + zlog_debug("%s: %u:%s added for tracking on %s", __func__, + rnh->vrf_id, prefix2str(&rnh->node->p, buf, sizeof(buf)), srcdest_rnode2str(rn, buf1, sizeof(buf))); } dest = rib_dest_from_rnode(rn); - listnode_add(dest->nht, rnh); + rnh_list_add_tail(&dest->nht, rnh); route_unlock_node(rn); } -struct rnh *zebra_add_rnh(struct prefix *p, vrf_id_t vrfid, rnh_type_t type, +struct rnh *zebra_add_rnh(struct prefix *p, vrf_id_t vrfid, enum rnh_type type, bool *exists) { struct route_table *table; @@ -160,15 +158,16 @@ struct rnh *zebra_add_rnh(struct prefix *p, vrf_id_t vrfid, rnh_type_t type, if (IS_ZEBRA_DEBUG_NHT) { prefix2str(p, buf, sizeof(buf)); - zlog_debug("%u: Add RNH %s type %d", vrfid, buf, type); + zlog_debug("%u: Add RNH %s type %s", vrfid, buf, + rnh_type2str(type)); } table = get_rnh_table(vrfid, afi, type); if (!table) { prefix2str(p, buf, sizeof(buf)); flog_warn(EC_ZEBRA_RNH_NO_TABLE, - "%u: Add RNH %s type %d - table not found", vrfid, - buf, type); - exists = false; + "%u: Add RNH %s type %s - table not found", vrfid, + buf, rnh_type2str(type)); + *exists = false; return NULL; } @@ -207,7 +206,8 @@ struct rnh *zebra_add_rnh(struct prefix *p, vrf_id_t vrfid, rnh_type_t type, return (rn->info); } -struct rnh *zebra_lookup_rnh(struct prefix *p, vrf_id_t vrfid, rnh_type_t type) +struct rnh *zebra_lookup_rnh(struct prefix *p, vrf_id_t vrfid, + enum rnh_type type) { struct route_table *table; struct route_node *rn; @@ -251,14 +251,14 @@ void zebra_free_rnh(struct rnh *rnh) route_unlock_node(rern); dest = rib_dest_from_rnode(rern); - listnode_delete(dest->nht, rnh); + rnh_list_del(&dest->nht, rnh); } } free_state(rnh->vrf_id, rnh->state, rnh->node); XFREE(MTYPE_RNH, rnh); } -static void zebra_delete_rnh(struct rnh *rnh, rnh_type_t type) +static void zebra_delete_rnh(struct rnh *rnh, enum rnh_type type) { struct route_node *rn; @@ -271,8 +271,8 @@ static void zebra_delete_rnh(struct rnh *rnh, rnh_type_t type) if (IS_ZEBRA_DEBUG_NHT) { char buf[PREFIX2STR_BUFFER]; - zlog_debug("%u: Del RNH %s type %d", rnh->vrf_id, - rnh_str(rnh, buf, sizeof(buf)), type); + zlog_debug("%u: Del RNH %s type %s", rnh->vrf_id, + rnh_str(rnh, buf, sizeof(buf)), rnh_type2str(type)); } zebra_free_rnh(rnh); @@ -289,13 +289,13 @@ static void zebra_delete_rnh(struct rnh *rnh, rnh_type_t type) * and as such it will have a resolved rnh. */ void zebra_add_rnh_client(struct rnh *rnh, struct zserv *client, - rnh_type_t type, vrf_id_t vrf_id) + enum rnh_type type, vrf_id_t vrf_id) { if (IS_ZEBRA_DEBUG_NHT) { char buf[PREFIX2STR_BUFFER]; - zlog_debug("%u: Client %s registers for RNH %s type %d", vrf_id, + zlog_debug("%u: Client %s registers for RNH %s type %s", vrf_id, zebra_route_string(client->proto), - rnh_str(rnh, buf, sizeof(buf)), type); + rnh_str(rnh, buf, sizeof(buf)), rnh_type2str(type)); } if (!listnode_lookup(rnh->client_list, client)) listnode_add(rnh->client_list, client); @@ -304,17 +304,17 @@ void zebra_add_rnh_client(struct rnh *rnh, struct zserv *client, * We always need to respond with known information, * currently multiple daemons expect this behavior */ - send_client(rnh, client, type, vrf_id); + zebra_send_rnh_update(rnh, client, type, vrf_id, 0); } void zebra_remove_rnh_client(struct rnh *rnh, struct zserv *client, - rnh_type_t type) + enum rnh_type type) { if (IS_ZEBRA_DEBUG_NHT) { char buf[PREFIX2STR_BUFFER]; - zlog_debug("Client %s unregisters for RNH %s type %d", + zlog_debug("Client %s unregisters for RNH %s type %s", zebra_route_string(client->proto), - rnh_str(rnh, buf, sizeof(buf)), type); + rnh_str(rnh, buf, sizeof(buf)), rnh_type2str(type)); } listnode_delete(rnh->client_list, client); zebra_delete_rnh(rnh, type); @@ -337,30 +337,37 @@ static void addr2hostprefix(int af, const union g_addr *addr, break; default: memset(prefix, 0, sizeof(*prefix)); - zlog_debug("%s: unknown address family %d", __func__, af); + zlog_warn("%s: unknown address family %d", __func__, af); break; } } -void zebra_register_rnh_pseudowire(vrf_id_t vrf_id, struct zebra_pw *pw) +void zebra_register_rnh_pseudowire(vrf_id_t vrf_id, struct zebra_pw *pw, + bool *nht_exists) { struct prefix nh; struct rnh *rnh; bool exists; struct zebra_vrf *zvrf; + *nht_exists = false; + zvrf = vrf_info_lookup(vrf_id); if (!zvrf) return; addr2hostprefix(pw->af, &pw->nexthop, &nh); rnh = zebra_add_rnh(&nh, vrf_id, RNH_NEXTHOP_TYPE, &exists); - if (rnh && !listnode_lookup(rnh->zebra_pseudowire_list, pw)) { + if (!rnh) + return; + + if (!listnode_lookup(rnh->zebra_pseudowire_list, pw)) { listnode_add(rnh->zebra_pseudowire_list, pw); pw->rnh = rnh; zebra_evaluate_rnh(zvrf, family2afi(pw->af), 1, RNH_NEXTHOP_TYPE, &nh); - } + } else + *nht_exists = true; } void zebra_deregister_rnh_pseudowire(vrf_id_t vrf_id, struct zebra_pw *pw) @@ -384,7 +391,7 @@ static void zebra_rnh_clear_nexthop_rnh_filters(struct route_entry *re) struct nexthop *nexthop; if (re) { - for (nexthop = re->ng.nexthop; nexthop; + for (nexthop = re->nhe->nhg.nexthop; nexthop; nexthop = nexthop->next) { UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_RNH_FILTERED); } @@ -400,10 +407,10 @@ static int zebra_rnh_apply_nht_rmap(afi_t afi, struct zebra_vrf *zvrf, { int at_least_one = 0; struct nexthop *nexthop; - int ret; + route_map_result_t ret; if (prn && re) { - for (nexthop = re->ng.nexthop; nexthop; + for (nexthop = re->nhe->nhg.nexthop; nexthop; nexthop = nexthop->next) { ret = zebra_nht_route_map_check( afi, proto, &prn->p, zvrf, re, nexthop); @@ -411,7 +418,7 @@ static int zebra_rnh_apply_nht_rmap(afi_t afi, struct zebra_vrf *zvrf, at_least_one++; /* at least one valid NH */ else { SET_FLAG(nexthop->flags, - NEXTHOP_FLAG_RNH_FILTERED); + NEXTHOP_FLAG_RNH_FILTERED); } } } @@ -450,18 +457,19 @@ zebra_rnh_resolve_import_entry(struct zebra_vrf *zvrf, afi_t afi, if (IS_ZEBRA_DEBUG_NHT_DETAILED) { char buf[PREFIX_STRLEN]; - char buf1[PREFIX_STRLEN]; + char buf1[SRCDEST2STR_BUFFER]; - zlog_debug("%s: %u:%s Resolved Import Entry to %s", - __PRETTY_FUNCTION__, rnh->vrf_id, + zlog_debug("%s: %u:%s Resolved Import Entry to %s", __func__, + rnh->vrf_id, prefix2str(&rnh->node->p, buf, sizeof(buf)), - srcdest_rnode2str(rn, buf1, sizeof(buf))); + srcdest_rnode2str(rn, buf1, sizeof(buf1))); } /* Identify appropriate route entry. */ RNODE_FOREACH_RE (rn, re) { if (!CHECK_FLAG(re->status, ROUTE_ENTRY_REMOVED) && CHECK_FLAG(re->flags, ZEBRA_FLAG_SELECTED) + && !CHECK_FLAG(re->status, ROUTE_ENTRY_QUEUED) && (re->type != ZEBRA_ROUTE_BGP)) break; } @@ -470,7 +478,7 @@ zebra_rnh_resolve_import_entry(struct zebra_vrf *zvrf, afi_t afi, *prn = rn; if (!re && IS_ZEBRA_DEBUG_NHT_DETAILED) - zlog_debug("\tRejected due to removed or is a bgp route"); + zlog_debug(" Rejected due to removed or is a bgp route"); return re; } @@ -522,8 +530,9 @@ static void zebra_rnh_eval_import_check_entry(struct zebra_vrf *zvrf, afi_t afi, } /* state changed, notify clients */ for (ALL_LIST_ELEMENTS_RO(rnh->client_list, node, client)) { - send_client(rnh, client, - RNH_IMPORT_CHECK_TYPE, zvrf->vrf->vrf_id); + zebra_send_rnh_update(rnh, client, + RNH_IMPORT_CHECK_TYPE, + zvrf->vrf->vrf_id, 0); } } } @@ -585,61 +594,14 @@ static void zebra_rnh_notify_protocol_clients(struct zebra_vrf *zvrf, afi_t afi, zebra_route_string(client->proto)); } - send_client(rnh, client, RNH_NEXTHOP_TYPE, zvrf->vrf->vrf_id); + zebra_send_rnh_update(rnh, client, RNH_NEXTHOP_TYPE, + zvrf->vrf->vrf_id, 0); } if (re) zebra_rnh_clear_nexthop_rnh_filters(re); } -static void zebra_rnh_process_pbr_tables(afi_t afi, struct route_node *nrn, - struct rnh *rnh, - struct route_node *prn, - struct route_entry *re) -{ - struct zebra_router_table *zrt; - struct route_entry *o_re; - struct route_node *o_rn; - struct listnode *node; - struct zserv *client; - - /* - * We are only concerned about nexthops that change for - * anyone using PBR - */ - for (ALL_LIST_ELEMENTS_RO(rnh->client_list, node, client)) { - if (client->proto == ZEBRA_ROUTE_PBR) - break; - } - - if (!client) - return; - - RB_FOREACH (zrt, zebra_router_table_head, &zrouter.tables) { - if (afi != zrt->afi) - continue; - - for (o_rn = route_top(zrt->table); o_rn; - o_rn = srcdest_route_next(o_rn)) { - RNODE_FOREACH_RE (o_rn, o_re) { - if (o_re->type == ZEBRA_ROUTE_PBR) - break; - - } - - /* - * If we have a PBR route and a nexthop changes - * just rethink it. Yes this is a hammer, but - * a small one - */ - if (o_re) { - SET_FLAG(o_re->status, ROUTE_ENTRY_CHANGED); - rib_queue_add(o_rn); - } - } - } -} - /* * Utility to determine whether a candidate nexthop is useable. We make this * check in a couple of places, so this is a single home for the logic we @@ -690,8 +652,8 @@ zebra_rnh_resolve_nexthop_entry(struct zebra_vrf *zvrf, afi_t afi, char buf[PREFIX_STRLEN]; char buf1[PREFIX_STRLEN]; - zlog_debug("%s: %u:%s Possible Match to %s", - __PRETTY_FUNCTION__, rnh->vrf_id, + zlog_debug("%s: %u:%s Possible Match to %s", __func__, + rnh->vrf_id, prefix2str(&rnh->node->p, buf, sizeof(buf)), srcdest_rnode2str(rn, buf1, sizeof(buf))); } @@ -700,10 +662,10 @@ zebra_rnh_resolve_nexthop_entry(struct zebra_vrf *zvrf, afi_t afi, * match route to be exact if so specified */ if (is_default_prefix(&rn->p) - && !rnh_resolve_via_default(rn->p.family)) { + && !rnh_resolve_via_default(zvrf, rn->p.family)) { if (IS_ZEBRA_DEBUG_NHT_DETAILED) zlog_debug( - "\tNot allowed to resolve through default prefix"); + " Not allowed to resolve through default prefix"); return NULL; } @@ -712,14 +674,23 @@ zebra_rnh_resolve_nexthop_entry(struct zebra_vrf *zvrf, afi_t afi, if (CHECK_FLAG(re->status, ROUTE_ENTRY_REMOVED)) { if (IS_ZEBRA_DEBUG_NHT_DETAILED) zlog_debug( - "\tRoute Entry %s removed", + " Route Entry %s removed", + zebra_route_string(re->type)); + continue; + } + if (!CHECK_FLAG(re->flags, ZEBRA_FLAG_SELECTED) && + !CHECK_FLAG(re->flags, ZEBRA_FLAG_FIB_OVERRIDE)) { + if (IS_ZEBRA_DEBUG_NHT_DETAILED) + zlog_debug( + " Route Entry %s !selected", zebra_route_string(re->type)); continue; } - if (!CHECK_FLAG(re->flags, ZEBRA_FLAG_SELECTED)) { + + if (CHECK_FLAG(re->status, ROUTE_ENTRY_QUEUED)) { if (IS_ZEBRA_DEBUG_NHT_DETAILED) zlog_debug( - "\tRoute Entry %s !selected", + " Route Entry %s queued", zebra_route_string(re->type)); continue; } @@ -727,7 +698,7 @@ zebra_rnh_resolve_nexthop_entry(struct zebra_vrf *zvrf, afi_t afi, /* Just being SELECTED isn't quite enough - must * have an installed nexthop to be useful. */ - for (ALL_NEXTHOPS(re->ng, nexthop)) { + for (ALL_NEXTHOPS(re->nhe->nhg, nexthop)) { if (rnh_nexthop_valid(re, nexthop)) break; } @@ -735,7 +706,7 @@ zebra_rnh_resolve_nexthop_entry(struct zebra_vrf *zvrf, afi_t afi, if (nexthop == NULL) { if (IS_ZEBRA_DEBUG_NHT_DETAILED) zlog_debug( - "\tRoute Entry %s no nexthops", + " Route Entry %s no nexthops", zebra_route_string(re->type)); continue; } @@ -746,7 +717,8 @@ zebra_rnh_resolve_nexthop_entry(struct zebra_vrf *zvrf, afi_t afi, break; if (re->type == ZEBRA_ROUTE_NHRP) { - for (nexthop = re->ng.nexthop; nexthop; + for (nexthop = re->nhe->nhg.nexthop; + nexthop; nexthop = nexthop->next) if (nexthop->type == NEXTHOP_TYPE_IFINDEX) @@ -769,7 +741,7 @@ zebra_rnh_resolve_nexthop_entry(struct zebra_vrf *zvrf, afi_t afi, else { if (IS_ZEBRA_DEBUG_NHT_DETAILED) zlog_debug( - "\tNexthop must be connected, cannot recurse up"); + " Nexthop must be connected, cannot recurse up"); return NULL; } } @@ -804,7 +776,7 @@ static void zebra_rnh_eval_nexthop_entry(struct zebra_vrf *zvrf, afi_t afi, * change. */ zebra_rnh_remove_from_routing_table(rnh); - if (!prefix_same(&rnh->resolved_route, prn ? NULL : &prn->p)) { + if (!prefix_same(&rnh->resolved_route, prn ? &prn->p : NULL)) { if (prn) prefix_copy(&rnh->resolved_route, &prn->p); else { @@ -834,8 +806,6 @@ static void zebra_rnh_eval_nexthop_entry(struct zebra_vrf *zvrf, afi_t afi, zebra_rnh_notify_protocol_clients(zvrf, afi, nrn, rnh, prn, rnh->state); - zebra_rnh_process_pbr_tables(afi, nrn, rnh, prn, rnh->state); - /* Process pseudowires attached to this nexthop */ zebra_rnh_process_pseudowires(zvrf->vrf->vrf_id, rnh); } @@ -843,7 +813,7 @@ static void zebra_rnh_eval_nexthop_entry(struct zebra_vrf *zvrf, afi_t afi, /* Evaluate one tracked entry */ static void zebra_rnh_evaluate_entry(struct zebra_vrf *zvrf, afi_t afi, - int force, rnh_type_t type, + int force, enum rnh_type type, struct route_node *nrn) { struct rnh *rnh; @@ -853,8 +823,8 @@ static void zebra_rnh_evaluate_entry(struct zebra_vrf *zvrf, afi_t afi, if (IS_ZEBRA_DEBUG_NHT) { prefix2str(&nrn->p, bufn, INET6_ADDRSTRLEN); - zlog_debug("%u:%s: Evaluate RNH, type %d %s", zvrf->vrf->vrf_id, - bufn, type, force ? "(force)" : ""); + zlog_debug("%u:%s: Evaluate RNH, type %s %s", zvrf->vrf->vrf_id, + bufn, rnh_type2str(type), force ? "(force)" : ""); } rnh = nrn->info; @@ -890,7 +860,7 @@ static void zebra_rnh_evaluate_entry(struct zebra_vrf *zvrf, afi_t afi, * covers multiple nexthops we are interested in. */ static void zebra_rnh_clear_nhc_flag(struct zebra_vrf *zvrf, afi_t afi, - rnh_type_t type, struct route_node *nrn) + enum rnh_type type, struct route_node *nrn) { struct rnh *rnh; struct route_entry *re; @@ -906,17 +876,15 @@ static void zebra_rnh_clear_nhc_flag(struct zebra_vrf *zvrf, afi_t afi, re = zebra_rnh_resolve_nexthop_entry(zvrf, afi, nrn, rnh, &prn); - if (re) { - UNSET_FLAG(re->status, ROUTE_ENTRY_NEXTHOPS_CHANGED); + if (re) UNSET_FLAG(re->status, ROUTE_ENTRY_LABELS_CHANGED); - } } /* Evaluate all tracked entries (nexthops or routes for import into BGP) * of a particular VRF and address-family or a specific prefix. */ void zebra_evaluate_rnh(struct zebra_vrf *zvrf, afi_t afi, int force, - rnh_type_t type, struct prefix *p) + enum rnh_type type, struct prefix *p) { struct route_table *rnh_table; struct route_node *nrn; @@ -952,19 +920,20 @@ void zebra_evaluate_rnh(struct zebra_vrf *zvrf, afi_t afi, int force, } void zebra_print_rnh_table(vrf_id_t vrfid, afi_t afi, struct vty *vty, - rnh_type_t type, struct prefix *p) + enum rnh_type type, struct prefix *p) { struct route_table *table; struct route_node *rn; table = get_rnh_table(vrfid, afi, type); if (!table) { - zlog_debug("print_rnhs: rnh table not found"); + if (IS_ZEBRA_DEBUG_NHT) + zlog_debug("print_rnhs: rnh table not found"); return; } for (rn = route_top(table); rn; rn = route_next(rn)) { - if (p && prefix_cmp(&rn->p, p) != 0) + if (p && !prefix_match(&rn->p, p)) continue; if (rn->info) @@ -982,11 +951,11 @@ static void free_state(vrf_id_t vrf_id, struct route_entry *re, return; /* free RE and nexthops */ - nexthops_free(re->ng.nexthop); + zebra_nhg_free(re->nhe); XFREE(MTYPE_RE, re); } -static void copy_state(struct rnh *rnh, struct route_entry *re, +static void copy_state(struct rnh *rnh, const struct route_entry *re, struct route_node *rn) { struct route_entry *state; @@ -1006,13 +975,121 @@ static void copy_state(struct rnh *rnh, struct route_entry *re, state->vrf_id = re->vrf_id; state->status = re->status; - route_entry_copy_nexthops(state, re->ng.nexthop); + state->nhe = zebra_nhe_copy(re->nhe, 0); + + /* Copy the 'fib' nexthops also, if present - we want to capture + * the true installed nexthops. + */ + if (re->fib_ng.nexthop) + nexthop_group_copy(&state->fib_ng, &re->fib_ng); + if (re->fib_backup_ng.nexthop) + nexthop_group_copy(&state->fib_backup_ng, &re->fib_backup_ng); + rnh->state = state; } -static int compare_state(struct route_entry *r1, struct route_entry *r2) +/* + * Compare two route_entries' nexthops. + */ +static bool compare_valid_nexthops(struct route_entry *r1, + struct route_entry *r2) { + bool matched_p = false; + struct nexthop_group *nhg1, *nhg2; + struct nexthop *nh1, *nh2; + + /* Account for backup nexthops and for the 'fib' nexthop lists, + * if present. + */ + nhg1 = rib_get_fib_nhg(r1); + nhg2 = rib_get_fib_nhg(r2); + + nh1 = nhg1->nexthop; + nh2 = nhg2->nexthop; + + while (1) { + /* Find each list's next valid nexthop */ + while ((nh1 != NULL) && !rnh_nexthop_valid(r1, nh1)) + nh1 = nexthop_next(nh1); + + while ((nh2 != NULL) && !rnh_nexthop_valid(r2, nh2)) + nh2 = nexthop_next(nh2); + + if (nh1 && nh2) { + /* Any difference is a no-match */ + if (nexthop_cmp(nh1, nh2) != 0) { + if (IS_ZEBRA_DEBUG_NHT_DETAILED) + zlog_debug("%s: nh1, nh2 differ", + __func__); + goto done; + } + + nh1 = nexthop_next(nh1); + nh2 = nexthop_next(nh2); + } else if (nh1 || nh2) { + /* One list has more valid nexthops than the other */ + if (IS_ZEBRA_DEBUG_NHT_DETAILED) + zlog_debug("%s: nh1 %s, nh2 %s", __func__, + nh1 ? "non-NULL" : "NULL", + nh2 ? "non-NULL" : "NULL"); + goto done; + } else + break; /* Done with both lists */ + } + + /* The test for the backups is slightly different: the only installed + * backups will be in the 'fib' list. + */ + nhg1 = rib_get_fib_backup_nhg(r1); + nhg2 = rib_get_fib_backup_nhg(r2); + + nh1 = nhg1->nexthop; + nh2 = nhg2->nexthop; + + while (1) { + /* Find each backup list's next valid nexthop */ + while ((nh1 != NULL) && !rnh_nexthop_valid(r1, nh1)) + nh1 = nexthop_next(nh1); + while ((nh2 != NULL) && !rnh_nexthop_valid(r2, nh2)) + nh2 = nexthop_next(nh2); + + if (nh1 && nh2) { + /* Any difference is a no-match */ + if (nexthop_cmp(nh1, nh2) != 0) { + if (IS_ZEBRA_DEBUG_NHT_DETAILED) + zlog_debug("%s: backup nh1, nh2 differ", + __func__); + goto done; + } + + nh1 = nexthop_next(nh1); + nh2 = nexthop_next(nh2); + } else if (nh1 || nh2) { + /* One list has more valid nexthops than the other */ + if (IS_ZEBRA_DEBUG_NHT_DETAILED) + zlog_debug("%s: backup nh1 %s, nh2 %s", + __func__, + nh1 ? "non-NULL" : "NULL", + nh2 ? "non-NULL" : "NULL"); + goto done; + } else + break; /* Done with both lists */ + } + + /* Well, it's a match */ + if (IS_ZEBRA_DEBUG_NHT_DETAILED) + zlog_debug("%s: matched", __func__); + + matched_p = true; + +done: + + return matched_p; +} + +static int compare_state(struct route_entry *r1, struct route_entry *r2) +{ if (!r1 && !r2) return 0; @@ -1025,25 +1102,24 @@ static int compare_state(struct route_entry *r1, struct route_entry *r2) if (r1->metric != r2->metric) return 1; - if (r1->nexthop_num != r2->nexthop_num) - return 1; - - if (CHECK_FLAG(r1->status, ROUTE_ENTRY_NEXTHOPS_CHANGED) - || CHECK_FLAG(r1->status, ROUTE_ENTRY_LABELS_CHANGED)) + if (!compare_valid_nexthops(r1, r2)) return 1; return 0; } -static int send_client(struct rnh *rnh, struct zserv *client, rnh_type_t type, - vrf_id_t vrf_id) +int zebra_send_rnh_update(struct rnh *rnh, struct zserv *client, + enum rnh_type type, vrf_id_t vrf_id, + uint32_t srte_color) { - struct stream *s; + struct stream *s = NULL; struct route_entry *re; unsigned long nump; uint8_t num; struct nexthop *nh; struct route_node *rn; + int ret; + uint32_t message = 0; int cmd = (type == RNH_IMPORT_CHECK_TYPE) ? ZEBRA_IMPORT_CHECK_UPDATE : ZEBRA_NEXTHOP_UPDATE; @@ -1055,6 +1131,11 @@ static int send_client(struct rnh *rnh, struct zserv *client, rnh_type_t type, zclient_create_header(s, cmd, vrf_id); + /* Message flags. */ + if (srte_color) + SET_FLAG(message, ZAPI_MESSAGE_SRTE); + stream_putl(s, message); + stream_putw(s, rn->p.family); switch (rn->p.family) { case AF_INET: @@ -1068,10 +1149,16 @@ static int send_client(struct rnh *rnh, struct zserv *client, rnh_type_t type, default: flog_err(EC_ZEBRA_RNH_UNKNOWN_FAMILY, "%s: Unknown family (%d) notification attempted\n", - __FUNCTION__, rn->p.family); - break; + __func__, rn->p.family); + goto failure; } + if (srte_color) + stream_putl(s, srte_color); + if (re) { + struct zapi_nexthop znh; + struct nexthop_group *nhg; + stream_putc(s, re->type); stream_putw(s, re->instance); stream_putc(s, re->distance); @@ -1079,41 +1166,33 @@ static int send_client(struct rnh *rnh, struct zserv *client, rnh_type_t type, num = 0; nump = stream_get_endp(s); stream_putc(s, 0); - for (ALL_NEXTHOPS(re->ng, nh)) + + nhg = rib_get_fib_nhg(re); + for (ALL_NEXTHOPS_PTR(nhg, nh)) if (rnh_nexthop_valid(re, nh)) { - stream_putl(s, nh->vrf_id); - stream_putc(s, nh->type); - switch (nh->type) { - case NEXTHOP_TYPE_IPV4: - case NEXTHOP_TYPE_IPV4_IFINDEX: - stream_put_in_addr(s, &nh->gate.ipv4); - stream_putl(s, nh->ifindex); - break; - case NEXTHOP_TYPE_IFINDEX: - stream_putl(s, nh->ifindex); - break; - case NEXTHOP_TYPE_IPV6: - case NEXTHOP_TYPE_IPV6_IFINDEX: - stream_put(s, &nh->gate.ipv6, 16); - stream_putl(s, nh->ifindex); - break; - default: - /* do nothing */ - break; - } - if (nh->nh_label) { - stream_putc(s, - nh->nh_label->num_labels); - if (nh->nh_label->num_labels) - stream_put( - s, - &nh->nh_label->label[0], - nh->nh_label->num_labels - * sizeof(mpls_label_t)); - } else - stream_putc(s, 0); + zapi_nexthop_from_nexthop(&znh, nh); + ret = zapi_nexthop_encode(s, &znh, 0, message); + if (ret < 0) + goto failure; + num++; } + + nhg = rib_get_fib_backup_nhg(re); + if (nhg) { + for (ALL_NEXTHOPS_PTR(nhg, nh)) + if (rnh_nexthop_valid(re, nh)) { + zapi_nexthop_from_nexthop(&znh, nh); + ret = zapi_nexthop_encode( + s, &znh, 0 /* flags */, + 0 /* message */); + if (ret < 0) + goto failure; + + num++; + } + } + stream_putc_at(s, nump, num); } else { stream_putc(s, 0); // type @@ -1127,12 +1206,17 @@ static int send_client(struct rnh *rnh, struct zserv *client, rnh_type_t type, client->nh_last_upd_time = monotime(NULL); client->last_write_cmd = cmd; return zserv_send_message(client, s); + +failure: + + stream_free(s); + return -1; } static void print_nh(struct nexthop *nexthop, struct vty *vty) { char buf[BUFSIZ]; - struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT); + struct zebra_ns *zns = zebra_ns_lookup(nexthop->vrf_id); switch (nexthop->type) { case NEXTHOP_TYPE_IPV4: @@ -1179,7 +1263,7 @@ static void print_rnh(struct route_node *rn, struct vty *vty) if (rnh->state) { vty_out(vty, " resolved via %s\n", zebra_route_string(rnh->state->type)); - for (nexthop = rnh->state->ng.nexthop; nexthop; + for (nexthop = rnh->state->nhe->nhg.nexthop; nexthop; nexthop = nexthop->next) print_nh(nexthop, vty); } else @@ -1199,16 +1283,16 @@ static void print_rnh(struct route_node *rn, struct vty *vty) } static int zebra_cleanup_rnh_client(vrf_id_t vrf_id, afi_t afi, - struct zserv *client, rnh_type_t type) + struct zserv *client, enum rnh_type type) { struct route_table *ntable; struct route_node *nrn; struct rnh *rnh; if (IS_ZEBRA_DEBUG_NHT) - zlog_debug("%u: Client %s RNH cleanup for family %s type %d", + zlog_debug("%u: Client %s RNH cleanup for family %s type %s", vrf_id, zebra_route_string(client->proto), - afi2str(afi), type); + afi2str(afi), rnh_type2str(type)); ntable = get_rnh_table(vrf_id, afi, type); if (!ntable) { @@ -1243,15 +1327,17 @@ static int zebra_client_cleanup_rnh(struct zserv *client) RNH_IMPORT_CHECK_TYPE); zebra_cleanup_rnh_client(zvrf_id(zvrf), AFI_IP6, client, RNH_IMPORT_CHECK_TYPE); - if (client->proto == ZEBRA_ROUTE_LDP) { - hash_iterate(zvrf->lsp_table, - mpls_ldp_lsp_uninstall_all, - zvrf->lsp_table); - mpls_ldp_ftn_uninstall_all(zvrf, AFI_IP); - mpls_ldp_ftn_uninstall_all(zvrf, AFI_IP6); - } } } return 0; } + +int rnh_resolve_via_default(struct zebra_vrf *zvrf, int family) +{ + if (((family == AF_INET) && zvrf->zebra_rnh_ip_default_route) + || ((family == AF_INET6) && zvrf->zebra_rnh_ipv6_default_route)) + return 1; + else + return 0; +} diff --git a/zebra/zebra_rnh.h b/zebra/zebra_rnh.h index 9cd9116eed..ba12b1738f 100644 --- a/zebra/zebra_rnh.h +++ b/zebra/zebra_rnh.h @@ -29,71 +29,42 @@ extern "C" { #endif -typedef enum { RNH_NEXTHOP_TYPE, RNH_IMPORT_CHECK_TYPE } rnh_type_t; - -/* Nexthop structure. */ -struct rnh { - uint8_t flags; - -#define ZEBRA_NHT_CONNECTED 0x1 -#define ZEBRA_NHT_DELETED 0x2 -#define ZEBRA_NHT_EXACT_MATCH 0x4 - - /* VRF identifier. */ - vrf_id_t vrf_id; - - afi_t afi; - - rnh_type_t type; - - uint32_t seqno; - - struct route_entry *state; - struct prefix resolved_route; - struct list *client_list; - - /* pseudowires dependent on this nh */ - struct list *zebra_pseudowire_list; - - struct route_node *node; - - /* - * if this has been filtered for the client - */ - int filtered[ZEBRA_ROUTE_MAX]; -}; - -extern int zebra_rnh_ip_default_route; -extern int zebra_rnh_ipv6_default_route; - extern void zebra_rnh_init(void); -static inline int rnh_resolve_via_default(int family) +static inline const char *rnh_type2str(enum rnh_type type) { - if (((family == AF_INET) && zebra_rnh_ip_default_route) - || ((family == AF_INET6) && zebra_rnh_ipv6_default_route)) - return 1; - else - return 0; + switch (type) { + case RNH_NEXTHOP_TYPE: + return "Nexthop"; + case RNH_IMPORT_CHECK_TYPE: + return "Import"; + } + + return "ERROR"; } extern struct rnh *zebra_add_rnh(struct prefix *p, vrf_id_t vrfid, - rnh_type_t type, bool *exists); + enum rnh_type type, bool *exists); extern struct rnh *zebra_lookup_rnh(struct prefix *p, vrf_id_t vrfid, - rnh_type_t type); + enum rnh_type type); extern void zebra_free_rnh(struct rnh *rnh); extern void zebra_add_rnh_client(struct rnh *rnh, struct zserv *client, - rnh_type_t type, vrf_id_t vrfid); -extern void zebra_register_rnh_pseudowire(vrf_id_t, struct zebra_pw *); + enum rnh_type type, vrf_id_t vrfid); +extern int zebra_send_rnh_update(struct rnh *rnh, struct zserv *client, + enum rnh_type type, vrf_id_t vrf_id, + uint32_t srte_color); +extern void zebra_register_rnh_pseudowire(vrf_id_t, struct zebra_pw *, bool *); extern void zebra_deregister_rnh_pseudowire(vrf_id_t, struct zebra_pw *); extern void zebra_remove_rnh_client(struct rnh *rnh, struct zserv *client, - rnh_type_t type); + enum rnh_type type); extern void zebra_evaluate_rnh(struct zebra_vrf *zvrf, afi_t afi, int force, - rnh_type_t type, struct prefix *p); + enum rnh_type type, struct prefix *p); extern void zebra_print_rnh_table(vrf_id_t vrfid, afi_t afi, struct vty *vty, - rnh_type_t type, struct prefix *p); + enum rnh_type type, struct prefix *p); extern char *rnh_str(struct rnh *rnh, char *buf, int size); +extern int rnh_resolve_via_default(struct zebra_vrf *zvrf, int family); + #ifdef __cplusplus } #endif diff --git a/zebra/zebra_routemap.c b/zebra/zebra_routemap.c index 5d1cbbe781..88ef4823b1 100644 --- a/zebra/zebra_routemap.c +++ b/zebra/zebra_routemap.c @@ -30,6 +30,8 @@ #include "filter.h" #include "plist.h" #include "nexthop.h" +#include "northbound_cli.h" +#include "route_types.h" #include "vrf.h" #include "frrstr.h" @@ -58,87 +60,12 @@ struct nh_rmap_obj { static void zebra_route_map_set_delay_timer(uint32_t value); - -/* Add zebra route map rule */ -static int zebra_route_match_add(struct vty *vty, const char *command, - const char *arg, route_map_event_t type) -{ - VTY_DECLVAR_CONTEXT(route_map_index, index); - int ret; - int retval = CMD_SUCCESS; - - ret = route_map_add_match(index, command, arg); - switch (ret) { - case RMAP_RULE_MISSING: - vty_out(vty, "%% Zebra Can't find rule.\n"); - retval = CMD_WARNING_CONFIG_FAILED; - break; - case RMAP_COMPILE_ERROR: - vty_out(vty, "%% Zebra Argument is malformed.\n"); - retval = CMD_WARNING_CONFIG_FAILED; - break; - case RMAP_COMPILE_SUCCESS: - if (type != RMAP_EVENT_MATCH_ADDED) { - route_map_upd8_dependency(type, arg, index->map->name); - } - break; - } - - return retval; -} - -/* Delete zebra route map rule. */ -static int zebra_route_match_delete(struct vty *vty, const char *command, - const char *arg, route_map_event_t type) -{ - VTY_DECLVAR_CONTEXT(route_map_index, index); - int ret; - int retval = CMD_SUCCESS; - char *dep_name = NULL; - const char *tmpstr; - char *rmap_name = NULL; - - if (type != RMAP_EVENT_MATCH_DELETED) { - /* ignore the mundane, the types without any dependency */ - if (arg == NULL) { - if ((tmpstr = route_map_get_match_arg(index, command)) - != NULL) - dep_name = - XSTRDUP(MTYPE_ROUTE_MAP_RULE, tmpstr); - } else { - dep_name = XSTRDUP(MTYPE_ROUTE_MAP_RULE, arg); - } - rmap_name = XSTRDUP(MTYPE_ROUTE_MAP_NAME, index->map->name); - } - - ret = route_map_delete_match(index, command, arg); - switch (ret) { - case RMAP_RULE_MISSING: - vty_out(vty, "%% Zebra Can't find rule.\n"); - retval = CMD_WARNING_CONFIG_FAILED; - break; - case RMAP_COMPILE_ERROR: - vty_out(vty, "%% Zebra Argument is malformed.\n"); - retval = CMD_WARNING_CONFIG_FAILED; - break; - case RMAP_COMPILE_SUCCESS: - if (type != RMAP_EVENT_MATCH_DELETED && dep_name) - route_map_upd8_dependency(type, dep_name, rmap_name); - break; - } - - XFREE(MTYPE_ROUTE_MAP_RULE, dep_name); - XFREE(MTYPE_ROUTE_MAP_NAME, rmap_name); - - return retval; -} - /* 'match tag TAG' * Match function return 1 if match is success else return 0 */ -static route_map_result_t route_match_tag(void *rule, - const struct prefix *prefix, - route_map_object_t type, void *object) +static enum route_map_cmd_result_t +route_match_tag(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { route_tag_t *tag; struct nh_rmap_obj *nh_data; @@ -154,18 +81,19 @@ static route_map_result_t route_match_tag(void *rule, } /* Route map commands for tag matching */ -static struct route_map_rule_cmd route_match_tag_cmd = { - "tag", route_match_tag, route_map_rule_tag_compile, +static const struct route_map_rule_cmd route_match_tag_cmd = { + "tag", + route_match_tag, + route_map_rule_tag_compile, route_map_rule_tag_free, }; /* `match interface IFNAME' */ /* Match function return 1 if match is success else return zero. */ -static route_map_result_t route_match_interface(void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_match_interface(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { struct nh_rmap_obj *nh_data; char *ifname = rule; @@ -203,22 +131,22 @@ static void show_vrf_proto_rm(struct vty *vty, struct zebra_vrf *zvrf, { int i; - vty_out(vty, "Protocol : route-map\n"); - vty_out(vty, "------------------------\n"); + vty_out(vty, "Protocol : route-map\n"); + vty_out(vty, "-------------------------------------\n"); for (i = 0; i < ZEBRA_ROUTE_MAX; i++) { if (PROTO_RM_NAME(zvrf, af_type, i)) - vty_out(vty, "%-10s : %-10s\n", zebra_route_string(i), + vty_out(vty, "%-24s : %-10s\n", zebra_route_string(i), PROTO_RM_NAME(zvrf, af_type, i)); else - vty_out(vty, "%-10s : none\n", zebra_route_string(i)); + vty_out(vty, "%-24s : none\n", zebra_route_string(i)); } if (PROTO_RM_NAME(zvrf, af_type, i)) - vty_out(vty, "%-10s : %-10s\n", "any", + vty_out(vty, "%-24s : %-10s\n", "any", PROTO_RM_NAME(zvrf, af_type, i)); else - vty_out(vty, "%-10s : none\n", "any"); + vty_out(vty, "%-24s : none\n", "any"); } static void show_vrf_nht_rm(struct vty *vty, struct zebra_vrf *zvrf, @@ -226,22 +154,22 @@ static void show_vrf_nht_rm(struct vty *vty, struct zebra_vrf *zvrf, { int i; - vty_out(vty, "Protocol : route-map\n"); - vty_out(vty, "------------------------\n"); + vty_out(vty, "Protocol : route-map\n"); + vty_out(vty, "-------------------------------------\n"); for (i = 0; i < ZEBRA_ROUTE_MAX; i++) { if (NHT_RM_NAME(zvrf, af_type, i)) - vty_out(vty, "%-10s : %-10s\n", zebra_route_string(i), + vty_out(vty, "%-24s : %-10s\n", zebra_route_string(i), NHT_RM_NAME(zvrf, af_type, i)); else - vty_out(vty, "%-10s : none\n", zebra_route_string(i)); + vty_out(vty, "%-24s : none\n", zebra_route_string(i)); } if (NHT_RM_NAME(zvrf, af_type, i)) - vty_out(vty, "%-10s : %-10s\n", "any", + vty_out(vty, "%-24s : %-10s\n", "any", NHT_RM_NAME(zvrf, af_type, i)); else - vty_out(vty, "%-10s : none\n", "any"); + vty_out(vty, "%-24s : none\n", "any"); } static int show_proto_rm(struct vty *vty, int af_type, const char *vrf_all, @@ -310,9 +238,12 @@ static int show_nht_rm(struct vty *vty, int af_type, const char *vrf_all, } /* Route map commands for interface matching */ -struct route_map_rule_cmd route_match_interface_cmd = { - "interface", route_match_interface, route_match_interface_compile, - route_match_interface_free}; +static const struct route_map_rule_cmd route_match_interface_cmd = { + "interface", + route_match_interface, + route_match_interface_compile, + route_match_interface_free +}; static int ip_protocol_rm_add(struct zebra_vrf *zvrf, const char *rmap, int rtype, afi_t afi, safi_t safi) @@ -420,246 +351,227 @@ static int ip_nht_rm_del(struct zebra_vrf *zvrf, const char *rmap, int rtype, return CMD_SUCCESS; } -DEFUN (match_ip_address_prefix_len, - match_ip_address_prefix_len_cmd, - "match ip address prefix-len (0-32)", - MATCH_STR - IP_STR - "Match prefix length of ip address\n" - "Match prefix length of ip address\n" - "Prefix length\n") +DEFPY_YANG( + match_ip_address_prefix_len, match_ip_address_prefix_len_cmd, + "match ip address prefix-len (0-32)$length", + MATCH_STR + IP_STR + "Match prefix length of IP address\n" + "Match prefix length of IP address\n" + "Prefix length\n") { - return zebra_route_match_add(vty, "ip address prefix-len", argv[4]->arg, - RMAP_EVENT_MATCH_ADDED); + const char *xpath = "./match-condition[condition='ipv4-prefix-length']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), + "%s/frr-zebra:ipv4-prefix-length", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, length_str); + + return nb_cli_apply_changes(vty, NULL); } -DEFUN (no_match_ip_address_prefix_len, - no_match_ip_address_prefix_len_cmd, - "no match ip address prefix-len [(0-32)]", - NO_STR - MATCH_STR - IP_STR - "Match prefix length of ip address\n" - "Match prefix length of ip address\n" - "Prefix length\n") +DEFPY_YANG( + no_match_ip_address_prefix_len, no_match_ip_address_prefix_len_cmd, + "no match ip address prefix-len [(0-32)]", + NO_STR + MATCH_STR + IP_STR + "Match prefix length of IP address\n" + "Match prefix length of IP address\n" + "Prefix length\n") { - char *plen = (argc == 6) ? argv[5]->arg : NULL; - return zebra_route_match_delete(vty, "ip address prefix-len", plen, - RMAP_EVENT_MATCH_DELETED); + const char *xpath = "./match-condition[condition='ipv4-prefix-length']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); } -DEFUN (match_ipv6_address_prefix_len, - match_ipv6_address_prefix_len_cmd, - "match ipv6 address prefix-len (0-128)", - MATCH_STR - IPV6_STR - "Match prefix length of ipv6 address\n" - "Match prefix length of ipv6 address\n" - "Prefix length\n") +DEFPY_YANG( + match_ipv6_address_prefix_len, match_ipv6_address_prefix_len_cmd, + "match ipv6 address prefix-len (0-128)$length", + MATCH_STR + IPV6_STR + "Match prefix length of IPv6 address\n" + "Match prefix length of IPv6 address\n" + "Prefix length\n") { - return zebra_route_match_add(vty, "ipv6 address prefix-len", - argv[4]->arg, RMAP_EVENT_MATCH_ADDED); + const char *xpath = "./match-condition[condition='ipv6-prefix-length']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), + "%s/frr-zebra:ipv6-prefix-length", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, length_str); + + return nb_cli_apply_changes(vty, NULL); } -DEFUN (no_match_ipv6_address_prefix_len, - no_match_ipv6_address_prefix_len_cmd, - "no match ipv6 address prefix-len [(0-128)]", - NO_STR - MATCH_STR - IPV6_STR - "Match prefix length of ip address\n" - "Match prefix length of ip address\n" - "Prefix length\n") +DEFPY_YANG( + no_match_ipv6_address_prefix_len, no_match_ipv6_address_prefix_len_cmd, + "no match ipv6 address prefix-len [(0-128)]", + NO_STR + MATCH_STR + IPV6_STR + "Match prefix length of IPv6 address\n" + "Match prefix length of IPv6 address\n" + "Prefix length\n") { - char *plen = (argc == 6) ? argv[5]->arg : NULL; - return zebra_route_match_delete(vty, "ipv6 address prefix-len", plen, - RMAP_EVENT_MATCH_DELETED); + const char *xpath = "./match-condition[condition='ipv6-prefix-length']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); } -DEFUN (match_ip_nexthop_prefix_len, - match_ip_nexthop_prefix_len_cmd, - "match ip next-hop prefix-len (0-32)", - MATCH_STR - IP_STR - "Match prefixlen of nexthop ip address\n" - "Match prefixlen of given nexthop\n" - "Prefix length\n") +DEFPY_YANG( + match_ip_nexthop_prefix_len, match_ip_nexthop_prefix_len_cmd, + "match ip next-hop prefix-len (0-32)$length", + MATCH_STR + IP_STR + "Match prefixlen of nexthop IP address\n" + "Match prefixlen of given nexthop\n" + "Prefix length\n") { - return zebra_route_match_add(vty, "ip next-hop prefix-len", - argv[4]->arg, RMAP_EVENT_MATCH_ADDED); + const char *xpath = + "./match-condition[condition='ipv4-next-hop-prefix-length']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), + "%s/frr-zebra:ipv4-prefix-length", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, length_str); + + return nb_cli_apply_changes(vty, NULL); } -DEFUN (no_match_ip_nexthop_prefix_len, - no_match_ip_nexthop_prefix_len_cmd, - "no match ip next-hop prefix-len [(0-32)]", - NO_STR - MATCH_STR - IP_STR - "Match prefixlen of nexthop ip address\n" - "Match prefix length of nexthop\n" - "Prefix length\n") -{ - char *plen = (argc == 6) ? argv[5]->arg : NULL; - return zebra_route_match_delete(vty, "ip next-hop prefix-len", plen, - RMAP_EVENT_MATCH_DELETED); -} - -DEFUN (match_source_protocol, - match_source_protocol_cmd, - "match source-protocol ", - MATCH_STR - "Match protocol via which the route was learnt\n" - "BGP protocol\n" - "OSPF protocol\n" - "RIP protocol\n" - "RIPNG protocol\n" - "ISIS protocol\n" - "OSPF6 protocol\n" - "PIM protocol\n" - "NHRP protocol\n" - "EIGRP protocol\n" - "BABEL protocol\n" - "Routes from directly connected peer\n" - "Routes from system configuration\n" - "Routes from kernel\n" - "Statically configured routes\n" - "SHARP process\n") -{ - char *proto = argv[2]->text; - int i; +DEFPY_YANG( + no_match_ip_nexthop_prefix_len, no_match_ip_nexthop_prefix_len_cmd, + "no match ip next-hop prefix-len [(0-32)]", + NO_STR + MATCH_STR + IP_STR + "Match prefixlen of nexthop IP address\n" + "Match prefix length of nexthop\n" + "Prefix length\n") +{ + const char *xpath = + "./match-condition[condition='ipv4-next-hop-prefix-length']"; - i = proto_name2num(proto); - if (i < 0) { - vty_out(vty, "invalid protocol name \"%s\"\n", proto); - return CMD_WARNING_CONFIG_FAILED; - } - return zebra_route_match_add(vty, "source-protocol", proto, - RMAP_EVENT_MATCH_ADDED); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); } -DEFUN (no_match_source_protocol, - no_match_source_protocol_cmd, - "no match source-protocol []", - NO_STR - MATCH_STR - "No match protocol via which the route was learnt\n" - "BGP protocol\n" - "OSPF protocol\n" - "RIP protocol\n" - "RIPNG protocol\n" - "ISIS protocol\n" - "OSPF6 protocol\n" - "PIM protocol\n" - "NHRP protocol\n" - "EIGRP protocol\n" - "BABEL protocol\n" - "Routes from directly connected peer\n" - "Routes from system configuration\n" - "Routes from kernel\n" - "Statically configured routes\n" - "SHARP process\n") -{ - char *proto = (argc == 4) ? argv[3]->text : NULL; - return zebra_route_match_delete(vty, "source-protocol", proto, - RMAP_EVENT_MATCH_DELETED); -} - -DEFUN (match_source_instance, - match_source_instance_cmd, - "match source-instance (0-255)", - MATCH_STR - "Match the protocol's instance number\n" - "The instance number\n") -{ - char *instance = argv[2]->arg; - - return zebra_route_match_add(vty, "source-instance", instance, - RMAP_EVENT_MATCH_ADDED); -} - -DEFUN (no_match_source_instance, - no_match_source_instance_cmd, - "no match source-instance [(0-255)]", - NO_STR MATCH_STR - "Match the protocol's instance number\n" - "The instance number\n") -{ - char *instance = (argc == 4) ? argv[3]->arg : NULL; - - return zebra_route_match_delete(vty, "source-instance", instance, - RMAP_EVENT_MATCH_ADDED); +DEFPY_YANG( + match_source_protocol, match_source_protocol_cmd, + "match source-protocol " FRR_REDIST_STR_ZEBRA "$proto", + MATCH_STR + "Match protocol via which the route was learnt\n" + FRR_REDIST_HELP_STR_ZEBRA) +{ + const char *xpath = "./match-condition[condition='source-protocol']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), + "%s/frr-zebra:source-protocol", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, proto); + + return nb_cli_apply_changes(vty, NULL); } -/* set functions */ +DEFPY_YANG( + no_match_source_protocol, no_match_source_protocol_cmd, + "no match source-protocol [" FRR_REDIST_STR_ZEBRA "]", + NO_STR + MATCH_STR + "Match protocol via which the route was learnt\n" + FRR_REDIST_HELP_STR_ZEBRA) +{ + const char *xpath = "./match-condition[condition='source-protocol']"; -DEFUN (set_src, - set_src_cmd, - "set src ", - SET_STR - "src address for route\n" - "IPv4 src address\n" - "IPv6 src address\n") -{ - int idx_ip = 2; - union g_addr src; - struct interface *pif = NULL; - int family; - struct prefix p; - struct vrf *vrf; - - if (inet_pton(AF_INET, argv[idx_ip]->arg, &src.ipv4) != 1) { - if (inet_pton(AF_INET6, argv[idx_ip]->arg, &src.ipv6) != 1) { - vty_out(vty, "%% not a valid IPv4/v6 address\n"); - return CMD_WARNING_CONFIG_FAILED; - } + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); - p.family = family = AF_INET6; - p.u.prefix6 = src.ipv6; - p.prefixlen = IPV6_MAX_BITLEN; - } else { - p.family = family = AF_INET; - p.u.prefix4 = src.ipv4; - p.prefixlen = IPV4_MAX_BITLEN; - } + return nb_cli_apply_changes(vty, NULL); +} - if (!zebra_check_addr(&p)) { - vty_out(vty, "%% not a valid source IPv4/v6 address\n"); - return CMD_WARNING_CONFIG_FAILED; - } +DEFPY_YANG( + match_source_instance, match_source_instance_cmd, + "match source-instance (0-255)$instance", + MATCH_STR + "Match the protocol's instance number\n" + "The instance number\n") +{ + const char *xpath = "./match-condition[condition='source-instance']"; + char xpath_value[XPATH_MAXLEN]; - RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) { - if (family == AF_INET) - pif = if_lookup_exact_address((void *)&src.ipv4, - AF_INET, vrf->vrf_id); - else if (family == AF_INET6) - pif = if_lookup_exact_address((void *)&src.ipv6, - AF_INET6, vrf->vrf_id); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), + "%s/frr-zebra:source-instance", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, instance_str); - if (pif != NULL) - break; - } + return nb_cli_apply_changes(vty, NULL); +} - if (!pif) { - vty_out(vty, "%% not a local address\n"); - return CMD_WARNING_CONFIG_FAILED; +DEFPY_YANG( + no_match_source_instance, no_match_source_instance_cmd, + "no match source-instance [(0-255)]", + NO_STR MATCH_STR + "Match the protocol's instance number\n" + "The instance number\n") +{ + const char *xpath = "./match-condition[condition='source-instance']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +/* set functions */ + +DEFPY_YANG( + set_src, set_src_cmd, + "set src ", + SET_STR + "src address for route\n" + "IPv4 src address\n" + "IPv6 src address\n") +{ + const char *xpath = "./set-action[action='source']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + if (addrv4_str) { + snprintf(xpath_value, sizeof(xpath_value), + "%s/frr-zebra:source-v4", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, + addrv4_str); + } else { + snprintf(xpath_value, sizeof(xpath_value), + "%s/frr-zebra:source-v6", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, + addrv6_str); } - VTY_DECLVAR_CONTEXT(route_map_index, index); - return generic_set_add(vty, index, "src", argv[idx_ip]->arg); + return nb_cli_apply_changes(vty, NULL); } -DEFUN (no_set_src, - no_set_src_cmd, - "no set src []", - NO_STR - SET_STR - "Source address for route\n" - "IPv4 address\n" - "IPv6 address\n") +DEFPY_YANG( + no_set_src, no_set_src_cmd, + "no set src []", + NO_STR + SET_STR + "Source address for route\n" + "IPv4 address\n" + "IPv6 address\n") { - char *ip = (argc == 4) ? argv[3]->arg : NULL; - VTY_DECLVAR_CONTEXT(route_map_index, index); - return generic_set_delete(vty, index, "src", ip); + const char *xpath = "./set-action[action='source']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); } DEFUN (zebra_route_map_timer, @@ -693,7 +605,7 @@ DEFUN (no_zebra_route_map_timer, return (CMD_SUCCESS); } -DEFPY (ip_protocol, +DEFPY_YANG (ip_protocol, ip_protocol_cmd, "ip protocol " FRR_IP_PROTOCOL_MAP_STR_ZEBRA " $proto route-map ROUTE-MAP$rmap", @@ -705,6 +617,9 @@ DEFPY (ip_protocol, { int ret, rtype; + assert(proto); + assert(rmap); + ZEBRA_DECLVAR_CONTEXT(vrf, zvrf); if (!zvrf) @@ -724,7 +639,7 @@ DEFPY (ip_protocol, return ret; } -DEFPY (no_ip_protocol, +DEFPY_YANG (no_ip_protocol, no_ip_protocol_cmd, "no ip protocol " FRR_IP_PROTOCOL_MAP_STR_ZEBRA " $proto [route-map ROUTE-MAP$rmap]", @@ -737,6 +652,8 @@ DEFPY (no_ip_protocol, { int ret, rtype; + assert(proto); + ZEBRA_DECLVAR_CONTEXT(vrf, zvrf); if (!zvrf) @@ -756,7 +673,7 @@ DEFPY (no_ip_protocol, return ret; } -DEFPY (show_ip_protocol, +DEFPY_YANG (show_ip_protocol, show_ip_protocol_cmd, "show ip protocol [vrf ]", SHOW_STR @@ -769,7 +686,7 @@ DEFPY (show_ip_protocol, return ret; } -DEFPY (ipv6_protocol, +DEFPY_YANG (ipv6_protocol, ipv6_protocol_cmd, "ipv6 protocol " FRR_IP6_PROTOCOL_MAP_STR_ZEBRA " $proto route-map ROUTE-MAP$rmap", @@ -781,6 +698,9 @@ DEFPY (ipv6_protocol, { int ret, rtype; + assert(rmap); + assert(proto); + ZEBRA_DECLVAR_CONTEXT(vrf, zvrf); if (!zvrf) @@ -800,7 +720,7 @@ DEFPY (ipv6_protocol, return ret; } -DEFPY (no_ipv6_protocol, +DEFPY_YANG (no_ipv6_protocol, no_ipv6_protocol_cmd, "no ipv6 protocol " FRR_IP6_PROTOCOL_MAP_STR_ZEBRA " $proto [route-map ROUTE-MAP$rmap]", @@ -813,6 +733,8 @@ DEFPY (no_ipv6_protocol, { int ret, rtype; + assert(proto); + ZEBRA_DECLVAR_CONTEXT(vrf, zvrf); if (!zvrf) @@ -832,7 +754,7 @@ DEFPY (no_ipv6_protocol, return ret; } -DEFPY (show_ipv6_protocol, +DEFPY_YANG (show_ipv6_protocol, show_ipv6_protocol_cmd, "show ipv6 protocol [vrf ]", SHOW_STR @@ -845,7 +767,7 @@ DEFPY (show_ipv6_protocol, return ret; } -DEFPY (ip_protocol_nht_rmap, +DEFPY_YANG (ip_protocol_nht_rmap, ip_protocol_nht_rmap_cmd, "ip nht " FRR_IP_PROTOCOL_MAP_STR_ZEBRA " $proto route-map ROUTE-MAP$rmap", @@ -858,6 +780,9 @@ DEFPY (ip_protocol_nht_rmap, int ret, rtype; + assert(proto); + assert(rmap); + ZEBRA_DECLVAR_CONTEXT(vrf, zvrf); if (!zvrf) @@ -877,7 +802,7 @@ DEFPY (ip_protocol_nht_rmap, return ret; } -DEFPY (no_ip_protocol_nht_rmap, +DEFPY_YANG (no_ip_protocol_nht_rmap, no_ip_protocol_nht_rmap_cmd, "no ip nht " FRR_IP_PROTOCOL_MAP_STR_ZEBRA " $proto route-map [ROUTE-MAP$rmap]", @@ -890,6 +815,8 @@ DEFPY (no_ip_protocol_nht_rmap, { int ret, rtype; + assert(proto); + ZEBRA_DECLVAR_CONTEXT(vrf, zvrf); if (!zvrf) @@ -909,7 +836,7 @@ DEFPY (no_ip_protocol_nht_rmap, return ret; } -DEFPY (show_ip_protocol_nht, +DEFPY_YANG (show_ip_protocol_nht, show_ip_protocol_nht_cmd, "show ip nht route-map [vrf ]", SHOW_STR @@ -923,7 +850,7 @@ DEFPY (show_ip_protocol_nht, return ret; } -DEFPY (ipv6_protocol_nht_rmap, +DEFPY_YANG (ipv6_protocol_nht_rmap, ipv6_protocol_nht_rmap_cmd, "ipv6 nht " FRR_IP6_PROTOCOL_MAP_STR_ZEBRA " $proto route-map ROUTE-MAP$rmap", @@ -935,6 +862,9 @@ DEFPY (ipv6_protocol_nht_rmap, { int ret, rtype; + assert(rmap); + assert(proto); + ZEBRA_DECLVAR_CONTEXT(vrf, zvrf); if (!zvrf) @@ -954,7 +884,7 @@ DEFPY (ipv6_protocol_nht_rmap, return ret; } -DEFPY (no_ipv6_protocol_nht_rmap, +DEFPY_YANG (no_ipv6_protocol_nht_rmap, no_ipv6_protocol_nht_rmap_cmd, "no ipv6 nht " FRR_IP6_PROTOCOL_MAP_STR_ZEBRA " $proto [route-map ROUTE-MAP$rmap]", @@ -967,6 +897,8 @@ DEFPY (no_ipv6_protocol_nht_rmap, { int ret, rtype; + assert(proto); + ZEBRA_DECLVAR_CONTEXT(vrf, zvrf); if (!zvrf) @@ -986,7 +918,7 @@ DEFPY (no_ipv6_protocol_nht_rmap, return ret; } -DEFPY (show_ipv6_protocol_nht, +DEFPY_YANG (show_ipv6_protocol_nht, show_ipv6_protocol_nht_cmd, "show ipv6 nht route-map [vrf ]", SHOW_STR @@ -1005,10 +937,9 @@ DEFPY (show_ipv6_protocol_nht, /* `match ip next-hop IP_ACCESS_LIST' */ /* Match function return 1 if match is success else return zero. */ -static route_map_result_t route_match_ip_next_hop(void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_match_ip_next_hop(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { struct access_list *alist; struct nh_rmap_obj *nh_data; @@ -1017,7 +948,7 @@ static route_map_result_t route_match_ip_next_hop(void *rule, if (type == RMAP_ZEBRA) { nh_data = object; if (!nh_data) - return RMAP_DENYMATCH; + return RMAP_NOMATCH; switch (nh_data->nexthop->type) { case NEXTHOP_TYPE_IFINDEX: @@ -1057,13 +988,16 @@ static void route_match_ip_next_hop_free(void *rule) } /* Route map commands for ip next-hop matching. */ -static struct route_map_rule_cmd route_match_ip_next_hop_cmd = { - "ip next-hop", route_match_ip_next_hop, route_match_ip_next_hop_compile, - route_match_ip_next_hop_free}; +static const struct route_map_rule_cmd route_match_ip_next_hop_cmd = { + "ip next-hop", + route_match_ip_next_hop, + route_match_ip_next_hop_compile, + route_match_ip_next_hop_free +}; /* `match ip next-hop prefix-list PREFIX_LIST' */ -static route_map_result_t +static enum route_map_cmd_result_t route_match_ip_next_hop_prefix_list(void *rule, const struct prefix *prefix, route_map_object_t type, void *object) { @@ -1074,7 +1008,7 @@ route_match_ip_next_hop_prefix_list(void *rule, const struct prefix *prefix, if (type == RMAP_ZEBRA) { nh_data = (struct nh_rmap_obj *)object; if (!nh_data) - return RMAP_DENYMATCH; + return RMAP_NOMATCH; switch (nh_data->nexthop->type) { case NEXTHOP_TYPE_IFINDEX: @@ -1110,19 +1044,21 @@ static void route_match_ip_next_hop_prefix_list_free(void *rule) XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); } -static struct route_map_rule_cmd route_match_ip_next_hop_prefix_list_cmd = { - "ip next-hop prefix-list", route_match_ip_next_hop_prefix_list, +static const struct route_map_rule_cmd + route_match_ip_next_hop_prefix_list_cmd = { + "ip next-hop prefix-list", + route_match_ip_next_hop_prefix_list, route_match_ip_next_hop_prefix_list_compile, - route_match_ip_next_hop_prefix_list_free}; + route_match_ip_next_hop_prefix_list_free +}; /* `match ip address IP_ACCESS_LIST' */ /* Match function should return 1 if match is success else return zero. */ -static route_map_result_t route_match_address(afi_t afi, void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_match_address(afi_t afi, void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { struct access_list *alist; @@ -1138,19 +1074,16 @@ static route_map_result_t route_match_address(afi_t afi, void *rule, return RMAP_NOMATCH; } -static route_map_result_t route_match_ip_address(void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_match_ip_address(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { return route_match_address(AFI_IP, rule, prefix, type, object); } -static route_map_result_t route_match_ipv6_address(void *rule, - const struct prefix *prefix, - route_map_object_t type, - void *object) - +static enum route_map_cmd_result_t +route_match_ipv6_address(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) { return route_match_address(AFI_IP6, rule, prefix, type, object); } @@ -1169,18 +1102,24 @@ static void route_match_address_free(void *rule) } /* Route map commands for ip address matching. */ -static struct route_map_rule_cmd route_match_ip_address_cmd = { - "ip address", route_match_ip_address, route_match_address_compile, - route_match_address_free}; +static const struct route_map_rule_cmd route_match_ip_address_cmd = { + "ip address", + route_match_ip_address, + route_match_address_compile, + route_match_address_free +}; /* Route map commands for ipv6 address matching. */ -static struct route_map_rule_cmd route_match_ipv6_address_cmd = { - "ipv6 address", route_match_ipv6_address, route_match_address_compile, - route_match_address_free}; +static const struct route_map_rule_cmd route_match_ipv6_address_cmd = { + "ipv6 address", + route_match_ipv6_address, + route_match_address_compile, + route_match_address_free +}; /* `match ip address prefix-list PREFIX_LIST' */ -static route_map_result_t +static enum route_map_cmd_result_t route_match_address_prefix_list(void *rule, const struct prefix *prefix, route_map_object_t type, void *object, afi_t afi) { @@ -1198,7 +1137,7 @@ route_match_address_prefix_list(void *rule, const struct prefix *prefix, return RMAP_NOMATCH; } -static route_map_result_t +static enum route_map_cmd_result_t route_match_ip_address_prefix_list(void *rule, const struct prefix *prefix, route_map_object_t type, void *object) { @@ -1216,12 +1155,15 @@ static void route_match_address_prefix_list_free(void *rule) XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); } -static struct route_map_rule_cmd route_match_ip_address_prefix_list_cmd = { - "ip address prefix-list", route_match_ip_address_prefix_list, +static const struct route_map_rule_cmd + route_match_ip_address_prefix_list_cmd = { + "ip address prefix-list", + route_match_ip_address_prefix_list, route_match_address_prefix_list_compile, - route_match_address_prefix_list_free}; + route_match_address_prefix_list_free +}; -static route_map_result_t +static enum route_map_cmd_result_t route_match_ipv6_address_prefix_list(void *rule, const struct prefix *prefix, route_map_object_t type, void *object) { @@ -1229,14 +1171,54 @@ route_match_ipv6_address_prefix_list(void *rule, const struct prefix *prefix, AFI_IP6)); } -static struct route_map_rule_cmd route_match_ipv6_address_prefix_list_cmd = { - "ipv6 address prefix-list", route_match_ipv6_address_prefix_list, +static const struct route_map_rule_cmd + route_match_ipv6_address_prefix_list_cmd = { + "ipv6 address prefix-list", + route_match_ipv6_address_prefix_list, route_match_address_prefix_list_compile, - route_match_address_prefix_list_free}; + route_match_address_prefix_list_free +}; + +/* `match ipv6 next-hop type ' */ + +static enum route_map_cmd_result_t +route_match_ipv6_next_hop_type(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) +{ + struct nh_rmap_obj *nh_data; + + if (type == RMAP_ZEBRA && prefix->family == AF_INET6) { + nh_data = (struct nh_rmap_obj *)object; + if (!nh_data) + return RMAP_NOMATCH; + + if (nh_data->nexthop->type == NEXTHOP_TYPE_BLACKHOLE) + return RMAP_MATCH; + } + return RMAP_NOMATCH; +} + +static void *route_match_ipv6_next_hop_type_compile(const char *arg) +{ + return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg); +} + +static void route_match_ipv6_next_hop_type_free(void *rule) +{ + XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); +} + +static const struct route_map_rule_cmd + route_match_ipv6_next_hop_type_cmd = { + "ipv6 next-hop type", + route_match_ipv6_next_hop_type, + route_match_ipv6_next_hop_type_compile, + route_match_ipv6_next_hop_type_free +}; /* `match ip address prefix-len PREFIXLEN' */ -static route_map_result_t +static enum route_map_cmd_result_t route_match_address_prefix_len(void *rule, const struct prefix *prefix, route_map_object_t type, void *object) { @@ -1275,19 +1257,25 @@ static void route_match_address_prefix_len_free(void *rule) XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); } -static struct route_map_rule_cmd route_match_ip_address_prefix_len_cmd = { - "ip address prefix-len", route_match_address_prefix_len, +static const struct route_map_rule_cmd + route_match_ip_address_prefix_len_cmd = { + "ip address prefix-len", + route_match_address_prefix_len, route_match_address_prefix_len_compile, - route_match_address_prefix_len_free}; + route_match_address_prefix_len_free +}; -static struct route_map_rule_cmd route_match_ipv6_address_prefix_len_cmd = { - "ipv6 address prefix-len", route_match_address_prefix_len, +static const struct route_map_rule_cmd + route_match_ipv6_address_prefix_len_cmd = { + "ipv6 address prefix-len", + route_match_address_prefix_len, route_match_address_prefix_len_compile, - route_match_address_prefix_len_free}; + route_match_address_prefix_len_free +}; /* `match ip nexthop prefix-len PREFIXLEN' */ -static route_map_result_t +static enum route_map_cmd_result_t route_match_ip_nexthop_prefix_len(void *rule, const struct prefix *prefix, route_map_object_t type, void *object) { @@ -1298,7 +1286,7 @@ route_match_ip_nexthop_prefix_len(void *rule, const struct prefix *prefix, if (type == RMAP_ZEBRA) { nh_data = (struct nh_rmap_obj *)object; if (!nh_data || !nh_data->nexthop) - return RMAP_DENYMATCH; + return RMAP_NOMATCH; switch (nh_data->nexthop->type) { case NEXTHOP_TYPE_IFINDEX: @@ -1319,18 +1307,56 @@ route_match_ip_nexthop_prefix_len(void *rule, const struct prefix *prefix, return RMAP_NOMATCH; } -static struct route_map_rule_cmd route_match_ip_nexthop_prefix_len_cmd = { - "ip next-hop prefix-len", route_match_ip_nexthop_prefix_len, +static const struct route_map_rule_cmd + route_match_ip_nexthop_prefix_len_cmd = { + "ip next-hop prefix-len", + route_match_ip_nexthop_prefix_len, route_match_address_prefix_len_compile, /* reuse */ route_match_address_prefix_len_free /* reuse */ }; +/* `match ip next-hop type ' */ + +static enum route_map_cmd_result_t +route_match_ip_next_hop_type(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) +{ + struct nh_rmap_obj *nh_data; + + if (type == RMAP_ZEBRA && prefix->family == AF_INET) { + nh_data = (struct nh_rmap_obj *)object; + if (!nh_data) + return RMAP_NOMATCH; + + if (nh_data->nexthop->type == NEXTHOP_TYPE_BLACKHOLE) + return RMAP_MATCH; + } + return RMAP_NOMATCH; +} + +static void *route_match_ip_next_hop_type_compile(const char *arg) +{ + return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg); +} + +static void route_match_ip_next_hop_type_free(void *rule) +{ + XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); +} + +static const struct route_map_rule_cmd + route_match_ip_next_hop_type_cmd = { + "ip next-hop type", + route_match_ip_next_hop_type, + route_match_ip_next_hop_type_compile, + route_match_ip_next_hop_type_free +}; + /* `match source-protocol PROTOCOL' */ -static route_map_result_t route_match_source_protocol(void *rule, - const struct prefix *p, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_match_source_protocol(void *rule, const struct prefix *p, + route_map_object_t type, void *object) { uint32_t *rib_type = (uint32_t *)rule; struct nh_rmap_obj *nh_data; @@ -1338,7 +1364,7 @@ static route_map_result_t route_match_source_protocol(void *rule, if (type == RMAP_ZEBRA) { nh_data = (struct nh_rmap_obj *)object; if (!nh_data) - return RMAP_DENYMATCH; + return RMAP_NOMATCH; return ((nh_data->source_protocol == *rib_type) ? RMAP_MATCH : RMAP_NOMATCH); @@ -1364,15 +1390,17 @@ static void route_match_source_protocol_free(void *rule) XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); } -static struct route_map_rule_cmd route_match_source_protocol_cmd = { - "source-protocol", route_match_source_protocol, - route_match_source_protocol_compile, route_match_source_protocol_free}; +static const struct route_map_rule_cmd route_match_source_protocol_cmd = { + "source-protocol", + route_match_source_protocol, + route_match_source_protocol_compile, + route_match_source_protocol_free +}; /* `source-instance` */ -static route_map_result_t route_match_source_instance(void *rule, - const struct prefix *p, - route_map_object_t type, - void *object) +static enum route_map_cmd_result_t +route_match_source_instance(void *rule, const struct prefix *p, + route_map_object_t type, void *object) { uint8_t *instance = (uint8_t *)rule; struct nh_rmap_obj *nh_data; @@ -1382,7 +1410,7 @@ static route_map_result_t route_match_source_instance(void *rule, nh_data = (struct nh_rmap_obj *)object; if (!nh_data) - return RMAP_DENYMATCH; + return RMAP_NOMATCH; return (nh_data->instance == *instance) ? RMAP_MATCH : RMAP_NOMATCH; } @@ -1405,15 +1433,19 @@ static void route_match_source_instance_free(void *rule) XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); } -static struct route_map_rule_cmd route_match_source_instance_cmd = { - "source-instance", route_match_source_instance, - route_match_source_instance_compile, route_match_source_instance_free}; +static const struct route_map_rule_cmd route_match_source_instance_cmd = { + "source-instance", + route_match_source_instance, + route_match_source_instance_compile, + route_match_source_instance_free +}; /* `set src A.B.C.D' */ /* Set src. */ -static route_map_result_t route_set_src(void *rule, const struct prefix *prefix, - route_map_object_t type, void *object) +static enum route_map_cmd_result_t +route_set_src(void *rule, const struct prefix *prefix, route_map_object_t type, + void *object) { struct nh_rmap_obj *nh_data; @@ -1445,8 +1477,11 @@ static void route_set_src_free(void *rule) } /* Set src rule structure. */ -static struct route_map_rule_cmd route_set_src_cmd = { - "src", route_set_src, route_set_src_compile, route_set_src_free, +static const struct route_map_rule_cmd route_set_src_cmd = { + "src", + route_set_src, + route_set_src_compile, + route_set_src_free, }; /* The function checks if the changed routemap specified by parameter rmap @@ -1651,7 +1686,7 @@ static int zebra_route_map_update_timer(struct thread *thread) * 1) VRF Aware * 2) Route-map aware */ - return (0); + return 0; } static void zebra_route_map_set_delay_timer(uint32_t value) @@ -1665,6 +1700,15 @@ static void zebra_route_map_set_delay_timer(uint32_t value) } } +void zebra_routemap_finish(void) +{ + /* Set zebra_rmap_update_timer to 0 so that it wont schedule again */ + zebra_rmap_update_timer = 0; + /* Thread off if any scheduled already */ + THREAD_TIMER_OFF(zebra_t_rmap_update); + route_map_finish(); +} + void zebra_route_map_write_delay_timer(struct vty *vty) { if (vty && (zebra_rmap_update_timer != ZEBRA_RMAP_DEFAULT_UPDATE_TIMER)) @@ -1679,7 +1723,7 @@ zebra_route_map_check(int family, int rib_type, uint8_t instance, struct zebra_vrf *zvrf, route_tag_t tag) { struct route_map *rmap = NULL; - route_map_result_t ret = RMAP_MATCH; + route_map_result_t ret = RMAP_PERMITMATCH; struct nh_rmap_obj nh_obj; nh_obj.nexthop = nexthop; @@ -1751,7 +1795,7 @@ route_map_result_t zebra_nht_route_map_check(afi_t afi, int client_proto, struct nexthop *nexthop) { struct route_map *rmap = NULL; - route_map_result_t ret = RMAP_MATCH; + route_map_result_t ret = RMAP_PERMITMATCH; struct nh_rmap_obj nh_obj; nh_obj.nexthop = nexthop; @@ -1798,8 +1842,7 @@ static void zebra_route_map_delete(const char *rmap_name) route_map_notify_dependencies(rmap_name, RMAP_EVENT_MATCH_DELETED); } -static void zebra_route_map_event(route_map_event_t event, - const char *rmap_name) +static void zebra_route_map_event(const char *rmap_name) { if (route_map_mark_updated(rmap_name) == 0) zebra_route_map_mark_update(rmap_name); @@ -1817,7 +1860,7 @@ void zebra_routemap_config_write_protocol(struct vty *vty, memset(space, 0, sizeof(space)); if (zvrf_id(zvrf) != VRF_DEFAULT) - sprintf(space, "%s", " "); + snprintf(space, sizeof(space), "%s", " "); for (i = 0; i < ZEBRA_ROUTE_MAX; i++) { if (PROTO_RM_NAME(zvrf, AFI_IP, i)) @@ -1857,7 +1900,8 @@ void zebra_routemap_config_write_protocol(struct vty *vty, vty_out(vty, "%sipv6 nht %s route-map %s\n", space, "any", NHT_RM_NAME(zvrf, AFI_IP6, ZEBRA_ROUTE_MAX)); - if (zebra_rmap_update_timer != ZEBRA_RMAP_DEFAULT_UPDATE_TIMER) + if (zvrf_id(zvrf) == VRF_DEFAULT + && zebra_rmap_update_timer != ZEBRA_RMAP_DEFAULT_UPDATE_TIMER) vty_out(vty, "zebra route-map delay-timer %d\n", zebra_rmap_update_timer); } @@ -1908,6 +1952,9 @@ void zebra_route_map_init(void) route_map_match_ip_next_hop_prefix_list_hook(generic_match_add); route_map_no_match_ip_next_hop_prefix_list_hook(generic_match_delete); + route_map_match_ip_next_hop_type_hook(generic_match_add); + route_map_no_match_ip_next_hop_type_hook(generic_match_delete); + route_map_match_tag_hook(generic_match_add); route_map_no_match_tag_hook(generic_match_delete); @@ -1917,6 +1964,9 @@ void zebra_route_map_init(void) route_map_match_ipv6_address_prefix_list_hook(generic_match_add); route_map_no_match_ipv6_address_prefix_list_hook(generic_match_delete); + route_map_match_ipv6_next_hop_type_hook(generic_match_add); + route_map_no_match_ipv6_next_hop_type_hook(generic_match_delete); + route_map_install_match(&route_match_tag_cmd); route_map_install_match(&route_match_interface_cmd); route_map_install_match(&route_match_ip_next_hop_cmd); @@ -1928,6 +1978,8 @@ void zebra_route_map_init(void) route_map_install_match(&route_match_ip_address_prefix_len_cmd); route_map_install_match(&route_match_ipv6_address_prefix_len_cmd); route_map_install_match(&route_match_ip_nexthop_prefix_len_cmd); + route_map_install_match(&route_match_ip_next_hop_type_cmd); + route_map_install_match(&route_match_ipv6_next_hop_type_cmd); route_map_install_match(&route_match_source_protocol_cmd); route_map_install_match(&route_match_source_instance_cmd); diff --git a/zebra/zebra_routemap.h b/zebra/zebra_routemap.h index 6a630e1ac0..56e805ea03 100644 --- a/zebra/zebra_routemap.h +++ b/zebra/zebra_routemap.h @@ -56,4 +56,5 @@ zebra_nht_route_map_check(afi_t afi, int client_proto, const struct prefix *p, } #endif +extern void zebra_routemap_finish(void); #endif diff --git a/zebra/zebra_router.c b/zebra/zebra_router.c index a81752d205..66f2924555 100644 --- a/zebra/zebra_router.c +++ b/zebra/zebra_router.c @@ -29,8 +29,15 @@ #include "zebra_pbr.h" #include "zebra_vxlan.h" #include "zebra_mlag.h" +#include "zebra_nhg.h" +#include "debug.h" -struct zebra_router zrouter; +DEFINE_MTYPE_STATIC(ZEBRA, RIB_TABLE_INFO, "RIB table info") + +struct zebra_router zrouter = { + .multipath_num = MULTIPATH_NUM, + .ipv4_multicast_mode = MCAST_NO_CONFIG, +}; static inline int zebra_router_table_entry_compare(const struct zebra_router_table *e1, @@ -59,6 +66,22 @@ zebra_router_table_entry_compare(const struct zebra_router_table *e1, return (e1->safi - e2->safi); } +struct zebra_router_table *zebra_router_find_zrt(struct zebra_vrf *zvrf, + uint32_t tableid, afi_t afi, + safi_t safi) +{ + struct zebra_router_table finder; + struct zebra_router_table *zrt; + + memset(&finder, 0, sizeof(finder)); + finder.afi = afi; + finder.safi = safi; + finder.tableid = tableid; + finder.ns_id = zvrf->zns->ns_id; + zrt = RB_FIND(zebra_router_table_head, &zrouter.tables, &finder); + + return zrt; +} struct route_table *zebra_router_find_table(struct zebra_vrf *zvrf, uint32_t tableid, afi_t afi, @@ -86,7 +109,7 @@ struct route_table *zebra_router_get_table(struct zebra_vrf *zvrf, { struct zebra_router_table finder; struct zebra_router_table *zrt; - rib_table_info_t *info; + struct rib_table_info *info; memset(&finder, 0, sizeof(finder)); finder.afi = afi; @@ -110,6 +133,7 @@ struct route_table *zebra_router_get_table(struct zebra_vrf *zvrf, info->zvrf = zvrf; info->afi = afi; info->safi = safi; + info->table_id = tableid; route_table_set_info(zrt->table, info); zrt->table->cleanup = zebra_rtable_node_cleanup; @@ -117,19 +141,6 @@ struct route_table *zebra_router_get_table(struct zebra_vrf *zvrf, return zrt->table; } -unsigned long zebra_router_score_proto(uint8_t proto, unsigned short instance) -{ - struct zebra_router_table *zrt; - unsigned long cnt = 0; - - RB_FOREACH (zrt, zebra_router_table_head, &zrouter.tables) { - if (zrt->ns_id != NS_DEFAULT) - continue; - cnt += rib_score_proto_table(proto, instance, zrt->table); - } - return cnt; -} - void zebra_router_show_table_summary(struct vty *vty) { struct zebra_router_table *zrt; @@ -139,7 +150,7 @@ void zebra_router_show_table_summary(struct vty *vty) vty_out(vty, "---------------------------------------------------------------------------\n"); RB_FOREACH (zrt, zebra_router_table_head, &zrouter.tables) { - rib_table_info_t *info = route_table_get_info(zrt->table); + struct rib_table_info *info = route_table_get_info(zrt->table); vty_out(vty, "%-16s%5d %9d %7s %15s %8d %10lu\n", info->zvrf->vrf->name, zrt->ns_id, info->zvrf->vrf->vrf_id, @@ -160,6 +171,11 @@ void zebra_router_sweep_route(void) } } +void zebra_router_sweep_nhgs(void) +{ + zebra_nhg_sweep_table(zrouter.nhgs_id); +} + static void zebra_router_free_table(struct zebra_router_table *zrt) { void *table_info; @@ -198,6 +214,19 @@ uint32_t zebra_router_get_next_sequence(void) memory_order_relaxed); } +void multicast_mode_ipv4_set(enum multicast_mode mode) +{ + if (IS_ZEBRA_DEBUG_RIB) + zlog_debug("%s: multicast lookup mode set (%d)", __func__, + mode); + zrouter.ipv4_multicast_mode = mode; +} + +enum multicast_mode multicast_mode_ipv4_get(void) +{ + return zrouter.ipv4_multicast_mode; +} + void zebra_router_terminate(void) { struct zebra_router_table *zrt, *tmp; @@ -211,6 +240,12 @@ void zebra_router_terminate(void) zebra_vxlan_disable(); zebra_mlag_terminate(); + /* Free NHE in ID table only since it has unhashable entries as well */ + hash_clean(zrouter.nhgs_id, zebra_nhg_hash_free); + hash_free(zrouter.nhgs_id); + hash_clean(zrouter.nhgs, NULL); + hash_free(zrouter.nhgs); + hash_clean(zrouter.rules_hash, zebra_pbr_rules_free); hash_free(zrouter.rules_hash); @@ -226,9 +261,10 @@ void zebra_router_init(void) { zrouter.sequence_num = 0; - zrouter.rtm_table_default = 0; zrouter.packets_to_process = ZEBRA_ZAPI_PACKETS_TO_PROCESS; + zrouter.rtadv_sock = -1; + zebra_vxlan_init(); zebra_mlag_init(); @@ -247,4 +283,11 @@ void zebra_router_init(void) zrouter.iptable_hash = hash_create_size(8, zebra_pbr_iptable_hash_key, zebra_pbr_iptable_hash_equal, "IPtable Hash Entry"); + + zrouter.nhgs = + hash_create_size(8, zebra_nhg_hash_key, zebra_nhg_hash_equal, + "Zebra Router Nexthop Groups"); + zrouter.nhgs_id = + hash_create_size(8, zebra_nhg_id_key, zebra_nhg_hash_id_equal, + "Zebra Router Nexthop Groups ID index"); } diff --git a/zebra/zebra_router.h b/zebra/zebra_router.h index 72b5e9b9b1..f73a8f2d59 100644 --- a/zebra/zebra_router.h +++ b/zebra/zebra_router.h @@ -50,6 +50,17 @@ RB_HEAD(zebra_router_table_head, zebra_router_table); RB_PROTOTYPE(zebra_router_table_head, zebra_router_table, zebra_router_table_entry, zebra_router_table_entry_compare) +/* RPF lookup behaviour */ +enum multicast_mode { + MCAST_NO_CONFIG = 0, /* MIX_MRIB_FIRST, but no show in config write */ + MCAST_MRIB_ONLY, /* MRIB only */ + MCAST_URIB_ONLY, /* URIB only */ + MCAST_MIX_MRIB_FIRST, /* MRIB, if nothing at all then URIB */ + MCAST_MIX_DISTANCE, /* MRIB & URIB, lower distance wins */ + MCAST_MIX_PFXLEN, /* MRIB & URIB, longer prefix wins */ + /* on equal value, MRIB wins for last 2 */ +}; + struct zebra_mlag_info { /* Role this zebra router is playing */ enum mlag_role role; @@ -60,20 +71,66 @@ struct zebra_mlag_info { /* The system mac being used */ struct ethaddr mac; + /* + * Zebra will open the communication channel with MLAGD only if any + * clients are interested and it is controlled dynamically based on + * client registers & un-registers. + */ + uint32_t clients_interested_cnt; + + /* coomunication channel with MLAGD is established */ + bool connected; + + /* connection retry timer is running */ + bool timer_running; + + /* Holds the client data(unencoded) that need to be pushed to MCLAGD*/ + struct stream_fifo *mlag_fifo; + + /* + * A new Kernel thread will be created to post the data to MCLAGD. + * where as, read will be performed from the zebra main thread, because + * read involves accessing client registartion data structures. + */ + struct frr_pthread *zebra_pth_mlag; + + /* MLAG Thread context 'master' */ + struct thread_master *th_master; + + /* + * Event for Initial MLAG Connection setup & Data Read + * Read can be performed only after successful connection establishment, + * so no issues. + * + */ + struct thread *t_read; + /* Event for MLAG write */ + struct thread *t_write; }; struct zebra_router { + atomic_bool in_shutdown; + /* Thread master */ struct thread_master *master; /* Lists of clients who have connected to us */ struct list *client_list; + /* List of clients in GR */ + struct list *stale_client_list; + struct zebra_router_table_head tables; /* L3-VNI hash table (for EVPN). Only in default instance */ struct hash *l3vni_table; + /* Tables and other global info maintained for EVPN multihoming */ + struct zebra_evpn_mh_info *mh_info; + + /* EVPN MH broadcast domains indexed by the VID */ + struct hash *evpn_vlan_table; + struct hash *rules_hash; struct hash *ipset_hash; @@ -82,16 +139,12 @@ struct zebra_router { struct hash *iptable_hash; -#if defined(HAVE_RTADV) - struct rtadv rtadv; -#endif /* HAVE_RTADV */ + /* used if vrf backend is not network namespace */ + int rtadv_sock; /* A sequence number used for tracking routes */ _Atomic uint32_t sequence_num; - /* The default table used for this router */ - uint32_t rtm_table_default; - /* rib work queue */ #define ZEBRA_RIB_PROCESS_HOLD_TIME 10 #define ZEBRA_RIB_PROCESS_RETRY_TIME 1 @@ -113,13 +166,35 @@ struct zebra_router { * The EVPN instance, if any */ struct zebra_vrf *evpn_vrf; + + uint32_t multipath_num; + + /* RPF Lookup behavior */ + enum multicast_mode ipv4_multicast_mode; + + /* + * Time for when we sweep the rib from old routes + */ + time_t startup_time; + + /* + * The hash of nexthop groups associated with this router + */ + struct hash *nhgs; + struct hash *nhgs_id; }; +#define GRACEFUL_RESTART_TIME 60 + extern struct zebra_router zrouter; extern void zebra_router_init(void); +extern void zebra_router_cleanup(void); extern void zebra_router_terminate(void); +extern struct zebra_router_table *zebra_router_find_zrt(struct zebra_vrf *zvrf, + uint32_t tableid, + afi_t afi, safi_t safi); extern struct route_table *zebra_router_find_table(struct zebra_vrf *zvrf, uint32_t tableid, afi_t afi, safi_t safi); @@ -131,9 +206,8 @@ extern void zebra_router_release_table(struct zebra_vrf *zvrf, uint32_t tableid, extern int zebra_router_config_write(struct vty *vty); -extern unsigned long zebra_router_score_proto(uint8_t proto, - unsigned short instance); extern void zebra_router_sweep_route(void); +extern void zebra_router_sweep_nhgs(void); extern void zebra_router_show_table_summary(struct vty *vty); @@ -149,6 +223,13 @@ static inline struct zebra_vrf *zebra_vrf_get_evpn(void) : zebra_vrf_lookup_by_id(VRF_DEFAULT); } +extern void multicast_mode_ipv4_set(enum multicast_mode mode); + +extern enum multicast_mode multicast_mode_ipv4_get(void); + +/* zebra_northbound.c */ +extern const struct frr_yang_module_info frr_zebra_info; + #ifdef __cplusplus } #endif diff --git a/zebra/zebra_snmp.c b/zebra/zebra_snmp.c index 74eab765c8..89b8238c29 100644 --- a/zebra/zebra_snmp.c +++ b/zebra/zebra_snmp.c @@ -266,9 +266,9 @@ static void check_replace(struct route_node *np2, struct route_entry *re2, return; } - if (in_addr_cmp(&(*np)->p.u.prefix, &np2->p.u.prefix) < 0) + if (prefix_cmp(&(*np)->p, &np2->p) < 0) return; - if (in_addr_cmp(&(*np)->p.u.prefix, &np2->p.u.prefix) > 0) { + if (prefix_cmp(&(*np)->p, &np2->p) > 0) { *np = np2; *re = re2; return; @@ -285,8 +285,8 @@ static void check_replace(struct route_node *np2, struct route_entry *re2, return; } - if (in_addr_cmp((uint8_t *)&(*re)->ng.nexthop->gate.ipv4, - (uint8_t *)&re2->ng.nexthop->gate.ipv4) + if (in_addr_cmp((uint8_t *)&(*re)->nhe->nhg.nexthop->gate.ipv4, + (uint8_t *)&re2->nhe->nhg.nexthop->gate.ipv4) <= 0) return; @@ -371,9 +371,9 @@ static void get_fwtable_route_node(struct variable *v, oid objid[], if (!in_addr_cmp(&(*np)->p.u.prefix, (uint8_t *)&dest)) { RNODE_FOREACH_RE (*np, *re) { - if (!in_addr_cmp((uint8_t *)&(*re) - ->ng.nexthop - ->gate.ipv4, + if (!in_addr_cmp((uint8_t *)&(*re)->nhe + ->nhg.nexthop + ->gate.ipv4, (uint8_t *)&nexthop)) if (proto == proto_trans((*re)->type)) @@ -406,8 +406,8 @@ static void get_fwtable_route_node(struct variable *v, oid objid[], || ((policy == policy2) && (proto < proto2)) || ((policy == policy2) && (proto == proto2) && (in_addr_cmp( - (uint8_t *)&re2->ng.nexthop - ->gate.ipv4, + (uint8_t *)&re2->nhe + ->nhg.nexthop->gate.ipv4, (uint8_t *)&nexthop) >= 0))) check_replace(np2, re2, np, re); @@ -432,7 +432,7 @@ static void get_fwtable_route_node(struct variable *v, oid objid[], { struct nexthop *nexthop; - nexthop = (*re)->ng.nexthop; + nexthop = (*re)->nhe->nhg.nexthop; if (nexthop) { pnt = (uint8_t *)&nexthop->gate.ipv4; for (i = 0; i < 4; i++) @@ -462,7 +462,7 @@ static uint8_t *ipFwTable(struct variable *v, oid objid[], size_t *objid_len, if (!np) return NULL; - nexthop = re->ng.nexthop; + nexthop = re->nhe->nhg.nexthop; if (!nexthop) return NULL; @@ -470,25 +470,20 @@ static uint8_t *ipFwTable(struct variable *v, oid objid[], size_t *objid_len, case IPFORWARDDEST: *val_len = 4; return &np->p.u.prefix; - break; case IPFORWARDMASK: masklen2ip(np->p.prefixlen, &netmask); *val_len = 4; return (uint8_t *)&netmask; - break; case IPFORWARDPOLICY: result = 0; *val_len = sizeof(int); return (uint8_t *)&result; - break; case IPFORWARDNEXTHOP: *val_len = 4; return (uint8_t *)&nexthop->gate.ipv4; - break; case IPFORWARDIFINDEX: *val_len = sizeof(int); return (uint8_t *)&nexthop->ifindex; - break; case IPFORWARDTYPE: if (nexthop->type == NEXTHOP_TYPE_IFINDEX) result = 3; @@ -496,56 +491,45 @@ static uint8_t *ipFwTable(struct variable *v, oid objid[], size_t *objid_len, result = 4; *val_len = sizeof(int); return (uint8_t *)&result; - break; case IPFORWARDPROTO: result = proto_trans(re->type); *val_len = sizeof(int); return (uint8_t *)&result; - break; case IPFORWARDAGE: result = 0; *val_len = sizeof(int); return (uint8_t *)&result; - break; case IPFORWARDINFO: resarr[0] = 0; resarr[1] = 0; *val_len = 2 * sizeof(int); return (uint8_t *)resarr; - break; case IPFORWARDNEXTHOPAS: result = -1; *val_len = sizeof(int); return (uint8_t *)&result; - break; case IPFORWARDMETRIC1: result = 0; *val_len = sizeof(int); return (uint8_t *)&result; - break; case IPFORWARDMETRIC2: result = 0; *val_len = sizeof(int); return (uint8_t *)&result; - break; case IPFORWARDMETRIC3: result = 0; *val_len = sizeof(int); return (uint8_t *)&result; - break; case IPFORWARDMETRIC4: result = 0; *val_len = sizeof(int); return (uint8_t *)&result; - break; case IPFORWARDMETRIC5: result = 0; *val_len = sizeof(int); return (uint8_t *)&result; - break; default: return NULL; - break; } return NULL; } @@ -563,7 +547,6 @@ static uint8_t *ipCidrTable(struct variable *v, oid objid[], size_t *objid_len, break; default: return NULL; - break; } return NULL; } diff --git a/zebra/zebra_srte.c b/zebra/zebra_srte.c new file mode 100644 index 0000000000..d6043534e3 --- /dev/null +++ b/zebra/zebra_srte.c @@ -0,0 +1,378 @@ +/* Zebra SR-TE code + * Copyright (C) 2020 NetDEF, Inc. + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "lib/zclient.h" +#include "lib/lib_errors.h" + +#include "zebra/zebra_srte.h" +#include "zebra/zebra_memory.h" +#include "zebra/zebra_mpls.h" +#include "zebra/zebra_rnh.h" +#include "zebra/zapi_msg.h" + +DEFINE_MTYPE_STATIC(ZEBRA, ZEBRA_SR_POLICY, "SR Policy") + +static void zebra_sr_policy_deactivate(struct zebra_sr_policy *policy); + +/* Generate rb-tree of SR Policy instances. */ +static inline int +zebra_sr_policy_instance_compare(const struct zebra_sr_policy *a, + const struct zebra_sr_policy *b) +{ + return sr_policy_compare(&a->endpoint, &b->endpoint, a->color, + b->color); +} +RB_GENERATE(zebra_sr_policy_instance_head, zebra_sr_policy, entry, + zebra_sr_policy_instance_compare) + +struct zebra_sr_policy_instance_head zebra_sr_policy_instances = + RB_INITIALIZER(&zebra_sr_policy_instances); + +struct zebra_sr_policy *zebra_sr_policy_add(uint32_t color, + struct ipaddr *endpoint, char *name) +{ + struct zebra_sr_policy *policy; + + policy = XCALLOC(MTYPE_ZEBRA_SR_POLICY, sizeof(*policy)); + policy->color = color; + policy->endpoint = *endpoint; + strlcpy(policy->name, name, sizeof(policy->name)); + policy->status = ZEBRA_SR_POLICY_DOWN; + RB_INSERT(zebra_sr_policy_instance_head, &zebra_sr_policy_instances, + policy); + + return policy; +} + +void zebra_sr_policy_del(struct zebra_sr_policy *policy) +{ + if (policy->status == ZEBRA_SR_POLICY_UP) + zebra_sr_policy_deactivate(policy); + RB_REMOVE(zebra_sr_policy_instance_head, &zebra_sr_policy_instances, + policy); + XFREE(MTYPE_ZEBRA_SR_POLICY, policy); +} + +struct zebra_sr_policy *zebra_sr_policy_find(uint32_t color, + struct ipaddr *endpoint) +{ + struct zebra_sr_policy policy = {}; + + policy.color = color; + policy.endpoint = *endpoint; + return RB_FIND(zebra_sr_policy_instance_head, + &zebra_sr_policy_instances, &policy); +} + +struct zebra_sr_policy *zebra_sr_policy_find_by_name(char *name) +{ + struct zebra_sr_policy *policy; + + // TODO: create index for policy names + RB_FOREACH (policy, zebra_sr_policy_instance_head, + &zebra_sr_policy_instances) { + if (strcmp(policy->name, name) == 0) + return policy; + } + + return NULL; +} + +static int zebra_sr_policy_notify_update_client(struct zebra_sr_policy *policy, + struct zserv *client) +{ + const zebra_nhlfe_t *nhlfe; + struct stream *s; + uint32_t message = 0; + unsigned long nump = 0; + uint8_t num; + struct zapi_nexthop znh; + int ret; + + /* Get output stream. */ + s = stream_new(ZEBRA_MAX_PACKET_SIZ); + + zclient_create_header(s, ZEBRA_NEXTHOP_UPDATE, zvrf_id(policy->zvrf)); + + /* Message flags. */ + SET_FLAG(message, ZAPI_MESSAGE_SRTE); + stream_putl(s, message); + + switch (policy->endpoint.ipa_type) { + case IPADDR_V4: + stream_putw(s, AF_INET); + stream_putc(s, IPV4_MAX_BITLEN); + stream_put_in_addr(s, &policy->endpoint.ipaddr_v4); + break; + case IPADDR_V6: + stream_putw(s, AF_INET6); + stream_putc(s, IPV6_MAX_BITLEN); + stream_put(s, &policy->endpoint.ipaddr_v6, IPV6_MAX_BYTELEN); + break; + default: + flog_warn(EC_LIB_DEVELOPMENT, + "%s: unknown policy endpoint address family: %u", + __func__, policy->endpoint.ipa_type); + exit(1); + } + stream_putl(s, policy->color); + + num = 0; + frr_each (nhlfe_list_const, &policy->lsp->nhlfe_list, nhlfe) { + if (!CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_SELECTED) + || CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_DELETED)) + continue; + + if (num == 0) { + stream_putc(s, re_type_from_lsp_type(nhlfe->type)); + stream_putw(s, 0); /* instance - not available */ + stream_putc(s, nhlfe->distance); + stream_putl(s, 0); /* metric - not available */ + nump = stream_get_endp(s); + stream_putc(s, 0); + } + + zapi_nexthop_from_nexthop(&znh, nhlfe->nexthop); + ret = zapi_nexthop_encode(s, &znh, 0, message); + if (ret < 0) + goto failure; + + num++; + } + stream_putc_at(s, nump, num); + stream_putw_at(s, 0, stream_get_endp(s)); + + client->nh_last_upd_time = monotime(NULL); + client->last_write_cmd = ZEBRA_NEXTHOP_UPDATE; + return zserv_send_message(client, s); + +failure: + + stream_free(s); + return -1; +} + +static void zebra_sr_policy_notify_update(struct zebra_sr_policy *policy) +{ + struct rnh *rnh; + struct prefix p = {}; + struct zebra_vrf *zvrf; + struct listnode *node; + struct zserv *client; + + zvrf = policy->zvrf; + switch (policy->endpoint.ipa_type) { + case IPADDR_V4: + p.family = AF_INET; + p.prefixlen = IPV4_MAX_BITLEN; + p.u.prefix4 = policy->endpoint.ipaddr_v4; + break; + case IPADDR_V6: + p.family = AF_INET6; + p.prefixlen = IPV6_MAX_BITLEN; + p.u.prefix6 = policy->endpoint.ipaddr_v6; + break; + default: + flog_warn(EC_LIB_DEVELOPMENT, + "%s: unknown policy endpoint address family: %u", + __func__, policy->endpoint.ipa_type); + exit(1); + } + + rnh = zebra_lookup_rnh(&p, zvrf_id(zvrf), RNH_NEXTHOP_TYPE); + if (!rnh) + return; + + for (ALL_LIST_ELEMENTS_RO(rnh->client_list, node, client)) { + if (policy->status == ZEBRA_SR_POLICY_UP) + zebra_sr_policy_notify_update_client(policy, client); + else + /* Fallback to the IGP shortest path. */ + zebra_send_rnh_update(rnh, client, RNH_NEXTHOP_TYPE, + zvrf_id(zvrf), policy->color); + } +} + +static void zebra_sr_policy_activate(struct zebra_sr_policy *policy, + zebra_lsp_t *lsp) +{ + policy->status = ZEBRA_SR_POLICY_UP; + policy->lsp = lsp; + (void)zebra_sr_policy_bsid_install(policy); + zsend_sr_policy_notify_status(policy->color, &policy->endpoint, + policy->name, ZEBRA_SR_POLICY_UP); + zebra_sr_policy_notify_update(policy); +} + +static void zebra_sr_policy_update(struct zebra_sr_policy *policy, + zebra_lsp_t *lsp, + struct zapi_srte_tunnel *old_tunnel) +{ + bool bsid_changed; + bool segment_list_changed; + + policy->lsp = lsp; + + bsid_changed = + policy->segment_list.local_label != old_tunnel->local_label; + segment_list_changed = + policy->segment_list.label_num != old_tunnel->label_num + || memcmp(policy->segment_list.labels, old_tunnel->labels, + sizeof(mpls_label_t) + * policy->segment_list.label_num); + + /* Re-install label stack if necessary. */ + if (bsid_changed || segment_list_changed) { + zebra_sr_policy_bsid_uninstall(policy, old_tunnel->local_label); + (void)zebra_sr_policy_bsid_install(policy); + } + + zsend_sr_policy_notify_status(policy->color, &policy->endpoint, + policy->name, ZEBRA_SR_POLICY_UP); + + /* Handle segment-list update. */ + if (segment_list_changed) + zebra_sr_policy_notify_update(policy); +} + +static void zebra_sr_policy_deactivate(struct zebra_sr_policy *policy) +{ + policy->status = ZEBRA_SR_POLICY_DOWN; + policy->lsp = NULL; + zebra_sr_policy_bsid_uninstall(policy, + policy->segment_list.local_label); + zsend_sr_policy_notify_status(policy->color, &policy->endpoint, + policy->name, ZEBRA_SR_POLICY_DOWN); + zebra_sr_policy_notify_update(policy); +} + +int zebra_sr_policy_validate(struct zebra_sr_policy *policy, + struct zapi_srte_tunnel *new_tunnel) +{ + struct zapi_srte_tunnel old_tunnel = policy->segment_list; + zebra_lsp_t *lsp; + + if (new_tunnel) + policy->segment_list = *new_tunnel; + + /* Try to resolve the Binding-SID nexthops. */ + lsp = mpls_lsp_find(policy->zvrf, policy->segment_list.labels[0]); + if (!lsp || !lsp->best_nhlfe + || lsp->addr_family != ipaddr_family(&policy->endpoint)) { + if (policy->status == ZEBRA_SR_POLICY_UP) + zebra_sr_policy_deactivate(policy); + return -1; + } + + /* First label was resolved successfully. */ + if (policy->status == ZEBRA_SR_POLICY_DOWN) + zebra_sr_policy_activate(policy, lsp); + else + zebra_sr_policy_update(policy, lsp, &old_tunnel); + + return 0; +} + +int zebra_sr_policy_bsid_install(struct zebra_sr_policy *policy) +{ + struct zapi_srte_tunnel *zt = &policy->segment_list; + zebra_nhlfe_t *nhlfe; + + if (zt->local_label == MPLS_LABEL_NONE) + return 0; + + frr_each_safe (nhlfe_list, &policy->lsp->nhlfe_list, nhlfe) { + uint8_t num_out_labels; + mpls_label_t *out_labels; + mpls_label_t null_label = MPLS_LABEL_IMPLICIT_NULL; + + if (!CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_SELECTED) + || CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_DELETED)) + continue; + + /* + * Don't push the first SID if the corresponding action in the + * LFIB is POP. + */ + if (!nhlfe->nexthop->nh_label + || !nhlfe->nexthop->nh_label->num_labels + || nhlfe->nexthop->nh_label->label[0] + == MPLS_LABEL_IMPLICIT_NULL) { + if (zt->label_num > 1) { + num_out_labels = zt->label_num - 1; + out_labels = &zt->labels[1]; + } else { + num_out_labels = 1; + out_labels = &null_label; + } + } else { + num_out_labels = zt->label_num; + out_labels = zt->labels; + } + + if (mpls_lsp_install( + policy->zvrf, zt->type, zt->local_label, + num_out_labels, out_labels, nhlfe->nexthop->type, + &nhlfe->nexthop->gate, nhlfe->nexthop->ifindex) + < 0) + return -1; + } + + return 0; +} + +void zebra_sr_policy_bsid_uninstall(struct zebra_sr_policy *policy, + mpls_label_t old_bsid) +{ + struct zapi_srte_tunnel *zt = &policy->segment_list; + + mpls_lsp_uninstall_all_vrf(policy->zvrf, zt->type, old_bsid); +} + +int zebra_sr_policy_label_update(mpls_label_t label, + enum zebra_sr_policy_update_label_mode mode) +{ + struct zebra_sr_policy *policy; + + RB_FOREACH (policy, zebra_sr_policy_instance_head, + &zebra_sr_policy_instances) { + mpls_label_t next_hop_label; + + next_hop_label = policy->segment_list.labels[0]; + if (next_hop_label != label) + continue; + + switch (mode) { + case ZEBRA_SR_POLICY_LABEL_CREATED: + case ZEBRA_SR_POLICY_LABEL_UPDATED: + case ZEBRA_SR_POLICY_LABEL_REMOVED: + zebra_sr_policy_validate(policy, NULL); + break; + } + } + + return 0; +} + +void zebra_srte_init(void) +{ +} diff --git a/zebra/zebra_srte.h b/zebra/zebra_srte.h new file mode 100644 index 0000000000..e5239b7b7b --- /dev/null +++ b/zebra/zebra_srte.h @@ -0,0 +1,74 @@ +/* Zebra's client header. + * Copyright (C) 2020 Netdef, Inc. + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _ZEBRA_SRTE_H +#define _ZEBRA_SRTE_H + +#include "zebra/zebra_mpls.h" + +#include "lib/zclient.h" +#include "lib/srte.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum zebra_sr_policy_update_label_mode { + ZEBRA_SR_POLICY_LABEL_CREATED = 1, + ZEBRA_SR_POLICY_LABEL_UPDATED = 2, + ZEBRA_SR_POLICY_LABEL_REMOVED = 3, +}; + +struct zebra_sr_policy { + RB_ENTRY(zebra_sr_policy) entry; + uint32_t color; + struct ipaddr endpoint; + char name[SRTE_POLICY_NAME_MAX_LENGTH]; + enum zebra_sr_policy_status status; + struct zapi_srte_tunnel segment_list; + zebra_lsp_t *lsp; + struct zebra_vrf *zvrf; +}; +RB_HEAD(zebra_sr_policy_instance_head, zebra_sr_policy); +RB_PROTOTYPE(zebra_sr_policy_instance_head, zebra_sr_policy, entry, + zebra_sr_policy_instance_compare) + +extern struct zebra_sr_policy_instance_head zebra_sr_policy_instances; + +struct zebra_sr_policy * +zebra_sr_policy_add(uint32_t color, struct ipaddr *endpoint, char *name); +void zebra_sr_policy_del(struct zebra_sr_policy *policy); +struct zebra_sr_policy *zebra_sr_policy_find(uint32_t color, + struct ipaddr *endpoint); +struct zebra_sr_policy *zebra_sr_policy_find_by_name(char *name); +int zebra_sr_policy_validate(struct zebra_sr_policy *policy, + struct zapi_srte_tunnel *new_tunnel); +int zebra_sr_policy_bsid_install(struct zebra_sr_policy *policy); +void zebra_sr_policy_bsid_uninstall(struct zebra_sr_policy *policy, + mpls_label_t old_bsid); +void zebra_srte_init(void); +int zebra_sr_policy_label_update(mpls_label_t label, + enum zebra_sr_policy_update_label_mode mode); + +#ifdef __cplusplus +} +#endif + +#endif /* _ZEBRA_SRTE_H */ diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c index 2d721ec8a1..98ed29a641 100644 --- a/zebra/zebra_vrf.c +++ b/zebra/zebra_vrf.c @@ -29,6 +29,7 @@ #include "vty.h" #include "zebra/zebra_router.h" +#include "zebra/rtadv.h" #include "zebra/debug.h" #include "zebra/zapi_msg.h" #include "zebra/rib.h" @@ -47,6 +48,9 @@ static void zebra_vrf_table_create(struct zebra_vrf *zvrf, afi_t afi, static void zebra_rnhtable_node_cleanup(struct route_table *table, struct route_node *node); +DEFINE_MTYPE_STATIC(ZEBRA, ZEBRA_VRF, "ZEBRA VRF") +DEFINE_MTYPE_STATIC(ZEBRA, OTHER_TABLE, "Other Table") + /* VRF information update. */ static void zebra_vrf_add_update(struct zebra_vrf *zvrf) { @@ -56,8 +60,13 @@ static void zebra_vrf_add_update(struct zebra_vrf *zvrf) if (IS_ZEBRA_DEBUG_EVENT) zlog_debug("MESSAGE: ZEBRA_VRF_ADD %s", zvrf_name(zvrf)); - for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) + for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) { + /* Do not send unsolicited messages to synchronous clients. */ + if (client->synchronous) + continue; + zsend_vrf_add(client, zvrf); + } } static void zebra_vrf_delete_update(struct zebra_vrf *zvrf) @@ -68,8 +77,13 @@ static void zebra_vrf_delete_update(struct zebra_vrf *zvrf) if (IS_ZEBRA_DEBUG_EVENT) zlog_debug("MESSAGE: ZEBRA_VRF_DELETE %s", zvrf_name(zvrf)); - for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) + for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) { + /* Do not send unsolicited messages to synchronous clients. */ + if (client->synchronous) + continue; + zsend_vrf_delete(client, zvrf); + } } void zebra_vrf_update_all(struct zserv *client) @@ -88,11 +102,16 @@ static int zebra_vrf_new(struct vrf *vrf) struct zebra_vrf *zvrf; if (IS_ZEBRA_DEBUG_EVENT) - zlog_info("VRF %s created, id %u", vrf->name, vrf->vrf_id); + zlog_debug("VRF %s created, id %u", vrf->name, vrf->vrf_id); zvrf = zebra_vrf_alloc(); vrf->info = zvrf; zvrf->vrf = vrf; + if (!vrf_is_backend_netns()) + zvrf->zns = zebra_ns_lookup(NS_DEFAULT); + + otable_init(&zvrf->other_tables); + router_id_init(zvrf); return 0; } @@ -114,6 +133,10 @@ static int zebra_vrf_enable(struct vrf *vrf) zvrf->zns = zebra_ns_lookup((ns_id_t)vrf->vrf_id); else zvrf->zns = zebra_ns_lookup(NS_DEFAULT); +#if defined(HAVE_RTADV) + rtadv_init(zvrf); +#endif + /* Inform clients that the VRF is now active. This is an * add for the clients. */ @@ -156,6 +179,10 @@ static int zebra_vrf_disable(struct vrf *vrf) /* Stop any VxLAN-EVPN processing. */ zebra_vxlan_vrf_disable(zvrf); +#if defined(HAVE_RTADV) + rtadv_vrf_terminate(zvrf); +#endif + /* Inform clients that the VRF is now inactive. This is a * delete for the clients. */ @@ -226,6 +253,7 @@ static int zebra_vrf_disable(struct vrf *vrf) static int zebra_vrf_delete(struct vrf *vrf) { struct zebra_vrf *zvrf = vrf->info; + struct other_route_table *otable; struct route_table *table; afi_t afi; safi_t safi; @@ -274,11 +302,25 @@ static int zebra_vrf_delete(struct vrf *vrf) route_table_finish(zvrf->import_check_table[afi]); } + otable = otable_pop(&zvrf->other_tables); + while (otable) { + zebra_router_release_table(zvrf, otable->table_id, + otable->afi, otable->safi); + XFREE(MTYPE_OTHER_TABLE, otable); + + otable = otable_pop(&zvrf->other_tables); + } + /* Cleanup EVPN states for vrf */ zebra_vxlan_vrf_delete(zvrf); list_delete_all_node(zvrf->rid_all_sorted_list); list_delete_all_node(zvrf->rid_lo_sorted_list); + + list_delete_all_node(zvrf->rid6_all_sorted_list); + list_delete_all_node(zvrf->rid6_lo_sorted_list); + + otable_fini(&zvrf->other_tables); XFREE(MTYPE_ZEBRA_VRF, zvrf); vrf->info = NULL; @@ -297,51 +339,63 @@ static int zebra_vrf_update(struct vrf *vrf) return 0; } - -/* Return if this VRF has any FRR configuration or not. - * IMPORTANT: This function needs to be updated when additional configuration - * is added for a VRF. - */ -int zebra_vrf_has_config(struct zebra_vrf *zvrf) -{ - /* EVPN L3-VNI? */ - if (zvrf->l3vni) - return 1; - - return 0; -} - /* Lookup the routing table in a VRF based on both VRF-Id and table-id. * NOTE: Table-id is relevant on two modes: * - case VRF backend is default : on default VRF only * - case VRF backend is netns : on all VRFs */ -struct route_table *zebra_vrf_table_with_table_id(afi_t afi, safi_t safi, - vrf_id_t vrf_id, - uint32_t table_id) +struct route_table *zebra_vrf_lookup_table_with_table_id(afi_t afi, safi_t safi, + vrf_id_t vrf_id, + uint32_t table_id) { - struct route_table *table = NULL; + struct zebra_vrf *zvrf = vrf_info_lookup(vrf_id); + struct other_route_table ort, *otable; + + if (!zvrf) + return NULL; if (afi >= AFI_MAX || safi >= SAFI_MAX) return NULL; - if (vrf_id == VRF_DEFAULT) { - if (table_id == RT_TABLE_MAIN - || table_id == zrouter.rtm_table_default) - table = zebra_vrf_table(afi, safi, vrf_id); - else - table = zebra_vrf_other_route_table(afi, table_id, - vrf_id); - } else if (vrf_is_backend_netns()) { - if (table_id == RT_TABLE_MAIN - || table_id == zrouter.rtm_table_default) - table = zebra_vrf_table(afi, safi, vrf_id); - else - table = zebra_vrf_other_route_table(afi, table_id, - vrf_id); - } else - table = zebra_vrf_table(afi, safi, vrf_id); + if (table_id == zvrf->table_id) + return zebra_vrf_table(afi, safi, vrf_id); + + ort.afi = afi; + ort.safi = safi; + ort.table_id = table_id; + otable = otable_find(&zvrf->other_tables, &ort); + + if (otable) + return otable->table; + return NULL; +} + +struct route_table *zebra_vrf_get_table_with_table_id(afi_t afi, safi_t safi, + vrf_id_t vrf_id, + uint32_t table_id) +{ + struct zebra_vrf *zvrf = vrf_info_lookup(vrf_id); + struct other_route_table *otable; + struct route_table *table; + + table = zebra_vrf_lookup_table_with_table_id(afi, safi, vrf_id, + table_id); + + if (table) + goto done; + + /* Create it as an `other` table */ + table = zebra_router_get_table(zvrf, table_id, afi, safi); + + otable = XCALLOC(MTYPE_OTHER_TABLE, sizeof(*otable)); + otable->afi = afi; + otable->safi = safi; + otable->table_id = table_id; + otable->table = table; + otable_add(&zvrf->other_tables, otable); + +done: return table; } @@ -357,7 +411,7 @@ void zebra_rtable_node_cleanup(struct route_table *table, if (node->info) { rib_dest_t *dest = node->info; - list_delete(&dest->nht); + rnh_list_fini(&dest->nht); XFREE(MTYPE_RIB_DEST, node->info); } } @@ -440,34 +494,6 @@ struct route_table *zebra_vrf_table(afi_t afi, safi_t safi, vrf_id_t vrf_id) return zvrf->table[afi][safi]; } -struct route_table *zebra_vrf_other_route_table(afi_t afi, uint32_t table_id, - vrf_id_t vrf_id) -{ - struct zebra_vrf *zvrf; - - zvrf = vrf_info_lookup(vrf_id); - if (!zvrf) - return NULL; - - if (afi >= AFI_MAX) - return NULL; - - if ((table_id != RT_TABLE_MAIN) - && (table_id != zrouter.rtm_table_default)) { - if (zvrf->table_id == RT_TABLE_MAIN || - zvrf->table_id == zrouter.rtm_table_default) { - /* this VRF use default table - * so in all cases, it does not use specific table - * so it is possible to configure tables in this VRF - */ - return zebra_router_get_table(zvrf, table_id, afi, - SAFI_UNICAST); - } - } - - return zvrf->table[afi][SAFI_UNICAST]; -} - static int vrf_config_write(struct vty *vty) { struct vrf *vrf; @@ -481,7 +507,16 @@ static int vrf_config_write(struct vty *vty) if (zvrf_id(zvrf) == VRF_DEFAULT) { if (zvrf->l3vni) - vty_out(vty, "vni %u\n", zvrf->l3vni); + vty_out(vty, "vni %u%s\n", zvrf->l3vni, + is_l3vni_for_prefix_routes_only( + zvrf->l3vni) + ? " prefix-routes-only" + : ""); + if (zvrf->zebra_rnh_ip_default_route) + vty_out(vty, "ip nht resolve-via-default\n"); + + if (zvrf->zebra_rnh_ipv6_default_route) + vty_out(vty, "ipv6 nht resolve-via-default\n"); } else { vty_frame(vty, "vrf %s\n", zvrf_name(zvrf)); if (zvrf->l3vni) @@ -491,9 +526,16 @@ static int vrf_config_write(struct vty *vty) ? " prefix-routes-only" : ""); zebra_ns_config_write(vty, (struct ns *)vrf->ns_ctxt); + if (zvrf->zebra_rnh_ip_default_route) + vty_out(vty, " ip nht resolve-via-default\n"); + + if (zvrf->zebra_rnh_ipv6_default_route) + vty_out(vty, " ipv6 nht resolve-via-default\n"); } + zebra_routemap_config_write_protocol(vty, zvrf); + router_id_write(vty, zvrf); if (zvrf_id(zvrf) != VRF_DEFAULT) vty_endframe(vty, " exit-vrf\n!\n"); diff --git a/zebra/zebra_vrf.h b/zebra/zebra_vrf.h index c7a64d300a..480866475c 100644 --- a/zebra/zebra_vrf.h +++ b/zebra/zebra_vrf.h @@ -33,16 +33,28 @@ extern "C" { #endif /* MPLS (Segment Routing) global block */ -typedef struct mpls_srgb_t_ { +struct mpls_srgb { uint32_t start_label; uint32_t end_label; -} mpls_srgb_t; +}; struct zebra_rmap { char *name; struct route_map *map; }; +PREDECL_RBTREE_UNIQ(otable); + +struct other_route_table { + struct otable_item next; + + afi_t afi; + safi_t safi; + uint32_t table_id; + + struct route_table *table; +}; + /* Routing table instance. */ struct zebra_vrf { /* Back pointer */ @@ -57,6 +69,7 @@ struct zebra_vrf { /* Flags. */ uint16_t flags; #define ZEBRA_VRF_RETAIN (1 << 0) +#define ZEBRA_PIM_SEND_VXLAN_SG (1 << 1) uint32_t table_id; @@ -69,6 +82,8 @@ struct zebra_vrf { /* Import check table (used mostly by BGP */ struct route_table *import_check_table[AFI_MAX]; + struct otable_head other_tables; + /* 2nd pointer type used primarily to quell a warning on * ALL_LIST_ELEMENTS_RO */ @@ -77,6 +92,11 @@ struct zebra_vrf { struct list *rid_all_sorted_list; struct list *rid_lo_sorted_list; struct prefix rid_user_assigned; + struct list _rid6_all_sorted_list; + struct list _rid6_lo_sorted_list; + struct list *rid6_all_sorted_list; + struct list *rid6_lo_sorted_list; + struct prefix rid6_user_assigned; /* * Back pointer to the owning namespace. @@ -96,7 +116,7 @@ struct zebra_vrf { struct route_table *fec_table[AFI_MAX]; /* MPLS Segment Routing Global block */ - mpls_srgb_t mpls_srgb; + struct mpls_srgb mpls_srgb; /* Pseudowires. */ struct zebra_pw_head pseudowires; @@ -110,9 +130,9 @@ struct zebra_vrf { #define MPLS_FLAG_SCHEDULE_LSPS (1 << 0) /* - * VNI hash table (for EVPN). Only in the EVPN instance. + * EVPN hash table. Only in the EVPN instance. */ - struct hash *vni_table; + struct hash *evpn_table; /* * Whether EVPN is enabled or not. Only in the EVPN instance. @@ -155,6 +175,13 @@ struct zebra_vrf { uint64_t lsp_removals_queued; uint64_t lsp_installs; uint64_t lsp_removals; + +#if defined(HAVE_RTADV) + struct rtadv rtadv; +#endif /* HAVE_RTADV */ + + int zebra_rnh_ip_default_route; + int zebra_rnh_ipv6_default_route; }; #define PROTO_RM_NAME(zvrf, afi, rtype) zvrf->proto_rm[afi][rtype].name #define NHT_RM_NAME(zvrf, afi, rtype) zvrf->nht_rm[afi][rtype].name @@ -171,7 +198,7 @@ struct zebra_vrf { static inline vrf_id_t zvrf_id(struct zebra_vrf *zvrf) { if (!zvrf || !zvrf->vrf) - return VRF_UNKNOWN; + return VRF_DEFAULT; return zvrf->vrf->vrf_id; } @@ -184,6 +211,8 @@ static inline const char *zvrf_ns_name(struct zebra_vrf *zvrf) static inline const char *zvrf_name(struct zebra_vrf *zvrf) { + if (!zvrf || !zvrf->vrf) + return "Unknown"; return zvrf->vrf->name; } @@ -192,9 +221,32 @@ static inline bool zvrf_is_active(struct zebra_vrf *zvrf) return zvrf->vrf->status & VRF_ACTIVE; } -struct route_table *zebra_vrf_table_with_table_id(afi_t afi, safi_t safi, - vrf_id_t vrf_id, - uint32_t table_id); +static inline int +zvrf_other_table_compare_func(const struct other_route_table *a, + const struct other_route_table *b) +{ + if (a->afi != b->afi) + return a->afi - b->afi; + + if (a->safi != b->safi) + return a->safi - b->safi; + + if (a->table_id != b->table_id) + return a->table_id - b->table_id; + + return 0; +} + +DECLARE_RBTREE_UNIQ(otable, struct other_route_table, next, + zvrf_other_table_compare_func) + +extern struct route_table * +zebra_vrf_lookup_table_with_table_id(afi_t afi, safi_t safi, vrf_id_t vrf_id, + uint32_t table_id); +extern struct route_table *zebra_vrf_get_table_with_table_id(afi_t afi, + safi_t safi, + vrf_id_t vrf_id, + uint32_t table_id); extern void zebra_vrf_update_all(struct zserv *client); extern struct zebra_vrf *zebra_vrf_lookup_by_id(vrf_id_t vrf_id); @@ -202,9 +254,6 @@ extern struct zebra_vrf *zebra_vrf_lookup_by_name(const char *); extern struct zebra_vrf *zebra_vrf_alloc(void); extern struct route_table *zebra_vrf_table(afi_t, safi_t, vrf_id_t); -extern struct route_table * -zebra_vrf_other_route_table(afi_t afi, uint32_t table_id, vrf_id_t vrf_id); -extern int zebra_vrf_has_config(struct zebra_vrf *zvrf); extern void zebra_vrf_init(void); extern void zebra_rtable_node_cleanup(struct route_table *table, diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 8cde07a109..be82099296 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -44,6 +44,7 @@ #include "zebra/zebra_routemap.h" #include "lib/json.h" #include "zebra/zebra_vxlan.h" +#include "zebra/zebra_evpn_mh.h" #ifndef VTYSH_EXTRACT_PL #include "zebra/zebra_vty_clippy.c" #endif @@ -52,21 +53,40 @@ #include "zebra/ipforward.h" #include "zebra/zebra_vxlan_private.h" #include "zebra/zebra_pbr.h" +#include "zebra/zebra_nhg.h" +#include "zebra/interface.h" +#include "northbound_cli.h" +#include "zebra/zebra_nb.h" +#include "zebra/kernel_netlink.h" extern int allow_delete; +/* context to manage dumps in multiple tables or vrfs */ +struct route_show_ctx { + bool multi; /* dump multiple tables or vrf */ + bool header_done; /* common header already displayed */ +}; + static int do_show_ip_route(struct vty *vty, const char *vrf_name, afi_t afi, safi_t safi, bool use_fib, bool use_json, route_tag_t tag, const struct prefix *longer_prefix_p, bool supernets_only, int type, - unsigned short ospf_instance_id); + unsigned short ospf_instance_id, uint32_t tableid, + struct route_show_ctx *ctx); static void vty_show_ip_route_detail(struct vty *vty, struct route_node *rn, - int mcast); + int mcast, bool use_fib, bool show_ng); static void vty_show_ip_route_summary(struct vty *vty, - struct route_table *table); + struct route_table *table, bool use_json); static void vty_show_ip_route_summary_prefix(struct vty *vty, - struct route_table *table); + struct route_table *table, + bool use_json); +/* Helper api to format a nexthop in the 'detailed' output path. */ +static void show_nexthop_detail_helper(struct vty *vty, + const struct route_entry *re, + const struct nexthop *nexthop, + bool is_backup); + DEFUN (ip_multicast_mode, ip_multicast_mode_cmd, @@ -127,8 +147,12 @@ DEFUN (show_ip_rpf, JSON_STR) { bool uj = use_json(argc, argv); + struct route_show_ctx ctx = { + .multi = false, + }; + return do_show_ip_route(vty, VRF_DEFAULT_NAME, AFI_IP, SAFI_MULTICAST, - false, uj, 0, NULL, false, 0, 0); + false, uj, 0, NULL, false, 0, 0, 0, &ctx); } DEFUN (show_ip_rpf_addr, @@ -154,18 +178,32 @@ DEFUN (show_ip_rpf_addr, re = rib_match_ipv4_multicast(VRF_DEFAULT, addr, &rn); if (re) - vty_show_ip_route_detail(vty, rn, 1); + vty_show_ip_route_detail(vty, rn, 1, false, false); else vty_out(vty, "%% No match for RPF lookup\n"); return CMD_SUCCESS; } -static char re_status_output_char(struct route_entry *re, struct nexthop *nhop) +static char re_status_output_char(const struct route_entry *re, + const struct nexthop *nhop, + bool is_fib) { if (CHECK_FLAG(re->status, ROUTE_ENTRY_INSTALLED)) { - if (!CHECK_FLAG(nhop->flags, NEXTHOP_FLAG_DUPLICATE) && - !CHECK_FLAG(nhop->flags, NEXTHOP_FLAG_RECURSIVE)) + bool star_p = false; + + if (nhop && + !CHECK_FLAG(nhop->flags, NEXTHOP_FLAG_DUPLICATE) && + !CHECK_FLAG(nhop->flags, NEXTHOP_FLAG_RECURSIVE)) { + /* More-specific test for 'fib' output */ + if (is_fib) { + star_p = !!CHECK_FLAG(nhop->flags, + NEXTHOP_FLAG_FIB); + } else + star_p = true; + } + + if (star_p) return '*'; else return ' '; @@ -184,19 +222,214 @@ static char re_status_output_char(struct route_entry *re, struct nexthop *nhop) return ' '; } +/* + * Show backup nexthop info, in the 'detailed' output path + */ +static void show_nh_backup_helper(struct vty *vty, + const struct route_entry *re, + const struct nexthop *nexthop) +{ + const struct nexthop *start, *backup, *temp; + int i, idx; + + /* Double-check that there _is_ a backup */ + if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP) || + re->nhe->backup_info == NULL || re->nhe->backup_info->nhe == NULL || + re->nhe->backup_info->nhe->nhg.nexthop == NULL) + return; + + /* Locate the backup nexthop(s) */ + start = re->nhe->backup_info->nhe->nhg.nexthop; + for (i = 0; i < nexthop->backup_num; i++) { + /* Format the backup(s) (indented) */ + backup = start; + for (idx = 0; idx < nexthop->backup_idx[i]; idx++) { + backup = backup->next; + if (backup == NULL) + break; + } + + /* It's possible for backups to be recursive too, + * so walk the recursive resolution list if present. + */ + temp = backup; + while (backup) { + vty_out(vty, " "); + show_nexthop_detail_helper(vty, re, backup, + true /*backup*/); + vty_out(vty, "\n"); + + if (backup->resolved && temp == backup) + backup = backup->resolved; + else + backup = nexthop_next(backup); + + if (backup == temp->next) + break; + } + } + +} + +/* + * Helper api to format output for a nexthop, used in the 'detailed' + * output path. + */ +static void show_nexthop_detail_helper(struct vty *vty, + const struct route_entry *re, + const struct nexthop *nexthop, + bool is_backup) +{ + char addrstr[32]; + char buf[MPLS_LABEL_STRLEN]; + int i; + + if (is_backup) + vty_out(vty, " b%s", + nexthop->rparent ? " " : ""); + else + vty_out(vty, " %c%s", + re_status_output_char(re, nexthop, false), + nexthop->rparent ? " " : ""); + + switch (nexthop->type) { + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + vty_out(vty, " %s", + inet_ntoa(nexthop->gate.ipv4)); + if (nexthop->ifindex) + vty_out(vty, ", via %s", + ifindex2ifname( + nexthop->ifindex, + nexthop->vrf_id)); + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + vty_out(vty, " %s", + inet_ntop(AF_INET6, &nexthop->gate.ipv6, + buf, sizeof(buf))); + if (nexthop->ifindex) + vty_out(vty, ", via %s", + ifindex2ifname( + nexthop->ifindex, + nexthop->vrf_id)); + break; + + case NEXTHOP_TYPE_IFINDEX: + vty_out(vty, " directly connected, %s", + ifindex2ifname(nexthop->ifindex, + nexthop->vrf_id)); + break; + case NEXTHOP_TYPE_BLACKHOLE: + vty_out(vty, " unreachable"); + switch (nexthop->bh_type) { + case BLACKHOLE_REJECT: + vty_out(vty, " (ICMP unreachable)"); + break; + case BLACKHOLE_ADMINPROHIB: + vty_out(vty, + " (ICMP admin-prohibited)"); + break; + case BLACKHOLE_NULL: + vty_out(vty, " (blackhole)"); + break; + case BLACKHOLE_UNSPEC: + break; + } + break; + default: + break; + } + + if ((re->vrf_id != nexthop->vrf_id) + && (nexthop->type != NEXTHOP_TYPE_BLACKHOLE)) + vty_out(vty, "(vrf %s)", vrf_id_to_name(nexthop->vrf_id)); + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE)) + vty_out(vty, " (duplicate nexthop removed)"); + + if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) + vty_out(vty, " inactive"); + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK)) + vty_out(vty, " onlink"); + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + vty_out(vty, " (recursive)"); + + /* Source specified? */ + switch (nexthop->type) { + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + if (nexthop->src.ipv4.s_addr) { + if (inet_ntop(AF_INET, &nexthop->src.ipv4, + addrstr, sizeof(addrstr))) + vty_out(vty, ", src %s", + addrstr); + } + break; + + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + if (!IPV6_ADDR_SAME(&nexthop->src.ipv6, + &in6addr_any)) { + if (inet_ntop(AF_INET6, &nexthop->src.ipv6, + addrstr, sizeof(addrstr))) + vty_out(vty, ", src %s", + addrstr); + } + break; + + default: + break; + } + + if (re->nexthop_mtu) + vty_out(vty, ", mtu %u", re->nexthop_mtu); + + /* Label information */ + if (nexthop->nh_label && nexthop->nh_label->num_labels) { + vty_out(vty, ", label %s", + mpls_label2str(nexthop->nh_label->num_labels, + nexthop->nh_label->label, buf, + sizeof(buf), 1 /*pretty*/)); + } + + if (nexthop->weight) + vty_out(vty, ", weight %u", nexthop->weight); + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) { + vty_out(vty, ", backup %d", nexthop->backup_idx[0]); + + for (i = 1; i < nexthop->backup_num; i++) + vty_out(vty, ",%d", nexthop->backup_idx[i]); + } +} + /* New RIB. Detailed information for IPv4 route. */ static void vty_show_ip_route_detail(struct vty *vty, struct route_node *rn, - int mcast) + int mcast, bool use_fib, bool show_ng) { struct route_entry *re; struct nexthop *nexthop; char buf[SRCDEST2STR_BUFFER]; struct zebra_vrf *zvrf; + rib_dest_t *dest; + + dest = rib_dest_from_rnode(rn); RNODE_FOREACH_RE (rn, re) { + /* + * If re not selected for forwarding, skip re + * for "show ip/ipv6 fib " + */ + if (use_fib && re != dest->selected_fib) + continue; + const char *mcast_info = ""; if (mcast) { - rib_table_info_t *info = srcdest_rnode_table_info(rn); + struct rib_table_info *info = + srcdest_rnode_table_info(rn); mcast_info = (info->safi == SAFI_MULTICAST) ? " using Multicast RIB" : " using Unicast RIB"; @@ -228,173 +461,368 @@ static void vty_show_ip_route_detail(struct vty *vty, struct route_node *rn, vty_out(vty, "\n"); time_t uptime; - struct tm *tm; - uptime = time(NULL); + uptime = monotime(NULL); uptime -= re->uptime; - tm = gmtime(&uptime); - vty_out(vty, " Last update "); + frrtime_to_interval(uptime, buf, sizeof(buf)); - if (uptime < ONE_DAY_SECOND) - vty_out(vty, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min, - tm->tm_sec); - else if (uptime < ONE_WEEK_SECOND) - vty_out(vty, "%dd%02dh%02dm", tm->tm_yday, tm->tm_hour, - tm->tm_min); - else - vty_out(vty, "%02dw%dd%02dh", tm->tm_yday / 7, - tm->tm_yday - ((tm->tm_yday / 7) * 7), - tm->tm_hour); - vty_out(vty, " ago\n"); - - for (ALL_NEXTHOPS(re->ng, nexthop)) { - char addrstr[32]; - - vty_out(vty, " %c%s", - re_status_output_char(re, nexthop), - nexthop->rparent ? " " : ""); - - switch (nexthop->type) { - case NEXTHOP_TYPE_IPV4: - case NEXTHOP_TYPE_IPV4_IFINDEX: - vty_out(vty, " %s", - inet_ntoa(nexthop->gate.ipv4)); - if (nexthop->ifindex) - vty_out(vty, ", via %s", - ifindex2ifname( - nexthop->ifindex, - nexthop->vrf_id)); - break; - case NEXTHOP_TYPE_IPV6: - case NEXTHOP_TYPE_IPV6_IFINDEX: - vty_out(vty, " %s", - inet_ntop(AF_INET6, &nexthop->gate.ipv6, - buf, sizeof buf)); - if (nexthop->ifindex) - vty_out(vty, ", via %s", - ifindex2ifname( - nexthop->ifindex, - nexthop->vrf_id)); - break; - case NEXTHOP_TYPE_IFINDEX: - vty_out(vty, " directly connected, %s", - ifindex2ifname(nexthop->ifindex, - nexthop->vrf_id)); - break; - case NEXTHOP_TYPE_BLACKHOLE: - vty_out(vty, " unreachable"); - switch (nexthop->bh_type) { - case BLACKHOLE_REJECT: - vty_out(vty, " (ICMP unreachable)"); - break; - case BLACKHOLE_ADMINPROHIB: - vty_out(vty, - " (ICMP admin-prohibited)"); - break; - case BLACKHOLE_NULL: - vty_out(vty, " (blackhole)"); - break; - case BLACKHOLE_UNSPEC: - break; - } - break; - default: - break; - } + vty_out(vty, " Last update %s ago\n", buf); - if ((re->vrf_id != nexthop->vrf_id) - && (nexthop->type != NEXTHOP_TYPE_BLACKHOLE)) { - struct vrf *vrf = - vrf_lookup_by_id(nexthop->vrf_id); + if (show_ng) + vty_out(vty, " Nexthop Group ID: %u\n", re->nhe_id); - if (vrf) - vty_out(vty, "(vrf %s)", vrf->name); - else - vty_out(vty, "(vrf UNKNOWN)"); - } + for (ALL_NEXTHOPS(re->nhe->nhg, nexthop)) { + /* Use helper to format each nexthop */ + show_nexthop_detail_helper(vty, re, nexthop, + false /*not backup*/); + vty_out(vty, "\n"); + + /* Include backup(s), if present */ + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) + show_nh_backup_helper(vty, re, nexthop); + } + vty_out(vty, "\n"); + } +} + +/* + * Helper for nexthop output, used in the 'show ip route' path + */ +static void show_route_nexthop_helper(struct vty *vty, + const struct route_entry *re, + const struct nexthop *nexthop) +{ + char buf[MPLS_LABEL_STRLEN]; + int i; + + switch (nexthop->type) { + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + vty_out(vty, " via %s", inet_ntoa(nexthop->gate.ipv4)); + if (nexthop->ifindex) + vty_out(vty, ", %s", + ifindex2ifname(nexthop->ifindex, + nexthop->vrf_id)); + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + vty_out(vty, " via %s", + inet_ntop(AF_INET6, &nexthop->gate.ipv6, buf, + sizeof(buf))); + if (nexthop->ifindex) + vty_out(vty, ", %s", + ifindex2ifname(nexthop->ifindex, + nexthop->vrf_id)); + break; + + case NEXTHOP_TYPE_IFINDEX: + vty_out(vty, " is directly connected, %s", + ifindex2ifname(nexthop->ifindex, + nexthop->vrf_id)); + break; + case NEXTHOP_TYPE_BLACKHOLE: + vty_out(vty, " unreachable"); + switch (nexthop->bh_type) { + case BLACKHOLE_REJECT: + vty_out(vty, " (ICMP unreachable)"); + break; + case BLACKHOLE_ADMINPROHIB: + vty_out(vty, " (ICMP admin-prohibited)"); + break; + case BLACKHOLE_NULL: + vty_out(vty, " (blackhole)"); + break; + case BLACKHOLE_UNSPEC: + break; + } + break; + default: + break; + } - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE)) - vty_out(vty, " (duplicate nexthop removed)"); + if ((re == NULL || (nexthop->vrf_id != re->vrf_id)) + && (nexthop->type != NEXTHOP_TYPE_BLACKHOLE)) + vty_out(vty, " (vrf %s)", vrf_id_to_name(nexthop->vrf_id)); + + if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) + vty_out(vty, " inactive"); + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK)) + vty_out(vty, " onlink"); + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + vty_out(vty, " (recursive)"); + + switch (nexthop->type) { + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + if (nexthop->src.ipv4.s_addr) { + if (inet_ntop(AF_INET, &nexthop->src.ipv4, buf, + sizeof(buf))) + vty_out(vty, ", src %s", buf); + /* SR-TE information */ + if (nexthop->srte_color) + vty_out(vty, ", SR-TE color %u", + nexthop->srte_color); + } + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + if (!IPV6_ADDR_SAME(&nexthop->src.ipv6, &in6addr_any)) { + if (inet_ntop(AF_INET6, &nexthop->src.ipv6, buf, + sizeof(buf))) + vty_out(vty, ", src %s", buf); + } + break; + default: + break; + } - if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) - vty_out(vty, " inactive"); + /* Label information */ + if (nexthop->nh_label && nexthop->nh_label->num_labels) { + vty_out(vty, ", label %s", + mpls_label2str(nexthop->nh_label->num_labels, + nexthop->nh_label->label, buf, + sizeof(buf), 1)); + } - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK)) - vty_out(vty, " onlink"); + if (nexthop->weight) + vty_out(vty, ", weight %u", nexthop->weight); - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) - vty_out(vty, " (recursive)"); + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) { + vty_out(vty, ", backup %d", nexthop->backup_idx[0]); - switch (nexthop->type) { - case NEXTHOP_TYPE_IPV4: - case NEXTHOP_TYPE_IPV4_IFINDEX: - if (nexthop->src.ipv4.s_addr) { - if (inet_ntop(AF_INET, - &nexthop->src.ipv4, - addrstr, sizeof addrstr)) - vty_out(vty, ", src %s", - addrstr); - } - break; - case NEXTHOP_TYPE_IPV6: - case NEXTHOP_TYPE_IPV6_IFINDEX: - if (!IPV6_ADDR_SAME(&nexthop->src.ipv6, - &in6addr_any)) { - if (inet_ntop(AF_INET6, - &nexthop->src.ipv6, - addrstr, sizeof addrstr)) - vty_out(vty, ", src %s", - addrstr); - } - break; - default: - break; - } + for (i = 1; i < nexthop->backup_num; i++) + vty_out(vty, ",%d", nexthop->backup_idx[i]); + } +} - if (re->nexthop_mtu) - vty_out(vty, ", mtu %u", re->nexthop_mtu); - - /* Label information */ - if (nexthop->nh_label - && nexthop->nh_label->num_labels) { - vty_out(vty, ", label %s", - mpls_label2str( - nexthop->nh_label->num_labels, - nexthop->nh_label->label, buf, - sizeof buf, 1)); - } +/* + * Render a nexthop into a json object; the caller allocates and owns + * the json object memory. + */ +static void show_nexthop_json_helper(json_object *json_nexthop, + const struct nexthop *nexthop, + const struct route_entry *re) +{ + char buf[SRCDEST2STR_BUFFER]; + json_object *json_labels = NULL; + json_object *json_backups = NULL; + int i; + + json_object_int_add(json_nexthop, "flags", + nexthop->flags); + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE)) + json_object_boolean_true_add(json_nexthop, + "duplicate"); + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) + json_object_boolean_true_add(json_nexthop, + "fib"); + + switch (nexthop->type) { + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + json_object_string_add( + json_nexthop, "ip", + inet_ntoa(nexthop->gate.ipv4)); + json_object_string_add(json_nexthop, "afi", + "ipv4"); + + if (nexthop->ifindex) { + json_object_int_add(json_nexthop, + "interfaceIndex", + nexthop->ifindex); + json_object_string_add( + json_nexthop, "interfaceName", + ifindex2ifname( + nexthop->ifindex, + nexthop->vrf_id)); + } + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + json_object_string_add( + json_nexthop, "ip", + inet_ntop(AF_INET6, &nexthop->gate.ipv6, + buf, sizeof(buf))); + json_object_string_add(json_nexthop, "afi", + "ipv6"); + + if (nexthop->ifindex) { + json_object_int_add(json_nexthop, + "interfaceIndex", + nexthop->ifindex); + json_object_string_add( + json_nexthop, "interfaceName", + ifindex2ifname( + nexthop->ifindex, + nexthop->vrf_id)); + } + break; - vty_out(vty, "\n"); + case NEXTHOP_TYPE_IFINDEX: + json_object_boolean_true_add( + json_nexthop, "directlyConnected"); + json_object_int_add(json_nexthop, + "interfaceIndex", + nexthop->ifindex); + json_object_string_add( + json_nexthop, "interfaceName", + ifindex2ifname(nexthop->ifindex, + nexthop->vrf_id)); + break; + case NEXTHOP_TYPE_BLACKHOLE: + json_object_boolean_true_add(json_nexthop, + "unreachable"); + switch (nexthop->bh_type) { + case BLACKHOLE_REJECT: + json_object_boolean_true_add( + json_nexthop, "reject"); + break; + case BLACKHOLE_ADMINPROHIB: + json_object_boolean_true_add( + json_nexthop, + "admin-prohibited"); + break; + case BLACKHOLE_NULL: + json_object_boolean_true_add( + json_nexthop, "blackhole"); + break; + case BLACKHOLE_UNSPEC: + break; } - vty_out(vty, "\n"); + break; + default: + break; + } + + if ((nexthop->vrf_id != re->vrf_id) + && (nexthop->type != NEXTHOP_TYPE_BLACKHOLE)) + json_object_string_add(json_nexthop, "vrf", + vrf_id_to_name(nexthop->vrf_id)); + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE)) + json_object_boolean_true_add(json_nexthop, + "duplicate"); + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) + json_object_boolean_true_add(json_nexthop, + "active"); + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK)) + json_object_boolean_true_add(json_nexthop, + "onLink"); + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + json_object_boolean_true_add(json_nexthop, + "recursive"); + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) { + json_backups = json_object_new_array(); + for (i = 0; i < nexthop->backup_num; i++) { + json_object_array_add( + json_backups, + json_object_new_int(nexthop->backup_idx[i])); + } + + json_object_object_add(json_nexthop, "backupIndex", + json_backups); + } + + switch (nexthop->type) { + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + if (nexthop->src.ipv4.s_addr) { + if (inet_ntop(AF_INET, + &nexthop->src.ipv4, buf, + sizeof(buf))) + json_object_string_add( + json_nexthop, "source", + buf); + } + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + if (!IPV6_ADDR_SAME(&nexthop->src.ipv6, + &in6addr_any)) { + if (inet_ntop(AF_INET6, + &nexthop->src.ipv6, buf, + sizeof(buf))) + json_object_string_add( + json_nexthop, "source", + buf); + } + break; + default: + break; } + + if (nexthop->nh_label + && nexthop->nh_label->num_labels) { + json_labels = json_object_new_array(); + + for (int label_index = 0; + label_index + < nexthop->nh_label->num_labels; + label_index++) + json_object_array_add( + json_labels, + json_object_new_int( + nexthop->nh_label->label + [label_index])); + + json_object_object_add(json_nexthop, "labels", + json_labels); + } + + if (nexthop->weight) + json_object_int_add(json_nexthop, "weight", + nexthop->weight); + + if (nexthop->srte_color) + json_object_int_add(json_nexthop, "srteColor", + nexthop->srte_color); } static void vty_show_ip_route(struct vty *vty, struct route_node *rn, - struct route_entry *re, json_object *json) + struct route_entry *re, json_object *json, + bool is_fib) { - struct nexthop *nexthop; + const struct nexthop *nexthop; int len = 0; char buf[SRCDEST2STR_BUFFER]; json_object *json_nexthops = NULL; json_object *json_nexthop = NULL; json_object *json_route = NULL; - json_object *json_labels = NULL; time_t uptime; - struct tm *tm; - rib_dest_t *dest = rib_dest_from_rnode(rn); + const rib_dest_t *dest = rib_dest_from_rnode(rn); + const struct nexthop_group *nhg; + char up_str[MONOTIME_STRLEN]; + bool first_p = true; + bool nhg_from_backup = false; - uptime = time(NULL); + uptime = monotime(NULL); uptime -= re->uptime; - tm = gmtime(&uptime); + + frrtime_to_interval(uptime, up_str, sizeof(up_str)); + + /* If showing fib information, use the fib view of the + * nexthops. + */ + if (is_fib) + nhg = rib_get_fib_nhg(re); + else + nhg = &(re->nhe->nhg); if (json) { json_route = json_object_new_object(); json_nexthops = json_object_new_array(); json_object_string_add(json_route, "prefix", - srcdest_rnode2str(rn, buf, sizeof buf)); + srcdest_rnode2str(rn, buf, sizeof(buf))); json_object_string_add(json_route, "protocol", zebra_route_string(re->type)); @@ -402,8 +830,9 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn, json_object_int_add(json_route, "instance", re->instance); - if (re->vrf_id) - json_object_int_add(json_route, "vrfId", re->vrf_id); + json_object_int_add(json_route, "vrfId", re->vrf_id); + json_object_string_add(json_route, "vrfName", + vrf_id_to_name(re->vrf_id)); if (CHECK_FLAG(re->flags, ZEBRA_FLAG_SELECTED)) json_object_boolean_true_add(json_route, "selected"); @@ -428,339 +857,164 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn, if (re->tag) json_object_int_add(json_route, "tag", re->tag); + if (re->table) + json_object_int_add(json_route, "table", re->table); + json_object_int_add(json_route, "internalStatus", re->status); json_object_int_add(json_route, "internalFlags", re->flags); - if (uptime < ONE_DAY_SECOND) - sprintf(buf, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min, - tm->tm_sec); - else if (uptime < ONE_WEEK_SECOND) - sprintf(buf, "%dd%02dh%02dm", tm->tm_yday, tm->tm_hour, - tm->tm_min); - else - sprintf(buf, "%02dw%dd%02dh", tm->tm_yday / 7, - tm->tm_yday - ((tm->tm_yday / 7) * 7), - tm->tm_hour); + json_object_int_add(json_route, "internalNextHopNum", + nexthop_group_nexthop_num(&(re->nhe->nhg))); + json_object_int_add(json_route, "internalNextHopActiveNum", + nexthop_group_active_nexthop_num( + &(re->nhe->nhg))); - json_object_string_add(json_route, "uptime", buf); + json_object_string_add(json_route, "uptime", up_str); - for (ALL_NEXTHOPS(re->ng, nexthop)) { + for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { json_nexthop = json_object_new_object(); + show_nexthop_json_helper(json_nexthop, + nexthop, re); - json_object_int_add(json_nexthop, "flags", - nexthop->flags); - - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE)) - json_object_boolean_true_add(json_nexthop, - "duplicate"); - - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) - json_object_boolean_true_add(json_nexthop, - "fib"); + json_object_array_add(json_nexthops, + json_nexthop); + } - switch (nexthop->type) { - case NEXTHOP_TYPE_IPV4: - case NEXTHOP_TYPE_IPV4_IFINDEX: - json_object_string_add( - json_nexthop, "ip", - inet_ntoa(nexthop->gate.ipv4)); - json_object_string_add(json_nexthop, "afi", - "ipv4"); - - if (nexthop->ifindex) { - json_object_int_add(json_nexthop, - "interfaceIndex", - nexthop->ifindex); - json_object_string_add( - json_nexthop, "interfaceName", - ifindex2ifname( - nexthop->ifindex, - nexthop->vrf_id)); - } - break; - case NEXTHOP_TYPE_IPV6: - case NEXTHOP_TYPE_IPV6_IFINDEX: - json_object_string_add( - json_nexthop, "ip", - inet_ntop(AF_INET6, &nexthop->gate.ipv6, - buf, sizeof buf)); - json_object_string_add(json_nexthop, "afi", - "ipv6"); - - if (nexthop->ifindex) { - json_object_int_add(json_nexthop, - "interfaceIndex", - nexthop->ifindex); - json_object_string_add( - json_nexthop, "interfaceName", - ifindex2ifname( - nexthop->ifindex, - nexthop->vrf_id)); - } - break; + json_object_object_add(json_route, "nexthops", json_nexthops); - case NEXTHOP_TYPE_IFINDEX: - json_object_boolean_true_add( - json_nexthop, "directlyConnected"); - json_object_int_add(json_nexthop, - "interfaceIndex", - nexthop->ifindex); - json_object_string_add( - json_nexthop, "interfaceName", - ifindex2ifname(nexthop->ifindex, - nexthop->vrf_id)); - break; - case NEXTHOP_TYPE_BLACKHOLE: - json_object_boolean_true_add(json_nexthop, - "unreachable"); - switch (nexthop->bh_type) { - case BLACKHOLE_REJECT: - json_object_boolean_true_add( - json_nexthop, "reject"); - break; - case BLACKHOLE_ADMINPROHIB: - json_object_boolean_true_add( - json_nexthop, - "admin-prohibited"); - break; - case BLACKHOLE_NULL: - json_object_boolean_true_add( - json_nexthop, "blackhole"); - break; - case BLACKHOLE_UNSPEC: - break; - } - break; - default: - break; - } + /* If there are backup nexthops, include them */ + if (is_fib) + nhg = rib_get_fib_backup_nhg(re); + else + nhg = zebra_nhg_get_backup_nhg(re->nhe); - if ((nexthop->vrf_id != re->vrf_id) - && (nexthop->type != NEXTHOP_TYPE_BLACKHOLE)) { - struct vrf *vrf = - vrf_lookup_by_id(nexthop->vrf_id); + if (nhg && nhg->nexthop) { + json_nexthops = json_object_new_array(); - json_object_string_add(json_nexthop, "vrf", - vrf->name); - } - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE)) - json_object_boolean_true_add(json_nexthop, - "duplicate"); - - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) - json_object_boolean_true_add(json_nexthop, - "active"); - - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK)) - json_object_boolean_true_add(json_nexthop, - "onLink"); - - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) - json_object_boolean_true_add(json_nexthop, - "recursive"); - - switch (nexthop->type) { - case NEXTHOP_TYPE_IPV4: - case NEXTHOP_TYPE_IPV4_IFINDEX: - if (nexthop->src.ipv4.s_addr) { - if (inet_ntop(AF_INET, - &nexthop->src.ipv4, buf, - sizeof buf)) - json_object_string_add( - json_nexthop, "source", - buf); - } - break; - case NEXTHOP_TYPE_IPV6: - case NEXTHOP_TYPE_IPV6_IFINDEX: - if (!IPV6_ADDR_SAME(&nexthop->src.ipv6, - &in6addr_any)) { - if (inet_ntop(AF_INET6, - &nexthop->src.ipv6, buf, - sizeof buf)) - json_object_string_add( - json_nexthop, "source", - buf); - } - break; - default: - break; - } + for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { + json_nexthop = json_object_new_object(); - if (nexthop->nh_label - && nexthop->nh_label->num_labels) { - json_labels = json_object_new_array(); - - for (int label_index = 0; - label_index - < nexthop->nh_label->num_labels; - label_index++) - json_object_array_add( - json_labels, - json_object_new_int( - nexthop->nh_label->label - [label_index])); - - json_object_object_add(json_nexthop, "labels", - json_labels); + show_nexthop_json_helper(json_nexthop, + nexthop, re); + json_object_array_add(json_nexthops, + json_nexthop); } - json_object_array_add(json_nexthops, json_nexthop); + json_object_object_add(json_route, "backupNexthops", + json_nexthops); } - json_object_object_add(json_route, "nexthops", json_nexthops); json_object_array_add(json, json_route); return; } + /* Prefix information, and first nexthop. If we're showing 'fib', + * and there are no installed primary nexthops, see if there are any + * backup nexthops and start with those. + */ + if (is_fib && nhg->nexthop == NULL) { + nhg = rib_get_fib_backup_nhg(re); + nhg_from_backup = true; + } + + len = vty_out(vty, "%c", zebra_route_char(re->type)); + if (re->instance) + len += vty_out(vty, "[%d]", re->instance); + if (nhg_from_backup && nhg->nexthop) { + len += vty_out( + vty, "%cb%c %s", + CHECK_FLAG(re->flags, ZEBRA_FLAG_SELECTED) ? '>' : ' ', + re_status_output_char(re, nhg->nexthop, is_fib), + srcdest_rnode2str(rn, buf, sizeof(buf))); + } else { + len += vty_out( + vty, "%c%c %s", + CHECK_FLAG(re->flags, ZEBRA_FLAG_SELECTED) ? '>' : ' ', + re_status_output_char(re, nhg->nexthop, is_fib), + srcdest_rnode2str(rn, buf, sizeof(buf))); + } + + /* Distance and metric display. */ + if (((re->type == ZEBRA_ROUTE_CONNECT) && + (re->distance || re->metric)) || + (re->type != ZEBRA_ROUTE_CONNECT)) + len += vty_out(vty, " [%u/%u]", re->distance, + re->metric); + /* Nexthop information. */ - for (ALL_NEXTHOPS(re->ng, nexthop)) { - if (nexthop == re->ng.nexthop) { - /* Prefix information. */ - len = vty_out(vty, "%c", zebra_route_char(re->type)); - if (re->instance) - len += vty_out(vty, "[%d]", re->instance); - len += vty_out( - vty, "%c%c %s", - CHECK_FLAG(re->flags, ZEBRA_FLAG_SELECTED) - ? '>' - : ' ', - re_status_output_char(re, nexthop), - srcdest_rnode2str(rn, buf, sizeof buf)); - - /* Distance and metric display. */ - if (((re->type == ZEBRA_ROUTE_CONNECT) && - (re->distance || re->metric)) || - (re->type != ZEBRA_ROUTE_CONNECT)) - len += vty_out(vty, " [%u/%u]", re->distance, - re->metric); + for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { + if (first_p) { + first_p = false; + } else if (nhg_from_backup) { + vty_out(vty, " b%c%*c", + re_status_output_char(re, nexthop, is_fib), + len - 3 + (2 * nexthop_level(nexthop)), ' '); } else { vty_out(vty, " %c%*c", - re_status_output_char(re, nexthop), + re_status_output_char(re, nexthop, is_fib), len - 3 + (2 * nexthop_level(nexthop)), ' '); } - switch (nexthop->type) { - case NEXTHOP_TYPE_IPV4: - case NEXTHOP_TYPE_IPV4_IFINDEX: - vty_out(vty, " via %s", inet_ntoa(nexthop->gate.ipv4)); - if (nexthop->ifindex) - vty_out(vty, ", %s", - ifindex2ifname(nexthop->ifindex, - nexthop->vrf_id)); - break; - case NEXTHOP_TYPE_IPV6: - case NEXTHOP_TYPE_IPV6_IFINDEX: - vty_out(vty, " via %s", - inet_ntop(AF_INET6, &nexthop->gate.ipv6, buf, - sizeof buf)); - if (nexthop->ifindex) - vty_out(vty, ", %s", - ifindex2ifname(nexthop->ifindex, - nexthop->vrf_id)); - break; - - case NEXTHOP_TYPE_IFINDEX: - vty_out(vty, " is directly connected, %s", - ifindex2ifname(nexthop->ifindex, - nexthop->vrf_id)); - break; - case NEXTHOP_TYPE_BLACKHOLE: - vty_out(vty, " unreachable"); - switch (nexthop->bh_type) { - case BLACKHOLE_REJECT: - vty_out(vty, " (ICMP unreachable)"); - break; - case BLACKHOLE_ADMINPROHIB: - vty_out(vty, " (ICMP admin-prohibited)"); - break; - case BLACKHOLE_NULL: - vty_out(vty, " (blackhole)"); - break; - case BLACKHOLE_UNSPEC: - break; - } - break; - default: - break; - } - - if ((nexthop->vrf_id != re->vrf_id) - && (nexthop->type != NEXTHOP_TYPE_BLACKHOLE)) { - struct vrf *vrf = vrf_lookup_by_id(nexthop->vrf_id); - - if (vrf) - vty_out(vty, "(vrf %s)", vrf->name); - else - vty_out(vty, "(vrf UNKNOWN)"); - } - - if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) - vty_out(vty, " inactive"); - - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK)) - vty_out(vty, " onlink"); + show_route_nexthop_helper(vty, re, nexthop); + vty_out(vty, ", %s\n", up_str); + } - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) - vty_out(vty, " (recursive)"); + /* If we only had backup nexthops, we're done */ + if (nhg_from_backup) + return; - switch (nexthop->type) { - case NEXTHOP_TYPE_IPV4: - case NEXTHOP_TYPE_IPV4_IFINDEX: - if (nexthop->src.ipv4.s_addr) { - if (inet_ntop(AF_INET, &nexthop->src.ipv4, buf, - sizeof buf)) - vty_out(vty, ", src %s", buf); - } - break; - case NEXTHOP_TYPE_IPV6: - case NEXTHOP_TYPE_IPV6_IFINDEX: - if (!IPV6_ADDR_SAME(&nexthop->src.ipv6, &in6addr_any)) { - if (inet_ntop(AF_INET6, &nexthop->src.ipv6, buf, - sizeof buf)) - vty_out(vty, ", src %s", buf); - } - break; - default: - break; - } + /* Check for backup nexthop info if present */ + if (is_fib) + nhg = rib_get_fib_backup_nhg(re); + else + nhg = zebra_nhg_get_backup_nhg(re->nhe); - /* Label information */ - if (nexthop->nh_label && nexthop->nh_label->num_labels) { - vty_out(vty, ", label %s", - mpls_label2str(nexthop->nh_label->num_labels, - nexthop->nh_label->label, buf, - sizeof buf, 1)); - } + if (nhg == NULL) + return; - if (uptime < ONE_DAY_SECOND) - vty_out(vty, ", %02d:%02d:%02d", tm->tm_hour, - tm->tm_min, tm->tm_sec); - else if (uptime < ONE_WEEK_SECOND) - vty_out(vty, ", %dd%02dh%02dm", tm->tm_yday, - tm->tm_hour, tm->tm_min); - else - vty_out(vty, ", %02dw%dd%02dh", tm->tm_yday / 7, - tm->tm_yday - ((tm->tm_yday / 7) * 7), - tm->tm_hour); + /* Print backup info */ + for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { + bool star_p = false; + + if (is_fib) + star_p = CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); + + /* TODO -- it'd be nice to be able to include + * the entire list of backups, *and* include the + * real installation state. + */ + vty_out(vty, " b%c %*c", + (star_p ? '*' : ' '), + len - 3 + (2 * nexthop_level(nexthop)), ' '); + show_route_nexthop_helper(vty, re, nexthop); vty_out(vty, "\n"); } + } static void vty_show_ip_route_detail_json(struct vty *vty, - struct route_node *rn) + struct route_node *rn, bool use_fib) { json_object *json = NULL; json_object *json_prefix = NULL; struct route_entry *re; char buf[BUFSIZ]; + rib_dest_t *dest; + + dest = rib_dest_from_rnode(rn); json = json_object_new_object(); json_prefix = json_object_new_array(); RNODE_FOREACH_RE (rn, re) { - vty_show_ip_route(vty, rn, re, json_prefix); + /* + * If re not selected for forwarding, skip re + * for "show ip/ipv6 fib json" + */ + if (use_fib && re != dest->selected_fib) + continue; + vty_show_ip_route(vty, rn, re, json_prefix, use_fib); } prefix2str(&rn->p, buf, sizeof(buf)); @@ -775,7 +1029,8 @@ static void do_show_route_helper(struct vty *vty, struct zebra_vrf *zvrf, bool use_fib, route_tag_t tag, const struct prefix *longer_prefix_p, bool supernets_only, int type, - unsigned short ospf_instance_id, bool use_json) + unsigned short ospf_instance_id, bool use_json, + uint32_t tableid, struct route_show_ctx *ctx) { struct route_node *rn; struct route_entry *re; @@ -786,6 +1041,17 @@ static void do_show_route_helper(struct vty *vty, struct zebra_vrf *zvrf, uint32_t addr; char buf[BUFSIZ]; + /* + * ctx->multi indicates if we are dumping multiple tables or vrfs. + * if set: + * => display the common header at most once + * => add newline at each call except first + * => always display the VRF and table + * else: + * => display the common header if at least one entry is found + * => display the VRF and table if specific + */ + if (use_json) json = json_object_new_object(); @@ -829,24 +1095,33 @@ static void do_show_route_helper(struct vty *vty, struct zebra_vrf *zvrf, if (use_json) { if (!json_prefix) json_prefix = json_object_new_array(); - } else { - if (first) { + } else if (first) { + if (!ctx->header_done) { if (afi == AFI_IP) vty_out(vty, SHOW_ROUTE_V4_HEADER); else vty_out(vty, SHOW_ROUTE_V6_HEADER); - - if (zvrf_id(zvrf) != VRF_DEFAULT) - vty_out(vty, "\nVRF %s:\n", + } + if (ctx->multi && ctx->header_done) + vty_out(vty, "\n"); + if (ctx->multi || zvrf_id(zvrf) != VRF_DEFAULT + || tableid) { + if (!tableid) + vty_out(vty, "VRF %s:\n", zvrf_name(zvrf)); - - first = 0; + else + vty_out(vty, + "VRF %s table %u:\n", + zvrf_name(zvrf), + tableid); } + ctx->header_done = true; + first = 0; } - vty_show_ip_route(vty, rn, re, json_prefix); + vty_show_ip_route(vty, rn, re, json_prefix, use_fib); } if (json_prefix) { @@ -863,12 +1138,41 @@ static void do_show_route_helper(struct vty *vty, struct zebra_vrf *zvrf, } } +static void do_show_ip_route_all(struct vty *vty, struct zebra_vrf *zvrf, + afi_t afi, bool use_fib, bool use_json, + route_tag_t tag, + const struct prefix *longer_prefix_p, + bool supernets_only, int type, + unsigned short ospf_instance_id, + struct route_show_ctx *ctx) +{ + struct zebra_router_table *zrt; + struct rib_table_info *info; + + RB_FOREACH (zrt, zebra_router_table_head, + &zrouter.tables) { + info = route_table_get_info(zrt->table); + + if (zvrf != info->zvrf) + continue; + if (zrt->afi != afi || + zrt->safi != SAFI_UNICAST) + continue; + + do_show_ip_route(vty, zvrf_name(zvrf), afi, SAFI_UNICAST, + use_fib, use_json, tag, longer_prefix_p, + supernets_only, type, ospf_instance_id, + zrt->tableid, ctx); + } +} + static int do_show_ip_route(struct vty *vty, const char *vrf_name, afi_t afi, safi_t safi, bool use_fib, bool use_json, route_tag_t tag, const struct prefix *longer_prefix_p, bool supernets_only, int type, - unsigned short ospf_instance_id) + unsigned short ospf_instance_id, uint32_t tableid, + struct route_show_ctx *ctx) { struct route_table *table; struct zebra_vrf *zvrf = NULL; @@ -889,7 +1193,10 @@ static int do_show_ip_route(struct vty *vty, const char *vrf_name, afi_t afi, return CMD_SUCCESS; } - table = zebra_vrf_table(afi, safi, zvrf_id(zvrf)); + if (tableid) + table = zebra_router_find_table(zvrf, tableid, afi, SAFI_UNICAST); + else + table = zebra_vrf_table(afi, safi, zvrf_id(zvrf)); if (!table) { if (use_json) vty_out(vty, "{}\n"); @@ -898,66 +1205,14 @@ static int do_show_ip_route(struct vty *vty, const char *vrf_name, afi_t afi, do_show_route_helper(vty, zvrf, table, afi, use_fib, tag, longer_prefix_p, supernets_only, type, - ospf_instance_id, use_json); - - return CMD_SUCCESS; -} - -DEFPY (show_route_table, - show_route_table_cmd, - "show route table (1-4294967295)$table [json$json]", - SHOW_STR - IP_STR - IP6_STR - "IP routing table\n" - "Table to display\n" - "The table number to display, if available\n" - JSON_STR) -{ - afi_t afi = ipv4 ? AFI_IP : AFI_IP6; - struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT); - struct route_table *t; - - t = zebra_router_find_table(zvrf, table, afi, SAFI_UNICAST); - if (t) - do_show_route_helper(vty, zvrf, t, afi, false, 0, false, false, - 0, 0, !!json); - - return CMD_SUCCESS; -} - -DEFPY (show_route_table_vrf, - show_route_table_vrf_cmd, - "show route table (1-4294967295)$table vrf NAME$vrf_name [json$json]", - SHOW_STR - IP_STR - IP6_STR - "IP routing table\n" - "Table to display\n" - "The table number to display, if available\n" - VRF_CMD_HELP_STR - JSON_STR) -{ - afi_t afi = ipv4 ? AFI_IP : AFI_IP6; - struct zebra_vrf *zvrf; - struct route_table *t; - vrf_id_t vrf_id = VRF_DEFAULT; - - if (vrf_name) - VRF_GET_ID(vrf_id, vrf_name, !!json); - zvrf = zebra_vrf_lookup_by_id(vrf_id); - - t = zebra_router_find_table(zvrf, table, afi, SAFI_UNICAST); - if (t) - do_show_route_helper(vty, zvrf, t, afi, false, 0, false, false, - 0, 0, !!json); + ospf_instance_id, use_json, tableid, ctx); return CMD_SUCCESS; } DEFPY (show_ip_nht, show_ip_nht_cmd, - "show $type [$addr|vrf NAME$vrf_name $addr|vrf all$vrf_all]", + "show $type [$addr|vrf NAME$vrf_name [$addr]|vrf all$vrf_all]", SHOW_STR IP_STR IP6_STR @@ -973,7 +1228,7 @@ DEFPY (show_ip_nht, afi_t afi = ipv4 ? AFI_IP : AFI_IP6; vrf_id_t vrf_id = VRF_DEFAULT; struct prefix prefix, *p = NULL; - rnh_type_t rtype; + enum rnh_type rtype; if (strcmp(type, "nht") == 0) rtype = RNH_NEXTHOP_TYPE; @@ -984,43 +1239,318 @@ DEFPY (show_ip_nht, struct vrf *vrf; struct zebra_vrf *zvrf; - RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) - if ((zvrf = vrf->info) != NULL) { - vty_out(vty, "\nVRF %s:\n", zvrf_name(zvrf)); - zebra_print_rnh_table(zvrf_id(zvrf), afi, vty, - rtype, NULL); + RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) + if ((zvrf = vrf->info) != NULL) { + vty_out(vty, "\nVRF %s:\n", zvrf_name(zvrf)); + zebra_print_rnh_table(zvrf_id(zvrf), afi, vty, + rtype, NULL); + } + return CMD_SUCCESS; + } + if (vrf_name) + VRF_GET_ID(vrf_id, vrf_name, false); + + memset(&prefix, 0, sizeof(prefix)); + if (addr) + p = sockunion2hostprefix(addr, &prefix); + + zebra_print_rnh_table(vrf_id, afi, vty, rtype, p); + return CMD_SUCCESS; +} + +DEFUN (ip_nht_default_route, + ip_nht_default_route_cmd, + "ip nht resolve-via-default", + IP_STR + "Filter Next Hop tracking route resolution\n" + "Resolve via default route\n") +{ + ZEBRA_DECLVAR_CONTEXT(vrf, zvrf); + + if (!zvrf) + return CMD_WARNING; + + if (zvrf->zebra_rnh_ip_default_route) + return CMD_SUCCESS; + + zvrf->zebra_rnh_ip_default_route = 1; + + zebra_evaluate_rnh(zvrf, AFI_IP, 0, RNH_NEXTHOP_TYPE, NULL); + return CMD_SUCCESS; +} + +static void show_nexthop_group_out(struct vty *vty, struct nhg_hash_entry *nhe) +{ + struct nexthop *nexthop = NULL; + struct nhg_connected *rb_node_dep = NULL; + struct nexthop_group *backup_nhg; + + vty_out(vty, "ID: %u\n", nhe->id); + 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)) + vty_out(vty, ", Installed"); + vty_out(vty, "\n"); + } + if (nhe->ifp) + vty_out(vty, " Interface Index: %d\n", nhe->ifp->ifindex); + + if (!zebra_nhg_depends_is_empty(nhe)) { + vty_out(vty, " Depends:"); + frr_each(nhg_connected_tree, &nhe->nhg_depends, rb_node_dep) { + vty_out(vty, " (%u)", rb_node_dep->nhe->id); + } + vty_out(vty, "\n"); + } + + /* Output nexthops */ + for (ALL_NEXTHOPS(nhe->nhg, nexthop)) { + if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + vty_out(vty, " "); + else + /* Make recursive nexthops a bit more clear */ + vty_out(vty, " "); + + show_route_nexthop_helper(vty, NULL, nexthop); + + if (nhe->backup_info == NULL || nhe->backup_info->nhe == NULL) { + if (CHECK_FLAG(nexthop->flags, + NEXTHOP_FLAG_HAS_BACKUP)) + vty_out(vty, " [backup %d]", + nexthop->backup_idx[0]); + + vty_out(vty, "\n"); + continue; + } + + /* TODO -- print more useful backup info */ + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) { + int i; + + vty_out(vty, "[backup"); + for (i = 0; i < nexthop->backup_num; i++) + vty_out(vty, " %d", nexthop->backup_idx[i]); + + vty_out(vty, "]"); + } + + vty_out(vty, "\n"); + } + + /* Output backup nexthops (if any) */ + backup_nhg = zebra_nhg_get_backup_nhg(nhe); + if (backup_nhg) { + vty_out(vty, " Backups:\n"); + + for (ALL_NEXTHOPS_PTR(backup_nhg, nexthop)) { + if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + vty_out(vty, " "); + else + /* Make recursive nexthops a bit more clear */ + vty_out(vty, " "); + + show_route_nexthop_helper(vty, NULL, nexthop); + vty_out(vty, "\n"); + } + } + + if (!zebra_nhg_dependents_is_empty(nhe)) { + vty_out(vty, " Dependents:"); + frr_each(nhg_connected_tree, &nhe->nhg_dependents, + rb_node_dep) { + vty_out(vty, " (%u)", rb_node_dep->nhe->id); + } + vty_out(vty, "\n"); + } + +} + +static int show_nexthop_group_id_cmd_helper(struct vty *vty, uint32_t id) +{ + struct nhg_hash_entry *nhe = NULL; + + nhe = zebra_nhg_lookup_id(id); + + if (nhe) + show_nexthop_group_out(vty, nhe); + else { + vty_out(vty, "Nexthop Group ID: %u does not exist\n", id); + return CMD_WARNING; + } + return CMD_SUCCESS; +} + +/* Helper function for iteration through the hash of nexthop-groups/nhe-s */ + +struct nhe_show_context { + struct vty *vty; + vrf_id_t vrf_id; + afi_t afi; +}; + +static int nhe_show_walker(struct hash_bucket *bucket, void *arg) +{ + struct nhe_show_context *ctx = arg; + struct nhg_hash_entry *nhe; + + nhe = bucket->data; /* We won't be offered NULL buckets */ + + if (ctx->afi && nhe->afi != ctx->afi) + goto done; + + if (ctx->vrf_id && nhe->vrf_id != ctx->vrf_id) + goto done; + + show_nexthop_group_out(ctx->vty, nhe); + +done: + return HASHWALK_CONTINUE; +} + +static void show_nexthop_group_cmd_helper(struct vty *vty, + struct zebra_vrf *zvrf, + afi_t afi) +{ + struct nhe_show_context ctx; + + ctx.vty = vty; + ctx.afi = afi; + ctx.vrf_id = zvrf->vrf->vrf_id; + + hash_walk(zrouter.nhgs_id, nhe_show_walker, &ctx); +} + +static void if_nexthop_group_dump_vty(struct vty *vty, struct interface *ifp) +{ + struct zebra_if *zebra_if = NULL; + struct nhg_connected *rb_node_dep = NULL; + + zebra_if = ifp->info; + + if (!if_nhg_dependents_is_empty(ifp)) { + vty_out(vty, "Interface %s:\n", ifp->name); + + frr_each(nhg_connected_tree, &zebra_if->nhg_dependents, + rb_node_dep) { + vty_out(vty, " "); + show_nexthop_group_out(vty, rb_node_dep->nhe); + } + } +} + +DEFPY (show_interface_nexthop_group, + show_interface_nexthop_group_cmd, + "show interface [IFNAME$if_name] nexthop-group", + SHOW_STR + "Interface status and configuration\n" + "Interface name\n" + "Show Nexthop Groups\n") +{ + struct vrf *vrf = NULL; + struct interface *ifp = NULL; + bool found = false; + + RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { + if (if_name) { + ifp = if_lookup_by_name(if_name, vrf->vrf_id); + if (ifp) { + if_nexthop_group_dump_vty(vty, ifp); + found = true; } - return CMD_SUCCESS; + } else { + FOR_ALL_INTERFACES (vrf, ifp) + if_nexthop_group_dump_vty(vty, ifp); + found = true; + } } - if (vrf_name) - VRF_GET_ID(vrf_id, vrf_name, false); - memset(&prefix, 0, sizeof(prefix)); - if (addr) - p = sockunion2hostprefix(addr, &prefix); + if (!found) { + vty_out(vty, "%% Can't find interface %s\n", if_name); + return CMD_WARNING; + } - zebra_print_rnh_table(vrf_id, afi, vty, rtype, p); return CMD_SUCCESS; } -DEFUN (ip_nht_default_route, - ip_nht_default_route_cmd, - "ip nht resolve-via-default", +DEFPY (show_nexthop_group, + show_nexthop_group_cmd, + "show nexthop-group rib <(0-4294967295)$id|[singleton ] [vrf ]>", + SHOW_STR + "Show Nexthop Groups\n" + "RIB information\n" + "Nexthop Group ID\n" + "Show Singleton Nexthop-Groups\n" IP_STR - "Filter Next Hop tracking route resolution\n" - "Resolve via default route\n") + IP6_STR + VRF_FULL_CMD_HELP_STR) { - ZEBRA_DECLVAR_CONTEXT(vrf, zvrf); - if (!zvrf) + struct zebra_vrf *zvrf = NULL; + afi_t afi = AFI_UNSPEC; + + if (id) + return show_nexthop_group_id_cmd_helper(vty, id); + + if (v4) + afi = AFI_IP; + else if (v6) + afi = AFI_IP6; + + if (!vrf_is_backend_netns() && (vrf_name || vrf_all)) { + vty_out(vty, + "VRF subcommand does not make any sense in l3mdev based vrf's\n"); return CMD_WARNING; + } + + if (vrf_all) { + struct vrf *vrf; + + RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { + struct zebra_vrf *zvrf; + + zvrf = vrf->info; + if (!zvrf) + continue; + + vty_out(vty, "VRF: %s\n", vrf->name); + show_nexthop_group_cmd_helper(vty, zvrf, afi); + } - if (zebra_rnh_ip_default_route) return CMD_SUCCESS; + } + + if (vrf_name) + zvrf = zebra_vrf_lookup_by_name(vrf_name); + else + zvrf = zebra_vrf_lookup_by_name(VRF_DEFAULT_NAME); - zebra_rnh_ip_default_route = 1; + if (!zvrf) { + vty_out(vty, "%% VRF '%s' specified does not exist\n", + vrf_name); + return CMD_WARNING; + } + + show_nexthop_group_cmd_helper(vty, zvrf, afi); + + return CMD_SUCCESS; +} - zebra_evaluate_rnh(zvrf, AFI_IP, 1, RNH_NEXTHOP_TYPE, NULL); +DEFPY_HIDDEN(nexthop_group_use_enable, + nexthop_group_use_enable_cmd, + "[no] zebra nexthop kernel enable", + NO_STR + ZEBRA_STR + "Nexthop configuration \n" + "Configure use of kernel nexthops\n" + "Enable kernel nexthops\n") +{ + zebra_nhg_enable_kernel_nexthops(!no); return CMD_SUCCESS; } @@ -1037,11 +1567,11 @@ DEFUN (no_ip_nht_default_route, if (!zvrf) return CMD_WARNING; - if (!zebra_rnh_ip_default_route) + if (!zvrf->zebra_rnh_ip_default_route) return CMD_SUCCESS; - zebra_rnh_ip_default_route = 0; - zebra_evaluate_rnh(zvrf, AFI_IP, 1, RNH_NEXTHOP_TYPE, NULL); + zvrf->zebra_rnh_ip_default_route = 0; + zebra_evaluate_rnh(zvrf, AFI_IP, 0, RNH_NEXTHOP_TYPE, NULL); return CMD_SUCCESS; } @@ -1057,11 +1587,11 @@ DEFUN (ipv6_nht_default_route, if (!zvrf) return CMD_WARNING; - if (zebra_rnh_ipv6_default_route) + if (zvrf->zebra_rnh_ipv6_default_route) return CMD_SUCCESS; - zebra_rnh_ipv6_default_route = 1; - zebra_evaluate_rnh(zvrf, AFI_IP6, 1, RNH_NEXTHOP_TYPE, NULL); + zvrf->zebra_rnh_ipv6_default_route = 1; + zebra_evaluate_rnh(zvrf, AFI_IP6, 0, RNH_NEXTHOP_TYPE, NULL); return CMD_SUCCESS; } @@ -1079,11 +1609,11 @@ DEFUN (no_ipv6_nht_default_route, if (!zvrf) return CMD_WARNING; - if (!zebra_rnh_ipv6_default_route) + if (!zvrf->zebra_rnh_ipv6_default_route) return CMD_SUCCESS; - zebra_rnh_ipv6_default_route = 0; - zebra_evaluate_rnh(zvrf, AFI_IP6, 1, RNH_NEXTHOP_TYPE, NULL); + zvrf->zebra_rnh_ipv6_default_route = 0; + zebra_evaluate_rnh(zvrf, AFI_IP6, 0, RNH_NEXTHOP_TYPE, NULL); return CMD_SUCCESS; } @@ -1091,7 +1621,8 @@ DEFPY (show_route, show_route_cmd, "show\ <\ - ip$ipv4 [vrf ]\ + ip$ipv4 [table <(1-4294967295)$table|all$table_all>]\ + [vrf ]\ [{\ tag (1-4294967295)\ |A.B.C.D/M$prefix longer-prefixes\ @@ -1101,7 +1632,8 @@ DEFPY (show_route, " FRR_IP_REDIST_STR_ZEBRA "$type_str\ |ospf$type_str (1-65535)$ospf_instance_id\ >]\ - |ipv6$ipv6 [vrf ]\ + |ipv6$ipv6 [table <(1-4294967295)$table|all$table_all>]\ + [vrf ]\ [{\ tag (1-4294967295)\ |X:X::X:X/M$prefix longer-prefixes\ @@ -1113,6 +1645,9 @@ DEFPY (show_route, IP_STR "IP forwarding table\n" "IP routing table\n" + "Table to display\n" + "The table number to display\n" + "All tables\n" VRF_FULL_CMD_HELP_STR "Show only routes with tag\n" "Tag value\n" @@ -1125,6 +1660,9 @@ DEFPY (show_route, IPV6_STR "IP forwarding table\n" "IP routing table\n" + "Table to display\n" + "The table number to display\n" + "All tables\n" VRF_FULL_CMD_HELP_STR "Show only routes with tag\n" "Tag value\n" @@ -1136,7 +1674,22 @@ DEFPY (show_route, afi_t afi = ipv4 ? AFI_IP : AFI_IP6; struct vrf *vrf; int type = 0; - + struct zebra_vrf *zvrf; + struct route_show_ctx ctx = { + .multi = vrf_all || table_all, + }; + + if (!vrf_is_backend_netns()) { + if ((vrf_all || vrf_name) && (table || table_all)) { + if (!!json) + vty_out(vty, "{}\n"); + else { + vty_out(vty, "Linux vrf backend already points to table id\n"); + vty_out(vty, "Either remove table parameter or vrf parameter\n"); + } + return CMD_SUCCESS; + } + } if (type_str) { type = proto_redistnum(afi, type_str); if (type < 0) { @@ -1147,17 +1700,23 @@ DEFPY (show_route, if (vrf_all) { RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { - struct zebra_vrf *zvrf; - struct route_table *table; - if ((zvrf = vrf->info) == NULL - || (table = zvrf->table[afi][SAFI_UNICAST]) == NULL) + || (zvrf->table[afi][SAFI_UNICAST] == NULL)) continue; - do_show_ip_route( - vty, zvrf_name(zvrf), afi, SAFI_UNICAST, !!fib, - !!json, tag, prefix_str ? prefix : NULL, - !!supernets_only, type, ospf_instance_id); + if (table_all) + do_show_ip_route_all(vty, zvrf, afi, !!fib, + !!json, tag, + prefix_str ? prefix : NULL, + !!supernets_only, type, + ospf_instance_id, &ctx); + else + do_show_ip_route(vty, zvrf_name(zvrf), afi, + SAFI_UNICAST, !!fib, !!json, + tag, + prefix_str ? prefix : NULL, + !!supernets_only, type, + ospf_instance_id, table, &ctx); } } else { vrf_id_t vrf_id = VRF_DEFAULT; @@ -1165,47 +1724,76 @@ DEFPY (show_route, if (vrf_name) VRF_GET_ID(vrf_id, vrf_name, !!json); vrf = vrf_lookup_by_id(vrf_id); - do_show_ip_route(vty, vrf->name, afi, SAFI_UNICAST, !!fib, - !!json, tag, prefix_str ? prefix : NULL, - !!supernets_only, type, ospf_instance_id); + if (vrf) + zvrf = vrf->info; + if (!vrf || !zvrf) + return CMD_SUCCESS; + + if (table_all) + do_show_ip_route_all(vty, zvrf, afi, !!fib, !!json, tag, + prefix_str ? prefix : NULL, + !!supernets_only, type, + ospf_instance_id, &ctx); + else + do_show_ip_route(vty, vrf->name, afi, SAFI_UNICAST, + !!fib, !!json, tag, + prefix_str ? prefix : NULL, + !!supernets_only, type, + ospf_instance_id, table, &ctx); } return CMD_SUCCESS; } +ALIAS_HIDDEN (show_route, + show_ro_cmd, + "show ro", + SHOW_STR + IP_STR + IPV6_STR + "IP routing table\n"); + + DEFPY (show_route_detail, show_route_detail_cmd, "show\ <\ - ip$ipv4 route [vrf ]\ + ip$ipv4 [vrf ]\ <\ A.B.C.D$address\ |A.B.C.D/M$prefix\ >\ - |ipv6$ipv6 route [vrf ]\ + |ipv6$ipv6 [vrf ]\ <\ X:X::X:X$address\ |X:X::X:X/M$prefix\ >\ >\ - [json$json]", + [json$json] [nexthop-group$ng]", SHOW_STR IP_STR + "IPv6 forwarding table\n" "IP routing table\n" VRF_FULL_CMD_HELP_STR "Network in the IP routing table to display\n" "IP prefix /, e.g., 35.0.0.0/8\n" IP6_STR - "IP routing table\n" + "IPv6 forwarding table\n" + "IPv6 routing table\n" VRF_FULL_CMD_HELP_STR "IPv6 Address\n" "IPv6 prefix\n" - JSON_STR) + JSON_STR + "Nexthop Group Information\n") { afi_t afi = ipv4 ? AFI_IP : AFI_IP6; struct route_table *table; struct prefix p; struct route_node *rn; + bool use_fib = !!fib; + rib_dest_t *dest; + bool network_found = false; + bool show_ng = !!ng; if (address_str) prefix_str = address_str; @@ -1231,13 +1819,35 @@ DEFPY (show_route_detail, continue; } + dest = rib_dest_from_rnode(rn); + if (use_fib && !dest->selected_fib) { + route_unlock_node(rn); + continue; + } + + network_found = true; if (json) - vty_show_ip_route_detail_json(vty, rn); + vty_show_ip_route_detail_json(vty, rn, use_fib); else - vty_show_ip_route_detail(vty, rn, 0); + vty_show_ip_route_detail(vty, rn, 0, use_fib, + show_ng); route_unlock_node(rn); } + + if (!network_found) { + if (json) + vty_out(vty, "{}\n"); + else { + if (use_fib) + vty_out(vty, + "%% Network not in FIB\n"); + else + vty_out(vty, + "%% Network not in RIB\n"); + } + return CMD_WARNING; + } } else { vrf_id_t vrf_id = VRF_DEFAULT; @@ -1249,20 +1859,30 @@ DEFPY (show_route_detail, return CMD_SUCCESS; rn = route_node_match(table, &p); - if (!rn) { - vty_out(vty, "%% Network not in table\n"); - return CMD_WARNING; - } - if (!address_str && rn->p.prefixlen != p.prefixlen) { - vty_out(vty, "%% Network not in table\n"); - route_unlock_node(rn); + if (rn) + dest = rib_dest_from_rnode(rn); + + if (!rn || (!address_str && rn->p.prefixlen != p.prefixlen) || + (use_fib && dest && !dest->selected_fib)) { + if (json) + vty_out(vty, "{}\n"); + else { + if (use_fib) + vty_out(vty, + "%% Network not in FIB\n"); + else + vty_out(vty, + "%% Network not in table\n"); + } + if (rn) + route_unlock_node(rn); return CMD_WARNING; } if (json) - vty_show_ip_route_detail_json(vty, rn); + vty_show_ip_route_detail_json(vty, rn, use_fib); else - vty_show_ip_route_detail(vty, rn, 0); + vty_show_ip_route_detail(vty, rn, 0, use_fib, show_ng); route_unlock_node(rn); } @@ -1272,41 +1892,47 @@ DEFPY (show_route_detail, DEFPY (show_route_summary, show_route_summary_cmd, - "show\ - <\ - ip$ipv4 route [vrf ]\ - summary [prefix$prefix]\ - |ipv6$ipv6 route [vrf ]\ - summary [prefix$prefix]\ - >", + "show route [vrf ] \ + summary [table (1-4294967295)$table_id] [prefix$prefix] [json]", SHOW_STR IP_STR - "IP routing table\n" - VRF_FULL_CMD_HELP_STR - "Summary of all routes\n" - "Prefix routes\n" IP6_STR "IP routing table\n" VRF_FULL_CMD_HELP_STR "Summary of all routes\n" - "Prefix routes\n") + "Table to display summary for\n" + "The table number\n" + "Prefix routes\n" + JSON_STR) { afi_t afi = ipv4 ? AFI_IP : AFI_IP6; struct route_table *table; + bool uj = use_json(argc, argv); if (vrf_all) { struct vrf *vrf; struct zebra_vrf *zvrf; RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { - if ((zvrf = vrf->info) == NULL - || (table = zvrf->table[afi][SAFI_UNICAST]) == NULL) + if ((zvrf = vrf->info) == NULL) + continue; + + if (table_id == 0) + table = zebra_vrf_table(afi, SAFI_UNICAST, + zvrf->vrf->vrf_id); + else + table = zebra_vrf_lookup_table_with_table_id( + afi, SAFI_UNICAST, zvrf->vrf->vrf_id, + table_id); + + if (!table) continue; if (prefix) - vty_show_ip_route_summary_prefix(vty, table); + vty_show_ip_route_summary_prefix(vty, table, + uj); else - vty_show_ip_route_summary(vty, table); + vty_show_ip_route_summary(vty, table, uj); } } else { vrf_id_t vrf_id = VRF_DEFAULT; @@ -1314,21 +1940,25 @@ DEFPY (show_route_summary, if (vrf_name) VRF_GET_ID(vrf_id, vrf_name, false); - table = zebra_vrf_table(afi, SAFI_UNICAST, vrf_id); + if (table_id == 0) + table = zebra_vrf_table(afi, SAFI_UNICAST, vrf_id); + else + table = zebra_vrf_lookup_table_with_table_id( + afi, SAFI_UNICAST, vrf_id, table_id); if (!table) return CMD_SUCCESS; if (prefix) - vty_show_ip_route_summary_prefix(vty, table); + vty_show_ip_route_summary_prefix(vty, table, uj); else - vty_show_ip_route_summary(vty, table); + vty_show_ip_route_summary(vty, table, uj); } return CMD_SUCCESS; } static void vty_show_ip_route_summary(struct vty *vty, - struct route_table *table) + struct route_table *table, bool use_json) { struct route_node *rn; struct route_entry *re; @@ -1338,9 +1968,19 @@ static void vty_show_ip_route_summary(struct vty *vty, uint32_t fib_cnt[ZEBRA_ROUTE_TOTAL + 1]; uint32_t i; uint32_t is_ibgp; + json_object *json_route_summary = NULL; + json_object *json_route_routes = NULL; memset(&rib_cnt, 0, sizeof(rib_cnt)); memset(&fib_cnt, 0, sizeof(fib_cnt)); + + if (use_json) { + json_route_summary = json_object_new_object(); + json_route_routes = json_object_new_array(); + json_object_object_add(json_route_summary, "routes", + json_route_routes); + } + for (rn = route_top(table); rn; rn = srcdest_route_next(rn)) RNODE_FOREACH_RE (rn, re) { is_ibgp = (re->type == ZEBRA_ROUTE_BGP @@ -1362,30 +2002,93 @@ static void vty_show_ip_route_summary(struct vty *vty, } } - vty_out(vty, "%-20s %-20s %s (vrf %s)\n", "Route Source", "Routes", - "FIB", zvrf_name(((rib_table_info_t *)route_table_get_info(table))->zvrf)); + if (!use_json) + vty_out(vty, "%-20s %-20s %s (vrf %s)\n", "Route Source", + "Routes", "FIB", + zvrf_name(((struct rib_table_info *) + route_table_get_info(table)) + ->zvrf)); for (i = 0; i < ZEBRA_ROUTE_MAX; i++) { if ((rib_cnt[i] > 0) || (i == ZEBRA_ROUTE_BGP && rib_cnt[ZEBRA_ROUTE_IBGP] > 0)) { if (i == ZEBRA_ROUTE_BGP) { - vty_out(vty, "%-20s %-20d %-20d \n", "ebgp", - rib_cnt[ZEBRA_ROUTE_BGP], - fib_cnt[ZEBRA_ROUTE_BGP]); - vty_out(vty, "%-20s %-20d %-20d \n", "ibgp", - rib_cnt[ZEBRA_ROUTE_IBGP], - fib_cnt[ZEBRA_ROUTE_IBGP]); - } else - vty_out(vty, "%-20s %-20d %-20d \n", - zebra_route_string(i), rib_cnt[i], - fib_cnt[i]); + if (use_json) { + json_object *json_route_ebgp = + json_object_new_object(); + + json_object_int_add( + json_route_ebgp, "fib", + fib_cnt[ZEBRA_ROUTE_BGP]); + json_object_int_add( + json_route_ebgp, "rib", + rib_cnt[ZEBRA_ROUTE_BGP]); + json_object_string_add(json_route_ebgp, + "type", "ebgp"); + json_object_array_add(json_route_routes, + json_route_ebgp); + + json_object *json_route_ibgp = + json_object_new_object(); + + json_object_int_add( + json_route_ibgp, "fib", + fib_cnt[ZEBRA_ROUTE_IBGP]); + json_object_int_add( + json_route_ibgp, "rib", + rib_cnt[ZEBRA_ROUTE_IBGP]); + json_object_string_add(json_route_ibgp, + "type", "ibgp"); + json_object_array_add(json_route_routes, + json_route_ibgp); + } else { + vty_out(vty, "%-20s %-20d %-20d \n", + "ebgp", + rib_cnt[ZEBRA_ROUTE_BGP], + fib_cnt[ZEBRA_ROUTE_BGP]); + vty_out(vty, "%-20s %-20d %-20d \n", + "ibgp", + rib_cnt[ZEBRA_ROUTE_IBGP], + fib_cnt[ZEBRA_ROUTE_IBGP]); + } + } else { + if (use_json) { + json_object *json_route_type = + json_object_new_object(); + + json_object_int_add(json_route_type, + "fib", fib_cnt[i]); + json_object_int_add(json_route_type, + "rib", rib_cnt[i]); + json_object_string_add( + json_route_type, "type", + zebra_route_string(i)); + json_object_array_add(json_route_routes, + json_route_type); + } else + vty_out(vty, "%-20s %-20d %-20d \n", + zebra_route_string(i), + rib_cnt[i], fib_cnt[i]); + } } } - vty_out(vty, "------\n"); - vty_out(vty, "%-20s %-20d %-20d \n", "Totals", - rib_cnt[ZEBRA_ROUTE_TOTAL], fib_cnt[ZEBRA_ROUTE_TOTAL]); - vty_out(vty, "\n"); + if (use_json) { + json_object_int_add(json_route_summary, "routesTotal", + rib_cnt[ZEBRA_ROUTE_TOTAL]); + json_object_int_add(json_route_summary, "routesTotalFib", + fib_cnt[ZEBRA_ROUTE_TOTAL]); + + vty_out(vty, "%s\n", + json_object_to_json_string_ext( + json_route_summary, JSON_C_TO_STRING_PRETTY)); + json_object_free(json_route_summary); + } else { + vty_out(vty, "------\n"); + vty_out(vty, "%-20s %-20d %-20d \n", "Totals", + rib_cnt[ZEBRA_ROUTE_TOTAL], fib_cnt[ZEBRA_ROUTE_TOTAL]); + vty_out(vty, "\n"); + } } /* @@ -1396,7 +2099,8 @@ static void vty_show_ip_route_summary(struct vty *vty, * */ static void vty_show_ip_route_summary_prefix(struct vty *vty, - struct route_table *table) + struct route_table *table, + bool use_json) { struct route_node *rn; struct route_entry *re; @@ -1407,9 +2111,19 @@ static void vty_show_ip_route_summary_prefix(struct vty *vty, uint32_t fib_cnt[ZEBRA_ROUTE_TOTAL + 1]; uint32_t i; int cnt; + json_object *json_route_summary = NULL; + json_object *json_route_routes = NULL; memset(&rib_cnt, 0, sizeof(rib_cnt)); memset(&fib_cnt, 0, sizeof(fib_cnt)); + + if (use_json) { + json_route_summary = json_object_new_object(); + json_route_routes = json_object_new_array(); + json_object_object_add(json_route_summary, "prefixRoutes", + json_route_routes); + } + for (rn = route_top(table); rn; rn = srcdest_route_next(rn)) RNODE_FOREACH_RE (rn, re) { @@ -1421,7 +2135,7 @@ static void vty_show_ip_route_summary_prefix(struct vty *vty, fib_cnt[ZEBRA_ROUTE_TOTAL]++; fib_cnt[re->type]++; } - for (nexthop = re->ng.nexthop; (!cnt && nexthop); + for (nexthop = re->nhe->nhg.nexthop; (!cnt && nexthop); nexthop = nexthop->next) { cnt++; rib_cnt[ZEBRA_ROUTE_TOTAL]++; @@ -1436,32 +2150,96 @@ static void vty_show_ip_route_summary_prefix(struct vty *vty, } } - vty_out(vty, "%-20s %-20s %s (vrf %s)\n", "Route Source", - "Prefix Routes", "FIB", - zvrf_name(((rib_table_info_t *)route_table_get_info(table))->zvrf)); + if (!use_json) + vty_out(vty, "%-20s %-20s %s (vrf %s)\n", "Route Source", + "Prefix Routes", "FIB", + zvrf_name(((struct rib_table_info *) + route_table_get_info(table)) + ->zvrf)); for (i = 0; i < ZEBRA_ROUTE_MAX; i++) { if (rib_cnt[i] > 0) { if (i == ZEBRA_ROUTE_BGP) { - vty_out(vty, "%-20s %-20d %-20d \n", "ebgp", - rib_cnt[ZEBRA_ROUTE_BGP] - - rib_cnt[ZEBRA_ROUTE_IBGP], - fib_cnt[ZEBRA_ROUTE_BGP] - - fib_cnt[ZEBRA_ROUTE_IBGP]); - vty_out(vty, "%-20s %-20d %-20d \n", "ibgp", - rib_cnt[ZEBRA_ROUTE_IBGP], - fib_cnt[ZEBRA_ROUTE_IBGP]); - } else - vty_out(vty, "%-20s %-20d %-20d \n", - zebra_route_string(i), rib_cnt[i], - fib_cnt[i]); + if (use_json) { + json_object *json_route_ebgp = + json_object_new_object(); + + json_object_int_add( + json_route_ebgp, "fib", + fib_cnt[ZEBRA_ROUTE_BGP] + - fib_cnt[ZEBRA_ROUTE_IBGP]); + json_object_int_add( + json_route_ebgp, "rib", + rib_cnt[ZEBRA_ROUTE_BGP] + - rib_cnt[ZEBRA_ROUTE_IBGP]); + json_object_string_add(json_route_ebgp, + "type", "ebgp"); + json_object_array_add(json_route_routes, + json_route_ebgp); + + json_object *json_route_ibgp = + json_object_new_object(); + + json_object_int_add( + json_route_ibgp, "fib", + fib_cnt[ZEBRA_ROUTE_IBGP]); + json_object_int_add( + json_route_ibgp, "rib", + rib_cnt[ZEBRA_ROUTE_IBGP]); + json_object_string_add(json_route_ibgp, + "type", "ibgp"); + json_object_array_add(json_route_routes, + json_route_ibgp); + } else { + vty_out(vty, "%-20s %-20d %-20d \n", + "ebgp", + rib_cnt[ZEBRA_ROUTE_BGP] + - rib_cnt[ZEBRA_ROUTE_IBGP], + fib_cnt[ZEBRA_ROUTE_BGP] + - fib_cnt[ZEBRA_ROUTE_IBGP]); + vty_out(vty, "%-20s %-20d %-20d \n", + "ibgp", + rib_cnt[ZEBRA_ROUTE_IBGP], + fib_cnt[ZEBRA_ROUTE_IBGP]); + } + } else { + if (use_json) { + json_object *json_route_type = + json_object_new_object(); + + json_object_int_add(json_route_type, + "fib", fib_cnt[i]); + json_object_int_add(json_route_type, + "rib", rib_cnt[i]); + json_object_string_add( + json_route_type, "type", + zebra_route_string(i)); + json_object_array_add(json_route_routes, + json_route_type); + } else + vty_out(vty, "%-20s %-20d %-20d \n", + zebra_route_string(i), + rib_cnt[i], fib_cnt[i]); + } } } - vty_out(vty, "------\n"); - vty_out(vty, "%-20s %-20d %-20d \n", "Totals", - rib_cnt[ZEBRA_ROUTE_TOTAL], fib_cnt[ZEBRA_ROUTE_TOTAL]); - vty_out(vty, "\n"); + if (use_json) { + json_object_int_add(json_route_summary, "prefixRoutesTotal", + rib_cnt[ZEBRA_ROUTE_TOTAL]); + json_object_int_add(json_route_summary, "prefixRoutesTotalFib", + fib_cnt[ZEBRA_ROUTE_TOTAL]); + + vty_out(vty, "%s\n", + json_object_to_json_string_ext( + json_route_summary, JSON_C_TO_STRING_PRETTY)); + json_object_free(json_route_summary); + } else { + vty_out(vty, "------\n"); + vty_out(vty, "%-20s %-20d %-20d \n", "Totals", + rib_cnt[ZEBRA_ROUTE_TOTAL], fib_cnt[ZEBRA_ROUTE_TOTAL]); + vty_out(vty, "\n"); + } } /* @@ -1496,7 +2274,7 @@ DEFUN (show_ipv6_mroute, vty_out(vty, SHOW_ROUTE_V6_HEADER); first = 0; } - vty_show_ip_route(vty, rn, re, NULL); + vty_show_ip_route(vty, rn, re, NULL, false); } return CMD_SUCCESS; } @@ -1528,7 +2306,7 @@ DEFUN (show_ipv6_mroute_vrf_all, vty_out(vty, SHOW_ROUTE_V6_HEADER); first = 0; } - vty_show_ip_route(vty, rn, re, NULL); + vty_show_ip_route(vty, rn, re, NULL, false); } } return CMD_SUCCESS; @@ -1598,10 +2376,8 @@ DEFUN (default_vrf_vni_mapping, "VNI-ID\n" "Prefix routes only \n") { - int ret = 0; - char err[ERR_STR_SZ]; + char xpath[XPATH_MAXLEN]; struct zebra_vrf *zvrf = NULL; - vni_t vni = strtoul(argv[1]->arg, NULL, 10); int filter = 0; zvrf = vrf_info_lookup(VRF_DEFAULT); @@ -1611,25 +2387,35 @@ DEFUN (default_vrf_vni_mapping, if (argc == 3) filter = 1; - ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, - filter, 1); - if (ret != 0) { - vty_out(vty, "%s\n", err); - return CMD_WARNING; + snprintf(xpath, sizeof(xpath), FRR_VRF_KEY_XPATH "/frr-zebra:zebra", + VRF_DEFAULT_NAME); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + snprintf(xpath, sizeof(xpath), + FRR_VRF_KEY_XPATH "/frr-zebra:zebra/l3vni-id", + VRF_DEFAULT_NAME); + nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, argv[1]->arg); + + if (filter) { + snprintf(xpath, sizeof(xpath), + FRR_VRF_KEY_XPATH "/frr-zebra:zebra/prefix-only", + VRF_DEFAULT_NAME); + nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, "true"); } - return CMD_SUCCESS; + return nb_cli_apply_changes(vty, NULL); } DEFUN (no_default_vrf_vni_mapping, no_default_vrf_vni_mapping_cmd, - "no vni " CMD_VNI_RANGE, + "no vni " CMD_VNI_RANGE "[prefix-routes-only]", NO_STR "VNI corresponding to DEFAULT VRF\n" - "VNI-ID") + "VNI-ID\n" + "Prefix routes only \n") { - int ret = 0; - char err[ERR_STR_SZ]; + char xpath[XPATH_MAXLEN]; + int filter = 0; vni_t vni = strtoul(argv[2]->arg, NULL, 10); struct zebra_vrf *zvrf = NULL; @@ -1637,13 +2423,32 @@ DEFUN (no_default_vrf_vni_mapping, if (!zvrf) return CMD_WARNING; - ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, 0, 0); - if (ret != 0) { - vty_out(vty, "%s\n", err); + if (argc == 4) + filter = 1; + + if (zvrf->l3vni != vni) { + vty_out(vty, "VNI %d doesn't exist in VRF: %s \n", vni, + zvrf->vrf->name); return CMD_WARNING; } - return CMD_SUCCESS; + snprintf(xpath, sizeof(xpath), + FRR_VRF_KEY_XPATH "/frr-zebra:zebra/l3vni-id", + VRF_DEFAULT_NAME); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, argv[2]->arg); + + if (filter) { + snprintf(xpath, sizeof(xpath), + FRR_VRF_KEY_XPATH "/frr-zebra:zebra/prefix-only", + VRF_DEFAULT_NAME); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, "true"); + } + + snprintf(xpath, sizeof(xpath), FRR_VRF_KEY_XPATH "/frr-zebra:zebra", + VRF_DEFAULT_NAME); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); } DEFUN (vrf_vni_mapping, @@ -1653,12 +2458,9 @@ DEFUN (vrf_vni_mapping, "VNI-ID\n" "prefix-routes-only\n") { - int ret = 0; int filter = 0; ZEBRA_DECLVAR_CONTEXT(vrf, zvrf); - vni_t vni = strtoul(argv[1]->arg, NULL, 10); - char err[ERR_STR_SZ]; assert(vrf); assert(zvrf); @@ -1666,16 +2468,15 @@ DEFUN (vrf_vni_mapping, if (argc == 3) filter = 1; - /* Mark as having FRR configuration */ - vrf_set_user_cfged(vrf); - ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, - filter, 1); - if (ret != 0) { - vty_out(vty, "%s\n", err); - return CMD_WARNING; - } + nb_cli_enqueue_change(vty, "./frr-zebra:zebra", NB_OP_CREATE, NULL); + nb_cli_enqueue_change(vty, "./frr-zebra:zebra/l3vni-id", NB_OP_MODIFY, + argv[1]->arg); - return CMD_SUCCESS; + if (filter) + nb_cli_enqueue_change(vty, "./frr-zebra:zebra/prefix-only", + NB_OP_MODIFY, "true"); + + return nb_cli_apply_changes(vty, NULL); } DEFUN (no_vrf_vni_mapping, @@ -1686,12 +2487,10 @@ DEFUN (no_vrf_vni_mapping, "VNI-ID\n" "prefix-routes-only\n") { - int ret = 0; int filter = 0; - char err[ERR_STR_SZ]; - vni_t vni = strtoul(argv[2]->arg, NULL, 10); ZEBRA_DECLVAR_CONTEXT(vrf, zvrf); + vni_t vni = strtoul(argv[2]->arg, NULL, 10); assert(vrf); assert(zvrf); @@ -1699,18 +2498,22 @@ DEFUN (no_vrf_vni_mapping, if (argc == 4) filter = 1; - ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, - ERR_STR_SZ, filter, 0); - if (ret != 0) { - vty_out(vty, "%s\n", err); + if (zvrf->l3vni != vni) { + vty_out(vty, "VNI %d doesn't exist in VRF: %s \n", vni, + zvrf->vrf->name); return CMD_WARNING; } - /* If no other FRR config for this VRF, mark accordingly. */ - if (!zebra_vrf_has_config(zvrf)) - vrf_reset_user_cfged(vrf); + nb_cli_enqueue_change(vty, "./frr-zebra:zebra/l3vni-id", NB_OP_DESTROY, + argv[2]->arg); - return CMD_SUCCESS; + if (filter) + nb_cli_enqueue_change(vty, "./frr-zebra:zebra/prefix-only", + NB_OP_DESTROY, "true"); + + nb_cli_enqueue_change(vty, "./frr-zebra:zebra", NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); } /* show vrf */ @@ -1768,6 +2571,81 @@ DEFUN (show_evpn_global, return CMD_SUCCESS; } +DEFPY(show_evpn_es, + show_evpn_es_cmd, + "show evpn es [NAME$esi_str] [json$json] [detail$detail]", + SHOW_STR + "EVPN\n" + "Ethernet Segment\n" + "ES ID\n" + JSON_STR + "Detailed information\n") +{ + esi_t esi; + bool uj = !!json; + + if (esi_str) { + if (!str_to_esi(esi_str, &esi)) { + vty_out(vty, "%% Malformed ESI\n"); + return CMD_WARNING; + } + zebra_evpn_es_show_esi(vty, uj, &esi); + } else { + if (detail) + zebra_evpn_es_show_detail(vty, uj); + else + zebra_evpn_es_show(vty, uj); + } + + return CMD_SUCCESS; +} + +DEFPY(show_evpn_es_evi, + show_evpn_es_evi_cmd, + "show evpn es-evi [vni (1-16777215)$vni] [json$json] [detail$detail]", + SHOW_STR + "EVPN\n" + "Ethernet Segment per EVI\n" + "VxLAN Network Identifier\n" + "VNI\n" + JSON_STR + "Detailed information\n") +{ + bool uj = !!json; + bool ud = !!detail; + + if (vni) + zebra_evpn_es_evi_show_vni(vty, uj, vni, ud); + else + zebra_evpn_es_evi_show(vty, uj, ud); + + return CMD_SUCCESS; +} + +DEFPY(show_evpn_access_vlan, + show_evpn_access_vlan_cmd, + "show evpn access-vlan [(1-4094)$vid] [json$json] [detail$detail]", + SHOW_STR + "EVPN\n" + "Access VLANs\n" + "VLAN ID\n" + JSON_STR + "Detailed information\n") +{ + bool uj = !!json; + + if (vid) { + zebra_evpn_acc_vl_show_vid(vty, uj, vid); + } else { + if (detail) + zebra_evpn_acc_vl_show_detail(vty, uj); + else + zebra_evpn_acc_vl_show(vty, uj); + } + + return CMD_SUCCESS; +} + DEFUN (show_evpn_vni, show_evpn_vni_cmd, "show evpn vni [json]", @@ -1815,7 +2693,7 @@ DEFUN (show_evpn_vni_vni, vni = strtoul(argv[3]->arg, NULL, 10); zvrf = zebra_vrf_get_evpn(); - zebra_vxlan_print_vni(vty, zvrf, vni, uj); + zebra_vxlan_print_vni(vty, zvrf, vni, uj, NULL); return CMD_SUCCESS; } @@ -2106,7 +2984,7 @@ DEFPY (show_evpn_mac_vni_all_dad, DEFPY (show_evpn_mac_vni_dad, show_evpn_mac_vni_dad_cmd, - "show evpn mac vni " CMD_VNI_RANGE " duplicate" "[json]", + "show evpn mac vni " CMD_VNI_RANGE " duplicate [json]", SHOW_STR "EVPN\n" "MAC addresses\n" @@ -2116,10 +2994,8 @@ DEFPY (show_evpn_mac_vni_dad, JSON_STR) { struct zebra_vrf *zvrf; - vni_t vni; bool uj = use_json(argc, argv); - vni = strtoul(argv[4]->arg, NULL, 10); zvrf = zebra_vrf_get_evpn(); zebra_vxlan_print_macs_vni_dad(vty, zvrf, vni, uj); @@ -2129,7 +3005,7 @@ DEFPY (show_evpn_mac_vni_dad, DEFPY (show_evpn_neigh_vni_dad, show_evpn_neigh_vni_dad_cmd, - "show evpn arp-cache vni " CMD_VNI_RANGE "duplicate" "[json]", + "show evpn arp-cache vni " CMD_VNI_RANGE "duplicate [json]", SHOW_STR "EVPN\n" "ARP and ND cache\n" @@ -2139,10 +3015,8 @@ DEFPY (show_evpn_neigh_vni_dad, JSON_STR) { struct zebra_vrf *zvrf; - vni_t vni; bool uj = use_json(argc, argv); - vni = strtoul(argv[4]->arg, NULL, 10); zvrf = zebra_vrf_get_evpn(); zebra_vxlan_print_neigh_vni_dad(vty, zvrf, vni, uj); return CMD_SUCCESS; @@ -2321,7 +3195,7 @@ DEFUN (show_pbr_iptable, DEFPY (clear_evpn_dup_addr, clear_evpn_dup_addr_cmd, - "clear evpn dup-addr vni ]>", + "clear evpn dup-addr vni ]>", CLEAR_STR "EVPN\n" "Duplicate address \n" @@ -2334,22 +3208,29 @@ DEFPY (clear_evpn_dup_addr, "IPv4 address\n" "IPv6 address\n") { - struct zebra_vrf *zvrf; - vni_t vni = 0; struct ipaddr host_ip = {.ipa_type = IPADDR_NONE }; - struct ethaddr mac_addr; int ret = CMD_SUCCESS; + struct list *input; + struct yang_data *yang_dup = NULL, *yang_dup_ip = NULL, + *yang_dup_mac = NULL; - zvrf = zebra_vrf_get_evpn(); - if (vni_val) { - vni = strtoul(vni_val, NULL, 10); - - if (mac_val) { - prefix_str2mac(mac_val, &mac_addr); - ret = zebra_vxlan_clear_dup_detect_vni_mac(vty, zvrf, - vni, - &mac_addr); - } else if (ip) { + input = list_new(); + + if (!vni_str) { + yang_dup = yang_data_new( + "/frr-zebra:clear-evpn-dup-addr/input/clear-dup-choice", + "all-case"); + } else { + yang_dup = yang_data_new_uint32( + "/frr-zebra:clear-evpn-dup-addr/input/clear-dup-choice/single-case/vni-id", + vni); + if (!is_zero_mac(&mac->eth_addr)) { + yang_dup_mac = yang_data_new_mac( + "/frr-zebra:clear-evpn-dup-addr/input/clear-dup-choice/single-case/vni-id/mac-addr", + &mac->eth_addr); + if (yang_dup_mac) + listnode_add(input, yang_dup_mac); + } else if (ip) { if (sockunion_family(ip) == AF_INET) { host_ip.ipa_type = IPADDR_V4; host_ip.ipaddr_v4.s_addr = sockunion2ip(ip); @@ -2358,16 +3239,23 @@ DEFPY (clear_evpn_dup_addr, memcpy(&host_ip.ipaddr_v6, &ip->sin6.sin6_addr, sizeof(struct in6_addr)); } - ret = zebra_vxlan_clear_dup_detect_vni_ip(vty, zvrf, - vni, - &host_ip); - } else - ret = zebra_vxlan_clear_dup_detect_vni(vty, zvrf, vni); - } else { - ret = zebra_vxlan_clear_dup_detect_vni_all(vty, zvrf); + yang_dup_ip = yang_data_new_ip( + "/frr-zebra:clear-evpn-dup-addr/input/clear-dup-choice/single-case/vni-id/vni-ipaddr", + &host_ip); + + if (yang_dup_ip) + listnode_add(input, yang_dup_ip); + } + } + + if (yang_dup) { + listnode_add(input, yang_dup); + ret = nb_cli_rpc("/frr-zebra:clear-evpn-dup-addr", input, NULL); } + list_delete(&input); + return ret; } @@ -2376,7 +3264,7 @@ static int zebra_ip_config(struct vty *vty) { int write = 0; - write += zebra_import_table_config(vty); + write += zebra_import_table_config(vty, VRF_DEFAULT); return write; } @@ -2423,7 +3311,8 @@ DEFUN (ip_zebra_import_table_distance, return CMD_WARNING; } - ret = zebra_import_table(AFI_IP, table_id, distance, rmap, 1); + ret = zebra_import_table(AFI_IP, VRF_DEFAULT, table_id, + distance, rmap, 1); if (rmap) XFREE(MTYPE_ROUTE_MAP_NAME, rmap); @@ -2514,10 +3403,10 @@ DEFUN (no_ip_zebra_import_table, return CMD_WARNING; } - if (!is_zebra_import_table_enabled(AFI_IP, table_id)) + if (!is_zebra_import_table_enabled(AFI_IP, VRF_DEFAULT, table_id)) return CMD_SUCCESS; - return (zebra_import_table(AFI_IP, table_id, 0, NULL, 0)); + return (zebra_import_table(AFI_IP, VRF_DEFAULT, table_id, 0, NULL, 0)); } static int config_write_protocol(struct vty *vty) @@ -2525,12 +3414,6 @@ static int config_write_protocol(struct vty *vty) if (allow_delete) vty_out(vty, "allow-external-route-update\n"); - if (zebra_rnh_ip_default_route) - vty_out(vty, "ip nht resolve-via-default\n"); - - if (zebra_rnh_ipv6_default_route) - vty_out(vty, "ipv6 nht resolve-via-default\n"); - if (zrouter.ribq->spec.hold != ZEBRA_RIB_PROCESS_HOLD_TIME) vty_out(vty, "zebra work-queue %u\n", zrouter.ribq->spec.hold); @@ -2553,42 +3436,21 @@ static int config_write_protocol(struct vty *vty) == MCAST_MIX_DISTANCE ? "lower-distance" : "longer-prefix"); - return 1; -} -#ifdef HAVE_NETLINK -/* Display default rtm_table for all clients. */ -DEFUN (show_table, - show_table_cmd, - "show table", - SHOW_STR - "default routing table to use for all clients\n") -{ - vty_out(vty, "table %d\n", zrouter.rtm_table_default); - return CMD_SUCCESS; -} + /* Include dataplane info */ + dplane_config_write_helper(vty); -DEFUN (config_table, - config_table_cmd, - "table TABLENO", - "Configure target kernel routing table\n" - "TABLE integer\n") -{ - zrouter.rtm_table_default = strtol(argv[1]->arg, (char **)0, 10); - return CMD_SUCCESS; -} + /* Include nexthop-group config */ + if (!zebra_nhg_kernel_nexthops_enabled()) + vty_out(vty, "no zebra nexthop kernel enable\n"); -DEFUN (no_config_table, - no_config_table_cmd, - "no table [TABLENO]", - NO_STR - "Configure target kernel routing table\n" - "TABLE integer\n") -{ - zrouter.rtm_table_default = 0; - return CMD_SUCCESS; +#ifdef HAVE_NETLINK + /* Include netlink info */ + netlink_config_write_helper(vty); +#endif /* HAVE_NETLINK */ + + return 1; } -#endif DEFUN (show_zebra, show_zebra_cmd, @@ -2606,8 +3468,7 @@ DEFUN (show_zebra, RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { struct zebra_vrf *zvrf = vrf->info; - vty_out(vty, "%-25s %10" PRIu64 " %10" PRIu64 " %10" PRIu64 - " %10" PRIu64 " %10" PRIu64 "\n", + vty_out(vty, "%-25s %10" PRIu64 " %10" PRIu64 " %10" PRIu64" %10" PRIu64 " %10" PRIu64 "\n", vrf->name, zvrf->installs, zvrf->removals, zvrf->neigh_updates, zvrf->lsp_installs, zvrf->lsp_removals); @@ -2833,17 +3694,12 @@ DEFUN (zebra_show_routing_tables_summary, /* Table configuration write function. */ static int config_write_table(struct vty *vty) { - if (zrouter.rtm_table_default) - vty_out(vty, "table %d\n", zrouter.rtm_table_default); return 0; } /* IPForwarding configuration write function. */ static int config_write_forwarding(struct vty *vty) { - /* FIXME: Find better place for that. */ - router_id_write(vty); - if (!ipforward()) vty_out(vty, "no ip forwarding\n"); if (!ipforward_ipv6()) @@ -2915,35 +3771,87 @@ DEFUN_HIDDEN (show_frr, return CMD_SUCCESS; } +#ifdef HAVE_NETLINK +DEFUN_HIDDEN(zebra_kernel_netlink_batch_tx_buf, + zebra_kernel_netlink_batch_tx_buf_cmd, + "zebra kernel netlink batch-tx-buf (1-1048576) (1-1048576)", + ZEBRA_STR + "Zebra kernel interface\n" + "Set Netlink parameters\n" + "Set batch buffer size and send threshold\n" + "Size of the buffer\n" + "Send threshold\n") +{ + uint32_t bufsize = 0, threshold = 0; + + bufsize = strtoul(argv[4]->arg, NULL, 10); + threshold = strtoul(argv[5]->arg, NULL, 10); + + netlink_set_batch_buffer_size(bufsize, threshold, true); + + return CMD_SUCCESS; +} + +DEFUN_HIDDEN(no_zebra_kernel_netlink_batch_tx_buf, + no_zebra_kernel_netlink_batch_tx_buf_cmd, + "no zebra kernel netlink batch-tx-buf [(0-1048576)] [(0-1048576)]", + NO_STR ZEBRA_STR + "Zebra kernel interface\n" + "Set Netlink parameters\n" + "Set batch buffer size and send threshold\n" + "Size of the buffer\n" + "Send threshold\n") +{ + netlink_set_batch_buffer_size(0, 0, false); + + return CMD_SUCCESS; +} + +#endif /* HAVE_NETLINK */ + /* IP node for static routes. */ -static struct cmd_node ip_node = {IP_NODE, "", 1}; -static struct cmd_node protocol_node = {PROTOCOL_NODE, "", 1}; +static int zebra_ip_config(struct vty *vty); +static struct cmd_node ip_node = { + .name = "static ip", + .node = IP_NODE, + .prompt = "", + .config_write = zebra_ip_config, +}; +static int config_write_protocol(struct vty *vty); +static struct cmd_node protocol_node = { + .name = "protocol", + .node = PROTOCOL_NODE, + .prompt = "", + .config_write = config_write_protocol, +}; /* table node for routing tables. */ -static struct cmd_node table_node = {TABLE_NODE, - "", /* This node has no interface. */ - 1}; -static struct cmd_node forwarding_node = {FORWARDING_NODE, - "", /* This node has no interface. */ - 1}; +static int config_write_table(struct vty *vty); +static struct cmd_node table_node = { + .name = "table", + .node = TABLE_NODE, + .prompt = "", + .config_write = config_write_table, +}; +static int config_write_forwarding(struct vty *vty); +static struct cmd_node forwarding_node = { + .name = "forwarding", + .node = FORWARDING_NODE, + .prompt = "", + .config_write = config_write_forwarding, +}; /* Route VTY. */ void zebra_vty_init(void) { /* Install configuration write function. */ - install_node(&table_node, config_write_table); - install_node(&forwarding_node, config_write_forwarding); + install_node(&table_node); + install_node(&forwarding_node); install_element(VIEW_NODE, &show_ip_forwarding_cmd); install_element(CONFIG_NODE, &ip_forwarding_cmd); install_element(CONFIG_NODE, &no_ip_forwarding_cmd); install_element(ENABLE_NODE, &show_zebra_cmd); -#ifdef HAVE_NETLINK - install_element(VIEW_NODE, &show_table_cmd); - install_element(CONFIG_NODE, &config_table_cmd); - install_element(CONFIG_NODE, &no_config_table_cmd); -#endif /* HAVE_NETLINK */ - install_element(VIEW_NODE, &show_ipv6_forwarding_cmd); install_element(CONFIG_NODE, &ipv6_forwarding_cmd); install_element(CONFIG_NODE, &no_ipv6_forwarding_cmd); @@ -2951,8 +3859,8 @@ void zebra_vty_init(void) /* Route-map */ zebra_route_map_init(); - install_node(&ip_node, zebra_ip_config); - install_node(&protocol_node, config_write_protocol); + install_node(&ip_node); + install_node(&protocol_node); install_element(CONFIG_NODE, &allow_external_route_update_cmd); install_element(CONFIG_NODE, &no_allow_external_route_update_cmd); @@ -2966,13 +3874,15 @@ void zebra_vty_init(void) install_element(CONFIG_NODE, &no_zebra_workqueue_timer_cmd); 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(VIEW_NODE, &show_nexthop_group_cmd); + install_element(VIEW_NODE, &show_interface_nexthop_group_cmd); install_element(VIEW_NODE, &show_vrf_cmd); install_element(VIEW_NODE, &show_vrf_vni_cmd); install_element(VIEW_NODE, &show_route_cmd); - install_element(VIEW_NODE, &show_route_table_cmd); - if (vrf_is_backend_netns()) - install_element(VIEW_NODE, &show_route_table_vrf_cmd); + install_element(VIEW_NODE, &show_ro_cmd); install_element(VIEW_NODE, &show_route_detail_cmd); install_element(VIEW_NODE, &show_route_summary_cmd); install_element(VIEW_NODE, &show_ip_nht_cmd); @@ -2998,6 +3908,9 @@ void zebra_vty_init(void) install_element(VIEW_NODE, &show_evpn_vni_cmd); install_element(VIEW_NODE, &show_evpn_vni_detail_cmd); install_element(VIEW_NODE, &show_evpn_vni_vni_cmd); + install_element(VIEW_NODE, &show_evpn_es_cmd); + install_element(VIEW_NODE, &show_evpn_es_evi_cmd); + install_element(VIEW_NODE, &show_evpn_access_vlan_cmd); install_element(VIEW_NODE, &show_evpn_rmac_vni_mac_cmd); install_element(VIEW_NODE, &show_evpn_rmac_vni_cmd); install_element(VIEW_NODE, &show_evpn_rmac_vni_all_cmd); @@ -3034,5 +3947,10 @@ void zebra_vty_init(void) install_element(CONFIG_NODE, &zebra_dplane_queue_limit_cmd); install_element(CONFIG_NODE, &no_zebra_dplane_queue_limit_cmd); +#ifdef HAVE_NETLINK + install_element(CONFIG_NODE, &zebra_kernel_netlink_batch_tx_buf_cmd); + install_element(CONFIG_NODE, &no_zebra_kernel_netlink_batch_tx_buf_cmd); +#endif /* HAVE_NETLINK */ + install_element(VIEW_NODE, &zebra_show_routing_tables_summary_cmd); } diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index a2e2171304..d8ed58edef 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -49,80 +49,39 @@ #include "zebra/zebra_ns.h" #include "zebra/zebra_vrf.h" #include "zebra/zebra_vxlan.h" +#include "zebra/zebra_evpn.h" +#include "zebra/zebra_evpn_mac.h" +#include "zebra/zebra_evpn_neigh.h" #include "zebra/zebra_vxlan_private.h" +#include "zebra/zebra_evpn_mh.h" +#include "zebra/zebra_evpn_vxlan.h" #include "zebra/zebra_router.h" DEFINE_MTYPE_STATIC(ZEBRA, HOST_PREFIX, "host prefix"); -DEFINE_MTYPE_STATIC(ZEBRA, ZVNI, "VNI hash"); DEFINE_MTYPE_STATIC(ZEBRA, ZL3VNI, "L3 VNI hash"); -DEFINE_MTYPE_STATIC(ZEBRA, ZVNI_VTEP, "VNI remote VTEP"); -DEFINE_MTYPE_STATIC(ZEBRA, MAC, "VNI MAC"); -DEFINE_MTYPE_STATIC(ZEBRA, NEIGH, "VNI Neighbor"); +DEFINE_MTYPE_STATIC(ZEBRA, L3VNI_MAC, "EVPN L3VNI MAC"); +DEFINE_MTYPE_STATIC(ZEBRA, L3NEIGH, "EVPN Neighbor"); DEFINE_MTYPE_STATIC(ZEBRA, ZVXLAN_SG, "zebra VxLAN multicast group"); -/* definitions */ -/* PMSI strings. */ -#define VXLAN_FLOOD_STR_NO_INFO "-" -#define VXLAN_FLOOD_STR_DEFAULT VXLAN_FLOOD_STR_NO_INFO -static const struct message zvtep_flood_str[] = { - {VXLAN_FLOOD_DISABLED, VXLAN_FLOOD_STR_NO_INFO}, - {VXLAN_FLOOD_PIM_SM, "PIM-SM"}, - {VXLAN_FLOOD_HEAD_END_REPL, "HER"}, - {0} -}; - +DEFINE_HOOK(zebra_rmac_update, (zebra_mac_t *rmac, zebra_l3vni_t *zl3vni, + bool delete, const char *reason), (rmac, zl3vni, delete, reason)) /* static function declarations */ -static int ip_prefix_send_to_client(vrf_id_t vrf_id, struct prefix *p, - uint16_t cmd); -static void zvni_print_neigh(zebra_neigh_t *n, void *ctxt, json_object *json); -static void zvni_print_neigh_hash(struct hash_bucket *bucket, void *ctxt); -static void zvni_print_dad_neigh_hash(struct hash_bucket *bucket, void *ctxt); -static void zvni_print_neigh_hash_all_vni(struct hash_bucket *bucket, - void **args); +static void zevpn_print_neigh_hash_all_evpn(struct hash_bucket *bucket, + void **args); static void zl3vni_print_nh(zebra_neigh_t *n, struct vty *vty, json_object *json); static void zl3vni_print_rmac(zebra_mac_t *zrmac, struct vty *vty, json_object *json); -static void zvni_print_mac(zebra_mac_t *mac, void *ctxt, json_object *json); -static void zvni_print_mac_hash(struct hash_bucket *bucket, void *ctxt); -static void zvni_print_mac_hash_all_vni(struct hash_bucket *bucket, void *ctxt); -static void zvni_print(zebra_vni_t *zvni, void **ctxt); -static void zvni_print_hash(struct hash_bucket *bucket, void *ctxt[]); - -static int zvni_macip_send_msg_to_client(vni_t vni, struct ethaddr *macaddr, - struct ipaddr *ip, uint8_t flags, - uint32_t seq, int state, uint16_t cmd); -static unsigned int neigh_hash_keymake(void *p); -static void *zvni_neigh_alloc(void *p); -static zebra_neigh_t *zvni_neigh_add(zebra_vni_t *zvni, struct ipaddr *ip, - struct ethaddr *mac); -static int zvni_neigh_del(zebra_vni_t *zvni, zebra_neigh_t *n); -static void zvni_neigh_del_from_vtep(zebra_vni_t *zvni, int uninstall, - struct in_addr *r_vtep_ip); -static void zvni_neigh_del_all(zebra_vni_t *zvni, int uninstall, int upd_client, - uint32_t flags); -static zebra_neigh_t *zvni_neigh_lookup(zebra_vni_t *zvni, struct ipaddr *ip); -static int zvni_neigh_send_add_to_client(vni_t vni, struct ipaddr *ip, - struct ethaddr *macaddr, - uint8_t flags, uint32_t seq); -static int zvni_neigh_send_del_to_client(vni_t vni, struct ipaddr *ip, - struct ethaddr *macaddr, - uint8_t flags, int state); -static int zvni_neigh_install(zebra_vni_t *zvni, zebra_neigh_t *n); -static int zvni_neigh_uninstall(zebra_vni_t *zvni, zebra_neigh_t *n); -static int zvni_neigh_probe(zebra_vni_t *zvni, zebra_neigh_t *n); -static zebra_vni_t *zvni_from_svi(struct interface *ifp, - struct interface *br_if); -static struct interface *zvni_map_to_svi(vlanid_t vid, struct interface *br_if); +static void zevpn_print_mac_hash_all_evpn(struct hash_bucket *bucket, void *ctxt); /* l3-vni next-hop neigh related APIs */ static zebra_neigh_t *zl3vni_nh_lookup(zebra_l3vni_t *zl3vni, - struct ipaddr *ip); + const struct ipaddr *ip); static void *zl3vni_nh_alloc(void *p); static zebra_neigh_t *zl3vni_nh_add(zebra_l3vni_t *zl3vni, - struct ipaddr *vtep_ip, - struct ethaddr *rmac); + const struct ipaddr *vtep_ip, + const struct ethaddr *rmac); static int zl3vni_nh_del(zebra_l3vni_t *zl3vni, zebra_neigh_t *n); static int zl3vni_nh_install(zebra_l3vni_t *zl3vni, zebra_neigh_t *n); static int zl3vni_nh_uninstall(zebra_l3vni_t *zl3vni, zebra_neigh_t *n); @@ -130,90 +89,23 @@ static int zl3vni_nh_uninstall(zebra_l3vni_t *zl3vni, zebra_neigh_t *n); /* l3-vni rmac related APIs */ static void zl3vni_print_rmac_hash(struct hash_bucket *, void *); static zebra_mac_t *zl3vni_rmac_lookup(zebra_l3vni_t *zl3vni, - struct ethaddr *rmac); + const struct ethaddr *rmac); static void *zl3vni_rmac_alloc(void *p); static zebra_mac_t *zl3vni_rmac_add(zebra_l3vni_t *zl3vni, - struct ethaddr *rmac); + const struct ethaddr *rmac); static int zl3vni_rmac_del(zebra_l3vni_t *zl3vni, zebra_mac_t *zrmac); static int zl3vni_rmac_install(zebra_l3vni_t *zl3vni, zebra_mac_t *zrmac); static int zl3vni_rmac_uninstall(zebra_l3vni_t *zl3vni, zebra_mac_t *zrmac); /* l3-vni related APIs*/ -static zebra_l3vni_t *zl3vni_lookup(vni_t vni); static void *zl3vni_alloc(void *p); static zebra_l3vni_t *zl3vni_add(vni_t vni, vrf_id_t vrf_id); static int zl3vni_del(zebra_l3vni_t *zl3vni); -static zebra_l3vni_t *zl3vni_from_vrf(vrf_id_t); -static struct interface *zl3vni_map_to_svi_if(zebra_l3vni_t *zl3vni); -static struct interface *zl3vni_map_to_vxlan_if(zebra_l3vni_t *zl3vni); static void zebra_vxlan_process_l3vni_oper_up(zebra_l3vni_t *zl3vni); static void zebra_vxlan_process_l3vni_oper_down(zebra_l3vni_t *zl3vni); -static unsigned int mac_hash_keymake(void *p); -static bool mac_cmp(const void *p1, const void *p2); -static void *zvni_mac_alloc(void *p); -static zebra_mac_t *zvni_mac_add(zebra_vni_t *zvni, struct ethaddr *macaddr); -static int zvni_mac_del(zebra_vni_t *zvni, zebra_mac_t *mac); -static void zvni_mac_del_from_vtep(zebra_vni_t *zvni, int uninstall, - struct in_addr *r_vtep_ip); -static void zvni_mac_del_all(zebra_vni_t *zvni, int uninstall, int upd_client, - uint32_t flags); -static zebra_mac_t *zvni_mac_lookup(zebra_vni_t *zvni, struct ethaddr *macaddr); -static int zvni_mac_send_add_to_client(vni_t vni, struct ethaddr *macaddr, - uint8_t flags, uint32_t seq); -static int zvni_mac_send_del_to_client(vni_t vni, struct ethaddr *macaddr); -static zebra_vni_t *zvni_map_vlan(struct interface *ifp, - struct interface *br_if, vlanid_t vid); -static int zvni_mac_install(zebra_vni_t *zvni, zebra_mac_t *mac); -static int zvni_mac_uninstall(zebra_vni_t *zvni, zebra_mac_t *mac); -static void zvni_install_mac_hash(struct hash_bucket *bucket, void *ctxt); - -static unsigned int vni_hash_keymake(void *p); -static void *zvni_alloc(void *p); -static zebra_vni_t *zvni_lookup(vni_t vni); -static zebra_vni_t *zvni_add(vni_t vni); -static int zvni_del(zebra_vni_t *zvni); -static int zvni_send_add_to_client(zebra_vni_t *zvni); -static int zvni_send_del_to_client(vni_t vni); -static void zvni_build_hash_table(void); -static int zvni_vtep_match(struct in_addr *vtep_ip, zebra_vtep_t *zvtep); -static zebra_vtep_t *zvni_vtep_find(zebra_vni_t *zvni, struct in_addr *vtep_ip); -static zebra_vtep_t *zvni_vtep_add(zebra_vni_t *zvni, struct in_addr *vtep_ip, - int flood_control); -static int zvni_vtep_del(zebra_vni_t *zvni, zebra_vtep_t *zvtep); -static int zvni_vtep_del_all(zebra_vni_t *zvni, int uninstall); -static int zvni_vtep_install(zebra_vni_t *zvni, zebra_vtep_t *zvtep); -static int zvni_vtep_uninstall(zebra_vni_t *zvni, struct in_addr *vtep_ip); -static int zvni_del_macip_for_intf(struct interface *ifp, zebra_vni_t *zvni); -static int zvni_add_macip_for_intf(struct interface *ifp, zebra_vni_t *zvni); -static int zvni_gw_macip_add(struct interface *ifp, zebra_vni_t *zvni, - struct ethaddr *macaddr, struct ipaddr *ip); -static int zvni_gw_macip_del(struct interface *ifp, zebra_vni_t *zvni, - struct ipaddr *ip); -struct interface *zebra_get_vrr_intf_for_svi(struct interface *ifp); -static int advertise_gw_macip_enabled(zebra_vni_t *zvni); -static int advertise_svi_macip_enabled(zebra_vni_t *zvni); -static int zebra_vxlan_ip_inherit_dad_from_mac(struct zebra_vrf *zvrf, - zebra_mac_t *old_zmac, - zebra_mac_t *new_zmac, - zebra_neigh_t *nbr); -static int remote_neigh_count(zebra_mac_t *zmac); -static void zvni_deref_ip2mac(zebra_vni_t *zvni, zebra_mac_t *mac); -static int zebra_vxlan_dad_mac_auto_recovery_exp(struct thread *t); -static int zebra_vxlan_dad_ip_auto_recovery_exp(struct thread *t); -static void zebra_vxlan_dup_addr_detect_for_neigh(struct zebra_vrf *zvrf, - zebra_neigh_t *nbr, - struct in_addr vtep_ip, - bool do_dad, - bool *is_dup_detect, - bool is_local); -static void zebra_vxlan_dup_addr_detect_for_mac(struct zebra_vrf *zvrf, - zebra_mac_t *mac, - struct in_addr vtep_ip, - bool do_dad, - bool *is_dup_detect, - bool is_local); -static unsigned int zebra_vxlan_sg_hash_key_make(void *p); +static void zevpn_build_hash_table(void); +static unsigned int zebra_vxlan_sg_hash_key_make(const void *p); static bool zebra_vxlan_sg_hash_eq(const void *p1, const void *p2); static void zebra_vxlan_sg_do_deref(struct zebra_vrf *zvrf, struct in_addr sip, struct in_addr mcast_grp); @@ -223,7 +115,7 @@ static void zebra_vxlan_sg_deref(struct in_addr local_vtep_ip, struct in_addr mcast_grp); static void zebra_vxlan_sg_ref(struct in_addr local_vtep_ip, struct in_addr mcast_grp); -static void zebra_vxlan_sg_cleanup(struct hash_backet *backet, void *arg); +static void zebra_vxlan_sg_cleanup(struct hash_bucket *bucket, void *arg); /* Private functions */ static int host_rb_entry_compare(const struct host_rb_entry *hle1, @@ -253,8 +145,8 @@ static int host_rb_entry_compare(const struct host_rb_entry *hle1, return memcmp(&hle1->p.u.prefix6, &hle2->p.u.prefix6, IPV6_MAX_BYTELEN); } else { - zlog_debug("%s: Unexpected family type: %d", - __PRETTY_FUNCTION__, hle1->p.family); + zlog_debug("%s: Unexpected family type: %d", __func__, + hle1->p.family); return 0; } } @@ -272,3745 +164,726 @@ static uint32_t rb_host_count(struct host_rb_tree_entry *hrbe) } /* - * Return number of valid MACs in a VNI's MAC hash table - all - * remote MACs and non-internal (auto) local MACs count. + * Print neighbors for all EVPN. */ -static uint32_t num_valid_macs(zebra_vni_t *zvni) +static void zevpn_print_neigh_hash_all_evpn(struct hash_bucket *bucket, + void **args) { - unsigned int i; - uint32_t num_macs = 0; - struct hash *hash; - struct hash_bucket *hb; - zebra_mac_t *mac; + struct vty *vty; + json_object *json = NULL, *json_evpn = NULL; + zebra_evpn_t *zevpn; + uint32_t num_neigh; + struct neigh_walk_ctx wctx; + char vni_str[VNI_STR_LEN]; + uint32_t print_dup; - hash = zvni->mac_table; - if (!hash) - return num_macs; - for (i = 0; i < hash->size; i++) { - for (hb = hash->index[i]; hb; hb = hb->next) { - mac = (zebra_mac_t *)hb->data; - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE) - || CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL) - || !CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO)) - num_macs++; - } - } + vty = (struct vty *)args[0]; + json = (json_object *)args[1]; + print_dup = (uint32_t)(uintptr_t)args[2]; - return num_macs; -} + zevpn = (zebra_evpn_t *)bucket->data; -static uint32_t num_dup_detected_macs(zebra_vni_t *zvni) -{ - unsigned int i; - uint32_t num_macs = 0; - struct hash *hash; - struct hash_bucket *hb; - zebra_mac_t *mac; + num_neigh = hashcount(zevpn->neigh_table); - hash = zvni->mac_table; - if (!hash) - return num_macs; - for (i = 0; i < hash->size; i++) { - for (hb = hash->index[i]; hb; hb = hb->next) { - mac = (zebra_mac_t *)hb->data; - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) - num_macs++; - } + if (print_dup) + num_neigh = num_dup_detected_neighs(zevpn); + + if (json == NULL) { + vty_out(vty, + "\nVNI %u #ARP (IPv4 and IPv6, local and remote) %u\n\n", + zevpn->vni, num_neigh); + } else { + json_evpn = json_object_new_object(); + json_object_int_add(json_evpn, "numArpNd", num_neigh); + snprintf(vni_str, VNI_STR_LEN, "%u", zevpn->vni); } - return num_macs; -} + if (!num_neigh) { + if (json) + json_object_object_add(json, vni_str, json_evpn); + return; + } -static uint32_t num_dup_detected_neighs(zebra_vni_t *zvni) -{ - unsigned int i; - uint32_t num_neighs = 0; - struct hash *hash; - struct hash_bucket *hb; - zebra_neigh_t *nbr; + /* Since we have IPv6 addresses to deal with which can vary widely in + * size, we try to be a bit more elegant in display by first computing + * the maximum width. + */ + memset(&wctx, 0, sizeof(struct neigh_walk_ctx)); + wctx.zevpn = zevpn; + wctx.vty = vty; + wctx.addr_width = 15; + wctx.json = json_evpn; + hash_iterate(zevpn->neigh_table, zebra_evpn_find_neigh_addr_width, + &wctx); - hash = zvni->neigh_table; - if (!hash) - return num_neighs; - for (i = 0; i < hash->size; i++) { - for (hb = hash->index[i]; hb; hb = hb->next) { - nbr = (zebra_neigh_t *)hb->data; - if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) - num_neighs++; - } - } + if (json == NULL) + zebra_evpn_print_neigh_hdr(vty, &wctx); + + if (print_dup) + hash_iterate(zevpn->neigh_table, + zebra_evpn_print_dad_neigh_hash, &wctx); + else + hash_iterate(zevpn->neigh_table, zebra_evpn_print_neigh_hash, + &wctx); - return num_neighs; + if (json) + json_object_object_add(json, vni_str, json_evpn); } -static int advertise_gw_macip_enabled(zebra_vni_t *zvni) +/* + * Print neighbors for all EVPNs in detail. + */ +static void zevpn_print_neigh_hash_all_evpn_detail(struct hash_bucket *bucket, + void **args) { - struct zebra_vrf *zvrf; + struct vty *vty; + json_object *json = NULL, *json_evpn = NULL; + zebra_evpn_t *zevpn; + uint32_t num_neigh; + struct neigh_walk_ctx wctx; + char vni_str[VNI_STR_LEN]; + uint32_t print_dup; - zvrf = zebra_vrf_get_evpn(); - if (zvrf && zvrf->advertise_gw_macip) - return 1; + vty = (struct vty *)args[0]; + json = (json_object *)args[1]; + print_dup = (uint32_t)(uintptr_t)args[2]; - if (zvni && zvni->advertise_gw_macip) - return 1; + zevpn = (zebra_evpn_t *)bucket->data; + if (!zevpn) { + if (json) + vty_out(vty, "{}\n"); + return; + } + num_neigh = hashcount(zevpn->neigh_table); - return 0; -} + if (print_dup && num_dup_detected_neighs(zevpn) == 0) + return; -static int advertise_svi_macip_enabled(zebra_vni_t *zvni) -{ - struct zebra_vrf *zvrf; + if (json == NULL) { + vty_out(vty, + "\nVNI %u #ARP (IPv4 and IPv6, local and remote) %u\n\n", + zevpn->vni, num_neigh); + } else { + json_evpn = json_object_new_object(); + json_object_int_add(json_evpn, "numArpNd", num_neigh); + snprintf(vni_str, VNI_STR_LEN, "%u", zevpn->vni); + } + if (!num_neigh) { + if (json) + json_object_object_add(json, vni_str, json_evpn); + return; + } - zvrf = zebra_vrf_get_evpn(); - if (zvrf && zvrf->advertise_svi_macip) - return 1; + memset(&wctx, 0, sizeof(struct neigh_walk_ctx)); + wctx.zevpn = zevpn; + wctx.vty = vty; + wctx.addr_width = 15; + wctx.json = json_evpn; - if (zvni && zvni->advertise_svi_macip) - return 1; + if (print_dup) + hash_iterate(zevpn->neigh_table, + zebra_evpn_print_dad_neigh_hash_detail, &wctx); + else + hash_iterate(zevpn->neigh_table, + zebra_evpn_print_neigh_hash_detail, &wctx); - return 0; + if (json) + json_object_object_add(json, vni_str, json_evpn); } -/* As part Duplicate Address Detection (DAD) for IP mobility - * MAC binding changes, ensure to inherit duplicate flag - * from MAC. - */ -static int zebra_vxlan_ip_inherit_dad_from_mac(struct zebra_vrf *zvrf, - zebra_mac_t *old_zmac, - zebra_mac_t *new_zmac, - zebra_neigh_t *nbr) +/* print a specific next hop for an l3vni */ +static void zl3vni_print_nh(zebra_neigh_t *n, struct vty *vty, + json_object *json) { - bool is_old_mac_dup = false; - bool is_new_mac_dup = false; - - if (!zvrf->dup_addr_detect) - return 0; - /* Check old or new MAC is detected as duplicate - * mark this neigh as duplicate - */ - if (old_zmac) - is_old_mac_dup = CHECK_FLAG(old_zmac->flags, - ZEBRA_MAC_DUPLICATE); - if (new_zmac) - is_new_mac_dup = CHECK_FLAG(new_zmac->flags, - ZEBRA_MAC_DUPLICATE); - /* Old and/or new MAC can be in duplicate state, - * based on that IP/Neigh Inherits the flag. - * If New MAC is marked duplicate, inherit to the IP. - * If old MAC is duplicate but new MAC is not, clear - * duplicate flag for IP and reset detection params - * and let IP DAD retrigger. - */ - if (is_new_mac_dup && !CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) { - SET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); - /* Capture Duplicate detection time */ - nbr->dad_dup_detect_time = monotime(NULL); - /* Mark neigh inactive */ - ZEBRA_NEIGH_SET_INACTIVE(nbr); + char buf1[ETHER_ADDR_STRLEN]; + char buf2[INET6_ADDRSTRLEN]; + json_object *json_hosts = NULL; + struct host_rb_entry *hle; - return 1; - } else if (is_old_mac_dup && !is_new_mac_dup) { - UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); - nbr->dad_count = 0; - nbr->detect_start_time.tv_sec = 0; - nbr->detect_start_time.tv_usec = 0; + if (!json) { + vty_out(vty, "Ip: %s\n", + ipaddr2str(&n->ip, buf2, sizeof(buf2))); + vty_out(vty, " RMAC: %s\n", + prefix_mac2str(&n->emac, buf1, sizeof(buf1))); + vty_out(vty, " Refcount: %d\n", + rb_host_count(&n->host_rb)); + vty_out(vty, " Prefixes:\n"); + RB_FOREACH (hle, host_rb_tree_entry, &n->host_rb) + vty_out(vty, " %s\n", + prefix2str(&hle->p, buf2, sizeof(buf2))); + } else { + json_hosts = json_object_new_array(); + json_object_string_add( + json, "ip", ipaddr2str(&(n->ip), buf2, sizeof(buf2))); + json_object_string_add( + json, "routerMac", + prefix_mac2str(&n->emac, buf2, sizeof(buf2))); + json_object_int_add(json, "refCount", + rb_host_count(&n->host_rb)); + RB_FOREACH (hle, host_rb_tree_entry, &n->host_rb) + json_object_array_add(json_hosts, + json_object_new_string(prefix2str( + &hle->p, buf2, sizeof(buf2)))); + json_object_object_add(json, "prefixList", json_hosts); } - return 0; } -static void zebra_vxlan_dup_addr_detect_for_mac(struct zebra_vrf *zvrf, - zebra_mac_t *mac, - struct in_addr vtep_ip, - bool do_dad, - bool *is_dup_detect, - bool is_local) +/* Print a specific RMAC entry */ +static void zl3vni_print_rmac(zebra_mac_t *zrmac, struct vty *vty, + json_object *json) { - zebra_neigh_t *nbr; - struct listnode *node = NULL; - struct timeval elapsed = {0, 0}; - char buf[ETHER_ADDR_STRLEN]; - char buf1[INET6_ADDRSTRLEN]; - bool reset_params = false; - - if (!(zvrf->dup_addr_detect && do_dad)) - return; - - /* MAC is detected as duplicate, - * Local MAC event -> hold on advertising to BGP. - * Remote MAC event -> hold on installing it. - */ - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "%s: duplicate addr MAC %s flags 0x%x skip update to client, learn count %u recover time %u", - __PRETTY_FUNCTION__, - prefix_mac2str(&mac->macaddr, buf, - sizeof(buf)), - mac->flags, mac->dad_count, - zvrf->dad_freeze_time); - - /* For duplicate MAC do not update - * client but update neigh due to - * this MAC update. - */ - if (zvrf->dad_freeze) - *is_dup_detect = true; - - return; - } - - /* Check if detection time (M-secs) expired. - * Reset learn count and detection start time. - */ - monotime_since(&mac->detect_start_time, &elapsed); - reset_params = (elapsed.tv_sec > zvrf->dad_time); - if (is_local && !reset_params) { - /* RFC-7432: A PE/VTEP that detects a MAC mobility - * event via LOCAL learning starts an M-second timer. - * - * NOTE: This is the START of the probe with count is - * 0 during LOCAL learn event. - * (mac->dad_count == 0 || elapsed.tv_sec >= zvrf->dad_time) - */ - reset_params = !mac->dad_count; - } - - if (reset_params) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "%s: duplicate addr MAC %s flags 0x%x detection time passed, reset learn count %u" - , __PRETTY_FUNCTION__, - prefix_mac2str(&mac->macaddr, buf, - sizeof(buf)), - mac->flags, mac->dad_count); - - mac->dad_count = 0; - /* Start dup. addr detection (DAD) start time, - * ONLY during LOCAL learn. - */ - if (is_local) - monotime(&mac->detect_start_time); + char buf1[ETHER_ADDR_STRLEN]; + char buf2[PREFIX_STRLEN]; + json_object *json_hosts = NULL; + struct host_rb_entry *hle; - } else if (!is_local) { - /* For REMOTE MAC, increment detection count - * ONLY while in probe window, once window passed, - * next local learn event should trigger DAD. - */ - mac->dad_count++; + if (!json) { + vty_out(vty, "MAC: %s\n", + prefix_mac2str(&zrmac->macaddr, buf1, sizeof(buf1))); + vty_out(vty, " Remote VTEP: %s\n", + inet_ntoa(zrmac->fwd_info.r_vtep_ip)); + vty_out(vty, " Refcount: %d\n", rb_host_count(&zrmac->host_rb)); + vty_out(vty, " Prefixes:\n"); + RB_FOREACH (hle, host_rb_tree_entry, &zrmac->host_rb) + vty_out(vty, " %s\n", + prefix2str(&hle->p, buf2, sizeof(buf2))); + } else { + json_hosts = json_object_new_array(); + json_object_string_add( + json, "routerMac", + prefix_mac2str(&zrmac->macaddr, buf1, sizeof(buf1))); + json_object_string_add(json, "vtepIp", + inet_ntoa(zrmac->fwd_info.r_vtep_ip)); + json_object_int_add(json, "refCount", + rb_host_count(&zrmac->host_rb)); + json_object_int_add(json, "localSequence", zrmac->loc_seq); + json_object_int_add(json, "remoteSequence", zrmac->rem_seq); + RB_FOREACH (hle, host_rb_tree_entry, &zrmac->host_rb) + json_object_array_add( + json_hosts, + json_object_new_string(prefix2str( + &hle->p, buf2, sizeof(buf2)))); + json_object_object_add(json, "prefixList", json_hosts); } +} - /* For LOCAL MAC learn event, once count is reset above via either - * initial/start detection time or passed the probe time, the count - * needs to be incremented. - */ - if (is_local) - mac->dad_count++; - - if (mac->dad_count >= zvrf->dad_max_moves) { - flog_warn(EC_ZEBRA_DUP_MAC_DETECTED, - "VNI %u: MAC %s detected as duplicate during %s VTEP %s", - mac->zvni->vni, - prefix_mac2str(&mac->macaddr, buf, sizeof(buf)), - is_local ? "local update, last" : - "remote update, from", inet_ntoa(vtep_ip)); +/* + * Print MACs for all EVPNs. + */ +static void zevpn_print_mac_hash_all_evpn(struct hash_bucket *bucket, void *ctxt) +{ + struct vty *vty; + json_object *json = NULL, *json_evpn = NULL; + json_object *json_mac = NULL; + zebra_evpn_t *zevpn; + uint32_t num_macs; + struct mac_walk_ctx *wctx = ctxt; + char vni_str[VNI_STR_LEN]; - SET_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE); + vty = wctx->vty; + json = wctx->json; - /* Capture Duplicate detection time */ - mac->dad_dup_detect_time = monotime(NULL); + zevpn = (zebra_evpn_t *)bucket->data; + wctx->zevpn = zevpn; - /* Mark all IPs/Neighs as duplicate - * associcated with this MAC - */ - for (ALL_LIST_ELEMENTS_RO(mac->neigh_list, node, nbr)) { + /*We are iterating over a new VNI, set the count to 0*/ + wctx->count = 0; - /* Ony Mark IPs which are Local */ - if (!CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL)) - continue; + num_macs = num_valid_macs(zevpn); + if (!num_macs) + return; - SET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); + if (wctx->print_dup) + num_macs = num_dup_detected_macs(zevpn); - nbr->dad_dup_detect_time = monotime(NULL); + if (json) { + json_evpn = json_object_new_object(); + json_mac = json_object_new_object(); + snprintf(vni_str, VNI_STR_LEN, "%u", zevpn->vni); + } - flog_warn(EC_ZEBRA_DUP_IP_INHERIT_DETECTED, - "VNI %u: MAC %s IP %s detected as duplicate during %s update, inherit duplicate from MAC", - mac->zvni->vni, - prefix_mac2str(&mac->macaddr, - buf, sizeof(buf)), - ipaddr2str(&nbr->ip, buf1, sizeof(buf1)), - is_local ? "local" : "remote"); - } + if (!CHECK_FLAG(wctx->flags, SHOW_REMOTE_MAC_FROM_VTEP)) { + if (json == NULL) { + vty_out(vty, "\nVNI %u #MACs (local and remote) %u\n\n", + zevpn->vni, num_macs); + vty_out(vty, + "Flags: N=sync-neighs, I=local-inactive, P=peer-active, X=peer-proxy\n"); + vty_out(vty, "%-17s %-6s %-5s %-30s %-5s %s\n", "MAC", + "Type", "Flags", "Intf/Remote ES/VTEP", + "VLAN", "Seq #'s"); + } else + json_object_int_add(json_evpn, "numMacs", num_macs); + } - /* Start auto recovery timer for this MAC */ - THREAD_OFF(mac->dad_mac_auto_recovery_timer); - if (zvrf->dad_freeze && zvrf->dad_freeze_time) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "%s: duplicate addr MAC %s flags 0x%x auto recovery time %u start" - , __PRETTY_FUNCTION__, - prefix_mac2str(&mac->macaddr, buf, - sizeof(buf)), - mac->flags, zvrf->dad_freeze_time); - - thread_add_timer(zrouter.master, - zebra_vxlan_dad_mac_auto_recovery_exp, - mac, zvrf->dad_freeze_time, - &mac->dad_mac_auto_recovery_timer); + if (!num_macs) { + if (json) { + json_object_int_add(json_evpn, "numMacs", num_macs); + json_object_object_add(json, vni_str, json_evpn); } + return; + } - /* In case of local update, do not inform to client (BGPd), - * upd_neigh for neigh sequence change. - */ - if (zvrf->dad_freeze) - *is_dup_detect = true; + /* assign per-evpn to wctx->json object to fill macs + * under the evpn. Re-assign primary json object to fill + * next evpn information. + */ + wctx->json = json_mac; + if (wctx->print_dup) + hash_iterate(zevpn->mac_table, zebra_evpn_print_dad_mac_hash, + wctx); + else + hash_iterate(zevpn->mac_table, zebra_evpn_print_mac_hash, wctx); + wctx->json = json; + if (json) { + if (wctx->count) + json_object_object_add(json_evpn, "macs", json_mac); + json_object_object_add(json, vni_str, json_evpn); } } -static void zebra_vxlan_dup_addr_detect_for_neigh(struct zebra_vrf *zvrf, - zebra_neigh_t *nbr, - struct in_addr vtep_ip, - bool do_dad, - bool *is_dup_detect, - bool is_local) +/* + * Print MACs in detail for all EVPNs. + */ +static void zevpn_print_mac_hash_all_evpn_detail(struct hash_bucket *bucket, + void *ctxt) { + struct vty *vty; + json_object *json = NULL, *json_evpn = NULL; + json_object *json_mac = NULL; + zebra_evpn_t *zevpn; + uint32_t num_macs; + struct mac_walk_ctx *wctx = ctxt; + char vni_str[VNI_STR_LEN]; - struct timeval elapsed = {0, 0}; - char buf[ETHER_ADDR_STRLEN]; - char buf1[INET6_ADDRSTRLEN]; - bool reset_params = false; - - if (!zvrf->dup_addr_detect) - return; + vty = wctx->vty; + json = wctx->json; - /* IP is detected as duplicate or inherit dup - * state, hold on to install as remote entry - * only if freeze is enabled. - */ - if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "%s: duplicate addr MAC %s IP %s flags 0x%x skip installing, learn count %u recover time %u", - __PRETTY_FUNCTION__, - prefix_mac2str(&nbr->emac, buf, sizeof(buf)), - ipaddr2str(&nbr->ip, buf1, sizeof(buf1)), - nbr->flags, nbr->dad_count, - zvrf->dad_freeze_time); - - if (zvrf->dad_freeze) - *is_dup_detect = true; - - /* warn-only action, neigh will be installed. - * freeze action, it wil not be installed. - */ + zevpn = (zebra_evpn_t *)bucket->data; + if (!zevpn) { + if (json) + vty_out(vty, "{}\n"); return; } + wctx->zevpn = zevpn; - if (!do_dad) - return; + /*We are iterating over a new EVPN, set the count to 0*/ + wctx->count = 0; - /* Check if detection time (M-secs) expired. - * Reset learn count and detection start time. - * During remote mac add, count should already be 1 - * via local learning. - */ - monotime_since(&nbr->detect_start_time, &elapsed); - reset_params = (elapsed.tv_sec > zvrf->dad_time); - - if (is_local && !reset_params) { - /* RFC-7432: A PE/VTEP that detects a MAC mobility - * event via LOCAL learning starts an M-second timer. - * - * NOTE: This is the START of the probe with count is - * 0 during LOCAL learn event. - */ - reset_params = !nbr->dad_count; - } - - if (reset_params) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "%s: duplicate addr MAC %s IP %s flags 0x%x detection time passed, reset learn count %u", - __PRETTY_FUNCTION__, - prefix_mac2str(&nbr->emac, buf, sizeof(buf)), - ipaddr2str(&nbr->ip, buf1, sizeof(buf1)), - nbr->flags, nbr->dad_count); - /* Reset learn count but do not start detection - * during REMOTE learn event. - */ - nbr->dad_count = 0; - /* Start dup. addr detection (DAD) start time, - * ONLY during LOCAL learn. - */ - if (is_local) - monotime(&nbr->detect_start_time); - - } else if (!is_local) { - /* For REMOTE IP/Neigh, increment detection count - * ONLY while in probe window, once window passed, - * next local learn event should trigger DAD. - */ - nbr->dad_count++; - } - - /* For LOCAL IP/Neigh learn event, once count is reset above via either - * initial/start detection time or passed the probe time, the count - * needs to be incremented. - */ - if (is_local) - nbr->dad_count++; - - if (nbr->dad_count >= zvrf->dad_max_moves) { - flog_warn(EC_ZEBRA_DUP_IP_DETECTED, - "VNI %u: MAC %s IP %s detected as duplicate during %s VTEP %s", - nbr->zvni->vni, - prefix_mac2str(&nbr->emac, buf, sizeof(buf)), - ipaddr2str(&nbr->ip, buf1, sizeof(buf1)), - is_local ? "local update, last" : - "remote update, from", - inet_ntoa(vtep_ip)); - - SET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); - - /* Capture Duplicate detection time */ - nbr->dad_dup_detect_time = monotime(NULL); - - /* Start auto recovery timer for this IP */ - THREAD_OFF(nbr->dad_ip_auto_recovery_timer); - if (zvrf->dad_freeze && zvrf->dad_freeze_time) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "%s: duplicate addr MAC %s IP %s flags 0x%x auto recovery time %u start", - __PRETTY_FUNCTION__, - prefix_mac2str(&nbr->emac, buf, sizeof(buf)), - ipaddr2str(&nbr->ip, buf1, sizeof(buf1)), - nbr->flags, zvrf->dad_freeze_time); - - thread_add_timer(zrouter.master, - zebra_vxlan_dad_ip_auto_recovery_exp, - nbr, zvrf->dad_freeze_time, - &nbr->dad_ip_auto_recovery_timer); - } - if (zvrf->dad_freeze) - *is_dup_detect = true; - } -} - -/* - * Helper function to determine maximum width of neighbor IP address for - * display - just because we're dealing with IPv6 addresses that can - * widely vary. - */ -static void zvni_find_neigh_addr_width(struct hash_bucket *bucket, void *ctxt) -{ - zebra_neigh_t *n; - char buf[INET6_ADDRSTRLEN]; - struct neigh_walk_ctx *wctx = ctxt; - int width; - - n = (zebra_neigh_t *)bucket->data; - - ipaddr2str(&n->ip, buf, sizeof(buf)); - width = strlen(buf); - if (width > wctx->addr_width) - wctx->addr_width = width; - -} - -/* - * Print a specific neighbor entry. - */ -static void zvni_print_neigh(zebra_neigh_t *n, void *ctxt, json_object *json) -{ - struct vty *vty; - char buf1[ETHER_ADDR_STRLEN]; - char buf2[INET6_ADDRSTRLEN]; - const char *type_str; - const char *state_str; - bool flags_present = false; - struct zebra_vrf *zvrf = NULL; - struct timeval detect_start_time = {0, 0}; + num_macs = num_valid_macs(zevpn); + if (!num_macs) + return; - zvrf = zebra_vrf_get_evpn(); - if (!zvrf) + if (wctx->print_dup && (num_dup_detected_macs(zevpn) == 0)) return; - ipaddr2str(&n->ip, buf2, sizeof(buf2)); - prefix_mac2str(&n->emac, buf1, sizeof(buf1)); - type_str = CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL) ? - "local" : "remote"; - state_str = IS_ZEBRA_NEIGH_ACTIVE(n) ? "active" : "inactive"; - vty = (struct vty *)ctxt; - if (json == NULL) { - vty_out(vty, "IP: %s\n", - ipaddr2str(&n->ip, buf2, sizeof(buf2))); - vty_out(vty, " Type: %s\n", type_str); - vty_out(vty, " State: %s\n", state_str); - vty_out(vty, " MAC: %s\n", - prefix_mac2str(&n->emac, buf1, sizeof(buf1))); - } else { - json_object_string_add(json, "ip", buf2); - json_object_string_add(json, "type", type_str); - json_object_string_add(json, "state", state_str); - json_object_string_add(json, "mac", buf1); + if (json) { + json_evpn = json_object_new_object(); + json_mac = json_object_new_object(); + snprintf(vni_str, VNI_STR_LEN, "%u", zevpn->vni); } - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) { + + if (!CHECK_FLAG(wctx->flags, SHOW_REMOTE_MAC_FROM_VTEP)) { if (json == NULL) { - vty_out(vty, " Remote VTEP: %s\n", - inet_ntoa(n->r_vtep_ip)); - } else - json_object_string_add(json, "remoteVtep", - inet_ntoa(n->r_vtep_ip)); - } - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DEF_GW)) { - if (!json) { - vty_out(vty, " Flags: Default-gateway"); - flags_present = true; + vty_out(vty, "\nVNI %u #MACs (local and remote) %u\n\n", + zevpn->vni, num_macs); } else - json_object_boolean_true_add(json, "defaultGateway"); - } - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG)) { - if (!json) { - vty_out(vty, - flags_present ? " ,Router" : " Flags: Router"); - flags_present = true; - } + json_object_int_add(json_evpn, "numMacs", num_macs); } - if (json == NULL) { - if (flags_present) - vty_out(vty, "\n"); - vty_out(vty, " Local Seq: %u Remote Seq: %u\n", - n->loc_seq, n->rem_seq); - - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE)) { - vty_out(vty, " Duplicate, detected at %s", - time_to_string(n->dad_dup_detect_time)); - } else if (n->dad_count) { - monotime_since(&n->detect_start_time, - &detect_start_time); - if (detect_start_time.tv_sec <= zvrf->dad_time) { - char *buf = time_to_string( - n->detect_start_time.tv_sec); - char tmp_buf[30]; - - strlcpy(tmp_buf, buf, sizeof(tmp_buf)); - vty_out(vty, - " Duplicate detection started at %s, detection count %u\n", - tmp_buf, n->dad_count); - } - } - } else { - json_object_int_add(json, "localSequence", n->loc_seq); - json_object_int_add(json, "remoteSequence", n->rem_seq); - json_object_int_add(json, "detectionCount", - n->dad_count); - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE)) - json_object_boolean_true_add(json, "isDuplicate"); - else - json_object_boolean_false_add(json, "isDuplicate"); - - + /* assign per-evpn to wctx->json object to fill macs + * under the evpn. Re-assign primary json object to fill + * next evpn information. + */ + wctx->json = json_mac; + if (wctx->print_dup) + hash_iterate(zevpn->mac_table, + zebra_evpn_print_dad_mac_hash_detail, wctx); + else + hash_iterate(zevpn->mac_table, zebra_evpn_print_mac_hash_detail, + wctx); + wctx->json = json; + if (json) { + if (wctx->count) + json_object_object_add(json_evpn, "macs", json_mac); + json_object_object_add(json, vni_str, json_evpn); } } -/* - * Print neighbor hash entry - called for display of all neighbors. - */ -static void zvni_print_neigh_hash(struct hash_bucket *bucket, void *ctxt) +static void zl3vni_print_nh_hash(struct hash_bucket *bucket, void *ctx) { - struct vty *vty; - json_object *json_vni = NULL, *json_row = NULL; - zebra_neigh_t *n; + struct nh_walk_ctx *wctx = NULL; + struct vty *vty = NULL; + struct json_object *json_evpn = NULL; + struct json_object *json_nh = NULL; + zebra_neigh_t *n = NULL; char buf1[ETHER_ADDR_STRLEN]; char buf2[INET6_ADDRSTRLEN]; - struct neigh_walk_ctx *wctx = ctxt; - const char *state_str; + wctx = (struct nh_walk_ctx *)ctx; vty = wctx->vty; - json_vni = wctx->json; + json_evpn = wctx->json; + if (json_evpn) + json_nh = json_object_new_object(); n = (zebra_neigh_t *)bucket->data; - if (json_vni) - json_row = json_object_new_object(); - - prefix_mac2str(&n->emac, buf1, sizeof(buf1)); - ipaddr2str(&n->ip, buf2, sizeof(buf2)); - state_str = IS_ZEBRA_NEIGH_ACTIVE(n) ? "active" : "inactive"; - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { - if (wctx->flags & SHOW_REMOTE_NEIGH_FROM_VTEP) - return; - - if (json_vni == NULL) { - vty_out(vty, "%*s %-6s %-8s %-17s\n", - -wctx->addr_width, buf2, "local", - state_str, buf1); - } else { - json_object_string_add(json_row, "type", "local"); - json_object_string_add(json_row, "state", state_str); - json_object_string_add(json_row, "mac", buf1); - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DEF_GW)) - json_object_boolean_true_add( - json_row, "defaultGateway"); - json_object_int_add(json_row, "localSequence", - n->loc_seq); - json_object_int_add(json_row, "remoteSequence", - n->rem_seq); - json_object_int_add(json_row, "detectionCount", - n->dad_count); - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE)) - json_object_boolean_true_add(json_row, - "isDuplicate"); - else - json_object_boolean_false_add(json_row, - "isDuplicate"); - } - wctx->count++; - } else if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) { - if ((wctx->flags & SHOW_REMOTE_NEIGH_FROM_VTEP) && - !IPV4_ADDR_SAME(&n->r_vtep_ip, &wctx->r_vtep_ip)) - return; - - if (json_vni == NULL) { - if ((wctx->flags & SHOW_REMOTE_NEIGH_FROM_VTEP) && - (wctx->count == 0)) - vty_out(vty, - "%*s %-6s %-8s %-17s %-21s\n", - -wctx->addr_width, "Neighbor", "Type", - "State", "MAC", "Remote VTEP"); - vty_out(vty, "%*s %-6s %-8s %-17s %-21s\n", - -wctx->addr_width, buf2, "remote", state_str, - buf1, inet_ntoa(n->r_vtep_ip)); - } else { - json_object_string_add(json_row, "type", "remote"); - json_object_string_add(json_row, "state", state_str); - json_object_string_add(json_row, "mac", buf1); - json_object_string_add(json_row, "remoteVtep", - inet_ntoa(n->r_vtep_ip)); - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DEF_GW)) - json_object_boolean_true_add(json_row, - "defaultGateway"); - json_object_int_add(json_row, "localSequence", - n->loc_seq); - json_object_int_add(json_row, "remoteSequence", - n->rem_seq); - json_object_int_add(json_row, "detectionCount", - n->dad_count); - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE)) - json_object_boolean_true_add(json_row, - "isDuplicate"); - else - json_object_boolean_false_add(json_row, - "isDuplicate"); - } - wctx->count++; + if (!json_evpn) { + vty_out(vty, "%-15s %-17s\n", + ipaddr2str(&(n->ip), buf2, sizeof(buf2)), + prefix_mac2str(&n->emac, buf1, sizeof(buf1))); + } else { + json_object_string_add(json_nh, "nexthopIp", + ipaddr2str(&n->ip, buf2, sizeof(buf2))); + json_object_string_add( + json_nh, "routerMac", + prefix_mac2str(&n->emac, buf1, sizeof(buf1))); + json_object_object_add(json_evpn, + ipaddr2str(&(n->ip), buf2, sizeof(buf2)), + json_nh); } - - if (json_vni) - json_object_object_add(json_vni, buf2, json_row); } -/* - * Print neighbor hash entry in detail - called for display of all neighbors. - */ -static void zvni_print_neigh_hash_detail(struct hash_bucket *bucket, void *ctxt) +static void zl3vni_print_nh_hash_all_vni(struct hash_bucket *bucket, + void **args) { - struct vty *vty; - json_object *json_vni = NULL, *json_row = NULL; - zebra_neigh_t *n; - char buf[INET6_ADDRSTRLEN]; - struct neigh_walk_ctx *wctx = ctxt; + struct vty *vty = NULL; + json_object *json = NULL; + json_object *json_evpn = NULL; + zebra_l3vni_t *zl3vni = NULL; + uint32_t num_nh = 0; + struct nh_walk_ctx wctx; + char vni_str[VNI_STR_LEN]; - vty = wctx->vty; - json_vni = wctx->json; - n = (zebra_neigh_t *)bucket->data; - if (!n) + vty = (struct vty *)args[0]; + json = (struct json_object *)args[1]; + + zl3vni = (zebra_l3vni_t *)bucket->data; + + num_nh = hashcount(zl3vni->nh_table); + if (!num_nh) return; - ipaddr2str(&n->ip, buf, sizeof(buf)); - if (json_vni) - json_row = json_object_new_object(); + if (json) { + json_evpn = json_object_new_object(); + snprintf(vni_str, VNI_STR_LEN, "%u", zl3vni->vni); + } - zvni_print_neigh(n, vty, json_row); + if (json == NULL) { + vty_out(vty, "\nVNI %u #Next-Hops %u\n\n", zl3vni->vni, num_nh); + vty_out(vty, "%-15s %-17s\n", "IP", "RMAC"); + } else + json_object_int_add(json_evpn, "numNextHops", num_nh); - if (json_vni) - json_object_object_add(json_vni, buf, json_row); + memset(&wctx, 0, sizeof(struct nh_walk_ctx)); + wctx.vty = vty; + wctx.json = json_evpn; + hash_iterate(zl3vni->nh_table, zl3vni_print_nh_hash, &wctx); + if (json) + json_object_object_add(json, vni_str, json_evpn); } -/* - * Print neighbors for all VNI. - */ -static void zvni_print_neigh_hash_all_vni(struct hash_bucket *bucket, - void **args) +static void zl3vni_print_rmac_hash_all_vni(struct hash_bucket *bucket, + void **args) { - struct vty *vty; - json_object *json = NULL, *json_vni = NULL; - zebra_vni_t *zvni; - uint32_t num_neigh; - struct neigh_walk_ctx wctx; + struct vty *vty = NULL; + json_object *json = NULL; + json_object *json_evpn = NULL; + zebra_l3vni_t *zl3vni = NULL; + uint32_t num_rmacs; + struct rmac_walk_ctx wctx; char vni_str[VNI_STR_LEN]; - uint32_t print_dup; vty = (struct vty *)args[0]; - json = (json_object *)args[1]; - print_dup = (uint32_t)(uintptr_t)args[2]; - - zvni = (zebra_vni_t *)bucket->data; + json = (struct json_object *)args[1]; - num_neigh = hashcount(zvni->neigh_table); + zl3vni = (zebra_l3vni_t *)bucket->data; - if (print_dup) - num_neigh = num_dup_detected_neighs(zvni); + num_rmacs = hashcount(zl3vni->rmac_table); + if (!num_rmacs) + return; - if (json == NULL) { - vty_out(vty, - "\nVNI %u #ARP (IPv4 and IPv6, local and remote) %u\n\n", - zvni->vni, num_neigh); - } else { - json_vni = json_object_new_object(); - json_object_int_add(json_vni, "numArpNd", num_neigh); - snprintf(vni_str, VNI_STR_LEN, "%u", zvni->vni); + if (json) { + json_evpn = json_object_new_object(); + snprintf(vni_str, VNI_STR_LEN, "%u", zl3vni->vni); } - if (!num_neigh) { - if (json) - json_object_object_add(json, vni_str, json_vni); - return; - } + if (json == NULL) { + vty_out(vty, "\nVNI %u #RMACs %u\n\n", zl3vni->vni, num_rmacs); + vty_out(vty, "%-17s %-21s\n", "RMAC", "Remote VTEP"); + } else + json_object_int_add(json_evpn, "numRmacs", num_rmacs); - /* Since we have IPv6 addresses to deal with which can vary widely in - * size, we try to be a bit more elegant in display by first computing - * the maximum width. + /* assign per-vni to wctx->json object to fill macs + * under the vni. Re-assign primary json object to fill + * next vni information. */ - memset(&wctx, 0, sizeof(struct neigh_walk_ctx)); - wctx.zvni = zvni; + memset(&wctx, 0, sizeof(struct rmac_walk_ctx)); wctx.vty = vty; - wctx.addr_width = 15; - wctx.json = json_vni; - hash_iterate(zvni->neigh_table, zvni_find_neigh_addr_width, &wctx); - - if (json == NULL) { - vty_out(vty, "%*s %-6s %-8s %-17s %-21s\n", - -wctx.addr_width, "IP", "Type", - "State", "MAC", "Remote VTEP"); - } - if (print_dup) - hash_iterate(zvni->neigh_table, zvni_print_dad_neigh_hash, - &wctx); - else - hash_iterate(zvni->neigh_table, zvni_print_neigh_hash, &wctx); - + wctx.json = json_evpn; + hash_iterate(zl3vni->rmac_table, zl3vni_print_rmac_hash, &wctx); if (json) - json_object_object_add(json, vni_str, json_vni); -} - -static void zvni_print_dad_neigh_hash(struct hash_bucket *bucket, void *ctxt) -{ - zebra_neigh_t *nbr; - - nbr = (zebra_neigh_t *)bucket->data; - if (!nbr) - return; - - if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) - zvni_print_neigh_hash(bucket, ctxt); + json_object_object_add(json, vni_str, json_evpn); } -static void zvni_print_dad_neigh_hash_detail(struct hash_bucket *bucket, - void *ctxt) +static void zl3vni_print_rmac_hash(struct hash_bucket *bucket, void *ctx) { - zebra_neigh_t *nbr; + zebra_mac_t *zrmac = NULL; + struct rmac_walk_ctx *wctx = NULL; + struct vty *vty = NULL; + struct json_object *json = NULL; + struct json_object *json_rmac = NULL; + char buf[ETHER_ADDR_STRLEN]; - nbr = (zebra_neigh_t *)bucket->data; - if (!nbr) - return; + wctx = (struct rmac_walk_ctx *)ctx; + vty = wctx->vty; + json = wctx->json; + if (json) + json_rmac = json_object_new_object(); + zrmac = (zebra_mac_t *)bucket->data; - if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) - zvni_print_neigh_hash_detail(bucket, ctxt); + if (!json) { + vty_out(vty, "%-17s %-21s\n", + prefix_mac2str(&zrmac->macaddr, buf, sizeof(buf)), + inet_ntoa(zrmac->fwd_info.r_vtep_ip)); + } else { + json_object_string_add( + json_rmac, "routerMac", + prefix_mac2str(&zrmac->macaddr, buf, sizeof(buf))); + json_object_string_add(json_rmac, "vtepIp", + inet_ntoa(zrmac->fwd_info.r_vtep_ip)); + json_object_object_add( + json, prefix_mac2str(&zrmac->macaddr, buf, sizeof(buf)), + json_rmac); + } } -/* - * Print neighbors for all VNIs in detail. - */ -static void zvni_print_neigh_hash_all_vni_detail(struct hash_bucket *bucket, - void **args) +/* print a specific L3 VNI entry */ +static void zl3vni_print(zebra_l3vni_t *zl3vni, void **ctx) { - struct vty *vty; - json_object *json = NULL, *json_vni = NULL; - zebra_vni_t *zvni; - uint32_t num_neigh; - struct neigh_walk_ctx wctx; - char vni_str[VNI_STR_LEN]; - uint32_t print_dup; - - vty = (struct vty *)args[0]; - json = (json_object *)args[1]; - print_dup = (uint32_t)(uintptr_t)args[2]; - - zvni = (zebra_vni_t *)bucket->data; - if (!zvni) { - if (json) - vty_out(vty, "{}\n"); - return; - } - num_neigh = hashcount(zvni->neigh_table); + char buf[ETHER_ADDR_STRLEN]; + struct vty *vty = NULL; + json_object *json = NULL; + zebra_evpn_t *zevpn = NULL; + json_object *json_evpn_list = NULL; + struct listnode *node = NULL, *nnode = NULL; - if (print_dup && num_dup_detected_neighs(zvni) == 0) - return; - - if (json == NULL) { - vty_out(vty, - "\nVNI %u #ARP (IPv4 and IPv6, local and remote) %u\n\n", - zvni->vni, num_neigh); - } else { - json_vni = json_object_new_object(); - json_object_int_add(json_vni, "numArpNd", num_neigh); - snprintf(vni_str, VNI_STR_LEN, "%u", zvni->vni); - } - if (!num_neigh) { - if (json) - json_object_object_add(json, vni_str, json_vni); - return; - } - - memset(&wctx, 0, sizeof(struct neigh_walk_ctx)); - wctx.zvni = zvni; - wctx.vty = vty; - wctx.addr_width = 15; - wctx.json = json_vni; - - if (print_dup) - hash_iterate(zvni->neigh_table, - zvni_print_dad_neigh_hash_detail, &wctx); - else - hash_iterate(zvni->neigh_table, zvni_print_neigh_hash_detail, - &wctx); - - if (json) - json_object_object_add(json, vni_str, json_vni); -} - -/* print a specific next hop for an l3vni */ -static void zl3vni_print_nh(zebra_neigh_t *n, struct vty *vty, - json_object *json) -{ - char buf1[ETHER_ADDR_STRLEN]; - char buf2[INET6_ADDRSTRLEN]; - json_object *json_hosts = NULL; - struct host_rb_entry *hle; + vty = ctx[0]; + json = ctx[1]; if (!json) { - vty_out(vty, "Ip: %s\n", - ipaddr2str(&n->ip, buf2, sizeof(buf2))); - vty_out(vty, " RMAC: %s\n", - prefix_mac2str(&n->emac, buf1, sizeof(buf1))); - vty_out(vty, " Refcount: %d\n", - rb_host_count(&n->host_rb)); - vty_out(vty, " Prefixes:\n"); - RB_FOREACH (hle, host_rb_tree_entry, &n->host_rb) - vty_out(vty, " %s\n", - prefix2str(&hle->p, buf2, sizeof(buf2))); + vty_out(vty, "VNI: %u\n", zl3vni->vni); + vty_out(vty, " Type: %s\n", "L3"); + vty_out(vty, " Tenant VRF: %s\n", zl3vni_vrf_name(zl3vni)); + vty_out(vty, " Local Vtep Ip: %s\n", + inet_ntoa(zl3vni->local_vtep_ip)); + vty_out(vty, " Vxlan-Intf: %s\n", + zl3vni_vxlan_if_name(zl3vni)); + vty_out(vty, " SVI-If: %s\n", zl3vni_svi_if_name(zl3vni)); + vty_out(vty, " State: %s\n", zl3vni_state2str(zl3vni)); + vty_out(vty, " VNI Filter: %s\n", + CHECK_FLAG(zl3vni->filter, PREFIX_ROUTES_ONLY) + ? "prefix-routes-only" + : "none"); + vty_out(vty, " System MAC: %s\n", + zl3vni_sysmac2str(zl3vni, buf, sizeof(buf))); + vty_out(vty, " Router MAC: %s\n", + zl3vni_rmac2str(zl3vni, buf, sizeof(buf))); + vty_out(vty, " L2 VNIs: "); + for (ALL_LIST_ELEMENTS(zl3vni->l2vnis, node, nnode, zevpn)) + vty_out(vty, "%u ", zevpn->vni); + vty_out(vty, "\n"); } else { - json_hosts = json_object_new_array(); + json_evpn_list = json_object_new_array(); + json_object_int_add(json, "vni", zl3vni->vni); + json_object_string_add(json, "type", "L3"); + json_object_string_add(json, "localVtepIp", + inet_ntoa(zl3vni->local_vtep_ip)); + json_object_string_add(json, "vxlanIntf", + zl3vni_vxlan_if_name(zl3vni)); + json_object_string_add(json, "sviIntf", + zl3vni_svi_if_name(zl3vni)); + json_object_string_add(json, "state", zl3vni_state2str(zl3vni)); + json_object_string_add(json, "vrf", zl3vni_vrf_name(zl3vni)); json_object_string_add( - json, "ip", ipaddr2str(&(n->ip), buf2, sizeof(buf2))); + json, "sysMac", + zl3vni_sysmac2str(zl3vni, buf, sizeof(buf))); json_object_string_add( json, "routerMac", - prefix_mac2str(&n->emac, buf2, sizeof(buf2))); - json_object_int_add(json, "refCount", - rb_host_count(&n->host_rb)); - RB_FOREACH (hle, host_rb_tree_entry, &n->host_rb) - json_object_array_add(json_hosts, - json_object_new_string(prefix2str( - &hle->p, buf2, sizeof(buf2)))); - json_object_object_add(json, "prefixList", json_hosts); - } -} - -/* Print a specific RMAC entry */ -static void zl3vni_print_rmac(zebra_mac_t *zrmac, struct vty *vty, - json_object *json) -{ - char buf1[ETHER_ADDR_STRLEN]; - char buf2[PREFIX_STRLEN]; - json_object *json_hosts = NULL; - struct host_rb_entry *hle; - - if (!json) { - vty_out(vty, "MAC: %s\n", - prefix_mac2str(&zrmac->macaddr, buf1, sizeof(buf1))); - vty_out(vty, " Remote VTEP: %s\n", - inet_ntoa(zrmac->fwd_info.r_vtep_ip)); - vty_out(vty, " Refcount: %d\n", rb_host_count(&zrmac->host_rb)); - vty_out(vty, " Prefixes:\n"); - RB_FOREACH (hle, host_rb_tree_entry, &zrmac->host_rb) - vty_out(vty, " %s\n", - prefix2str(&hle->p, buf2, sizeof(buf2))); - } else { - json_hosts = json_object_new_array(); + zl3vni_rmac2str(zl3vni, buf, sizeof(buf))); json_object_string_add( - json, "routerMac", - prefix_mac2str(&zrmac->macaddr, buf1, sizeof(buf1))); - json_object_string_add(json, "vtepIp", - inet_ntoa(zrmac->fwd_info.r_vtep_ip)); - json_object_int_add(json, "refCount", - rb_host_count(&zrmac->host_rb)); - json_object_int_add(json, "localSequence", zrmac->loc_seq); - json_object_int_add(json, "remoteSequence", zrmac->rem_seq); - RB_FOREACH (hle, host_rb_tree_entry, &zrmac->host_rb) - json_object_array_add( - json_hosts, - json_object_new_string(prefix2str( - &hle->p, buf2, sizeof(buf2)))); - json_object_object_add(json, "prefixList", json_hosts); - } -} - -/* - * Print a specific MAC entry. - */ -static void zvni_print_mac(zebra_mac_t *mac, void *ctxt, json_object *json) -{ - struct vty *vty; - zebra_neigh_t *n = NULL; - struct listnode *node = NULL; - char buf1[ETHER_ADDR_STRLEN]; - char buf2[INET6_ADDRSTRLEN]; - struct zebra_vrf *zvrf; - struct timeval detect_start_time = {0, 0}; - - zvrf = zebra_vrf_get_evpn(); - if (!zvrf) - return; - - vty = (struct vty *)ctxt; - prefix_mac2str(&mac->macaddr, buf1, sizeof(buf1)); - - if (json) { - json_object *json_mac = json_object_new_object(); - - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) { - struct zebra_ns *zns; - struct interface *ifp; - ifindex_t ifindex; - - ifindex = mac->fwd_info.local.ifindex; - zns = zebra_ns_lookup(NS_DEFAULT); - ifp = if_lookup_by_index_per_ns(zns, ifindex); - if (!ifp) - return; - json_object_string_add(json_mac, "type", "local"); - json_object_string_add(json_mac, "intf", ifp->name); - json_object_int_add(json_mac, "ifindex", ifindex); - if (mac->fwd_info.local.vid) - json_object_int_add(json_mac, "vlan", - mac->fwd_info.local.vid); - } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { - json_object_string_add(json_mac, "type", "remote"); - json_object_string_add( - json_mac, "remoteVtep", - inet_ntoa(mac->fwd_info.r_vtep_ip)); - } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO)) - json_object_string_add(json_mac, "type", "auto"); - - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY)) - json_object_boolean_true_add(json_mac, "stickyMac"); - - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DEF_GW)) - json_object_boolean_true_add(json_mac, - "defaultGateway"); - - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE_DEF_GW)) - json_object_boolean_true_add(json_mac, - "remoteGatewayMac"); - - json_object_int_add(json_mac, "localSequence", mac->loc_seq); - json_object_int_add(json_mac, "remoteSequence", mac->rem_seq); - - json_object_int_add(json_mac, "detectionCount", mac->dad_count); - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) - json_object_boolean_true_add(json_mac, "isDuplicate"); - else - json_object_boolean_false_add(json_mac, "isDuplicate"); - - /* print all the associated neigh */ - if (!listcount(mac->neigh_list)) - json_object_string_add(json_mac, "neighbors", "none"); - else { - json_object *json_active_nbrs = json_object_new_array(); - json_object *json_inactive_nbrs = - json_object_new_array(); - json_object *json_nbrs = json_object_new_object(); - - for (ALL_LIST_ELEMENTS_RO(mac->neigh_list, node, n)) { - if (IS_ZEBRA_NEIGH_ACTIVE(n)) - json_object_array_add( - json_active_nbrs, - json_object_new_string( - ipaddr2str( - &n->ip, buf2, - sizeof(buf2)))); - else - json_object_array_add( - json_inactive_nbrs, - json_object_new_string( - ipaddr2str( - &n->ip, buf2, - sizeof(buf2)))); - } - - json_object_object_add(json_nbrs, "active", - json_active_nbrs); - json_object_object_add(json_nbrs, "inactive", - json_inactive_nbrs); - json_object_object_add(json_mac, "neighbors", - json_nbrs); - } - - json_object_object_add(json, buf1, json_mac); - } else { - vty_out(vty, "MAC: %s\n", buf1); - - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) { - struct zebra_ns *zns; - struct interface *ifp; - ifindex_t ifindex; - - ifindex = mac->fwd_info.local.ifindex; - zns = zebra_ns_lookup(NS_DEFAULT); - ifp = if_lookup_by_index_per_ns(zns, ifindex); - if (!ifp) - return; - vty_out(vty, " Intf: %s(%u)", ifp->name, ifindex); - if (mac->fwd_info.local.vid) - vty_out(vty, " VLAN: %u", - mac->fwd_info.local.vid); - } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { - vty_out(vty, " Remote VTEP: %s", - inet_ntoa(mac->fwd_info.r_vtep_ip)); - } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO)) { - vty_out(vty, " Auto Mac "); - } - - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY)) - vty_out(vty, " Sticky Mac "); - - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DEF_GW)) - vty_out(vty, " Default-gateway Mac "); - - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE_DEF_GW)) - vty_out(vty, " Remote-gateway Mac "); - - vty_out(vty, "\n"); - vty_out(vty, " Local Seq: %u Remote Seq: %u", mac->loc_seq, - mac->rem_seq); - vty_out(vty, "\n"); - - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) { - vty_out(vty, " Duplicate, detected at %s", - time_to_string(mac->dad_dup_detect_time)); - } else if (mac->dad_count) { - monotime_since(&mac->detect_start_time, - &detect_start_time); - if (detect_start_time.tv_sec <= zvrf->dad_time) { - char *buf = time_to_string( - mac->detect_start_time.tv_sec); - char tmp_buf[30]; - - strlcpy(tmp_buf, buf, sizeof(tmp_buf)); - vty_out(vty, - " Duplicate detection started at %s, detection count %u\n", - tmp_buf, mac->dad_count); - } - } - - /* print all the associated neigh */ - vty_out(vty, " Neighbors:\n"); - if (!listcount(mac->neigh_list)) - vty_out(vty, " No Neighbors\n"); - else { - for (ALL_LIST_ELEMENTS_RO(mac->neigh_list, node, n)) { - vty_out(vty, " %s %s\n", - ipaddr2str(&n->ip, buf2, sizeof(buf2)), - (IS_ZEBRA_NEIGH_ACTIVE(n) - ? "Active" - : "Inactive")); - } - } - - vty_out(vty, "\n"); - } -} - -/* - * Print MAC hash entry - called for display of all MACs. - */ -static void zvni_print_mac_hash(struct hash_bucket *bucket, void *ctxt) -{ - struct vty *vty; - json_object *json_mac_hdr = NULL, *json_mac = NULL; - zebra_mac_t *mac; - char buf1[ETHER_ADDR_STRLEN]; - struct mac_walk_ctx *wctx = ctxt; - - vty = wctx->vty; - json_mac_hdr = wctx->json; - mac = (zebra_mac_t *)bucket->data; - - prefix_mac2str(&mac->macaddr, buf1, sizeof(buf1)); - - if (json_mac_hdr) - json_mac = json_object_new_object(); - - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) { - struct zebra_ns *zns; - ifindex_t ifindex; - struct interface *ifp; - vlanid_t vid; - - if (wctx->flags & SHOW_REMOTE_MAC_FROM_VTEP) - return; - - zns = zebra_ns_lookup(NS_DEFAULT); - ifindex = mac->fwd_info.local.ifindex; - ifp = if_lookup_by_index_per_ns(zns, ifindex); - if (!ifp) // unexpected - return; - vid = mac->fwd_info.local.vid; - if (json_mac_hdr == NULL) - vty_out(vty, "%-17s %-6s %-21s", buf1, "local", - ifp->name); - else { - json_object_string_add(json_mac, "type", "local"); - json_object_string_add(json_mac, "intf", ifp->name); - } - if (vid) { - if (json_mac_hdr == NULL) - vty_out(vty, " %-5u", vid); - else - json_object_int_add(json_mac, "vlan", vid); - } - if (json_mac_hdr == NULL) { - vty_out(vty, "\n"); - } else { - json_object_int_add(json_mac, "localSequence", - mac->loc_seq); - json_object_int_add(json_mac, "remoteSequence", - mac->rem_seq); - json_object_int_add(json_mac, "detectionCount", - mac->dad_count); - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) - json_object_boolean_true_add(json_mac, - "isDuplicate"); - else - json_object_boolean_false_add(json_mac, - "isDuplicate"); - json_object_object_add(json_mac_hdr, buf1, json_mac); - } - - wctx->count++; - - } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { - - if ((wctx->flags & SHOW_REMOTE_MAC_FROM_VTEP) && - !IPV4_ADDR_SAME(&mac->fwd_info.r_vtep_ip, - &wctx->r_vtep_ip)) - return; - - if (json_mac_hdr == NULL) { - if ((wctx->flags & SHOW_REMOTE_MAC_FROM_VTEP) && - (wctx->count == 0)) { - vty_out(vty, "\nVNI %u\n\n", wctx->zvni->vni); - vty_out(vty, "%-17s %-6s %-21s %-5s\n", "MAC", - "Type", "Intf/Remote VTEP", "VLAN"); - } - vty_out(vty, "%-17s %-6s %-21s\n", buf1, "remote", - inet_ntoa(mac->fwd_info.r_vtep_ip)); - } else { - json_object_string_add(json_mac, "type", "remote"); - json_object_string_add(json_mac, "remoteVtep", - inet_ntoa(mac->fwd_info.r_vtep_ip)); - json_object_object_add(json_mac_hdr, buf1, json_mac); - json_object_int_add(json_mac, "localSequence", - mac->loc_seq); - json_object_int_add(json_mac, "remoteSequence", - mac->rem_seq); - json_object_int_add(json_mac, "detectionCount", - mac->dad_count); - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) - json_object_boolean_true_add(json_mac, - "isDuplicate"); - else - json_object_boolean_false_add(json_mac, - "isDuplicate"); - + json, "vniFilter", + CHECK_FLAG(zl3vni->filter, PREFIX_ROUTES_ONLY) + ? "prefix-routes-only" + : "none"); + for (ALL_LIST_ELEMENTS(zl3vni->l2vnis, node, nnode, zevpn)) { + json_object_array_add(json_evpn_list, + json_object_new_int(zevpn->vni)); } - - wctx->count++; + json_object_object_add(json, "l2Vnis", json_evpn_list); } } -/* Print Duplicate MAC */ -static void zvni_print_dad_mac_hash(struct hash_bucket *bucket, void *ctxt) -{ - zebra_mac_t *mac; - - mac = (zebra_mac_t *)bucket->data; - if (!mac) - return; - - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) - zvni_print_mac_hash(bucket, ctxt); -} - -/* - * Print MAC hash entry in detail - called for display of all MACs. - */ -static void zvni_print_mac_hash_detail(struct hash_bucket *bucket, void *ctxt) -{ - struct vty *vty; - json_object *json_mac_hdr = NULL; - zebra_mac_t *mac; - struct mac_walk_ctx *wctx = ctxt; - char buf1[ETHER_ADDR_STRLEN]; - - vty = wctx->vty; - json_mac_hdr = wctx->json; - mac = (zebra_mac_t *)bucket->data; - if (!mac) - return; - - wctx->count++; - prefix_mac2str(&mac->macaddr, buf1, sizeof(buf1)); - - zvni_print_mac(mac, vty, json_mac_hdr); -} - -/* Print Duplicate MAC in detail */ -static void zvni_print_dad_mac_hash_detail(struct hash_bucket *bucket, - void *ctxt) -{ - zebra_mac_t *mac; - - mac = (zebra_mac_t *)bucket->data; - if (!mac) - return; - - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) - zvni_print_mac_hash_detail(bucket, ctxt); -} - -/* - * Print MACs for all VNI. - */ -static void zvni_print_mac_hash_all_vni(struct hash_bucket *bucket, void *ctxt) +/* print a L3 VNI hash entry */ +static void zl3vni_print_hash(struct hash_bucket *bucket, void *ctx[]) { - struct vty *vty; - json_object *json = NULL, *json_vni = NULL; - json_object *json_mac = NULL; - zebra_vni_t *zvni; - uint32_t num_macs; - struct mac_walk_ctx *wctx = ctxt; - char vni_str[VNI_STR_LEN]; - - vty = (struct vty *)wctx->vty; - json = (struct json_object *)wctx->json; - - zvni = (zebra_vni_t *)bucket->data; - wctx->zvni = zvni; + struct vty *vty = NULL; + json_object *json = NULL; + json_object *json_evpn = NULL; + zebra_l3vni_t *zl3vni = NULL; - /*We are iterating over a new VNI, set the count to 0*/ - wctx->count = 0; - - num_macs = num_valid_macs(zvni); - if (!num_macs) - return; - - if (wctx->print_dup) - num_macs = num_dup_detected_macs(zvni); - - if (json) { - json_vni = json_object_new_object(); - json_mac = json_object_new_object(); - snprintf(vni_str, VNI_STR_LEN, "%u", zvni->vni); - } - - if (!CHECK_FLAG(wctx->flags, SHOW_REMOTE_MAC_FROM_VTEP)) { - if (json == NULL) { - vty_out(vty, "\nVNI %u #MACs (local and remote) %u\n\n", - zvni->vni, num_macs); - vty_out(vty, "%-17s %-6s %-21s %-5s\n", "MAC", "Type", - "Intf/Remote VTEP", "VLAN"); - } else - json_object_int_add(json_vni, "numMacs", num_macs); - } - - if (!num_macs) { - if (json) { - json_object_int_add(json_vni, "numMacs", num_macs); - json_object_object_add(json, vni_str, json_vni); - } - return; - } - - /* assign per-vni to wctx->json object to fill macs - * under the vni. Re-assign primary json object to fill - * next vni information. - */ - wctx->json = json_mac; - if (wctx->print_dup) - hash_iterate(zvni->mac_table, zvni_print_dad_mac_hash, wctx); - else - hash_iterate(zvni->mac_table, zvni_print_mac_hash, wctx); - wctx->json = json; - if (json) { - if (wctx->count) - json_object_object_add(json_vni, "macs", json_mac); - json_object_object_add(json, vni_str, json_vni); - } -} - -/* - * Print MACs in detail for all VNI. - */ -static void zvni_print_mac_hash_all_vni_detail(struct hash_bucket *bucket, - void *ctxt) -{ - struct vty *vty; - json_object *json = NULL, *json_vni = NULL; - json_object *json_mac = NULL; - zebra_vni_t *zvni; - uint32_t num_macs; - struct mac_walk_ctx *wctx = ctxt; - char vni_str[VNI_STR_LEN]; - - vty = (struct vty *)wctx->vty; - json = (struct json_object *)wctx->json; - - zvni = (zebra_vni_t *)bucket->data; - if (!zvni) { - if (json) - vty_out(vty, "{}\n"); - return; - } - wctx->zvni = zvni; - - /*We are iterating over a new VNI, set the count to 0*/ - wctx->count = 0; - - num_macs = num_valid_macs(zvni); - if (!num_macs) - return; - - if (wctx->print_dup && (num_dup_detected_macs(zvni) == 0)) - return; - - if (json) { - json_vni = json_object_new_object(); - json_mac = json_object_new_object(); - snprintf(vni_str, VNI_STR_LEN, "%u", zvni->vni); - } - - if (!CHECK_FLAG(wctx->flags, SHOW_REMOTE_MAC_FROM_VTEP)) { - if (json == NULL) { - vty_out(vty, "\nVNI %u #MACs (local and remote) %u\n\n", - zvni->vni, num_macs); - } else - json_object_int_add(json_vni, "numMacs", num_macs); - } - /* assign per-vni to wctx->json object to fill macs - * under the vni. Re-assign primary json object to fill - * next vni information. - */ - wctx->json = json_mac; - if (wctx->print_dup) - hash_iterate(zvni->mac_table, zvni_print_dad_mac_hash_detail, - wctx); - else - hash_iterate(zvni->mac_table, zvni_print_mac_hash_detail, wctx); - wctx->json = json; - if (json) { - if (wctx->count) - json_object_object_add(json_vni, "macs", json_mac); - json_object_object_add(json, vni_str, json_vni); - } -} - -static void zl3vni_print_nh_hash(struct hash_bucket *bucket, void *ctx) -{ - struct nh_walk_ctx *wctx = NULL; - struct vty *vty = NULL; - struct json_object *json_vni = NULL; - struct json_object *json_nh = NULL; - zebra_neigh_t *n = NULL; - char buf1[ETHER_ADDR_STRLEN]; - char buf2[INET6_ADDRSTRLEN]; - - wctx = (struct nh_walk_ctx *)ctx; - vty = wctx->vty; - json_vni = wctx->json; - if (json_vni) - json_nh = json_object_new_object(); - n = (zebra_neigh_t *)bucket->data; - - if (!json_vni) { - vty_out(vty, "%-15s %-17s\n", - ipaddr2str(&(n->ip), buf2, sizeof(buf2)), - prefix_mac2str(&n->emac, buf1, sizeof(buf1))); - } else { - json_object_string_add(json_nh, "nexthopIp", - ipaddr2str(&n->ip, buf2, sizeof(buf2))); - json_object_string_add( - json_nh, "routerMac", - prefix_mac2str(&n->emac, buf1, sizeof(buf1))); - json_object_object_add(json_vni, - ipaddr2str(&(n->ip), buf2, sizeof(buf2)), - json_nh); - } -} - -static void zl3vni_print_nh_hash_all_vni(struct hash_bucket *bucket, - void **args) -{ - struct vty *vty = NULL; - json_object *json = NULL; - json_object *json_vni = NULL; - zebra_l3vni_t *zl3vni = NULL; - uint32_t num_nh = 0; - struct nh_walk_ctx wctx; - char vni_str[VNI_STR_LEN]; - - vty = (struct vty *)args[0]; - json = (struct json_object *)args[1]; - - zl3vni = (zebra_l3vni_t *)bucket->data; - - num_nh = hashcount(zl3vni->nh_table); - if (!num_nh) - return; - - if (json) { - json_vni = json_object_new_object(); - snprintf(vni_str, VNI_STR_LEN, "%u", zl3vni->vni); - } - - if (json == NULL) { - vty_out(vty, "\nVNI %u #Next-Hops %u\n\n", zl3vni->vni, num_nh); - vty_out(vty, "%-15s %-17s\n", "IP", "RMAC"); - } else - json_object_int_add(json_vni, "numNextHops", num_nh); - - memset(&wctx, 0, sizeof(struct nh_walk_ctx)); - wctx.vty = vty; - wctx.json = json_vni; - hash_iterate(zl3vni->nh_table, zl3vni_print_nh_hash, &wctx); - if (json) - json_object_object_add(json, vni_str, json_vni); -} - -static void zl3vni_print_rmac_hash_all_vni(struct hash_bucket *bucket, - void **args) -{ - struct vty *vty = NULL; - json_object *json = NULL; - json_object *json_vni = NULL; - zebra_l3vni_t *zl3vni = NULL; - uint32_t num_rmacs; - struct rmac_walk_ctx wctx; - char vni_str[VNI_STR_LEN]; - - vty = (struct vty *)args[0]; - json = (struct json_object *)args[1]; - - zl3vni = (zebra_l3vni_t *)bucket->data; - - num_rmacs = hashcount(zl3vni->rmac_table); - if (!num_rmacs) - return; - - if (json) { - json_vni = json_object_new_object(); - snprintf(vni_str, VNI_STR_LEN, "%u", zl3vni->vni); - } - - if (json == NULL) { - vty_out(vty, "\nVNI %u #RMACs %u\n\n", zl3vni->vni, num_rmacs); - vty_out(vty, "%-17s %-21s\n", "RMAC", "Remote VTEP"); - } else - json_object_int_add(json_vni, "numRmacs", num_rmacs); - - /* assign per-vni to wctx->json object to fill macs - * under the vni. Re-assign primary json object to fill - * next vni information. - */ - memset(&wctx, 0, sizeof(struct rmac_walk_ctx)); - wctx.vty = vty; - wctx.json = json_vni; - hash_iterate(zl3vni->rmac_table, zl3vni_print_rmac_hash, &wctx); - if (json) - json_object_object_add(json, vni_str, json_vni); -} - -static void zl3vni_print_rmac_hash(struct hash_bucket *bucket, void *ctx) -{ - zebra_mac_t *zrmac = NULL; - struct rmac_walk_ctx *wctx = NULL; - struct vty *vty = NULL; - struct json_object *json = NULL; - struct json_object *json_rmac = NULL; - char buf[ETHER_ADDR_STRLEN]; - - wctx = (struct rmac_walk_ctx *)ctx; - vty = wctx->vty; - json = wctx->json; - if (json) - json_rmac = json_object_new_object(); - zrmac = (zebra_mac_t *)bucket->data; - - if (!json) { - vty_out(vty, "%-17s %-21s\n", - prefix_mac2str(&zrmac->macaddr, buf, sizeof(buf)), - inet_ntoa(zrmac->fwd_info.r_vtep_ip)); - } else { - json_object_string_add( - json_rmac, "routerMac", - prefix_mac2str(&zrmac->macaddr, buf, sizeof(buf))); - json_object_string_add(json_rmac, "vtepIp", - inet_ntoa(zrmac->fwd_info.r_vtep_ip)); - json_object_object_add( - json, prefix_mac2str(&zrmac->macaddr, buf, sizeof(buf)), - json_rmac); - } -} - -/* print a specific L3 VNI entry */ -static void zl3vni_print(zebra_l3vni_t *zl3vni, void **ctx) -{ - char buf[ETHER_ADDR_STRLEN]; - struct vty *vty = NULL; - json_object *json = NULL; - zebra_vni_t *zvni = NULL; - json_object *json_vni_list = NULL; - struct listnode *node = NULL, *nnode = NULL; - - vty = ctx[0]; - json = ctx[1]; - - if (!json) { - vty_out(vty, "VNI: %u\n", zl3vni->vni); - vty_out(vty, " Type: %s\n", "L3"); - vty_out(vty, " Tenant VRF: %s\n", zl3vni_vrf_name(zl3vni)); - vty_out(vty, " Local Vtep Ip: %s\n", - inet_ntoa(zl3vni->local_vtep_ip)); - vty_out(vty, " Vxlan-Intf: %s\n", - zl3vni_vxlan_if_name(zl3vni)); - vty_out(vty, " SVI-If: %s\n", zl3vni_svi_if_name(zl3vni)); - vty_out(vty, " State: %s\n", zl3vni_state2str(zl3vni)); - vty_out(vty, " VNI Filter: %s\n", - CHECK_FLAG(zl3vni->filter, PREFIX_ROUTES_ONLY) - ? "prefix-routes-only" - : "none"); - vty_out(vty, " Router MAC: %s\n", - zl3vni_rmac2str(zl3vni, buf, sizeof(buf))); - vty_out(vty, " L2 VNIs: "); - for (ALL_LIST_ELEMENTS(zl3vni->l2vnis, node, nnode, zvni)) - vty_out(vty, "%u ", zvni->vni); - vty_out(vty, "\n"); - } else { - json_vni_list = json_object_new_array(); - json_object_int_add(json, "vni", zl3vni->vni); - json_object_string_add(json, "type", "L3"); - json_object_string_add(json, "localVtepIp", - inet_ntoa(zl3vni->local_vtep_ip)); - json_object_string_add(json, "vxlanIntf", - zl3vni_vxlan_if_name(zl3vni)); - json_object_string_add(json, "sviIntf", - zl3vni_svi_if_name(zl3vni)); - json_object_string_add(json, "state", zl3vni_state2str(zl3vni)); - json_object_string_add(json, "vrf", zl3vni_vrf_name(zl3vni)); - json_object_string_add( - json, "routerMac", - zl3vni_rmac2str(zl3vni, buf, sizeof(buf))); - json_object_string_add( - json, "vniFilter", - CHECK_FLAG(zl3vni->filter, PREFIX_ROUTES_ONLY) - ? "prefix-routes-only" - : "none"); - for (ALL_LIST_ELEMENTS(zl3vni->l2vnis, node, nnode, zvni)) { - json_object_array_add(json_vni_list, - json_object_new_int(zvni->vni)); - } - json_object_object_add(json, "l2Vnis", json_vni_list); - } -} - -/* - * Print a specific VNI entry. - */ -static void zvni_print(zebra_vni_t *zvni, void **ctxt) -{ - struct vty *vty; - zebra_vtep_t *zvtep; - uint32_t num_macs; - uint32_t num_neigh; - json_object *json = NULL; - json_object *json_vtep_list = NULL; - json_object *json_ip_str = NULL; - - vty = ctxt[0]; - json = ctxt[1]; - - if (json == NULL) { - vty_out(vty, "VNI: %u\n", zvni->vni); - vty_out(vty, " Type: %s\n", "L2"); - vty_out(vty, " Tenant VRF: %s\n", vrf_id_to_name(zvni->vrf_id)); - } else { - json_object_int_add(json, "vni", zvni->vni); - json_object_string_add(json, "type", "L2"); - json_object_string_add(json, "vrf", - vrf_id_to_name(zvni->vrf_id)); - } - - if (!zvni->vxlan_if) { // unexpected - if (json == NULL) - vty_out(vty, " VxLAN interface: unknown\n"); - return; - } - num_macs = num_valid_macs(zvni); - num_neigh = hashcount(zvni->neigh_table); - if (json == NULL) { - vty_out(vty, " VxLAN interface: %s\n", zvni->vxlan_if->name); - vty_out(vty, " VxLAN ifIndex: %u\n", zvni->vxlan_if->ifindex); - vty_out(vty, " Local VTEP IP: %s\n", - inet_ntoa(zvni->local_vtep_ip)); - vty_out(vty, " Mcast group: %s\n", - inet_ntoa(zvni->mcast_grp)); - } else { - json_object_string_add(json, "vxlanInterface", - zvni->vxlan_if->name); - json_object_int_add(json, "ifindex", zvni->vxlan_if->ifindex); - json_object_string_add(json, "vtepIp", - inet_ntoa(zvni->local_vtep_ip)); - json_object_string_add(json, "mcastGroup", - inet_ntoa(zvni->mcast_grp)); - json_object_string_add(json, "advertiseGatewayMacip", - zvni->advertise_gw_macip ? "Yes" : "No"); - json_object_int_add(json, "numMacs", num_macs); - json_object_int_add(json, "numArpNd", num_neigh); - } - if (!zvni->vteps) { - if (json == NULL) - vty_out(vty, " No remote VTEPs known for this VNI\n"); - } else { - if (json == NULL) - vty_out(vty, " Remote VTEPs for this VNI:\n"); - else - json_vtep_list = json_object_new_array(); - for (zvtep = zvni->vteps; zvtep; zvtep = zvtep->next) { - const char *flood_str = lookup_msg(zvtep_flood_str, - zvtep->flood_control, - VXLAN_FLOOD_STR_DEFAULT); - - if (json == NULL) { - vty_out(vty, " %s flood: %s\n", - inet_ntoa(zvtep->vtep_ip), - flood_str); - } else { - json_ip_str = json_object_new_string( - inet_ntoa(zvtep->vtep_ip)); - json_object_array_add(json_vtep_list, - json_ip_str); - } - } - if (json) - json_object_object_add(json, "numRemoteVteps", - json_vtep_list); - } - if (json == NULL) { - vty_out(vty, - " Number of MACs (local and remote) known for this VNI: %u\n", - num_macs); - vty_out(vty, - " Number of ARPs (IPv4 and IPv6, local and remote) " - "known for this VNI: %u\n", - num_neigh); - vty_out(vty, " Advertise-gw-macip: %s\n", - zvni->advertise_gw_macip ? "Yes" : "No"); - } -} - -/* print a L3 VNI hash entry */ -static void zl3vni_print_hash(struct hash_bucket *bucket, void *ctx[]) -{ - struct vty *vty = NULL; - json_object *json = NULL; - json_object *json_vni = NULL; - zebra_l3vni_t *zl3vni = NULL; - - vty = (struct vty *)ctx[0]; - json = (json_object *)ctx[1]; - - zl3vni = (zebra_l3vni_t *)bucket->data; - - if (!json) { - vty_out(vty, "%-10u %-4s %-21s %-8lu %-8lu %-15s %-37s\n", - zl3vni->vni, "L3", zl3vni_vxlan_if_name(zl3vni), - hashcount(zl3vni->rmac_table), - hashcount(zl3vni->nh_table), "n/a", - zl3vni_vrf_name(zl3vni)); - } else { - char vni_str[VNI_STR_LEN]; - - snprintf(vni_str, VNI_STR_LEN, "%u", zl3vni->vni); - json_vni = json_object_new_object(); - json_object_int_add(json_vni, "vni", zl3vni->vni); - json_object_string_add(json_vni, "vxlanIf", - zl3vni_vxlan_if_name(zl3vni)); - json_object_int_add(json_vni, "numMacs", - hashcount(zl3vni->rmac_table)); - json_object_int_add(json_vni, "numArpNd", - hashcount(zl3vni->nh_table)); - json_object_string_add(json_vni, "numRemoteVteps", "n/a"); - json_object_string_add(json_vni, "type", "L3"); - json_object_string_add(json_vni, "tenantVrf", - zl3vni_vrf_name(zl3vni)); - json_object_object_add(json, vni_str, json_vni); - } -} - -/* Private Structure to pass callback data for hash iterator */ -struct zvni_evpn_show { - struct vty *vty; - json_object *json; - struct zebra_vrf *zvrf; -}; - -/* print a L3 VNI hash entry in detail*/ -static void zl3vni_print_hash_detail(struct hash_bucket *bucket, void *data) -{ - struct vty *vty = NULL; - zebra_l3vni_t *zl3vni = NULL; - json_object *json = NULL; - bool use_json = false; - struct zvni_evpn_show *zes = data; - - vty = zes->vty; - json = zes->json; - - if (json) - use_json = true; - - zl3vni = (zebra_l3vni_t *)bucket->data; - - zebra_vxlan_print_vni(vty, zes->zvrf, zl3vni->vni, use_json); - vty_out(vty, "\n"); -} - - -/* - * Print a VNI hash entry - called for display of all VNIs. - */ -static void zvni_print_hash(struct hash_bucket *bucket, void *ctxt[]) -{ - struct vty *vty; - zebra_vni_t *zvni; - zebra_vtep_t *zvtep; - uint32_t num_vteps = 0; - uint32_t num_macs = 0; - uint32_t num_neigh = 0; - json_object *json = NULL; - json_object *json_vni = NULL; - json_object *json_ip_str = NULL; - json_object *json_vtep_list = NULL; - - vty = ctxt[0]; - json = ctxt[1]; - - zvni = (zebra_vni_t *)bucket->data; - - zvtep = zvni->vteps; - while (zvtep) { - num_vteps++; - zvtep = zvtep->next; - } - - num_macs = num_valid_macs(zvni); - num_neigh = hashcount(zvni->neigh_table); - if (json == NULL) - vty_out(vty, "%-10u %-4s %-21s %-8u %-8u %-15u %-37s\n", - zvni->vni, "L2", - zvni->vxlan_if ? zvni->vxlan_if->name : "unknown", - num_macs, num_neigh, num_vteps, - vrf_id_to_name(zvni->vrf_id)); - else { - char vni_str[VNI_STR_LEN]; - snprintf(vni_str, VNI_STR_LEN, "%u", zvni->vni); - json_vni = json_object_new_object(); - json_object_int_add(json_vni, "vni", zvni->vni); - json_object_string_add(json_vni, "type", "L2"); - json_object_string_add(json_vni, "vxlanIf", - zvni->vxlan_if ? zvni->vxlan_if->name - : "unknown"); - json_object_int_add(json_vni, "numMacs", num_macs); - json_object_int_add(json_vni, "numArpNd", num_neigh); - json_object_int_add(json_vni, "numRemoteVteps", num_vteps); - json_object_string_add(json_vni, "tenantVrf", - vrf_id_to_name(zvni->vrf_id)); - if (num_vteps) { - json_vtep_list = json_object_new_array(); - for (zvtep = zvni->vteps; zvtep; zvtep = zvtep->next) { - json_ip_str = json_object_new_string( - inet_ntoa(zvtep->vtep_ip)); - json_object_array_add(json_vtep_list, - json_ip_str); - } - json_object_object_add(json_vni, "remoteVteps", - json_vtep_list); - } - json_object_object_add(json, vni_str, json_vni); - } -} - -/* - * Print a VNI hash entry in detail - called for display of all VNIs. - */ -static void zvni_print_hash_detail(struct hash_bucket *bucket, void *data) -{ - struct vty *vty; - zebra_vni_t *zvni; - json_object *json = NULL; - bool use_json = false; - struct zvni_evpn_show *zes = data; - - vty = zes->vty; - json = zes->json; - - if (json) - use_json = true; - - zvni = (zebra_vni_t *)bucket->data; - - zebra_vxlan_print_vni(vty, zes->zvrf, zvni->vni, use_json); - vty_out(vty, "\n"); -} - -/* - * Inform BGP about local MACIP. - */ -static int zvni_macip_send_msg_to_client(vni_t vni, struct ethaddr *macaddr, - struct ipaddr *ip, uint8_t flags, - uint32_t seq, int state, uint16_t cmd) -{ - char buf[ETHER_ADDR_STRLEN]; - char buf2[INET6_ADDRSTRLEN]; - int ipa_len; - struct zserv *client = NULL; - struct stream *s = NULL; - - client = zserv_find_client(ZEBRA_ROUTE_BGP, 0); - /* BGP may not be running. */ - if (!client) - return 0; - - s = stream_new(ZEBRA_MAX_PACKET_SIZ); - - zclient_create_header(s, cmd, zebra_vrf_get_evpn_id()); - stream_putl(s, vni); - stream_put(s, macaddr->octet, ETH_ALEN); - if (ip) { - ipa_len = 0; - if (IS_IPADDR_V4(ip)) - ipa_len = IPV4_MAX_BYTELEN; - else if (IS_IPADDR_V6(ip)) - ipa_len = IPV6_MAX_BYTELEN; - - stream_putl(s, ipa_len); /* IP address length */ - if (ipa_len) - stream_put(s, &ip->ip.addr, ipa_len); /* IP address */ - } else - stream_putl(s, 0); /* Just MAC. */ - - if (cmd == ZEBRA_MACIP_ADD) { - stream_putc(s, flags); /* sticky mac/gateway mac */ - stream_putl(s, seq); /* sequence number */ - } else { - stream_putl(s, state); /* state - active/inactive */ - } - - - /* Write packet size. */ - stream_putw_at(s, 0, stream_get_endp(s)); - - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "Send MACIP %s flags 0x%x MAC %s IP %s seq %u L2-VNI %u to %s", - (cmd == ZEBRA_MACIP_ADD) ? "Add" : "Del", flags, - prefix_mac2str(macaddr, buf, sizeof(buf)), - ipaddr2str(ip, buf2, sizeof(buf2)), seq, vni, - zebra_route_string(client->proto)); - - if (cmd == ZEBRA_MACIP_ADD) - client->macipadd_cnt++; - else - client->macipdel_cnt++; - - return zserv_send_message(client, s); -} - -/* - * Make hash key for neighbors. - */ -static unsigned int neigh_hash_keymake(void *p) -{ - zebra_neigh_t *n = p; - struct ipaddr *ip = &n->ip; - - if (IS_IPADDR_V4(ip)) - return jhash_1word(ip->ipaddr_v4.s_addr, 0); - - return jhash2(ip->ipaddr_v6.s6_addr32, - array_size(ip->ipaddr_v6.s6_addr32), 0); -} - -/* - * Compare two neighbor hash structures. - */ -static bool neigh_cmp(const void *p1, const void *p2) -{ - const zebra_neigh_t *n1 = p1; - const zebra_neigh_t *n2 = p2; - - if (n1 == NULL && n2 == NULL) - return true; - - if (n1 == NULL || n2 == NULL) - return false; - - return (memcmp(&n1->ip, &n2->ip, sizeof(struct ipaddr)) == 0); -} - -static int neigh_list_cmp(void *p1, void *p2) -{ - const zebra_neigh_t *n1 = p1; - const zebra_neigh_t *n2 = p2; - - return memcmp(&n1->ip, &n2->ip, sizeof(struct ipaddr)); -} - -/* - * Callback to allocate neighbor hash entry. - */ -static void *zvni_neigh_alloc(void *p) -{ - const zebra_neigh_t *tmp_n = p; - zebra_neigh_t *n; - - n = XCALLOC(MTYPE_NEIGH, sizeof(zebra_neigh_t)); - *n = *tmp_n; - - return ((void *)n); -} - -/* - * Add neighbor entry. - */ -static zebra_neigh_t *zvni_neigh_add(zebra_vni_t *zvni, struct ipaddr *ip, - struct ethaddr *mac) -{ - zebra_neigh_t tmp_n; - zebra_neigh_t *n = NULL; - zebra_mac_t *zmac = NULL; - - memset(&tmp_n, 0, sizeof(zebra_neigh_t)); - memcpy(&tmp_n.ip, ip, sizeof(struct ipaddr)); - n = hash_get(zvni->neigh_table, &tmp_n, zvni_neigh_alloc); - assert(n); - - memcpy(&n->emac, mac, ETH_ALEN); - n->state = ZEBRA_NEIGH_INACTIVE; - n->zvni = zvni; - n->dad_ip_auto_recovery_timer = NULL; - - /* Associate the neigh to mac */ - zmac = zvni_mac_lookup(zvni, mac); - if (zmac) - listnode_add_sort(zmac->neigh_list, n); - - return n; -} - -/* - * Delete neighbor entry. - */ -static int zvni_neigh_del(zebra_vni_t *zvni, zebra_neigh_t *n) -{ - zebra_neigh_t *tmp_n; - zebra_mac_t *zmac = NULL; - - zmac = zvni_mac_lookup(zvni, &n->emac); - if (zmac) - listnode_delete(zmac->neigh_list, n); - - /* Cancel auto recovery */ - THREAD_OFF(n->dad_ip_auto_recovery_timer); - - /* Free the VNI hash entry and allocated memory. */ - tmp_n = hash_release(zvni->neigh_table, n); - XFREE(MTYPE_NEIGH, tmp_n); - - return 0; -} - -/* - * Free neighbor hash entry (callback) - */ -static void zvni_neigh_del_hash_entry(struct hash_bucket *bucket, void *arg) -{ - struct neigh_walk_ctx *wctx = arg; - zebra_neigh_t *n = bucket->data; - - if (((wctx->flags & DEL_LOCAL_NEIGH) && (n->flags & ZEBRA_NEIGH_LOCAL)) - || ((wctx->flags & DEL_REMOTE_NEIGH) - && (n->flags & ZEBRA_NEIGH_REMOTE)) - || ((wctx->flags & DEL_REMOTE_NEIGH_FROM_VTEP) - && (n->flags & ZEBRA_NEIGH_REMOTE) - && IPV4_ADDR_SAME(&n->r_vtep_ip, &wctx->r_vtep_ip))) { - if (wctx->upd_client && (n->flags & ZEBRA_NEIGH_LOCAL)) - zvni_neigh_send_del_to_client(wctx->zvni->vni, &n->ip, - &n->emac, 0, n->state); - - if (wctx->uninstall) - zvni_neigh_uninstall(wctx->zvni, n); - - zvni_neigh_del(wctx->zvni, n); - } - - return; -} - -/* - * Delete all neighbor entries from specific VTEP for a particular VNI. - */ -static void zvni_neigh_del_from_vtep(zebra_vni_t *zvni, int uninstall, - struct in_addr *r_vtep_ip) -{ - struct neigh_walk_ctx wctx; - - if (!zvni->neigh_table) - return; - - memset(&wctx, 0, sizeof(struct neigh_walk_ctx)); - wctx.zvni = zvni; - wctx.uninstall = uninstall; - wctx.flags = DEL_REMOTE_NEIGH_FROM_VTEP; - wctx.r_vtep_ip = *r_vtep_ip; - - hash_iterate(zvni->neigh_table, zvni_neigh_del_hash_entry, &wctx); -} - -/* - * Delete all neighbor entries for this VNI. - */ -static void zvni_neigh_del_all(zebra_vni_t *zvni, int uninstall, int upd_client, - uint32_t flags) -{ - struct neigh_walk_ctx wctx; - - if (!zvni->neigh_table) - return; - - memset(&wctx, 0, sizeof(struct neigh_walk_ctx)); - wctx.zvni = zvni; - wctx.uninstall = uninstall; - wctx.upd_client = upd_client; - wctx.flags = flags; - - hash_iterate(zvni->neigh_table, zvni_neigh_del_hash_entry, &wctx); -} - -/* - * Look up neighbor hash entry. - */ -static zebra_neigh_t *zvni_neigh_lookup(zebra_vni_t *zvni, struct ipaddr *ip) -{ - zebra_neigh_t tmp; - zebra_neigh_t *n; - - memset(&tmp, 0, sizeof(tmp)); - memcpy(&tmp.ip, ip, sizeof(struct ipaddr)); - n = hash_lookup(zvni->neigh_table, &tmp); - - return n; -} - -/* - * Process all neighbors associated with a MAC upon the MAC being learnt - * locally or undergoing any other change (such as sequence number). - */ -static void zvni_process_neigh_on_local_mac_change(zebra_vni_t *zvni, - zebra_mac_t *zmac, - bool seq_change) -{ - zebra_neigh_t *n = NULL; - struct listnode *node = NULL; - struct zebra_vrf *zvrf = NULL; - char buf[ETHER_ADDR_STRLEN]; - - zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id); - - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Processing neighbors on local MAC %s %s, VNI %u", - prefix_mac2str(&zmac->macaddr, buf, sizeof(buf)), - seq_change ? "CHANGE" : "ADD", zvni->vni); - - /* Walk all neighbors and mark any inactive local neighbors as - * active and/or update sequence number upon a move, and inform BGP. - * The action for remote neighbors is TBD. - * NOTE: We can't simply uninstall remote neighbors as the kernel may - * accidentally end up deleting a just-learnt local neighbor. - */ - for (ALL_LIST_ELEMENTS_RO(zmac->neigh_list, node, n)) { - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { - if (IS_ZEBRA_NEIGH_INACTIVE(n) || seq_change) { - ZEBRA_NEIGH_SET_ACTIVE(n); - n->loc_seq = zmac->loc_seq; - if (!(zvrf->dup_addr_detect && - zvrf->dad_freeze && !!CHECK_FLAG(n->flags, - ZEBRA_NEIGH_DUPLICATE))) - zvni_neigh_send_add_to_client( - zvni->vni, &n->ip, &n->emac, - n->flags, n->loc_seq); - } - } - } -} - -/* - * Process all neighbors associated with a local MAC upon the MAC being - * deleted. - */ -static void zvni_process_neigh_on_local_mac_del(zebra_vni_t *zvni, - zebra_mac_t *zmac) -{ - zebra_neigh_t *n = NULL; - struct listnode *node = NULL; - char buf[ETHER_ADDR_STRLEN]; - - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Processing neighbors on local MAC %s DEL, VNI %u", - prefix_mac2str(&zmac->macaddr, buf, sizeof(buf)), - zvni->vni); - - /* Walk all local neighbors and mark as inactive and inform - * BGP, if needed. - * TBD: There is currently no handling for remote neighbors. We - * don't expect them to exist, if they do, do we install the MAC - * as a remote MAC and the neighbor as remote? - */ - for (ALL_LIST_ELEMENTS_RO(zmac->neigh_list, node, n)) { - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { - if (IS_ZEBRA_NEIGH_ACTIVE(n)) { - ZEBRA_NEIGH_SET_INACTIVE(n); - n->loc_seq = 0; - zvni_neigh_send_del_to_client(zvni->vni, &n->ip, - &n->emac, 0, ZEBRA_NEIGH_ACTIVE); - } - } - } -} - -/* - * Process all neighbors associated with a MAC upon the MAC being remotely - * learnt. - */ -static void zvni_process_neigh_on_remote_mac_add(zebra_vni_t *zvni, - zebra_mac_t *zmac) -{ - zebra_neigh_t *n = NULL; - struct listnode *node = NULL; - char buf[ETHER_ADDR_STRLEN]; - - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Processing neighbors on remote MAC %s ADD, VNI %u", - prefix_mac2str(&zmac->macaddr, buf, sizeof(buf)), - zvni->vni); - - /* Walk all local neighbors and mark as inactive and inform - * BGP, if needed. - */ - for (ALL_LIST_ELEMENTS_RO(zmac->neigh_list, node, n)) { - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { - if (IS_ZEBRA_NEIGH_ACTIVE(n)) { - ZEBRA_NEIGH_SET_INACTIVE(n); - n->loc_seq = 0; - zvni_neigh_send_del_to_client(zvni->vni, &n->ip, - &n->emac, 0, ZEBRA_NEIGH_ACTIVE); - } - } - } -} - -/* - * Process all neighbors associated with a remote MAC upon the MAC being - * deleted. - */ -static void zvni_process_neigh_on_remote_mac_del(zebra_vni_t *zvni, - zebra_mac_t *zmac) -{ - /* NOTE: Currently a NO-OP. */ -} - -static void zvni_probe_neigh_on_mac_add(zebra_vni_t *zvni, zebra_mac_t *zmac) -{ - zebra_neigh_t *nbr = NULL; - struct listnode *node = NULL; - - for (ALL_LIST_ELEMENTS_RO(zmac->neigh_list, node, nbr)) { - if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL) && - IS_ZEBRA_NEIGH_INACTIVE(nbr)) - zvni_neigh_probe(zvni, nbr); - } -} - -/* - * Inform BGP about local neighbor addition. - */ -static int zvni_neigh_send_add_to_client(vni_t vni, struct ipaddr *ip, - struct ethaddr *macaddr, - uint8_t neigh_flags, - uint32_t seq) -{ - uint8_t flags = 0; - - if (CHECK_FLAG(neigh_flags, ZEBRA_NEIGH_DEF_GW)) - SET_FLAG(flags, ZEBRA_MACIP_TYPE_GW); - /* Set router flag (R-bit) based on local neigh entry add */ - if (CHECK_FLAG(neigh_flags, ZEBRA_NEIGH_ROUTER_FLAG)) - SET_FLAG(flags, ZEBRA_MACIP_TYPE_ROUTER_FLAG); - - return zvni_macip_send_msg_to_client(vni, macaddr, ip, flags, - seq, ZEBRA_NEIGH_ACTIVE, ZEBRA_MACIP_ADD); -} - -/* - * Inform BGP about local neighbor deletion. - */ -static int zvni_neigh_send_del_to_client(vni_t vni, struct ipaddr *ip, - struct ethaddr *macaddr, uint8_t flags, - int state) -{ - return zvni_macip_send_msg_to_client(vni, macaddr, ip, flags, - 0, state, ZEBRA_MACIP_DEL); -} - -/* - * Install remote neighbor into the kernel. - */ -static int zvni_neigh_install(zebra_vni_t *zvni, zebra_neigh_t *n) -{ - struct zebra_if *zif; - struct zebra_l2info_vxlan *vxl; - struct interface *vlan_if; -#ifdef GNU_LINUX - uint8_t flags; -#endif - int ret = 0; - - if (!(n->flags & ZEBRA_NEIGH_REMOTE)) - return 0; - - zif = zvni->vxlan_if->info; - if (!zif) - return -1; - vxl = &zif->l2info.vxl; - - vlan_if = zvni_map_to_svi(vxl->access_vlan, zif->brslave_info.br_if); - if (!vlan_if) - return -1; -#ifdef GNU_LINUX - flags = NTF_EXT_LEARNED; - if (n->flags & ZEBRA_NEIGH_ROUTER_FLAG) - flags |= NTF_ROUTER; - ZEBRA_NEIGH_SET_ACTIVE(n); - ret = kernel_add_neigh(vlan_if, &n->ip, &n->emac, flags); -#endif - return ret; -} - -/* - * Uninstall remote neighbor from the kernel. - */ -static int zvni_neigh_uninstall(zebra_vni_t *zvni, zebra_neigh_t *n) -{ - struct zebra_if *zif; - struct zebra_l2info_vxlan *vxl; - struct interface *vlan_if; - - if (!(n->flags & ZEBRA_NEIGH_REMOTE)) - return 0; - - if (!zvni->vxlan_if) { - zlog_debug("VNI %u hash %p couldn't be uninstalled - no intf", - zvni->vni, zvni); - return -1; - } - - zif = zvni->vxlan_if->info; - if (!zif) - return -1; - vxl = &zif->l2info.vxl; - vlan_if = zvni_map_to_svi(vxl->access_vlan, zif->brslave_info.br_if); - if (!vlan_if) - return -1; - - ZEBRA_NEIGH_SET_INACTIVE(n); - n->loc_seq = 0; - return kernel_del_neigh(vlan_if, &n->ip); -} - -/* - * Probe neighbor from the kernel. - */ -static int zvni_neigh_probe(zebra_vni_t *zvni, zebra_neigh_t *n) -{ - struct zebra_if *zif; - struct zebra_l2info_vxlan *vxl; - struct interface *vlan_if; - - zif = zvni->vxlan_if->info; - if (!zif) - return -1; - vxl = &zif->l2info.vxl; - - vlan_if = zvni_map_to_svi(vxl->access_vlan, zif->brslave_info.br_if); - if (!vlan_if) - return -1; - -#ifdef GNU_LINUX - return kernel_upd_neigh(vlan_if, &n->ip, &n->emac, - 0, NUD_PROBE); -#else - return 0; -#endif -} - -/* - * Install neighbor hash entry - called upon access VLAN change. - */ -static void zvni_install_neigh_hash(struct hash_bucket *bucket, void *ctxt) -{ - zebra_neigh_t *n; - struct neigh_walk_ctx *wctx = ctxt; - - n = (zebra_neigh_t *)bucket->data; - - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) - zvni_neigh_install(wctx->zvni, n); -} - -/* Get the VRR interface for SVI if any */ -struct interface *zebra_get_vrr_intf_for_svi(struct interface *ifp) -{ - struct zebra_vrf *zvrf = NULL; - struct interface *tmp_if = NULL; - struct zebra_if *zif = NULL; - - zvrf = vrf_info_lookup(ifp->vrf_id); - assert(zvrf); - - FOR_ALL_INTERFACES (zvrf->vrf, tmp_if) { - zif = tmp_if->info; - if (!zif) - continue; - - if (!IS_ZEBRA_IF_MACVLAN(tmp_if)) - continue; - - if (zif->link == ifp) - return tmp_if; - } - - return NULL; -} - -static int zvni_del_macip_for_intf(struct interface *ifp, zebra_vni_t *zvni) -{ - struct listnode *cnode = NULL, *cnnode = NULL; - struct connected *c = NULL; - struct ethaddr macaddr; - - memcpy(&macaddr.octet, ifp->hw_addr, ETH_ALEN); - - for (ALL_LIST_ELEMENTS(ifp->connected, cnode, cnnode, c)) { - struct ipaddr ip; - - memset(&ip, 0, sizeof(struct ipaddr)); - if (!CHECK_FLAG(c->conf, ZEBRA_IFC_REAL)) - continue; - - if (c->address->family == AF_INET) { - ip.ipa_type = IPADDR_V4; - memcpy(&(ip.ipaddr_v4), &(c->address->u.prefix4), - sizeof(struct in_addr)); - } else if (c->address->family == AF_INET6) { - ip.ipa_type = IPADDR_V6; - memcpy(&(ip.ipaddr_v6), &(c->address->u.prefix6), - sizeof(struct in6_addr)); - } else { - continue; - } - - zvni_gw_macip_del(ifp, zvni, &ip); - } - - return 0; -} - -static int zvni_add_macip_for_intf(struct interface *ifp, zebra_vni_t *zvni) -{ - struct listnode *cnode = NULL, *cnnode = NULL; - struct connected *c = NULL; - struct ethaddr macaddr; - - memcpy(&macaddr.octet, ifp->hw_addr, ETH_ALEN); - - for (ALL_LIST_ELEMENTS(ifp->connected, cnode, cnnode, c)) { - struct ipaddr ip; - - memset(&ip, 0, sizeof(struct ipaddr)); - if (!CHECK_FLAG(c->conf, ZEBRA_IFC_REAL)) - continue; - - if (c->address->family == AF_INET) { - ip.ipa_type = IPADDR_V4; - memcpy(&(ip.ipaddr_v4), &(c->address->u.prefix4), - sizeof(struct in_addr)); - } else if (c->address->family == AF_INET6) { - ip.ipa_type = IPADDR_V6; - memcpy(&(ip.ipaddr_v6), &(c->address->u.prefix6), - sizeof(struct in6_addr)); - } else { - continue; - } - - zvni_gw_macip_add(ifp, zvni, &macaddr, &ip); - } - return 0; -} - - -static int zvni_advertise_subnet(zebra_vni_t *zvni, struct interface *ifp, - int advertise) -{ - struct listnode *cnode = NULL, *cnnode = NULL; - struct connected *c = NULL; - struct ethaddr macaddr; - - memcpy(&macaddr.octet, ifp->hw_addr, ETH_ALEN); - - for (ALL_LIST_ELEMENTS(ifp->connected, cnode, cnnode, c)) { - struct prefix p; - - memcpy(&p, c->address, sizeof(struct prefix)); - - /* skip link local address */ - if (IN6_IS_ADDR_LINKLOCAL(&p.u.prefix6)) - continue; - - apply_mask(&p); - if (advertise) - ip_prefix_send_to_client(ifp->vrf_id, &p, - ZEBRA_IP_PREFIX_ROUTE_ADD); - else - ip_prefix_send_to_client(ifp->vrf_id, &p, - ZEBRA_IP_PREFIX_ROUTE_DEL); - } - return 0; -} - -/* - * zvni_gw_macip_add_to_client - */ -static int zvni_gw_macip_add(struct interface *ifp, zebra_vni_t *zvni, - struct ethaddr *macaddr, struct ipaddr *ip) -{ - char buf[ETHER_ADDR_STRLEN]; - char buf2[INET6_ADDRSTRLEN]; - zebra_neigh_t *n = NULL; - zebra_mac_t *mac = NULL; - struct zebra_if *zif = NULL; - struct zebra_l2info_vxlan *vxl = NULL; - - zif = zvni->vxlan_if->info; - if (!zif) - return -1; - - vxl = &zif->l2info.vxl; - - mac = zvni_mac_lookup(zvni, macaddr); - if (!mac) { - mac = zvni_mac_add(zvni, macaddr); - if (!mac) { - flog_err(EC_ZEBRA_MAC_ADD_FAILED, - "Failed to add MAC %s intf %s(%u) VID %u", - prefix_mac2str(macaddr, buf, sizeof(buf)), - ifp->name, ifp->ifindex, vxl->access_vlan); - return -1; - } - } - - /* Set "local" forwarding info. */ - SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL); - SET_FLAG(mac->flags, ZEBRA_MAC_AUTO); - SET_FLAG(mac->flags, ZEBRA_MAC_DEF_GW); - memset(&mac->fwd_info, 0, sizeof(mac->fwd_info)); - mac->fwd_info.local.ifindex = ifp->ifindex; - mac->fwd_info.local.vid = vxl->access_vlan; - - n = zvni_neigh_lookup(zvni, ip); - if (!n) { - n = zvni_neigh_add(zvni, ip, macaddr); - if (!n) { - flog_err( - EC_ZEBRA_MAC_ADD_FAILED, - "Failed to add neighbor %s MAC %s intf %s(%u) -> VNI %u", - ipaddr2str(ip, buf2, sizeof(buf2)), - prefix_mac2str(macaddr, buf, sizeof(buf)), - ifp->name, ifp->ifindex, zvni->vni); - return -1; - } - } - - /* Set "local" forwarding info. */ - SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL); - SET_FLAG(n->flags, ZEBRA_NEIGH_DEF_GW); - ZEBRA_NEIGH_SET_ACTIVE(n); - /* Set Router flag (R-bit) */ - if (ip->ipa_type == IPADDR_V6) - SET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); - memcpy(&n->emac, macaddr, ETH_ALEN); - n->ifindex = ifp->ifindex; - - /* Only advertise in BGP if the knob is enabled */ - if (!advertise_gw_macip_enabled(zvni)) - return 0; - - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "SVI %s(%u) L2-VNI %u, sending GW MAC %s IP %s add to BGP with flags 0x%x", - ifp->name, ifp->ifindex, zvni->vni, - prefix_mac2str(macaddr, buf, sizeof(buf)), - ipaddr2str(ip, buf2, sizeof(buf2)), n->flags); - - zvni_neigh_send_add_to_client(zvni->vni, ip, macaddr, - n->flags, n->loc_seq); - - return 0; -} - -/* - * zvni_gw_macip_del_from_client - */ -static int zvni_gw_macip_del(struct interface *ifp, zebra_vni_t *zvni, - struct ipaddr *ip) -{ - char buf1[ETHER_ADDR_STRLEN]; - char buf2[INET6_ADDRSTRLEN]; - zebra_neigh_t *n = NULL; - zebra_mac_t *mac = NULL; - - /* If the neigh entry is not present nothing to do*/ - n = zvni_neigh_lookup(zvni, ip); - if (!n) - return 0; - - /* mac entry should be present */ - mac = zvni_mac_lookup(zvni, &n->emac); - if (!mac) { - zlog_debug("MAC %s doesn't exist for neigh %s on VNI %u", - prefix_mac2str(&n->emac, buf1, sizeof(buf1)), - ipaddr2str(ip, buf2, sizeof(buf2)), zvni->vni); - return -1; - } - - /* If the entry is not local nothing to do*/ - if (!CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) - return -1; - - /* only need to delete the entry from bgp if we sent it before */ - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "%u:SVI %s(%u) VNI %u, sending GW MAC %s IP %s del to BGP", - ifp->vrf_id, ifp->name, ifp->ifindex, zvni->vni, - prefix_mac2str(&(n->emac), buf1, sizeof(buf1)), - ipaddr2str(ip, buf2, sizeof(buf2))); - - /* Remove neighbor from BGP. */ - zvni_neigh_send_del_to_client(zvni->vni, &n->ip, &n->emac, - ZEBRA_MACIP_TYPE_GW, ZEBRA_NEIGH_ACTIVE); - - /* Delete this neighbor entry. */ - zvni_neigh_del(zvni, n); - - /* see if the mac needs to be deleted as well*/ - if (mac) - zvni_deref_ip2mac(zvni, mac); - - return 0; -} - -static void zvni_gw_macip_del_for_vni_hash(struct hash_bucket *bucket, - void *ctxt) -{ - zebra_vni_t *zvni = NULL; - struct zebra_if *zif = NULL; - struct zebra_l2info_vxlan zl2_info; - struct interface *vlan_if = NULL; - struct interface *vrr_if = NULL; - struct interface *ifp; - - /* Add primary SVI MAC*/ - zvni = (zebra_vni_t *)bucket->data; - - ifp = zvni->vxlan_if; - if (!ifp) - return; - zif = ifp->info; - - /* If down or not mapped to a bridge, we're done. */ - if (!if_is_operative(ifp) || !zif->brslave_info.br_if) - return; - - zl2_info = zif->l2info.vxl; - - vlan_if = - zvni_map_to_svi(zl2_info.access_vlan, zif->brslave_info.br_if); - if (!vlan_if) - return; - - /* Del primary MAC-IP */ - zvni_del_macip_for_intf(vlan_if, zvni); - - /* Del VRR MAC-IP - if any*/ - vrr_if = zebra_get_vrr_intf_for_svi(vlan_if); - if (vrr_if) - zvni_del_macip_for_intf(vrr_if, zvni); - - return; -} - -static void zvni_gw_macip_add_for_vni_hash(struct hash_bucket *bucket, - void *ctxt) -{ - zebra_vni_t *zvni = NULL; - struct zebra_if *zif = NULL; - struct zebra_l2info_vxlan zl2_info; - struct interface *vlan_if = NULL; - struct interface *vrr_if = NULL; - struct interface *ifp = NULL; - - zvni = (zebra_vni_t *)bucket->data; - - ifp = zvni->vxlan_if; - if (!ifp) - return; - zif = ifp->info; - - /* If down or not mapped to a bridge, we're done. */ - if (!if_is_operative(ifp) || !zif->brslave_info.br_if) - return; - zl2_info = zif->l2info.vxl; - - vlan_if = - zvni_map_to_svi(zl2_info.access_vlan, zif->brslave_info.br_if); - if (!vlan_if) - return; - - /* Add primary SVI MAC-IP */ - zvni_add_macip_for_intf(vlan_if, zvni); - - if (advertise_gw_macip_enabled(zvni)) { - /* Add VRR MAC-IP - if any*/ - vrr_if = zebra_get_vrr_intf_for_svi(vlan_if); - if (vrr_if) - zvni_add_macip_for_intf(vrr_if, zvni); - } - - return; -} - -static void zvni_svi_macip_del_for_vni_hash(struct hash_bucket *bucket, - void *ctxt) -{ - zebra_vni_t *zvni = NULL; - struct zebra_if *zif = NULL; - struct zebra_l2info_vxlan zl2_info; - struct interface *vlan_if = NULL; - struct interface *ifp; - - /* Add primary SVI MAC*/ - zvni = (zebra_vni_t *)bucket->data; - if (!zvni) - return; - - ifp = zvni->vxlan_if; - if (!ifp) - return; - zif = ifp->info; - - /* If down or not mapped to a bridge, we're done. */ - if (!if_is_operative(ifp) || !zif->brslave_info.br_if) - return; - - zl2_info = zif->l2info.vxl; - - vlan_if = zvni_map_to_svi(zl2_info.access_vlan, - zif->brslave_info.br_if); - if (!vlan_if) - return; - - /* Del primary MAC-IP */ - zvni_del_macip_for_intf(vlan_if, zvni); - - return; -} - -static int zvni_local_neigh_update(zebra_vni_t *zvni, - struct interface *ifp, - struct ipaddr *ip, - struct ethaddr *macaddr, - bool is_router) -{ - char buf[ETHER_ADDR_STRLEN]; - char buf2[INET6_ADDRSTRLEN]; - struct zebra_vrf *zvrf; - zebra_neigh_t *n = NULL; - zebra_mac_t *zmac = NULL, *old_zmac = NULL; - uint32_t old_mac_seq = 0, mac_new_seq = 0; - bool upd_mac_seq = false; - bool neigh_mac_change = false; - bool neigh_on_hold = false; - bool neigh_was_remote = false; - bool do_dad = false; - struct in_addr vtep_ip = {.s_addr = 0}; - - /* Check if the MAC exists. */ - zmac = zvni_mac_lookup(zvni, macaddr); - if (!zmac) { - /* create a dummy MAC if the MAC is not already present */ - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "AUTO MAC %s created for neigh %s on VNI %u", - prefix_mac2str(macaddr, buf, sizeof(buf)), - ipaddr2str(ip, buf2, sizeof(buf2)), zvni->vni); - - zmac = zvni_mac_add(zvni, macaddr); - if (!zmac) { - zlog_debug("Failed to add MAC %s VNI %u", - prefix_mac2str(macaddr, buf, sizeof(buf)), - zvni->vni); - return -1; - } - - memset(&zmac->fwd_info, 0, sizeof(zmac->fwd_info)); - memset(&zmac->flags, 0, sizeof(uint32_t)); - SET_FLAG(zmac->flags, ZEBRA_MAC_AUTO); - } else { - if (CHECK_FLAG(zmac->flags, ZEBRA_MAC_REMOTE)) { - /* - * We don't change the MAC to local upon a neighbor - * learn event, we wait for the explicit local MAC - * learn. However, we have to compute its sequence - * number in preparation for when it actually turns - * local. - */ - upd_mac_seq = true; - } - } - - zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id); - if (!zvrf) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("\tUnable to find vrf for: %d", - zvni->vxlan_if->vrf_id); - return -1; - } - - /* Check if the neighbor exists. */ - n = zvni_neigh_lookup(zvni, ip); - if (!n) { - /* New neighbor - create */ - n = zvni_neigh_add(zvni, ip, macaddr); - if (!n) { - flog_err( - EC_ZEBRA_MAC_ADD_FAILED, - "Failed to add neighbor %s MAC %s intf %s(%u) -> VNI %u", - ipaddr2str(ip, buf2, sizeof(buf2)), - prefix_mac2str(macaddr, buf, sizeof(buf)), - ifp->name, ifp->ifindex, zvni->vni); - return -1; - } - /* Set "local" forwarding info. */ - SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL); - n->ifindex = ifp->ifindex; - } else { - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { - bool mac_different; - bool cur_is_router; - - /* Note any changes and see if of interest to BGP. */ - mac_different = (memcmp(n->emac.octet, - macaddr->octet, ETH_ALEN) != 0) ? 1 : 0; - cur_is_router = !!CHECK_FLAG(n->flags, - ZEBRA_NEIGH_ROUTER_FLAG); - if (!mac_different && is_router == cur_is_router) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "\tIgnoring entry mac is the same and is_router == cur_is_router"); - n->ifindex = ifp->ifindex; - return 0; - } - - if (!mac_different) { - bool is_neigh_freezed = false; - - /* Only the router flag has changed. */ - if (is_router) - SET_FLAG(n->flags, - ZEBRA_NEIGH_ROUTER_FLAG); - else - UNSET_FLAG(n->flags, - ZEBRA_NEIGH_ROUTER_FLAG); - - /* Neigh is in freeze state and freeze action - * is enabled, do not send update to client. - */ - is_neigh_freezed = (zvrf->dup_addr_detect && - zvrf->dad_freeze && - CHECK_FLAG(n->flags, - ZEBRA_NEIGH_DUPLICATE)); - - if (IS_ZEBRA_NEIGH_ACTIVE(n) && - !is_neigh_freezed) - return zvni_neigh_send_add_to_client( - zvni->vni, ip, macaddr, - n->flags, n->loc_seq); - else { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "\tNeighbor active and frozen"); - } - return 0; - } - - /* The MAC has changed, need to issue a delete - * first as this means a different MACIP route. - * Also, need to do some unlinking/relinking. - * We also need to update the MAC's sequence number - * in different situations. - */ - if (IS_ZEBRA_NEIGH_ACTIVE(n)) - zvni_neigh_send_del_to_client(zvni->vni, &n->ip, - &n->emac, 0, n->state); - old_zmac = zvni_mac_lookup(zvni, &n->emac); - if (old_zmac) { - old_mac_seq = CHECK_FLAG(old_zmac->flags, - ZEBRA_MAC_REMOTE) ? - old_zmac->rem_seq : old_zmac->loc_seq; - neigh_mac_change = upd_mac_seq = true; - listnode_delete(old_zmac->neigh_list, n); - zvni_deref_ip2mac(zvni, old_zmac); - } - - /* Update the forwarding info. */ - n->ifindex = ifp->ifindex; - memcpy(&n->emac, macaddr, ETH_ALEN); - - /* Link to new MAC */ - listnode_add_sort(zmac->neigh_list, n); - } else if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) { - /* - * Neighbor has moved from remote to local. Its - * MAC could have also changed as part of the move. - */ - if (memcmp(n->emac.octet, macaddr->octet, - ETH_ALEN) != 0) { - old_zmac = zvni_mac_lookup(zvni, &n->emac); - if (old_zmac) { - old_mac_seq = CHECK_FLAG( - old_zmac->flags, - ZEBRA_MAC_REMOTE) ? - old_zmac->rem_seq : - old_zmac->loc_seq; - neigh_mac_change = upd_mac_seq = true; - listnode_delete(old_zmac->neigh_list, - n); - zvni_deref_ip2mac(zvni, old_zmac); - } - - /* Link to new MAC */ - memcpy(&n->emac, macaddr, ETH_ALEN); - listnode_add_sort(zmac->neigh_list, n); - } - /* Based on Mobility event Scenario-B from the - * draft, neigh's previous state was remote treat this - * event for DAD. - */ - neigh_was_remote = true; - vtep_ip = n->r_vtep_ip; - /* Mark appropriately */ - UNSET_FLAG(n->flags, ZEBRA_NEIGH_REMOTE); - n->r_vtep_ip.s_addr = 0; - SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL); - n->ifindex = ifp->ifindex; - } - } - - /* If MAC was previously remote, or the neighbor had a different - * MAC earlier, recompute the sequence number. - */ - if (upd_mac_seq) { - uint32_t seq1, seq2; - - seq1 = CHECK_FLAG(zmac->flags, ZEBRA_MAC_REMOTE) ? - zmac->rem_seq + 1 : zmac->loc_seq; - seq2 = neigh_mac_change ? old_mac_seq + 1 : 0; - mac_new_seq = zmac->loc_seq < MAX(seq1, seq2) ? - MAX(seq1, seq2) : zmac->loc_seq; - } - - /* Mark Router flag (R-bit) */ - if (is_router) - SET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); - else - UNSET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); - - /* Check old and/or new MAC detected as duplicate mark - * the neigh as duplicate - */ - if (zebra_vxlan_ip_inherit_dad_from_mac(zvrf, old_zmac, zmac, n)) { - flog_warn(EC_ZEBRA_DUP_IP_INHERIT_DETECTED, - "VNI %u: MAC %s IP %s detected as duplicate during local update, inherit duplicate from MAC", - zvni->vni, - prefix_mac2str(macaddr, buf, sizeof(buf)), - ipaddr2str(&n->ip, buf2, sizeof(buf2))); - } - - /* For IP Duplicate Address Detection (DAD) is trigger, - * when the event is extended mobility based on scenario-B - * from the draft, IP/Neigh's MAC binding changed and - * neigh's previous state was remote. - */ - if (neigh_mac_change && neigh_was_remote) - do_dad = true; - - zebra_vxlan_dup_addr_detect_for_neigh(zvrf, n, vtep_ip, do_dad, - &neigh_on_hold, true); - - /* Before we program this in BGP, we need to check if MAC is locally - * learnt. If not, force neighbor to be inactive and reset its seq. - */ - if (!CHECK_FLAG(zmac->flags, ZEBRA_MAC_LOCAL)) { - ZEBRA_NEIGH_SET_INACTIVE(n); - n->loc_seq = 0; - zmac->loc_seq = mac_new_seq; - return 0; - } - - /* If the MAC's sequence number has changed, inform the MAC and all - * neighbors associated with the MAC to BGP, else just inform this - * neighbor. - */ - if (upd_mac_seq && zmac->loc_seq != mac_new_seq) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Seq changed for MAC %s VNI %u - old %u new %u", - prefix_mac2str(macaddr, buf, sizeof(buf)), - zvni->vni, zmac->loc_seq, mac_new_seq); - zmac->loc_seq = mac_new_seq; - if (zvni_mac_send_add_to_client(zvni->vni, macaddr, - zmac->flags, zmac->loc_seq)) - return -1; - zvni_process_neigh_on_local_mac_change(zvni, zmac, 1); - return 0; - } - - n->loc_seq = zmac->loc_seq; - - if (!neigh_on_hold) { - ZEBRA_NEIGH_SET_ACTIVE(n); - - return zvni_neigh_send_add_to_client(zvni->vni, ip, macaddr, - n->flags, n->loc_seq); - } else { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("\tNeighbor on hold not sending"); - } - return 0; -} - -static int zvni_remote_neigh_update(zebra_vni_t *zvni, - struct interface *ifp, - struct ipaddr *ip, - struct ethaddr *macaddr, - uint16_t state) -{ - char buf[ETHER_ADDR_STRLEN]; - char buf2[INET6_ADDRSTRLEN]; - zebra_neigh_t *n = NULL; - zebra_mac_t *zmac = NULL; - - /* If the neighbor is unknown, there is no further action. */ - n = zvni_neigh_lookup(zvni, ip); - if (!n) - return 0; - - /* If a remote entry, see if it needs to be refreshed */ - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) { -#ifdef GNU_LINUX - if (state & NUD_STALE) - zvni_neigh_install(zvni, n); -#endif - } else { - /* We got a "remote" neighbor notification for an entry - * we think is local. This can happen in a multihoming - * scenario - but only if the MAC is already "remote". - * Just mark our entry as "remote". - */ - zmac = zvni_mac_lookup(zvni, macaddr); - if (!zmac || !CHECK_FLAG(zmac->flags, ZEBRA_MAC_REMOTE)) { - zlog_debug( - "Ignore remote neigh %s (MAC %s) on L2-VNI %u - MAC unknown or local", - ipaddr2str(&n->ip, buf2, sizeof(buf2)), - prefix_mac2str(macaddr, buf, sizeof(buf)), - zvni->vni); - return -1; - } - - UNSET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL); - SET_FLAG(n->flags, ZEBRA_NEIGH_REMOTE); - ZEBRA_NEIGH_SET_ACTIVE(n); - n->r_vtep_ip = zmac->fwd_info.r_vtep_ip; - } - - return 0; -} - -/* - * Make hash key for MAC. - */ -static unsigned int mac_hash_keymake(void *p) -{ - zebra_mac_t *pmac = p; - const void *pnt = (void *)pmac->macaddr.octet; - - return jhash(pnt, ETH_ALEN, 0xa5a5a55a); -} - -/* - * Compare two MAC addresses. - */ -static bool mac_cmp(const void *p1, const void *p2) -{ - const zebra_mac_t *pmac1 = p1; - const zebra_mac_t *pmac2 = p2; - - if (pmac1 == NULL && pmac2 == NULL) - return true; - - if (pmac1 == NULL || pmac2 == NULL) - return false; - - return (memcmp(pmac1->macaddr.octet, pmac2->macaddr.octet, ETH_ALEN) - == 0); -} - -/* - * Callback to allocate MAC hash entry. - */ -static void *zvni_mac_alloc(void *p) -{ - const zebra_mac_t *tmp_mac = p; - zebra_mac_t *mac; - - mac = XCALLOC(MTYPE_MAC, sizeof(zebra_mac_t)); - *mac = *tmp_mac; - - return ((void *)mac); -} - -/* - * Add MAC entry. - */ -static zebra_mac_t *zvni_mac_add(zebra_vni_t *zvni, struct ethaddr *macaddr) -{ - zebra_mac_t tmp_mac; - zebra_mac_t *mac = NULL; - - memset(&tmp_mac, 0, sizeof(zebra_mac_t)); - memcpy(&tmp_mac.macaddr, macaddr, ETH_ALEN); - mac = hash_get(zvni->mac_table, &tmp_mac, zvni_mac_alloc); - assert(mac); - - mac->zvni = zvni; - mac->dad_mac_auto_recovery_timer = NULL; - - mac->neigh_list = list_new(); - mac->neigh_list->cmp = neigh_list_cmp; - - return mac; -} - -/* - * Delete MAC entry. - */ -static int zvni_mac_del(zebra_vni_t *zvni, zebra_mac_t *mac) -{ - zebra_mac_t *tmp_mac; - - /* Cancel auto recovery */ - THREAD_OFF(mac->dad_mac_auto_recovery_timer); - - list_delete(&mac->neigh_list); - - /* Free the VNI hash entry and allocated memory. */ - tmp_mac = hash_release(zvni->mac_table, mac); - XFREE(MTYPE_MAC, tmp_mac); - - return 0; -} - -/* - * Free MAC hash entry (callback) - */ -static void zvni_mac_del_hash_entry(struct hash_bucket *bucket, void *arg) -{ - struct mac_walk_ctx *wctx = arg; - zebra_mac_t *mac = bucket->data; - - if (((wctx->flags & DEL_LOCAL_MAC) && (mac->flags & ZEBRA_MAC_LOCAL)) - || ((wctx->flags & DEL_REMOTE_MAC) - && (mac->flags & ZEBRA_MAC_REMOTE)) - || ((wctx->flags & DEL_REMOTE_MAC_FROM_VTEP) - && (mac->flags & ZEBRA_MAC_REMOTE) - && IPV4_ADDR_SAME(&mac->fwd_info.r_vtep_ip, - &wctx->r_vtep_ip))) { - if (wctx->upd_client && (mac->flags & ZEBRA_MAC_LOCAL)) { - zvni_mac_send_del_to_client(wctx->zvni->vni, - &mac->macaddr); - } - - if (wctx->uninstall) - zvni_mac_uninstall(wctx->zvni, mac); - - zvni_mac_del(wctx->zvni, mac); - } - - return; -} - -/* - * Delete all MAC entries from specific VTEP for a particular VNI. - */ -static void zvni_mac_del_from_vtep(zebra_vni_t *zvni, int uninstall, - struct in_addr *r_vtep_ip) -{ - struct mac_walk_ctx wctx; - - if (!zvni->mac_table) - return; - - memset(&wctx, 0, sizeof(struct mac_walk_ctx)); - wctx.zvni = zvni; - wctx.uninstall = uninstall; - wctx.flags = DEL_REMOTE_MAC_FROM_VTEP; - wctx.r_vtep_ip = *r_vtep_ip; - - hash_iterate(zvni->mac_table, zvni_mac_del_hash_entry, &wctx); -} - -/* - * Delete all MAC entries for this VNI. - */ -static void zvni_mac_del_all(zebra_vni_t *zvni, int uninstall, int upd_client, - uint32_t flags) -{ - struct mac_walk_ctx wctx; - - if (!zvni->mac_table) - return; - - memset(&wctx, 0, sizeof(struct mac_walk_ctx)); - wctx.zvni = zvni; - wctx.uninstall = uninstall; - wctx.upd_client = upd_client; - wctx.flags = flags; - - hash_iterate(zvni->mac_table, zvni_mac_del_hash_entry, &wctx); -} - -/* - * Look up MAC hash entry. - */ -static zebra_mac_t *zvni_mac_lookup(zebra_vni_t *zvni, struct ethaddr *mac) -{ - zebra_mac_t tmp; - zebra_mac_t *pmac; - - memset(&tmp, 0, sizeof(tmp)); - memcpy(&tmp.macaddr, mac, ETH_ALEN); - pmac = hash_lookup(zvni->mac_table, &tmp); - - return pmac; -} - -/* - * Inform BGP about local MAC addition. - */ -static int zvni_mac_send_add_to_client(vni_t vni, struct ethaddr *macaddr, - uint8_t mac_flags, uint32_t seq) -{ - uint8_t flags = 0; - - if (CHECK_FLAG(mac_flags, ZEBRA_MAC_STICKY)) - SET_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY); - if (CHECK_FLAG(mac_flags, ZEBRA_MAC_DEF_GW)) - SET_FLAG(flags, ZEBRA_MACIP_TYPE_GW); - - return zvni_macip_send_msg_to_client(vni, macaddr, NULL, flags, - seq, ZEBRA_NEIGH_ACTIVE, ZEBRA_MACIP_ADD); -} - -/* - * Inform BGP about local MAC deletion. - */ -static int zvni_mac_send_del_to_client(vni_t vni, struct ethaddr *macaddr) -{ - return zvni_macip_send_msg_to_client(vni, macaddr, NULL, 0 /* flags */, - 0 /* seq */, ZEBRA_NEIGH_ACTIVE, ZEBRA_MACIP_DEL); -} - -/* - * Map port or (port, VLAN) to a VNI. This is invoked upon getting MAC - * notifications, to see if they are of interest. - */ -static zebra_vni_t *zvni_map_vlan(struct interface *ifp, - struct interface *br_if, vlanid_t vid) -{ - struct zebra_ns *zns; - struct route_node *rn; - struct interface *tmp_if = NULL; - struct zebra_if *zif; - struct zebra_l2info_bridge *br; - struct zebra_l2info_vxlan *vxl = NULL; - uint8_t bridge_vlan_aware; - zebra_vni_t *zvni; - int found = 0; - - /* Determine if bridge is VLAN-aware or not */ - zif = br_if->info; - assert(zif); - br = &zif->l2info.br; - bridge_vlan_aware = br->vlan_aware; - - /* See if this interface (or interface plus VLAN Id) maps to a VxLAN */ - /* TODO: Optimize with a hash. */ - zns = zebra_ns_lookup(NS_DEFAULT); - for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { - tmp_if = (struct interface *)rn->info; - if (!tmp_if) - continue; - zif = tmp_if->info; - if (!zif || zif->zif_type != ZEBRA_IF_VXLAN) - continue; - if (!if_is_operative(tmp_if)) - continue; - vxl = &zif->l2info.vxl; - - if (zif->brslave_info.br_if != br_if) - continue; - - if (!bridge_vlan_aware || vxl->access_vlan == vid) { - found = 1; - break; - } - } - - if (!found) - return NULL; - - zvni = zvni_lookup(vxl->vni); - return zvni; -} - -/* - * Map SVI and associated bridge to a VNI. This is invoked upon getting - * neighbor notifications, to see if they are of interest. - */ -static zebra_vni_t *zvni_from_svi(struct interface *ifp, - struct interface *br_if) -{ - struct zebra_ns *zns; - struct route_node *rn; - struct interface *tmp_if = NULL; - struct zebra_if *zif; - struct zebra_l2info_bridge *br; - struct zebra_l2info_vxlan *vxl = NULL; - uint8_t bridge_vlan_aware; - vlanid_t vid = 0; - zebra_vni_t *zvni; - int found = 0; - - if (!br_if) - return NULL; - - /* Make sure the linked interface is a bridge. */ - if (!IS_ZEBRA_IF_BRIDGE(br_if)) - return NULL; - - /* Determine if bridge is VLAN-aware or not */ - zif = br_if->info; - assert(zif); - br = &zif->l2info.br; - bridge_vlan_aware = br->vlan_aware; - if (bridge_vlan_aware) { - struct zebra_l2info_vlan *vl; - - if (!IS_ZEBRA_IF_VLAN(ifp)) - return NULL; - - zif = ifp->info; - assert(zif); - vl = &zif->l2info.vl; - vid = vl->vid; - } - - /* See if this interface (or interface plus VLAN Id) maps to a VxLAN */ - /* TODO: Optimize with a hash. */ - zns = zebra_ns_lookup(NS_DEFAULT); - for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { - tmp_if = (struct interface *)rn->info; - if (!tmp_if) - continue; - zif = tmp_if->info; - if (!zif || zif->zif_type != ZEBRA_IF_VXLAN) - continue; - if (!if_is_operative(tmp_if)) - continue; - vxl = &zif->l2info.vxl; - - if (zif->brslave_info.br_if != br_if) - continue; - - if (!bridge_vlan_aware || vxl->access_vlan == vid) { - found = 1; - break; - } - } - - if (!found) - return NULL; - - zvni = zvni_lookup(vxl->vni); - return zvni; -} - -/* Map to SVI on bridge corresponding to specified VLAN. This can be one - * of two cases: - * (a) In the case of a VLAN-aware bridge, the SVI is a L3 VLAN interface - * linked to the bridge - * (b) In the case of a VLAN-unaware bridge, the SVI is the bridge inteface - * itself - */ -static struct interface *zvni_map_to_svi(vlanid_t vid, struct interface *br_if) -{ - struct zebra_ns *zns; - struct route_node *rn; - struct interface *tmp_if = NULL; - struct zebra_if *zif; - struct zebra_l2info_bridge *br; - struct zebra_l2info_vlan *vl; - uint8_t bridge_vlan_aware; - int found = 0; - - /* Defensive check, caller expected to invoke only with valid bridge. */ - if (!br_if) - return NULL; - - /* Determine if bridge is VLAN-aware or not */ - zif = br_if->info; - assert(zif); - br = &zif->l2info.br; - bridge_vlan_aware = br->vlan_aware; - - /* Check oper status of the SVI. */ - if (!bridge_vlan_aware) - return if_is_operative(br_if) ? br_if : NULL; - - /* Identify corresponding VLAN interface. */ - /* TODO: Optimize with a hash. */ - zns = zebra_ns_lookup(NS_DEFAULT); - for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { - tmp_if = (struct interface *)rn->info; - /* Check oper status of the SVI. */ - if (!tmp_if || !if_is_operative(tmp_if)) - continue; - zif = tmp_if->info; - if (!zif || zif->zif_type != ZEBRA_IF_VLAN - || zif->link != br_if) - continue; - vl = (struct zebra_l2info_vlan *)&zif->l2info.vl; - - if (vl->vid == vid) { - found = 1; - break; - } - } - - return found ? tmp_if : NULL; -} - -/* - * Install remote MAC into the kernel. - */ -static int zvni_mac_install(zebra_vni_t *zvni, zebra_mac_t *mac) -{ - struct zebra_if *zif; - struct zebra_l2info_vxlan *vxl; - bool sticky; - - if (!(mac->flags & ZEBRA_MAC_REMOTE)) - return 0; - - zif = zvni->vxlan_if->info; - if (!zif) - return -1; - vxl = &zif->l2info.vxl; - - sticky = !!CHECK_FLAG(mac->flags, - (ZEBRA_MAC_STICKY | ZEBRA_MAC_REMOTE_DEF_GW)); - - return kernel_add_mac(zvni->vxlan_if, vxl->access_vlan, &mac->macaddr, - mac->fwd_info.r_vtep_ip, sticky); -} - -/* - * Uninstall remote MAC from the kernel. - */ -static int zvni_mac_uninstall(zebra_vni_t *zvni, zebra_mac_t *mac) -{ - struct zebra_if *zif; - struct zebra_l2info_vxlan *vxl; - struct in_addr vtep_ip; - struct interface *ifp; - - if (!(mac->flags & ZEBRA_MAC_REMOTE)) - return 0; - - if (!zvni->vxlan_if) { - zlog_debug("VNI %u hash %p couldn't be uninstalled - no intf", - zvni->vni, zvni); - return -1; - } - - zif = zvni->vxlan_if->info; - if (!zif) - return -1; - vxl = &zif->l2info.vxl; - - ifp = zvni->vxlan_if; - vtep_ip = mac->fwd_info.r_vtep_ip; - - return kernel_del_mac(ifp, vxl->access_vlan, &mac->macaddr, vtep_ip); -} - -/* - * Install MAC hash entry - called upon access VLAN change. - */ -static void zvni_install_mac_hash(struct hash_bucket *bucket, void *ctxt) -{ - zebra_mac_t *mac; - struct mac_walk_ctx *wctx = ctxt; - - mac = (zebra_mac_t *)bucket->data; - - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) - zvni_mac_install(wctx->zvni, mac); -} - -/* - * Count of remote neighbors referencing this MAC. - */ -static int remote_neigh_count(zebra_mac_t *zmac) -{ - zebra_neigh_t *n = NULL; - struct listnode *node = NULL; - int count = 0; - - for (ALL_LIST_ELEMENTS_RO(zmac->neigh_list, node, n)) { - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) - count++; - } - - return count; -} - -/* - * Decrement neighbor refcount of MAC; uninstall and free it if - * appropriate. - */ -static void zvni_deref_ip2mac(zebra_vni_t *zvni, zebra_mac_t *mac) -{ - if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO)) - return; - - /* If all remote neighbors referencing a remote MAC go away, - * we need to uninstall the MAC. - */ - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE) && - remote_neigh_count(mac) == 0) { - zvni_mac_uninstall(zvni, mac); - UNSET_FLAG(mac->flags, ZEBRA_MAC_REMOTE); - } - - /* If no neighbors, delete the MAC. */ - if (list_isempty(mac->neigh_list)) - zvni_mac_del(zvni, mac); -} - -/* - * Read and populate local MACs and neighbors corresponding to this VNI. - */ -static void zvni_read_mac_neigh(zebra_vni_t *zvni, struct interface *ifp) -{ - struct zebra_ns *zns; - struct zebra_if *zif; - struct interface *vlan_if; - struct zebra_l2info_vxlan *vxl; - struct interface *vrr_if; - - zif = ifp->info; - vxl = &zif->l2info.vxl; - zns = zebra_ns_lookup(NS_DEFAULT); - - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "Reading MAC FDB and Neighbors for intf %s(%u) VNI %u master %u", - ifp->name, ifp->ifindex, zvni->vni, - zif->brslave_info.bridge_ifindex); - - macfdb_read_for_bridge(zns, ifp, zif->brslave_info.br_if); - vlan_if = zvni_map_to_svi(vxl->access_vlan, zif->brslave_info.br_if); - if (vlan_if) { - - /* Add SVI MAC-IP */ - zvni_add_macip_for_intf(vlan_if, zvni); - - /* Add VRR MAC-IP - if any*/ - vrr_if = zebra_get_vrr_intf_for_svi(vlan_if); - if (vrr_if) - zvni_add_macip_for_intf(vrr_if, zvni); - - neigh_read_for_vlan(zns, vlan_if); - } -} - -/* - * Hash function for VNI. - */ -static unsigned int vni_hash_keymake(void *p) -{ - const zebra_vni_t *zvni = p; - - return (jhash_1word(zvni->vni, 0)); -} - -/* - * Compare 2 VNI hash entries. - */ -static bool vni_hash_cmp(const void *p1, const void *p2) -{ - const zebra_vni_t *zvni1 = p1; - const zebra_vni_t *zvni2 = p2; - - return (zvni1->vni == zvni2->vni); -} - -static int vni_list_cmp(void *p1, void *p2) -{ - const zebra_vni_t *zvni1 = p1; - const zebra_vni_t *zvni2 = p2; - - if (zvni1->vni == zvni2->vni) - return 0; - return (zvni1->vni < zvni2->vni) ? -1 : 1; -} - -/* - * Callback to allocate VNI hash entry. - */ -static void *zvni_alloc(void *p) -{ - const zebra_vni_t *tmp_vni = p; - zebra_vni_t *zvni; - - zvni = XCALLOC(MTYPE_ZVNI, sizeof(zebra_vni_t)); - zvni->vni = tmp_vni->vni; - return ((void *)zvni); -} - -/* - * Look up VNI hash entry. - */ -static zebra_vni_t *zvni_lookup(vni_t vni) -{ - struct zebra_vrf *zvrf; - zebra_vni_t tmp_vni; - zebra_vni_t *zvni = NULL; - - zvrf = zebra_vrf_get_evpn(); - assert(zvrf); - memset(&tmp_vni, 0, sizeof(zebra_vni_t)); - tmp_vni.vni = vni; - zvni = hash_lookup(zvrf->vni_table, &tmp_vni); - - return zvni; -} - -/* - * Add VNI hash entry. - */ -static zebra_vni_t *zvni_add(vni_t vni) -{ - struct zebra_vrf *zvrf; - zebra_vni_t tmp_zvni; - zebra_vni_t *zvni = NULL; - - zvrf = zebra_vrf_get_evpn(); - assert(zvrf); - memset(&tmp_zvni, 0, sizeof(zebra_vni_t)); - tmp_zvni.vni = vni; - zvni = hash_get(zvrf->vni_table, &tmp_zvni, zvni_alloc); - assert(zvni); + vty = (struct vty *)ctx[0]; + json = (json_object *)ctx[1]; - /* Create hash table for MAC */ - zvni->mac_table = - hash_create(mac_hash_keymake, mac_cmp, "Zebra VNI MAC Table"); + zl3vni = (zebra_l3vni_t *)bucket->data; - /* Create hash table for neighbors */ - zvni->neigh_table = hash_create(neigh_hash_keymake, neigh_cmp, - "Zebra VNI Neighbor Table"); + if (!json) { + vty_out(vty, "%-10u %-4s %-21s %-8lu %-8lu %-15s %-37s\n", + zl3vni->vni, "L3", zl3vni_vxlan_if_name(zl3vni), + hashcount(zl3vni->rmac_table), + hashcount(zl3vni->nh_table), "n/a", + zl3vni_vrf_name(zl3vni)); + } else { + char vni_str[VNI_STR_LEN]; - return zvni; + snprintf(vni_str, VNI_STR_LEN, "%u", zl3vni->vni); + json_evpn = json_object_new_object(); + json_object_int_add(json_evpn, "vni", zl3vni->vni); + json_object_string_add(json_evpn, "vxlanIf", + zl3vni_vxlan_if_name(zl3vni)); + json_object_int_add(json_evpn, "numMacs", + hashcount(zl3vni->rmac_table)); + json_object_int_add(json_evpn, "numArpNd", + hashcount(zl3vni->nh_table)); + json_object_string_add(json_evpn, "numRemoteVteps", "n/a"); + json_object_string_add(json_evpn, "type", "L3"); + json_object_string_add(json_evpn, "tenantVrf", + zl3vni_vrf_name(zl3vni)); + json_object_object_add(json, vni_str, json_evpn); + } } -/* - * Delete VNI hash entry. - */ -static int zvni_del(zebra_vni_t *zvni) +/* print a L3 VNI hash entry in detail*/ +static void zl3vni_print_hash_detail(struct hash_bucket *bucket, void *data) { - struct zebra_vrf *zvrf; - zebra_vni_t *tmp_zvni; - - zvrf = zebra_vrf_get_evpn(); - assert(zvrf); - - zvni->vxlan_if = NULL; - - /* Remove references to the BUM mcast grp */ - zebra_vxlan_sg_deref(zvni->local_vtep_ip, zvni->mcast_grp); + struct vty *vty = NULL; + zebra_l3vni_t *zl3vni = NULL; + json_object *json_array = NULL; + bool use_json = false; + struct zebra_evpn_show *zes = data; - /* Free the neighbor hash table. */ - hash_free(zvni->neigh_table); - zvni->neigh_table = NULL; + vty = zes->vty; + json_array = zes->json; + use_json = zes->use_json; - /* Free the MAC hash table. */ - hash_free(zvni->mac_table); - zvni->mac_table = NULL; + zl3vni = (zebra_l3vni_t *)bucket->data; - /* Free the VNI hash entry and allocated memory. */ - tmp_zvni = hash_release(zvrf->vni_table, zvni); - XFREE(MTYPE_ZVNI, tmp_zvni); + zebra_vxlan_print_vni(vty, zes->zvrf, zl3vni->vni, + use_json, json_array); - return 0; + if (!use_json) + vty_out(vty, "\n"); } -/* - * Inform BGP about local VNI addition. - */ -static int zvni_send_add_to_client(zebra_vni_t *zvni) +static int zvni_map_to_svi_ns(struct ns *ns, + void *_in_param, + void **_p_ifp) { - struct zserv *client; - struct stream *s; - - client = zserv_find_client(ZEBRA_ROUTE_BGP, 0); - /* BGP may not be running. */ - if (!client) - return 0; - - s = stream_new(ZEBRA_MAX_PACKET_SIZ); - - zclient_create_header(s, ZEBRA_VNI_ADD, zebra_vrf_get_evpn_id()); - stream_putl(s, zvni->vni); - stream_put_in_addr(s, &zvni->local_vtep_ip); - stream_put(s, &zvni->vrf_id, sizeof(vrf_id_t)); /* tenant vrf */ - stream_put_in_addr(s, &zvni->mcast_grp); + struct zebra_ns *zns = ns->info; + struct route_node *rn; + struct zebra_from_svi_param *in_param = + (struct zebra_from_svi_param *)_in_param; + struct zebra_l2info_vlan *vl; + struct interface *tmp_if = NULL; + struct interface **p_ifp = (struct interface **)_p_ifp; + struct zebra_if *zif; - /* Write packet size. */ - stream_putw_at(s, 0, stream_get_endp(s)); + if (!in_param) + return NS_WALK_STOP; - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Send VNI_ADD %u %s tenant vrf %s to %s", zvni->vni, - inet_ntoa(zvni->local_vtep_ip), - vrf_id_to_name(zvni->vrf_id), - zebra_route_string(client->proto)); + /* TODO: Optimize with a hash. */ + for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { + tmp_if = (struct interface *)rn->info; + /* Check oper status of the SVI. */ + if (!tmp_if || !if_is_operative(tmp_if)) + continue; + zif = tmp_if->info; + if (!zif || zif->zif_type != ZEBRA_IF_VLAN + || zif->link != in_param->br_if) + continue; + vl = (struct zebra_l2info_vlan *)&zif->l2info.vl; - client->vniadd_cnt++; - return zserv_send_message(client, s); + if (vl->vid == in_param->vid) { + if (p_ifp) + *p_ifp = tmp_if; + return NS_WALK_STOP; + } + } + return NS_WALK_CONTINUE; } -/* - * Inform BGP about local VNI deletion. +/* Map to SVI on bridge corresponding to specified VLAN. This can be one + * of two cases: + * (a) In the case of a VLAN-aware bridge, the SVI is a L3 VLAN interface + * linked to the bridge + * (b) In the case of a VLAN-unaware bridge, the SVI is the bridge interface + * itself */ -static int zvni_send_del_to_client(vni_t vni) +struct interface *zvni_map_to_svi(vlanid_t vid, struct interface *br_if) { - struct zserv *client; - struct stream *s; - - client = zserv_find_client(ZEBRA_ROUTE_BGP, 0); - /* BGP may not be running. */ - if (!client) - return 0; + struct interface *tmp_if = NULL; + struct zebra_if *zif; + struct zebra_l2info_bridge *br; + struct zebra_from_svi_param in_param; + struct interface **p_ifp; + /* Defensive check, caller expected to invoke only with valid bridge. */ + if (!br_if) + return NULL; - s = stream_new(ZEBRA_MAX_PACKET_SIZ); - stream_reset(s); + /* Determine if bridge is VLAN-aware or not */ + zif = br_if->info; + assert(zif); + br = &zif->l2info.br; + in_param.bridge_vlan_aware = br->vlan_aware; + /* Check oper status of the SVI. */ + if (!in_param.bridge_vlan_aware) + return if_is_operative(br_if) ? br_if : NULL; - zclient_create_header(s, ZEBRA_VNI_DEL, zebra_vrf_get_evpn_id()); - stream_putl(s, vni); + in_param.vid = vid; + in_param.br_if = br_if; + in_param.zif = NULL; + p_ifp = &tmp_if; + /* Identify corresponding VLAN interface. */ + ns_walk_func(zvni_map_to_svi_ns, (void *)&in_param, + (void **)p_ifp); + return tmp_if; +} - /* Write packet size. */ - stream_putw_at(s, 0, stream_get_endp(s)); +static int zebra_evpn_vxlan_del(zebra_evpn_t *zevpn) +{ + zevpn_vxlan_if_set(zevpn, zevpn->vxlan_if, false /* set */); - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Send VNI_DEL %u to %s", vni, - zebra_route_string(client->proto)); + /* Remove references to the BUM mcast grp */ + zebra_vxlan_sg_deref(zevpn->local_vtep_ip, zevpn->mcast_grp); - client->vnidel_cnt++; - return zserv_send_message(client, s); + return zebra_evpn_del(zevpn); } -/* - * Build the VNI hash table by going over the VxLAN interfaces. This - * is called when EVPN (advertise-all-vni) is enabled. - */ -static void zvni_build_hash_table(void) +static int zevpn_build_hash_table_zns(struct ns *ns, + void *param_in __attribute__((unused)), + void **param_out __attribute__((unused))) { - struct zebra_ns *zns; + struct zebra_ns *zns = ns->info; struct route_node *rn; struct interface *ifp; + struct zebra_vrf *zvrf; - /* Walk VxLAN interfaces and create VNI hash. */ - zns = zebra_ns_lookup(NS_DEFAULT); + zvrf = zebra_vrf_get_evpn(); + + if (!zvrf) + return NS_WALK_STOP; + + /* Walk VxLAN interfaces and create EVPN hash. */ for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { vni_t vni; - zebra_vni_t *zvni = NULL; + zebra_evpn_t *zevpn = NULL; zebra_l3vni_t *zl3vni = NULL; struct zebra_if *zif; struct zebra_l2info_vxlan *vxl; @@ -4024,7 +897,15 @@ static void zvni_build_hash_table(void) vxl = &zif->l2info.vxl; vni = vxl->vni; - + /* link of VXLAN interface should be in zebra_evpn_vrf */ + if (zvrf->zns->ns_id != vxl->link_nsid) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Intf %s(%u) VNI %u, link not in same " + "namespace than BGP EVPN core instance ", + ifp->name, ifp->ifindex, vni); + continue; + } /* L3-VNI and L2-VNI are handled seperately */ zl3vni = zl3vni_lookup(vni); if (zl3vni) { @@ -4045,6 +926,16 @@ static void zvni_build_hash_table(void) */ zl3vni->svi_if = zl3vni_map_to_svi_if(zl3vni); + /* Associate l3vni to mac-vlan and extract VRR MAC */ + zl3vni->mac_vlan_if = zl3vni_map_to_mac_vlan_if(zl3vni); + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("create l3vni %u svi_if %s mac_vlan_if %s", + vni, zl3vni->svi_if ? zl3vni->svi_if->name + : "NIL", + zl3vni->mac_vlan_if ? + zl3vni->mac_vlan_if->name : "NIL"); + if (is_l3vni_oper_up(zl3vni)) zebra_vxlan_process_l3vni_oper_up(zl3vni); @@ -4057,212 +948,107 @@ static void zvni_build_hash_table(void) ifp->name, ifp->ifindex, vni, inet_ntoa(vxl->vtep_ip)); - /* VNI hash entry is not expected to exist. */ - zvni = zvni_lookup(vni); - if (zvni) { - zlog_debug( - "VNI hash already present for IF %s(%u) L2-VNI %u", - ifp->name, ifp->ifindex, vni); - continue; - } - - zvni = zvni_add(vni); - if (!zvni) { + /* EVPN hash entry is expected to exist, if the BGP process is killed */ + zevpn = zebra_evpn_lookup(vni); + if (zevpn) { zlog_debug( - "Failed to add VNI hash, IF %s(%u) L2-VNI %u", + "EVPN hash already present for IF %s(%u) L2-VNI %u", ifp->name, ifp->ifindex, vni); - return; - } - - if (zvni->local_vtep_ip.s_addr != vxl->vtep_ip.s_addr || - zvni->mcast_grp.s_addr != vxl->mcast_grp.s_addr) { - zebra_vxlan_sg_deref(zvni->local_vtep_ip, - zvni->mcast_grp); - zebra_vxlan_sg_ref(vxl->vtep_ip, - vxl->mcast_grp); - zvni->local_vtep_ip = vxl->vtep_ip; - zvni->mcast_grp = vxl->mcast_grp; - } - zvni->vxlan_if = ifp; - vlan_if = zvni_map_to_svi(vxl->access_vlan, - zif->brslave_info.br_if); - if (vlan_if) { - zvni->vrf_id = vlan_if->vrf_id; - zl3vni = zl3vni_from_vrf(vlan_if->vrf_id); - if (zl3vni) - listnode_add_sort(zl3vni->l2vnis, zvni); - } - - - /* Inform BGP if intf is up and mapped to bridge. */ - if (if_is_operative(ifp) && zif->brslave_info.br_if) - zvni_send_add_to_client(zvni); - } - } -} - -/* - * See if remote VTEP matches with prefix. - */ -static int zvni_vtep_match(struct in_addr *vtep_ip, zebra_vtep_t *zvtep) -{ - return (IPV4_ADDR_SAME(vtep_ip, &zvtep->vtep_ip)); -} - -/* - * Locate remote VTEP in VNI hash table. - */ -static zebra_vtep_t *zvni_vtep_find(zebra_vni_t *zvni, struct in_addr *vtep_ip) -{ - zebra_vtep_t *zvtep; - - if (!zvni) - return NULL; - - for (zvtep = zvni->vteps; zvtep; zvtep = zvtep->next) { - if (zvni_vtep_match(vtep_ip, zvtep)) - break; - } - - return zvtep; -} - -/* - * Add remote VTEP to VNI hash table. - */ -static zebra_vtep_t *zvni_vtep_add(zebra_vni_t *zvni, struct in_addr *vtep_ip, - int flood_control) - -{ - zebra_vtep_t *zvtep; - - zvtep = XCALLOC(MTYPE_ZVNI_VTEP, sizeof(zebra_vtep_t)); - - zvtep->vtep_ip = *vtep_ip; - zvtep->flood_control = flood_control; - if (zvni->vteps) - zvni->vteps->prev = zvtep; - zvtep->next = zvni->vteps; - zvni->vteps = zvtep; - - return zvtep; -} - -/* - * Remove remote VTEP from VNI hash table. - */ -static int zvni_vtep_del(zebra_vni_t *zvni, zebra_vtep_t *zvtep) -{ - if (zvtep->next) - zvtep->next->prev = zvtep->prev; - if (zvtep->prev) - zvtep->prev->next = zvtep->next; - else - zvni->vteps = zvtep->next; - - zvtep->prev = zvtep->next = NULL; - XFREE(MTYPE_ZVNI_VTEP, zvtep); + /* + * Inform BGP if intf is up and mapped to + * bridge. + */ + if (if_is_operative(ifp) && + zif->brslave_info.br_if) + zebra_evpn_send_add_to_client(zevpn); - return 0; -} + /* Send Local MAC-entries to client */ + zebra_evpn_send_mac_list_to_client(zevpn); -/* - * Delete all remote VTEPs for this VNI (upon VNI delete). Also - * uninstall from kernel if asked to. - */ -static int zvni_vtep_del_all(zebra_vni_t *zvni, int uninstall) -{ - zebra_vtep_t *zvtep, *zvtep_next; + /* Send Loval Neighbor entries to client */ + zebra_evpn_send_neigh_to_client(zevpn); + } else { + zevpn = zebra_evpn_add(vni); + if (!zevpn) { + zlog_debug( + "Failed to add EVPN hash, IF %s(%u) L2-VNI %u", + ifp->name, ifp->ifindex, vni); + return NS_WALK_CONTINUE; + } - if (!zvni) - return -1; + if (zevpn->local_vtep_ip.s_addr != + vxl->vtep_ip.s_addr || + zevpn->mcast_grp.s_addr != + vxl->mcast_grp.s_addr) { + zebra_vxlan_sg_deref( + zevpn->local_vtep_ip, + zevpn->mcast_grp); + zebra_vxlan_sg_ref(vxl->vtep_ip, + vxl->mcast_grp); + zevpn->local_vtep_ip = vxl->vtep_ip; + zevpn->mcast_grp = vxl->mcast_grp; + /* on local vtep-ip check if ES + * orig-ip needs to be updated + */ + zebra_evpn_es_set_base_evpn(zevpn); + } + zevpn_vxlan_if_set(zevpn, ifp, true /* set */); + vlan_if = zvni_map_to_svi( + vxl->access_vlan, + zif->brslave_info.br_if); + if (vlan_if) { + zevpn->vrf_id = vlan_if->vrf_id; + zl3vni = zl3vni_from_vrf( + vlan_if->vrf_id); + if (zl3vni) + listnode_add_sort( + zl3vni->l2vnis, zevpn); + } - for (zvtep = zvni->vteps; zvtep; zvtep = zvtep_next) { - zvtep_next = zvtep->next; - if (uninstall) - zvni_vtep_uninstall(zvni, &zvtep->vtep_ip); - zvni_vtep_del(zvni, zvtep); + /* + * Inform BGP if intf is up and mapped to + * bridge. + */ + if (if_is_operative(ifp) && + zif->brslave_info.br_if) + zebra_evpn_send_add_to_client(zevpn); + } + } } - - return 0; -} - -/* - * Install remote VTEP into the kernel if the remote VTEP has asked - * for head-end-replication. - */ -static int zvni_vtep_install(zebra_vni_t *zvni, zebra_vtep_t *zvtep) -{ - if (is_vxlan_flooding_head_end() && - (zvtep->flood_control == VXLAN_FLOOD_HEAD_END_REPL)) - return kernel_add_vtep(zvni->vni, zvni->vxlan_if, - &zvtep->vtep_ip); - return 0; + return NS_WALK_CONTINUE; } /* - * Uninstall remote VTEP from the kernel. + * Build the VNI hash table by going over the VxLAN interfaces. This + * is called when EVPN (advertise-all-vni) is enabled. */ -static int zvni_vtep_uninstall(zebra_vni_t *zvni, struct in_addr *vtep_ip) -{ - if (!zvni->vxlan_if) { - zlog_debug("VNI %u hash %p couldn't be uninstalled - no intf", - zvni->vni, zvni); - return -1; - } - - return kernel_del_vtep(zvni->vni, zvni->vxlan_if, vtep_ip); -} -/* - * Install or uninstall flood entries in the kernel corresponding to - * remote VTEPs. This is invoked upon change to BUM handling. - */ -static void zvni_handle_flooding_remote_vteps(struct hash_bucket *bucket, - void *zvrf) +static void zevpn_build_hash_table(void) { - zebra_vni_t *zvni; - zebra_vtep_t *zvtep; - - zvni = (zebra_vni_t *)bucket->data; - if (!zvni) - return; - - for (zvtep = zvni->vteps; zvtep; zvtep = zvtep->next) { - if (is_vxlan_flooding_head_end()) - zvni_vtep_install(zvni, zvtep); - else - zvni_vtep_uninstall(zvni, &zvtep->vtep_ip); - } + ns_walk_func(zevpn_build_hash_table_zns, + (void *)NULL, + (void **)NULL); } /* - * Cleanup VNI/VTEP and update kernel + * Cleanup EVPN/VTEP and update kernel */ -static void zvni_cleanup_all(struct hash_bucket *bucket, void *arg) +static void zebra_evpn_vxlan_cleanup_all(struct hash_bucket *bucket, void *arg) { - zebra_vni_t *zvni = NULL; + zebra_evpn_t *zevpn = NULL; zebra_l3vni_t *zl3vni = NULL; struct zebra_vrf *zvrf = (struct zebra_vrf *)arg; - zvni = (zebra_vni_t *)bucket->data; + zevpn = (zebra_evpn_t *)bucket->data; /* remove from l3-vni list */ if (zvrf->l3vni) zl3vni = zl3vni_lookup(zvrf->l3vni); if (zl3vni) - listnode_delete(zl3vni->l2vnis, zvni); + listnode_delete(zl3vni->l2vnis, zevpn); - /* Free up all neighbors and MACs, if any. */ - zvni_neigh_del_all(zvni, 1, 0, DEL_ALL_NEIGH); - zvni_mac_del_all(zvni, 1, 0, DEL_ALL_MAC); - - /* Free up all remote VTEPs, if any. */ - zvni_vtep_del_all(zvni, 1); - - /* Delete the hash entry. */ - zvni_del(zvni); + zebra_evpn_cleanup_all(bucket, arg); } /* cleanup L3VNI */ @@ -4276,7 +1062,7 @@ static void zl3vni_cleanup_all(struct hash_bucket *bucket, void *args) } static void rb_find_or_add_host(struct host_rb_tree_entry *hrbe, - struct prefix *host) + const struct prefix *host) { struct host_rb_entry lookup; struct host_rb_entry *hle; @@ -4315,7 +1101,7 @@ static void rb_delete_host(struct host_rb_tree_entry *hrbe, struct prefix *host) * Look up MAC hash entry. */ static zebra_mac_t *zl3vni_rmac_lookup(zebra_l3vni_t *zl3vni, - struct ethaddr *rmac) + const struct ethaddr *rmac) { zebra_mac_t tmp; zebra_mac_t *pmac; @@ -4335,7 +1121,7 @@ static void *zl3vni_rmac_alloc(void *p) const zebra_mac_t *tmp_rmac = p; zebra_mac_t *zrmac; - zrmac = XCALLOC(MTYPE_MAC, sizeof(zebra_mac_t)); + zrmac = XCALLOC(MTYPE_L3VNI_MAC, sizeof(zebra_mac_t)); *zrmac = *tmp_rmac; return ((void *)zrmac); @@ -4344,7 +1130,8 @@ static void *zl3vni_rmac_alloc(void *p) /* * Add RMAC entry to l3-vni */ -static zebra_mac_t *zl3vni_rmac_add(zebra_l3vni_t *zl3vni, struct ethaddr *rmac) +static zebra_mac_t *zl3vni_rmac_add(zebra_l3vni_t *zl3vni, + const struct ethaddr *rmac) { zebra_mac_t tmp_rmac; zebra_mac_t *zrmac = NULL; @@ -4378,18 +1165,21 @@ static int zl3vni_rmac_del(zebra_l3vni_t *zl3vni, zebra_mac_t *zrmac) } tmp_rmac = hash_release(zl3vni->rmac_table, zrmac); - XFREE(MTYPE_MAC, tmp_rmac); + XFREE(MTYPE_L3VNI_MAC, tmp_rmac); return 0; } /* - * Install remote RMAC into the kernel. + * Install remote RMAC into the forwarding plane. */ static int zl3vni_rmac_install(zebra_l3vni_t *zl3vni, zebra_mac_t *zrmac) { - struct zebra_if *zif = NULL; - struct zebra_l2info_vxlan *vxl = NULL; + const struct zebra_if *zif = NULL, *br_zif = NULL; + const struct zebra_l2info_vxlan *vxl = NULL; + const struct interface *br_ifp; + enum zebra_dplane_result res; + vlanid_t vid; if (!(CHECK_FLAG(zrmac->flags, ZEBRA_MAC_REMOTE)) || !(CHECK_FLAG(zrmac->flags, ZEBRA_MAC_REMOTE_RMAC))) @@ -4399,30 +1189,51 @@ static int zl3vni_rmac_install(zebra_l3vni_t *zl3vni, zebra_mac_t *zrmac) if (!zif) return -1; + br_ifp = zif->brslave_info.br_if; + if (br_ifp == NULL) + return -1; + vxl = &zif->l2info.vxl; - return kernel_add_mac(zl3vni->vxlan_if, vxl->access_vlan, - &zrmac->macaddr, zrmac->fwd_info.r_vtep_ip, 0); + br_zif = (const struct zebra_if *)br_ifp->info; + + if (IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(br_zif)) + vid = vxl->access_vlan; + else + vid = 0; + + res = dplane_rem_mac_add(zl3vni->vxlan_if, br_ifp, vid, + &zrmac->macaddr, zrmac->fwd_info.r_vtep_ip, 0, 0, + false /*was_static*/); + if (res != ZEBRA_DPLANE_REQUEST_FAILURE) + return 0; + else + return -1; } /* - * Uninstall remote RMAC from the kernel. + * Uninstall remote RMAC from the forwarding plane. */ static int zl3vni_rmac_uninstall(zebra_l3vni_t *zl3vni, zebra_mac_t *zrmac) { char buf[ETHER_ADDR_STRLEN]; - struct zebra_if *zif = NULL; - struct zebra_l2info_vxlan *vxl = NULL; + const struct zebra_if *zif = NULL, *br_zif; + const struct zebra_l2info_vxlan *vxl = NULL; + const struct interface *br_ifp; + vlanid_t vid; + enum zebra_dplane_result res; if (!(CHECK_FLAG(zrmac->flags, ZEBRA_MAC_REMOTE)) || !(CHECK_FLAG(zrmac->flags, ZEBRA_MAC_REMOTE_RMAC))) return 0; if (!zl3vni->vxlan_if) { - zlog_debug( - "RMAC %s on L3-VNI %u hash %p couldn't be uninstalled - no vxlan_if", - prefix_mac2str(&zrmac->macaddr, buf, sizeof(buf)), - zl3vni->vni, zl3vni); + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "RMAC %s on L3-VNI %u hash %p couldn't be uninstalled - no vxlan_if", + prefix_mac2str(&zrmac->macaddr, + buf, sizeof(buf)), + zl3vni->vni, zl3vni); return -1; } @@ -4430,36 +1241,73 @@ static int zl3vni_rmac_uninstall(zebra_l3vni_t *zl3vni, zebra_mac_t *zrmac) if (!zif) return -1; + br_ifp = zif->brslave_info.br_if; + if (br_ifp == NULL) + return -1; + vxl = &zif->l2info.vxl; - return kernel_del_mac(zl3vni->vxlan_if, vxl->access_vlan, - &zrmac->macaddr, zrmac->fwd_info.r_vtep_ip); + br_zif = (const struct zebra_if *)br_ifp->info; + if (IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(br_zif)) + vid = vxl->access_vlan; + else + vid = 0; + + res = dplane_rem_mac_del(zl3vni->vxlan_if, br_ifp, vid, + &zrmac->macaddr, zrmac->fwd_info.r_vtep_ip); + if (res != ZEBRA_DPLANE_REQUEST_FAILURE) + return 0; + else + return -1; } /* handle rmac add */ -static int zl3vni_remote_rmac_add(zebra_l3vni_t *zl3vni, struct ethaddr *rmac, - struct ipaddr *vtep_ip, - struct prefix *host_prefix) +static int zl3vni_remote_rmac_add(zebra_l3vni_t *zl3vni, + const struct ethaddr *rmac, + const struct ipaddr *vtep_ip, + const struct prefix *host_prefix) { char buf[ETHER_ADDR_STRLEN]; char buf1[INET6_ADDRSTRLEN]; + char buf2[PREFIX_STRLEN]; zebra_mac_t *zrmac = NULL; zrmac = zl3vni_rmac_lookup(zl3vni, rmac); if (!zrmac) { + /* Create the RMAC entry, or update its vtep, if necessary. */ zrmac = zl3vni_rmac_add(zl3vni, rmac); if (!zrmac) { zlog_debug( - "Failed to add RMAC %s L3VNI %u Remote VTEP %s", + "Failed to add RMAC %s L3VNI %u Remote VTEP %s, prefix %s", prefix_mac2str(rmac, buf, sizeof(buf)), zl3vni->vni, - ipaddr2str(vtep_ip, buf1, sizeof(buf1))); + ipaddr2str(vtep_ip, buf1, sizeof(buf1)), + prefix2str(host_prefix, buf2, sizeof(buf2))); return -1; } memset(&zrmac->fwd_info, 0, sizeof(zrmac->fwd_info)); zrmac->fwd_info.r_vtep_ip = vtep_ip->ipaddr_v4; + /* Send RMAC for FPM processing */ + hook_call(zebra_rmac_update, zrmac, zl3vni, false, + "new RMAC added"); + + /* install rmac in kernel */ + zl3vni_rmac_install(zl3vni, zrmac); + } else if (!IPV4_ADDR_SAME(&zrmac->fwd_info.r_vtep_ip, + &vtep_ip->ipaddr_v4)) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "L3VNI %u Remote VTEP change(%s -> %s) for RMAC %s, prefix %s", + zl3vni->vni, + inet_ntoa(zrmac->fwd_info.r_vtep_ip), + ipaddr2str(vtep_ip, buf1, sizeof(buf1)), + prefix_mac2str(rmac, buf, sizeof(buf)), + prefix2str(host_prefix, buf2, sizeof(buf2))); + + zrmac->fwd_info.r_vtep_ip = vtep_ip->ipaddr_v4; + /* install rmac in kernel */ zl3vni_rmac_install(zl3vni, zrmac); } @@ -4480,6 +1328,10 @@ static void zl3vni_remote_rmac_del(zebra_l3vni_t *zl3vni, zebra_mac_t *zrmac, /* uninstall from kernel */ zl3vni_rmac_uninstall(zl3vni, zrmac); + /* Send RMAC for FPM processing */ + hook_call(zebra_rmac_update, zrmac, zl3vni, true, + "RMAC deleted"); + /* del the rmac entry */ zl3vni_rmac_del(zl3vni, zrmac); } @@ -4488,7 +1340,8 @@ static void zl3vni_remote_rmac_del(zebra_l3vni_t *zl3vni, zebra_mac_t *zrmac, /* * Look up nh hash entry on a l3-vni. */ -static zebra_neigh_t *zl3vni_nh_lookup(zebra_l3vni_t *zl3vni, struct ipaddr *ip) +static zebra_neigh_t *zl3vni_nh_lookup(zebra_l3vni_t *zl3vni, + const struct ipaddr *ip) { zebra_neigh_t tmp; zebra_neigh_t *n; @@ -4509,7 +1362,7 @@ static void *zl3vni_nh_alloc(void *p) const zebra_neigh_t *tmp_n = p; zebra_neigh_t *n; - n = XCALLOC(MTYPE_NEIGH, sizeof(zebra_neigh_t)); + n = XCALLOC(MTYPE_L3NEIGH, sizeof(zebra_neigh_t)); *n = *tmp_n; return ((void *)n); @@ -4518,8 +1371,9 @@ static void *zl3vni_nh_alloc(void *p) /* * Add neighbor entry. */ -static zebra_neigh_t *zl3vni_nh_add(zebra_l3vni_t *zl3vni, struct ipaddr *ip, - struct ethaddr *mac) +static zebra_neigh_t *zl3vni_nh_add(zebra_l3vni_t *zl3vni, + const struct ipaddr *ip, + const struct ethaddr *mac) { zebra_neigh_t tmp_n; zebra_neigh_t *n = NULL; @@ -4554,7 +1408,7 @@ static int zl3vni_nh_del(zebra_l3vni_t *zl3vni, zebra_neigh_t *n) } tmp_n = hash_release(zl3vni->nh_table, n); - XFREE(MTYPE_NEIGH, tmp_n); + XFREE(MTYPE_L3NEIGH, tmp_n); return 0; } @@ -4564,9 +1418,7 @@ static int zl3vni_nh_del(zebra_l3vni_t *zl3vni, zebra_neigh_t *n) */ static int zl3vni_nh_install(zebra_l3vni_t *zl3vni, zebra_neigh_t *n) { -#ifdef GNU_LINUX uint8_t flags; -#endif int ret = 0; if (!is_l3vni_oper_up(zl3vni)) @@ -4575,12 +1427,14 @@ static int zl3vni_nh_install(zebra_l3vni_t *zl3vni, zebra_neigh_t *n) if (!(n->flags & ZEBRA_NEIGH_REMOTE) || !(n->flags & ZEBRA_NEIGH_REMOTE_NH)) return 0; -#ifdef GNU_LINUX - flags = NTF_EXT_LEARNED; + + flags = DPLANE_NTF_EXT_LEARNED; if (n->flags & ZEBRA_NEIGH_ROUTER_FLAG) - flags |= NTF_ROUTER; - ret = kernel_add_neigh(zl3vni->svi_if, &n->ip, &n->emac, flags); -#endif + flags |= DPLANE_NTF_ROUTER; + + dplane_rem_neigh_add(zl3vni->svi_if, &n->ip, &n->emac, flags, + false /*was_static*/); + return ret; } @@ -4596,33 +1450,51 @@ static int zl3vni_nh_uninstall(zebra_l3vni_t *zl3vni, zebra_neigh_t *n) if (!zl3vni->svi_if || !if_is_operative(zl3vni->svi_if)) return 0; - return kernel_del_neigh(zl3vni->svi_if, &n->ip); + dplane_rem_neigh_delete(zl3vni->svi_if, &n->ip); + + return 0; } /* add remote vtep as a neigh entry */ -static int zl3vni_remote_nh_add(zebra_l3vni_t *zl3vni, struct ipaddr *vtep_ip, - struct ethaddr *rmac, - struct prefix *host_prefix) +static int zl3vni_remote_nh_add(zebra_l3vni_t *zl3vni, + const struct ipaddr *vtep_ip, + const struct ethaddr *rmac, + const struct prefix *host_prefix) { char buf[ETHER_ADDR_STRLEN]; - char buf1[INET6_ADDRSTRLEN]; + char buf1[ETHER_ADDR_STRLEN]; + char buf2[INET6_ADDRSTRLEN]; + char buf3[PREFIX_STRLEN]; zebra_neigh_t *nh = NULL; + /* Create the next hop entry, or update its mac, if necessary. */ nh = zl3vni_nh_lookup(zl3vni, vtep_ip); if (!nh) { nh = zl3vni_nh_add(zl3vni, vtep_ip, rmac); if (!nh) { - zlog_debug( - "Failed to add NH as Neigh (IP %s MAC %s L3-VNI %u)", - ipaddr2str(vtep_ip, buf1, sizeof(buf1)), + "Failed to add NH %s as Neigh (RMAC %s L3-VNI %u prefix %s)", + ipaddr2str(vtep_ip, buf1, sizeof(buf2)), prefix_mac2str(rmac, buf, sizeof(buf)), - zl3vni->vni); + zl3vni->vni, + prefix2str(host_prefix, buf2, sizeof(buf2))); return -1; } /* install the nh neigh in kernel */ zl3vni_nh_install(zl3vni, nh); + } else if (memcmp(&nh->emac, rmac, ETH_ALEN) != 0) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("L3VNI %u RMAC change(%s --> %s) for nexthop %s, prefix %s", + zl3vni->vni, + prefix_mac2str(&nh->emac, buf, sizeof(buf)), + prefix_mac2str(rmac, buf1, sizeof(buf1)), + ipaddr2str(vtep_ip, buf2, sizeof(buf2)), + prefix2str(host_prefix, buf3, sizeof(buf3))); + + memcpy(&nh->emac, rmac, ETH_ALEN); + /* install (update) the nh neigh in kernel */ + zl3vni_nh_install(zl3vni, nh); } rb_find_or_add_host(&nh->host_rb, host_prefix); @@ -4688,7 +1560,7 @@ static int zl3vni_local_nh_del(zebra_l3vni_t *zl3vni, struct ipaddr *ip) /* * Hash function for L3 VNI. */ -static unsigned int l3vni_hash_keymake(void *p) +static unsigned int l3vni_hash_keymake(const void *p) { const zebra_l3vni_t *zl3vni = p; @@ -4722,7 +1594,7 @@ static void *zl3vni_alloc(void *p) /* * Look up L3 VNI hash entry. */ -static zebra_l3vni_t *zl3vni_lookup(vni_t vni) +zebra_l3vni_t *zl3vni_lookup(vni_t vni) { zebra_l3vni_t tmp_l3vni; zebra_l3vni_t *zl3vni = NULL; @@ -4752,15 +1624,13 @@ static zebra_l3vni_t *zl3vni_add(vni_t vni, vrf_id_t vrf_id) zl3vni->svi_if = NULL; zl3vni->vxlan_if = NULL; zl3vni->l2vnis = list_new(); - zl3vni->l2vnis->cmp = vni_list_cmp; + zl3vni->l2vnis->cmp = zebra_evpn_list_cmp; /* Create hash table for remote RMAC */ - zl3vni->rmac_table = hash_create(mac_hash_keymake, mac_cmp, - "Zebra L3-VNI RMAC-Table"); + zl3vni->rmac_table = zebra_mac_db_create("Zebra L3-VNI RMAC-Table"); /* Create hash table for neighbors */ - zl3vni->nh_table = hash_create(neigh_hash_keymake, neigh_cmp, - "Zebra L3-VNI next-hop table"); + zl3vni->nh_table = zebra_neigh_db_create("Zebra L3-VNI next-hop table"); return zl3vni; } @@ -4791,14 +1661,22 @@ static int zl3vni_del(zebra_l3vni_t *zl3vni) return 0; } -static struct interface *zl3vni_map_to_vxlan_if(zebra_l3vni_t *zl3vni) +static int zl3vni_map_to_vxlan_if_ns(struct ns *ns, + void *_zl3vni, + void **_pifp) { - struct zebra_ns *zns = NULL; + struct zebra_ns *zns = ns->info; + zebra_l3vni_t *zl3vni = (zebra_l3vni_t *)_zl3vni; struct route_node *rn = NULL; struct interface *ifp = NULL; + struct zebra_vrf *zvrf; + + zvrf = zebra_vrf_get_evpn(); + + if (!zvrf) + return NS_WALK_STOP; /* loop through all vxlan-interface */ - zns = zebra_ns_lookup(NS_DEFAULT); for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { struct zebra_if *zif = NULL; @@ -4813,16 +1691,42 @@ static struct interface *zl3vni_map_to_vxlan_if(zebra_l3vni_t *zl3vni) continue; vxl = &zif->l2info.vxl; - if (vxl->vni == zl3vni->vni) { - zl3vni->local_vtep_ip = vxl->vtep_ip; - return ifp; + if (vxl->vni != zl3vni->vni) + continue; + + /* link of VXLAN interface should be in zebra_evpn_vrf */ + if (zvrf->zns->ns_id != vxl->link_nsid) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Intf %s(%u) VNI %u, link not in same " + "namespace than BGP EVPN core instance ", + ifp->name, ifp->ifindex, vxl->vni); + continue; } + + + zl3vni->local_vtep_ip = vxl->vtep_ip; + if (_pifp) + *_pifp = (void *)ifp; + return NS_WALK_STOP; } - return NULL; + return NS_WALK_CONTINUE; +} + +struct interface *zl3vni_map_to_vxlan_if(zebra_l3vni_t *zl3vni) +{ + struct interface **p_ifp; + struct interface *ifp = NULL; + + p_ifp = &ifp; + + ns_walk_func(zl3vni_map_to_vxlan_if_ns, + (void *)zl3vni, (void **)p_ifp); + return ifp; } -static struct interface *zl3vni_map_to_svi_if(zebra_l3vni_t *zl3vni) +struct interface *zl3vni_map_to_svi_if(zebra_l3vni_t *zl3vni) { struct zebra_if *zif = NULL; /* zebra_if for vxlan_if */ struct zebra_l2info_vxlan *vxl = NULL; /* l2 info for vxlan_if */ @@ -4842,7 +1746,26 @@ static struct interface *zl3vni_map_to_svi_if(zebra_l3vni_t *zl3vni) return zvni_map_to_svi(vxl->access_vlan, zif->brslave_info.br_if); } -static zebra_l3vni_t *zl3vni_from_vrf(vrf_id_t vrf_id) +struct interface *zl3vni_map_to_mac_vlan_if(zebra_l3vni_t *zl3vni) +{ + struct zebra_if *zif = NULL; /* zebra_if for vxlan_if */ + + if (!zl3vni) + return NULL; + + if (!zl3vni->vxlan_if) + return NULL; + + zif = zl3vni->vxlan_if->info; + if (!zif) + return NULL; + + return zebra_evpn_map_to_macvlan(zif->brslave_info.br_if, + zl3vni->svi_if); +} + + +zebra_l3vni_t *zl3vni_from_vrf(vrf_id_t vrf_id) { struct zebra_vrf *zvrf = NULL; @@ -4925,6 +1848,19 @@ static zebra_l3vni_t *zl3vni_from_svi(struct interface *ifp, return zl3vni; } +static inline void zl3vni_get_vrr_rmac(zebra_l3vni_t *zl3vni, + struct ethaddr *rmac) +{ + if (!zl3vni) + return; + + if (!is_l3vni_oper_up(zl3vni)) + return; + + if (zl3vni->mac_vlan_if && if_is_operative(zl3vni->mac_vlan_if)) + memcpy(rmac->octet, zl3vni->mac_vlan_if->hw_addr, ETH_ALEN); +} + /* * Inform BGP about l3-vni. */ @@ -4932,35 +1868,54 @@ static int zl3vni_send_add_to_client(zebra_l3vni_t *zl3vni) { struct stream *s = NULL; struct zserv *client = NULL; - struct ethaddr rmac; + struct ethaddr svi_rmac, vrr_rmac = {.octet = {0} }; + struct zebra_vrf *zvrf; char buf[ETHER_ADDR_STRLEN]; + char buf1[ETHER_ADDR_STRLEN]; + bool is_anycast_mac = true; client = zserv_find_client(ZEBRA_ROUTE_BGP, 0); /* BGP may not be running. */ if (!client) return 0; - /* get the rmac */ - memset(&rmac, 0, sizeof(struct ethaddr)); - zl3vni_get_rmac(zl3vni, &rmac); + zvrf = zebra_vrf_lookup_by_id(zl3vni->vrf_id); + assert(zvrf); + + /* get the svi and vrr rmac values */ + memset(&svi_rmac, 0, sizeof(struct ethaddr)); + zl3vni_get_svi_rmac(zl3vni, &svi_rmac); + zl3vni_get_vrr_rmac(zl3vni, &vrr_rmac); + + /* In absence of vrr mac use svi mac as anycast MAC value */ + if (is_zero_mac(&vrr_rmac)) { + memcpy(&vrr_rmac, &svi_rmac, ETH_ALEN); + is_anycast_mac = false; + } s = stream_new(ZEBRA_MAX_PACKET_SIZ); + /* The message is used for both vni add and/or update like + * vrr mac is added for l3vni SVI. + */ zclient_create_header(s, ZEBRA_L3VNI_ADD, zl3vni_vrf_id(zl3vni)); stream_putl(s, zl3vni->vni); - stream_put(s, &rmac, sizeof(struct ethaddr)); + stream_put(s, &svi_rmac, sizeof(struct ethaddr)); stream_put_in_addr(s, &zl3vni->local_vtep_ip); stream_put(s, &zl3vni->filter, sizeof(int)); stream_putl(s, zl3vni->svi_if->ifindex); + stream_put(s, &vrr_rmac, sizeof(struct ethaddr)); + stream_putl(s, is_anycast_mac); /* Write packet size. */ stream_putw_at(s, 0, stream_get_endp(s)); if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( - "Send L3_VNI_ADD %u VRF %s RMAC %s local-ip %s filter %s to %s", + "Send L3_VNI_ADD %u VRF %s RMAC %s VRR %s local-ip %s filter %s to %s", zl3vni->vni, vrf_id_to_name(zl3vni_vrf_id(zl3vni)), - prefix_mac2str(&rmac, buf, sizeof(buf)), + prefix_mac2str(&svi_rmac, buf, sizeof(buf)), + prefix_mac2str(&vrr_rmac, buf1, sizeof(buf1)), inet_ntoa(zl3vni->local_vtep_ip), CHECK_FLAG(zl3vni->filter, PREFIX_ROUTES_ONLY) ? "prefix-routes-only" @@ -5019,13 +1974,13 @@ static void zebra_vxlan_process_l3vni_oper_down(zebra_l3vni_t *zl3vni) zl3vni_send_del_to_client(zl3vni); } -static void zvni_add_to_l3vni_list(struct hash_bucket *bucket, void *ctxt) +static void zevpn_add_to_l3vni_list(struct hash_bucket *bucket, void *ctxt) { - zebra_vni_t *zvni = (zebra_vni_t *)bucket->data; + zebra_evpn_t *zevpn = (zebra_evpn_t *)bucket->data; zebra_l3vni_t *zl3vni = (zebra_l3vni_t *)ctxt; - if (zvni->vrf_id == zl3vni_vrf_id(zl3vni)) - listnode_add_sort(zl3vni->l2vnis, zvni); + if (zevpn->vrf_id == zl3vni_vrf_id(zl3vni)) + listnode_add_sort(zl3vni->l2vnis, zevpn); } /* @@ -5034,7 +1989,7 @@ static void zvni_add_to_l3vni_list(struct hash_bucket *bucket, void *ctxt) static int zebra_vxlan_handle_vni_transition(struct zebra_vrf *zvrf, vni_t vni, int add) { - zebra_vni_t *zvni = NULL; + zebra_evpn_t *zevpn = NULL; /* There is a possibility that VNI notification was already received * from kernel and we programmed it as L2-VNI @@ -5046,28 +2001,27 @@ static int zebra_vxlan_handle_vni_transition(struct zebra_vrf *zvrf, vni_t vni, */ if (add) { /* Locate hash entry */ - zvni = zvni_lookup(vni); - if (!zvni) + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) return 0; if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug("Del L2-VNI %u - transition to L3-VNI", vni); - /* Delete VNI from BGP. */ - zvni_send_del_to_client(zvni->vni); + /* Delete EVPN from BGP. */ + zebra_evpn_send_del_to_client(zevpn); - /* Free up all neighbors and MAC, if any. */ - zvni_neigh_del_all(zvni, 0, 0, DEL_ALL_NEIGH); - zvni_mac_del_all(zvni, 0, 0, DEL_ALL_MAC); + zebra_evpn_neigh_del_all(zevpn, 0, 0, DEL_ALL_NEIGH); + zebra_evpn_mac_del_all(zevpn, 0, 0, DEL_ALL_MAC); /* Free up all remote VTEPs, if any. */ - zvni_vtep_del_all(zvni, 0); + zebra_evpn_vtep_del_all(zevpn, 0); /* Delete the hash entry. */ - if (zvni_del(zvni)) { + if (zebra_evpn_vxlan_del(zevpn)) { flog_err(EC_ZEBRA_VNI_DEL_FAILED, - "Failed to del VNI hash %p, VNI %u", zvni, - zvni->vni); + "Failed to del EVPN hash %p, VNI %u", zevpn, + zevpn->vni); return -1; } } else { @@ -5090,6 +2044,10 @@ static void zl3vni_del_rmac_hash_entry(struct hash_bucket *bucket, void *ctx) zrmac = (zebra_mac_t *)bucket->data; zl3vni = (zebra_l3vni_t *)ctx; zl3vni_rmac_uninstall(zl3vni, zrmac); + + /* Send RMAC for FPM processing */ + hook_call(zebra_rmac_update, zrmac, zl3vni, true, "RMAC deleted"); + zl3vni_rmac_del(zl3vni, zrmac); } @@ -5105,40 +2063,6 @@ static void zl3vni_del_nh_hash_entry(struct hash_bucket *bucket, void *ctx) zl3vni_nh_del(zl3vni, n); } -static int ip_prefix_send_to_client(vrf_id_t vrf_id, struct prefix *p, - uint16_t cmd) -{ - struct zserv *client = NULL; - struct stream *s = NULL; - char buf[PREFIX_STRLEN]; - - client = zserv_find_client(ZEBRA_ROUTE_BGP, 0); - /* BGP may not be running. */ - if (!client) - return 0; - - s = stream_new(ZEBRA_MAX_PACKET_SIZ); - - zclient_create_header(s, cmd, vrf_id); - stream_put(s, p, sizeof(struct prefix)); - - /* Write packet size. */ - stream_putw_at(s, 0, stream_get_endp(s)); - - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Send ip prefix %s %s on vrf %s", - prefix2str(p, buf, sizeof(buf)), - (cmd == ZEBRA_IP_PREFIX_ROUTE_ADD) ? "ADD" : "DEL", - vrf_id_to_name(vrf_id)); - - if (cmd == ZEBRA_IP_PREFIX_ROUTE_ADD) - client->prefixadd_cnt++; - else - client->prefixdel_cnt++; - - return zserv_send_message(client, s); -} - /* re-add remote rmac if needed */ static int zebra_vxlan_readd_remote_rmac(zebra_l3vni_t *zl3vni, struct ethaddr *rmac) @@ -5158,508 +2082,6 @@ static int zebra_vxlan_readd_remote_rmac(zebra_l3vni_t *zl3vni, return 0; } -/* Process a remote MACIP add from BGP. */ -static void process_remote_macip_add(vni_t vni, - struct ethaddr *macaddr, - uint16_t ipa_len, - struct ipaddr *ipaddr, - uint8_t flags, - uint32_t seq, - struct in_addr vtep_ip) -{ - zebra_vni_t *zvni; - zebra_vtep_t *zvtep; - zebra_mac_t *mac = NULL, *old_mac = NULL; - zebra_neigh_t *n = NULL; - int update_mac = 0, update_neigh = 0; - char buf[ETHER_ADDR_STRLEN]; - char buf1[INET6_ADDRSTRLEN]; - struct interface *ifp = NULL; - struct zebra_if *zif = NULL; - struct zebra_vrf *zvrf; - uint32_t tmp_seq; - bool sticky; - bool remote_gw; - bool is_router; - bool do_dad = false; - bool is_dup_detect = false; - - /* Locate VNI hash entry - expected to exist. */ - zvni = zvni_lookup(vni); - if (!zvni) { - zlog_warn("Unknown VNI %u upon remote MACIP ADD", vni); - return; - } - - ifp = zvni->vxlan_if; - if (ifp) - zif = ifp->info; - if (!ifp || - !if_is_operative(ifp) || - !zif || - !zif->brslave_info.br_if) { - zlog_warn("Ignoring remote MACIP ADD VNI %u, invalid interface state or info", - vni); - return; - } - - /* The remote VTEP specified should normally exist, but it is - * possible that when peering comes up, peer may advertise MACIP - * routes before advertising type-3 routes. - */ - zvtep = zvni_vtep_find(zvni, &vtep_ip); - if (!zvtep) { - zvtep = zvni_vtep_add(zvni, &vtep_ip, VXLAN_FLOOD_DISABLED); - if (!zvtep) { - flog_err( - EC_ZEBRA_VTEP_ADD_FAILED, - "Failed to add remote VTEP, VNI %u zvni %p upon remote MACIP ADD", - vni, zvni); - return; - } - - zvni_vtep_install(zvni, zvtep); - } - - sticky = !!CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY); - remote_gw = !!CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_GW); - is_router = !!CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_ROUTER_FLAG); - - mac = zvni_mac_lookup(zvni, macaddr); - - /* Ignore if the mac is already present as a gateway mac */ - if (mac && - CHECK_FLAG(mac->flags, ZEBRA_MAC_DEF_GW) && - CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_GW)) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Ignore remote MACIP ADD VNI %u MAC %s%s%s as MAC is already configured as gateway MAC", - vni, - prefix_mac2str(macaddr, buf, sizeof(buf)), - ipa_len ? " IP " : "", - ipa_len ? - ipaddr2str(ipaddr, buf1, sizeof(buf1)) : ""); - return; - } - - zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id); - if (!zvrf) - return; - - /* check if the remote MAC is unknown or has a change. - * If so, that needs to be updated first. Note that client could - * install MAC and MACIP separately or just install the latter. - */ - if (!mac - || !CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE) - || sticky != !!CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY) - || remote_gw != !!CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE_DEF_GW) - || !IPV4_ADDR_SAME(&mac->fwd_info.r_vtep_ip, &vtep_ip) - || seq != mac->rem_seq) - update_mac = 1; - - if (update_mac) { - if (!mac) { - mac = zvni_mac_add(zvni, macaddr); - if (!mac) { - zlog_warn( - "Failed to add MAC %s VNI %u Remote VTEP %s", - prefix_mac2str(macaddr, buf, - sizeof(buf)), - vni, inet_ntoa(vtep_ip)); - return; - } - - /* Is this MAC created for a MACIP? */ - if (ipa_len) - SET_FLAG(mac->flags, ZEBRA_MAC_AUTO); - } else { - const char *mac_type; - - /* When host moves but changes its (MAC,IP) - * binding, BGP may install a MACIP entry that - * corresponds to "older" location of the host - * in transient situations (because {IP1,M1} - * is a different route from {IP1,M2}). Check - * the sequence number and ignore this update - * if appropriate. - */ - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) { - tmp_seq = mac->loc_seq; - mac_type = "local"; - } else { - tmp_seq = mac->rem_seq; - mac_type = "remote"; - } - if (seq < tmp_seq) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Ignore remote MACIP ADD VNI %u MAC %s%s%s as existing %s MAC has higher seq %u", - vni, - prefix_mac2str(macaddr, - buf, sizeof(buf)), - ipa_len ? " IP " : "", - ipa_len ? - ipaddr2str(ipaddr, - buf1, sizeof(buf1)) : "", - mac_type, - tmp_seq); - return; - } - } - - /* Check MAC's curent state is local (this is the case - * where MAC has moved from L->R) and check previous - * detection started via local learning. - * RFC-7432: A PE/VTEP that detects a MAC mobility - * event via local learning starts an M-second timer. - * - * VTEP-IP or seq. change alone is not considered - * for dup. detection. - * - * MAC is already marked duplicate set dad, then - * is_dup_detect will be set to not install the entry. - */ - if ((!CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE) && - mac->dad_count) || - CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) - do_dad = true; - - /* Remove local MAC from BGP. */ - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) - zvni_mac_send_del_to_client(zvni->vni, macaddr); - - /* Set "auto" and "remote" forwarding info. */ - UNSET_FLAG(mac->flags, ZEBRA_MAC_LOCAL); - memset(&mac->fwd_info, 0, sizeof(mac->fwd_info)); - SET_FLAG(mac->flags, ZEBRA_MAC_REMOTE); - mac->fwd_info.r_vtep_ip = vtep_ip; - - if (sticky) - SET_FLAG(mac->flags, ZEBRA_MAC_STICKY); - else - UNSET_FLAG(mac->flags, ZEBRA_MAC_STICKY); - - if (remote_gw) - SET_FLAG(mac->flags, ZEBRA_MAC_REMOTE_DEF_GW); - else - UNSET_FLAG(mac->flags, ZEBRA_MAC_REMOTE_DEF_GW); - - zebra_vxlan_dup_addr_detect_for_mac(zvrf, mac, - mac->fwd_info.r_vtep_ip, - do_dad, &is_dup_detect, - false); - - if (!is_dup_detect) { - zvni_process_neigh_on_remote_mac_add(zvni, mac); - /* Install the entry. */ - zvni_mac_install(zvni, mac); - } - } - - /* Update seq number. */ - mac->rem_seq = seq; - - /* If there is no IP, return after clearing AUTO flag of MAC. */ - if (!ipa_len) { - UNSET_FLAG(mac->flags, ZEBRA_MAC_AUTO); - return; - } - - /* Reset flag */ - do_dad = false; - - /* Check if the remote neighbor itself is unknown or has a - * change. If so, create or update and then install the entry. - */ - n = zvni_neigh_lookup(zvni, ipaddr); - if (!n - || !CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE) - || is_router != !!CHECK_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG) - || (memcmp(&n->emac, macaddr, sizeof(*macaddr)) != 0) - || !IPV4_ADDR_SAME(&n->r_vtep_ip, &vtep_ip) - || seq != n->rem_seq) - update_neigh = 1; - - if (update_neigh) { - if (!n) { - n = zvni_neigh_add(zvni, ipaddr, macaddr); - if (!n) { - zlog_warn( - "Failed to add Neigh %s MAC %s VNI %u Remote VTEP %s", - ipaddr2str(ipaddr, buf1, - sizeof(buf1)), - prefix_mac2str(macaddr, buf, - sizeof(buf)), - vni, inet_ntoa(vtep_ip)); - return; - } - - } else { - const char *n_type; - - /* When host moves but changes its (MAC,IP) - * binding, BGP may install a MACIP entry that - * corresponds to "older" location of the host - * in transient situations (because {IP1,M1} - * is a different route from {IP1,M2}). Check - * the sequence number and ignore this update - * if appropriate. - */ - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { - tmp_seq = n->loc_seq; - n_type = "local"; - } else { - tmp_seq = n->rem_seq; - n_type = "remote"; - } - if (seq < tmp_seq) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Ignore remote MACIP ADD VNI %u MAC %s%s%s as existing %s Neigh has higher seq %u", - vni, - prefix_mac2str(macaddr, - buf, sizeof(buf)), - " IP ", - ipaddr2str(ipaddr, buf1, sizeof(buf1)), - n_type, - tmp_seq); - return; - } - if (memcmp(&n->emac, macaddr, sizeof(*macaddr)) != 0) { - /* MAC change, send a delete for old - * neigh if learnt locally. - */ - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL) && - IS_ZEBRA_NEIGH_ACTIVE(n)) - zvni_neigh_send_del_to_client( - zvni->vni, &n->ip, - &n->emac, 0, n->state); - - /* update neigh list for macs */ - old_mac = zvni_mac_lookup(zvni, &n->emac); - if (old_mac) { - listnode_delete(old_mac->neigh_list, n); - zvni_deref_ip2mac(zvni, old_mac); - } - listnode_add_sort(mac->neigh_list, n); - memcpy(&n->emac, macaddr, ETH_ALEN); - - /* Check Neigh's curent state is local - * (this is the case where neigh/host has moved - * from L->R) and check previous detction - * started via local learning. - * - * RFC-7432: A PE/VTEP that detects a MAC - * mobilit event via local learning starts - * an M-second timer. - * VTEP-IP or seq. change along is not - * considered for dup. detection. - * - * Mobilty event scenario-B IP-MAC binding - * changed. - */ - if ((!CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) - && n->dad_count) - do_dad = true; - - } - } - - /* Set "remote" forwarding info. */ - UNSET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL); - n->r_vtep_ip = vtep_ip; - SET_FLAG(n->flags, ZEBRA_NEIGH_REMOTE); - - /* Set router flag (R-bit) to this Neighbor entry */ - if (CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_ROUTER_FLAG)) - SET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); - else - UNSET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); - - /* Check old or new MAC detected as duplicate, - * inherit duplicate flag to this neigh. - */ - if (zebra_vxlan_ip_inherit_dad_from_mac(zvrf, old_mac, - mac, n)) { - flog_warn(EC_ZEBRA_DUP_IP_INHERIT_DETECTED, - "VNI %u: MAC %s IP %s detected as duplicate during remote update, inherit duplicate from MAC", - zvni->vni, - prefix_mac2str(&mac->macaddr, buf, sizeof(buf)), - ipaddr2str(&n->ip, buf1, sizeof(buf1))); - } - - /* Check duplicate address detection for IP */ - zebra_vxlan_dup_addr_detect_for_neigh(zvrf, n, - n->r_vtep_ip, - do_dad, - &is_dup_detect, - false); - /* Install the entry. */ - if (!is_dup_detect) - zvni_neigh_install(zvni, n); - } - - zvni_probe_neigh_on_mac_add(zvni, mac); - - /* Update seq number. */ - n->rem_seq = seq; -} - -/* Process a remote MACIP delete from BGP. */ -static void process_remote_macip_del(vni_t vni, - struct ethaddr *macaddr, - uint16_t ipa_len, - struct ipaddr *ipaddr, - struct in_addr vtep_ip) -{ - zebra_vni_t *zvni; - zebra_mac_t *mac = NULL; - zebra_neigh_t *n = NULL; - struct interface *ifp = NULL; - struct zebra_if *zif = NULL; - struct zebra_ns *zns; - struct zebra_l2info_vxlan *vxl; - struct zebra_vrf *zvrf; - char buf[ETHER_ADDR_STRLEN]; - char buf1[INET6_ADDRSTRLEN]; - - /* Locate VNI hash entry - expected to exist. */ - zvni = zvni_lookup(vni); - if (!zvni) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Unknown VNI %u upon remote MACIP DEL", vni); - return; - } - - ifp = zvni->vxlan_if; - if (ifp) - zif = ifp->info; - if (!ifp || - !if_is_operative(ifp) || - !zif || - !zif->brslave_info.br_if) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Ignoring remote MACIP DEL VNI %u, invalid interface state or info", - vni); - return; - } - zns = zebra_ns_lookup(NS_DEFAULT); - vxl = &zif->l2info.vxl; - - /* The remote VTEP specified is normally expected to exist, but - * it is possible that the peer may delete the VTEP before deleting - * any MACs referring to the VTEP, in which case the handler (see - * remote_vtep_del) would have already deleted the MACs. - */ - if (!zvni_vtep_find(zvni, &vtep_ip)) - return; - - mac = zvni_mac_lookup(zvni, macaddr); - if (ipa_len) - n = zvni_neigh_lookup(zvni, ipaddr); - - if (n && !mac) { - zlog_warn("Failed to locate MAC %s for neigh %s VNI %u upon remote MACIP DEL", - prefix_mac2str(macaddr, buf, sizeof(buf)), - ipaddr2str(ipaddr, buf1, sizeof(buf1)), vni); - return; - } - - /* If the remote mac or neighbor doesn't exist there is nothing - * more to do. Otherwise, uninstall the entry and then remove it. - */ - if (!mac && !n) - return; - - zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id); - - /* Ignore the delete if this mac is a gateway mac-ip */ - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL) - && CHECK_FLAG(mac->flags, ZEBRA_MAC_DEF_GW)) { - zlog_warn( - "Ignore remote MACIP DEL VNI %u MAC %s%s%s as MAC is already configured as gateway MAC", - vni, - prefix_mac2str(macaddr, buf, sizeof(buf)), - ipa_len ? " IP " : "", - ipa_len ? - ipaddr2str(ipaddr, buf1, sizeof(buf1)) : ""); - return; - } - - /* Uninstall remote neighbor or MAC. */ - if (n) { - if (zvrf->dad_freeze && - CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE) && - CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE) && - (memcmp(n->emac.octet, macaddr->octet, ETH_ALEN) == 0)) { - struct interface *vlan_if; - - vlan_if = zvni_map_to_svi(vxl->access_vlan, - zif->brslave_info.br_if); - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("%s: IP %s (flags 0x%x intf %s) is remote and duplicate, read kernel for local entry", - __PRETTY_FUNCTION__, - ipaddr2str(ipaddr, buf1, - sizeof(buf1)), n->flags, - vlan_if->name); - neigh_read_specific_ip(ipaddr, vlan_if); - } - - /* When the MAC changes for an IP, it is possible the - * client may update the new MAC before trying to delete the - * "old" neighbor (as these are two different MACIP routes). - * Do the delete only if the MAC matches. - */ - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE) - && (memcmp(n->emac.octet, macaddr->octet, ETH_ALEN) == 0)) { - zvni_neigh_uninstall(zvni, n); - zvni_neigh_del(zvni, n); - zvni_deref_ip2mac(zvni, mac); - } - } else { - /* DAD: when MAC is freeze state as remote learn event, - * remote mac-ip delete event is received will result in freeze - * entry removal, first fetch kernel for the same entry present - * as LOCAL and reachable, avoid deleting this entry instead - * use kerenel local entry to update during unfreeze time. - */ - if (zvrf->dad_freeze && - CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE) && - CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("%s: MAC %s (flags 0x%x) is remote and duplicate, read kernel for local entry", - __PRETTY_FUNCTION__, - prefix_mac2str(macaddr, buf, - sizeof(buf)), - mac->flags); - macfdb_read_specific_mac(zns, zif->brslave_info.br_if, - macaddr, vxl->access_vlan); - } - - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { - zvni_process_neigh_on_remote_mac_del(zvni, mac); - /* - * the remote sequence number in the auto mac entry - * needs to be reset to 0 as the mac entry may have - * been removed on all VTEPs (including - * the originating one) - */ - mac->rem_seq = 0; - - /* If all remote neighbors referencing a remote MAC - * go away, we need to uninstall the MAC. - */ - if (remote_neigh_count(mac) == 0) { - zvni_mac_uninstall(zvni, mac); - UNSET_FLAG(mac->flags, ZEBRA_MAC_REMOTE); - } - if (list_isempty(mac->neigh_list)) - zvni_mac_del(zvni, mac); - else - SET_FLAG(mac->flags, ZEBRA_MAC_AUTO); - } - } -} - - /* Public functions */ int is_l3vni_for_prefix_routes_only(vni_t vni) @@ -5674,9 +2096,9 @@ int is_l3vni_for_prefix_routes_only(vni_t vni) } /* handle evpn route in vrf table */ -void zebra_vxlan_evpn_vrf_route_add(vrf_id_t vrf_id, struct ethaddr *rmac, - struct ipaddr *vtep_ip, - struct prefix *host_prefix) +void zebra_vxlan_evpn_vrf_route_add(vrf_id_t vrf_id, const struct ethaddr *rmac, + const struct ipaddr *vtep_ip, + const struct prefix *host_prefix) { zebra_l3vni_t *zl3vni = NULL; struct ipaddr ipv4_vtep; @@ -6053,22 +2475,22 @@ void zebra_vxlan_print_vrf_vni(struct vty *vty, struct zebra_vrf *zvrf, void zebra_vxlan_print_neigh_vni(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni, bool use_json) { - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; uint32_t num_neigh; struct neigh_walk_ctx wctx; json_object *json = NULL; if (!is_evpn_enabled()) return; - zvni = zvni_lookup(vni); - if (!zvni) { + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { if (use_json) vty_out(vty, "{}\n"); else vty_out(vty, "%% VNI %u does not exist\n", vni); return; } - num_neigh = hashcount(zvni->neigh_table); + num_neigh = hashcount(zevpn->neigh_table); if (!num_neigh) return; @@ -6080,23 +2502,22 @@ void zebra_vxlan_print_neigh_vni(struct vty *vty, struct zebra_vrf *zvrf, * the maximum width. */ memset(&wctx, 0, sizeof(struct neigh_walk_ctx)); - wctx.zvni = zvni; + wctx.zevpn = zevpn; wctx.vty = vty; wctx.addr_width = 15; wctx.json = json; - hash_iterate(zvni->neigh_table, zvni_find_neigh_addr_width, &wctx); + hash_iterate(zevpn->neigh_table, zebra_evpn_find_neigh_addr_width, + &wctx); if (!use_json) { vty_out(vty, "Number of ARPs (local and remote) known for this VNI: %u\n", num_neigh); - vty_out(vty, "%*s %-6s %-8s %-17s %-21s\n", - -wctx.addr_width, "IP", "Type", - "State", "MAC", "Remote VTEP"); + zebra_evpn_print_neigh_hdr(vty, &wctx); } else json_object_int_add(json, "numArpNd", num_neigh); - hash_iterate(zvni->neigh_table, zvni_print_neigh_hash, &wctx); + hash_iterate(zevpn->neigh_table, zebra_evpn_print_neigh_hash, &wctx); if (use_json) { vty_out(vty, "%s\n", json_object_to_json_string_ext( json, JSON_C_TO_STRING_PRETTY)); @@ -6123,9 +2544,9 @@ void zebra_vxlan_print_neigh_all_vni(struct vty *vty, struct zebra_vrf *zvrf, args[1] = json; args[2] = (void *)(ptrdiff_t)print_dup; - hash_iterate(zvrf->vni_table, + hash_iterate(zvrf->evpn_table, (void (*)(struct hash_bucket *, - void *))zvni_print_neigh_hash_all_vni, + void *))zevpn_print_neigh_hash_all_evpn, args); if (use_json) { vty_out(vty, "%s\n", json_object_to_json_string_ext( @@ -6154,9 +2575,9 @@ void zebra_vxlan_print_neigh_all_vni_detail(struct vty *vty, args[1] = json; args[2] = (void *)(ptrdiff_t)print_dup; - hash_iterate(zvrf->vni_table, + hash_iterate(zvrf->evpn_table, (void (*)(struct hash_bucket *, - void *))zvni_print_neigh_hash_all_vni_detail, + void *))zevpn_print_neigh_hash_all_evpn_detail, args); if (use_json) { vty_out(vty, "%s\n", json_object_to_json_string_ext( @@ -6172,21 +2593,21 @@ void zebra_vxlan_print_specific_neigh_vni(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni, struct ipaddr *ip, bool use_json) { - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; zebra_neigh_t *n; json_object *json = NULL; if (!is_evpn_enabled()) return; - zvni = zvni_lookup(vni); - if (!zvni) { + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { if (use_json) vty_out(vty, "{}\n"); else vty_out(vty, "%% VNI %u does not exist\n", vni); return; } - n = zvni_neigh_lookup(zvni, ip); + n = zebra_evpn_neigh_lookup(zevpn, ip); if (!n) { if (!use_json) vty_out(vty, @@ -6197,7 +2618,7 @@ void zebra_vxlan_print_specific_neigh_vni(struct vty *vty, if (use_json) json = json_object_new_object(); - zvni_print_neigh(n, vty, json); + zebra_evpn_print_neigh(n, vty, json); if (use_json) { vty_out(vty, "%s\n", json_object_to_json_string_ext( @@ -6214,34 +2635,38 @@ void zebra_vxlan_print_neigh_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni, struct in_addr vtep_ip, bool use_json) { - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; uint32_t num_neigh; struct neigh_walk_ctx wctx; json_object *json = NULL; if (!is_evpn_enabled()) return; - zvni = zvni_lookup(vni); - if (!zvni) { + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { if (use_json) vty_out(vty, "{}\n"); else vty_out(vty, "%% VNI %u does not exist\n", vni); return; } - num_neigh = hashcount(zvni->neigh_table); + num_neigh = hashcount(zevpn->neigh_table); if (!num_neigh) return; + if (use_json) + json = json_object_new_object(); + memset(&wctx, 0, sizeof(struct neigh_walk_ctx)); - wctx.zvni = zvni; + wctx.zevpn = zevpn; wctx.vty = vty; wctx.addr_width = 15; wctx.flags = SHOW_REMOTE_NEIGH_FROM_VTEP; wctx.r_vtep_ip = vtep_ip; wctx.json = json; - hash_iterate(zvni->neigh_table, zvni_find_neigh_addr_width, &wctx); - hash_iterate(zvni->neigh_table, zvni_print_neigh_hash, &wctx); + hash_iterate(zevpn->neigh_table, zebra_evpn_find_neigh_addr_width, + &wctx); + hash_iterate(zevpn->neigh_table, zebra_evpn_print_neigh_hash, &wctx); if (use_json) { vty_out(vty, "%s\n", json_object_to_json_string_ext( @@ -6259,7 +2684,7 @@ void zebra_vxlan_print_neigh_vni_dad(struct vty *vty, vni_t vni, bool use_json) { - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; uint32_t num_neigh; struct neigh_walk_ctx wctx; json_object *json = NULL; @@ -6267,17 +2692,17 @@ void zebra_vxlan_print_neigh_vni_dad(struct vty *vty, if (!is_evpn_enabled()) return; - zvni = zvni_lookup(vni); - if (!zvni) { + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { vty_out(vty, "%% VNI %u does not exist\n", vni); return; } - num_neigh = hashcount(zvni->neigh_table); + num_neigh = hashcount(zevpn->neigh_table); if (!num_neigh) return; - num_neigh = num_dup_detected_neighs(zvni); + num_neigh = num_dup_detected_neighs(zevpn); if (!num_neigh) return; @@ -6289,23 +2714,25 @@ void zebra_vxlan_print_neigh_vni_dad(struct vty *vty, * the maximum width. */ memset(&wctx, 0, sizeof(struct neigh_walk_ctx)); - wctx.zvni = zvni; + wctx.zevpn = zevpn; wctx.vty = vty; wctx.addr_width = 15; wctx.json = json; - hash_iterate(zvni->neigh_table, zvni_find_neigh_addr_width, &wctx); + hash_iterate(zevpn->neigh_table, zebra_evpn_find_neigh_addr_width, + &wctx); if (!use_json) { vty_out(vty, "Number of ARPs (local and remote) known for this VNI: %u\n", num_neigh); - vty_out(vty, "%*s %-6s %-8s %-17s %-21s\n", + vty_out(vty, "%*s %-6s %-8s %-17s %-30s\n", -wctx.addr_width, "IP", "Type", - "State", "MAC", "Remote VTEP"); + "State", "MAC", "Remote ES/VTEP"); } else json_object_int_add(json, "numArpNd", num_neigh); - hash_iterate(zvni->neigh_table, zvni_print_dad_neigh_hash, &wctx); + hash_iterate(zevpn->neigh_table, zebra_evpn_print_dad_neigh_hash, + &wctx); if (use_json) { vty_out(vty, "%s\n", json_object_to_json_string_ext( @@ -6320,7 +2747,7 @@ void zebra_vxlan_print_neigh_vni_dad(struct vty *vty, void zebra_vxlan_print_macs_vni(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni, bool use_json) { - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; uint32_t num_macs; struct mac_walk_ctx wctx; json_object *json = NULL; @@ -6328,15 +2755,15 @@ void zebra_vxlan_print_macs_vni(struct vty *vty, struct zebra_vrf *zvrf, if (!is_evpn_enabled()) return; - zvni = zvni_lookup(vni); - if (!zvni) { + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { if (use_json) vty_out(vty, "{}\n"); else vty_out(vty, "%% VNI %u does not exist\n", vni); return; } - num_macs = num_valid_macs(zvni); + num_macs = num_valid_macs(zevpn); if (!num_macs) return; @@ -6346,7 +2773,7 @@ void zebra_vxlan_print_macs_vni(struct vty *vty, struct zebra_vrf *zvrf, } memset(&wctx, 0, sizeof(struct mac_walk_ctx)); - wctx.zvni = zvni; + wctx.zevpn = zevpn; wctx.vty = vty; wctx.json = json_mac; @@ -6354,12 +2781,15 @@ void zebra_vxlan_print_macs_vni(struct vty *vty, struct zebra_vrf *zvrf, vty_out(vty, "Number of MACs (local and remote) known for this VNI: %u\n", num_macs); - vty_out(vty, "%-17s %-6s %-21s %-5s\n", "MAC", "Type", - "Intf/Remote VTEP", "VLAN"); + vty_out(vty, + "Flags: N=sync-neighs, I=local-inactive, P=peer-active, X=peer-proxy\n"); + vty_out(vty, "%-17s %-6s %-5s %-30s %-5s %s\n", "MAC", + "Type", "Flags", "Intf/Remote ES/VTEP", + "VLAN", "Seq #'s"); } else json_object_int_add(json, "numMacs", num_macs); - hash_iterate(zvni->mac_table, zvni_print_mac_hash, &wctx); + hash_iterate(zevpn->mac_table, zebra_evpn_print_mac_hash, &wctx); if (use_json) { json_object_object_add(json, "macs", json_mac); @@ -6390,7 +2820,7 @@ void zebra_vxlan_print_macs_all_vni(struct vty *vty, struct zebra_vrf *zvrf, wctx.vty = vty; wctx.json = json; wctx.print_dup = print_dup; - hash_iterate(zvrf->vni_table, zvni_print_mac_hash_all_vni, &wctx); + hash_iterate(zvrf->evpn_table, zevpn_print_mac_hash_all_evpn, &wctx); if (use_json) { vty_out(vty, "%s\n", json_object_to_json_string_ext( @@ -6421,7 +2851,7 @@ void zebra_vxlan_print_macs_all_vni_detail(struct vty *vty, wctx.vty = vty; wctx.json = json; wctx.print_dup = print_dup; - hash_iterate(zvrf->vni_table, zvni_print_mac_hash_all_vni_detail, + hash_iterate(zvrf->evpn_table, zevpn_print_mac_hash_all_evpn_detail, &wctx); if (use_json) { @@ -6452,7 +2882,7 @@ void zebra_vxlan_print_macs_all_vni_vtep(struct vty *vty, wctx.flags = SHOW_REMOTE_MAC_FROM_VTEP; wctx.r_vtep_ip = vtep_ip; wctx.json = json; - hash_iterate(zvrf->vni_table, zvni_print_mac_hash_all_vni, &wctx); + hash_iterate(zvrf->evpn_table, zevpn_print_mac_hash_all_evpn, &wctx); if (use_json) { vty_out(vty, "%s\n", json_object_to_json_string_ext( @@ -6468,22 +2898,22 @@ void zebra_vxlan_print_specific_mac_vni(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni, struct ethaddr *macaddr, bool use_json) { - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; zebra_mac_t *mac; json_object *json = NULL; if (!is_evpn_enabled()) return; - zvni = zvni_lookup(vni); - if (!zvni) { + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { if (use_json) vty_out(vty, "{}\n"); else vty_out(vty, "%% VNI %u does not exist\n", vni); return; } - mac = zvni_mac_lookup(zvni, macaddr); + mac = zebra_evpn_mac_lookup(zevpn, macaddr); if (!mac) { if (use_json) vty_out(vty, "{}\n"); @@ -6497,7 +2927,7 @@ void zebra_vxlan_print_specific_mac_vni(struct vty *vty, struct zebra_vrf *zvrf, if (use_json) json = json_object_new_object(); - zvni_print_mac(mac, vty, json); + zebra_evpn_print_mac(mac, vty, json); if (use_json) { vty_out(vty, "%s\n", json_object_to_json_string_ext( json, JSON_C_TO_STRING_PRETTY)); @@ -6510,7 +2940,7 @@ void zebra_vxlan_print_macs_vni_dad(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni, bool use_json) { - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; struct mac_walk_ctx wctx; uint32_t num_macs; json_object *json = NULL; @@ -6519,17 +2949,17 @@ void zebra_vxlan_print_macs_vni_dad(struct vty *vty, if (!is_evpn_enabled()) return; - zvni = zvni_lookup(vni); - if (!zvni) { + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { vty_out(vty, "%% VNI %u does not exist\n", vni); return; } - num_macs = num_valid_macs(zvni); + num_macs = num_valid_macs(zevpn); if (!num_macs) return; - num_macs = num_dup_detected_macs(zvni); + num_macs = num_dup_detected_macs(zevpn); if (!num_macs) return; @@ -6539,7 +2969,7 @@ void zebra_vxlan_print_macs_vni_dad(struct vty *vty, } memset(&wctx, 0, sizeof(struct mac_walk_ctx)); - wctx.zvni = zvni; + wctx.zevpn = zevpn; wctx.vty = vty; wctx.json = json_mac; @@ -6547,12 +2977,12 @@ void zebra_vxlan_print_macs_vni_dad(struct vty *vty, vty_out(vty, "Number of MACs (local and remote) known for this VNI: %u\n", num_macs); - vty_out(vty, "%-17s %-6s %-21s %-5s\n", "MAC", "Type", - "Intf/Remote VTEP", "VLAN"); + vty_out(vty, "%-17s %-6s %-5s %-30s %-5s\n", "MAC", "Type", + "Flags", "Intf/Remote ES/VTEP", "VLAN"); } else json_object_int_add(json, "numMacs", num_macs); - hash_iterate(zvni->mac_table, zvni_print_dad_mac_hash, &wctx); + hash_iterate(zevpn->mac_table, zebra_evpn_print_dad_mac_hash, &wctx); if (use_json) { json_object_object_add(json, "macs", json_mac); @@ -6563,34 +2993,32 @@ void zebra_vxlan_print_macs_vni_dad(struct vty *vty, } -int zebra_vxlan_clear_dup_detect_vni_mac(struct vty *vty, - struct zebra_vrf *zvrf, - vni_t vni, struct ethaddr *macaddr) +int zebra_vxlan_clear_dup_detect_vni_mac(struct zebra_vrf *zvrf, vni_t vni, + struct ethaddr *macaddr) { - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; zebra_mac_t *mac; struct listnode *node = NULL; zebra_neigh_t *nbr = NULL; if (!is_evpn_enabled()) - return CMD_SUCCESS; + return 0; - zvni = zvni_lookup(vni); - if (!zvni) { - vty_out(vty, "%% VNI %u does not exist\n", vni); - return CMD_WARNING; + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { + zlog_warn("VNI %u does not exist\n", vni); + return -1; } - mac = zvni_mac_lookup(zvni, macaddr); + mac = zebra_evpn_mac_lookup(zevpn, macaddr); if (!mac) { - vty_out(vty, "%% Requested MAC does not exist in VNI %u\n", - vni); - return CMD_WARNING; + zlog_warn("Requested MAC does not exist in VNI %u\n", vni); + return -1; } if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) { - vty_out(vty, "%% Requested MAC is not duplicate detected\n"); - return CMD_WARNING; + zlog_warn("Requested MAC is not duplicate detected\n"); + return -1; } /* Remove all IPs as duplicate associcated with this MAC */ @@ -6607,7 +3035,8 @@ int zebra_vxlan_clear_dup_detect_vni_mac(struct vty *vty, if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL)) ZEBRA_NEIGH_SET_INACTIVE(nbr); else if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_REMOTE)) - zvni_neigh_install(zvni, nbr); + zebra_evpn_rem_neigh_install( + zevpn, nbr, false /*was_static*/); } UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); @@ -6625,79 +3054,74 @@ int zebra_vxlan_clear_dup_detect_vni_mac(struct vty *vty, /* warn-only action return */ if (!zvrf->dad_freeze) - return CMD_SUCCESS; + return 0; /* Local: Notify Peer VTEPs, Remote: Install the entry */ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) { /* Inform to BGP */ - if (zvni_mac_send_add_to_client(zvni->vni, - &mac->macaddr, - mac->flags, - mac->loc_seq)) - return CMD_SUCCESS; + if (zebra_evpn_mac_send_add_to_client(zevpn->vni, &mac->macaddr, + mac->flags, mac->loc_seq, + mac->es)) + return 0; /* Process all neighbors associated with this MAC. */ - zvni_process_neigh_on_local_mac_change(zvni, mac, 0); + zebra_evpn_process_neigh_on_local_mac_change(zevpn, mac, 0, + 0 /*es_change*/); } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { - zvni_process_neigh_on_remote_mac_add(zvni, mac); + zebra_evpn_process_neigh_on_remote_mac_add(zevpn, mac); /* Install the entry. */ - zvni_mac_install(zvni, mac); + zebra_evpn_rem_mac_install(zevpn, mac, false /* was_static */); } - return CMD_SUCCESS; + return 0; } -int zebra_vxlan_clear_dup_detect_vni_ip(struct vty *vty, - struct zebra_vrf *zvrf, - vni_t vni, struct ipaddr *ip) +int zebra_vxlan_clear_dup_detect_vni_ip(struct zebra_vrf *zvrf, vni_t vni, + struct ipaddr *ip) { - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; zebra_neigh_t *nbr; zebra_mac_t *mac; char buf[INET6_ADDRSTRLEN]; char buf2[ETHER_ADDR_STRLEN]; if (!is_evpn_enabled()) - return CMD_SUCCESS; + return 0; - zvni = zvni_lookup(vni); - if (!zvni) { - vty_out(vty, "%% VNI %u does not exist\n", vni); - return CMD_WARNING; + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { + zlog_debug("VNI %u does not exist\n", vni); + return -1; } - nbr = zvni_neigh_lookup(zvni, ip); + nbr = zebra_evpn_neigh_lookup(zevpn, ip); if (!nbr) { - vty_out(vty, - "%% Requested host IP does not exist in VNI %u\n", - vni); - return CMD_WARNING; + zlog_warn("Requested host IP does not exist in VNI %u\n", vni); + return -1; } ipaddr2str(&nbr->ip, buf, sizeof(buf)); if (!CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) { - vty_out(vty, - "%% Requsted host IP %s is not duplicate detected\n", - buf); - return CMD_WARNING; + zlog_warn("Requested host IP %s is not duplicate detected\n", + buf); + return -1; } - mac = zvni_mac_lookup(zvni, &nbr->emac); + mac = zebra_evpn_mac_lookup(zevpn, &nbr->emac); if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) { - vty_out(vty, - "%% Requested IP's associated MAC %s is still in duplicate state\n", + zlog_warn( + "Requested IP's associated MAC %s is still in duplicate state\n", prefix_mac2str(&nbr->emac, buf2, sizeof(buf2))); - return CMD_WARNING_CONFIG_FAILED; + return -1; } if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug("%s: clear neigh %s in dup state, flags 0x%x seq %u", - __PRETTY_FUNCTION__, buf, nbr->flags, - nbr->loc_seq); + __func__, buf, nbr->flags, nbr->loc_seq); UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); nbr->dad_count = 0; @@ -6707,21 +3131,21 @@ int zebra_vxlan_clear_dup_detect_vni_ip(struct vty *vty, THREAD_OFF(nbr->dad_ip_auto_recovery_timer); if (!!CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL)) { - zvni_neigh_send_add_to_client(zvni->vni, ip, - &nbr->emac, - nbr->flags, nbr->loc_seq); + zebra_evpn_neigh_send_add_to_client(zevpn->vni, ip, &nbr->emac, + nbr->mac, nbr->flags, + nbr->loc_seq); } else if (!!CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_REMOTE)) { - zvni_neigh_install(zvni, nbr); + zebra_evpn_rem_neigh_install(zevpn, nbr, false /*was_static*/); } - return CMD_SUCCESS; + return 0; } -static void zvni_clear_dup_mac_hash(struct hash_bucket *bucket, void *ctxt) +static void zevpn_clear_dup_mac_hash(struct hash_bucket *bucket, void *ctxt) { struct mac_walk_ctx *wctx = ctxt; zebra_mac_t *mac; - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; struct listnode *node = NULL; zebra_neigh_t *nbr = NULL; @@ -6729,7 +3153,7 @@ static void zvni_clear_dup_mac_hash(struct hash_bucket *bucket, void *ctxt) if (!mac) return; - zvni = wctx->zvni; + zevpn = wctx->zevpn; if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) return; @@ -6756,150 +3180,101 @@ static void zvni_clear_dup_mac_hash(struct hash_bucket *bucket, void *ctxt) /* Local: Notify Peer VTEPs, Remote: Install the entry */ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) { /* Inform to BGP */ - if (zvni_mac_send_add_to_client(zvni->vni, - &mac->macaddr, - mac->flags, mac->loc_seq)) + if (zebra_evpn_mac_send_add_to_client(zevpn->vni, &mac->macaddr, + mac->flags, mac->loc_seq, + mac->es)) return; /* Process all neighbors associated with this MAC. */ - zvni_process_neigh_on_local_mac_change(zvni, mac, 0); + zebra_evpn_process_neigh_on_local_mac_change(zevpn, mac, 0, + 0 /*es_change*/); } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { - zvni_process_neigh_on_remote_mac_add(zvni, mac); + zebra_evpn_process_neigh_on_remote_mac_add(zevpn, mac); /* Install the entry. */ - zvni_mac_install(zvni, mac); - } -} - -static void zvni_clear_dup_neigh_hash(struct hash_bucket *bucket, void *ctxt) -{ - struct neigh_walk_ctx *wctx = ctxt; - zebra_neigh_t *nbr; - zebra_vni_t *zvni; - char buf[INET6_ADDRSTRLEN]; - - nbr = (zebra_neigh_t *)bucket->data; - if (!nbr) - return; - - zvni = wctx->zvni; - - if (!CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) - return; - - if (IS_ZEBRA_DEBUG_VXLAN) { - ipaddr2str(&nbr->ip, buf, sizeof(buf)); - zlog_debug( - "%s: clear neigh %s dup state, flags 0x%x seq %u", - __PRETTY_FUNCTION__, buf, - nbr->flags, nbr->loc_seq); - } - - UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); - nbr->dad_count = 0; - nbr->detect_start_time.tv_sec = 0; - nbr->detect_start_time.tv_usec = 0; - nbr->dad_dup_detect_time = 0; - THREAD_OFF(nbr->dad_ip_auto_recovery_timer); - - if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL)) { - zvni_neigh_send_add_to_client(zvni->vni, &nbr->ip, - &nbr->emac, - nbr->flags, nbr->loc_seq); - } else if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_REMOTE)) { - zvni_neigh_install(zvni, nbr); + zebra_evpn_rem_mac_install(zevpn, mac, false /* was_static */); } } -static void zvni_clear_dup_detect_hash_vni_all(struct hash_bucket *bucket, +static void zevpn_clear_dup_detect_hash_vni_all(struct hash_bucket *bucket, void **args) { - struct vty *vty; - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; struct zebra_vrf *zvrf; struct mac_walk_ctx m_wctx; struct neigh_walk_ctx n_wctx; - zvni = (zebra_vni_t *)bucket->data; - if (!zvni) + zevpn = (zebra_evpn_t *)bucket->data; + if (!zevpn) return; - vty = (struct vty *)args[0]; - zvrf = (struct zebra_vrf *)args[1]; + zvrf = (struct zebra_vrf *)args[0]; - if (hashcount(zvni->neigh_table)) { + if (hashcount(zevpn->neigh_table)) { memset(&n_wctx, 0, sizeof(struct neigh_walk_ctx)); - n_wctx.vty = vty; - n_wctx.zvni = zvni; + n_wctx.zevpn = zevpn; n_wctx.zvrf = zvrf; - hash_iterate(zvni->neigh_table, zvni_clear_dup_neigh_hash, - &n_wctx); + hash_iterate(zevpn->neigh_table, + zebra_evpn_clear_dup_neigh_hash, &n_wctx); } - if (num_valid_macs(zvni)) { + if (num_valid_macs(zevpn)) { memset(&m_wctx, 0, sizeof(struct mac_walk_ctx)); - m_wctx.zvni = zvni; - m_wctx.vty = vty; + m_wctx.zevpn = zevpn; m_wctx.zvrf = zvrf; - hash_iterate(zvni->mac_table, zvni_clear_dup_mac_hash, &m_wctx); + hash_iterate(zevpn->mac_table, zevpn_clear_dup_mac_hash, &m_wctx); } } -int zebra_vxlan_clear_dup_detect_vni_all(struct vty *vty, - struct zebra_vrf *zvrf) +int zebra_vxlan_clear_dup_detect_vni_all(struct zebra_vrf *zvrf) { - void *args[2]; + void *args[1]; if (!is_evpn_enabled()) - return CMD_SUCCESS; + return 0; - args[0] = vty; - args[1] = zvrf; + args[0] = zvrf; - hash_iterate(zvrf->vni_table, + hash_iterate(zvrf->evpn_table, (void (*)(struct hash_bucket *, void *)) - zvni_clear_dup_detect_hash_vni_all, args); + zevpn_clear_dup_detect_hash_vni_all, args); - return CMD_SUCCESS; + return 0; } -int zebra_vxlan_clear_dup_detect_vni(struct vty *vty, - struct zebra_vrf *zvrf, - vni_t vni) +int zebra_vxlan_clear_dup_detect_vni(struct zebra_vrf *zvrf, vni_t vni) { - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; struct mac_walk_ctx m_wctx; struct neigh_walk_ctx n_wctx; if (!is_evpn_enabled()) - return CMD_SUCCESS; + return 0; - zvni = zvni_lookup(vni); - if (!zvni) { - vty_out(vty, "%% VNI %u does not exist\n", vni); + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { + zlog_warn("VNI %u does not exist\n", vni); return CMD_WARNING; } - if (hashcount(zvni->neigh_table)) { + if (hashcount(zevpn->neigh_table)) { memset(&n_wctx, 0, sizeof(struct neigh_walk_ctx)); - n_wctx.vty = vty; - n_wctx.zvni = zvni; + n_wctx.zevpn = zevpn; n_wctx.zvrf = zvrf; - hash_iterate(zvni->neigh_table, zvni_clear_dup_neigh_hash, - &n_wctx); + hash_iterate(zevpn->neigh_table, + zebra_evpn_clear_dup_neigh_hash, &n_wctx); } - if (num_valid_macs(zvni)) { + if (num_valid_macs(zevpn)) { memset(&m_wctx, 0, sizeof(struct mac_walk_ctx)); - m_wctx.zvni = zvni; - m_wctx.vty = vty; + m_wctx.zevpn = zevpn; m_wctx.zvrf = zvrf; - hash_iterate(zvni->mac_table, zvni_clear_dup_mac_hash, &m_wctx); + hash_iterate(zevpn->mac_table, zevpn_clear_dup_mac_hash, &m_wctx); } - return CMD_SUCCESS; + return 0; } /* @@ -6909,7 +3284,7 @@ void zebra_vxlan_print_macs_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni, struct in_addr vtep_ip, bool use_json) { - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; uint32_t num_macs; struct mac_walk_ctx wctx; json_object *json = NULL; @@ -6917,15 +3292,15 @@ void zebra_vxlan_print_macs_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf, if (!is_evpn_enabled()) return; - zvni = zvni_lookup(vni); - if (!zvni) { + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { if (use_json) vty_out(vty, "{}\n"); else vty_out(vty, "%% VNI %u does not exist\n", vni); return; } - num_macs = num_valid_macs(zvni); + num_macs = num_valid_macs(zevpn); if (!num_macs) return; @@ -6935,12 +3310,12 @@ void zebra_vxlan_print_macs_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf, } memset(&wctx, 0, sizeof(struct mac_walk_ctx)); - wctx.zvni = zvni; + wctx.zevpn = zevpn; wctx.vty = vty; wctx.flags = SHOW_REMOTE_MAC_FROM_VTEP; wctx.r_vtep_ip = vtep_ip; wctx.json = json_mac; - hash_iterate(zvni->mac_table, zvni_print_mac_hash, &wctx); + hash_iterate(zevpn->mac_table, zebra_evpn_print_mac_hash, &wctx); if (use_json) { json_object_int_add(json, "numMacs", wctx.count); @@ -6955,20 +3330,26 @@ void zebra_vxlan_print_macs_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf, /* * Display VNI information (VTY command handler). + * + * use_json flag indicates that output should be in JSON format. + * json_array is non NULL when JSON output needs to be aggregated (by the + * caller) and then printed, otherwise, JSON evpn vni info is printed + * right away. */ void zebra_vxlan_print_vni(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni, - bool use_json) + bool use_json, json_object *json_array) { json_object *json = NULL; void *args[2]; zebra_l3vni_t *zl3vni = NULL; - zebra_vni_t *zvni = NULL; + zebra_evpn_t *zevpn = NULL; if (!is_evpn_enabled()) return; if (use_json) json = json_object_new_object(); + args[0] = vty; args[1] = json; @@ -6976,22 +3357,26 @@ void zebra_vxlan_print_vni(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni, if (zl3vni) { zl3vni_print(zl3vni, (void *)args); } else { - zvni = zvni_lookup(vni); - if (!zvni) { - if (use_json) - vty_out(vty, "{}\n"); - else - vty_out(vty, "%% VNI %u does not exist\n", vni); - return; - } - - zvni_print(zvni, (void *)args); + zevpn = zebra_evpn_lookup(vni); + if (zevpn) + zebra_evpn_print(zevpn, (void *)args); + else if (!json) + vty_out(vty, "%% VNI %u does not exist\n", vni); } if (use_json) { - vty_out(vty, "%s\n", json_object_to_json_string_ext( - json, JSON_C_TO_STRING_PRETTY)); - json_object_free(json); + /* + * Each "json" object contains info about 1 VNI. + * When "json_array" is non-null, we aggreggate the json output + * into json_array and print it as a JSON array. + */ + if (json_array) + json_object_array_add(json_array, json); + else { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } } } @@ -7012,7 +3397,7 @@ void zebra_vxlan_print_evpn(struct vty *vty, bool uj) return; num_l3vnis = hashcount(zrouter.l3vni_table); - num_l2vnis = hashcount(zvrf->vni_table); + num_l2vnis = hashcount(zvrf->evpn_table); num_vnis = num_l2vnis + num_l3vnis; if (uj) { @@ -7084,9 +3469,10 @@ void zebra_vxlan_print_vnis(struct vty *vty, struct zebra_vrf *zvrf, args[1] = json; /* Display all L2-VNIs */ - hash_iterate(zvrf->vni_table, - (void (*)(struct hash_bucket *, void *))zvni_print_hash, - args); + hash_iterate( + zvrf->evpn_table, + (void (*)(struct hash_bucket *, void *))zebra_evpn_print_hash, + args); /* Display all L3-VNIs */ hash_iterate(zrouter.l3vni_table, @@ -7120,7 +3506,7 @@ void zebra_vxlan_dup_addr_detection(ZAPI_HANDLER_ARGS) * clear all duplicate detected addresses. */ if (zvrf->dup_addr_detect && !dup_addr_detect) - zebra_vxlan_clear_dup_detect_vni_all(NULL, zvrf); + zebra_vxlan_clear_dup_detect_vni_all(zvrf); zvrf->dup_addr_detect = dup_addr_detect; zvrf->dad_time = time; @@ -7148,9 +3534,9 @@ void zebra_vxlan_dup_addr_detection(ZAPI_HANDLER_ARGS) void zebra_vxlan_print_vnis_detail(struct vty *vty, struct zebra_vrf *zvrf, bool use_json) { - json_object *json = NULL; + json_object *json_array = NULL; struct zebra_ns *zns = NULL; - struct zvni_evpn_show zes; + struct zebra_evpn_show zes; if (!is_evpn_enabled()) return; @@ -7159,19 +3545,19 @@ void zebra_vxlan_print_vnis_detail(struct vty *vty, struct zebra_vrf *zvrf, if (!zns) return; - if (use_json) - json = json_object_new_object(); + json_array = json_object_new_array(); zes.vty = vty; - zes.json = json; + zes.json = json_array; zes.zvrf = zvrf; + zes.use_json = use_json; /* Display all L2-VNIs */ - hash_iterate( - zvrf->vni_table, - (void (*)(struct hash_bucket *, void *))zvni_print_hash_detail, - &zes); + hash_iterate(zvrf->evpn_table, + (void (*)(struct hash_bucket *, + void *))zebra_evpn_print_hash_detail, + &zes); /* Display all L3-VNIs */ hash_iterate(zrouter.l3vni_table, @@ -7182,8 +3568,8 @@ void zebra_vxlan_print_vnis_detail(struct vty *vty, struct zebra_vrf *zvrf, if (use_json) { vty_out(vty, "%s\n", json_object_to_json_string_ext( - json, JSON_C_TO_STRING_PRETTY)); - json_object_free(json); + json_array, JSON_C_TO_STRING_PRETTY)); + json_object_free(json_array); } } @@ -7198,12 +3584,8 @@ int zebra_vxlan_handle_kernel_neigh_del(struct interface *ifp, struct ipaddr *ip) { char buf[INET6_ADDRSTRLEN]; - char buf2[ETHER_ADDR_STRLEN]; - zebra_neigh_t *n = NULL; - zebra_vni_t *zvni = NULL; - zebra_mac_t *zmac = NULL; + zebra_evpn_t *zevpn = NULL; zebra_l3vni_t *zl3vni = NULL; - struct zebra_vrf *zvrf; /* check if this is a remote neigh entry corresponding to remote * next-hop @@ -7215,74 +3597,29 @@ int zebra_vxlan_handle_kernel_neigh_del(struct interface *ifp, /* We are only interested in neighbors on an SVI that resides on top * of a VxLAN bridge. */ - zvni = zvni_from_svi(ifp, link_if); - if (!zvni) + zevpn = zebra_evpn_from_svi(ifp, link_if); + if (!zevpn) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%s: Del neighbor %s EVPN is not present for interface %s", + __func__, ipaddr2str(ip, buf, sizeof(buf)), + ifp->name); return 0; + } - if (!zvni->vxlan_if) { + if (!zevpn->vxlan_if) { zlog_debug( "VNI %u hash %p doesn't have intf upon local neighbor DEL", - zvni->vni, zvni); + zevpn->vni, zevpn); return -1; } if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug("Del neighbor %s intf %s(%u) -> L2-VNI %u", ipaddr2str(ip, buf, sizeof(buf)), ifp->name, - ifp->ifindex, zvni->vni); - - /* If entry doesn't exist, nothing to do. */ - n = zvni_neigh_lookup(zvni, ip); - if (!n) - return 0; - - zmac = zvni_mac_lookup(zvni, &n->emac); - if (!zmac) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "Trying to del a neigh %s without a mac %s on VNI %u", - ipaddr2str(ip, buf, sizeof(buf)), - prefix_mac2str(&n->emac, buf2, sizeof(buf2)), - zvni->vni); - - return 0; - } - - /* If it is a remote entry, the kernel has aged this out or someone has - * deleted it, it needs to be re-installed as Quagga is the owner. - */ - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) { - zvni_neigh_install(zvni, n); - return 0; - } - - zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id); - if (!zvrf) { - zlog_debug("%s: VNI %u vrf lookup failed.", - __PRETTY_FUNCTION__, zvni->vni); - return -1; - } - - /* In case of feeze action, if local neigh is in duplicate state, - * Mark the Neigh as inactive before sending delete request to BGPd, - * If BGPd has remote entry, it will re-install - */ - if (zvrf->dad_freeze && - CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE)) - ZEBRA_NEIGH_SET_INACTIVE(n); - - /* Remove neighbor from BGP. */ - zvni_neigh_send_del_to_client(zvni->vni, &n->ip, &n->emac, 0, n->state); - - /* Delete this neighbor entry. */ - zvni_neigh_del(zvni, n); - - /* see if the AUTO mac needs to be deleted */ - if (CHECK_FLAG(zmac->flags, ZEBRA_MAC_AUTO) - && !listcount(zmac->neigh_list)) - zvni_mac_del(zvni, zmac); + ifp->ifindex, zevpn->vni); - return 0; + return zebra_evpn_neigh_del_ip(zevpn, ip); } /* @@ -7297,11 +3634,12 @@ int zebra_vxlan_handle_kernel_neigh_update(struct interface *ifp, struct ethaddr *macaddr, uint16_t state, bool is_ext, - bool is_router) + bool is_router, + bool local_inactive, bool dp_static) { char buf[ETHER_ADDR_STRLEN]; char buf2[INET6_ADDRSTRLEN]; - zebra_vni_t *zvni = NULL; + zebra_evpn_t *zevpn = NULL; zebra_l3vni_t *zl3vni = NULL; /* check if this is a remote neigh entry corresponding to remote @@ -7314,27 +3652,80 @@ int zebra_vxlan_handle_kernel_neigh_update(struct interface *ifp, /* We are only interested in neighbors on an SVI that resides on top * of a VxLAN bridge. */ - zvni = zvni_from_svi(ifp, link_if); - if (!zvni) + zevpn = zebra_evpn_from_svi(ifp, link_if); + if (!zevpn) return 0; - if (IS_ZEBRA_DEBUG_VXLAN) + if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) zlog_debug( - "Add/Update neighbor %s MAC %s intf %s(%u) state 0x%x %s %s-> L2-VNI %u", + "Add/Update neighbor %s MAC %s intf %s(%u) state 0x%x %s%s%s-> L2-VNI %u", ipaddr2str(ip, buf2, sizeof(buf2)), prefix_mac2str(macaddr, buf, sizeof(buf)), ifp->name, ifp->ifindex, state, is_ext ? "ext-learned " : "", is_router ? "router " : "", - zvni->vni); + local_inactive ? "local_inactive " : "", + zevpn->vni); /* Is this about a local neighbor or a remote one? */ if (!is_ext) - return zvni_local_neigh_update(zvni, ifp, ip, macaddr, - is_router); + return zebra_evpn_local_neigh_update(zevpn, ifp, ip, macaddr, + is_router, local_inactive, + dp_static); - return zvni_remote_neigh_update(zvni, ifp, ip, macaddr, state); + return zebra_evpn_remote_neigh_update(zevpn, ifp, ip, macaddr, state); } +static int32_t +zebra_vxlan_remote_macip_helper(bool add, struct stream *s, vni_t *vni, + struct ethaddr *macaddr, uint16_t *ipa_len, + struct ipaddr *ip, struct in_addr *vtep_ip, + uint8_t *flags, uint32_t *seq, esi_t *esi) +{ + uint16_t l = 0; + + /* + * Obtain each remote MACIP and process. + * Message contains VNI, followed by MAC followed by IP (if any) + * followed by remote VTEP IP. + */ + memset(ip, 0, sizeof(*ip)); + STREAM_GETL(s, *vni); + STREAM_GET(macaddr->octet, s, ETH_ALEN); + STREAM_GETL(s, *ipa_len); + + if (*ipa_len) { + if (*ipa_len == IPV4_MAX_BYTELEN) + ip->ipa_type = IPADDR_V4; + else if (*ipa_len == IPV6_MAX_BYTELEN) + ip->ipa_type = IPADDR_V6; + else { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "ipa_len *must* be %d or %d bytes in length not %d", + IPV4_MAX_BYTELEN, IPV6_MAX_BYTELEN, + *ipa_len); + goto stream_failure; + } + + STREAM_GET(&ip->ip.addr, s, *ipa_len); + } + l += 4 + ETH_ALEN + 4 + *ipa_len; + STREAM_GET(&vtep_ip->s_addr, s, IPV4_MAX_BYTELEN); + l += IPV4_MAX_BYTELEN; + + if (add) { + STREAM_GETC(s, *flags); + STREAM_GETL(s, *seq); + l += 5; + STREAM_GET(esi, s, sizeof(esi_t)); + l += sizeof(esi_t); + } + + return l; + +stream_failure: + return -1; +} /* * Handle message from client to delete a remote MACIP for a VNI. @@ -7357,23 +3748,14 @@ void zebra_vxlan_remote_macip_del(ZAPI_HANDLER_ARGS) s = msg; while (l < hdr->length) { - /* Obtain each remote MACIP and process. */ - /* Message contains VNI, followed by MAC followed by IP (if any) - * followed by remote VTEP IP. - */ - memset(&ip, 0, sizeof(ip)); - STREAM_GETL(s, vni); - STREAM_GET(&macaddr.octet, s, ETH_ALEN); - STREAM_GETL(s, ipa_len); - if (ipa_len) { - ip.ipa_type = (ipa_len == IPV4_MAX_BYTELEN) ? IPADDR_V4 - : IPADDR_V6; - STREAM_GET(&ip.ip.addr, s, ipa_len); - } - l += 4 + ETH_ALEN + 4 + ipa_len; - STREAM_GET(&vtep_ip.s_addr, s, IPV4_MAX_BYTELEN); - l += IPV4_MAX_BYTELEN; + int res_length = zebra_vxlan_remote_macip_helper( + false, s, &vni, &macaddr, &ipa_len, &ip, &vtep_ip, NULL, + NULL, NULL); + if (res_length == -1) + goto stream_failure; + + l += res_length; if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( "Recv MACIP DEL VNI %u MAC %s%s%s Remote VTEP %s from %s", @@ -7409,6 +3791,8 @@ void zebra_vxlan_remote_macip_add(ZAPI_HANDLER_ARGS) uint32_t seq; char buf[ETHER_ADDR_STRLEN]; char buf1[INET6_ADDRSTRLEN]; + esi_t esi; + char esi_buf[ESI_STR_LEN]; memset(&macaddr, 0, sizeof(struct ethaddr)); memset(&ip, 0, sizeof(struct ipaddr)); @@ -7422,48 +3806,86 @@ void zebra_vxlan_remote_macip_add(ZAPI_HANDLER_ARGS) s = msg; while (l < hdr->length) { - /* Obtain each remote MACIP and process. */ - /* Message contains VNI, followed by MAC followed by IP (if any) - * followed by remote VTEP IP. - */ - memset(&ip, 0, sizeof(ip)); - STREAM_GETL(s, vni); - STREAM_GET(&macaddr.octet, s, ETH_ALEN); - STREAM_GETL(s, ipa_len); - if (ipa_len) { - ip.ipa_type = (ipa_len == IPV4_MAX_BYTELEN) ? IPADDR_V4 - : IPADDR_V6; - STREAM_GET(&ip.ip.addr, s, ipa_len); - } - l += 4 + ETH_ALEN + 4 + ipa_len; - STREAM_GET(&vtep_ip.s_addr, s, IPV4_MAX_BYTELEN); - l += IPV4_MAX_BYTELEN; + int res_length = zebra_vxlan_remote_macip_helper( + true, s, &vni, &macaddr, &ipa_len, &ip, &vtep_ip, + &flags, &seq, &esi); - /* Get flags - sticky mac and/or gateway mac */ - STREAM_GETC(s, flags); - l++; - STREAM_GETL(s, seq); - l += 4; + if (res_length == -1) + goto stream_failure; - if (IS_ZEBRA_DEBUG_VXLAN) + l += res_length; + if (IS_ZEBRA_DEBUG_VXLAN) { + if (memcmp(&esi, zero_esi, sizeof(esi_t))) + esi_to_str(&esi, esi_buf, sizeof(esi_buf)); + else + strlcpy(esi_buf, "-", ESI_STR_LEN); zlog_debug( - "Recv MACIP ADD VNI %u MAC %s%s%s flags 0x%x seq %u VTEP %s from %s", + "Recv %sMACIP ADD VNI %u MAC %s%s%s flags 0x%x seq %u VTEP %s ESI %s from %s", + (flags & ZEBRA_MACIP_TYPE_SYNC_PATH) ? + "sync-" : "", vni, prefix_mac2str(&macaddr, buf, sizeof(buf)), ipa_len ? " IP " : "", ipa_len ? ipaddr2str(&ip, buf1, sizeof(buf1)) : "", - flags, seq, inet_ntoa(vtep_ip), + flags, seq, inet_ntoa(vtep_ip), esi_buf, zebra_route_string(client->proto)); + } process_remote_macip_add(vni, &macaddr, ipa_len, &ip, - flags, seq, vtep_ip); + flags, seq, vtep_ip, &esi); } stream_failure: return; } +/* + * Handle remote vtep delete by kernel; re-add the vtep if we have it + */ +int zebra_vxlan_check_readd_vtep(struct interface *ifp, + struct in_addr vtep_ip) +{ + struct zebra_if *zif; + struct zebra_vrf *zvrf = NULL; + struct zebra_l2info_vxlan *vxl; + vni_t vni; + zebra_evpn_t *zevpn = NULL; + zebra_vtep_t *zvtep = NULL; + + zif = ifp->info; + assert(zif); + vxl = &zif->l2info.vxl; + vni = vxl->vni; + + /* If EVPN is not enabled, nothing to do. */ + if (!is_evpn_enabled()) + return 0; + + /* Locate VRF corresponding to interface. */ + zvrf = vrf_info_lookup(ifp->vrf_id); + if (!zvrf) + return -1; + + /* Locate hash entry; it is expected to exist. */ + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) + return 0; + + /* If the remote vtep entry doesn't exists nothing to do */ + zvtep = zebra_evpn_vtep_find(zevpn, &vtep_ip); + if (!zvtep) + return 0; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Del MAC for remote VTEP %s intf %s(%u) VNI %u - readd", + inet_ntoa(vtep_ip), ifp->name, ifp->ifindex, vni); + + zebra_evpn_vtep_install(zevpn, zvtep); + return 0; +} + /* * Handle notification of MAC add/update over VxLAN. If the kernel is notifying * us, this must involve a multihoming scenario. Treat this as implicit delete @@ -7476,7 +3898,7 @@ int zebra_vxlan_check_del_local_mac(struct interface *ifp, struct zebra_if *zif; struct zebra_l2info_vxlan *vxl; vni_t vni; - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; zebra_mac_t *mac; char buf[ETHER_ADDR_STRLEN]; @@ -7490,12 +3912,12 @@ int zebra_vxlan_check_del_local_mac(struct interface *ifp, return 0; /* Locate hash entry; it is expected to exist. */ - zvni = zvni_lookup(vni); - if (!zvni) + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) return 0; /* If entry doesn't exist, nothing to do. */ - mac = zvni_mac_lookup(zvni, macaddr); + mac = zebra_evpn_mac_lookup(zevpn, macaddr); if (!mac) return 0; @@ -7505,21 +3927,23 @@ int zebra_vxlan_check_del_local_mac(struct interface *ifp, if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( - "Add/update remote MAC %s intf %s(%u) VNI %u - del local", + "Add/update remote MAC %s intf %s(%u) VNI %u flags 0x%x - del local", prefix_mac2str(macaddr, buf, sizeof(buf)), ifp->name, - ifp->ifindex, vni); + ifp->ifindex, vni, mac->flags); /* Remove MAC from BGP. */ - zvni_mac_send_del_to_client(zvni->vni, macaddr); + zebra_evpn_mac_send_del_to_client(zevpn->vni, macaddr, mac->flags, + false /* force */); /* * If there are no neigh associated with the mac delete the mac * else mark it as AUTO for forward reference */ if (!listcount(mac->neigh_list)) { - zvni_mac_del(zvni, mac); + zebra_evpn_mac_del(zevpn, mac); } else { - UNSET_FLAG(mac->flags, ZEBRA_MAC_LOCAL); + UNSET_FLAG(mac->flags, ZEBRA_MAC_ALL_LOCAL_FLAGS); + UNSET_FLAG(mac->flags, ZEBRA_MAC_STICKY); SET_FLAG(mac->flags, ZEBRA_MAC_AUTO); } @@ -7538,7 +3962,7 @@ int zebra_vxlan_check_readd_remote_mac(struct interface *ifp, struct zebra_if *zif = NULL; struct zebra_l2info_vxlan *vxl = NULL; vni_t vni; - zebra_vni_t *zvni = NULL; + zebra_evpn_t *zevpn = NULL; zebra_l3vni_t *zl3vni = NULL; zebra_mac_t *mac = NULL; char buf[ETHER_ADDR_STRLEN]; @@ -7558,12 +3982,12 @@ int zebra_vxlan_check_readd_remote_mac(struct interface *ifp, return zebra_vxlan_readd_remote_rmac(zl3vni, macaddr); /* Locate hash entry; it is expected to exist. */ - zvni = zvni_lookup(vni); - if (!zvni) + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) return 0; /* If entry doesn't exist, nothing to do. */ - mac = zvni_mac_lookup(zvni, macaddr); + mac = zebra_evpn_mac_lookup(zevpn, macaddr); if (!mac) return 0; @@ -7576,7 +4000,7 @@ int zebra_vxlan_check_readd_remote_mac(struct interface *ifp, prefix_mac2str(macaddr, buf, sizeof(buf)), ifp->name, ifp->ifindex, vni); - zvni_mac_install(zvni, mac); + zebra_evpn_rem_mac_install(zevpn, mac, false /* was_static */); return 0; } @@ -7586,55 +4010,22 @@ int zebra_vxlan_check_readd_remote_mac(struct interface *ifp, int zebra_vxlan_local_mac_del(struct interface *ifp, struct interface *br_if, struct ethaddr *macaddr, vlanid_t vid) { - zebra_vni_t *zvni; - zebra_mac_t *mac; - char buf[ETHER_ADDR_STRLEN]; + zebra_evpn_t *zevpn; /* We are interested in MACs only on ports or (port, VLAN) that * map to a VNI. */ - zvni = zvni_map_vlan(ifp, br_if, vid); - if (!zvni) + zevpn = zebra_evpn_map_vlan(ifp, br_if, vid); + if (!zevpn) return 0; - if (!zvni->vxlan_if) { + if (!zevpn->vxlan_if) { zlog_debug( "VNI %u hash %p doesn't have intf upon local MAC DEL", - zvni->vni, zvni); + zevpn->vni, zevpn); return -1; } - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("DEL MAC %s intf %s(%u) VID %u -> VNI %u", - prefix_mac2str(macaddr, buf, sizeof(buf)), ifp->name, - ifp->ifindex, vid, zvni->vni); - - /* If entry doesn't exist, nothing to do. */ - mac = zvni_mac_lookup(zvni, macaddr); - if (!mac) - return 0; - - /* Is it a local entry? */ - if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) - return 0; - - /* Update all the neigh entries associated with this mac */ - zvni_process_neigh_on_local_mac_del(zvni, mac); - - /* Remove MAC from BGP. */ - zvni_mac_send_del_to_client(zvni->vni, macaddr); - - /* - * If there are no neigh associated with the mac delete the mac - * else mark it as AUTO for forward reference - */ - if (!listcount(mac->neigh_list)) { - zvni_mac_del(zvni, mac); - } else { - UNSET_FLAG(mac->flags, ZEBRA_MAC_LOCAL); - SET_FLAG(mac->flags, ZEBRA_MAC_AUTO); - } - - return 0; + return zebra_evpn_del_local_mac(zevpn, macaddr, ifp); } /* @@ -7642,194 +4033,52 @@ int zebra_vxlan_local_mac_del(struct interface *ifp, struct interface *br_if, */ int zebra_vxlan_local_mac_add_update(struct interface *ifp, struct interface *br_if, - struct ethaddr *macaddr, vlanid_t vid, - bool sticky) -{ - zebra_vni_t *zvni; - zebra_mac_t *mac; - struct zebra_vrf *zvrf; - char buf[ETHER_ADDR_STRLEN]; - bool mac_sticky = false; - bool inform_client = false; - bool upd_neigh = false; - bool is_dup_detect = false; - struct in_addr vtep_ip = {.s_addr = 0}; - - /* We are interested in MACs only on ports or (port, VLAN) that - * map to a VNI. - */ - zvni = zvni_map_vlan(ifp, br_if, vid); - if (!zvni) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "\tAdd/Update %sMAC %s intf %s(%u) VID %u, could not find VNI", - sticky ? "sticky " : "", - prefix_mac2str(macaddr, buf, sizeof(buf)), - ifp->name, ifp->ifindex, vid); - return 0; - } - - if (!zvni->vxlan_if) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "\tVNI %u hash %p doesn't have intf upon local MAC ADD", - zvni->vni, zvni); - return -1; - } - - zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id); - if (!zvrf) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("\tNo Vrf found for vrf_id: %d", - zvni->vxlan_if->vrf_id); - return -1; - } - - /* Check if we need to create or update or it is a NO-OP. */ - mac = zvni_mac_lookup(zvni, macaddr); - if (!mac) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "ADD %sMAC %s intf %s(%u) VID %u -> VNI %u", - sticky ? "sticky " : "", - prefix_mac2str(macaddr, buf, sizeof(buf)), - ifp->name, ifp->ifindex, vid, zvni->vni); - - mac = zvni_mac_add(zvni, macaddr); - if (!mac) { - flog_err( - EC_ZEBRA_MAC_ADD_FAILED, - "Failed to add MAC %s intf %s(%u) VID %u VNI %u", - prefix_mac2str(macaddr, buf, sizeof(buf)), - ifp->name, ifp->ifindex, vid, zvni->vni); - return -1; - } - SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL); - mac->fwd_info.local.ifindex = ifp->ifindex; - mac->fwd_info.local.vid = vid; - if (sticky) - SET_FLAG(mac->flags, ZEBRA_MAC_STICKY); - inform_client = true; - - } else { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "UPD %sMAC %s intf %s(%u) VID %u -> VNI %u curFlags 0x%x", - sticky ? "sticky " : "", - prefix_mac2str(macaddr, buf, sizeof(buf)), - ifp->name, ifp->ifindex, vid, zvni->vni, - mac->flags); - - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) { - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY)) - mac_sticky = true; - - /* - * Update any changes and if changes are relevant to - * BGP, note it. - */ - if (mac_sticky == sticky - && mac->fwd_info.local.ifindex == ifp->ifindex - && mac->fwd_info.local.vid == vid) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "\tAdd/Update %sMAC %s intf %s(%u) VID %u -> VNI %u, " - "entry exists and has not changed ", - sticky ? "sticky " : "", - prefix_mac2str(macaddr, buf, - sizeof(buf)), - ifp->name, ifp->ifindex, vid, - zvni->vni); - return 0; - } - if (mac_sticky != sticky) { - if (sticky) - SET_FLAG(mac->flags, - ZEBRA_MAC_STICKY); - else - UNSET_FLAG(mac->flags, - ZEBRA_MAC_STICKY); - inform_client = true; - } - - memset(&mac->fwd_info, 0, sizeof(mac->fwd_info)); - mac->fwd_info.local.ifindex = ifp->ifindex; - mac->fwd_info.local.vid = vid; - - } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE) || - CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO)) { - bool do_dad = false; - - /* - * MAC has either moved or was "internally" created due - * to a neighbor learn and is now actually learnt. If - * it was learnt as a remote sticky MAC, this is an - * operator error. - */ - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY)) { - flog_warn( - EC_ZEBRA_STICKY_MAC_ALREADY_LEARNT, - "MAC %s already learnt as remote sticky MAC behind VTEP %s VNI %u", - prefix_mac2str(macaddr, buf, - sizeof(buf)), - inet_ntoa(mac->fwd_info.r_vtep_ip), - zvni->vni); - return 0; - } + struct ethaddr *macaddr, vlanid_t vid, + bool sticky, bool local_inactive, + bool dp_static) +{ + zebra_evpn_t *zevpn; + struct zebra_vrf *zvrf; + char buf[ETHER_ADDR_STRLEN]; - /* If an actual move, compute MAC's seq number */ - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { - mac->loc_seq = MAX(mac->rem_seq + 1, - mac->loc_seq); - vtep_ip = mac->fwd_info.r_vtep_ip; - /* Trigger DAD for remote MAC */ - do_dad = true; - } + assert(ifp); - UNSET_FLAG(mac->flags, ZEBRA_MAC_REMOTE); - UNSET_FLAG(mac->flags, ZEBRA_MAC_AUTO); - SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL); - memset(&mac->fwd_info, 0, sizeof(mac->fwd_info)); - mac->fwd_info.local.ifindex = ifp->ifindex; - mac->fwd_info.local.vid = vid; - if (sticky) - SET_FLAG(mac->flags, ZEBRA_MAC_STICKY); - else - UNSET_FLAG(mac->flags, ZEBRA_MAC_STICKY); - /* - * We have to inform BGP of this MAC as well as process - * all neighbors. - */ - inform_client = true; - upd_neigh = true; - - zebra_vxlan_dup_addr_detect_for_mac(zvrf, mac, vtep_ip, - do_dad, - &is_dup_detect, - true); - if (is_dup_detect) { - inform_client = false; - upd_neigh = false; - } - } + /* We are interested in MACs only on ports or (port, VLAN) that + * map to an EVPN. + */ + zevpn = zebra_evpn_map_vlan(ifp, br_if, vid); + if (!zevpn) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + " Add/Update %sMAC %s intf %s(%u) VID %u, could not find EVPN", + sticky ? "sticky " : "", + prefix_mac2str(macaddr, buf, sizeof(buf)), + ifp->name, ifp->ifindex, vid); + return 0; } - /* Inform BGP if required. */ - if (inform_client) { - if (zvni_mac_send_add_to_client(zvni->vni, macaddr, - mac->flags, mac->loc_seq)) - return -1; + if (!zevpn->vxlan_if) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + " VNI %u hash %p doesn't have intf upon local MAC ADD", + zevpn->vni, zevpn); + return -1; } - /* Process all neighbors associated with this MAC, if required. */ - if (upd_neigh) - zvni_process_neigh_on_local_mac_change(zvni, mac, 0); + zvrf = zebra_vrf_get_evpn(); + if (!zvrf) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug(" No Evpn Global Vrf found"); + return -1; + } - return 0; + return zebra_evpn_add_update_local_mac(zvrf, zevpn, ifp, macaddr, vid, + sticky, local_inactive, + dp_static); } /* - * Handle message from client to delete a remote VTEP for a VNI. + * Handle message from client to delete a remote VTEP for an EVPN. */ void zebra_vxlan_remote_vtep_del(ZAPI_HANDLER_ARGS) { @@ -7837,7 +4086,7 @@ void zebra_vxlan_remote_vtep_del(ZAPI_HANDLER_ARGS) unsigned short l = 0; vni_t vni; struct in_addr vtep_ip; - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; zebra_vtep_t *zvtep; struct interface *ifp; struct zebra_if *zif; @@ -7845,7 +4094,7 @@ void zebra_vxlan_remote_vtep_del(ZAPI_HANDLER_ARGS) if (!is_evpn_enabled()) { zlog_debug( "%s: EVPN is not enabled yet we have received a vtep del command", - __PRETTY_FUNCTION__); + __func__); return; } @@ -7858,33 +4107,38 @@ void zebra_vxlan_remote_vtep_del(ZAPI_HANDLER_ARGS) s = msg; while (l < hdr->length) { + int flood_control __attribute__((unused)); + /* Obtain each remote VTEP and process. */ STREAM_GETL(s, vni); l += 4; STREAM_GET(&vtep_ip.s_addr, s, IPV4_MAX_BYTELEN); l += IPV4_MAX_BYTELEN; + /* Flood control is intentionally ignored right now */ + STREAM_GETL(s, flood_control); + l += 4; + if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug("Recv VTEP_DEL %s VNI %u from %s", inet_ntoa(vtep_ip), vni, zebra_route_string(client->proto)); /* Locate VNI hash entry - expected to exist. */ - zvni = zvni_lookup(vni); - if (!zvni) { + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( - "Failed to locate VNI hash upon remote VTEP DEL, " - "VNI %u", + "Failed to locate VNI hash upon remote VTEP DEL, VNI %u", vni); continue; } - ifp = zvni->vxlan_if; + ifp = zevpn->vxlan_if; if (!ifp) { zlog_debug( "VNI %u hash %p doesn't have intf upon remote VTEP DEL", - zvni->vni, zvni); + zevpn->vni, zevpn); continue; } zif = ifp->info; @@ -7899,14 +4153,12 @@ void zebra_vxlan_remote_vtep_del(ZAPI_HANDLER_ARGS) * and * then, the VTEP entry itself and remove it. */ - zvtep = zvni_vtep_find(zvni, &vtep_ip); + zvtep = zebra_evpn_vtep_find(zevpn, &vtep_ip); if (!zvtep) continue; - zvni_neigh_del_from_vtep(zvni, 1, &vtep_ip); - zvni_mac_del_from_vtep(zvni, 1, &vtep_ip); - zvni_vtep_uninstall(zvni, &vtep_ip); - zvni_vtep_del(zvni, zvtep); + zebra_evpn_vtep_uninstall(zevpn, &vtep_ip); + zebra_evpn_vtep_del(zevpn, zvtep); } stream_failure: @@ -7914,7 +4166,7 @@ void zebra_vxlan_remote_vtep_del(ZAPI_HANDLER_ARGS) } /* - * Handle message from client to add a remote VTEP for a VNI. + * Handle message from client to add a remote VTEP for an EVPN. */ void zebra_vxlan_remote_vtep_add(ZAPI_HANDLER_ARGS) { @@ -7922,7 +4174,7 @@ void zebra_vxlan_remote_vtep_add(ZAPI_HANDLER_ARGS) unsigned short l = 0; vni_t vni; struct in_addr vtep_ip; - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; struct interface *ifp; struct zebra_if *zif; int flood_control; @@ -7931,7 +4183,7 @@ void zebra_vxlan_remote_vtep_add(ZAPI_HANDLER_ARGS) if (!is_evpn_enabled()) { zlog_debug( "%s: EVPN not enabled yet we received a vtep_add zapi call", - __PRETTY_FUNCTION__); + __func__); return; } @@ -7949,7 +4201,7 @@ void zebra_vxlan_remote_vtep_add(ZAPI_HANDLER_ARGS) l += 4; STREAM_GET(&vtep_ip.s_addr, s, IPV4_MAX_BYTELEN); STREAM_GETL(s, flood_control); - l += IPV4_MAX_BYTELEN; + l += IPV4_MAX_BYTELEN + 4; if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug("Recv VTEP_ADD %s VNI %u flood %d from %s", @@ -7957,21 +4209,21 @@ void zebra_vxlan_remote_vtep_add(ZAPI_HANDLER_ARGS) zebra_route_string(client->proto)); /* Locate VNI hash entry - expected to exist. */ - zvni = zvni_lookup(vni); - if (!zvni) { + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { flog_err( EC_ZEBRA_VTEP_ADD_FAILED, - "Failed to locate VNI hash upon remote VTEP ADD, VNI %u", + "Failed to locate EVPN hash upon remote VTEP ADD, VNI %u", vni); continue; } - ifp = zvni->vxlan_if; + ifp = zevpn->vxlan_if; if (!ifp) { flog_err( EC_ZEBRA_VTEP_ADD_FAILED, "VNI %u hash %p doesn't have intf upon remote VTEP ADD", - zvni->vni, zvni); + zevpn->vni, zevpn); continue; } @@ -7981,7 +4233,7 @@ void zebra_vxlan_remote_vtep_add(ZAPI_HANDLER_ARGS) if (!if_is_operative(ifp) || !zif->brslave_info.br_if) continue; - zvtep = zvni_vtep_find(zvni, &vtep_ip); + zvtep = zebra_evpn_vtep_find(zevpn, &vtep_ip); if (zvtep) { /* If the remote VTEP already exists check if * the flood mode has changed @@ -7993,18 +4245,20 @@ void zebra_vxlan_remote_vtep_add(ZAPI_HANDLER_ARGS) * is no longer; get rid of the HER fdb * entry installed before */ - zvni_vtep_uninstall(zvni, &vtep_ip); + zebra_evpn_vtep_uninstall(zevpn, + &vtep_ip); zvtep->flood_control = flood_control; - zvni_vtep_install(zvni, zvtep); + zebra_evpn_vtep_install(zevpn, zvtep); } } else { - zvtep = zvni_vtep_add(zvni, &vtep_ip, flood_control); + zvtep = zebra_evpn_vtep_add(zevpn, &vtep_ip, + flood_control); if (zvtep) - zvni_vtep_install(zvni, zvtep); + zebra_evpn_vtep_install(zevpn, zvtep); else flog_err(EC_ZEBRA_VTEP_ADD_FAILED, - "Failed to add remote VTEP, VNI %u zvni %p", - vni, zvni); + "Failed to add remote VTEP, VNI %u zevpn %p", + vni, zevpn); } } @@ -8025,7 +4279,7 @@ int zebra_vxlan_add_del_gw_macip(struct interface *ifp, struct prefix *p, { struct ipaddr ip; struct ethaddr macaddr; - zebra_vni_t *zvni = NULL; + zebra_evpn_t *zevpn = NULL; memset(&ip, 0, sizeof(struct ipaddr)); memset(&macaddr, 0, sizeof(struct ethaddr)); @@ -8069,14 +4323,15 @@ int zebra_vxlan_add_del_gw_macip(struct interface *ifp, struct prefix *p, svi_if_link = if_lookup_by_index_per_ns( zebra_ns_lookup(NS_DEFAULT), svi_if_zif->link_ifindex); - zvni = zvni_from_svi(svi_if, svi_if_link); + zevpn = zebra_evpn_from_svi(svi_if, + svi_if_link); } } else if (IS_ZEBRA_IF_BRIDGE(svi_if)) { /* * If it is a vlan unaware bridge then svi is the bridge * itself */ - zvni = zvni_from_svi(svi_if, svi_if); + zevpn = zebra_evpn_from_svi(svi_if, svi_if); } } else if (IS_ZEBRA_IF_VLAN(ifp)) { struct zebra_if *svi_if_zif = @@ -8090,18 +4345,18 @@ int zebra_vxlan_add_del_gw_macip(struct interface *ifp, struct prefix *p, zebra_ns_lookup(NS_DEFAULT), svi_if_zif->link_ifindex); if (svi_if_link) - zvni = zvni_from_svi(ifp, svi_if_link); + zevpn = zebra_evpn_from_svi(ifp, svi_if_link); } } else if (IS_ZEBRA_IF_BRIDGE(ifp)) { - zvni = zvni_from_svi(ifp, ifp); + zevpn = zebra_evpn_from_svi(ifp, ifp); } - if (!zvni) + if (!zevpn) return 0; - if (!zvni->vxlan_if) { + if (!zevpn->vxlan_if) { zlog_debug("VNI %u hash %p doesn't have intf upon MACVLAN up", - zvni->vni, zvni); + zevpn->vni, zevpn); return -1; } @@ -8120,9 +4375,9 @@ int zebra_vxlan_add_del_gw_macip(struct interface *ifp, struct prefix *p, if (add) - zvni_gw_macip_add(ifp, zvni, &macaddr, &ip); + zebra_evpn_gw_macip_add(ifp, zevpn, &macaddr, &ip); else - zvni_gw_macip_del(ifp, zvni, &ip); + zebra_evpn_gw_macip_del(ifp, zevpn, &ip); return 0; } @@ -8132,7 +4387,7 @@ int zebra_vxlan_add_del_gw_macip(struct interface *ifp, struct prefix *p, * SVI can be associated to either L3-VNI or L2-VNI. * For L2-VNI: At this point, this is a NOP since * the kernel deletes the neighbor entries on this SVI (if any). - * We only need to update the vrf corresponding to zvni. + * We only need to update the vrf corresponding to zevpn. * For L3-VNI: L3-VNI is operationally down, update mac-ip routes and delete * from bgp */ @@ -8149,17 +4404,17 @@ int zebra_vxlan_svi_down(struct interface *ifp, struct interface *link_if) /* remove association with svi-if */ zl3vni->svi_if = NULL; } else { - zebra_vni_t *zvni = NULL; + zebra_evpn_t *zevpn = NULL; - /* since we dont have svi corresponding to zvni, we associate it + /* since we dont have svi corresponding to zevpn, we associate it * to default vrf. Note: the corresponding neigh entries on the * SVI would have already been deleted */ - zvni = zvni_from_svi(ifp, link_if); - if (zvni) { - zvni->vrf_id = VRF_DEFAULT; + zevpn = zebra_evpn_from_svi(ifp, link_if); + if (zevpn) { + zevpn->vrf_id = VRF_DEFAULT; /* update the tenant vrf in BGP */ - zvni_send_add_to_client(zvni); + zebra_evpn_send_add_to_client(zevpn); } } return 0; @@ -8175,7 +4430,7 @@ int zebra_vxlan_svi_down(struct interface *ifp, struct interface *link_if) */ int zebra_vxlan_svi_up(struct interface *ifp, struct interface *link_if) { - zebra_vni_t *zvni = NULL; + zebra_evpn_t *zevpn = NULL; zebra_l3vni_t *zl3vni = NULL; zl3vni = zl3vni_from_svi(ifp, link_if); @@ -8192,37 +4447,108 @@ int zebra_vxlan_svi_up(struct interface *ifp, struct interface *link_if) /* process SVI up for l2-vni */ struct neigh_walk_ctx n_wctx; - zvni = zvni_from_svi(ifp, link_if); - if (!zvni) + zevpn = zebra_evpn_from_svi(ifp, link_if); + if (!zevpn) return 0; - if (!zvni->vxlan_if) { + if (!zevpn->vxlan_if) { zlog_debug( "VNI %u hash %p doesn't have intf upon SVI up", - zvni->vni, zvni); + zevpn->vni, zevpn); return -1; } if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( "SVI %s(%u) VNI %u VRF %s is UP, installing neighbors", - ifp->name, ifp->ifindex, zvni->vni, + ifp->name, ifp->ifindex, zevpn->vni, vrf_id_to_name(ifp->vrf_id)); /* update the vrf information for l2-vni and inform bgp */ - zvni->vrf_id = ifp->vrf_id; - zvni_send_add_to_client(zvni); + zevpn->vrf_id = ifp->vrf_id; + zebra_evpn_send_add_to_client(zevpn); /* Install any remote neighbors for this VNI. */ memset(&n_wctx, 0, sizeof(struct neigh_walk_ctx)); - n_wctx.zvni = zvni; - hash_iterate(zvni->neigh_table, zvni_install_neigh_hash, + n_wctx.zevpn = zevpn; + hash_iterate(zevpn->neigh_table, zebra_evpn_install_neigh_hash, &n_wctx); } return 0; } +/* + * Handle MAC-VLAN interface going down. + * L3VNI: When MAC-VLAN interface goes down, + * find its associated SVI and update type2/type-5 routes + * with SVI as RMAC + */ +void zebra_vxlan_macvlan_down(struct interface *ifp) +{ + zebra_l3vni_t *zl3vni = NULL; + struct zebra_if *zif, *link_zif; + struct interface *link_ifp, *link_if; + + zif = ifp->info; + assert(zif); + link_ifp = zif->link; + if (!link_ifp) { + if (IS_ZEBRA_DEBUG_VXLAN) { + struct interface *ifp; + + ifp = if_lookup_by_index_all_vrf(zif->link_ifindex); + zlog_debug("macvlan parent link is not found. Parent index %d ifp %s", + zif->link_ifindex, ifp ? ifp->name : " "); + } + return; + } + link_zif = link_ifp->info; + assert(link_zif); + + link_if = if_lookup_by_index_per_ns(zebra_ns_lookup(NS_DEFAULT), + link_zif->link_ifindex); + + zl3vni = zl3vni_from_svi(link_ifp, link_if); + if (zl3vni) { + zl3vni->mac_vlan_if = NULL; + if (is_l3vni_oper_up(zl3vni)) + zebra_vxlan_process_l3vni_oper_up(zl3vni); + } +} + +/* + * Handle MAC-VLAN interface going up. + * L3VNI: When MAC-VLAN interface comes up, + * find its associated SVI and update type-2 routes + * with MAC-VLAN's MAC as RMAC and for type-5 routes + * use SVI's MAC as RMAC. + */ +void zebra_vxlan_macvlan_up(struct interface *ifp) +{ + zebra_l3vni_t *zl3vni = NULL; + struct zebra_if *zif, *link_zif; + struct interface *link_ifp, *link_if; + + zif = ifp->info; + assert(zif); + link_ifp = zif->link; + link_zif = link_ifp->info; + assert(link_zif); + + link_if = if_lookup_by_index_per_ns(zebra_ns_lookup(NS_DEFAULT), + link_zif->link_ifindex); + zl3vni = zl3vni_from_svi(link_ifp, link_if); + if (zl3vni) { + /* associate with macvlan (VRR) interface */ + zl3vni->mac_vlan_if = ifp; + + /* process oper-up */ + if (is_l3vni_oper_up(zl3vni)) + zebra_vxlan_process_l3vni_oper_up(zl3vni); + } +} + /* * Handle VxLAN interface down */ @@ -8232,7 +4558,7 @@ int zebra_vxlan_if_down(struct interface *ifp) struct zebra_if *zif = NULL; struct zebra_l2info_vxlan *vxl = NULL; zebra_l3vni_t *zl3vni = NULL; - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; /* Check if EVPN is enabled. */ if (!is_evpn_enabled()) @@ -8258,25 +4584,30 @@ int zebra_vxlan_if_down(struct interface *ifp) ifp->ifindex, vni); /* Locate hash entry; it is expected to exist. */ - zvni = zvni_lookup(vni); - if (!zvni) { + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { zlog_debug( "Failed to locate VNI hash at DOWN, IF %s(%u) VNI %u", ifp->name, ifp->ifindex, vni); return -1; } - assert(zvni->vxlan_if == 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. */ - zvni_send_del_to_client(zvni->vni); + zebra_evpn_send_del_to_client(zevpn); /* Free up all neighbors and MACs, if any. */ - zvni_neigh_del_all(zvni, 1, 0, DEL_ALL_NEIGH); - zvni_mac_del_all(zvni, 1, 0, DEL_ALL_MAC); + zebra_evpn_neigh_del_all(zevpn, 1, 0, DEL_ALL_NEIGH); + zebra_evpn_mac_del_all(zevpn, 1, 0, DEL_ALL_MAC); /* Free up all remote VTEPs, if any. */ - zvni_vtep_del_all(zvni, 1); + zebra_evpn_vtep_del_all(zevpn, 1); } return 0; } @@ -8289,7 +4620,7 @@ int zebra_vxlan_if_up(struct interface *ifp) vni_t vni; struct zebra_if *zif = NULL; struct zebra_l2info_vxlan *vxl = NULL; - zebra_vni_t *zvni = NULL; + zebra_evpn_t *zevpn = NULL; zebra_l3vni_t *zl3vni = NULL; /* Check if EVPN is enabled. */ @@ -8303,15 +4634,18 @@ int zebra_vxlan_if_up(struct interface *ifp) zl3vni = zl3vni_lookup(vni); if (zl3vni) { - - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Intf %s(%u) L3-VNI %u is UP", ifp->name, - ifp->ifindex, vni); - /* we need to associate with SVI, if any, we can associate with * svi-if only after association with vxlan-intf is complete */ zl3vni->svi_if = zl3vni_map_to_svi_if(zl3vni); + zl3vni->mac_vlan_if = zl3vni_map_to_mac_vlan_if(zl3vni); + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Intf %s(%u) L3-VNI %u is UP svi_if %s mac_vlan_if %s" + , ifp->name, ifp->ifindex, vni, + zl3vni->svi_if ? zl3vni->svi_if->name : "NIL", + zl3vni->mac_vlan_if ? + zl3vni->mac_vlan_if->name : "NIL"); if (is_l3vni_oper_up(zl3vni)) zebra_vxlan_process_l3vni_oper_up(zl3vni); @@ -8324,29 +4658,29 @@ int zebra_vxlan_if_up(struct interface *ifp) ifp->ifindex, vni); /* Locate hash entry; it is expected to exist. */ - zvni = zvni_lookup(vni); - if (!zvni) { + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { zlog_debug( - "Failed to locate VNI hash at UP, IF %s(%u) VNI %u", + "Failed to locate EVPN hash at UP, IF %s(%u) VNI %u", ifp->name, ifp->ifindex, vni); return -1; } - assert(zvni->vxlan_if == ifp); + assert(zevpn->vxlan_if == ifp); vlan_if = zvni_map_to_svi(vxl->access_vlan, zif->brslave_info.br_if); if (vlan_if) { - zvni->vrf_id = vlan_if->vrf_id; + zevpn->vrf_id = vlan_if->vrf_id; zl3vni = zl3vni_from_vrf(vlan_if->vrf_id); if (zl3vni) - listnode_add_sort(zl3vni->l2vnis, zvni); + listnode_add_sort_nodup(zl3vni->l2vnis, zevpn); } /* If part of a bridge, inform BGP about this VNI. */ /* Also, read and populate local MACs and neighbors. */ if (zif->brslave_info.br_if) { - zvni_send_add_to_client(zvni); - zvni_read_mac_neigh(zvni, ifp); + zebra_evpn_send_add_to_client(zevpn); + zebra_evpn_read_mac_neigh(zevpn, ifp); } } @@ -8362,7 +4696,7 @@ int zebra_vxlan_if_del(struct interface *ifp) vni_t vni; struct zebra_if *zif = NULL; struct zebra_l2info_vxlan *vxl = NULL; - zebra_vni_t *zvni = NULL; + zebra_evpn_t *zevpn = NULL; zebra_l3vni_t *zl3vni = NULL; /* Check if EVPN is enabled. */ @@ -8395,8 +4729,8 @@ int zebra_vxlan_if_del(struct interface *ifp) ifp->ifindex); /* Locate hash entry; it is expected to exist. */ - zvni = zvni_lookup(vni); - if (!zvni) { + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { zlog_debug( "Failed to locate VNI hash at del, IF %s(%u) VNI %u", ifp->name, ifp->ifindex, vni); @@ -8404,25 +4738,24 @@ int zebra_vxlan_if_del(struct interface *ifp) } /* remove from l3-vni list */ - zl3vni = zl3vni_from_vrf(zvni->vrf_id); + zl3vni = zl3vni_from_vrf(zevpn->vrf_id); if (zl3vni) - listnode_delete(zl3vni->l2vnis, zvni); - + listnode_delete(zl3vni->l2vnis, zevpn); /* Delete VNI from BGP. */ - zvni_send_del_to_client(zvni->vni); + zebra_evpn_send_del_to_client(zevpn); /* Free up all neighbors and MAC, if any. */ - zvni_neigh_del_all(zvni, 0, 0, DEL_ALL_NEIGH); - zvni_mac_del_all(zvni, 0, 0, DEL_ALL_MAC); + zebra_evpn_neigh_del_all(zevpn, 0, 0, DEL_ALL_NEIGH); + zebra_evpn_mac_del_all(zevpn, 0, 0, DEL_ALL_MAC); /* Free up all remote VTEPs, if any. */ - zvni_vtep_del_all(zvni, 0); + zebra_evpn_vtep_del_all(zevpn, 0); /* Delete the hash entry. */ - if (zvni_del(zvni)) { + if (zebra_evpn_vxlan_del(zevpn)) { flog_err(EC_ZEBRA_VNI_DEL_FAILED, - "Failed to del VNI hash %p, IF %s(%u) VNI %u", - zvni, ifp->name, ifp->ifindex, zvni->vni); + "Failed to del EVPN hash %p, IF %s(%u) VNI %u", + zevpn, ifp->name, ifp->ifindex, zevpn->vni); return -1; } } @@ -8437,7 +4770,7 @@ int zebra_vxlan_if_update(struct interface *ifp, uint16_t chgflags) vni_t vni; struct zebra_if *zif = NULL; struct zebra_l2info_vxlan *vxl = NULL; - zebra_vni_t *zvni = NULL; + zebra_evpn_t *zevpn = NULL; zebra_l3vni_t *zl3vni = NULL; /* Check if EVPN is enabled. */ @@ -8474,6 +4807,8 @@ int zebra_vxlan_if_update(struct interface *ifp, uint16_t chgflags) zebra_vxlan_process_l3vni_oper_down(zl3vni); zl3vni->svi_if = NULL; zl3vni->svi_if = zl3vni_map_to_svi_if(zl3vni); + zl3vni->mac_vlan_if = + zl3vni_map_to_mac_vlan_if(zl3vni); zl3vni->local_vtep_ip = vxl->vtep_ip; if (is_l3vni_oper_up(zl3vni)) zebra_vxlan_process_l3vni_oper_up( @@ -8506,10 +4841,10 @@ int zebra_vxlan_if_update(struct interface *ifp, uint16_t chgflags) } else { /* Update VNI hash. */ - zvni = zvni_lookup(vni); - if (!zvni) { + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { zlog_debug( - "Failed to find L2-VNI hash on update, IF %s(%u) VNI %u", + "Failed to find EVPN hash on update, IF %s(%u) VNI %u", ifp->name, ifp->ifindex, vni); return -1; } @@ -8526,10 +4861,10 @@ int zebra_vxlan_if_update(struct interface *ifp, uint16_t chgflags) && (zif->brslave_info.bridge_ifindex == IFINDEX_INTERNAL)) { /* Delete from client, remove all remote VTEPs */ /* Also, free up all MACs and neighbors. */ - zvni_send_del_to_client(zvni->vni); - zvni_neigh_del_all(zvni, 1, 0, DEL_ALL_NEIGH); - zvni_mac_del_all(zvni, 1, 0, DEL_ALL_MAC); - zvni_vtep_del_all(zvni, 1); + zebra_evpn_send_del_to_client(zevpn); + zebra_evpn_neigh_del_all(zevpn, 1, 0, DEL_ALL_NEIGH); + zebra_evpn_mac_del_all(zevpn, 1, 0, DEL_ALL_MAC); + zebra_evpn_vtep_del_all(zevpn, 1); return 0; } @@ -8538,20 +4873,23 @@ int zebra_vxlan_if_update(struct interface *ifp, uint16_t chgflags) /* Remove all existing local neigh and MACs for this VNI * (including from BGP) */ - zvni_neigh_del_all(zvni, 0, 1, DEL_LOCAL_MAC); - zvni_mac_del_all(zvni, 0, 1, DEL_LOCAL_MAC); + zebra_evpn_neigh_del_all(zevpn, 0, 1, DEL_LOCAL_MAC); + zebra_evpn_mac_del_all(zevpn, 0, 1, DEL_LOCAL_MAC); } - if (zvni->local_vtep_ip.s_addr != vxl->vtep_ip.s_addr || - zvni->mcast_grp.s_addr != vxl->mcast_grp.s_addr) { - zebra_vxlan_sg_deref(zvni->local_vtep_ip, - zvni->mcast_grp); + if (zevpn->local_vtep_ip.s_addr != vxl->vtep_ip.s_addr || + zevpn->mcast_grp.s_addr != vxl->mcast_grp.s_addr) { + zebra_vxlan_sg_deref(zevpn->local_vtep_ip, + zevpn->mcast_grp); zebra_vxlan_sg_ref(vxl->vtep_ip, vxl->mcast_grp); - zvni->local_vtep_ip = vxl->vtep_ip; - zvni->mcast_grp = vxl->mcast_grp; + zevpn->local_vtep_ip = vxl->vtep_ip; + zevpn->mcast_grp = vxl->mcast_grp; + /* on local vtep-ip check if ES orig-ip + * needs to be updated + */ + zebra_evpn_es_set_base_evpn(zevpn); } - zvni->vxlan_if = ifp; - + zevpn_vxlan_if_set(zevpn, ifp, true /* set */); /* Take further actions needed. * Note that if we are here, there is a change of interest. */ @@ -8564,7 +4902,7 @@ int zebra_vxlan_if_update(struct interface *ifp, uint16_t chgflags) & (ZEBRA_VXLIF_MASTER_CHANGE | ZEBRA_VXLIF_LOCAL_IP_CHANGE | ZEBRA_VXLIF_MCAST_GRP_CHANGE)) - zvni_send_add_to_client(zvni); + zebra_evpn_send_add_to_client(zevpn); /* If there is a valid new master or a VLAN mapping change, * read and populate local MACs and neighbors. @@ -8572,22 +4910,22 @@ int zebra_vxlan_if_update(struct interface *ifp, uint16_t chgflags) * for this VNI (based on new VLAN). */ if (chgflags & ZEBRA_VXLIF_MASTER_CHANGE) - zvni_read_mac_neigh(zvni, ifp); + zebra_evpn_read_mac_neigh(zevpn, ifp); else if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE) { struct mac_walk_ctx m_wctx; struct neigh_walk_ctx n_wctx; - zvni_read_mac_neigh(zvni, ifp); + zebra_evpn_read_mac_neigh(zevpn, ifp); memset(&m_wctx, 0, sizeof(struct mac_walk_ctx)); - m_wctx.zvni = zvni; - hash_iterate(zvni->mac_table, zvni_install_mac_hash, - &m_wctx); + m_wctx.zevpn = zevpn; + hash_iterate(zevpn->mac_table, + zebra_evpn_install_mac_hash, &m_wctx); memset(&n_wctx, 0, sizeof(struct neigh_walk_ctx)); - n_wctx.zvni = zvni; - hash_iterate(zvni->neigh_table, zvni_install_neigh_hash, - &n_wctx); + n_wctx.zevpn = zevpn; + hash_iterate(zevpn->neigh_table, + zebra_evpn_install_neigh_hash, &n_wctx); } } @@ -8602,7 +4940,7 @@ int zebra_vxlan_if_add(struct interface *ifp) vni_t vni; struct zebra_if *zif = NULL; struct zebra_l2info_vxlan *vxl = NULL; - zebra_vni_t *zvni = NULL; + zebra_evpn_t *zevpn = NULL; zebra_l3vni_t *zl3vni = NULL; /* Check if EVPN is enabled. */ @@ -8633,6 +4971,8 @@ int zebra_vxlan_if_add(struct interface *ifp) * after association with vxlan_if is complete */ zl3vni->svi_if = zl3vni_map_to_svi_if(zl3vni); + zl3vni->mac_vlan_if = zl3vni_map_to_mac_vlan_if(zl3vni); + if (is_l3vni_oper_up(zl3vni)) zebra_vxlan_process_l3vni_oper_up(zl3vni); } else { @@ -8640,35 +4980,39 @@ int zebra_vxlan_if_add(struct interface *ifp) /* process if-add for l2-vni */ struct interface *vlan_if = NULL; - /* Create or update VNI hash. */ - zvni = zvni_lookup(vni); - if (!zvni) { - zvni = zvni_add(vni); - if (!zvni) { + /* Create or update EVPN hash. */ + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { + zevpn = zebra_evpn_add(vni); + if (!zevpn) { flog_err( EC_ZEBRA_VNI_ADD_FAILED, - "Failed to add VNI hash, IF %s(%u) VNI %u", + "Failed to add EVPN hash, IF %s(%u) VNI %u", ifp->name, ifp->ifindex, vni); return -1; } } - if (zvni->local_vtep_ip.s_addr != vxl->vtep_ip.s_addr || - zvni->mcast_grp.s_addr != vxl->mcast_grp.s_addr) { - zebra_vxlan_sg_deref(zvni->local_vtep_ip, - zvni->mcast_grp); + if (zevpn->local_vtep_ip.s_addr != vxl->vtep_ip.s_addr || + zevpn->mcast_grp.s_addr != vxl->mcast_grp.s_addr) { + zebra_vxlan_sg_deref(zevpn->local_vtep_ip, + zevpn->mcast_grp); zebra_vxlan_sg_ref(vxl->vtep_ip, vxl->mcast_grp); - zvni->local_vtep_ip = vxl->vtep_ip; - zvni->mcast_grp = vxl->mcast_grp; + zevpn->local_vtep_ip = vxl->vtep_ip; + zevpn->mcast_grp = vxl->mcast_grp; + /* on local vtep-ip check if ES orig-ip + * needs to be updated + */ + zebra_evpn_es_set_base_evpn(zevpn); } - zvni->vxlan_if = ifp; + zevpn_vxlan_if_set(zevpn, ifp, true /* set */); vlan_if = zvni_map_to_svi(vxl->access_vlan, zif->brslave_info.br_if); if (vlan_if) { - zvni->vrf_id = vlan_if->vrf_id; + zevpn->vrf_id = vlan_if->vrf_id; zl3vni = zl3vni_from_vrf(vlan_if->vrf_id); if (zl3vni) - listnode_add_sort(zl3vni->l2vnis, zvni); + listnode_add_sort_nodup(zl3vni->l2vnis, zevpn); } if (IS_ZEBRA_DEBUG_VXLAN) { @@ -8695,10 +5039,10 @@ int zebra_vxlan_if_add(struct interface *ifp) return 0; /* Inform BGP */ - zvni_send_add_to_client(zvni); + zebra_evpn_send_add_to_client(zevpn); /* Read and populate local MACs and neighbors */ - zvni_read_mac_neigh(zvni, ifp); + zebra_evpn_read_mac_neigh(zevpn, ifp); } return 0; @@ -8765,8 +5109,18 @@ int zebra_vxlan_process_vrf_vni_cmd(struct zebra_vrf *zvrf, vni_t vni, */ zl3vni->svi_if = zl3vni_map_to_svi_if(zl3vni); + zl3vni->mac_vlan_if = zl3vni_map_to_mac_vlan_if(zl3vni); + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%s: l3vni %u svi_if %s mac_vlan_if %s", + __func__, vni, + zl3vni->svi_if ? zl3vni->svi_if->name : "NIL", + zl3vni->mac_vlan_if ? zl3vni->mac_vlan_if->name + : "NIL"); + /* formulate l2vni list */ - hash_iterate(zvrf_evpn->vni_table, zvni_add_to_l3vni_list, + hash_iterate(zvrf_evpn->evpn_table, zevpn_add_to_l3vni_list, zl3vni); if (is_l3vni_oper_up(zl3vni)) @@ -8834,8 +5188,15 @@ int zebra_vxlan_vrf_disable(struct zebra_vrf *zvrf) if (!zl3vni) return 0; - zl3vni->vrf_id = VRF_UNKNOWN; zebra_vxlan_process_l3vni_oper_down(zl3vni); + + /* delete and uninstall all rmacs */ + hash_iterate(zl3vni->rmac_table, zl3vni_del_rmac_hash_entry, zl3vni); + /* delete and uninstall all next-hops */ + hash_iterate(zl3vni->nh_table, zl3vni_del_nh_hash_entry, zl3vni); + + zl3vni->vrf_id = VRF_UNKNOWN; + return 0; } @@ -8889,7 +5250,7 @@ void zebra_vxlan_flood_control(ZAPI_HANDLER_ARGS) /* Install or uninstall flood entries corresponding to * remote VTEPs. */ - hash_iterate(zvrf->vni_table, zvni_handle_flooding_remote_vteps, + hash_iterate(zvrf->evpn_table, zebra_evpn_handle_flooding_remote_vteps, zvrf); stream_failure: @@ -8905,11 +5266,11 @@ void zebra_vxlan_advertise_svi_macip(ZAPI_HANDLER_ARGS) struct stream *s; int advertise; vni_t vni = 0; - zebra_vni_t *zvni = NULL; + zebra_evpn_t *zevpn = NULL; struct interface *ifp = NULL; if (!EVPN_ENABLED(zvrf)) { - zlog_debug("EVPN GW-MACIP Adv for non-EVPN VRF %u", + zlog_debug("EVPN SVI-MACIP Adv for non-EVPN VRF %u", zvrf_id(zvrf)); return; } @@ -8920,9 +5281,9 @@ void zebra_vxlan_advertise_svi_macip(ZAPI_HANDLER_ARGS) if (!vni) { if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("EVPN gateway macip Adv %s, currently %s", + zlog_debug("EVPN SVI-MACIP Adv %s, currently %s", advertise ? "enabled" : "disabled", - advertise_gw_macip_enabled(NULL) + advertise_svi_macip_enabled(NULL) ? "enabled" : "disabled"); @@ -8932,11 +5293,13 @@ void zebra_vxlan_advertise_svi_macip(ZAPI_HANDLER_ARGS) if (advertise) { zvrf->advertise_svi_macip = advertise; - hash_iterate(zvrf->vni_table, - zvni_gw_macip_add_for_vni_hash, NULL); + hash_iterate(zvrf->evpn_table, + zebra_evpn_gw_macip_add_for_evpn_hash, + NULL); } else { - hash_iterate(zvrf->vni_table, - zvni_svi_macip_del_for_vni_hash, NULL); + hash_iterate(zvrf->evpn_table, + zebra_evpn_svi_macip_del_for_evpn_hash, + NULL); zvrf->advertise_svi_macip = advertise; } @@ -8945,22 +5308,27 @@ void zebra_vxlan_advertise_svi_macip(ZAPI_HANDLER_ARGS) struct zebra_l2info_vxlan zl2_info; struct interface *vlan_if = NULL; - zvni = zvni_lookup(vni); - if (!zvni) + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) return; if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( "EVPN SVI macip Adv %s on VNI %d , currently %s", advertise ? "enabled" : "disabled", vni, - advertise_svi_macip_enabled(zvni) + advertise_svi_macip_enabled(zevpn) ? "enabled" : "disabled"); - if (zvni->advertise_svi_macip == advertise) + if (zevpn->advertise_svi_macip == advertise) return; - ifp = zvni->vxlan_if; + /* Store flag even though SVI is not present. + * Once SVI comes up triggers self MAC-IP route add. + */ + zevpn->advertise_svi_macip = advertise; + + ifp = zevpn->vxlan_if; if (!ifp) return; @@ -8971,20 +5339,17 @@ void zebra_vxlan_advertise_svi_macip(ZAPI_HANDLER_ARGS) return; zl2_info = zif->l2info.vxl; - vlan_if = zvni_map_to_svi(zl2_info.access_vlan, zif->brslave_info.br_if); if (!vlan_if) return; if (advertise) { - zvni->advertise_svi_macip = advertise; /* Add primary SVI MAC-IP */ - zvni_add_macip_for_intf(vlan_if, zvni); + zebra_evpn_add_macip_for_intf(vlan_if, zevpn); } else { - /* Del primary MAC-IP */ - zvni_del_macip_for_intf(vlan_if, zvni); - zvni->advertise_svi_macip = advertise; + /* Del primary SVI MAC-IP */ + zebra_evpn_del_macip_for_intf(vlan_if, zevpn); } } @@ -9001,7 +5366,7 @@ void zebra_vxlan_advertise_subnet(ZAPI_HANDLER_ARGS) struct stream *s; int advertise; vni_t vni = 0; - zebra_vni_t *zvni = NULL; + zebra_evpn_t *zevpn = NULL; struct interface *ifp = NULL; struct zebra_if *zif = NULL; struct zebra_l2info_vxlan zl2_info; @@ -9015,24 +5380,24 @@ void zebra_vxlan_advertise_subnet(ZAPI_HANDLER_ARGS) s = msg; STREAM_GETC(s, advertise); - vni = stream_get3(s); + STREAM_GET(&vni, s, 3); - zvni = zvni_lookup(vni); - if (!zvni) + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) return; - if (zvni->advertise_subnet == advertise) + if (zevpn->advertise_subnet == advertise) return; if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug("EVPN subnet Adv %s on VNI %d , currently %s", advertise ? "enabled" : "disabled", vni, - zvni->advertise_subnet ? "enabled" : "disabled"); + zevpn->advertise_subnet ? "enabled" : "disabled"); - zvni->advertise_subnet = advertise; + zevpn->advertise_subnet = advertise; - ifp = zvni->vxlan_if; + ifp = zevpn->vxlan_if; if (!ifp) return; @@ -9049,10 +5414,10 @@ void zebra_vxlan_advertise_subnet(ZAPI_HANDLER_ARGS) if (!vlan_if) return; - if (zvni->advertise_subnet) - zvni_advertise_subnet(zvni, vlan_if, 1); + if (zevpn->advertise_subnet) + zebra_evpn_advertise_subnet(zevpn, vlan_if, 1); else - zvni_advertise_subnet(zvni, vlan_if, 0); + zebra_evpn_advertise_subnet(zevpn, vlan_if, 0); stream_failure: return; @@ -9067,7 +5432,7 @@ void zebra_vxlan_advertise_gw_macip(ZAPI_HANDLER_ARGS) struct stream *s; int advertise; vni_t vni = 0; - zebra_vni_t *zvni = NULL; + zebra_evpn_t *zevpn = NULL; struct interface *ifp = NULL; if (!EVPN_ENABLED(zvrf)) { @@ -9093,12 +5458,14 @@ void zebra_vxlan_advertise_gw_macip(ZAPI_HANDLER_ARGS) zvrf->advertise_gw_macip = advertise; - if (advertise_gw_macip_enabled(zvni)) - hash_iterate(zvrf->vni_table, - zvni_gw_macip_add_for_vni_hash, NULL); + if (advertise_gw_macip_enabled(zevpn)) + hash_iterate(zvrf->evpn_table, + zebra_evpn_gw_macip_add_for_evpn_hash, + NULL); else - hash_iterate(zvrf->vni_table, - zvni_gw_macip_del_for_vni_hash, NULL); + hash_iterate(zvrf->evpn_table, + zebra_evpn_gw_macip_del_for_evpn_hash, + NULL); } else { struct zebra_if *zif = NULL; @@ -9106,23 +5473,23 @@ void zebra_vxlan_advertise_gw_macip(ZAPI_HANDLER_ARGS) struct interface *vlan_if = NULL; struct interface *vrr_if = NULL; - zvni = zvni_lookup(vni); - if (!zvni) + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) return; if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( "EVPN gateway macip Adv %s on VNI %d , currently %s", advertise ? "enabled" : "disabled", vni, - advertise_gw_macip_enabled(zvni) ? "enabled" + advertise_gw_macip_enabled(zevpn) ? "enabled" : "disabled"); - if (zvni->advertise_gw_macip == advertise) + if (zevpn->advertise_gw_macip == advertise) return; - zvni->advertise_gw_macip = advertise; + zevpn->advertise_gw_macip = advertise; - ifp = zvni->vxlan_if; + ifp = zevpn->vxlan_if; if (!ifp) return; @@ -9139,22 +5506,22 @@ void zebra_vxlan_advertise_gw_macip(ZAPI_HANDLER_ARGS) if (!vlan_if) return; - if (advertise_gw_macip_enabled(zvni)) { + if (advertise_gw_macip_enabled(zevpn)) { /* Add primary SVI MAC-IP */ - zvni_add_macip_for_intf(vlan_if, zvni); + zebra_evpn_add_macip_for_intf(vlan_if, zevpn); /* Add VRR MAC-IP - if any*/ vrr_if = zebra_get_vrr_intf_for_svi(vlan_if); if (vrr_if) - zvni_add_macip_for_intf(vrr_if, zvni); + zebra_evpn_add_macip_for_intf(vrr_if, zevpn); } else { /* Del primary MAC-IP */ - zvni_del_macip_for_intf(vlan_if, zvni); + zebra_evpn_del_macip_for_intf(vlan_if, zevpn); /* Del VRR MAC-IP - if any*/ vrr_if = zebra_get_vrr_intf_for_svi(vlan_if); if (vrr_if) - zvni_del_macip_for_intf(vrr_if, zvni); + zebra_evpn_del_macip_for_intf(vrr_if, zevpn); } } @@ -9162,6 +5529,25 @@ void zebra_vxlan_advertise_gw_macip(ZAPI_HANDLER_ARGS) return; } +static int macfdb_read_ns(struct ns *ns, + void *_in_param __attribute__((unused)), + void **out_param __attribute__((unused))) +{ + struct zebra_ns *zns = ns->info; + + macfdb_read(zns); + return NS_WALK_CONTINUE; +} + +static int neigh_read_ns(struct ns *ns, + void *_in_param __attribute__((unused)), + void **out_param __attribute__((unused))) +{ + struct zebra_ns *zns = ns->info; + + neigh_read(zns); + return NS_WALK_CONTINUE; +} /* * Handle message from client to learn (or stop learning) about VNIs and MACs. @@ -9203,23 +5589,30 @@ void zebra_vxlan_advertise_all_vni(ZAPI_HANDLER_ARGS) /* Note BUM handling */ zvrf->vxlan_flood_ctrl = flood_ctrl; - /* Build VNI hash table and inform BGP. */ - zvni_build_hash_table(); + /* Replay all ESs */ + zebra_evpn_es_send_all_to_client(true /* add */); + + /* Build EVPN hash table and inform BGP. */ + zevpn_build_hash_table(); /* Add all SVI (L3 GW) MACs to BGP*/ - hash_iterate(zvrf->vni_table, zvni_gw_macip_add_for_vni_hash, - NULL); + hash_iterate(zvrf->evpn_table, + zebra_evpn_gw_macip_add_for_evpn_hash, NULL); /* Read the MAC FDB */ - macfdb_read(zvrf->zns); + ns_walk_func(macfdb_read_ns, NULL, NULL); /* Read neighbors */ - neigh_read(zvrf->zns); + ns_walk_func(neigh_read_ns, NULL, NULL); } else { - /* Cleanup VTEPs for all VNIs - uninstall from + /* Cleanup VTEPs for all EVPNs - uninstall from * kernel and free entries. */ - hash_iterate(zvrf->vni_table, zvni_cleanup_all, zvrf); + hash_iterate(zvrf->evpn_table, zebra_evpn_vxlan_cleanup_all, + zvrf); + + /* Delete all ESs in BGP */ + zebra_evpn_es_send_all_to_client(false /* add */); /* cleanup all l3vnis */ hash_iterate(zrouter.l3vni_table, zl3vni_cleanup_all, NULL); @@ -9233,35 +5626,41 @@ void zebra_vxlan_advertise_all_vni(ZAPI_HANDLER_ARGS) } /* - * Allocate VNI hash table for this VRF and do other initialization. + * Allocate EVPN hash table for this VRF and do other initialization. * NOTE: Currently supported only for default VRF. */ void zebra_vxlan_init_tables(struct zebra_vrf *zvrf) { if (!zvrf) return; - zvrf->vni_table = hash_create(vni_hash_keymake, vni_hash_cmp, - "Zebra VRF VNI Table"); + zvrf->evpn_table = + hash_create(zebra_evpn_hash_keymake, zebra_evpn_hash_cmp, + "Zebra VRF EVPN Table"); zvrf->vxlan_sg_table = hash_create(zebra_vxlan_sg_hash_key_make, zebra_vxlan_sg_hash_eq, "Zebra VxLAN SG Table"); } -/* Cleanup VNI info, but don't free the table. */ +/* Cleanup EVPN info, but don't free the table. */ void zebra_vxlan_cleanup_tables(struct zebra_vrf *zvrf) { + struct zebra_vrf *evpn_zvrf = zebra_vrf_get_evpn(); + if (!zvrf) return; - hash_iterate(zvrf->vni_table, zvni_cleanup_all, zvrf); + hash_iterate(zvrf->evpn_table, zebra_evpn_vxlan_cleanup_all, zvrf); hash_iterate(zvrf->vxlan_sg_table, zebra_vxlan_sg_cleanup, NULL); + + if (zvrf == evpn_zvrf) + zebra_evpn_es_cleanup(); } -/* Close all VNI handling */ +/* Close all EVPN handling */ void zebra_vxlan_close_tables(struct zebra_vrf *zvrf) { if (!zvrf) return; - hash_iterate(zvrf->vni_table, zvni_cleanup_all, zvrf); - hash_free(zvrf->vni_table); + hash_iterate(zvrf->evpn_table, zebra_evpn_vxlan_cleanup_all, zvrf); + hash_free(zvrf->evpn_table); } /* init the l3vni table */ @@ -9270,12 +5669,14 @@ void zebra_vxlan_init(void) zrouter.l3vni_table = hash_create(l3vni_hash_keymake, l3vni_hash_cmp, "Zebra VRF L3 VNI table"); zrouter.evpn_vrf = NULL; + zebra_evpn_mh_init(); } /* free l3vni table */ void zebra_vxlan_disable(void) { hash_free(zrouter.l3vni_table); + zebra_evpn_mh_terminate(); } /* get the l3vni svi ifindex */ @@ -9290,133 +5691,11 @@ ifindex_t get_l3vni_svi_ifindex(vrf_id_t vrf_id) return zl3vni->svi_if->ifindex; } -static int zebra_vxlan_dad_ip_auto_recovery_exp(struct thread *t) -{ - struct zebra_vrf *zvrf = NULL; - zebra_neigh_t *nbr = NULL; - zebra_vni_t *zvni = NULL; - char buf1[INET6_ADDRSTRLEN]; - char buf2[ETHER_ADDR_STRLEN]; - - nbr = THREAD_ARG(t); - - /* since this is asynchronous we need sanity checks*/ - zvrf = vrf_info_lookup(nbr->zvni->vrf_id); - if (!zvrf) - return 0; - - zvni = zvni_lookup(nbr->zvni->vni); - if (!zvni) - return 0; - - nbr = zvni_neigh_lookup(zvni, &nbr->ip); - if (!nbr) - return 0; - - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("%s: duplicate addr MAC %s IP %s flags 0x%x learn count %u vni %u auto recovery expired", - __PRETTY_FUNCTION__, - prefix_mac2str(&nbr->emac, buf2, sizeof(buf2)), - ipaddr2str(&nbr->ip, buf1, sizeof(buf1)), - nbr->flags, - nbr->dad_count, zvni->vni); - - UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); - nbr->dad_count = 0; - nbr->detect_start_time.tv_sec = 0; - nbr->detect_start_time.tv_usec = 0; - nbr->dad_dup_detect_time = 0; - nbr->dad_ip_auto_recovery_timer = NULL; - ZEBRA_NEIGH_SET_ACTIVE(nbr); - - /* Send to BGP */ - if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL)) { - zvni_neigh_send_add_to_client(zvni->vni, &nbr->ip, &nbr->emac, - nbr->flags, nbr->loc_seq); - } else if (!!CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_REMOTE)) { - zvni_neigh_install(zvni, nbr); - } - - return 0; -} - -static int zebra_vxlan_dad_mac_auto_recovery_exp(struct thread *t) -{ - struct zebra_vrf *zvrf = NULL; - zebra_mac_t *mac = NULL; - zebra_vni_t *zvni = NULL; - struct listnode *node = NULL; - zebra_neigh_t *nbr = NULL; - char buf[ETHER_ADDR_STRLEN]; - - mac = THREAD_ARG(t); - - /* since this is asynchronous we need sanity checks*/ - zvrf = vrf_info_lookup(mac->zvni->vrf_id); - if (!zvrf) - return 0; - - zvni = zvni_lookup(mac->zvni->vni); - if (!zvni) - return 0; - - mac = zvni_mac_lookup(zvni, &mac->macaddr); - if (!mac) - return 0; - - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("%s: duplicate addr mac %s flags 0x%x learn count %u host count %u auto recovery expired", - __PRETTY_FUNCTION__, - prefix_mac2str(&mac->macaddr, buf, sizeof(buf)), - mac->flags, - mac->dad_count, - listcount(mac->neigh_list)); - - /* Remove all IPs as duplicate associcated with this MAC */ - for (ALL_LIST_ELEMENTS_RO(mac->neigh_list, node, nbr)) { - if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) { - if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL)) - ZEBRA_NEIGH_SET_INACTIVE(nbr); - else if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_REMOTE)) - zvni_neigh_install(zvni, nbr); - } - - UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); - nbr->dad_count = 0; - nbr->detect_start_time.tv_sec = 0; - nbr->dad_dup_detect_time = 0; - } - - UNSET_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE); - mac->dad_count = 0; - mac->detect_start_time.tv_sec = 0; - mac->detect_start_time.tv_usec = 0; - mac->dad_dup_detect_time = 0; - mac->dad_mac_auto_recovery_timer = NULL; - - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) { - /* Inform to BGP */ - if (zvni_mac_send_add_to_client(zvni->vni, &mac->macaddr, - mac->flags, mac->loc_seq)) - return -1; - - /* Process all neighbors associated with this MAC. */ - zvni_process_neigh_on_local_mac_change(zvni, mac, 0); - - } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { - zvni_process_neigh_on_remote_mac_add(zvni, mac); - - /* Install the entry. */ - zvni_mac_install(zvni, mac); - } - - return 0; -} - /************************** vxlan SG cache management ************************/ /* Inform PIM about the mcast group */ -static int zebra_vxlan_sg_send(struct prefix_sg *sg, - char *sg_str, uint16_t cmd) +static int zebra_vxlan_sg_send(struct zebra_vrf *zvrf, + struct prefix_sg *sg, + char *sg_str, uint16_t cmd) { struct zserv *client = NULL; struct stream *s = NULL; @@ -9425,6 +5704,9 @@ static int zebra_vxlan_sg_send(struct prefix_sg *sg, if (!client) return 0; + if (!CHECK_FLAG(zvrf->flags, ZEBRA_PIM_SEND_VXLAN_SG)) + return 0; + s = stream_new(ZEBRA_MAX_PACKET_SIZ); zclient_create_header(s, cmd, VRF_DEFAULT); @@ -9449,9 +5731,9 @@ static int zebra_vxlan_sg_send(struct prefix_sg *sg, return zserv_send_message(client, s); } -static unsigned int zebra_vxlan_sg_hash_key_make(void *p) +static unsigned int zebra_vxlan_sg_hash_key_make(const void *p) { - zebra_vxlan_sg_t *vxlan_sg = p; + const zebra_vxlan_sg_t *vxlan_sg = p; return (jhash_2words(vxlan_sg->sg.src.s_addr, vxlan_sg->sg.grp.s_addr, 0)); @@ -9510,7 +5792,7 @@ static zebra_vxlan_sg_t *zebra_vxlan_sg_add(struct zebra_vrf *zvrf, * 2. the XG entry is used by pimd to setup the * vxlan-termination-mroute */ - if (sg->src.s_addr) { + if (sg->src.s_addr != INADDR_ANY) { memset(&sip, 0, sizeof(sip)); parent = zebra_vxlan_sg_do_ref(zvrf, sip, sg->grp); if (!parent) @@ -9524,7 +5806,8 @@ static zebra_vxlan_sg_t *zebra_vxlan_sg_add(struct zebra_vrf *zvrf, return vxlan_sg; } - zebra_vxlan_sg_send(sg, vxlan_sg->sg_str, ZEBRA_VXLAN_SG_ADD); + zebra_vxlan_sg_send(zvrf, sg, vxlan_sg->sg_str, + ZEBRA_VXLAN_SG_ADD); return vxlan_sg; } @@ -9541,13 +5824,13 @@ static void zebra_vxlan_sg_del(zebra_vxlan_sg_t *vxlan_sg) /* On SG entry deletion remove the reference to its parent XG * entry */ - if (vxlan_sg->sg.src.s_addr) { + if (vxlan_sg->sg.src.s_addr != INADDR_ANY) { memset(&sip, 0, sizeof(sip)); zebra_vxlan_sg_do_deref(zvrf, sip, vxlan_sg->sg.grp); } - zebra_vxlan_sg_send(&vxlan_sg->sg, vxlan_sg->sg_str, - ZEBRA_VXLAN_SG_DEL); + zebra_vxlan_sg_send(zvrf, &vxlan_sg->sg, + vxlan_sg->sg_str, ZEBRA_VXLAN_SG_DEL); hash_release(vxlan_sg->zvrf->vxlan_sg_table, vxlan_sg); @@ -9600,7 +5883,8 @@ static void zebra_vxlan_sg_deref(struct in_addr local_vtep_ip, { struct zebra_vrf *zvrf; - if (!local_vtep_ip.s_addr || !mcast_grp.s_addr) + if (local_vtep_ip.s_addr == INADDR_ANY + || mcast_grp.s_addr == INADDR_ANY) return; zvrf = vrf_info_lookup(VRF_DEFAULT); @@ -9615,7 +5899,8 @@ static void zebra_vxlan_sg_ref(struct in_addr local_vtep_ip, { struct zebra_vrf *zvrf; - if (!local_vtep_ip.s_addr || !mcast_grp.s_addr) + if (local_vtep_ip.s_addr == INADDR_ANY + || mcast_grp.s_addr == INADDR_ANY) return; zvrf = vrf_info_lookup(VRF_DEFAULT); @@ -9624,9 +5909,114 @@ static void zebra_vxlan_sg_ref(struct in_addr local_vtep_ip, zebra_vxlan_sg_do_ref(zvrf, local_vtep_ip, mcast_grp); } -static void zebra_vxlan_sg_cleanup(struct hash_backet *backet, void *arg) +static void zebra_vxlan_sg_cleanup(struct hash_bucket *backet, void *arg) { zebra_vxlan_sg_t *vxlan_sg = (zebra_vxlan_sg_t *)backet->data; zebra_vxlan_sg_del(vxlan_sg); } + +static void zebra_vxlan_sg_replay_send(struct hash_bucket *backet, void *arg) +{ + zebra_vxlan_sg_t *vxlan_sg = (zebra_vxlan_sg_t *)backet->data; + + zebra_vxlan_sg_send(vxlan_sg->zvrf, &vxlan_sg->sg, + vxlan_sg->sg_str, ZEBRA_VXLAN_SG_ADD); +} + +/* Handle message from client to replay vxlan SG entries */ +void zebra_vxlan_sg_replay(ZAPI_HANDLER_ARGS) +{ + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("VxLAN SG updates to PIM, start"); + + SET_FLAG(zvrf->flags, ZEBRA_PIM_SEND_VXLAN_SG); + + if (!EVPN_ENABLED(zvrf)) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("VxLAN SG replay request on unexpected vrf %d", + zvrf->vrf->vrf_id); + return; + } + + hash_iterate(zvrf->vxlan_sg_table, zebra_vxlan_sg_replay_send, NULL); +} + + +/* Cleanup EVPN configuration of a specific VRF */ +static void zebra_evpn_vrf_cfg_cleanup(struct zebra_vrf *zvrf) +{ + zebra_l3vni_t *zl3vni = NULL; + + zvrf->advertise_all_vni = 0; + zvrf->advertise_gw_macip = 0; + zvrf->advertise_svi_macip = 0; + zvrf->vxlan_flood_ctrl = VXLAN_FLOOD_HEAD_END_REPL; + + hash_iterate(zvrf->evpn_table, zebra_evpn_cfg_cleanup, NULL); + + if (zvrf->l3vni) + zl3vni = zl3vni_lookup(zvrf->l3vni); + if (zl3vni) { + /* delete and uninstall all rmacs */ + hash_iterate(zl3vni->rmac_table, zl3vni_del_rmac_hash_entry, + zl3vni); + /* delete and uninstall all next-hops */ + hash_iterate(zl3vni->nh_table, zl3vni_del_nh_hash_entry, + zl3vni); + } +} + +/* Cleanup BGP EVPN configuration upon client disconnect */ +static int zebra_evpn_bgp_cfg_clean_up(struct zserv *client) +{ + struct vrf *vrf; + struct zebra_vrf *zvrf; + + RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) { + zvrf = vrf->info; + if (zvrf) + zebra_evpn_vrf_cfg_cleanup(zvrf); + } + + return 0; +} + +static int zebra_evpn_pim_cfg_clean_up(struct zserv *client) +{ + struct zebra_vrf *zvrf = zebra_vrf_get_evpn(); + + if (zvrf && CHECK_FLAG(zvrf->flags, ZEBRA_PIM_SEND_VXLAN_SG)) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("VxLAN SG updates to PIM, stop"); + UNSET_FLAG(zvrf->flags, ZEBRA_PIM_SEND_VXLAN_SG); + } + + return 0; +} + +static int zebra_evpn_cfg_clean_up(struct zserv *client) +{ + if (client->proto == ZEBRA_ROUTE_BGP) + return zebra_evpn_bgp_cfg_clean_up(client); + + if (client->proto == ZEBRA_ROUTE_PIM) + return zebra_evpn_pim_cfg_clean_up(client); + + return 0; +} + +/* + * Handle results for vxlan dataplane operations. + */ +extern void zebra_vxlan_handle_result(struct zebra_dplane_ctx *ctx) +{ + /* TODO -- anything other than freeing the context? */ + dplane_ctx_fini(&ctx); +} + +/* Cleanup BGP EVPN configuration upon client disconnect */ +extern void zebra_evpn_init(void) +{ + hook_register(zserv_client_close, zebra_evpn_cfg_clean_up); +} diff --git a/zebra/zebra_vxlan.h b/zebra/zebra_vxlan.h index f752bdd690..9c8af9d1fc 100644 --- a/zebra/zebra_vxlan.h +++ b/zebra/zebra_vxlan.h @@ -35,6 +35,7 @@ #include "lib/json.h" #include "zebra/zebra_vrf.h" #include "zebra/zserv.h" +#include "zebra/zebra_dplane.h" #ifdef __cplusplus extern "C" { @@ -79,6 +80,7 @@ extern void zebra_vxlan_advertise_svi_macip(ZAPI_HANDLER_ARGS); extern void zebra_vxlan_advertise_gw_macip(ZAPI_HANDLER_ARGS); extern void zebra_vxlan_advertise_all_vni(ZAPI_HANDLER_ARGS); extern void zebra_vxlan_dup_addr_detection(ZAPI_HANDLER_ARGS); +extern void zebra_vxlan_sg_replay(ZAPI_HANDLER_ARGS); extern int is_l3vni_for_prefix_routes_only(vni_t vni); extern ifindex_t get_l3vni_svi_ifindex(vrf_id_t vrf_id); @@ -139,7 +141,8 @@ extern void zebra_vxlan_print_neigh_vni_dad(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni, bool use_json); extern void zebra_vxlan_print_vni(struct vty *vty, struct zebra_vrf *zvrf, - vni_t vni, bool use_json); + vni_t vni, bool use_json, + json_object *json_array); extern void zebra_vxlan_print_vnis(struct vty *vty, struct zebra_vrf *zvrf, bool use_json); extern void zebra_vxlan_print_vnis_detail(struct vty *vty, @@ -162,14 +165,15 @@ extern int zebra_vxlan_svi_down(struct interface *ifp, extern int zebra_vxlan_handle_kernel_neigh_update( struct interface *ifp, struct interface *link_if, struct ipaddr *ip, struct ethaddr *macaddr, uint16_t state, bool is_ext, - bool is_router); + bool is_router, bool local_inactive, bool dp_static); extern int zebra_vxlan_handle_kernel_neigh_del(struct interface *ifp, struct interface *link_if, struct ipaddr *ip); extern int zebra_vxlan_local_mac_add_update(struct interface *ifp, struct interface *br_if, struct ethaddr *mac, vlanid_t vid, - bool sticky); + bool sticky, bool local_inactive, + bool dp_static); extern int zebra_vxlan_local_mac_del(struct interface *ifp, struct interface *br_if, struct ethaddr *mac, vlanid_t vid); @@ -180,6 +184,8 @@ extern int zebra_vxlan_check_readd_remote_mac(struct interface *ifp, extern int zebra_vxlan_check_del_local_mac(struct interface *ifp, struct interface *br_if, struct ethaddr *mac, vlanid_t vid); +extern int zebra_vxlan_check_readd_vtep(struct interface *ifp, + struct in_addr vtep_ip); extern int zebra_vxlan_if_up(struct interface *ifp); extern int zebra_vxlan_if_down(struct interface *ifp); extern int zebra_vxlan_if_add(struct interface *ifp); @@ -194,24 +200,25 @@ extern void zebra_vxlan_cleanup_tables(struct zebra_vrf *); extern void zebra_vxlan_init(void); extern void zebra_vxlan_disable(void); extern void zebra_vxlan_evpn_vrf_route_add(vrf_id_t vrf_id, - struct ethaddr *rmac, - struct ipaddr *ip, - struct prefix *host_prefix); + const struct ethaddr *rmac, + const struct ipaddr *ip, + const struct prefix *host_prefix); extern void zebra_vxlan_evpn_vrf_route_del(vrf_id_t vrf_id, struct ipaddr *vtep_ip, struct prefix *host_prefix); -extern int zebra_vxlan_clear_dup_detect_vni_mac(struct vty *vty, - struct zebra_vrf *zvrf, +extern int zebra_vxlan_clear_dup_detect_vni_mac(struct zebra_vrf *zvrf, vni_t vni, struct ethaddr *macaddr); -extern int zebra_vxlan_clear_dup_detect_vni_ip(struct vty *vty, - struct zebra_vrf *zvrf, +extern int zebra_vxlan_clear_dup_detect_vni_ip(struct zebra_vrf *zvrf, vni_t vni, struct ipaddr *ip); -extern int zebra_vxlan_clear_dup_detect_vni_all(struct vty *vty, - struct zebra_vrf *zvrf); -extern int zebra_vxlan_clear_dup_detect_vni(struct vty *vty, - struct zebra_vrf *zvrf, - vni_t vni); +extern int zebra_vxlan_clear_dup_detect_vni_all(struct zebra_vrf *zvrf); +extern int zebra_vxlan_clear_dup_detect_vni(struct zebra_vrf *zvrf, vni_t vni); +extern void zebra_vxlan_handle_result(struct zebra_dplane_ctx *ctx); + +extern void zebra_evpn_init(void); +extern void zebra_vxlan_macvlan_up(struct interface *ifp); +extern void zebra_vxlan_macvlan_down(struct interface *ifp); +extern int vni_list_cmp(void *p1, void *p2); #ifdef __cplusplus } diff --git a/zebra/zebra_vxlan_private.h b/zebra/zebra_vxlan_private.h index 9f945442bb..9a88d98b81 100644 --- a/zebra/zebra_vxlan_private.h +++ b/zebra/zebra_vxlan_private.h @@ -29,6 +29,8 @@ #include "if.h" #include "linklist.h" #include "zebra_vxlan.h" +#include "zebra_evpn.h" +#include "zebra_evpn_mac.h" #ifdef __cplusplus extern "C" { @@ -37,72 +39,8 @@ extern "C" { #define ERR_STR_SZ 256 /* definitions */ -typedef struct zebra_vni_t_ zebra_vni_t; -typedef struct zebra_vtep_t_ zebra_vtep_t; -typedef struct zebra_mac_t_ zebra_mac_t; -typedef struct zebra_neigh_t_ zebra_neigh_t; typedef struct zebra_l3vni_t_ zebra_l3vni_t; -/* - * VTEP info - * - * Right now, this just has each remote VTEP's IP address. - */ -struct zebra_vtep_t_ { - /* Remote IP. */ - /* NOTE: Can only be IPv4 right now. */ - struct in_addr vtep_ip; - /* Flood mode (one of enum vxlan_flood_control) based on the PMSI - * tunnel type advertised by the remote VTEP - */ - int flood_control; - - /* Links. */ - struct zebra_vtep_t_ *next; - struct zebra_vtep_t_ *prev; -}; - - -/* - * VNI hash table - * - * Contains information pertaining to a VNI: - * - the list of remote VTEPs (with this VNI) - */ -struct zebra_vni_t_ { - /* VNI - key */ - vni_t vni; - - /* Flag for advertising gw macip */ - uint8_t advertise_gw_macip; - - /* Flag for advertising svi macip */ - uint8_t advertise_svi_macip; - - /* Flag for advertising gw macip */ - uint8_t advertise_subnet; - - /* Corresponding VxLAN interface. */ - struct interface *vxlan_if; - - /* List of remote VTEPs */ - zebra_vtep_t *vteps; - - /* Local IP */ - struct in_addr local_vtep_ip; - - /* PIM-SM MDT group for BUM flooding */ - struct in_addr mcast_grp; - - /* tenant VRF, if any */ - vrf_id_t vrf_id; - - /* List of local or remote MAC */ - struct hash *mac_table; - - /* List of local or remote neighbors (MAC+IP) */ - struct hash *neigh_table; -}; /* L3 VNI hash table */ struct zebra_l3vni_t_ { @@ -125,6 +63,8 @@ struct zebra_l3vni_t_ { /* SVI interface corresponding to the l3vni */ struct interface *svi_if; + struct interface *mac_vlan_if; + /* list of L2 VNIs associated with the L3 VNI */ struct list *l2vnis; @@ -160,8 +100,44 @@ static inline const char *zl3vni_rmac2str(zebra_l3vni_t *zl3vni, char *buf, char *ptr; if (!buf) - ptr = (char *)XMALLOC(MTYPE_TMP, - ETHER_ADDR_STRLEN * sizeof(char)); + ptr = XMALLOC(MTYPE_TMP, ETHER_ADDR_STRLEN * sizeof(char)); + else { + assert(size >= ETHER_ADDR_STRLEN); + ptr = buf; + } + + if (zl3vni->mac_vlan_if) + snprintf(ptr, (ETHER_ADDR_STRLEN), + "%02x:%02x:%02x:%02x:%02x:%02x", + (uint8_t)zl3vni->mac_vlan_if->hw_addr[0], + (uint8_t)zl3vni->mac_vlan_if->hw_addr[1], + (uint8_t)zl3vni->mac_vlan_if->hw_addr[2], + (uint8_t)zl3vni->mac_vlan_if->hw_addr[3], + (uint8_t)zl3vni->mac_vlan_if->hw_addr[4], + (uint8_t)zl3vni->mac_vlan_if->hw_addr[5]); + else if (zl3vni->svi_if) + snprintf(ptr, (ETHER_ADDR_STRLEN), + "%02x:%02x:%02x:%02x:%02x:%02x", + (uint8_t)zl3vni->svi_if->hw_addr[0], + (uint8_t)zl3vni->svi_if->hw_addr[1], + (uint8_t)zl3vni->svi_if->hw_addr[2], + (uint8_t)zl3vni->svi_if->hw_addr[3], + (uint8_t)zl3vni->svi_if->hw_addr[4], + (uint8_t)zl3vni->svi_if->hw_addr[5]); + else + snprintf(ptr, ETHER_ADDR_STRLEN, "None"); + + return ptr; +} + +/* get the sys mac string */ +static inline const char *zl3vni_sysmac2str(zebra_l3vni_t *zl3vni, char *buf, + int size) +{ + char *ptr; + + if (!buf) + ptr = XMALLOC(MTYPE_TMP, ETHER_ADDR_STRLEN * sizeof(char)); else { assert(size >= ETHER_ADDR_STRLEN); ptr = buf; @@ -215,7 +191,8 @@ static inline vrf_id_t zl3vni_vrf_id(zebra_l3vni_t *zl3vni) return zl3vni->vrf_id; } -static inline void zl3vni_get_rmac(zebra_l3vni_t *zl3vni, struct ethaddr *rmac) +static inline void zl3vni_get_svi_rmac(zebra_l3vni_t *zl3vni, + struct ethaddr *rmac) { if (!zl3vni) return; @@ -227,199 +204,11 @@ static inline void zl3vni_get_rmac(zebra_l3vni_t *zl3vni, struct ethaddr *rmac) memcpy(rmac->octet, zl3vni->svi_if->hw_addr, ETH_ALEN); } -struct host_rb_entry { - RB_ENTRY(host_rb_entry) hl_entry; - - struct prefix p; -}; - -RB_HEAD(host_rb_tree_entry, host_rb_entry); -RB_PROTOTYPE(host_rb_tree_entry, host_rb_entry, hl_entry, - host_rb_entry_compare); -/* - * MAC hash table. - * - * This table contains the MAC addresses pertaining to this VNI. - * This includes local MACs learnt on an attached VLAN that maps - * to this VNI as well as remote MACs learnt and installed by BGP. - * Local MACs will be known either on a VLAN sub-interface or - * on (port, VLAN); however, it is sufficient for zebra to maintain - * against the VNI i.e., it does not need to retain the local "port" - * information. The correct VNI will be obtained as zebra maintains - * the mapping (of VLAN to VNI). - */ -struct zebra_mac_t_ { - /* MAC address. */ - struct ethaddr macaddr; - - uint32_t flags; -#define ZEBRA_MAC_LOCAL 0x01 -#define ZEBRA_MAC_REMOTE 0x02 -#define ZEBRA_MAC_AUTO 0x04 /* Auto created for neighbor. */ -#define ZEBRA_MAC_STICKY 0x08 /* Static MAC */ -#define ZEBRA_MAC_REMOTE_RMAC 0x10 /* remote router mac */ -#define ZEBRA_MAC_DEF_GW 0x20 -/* remote VTEP advertised MAC as default GW */ -#define ZEBRA_MAC_REMOTE_DEF_GW 0x40 -#define ZEBRA_MAC_DUPLICATE 0x80 - - /* back pointer to zvni */ - zebra_vni_t *zvni; - - /* Local or remote info. */ - union { - struct { - ifindex_t ifindex; - vlanid_t vid; - } local; - - struct in_addr r_vtep_ip; - } fwd_info; - - /* Mobility sequence numbers associated with this entry. */ - uint32_t rem_seq; - uint32_t loc_seq; - - /* List of neigh associated with this mac */ - struct list *neigh_list; - - /* list of hosts pointing to this remote RMAC */ - struct host_rb_tree_entry host_rb; - - /* Duplicate mac detection */ - uint32_t dad_count; - - struct thread *dad_mac_auto_recovery_timer; - - struct timeval detect_start_time; - - time_t dad_dup_detect_time; -}; - -/* - * Context for MAC hash walk - used by callbacks. - */ -struct mac_walk_ctx { - zebra_vni_t *zvni; /* VNI hash */ - struct zebra_vrf *zvrf; /* VRF - for client notification. */ - int uninstall; /* uninstall from kernel? */ - int upd_client; /* uninstall from client? */ - - uint32_t flags; -#define DEL_LOCAL_MAC 0x1 -#define DEL_REMOTE_MAC 0x2 -#define DEL_ALL_MAC (DEL_LOCAL_MAC | DEL_REMOTE_MAC) -#define DEL_REMOTE_MAC_FROM_VTEP 0x4 -#define SHOW_REMOTE_MAC_FROM_VTEP 0x8 - - struct in_addr r_vtep_ip; /* To walk MACs from specific VTEP */ - - struct vty *vty; /* Used by VTY handlers */ - uint32_t count; /* Used by VTY handlers */ - struct json_object *json; /* Used for JSON Output */ - bool print_dup; /* Used to print dup addr list */ -}; - -struct rmac_walk_ctx { - struct vty *vty; - struct json_object *json; -}; - -#define IS_ZEBRA_NEIGH_ACTIVE(n) (n->state == ZEBRA_NEIGH_ACTIVE) - -#define IS_ZEBRA_NEIGH_INACTIVE(n) (n->state == ZEBRA_NEIGH_INACTIVE) - -#define ZEBRA_NEIGH_SET_ACTIVE(n) n->state = ZEBRA_NEIGH_ACTIVE - -#define ZEBRA_NEIGH_SET_INACTIVE(n) n->state = ZEBRA_NEIGH_INACTIVE - -/* - * Neighbor hash table. - * - * This table contains the neighbors (IP to MAC bindings) pertaining to - * this VNI. This includes local neighbors learnt on the attached VLAN - * device that maps to this VNI as well as remote neighbors learnt and - * installed by BGP. - * Local neighbors will be known against the VLAN device (SVI); however, - * it is sufficient for zebra to maintain against the VNI. The correct - * VNI will be obtained as zebra maintains the mapping (of VLAN to VNI). - */ -struct zebra_neigh_t_ { - /* IP address. */ - struct ipaddr ip; - - /* MAC address. */ - struct ethaddr emac; - - /* Underlying interface. */ - ifindex_t ifindex; - - zebra_vni_t *zvni; - - uint32_t flags; -#define ZEBRA_NEIGH_LOCAL 0x01 -#define ZEBRA_NEIGH_REMOTE 0x02 -#define ZEBRA_NEIGH_REMOTE_NH 0x04 /* neigh entry for remote vtep */ -#define ZEBRA_NEIGH_DEF_GW 0x08 -#define ZEBRA_NEIGH_ROUTER_FLAG 0x10 -#define ZEBRA_NEIGH_DUPLICATE 0x20 - - enum zebra_neigh_state state; - - /* Remote VTEP IP - applicable only for remote neighbors. */ - struct in_addr r_vtep_ip; - - /* - * Mobility sequence numbers associated with this entry. The rem_seq - * represents the sequence number from the client (BGP) for the most - * recent add or update of this entry while the loc_seq represents - * the sequence number informed (or to be informed) by zebra to BGP - * for this entry. - */ - uint32_t rem_seq; - uint32_t loc_seq; - - /* list of hosts pointing to this remote NH entry */ - struct host_rb_tree_entry host_rb; - - /* Duplicate ip detection */ - uint32_t dad_count; - - struct thread *dad_ip_auto_recovery_timer; - - struct timeval detect_start_time; - - time_t dad_dup_detect_time; -}; - -/* - * Context for neighbor hash walk - used by callbacks. - */ -struct neigh_walk_ctx { - zebra_vni_t *zvni; /* VNI hash */ - struct zebra_vrf *zvrf; /* VRF - for client notification. */ - int uninstall; /* uninstall from kernel? */ - int upd_client; /* uninstall from client? */ - - uint32_t flags; -#define DEL_LOCAL_NEIGH 0x1 -#define DEL_REMOTE_NEIGH 0x2 -#define DEL_ALL_NEIGH (DEL_LOCAL_NEIGH | DEL_REMOTE_NEIGH) -#define DEL_REMOTE_NEIGH_FROM_VTEP 0x4 -#define SHOW_REMOTE_NEIGH_FROM_VTEP 0x8 - - struct in_addr r_vtep_ip; /* To walk neighbors from specific VTEP */ - - struct vty *vty; /* Used by VTY handlers */ - uint32_t count; /* Used by VTY handlers */ - uint8_t addr_width; /* Used by VTY handlers */ - struct json_object *json; /* Used for JSON Output */ -}; /* context for neigh hash walk - update l3vni and rmac */ struct neigh_l3info_walk_ctx { - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; zebra_l3vni_t *zl3vni; int add; }; @@ -430,6 +219,16 @@ struct nh_walk_ctx { struct json_object *json; }; +extern zebra_l3vni_t *zl3vni_from_vrf(vrf_id_t vrf_id); +extern struct interface *zl3vni_map_to_vxlan_if(zebra_l3vni_t *zl3vni); +extern struct interface *zl3vni_map_to_svi_if(zebra_l3vni_t *zl3vni); +extern struct interface *zl3vni_map_to_mac_vlan_if(zebra_l3vni_t *zl3vni); +extern zebra_l3vni_t *zl3vni_lookup(vni_t vni); + +DECLARE_HOOK(zebra_rmac_update, (zebra_mac_t *rmac, zebra_l3vni_t *zl3vni, + bool delete, const char *reason), (rmac, zl3vni, delete, reason)) + + #ifdef __cplusplus } #endif @@ -456,4 +255,8 @@ typedef struct zebra_vxlan_sg_ { uint32_t ref_cnt; } zebra_vxlan_sg_t; +extern zebra_evpn_t *zevpn_lookup(vni_t vni); +extern void zebra_vxlan_sync_mac_dp_install(zebra_mac_t *mac, bool set_inactive, + bool force_clear_static, const char *caller); + #endif /* _ZEBRA_VXLAN_PRIVATE_H */ diff --git a/zebra/zserv.c b/zebra/zserv.c index df5f236c04..4c8656af0d 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -71,6 +71,14 @@ extern struct zebra_privs_t zserv_privs; /* The listener socket for clients connecting to us */ static int zsock; +/* The lock that protects access to zapi client objects */ +static pthread_mutex_t client_mutex; + +static struct zserv *find_client_internal(uint8_t proto, + unsigned short instance, + uint32_t session_id); + + /* * Client thread events. * @@ -149,8 +157,8 @@ static void zserv_event(struct zserv *client, enum zserv_event event); * hdr (optional) * The message header */ -static void zserv_log_message(const char *errmsg, struct stream *msg, - struct zmsghdr *hdr) +void zserv_log_message(const char *errmsg, struct stream *msg, + struct zmsghdr *hdr) { zlog_debug("Rx'd ZAPI message"); if (errmsg) @@ -160,7 +168,7 @@ static void zserv_log_message(const char *errmsg, struct stream *msg, zlog_debug("Command: %s", zserv_command_string(hdr->command)); zlog_debug(" VRF: %u", hdr->vrf_id); } - zlog_hexdump(msg->data, STREAM_READABLE(msg)); + stream_hexdump(msg); } /* @@ -231,18 +239,16 @@ static int zserv_write(struct thread *thread) cache = stream_fifo_new(); - pthread_mutex_lock(&client->obuf_mtx); - { + frr_with_mutex(&client->obuf_mtx) { while (stream_fifo_head(client->obuf_fifo)) stream_fifo_push(cache, stream_fifo_pop(client->obuf_fifo)); } - pthread_mutex_unlock(&client->obuf_mtx); if (cache->tail) { msg = cache->tail; stream_set_getp(msg, 0); - wcmd = stream_getw_from(msg, 6); + wcmd = stream_getw_from(msg, ZAPI_HEADER_CMD_LOCATION); } while (stream_fifo_head(cache)) { @@ -411,9 +417,6 @@ static int zserv_read(struct thread *thread) hdr.vrf_id, hdr.length, sock); - if (IS_ZEBRA_DEBUG_PACKET && IS_ZEBRA_DEBUG_RECV) - zserv_log_message(NULL, client->ibuf_work, &hdr); - stream_set_getp(client->ibuf_work, 0); struct stream *msg = stream_dup(client->ibuf_work); @@ -430,13 +433,11 @@ static int zserv_read(struct thread *thread) memory_order_relaxed); /* publish read packets on client's input queue */ - pthread_mutex_lock(&client->ibuf_mtx); - { + frr_with_mutex(&client->ibuf_mtx) { while (cache->head) stream_fifo_push(client->ibuf_fifo, stream_fifo_pop(cache)); } - pthread_mutex_unlock(&client->ibuf_mtx); /* Schedule job to process those packets */ zserv_event(client, ZSERV_PROCESS_MESSAGES); @@ -502,8 +503,7 @@ static int zserv_process_messages(struct thread *thread) uint32_t p2p = zrouter.packets_to_process; bool need_resched = false; - pthread_mutex_lock(&client->ibuf_mtx); - { + frr_with_mutex(&client->ibuf_mtx) { uint32_t i; for (i = 0; i < p2p && stream_fifo_head(client->ibuf_fifo); ++i) { @@ -519,13 +519,10 @@ static int zserv_process_messages(struct thread *thread) if (stream_fifo_head(client->ibuf_fifo)) need_resched = true; } - pthread_mutex_unlock(&client->ibuf_mtx); - while (stream_fifo_head(cache)) { - msg = stream_fifo_pop(cache); - zserv_handle_commands(client, msg); - stream_free(msg); - } + /* Process the batch of messages */ + if (stream_fifo_head(cache)) + zserv_handle_commands(client, cache); stream_fifo_free(cache); @@ -538,39 +535,34 @@ static int zserv_process_messages(struct thread *thread) int zserv_send_message(struct zserv *client, struct stream *msg) { - /* - * This is a somewhat poorly named variable added with Zebra's portion - * of the label manager. That component does not use the regular - * zserv/zapi_msg interface for handling its messages, as the client - * itself runs in-process. Instead it uses synchronous writes on the - * zserv client's socket directly in the zread* handlers for its - * message types. Furthermore, it cannot handle the usual messages - * Zebra sends (such as those for interface changes) and so has added - * this flag and check here as a hack to suppress all messages that it - * does not explicitly know about. - * - * In any case this needs to be cleaned up at some point. - * - * See also: - * zread_label_manager_request - * zsend_label_manager_connect_response - * zsend_assign_label_chunk_response - * ... - */ - if (client->is_synchronous) - return 0; - - pthread_mutex_lock(&client->obuf_mtx); - { + frr_with_mutex(&client->obuf_mtx) { stream_fifo_push(client->obuf_fifo, msg); } - pthread_mutex_unlock(&client->obuf_mtx); zserv_client_event(client, ZSERV_CLIENT_WRITE); return 0; } +/* + * Send a batch of messages to a connected Zebra API client. + */ +int zserv_send_batch(struct zserv *client, struct stream_fifo *fifo) +{ + struct stream *msg; + + frr_with_mutex(&client->obuf_mtx) { + msg = stream_fifo_pop(fifo); + while (msg) { + stream_fifo_push(client->obuf_fifo, msg); + msg = stream_fifo_pop(fifo); + } + } + + zserv_client_event(client, ZSERV_CLIENT_WRITE); + + return 0; +} /* Hooks for client connect / disconnect */ DEFINE_HOOK(zserv_client_connect, (struct zserv *client), (client)); @@ -590,6 +582,9 @@ DEFINE_KOOH(zserv_client_close, (struct zserv *client), (client)); */ static void zserv_client_free(struct zserv *client) { + if (client == NULL) + return; + hook_call(zserv_client_close, client); /* Close file descriptor. */ @@ -598,11 +593,14 @@ static void zserv_client_free(struct zserv *client) close(client->sock); - nroutes = rib_score_proto(client->proto, client->instance); - zlog_notice( - "client %d disconnected. %lu %s routes removed from the rib", - client->sock, nroutes, - zebra_route_string(client->proto)); + if (DYNAMIC_CLIENT_GR_DISABLED(client)) { + nroutes = rib_score_proto(client->proto, + client->instance); + zlog_notice( + "client %d disconnected %lu %s routes removed from the rib", + client->sock, nroutes, + zebra_route_string(client->proto)); + } client->sock = -1; } @@ -624,38 +622,79 @@ static void zserv_client_free(struct zserv *client) /* Free bitmaps. */ for (afi_t afi = AFI_IP; afi < AFI_MAX; afi++) { - for (int i = 0; i < ZEBRA_ROUTE_MAX; i++) + for (int i = 0; i < ZEBRA_ROUTE_MAX; i++) { vrf_bitmap_free(client->redist[afi][i]); + redist_del_all_instances(&client->mi_redist[afi][i]); + } vrf_bitmap_free(client->redist_default[afi]); + vrf_bitmap_free(client->ridinfo[afi]); } - vrf_bitmap_free(client->ridinfo); - XFREE(MTYPE_TMP, client); + /* + * If any instance are graceful restart enabled, + * client is not deleted + */ + if (DYNAMIC_CLIENT_GR_DISABLED(client)) { + if (IS_ZEBRA_DEBUG_EVENT) + zlog_debug("%s: Deleting client %s", __func__, + zebra_route_string(client->proto)); + XFREE(MTYPE_TMP, client); + } else { + /* Handle cases where client has GR instance. */ + if (IS_ZEBRA_DEBUG_EVENT) + zlog_debug("%s: client %s restart enabled", __func__, + zebra_route_string(client->proto)); + if (zebra_gr_client_disconnect(client) < 0) + zlog_err( + "%s: GR enabled but could not handle disconnect event", + __func__); + } } void zserv_close_client(struct zserv *client) { - /* synchronously stop and join pthread */ - frr_pthread_stop(client->pthread, NULL); + bool free_p = true; - if (IS_ZEBRA_DEBUG_EVENT) - zlog_debug("Closing client '%s'", - zebra_route_string(client->proto)); + if (client->pthread) { + /* synchronously stop and join pthread */ + frr_pthread_stop(client->pthread, NULL); - thread_cancel_event(zrouter.master, client); - THREAD_OFF(client->t_cleanup); - THREAD_OFF(client->t_process); + if (IS_ZEBRA_DEBUG_EVENT) + zlog_debug("Closing client '%s'", + zebra_route_string(client->proto)); - /* destroy pthread */ - frr_pthread_destroy(client->pthread); - client->pthread = NULL; + thread_cancel_event(zrouter.master, client); + THREAD_OFF(client->t_cleanup); + THREAD_OFF(client->t_process); - /* remove from client list */ - listnode_delete(zrouter.client_list, client); + /* destroy pthread */ + frr_pthread_destroy(client->pthread); + client->pthread = NULL; + } + + /* + * Final check in case the client struct is in use in another + * pthread: if not in-use, continue and free the client + */ + frr_with_mutex(&client_mutex) { + if (client->busy_count <= 0) { + /* remove from client list */ + listnode_delete(zrouter.client_list, client); + } else { + /* + * The client session object may be in use, although + * the associated pthread is gone. Defer final + * cleanup. + */ + client->is_closed = true; + free_p = false; + } + } /* delete client */ - zserv_client_free(client); + if (free_p) + zserv_client_free(client); } /* @@ -685,6 +724,8 @@ static int zserv_handle_client_fail(struct thread *thread) static struct zserv *zserv_client_create(int sock) { struct zserv *client; + size_t stream_size = + MAX(ZEBRA_MAX_PACKET_SIZ, sizeof(struct zapi_route)); int i; afi_t afi; @@ -694,14 +735,12 @@ static struct zserv *zserv_client_create(int sock) client->sock = sock; client->ibuf_fifo = stream_fifo_new(); client->obuf_fifo = stream_fifo_new(); - client->ibuf_work = stream_new(ZEBRA_MAX_PACKET_SIZ); - client->obuf_work = stream_new(ZEBRA_MAX_PACKET_SIZ); + client->ibuf_work = stream_new(stream_size); + client->obuf_work = stream_new(stream_size); pthread_mutex_init(&client->ibuf_mtx, NULL); pthread_mutex_init(&client->obuf_mtx, NULL); client->wb = buffer_new(0); - - /* Set table number. */ - client->rtm_table = zrouter.rtm_table_default; + TAILQ_INIT(&(client->gr_info_queue)); atomic_store_explicit(&client->connect_time, (uint32_t) monotime(NULL), memory_order_relaxed); @@ -711,14 +750,13 @@ static struct zserv *zserv_client_create(int sock) for (i = 0; i < ZEBRA_ROUTE_MAX; i++) client->redist[afi][i] = vrf_bitmap_init(); client->redist_default[afi] = vrf_bitmap_init(); + client->ridinfo[afi] = vrf_bitmap_init(); } - client->ridinfo = vrf_bitmap_init(); - - /* by default, it's not a synchronous client */ - client->is_synchronous = 0; /* Add this client to linked list. */ - listnode_add(zrouter.client_list, client); + frr_with_mutex(&client_mutex) { + listnode_add(zrouter.client_list, client); + } struct frr_pthread_attr zclient_pthr_attrs = { .start = frr_pthread_attr_default.start, @@ -740,6 +778,66 @@ static struct zserv *zserv_client_create(int sock) return client; } +/* + * Retrieve a client object by the complete tuple of + * {protocol, instance, session}. This version supports use + * from a different pthread: the object will be returned marked + * in-use. The caller *must* release the client object with the + * release_client() api, to ensure that the in-use marker is cleared properly. + */ +struct zserv *zserv_acquire_client(uint8_t proto, unsigned short instance, + uint32_t session_id) +{ + struct zserv *client = NULL; + + frr_with_mutex(&client_mutex) { + client = find_client_internal(proto, instance, session_id); + if (client) { + /* Don't return a dead/closed client object */ + if (client->is_closed) + client = NULL; + else + client->busy_count++; + } + } + + return client; +} + +/* + * Release a client object that was acquired with the acquire_client() api. + * After this has been called, the caller must not use the client pointer - + * it may be freed if the client has closed. + */ +void zserv_release_client(struct zserv *client) +{ + /* + * Once we've decremented the client object's refcount, it's possible + * for it to be deleted as soon as we release the lock, so we won't + * touch the object again. + */ + frr_with_mutex(&client_mutex) { + client->busy_count--; + + if (client->busy_count <= 0) { + /* + * No more users of the client object. If the client + * session is closed, schedule cleanup on the zebra + * main pthread. + */ + if (client->is_closed) + thread_add_event(zrouter.master, + zserv_handle_client_fail, + client, 0, &client->t_cleanup); + } + } + + /* + * Cleanup must take place on the zebra main pthread, so we've + * scheduled an event. + */ +} + /* * Accept socket connection. */ @@ -783,6 +881,9 @@ void zserv_close(void) */ close(zsock); zsock = -1; + + /* Free client list's mutex */ + pthread_mutex_destroy(&client_mutex); } void zserv_start(char *path) @@ -819,7 +920,7 @@ void zserv_start(char *path) setsockopt_so_recvbuf(zsock, 1048576); setsockopt_so_sendbuf(zsock, 1048576); - frr_elevate_privs((sa.ss_family != AF_UNIX) ? &zserv_privs : NULL) { + frr_with_privs((sa.ss_family != AF_UNIX) ? &zserv_privs : NULL) { ret = bind(zsock, (struct sockaddr *)&sa, sa_len); } if (ret < 0) { @@ -868,7 +969,6 @@ void zserv_event(struct zserv *client, enum zserv_event event) #define ZEBRA_TIME_BUF 32 static char *zserv_time_buf(time_t *time1, char *buf, int buflen) { - struct tm *tm; time_t now; assert(buf != NULL); @@ -882,35 +982,30 @@ static char *zserv_time_buf(time_t *time1, char *buf, int buflen) now = monotime(NULL); now -= *time1; - tm = gmtime(&now); - - if (now < ONE_DAY_SECOND) - snprintf(buf, buflen, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min, - tm->tm_sec); - else if (now < ONE_WEEK_SECOND) - snprintf(buf, buflen, "%dd%02dh%02dm", tm->tm_yday, tm->tm_hour, - tm->tm_min); - else - snprintf(buf, buflen, "%02dw%dd%02dh", tm->tm_yday / 7, - tm->tm_yday - ((tm->tm_yday / 7) * 7), tm->tm_hour); + + frrtime_to_interval(now, buf, buflen); + return buf; } +/* Display client info details */ static void zebra_show_client_detail(struct vty *vty, struct zserv *client) { char cbuf[ZEBRA_TIME_BUF], rbuf[ZEBRA_TIME_BUF]; char wbuf[ZEBRA_TIME_BUF], nhbuf[ZEBRA_TIME_BUF], mbuf[ZEBRA_TIME_BUF]; time_t connect_time, last_read_time, last_write_time; uint32_t last_read_cmd, last_write_cmd; + struct client_gr_info *info = NULL; vty_out(vty, "Client: %s", zebra_route_string(client->proto)); if (client->instance) - vty_out(vty, " Instance: %d", client->instance); + vty_out(vty, " Instance: %u", client->instance); + if (client->session_id) + vty_out(vty, " [%u]", client->session_id); vty_out(vty, "\n"); vty_out(vty, "------------------------ \n"); vty_out(vty, "FD: %d \n", client->sock); - vty_out(vty, "Route Table ID: %d \n", client->rtm_table); connect_time = (time_t) atomic_load_explicit(&client->connect_time, memory_order_relaxed); @@ -952,34 +1047,56 @@ static void zebra_show_client_detail(struct vty *vty, struct zserv *client) zserv_command_string(last_write_cmd)); vty_out(vty, "\n"); - vty_out(vty, "Type Add Update Del \n"); + vty_out(vty, "Type Add Update Del \n"); vty_out(vty, "================================================== \n"); - vty_out(vty, "IPv4 %-12d%-12d%-12d\n", client->v4_route_add_cnt, + vty_out(vty, "IPv4 %-12u%-12u%-12u\n", client->v4_route_add_cnt, client->v4_route_upd8_cnt, client->v4_route_del_cnt); - vty_out(vty, "IPv6 %-12d%-12d%-12d\n", client->v6_route_add_cnt, + vty_out(vty, "IPv6 %-12u%-12u%-12u\n", client->v6_route_add_cnt, client->v6_route_upd8_cnt, client->v6_route_del_cnt); - vty_out(vty, "Redist:v4 %-12d%-12d%-12d\n", client->redist_v4_add_cnt, + vty_out(vty, "Redist:v4 %-12u%-12u%-12u\n", client->redist_v4_add_cnt, 0, client->redist_v4_del_cnt); - vty_out(vty, "Redist:v6 %-12d%-12d%-12d\n", client->redist_v6_add_cnt, + vty_out(vty, "Redist:v6 %-12u%-12u%-12u\n", client->redist_v6_add_cnt, 0, client->redist_v6_del_cnt); - vty_out(vty, "Connected %-12d%-12d%-12d\n", client->ifadd_cnt, 0, + vty_out(vty, "Connected %-12u%-12u%-12u\n", client->ifadd_cnt, 0, client->ifdel_cnt); - vty_out(vty, "BFD peer %-12d%-12d%-12d\n", client->bfd_peer_add_cnt, + vty_out(vty, "BFD peer %-12u%-12u%-12u\n", client->bfd_peer_add_cnt, client->bfd_peer_upd8_cnt, client->bfd_peer_del_cnt); - vty_out(vty, "NHT v4 %-12d%-12d%-12d\n", + vty_out(vty, "NHT v4 %-12u%-12u%-12u\n", client->v4_nh_watch_add_cnt, 0, client->v4_nh_watch_rem_cnt); - vty_out(vty, "NHT v6 %-12d%-12d%-12d\n", + vty_out(vty, "NHT v6 %-12u%-12u%-12u\n", client->v6_nh_watch_add_cnt, 0, client->v6_nh_watch_rem_cnt); - vty_out(vty, "VxLAN SG %-12d%-12d%-12d\n", client->vxlan_sg_add_cnt, + vty_out(vty, "VxLAN SG %-12u%-12u%-12u\n", client->vxlan_sg_add_cnt, 0, client->vxlan_sg_del_cnt); - vty_out(vty, "Interface Up Notifications: %d\n", client->ifup_cnt); - vty_out(vty, "Interface Down Notifications: %d\n", client->ifdown_cnt); - vty_out(vty, "VNI add notifications: %d\n", client->vniadd_cnt); - vty_out(vty, "VNI delete notifications: %d\n", client->vnidel_cnt); - vty_out(vty, "L3-VNI add notifications: %d\n", client->l3vniadd_cnt); - vty_out(vty, "L3-VNI delete notifications: %d\n", client->l3vnidel_cnt); - vty_out(vty, "MAC-IP add notifications: %d\n", client->macipadd_cnt); - vty_out(vty, "MAC-IP delete notifications: %d\n", client->macipdel_cnt); + vty_out(vty, "Interface Up Notifications: %u\n", client->ifup_cnt); + vty_out(vty, "Interface Down Notifications: %u\n", client->ifdown_cnt); + vty_out(vty, "VNI add notifications: %u\n", client->vniadd_cnt); + vty_out(vty, "VNI delete notifications: %u\n", client->vnidel_cnt); + vty_out(vty, "L3-VNI add notifications: %u\n", client->l3vniadd_cnt); + vty_out(vty, "L3-VNI delete notifications: %u\n", client->l3vnidel_cnt); + vty_out(vty, "MAC-IP add notifications: %u\n", client->macipadd_cnt); + vty_out(vty, "MAC-IP delete notifications: %u\n", client->macipdel_cnt); + vty_out(vty, "ES add notifications: %u\n", client->local_es_add_cnt); + vty_out(vty, "ES delete notifications: %u\n", client->local_es_del_cnt); + vty_out(vty, "ES-EVI add notifications: %u\n", + client->local_es_evi_add_cnt); + vty_out(vty, "ES-EVI delete notifications: %u\n", + client->local_es_evi_del_cnt); + + TAILQ_FOREACH (info, &client->gr_info_queue, gr_info) { + vty_out(vty, "VRF : %s\n", vrf_id_to_name(info->vrf_id)); + vty_out(vty, "Capabilities : "); + switch (info->capabilities) { + case ZEBRA_CLIENT_GR_CAPABILITIES: + vty_out(vty, "Graceful Restart\n"); + break; + case ZEBRA_CLIENT_ROUTE_UPDATE_COMPLETE: + case ZEBRA_CLIENT_ROUTE_UPDATE_PENDING: + case ZEBRA_CLIENT_GR_DISABLE: + case ZEBRA_CLIENT_RIB_STALE_TIME: + vty_out(vty, "None\n"); + break; + } + } #if defined DEV_BUILD vty_out(vty, "Input Fifo: %zu:%zu Output Fifo: %zu:%zu\n", @@ -987,6 +1104,74 @@ static void zebra_show_client_detail(struct vty *vty, struct zserv *client) client->obuf_fifo->count, client->obuf_fifo->max_count); #endif vty_out(vty, "\n"); +} + +/* Display stale client information */ +static void zebra_show_stale_client_detail(struct vty *vty, + struct zserv *client) +{ + char buf[PREFIX2STR_BUFFER]; + time_t uptime; + struct client_gr_info *info = NULL; + struct zserv *s = NULL; + bool first_p = true; + + TAILQ_FOREACH (info, &client->gr_info_queue, gr_info) { + if (first_p) { + vty_out(vty, "Stale Client Information\n"); + vty_out(vty, "------------------------\n"); + + if (client->instance) + vty_out(vty, " Instance: %u", client->instance); + if (client->session_id) + vty_out(vty, " [%u]", client->session_id); + + first_p = false; + } + + vty_out(vty, "VRF : %s\n", vrf_id_to_name(info->vrf_id)); + vty_out(vty, "Capabilities : "); + switch (info->capabilities) { + case ZEBRA_CLIENT_GR_CAPABILITIES: + vty_out(vty, "Graceful Restart\n"); + break; + case ZEBRA_CLIENT_ROUTE_UPDATE_COMPLETE: + case ZEBRA_CLIENT_ROUTE_UPDATE_PENDING: + case ZEBRA_CLIENT_GR_DISABLE: + case ZEBRA_CLIENT_RIB_STALE_TIME: + vty_out(vty, "None\n"); + break; + } + + if (ZEBRA_CLIENT_GR_ENABLED(info->capabilities)) { + if (info->stale_client_ptr) { + s = (struct zserv *)(info->stale_client_ptr); + uptime = monotime(NULL); + uptime -= s->restart_time; + + frrtime_to_interval(uptime, buf, sizeof(buf)); + + vty_out(vty, "Last restart time : %s ago\n", + buf); + + vty_out(vty, "Stalepath removal time: %d sec\n", + info->stale_removal_time); + if (info->t_stale_removal) { + vty_out(vty, + "Stale delete timer: %ld sec\n", + thread_timer_remain_second( + info->t_stale_removal)); + } + } + vty_out(vty, "Current AFI : %d\n", info->current_afi); + if (info->current_prefix) { + prefix2str(info->current_prefix, buf, + sizeof(buf)); + vty_out(vty, "Current prefix : %s\n", buf); + } + } + } + vty_out(vty, "\n"); return; } @@ -1003,7 +1188,7 @@ static void zebra_show_client_brief(struct vty *vty, struct zserv *client) last_write_time = (time_t)atomic_load_explicit(&client->last_write_time, memory_order_relaxed); - vty_out(vty, "%-8s%12s %12s%12s%8d/%-8d%8d/%-8d\n", + vty_out(vty, "%-10s%12s %12s%12s%8d/%-8d%8d/%-8d\n", zebra_route_string(client->proto), zserv_time_buf(&connect_time, cbuf, ZEBRA_TIME_BUF), zserv_time_buf(&last_read_time, rbuf, ZEBRA_TIME_BUF), @@ -1014,17 +1199,55 @@ static void zebra_show_client_brief(struct vty *vty, struct zserv *client) client->v6_route_del_cnt); } -struct zserv *zserv_find_client(uint8_t proto, unsigned short instance) +/* + * Common logic that searches the client list for a zapi client; this + * MUST be called holding the client list mutex. + */ +static struct zserv *find_client_internal(uint8_t proto, + unsigned short instance, + uint32_t session_id) { struct listnode *node, *nnode; - struct zserv *client; + struct zserv *client = NULL; for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) { - if (client->proto == proto && client->instance == instance) - return client; + if (client->proto == proto && client->instance == instance && + client->session_id == session_id) + break; } - return NULL; + return client; +} + +/* + * Public api that searches for a client session; this version is + * used from the zebra main pthread. + */ +struct zserv *zserv_find_client(uint8_t proto, unsigned short instance) +{ + struct zserv *client; + + frr_with_mutex(&client_mutex) { + client = find_client_internal(proto, instance, 0); + } + + return client; +} + +/* + * Retrieve a client by its protocol, instance number, and session id. + */ +struct zserv *zserv_find_client_session(uint8_t proto, unsigned short instance, + uint32_t session_id) +{ + struct zserv *client; + + frr_with_mutex(&client_mutex) { + client = find_client_internal(proto, instance, session_id); + } + + return client; + } /* This command is for debugging purpose. */ @@ -1038,8 +1261,11 @@ DEFUN (show_zebra_client, struct listnode *node; struct zserv *client; - for (ALL_LIST_ELEMENTS_RO(zrouter.client_list, node, client)) + for (ALL_LIST_ELEMENTS_RO(zrouter.client_list, node, client)) { zebra_show_client_detail(vty, client); + /* Show GR info if present */ + zebra_show_stale_client_detail(vty, client); + } return CMD_SUCCESS; } @@ -1057,7 +1283,7 @@ DEFUN (show_zebra_client_summary, struct zserv *client; vty_out(vty, - "Name Connect Time Last Read Last Write IPv4 Routes IPv6 Routes \n"); + "Name Connect Time Last Read Last Write IPv4 Routes IPv6 Routes \n"); vty_out(vty, "--------------------------------------------------------------------------------\n"); @@ -1068,26 +1294,15 @@ DEFUN (show_zebra_client_summary, return CMD_SUCCESS; } -#if defined(HANDLE_ZAPI_FUZZING) -void zserv_read_file(char *input) -{ - int fd; - struct thread t; - - fd = open(input, O_RDONLY | O_NONBLOCK); - t.u.fd = fd; - - zserv_client_create(fd); -} -#endif - void zserv_init(void) { /* Client list init. */ zrouter.client_list = list_new(); + zrouter.stale_client_list = list_new(); /* Misc init. */ zsock = -1; + pthread_mutex_init(&client_mutex, NULL); install_element(ENABLE_NODE, &show_zebra_client_cmd); install_element(ENABLE_NODE, &show_zebra_client_summary_cmd); diff --git a/zebra/zserv.h b/zebra/zserv.h index 90fd195712..c60799b8ba 100644 --- a/zebra/zserv.h +++ b/zebra/zserv.h @@ -52,6 +52,42 @@ extern "C" { #define ZEBRA_RMAP_DEFAULT_UPDATE_TIMER 5 /* disabled by default */ + +/* Stale route marker timer */ +#define ZEBRA_DEFAULT_STALE_UPDATE_DELAY 1 + +/* Count of stale routes processed in timer context */ +#define ZEBRA_MAX_STALE_ROUTE_COUNT 50000 + +/* Graceful Restart information */ +struct client_gr_info { + /* VRF for which GR enabled */ + vrf_id_t vrf_id; + + /* AFI */ + afi_t current_afi; + + /* Stale time and GR cap */ + uint32_t stale_removal_time; + enum zserv_client_capabilities capabilities; + + /* GR commands */ + bool do_delete; + bool gr_enable; + bool stale_client; + + /* Route sync and enable flags for AFI/SAFI */ + bool af_enabled[AFI_MAX][SAFI_MAX]; + bool route_sync[AFI_MAX][SAFI_MAX]; + + /* Book keeping */ + struct prefix *current_prefix; + void *stale_client_ptr; + struct thread *t_stale_removal; + + TAILQ_ENTRY(client_gr_info) gr_info; +}; + /* Client structure. */ struct zserv { /* Client pthread */ @@ -60,6 +96,14 @@ struct zserv { /* Client file descriptor. */ int sock; + /* Attributes used to permit access to zapi clients from + * other pthreads: the client has a busy counter, and a + * 'closed' flag. These attributes are managed using a + * lock, via the acquire_client() and release_client() apis. + */ + int busy_count; + bool is_closed; + /* Input/output buffer to the client. */ pthread_mutex_t ibuf_mtx; struct stream_fifo *ibuf_fifo; @@ -80,12 +124,9 @@ struct zserv { /* Event for message processing, for the main pthread */ struct thread *t_process; - /* Threads for the main pthread */ + /* Event for the main pthread */ struct thread *t_cleanup; - /* default routing table this client munges */ - int rtm_table; - /* This client's redistribute flag. */ struct redist_proto mi_redist[AFI_MAX][ZEBRA_ROUTE_MAX]; vrf_bitmap_t redist[AFI_MAX][ZEBRA_ROUTE_MAX]; @@ -94,14 +135,24 @@ struct zserv { vrf_bitmap_t redist_default[AFI_MAX]; /* Router-id information. */ - vrf_bitmap_t ridinfo; + vrf_bitmap_t ridinfo[AFI_MAX]; bool notify_owner; - /* client's protocol */ + /* Indicates if client is synchronous. */ + bool synchronous; + + /* client's protocol and session info */ uint8_t proto; uint16_t instance; - uint8_t is_synchronous; + uint32_t session_id; + + /* + * Interested for MLAG Updates, and also stores the client + * interested message mask + */ + bool mlag_updates_interested; + uint32_t mlag_reg_mask1; /* Statistics */ uint32_t redist_v4_add_cnt; @@ -143,6 +194,11 @@ struct zserv { uint32_t v6_nh_watch_rem_cnt; uint32_t vxlan_sg_add_cnt; uint32_t vxlan_sg_del_cnt; + uint32_t local_es_add_cnt; + uint32_t local_es_del_cnt; + uint32_t local_es_evi_add_cnt; + uint32_t local_es_evi_del_cnt; + uint32_t error_cnt; time_t nh_reg_time; time_t nh_dereg_time; @@ -166,6 +222,19 @@ struct zserv { _Atomic uint32_t last_read_cmd; /* command code of last message written */ _Atomic uint32_t last_write_cmd; + + /* + * Number of instances configured with + * graceful restart + */ + uint32_t gr_instance_count; + time_t restart_time; + + /* + * Graceful restart information for + * each instance + */ + TAILQ_HEAD(info_list, client_gr_info) gr_info_queue; }; #define ZAPI_HANDLER_ARGS \ @@ -176,7 +245,9 @@ struct zserv { DECLARE_HOOK(zserv_client_connect, (struct zserv *client), (client)); DECLARE_KOOH(zserv_client_close, (struct zserv *client), (client)); -extern unsigned int multipath_num; +#define DYNAMIC_CLIENT_GR_DISABLED(_client) \ + ((_client->proto <= ZEBRA_ROUTE_CONNECT) \ + || !(_client->gr_instance_count)) /* * Initialize Zebra API server. @@ -214,6 +285,17 @@ extern void zserv_start(char *path); */ extern int zserv_send_message(struct zserv *client, struct stream *msg); +/* + * Send a batch of messages to a connected Zebra API client. + * + * client + * the client to send to + * + * fifo + * the list of messages to send + */ +extern int zserv_send_batch(struct zserv *client, struct stream_fifo *fifo); + /* * Retrieve a client by its protocol and instance number. * @@ -228,6 +310,44 @@ extern int zserv_send_message(struct zserv *client, struct stream *msg); */ extern struct zserv *zserv_find_client(uint8_t proto, unsigned short instance); +/* + * Retrieve a client by its protocol, instance number, and session id. + * + * proto + * protocol number + * + * instance + * instance number + * + * session_id + * session id + * + * Returns: + * The Zebra API client. + */ +struct zserv *zserv_find_client_session(uint8_t proto, unsigned short instance, + uint32_t session_id); + +/* + * Retrieve a client object by the complete tuple of + * {protocol, instance, session}. This version supports use + * from a different pthread: the object will be returned marked + * in-use. The caller *must* release the client object with the + * release_client() api, to ensure that the in-use marker is cleared properly. + * + * Returns: + * The Zebra API client. + */ +extern struct zserv *zserv_acquire_client(uint8_t proto, + unsigned short instance, + uint32_t session_id); + +/* + * Release a client object that was acquired with the acquire_client() api. + * After this has been called, the pointer must not be used - it may be freed + * in another pthread if the client has closed. + */ +extern void zserv_release_client(struct zserv *client); /* * Close a client. @@ -240,12 +360,33 @@ extern struct zserv *zserv_find_client(uint8_t proto, unsigned short instance); */ extern void zserv_close_client(struct zserv *client); -#if defined(HANDLE_ZAPI_FUZZING) -extern void zserv_read_file(char *input); -#endif +/* + * Log a ZAPI message hexdump. + * + * errmsg + * Error message to include with packet hexdump + * + * msg + * Message to log + * + * hdr + * Message header + */ +void zserv_log_message(const char *errmsg, struct stream *msg, + struct zmsghdr *hdr); /* TODO */ -int zebra_finalize(struct thread *event); +__attribute__((__noreturn__)) int zebra_finalize(struct thread *event); + +/* + * Graceful restart functions. + */ +extern int zebra_gr_client_disconnect(struct zserv *client); +extern void zebra_gr_client_reconnect(struct zserv *client); +extern void zebra_gr_stale_client_cleanup(struct list *client_list); +extern void zread_client_capabilities(struct zserv *client, struct zmsghdr *hdr, + struct stream *msg, + struct zebra_vrf *zvrf); #ifdef __cplusplus }